Whamcloud - gitweb
* Compiles after merging b1_4
authoreeb <eeb>
Tue, 5 Jul 2005 18:35:10 +0000 (18:35 +0000)
committereeb <eeb>
Tue, 5 Jul 2005 18:35:10 +0000 (18:35 +0000)
*   Changed socknal...
    - use PID as well as NID to match connections so userspace (tcpnal) clients
      can be distinguished without changing the NID format.
    - unprivileged port == userspace client
    - don't create new connections to userspace clients
    - derive the NID/PID of a userspace client from the remote IP/port

*   Changed tcpnal...
    - use non-privileged ports
    - no concept of own NID (peer assigns)
    - don't accept connections

298 files changed:
ldiskfs/kernel_patches/patches/ext3-extents-2.6.9-rhel4.patch
ldiskfs/kernel_patches/patches/ext3-htree-dot-2.6.patch [new file with mode: 0644]
ldiskfs/kernel_patches/patches/ext3-ialloc-2.6.patch [new file with mode: 0644]
ldiskfs/kernel_patches/patches/ext3-mballoc2-2.6-suse.patch
ldiskfs/kernel_patches/patches/ext3-mballoc2-2.6.9-rhel4.patch
ldiskfs/kernel_patches/series/ldiskfs-2.6-rhel4.series
ldiskfs/kernel_patches/series/ldiskfs-2.6-suse.series
ldiskfs/ldiskfs/Makefile.in
ldiskfs/ldiskfs/autoMakefile.am
lustre/ChangeLog
lustre/Makefile.in
lustre/autoMakefile.am
lustre/autoconf/lustre-core.m4
lustre/autoconf/lustre-version.ac
lustre/include/liblustre.h
lustre/include/linux/lprocfs_status.h
lustre/include/linux/lustre_cfg.h
lustre/include/linux/lustre_compat25.h
lustre/include/linux/lustre_debug.h
lustre/include/linux/lustre_dlm.h
lustre/include/linux/lustre_export.h
lustre/include/linux/lustre_fsfilt.h
lustre/include/linux/lustre_ha.h
lustre/include/linux/lustre_idl.h
lustre/include/linux/lustre_lib.h
lustre/include/linux/lustre_lite.h
lustre/include/linux/lustre_mds.h
lustre/include/linux/lustre_net.h
lustre/include/linux/lustre_quota.h
lustre/include/linux/lvfs.h
lustre/include/linux/lvfs_linux.h
lustre/include/linux/obd.h
lustre/include/linux/obd_class.h
lustre/include/linux/obd_support.h
lustre/include/lustre/lustre_user.h
lustre/kernel_patches/kernel_configs/kernel-2.4.21-rhel-2.4-i686-smp.config
lustre/kernel_patches/kernel_configs/kernel-2.4.21-rhel-2.4-i686.config
lustre/kernel_patches/kernel_configs/kernel-2.4.21-rhel-2.4-ia64-smp.config
lustre/kernel_patches/kernel_configs/kernel-2.4.21-rhel-2.4-ia64.config
lustre/kernel_patches/kernel_configs/kernel-2.4.21-rhel-2.4-x86_64-smp.config
lustre/kernel_patches/kernel_configs/kernel-2.4.21-rhel-2.4-x86_64.config
lustre/kernel_patches/kernel_configs/kernel-2.6.5-2.6-suse-i686-bigsmp.config
lustre/kernel_patches/kernel_configs/kernel-2.6.5-2.6-suse-i686.config
lustre/kernel_patches/kernel_configs/kernel-2.6.5-2.6-suse-ia64-smp.config
lustre/kernel_patches/kernel_configs/kernel-2.6.5-2.6-suse-ia64.config
lustre/kernel_patches/kernel_configs/kernel-2.6.5-2.6-suse-x86_64-smp.config
lustre/kernel_patches/kernel_configs/kernel-2.6.5-2.6-suse-x86_64.config
lustre/kernel_patches/kernel_configs/kernel-2.6.9-2.6-rhel4-i686-smp.config
lustre/kernel_patches/kernel_configs/kernel-2.6.9-2.6-rhel4-i686.config
lustre/kernel_patches/kernel_configs/uml-2.6.10-fc3.config [new file with mode: 0644]
lustre/kernel_patches/kernel_configs/uml-vanilla-2.4.24.config
lustre/kernel_patches/patches/blkdev_tunables-2.4.21-chaos.patch
lustre/kernel_patches/patches/dev_read_only-2.6-lnxi.patch
lustre/kernel_patches/patches/dev_read_only-2.6-suse.patch
lustre/kernel_patches/patches/dev_read_only_2.4.20-rh.patch
lustre/kernel_patches/patches/dev_read_only_2.4.21-chaos.patch
lustre/kernel_patches/patches/elevator-cfq.patch [new file with mode: 0644]
lustre/kernel_patches/patches/export-show_task-2.4-cray.patch [new file with mode: 0644]
lustre/kernel_patches/patches/export-show_task-2.6-vanilla.patch
lustre/kernel_patches/patches/ext-2.4-patch-1-2.4.19-suse.patch
lustre/kernel_patches/patches/ext-2.4-patch-1.patch
lustre/kernel_patches/patches/ext3-delete_thread-2.4.29.patch [new file with mode: 0644]
lustre/kernel_patches/patches/ext3-ea-in-inode-2.4.29.patch [new file with mode: 0644]
lustre/kernel_patches/patches/ext3-extents-2.4.21-chaos.patch
lustre/kernel_patches/patches/ext3-extents-2.4.21-suse2.patch
lustre/kernel_patches/patches/ext3-extents-2.4.24.patch
lustre/kernel_patches/patches/ext3-extents-2.4.29.patch [new file with mode: 0644]
lustre/kernel_patches/patches/ext3-extents-2.6.9-rhel4.patch
lustre/kernel_patches/patches/ext3-htree-2.4.21-rhel.patch
lustre/kernel_patches/patches/ext3-htree-2.4.22-rh.patch
lustre/kernel_patches/patches/ext3-htree-2.4.29.patch [new file with mode: 0644]
lustre/kernel_patches/patches/ext3-htree-dot-2.6.5-suse.patch [new file with mode: 0644]
lustre/kernel_patches/patches/ext3-htree-dot-2.6.patch [new file with mode: 0644]
lustre/kernel_patches/patches/ext3-ialloc-2.4.24.patch [new file with mode: 0644]
lustre/kernel_patches/patches/ext3-ialloc-2.6.patch [new file with mode: 0644]
lustre/kernel_patches/patches/ext3-mballoc2-2.6-suse.patch
lustre/kernel_patches/patches/ext3-mballoc2-2.6.9-rhel4.patch
lustre/kernel_patches/patches/fsprivate-2.4-suse.patch [new file with mode: 0644]
lustre/kernel_patches/patches/fsprivate-2.4.patch [new file with mode: 0644]
lustre/kernel_patches/patches/fsprivate-2.6.patch [new file with mode: 0644]
lustre/kernel_patches/patches/gfp_debug-2.4.21-rhel.patch [new file with mode: 0644]
lustre/kernel_patches/patches/grab_cache_page_nowait_gfp-2.4.21-suse2.patch [new file with mode: 0644]
lustre/kernel_patches/patches/invalidate_show-2.4.29.patch [new file with mode: 0644]
lustre/kernel_patches/patches/kallsyms-2.4.29.patch [new file with mode: 0644]
lustre/kernel_patches/patches/linux-2.4.24-jbd-handle-EIO.patch [new file with mode: 0644]
lustre/kernel_patches/patches/linux-2.4.29-xattr-0.8.54.patch [new file with mode: 0644]
lustre/kernel_patches/patches/lustre_version.patch
lustre/kernel_patches/patches/nfs_export_kernel-2.4.20-hp.patch
lustre/kernel_patches/patches/nfs_export_kernel-2.4.29.patch [new file with mode: 0644]
lustre/kernel_patches/patches/nfs_export_kernel-2.4.29.patch-1 [new file with mode: 0644]
lustre/kernel_patches/patches/nfs_statfs-toomanyfiles-rhel-2.4.patch [new file with mode: 0644]
lustre/kernel_patches/patches/nfsd_iallocsem.patch [new file with mode: 0644]
lustre/kernel_patches/patches/qsnet-rhel-2.4.patch [new file with mode: 0644]
lustre/kernel_patches/patches/qsnet-suse-2.6.patch [new file with mode: 0644]
lustre/kernel_patches/patches/small_scatterlist-2.4.21-rhel.patch
lustre/kernel_patches/patches/statfs64-cast-unsigned-2.4-rhel.patch [new file with mode: 0644]
lustre/kernel_patches/patches/tcp-zero-copy-2.4.21-suse-171.patch [deleted file]
lustre/kernel_patches/patches/uml-exprt-clearuser.patch [new file with mode: 0644]
lustre/kernel_patches/patches/uml-patch-2.4.24-1.patch
lustre/kernel_patches/patches/uml-patch-2.4.29-1.patch [new file with mode: 0644]
lustre/kernel_patches/patches/vfs_intent-2.4.21-rhel.patch
lustre/kernel_patches/patches/vfs_intent-2.4.29-vanilla.patch [new file with mode: 0644]
lustre/kernel_patches/patches/vfs_races-2.6-fc3.patch [new file with mode: 0644]
lustre/kernel_patches/series/2.6-fc3.series
lustre/kernel_patches/series/2.6-rhel4.series
lustre/kernel_patches/series/2.6-suse-lnxi.series
lustre/kernel_patches/series/bgl-2.4.19
lustre/kernel_patches/series/ldiskfs-2.6-rhel4.series
lustre/kernel_patches/series/ldiskfs-2.6-suse.series
lustre/kernel_patches/series/rhel-2.4.21
lustre/kernel_patches/series/suse-2.4.21-cray
lustre/kernel_patches/series/suse-2.4.21-jvn
lustre/kernel_patches/series/vanilla-2.4.24
lustre/kernel_patches/series/vanilla-2.4.29 [new file with mode: 0644]
lustre/kernel_patches/series/vanilla-2.4.29-uml [new file with mode: 0644]
lustre/kernel_patches/targets/2.6-rhel4.target.in
lustre/kernel_patches/targets/2.6-suse.target.in
lustre/kernel_patches/targets/2.6-vanilla.target.in
lustre/kernel_patches/targets/hp_pnnl-2.4.target.in
lustre/kernel_patches/targets/rh-2.4.target.in
lustre/kernel_patches/targets/rhel-2.4.target.in
lustre/kernel_patches/targets/sles-2.4.target.in
lustre/kernel_patches/targets/suse-2.4.21-2.target.in
lustre/ldiskfs/Makefile.in
lustre/ldiskfs/autoMakefile.am
lustre/ldiskfs/lustre_quota_fmt.c
lustre/ldiskfs/quotafmt_test.c
lustre/ldlm/Makefile.am
lustre/ldlm/ldlm_extent.c
lustre/ldlm/ldlm_flock.c
lustre/ldlm/ldlm_internal.h
lustre/ldlm/ldlm_lib.c
lustre/ldlm/ldlm_lock.c
lustre/ldlm/ldlm_lockd.c
lustre/ldlm/ldlm_request.c
lustre/ldlm/ldlm_resource.c
lustre/liblustre/Makefile.am
lustre/liblustre/dir.c
lustre/liblustre/file.c
lustre/liblustre/genlib.sh
lustre/liblustre/llite_lib.c
lustre/liblustre/llite_lib.h
lustre/liblustre/lutil.c
lustre/liblustre/lutil.h
lustre/liblustre/namei.c
lustre/liblustre/rw.c
lustre/liblustre/super.c
lustre/liblustre/tests/Makefile.am
lustre/liblustre/tests/echo_test.c
lustre/liblustre/tests/recovery_small.c
lustre/liblustre/tests/replay_single.c
lustre/liblustre/tests/sanity.c
lustre/liblustre/tests/test_common.c
lustre/liblustre/tests/test_common.h
lustre/llite/autoMakefile.am
lustre/llite/dcache.c
lustre/llite/dir.c
lustre/llite/file.c
lustre/llite/llite_internal.h
lustre/llite/llite_lib.c
lustre/llite/llite_mmap.c
lustre/llite/lproc_llite.c
lustre/llite/namei.c
lustre/llite/rw.c
lustre/llite/super.c
lustre/lov/autoMakefile.am
lustre/lov/lov_internal.h
lustre/lov/lov_merge.c
lustre/lov/lov_obd.c
lustre/lov/lov_pack.c
lustre/lov/lov_qos.c
lustre/lov/lov_request.c
lustre/lov/lproc_lov.c
lustre/lvfs/.cvsignore
lustre/lvfs/Makefile.in
lustre/lvfs/autoMakefile.am
lustre/lvfs/fsfilt_ext3.c
lustre/lvfs/lvfs_common.c
lustre/lvfs/lvfs_linux.c
lustre/lvfs/lvfs_userfs.c
lustre/lvfs/quotacheck_test.c
lustre/lvfs/quotactl_test.c
lustre/mdc/autoMakefile.am
lustre/mdc/lproc_mdc.c
lustre/mdc/mdc_internal.h
lustre/mdc/mdc_lib.c
lustre/mdc/mdc_locks.c
lustre/mdc/mdc_request.c
lustre/mds/Makefile.in
lustre/mds/autoMakefile.am
lustre/mds/handler.c
lustre/mds/lproc_mds.c
lustre/mds/mds_fs.c
lustre/mds/mds_internal.h
lustre/mds/mds_lib.c
lustre/mds/mds_log.c
lustre/mds/mds_lov.c
lustre/mds/mds_open.c
lustre/mds/mds_reint.c
lustre/mds/mds_unlink_open.c
lustre/mds/quota_context.c
lustre/mds/quota_master.c
lustre/obdclass/autoMakefile.am
lustre/obdclass/class_obd.c
lustre/obdclass/genops.c
lustre/obdclass/llog.c
lustre/obdclass/llog_ioctl.c
lustre/obdclass/llog_lvfs.c
lustre/obdclass/llog_obd.c
lustre/obdclass/llog_swab.c
lustre/obdclass/llog_test.c
lustre/obdclass/lprocfs_status.c
lustre/obdclass/obd_config.c
lustre/obdclass/simple.c [deleted file]
lustre/obdclass/sysctl.c
lustre/obdclass/uuid.c
lustre/obdecho/autoMakefile.am
lustre/obdecho/echo.c
lustre/obdecho/lproc_echo.c
lustre/obdfilter/Makefile.in
lustre/obdfilter/autoMakefile.am
lustre/obdfilter/filter.c
lustre/obdfilter/filter_internal.h
lustre/obdfilter/filter_io.c
lustre/obdfilter/filter_io_24.c
lustre/obdfilter/filter_io_26.c
lustre/obdfilter/filter_log.c
lustre/obdfilter/filter_lvb.c
lustre/obdfilter/filter_san.c
lustre/obdfilter/lproc_obdfilter.c
lustre/osc/Makefile.in
lustre/osc/autoMakefile.am
lustre/osc/lproc_osc.c
lustre/osc/osc_create.c
lustre/osc/osc_internal.h
lustre/osc/osc_quota.c
lustre/osc/osc_request.c
lustre/ost/autoMakefile.am
lustre/ost/lproc_ost.c
lustre/ost/ost_handler.c
lustre/ost/ost_internal.h
lustre/ptlrpc/Makefile.in
lustre/ptlrpc/autoMakefile.am
lustre/ptlrpc/client.c
lustre/ptlrpc/events.c
lustre/ptlrpc/import.c
lustre/ptlrpc/llog_client.c
lustre/ptlrpc/llog_server.c
lustre/ptlrpc/lproc_ptlrpc.c
lustre/ptlrpc/niobuf.c
lustre/ptlrpc/pack_generic.c
lustre/ptlrpc/pers.c
lustre/ptlrpc/pinger.c
lustre/ptlrpc/ptlrpc_internal.h
lustre/ptlrpc/ptlrpc_module.c
lustre/ptlrpc/recov_thread.c
lustre/ptlrpc/recover.c
lustre/ptlrpc/service.c
lustre/tests/.RC_CURRENT.tag
lustre/tests/.cvsignore
lustre/tests/2ost.sh [new file with mode: 0644]
lustre/tests/acceptance-small.sh
lustre/tests/cfg/local.sh
lustre/tests/cfg/mdev.sh
lustre/tests/echo.sh
lustre/tests/llmount.sh
lustre/tests/lov.sh
lustre/tests/mcr-individual-ost-nogw-config.sh [deleted file]
lustre/tests/mcr-mds-failover-config.sh [deleted file]
lustre/tests/mcr-routed-config.sh [deleted file]
lustre/tests/mcrlov.sh [deleted file]
lustre/tests/mount2fs.sh
lustre/tests/oos.sh
lustre/tests/quota_sanity.sh
lustre/tests/recovery-small.sh
lustre/tests/replay-single.sh
lustre/tests/routed.sh [new file with mode: 0644]
lustre/tests/runtests
lustre/tests/sanity.sh
lustre/tests/sanityN.sh
lustre/tests/test-framework.sh
lustre/tests/test.c [deleted file]
lustre/tests/test_brw.c
lustre/tests/uml.sh
lustre/tests/writemany.c
lustre/utils/Lustre/lustredb.py
lustre/utils/Makefile.am
lustre/utils/l_getgroups.c [new file with mode: 0644]
lustre/utils/lconf
lustre/utils/lfs.c
lustre/utils/liblustreapi.c
lustre/utils/llmount.c
lustre/utils/lrun
lustre/utils/lustre_cfg.c
lustre/utils/obd.c
lustre/utils/parser.c
lustre/utils/wirecheck.c
lustre/utils/wiretest.c

index c8fc99a..3b873c2 100644 (file)
@@ -2556,16 +2556,16 @@ Index: linux-stage/include/linux/ext3_fs.h
 --- linux-stage.orig/include/linux/ext3_fs.h   2005-02-25 14:53:56.424908168 +0200
 +++ linux-stage/include/linux/ext3_fs.h        2005-02-25 15:39:12.841950008 +0200
 @@ -186,8 +186,9 @@
+ #define EXT3_NOTAIL_FL                        0x00008000 /* don't merge file tail */
  #define EXT3_DIRSYNC_FL                       0x00010000 /* dirsync behaviour (directories only) */
  #define EXT3_TOPDIR_FL                        0x00020000 /* Top of directory hierarchies*/
- #define EXT3_RESERVED_FL              0x80000000 /* reserved for ext3 lib */
 +#define EXT3_EXTENTS_FL                       0x00080000 /* Inode uses extents */
+ #define EXT3_RESERVED_FL              0x80000000 /* reserved for ext3 lib */
  
 -#define EXT3_FL_USER_VISIBLE          0x0003DFFF /* User visible flags */
 +#define EXT3_FL_USER_VISIBLE          0x000BDFFF /* User visible flags */
  #define EXT3_FL_USER_MODIFIABLE               0x000380FF /* User modifiable flags */
  
- /*
 @@ -237,6 +238,9 @@
  #endif
  #define EXT3_IOC_GETRSVSZ             _IOR('f', 5, long)
diff --git a/ldiskfs/kernel_patches/patches/ext3-htree-dot-2.6.patch b/ldiskfs/kernel_patches/patches/ext3-htree-dot-2.6.patch
new file mode 100644 (file)
index 0000000..9192112
--- /dev/null
@@ -0,0 +1,23 @@
+Index: linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891/fs/ext3/namei.c
+===================================================================
+--- linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891.orig/fs/ext3/namei.c 2005-04-04 05:06:46.000000000 -0600
++++ linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891/fs/ext3/namei.c      2005-04-04 05:09:18.000000000 -0600
+@@ -926,8 +926,16 @@
+       struct inode *dir = dentry->d_parent->d_inode;
+       sb = dir->i_sb;
+-      if (!(frame = dx_probe(dentry, NULL, &hinfo, frames, err)))
+-              return NULL;
++      /* NFS may look up ".." - look at dx_root directory block */
++      if (namelen > 2 || name[0] != '.'||(name[1] != '.' && name[1] != '\0')){
++              if (!(frame = dx_probe(dentry, NULL, &hinfo, frames, err)))
++                      return NULL;
++      } else {
++              frame = frames;
++              frame->bh = NULL;                       /* for dx_release() */
++              frame->at = (struct dx_entry *)frames;  /* hack for zero entry*/
++              dx_set_block(frame->at, 0);             /* dx_root block is 0 */
++      }
+       hash = hinfo.hash;
+       do {
+               block = dx_get_block(frame->at);
diff --git a/ldiskfs/kernel_patches/patches/ext3-ialloc-2.6.patch b/ldiskfs/kernel_patches/patches/ext3-ialloc-2.6.patch
new file mode 100644 (file)
index 0000000..15d37a9
--- /dev/null
@@ -0,0 +1,128 @@
+Index: linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891/fs/ext3/ialloc.c
+===================================================================
+--- linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891.orig/fs/ext3/ialloc.c        2005-05-16 14:10:54.000000000 -0600
++++ linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891/fs/ext3/ialloc.c     2005-05-16 14:18:29.000000000 -0600
+@@ -352,13 +352,17 @@
+       return -1;
+ }
+-static int find_group_other(struct super_block *sb, struct inode *parent)
++static int find_group_other(struct super_block *sb, struct inode *parent,
++                          int mode)
+ {
+       int parent_group = EXT3_I(parent)->i_block_group;
++      struct ext3_sb_info *sbi = EXT3_SB(sb);
+       int ngroups = EXT3_SB(sb)->s_groups_count;
+       struct ext3_group_desc *desc;
+       struct buffer_head *bh;
+       int group, i;
++      int best_group = -1;
++      int avefreeb, freeb, best_group_freeb = 0;
+       /*
+        * Try to place the inode in its parent directory
+@@ -366,9 +370,9 @@
+       group = parent_group;
+       desc = ext3_get_group_desc (sb, group, &bh);
+       if (desc && le16_to_cpu(desc->bg_free_inodes_count) &&
+-                      le16_to_cpu(desc->bg_free_blocks_count))
++          (!S_ISREG(mode) || le16_to_cpu(desc->bg_free_blocks_count)))
+               return group;
+-
++      avefreeb = le32_to_cpu(sbi->s_es->s_free_blocks_count) / ngroups;
+       /*
+        * We're going to place this inode in a different blockgroup from its
+        * parent.  We want to cause files in a common directory to all land in
+@@ -381,33 +385,47 @@
+       group = (group + parent->i_ino) % ngroups;
+       /*
+-       * Use a quadratic hash to find a group with a free inode and some free
+-       * blocks.
++       * Use a quadratic hash to find a group with a free inode and
++       * average number of free blocks.
+        */
+       for (i = 1; i < ngroups; i <<= 1) {
+               group += i;
+               if (group >= ngroups)
+                       group -= ngroups;
+               desc = ext3_get_group_desc (sb, group, &bh);
+-              if (desc && le16_to_cpu(desc->bg_free_inodes_count) &&
+-                              le16_to_cpu(desc->bg_free_blocks_count))
++              if (!desc || !desc->bg_free_inodes_count)
++                      continue;
++              if (!S_ISREG(mode))
++                      return group;
++              if (le16_to_cpu(desc->bg_free_blocks_count) >= avefreeb)
+                       return group;
+       }
+       /*
+-       * That failed: try linear search for a free inode, even if that group
+-       * has no free blocks.
++       * That failed: start from last group used to allocate inode
++       * try linear search for a free inode and prefereably
++       * free blocks.
+        */
+-      group = parent_group;
++      group = sbi->s_last_alloc_group;
++      if (group == -1)
++              group = parent_group;
++
+       for (i = 0; i < ngroups; i++) {
+               if (++group >= ngroups)
+                       group = 0;
+               desc = ext3_get_group_desc (sb, group, &bh);
+-              if (desc && le16_to_cpu(desc->bg_free_inodes_count))
+-                      return group;
++              if (!desc || !desc->bg_free_inodes_count)
++                      continue;
++              freeb = le16_to_cpu(desc->bg_free_blocks_count);
++              if (freeb > best_group_freeb) {
++                      best_group_freeb = freeb;
++                      best_group = group;
++                      if (freeb >= avefreeb || !S_ISREG(mode))
++                              break;
++              }
+       }
+-
+-      return -1;
++      sbi->s_last_alloc_group = best_group;
++      return best_group;
+ }
+ /*
+@@ -454,7 +472,7 @@
+               else
+                       group = find_group_orlov(sb, dir);
+       } else 
+-              group = find_group_other(sb, dir);
++              group = find_group_other(sb, dir, mode);
+       err = -ENOSPC;
+       if (group == -1)
+Index: linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891/fs/ext3/super.c
+===================================================================
+--- linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891.orig/fs/ext3/super.c 2005-05-16 14:10:54.000000000 -0600
++++ linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891/fs/ext3/super.c      2005-05-16 14:17:14.000000000 -0600
+@@ -1297,6 +1297,7 @@
+       percpu_counter_init(&sbi->s_dirs_counter);
+       bgl_lock_init(&sbi->s_blockgroup_lock);
++      sbi->s_last_alloc_group = -1;
+       for (i = 0; i < db_count; i++) {
+               block = descriptor_loc(sb, logic_sb_block, i);
+               sbi->s_group_desc[i] = sb_bread(sb, block);
+Index: linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891/include/linux/ext3_fs_sb.h
+===================================================================
+--- linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891.orig/include/linux/ext3_fs_sb.h      2005-05-16 14:10:54.000000000 -0600
++++ linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891/include/linux/ext3_fs_sb.h   2005-05-16 14:17:14.000000000 -0600
+@@ -59,6 +59,8 @@
+       struct percpu_counter s_freeinodes_counter;
+       struct percpu_counter s_dirs_counter;
+       struct blockgroup_lock s_blockgroup_lock;
++      /* Last group used to allocate inode */
++      int s_last_alloc_group;
+       /* root of the per fs reservation window tree */
+       spinlock_t s_rsv_window_lock;
index 4de2b32..d38fb54 100644 (file)
@@ -2,7 +2,7 @@ Index: linux-2.6.5-suse/fs/ext3/mballoc.c
 ===================================================================
 --- linux-2.6.5-suse.orig/fs/ext3/mballoc.c    2005-03-02 22:42:20.659360368 +0300
 +++ linux-2.6.5-suse/fs/ext3/mballoc.c 2005-03-11 16:13:13.000000000 +0300
-@@ -0,0 +1,1863 @@
+@@ -0,0 +1,1864 @@
 +/*
 + * Copyright(c) 2003, 2004, 2005, Cluster File Systems, Inc, info@clusterfs.com
 + * Written by Alex Tomas <alex@clusterfs.com>
@@ -932,8 +932,9 @@ Index: linux-2.6.5-suse/fs/ext3/mballoc.c
 +               * We've been searching too long. Let's try to allocate
 +               * the best chunk we've found so far
 +               */
-+              printk(KERN_ERR "EXT3-fs: too long searching (%d/%d)\n",
-+                              ac.ac_b_ex.fe_len, ac.ac_g_ex.fe_len);
++              ext3_warning(inode->i_sb, __FUNCTION__,
++                           "too long searching: got %d want %d\n",
++                           ac.ac_b_ex.fe_len, ac.ac_g_ex.fe_len);
 +              ext3_mb_try_best_found(&ac, &e3b);
 +              if (ac.ac_status != AC_STATUS_FOUND) {
 +                      /*
index dc6ffe3..24b2595 100644 (file)
@@ -2,7 +2,7 @@ Index: linux-stage/fs/ext3/mballoc.c
 ===================================================================
 --- linux-stage.orig/fs/ext3/mballoc.c 2005-02-25 17:28:41.836311072 +0200
 +++ linux-stage/fs/ext3/mballoc.c      2005-02-25 17:28:41.859307576 +0200
-@@ -0,0 +1,1860 @@
+@@ -0,0 +1,1861 @@
 +/*
 + * Copyright (c) 2003, Cluster File Systems, Inc, info@clusterfs.com
 + * Written by Alex Tomas <alex@clusterfs.com>
@@ -932,8 +932,9 @@ Index: linux-stage/fs/ext3/mballoc.c
 +               * We've been searching too long. Let's try to allocate
 +               * the best chunk we've found so far
 +               */
-+              printk(KERN_ERR "EXT3-fs: too long searching (%d/%d)\n",
-+                              ac.ac_b_ex.fe_len, ac.ac_g_ex.fe_len);
++              ext3_warning(inode->i_sb, __FUNCTION__,
++                           "too long searching: got %d want %d\n",
++                           ac.ac_b_ex.fe_len, ac.ac_g_ex.fe_len);
 +              ext3_mb_try_best_found(&ac, &e3b);
 +              if (ac.ac_status != AC_STATUS_FOUND) {
 +                      /*
index 70e7b12..0980162 100644 (file)
@@ -9,3 +9,4 @@ ext3-include-fixes-2.6-rhel4.patch
 ext3-extents-2.6.9-rhel4.patch
 ext3-mballoc2-2.6.9-rhel4.patch 
 ext3-nlinks-2.6.7.patch
+ext3-htree-dot-2.6.patch
index fd05c25..c44eea3 100644 (file)
@@ -10,3 +10,4 @@ ext3-extents-2.6.5.patch
 ext3-mballoc2-2.6-suse.patch
 ext3-nlinks-2.6.7.patch
 ext3-rename-reserve-2.6-suse.patch
+ext3-htree-dot-2.6.5-suse.patch
index be51da2..7236410 100644 (file)
@@ -1,6 +1,6 @@
 default: all
 
-MODULES := ldiskfs quotafmt_test
+MODULES := ldiskfs #quotafmt_test
 
 # copy makefile over to not break patches
 ext3_extra := $(wildcard @LINUX@/fs/ext3/Makefile)
@@ -11,13 +11,13 @@ linux_headers := $(wildcard @LINUX@/include/linux/ext3*.h)
 ext3_sources := $(filter-out %.mod.c,$(wildcard @LINUX@/fs/ext3/*.c))
 new_sources := iopen.c iopen.h extents.c mballoc.c
 new_headers := ext3_extents.h
-quotafmt_sources := lustre_quota_fmt.c
-quotafmt_headers := lustre_quota_fmt.h
+#quotafmt_sources := lustre_quota_fmt.c
+#quotafmt_headers := lustre_quota_fmt.h
 ldiskfs_patched_sources := $(notdir $(ext3_sources) $(ext3_headers)) $(new_sources) $(new_headers)
-ldiskfs_sources := $(ldiskfs_patched_sources) $(quotafmt_sources) $(quotafmt_headers)
+ldiskfs_sources := $(ldiskfs_patched_sources) #$(quotafmt_sources) $(quotafmt_headers)
 
 ldiskfs-objs := $(filter %.o,$(ldiskfs_sources:.c=.o))
-quotafmt-objs := quotafmt_test.o
+#quotafmt-objs := quotafmt_test.o
 
 EXTRA_PRE_CFLAGS := -I@LINUX@/fs -I@LUSTRE@ -I@LUSTRE@/ldiskfs
 
index 4f9e784..53ca41e 100644 (file)
@@ -41,10 +41,12 @@ if USE_QUILT
        cd linux-stage && quilt setup -l ../$(series) -d ../$(patches)
        cd linux-stage && quilt push -a -q
 else
+       @echo -n "Applying ext3 patches:"
        @cd linux-stage && for i in $$(<../$(series)) ; do \
-               echo "patch -p1 < ../$(patches)/$$i" ; \
-               patch -p1 < ../$(patches)/$$i || exit 1 ; \
+               echo -n " $$i" ; \
+               patch -s -p1 < ../$(patches)/$$i || exit 1 ; \
        done
+       @echo
 endif
        mkdir linux
        @echo -n "Replacing 'ext3' with 'ldiskfs':"
@@ -70,7 +72,7 @@ foo-check:
        @echo "ldiskfs_OBJECTS: $(ldiskfs_OBJECTS)"
        @echo "ldiskfs_LDADD: $(ldiskfs_LDADD)"
 
-MOSTLYCLEANFILES = *.o *.ko *.mod.c
+MOSTLYCLEANFILES := @MOSTLYCLEANFILES@ 
 CLEANFILES = sources $(notdir $(linux_headers) $(ext3_headers) $(ext3_sources) $(new_sources) $(new_headers))
 
 EXTRA_DIST := lustre_quota_fmt.c lustre_quota_fmt.h quotafmt_test.c
index ace07ea..0fb9dff 100644 (file)
@@ -1,4 +1,172 @@
 tbd         Cluster File Systems, Inc. <info@clusterfs.com>
+       * version 1.4.4
+       * bug fixes
+
+Severity   : minor
+Frequency  : Clusters with multiple interfaces not on the same subnet
+Bugzilla   : 5541
+Description: Nodes will repeatedly try to reconnect to an interface which it
+             cannot reach and report an error to the log.
+Details    : Extra peer list entries will be created by lconf with some peers
+             unreachable.  lconf now validates the peer before adding it.
+
+Severity   : major
+Frequency  : Only if a default stripe is set on the filesystem root.
+Bugzilla   : 6367
+Description: Setting a default stripe on the filesystem root prevented the
+             filesystem from being remounted.
+Details    : The client was sending extra request flags in the root getattr
+             request and did not allocate a reply buffer for the dir EA.
+
+Severity   : major
+Frequency  : occasional, higher if lots of files are accessed by one client
+Bugzilla   : 6159, 6097
+Description: Client trips assertion regarding lsm mismatch/magic
+Details    : While revalidating inodes the VFS looks up inodes with ifind()
+             and in rare cases can find an inode that is being freed.
+             The ll_test_inode() code will free the lsm during ifind()
+             when it finds an existing inode and then the VFS later attaches
+             this free lsm to a new inode.
+
+Severity   : minor
+Frequency  : occasional
+Description: While starting a server, the fsfilt_ext3 module could not be 
+             loaded.
+Details    : CFS's improved ext3 filesystem is named ldiskfs for 2.6
+             kernels.  Previously, lconf would still use the ext3 name
+             when trying to load modules.  Now, it will correctly use
+             ext3 on 2.4 and ldiskfs on 2.6.
+
+Severity   : enhancement
+Description: The default stripe count has been changed to 1
+Details    : The interpretation of the default stripe count (0, to lfs
+             or lmc) has been changed to mean striping across a single
+             OST, rather than all available.  For general usage we have
+             found a stripe count of 1 or 2 works best.
+
+Severity   : major
+Frequency  : occasional
+Bugzilla   : 6409, 6834
+Description: Creating files with an explicit stripe count may lead to
+             a failed assertion on the MDS
+Details    : If some OSTs are full or unavailable, creating files may
+             trigger a failed assertion on the MDS.  Now, Lustre will
+             try to use other servers or return an error to the
+             client.
+
+Severity   : minor
+Frequency  : occasional
+Bugzilla   : 6469
+Description: Multiple concurrent overlapping read+write on multiple SMP nodes
+             caused lock timeout during readahead (since 1.4.2).
+Details    : Processes doing readahead might match a lock that hasn't been
+             granted yet if there are overlapping and conflicting lock
+             requests.  The readahead process waits on ungranted lock
+            (original lock is CBPENDING), while OST waits for that process
+            to cancel CBPENDING read lock and eventually evicts client.
+
+------------------------------------------------------------------------------
+
+2005-06-20  Cluster File Systems, Inc. <info@clusterfs.com>
+       * version 1.4.3
+       * bug fixes
+
+Severity   : minor
+Frequency  : rare (extremely heavy IO load with hundreds of clients)
+Bugzilla   : 6172
+Description: Client is evicted, gets IO error writing to file
+Details    : lock ordering changes for bug 5492 reintroduced bug 3267 and
+             caused clients to be evicted for AST timeouts.  The fixes in
+             bug 5192 mean we no longer need to have such short AST timeouts
+             so ldlm_timeout has been increased.
+
+Severity   : major
+Frequency  : occasional during --force or --failover shutdown under load
+Bugzilla   : 5949, 4834
+Description: Server oops/LBUG if stopped with --force or --failover under load
+Details    : a collection of import/export refcount and cleanup ordering
+             issues fixed for safer force cleanup
+
+Severity   : major
+Frequency  : only filesystems larger than 120 OSTs
+Bugzilla   : 5990, 6223
+Description: lfs getstripe would oops on a very large filesystem
+Details    : lov_getconfig used kfree on vmalloc'd memory
+
+Severity   : minor
+Frequency  : only filesystems exporting via NFS to Solaris 10 clients
+Bugzilla   : 6242, 6243
+Description: reading from files that had been truncated to a non-zero size
+             but never opened returned no data
+Details    : ll_file_read() reads zeros from no-object files to EOF
+
+Severity   : major
+Frequency  : rare
+Bugzilla   : 6200
+Description: A bug in MDS/OSS recovery could cause the OSS to fail an assertion
+Details    : There's little harm in aborting MDS/OSS recovery and letting it
+             try again, so I removed the LASSERT and return an error instead.
+
+Severity   : enhancement
+Bugzilla   : 5902
+Description: New debugging infrastructure for tracking down data corruption
+Details    : The I/O checksum code was replaced to: (a) control it at runtime,
+             (b) cover more of the client-side code path, and (c) try to narrow
+             down where problems occurred
+
+Severity   : major
+Frequency  : rare
+Bugzilla   : 3819, 4364, 4397, 6313
+Description: Racing close and eviction MDS could cause assertion in mds_close
+Details    : It was possible to get multiple mfd references during close and
+             client eviction, leading to one thread referencing a freed mfd.
+
+Severity:  : enhancement
+Bugzilla   : 3262, 6359
+Description: Attempts to reconnect to servers are now more aggressive.
+Details    : This builds on the enhanced upcall-less recovery that was added
+             in 1.4.2.  When trying to reconnect to servers,  clients will 
+             now try each server in the failover group every 10 seconds.  By
+             default, clients would previously try one server every 25 seconds.
+
+Severity   : major
+Frequency  : rare
+Bugzilla   : 6371
+Description: After recovery, certain operations trigger a failed
+             assertion on a client.
+Details    : Failing over an mds, using lconf -d --failover, while a
+             client was doing a readdir() call would cause the client to 
+             LBUG after recovery completed and the readdir() was resent.
+
+Severity   : enhancement
+Bugzilla   : 6296
+Description: Default groups are now added by lconf
+Details    : You can now run lconf --group <servicename> without having to 
+             manually add groups with lmc.
+
+Severity   : major
+Frequency  : occasional
+Bugzilla   : 6412
+Description: Nodes with an elan id of 0 trigger a failed assertion
+
+Severity   : minor
+Frequency  : always when accessing e.g. tty/console device nodes
+Bugzilla   : 3790
+Description: tty and some other devices nodes cannot be used on lustre
+Details    : file's private_data field is used by device data and lustre
+             values in there got lost. New field was added to struct file to
+             store fs-specific private data.
+
+Severity   : minor
+Frequency  : when exporting Lustre via NFS
+Bugzilla   : 5275
+Description: NFSD failed occasionally when looking up a path component
+Details    : NFSD is looking up ".." which was broken in ext3 directories
+             that had grown large enough to become hashed.
+
+------------------------------------------------------------------------------
+
+2005-05-05  Cluster File Systems, Inc. <info@clusterfs.com>
        * version 1.4.2
        NOTE: Lustre 1.4.2 uses an incompatible network protocol than previous
             versions of Lustre.  Please update all servers and clients to
@@ -19,7 +187,18 @@ tbd         Cluster File Systems, Inc. <info@clusterfs.com>
        - don't hold i_size_sem in ll_nopage() and ll_ap_refresh_count (6077)
        - don't hold client locks on temporary worklist from l_lru (5666)
        - handle IO errors in 2.6 obdfilter bio completion routine (6046)
-       * miscellania
+       - automatically evict dead clients (5921)
+       - Update file size properly in create+truncate+fstat case (6196)
+       - Do not unhash mountpoint dentries, do not allow removal of
+         mountpoints (5907)
+       - Avoid lock ordering deadlock issue with write/truncate (6203,5654)
+       - reserve enough journal credits in fsfilt_start_log for setattr (4554)
+       - ldlm_enqueue freed-export error path would always LBUG (6149,6184)
+       - don't reference lr_lvb_data until after we hold lr_lvb_sem (6170)
+       - don't overwrite last_rcvd if there is a *_client_add() error (6086)
+       - Correctly handle reads of files with no objects (6243)
+       - lctl recover will also mark a device active if deactivate used (5933)
+       * miscellania
        - by default create 1 inode per 4kB space on MDS, per 16kB on OSTs
        - allow --write-conf on an MDS with different nettype than client (5619)
        - don't write config llogs to MDS for mounts not from that MDS (5617)
@@ -36,7 +215,16 @@ tbd         Cluster File Systems, Inc. <info@clusterfs.com>
        - rmmod NALs that might be loaded because of /etc/modules.conf (6133)
        - support for mountfsoptions and clientoptions to the Lustre LDAP (5873)
        - improved "lustre status" script
-       
+       - initialize blocksize for non-regular files (6062)
+       - added --disable-server and --disable-client configure options (5782)
+       - introduce a lookup cache for lconf to avoid repeated DB scans (6204)
+       - Vanilla 2.4.29 support
+       - increase maximum number of obd devices to 520 (6242)
+       - remove the tcp-zero-copy patch from the suse-2.4 series (5902)
+       - Quadrics Elan drivers are now included for the RHEL 3 2.4.21 and
+         SLES 9 2.6.5 kernels
+       - limit stripes per file to 160 (the maximum EA size) (6093)
+
 2005-03-22  Cluster File Systems, Inc. <info@clusterfs.com>
        * version 1.4.1
        * bug fixes
@@ -101,7 +289,7 @@ tbd         Cluster File Systems, Inc. <info@clusterfs.com>
        - fix ppc64/x86_64 spec to use %{_libdir} instead of /usr/lib (5389)
        - remove ancient LOV_MAGIC_V0 EA support (5047)
        - add "disk I/Os in flight" and "I/O req time" stats in obdfilter
-       - align r/w RPCs to PTLRPC_MAX_BRW_SIZE boundaries for performance
+       - align r/w RPCs to PTLRPC_MAX_BRW_SIZE boundary for performance (3451)
        - allow readahead allocations to fail when low on memory (5383)
        - mmap locking landed again, after considerable improvement (2828)
        - add get_hostaddr() to lustreDB.py for LDAP support (5459)
index 789f656..d6e7684 100644 (file)
@@ -1,15 +1,13 @@
-@LDISKFS_TRUE@subdir-m  += ldiskfs
+@LDISKFS_TRUE@subdir-m += ldiskfs
 
 subdir-m += lvfs
 subdir-m += obdclass
 subdir-m += lov
 subdir-m += ptlrpc
-subdir-m += obdecho
 subdir-m += osc
-subdir-m += mdc
-subdir-m += mds
-subdir-m += obdfilter
-subdir-m += ost
-subdir-m += llite
+subdir-m += obdecho
+
+@SERVER_TRUE@subdir-m += mds obdfilter ost
+@CLIENT_TRUE@subdir-m += mdc llite
 
 @INCLUDE_RULES@
index c94d7cc..65f636b 100644 (file)
@@ -5,9 +5,34 @@
 
 AUTOMAKE_OPTIONS = foreign
 
-SUBDIRS = include ldiskfs lvfs obdclass lov ldlm ptlrpc                        \
-       obdecho osc mdc mds obdfilter ost llite \
-       liblustre doc utils tests conf scripts autoconf
+ALWAYS_SUBDIRS := include lvfs obdclass ldlm ptlrpc osc lov obdecho \
+       doc utils tests conf scripts autoconf
+
+SERVER_SUBDIRS := ldiskfs obdfilter ost mds
+
+CLIENT_SUBDIRS := mdc llite
+
+LIBLUSTRE_SUBDIRS := liblustre
+
+SUBDIRS := $(ALWAYS_SUBDIRS)
+
+if SERVER
+SUBDIRS += $(SERVER_SUBDIRS)
+endif
+
+if CLIENT
+SUBDIRS += $(CLIENT_SUBDIRS)
+endif
+
+# this needs to be after the client subdirs
+if LIBLUSTRE
+if !CLIENT
+SUBDIRS += $(CLIENT_SUBDIRS)
+endif
+SUBDIRS += $(LIBLUSTRE_SUBDIRS)
+endif
+
+DIST_SUBDIRS := $(ALWAYS_SUBDIRS) $(SERVER_SUBDIRS) $(CLIENT_SUBDIRS) $(LIBLUSTRE_SUBDIRS)
 
 EXTRA_DIST = BUGS FDL kernel_patches
 
index c8ac15b..6d639ec 100644 (file)
@@ -155,6 +155,25 @@ fi
 ])
 
 #
+# LC_FUNC_FILEMAP_FDATASYNC
+#
+# if filemap_fdatasync() exists
+#
+AC_DEFUN([LC_FUNC_FILEMAP_FDATAWRITE],
+[AC_MSG_CHECKING([whether filemap_fdatawrite() is defined])
+LB_LINUX_TRY_COMPILE([
+       #include <linux/fs.h>
+],[
+       int (*foo)(struct address_space *)= filemap_fdatawrite;
+],[
+       AC_MSG_RESULT([yes])
+       AC_DEFINE(HAVE_FILEMAP_FDATAWRITE, 1, [filemap_fdatawrite() found])
+],[
+       AC_MSG_RESULT([no])
+])
+])
+
+#
 # LC_FUNC_DIRECT_IO
 #
 # if direct_IO takes a struct file argument
@@ -389,7 +408,9 @@ AC_DEFINE_UNQUOTED(OBD_MAX_IOCTL_BUFFER, $OBD_BUFFER_SIZE, [IOCTL Buffer Size])
 # Lustre linux kernel checks
 #
 AC_DEFUN([LC_PROG_LINUX],
-[LC_CONFIG_BACKINGFS
+[if test x$enable_server = xyes ; then
+       LC_CONFIG_BACKINGFS
+fi
 LC_CONFIG_PINGER
 
 LC_STRUCT_KIOBUF
@@ -402,9 +423,30 @@ LC_STRUCT_INODE
 LC_FUNC_REGISTER_CACHE
 LC_FUNC_GRAB_CACHE_PAGE_NOWAIT_GFP
 LC_FUNC_DEV_SET_RDONLY
+LC_FUNC_FILEMAP_FDATAWRITE
 ])
 
 #
+# LC_CONFIG_CLIENT_SERVER
+#
+# Build client/server sides of Lustre
+#
+AC_DEFUN([LC_CONFIG_CLIENT_SERVER],
+[AC_MSG_CHECKING([whether to build Lustre server support])
+AC_ARG_ENABLE([server],
+       AC_HELP_STRING([--disable-server],
+                       [disable Lustre server support]),
+       [],[enable_server='yes'])
+AC_MSG_RESULT([$enable_server])
+
+AC_MSG_CHECKING([whether to build Lustre client support])
+AC_ARG_ENABLE([client],
+       AC_HELP_STRING([--disable-client],
+                       [disable Lustre client support]),
+       [],[enable_client='yes'])
+AC_MSG_RESULT([$enable_client])])
+
+#
 # LC_CONFIG_LIBLUSTRE
 #
 # whether to build liblustre
@@ -439,8 +481,17 @@ AC_DEFUN([LC_CONFIGURE],
 AC_CHECK_HEADERS([asm/page.h sys/user.h stdint.h])
 
 # include/lustre/lustre_user.h
-AC_CHECK_TYPES([struct if_dqinfo],[],[],[#include <linux/quota.h>])
-AC_CHECK_TYPES([struct if_dqblk],[],[],[#include <linux/quota.h>])
+# See note there re: __ASM_X86_64_PROCESSOR_H
+
+AC_CHECK_TYPES([struct if_dqinfo],[],[],[
+#define __ASM_X86_64_PROCESSOR_H
+#include <linux/quota.h>
+])
+
+AC_CHECK_TYPES([struct if_dqblk],[],[],[
+#define __ASM_X86_64_PROCESSOR_H
+#include <linux/quota.h>
+])
 
 # liblustre/llite_lib.h
 AC_CHECK_HEADERS([xtio.h file.h])
@@ -463,9 +514,11 @@ AC_DEFUN([LC_CONDITIONALS],
 AM_CONDITIONAL(EXTN, test x$enable_extN = xyes)
 AM_CONDITIONAL(LDISKFS, test x$enable_ldiskfs = xyes)
 AM_CONDITIONAL(USE_QUILT, test x$QUILT != xno)
-AM_CONDITIONAL(MPITESTS, test x$enable_mpitests = xyes, Build MPI Tests)
 AM_CONDITIONAL(LIBLUSTRE, test x$enable_liblustre = xyes)
+AM_CONDITIONAL(LIBLUSTRE_TESTS, test x$enable_liblustre_tests = xyes)
 AM_CONDITIONAL(MPITESTS, test x$enable_mpitests = xyes, Build MPI Tests)
+AM_CONDITIONAL(CLIENT, test x$enable_client = xyes)
+AM_CONDITIONAL(SERVER, test x$enable_server = xyes)
 ])
 
 #
index 98c7811..b3d7ec7 100644 (file)
@@ -1 +1 @@
-m4_define([LUSTRE_VERSION],[1.4.1.11])
+m4_define([LUSTRE_VERSION],[1.4.3.3])
index 42a7902..382021e 100644 (file)
@@ -58,6 +58,8 @@
 #include <errno.h>
 #include <sys/stat.h>
 #include <sys/vfs.h>
+#include <unistd.h>
+#include <fcntl.h>
 
 #include <libcfs/list.h>
 #include <portals/p30.h>
@@ -76,6 +78,10 @@ typedef unsigned short umode_t;
 
 #endif
 
+#ifndef CURRENT_SECONDS
+# define CURRENT_SECONDS time(0)
+#endif
+
 /* This is because lprocfs_status.h gets included here indirectly.  It would
  * be much better to just avoid lprocfs being included into liblustre entirely
  * but that requires more header surgery than I can handle right now.
@@ -86,7 +92,7 @@ typedef unsigned short umode_t;
 
 /* always adopt 2.5 definitions */
 #define KERNEL_VERSION(a,b,c) ((a)*100+(b)*10+c)
-#define LINUX_VERSION_CODE (2*200+5*10+0)
+#define LINUX_VERSION_CODE KERNEL_VERSION(2,5,0)
 
 static inline void inter_module_put(void *a)
 {
@@ -162,35 +168,38 @@ typedef int (write_proc_t)(struct file *file, const char *buffer,
 #endif /* __LITTLE_ENDIAN */
 
 /* bits ops */
-static __inline__ int set_bit(int nr,long * addr)
+
+/* a long can be more than 32 bits, so use BITS_PER_LONG
+ * to allow the compiler to adjust the bit shifting accordingly
+ */
+
+/* test if bit nr is set in bitmap addr; returns previous value of bit nr */
+static __inline__ int set_bit(int nr, long * addr)
 {
-       int     mask, retval;
+        long    mask;
 
-       addr += nr >> 5;
-       mask = 1 << (nr & 0x1f);
-       retval = (mask & *addr) != 0;
-       *addr |= mask;
-       return retval;
+        addr += nr / BITS_PER_LONG;
+        mask = 1UL << (nr & (BITS_PER_LONG - 1));
+        nr = (mask & *addr) != 0;
+        *addr |= mask;
+        return nr;
 }
 
+/* clear bit nr in bitmap addr; returns previous value of bit nr*/
 static __inline__ int clear_bit(int nr, long * addr)
 {
-       int     mask, retval;
+        long    mask;
 
-       addr += nr >> 5;
-       mask = 1 << (nr & 0x1f);
-       retval = (mask & *addr) != 0;
-       *addr &= ~mask;
-       return retval;
+        addr += nr / BITS_PER_LONG;
+        mask = 1UL << (nr & (BITS_PER_LONG - 1));
+        nr = (mask & *addr) != 0;
+        *addr &= ~mask;
+        return nr;
 }
 
 static __inline__ int test_bit(int nr, long * addr)
 {
-       int     mask;
-
-       addr += nr >> 5;
-       mask = 1 << (nr & 0x1f);
-       return ((mask & *addr) != 0);
+        return ((1UL << (nr & (BITS_PER_LONG - 1))) & ((addr)[nr / BITS_PER_LONG])) != 0;
 }
 
 static __inline__ int ext2_set_bit(int nr, void *addr)
@@ -312,14 +321,7 @@ static inline void spin_unlock_irqrestore(spinlock_t *a, unsigned long b) {}
 
 /* random */
 
-static inline void get_random_bytes(void *ptr, int size)
-{
-        int *p = (int *)ptr;
-        int i, count = size/sizeof(int);
-
-        for (i = 0; i< count; i++)
-                *p++ = rand();
-}
+void get_random_bytes(void *ptr, int size);
 
 /* memory */
 
@@ -375,6 +377,10 @@ static inline int kmem_cache_destroy(kmem_cache_t *a)
 
 /* struct page decl moved out from here into portals/include/libcfs/user-prim.h */
 
+/* 2.4 defines */
+#define PAGE_LIST_ENTRY list
+#define PAGE_LIST(page) ((page)->list)
+
 #define kmap(page) (page)->addr
 #define kunmap(a) do {} while (0)
 
@@ -552,24 +558,28 @@ struct signal {
         int signal;
 };
 
-struct fs_struct {
-        int umask;
-};
-
 struct task_struct {
-        struct fs_struct *fs;
         int state;
         struct signal pending;
         char comm[32];
         int pid;
         int fsuid;
         int fsgid;
+        int max_groups;
+        int ngroups;
+        gid_t *groups;
         __u32 cap_effective;
 };
 
 extern struct task_struct *current;
-
-#define in_group_p(a) 0 /* FIXME */
+int in_group_p(gid_t gid);
+static inline int capable(int cap)
+{
+        if (current->cap_effective & (1 << cap))
+                return 1;
+        else
+                return 0;
+}
 
 #define set_current_state(foo) do { current->state = foo; } while (0)
 
@@ -609,6 +619,7 @@ static inline int schedule_timeout(signed long t)
 }
 
 #define lock_kernel() do {} while (0)
+#define unlock_kernel() do {} while (0)
 #define daemonize(l) do {} while (0)
 #define sigfillset(l) do {} while (0)
 #define recalc_sigpending(l) do {} while (0)
@@ -682,6 +693,33 @@ typedef struct { volatile int counter; } atomic_t;
 #define unlikely(exp) (exp)
 #endif
 
+/* FIXME sys/capability will finally included linux/fs.h thus
+ * cause numerous trouble on x86-64. as temporary solution for
+ * build broken at cary, we copy definition we need from capability.h
+ * FIXME
+ */
+struct _cap_struct;
+typedef struct _cap_struct *cap_t;
+typedef int cap_value_t;
+typedef enum {
+    CAP_EFFECTIVE=0,
+    CAP_PERMITTED=1,
+    CAP_INHERITABLE=2
+} cap_flag_t;
+typedef enum {
+    CAP_CLEAR=0,
+    CAP_SET=1
+} cap_flag_value_t;
+
+#define CAP_DAC_OVERRIDE        1
+#define CAP_DAC_READ_SEARCH     2
+#define CAP_FOWNER              3
+#define CAP_FSETID              4
+#define CAP_SYS_ADMIN          21
+
+cap_t   cap_get_proc(void);
+int     cap_get_flag(cap_t, cap_value_t, cap_flag_t, cap_flag_value_t *);
+
 /* log related */
 static inline int llog_init_commit_master(void) { return 0; }
 static inline int llog_cleanup_commit_master(int force) { return 0; }
@@ -718,6 +756,62 @@ void *liblustre_register_wait_callback(int (*fn)(void *arg), void *arg);
 void liblustre_deregister_wait_callback(void *notifier);
 int liblustre_wait_event(int timeout);
 
+/* flock related */
+struct nfs_lock_info {
+        __u32             state;
+        __u32             flags;
+        void            *host;
+};
+
+struct file_lock {
+        struct file_lock *fl_next;      /* singly linked list for this inode  */
+        struct list_head fl_link;       /* doubly linked list of all locks */
+        struct list_head fl_block;      /* circular list of blocked processes */
+        void *fl_owner;
+        unsigned int fl_pid;
+        wait_queue_head_t fl_wait;
+        struct file *fl_file;
+        unsigned char fl_flags;
+        unsigned char fl_type;
+        loff_t fl_start;
+        loff_t fl_end;
+
+        void (*fl_notify)(struct file_lock *);  /* unblock callback */
+        void (*fl_insert)(struct file_lock *);  /* lock insertion callback */
+        void (*fl_remove)(struct file_lock *);  /* lock removal callback */
+
+        void *fl_fasync; /* for lease break notifications */
+        unsigned long fl_break_time;    /* for nonblocking lease breaks */
+
+        union {
+                struct nfs_lock_info    nfs_fl;       
+        } fl_u;
+};
+
+#ifndef OFFSET_MAX
+#define INT_LIMIT(x)    (~((x)1 << (sizeof(x)*8 - 1)))
+#define OFFSET_MAX      INT_LIMIT(loff_t)
+#endif
+
+/* XXX: defined in kernel */
+#define FL_POSIX        1
+#define FL_SLEEP        128
+
+/* quota */
+#define QUOTA_OK 0
+#define NO_QUOTA 1
+
+/* proc */
+#define proc_symlink(...)                       \
+({                                              \
+        void *result = NULL;                    \
+        result;                                 \
+})
+
+#ifndef ENOTSUPP
+#define ENOTSUPP ENOTSUP
+#endif
+
 #include <linux/obd_support.h>
 #include <linux/lustre_idl.h>
 #include <linux/lustre_lib.h>
index 6c0adbd..5ef1d2e 100644 (file)
@@ -128,6 +128,7 @@ extern struct proc_dir_entry *proc_lustre_root;
 
 struct obd_device;
 struct file;
+struct obd_histogram;
 
 #ifdef LPROCFS
 
@@ -259,7 +260,6 @@ extern int lprocfs_write_u64_helper(const char *buffer, unsigned long count,
                                     __u64 *val);
 int lprocfs_obd_seq_create(struct obd_device *dev, char *name, mode_t mode,
                            struct file_operations *seq_fops, void *data);
-struct obd_histogram;
 void lprocfs_oh_tally(struct obd_histogram *oh, unsigned int value);
 void lprocfs_oh_tally_log2(struct obd_histogram *oh, unsigned int value);
 void lprocfs_oh_clear(struct obd_histogram *oh);
@@ -303,7 +303,7 @@ static inline void lprocfs_free_obd_stats(struct obd_device *obddev)
 static inline struct proc_dir_entry *
 lprocfs_register(const char *name, struct proc_dir_entry *parent,
                  struct lprocfs_vars *list, void *data) { return NULL; }
-#define LPROCFS_INIT_VARS(name, vclass, vinstance) do {} while (0)
+#define LPROCFS_INIT_VARS(name, vclass, vinstance)
 #define lprocfs_init_vars(...) do {} while (0)
 static inline int lprocfs_add_vars(struct proc_dir_entry *root,
                                    struct lprocfs_vars *var,
@@ -363,6 +363,14 @@ static inline
 int lprocfs_rd_filegroups(char *page, char **start, off_t off,
                           int count, int *eof, void *data) { return 0; }
 static inline
+void lprocfs_oh_tally(struct obd_histogram *oh, unsigned int value) {}
+static inline
+void lprocfs_oh_tally_log2(struct obd_histogram *oh, unsigned int value) {}
+static inline
+void lprocfs_oh_clear(struct obd_histogram *oh) {}
+static inline
+unsigned long lprocfs_oh_sum(struct obd_histogram *oh) { return 0; }
+static inline
 int lprocfs_counter_read(char *page, char **start, off_t off,
                          int count, int *eof, void *data) { return 0; }
 static inline
index 5bc0b95..b9dee63 100644 (file)
@@ -63,6 +63,7 @@ struct lustre_cfg {
         uint32_t lcfg_num; 
         uint32_t lcfg_flags;
         uint64_t lcfg_nid;
+        uint32_t lcfg_nal;                      /* not used any more */
 
         uint32_t lcfg_bufcount;
         uint32_t lcfg_buflens[0];
index 0de68b6..35a7cde 100644 (file)
@@ -44,9 +44,8 @@
 #endif
 
 /* XXX our code should be using the 2.6 calls, not the other way around */
-#define TryLockPage(page)                TestSetPageLocked(page)
-#define filemap_fdatasync(mapping)       filemap_fdatawrite(mapping)
-#define Page_Uptodate(page)              PageUptodate(page)
+#define TryLockPage(page)               TestSetPageLocked(page)
+#define Page_Uptodate(page)             PageUptodate(page)
 #define ll_redirty_page(page)           set_page_dirty(page)
 
 #define KDEVT_INIT(val)                 (val)
@@ -64,8 +63,7 @@
 #define ll_truncate_complete_page(page) \
                                 truncate_complete_page(page->mapping, page)
 
-#define ll_vfs_create(a,b,c,d)              vfs_create(a,b,c,d)
-
+#define ll_vfs_create(a,b,c,d)          vfs_create(a,b,c,d)
 #define ll_dev_t                        dev_t
 #define kdev_t                          dev_t
 #define to_kdev_t(dev)                  (dev)
@@ -120,8 +118,8 @@ static inline int cleanup_group_info(void)
 
 #else /* 2.4.. */
 
-#ifdef HAVE_MM_INLINE 
-#include <linux/mm_inline.h> 
+#ifdef HAVE_MM_INLINE
+#include <linux/mm_inline.h>
 #endif
 
 #define ll_vfs_create(a,b,c,d)              vfs_create(a,b,c)
@@ -236,8 +234,8 @@ static inline void cond_resched(void)
 #define __set_page_ll_data(page, llap) page->private = (unsigned long)llap
 #define __clear_page_ll_data(page) page->private = 0
 #define PageWriteback(page) 0
-#define set_page_writeback(page)
-#define end_page_writeback(page)
+#define set_page_writeback(page) do {} while (0)
+#define end_page_writeback(page) do {} while (0)
 
 static inline int mapping_mapped(struct address_space *mapping)
 {
@@ -324,5 +322,9 @@ static inline int mapping_has_pages(struct address_space *mapping)
 #define grab_cache_page_nowait_gfp(x, y, z) grab_cache_page_nowait((x), (y))
 #endif
 
+#ifndef HAVE_FILEMAP_FDATAWRITE
+#define filemap_fdatawrite(mapping)      filemap_fdatasync(mapping)
+#endif
+
 #endif /* __KERNEL__ */
 #endif /* _COMPAT25_H */
index 1ca2d89..e550272 100644 (file)
@@ -42,10 +42,16 @@ do { if (offset > ASSERT_MAX_SIZE_MB << 20) {                           \
         OP;                                                             \
 }} while(0)
 
+#ifdef __KERNEL__
 #define LL_CDEBUG_PAGE(mask, page, fmt, arg...)                               \
         CDEBUG(mask, "page %p map %p index %lu flags %lx count %u priv %0lx: "\
                fmt, page, page->mapping, page->index, (long)page->flags,      \
                page_count(page), page->private, ## arg)
+#else
+#define LL_CDEBUG_PAGE(mask, page, fmt, arg...)                               \
+        CDEBUG(mask, "page %p index %lu priv %0lx: "\
+               fmt, page, page->index, page->private, ## arg)
+#endif
 
 /* lib/debug.c */
 int dump_lniobuf(struct niobuf_local *lnb);
index 8686034..fcc9a74 100644 (file)
@@ -68,6 +68,9 @@ typedef enum {
 #define LDLM_FL_WARN           0x008000 /* see ldlm_cli_cancel_unused */
 #define LDLM_FL_DISCARD_DATA   0x010000 /* discard (no writeback) on cancel */
 
+#define LDLM_FL_NO_TIMEOUT    0x020000 /* Blocked by CW lock - wait
+                                           indefinitely */
+
 /* file & record locking */
 #define LDLM_FL_BLOCK_NOWAIT   0x040000 // server told not to wait if blocked
 #define LDLM_FL_TEST_LOCK      0x080000 // return blocking lock
@@ -110,6 +113,7 @@ typedef enum {
 #define LCK_COMPAT_CW  (LCK_COMPAT_PW | LCK_CW)
 #define LCK_COMPAT_CR  (LCK_COMPAT_CW | LCK_PR | LCK_PW)
 #define LCK_COMPAT_NL  (LCK_COMPAT_CR | LCK_EX)
+#define LCK_COMPAT_GROUP  (LCK_GROUP | LCK_NL)
 
 static ldlm_mode_t lck_compat_array[] = {
         [LCK_EX] LCK_COMPAT_EX,
@@ -117,12 +121,13 @@ static ldlm_mode_t lck_compat_array[] = {
         [LCK_PR] LCK_COMPAT_PR,
         [LCK_CW] LCK_COMPAT_CW,
         [LCK_CR] LCK_COMPAT_CR,
-        [LCK_NL] LCK_COMPAT_NL
+        [LCK_NL] LCK_COMPAT_NL,
+        [LCK_GROUP] LCK_COMPAT_GROUP
 };
 
 static inline void lockmode_verify(ldlm_mode_t mode)
 {
-       LASSERT(mode >= LCK_EX && mode <= LCK_NL);
+       LASSERT(mode > LCK_MINMODE && mode < LCK_MAXMODE);
 }
 
 static inline int lockmode_compat(ldlm_mode_t exist, ldlm_mode_t new)
@@ -168,9 +173,6 @@ struct ldlm_namespace {
         struct list_head       ns_root_list; /* all root resources in ns */
         struct lustre_lock     ns_lock; /* protects hash, refcount, list */
         struct list_head       ns_list_chain; /* position in global NS list */
-        /*
-        struct proc_dir_entry *ns_proc_dir;
-        */
 
         struct list_head       ns_unused_list; /* all root resources in ns */
         int                    ns_nr_unused;
@@ -488,7 +490,11 @@ struct ldlm_namespace *ldlm_namespace_new(char *name, __u32 local);
 int ldlm_namespace_cleanup(struct ldlm_namespace *ns, int flags);
 int ldlm_namespace_free(struct ldlm_namespace *ns, int force);
 int ldlm_proc_setup(void);
+#ifdef LPROCFS
 void ldlm_proc_cleanup(void);
+#else
+static inline void ldlm_proc_cleanup(void) {}
+#endif
 
 /* resource.c - internal */
 struct ldlm_resource *ldlm_resource_get(struct ldlm_namespace *ns,
index 5136d66..012f3c8 100644 (file)
@@ -12,10 +12,10 @@ struct mds_client_data;
 
 struct mds_export_data {
         struct list_head        med_open_head;
-        spinlock_t              med_open_lock;
+        spinlock_t              med_open_lock; /* lock med_open_head, mfd_list*/
         struct mds_client_data *med_mcd;
-        loff_t                  med_off;
-        int                     med_idx;
+        loff_t                  med_lr_off;
+        int                     med_lr_idx;
 };
 
 struct osc_creator {
@@ -55,6 +55,7 @@ struct obd_export {
         atomic_t                  exp_refcount;
         struct obd_uuid           exp_client_uuid;
         struct list_head          exp_obd_chain;
+        struct list_head          exp_obd_chain_timed; /* for ping evictor */
         struct obd_device        *exp_obd;
         struct obd_import        *exp_imp_reverse; /* to make RPCs backwards */
         struct ptlrpc_connection *exp_connection;
@@ -67,6 +68,7 @@ struct obd_export {
         __u64                     exp_connect_flags;
         int                       exp_flags;
         unsigned int              exp_failed:1,
+                                  exp_disconnected:1,
                                   exp_replay_needed:1,
                                   exp_libclient:1; /* liblustre client? */
         union {
index ceb3a41..2cc850e 100644 (file)
@@ -1,7 +1,7 @@
 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
  * vim:expandtab:shiftwidth=8:tabstop=8:
  *
- *  Copyright (C) 2001 Cluster File Systems, Inc. <info@clusterfs.com>
+ *  Copyright (C) 2001-2004 Cluster File Systems, Inc. <info@clusterfs.com>
  *
  *   This file is part of Lustre, http://www.lustre.org.
  *
@@ -58,7 +58,16 @@ struct fsfilt_operations {
         int     (* fs_set_md)(struct inode *inode, void *handle, void *md,
                               int size);
         int     (* fs_get_md)(struct inode *inode, void *md, int size);
-        /* this method is needed to make IO operation fsfilt nature depend. */
+        /*
+         * this method is needed to make IO operation fsfilt nature depend.
+         *
+         * This operation maybe synchronous or asynchronous.
+         *
+         * Return convention: positive number of bytes written (synchronously)
+         * on success. Negative errno value on failure. Zero if asynchronous
+         * IO was submitted successfully.
+         *
+         */
         int     (* fs_send_bio)(int rw, struct inode *inode,struct kiobuf *bio);
         ssize_t (* fs_readpage)(struct file *file, char *buf, size_t count,
                                 loff_t *offset);
@@ -82,7 +91,7 @@ struct fsfilt_operations {
                                   struct obd_quotactl *oqctl);
         int     (* fs_quotactl)(struct super_block *sb,
                                 struct obd_quotactl *oqctl);
-        int     (* fs_quotainfo)(struct lustre_quota_info *lqi, int type, 
+        int     (* fs_quotainfo)(struct lustre_quota_info *lqi, int type,
                                  int cmd);
         int     (* fs_dquot)(struct lustre_dquot *dquot, int cmd);
 };
@@ -253,7 +262,7 @@ static inline int fsfilt_send_bio(int rw, struct obd_device *obd,
                                   struct inode *inode, void *bio)
 {
         LASSERTF(rw == OBD_BRW_WRITE || rw == OBD_BRW_READ, "%x\n", rw);
-        
+
         if (rw == OBD_BRW_READ)
                 return obd->obd_fsops->fs_send_bio(READ, inode, bio);
         return obd->obd_fsops->fs_send_bio(WRITE, inode, bio);
@@ -301,27 +310,35 @@ static inline int fsfilt_quotacheck(struct obd_device *obd,
                                     struct super_block *sb,
                                     struct obd_quotactl *oqctl)
 {
-       return obd->obd_fsops->fs_quotacheck(sb, oqctl);
+        if (obd->obd_fsops->fs_quotacheck)
+                return obd->obd_fsops->fs_quotacheck(sb, oqctl);
+        return -ENOTSUPP;
 }
 
 static inline int fsfilt_quotactl(struct obd_device *obd,
                                   struct super_block *sb,
                                   struct obd_quotactl *oqctl)
 {
-       return obd->obd_fsops->fs_quotactl(sb, oqctl);
+        if (obd->obd_fsops->fs_quotactl)
+                return obd->obd_fsops->fs_quotactl(sb, oqctl);
+        return -ENOTSUPP;
 }
 
 static inline int fsfilt_quotainfo(struct obd_device *obd,
                                    struct lustre_quota_info *lqi,
                                    int type, int cmd)
 {
-        return obd->obd_fsops->fs_quotainfo(lqi, type, cmd);
+        if (obd->obd_fsops->fs_quotainfo)
+                return obd->obd_fsops->fs_quotainfo(lqi, type, cmd);
+        return -ENOTSUPP;
 }
 
 static inline int fsfilt_dquot(struct obd_device *obd,
                                struct lustre_dquot *dquot, int cmd)
 {
-        return obd->obd_fsops->fs_dquot(dquot, cmd);
+        if (obd->obd_fsops->fs_dquot)
+                return obd->obd_fsops->fs_dquot(dquot, cmd);
+        return -ENOTSUPP;
 }
 
 static inline int fsfilt_map_inode_pages(struct obd_device *obd,
index 8b7bef4..5083b94 100644 (file)
@@ -23,6 +23,5 @@ void ptlrpc_activate_import(struct obd_import *imp);
 void ptlrpc_deactivate_import(struct obd_import *imp);
 void ptlrpc_invalidate_import(struct obd_import *imp);
 void ptlrpc_fail_import(struct obd_import *imp, int generation);
-void ptlrpc_fail_export(struct obd_export *exp);
 
 #endif
index 9c6939c..9e676ab 100644 (file)
@@ -197,6 +197,7 @@ static inline void lustre_msg_set_op_flags(struct lustre_msg *msg, int flags)
 //#define MSG_CONNECT_PEER        0x8
 #define MSG_CONNECT_LIBCLIENT   0x10
 #define MSG_CONNECT_INITIAL     0x20
+#define MSG_CONNECT_ASYNC       0x40
 
 /* Connect flags */
 
@@ -369,11 +370,11 @@ struct lov_mds_md_v1 {            /* LOV EA mds/wire data (little-endian) */
 #define OBD_MD_MDS        (0x100000000ULL) /* where an inode lives on */
 #define OBD_MD_REINT      (0x200000000ULL) /* reintegrate oa */
 
-#define OBD_MD_FLNOTOBD (~(OBD_MD_FLBLOCKS | OBD_MD_LINKNAME|\
-                           OBD_MD_FLEASIZE | OBD_MD_FLHANDLE | OBD_MD_FLCKSUM|\
-                           OBD_MD_FLQOS | OBD_MD_FLOSCOPQ | OBD_MD_FLCOOKIE|\
-                           OBD_MD_FLUSRQUOTA | OBD_MD_FLGRPQUOTA))
-
+#define OBD_MD_FLGETATTR (OBD_MD_FLID    | OBD_MD_FLATIME | OBD_MD_FLMTIME | \
+                          OBD_MD_FLCTIME | OBD_MD_FLSIZE  | OBD_MD_FLBLKSZ | \
+                          OBD_MD_FLMODE  | OBD_MD_FLTYPE  | OBD_MD_FLUID   | \
+                          OBD_MD_FLGID   | OBD_MD_FLFLAGS | OBD_MD_FLNLINK | \
+                          OBD_MD_FLGENER | OBD_MD_FLRDEV  | OBD_MD_FLGROUP)
 
 static inline struct lustre_handle *obdo_handle(struct obdo *oa)
 {
@@ -499,6 +500,8 @@ typedef enum {
         REINT_UNLINK   = 4,
         REINT_RENAME   = 5,
         REINT_OPEN     = 6,
+//      REINT_CLOSE    = 7,
+//      REINT_WRITE    = 8,
         REINT_MAX
 } mds_reint_t;
 
@@ -731,7 +734,8 @@ extern void lustre_swab_mds_rec_rename (struct mds_rec_rename *rn);
  *  LOV data structures
  */
 
-#define LOV_MIN_STRIPE_SIZE 65536UL /* maximum PAGE_SIZE (ia64), power of 2 */
+#define LOV_MIN_STRIPE_SIZE 65536   /* maximum PAGE_SIZE (ia64), power of 2 */
+#define LOV_MAX_STRIPE_COUNT  160   /* until bug 4424 is fixed */
 
 #define LOV_MAX_UUID_BUFFER_SIZE  8192
 /* The size of the buffer the lov/mdc reserves for the
@@ -782,18 +786,22 @@ extern void lustre_swab_ldlm_res_id (struct ldlm_res_id *id);
 
 /* lock types */
 typedef enum {
+        LCK_MINMODE = 0,
         LCK_EX = 1,
         LCK_PW = 2,
         LCK_PR = 4,
         LCK_CW = 8,
         LCK_CR = 16,
-        LCK_NL = 32
+        LCK_NL = 32,
+        LCK_GROUP = 64,
+        LCK_MAXMODE
 } ldlm_mode_t;
 
 typedef enum {
         LDLM_PLAIN     = 10,
         LDLM_EXTENT    = 11,
         LDLM_FLOCK     = 12,
+//      LDLM_IBITS     = 13,
         LDLM_MAX_TYPE
 } ldlm_type_t;
 
@@ -802,6 +810,7 @@ typedef enum {
 struct ldlm_extent {
         __u64 start;
         __u64 end;
+        __u64 gid;
 };
 
 struct ldlm_flock {
@@ -961,7 +970,7 @@ typedef enum {
         MDS_UNLINK_REC   = LLOG_OP_MAGIC | 0x10000 | (MDS_REINT << 8) | REINT_UNLINK,
         MDS_SETATTR_REC  = LLOG_OP_MAGIC | 0x10000 | (MDS_REINT << 8) | REINT_SETATTR,
         OBD_CFG_REC      = LLOG_OP_MAGIC | 0x20000,
-//      unused           = LLOG_OP_MAGIC | 0x30000,
+        PTL_CFG_REC      = LLOG_OP_MAGIC | 0x30000, /* obsolete */
         LLOG_GEN_REC     = LLOG_OP_MAGIC | 0x40000,
         LLOG_HDR_MAGIC   = LLOG_OP_MAGIC | 0x45539,
         LLOG_LOGID_MAGIC = LLOG_OP_MAGIC | 0x4553b,
@@ -1139,10 +1148,10 @@ static inline struct ll_fid *obdo_fid(struct obdo *oa)
 
 /* qutoa */
 struct qunit_data {
-       __u32 qd_id;
-       __u32 qd_type;
-       __u32 qd_count;
-       __u32 qd_isblk; /* indicating if it's block quota */
+        __u32 qd_id;
+        __u32 qd_type;
+        __u32 qd_count;
+        __u32 qd_isblk; /* indicating if it's block quota */
 };
 extern void lustre_swab_qdata(struct qunit_data *d);
 
index 2bc042c..b2baf7e 100644 (file)
@@ -35,9 +35,7 @@
 # include <linux/signal.h>
 # include <linux/types.h>
 #endif
-#include <portals/p30.h>
-#include <libcfs/portals_lib.h>
-#include <libcfs/kp30.h> /* XXX just for LASSERT! */
+#include <libcfs/kp30.h>
 #include <linux/lustre_idl.h>
 #include <linux/lustre_cfg.h>
 
 #endif
 
 #ifndef LPU64
-/* x86_64 has 64bit longs and defines u64 as long long */
-#if BITS_PER_LONG > 32 && !defined(__x86_64__)
-#define LPU64 "%lu"
-#define LPD64 "%ld"
-#define LPX64 "%#lx"
-#else
-#define LPU64 "%Lu"
-#define LPD64 "%Ld"
-#define LPX64 "%#Lx"
+/* x86_64 defines __u64 as "long" in userspace, but "long long" in the kernel */
+#if defined(__x86_64__) && defined(__KERNEL__)
+# define LPU64 "%Lu"
+# define LPD64 "%Ld"
+# define LPX64 "%#Lx"
+# define LPSZ  "%lu"
+# define LPSSZ "%ld"
+#elif (BITS_PER_LONG == 32 || __WORDSIZE == 32)
+# define LPU64 "%Lu"
+# define LPD64 "%Ld"
+# define LPX64 "%#Lx"
+# define LPSZ  "%u"
+# define LPSSZ "%d"
+#elif (BITS_PER_LONG == 64 || __WORDSIZE == 64)
+# define LPU64 "%lu"
+# define LPD64 "%ld"
+# define LPX64 "%#lx"
+# define LPSZ  "%lu"
+# define LPSSZ "%ld"
+#endif
+#ifndef LPU64
+# error "No word size defined"
 #endif
 #endif
 
@@ -84,9 +95,14 @@ int target_handle_reconnect(struct lustre_handle *conn, struct obd_export *exp,
 int target_handle_ping(struct ptlrpc_request *req);
 void target_committed_to_req(struct ptlrpc_request *req);
 
+#ifdef HAVE_QUOTA_SUPPORT
 /* quotacheck callback, dqacq/dqrel callback handler */
 int target_handle_qc_callback(struct ptlrpc_request *req);
 int target_handle_dqacq_callback(struct ptlrpc_request *req);
+#else
+#define target_handle_dqacq_callback(req) ldlm_callback_reply(req, -ENOTSUPP)
+#define target_handle_qc_callback(req) (0)
+#endif
 
 void target_cancel_recovery_timer(struct obd_device *obd);
 
@@ -194,59 +210,59 @@ static inline int obd_ioctl_packlen(struct obd_ioctl_data *data)
 static inline int obd_ioctl_is_invalid(struct obd_ioctl_data *data)
 {
         if (data->ioc_len > (1<<30)) {
-                printk("LustreError: OBD ioctl: ioc_len larger than 1<<30\n");
+                CERROR("OBD ioctl: ioc_len larger than 1<<30\n");
                 return 1;
         }
         if (data->ioc_inllen1 > (1<<30)) {
-                printk("LustreError: OBD ioctl: ioc_inllen1 larger than 1<<30\n");
+                CERROR("OBD ioctl: ioc_inllen1 larger than 1<<30\n");
                 return 1;
         }
         if (data->ioc_inllen2 > (1<<30)) {
-                printk("LustreError: OBD ioctl: ioc_inllen2 larger than 1<<30\n");
+                CERROR("OBD ioctl: ioc_inllen2 larger than 1<<30\n");
                 return 1;
         }
         if (data->ioc_inllen3 > (1<<30)) {
-                printk("LustreError: OBD ioctl: ioc_inllen3 larger than 1<<30\n");
+                CERROR("OBD ioctl: ioc_inllen3 larger than 1<<30\n");
                 return 1;
         }
         if (data->ioc_inllen4 > (1<<30)) {
-                printk("LustreError: OBD ioctl: ioc_inllen4 larger than 1<<30\n");
+                CERROR("OBD ioctl: ioc_inllen4 larger than 1<<30\n");
                 return 1;
         }
         if (data->ioc_inlbuf1 && !data->ioc_inllen1) {
-                printk("LustreError: OBD ioctl: inlbuf1 pointer but 0 length\n");
+                CERROR("OBD ioctl: inlbuf1 pointer but 0 length\n");
                 return 1;
         }
         if (data->ioc_inlbuf2 && !data->ioc_inllen2) {
-                printk("LustreError: OBD ioctl: inlbuf2 pointer but 0 length\n");
+                CERROR("OBD ioctl: inlbuf2 pointer but 0 length\n");
                 return 1;
         }
         if (data->ioc_inlbuf3 && !data->ioc_inllen3) {
-                printk("LustreError: OBD ioctl: inlbuf3 pointer but 0 length\n");
+                CERROR("OBD ioctl: inlbuf3 pointer but 0 length\n");
                 return 1;
         }
         if (data->ioc_inlbuf4 && !data->ioc_inllen4) {
-                printk("LustreError: OBD ioctl: inlbuf4 pointer but 0 length\n");
+                CERROR("OBD ioctl: inlbuf4 pointer but 0 length\n");
                 return 1;
         }
         if (data->ioc_pbuf1 && !data->ioc_plen1) {
-                printk("LustreError: OBD ioctl: pbuf1 pointer but 0 length\n");
+                CERROR("OBD ioctl: pbuf1 pointer but 0 length\n");
                 return 1;
         }
         if (data->ioc_pbuf2 && !data->ioc_plen2) {
-                printk("LustreError: OBD ioctl: pbuf2 pointer but 0 length\n");
+                CERROR("OBD ioctl: pbuf2 pointer but 0 length\n");
                 return 1;
         }
         if (data->ioc_plen1 && !data->ioc_pbuf1) {
-                printk("LustreError: OBD ioctl: plen1 set but NULL pointer\n");
+                CERROR("OBD ioctl: plen1 set but NULL pointer\n");
                 return 1;
         }
         if (data->ioc_plen2 && !data->ioc_pbuf2) {
-                printk("LustreError: OBD ioctl: plen2 set but NULL pointer\n");
+                CERROR("OBD ioctl: plen2 set but NULL pointer\n");
                 return 1;
         }
         if (obd_ioctl_packlen(data) != data->ioc_len) {
-                printk("LustreError: OBD ioctl: packlen exceeds ioc_len (%d != %d)\n",
+                CERROR("OBD ioctl: packlen exceeds ioc_len (%d != %d)\n",
                        obd_ioctl_packlen(data), data->ioc_len);
                 return 1;
         }
@@ -331,25 +347,23 @@ static inline int obd_ioctl_getdata(char **buf, int *len, void *arg)
         ENTRY;
 
         err = copy_from_user(&hdr, (void *)arg, sizeof(hdr));
-        if ( err ) {
-                EXIT;
-                return err;
-        }
+        if (err) 
+                RETURN(err);
 
         if (hdr.ioc_version != OBD_IOCTL_VERSION) {
                 CERROR("Version mismatch kernel vs application\n");
-                return -EINVAL;
+                RETURN(-EINVAL);
         }
 
         if (hdr.ioc_len > OBD_MAX_IOCTL_BUFFER) {
                 CERROR("User buffer len %d exceeds %d max buffer\n",
                        hdr.ioc_len, OBD_MAX_IOCTL_BUFFER);
-                return -EINVAL;
+                RETURN(-EINVAL);
         }
 
         if (hdr.ioc_len < sizeof(struct obd_ioctl_data)) {
-                printk("LustreError: OBD: user buffer too small for ioctl\n");
-                return -EINVAL;
+                CERROR("user buffer too small for ioctl (%d)\n", hdr.ioc_len);
+                RETURN(-EINVAL);
         }
 
         /* XXX allocate this more intelligently, using kmalloc when
@@ -364,14 +378,15 @@ static inline int obd_ioctl_getdata(char **buf, int *len, void *arg)
         data = (struct obd_ioctl_data *)*buf;
 
         err = copy_from_user(*buf, (void *)arg, hdr.ioc_len);
-        if ( err ) {
-                EXIT;
-                return err;
+        if (err) {
+                OBD_VFREE(*buf, hdr.ioc_len);
+                RETURN(err);
         }
 
         if (obd_ioctl_is_invalid(data)) {
                 CERROR("ioctl not correctly formatted\n");
-                return -EINVAL;
+                OBD_VFREE(*buf, hdr.ioc_len);
+                RETURN(-EINVAL);
         }
 
         if (data->ioc_inllen1) {
@@ -393,8 +408,7 @@ static inline int obd_ioctl_getdata(char **buf, int *len, void *arg)
                 data->ioc_inlbuf4 = &data->ioc_bulk[0] + offset;
         }
 
-        EXIT;
-        return 0;
+        RETURN(0);
 }
 
 static inline void obd_ioctl_freedata(char *buf, int len)
@@ -430,6 +444,7 @@ static inline void obd_ioctl_freedata(char *buf, int len)
 #define OBD_IOC_BRW_WRITE              _IOWR('f', 126, long)
 #define OBD_IOC_NAME2DEV               _IOWR('f', 127, long)
 #define OBD_IOC_UUID2DEV               _IOWR('f', 130, long)
+#define OBD_IOC_GETNAME                _IOR ('f', 131, long)
 
 #define OBD_IOC_LOV_GET_CONFIG         _IOWR('f', 132, long)
 #define OBD_IOC_CLIENT_RECOVER         _IOW ('f', 133, long)
@@ -482,26 +497,8 @@ static inline void obd_ioctl_freedata(char *buf, int len)
  * we define this to be 2T - 4k, which is the ext3 maxbytes. */
 #define LUSTRE_STRIPE_MAXBYTES 0x1fffffff000ULL
 
-#define CHECKSUM_CHUNK 4096
-#define CHECKSUM_BULK 0
 #define POISON_BULK 0
 
-#if CHECKSUM_BULK
-static inline void ost_checksum(obd_count *cksum,int *psum, void *addr, int len)
-{
-        unsigned char *ptr = (unsigned char *)addr;
-        int sum = 0;
-
-        /* very stupid, but means I don't have to think about byte order */
-        while (len-- > 0)
-                sum += *ptr++;
-
-        *cksum = (*cksum << 2) + sum;
-        if (psum)
-                *psum = sum;
-}
-#endif
-
 static inline int ll_insecure_random_int(void)
 {
         struct timeval t;
@@ -675,13 +672,29 @@ do {                                                                           \
 #else /* !__KERNEL__ */
 #define __l_wait_event(wq, condition, info, ret, excl)                         \
 do {                                                                           \
+        long timeout = info->lwi_timeout, elapse, last = 0;                    \
         int __timed_out = 0;                                                   \
                                                                                \
+        if (info->lwi_timeout == 0)                                            \
+            timeout = 1000000000;                                              \
+        else                                                                   \
+            last = time(NULL);                                                 \
+                                                                               \
         for (;;) {                                                             \
             if (condition)                                                     \
                 break;                                                         \
-            if (liblustre_wait_event(info->lwi_timeout))                       \
+            if (liblustre_wait_event(timeout)) {                               \
+                if (timeout == 0 || info->lwi_timeout == 0)                    \
+                        continue;                                              \
+                elapse = time(NULL) - last;                                    \
+                if (elapse) {                                                  \
+                        last += elapse;                                        \
+                        timeout -= elapse;                                     \
+                        if (timeout < 0)                                       \
+                                timeout = 0;                                   \
+                }                                                              \
                 continue;                                                      \
+            }                                                                  \
             if (info->lwi_timeout && !__timed_out) {                           \
                 __timed_out = 1;                                               \
                 if (info->lwi_on_timeout == NULL ||                            \
index c5c045d..094eafe 100644 (file)
@@ -69,6 +69,11 @@ enum {
 #include <linux/lustre_idl.h>
 #endif /* __KERNEL__ */
 
+#define LLAP_FROM_COOKIE(c)                                                    \
+        (LASSERT(((struct ll_async_page *)(c))->llap_magic == LLAP_MAGIC),     \
+         (struct ll_async_page *)(c))
+#define LL_MAX_BLKSIZE          (4UL * 1024 * 1024)
+
 #include <lustre/lustre_user.h>
 
 #endif
index b6d30be..b3b23e8 100644 (file)
@@ -68,19 +68,19 @@ struct mds_update_record {
         int ur_cookielen;
         struct llog_cookie *ur_logcookies;
         struct iattr ur_iattr;
-        struct obd_ucred ur_uc;
+        struct lvfs_ucred ur_uc;
         __u64 ur_rdev;
         __u64 ur_time;
         __u32 ur_mode;
         __u32 ur_flags;
 };
 
-#define ur_fsuid    ur_uc.ouc_fsuid
-#define ur_fsgid    ur_uc.ouc_fsgid
-#define ur_cap      ur_uc.ouc_cap
-#define ur_suppgid1 ur_uc.ouc_suppgid1
-#define ur_suppgid2 ur_uc.ouc_suppgid2
-#define ur_umask    ur_uc.ouc_umask
+#define ur_fsuid    ur_uc.luc_fsuid
+#define ur_fsgid    ur_uc.luc_fsgid
+#define ur_cap      ur_uc.luc_cap
+#define ur_suppgid1 ur_uc.luc_suppgid1
+#define ur_suppgid2 ur_uc.luc_suppgid2
+#define ur_umask    ur_uc.luc_umask
 
 #define MDS_LR_SERVER_SIZE    512
 
@@ -131,13 +131,13 @@ struct mds_client_data {
 struct mds_file_data {
         struct portals_handle mfd_handle; /* must be first */
         atomic_t              mfd_refcount;
-        struct list_head      mfd_list;
+        struct list_head      mfd_list; /* protected by med_open_lock */
         __u64                 mfd_xid;
         int                   mfd_mode;
         struct dentry        *mfd_dentry;
 };
 
-/* mds/mds_reint.c  */
+/* mds/mds_reint.c */
 int mds_reint_rec(struct mds_update_record *r, int offset,
                   struct ptlrpc_request *req, struct lustre_handle *);
 
index 4445a57..5003d73 100644 (file)
@@ -71,6 +71,7 @@
 
 /* ...and make consistent... */
 
+#ifdef __KERNEL__
 #if (PTLRPC_MAX_BRW_SIZE > PTLRPC_MAX_BRW_PAGES * PAGE_SIZE)
 # undef  PTLRPC_MAX_BRW_SIZE
 # define PTLRPC_MAX_BRW_SIZE   (PTLRPC_MAX_BRW_PAGES * PAGE_SIZE)
 #if ((PTLRPC_MAX_BRW_PAGES & (PTLRPC_MAX_BRW_PAGES - 1)) != 0)
 #error "PTLRPC_MAX_BRW_PAGES isn't a power of two"
 #endif
+#else /* !__KERNEL__ */
+/* PAGE_SIZE isn't a constant, can't use CPP on it.  We assume that the
+ * limit is on the number of pages for large pages, which is currently true. */
+# undef  PTLRPC_MAX_BRW_PAGES
+# define PTLRPC_MAX_BRW_PAGES  (PTLRPC_MAX_BRW_SIZE / PAGE_SIZE)
+#endif /* __KERNEL__ */
 
 /* Size over which to OBD_VMALLOC() rather than OBD_ALLOC() service request
  * buffers */
-#define SVC_BUF_VMALLOC_THRESHOLD (2*PAGE_SIZE)
+#define SVC_BUF_VMALLOC_THRESHOLD (2 * PAGE_SIZE)
 
 /* The following constants determine how memory is used to buffer incoming
  * service requests.
@@ -267,6 +274,14 @@ struct ptlrpc_reply_state {
         struct lustre_msg      rs_msg;
 };
 
+enum rq_phase {
+        RQ_PHASE_NEW         = 0xebc0de00,
+        RQ_PHASE_RPC         = 0xebc0de01,
+        RQ_PHASE_BULK        = 0xebc0de02,
+        RQ_PHASE_INTERPRET   = 0xebc0de03,
+        RQ_PHASE_COMPLETE    = 0xebc0de04,
+};
+
 struct ptlrpc_request {
         int rq_type; /* one of PTL_RPC_MSG_* */
         struct list_head rq_list;
@@ -279,7 +294,7 @@ struct ptlrpc_request {
                 rq_timedout:1, rq_resend:1, rq_restart:1, rq_replay:1,
                 rq_no_resend:1, rq_waiting:1, rq_receiving_reply:1,
                 rq_no_delay:1, rq_net_err:1;
-        int rq_phase;
+        enum rq_phase rq_phase; /* one of RQ_PHASE_* */
         atomic_t rq_refcount;   /* client-side refcount for SENT race */
 
         int rq_request_portal;  /* XXX FIXME bug 249 */
@@ -313,7 +328,10 @@ struct ptlrpc_request {
         struct timeval       rq_arrival_time;       /* request arrival time */
         struct ptlrpc_reply_state *rq_reply_state;  /* separated reply state */
         struct ptlrpc_request_buffer_desc *rq_rqbd; /* incoming request buffer*/
-
+#if CRAY_PORTALS
+        ptl_uid_t            rq_uid;            /* peer uid, used in MDS only */
+#endif
+        
         /* client-only incoming reply */
         ptl_handle_md_t      rq_reply_md_h;
         wait_queue_head_t    rq_reply_waitq;
@@ -338,13 +356,6 @@ struct ptlrpc_request {
         void *rq_ptlrpcd_data;
 };
 
-
-#define RQ_PHASE_NEW           0xebc0de00
-#define RQ_PHASE_RPC           0xebc0de01
-#define RQ_PHASE_BULK          0xebc0de02
-#define RQ_PHASE_INTERPRET     0xebc0de03
-#define RQ_PHASE_COMPLETE      0xebc0de04
-
 static inline const char *
 ptlrpc_rqphase2str(struct ptlrpc_request *req)
 {
@@ -378,9 +389,8 @@ ptlrpc_rqphase2str(struct ptlrpc_request *req)
 
 #define REQ_FLAGS_FMT "%s:%s%s%s%s%s%s%s%s%s"
 
-#define DEBUG_REQ(level, req, fmt, args...)                                    \
-do {                                                                           \
-CDEBUG(level, "@@@ " fmt                                                       \
+#define __DEBUG_REQ(CDEB_TYPE, level, req, fmt, args...)                       \
+CDEB_TYPE(level, "@@@ " fmt                                                    \
        " req@%p x"LPD64"/t"LPD64" o%d->%s@%s:%d lens %d/%d ref %d fl "         \
        REQ_FLAGS_FMT"/%x/%x rc %d/%d\n" , ## args, req, req->rq_xid,           \
        req->rq_transno,                                                        \
@@ -395,7 +405,15 @@ CDEBUG(level, "@@@ " fmt                                                       \
        DEBUG_REQ_FLAGS(req),                                                   \
        req->rq_reqmsg ? req->rq_reqmsg->flags : 0,                             \
        req->rq_repmsg ? req->rq_repmsg->flags : 0,                             \
-       req->rq_status, req->rq_repmsg ? req->rq_repmsg->status : 0);           \
+       req->rq_status, req->rq_repmsg ? req->rq_repmsg->status : 0)
+
+/* for most callers (level is a constant) this is resolved at compile time */
+#define DEBUG_REQ(level, req, fmt, args...)                                    \
+do {                                                                           \
+        if ((level) & (D_ERROR | D_WARNING))                                   \
+            __DEBUG_REQ(CDEBUG_LIMIT, level, req, fmt, ## args);               \
+        else                                                                   \
+            __DEBUG_REQ(CDEBUG, level, req, fmt, ## args);                     \
 } while (0)
 
 struct ptlrpc_bulk_page {
@@ -718,12 +736,12 @@ int ptlrpcd_addref(void);
 void ptlrpcd_decref(void);
 
 /* ptlrpc/lproc_ptlrpc.c */
-#ifdef __KERNEL__
-void ptlrpc_lprocfs_register_obd(struct obd_device *obddev);
-void ptlrpc_lprocfs_unregister_obd(struct obd_device *obddev);
+#ifdef LPROCFS
+void ptlrpc_lprocfs_register_obd(struct obd_device *obd);
+void ptlrpc_lprocfs_unregister_obd(struct obd_device *obd);
 #else
-#define ptlrpc_lprocfs_register_obd(param...) do{}while(0)
-#define ptlrpc_lprocfs_unregister_obd(param...) do{}while(0)
+static inline void ptlrpc_lprocfs_register_obd(struct obd_device *obd) {}
+static inline void ptlrpc_lprocfs_unregister_obd(struct obd_device *obd) {}
 #endif
 
 /* ptlrpc/llog_server.c */
index 85fa3a2..6e0355a 100644 (file)
@@ -4,80 +4,24 @@
 #ifndef _LUSTRE_QUOTA_H
 #define _LUSTRE_QUOTA_H
 
-#include <linux/version.h>
-#include <linux/quota.h>
+#ifdef __KERNEL__
+# include <linux/version.h>
+#endif
 #include <linux/lustre_idl.h>
 
-/* XXX disable amdin quotafile delete dquot temporarily */
-#define QFMT_NO_DELETE 1
-
-#define QUSG(count, isblk)      (isblk ? toqb(count) : count)
-
-/* If the (quota limit < qunit * slave count), the slave which can't
- * acquire qunit should set it's local limit as MIN_QLIMIT */
-#define MIN_QLIMIT      1
-
-#ifndef NR_DQHASH
-#define NR_DQHASH 45
-#endif
+#ifdef HAVE_QUOTA_SUPPORT
+#include <linux/lustre_realquota.h>
+#else
 
-/* structures to access admin quotafile */
 struct lustre_mem_dqinfo {
-        unsigned int dqi_bgrace;
-        unsigned int dqi_igrace;
-        unsigned long dqi_flags;
-        unsigned int dqi_blocks;
-        unsigned int dqi_free_blk;
-        unsigned int dqi_free_entry;
 };
 
 struct lustre_quota_info {
-        struct semaphore qi_sem;
-        struct file *qi_files[MAXQUOTAS];
-        struct lustre_mem_dqinfo qi_info[MAXQUOTAS];
 };
 
-#ifdef __KERNEL__
 struct lustre_dquot {
-        struct list_head dq_hash;
-        struct list_head dq_unused;
-
-        /* this semaphore is unused until we implement wb dquot cache */
-        struct semaphore dq_sem;
-        atomic_t dq_refcnt;
-
-        struct lustre_quota_info *dq_info;
-        loff_t dq_off;
-        unsigned int dq_id;
-        int dq_type;
-        unsigned long dq_flags;
-        struct mem_dqblk dq_dqb;
 };
-#endif
 
-#define QFILE_CHK               1
-#define QFILE_RD_INFO           2
-#define QFILE_WR_INFO           3
-#define QFILE_INIT_INFO         4
-#define QFILE_RD_DQUOT          5
-#define QFILE_WR_DQUOT          6
-
-/* admin quotafile operations */
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
-int lustre_check_quota_file(struct lustre_quota_info *lqi, int type);
-int lustre_read_quota_info(struct lustre_quota_info *lqi, int type);
-int lustre_write_quota_info(struct lustre_quota_info *lqi, int type);
-#ifdef __KERNEL__
-int lustre_read_dquot(struct lustre_dquot *dquot);
-int lustre_commit_dquot(struct lustre_dquot *dquot);
-#endif
-int lustre_init_quota_info(struct lustre_quota_info *lqi, int type);
-
-#else
-
-#ifndef DQ_FAKE_B
-#define DQ_FAKE_B       6
-#endif
 
 static inline int lustre_check_quota_file(struct lustre_quota_info *lqi,
                                           int type)
@@ -109,42 +53,19 @@ static inline int lustre_init_quota_info(struct lustre_quota_info *lqi,
 {
         return 0;
 }
-#endif                          /* KERNEL_VERSION(2,5,0) */
 
-/* quota context structures */
 struct obd_device;
+
 typedef int (*dqacq_handler_t) (struct obd_device * obd, struct qunit_data * qd,
                                 int opc);
 
 struct lustre_quota_ctxt {
-        struct super_block *lqc_sb;
-        struct obd_import *lqc_import;
-        dqacq_handler_t lqc_handler;
-        unsigned long lqc_flags;
-        unsigned long lqc_iunit_sz;
-        unsigned long lqc_itune_sz;
-        unsigned long lqc_bunit_sz;
-        unsigned long lqc_btune_sz;
 };
 
 struct lustre_qunit {
-        struct list_head lq_hash;
-        atomic_t lq_refcnt;
-        struct lustre_quota_ctxt *lq_ctxt;
-        struct qunit_data lq_data;
-        unsigned int lq_opc;
-        struct list_head lq_waiters;
 };
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
-int qctxt_init(struct lustre_quota_ctxt *qctxt, struct super_block *sb,
-               dqacq_handler_t handler);
-void qctxt_cleanup(struct lustre_quota_ctxt *qctxt, int force);
-int qctxt_adjust_qunit(struct obd_device *obd, struct lustre_quota_ctxt *qctxt,
-                       uid_t uid, gid_t gid, __u32 isblk);
-int qctxt_wait_on_dqacq(struct obd_device *obd, struct lustre_quota_ctxt *qctxt,
-                        uid_t uid, gid_t gid, __u32 isblk);
-#else
+struct super_block;
 static inline int qctxt_init(struct lustre_quota_ctxt *qctxt,
                              struct super_block *sb, dqacq_handler_t handler)
 {
@@ -166,18 +87,11 @@ static inline int qctxt_wait_on_dqacq(struct obd_device *obd,
 {
         return 0;
 }
-#endif                          /* KERNEL_VERSION(2,5,0) */
-
-/* quota check & quotactl */
-#define LUSTRE_ADMIN_QUOTAFILES {\
-       "admin_quotafile.usr",  /* user admin quotafile */\
-       "admin_quotafile.grp"   /* group admin quotafile */\
-}
 
 struct quotacheck_info {
-        struct completion qi_starting;
-        struct obd_export *qi_exp;
-        struct obd_quotactl qi_oqctl;
 };
 
+#define LL_DQUOT_OFF(sb) do {} while(0)
+
+#endif /*!HAVE_QUOTA_SUPPORT */
 #endif                          /* _LUSTRE_QUOTA_H */
index e645441..2862b8d 100644 (file)
 #endif
 
 /* simple.c */
-struct obd_ucred {
-        __u32 ouc_fsuid;
-        __u32 ouc_fsgid;
-        __u32 ouc_cap;
-        __u32 ouc_suppgid1;
-        __u32 ouc_suppgid2;
-        __u32 ouc_umask;
+struct lvfs_ucred {
+        __u32 luc_fsuid;
+        __u32 luc_fsgid;
+        __u32 luc_cap;
+        __u32 luc_suppgid1;
+        __u32 luc_suppgid2;
+        __u32 luc_umask;
 };
 
 struct lvfs_callback_ops {
@@ -52,11 +52,11 @@ struct lvfs_callback_ops {
 
 #define OBD_RUN_CTXT_MAGIC      0xC0FFEEAA
 #define OBD_CTXT_DEBUG          /* development-only debugging */
-struct obd_run_ctxt {
+struct lvfs_run_ctxt {
         struct vfsmount *pwdmnt;
         struct dentry   *pwd;
         mm_segment_t     fs;
-        struct obd_ucred ouc;
+        struct lvfs_ucred luc;
         int              ngroups;
         struct lvfs_callback_ops cb_ops;
 #ifdef OBD_CTXT_DEBUG
@@ -71,12 +71,12 @@ struct obd_run_ctxt {
 #endif
 
 /* lvfs_common.c */
-struct dentry *lvfs_fid2dentry(struct obd_run_ctxt *, __u64, __u32, __u64 ,void *data);
+struct dentry *lvfs_fid2dentry(struct lvfs_run_ctxt *, __u64, __u32, __u64 ,void *data);
 
-void push_ctxt(struct obd_run_ctxt *save, struct obd_run_ctxt *new_ctx,
-               struct obd_ucred *cred);
-void pop_ctxt(struct obd_run_ctxt *saved, struct obd_run_ctxt *new_ctx,
-              struct obd_ucred *cred);
+void push_ctxt(struct lvfs_run_ctxt *save, struct lvfs_run_ctxt *new_ctx,
+               struct lvfs_ucred *cred);
+void pop_ctxt(struct lvfs_run_ctxt *saved, struct lvfs_run_ctxt *new_ctx,
+              struct lvfs_ucred *cred);
 
 #ifdef __KERNEL__
 
index cf73d47..7c31b31 100644 (file)
@@ -17,8 +17,8 @@
 
 #define l_filp_open filp_open
 
-struct obd_run_ctxt;
-struct l_file *l_dentry_open(struct obd_run_ctxt *, struct l_dentry *,
+struct lvfs_run_ctxt;
+struct l_file *l_dentry_open(struct lvfs_run_ctxt *, struct l_dentry *,
                              int flags);
 
 struct l_linux_dirent {
@@ -32,4 +32,23 @@ struct l_readdir_callback {
         struct list_head      *lrc_list;
 };
 
+# if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0))
+#  define BDEVNAME_DECLARE_STORAGE(foo) char foo[BDEVNAME_SIZE]
+#  define ll_bdevname(SB, STORAGE) __bdevname(kdev_t_to_nr(SB->s_dev), STORAGE)
+#  define lvfs_sbdev(SB)       ((SB)->s_bdev)
+#  define lvfs_sbdev_type      struct block_device *
+   int fsync_bdev(struct block_device *);
+#  define lvfs_sbdev_sync      fsync_bdev
+# else
+#  define BDEVNAME_DECLARE_STORAGE(foo) char __unused_##foo
+#  define ll_bdevname(SB,STORAGE) ((void)__unused_##STORAGE,bdevname(lvfs_sbdev(SB)))
+#  define lvfs_sbdev(SB)       (kdev_t_to_nr((SB)->s_dev))
+#  define lvfs_sbdev_type      kdev_t
+#  define lvfs_sbdev_sync      fsync_dev
+# endif
+
+void lvfs_set_rdonly(lvfs_sbdev_type dev);
+int lvfs_check_rdonly(lvfs_sbdev_type dev);
+void lvfs_clear_rdonly(lvfs_sbdev_type dev);
+
 #endif
index 4dca21b..c2b72b3 100644 (file)
@@ -130,7 +130,7 @@ struct obd_async_page_ops {
         int  (*ap_refresh_count)(void *data, int cmd);
         void (*ap_fill_obdo)(void *data, int cmd, struct obdo *oa);
         void (*ap_completion)(void *data, int cmd, struct obdo *oa, int rc);
-        void (*ap_get_ucred)(void *data, struct obd_ucred *ouc);
+        void (*ap_get_ucred)(void *data, struct lvfs_ucred *ouc);
 };
 
 /* the `oig' is passed down from a caller of obd rw methods.  the callee
@@ -291,6 +291,9 @@ struct client_obd {
         struct mdc_rpc_lock     *cl_setattr_lock;
         struct osc_creator       cl_oscc;
 
+        /* Flags section */
+        unsigned int             cl_checksum:1; /* debug checksums */
+
         /* also protected by the poorly named _loi_list_lock lock above */
         struct osc_async_rc      cl_ar;
 
@@ -521,10 +524,12 @@ struct obd_device {
         spinlock_t              obd_osfs_lock;
         struct obd_statfs       obd_osfs;
         unsigned long           obd_osfs_age;   /* jiffies */
-        struct obd_run_ctxt     obd_ctxt;
+        struct lvfs_run_ctxt    obd_lvfs_ctxt;
         struct llog_ctxt        *obd_llog_ctxt[LLOG_MAX_CTXTS];
         struct obd_device       *obd_observer;
         struct obd_export       *obd_self_export;
+        struct list_head        obd_exports_timed;  /* for ping evictor */
+        time_t                  obd_eviction_timer; /* for ping evictor */
 
         /* XXX encapsulate all this recovery data into one struct */
         svc_handler_t                    obd_recovery_handler;
@@ -581,7 +586,7 @@ struct obd_ops {
         int (*o_attach)(struct obd_device *dev, obd_count len, void *data);
         int (*o_detach)(struct obd_device *dev);
         int (*o_setup) (struct obd_device *dev, obd_count len, void *data);
-        int (*o_precleanup)(struct obd_device *dev);
+        int (*o_precleanup)(struct obd_device *dev, int cleanup_stage);
         int (*o_cleanup)(struct obd_device *dev);
         int (*o_process_config)(struct obd_device *dev, obd_count len,
                                 void *data);
index eaacac5..bdf2d27 100644 (file)
@@ -44,7 +44,7 @@
 #include <linux/lprocfs_status.h>
 
 /* OBD Device Declarations */
-#define MAX_OBD_DEVICES 256
+#define MAX_OBD_DEVICES 520
 extern struct obd_device obd_dev[MAX_OBD_DEVICES];
 extern spinlock_t obd_dev_lock;
 
@@ -79,6 +79,17 @@ void oig_complete_one(struct obd_io_group *oig,
                        struct oig_callback_context *occ, int rc);
 void oig_release(struct obd_io_group *oig);
 int oig_wait(struct obd_io_group *oig);
+/* ping evictor */
+#ifdef __KERNEL__
+void ping_evictor_start(void);
+void ping_evictor_stop(void);
+#else
+#define ping_evictor_start()    do {} while (0)
+#define ping_evictor_stop()     do {} while (0)
+#endif
+
+
+char *obd_export_nid2str(struct obd_export *exp);
 
 /* config.c */
 int class_process_config(struct lustre_cfg *lcfg);
@@ -93,9 +104,9 @@ struct config_llog_instance {
         char * cfg_instance;
         struct obd_uuid cfg_uuid;
 };
-int class_config_parse_llog(struct llog_ctxt *ctxt, char *name, 
+int class_config_parse_llog(struct llog_ctxt *ctxt, char *name,
                             struct config_llog_instance *cfg);
-int class_config_dump_llog(struct llog_ctxt *ctxt, char *name, 
+int class_config_dump_llog(struct llog_ctxt *ctxt, char *name,
                            struct config_llog_instance *cfg);
 
 struct lustre_profile {
@@ -129,6 +140,7 @@ do {                                                                           \
 void __class_export_put(struct obd_export *);
 struct obd_export *class_new_export(struct obd_device *obddev);
 void class_unlink_export(struct obd_export *exp);
+void class_update_export_timer(struct obd_export *exp, time_t extra_delay);
 
 struct obd_import *class_import_get(struct obd_import *);
 void class_import_put(struct obd_import *);
@@ -140,6 +152,7 @@ void class_put_type(struct obd_type *type);
 int class_connect(struct lustre_handle *conn, struct obd_device *obd,
                   struct obd_uuid *cluuid);
 int class_disconnect(struct obd_export *exp);
+void class_fail_export(struct obd_export *exp);
 void class_disconnect_exports(struct obd_device *obddev);
 void class_disconnect_stale_exports(struct obd_device *obddev);
 /* generic operations shared by various OBD types */
@@ -158,6 +171,7 @@ void obdo_cpy_md(struct obdo *dst, struct obdo *src, obd_flag valid);
 int obdo_cmp_md(struct obdo *dst, struct obdo *src, obd_flag compare);
 void obdo_to_ioobj(struct obdo *oa, struct obd_ioobj *ioobj);
 
+
 #define OBT(dev)        (dev)->obd_type
 #define OBP(dev, op)    (dev)->obd_type->typ_ops->o_ ## op
 #define CTXTP(ctxt, op) (ctxt)->loc_logops->lop_##op
@@ -278,7 +292,7 @@ static inline int obd_setup(struct obd_device *obd, int datalen, void *data)
         RETURN(rc);
 }
 
-static inline int obd_precleanup(struct obd_device *obd)
+static inline int obd_precleanup(struct obd_device *obd, int cleanup_stage)
 {
         int rc;
         ENTRY;
@@ -286,7 +300,7 @@ static inline int obd_precleanup(struct obd_device *obd)
         OBD_CHECK_OP(obd, precleanup, 0);
         OBD_COUNTER_INCREMENT(obd, precleanup);
 
-        rc = OBP(obd, precleanup)(obd);
+        rc = OBP(obd, precleanup)(obd, cleanup_stage);
         RETURN(rc);
 }
 
@@ -294,8 +308,8 @@ static inline int obd_cleanup(struct obd_device *obd)
 {
         int rc;
         ENTRY;
-        
-        OBD_CHECK_DEV(obd);                                     
+
+        OBD_CHECK_DEV(obd);
         OBD_CHECK_OP(obd, cleanup, 0);
         OBD_COUNTER_INCREMENT(obd, cleanup);
 
@@ -308,10 +322,10 @@ obd_process_config(struct obd_device *obd, int datalen, void *data)
 {
         int rc;
         ENTRY;
+
         OBD_CHECK_OP(obd, process_config, -EOPNOTSUPP);
         OBD_COUNTER_INCREMENT(obd, process_config);
+
         rc = OBP(obd, process_config)(obd, datalen, data);
         RETURN(rc);
 }
@@ -468,17 +482,17 @@ static inline int obd_setattr(struct obd_export *exp, struct obdo *obdo,
         RETURN(rc);
 }
 
-static inline int obd_setattr_async(struct obd_export *exp, 
+static inline int obd_setattr_async(struct obd_export *exp,
                                     struct obdo *obdo,
                                     struct lov_stripe_md *ea,
                                     struct obd_trans_info *oti)
 {
         int rc;
         ENTRY;
-                                                                                                                             
+
         EXP_CHECK_OP(exp, setattr_async);
         OBD_COUNTER_INCREMENT(exp->exp_obd, setattr_async);
-                                                                                                                             
+
         rc = OBP(exp->exp_obd, setattr_async)(exp, obdo, ea, oti);
         RETURN(rc);
 }
@@ -564,7 +578,7 @@ obd_lvfs_fid2dentry(struct obd_export *exp, __u64 id_ino, __u32 gen, __u64 gr)
 {
         LASSERT(exp->exp_obd);
 
-        return lvfs_fid2dentry(&exp->exp_obd->obd_ctxt, id_ino, gen, gr,
+        return lvfs_fid2dentry(&exp->exp_obd->obd_lvfs_ctxt, id_ino, gen, gr,
                                exp->exp_obd);
 }
 
@@ -606,7 +620,7 @@ static inline int obd_statfs(struct obd_device *obd, struct obd_statfs *osfs,
 }
 
 static inline int obd_sync(struct obd_export *exp, struct obdo *oa,
-                           struct lov_stripe_md *ea, obd_size start, 
+                           struct lov_stripe_md *ea, obd_size start,
                            obd_size end)
 {
         int rc;
@@ -675,11 +689,11 @@ static inline int obd_brw_async(int cmd, struct obd_export *exp,
         RETURN(rc);
 }
 
-static inline  int obd_prep_async_page(struct obd_export *exp, 
+static inline  int obd_prep_async_page(struct obd_export *exp,
                                        struct lov_stripe_md *lsm,
-                                       struct lov_oinfo *loi, 
-                                       struct page *page, obd_off offset, 
-                                       struct obd_async_page_ops *ops, 
+                                       struct lov_oinfo *loi,
+                                       struct page *page, obd_off offset,
+                                       struct obd_async_page_ops *ops,
                                        void *data, void **res)
 {
         int ret;
@@ -722,16 +736,16 @@ static inline int obd_set_async_flags(struct obd_export *exp,
         OBD_CHECK_OP(exp->exp_obd, set_async_flags, -EOPNOTSUPP);
         OBD_COUNTER_INCREMENT(exp->exp_obd, set_async_flags);
 
-        rc = OBP(exp->exp_obd, set_async_flags)(exp, lsm, loi, cookie, 
+        rc = OBP(exp->exp_obd, set_async_flags)(exp, lsm, loi, cookie,
                                                 async_flags);
         RETURN(rc);
 }
 
-static inline int obd_queue_group_io(struct obd_export *exp, 
-                                     struct lov_stripe_md *lsm, 
-                                     struct lov_oinfo *loi, 
-                                     struct obd_io_group *oig, 
-                                     void *cookie, int cmd, obd_off off, 
+static inline int obd_queue_group_io(struct obd_export *exp,
+                                     struct lov_stripe_md *lsm,
+                                     struct lov_oinfo *loi,
+                                     struct obd_io_group *oig,
+                                     void *cookie, int cmd, obd_off off,
                                      int count, obd_flag brw_flags,
                                      obd_flag async_flags)
 {
@@ -742,14 +756,14 @@ static inline int obd_queue_group_io(struct obd_export *exp,
         OBD_COUNTER_INCREMENT(exp->exp_obd, queue_group_io);
         LASSERT(cmd & OBD_BRW_RWMASK);
 
-        rc = OBP(exp->exp_obd, queue_group_io)(exp, lsm, loi, oig, cookie, 
+        rc = OBP(exp->exp_obd, queue_group_io)(exp, lsm, loi, oig, cookie,
                                                cmd, off, count, brw_flags,
                                                async_flags);
         RETURN(rc);
 }
 
-static inline int obd_trigger_group_io(struct obd_export *exp, 
-                                       struct lov_stripe_md *lsm, 
+static inline int obd_trigger_group_io(struct obd_export *exp,
+                                       struct lov_stripe_md *lsm,
                                        struct lov_oinfo *loi,
                                        struct obd_io_group *oig)
 {
@@ -809,6 +823,11 @@ static inline int obd_commitrw(int cmd, struct obd_export *exp, struct obdo *oa,
         RETURN(rc);
 }
 
+/* b1_4_bug5047 has changes to make this an obd_merge_lvb() method */
+__u64 lov_merge_size(struct lov_stripe_md *lsm, int kms_only);
+__u64 lov_merge_blocks(struct lov_stripe_md *lsm);
+__u64 lov_merge_mtime(struct lov_stripe_md *lsm, __u64 current_time);
+
 static inline int obd_adjust_kms(struct obd_export *exp,
                                  struct lov_stripe_md *lsm, obd_off size,
                                  int shrink)
@@ -870,7 +889,7 @@ static inline int obd_match(struct obd_export *exp, struct lov_stripe_md *ea,
 }
 
 static inline int obd_change_cbdata(struct obd_export *exp,
-                                    struct lov_stripe_md *lsm, 
+                                    struct lov_stripe_md *lsm,
                                     ldlm_iterator_t it, void *data)
 {
         int rc;
@@ -1010,7 +1029,7 @@ static inline int obd_quotacheck(struct obd_export *exp,
 
         rc = OBP(exp->exp_obd, quotacheck)(exp, oqctl);
         RETURN(rc);
-} 
+}
 
 static inline int obd_quotactl(struct obd_export *exp,
                                struct obd_quotactl *oqctl)
@@ -1023,7 +1042,7 @@ static inline int obd_quotactl(struct obd_export *exp,
 
         rc = OBP(exp->exp_obd, quotactl)(exp, oqctl);
         RETURN(rc);
-} 
+}
 
 
 static inline int obd_register_observer(struct obd_device *obd,
index 2c54309..757dba2 100644 (file)
@@ -37,7 +37,8 @@ extern atomic_t obd_memory;
 extern int obd_memmax;
 extern unsigned int obd_fail_loc;
 extern unsigned int obd_dump_on_timeout;
-extern unsigned int obd_timeout;
+extern unsigned int obd_timeout;          /* seconds */
+#define PING_INTERVAL max(obd_timeout / 4, 1U)
 extern unsigned int ldlm_timeout;
 extern char obd_lustre_upcall[128];
 extern unsigned int obd_sync_filter;
@@ -90,6 +91,7 @@ extern wait_queue_head_t obd_race_waitq;
 #define OBD_FAIL_MDS_OST_SETATTR         0x12c
 #define OBD_FAIL_MDS_QUOTACHECK_NET      0x12d
 #define OBD_FAIL_MDS_QUOTACTL_NET        0x12e
+#define OBD_FAIL_MDS_CLIENT_ADD          0x12f
 
 #define OBD_FAIL_OST                     0x200
 #define OBD_FAIL_OST_CONNECT_NET         0x201
@@ -132,6 +134,7 @@ extern wait_queue_head_t obd_race_waitq;
 #define OBD_FAIL_LDLM_ENQUEUE_BLOCKED    0x30b
 #define OBD_FAIL_LDLM_REPLY              0x30c
 #define OBD_FAIL_LDLM_RECOV_CLIENTS      0x30d
+#define OBD_FAIL_LDLM_ENQUEUE_OLD_EXPORT 0x30e
 
 #define OBD_FAIL_OSC                     0x400
 #define OBD_FAIL_OSC_BRW_READ_BULK       0x401
@@ -140,12 +143,14 @@ extern wait_queue_head_t obd_race_waitq;
 #define OBD_FAIL_OSC_LOCK_CP_AST         0x404
 #define OBD_FAIL_OSC_MATCH               0x405
 #define OBD_FAIL_OSC_BRW_PREP_REQ        0x406
+#define OBD_FAIL_OSC_SHUTDOWN            0x407
 
 #define OBD_FAIL_PTLRPC                  0x500
 #define OBD_FAIL_PTLRPC_ACK              0x501
 #define OBD_FAIL_PTLRPC_RQBD             0x502
 #define OBD_FAIL_PTLRPC_BULK_GET_NET     0x503
 #define OBD_FAIL_PTLRPC_BULK_PUT_NET     0x504
+#define OBD_FAIL_PTLRPC_DROP_RPC         0x505
 
 #define OBD_FAIL_OBD_PING_NET            0x600
 #define OBD_FAIL_OBD_LOG_CANCEL_NET      0x601
@@ -191,7 +196,7 @@ do {                                                                         \
 
 #define OBD_FAIL_TIMEOUT(id, secs)                                           \
 do {                                                                         \
-        if  (OBD_FAIL_CHECK_ONCE(id)) {                                      \
+        if (OBD_FAIL_CHECK_ONCE(id)) {                                      \
                 CERROR("obd_fail_timeout id %x sleeping for %d secs\n",      \
                        (id), (secs));                                        \
                 set_current_state(TASK_UNINTERRUPTIBLE);                     \
@@ -201,6 +206,38 @@ do {                                                                         \
        }                                                                     \
 } while(0)
 
+/* Prefer the kernel's version, if it exports it, because it might be
+ * optimized for this CPU. */
+#if defined(__KERNEL__) && (defined(CONFIG_CRC32) || defined(CONFIG_CRC32_MODULE))
+# include <linux/crc32.h>
+#else
+/* crc32_le lifted from the Linux kernel, which had the following to say:
+ *
+ * This code is in the public domain; copyright abandoned.
+ * Liability for non-performance of this code is limited to the amount
+ * you paid for it.  Since it is distributed for free, your refund will
+ * be very very small.  If it breaks, you get to keep both pieces.
+ */
+#define CRCPOLY_LE 0xedb88320
+/**
+ * crc32_le() - Calculate bitwise little-endian Ethernet AUTODIN II CRC32
+ * @crc - seed value for computation.  ~0 for Ethernet, sometimes 0 for
+ *        other uses, or the previous crc32 value if computing incrementally.
+ * @p   - pointer to buffer over which CRC is run
+ * @len - length of buffer @p
+ */
+static inline __u32 crc32_le(__u32 crc, unsigned char const *p, size_t len)
+{
+        int i;
+        while (len--) {
+                crc ^= *p++;
+                for (i = 0; i < 8; i++)
+                        crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0);
+        }
+        return crc;
+}
+#endif
+
 #ifdef __KERNEL__
 /* The idea here is to synchronise two threads to force a race. The
  * first thread that calls this with a matching fail_loc is put to
@@ -219,7 +256,7 @@ do {                                                            \
 } while(0)
 #else
 /* sigh.  an expedient fix until OBD_RACE is fixed up */
-#define OBD_RACE(foo) LBUG()
+#define OBD_RACE(foo) do {} while(0)
 #endif
 
 #define fixme() CDEBUG(D_OTHER, "FIXME\n");
@@ -227,37 +264,7 @@ do {                                                            \
 #ifdef __KERNEL__
 # include <linux/types.h>
 # include <linux/blkdev.h>
-
-
-# if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0))
-#  define BDEVNAME_DECLARE_STORAGE(foo) char foo[BDEVNAME_SIZE]
-#  define ll_bdevname(SB, STORAGE) __bdevname(kdev_t_to_nr(SB->s_dev), STORAGE)
-#  define ll_sbdev(SB)       ((SB)->s_bdev)
-#  define ll_sbdev_type      struct block_device *
-   int fsync_bdev(struct block_device *);
-#  define ll_sbdev_sync      fsync_bdev
-#  define ll_lock_kernel     lock_kernel()
-# else
-#  define BDEVNAME_DECLARE_STORAGE(foo) char __unused_##foo
-#  define ll_bdevname(SB,STORAGE) ((void)__unused_##STORAGE,bdevname(ll_sbdev(SB)))
-#  define ll_sbdev(SB)       (kdev_t_to_nr((SB)->s_dev))
-#  define ll_sbdev_type      kdev_t
-#  define ll_sbdev_sync      fsync_dev
-#  define ll_lock_kernel
-# endif
-
-#ifdef HAVE_OLD_DEV_SET_RDONLY
-  void dev_set_rdonly(ll_sbdev_type dev, int no_write);
-  void dev_clear_rdonly(int no_write);
-#else
-  void dev_set_rdonly(ll_sbdev_type dev);
-  void dev_clear_rdonly(ll_sbdev_type dev);
-#endif
-int dev_check_rdonly(ll_sbdev_type dev);
-#define ll_check_rdonly(dev) dev_check_rdonly(dev)
-
-void ll_set_rdonly(ll_sbdev_type dev);
-void ll_clear_rdonly(ll_sbdev_type dev);
+# include <linux/lvfs.h>
 
 static inline void OBD_FAIL_WRITE(int id, struct super_block *sb)
 {
@@ -265,7 +272,7 @@ static inline void OBD_FAIL_WRITE(int id, struct super_block *sb)
                 BDEVNAME_DECLARE_STORAGE(tmp);
                 CERROR("obd_fail_loc=%x, fail write operation on %s\n",
                        id, ll_bdevname(sb, tmp));
-                ll_set_rdonly(ll_sbdev(sb));
+                lvfs_set_rdonly(lvfs_sbdev(sb));
                 /* We set FAIL_ONCE because we never "un-fail" a device */
                 obd_fail_loc |= OBD_FAILED | OBD_FAIL_ONCE;
         }
@@ -280,6 +287,20 @@ static inline void OBD_FAIL_WRITE(int id, struct super_block *sb)
 
 extern atomic_t portal_kmemory;
 
+#if defined(LUSTRE_UTILS) /* this version is for utils only */
+#define OBD_ALLOC_GFP(ptr, size, gfp_mask)                                    \
+do {                                                                          \
+        (ptr) = kmalloc(size, (gfp_mask));                                    \
+        if ((ptr) == NULL) {                                                  \
+                CERROR("kmalloc of '" #ptr "' (%d bytes) failed at %s:%d\n",  \
+                       (int)(size), __FILE__, __LINE__);                      \
+        } else {                                                              \
+                memset(ptr, 0, size);                                         \
+                CDEBUG(D_MALLOC, "kmalloced '" #ptr "': %d at %p\n",          \
+                       (int)(size), ptr);                                     \
+        }                                                                     \
+} while (0)
+#else /* this version is for the kernel and liblustre */
 #define OBD_ALLOC_GFP(ptr, size, gfp_mask)                                    \
 do {                                                                          \
         (ptr) = kmalloc(size, (gfp_mask));                                    \
@@ -297,17 +318,13 @@ do {                                                                          \
                        (int)(size), ptr, atomic_read(&obd_memory));           \
         }                                                                     \
 } while (0)
+#endif
 
 #ifndef OBD_GFP_MASK
 # define OBD_GFP_MASK GFP_NOFS
 #endif
 
-#ifdef __KERNEL__
 #define OBD_ALLOC(ptr, size) OBD_ALLOC_GFP(ptr, size, OBD_GFP_MASK)
-#else
-#define OBD_ALLOC(ptr, size) (ptr = malloc(size))
-#endif
-
 #define OBD_ALLOC_WAIT(ptr, size) OBD_ALLOC_GFP(ptr, size, GFP_KERNEL)
 
 #ifdef __arch_um__
@@ -408,4 +425,7 @@ do {                                                                          \
         (ptr) = (void *)0xdeadbeef;                                           \
 } while (0)
 
+#define KEY_IS(str) \
+        (keylen == strlen(str) && memcmp(key, str, keylen) == 0)
+
 #endif
index 64c951d..9c4b9e5 100644 (file)
@@ -9,6 +9,14 @@
 #ifndef _LUSTRE_USER_H
 #define _LUSTRE_USER_H
 #include <asm/types.h>
+
+/*
+ * asm-x86_64/processor.h on some SLES 9 distros seems to use
+ * kernel-only typedefs.  fortunately skipping it altogether is ok
+ * (for now).
+ */
+#define __ASM_X86_64_PROCESSOR_H
+
 #include <linux/quota.h>
 #ifdef __KERNEL__
 #include <linux/string.h>
@@ -50,6 +58,7 @@
 
 #define LL_FILE_IGNORE_LOCK             0x00000001
 #define LL_FILE_GROUP_LOCKED            0x00000002
+#define LL_FILE_READAHEAD               0x00000004
 
 #define LOV_USER_MAGIC_V1 0x0BD10BD0
 #define LOV_USER_MAGIC    LOV_USER_MAGIC_V1
@@ -133,7 +142,15 @@ struct if_quotacheck {
 };
 
 #ifndef __KERNEL__
+#define NEED_QUOTA_DEFS
+#else
+# include <linux/version.h>
+# if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,21)
+#  define NEED_QUOTA_DEFS
+# endif
+#endif
 
+#ifdef NEED_QUOTA_DEFS
 #ifndef QUOTABLOCK_BITS
 #define QUOTABLOCK_BITS 10
 #endif
@@ -170,6 +187,19 @@ struct if_dqblk {
 };
 #endif
 
+#ifndef QIF_BLIMITS
+#define QIF_BLIMITS     1
+#define QIF_SPACE       2
+#define QIF_ILIMITS     4
+#define QIF_INODES      8
+#define QIF_BTIME       16
+#define QIF_ITIME       32
+#define QIF_LIMITS      (QIF_BLIMITS | QIF_ILIMITS)
+#define QIF_USAGE       (QIF_SPACE | QIF_INODES)
+#define QIF_TIMES       (QIF_BTIME | QIF_ITIME)
+#define QIF_ALL         (QIF_LIMITS | QIF_USAGE | QIF_TIMES)
+#endif
+
 #endif /* !__KERNEL__ */
 
 struct if_quotactl {
index 3781659..e8ba99d 100644 (file)
@@ -588,6 +588,7 @@ CONFIG_SCSI_LOGGING=y
 # SCSI low-level drivers
 #
 CONFIG_BLK_DEV_3W_XXXX_RAID=m
+CONFIG_BLK_DEV_3W_9XXX_RAID=m
 CONFIG_SCSI_7000FASST=m
 CONFIG_SCSI_ACARD=m
 CONFIG_SCSI_AHA152X=m
@@ -621,6 +622,7 @@ CONFIG_SCSI_AM53C974=m
 CONFIG_SCSI_MEGARAID=m
 CONFIG_SCSI_MEGARAID2=m
 CONFIG_SCSI_SATA=y
+CONFIG_SCSI_SATA_AHCI=m
 CONFIG_SCSI_SATA_SVW=m
 CONFIG_SCSI_ATA_PIIX=m
 CONFIG_SCSI_SATA_NV=m
@@ -628,6 +630,7 @@ CONFIG_SCSI_SATA_PROMISE=m
 CONFIG_SCSI_SATA_SX4=m
 CONFIG_SCSI_SATA_SIL=m
 CONFIG_SCSI_SATA_SIS=m
+CONFIG_SCSI_SATA_ULI=m
 CONFIG_SCSI_SATA_VIA=m
 CONFIG_SCSI_SATA_VITESSE=m
 CONFIG_SCSI_BUSLOGIC=m
@@ -868,6 +871,17 @@ CONFIG_YELLOWFIN=m
 CONFIG_R8169=m
 CONFIG_SK98LIN=m
 CONFIG_TIGON3=m
+
+#
+# Quadrics QsNet device support
+#
+CONFIG_QSNET=m
+CONFIG_ELAN3=m
+CONFIG_ELAN4=m
+CONFIG_EP=m
+CONFIG_EIP=m
+CONFIG_RMS=m
+CONFIG_JTAG=m
 CONFIG_FDDI=y
 CONFIG_DEFXX=m
 CONFIG_SKFP=m
index fce798b..e8ba99d 100644 (file)
@@ -49,7 +49,7 @@ CONFIG_RWSEM_XCHGADD_ALGORITHM=y
 CONFIG_X86_L1_CACHE_SHIFT=7
 CONFIG_X86_HAS_TSC=y
 CONFIG_X86_GOOD_APIC=y
-# CONFIG_X86_PGE is not set
+CONFIG_X86_PGE=y
 CONFIG_X86_USE_PPRO_CHECKSUM=y
 CONFIG_X86_PPRO_FENCE=y
 CONFIG_X86_F00F_WORKS_OK=y
@@ -58,14 +58,7 @@ CONFIG_X86_MCE=y
 #
 # CPU Frequency scaling
 #
-CONFIG_CPU_FREQ=y
-# CONFIG_CPU_FREQ_24_API is not set
-CONFIG_X86_POWERNOW_K6=m
-# CONFIG_X86_LONGHAUL is not set
-CONFIG_X86_SPEEDSTEP=m
-# CONFIG_X86_P4_CLOCKMOD is not set
-# CONFIG_X86_LONGRUN is not set
-# CONFIG_X86_GX_SUSPMOD is not set
+# CONFIG_CPU_FREQ is not set
 CONFIG_TOSHIBA=m
 CONFIG_I8K=m
 CONFIG_MICROCODE=m
@@ -74,10 +67,11 @@ CONFIG_X86_CPUID=m
 # CONFIG_E820_PROC is not set
 CONFIG_EDD=m
 # CONFIG_NOHIGHMEM is not set
-#CONFIG_HIGHMEM4G is not set
+# CONFIG_HIGHMEM4G is not set
 CONFIG_HIGHMEM64G=y
 CONFIG_HIGHMEM=y
 CONFIG_HIGHPTE=y
+CONFIG_X86_PAE=y
 CONFIG_HIGHIO=y
 # CONFIG_X86_4G is not set
 CONFIG_3GB=y
@@ -85,21 +79,29 @@ CONFIG_3GB=y
 # CONFIG_1GB is not set
 # CONFIG_MATH_EMULATION is not set
 CONFIG_MTRR=y
-# CONFIG_SMP is not set
-# CONFIG_X86_UP_APIC is not set
-# CONFIG_X86_UP_IOAPIC is not set
-# CONFIG_X86_TSC_DISABLE is not set
-CONFIG_X86_TSC=y
+CONFIG_SMP=y
+# CONFIG_NR_SIBLINGS_0 is not set
+CONFIG_NR_SIBLINGS_2=y
+CONFIG_SHARE_RUNQUEUE=y
+CONFIG_MAX_NR_SIBLINGS=2
+CONFIG_X86_CLUSTERED_APIC=y
+CONFIG_X86_NUMA=y
+# CONFIG_X86_NUMAQ is not set
+CONFIG_X86_SUMMIT=y
+CONFIG_X86_CLUSTERED_APIC=y
 # CONFIG_NOBIGSTACK is not set
 CONFIG_STACK_SIZE_16KB=y
 # CONFIG_STACK_SIZE_32KB is not set
 # CONFIG_STACK_SIZE_64KB is not set
 CONFIG_STACK_SIZE_SHIFT=2
+CONFIG_HAVE_DEC_LOCK=y
 
 #
 # General setup
 #
 CONFIG_NET=y
+CONFIG_X86_IO_APIC=y
+CONFIG_X86_LOCAL_APIC=y
 CONFIG_PCI=y
 # CONFIG_PCI_GOBIOS is not set
 # CONFIG_PCI_GODIRECT is not set
@@ -125,10 +127,10 @@ CONFIG_I82365=y
 #
 # PCI Hotplug Support
 #
-# CONFIG_HOTPLUG_PCI is not set
-# CONFIG_HOTPLUG_PCI_COMPAQ is not set
+CONFIG_HOTPLUG_PCI=y
+CONFIG_HOTPLUG_PCI_COMPAQ=m
 # CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM is not set
-# CONFIG_HOTPLUG_PCI_IBM is not set
+CONFIG_HOTPLUG_PCI_IBM=m
 # CONFIG_HOTPLUG_PCI_ACPI is not set
 CONFIG_SYSVIPC=y
 CONFIG_BSD_PROCESS_ACCT=y
@@ -586,6 +588,7 @@ CONFIG_SCSI_LOGGING=y
 # SCSI low-level drivers
 #
 CONFIG_BLK_DEV_3W_XXXX_RAID=m
+CONFIG_BLK_DEV_3W_9XXX_RAID=m
 CONFIG_SCSI_7000FASST=m
 CONFIG_SCSI_ACARD=m
 CONFIG_SCSI_AHA152X=m
@@ -619,6 +622,7 @@ CONFIG_SCSI_AM53C974=m
 CONFIG_SCSI_MEGARAID=m
 CONFIG_SCSI_MEGARAID2=m
 CONFIG_SCSI_SATA=y
+CONFIG_SCSI_SATA_AHCI=m
 CONFIG_SCSI_SATA_SVW=m
 CONFIG_SCSI_ATA_PIIX=m
 CONFIG_SCSI_SATA_NV=m
@@ -626,6 +630,7 @@ CONFIG_SCSI_SATA_PROMISE=m
 CONFIG_SCSI_SATA_SX4=m
 CONFIG_SCSI_SATA_SIL=m
 CONFIG_SCSI_SATA_SIS=m
+CONFIG_SCSI_SATA_ULI=m
 CONFIG_SCSI_SATA_VIA=m
 CONFIG_SCSI_SATA_VITESSE=m
 CONFIG_SCSI_BUSLOGIC=m
@@ -799,7 +804,7 @@ CONFIG_DEPCA=m
 CONFIG_HP100=m
 CONFIG_NET_ISA=y
 CONFIG_E2100=m
-CONFIG_EWRK3=m
+# CONFIG_EWRK3 is not set
 CONFIG_EEXPRESS=m
 CONFIG_EEXPRESS_PRO=m
 CONFIG_HPLAN_PLUS=m
@@ -828,7 +833,7 @@ CONFIG_LNE390=m
 CONFIG_FEALNX=m
 CONFIG_NATSEMI=m
 CONFIG_NE2K_PCI=m
-# CONFIG_FORCEDETH is not set
+CONFIG_FORCEDETH=m
 CONFIG_NE3210=m
 CONFIG_ES3210=m
 CONFIG_8139CP=m
@@ -866,6 +871,17 @@ CONFIG_YELLOWFIN=m
 CONFIG_R8169=m
 CONFIG_SK98LIN=m
 CONFIG_TIGON3=m
+
+#
+# Quadrics QsNet device support
+#
+CONFIG_QSNET=m
+CONFIG_ELAN3=m
+CONFIG_ELAN4=m
+CONFIG_EP=m
+CONFIG_EIP=m
+CONFIG_RMS=m
+CONFIG_JTAG=m
 CONFIG_FDDI=y
 CONFIG_DEFXX=m
 CONFIG_SKFP=m
index 4058f4b..94fca76 100644 (file)
@@ -536,6 +536,7 @@ CONFIG_SCSI_CONSTANTS=y
 # SCSI low-level drivers
 #
 CONFIG_BLK_DEV_3W_XXXX_RAID=m
+CONFIG_BLK_DEV_3W_9XXX_RAID=m
 # CONFIG_SCSI_7000FASST is not set
 # CONFIG_SCSI_ACARD is not set
 # CONFIG_SCSI_AHA152X is not set
@@ -569,6 +570,7 @@ CONFIG_AIC7XXX_OLD_PROC_STATS=y
 CONFIG_SCSI_MEGARAID=m
 CONFIG_SCSI_MEGARAID2=m
 CONFIG_SCSI_SATA=y
+CONFIG_SCSI_SATA_AHCI=m
 CONFIG_SCSI_SATA_SVW=m
 CONFIG_SCSI_ATA_PIIX=m
 CONFIG_SCSI_SATA_NV=m
@@ -576,6 +578,7 @@ CONFIG_SCSI_SATA_PROMISE=m
 CONFIG_SCSI_SATA_SX4=m
 CONFIG_SCSI_SATA_SIL=m
 CONFIG_SCSI_SATA_SIS=m
+CONFIG_SCSI_SATA_ULI=m
 CONFIG_SCSI_SATA_VIA=m
 CONFIG_SCSI_SATA_VITESSE=m
 # CONFIG_SCSI_BUSLOGIC is not set
index 7e440cf..94fca76 100644 (file)
@@ -53,7 +53,7 @@ CONFIG_HUGETLB_PAGE_SIZE_256MB=y
 # CONFIG_HUGETLB_PAGE_SIZE_1MB is not set
 # CONFIG_HUGETLB_PAGE_SIZE_256KB is not set
 # CONFIG_IA64_PAL_IDLE is not set
-# CONFIG_SMP is not set
+CONFIG_SMP=y
 CONFIG_IA32_SUPPORT=y
 CONFIG_COMPAT=y
 CONFIG_PERFMON=y
@@ -536,6 +536,7 @@ CONFIG_SCSI_CONSTANTS=y
 # SCSI low-level drivers
 #
 CONFIG_BLK_DEV_3W_XXXX_RAID=m
+CONFIG_BLK_DEV_3W_9XXX_RAID=m
 # CONFIG_SCSI_7000FASST is not set
 # CONFIG_SCSI_ACARD is not set
 # CONFIG_SCSI_AHA152X is not set
@@ -569,6 +570,7 @@ CONFIG_AIC7XXX_OLD_PROC_STATS=y
 CONFIG_SCSI_MEGARAID=m
 CONFIG_SCSI_MEGARAID2=m
 CONFIG_SCSI_SATA=y
+CONFIG_SCSI_SATA_AHCI=m
 CONFIG_SCSI_SATA_SVW=m
 CONFIG_SCSI_ATA_PIIX=m
 CONFIG_SCSI_SATA_NV=m
@@ -576,6 +578,7 @@ CONFIG_SCSI_SATA_PROMISE=m
 CONFIG_SCSI_SATA_SX4=m
 CONFIG_SCSI_SATA_SIL=m
 CONFIG_SCSI_SATA_SIS=m
+CONFIG_SCSI_SATA_ULI=m
 CONFIG_SCSI_SATA_VIA=m
 CONFIG_SCSI_SATA_VITESSE=m
 # CONFIG_SCSI_BUSLOGIC is not set
index 5be9ee4..594b821 100644 (file)
@@ -543,6 +543,7 @@ CONFIG_SCSI_LOGGING=y
 # SCSI low-level drivers
 #
 CONFIG_BLK_DEV_3W_XXXX_RAID=m
+CONFIG_BLK_DEV_3W_9XXX_RAID=m
 # CONFIG_SCSI_7000FASST is not set
 # CONFIG_SCSI_ACARD is not set
 # CONFIG_SCSI_AHA152X is not set
@@ -575,6 +576,7 @@ CONFIG_AIC7XXX_OLD_PROC_STATS=y
 CONFIG_SCSI_MEGARAID=m
 CONFIG_SCSI_MEGARAID2=m
 CONFIG_SCSI_SATA=y
+CONFIG_SCSI_SATA_AHCI=m
 CONFIG_SCSI_SATA_SVW=m
 CONFIG_SCSI_ATA_PIIX=m
 CONFIG_SCSI_SATA_NV=m
@@ -582,6 +584,7 @@ CONFIG_SCSI_SATA_PROMISE=m
 CONFIG_SCSI_SATA_SX4=m
 CONFIG_SCSI_SATA_SIL=m
 CONFIG_SCSI_SATA_SIS=m
+CONFIG_SCSI_SATA_ULI=m
 CONFIG_SCSI_SATA_VIA=m
 CONFIG_SCSI_SATA_VITESSE=m
 # CONFIG_SCSI_BUSLOGIC is not set
@@ -777,6 +780,17 @@ CONFIG_YELLOWFIN=m
 CONFIG_R8169=m
 CONFIG_SK98LIN=m
 CONFIG_TIGON3=m
+
+#
+# Quadrics QsNet device support
+#
+CONFIG_QSNET=m
+CONFIG_ELAN3=m
+CONFIG_ELAN4=m
+CONFIG_EP=m
+CONFIG_EIP=m
+CONFIG_RMS=m
+CONFIG_JTAG=m
 CONFIG_FDDI=y
 CONFIG_DEFXX=m
 CONFIG_SKFP=m
index a32b0ba..594b821 100644 (file)
@@ -42,13 +42,17 @@ CONFIG_X86_CPUID=m
 CONFIG_X86_IO_APIC=y
 CONFIG_X86_LOCAL_APIC=y
 CONFIG_MTRR=y
-# CONFIG_SMP is not set
+CONFIG_SMP=y
 CONFIG_HPET_TIMER=y
 CONFIG_GART_IOMMU=y
 # CONFIG_SWIOTLB is not set
-CONFIG_X86_UP_IOAPIC=y
+CONFIG_NR_SIBLINGS_0=y
+# CONFIG_NR_SIBLINGS_2 is not set
+CONFIG_HAVE_DEC_LOCK=y
 CONFIG_MCE=y
-# CONFIG_K8_NUMA is not set
+CONFIG_K8_NUMA=y
+CONFIG_DISCONTIGMEM=y
+CONFIG_NUMA=y
 # CONFIG_NOBIGSTACK is not set
 CONFIG_STACK_SIZE_16KB=y
 # CONFIG_STACK_SIZE_32KB is not set
@@ -108,6 +112,7 @@ CONFIG_ACPI_BUTTON=m
 CONFIG_ACPI_FAN=m
 CONFIG_ACPI_PROCESSOR=m
 CONFIG_ACPI_THERMAL=m
+# CONFIG_ACPI_NUMA is not set
 # CONFIG_ACPI_ASUS is not set
 # CONFIG_ACPI_TOSHIBA is not set
 # CONFIG_ACPI_DEBUG is not set
@@ -538,6 +543,7 @@ CONFIG_SCSI_LOGGING=y
 # SCSI low-level drivers
 #
 CONFIG_BLK_DEV_3W_XXXX_RAID=m
+CONFIG_BLK_DEV_3W_9XXX_RAID=m
 # CONFIG_SCSI_7000FASST is not set
 # CONFIG_SCSI_ACARD is not set
 # CONFIG_SCSI_AHA152X is not set
@@ -570,6 +576,7 @@ CONFIG_AIC7XXX_OLD_PROC_STATS=y
 CONFIG_SCSI_MEGARAID=m
 CONFIG_SCSI_MEGARAID2=m
 CONFIG_SCSI_SATA=y
+CONFIG_SCSI_SATA_AHCI=m
 CONFIG_SCSI_SATA_SVW=m
 CONFIG_SCSI_ATA_PIIX=m
 CONFIG_SCSI_SATA_NV=m
@@ -577,6 +584,7 @@ CONFIG_SCSI_SATA_PROMISE=m
 CONFIG_SCSI_SATA_SX4=m
 CONFIG_SCSI_SATA_SIL=m
 CONFIG_SCSI_SATA_SIS=m
+CONFIG_SCSI_SATA_ULI=m
 CONFIG_SCSI_SATA_VIA=m
 CONFIG_SCSI_SATA_VITESSE=m
 # CONFIG_SCSI_BUSLOGIC is not set
@@ -772,6 +780,17 @@ CONFIG_YELLOWFIN=m
 CONFIG_R8169=m
 CONFIG_SK98LIN=m
 CONFIG_TIGON3=m
+
+#
+# Quadrics QsNet device support
+#
+CONFIG_QSNET=m
+CONFIG_ELAN3=m
+CONFIG_ELAN4=m
+CONFIG_EP=m
+CONFIG_EIP=m
+CONFIG_RMS=m
+CONFIG_JTAG=m
 CONFIG_FDDI=y
 CONFIG_DEFXX=m
 CONFIG_SKFP=m
index 06e4e8b..beb776a 100644 (file)
@@ -201,6 +201,8 @@ CONFIG_APM_DISPLAY_BLANK=y
 # CONFIG_APM_RTC_IS_GMT is not set
 CONFIG_APM_ALLOW_INTS=y
 # CONFIG_APM_REAL_MODE_POWER_OFF is not set
+CONFIG_IOPROC=y
+CONFIG_PTRACK=y
 
 #
 # CPU Frequency scaling
@@ -1310,6 +1312,17 @@ CONFIG_SKISA=m
 CONFIG_PROTEON=m
 CONFIG_ABYSS=m
 CONFIG_SMCTR=m
+
+#
+# Quadrics QsNet
+#
+CONFIG_QSNET=m
+CONFIG_ELAN3=m
+CONFIG_ELAN4=m
+CONFIG_EP=m
+CONFIG_EIP=m
+CONFIG_RMS=m
+CONFIG_JTAG=m
 CONFIG_NET_FC=y
 CONFIG_NET_LPFC=m
 CONFIG_RCPCI=m
index e90083d..beb776a 100644 (file)
@@ -201,6 +201,8 @@ CONFIG_APM_DISPLAY_BLANK=y
 # CONFIG_APM_RTC_IS_GMT is not set
 CONFIG_APM_ALLOW_INTS=y
 # CONFIG_APM_REAL_MODE_POWER_OFF is not set
+CONFIG_IOPROC=y
+CONFIG_PTRACK=y
 
 #
 # CPU Frequency scaling
@@ -589,6 +591,7 @@ CONFIG_SCSI_PROC_FS=y
 # SCSI support type (disk, tape, CD-ROM)
 #
 CONFIG_BLK_DEV_SD=m
+CONFIG_SD_IOSTATS=y
 CONFIG_CHR_DEV_ST=m
 CONFIG_CHR_DEV_OSST=m
 CONFIG_BLK_DEV_SR=m
@@ -1309,6 +1312,17 @@ CONFIG_SKISA=m
 CONFIG_PROTEON=m
 CONFIG_ABYSS=m
 CONFIG_SMCTR=m
+
+#
+# Quadrics QsNet
+#
+CONFIG_QSNET=m
+CONFIG_ELAN3=m
+CONFIG_ELAN4=m
+CONFIG_EP=m
+CONFIG_EIP=m
+CONFIG_RMS=m
+CONFIG_JTAG=m
 CONFIG_NET_FC=y
 CONFIG_NET_LPFC=m
 CONFIG_RCPCI=m
index 6844769..48f481b 100644 (file)
@@ -123,6 +123,8 @@ CONFIG_ACPI_POWER=y
 CONFIG_ACPI_PCI=y
 CONFIG_ACPI_SYSTEM=y
 CONFIG_ACPI_INITRD=y
+CONFIG_IOPROC=y
+CONFIG_PTRACK=y
 
 #
 # Bus options (PCI, PCMCIA)
@@ -1027,6 +1029,17 @@ CONFIG_IBMOL=m
 CONFIG_IBMLS=m
 CONFIG_3C359=m
 # CONFIG_TMS380TR is not set
+
+#
+# Quadrics QsNet
+#
+CONFIG_QSNET=m
+CONFIG_ELAN3=m
+CONFIG_ELAN4=m
+CONFIG_EP=m
+CONFIG_EIP=m
+CONFIG_RMS=m
+CONFIG_JTAG=m
 CONFIG_NET_FC=y
 CONFIG_NET_LPFC=m
 CONFIG_SHAPER=m
index 6844769..48f481b 100644 (file)
@@ -123,6 +123,8 @@ CONFIG_ACPI_POWER=y
 CONFIG_ACPI_PCI=y
 CONFIG_ACPI_SYSTEM=y
 CONFIG_ACPI_INITRD=y
+CONFIG_IOPROC=y
+CONFIG_PTRACK=y
 
 #
 # Bus options (PCI, PCMCIA)
@@ -1027,6 +1029,17 @@ CONFIG_IBMOL=m
 CONFIG_IBMLS=m
 CONFIG_3C359=m
 # CONFIG_TMS380TR is not set
+
+#
+# Quadrics QsNet
+#
+CONFIG_QSNET=m
+CONFIG_ELAN3=m
+CONFIG_ELAN4=m
+CONFIG_EP=m
+CONFIG_EIP=m
+CONFIG_RMS=m
+CONFIG_JTAG=m
 CONFIG_NET_FC=y
 CONFIG_NET_LPFC=m
 CONFIG_SHAPER=m
index 5afe34e..0730819 100644 (file)
@@ -137,6 +137,8 @@ CONFIG_ACPI_POWER=y
 CONFIG_ACPI_PCI=y
 CONFIG_ACPI_SYSTEM=y
 CONFIG_ACPI_INITRD=y
+CONFIG_IOPROC=y
+CONFIG_PTRACK=y
 
 #
 # CPU Frequency scaling
@@ -1109,6 +1111,17 @@ CONFIG_3C359=m
 CONFIG_TMS380TR=m
 CONFIG_TMSPCI=m
 CONFIG_ABYSS=m
+
+#
+# Quadrics QsNet
+#
+CONFIG_QSNET=m
+CONFIG_ELAN3=m
+CONFIG_ELAN4=m
+CONFIG_EP=m
+CONFIG_EIP=m
+CONFIG_RMS=m
+CONFIG_JTAG=m
 CONFIG_NET_FC=y
 CONFIG_NET_LPFC=m
 CONFIG_SHAPER=m
index 5afe34e..0730819 100644 (file)
@@ -137,6 +137,8 @@ CONFIG_ACPI_POWER=y
 CONFIG_ACPI_PCI=y
 CONFIG_ACPI_SYSTEM=y
 CONFIG_ACPI_INITRD=y
+CONFIG_IOPROC=y
+CONFIG_PTRACK=y
 
 #
 # CPU Frequency scaling
@@ -1109,6 +1111,17 @@ CONFIG_3C359=m
 CONFIG_TMS380TR=m
 CONFIG_TMSPCI=m
 CONFIG_ABYSS=m
+
+#
+# Quadrics QsNet
+#
+CONFIG_QSNET=m
+CONFIG_ELAN3=m
+CONFIG_ELAN4=m
+CONFIG_EP=m
+CONFIG_EIP=m
+CONFIG_RMS=m
+CONFIG_JTAG=m
 CONFIG_NET_FC=y
 CONFIG_NET_LPFC=m
 CONFIG_SHAPER=m
index f8e83ec..18cb4d1 100644 (file)
@@ -1,28 +1,34 @@
 #
 # Automatically generated make config: don't edit
+# Linux kernel version: 2.6.9-prep
+# Fri May 13 14:09:31 2005
 #
+CONFIG_X86=y
 CONFIG_MMU=y
-CONFIG_SMP=y
-# CONFIG_HOTPLUG_CPU is not set
-CONFIG_LOCALVERSION=""
+CONFIG_UID16=y
+CONFIG_GENERIC_ISA_DMA=y
+CONFIG_GENERIC_IOMAP=y
 
 #
 # Code maturity level options
 #
 CONFIG_EXPERIMENTAL=y
 CONFIG_CLEAN_COMPILE=y
-CONFIG_STANDALONE=y
-CONFIG_PREVENT_FIRMWARE_BUILD=y
 
 #
 # General setup
 #
+CONFIG_LOCALVERSION=""
 CONFIG_SWAP=y
 CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
 CONFIG_BSD_PROCESS_ACCT=y
 # CONFIG_BSD_PROCESS_ACCT_V3 is not set
 CONFIG_SYSCTL=y
+CONFIG_AUDIT=y
+CONFIG_AUDITSYSCALL=y
 CONFIG_LOG_BUF_SHIFT=17
+CONFIG_HOTPLUG=y
 # CONFIG_IKCONFIG is not set
 # CONFIG_EMBEDDED is not set
 CONFIG_KALLSYMS=y
@@ -33,9 +39,10 @@ CONFIG_EPOLL=y
 CONFIG_IOSCHED_NOOP=y
 CONFIG_IOSCHED_AS=y
 CONFIG_IOSCHED_DEADLINE=y
-# CONFIG_PREEMPT is not set
-CONFIG_PREEMPT_VOLUNTARY=y
-CONFIG_POSIX_MQUEUE=y
+CONFIG_IOSCHED_CFQ=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SHMEM=y
+# CONFIG_TINY_SHMEM is not set
 
 #
 # Loadable module support
@@ -45,14 +52,188 @@ CONFIG_MODULE_UNLOAD=y
 # CONFIG_MODULE_FORCE_UNLOAD is not set
 CONFIG_OBSOLETE_MODPARM=y
 CONFIG_MODVERSIONS=y
-CONFIG_KMOD=y
 CONFIG_MODULE_SIG=y
 # CONFIG_MODULE_SIG_FORCE is not set
+CONFIG_KMOD=y
+CONFIG_STOP_MACHINE=y
+
+#
+# Processor type and features
+#
+# CONFIG_X86_PC is not set
+# CONFIG_X86_ELAN is not set
+# CONFIG_X86_VOYAGER is not set
+# CONFIG_X86_NUMAQ is not set
+# CONFIG_X86_SUMMIT is not set
+# CONFIG_X86_BIGSMP is not set
+# CONFIG_X86_VISWS is not set
+CONFIG_X86_GENERICARCH=y
+# CONFIG_X86_ES7000 is not set
+CONFIG_X86_CYCLONE_TIMER=y
+# CONFIG_M386 is not set
+# CONFIG_M486 is not set
+# CONFIG_M586 is not set
+# CONFIG_M586TSC is not set
+# CONFIG_M586MMX is not set
+CONFIG_M686=y
+# CONFIG_MPENTIUMII is not set
+# CONFIG_MPENTIUMIII is not set
+# CONFIG_MPENTIUMM is not set
+# CONFIG_MPENTIUM4 is not set
+# CONFIG_MK6 is not set
+# CONFIG_MK7 is not set
+# CONFIG_MK8 is not set
+# CONFIG_MCRUSOE is not set
+# CONFIG_MWINCHIPC6 is not set
+# CONFIG_MWINCHIP2 is not set
+# CONFIG_MWINCHIP3D is not set
+# CONFIG_MCYRIXIII is not set
+# CONFIG_MVIAC3_2 is not set
+CONFIG_X86_GENERIC=y
+CONFIG_X86_CMPXCHG=y
+CONFIG_X86_XADD=y
+CONFIG_X86_L1_CACHE_SHIFT=7
+CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_X86_PPRO_FENCE=y
+CONFIG_X86_WP_WORKS_OK=y
+CONFIG_X86_INVLPG=y
+CONFIG_X86_BSWAP=y
+CONFIG_X86_POPAD_OK=y
+CONFIG_X86_GOOD_APIC=y
+CONFIG_X86_INTEL_USERCOPY=y
+CONFIG_X86_USE_PPRO_CHECKSUM=y
+# CONFIG_X86_4G is not set
+# CONFIG_X86_SWITCH_PAGETABLES is not set
+# CONFIG_X86_4G_VM_LAYOUT is not set
+# CONFIG_X86_UACCESS_INDIRECT is not set
+# CONFIG_X86_HIGH_ENTRY is not set
+CONFIG_HPET_TIMER=y
+CONFIG_HPET_EMULATE_RTC=y
+CONFIG_SMP=y
+CONFIG_NR_CPUS=32
+CONFIG_SCHED_SMT=y
+# CONFIG_PREEMPT is not set
+CONFIG_PREEMPT_VOLUNTARY=y
+CONFIG_X86_LOCAL_APIC=y
+CONFIG_X86_IO_APIC=y
+CONFIG_X86_TSC=y
+CONFIG_X86_MCE=y
+# CONFIG_X86_MCE_NONFATAL is not set
+CONFIG_X86_MCE_P4THERMAL=y
+CONFIG_TOSHIBA=m
+CONFIG_I8K=m
+CONFIG_MICROCODE=m
+CONFIG_X86_MSR=m
+CONFIG_X86_CPUID=m
+
+#
+# Firmware Drivers
+#
+CONFIG_EDD=m
+# CONFIG_NOHIGHMEM is not set
+# CONFIG_HIGHMEM4G is not set
+CONFIG_HIGHMEM64G=y
+CONFIG_HIGHMEM=y
+CONFIG_X86_PAE=y
+# CONFIG_NUMA is not set
+CONFIG_HIGHPTE=y
+# CONFIG_MATH_EMULATION is not set
+CONFIG_MTRR=y
+# CONFIG_EFI is not set
+# CONFIG_IRQBALANCE is not set
+CONFIG_HAVE_DEC_LOCK=y
+CONFIG_REGPARM=y
+CONFIG_KEXEC=y
+
+#
+# Power management options (ACPI, APM)
+#
+CONFIG_PM=y
+# CONFIG_PM_DEBUG is not set
+# CONFIG_SOFTWARE_SUSPEND is not set
+
+#
+# ACPI (Advanced Configuration and Power Interface) Support
+#
+CONFIG_ACPI=y
+CONFIG_ACPI_BOOT=y
+CONFIG_ACPI_INTERPRETER=y
+CONFIG_ACPI_SLEEP=y
+CONFIG_ACPI_SLEEP_PROC_FS=y
+CONFIG_ACPI_AC=m
+CONFIG_ACPI_BATTERY=m
+CONFIG_ACPI_BUTTON=m
+CONFIG_ACPI_FAN=y
+CONFIG_ACPI_PROCESSOR=y
+CONFIG_ACPI_THERMAL=y
+CONFIG_ACPI_ASUS=m
+CONFIG_ACPI_TOSHIBA=m
+CONFIG_ACPI_BLACKLIST_YEAR=2001
+# CONFIG_ACPI_DEBUG is not set
+CONFIG_ACPI_BUS=y
+CONFIG_ACPI_EC=y
+CONFIG_ACPI_POWER=y
+CONFIG_ACPI_PCI=y
+CONFIG_ACPI_SYSTEM=y
+CONFIG_X86_PM_TIMER=y
+
+#
+# APM (Advanced Power Management) BIOS Support
+#
+CONFIG_APM=y
+# CONFIG_APM_IGNORE_USER_SUSPEND is not set
+# CONFIG_APM_DO_ENABLE is not set
+CONFIG_APM_CPU_IDLE=y
+# CONFIG_APM_DISPLAY_BLANK is not set
+CONFIG_APM_RTC_IS_GMT=y
+# CONFIG_APM_ALLOW_INTS is not set
+# CONFIG_APM_REAL_MODE_POWER_OFF is not set
+
+#
+# CPU Frequency scaling
+#
+CONFIG_CPU_FREQ=y
+# CONFIG_CPU_FREQ_PROC_INTF is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set
+CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=m
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=m
+# CONFIG_CPU_FREQ_24_API is not set
+CONFIG_CPU_FREQ_TABLE=y
+
+#
+# CPUFreq processor drivers
+#
+CONFIG_X86_ACPI_CPUFREQ=y
+# CONFIG_X86_ACPI_CPUFREQ_PROC_INTF is not set
+CONFIG_X86_POWERNOW_K6=m
+CONFIG_X86_POWERNOW_K7=y
+CONFIG_X86_POWERNOW_K7_ACPI=y
+CONFIG_X86_POWERNOW_K8=m
+CONFIG_X86_POWERNOW_K8_ACPI=y
+# CONFIG_X86_GX_SUSPMOD is not set
+# CONFIG_X86_SPEEDSTEP_CENTRINO is not set
+CONFIG_X86_SPEEDSTEP_ICH=y
+CONFIG_X86_SPEEDSTEP_SMI=m
+CONFIG_X86_P4_CLOCKMOD=m
+CONFIG_X86_SPEEDSTEP_LIB=y
+# CONFIG_X86_SPEEDSTEP_RELAXED_CAP_CHECK is not set
+CONFIG_X86_LONGRUN=y
+CONFIG_X86_LONGHAUL=y
 
 #
 # Bus options (PCI, PCMCIA, EISA, MCA, ISA)
 #
 CONFIG_PCI=y
+# CONFIG_PCI_GOBIOS is not set
+# CONFIG_PCI_GOMMCONFIG is not set
+# CONFIG_PCI_GODIRECT is not set
+CONFIG_PCI_GOANY=y
+CONFIG_PCI_BIOS=y
+CONFIG_PCI_DIRECT=y
+CONFIG_PCI_MMCONFIG=y
 CONFIG_PCI_MSI=y
 CONFIG_PCI_LEGACY_PROC=y
 # CONFIG_PCI_NAMES is not set
@@ -60,22 +241,37 @@ CONFIG_ISA=y
 # CONFIG_EISA is not set
 # CONFIG_MCA is not set
 # CONFIG_SCx200 is not set
-CONFIG_HOTPLUG=y
 
 #
 # PCMCIA/CardBus support
 #
 CONFIG_PCMCIA=m
 # CONFIG_PCMCIA_DEBUG is not set
-CONFIG_YENTA=y
+CONFIG_YENTA=m
 CONFIG_CARDBUS=y
+CONFIG_PD6729=m
 # CONFIG_I82092 is not set
 CONFIG_I82365=m
-CONFIG_PD6729=m
 CONFIG_TCIC=m
 CONFIG_PCMCIA_PROBE=y
 
 #
+# PCI Hotplug Support
+#
+CONFIG_HOTPLUG_PCI=y
+# CONFIG_HOTPLUG_PCI_FAKE is not set
+CONFIG_HOTPLUG_PCI_COMPAQ=m
+# CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM is not set
+CONFIG_HOTPLUG_PCI_IBM=m
+CONFIG_HOTPLUG_PCI_ACPI=m
+CONFIG_HOTPLUG_PCI_ACPI_IBM=m
+# CONFIG_HOTPLUG_PCI_CPCI is not set
+CONFIG_HOTPLUG_PCI_PCIE=m
+# CONFIG_HOTPLUG_PCI_PCIE_POLL_EVENT_MODE is not set
+CONFIG_HOTPLUG_PCI_SHPC=m
+# CONFIG_HOTPLUG_PCI_SHPC_POLL_EVENT_MODE is not set
+
+#
 # Executable file formats
 #
 CONFIG_BINFMT_ELF=y
@@ -89,18 +285,23 @@ CONFIG_BINFMT_MISC=y
 #
 # Generic Driver Options
 #
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
 CONFIG_FW_LOADER=y
+# CONFIG_DEBUG_DRIVER is not set
 
 #
 # Memory Technology Devices (MTD)
 #
 CONFIG_MTD=m
 # CONFIG_MTD_DEBUG is not set
-CONFIG_MTD_PARTITIONS=m
+CONFIG_MTD_PARTITIONS=y
 CONFIG_MTD_CONCAT=m
 CONFIG_MTD_REDBOOT_PARTS=m
+# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set
+# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set
 CONFIG_MTD_CMDLINE_PARTS=y
-CONFIG_MTD_PARTITIONS=y
+
 #
 # User Modules And Translation Layers
 #
@@ -119,13 +320,24 @@ CONFIG_MTD_CFI=m
 CONFIG_MTD_JEDECPROBE=m
 CONFIG_MTD_GEN_PROBE=m
 # CONFIG_MTD_CFI_ADV_OPTIONS is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
 CONFIG_MTD_CFI_INTELEXT=m
 CONFIG_MTD_CFI_AMDSTD=m
+CONFIG_MTD_CFI_AMDSTD_RETRY=3
 CONFIG_MTD_CFI_STAA=m
+CONFIG_MTD_CFI_UTIL=m
 CONFIG_MTD_RAM=m
 CONFIG_MTD_ROM=m
 CONFIG_MTD_ABSENT=m
-# CONFIG_MTD_OBSOLETE_CHIPS is not set
 
 #
 # Mapping drivers for chip access
@@ -135,23 +347,19 @@ CONFIG_MTD_ABSENT=m
 # CONFIG_MTD_PNC2000 is not set
 CONFIG_MTD_SC520CDP=m
 CONFIG_MTD_NETSC520=m
-CONFIG_MTD_SBC_GXX=m
-CONFIG_MTD_ELAN_104NC=m
 CONFIG_MTD_SCx200_DOCFLASH=m
 # CONFIG_MTD_AMD76XROM is not set
 # CONFIG_MTD_SCB2_FLASH is not set
 # CONFIG_MTD_NETtel is not set
 # CONFIG_MTD_DILNETPC is not set
 # CONFIG_MTD_L440GX is not set
-# CONFIG_MTD_PCI is not set
 
 #
 # Self-contained MTD device drivers
 #
 # CONFIG_MTD_PMC551 is not set
-# CONFIG_MTD_PMC551_BUGFIX is not set
-# CONFIG_MTD_PMC551_DEBUG is not set
 # CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
 CONFIG_MTD_MTDRAM=m
 CONFIG_MTDRAM_TOTAL_SIZE=4096
 CONFIG_MTDRAM_ERASE_SIZE=128
@@ -163,9 +371,6 @@ CONFIG_MTDRAM_ERASE_SIZE=128
 # CONFIG_MTD_DOC2000 is not set
 # CONFIG_MTD_DOC2001 is not set
 # CONFIG_MTD_DOC2001PLUS is not set
-CONFIG_MTD_DOCPROBE=m
-# CONFIG_MTD_DOCPROBE_ADVANCED is not set
-CONFIG_MTD_DOCPROBE_ADDRESS=0
 
 #
 # NAND Flash Device Drivers
@@ -173,15 +378,7 @@ CONFIG_MTD_DOCPROBE_ADDRESS=0
 CONFIG_MTD_NAND=m
 # CONFIG_MTD_NAND_VERIFY_WRITE is not set
 CONFIG_MTD_NAND_IDS=m
-
-# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set
-# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set
-CONFIG_MTD_CFI_AMDSTD_RETRY=3
-# CONFIG_MTD_ICHXROM is not set
-# CONFIG_MTD_PHRAM is not set
 # CONFIG_MTD_NAND_DISKONCHIP is not set
-# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set
-
 
 #
 # Parallel port support
@@ -222,45 +419,41 @@ CONFIG_BLK_DEV_DAC960=m
 CONFIG_BLK_DEV_LOOP=m
 CONFIG_BLK_DEV_CRYPTOLOOP=m
 CONFIG_BLK_DEV_NBD=m
+CONFIG_BLK_DEV_SX8=m
+# CONFIG_BLK_DEV_UB is not set
 CONFIG_BLK_DEV_RAM=y
 CONFIG_BLK_DEV_RAM_SIZE=16384
 CONFIG_BLK_DEV_INITRD=y
-CONFIG_BLK_DEV_ATIIXP=y
-CONFIG_BLK_DEV_DELKIN=y
-CONFIG_BLK_DEV_IT8212=y
 CONFIG_LBD=y
-# CONFIG_DCSSBLK is not set
-
+CONFIG_DISKDUMP=m
 
 #
 # ATA/ATAPI/MFM/RLL support
 #
 CONFIG_IDE=y
-CONFIG_IDE_GENERIC=y
-# CONFIG_HPT34X_AUTODMA is not set
-
 CONFIG_BLK_DEV_IDE=y
 
 #
 # Please see Documentation/ide.txt for help/info on IDE drives
 #
+# CONFIG_BLK_DEV_IDE_SATA is not set
 # CONFIG_BLK_DEV_HD_IDE is not set
 CONFIG_BLK_DEV_IDEDISK=y
 CONFIG_IDEDISK_MULTI_MODE=y
 CONFIG_BLK_DEV_IDECS=m
+CONFIG_BLK_DEV_DELKIN=m
 CONFIG_BLK_DEV_IDECD=y
 # CONFIG_BLK_DEV_IDETAPE is not set
 CONFIG_BLK_DEV_IDEFLOPPY=y
 CONFIG_BLK_DEV_IDESCSI=m
 # CONFIG_IDE_TASK_IOCTL is not set
 # CONFIG_IDE_TASKFILE_IO is not set
-# CONFIG_BLK_DEV_IDE_SATA is not set
 
 #
 # IDE chipset support/bugfixes
 #
+CONFIG_IDE_GENERIC=y
 # CONFIG_BLK_DEV_CMD640 is not set
-# CONFIG_BLK_DEV_CMD640_ENHANCED is not set
 CONFIG_BLK_DEV_IDEPNP=y
 CONFIG_BLK_DEV_IDEPCI=y
 CONFIG_IDEPCI_SHARE_IRQ=y
@@ -276,15 +469,18 @@ CONFIG_BLK_DEV_AEC62XX=y
 CONFIG_BLK_DEV_ALI15X3=y
 # CONFIG_WDC_ALI15X3 is not set
 CONFIG_BLK_DEV_AMD74XX=y
+CONFIG_BLK_DEV_ATIIXP=y
 CONFIG_BLK_DEV_CMD64X=y
 CONFIG_BLK_DEV_TRIFLEX=y
 CONFIG_BLK_DEV_CY82C693=y
 CONFIG_BLK_DEV_CS5520=y
 CONFIG_BLK_DEV_CS5530=y
 CONFIG_BLK_DEV_HPT34X=y
+# CONFIG_HPT34X_AUTODMA is not set
 CONFIG_BLK_DEV_HPT366=y
 # CONFIG_BLK_DEV_SC1200 is not set
 CONFIG_BLK_DEV_PIIX=y
+CONFIG_BLK_DEV_IT8212=y
 # CONFIG_BLK_DEV_NS87415 is not set
 CONFIG_BLK_DEV_PDC202XX_OLD=y
 # CONFIG_PDC202XX_BURST is not set
@@ -296,6 +492,7 @@ CONFIG_BLK_DEV_SIS5513=y
 CONFIG_BLK_DEV_SLC90E66=y
 # CONFIG_BLK_DEV_TRM290 is not set
 CONFIG_BLK_DEV_VIA82CXXX=y
+# CONFIG_IDE_ARM is not set
 # CONFIG_IDE_CHIPSETS is not set
 CONFIG_BLK_DEV_IDEDMA=y
 # CONFIG_IDEDMA_IVB is not set
@@ -312,6 +509,8 @@ CONFIG_SCSI_PROC_FS=y
 # SCSI support type (disk, tape, CD-ROM)
 #
 CONFIG_BLK_DEV_SD=m
+CONFIG_SCSI_DUMP=m
+CONFIG_SD_IOSTATS=y
 CONFIG_CHR_DEV_ST=m
 CONFIG_CHR_DEV_OSST=m
 CONFIG_BLK_DEV_SR=m
@@ -324,8 +523,12 @@ CONFIG_CHR_DEV_SG=m
 # CONFIG_SCSI_MULTI_LUN is not set
 CONFIG_SCSI_CONSTANTS=y
 CONFIG_SCSI_LOGGING=y
-CONFIG_SCSI_SPI_ATTRS=y
-CONFIG_SCSI_FC_ATTRS=y
+
+#
+# SCSI Transport Attributes
+#
+CONFIG_SCSI_SPI_ATTRS=m
+CONFIG_SCSI_FC_ATTRS=m
 
 #
 # SCSI low-level drivers
@@ -340,8 +543,6 @@ CONFIG_SCSI_AACRAID=m
 CONFIG_SCSI_AIC7XXX=m
 CONFIG_AIC7XXX_CMDS_PER_DEVICE=4
 CONFIG_AIC7XXX_RESET_DELAY_MS=15000
-# CONFIG_AIC7XXX_PROBE_EISA_VL is not set
-# CONFIG_AIC7XXX_BUILD_FIRMWARE is not set
 # CONFIG_AIC7XXX_DEBUG_ENABLE is not set
 CONFIG_AIC7XXX_DEBUG_MASK=0
 # CONFIG_AIC7XXX_REG_PRETTY_PRINT is not set
@@ -349,42 +550,38 @@ CONFIG_SCSI_AIC7XXX_OLD=m
 CONFIG_SCSI_AIC79XX=m
 CONFIG_AIC79XX_CMDS_PER_DEVICE=4
 CONFIG_AIC79XX_RESET_DELAY_MS=15000
-# CONFIG_AIC79XX_BUILD_FIRMWARE is not set
 # CONFIG_AIC79XX_ENABLE_RD_STRM is not set
 # CONFIG_AIC79XX_DEBUG_ENABLE is not set
 CONFIG_AIC79XX_DEBUG_MASK=0
 # CONFIG_AIC79XX_REG_PRETTY_PRINT is not set
-# CONFIG_SCSI_ADVANSYS is not set
+# CONFIG_SCSI_DPT_I2O is not set
 # CONFIG_SCSI_IN2000 is not set
 CONFIG_MEGARAID_NEWGEN=y
 CONFIG_MEGARAID_MM=m
 CONFIG_MEGARAID_MAILBOX=m
 CONFIG_SCSI_SATA=y
+CONFIG_SCSI_SATA_AHCI=m
 CONFIG_SCSI_SATA_SVW=m
 CONFIG_SCSI_ATA_PIIX=m
+CONFIG_SCSI_SATA_NV=m
 CONFIG_SCSI_SATA_PROMISE=m
-CONFIG_SCSI_SATA_VIA=m
-CONFIG_BLK_DEV_SX8=m
-CONFIG_SCSI_SATA_VITESSE=m
+CONFIG_SCSI_SATA_SX4=m
 CONFIG_SCSI_SATA_SIL=m
 CONFIG_SCSI_SATA_SIS=m
-CONFIG_SCSI_SATA_SX4=m
-CONFIG_SCSI_SATA_NV=m
-CONFIG_SCSI_SATA_AHCI=m
-
+CONFIG_SCSI_SATA_VIA=m
+CONFIG_SCSI_SATA_VITESSE=m
 # CONFIG_SCSI_BUSLOGIC is not set
-CONFIG_SCSI_INITIO=m
-# CONFIG_SCSI_OMIT_FLASHPOINT is not set
-# CONFIG_SCSI_CPQFCTS is not set
 # CONFIG_SCSI_DMX3191D is not set
 # CONFIG_SCSI_DTC3280 is not set
 # CONFIG_SCSI_EATA is not set
 # CONFIG_SCSI_EATA_PIO is not set
+CONFIG_SCSI_LPFC=m
 CONFIG_SCSI_FUTURE_DOMAIN=m
 CONFIG_SCSI_GDTH=m
 # CONFIG_SCSI_GENERIC_NCR5380 is not set
 # CONFIG_SCSI_GENERIC_NCR5380_MMIO is not set
 CONFIG_SCSI_IPS=m
+CONFIG_SCSI_INITIO=m
 # CONFIG_SCSI_INIA100 is not set
 CONFIG_SCSI_PPA=m
 CONFIG_SCSI_IMM=m
@@ -396,21 +593,13 @@ CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=1
 CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16
 CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64
 # CONFIG_SCSI_SYM53C8XX_IOMAPPED is not set
+# CONFIG_SCSI_IPR is not set
 # CONFIG_SCSI_PAS16 is not set
 # CONFIG_SCSI_PSI240I is not set
 # CONFIG_SCSI_QLOGIC_FAS is not set
 # CONFIG_SCSI_QLOGIC_ISP is not set
 # CONFIG_SCSI_QLOGIC_FC is not set
-# CONFIG_SCSI_QLOGIC_FC_FIRMWARE is not set
 CONFIG_SCSI_QLOGIC_1280=m
-# CONFIG_SCSI_SYM53C416 is not set
-# CONFIG_SCSI_DC395x is not set
-# CONFIG_SCSI_T128 is not set
-# CONFIG_SCSI_U14_34F is not set
-# CONFIG_SCSI_ULTRASTOR is not set
-# CONFIG_SCSI_NSP32 is not set
-# CONFIG_SCSI_DEBUG is not set
-# CONFIG_SCSI_DC390T is not set
 CONFIG_SCSI_QLA2XXX=m
 CONFIG_SCSI_QLA21XX=m
 CONFIG_SCSI_QLA22XX=m
@@ -418,11 +607,15 @@ CONFIG_SCSI_QLA2300=m
 CONFIG_SCSI_QLA2322=m
 CONFIG_SCSI_QLA6312=m
 CONFIG_SCSI_QLA6322=m
-# CONFIG_SCSI_IPR is not set
-# CONFIG_SCSI_DPT_I2O is not set
-
-CONFIG_SCSI_LPFC=m
-
+# CONFIG_SCSI_SYM53C416 is not set
+# CONFIG_SCSI_DC395x is not set
+# CONFIG_SCSI_DC390T is not set
+# CONFIG_SCSI_T128 is not set
+# CONFIG_SCSI_U14_34F is not set
+# CONFIG_SCSI_ULTRASTOR is not set
+# CONFIG_SCSI_NSP32 is not set
+# CONFIG_SCSI_DEBUG is not set
+
 #
 # PCMCIA SCSI adapter support
 #
@@ -432,7 +625,6 @@ CONFIG_PCMCIA_FDOMAIN=m
 # CONFIG_PCMCIA_QLOGIC is not set
 # CONFIG_PCMCIA_SYM53C500 is not set
 
-
 #
 # Old CD-ROM drivers (not SCSI, not IDE)
 #
@@ -446,15 +638,15 @@ CONFIG_BLK_DEV_MD=y
 CONFIG_MD_LINEAR=m
 CONFIG_MD_RAID0=m
 CONFIG_MD_RAID1=m
+CONFIG_MD_RAID10=m
 CONFIG_MD_RAID5=m
 CONFIG_MD_RAID6=m
-CONFIG_MD_RAID10=m
 CONFIG_MD_MULTIPATH=m
 CONFIG_BLK_DEV_DM=m
+CONFIG_DM_CRYPT=m
 CONFIG_DM_SNAPSHOT=m
 CONFIG_DM_MIRROR=m
 CONFIG_DM_ZERO=m
-CONFIG_DM_CRYPT=m
 
 #
 # Fusion MPT device support
@@ -465,39 +657,18 @@ CONFIG_FUSION_CTL=m
 CONFIG_FUSION_LAN=m
 
 #
-# IEEE 1394 (FireWire) support (EXPERIMENTAL)
+# IEEE 1394 (FireWire) support
 #
 # CONFIG_IEEE1394 is not set
 
 #
-# Subsystem Options
-#
-# CONFIG_IEEE1394_VERBOSEDEBUG is not set
-CONFIG_IEEE1394_OUI_DB=y
-
-#
-# Device Drivers
-#
-# CONFIG_IEEE1394_PCILYNX is not set
-CONFIG_IEEE1394_OHCI1394=m
-
-#
-# Protocol Drivers
-#
-CONFIG_IEEE1394_VIDEO1394=m
-CONFIG_IEEE1394_SBP2=m
-# CONFIG_IEEE1394_SBP2_PHYS_DMA is not set
-# CONFIG_IEEE1394_ETH1394 is not set
-CONFIG_IEEE1394_DV1394=m
-CONFIG_IEEE1394_RAWIO=m
-CONFIG_IEEE1394_CMP=m
-CONFIG_IEEE1394_AMDTP=m
-# CONFIG_IEEE1394_EXTRA_CONFIG_ROMS is not set
-
-#
 # I2O device support
 #
 CONFIG_I2O=m
+CONFIG_I2O_CONFIG=m
+CONFIG_I2O_BLOCK=m
+CONFIG_I2O_SCSI=m
+CONFIG_I2O_PROC=m
 
 #
 # Networking support
@@ -513,7 +684,6 @@ CONFIG_NETLINK_DEV=y
 CONFIG_UNIX=y
 CONFIG_NET_KEY=m
 CONFIG_INET=y
-CONFIG_INET_TUNNEL=m
 CONFIG_IP_MULTICAST=y
 CONFIG_IP_ADVANCED_ROUTER=y
 CONFIG_IP_MULTIPLE_TABLES=y
@@ -532,14 +702,7 @@ CONFIG_SYN_COOKIES=y
 CONFIG_INET_AH=m
 CONFIG_INET_ESP=m
 CONFIG_INET_IPCOMP=m
-CONFIG_NETCONSOLE=m
-# CONFIG_NETPOLL_RX is not set
-CONFIG_NETPOLL_TRAP=y
-CONFIG_NET_POLL_CONTROLLER=y
-CONFIG_NETDUMP=m
-CONFIG_DISKDUMP=m
-CONFIG_SCSI_DUMP=m
-CONFIG_SD_IOSTATS=y
+CONFIG_INET_TUNNEL=m
 
 #
 # IP: Virtual Server Configuration
@@ -547,10 +710,18 @@ CONFIG_SD_IOSTATS=y
 CONFIG_IP_VS=m
 # CONFIG_IP_VS_DEBUG is not set
 CONFIG_IP_VS_TAB_BITS=12
+
+#
+# IPVS transport protocol load balancing support
+#
 CONFIG_IP_VS_PROTO_TCP=y
 CONFIG_IP_VS_PROTO_UDP=y
 CONFIG_IP_VS_PROTO_ESP=y
 CONFIG_IP_VS_PROTO_AH=y
+
+#
+# IPVS scheduler
+#
 CONFIG_IP_VS_RR=m
 CONFIG_IP_VS_WRR=m
 CONFIG_IP_VS_LC=m
@@ -561,17 +732,18 @@ CONFIG_IP_VS_DH=m
 CONFIG_IP_VS_SH=m
 CONFIG_IP_VS_SED=m
 CONFIG_IP_VS_NQ=m
-CONFIG_IP_VS_FTP=m
-
 
+#
+# IPVS application helper
+#
+CONFIG_IP_VS_FTP=m
 CONFIG_IPV6=m
 CONFIG_IPV6_PRIVACY=y
 CONFIG_INET6_AH=m
 CONFIG_INET6_ESP=m
 CONFIG_INET6_IPCOMP=m
+CONFIG_INET6_TUNNEL=m
 CONFIG_IPV6_TUNNEL=m
-# CONFIG_DECNET is not set
-CONFIG_BRIDGE=m
 CONFIG_NETFILTER=y
 # CONFIG_NETFILTER_DEBUG is not set
 CONFIG_BRIDGE_NETFILTER=y
@@ -580,14 +752,14 @@ CONFIG_BRIDGE_NETFILTER=y
 # IP: Netfilter Configuration
 #
 CONFIG_IP_NF_CONNTRACK=m
+CONFIG_IP_NF_CT_ACCT=y
+CONFIG_IP_NF_CT_PROTO_SCTP=m
 CONFIG_IP_NF_FTP=m
 CONFIG_IP_NF_IRC=m
 CONFIG_IP_NF_TFTP=m
 CONFIG_IP_NF_AMANDA=m
 CONFIG_IP_NF_QUEUE=m
-CONFIG_IP_NF_RAW=m
 CONFIG_IP_NF_IPTABLES=m
-CONFIG_IP_NF_MATCH_COMMENT=m
 CONFIG_IP_NF_MATCH_LIMIT=m
 CONFIG_IP_NF_MATCH_IPRANGE=m
 CONFIG_IP_NF_MATCH_MAC=m
@@ -610,15 +782,18 @@ CONFIG_IP_NF_MATCH_PHYSDEV=m
 CONFIG_IP_NF_MATCH_ADDRTYPE=m
 CONFIG_IP_NF_MATCH_REALM=m
 CONFIG_IP_NF_MATCH_SCTP=m
+CONFIG_IP_NF_MATCH_COMMENT=m
 CONFIG_IP_NF_FILTER=m
 CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_LOG=m
+CONFIG_IP_NF_TARGET_ULOG=m
+CONFIG_IP_NF_TARGET_TCPMSS=m
 CONFIG_IP_NF_NAT=m
 CONFIG_IP_NF_NAT_NEEDED=y
 CONFIG_IP_NF_TARGET_MASQUERADE=m
 CONFIG_IP_NF_TARGET_REDIRECT=m
 CONFIG_IP_NF_TARGET_NETMAP=m
 CONFIG_IP_NF_TARGET_SAME=m
-CONFIG_IP_NF_TARGET_NOTRACK=m
 CONFIG_IP_NF_NAT_LOCAL=y
 CONFIG_IP_NF_NAT_SNMP_BASIC=m
 CONFIG_IP_NF_NAT_IRC=m
@@ -631,14 +806,11 @@ CONFIG_IP_NF_TARGET_ECN=m
 CONFIG_IP_NF_TARGET_DSCP=m
 CONFIG_IP_NF_TARGET_MARK=m
 CONFIG_IP_NF_TARGET_CLASSIFY=m
-CONFIG_IP_NF_TARGET_LOG=m
-CONFIG_IP_NF_TARGET_ULOG=m
-CONFIG_IP_NF_TARGET_TCPMSS=m
+CONFIG_IP_NF_RAW=m
+CONFIG_IP_NF_TARGET_NOTRACK=m
 CONFIG_IP_NF_ARPTABLES=m
 CONFIG_IP_NF_ARPFILTER=m
 CONFIG_IP_NF_ARP_MANGLE=m
-CONFIG_IP_NF_CT_ACCT=y
-CONFIG_IP_NF_CT_PROTO_SCTP=m
 # CONFIG_IP_NF_COMPAT_IPCHAINS is not set
 # CONFIG_IP_NF_COMPAT_IPFWADM is not set
 
@@ -667,7 +839,6 @@ CONFIG_IP6_NF_MANGLE=m
 CONFIG_IP6_NF_TARGET_MARK=m
 CONFIG_IP6_NF_RAW=m
 
-
 #
 # Bridge: Netfilter Configuration
 #
@@ -703,33 +874,36 @@ CONFIG_IP_SCTP=m
 # CONFIG_SCTP_HMAC_SHA1 is not set
 CONFIG_SCTP_HMAC_MD5=y
 CONFIG_ATM=m
+CONFIG_ATM_CLIP=m
+# CONFIG_ATM_CLIP_NO_ICMP is not set
+CONFIG_ATM_LANE=m
+# CONFIG_ATM_MPOA is not set
+CONFIG_ATM_BR2684=m
+# CONFIG_ATM_BR2684_IPFILTER is not set
+CONFIG_BRIDGE=m
 CONFIG_VLAN_8021Q=m
-CONFIG_LLC=m
+# CONFIG_DECNET is not set
+CONFIG_LLC=y
 # CONFIG_LLC2 is not set
 # CONFIG_IPX is not set
-# CONFIG_IPX_INTERN is not set
 # CONFIG_ATALK is not set
-# CONFIG_DEV_APPLETALK is not set
-# CONFIG_LTPC is not set
-# CONFIG_COPS is not set
-CONFIG_COPS_DAYNA=y
-CONFIG_COPS_TANGENT=y
-# CONFIG_IPDDP is not set
-CONFIG_IPDDP_ENCAP=y
-CONFIG_IPDDP_DECAP=y
 # CONFIG_X25 is not set
 # CONFIG_LAPB is not set
 CONFIG_NET_DIVERT=y
 # CONFIG_ECONET is not set
 # CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_HW_FLOWCONTROL is not set
 
 #
 # QoS and/or fair queueing
 #
 CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_CLK_JIFFIES=y
+# CONFIG_NET_SCH_CLK_GETTIMEOFDAY is not set
+# CONFIG_NET_SCH_CLK_CPU is not set
 CONFIG_NET_SCH_CBQ=m
 CONFIG_NET_SCH_HTB=m
+CONFIG_NET_SCH_HFSC=m
+CONFIG_NET_SCH_ATM=m
 CONFIG_NET_SCH_PRIO=m
 CONFIG_NET_SCH_RED=m
 CONFIG_NET_SCH_SFQ=m
@@ -737,9 +911,8 @@ CONFIG_NET_SCH_TEQL=m
 CONFIG_NET_SCH_TBF=m
 CONFIG_NET_SCH_GRED=m
 CONFIG_NET_SCH_DSMARK=m
-CONFIG_NET_SCH_INGRESS=m
-CONFIG_NET_SCH_HFSC=m
 CONFIG_NET_SCH_NETEM=m
+CONFIG_NET_SCH_INGRESS=m
 CONFIG_NET_QOS=y
 CONFIG_NET_ESTIMATOR=y
 CONFIG_NET_CLS=y
@@ -748,24 +921,59 @@ CONFIG_NET_CLS_ROUTE4=m
 CONFIG_NET_CLS_ROUTE=y
 CONFIG_NET_CLS_FW=m
 CONFIG_NET_CLS_U32=m
-CONFIG_NET_CLS_RSVP=m
-CONFIG_NET_CLS_RSVP6=m
-CONFIG_NET_CLS_POLICE=y
-# CONFIG_NET_ACT_POLICE is not set
 CONFIG_CLS_U32_PERF=y
 CONFIG_NET_CLS_IND=y
+CONFIG_NET_CLS_RSVP=m
+CONFIG_NET_CLS_RSVP6=m
 # CONFIG_NET_CLS_ACT is not set
+CONFIG_NET_CLS_POLICE=y
 
 #
 # Network testing
 #
 # CONFIG_NET_PKTGEN is not set
-CONFIG_NETDEVICES=y
+CONFIG_NETPOLL=y
+# CONFIG_NETPOLL_RX is not set
+CONFIG_NETPOLL_TRAP=y
+CONFIG_NET_POLL_CONTROLLER=y
+# CONFIG_HAMRADIO is not set
+# CONFIG_IRDA is not set
+CONFIG_BT=m
+CONFIG_BT_L2CAP=m
+CONFIG_BT_SCO=m
+CONFIG_BT_RFCOMM=m
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=m
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+CONFIG_BT_CMTP=m
+CONFIG_BT_HIDP=m
 
 #
-# ARCnet devices
+# Bluetooth device drivers
 #
-# CONFIG_ARCNET is not set
+CONFIG_BT_HCIUSB=m
+CONFIG_BT_HCIUSB_SCO=y
+CONFIG_BT_HCIUART=m
+CONFIG_BT_HCIUART_H4=y
+CONFIG_BT_HCIUART_BCSP=y
+CONFIG_BT_HCIUART_BCSP_TXCRC=y
+CONFIG_BT_HCIBCM203X=m
+CONFIG_BT_HCIBFUSB=m
+CONFIG_BT_HCIDTL1=m
+CONFIG_BT_HCIBT3C=m
+CONFIG_BT_HCIBLUECARD=m
+CONFIG_BT_HCIBTUART=m
+CONFIG_BT_HCIVHCI=m
+CONFIG_TUX=m
+
+#
+# TUX options
+#
+CONFIG_TUX_EXTCGI=y
+# CONFIG_TUX_EXTENDED_LOG is not set
+# CONFIG_TUX_DEBUG is not set
+CONFIG_NETDEVICES=y
 CONFIG_DUMMY=m
 CONFIG_BONDING=m
 # CONFIG_EQUALIZER is not set
@@ -774,46 +982,9 @@ CONFIG_ETHERTAP=m
 # CONFIG_NET_SB1000 is not set
 
 #
-# ATM
-# 
-CONFIG_ATM_CLIP=m
-CONFIG_ATM_LANE=m
-CONFIG_ATM_BR2684=m
-CONFIG_NET_SCH_ATM=m
-CONFIG_ATM_TCP=m
-CONFIG_ATM_LANAI=m
-CONFIG_ATM_ENI=m
-CONFIG_ATM_FIRESTREAM=m
-# CONFIG_ATM_ZATM is not set
-CONFIG_ATM_IDT77252=m
-CONFIG_ATM_AMBASSADOR=m
-CONFIG_ATM_HORIZON=m
-CONFIG_ATM_FORE200E_MAYBE=m
-CONFIG_ATM_HE=m
-CONFIG_PPPOATM=m
-CONFIG_ATM_NICSTAR=m
-# CONFIG_ATM_IA is not set
-
-
-# CONFIG_ATM_CLIP_NO_ICMP is not set
-# CONFIG_ATM_MPOA is not set
-# CONFIG_ATM_BR2684_IPFILTER is not set
-# CONFIG_ATM_ENI_DEBUG is not set
-# CONFIG_ATM_ENI_TUNE_BURST is not set
-# CONFIG_ATM_ZATM_DEBUG is not set
-# CONFIG_ATM_IDT77252_DEBUG is not set
-# CONFIG_ATM_IDT77252_RCV_ALL is not set
-# CONFIG_ATM_AMBASSADOR_DEBUG is not set
-# CONFIG_ATM_HORIZON_DEBUG is not set
-# CONFIG_ATM_FORE200E_PCA is not set
-# CONFIG_ATM_HE_USE_SUNI is not set
-# CONFIG_ATM_NICSTAR_USE_SUNI is not set
-# CONFIG_ATM_NICSTAR_USE_IDT77105 is not set
-# CONFIG_ATM_IA_DEBUG is not set
-
-
-
-
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
 
 #
 # Ethernet (10 or 100Mbit)
@@ -826,7 +997,6 @@ CONFIG_NET_VENDOR_3COM=y
 # CONFIG_EL1 is not set
 # CONFIG_EL2 is not set
 # CONFIG_ELPLUS is not set
-# CONFIG_ELPLUS is not set
 # CONFIG_EL16 is not set
 # CONFIG_EL3 is not set
 # CONFIG_3C515 is not set
@@ -847,12 +1017,9 @@ CONFIG_NET_VENDOR_RACAL=y
 CONFIG_NET_TULIP=y
 CONFIG_DE2104X=m
 CONFIG_TULIP=m
-# CONFIG_TULIP_NAPI is not set
-
 # CONFIG_TULIP_MWI is not set
 CONFIG_TULIP_MMIO=y
-# CONFIG_NI5010 is not set
-# CONFIG_PCMCIA_XIRTULIP is not set
+# CONFIG_TULIP_NAPI is not set
 CONFIG_DE4X5=m
 CONFIG_WINBOND_840=m
 CONFIG_DM9102=m
@@ -861,20 +1028,6 @@ CONFIG_PCMCIA_XIRCOM=m
 # CONFIG_DEPCA is not set
 CONFIG_HP100=m
 # CONFIG_NET_ISA is not set
-# CONFIG_EWRK3 is not set
-CONFIG_E2100=m
-CONFIG_EEXPRESS=m
-CONFIG_EEXPRESS_PRO=m
-CONFIG_HPLAN_PLUS=m
-CONFIG_HPLAN=m
-CONFIG_LP486E=m
-CONFIG_ETH16I=m
-CONFIG_NE2000=m
-CONFIG_ZNET=m
-CONFIG_SEEQ8005=m
-CONFIG_LNE390=m
-CONFIG_NE3210=m
-CONFIG_ES3210=m
 CONFIG_NET_PCI=y
 CONFIG_PCNET32=m
 CONFIG_AMD8111_ETH=m
@@ -884,6 +1037,7 @@ CONFIG_ADAPTEC_STARFIRE_NAPI=y
 # CONFIG_AC3200 is not set
 CONFIG_APRICOT=m
 CONFIG_B44=m
+CONFIG_FORCEDETH=m
 # CONFIG_CS89x0 is not set
 # CONFIG_DGRS is not set
 CONFIG_EEPRO100=m
@@ -891,7 +1045,6 @@ CONFIG_EEPRO100=m
 CONFIG_E100=m
 CONFIG_E100_NAPI=y
 CONFIG_FEALNX=m
-CONFIG_FORCEDETH=m
 CONFIG_NATSEMI=m
 CONFIG_NE2K_PCI=m
 CONFIG_8139CP=m
@@ -903,11 +1056,9 @@ CONFIG_8139TOO_8129=y
 CONFIG_SIS900=m
 CONFIG_EPIC100=m
 # CONFIG_SUNDANCE is not set
-# CONFIG_SUNDANCE_MMIO is not set
 CONFIG_TLAN=m
 CONFIG_VIA_RHINE=m
 CONFIG_VIA_RHINE_MMIO=y
-CONFIG_VIA_VELOCITY=m
 CONFIG_NET_POCKET=y
 # CONFIG_ATP is not set
 # CONFIG_DE600 is not set
@@ -927,6 +1078,7 @@ CONFIG_NS83820=m
 CONFIG_R8169=m
 CONFIG_R8169_NAPI=y
 CONFIG_SK98LIN=m
+CONFIG_VIA_VELOCITY=m
 CONFIG_TIGON3=m
 
 #
@@ -936,21 +1088,21 @@ CONFIG_IXGB=m
 CONFIG_IXGB_NAPI=y
 CONFIG_S2IO=m
 CONFIG_S2IO_NAPI=y
-CONFIG_FDDI=y
-# CONFIG_DEFXX is not set
-# CONFIG_SKFP is not set
-# CONFIG_HIPPI is not set
-# CONFIG_PLIP is not set
-CONFIG_PPP=m
-CONFIG_PPP_MULTILINK=y
-CONFIG_PPP_FILTER=y
-CONFIG_PPP_ASYNC=m
-CONFIG_PPP_SYNC_TTY=m
-CONFIG_PPP_DEFLATE=m
-CONFIG_IPPP_FILTER=y
-# CONFIG_PPP_BSDCOMP is not set
-CONFIG_PPPOE=m
-# CONFIG_SLIP is not set
+
+#
+# Token Ring devices
+#
+CONFIG_TR=y
+CONFIG_IBMTR=m
+CONFIG_IBMOL=m
+CONFIG_IBMLS=m
+CONFIG_3C359=m
+CONFIG_TMS380TR=m
+CONFIG_TMSPCI=m
+CONFIG_SKISA=m
+CONFIG_PROTEON=m
+CONFIG_ABYSS=m
+CONFIG_SMCTR=m
 
 #
 # Wireless LAN (non-hamradio)
@@ -992,7 +1144,6 @@ CONFIG_TMD_HERMES=m
 CONFIG_PCI_HERMES=m
 CONFIG_ATMEL=m
 CONFIG_PCI_ATMEL=m
-CONFIG_PRISM54=m
 
 #
 # Wireless 802.11b Pcmcia/Cardbus cards support
@@ -1001,32 +1152,12 @@ CONFIG_PCMCIA_HERMES=m
 CONFIG_AIRO_CS=m
 CONFIG_PCMCIA_ATMEL=m
 CONFIG_PCMCIA_WL3501=m
-CONFIG_NET_WIRELESS=y
-
-#
-# Token Ring devices
-#
-CONFIG_TR=y
-CONFIG_IBMOL=m
-CONFIG_3C359=m
-CONFIG_TMS380TR=m
-CONFIG_TMSPCI=m
-CONFIG_ABYSS=m
-CONFIG_IBMTR=m
-CONFIG_IBMLS=m
-CONFIG_SKISA=m
-CONFIG_PROTEON=m
-CONFIG_SMCTR=m
-CONFIG_PCMCIA_IBMTR=m
-
-
-CONFIG_NET_FC=y
-# CONFIG_SHAPER is not set
 
 #
-# Wan interfaces
+# Prism GT/Duette 802.11(a/b/g) PCI/Cardbus support
 #
-# CONFIG_WAN is not set
+CONFIG_PRISM54=m
+CONFIG_NET_WIRELESS=y
 
 #
 # PCMCIA network device support
@@ -1040,119 +1171,104 @@ CONFIG_PCMCIA_NMCLAN=m
 CONFIG_PCMCIA_SMC91C92=m
 CONFIG_PCMCIA_XIRC2PS=m
 CONFIG_PCMCIA_AXNET=m
+CONFIG_PCMCIA_IBMTR=m
 
 #
-# Amateur Radio support
-#
-# CONFIG_HAMRADIO is not set
-
-#
-# IrDA (infrared) support
-#
-# CONFIG_IRDA is not set
-# CONFIG_IRDA is not set
-# CONFIG_IRDA_DEBUG is not set
-CONFIG_IRLAN=m
-CONFIG_IRNET=m
-CONFIG_IRCOMM=m
-# CONFIG_IRDA_ULTRA is not set
-CONFIG_IRDA_CACHE_LAST_LSAP=y
-CONFIG_IRDA_FAST_RR=y
-CONFIG_IRTTY_SIR=m
-CONFIG_DONGLE=y
-CONFIG_ESI_DONGLE=m
-CONFIG_ACTISYS_DONGLE=m
-CONFIG_TEKRAM_DONGLE=m
-CONFIG_IRPORT_SIR=m
-# CONFIG_DONGLE_OLD is not set
-CONFIG_LITELINK_DONGLE=m
-CONFIG_MA600_DONGLE=m
-CONFIG_GIRBIL_DONGLE=m
-CONFIG_MCP2120_DONGLE=m
-CONFIG_OLD_BELKIN_DONGLE=m
-CONFIG_ACT200L_DONGLE=m
-
-CONFIG_USB_IRDA=m
-CONFIG_NSC_FIR=m
-CONFIG_SIGMATEL_FIR=m
-# CONFIG_WINBOND_FIR is not set
-# CONFIG_TOSHIBA_FIR is not set
-# CONFIG_SMC_IRCC_FIR is not set
-# CONFIG_ALI_FIR is not set
-# CONFIG_VLSI_FIR is not set
-# CONFIG_VIA_FIR is not set
-
-
-
-#
-# Bluetooth support
+# Wan interfaces
 #
-CONFIG_BT=m
-CONFIG_BT_L2CAP=m
-CONFIG_BT_SCO=m
-CONFIG_BT_CMTP=m
-CONFIG_BT_RFCOMM=m
-CONFIG_BT_RFCOMM_TTY=y
-CONFIG_BT_BNEP=m
-CONFIG_BT_BNEP_MC_FILTER=y
-CONFIG_BT_BNEP_PROTO_FILTER=y
-CONFIG_BT_HIDP=m
+# CONFIG_WAN is not set
 
 #
-# Bluetooth device drivers
+# ATM drivers
 #
-CONFIG_BT_HCIUSB=m
-CONFIG_BT_HCIUART=m
-CONFIG_BT_HCIUART_H4=y
-CONFIG_BT_HCIUART_BCSP=y
-CONFIG_BT_HCIUART_BCSP_TXCRC=y
-CONFIG_BT_HCIDTL1=m
-CONFIG_BT_HCIBT3C=m
-CONFIG_BT_HCIBLUECARD=m
-CONFIG_BT_HCIBTUART=m
-CONFIG_BT_HCIVHCI=m
-CONFIG_BT_HCIUSB_SCO=y
-CONFIG_BT_HCIBCM203X=m
-CONFIG_BT_HCIBFUSB=m
-CONFIG_USB_BLUETOOTH_TTY=m
+CONFIG_ATM_TCP=m
+CONFIG_ATM_LANAI=m
+CONFIG_ATM_ENI=m
+# CONFIG_ATM_ENI_DEBUG is not set
+# CONFIG_ATM_ENI_TUNE_BURST is not set
+CONFIG_ATM_FIRESTREAM=m
+# CONFIG_ATM_ZATM is not set
+CONFIG_ATM_NICSTAR=m
+# CONFIG_ATM_NICSTAR_USE_SUNI is not set
+# CONFIG_ATM_NICSTAR_USE_IDT77105 is not set
+CONFIG_ATM_IDT77252=m
+# CONFIG_ATM_IDT77252_DEBUG is not set
+# CONFIG_ATM_IDT77252_RCV_ALL is not set
+CONFIG_ATM_IDT77252_USE_SUNI=y
+CONFIG_ATM_AMBASSADOR=m
+# CONFIG_ATM_AMBASSADOR_DEBUG is not set
+CONFIG_ATM_HORIZON=m
+# CONFIG_ATM_HORIZON_DEBUG is not set
+# CONFIG_ATM_IA is not set
+CONFIG_ATM_FORE200E_MAYBE=m
+# CONFIG_ATM_FORE200E_PCA is not set
+CONFIG_ATM_HE=m
+# CONFIG_ATM_HE_USE_SUNI is not set
+CONFIG_FDDI=y
+# CONFIG_DEFXX is not set
+# CONFIG_SKFP is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PLIP is not set
+CONFIG_PPP=m
+CONFIG_PPP_MULTILINK=y
+CONFIG_PPP_FILTER=y
+CONFIG_PPP_ASYNC=m
+CONFIG_PPP_SYNC_TTY=m
+CONFIG_PPP_DEFLATE=m
+# CONFIG_PPP_BSDCOMP is not set
+CONFIG_PPPOE=m
+CONFIG_PPPOATM=m
+# CONFIG_SLIP is not set
+CONFIG_NET_FC=y
+# CONFIG_SHAPER is not set
+CONFIG_NETCONSOLE=m
+CONFIG_NETDUMP=m
 
 #
 # ISDN subsystem
 #
-
 CONFIG_ISDN=m
-CONFIG_ISDN_I4L=m
-CONFIG_ISDN_DRV_AVMB1_B1ISA=m
-CONFIG_ISDN_DRV_AVMB1_B1PCI=m
-CONFIG_ISDN_DRV_AVMB1_T1ISA=m
-CONFIG_ISDN_DRV_AVMB1_B1PCMCIA=m
-CONFIG_ISDN_DRV_AVMB1_T1PCI=m
-CONFIG_ISDN_DRV_AVMB1_C4=m
 
+#
+# Old ISDN4Linux
+#
+CONFIG_ISDN_I4L=m
 CONFIG_ISDN_PPP=y
 CONFIG_ISDN_PPP_VJ=y
 CONFIG_ISDN_MPP=y
+CONFIG_IPPP_FILTER=y
 # CONFIG_ISDN_PPP_BSDCOMP is not set
+CONFIG_ISDN_AUDIO=y
 CONFIG_ISDN_TTY_FAX=y
-CONFIG_DE_AOC=y
 
-CONFIG_ISDN_AUDIO=y
+#
+# ISDN feature submodules
+#
 
-CONFIG_ISDN_DRV_HISAX=m
-CONFIG_ISDN_DRV_ICN=m
-CONFIG_ISDN_DRV_PCBIT=m
-CONFIG_ISDN_DRV_SC=m
-CONFIG_ISDN_DRV_ACT2000=m
-CONFIG_ISDN_DRV_TPAM=m
-CONFIG_ISDN_DRV_AVMB1_B1PCIV4=y
-CONFIG_ISDN_DRV_AVMB1_AVM_CS=m
+#
+# ISDN4Linux hardware drivers
+#
 
-CONFIG_ISDN_CAPI_CAPIDRV=m
+#
+# Passive cards
+#
+CONFIG_ISDN_DRV_HISAX=m
 
+#
+# D-channel protocol features
+#
 CONFIG_HISAX_EURO=y
+CONFIG_DE_AOC=y
+CONFIG_HISAX_NO_SENDCOMPLETE=y
+CONFIG_HISAX_NO_LLC=y
+CONFIG_HISAX_NO_KEYPAD=y
 CONFIG_HISAX_1TR6=y
 CONFIG_HISAX_NI1=y
 CONFIG_HISAX_MAX_CARDS=8
+
+#
+# HiSax supported cards
+#
 CONFIG_HISAX_16_0=y
 CONFIG_HISAX_16_3=y
 CONFIG_HISAX_TELESPCI=y
@@ -1182,21 +1298,31 @@ CONFIG_HISAX_W6692=y
 CONFIG_HISAX_HFC_SX=y
 CONFIG_HISAX_ENTERNOW_PCI=y
 # CONFIG_HISAX_DEBUG is not set
-CONFIG_HISAX_AVM_A1_CS=m
-CONFIG_HISAX_ST5481=m
-CONFIG_HISAX_HFCUSB=m
-CONFIG_HISAX_FRITZ_PCIPNP=m
-CONFIG_HISAX_NO_SENDCOMPLETE=y
-CONFIG_HISAX_NO_LLC=y
-CONFIG_HISAX_NO_KEYPAD=y
+
+#
+# HiSax PCMCIA card service modules
+#
 CONFIG_HISAX_SEDLBAUER_CS=m
 CONFIG_HISAX_ELSA_CS=m
+CONFIG_HISAX_AVM_A1_CS=m
 CONFIG_HISAX_TELES_CS=m
 
-CONFIG_ISDN_DRV_LOOP=m
-CONFIG_HYSDN=m
-CONFIG_HYSDN_CAPI=y
+#
+# HiSax sub driver modules
+#
+CONFIG_HISAX_ST5481=m
+CONFIG_HISAX_HFCUSB=m
+CONFIG_HISAX_FRITZ_PCIPNP=m
+CONFIG_HISAX_HDLC=y
 
+#
+# Active cards
+#
+CONFIG_ISDN_DRV_ICN=m
+CONFIG_ISDN_DRV_PCBIT=m
+CONFIG_ISDN_DRV_SC=m
+CONFIG_ISDN_DRV_ACT2000=m
+CONFIG_ISDN_DRV_TPAM=m
 
 #
 # CAPI subsystem
@@ -1207,6 +1333,7 @@ CONFIG_ISDN_CAPI_MIDDLEWARE=y
 CONFIG_ISDN_CAPI_CAPI20=m
 CONFIG_ISDN_CAPI_CAPIFS_BOOL=y
 CONFIG_ISDN_CAPI_CAPIFS=m
+CONFIG_ISDN_CAPI_CAPIDRV=m
 
 #
 # CAPI hardware drivers
@@ -1254,21 +1381,14 @@ CONFIG_INPUT_EVDEV=y
 # Input I/O drivers
 #
 # CONFIG_GAMEPORT is not set
-CONFIG_SOUND_GAMEPORT=m
-CONFIG_GAMEPORT_NS558=m
-CONFIG_GAMEPORT_L4=m
-CONFIG_GAMEPORT_EMU10K1=m
-CONFIG_GAMEPORT_VORTEX=m
-CONFIG_GAMEPORT_FM801=m
-CONFIG_GAMEPORT_CS461x=m
+CONFIG_SOUND_GAMEPORT=y
 CONFIG_SERIO=y
 CONFIG_SERIO_I8042=y
 CONFIG_SERIO_SERPORT=y
-# CONFIG_SERIO_RAW is not set
-
 # CONFIG_SERIO_CT82C710 is not set
 # CONFIG_SERIO_PARKBD is not set
 # CONFIG_SERIO_PCIPS2 is not set
+# CONFIG_SERIO_RAW is not set
 
 #
 # Input Device Drivers
@@ -1276,9 +1396,9 @@ CONFIG_SERIO_SERPORT=y
 CONFIG_INPUT_KEYBOARD=y
 CONFIG_KEYBOARD_ATKBD=y
 # CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_LKKBD is not set
 # CONFIG_KEYBOARD_XTKBD is not set
 # CONFIG_KEYBOARD_NEWTON is not set
-# CONFIG_KEYBOARD_LKKBD is not set
 CONFIG_INPUT_MOUSE=y
 CONFIG_MOUSE_PS2=y
 CONFIG_MOUSE_SERIAL=m
@@ -1288,20 +1408,7 @@ CONFIG_MOUSE_LOGIBM=m
 CONFIG_MOUSE_PC110PAD=m
 CONFIG_MOUSE_VSXXXAA=m
 CONFIG_INPUT_JOYSTICK=y
-# CONFIG_JOYSTICK_ANALOG is not set
-# CONFIG_JOYSTICK_A3D is not set
-# CONFIG_JOYSTICK_ADI is not set
-# CONFIG_JOYSTICK_COBRA is not set
-# CONFIG_JOYSTICK_GF2K is not set
-# CONFIG_JOYSTICK_GRIP is not set
-# CONFIG_JOYSTICK_GRIP_MP is not set
-# CONFIG_JOYSTICK_GUILLEMOT is not set
-# CONFIG_JOYSTICK_INTERACT is not set
-# CONFIG_JOYSTICK_SIDEWINDER is not set
-# CONFIG_JOYSTICK_TMDC is not set
 # CONFIG_JOYSTICK_IFORCE is not set
-# CONFIG_JOYSTICK_IFORCE_USB=y
-# CONFIG_JOYSTICK_IFORCE_232=y
 # CONFIG_JOYSTICK_WARRIOR is not set
 # CONFIG_JOYSTICK_MAGELLAN is not set
 # CONFIG_JOYSTICK_SPACEORB is not set
@@ -1311,12 +1418,11 @@ CONFIG_INPUT_JOYSTICK=y
 # CONFIG_JOYSTICK_DB9 is not set
 # CONFIG_JOYSTICK_GAMECON is not set
 # CONFIG_JOYSTICK_TURBOGRAFX is not set
-CONFIG_JOYSTICK_JOYDUMP=m
 CONFIG_INPUT_TOUCHSCREEN=y
 CONFIG_TOUCHSCREEN_GUNZE=m
 CONFIG_INPUT_MISC=y
 CONFIG_INPUT_PCSPKR=m
-CONFIG_INPUT_UINPUT=m 
+CONFIG_INPUT_UINPUT=m
 
 #
 # Character devices
@@ -1326,12 +1432,11 @@ CONFIG_VT_CONSOLE=y
 CONFIG_HW_CONSOLE=y
 CONFIG_SERIAL_NONSTANDARD=y
 # CONFIG_ROCKETPORT is not set
+# CONFIG_CYCLADES is not set
 CONFIG_SYNCLINK=m
 CONFIG_SYNCLINKMP=m
 CONFIG_N_HDLC=m
 CONFIG_STALDRV=y
-# CONFIG_FTAPE is not set
-CONFIG_IBM_ASM=m
 
 #
 # Serial drivers
@@ -1347,20 +1452,6 @@ CONFIG_SERIAL_8250_SHARE_IRQ=y
 CONFIG_SERIAL_8250_DETECT_IRQ=y
 CONFIG_SERIAL_8250_MULTIPORT=y
 CONFIG_SERIAL_8250_RSA=y
-# CONFIG_COMPUTONE is not set
-# CONFIG_CYCLADES is not set
-# CONFIG_DIGIEPCA is not set
-# CONFIG_DIGI is not set
-# CONFIG_ESPSERIAL is not set
-# CONFIG_MOXA_INTELLIO is not set
-# CONFIG_MOXA_SMARTIO is not set
-# CONFIG_ISI is not set
-# CONFIG_RISCOM8 is not set
-# CONFIG_SPECIALIX is not set
-# CONFIG_SX is not set
-# CONFIG_RIO is not set
-# CONFIG_STALLION is not set
-# CONFIG_ISTALLION is not set
 
 #
 # Non-8250 serial port support
@@ -1369,104 +1460,20 @@ CONFIG_SERIAL_CORE=y
 CONFIG_SERIAL_CORE_CONSOLE=y
 CONFIG_UNIX98_PTYS=y
 # CONFIG_LEGACY_PTYS is not set
+CONFIG_CRASH=m
 CONFIG_PRINTER=m
 CONFIG_LP_CONSOLE=y
 CONFIG_PPDEV=m
 # CONFIG_TIPAR is not set
 
 #
-# I2C support
-#
-CONFIG_I2C=m
-CONFIG_I2C_CHARDEV=m
-
-#
-# I2C Algorithms
-#
-# CONFIG_I2C_DEBUG_ALGO is not set
-CONFIG_I2C_ALGOBIT=m
-CONFIG_I2C_ALGOPCF=m
-
-#
-# I2C Hardware Bus support
-#
-CONFIG_I2C_ALI1535=m
-CONFIG_I2C_ALI15X3=m
-CONFIG_I2C_AMD756=m
-CONFIG_I2C_AMD8111=m
-CONFIG_I2C_I801=m
-CONFIG_I2C_I810=m
-CONFIG_I2C_ISA=m
-CONFIG_I2C_NFORCE2=m
-CONFIG_I2C_PIIX4=m
-CONFIG_I2C_PROSAVAGE=m
-CONFIG_I2C_SAVAGE4=m
-# CONFIG_SCx200_ACB is not set
-CONFIG_I2C_SIS5595=m
-CONFIG_I2C_SIS630=m
-CONFIG_I2C_SIS96X=m
-CONFIG_I2C_VIA=m
-CONFIG_I2C_VIAPRO=m
-CONFIG_I2C_VOODOO3=m
-# CONFIG_I2C_ELEKTOR is not set
-CONFIG_I2C_PARPORT=m
-CONFIG_I2C_PARPORT_LIGHT=m
-# CONFIG_I2C_DEBUG_CORE is not set
-# CONFIG_I2C_DEBUG_BUS is not set
-# CONFIG_I2C_DEBUG_CHIP is not set
-# CONFIG_I2C_PARPORT is not set
-CONFIG_I2C_ALI1563=m
-# CONFIG_I2C_PARPORT_LIGHT is not set
-CONFIG_I2C_ALGOPCA=m
-# CONFIG_I2C_PCA_ISA is not set
-
-
-#
-# I2C Hardware Sensors Chip support
-#
-CONFIG_I2C_SENSOR=m
-CONFIG_SENSORS_ADM1021=m
-CONFIG_SENSORS_EEPROM=m
-CONFIG_SENSORS_IT87=m
-CONFIG_SENSORS_LM75=m
-CONFIG_SENSORS_LM78=m
-CONFIG_SENSORS_LM80=m
-CONFIG_SENSORS_LM83=m
-CONFIG_SENSORS_LM85=m
-CONFIG_SENSORS_VIA686A=m
-CONFIG_SENSORS_W83781D=m
-CONFIG_SENSORS_ASB100=m
-CONFIG_SENSORS_LM90=m
-CONFIG_SENSORS_W83L785TS=m
-CONFIG_SENSORS_FSCHER=m
-CONFIG_SENSORS_GL518SM=m
-CONFIG_SENSORS_DS1621=m
-CONFIG_SENSORS_W83627HF=m
-CONFIG_SENSORS_PCF8574=m
-CONFIG_SENSORS_PCF8591=m
-CONFIG_SENSORS_RTC8564=m
-CONFIG_SENSORS_MAX1619=m
-CONFIG_SENSORS_ADM1025=m
-CONFIG_SENSORS_ADM1031=m
-CONFIG_SENSORS_LM77=m
-CONFIG_SENSORS_SMSC47M1=m
-
-# CONFIG_W1 is not set
-
-#
-# Mice
-#
-CONFIG_CRASH=m
-
-#
 # IPMI
 #
 CONFIG_IPMI_HANDLER=m
 # CONFIG_IPMI_PANIC_EVENT is not set
 CONFIG_IPMI_DEVICE_INTERFACE=m
-CONFIG_IPMI_WATCHDOG=m
 CONFIG_IPMI_SI=m
+CONFIG_IPMI_WATCHDOG=m
 CONFIG_IPMI_POWEROFF=m
 
 #
@@ -1474,33 +1481,47 @@ CONFIG_IPMI_POWEROFF=m
 #
 CONFIG_WATCHDOG=y
 # CONFIG_WATCHDOG_NOWAYOUT is not set
+
+#
+# Watchdog Device Drivers
+#
 CONFIG_SOFT_WATCHDOG=m
-CONFIG_WDT=m
-# CONFIG_WDT_501 is not set
-CONFIG_WDTPCI=m
-CONFIG_WDT_501_PCI=y
-CONFIG_PCWATCHDOG=m
 CONFIG_ACQUIRE_WDT=m
 CONFIG_ADVANTECH_WDT=m
+CONFIG_ALIM1535_WDT=m
+CONFIG_ALIM7101_WDT=m
+CONFIG_SC520_WDT=m
 CONFIG_EUROTECH_WDT=m
 CONFIG_IB700_WDT=m
+CONFIG_WAFER_WDT=m
 CONFIG_I8XX_TCO=m
-# CONFIG_MIXCOMWD is not set
+CONFIG_SC1200_WDT=m
 # CONFIG_SCx200_WDT is not set
 # CONFIG_60XX_WDT is not set
-CONFIG_W83877F_WDT=m
+CONFIG_CPU5_WDT=m
 CONFIG_W83627HF_WDT=m
+CONFIG_W83877F_WDT=m
 CONFIG_MACHZ_WDT=m
-CONFIG_SC520_WDT=m
-CONFIG_ALIM7101_WDT=m
-CONFIG_ALIM1535_WDT=m
-CONFIG_SC1200_WDT=m
-CONFIG_WAFER_WDT=m
-CONFIG_CPU5_WDT=m
-CONFIG_PCIPCWATCHDOG=m
-CONFIG_USBPCWATCHDOG=m
 
+#
+# ISA-based Watchdog Cards
+#
+CONFIG_PCWATCHDOG=m
+# CONFIG_MIXCOMWD is not set
+CONFIG_WDT=m
+# CONFIG_WDT_501 is not set
+
+#
+# PCI-based Watchdog Cards
+#
+CONFIG_PCIPCWATCHDOG=m
+CONFIG_WDTPCI=m
+CONFIG_WDT_501_PCI=y
 
+#
+# USB-based Watchdog Cards
+#
+CONFIG_USBPCWATCHDOG=m
 CONFIG_HW_RANDOM=m
 CONFIG_NVRAM=m
 CONFIG_RTC=y
@@ -1526,70 +1547,142 @@ CONFIG_AGP_VIA=y
 CONFIG_AGP_EFFICEON=y
 CONFIG_DRM=y
 # CONFIG_DRM_TDFX is not set
-# CONFIG_DRM_GAMMA is not set
 CONFIG_DRM_R128=m
 CONFIG_DRM_RADEON=m
 CONFIG_DRM_I810=m
 CONFIG_DRM_I830=m
+CONFIG_DRM_I915=m
 CONFIG_DRM_MGA=m
 # CONFIG_DRM_SIS is not set
-CONFIG_DRM_I915=m
-
-
 
 #
 # PCMCIA character devices
 #
 CONFIG_SYNCLINK_CS=m
-
-
-
 # CONFIG_MWAVE is not set
 CONFIG_RAW_DRIVER=y
+# CONFIG_HPET is not set
 CONFIG_MAX_RAW_DEVS=8192
 CONFIG_HANGCHECK_TIMER=m
 
 #
-# Multimedia devices
+# I2C support
 #
-# CONFIG_VIDEO_DEV is not set
+CONFIG_I2C=m
+CONFIG_I2C_CHARDEV=m
 
 #
-# Video For Linux
+# I2C Algorithms
 #
+CONFIG_I2C_ALGOBIT=m
+CONFIG_I2C_ALGOPCF=m
+CONFIG_I2C_ALGOPCA=m
 
 #
-# Video Adapters
+# I2C Hardware Bus support
 #
-# CONFIG_VIDEO_BT848 is not set
-# CONFIG_VIDEO_PMS is not set
-# CONFIG_VIDEO_BWQCAM is not set
+CONFIG_I2C_ALI1535=m
+CONFIG_I2C_ALI1563=m
+CONFIG_I2C_ALI15X3=m
+CONFIG_I2C_AMD756=m
+CONFIG_I2C_AMD8111=m
+CONFIG_I2C_I801=m
+CONFIG_I2C_I810=m
+CONFIG_I2C_ISA=m
+CONFIG_I2C_NFORCE2=m
+# CONFIG_I2C_PARPORT is not set
+# CONFIG_I2C_PARPORT_LIGHT is not set
+CONFIG_I2C_PIIX4=m
+CONFIG_I2C_PROSAVAGE=m
+CONFIG_I2C_SAVAGE4=m
+# CONFIG_SCx200_ACB is not set
+CONFIG_I2C_SIS5595=m
+CONFIG_I2C_SIS630=m
+CONFIG_I2C_SIS96X=m
+CONFIG_I2C_VIA=m
+CONFIG_I2C_VIAPRO=m
+CONFIG_I2C_VOODOO3=m
+# CONFIG_I2C_PCA_ISA is not set
+
+#
+# Hardware Sensors Chip support
+#
+CONFIG_I2C_SENSOR=m
+CONFIG_SENSORS_ADM1021=m
+CONFIG_SENSORS_ADM1025=m
+CONFIG_SENSORS_ADM1031=m
+CONFIG_SENSORS_ASB100=m
+CONFIG_SENSORS_DS1621=m
+CONFIG_SENSORS_FSCHER=m
+CONFIG_SENSORS_GL518SM=m
+CONFIG_SENSORS_IT87=m
+CONFIG_SENSORS_LM75=m
+CONFIG_SENSORS_LM77=m
+CONFIG_SENSORS_LM78=m
+CONFIG_SENSORS_LM80=m
+CONFIG_SENSORS_LM83=m
+CONFIG_SENSORS_LM85=m
+CONFIG_SENSORS_LM90=m
+CONFIG_SENSORS_MAX1619=m
+CONFIG_SENSORS_SMSC47M1=m
+CONFIG_SENSORS_VIA686A=m
+CONFIG_SENSORS_W83781D=m
+CONFIG_SENSORS_W83L785TS=m
+CONFIG_SENSORS_W83627HF=m
+
+#
+# Other I2C Chip support
+#
+CONFIG_SENSORS_EEPROM=m
+CONFIG_SENSORS_PCF8574=m
+CONFIG_SENSORS_PCF8591=m
+CONFIG_SENSORS_RTC8564=m
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_I2C_DEBUG_CHIP is not set
+
+#
+# Dallas's 1-wire bus
+#
+# CONFIG_W1 is not set
+
+#
+# Misc devices
+#
+CONFIG_IBM_ASM=m
+
+#
+# Multimedia devices
+#
+CONFIG_VIDEO_DEV=m
+
+#
+# Video For Linux
+#
+
+#
+# Video Adapters
+#
+# CONFIG_VIDEO_BT848 is not set
+# CONFIG_VIDEO_PMS is not set
+# CONFIG_VIDEO_BWQCAM is not set
 # CONFIG_VIDEO_CQCAM is not set
 # CONFIG_VIDEO_W9966 is not set
 # CONFIG_VIDEO_CPIA is not set
-# CONFIG_VIDEO_CPIA_PP is not set
-# CONFIG_VIDEO_CPIA_USB is not set
+# CONFIG_VIDEO_SAA5246A is not set
 # CONFIG_VIDEO_SAA5249 is not set
 # CONFIG_TUNER_3036 is not set
 # CONFIG_VIDEO_STRADIS is not set
 # CONFIG_VIDEO_ZORAN is not set
-# CONFIG_VIDEO_ZORAN is not set
-# CONFIG_VIDEO_ZORAN_BUZ is not set
-# CONFIG_VIDEO_ZORAN_DC10 is not set
-# CONFIG_VIDEO_ZORAN_DC30 is not set
-# CONFIG_VIDEO_ZORAN_LML33 is not set
-# CONFIG_VIDEO_ZORAN_LML33R10 is not set
-# CONFIG_VIDEO_MEYE is not set
 # CONFIG_VIDEO_SAA7134 is not set
 # CONFIG_VIDEO_MXB is not set
 # CONFIG_VIDEO_DPC is not set
 # CONFIG_VIDEO_HEXIUM_ORION is not set
 # CONFIG_VIDEO_HEXIUM_GEMINI is not set
 # CONFIG_VIDEO_CX88 is not set
-# CONFIG_VIDEO_SAA5246A is not set
 CONFIG_VIDEO_OVCAMCHIP=m
 
-
 #
 # Radio Adapters
 #
@@ -1601,119 +1694,49 @@ CONFIG_VIDEO_OVCAMCHIP=m
 # CONFIG_RADIO_GEMTEK_PCI is not set
 # CONFIG_RADIO_MAXIRADIO is not set
 # CONFIG_RADIO_MAESTRO is not set
-# CONFIG_RADIO_MIROPCM20 is not set
-# CONFIG_RADIO_MIROPCM20_RDS is not set
 # CONFIG_RADIO_SF16FMI is not set
 # CONFIG_RADIO_SF16FMR2 is not set
 # CONFIG_RADIO_TERRATEC is not set
 # CONFIG_RADIO_TRUST is not set
 # CONFIG_RADIO_TYPHOON is not set
-# CONFIG_RADIO_TYPHOON_PROC_FS=y
 # CONFIG_RADIO_ZOLTRIX is not set
 
-
 #
 # Digital Video Broadcasting Devices
 #
 # CONFIG_DVB is not set
-CONFIG_DVB_CORE=m
-
-#
-# Supported Frontend Modules
-#
-CONFIG_DVB_STV0299=m
-# CONFIG_DVB_SP887X is not set
-# CONFIG_DVB_ALPS_TDLB7 is not set
-CONFIG_DVB_ALPS_TDMB7=m
-CONFIG_DVB_ATMEL_AT76C651=m
-CONFIG_DVB_CX24110=m
-CONFIG_DVB_GRUNDIG_29504_491=m
-CONFIG_DVB_GRUNDIG_29504_401=m
-CONFIG_DVB_MT312=m
-CONFIG_DVB_VES1820=m
-CONFIG_DVB_VES1X93=m
-CONFIG_DVB_TWINHAN_DST=m
-CONFIG_DVB_TTUSB_DEC=m
-CONFIG_DVB_BT8XX=m
-# CONFIG_DVB_TDA1004X is not set
-CONFIG_DVB_NXT6000=m
-
-#
-# Supported SAA7146 based PCI Adapters
-#
-CONFIG_DVB_AV7110=m
-CONFIG_DVB_AV7110_OSD=y
-# CONFIG_DVB_AV7110_FIRMWARE is not set
-CONFIG_DVB_BUDGET=m
-CONFIG_DVB_BUDGET_CI=m
-CONFIG_DVB_BUDGET_AV=m
-CONFIG_DVB_BUDGET_PATCH=m
-
-#
-# Supported USB Adapters
-#
-CONFIG_DVB_TTUSB_BUDGET=m
-
-#
-# Supported FlexCopII (B2C2) Adapters
-#
-CONFIG_DVB_B2C2_SKYSTAR=m
-CONFIG_VIDEO_SAA7146=m
-CONFIG_VIDEO_SAA7146_VV=m
-CONFIG_VIDEO_VIDEOBUF=m
-CONFIG_VIDEO_TUNER=m
-CONFIG_VIDEO_BUF=m
-CONFIG_VIDEO_BTCX=m
 
 #
 # Graphics support
 #
 CONFIG_FB=y
+CONFIG_FB_MODE_HELPERS=y
+CONFIG_FB_CIRRUS=m
+# CONFIG_FB_PM2 is not set
 # CONFIG_FB_CYBER2000 is not set
+# CONFIG_FB_ASILIANT is not set
 # CONFIG_FB_IMSTT is not set
 CONFIG_FB_VGA16=m
 CONFIG_FB_VESA=y
 CONFIG_VIDEO_SELECT=y
 # CONFIG_FB_HGA is not set
 CONFIG_FB_RIVA=m
-# CONFIG_FB_RIVA_DEBUG is not set
 # CONFIG_FB_RIVA_I2C is not set
+# CONFIG_FB_RIVA_DEBUG is not set
 CONFIG_FB_I810=m
 CONFIG_FB_I810_GTF=y
 # CONFIG_FB_MATROX is not set
-CONFIG_FB_MATROX_MILLENIUM=y
-CONFIG_FB_MATROX_MYSTIQUE=y
-CONFIG_FB_MATROX_G450=y
-CONFIG_FB_MATROX_G100=y
-CONFIG_FB_MATROX_I2C=m
-CONFIG_FB_MATROX_MAVEN=m
-CONFIG_FB_MATROX_MULTIHEAD=y
-# CONFIG_FB_RADEON is not set
-# CONFIG_FB_RADEON_DEBUG is not set
 # CONFIG_FB_RADEON_OLD is not set
-CONFIG_FB_RADEON_I2C=y
+# CONFIG_FB_RADEON is not set
 # CONFIG_FB_ATY128 is not set
 # CONFIG_FB_ATY is not set
-CONFIG_FB_ATY_CT=y
-CONFIG_FB_ATY_GX=y
-# CONFIG_FB_ATY_XL_INIT is not set
 # CONFIG_FB_SIS is not set
-CONFIG_FB_SIS_300=y
-CONFIG_FB_SIS_315=y
-# CONFIG_FB_NEOMAGIC is not set
 # CONFIG_FB_NEOMAGIC is not set
+CONFIG_FB_KYRO=m
 # CONFIG_FB_3DFX is not set
 # CONFIG_FB_VOODOO1 is not set
 # CONFIG_FB_TRIDENT is not set
 # CONFIG_FB_VIRTUAL is not set
-CONFIG_FB_KYRO=m
-# CONFIG_FB_PM2 is not set
-# CONFIG_FB_PM2_FIFO_DISCONNECT is not set
-# CONFIG_FB_ASILIANT is not set
-# CONFIG_FB_HGA_ACCEL is not set
-# CONFIG_FB_3DFX_ACCEL is not set
-# CONFIG_FB_TRIDENT_ACCEL is not set
-CONFIG_FB_CIRRUS=m
 
 #
 # Console display driver support
@@ -1723,7 +1746,8 @@ CONFIG_MDA_CONSOLE=m
 CONFIG_DUMMY_CONSOLE=y
 CONFIG_FRAMEBUFFER_CONSOLE=y
 # CONFIG_FONTS is not set
-
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
 
 #
 # Logo configuration
@@ -1742,6 +1766,10 @@ CONFIG_SOUND=m
 # Advanced Linux Sound Architecture
 #
 CONFIG_SND=m
+CONFIG_SND_TIMER=m
+CONFIG_SND_PCM=m
+CONFIG_SND_HWDEP=m
+CONFIG_SND_RAWMIDI=m
 CONFIG_SND_SEQUENCER=m
 CONFIG_SND_SEQ_DUMMY=m
 CONFIG_SND_OSSEMUL=y
@@ -1751,11 +1779,13 @@ CONFIG_SND_SEQUENCER_OSS=y
 CONFIG_SND_RTCTIMER=m
 # CONFIG_SND_VERBOSE_PRINTK is not set
 # CONFIG_SND_DEBUG is not set
-CONFIG_SND_BIT32_EMUL=y
 
 #
 # Generic devices
 #
+CONFIG_SND_MPU401_UART=m
+CONFIG_SND_OPL3_LIB=m
+CONFIG_SND_VX_LIB=m
 CONFIG_SND_DUMMY=m
 CONFIG_SND_VIRMIDI=m
 CONFIG_SND_MTPAV=m
@@ -1765,12 +1795,10 @@ CONFIG_SND_MPU401=m
 #
 # ISA devices
 #
-# CONFIG_SND_AD1816A is not set
 # CONFIG_SND_AD1848 is not set
 # CONFIG_SND_CS4231 is not set
 # CONFIG_SND_CS4232 is not set
 # CONFIG_SND_CS4236 is not set
-# CONFIG_SND_ES968 is not set
 # CONFIG_SND_ES1688 is not set
 # CONFIG_SND_ES18XX is not set
 # CONFIG_SND_GUSCLASSIC is not set
@@ -1784,12 +1812,8 @@ CONFIG_SND_MPU401=m
 # CONFIG_SND_SB8 is not set
 # CONFIG_SND_SB16 is not set
 # CONFIG_SND_SBAWE is not set
-# CONFIG_SND_SB16_CSP=y
 # CONFIG_SND_WAVEFRONT is not set
-# CONFIG_SND_ALS100 is not set
-# CONFIG_SND_AZT2320 is not set
 # CONFIG_SND_CMI8330 is not set
-# CONFIG_SND_DT019X is not set
 # CONFIG_SND_OPL3SA2 is not set
 # CONFIG_SND_SGALAXY is not set
 # CONFIG_SND_SSCAPE is not set
@@ -1797,13 +1821,21 @@ CONFIG_SND_MPU401=m
 #
 # PCI devices
 #
+CONFIG_SND_AC97_CODEC=m
 CONFIG_SND_ALI5451=m
+CONFIG_SND_ATIIXP=m
+CONFIG_SND_ATIIXP_MODEM=m
+CONFIG_SND_AU8810=m
+CONFIG_SND_AU8820=m
+CONFIG_SND_AU8830=m
 CONFIG_SND_AZT3328=m
+CONFIG_SND_BT87X=m
 CONFIG_SND_CS46XX=m
 CONFIG_SND_CS46XX_NEW_DSP=y
 CONFIG_SND_CS4281=m
 CONFIG_SND_EMU10K1=m
 CONFIG_SND_KORG1212=m
+CONFIG_SND_MIXART=m
 CONFIG_SND_NM256=m
 CONFIG_SND_RME32=m
 CONFIG_SND_RME96=m
@@ -1819,23 +1851,14 @@ CONFIG_SND_ES1938=m
 CONFIG_SND_ES1968=m
 CONFIG_SND_MAESTRO3=m
 CONFIG_SND_FM801=m
+CONFIG_SND_FM801_TEA575X=m
 CONFIG_SND_ICE1712=m
 CONFIG_SND_ICE1724=m
 CONFIG_SND_INTEL8X0=m
+CONFIG_SND_INTEL8X0M=m
 CONFIG_SND_SONICVIBES=m
 CONFIG_SND_VIA82XX=m
 CONFIG_SND_VX222=m
-CONFIG_SND_BT87X=m
-CONFIG_SND_ATIIXP=m
-CONFIG_SND_ATIIXP_MODEM=m
-CONFIG_SND_AU8810=m
-CONFIG_SND_AU8820=m
-CONFIG_SND_AU8830=m
-CONFIG_SND_MIXART=m
-CONFIG_SND_FM801_TEA575X=m
-CONFIG_SND_INTEL8X0M=m
-CONFIG_SND_PDAUDIOCF=m
-
 
 #
 # ALSA USB devices
@@ -1848,13 +1871,12 @@ CONFIG_SND_USB_USX2Y=m
 #
 # CONFIG_SND_VXPOCKET is not set
 # CONFIG_SND_VXP440 is not set
+CONFIG_SND_PDAUDIOCF=m
 
 #
 # Open Sound System
 #
 # CONFIG_SOUND_PRIME is not set
-# CONFIG_SOUND_BT878 is not set
-# CONFIG_SOUND_CMPCI is not set
 
 #
 # USB support
@@ -1869,6 +1891,7 @@ CONFIG_USB_DEVICEFS=y
 # CONFIG_USB_BANDWIDTH is not set
 # CONFIG_USB_DYNAMIC_MINORS is not set
 CONFIG_USB_SUSPEND=y
+# CONFIG_USB_OTG is not set
 
 #
 # USB Host Controller Drivers
@@ -1890,9 +1913,9 @@ CONFIG_USB_UHCI_HCD=m
 CONFIG_USB_MIDI=m
 CONFIG_USB_ACM=m
 CONFIG_USB_PRINTER=m
-# CONFIG_BLK_DEV_UB is not set
 CONFIG_USB_STORAGE=m
 # CONFIG_USB_STORAGE_DEBUG is not set
+CONFIG_USB_STORAGE_RW_DETECT=y
 CONFIG_USB_STORAGE_DATAFAB=y
 CONFIG_USB_STORAGE_FREECOM=y
 CONFIG_USB_STORAGE_ISD200=y
@@ -1901,7 +1924,6 @@ CONFIG_USB_STORAGE_HP8200e=y
 CONFIG_USB_STORAGE_SDDR09=y
 CONFIG_USB_STORAGE_SDDR55=y
 CONFIG_USB_STORAGE_JUMPSHOT=y
-CONFIG_USB_STORAGE_RW_DETECT=y
 
 #
 # USB Human Interface Devices (HID)
@@ -1913,17 +1935,14 @@ CONFIG_HID_PID=y
 CONFIG_LOGITECH_FF=y
 CONFIG_THRUSTMASTER_FF=y
 CONFIG_USB_HIDDEV=y
-
-#
-# USB HID Boot Protocol drivers
-#
-# CONFIG_USB_KBD is not set
-# CONFIG_USB_MOUSE is not set
 CONFIG_USB_AIPTEK=m
 CONFIG_USB_WACOM=m
 CONFIG_USB_KBTAB=m
 CONFIG_USB_POWERMATE=m
+CONFIG_USB_MTOUCH=m
+CONFIG_USB_EGALAX=m
 CONFIG_USB_XPAD=m
+CONFIG_USB_ATI_REMOTE=m
 
 #
 # USB Imaging devices
@@ -1942,8 +1961,10 @@ CONFIG_USB_IBMCAM=m
 CONFIG_USB_KONICAWC=m
 CONFIG_USB_OV511=m
 CONFIG_USB_SE401=m
-CONFIG_USB_STV680=m
 CONFIG_USB_SN9C102=m
+CONFIG_USB_STV680=m
+CONFIG_USB_W9968CF=m
+CONFIG_USB_PWC=m
 
 #
 # USB Network adaptors
@@ -1953,11 +1974,11 @@ CONFIG_USB_KAWETH=m
 CONFIG_USB_PEGASUS=m
 CONFIG_USB_RTL8150=m
 CONFIG_USB_USBNET=m
-CONFIG_USB_SPEEDTOUCH=m
 
 #
 # USB Host-to-Host Cables
 #
+CONFIG_USB_ALI_M5632=y
 CONFIG_USB_AN2720=y
 CONFIG_USB_BELKIN=y
 CONFIG_USB_GENESYS=y
@@ -2019,40 +2040,33 @@ CONFIG_USB_SERIAL_SAFE_PADDED=y
 CONFIG_USB_SERIAL_CYBERJACK=m
 CONFIG_USB_SERIAL_XIRCOM=m
 CONFIG_USB_SERIAL_OMNINET=m
-CONFIG_USB_SERIAL_WHITEHEAT=m
 CONFIG_USB_EZUSB=y
-CONFIG_USB_EMI62=m
-CONFIG_USB_LED=m
-CONFIG_USB_G_SERIAL=m
-
 
 #
 # USB Miscellaneous drivers
 #
+CONFIG_USB_EMI62=m
 # CONFIG_USB_EMI26 is not set
 # CONFIG_USB_TIGL is not set
 CONFIG_USB_AUERSWALD=m
 CONFIG_USB_RIO500=m
-CONFIG_USB_LCD=m
-CONFIG_USB_TEST=m
-# CONFIG_USB_GADGET is not set
-# CONFIG_USB_GADGET_PXA2XX is not set
-# CONFIG_USB_GADGET_GOKU is not set
-# CONFIG_USB_GADGET_SA1100 is not set
-CONFIG_USB_ZERO=m
-CONFIG_USB_ETH=m
-CONFIG_USB_GADGETFS=m
-CONFIG_USB_W9968CF=m
-CONFIG_USB_PWC=m
 CONFIG_USB_LEGOTOWER=m
-CONFIG_USB_FILE_STORAGE=m
-# CONFIG_USB_FILE_STORAGE_TEST is not set
-CONFIG_USB_MTOUCH=m
-CONFIG_USB_ATI_REMOTE=m
-CONFIG_USB_ALI_M5632=y
+CONFIG_USB_LCD=m
+CONFIG_USB_LED=m
 # CONFIG_USB_CYTHERM is not set
-CONFIG_USB_EGALAX=m
 CONFIG_USB_PHIDGETSERVO=m
+CONFIG_USB_TEST=m
+
+#
+# USB ATM/DSL drivers
+#
+CONFIG_USB_ATM=m
+CONFIG_USB_SPEEDTOUCH=m
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET is not set
 
 #
 # File systems
@@ -2069,20 +2083,9 @@ CONFIG_JBD=m
 # CONFIG_JBD_DEBUG is not set
 CONFIG_FS_MBCACHE=y
 # CONFIG_REISERFS_FS is not set
-# CONFIG_REISERFS_CHECK is not set
-CONFIG_REISERFS_PROC_INFO=y
-CONFIG_REISERFS_FS_XATTR=y
-CONFIG_REISERFS_FS_POSIX_ACL=y
-CONFIG_REISERFS_FS_SECURITY=y
 # CONFIG_JFS_FS is not set
-# CONFIG_JFS_DEBUG is not set
-# CONFIG_JFS_STATISTICS is not set
-CONFIG_JFS_POSIX_ACL=y
+CONFIG_FS_POSIX_ACL=y
 # CONFIG_XFS_FS is not set
-# CONFIG_XFS_RT is not set
-CONFIG_XFS_QUOTA=y
-CONFIG_XFS_POSIX_ACL=y
-CONFIG_XFS_SECURITY=y
 # CONFIG_MINIX_FS is not set
 # CONFIG_ROMFS_FS is not set
 CONFIG_QUOTA=y
@@ -2100,6 +2103,7 @@ CONFIG_JOLIET=y
 CONFIG_ZISOFS=y
 CONFIG_ZISOFS_FS=y
 CONFIG_UDF_FS=m
+CONFIG_UDF_NLS=y
 
 #
 # DOS/FAT/NT Filesystems
@@ -2116,6 +2120,7 @@ CONFIG_FAT_DEFAULT_IOCHARSET="ascii"
 #
 CONFIG_PROC_FS=y
 CONFIG_PROC_KCORE=y
+CONFIG_SYSFS=y
 # CONFIG_DEVFS_FS is not set
 CONFIG_DEVPTS_FS_XATTR=y
 CONFIG_DEVPTS_FS_SECURITY=y
@@ -2129,27 +2134,27 @@ CONFIG_RAMFS=y
 #
 # Miscellaneous filesystems
 #
-# CONFIG_ADFS_FS is not seta
+# CONFIG_ADFS_FS is not set
 # CONFIG_AFFS_FS is not set
-# uses sleepon and needs a major update
 CONFIG_HFS_FS=m
 CONFIG_HFSPLUS_FS=m
 # CONFIG_BEFS_FS is not set
-# CONFIG_BEFS_DEBUG is not set
 # CONFIG_BFS_FS is not set
 # CONFIG_EFS_FS is not set
 # CONFIG_JFFS_FS is not set
 CONFIG_JFFS2_FS=m
 CONFIG_JFFS2_FS_DEBUG=0
 CONFIG_JFFS2_FS_NAND=y
+# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set
+CONFIG_JFFS2_ZLIB=y
+CONFIG_JFFS2_RTIME=y
+# CONFIG_JFFS2_RUBIN is not set
 CONFIG_CRAMFS=m
 CONFIG_VXFS_FS=m
 # CONFIG_HPFS_FS is not set
 # CONFIG_QNX4FS_FS is not set
-# CONFIG_QNX4FS_RW is not set
 # CONFIG_SYSV_FS is not set
 # CONFIG_UFS_FS is not set
-# CONFIG_UFS_FS_WRITE is not set
 
 #
 # Network File Systems
@@ -2176,17 +2181,8 @@ CONFIG_CIFS=m
 CONFIG_CIFS_XATTR=y
 CONFIG_CIFS_POSIX=y
 # CONFIG_NCP_FS is not set
-CONFIG_NCPFS_PACKET_SIGNING=y
-CONFIG_NCPFS_IOCTL_LOCKING=y
-CONFIG_NCPFS_STRONG=y
-CONFIG_NCPFS_NFS_NS=y
-CONFIG_NCPFS_OS2_NS=y
-CONFIG_NCPFS_SMALLDOS=y
-CONFIG_NCPFS_NLS=y
-CONFIG_NCPFS_EXTRAS=y
 # CONFIG_CODA_FS is not set
 # CONFIG_AFS_FS is not set
-# CONFIG_RXRPC is not set
 
 #
 # Partition Types
@@ -2207,11 +2203,11 @@ CONFIG_SGI_PARTITION=y
 # CONFIG_ULTRIX_PARTITION is not set
 CONFIG_SUN_PARTITION=y
 CONFIG_EFI_PARTITION=y
-CONFIG_NLS=y
 
 #
 # Native Language Support
 #
+CONFIG_NLS=y
 CONFIG_NLS_DEFAULT="utf8"
 CONFIG_NLS_CODEPAGE_437=y
 CONFIG_NLS_CODEPAGE_737=m
@@ -2236,6 +2232,7 @@ CONFIG_NLS_CODEPAGE_874=m
 CONFIG_NLS_ISO8859_8=m
 CONFIG_NLS_CODEPAGE_1250=m
 CONFIG_NLS_CODEPAGE_1251=m
+CONFIG_NLS_ASCII=y
 CONFIG_NLS_ISO8859_1=m
 CONFIG_NLS_ISO8859_2=m
 CONFIG_NLS_ISO8859_3=m
@@ -2250,7 +2247,6 @@ CONFIG_NLS_ISO8859_15=m
 CONFIG_NLS_KOI8_R=m
 CONFIG_NLS_KOI8_U=m
 CONFIG_NLS_UTF8=m
-CONFIG_NLS_ASCII=y
 
 #
 # Profiling support
@@ -2259,27 +2255,24 @@ CONFIG_PROFILING=y
 CONFIG_OPROFILE=m
 
 #
-# Tux
-#
-CONFIG_TUX=m
-CONFIG_TUX_EXTCGI=y
-# CONFIG_TUX_EXTENDED_LOG is not set
-# CONFIG_TUX_DEBUG is not set
-
-
-#
 # Kernel hacking
 #
 CONFIG_DEBUG_KERNEL=y
-# CONFIG_DEBUG_SLAB is not set
 CONFIG_MAGIC_SYSRQ=y
+# CONFIG_DEBUG_SLAB is not set
 CONFIG_DEBUG_SPINLOCK=y
+CONFIG_DEBUG_SPINLOCK_SLEEP=y
 CONFIG_DEBUG_HIGHMEM=y
 # CONFIG_DEBUG_INFO is not set
-CONFIG_DEBUG_SPINLOCK_SLEEP=y
 # CONFIG_FRAME_POINTER is not set
-CONFIG_CC_OPTIMIZE_FOR_SIZE=y
-# CONFIG_DEBUG_DRIVER is not set
+CONFIG_EARLY_PRINTK=y
+CONFIG_DEBUG_STACKOVERFLOW=y
+# CONFIG_KPROBES is not set
+CONFIG_DEBUG_STACK_USAGE=y
+# CONFIG_DEBUG_PAGEALLOC is not set
+# CONFIG_SCHEDSTATS is not set
+CONFIG_X86_FIND_SMP_CONFIG=y
+CONFIG_X86_MPPARSE=y
 
 #
 # Security options
@@ -2290,13 +2283,11 @@ CONFIG_SECURITY_CAPABILITIES=y
 # CONFIG_SECURITY_ROOTPLUG is not set
 CONFIG_SECURITY_SELINUX=y
 CONFIG_SECURITY_SELINUX_BOOTPARAM=y
+CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE=1
 CONFIG_SECURITY_SELINUX_DISABLE=y
 CONFIG_SECURITY_SELINUX_DEVELOP=y
 CONFIG_SECURITY_SELINUX_AVC_STATS=y
 # CONFIG_SECURITY_SELINUX_MLS is not set
-CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE=1
-CONFIG_AUDIT=y
-CONFIG_AUDITSYSCALL=y
 
 #
 # Cryptographic options
@@ -2309,432 +2300,35 @@ CONFIG_CRYPTO_MD5=m
 CONFIG_CRYPTO_SHA1=y
 CONFIG_CRYPTO_SHA256=m
 CONFIG_CRYPTO_SHA512=m
+CONFIG_CRYPTO_WP512=m
 CONFIG_CRYPTO_DES=m
 CONFIG_CRYPTO_BLOWFISH=m
 CONFIG_CRYPTO_TWOFISH=m
 CONFIG_CRYPTO_SERPENT=m
-CONFIG_CRYPTO_AES=m
+CONFIG_CRYPTO_AES_586=m
 CONFIG_CRYPTO_CAST5=m
 CONFIG_CRYPTO_CAST6=m
-CONFIG_CRYPTO_DEFLATE=m
+CONFIG_CRYPTO_TEA=m
 CONFIG_CRYPTO_ARC4=m
+CONFIG_CRYPTO_KHAZAD=m
+CONFIG_CRYPTO_DEFLATE=m
 CONFIG_CRYPTO_MICHAEL_MIC=m
-# CONFIG_CRYPTO_TEST is not set
-CONFIG_LIBCRC32C=m
 CONFIG_CRYPTO_CRC32C=m
+# CONFIG_CRYPTO_TEST is not set
+CONFIG_CRYPTO_SIGNATURE=y
 CONFIG_CRYPTO_SIGNATURE_DSA=y
 CONFIG_CRYPTO_MPILIB=y
-CONFIG_CRYPTO_TEA=m
-CONFIG_CRYPTO_KHAZAD=m
-CONFIG_CRYPTO_WP512=m
 
 #
 # Library routines
 #
-CONFIG_CRC32=m
 CONFIG_CRC_CCITT=m
-
+CONFIG_CRC32=y
+CONFIG_LIBCRC32C=m
 CONFIG_ZLIB_INFLATE=y
 CONFIG_ZLIB_DEFLATE=m
+CONFIG_X86_SMP=y
+CONFIG_X86_HT=y
+CONFIG_X86_BIOS_REBOOT=y
+CONFIG_X86_TRAMPOLINE=y
 CONFIG_PC=y
-# CONFIG_SCSI_BUSLOGIC is not set
-# CONFIG_SCSI_INIA100 is not set
-# CONFIG_ATALK is not set
-# CONFIG_DEV_APPLETALK is not set
-# CONFIG_LTPC is not set
-# CONFIG_COPS is not set
-# CONFIG_IPX is not set
-# CONFIG_IPDDP is not set
-# CONFIG_IRDA is not set
-# CONFIG_NCP_FS is not set
-# CONFIG_ISAPNP is not set
-# CONFIG_PCMCIA_AHA152X is not set
-# CONFIG_PCMCIA_NINJA_SCSI is not set
-# CONFIG_PCMCIA_QLOGIC is not set
-# CONFIG_IEEE1394 is not set
-# CONFIG_EL1 is not set
-# CONFIG_EL2 is not set
-# CONFIG_ELPLUS is not set
-# CONFIG_WD80x3 is not set
-# CONFIG_IRDA is not set
-# CONFIG_GAMEPORT is not set
-# CONFIG_DVB is not set
-# CONFIG_SND_AD1816A is not set
-# CONFIG_SND_AD1848 is not set
-# CONFIG_SND_CS4231 is not set
-# CONFIG_SND_CS4232 is not set
-# CONFIG_SND_CS4236 is not set
-# CONFIG_SND_ES968 is not set
-# CONFIG_SND_ES1688 is not set
-# CONFIG_SND_ES18XX is not set
-# CONFIG_SND_GUSCLASSIC is not set
-# CONFIG_SND_GUSEXTREME is not set
-# CONFIG_SND_GUSMAX is not set
-# CONFIG_SND_INTERWAVE is not set
-# CONFIG_SND_INTERWAVE_STB is not set
-# CONFIG_SND_OPTI92X_AD1848 is not set
-# CONFIG_SND_OPTI92X_CS4231 is not set
-# CONFIG_SND_OPTI93X is not set
-# CONFIG_SND_SB8 is not set
-# CONFIG_SND_SB16 is not set
-# CONFIG_SND_SBAWE is not set
-# CONFIG_SND_SB16_CSP=y
-# CONFIG_SND_WAVEFRONT is not set
-# CONFIG_SND_ALS100 is not set
-# CONFIG_SND_AZT2320 is not set
-# CONFIG_SND_CMI8330 is not set
-# CONFIG_SND_DT019X is not set
-# CONFIG_SND_OPL3SA2 is not set
-# CONFIG_SND_SGALAXY is not set
-# CONFIG_SND_SSCAPE is not set
-# CONFIG_REISERFS_FS is not set
-# CONFIG_XFS_FS is not set
-# CONFIG_AFFS_FS is not set
-# CONFIG_BEFS_FS is not set
-# CONFIG_EFS_FS is not set
-# CONFIG_BFS_FS is not set
-# CONFIG_QNX4FS_FS is not set
-# CONFIG_SYSV_FS is not set
-# CONFIG_UFS_FS is not set
-# CONFIG_WAN_ROUTER is not set
-# CONFIG_ROMFS_FS is not set
-# CONFIG_MINIX_FS is not set
-# CONFIG_BINFMT_AOUT is not set
-# CONFIG_DRM_TDFX is not set
-# CONFIG_DRM_GAMMA is not set
-# CONFIG_DRM_SIS is not set
-# CONFIG_BLK_DEV_UMEM is not set
-# CONFIG_MWAVE is not set
-# CONFIG_ROCKETPORT is not set
-# CONFIG_R3964 is not set
-# CONFIG_TIPAR is not set
-# CONFIG_JOYSTICK_ANALOG is not set
-# CONFIG_JOYSTICK_A3D is not set
-# CONFIG_JOYSTICK_ADI is not set
-# CONFIG_JOYSTICK_COBRA is not set
-# CONFIG_JOYSTICK_GF2K is not set
-# CONFIG_JOYSTICK_GRIP is not set
-# CONFIG_JOYSTICK_GRIP_MP is not set
-# CONFIG_JOYSTICK_GUILLEMOT is not set
-# CONFIG_JOYSTICK_INTERACT is not set
-# CONFIG_JOYSTICK_SIDEWINDER is not set
-# CONFIG_JOYSTICK_TMDC is not set
-# CONFIG_JOYSTICK_IFORCE is not set
-# CONFIG_JOYSTICK_IFORCE_USB=y
-# CONFIG_JOYSTICK_IFORCE_232=y
-# CONFIG_JOYSTICK_WARRIOR is not set
-# CONFIG_JOYSTICK_MAGELLAN is not set
-# CONFIG_JOYSTICK_SPACEORB is not set
-# CONFIG_JOYSTICK_SPACEBALL is not set
-# CONFIG_JOYSTICK_STINGER is not set
-# CONFIG_JOYSTICK_TWIDDLER is not set
-# CONFIG_JOYSTICK_DB9 is not set
-# CONFIG_JOYSTICK_GAMECON is not set
-# CONFIG_JOYSTICK_TURBOGRAFX is not set
-# CONFIG_RADIO_CADET is not set
-# CONFIG_RADIO_RTRACK is not set
-# CONFIG_RADIO_RTRACK2 is not set
-# CONFIG_RADIO_AZTECH is not set
-# CONFIG_RADIO_GEMTEK is not set
-# CONFIG_RADIO_GEMTEK_PCI is not set
-# CONFIG_RADIO_MAXIRADIO is not set
-# CONFIG_RADIO_MAESTRO is not set
-# CONFIG_RADIO_MIROPCM20 is not set
-# CONFIG_RADIO_MIROPCM20_RDS is not set
-# CONFIG_RADIO_SF16FMI is not set
-# CONFIG_RADIO_SF16FMR2 is not set
-# CONFIG_RADIO_TERRATEC is not set
-# CONFIG_RADIO_TRUST is not set
-# CONFIG_RADIO_TYPHOON is not set
-# CONFIG_RADIO_TYPHOON_PROC_FS=y
-# CONFIG_RADIO_ZOLTRIX is not set
-# CONFIG_VIDEO_DEV is not set
-# CONFIG_PLIP is not set
-# CONFIG_FB_MATROX is not set
-# CONFIG_FB_3DFX is not set
-# CONFIG_FB_HGA is not set
-# CONFIG_FB_ATY is not set
-# CONFIG_FB_TRIDENT is not set
-# CONFIG_FB_VOODOO1 is not set
-# CONFIG_SCSI_ADVANSYS is not set
-# CONFIG_SCSI_AHA1542 is not set
-CONFIG_SCSI_FUTURE_DOMAIN=m
-# CONFIG_SCSI_IN2000 is not set
-# CONFIG_SCSI_QLOGIC_FAS is not set
-# CONFIG_SCSI_QLOGIC_ISP is not set
-# CONFIG_VIDEO_BT848 is not set
-# CONFIG_VIDEO_PMS is not set
-# CONFIG_VIDEO_BWQCAM is not set
-# CONFIG_VIDEO_CQCAM is not set
-# CONFIG_VIDEO_W9966 is not set
-# CONFIG_VIDEO_CPIA is not set
-# CONFIG_VIDEO_CPIA_PP is not set
-# CONFIG_VIDEO_CPIA_USB is not set
-# CONFIG_VIDEO_SAA5249 is not set
-# CONFIG_TUNER_3036 is not set
-# CONFIG_VIDEO_STRADIS is not set
-# CONFIG_VIDEO_ZORAN is not set
-# CONFIG_VIDEO_ZORAN is not set
-# CONFIG_VIDEO_ZORAN_BUZ is not set
-# CONFIG_VIDEO_ZORAN_DC10 is not set
-# CONFIG_VIDEO_ZORAN_DC30 is not set
-# CONFIG_VIDEO_ZORAN_LML33 is not set
-# CONFIG_VIDEO_ZORAN_LML33R10 is not set
-# CONFIG_VIDEO_MEYE is not set
-# CONFIG_VIDEO_SAA7134 is not set
-# CONFIG_VIDEO_MXB is not set
-# CONFIG_VIDEO_DPC is not set
-# CONFIG_VIDEO_HEXIUM_ORION is not set
-# CONFIG_VIDEO_HEXIUM_GEMINI is not set
-# CONFIG_VIDEO_CX88 is not set
-# CONFIG_VIDEO_SAA5246A is not set
-# CONFIG_FB_ATY128 is not set
-# CONFIG_FB_RADEON is not set
-# CONFIG_FB_NEOMAGIC is not set
-# CONFIG_I82092 is not set
-# CONFIG_YELLOWFIN is not set
-# CONFIG_SUNDANCE is not set
-# CONFIG_ULTRA is not set
-# CONFIG_SKFP is not set
-# CONFIG_DE600 is not set
-# CONFIG_DE620 is not set
-# CONFIG_CS89x0 is not set
-# CONFIG_DGRS is not set
-# CONFIG_AC3200 is not set
-# CONFIG_NI52 is not set
-# CONFIG_NI65 is not set
-# CONFIG_LANCE is not set
-# CONFIG_ELPLUS is not set
-# CONFIG_EL16 is not set
-# CONFIG_EL3 is not set
-# CONFIG_3C515 is not set
-# CONFIG_HAMACHI is not set
-CONFIG_HP100=m
-# CONFIG_EQUALIZER is not set
-# CONFIG_NET_SB1000 is not set
-# CONFIG_DEPCA is not set
-# CONFIG_ATP is not set
-# CONFIG_FB_NEOMAGIC is not set
-# CONFIG_INFTL is not set
-# CONFIG_MTD_DOC2000 is not set
-# CONFIG_MTD_DOC2001PLUS is not set
-# CONFIG_MTD_PMC551 is not set
-# CONFIG_MTD_COMPLEX_MAPPINGS is not set
-# CONFIG_MTD_PCI is not set
-# CONFIG_PCMCIA_SYM53C500 is not set
-# CONFIG_FB_ASILIANT is not set
-# CONFIG_FB_HGA_ACCEL is not set
-# CONFIG_FB_3DFX_ACCEL is not set
-# CONFIG_FB_TRIDENT_ACCEL is not set
-# CONFIG_SCSI_DC390T is not set
-CONFIG_AUDIT=y
-# CONFIG_AUTOFS_FS is not set
-# CONFIG_JFS_FS is not set
-# CONFIG_I2C_PCA_ISA is not set
-CONFIG_RAW_DRIVER=y
-# CONFIG_MTD_SCB2_FLASH is not set
-CONFIG_UID16=y
-# CONFIG_X86_PC is not set
-# CONFIG_X86_ELAN is not set
-# CONFIG_X86_VOYAGER is not set
-# CONFIG_X86_NUMAQ is not set
-# CONFIG_X86_SUMMIT is not set
-# CONFIG_X86_BIGSMP is not set
-# CONFIG_X86_VISWS is not set
-CONFIG_X86_GENERICARCH=y
-# CONFIG_X86_ES7000 is not set
-# CONFIG_M386 is not set
-# CONFIG_M486 is not set
-# CONFIG_M586 is not set
-# CONFIG_M586TSC is not set
-# CONFIG_M586MMX is not set
-CONFIG_M686=y
-# CONFIG_MPENTIUMII is not set
-# CONFIG_MPENTIUMIII is not set
-# CONFIG_MPENTIUMM is not set
-# CONFIG_MPENTIUM4 is not set
-# CONFIG_MK6 is not set
-# CONFIG_MK7 is not set
-# CONFIG_MK8 is not set
-# CONFIG_MCRUSOE is not set
-# CONFIG_MWINCHIPC6 is not set
-# CONFIG_MWINCHIP2 is not set
-# CONFIG_MWINCHIP3D is not set
-# CONFIG_MCYRIXIII is not set
-# CONFIG_MVIAC3_2 is not set
-CONFIG_X86_GENERIC=y
-CONFIG_X86_CMPXCHG=y
-CONFIG_X86_XADD=y
-CONFIG_X86_L1_CACHE_SHIFT=7
-CONFIG_RWSEM_XCHGADD_ALGORITHM=y
-CONFIG_X86_PPRO_FENCE=y
-CONFIG_X86_WP_WORKS_OK=y
-CONFIG_X86_INVLPG=y
-CONFIG_X86_BSWAP=y
-CONFIG_X86_POPAD_OK=y
-CONFIG_X86_GOOD_APIC=y
-CONFIG_X86_INTEL_USERCOPY=y
-CONFIG_X86_USE_PPRO_CHECKSUM=y
-# CONFIG_HPET is not set
-CONFIG_HPET_TIMER=y
-CONFIG_HPET_EMULATE_RTC=y
-CONFIG_NR_CPUS=32
-CONFIG_X86_LOCAL_APIC=y
-CONFIG_X86_IO_APIC=y
-CONFIG_X86_TSC=y
-CONFIG_X86_MCE=y
-# CONFIG_X86_MCE_NONFATAL is not set
-CONFIG_X86_MCE_P4THERMAL=y
-CONFIG_TOSHIBA=m
-CONFIG_I8K=m
-CONFIG_SONYPI=m
-CONFIG_MICROCODE=m
-CONFIG_X86_MSR=m
-CONFIG_X86_CPUID=m
-CONFIG_EDD=m
-# CONFIG_NUMA is not set
-# CONFIG_NOHIGHMEM is not set
-CONFIG_HIGHMEM4G=y
-CONFIG_HIGHMEM64G=y
-# CONFIG_HIGHMEM64G is not set
-CONFIG_HIGHMEM=y
-CONFIG_HIGHPTE=y
-# CONFIG_MATH_EMULATION is not set
-CONFIG_MTRR=y
-CONFIG_HAVE_DEC_LOCK=y
-# CONFIG_X86_UP_APIC is not set
-CONFIG_X86_PM_TIMER=y
-# CONFIG_X86_4G is not set
-# CONFIG_EFI is not set
-CONFIG_REGPARM=y
-# CONFIG_PCI_GOBIOS is not set
-# CONFIG_PCI_GODIRECT is not set
-# CONFIG_PCI_GOMMCONFIG is not set
-CONFIG_PCI_GOANY=y
-CONFIG_MDA_CONSOLE=m
-CONFIG_SYNCLINK_CS=m
-CONFIG_SYNCLINK=m
-CONFIG_SYNCLINKMP=m
-CONFIG_HP100=m
-CONFIG_PCMCIA_FDOMAIN=m
-CONFIG_SCSI_FUTURE_DOMAIN=m
-CONFIG_CRASH=m
-CONFIG_CAPI_EICON=y
-CONFIG_I2O=m
-CONFIG_I2O_BLOCK=m
-CONFIG_I2O_SCSI=m
-CONFIG_I2O_PROC=m
-CONFIG_I2O_CONFIG=y
-CONFIG_APM=y
-# CONFIG_APM_IGNORE_USER_SUSPEND is not set
-# CONFIG_APM_DO_ENABLE is not set
-CONFIG_APM_CPU_IDLE=y
-# CONFIG_APM_DISPLAY_BLANK is not set
-CONFIG_APM_RTC_IS_GMT=y
-# CONFIG_APM_ALLOW_INTS is not set
-# CONFIG_APM_REAL_MODE_POWER_OFF is not set
-CONFIG_X86_FIND_SMP_CONFIG=y
-CONFIG_X86_MPPARSE=y
-CONFIG_ACPI=y
-CONFIG_ACPI_BOOT=y
-CONFIG_ACPI_INTERPRETER=y
-CONFIG_ACPI_SLEEP=y
-CONFIG_ACPI_AC=m
-CONFIG_ACPI_BATTERY=m
-CONFIG_ACPI_BUTTON=m
-CONFIG_ACPI_FAN=y
-CONFIG_ACPI_PROCESSOR=y
-CONFIG_ACPI_THERMAL=y
-CONFIG_ACPI_ASUS=m
-CONFIG_ACPI_TOSHIBA=m
-# CONFIG_ACPI_DEBUG is not set
-CONFIG_ACPI_BUS=y
-CONFIG_ACPI_EC=y
-CONFIG_ACPI_POWER=y
-CONFIG_ACPI_PCI=y
-CONFIG_ACPI_SYSTEM=y
-CONFIG_ACPI_NUMA=y
-CONFIG_ACPI_BLACKLIST_YEAR=2001
-CONFIG_X86_ACPI_CPUFREQ=y
-# CONFIG_X86_ACPI_CPUFREQ_PROC_INTF is not set
-CONFIG_X86_POWERNOW_K6=m
-CONFIG_X86_POWERNOW_K7=y
-CONFIG_X86_POWERNOW_K8=m
-# CONFIG_X86_GX_SUSPMOD is not set
-# CONFIG_X86_SPEEDSTEP_CENTRINO is not set
-CONFIG_X86_SPEEDSTEP_ICH=y
-CONFIG_X86_SPEEDSTEP_SMI=m
-CONFIG_X86_SPEEDSTEP_LIB=y
-# CONFIG_X86_SPEEDSTEP_RELAXED_CAP_CHECK is not set
-CONFIG_X86_P4_CLOCKMOD=m
-CONFIG_X86_LONGRUN=y
-CONFIG_X86_LONGHAUL=y
-CONFIG_X86_SMP=y
-CONFIG_X86_HT=y
-CONFIG_X86_BIOS_REBOOT=y
-CONFIG_X86_TRAMPOLINE=y
-CONFIG_TUX=m
-CONFIG_NVRAM=m
-CONFIG_IBM_ASM=m
-CONFIG_CRYPTO_AES_586=m
-CONFIG_GENERIC_ISA_DMA=y
-CONFIG_SCHED_SMT=y
-# CONFIG_IRQBALANCE is not set
-# CONFIG_SOFTWARE_SUSPEND is not set
-# CONFIG_PM_DEBUG is not set
-CONFIG_CPU_FREQ=y
-# CONFIG_CPU_FREQ_PROC_INTF is not set
-# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set
-CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
-CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
-CONFIG_CPU_FREQ_GOV_POWERSAVE=m
-CONFIG_CPU_FREQ_GOV_USERSPACE=m
-CONFIG_CPU_FREQ_GOV_ONDEMAND=m
-# CONFIG_CPU_FREQ_24_API is not set
-CONFIG_CPU_FREQ_TABLE=y
-CONFIG_DEBUG_STACKOVERFLOW=y
-CONFIG_DEBUG_STACK_USAGE=y
-# CONFIG_DEBUG_PAGEALLOC is not set
-# CONFIG_KPROBES is not set
-CONFIG_KEXEC=y
-CONFIG_NETDUMP=m
-# CONFIG_SCHEDSTATS is not set
-CONFIG_PCI_DIRECT=y
-CONFIG_PCI_MMCONFIG=y
-CONFIG_PCI_BIOS=y
-CONFIG_HOTPLUG_PCI=y
-# CONFIG_HOTPLUG_PCI_FAKE is not set
-CONFIG_HOTPLUG_PCI_COMPAQ=m
-# CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM is not set
-CONFIG_HOTPLUG_PCI_IBM=m
-CONFIG_HOTPLUG_PCI_ACPI=m 
-CONFIG_HOTPLUG_PCI_ACPI_IBM=m
-# CONFIG_HOTPLUG_PCI_CPCI is not set
-CONFIG_HOTPLUG_PCI_PCIE=m
-# CONFIG_HOTPLUG_PCI_PCIE_POLL_EVENT_MODE is not set
-CONFIG_HOTPLUG_PCI_SHPC=m
-# CONFIG_HOTPLUG_PCI_SHPC_POLL_EVENT_MODE is not set
-# CONFIG_HOTPLUG_PCI_SHPC_PHPRM_LEGACY is not set
-CONFIG_PM=y
-CONFIG_IEEE80211=m
-# CONFIG_IEEE80211_DEBUG is not set
-CONFIG_IEEE80211_CRYPT=m
-CONFIG_IEEE80211_WPA=m
-CONFIG_IEEE80211_CRYPT_CCMP=m
-CONFIG_IEEE80211_CRYPT_TKIP=m
-CONFIG_IPW2100=m
-# CONFIG_IPW_DEBUG is not set
-CONFIG_IPW2100_PROMISC=y
-# CONFIG_IPW2100_LEGACY_FW_LOAD is not set
-CONFIG_IPW2200=m
-CONFIG_M686=y
-# CONFIG_NOHIGHMEM is not set
-CONFIG_HIGHMEM64G=y
-CONFIG_HOTPLUG_PCI=y
-CONFIG_HOTPLUG_PCI_COMPAQ=m
-CONFIG_HOTPLUG_PCI_IBM=m
-# CONFIG_HIGHMEM64G is not set
-# CONFIG_EWRK3 is not set
-CONFIG_NR_CPUS=32
-# CONFIG_X86_PC is not set
-CONFIG_X86_GENERICARCH=y
index 8d66567..18cb4d1 100644 (file)
@@ -1,28 +1,34 @@
 #
 # Automatically generated make config: don't edit
+# Linux kernel version: 2.6.9-prep
+# Fri May 13 14:09:31 2005
 #
+CONFIG_X86=y
 CONFIG_MMU=y
-# CONFIG_SMP is not set
-# CONFIG_HOTPLUG_CPU is not set
-CONFIG_LOCALVERSION=""
+CONFIG_UID16=y
+CONFIG_GENERIC_ISA_DMA=y
+CONFIG_GENERIC_IOMAP=y
 
 #
 # Code maturity level options
 #
 CONFIG_EXPERIMENTAL=y
 CONFIG_CLEAN_COMPILE=y
-CONFIG_STANDALONE=y
-CONFIG_PREVENT_FIRMWARE_BUILD=y
 
 #
 # General setup
 #
+CONFIG_LOCALVERSION=""
 CONFIG_SWAP=y
 CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
 CONFIG_BSD_PROCESS_ACCT=y
 # CONFIG_BSD_PROCESS_ACCT_V3 is not set
 CONFIG_SYSCTL=y
+CONFIG_AUDIT=y
+CONFIG_AUDITSYSCALL=y
 CONFIG_LOG_BUF_SHIFT=17
+CONFIG_HOTPLUG=y
 # CONFIG_IKCONFIG is not set
 # CONFIG_EMBEDDED is not set
 CONFIG_KALLSYMS=y
@@ -33,9 +39,10 @@ CONFIG_EPOLL=y
 CONFIG_IOSCHED_NOOP=y
 CONFIG_IOSCHED_AS=y
 CONFIG_IOSCHED_DEADLINE=y
-# CONFIG_PREEMPT is not set
-CONFIG_PREEMPT_VOLUNTARY=y
-CONFIG_POSIX_MQUEUE=y
+CONFIG_IOSCHED_CFQ=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SHMEM=y
+# CONFIG_TINY_SHMEM is not set
 
 #
 # Loadable module support
@@ -45,14 +52,188 @@ CONFIG_MODULE_UNLOAD=y
 # CONFIG_MODULE_FORCE_UNLOAD is not set
 CONFIG_OBSOLETE_MODPARM=y
 CONFIG_MODVERSIONS=y
-CONFIG_KMOD=y
 CONFIG_MODULE_SIG=y
 # CONFIG_MODULE_SIG_FORCE is not set
+CONFIG_KMOD=y
+CONFIG_STOP_MACHINE=y
+
+#
+# Processor type and features
+#
+# CONFIG_X86_PC is not set
+# CONFIG_X86_ELAN is not set
+# CONFIG_X86_VOYAGER is not set
+# CONFIG_X86_NUMAQ is not set
+# CONFIG_X86_SUMMIT is not set
+# CONFIG_X86_BIGSMP is not set
+# CONFIG_X86_VISWS is not set
+CONFIG_X86_GENERICARCH=y
+# CONFIG_X86_ES7000 is not set
+CONFIG_X86_CYCLONE_TIMER=y
+# CONFIG_M386 is not set
+# CONFIG_M486 is not set
+# CONFIG_M586 is not set
+# CONFIG_M586TSC is not set
+# CONFIG_M586MMX is not set
+CONFIG_M686=y
+# CONFIG_MPENTIUMII is not set
+# CONFIG_MPENTIUMIII is not set
+# CONFIG_MPENTIUMM is not set
+# CONFIG_MPENTIUM4 is not set
+# CONFIG_MK6 is not set
+# CONFIG_MK7 is not set
+# CONFIG_MK8 is not set
+# CONFIG_MCRUSOE is not set
+# CONFIG_MWINCHIPC6 is not set
+# CONFIG_MWINCHIP2 is not set
+# CONFIG_MWINCHIP3D is not set
+# CONFIG_MCYRIXIII is not set
+# CONFIG_MVIAC3_2 is not set
+CONFIG_X86_GENERIC=y
+CONFIG_X86_CMPXCHG=y
+CONFIG_X86_XADD=y
+CONFIG_X86_L1_CACHE_SHIFT=7
+CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_X86_PPRO_FENCE=y
+CONFIG_X86_WP_WORKS_OK=y
+CONFIG_X86_INVLPG=y
+CONFIG_X86_BSWAP=y
+CONFIG_X86_POPAD_OK=y
+CONFIG_X86_GOOD_APIC=y
+CONFIG_X86_INTEL_USERCOPY=y
+CONFIG_X86_USE_PPRO_CHECKSUM=y
+# CONFIG_X86_4G is not set
+# CONFIG_X86_SWITCH_PAGETABLES is not set
+# CONFIG_X86_4G_VM_LAYOUT is not set
+# CONFIG_X86_UACCESS_INDIRECT is not set
+# CONFIG_X86_HIGH_ENTRY is not set
+CONFIG_HPET_TIMER=y
+CONFIG_HPET_EMULATE_RTC=y
+CONFIG_SMP=y
+CONFIG_NR_CPUS=32
+CONFIG_SCHED_SMT=y
+# CONFIG_PREEMPT is not set
+CONFIG_PREEMPT_VOLUNTARY=y
+CONFIG_X86_LOCAL_APIC=y
+CONFIG_X86_IO_APIC=y
+CONFIG_X86_TSC=y
+CONFIG_X86_MCE=y
+# CONFIG_X86_MCE_NONFATAL is not set
+CONFIG_X86_MCE_P4THERMAL=y
+CONFIG_TOSHIBA=m
+CONFIG_I8K=m
+CONFIG_MICROCODE=m
+CONFIG_X86_MSR=m
+CONFIG_X86_CPUID=m
+
+#
+# Firmware Drivers
+#
+CONFIG_EDD=m
+# CONFIG_NOHIGHMEM is not set
+# CONFIG_HIGHMEM4G is not set
+CONFIG_HIGHMEM64G=y
+CONFIG_HIGHMEM=y
+CONFIG_X86_PAE=y
+# CONFIG_NUMA is not set
+CONFIG_HIGHPTE=y
+# CONFIG_MATH_EMULATION is not set
+CONFIG_MTRR=y
+# CONFIG_EFI is not set
+# CONFIG_IRQBALANCE is not set
+CONFIG_HAVE_DEC_LOCK=y
+CONFIG_REGPARM=y
+CONFIG_KEXEC=y
+
+#
+# Power management options (ACPI, APM)
+#
+CONFIG_PM=y
+# CONFIG_PM_DEBUG is not set
+# CONFIG_SOFTWARE_SUSPEND is not set
+
+#
+# ACPI (Advanced Configuration and Power Interface) Support
+#
+CONFIG_ACPI=y
+CONFIG_ACPI_BOOT=y
+CONFIG_ACPI_INTERPRETER=y
+CONFIG_ACPI_SLEEP=y
+CONFIG_ACPI_SLEEP_PROC_FS=y
+CONFIG_ACPI_AC=m
+CONFIG_ACPI_BATTERY=m
+CONFIG_ACPI_BUTTON=m
+CONFIG_ACPI_FAN=y
+CONFIG_ACPI_PROCESSOR=y
+CONFIG_ACPI_THERMAL=y
+CONFIG_ACPI_ASUS=m
+CONFIG_ACPI_TOSHIBA=m
+CONFIG_ACPI_BLACKLIST_YEAR=2001
+# CONFIG_ACPI_DEBUG is not set
+CONFIG_ACPI_BUS=y
+CONFIG_ACPI_EC=y
+CONFIG_ACPI_POWER=y
+CONFIG_ACPI_PCI=y
+CONFIG_ACPI_SYSTEM=y
+CONFIG_X86_PM_TIMER=y
+
+#
+# APM (Advanced Power Management) BIOS Support
+#
+CONFIG_APM=y
+# CONFIG_APM_IGNORE_USER_SUSPEND is not set
+# CONFIG_APM_DO_ENABLE is not set
+CONFIG_APM_CPU_IDLE=y
+# CONFIG_APM_DISPLAY_BLANK is not set
+CONFIG_APM_RTC_IS_GMT=y
+# CONFIG_APM_ALLOW_INTS is not set
+# CONFIG_APM_REAL_MODE_POWER_OFF is not set
+
+#
+# CPU Frequency scaling
+#
+CONFIG_CPU_FREQ=y
+# CONFIG_CPU_FREQ_PROC_INTF is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set
+CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=m
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=m
+# CONFIG_CPU_FREQ_24_API is not set
+CONFIG_CPU_FREQ_TABLE=y
+
+#
+# CPUFreq processor drivers
+#
+CONFIG_X86_ACPI_CPUFREQ=y
+# CONFIG_X86_ACPI_CPUFREQ_PROC_INTF is not set
+CONFIG_X86_POWERNOW_K6=m
+CONFIG_X86_POWERNOW_K7=y
+CONFIG_X86_POWERNOW_K7_ACPI=y
+CONFIG_X86_POWERNOW_K8=m
+CONFIG_X86_POWERNOW_K8_ACPI=y
+# CONFIG_X86_GX_SUSPMOD is not set
+# CONFIG_X86_SPEEDSTEP_CENTRINO is not set
+CONFIG_X86_SPEEDSTEP_ICH=y
+CONFIG_X86_SPEEDSTEP_SMI=m
+CONFIG_X86_P4_CLOCKMOD=m
+CONFIG_X86_SPEEDSTEP_LIB=y
+# CONFIG_X86_SPEEDSTEP_RELAXED_CAP_CHECK is not set
+CONFIG_X86_LONGRUN=y
+CONFIG_X86_LONGHAUL=y
 
 #
 # Bus options (PCI, PCMCIA, EISA, MCA, ISA)
 #
 CONFIG_PCI=y
+# CONFIG_PCI_GOBIOS is not set
+# CONFIG_PCI_GOMMCONFIG is not set
+# CONFIG_PCI_GODIRECT is not set
+CONFIG_PCI_GOANY=y
+CONFIG_PCI_BIOS=y
+CONFIG_PCI_DIRECT=y
+CONFIG_PCI_MMCONFIG=y
 CONFIG_PCI_MSI=y
 CONFIG_PCI_LEGACY_PROC=y
 # CONFIG_PCI_NAMES is not set
@@ -60,22 +241,37 @@ CONFIG_ISA=y
 # CONFIG_EISA is not set
 # CONFIG_MCA is not set
 # CONFIG_SCx200 is not set
-CONFIG_HOTPLUG=y
 
 #
 # PCMCIA/CardBus support
 #
 CONFIG_PCMCIA=m
 # CONFIG_PCMCIA_DEBUG is not set
-CONFIG_YENTA=y
+CONFIG_YENTA=m
 CONFIG_CARDBUS=y
+CONFIG_PD6729=m
 # CONFIG_I82092 is not set
 CONFIG_I82365=m
-CONFIG_PD6729=m
 CONFIG_TCIC=m
 CONFIG_PCMCIA_PROBE=y
 
 #
+# PCI Hotplug Support
+#
+CONFIG_HOTPLUG_PCI=y
+# CONFIG_HOTPLUG_PCI_FAKE is not set
+CONFIG_HOTPLUG_PCI_COMPAQ=m
+# CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM is not set
+CONFIG_HOTPLUG_PCI_IBM=m
+CONFIG_HOTPLUG_PCI_ACPI=m
+CONFIG_HOTPLUG_PCI_ACPI_IBM=m
+# CONFIG_HOTPLUG_PCI_CPCI is not set
+CONFIG_HOTPLUG_PCI_PCIE=m
+# CONFIG_HOTPLUG_PCI_PCIE_POLL_EVENT_MODE is not set
+CONFIG_HOTPLUG_PCI_SHPC=m
+# CONFIG_HOTPLUG_PCI_SHPC_POLL_EVENT_MODE is not set
+
+#
 # Executable file formats
 #
 CONFIG_BINFMT_ELF=y
@@ -89,18 +285,23 @@ CONFIG_BINFMT_MISC=y
 #
 # Generic Driver Options
 #
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
 CONFIG_FW_LOADER=y
+# CONFIG_DEBUG_DRIVER is not set
 
 #
 # Memory Technology Devices (MTD)
 #
 CONFIG_MTD=m
 # CONFIG_MTD_DEBUG is not set
-CONFIG_MTD_PARTITIONS=m
+CONFIG_MTD_PARTITIONS=y
 CONFIG_MTD_CONCAT=m
 CONFIG_MTD_REDBOOT_PARTS=m
+# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set
+# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set
 CONFIG_MTD_CMDLINE_PARTS=y
-CONFIG_MTD_PARTITIONS=y
+
 #
 # User Modules And Translation Layers
 #
@@ -119,13 +320,24 @@ CONFIG_MTD_CFI=m
 CONFIG_MTD_JEDECPROBE=m
 CONFIG_MTD_GEN_PROBE=m
 # CONFIG_MTD_CFI_ADV_OPTIONS is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
 CONFIG_MTD_CFI_INTELEXT=m
 CONFIG_MTD_CFI_AMDSTD=m
+CONFIG_MTD_CFI_AMDSTD_RETRY=3
 CONFIG_MTD_CFI_STAA=m
+CONFIG_MTD_CFI_UTIL=m
 CONFIG_MTD_RAM=m
 CONFIG_MTD_ROM=m
 CONFIG_MTD_ABSENT=m
-# CONFIG_MTD_OBSOLETE_CHIPS is not set
 
 #
 # Mapping drivers for chip access
@@ -135,23 +347,19 @@ CONFIG_MTD_ABSENT=m
 # CONFIG_MTD_PNC2000 is not set
 CONFIG_MTD_SC520CDP=m
 CONFIG_MTD_NETSC520=m
-CONFIG_MTD_SBC_GXX=m
-CONFIG_MTD_ELAN_104NC=m
 CONFIG_MTD_SCx200_DOCFLASH=m
 # CONFIG_MTD_AMD76XROM is not set
 # CONFIG_MTD_SCB2_FLASH is not set
 # CONFIG_MTD_NETtel is not set
 # CONFIG_MTD_DILNETPC is not set
 # CONFIG_MTD_L440GX is not set
-# CONFIG_MTD_PCI is not set
 
 #
 # Self-contained MTD device drivers
 #
 # CONFIG_MTD_PMC551 is not set
-# CONFIG_MTD_PMC551_BUGFIX is not set
-# CONFIG_MTD_PMC551_DEBUG is not set
 # CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
 CONFIG_MTD_MTDRAM=m
 CONFIG_MTDRAM_TOTAL_SIZE=4096
 CONFIG_MTDRAM_ERASE_SIZE=128
@@ -163,9 +371,6 @@ CONFIG_MTDRAM_ERASE_SIZE=128
 # CONFIG_MTD_DOC2000 is not set
 # CONFIG_MTD_DOC2001 is not set
 # CONFIG_MTD_DOC2001PLUS is not set
-CONFIG_MTD_DOCPROBE=m
-# CONFIG_MTD_DOCPROBE_ADVANCED is not set
-CONFIG_MTD_DOCPROBE_ADDRESS=0
 
 #
 # NAND Flash Device Drivers
@@ -173,15 +378,7 @@ CONFIG_MTD_DOCPROBE_ADDRESS=0
 CONFIG_MTD_NAND=m
 # CONFIG_MTD_NAND_VERIFY_WRITE is not set
 CONFIG_MTD_NAND_IDS=m
-
-# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set
-# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set
-CONFIG_MTD_CFI_AMDSTD_RETRY=3
-# CONFIG_MTD_ICHXROM is not set
-# CONFIG_MTD_PHRAM is not set
 # CONFIG_MTD_NAND_DISKONCHIP is not set
-# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set
-
 
 #
 # Parallel port support
@@ -222,45 +419,41 @@ CONFIG_BLK_DEV_DAC960=m
 CONFIG_BLK_DEV_LOOP=m
 CONFIG_BLK_DEV_CRYPTOLOOP=m
 CONFIG_BLK_DEV_NBD=m
+CONFIG_BLK_DEV_SX8=m
+# CONFIG_BLK_DEV_UB is not set
 CONFIG_BLK_DEV_RAM=y
 CONFIG_BLK_DEV_RAM_SIZE=16384
 CONFIG_BLK_DEV_INITRD=y
-CONFIG_BLK_DEV_ATIIXP=y
-CONFIG_BLK_DEV_DELKIN=y
-CONFIG_BLK_DEV_IT8212=y
 CONFIG_LBD=y
-# CONFIG_DCSSBLK is not set
-
+CONFIG_DISKDUMP=m
 
 #
 # ATA/ATAPI/MFM/RLL support
 #
 CONFIG_IDE=y
-CONFIG_IDE_GENERIC=y
-# CONFIG_HPT34X_AUTODMA is not set
-
 CONFIG_BLK_DEV_IDE=y
 
 #
 # Please see Documentation/ide.txt for help/info on IDE drives
 #
+# CONFIG_BLK_DEV_IDE_SATA is not set
 # CONFIG_BLK_DEV_HD_IDE is not set
 CONFIG_BLK_DEV_IDEDISK=y
 CONFIG_IDEDISK_MULTI_MODE=y
 CONFIG_BLK_DEV_IDECS=m
+CONFIG_BLK_DEV_DELKIN=m
 CONFIG_BLK_DEV_IDECD=y
 # CONFIG_BLK_DEV_IDETAPE is not set
 CONFIG_BLK_DEV_IDEFLOPPY=y
 CONFIG_BLK_DEV_IDESCSI=m
 # CONFIG_IDE_TASK_IOCTL is not set
 # CONFIG_IDE_TASKFILE_IO is not set
-# CONFIG_BLK_DEV_IDE_SATA is not set
 
 #
 # IDE chipset support/bugfixes
 #
+CONFIG_IDE_GENERIC=y
 # CONFIG_BLK_DEV_CMD640 is not set
-# CONFIG_BLK_DEV_CMD640_ENHANCED is not set
 CONFIG_BLK_DEV_IDEPNP=y
 CONFIG_BLK_DEV_IDEPCI=y
 CONFIG_IDEPCI_SHARE_IRQ=y
@@ -276,15 +469,18 @@ CONFIG_BLK_DEV_AEC62XX=y
 CONFIG_BLK_DEV_ALI15X3=y
 # CONFIG_WDC_ALI15X3 is not set
 CONFIG_BLK_DEV_AMD74XX=y
+CONFIG_BLK_DEV_ATIIXP=y
 CONFIG_BLK_DEV_CMD64X=y
 CONFIG_BLK_DEV_TRIFLEX=y
 CONFIG_BLK_DEV_CY82C693=y
 CONFIG_BLK_DEV_CS5520=y
 CONFIG_BLK_DEV_CS5530=y
 CONFIG_BLK_DEV_HPT34X=y
+# CONFIG_HPT34X_AUTODMA is not set
 CONFIG_BLK_DEV_HPT366=y
 # CONFIG_BLK_DEV_SC1200 is not set
 CONFIG_BLK_DEV_PIIX=y
+CONFIG_BLK_DEV_IT8212=y
 # CONFIG_BLK_DEV_NS87415 is not set
 CONFIG_BLK_DEV_PDC202XX_OLD=y
 # CONFIG_PDC202XX_BURST is not set
@@ -296,6 +492,7 @@ CONFIG_BLK_DEV_SIS5513=y
 CONFIG_BLK_DEV_SLC90E66=y
 # CONFIG_BLK_DEV_TRM290 is not set
 CONFIG_BLK_DEV_VIA82CXXX=y
+# CONFIG_IDE_ARM is not set
 # CONFIG_IDE_CHIPSETS is not set
 CONFIG_BLK_DEV_IDEDMA=y
 # CONFIG_IDEDMA_IVB is not set
@@ -312,6 +509,8 @@ CONFIG_SCSI_PROC_FS=y
 # SCSI support type (disk, tape, CD-ROM)
 #
 CONFIG_BLK_DEV_SD=m
+CONFIG_SCSI_DUMP=m
+CONFIG_SD_IOSTATS=y
 CONFIG_CHR_DEV_ST=m
 CONFIG_CHR_DEV_OSST=m
 CONFIG_BLK_DEV_SR=m
@@ -324,8 +523,12 @@ CONFIG_CHR_DEV_SG=m
 # CONFIG_SCSI_MULTI_LUN is not set
 CONFIG_SCSI_CONSTANTS=y
 CONFIG_SCSI_LOGGING=y
-CONFIG_SCSI_SPI_ATTRS=y
-CONFIG_SCSI_FC_ATTRS=y
+
+#
+# SCSI Transport Attributes
+#
+CONFIG_SCSI_SPI_ATTRS=m
+CONFIG_SCSI_FC_ATTRS=m
 
 #
 # SCSI low-level drivers
@@ -340,8 +543,6 @@ CONFIG_SCSI_AACRAID=m
 CONFIG_SCSI_AIC7XXX=m
 CONFIG_AIC7XXX_CMDS_PER_DEVICE=4
 CONFIG_AIC7XXX_RESET_DELAY_MS=15000
-# CONFIG_AIC7XXX_PROBE_EISA_VL is not set
-# CONFIG_AIC7XXX_BUILD_FIRMWARE is not set
 # CONFIG_AIC7XXX_DEBUG_ENABLE is not set
 CONFIG_AIC7XXX_DEBUG_MASK=0
 # CONFIG_AIC7XXX_REG_PRETTY_PRINT is not set
@@ -349,42 +550,38 @@ CONFIG_SCSI_AIC7XXX_OLD=m
 CONFIG_SCSI_AIC79XX=m
 CONFIG_AIC79XX_CMDS_PER_DEVICE=4
 CONFIG_AIC79XX_RESET_DELAY_MS=15000
-# CONFIG_AIC79XX_BUILD_FIRMWARE is not set
 # CONFIG_AIC79XX_ENABLE_RD_STRM is not set
 # CONFIG_AIC79XX_DEBUG_ENABLE is not set
 CONFIG_AIC79XX_DEBUG_MASK=0
 # CONFIG_AIC79XX_REG_PRETTY_PRINT is not set
-# CONFIG_SCSI_ADVANSYS is not set
+# CONFIG_SCSI_DPT_I2O is not set
 # CONFIG_SCSI_IN2000 is not set
 CONFIG_MEGARAID_NEWGEN=y
 CONFIG_MEGARAID_MM=m
 CONFIG_MEGARAID_MAILBOX=m
 CONFIG_SCSI_SATA=y
+CONFIG_SCSI_SATA_AHCI=m
 CONFIG_SCSI_SATA_SVW=m
 CONFIG_SCSI_ATA_PIIX=m
+CONFIG_SCSI_SATA_NV=m
 CONFIG_SCSI_SATA_PROMISE=m
-CONFIG_SCSI_SATA_VIA=m
-CONFIG_BLK_DEV_SX8=m
-CONFIG_SCSI_SATA_VITESSE=m
+CONFIG_SCSI_SATA_SX4=m
 CONFIG_SCSI_SATA_SIL=m
 CONFIG_SCSI_SATA_SIS=m
-CONFIG_SCSI_SATA_SX4=m
-CONFIG_SCSI_SATA_NV=m
-CONFIG_SCSI_SATA_AHCI=m
-
+CONFIG_SCSI_SATA_VIA=m
+CONFIG_SCSI_SATA_VITESSE=m
 # CONFIG_SCSI_BUSLOGIC is not set
-CONFIG_SCSI_INITIO=m
-# CONFIG_SCSI_OMIT_FLASHPOINT is not set
-# CONFIG_SCSI_CPQFCTS is not set
 # CONFIG_SCSI_DMX3191D is not set
 # CONFIG_SCSI_DTC3280 is not set
 # CONFIG_SCSI_EATA is not set
 # CONFIG_SCSI_EATA_PIO is not set
+CONFIG_SCSI_LPFC=m
 CONFIG_SCSI_FUTURE_DOMAIN=m
 CONFIG_SCSI_GDTH=m
 # CONFIG_SCSI_GENERIC_NCR5380 is not set
 # CONFIG_SCSI_GENERIC_NCR5380_MMIO is not set
 CONFIG_SCSI_IPS=m
+CONFIG_SCSI_INITIO=m
 # CONFIG_SCSI_INIA100 is not set
 CONFIG_SCSI_PPA=m
 CONFIG_SCSI_IMM=m
@@ -396,21 +593,13 @@ CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=1
 CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16
 CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64
 # CONFIG_SCSI_SYM53C8XX_IOMAPPED is not set
+# CONFIG_SCSI_IPR is not set
 # CONFIG_SCSI_PAS16 is not set
 # CONFIG_SCSI_PSI240I is not set
 # CONFIG_SCSI_QLOGIC_FAS is not set
 # CONFIG_SCSI_QLOGIC_ISP is not set
 # CONFIG_SCSI_QLOGIC_FC is not set
-# CONFIG_SCSI_QLOGIC_FC_FIRMWARE is not set
 CONFIG_SCSI_QLOGIC_1280=m
-# CONFIG_SCSI_SYM53C416 is not set
-# CONFIG_SCSI_DC395x is not set
-# CONFIG_SCSI_T128 is not set
-# CONFIG_SCSI_U14_34F is not set
-# CONFIG_SCSI_ULTRASTOR is not set
-# CONFIG_SCSI_NSP32 is not set
-# CONFIG_SCSI_DEBUG is not set
-# CONFIG_SCSI_DC390T is not set
 CONFIG_SCSI_QLA2XXX=m
 CONFIG_SCSI_QLA21XX=m
 CONFIG_SCSI_QLA22XX=m
@@ -418,10 +607,14 @@ CONFIG_SCSI_QLA2300=m
 CONFIG_SCSI_QLA2322=m
 CONFIG_SCSI_QLA6312=m
 CONFIG_SCSI_QLA6322=m
-# CONFIG_SCSI_IPR is not set
-# CONFIG_SCSI_DPT_I2O is not set
-
-CONFIG_SCSI_LPFC=m
+# CONFIG_SCSI_SYM53C416 is not set
+# CONFIG_SCSI_DC395x is not set
+# CONFIG_SCSI_DC390T is not set
+# CONFIG_SCSI_T128 is not set
+# CONFIG_SCSI_U14_34F is not set
+# CONFIG_SCSI_ULTRASTOR is not set
+# CONFIG_SCSI_NSP32 is not set
+# CONFIG_SCSI_DEBUG is not set
 
 #
 # PCMCIA SCSI adapter support
@@ -432,7 +625,6 @@ CONFIG_PCMCIA_FDOMAIN=m
 # CONFIG_PCMCIA_QLOGIC is not set
 # CONFIG_PCMCIA_SYM53C500 is not set
 
-
 #
 # Old CD-ROM drivers (not SCSI, not IDE)
 #
@@ -446,15 +638,15 @@ CONFIG_BLK_DEV_MD=y
 CONFIG_MD_LINEAR=m
 CONFIG_MD_RAID0=m
 CONFIG_MD_RAID1=m
+CONFIG_MD_RAID10=m
 CONFIG_MD_RAID5=m
 CONFIG_MD_RAID6=m
-CONFIG_MD_RAID10=m
 CONFIG_MD_MULTIPATH=m
 CONFIG_BLK_DEV_DM=m
+CONFIG_DM_CRYPT=m
 CONFIG_DM_SNAPSHOT=m
 CONFIG_DM_MIRROR=m
 CONFIG_DM_ZERO=m
-CONFIG_DM_CRYPT=m
 
 #
 # Fusion MPT device support
@@ -465,39 +657,18 @@ CONFIG_FUSION_CTL=m
 CONFIG_FUSION_LAN=m
 
 #
-# IEEE 1394 (FireWire) support (EXPERIMENTAL)
+# IEEE 1394 (FireWire) support
 #
 # CONFIG_IEEE1394 is not set
 
 #
-# Subsystem Options
-#
-# CONFIG_IEEE1394_VERBOSEDEBUG is not set
-CONFIG_IEEE1394_OUI_DB=y
-
-#
-# Device Drivers
-#
-# CONFIG_IEEE1394_PCILYNX is not set
-CONFIG_IEEE1394_OHCI1394=m
-
-#
-# Protocol Drivers
-#
-CONFIG_IEEE1394_VIDEO1394=m
-CONFIG_IEEE1394_SBP2=m
-# CONFIG_IEEE1394_SBP2_PHYS_DMA is not set
-# CONFIG_IEEE1394_ETH1394 is not set
-CONFIG_IEEE1394_DV1394=m
-CONFIG_IEEE1394_RAWIO=m
-CONFIG_IEEE1394_CMP=m
-CONFIG_IEEE1394_AMDTP=m
-# CONFIG_IEEE1394_EXTRA_CONFIG_ROMS is not set
-
-#
 # I2O device support
 #
 CONFIG_I2O=m
+CONFIG_I2O_CONFIG=m
+CONFIG_I2O_BLOCK=m
+CONFIG_I2O_SCSI=m
+CONFIG_I2O_PROC=m
 
 #
 # Networking support
@@ -513,7 +684,6 @@ CONFIG_NETLINK_DEV=y
 CONFIG_UNIX=y
 CONFIG_NET_KEY=m
 CONFIG_INET=y
-CONFIG_INET_TUNNEL=m
 CONFIG_IP_MULTICAST=y
 CONFIG_IP_ADVANCED_ROUTER=y
 CONFIG_IP_MULTIPLE_TABLES=y
@@ -532,14 +702,7 @@ CONFIG_SYN_COOKIES=y
 CONFIG_INET_AH=m
 CONFIG_INET_ESP=m
 CONFIG_INET_IPCOMP=m
-CONFIG_NETCONSOLE=m
-# CONFIG_NETPOLL_RX is not set
-CONFIG_NETPOLL_TRAP=y
-CONFIG_NET_POLL_CONTROLLER=y
-CONFIG_NETDUMP=m
-CONFIG_DISKDUMP=m
-CONFIG_SCSI_DUMP=m
-CONFIG_SD_IOSTATS=y
+CONFIG_INET_TUNNEL=m
 
 #
 # IP: Virtual Server Configuration
@@ -547,10 +710,18 @@ CONFIG_SD_IOSTATS=y
 CONFIG_IP_VS=m
 # CONFIG_IP_VS_DEBUG is not set
 CONFIG_IP_VS_TAB_BITS=12
+
+#
+# IPVS transport protocol load balancing support
+#
 CONFIG_IP_VS_PROTO_TCP=y
 CONFIG_IP_VS_PROTO_UDP=y
 CONFIG_IP_VS_PROTO_ESP=y
 CONFIG_IP_VS_PROTO_AH=y
+
+#
+# IPVS scheduler
+#
 CONFIG_IP_VS_RR=m
 CONFIG_IP_VS_WRR=m
 CONFIG_IP_VS_LC=m
@@ -561,17 +732,18 @@ CONFIG_IP_VS_DH=m
 CONFIG_IP_VS_SH=m
 CONFIG_IP_VS_SED=m
 CONFIG_IP_VS_NQ=m
-CONFIG_IP_VS_FTP=m
-
 
+#
+# IPVS application helper
+#
+CONFIG_IP_VS_FTP=m
 CONFIG_IPV6=m
 CONFIG_IPV6_PRIVACY=y
 CONFIG_INET6_AH=m
 CONFIG_INET6_ESP=m
 CONFIG_INET6_IPCOMP=m
+CONFIG_INET6_TUNNEL=m
 CONFIG_IPV6_TUNNEL=m
-# CONFIG_DECNET is not set
-CONFIG_BRIDGE=m
 CONFIG_NETFILTER=y
 # CONFIG_NETFILTER_DEBUG is not set
 CONFIG_BRIDGE_NETFILTER=y
@@ -580,14 +752,14 @@ CONFIG_BRIDGE_NETFILTER=y
 # IP: Netfilter Configuration
 #
 CONFIG_IP_NF_CONNTRACK=m
+CONFIG_IP_NF_CT_ACCT=y
+CONFIG_IP_NF_CT_PROTO_SCTP=m
 CONFIG_IP_NF_FTP=m
 CONFIG_IP_NF_IRC=m
 CONFIG_IP_NF_TFTP=m
 CONFIG_IP_NF_AMANDA=m
 CONFIG_IP_NF_QUEUE=m
-CONFIG_IP_NF_RAW=m
 CONFIG_IP_NF_IPTABLES=m
-CONFIG_IP_NF_MATCH_COMMENT=m
 CONFIG_IP_NF_MATCH_LIMIT=m
 CONFIG_IP_NF_MATCH_IPRANGE=m
 CONFIG_IP_NF_MATCH_MAC=m
@@ -610,15 +782,18 @@ CONFIG_IP_NF_MATCH_PHYSDEV=m
 CONFIG_IP_NF_MATCH_ADDRTYPE=m
 CONFIG_IP_NF_MATCH_REALM=m
 CONFIG_IP_NF_MATCH_SCTP=m
+CONFIG_IP_NF_MATCH_COMMENT=m
 CONFIG_IP_NF_FILTER=m
 CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_LOG=m
+CONFIG_IP_NF_TARGET_ULOG=m
+CONFIG_IP_NF_TARGET_TCPMSS=m
 CONFIG_IP_NF_NAT=m
 CONFIG_IP_NF_NAT_NEEDED=y
 CONFIG_IP_NF_TARGET_MASQUERADE=m
 CONFIG_IP_NF_TARGET_REDIRECT=m
 CONFIG_IP_NF_TARGET_NETMAP=m
 CONFIG_IP_NF_TARGET_SAME=m
-CONFIG_IP_NF_TARGET_NOTRACK=m
 CONFIG_IP_NF_NAT_LOCAL=y
 CONFIG_IP_NF_NAT_SNMP_BASIC=m
 CONFIG_IP_NF_NAT_IRC=m
@@ -631,14 +806,11 @@ CONFIG_IP_NF_TARGET_ECN=m
 CONFIG_IP_NF_TARGET_DSCP=m
 CONFIG_IP_NF_TARGET_MARK=m
 CONFIG_IP_NF_TARGET_CLASSIFY=m
-CONFIG_IP_NF_TARGET_LOG=m
-CONFIG_IP_NF_TARGET_ULOG=m
-CONFIG_IP_NF_TARGET_TCPMSS=m
+CONFIG_IP_NF_RAW=m
+CONFIG_IP_NF_TARGET_NOTRACK=m
 CONFIG_IP_NF_ARPTABLES=m
 CONFIG_IP_NF_ARPFILTER=m
 CONFIG_IP_NF_ARP_MANGLE=m
-CONFIG_IP_NF_CT_ACCT=y
-CONFIG_IP_NF_CT_PROTO_SCTP=m
 # CONFIG_IP_NF_COMPAT_IPCHAINS is not set
 # CONFIG_IP_NF_COMPAT_IPFWADM is not set
 
@@ -667,7 +839,6 @@ CONFIG_IP6_NF_MANGLE=m
 CONFIG_IP6_NF_TARGET_MARK=m
 CONFIG_IP6_NF_RAW=m
 
-
 #
 # Bridge: Netfilter Configuration
 #
@@ -703,33 +874,36 @@ CONFIG_IP_SCTP=m
 # CONFIG_SCTP_HMAC_SHA1 is not set
 CONFIG_SCTP_HMAC_MD5=y
 CONFIG_ATM=m
+CONFIG_ATM_CLIP=m
+# CONFIG_ATM_CLIP_NO_ICMP is not set
+CONFIG_ATM_LANE=m
+# CONFIG_ATM_MPOA is not set
+CONFIG_ATM_BR2684=m
+# CONFIG_ATM_BR2684_IPFILTER is not set
+CONFIG_BRIDGE=m
 CONFIG_VLAN_8021Q=m
-CONFIG_LLC=m
+# CONFIG_DECNET is not set
+CONFIG_LLC=y
 # CONFIG_LLC2 is not set
 # CONFIG_IPX is not set
-# CONFIG_IPX_INTERN is not set
 # CONFIG_ATALK is not set
-# CONFIG_DEV_APPLETALK is not set
-# CONFIG_LTPC is not set
-# CONFIG_COPS is not set
-CONFIG_COPS_DAYNA=y
-CONFIG_COPS_TANGENT=y
-# CONFIG_IPDDP is not set
-CONFIG_IPDDP_ENCAP=y
-CONFIG_IPDDP_DECAP=y
 # CONFIG_X25 is not set
 # CONFIG_LAPB is not set
 CONFIG_NET_DIVERT=y
 # CONFIG_ECONET is not set
 # CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_HW_FLOWCONTROL is not set
 
 #
 # QoS and/or fair queueing
 #
 CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_CLK_JIFFIES=y
+# CONFIG_NET_SCH_CLK_GETTIMEOFDAY is not set
+# CONFIG_NET_SCH_CLK_CPU is not set
 CONFIG_NET_SCH_CBQ=m
 CONFIG_NET_SCH_HTB=m
+CONFIG_NET_SCH_HFSC=m
+CONFIG_NET_SCH_ATM=m
 CONFIG_NET_SCH_PRIO=m
 CONFIG_NET_SCH_RED=m
 CONFIG_NET_SCH_SFQ=m
@@ -737,9 +911,8 @@ CONFIG_NET_SCH_TEQL=m
 CONFIG_NET_SCH_TBF=m
 CONFIG_NET_SCH_GRED=m
 CONFIG_NET_SCH_DSMARK=m
-CONFIG_NET_SCH_INGRESS=m
-CONFIG_NET_SCH_HFSC=m
 CONFIG_NET_SCH_NETEM=m
+CONFIG_NET_SCH_INGRESS=m
 CONFIG_NET_QOS=y
 CONFIG_NET_ESTIMATOR=y
 CONFIG_NET_CLS=y
@@ -748,24 +921,59 @@ CONFIG_NET_CLS_ROUTE4=m
 CONFIG_NET_CLS_ROUTE=y
 CONFIG_NET_CLS_FW=m
 CONFIG_NET_CLS_U32=m
-CONFIG_NET_CLS_RSVP=m
-CONFIG_NET_CLS_RSVP6=m
-CONFIG_NET_CLS_POLICE=y
-# CONFIG_NET_ACT_POLICE is not set
 CONFIG_CLS_U32_PERF=y
 CONFIG_NET_CLS_IND=y
+CONFIG_NET_CLS_RSVP=m
+CONFIG_NET_CLS_RSVP6=m
 # CONFIG_NET_CLS_ACT is not set
+CONFIG_NET_CLS_POLICE=y
 
 #
 # Network testing
 #
 # CONFIG_NET_PKTGEN is not set
-CONFIG_NETDEVICES=y
+CONFIG_NETPOLL=y
+# CONFIG_NETPOLL_RX is not set
+CONFIG_NETPOLL_TRAP=y
+CONFIG_NET_POLL_CONTROLLER=y
+# CONFIG_HAMRADIO is not set
+# CONFIG_IRDA is not set
+CONFIG_BT=m
+CONFIG_BT_L2CAP=m
+CONFIG_BT_SCO=m
+CONFIG_BT_RFCOMM=m
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=m
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+CONFIG_BT_CMTP=m
+CONFIG_BT_HIDP=m
 
 #
-# ARCnet devices
+# Bluetooth device drivers
 #
-# CONFIG_ARCNET is not set
+CONFIG_BT_HCIUSB=m
+CONFIG_BT_HCIUSB_SCO=y
+CONFIG_BT_HCIUART=m
+CONFIG_BT_HCIUART_H4=y
+CONFIG_BT_HCIUART_BCSP=y
+CONFIG_BT_HCIUART_BCSP_TXCRC=y
+CONFIG_BT_HCIBCM203X=m
+CONFIG_BT_HCIBFUSB=m
+CONFIG_BT_HCIDTL1=m
+CONFIG_BT_HCIBT3C=m
+CONFIG_BT_HCIBLUECARD=m
+CONFIG_BT_HCIBTUART=m
+CONFIG_BT_HCIVHCI=m
+CONFIG_TUX=m
+
+#
+# TUX options
+#
+CONFIG_TUX_EXTCGI=y
+# CONFIG_TUX_EXTENDED_LOG is not set
+# CONFIG_TUX_DEBUG is not set
+CONFIG_NETDEVICES=y
 CONFIG_DUMMY=m
 CONFIG_BONDING=m
 # CONFIG_EQUALIZER is not set
@@ -774,46 +982,9 @@ CONFIG_ETHERTAP=m
 # CONFIG_NET_SB1000 is not set
 
 #
-# ATM
-# 
-CONFIG_ATM_CLIP=m
-CONFIG_ATM_LANE=m
-CONFIG_ATM_BR2684=m
-CONFIG_NET_SCH_ATM=m
-CONFIG_ATM_TCP=m
-CONFIG_ATM_LANAI=m
-CONFIG_ATM_ENI=m
-CONFIG_ATM_FIRESTREAM=m
-# CONFIG_ATM_ZATM is not set
-CONFIG_ATM_IDT77252=m
-CONFIG_ATM_AMBASSADOR=m
-CONFIG_ATM_HORIZON=m
-CONFIG_ATM_FORE200E_MAYBE=m
-CONFIG_ATM_HE=m
-CONFIG_PPPOATM=m
-CONFIG_ATM_NICSTAR=m
-# CONFIG_ATM_IA is not set
-
-
-# CONFIG_ATM_CLIP_NO_ICMP is not set
-# CONFIG_ATM_MPOA is not set
-# CONFIG_ATM_BR2684_IPFILTER is not set
-# CONFIG_ATM_ENI_DEBUG is not set
-# CONFIG_ATM_ENI_TUNE_BURST is not set
-# CONFIG_ATM_ZATM_DEBUG is not set
-# CONFIG_ATM_IDT77252_DEBUG is not set
-# CONFIG_ATM_IDT77252_RCV_ALL is not set
-# CONFIG_ATM_AMBASSADOR_DEBUG is not set
-# CONFIG_ATM_HORIZON_DEBUG is not set
-# CONFIG_ATM_FORE200E_PCA is not set
-# CONFIG_ATM_HE_USE_SUNI is not set
-# CONFIG_ATM_NICSTAR_USE_SUNI is not set
-# CONFIG_ATM_NICSTAR_USE_IDT77105 is not set
-# CONFIG_ATM_IA_DEBUG is not set
-
-
-
-
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
 
 #
 # Ethernet (10 or 100Mbit)
@@ -826,7 +997,6 @@ CONFIG_NET_VENDOR_3COM=y
 # CONFIG_EL1 is not set
 # CONFIG_EL2 is not set
 # CONFIG_ELPLUS is not set
-# CONFIG_ELPLUS is not set
 # CONFIG_EL16 is not set
 # CONFIG_EL3 is not set
 # CONFIG_3C515 is not set
@@ -847,12 +1017,9 @@ CONFIG_NET_VENDOR_RACAL=y
 CONFIG_NET_TULIP=y
 CONFIG_DE2104X=m
 CONFIG_TULIP=m
-# CONFIG_TULIP_NAPI is not set
-
 # CONFIG_TULIP_MWI is not set
 CONFIG_TULIP_MMIO=y
-# CONFIG_NI5010 is not set
-# CONFIG_PCMCIA_XIRTULIP is not set
+# CONFIG_TULIP_NAPI is not set
 CONFIG_DE4X5=m
 CONFIG_WINBOND_840=m
 CONFIG_DM9102=m
@@ -861,20 +1028,6 @@ CONFIG_PCMCIA_XIRCOM=m
 # CONFIG_DEPCA is not set
 CONFIG_HP100=m
 # CONFIG_NET_ISA is not set
-CONFIG_EWRK3=m
-CONFIG_E2100=m
-CONFIG_EEXPRESS=m
-CONFIG_EEXPRESS_PRO=m
-CONFIG_HPLAN_PLUS=m
-CONFIG_HPLAN=m
-CONFIG_LP486E=m
-CONFIG_ETH16I=m
-CONFIG_NE2000=m
-CONFIG_ZNET=m
-CONFIG_SEEQ8005=m
-CONFIG_LNE390=m
-CONFIG_NE3210=m
-CONFIG_ES3210=m
 CONFIG_NET_PCI=y
 CONFIG_PCNET32=m
 CONFIG_AMD8111_ETH=m
@@ -884,6 +1037,7 @@ CONFIG_ADAPTEC_STARFIRE_NAPI=y
 # CONFIG_AC3200 is not set
 CONFIG_APRICOT=m
 CONFIG_B44=m
+CONFIG_FORCEDETH=m
 # CONFIG_CS89x0 is not set
 # CONFIG_DGRS is not set
 CONFIG_EEPRO100=m
@@ -891,7 +1045,6 @@ CONFIG_EEPRO100=m
 CONFIG_E100=m
 CONFIG_E100_NAPI=y
 CONFIG_FEALNX=m
-CONFIG_FORCEDETH=m
 CONFIG_NATSEMI=m
 CONFIG_NE2K_PCI=m
 CONFIG_8139CP=m
@@ -903,11 +1056,9 @@ CONFIG_8139TOO_8129=y
 CONFIG_SIS900=m
 CONFIG_EPIC100=m
 # CONFIG_SUNDANCE is not set
-# CONFIG_SUNDANCE_MMIO is not set
 CONFIG_TLAN=m
 CONFIG_VIA_RHINE=m
 CONFIG_VIA_RHINE_MMIO=y
-CONFIG_VIA_VELOCITY=m
 CONFIG_NET_POCKET=y
 # CONFIG_ATP is not set
 # CONFIG_DE600 is not set
@@ -927,6 +1078,7 @@ CONFIG_NS83820=m
 CONFIG_R8169=m
 CONFIG_R8169_NAPI=y
 CONFIG_SK98LIN=m
+CONFIG_VIA_VELOCITY=m
 CONFIG_TIGON3=m
 
 #
@@ -936,21 +1088,21 @@ CONFIG_IXGB=m
 CONFIG_IXGB_NAPI=y
 CONFIG_S2IO=m
 CONFIG_S2IO_NAPI=y
-CONFIG_FDDI=y
-# CONFIG_DEFXX is not set
-# CONFIG_SKFP is not set
-# CONFIG_HIPPI is not set
-# CONFIG_PLIP is not set
-CONFIG_PPP=m
-CONFIG_PPP_MULTILINK=y
-CONFIG_PPP_FILTER=y
-CONFIG_PPP_ASYNC=m
-CONFIG_PPP_SYNC_TTY=m
-CONFIG_PPP_DEFLATE=m
-CONFIG_IPPP_FILTER=y
-# CONFIG_PPP_BSDCOMP is not set
-CONFIG_PPPOE=m
-# CONFIG_SLIP is not set
+
+#
+# Token Ring devices
+#
+CONFIG_TR=y
+CONFIG_IBMTR=m
+CONFIG_IBMOL=m
+CONFIG_IBMLS=m
+CONFIG_3C359=m
+CONFIG_TMS380TR=m
+CONFIG_TMSPCI=m
+CONFIG_SKISA=m
+CONFIG_PROTEON=m
+CONFIG_ABYSS=m
+CONFIG_SMCTR=m
 
 #
 # Wireless LAN (non-hamradio)
@@ -992,7 +1144,6 @@ CONFIG_TMD_HERMES=m
 CONFIG_PCI_HERMES=m
 CONFIG_ATMEL=m
 CONFIG_PCI_ATMEL=m
-CONFIG_PRISM54=m
 
 #
 # Wireless 802.11b Pcmcia/Cardbus cards support
@@ -1001,32 +1152,12 @@ CONFIG_PCMCIA_HERMES=m
 CONFIG_AIRO_CS=m
 CONFIG_PCMCIA_ATMEL=m
 CONFIG_PCMCIA_WL3501=m
-CONFIG_NET_WIRELESS=y
-
-#
-# Token Ring devices
-#
-CONFIG_TR=y
-CONFIG_IBMOL=m
-CONFIG_3C359=m
-CONFIG_TMS380TR=m
-CONFIG_TMSPCI=m
-CONFIG_ABYSS=m
-CONFIG_IBMTR=m
-CONFIG_IBMLS=m
-CONFIG_SKISA=m
-CONFIG_PROTEON=m
-CONFIG_SMCTR=m
-CONFIG_PCMCIA_IBMTR=m
-
-
-CONFIG_NET_FC=y
-# CONFIG_SHAPER is not set
 
 #
-# Wan interfaces
+# Prism GT/Duette 802.11(a/b/g) PCI/Cardbus support
 #
-# CONFIG_WAN is not set
+CONFIG_PRISM54=m
+CONFIG_NET_WIRELESS=y
 
 #
 # PCMCIA network device support
@@ -1040,119 +1171,104 @@ CONFIG_PCMCIA_NMCLAN=m
 CONFIG_PCMCIA_SMC91C92=m
 CONFIG_PCMCIA_XIRC2PS=m
 CONFIG_PCMCIA_AXNET=m
+CONFIG_PCMCIA_IBMTR=m
 
 #
-# Amateur Radio support
-#
-# CONFIG_HAMRADIO is not set
-
-#
-# IrDA (infrared) support
-#
-# CONFIG_IRDA is not set
-# CONFIG_IRDA is not set
-# CONFIG_IRDA_DEBUG is not set
-CONFIG_IRLAN=m
-CONFIG_IRNET=m
-CONFIG_IRCOMM=m
-# CONFIG_IRDA_ULTRA is not set
-CONFIG_IRDA_CACHE_LAST_LSAP=y
-CONFIG_IRDA_FAST_RR=y
-CONFIG_IRTTY_SIR=m
-CONFIG_DONGLE=y
-CONFIG_ESI_DONGLE=m
-CONFIG_ACTISYS_DONGLE=m
-CONFIG_TEKRAM_DONGLE=m
-CONFIG_IRPORT_SIR=m
-# CONFIG_DONGLE_OLD is not set
-CONFIG_LITELINK_DONGLE=m
-CONFIG_MA600_DONGLE=m
-CONFIG_GIRBIL_DONGLE=m
-CONFIG_MCP2120_DONGLE=m
-CONFIG_OLD_BELKIN_DONGLE=m
-CONFIG_ACT200L_DONGLE=m
-
-CONFIG_USB_IRDA=m
-CONFIG_NSC_FIR=m
-CONFIG_SIGMATEL_FIR=m
-# CONFIG_WINBOND_FIR is not set
-# CONFIG_TOSHIBA_FIR is not set
-# CONFIG_SMC_IRCC_FIR is not set
-# CONFIG_ALI_FIR is not set
-# CONFIG_VLSI_FIR is not set
-# CONFIG_VIA_FIR is not set
-
-
-
-#
-# Bluetooth support
+# Wan interfaces
 #
-CONFIG_BT=m
-CONFIG_BT_L2CAP=m
-CONFIG_BT_SCO=m
-CONFIG_BT_CMTP=m
-CONFIG_BT_RFCOMM=m
-CONFIG_BT_RFCOMM_TTY=y
-CONFIG_BT_BNEP=m
-CONFIG_BT_BNEP_MC_FILTER=y
-CONFIG_BT_BNEP_PROTO_FILTER=y
-CONFIG_BT_HIDP=m
+# CONFIG_WAN is not set
 
 #
-# Bluetooth device drivers
+# ATM drivers
 #
-CONFIG_BT_HCIUSB=m
-CONFIG_BT_HCIUART=m
-CONFIG_BT_HCIUART_H4=y
-CONFIG_BT_HCIUART_BCSP=y
-CONFIG_BT_HCIUART_BCSP_TXCRC=y
-CONFIG_BT_HCIDTL1=m
-CONFIG_BT_HCIBT3C=m
-CONFIG_BT_HCIBLUECARD=m
-CONFIG_BT_HCIBTUART=m
-CONFIG_BT_HCIVHCI=m
-CONFIG_BT_HCIUSB_SCO=y
-CONFIG_BT_HCIBCM203X=m
-CONFIG_BT_HCIBFUSB=m
-CONFIG_USB_BLUETOOTH_TTY=m
-
+CONFIG_ATM_TCP=m
+CONFIG_ATM_LANAI=m
+CONFIG_ATM_ENI=m
+# CONFIG_ATM_ENI_DEBUG is not set
+# CONFIG_ATM_ENI_TUNE_BURST is not set
+CONFIG_ATM_FIRESTREAM=m
+# CONFIG_ATM_ZATM is not set
+CONFIG_ATM_NICSTAR=m
+# CONFIG_ATM_NICSTAR_USE_SUNI is not set
+# CONFIG_ATM_NICSTAR_USE_IDT77105 is not set
+CONFIG_ATM_IDT77252=m
+# CONFIG_ATM_IDT77252_DEBUG is not set
+# CONFIG_ATM_IDT77252_RCV_ALL is not set
+CONFIG_ATM_IDT77252_USE_SUNI=y
+CONFIG_ATM_AMBASSADOR=m
+# CONFIG_ATM_AMBASSADOR_DEBUG is not set
+CONFIG_ATM_HORIZON=m
+# CONFIG_ATM_HORIZON_DEBUG is not set
+# CONFIG_ATM_IA is not set
+CONFIG_ATM_FORE200E_MAYBE=m
+# CONFIG_ATM_FORE200E_PCA is not set
+CONFIG_ATM_HE=m
+# CONFIG_ATM_HE_USE_SUNI is not set
+CONFIG_FDDI=y
+# CONFIG_DEFXX is not set
+# CONFIG_SKFP is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PLIP is not set
+CONFIG_PPP=m
+CONFIG_PPP_MULTILINK=y
+CONFIG_PPP_FILTER=y
+CONFIG_PPP_ASYNC=m
+CONFIG_PPP_SYNC_TTY=m
+CONFIG_PPP_DEFLATE=m
+# CONFIG_PPP_BSDCOMP is not set
+CONFIG_PPPOE=m
+CONFIG_PPPOATM=m
+# CONFIG_SLIP is not set
+CONFIG_NET_FC=y
+# CONFIG_SHAPER is not set
+CONFIG_NETCONSOLE=m
+CONFIG_NETDUMP=m
+
 #
 # ISDN subsystem
 #
-
 CONFIG_ISDN=m
-CONFIG_ISDN_I4L=m
-CONFIG_ISDN_DRV_AVMB1_B1ISA=m
-CONFIG_ISDN_DRV_AVMB1_B1PCI=m
-CONFIG_ISDN_DRV_AVMB1_T1ISA=m
-CONFIG_ISDN_DRV_AVMB1_B1PCMCIA=m
-CONFIG_ISDN_DRV_AVMB1_T1PCI=m
-CONFIG_ISDN_DRV_AVMB1_C4=m
 
+#
+# Old ISDN4Linux
+#
+CONFIG_ISDN_I4L=m
 CONFIG_ISDN_PPP=y
 CONFIG_ISDN_PPP_VJ=y
 CONFIG_ISDN_MPP=y
+CONFIG_IPPP_FILTER=y
 # CONFIG_ISDN_PPP_BSDCOMP is not set
+CONFIG_ISDN_AUDIO=y
 CONFIG_ISDN_TTY_FAX=y
-CONFIG_DE_AOC=y
 
-CONFIG_ISDN_AUDIO=y
+#
+# ISDN feature submodules
+#
 
-CONFIG_ISDN_DRV_HISAX=m
-CONFIG_ISDN_DRV_ICN=m
-CONFIG_ISDN_DRV_PCBIT=m
-CONFIG_ISDN_DRV_SC=m
-CONFIG_ISDN_DRV_ACT2000=m
-CONFIG_ISDN_DRV_TPAM=m
-CONFIG_ISDN_DRV_AVMB1_B1PCIV4=y
-CONFIG_ISDN_DRV_AVMB1_AVM_CS=m
+#
+# ISDN4Linux hardware drivers
+#
 
-CONFIG_ISDN_CAPI_CAPIDRV=m
+#
+# Passive cards
+#
+CONFIG_ISDN_DRV_HISAX=m
 
+#
+# D-channel protocol features
+#
 CONFIG_HISAX_EURO=y
+CONFIG_DE_AOC=y
+CONFIG_HISAX_NO_SENDCOMPLETE=y
+CONFIG_HISAX_NO_LLC=y
+CONFIG_HISAX_NO_KEYPAD=y
 CONFIG_HISAX_1TR6=y
 CONFIG_HISAX_NI1=y
 CONFIG_HISAX_MAX_CARDS=8
+
+#
+# HiSax supported cards
+#
 CONFIG_HISAX_16_0=y
 CONFIG_HISAX_16_3=y
 CONFIG_HISAX_TELESPCI=y
@@ -1182,21 +1298,31 @@ CONFIG_HISAX_W6692=y
 CONFIG_HISAX_HFC_SX=y
 CONFIG_HISAX_ENTERNOW_PCI=y
 # CONFIG_HISAX_DEBUG is not set
-CONFIG_HISAX_AVM_A1_CS=m
-CONFIG_HISAX_ST5481=m
-CONFIG_HISAX_HFCUSB=m
-CONFIG_HISAX_FRITZ_PCIPNP=m
-CONFIG_HISAX_NO_SENDCOMPLETE=y
-CONFIG_HISAX_NO_LLC=y
-CONFIG_HISAX_NO_KEYPAD=y
+
+#
+# HiSax PCMCIA card service modules
+#
 CONFIG_HISAX_SEDLBAUER_CS=m
 CONFIG_HISAX_ELSA_CS=m
+CONFIG_HISAX_AVM_A1_CS=m
 CONFIG_HISAX_TELES_CS=m
 
-CONFIG_ISDN_DRV_LOOP=m
-CONFIG_HYSDN=m
-CONFIG_HYSDN_CAPI=y
+#
+# HiSax sub driver modules
+#
+CONFIG_HISAX_ST5481=m
+CONFIG_HISAX_HFCUSB=m
+CONFIG_HISAX_FRITZ_PCIPNP=m
+CONFIG_HISAX_HDLC=y
 
+#
+# Active cards
+#
+CONFIG_ISDN_DRV_ICN=m
+CONFIG_ISDN_DRV_PCBIT=m
+CONFIG_ISDN_DRV_SC=m
+CONFIG_ISDN_DRV_ACT2000=m
+CONFIG_ISDN_DRV_TPAM=m
 
 #
 # CAPI subsystem
@@ -1207,6 +1333,7 @@ CONFIG_ISDN_CAPI_MIDDLEWARE=y
 CONFIG_ISDN_CAPI_CAPI20=m
 CONFIG_ISDN_CAPI_CAPIFS_BOOL=y
 CONFIG_ISDN_CAPI_CAPIFS=m
+CONFIG_ISDN_CAPI_CAPIDRV=m
 
 #
 # CAPI hardware drivers
@@ -1254,21 +1381,14 @@ CONFIG_INPUT_EVDEV=y
 # Input I/O drivers
 #
 # CONFIG_GAMEPORT is not set
-CONFIG_SOUND_GAMEPORT=m
-CONFIG_GAMEPORT_NS558=m
-CONFIG_GAMEPORT_L4=m
-CONFIG_GAMEPORT_EMU10K1=m
-CONFIG_GAMEPORT_VORTEX=m
-CONFIG_GAMEPORT_FM801=m
-CONFIG_GAMEPORT_CS461x=m
+CONFIG_SOUND_GAMEPORT=y
 CONFIG_SERIO=y
 CONFIG_SERIO_I8042=y
 CONFIG_SERIO_SERPORT=y
-# CONFIG_SERIO_RAW is not set
-
 # CONFIG_SERIO_CT82C710 is not set
 # CONFIG_SERIO_PARKBD is not set
 # CONFIG_SERIO_PCIPS2 is not set
+# CONFIG_SERIO_RAW is not set
 
 #
 # Input Device Drivers
@@ -1276,9 +1396,9 @@ CONFIG_SERIO_SERPORT=y
 CONFIG_INPUT_KEYBOARD=y
 CONFIG_KEYBOARD_ATKBD=y
 # CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_LKKBD is not set
 # CONFIG_KEYBOARD_XTKBD is not set
 # CONFIG_KEYBOARD_NEWTON is not set
-# CONFIG_KEYBOARD_LKKBD is not set
 CONFIG_INPUT_MOUSE=y
 CONFIG_MOUSE_PS2=y
 CONFIG_MOUSE_SERIAL=m
@@ -1288,20 +1408,7 @@ CONFIG_MOUSE_LOGIBM=m
 CONFIG_MOUSE_PC110PAD=m
 CONFIG_MOUSE_VSXXXAA=m
 CONFIG_INPUT_JOYSTICK=y
-# CONFIG_JOYSTICK_ANALOG is not set
-# CONFIG_JOYSTICK_A3D is not set
-# CONFIG_JOYSTICK_ADI is not set
-# CONFIG_JOYSTICK_COBRA is not set
-# CONFIG_JOYSTICK_GF2K is not set
-# CONFIG_JOYSTICK_GRIP is not set
-# CONFIG_JOYSTICK_GRIP_MP is not set
-# CONFIG_JOYSTICK_GUILLEMOT is not set
-# CONFIG_JOYSTICK_INTERACT is not set
-# CONFIG_JOYSTICK_SIDEWINDER is not set
-# CONFIG_JOYSTICK_TMDC is not set
 # CONFIG_JOYSTICK_IFORCE is not set
-# CONFIG_JOYSTICK_IFORCE_USB=y
-# CONFIG_JOYSTICK_IFORCE_232=y
 # CONFIG_JOYSTICK_WARRIOR is not set
 # CONFIG_JOYSTICK_MAGELLAN is not set
 # CONFIG_JOYSTICK_SPACEORB is not set
@@ -1311,12 +1418,11 @@ CONFIG_INPUT_JOYSTICK=y
 # CONFIG_JOYSTICK_DB9 is not set
 # CONFIG_JOYSTICK_GAMECON is not set
 # CONFIG_JOYSTICK_TURBOGRAFX is not set
-CONFIG_JOYSTICK_JOYDUMP=m
 CONFIG_INPUT_TOUCHSCREEN=y
 CONFIG_TOUCHSCREEN_GUNZE=m
 CONFIG_INPUT_MISC=y
 CONFIG_INPUT_PCSPKR=m
-CONFIG_INPUT_UINPUT=m 
+CONFIG_INPUT_UINPUT=m
 
 #
 # Character devices
@@ -1326,12 +1432,11 @@ CONFIG_VT_CONSOLE=y
 CONFIG_HW_CONSOLE=y
 CONFIG_SERIAL_NONSTANDARD=y
 # CONFIG_ROCKETPORT is not set
+# CONFIG_CYCLADES is not set
 CONFIG_SYNCLINK=m
 CONFIG_SYNCLINKMP=m
 CONFIG_N_HDLC=m
 CONFIG_STALDRV=y
-# CONFIG_FTAPE is not set
-CONFIG_IBM_ASM=m
 
 #
 # Serial drivers
@@ -1347,20 +1452,6 @@ CONFIG_SERIAL_8250_SHARE_IRQ=y
 CONFIG_SERIAL_8250_DETECT_IRQ=y
 CONFIG_SERIAL_8250_MULTIPORT=y
 CONFIG_SERIAL_8250_RSA=y
-# CONFIG_COMPUTONE is not set
-# CONFIG_CYCLADES is not set
-# CONFIG_DIGIEPCA is not set
-# CONFIG_DIGI is not set
-# CONFIG_ESPSERIAL is not set
-# CONFIG_MOXA_INTELLIO is not set
-# CONFIG_MOXA_SMARTIO is not set
-# CONFIG_ISI is not set
-# CONFIG_RISCOM8 is not set
-# CONFIG_SPECIALIX is not set
-# CONFIG_SX is not set
-# CONFIG_RIO is not set
-# CONFIG_STALLION is not set
-# CONFIG_ISTALLION is not set
 
 #
 # Non-8250 serial port support
@@ -1369,104 +1460,20 @@ CONFIG_SERIAL_CORE=y
 CONFIG_SERIAL_CORE_CONSOLE=y
 CONFIG_UNIX98_PTYS=y
 # CONFIG_LEGACY_PTYS is not set
+CONFIG_CRASH=m
 CONFIG_PRINTER=m
 CONFIG_LP_CONSOLE=y
 CONFIG_PPDEV=m
 # CONFIG_TIPAR is not set
 
 #
-# I2C support
-#
-CONFIG_I2C=m
-CONFIG_I2C_CHARDEV=m
-
-#
-# I2C Algorithms
-#
-# CONFIG_I2C_DEBUG_ALGO is not set
-CONFIG_I2C_ALGOBIT=m
-CONFIG_I2C_ALGOPCF=m
-
-#
-# I2C Hardware Bus support
-#
-CONFIG_I2C_ALI1535=m
-CONFIG_I2C_ALI15X3=m
-CONFIG_I2C_AMD756=m
-CONFIG_I2C_AMD8111=m
-CONFIG_I2C_I801=m
-CONFIG_I2C_I810=m
-CONFIG_I2C_ISA=m
-CONFIG_I2C_NFORCE2=m
-CONFIG_I2C_PIIX4=m
-CONFIG_I2C_PROSAVAGE=m
-CONFIG_I2C_SAVAGE4=m
-# CONFIG_SCx200_ACB is not set
-CONFIG_I2C_SIS5595=m
-CONFIG_I2C_SIS630=m
-CONFIG_I2C_SIS96X=m
-CONFIG_I2C_VIA=m
-CONFIG_I2C_VIAPRO=m
-CONFIG_I2C_VOODOO3=m
-# CONFIG_I2C_ELEKTOR is not set
-CONFIG_I2C_PARPORT=m
-CONFIG_I2C_PARPORT_LIGHT=m
-# CONFIG_I2C_DEBUG_CORE is not set
-# CONFIG_I2C_DEBUG_BUS is not set
-# CONFIG_I2C_DEBUG_CHIP is not set
-# CONFIG_I2C_PARPORT is not set
-CONFIG_I2C_ALI1563=m
-# CONFIG_I2C_PARPORT_LIGHT is not set
-CONFIG_I2C_ALGOPCA=m
-# CONFIG_I2C_PCA_ISA is not set
-
-
-#
-# I2C Hardware Sensors Chip support
-#
-CONFIG_I2C_SENSOR=m
-CONFIG_SENSORS_ADM1021=m
-CONFIG_SENSORS_EEPROM=m
-CONFIG_SENSORS_IT87=m
-CONFIG_SENSORS_LM75=m
-CONFIG_SENSORS_LM78=m
-CONFIG_SENSORS_LM80=m
-CONFIG_SENSORS_LM83=m
-CONFIG_SENSORS_LM85=m
-CONFIG_SENSORS_VIA686A=m
-CONFIG_SENSORS_W83781D=m
-CONFIG_SENSORS_ASB100=m
-CONFIG_SENSORS_LM90=m
-CONFIG_SENSORS_W83L785TS=m
-CONFIG_SENSORS_FSCHER=m
-CONFIG_SENSORS_GL518SM=m
-CONFIG_SENSORS_DS1621=m
-CONFIG_SENSORS_W83627HF=m
-CONFIG_SENSORS_PCF8574=m
-CONFIG_SENSORS_PCF8591=m
-CONFIG_SENSORS_RTC8564=m
-CONFIG_SENSORS_MAX1619=m
-CONFIG_SENSORS_ADM1025=m
-CONFIG_SENSORS_ADM1031=m
-CONFIG_SENSORS_LM77=m
-CONFIG_SENSORS_SMSC47M1=m
-
-# CONFIG_W1 is not set
-
-#
-# Mice
-#
-CONFIG_CRASH=m
-
-#
 # IPMI
 #
 CONFIG_IPMI_HANDLER=m
 # CONFIG_IPMI_PANIC_EVENT is not set
 CONFIG_IPMI_DEVICE_INTERFACE=m
-CONFIG_IPMI_WATCHDOG=m
 CONFIG_IPMI_SI=m
+CONFIG_IPMI_WATCHDOG=m
 CONFIG_IPMI_POWEROFF=m
 
 #
@@ -1474,33 +1481,47 @@ CONFIG_IPMI_POWEROFF=m
 #
 CONFIG_WATCHDOG=y
 # CONFIG_WATCHDOG_NOWAYOUT is not set
+
+#
+# Watchdog Device Drivers
+#
 CONFIG_SOFT_WATCHDOG=m
-CONFIG_WDT=m
-# CONFIG_WDT_501 is not set
-CONFIG_WDTPCI=m
-CONFIG_WDT_501_PCI=y
-CONFIG_PCWATCHDOG=m
 CONFIG_ACQUIRE_WDT=m
 CONFIG_ADVANTECH_WDT=m
+CONFIG_ALIM1535_WDT=m
+CONFIG_ALIM7101_WDT=m
+CONFIG_SC520_WDT=m
 CONFIG_EUROTECH_WDT=m
 CONFIG_IB700_WDT=m
+CONFIG_WAFER_WDT=m
 CONFIG_I8XX_TCO=m
-# CONFIG_MIXCOMWD is not set
+CONFIG_SC1200_WDT=m
 # CONFIG_SCx200_WDT is not set
 # CONFIG_60XX_WDT is not set
-CONFIG_W83877F_WDT=m
+CONFIG_CPU5_WDT=m
 CONFIG_W83627HF_WDT=m
+CONFIG_W83877F_WDT=m
 CONFIG_MACHZ_WDT=m
-CONFIG_SC520_WDT=m
-CONFIG_ALIM7101_WDT=m
-CONFIG_ALIM1535_WDT=m
-CONFIG_SC1200_WDT=m
-CONFIG_WAFER_WDT=m
-CONFIG_CPU5_WDT=m
-CONFIG_PCIPCWATCHDOG=m
-CONFIG_USBPCWATCHDOG=m
 
+#
+# ISA-based Watchdog Cards
+#
+CONFIG_PCWATCHDOG=m
+# CONFIG_MIXCOMWD is not set
+CONFIG_WDT=m
+# CONFIG_WDT_501 is not set
+
+#
+# PCI-based Watchdog Cards
+#
+CONFIG_PCIPCWATCHDOG=m
+CONFIG_WDTPCI=m
+CONFIG_WDT_501_PCI=y
 
+#
+# USB-based Watchdog Cards
+#
+CONFIG_USBPCWATCHDOG=m
 CONFIG_HW_RANDOM=m
 CONFIG_NVRAM=m
 CONFIG_RTC=y
@@ -1526,70 +1547,142 @@ CONFIG_AGP_VIA=y
 CONFIG_AGP_EFFICEON=y
 CONFIG_DRM=y
 # CONFIG_DRM_TDFX is not set
-# CONFIG_DRM_GAMMA is not set
 CONFIG_DRM_R128=m
 CONFIG_DRM_RADEON=m
 CONFIG_DRM_I810=m
 CONFIG_DRM_I830=m
+CONFIG_DRM_I915=m
 CONFIG_DRM_MGA=m
 # CONFIG_DRM_SIS is not set
-CONFIG_DRM_I915=m
-
-
 
 #
 # PCMCIA character devices
 #
 CONFIG_SYNCLINK_CS=m
-
-
-
 # CONFIG_MWAVE is not set
 CONFIG_RAW_DRIVER=y
+# CONFIG_HPET is not set
 CONFIG_MAX_RAW_DEVS=8192
 CONFIG_HANGCHECK_TIMER=m
 
 #
-# Multimedia devices
+# I2C support
 #
-# CONFIG_VIDEO_DEV is not set
+CONFIG_I2C=m
+CONFIG_I2C_CHARDEV=m
 
 #
-# Video For Linux
+# I2C Algorithms
 #
+CONFIG_I2C_ALGOBIT=m
+CONFIG_I2C_ALGOPCF=m
+CONFIG_I2C_ALGOPCA=m
 
 #
-# Video Adapters
+# I2C Hardware Bus support
 #
-# CONFIG_VIDEO_BT848 is not set
-# CONFIG_VIDEO_PMS is not set
-# CONFIG_VIDEO_BWQCAM is not set
-# CONFIG_VIDEO_CQCAM is not set
-# CONFIG_VIDEO_W9966 is not set
-# CONFIG_VIDEO_CPIA is not set
-# CONFIG_VIDEO_CPIA_PP is not set
-# CONFIG_VIDEO_CPIA_USB is not set
-# CONFIG_VIDEO_SAA5249 is not set
-# CONFIG_TUNER_3036 is not set
-# CONFIG_VIDEO_STRADIS is not set
-# CONFIG_VIDEO_ZORAN is not set
+CONFIG_I2C_ALI1535=m
+CONFIG_I2C_ALI1563=m
+CONFIG_I2C_ALI15X3=m
+CONFIG_I2C_AMD756=m
+CONFIG_I2C_AMD8111=m
+CONFIG_I2C_I801=m
+CONFIG_I2C_I810=m
+CONFIG_I2C_ISA=m
+CONFIG_I2C_NFORCE2=m
+# CONFIG_I2C_PARPORT is not set
+# CONFIG_I2C_PARPORT_LIGHT is not set
+CONFIG_I2C_PIIX4=m
+CONFIG_I2C_PROSAVAGE=m
+CONFIG_I2C_SAVAGE4=m
+# CONFIG_SCx200_ACB is not set
+CONFIG_I2C_SIS5595=m
+CONFIG_I2C_SIS630=m
+CONFIG_I2C_SIS96X=m
+CONFIG_I2C_VIA=m
+CONFIG_I2C_VIAPRO=m
+CONFIG_I2C_VOODOO3=m
+# CONFIG_I2C_PCA_ISA is not set
+
+#
+# Hardware Sensors Chip support
+#
+CONFIG_I2C_SENSOR=m
+CONFIG_SENSORS_ADM1021=m
+CONFIG_SENSORS_ADM1025=m
+CONFIG_SENSORS_ADM1031=m
+CONFIG_SENSORS_ASB100=m
+CONFIG_SENSORS_DS1621=m
+CONFIG_SENSORS_FSCHER=m
+CONFIG_SENSORS_GL518SM=m
+CONFIG_SENSORS_IT87=m
+CONFIG_SENSORS_LM75=m
+CONFIG_SENSORS_LM77=m
+CONFIG_SENSORS_LM78=m
+CONFIG_SENSORS_LM80=m
+CONFIG_SENSORS_LM83=m
+CONFIG_SENSORS_LM85=m
+CONFIG_SENSORS_LM90=m
+CONFIG_SENSORS_MAX1619=m
+CONFIG_SENSORS_SMSC47M1=m
+CONFIG_SENSORS_VIA686A=m
+CONFIG_SENSORS_W83781D=m
+CONFIG_SENSORS_W83L785TS=m
+CONFIG_SENSORS_W83627HF=m
+
+#
+# Other I2C Chip support
+#
+CONFIG_SENSORS_EEPROM=m
+CONFIG_SENSORS_PCF8574=m
+CONFIG_SENSORS_PCF8591=m
+CONFIG_SENSORS_RTC8564=m
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_I2C_DEBUG_CHIP is not set
+
+#
+# Dallas's 1-wire bus
+#
+# CONFIG_W1 is not set
+
+#
+# Misc devices
+#
+CONFIG_IBM_ASM=m
+
+#
+# Multimedia devices
+#
+CONFIG_VIDEO_DEV=m
+
+#
+# Video For Linux
+#
+
+#
+# Video Adapters
+#
+# CONFIG_VIDEO_BT848 is not set
+# CONFIG_VIDEO_PMS is not set
+# CONFIG_VIDEO_BWQCAM is not set
+# CONFIG_VIDEO_CQCAM is not set
+# CONFIG_VIDEO_W9966 is not set
+# CONFIG_VIDEO_CPIA is not set
+# CONFIG_VIDEO_SAA5246A is not set
+# CONFIG_VIDEO_SAA5249 is not set
+# CONFIG_TUNER_3036 is not set
+# CONFIG_VIDEO_STRADIS is not set
 # CONFIG_VIDEO_ZORAN is not set
-# CONFIG_VIDEO_ZORAN_BUZ is not set
-# CONFIG_VIDEO_ZORAN_DC10 is not set
-# CONFIG_VIDEO_ZORAN_DC30 is not set
-# CONFIG_VIDEO_ZORAN_LML33 is not set
-# CONFIG_VIDEO_ZORAN_LML33R10 is not set
-# CONFIG_VIDEO_MEYE is not set
 # CONFIG_VIDEO_SAA7134 is not set
 # CONFIG_VIDEO_MXB is not set
 # CONFIG_VIDEO_DPC is not set
 # CONFIG_VIDEO_HEXIUM_ORION is not set
 # CONFIG_VIDEO_HEXIUM_GEMINI is not set
 # CONFIG_VIDEO_CX88 is not set
-# CONFIG_VIDEO_SAA5246A is not set
 CONFIG_VIDEO_OVCAMCHIP=m
 
-
 #
 # Radio Adapters
 #
@@ -1601,119 +1694,49 @@ CONFIG_VIDEO_OVCAMCHIP=m
 # CONFIG_RADIO_GEMTEK_PCI is not set
 # CONFIG_RADIO_MAXIRADIO is not set
 # CONFIG_RADIO_MAESTRO is not set
-# CONFIG_RADIO_MIROPCM20 is not set
-# CONFIG_RADIO_MIROPCM20_RDS is not set
 # CONFIG_RADIO_SF16FMI is not set
 # CONFIG_RADIO_SF16FMR2 is not set
 # CONFIG_RADIO_TERRATEC is not set
 # CONFIG_RADIO_TRUST is not set
 # CONFIG_RADIO_TYPHOON is not set
-# CONFIG_RADIO_TYPHOON_PROC_FS=y
 # CONFIG_RADIO_ZOLTRIX is not set
 
-
 #
 # Digital Video Broadcasting Devices
 #
 # CONFIG_DVB is not set
-CONFIG_DVB_CORE=m
-
-#
-# Supported Frontend Modules
-#
-CONFIG_DVB_STV0299=m
-# CONFIG_DVB_SP887X is not set
-# CONFIG_DVB_ALPS_TDLB7 is not set
-CONFIG_DVB_ALPS_TDMB7=m
-CONFIG_DVB_ATMEL_AT76C651=m
-CONFIG_DVB_CX24110=m
-CONFIG_DVB_GRUNDIG_29504_491=m
-CONFIG_DVB_GRUNDIG_29504_401=m
-CONFIG_DVB_MT312=m
-CONFIG_DVB_VES1820=m
-CONFIG_DVB_VES1X93=m
-CONFIG_DVB_TWINHAN_DST=m
-CONFIG_DVB_TTUSB_DEC=m
-CONFIG_DVB_BT8XX=m
-# CONFIG_DVB_TDA1004X is not set
-CONFIG_DVB_NXT6000=m
-
-#
-# Supported SAA7146 based PCI Adapters
-#
-CONFIG_DVB_AV7110=m
-CONFIG_DVB_AV7110_OSD=y
-# CONFIG_DVB_AV7110_FIRMWARE is not set
-CONFIG_DVB_BUDGET=m
-CONFIG_DVB_BUDGET_CI=m
-CONFIG_DVB_BUDGET_AV=m
-CONFIG_DVB_BUDGET_PATCH=m
-
-#
-# Supported USB Adapters
-#
-CONFIG_DVB_TTUSB_BUDGET=m
-
-#
-# Supported FlexCopII (B2C2) Adapters
-#
-CONFIG_DVB_B2C2_SKYSTAR=m
-CONFIG_VIDEO_SAA7146=m
-CONFIG_VIDEO_SAA7146_VV=m
-CONFIG_VIDEO_VIDEOBUF=m
-CONFIG_VIDEO_TUNER=m
-CONFIG_VIDEO_BUF=m
-CONFIG_VIDEO_BTCX=m
 
 #
 # Graphics support
 #
 CONFIG_FB=y
+CONFIG_FB_MODE_HELPERS=y
+CONFIG_FB_CIRRUS=m
+# CONFIG_FB_PM2 is not set
 # CONFIG_FB_CYBER2000 is not set
+# CONFIG_FB_ASILIANT is not set
 # CONFIG_FB_IMSTT is not set
 CONFIG_FB_VGA16=m
 CONFIG_FB_VESA=y
 CONFIG_VIDEO_SELECT=y
 # CONFIG_FB_HGA is not set
 CONFIG_FB_RIVA=m
-# CONFIG_FB_RIVA_DEBUG is not set
 # CONFIG_FB_RIVA_I2C is not set
+# CONFIG_FB_RIVA_DEBUG is not set
 CONFIG_FB_I810=m
 CONFIG_FB_I810_GTF=y
 # CONFIG_FB_MATROX is not set
-CONFIG_FB_MATROX_MILLENIUM=y
-CONFIG_FB_MATROX_MYSTIQUE=y
-CONFIG_FB_MATROX_G450=y
-CONFIG_FB_MATROX_G100=y
-CONFIG_FB_MATROX_I2C=m
-CONFIG_FB_MATROX_MAVEN=m
-CONFIG_FB_MATROX_MULTIHEAD=y
-# CONFIG_FB_RADEON is not set
-# CONFIG_FB_RADEON_DEBUG is not set
 # CONFIG_FB_RADEON_OLD is not set
-CONFIG_FB_RADEON_I2C=y
+# CONFIG_FB_RADEON is not set
 # CONFIG_FB_ATY128 is not set
 # CONFIG_FB_ATY is not set
-CONFIG_FB_ATY_CT=y
-CONFIG_FB_ATY_GX=y
-# CONFIG_FB_ATY_XL_INIT is not set
 # CONFIG_FB_SIS is not set
-CONFIG_FB_SIS_300=y
-CONFIG_FB_SIS_315=y
-# CONFIG_FB_NEOMAGIC is not set
 # CONFIG_FB_NEOMAGIC is not set
+CONFIG_FB_KYRO=m
 # CONFIG_FB_3DFX is not set
 # CONFIG_FB_VOODOO1 is not set
 # CONFIG_FB_TRIDENT is not set
 # CONFIG_FB_VIRTUAL is not set
-CONFIG_FB_KYRO=m
-# CONFIG_FB_PM2 is not set
-# CONFIG_FB_PM2_FIFO_DISCONNECT is not set
-# CONFIG_FB_ASILIANT is not set
-# CONFIG_FB_HGA_ACCEL is not set
-# CONFIG_FB_3DFX_ACCEL is not set
-# CONFIG_FB_TRIDENT_ACCEL is not set
-CONFIG_FB_CIRRUS=m
 
 #
 # Console display driver support
@@ -1723,7 +1746,8 @@ CONFIG_MDA_CONSOLE=m
 CONFIG_DUMMY_CONSOLE=y
 CONFIG_FRAMEBUFFER_CONSOLE=y
 # CONFIG_FONTS is not set
-
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
 
 #
 # Logo configuration
@@ -1742,6 +1766,10 @@ CONFIG_SOUND=m
 # Advanced Linux Sound Architecture
 #
 CONFIG_SND=m
+CONFIG_SND_TIMER=m
+CONFIG_SND_PCM=m
+CONFIG_SND_HWDEP=m
+CONFIG_SND_RAWMIDI=m
 CONFIG_SND_SEQUENCER=m
 CONFIG_SND_SEQ_DUMMY=m
 CONFIG_SND_OSSEMUL=y
@@ -1751,11 +1779,13 @@ CONFIG_SND_SEQUENCER_OSS=y
 CONFIG_SND_RTCTIMER=m
 # CONFIG_SND_VERBOSE_PRINTK is not set
 # CONFIG_SND_DEBUG is not set
-CONFIG_SND_BIT32_EMUL=y
 
 #
 # Generic devices
 #
+CONFIG_SND_MPU401_UART=m
+CONFIG_SND_OPL3_LIB=m
+CONFIG_SND_VX_LIB=m
 CONFIG_SND_DUMMY=m
 CONFIG_SND_VIRMIDI=m
 CONFIG_SND_MTPAV=m
@@ -1765,12 +1795,10 @@ CONFIG_SND_MPU401=m
 #
 # ISA devices
 #
-# CONFIG_SND_AD1816A is not set
 # CONFIG_SND_AD1848 is not set
 # CONFIG_SND_CS4231 is not set
 # CONFIG_SND_CS4232 is not set
 # CONFIG_SND_CS4236 is not set
-# CONFIG_SND_ES968 is not set
 # CONFIG_SND_ES1688 is not set
 # CONFIG_SND_ES18XX is not set
 # CONFIG_SND_GUSCLASSIC is not set
@@ -1784,12 +1812,8 @@ CONFIG_SND_MPU401=m
 # CONFIG_SND_SB8 is not set
 # CONFIG_SND_SB16 is not set
 # CONFIG_SND_SBAWE is not set
-# CONFIG_SND_SB16_CSP=y
 # CONFIG_SND_WAVEFRONT is not set
-# CONFIG_SND_ALS100 is not set
-# CONFIG_SND_AZT2320 is not set
 # CONFIG_SND_CMI8330 is not set
-# CONFIG_SND_DT019X is not set
 # CONFIG_SND_OPL3SA2 is not set
 # CONFIG_SND_SGALAXY is not set
 # CONFIG_SND_SSCAPE is not set
@@ -1797,13 +1821,21 @@ CONFIG_SND_MPU401=m
 #
 # PCI devices
 #
+CONFIG_SND_AC97_CODEC=m
 CONFIG_SND_ALI5451=m
+CONFIG_SND_ATIIXP=m
+CONFIG_SND_ATIIXP_MODEM=m
+CONFIG_SND_AU8810=m
+CONFIG_SND_AU8820=m
+CONFIG_SND_AU8830=m
 CONFIG_SND_AZT3328=m
+CONFIG_SND_BT87X=m
 CONFIG_SND_CS46XX=m
 CONFIG_SND_CS46XX_NEW_DSP=y
 CONFIG_SND_CS4281=m
 CONFIG_SND_EMU10K1=m
 CONFIG_SND_KORG1212=m
+CONFIG_SND_MIXART=m
 CONFIG_SND_NM256=m
 CONFIG_SND_RME32=m
 CONFIG_SND_RME96=m
@@ -1819,23 +1851,14 @@ CONFIG_SND_ES1938=m
 CONFIG_SND_ES1968=m
 CONFIG_SND_MAESTRO3=m
 CONFIG_SND_FM801=m
+CONFIG_SND_FM801_TEA575X=m
 CONFIG_SND_ICE1712=m
 CONFIG_SND_ICE1724=m
 CONFIG_SND_INTEL8X0=m
+CONFIG_SND_INTEL8X0M=m
 CONFIG_SND_SONICVIBES=m
 CONFIG_SND_VIA82XX=m
 CONFIG_SND_VX222=m
-CONFIG_SND_BT87X=m
-CONFIG_SND_ATIIXP=m
-CONFIG_SND_ATIIXP_MODEM=m
-CONFIG_SND_AU8810=m
-CONFIG_SND_AU8820=m
-CONFIG_SND_AU8830=m
-CONFIG_SND_MIXART=m
-CONFIG_SND_FM801_TEA575X=m
-CONFIG_SND_INTEL8X0M=m
-CONFIG_SND_PDAUDIOCF=m
-
 
 #
 # ALSA USB devices
@@ -1848,13 +1871,12 @@ CONFIG_SND_USB_USX2Y=m
 #
 # CONFIG_SND_VXPOCKET is not set
 # CONFIG_SND_VXP440 is not set
+CONFIG_SND_PDAUDIOCF=m
 
 #
 # Open Sound System
 #
 # CONFIG_SOUND_PRIME is not set
-# CONFIG_SOUND_BT878 is not set
-# CONFIG_SOUND_CMPCI is not set
 
 #
 # USB support
@@ -1869,6 +1891,7 @@ CONFIG_USB_DEVICEFS=y
 # CONFIG_USB_BANDWIDTH is not set
 # CONFIG_USB_DYNAMIC_MINORS is not set
 CONFIG_USB_SUSPEND=y
+# CONFIG_USB_OTG is not set
 
 #
 # USB Host Controller Drivers
@@ -1890,9 +1913,9 @@ CONFIG_USB_UHCI_HCD=m
 CONFIG_USB_MIDI=m
 CONFIG_USB_ACM=m
 CONFIG_USB_PRINTER=m
-# CONFIG_BLK_DEV_UB is not set
 CONFIG_USB_STORAGE=m
 # CONFIG_USB_STORAGE_DEBUG is not set
+CONFIG_USB_STORAGE_RW_DETECT=y
 CONFIG_USB_STORAGE_DATAFAB=y
 CONFIG_USB_STORAGE_FREECOM=y
 CONFIG_USB_STORAGE_ISD200=y
@@ -1901,7 +1924,6 @@ CONFIG_USB_STORAGE_HP8200e=y
 CONFIG_USB_STORAGE_SDDR09=y
 CONFIG_USB_STORAGE_SDDR55=y
 CONFIG_USB_STORAGE_JUMPSHOT=y
-CONFIG_USB_STORAGE_RW_DETECT=y
 
 #
 # USB Human Interface Devices (HID)
@@ -1913,17 +1935,14 @@ CONFIG_HID_PID=y
 CONFIG_LOGITECH_FF=y
 CONFIG_THRUSTMASTER_FF=y
 CONFIG_USB_HIDDEV=y
-
-#
-# USB HID Boot Protocol drivers
-#
-# CONFIG_USB_KBD is not set
-# CONFIG_USB_MOUSE is not set
 CONFIG_USB_AIPTEK=m
 CONFIG_USB_WACOM=m
 CONFIG_USB_KBTAB=m
 CONFIG_USB_POWERMATE=m
+CONFIG_USB_MTOUCH=m
+CONFIG_USB_EGALAX=m
 CONFIG_USB_XPAD=m
+CONFIG_USB_ATI_REMOTE=m
 
 #
 # USB Imaging devices
@@ -1942,8 +1961,10 @@ CONFIG_USB_IBMCAM=m
 CONFIG_USB_KONICAWC=m
 CONFIG_USB_OV511=m
 CONFIG_USB_SE401=m
-CONFIG_USB_STV680=m
 CONFIG_USB_SN9C102=m
+CONFIG_USB_STV680=m
+CONFIG_USB_W9968CF=m
+CONFIG_USB_PWC=m
 
 #
 # USB Network adaptors
@@ -1953,11 +1974,11 @@ CONFIG_USB_KAWETH=m
 CONFIG_USB_PEGASUS=m
 CONFIG_USB_RTL8150=m
 CONFIG_USB_USBNET=m
-CONFIG_USB_SPEEDTOUCH=m
 
 #
 # USB Host-to-Host Cables
 #
+CONFIG_USB_ALI_M5632=y
 CONFIG_USB_AN2720=y
 CONFIG_USB_BELKIN=y
 CONFIG_USB_GENESYS=y
@@ -2019,40 +2040,33 @@ CONFIG_USB_SERIAL_SAFE_PADDED=y
 CONFIG_USB_SERIAL_CYBERJACK=m
 CONFIG_USB_SERIAL_XIRCOM=m
 CONFIG_USB_SERIAL_OMNINET=m
-CONFIG_USB_SERIAL_WHITEHEAT=m
 CONFIG_USB_EZUSB=y
-CONFIG_USB_EMI62=m
-CONFIG_USB_LED=m
-CONFIG_USB_G_SERIAL=m
-
 
 #
 # USB Miscellaneous drivers
 #
+CONFIG_USB_EMI62=m
 # CONFIG_USB_EMI26 is not set
 # CONFIG_USB_TIGL is not set
 CONFIG_USB_AUERSWALD=m
 CONFIG_USB_RIO500=m
-CONFIG_USB_LCD=m
-CONFIG_USB_TEST=m
-# CONFIG_USB_GADGET is not set
-# CONFIG_USB_GADGET_PXA2XX is not set
-# CONFIG_USB_GADGET_GOKU is not set
-# CONFIG_USB_GADGET_SA1100 is not set
-CONFIG_USB_ZERO=m
-CONFIG_USB_ETH=m
-CONFIG_USB_GADGETFS=m
-CONFIG_USB_W9968CF=m
-CONFIG_USB_PWC=m
 CONFIG_USB_LEGOTOWER=m
-CONFIG_USB_FILE_STORAGE=m
-# CONFIG_USB_FILE_STORAGE_TEST is not set
-CONFIG_USB_MTOUCH=m
-CONFIG_USB_ATI_REMOTE=m
-CONFIG_USB_ALI_M5632=y
+CONFIG_USB_LCD=m
+CONFIG_USB_LED=m
 # CONFIG_USB_CYTHERM is not set
-CONFIG_USB_EGALAX=m
 CONFIG_USB_PHIDGETSERVO=m
+CONFIG_USB_TEST=m
+
+#
+# USB ATM/DSL drivers
+#
+CONFIG_USB_ATM=m
+CONFIG_USB_SPEEDTOUCH=m
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET is not set
 
 #
 # File systems
@@ -2069,20 +2083,9 @@ CONFIG_JBD=m
 # CONFIG_JBD_DEBUG is not set
 CONFIG_FS_MBCACHE=y
 # CONFIG_REISERFS_FS is not set
-# CONFIG_REISERFS_CHECK is not set
-CONFIG_REISERFS_PROC_INFO=y
-CONFIG_REISERFS_FS_XATTR=y
-CONFIG_REISERFS_FS_POSIX_ACL=y
-CONFIG_REISERFS_FS_SECURITY=y
 # CONFIG_JFS_FS is not set
-# CONFIG_JFS_DEBUG is not set
-# CONFIG_JFS_STATISTICS is not set
-CONFIG_JFS_POSIX_ACL=y
+CONFIG_FS_POSIX_ACL=y
 # CONFIG_XFS_FS is not set
-# CONFIG_XFS_RT is not set
-CONFIG_XFS_QUOTA=y
-CONFIG_XFS_POSIX_ACL=y
-CONFIG_XFS_SECURITY=y
 # CONFIG_MINIX_FS is not set
 # CONFIG_ROMFS_FS is not set
 CONFIG_QUOTA=y
@@ -2100,6 +2103,7 @@ CONFIG_JOLIET=y
 CONFIG_ZISOFS=y
 CONFIG_ZISOFS_FS=y
 CONFIG_UDF_FS=m
+CONFIG_UDF_NLS=y
 
 #
 # DOS/FAT/NT Filesystems
@@ -2116,6 +2120,7 @@ CONFIG_FAT_DEFAULT_IOCHARSET="ascii"
 #
 CONFIG_PROC_FS=y
 CONFIG_PROC_KCORE=y
+CONFIG_SYSFS=y
 # CONFIG_DEVFS_FS is not set
 CONFIG_DEVPTS_FS_XATTR=y
 CONFIG_DEVPTS_FS_SECURITY=y
@@ -2129,27 +2134,27 @@ CONFIG_RAMFS=y
 #
 # Miscellaneous filesystems
 #
-# CONFIG_ADFS_FS is not seta
+# CONFIG_ADFS_FS is not set
 # CONFIG_AFFS_FS is not set
-# uses sleepon and needs a major update
 CONFIG_HFS_FS=m
 CONFIG_HFSPLUS_FS=m
 # CONFIG_BEFS_FS is not set
-# CONFIG_BEFS_DEBUG is not set
 # CONFIG_BFS_FS is not set
 # CONFIG_EFS_FS is not set
 # CONFIG_JFFS_FS is not set
 CONFIG_JFFS2_FS=m
 CONFIG_JFFS2_FS_DEBUG=0
 CONFIG_JFFS2_FS_NAND=y
+# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set
+CONFIG_JFFS2_ZLIB=y
+CONFIG_JFFS2_RTIME=y
+# CONFIG_JFFS2_RUBIN is not set
 CONFIG_CRAMFS=m
 CONFIG_VXFS_FS=m
 # CONFIG_HPFS_FS is not set
 # CONFIG_QNX4FS_FS is not set
-# CONFIG_QNX4FS_RW is not set
 # CONFIG_SYSV_FS is not set
 # CONFIG_UFS_FS is not set
-# CONFIG_UFS_FS_WRITE is not set
 
 #
 # Network File Systems
@@ -2176,17 +2181,8 @@ CONFIG_CIFS=m
 CONFIG_CIFS_XATTR=y
 CONFIG_CIFS_POSIX=y
 # CONFIG_NCP_FS is not set
-CONFIG_NCPFS_PACKET_SIGNING=y
-CONFIG_NCPFS_IOCTL_LOCKING=y
-CONFIG_NCPFS_STRONG=y
-CONFIG_NCPFS_NFS_NS=y
-CONFIG_NCPFS_OS2_NS=y
-CONFIG_NCPFS_SMALLDOS=y
-CONFIG_NCPFS_NLS=y
-CONFIG_NCPFS_EXTRAS=y
 # CONFIG_CODA_FS is not set
 # CONFIG_AFS_FS is not set
-# CONFIG_RXRPC is not set
 
 #
 # Partition Types
@@ -2207,11 +2203,11 @@ CONFIG_SGI_PARTITION=y
 # CONFIG_ULTRIX_PARTITION is not set
 CONFIG_SUN_PARTITION=y
 CONFIG_EFI_PARTITION=y
-CONFIG_NLS=y
 
 #
 # Native Language Support
 #
+CONFIG_NLS=y
 CONFIG_NLS_DEFAULT="utf8"
 CONFIG_NLS_CODEPAGE_437=y
 CONFIG_NLS_CODEPAGE_737=m
@@ -2236,6 +2232,7 @@ CONFIG_NLS_CODEPAGE_874=m
 CONFIG_NLS_ISO8859_8=m
 CONFIG_NLS_CODEPAGE_1250=m
 CONFIG_NLS_CODEPAGE_1251=m
+CONFIG_NLS_ASCII=y
 CONFIG_NLS_ISO8859_1=m
 CONFIG_NLS_ISO8859_2=m
 CONFIG_NLS_ISO8859_3=m
@@ -2250,36 +2247,32 @@ CONFIG_NLS_ISO8859_15=m
 CONFIG_NLS_KOI8_R=m
 CONFIG_NLS_KOI8_U=m
 CONFIG_NLS_UTF8=m
-CONFIG_NLS_ASCII=y
 
 #
 # Profiling support
 #
-# CONFIG_PROFILING is not set
-# CONFIG_OPROFILE is not set
-
-#
-# Tux
-#
-CONFIG_TUX=m
-CONFIG_TUX_EXTCGI=y
-# CONFIG_TUX_EXTENDED_LOG is not set
-# CONFIG_TUX_DEBUG is not set
-
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=m
 
 #
 # Kernel hacking
 #
 CONFIG_DEBUG_KERNEL=y
-# CONFIG_DEBUG_SLAB is not set
 CONFIG_MAGIC_SYSRQ=y
+# CONFIG_DEBUG_SLAB is not set
 CONFIG_DEBUG_SPINLOCK=y
+CONFIG_DEBUG_SPINLOCK_SLEEP=y
 CONFIG_DEBUG_HIGHMEM=y
 # CONFIG_DEBUG_INFO is not set
-CONFIG_DEBUG_SPINLOCK_SLEEP=y
 # CONFIG_FRAME_POINTER is not set
-CONFIG_CC_OPTIMIZE_FOR_SIZE=y
-# CONFIG_DEBUG_DRIVER is not set
+CONFIG_EARLY_PRINTK=y
+CONFIG_DEBUG_STACKOVERFLOW=y
+# CONFIG_KPROBES is not set
+CONFIG_DEBUG_STACK_USAGE=y
+# CONFIG_DEBUG_PAGEALLOC is not set
+# CONFIG_SCHEDSTATS is not set
+CONFIG_X86_FIND_SMP_CONFIG=y
+CONFIG_X86_MPPARSE=y
 
 #
 # Security options
@@ -2290,13 +2283,11 @@ CONFIG_SECURITY_CAPABILITIES=y
 # CONFIG_SECURITY_ROOTPLUG is not set
 CONFIG_SECURITY_SELINUX=y
 CONFIG_SECURITY_SELINUX_BOOTPARAM=y
+CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE=1
 CONFIG_SECURITY_SELINUX_DISABLE=y
 CONFIG_SECURITY_SELINUX_DEVELOP=y
 CONFIG_SECURITY_SELINUX_AVC_STATS=y
 # CONFIG_SECURITY_SELINUX_MLS is not set
-CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE=1
-CONFIG_AUDIT=y
-CONFIG_AUDITSYSCALL=y
 
 #
 # Cryptographic options
@@ -2309,427 +2300,35 @@ CONFIG_CRYPTO_MD5=m
 CONFIG_CRYPTO_SHA1=y
 CONFIG_CRYPTO_SHA256=m
 CONFIG_CRYPTO_SHA512=m
+CONFIG_CRYPTO_WP512=m
 CONFIG_CRYPTO_DES=m
 CONFIG_CRYPTO_BLOWFISH=m
 CONFIG_CRYPTO_TWOFISH=m
 CONFIG_CRYPTO_SERPENT=m
-CONFIG_CRYPTO_AES=m
+CONFIG_CRYPTO_AES_586=m
 CONFIG_CRYPTO_CAST5=m
 CONFIG_CRYPTO_CAST6=m
-CONFIG_CRYPTO_DEFLATE=m
+CONFIG_CRYPTO_TEA=m
 CONFIG_CRYPTO_ARC4=m
+CONFIG_CRYPTO_KHAZAD=m
+CONFIG_CRYPTO_DEFLATE=m
 CONFIG_CRYPTO_MICHAEL_MIC=m
-# CONFIG_CRYPTO_TEST is not set
-CONFIG_LIBCRC32C=m
 CONFIG_CRYPTO_CRC32C=m
+# CONFIG_CRYPTO_TEST is not set
+CONFIG_CRYPTO_SIGNATURE=y
 CONFIG_CRYPTO_SIGNATURE_DSA=y
 CONFIG_CRYPTO_MPILIB=y
-CONFIG_CRYPTO_TEA=m
-CONFIG_CRYPTO_KHAZAD=m
-CONFIG_CRYPTO_WP512=m
 
 #
 # Library routines
 #
-CONFIG_CRC32=m
 CONFIG_CRC_CCITT=m
-
+CONFIG_CRC32=y
+CONFIG_LIBCRC32C=m
 CONFIG_ZLIB_INFLATE=y
 CONFIG_ZLIB_DEFLATE=m
+CONFIG_X86_SMP=y
+CONFIG_X86_HT=y
+CONFIG_X86_BIOS_REBOOT=y
+CONFIG_X86_TRAMPOLINE=y
 CONFIG_PC=y
-# CONFIG_SCSI_BUSLOGIC is not set
-# CONFIG_SCSI_INIA100 is not set
-# CONFIG_ATALK is not set
-# CONFIG_DEV_APPLETALK is not set
-# CONFIG_LTPC is not set
-# CONFIG_COPS is not set
-# CONFIG_IPX is not set
-# CONFIG_IPDDP is not set
-# CONFIG_IRDA is not set
-# CONFIG_NCP_FS is not set
-# CONFIG_ISAPNP is not set
-# CONFIG_PCMCIA_AHA152X is not set
-# CONFIG_PCMCIA_NINJA_SCSI is not set
-# CONFIG_PCMCIA_QLOGIC is not set
-# CONFIG_IEEE1394 is not set
-# CONFIG_EL1 is not set
-# CONFIG_EL2 is not set
-# CONFIG_ELPLUS is not set
-# CONFIG_WD80x3 is not set
-# CONFIG_IRDA is not set
-# CONFIG_GAMEPORT is not set
-# CONFIG_DVB is not set
-# CONFIG_SND_AD1816A is not set
-# CONFIG_SND_AD1848 is not set
-# CONFIG_SND_CS4231 is not set
-# CONFIG_SND_CS4232 is not set
-# CONFIG_SND_CS4236 is not set
-# CONFIG_SND_ES968 is not set
-# CONFIG_SND_ES1688 is not set
-# CONFIG_SND_ES18XX is not set
-# CONFIG_SND_GUSCLASSIC is not set
-# CONFIG_SND_GUSEXTREME is not set
-# CONFIG_SND_GUSMAX is not set
-# CONFIG_SND_INTERWAVE is not set
-# CONFIG_SND_INTERWAVE_STB is not set
-# CONFIG_SND_OPTI92X_AD1848 is not set
-# CONFIG_SND_OPTI92X_CS4231 is not set
-# CONFIG_SND_OPTI93X is not set
-# CONFIG_SND_SB8 is not set
-# CONFIG_SND_SB16 is not set
-# CONFIG_SND_SBAWE is not set
-# CONFIG_SND_SB16_CSP=y
-# CONFIG_SND_WAVEFRONT is not set
-# CONFIG_SND_ALS100 is not set
-# CONFIG_SND_AZT2320 is not set
-# CONFIG_SND_CMI8330 is not set
-# CONFIG_SND_DT019X is not set
-# CONFIG_SND_OPL3SA2 is not set
-# CONFIG_SND_SGALAXY is not set
-# CONFIG_SND_SSCAPE is not set
-# CONFIG_REISERFS_FS is not set
-# CONFIG_XFS_FS is not set
-# CONFIG_AFFS_FS is not set
-# CONFIG_BEFS_FS is not set
-# CONFIG_EFS_FS is not set
-# CONFIG_BFS_FS is not set
-# CONFIG_QNX4FS_FS is not set
-# CONFIG_SYSV_FS is not set
-# CONFIG_UFS_FS is not set
-# CONFIG_WAN_ROUTER is not set
-# CONFIG_ROMFS_FS is not set
-# CONFIG_MINIX_FS is not set
-# CONFIG_BINFMT_AOUT is not set
-# CONFIG_DRM_TDFX is not set
-# CONFIG_DRM_GAMMA is not set
-# CONFIG_DRM_SIS is not set
-# CONFIG_BLK_DEV_UMEM is not set
-# CONFIG_MWAVE is not set
-# CONFIG_ROCKETPORT is not set
-# CONFIG_R3964 is not set
-# CONFIG_TIPAR is not set
-# CONFIG_JOYSTICK_ANALOG is not set
-# CONFIG_JOYSTICK_A3D is not set
-# CONFIG_JOYSTICK_ADI is not set
-# CONFIG_JOYSTICK_COBRA is not set
-# CONFIG_JOYSTICK_GF2K is not set
-# CONFIG_JOYSTICK_GRIP is not set
-# CONFIG_JOYSTICK_GRIP_MP is not set
-# CONFIG_JOYSTICK_GUILLEMOT is not set
-# CONFIG_JOYSTICK_INTERACT is not set
-# CONFIG_JOYSTICK_SIDEWINDER is not set
-# CONFIG_JOYSTICK_TMDC is not set
-# CONFIG_JOYSTICK_IFORCE is not set
-# CONFIG_JOYSTICK_IFORCE_USB=y
-# CONFIG_JOYSTICK_IFORCE_232=y
-# CONFIG_JOYSTICK_WARRIOR is not set
-# CONFIG_JOYSTICK_MAGELLAN is not set
-# CONFIG_JOYSTICK_SPACEORB is not set
-# CONFIG_JOYSTICK_SPACEBALL is not set
-# CONFIG_JOYSTICK_STINGER is not set
-# CONFIG_JOYSTICK_TWIDDLER is not set
-# CONFIG_JOYSTICK_DB9 is not set
-# CONFIG_JOYSTICK_GAMECON is not set
-# CONFIG_JOYSTICK_TURBOGRAFX is not set
-# CONFIG_RADIO_CADET is not set
-# CONFIG_RADIO_RTRACK is not set
-# CONFIG_RADIO_RTRACK2 is not set
-# CONFIG_RADIO_AZTECH is not set
-# CONFIG_RADIO_GEMTEK is not set
-# CONFIG_RADIO_GEMTEK_PCI is not set
-# CONFIG_RADIO_MAXIRADIO is not set
-# CONFIG_RADIO_MAESTRO is not set
-# CONFIG_RADIO_MIROPCM20 is not set
-# CONFIG_RADIO_MIROPCM20_RDS is not set
-# CONFIG_RADIO_SF16FMI is not set
-# CONFIG_RADIO_SF16FMR2 is not set
-# CONFIG_RADIO_TERRATEC is not set
-# CONFIG_RADIO_TRUST is not set
-# CONFIG_RADIO_TYPHOON is not set
-# CONFIG_RADIO_TYPHOON_PROC_FS=y
-# CONFIG_RADIO_ZOLTRIX is not set
-# CONFIG_VIDEO_DEV is not set
-# CONFIG_PLIP is not set
-# CONFIG_FB_MATROX is not set
-# CONFIG_FB_3DFX is not set
-# CONFIG_FB_HGA is not set
-# CONFIG_FB_ATY is not set
-# CONFIG_FB_TRIDENT is not set
-# CONFIG_FB_VOODOO1 is not set
-# CONFIG_SCSI_ADVANSYS is not set
-# CONFIG_SCSI_AHA1542 is not set
-CONFIG_SCSI_FUTURE_DOMAIN=m
-# CONFIG_SCSI_IN2000 is not set
-# CONFIG_SCSI_QLOGIC_FAS is not set
-# CONFIG_SCSI_QLOGIC_ISP is not set
-# CONFIG_VIDEO_BT848 is not set
-# CONFIG_VIDEO_PMS is not set
-# CONFIG_VIDEO_BWQCAM is not set
-# CONFIG_VIDEO_CQCAM is not set
-# CONFIG_VIDEO_W9966 is not set
-# CONFIG_VIDEO_CPIA is not set
-# CONFIG_VIDEO_CPIA_PP is not set
-# CONFIG_VIDEO_CPIA_USB is not set
-# CONFIG_VIDEO_SAA5249 is not set
-# CONFIG_TUNER_3036 is not set
-# CONFIG_VIDEO_STRADIS is not set
-# CONFIG_VIDEO_ZORAN is not set
-# CONFIG_VIDEO_ZORAN is not set
-# CONFIG_VIDEO_ZORAN_BUZ is not set
-# CONFIG_VIDEO_ZORAN_DC10 is not set
-# CONFIG_VIDEO_ZORAN_DC30 is not set
-# CONFIG_VIDEO_ZORAN_LML33 is not set
-# CONFIG_VIDEO_ZORAN_LML33R10 is not set
-# CONFIG_VIDEO_MEYE is not set
-# CONFIG_VIDEO_SAA7134 is not set
-# CONFIG_VIDEO_MXB is not set
-# CONFIG_VIDEO_DPC is not set
-# CONFIG_VIDEO_HEXIUM_ORION is not set
-# CONFIG_VIDEO_HEXIUM_GEMINI is not set
-# CONFIG_VIDEO_CX88 is not set
-# CONFIG_VIDEO_SAA5246A is not set
-# CONFIG_FB_ATY128 is not set
-# CONFIG_FB_RADEON is not set
-# CONFIG_FB_NEOMAGIC is not set
-# CONFIG_I82092 is not set
-# CONFIG_YELLOWFIN is not set
-# CONFIG_SUNDANCE is not set
-# CONFIG_ULTRA is not set
-# CONFIG_SKFP is not set
-# CONFIG_DE600 is not set
-# CONFIG_DE620 is not set
-# CONFIG_CS89x0 is not set
-# CONFIG_DGRS is not set
-# CONFIG_AC3200 is not set
-# CONFIG_NI52 is not set
-# CONFIG_NI65 is not set
-# CONFIG_LANCE is not set
-# CONFIG_ELPLUS is not set
-# CONFIG_EL16 is not set
-# CONFIG_EL3 is not set
-# CONFIG_3C515 is not set
-# CONFIG_HAMACHI is not set
-CONFIG_HP100=m
-# CONFIG_EQUALIZER is not set
-# CONFIG_NET_SB1000 is not set
-# CONFIG_DEPCA is not set
-# CONFIG_ATP is not set
-# CONFIG_FB_NEOMAGIC is not set
-# CONFIG_INFTL is not set
-# CONFIG_MTD_DOC2000 is not set
-# CONFIG_MTD_DOC2001PLUS is not set
-# CONFIG_MTD_PMC551 is not set
-# CONFIG_MTD_COMPLEX_MAPPINGS is not set
-# CONFIG_MTD_PCI is not set
-# CONFIG_PCMCIA_SYM53C500 is not set
-# CONFIG_FB_ASILIANT is not set
-# CONFIG_FB_HGA_ACCEL is not set
-# CONFIG_FB_3DFX_ACCEL is not set
-# CONFIG_FB_TRIDENT_ACCEL is not set
-# CONFIG_SCSI_DC390T is not set
-CONFIG_AUDIT=y
-# CONFIG_AUTOFS_FS is not set
-# CONFIG_JFS_FS is not set
-# CONFIG_I2C_PCA_ISA is not set
-CONFIG_RAW_DRIVER=y
-# CONFIG_MTD_SCB2_FLASH is not set
-CONFIG_UID16=y
-CONFIG_X86_PC=y
-# CONFIG_X86_ELAN is not set
-# CONFIG_X86_VOYAGER is not set
-# CONFIG_X86_NUMAQ is not set
-# CONFIG_X86_SUMMIT is not set
-# CONFIG_X86_BIGSMP is not set
-# CONFIG_X86_VISWS is not set
-# CONFIG_X86_GENERICARCH is not set
-# CONFIG_X86_ES7000 is not set
-# CONFIG_M386 is not set
-# CONFIG_M486 is not set
-# CONFIG_M586 is not set
-# CONFIG_M586TSC is not set
-# CONFIG_M586MMX is not set
-CONFIG_M686=y
-# CONFIG_MPENTIUMII is not set
-# CONFIG_MPENTIUMIII is not set
-# CONFIG_MPENTIUMM is not set
-# CONFIG_MPENTIUM4 is not set
-# CONFIG_MK6 is not set
-# CONFIG_MK7 is not set
-# CONFIG_MK8 is not set
-# CONFIG_MCRUSOE is not set
-# CONFIG_MWINCHIPC6 is not set
-# CONFIG_MWINCHIP2 is not set
-# CONFIG_MWINCHIP3D is not set
-# CONFIG_MCYRIXIII is not set
-# CONFIG_MVIAC3_2 is not set
-CONFIG_X86_GENERIC=y
-CONFIG_X86_CMPXCHG=y
-CONFIG_X86_XADD=y
-CONFIG_X86_L1_CACHE_SHIFT=7
-CONFIG_RWSEM_XCHGADD_ALGORITHM=y
-CONFIG_X86_PPRO_FENCE=y
-CONFIG_X86_WP_WORKS_OK=y
-CONFIG_X86_INVLPG=y
-CONFIG_X86_BSWAP=y
-CONFIG_X86_POPAD_OK=y
-CONFIG_X86_GOOD_APIC=y
-CONFIG_X86_INTEL_USERCOPY=y
-CONFIG_X86_USE_PPRO_CHECKSUM=y
-# CONFIG_HPET is not set
-CONFIG_HPET_TIMER=y
-CONFIG_HPET_EMULATE_RTC=y
-CONFIG_NR_CPUS=8
-CONFIG_X86_LOCAL_APIC=y
-CONFIG_X86_IO_APIC=y
-CONFIG_X86_TSC=y
-CONFIG_X86_MCE=y
-# CONFIG_X86_MCE_NONFATAL is not set
-CONFIG_X86_MCE_P4THERMAL=y
-CONFIG_TOSHIBA=m
-CONFIG_I8K=m
-CONFIG_SONYPI=m
-CONFIG_MICROCODE=m
-CONFIG_X86_MSR=m
-CONFIG_X86_CPUID=m
-CONFIG_EDD=m
-# CONFIG_NUMA is not set
-# CONFIG_NOHIGHMEM is not set
-CONFIG_HIGHMEM4G=y
-# CONFIG_HIGHMEM64G is not set
-CONFIG_HIGHMEM=y
-CONFIG_HIGHPTE=y
-# CONFIG_MATH_EMULATION is not set
-CONFIG_MTRR=y
-CONFIG_HAVE_DEC_LOCK=y
-# CONFIG_X86_UP_APIC is not set
-CONFIG_X86_PM_TIMER=y
-# CONFIG_X86_4G is not set
-# CONFIG_EFI is not set
-CONFIG_REGPARM=y
-# CONFIG_PCI_GOBIOS is not set
-# CONFIG_PCI_GODIRECT is not set
-# CONFIG_PCI_GOMMCONFIG is not set
-CONFIG_PCI_GOANY=y
-CONFIG_MDA_CONSOLE=m
-CONFIG_SYNCLINK_CS=m
-CONFIG_SYNCLINK=m
-CONFIG_SYNCLINKMP=m
-CONFIG_HP100=m
-CONFIG_PCMCIA_FDOMAIN=m
-CONFIG_SCSI_FUTURE_DOMAIN=m
-CONFIG_CRASH=m
-CONFIG_CAPI_EICON=y
-CONFIG_I2O=m
-CONFIG_I2O_BLOCK=m
-CONFIG_I2O_SCSI=m
-CONFIG_I2O_PROC=m
-CONFIG_I2O_CONFIG=y
-CONFIG_APM=y
-# CONFIG_APM_IGNORE_USER_SUSPEND is not set
-# CONFIG_APM_DO_ENABLE is not set
-CONFIG_APM_CPU_IDLE=y
-# CONFIG_APM_DISPLAY_BLANK is not set
-CONFIG_APM_RTC_IS_GMT=y
-# CONFIG_APM_ALLOW_INTS is not set
-# CONFIG_APM_REAL_MODE_POWER_OFF is not set
-CONFIG_X86_FIND_SMP_CONFIG=y
-CONFIG_X86_MPPARSE=y
-CONFIG_ACPI=y
-CONFIG_ACPI_BOOT=y
-CONFIG_ACPI_INTERPRETER=y
-CONFIG_ACPI_SLEEP=y
-CONFIG_ACPI_AC=m
-CONFIG_ACPI_BATTERY=m
-CONFIG_ACPI_BUTTON=m
-CONFIG_ACPI_FAN=y
-CONFIG_ACPI_PROCESSOR=y
-CONFIG_ACPI_THERMAL=y
-CONFIG_ACPI_ASUS=m
-CONFIG_ACPI_TOSHIBA=m
-# CONFIG_ACPI_DEBUG is not set
-CONFIG_ACPI_BUS=y
-CONFIG_ACPI_EC=y
-CONFIG_ACPI_POWER=y
-CONFIG_ACPI_PCI=y
-CONFIG_ACPI_SYSTEM=y
-CONFIG_ACPI_NUMA=y
-CONFIG_ACPI_BLACKLIST_YEAR=2001
-CONFIG_X86_ACPI_CPUFREQ=y
-# CONFIG_X86_ACPI_CPUFREQ_PROC_INTF is not set
-CONFIG_X86_POWERNOW_K6=m
-CONFIG_X86_POWERNOW_K7=y
-CONFIG_X86_POWERNOW_K8=m
-# CONFIG_X86_GX_SUSPMOD is not set
-# CONFIG_X86_SPEEDSTEP_CENTRINO is not set
-CONFIG_X86_SPEEDSTEP_ICH=y
-CONFIG_X86_SPEEDSTEP_SMI=m
-CONFIG_X86_SPEEDSTEP_LIB=y
-# CONFIG_X86_SPEEDSTEP_RELAXED_CAP_CHECK is not set
-CONFIG_X86_P4_CLOCKMOD=m
-CONFIG_X86_LONGRUN=y
-CONFIG_X86_LONGHAUL=y
-CONFIG_X86_SMP=y
-CONFIG_X86_HT=y
-CONFIG_X86_BIOS_REBOOT=y
-CONFIG_X86_TRAMPOLINE=y
-CONFIG_TUX=m
-CONFIG_NVRAM=m
-CONFIG_IBM_ASM=m
-CONFIG_CRYPTO_AES_586=m
-CONFIG_GENERIC_ISA_DMA=y
-CONFIG_SCHED_SMT=y
-# CONFIG_IRQBALANCE is not set
-# CONFIG_SOFTWARE_SUSPEND is not set
-# CONFIG_PM_DEBUG is not set
-CONFIG_CPU_FREQ=y
-# CONFIG_CPU_FREQ_PROC_INTF is not set
-# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set
-CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
-CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
-CONFIG_CPU_FREQ_GOV_POWERSAVE=m
-CONFIG_CPU_FREQ_GOV_USERSPACE=m
-CONFIG_CPU_FREQ_GOV_ONDEMAND=m
-# CONFIG_CPU_FREQ_24_API is not set
-CONFIG_CPU_FREQ_TABLE=y
-CONFIG_DEBUG_STACKOVERFLOW=y
-CONFIG_DEBUG_STACK_USAGE=y
-# CONFIG_DEBUG_PAGEALLOC is not set
-# CONFIG_KPROBES is not set
-CONFIG_KEXEC=y
-CONFIG_NETDUMP=m
-# CONFIG_SCHEDSTATS is not set
-CONFIG_PCI_DIRECT=y
-CONFIG_PCI_MMCONFIG=y
-CONFIG_PCI_BIOS=y
-CONFIG_HOTPLUG_PCI=y
-# CONFIG_HOTPLUG_PCI_FAKE is not set
-CONFIG_HOTPLUG_PCI_COMPAQ=m
-# CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM is not set
-CONFIG_HOTPLUG_PCI_IBM=m
-CONFIG_HOTPLUG_PCI_ACPI=m 
-CONFIG_HOTPLUG_PCI_ACPI_IBM=m
-# CONFIG_HOTPLUG_PCI_CPCI is not set
-CONFIG_HOTPLUG_PCI_PCIE=m
-# CONFIG_HOTPLUG_PCI_PCIE_POLL_EVENT_MODE is not set
-CONFIG_HOTPLUG_PCI_SHPC=m
-# CONFIG_HOTPLUG_PCI_SHPC_POLL_EVENT_MODE is not set
-# CONFIG_HOTPLUG_PCI_SHPC_PHPRM_LEGACY is not set
-CONFIG_PM=y
-CONFIG_IEEE80211=m
-# CONFIG_IEEE80211_DEBUG is not set
-CONFIG_IEEE80211_CRYPT=m
-CONFIG_IEEE80211_WPA=m
-CONFIG_IEEE80211_CRYPT_CCMP=m
-CONFIG_IEEE80211_CRYPT_TKIP=m
-CONFIG_IPW2100=m
-# CONFIG_IPW_DEBUG is not set
-CONFIG_IPW2100_PROMISC=y
-# CONFIG_IPW2100_LEGACY_FW_LOAD is not set
-CONFIG_IPW2200=m
-CONFIG_M686=y
-# CONFIG_NOHIGHMEM is not set
-# CONFIG_SMP is not set
-CONFIG_HIGHMEM4G=y
-# CONFIG_HIGHMEM64G is not set
-# CONFIG_PROFILING is not set
-# CONFIG_OPROFILE is not set
diff --git a/lustre/kernel_patches/kernel_configs/uml-2.6.10-fc3.config b/lustre/kernel_patches/kernel_configs/uml-2.6.10-fc3.config
new file mode 100644 (file)
index 0000000..30aab18
--- /dev/null
@@ -0,0 +1,659 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.10-ac12
+# Tue Jun 14 08:15:05 2005
+#
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_USERMODE=y
+CONFIG_MMU=y
+CONFIG_UID16=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+
+#
+# UML-specific options
+#
+CONFIG_MODE_TT=y
+CONFIG_MODE_SKAS=y
+CONFIG_NET=y
+CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_MISC=y
+CONFIG_HOSTFS=y
+CONFIG_MCONSOLE=y
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_HOST_2G_2G is not set
+# CONFIG_SMP is not set
+CONFIG_NEST_LEVEL=0
+CONFIG_KERNEL_HALF_GIGS=1
+CONFIG_KERNEL_STACK_ORDER=4
+CONFIG_UML_REAL_TIME_CLOCK=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_CLEAN_COMPILE=y
+CONFIG_BROKEN_ON_SMP=y
+
+#
+# General setup
+#
+CONFIG_LOCALVERSION=""
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_BSD_PROCESS_ACCT=y
+# CONFIG_BSD_PROCESS_ACCT_V3 is not set
+CONFIG_SYSCTL=y
+CONFIG_AUDIT=y
+CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_HOTPLUG is not set
+CONFIG_KOBJECT_UEVENT=y
+# CONFIG_IKCONFIG is not set
+# CONFIG_EMBEDDED is not set
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+CONFIG_KALLSYMS_EXTRA_PASS=y
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SHMEM=y
+CONFIG_CC_ALIGN_FUNCTIONS=0
+CONFIG_CC_ALIGN_LABELS=0
+CONFIG_CC_ALIGN_LOOPS=0
+CONFIG_CC_ALIGN_JUMPS=0
+# CONFIG_TINY_SHMEM is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+CONFIG_OBSOLETE_MODPARM=y
+CONFIG_MODVERSIONS=y
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+# CONFIG_MODULE_SIG is not set
+CONFIG_KMOD=y
+
+#
+# Generic Driver Options
+#
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+# CONFIG_DEBUG_DRIVER is not set
+
+#
+# Character Devices
+#
+CONFIG_STDIO_CONSOLE=y
+CONFIG_SSL=y
+CONFIG_FD_CHAN=y
+CONFIG_NULL_CHAN=y
+CONFIG_PORT_CHAN=y
+CONFIG_PTY_CHAN=y
+CONFIG_TTY_CHAN=y
+CONFIG_XTERM_CHAN=y
+# CONFIG_NOCONFIG_CHAN is not set
+CONFIG_CON_ZERO_CHAN="fd:0,fd:1"
+CONFIG_CON_CHAN="xterm"
+CONFIG_SSL_CHAN="pty"
+CONFIG_UNIX98_PTYS=y
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_WATCHDOG=y
+# CONFIG_WATCHDOG_NOWAYOUT is not set
+CONFIG_SOFT_WATCHDOG=m
+# CONFIG_UML_WATCHDOG is not set
+# CONFIG_UML_SOUND is not set
+# CONFIG_SOUND is not set
+# CONFIG_HOSTAUDIO is not set
+
+#
+# Block Devices
+#
+CONFIG_BLK_DEV_UBD=y
+# CONFIG_BLK_DEV_UBD_SYNC is not set
+CONFIG_BLK_DEV_COW_COMMON=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_NBD=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=16384
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+CONFIG_NETDEVICES=y
+
+#
+# UML Network Devices
+#
+CONFIG_UML_NET=y
+CONFIG_UML_NET_ETHERTAP=y
+CONFIG_UML_NET_TUNTAP=y
+# CONFIG_UML_NET_SLIP is not set
+# CONFIG_UML_NET_DAEMON is not set
+# CONFIG_UML_NET_MCAST is not set
+# CONFIG_UML_NET_SLIRP is not set
+
+#
+# Networking support
+#
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+CONFIG_PACKET_MMAP=y
+CONFIG_NETLINK_DEV=y
+CONFIG_UNIX=y
+CONFIG_NET_KEY=m
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_ROUTE_FWMARK=y
+CONFIG_IP_ROUTE_MULTIPATH=y
+CONFIG_IP_ROUTE_VERBOSE=y
+# CONFIG_IP_PNP is not set
+CONFIG_NET_IPIP=m
+CONFIG_NET_IPGRE=m
+CONFIG_NET_IPGRE_BROADCAST=y
+CONFIG_IP_MROUTE=y
+CONFIG_IP_PIMSM_V1=y
+CONFIG_IP_PIMSM_V2=y
+# CONFIG_ARPD is not set
+CONFIG_SYN_COOKIES=y
+CONFIG_INET_AH=m
+CONFIG_INET_ESP=m
+CONFIG_INET_IPCOMP=m
+CONFIG_INET_TUNNEL=m
+CONFIG_IP_TCPDIAG=y
+# CONFIG_IP_TCPDIAG_IPV6 is not set
+
+#
+# IP: Virtual Server Configuration
+#
+CONFIG_IP_VS=m
+# CONFIG_IP_VS_DEBUG is not set
+CONFIG_IP_VS_TAB_BITS=12
+
+#
+# IPVS transport protocol load balancing support
+#
+CONFIG_IP_VS_PROTO_TCP=y
+CONFIG_IP_VS_PROTO_UDP=y
+CONFIG_IP_VS_PROTO_ESP=y
+CONFIG_IP_VS_PROTO_AH=y
+
+#
+# IPVS scheduler
+#
+CONFIG_IP_VS_RR=m
+CONFIG_IP_VS_WRR=m
+CONFIG_IP_VS_LC=m
+CONFIG_IP_VS_WLC=m
+CONFIG_IP_VS_LBLC=m
+CONFIG_IP_VS_LBLCR=m
+CONFIG_IP_VS_DH=m
+CONFIG_IP_VS_SH=m
+CONFIG_IP_VS_SED=m
+CONFIG_IP_VS_NQ=m
+
+#
+# IPVS application helper
+#
+CONFIG_IP_VS_FTP=m
+CONFIG_IPV6=m
+CONFIG_IPV6_PRIVACY=y
+CONFIG_INET6_AH=m
+CONFIG_INET6_ESP=m
+CONFIG_INET6_IPCOMP=m
+CONFIG_INET6_TUNNEL=m
+CONFIG_IPV6_TUNNEL=m
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+CONFIG_BRIDGE_NETFILTER=y
+
+#
+# IP: Netfilter Configuration
+#
+CONFIG_IP_NF_CONNTRACK=m
+CONFIG_IP_NF_CT_ACCT=y
+# CONFIG_IP_NF_CONNTRACK_MARK is not set
+CONFIG_IP_NF_CT_PROTO_SCTP=m
+CONFIG_IP_NF_FTP=m
+CONFIG_IP_NF_IRC=m
+CONFIG_IP_NF_TFTP=m
+CONFIG_IP_NF_AMANDA=m
+CONFIG_IP_NF_QUEUE=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_LIMIT=m
+CONFIG_IP_NF_MATCH_IPRANGE=m
+CONFIG_IP_NF_MATCH_MAC=m
+CONFIG_IP_NF_MATCH_PKTTYPE=m
+CONFIG_IP_NF_MATCH_MARK=m
+CONFIG_IP_NF_MATCH_MULTIPORT=m
+CONFIG_IP_NF_MATCH_TOS=m
+CONFIG_IP_NF_MATCH_RECENT=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_DSCP=m
+CONFIG_IP_NF_MATCH_AH_ESP=m
+CONFIG_IP_NF_MATCH_LENGTH=m
+CONFIG_IP_NF_MATCH_TTL=m
+CONFIG_IP_NF_MATCH_TCPMSS=m
+CONFIG_IP_NF_MATCH_HELPER=m
+CONFIG_IP_NF_MATCH_STATE=m
+CONFIG_IP_NF_MATCH_CONNTRACK=m
+CONFIG_IP_NF_MATCH_OWNER=m
+CONFIG_IP_NF_MATCH_PHYSDEV=m
+CONFIG_IP_NF_MATCH_ADDRTYPE=m
+CONFIG_IP_NF_MATCH_REALM=m
+CONFIG_IP_NF_MATCH_SCTP=m
+CONFIG_IP_NF_MATCH_COMMENT=m
+# CONFIG_IP_NF_MATCH_HASHLIMIT is not set
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_LOG=m
+CONFIG_IP_NF_TARGET_ULOG=m
+CONFIG_IP_NF_TARGET_TCPMSS=m
+CONFIG_IP_NF_NAT=m
+CONFIG_IP_NF_NAT_NEEDED=y
+CONFIG_IP_NF_TARGET_MASQUERADE=m
+CONFIG_IP_NF_TARGET_REDIRECT=m
+CONFIG_IP_NF_TARGET_NETMAP=m
+CONFIG_IP_NF_TARGET_SAME=m
+CONFIG_IP_NF_NAT_LOCAL=y
+CONFIG_IP_NF_NAT_SNMP_BASIC=m
+CONFIG_IP_NF_NAT_IRC=m
+CONFIG_IP_NF_NAT_FTP=m
+CONFIG_IP_NF_NAT_TFTP=m
+CONFIG_IP_NF_NAT_AMANDA=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_TOS=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_DSCP=m
+CONFIG_IP_NF_TARGET_MARK=m
+CONFIG_IP_NF_TARGET_CLASSIFY=m
+CONFIG_IP_NF_RAW=m
+CONFIG_IP_NF_TARGET_NOTRACK=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+# CONFIG_IP_NF_COMPAT_IPCHAINS is not set
+# CONFIG_IP_NF_COMPAT_IPFWADM is not set
+
+#
+# IPv6: Netfilter Configuration
+#
+# CONFIG_IP6_NF_QUEUE is not set
+CONFIG_IP6_NF_IPTABLES=m
+CONFIG_IP6_NF_MATCH_LIMIT=m
+CONFIG_IP6_NF_MATCH_MAC=m
+CONFIG_IP6_NF_MATCH_RT=m
+CONFIG_IP6_NF_MATCH_OPTS=m
+CONFIG_IP6_NF_MATCH_FRAG=m
+CONFIG_IP6_NF_MATCH_HL=m
+CONFIG_IP6_NF_MATCH_MULTIPORT=m
+CONFIG_IP6_NF_MATCH_OWNER=m
+CONFIG_IP6_NF_MATCH_MARK=m
+CONFIG_IP6_NF_MATCH_IPV6HEADER=m
+CONFIG_IP6_NF_MATCH_AHESP=m
+CONFIG_IP6_NF_MATCH_LENGTH=m
+CONFIG_IP6_NF_MATCH_EUI64=m
+CONFIG_IP6_NF_MATCH_PHYSDEV=m
+CONFIG_IP6_NF_FILTER=m
+CONFIG_IP6_NF_TARGET_LOG=m
+CONFIG_IP6_NF_MANGLE=m
+CONFIG_IP6_NF_TARGET_MARK=m
+CONFIG_IP6_NF_RAW=m
+
+#
+# Bridge: Netfilter Configuration
+#
+CONFIG_BRIDGE_NF_EBTABLES=m
+CONFIG_BRIDGE_EBT_BROUTE=m
+CONFIG_BRIDGE_EBT_T_FILTER=m
+CONFIG_BRIDGE_EBT_T_NAT=m
+CONFIG_BRIDGE_EBT_802_3=m
+CONFIG_BRIDGE_EBT_AMONG=m
+CONFIG_BRIDGE_EBT_ARP=m
+CONFIG_BRIDGE_EBT_IP=m
+CONFIG_BRIDGE_EBT_LIMIT=m
+CONFIG_BRIDGE_EBT_MARK=m
+CONFIG_BRIDGE_EBT_PKTTYPE=m
+CONFIG_BRIDGE_EBT_STP=m
+CONFIG_BRIDGE_EBT_VLAN=m
+CONFIG_BRIDGE_EBT_ARPREPLY=m
+CONFIG_BRIDGE_EBT_DNAT=m
+CONFIG_BRIDGE_EBT_MARK_T=m
+CONFIG_BRIDGE_EBT_REDIRECT=m
+CONFIG_BRIDGE_EBT_SNAT=m
+CONFIG_BRIDGE_EBT_LOG=m
+CONFIG_XFRM=y
+CONFIG_XFRM_USER=y
+
+#
+# SCTP Configuration (EXPERIMENTAL)
+#
+CONFIG_IP_SCTP=m
+# CONFIG_SCTP_DBG_MSG is not set
+# CONFIG_SCTP_DBG_OBJCNT is not set
+# CONFIG_SCTP_HMAC_NONE is not set
+# CONFIG_SCTP_HMAC_SHA1 is not set
+CONFIG_SCTP_HMAC_MD5=y
+CONFIG_ATM=m
+CONFIG_ATM_CLIP=m
+# CONFIG_ATM_CLIP_NO_ICMP is not set
+CONFIG_ATM_LANE=m
+# CONFIG_ATM_MPOA is not set
+CONFIG_ATM_BR2684=m
+# CONFIG_ATM_BR2684_IPFILTER is not set
+CONFIG_BRIDGE=m
+CONFIG_VLAN_8021Q=m
+# CONFIG_DECNET is not set
+CONFIG_LLC=m
+# CONFIG_LLC2 is not set
+CONFIG_IPX=m
+# CONFIG_IPX_INTERN is not set
+CONFIG_ATALK=m
+CONFIG_DEV_APPLETALK=y
+CONFIG_IPDDP=m
+CONFIG_IPDDP_ENCAP=y
+CONFIG_IPDDP_DECAP=y
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+CONFIG_NET_DIVERT=y
+# CONFIG_ECONET is not set
+CONFIG_WAN_ROUTER=m
+
+#
+# QoS and/or fair queueing
+#
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_CLK_JIFFIES=y
+# CONFIG_NET_SCH_CLK_GETTIMEOFDAY is not set
+# CONFIG_NET_SCH_CLK_CPU is not set
+CONFIG_NET_SCH_CBQ=m
+CONFIG_NET_SCH_HTB=m
+CONFIG_NET_SCH_HFSC=m
+CONFIG_NET_SCH_ATM=m
+CONFIG_NET_SCH_PRIO=m
+CONFIG_NET_SCH_RED=m
+CONFIG_NET_SCH_SFQ=m
+CONFIG_NET_SCH_TEQL=m
+CONFIG_NET_SCH_TBF=m
+CONFIG_NET_SCH_GRED=m
+CONFIG_NET_SCH_DSMARK=m
+CONFIG_NET_SCH_NETEM=m
+CONFIG_NET_SCH_INGRESS=m
+CONFIG_NET_QOS=y
+CONFIG_NET_ESTIMATOR=y
+CONFIG_NET_CLS=y
+CONFIG_NET_CLS_TCINDEX=m
+CONFIG_NET_CLS_ROUTE4=m
+CONFIG_NET_CLS_ROUTE=y
+CONFIG_NET_CLS_FW=m
+CONFIG_NET_CLS_U32=m
+CONFIG_CLS_U32_PERF=y
+CONFIG_NET_CLS_IND=y
+CONFIG_NET_CLS_RSVP=m
+CONFIG_NET_CLS_RSVP6=m
+# CONFIG_NET_CLS_ACT is not set
+CONFIG_NET_CLS_POLICE=y
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+# CONFIG_TUX is not set
+CONFIG_DUMMY=m
+# CONFIG_BONDING is not set
+# CONFIG_EQUALIZER is not set
+CONFIG_TUN=m
+CONFIG_ETHERTAP=m
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=m
+
+#
+# Ethernet (1000 Mbit)
+#
+
+#
+# Ethernet (10000 Mbit)
+#
+
+#
+# Token Ring devices
+#
+
+#
+# Wireless LAN (non-hamradio)
+#
+# CONFIG_NET_RADIO is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+
+#
+# ATM drivers
+#
+# CONFIG_ATM_TCP is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_SHAPER is not set
+# CONFIG_NETCONSOLE is not set
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT2_FS_POSIX_ACL=y
+CONFIG_EXT2_FS_SECURITY=y
+CONFIG_EXT3_FS=y
+CONFIG_EXT3_FS_XATTR=y
+CONFIG_EXT3_FS_POSIX_ACL=y
+CONFIG_EXT3_FS_SECURITY=y
+CONFIG_JBD=y
+# CONFIG_JBD_DEBUG is not set
+CONFIG_FS_MBCACHE=y
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+CONFIG_FS_POSIX_ACL=y
+# CONFIG_XFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_QUOTA is not set
+CONFIG_DNOTIFY=y
+# CONFIG_AUTOFS_FS is not set
+CONFIG_AUTOFS4_FS=m
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+# CONFIG_MSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_SYSFS=y
+# CONFIG_DEVFS_FS is not set
+CONFIG_DEVPTS_FS_XATTR=y
+CONFIG_DEVPTS_FS_SECURITY=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_XATTR is not set
+# CONFIG_HUGETLB_PAGE is not set
+CONFIG_RAMFS=y
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+
+#
+# Network File Systems
+#
+CONFIG_NFS_FS=m
+CONFIG_NFS_V3=y
+CONFIG_NFS_V4=y
+CONFIG_NFS_DIRECTIO=y
+CONFIG_NFSD=y
+CONFIG_NFSD_V3=y
+CONFIG_NFSD_V4=y
+CONFIG_NFSD_TCP=y
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+CONFIG_EXPORTFS=y
+CONFIG_SUNRPC=y
+CONFIG_SUNRPC_GSS=y
+CONFIG_RPCSEC_GSS_KRB5=y
+CONFIG_RPCSEC_GSS_SPKM3=y
+# CONFIG_SMB_FS is not set
+# CONFIG_CIFS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+CONFIG_OSF_PARTITION=y
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+CONFIG_MAC_PARTITION=y
+CONFIG_MSDOS_PARTITION=y
+CONFIG_BSD_DISKLABEL=y
+CONFIG_MINIX_SUBPARTITION=y
+CONFIG_SOLARIS_X86_PARTITION=y
+CONFIG_UNIXWARE_DISKLABEL=y
+# CONFIG_LDM_PARTITION is not set
+CONFIG_SGI_PARTITION=y
+# CONFIG_ULTRIX_PARTITION is not set
+CONFIG_SUN_PARTITION=y
+CONFIG_EFI_PARTITION=y
+
+#
+# Native Language Support
+#
+# CONFIG_NLS is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+
+#
+# Cryptographic options
+#
+CONFIG_CRYPTO=y
+CONFIG_CRYPTO_HMAC=y
+CONFIG_CRYPTO_NULL=m
+CONFIG_CRYPTO_MD4=m
+CONFIG_CRYPTO_MD5=y
+CONFIG_CRYPTO_SHA1=y
+CONFIG_CRYPTO_SHA256=m
+CONFIG_CRYPTO_SHA512=m
+CONFIG_CRYPTO_WP512=m
+CONFIG_CRYPTO_DES=y
+CONFIG_CRYPTO_BLOWFISH=m
+CONFIG_CRYPTO_TWOFISH=m
+CONFIG_CRYPTO_SERPENT=m
+# CONFIG_CRYPTO_AES is not set
+CONFIG_CRYPTO_CAST5=m
+CONFIG_CRYPTO_CAST6=m
+CONFIG_CRYPTO_TEA=m
+CONFIG_CRYPTO_ARC4=m
+CONFIG_CRYPTO_KHAZAD=m
+# CONFIG_CRYPTO_ANUBIS is not set
+CONFIG_CRYPTO_DEFLATE=m
+CONFIG_CRYPTO_MICHAEL_MIC=m
+CONFIG_CRYPTO_CRC32C=m
+# CONFIG_CRYPTO_TEST is not set
+# CONFIG_CRYPTO_SIGNATURE is not set
+# CONFIG_CRYPTO_MPILIB is not set
+
+#
+# Library routines
+#
+CONFIG_CRC_CCITT=m
+CONFIG_CRC32=y
+CONFIG_LIBCRC32C=m
+CONFIG_ZLIB_INFLATE=m
+CONFIG_ZLIB_DEFLATE=m
+
+#
+# Multi-device support (RAID and LVM)
+#
+CONFIG_MD=y
+CONFIG_BLK_DEV_MD=y
+CONFIG_MD_LINEAR=m
+CONFIG_MD_RAID0=m
+CONFIG_MD_RAID1=m
+CONFIG_MD_RAID10=m
+CONFIG_MD_RAID5=m
+CONFIG_MD_RAID6=m
+CONFIG_MD_MULTIPATH=m
+# CONFIG_MD_FAULTY is not set
+CONFIG_BLK_DEV_DM=m
+CONFIG_DM_CRYPT=m
+CONFIG_DM_SNAPSHOT=m
+CONFIG_DM_MIRROR=m
+CONFIG_DM_ZERO=m
+# CONFIG_INPUT is not set
+
+#
+# Kernel hacking
+#
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_SCHEDSTATS is not set
+# CONFIG_DEBUG_SLAB is not set
+CONFIG_DEBUG_SPINLOCK=y
+# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
+# CONFIG_DEBUG_KOBJECT is not set
+CONFIG_DEBUG_INFO=y
+CONFIG_FRAME_POINTER=y
+CONFIG_PT_PROXY=y
+# CONFIG_GPROF is not set
+# CONFIG_GCOV is not set
index 8ffa9c8..7c3217a 100644 (file)
@@ -36,7 +36,7 @@ CONFIG_NEST_LEVEL=0
 CONFIG_KERNEL_HALF_GIGS=1
 # CONFIG_HIGHMEM is not set
 CONFIG_PROC_MM=y
-CONFIG_KERNEL_STACK_ORDER=2
+CONFIG_KERNEL_STACK_ORDER=4
 CONFIG_UML_REAL_TIME_CLOCK=y
 
 #
index 9349422..e863bd6 100644 (file)
@@ -1,6 +1,8 @@
---- ./drivers/addon/qla2200/qla2x00.h  2004-07-26 12:52:08.000000000 +0100
-+++ ./drivers/addon/qla2200/qla2x00.h  2004-07-26 12:58:42.000000000 +0100
-@@ -3208,7 +3208,7 @@ void qla2x00_setup(char *s);
+Index: linux-2.4.21/drivers/addon/qla2200/qla2x00.h
+===================================================================
+--- linux-2.4.21.orig/drivers/addon/qla2200/qla2x00.h  2005-06-01 22:51:57.000000000 -0400
++++ linux-2.4.21/drivers/addon/qla2200/qla2x00.h       2005-06-01 23:06:10.592857440 -0400
+@@ -3275,7 +3275,7 @@
  /* Kernel version specific template additions */
  
  /* Number of segments 1 - 65535 */
@@ -9,7 +11,7 @@
  
  /*
   * Scsi_Host_template (see hosts.h) 
-@@ -3222,7 +3222,7 @@ void qla2x00_setup(char *s);
+@@ -3289,7 +3289,7 @@
   *
   */
  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,8)
  #else
  #define TEMPLATE_MAX_SECTORS 
  #endif
---- ./include/linux/blkdev.h   2004-07-26 12:53:11.000000000 +0100
-+++ ./include/linux/blkdev.h   2004-07-26 13:12:42.000000000 +0100
-@@ -255,9 +255,9 @@ extern int * max_segments[MAX_BLKDEV];
+Index: linux-2.4.21/include/linux/blkdev.h
+===================================================================
+--- linux-2.4.21.orig/include/linux/blkdev.h   2005-06-01 22:51:55.000000000 -0400
++++ linux-2.4.21/include/linux/blkdev.h        2005-06-01 23:07:26.186365480 -0400
+@@ -262,10 +262,10 @@
  
  extern char * blkdev_varyio[MAX_BLKDEV];
  
 -#define MAX_SEGMENTS 128
 +#define MAX_SEGMENTS 256
  #define MAX_SECTORS 255
+ /* General-case limit for superbh size: */
 -#define MAX_SUPERBH 32768     /* must fit info ->b_size right now */
-+#define MAX_SUPERBH (1<<20)
++#define MAX_SUPERBH (1<<20)   /* must fit info ->b_size right now */
  
- /*
-  * bh abuse :/
---- ./mm/highmem.c.orig        2004-09-11 08:16:19.000000000 -0600
-+++ ./mm/highmem.c     2004-10-06 11:52:34.000000000 -0600
-@@ -465,7 +465,7 @@ struct buffer_head * create_bounce(int r
+ /* Limit for superbh when we're certain it cannot be bounce-buffered: */
+ #define MAX_SUPERBH_NOBOUNCE (1024*1024) /* must fit info ->b_size right now */
+Index: linux-2.4.21/mm/highmem.c
+===================================================================
+--- linux-2.4.21.orig/mm/highmem.c     2005-06-01 22:51:50.000000000 -0400
++++ linux-2.4.21/mm/highmem.c  2005-06-01 23:06:10.594857136 -0400
+@@ -474,7 +474,7 @@
  /*
   * FIXME: assuming PAGE_SIZE buffer_heads
   */
index 52d168b..c6b38ab 100644 (file)
@@ -15,11 +15,11 @@ diff -ur linux-2.6.5-lnxi.orig/drivers/block/ll_rw_blk.c linux-2.6.5-lnxi/driver
                /* this is cfs's dev_rdonly check */
                if (bio->bi_rw == WRITE &&
 -                              dev_check_rdonly(bio->bi_bdev->bd_dev)) {
-+                              dev_check_rdonly(bio->bi_bdev)) {
++                              dev_check_rdonly(bio->bi_bdev)) {
                        bio_endio(bio, bio->bi_size, 0);
                        break;
                }
-@@ -3086,53 +3086,85 @@ void swap_io_context(struct io_context *
+@@ -3086,53 +3086,86 @@ void swap_io_context(struct io_context *
        *ioc2 = temp;
  }
  
@@ -33,8 +33,8 @@ diff -ur linux-2.6.5-lnxi.orig/drivers/block/ll_rw_blk.c linux-2.6.5-lnxi/driver
   */
 -void dev_set_rdonly(struct block_device *bdev, int no_write)
 +struct deventry {
-+        dev_t dev;
-+        struct deventry *next;
++      dev_t dev;
++      struct deventry *next;
 +};
 +
 +static struct deventry *devlist = NULL;
@@ -46,14 +46,14 @@ diff -ur linux-2.6.5-lnxi.orig/drivers/block/ll_rw_blk.c linux-2.6.5-lnxi/driver
 -              printk(KERN_ALERT "%s:%d illegal arg %d (max %d)\n",
 -                              __FILE__, __LINE__, no_write, MAX_RDONLY_DEVS);
 -              return;
-+        struct deventry *cur;
-+        if (!bdev) return 0;
-+        spin_lock(&devlock);
-+        cur = devlist;
-+        while(cur) {
-+                if (bdev->bd_dev == cur->dev) {
-+                        spin_unlock(&devlock);
-+                        return 1;
++      struct deventry *cur;
++      if (!bdev) return 0;
++      spin_lock(&devlock);
++      cur = devlist;
++      while(cur) {
++              if (bdev->bd_dev == cur->dev) {
++                      spin_unlock(&devlock);
++                      return 1;
        }
 -
 -      if (bdev) {
@@ -61,10 +61,10 @@ diff -ur linux-2.6.5-lnxi.orig/drivers/block/ll_rw_blk.c linux-2.6.5-lnxi/driver
 -                              bdev->bd_disk ? bdev->bd_disk->disk_name : "?",
 -                              no_write);
 -              rdonly_devs[no_write] = bdev->bd_dev;
-+                cur = cur->next;
++              cur = cur->next;
        }
-+        spin_unlock(&devlock);
-+        return 0;
++      spin_unlock(&devlock);
++      return 0;
  }
  
 -void dev_clear_rdonly(int no_write)
@@ -75,13 +75,13 @@ diff -ur linux-2.6.5-lnxi.orig/drivers/block/ll_rw_blk.c linux-2.6.5-lnxi/driver
 -                              __FILE__, __LINE__, no_write, MAX_RDONLY_DEVS);
 -              return;
 -      }
-+        struct deventry *newdev, *cur;
++      struct deventry *newdev, *cur;
  
 -      if (rdonly_devs[no_write] == 0)
-+        if (!bdev) 
-+              return;
-+        newdev = kmalloc(sizeof(struct deventry), GFP_KERNEL);
-+        if (!newdev) 
++      if (!bdev) 
++              return;
++      newdev = kmalloc(sizeof(struct deventry), GFP_KERNEL);
++      if (!newdev) 
                return;
        
 -      printk(KERN_WARNING "Clearing read-only at %d\n", no_write);
@@ -96,46 +96,47 @@ diff -ur linux-2.6.5-lnxi.orig/drivers/block/ll_rw_blk.c linux-2.6.5-lnxi/driver
 -              if (rdonly_devs[i] == dev)
 -                      return 1;
 -      return 0;
-+        spin_lock(&devlock);
-+        cur = devlist;
-+        while(cur) {
-+                if (bdev->bd_dev == cur->dev) {
-+                        spin_unlock(&devlock);
-+                        kfree(newdev);
-+                        return;
-+}
-+                cur = cur->next;
-+        }
-+        newdev->dev = bdev->bd_dev;
-+        newdev->next = devlist;
-+        devlist = newdev;
-+        spin_unlock(&devlock);
-+        printk(KERN_WARNING "Turning device %s read-only\n",
-+               bdev->bd_disk ? bdev->bd_disk->disk_name : "?");
++      spin_lock(&devlock);
++      cur = devlist;
++      while(cur) {
++              if (bdev->bd_dev == cur->dev) {
++                      spin_unlock(&devlock);
++                      kfree(newdev);
++                      return;
++              }
++              cur = cur->next;
++      }
++      newdev->dev = bdev->bd_dev;
++      newdev->next = devlist;
++      devlist = newdev;
++      spin_unlock(&devlock);
++      printk(KERN_WARNING "Turning device %s (%#x) read-only\n",
++             bdev->bd_disk ? bdev->bd_disk->disk_name : "", bdev->bd_dev);
 +}
 +
 +void dev_clear_rdonly(struct block_device *bdev) 
 +{
-+        struct deventry *cur, *last = NULL;
-+        if (!bdev) return;
-+        spin_lock(&devlock);
-+        cur = devlist;
-+        while(cur) {
-+                if (bdev->bd_dev == cur->dev) {
-+                        if (last) 
-+                                last->next = cur->next;
-+                        else
-+                                devlist = cur->next;
-+                        spin_unlock(&devlock);
-+                        kfree(cur);
-+                        printk(KERN_WARNING "Removing read-only on %s\n",
-+                             bdev->bd_disk ? bdev->bd_disk->disk_name : "?");
++      struct deventry *cur, *last = NULL;
++      if (!bdev) return;
++      spin_lock(&devlock);
++      cur = devlist;
++      while(cur) {
++              if (bdev->bd_dev == cur->dev) {
++                      if (last) 
++                              last->next = cur->next;
++                      else
++                              devlist = cur->next;
++                      spin_unlock(&devlock);
++                      kfree(cur);
++                      printk(KERN_WARNING "Removing read-only on %s (%#x)\n",
++                             bdev->bd_disk ? bdev->bd_disk->disk_name :
++                                             "unknown block", bdev->bd_dev);
 +                      return;
-+                }
-+                last = cur;
-+                cur = cur->next;
-+        }
-+        spin_unlock(&devlock);
++              }
++              last = cur;
++              cur = cur->next;
++      }
++      spin_unlock(&devlock);
  }
  
  EXPORT_SYMBOL(dev_set_rdonly);
index ce244ae..d5a5ac4 100644 (file)
@@ -16,7 +16,7 @@ diff -ur linux-2.6.5-lnxi.orig/drivers/block/ll_rw_blk.c linux-2.6.5-lnxi/driver
  
 +              /* this is cfs's dev_rdonly check */
 +              if (bio->bi_rw == WRITE &&
-+                              dev_check_rdonly(bio->bi_bdev)) {
++                              dev_check_rdonly(bio->bi_bdev)) {
 +                      bio_endio(bio, bio->bi_size, 0);
 +                      break;
 +              }
@@ -24,7 +24,7 @@ diff -ur linux-2.6.5-lnxi.orig/drivers/block/ll_rw_blk.c linux-2.6.5-lnxi/driver
                /*
                 * If this device has partitions, remap block n
                 * of partition p to block n+start(p) of the disk.
-@@ -3078,6 +3087,91 @@ void swap_io_context(struct io_context *
+@@ -3078,6 +3087,92 @@ void swap_io_context(struct io_context *
  }
  
  /*
@@ -32,8 +32,8 @@ diff -ur linux-2.6.5-lnxi.orig/drivers/block/ll_rw_blk.c linux-2.6.5-lnxi/driver
 + * silently).  This is for filesystem crash/recovery testing.
 + */
 +struct deventry {
-+        dev_t dev;
-+        struct deventry *next;
++      dev_t dev;
++      struct deventry *next;
 +};
 +
 +static struct deventry *devlist = NULL;
@@ -41,71 +41,72 @@ diff -ur linux-2.6.5-lnxi.orig/drivers/block/ll_rw_blk.c linux-2.6.5-lnxi/driver
 +
 +int dev_check_rdonly(struct block_device *bdev) 
 +{
-+        struct deventry *cur;
-+        if (!bdev) return 0;
-+        spin_lock(&devlock);
-+        cur = devlist;
-+        while(cur) {
-+                if (bdev->bd_dev == cur->dev) {
-+                        spin_unlock(&devlock);
-+                        return 1;
++      struct deventry *cur;
++      if (!bdev) return 0;
++      spin_lock(&devlock);
++      cur = devlist;
++      while(cur) {
++              if (bdev->bd_dev == cur->dev) {
++                      spin_unlock(&devlock);
++                      return 1;
 +      }
-+                cur = cur->next;
++              cur = cur->next;
 +      }
-+        spin_unlock(&devlock);
-+        return 0;
++      spin_unlock(&devlock);
++      return 0;
 +}
 +
 +void dev_set_rdonly(struct block_device *bdev)
 +{
-+        struct deventry *newdev, *cur;
++      struct deventry *newdev, *cur;
 +
-+        if (!bdev) 
-+              return;
-+        newdev = kmalloc(sizeof(struct deventry), GFP_KERNEL);
-+        if (!newdev) 
++      if (!bdev) 
++              return;
++      newdev = kmalloc(sizeof(struct deventry), GFP_KERNEL);
++      if (!newdev) 
 +              return;
 +      
-+        spin_lock(&devlock);
-+        cur = devlist;
-+        while(cur) {
-+                if (bdev->bd_dev == cur->dev) {
-+                        spin_unlock(&devlock);
-+                        kfree(newdev);
-+                        return;
-+}
-+                cur = cur->next;
-+        }
-+        newdev->dev = bdev->bd_dev;
-+        newdev->next = devlist;
-+        devlist = newdev;
-+        spin_unlock(&devlock);
-+        printk(KERN_WARNING "Turning device %s read-only\n",
-+               bdev->bd_disk ? bdev->bd_disk->disk_name : "?");
++      spin_lock(&devlock);
++      cur = devlist;
++      while(cur) {
++              if (bdev->bd_dev == cur->dev) {
++                      spin_unlock(&devlock);
++                      kfree(newdev);
++                      return;
++              }
++              cur = cur->next;
++      }
++      newdev->dev = bdev->bd_dev;
++      newdev->next = devlist;
++      devlist = newdev;
++      spin_unlock(&devlock);
++      printk(KERN_WARNING "Turning device %s (%#x) read-only\n",
++             bdev->bd_disk ? bdev->bd_disk->disk_name : "", bdev->bd_dev);
 +}
 +
 +void dev_clear_rdonly(struct block_device *bdev) 
 +{
-+        struct deventry *cur, *last = NULL;
-+        if (!bdev) return;
-+        spin_lock(&devlock);
-+        cur = devlist;
-+        while(cur) {
-+                if (bdev->bd_dev == cur->dev) {
-+                        if (last) 
-+                                last->next = cur->next;
-+                        else
-+                                devlist = cur->next;
-+                        spin_unlock(&devlock);
-+                        kfree(cur);
-+                        printk(KERN_WARNING "Removing read-only on %s\n",
-+                             bdev->bd_disk ? bdev->bd_disk->disk_name : "?");
++      struct deventry *cur, *last = NULL;
++      if (!bdev) return;
++      spin_lock(&devlock);
++      cur = devlist;
++      while(cur) {
++              if (bdev->bd_dev == cur->dev) {
++                      if (last) 
++                              last->next = cur->next;
++                      else
++                              devlist = cur->next;
++                      spin_unlock(&devlock);
++                      kfree(cur);
++                      printk(KERN_WARNING "Removing read-only on %s (%#x)\n",
++                             bdev->bd_disk ? bdev->bd_disk->disk_name :
++                                             "unknown block", bdev->bd_dev);
 +                      return;
-+                }
-+                last = cur;
-+                cur = cur->next;
-+        }
-+        spin_unlock(&devlock);
++              }
++              last = cur;
++              cur = cur->next;
++      }
++      spin_unlock(&devlock);
 +}
 +
 +EXPORT_SYMBOL(dev_set_rdonly);
index 44f0ade..c7650fd 100644 (file)
@@ -1,90 +1,88 @@
 diff -ur linux-2.4.20-rh.orig/drivers/block/ll_rw_blk.c linux-2.4.20-rh/drivers/block/ll_rw_blk.c
 --- linux-2.4.20-rh.orig/drivers/block/ll_rw_blk.c     2004-05-27 11:25:09.000000000 -0700
 +++ linux-2.4.20-rh/drivers/block/ll_rw_blk.c  2005-04-08 09:02:14.734804881 -0700
-@@ -645,6 +645,86 @@ void set_device_ro(kdev_t dev,int flag)
+@@ -645,6 +645,84 @@ void set_device_ro(kdev_t dev,int flag)
        else ro_bits[major][minor >> 5] &= ~(1 << (minor & 31));
  }
  
-+
 +/*
-+ * Debug code for turning block devices read-only *silently* (will 
++ * Debug code for turning block devices read-only *silently* (will
 + * discard writes silently).  This is only for filesystem crash/recovery
 + * testing.
 + */
 +struct deventry {
-+        kdev_t dev;
-+        struct deventry *next;
++      kdev_t dev;
++      struct deventry *next;
 +};
 +
 +static struct deventry *devlist = NULL;
-+static spinlock_t devlock = SPIN_LOCK_UNLOCKED; 
++static spinlock_t devlock = SPIN_LOCK_UNLOCKED;
 +
 +int dev_check_rdonly(kdev_t dev) {
-+        struct deventry *cur;
-+        spin_lock(&devlock);
-+        cur = devlist;
-+        while(cur) {
-+                if (dev == cur->dev) {
-+                        spin_unlock(&devlock);
-+                        return 1;
-+                }
-+                cur = cur->next;
-+        }
-+        spin_unlock(&devlock);
-+        return 0;
++      struct deventry *cur;
++      spin_lock(&devlock);
++      cur = devlist;
++      while(cur) {
++              if (dev == cur->dev) {
++                      spin_unlock(&devlock);
++                      return 1;
++              }
++              cur = cur->next;
++      }
++      spin_unlock(&devlock);
++      return 0;
 +}
 +
 +void dev_set_rdonly(kdev_t dev)
 +{
-+        struct deventry *newdev, *cur;
-+        newdev = kmalloc(sizeof(struct deventry), GFP_KERNEL);
-+        if (!newdev) return;
++      struct deventry *newdev, *cur;
++      newdev = kmalloc(sizeof(struct deventry), GFP_KERNEL);
++      if (!newdev) return;
 +
-+        spin_lock(&devlock);
-+        cur = devlist;
-+        while(cur) {
-+                if (dev == cur->dev) {
-+                        spin_unlock(&devlock);
-+                        kfree(newdev);
-+                        return;
-+                }
-+                cur = cur->next;
-+        }
-+        newdev->dev = dev;
-+        newdev->next = devlist;
-+        devlist = newdev;
-+        spin_unlock(&devlock);
-+        printk(KERN_WARNING "Turning device %s read-only\n",
-+               bdevname(dev));
++      spin_lock(&devlock);
++      cur = devlist;
++      while(cur) {
++              if (dev == cur->dev) {
++                      spin_unlock(&devlock);
++                      kfree(newdev);
++                      return;
++              }
++              cur = cur->next;
++      }
++      newdev->dev = dev;
++      newdev->next = devlist;
++      devlist = newdev;
++      spin_unlock(&devlock);
++      printk(KERN_WARNING "Turning device %s read-only\n", bdevname(dev));
 +}
 +
 +void dev_clear_rdonly(kdev_t dev) {
-+        struct deventry *cur, *last = NULL;
-+        spin_lock(&devlock);
-+        cur = devlist;
-+        while(cur) {
-+                if (dev == cur->dev) {
-+                        if (last) 
-+                                last->next = cur->next;
-+                        else
-+                                devlist = cur->next;
-+                        spin_unlock(&devlock);
-+                        kfree(cur);
-+                        printk(KERN_WARNING "Removing read-only on %s\n",
-+                               bdevname(dev));
-+                        return;
-+                }
-+                last = cur;
-+                cur = cur->next;
-+        }
-+        spin_unlock(&devlock);
++      struct deventry *cur, *last = NULL;
++
++      spin_lock(&devlock);
++      cur = devlist;
++      while(cur) {
++              if (dev == cur->dev) {
++                      if (last)
++                              last->next = cur->next;
++                      else
++                              devlist = cur->next;
++                      spin_unlock(&devlock);
++                      kfree(cur);
++                      printk(KERN_WARNING "Removing read-only on %s\n",
++                             bdevname(dev));
++                      return;
++              }
++              last = cur;
++              cur = cur->next;
++      }
++      spin_unlock(&devlock);
 +}
 +
 +EXPORT_SYMBOL(dev_set_rdonly);
 +EXPORT_SYMBOL(dev_check_rdonly);
 +EXPORT_SYMBOL(dev_clear_rdonly);
 +
-+
  inline void drive_stat_acct (kdev_t dev, int rw,
                                unsigned long nr_sectors, int new_io)
  {
@@ -92,10 +90,10 @@ diff -ur linux-2.4.20-rh.orig/drivers/block/ll_rw_blk.c linux-2.4.20-rh/drivers/
                        buffer_IO_error(bh);
                        break;
                }
-+                if ((rw & WRITE)&&(dev_check_rdonly(bh->b_rdev))) {
-+                        bh->b_end_io(bh, 0);
-+                        break;
-+                }
++              if ((rw & WRITE)&&(dev_check_rdonly(bh->b_rdev))) {
++                      bh->b_end_io(bh, 0);
++                      break;
++              }
        } while (q->make_request_fn(q, rw, bh));
  }
  
index e37a15d..3902db5 100644 (file)
@@ -1,6 +1,6 @@
 --- linux-2.4.24.orig/drivers/block/ll_rw_blk.c        2005-04-07 17:30:58.978035892 -0700
 +++ linux-2.4.24/drivers/block/ll_rw_blk.c     2005-04-07 17:22:04.354867801 -0700
-@@ -691,6 +691,86 @@ void set_device_ro(kdev_t dev,int flag)
+@@ -691,6 +691,85 @@ void set_device_ro(kdev_t dev,int flag)
        else ro_bits[major][minor >> 5] &= ~(1 << (minor & 31));
  }
  
 + * testing.
 + */
 +struct deventry {
-+        kdev_t dev;
-+        struct deventry *next;
++      kdev_t dev;
++      struct deventry *next;
 +};
 +
 +static struct deventry *devlist = NULL;
 +static spinlock_t devlock = SPIN_LOCK_UNLOCKED; 
 +
 +int dev_check_rdonly(kdev_t dev) {
-+        struct deventry *cur;
-+        spin_lock(&devlock);
-+        cur = devlist;
-+        while(cur) {
-+                if (dev == cur->dev) {
-+                        spin_unlock(&devlock);
-+                        return 1;
-+                }
-+                cur = cur->next;
-+        }
-+        spin_unlock(&devlock);
-+        return 0;
++      struct deventry *cur;
++      spin_lock(&devlock);
++      cur = devlist;
++      while(cur) {
++              if (dev == cur->dev) {
++                      spin_unlock(&devlock);
++                      return 1;
++              }
++              cur = cur->next;
++      }
++      spin_unlock(&devlock);
++      return 0;
 +}
 +
 +void dev_set_rdonly(kdev_t dev)
 +{
-+        struct deventry *newdev, *cur;
-+        newdev = kmalloc(sizeof(struct deventry), GFP_KERNEL);
-+        if (!newdev) return;
++      struct deventry *newdev, *cur;
++      newdev = kmalloc(sizeof(struct deventry), GFP_KERNEL);
++      if (!newdev) return;
 +
-+        spin_lock(&devlock);
-+        cur = devlist;
-+        while(cur) {
-+                if (dev == cur->dev) {
-+                        spin_unlock(&devlock);
-+                        kfree(newdev);
-+                        return;
-+                }
-+                cur = cur->next;
-+        }
-+        newdev->dev = dev;
-+        newdev->next = devlist;
-+        devlist = newdev;
-+        spin_unlock(&devlock);
-+        printk(KERN_WARNING "Turning device %s read-only\n",
-+               bdevname(dev));
++      spin_lock(&devlock);
++      cur = devlist;
++      while(cur) {
++              if (dev == cur->dev) {
++                      spin_unlock(&devlock);
++                      kfree(newdev);
++                      return;
++              }
++              cur = cur->next;
++      }
++      newdev->dev = dev;
++      newdev->next = devlist;
++      devlist = newdev;
++      spin_unlock(&devlock);
++      printk(KERN_WARNING "Turning device %s read-only\n", bdevname(dev));
 +}
 +
 +void dev_clear_rdonly(kdev_t dev) {
-+        struct deventry *cur, *last = NULL;
-+        spin_lock(&devlock);
-+        cur = devlist;
-+        while(cur) {
-+                if (dev == cur->dev) {
-+                        if (last) 
-+                                last->next = cur->next;
-+                        else
-+                                devlist = cur->next;
-+                        spin_unlock(&devlock);
-+                        kfree(cur);
-+                        printk(KERN_WARNING "Removing read-only on %s\n",
-+                               bdevname(dev));
-+                        return;
-+                }
-+                last = cur;
-+                cur = cur->next;
-+        }
-+        spin_unlock(&devlock);
++      struct deventry *cur, *last = NULL;
++      spin_lock(&devlock);
++      cur = devlist;
++      while(cur) {
++              if (dev == cur->dev) {
++                      if (last) 
++                              last->next = cur->next;
++                      else
++                              devlist = cur->next;
++                      spin_unlock(&devlock);
++                      kfree(cur);
++                      printk(KERN_WARNING "Removing read-only on %s\n",
++                             bdevname(dev));
++                      return;
++              }
++              last = cur;
++              cur = cur->next;
++      }
++      spin_unlock(&devlock);
 +}
 +
 +EXPORT_SYMBOL(dev_set_rdonly);
                        break;
                }
  
-+                if ((rw & WRITE)&&(dev_check_rdonly(bh->b_rdev))) {
-+                        bh->b_end_io(bh, 0);
-+                        break;
-+                }
++              if ((rw & WRITE)&&(dev_check_rdonly(bh->b_rdev))) {
++                      bh->b_end_io(bh, 0);
++                      break;
++              }
        } while (q->make_request_fn(q, rw, bh));
  }
  
                bdev->bd_op = NULL;
        unlock_kernel();
        up(&bdev->bd_sem);
-+        dev_clear_rdonly(to_kdev_t(bdev->bd_dev));
++      dev_clear_rdonly(to_kdev_t(bdev->bd_dev));
        bdput(bdev);
        return ret;
  }
diff --git a/lustre/kernel_patches/patches/elevator-cfq.patch b/lustre/kernel_patches/patches/elevator-cfq.patch
new file mode 100644 (file)
index 0000000..a13194e
--- /dev/null
@@ -0,0 +1,20 @@
+Index: linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891/drivers/block/ll_rw_blk.c
+===================================================================
+--- linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891.orig/drivers/block/ll_rw_blk.c       2005-06-28 01:53:39.000000000 -0600
++++ linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891/drivers/block/ll_rw_blk.c    2005-06-28 01:58:45.000000000 -0600
+@@ -1380,12 +1380,12 @@
+ static int __make_request(request_queue_t *, struct bio *);
+ static elevator_t *chosen_elevator =
+-#if defined(CONFIG_IOSCHED_AS)
++#if defined(CONFIG_IOSCHED_CFQ)
++      &iosched_cfq;
++#elif defined(CONFIG_IOSCHED_AS)
+       &iosched_as;
+ #elif defined(CONFIG_IOSCHED_DEADLINE)
+       &iosched_deadline;
+-#elif defined(CONFIG_IOSCHED_CFQ)
+-      &iosched_cfq;
+ #elif defined(CONFIG_IOSCHED_NOOP)
+       &elevator_noop;
+ #else
diff --git a/lustre/kernel_patches/patches/export-show_task-2.4-cray.patch b/lustre/kernel_patches/patches/export-show_task-2.4-cray.patch
new file mode 100644 (file)
index 0000000..8211401
--- /dev/null
@@ -0,0 +1,33 @@
+Index: kernel-l0405/kernel/sched.c
+===================================================================
+--- kernel-l0405.orig/kernel/sched.c   2003-11-06 16:15:20.000000000 -0800
++++ kernel-l0405/kernel/sched.c        2005-04-05 14:44:27.000000000 -0700
+@@ -1627,7 +1627,7 @@
+       return retval;
+ }
+-static void show_task(task_t * p)
++void show_task(task_t * p)
+ {
+       unsigned long free = 0;
+       int state;
+Index: kernel-l0405/kernel/ksyms.c
+===================================================================
+--- kernel-l0405.orig/kernel/ksyms.c   2005-04-05 14:44:15.000000000 -0700
++++ kernel-l0405/kernel/ksyms.c        2005-04-05 14:44:50.000000000 -0700
+@@ -55,6 +55,7 @@
+ #include <linux/unistd.h>
+ #include <linux/bdf_prm.h>
++extern void show_task(task_t *);
+ #if defined(CONFIG_PROC_FS)
+ #include <linux/proc_fs.h>
+@@ -684,6 +685,7 @@
+ /* debug */
+ EXPORT_SYMBOL(dump_stack);
++EXPORT_SYMBOL(show_task);
+ #if defined(CONFIG_KDB_USB)
+ #include <linux/kdb.h>
index 81f62ff..828d3ab 100644 (file)
@@ -11,8 +11,12 @@ Index: linux-2.6.5-SLES9_SP1_BRANCH_2004102113353091/kernel/sched.c
  {
        task_t *relative;
        unsigned state;
-@@ -3200,6 +3200,7 @@
-       if (state != TASK_RUNNING)
+@@ -3200,9 +3200,10 @@
+       else
+               printk(" (NOTLB)\n");
+-      if (state != TASK_RUNNING)
++      if (state != TASK_RUNNING || p == current)
                show_stack(p, NULL);
  }
 +EXPORT_SYMBOL(show_task);
index a6b126e..1cdaa93 100644 (file)
@@ -1079,7 +1079,7 @@ Index: linux-2.4.19.SuSE/fs/ext3/namei.c
                        ret = bh;
                        goto cleanup_and_exit;
                } else {
-@@ -198,6 +851,66 @@
+@@ -198,6 +851,74 @@
        return ret;
  }
  
@@ -1098,17 +1098,25 @@ Index: linux-2.4.19.SuSE/fs/ext3/namei.c
 +      int namelen = dentry->d_name.len;
 +      const u8 *name = dentry->d_name.name;
 +      struct inode *dir = dentry->d_parent->d_inode;
-+      
++
 +      sb = dir->i_sb;
-+      if (!(frame = dx_probe (dentry, 0, &hinfo, frames, err)))
-+              return NULL;
++      /* NFS may look up ".." - look at dx_root directory block */
++      if (namelen > 2 || name[0] != '.'||(name[1] != '.' && name[1] != '\0')){
++              if (!(frame = dx_probe(dentry, 0, &hinfo, frames, err)))
++                      return NULL;
++      } else {
++              frame = frames;
++              frame->bh = NULL;                       /* for dx_release() */
++              frame->at = (struct dx_entry *)frames;  /* hack for zero entry*/
++              dx_set_block(frame->at, 0);             /* dx_root block is 0 */
++      }
 +      hash = hinfo.hash;
 +      do {
 +              block = dx_get_block(frame->at);
 +              if (!(bh = ext3_bread (NULL,dir, block, 0, err)))
 +                      goto errout;
 +              de = (struct ext3_dir_entry_2 *) bh->b_data;
-+              top = (struct ext3_dir_entry_2 *) ((char *) de + sb->s_blocksize -
++              top = (struct ext3_dir_entry_2 *)((char *)de + sb->s_blocksize -
 +                                     EXT3_DIR_REC_LEN(0));
 +              for (; de < top; de = ext3_next_entry(de))
 +              if (ext3_match (namelen, name, de)) {
index 28a1ad6..c5e1ee0 100644 (file)
                        ret = bh;
                        goto cleanup_and_exit;
                } else {
-@@ -196,6 +849,66 @@ cleanup_and_exit:
+@@ -196,6 +849,74 @@ cleanup_and_exit:
        return ret;
  }
  
 +      int namelen = dentry->d_name.len;
 +      const u8 *name = dentry->d_name.name;
 +      struct inode *dir = dentry->d_parent->d_inode;
-+      
++
 +      sb = dir->i_sb;
-+      if (!(frame = dx_probe (dentry, 0, &hinfo, frames, err)))
-+              return NULL;
++      /* NFS may look up ".." - look at dx_root directory block */
++      if (namelen > 2 || name[0] != '.'||(name[1] != '.' && name[1] != '\0')){
++              if (!(frame = dx_probe(dentry, 0, &hinfo, frames, err)))
++                      return NULL;
++      } else {
++              frame = frames;
++              frame->bh = NULL;                       /* for dx_release() */
++              frame->at = (struct dx_entry *)frames;  /* hack for zero entry*/
++              dx_set_block(frame->at, 0);             /* dx_root block is 0 */
++      }
 +      hash = hinfo.hash;
 +      do {
 +              block = dx_get_block(frame->at);
 +              if (!(bh = ext3_bread (NULL,dir, block, 0, err)))
 +                      goto errout;
 +              de = (struct ext3_dir_entry_2 *) bh->b_data;
-+              top = (struct ext3_dir_entry_2 *) ((char *) de + sb->s_blocksize -
++              top = (struct ext3_dir_entry_2 *)((char *)de + sb->s_blocksize -
 +                                     EXT3_DIR_REC_LEN(0));
 +              for (; de < top; de = ext3_next_entry(de))
 +              if (ext3_match (namelen, name, de)) {
diff --git a/lustre/kernel_patches/patches/ext3-delete_thread-2.4.29.patch b/lustre/kernel_patches/patches/ext3-delete_thread-2.4.29.patch
new file mode 100644 (file)
index 0000000..39c47a7
--- /dev/null
@@ -0,0 +1,442 @@
+Index: linux-2.4.29/fs/ext3/super.c
+===================================================================
+--- linux-2.4.29.orig/fs/ext3/super.c  2005-05-03 15:53:33.047533872 +0300
++++ linux-2.4.29/fs/ext3/super.c       2005-05-03 15:54:47.192262160 +0300
+@@ -400,6 +400,127 @@
+       }
+ }
++#ifdef EXT3_DELETE_THREAD
++/*
++ * Delete inodes in a loop until there are no more to be deleted.
++ * Normally, we run in the background doing the deletes and sleeping again,
++ * and clients just add new inodes to be deleted onto the end of the list.
++ * If someone is concerned about free space (e.g. block allocation or similar)
++ * then they can sleep on s_delete_waiter_queue and be woken up when space
++ * has been freed.
++ */
++int ext3_delete_thread(void *data)
++{
++      struct super_block *sb = data;
++      struct ext3_sb_info *sbi = EXT3_SB(sb);
++      struct task_struct *tsk = current;
++
++      /* Almost like daemonize, but not quite */
++      exit_mm(current);
++      tsk->session = 1;
++      tsk->pgrp = 1;
++      tsk->tty = NULL;
++      exit_files(current);
++      reparent_to_init();
++
++      sprintf(tsk->comm, "kdelext3-%s", kdevname(sb->s_dev));
++      sigfillset(&tsk->blocked);
++
++      /*tsk->flags |= PF_KERNTHREAD;*/
++
++      INIT_LIST_HEAD(&sbi->s_delete_list);
++      wake_up(&sbi->s_delete_waiter_queue);
++      ext3_debug("delete thread on %s started\n", kdevname(sb->s_dev));
++
++      /* main loop */
++      for (;;) {
++              wait_event_interruptible(sbi->s_delete_thread_queue,
++                                       !list_empty(&sbi->s_delete_list) ||
++                                       !test_opt(sb, ASYNCDEL));
++              ext3_debug("%s woken up: %lu inodes, %lu blocks\n",
++                         tsk->comm,sbi->s_delete_inodes,sbi->s_delete_blocks);
++
++              spin_lock(&sbi->s_delete_lock);
++              if (list_empty(&sbi->s_delete_list)) {
++                      clear_opt(sbi->s_mount_opt, ASYNCDEL);
++                      memset(&sbi->s_delete_list, 0,
++                             sizeof(sbi->s_delete_list));
++                      spin_unlock(&sbi->s_delete_lock);
++                      ext3_debug("delete thread on %s exiting\n",
++                                 kdevname(sb->s_dev));
++                      wake_up(&sbi->s_delete_waiter_queue);
++                      break;
++              }
++
++              while (!list_empty(&sbi->s_delete_list)) {
++                      struct inode *inode=list_entry(sbi->s_delete_list.next,
++                                                     struct inode, i_devices);
++                      unsigned long blocks = inode->i_blocks >>
++                                                      (inode->i_blkbits - 9);
++
++                      list_del_init(&inode->i_devices);
++                      spin_unlock(&sbi->s_delete_lock);
++                      ext3_debug("%s delete ino %lu blk %lu\n",
++                                 tsk->comm, inode->i_ino, blocks);
++
++                      J_ASSERT(EXT3_I(inode)->i_state & EXT3_STATE_DELETE);
++                      J_ASSERT(inode->i_nlink == 1);
++                      inode->i_nlink = 0;
++                      iput(inode);
++
++                      spin_lock(&sbi->s_delete_lock);
++                      sbi->s_delete_blocks -= blocks;
++                      sbi->s_delete_inodes--;
++              }
++              if (sbi->s_delete_blocks != 0 || sbi->s_delete_inodes != 0) {
++                      ext3_warning(sb, __FUNCTION__,
++                                   "%lu blocks, %lu inodes on list?\n",
++                                   sbi->s_delete_blocks,sbi->s_delete_inodes);
++                      sbi->s_delete_blocks = 0;
++                      sbi->s_delete_inodes = 0;
++              }
++              spin_unlock(&sbi->s_delete_lock);
++              wake_up(&sbi->s_delete_waiter_queue);
++      }
++
++      return 0;
++}
++
++static void ext3_start_delete_thread(struct super_block *sb)
++{
++      struct ext3_sb_info *sbi = EXT3_SB(sb);
++      int rc;
++
++      spin_lock_init(&sbi->s_delete_lock);
++      init_waitqueue_head(&sbi->s_delete_thread_queue);
++      init_waitqueue_head(&sbi->s_delete_waiter_queue);
++
++      if (!test_opt(sb, ASYNCDEL))
++              return;
++
++      rc = kernel_thread(ext3_delete_thread, sb, CLONE_VM | CLONE_FILES);
++      if (rc < 0)
++              printk(KERN_ERR "EXT3-fs: cannot start delete thread: rc %d\n",
++                     rc);
++      else
++              wait_event(sbi->s_delete_waiter_queue, sbi->s_delete_list.next);
++}
++
++static void ext3_stop_delete_thread(struct ext3_sb_info *sbi)
++{
++      if (sbi->s_delete_list.next == 0)       /* thread never started */
++              return;
++
++      clear_opt(sbi->s_mount_opt, ASYNCDEL);
++      wake_up(&sbi->s_delete_thread_queue);
++      wait_event(sbi->s_delete_waiter_queue,
++                      sbi->s_delete_list.next == 0 && sbi->s_delete_inodes == 0);
++}
++#else
++#define ext3_start_delete_thread(sbi) do {} while(0)
++#define ext3_stop_delete_thread(sbi) do {} while(0)
++#endif /* EXT3_DELETE_THREAD */
++
+ void ext3_put_super (struct super_block * sb)
+ {
+       struct ext3_sb_info *sbi = EXT3_SB(sb);
+@@ -407,6 +528,9 @@
+       kdev_t j_dev = sbi->s_journal->j_dev;
+       int i;
++#ifdef EXT3_DELETE_THREAD
++      J_ASSERT(sbi->s_delete_inodes == 0);
++#endif
+       ext3_xattr_put_super(sb);
+       journal_destroy(sbi->s_journal);
+       if (!(sb->s_flags & MS_RDONLY)) {
+@@ -526,6 +650,13 @@
+                       clear_opt (*mount_options, XATTR_USER);
+               else
+ #endif
++#ifdef EXT3_DELETE_THREAD
++              if (!strcmp(this_char, "asyncdel"))
++                      set_opt(*mount_options, ASYNCDEL);
++              else if (!strcmp(this_char, "noasyncdel"))
++                      clear_opt(*mount_options, ASYNCDEL);
++              else
++#endif
+               if (!strcmp (this_char, "bsddf"))
+                       clear_opt (*mount_options, MINIX_DF);
+               else if (!strcmp (this_char, "nouid32")) {
+@@ -1244,6 +1375,7 @@
+       }
+       ext3_setup_super (sb, es, sb->s_flags & MS_RDONLY);
++      ext3_start_delete_thread(sb);
+       EXT3_SB(sb)->s_mount_state |= EXT3_ORPHAN_FS;
+       ext3_orphan_cleanup(sb, es);
+       EXT3_SB(sb)->s_mount_state &= ~EXT3_ORPHAN_FS;
+@@ -1626,7 +1758,12 @@
+ static int ext3_sync_fs(struct super_block *sb)
+ {
+       tid_t target;
+-      
++
++      if (atomic_read(&sb->s_active) == 0) {
++              /* fs is being umounted: time to stop delete thread */
++              ext3_stop_delete_thread(EXT3_SB(sb));
++      }
++
+       sb->s_dirt = 0;
+       target = log_start_commit(EXT3_SB(sb)->s_journal, NULL);
+       log_wait_commit(EXT3_SB(sb)->s_journal, target);
+@@ -1690,6 +1827,9 @@
+       if (!parse_options(data, &tmp, sbi, &tmp, 1))
+               return -EINVAL;
++      if (!test_opt(sb, ASYNCDEL) || (*flags & MS_RDONLY))
++              ext3_stop_delete_thread(sbi);
++
+       if (sbi->s_mount_opt & EXT3_MOUNT_ABORT)
+               ext3_abort(sb, __FUNCTION__, "Abort forced by user");
+Index: linux-2.4.29/fs/ext3/inode.c
+===================================================================
+--- linux-2.4.29.orig/fs/ext3/inode.c  2005-05-03 15:53:36.555000656 +0300
++++ linux-2.4.29/fs/ext3/inode.c       2005-05-03 15:53:56.901907456 +0300
+@@ -2562,6 +2562,118 @@
+       return err;
+ }
++#ifdef EXT3_DELETE_THREAD
++/* Move blocks from to-be-truncated inode over to a new inode, and delete
++ * that one from the delete thread instead.  This avoids a lot of latency
++ * when truncating large files.
++ *
++ * If we have any problem deferring the truncate, just truncate it right away.
++ * If we defer it, we also mark how many blocks it would free, so that we
++ * can keep the statfs data correct, and we know if we should sleep on the
++ * delete thread when we run out of space.
++ */
++void ext3_truncate_thread(struct inode *old_inode)
++{
++      struct ext3_sb_info *sbi = EXT3_SB(old_inode->i_sb);
++      struct ext3_inode_info *nei, *oei = EXT3_I(old_inode);
++      struct inode *new_inode;
++      handle_t *handle;
++      unsigned long blocks = old_inode->i_blocks >> (old_inode->i_blkbits-9);
++
++      if (!test_opt(old_inode->i_sb, ASYNCDEL) || !sbi->s_delete_list.next)
++              goto out_truncate;
++
++      /* XXX This is a temporary limitation for code simplicity.
++       *     We could truncate to arbitrary sizes at some later time.
++       */
++      if (old_inode->i_size != 0)
++              goto out_truncate;
++
++      /* We may want to truncate the inode immediately and not defer it */
++      if (IS_SYNC(old_inode) || blocks <= EXT3_NDIR_BLOCKS ||
++          old_inode->i_size > oei->i_disksize)
++              goto out_truncate;
++
++      /* We can't use the delete thread as-is during real orphan recovery,
++       * as we add to the orphan list here, causing ext3_orphan_cleanup()
++       * to loop endlessly.  It would be nice to do so, but needs work.
++       */
++      if (oei->i_state & EXT3_STATE_DELETE ||
++          sbi->s_mount_state & EXT3_ORPHAN_FS) {
++              ext3_debug("doing deferred inode %lu delete (%lu blocks)\n",
++                         old_inode->i_ino, blocks);
++              goto out_truncate;
++      }
++
++      ext3_discard_prealloc(old_inode);
++
++      /* old_inode   = 1
++       * new_inode   = sb + GDT + ibitmap
++       * orphan list = 1 inode/superblock for add, 2 inodes for del
++       * quota files = 2 * EXT3_SINGLEDATA_TRANS_BLOCKS
++       */
++      handle = ext3_journal_start(old_inode, 7);
++      if (IS_ERR(handle))
++              goto out_truncate;
++
++      new_inode = ext3_new_inode(handle, old_inode, old_inode->i_mode);
++      if (IS_ERR(new_inode)) {
++              ext3_debug("truncate inode %lu directly (no new inodes)\n",
++                         old_inode->i_ino);
++              goto out_journal;
++      }
++
++      nei = EXT3_I(new_inode);
++
++      down_write(&oei->truncate_sem);
++      new_inode->i_size = old_inode->i_size;
++      new_inode->i_blocks = old_inode->i_blocks;
++      new_inode->i_uid = old_inode->i_uid;
++      new_inode->i_gid = old_inode->i_gid;
++      new_inode->i_nlink = 1;
++
++      /* FIXME when we do arbitrary truncates */
++      old_inode->i_blocks = oei->i_file_acl ? old_inode->i_blksize / 512 : 0;
++      old_inode->i_mtime = old_inode->i_ctime = CURRENT_TIME;
++
++      memcpy(nei->i_data, oei->i_data, sizeof(nei->i_data));
++      memset(oei->i_data, 0, sizeof(oei->i_data));
++
++      nei->i_disksize = oei->i_disksize;
++      nei->i_state |= EXT3_STATE_DELETE;
++      up_write(&oei->truncate_sem);
++
++      if (ext3_orphan_add(handle, new_inode) < 0)
++              goto out_journal;
++
++      if (ext3_orphan_del(handle, old_inode) < 0) {
++              ext3_orphan_del(handle, new_inode);
++              iput(new_inode);
++              goto out_journal;
++      }
++
++      ext3_journal_stop(handle, old_inode);
++
++      spin_lock(&sbi->s_delete_lock);
++      J_ASSERT(list_empty(&new_inode->i_devices));
++      list_add_tail(&new_inode->i_devices, &sbi->s_delete_list);
++      sbi->s_delete_blocks += blocks;
++      sbi->s_delete_inodes++;
++      spin_unlock(&sbi->s_delete_lock);
++
++      ext3_debug("delete inode %lu (%lu blocks) by thread\n",
++                 new_inode->i_ino, blocks);
++
++      wake_up(&sbi->s_delete_thread_queue);
++      return;
++
++out_journal:
++      ext3_journal_stop(handle, old_inode);
++out_truncate:
++      ext3_truncate(old_inode);
++}
++#endif /* EXT3_DELETE_THREAD */
++
+ /* 
+  * On success, We end up with an outstanding reference count against
+  * iloc->bh.  This _must_ be cleaned up later. 
+Index: linux-2.4.29/fs/ext3/file.c
+===================================================================
+--- linux-2.4.29.orig/fs/ext3/file.c   2005-04-07 19:31:00.000000000 +0300
++++ linux-2.4.29/fs/ext3/file.c        2005-05-03 15:53:56.902907304 +0300
+@@ -123,7 +123,11 @@
+ };
+ struct inode_operations ext3_file_inode_operations = {
++#ifdef EXT3_DELETE_THREAD
++      truncate:       ext3_truncate_thread,   /* BKL held */
++#else
+       truncate:       ext3_truncate,          /* BKL held */
++#endif
+       setattr:        ext3_setattr,           /* BKL held */
+       setxattr:       ext3_setxattr,          /* BKL held */
+       getxattr:       ext3_getxattr,          /* BKL held */
+Index: linux-2.4.29/fs/ext3/namei.c
+===================================================================
+--- linux-2.4.29.orig/fs/ext3/namei.c  2005-05-03 15:53:33.044534328 +0300
++++ linux-2.4.29/fs/ext3/namei.c       2005-05-03 15:53:56.905906848 +0300
+@@ -838,6 +838,40 @@
+       return retval;
+ }
++#ifdef EXT3_DELETE_THREAD
++static int ext3_try_to_delay_deletion(struct inode *inode)
++{
++      struct ext3_sb_info *sbi = EXT3_SB(inode->i_sb);
++      struct ext3_inode_info *ei = EXT3_I(inode);
++      unsigned long blocks;
++
++      if (!test_opt(inode->i_sb, ASYNCDEL))
++              return 0;
++
++      /* We may want to delete the inode immediately and not defer it */
++      blocks = inode->i_blocks >> (inode->i_blkbits - 9);
++      if (IS_SYNC(inode) || blocks <= EXT3_NDIR_BLOCKS)
++              return 0;
++
++      inode->i_nlink = 1;
++      atomic_inc(&inode->i_count);
++      ei->i_state |= EXT3_STATE_DELETE;
++
++      spin_lock(&sbi->s_delete_lock);
++      J_ASSERT(list_empty(&inode->i_devices));
++      list_add_tail(&inode->i_devices, &sbi->s_delete_list);
++      sbi->s_delete_blocks += blocks;
++      sbi->s_delete_inodes++;
++      spin_unlock(&sbi->s_delete_lock);
++
++      wake_up(&sbi->s_delete_thread_queue);
++
++      return 0;
++}
++#else
++#define ext3_try_to_delay_deletion(inode) do {} while (0)
++#endif
++
+ static int ext3_unlink(struct inode * dir, struct dentry *dentry)
+ {
+       int retval;
+@@ -878,8 +912,10 @@
+       dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL;
+       ext3_mark_inode_dirty(handle, dir);
+       inode->i_nlink--;
+-      if (!inode->i_nlink)
++      if (!inode->i_nlink) {
++              ext3_try_to_delay_deletion(inode);
+               ext3_orphan_add(handle, inode);
++      }
+       inode->i_ctime = dir->i_ctime;
+       ext3_mark_inode_dirty(handle, inode);
+       retval = 0;
+Index: linux-2.4.29/include/linux/ext3_fs.h
+===================================================================
+--- linux-2.4.29.orig/include/linux/ext3_fs.h  2005-05-03 15:53:37.124914016 +0300
++++ linux-2.4.29/include/linux/ext3_fs.h       2005-05-03 15:53:56.907906544 +0300
+@@ -188,6 +188,7 @@
+  */
+ #define EXT3_STATE_JDATA              0x00000001 /* journaled data exists */
+ #define EXT3_STATE_NEW                        0x00000002 /* inode is newly created */
++#define EXT3_STATE_DELETE             0x00000010 /* deferred delete inode */
+ /*
+  * ioctl commands
+@@ -315,6 +316,7 @@
+ #define EXT3_MOUNT_UPDATE_JOURNAL     0x1000  /* Update the journal format */
+ #define EXT3_MOUNT_NO_UID32           0x2000  /* Disable 32-bit UIDs */
+ #define EXT3_MOUNT_XATTR_USER         0x4000  /* Extended user attributes */
++#define EXT3_MOUNT_ASYNCDEL           0x20000 /* Delayed deletion */
+ /* Compatibility, for having both ext2_fs.h and ext3_fs.h included at once */
+ #ifndef _LINUX_EXT2_FS_H
+@@ -639,6 +641,9 @@
+ extern void ext3_dirty_inode(struct inode *);
+ extern int ext3_change_inode_journal_flag(struct inode *, int);
+ extern void ext3_truncate (struct inode *);
++#ifdef EXT3_DELETE_THREAD
++extern void ext3_truncate_thread(struct inode *inode);
++#endif
+ extern void ext3_set_inode_flags(struct inode *);
+ /* ioctl.c */
+Index: linux-2.4.29/include/linux/ext3_fs_sb.h
+===================================================================
+--- linux-2.4.29.orig/include/linux/ext3_fs_sb.h       2005-05-03 15:53:33.048533720 +0300
++++ linux-2.4.29/include/linux/ext3_fs_sb.h    2005-05-03 15:53:56.909906240 +0300
+@@ -29,6 +29,8 @@
+ #define EXT3_MAX_GROUP_LOADED 8
++#define EXT3_DELETE_THREAD
++
+ /*
+  * third extended-fs super-block data in memory
+  */
+@@ -74,6 +76,14 @@
+       struct timer_list turn_ro_timer;        /* For turning read-only (crash simulation) */
+       wait_queue_head_t ro_wait_queue;        /* For people waiting for the fs to go read-only */
+ #endif
++#ifdef EXT3_DELETE_THREAD
++      spinlock_t s_delete_lock;
++      struct list_head s_delete_list;
++      unsigned long s_delete_blocks;
++      unsigned long s_delete_inodes;
++      wait_queue_head_t s_delete_thread_queue;
++      wait_queue_head_t s_delete_waiter_queue;
++#endif
+ };
+ #endif        /* _LINUX_EXT3_FS_SB */
diff --git a/lustre/kernel_patches/patches/ext3-ea-in-inode-2.4.29.patch b/lustre/kernel_patches/patches/ext3-ea-in-inode-2.4.29.patch
new file mode 100644 (file)
index 0000000..f4832af
--- /dev/null
@@ -0,0 +1,733 @@
+Index: linux-2.4.29/fs/ext3/ialloc.c
+===================================================================
+--- linux-2.4.29.orig/fs/ext3/ialloc.c 2005-05-03 15:56:43.831530296 +0300
++++ linux-2.4.29/fs/ext3/ialloc.c      2005-05-03 16:07:32.990843080 +0300
+@@ -576,6 +576,12 @@
+       insert_inode_hash(inode);
+       inode->i_generation = sb->u.ext3_sb.s_next_generation++;
++      if (EXT3_INODE_SIZE(inode->i_sb) > EXT3_GOOD_OLD_INODE_SIZE) {
++              inode->u.ext3_i.i_extra_isize = sizeof(__u16)   /* i_extra_isize */
++                              + sizeof(__u16);        /* i_pad1 */
++      } else
++              inode->u.ext3_i.i_extra_isize = 0;
++
+       inode->u.ext3_i.i_state = EXT3_STATE_NEW;
+       err = ext3_get_inode_loc_new(inode, &iloc, 1);
+       if (err) goto fail;
+Index: linux-2.4.29/fs/ext3/inode.c
+===================================================================
+--- linux-2.4.29.orig/fs/ext3/inode.c  2005-05-03 15:58:30.758274960 +0300
++++ linux-2.4.29/fs/ext3/inode.c       2005-05-03 16:07:32.995842320 +0300
+@@ -2240,6 +2240,12 @@
+               inode->u.ext3_i.i_data[block] = iloc.raw_inode->i_block[block];
+       INIT_LIST_HEAD(&inode->u.ext3_i.i_orphan);
++      if (EXT3_INODE_SIZE(inode->i_sb) > EXT3_GOOD_OLD_INODE_SIZE)
++              inode->u.ext3_i.i_extra_isize =
++                      le16_to_cpu(raw_inode->i_extra_isize);
++      else
++              inode->u.ext3_i.i_extra_isize = 0;
++
+       if (S_ISREG(inode->i_mode)) {
+               inode->i_op = &ext3_file_inode_operations;
+               inode->i_fop = &ext3_file_operations;
+@@ -2367,6 +2373,10 @@
+       else for (block = 0; block < EXT3_N_BLOCKS; block++)
+               raw_inode->i_block[block] = inode->u.ext3_i.i_data[block];
++      if (EXT3_INODE_SIZE(inode->i_sb) > EXT3_GOOD_OLD_INODE_SIZE)
++              raw_inode->i_extra_isize =
++                      cpu_to_le16(EXT3_I(inode)->i_extra_isize);
++
+       BUFFER_TRACE(bh, "call ext3_journal_dirty_metadata");
+       rc = ext3_journal_dirty_metadata(handle, bh);
+       if (!err)
+Index: linux-2.4.29/fs/ext3/xattr.c
+===================================================================
+--- linux-2.4.29.orig/fs/ext3/xattr.c  2005-04-07 19:31:00.000000000 +0300
++++ linux-2.4.29/fs/ext3/xattr.c       2005-05-03 16:07:33.007840496 +0300
+@@ -100,6 +100,9 @@
+ static int ext3_xattr_set2(handle_t *, struct inode *, struct buffer_head *,
+                          struct ext3_xattr_header *);
++int ext3_xattr_block_set(handle_t *, struct inode *, int, const char *,
++                      const void *, size_t, int);
++
+ #ifdef CONFIG_EXT3_FS_XATTR_SHARING
+ static int ext3_xattr_cache_insert(struct buffer_head *);
+@@ -348,17 +351,12 @@
+ }
+ /*
+- * ext3_xattr_get()
+- *
+- * Copy an extended attribute into the buffer
+- * provided, or compute the buffer size required.
+- * Buffer is NULL to compute the size of the buffer required.
++ * ext3_xattr_block_get()
+  *
+- * Returns a negative error number on failure, or the number of bytes
+- * used / required on success.
++ * routine looks for attribute in EA block and returns it's value and size
+  */
+ int
+-ext3_xattr_get(struct inode *inode, int name_index, const char *name,
++ext3_xattr_block_get(struct inode *inode, int name_index, const char *name,
+              void *buffer, size_t buffer_size)
+ {
+       struct buffer_head *bh = NULL;
+@@ -447,6 +445,94 @@
+ }
+ /*
++ * ext3_xattr_ibody_get()
++ *
++ * routine looks for attribute in inode body and returns it's value and size
++ */
++int
++ext3_xattr_ibody_get(struct inode *inode, int name_index, const char *name,
++             void *buffer, size_t buffer_size)
++{
++      int size, name_len = strlen(name), storage_size;
++      struct ext3_xattr_entry *last;
++      struct ext3_inode *raw_inode;
++      struct ext3_iloc iloc;
++      char *start, *end;
++      int ret = -ENOENT;
++      
++      if (EXT3_SB(inode->i_sb)->s_inode_size <= EXT3_GOOD_OLD_INODE_SIZE)
++              return -ENOENT;
++
++      ret = ext3_get_inode_loc(inode, &iloc);
++      if (ret)
++              return ret;
++      raw_inode = iloc.raw_inode;
++
++      storage_size = EXT3_SB(inode->i_sb)->s_inode_size -
++                              EXT3_GOOD_OLD_INODE_SIZE -
++                              EXT3_I(inode)->i_extra_isize -
++                              sizeof(__u32);
++      start = (char *) raw_inode + EXT3_GOOD_OLD_INODE_SIZE +
++                      EXT3_I(inode)->i_extra_isize;
++      if (le32_to_cpu((*(__u32*) start)) != EXT3_XATTR_MAGIC) {
++              brelse(iloc.bh);
++              return -ENOENT;
++      }
++      start += sizeof(__u32);
++      end = (char *) raw_inode + EXT3_SB(inode->i_sb)->s_inode_size;
++
++      last = (struct ext3_xattr_entry *) start;
++      while (!IS_LAST_ENTRY(last)) {
++              struct ext3_xattr_entry *next = EXT3_XATTR_NEXT(last);
++              if (le32_to_cpu(last->e_value_size) > storage_size ||
++                              (char *) next >= end) {
++                      ext3_error(inode->i_sb, "ext3_xattr_ibody_get",
++                              "inode %ld", inode->i_ino);
++                      brelse(iloc.bh);
++                      return -EIO;
++              }
++              if (name_index == last->e_name_index &&
++                  name_len == last->e_name_len &&
++                  !memcmp(name, last->e_name, name_len))
++                      goto found;
++              last = next;
++      }
++
++      /* can't find EA */
++      brelse(iloc.bh);
++      return -ENOENT;
++      
++found:
++      size = le32_to_cpu(last->e_value_size);
++      if (buffer) {
++              ret = -ERANGE;
++              if (buffer_size >= size) {
++                      memcpy(buffer, start + le16_to_cpu(last->e_value_offs),
++                      size);
++                      ret = size;
++              }
++      } else
++              ret = size;
++      brelse(iloc.bh);
++      return ret;
++}
++
++int ext3_xattr_get(struct inode *inode, int name_index, const char *name,
++                      void *buffer, size_t buffer_size)
++{
++      int err;
++
++      /* try to find attribute in inode body */
++      err = ext3_xattr_ibody_get(inode, name_index, name,
++                                      buffer, buffer_size);
++      if (err < 0)
++              /* search was unsuccessful, try to find EA in dedicated block */
++              err = ext3_xattr_block_get(inode, name_index, name,
++                              buffer, buffer_size);
++      return err;
++}
++
++/*
+  * ext3_xattr_list()
+  *
+  * Copy a list of attribute names into the buffer
+@@ -457,7 +543,7 @@
+  * used / required on success.
+  */
+ int
+-ext3_xattr_list(struct inode *inode, char *buffer, size_t buffer_size)
++ext3_xattr_block_list(struct inode *inode, char *buffer, size_t buffer_size)
+ {
+       struct buffer_head *bh = NULL;
+       struct ext3_xattr_entry *entry;
+@@ -530,6 +616,131 @@
+       return error;
+ }
++/* ext3_xattr_ibody_list()
++ *
++ * generate list of attributes stored in inode body
++ */
++int
++ext3_xattr_ibody_list(struct inode *inode, char *buffer, size_t buffer_size)
++{
++      struct ext3_xattr_entry *last;
++      struct ext3_inode *raw_inode;
++      char *start, *end, *buf;
++      struct ext3_iloc iloc;
++      int storage_size;
++      int ret;
++      int size = 0;
++      
++      if (EXT3_SB(inode->i_sb)->s_inode_size <= EXT3_GOOD_OLD_INODE_SIZE)
++              return 0;
++
++      ret = ext3_get_inode_loc(inode, &iloc);
++      if (ret)
++              return ret;
++      raw_inode = iloc.raw_inode;
++
++      storage_size = EXT3_SB(inode->i_sb)->s_inode_size -
++                              EXT3_GOOD_OLD_INODE_SIZE -
++                              EXT3_I(inode)->i_extra_isize -
++                              sizeof(__u32);
++      start = (char *) raw_inode + EXT3_GOOD_OLD_INODE_SIZE +
++                      EXT3_I(inode)->i_extra_isize;
++      if (le32_to_cpu((*(__u32*) start)) != EXT3_XATTR_MAGIC) {
++              brelse(iloc.bh);
++              return 0;
++      }
++      start += sizeof(__u32);
++      end = (char *) raw_inode + EXT3_SB(inode->i_sb)->s_inode_size;
++
++      last = (struct ext3_xattr_entry *) start;
++      while (!IS_LAST_ENTRY(last)) {
++              struct ext3_xattr_entry *next = EXT3_XATTR_NEXT(last);
++              struct ext3_xattr_handler *handler;
++              if (le32_to_cpu(last->e_value_size) > storage_size ||
++                              (char *) next >= end) {
++                      ext3_error(inode->i_sb, "ext3_xattr_ibody_list",
++                              "inode %ld", inode->i_ino);
++                      brelse(iloc.bh);
++                      return -EIO;
++              }
++              handler = ext3_xattr_handler(last->e_name_index);
++              if (handler)
++                      size += handler->list(NULL, inode, last->e_name,
++                                            last->e_name_len);
++              last = next;
++      }
++
++      if (!buffer) {
++              ret = size;
++              goto cleanup;
++      } else {
++              ret = -ERANGE;
++              if (size > buffer_size)
++                      goto cleanup;
++      }
++
++      last = (struct ext3_xattr_entry *) start;
++      buf = buffer;
++      while (!IS_LAST_ENTRY(last)) {
++              struct ext3_xattr_entry *next = EXT3_XATTR_NEXT(last);
++              struct ext3_xattr_handler *handler;
++              handler = ext3_xattr_handler(last->e_name_index);
++              if (handler)
++                      buf += handler->list(buf, inode, last->e_name,
++                                            last->e_name_len);
++              last = next;
++      }
++      ret = size;
++cleanup:
++      brelse(iloc.bh);
++      return ret;
++}
++
++/*
++ * ext3_xattr_list()
++ *
++ * Copy a list of attribute names into the buffer
++ * provided, or compute the buffer size required.
++ * Buffer is NULL to compute the size of the buffer required.
++ *
++ * Returns a negative error number on failure, or the number of bytes
++ * used / required on success.
++ */
++int
++ext3_xattr_list(struct inode *inode, char *buffer, size_t buffer_size)
++{
++      int error;
++      int size = buffer_size;
++
++      /* get list of attributes stored in inode body */
++      error = ext3_xattr_ibody_list(inode, buffer, buffer_size);
++      if (error < 0) {
++              /* some error occured while collecting
++               * attributes in inode body */
++              size = 0;
++              goto cleanup;
++      }
++      size = error;
++
++      /* get list of attributes stored in dedicated block */
++      if (buffer) {
++              buffer_size -= error;
++              if (buffer_size <= 0) {
++                      buffer = NULL;
++                      buffer_size = 0;
++              } else
++                      buffer += error;
++      }
++
++      error = ext3_xattr_block_list(inode, buffer, buffer_size);
++      if (error < 0)
++              /* listing was successful, so we return len */
++              size = 0;
++
++cleanup:
++      return error + size;
++}
++
+ /*
+  * If the EXT3_FEATURE_COMPAT_EXT_ATTR feature of this file system is
+  * not set, set it.
+@@ -553,6 +764,279 @@
+ }
+ /*
++ * ext3_xattr_ibody_find()
++ *
++ * search attribute and calculate free space in inode body
++ * NOTE: free space includes space our attribute hold
++ */
++int
++ext3_xattr_ibody_find(struct inode *inode, int name_index,
++              const char *name, struct ext3_xattr_entry *rentry, int *free)
++{
++      struct ext3_xattr_entry *last;
++      struct ext3_inode *raw_inode;
++      int name_len = strlen(name);
++      int err, storage_size;
++      struct ext3_iloc iloc;
++      char *start, *end;
++      int ret = -ENOENT;
++      
++      if (EXT3_SB(inode->i_sb)->s_inode_size <= EXT3_GOOD_OLD_INODE_SIZE)
++              return ret;
++
++      err = ext3_get_inode_loc(inode, &iloc);
++      if (err)
++              return -EIO;
++      raw_inode = iloc.raw_inode;
++
++      storage_size = EXT3_SB(inode->i_sb)->s_inode_size -
++                              EXT3_GOOD_OLD_INODE_SIZE -
++                              EXT3_I(inode)->i_extra_isize -
++                              sizeof(__u32);
++      *free = storage_size - sizeof(__u32);
++      start = (char *) raw_inode + EXT3_GOOD_OLD_INODE_SIZE +
++                      EXT3_I(inode)->i_extra_isize;
++      if (le32_to_cpu((*(__u32*) start)) != EXT3_XATTR_MAGIC) {
++              brelse(iloc.bh);
++              return -ENOENT;
++      }
++      start += sizeof(__u32);
++      end = (char *) raw_inode + EXT3_SB(inode->i_sb)->s_inode_size;
++
++      last = (struct ext3_xattr_entry *) start;
++      while (!IS_LAST_ENTRY(last)) {
++              struct ext3_xattr_entry *next = EXT3_XATTR_NEXT(last);
++              if (le32_to_cpu(last->e_value_size) > storage_size ||
++                              (char *) next >= end) {
++                      ext3_error(inode->i_sb, "ext3_xattr_ibody_find",
++                              "inode %ld", inode->i_ino);
++                      brelse(iloc.bh);
++                      return -EIO;
++              }
++
++              if (name_index == last->e_name_index &&
++                  name_len == last->e_name_len &&
++                  !memcmp(name, last->e_name, name_len)) {
++                      memcpy(rentry, last, sizeof(struct ext3_xattr_entry));
++                      ret = 0;
++              } else {
++                      *free -= EXT3_XATTR_LEN(last->e_name_len);
++                      *free -= le32_to_cpu(last->e_value_size);
++              }
++              last = next;
++      }
++      
++      brelse(iloc.bh);
++      return ret;
++}
++
++/*
++ * ext3_xattr_block_find()
++ *
++ * search attribute and calculate free space in EA block (if it allocated)
++ * NOTE: free space includes space our attribute hold
++ */
++int
++ext3_xattr_block_find(struct inode *inode, int name_index, const char *name,
++             struct ext3_xattr_entry *rentry, int *free)
++{
++      struct buffer_head *bh = NULL;
++      struct ext3_xattr_entry *entry;
++      char *end;
++      int name_len, error = -ENOENT;
++
++      if (!EXT3_I(inode)->i_file_acl) {
++              *free = inode->i_sb->s_blocksize -
++                      sizeof(struct ext3_xattr_header) -
++                      sizeof(__u32);
++              return -ENOENT;
++      }
++      ea_idebug(inode, "reading block %d", EXT3_I(inode)->i_file_acl);
++      bh = sb_bread(inode->i_sb, EXT3_I(inode)->i_file_acl);
++      if (!bh)
++              return -EIO;
++      ea_bdebug(bh, "b_count=%d, refcount=%d",
++              atomic_read(&(bh->b_count)), le32_to_cpu(HDR(bh)->h_refcount));
++      end = bh->b_data + bh->b_size;
++      if (HDR(bh)->h_magic != cpu_to_le32(EXT3_XATTR_MAGIC) ||
++          HDR(bh)->h_blocks != cpu_to_le32(1)) {
++bad_block:    ext3_error(inode->i_sb, "ext3_xattr_get",
++                      "inode %ld: bad block %d", inode->i_ino,
++                      EXT3_I(inode)->i_file_acl);
++              brelse(bh);
++              return -EIO;
++      }
++      /* find named attribute */
++      name_len = strlen(name);
++      *free = bh->b_size - sizeof(__u32);
++
++      entry = FIRST_ENTRY(bh);
++      while (!IS_LAST_ENTRY(entry)) {
++              struct ext3_xattr_entry *next =
++                      EXT3_XATTR_NEXT(entry);
++              if ((char *)next >= end)
++                      goto bad_block;
++              if (name_index == entry->e_name_index &&
++                  name_len == entry->e_name_len &&
++                  memcmp(name, entry->e_name, name_len) == 0) {
++                      memcpy(rentry, entry, sizeof(struct ext3_xattr_entry));
++                      error = 0;
++              } else {
++                      *free -= EXT3_XATTR_LEN(entry->e_name_len);
++                      *free -= le32_to_cpu(entry->e_value_size);
++              }
++              entry = next;
++      }
++      brelse(bh);
++
++      return error;
++}
++
++/*
++ * ext3_xattr_inode_set()
++ *
++ * this routine add/remove/replace attribute in inode body
++ */
++int
++ext3_xattr_ibody_set(handle_t *handle, struct inode *inode, int name_index,
++                    const char *name, const void *value, size_t value_len,
++                    int flags)
++{
++      struct ext3_xattr_entry *last, *next, *here = NULL;
++      struct ext3_inode *raw_inode;
++      int name_len = strlen(name);
++      int esize = EXT3_XATTR_LEN(name_len);
++      struct buffer_head *bh;
++      int err, storage_size;
++      struct ext3_iloc iloc;
++      int free, min_offs;
++      char *start, *end;
++      
++      if (EXT3_SB(inode->i_sb)->s_inode_size <= EXT3_GOOD_OLD_INODE_SIZE)
++              return -ENOSPC;
++
++      err = ext3_get_inode_loc(inode, &iloc);
++      if (err)
++              return err;
++      raw_inode = iloc.raw_inode;
++      bh = iloc.bh;
++
++      storage_size = EXT3_SB(inode->i_sb)->s_inode_size -
++                              EXT3_GOOD_OLD_INODE_SIZE -
++                              EXT3_I(inode)->i_extra_isize -
++                              sizeof(__u32);
++      start = (char *) raw_inode + EXT3_GOOD_OLD_INODE_SIZE +
++                      EXT3_I(inode)->i_extra_isize;
++      if ((*(__u32*) start) != EXT3_XATTR_MAGIC) {
++              /* inode had no attributes before */
++              *((__u32*) start) = cpu_to_le32(EXT3_XATTR_MAGIC);
++      }
++      start += sizeof(__u32);
++      end = (char *) raw_inode + EXT3_SB(inode->i_sb)->s_inode_size;
++      min_offs = storage_size;
++      free = storage_size - sizeof(__u32);
++
++      last = (struct ext3_xattr_entry *) start;       
++      while (!IS_LAST_ENTRY(last)) {
++              next = EXT3_XATTR_NEXT(last);
++              if (le32_to_cpu(last->e_value_size) > storage_size ||
++                              (char *) next >= end) {
++                      ext3_error(inode->i_sb, "ext3_xattr_ibody_set",
++                              "inode %ld", inode->i_ino);
++                      brelse(bh);
++                      return -EIO;
++              }
++              
++              if (last->e_value_size) {
++                      int offs = le16_to_cpu(last->e_value_offs);
++                      if (offs < min_offs)
++                              min_offs = offs;
++              }
++              if (name_index == last->e_name_index &&
++                      name_len == last->e_name_len &&
++                      !memcmp(name, last->e_name, name_len))
++                      here = last;
++              else {
++                      /* we calculate all but our attribute
++                       * because it will be removed before changing */
++                      free -= EXT3_XATTR_LEN(last->e_name_len);
++                      free -= le32_to_cpu(last->e_value_size);
++              }
++              last = next;
++      }
++
++      if (value && (esize + value_len > free)) {
++              brelse(bh);
++              return -ENOSPC;
++      }
++      
++      err = ext3_reserve_inode_write(handle, inode, &iloc);
++      if (err) {
++              brelse(bh);     
++              return err;
++      }
++
++      if (here) {
++              /* time to remove old value */
++              struct ext3_xattr_entry *e;
++              int size = le32_to_cpu(here->e_value_size);
++              int border = le16_to_cpu(here->e_value_offs);
++              char *src;
++
++              /* move tail */
++              memmove(start + min_offs + size, start + min_offs,
++                              border - min_offs);
++
++              /* recalculate offsets */
++              e = (struct ext3_xattr_entry *) start;
++              while (!IS_LAST_ENTRY(e)) {
++                      struct ext3_xattr_entry *next = EXT3_XATTR_NEXT(e);
++                      int offs = le16_to_cpu(e->e_value_offs);
++                      if (offs < border)
++                              e->e_value_offs =
++                                      cpu_to_le16(offs + size);
++                      e = next;
++              }
++              min_offs += size;
++
++              /* remove entry */
++              border = EXT3_XATTR_LEN(here->e_name_len);
++              src = (char *) here + EXT3_XATTR_LEN(here->e_name_len);
++              size = (char *) last - src;
++              if ((char *) here + size > end)
++                      printk("ALERT at %s:%d: 0x%p + %d > 0x%p\n",
++                                      __FILE__, __LINE__, here, size, end);
++              memmove(here, src, size);
++              last = (struct ext3_xattr_entry *) ((char *) last - border);
++              *((__u32 *) last) = 0;
++      }
++      
++      if (value) {
++              int offs = min_offs - value_len;
++              /* use last to create new entry */
++              last->e_name_len = strlen(name);
++              last->e_name_index = name_index;
++              last->e_value_offs = cpu_to_le16(offs);
++              last->e_value_size = cpu_to_le32(value_len);
++              last->e_hash = last->e_value_block = 0;
++              memset(last->e_name, 0, esize);
++              memcpy(last->e_name, name, last->e_name_len);
++              if (start + offs + value_len > end)
++                      printk("ALERT at %s:%d: 0x%p + %d + %d > 0x%p\n",
++                                      __FILE__, __LINE__, start, offs,
++                                      value_len, end);
++              memcpy(start + offs, value, value_len);
++              last = EXT3_XATTR_NEXT(last);
++              *((__u32 *) last) = 0;
++      }
++      
++      ext3_mark_iloc_dirty(handle, inode, &iloc);
++      brelse(bh);
++
++      return 0;
++}
++
++/*
+  * ext3_xattr_set()
+  *
+  * Create, replace or remove an extended attribute for this inode. Buffer
+@@ -566,6 +1050,101 @@
+  */
+ int
+ ext3_xattr_set(handle_t *handle, struct inode *inode, int name_index,
++              const char *name, const void *value, size_t value_len, int flags)
++{
++      struct ext3_xattr_entry entry;
++      int err, where = 0, found = 0, total;
++      int free1 = -1, free2 = -1;
++      int name_len;
++      
++      ea_idebug(inode, "name=%d.%s, value=%p, value_len=%ld",
++                name_index, name, value, (long)value_len);
++
++      if (IS_RDONLY(inode))
++              return -EROFS;
++      if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
++              return -EPERM;
++      if (value == NULL)
++              value_len = 0;
++      if (name == NULL)
++              return -EINVAL;
++      name_len = strlen(name);
++      if (name_len > 255 || value_len > inode->i_sb->s_blocksize)
++              return -ERANGE;
++
++      /* try to find attribute in inode body */
++      err = ext3_xattr_ibody_find(inode, name_index, name, &entry, &free1);
++      if (err == 0) {
++              /* found EA in inode */
++              found = 1;
++              where = 0;
++      } else if (err == -ENOENT) {
++              /* there is no such attribute in inode body */
++              /* try to find attribute in dedicated block */
++              err = ext3_xattr_block_find(inode, name_index, name,
++                                              &entry, &free2);
++              if (err != 0 && err != -ENOENT) {
++                      /* not found EA in block */
++                      goto finish;    
++              } else if (err == 0) {
++                      /* found EA in block */
++                      where = 1;
++                      found = 1;
++              }
++      } else
++              goto finish;
++
++      /* check flags: may replace? may create ? */
++      if (found && (flags & XATTR_CREATE)) {
++              err = -EEXIST;
++              goto finish;
++      } else if (!found && (flags & XATTR_REPLACE)) {
++              err = -ENODATA;
++              goto finish;
++      }
++
++      /* check if we have enough space to store attribute */
++      total = EXT3_XATTR_LEN(strlen(name)) + value_len;
++      if (free1 >= 0 && total > free1 && free2 >= 0 && total > free2) {
++              /* have no enough space */
++              err = -ENOSPC;
++              goto finish;
++      }
++      
++      /* time to remove attribute */
++      if (found) {
++              if (where == 0) {
++                      /* EA is stored in inode body */
++                      ext3_xattr_ibody_set(handle, inode, name_index, name,
++                                      NULL, 0, flags);
++              } else {
++                      /* EA is stored in separated block */
++                      ext3_xattr_block_set(handle, inode, name_index, name,
++                                      NULL, 0, flags);
++              }
++      }
++
++      /* try to store EA in inode body */
++      err = ext3_xattr_ibody_set(handle, inode, name_index, name,
++                              value, value_len, flags);
++      if (err) {
++              /* can't store EA in inode body */
++              /* try to store in block */
++              err = ext3_xattr_block_set(handle, inode, name_index,
++                                      name, value, value_len, flags); 
++      }
++
++finish:       
++      return err;
++}
++
++/*
++ * ext3_xattr_block_set()
++ *
++ * this routine add/remove/replace attribute in EA block
++ */
++int
++ext3_xattr_block_set(handle_t *handle, struct inode *inode, int name_index,
+              const char *name, const void *value, size_t value_len, int flags)
+ {
+       struct super_block *sb = inode->i_sb;
+@@ -603,6 +1182,7 @@
+       name_len = strlen(name);
+       if (name_len > 255 || value_len > sb->s_blocksize)
+               return -ERANGE;
++
+       down(&ext3_xattr_sem);
+       if (block) {
+Index: linux-2.4.29/include/linux/ext3_fs.h
+===================================================================
+--- linux-2.4.29.orig/include/linux/ext3_fs.h  2005-05-03 15:58:30.767273592 +0300
++++ linux-2.4.29/include/linux/ext3_fs.h       2005-05-03 16:07:33.009840192 +0300
+@@ -259,6 +259,8 @@
+                       __u32   m_i_reserved2[2];
+               } masix2;
+       } osd2;                         /* OS dependent 2 */
++      __u16   i_extra_isize;
++      __u16   i_pad1;
+ };
+ #define i_size_high   i_dir_acl
+Index: linux-2.4.29/include/linux/ext3_fs_i.h
+===================================================================
+--- linux-2.4.29.orig/include/linux/ext3_fs_i.h        2005-04-07 18:52:18.000000000 +0300
++++ linux-2.4.29/include/linux/ext3_fs_i.h     2005-05-03 16:07:33.010840040 +0300
+@@ -62,6 +62,9 @@
+        */
+       loff_t  i_disksize;
++      /* on-disk additional length */
++      __u16 i_extra_isize;
++
+       /*
+        * truncate_sem is for serialising ext3_truncate() against
+        * ext3_getblock().  In the 2.4 ext2 design, great chunks of inode's
index 3b4d07b..5ef713b 100644 (file)
@@ -2526,9 +2526,9 @@ Index: linux-2.4.21-rhel/include/linux/ext3_fs.h
  #ifdef CONFIG_JBD_DEBUG
  #define EXT3_IOC_WAIT_FOR_READONLY    _IOR('f', 99, long)
  #endif
-+#define EXT3_IOC_GET_EXTENTS          _IOR('f', 5, long)
-+#define EXT3_IOC_GET_TREE_DEPTH               _IOR('f', 6, long)
-+#define EXT3_IOC_GET_TREE_STATS               _IOR('f', 7, long)
++#define EXT3_IOC_GET_EXTENTS          _IOR('f', 7, long)
++#define EXT3_IOC_GET_TREE_DEPTH               _IOR('f', 8, long)
++#define EXT3_IOC_GET_TREE_STATS               _IOR('f', 9, long)
  
  /*
   * Structure of an inode on the disk
index eb295e1..53cb38e 100644 (file)
@@ -2461,17 +2461,17 @@ Index: linux-2.4.21-suse2/fs/ext3/Makefile
  export-objs += xattr.o
 Index: linux-2.4.21-suse2/fs/ext3/super.c
 ===================================================================
---- linux-2.4.21-suse2.orig/fs/ext3/super.c    2004-11-02 20:31:37.000000000 +0300
-+++ linux-2.4.21-suse2/fs/ext3/super.c 2004-11-02 20:31:39.000000000 +0300
-@@ -624,6 +624,7 @@
-       int i;
+--- linux-2.4.21-suse2.orig/fs/ext3/super.c    2005-04-04 05:44:58.000000000 -0600
++++ linux-2.4.21-suse2/fs/ext3/super.c 2005-04-04 05:45:43.000000000 -0600
+@@ -532,6 +532,7 @@
+ #ifdef EXT3_DELETE_THREAD
        J_ASSERT(sbi->s_delete_inodes == 0);
+ #endif
 +      ext3_ext_release(sb);
        ext3_xattr_put_super(sb);
        journal_destroy(sbi->s_journal);
        if (!(sb->s_flags & MS_RDONLY)) {
-@@ -829,6 +830,10 @@
+@@ -733,6 +734,10 @@
                                return 0;
                        }
                }
@@ -2482,7 +2482,7 @@ Index: linux-2.4.21-suse2/fs/ext3/super.c
                else if (!strcmp (this_char, "grpid") ||
                         !strcmp (this_char, "bsdgroups"))
                        set_opt (*mount_options, GRPID);
-@@ -1524,6 +1529,8 @@
+@@ -1428,6 +1433,8 @@
                test_opt(sb,DATA_FLAGS) == EXT3_MOUNT_ORDERED_DATA ? "ordered":
                "writeback");
  
@@ -2525,9 +2525,9 @@ Index: linux-2.4.21-suse2/include/linux/ext3_fs.h
  #ifdef CONFIG_JBD_DEBUG
  #define EXT3_IOC_WAIT_FOR_READONLY    _IOR('f', 99, long)
  #endif
-+#define EXT3_IOC_GET_EXTENTS          _IOR('f', 5, long)
-+#define EXT3_IOC_GET_TREE_DEPTH               _IOR('f', 6, long)
-+#define EXT3_IOC_GET_TREE_STATS               _IOR('f', 7, long)
++#define EXT3_IOC_GET_EXTENTS          _IOR('f', 7, long)
++#define EXT3_IOC_GET_TREE_DEPTH               _IOR('f', 8, long)
++#define EXT3_IOC_GET_TREE_STATS               _IOR('f', 9, long)
  
  /*
   * Structure of an inode on the disk
index 81dc7ad..3364139 100644 (file)
@@ -2513,9 +2513,9 @@ Index: linux-2.4.24/include/linux/ext3_fs.h
  #ifdef CONFIG_JBD_DEBUG
  #define EXT3_IOC_WAIT_FOR_READONLY    _IOR('f', 99, long)
  #endif
-+#define EXT3_IOC_GET_EXTENTS          _IOR('f', 5, long)
-+#define EXT3_IOC_GET_TREE_DEPTH               _IOR('f', 6, long)
-+#define EXT3_IOC_GET_TREE_STATS               _IOR('f', 7, long)
++#define EXT3_IOC_GET_EXTENTS          _IOR('f', 7, long)
++#define EXT3_IOC_GET_TREE_DEPTH               _IOR('f', 8, long)
++#define EXT3_IOC_GET_TREE_STATS               _IOR('f', 9, long)
  
  /*
   * Structure of an inode on the disk
diff --git a/lustre/kernel_patches/patches/ext3-extents-2.4.29.patch b/lustre/kernel_patches/patches/ext3-extents-2.4.29.patch
new file mode 100644 (file)
index 0000000..11acf5c
--- /dev/null
@@ -0,0 +1,2851 @@
+Index: linux-2.4.29/fs/ext3/extents.c
+===================================================================
+--- linux-2.4.29.orig/fs/ext3/extents.c        2005-05-03 16:52:08.723069952 +0300
++++ linux-2.4.29/fs/ext3/extents.c     2005-05-03 16:52:08.802057944 +0300
+@@ -0,0 +1,2302 @@
++/*
++ * Copyright(c) 2003, 2004, 2005, Cluster File Systems, Inc, info@clusterfs.com
++ * Written by Alex Tomas <alex@clusterfs.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public Licens
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-
++ */
++
++/*
++ * Extents support for EXT3
++ *
++ * TODO:
++ *   - ext3_ext_walk_space() sould not use ext3_ext_find_extent()
++ *   - ext3_ext_calc_credits() could take 'mergable' into account
++ *   - ext3*_error() should be used in some situations
++ *   - find_goal() [to be tested and improved]
++ *   - smart tree reduction
++ *   - arch-independence
++ *     common on-disk format for big/little-endian arch
++ */
++
++#include <linux/module.h>
++#include <linux/fs.h>
++#include <linux/time.h>
++#include <linux/ext3_jbd.h>
++#include <linux/jbd.h>
++#include <linux/locks.h>
++#include <linux/smp_lock.h>
++#include <linux/highuid.h>
++#include <linux/pagemap.h>
++#include <linux/quotaops.h>
++#include <linux/string.h>
++#include <linux/slab.h>
++#include <linux/ext3_extents.h>
++#include <asm/uaccess.h>
++
++
++static inline int ext3_ext_check_header(struct ext3_extent_header *eh)
++{
++      if (eh->eh_magic != EXT3_EXT_MAGIC) {
++              printk(KERN_ERR "EXT3-fs: invalid magic = 0x%x\n",
++                     (unsigned)eh->eh_magic);
++              return -EIO;
++      }
++      if (eh->eh_max == 0) {
++              printk(KERN_ERR "EXT3-fs: invalid eh_max = %u\n",
++                     (unsigned)eh->eh_max);
++              return -EIO;
++      }
++      if (eh->eh_entries > eh->eh_max) {
++              printk(KERN_ERR "EXT3-fs: invalid eh_entries = %u\n",
++                     (unsigned)eh->eh_entries);
++              return -EIO;
++      }
++      return 0;
++}
++
++static handle_t *ext3_ext_journal_restart(handle_t *handle, int needed)
++{
++      int err;
++
++      if (handle->h_buffer_credits > needed)
++              return handle;
++      if (!ext3_journal_extend(handle, needed))
++              return handle;
++      err = ext3_journal_restart(handle, needed);
++      
++      return handle;
++}
++
++static int inline
++ext3_ext_get_access_for_root(handle_t *h, struct ext3_extents_tree *tree)
++{
++      if (tree->ops->get_write_access)
++              return tree->ops->get_write_access(h,tree->buffer);
++      else
++              return 0;
++}
++
++static int inline
++ext3_ext_mark_root_dirty(handle_t *h, struct ext3_extents_tree *tree)
++{
++      if (tree->ops->mark_buffer_dirty)
++              return tree->ops->mark_buffer_dirty(h,tree->buffer);
++      else
++              return 0;
++}
++
++/*
++ * could return:
++ *  - EROFS
++ *  - ENOMEM
++ */
++static int ext3_ext_get_access(handle_t *handle,
++                             struct ext3_extents_tree *tree,
++                             struct ext3_ext_path *path)
++{
++      int err;
++
++      if (path->p_bh) {
++              /* path points to block */
++              err = ext3_journal_get_write_access(handle, path->p_bh);
++      } else {
++              /* path points to leaf/index in inode body */
++              err = ext3_ext_get_access_for_root(handle, tree);
++      }
++      return err;
++}
++
++/*
++ * could return:
++ *  - EROFS
++ *  - ENOMEM
++ *  - EIO
++ */
++static int ext3_ext_dirty(handle_t *handle, struct ext3_extents_tree *tree,
++                        struct ext3_ext_path *path)
++{
++      int err;
++      if (path->p_bh) {
++              /* path points to block */
++              err =ext3_journal_dirty_metadata(handle, path->p_bh);
++      } else {
++              /* path points to leaf/index in inode body */
++              err = ext3_ext_mark_root_dirty(handle, tree);
++      }
++      return err;
++}
++
++static int inline
++ext3_ext_new_block(handle_t *handle, struct ext3_extents_tree *tree,
++                 struct ext3_ext_path *path, struct ext3_extent *ex,
++                 int *err)
++{
++      int goal, depth, newblock;
++      struct inode *inode;
++
++      EXT_ASSERT(tree);
++      if (tree->ops->new_block)
++              return tree->ops->new_block(handle, tree, path, ex, err);
++
++      inode = tree->inode;
++      depth = EXT_DEPTH(tree);
++      if (path && depth > 0) {
++              goal = path[depth-1].p_block;
++      } else {
++              struct ext3_inode_info *ei = EXT3_I(inode);
++              unsigned long bg_start;
++              unsigned long colour;
++
++              bg_start = (ei->i_block_group *
++                          EXT3_BLOCKS_PER_GROUP(inode->i_sb)) +
++                      le32_to_cpu(EXT3_SB(inode->i_sb)->s_es->s_first_data_block);
++              colour = (current->pid % 16) *
++                      (EXT3_BLOCKS_PER_GROUP(inode->i_sb) / 16);
++              goal = bg_start + colour;
++      }
++
++      newblock = ext3_new_block(handle, inode, goal, 0, 0, err);
++      return newblock;
++}
++
++static inline void ext3_ext_tree_changed(struct ext3_extents_tree *tree)
++{
++      struct ext3_extent_header *neh;
++      neh = EXT_ROOT_HDR(tree);
++      neh->eh_generation++;
++}
++
++static inline int ext3_ext_space_block(struct ext3_extents_tree *tree)
++{
++      int size;
++
++      size = (tree->inode->i_sb->s_blocksize -
++              sizeof(struct ext3_extent_header)) /
++                              sizeof(struct ext3_extent);
++#ifdef AGRESSIVE_TEST
++      size = 6;
++#endif
++      return size;
++}
++
++static inline int ext3_ext_space_block_idx(struct ext3_extents_tree *tree)
++{
++      int size;
++
++      size = (tree->inode->i_sb->s_blocksize -
++              sizeof(struct ext3_extent_header)) /
++                              sizeof(struct ext3_extent_idx);
++#ifdef AGRESSIVE_TEST
++      size = 5;
++#endif
++      return size;
++}
++
++static inline int ext3_ext_space_root(struct ext3_extents_tree *tree)
++{
++      int size;
++
++      size = (tree->buffer_len - sizeof(struct ext3_extent_header)) /
++                      sizeof(struct ext3_extent);
++#ifdef AGRESSIVE_TEST
++      size = 3;
++#endif
++      return size;
++}
++
++static inline int ext3_ext_space_root_idx(struct ext3_extents_tree *tree)
++{
++      int size;
++
++      size = (tree->buffer_len - sizeof(struct ext3_extent_header)) /
++                      sizeof(struct ext3_extent_idx);
++#ifdef AGRESSIVE_TEST
++      size = 4;
++#endif
++      return size;
++}
++
++static void ext3_ext_show_path(struct ext3_extents_tree *tree,
++                             struct ext3_ext_path *path)
++{
++#ifdef EXT_DEBUG
++      int k, l = path->p_depth;
++
++      ext_debug(tree, "path:");
++      for (k = 0; k <= l; k++, path++) {
++              if (path->p_idx) {
++                      ext_debug(tree, "  %d->%d", path->p_idx->ei_block,
++                                path->p_idx->ei_leaf);
++              } else if (path->p_ext) {
++                      ext_debug(tree, "  %d:%d:%d",
++                                path->p_ext->ee_block,
++                                path->p_ext->ee_len,
++                                path->p_ext->ee_start);
++              } else
++                      ext_debug(tree, "  []");
++      }
++      ext_debug(tree, "\n");
++#endif
++}
++
++static void ext3_ext_show_leaf(struct ext3_extents_tree *tree,
++                             struct ext3_ext_path *path)
++{
++#ifdef EXT_DEBUG
++      int depth = EXT_DEPTH(tree);
++      struct ext3_extent_header *eh;
++      struct ext3_extent *ex;
++      int i;
++
++      if (!path)
++              return;
++
++      eh = path[depth].p_hdr;
++      ex = EXT_FIRST_EXTENT(eh);
++
++      for (i = 0; i < eh->eh_entries; i++, ex++) {
++              ext_debug(tree, "%d:%d:%d ",
++                        ex->ee_block, ex->ee_len, ex->ee_start);
++      }
++      ext_debug(tree, "\n");
++#endif
++}
++
++static void ext3_ext_drop_refs(struct ext3_ext_path *path)
++{
++      int depth = path->p_depth;
++      int i;
++
++      for (i = 0; i <= depth; i++, path++) {
++              if (path->p_bh) {
++                      brelse(path->p_bh);
++                      path->p_bh = NULL;
++              }
++      }
++}
++
++/*
++ * binary search for closest index by given block
++ */
++static inline void
++ext3_ext_binsearch_idx(struct ext3_extents_tree *tree,
++                     struct ext3_ext_path *path, int block)
++{
++      struct ext3_extent_header *eh = path->p_hdr;
++      struct ext3_extent_idx *ix;
++      int l = 0, k, r;
++
++      EXT_ASSERT(eh->eh_magic == EXT3_EXT_MAGIC);
++      EXT_ASSERT(eh->eh_entries <= eh->eh_max);
++      EXT_ASSERT(eh->eh_entries > 0);
++
++      ext_debug(tree, "binsearch for %d(idx):  ", block);
++
++      path->p_idx = ix = EXT_FIRST_INDEX(eh);
++
++      r = k = eh->eh_entries;
++      while (k > 1) {
++              k = (r - l) / 2;
++              if (block < ix[l + k].ei_block)
++                      r -= k;
++              else
++                      l += k;
++              ext_debug(tree, "%d:%d:%d ", k, l, r);
++      }
++
++      ix += l;
++      path->p_idx = ix;
++      ext_debug(tree," -> %d->%d ",path->p_idx->ei_block,path->p_idx->ei_leaf);
++
++      while (l++ < r) {
++              if (block < ix->ei_block) 
++                      break;
++              path->p_idx = ix++;
++      }
++      ext_debug(tree, "  -> %d->%d\n", path->p_idx->ei_block,
++                path->p_idx->ei_leaf);
++
++#ifdef CHECK_BINSEARCH 
++      {
++              struct ext3_extent_idx *chix;
++
++              chix = ix = EXT_FIRST_INDEX(eh);
++              for (k = 0; k < eh->eh_entries; k++, ix++) {
++                      if (k != 0 && ix->ei_block <= ix[-1].ei_block) {
++                              printk("k=%d, ix=0x%p, first=0x%p\n", k,
++                                     ix, EXT_FIRST_INDEX(eh));
++                              printk("%u <= %u\n",
++                                     ix->ei_block,ix[-1].ei_block);
++                      }
++                      EXT_ASSERT(k == 0 || ix->ei_block > ix[-1].ei_block);
++                      if (block < ix->ei_block) 
++                              break;
++                      chix = ix;
++              }
++              EXT_ASSERT(chix == path->p_idx);
++      }
++#endif
++}
++
++/*
++ * binary search for closest extent by given block
++ */
++static inline void
++ext3_ext_binsearch(struct ext3_extents_tree *tree,
++                 struct ext3_ext_path *path, int block)
++{
++      struct ext3_extent_header *eh = path->p_hdr;
++      struct ext3_extent *ex;
++      int l = 0, k, r;
++
++      EXT_ASSERT(eh->eh_magic == EXT3_EXT_MAGIC);
++      EXT_ASSERT(eh->eh_entries <= eh->eh_max);
++
++      if (eh->eh_entries == 0) {
++              /*
++               * this leaf is empty yet:
++               *  we get such a leaf in split/add case
++               */
++              return;
++      }
++      
++      ext_debug(tree, "binsearch for %d:  ", block);
++
++      path->p_ext = ex = EXT_FIRST_EXTENT(eh);
++
++      r = k = eh->eh_entries;
++      while (k > 1) {
++              k = (r - l) / 2;
++              if (block < ex[l + k].ee_block)
++                      r -= k;
++              else
++                      l += k;
++              ext_debug(tree, "%d:%d:%d ", k, l, r);
++      }
++
++      ex += l;
++      path->p_ext = ex;
++      ext_debug(tree, "  -> %d:%d:%d ", path->p_ext->ee_block,
++                path->p_ext->ee_start, path->p_ext->ee_len);
++
++      while (l++ < r) {
++              if (block < ex->ee_block) 
++                      break;
++              path->p_ext = ex++;
++      }
++      ext_debug(tree, "  -> %d:%d:%d\n", path->p_ext->ee_block,
++                path->p_ext->ee_start, path->p_ext->ee_len);
++
++#ifdef CHECK_BINSEARCH 
++      {
++              struct ext3_extent *chex;
++
++              chex = ex = EXT_FIRST_EXTENT(eh);
++              for (k = 0; k < eh->eh_entries; k++, ex++) {
++                      EXT_ASSERT(k == 0 || ex->ee_block > ex[-1].ee_block);
++                      if (block < ex->ee_block) 
++                              break;
++                      chex = ex;
++              }
++              EXT_ASSERT(chex == path->p_ext);
++      }
++#endif
++}
++
++int ext3_extent_tree_init(handle_t *handle, struct ext3_extents_tree *tree)
++{
++      struct ext3_extent_header *eh;
++
++      BUG_ON(tree->buffer_len == 0);
++      ext3_ext_get_access_for_root(handle, tree);
++      eh = EXT_ROOT_HDR(tree);
++      eh->eh_depth = 0;
++      eh->eh_entries = 0;
++      eh->eh_magic = EXT3_EXT_MAGIC;
++      eh->eh_max = ext3_ext_space_root(tree);
++      ext3_ext_mark_root_dirty(handle, tree);
++      ext3_ext_invalidate_cache(tree);
++      return 0;
++}
++
++struct ext3_ext_path *
++ext3_ext_find_extent(struct ext3_extents_tree *tree, int block,
++                   struct ext3_ext_path *path)
++{
++      struct ext3_extent_header *eh;
++      struct buffer_head *bh;
++      int depth, i, ppos = 0;
++
++      EXT_ASSERT(tree);
++      EXT_ASSERT(tree->inode);
++      EXT_ASSERT(tree->root);
++
++      eh = EXT_ROOT_HDR(tree);
++      EXT_ASSERT(eh);
++      if (ext3_ext_check_header(eh))
++              goto err;
++
++      i = depth = EXT_DEPTH(tree);
++      EXT_ASSERT(eh->eh_max);
++      EXT_ASSERT(eh->eh_magic == EXT3_EXT_MAGIC);
++      
++      /* account possible depth increase */
++      if (!path) {
++              path = kmalloc(sizeof(struct ext3_ext_path) * (depth + 2),
++                             GFP_NOFS);
++              if (!path)
++                      return ERR_PTR(-ENOMEM);
++      }
++      memset(path, 0, sizeof(struct ext3_ext_path) * (depth + 1));
++      path[0].p_hdr = eh;
++
++      /* walk through the tree */
++      while (i) {
++              ext_debug(tree, "depth %d: num %d, max %d\n",
++                        ppos, eh->eh_entries, eh->eh_max);
++              ext3_ext_binsearch_idx(tree, path + ppos, block);
++              path[ppos].p_block = path[ppos].p_idx->ei_leaf;
++              path[ppos].p_depth = i;
++              path[ppos].p_ext = NULL;
++
++              bh = sb_bread(tree->inode->i_sb, path[ppos].p_block);
++              if (!bh)
++                      goto err;
++              eh = EXT_BLOCK_HDR(bh);
++              ppos++;
++              EXT_ASSERT(ppos <= depth);
++              path[ppos].p_bh = bh;
++              path[ppos].p_hdr = eh;
++              i--;
++
++              if (ext3_ext_check_header(eh))
++                      goto err;
++      }
++
++      path[ppos].p_depth = i;
++      path[ppos].p_hdr = eh;
++      path[ppos].p_ext = NULL;
++      path[ppos].p_idx = NULL;
++
++      if (ext3_ext_check_header(eh))
++              goto err;
++
++      /* find extent */
++      ext3_ext_binsearch(tree, path + ppos, block);
++
++      ext3_ext_show_path(tree, path);
++
++      return path;
++
++err:
++      printk(KERN_ERR "EXT3-fs: header is corrupted!\n");
++      ext3_ext_drop_refs(path);
++      kfree(path);
++      return ERR_PTR(-EIO);
++}
++
++/*
++ * insert new index [logical;ptr] into the block at cupr
++ * it check where to insert: before curp or after curp
++ */
++static int ext3_ext_insert_index(handle_t *handle,
++                               struct ext3_extents_tree *tree,
++                               struct ext3_ext_path *curp,
++                               int logical, int ptr)
++{
++      struct ext3_extent_idx *ix;
++      int len, err;
++
++      if ((err = ext3_ext_get_access(handle, tree, curp)))
++              return err;
++
++      EXT_ASSERT(logical != curp->p_idx->ei_block);
++      len = EXT_MAX_INDEX(curp->p_hdr) - curp->p_idx;
++      if (logical > curp->p_idx->ei_block) {
++              /* insert after */
++              if (curp->p_idx != EXT_LAST_INDEX(curp->p_hdr)) {
++                      len = (len - 1) * sizeof(struct ext3_extent_idx);
++                      len = len < 0 ? 0 : len;
++                      ext_debug(tree, "insert new index %d after: %d. "
++                                "move %d from 0x%p to 0x%p\n",
++                                logical, ptr, len,
++                                (curp->p_idx + 1), (curp->p_idx + 2));
++                      memmove(curp->p_idx + 2, curp->p_idx + 1, len);
++              }
++              ix = curp->p_idx + 1;
++      } else {
++              /* insert before */
++              len = len * sizeof(struct ext3_extent_idx);
++              len = len < 0 ? 0 : len;
++              ext_debug(tree, "insert new index %d before: %d. "
++                        "move %d from 0x%p to 0x%p\n",
++                        logical, ptr, len,
++                        curp->p_idx, (curp->p_idx + 1));
++              memmove(curp->p_idx + 1, curp->p_idx, len);
++              ix = curp->p_idx;
++      }
++
++      ix->ei_block = logical;
++      ix->ei_leaf = ptr;
++      curp->p_hdr->eh_entries++;
++
++      EXT_ASSERT(curp->p_hdr->eh_entries <= curp->p_hdr->eh_max);
++      EXT_ASSERT(ix <= EXT_LAST_INDEX(curp->p_hdr));
++
++      err = ext3_ext_dirty(handle, tree, curp);
++      ext3_std_error(tree->inode->i_sb, err);
++
++      return err;
++}
++
++/*
++ * routine inserts new subtree into the path, using free index entry
++ * at depth 'at:
++ *  - allocates all needed blocks (new leaf and all intermediate index blocks)
++ *  - makes decision where to split
++ *  - moves remaining extens and index entries (right to the split point)
++ *    into the newly allocated blocks
++ *  - initialize subtree
++ */
++static int ext3_ext_split(handle_t *handle, struct ext3_extents_tree *tree,
++                        struct ext3_ext_path *path,
++                        struct ext3_extent *newext, int at)
++{
++      struct buffer_head *bh = NULL;
++      int depth = EXT_DEPTH(tree);
++      struct ext3_extent_header *neh;
++      struct ext3_extent_idx *fidx;
++      struct ext3_extent *ex;
++      int i = at, k, m, a;
++      unsigned long newblock, oldblock, border;
++      int *ablocks = NULL; /* array of allocated blocks */
++      int err = 0;
++
++      /* make decision: where to split? */
++      /* FIXME: now desicion is simplest: at current extent */
++
++      /* if current leaf will be splitted, then we should use 
++       * border from split point */
++      EXT_ASSERT(path[depth].p_ext <= EXT_MAX_EXTENT(path[depth].p_hdr));
++      if (path[depth].p_ext != EXT_MAX_EXTENT(path[depth].p_hdr)) {
++              border = path[depth].p_ext[1].ee_block;
++              ext_debug(tree, "leaf will be splitted."
++                        " next leaf starts at %d\n",
++                        (int)border);
++      } else {
++              border = newext->ee_block;
++              ext_debug(tree, "leaf will be added."
++                        " next leaf starts at %d\n",
++                        (int)border);
++      }
++
++      /* 
++       * if error occurs, then we break processing
++       * and turn filesystem read-only. so, index won't
++       * be inserted and tree will be in consistent
++       * state. next mount will repair buffers too
++       */
++
++      /*
++       * get array to track all allocated blocks
++       * we need this to handle errors and free blocks
++       * upon them
++       */
++      ablocks = kmalloc(sizeof(unsigned long) * depth, GFP_NOFS);
++      if (!ablocks)
++              return -ENOMEM;
++      memset(ablocks, 0, sizeof(unsigned long) * depth);
++
++      /* allocate all needed blocks */
++      ext_debug(tree, "allocate %d blocks for indexes/leaf\n", depth - at);
++      for (a = 0; a < depth - at; a++) {
++              newblock = ext3_ext_new_block(handle, tree, path, newext, &err);
++              if (newblock == 0)
++                      goto cleanup;
++              ablocks[a] = newblock;
++      }
++
++      /* initialize new leaf */
++      newblock = ablocks[--a];
++      EXT_ASSERT(newblock);
++      bh = sb_getblk(tree->inode->i_sb, newblock);
++      if (!bh) {
++              err = -EIO;
++              goto cleanup;
++      }
++      lock_buffer(bh);
++
++      if ((err = ext3_journal_get_create_access(handle, bh)))
++              goto cleanup;
++
++      neh = EXT_BLOCK_HDR(bh);
++      neh->eh_entries = 0;
++      neh->eh_max = ext3_ext_space_block(tree);
++      neh->eh_magic = EXT3_EXT_MAGIC;
++      neh->eh_depth = 0;
++      ex = EXT_FIRST_EXTENT(neh);
++
++      /* move remain of path[depth] to the new leaf */
++      EXT_ASSERT(path[depth].p_hdr->eh_entries == path[depth].p_hdr->eh_max);
++      /* start copy from next extent */
++      /* TODO: we could do it by single memmove */
++      m = 0;
++      path[depth].p_ext++;
++      while (path[depth].p_ext <=
++                      EXT_MAX_EXTENT(path[depth].p_hdr)) {
++              ext_debug(tree, "move %d:%d:%d in new leaf %lu\n",
++                        path[depth].p_ext->ee_block,
++                        path[depth].p_ext->ee_start,
++                        path[depth].p_ext->ee_len,
++                        newblock);
++              memmove(ex++, path[depth].p_ext++, sizeof(struct ext3_extent));
++              neh->eh_entries++;
++              m++;
++      }
++      mark_buffer_uptodate(bh, 1);
++      unlock_buffer(bh);
++
++      if ((err = ext3_journal_dirty_metadata(handle, bh)))
++              goto cleanup;   
++      brelse(bh);
++      bh = NULL;
++
++      /* correct old leaf */
++      if (m) {
++              if ((err = ext3_ext_get_access(handle, tree, path + depth)))
++                      goto cleanup;
++              path[depth].p_hdr->eh_entries -= m;
++              if ((err = ext3_ext_dirty(handle, tree, path + depth)))
++                      goto cleanup;
++              
++      }
++
++      /* create intermediate indexes */
++      k = depth - at - 1;
++      EXT_ASSERT(k >= 0);
++      if (k)
++              ext_debug(tree, "create %d intermediate indices\n", k);
++      /* insert new index into current index block */
++      /* current depth stored in i var */
++      i = depth - 1;
++      while (k--) {
++              oldblock = newblock;
++              newblock = ablocks[--a];
++              bh = sb_getblk(tree->inode->i_sb, newblock);
++              if (!bh) {
++                      err = -EIO;
++                      goto cleanup;
++              }
++              lock_buffer(bh);
++
++              if ((err = ext3_journal_get_create_access(handle, bh)))
++                      goto cleanup;
++
++              neh = EXT_BLOCK_HDR(bh);
++              neh->eh_entries = 1;
++              neh->eh_magic = EXT3_EXT_MAGIC;
++              neh->eh_max = ext3_ext_space_block_idx(tree);
++              neh->eh_depth = depth - i; 
++              fidx = EXT_FIRST_INDEX(neh);
++              fidx->ei_block = border;
++              fidx->ei_leaf = oldblock;
++
++              ext_debug(tree, "int.index at %d (block %lu): %lu -> %lu\n",
++                        i, newblock, border, oldblock);
++              /* copy indexes */
++              m = 0;
++              path[i].p_idx++;
++
++              ext_debug(tree, "cur 0x%p, last 0x%p\n", path[i].p_idx,
++                        EXT_MAX_INDEX(path[i].p_hdr));
++              EXT_ASSERT(EXT_MAX_INDEX(path[i].p_hdr) ==
++                         EXT_LAST_INDEX(path[i].p_hdr));
++              while (path[i].p_idx <= EXT_MAX_INDEX(path[i].p_hdr)) {
++                      ext_debug(tree, "%d: move %d:%d in new index %lu\n",
++                                i, path[i].p_idx->ei_block,
++                                path[i].p_idx->ei_leaf, newblock);
++                      memmove(++fidx, path[i].p_idx++,
++                              sizeof(struct ext3_extent_idx));
++                      neh->eh_entries++;
++                      EXT_ASSERT(neh->eh_entries <= neh->eh_max);
++                      m++;
++              }
++              mark_buffer_uptodate(bh, 1);
++              unlock_buffer(bh);
++
++              if ((err = ext3_journal_dirty_metadata(handle, bh)))
++                      goto cleanup;
++              brelse(bh);
++              bh = NULL;
++
++              /* correct old index */
++              if (m) {
++                      err = ext3_ext_get_access(handle, tree, path + i);
++                      if (err)
++                              goto cleanup;
++                      path[i].p_hdr->eh_entries -= m;
++                      err = ext3_ext_dirty(handle, tree, path + i);
++                      if (err)
++                              goto cleanup;
++              }
++
++              i--;
++      }
++
++      /* insert new index */
++      if (!err)
++              err = ext3_ext_insert_index(handle, tree, path + at,
++                                          border, newblock);
++
++cleanup:
++      if (bh) {
++              if (buffer_locked(bh))
++                      unlock_buffer(bh);
++              brelse(bh);
++      }
++
++      if (err) {
++              /* free all allocated blocks in error case */
++              for (i = 0; i < depth; i++) {
++                      if (!ablocks[i])
++                              continue;
++                      ext3_free_blocks(handle, tree->inode, ablocks[i], 1);
++              }
++      }
++      kfree(ablocks);
++
++      return err;
++}
++
++/*
++ * routine implements tree growing procedure:
++ *  - allocates new block
++ *  - moves top-level data (index block or leaf) into the new block
++ *  - initialize new top-level, creating index that points to the
++ *    just created block
++ */
++static int ext3_ext_grow_indepth(handle_t *handle,
++                               struct ext3_extents_tree *tree,
++                               struct ext3_ext_path *path,
++                               struct ext3_extent *newext)
++{
++      struct ext3_ext_path *curp = path;
++      struct ext3_extent_header *neh;
++      struct ext3_extent_idx *fidx;
++      struct buffer_head *bh;
++      unsigned long newblock;
++      int err = 0;
++
++      newblock = ext3_ext_new_block(handle, tree, path, newext, &err);
++      if (newblock == 0)
++              return err;
++
++      bh = sb_getblk(tree->inode->i_sb, newblock);
++      if (!bh) {
++              err = -EIO;
++              ext3_std_error(tree->inode->i_sb, err);
++              return err;
++      }
++      lock_buffer(bh);
++
++      if ((err = ext3_journal_get_create_access(handle, bh))) {
++              unlock_buffer(bh);
++              goto out;       
++      }
++
++      /* move top-level index/leaf into new block */
++      memmove(bh->b_data, curp->p_hdr, tree->buffer_len);
++
++      /* set size of new block */
++      neh = EXT_BLOCK_HDR(bh);
++      /* old root could have indexes or leaves
++       * so calculate eh_max right way */
++      if (EXT_DEPTH(tree))
++              neh->eh_max = ext3_ext_space_block_idx(tree);
++      else
++              neh->eh_max = ext3_ext_space_block(tree);
++      neh->eh_magic = EXT3_EXT_MAGIC;
++      mark_buffer_uptodate(bh, 1);
++      unlock_buffer(bh);
++
++      if ((err = ext3_journal_dirty_metadata(handle, bh)))
++              goto out;
++
++      /* create index in new top-level index: num,max,pointer */
++      if ((err = ext3_ext_get_access(handle, tree, curp)))
++              goto out;
++
++      curp->p_hdr->eh_magic = EXT3_EXT_MAGIC;
++      curp->p_hdr->eh_max = ext3_ext_space_root_idx(tree);
++      curp->p_hdr->eh_entries = 1;
++      curp->p_idx = EXT_FIRST_INDEX(curp->p_hdr);
++      /* FIXME: it works, but actually path[0] can be index */
++      curp->p_idx->ei_block = EXT_FIRST_EXTENT(path[0].p_hdr)->ee_block;
++      curp->p_idx->ei_leaf = newblock;
++
++      neh = EXT_ROOT_HDR(tree);
++      fidx = EXT_FIRST_INDEX(neh);
++      ext_debug(tree, "new root: num %d(%d), lblock %d, ptr %d\n",
++                neh->eh_entries, neh->eh_max, fidx->ei_block, fidx->ei_leaf); 
++
++      neh->eh_depth = path->p_depth + 1;
++      err = ext3_ext_dirty(handle, tree, curp);
++out:
++      brelse(bh);
++
++      return err;
++}
++
++/*
++ * routine finds empty index and adds new leaf. if no free index found
++ * then it requests in-depth growing
++ */
++static int ext3_ext_create_new_leaf(handle_t *handle,
++                                  struct ext3_extents_tree *tree,
++                                  struct ext3_ext_path *path,
++                                  struct ext3_extent *newext)
++{
++      struct ext3_ext_path *curp;
++      int depth, i, err = 0;
++
++repeat:
++      i = depth = EXT_DEPTH(tree);
++      
++      /* walk up to the tree and look for free index entry */
++      curp = path + depth;
++      while (i > 0 && !EXT_HAS_FREE_INDEX(curp)) {
++              i--;
++              curp--;
++      }
++
++      /* we use already allocated block for index block
++       * so, subsequent data blocks should be contigoues */
++      if (EXT_HAS_FREE_INDEX(curp)) {
++              /* if we found index with free entry, then use that
++               * entry: create all needed subtree and add new leaf */
++              err = ext3_ext_split(handle, tree, path, newext, i);
++
++              /* refill path */
++              ext3_ext_drop_refs(path);
++              path = ext3_ext_find_extent(tree, newext->ee_block, path);
++              if (IS_ERR(path))
++                      err = PTR_ERR(path);
++      } else {
++              /* tree is full, time to grow in depth */
++              err = ext3_ext_grow_indepth(handle, tree, path, newext);
++
++              /* refill path */
++              ext3_ext_drop_refs(path);
++              path = ext3_ext_find_extent(tree, newext->ee_block, path);
++              if (IS_ERR(path))
++                      err = PTR_ERR(path);
++
++              /*
++               * only first (depth 0 -> 1) produces free space
++               * in all other cases we have to split growed tree
++               */
++              depth = EXT_DEPTH(tree);
++              if (path[depth].p_hdr->eh_entries == path[depth].p_hdr->eh_max) {
++                      /* now we need split */
++                      goto repeat;
++              }
++      }
++
++      if (err)
++              return err;
++
++      return 0;
++}
++
++/*
++ * returns allocated block in subsequent extent or EXT_MAX_BLOCK
++ * NOTE: it consider block number from index entry as
++ * allocated block. thus, index entries have to be consistent
++ * with leafs
++ */
++static unsigned long
++ext3_ext_next_allocated_block(struct ext3_ext_path *path)
++{
++      int depth;
++
++      EXT_ASSERT(path != NULL);
++      depth = path->p_depth;
++
++      if (depth == 0 && path->p_ext == NULL)
++              return EXT_MAX_BLOCK;
++
++      /* FIXME: what if index isn't full ?! */
++      while (depth >= 0) {
++              if (depth == path->p_depth) {
++                      /* leaf */
++                      if (path[depth].p_ext !=
++                          EXT_LAST_EXTENT(path[depth].p_hdr))
++                              return path[depth].p_ext[1].ee_block;
++              } else {
++                      /* index */
++                      if (path[depth].p_idx !=
++                          EXT_LAST_INDEX(path[depth].p_hdr))
++                              return path[depth].p_idx[1].ei_block;
++              }
++              depth--;        
++      }
++
++      return EXT_MAX_BLOCK;
++}
++
++/*
++ * returns first allocated block from next leaf or EXT_MAX_BLOCK
++ */
++static unsigned ext3_ext_next_leaf_block(struct ext3_extents_tree *tree,
++                                       struct ext3_ext_path *path)
++{
++      int depth;
++
++      EXT_ASSERT(path != NULL);
++      depth = path->p_depth;
++
++      /* zero-tree has no leaf blocks at all */
++      if (depth == 0)
++              return EXT_MAX_BLOCK;
++
++      /* go to index block */
++      depth--;
++      
++      while (depth >= 0) {
++              if (path[depth].p_idx !=
++                  EXT_LAST_INDEX(path[depth].p_hdr))
++                      return path[depth].p_idx[1].ei_block;
++              depth--;        
++      }
++
++      return EXT_MAX_BLOCK;
++}
++
++/*
++ * if leaf gets modified and modified extent is first in the leaf
++ * then we have to correct all indexes above
++ * TODO: do we need to correct tree in all cases?
++ */
++int ext3_ext_correct_indexes(handle_t *handle, struct ext3_extents_tree *tree,
++                           struct ext3_ext_path *path)
++{
++      struct ext3_extent_header *eh;
++      int depth = EXT_DEPTH(tree);    
++      struct ext3_extent *ex;
++      unsigned long border;
++      int k, err = 0;
++      
++      eh = path[depth].p_hdr;
++      ex = path[depth].p_ext;
++      EXT_ASSERT(ex);
++      EXT_ASSERT(eh);
++      
++      if (depth == 0) {
++              /* there is no tree at all */
++              return 0;
++      }
++      
++      if (ex != EXT_FIRST_EXTENT(eh)) {
++              /* we correct tree if first leaf got modified only */
++              return 0;
++      }
++      
++      /*
++       * TODO: we need correction if border is smaller then current one
++       */
++      k = depth - 1;
++      border = path[depth].p_ext->ee_block;
++      if ((err = ext3_ext_get_access(handle, tree, path + k)))
++              return err;
++      path[k].p_idx->ei_block = border;
++      if ((err = ext3_ext_dirty(handle, tree, path + k)))
++              return err;
++
++      while (k--) {
++              /* change all left-side indexes */
++              if (path[k+1].p_idx != EXT_FIRST_INDEX(path[k+1].p_hdr))
++                      break;
++              if ((err = ext3_ext_get_access(handle, tree, path + k)))
++                      break;
++              path[k].p_idx->ei_block = border;
++              if ((err = ext3_ext_dirty(handle, tree, path + k)))
++                      break;
++      }
++
++      return err;
++}
++
++static int inline
++ext3_can_extents_be_merged(struct ext3_extents_tree *tree,
++                         struct ext3_extent *ex1,
++                         struct ext3_extent *ex2)
++{
++      if (ex1->ee_block + ex1->ee_len != ex2->ee_block)
++              return 0;
++
++#ifdef AGRESSIVE_TEST
++      if (ex1->ee_len >= 4)
++              return 0;
++#endif
++
++      if (!tree->ops->mergable)
++              return 1;
++
++      return tree->ops->mergable(ex1, ex2);
++}
++
++/*
++ * this routine tries to merge requsted extent into the existing
++ * extent or inserts requested extent as new one into the tree,
++ * creating new leaf in no-space case
++ */
++int ext3_ext_insert_extent(handle_t *handle, struct ext3_extents_tree *tree,
++                         struct ext3_ext_path *path,
++                         struct ext3_extent *newext)
++{
++      struct ext3_extent_header * eh;
++      struct ext3_extent *ex, *fex;
++      struct ext3_extent *nearex; /* nearest extent */
++      struct ext3_ext_path *npath = NULL;
++      int depth, len, err, next;
++
++      EXT_ASSERT(newext->ee_len > 0);
++      depth = EXT_DEPTH(tree);
++      ex = path[depth].p_ext;
++      EXT_ASSERT(path[depth].p_hdr);
++
++      /* try to insert block into found extent and return */
++      if (ex && ext3_can_extents_be_merged(tree, ex, newext)) {
++              ext_debug(tree, "append %d block to %d:%d (from %d)\n",
++                        newext->ee_len, ex->ee_block, ex->ee_len,
++                        ex->ee_start);
++              if ((err = ext3_ext_get_access(handle, tree, path + depth)))
++                      return err;
++              ex->ee_len += newext->ee_len;
++              eh = path[depth].p_hdr;
++              nearex = ex;
++              goto merge;
++      }
++
++repeat:
++      depth = EXT_DEPTH(tree);
++      eh = path[depth].p_hdr;
++      if (eh->eh_entries < eh->eh_max)
++              goto has_space;
++
++      /* probably next leaf has space for us? */
++      fex = EXT_LAST_EXTENT(eh);
++      next = ext3_ext_next_leaf_block(tree, path);
++      if (newext->ee_block > fex->ee_block && next != EXT_MAX_BLOCK) {
++              ext_debug(tree, "next leaf block - %d\n", next);
++              EXT_ASSERT(!npath);
++              npath = ext3_ext_find_extent(tree, next, NULL);
++              if (IS_ERR(npath))
++                      return PTR_ERR(npath);
++              EXT_ASSERT(npath->p_depth == path->p_depth);
++              eh = npath[depth].p_hdr;
++              if (eh->eh_entries < eh->eh_max) {
++                      ext_debug(tree, "next leaf isnt full(%d)\n",
++                                eh->eh_entries);
++                      path = npath;
++                      goto repeat;
++              }
++              ext_debug(tree, "next leaf hasno free space(%d,%d)\n",
++                        eh->eh_entries, eh->eh_max);
++      }
++
++      /*
++       * there is no free space in found leaf
++       * we're gonna add new leaf in the tree
++       */
++      err = ext3_ext_create_new_leaf(handle, tree, path, newext);
++      if (err)
++              goto cleanup;
++      depth = EXT_DEPTH(tree);
++      eh = path[depth].p_hdr;
++
++has_space:
++      nearex = path[depth].p_ext;
++
++      if ((err = ext3_ext_get_access(handle, tree, path + depth)))
++              goto cleanup;
++
++      if (!nearex) {
++              /* there is no extent in this leaf, create first one */
++              ext_debug(tree, "first extent in the leaf: %d:%d:%d\n",
++                        newext->ee_block, newext->ee_start,
++                        newext->ee_len);
++              path[depth].p_ext = EXT_FIRST_EXTENT(eh);
++      } else if (newext->ee_block > nearex->ee_block) {
++              EXT_ASSERT(newext->ee_block != nearex->ee_block);
++              if (nearex != EXT_LAST_EXTENT(eh)) {
++                      len = EXT_MAX_EXTENT(eh) - nearex;
++                      len = (len - 1) * sizeof(struct ext3_extent);
++                      len = len < 0 ? 0 : len;
++                      ext_debug(tree, "insert %d:%d:%d after: nearest 0x%p, "
++                                "move %d from 0x%p to 0x%p\n",
++                                newext->ee_block, newext->ee_start,
++                                newext->ee_len,
++                                nearex, len, nearex + 1, nearex + 2);
++                      memmove(nearex + 2, nearex + 1, len);
++              }
++              path[depth].p_ext = nearex + 1;
++      } else {
++              EXT_ASSERT(newext->ee_block != nearex->ee_block);
++              len = (EXT_MAX_EXTENT(eh) - nearex) * sizeof(struct ext3_extent);
++              len = len < 0 ? 0 : len;
++              ext_debug(tree, "insert %d:%d:%d before: nearest 0x%p, "
++                        "move %d from 0x%p to 0x%p\n",
++                        newext->ee_block, newext->ee_start, newext->ee_len,
++                        nearex, len, nearex + 1, nearex + 2);
++              memmove(nearex + 1, nearex, len);
++              path[depth].p_ext = nearex;
++      }
++
++      eh->eh_entries++;
++      nearex = path[depth].p_ext;
++      nearex->ee_block = newext->ee_block;
++      nearex->ee_start = newext->ee_start;
++      nearex->ee_len = newext->ee_len;
++      /* FIXME: support for large fs */
++      nearex->ee_start_hi = 0;
++
++merge:
++      /* try to merge extents to the right */
++      while (nearex < EXT_LAST_EXTENT(eh)) {
++              if (!ext3_can_extents_be_merged(tree, nearex, nearex + 1))
++                      break;
++              /* merge with next extent! */
++              nearex->ee_len += nearex[1].ee_len;
++              if (nearex + 1 < EXT_LAST_EXTENT(eh)) {
++                      len = (EXT_LAST_EXTENT(eh) - nearex - 1) *
++                              sizeof(struct ext3_extent);
++                      memmove(nearex + 1, nearex + 2, len);
++              }
++              eh->eh_entries--;
++              EXT_ASSERT(eh->eh_entries > 0);
++      }
++
++      /* try to merge extents to the left */
++
++      /* time to correct all indexes above */
++      err = ext3_ext_correct_indexes(handle, tree, path);
++      if (err)
++              goto cleanup;
++
++      err = ext3_ext_dirty(handle, tree, path + depth);
++
++cleanup:
++      if (npath) {
++              ext3_ext_drop_refs(npath);
++              kfree(npath);
++      }
++      ext3_ext_tree_changed(tree);
++      ext3_ext_invalidate_cache(tree);
++      return err;
++}
++
++int ext3_ext_walk_space(struct ext3_extents_tree *tree, unsigned long block,
++                      unsigned long num, ext_prepare_callback func)
++{
++      struct ext3_ext_path *path = NULL;
++      struct ext3_ext_cache cbex;
++      struct ext3_extent *ex;
++      unsigned long next, start = 0, end = 0;
++      unsigned long last = block + num;
++      int depth, exists, err = 0;
++
++      EXT_ASSERT(tree);
++      EXT_ASSERT(func);
++      EXT_ASSERT(tree->inode);
++      EXT_ASSERT(tree->root);
++
++      while (block < last && block != EXT_MAX_BLOCK) {
++              num = last - block;
++              /* find extent for this block */
++              path = ext3_ext_find_extent(tree, block, path);
++              if (IS_ERR(path)) {
++                      err = PTR_ERR(path);
++                      path = NULL;
++                      break;
++              }
++
++              depth = EXT_DEPTH(tree);
++              EXT_ASSERT(path[depth].p_hdr);
++              ex = path[depth].p_ext;
++              next = ext3_ext_next_allocated_block(path);
++
++              exists = 0;
++              if (!ex) {
++                      /* there is no extent yet, so try to allocate
++                       * all requested space */
++                      start = block;
++                      end = block + num;
++              } else if (ex->ee_block > block) {
++                      /* need to allocate space before found extent */
++                      start = block;
++                      end = ex->ee_block;
++                      if (block + num < end)
++                              end = block + num;
++              } else if (block >= ex->ee_block + ex->ee_len) {
++                      /* need to allocate space after found extent */
++                      start = block;
++                      end = block + num;
++                      if (end >= next)
++                              end = next;
++              } else if (block >= ex->ee_block) {
++                      /* 
++                       * some part of requested space is covered
++                       * by found extent
++                       */
++                      start = block;
++                      end = ex->ee_block + ex->ee_len;
++                      if (block + num < end)
++                              end = block + num;
++                      exists = 1;
++              } else {
++                      BUG();
++              }
++              EXT_ASSERT(end > start);
++
++              if (!exists) {
++                      cbex.ec_block = start;
++                      cbex.ec_len = end - start;
++                      cbex.ec_start = 0;
++                      cbex.ec_type = EXT3_EXT_CACHE_GAP;
++              } else {
++                      cbex.ec_block = ex->ee_block;
++                      cbex.ec_len = ex->ee_len;
++                      cbex.ec_start = ex->ee_start;
++                      cbex.ec_type = EXT3_EXT_CACHE_EXTENT;
++              }
++
++              EXT_ASSERT(cbex.ec_len > 0);
++              EXT_ASSERT(path[depth].p_hdr);
++              err = func(tree, path, &cbex);
++              ext3_ext_drop_refs(path);
++
++              if (err < 0)
++                      break;
++              if (err == EXT_REPEAT)
++                      continue;
++              else if (err == EXT_BREAK) {
++                      err = 0;
++                      break;
++              }
++
++              if (EXT_DEPTH(tree) != depth) {
++                      /* depth was changed. we have to realloc path */
++                      kfree(path);
++                      path = NULL;
++              }
++
++              block = cbex.ec_block + cbex.ec_len;
++      }
++
++      if (path) {
++              ext3_ext_drop_refs(path);
++              kfree(path);
++      }
++
++      return err;
++}
++
++static inline void
++ext3_ext_put_in_cache(struct ext3_extents_tree *tree, __u32 block,
++                    __u32 len, __u32 start, int type)
++{
++      EXT_ASSERT(len > 0);
++      if (tree->cex) {
++              tree->cex->ec_type = type;
++              tree->cex->ec_block = block;
++              tree->cex->ec_len = len;
++              tree->cex->ec_start = start;
++      }
++}
++
++/*
++ * this routine calculate boundaries of the gap requested block fits into
++ * and cache this gap
++ */
++static inline void
++ext3_ext_put_gap_in_cache(struct ext3_extents_tree *tree,
++                        struct ext3_ext_path *path,
++                        unsigned long block)
++{
++      int depth = EXT_DEPTH(tree);
++      unsigned long lblock, len;
++      struct ext3_extent *ex;
++
++      if (!tree->cex)
++              return;
++
++      ex = path[depth].p_ext;
++      if (ex == NULL) {
++              /* there is no extent yet, so gap is [0;-] */
++              lblock = 0;
++              len = EXT_MAX_BLOCK;
++              ext_debug(tree, "cache gap(whole file):");
++      } else if (block < ex->ee_block) {
++              lblock = block;
++              len = ex->ee_block - block;
++              ext_debug(tree, "cache gap(before): %lu [%lu:%lu]",
++                        (unsigned long) block,
++                        (unsigned long) ex->ee_block,
++                        (unsigned long) ex->ee_len);
++      } else if (block >= ex->ee_block + ex->ee_len) {
++              lblock = ex->ee_block + ex->ee_len;
++              len = ext3_ext_next_allocated_block(path);
++              ext_debug(tree, "cache gap(after): [%lu:%lu] %lu",
++                        (unsigned long) ex->ee_block,
++                        (unsigned long) ex->ee_len,
++                        (unsigned long) block);
++              EXT_ASSERT(len > lblock);
++              len = len - lblock;
++      } else {
++              lblock = len = 0;
++              BUG();
++      }
++
++      ext_debug(tree, " -> %lu:%lu\n", (unsigned long) lblock, len);
++      ext3_ext_put_in_cache(tree, lblock, len, 0, EXT3_EXT_CACHE_GAP);
++}
++
++static inline int
++ext3_ext_in_cache(struct ext3_extents_tree *tree, unsigned long block,
++                struct ext3_extent *ex)
++{
++      struct ext3_ext_cache *cex = tree->cex;
++
++      /* is there cache storage at all? */
++      if (!cex)
++              return EXT3_EXT_CACHE_NO;
++
++      /* has cache valid data? */
++      if (cex->ec_type == EXT3_EXT_CACHE_NO)
++              return EXT3_EXT_CACHE_NO;
++
++      EXT_ASSERT(cex->ec_type == EXT3_EXT_CACHE_GAP ||
++                 cex->ec_type == EXT3_EXT_CACHE_EXTENT);
++      if (block >= cex->ec_block && block < cex->ec_block + cex->ec_len) {
++              ex->ee_block = cex->ec_block;
++              ex->ee_start = cex->ec_start;
++              ex->ee_len = cex->ec_len;
++              ext_debug(tree, "%lu cached by %lu:%lu:%lu\n",
++                        (unsigned long) block,
++                        (unsigned long) ex->ee_block,
++                        (unsigned long) ex->ee_len,
++                        (unsigned long) ex->ee_start);
++              return cex->ec_type;
++      }
++
++      /* not in cache */
++      return EXT3_EXT_CACHE_NO;
++}
++
++/*
++ * routine removes index from the index block
++ * it's used in truncate case only. thus all requests are for
++ * last index in the block only
++ */
++int ext3_ext_rm_idx(handle_t *handle, struct ext3_extents_tree *tree,
++                  struct ext3_ext_path *path)
++{
++      struct buffer_head *bh;
++      int err;
++      
++      /* free index block */
++      path--;
++      EXT_ASSERT(path->p_hdr->eh_entries);
++      if ((err = ext3_ext_get_access(handle, tree, path)))
++              return err;
++      path->p_hdr->eh_entries--;
++      if ((err = ext3_ext_dirty(handle, tree, path)))
++              return err;
++      ext_debug(tree, "index is empty, remove it, free block %d\n",
++                path->p_idx->ei_leaf);
++      bh = sb_get_hash_table(tree->inode->i_sb, path->p_idx->ei_leaf);
++      ext3_forget(handle, 1, tree->inode, bh, path->p_idx->ei_leaf);
++      ext3_free_blocks(handle, tree->inode, path->p_idx->ei_leaf, 1);
++      return err;
++}
++
++int ext3_ext_calc_credits_for_insert(struct ext3_extents_tree *tree,
++                                   struct ext3_ext_path *path)
++{
++      int depth = EXT_DEPTH(tree);
++      int needed;
++
++      if (path) {
++              /* probably there is space in leaf? */
++              if (path[depth].p_hdr->eh_entries < path[depth].p_hdr->eh_max)
++                      return 1;
++      }
++      
++      /*
++       * the worste case we're expecting is creation of the
++       * new root (growing in depth) with index splitting
++       * for splitting we have to consider depth + 1 because
++       * previous growing could increase it
++       */
++      depth = depth + 1;
++
++      /* 
++       * growing in depth:
++       * block allocation + new root + old root
++       */
++      needed = EXT3_ALLOC_NEEDED + 2;
++
++      /* index split. we may need:
++       *   allocate intermediate indexes and new leaf
++       *   change two blocks at each level, but root
++       *   modify root block (inode)
++       */
++      needed += (depth * EXT3_ALLOC_NEEDED) + (2 * depth) + 1;
++
++      return needed;
++}
++
++static int
++ext3_ext_split_for_rm(handle_t *handle, struct ext3_extents_tree *tree,
++                    struct ext3_ext_path *path, unsigned long start,
++                    unsigned long end)
++{
++      struct ext3_extent *ex, tex;
++      struct ext3_ext_path *npath;
++      int depth, creds, err;
++
++      depth = EXT_DEPTH(tree);
++      ex = path[depth].p_ext;
++      EXT_ASSERT(ex);
++      EXT_ASSERT(end < ex->ee_block + ex->ee_len - 1);
++      EXT_ASSERT(ex->ee_block < start);
++
++      /* calculate tail extent */
++      tex.ee_block = end + 1;
++      EXT_ASSERT(tex.ee_block < ex->ee_block + ex->ee_len);
++      tex.ee_len = ex->ee_block + ex->ee_len - tex.ee_block;
++
++      creds = ext3_ext_calc_credits_for_insert(tree, path);
++      handle = ext3_ext_journal_restart(handle, creds);
++      if (IS_ERR(handle))
++              return PTR_ERR(handle);
++      
++      /* calculate head extent. use primary extent */
++      err = ext3_ext_get_access(handle, tree, path + depth);
++      if (err)
++              return err;
++      ex->ee_len = start - ex->ee_block;
++      err = ext3_ext_dirty(handle, tree, path + depth);
++      if (err)
++              return err;
++
++      /* FIXME: some callback to free underlying resource
++       * and correct ee_start? */
++      ext_debug(tree, "split extent: head %u:%u, tail %u:%u\n",
++                ex->ee_block, ex->ee_len, tex.ee_block, tex.ee_len);
++
++      npath = ext3_ext_find_extent(tree, ex->ee_block, NULL);
++      if (IS_ERR(npath))
++              return PTR_ERR(npath);
++      depth = EXT_DEPTH(tree);
++      EXT_ASSERT(npath[depth].p_ext->ee_block == ex->ee_block);
++      EXT_ASSERT(npath[depth].p_ext->ee_len == ex->ee_len);
++
++      err = ext3_ext_insert_extent(handle, tree, npath, &tex);
++      ext3_ext_drop_refs(npath);
++      kfree(npath);
++
++      return err;
++}
++
++static int
++ext3_ext_rm_leaf(handle_t *handle, struct ext3_extents_tree *tree,
++               struct ext3_ext_path *path, unsigned long start,
++               unsigned long end)
++{
++      struct ext3_extent *ex, *fu = NULL, *lu, *le;
++      int err = 0, correct_index = 0;
++      int depth = EXT_DEPTH(tree), credits;
++      struct ext3_extent_header *eh;
++      unsigned a, b, block, num;
++
++      ext_debug(tree, "remove [%lu:%lu] in leaf\n", start, end);
++      if (!path[depth].p_hdr)
++              path[depth].p_hdr = EXT_BLOCK_HDR(path[depth].p_bh);
++      eh = path[depth].p_hdr;
++      EXT_ASSERT(eh);
++      EXT_ASSERT(eh->eh_entries <= eh->eh_max);
++      EXT_ASSERT(eh->eh_magic == EXT3_EXT_MAGIC);
++      
++      /* find where to start removing */
++      le = ex = EXT_LAST_EXTENT(eh);
++      while (ex != EXT_FIRST_EXTENT(eh)) {
++              if (ex->ee_block <= end)
++                      break;
++              ex--;
++      }
++
++      if (start > ex->ee_block && end < ex->ee_block + ex->ee_len - 1) {
++              /* removal of internal part of the extent requested
++               * tail and head must be placed in different extent
++               * so, we have to insert one more extent */
++              path[depth].p_ext = ex;
++              return ext3_ext_split_for_rm(handle, tree, path, start, end);
++      }
++      
++      lu = ex;
++      while (ex >= EXT_FIRST_EXTENT(eh) && ex->ee_block + ex->ee_len > start) {
++              ext_debug(tree, "remove ext %u:%u\n", ex->ee_block, ex->ee_len);
++              path[depth].p_ext = ex;
++      
++              a = ex->ee_block > start ? ex->ee_block : start;
++              b = ex->ee_block + ex->ee_len - 1 < end ?
++                      ex->ee_block + ex->ee_len - 1 : end;
++              
++              ext_debug(tree, "  border %u:%u\n", a, b);
++
++              if (a != ex->ee_block && b != ex->ee_block + ex->ee_len - 1) {
++                      block = 0;
++                      num = 0;
++                      BUG();
++              } else if (a != ex->ee_block) {
++                      /* remove tail of the extent */
++                      block = ex->ee_block;
++                      num = a - block;
++              } else if (b != ex->ee_block + ex->ee_len - 1) {
++                      /* remove head of the extent */
++                      block = a;
++                      num = b - a;
++              } else {
++                      /* remove whole extent: excelent! */
++                      block = ex->ee_block; 
++                      num = 0;
++                      EXT_ASSERT(a == ex->ee_block &&
++                                 b == ex->ee_block + ex->ee_len - 1);
++              }
++
++              if (ex == EXT_FIRST_EXTENT(eh))
++                      correct_index = 1;
++
++              credits = 1;
++              if (correct_index)
++                      credits += (EXT_DEPTH(tree) * EXT3_ALLOC_NEEDED) + 1;
++              if (tree->ops->remove_extent_credits)
++                      credits+=tree->ops->remove_extent_credits(tree,ex,a,b);
++              
++              handle = ext3_ext_journal_restart(handle, credits);
++              if (IS_ERR(handle)) {
++                      err = PTR_ERR(handle);
++                      goto out;
++              }
++
++              err = ext3_ext_get_access(handle, tree, path + depth);
++              if (err)
++                      goto out;
++
++              if (tree->ops->remove_extent)
++                      err = tree->ops->remove_extent(tree, ex, a, b);
++              if (err)
++                      goto out;
++
++              if (num == 0) {
++                      /* this extent is removed entirely mark slot unused */
++                      ex->ee_start = 0;
++                      eh->eh_entries--;
++                      fu = ex;
++              }
++
++              ex->ee_block = block;
++              ex->ee_len = num;
++
++              err = ext3_ext_dirty(handle, tree, path + depth);
++              if (err)
++                      goto out;
++
++              ext_debug(tree, "new extent: %u:%u:%u\n",
++                        ex->ee_block, ex->ee_len, ex->ee_start);
++              ex--;
++      }
++
++      if (fu) {
++              /* reuse unused slots */
++              while (lu < le) {
++                      if (lu->ee_start) {
++                              *fu = *lu;
++                              lu->ee_start = 0;
++                              fu++;
++                      }
++                      lu++;
++              }
++      }
++
++      if (correct_index && eh->eh_entries)
++              err = ext3_ext_correct_indexes(handle, tree, path);
++
++      /* if this leaf is free, then we should
++       * remove it from index block above */
++      if (err == 0 && eh->eh_entries == 0 && path[depth].p_bh != NULL)
++              err = ext3_ext_rm_idx(handle, tree, path + depth);
++
++out:
++      return err;
++}
++
++
++static struct ext3_extent_idx *
++ext3_ext_last_covered(struct ext3_extent_header *hdr, unsigned long block)
++{
++      struct ext3_extent_idx *ix;
++      
++      ix = EXT_LAST_INDEX(hdr);
++      while (ix != EXT_FIRST_INDEX(hdr)) {
++              if (ix->ei_block <= block)
++                      break;
++              ix--;
++      }
++      return ix;
++}
++
++/*
++ * returns 1 if current index have to be freed (even partial)
++ */
++static int inline
++ext3_ext_more_to_rm(struct ext3_ext_path *path)
++{
++      EXT_ASSERT(path->p_idx);
++
++      if (path->p_idx < EXT_FIRST_INDEX(path->p_hdr))
++              return 0;
++
++      /*
++       * if truncate on deeper level happened it it wasn't partial
++       * so we have to consider current index for truncation
++       */
++      if (path->p_hdr->eh_entries == path->p_block)
++              return 0;
++      return 1;
++}
++
++int ext3_ext_remove_space(struct ext3_extents_tree *tree,
++                        unsigned long start, unsigned long end)
++{
++      struct inode *inode = tree->inode;
++      struct super_block *sb = inode->i_sb;
++      int depth = EXT_DEPTH(tree);
++      struct ext3_ext_path *path;
++      handle_t *handle;
++      int i = 0, err = 0;
++
++      ext_debug(tree, "space to be removed: %lu:%lu\n", start, end);
++
++      /* probably first extent we're gonna free will be last in block */
++      handle = ext3_journal_start(inode, depth + 1);
++      if (IS_ERR(handle))
++              return PTR_ERR(handle);
++
++      ext3_ext_invalidate_cache(tree);
++
++      /*
++       * we start scanning from right side freeing all the blocks
++       * after i_size and walking into the deep
++       */
++      path = kmalloc(sizeof(struct ext3_ext_path) * (depth + 1), GFP_KERNEL);
++      if (IS_ERR(path)) {
++              ext3_error(sb, __FUNCTION__, "Can't allocate path array");
++              ext3_journal_stop(handle, inode);
++              return -ENOMEM;
++      }
++      memset(path, 0, sizeof(struct ext3_ext_path) * (depth + 1));
++      path[i].p_hdr = EXT_ROOT_HDR(tree);
++      
++      while (i >= 0 && err == 0) {
++              if (i == depth) {
++                      /* this is leaf block */
++                      err = ext3_ext_rm_leaf(handle, tree, path, start, end);
++                      /* root level have p_bh == NULL, brelse() eats this */
++                      brelse(path[i].p_bh);
++                      i--;
++                      continue;
++              }
++              
++              /* this is index block */
++              if (!path[i].p_hdr) {
++                      ext_debug(tree, "initialize header\n");
++                      path[i].p_hdr = EXT_BLOCK_HDR(path[i].p_bh);
++              }
++
++              EXT_ASSERT(path[i].p_hdr->eh_entries <= path[i].p_hdr->eh_max);
++              EXT_ASSERT(path[i].p_hdr->eh_magic == EXT3_EXT_MAGIC);
++              
++              if (!path[i].p_idx) {
++                      /* this level hasn't touched yet */
++                      path[i].p_idx =
++                              ext3_ext_last_covered(path[i].p_hdr, end);
++                      path[i].p_block = path[i].p_hdr->eh_entries + 1;
++                      ext_debug(tree, "init index ptr: hdr 0x%p, num %d\n",
++                                path[i].p_hdr, path[i].p_hdr->eh_entries);
++              } else {
++                      /* we've already was here, see at next index */
++                      path[i].p_idx--;
++              }
++
++              ext_debug(tree, "level %d - index, first 0x%p, cur 0x%p\n",
++                        i, EXT_FIRST_INDEX(path[i].p_hdr),
++                        path[i].p_idx);
++              if (ext3_ext_more_to_rm(path + i)) {
++                      /* go to the next level */
++                      ext_debug(tree, "move to level %d (block %d)\n",
++                                i + 1, path[i].p_idx->ei_leaf);
++                      memset(path + i + 1, 0, sizeof(*path));
++                      path[i+1].p_bh = sb_bread(sb, path[i].p_idx->ei_leaf);
++                      if (!path[i+1].p_bh) {
++                              /* should we reset i_size? */
++                              err = -EIO;
++                              break;
++                      }
++                      /* put actual number of indexes to know is this
++                       * number got changed at the next iteration */
++                      path[i].p_block = path[i].p_hdr->eh_entries;
++                      i++;
++              } else {
++                      /* we finish processing this index, go up */
++                      if (path[i].p_hdr->eh_entries == 0 && i > 0) {
++                              /* index is empty, remove it
++                               * handle must be already prepared by the
++                               * truncatei_leaf() */
++                              err = ext3_ext_rm_idx(handle, tree, path + i);
++                      }
++                      /* root level have p_bh == NULL, brelse() eats this */
++                      brelse(path[i].p_bh);
++                      i--;
++                      ext_debug(tree, "return to level %d\n", i);
++              }
++      }
++
++      /* TODO: flexible tree reduction should be here */
++      if (path->p_hdr->eh_entries == 0) {
++              /*
++               * truncate to zero freed all the tree
++               * so, we need to correct eh_depth
++               */
++              err = ext3_ext_get_access(handle, tree, path);
++              if (err == 0) {
++                      EXT_ROOT_HDR(tree)->eh_depth = 0;
++                      EXT_ROOT_HDR(tree)->eh_max = ext3_ext_space_root(tree);
++                      err = ext3_ext_dirty(handle, tree, path);
++              }
++      }
++      ext3_ext_tree_changed(tree);
++
++      kfree(path);
++      ext3_journal_stop(handle, inode);
++
++      return err;
++}
++
++/*
++ * called at mount time
++ */
++void ext3_ext_init(struct super_block *sb)
++{
++      /*
++       * possible initialization would be here
++       */
++
++      if (test_opt(sb, EXTENTS)) {
++              printk("EXT3-fs: file extents enabled");
++#ifdef AGRESSIVE_TEST
++              printk(", agressive tests");
++#endif
++#ifdef CHECK_BINSEARCH
++              printk(", check binsearch");
++#endif
++              printk("\n");
++      }
++}
++
++/*
++ * called at umount time
++ */
++void ext3_ext_release(struct super_block *sb)
++{
++}
++
++/************************************************************************
++ * VFS related routines
++ ************************************************************************/
++
++static int ext3_get_inode_write_access(handle_t *handle, void *buffer)
++{
++      /* we use in-core data, not bh */
++      return 0;
++}
++
++static int ext3_mark_buffer_dirty(handle_t *handle, void *buffer)
++{
++      struct inode *inode = buffer;
++      return ext3_mark_inode_dirty(handle, inode);
++}
++
++static int ext3_ext_mergable(struct ext3_extent *ex1,
++                           struct ext3_extent *ex2)
++{
++      /* FIXME: support for large fs */
++      if (ex1->ee_start + ex1->ee_len == ex2->ee_start)
++              return 1;
++      return 0;
++}
++
++static int
++ext3_remove_blocks_credits(struct ext3_extents_tree *tree,
++                         struct ext3_extent *ex,
++                         unsigned long from, unsigned long to)
++{
++      int needed;
++      
++      /* at present, extent can't cross block group */;
++      needed = 4; /* bitmap + group desc + sb + inode */
++
++#ifdef CONFIG_QUOTA
++      needed += 2 * EXT3_SINGLEDATA_TRANS_BLOCKS;
++#endif
++      return needed;
++}
++
++static int
++ext3_remove_blocks(struct ext3_extents_tree *tree,
++                 struct ext3_extent *ex,
++                 unsigned long from, unsigned long to)
++{
++      int needed = ext3_remove_blocks_credits(tree, ex, from, to);
++      handle_t *handle = ext3_journal_start(tree->inode, needed);
++      struct buffer_head *bh;
++      int i;
++
++      if (IS_ERR(handle))
++              return PTR_ERR(handle);
++      if (from >= ex->ee_block && to == ex->ee_block + ex->ee_len - 1) {
++              /* tail removal */
++              unsigned long num, start;
++              num = ex->ee_block + ex->ee_len - from;
++              start = ex->ee_start + ex->ee_len - num;
++              ext_debug(tree, "free last %lu blocks starting %lu\n",
++                        num, start);
++              for (i = 0; i < num; i++) {
++                      bh = sb_get_hash_table(tree->inode->i_sb, start + i);
++                      ext3_forget(handle, 0, tree->inode, bh, start + i);
++              }
++              ext3_free_blocks(handle, tree->inode, start, num);
++      } else if (from == ex->ee_block && to <= ex->ee_block + ex->ee_len - 1) {
++              printk("strange request: removal %lu-%lu from %u:%u\n",
++                     from, to, ex->ee_block, ex->ee_len);
++      } else {
++              printk("strange request: removal(2) %lu-%lu from %u:%u\n",
++                     from, to, ex->ee_block, ex->ee_len);
++      }
++      ext3_journal_stop(handle, tree->inode);
++      return 0;
++}
++
++int ext3_ext_find_goal(struct inode *inode, struct ext3_ext_path *path,
++                     unsigned long block)
++{
++      struct ext3_inode_info *ei = EXT3_I(inode);
++      unsigned long bg_start;
++      unsigned long colour;
++      int depth;
++      
++      if (path) {
++              struct ext3_extent *ex;
++              depth = path->p_depth;
++              
++              /* try to predict block placement */
++              if ((ex = path[depth].p_ext))
++                      return ex->ee_start + (block - ex->ee_block);
++
++              /* it looks index is empty
++               * try to find starting from index itself */
++              if (path[depth].p_bh)
++                      return path[depth].p_bh->b_blocknr;
++      }
++
++      /* OK. use inode's group */
++      bg_start = (ei->i_block_group * EXT3_BLOCKS_PER_GROUP(inode->i_sb)) +
++              le32_to_cpu(EXT3_SB(inode->i_sb)->s_es->s_first_data_block);
++      colour = (current->pid % 16) *
++                      (EXT3_BLOCKS_PER_GROUP(inode->i_sb) / 16);
++      return bg_start + colour + block;
++}
++
++static int ext3_new_block_cb(handle_t *handle, struct ext3_extents_tree *tree,
++                           struct ext3_ext_path *path,
++                           struct ext3_extent *ex, int *err)
++{
++      struct inode *inode = tree->inode;
++      int newblock, goal;
++      
++      EXT_ASSERT(path);
++      EXT_ASSERT(ex);
++      EXT_ASSERT(ex->ee_start);
++      EXT_ASSERT(ex->ee_len);
++      
++      /* reuse block from the extent to order data/metadata */
++      newblock = ex->ee_start++;
++      ex->ee_len--;
++      if (ex->ee_len == 0) {
++              ex->ee_len = 1;
++              /* allocate new block for the extent */
++              goal = ext3_ext_find_goal(inode, path, ex->ee_block);
++              ex->ee_start = ext3_new_block(handle, inode, goal, 0, 0, err);
++              if (ex->ee_start == 0) {
++                      /* error occured: restore old extent */
++                      ex->ee_start = newblock;
++                      return 0;
++              }
++      }
++      return newblock;
++}
++
++static struct ext3_extents_helpers ext3_blockmap_helpers = {
++      .get_write_access       = ext3_get_inode_write_access,
++      .mark_buffer_dirty      = ext3_mark_buffer_dirty,
++      .mergable               = ext3_ext_mergable,
++      .new_block              = ext3_new_block_cb,
++      .remove_extent          = ext3_remove_blocks,
++      .remove_extent_credits  = ext3_remove_blocks_credits,
++};
++
++void ext3_init_tree_desc(struct ext3_extents_tree *tree,
++                       struct inode *inode)
++{
++      tree->inode = inode;
++      tree->root = (void *) EXT3_I(inode)->i_data;
++      tree->buffer = (void *) inode;
++      tree->buffer_len = sizeof(EXT3_I(inode)->i_data);
++      tree->cex = (struct ext3_ext_cache *) &EXT3_I(inode)->i_cached_extent;
++      tree->ops = &ext3_blockmap_helpers;
++}
++
++int ext3_ext_get_block(handle_t *handle, struct inode *inode,
++                     long iblock, struct buffer_head *bh_result, int create)
++{
++      struct ext3_ext_path *path = NULL;
++      struct ext3_extent newex;
++      struct ext3_extent *ex;
++      int goal, newblock, err = 0, depth;
++      struct ext3_extents_tree tree;
++
++      clear_bit(BH_New, &bh_result->b_state);
++      ext3_init_tree_desc(&tree, inode);
++      ext_debug(&tree, "block %d requested for inode %u\n",
++                (int) iblock, (unsigned) inode->i_ino);
++      down_write(&EXT3_I(inode)->truncate_sem);
++
++      /* check in cache */
++      if ((goal = ext3_ext_in_cache(&tree, iblock, &newex))) {
++              if (goal == EXT3_EXT_CACHE_GAP) {
++                      if (!create) {
++                              /* block isn't allocated yet and
++                               * user don't want to allocate it */
++                              goto out2;
++                      }
++                      /* we should allocate requested block */
++              } else if (goal == EXT3_EXT_CACHE_EXTENT) {
++                      /* block is already allocated */
++                      newblock = iblock - newex.ee_block + newex.ee_start;
++                      goto out;
++              } else {
++                      EXT_ASSERT(0);
++              }
++      }
++
++      /* find extent for this block */
++      path = ext3_ext_find_extent(&tree, iblock, NULL);
++      if (IS_ERR(path)) {
++              err = PTR_ERR(path);
++              path = NULL;
++              goto out2;
++      }
++
++      depth = EXT_DEPTH(&tree);
++
++      /*
++       * consistent leaf must not be empty
++       * this situations is possible, though, _during_ tree modification
++       * this is why assert can't be put in ext3_ext_find_extent()
++       */
++      EXT_ASSERT(path[depth].p_ext != NULL || depth == 0);
++
++      if ((ex = path[depth].p_ext)) {
++              /* if found exent covers block, simple return it */
++              if (iblock >= ex->ee_block && iblock < ex->ee_block + ex->ee_len) {
++                      newblock = iblock - ex->ee_block + ex->ee_start;
++                      ext_debug(&tree, "%d fit into %d:%d -> %d\n",
++                                (int) iblock, ex->ee_block, ex->ee_len,
++                                newblock);
++                      ext3_ext_put_in_cache(&tree, ex->ee_block,
++                                            ex->ee_len, ex->ee_start,
++                                            EXT3_EXT_CACHE_EXTENT);
++                      goto out;
++              }
++      }
++
++      /*
++       * requested block isn't allocated yet
++       * we couldn't try to create block if create flag is zero 
++       */
++      if (!create) {
++              /* put just found gap into cache to speedup subsequest reqs */
++              ext3_ext_put_gap_in_cache(&tree, path, iblock);
++              goto out2;
++      }
++
++      /* allocate new block */
++      goal = ext3_ext_find_goal(inode, path, iblock);
++      newblock = ext3_new_block(handle, inode, goal, 0, 0, &err);
++      if (!newblock)
++              goto out2;
++      ext_debug(&tree, "allocate new block: goal %d, found %d\n",
++                goal, newblock);
++
++      /* try to insert new extent into found leaf and return */
++      newex.ee_block = iblock;
++      newex.ee_start = newblock;
++      newex.ee_len = 1;
++      err = ext3_ext_insert_extent(handle, &tree, path, &newex);
++      if (err)
++              goto out2;
++      
++      if (inode->i_size > EXT3_I(inode)->i_disksize)
++              EXT3_I(inode)->i_disksize = inode->i_size;
++
++      /* previous routine could use block we allocated */
++      newblock = newex.ee_start;
++      set_bit(BH_New, &bh_result->b_state);
++
++      ext3_ext_put_in_cache(&tree, newex.ee_block, newex.ee_len,
++                            newex.ee_start, EXT3_EXT_CACHE_EXTENT);
++out:
++      ext3_ext_show_leaf(&tree, path);
++      set_bit(BH_Mapped, &bh_result->b_state);
++      bh_result->b_dev = inode->i_sb->s_dev;
++      bh_result->b_blocknr = newblock;
++out2:
++      if (path) {
++              ext3_ext_drop_refs(path);
++              kfree(path);
++      }
++      up_write(&EXT3_I(inode)->truncate_sem);
++
++      return err;     
++}
++
++void ext3_ext_truncate(struct inode * inode)
++{
++      struct address_space *mapping = inode->i_mapping;
++      struct super_block *sb = inode->i_sb;
++      struct ext3_extents_tree tree;
++      unsigned long last_block;
++      handle_t *handle;
++      int err = 0;
++
++      ext3_init_tree_desc(&tree, inode);
++
++      /*
++       * probably first extent we're gonna free will be last in block
++       */
++      err = ext3_writepage_trans_blocks(inode) + 3;
++      handle = ext3_journal_start(inode, err);
++      if (IS_ERR(handle))
++              return;
++
++      ext3_block_truncate_page(handle, mapping, inode->i_size);
++
++      down_write(&EXT3_I(inode)->truncate_sem);
++      ext3_ext_invalidate_cache(&tree);
++
++      /* 
++       * TODO: optimization is possible here
++       * probably we need not scaning at all,
++       * because page truncation is enough
++       */
++      if (ext3_orphan_add(handle, inode))
++              goto out_stop;
++
++      /* we have to know where to truncate from in crash case */
++      EXT3_I(inode)->i_disksize = inode->i_size;
++      ext3_mark_inode_dirty(handle, inode);
++
++      last_block = (inode->i_size + sb->s_blocksize - 1) >>
++                      EXT3_BLOCK_SIZE_BITS(sb);
++      err = ext3_ext_remove_space(&tree, last_block, EXT_MAX_BLOCK);
++      
++      /* In a multi-transaction truncate, we only make the final
++       * transaction synchronous */
++      if (IS_SYNC(inode))
++              handle->h_sync = 1;
++
++out_stop:
++      /*
++       * If this was a simple ftruncate(), and the file will remain alive
++       * then we need to clear up the orphan record which we created above.
++       * However, if this was a real unlink then we were called by
++       * ext3_delete_inode(), and we allow that function to clean up the
++       * orphan info for us.
++       */
++      if (inode->i_nlink)
++              ext3_orphan_del(handle, inode);
++
++      up_write(&EXT3_I(inode)->truncate_sem);
++      ext3_journal_stop(handle, inode);
++}
++
++/*
++ * this routine calculate max number of blocks we could modify
++ * in order to allocate new block for an inode
++ */
++int ext3_ext_writepage_trans_blocks(struct inode *inode, int num)
++{
++      struct ext3_extents_tree tree;
++      int needed;
++      
++      ext3_init_tree_desc(&tree, inode);
++      
++      needed = ext3_ext_calc_credits_for_insert(&tree, NULL);
++
++      /* caller want to allocate num blocks */
++      needed *= num;
++      
++#ifdef CONFIG_QUOTA
++      /* 
++       * FIXME: real calculation should be here
++       * it depends on blockmap format of qouta file
++       */
++      needed += 2 * EXT3_SINGLEDATA_TRANS_BLOCKS;
++#endif
++
++      return needed;
++}
++
++void ext3_extents_initialize_blockmap(handle_t *handle, struct inode *inode)
++{
++      struct ext3_extents_tree tree;
++
++      ext3_init_tree_desc(&tree, inode);
++      ext3_extent_tree_init(handle, &tree);
++}
++
++static int
++ext3_ext_store_extent_cb(struct ext3_extents_tree *tree,
++                       struct ext3_ext_path *path,
++                       struct ext3_ext_cache *newex)
++{
++      struct ext3_extent_buf *buf = (struct ext3_extent_buf *) tree->private;
++
++      if (newex->ec_type != EXT3_EXT_CACHE_EXTENT)
++              return EXT_CONTINUE;
++
++      if (buf->err < 0)
++              return EXT_BREAK;
++      if (buf->cur - buf->buffer + sizeof(*newex) > buf->buflen)
++              return EXT_BREAK;
++
++      if (!copy_to_user(buf->cur, newex, sizeof(*newex))) {
++              buf->err++;
++              buf->cur += sizeof(*newex);
++      } else {
++              buf->err = -EFAULT;
++              return EXT_BREAK;
++      }
++      return EXT_CONTINUE;
++}
++
++static int
++ext3_ext_collect_stats_cb(struct ext3_extents_tree *tree,
++                        struct ext3_ext_path *path,
++                        struct ext3_ext_cache *ex)
++{
++      struct ext3_extent_tree_stats *buf =
++              (struct ext3_extent_tree_stats *) tree->private;
++      int depth;
++
++      if (ex->ec_type != EXT3_EXT_CACHE_EXTENT)
++              return EXT_CONTINUE;
++
++      depth = EXT_DEPTH(tree);
++      buf->extents_num++;
++      if (path[depth].p_ext == EXT_FIRST_EXTENT(path[depth].p_hdr))
++              buf->leaf_num++;
++      return EXT_CONTINUE;
++}
++
++int ext3_ext_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
++                 unsigned long arg)
++{
++      int err = 0;
++
++      if (!(EXT3_I(inode)->i_flags & EXT3_EXTENTS_FL))
++              return -EINVAL;
++
++      if (cmd == EXT3_IOC_GET_EXTENTS) {
++              struct ext3_extent_buf buf;
++              struct ext3_extents_tree tree;
++
++              if (copy_from_user(&buf, (void *) arg, sizeof(buf)))
++                      return -EFAULT;
++
++              ext3_init_tree_desc(&tree, inode);
++              buf.cur = buf.buffer;
++              buf.err = 0;
++              tree.private = &buf;
++              down_write(&EXT3_I(inode)->truncate_sem);
++              err = ext3_ext_walk_space(&tree, buf.start, EXT_MAX_BLOCK,
++                                        ext3_ext_store_extent_cb);
++              up_write(&EXT3_I(inode)->truncate_sem);
++              if (err == 0)
++                      err = buf.err;
++      } else if (cmd == EXT3_IOC_GET_TREE_STATS) {
++              struct ext3_extent_tree_stats buf;
++              struct ext3_extents_tree tree;
++
++              ext3_init_tree_desc(&tree, inode);
++              down_write(&EXT3_I(inode)->truncate_sem);
++              buf.depth = EXT_DEPTH(&tree);
++              buf.extents_num = 0;
++              buf.leaf_num = 0;
++              tree.private = &buf;
++              err = ext3_ext_walk_space(&tree, 0, EXT_MAX_BLOCK,
++                                        ext3_ext_collect_stats_cb);
++              up_write(&EXT3_I(inode)->truncate_sem);
++              if (!err)
++                      err = copy_to_user((void *) arg, &buf, sizeof(buf));
++      } else if (cmd == EXT3_IOC_GET_TREE_DEPTH) {
++              struct ext3_extents_tree tree;
++              ext3_init_tree_desc(&tree, inode);
++              down_write(&EXT3_I(inode)->truncate_sem);
++              err = EXT_DEPTH(&tree);
++              up_write(&EXT3_I(inode)->truncate_sem);
++      }
++
++      return err;
++}
++
++EXPORT_SYMBOL(ext3_init_tree_desc);
++EXPORT_SYMBOL(ext3_mark_inode_dirty);
++EXPORT_SYMBOL(ext3_ext_invalidate_cache);
++EXPORT_SYMBOL(ext3_ext_insert_extent);
++EXPORT_SYMBOL(ext3_ext_walk_space);
++EXPORT_SYMBOL(ext3_ext_find_goal);
++EXPORT_SYMBOL(ext3_ext_calc_credits_for_insert);
+Index: linux-2.4.29/fs/ext3/ialloc.c
+===================================================================
+--- linux-2.4.29.orig/fs/ext3/ialloc.c 2005-05-03 16:50:30.216045296 +0300
++++ linux-2.4.29/fs/ext3/ialloc.c      2005-05-03 16:52:08.804057640 +0300
+@@ -553,7 +553,8 @@
+       inode->i_blksize = PAGE_SIZE;
+       inode->i_blocks = 0;
+       inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+-      inode->u.ext3_i.i_flags = dir->u.ext3_i.i_flags & ~EXT3_INDEX_FL;
++      inode->u.ext3_i.i_flags = dir->u.ext3_i.i_flags &
++              ~(EXT3_INDEX_FL | EXT3_EXTENTS_FL);
+       if (S_ISLNK(mode))
+               inode->u.ext3_i.i_flags &= ~(EXT3_IMMUTABLE_FL|EXT3_APPEND_FL);
+ #ifdef EXT3_FRAGMENTS
+@@ -592,6 +593,19 @@
+               iloc.bh = NULL;
+               goto fail;
+       }
++      if (test_opt(sb, EXTENTS) && S_ISREG(inode->i_mode)) {
++              EXT3_I(inode)->i_flags |= EXT3_EXTENTS_FL;
++              memset(&EXT3_I(inode)->i_cached_extent, 0, sizeof(__u32) * 4);
++              ext3_extents_initialize_blockmap(handle, inode);
++              if (!EXT3_HAS_INCOMPAT_FEATURE(sb, EXT3_FEATURE_INCOMPAT_EXTENTS)) {
++                      err = ext3_journal_get_write_access(handle, EXT3_SB(sb)->s_sbh);
++                      if (err) goto fail;
++                      EXT3_SET_INCOMPAT_FEATURE(sb, EXT3_FEATURE_INCOMPAT_EXTENTS);
++                      BUFFER_TRACE(EXT3_SB(sb)->s_sbh, "call ext3_journal_dirty_metadata");
++                      err = ext3_journal_dirty_metadata(handle, EXT3_SB(sb)->s_sbh);
++              }
++      }
++
+       err = ext3_mark_iloc_dirty(handle, inode, &iloc);
+       if (err) goto fail;
+Index: linux-2.4.29/fs/ext3/inode.c
+===================================================================
+--- linux-2.4.29.orig/fs/ext3/inode.c  2005-05-03 16:51:50.331865840 +0300
++++ linux-2.4.29/fs/ext3/inode.c       2005-05-03 16:52:08.808057032 +0300
+@@ -861,6 +861,15 @@
+       goto reread;
+ }
++static inline int
++ext3_get_block_wrap(handle_t *handle, struct inode *inode, long block,
++                  struct buffer_head *bh, int create)
++{
++      if (EXT3_I(inode)->i_flags & EXT3_EXTENTS_FL)
++              return ext3_ext_get_block(handle, inode, block, bh, create);
++      return ext3_get_block_handle(handle, inode, block, bh, create);
++}
++
+ /*
+  * The BKL is not held on entry here.
+  */
+@@ -874,7 +883,7 @@
+               handle = ext3_journal_current_handle();
+               J_ASSERT(handle != 0);
+       }
+-      ret = ext3_get_block_handle(handle, inode, iblock, bh_result, create);
++      ret = ext3_get_block_wrap(handle, inode, iblock, bh_result, create);
+       return ret;
+ }
+@@ -892,7 +901,7 @@
+       dummy.b_state = 0;
+       dummy.b_blocknr = -1000;
+       buffer_trace_init(&dummy.b_history);
+-      *errp = ext3_get_block_handle(handle, inode, block, &dummy, create);
++      *errp = ext3_get_block_wrap(handle, inode, block, &dummy, create);
+       if (!*errp && buffer_mapped(&dummy)) {
+               struct buffer_head *bh;
+               bh = sb_getblk(inode->i_sb, dummy.b_blocknr);
+@@ -1416,7 +1425,7 @@
+  * This required during truncate. We need to physically zero the tail end
+  * of that block so it doesn't yield old data if the file is later grown.
+  */
+-static int ext3_block_truncate_page(handle_t *handle,
++int ext3_block_truncate_page(handle_t *handle,
+               struct address_space *mapping, loff_t from)
+ {
+       unsigned long index = from >> PAGE_CACHE_SHIFT;
+@@ -1904,6 +1913,9 @@
+       ext3_discard_prealloc(inode);
++      if (EXT3_I(inode)->i_flags & EXT3_EXTENTS_FL)
++              return ext3_ext_truncate(inode);
++
+       handle = start_transaction(inode);
+       if (IS_ERR(handle))
+               return;         /* AKPM: return what? */
+@@ -2240,6 +2252,7 @@
+       for (block = 0; block < EXT3_N_BLOCKS; block++)
+               inode->u.ext3_i.i_data[block] = iloc.raw_inode->i_block[block];
+       INIT_LIST_HEAD(&inode->u.ext3_i.i_orphan);
++      memset(&EXT3_I(inode)->i_cached_extent, 0, sizeof(__u32) * 4);
+       if (EXT3_INODE_SIZE(inode->i_sb) > EXT3_GOOD_OLD_INODE_SIZE)
+               inode->u.ext3_i.i_extra_isize =
+@@ -2546,6 +2559,9 @@
+       int indirects = (EXT3_NDIR_BLOCKS % bpp) ? 5 : 3;
+       int ret;
+       
++      if (EXT3_I(inode)->i_flags & EXT3_EXTENTS_FL)
++              return ext3_ext_writepage_trans_blocks(inode, bpp);
++
+       if (ext3_should_journal_data(inode))
+               ret = 3 * (bpp + indirects) + 2;
+       else
+@@ -2982,7 +2998,7 @@
+       /* alloc blocks one by one */
+       for (i = 0; i < nblocks; i++) {
+-              ret = ext3_get_block_handle(handle, inode, blocks[i],
++              ret = ext3_get_block_wrap(handle, inode, blocks[i],
+                                               &bh_tmp, 1);
+               if (ret)
+                       break;
+@@ -3058,7 +3074,7 @@
+                 if (blocks[i] != 0)
+                         continue;
+-                rc = ext3_get_block_handle(handle, inode, iblock, &bh, 1);
++                rc = ext3_get_block_wrap(handle, inode, iblock, &bh, 1);
+                 if (rc) {
+                         printk(KERN_INFO "ext3_map_inode_page: error %d "
+                                "allocating block %ld\n", rc, iblock);
+Index: linux-2.4.29/fs/ext3/Makefile
+===================================================================
+--- linux-2.4.29.orig/fs/ext3/Makefile 2005-05-03 16:51:32.127633304 +0300
++++ linux-2.4.29/fs/ext3/Makefile      2005-05-03 16:53:38.634401352 +0300
+@@ -13,7 +13,9 @@
+ obj-y    := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o iopen.o \
+               ioctl.o namei.o super.o symlink.o hash.o ext3-exports.o \
+-              xattr_trusted.o
++              xattr_trusted.o extents.o
++export-objs += extents.o
++
+ obj-m    := $(O_TARGET)
+ export-objs += xattr.o
+Index: linux-2.4.29/fs/ext3/super.c
+===================================================================
+--- linux-2.4.29.orig/fs/ext3/super.c  2005-05-03 16:50:14.750396432 +0300
++++ linux-2.4.29/fs/ext3/super.c       2005-05-03 16:52:08.813056272 +0300
+@@ -531,6 +531,7 @@
+ #ifdef EXT3_DELETE_THREAD
+       J_ASSERT(sbi->s_delete_inodes == 0);
+ #endif
++      ext3_ext_release(sb);
+       ext3_xattr_put_super(sb);
+       journal_destroy(sbi->s_journal);
+       if (!(sb->s_flags & MS_RDONLY)) {
+@@ -702,6 +703,10 @@
+                               return 0;
+                       }
+               }
++              else if (!strcmp (this_char, "extents"))
++                      set_opt (*mount_options, EXTENTS);
++              else if (!strcmp (this_char, "extdebug"))
++                      set_opt (*mount_options, EXTDEBUG);
+               else if (!strcmp (this_char, "grpid") ||
+                        !strcmp (this_char, "bsdgroups"))
+                       set_opt (*mount_options, GRPID);
+@@ -1405,6 +1410,8 @@
+               test_opt(sb,DATA_FLAGS) == EXT3_MOUNT_ORDERED_DATA ? "ordered":
+               "writeback");
++      ext3_ext_init(sb);
++
+       return sb;
+ failed_mount3:
+Index: linux-2.4.29/fs/ext3/ioctl.c
+===================================================================
+--- linux-2.4.29.orig/fs/ext3/ioctl.c  2005-05-03 16:49:36.825161944 +0300
++++ linux-2.4.29/fs/ext3/ioctl.c       2005-05-03 16:52:08.814056120 +0300
+@@ -174,6 +174,10 @@
+                       return ret;
+               }
+ #endif
++      case EXT3_IOC_GET_EXTENTS:
++      case EXT3_IOC_GET_TREE_STATS:
++      case EXT3_IOC_GET_TREE_DEPTH:
++              return ext3_ext_ioctl(inode, filp, cmd, arg);
+       default:
+               return -ENOTTY;
+       }
+Index: linux-2.4.29/include/linux/ext3_fs.h
+===================================================================
+--- linux-2.4.29.orig/include/linux/ext3_fs.h  2005-05-03 16:50:30.228043472 +0300
++++ linux-2.4.29/include/linux/ext3_fs.h       2005-05-03 16:52:08.817055664 +0300
+@@ -184,8 +184,9 @@
+ #define EXT3_IMAGIC_FL                        0x00002000 /* AFS directory */
+ #define EXT3_JOURNAL_DATA_FL          0x00004000 /* file data should be journaled */
+ #define EXT3_RESERVED_FL              0x80000000 /* reserved for ext3 lib */
++#define EXT3_EXTENTS_FL                       0x00080000 /* Inode uses extents */
+-#define EXT3_FL_USER_VISIBLE          0x00005FFF /* User visible flags */
++#define EXT3_FL_USER_VISIBLE          0x00085FFF /* User visible flags */
+ #define EXT3_FL_USER_MODIFIABLE               0x000000FF /* User modifiable flags */
+ /*
+@@ -208,6 +209,9 @@
+ #ifdef CONFIG_JBD_DEBUG
+ #define EXT3_IOC_WAIT_FOR_READONLY    _IOR('f', 99, long)
+ #endif
++#define EXT3_IOC_GET_EXTENTS          _IOR('f', 7, long)
++#define EXT3_IOC_GET_TREE_DEPTH               _IOR('f', 8, long)
++#define EXT3_IOC_GET_TREE_STATS               _IOR('f', 9, long)
+ /*
+  * Structure of an inode on the disk
+@@ -327,6 +331,8 @@
+ #define EXT3_MOUNT_ASYNCDEL           0x20000 /* Delayed deletion */
+ #define EXT3_MOUNT_IOPEN              0x40000 /* Allow access via iopen */
+ #define EXT3_MOUNT_IOPEN_NOPRIV               0x80000 /* Make iopen world-readable */
++#define EXT3_MOUNT_EXTENTS            0x100000/* Extents support */
++#define EXT3_MOUNT_EXTDEBUG           0x200000/* Extents debug */
+ /* Compatibility, for having both ext2_fs.h and ext3_fs.h included at once */
+ #ifndef _LINUX_EXT2_FS_H
+@@ -506,11 +512,13 @@
+ #define EXT3_FEATURE_INCOMPAT_RECOVER         0x0004 /* Needs recovery */
+ #define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV     0x0008 /* Journal device */
+ #define EXT3_FEATURE_INCOMPAT_META_BG         0x0010
++#define EXT3_FEATURE_INCOMPAT_EXTENTS         0x0040 /* extents support */
+ #define EXT3_FEATURE_COMPAT_SUPP      EXT2_FEATURE_COMPAT_EXT_ATTR
+ #define EXT3_FEATURE_INCOMPAT_SUPP    (EXT3_FEATURE_INCOMPAT_FILETYPE| \
+                                        EXT3_FEATURE_INCOMPAT_RECOVER| \
+-                                       EXT3_FEATURE_INCOMPAT_META_BG)
++                                       EXT3_FEATURE_INCOMPAT_META_BG| \
++                                       EXT3_FEATURE_INCOMPAT_EXTENTS)
+ #define EXT3_FEATURE_RO_COMPAT_SUPP   (EXT3_FEATURE_RO_COMPAT_SPARSE_SUPER| \
+                                        EXT3_FEATURE_RO_COMPAT_LARGE_FILE| \
+                                        EXT3_FEATURE_RO_COMPAT_BTREE_DIR)
+@@ -702,6 +711,7 @@
+ extern unsigned long ext3_count_free (struct buffer_head *, unsigned);
+ /* inode.c */
++extern int ext3_block_truncate_page(handle_t *, struct address_space *, loff_t);
+ extern int ext3_forget(handle_t *, int, struct inode *, struct buffer_head *, int);
+ extern struct buffer_head * ext3_getblk (handle_t *, struct inode *, long, int, int *);
+ extern struct buffer_head * ext3_bread (handle_t *, struct inode *, int, int, int *);
+@@ -783,6 +793,16 @@
+ extern struct inode_operations ext3_symlink_inode_operations;
+ extern struct inode_operations ext3_fast_symlink_inode_operations;
++/* extents.c */
++extern int ext3_ext_writepage_trans_blocks(struct inode *, int);
++extern int ext3_ext_get_block(handle_t *, struct inode *, long,
++                            struct buffer_head *, int);
++extern void ext3_ext_truncate(struct inode *);
++extern void ext3_ext_init(struct super_block *);
++extern void ext3_ext_release(struct super_block *);
++extern void ext3_extents_initialize_blockmap(handle_t *, struct inode *);
++extern int ext3_ext_ioctl(struct inode *inode, struct file *filp,
++                        unsigned int cmd, unsigned long arg);
+ #endif        /* __KERNEL__ */
+Index: linux-2.4.29/include/linux/ext3_extents.h
+===================================================================
+--- linux-2.4.29.orig/include/linux/ext3_extents.h     2005-05-03 16:52:08.724069800 +0300
++++ linux-2.4.29/include/linux/ext3_extents.h  2005-05-03 16:52:08.819055360 +0300
+@@ -0,0 +1,263 @@
++/*
++ * Copyright (c) 2003, Cluster File Systems, Inc, info@clusterfs.com
++ * Written by Alex Tomas <alex@clusterfs.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public Licens
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-
++ */
++
++#ifndef _LINUX_EXT3_EXTENTS
++#define _LINUX_EXT3_EXTENTS
++
++/*
++ * with AGRESSIVE_TEST defined capacity of index/leaf blocks
++ * become very little, so index split, in-depth growing and
++ * other hard changes happens much more often
++ * this is for debug purposes only
++ */
++#define AGRESSIVE_TEST_
++
++/*
++ * if CHECK_BINSEARCH defined, then results of binary search
++ * will be checked by linear search
++ */
++#define CHECK_BINSEARCH_
++
++/*
++ * if EXT_DEBUG is defined you can use 'extdebug' mount option
++ * to get lots of info what's going on
++ */
++#define EXT_DEBUG_
++#ifdef EXT_DEBUG
++#define ext_debug(tree,fmt,a...)                      \
++do {                                                  \
++      if (test_opt((tree)->inode->i_sb, EXTDEBUG))    \
++              printk(fmt, ##a);                       \
++} while (0);
++#else
++#define ext_debug(tree,fmt,a...)
++#endif
++
++/*
++ * if EXT_STATS is defined then stats numbers are collected
++ * these number will be displayed at umount time
++ */
++#define EXT_STATS_
++
++
++#define EXT3_ALLOC_NEEDED     3       /* block bitmap + group desc. + sb */
++
++/*
++ * ext3_inode has i_block array (total 60 bytes)
++ * first 4 bytes are used to store:
++ *  - tree depth (0 mean there is no tree yet. all extents in the inode)
++ *  - number of alive extents in the inode
++ */
++
++/*
++ * this is extent on-disk structure
++ * it's used at the bottom of the tree
++ */
++struct ext3_extent {
++      __u32   ee_block;       /* first logical block extent covers */
++      __u16   ee_len;         /* number of blocks covered by extent */
++      __u16   ee_start_hi;    /* high 16 bits of physical block */
++      __u32   ee_start;       /* low 32 bigs of physical block */
++};
++
++/*
++ * this is index on-disk structure
++ * it's used at all the levels, but the bottom
++ */
++struct ext3_extent_idx {
++      __u32   ei_block;       /* index covers logical blocks from 'block' */
++      __u32   ei_leaf;        /* pointer to the physical block of the next *
++                               * level. leaf or next index could bet here */
++      __u16   ei_leaf_hi;     /* high 16 bits of physical block */
++      __u16   ei_unused;
++};
++
++/*
++ * each block (leaves and indexes), even inode-stored has header
++ */
++struct ext3_extent_header {   
++      __u16   eh_magic;       /* probably will support different formats */   
++      __u16   eh_entries;     /* number of valid entries */
++      __u16   eh_max;         /* capacity of store in entries */
++      __u16   eh_depth;       /* has tree real underlaying blocks? */
++      __u32   eh_generation;  /* generation of the tree */
++};
++
++#define EXT3_EXT_MAGIC                0xf30a
++
++/*
++ * array of ext3_ext_path contains path to some extent
++ * creation/lookup routines use it for traversal/splitting/etc
++ * truncate uses it to simulate recursive walking
++ */
++struct ext3_ext_path {
++      __u32                           p_block;
++      __u16                           p_depth;
++      struct ext3_extent              *p_ext;
++      struct ext3_extent_idx          *p_idx;
++      struct ext3_extent_header       *p_hdr;
++      struct buffer_head              *p_bh;
++};
++
++/*
++ * structure for external API
++ */
++
++/*
++ * storage for cached extent
++ */
++struct ext3_ext_cache {
++      __u32   ec_start;
++      __u32   ec_block;
++      __u32   ec_len;
++      __u32   ec_type;
++};
++
++#define EXT3_EXT_CACHE_NO     0
++#define EXT3_EXT_CACHE_GAP    1
++#define EXT3_EXT_CACHE_EXTENT 2
++
++/*
++ * ext3_extents_tree is used to pass initial information
++ * to top-level extents API
++ */
++struct ext3_extents_helpers;
++struct ext3_extents_tree {
++      struct inode *inode;    /* inode which tree belongs to */
++      void *root;             /* ptr to data top of tree resides at */
++      void *buffer;           /* will be passed as arg to ^^ routines */
++      int buffer_len;
++      void *private;
++      struct ext3_ext_cache *cex;/* last found extent */
++      struct ext3_extents_helpers *ops;
++};
++
++struct ext3_extents_helpers {
++      int (*get_write_access)(handle_t *h, void *buffer);
++      int (*mark_buffer_dirty)(handle_t *h, void *buffer);
++      int (*mergable)(struct ext3_extent *ex1, struct ext3_extent *ex2);
++      int (*remove_extent_credits)(struct ext3_extents_tree *,
++                                   struct ext3_extent *, unsigned long,
++                                   unsigned long);
++      int (*remove_extent)(struct ext3_extents_tree *,
++                           struct ext3_extent *, unsigned long,
++                           unsigned long);
++      int (*new_block)(handle_t *, struct ext3_extents_tree *,
++                       struct ext3_ext_path *, struct ext3_extent *,
++                       int *);
++};
++
++/*
++ * to be called by ext3_ext_walk_space()
++ * negative retcode - error
++ * positive retcode - signal for ext3_ext_walk_space(), see below
++ * callback must return valid extent (passed or newly created)
++ */
++typedef int (*ext_prepare_callback)(struct ext3_extents_tree *,
++                                  struct ext3_ext_path *,
++                                  struct ext3_ext_cache *);
++
++#define EXT_CONTINUE  0
++#define EXT_BREAK     1
++#define EXT_REPEAT    2
++
++
++#define EXT_MAX_BLOCK 0xffffffff
++
++
++#define EXT_FIRST_EXTENT(__hdr__) \
++      ((struct ext3_extent *) (((char *) (__hdr__)) +         \
++                               sizeof(struct ext3_extent_header)))
++#define EXT_FIRST_INDEX(__hdr__) \
++      ((struct ext3_extent_idx *) (((char *) (__hdr__)) +     \
++                                   sizeof(struct ext3_extent_header)))
++#define EXT_HAS_FREE_INDEX(__path__) \
++      ((__path__)->p_hdr->eh_entries < (__path__)->p_hdr->eh_max)
++#define EXT_LAST_EXTENT(__hdr__) \
++      (EXT_FIRST_EXTENT((__hdr__)) + (__hdr__)->eh_entries - 1)
++#define EXT_LAST_INDEX(__hdr__) \
++      (EXT_FIRST_INDEX((__hdr__)) + (__hdr__)->eh_entries - 1)
++#define EXT_MAX_EXTENT(__hdr__) \
++      (EXT_FIRST_EXTENT((__hdr__)) + (__hdr__)->eh_max - 1)
++#define EXT_MAX_INDEX(__hdr__) \
++      (EXT_FIRST_INDEX((__hdr__)) + (__hdr__)->eh_max - 1)
++
++#define EXT_ROOT_HDR(tree) \
++      ((struct ext3_extent_header *) (tree)->root)
++#define EXT_BLOCK_HDR(bh) \
++      ((struct ext3_extent_header *) (bh)->b_data)
++#define EXT_DEPTH(_t_)        \
++      (((struct ext3_extent_header *)((_t_)->root))->eh_depth)
++#define EXT_GENERATION(_t_)   \
++      (((struct ext3_extent_header *)((_t_)->root))->eh_generation)
++
++
++#define EXT_ASSERT(__x__) if (!(__x__)) BUG();
++
++#define EXT_CHECK_PATH(tree,path)                                     \
++{                                                                     \
++      int depth = EXT_DEPTH(tree);                                    \
++      BUG_ON((unsigned long) (path) < __PAGE_OFFSET);                 \
++      BUG_ON((unsigned long) (path)[depth].p_idx <                    \
++                      __PAGE_OFFSET && (path)[depth].p_idx != NULL);  \
++      BUG_ON((unsigned long) (path)[depth].p_ext <                    \
++                      __PAGE_OFFSET && (path)[depth].p_ext != NULL);  \
++      BUG_ON((unsigned long) (path)[depth].p_hdr < __PAGE_OFFSET);    \
++      BUG_ON((unsigned long) (path)[depth].p_bh < __PAGE_OFFSET       \
++                      && depth != 0);                                 \
++      BUG_ON((path)[0].p_depth != depth);                             \
++}
++
++
++/*
++ * this structure is used to gather extents from the tree via ioctl
++ */
++struct ext3_extent_buf {
++      unsigned long start;
++      int buflen;
++      void *buffer;
++      void *cur;
++      int err;
++};
++
++/*
++ * this structure is used to collect stats info about the tree
++ */
++struct ext3_extent_tree_stats {
++      int depth;
++      int extents_num;
++      int leaf_num;
++};
++
++extern void ext3_init_tree_desc(struct ext3_extents_tree *, struct inode *);
++extern int ext3_extent_tree_init(handle_t *, struct ext3_extents_tree *);
++extern int ext3_ext_calc_credits_for_insert(struct ext3_extents_tree *, struct ext3_ext_path *);
++extern int ext3_ext_insert_extent(handle_t *, struct ext3_extents_tree *, struct ext3_ext_path *, struct ext3_extent *);
++extern int ext3_ext_walk_space(struct ext3_extents_tree *, unsigned long, unsigned long, ext_prepare_callback);
++extern int ext3_ext_remove_space(struct ext3_extents_tree *, unsigned long, unsigned long);
++extern struct ext3_ext_path * ext3_ext_find_extent(struct ext3_extents_tree *, int, struct ext3_ext_path *);
++
++static inline void
++ext3_ext_invalidate_cache(struct ext3_extents_tree *tree)
++{
++      if (tree->cex)
++              tree->cex->ec_type = EXT3_EXT_CACHE_NO;
++}
++
++
++#endif /* _LINUX_EXT3_EXTENTS */
+Index: linux-2.4.29/include/linux/ext3_fs_i.h
+===================================================================
+--- linux-2.4.29.orig/include/linux/ext3_fs_i.h        2005-05-03 16:50:30.229043320 +0300
++++ linux-2.4.29/include/linux/ext3_fs_i.h     2005-05-03 16:52:08.823054752 +0300
+@@ -76,6 +76,8 @@
+        * by other means, so we have truncate_sem.
+        */
+       struct rw_semaphore truncate_sem;
++
++      __u32 i_cached_extent[4];
+ };
+ #endif        /* _LINUX_EXT3_FS_I */
index c8fc99a..3b873c2 100644 (file)
@@ -2556,16 +2556,16 @@ Index: linux-stage/include/linux/ext3_fs.h
 --- linux-stage.orig/include/linux/ext3_fs.h   2005-02-25 14:53:56.424908168 +0200
 +++ linux-stage/include/linux/ext3_fs.h        2005-02-25 15:39:12.841950008 +0200
 @@ -186,8 +186,9 @@
+ #define EXT3_NOTAIL_FL                        0x00008000 /* don't merge file tail */
  #define EXT3_DIRSYNC_FL                       0x00010000 /* dirsync behaviour (directories only) */
  #define EXT3_TOPDIR_FL                        0x00020000 /* Top of directory hierarchies*/
- #define EXT3_RESERVED_FL              0x80000000 /* reserved for ext3 lib */
 +#define EXT3_EXTENTS_FL                       0x00080000 /* Inode uses extents */
+ #define EXT3_RESERVED_FL              0x80000000 /* reserved for ext3 lib */
  
 -#define EXT3_FL_USER_VISIBLE          0x0003DFFF /* User visible flags */
 +#define EXT3_FL_USER_VISIBLE          0x000BDFFF /* User visible flags */
  #define EXT3_FL_USER_MODIFIABLE               0x000380FF /* User modifiable flags */
  
- /*
 @@ -237,6 +238,9 @@
  #endif
  #define EXT3_IOC_GETRSVSZ             _IOR('f', 5, long)
index 5fb5951..c42156b 100644 (file)
@@ -1328,7 +1328,7 @@ Index: linux-2.4.21/fs/ext3/namei.c
                        ret = bh;
                        goto cleanup_and_exit;
                } else {
-@@ -198,6 +863,66 @@
+@@ -198,6 +863,74 @@
        return ret;
  }
  
@@ -1347,17 +1347,25 @@ Index: linux-2.4.21/fs/ext3/namei.c
 +      int namelen = dentry->d_name.len;
 +      const u8 *name = dentry->d_name.name;
 +      struct inode *dir = dentry->d_parent->d_inode;
-+      
++
 +      sb = dir->i_sb;
-+      if (!(frame = dx_probe (dentry, 0, &hinfo, frames, err)))
-+              return NULL;
++      /* NFS may look up ".." - look at dx_root directory block */
++      if (namelen > 2 || name[0] != '.'||(name[1] != '.' && name[1] != '\0')){
++              if (!(frame = dx_probe(dentry, 0, &hinfo, frames, err)))
++                      return NULL;
++      } else {
++              frame = frames;
++              frame->bh = NULL;                       /* for dx_release() */
++              frame->at = (struct dx_entry *)frames;  /* hack for zero entry*/
++              dx_set_block(frame->at, 0);             /* dx_root block is 0 */
++      }
 +      hash = hinfo.hash;
 +      do {
 +              block = dx_get_block(frame->at);
 +              if (!(bh = ext3_bread (NULL,dir, block, 0, err)))
 +                      goto errout;
 +              de = (struct ext3_dir_entry_2 *) bh->b_data;
-+              top = (struct ext3_dir_entry_2 *) ((char *) de + sb->s_blocksize -
++              top = (struct ext3_dir_entry_2 *)((char *)de + sb->s_blocksize -
 +                                     EXT3_DIR_REC_LEN(0));
 +              for (; de < top; de = ext3_next_entry(de))
 +              if (ext3_match (namelen, name, de)) {
@@ -2521,16 +2529,3 @@ Index: linux-2.4.21/include/linux/ext3_jbd.h
  int
  ext3_mark_iloc_dirty(handle_t *handle, 
                     struct inode *inode,
-Index: linux-2.4.21/lib/rbtree.c
-===================================================================
---- linux-2.4.21.orig/lib/rbtree.c     2004-09-11 10:16:18.000000000 -0400
-+++ linux-2.4.21/lib/rbtree.c  2004-09-16 19:40:16.000000000 -0400
-@@ -17,6 +17,8 @@
-   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-   linux/lib/rbtree.c
-+
-+  rb_get_first and rb_get_next written by Theodore Ts'o, 9/8/2002
- */
- #include <linux/rbtree.h>
index ca2cacf..7895513 100644 (file)
                        ret = bh;
                        goto cleanup_and_exit;
                } else {
-@@ -197,6 +862,66 @@ cleanup_and_exit:
+@@ -197,6 +862,74 @@ cleanup_and_exit:
        return ret;
  }
  
 +      int namelen = dentry->d_name.len;
 +      const u8 *name = dentry->d_name.name;
 +      struct inode *dir = dentry->d_parent->d_inode;
-+      
++
 +      sb = dir->i_sb;
-+      if (!(frame = dx_probe (dentry, 0, &hinfo, frames, err)))
-+              return NULL;
++      /* NFS may look up ".." - look at dx_root directory block */
++      if (namelen > 2 || name[0] != '.'||(name[1] != '.' && name[1] != '\0')){
++              if (!(frame = dx_probe(dentry, 0, &hinfo, frames, err)))
++                      return NULL;
++      } else {
++              frame = frames;
++              frame->bh = NULL;                       /* for dx_release() */
++              frame->at = (struct dx_entry *)frames;  /* hack for zero entry*/
++              dx_set_block(frame->at, 0);             /* dx_root block is 0 */
++      }
 +      hash = hinfo.hash;
 +      do {
 +              block = dx_get_block(frame->at);
 +              if (!(bh = ext3_bread (NULL,dir, block, 0, err)))
 +                      goto errout;
 +              de = (struct ext3_dir_entry_2 *) bh->b_data;
-+              top = (struct ext3_dir_entry_2 *) ((char *) de + sb->s_blocksize -
++              top = (struct ext3_dir_entry_2 *)((char *)de + sb->s_blocksize -
 +                                     EXT3_DIR_REC_LEN(0));
 +              for (; de < top; de = ext3_next_entry(de))
 +              if (ext3_match (namelen, name, de)) {
diff --git a/lustre/kernel_patches/patches/ext3-htree-2.4.29.patch b/lustre/kernel_patches/patches/ext3-htree-2.4.29.patch
new file mode 100644 (file)
index 0000000..259c7b7
--- /dev/null
@@ -0,0 +1,2496 @@
+Index: linux-2.4.29/fs/ext3/dir.c
+===================================================================
+--- linux-2.4.29.orig/fs/ext3/dir.c    2005-04-07 18:53:53.000000000 +0300
++++ linux-2.4.29/fs/ext3/dir.c 2005-05-03 16:34:05.481747664 +0300
+@@ -21,12 +21,16 @@
+ #include <linux/fs.h>
+ #include <linux/jbd.h>
+ #include <linux/ext3_fs.h>
++#include <linux/slab.h>
++#include <linux/rbtree.h>
+ static unsigned char ext3_filetype_table[] = {
+       DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR, DT_BLK, DT_FIFO, DT_SOCK, DT_LNK
+ };
+ static int ext3_readdir(struct file *, void *, filldir_t);
++static int ext3_dx_readdir(struct file * filp,
++                         void * dirent, filldir_t filldir);
+ struct file_operations ext3_dir_operations = {
+       read:           generic_read_dir,
+@@ -35,6 +39,17 @@
+       fsync:          ext3_sync_file,         /* BKL held */
+ };
++
++static unsigned char get_dtype(struct super_block *sb, int filetype)
++{
++      if (!EXT3_HAS_INCOMPAT_FEATURE(sb, EXT3_FEATURE_INCOMPAT_FILETYPE) ||
++          (filetype >= EXT3_FT_MAX))
++              return DT_UNKNOWN;
++
++      return (ext3_filetype_table[filetype]);
++}
++                             
++
+ int ext3_check_dir_entry (const char * function, struct inode * dir,
+                         struct ext3_dir_entry_2 * de,
+                         struct buffer_head * bh,
+@@ -79,6 +94,16 @@
+       sb = inode->i_sb;
++      if (is_dx(inode)) {
++              err = ext3_dx_readdir(filp, dirent, filldir);
++              if (err != ERR_BAD_DX_DIR)
++                      return err;
++              /*
++               * We don't set the inode dirty flag since it's not
++               * critical that it get flushed back to the disk.
++               */
++              EXT3_I(filp->f_dentry->d_inode)->i_flags &= ~EXT3_INDEX_FL;
++      }
+       stored = 0;
+       bh = NULL;
+       offset = filp->f_pos & (sb->s_blocksize - 1);
+@@ -162,18 +187,12 @@
+                                * during the copy operation.
+                                */
+                               unsigned long version = filp->f_version;
+-                              unsigned char d_type = DT_UNKNOWN;
+-                              if (EXT3_HAS_INCOMPAT_FEATURE(sb,
+-                                              EXT3_FEATURE_INCOMPAT_FILETYPE)
+-                                              && de->file_type < EXT3_FT_MAX)
+-                                      d_type =
+-                                        ext3_filetype_table[de->file_type];
+                               error = filldir(dirent, de->name,
+                                               de->name_len,
+                                               filp->f_pos,
+                                               le32_to_cpu(de->inode),
+-                                              d_type);
++                                              get_dtype(sb, de->file_type));
+                               if (error)
+                                       break;
+                               if (version != filp->f_version)
+@@ -188,3 +207,272 @@
+       UPDATE_ATIME(inode);
+       return 0;
+ }
++
++#ifdef CONFIG_EXT3_INDEX
++/*
++ * These functions convert from the major/minor hash to an f_pos
++ * value.
++ * 
++ * Currently we only use major hash numer.  This is unfortunate, but
++ * on 32-bit machines, the same VFS interface is used for lseek and
++ * llseek, so if we use the 64 bit offset, then the 32-bit versions of
++ * lseek/telldir/seekdir will blow out spectacularly, and from within
++ * the ext2 low-level routine, we don't know if we're being called by
++ * a 64-bit version of the system call or the 32-bit version of the
++ * system call.  Worse yet, NFSv2 only allows for a 32-bit readdir
++ * cookie.  Sigh.
++ */
++#define hash2pos(major, minor)        (major >> 1)
++#define pos2maj_hash(pos)     ((pos << 1) & 0xffffffff)
++#define pos2min_hash(pos)     (0)
++
++/*
++ * This structure holds the nodes of the red-black tree used to store
++ * the directory entry in hash order.
++ */
++struct fname {
++      __u32           hash;
++      __u32           minor_hash;
++      rb_node_t       rb_hash; 
++      struct fname    *next;
++      __u32           inode;
++      __u8            name_len;
++      __u8            file_type;
++      char            name[0];
++};
++
++/*
++ * This functoin implements a non-recursive way of freeing all of the
++ * nodes in the red-black tree.
++ */
++static void free_rb_tree_fname(rb_root_t *root)
++{
++      rb_node_t       *n = root->rb_node;
++      rb_node_t       *parent;
++      struct fname    *fname;
++
++      while (n) {
++              /* Do the node's children first */
++              if ((n)->rb_left) {
++                      n = n->rb_left;
++                      continue;
++              }
++              if (n->rb_right) {
++                      n = n->rb_right;
++                      continue;
++              }
++              /*
++               * The node has no children; free it, and then zero
++               * out parent's link to it.  Finally go to the
++               * beginning of the loop and try to free the parent
++               * node.
++               */
++              parent = n->rb_parent;
++              fname = rb_entry(n, struct fname, rb_hash);
++              kfree(fname);
++              if (!parent)
++                      root->rb_node = 0;
++              else if (parent->rb_left == n)
++                      parent->rb_left = 0;
++              else if (parent->rb_right == n)
++                      parent->rb_right = 0;
++              n = parent;
++      }
++      root->rb_node = 0;
++}
++
++
++struct dir_private_info *create_dir_info(loff_t pos)
++{
++      struct dir_private_info *p;
++
++      p = kmalloc(sizeof(struct dir_private_info), GFP_KERNEL);
++      if (!p)
++              return NULL;
++      p->root.rb_node = 0;
++      p->curr_node = 0;
++      p->extra_fname = 0;
++      p->last_pos = 0;
++      p->curr_hash = pos2maj_hash(pos);
++      p->curr_minor_hash = pos2min_hash(pos);
++      p->next_hash = 0;
++      return p;
++}
++
++void ext3_htree_free_dir_info(struct dir_private_info *p)
++{
++      free_rb_tree_fname(&p->root);
++      kfree(p);
++}
++              
++/*
++ * Given a directory entry, enter it into the fname rb tree.
++ */
++int ext3_htree_store_dirent(struct file *dir_file, __u32 hash,
++                           __u32 minor_hash,
++                           struct ext3_dir_entry_2 *dirent)
++{
++      rb_node_t **p, *parent = NULL;
++      struct fname * fname, *new_fn;
++      struct dir_private_info *info;
++      int len;
++
++      info = (struct dir_private_info *) dir_file->private_data;
++      p = &info->root.rb_node;
++
++      /* Create and allocate the fname structure */
++      len = sizeof(struct fname) + dirent->name_len + 1;
++      new_fn = kmalloc(len, GFP_KERNEL);
++      if (!new_fn)
++              return -ENOMEM;
++      memset(new_fn, 0, len);
++      new_fn->hash = hash;
++      new_fn->minor_hash = minor_hash;
++      new_fn->inode = le32_to_cpu(dirent->inode);
++      new_fn->name_len = dirent->name_len;
++      new_fn->file_type = dirent->file_type;
++      memcpy(new_fn->name, dirent->name, dirent->name_len);
++      new_fn->name[dirent->name_len] = 0;
++      
++      while (*p) {
++              parent = *p;
++              fname = rb_entry(parent, struct fname, rb_hash);
++
++              /*
++               * If the hash and minor hash match up, then we put
++               * them on a linked list.  This rarely happens...
++               */
++              if ((new_fn->hash == fname->hash) &&
++                  (new_fn->minor_hash == fname->minor_hash)) {
++                      new_fn->next = fname->next;
++                      fname->next = new_fn;
++                      return 0;
++              }
++                      
++              if (new_fn->hash < fname->hash)
++                      p = &(*p)->rb_left;
++              else if (new_fn->hash > fname->hash)
++                      p = &(*p)->rb_right;
++              else if (new_fn->minor_hash < fname->minor_hash)
++                      p = &(*p)->rb_left;
++              else /* if (new_fn->minor_hash > fname->minor_hash) */
++                      p = &(*p)->rb_right;
++      }
++
++      rb_link_node(&new_fn->rb_hash, parent, p);
++      rb_insert_color(&new_fn->rb_hash, &info->root);
++      return 0;
++}
++
++
++
++/*
++ * This is a helper function for ext3_dx_readdir.  It calls filldir
++ * for all entres on the fname linked list.  (Normally there is only
++ * one entry on the linked list, unless there are 62 bit hash collisions.)
++ */
++static int call_filldir(struct file * filp, void * dirent,
++                      filldir_t filldir, struct fname *fname)
++{
++      struct dir_private_info *info = filp->private_data;
++      loff_t  curr_pos;
++      struct inode *inode = filp->f_dentry->d_inode;
++      struct super_block * sb;
++      int error;
++
++      sb = inode->i_sb;
++      
++      if (!fname) {
++              printk("call_filldir: called with null fname?!?\n");
++              return 0;
++      }
++      curr_pos = hash2pos(fname->hash, fname->minor_hash);
++      while (fname) {
++              error = filldir(dirent, fname->name,
++                              fname->name_len, curr_pos, 
++                              fname->inode,
++                              get_dtype(sb, fname->file_type));
++              if (error) {
++                      filp->f_pos = curr_pos;
++                      info->extra_fname = fname->next;
++                      return error;
++              }
++              fname = fname->next;
++      }
++      return 0;
++}
++
++static int ext3_dx_readdir(struct file * filp,
++                       void * dirent, filldir_t filldir)
++{
++      struct dir_private_info *info = filp->private_data;
++      struct inode *inode = filp->f_dentry->d_inode;
++      struct fname *fname;
++      int     ret;
++
++      if (!info) {
++              info = create_dir_info(filp->f_pos);
++              if (!info)
++                      return -ENOMEM;
++              filp->private_data = info;
++      }
++
++      /* Some one has messed with f_pos; reset the world */
++      if (info->last_pos != filp->f_pos) {
++              free_rb_tree_fname(&info->root);
++              info->curr_node = 0;
++              info->extra_fname = 0;
++              info->curr_hash = pos2maj_hash(filp->f_pos);
++              info->curr_minor_hash = pos2min_hash(filp->f_pos);
++      }
++
++      /*
++       * If there are any leftover names on the hash collision
++       * chain, return them first.
++       */
++      if (info->extra_fname &&
++          call_filldir(filp, dirent, filldir, info->extra_fname))
++              goto finished;
++
++      if (!info->curr_node)
++              info->curr_node = rb_first(&info->root);
++
++      while (1) {
++              /*
++               * Fill the rbtree if we have no more entries,
++               * or the inode has changed since we last read in the
++               * cached entries. 
++               */
++              if ((!info->curr_node) ||
++                  (filp->f_version != inode->i_version)) {
++                      info->curr_node = 0;
++                      free_rb_tree_fname(&info->root);
++                      filp->f_version = inode->i_version;
++                      ret = ext3_htree_fill_tree(filp, info->curr_hash,
++                                                 info->curr_minor_hash,
++                                                 &info->next_hash);
++                      if (ret < 0)
++                              return ret;
++                      if (ret == 0)
++                              break;
++                      info->curr_node = rb_first(&info->root);
++              }
++
++              fname = rb_entry(info->curr_node, struct fname, rb_hash);
++              info->curr_hash = fname->hash;
++              info->curr_minor_hash = fname->minor_hash;
++              if (call_filldir(filp, dirent, filldir, fname))
++                      break;
++
++              info->curr_node = rb_next(info->curr_node);
++              if (!info->curr_node) {
++                      info->curr_hash = info->next_hash;
++                      info->curr_minor_hash = 0;
++              }
++      }
++finished:
++      info->last_pos = filp->f_pos;
++      UPDATE_ATIME(inode);
++      return 0;
++}
++#endif
+Index: linux-2.4.29/fs/ext3/file.c
+===================================================================
+--- linux-2.4.29.orig/fs/ext3/file.c   2005-04-07 18:55:11.000000000 +0300
++++ linux-2.4.29/fs/ext3/file.c        2005-05-03 16:29:50.563501128 +0300
+@@ -35,6 +35,9 @@
+ {
+       if (filp->f_mode & FMODE_WRITE)
+               ext3_discard_prealloc (inode);
++      if (is_dx(inode) && filp->private_data)
++              ext3_htree_free_dir_info(filp->private_data);
++
+       return 0;
+ }
+Index: linux-2.4.29/fs/ext3/hash.c
+===================================================================
+--- linux-2.4.29.orig/fs/ext3/hash.c   2005-05-03 16:29:50.539504776 +0300
++++ linux-2.4.29/fs/ext3/hash.c        2005-05-03 16:29:50.565500824 +0300
+@@ -0,0 +1,215 @@
++/*
++ *  linux/fs/ext3/hash.c
++ *
++ * Copyright (C) 2002 by Theodore Ts'o
++ *
++ * This file is released under the GPL v2.
++ * 
++ * This file may be redistributed under the terms of the GNU Public
++ * License.
++ */
++
++#include <linux/fs.h>
++#include <linux/jbd.h>
++#include <linux/sched.h>
++#include <linux/ext3_fs.h>
++
++#define DELTA 0x9E3779B9
++
++static void TEA_transform(__u32 buf[4], __u32 const in[])
++{
++      __u32   sum = 0;
++      __u32   b0 = buf[0], b1 = buf[1];
++      __u32   a = in[0], b = in[1], c = in[2], d = in[3];
++      int     n = 16;
++
++      do {                                                    
++              sum += DELTA;                                   
++              b0 += ((b1 << 4)+a) ^ (b1+sum) ^ ((b1 >> 5)+b); 
++              b1 += ((b0 << 4)+c) ^ (b0+sum) ^ ((b0 >> 5)+d); 
++      } while(--n);
++
++      buf[0] += b0;
++      buf[1] += b1;
++}
++
++/* F, G and H are basic MD4 functions: selection, majority, parity */
++#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
++#define G(x, y, z) (((x) & (y)) + (((x) ^ (y)) & (z)))
++#define H(x, y, z) ((x) ^ (y) ^ (z))
++
++/*
++ * The generic round function.  The application is so specific that
++ * we don't bother protecting all the arguments with parens, as is generally
++ * good macro practice, in favor of extra legibility.
++ * Rotation is separate from addition to prevent recomputation
++ */
++#define ROUND(f, a, b, c, d, x, s)    \
++      (a += f(b, c, d) + x, a = (a << s) | (a >> (32-s)))
++#define K1 0
++#define K2 013240474631UL
++#define K3 015666365641UL
++
++/*
++ * Basic cut-down MD4 transform.  Returns only 32 bits of result.
++ */
++static void halfMD4Transform (__u32 buf[4], __u32 const in[])
++{
++      __u32   a = buf[0], b = buf[1], c = buf[2], d = buf[3];
++
++      /* Round 1 */
++      ROUND(F, a, b, c, d, in[0] + K1,  3);
++      ROUND(F, d, a, b, c, in[1] + K1,  7);
++      ROUND(F, c, d, a, b, in[2] + K1, 11);
++      ROUND(F, b, c, d, a, in[3] + K1, 19);
++      ROUND(F, a, b, c, d, in[4] + K1,  3);
++      ROUND(F, d, a, b, c, in[5] + K1,  7);
++      ROUND(F, c, d, a, b, in[6] + K1, 11);
++      ROUND(F, b, c, d, a, in[7] + K1, 19);
++
++      /* Round 2 */
++      ROUND(G, a, b, c, d, in[1] + K2,  3);
++      ROUND(G, d, a, b, c, in[3] + K2,  5);
++      ROUND(G, c, d, a, b, in[5] + K2,  9);
++      ROUND(G, b, c, d, a, in[7] + K2, 13);
++      ROUND(G, a, b, c, d, in[0] + K2,  3);
++      ROUND(G, d, a, b, c, in[2] + K2,  5);
++      ROUND(G, c, d, a, b, in[4] + K2,  9);
++      ROUND(G, b, c, d, a, in[6] + K2, 13);
++
++      /* Round 3 */
++      ROUND(H, a, b, c, d, in[3] + K3,  3);
++      ROUND(H, d, a, b, c, in[7] + K3,  9);
++      ROUND(H, c, d, a, b, in[2] + K3, 11);
++      ROUND(H, b, c, d, a, in[6] + K3, 15);
++      ROUND(H, a, b, c, d, in[1] + K3,  3);
++      ROUND(H, d, a, b, c, in[5] + K3,  9);
++      ROUND(H, c, d, a, b, in[0] + K3, 11);
++      ROUND(H, b, c, d, a, in[4] + K3, 15);
++
++      buf[0] += a;
++      buf[1] += b;
++      buf[2] += c;
++      buf[3] += d;
++}
++
++#undef ROUND
++#undef F
++#undef G
++#undef H
++#undef K1
++#undef K2
++#undef K3
++
++/* The old legacy hash */
++static __u32 dx_hack_hash (const char *name, int len)
++{
++      __u32 hash0 = 0x12a3fe2d, hash1 = 0x37abe8f9;
++      while (len--) {
++              __u32 hash = hash1 + (hash0 ^ (*name++ * 7152373));
++              
++              if (hash & 0x80000000) hash -= 0x7fffffff;
++              hash1 = hash0;
++              hash0 = hash;
++      }
++      return (hash0 << 1);
++}
++
++static void str2hashbuf(const char *msg, int len, __u32 *buf, int num)
++{
++      __u32   pad, val;
++      int     i;
++
++      pad = (__u32)len | ((__u32)len << 8);
++      pad |= pad << 16;
++
++      val = pad;
++      if (len > num*4)
++              len = num * 4;
++      for (i=0; i < len; i++) {
++              if ((i % 4) == 0)
++                      val = pad;
++              val = msg[i] + (val << 8);
++              if ((i % 4) == 3) {
++                      *buf++ = val;
++                      val = pad;
++                      num--;
++              }
++      }
++      if (--num >= 0)
++              *buf++ = val;
++      while (--num >= 0)
++              *buf++ = pad;
++}
++
++/*
++ * Returns the hash of a filename.  If len is 0 and name is NULL, then
++ * this function can be used to test whether or not a hash version is
++ * supported.
++ * 
++ * The seed is an 4 longword (32 bits) "secret" which can be used to
++ * uniquify a hash.  If the seed is all zero's, then some default seed
++ * may be used.
++ * 
++ * A particular hash version specifies whether or not the seed is
++ * represented, and whether or not the returned hash is 32 bits or 64
++ * bits.  32 bit hashes will return 0 for the minor hash.
++ */
++int ext3fs_dirhash(const char *name, int len, struct dx_hash_info *hinfo)
++{
++      __u32   hash;
++      __u32   minor_hash = 0;
++      const char      *p;
++      int             i;
++      __u32           in[8], buf[4];
++
++      /* Initialize the default seed for the hash checksum functions */
++      buf[0] = 0x67452301;
++      buf[1] = 0xefcdab89;
++      buf[2] = 0x98badcfe;
++      buf[3] = 0x10325476;
++
++      /* Check to see if the seed is all zero's */
++      if (hinfo->seed) {
++              for (i=0; i < 4; i++) {
++                      if (hinfo->seed[i])
++                              break;
++              }
++              if (i < 4)
++                      memcpy(buf, hinfo->seed, sizeof(buf));
++      }
++              
++      switch (hinfo->hash_version) {
++      case DX_HASH_LEGACY:
++              hash = dx_hack_hash(name, len);
++              break;
++      case DX_HASH_HALF_MD4:
++              p = name;
++              while (len > 0) {
++                      str2hashbuf(p, len, in, 8);
++                      halfMD4Transform(buf, in);
++                      len -= 32;
++                      p += 32;
++              }
++              minor_hash = buf[2];
++              hash = buf[1];
++              break;
++      case DX_HASH_TEA:
++              p = name;
++              while (len > 0) {
++                      str2hashbuf(p, len, in, 4);
++                      TEA_transform(buf, in);
++                      len -= 16;
++                      p += 16;
++              }
++              hash = buf[0];
++              minor_hash = buf[1];
++              break;
++      default:
++              hinfo->hash = 0;
++              return -1;
++      }
++      hinfo->hash = hash & ~1;
++      hinfo->minor_hash = minor_hash;
++      return 0;
++}
+Index: linux-2.4.29/fs/ext3/Makefile
+===================================================================
+--- linux-2.4.29.orig/fs/ext3/Makefile 2005-04-07 18:59:19.000000000 +0300
++++ linux-2.4.29/fs/ext3/Makefile      2005-05-03 16:29:50.565500824 +0300
+@@ -12,7 +12,7 @@
+ export-objs :=        super.o inode.o
+ obj-y    := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o \
+-              ioctl.o namei.o super.o symlink.o
++              ioctl.o namei.o super.o symlink.o hash.o
+ obj-m    := $(O_TARGET)
+ include $(TOPDIR)/Rules.make
+Index: linux-2.4.29/fs/ext3/namei.c
+===================================================================
+--- linux-2.4.29.orig/fs/ext3/namei.c  2005-04-07 18:53:59.000000000 +0300
++++ linux-2.4.29/fs/ext3/namei.c       2005-05-03 16:29:50.576499152 +0300
+@@ -16,6 +16,12 @@
+  *        David S. Miller (davem@caip.rutgers.edu), 1995
+  *  Directory entry file type support and forward compatibility hooks
+  *    for B-tree directories by Theodore Ts'o (tytso@mit.edu), 1998
++ *  Hash Tree Directory indexing (c)
++ *    Daniel Phillips, 2001
++ *  Hash Tree Directory indexing porting
++ *    Christopher Li, 2002
++ *  Hash Tree Directory indexing cleanup
++ *    Theodore Ts'o, 2002
+  */
+ #include <linux/fs.h>
+@@ -38,6 +44,642 @@
+ #define NAMEI_RA_SIZE        (NAMEI_RA_CHUNKS * NAMEI_RA_BLOCKS)
+ #define NAMEI_RA_INDEX(c,b)  (((c) * NAMEI_RA_BLOCKS) + (b))
++static struct buffer_head *ext3_append(handle_t *handle,
++                                      struct inode *inode,
++                                      u32 *block, int *err)
++{
++      struct buffer_head *bh;
++
++      *block = inode->i_size >> inode->i_sb->s_blocksize_bits;
++
++      if ((bh = ext3_bread(handle, inode, *block, 1, err))) {
++              inode->i_size += inode->i_sb->s_blocksize;
++              EXT3_I(inode)->i_disksize = inode->i_size;
++              ext3_journal_get_write_access(handle,bh);
++      }
++      return bh;
++}
++
++#ifndef assert
++#define assert(test) J_ASSERT(test)
++#endif
++
++#ifndef swap
++#define swap(x, y) do { typeof(x) z = x; x = y; y = z; } while (0)
++#endif
++
++typedef struct { u32 v; } le_u32;
++typedef struct { u16 v; } le_u16;
++
++#ifdef DX_DEBUG
++#define dxtrace(command) command
++#else
++#define dxtrace(command) 
++#endif
++
++struct fake_dirent
++{
++      /*le*/u32 inode;
++      /*le*/u16 rec_len;
++      u8 name_len;
++      u8 file_type;
++};
++
++struct dx_countlimit
++{
++      le_u16 limit;
++      le_u16 count;
++};
++
++struct dx_entry
++{
++      le_u32 hash;
++      le_u32 block;
++};
++
++/*
++ * dx_root_info is laid out so that if it should somehow get overlaid by a
++ * dirent the two low bits of the hash version will be zero.  Therefore, the
++ * hash version mod 4 should never be 0.  Sincerely, the paranoia department.
++ */
++
++struct dx_root
++{
++      struct fake_dirent dot;
++      char dot_name[4];
++      struct fake_dirent dotdot;
++      char dotdot_name[4];
++      struct dx_root_info
++      {
++              le_u32 reserved_zero;
++              u8 hash_version;
++              u8 info_length; /* 8 */
++              u8 indirect_levels;
++              u8 unused_flags;
++      }
++      info;
++      struct dx_entry entries[0];
++};
++
++struct dx_node
++{
++      struct fake_dirent fake;
++      struct dx_entry entries[0];
++};
++
++
++struct dx_frame
++{
++      struct buffer_head *bh;
++      struct dx_entry *entries;
++      struct dx_entry *at;
++};
++
++struct dx_map_entry
++{
++      u32 hash;
++      u32 offs;
++};
++
++#ifdef CONFIG_EXT3_INDEX
++static inline unsigned dx_get_block (struct dx_entry *entry);
++static void dx_set_block (struct dx_entry *entry, unsigned value);
++static inline unsigned dx_get_hash (struct dx_entry *entry);
++static void dx_set_hash (struct dx_entry *entry, unsigned value);
++static unsigned dx_get_count (struct dx_entry *entries);
++static unsigned dx_get_limit (struct dx_entry *entries);
++static void dx_set_count (struct dx_entry *entries, unsigned value);
++static void dx_set_limit (struct dx_entry *entries, unsigned value);
++static unsigned dx_root_limit (struct inode *dir, unsigned infosize);
++static unsigned dx_node_limit (struct inode *dir);
++static struct dx_frame *dx_probe(struct dentry *dentry,
++                               struct inode *dir,
++                               struct dx_hash_info *hinfo,
++                               struct dx_frame *frame,
++                               int *err);
++static void dx_release (struct dx_frame *frames);
++static int dx_make_map (struct ext3_dir_entry_2 *de, int size,
++                      struct dx_hash_info *hinfo, struct dx_map_entry map[]);
++static void dx_sort_map(struct dx_map_entry *map, unsigned count);
++static struct ext3_dir_entry_2 *dx_move_dirents (char *from, char *to,
++              struct dx_map_entry *offsets, int count);
++static struct ext3_dir_entry_2* dx_pack_dirents (char *base, int size);
++static void dx_insert_block (struct dx_frame *frame, u32 hash, u32 block);
++static int ext3_htree_next_block(struct inode *dir, __u32 hash,
++                               struct dx_frame *frame,
++                               struct dx_frame *frames, int *err,
++                               __u32 *start_hash);
++static struct buffer_head * ext3_dx_find_entry(struct dentry *dentry,
++                     struct ext3_dir_entry_2 **res_dir, int *err);
++static int ext3_dx_add_entry(handle_t *handle, struct dentry *dentry,
++                           struct inode *inode);
++
++/*
++ * Future: use high four bits of block for coalesce-on-delete flags
++ * Mask them off for now.
++ */
++
++static inline unsigned dx_get_block (struct dx_entry *entry)
++{
++      return le32_to_cpu(entry->block.v) & 0x00ffffff;
++}
++
++static inline void dx_set_block (struct dx_entry *entry, unsigned value)
++{
++      entry->block.v = cpu_to_le32(value);
++}
++
++static inline unsigned dx_get_hash (struct dx_entry *entry)
++{
++      return le32_to_cpu(entry->hash.v);
++}
++
++static inline void dx_set_hash (struct dx_entry *entry, unsigned value)
++{
++      entry->hash.v = cpu_to_le32(value);
++}
++
++static inline unsigned dx_get_count (struct dx_entry *entries)
++{
++      return le16_to_cpu(((struct dx_countlimit *) entries)->count.v);
++}
++
++static inline unsigned dx_get_limit (struct dx_entry *entries)
++{
++      return le16_to_cpu(((struct dx_countlimit *) entries)->limit.v);
++}
++
++static inline void dx_set_count (struct dx_entry *entries, unsigned value)
++{
++      ((struct dx_countlimit *) entries)->count.v = cpu_to_le16(value);
++}
++
++static inline void dx_set_limit (struct dx_entry *entries, unsigned value)
++{
++      ((struct dx_countlimit *) entries)->limit.v = cpu_to_le16(value);
++}
++
++static inline unsigned dx_root_limit (struct inode *dir, unsigned infosize)
++{
++      unsigned entry_space = dir->i_sb->s_blocksize - EXT3_DIR_REC_LEN(1) -
++              EXT3_DIR_REC_LEN(2) - infosize;
++      return 0? 20: entry_space / sizeof(struct dx_entry);
++}
++
++static inline unsigned dx_node_limit (struct inode *dir)
++{
++      unsigned entry_space = dir->i_sb->s_blocksize - EXT3_DIR_REC_LEN(0);
++      return 0? 22: entry_space / sizeof(struct dx_entry);
++}
++
++/*
++ * Debug
++ */
++#ifdef DX_DEBUG
++struct stats
++{ 
++      unsigned names;
++      unsigned space;
++      unsigned bcount;
++};
++
++static struct stats dx_show_leaf(struct dx_hash_info *hinfo, struct ext3_dir_entry_2 *de,
++                               int size, int show_names)
++{
++      unsigned names = 0, space = 0;
++      char *base = (char *) de;
++      struct dx_hash_info h = *hinfo;
++      
++      printk("names: ");
++      while ((char *) de < base + size)
++      {
++              if (de->inode)
++              {
++                      if (show_names)
++                      {
++                              int len = de->name_len;
++                              char *name = de->name;
++                              while (len--) printk("%c", *name++);
++                              ext3fs_dirhash(de->name, de->name_len, &h);
++                              printk(":%x.%u ", h.hash,
++                                     ((char *) de - base));
++                      }
++                      space += EXT3_DIR_REC_LEN(de->name_len);
++                      names++;
++              }
++              de = (struct ext3_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len));
++      }
++      printk("(%i)\n", names);
++      return (struct stats) { names, space, 1 };
++}
++
++struct stats dx_show_entries(struct dx_hash_info *hinfo, struct inode *dir,
++                           struct dx_entry *entries, int levels)
++{
++      unsigned blocksize = dir->i_sb->s_blocksize;
++      unsigned count = dx_get_count (entries), names = 0, space = 0, i;
++      unsigned bcount = 0;
++      struct buffer_head *bh;
++      int err;
++      printk("%i indexed blocks...\n", count);
++      for (i = 0; i < count; i++, entries++)
++      {
++              u32 block = dx_get_block(entries), hash = i? dx_get_hash(entries): 0;
++              u32 range = i < count - 1? (dx_get_hash(entries + 1) - hash): ~hash;
++              struct stats stats;
++              printk("%s%3u:%03u hash %8x/%8x ",levels?"":"   ", i, block, hash, range);
++              if (!(bh = ext3_bread (NULL,dir, block, 0,&err))) continue;
++              stats = levels?
++                 dx_show_entries(hinfo, dir, ((struct dx_node *) bh->b_data)->entries, levels - 1):
++                 dx_show_leaf(hinfo, (struct ext3_dir_entry_2 *) bh->b_data, blocksize, 0);
++              names += stats.names;
++              space += stats.space;
++              bcount += stats.bcount;
++              brelse (bh);
++      }
++      if (bcount)
++              printk("%snames %u, fullness %u (%u%%)\n", levels?"":"   ",
++                      names, space/bcount,(space/bcount)*100/blocksize);
++      return (struct stats) { names, space, bcount};
++}
++#endif /* DX_DEBUG */
++
++/*
++ * Probe for a directory leaf block to search.
++ *
++ * dx_probe can return ERR_BAD_DX_DIR, which means there was a format
++ * error in the directory index, and the caller should fall back to
++ * searching the directory normally.  The callers of dx_probe **MUST**
++ * check for this error code, and make sure it never gets reflected
++ * back to userspace.
++ */
++static struct dx_frame *
++dx_probe(struct dentry *dentry, struct inode *dir,
++       struct dx_hash_info *hinfo, struct dx_frame *frame_in, int *err)
++{
++      unsigned count, indirect;
++      struct dx_entry *at, *entries, *p, *q, *m;
++      struct dx_root *root;
++      struct buffer_head *bh;
++      struct dx_frame *frame = frame_in;
++      u32 hash;
++
++      frame->bh = NULL;
++      if (dentry)
++              dir = dentry->d_parent->d_inode;
++      if (!(bh = ext3_bread (NULL,dir, 0, 0, err)))
++              goto fail;
++      root = (struct dx_root *) bh->b_data;
++      if (root->info.hash_version != DX_HASH_TEA &&
++          root->info.hash_version != DX_HASH_HALF_MD4 &&
++          root->info.hash_version != DX_HASH_LEGACY) {
++              ext3_warning(dir->i_sb, __FUNCTION__,
++                           "Unrecognised inode hash code %d",
++                           root->info.hash_version);
++              brelse(bh);
++              *err = ERR_BAD_DX_DIR;
++              goto fail;
++      }
++      hinfo->hash_version = root->info.hash_version;
++      hinfo->seed = dir->i_sb->u.ext3_sb.s_hash_seed;
++      if (dentry)
++              ext3fs_dirhash(dentry->d_name.name, dentry->d_name.len, hinfo);
++      hash = hinfo->hash;
++
++      if (root->info.unused_flags & 1) {
++              ext3_warning(dir->i_sb, __FUNCTION__,
++                           "Unimplemented inode hash flags: %#06x",
++                           root->info.unused_flags);
++              brelse(bh);
++              *err = ERR_BAD_DX_DIR;
++              goto fail;
++      }
++
++      if ((indirect = root->info.indirect_levels) > 1) {
++              ext3_warning(dir->i_sb, __FUNCTION__,
++                           "Unimplemented inode hash depth: %#06x",
++                           root->info.indirect_levels);
++              brelse(bh);
++              *err = ERR_BAD_DX_DIR;
++              goto fail;
++      }
++
++      entries = (struct dx_entry *) (((char *)&root->info) +
++                                     root->info.info_length);
++      assert(dx_get_limit(entries) == dx_root_limit(dir,
++                                                    root->info.info_length));
++      dxtrace (printk("Look up %x", hash));
++      while (1)
++      {
++              count = dx_get_count(entries);
++              assert (count && count <= dx_get_limit(entries));
++              p = entries + 1;
++              q = entries + count - 1;
++              while (p <= q)
++              {
++                      m = p + (q - p)/2;
++                      dxtrace(printk("."));
++                      if (dx_get_hash(m) > hash)
++                              q = m - 1;
++                      else
++                              p = m + 1;
++              }
++
++              if (0) // linear search cross check
++              {
++                      unsigned n = count - 1;
++                      at = entries;
++                      while (n--)
++                      {
++                              dxtrace(printk(","));
++                              if (dx_get_hash(++at) > hash)
++                              {
++                                      at--;
++                                      break;
++                              }
++                      }
++                      assert (at == p - 1);
++              }
++
++              at = p - 1;
++              dxtrace(printk(" %x->%u\n", at == entries? 0: dx_get_hash(at), dx_get_block(at)));
++              frame->bh = bh;
++              frame->entries = entries;
++              frame->at = at;
++              if (!indirect--) return frame;
++              if (!(bh = ext3_bread (NULL,dir, dx_get_block(at), 0, err)))
++                      goto fail2;
++              at = entries = ((struct dx_node *) bh->b_data)->entries;
++              assert (dx_get_limit(entries) == dx_node_limit (dir));
++              frame++;
++      }
++fail2:
++      while (frame >= frame_in) {
++              brelse(frame->bh);
++              frame--;
++      }
++fail:
++      return NULL;
++}
++
++static void dx_release (struct dx_frame *frames)
++{
++      if (frames[0].bh == NULL)
++              return;
++
++      if (((struct dx_root *) frames[0].bh->b_data)->info.indirect_levels)
++              brelse(frames[1].bh);
++      brelse(frames[0].bh);
++}
++
++/*
++ * This function increments the frame pointer to search the next leaf
++ * block, and reads in the necessary intervening nodes if the search
++ * should be necessary.  Whether or not the search is necessary is
++ * controlled by the hash parameter.  If the hash value is even, then
++ * the search is only continued if the next block starts with that
++ * hash value.  This is used if we are searching for a specific file.
++ *
++ * If the hash value is HASH_NB_ALWAYS, then always go to the next block.
++ *
++ * This function returns 1 if the caller should continue to search,
++ * or 0 if it should not.  If there is an error reading one of the
++ * index blocks, it will return -1.
++ *
++ * If start_hash is non-null, it will be filled in with the starting
++ * hash of the next page.
++ */
++static int ext3_htree_next_block(struct inode *dir, __u32 hash,
++                               struct dx_frame *frame,
++                               struct dx_frame *frames, int *err,
++                               __u32 *start_hash)
++{
++      struct dx_frame *p;
++      struct buffer_head *bh;
++      int num_frames = 0;
++      __u32 bhash;
++
++      *err = ENOENT;
++      p = frame;
++      /*
++       * Find the next leaf page by incrementing the frame pointer.
++       * If we run out of entries in the interior node, loop around and
++       * increment pointer in the parent node.  When we break out of
++       * this loop, num_frames indicates the number of interior
++       * nodes need to be read.
++       */
++      while (1) {
++              if (++(p->at) < p->entries + dx_get_count(p->entries))
++                      break;
++              if (p == frames)
++                      return 0;
++              num_frames++;
++              p--;
++      }
++
++      /*
++       * If the hash is 1, then continue only if the next page has a
++       * continuation hash of any value.  This is used for readdir
++       * handling.  Otherwise, check to see if the hash matches the
++       * desired contiuation hash.  If it doesn't, return since
++       * there's no point to read in the successive index pages.
++       */
++      bhash = dx_get_hash(p->at);
++      if (start_hash)
++              *start_hash = bhash;
++      if ((hash & 1) == 0) {
++              if ((bhash & ~1) != hash)
++                      return 0;
++      }
++      /*
++       * If the hash is HASH_NB_ALWAYS, we always go to the next
++       * block so no check is necessary
++       */
++      while (num_frames--) {
++              if (!(bh = ext3_bread(NULL, dir, dx_get_block(p->at),
++                                    0, err)))
++                      return -1; /* Failure */
++              p++;
++              brelse (p->bh);
++              p->bh = bh;
++              p->at = p->entries = ((struct dx_node *) bh->b_data)->entries;
++      }
++      return 1;
++}
++
++
++/*
++ * p is at least 6 bytes before the end of page
++ */
++static inline struct ext3_dir_entry_2 *ext3_next_entry(struct ext3_dir_entry_2 *p)
++{
++      return (struct ext3_dir_entry_2 *)((char*)p + le16_to_cpu(p->rec_len));
++}
++
++/*
++ * This function fills a red-black tree with information from a
++ * directory.  We start scanning the directory in hash order, starting
++ * at start_hash and start_minor_hash.
++ *
++ * This function returns the number of entries inserted into the tree,
++ * or a negative error code.
++ */
++int ext3_htree_fill_tree(struct file *dir_file, __u32 start_hash,
++                       __u32 start_minor_hash, __u32 *next_hash)
++{
++      struct dx_hash_info hinfo;
++      struct buffer_head *bh;
++      struct ext3_dir_entry_2 *de, *top;
++      static struct dx_frame frames[2], *frame;
++      struct inode *dir;
++      int block, err;
++      int count = 0;
++      int ret;
++      __u32 hashval;
++      
++      dxtrace(printk("In htree_fill_tree, start hash: %x:%x\n", start_hash,
++                     start_minor_hash));
++      dir = dir_file->f_dentry->d_inode;
++      hinfo.hash = start_hash;
++      hinfo.minor_hash = 0;
++      frame = dx_probe(0, dir_file->f_dentry->d_inode, &hinfo, frames, &err);
++      if (!frame)
++              return err;
++
++      /* Add '.' and '..' from the htree header */
++      if (!start_hash && !start_minor_hash) {
++              de = (struct ext3_dir_entry_2 *) frames[0].bh->b_data;
++              if ((err = ext3_htree_store_dirent(dir_file, 0, 0, de)) != 0)
++                      goto errout;
++              de = ext3_next_entry(de);
++              if ((err = ext3_htree_store_dirent(dir_file, 0, 0, de)) != 0)
++                      goto errout;
++              count += 2;
++      }
++
++      while (1) {
++              block = dx_get_block(frame->at);
++              dxtrace(printk("Reading block %d\n", block));
++              if (!(bh = ext3_bread (NULL, dir, block, 0, &err)))
++                      goto errout;
++      
++              de = (struct ext3_dir_entry_2 *) bh->b_data;
++              top = (struct ext3_dir_entry_2 *) ((char *) de + dir->i_sb->s_blocksize -
++                                     EXT3_DIR_REC_LEN(0));
++              for (; de < top; de = ext3_next_entry(de)) {
++                      ext3fs_dirhash(de->name, de->name_len, &hinfo);
++                      if ((hinfo.hash < start_hash) ||
++                          ((hinfo.hash == start_hash) &&
++                           (hinfo.minor_hash < start_minor_hash)))
++                              continue;
++                      if ((err = ext3_htree_store_dirent(dir_file,
++                                 hinfo.hash, hinfo.minor_hash, de)) != 0)
++                              goto errout;
++                      count++;
++              }
++              brelse (bh);
++              hashval = ~1;
++              ret = ext3_htree_next_block(dir, HASH_NB_ALWAYS, 
++                                          frame, frames, &err, &hashval);
++              if (next_hash)
++                      *next_hash = hashval;
++              if (ret == -1)
++                      goto errout;
++              /*
++               * Stop if:  (a) there are no more entries, or
++               * (b) we have inserted at least one entry and the
++               * next hash value is not a continuation
++               */
++              if ((ret == 0) ||
++                  (count && ((hashval & 1) == 0)))
++                      break;
++      }
++      dx_release(frames);
++      dxtrace(printk("Fill tree: returned %d entries\n", count));
++      return count;
++errout:
++      dx_release(frames);
++      return (err);
++}
++
++
++/*
++ * Directory block splitting, compacting
++ */
++
++static int dx_make_map (struct ext3_dir_entry_2 *de, int size,
++                      struct dx_hash_info *hinfo, struct dx_map_entry *map_tail)
++{
++      int count = 0;
++      char *base = (char *) de;
++      struct dx_hash_info h = *hinfo;
++      
++      while ((char *) de < base + size)
++      {
++              if (de->name_len && de->inode) {
++                      ext3fs_dirhash(de->name, de->name_len, &h);
++                      map_tail--;
++                      map_tail->hash = h.hash;
++                      map_tail->offs = (u32) ((char *) de - base);
++                      count++;
++              }
++              /* XXX: do we need to check rec_len == 0 case? -Chris */
++              de = (struct ext3_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len));
++      }
++      return count;
++}
++
++static void dx_sort_map (struct dx_map_entry *map, unsigned count)
++{
++      struct dx_map_entry *p, *q, *top = map + count - 1;
++      int more;
++      /* Combsort until bubble sort doesn't suck */
++      while (count > 2)
++      {
++              count = count*10/13;
++              if (count - 9 < 2) /* 9, 10 -> 11 */
++                      count = 11;
++              for (p = top, q = p - count; q >= map; p--, q--)
++                      if (p->hash < q->hash)
++                              swap(*p, *q);
++      }
++      /* Garden variety bubble sort */
++      do {
++              more = 0;
++              q = top;
++              while (q-- > map)
++              {
++                      if (q[1].hash >= q[0].hash)
++                              continue;
++                      swap(*(q+1), *q);
++                      more = 1;
++              }
++      } while(more);
++}
++
++static void dx_insert_block(struct dx_frame *frame, u32 hash, u32 block)
++{
++      struct dx_entry *entries = frame->entries;
++      struct dx_entry *old = frame->at, *new = old + 1;
++      int count = dx_get_count(entries);
++
++      assert(count < dx_get_limit(entries));
++      assert(old < entries + count);
++      memmove(new + 1, new, (char *)(entries + count) - (char *)(new));
++      dx_set_hash(new, hash);
++      dx_set_block(new, block);
++      dx_set_count(entries, count + 1);
++}
++#endif
++
++
++static void ext3_update_dx_flag(struct inode *inode)
++{
++      if (!EXT3_HAS_COMPAT_FEATURE(inode->i_sb,
++                                   EXT3_FEATURE_COMPAT_DIR_INDEX))
++              EXT3_I(inode)->i_flags &= ~EXT3_INDEX_FL;
++}
++
+ /*
+  * NOTE! unlike strncmp, ext3_match returns 1 for success, 0 for failure.
+  *
+@@ -94,6 +736,7 @@
+       return 0;
+ }
++
+ /*
+  *    ext3_find_entry()
+  *
+@@ -105,6 +748,8 @@
+  * The returned buffer_head has ->b_count elevated.  The caller is expected
+  * to brelse() it when appropriate.
+  */
++
++      
+ static struct buffer_head * ext3_find_entry (struct dentry *dentry,
+                                       struct ext3_dir_entry_2 ** res_dir)
+ {
+@@ -119,12 +764,32 @@
+       int num = 0;
+       int nblocks, i, err;
+       struct inode *dir = dentry->d_parent->d_inode;
++      int namelen;
++      const u8 *name;
++      unsigned blocksize;
+       *res_dir = NULL;
+       sb = dir->i_sb;
+-
++      blocksize = sb->s_blocksize;
++      namelen = dentry->d_name.len;
++      name = dentry->d_name.name;
++      if (namelen > EXT3_NAME_LEN)
++              return NULL;
++#ifdef CONFIG_EXT3_INDEX
++      if (is_dx(dir)) {
++              bh = ext3_dx_find_entry(dentry, res_dir, &err);
++              /*
++               * On success, or if the error was file not found,
++               * return.  Otherwise, fall back to doing a search the
++               * old fashioned way.
++               */
++              if (bh || (err != ERR_BAD_DX_DIR))
++                      return bh;
++              dxtrace(printk("ext3_find_entry: dx failed, falling back\n"));
++      }
++#endif
+       nblocks = dir->i_size >> EXT3_BLOCK_SIZE_BITS(sb);
+-      start = dir->u.ext3_i.i_dir_start_lookup;
++      start = EXT3_I(dir)->i_dir_start_lookup;
+       if (start >= nblocks)
+               start = 0;
+       block = start;
+@@ -165,7 +830,7 @@
+               i = search_dirblock(bh, dir, dentry,
+                           block << EXT3_BLOCK_SIZE_BITS(sb), res_dir);
+               if (i == 1) {
+-                      dir->u.ext3_i.i_dir_start_lookup = block;
++                      EXT3_I(dir)->i_dir_start_lookup = block;
+                       ret = bh;
+                       goto cleanup_and_exit;
+               } else {
+@@ -196,6 +861,66 @@
+       return ret;
+ }
++#ifdef CONFIG_EXT3_INDEX
++static struct buffer_head * ext3_dx_find_entry(struct dentry *dentry,
++                     struct ext3_dir_entry_2 **res_dir, int *err)
++{
++      struct super_block * sb;
++      struct dx_hash_info     hinfo;
++      u32 hash;
++      struct dx_frame frames[2], *frame;
++      struct ext3_dir_entry_2 *de, *top;
++      struct buffer_head *bh;
++      unsigned long block;
++      int retval;
++      int namelen = dentry->d_name.len;
++      const u8 *name = dentry->d_name.name;
++      struct inode *dir = dentry->d_parent->d_inode;
++      
++      sb = dir->i_sb;
++      if (!(frame = dx_probe (dentry, 0, &hinfo, frames, err)))
++              return NULL;
++      hash = hinfo.hash;
++      do {
++              block = dx_get_block(frame->at);
++              if (!(bh = ext3_bread (NULL,dir, block, 0, err)))
++                      goto errout;
++              de = (struct ext3_dir_entry_2 *) bh->b_data;
++              top = (struct ext3_dir_entry_2 *) ((char *) de + sb->s_blocksize -
++                                     EXT3_DIR_REC_LEN(0));
++              for (; de < top; de = ext3_next_entry(de))
++              if (ext3_match (namelen, name, de)) {
++                      if (!ext3_check_dir_entry("ext3_find_entry",
++                                                dir, de, bh,
++                                (block<<EXT3_BLOCK_SIZE_BITS(sb))
++                                        +((char *)de - bh->b_data))) {
++                              brelse (bh);
++                              goto errout;
++                      }
++                      *res_dir = de;
++                      dx_release (frames);
++                      return bh;
++              }
++              brelse (bh);
++              /* Check to see if we should continue to search */
++              retval = ext3_htree_next_block(dir, hash, frame,
++                                             frames, err, 0);
++              if (retval == -1) {
++                      ext3_warning(sb, __FUNCTION__,
++                           "error reading index page in directory #%lu",
++                           dir->i_ino);
++                      goto errout;
++              }
++      } while (retval == 1);
++      
++      *err = -ENOENT;
++errout:
++      dxtrace(printk("%s not found\n", name));
++      dx_release (frames);
++      return NULL;
++}
++#endif
++
+ static struct dentry *ext3_lookup(struct inode * dir, struct dentry *dentry)
+ {
+       struct inode * inode;
+@@ -212,8 +937,9 @@
+               brelse (bh);
+               inode = iget(dir->i_sb, ino);
+-              if (!inode)
++              if (!inode) {
+                       return ERR_PTR(-EACCES);
++              }
+       }
+       d_add(dentry, inode);
+       return NULL;
+@@ -237,6 +963,301 @@
+               de->file_type = ext3_type_by_mode[(mode & S_IFMT)>>S_SHIFT];
+ }
++#ifdef CONFIG_EXT3_INDEX
++static struct ext3_dir_entry_2 *
++dx_move_dirents(char *from, char *to, struct dx_map_entry *map, int count)
++{
++      unsigned rec_len = 0;
++
++      while (count--) {
++              struct ext3_dir_entry_2 *de = (struct ext3_dir_entry_2 *) (from + map->offs);
++              rec_len = EXT3_DIR_REC_LEN(de->name_len);
++              memcpy (to, de, rec_len);
++              ((struct ext3_dir_entry_2 *)to)->rec_len = cpu_to_le16(rec_len);
++              de->inode = 0;
++              map++;
++              to += rec_len;
++      }
++      return (struct ext3_dir_entry_2 *) (to - rec_len);
++}
++
++static struct ext3_dir_entry_2* dx_pack_dirents(char *base, int size)
++{
++      struct ext3_dir_entry_2 *next, *to, *prev, *de = (struct ext3_dir_entry_2 *) base;
++      unsigned rec_len = 0;
++
++      prev = to = de;
++      while ((char*)de < base + size) {
++              next = (struct ext3_dir_entry_2 *) ((char *) de +
++                                                  le16_to_cpu(de->rec_len));
++              if (de->inode && de->name_len) {
++                      rec_len = EXT3_DIR_REC_LEN(de->name_len);
++                      if (de > to)
++                              memmove(to, de, rec_len);
++                      to->rec_len = cpu_to_le16(rec_len);
++                      prev = to;
++                      to = (struct ext3_dir_entry_2 *)((char *)to + rec_len);
++              }
++              de = next;
++      }
++      return prev;
++}
++
++static struct ext3_dir_entry_2 *do_split(handle_t *handle, struct inode *dir,
++                      struct buffer_head **bh,struct dx_frame *frame,
++                      struct dx_hash_info *hinfo, int *error)
++{
++      unsigned blocksize = dir->i_sb->s_blocksize;
++      unsigned count, continued;
++      struct buffer_head *bh2;
++      u32 newblock;
++      u32 hash2;
++      struct dx_map_entry *map;
++      char *data1 = (*bh)->b_data, *data2;
++      unsigned split;
++      struct ext3_dir_entry_2 *de = NULL, *de2;
++      int     err;
++
++      bh2 = ext3_append (handle, dir, &newblock, error);
++      if (!(bh2)) {
++              brelse(*bh);
++              *bh = NULL;
++              goto errout;
++      }
++
++      BUFFER_TRACE(*bh, "get_write_access");
++      err = ext3_journal_get_write_access(handle, *bh);
++      if (err) {
++      journal_error:
++              brelse(*bh);
++              brelse(bh2);
++              *bh = NULL;
++              ext3_std_error(dir->i_sb, err);
++              goto errout;
++      }
++      BUFFER_TRACE(frame->bh, "get_write_access");
++      err = ext3_journal_get_write_access(handle, frame->bh);
++      if (err)
++              goto journal_error;
++
++      data2 = bh2->b_data;
++
++      /* create map in the end of data2 block */
++      map = (struct dx_map_entry *) (data2 + blocksize);
++      count = dx_make_map ((struct ext3_dir_entry_2 *) data1,
++                           blocksize, hinfo, map);
++      map -= count;
++      split = count/2; // need to adjust to actual middle
++      dx_sort_map (map, count);
++      hash2 = map[split].hash;
++      continued = hash2 == map[split - 1].hash;
++      dxtrace(printk("Split block %i at %x, %i/%i\n",
++              dx_get_block(frame->at), hash2, split, count-split));
++
++      /* Fancy dance to stay within two buffers */
++      de2 = dx_move_dirents(data1, data2, map + split, count - split);
++      de = dx_pack_dirents(data1,blocksize);
++      de->rec_len = cpu_to_le16(data1 + blocksize - (char *) de);
++      de2->rec_len = cpu_to_le16(data2 + blocksize - (char *) de2);
++      dxtrace(dx_show_leaf (hinfo, (struct ext3_dir_entry_2 *) data1, blocksize, 1));
++      dxtrace(dx_show_leaf (hinfo, (struct ext3_dir_entry_2 *) data2, blocksize, 1));
++
++      /* Which block gets the new entry? */
++      if (hinfo->hash >= hash2)
++      {
++              swap(*bh, bh2);
++              de = de2;
++      }
++      dx_insert_block (frame, hash2 + continued, newblock);
++      err = ext3_journal_dirty_metadata (handle, bh2);
++      if (err)
++              goto journal_error;
++      err = ext3_journal_dirty_metadata (handle, frame->bh);
++      if (err)
++              goto journal_error;
++      brelse (bh2);
++      dxtrace(dx_show_index ("frame", frame->entries));
++errout:
++      return de;
++}
++#endif
++
++
++/*
++ * Add a new entry into a directory (leaf) block.  If de is non-NULL,
++ * it points to a directory entry which is guaranteed to be large
++ * enough for new directory entry.  If de is NULL, then
++ * add_dirent_to_buf will attempt search the directory block for
++ * space.  It will return -ENOSPC if no space is available, and -EIO
++ * and -EEXIST if directory entry already exists.
++ * 
++ * NOTE!  bh is NOT released in the case where ENOSPC is returned.  In
++ * all other cases bh is released.
++ */
++static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry,
++                           struct inode *inode, struct ext3_dir_entry_2 *de,
++                           struct buffer_head * bh)
++{
++      struct inode    *dir = dentry->d_parent->d_inode;
++      const char      *name = dentry->d_name.name;
++      int             namelen = dentry->d_name.len;
++      unsigned long   offset = 0;
++      unsigned short  reclen;
++      int             nlen, rlen, err;
++      char            *top;
++      
++      reclen = EXT3_DIR_REC_LEN(namelen);
++      if (!de) {
++              de = (struct ext3_dir_entry_2 *)bh->b_data;
++              top = bh->b_data + dir->i_sb->s_blocksize - reclen;
++              while ((char *) de <= top) {
++                      if (!ext3_check_dir_entry("ext3_add_entry", dir, de,
++                                                bh, offset)) {
++                              brelse (bh);
++                              return -EIO;
++                      }
++                      if (ext3_match (namelen, name, de)) {
++                              brelse (bh);
++                              return -EEXIST;
++                      }
++                      nlen = EXT3_DIR_REC_LEN(de->name_len);
++                      rlen = le16_to_cpu(de->rec_len);
++                      if ((de->inode? rlen - nlen: rlen) >= reclen)
++                              break;
++                      de = (struct ext3_dir_entry_2 *)((char *)de + rlen);
++                      offset += rlen;
++              }
++              if ((char *) de > top)
++                      return -ENOSPC;
++      }
++      BUFFER_TRACE(bh, "get_write_access");
++      err = ext3_journal_get_write_access(handle, bh);
++      if (err) {
++              ext3_std_error(dir->i_sb, err);
++              brelse(bh);
++              return err;
++      }
++      
++      /* By now the buffer is marked for journaling */
++      nlen = EXT3_DIR_REC_LEN(de->name_len);
++      rlen = le16_to_cpu(de->rec_len);
++      if (de->inode) {
++              struct ext3_dir_entry_2 *de1 = (struct ext3_dir_entry_2 *)((char *)de + nlen);
++              de1->rec_len = cpu_to_le16(rlen - nlen);
++              de->rec_len = cpu_to_le16(nlen);
++              de = de1;
++      }
++      de->file_type = EXT3_FT_UNKNOWN;
++      if (inode) {
++              de->inode = cpu_to_le32(inode->i_ino);
++              ext3_set_de_type(dir->i_sb, de, inode->i_mode);
++      } else
++              de->inode = 0;
++      de->name_len = namelen;
++      memcpy (de->name, name, namelen);
++      /*
++       * XXX shouldn't update any times until successful
++       * completion of syscall, but too many callers depend
++       * on this.
++       *
++       * XXX similarly, too many callers depend on
++       * ext3_new_inode() setting the times, but error
++       * recovery deletes the inode, so the worst that can
++       * happen is that the times are slightly out of date
++       * and/or different from the directory change time.
++       */
++      dir->i_mtime = dir->i_ctime = CURRENT_TIME;
++      ext3_update_dx_flag(dir);
++      dir->i_version = ++event;
++      ext3_mark_inode_dirty(handle, dir);
++      BUFFER_TRACE(bh, "call ext3_journal_dirty_metadata");
++      err = ext3_journal_dirty_metadata(handle, bh);
++      if (err)
++              ext3_std_error(dir->i_sb, err);
++      brelse(bh);
++      return 0;
++}
++
++#ifdef CONFIG_EXT3_INDEX
++/*
++ * This converts a one block unindexed directory to a 3 block indexed
++ * directory, and adds the dentry to the indexed directory.
++ */
++static int make_indexed_dir(handle_t *handle, struct dentry *dentry,
++                          struct inode *inode, struct buffer_head *bh)
++{
++      struct inode    *dir = dentry->d_parent->d_inode;
++      const char      *name = dentry->d_name.name;
++      int             namelen = dentry->d_name.len;
++      struct buffer_head *bh2;
++      struct dx_root  *root;
++      struct dx_frame frames[2], *frame;
++      struct dx_entry *entries;
++      struct ext3_dir_entry_2 *de, *de2;
++      char            *data1, *top;
++      unsigned        len;
++      int             retval;
++      unsigned        blocksize;
++      struct dx_hash_info hinfo;
++      u32             block;
++              
++      blocksize =  dir->i_sb->s_blocksize;
++      dxtrace(printk("Creating index\n"));
++      retval = ext3_journal_get_write_access(handle, bh);
++      if (retval) {
++              ext3_std_error(dir->i_sb, retval);
++              brelse(bh);
++              return retval;
++      }
++      root = (struct dx_root *) bh->b_data;
++              
++      EXT3_I(dir)->i_flags |= EXT3_INDEX_FL;
++      bh2 = ext3_append (handle, dir, &block, &retval);
++      if (!(bh2)) {
++              brelse(bh);
++              return retval;
++      }
++      data1 = bh2->b_data;
++
++      /* The 0th block becomes the root, move the dirents out */
++      de = (struct ext3_dir_entry_2 *)&root->dotdot;
++      de = (struct ext3_dir_entry_2 *)((char *)de + le16_to_cpu(de->rec_len));
++      len = ((char *) root) + blocksize - (char *) de;
++      memcpy (data1, de, len);
++      de = (struct ext3_dir_entry_2 *) data1;
++      top = data1 + len;
++      while (((char *) de2=(char*)de+le16_to_cpu(de->rec_len)) < top)
++              de = de2;
++      de->rec_len = cpu_to_le16(data1 + blocksize - (char *) de);
++      /* Initialize the root; the dot dirents already exist */
++      de = (struct ext3_dir_entry_2 *) (&root->dotdot);
++      de->rec_len = cpu_to_le16(blocksize - EXT3_DIR_REC_LEN(2));
++      memset (&root->info, 0, sizeof(root->info));
++      root->info.info_length = sizeof(root->info);
++      root->info.hash_version = dir->i_sb->u.ext3_sb.s_def_hash_version;
++      entries = root->entries;
++      dx_set_block (entries, 1);
++      dx_set_count (entries, 1);
++      dx_set_limit (entries, dx_root_limit(dir, sizeof(root->info)));
++
++      /* Initialize as for dx_probe */
++      hinfo.hash_version = root->info.hash_version;
++      hinfo.seed = dir->i_sb->u.ext3_sb.s_hash_seed;
++      ext3fs_dirhash(name, namelen, &hinfo);
++      frame = frames;
++      frame->entries = entries;
++      frame->at = entries;
++      frame->bh = bh;
++      bh = bh2;
++      de = do_split(handle,dir, &bh, frame, &hinfo, &retval);
++      dx_release (frames);
++      if (!(de))
++              return retval;
++
++      return add_dirent_to_buf(handle, dentry, inode, de, bh);
++}
++#endif
++
+ /*
+  *    ext3_add_entry()
+  *
+@@ -247,127 +1268,198 @@
+  * may not sleep between calling this and putting something into
+  * the entry, as someone else might have used it while you slept.
+  */
+-
+-/*
+- * AKPM: the journalling code here looks wrong on the error paths
+- */
+ static int ext3_add_entry (handle_t *handle, struct dentry *dentry,
+       struct inode *inode)
+ {
+       struct inode *dir = dentry->d_parent->d_inode;
+-      const char *name = dentry->d_name.name;
+-      int namelen = dentry->d_name.len;
+       unsigned long offset;
+-      unsigned short rec_len;
+       struct buffer_head * bh;
+-      struct ext3_dir_entry_2 * de, * de1;
++      struct ext3_dir_entry_2 *de;
+       struct super_block * sb;
+       int     retval;
++#ifdef CONFIG_EXT3_INDEX
++      int     dx_fallback=0;
++#endif
++      unsigned blocksize;
++      unsigned nlen, rlen;
++      u32 block, blocks;
+       sb = dir->i_sb;
+-
+-      if (!namelen)
++      blocksize = sb->s_blocksize;
++      if (!dentry->d_name.len)
+               return -EINVAL;
+-      bh = ext3_bread (handle, dir, 0, 0, &retval);
++#ifdef CONFIG_EXT3_INDEX
++      if (is_dx(dir)) {
++              retval = ext3_dx_add_entry(handle, dentry, inode);
++              if (!retval || (retval != ERR_BAD_DX_DIR))
++                      return retval;
++              EXT3_I(dir)->i_flags &= ~EXT3_INDEX_FL;
++              dx_fallback++;
++              ext3_mark_inode_dirty(handle, dir);
++      }
++#endif
++      blocks = dir->i_size >> sb->s_blocksize_bits;
++      for (block = 0, offset = 0; block < blocks; block++) {
++              bh = ext3_bread(handle, dir, block, 0, &retval);
++              if(!bh)
++                      return retval;
++              retval = add_dirent_to_buf(handle, dentry, inode, 0, bh);
++              if (retval != -ENOSPC)
++                      return retval;
++
++#ifdef CONFIG_EXT3_INDEX
++              if (blocks == 1 && !dx_fallback &&
++                  EXT3_HAS_COMPAT_FEATURE(sb, EXT3_FEATURE_COMPAT_DIR_INDEX))
++                      return make_indexed_dir(handle, dentry, inode, bh);
++#endif
++              brelse(bh);
++      }
++      bh = ext3_append(handle, dir, &block, &retval);
+       if (!bh)
+               return retval;
+-      rec_len = EXT3_DIR_REC_LEN(namelen);
+-      offset = 0;
+       de = (struct ext3_dir_entry_2 *) bh->b_data;
+-      while (1) {
+-              if ((char *)de >= sb->s_blocksize + bh->b_data) {
+-                      brelse (bh);
+-                      bh = NULL;
+-                      bh = ext3_bread (handle, dir,
+-                              offset >> EXT3_BLOCK_SIZE_BITS(sb), 1, &retval);
+-                      if (!bh)
+-                              return retval;
+-                      if (dir->i_size <= offset) {
+-                              if (dir->i_size == 0) {
+-                                      brelse(bh);
+-                                      return -ENOENT;
+-                              }
++      de->inode = 0;
++      de->rec_len = cpu_to_le16(rlen = blocksize);
++      nlen = 0;
++      return add_dirent_to_buf(handle, dentry, inode, de, bh);
++}
+-                              ext3_debug ("creating next block\n");
++#ifdef CONFIG_EXT3_INDEX
++/*
++ * Returns 0 for success, or a negative error value
++ */
++static int ext3_dx_add_entry(handle_t *handle, struct dentry *dentry,
++                           struct inode *inode)
++{
++      struct dx_frame frames[2], *frame;
++      struct dx_entry *entries, *at;
++      struct dx_hash_info hinfo;
++      struct buffer_head * bh;
++      struct inode *dir = dentry->d_parent->d_inode;
++      struct super_block * sb = dir->i_sb;
++      struct ext3_dir_entry_2 *de;
++      int err;
+-                              BUFFER_TRACE(bh, "get_write_access");
+-                              ext3_journal_get_write_access(handle, bh);
+-                              de = (struct ext3_dir_entry_2 *) bh->b_data;
+-                              de->inode = 0;
+-                              de->rec_len = le16_to_cpu(sb->s_blocksize);
+-                              dir->u.ext3_i.i_disksize =
+-                                      dir->i_size = offset + sb->s_blocksize;
+-                              dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL;
+-                              ext3_mark_inode_dirty(handle, dir);
+-                      } else {
++      frame = dx_probe(dentry, 0, &hinfo, frames, &err);
++      if (!frame)
++              return err;
++      entries = frame->entries;
++      at = frame->at;
+-                              ext3_debug ("skipping to next block\n");
++      if (!(bh = ext3_bread(handle,dir, dx_get_block(frame->at), 0, &err)))
++              goto cleanup;
+-                              de = (struct ext3_dir_entry_2 *) bh->b_data;
+-                      }
+-              }
+-              if (!ext3_check_dir_entry ("ext3_add_entry", dir, de, bh,
+-                                         offset)) {
+-                      brelse (bh);
+-                      return -ENOENT;
+-              }
+-              if (ext3_match (namelen, name, de)) {
+-                              brelse (bh);
+-                              return -EEXIST;
++      BUFFER_TRACE(bh, "get_write_access");
++      err = ext3_journal_get_write_access(handle, bh);
++      if (err)
++              goto journal_error;
++
++      err = add_dirent_to_buf(handle, dentry, inode, 0, bh);
++      if (err != -ENOSPC) {
++              bh = 0;
++              goto cleanup;
++      }
++
++      /* Block full, should compress but for now just split */
++      dxtrace(printk("using %u of %u node entries\n",
++                     dx_get_count(entries), dx_get_limit(entries)));
++      /* Need to split index? */
++      if (dx_get_count(entries) == dx_get_limit(entries)) {
++              u32 newblock;
++              unsigned icount = dx_get_count(entries);
++              int levels = frame - frames;
++              struct dx_entry *entries2;
++              struct dx_node *node2;
++              struct buffer_head *bh2;
++
++              if (levels && (dx_get_count(frames->entries) ==
++                             dx_get_limit(frames->entries))) {
++                      ext3_warning(sb, __FUNCTION__,
++                                   "Directory index full!\n");
++                      err = -ENOSPC;
++                      goto cleanup;
+               }
+-              if ((le32_to_cpu(de->inode) == 0 &&
+-                              le16_to_cpu(de->rec_len) >= rec_len) ||
+-                  (le16_to_cpu(de->rec_len) >=
+-                              EXT3_DIR_REC_LEN(de->name_len) + rec_len)) {
+-                      BUFFER_TRACE(bh, "get_write_access");
+-                      ext3_journal_get_write_access(handle, bh);
+-                      /* By now the buffer is marked for journaling */
+-                      offset += le16_to_cpu(de->rec_len);
+-                      if (le32_to_cpu(de->inode)) {
+-                              de1 = (struct ext3_dir_entry_2 *) ((char *) de +
+-                                      EXT3_DIR_REC_LEN(de->name_len));
+-                              de1->rec_len =
+-                                      cpu_to_le16(le16_to_cpu(de->rec_len) -
+-                                      EXT3_DIR_REC_LEN(de->name_len));
+-                              de->rec_len = cpu_to_le16(
+-                                              EXT3_DIR_REC_LEN(de->name_len));
+-                              de = de1;
++              bh2 = ext3_append (handle, dir, &newblock, &err);
++              if (!(bh2))
++                      goto cleanup;
++              node2 = (struct dx_node *)(bh2->b_data);
++              entries2 = node2->entries;
++              node2->fake.rec_len = cpu_to_le16(sb->s_blocksize);
++              node2->fake.inode = 0;
++              BUFFER_TRACE(frame->bh, "get_write_access");
++              err = ext3_journal_get_write_access(handle, frame->bh);
++              if (err)
++                      goto journal_error;
++              if (levels) {
++                      unsigned icount1 = icount/2, icount2 = icount - icount1;
++                      unsigned hash2 = dx_get_hash(entries + icount1);
++                      dxtrace(printk("Split index %i/%i\n", icount1, icount2));
++                              
++                      BUFFER_TRACE(frame->bh, "get_write_access"); /* index root */
++                      err = ext3_journal_get_write_access(handle,
++                                                           frames[0].bh);
++                      if (err)
++                              goto journal_error;
++                              
++                      memcpy ((char *) entries2, (char *) (entries + icount1),
++                              icount2 * sizeof(struct dx_entry));
++                      dx_set_count (entries, icount1);
++                      dx_set_count (entries2, icount2);
++                      dx_set_limit (entries2, dx_node_limit(dir));
++
++                      /* Which index block gets the new entry? */
++                      if (at - entries >= icount1) {
++                              frame->at = at = at - entries - icount1 + entries2;
++                              frame->entries = entries = entries2;
++                              swap(frame->bh, bh2);
+                       }
+-                      de->file_type = EXT3_FT_UNKNOWN;
+-                      if (inode) {
+-                              de->inode = cpu_to_le32(inode->i_ino);
+-                              ext3_set_de_type(dir->i_sb, de, inode->i_mode);
+-                      } else
+-                              de->inode = 0;
+-                      de->name_len = namelen;
+-                      memcpy (de->name, name, namelen);
+-                      /*
+-                       * XXX shouldn't update any times until successful
+-                       * completion of syscall, but too many callers depend
+-                       * on this.
+-                       *
+-                       * XXX similarly, too many callers depend on
+-                       * ext3_new_inode() setting the times, but error
+-                       * recovery deletes the inode, so the worst that can
+-                       * happen is that the times are slightly out of date
+-                       * and/or different from the directory change time.
+-                       */
+-                      dir->i_mtime = dir->i_ctime = CURRENT_TIME;
+-                      dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL;
+-                      dir->i_version = ++event;
+-                      ext3_mark_inode_dirty(handle, dir);
+-                      BUFFER_TRACE(bh, "call ext3_journal_dirty_metadata");
+-                      ext3_journal_dirty_metadata(handle, bh);
+-                      brelse(bh);
+-                      return 0;
++                      dx_insert_block (frames + 0, hash2, newblock);
++                      dxtrace(dx_show_index ("node", frames[1].entries));
++                      dxtrace(dx_show_index ("node",
++                             ((struct dx_node *) bh2->b_data)->entries));
++                      err = ext3_journal_dirty_metadata(handle, bh2);
++                      if (err)
++                              goto journal_error;
++                      brelse (bh2);
++              } else {
++                      dxtrace(printk("Creating second level index...\n"));
++                      memcpy((char *) entries2, (char *) entries,
++                             icount * sizeof(struct dx_entry));
++                      dx_set_limit(entries2, dx_node_limit(dir));
++
++                      /* Set up root */
++                      dx_set_count(entries, 1);
++                      dx_set_block(entries + 0, newblock);
++                      ((struct dx_root *) frames[0].bh->b_data)->info.indirect_levels = 1;
++
++                      /* Add new access path frame */
++                      frame = frames + 1;
++                      frame->at = at = at - entries + entries2;
++                      frame->entries = entries = entries2;
++                      frame->bh = bh2;
++                      err = ext3_journal_get_write_access(handle,
++                                                           frame->bh);
++                      if (err)
++                              goto journal_error;
+               }
+-              offset += le16_to_cpu(de->rec_len);
+-              de = (struct ext3_dir_entry_2 *)
+-                      ((char *) de + le16_to_cpu(de->rec_len));
++              ext3_journal_dirty_metadata(handle, frames[0].bh);
+       }
+-      brelse (bh);
+-      return -ENOSPC;
++      de = do_split(handle, dir, &bh, frame, &hinfo, &err);
++      if (!de)
++              goto cleanup;
++      err = add_dirent_to_buf(handle, dentry, inode, de, bh);
++      bh = 0;
++      goto cleanup;
++      
++journal_error:
++      ext3_std_error(dir->i_sb, err);
++cleanup:
++      if (bh)
++              brelse(bh);
++      dx_release(frames);
++      return err;
+ }
++#endif
+ /*
+  * ext3_delete_entry deletes a directory entry by merging it with the
+@@ -454,9 +1546,11 @@
+       struct inode * inode;
+       int err;
+-      handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS + 3);
+-      if (IS_ERR(handle))
++      handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS +
++                                      EXT3_INDEX_EXTRA_TRANS_BLOCKS + 3);
++      if (IS_ERR(handle)) {
+               return PTR_ERR(handle);
++      }
+       if (IS_SYNC(dir))
+               handle->h_sync = 1;
+@@ -480,9 +1574,11 @@
+       struct inode *inode;
+       int err;
+-      handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS + 3);
+-      if (IS_ERR(handle))
++      handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS +
++                                      EXT3_INDEX_EXTRA_TRANS_BLOCKS + 3);
++      if (IS_ERR(handle)) {
+               return PTR_ERR(handle);
++      }
+       if (IS_SYNC(dir))
+               handle->h_sync = 1;
+@@ -508,9 +1604,11 @@
+       if (dir->i_nlink >= EXT3_LINK_MAX)
+               return -EMLINK;
+-      handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS + 3);
+-      if (IS_ERR(handle))
++      handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS +
++                                      EXT3_INDEX_EXTRA_TRANS_BLOCKS + 3);
++      if (IS_ERR(handle)) {
+               return PTR_ERR(handle);
++      }
+       if (IS_SYNC(dir))
+               handle->h_sync = 1;
+@@ -522,7 +1620,7 @@
+       inode->i_op = &ext3_dir_inode_operations;
+       inode->i_fop = &ext3_dir_operations;
+-      inode->i_size = inode->u.ext3_i.i_disksize = inode->i_sb->s_blocksize;
++      inode->i_size = EXT3_I(inode)->i_disksize = inode->i_sb->s_blocksize;
+       inode->i_blocks = 0;    
+       dir_block = ext3_bread (handle, inode, 0, 1, &err);
+       if (!dir_block) {
+@@ -555,21 +1653,19 @@
+               inode->i_mode |= S_ISGID;
+       ext3_mark_inode_dirty(handle, inode);
+       err = ext3_add_entry (handle, dentry, inode);
+-      if (err)
+-              goto out_no_entry;
++      if (err) {
++              inode->i_nlink = 0;
++              ext3_mark_inode_dirty(handle, inode);
++              iput (inode);
++              goto out_stop;
++      }
+       dir->i_nlink++;
+-      dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL;
++      ext3_update_dx_flag(dir);
+       ext3_mark_inode_dirty(handle, dir);
+       d_instantiate(dentry, inode);
+ out_stop:
+       ext3_journal_stop(handle, dir);
+       return err;
+-
+-out_no_entry:
+-      inode->i_nlink = 0;
+-      ext3_mark_inode_dirty(handle, inode);
+-      iput (inode);
+-      goto out_stop;
+ }
+ /*
+@@ -656,7 +1752,7 @@
+       int err = 0, rc;
+       
+       lock_super(sb);
+-      if (!list_empty(&inode->u.ext3_i.i_orphan))
++      if (!list_empty(&EXT3_I(inode)->i_orphan))
+               goto out_unlock;
+       /* Orphan handling is only valid for files with data blocks
+@@ -697,7 +1793,7 @@
+        * This is safe: on error we're going to ignore the orphan list
+        * anyway on the next recovery. */
+       if (!err)
+-              list_add(&inode->u.ext3_i.i_orphan, &EXT3_SB(sb)->s_orphan);
++              list_add(&EXT3_I(inode)->i_orphan, &EXT3_SB(sb)->s_orphan);
+       jbd_debug(4, "superblock will point to %ld\n", inode->i_ino);
+       jbd_debug(4, "orphan inode %ld will point to %d\n",
+@@ -715,25 +1811,26 @@
+ int ext3_orphan_del(handle_t *handle, struct inode *inode)
+ {
+       struct list_head *prev;
++      struct ext3_inode_info *ei = EXT3_I(inode);
+       struct ext3_sb_info *sbi;
+       unsigned long ino_next;
+       struct ext3_iloc iloc;
+       int err = 0;
+       lock_super(inode->i_sb);
+-      if (list_empty(&inode->u.ext3_i.i_orphan)) {
++      if (list_empty(&ei->i_orphan)) {
+               unlock_super(inode->i_sb);
+               return 0;
+       }
+       ino_next = NEXT_ORPHAN(inode);
+-      prev = inode->u.ext3_i.i_orphan.prev;
++      prev = ei->i_orphan.prev;
+       sbi = EXT3_SB(inode->i_sb);
+       jbd_debug(4, "remove inode %lu from orphan list\n", inode->i_ino);
+-      list_del(&inode->u.ext3_i.i_orphan);
+-      INIT_LIST_HEAD(&inode->u.ext3_i.i_orphan);
++      list_del(&ei->i_orphan);
++      INIT_LIST_HEAD(&ei->i_orphan);
+       /* If we're on an error path, we may not have a valid
+        * transaction handle with which to update the orphan list on
+@@ -794,8 +1891,9 @@
+       handle_t *handle;
+       handle = ext3_journal_start(dir, EXT3_DELETE_TRANS_BLOCKS);
+-      if (IS_ERR(handle))
++      if (IS_ERR(handle)) {
+               return PTR_ERR(handle);
++      }
+       retval = -ENOENT;
+       bh = ext3_find_entry (dentry, &de);
+@@ -833,7 +1931,7 @@
+       dir->i_nlink--;
+       inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+       ext3_mark_inode_dirty(handle, inode);
+-      dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL;
++      ext3_update_dx_flag(dir);
+       ext3_mark_inode_dirty(handle, dir);
+ end_rmdir:
+@@ -851,8 +1949,9 @@
+       handle_t *handle;
+       handle = ext3_journal_start(dir, EXT3_DELETE_TRANS_BLOCKS);
+-      if (IS_ERR(handle))
++      if (IS_ERR(handle)) {
+               return PTR_ERR(handle);
++      }
+       if (IS_SYNC(dir))
+               handle->h_sync = 1;
+@@ -879,7 +1978,7 @@
+       if (retval)
+               goto end_unlink;
+       dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+-      dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL;
++      ext3_update_dx_flag(dir);
+       ext3_mark_inode_dirty(handle, dir);
+       inode->i_nlink--;
+       if (!inode->i_nlink)
+@@ -905,9 +2004,11 @@
+       if (l > dir->i_sb->s_blocksize)
+               return -ENAMETOOLONG;
+-      handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS + 5);
+-      if (IS_ERR(handle))
++      handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS +
++                                      EXT3_INDEX_EXTRA_TRANS_BLOCKS + 5);
++      if (IS_ERR(handle)) {
+               return PTR_ERR(handle);
++      }
+       if (IS_SYNC(dir))
+               handle->h_sync = 1;
+@@ -917,7 +2018,7 @@
+       if (IS_ERR(inode))
+               goto out_stop;
+-      if (l > sizeof (inode->u.ext3_i.i_data)) {
++      if (l > sizeof (EXT3_I(inode)->i_data)) {
+               inode->i_op = &page_symlink_inode_operations;
+               inode->i_mapping->a_ops = &ext3_aops;
+               /*
+@@ -926,24 +2027,22 @@
+                * i_size in generic_commit_write().
+                */
+               err = block_symlink(inode, symname, l);
+-              if (err)
+-                      goto out_no_entry;
++              if (err) {
++                      ext3_dec_count(handle, inode);
++                      ext3_mark_inode_dirty(handle, inode);
++                      iput (inode);
++                      goto out_stop;
++              }
+       } else {
+               inode->i_op = &ext3_fast_symlink_inode_operations;
+-              memcpy((char*)&inode->u.ext3_i.i_data,symname,l);
++              memcpy((char*)&EXT3_I(inode)->i_data,symname,l);
+               inode->i_size = l-1;
+       }
+-      inode->u.ext3_i.i_disksize = inode->i_size;
++      EXT3_I(inode)->i_disksize = inode->i_size;
+       err = ext3_add_nondir(handle, dentry, inode);
+ out_stop:
+       ext3_journal_stop(handle, dir);
+       return err;
+-
+-out_no_entry:
+-      ext3_dec_count(handle, inode);
+-      ext3_mark_inode_dirty(handle, inode);
+-      iput (inode);
+-      goto out_stop;
+ }
+ static int ext3_link (struct dentry * old_dentry,
+@@ -956,12 +2055,15 @@
+       if (S_ISDIR(inode->i_mode))
+               return -EPERM;
+-      if (inode->i_nlink >= EXT3_LINK_MAX)
++      if (inode->i_nlink >= EXT3_LINK_MAX) {
+               return -EMLINK;
++      }
+-      handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS);
+-      if (IS_ERR(handle))
++      handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS +
++                                      EXT3_INDEX_EXTRA_TRANS_BLOCKS);
++      if (IS_ERR(handle)) {
+               return PTR_ERR(handle);
++      }
+       if (IS_SYNC(dir))
+               handle->h_sync = 1;
+@@ -994,9 +2096,11 @@
+       old_bh = new_bh = dir_bh = NULL;
+-      handle = ext3_journal_start(old_dir, 2 * EXT3_DATA_TRANS_BLOCKS + 2);
+-      if (IS_ERR(handle))
++      handle = ext3_journal_start(old_dir, 2 * EXT3_DATA_TRANS_BLOCKS +
++                                      EXT3_INDEX_EXTRA_TRANS_BLOCKS + 2);
++      if (IS_ERR(handle)) {
+               return PTR_ERR(handle);
++      }
+       if (IS_SYNC(old_dir) || IS_SYNC(new_dir))
+               handle->h_sync = 1;
+@@ -1069,14 +2173,37 @@
+       /*
+        * ok, that's it
+        */
+-      ext3_delete_entry(handle, old_dir, old_de, old_bh);
++      if (le32_to_cpu(old_de->inode) != old_inode->i_ino ||
++          old_de->name_len != old_dentry->d_name.len ||
++          strncmp(old_de->name, old_dentry->d_name.name, old_de->name_len) ||
++          (retval = ext3_delete_entry(handle, old_dir,
++                                      old_de, old_bh)) == -ENOENT) {
++              /* old_de could have moved from under us during htree split, so
++               * make sure that we are deleting the right entry.  We might
++               * also be pointing to a stale entry in the unused part of
++               * old_bh so just checking inum and the name isn't enough. */
++              struct buffer_head *old_bh2;
++              struct ext3_dir_entry_2 *old_de2;
++
++              old_bh2 = ext3_find_entry(old_dentry, &old_de2);
++              if (old_bh2) {
++                      retval = ext3_delete_entry(handle, old_dir,
++                                                 old_de2, old_bh2);
++                      brelse(old_bh2);
++              }
++      }
++      if (retval) {
++              ext3_warning(old_dir->i_sb, "ext3_rename",
++                              "Deleting old file (%lu), %d, error=%d",
++                              old_dir->i_ino, old_dir->i_nlink, retval);
++      }
+       if (new_inode) {
+               new_inode->i_nlink--;
+               new_inode->i_ctime = CURRENT_TIME;
+       }
+       old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
+-      old_dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL;
++      ext3_update_dx_flag(old_dir);
+       if (dir_bh) {
+               BUFFER_TRACE(dir_bh, "get_write_access");
+               ext3_journal_get_write_access(handle, dir_bh);
+@@ -1088,7 +2215,7 @@
+                       new_inode->i_nlink--;
+               } else {
+                       new_dir->i_nlink++;
+-                      new_dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL;
++                      ext3_update_dx_flag(new_dir);
+                       ext3_mark_inode_dirty(handle, new_dir);
+               }
+       }
+Index: linux-2.4.29/fs/ext3/super.c
+===================================================================
+--- linux-2.4.29.orig/fs/ext3/super.c  2005-04-07 18:59:19.000000000 +0300
++++ linux-2.4.29/fs/ext3/super.c       2005-05-03 16:29:50.580498544 +0300
+@@ -712,6 +712,7 @@
+       es->s_mtime = cpu_to_le32(CURRENT_TIME);
+       ext3_update_dynamic_rev(sb);
+       EXT3_SET_INCOMPAT_FEATURE(sb, EXT3_FEATURE_INCOMPAT_RECOVER);
++
+       ext3_commit_super (sb, es, 1);
+       if (test_opt (sb, DEBUG))
+               printk (KERN_INFO
+@@ -722,6 +723,7 @@
+                       EXT3_BLOCKS_PER_GROUP(sb),
+                       EXT3_INODES_PER_GROUP(sb),
+                       sbi->s_mount_opt);
++
+       printk(KERN_INFO "EXT3 FS " EXT3FS_VERSION ", " EXT3FS_DATE " on %s, ",
+                               bdevname(sb->s_dev));
+       if (EXT3_SB(sb)->s_journal->j_inode == NULL) {
+@@ -915,6 +917,7 @@
+       return res;
+ }
++
+ struct super_block * ext3_read_super (struct super_block * sb, void * data,
+                                     int silent)
+ {
+@@ -1094,6 +1097,9 @@
+       sbi->s_mount_state = le16_to_cpu(es->s_state);
+       sbi->s_addr_per_block_bits = log2(EXT3_ADDR_PER_BLOCK(sb));
+       sbi->s_desc_per_block_bits = log2(EXT3_DESC_PER_BLOCK(sb));
++      for (i=0; i < 4; i++)
++              sbi->s_hash_seed[i] = le32_to_cpu(es->s_hash_seed[i]);
++      sbi->s_def_hash_version = es->s_def_hash_version;
+       if (sbi->s_blocks_per_group > blocksize * 8) {
+               printk (KERN_ERR
+@@ -1845,6 +1851,7 @@
+       unregister_filesystem(&ext3_fs_type);
+ }
++EXPORT_SYMBOL(ext3_force_commit);
+ EXPORT_SYMBOL(ext3_bread);
+ MODULE_AUTHOR("Remy Card, Stephen Tweedie, Andrew Morton, Andreas Dilger, Theodore Ts'o and others");
+Index: linux-2.4.29/include/linux/ext3_fs.h
+===================================================================
+--- linux-2.4.29.orig/include/linux/ext3_fs.h  2005-04-07 18:52:26.000000000 +0300
++++ linux-2.4.29/include/linux/ext3_fs.h       2005-05-03 16:29:50.584497936 +0300
+@@ -40,6 +40,11 @@
+ #define EXT3FS_VERSION                "2.4-0.9.19"
+ /*
++ * Always enable hashed directories
++ */
++#define CONFIG_EXT3_INDEX
++
++/*
+  * Debug code
+  */
+ #ifdef EXT3FS_DEBUG
+@@ -593,9 +598,46 @@
+ #define EXT3_DIR_ROUND                        (EXT3_DIR_PAD - 1)
+ #define EXT3_DIR_REC_LEN(name_len)    (((name_len) + 8 + EXT3_DIR_ROUND) & \
+                                        ~EXT3_DIR_ROUND)
++/*
++ * Hash Tree Directory indexing
++ * (c) Daniel Phillips, 2001
++ */
++
++#ifdef CONFIG_EXT3_INDEX
++  #define is_dx(dir) (EXT3_HAS_COMPAT_FEATURE(dir->i_sb, \
++                                            EXT3_FEATURE_COMPAT_DIR_INDEX) && \
++                    (EXT3_I(dir)->i_flags & EXT3_INDEX_FL))
++#define EXT3_DIR_LINK_MAX(dir) (!is_dx(dir) && (dir)->i_nlink >= EXT3_LINK_MAX)
++#define EXT3_DIR_LINK_EMPTY(dir) ((dir)->i_nlink == 2 || (dir)->i_nlink == 1)
++#else
++  #define is_dx(dir) 0
++#define EXT3_DIR_LINK_MAX(dir) ((dir)->i_nlink >= EXT3_LINK_MAX)
++#define EXT3_DIR_LINK_EMPTY(dir) ((dir)->i_nlink == 2)
++#endif
++
++/* Legal values for the dx_root hash_version field: */
++
++#define DX_HASH_LEGACY                0
++#define DX_HASH_HALF_MD4      1
++#define DX_HASH_TEA           2
++
++/* hash info structure used by the directory hash */
++struct dx_hash_info
++{
++      u32             hash;
++      u32             minor_hash;
++      int             hash_version;
++      u32             *seed;
++};
+ #ifdef __KERNEL__
+ /*
++ * Control parameters used by ext3_htree_next_block
++ */
++#define HASH_NB_ALWAYS                1
++
++
++/*
+  * Describe an inode's exact location on disk and in memory
+  */
+ struct ext3_iloc
+@@ -605,6 +647,27 @@
+       unsigned long block_group;
+ };
++
++/*
++ * This structure is stuffed into the struct file's private_data field
++ * for directories.  It is where we put information so that we can do
++ * readdir operations in hash tree order.
++ */
++struct dir_private_info {
++      rb_root_t       root;
++      rb_node_t       *curr_node;
++      struct fname    *extra_fname;
++      loff_t          last_pos;
++      __u32           curr_hash;
++      __u32           curr_minor_hash;
++      __u32           next_hash;
++};
++
++/*
++ * Special error return code only used by dx_probe() and its callers.
++ */
++#define ERR_BAD_DX_DIR        -75000
++
+ /*
+  * Function prototypes
+  */
+@@ -632,11 +695,20 @@
+ /* dir.c */
+ extern int ext3_check_dir_entry(const char *, struct inode *,
+-                              struct ext3_dir_entry_2 *, struct buffer_head *,
+-                              unsigned long);
++                              struct ext3_dir_entry_2 *,
++                              struct buffer_head *, unsigned long);
++extern int ext3_htree_store_dirent(struct file *dir_file, __u32 hash,
++                                  __u32 minor_hash,
++                                  struct ext3_dir_entry_2 *dirent);
++extern void ext3_htree_free_dir_info(struct dir_private_info *p);
++
+ /* fsync.c */
+ extern int ext3_sync_file (struct file *, struct dentry *, int);
++/* hash.c */
++extern int ext3fs_dirhash(const char *name, int len, struct
++                        dx_hash_info *hinfo);
++
+ /* ialloc.c */
+ extern struct inode * ext3_new_inode (handle_t *, const struct inode *, int);
+ extern void ext3_free_inode (handle_t *, struct inode *);
+@@ -669,6 +741,8 @@
+ /* namei.c */
+ extern int ext3_orphan_add(handle_t *, struct inode *);
+ extern int ext3_orphan_del(handle_t *, struct inode *);
++extern int ext3_htree_fill_tree(struct file *dir_file, __u32 start_hash,
++                              __u32 start_minor_hash, __u32 *next_hash);
+ /* super.c */
+ extern void ext3_error (struct super_block *, const char *, const char *, ...)
+Index: linux-2.4.29/include/linux/ext3_fs_sb.h
+===================================================================
+--- linux-2.4.29.orig/include/linux/ext3_fs_sb.h       2005-04-07 18:54:55.000000000 +0300
++++ linux-2.4.29/include/linux/ext3_fs_sb.h    2005-05-03 16:29:50.586497632 +0300
+@@ -62,6 +62,8 @@
+       int s_inode_size;
+       int s_first_ino;
+       u32 s_next_generation;
++      u32 s_hash_seed[4];
++      int s_def_hash_version;
+       /* Journaling */
+       struct inode * s_journal_inode;
+Index: linux-2.4.29/include/linux/ext3_jbd.h
+===================================================================
+--- linux-2.4.29.orig/include/linux/ext3_jbd.h 2005-04-07 18:52:32.000000000 +0300
++++ linux-2.4.29/include/linux/ext3_jbd.h      2005-05-03 16:29:50.587497480 +0300
+@@ -63,6 +63,8 @@
+ #define EXT3_RESERVE_TRANS_BLOCKS     12U
++#define EXT3_INDEX_EXTRA_TRANS_BLOCKS 8
++
+ int
+ ext3_mark_iloc_dirty(handle_t *handle, 
+                    struct inode *inode,
diff --git a/lustre/kernel_patches/patches/ext3-htree-dot-2.6.5-suse.patch b/lustre/kernel_patches/patches/ext3-htree-dot-2.6.5-suse.patch
new file mode 100644 (file)
index 0000000..e8ed192
--- /dev/null
@@ -0,0 +1,23 @@
+Index: linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891/fs/ext3/namei.c
+===================================================================
+--- linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891.orig/fs/ext3/namei.c 2005-04-04 05:06:46.000000000 -0600
++++ linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891/fs/ext3/namei.c      2005-04-04 05:09:18.000000000 -0600
+@@ -926,8 +926,16 @@
+       struct inode *dir = dentry->d_parent->d_inode;
+       sb = dir->i_sb;
+-      if (!(frame = dx_probe (dentry, 0, &hinfo, frames, err)))
+-              return NULL;
++      /* NFS may look up ".." - look at dx_root directory block */
++      if (namelen > 2 || name[0] != '.'||(name[1] != '.' && name[1] != '\0')){
++              if (!(frame = dx_probe(dentry, NULL, &hinfo, frames, err)))
++                      return NULL;
++      } else {
++              frame = frames;
++              frame->bh = NULL;                       /* for dx_release() */
++              frame->at = (struct dx_entry *)frames;  /* hack for zero entry*/
++              dx_set_block(frame->at, 0);             /* dx_root block is 0 */
++      }
+       hash = hinfo.hash;
+       do {
+               block = dx_get_block(frame->at);
diff --git a/lustre/kernel_patches/patches/ext3-htree-dot-2.6.patch b/lustre/kernel_patches/patches/ext3-htree-dot-2.6.patch
new file mode 100644 (file)
index 0000000..9192112
--- /dev/null
@@ -0,0 +1,23 @@
+Index: linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891/fs/ext3/namei.c
+===================================================================
+--- linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891.orig/fs/ext3/namei.c 2005-04-04 05:06:46.000000000 -0600
++++ linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891/fs/ext3/namei.c      2005-04-04 05:09:18.000000000 -0600
+@@ -926,8 +926,16 @@
+       struct inode *dir = dentry->d_parent->d_inode;
+       sb = dir->i_sb;
+-      if (!(frame = dx_probe(dentry, NULL, &hinfo, frames, err)))
+-              return NULL;
++      /* NFS may look up ".." - look at dx_root directory block */
++      if (namelen > 2 || name[0] != '.'||(name[1] != '.' && name[1] != '\0')){
++              if (!(frame = dx_probe(dentry, NULL, &hinfo, frames, err)))
++                      return NULL;
++      } else {
++              frame = frames;
++              frame->bh = NULL;                       /* for dx_release() */
++              frame->at = (struct dx_entry *)frames;  /* hack for zero entry*/
++              dx_set_block(frame->at, 0);             /* dx_root block is 0 */
++      }
+       hash = hinfo.hash;
+       do {
+               block = dx_get_block(frame->at);
diff --git a/lustre/kernel_patches/patches/ext3-ialloc-2.4.24.patch b/lustre/kernel_patches/patches/ext3-ialloc-2.4.24.patch
new file mode 100644 (file)
index 0000000..83e25fa
--- /dev/null
@@ -0,0 +1,238 @@
+Index: lum/fs/ext3/ialloc.c
+===================================================================
+--- lum.orig/fs/ext3/ialloc.c  2004-08-26 13:14:35.000000000 -0600
++++ lum/fs/ext3/ialloc.c       2004-08-31 15:00:35.000000000 -0600
+@@ -327,8 +327,130 @@ int ext3_itable_block_used(struct super_
+  * directories already is chosen.
+  *
+  * For other inodes, search forward from the parent directory's block
+- * group to find a free inode.
++ * group to find a free inode in a group with some free blocks.
+  */
++static int find_group_dir(struct super_block *sb, const struct inode *parent,
++                        struct ext3_group_desc **best_desc,
++                        struct buffer_head **best_bh)
++{
++      struct ext3_sb_info *sbi = EXT3_SB(sb);
++      int ngroups = sbi->s_groups_count;
++      int avefreei;
++      struct ext3_group_desc *desc;
++      struct buffer_head *bh;
++      int group, best_group = -1, ndir_best = 999999999;
++
++      *best_desc = NULL;
++      *best_bh = NULL;
++
++      avefreei = le32_to_cpu(sbi->s_es->s_free_inodes_count) / ngroups;
++
++      for (group = 0; group < ngroups; group++) {
++              desc = ext3_get_group_desc(sb, group, &bh);
++              if (!desc || !desc->bg_free_inodes_count)
++                      continue;
++              if (le16_to_cpu(desc->bg_free_inodes_count) < avefreei)
++                      continue;
++              if (le16_to_cpu(desc->bg_used_dirs_count) > ndir_best)
++                      continue;
++              if (!*best_desc ||
++                  (le16_to_cpu(desc->bg_free_blocks_count) >
++                   le16_to_cpu((*best_desc)->bg_free_blocks_count))) {
++                      *best_bh = bh;
++                      *best_desc = desc;
++                      best_group = group;
++                      ndir_best = le16_to_cpu(desc->bg_used_dirs_count);
++              }
++      }
++
++      return best_group;
++}
++
++static int find_group_other(struct super_block *sb, const struct inode *parent,
++                          struct ext3_group_desc **best_desc,
++                          struct buffer_head **best_bh)
++{
++      struct ext3_sb_info *sbi = EXT3_SB(sb);
++      int parent_group = EXT3_I(parent)->i_block_group;
++      int ngroups = sbi->s_groups_count;
++      int avefreeb;
++      struct ext3_group_desc *desc;
++      struct buffer_head *bh;
++      int group, i, best_group = -1;
++
++      *best_desc = NULL;
++      *best_bh = NULL;
++
++      /*
++       * Try to place the inode in its parent directory
++       */
++      group = parent_group;
++      desc = ext3_get_group_desc (sb, group, &bh);
++      if (desc && le16_to_cpu(desc->bg_free_inodes_count) &&
++          le16_to_cpu(desc->bg_free_blocks_count)) {
++              *best_desc = desc;
++              *best_bh = bh;
++              return group;
++      }
++
++      /*
++       * We're going to place this inode in a different blockgroup from its
++       * parent.  We want to cause files in a common directory to all land in
++       * the same blockgroup if it has space.  But we want files which are
++       * in a different directory which shares a blockgroup with our parent
++       * to land in a different blockgroup.
++       *
++       * So add our directory's i_ino into the starting point for the hash.
++       */
++      group = (group + parent->i_ino) % ngroups;
++
++      avefreeb = le32_to_cpu(sbi->s_es->s_free_blocks_count) /
++              sbi->s_groups_count / ngroups;
++
++      /*
++       * Use a quadratic hash to find a group with a free inode and some free
++       * blocks.
++       */
++      for (i = 1; i < ngroups; i <<= 1) {
++              group += i;
++              if (group >= ngroups)
++                      group -= ngroups;
++              desc = ext3_get_group_desc(sb, group, &bh);
++              if (!desc || !desc->bg_free_inodes_count)
++                      continue;
++              if (le16_to_cpu(desc->bg_free_blocks_count) > avefreeb) {
++                      *best_bh = bh;
++                      *best_desc = desc;
++                      return group;
++              }
++      }
++
++      /*
++       * That failed: try linear search for a group with free inodes and
++       * preferrably free blocks, returning as soon as we find a good one.
++       */
++      group = sbi->s_last_group;
++      for (i = 0; i < ngroups; i++) {
++              if (++group >= ngroups)
++                      group = 0;
++              desc = ext3_get_group_desc(sb, group, &bh);
++              if (!desc || !desc->bg_free_inodes_count)
++                      continue;
++              if (!*best_desc ||
++                  (le16_to_cpu(desc->bg_free_blocks_count) >
++                   le16_to_cpu((*best_desc)->bg_free_blocks_count))) {
++                      *best_bh = bh;
++                      *best_desc = desc;
++                      best_group = group;
++                      if (le16_to_cpu(desc->bg_free_blocks_count) >= avefreeb)
++                              break;
++              }
++      }
++      sbi->s_last_group = best_group;
++
++      return best_group;
++}
++
+ struct inode * ext3_new_inode (handle_t *handle,
+                               const struct inode * dir, int mode,
+                               unsigned long goal)
+@@ -336,11 +459,10 @@ struct inode * ext3_new_inode (handle_t 
+       struct super_block * sb;
+       struct buffer_head * bh;
+       struct buffer_head * bh2;
+-      int i, j, avefreei;
++      int i, j;
+       struct inode * inode;
+       int bitmap_nr;
+       struct ext3_group_desc * gdp;
+-      struct ext3_group_desc * tmp;
+       struct ext3_super_block * es;
+       struct ext3_iloc iloc;
+       int err = 0;
+@@ -392,72 +514,10 @@ struct inode * ext3_new_inode (handle_t 
+       }
+ repeat:
+-      gdp = NULL;
+-      i = 0;
+-
+-      if (S_ISDIR(mode)) {
+-              avefreei = le32_to_cpu(es->s_free_inodes_count) /
+-                      sb->u.ext3_sb.s_groups_count;
+-              if (!gdp) {
+-                      for (j = 0; j < sb->u.ext3_sb.s_groups_count; j++) {
+-                              struct buffer_head *temp_buffer;
+-                              tmp = ext3_get_group_desc (sb, j, &temp_buffer);
+-                              if (tmp &&
+-                                  le16_to_cpu(tmp->bg_free_inodes_count) &&
+-                                  le16_to_cpu(tmp->bg_free_inodes_count) >=
+-                                                      avefreei) {
+-                                      if (!gdp || (le16_to_cpu(tmp->bg_free_blocks_count) >
+-                                              le16_to_cpu(gdp->bg_free_blocks_count))) {
+-                                              i = j;
+-                                              gdp = tmp;
+-                                              bh2 = temp_buffer;
+-                                      }
+-                              }
+-                      }
+-              }
+-      } else {
+-              /*
+-               * Try to place the inode in its parent directory
+-               */
+-              i = dir->u.ext3_i.i_block_group;
+-              tmp = ext3_get_group_desc (sb, i, &bh2);
+-              if (tmp && le16_to_cpu(tmp->bg_free_inodes_count))
+-                      gdp = tmp;
+-              else
+-              {
+-                      /*
+-                       * Use a quadratic hash to find a group with a
+-                       * free inode
+-                       */
+-                      for (j = 1; j < sb->u.ext3_sb.s_groups_count; j <<= 1) {
+-                              i += j;
+-                              if (i >= sb->u.ext3_sb.s_groups_count)
+-                                      i -= sb->u.ext3_sb.s_groups_count;
+-                              tmp = ext3_get_group_desc (sb, i, &bh2);
+-                              if (tmp &&
+-                                  le16_to_cpu(tmp->bg_free_inodes_count)) {
+-                                      gdp = tmp;
+-                                      break;
+-                              }
+-                      }
+-              }
+-              if (!gdp) {
+-                      /*
+-                       * That failed: try linear search for a free inode
+-                       */
+-                      i = dir->u.ext3_i.i_block_group + 1;
+-                      for (j = 2; j < sb->u.ext3_sb.s_groups_count; j++) {
+-                              if (++i >= sb->u.ext3_sb.s_groups_count)
+-                                      i = 0;
+-                              tmp = ext3_get_group_desc (sb, i, &bh2);
+-                              if (tmp &&
+-                                  le16_to_cpu(tmp->bg_free_inodes_count)) {
+-                                      gdp = tmp;
+-                                      break;
+-                              }
+-                      }
+-              }
+-      }
++      if (S_ISDIR(mode))
++              i = find_group_dir(sb, dir, &gdp, &bh2);
++      else
++              i = find_group_other(sb, dir, &gdp, &bh2);
+       err = -ENOSPC;
+       if (!gdp)
+Index: lum/include/linux/ext3_fs_sb.h
+===================================================================
+--- lum.orig/include/linux/ext3_fs_sb.h        2004-08-26 13:28:53.000000000 -0600
++++ lum/include/linux/ext3_fs_sb.h     2004-08-31 11:04:27.000000000 -0600
+@@ -45,6 +45,7 @@ struct ext3_sb_info {
+       unsigned long s_gdb_count;      /* Number of group descriptor blocks */
+       unsigned long s_desc_per_block; /* Number of group descriptors per block */
+       unsigned long s_groups_count;   /* Number of groups in the fs */
++      unsigned long s_last_group;     /* Last group used for inode allocation */
+       struct buffer_head * s_sbh;     /* Buffer containing the super block */
+       struct ext3_super_block * s_es; /* Pointer to the super block in the buffer */
+       struct buffer_head ** s_group_desc;
diff --git a/lustre/kernel_patches/patches/ext3-ialloc-2.6.patch b/lustre/kernel_patches/patches/ext3-ialloc-2.6.patch
new file mode 100644 (file)
index 0000000..15d37a9
--- /dev/null
@@ -0,0 +1,128 @@
+Index: linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891/fs/ext3/ialloc.c
+===================================================================
+--- linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891.orig/fs/ext3/ialloc.c        2005-05-16 14:10:54.000000000 -0600
++++ linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891/fs/ext3/ialloc.c     2005-05-16 14:18:29.000000000 -0600
+@@ -352,13 +352,17 @@
+       return -1;
+ }
+-static int find_group_other(struct super_block *sb, struct inode *parent)
++static int find_group_other(struct super_block *sb, struct inode *parent,
++                          int mode)
+ {
+       int parent_group = EXT3_I(parent)->i_block_group;
++      struct ext3_sb_info *sbi = EXT3_SB(sb);
+       int ngroups = EXT3_SB(sb)->s_groups_count;
+       struct ext3_group_desc *desc;
+       struct buffer_head *bh;
+       int group, i;
++      int best_group = -1;
++      int avefreeb, freeb, best_group_freeb = 0;
+       /*
+        * Try to place the inode in its parent directory
+@@ -366,9 +370,9 @@
+       group = parent_group;
+       desc = ext3_get_group_desc (sb, group, &bh);
+       if (desc && le16_to_cpu(desc->bg_free_inodes_count) &&
+-                      le16_to_cpu(desc->bg_free_blocks_count))
++          (!S_ISREG(mode) || le16_to_cpu(desc->bg_free_blocks_count)))
+               return group;
+-
++      avefreeb = le32_to_cpu(sbi->s_es->s_free_blocks_count) / ngroups;
+       /*
+        * We're going to place this inode in a different blockgroup from its
+        * parent.  We want to cause files in a common directory to all land in
+@@ -381,33 +385,47 @@
+       group = (group + parent->i_ino) % ngroups;
+       /*
+-       * Use a quadratic hash to find a group with a free inode and some free
+-       * blocks.
++       * Use a quadratic hash to find a group with a free inode and
++       * average number of free blocks.
+        */
+       for (i = 1; i < ngroups; i <<= 1) {
+               group += i;
+               if (group >= ngroups)
+                       group -= ngroups;
+               desc = ext3_get_group_desc (sb, group, &bh);
+-              if (desc && le16_to_cpu(desc->bg_free_inodes_count) &&
+-                              le16_to_cpu(desc->bg_free_blocks_count))
++              if (!desc || !desc->bg_free_inodes_count)
++                      continue;
++              if (!S_ISREG(mode))
++                      return group;
++              if (le16_to_cpu(desc->bg_free_blocks_count) >= avefreeb)
+                       return group;
+       }
+       /*
+-       * That failed: try linear search for a free inode, even if that group
+-       * has no free blocks.
++       * That failed: start from last group used to allocate inode
++       * try linear search for a free inode and prefereably
++       * free blocks.
+        */
+-      group = parent_group;
++      group = sbi->s_last_alloc_group;
++      if (group == -1)
++              group = parent_group;
++
+       for (i = 0; i < ngroups; i++) {
+               if (++group >= ngroups)
+                       group = 0;
+               desc = ext3_get_group_desc (sb, group, &bh);
+-              if (desc && le16_to_cpu(desc->bg_free_inodes_count))
+-                      return group;
++              if (!desc || !desc->bg_free_inodes_count)
++                      continue;
++              freeb = le16_to_cpu(desc->bg_free_blocks_count);
++              if (freeb > best_group_freeb) {
++                      best_group_freeb = freeb;
++                      best_group = group;
++                      if (freeb >= avefreeb || !S_ISREG(mode))
++                              break;
++              }
+       }
+-
+-      return -1;
++      sbi->s_last_alloc_group = best_group;
++      return best_group;
+ }
+ /*
+@@ -454,7 +472,7 @@
+               else
+                       group = find_group_orlov(sb, dir);
+       } else 
+-              group = find_group_other(sb, dir);
++              group = find_group_other(sb, dir, mode);
+       err = -ENOSPC;
+       if (group == -1)
+Index: linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891/fs/ext3/super.c
+===================================================================
+--- linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891.orig/fs/ext3/super.c 2005-05-16 14:10:54.000000000 -0600
++++ linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891/fs/ext3/super.c      2005-05-16 14:17:14.000000000 -0600
+@@ -1297,6 +1297,7 @@
+       percpu_counter_init(&sbi->s_dirs_counter);
+       bgl_lock_init(&sbi->s_blockgroup_lock);
++      sbi->s_last_alloc_group = -1;
+       for (i = 0; i < db_count; i++) {
+               block = descriptor_loc(sb, logic_sb_block, i);
+               sbi->s_group_desc[i] = sb_bread(sb, block);
+Index: linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891/include/linux/ext3_fs_sb.h
+===================================================================
+--- linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891.orig/include/linux/ext3_fs_sb.h      2005-05-16 14:10:54.000000000 -0600
++++ linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891/include/linux/ext3_fs_sb.h   2005-05-16 14:17:14.000000000 -0600
+@@ -59,6 +59,8 @@
+       struct percpu_counter s_freeinodes_counter;
+       struct percpu_counter s_dirs_counter;
+       struct blockgroup_lock s_blockgroup_lock;
++      /* Last group used to allocate inode */
++      int s_last_alloc_group;
+       /* root of the per fs reservation window tree */
+       spinlock_t s_rsv_window_lock;
index 4de2b32..d38fb54 100644 (file)
@@ -2,7 +2,7 @@ Index: linux-2.6.5-suse/fs/ext3/mballoc.c
 ===================================================================
 --- linux-2.6.5-suse.orig/fs/ext3/mballoc.c    2005-03-02 22:42:20.659360368 +0300
 +++ linux-2.6.5-suse/fs/ext3/mballoc.c 2005-03-11 16:13:13.000000000 +0300
-@@ -0,0 +1,1863 @@
+@@ -0,0 +1,1864 @@
 +/*
 + * Copyright(c) 2003, 2004, 2005, Cluster File Systems, Inc, info@clusterfs.com
 + * Written by Alex Tomas <alex@clusterfs.com>
@@ -932,8 +932,9 @@ Index: linux-2.6.5-suse/fs/ext3/mballoc.c
 +               * We've been searching too long. Let's try to allocate
 +               * the best chunk we've found so far
 +               */
-+              printk(KERN_ERR "EXT3-fs: too long searching (%d/%d)\n",
-+                              ac.ac_b_ex.fe_len, ac.ac_g_ex.fe_len);
++              ext3_warning(inode->i_sb, __FUNCTION__,
++                           "too long searching: got %d want %d\n",
++                           ac.ac_b_ex.fe_len, ac.ac_g_ex.fe_len);
 +              ext3_mb_try_best_found(&ac, &e3b);
 +              if (ac.ac_status != AC_STATUS_FOUND) {
 +                      /*
index dc6ffe3..24b2595 100644 (file)
@@ -2,7 +2,7 @@ Index: linux-stage/fs/ext3/mballoc.c
 ===================================================================
 --- linux-stage.orig/fs/ext3/mballoc.c 2005-02-25 17:28:41.836311072 +0200
 +++ linux-stage/fs/ext3/mballoc.c      2005-02-25 17:28:41.859307576 +0200
-@@ -0,0 +1,1860 @@
+@@ -0,0 +1,1861 @@
 +/*
 + * Copyright (c) 2003, Cluster File Systems, Inc, info@clusterfs.com
 + * Written by Alex Tomas <alex@clusterfs.com>
@@ -932,8 +932,9 @@ Index: linux-stage/fs/ext3/mballoc.c
 +               * We've been searching too long. Let's try to allocate
 +               * the best chunk we've found so far
 +               */
-+              printk(KERN_ERR "EXT3-fs: too long searching (%d/%d)\n",
-+                              ac.ac_b_ex.fe_len, ac.ac_g_ex.fe_len);
++              ext3_warning(inode->i_sb, __FUNCTION__,
++                           "too long searching: got %d want %d\n",
++                           ac.ac_b_ex.fe_len, ac.ac_g_ex.fe_len);
 +              ext3_mb_try_best_found(&ac, &e3b);
 +              if (ac.ac_status != AC_STATUS_FOUND) {
 +                      /*
diff --git a/lustre/kernel_patches/patches/fsprivate-2.4-suse.patch b/lustre/kernel_patches/patches/fsprivate-2.4-suse.patch
new file mode 100644 (file)
index 0000000..fc3a72a
--- /dev/null
@@ -0,0 +1,10 @@
+--- uml-2.4.24/include/linux/fs.h.orig 2005-05-18 21:27:58.120742112 +0300
++++ uml-2.4.24/include/linux/fs.h      2005-05-18 22:48:12.081908776 +0300
+@@ -592,6 +592,7 @@
+       /* needed for tty driver, and maybe others */
+       void                    *private_data;
+       struct lookup_intent    *f_it;
++      void                    *fs_private;
+       struct list_head        f_ep_links;
+       spinlock_t              f_ep_lock;
+ };
diff --git a/lustre/kernel_patches/patches/fsprivate-2.4.patch b/lustre/kernel_patches/patches/fsprivate-2.4.patch
new file mode 100644 (file)
index 0000000..4e83dbe
--- /dev/null
@@ -0,0 +1,10 @@
+--- uml-2.4.24/include/linux/fs.h.orig 2005-05-18 21:27:58.120742112 +0300
++++ uml-2.4.24/include/linux/fs.h      2005-05-18 22:48:12.081908776 +0300
+@@ -592,6 +592,7 @@
+       /* needed for tty driver, and maybe others */
+       void                    *private_data;
+       struct lookup_intent    *f_it;
++      void                    *fs_private;
+       /* preallocated helper kiobuf to speedup O_DIRECT */
+       struct kiobuf           *f_iobuf;
diff --git a/lustre/kernel_patches/patches/fsprivate-2.6.patch b/lustre/kernel_patches/patches/fsprivate-2.6.patch
new file mode 100644 (file)
index 0000000..a5c3d48
--- /dev/null
@@ -0,0 +1,10 @@
+--- linux-2.6.5-7.141/include/linux/fs.h.orig  2005-02-01 23:56:07.000000000 +0200
++++ linux-2.6.5-7.141/include/linux/fs.h       2005-05-18 23:23:15.486142728 +0300
+@@ -574,6 +574,7 @@
+ #endif /* #ifdef CONFIG_EPOLL */
+       struct address_space    *f_mapping;
+       struct lookup_intent    *f_it;
++      void                    *fs_private;
+ };
+ extern spinlock_t files_lock;
+ #define file_list_lock() spin_lock(&files_lock);
diff --git a/lustre/kernel_patches/patches/gfp_debug-2.4.21-rhel.patch b/lustre/kernel_patches/patches/gfp_debug-2.4.21-rhel.patch
new file mode 100644 (file)
index 0000000..6686b15
--- /dev/null
@@ -0,0 +1,77 @@
+Index: linux-2.4.21-rhel/kernel/sysctl.c
+===================================================================
+--- linux-2.4.21-rhel.orig/kernel/sysctl.c     2005-01-20 18:52:39.000000000 -0700
++++ linux-2.4.21-rhel/kernel/sysctl.c  2005-05-31 18:47:22.000000000 -0600
+@@ -355,6 +355,8 @@
+ extern int skip_mapped_pages;
+ static ctl_table vm_table[] = {
++      {VM_GFP_DEBUG, "vm_gfp_debug",
++       &vm_gfp_debug, sizeof(int), 0644, NULL, &proc_dointvec},
+       {VM_BDFLUSH, "bdflush", &bdf_prm, 9*sizeof(int), 0644, NULL,
+        &proc_dointvec_minmax, &sysctl_intvec, NULL,
+        &bdflush_min, &bdflush_max},
+Index: linux-2.4.21-rhel/mm/page_alloc.c
+===================================================================
+--- linux-2.4.21-rhel.orig/mm/page_alloc.c     2005-05-31 18:29:37.000000000 -0600
++++ linux-2.4.21-rhel/mm/page_alloc.c  2005-05-31 18:49:27.000000000 -0600
+@@ -58,6 +58,8 @@
+ static int zone_extrafree_max[MAX_NR_ZONES] __initdata = { 1024 , 1024, 0, };
+ #endif
++int vm_gfp_debug = 0;
++
+ /*
+  * Temporary debugging check.
+  */
+@@ -773,8 +775,12 @@
+       }
+ out_failed:
+-      /* No luck.. */
+-//    printk(KERN_ERR "__alloc_pages: %lu-order allocation failed.\n", order);
++      if (!(gfp_mask & __GFP_NOWARN)) {
++              printk(KERN_ERR "__alloc_pages: %lu-order allocation failed (gfp=%#x/%i).\n",
++                     order, gfp_mask, !!(current->flags & PF_MEMALLOC));
++              if (unlikely(vm_gfp_debug))
++                      dump_stack();
++      }
+       return NULL;
+ }
+Index: linux-2.4.21-rhel/include/linux/sysctl.h
+===================================================================
+--- linux-2.4.21-rhel.orig/include/linux/sysctl.h      2005-03-18 18:25:26.000000000 -0700
++++ linux-2.4.21-rhel/include/linux/sysctl.h   2005-05-31 18:48:17.000000000 -0600
+@@ -153,6 +153,7 @@
+       VM_MIN_READAHEAD=12,    /* Min file readahead */
+       VM_MAX_READAHEAD=13,    /* Max file readahead */
+       VM_OVERCOMMIT_RATIO=16, /* percent of RAM to allow overcommit in */
++      VM_GFP_DEBUG=18,        /* debug GFP failures */
+       VM_PAGEBUF=22,          /* struct: Control pagebuf parameters */
+       VM_HUGETLB_POOL=23,     /* int: size of the hugetlb pool, in MB */
+       VM_DCACHE_PRIORITY=24,  /* int: priority of the dcache pool */
+Index: linux-2.4.21-rhel/include/linux/mm.h
+===================================================================
+--- linux-2.4.21-rhel.orig/include/linux/mm.h  2005-05-31 18:50:32.000000000 -0600
++++ linux-2.4.21-rhel/include/linux/mm.h       2005-05-31 18:50:45.000000000 -0600
+@@ -846,6 +846,7 @@
+ #define __GFP_HIGHIO  0x80    /* Can start high mem physical IO? */
+ #define __GFP_FS      0x100   /* Can call down to low-level FS? */
+ #define __GFP_WIRED   0x200   /* Highmem bias and wired */
++#define __GFP_NOWARN  0x400   /* Don't report error on allocation failure */
+ #define GFP_NOHIGHIO  (__GFP_HIGH | __GFP_WAIT | __GFP_IO)
+ #define GFP_NOIO      (__GFP_HIGH | __GFP_WAIT)
+Index: linux-2.4.21-rhel/include/linux/swap.h
+===================================================================
+--- linux-2.4.21-rhel.orig/include/linux/swap.h        2005-03-18 18:25:26.000000000 -0700
++++ linux-2.4.21-rhel/include/linux/swap.h     2005-05-31 18:52:44.000000000 -0600
+@@ -178,6 +178,7 @@
+ extern int rebalance_inactive(int);
+ extern void wakeup_kswapd(unsigned int);
+ extern void rss_free_pages(unsigned int);
++extern int vm_gfp_debug;
+ /*
+  * Limits, in percent, on how large the cache can be and how to do
diff --git a/lustre/kernel_patches/patches/grab_cache_page_nowait_gfp-2.4.21-suse2.patch b/lustre/kernel_patches/patches/grab_cache_page_nowait_gfp-2.4.21-suse2.patch
new file mode 100644 (file)
index 0000000..94c50d0
--- /dev/null
@@ -0,0 +1,85 @@
+Index: linux-2.4.21-suse2/mm/filemap.c
+===================================================================
+--- linux-2.4.21-suse2.orig/mm/filemap.c       2005-04-04 05:58:21.000000000 -0600
++++ linux-2.4.21-suse2/mm/filemap.c    2005-04-04 06:18:57.000000000 -0600
+@@ -1022,6 +1022,14 @@
+  */
+ struct page *grab_cache_page_nowait(struct address_space *mapping, unsigned long index)
+ {
++      return grab_cache_page_nowait_gfp(mapping, index, mapping->gfp_mask);
++}
++
++
++struct page *grab_cache_page_nowait_gfp(struct address_space *mapping,
++                                      unsigned long index, 
++                                      unsigned int gfp_mask)
++{
+       struct page *page, **hash;
+       hash = page_hash(mapping, index);
+@@ -1046,7 +1054,7 @@
+               }
+       }
+-      page = page_cache_alloc(mapping);
++      page = alloc_pages(gfp_mask, 0);
+       if ( unlikely(!page) )
+               return NULL;    /* Failed to allocate a page */
+Index: linux-2.4.21-suse2/mm/page_alloc.c
+===================================================================
+--- linux-2.4.21-suse2.orig/mm/page_alloc.c    2005-04-04 05:58:04.000000000 -0600
++++ linux-2.4.21-suse2/mm/page_alloc.c 2005-04-04 06:12:11.000000000 -0600
+@@ -435,7 +435,7 @@
+                       break;
+               min = z->watermarks[class_idx].min;
+-              if (!(gfp_mask & __GFP_WAIT))
++              if (!(gfp_mask & __GFP_WAIT) && (gfp_mask & __GFP_HIGH))
+                       min >>= 2;
+               else if (current->rt_priority)
+                       min >>= 1;
+@@ -504,6 +504,7 @@
+       }
+  out:
++      if (!(gfp_mask & __GFP_NOWARN))
+       printk(KERN_NOTICE "__alloc_pages: %u-order allocation failed (gfp=0x%x/%i)\n",
+              order, gfp_mask, !!(current->flags & PF_MEMALLOC));
+       if (unlikely(vm_gfp_debug))
+Index: linux-2.4.21-suse2/kernel/ksyms.c
+===================================================================
+--- linux-2.4.21-suse2.orig/kernel/ksyms.c     2005-04-04 06:01:39.000000000 -0600
++++ linux-2.4.21-suse2/kernel/ksyms.c  2005-04-04 06:01:43.000000000 -0600
+@@ -326,6 +326,7 @@
+ EXPORT_SYMBOL(__find_lock_page);
+ EXPORT_SYMBOL(find_or_create_page);
+ EXPORT_SYMBOL(grab_cache_page_nowait);
++EXPORT_SYMBOL(grab_cache_page_nowait_gfp);
+ EXPORT_SYMBOL(read_cache_page);
+ EXPORT_SYMBOL(set_page_dirty);
+ EXPORT_SYMBOL(vfs_readlink);
+Index: linux-2.4.21-suse2/include/linux/pagemap.h
+===================================================================
+--- linux-2.4.21-suse2.orig/include/linux/pagemap.h    2003-11-10 17:44:33.000000000 -0700
++++ linux-2.4.21-suse2/include/linux/pagemap.h 2005-04-04 06:01:43.000000000 -0600
+@@ -110,6 +110,7 @@
+ extern struct page * grab_cache_page_nowait (struct address_space *, unsigned long);
++extern struct page * grab_cache_page_nowait_gfp (struct address_space *, unsigned long, unsigned int);
+ typedef int filler_t(void *, struct page*);
+Index: linux-2.4.21-suse2/include/linux/mm.h
+===================================================================
+--- linux-2.4.21-suse2.orig/include/linux/mm.h 2005-04-04 05:58:14.000000000 -0600
++++ linux-2.4.21-suse2/include/linux/mm.h      2005-04-04 06:17:10.000000000 -0600
+@@ -661,6 +661,7 @@
+ #define __GFP_IO      0x40    /* Can start low memory physical IO? */
+ #define __GFP_HIGHIO  0x80    /* Can start high mem physical IO? */
+ #define __GFP_FS      0x100   /* Can call down to low-level FS? */
++#define __GFP_NOWARN  0x200   /* Don't warn on allocation failure */
+ #define GFP_NOHIGHIO  (__GFP_HIGH | __GFP_WAIT | __GFP_IO)
+ #define GFP_NOIO      (__GFP_HIGH | __GFP_WAIT)
diff --git a/lustre/kernel_patches/patches/invalidate_show-2.4.29.patch b/lustre/kernel_patches/patches/invalidate_show-2.4.29.patch
new file mode 100644 (file)
index 0000000..dedc90b
--- /dev/null
@@ -0,0 +1,107 @@
+Index: linux-2.4.29/fs/inode.c
+===================================================================
+--- linux-2.4.29.orig/fs/inode.c       2005-04-07 18:55:16.732416736 +0300
++++ linux-2.4.29/fs/inode.c    2005-04-07 19:16:46.772300864 +0300
+@@ -670,7 +670,8 @@
+ /*
+  * Invalidate all inodes for a device.
+  */
+-static int invalidate_list(struct list_head *head, struct super_block * sb, struct list_head * dispose)
++static int invalidate_list(struct list_head *head, struct super_block * sb,
++                         struct list_head * dispose, int show)
+ {
+       struct list_head *next;
+       int busy = 0, count = 0;
+@@ -695,6 +696,11 @@
+                       count++;
+                       continue;
+               }
++              if (show)
++                      printk(KERN_ERR
++                             "inode busy: dev %s:%lu (%p) mode %o count %u\n",
++                             kdevname(sb->s_dev), inode->i_ino, inode,
++                             inode->i_mode, atomic_read(&inode->i_count));
+               busy = 1;
+       }
+       /* only unused inodes may be cached with i_count zero */
+@@ -719,17 +725,17 @@
+  *    If the discard is successful all the inodes have been discarded.
+  */
+  
+-int invalidate_inodes(struct super_block * sb)
++int invalidate_inodes(struct super_block * sb, int show)
+ {
+       int busy;
+       LIST_HEAD(throw_away);
+       spin_lock(&inode_lock);
+-      busy = invalidate_list(&inode_in_use, sb, &throw_away);
+-      busy |= invalidate_list(&inode_unused, sb, &throw_away);
+-      busy |= invalidate_list(&inode_unused_pagecache, sb, &throw_away);
+-      busy |= invalidate_list(&sb->s_dirty, sb, &throw_away);
+-      busy |= invalidate_list(&sb->s_locked_inodes, sb, &throw_away);
++      busy = invalidate_list(&inode_in_use, sb, &throw_away, show);
++      busy |= invalidate_list(&inode_unused, sb, &throw_away, show);
++      busy |= invalidate_list(&inode_unused_pagecache, sb, &throw_away, show);
++      busy |= invalidate_list(&sb->s_dirty, sb, &throw_away, show);
++      busy |= invalidate_list(&sb->s_locked_inodes, sb, &throw_away, show);
+       spin_unlock(&inode_lock);
+       dispose_list(&throw_away);
+@@ -755,7 +761,7 @@
+                * hold).
+                */
+               shrink_dcache_sb(sb);
+-              res = invalidate_inodes(sb);
++              res = invalidate_inodes(sb, 0);
+               drop_super(sb);
+       }
+       invalidate_buffers(dev);
+Index: linux-2.4.29/fs/super.c
+===================================================================
+--- linux-2.4.29.orig/fs/super.c       2005-04-07 18:53:30.978493776 +0300
++++ linux-2.4.29/fs/super.c    2005-04-07 19:14:26.187672976 +0300
+@@ -844,7 +844,7 @@
+       lock_super(sb);
+       lock_kernel();
+       sb->s_flags &= ~MS_ACTIVE;
+-      invalidate_inodes(sb);  /* bad name - it should be evict_inodes() */
++      invalidate_inodes(sb, 0);  /* bad name - it should be evict_inodes() */
+       if (sop) {
+               if (sop->write_super && sb->s_dirt)
+                       sop->write_super(sb);
+@@ -853,7 +853,7 @@
+       }
+       /* Forget any remaining inodes */
+-      if (invalidate_inodes(sb)) {
++      if (invalidate_inodes(sb, 1)) {
+               printk(KERN_ERR "VFS: Busy inodes after unmount. "
+                       "Self-destruct in 5 seconds.  Have a nice day...\n");
+       }
+Index: linux-2.4.29/include/linux/fs.h
+===================================================================
+--- linux-2.4.29.orig/include/linux/fs.h       2005-04-07 19:14:06.319693368 +0300
++++ linux-2.4.29/include/linux/fs.h    2005-04-07 19:14:26.190672520 +0300
+@@ -1286,7 +1286,7 @@
+ extern int get_buffer_flushtime(void);
+ extern void balance_dirty(void);
+ extern int check_disk_change(kdev_t);
+-extern int invalidate_inodes(struct super_block *);
++extern int invalidate_inodes(struct super_block *, int);
+ extern int invalidate_device(kdev_t, int);
+ extern void invalidate_inode_pages(struct inode *);
+ extern void invalidate_inode_pages2(struct address_space *);
+Index: linux-2.4.29/fs/smbfs/inode.c
+===================================================================
+--- linux-2.4.29.orig/fs/smbfs/inode.c 2005-04-07 18:52:37.889564520 +0300
++++ linux-2.4.29/fs/smbfs/inode.c      2005-04-07 19:14:26.192672216 +0300
+@@ -175,7 +175,7 @@
+ {
+       VERBOSE("\n");
+       shrink_dcache_sb(SB_of(server));
+-      invalidate_inodes(SB_of(server));
++      invalidate_inodes(SB_of(server), 0);
+ }
+ /*
diff --git a/lustre/kernel_patches/patches/kallsyms-2.4.29.patch b/lustre/kernel_patches/patches/kallsyms-2.4.29.patch
new file mode 100644 (file)
index 0000000..d270aa4
--- /dev/null
@@ -0,0 +1,689 @@
+Index: linux-2.4.29/arch/arm/vmlinux-armo.lds.in
+===================================================================
+--- linux-2.4.29.orig/arch/arm/vmlinux-armo.lds.in     2005-05-08 23:06:26.916055656 +0300
++++ linux-2.4.29/arch/arm/vmlinux-armo.lds.in  2005-05-08 23:07:11.214321296 +0300
+@@ -62,6 +62,10 @@
+                       *(__ksymtab)
+               __stop___ksymtab = .;
++              __start___kallsyms = .; /* All kernel symbols */
++                      *(__kallsyms)
++              __stop___kallsyms = .;
++
+               *(.got)                 /* Global offset table          */
+               _etext = .;             /* End of text section          */
+Index: linux-2.4.29/arch/arm/vmlinux-armv.lds.in
+===================================================================
+--- linux-2.4.29.orig/arch/arm/vmlinux-armv.lds.in     2005-05-08 23:06:26.917055504 +0300
++++ linux-2.4.29/arch/arm/vmlinux-armv.lds.in  2005-05-08 23:07:11.215321144 +0300
+@@ -67,6 +67,12 @@
+               __stop___ksymtab = .;
+       }
++      __kallsyms : {                  /* Kernel debugging table       */
++              __start___kallsyms = .; /* All kernel symbols */
++                      *(__kallsyms)
++              __stop___kallsyms = .;
++      }
++
+       . = ALIGN(8192);
+       .data : {
+Index: linux-2.4.29/arch/ppc/config.in
+===================================================================
+--- linux-2.4.29.orig/arch/ppc/config.in       2005-05-08 23:06:26.933053072 +0300
++++ linux-2.4.29/arch/ppc/config.in    2005-05-08 23:07:11.216320992 +0300
+@@ -655,6 +655,7 @@
+     fi
+   fi
+ fi
++bool 'Load all symbols for debugging/kksymoops' CONFIG_KALLSYMS
+ if [ "$CONFIG_ALL_PPC" = "y" ]; then
+   bool 'Support for early boot text console (BootX or OpenFirmware only)' CONFIG_BOOTX_TEXT
+Index: linux-2.4.29/arch/ppc/vmlinux.lds
+===================================================================
+--- linux-2.4.29.orig/arch/ppc/vmlinux.lds     2005-05-08 23:06:26.934052920 +0300
++++ linux-2.4.29/arch/ppc/vmlinux.lds  2005-05-08 23:07:11.217320840 +0300
+@@ -74,6 +74,10 @@
+   __ksymtab : { *(__ksymtab) }
+   __stop___ksymtab = .;
++  __start___kallsyms = .;     /* All kernel symbols */
++  __kallsyms : { *(__kallsyms) }
++  __stop___kallsyms = .;
++
+   . = ALIGN(8);
+   __start___ftr_fixup = .;
+   __ftr_fixup : { *(__ftr_fixup) }
+Index: linux-2.4.29/arch/i386/config.in
+===================================================================
+--- linux-2.4.29.orig/arch/i386/config.in      2005-05-08 23:07:09.946514032 +0300
++++ linux-2.4.29/arch/i386/config.in   2005-05-08 23:33:00.395809912 +0300
+@@ -512,6 +512,7 @@
+    bool '  Magic SysRq key' CONFIG_MAGIC_SYSRQ
+    bool '  Spinlock debugging' CONFIG_DEBUG_SPINLOCK
+    bool '  Compile the kernel with frame pointers' CONFIG_FRAME_POINTER
++   bool '  Load all symbols for debugging/kksymoops' CONFIG_KALLSYMS
+ fi
+ int 'Kernel messages buffer length shift (0 = default)' CONFIG_LOG_BUF_SHIFT 0
+Index: linux-2.4.29/arch/ia64/config.in
+===================================================================
+--- linux-2.4.29.orig/arch/ia64/config.in      2005-05-08 23:06:26.936052616 +0300
++++ linux-2.4.29/arch/ia64/config.in   2005-05-08 23:07:11.219320536 +0300
+@@ -318,4 +318,6 @@
+ int 'Kernel messages buffer length shift (0 = default)' CONFIG_LOG_BUF_SHIFT 0
++bool '  Load all symbols for debugging/kksymoops' CONFIG_KALLSYMS
++
+ endmenu
+Index: linux-2.4.29/arch/alpha/vmlinux.lds.in
+===================================================================
+--- linux-2.4.29.orig/arch/alpha/vmlinux.lds.in        2005-05-08 23:06:26.937052464 +0300
++++ linux-2.4.29/arch/alpha/vmlinux.lds.in     2005-05-08 23:07:11.220320384 +0300
+@@ -28,6 +28,10 @@
+   __stop___ksymtab = .;
+   .kstrtab : { *(.kstrtab) }
++  __start___kallsyms = .;       /* All kernel symbols */
++  __kallsyms : { *(__kallsyms) }
++  __stop___kallsyms = .;
++
+   /* Startup code */
+   . = ALIGN(8192);
+   __init_begin = .;
+Index: linux-2.4.29/Makefile
+===================================================================
+--- linux-2.4.29.orig/Makefile 2005-05-08 22:59:19.203077912 +0300
++++ linux-2.4.29/Makefile      2005-05-08 23:07:11.222320080 +0300
+@@ -37,6 +37,7 @@
+ MAKEFILES     = $(TOPDIR)/.config
+ GENKSYMS      = /sbin/genksyms
+ DEPMOD                = /sbin/depmod
++KALLSYMS      = /sbin/kallsyms
+ MODFLAGS      = -DMODULE
+ CFLAGS_KERNEL =
+ PERL          = perl
+@@ -44,6 +45,8 @@
+ RPM           := $(shell if [ -x "/usr/bin/rpmbuild" ]; then echo rpmbuild; \
+                       else echo rpm; fi)
++TMPPREFIX     = 
++
+ export        VERSION PATCHLEVEL SUBLEVEL EXTRAVERSION KERNELRELEASE ARCH \
+       CONFIG_SHELL TOPDIR HPATH HOSTCC HOSTCFLAGS CROSS_COMPILE AS LD CC \
+       CPP AR NM STRIP OBJCOPY OBJDUMP MAKE MAKEFILES GENKSYMS MODFLAGS PERL AWK
+@@ -202,7 +205,7 @@
+ CLEAN_FILES = \
+       kernel/ksyms.lst include/linux/compile.h \
+       vmlinux System.map \
+-      .tmp* \
++      $(TMPPREFIX).tmp* \
+       drivers/char/consolemap_deftbl.c drivers/video/promcon_tbl.c \
+       drivers/char/conmakehash \
+       drivers/char/drm/*-mod.c \
+@@ -285,16 +288,39 @@
+ boot: vmlinux
+       @$(MAKE) CFLAGS="$(CFLAGS) $(CFLAGS_KERNEL)" -C arch/$(ARCH)/boot
++LD_VMLINUX    := $(LD) $(LINKFLAGS) $(HEAD) init/main.o init/version.o init/do_mounts.o \
++                      --start-group \
++                      $(CORE_FILES) \
++                      $(DRIVERS) \
++                      $(NETWORKS) \
++                      $(LIBS) \
++                      --end-group
++ifeq ($(CONFIG_KALLSYMS),y)
++LD_VMLINUX_KALLSYMS   := $(TMPPREFIX).tmp_kallsyms3.o
++else
++LD_VMLINUX_KALLSYMS   :=
++endif
++
+ vmlinux: include/linux/version.h $(CONFIGURATION) init/main.o init/version.o init/do_mounts.o linuxsubdirs
+-      $(LD) $(LINKFLAGS) $(HEAD) init/main.o init/version.o init/do_mounts.o \
+-              --start-group \
+-              $(CORE_FILES) \
+-              $(DRIVERS) \
+-              $(NETWORKS) \
+-              $(LIBS) \
+-              --end-group \
+-              -o vmlinux
++      @$(MAKE) CFLAGS="$(CFLAGS) $(CFLAGS_KERNEL)" kallsyms
++
++.PHONY:       kallsyms
++
++kallsyms:
++ifeq ($(CONFIG_KALLSYMS),y)
++      @echo kallsyms pass 1
++      $(LD_VMLINUX) -o $(TMPPREFIX).tmp_vmlinux1
++      @$(KALLSYMS) $(TMPPREFIX).tmp_vmlinux1 > $(TMPPREFIX).tmp_kallsyms1.o
++      @echo kallsyms pass 2
++      @$(LD_VMLINUX) $(TMPPREFIX).tmp_kallsyms1.o -o $(TMPPREFIX).tmp_vmlinux2
++      @$(KALLSYMS) $(TMPPREFIX).tmp_vmlinux2 > $(TMPPREFIX).tmp_kallsyms2.o
++      @echo kallsyms pass 3
++      @$(LD_VMLINUX) $(TMPPREFIX).tmp_kallsyms2.o -o $(TMPPREFIX).tmp_vmlinux3
++      @$(KALLSYMS) $(TMPPREFIX).tmp_vmlinux3 > $(TMPPREFIX).tmp_kallsyms3.o
++endif
++      $(LD_VMLINUX) $(LD_VMLINUX_KALLSYMS) -o vmlinux
+       $(NM) vmlinux | grep -v '\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | sort > System.map
++      @rm -f $(TMPPREFIX).tmp_vmlinux* $(TMPPREFIX).tmp_kallsyms*
+ symlinks:
+       rm -f include/asm
+Index: linux-2.4.29/kernel/Makefile
+===================================================================
+--- linux-2.4.29.orig/kernel/Makefile  2005-05-08 23:06:26.939052160 +0300
++++ linux-2.4.29/kernel/Makefile       2005-05-08 23:07:11.223319928 +0300
+@@ -19,6 +19,7 @@
+ obj-$(CONFIG_UID16) += uid16.o
+ obj-$(CONFIG_MODULES) += ksyms.o
+ obj-$(CONFIG_PM) += pm.o
++obj-$(CONFIG_KALLSYMS) += kallsyms.o
+ ifneq ($(CONFIG_IA64),y)
+ # According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is
+Index: linux-2.4.29/kernel/ksyms.c
+===================================================================
+--- linux-2.4.29.orig/kernel/ksyms.c   2005-05-08 23:07:10.878372368 +0300
++++ linux-2.4.29/kernel/ksyms.c        2005-05-08 23:07:11.224319776 +0300
+@@ -59,6 +59,9 @@
+ #ifdef CONFIG_KMOD
+ #include <linux/kmod.h>
+ #endif
++#ifdef CONFIG_KALLSYMS
++#include <linux/kallsyms.h>
++#endif
+ extern void set_device_ro(kdev_t dev,int flag);
+@@ -87,6 +90,15 @@
+ EXPORT_SYMBOL(inter_module_put);
+ EXPORT_SYMBOL(try_inc_mod_count);
++#ifdef CONFIG_KALLSYMS
++extern const char __start___kallsyms[];
++extern const char __stop___kallsyms[];
++EXPORT_SYMBOL(__start___kallsyms);
++EXPORT_SYMBOL(__stop___kallsyms);
++
++
++#endif
++
+ /* process memory management */
+ EXPORT_SYMBOL(do_mmap_pgoff);
+ EXPORT_SYMBOL(do_munmap);
+Index: linux-2.4.29/kernel/kallsyms.c
+===================================================================
+--- linux-2.4.29.orig/kernel/kallsyms.c        2005-05-08 23:07:11.196324032 +0300
++++ linux-2.4.29/kernel/kallsyms.c     2005-05-08 23:07:11.226319472 +0300
+@@ -0,0 +1,306 @@
++/* An example of using kallsyms data in a kernel debugger.
++
++   Copyright 2000 Keith Owens <kaos@ocs.com.au> April 2000
++
++   This file is part of the Linux modutils.
++
++   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.
++
++   This program is distributed in the hope that it will be useful, but
++   WITHOUT ANY WARRANTY; without even the implied warranty of
++   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++   General Public License for more details.
++
++   You should have received a copy of the GNU General Public License
++   along with this program; if not, write to the Free Software Foundation,
++   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++  */
++
++#ident "$Id: kallsyms-2.4-bgl.patch,v 1.1.20.1 2005/03/24 22:50:28 jacob Exp $"
++
++/*
++   This code uses the list of all kernel and module symbols to :-
++
++   * Find any non-stack symbol in a kernel or module.  Symbols do
++     not have to be exported for debugging.
++
++   * Convert an address to the module (or kernel) that owns it, the
++     section it is in and the nearest symbol.  This finds all non-stack
++     symbols, not just exported ones.
++
++   You need modutils >= 2.3.11 and a kernel with the kallsyms patch
++   which was compiled with CONFIG_KALLSYMS.
++ */
++
++#include <linux/elf.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/string.h>
++#include <linux/kallsyms.h>
++
++/* These external symbols are only set on kernels compiled with
++ * CONFIG_KALLSYMS.
++ */
++
++extern const char __start___kallsyms[];
++extern const char __stop___kallsyms[];
++
++static struct module **kallsyms_module_list;
++
++static void kallsyms_get_module_list(void)
++{
++      const struct kallsyms_header    *ka_hdr;
++      const struct kallsyms_section   *ka_sec;
++      const struct kallsyms_symbol    *ka_sym;
++      const char                      *ka_str;
++      int i;
++      const char *p;
++
++      if (__start___kallsyms >= __stop___kallsyms)
++              return;
++      ka_hdr = (struct kallsyms_header *)__start___kallsyms;
++      ka_sec = (struct kallsyms_section *)
++              ((char *)(ka_hdr) + ka_hdr->section_off);
++      ka_sym = (struct kallsyms_symbol *)
++              ((char *)(ka_hdr) + ka_hdr->symbol_off);
++      ka_str = 
++              ((char *)(ka_hdr) + ka_hdr->string_off);
++
++      for (i = 0; i < ka_hdr->symbols; kallsyms_next_sym(ka_hdr, ka_sym), ++i) {
++              p = ka_str + ka_sym->name_off;
++              if (strcmp(p, "module_list") == 0) {
++                      if (ka_sym->symbol_addr)
++                              kallsyms_module_list = (struct module **)(ka_sym->symbol_addr);
++                      break;
++              }
++      }
++}
++
++static inline void kallsyms_do_first_time(void)
++{
++      static int first_time = 1;
++      if (first_time)
++              kallsyms_get_module_list();
++      first_time = 0;
++}
++
++/* A symbol can appear in more than one module.  A token is used to
++ * restart the scan at the next module, set the token to 0 for the
++ * first scan of each symbol.
++ */
++
++int kallsyms_symbol_to_address(
++      const char       *name,         /* Name to lookup */
++      unsigned long    *token,        /* Which module to start at */
++      const char      **mod_name,     /* Set to module name */
++      unsigned long    *mod_start,    /* Set to start address of module */
++      unsigned long    *mod_end,      /* Set to end address of module */
++      const char      **sec_name,     /* Set to section name */
++      unsigned long    *sec_start,    /* Set to start address of section */
++      unsigned long    *sec_end,      /* Set to end address of section */
++      const char      **sym_name,     /* Set to full symbol name */
++      unsigned long    *sym_start,    /* Set to start address of symbol */
++      unsigned long    *sym_end       /* Set to end address of symbol */
++      )
++{
++      const struct kallsyms_header    *ka_hdr = NULL; /* stupid gcc */
++      const struct kallsyms_section   *ka_sec;
++      const struct kallsyms_symbol    *ka_sym = NULL;
++      const char                      *ka_str = NULL;
++      const struct module *m;
++      int i = 0, l;
++      const char *p, *pt_R;
++      char *p2;
++
++      kallsyms_do_first_time();
++      if (!kallsyms_module_list)
++              return(0);
++
++      /* Restart? */
++      m = *kallsyms_module_list;
++      if (token && *token) {
++              for (; m; m = m->next)
++                      if ((unsigned long)m == *token)
++                              break;
++              if (m)
++                      m = m->next;
++      }
++
++      for (; m; m = m->next) {
++              if (!mod_member_present(m, kallsyms_start) || 
++                  !mod_member_present(m, kallsyms_end) ||
++                  m->kallsyms_start >= m->kallsyms_end)
++                      continue;
++              ka_hdr = (struct kallsyms_header *)m->kallsyms_start;
++              ka_sym = (struct kallsyms_symbol *)
++                      ((char *)(ka_hdr) + ka_hdr->symbol_off);
++              ka_str = 
++                      ((char *)(ka_hdr) + ka_hdr->string_off);
++              for (i = 0; i < ka_hdr->symbols; ++i, kallsyms_next_sym(ka_hdr, ka_sym)) {
++                      p = ka_str + ka_sym->name_off;
++                      if (strcmp(p, name) == 0)
++                              break;
++                      /* Unversioned requests match versioned names */
++                      if (!(pt_R = strstr(p, "_R")))
++                              continue;
++                      l = strlen(pt_R);
++                      if (l < 10)
++                              continue;       /* Not _R.*xxxxxxxx */
++                      (void)simple_strtoul(pt_R+l-8, &p2, 16);
++                      if (*p2)
++                              continue;       /* Not _R.*xxxxxxxx */
++                      if (strncmp(p, name, pt_R-p) == 0)
++                              break;  /* Match with version */
++              }
++              if (i < ka_hdr->symbols)
++                      break;
++      }
++
++      if (token)
++              *token = (unsigned long)m;
++      if (!m)
++              return(0);      /* not found */
++
++      ka_sec = (const struct kallsyms_section *)
++              ((char *)ka_hdr + ka_hdr->section_off + ka_sym->section_off);
++      *mod_name = *(m->name) ? m->name : "kernel";
++      *mod_start = ka_hdr->start;
++      *mod_end = ka_hdr->end;
++      *sec_name = ka_sec->name_off + ka_str;
++      *sec_start = ka_sec->start;
++      *sec_end = ka_sec->start + ka_sec->size;
++      *sym_name = ka_sym->name_off + ka_str;
++      *sym_start = ka_sym->symbol_addr;
++      if (i < ka_hdr->symbols-1) {
++              const struct kallsyms_symbol *ka_symn = ka_sym;
++              kallsyms_next_sym(ka_hdr, ka_symn);
++              *sym_end = ka_symn->symbol_addr;
++      }
++      else
++              *sym_end = *sec_end;
++      return(1);
++}
++
++int kallsyms_address_to_symbol(
++      unsigned long     address,      /* Address to lookup */
++      const char      **mod_name,     /* Set to module name */
++      unsigned long    *mod_start,    /* Set to start address of module */
++      unsigned long    *mod_end,      /* Set to end address of module */
++      const char      **sec_name,     /* Set to section name */
++      unsigned long    *sec_start,    /* Set to start address of section */
++      unsigned long    *sec_end,      /* Set to end address of section */
++      const char      **sym_name,     /* Set to full symbol name */
++      unsigned long    *sym_start,    /* Set to start address of symbol */
++      unsigned long    *sym_end       /* Set to end address of symbol */
++      )
++{
++      const struct kallsyms_header    *ka_hdr = NULL; /* stupid gcc */
++      const struct kallsyms_section   *ka_sec = NULL;
++      const struct kallsyms_symbol    *ka_sym;
++      const char                      *ka_str;
++      const struct module *m;
++      int i;
++      unsigned long end;
++
++      kallsyms_do_first_time();
++      if (!kallsyms_module_list)
++              return(0);
++
++      for (m = *kallsyms_module_list; m; m = m->next) {
++              if (!mod_member_present(m, kallsyms_start) || 
++                  !mod_member_present(m, kallsyms_end) ||
++                  m->kallsyms_start >= m->kallsyms_end)
++                      continue;
++              ka_hdr = (struct kallsyms_header *)m->kallsyms_start;
++              ka_sec = (const struct kallsyms_section *)
++                      ((char *)ka_hdr + ka_hdr->section_off);
++              /* Is the address in any section in this module? */
++              for (i = 0; i < ka_hdr->sections; ++i, kallsyms_next_sec(ka_hdr, ka_sec)) {
++                      if (ka_sec->start <= address &&
++                          (ka_sec->start + ka_sec->size) > address)
++                              break;
++              }
++              if (i < ka_hdr->sections)
++                      break;  /* Found a matching section */
++      }
++
++      if (!m)
++              return(0);      /* not found */
++
++      ka_sym = (struct kallsyms_symbol *)
++              ((char *)(ka_hdr) + ka_hdr->symbol_off);
++      ka_str = 
++              ((char *)(ka_hdr) + ka_hdr->string_off);
++      *mod_name = *(m->name) ? m->name : "kernel";
++      *mod_start = ka_hdr->start;
++      *mod_end = ka_hdr->end;
++      *sec_name = ka_sec->name_off + ka_str;
++      *sec_start = ka_sec->start;
++      *sec_end = ka_sec->start + ka_sec->size;
++      *sym_name = *sec_name;          /* In case we find no matching symbol */
++      *sym_start = *sec_start;
++      *sym_end = *sec_end;
++
++      for (i = 0; i < ka_hdr->symbols; ++i, kallsyms_next_sym(ka_hdr, ka_sym)) {
++              if (ka_sym->symbol_addr > address)
++                      continue;
++              if (i < ka_hdr->symbols-1) {
++                      const struct kallsyms_symbol *ka_symn = ka_sym;
++                      kallsyms_next_sym(ka_hdr, ka_symn);
++                      end = ka_symn->symbol_addr;
++              }
++              else
++                      end = *sec_end;
++              if (end <= address)
++                      continue;
++              if ((char *)ka_hdr + ka_hdr->section_off + ka_sym->section_off
++                  != (char *)ka_sec)
++                      continue;       /* wrong section */
++              *sym_name = ka_str + ka_sym->name_off;
++              *sym_start = ka_sym->symbol_addr;
++              *sym_end = end;
++              break;
++      }
++      return(1);
++}
++
++/* List all sections in all modules.  The callback routine is invoked with
++ * token, module name, section name, section start, section end, section flags.
++ */
++int kallsyms_sections(void *token,
++                    int (*callback)(void *, const char *, const char *, ElfW(Addr), ElfW(Addr), ElfW(Word)))
++{
++      const struct kallsyms_header    *ka_hdr = NULL; /* stupid gcc */
++      const struct kallsyms_section   *ka_sec = NULL;
++      const char                      *ka_str;
++      const struct module *m;
++      int i;
++
++      kallsyms_do_first_time();
++      if (!kallsyms_module_list)
++              return(0);
++
++      for (m = *kallsyms_module_list; m; m = m->next) {
++              if (!mod_member_present(m, kallsyms_start) || 
++                  !mod_member_present(m, kallsyms_end) ||
++                  m->kallsyms_start >= m->kallsyms_end)
++                      continue;
++              ka_hdr = (struct kallsyms_header *)m->kallsyms_start;
++              ka_sec = (const struct kallsyms_section *) ((char *)ka_hdr + ka_hdr->section_off);
++              ka_str = ((char *)(ka_hdr) + ka_hdr->string_off);
++              for (i = 0; i < ka_hdr->sections; ++i, kallsyms_next_sec(ka_hdr, ka_sec)) {
++                      if (callback(
++                              token,
++                              *(m->name) ? m->name : "kernel",
++                              ka_sec->name_off + ka_str,
++                              ka_sec->start,
++                              ka_sec->start + ka_sec->size,
++                              ka_sec->flags))
++                              return(0);
++              }
++      }
++      return(1);
++}
+Index: linux-2.4.29/include/linux/kallsyms.h
+===================================================================
+--- linux-2.4.29.orig/include/linux/kallsyms.h 2005-05-08 23:07:11.196324032 +0300
++++ linux-2.4.29/include/linux/kallsyms.h      2005-05-08 23:08:04.316248576 +0300
+@@ -0,0 +1,141 @@
++/* kallsyms headers
++   Copyright 2000 Keith Owens <kaos@ocs.com.au>
++
++   This file is part of the Linux modutils.  It is exported to kernel
++   space so debuggers can access the kallsyms data.
++
++   The kallsyms data contains all the non-stack symbols from a kernel
++   or a module.  The kernel symbols are held between __start___kallsyms
++   and __stop___kallsyms.  The symbols for a module are accessed via
++   the struct module chain which is based at module_list.
++
++   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.
++
++   This program is distributed in the hope that it will be useful, but
++   WITHOUT ANY WARRANTY; without even the implied warranty of
++   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++   General Public License for more details.
++
++   You should have received a copy of the GNU General Public License
++   along with this program; if not, write to the Free Software Foundation,
++   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ */
++
++#ident "$Id: kallsyms-2.4-bgl.patch,v 1.1.20.1 2005/03/24 22:50:28 jacob Exp $"
++
++#ifndef MODUTILS_KALLSYMS_H
++#define MODUTILS_KALLSYMS_H 1
++
++/* Have to (re)define these ElfW entries here because external kallsyms
++ * code does not have access to modutils/include/obj.h.  This code is
++ * included from user spaces tools (modutils) and kernel, they need
++ * different includes.
++ */
++
++#ifndef ELFCLASS32
++#ifdef __KERNEL__
++#include <linux/elf.h>
++#else /* __KERNEL__ */
++#include <elf.h>
++#endif        /* __KERNEL__ */
++#endif        /* ELFCLASS32 */
++
++#ifndef ELFCLASSM
++#define ELFCLASSM ELF_CLASS
++#endif
++
++#ifndef ElfW
++# if ELFCLASSM == ELFCLASS32
++#  define ElfW(x)  Elf32_ ## x
++#  define ELFW(x)  ELF32_ ## x
++# else
++#  define ElfW(x)  Elf64_ ## x
++#  define ELFW(x)  ELF64_ ## x
++# endif
++#endif
++
++/* Format of data in the kallsyms section.
++ * Most of the fields are small numbers but the total size and all
++ * offsets can be large so use the 32/64 bit types for these fields.
++ *
++ * Do not use sizeof() on these structures, modutils may be using extra
++ * fields.  Instead use the size fields in the header to access the
++ * other bits of data.
++ */  
++
++struct kallsyms_header {
++      int             size;           /* Size of this header */
++      ElfW(Word)      total_size;     /* Total size of kallsyms data */
++      int             sections;       /* Number of section entries */
++      ElfW(Off)       section_off;    /* Offset to first section entry */
++      int             section_size;   /* Size of one section entry */
++      int             symbols;        /* Number of symbol entries */
++      ElfW(Off)       symbol_off;     /* Offset to first symbol entry */
++      int             symbol_size;    /* Size of one symbol entry */
++      ElfW(Off)       string_off;     /* Offset to first string */
++      ElfW(Addr)      start;          /* Start address of first section */
++      ElfW(Addr)      end;            /* End address of last section */
++};
++
++struct kallsyms_section {
++      ElfW(Addr)      start;          /* Start address of section */
++      ElfW(Word)      size;           /* Size of this section */
++      ElfW(Off)       name_off;       /* Offset to section name */
++      ElfW(Word)      flags;          /* Flags from section */
++};
++
++struct kallsyms_symbol {
++      ElfW(Off)       section_off;    /* Offset to section that owns this symbol */
++      ElfW(Addr)      symbol_addr;    /* Address of symbol */
++      ElfW(Off)       name_off;       /* Offset to symbol name */
++};
++
++#define KALLSYMS_SEC_NAME "__kallsyms"
++#define KALLSYMS_IDX 2                        /* obj_kallsyms creates kallsyms as section 2 */
++
++#define kallsyms_next_sec(h,s) \
++      ((s) = (struct kallsyms_section *)((char *)(s) + (h)->section_size))
++#define kallsyms_next_sym(h,s) \
++      ((s) = (struct kallsyms_symbol *)((char *)(s) + (h)->symbol_size))
++
++int kallsyms_symbol_to_address(
++      const char       *name,                 /* Name to lookup */
++      unsigned long    *token,                /* Which module to start with */
++      const char      **mod_name,             /* Set to module name or "kernel" */
++      unsigned long    *mod_start,            /* Set to start address of module */
++      unsigned long    *mod_end,              /* Set to end address of module */
++      const char      **sec_name,             /* Set to section name */
++      unsigned long    *sec_start,            /* Set to start address of section */
++      unsigned long    *sec_end,              /* Set to end address of section */
++      const char      **sym_name,             /* Set to full symbol name */
++      unsigned long    *sym_start,            /* Set to start address of symbol */
++      unsigned long    *sym_end               /* Set to end address of symbol */
++      );
++
++int kallsyms_address_to_symbol(
++      unsigned long     address,              /* Address to lookup */
++      const char      **mod_name,             /* Set to module name */
++      unsigned long    *mod_start,            /* Set to start address of module */
++      unsigned long    *mod_end,              /* Set to end address of module */
++      const char      **sec_name,             /* Set to section name */
++      unsigned long    *sec_start,            /* Set to start address of section */
++      unsigned long    *sec_end,              /* Set to end address of section */
++      const char      **sym_name,             /* Set to full symbol name */
++      unsigned long    *sym_start,            /* Set to start address of symbol */
++      unsigned long    *sym_end               /* Set to end address of symbol */
++      );
++
++int kallsyms_sections(void *token,
++                    int (*callback)(void *,   /* token */
++                      const char *,           /* module name */
++                      const char *,           /* section name */
++                      ElfW(Addr),             /* Section start */
++                      ElfW(Addr),             /* Section end */
++                      ElfW(Word)              /* Section flags */
++                    )
++              );
++
++#endif /* kallsyms.h */
+Index: linux-2.4.29/arch/i386/vmlinux.lds.S
+===================================================================
+--- linux-2.4.29.orig/arch/i386/vmlinux.lds.S  2005-05-08 23:07:09.948513728 +0300
++++ linux-2.4.29/arch/i386/vmlinux.lds.S       2005-05-08 23:14:24.128508336 +0300
+@@ -28,6 +28,10 @@
+   __ksymtab : { *(__ksymtab) }
+   __stop___ksymtab = .;
++  __start___kallsyms = .; /* All kernel symbols */
++  __kallsyms : { *(__kallsyms) }
++  __stop___kallsyms = .;
++
+   .data : {                   /* Data */
+       *(.data)
+       CONSTRUCTORS
diff --git a/lustre/kernel_patches/patches/linux-2.4.24-jbd-handle-EIO.patch b/lustre/kernel_patches/patches/linux-2.4.24-jbd-handle-EIO.patch
new file mode 100644 (file)
index 0000000..ff2991f
--- /dev/null
@@ -0,0 +1,51 @@
+diff -X /home/nikita/src/linux-git/linux-2.6.git/Documentation/dontdiff -rupbB linux-2.4.24.orig/fs/jbd/commit.c linux-2.4.24/fs/jbd/commit.c
+--- linux-2.4.24.orig/fs/jbd/commit.c  2005-06-23 17:39:32.000000000 +0400
++++ linux-2.4.24/fs/jbd/commit.c       2005-06-23 15:56:05.000000000 +0400
+@@ -47,7 +47,7 @@ void journal_commit_transaction(journal_
+       struct buffer_head *wbuf[64];
+       int bufs;
+       int flags;
+-      int err;
++      int err = 0;
+       unsigned long blocknr;
+       char *tagp = NULL;
+       journal_header_t *header;
+@@ -505,6 +505,9 @@ start_journal_io:
+                       goto wait_for_iobuf;
+               }
++              if (unlikely(!buffer_uptodate(bh)))
++                      err = -EIO;
++
+               clear_bit(BH_JWrite, &jh2bh(jh)->b_state);
+               JBUFFER_TRACE(jh, "ph4: unfile after journal write");
+@@ -566,6 +569,9 @@ start_journal_io:
+                       goto wait_for_ctlbuf;
+               }
++              if (unlikely(!buffer_uptodate(bh)))
++                      err = -EIO;
++
+               BUFFER_TRACE(bh, "ph5: control buffer writeout done: unfile");
+               clear_bit(BH_JWrite, &bh->b_state);
+               journal_unfile_buffer(jh);
+@@ -610,6 +616,8 @@ start_journal_io:
+               bh->b_end_io = journal_end_buffer_io_sync;
+               submit_bh(WRITE, bh);
+               wait_on_buffer(bh);
++              if (unlikely(!buffer_uptodate(bh)))
++                      err = -EIO;
+               put_bh(bh);             /* One for getblk() */
+               journal_unlock_journal_head(descriptor);
+       }
+@@ -621,6 +629,9 @@ start_journal_io:
+ skip_commit: /* The journal should be unlocked by now. */
++      if (err)
++              __journal_abort_hard(journal);
++
+       /* Call any callbacks that had been registered for handles in this
+        * transaction.  It is up to the callback to free any allocated
+        * memory.
diff --git a/lustre/kernel_patches/patches/linux-2.4.29-xattr-0.8.54.patch b/lustre/kernel_patches/patches/linux-2.4.29-xattr-0.8.54.patch
new file mode 100644 (file)
index 0000000..8225ea3
--- /dev/null
@@ -0,0 +1,5362 @@
+Index: linux-2.4.29/Documentation/Configure.help
+===================================================================
+--- linux-2.4.29.orig/Documentation/Configure.help     2005-04-07 18:55:00.000000000 +0300
++++ linux-2.4.29/Documentation/Configure.help  2005-05-03 17:59:40.363127040 +0300
+@@ -16679,6 +16679,39 @@
+   be compiled as a module, and so this could be dangerous.  Most
+   everyone wants to say Y here.
++Ext2 extended attributes
++CONFIG_EXT2_FS_XATTR
++  Extended attributes are name:value pairs associated with inodes by
++  the kernel or by users (see the attr(5) manual page, or visit
++  <http://acl.bestbits.at/> for details).
++
++  If unsure, say N.
++
++Ext2 extended attribute block sharing
++CONFIG_EXT2_FS_XATTR_SHARING
++  This options enables code for sharing identical extended attribute
++  blocks among multiple inodes.
++
++  Usually, say Y.
++
++Ext2 extended user attributes
++CONFIG_EXT2_FS_XATTR_USER
++  This option enables extended user attributes on ext2. Processes can
++  associate extended user attributes with inodes to store additional
++  information such as the character encoding of files, etc. (see the
++  attr(5) manual page, or visit <http://acl.bestbits.at/> for details).
++
++  If unsure, say N.
++
++Ext2 trusted extended attributes
++CONFIG_EXT2_FS_XATTR_TRUSTED
++  This option enables extended attributes on ext2 that are accessible
++  (and visible) only to users capable of CAP_SYS_ADMIN. Usually this
++  is only the super user. Trusted extended attributes are meant for
++  implementing system/security services.
++
++  If unsure, say N.
++
+ Ext3 journalling file system support (EXPERIMENTAL)
+ CONFIG_EXT3_FS
+   This is the journalling version of the Second extended file system
+@@ -16711,6 +16744,39 @@
+   of your root partition (the one containing the directory /) cannot
+   be compiled as a module, and so this may be dangerous.
++Ext3 extended attributes
++CONFIG_EXT3_FS_XATTR
++  Extended attributes are name:value pairs associated with inodes by
++  the kernel or by users (see the attr(5) manual page, or visit
++  <http://acl.bestbits.at/> for details).
++
++  If unsure, say N.
++
++Ext3 extended attribute block sharing
++CONFIG_EXT3_FS_XATTR_SHARING
++  This options enables code for sharing identical extended attribute
++  blocks among multiple inodes.
++
++  Usually, say Y.
++
++Ext3 extended user attributes
++CONFIG_EXT3_FS_XATTR_USER
++  This option enables extended user attributes on ext3. Processes can
++  associate extended user attributes with inodes to store additional
++  information such as the character encoding of files, etc. (see the
++  attr(5) manual page, or visit <http://acl.bestbits.at/> for details).
++
++  If unsure, say N.
++
++Ext3 trusted extended attributes
++CONFIG_EXT3_FS_XATTR_TRUSTED
++  This option enables extended attributes on ext3 that are accessible
++  (and visible) only to users capable of CAP_SYS_ADMIN. Usually this
++  is only the super user. Trusted extended attributes are meant for
++  implementing system/security services.
++
++  If unsure, say N.
++
+ Journal Block Device support (JBD for ext3) (EXPERIMENTAL)
+ CONFIG_JBD
+   This is a generic journalling layer for block devices.  It is
+Index: linux-2.4.29/arch/alpha/defconfig
+===================================================================
+--- linux-2.4.29.orig/arch/alpha/defconfig     2005-04-07 18:53:42.000000000 +0300
++++ linux-2.4.29/arch/alpha/defconfig  2005-05-03 17:59:40.365126736 +0300
+@@ -1,6 +1,13 @@
+ #
+ # Automatically generated make config: don't edit
+ #
++CONFIG_EXT3_FS_XATTR=y
++# CONFIG_EXT3_FS_XATTR_SHARING is not set
++# CONFIG_EXT3_FS_XATTR_USER is not set
++# CONFIG_EXT2_FS_XATTR is not set
++# CONFIG_EXT2_FS_XATTR_SHARING is not set
++# CONFIG_EXT2_FS_XATTR_USER is not set
++# CONFIG_FS_MBCACHE is not set
+ CONFIG_ALPHA=y
+ # CONFIG_UID16 is not set
+ # CONFIG_RWSEM_GENERIC_SPINLOCK is not set
+Index: linux-2.4.29/arch/alpha/kernel/entry.S
+===================================================================
+--- linux-2.4.29.orig/arch/alpha/kernel/entry.S        2005-04-07 18:52:17.000000000 +0300
++++ linux-2.4.29/arch/alpha/kernel/entry.S     2005-05-03 17:59:40.367126432 +0300
+@@ -1154,6 +1154,18 @@
+       .quad sys_readahead
+       .quad sys_ni_syscall                    /* 380, sys_security */
+       .quad sys_tkill
++      .quad sys_setxattr
++      .quad sys_lsetxattr
++      .quad sys_fsetxattr
++      .quad sys_getxattr                      /* 385 */
++      .quad sys_lgetxattr
++      .quad sys_fgetxattr
++      .quad sys_listxattr
++      .quad sys_llistxattr
++      .quad sys_flistxattr                    /* 390 */
++      .quad sys_removexattr
++      .quad sys_lremovexattr
++      .quad sys_fremovexattr
+ /* Remember to update everything, kids.  */
+ .ifne (. - sys_call_table) - (NR_SYSCALLS * 8)
+Index: linux-2.4.29/arch/arm/defconfig
+===================================================================
+--- linux-2.4.29.orig/arch/arm/defconfig       2005-04-07 18:53:03.000000000 +0300
++++ linux-2.4.29/arch/arm/defconfig    2005-05-03 17:59:40.369126128 +0300
+@@ -1,6 +1,13 @@
+ #
+ # Automatically generated make config: don't edit
+ #
++CONFIG_EXT3_FS_XATTR=y
++# CONFIG_EXT3_FS_XATTR_SHARING is not set
++# CONFIG_EXT3_FS_XATTR_USER is not set
++# CONFIG_EXT2_FS_XATTR is not set
++# CONFIG_EXT2_FS_XATTR_SHARING is not set
++# CONFIG_EXT2_FS_XATTR_USER is not set
++# CONFIG_FS_MBCACHE is not set
+ CONFIG_ARM=y
+ # CONFIG_EISA is not set
+ # CONFIG_SBUS is not set
+Index: linux-2.4.29/arch/arm/kernel/calls.S
+===================================================================
+--- linux-2.4.29.orig/arch/arm/kernel/calls.S  2005-04-07 18:55:23.000000000 +0300
++++ linux-2.4.29/arch/arm/kernel/calls.S       2005-05-03 17:59:40.371125824 +0300
+@@ -240,18 +240,18 @@
+               .long   SYMBOL_NAME(sys_ni_syscall) /* Security */
+               .long   SYMBOL_NAME(sys_gettid)
+ /* 225 */     .long   SYMBOL_NAME(sys_readahead)
+-              .long   SYMBOL_NAME(sys_ni_syscall) /* setxattr */
+-              .long   SYMBOL_NAME(sys_ni_syscall) /* lsetxattr */
+-              .long   SYMBOL_NAME(sys_ni_syscall) /* fsetxattr */
+-              .long   SYMBOL_NAME(sys_ni_syscall) /* getxattr */
+-/* 230 */     .long   SYMBOL_NAME(sys_ni_syscall) /* lgetxattr */
+-              .long   SYMBOL_NAME(sys_ni_syscall) /* fgetxattr */
+-              .long   SYMBOL_NAME(sys_ni_syscall) /* listxattr */
+-              .long   SYMBOL_NAME(sys_ni_syscall) /* llistxattr */
+-              .long   SYMBOL_NAME(sys_ni_syscall) /* flistxattr */
+-/* 235 */     .long   SYMBOL_NAME(sys_ni_syscall) /* removexattr */
+-              .long   SYMBOL_NAME(sys_ni_syscall) /* lremovexattr */
+-              .long   SYMBOL_NAME(sys_ni_syscall) /* fremovexattr */
++              .long   SYMBOL_NAME(sys_setxattr)
++              .long   SYMBOL_NAME(sys_lsetxattr)
++              .long   SYMBOL_NAME(sys_fsetxattr)
++              .long   SYMBOL_NAME(sys_getxattr)
++/* 230 */     .long   SYMBOL_NAME(sys_lgetxattr)
++              .long   SYMBOL_NAME(sys_fgetxattr)
++              .long   SYMBOL_NAME(sys_listxattr)
++              .long   SYMBOL_NAME(sys_llistxattr)
++              .long   SYMBOL_NAME(sys_flistxattr)
++/* 235 */     .long   SYMBOL_NAME(sys_removexattr)
++              .long   SYMBOL_NAME(sys_lremovexattr)
++              .long   SYMBOL_NAME(sys_fremovexattr)
+               .long   SYMBOL_NAME(sys_tkill)
+               .long   SYMBOL_NAME(sys_ni_syscall) /* sendfile64 */
+ /* 240 */     .long   SYMBOL_NAME(sys_ni_syscall) /* futex */
+Index: linux-2.4.29/arch/i386/defconfig
+===================================================================
+--- linux-2.4.29.orig/arch/i386/defconfig      2005-04-07 18:52:37.000000000 +0300
++++ linux-2.4.29/arch/i386/defconfig   2005-05-03 17:59:40.372125672 +0300
+@@ -1,6 +1,13 @@
+ #
+ # Automatically generated make config: don't edit
+ #
++CONFIG_EXT3_FS_XATTR=y
++# CONFIG_EXT3_FS_XATTR_SHARING is not set
++# CONFIG_EXT3_FS_XATTR_USER is not set
++# CONFIG_EXT2_FS_XATTR is not set
++# CONFIG_EXT2_FS_XATTR_SHARING is not set
++# CONFIG_EXT2_FS_XATTR_USER is not set
++# CONFIG_FS_MBCACHE is not set
+ CONFIG_X86=y
+ # CONFIG_SBUS is not set
+ CONFIG_UID16=y
+Index: linux-2.4.29/arch/ia64/defconfig
+===================================================================
+--- linux-2.4.29.orig/arch/ia64/defconfig      2005-04-07 18:52:32.000000000 +0300
++++ linux-2.4.29/arch/ia64/defconfig   2005-05-03 17:59:40.374125368 +0300
+@@ -1,6 +1,13 @@
+ #
+ # Automatically generated make config: don't edit
+ #
++CONFIG_EXT3_FS_XATTR=y
++# CONFIG_EXT3_FS_XATTR_SHARING is not set
++# CONFIG_EXT3_FS_XATTR_USER is not set
++# CONFIG_EXT2_FS_XATTR is not set
++# CONFIG_EXT2_FS_XATTR_SHARING is not set
++# CONFIG_EXT2_FS_XATTR_USER is not set
++# CONFIG_FS_MBCACHE is not set
+ #
+ # Code maturity level options
+Index: linux-2.4.29/arch/m68k/defconfig
+===================================================================
+--- linux-2.4.29.orig/arch/m68k/defconfig      2005-04-07 18:52:26.000000000 +0300
++++ linux-2.4.29/arch/m68k/defconfig   2005-05-03 17:59:40.375125216 +0300
+@@ -1,6 +1,13 @@
+ #
+ # Automatically generated make config: don't edit
+ #
++CONFIG_EXT3_FS_XATTR=y
++# CONFIG_EXT3_FS_XATTR_SHARING is not set
++# CONFIG_EXT3_FS_XATTR_USER is not set
++# CONFIG_EXT2_FS_XATTR is not set
++# CONFIG_EXT2_FS_XATTR_SHARING is not set
++# CONFIG_EXT2_FS_XATTR_USER is not set
++# CONFIG_FS_MBCACHE is not set
+ CONFIG_UID16=y
+ #
+Index: linux-2.4.29/arch/mips/defconfig
+===================================================================
+--- linux-2.4.29.orig/arch/mips/defconfig      2005-04-07 18:52:42.000000000 +0300
++++ linux-2.4.29/arch/mips/defconfig   2005-05-03 17:59:40.376125064 +0300
+@@ -1,6 +1,13 @@
+ #
+ # Automatically generated make config: don't edit
+ #
++CONFIG_EXT3_FS_XATTR=y
++# CONFIG_EXT3_FS_XATTR_SHARING is not set
++# CONFIG_EXT3_FS_XATTR_USER is not set
++# CONFIG_EXT2_FS_XATTR is not set
++# CONFIG_EXT2_FS_XATTR_SHARING is not set
++# CONFIG_EXT2_FS_XATTR_USER is not set
++# CONFIG_FS_MBCACHE is not set
+ CONFIG_MIPS=y
+ CONFIG_MIPS32=y
+ # CONFIG_MIPS64 is not set
+Index: linux-2.4.29/arch/mips64/defconfig
+===================================================================
+--- linux-2.4.29.orig/arch/mips64/defconfig    2005-04-07 18:52:47.000000000 +0300
++++ linux-2.4.29/arch/mips64/defconfig 2005-05-03 17:59:40.378124760 +0300
+@@ -1,6 +1,13 @@
+ #
+ # Automatically generated make config: don't edit
+ #
++CONFIG_EXT3_FS_XATTR=y
++# CONFIG_EXT3_FS_XATTR_SHARING is not set
++# CONFIG_EXT3_FS_XATTR_USER is not set
++# CONFIG_EXT2_FS_XATTR is not set
++# CONFIG_EXT2_FS_XATTR_SHARING is not set
++# CONFIG_EXT2_FS_XATTR_USER is not set
++# CONFIG_FS_MBCACHE is not set
+ CONFIG_MIPS=y
+ # CONFIG_MIPS32 is not set
+ CONFIG_MIPS64=y
+Index: linux-2.4.29/arch/s390/defconfig
+===================================================================
+--- linux-2.4.29.orig/arch/s390/defconfig      2005-04-07 18:54:49.000000000 +0300
++++ linux-2.4.29/arch/s390/defconfig   2005-05-03 17:59:40.379124608 +0300
+@@ -1,6 +1,13 @@
+ #
+ # Automatically generated make config: don't edit
+ #
++CONFIG_EXT3_FS_XATTR=y
++# CONFIG_EXT3_FS_XATTR_SHARING is not set
++# CONFIG_EXT3_FS_XATTR_USER is not set
++# CONFIG_EXT2_FS_XATTR is not set
++# CONFIG_EXT2_FS_XATTR_SHARING is not set
++# CONFIG_EXT2_FS_XATTR_USER is not set
++# CONFIG_FS_MBCACHE is not set
+ # CONFIG_ISA is not set
+ # CONFIG_EISA is not set
+ # CONFIG_MCA is not set
+Index: linux-2.4.29/arch/s390/kernel/entry.S
+===================================================================
+--- linux-2.4.29.orig/arch/s390/kernel/entry.S 2005-04-07 18:52:47.000000000 +0300
++++ linux-2.4.29/arch/s390/kernel/entry.S      2005-05-03 17:59:40.381124304 +0300
+@@ -558,18 +558,18 @@
+         .long  sys_fcntl64 
+       .long  sys_readahead
+       .long  sys_ni_syscall
+-      .long  sys_ni_syscall            /* 224 - reserved for setxattr  */
+-      .long  sys_ni_syscall            /* 225 - reserved for lsetxattr */
+-      .long  sys_ni_syscall            /* 226 - reserved for fsetxattr */
+-      .long  sys_ni_syscall            /* 227 - reserved for getxattr  */
+-      .long  sys_ni_syscall            /* 228 - reserved for lgetxattr */
+-      .long  sys_ni_syscall            /* 229 - reserved for fgetxattr */
+-      .long  sys_ni_syscall            /* 230 - reserved for listxattr */
+-      .long  sys_ni_syscall            /* 231 - reserved for llistxattr */
+-      .long  sys_ni_syscall            /* 232 - reserved for flistxattr */
+-      .long  sys_ni_syscall            /* 233 - reserved for removexattr */
+-      .long  sys_ni_syscall            /* 234 - reserved for lremovexattr */
+-      .long  sys_ni_syscall            /* 235 - reserved for fremovexattr */
++      .long  sys_setxattr
++      .long  sys_lsetxattr            /* 225 */
++      .long  sys_fsetxattr
++      .long  sys_getxattr
++      .long  sys_lgetxattr
++      .long  sys_fgetxattr
++      .long  sys_listxattr            /* 230 */
++      .long  sys_llistxattr
++      .long  sys_flistxattr
++      .long  sys_removexattr
++      .long  sys_lremovexattr
++      .long  sys_fremovexattr         /* 235 */
+       .long  sys_gettid
+       .long  sys_tkill
+       .rept  255-237
+Index: linux-2.4.29/arch/s390x/defconfig
+===================================================================
+--- linux-2.4.29.orig/arch/s390x/defconfig     2005-04-07 18:52:17.000000000 +0300
++++ linux-2.4.29/arch/s390x/defconfig  2005-05-03 17:59:40.382124152 +0300
+@@ -1,6 +1,13 @@
+ #
+ # Automatically generated make config: don't edit
+ #
++CONFIG_EXT3_FS_XATTR=y
++# CONFIG_EXT3_FS_XATTR_SHARING is not set
++# CONFIG_EXT3_FS_XATTR_USER is not set
++# CONFIG_EXT2_FS_XATTR is not set
++# CONFIG_EXT2_FS_XATTR_SHARING is not set
++# CONFIG_EXT2_FS_XATTR_USER is not set
++# CONFIG_FS_MBCACHE is not set
+ # CONFIG_ISA is not set
+ # CONFIG_EISA is not set
+ # CONFIG_MCA is not set
+Index: linux-2.4.29/arch/s390x/kernel/entry.S
+===================================================================
+--- linux-2.4.29.orig/arch/s390x/kernel/entry.S        2005-04-07 18:52:58.000000000 +0300
++++ linux-2.4.29/arch/s390x/kernel/entry.S     2005-05-03 17:59:40.384123848 +0300
+@@ -591,18 +591,18 @@
+       .long  SYSCALL(sys_ni_syscall,sys32_fcntl64_wrapper)
+       .long  SYSCALL(sys_readahead,sys32_readahead)
+       .long  SYSCALL(sys_ni_syscall,sys_ni_syscall)
+-      .long  SYSCALL(sys_ni_syscall,sys_ni_syscall) /* 224 - reserved for setxattr  */
+-      .long  SYSCALL(sys_ni_syscall,sys_ni_syscall) /* 225 - reserved for lsetxattr */
+-      .long  SYSCALL(sys_ni_syscall,sys_ni_syscall) /* 226 - reserved for fsetxattr */
+-      .long  SYSCALL(sys_ni_syscall,sys_ni_syscall) /* 227 - reserved for getxattr  */
+-      .long  SYSCALL(sys_ni_syscall,sys_ni_syscall) /* 228 - reserved for lgetxattr */
+-      .long  SYSCALL(sys_ni_syscall,sys_ni_syscall) /* 229 - reserved for fgetxattr */
+-      .long  SYSCALL(sys_ni_syscall,sys_ni_syscall) /* 230 - reserved for listxattr */
+-      .long  SYSCALL(sys_ni_syscall,sys_ni_syscall) /* 231 - reserved for llistxattr */
+-      .long  SYSCALL(sys_ni_syscall,sys_ni_syscall) /* 232 - reserved for flistxattr */
+-      .long  SYSCALL(sys_ni_syscall,sys_ni_syscall) /* 233 - reserved for removexattr */
+-      .long  SYSCALL(sys_ni_syscall,sys_ni_syscall) /* 234 - reserved for lremovexattr */
+-      .long  SYSCALL(sys_ni_syscall,sys_ni_syscall) /* 235 - reserved for fremovexattr */
++      .long  SYSCALL(sys_setxattr,sys32_setxattr_wrapper)
++      .long  SYSCALL(sys_lsetxattr,sys32_lsetxattr_wrapper)   /* 225 */
++      .long  SYSCALL(sys_fsetxattr,sys32_fsetxattr_wrapper)
++      .long  SYSCALL(sys_getxattr,sys32_getxattr_wrapper)
++      .long  SYSCALL(sys_lgetxattr,sys32_lgetxattr_wrapper)
++      .long  SYSCALL(sys_fgetxattr,sys32_fgetxattr_wrapper)
++      .long  SYSCALL(sys_listxattr,sys32_listxattr_wrapper)   /* 230 */
++      .long  SYSCALL(sys_llistxattr,sys32_llistxattr_wrapper)
++      .long  SYSCALL(sys_flistxattr,sys32_flistxattr_wrapper)
++      .long  SYSCALL(sys_removexattr,sys32_removexattr_wrapper)
++      .long  SYSCALL(sys_lremovexattr,sys32_lremovexattr_wrapper)
++      .long  SYSCALL(sys_fremovexattr,sys32_fremovexattr_wrapper)/* 235 */
+       .long  SYSCALL(sys_gettid,sys_gettid)
+       .long  SYSCALL(sys_tkill,sys_tkill)
+       .rept  255-237
+Index: linux-2.4.29/arch/s390x/kernel/wrapper32.S
+===================================================================
+--- linux-2.4.29.orig/arch/s390x/kernel/wrapper32.S    2005-04-07 18:55:12.000000000 +0300
++++ linux-2.4.29/arch/s390x/kernel/wrapper32.S 2005-05-03 17:59:40.386123544 +0300
+@@ -1098,6 +1098,98 @@
+       llgfr   %r4,%r4                 # long
+       jg      sys32_fstat64           # branch to system call
++      .globl  sys32_setxattr_wrapper
++sys32_setxattr_wrapper:
++      llgtr   %r2,%r2                 # char *
++      llgtr   %r3,%r3                 # char *
++      llgtr   %r4,%r4                 # void *
++      llgfr   %r5,%r5                 # size_t
++      lgfr    %r6,%r6                 # int
++      jg      sys_setxattr
++
++      .globl  sys32_lsetxattr_wrapper
++sys32_lsetxattr_wrapper:
++      llgtr   %r2,%r2                 # char *
++      llgtr   %r3,%r3                 # char *
++      llgtr   %r4,%r4                 # void *
++      llgfr   %r5,%r5                 # size_t
++      lgfr    %r6,%r6                 # int
++      jg      sys_lsetxattr
++
++      .globl  sys32_fsetxattr_wrapper
++sys32_fsetxattr_wrapper:
++      lgfr    %r2,%r2                 # int
++      llgtr   %r3,%r3                 # char *
++      llgtr   %r4,%r4                 # void *
++      llgfr   %r5,%r5                 # size_t
++      lgfr    %r6,%r6                 # int
++      jg      sys_fsetxattr
++
++      .globl  sys32_getxattr_wrapper
++sys32_getxattr_wrapper:
++      llgtr   %r2,%r2                 # char *
++      llgtr   %r3,%r3                 # char *
++      llgtr   %r4,%r4                 # void *
++      llgfr   %r5,%r5                 # size_t
++      jg      sys_getxattr
++
++      .globl  sys32_lgetxattr_wrapper
++sys32_lgetxattr_wrapper:
++      llgtr   %r2,%r2                 # char *
++      llgtr   %r3,%r3                 # char *
++      llgtr   %r4,%r4                 # void *
++      llgfr   %r5,%r5                 # size_t
++      jg      sys_lgetxattr
++
++      .globl  sys32_fgetxattr_wrapper
++sys32_fgetxattr_wrapper:
++      lgfr    %r2,%r2                 # int
++      llgtr   %r3,%r3                 # char *
++      llgtr   %r4,%r4                 # void *
++      llgfr   %r5,%r5                 # size_t
++      jg      sys_fgetxattr
++
++      .globl  sys32_listxattr_wrapper
++sys32_listxattr_wrapper:
++      llgtr   %r2,%r2                 # char *
++      llgtr   %r3,%r3                 # char *
++      llgfr   %r4,%r4                 # size_t
++      jg      sys_listxattr
++
++      .globl  sys32_llistxattr_wrapper
++sys32_llistxattr_wrapper:
++      llgtr   %r2,%r2                 # char *
++      llgtr   %r3,%r3                 # char *
++      llgfr   %r4,%r4                 # size_t
++      jg      sys_llistxattr
++
++      .globl  sys32_flistxattr_wrapper
++sys32_flistxattr_wrapper:
++      lgfr    %r2,%r2                 # int
++      llgtr   %r3,%r3                 # char *
++      llgfr   %r4,%r4                 # size_t
++      jg      sys_flistxattr
++
++      .globl  sys32_removexattr_wrapper
++sys32_removexattr_wrapper:
++      llgtr   %r2,%r2                 # char *
++      llgtr   %r3,%r3                 # char *
++      jg      sys_removexattr
++
++      .globl  sys32_lremovexattr_wrapper
++sys32_lremovexattr_wrapper:
++      llgtr   %r2,%r2                 # char *
++      llgtr   %r3,%r3                 # char *
++      jg      sys_lremovexattr
++
++      .globl  sys32_fremovexattr_wrapper
++sys32_fremovexattr_wrapper:
++      lgfr    %r2,%r2                 # int
++      llgtr   %r3,%r3                 # char *
++      jg      sys_fremovexattr
++
++
++
+       .globl  sys32_stime_wrapper
+ sys32_stime_wrapper:
+       llgtr   %r2,%r2                 # int *
+Index: linux-2.4.29/arch/sparc64/defconfig
+===================================================================
+--- linux-2.4.29.orig/arch/sparc64/defconfig   2005-04-07 18:53:09.000000000 +0300
++++ linux-2.4.29/arch/sparc64/defconfig        2005-05-03 17:59:40.388123240 +0300
+@@ -1,6 +1,13 @@
+ #
+ # Automatically generated make config: don't edit
+ #
++CONFIG_EXT3_FS_XATTR=y
++# CONFIG_EXT3_FS_XATTR_SHARING is not set
++# CONFIG_EXT3_FS_XATTR_USER is not set
++# CONFIG_EXT2_FS_XATTR is not set
++# CONFIG_EXT2_FS_XATTR_SHARING is not set
++# CONFIG_EXT2_FS_XATTR_USER is not set
++# CONFIG_FS_MBCACHE is not set
+ #
+ # Code maturity level options
+Index: linux-2.4.29/fs/Config.in
+===================================================================
+--- linux-2.4.29.orig/fs/Config.in     2005-04-07 18:54:16.000000000 +0300
++++ linux-2.4.29/fs/Config.in  2005-05-03 17:59:40.389123088 +0300
+@@ -29,6 +29,11 @@
+ dep_tristate 'BFS file system support (EXPERIMENTAL)' CONFIG_BFS_FS $CONFIG_EXPERIMENTAL
+ tristate 'Ext3 journalling file system support' CONFIG_EXT3_FS
++dep_mbool '  Ext3 extended attributes' CONFIG_EXT3_FS_XATTR $CONFIG_EXT3_FS
++dep_bool '    Ext3 extended attribute block sharing' \
++    CONFIG_EXT3_FS_XATTR_SHARING $CONFIG_EXT3_FS_XATTR
++dep_bool '    Ext3 extended user attributes' \
++    CONFIG_EXT3_FS_XATTR_USER $CONFIG_EXT3_FS_XATTR
+ # CONFIG_JBD could be its own option (even modular), but until there are
+ # other users than ext3, we will simply make it be the same as CONFIG_EXT3_FS
+ # dep_tristate '  Journal Block Device support (JBD for ext3)' CONFIG_JBD $CONFIG_EXT3_FS
+@@ -92,6 +97,11 @@
+ tristate 'ROM file system support' CONFIG_ROMFS_FS
+ tristate 'Second extended fs support' CONFIG_EXT2_FS
++dep_mbool '  Ext2 extended attributes' CONFIG_EXT2_FS_XATTR $CONFIG_EXT2_FS
++dep_bool '    Ext2 extended attribute block sharing' \
++    CONFIG_EXT2_FS_XATTR_SHARING $CONFIG_EXT2_FS_XATTR
++dep_bool '    Ext2 extended user attributes' \
++    CONFIG_EXT2_FS_XATTR_USER $CONFIG_EXT2_FS_XATTR
+ tristate 'System V/Xenix/V7/Coherent file system support' CONFIG_SYSV_FS
+@@ -171,6 +181,10 @@
+    define_tristate CONFIG_ZISOFS_FS n
+ fi
++# Meta block cache for Extended Attributes (ext2/ext3)
++#tristate 'Meta block cache' CONFIG_FS_MBCACHE
++define_tristate CONFIG_FS_MBCACHE y 
++
+ mainmenu_option next_comment
+ comment 'Partition Types'
+ source fs/partitions/Config.in
+Index: linux-2.4.29/fs/Makefile
+===================================================================
+--- linux-2.4.29.orig/fs/Makefile      2005-05-03 17:23:53.969428480 +0300
++++ linux-2.4.29/fs/Makefile   2005-05-03 17:59:40.390122936 +0300
+@@ -77,6 +77,9 @@
+ obj-$(CONFIG_BINFMT_ELF)      += binfmt_elf.o
++export-objs += mbcache.o
++obj-$(CONFIG_FS_MBCACHE)      += mbcache.o
++
+ # persistent filesystems
+ obj-y += $(join $(subdir-y),$(subdir-y:%=/%.o))
+Index: linux-2.4.29/fs/ext2/Makefile
+===================================================================
+--- linux-2.4.29.orig/fs/ext2/Makefile 2005-04-07 18:54:32.000000000 +0300
++++ linux-2.4.29/fs/ext2/Makefile      2005-05-03 17:59:40.391122784 +0300
+@@ -13,4 +13,8 @@
+               ioctl.o namei.o super.o symlink.o
+ obj-m    := $(O_TARGET)
++export-objs += xattr.o
++obj-$(CONFIG_EXT2_FS_XATTR) += xattr.o
++obj-$(CONFIG_EXT2_FS_XATTR_USER) += xattr_user.o
++
+ include $(TOPDIR)/Rules.make
+Index: linux-2.4.29/fs/ext2/file.c
+===================================================================
+--- linux-2.4.29.orig/fs/ext2/file.c   2005-04-07 18:53:14.000000000 +0300
++++ linux-2.4.29/fs/ext2/file.c        2005-05-03 17:59:40.392122632 +0300
+@@ -20,6 +20,7 @@
+ #include <linux/fs.h>
+ #include <linux/ext2_fs.h>
++#include <linux/ext2_xattr.h>
+ #include <linux/sched.h>
+ /*
+@@ -51,4 +52,8 @@
+ struct inode_operations ext2_file_inode_operations = {
+       truncate:       ext2_truncate,
++      setxattr:       ext2_setxattr,
++      getxattr:       ext2_getxattr,
++      listxattr:      ext2_listxattr,
++      removexattr:    ext2_removexattr,
+ };
+Index: linux-2.4.29/fs/ext2/ialloc.c
+===================================================================
+--- linux-2.4.29.orig/fs/ext2/ialloc.c 2005-04-07 18:53:47.000000000 +0300
++++ linux-2.4.29/fs/ext2/ialloc.c      2005-05-03 17:59:40.393122480 +0300
+@@ -15,6 +15,7 @@
+ #include <linux/config.h>
+ #include <linux/fs.h>
+ #include <linux/ext2_fs.h>
++#include <linux/ext2_xattr.h>
+ #include <linux/locks.h>
+ #include <linux/quotaops.h>
+@@ -167,6 +168,7 @@
+        */
+       if (!is_bad_inode(inode)) {
+               /* Quota is already initialized in iput() */
++              ext2_xattr_delete_inode(inode);
+               DQUOT_FREE_INODE(inode);
+               DQUOT_DROP(inode);
+       }
+Index: linux-2.4.29/fs/ext2/inode.c
+===================================================================
+--- linux-2.4.29.orig/fs/ext2/inode.c  2005-04-07 18:52:32.000000000 +0300
++++ linux-2.4.29/fs/ext2/inode.c       2005-05-03 17:59:40.396122024 +0300
+@@ -64,9 +64,7 @@
+ {
+       lock_kernel();
+-      if (is_bad_inode(inode) ||
+-          inode->i_ino == EXT2_ACL_IDX_INO ||
+-          inode->i_ino == EXT2_ACL_DATA_INO)
++      if (is_bad_inode(inode))
+               goto no_delete;
+       inode->u.ext2_i.i_dtime = CURRENT_TIME;
+       mark_inode_dirty(inode);
+@@ -815,6 +813,8 @@
+               return;
+       if (ext2_inode_is_fast_symlink(inode))
+               return;
++      if (ext2_inode_is_fast_symlink(inode))
++              return;
+       if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
+               return;
+@@ -917,8 +917,7 @@
+       unsigned long offset;
+       struct ext2_group_desc * gdp;
+-      if ((inode->i_ino != EXT2_ROOT_INO && inode->i_ino != EXT2_ACL_IDX_INO &&
+-           inode->i_ino != EXT2_ACL_DATA_INO &&
++      if ((inode->i_ino != EXT2_ROOT_INO &&
+            inode->i_ino < EXT2_FIRST_INO(inode->i_sb)) ||
+           inode->i_ino > le32_to_cpu(inode->i_sb->u.ext2_sb.s_es->s_inodes_count)) {
+               ext2_error (inode->i_sb, "ext2_read_inode",
+@@ -1004,10 +1003,7 @@
+       for (block = 0; block < EXT2_N_BLOCKS; block++)
+               inode->u.ext2_i.i_data[block] = raw_inode->i_block[block];
+-      if (inode->i_ino == EXT2_ACL_IDX_INO ||
+-          inode->i_ino == EXT2_ACL_DATA_INO)
+-              /* Nothing to do */ ;
+-      else if (S_ISREG(inode->i_mode)) {
++      if (S_ISREG(inode->i_mode)) {
+               inode->i_op = &ext2_file_inode_operations;
+               inode->i_fop = &ext2_file_operations;
+               inode->i_mapping->a_ops = &ext2_aops;
+@@ -1019,12 +1015,14 @@
+               if (ext2_inode_is_fast_symlink(inode))
+                       inode->i_op = &ext2_fast_symlink_inode_operations;
+               else {
+-                      inode->i_op = &page_symlink_inode_operations;
++                      inode->i_op = &ext2_symlink_inode_operations;
+                       inode->i_mapping->a_ops = &ext2_aops;
+               }
+-      } else 
++      } else {
++              inode->i_op = &ext2_special_inode_operations;
+               init_special_inode(inode, inode->i_mode,
+                                  le32_to_cpu(raw_inode->i_block[0]));
++      }
+       brelse (bh);
+       inode->i_attr_flags = 0;
+       ext2_set_inode_flags(inode);
+Index: linux-2.4.29/fs/ext2/namei.c
+===================================================================
+--- linux-2.4.29.orig/fs/ext2/namei.c  2005-04-07 18:54:50.000000000 +0300
++++ linux-2.4.29/fs/ext2/namei.c       2005-05-03 17:59:40.397121872 +0300
+@@ -31,6 +31,7 @@
+ #include <linux/fs.h>
+ #include <linux/ext2_fs.h>
++#include <linux/ext2_xattr.h>
+ #include <linux/pagemap.h>
+ /*
+@@ -136,7 +137,7 @@
+       if (l > sizeof (inode->u.ext2_i.i_data)) {
+               /* slow symlink */
+-              inode->i_op = &page_symlink_inode_operations;
++              inode->i_op = &ext2_symlink_inode_operations;
+               inode->i_mapping->a_ops = &ext2_aops;
+               err = block_symlink(inode, symname, l);
+               if (err)
+@@ -345,4 +346,15 @@
+       rmdir:          ext2_rmdir,
+       mknod:          ext2_mknod,
+       rename:         ext2_rename,
++      setxattr:       ext2_setxattr,
++      getxattr:       ext2_getxattr,
++      listxattr:      ext2_listxattr,
++      removexattr:    ext2_removexattr,
++};
++
++struct inode_operations ext2_special_inode_operations = {
++      setxattr:       ext2_setxattr,
++      getxattr:       ext2_getxattr,
++      listxattr:      ext2_listxattr,
++      removexattr:    ext2_removexattr,
+ };
+Index: linux-2.4.29/fs/ext2/super.c
+===================================================================
+--- linux-2.4.29.orig/fs/ext2/super.c  2005-04-07 18:54:16.000000000 +0300
++++ linux-2.4.29/fs/ext2/super.c       2005-05-03 17:59:40.400121416 +0300
+@@ -21,6 +21,7 @@
+ #include <linux/string.h>
+ #include <linux/fs.h>
+ #include <linux/ext2_fs.h>
++#include <linux/ext2_xattr.h>
+ #include <linux/slab.h>
+ #include <linux/init.h>
+ #include <linux/locks.h>
+@@ -125,6 +126,7 @@
+       int db_count;
+       int i;
++      ext2_xattr_put_super(sb);
+       if (!(sb->s_flags & MS_RDONLY)) {
+               struct ext2_super_block *es = EXT2_SB(sb)->s_es;
+@@ -175,6 +177,13 @@
+            this_char = strtok (NULL, ",")) {
+               if ((value = strchr (this_char, '=')) != NULL)
+                       *value++ = 0;
++#ifdef CONFIG_EXT2_FS_XATTR_USER
++              if (!strcmp (this_char, "user_xattr"))
++                      set_opt (*mount_options, XATTR_USER);
++              else if (!strcmp (this_char, "nouser_xattr"))
++                      clear_opt (*mount_options, XATTR_USER);
++              else
++#endif
+               if (!strcmp (this_char, "bsddf"))
+                       clear_opt (*mount_options, MINIX_DF);
+               else if (!strcmp (this_char, "nouid32")) {
+@@ -446,6 +455,9 @@
+           blocksize = BLOCK_SIZE;
+       sb->u.ext2_sb.s_mount_opt = 0;
++#ifdef CONFIG_EXT2_FS_XATTR_USER
++      /* set_opt (sb->u.ext2_sb.s_mount_opt, XATTR_USER); */
++#endif
+       if (!parse_options ((char *) data, &sb_block, &resuid, &resgid,
+           &sb->u.ext2_sb.s_mount_opt)) {
+               return NULL;
+@@ -840,12 +852,27 @@
+ static int __init init_ext2_fs(void)
+ {
+-        return register_filesystem(&ext2_fs_type);
++      int error = init_ext2_xattr();
++      if (error)
++              return error;
++      error = init_ext2_xattr_user();
++      if (error)
++              goto fail;
++      error = register_filesystem(&ext2_fs_type);
++      if (!error)
++              return 0;
++
++      exit_ext2_xattr_user();
++fail:
++      exit_ext2_xattr();
++      return error;
+ }
+ static void __exit exit_ext2_fs(void)
+ {
+       unregister_filesystem(&ext2_fs_type);
++      exit_ext2_xattr_user();
++      exit_ext2_xattr();
+ }
+ EXPORT_NO_SYMBOLS;
+Index: linux-2.4.29/fs/ext2/symlink.c
+===================================================================
+--- linux-2.4.29.orig/fs/ext2/symlink.c        2005-04-07 18:52:53.000000000 +0300
++++ linux-2.4.29/fs/ext2/symlink.c     2005-05-03 17:59:40.400121416 +0300
+@@ -19,6 +19,7 @@
+ #include <linux/fs.h>
+ #include <linux/ext2_fs.h>
++#include <linux/ext2_xattr.h>
+ static int ext2_readlink(struct dentry *dentry, char *buffer, int buflen)
+ {
+@@ -32,7 +33,20 @@
+       return vfs_follow_link(nd, s);
+ }
++struct inode_operations ext2_symlink_inode_operations = {
++      readlink:       page_readlink,
++      follow_link:    page_follow_link,
++      setxattr:       ext2_setxattr,
++      getxattr:       ext2_getxattr,
++      listxattr:      ext2_listxattr,
++      removexattr:    ext2_removexattr,
++};
++
+ struct inode_operations ext2_fast_symlink_inode_operations = {
+       readlink:       ext2_readlink,
+       follow_link:    ext2_follow_link,
++      setxattr:       ext2_setxattr,
++      getxattr:       ext2_getxattr,
++      listxattr:      ext2_listxattr,
++      removexattr:    ext2_removexattr,
+ };
+Index: linux-2.4.29/fs/ext2/xattr.c
+===================================================================
+--- linux-2.4.29.orig/fs/ext2/xattr.c  2005-05-03 17:59:40.233146800 +0300
++++ linux-2.4.29/fs/ext2/xattr.c       2005-05-03 17:59:40.405120656 +0300
+@@ -0,0 +1,1212 @@
++/*
++ * linux/fs/ext2/xattr.c
++ *
++ * Copyright (C) 2001 by Andreas Gruenbacher, <a.gruenbacher@computer.org>
++ *
++ * Fix by Harrison Xing <harrison@mountainviewdata.com>.
++ * Extended attributes for symlinks and special files added per
++ *  suggestion of Luka Renko <luka.renko@hermes.si>.
++ */
++
++/*
++ * Extended attributes are stored on disk blocks allocated outside of
++ * any inode. The i_file_acl field is then made to point to this allocated
++ * block. If all extended attributes of an inode are identical, these
++ * inodes may share the same extended attribute block. Such situations
++ * are automatically detected by keeping a cache of recent attribute block
++ * numbers and hashes over the block's contents in memory.
++ *
++ *
++ * Extended attribute block layout:
++ *
++ *   +------------------+
++ *   | header           |
++ *   | entry 1          | |
++ *   | entry 2          | | growing downwards
++ *   | entry 3          | v
++ *   | four null bytes  |
++ *   | . . .            |
++ *   | value 1          | ^
++ *   | value 3          | | growing upwards
++ *   | value 2          | |
++ *   +------------------+
++ *
++ * The block header is followed by multiple entry descriptors. These entry
++ * descriptors are variable in size, and alligned to EXT2_XATTR_PAD
++ * byte boundaries. The entry descriptors are sorted by attribute name,
++ * so that two extended attribute blocks can be compared efficiently.
++ *
++ * Attribute values are aligned to the end of the block, stored in
++ * no specific order. They are also padded to EXT2_XATTR_PAD byte
++ * boundaries. No additional gaps are left between them.
++ *
++ * Locking strategy
++ * ----------------
++ * The VFS already holds the BKL and the inode->i_sem semaphore when any of
++ * the xattr inode operations are called, so we are guaranteed that only one
++ * processes accesses extended attributes of an inode at any time.
++ *
++ * For writing we also grab the ext2_xattr_sem semaphore. This ensures that
++ * only a single process is modifying an extended attribute block, even
++ * if the block is shared among inodes.
++ *
++ * Note for porting to 2.5
++ * -----------------------
++ * The BKL will no longer be held in the xattr inode operations.
++ */
++
++#include <linux/module.h>
++#include <linux/locks.h>
++#include <linux/slab.h>
++#include <linux/fs.h>
++#include <linux/ext2_fs.h>
++#include <linux/ext2_xattr.h>
++#include <linux/mbcache.h>
++#include <linux/quotaops.h>
++#include <asm/semaphore.h>
++#include <linux/compatmac.h>
++
++/* These symbols may be needed by a module. */
++EXPORT_SYMBOL(ext2_xattr_register);
++EXPORT_SYMBOL(ext2_xattr_unregister);
++EXPORT_SYMBOL(ext2_xattr_get);
++EXPORT_SYMBOL(ext2_xattr_list);
++EXPORT_SYMBOL(ext2_xattr_set);
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
++# define mark_buffer_dirty(bh) mark_buffer_dirty(bh, 1)
++#endif
++
++#define HDR(bh) ((struct ext2_xattr_header *)((bh)->b_data))
++#define ENTRY(ptr) ((struct ext2_xattr_entry *)(ptr))
++#define FIRST_ENTRY(bh) ENTRY(HDR(bh)+1)
++#define IS_LAST_ENTRY(entry) (*(__u32 *)(entry) == 0)
++
++#ifdef EXT2_XATTR_DEBUG
++# define ea_idebug(inode, f...) do { \
++              printk(KERN_DEBUG "inode %s:%ld: ", \
++                      kdevname(inode->i_dev), inode->i_ino); \
++              printk(f); \
++              printk("\n"); \
++      } while (0)
++# define ea_bdebug(bh, f...) do { \
++              printk(KERN_DEBUG "block %s:%ld: ", \
++                      kdevname(bh->b_dev), bh->b_blocknr); \
++              printk(f); \
++              printk("\n"); \
++      } while (0)
++#else
++# define ea_idebug(f...)
++# define ea_bdebug(f...)
++#endif
++
++static int ext2_xattr_set2(struct inode *, struct buffer_head *,
++                         struct ext2_xattr_header *);
++
++#ifdef CONFIG_EXT2_FS_XATTR_SHARING
++
++static int ext2_xattr_cache_insert(struct buffer_head *);
++static struct buffer_head *ext2_xattr_cache_find(struct inode *,
++                                               struct ext2_xattr_header *);
++static void ext2_xattr_cache_remove(struct buffer_head *);
++static void ext2_xattr_rehash(struct ext2_xattr_header *,
++                            struct ext2_xattr_entry *);
++
++static struct mb_cache *ext2_xattr_cache;
++
++#else
++# define ext2_xattr_cache_insert(bh) 0
++# define ext2_xattr_cache_find(inode, header) NULL
++# define ext2_xattr_cache_remove(bh) while(0) {}
++# define ext2_xattr_rehash(header, entry) while(0) {}
++#endif
++
++/*
++ * If a file system does not share extended attributes among inodes,
++ * we should not need the ext2_xattr_sem semaphore. However, the
++ * filesystem may still contain shared blocks, so we always take
++ * the lock.
++ */
++
++DECLARE_MUTEX(ext2_xattr_sem);
++
++static inline int
++ext2_xattr_new_block(struct inode *inode, int * errp, int force)
++{
++      struct super_block *sb = inode->i_sb;
++      int goal = le32_to_cpu(EXT2_SB(sb)->s_es->s_first_data_block) +
++              EXT2_I(inode)->i_block_group * EXT2_BLOCKS_PER_GROUP(sb);
++
++      /* How can we enforce the allocation? */
++      int block = ext2_new_block(inode, goal, 0, 0, errp);
++#ifdef OLD_QUOTAS
++      if (!*errp)
++              inode->i_blocks += inode->i_sb->s_blocksize >> 9;
++#endif
++      return block;
++}
++
++static inline int
++ext2_xattr_quota_alloc(struct inode *inode, int force)
++{
++      /* How can we enforce the allocation? */
++#ifdef OLD_QUOTAS
++      int error = DQUOT_ALLOC_BLOCK(inode->i_sb, inode, 1);
++      if (!error)
++              inode->i_blocks += inode->i_sb->s_blocksize >> 9;
++#else
++      int error = DQUOT_ALLOC_BLOCK(inode, 1);
++#endif
++      return error;
++}
++
++#ifdef OLD_QUOTAS
++
++static inline void
++ext2_xattr_quota_free(struct inode *inode)
++{
++      DQUOT_FREE_BLOCK(inode->i_sb, inode, 1);
++      inode->i_blocks -= inode->i_sb->s_blocksize >> 9;
++}
++
++static inline void
++ext2_xattr_free_block(struct inode * inode, unsigned long block)
++{
++      ext2_free_blocks(inode, block, 1);
++      inode->i_blocks -= inode->i_sb->s_blocksize >> 9;
++}
++
++#else
++# define ext2_xattr_quota_free(inode) \
++      DQUOT_FREE_BLOCK(inode, 1)
++# define ext2_xattr_free_block(inode, block) \
++      ext2_free_blocks(inode, block, 1)
++#endif
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,18)
++
++static inline struct buffer_head *
++sb_bread(struct super_block *sb, int block)
++{
++      return bread(sb->s_dev, block, sb->s_blocksize);
++}
++
++static inline struct buffer_head *
++sb_getblk(struct super_block *sb, int block)
++{
++      return getblk(sb->s_dev, block, sb->s_blocksize);
++}
++
++#endif
++
++struct ext2_xattr_handler *ext2_xattr_handlers[EXT2_XATTR_INDEX_MAX];
++rwlock_t ext2_handler_lock = RW_LOCK_UNLOCKED;
++
++int
++ext2_xattr_register(int name_index, struct ext2_xattr_handler *handler)
++{
++      int error = -EINVAL;
++
++      if (name_index > 0 && name_index <= EXT2_XATTR_INDEX_MAX) {
++              write_lock(&ext2_handler_lock);
++              if (!ext2_xattr_handlers[name_index-1]) {
++                      ext2_xattr_handlers[name_index-1] = handler;
++                      error = 0;
++              }
++              write_unlock(&ext2_handler_lock);
++      }
++      return error;
++}
++
++void
++ext2_xattr_unregister(int name_index, struct ext2_xattr_handler *handler)
++{
++      if (name_index > 0 && name_index <= EXT2_XATTR_INDEX_MAX) {
++              write_lock(&ext2_handler_lock);
++              ext2_xattr_handlers[name_index-1] = NULL;
++              write_unlock(&ext2_handler_lock);
++      }
++}
++
++static inline const char *
++strcmp_prefix(const char *a, const char *a_prefix)
++{
++      while (*a_prefix && *a == *a_prefix) {
++              a++;
++              a_prefix++;
++      }
++      return *a_prefix ? NULL : a;
++}
++
++/*
++ * Decode the extended attribute name, and translate it into
++ * the name_index and name suffix.
++ */
++static struct ext2_xattr_handler *
++ext2_xattr_resolve_name(const char **name)
++{
++      struct ext2_xattr_handler *handler = NULL;
++      int i;
++
++      if (!*name)
++              return NULL;
++      read_lock(&ext2_handler_lock);
++      for (i=0; i<EXT2_XATTR_INDEX_MAX; i++) {
++              if (ext2_xattr_handlers[i]) {
++                      const char *n = strcmp_prefix(*name,
++                              ext2_xattr_handlers[i]->prefix);
++                      if (n) {
++                              handler = ext2_xattr_handlers[i];
++                              *name = n;
++                              break;
++                      }
++              }
++      }
++      read_unlock(&ext2_handler_lock);
++      return handler;
++}
++
++static inline struct ext2_xattr_handler *
++ext2_xattr_handler(int name_index)
++{
++      struct ext2_xattr_handler *handler = NULL;
++      if (name_index > 0 && name_index <= EXT2_XATTR_INDEX_MAX) {
++              read_lock(&ext2_handler_lock);
++              handler = ext2_xattr_handlers[name_index-1];
++              read_unlock(&ext2_handler_lock);
++      }
++      return handler;
++}
++
++/*
++ * Inode operation getxattr()
++ *
++ * dentry->d_inode->i_sem down
++ * BKL held [before 2.5.x]
++ */
++ssize_t
++ext2_getxattr(struct dentry *dentry, const char *name,
++            void *buffer, size_t size)
++{
++      struct ext2_xattr_handler *handler;
++      struct inode *inode = dentry->d_inode;
++
++      handler = ext2_xattr_resolve_name(&name);
++      if (!handler)
++              return -ENOTSUP;
++      return handler->get(inode, name, buffer, size);
++}
++
++/*
++ * Inode operation listxattr()
++ *
++ * dentry->d_inode->i_sem down
++ * BKL held [before 2.5.x]
++ */
++ssize_t
++ext2_listxattr(struct dentry *dentry, char *buffer, size_t size)
++{
++      return ext2_xattr_list(dentry->d_inode, buffer, size);
++}
++
++/*
++ * Inode operation setxattr()
++ *
++ * dentry->d_inode->i_sem down
++ * BKL held [before 2.5.x]
++ */
++int
++ext2_setxattr(struct dentry *dentry, const char *name,
++            const void *value, size_t size, int flags)
++{
++      struct ext2_xattr_handler *handler;
++      struct inode *inode = dentry->d_inode;
++
++      if (size == 0)
++              value = "";  /* empty EA, do not remove */
++      handler = ext2_xattr_resolve_name(&name);
++      if (!handler)
++              return -ENOTSUP;
++      return handler->set(inode, name, value, size, flags);
++}
++
++/*
++ * Inode operation removexattr()
++ *
++ * dentry->d_inode->i_sem down
++ * BKL held [before 2.5.x]
++ */
++int
++ext2_removexattr(struct dentry *dentry, const char *name)
++{
++      struct ext2_xattr_handler *handler;
++      struct inode *inode = dentry->d_inode;
++
++      handler = ext2_xattr_resolve_name(&name);
++      if (!handler)
++              return -ENOTSUP;
++      return handler->set(inode, name, NULL, 0, XATTR_REPLACE);
++}
++
++/*
++ * ext2_xattr_get()
++ *
++ * Copy an extended attribute into the buffer
++ * provided, or compute the buffer size required.
++ * Buffer is NULL to compute the size of the buffer required.
++ *
++ * Returns a negative error number on failure, or the number of bytes
++ * used / required on success.
++ */
++int
++ext2_xattr_get(struct inode *inode, int name_index, const char *name,
++             void *buffer, size_t buffer_size)
++{
++      struct buffer_head *bh = NULL;
++      struct ext2_xattr_entry *entry;
++      unsigned int block, size;
++      char *end;
++      int name_len, error;
++
++      ea_idebug(inode, "name=%d.%s, buffer=%p, buffer_size=%ld",
++                name_index, name, buffer, (long)buffer_size);
++
++      if (name == NULL)
++              return -EINVAL;
++      if (!EXT2_I(inode)->i_file_acl)
++              return -ENOATTR;
++      block = EXT2_I(inode)->i_file_acl;
++      ea_idebug(inode, "reading block %d", block);
++      bh = sb_bread(inode->i_sb, block);
++      if (!bh)
++              return -EIO;
++      ea_bdebug(bh, "b_count=%d, refcount=%d",
++              atomic_read(&(bh->b_count)), le32_to_cpu(HDR(bh)->h_refcount));
++      end = bh->b_data + bh->b_size;
++      if (HDR(bh)->h_magic != cpu_to_le32(EXT2_XATTR_MAGIC) ||
++          HDR(bh)->h_blocks != cpu_to_le32(1)) {
++bad_block:    ext2_error(inode->i_sb, "ext2_xattr_get",
++                      "inode %ld: bad block %d", inode->i_ino, block);
++              error = -EIO;
++              goto cleanup;
++      }
++      /* find named attribute */
++      name_len = strlen(name);
++
++      error = -ERANGE;
++      if (name_len > 255)
++              goto cleanup;
++      entry = FIRST_ENTRY(bh);
++      while (!IS_LAST_ENTRY(entry)) {
++              struct ext2_xattr_entry *next =
++                      EXT2_XATTR_NEXT(entry);
++              if ((char *)next >= end)
++                      goto bad_block;
++              if (name_index == entry->e_name_index &&
++                  name_len == entry->e_name_len &&
++                  memcmp(name, entry->e_name, name_len) == 0)
++                      goto found;
++              entry = next;
++      }
++      /* Check the remaining name entries */
++      while (!IS_LAST_ENTRY(entry)) {
++              struct ext2_xattr_entry *next =
++                      EXT2_XATTR_NEXT(entry);
++              if ((char *)next >= end)
++                      goto bad_block;
++              entry = next;
++      }
++      if (ext2_xattr_cache_insert(bh))
++              ea_idebug(inode, "cache insert failed");
++      error = -ENOATTR;
++      goto cleanup;
++found:
++      /* check the buffer size */
++      if (entry->e_value_block != 0)
++              goto bad_block;
++      size = le32_to_cpu(entry->e_value_size);
++      if (size > inode->i_sb->s_blocksize ||
++          le16_to_cpu(entry->e_value_offs) + size > inode->i_sb->s_blocksize)
++              goto bad_block;
++
++      if (ext2_xattr_cache_insert(bh))
++              ea_idebug(inode, "cache insert failed");
++      if (buffer) {
++              error = -ERANGE;
++              if (size > buffer_size)
++                      goto cleanup;
++              /* return value of attribute */
++              memcpy(buffer, bh->b_data + le16_to_cpu(entry->e_value_offs),
++                      size);
++      }
++      error = size;
++
++cleanup:
++      brelse(bh);
++
++      return error;
++}
++
++/*
++ * ext2_xattr_list()
++ *
++ * Copy a list of attribute names into the buffer
++ * provided, or compute the buffer size required.
++ * Buffer is NULL to compute the size of the buffer required.
++ *
++ * Returns a negative error number on failure, or the number of bytes
++ * used / required on success.
++ */
++int
++ext2_xattr_list(struct inode *inode, char *buffer, size_t buffer_size)
++{
++      struct buffer_head *bh = NULL;
++      struct ext2_xattr_entry *entry;
++      unsigned int block, size = 0;
++      char *buf, *end;
++      int error;
++
++      ea_idebug(inode, "buffer=%p, buffer_size=%ld",
++                buffer, (long)buffer_size);
++
++      if (!EXT2_I(inode)->i_file_acl)
++              return 0;
++      block = EXT2_I(inode)->i_file_acl;
++      ea_idebug(inode, "reading block %d", block);
++      bh = sb_bread(inode->i_sb, block);
++      if (!bh)
++              return -EIO;
++      ea_bdebug(bh, "b_count=%d, refcount=%d",
++              atomic_read(&(bh->b_count)), le32_to_cpu(HDR(bh)->h_refcount));
++      end = bh->b_data + bh->b_size;
++      if (HDR(bh)->h_magic != cpu_to_le32(EXT2_XATTR_MAGIC) ||
++          HDR(bh)->h_blocks != cpu_to_le32(1)) {
++bad_block:    ext2_error(inode->i_sb, "ext2_xattr_list",
++                      "inode %ld: bad block %d", inode->i_ino, block);
++              error = -EIO;
++              goto cleanup;
++      }
++      /* compute the size required for the list of attribute names */
++      for (entry = FIRST_ENTRY(bh); !IS_LAST_ENTRY(entry);
++           entry = EXT2_XATTR_NEXT(entry)) {
++              struct ext2_xattr_handler *handler;
++              struct ext2_xattr_entry *next =
++                      EXT2_XATTR_NEXT(entry);
++              if ((char *)next >= end)
++                      goto bad_block;
++
++              handler = ext2_xattr_handler(entry->e_name_index);
++              if (handler)
++                      size += handler->list(NULL, inode, entry->e_name,
++                                            entry->e_name_len);
++      }
++
++      if (ext2_xattr_cache_insert(bh))
++              ea_idebug(inode, "cache insert failed");
++      if (!buffer) {
++              error = size;
++              goto cleanup;
++      } else {
++              error = -ERANGE;
++              if (size > buffer_size)
++                      goto cleanup;
++      }
++
++      /* list the attribute names */
++      buf = buffer;
++      for (entry = FIRST_ENTRY(bh); !IS_LAST_ENTRY(entry);
++           entry = EXT2_XATTR_NEXT(entry)) {
++              struct ext2_xattr_handler *handler;
++              
++              handler = ext2_xattr_handler(entry->e_name_index);
++              if (handler)
++                      buf += handler->list(buf, inode, entry->e_name,
++                                           entry->e_name_len);
++      }
++      error = size;
++
++cleanup:
++      brelse(bh);
++
++      return error;
++}
++
++/*
++ * If the EXT2_FEATURE_COMPAT_EXT_ATTR feature of this file system is
++ * not set, set it.
++ */
++static void ext2_xattr_update_super_block(struct super_block *sb)
++{
++      if (EXT2_HAS_COMPAT_FEATURE(sb, EXT2_FEATURE_COMPAT_EXT_ATTR))
++              return;
++
++      lock_super(sb);
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
++      EXT2_SB(sb)->s_feature_compat |= EXT2_FEATURE_COMPAT_EXT_ATTR;
++#endif
++      EXT2_SB(sb)->s_es->s_feature_compat |=
++              cpu_to_le32(EXT2_FEATURE_COMPAT_EXT_ATTR);
++      sb->s_dirt = 1;
++      mark_buffer_dirty(EXT2_SB(sb)->s_sbh);
++      unlock_super(sb);
++}
++
++/*
++ * ext2_xattr_set()
++ *
++ * Create, replace or remove an extended attribute for this inode. Buffer
++ * is NULL to remove an existing extended attribute, and non-NULL to
++ * either replace an existing extended attribute, or create a new extended
++ * attribute. The flags XATTR_REPLACE and XATTR_CREATE
++ * specify that an extended attribute must exist and must not exist
++ * previous to the call, respectively.
++ *
++ * Returns 0, or a negative error number on failure.
++ */
++int
++ext2_xattr_set(struct inode *inode, int name_index, const char *name,
++             const void *value, size_t value_len, int flags)
++{
++      struct super_block *sb = inode->i_sb;
++      struct buffer_head *bh = NULL;
++      struct ext2_xattr_header *header = NULL;
++      struct ext2_xattr_entry *here, *last;
++      unsigned int name_len;
++      int block = EXT2_I(inode)->i_file_acl;
++      int min_offs = sb->s_blocksize, not_found = 1, free, error;
++      char *end;
++      
++      /*
++       * header -- Points either into bh, or to a temporarily
++       *           allocated buffer.
++       * here -- The named entry found, or the place for inserting, within
++       *         the block pointed to by header.
++       * last -- Points right after the last named entry within the block
++       *         pointed to by header.
++       * min_offs -- The offset of the first value (values are aligned
++       *             towards the end of the block).
++       * end -- Points right after the block pointed to by header.
++       */
++      
++      ea_idebug(inode, "name=%d.%s, value=%p, value_len=%ld",
++                name_index, name, value, (long)value_len);
++
++      if (IS_RDONLY(inode))
++              return -EROFS;
++      if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
++              return -EPERM;
++      if (value == NULL)
++              value_len = 0;
++      if (name == NULL)
++              return -EINVAL;
++      name_len = strlen(name);
++      if (name_len > 255 || value_len > sb->s_blocksize)
++              return -ERANGE;
++      down(&ext2_xattr_sem);
++
++      if (block) {
++              /* The inode already has an extended attribute block. */
++
++              bh = sb_bread(sb, block);
++              error = -EIO;
++              if (!bh)
++                      goto cleanup;
++              ea_bdebug(bh, "b_count=%d, refcount=%d",
++                      atomic_read(&(bh->b_count)),
++                      le32_to_cpu(HDR(bh)->h_refcount));
++              header = HDR(bh);
++              end = bh->b_data + bh->b_size;
++              if (header->h_magic != cpu_to_le32(EXT2_XATTR_MAGIC) ||
++                  header->h_blocks != cpu_to_le32(1)) {
++bad_block:            ext2_error(sb, "ext2_xattr_set",
++                              "inode %ld: bad block %d", inode->i_ino, block);
++                      error = -EIO;
++                      goto cleanup;
++              }
++              /* Find the named attribute. */
++              here = FIRST_ENTRY(bh);
++              while (!IS_LAST_ENTRY(here)) {
++                      struct ext2_xattr_entry *next = EXT2_XATTR_NEXT(here);
++                      if ((char *)next >= end)
++                              goto bad_block;
++                      if (!here->e_value_block && here->e_value_size) {
++                              int offs = le16_to_cpu(here->e_value_offs);
++                              if (offs < min_offs)
++                                      min_offs = offs;
++                      }
++                      not_found = name_index - here->e_name_index;
++                      if (!not_found)
++                              not_found = name_len - here->e_name_len;
++                      if (!not_found)
++                              not_found = memcmp(name, here->e_name,name_len);
++                      if (not_found <= 0)
++                              break;
++                      here = next;
++              }
++              last = here;
++              /* We still need to compute min_offs and last. */
++              while (!IS_LAST_ENTRY(last)) {
++                      struct ext2_xattr_entry *next = EXT2_XATTR_NEXT(last);
++                      if ((char *)next >= end)
++                              goto bad_block;
++                      if (!last->e_value_block && last->e_value_size) {
++                              int offs = le16_to_cpu(last->e_value_offs);
++                              if (offs < min_offs)
++                                      min_offs = offs;
++                      }
++                      last = next;
++              }
++
++              /* Check whether we have enough space left. */
++              free = min_offs - ((char*)last - (char*)header) - sizeof(__u32);
++      } else {
++              /* We will use a new extended attribute block. */
++              free = sb->s_blocksize -
++                      sizeof(struct ext2_xattr_header) - sizeof(__u32);
++              here = last = NULL;  /* avoid gcc uninitialized warning. */
++      }
++
++      if (not_found) {
++              /* Request to remove a nonexistent attribute? */
++              error = -ENOATTR;
++              if (flags & XATTR_REPLACE)
++                      goto cleanup;
++              error = 0;
++              if (value == NULL)
++                      goto cleanup;
++              else
++                      free -= EXT2_XATTR_LEN(name_len);
++      } else {
++              /* Request to create an existing attribute? */
++              error = -EEXIST;
++              if (flags & XATTR_CREATE)
++                      goto cleanup;
++              if (!here->e_value_block && here->e_value_size) {
++                      unsigned int size = le32_to_cpu(here->e_value_size);
++
++                      if (le16_to_cpu(here->e_value_offs) + size > 
++                          sb->s_blocksize || size > sb->s_blocksize)
++                              goto bad_block;
++                      free += EXT2_XATTR_SIZE(size);
++              }
++      }
++      free -= EXT2_XATTR_SIZE(value_len);
++      error = -ENOSPC;
++      if (free < 0)
++              goto cleanup;
++
++      /* Here we know that we can set the new attribute. */
++
++      if (header) {
++              if (header->h_refcount == cpu_to_le32(1)) {
++                      ea_bdebug(bh, "modifying in-place");
++                      ext2_xattr_cache_remove(bh);
++              } else {
++                      int offset;
++
++                      ea_bdebug(bh, "cloning");
++                      header = kmalloc(bh->b_size, GFP_KERNEL);
++                      error = -ENOMEM;
++                      if (header == NULL)
++                              goto cleanup;
++                      memcpy(header, HDR(bh), bh->b_size);
++                      header->h_refcount = cpu_to_le32(1);
++                      offset = (char *)header - bh->b_data;
++                      here = ENTRY((char *)here + offset);
++                      last = ENTRY((char *)last + offset);
++              }
++      } else {
++              /* Allocate a buffer where we construct the new block. */
++              header = kmalloc(sb->s_blocksize, GFP_KERNEL);
++              error = -ENOMEM;
++              if (header == NULL)
++                      goto cleanup;
++              memset(header, 0, sb->s_blocksize);
++              end = (char *)header + sb->s_blocksize;
++              header->h_magic = cpu_to_le32(EXT2_XATTR_MAGIC);
++              header->h_blocks = header->h_refcount = cpu_to_le32(1);
++              last = here = ENTRY(header+1);
++      }
++
++      if (not_found) {
++              /* Insert the new name. */
++              int size = EXT2_XATTR_LEN(name_len);
++              int rest = (char *)last - (char *)here;
++              memmove((char *)here + size, here, rest);
++              memset(here, 0, size);
++              here->e_name_index = name_index;
++              here->e_name_len = name_len;
++              memcpy(here->e_name, name, name_len);
++      } else {
++              /* Remove the old value. */
++              if (!here->e_value_block && here->e_value_size) {
++                      char *first_val = (char *)header + min_offs;
++                      int offs = le16_to_cpu(here->e_value_offs);
++                      char *val = (char *)header + offs;
++                      size_t size = EXT2_XATTR_SIZE(
++                              le32_to_cpu(here->e_value_size));
++                      memmove(first_val + size, first_val, val - first_val);
++                      memset(first_val, 0, size);
++                      here->e_value_offs = 0;
++                      min_offs += size;
++
++                      /* Adjust all value offsets. */
++                      last = ENTRY(header+1);
++                      while (!IS_LAST_ENTRY(last)) {
++                              int o = le16_to_cpu(last->e_value_offs);
++                              if (!last->e_value_block && o < offs)
++                                      last->e_value_offs =
++                                              cpu_to_le16(o + size);
++                              last = EXT2_XATTR_NEXT(last);
++                      }
++              }
++              if (value == NULL) {
++                      /* Remove this attribute. */
++                      if (EXT2_XATTR_NEXT(ENTRY(header+1)) == last) {
++                              /* This block is now empty. */
++                              error = ext2_xattr_set2(inode, bh, NULL);
++                              goto cleanup;
++                      } else {
++                              /* Remove the old name. */
++                              int size = EXT2_XATTR_LEN(name_len);
++                              last = ENTRY((char *)last - size);
++                              memmove(here, (char*)here + size,
++                                      (char*)last - (char*)here);
++                              memset(last, 0, size);
++                      }
++              }
++      }
++
++      if (value != NULL) {
++              /* Insert the new value. */
++              here->e_value_size = cpu_to_le32(value_len);
++              if (value_len) {
++                      size_t size = EXT2_XATTR_SIZE(value_len);
++                      char *val = (char *)header + min_offs - size;
++                      here->e_value_offs =
++                              cpu_to_le16((char *)val - (char *)header);
++                      memset(val + size - EXT2_XATTR_PAD, 0,
++                             EXT2_XATTR_PAD); /* Clear the pad bytes. */
++                      memcpy(val, value, value_len);
++              }
++      }
++      ext2_xattr_rehash(header, here);
++
++      error = ext2_xattr_set2(inode, bh, header);
++
++cleanup:
++      brelse(bh);
++      if (!(bh && header == HDR(bh)))
++              kfree(header);
++      up(&ext2_xattr_sem);
++
++      return error;
++}
++
++/*
++ * Second half of ext2_xattr_set(): Update the file system.
++ */
++static int
++ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh,
++              struct ext2_xattr_header *header)
++{
++      struct super_block *sb = inode->i_sb;
++      struct buffer_head *new_bh = NULL;
++      int error;
++
++      if (header) {
++              new_bh = ext2_xattr_cache_find(inode, header);
++              if (new_bh) {
++                      /*
++                       * We found an identical block in the cache.
++                       * The old block will be released after updating
++                       * the inode.
++                       */
++                      ea_bdebug(old_bh, "reusing block %ld",
++                              new_bh->b_blocknr);
++                      
++                      error = -EDQUOT;
++                      if (ext2_xattr_quota_alloc(inode, 1))
++                              goto cleanup;
++                      
++                      HDR(new_bh)->h_refcount = cpu_to_le32(
++                              le32_to_cpu(HDR(new_bh)->h_refcount) + 1);
++                      ea_bdebug(new_bh, "refcount now=%d",
++                              le32_to_cpu(HDR(new_bh)->h_refcount));
++              } else if (old_bh && header == HDR(old_bh)) {
++                      /* Keep this block. */
++                      new_bh = old_bh;
++                      (void)ext2_xattr_cache_insert(new_bh);
++              } else {
++                      /* We need to allocate a new block */
++                      int force = EXT2_I(inode)->i_file_acl != 0;
++                      int block = ext2_xattr_new_block(inode, &error, force);
++                      if (error)
++                              goto cleanup;
++                      ea_idebug(inode, "creating block %d", block);
++
++                      new_bh = sb_getblk(sb, block);
++                      if (!new_bh) {
++                              ext2_xattr_free_block(inode, block);
++                              error = -EIO;
++                              goto cleanup;
++                      }
++                      lock_buffer(new_bh);
++                      memcpy(new_bh->b_data, header, new_bh->b_size);
++                      mark_buffer_uptodate(new_bh, 1);
++                      unlock_buffer(new_bh);
++                      (void)ext2_xattr_cache_insert(new_bh);
++                      
++                      ext2_xattr_update_super_block(sb);
++              }
++              mark_buffer_dirty(new_bh);
++              if (IS_SYNC(inode)) {
++                      ll_rw_block(WRITE, 1, &new_bh);
++                      wait_on_buffer(new_bh); 
++                      error = -EIO;
++                      if (buffer_req(new_bh) && !buffer_uptodate(new_bh))
++                              goto cleanup;
++              }
++      }
++
++      /* Update the inode. */
++      EXT2_I(inode)->i_file_acl = new_bh ? new_bh->b_blocknr : 0;
++      inode->i_ctime = CURRENT_TIME;
++      if (IS_SYNC(inode)) {
++              error = ext2_sync_inode (inode);
++              if (error)
++                      goto cleanup;
++      } else
++              mark_inode_dirty(inode);
++
++      error = 0;
++      if (old_bh && old_bh != new_bh) {
++              /*
++               * If there was an old block, and we are not still using it,
++               * we now release the old block.
++              */
++              unsigned int refcount = le32_to_cpu(HDR(old_bh)->h_refcount);
++
++              if (refcount == 1) {
++                      /* Free the old block. */
++                      ea_bdebug(old_bh, "freeing");
++                      ext2_xattr_free_block(inode, old_bh->b_blocknr);
++                      mark_buffer_clean(old_bh);
++              } else {
++                      /* Decrement the refcount only. */
++                      refcount--;
++                      HDR(old_bh)->h_refcount = cpu_to_le32(refcount);
++                      ext2_xattr_quota_free(inode);
++                      mark_buffer_dirty(old_bh);
++                      ea_bdebug(old_bh, "refcount now=%d", refcount);
++              }
++      }
++
++cleanup:
++      if (old_bh != new_bh)
++              brelse(new_bh);
++
++      return error;
++}
++
++/*
++ * ext2_xattr_delete_inode()
++ *
++ * Free extended attribute resources associated with this inode. This
++ * is called immediately before an inode is freed.
++ */
++void
++ext2_xattr_delete_inode(struct inode *inode)
++{
++      struct buffer_head *bh;
++      unsigned int block = EXT2_I(inode)->i_file_acl;
++
++      if (!block)
++              return;
++      down(&ext2_xattr_sem);
++
++      bh = sb_bread(inode->i_sb, block);
++      if (!bh) {
++              ext2_error(inode->i_sb, "ext2_xattr_delete_inode",
++                      "inode %ld: block %d read error", inode->i_ino, block);
++              goto cleanup;
++      }
++      ea_bdebug(bh, "b_count=%d", atomic_read(&(bh->b_count)));
++      if (HDR(bh)->h_magic != cpu_to_le32(EXT2_XATTR_MAGIC) ||
++          HDR(bh)->h_blocks != cpu_to_le32(1)) {
++              ext2_error(inode->i_sb, "ext2_xattr_delete_inode",
++                      "inode %ld: bad block %d", inode->i_ino, block);
++              goto cleanup;
++      }
++      ea_bdebug(bh, "refcount now=%d", le32_to_cpu(HDR(bh)->h_refcount) - 1);
++      if (HDR(bh)->h_refcount == cpu_to_le32(1)) {
++              ext2_xattr_cache_remove(bh);
++              ext2_xattr_free_block(inode, block);
++              bforget(bh);
++              bh = NULL;
++      } else {
++              HDR(bh)->h_refcount = cpu_to_le32(
++                      le32_to_cpu(HDR(bh)->h_refcount) - 1);
++              mark_buffer_dirty(bh);
++              if (IS_SYNC(inode)) {
++                      ll_rw_block(WRITE, 1, &bh);
++                      wait_on_buffer(bh);
++              }
++              ext2_xattr_quota_free(inode);
++      }
++      EXT2_I(inode)->i_file_acl = 0;
++
++cleanup:
++      brelse(bh);
++      up(&ext2_xattr_sem);
++}
++
++/*
++ * ext2_xattr_put_super()
++ *
++ * This is called when a file system is unmounted.
++ */
++void
++ext2_xattr_put_super(struct super_block *sb)
++{
++#ifdef CONFIG_EXT2_FS_XATTR_SHARING
++      mb_cache_shrink(ext2_xattr_cache, sb->s_dev);
++#endif
++}
++
++#ifdef CONFIG_EXT2_FS_XATTR_SHARING
++
++/*
++ * ext2_xattr_cache_insert()
++ *
++ * Create a new entry in the extended attribute cache, and insert
++ * it unless such an entry is already in the cache.
++ *
++ * Returns 0, or a negative error number on failure.
++ */
++static int
++ext2_xattr_cache_insert(struct buffer_head *bh)
++{
++      __u32 hash = le32_to_cpu(HDR(bh)->h_hash);
++      struct mb_cache_entry *ce;
++      int error;
++
++      ce = mb_cache_entry_alloc(ext2_xattr_cache);
++      if (!ce)
++              return -ENOMEM;
++      error = mb_cache_entry_insert(ce, bh->b_dev, bh->b_blocknr, &hash);
++      if (error) {
++              mb_cache_entry_free(ce);
++              if (error == -EBUSY) {
++                      ea_bdebug(bh, "already in cache (%d cache entries)",
++                              atomic_read(&ext2_xattr_cache->c_entry_count));
++                      error = 0;
++              }
++      } else {
++              ea_bdebug(bh, "inserting [%x] (%d cache entries)", (int)hash,
++                        atomic_read(&ext2_xattr_cache->c_entry_count));
++              mb_cache_entry_release(ce);
++      }
++      return error;
++}
++
++/*
++ * ext2_xattr_cmp()
++ *
++ * Compare two extended attribute blocks for equality.
++ *
++ * Returns 0 if the blocks are equal, 1 if they differ, and
++ * a negative error number on errors.
++ */
++static int
++ext2_xattr_cmp(struct ext2_xattr_header *header1,
++             struct ext2_xattr_header *header2)
++{
++      struct ext2_xattr_entry *entry1, *entry2;
++
++      entry1 = ENTRY(header1+1);
++      entry2 = ENTRY(header2+1);
++      while (!IS_LAST_ENTRY(entry1)) {
++              if (IS_LAST_ENTRY(entry2))
++                      return 1;
++              if (entry1->e_hash != entry2->e_hash ||
++                  entry1->e_name_len != entry2->e_name_len ||
++                  entry1->e_value_size != entry2->e_value_size ||
++                  memcmp(entry1->e_name, entry2->e_name, entry1->e_name_len))
++                      return 1;
++              if (entry1->e_value_block != 0 || entry2->e_value_block != 0)
++                      return -EIO;
++              if (memcmp((char *)header1 + le16_to_cpu(entry1->e_value_offs),
++                         (char *)header2 + le16_to_cpu(entry2->e_value_offs),
++                         le32_to_cpu(entry1->e_value_size)))
++                      return 1;
++
++              entry1 = EXT2_XATTR_NEXT(entry1);
++              entry2 = EXT2_XATTR_NEXT(entry2);
++      }
++      if (!IS_LAST_ENTRY(entry2))
++              return 1;
++      return 0;
++}
++
++/*
++ * ext2_xattr_cache_find()
++ *
++ * Find an identical extended attribute block.
++ *
++ * Returns a pointer to the block found, or NULL if such a block was
++ * not found or an error occurred.
++ */
++static struct buffer_head *
++ext2_xattr_cache_find(struct inode *inode, struct ext2_xattr_header *header)
++{
++      __u32 hash = le32_to_cpu(header->h_hash);
++      struct mb_cache_entry *ce;
++
++      if (!header->h_hash)
++              return NULL;  /* never share */
++      ea_idebug(inode, "looking for cached blocks [%x]", (int)hash);
++      ce = mb_cache_entry_find_first(ext2_xattr_cache, 0, inode->i_dev, hash);
++      while (ce) {
++              struct buffer_head *bh = sb_bread(inode->i_sb, ce->e_block);
++
++              if (!bh) {
++                      ext2_error(inode->i_sb, "ext2_xattr_cache_find",
++                              "inode %ld: block %ld read error",
++                              inode->i_ino, ce->e_block);
++              } else if (le32_to_cpu(HDR(bh)->h_refcount) >
++                         EXT2_XATTR_REFCOUNT_MAX) {
++                      ea_idebug(inode, "block %ld refcount %d>%d",ce->e_block,
++                              le32_to_cpu(HDR(bh)->h_refcount),
++                              EXT2_XATTR_REFCOUNT_MAX);
++              } else if (!ext2_xattr_cmp(header, HDR(bh))) {
++                      ea_bdebug(bh, "b_count=%d",atomic_read(&(bh->b_count)));
++                      mb_cache_entry_release(ce);
++                      return bh;
++              }
++              brelse(bh);
++              ce = mb_cache_entry_find_next(ce, 0, inode->i_dev, hash);
++      }
++      return NULL;
++}
++
++/*
++ * ext2_xattr_cache_remove()
++ *
++ * Remove the cache entry of a block from the cache. Called when a
++ * block becomes invalid.
++ */
++static void
++ext2_xattr_cache_remove(struct buffer_head *bh)
++{
++      struct mb_cache_entry *ce;
++
++      ce = mb_cache_entry_get(ext2_xattr_cache, bh->b_dev, bh->b_blocknr);
++      if (ce) {
++              ea_bdebug(bh, "removing (%d cache entries remaining)",
++                        atomic_read(&ext2_xattr_cache->c_entry_count)-1);
++              mb_cache_entry_free(ce);
++      } else 
++              ea_bdebug(bh, "no cache entry");
++}
++
++#define NAME_HASH_SHIFT 5
++#define VALUE_HASH_SHIFT 16
++
++/*
++ * ext2_xattr_hash_entry()
++ *
++ * Compute the hash of an extended attribute.
++ */
++static inline void ext2_xattr_hash_entry(struct ext2_xattr_header *header,
++                                       struct ext2_xattr_entry *entry)
++{
++      __u32 hash = 0;
++      char *name = entry->e_name;
++      int n;
++
++      for (n=0; n < entry->e_name_len; n++) {
++              hash = (hash << NAME_HASH_SHIFT) ^
++                     (hash >> (8*sizeof(hash) - NAME_HASH_SHIFT)) ^
++                     *name++;
++      }
++
++      if (entry->e_value_block == 0 && entry->e_value_size != 0) {
++              __u32 *value = (__u32 *)((char *)header +
++                      le16_to_cpu(entry->e_value_offs));
++              for (n = (le32_to_cpu(entry->e_value_size) +
++                   EXT2_XATTR_ROUND) >> EXT2_XATTR_PAD_BITS; n; n--) {
++                      hash = (hash << VALUE_HASH_SHIFT) ^
++                             (hash >> (8*sizeof(hash) - VALUE_HASH_SHIFT)) ^
++                             le32_to_cpu(*value++);
++              }
++      }
++      entry->e_hash = cpu_to_le32(hash);
++}
++
++#undef NAME_HASH_SHIFT
++#undef VALUE_HASH_SHIFT
++
++#define BLOCK_HASH_SHIFT 16
++
++/*
++ * ext2_xattr_rehash()
++ *
++ * Re-compute the extended attribute hash value after an entry has changed.
++ */
++static void ext2_xattr_rehash(struct ext2_xattr_header *header,
++                            struct ext2_xattr_entry *entry)
++{
++      struct ext2_xattr_entry *here;
++      __u32 hash = 0;
++      
++      ext2_xattr_hash_entry(header, entry);
++      here = ENTRY(header+1);
++      while (!IS_LAST_ENTRY(here)) {
++              if (!here->e_hash) {
++                      /* Block is not shared if an entry's hash value == 0 */
++                      hash = 0;
++                      break;
++              }
++              hash = (hash << BLOCK_HASH_SHIFT) ^
++                     (hash >> (8*sizeof(hash) - BLOCK_HASH_SHIFT)) ^
++                     le32_to_cpu(here->e_hash);
++              here = EXT2_XATTR_NEXT(here);
++      }
++      header->h_hash = cpu_to_le32(hash);
++}
++
++#undef BLOCK_HASH_SHIFT
++
++int __init
++init_ext2_xattr(void)
++{
++      ext2_xattr_cache = mb_cache_create("ext2_xattr", NULL,
++              sizeof(struct mb_cache_entry) +
++              sizeof(struct mb_cache_entry_index), 1, 61);
++      if (!ext2_xattr_cache)
++              return -ENOMEM;
++
++      return 0;
++}
++
++void
++exit_ext2_xattr(void)
++{
++      mb_cache_destroy(ext2_xattr_cache);
++}
++
++#else  /* CONFIG_EXT2_FS_XATTR_SHARING */
++
++int __init
++init_ext2_xattr(void)
++{
++      return 0;
++}
++
++void
++exit_ext2_xattr(void)
++{
++}
++
++#endif  /* CONFIG_EXT2_FS_XATTR_SHARING */
+Index: linux-2.4.29/fs/ext2/xattr_user.c
+===================================================================
+--- linux-2.4.29.orig/fs/ext2/xattr_user.c     2005-05-03 17:59:40.233146800 +0300
++++ linux-2.4.29/fs/ext2/xattr_user.c  2005-05-03 17:59:40.407120352 +0300
+@@ -0,0 +1,103 @@
++/*
++ * linux/fs/ext2/xattr_user.c
++ * Handler for extended user attributes.
++ *
++ * Copyright (C) 2001 by Andreas Gruenbacher, <a.gruenbacher@computer.org>
++ */
++
++#include <linux/module.h>
++#include <linux/string.h>
++#include <linux/fs.h>
++#include <linux/ext2_fs.h>
++#include <linux/ext2_xattr.h>
++
++#ifdef CONFIG_EXT2_FS_POSIX_ACL
++# include <linux/ext2_acl.h>
++#endif
++
++#define XATTR_USER_PREFIX "user."
++
++static size_t
++ext2_xattr_user_list(char *list, struct inode *inode,
++                   const char *name, int name_len)
++{
++      const int prefix_len = sizeof(XATTR_USER_PREFIX)-1;
++
++      if (!test_opt(inode->i_sb, XATTR_USER))
++              return 0;
++
++      if (list) {
++              memcpy(list, XATTR_USER_PREFIX, prefix_len);
++              memcpy(list+prefix_len, name, name_len);
++              list[prefix_len + name_len] = '\0';
++      }
++      return prefix_len + name_len + 1;
++}
++
++static int
++ext2_xattr_user_get(struct inode *inode, const char *name,
++                  void *buffer, size_t size)
++{
++      int error;
++
++      if (strcmp(name, "") == 0)
++              return -EINVAL;
++      if (!test_opt(inode->i_sb, XATTR_USER))
++              return -ENOTSUP;
++#ifdef CONFIG_EXT2_FS_POSIX_ACL
++      error = ext2_permission_locked(inode, MAY_READ);
++#else
++      error = permission(inode, MAY_READ);
++#endif
++      if (error)
++              return error;
++
++      return ext2_xattr_get(inode, EXT2_XATTR_INDEX_USER, name,
++                            buffer, size);
++}
++
++static int
++ext2_xattr_user_set(struct inode *inode, const char *name,
++                  const void *value, size_t size, int flags)
++{
++      int error;
++
++      if (strcmp(name, "") == 0)
++              return -EINVAL;
++      if (!test_opt(inode->i_sb, XATTR_USER))
++              return -ENOTSUP;
++      if ( !S_ISREG(inode->i_mode) &&
++          (!S_ISDIR(inode->i_mode) || inode->i_mode & S_ISVTX))
++              return -EPERM;
++#ifdef CONFIG_EXT2_FS_POSIX_ACL
++      error = ext2_permission_locked(inode, MAY_WRITE);
++#else
++      error = permission(inode, MAY_WRITE);
++#endif
++      if (error)
++              return error;
++
++      return ext2_xattr_set(inode, EXT2_XATTR_INDEX_USER, name,
++                            value, size, flags);
++}
++
++struct ext2_xattr_handler ext2_xattr_user_handler = {
++      prefix: XATTR_USER_PREFIX,
++      list:   ext2_xattr_user_list,
++      get:    ext2_xattr_user_get,
++      set:    ext2_xattr_user_set,
++};
++
++int __init
++init_ext2_xattr_user(void)
++{
++      return ext2_xattr_register(EXT2_XATTR_INDEX_USER,
++                                 &ext2_xattr_user_handler);
++}
++
++void
++exit_ext2_xattr_user(void)
++{
++      ext2_xattr_unregister(EXT2_XATTR_INDEX_USER,
++                            &ext2_xattr_user_handler);
++}
+Index: linux-2.4.29/fs/ext3/Makefile
+===================================================================
+--- linux-2.4.29.orig/fs/ext3/Makefile 2005-05-03 17:23:54.093409632 +0300
++++ linux-2.4.29/fs/ext3/Makefile      2005-05-03 17:59:40.408120200 +0300
+@@ -1,5 +1,5 @@
+ #
+-# Makefile for the linux ext2-filesystem routines.
++# Makefile for the linux ext3-filesystem routines.
+ #
+ # Note! Dependencies are done automagically by 'make dep', which also
+ # removes any old dependencies. DON'T put your own dependencies here
+@@ -9,10 +9,14 @@
+ O_TARGET := ext3.o
+-export-objs :=        super.o inode.o
++export-objs :=        ext3-exports.o
+ obj-y    := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o \
+-              ioctl.o namei.o super.o symlink.o hash.o
++              ioctl.o namei.o super.o symlink.o hash.o ext3-exports.o
+ obj-m    := $(O_TARGET)
++export-objs += xattr.o
++obj-$(CONFIG_EXT3_FS_XATTR) += xattr.o
++obj-$(CONFIG_EXT3_FS_XATTR_USER) += xattr_user.o
++
+ include $(TOPDIR)/Rules.make
+Index: linux-2.4.29/fs/ext3/file.c
+===================================================================
+--- linux-2.4.29.orig/fs/ext3/file.c   2005-05-03 17:23:54.091409936 +0300
++++ linux-2.4.29/fs/ext3/file.c        2005-05-03 17:59:40.410119896 +0300
+@@ -23,6 +23,7 @@
+ #include <linux/locks.h>
+ #include <linux/jbd.h>
+ #include <linux/ext3_fs.h>
++#include <linux/ext3_xattr.h>
+ #include <linux/ext3_jbd.h>
+ #include <linux/smp_lock.h>
+@@ -127,5 +128,9 @@
+ struct inode_operations ext3_file_inode_operations = {
+       truncate:       ext3_truncate,          /* BKL held */
+       setattr:        ext3_setattr,           /* BKL held */
++      setxattr:       ext3_setxattr,          /* BKL held */
++      getxattr:       ext3_getxattr,          /* BKL held */
++      listxattr:      ext3_listxattr,         /* BKL held */
++      removexattr:    ext3_removexattr,       /* BKL held */
+ };
+Index: linux-2.4.29/fs/ext3/ialloc.c
+===================================================================
+--- linux-2.4.29.orig/fs/ext3/ialloc.c 2005-04-07 18:53:42.000000000 +0300
++++ linux-2.4.29/fs/ext3/ialloc.c      2005-05-03 17:59:40.411119744 +0300
+@@ -17,6 +17,7 @@
+ #include <linux/jbd.h>
+ #include <linux/ext3_fs.h>
+ #include <linux/ext3_jbd.h>
++#include <linux/ext3_xattr.h>
+ #include <linux/stat.h>
+ #include <linux/string.h>
+ #include <linux/locks.h>
+@@ -216,6 +217,7 @@
+        * as writing the quota to disk may need the lock as well.
+        */
+       DQUOT_INIT(inode);
++      ext3_xattr_delete_inode(handle, inode);
+       DQUOT_FREE_INODE(inode);
+       DQUOT_DROP(inode);
+Index: linux-2.4.29/fs/ext3/inode.c
+===================================================================
+--- linux-2.4.29.orig/fs/ext3/inode.c  2005-04-07 18:54:16.000000000 +0300
++++ linux-2.4.29/fs/ext3/inode.c       2005-05-03 17:59:40.415119136 +0300
+@@ -60,7 +60,7 @@
+  * still needs to be revoked.
+  */
+-static int ext3_forget(handle_t *handle, int is_metadata,
++int ext3_forget(handle_t *handle, int is_metadata,
+                      struct inode *inode, struct buffer_head *bh,
+                      int blocknr)
+ {
+@@ -191,9 +191,7 @@
+ {
+       handle_t *handle;
+       
+-      if (is_bad_inode(inode) ||
+-          inode->i_ino == EXT3_ACL_IDX_INO ||
+-          inode->i_ino == EXT3_ACL_DATA_INO)
++      if (is_bad_inode(inode))
+               goto no_delete;
+       lock_kernel();
+@@ -1885,6 +1883,8 @@
+               return;
+       if (ext3_inode_is_fast_symlink(inode))
+               return;
++      if (ext3_inode_is_fast_symlink(inode))
++              return;
+       if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
+               return;
+@@ -2032,8 +2032,6 @@
+       struct ext3_group_desc * gdp;
+               
+       if ((inode->i_ino != EXT3_ROOT_INO &&
+-              inode->i_ino != EXT3_ACL_IDX_INO &&
+-              inode->i_ino != EXT3_ACL_DATA_INO &&
+               inode->i_ino != EXT3_JOURNAL_INO &&
+               inode->i_ino < EXT3_FIRST_INO(inode->i_sb)) ||
+               inode->i_ino > le32_to_cpu(
+@@ -2174,10 +2172,7 @@
+               inode->u.ext3_i.i_data[block] = iloc.raw_inode->i_block[block];
+       INIT_LIST_HEAD(&inode->u.ext3_i.i_orphan);
+-      if (inode->i_ino == EXT3_ACL_IDX_INO ||
+-          inode->i_ino == EXT3_ACL_DATA_INO)
+-              /* Nothing to do */ ;
+-      else if (S_ISREG(inode->i_mode)) {
++      if (S_ISREG(inode->i_mode)) {
+               inode->i_op = &ext3_file_inode_operations;
+               inode->i_fop = &ext3_file_operations;
+               inode->i_mapping->a_ops = &ext3_aops;
+@@ -2188,12 +2183,14 @@
+               if (ext3_inode_is_fast_symlink(inode))
+                       inode->i_op = &ext3_fast_symlink_inode_operations;
+               else {
+-                      inode->i_op = &page_symlink_inode_operations;
++                      inode->i_op = &ext3_symlink_inode_operations;
+                       inode->i_mapping->a_ops = &ext3_aops;
+               }
+-      } else 
++      } else {
++              inode->i_op = &ext3_special_inode_operations;
+               init_special_inode(inode, inode->i_mode,
+                                  le32_to_cpu(iloc.raw_inode->i_block[0]));
++      }
+       brelse(iloc.bh);
+       ext3_set_inode_flags(inode);
+       return;
+Index: linux-2.4.29/fs/ext3/namei.c
+===================================================================
+--- linux-2.4.29.orig/fs/ext3/namei.c  2005-05-03 17:23:54.101408416 +0300
++++ linux-2.4.29/fs/ext3/namei.c       2005-05-03 17:59:40.419118528 +0300
+@@ -29,6 +29,7 @@
+ #include <linux/sched.h>
+ #include <linux/ext3_fs.h>
+ #include <linux/ext3_jbd.h>
++#include <linux/ext3_xattr.h>
+ #include <linux/fcntl.h>
+ #include <linux/stat.h>
+ #include <linux/string.h>
+@@ -1613,7 +1614,7 @@
+       if (IS_SYNC(dir))
+               handle->h_sync = 1;
+-      inode = ext3_new_inode (handle, dir, S_IFDIR);
++      inode = ext3_new_inode (handle, dir, S_IFDIR | mode);
+       err = PTR_ERR(inode);
+       if (IS_ERR(inode))
+               goto out_stop;
+@@ -1621,7 +1622,6 @@
+       inode->i_op = &ext3_dir_inode_operations;
+       inode->i_fop = &ext3_dir_operations;
+       inode->i_size = EXT3_I(inode)->i_disksize = inode->i_sb->s_blocksize;
+-      inode->i_blocks = 0;    
+       dir_block = ext3_bread (handle, inode, 0, 1, &err);
+       if (!dir_block) {
+               inode->i_nlink--; /* is this nlink == 0? */
+@@ -1648,9 +1648,6 @@
+       BUFFER_TRACE(dir_block, "call ext3_journal_dirty_metadata");
+       ext3_journal_dirty_metadata(handle, dir_block);
+       brelse (dir_block);
+-      inode->i_mode = S_IFDIR | mode;
+-      if (dir->i_mode & S_ISGID)
+-              inode->i_mode |= S_ISGID;
+       ext3_mark_inode_dirty(handle, inode);
+       err = ext3_add_entry (handle, dentry, inode);
+       if (err) {
+@@ -2019,7 +2016,7 @@
+               goto out_stop;
+       if (l > sizeof (EXT3_I(inode)->i_data)) {
+-              inode->i_op = &page_symlink_inode_operations;
++              inode->i_op = &ext3_symlink_inode_operations;
+               inode->i_mapping->a_ops = &ext3_aops;
+               /*
+                * block_symlink() calls back into ext3_prepare/commit_write.
+@@ -2248,4 +2245,16 @@
+       rmdir:          ext3_rmdir,             /* BKL held */
+       mknod:          ext3_mknod,             /* BKL held */
+       rename:         ext3_rename,            /* BKL held */
++      setxattr:       ext3_setxattr,          /* BKL held */
++      getxattr:       ext3_getxattr,          /* BKL held */
++      listxattr:      ext3_listxattr,         /* BKL held */
++      removexattr:    ext3_removexattr,       /* BKL held */
+ };
++
++struct inode_operations ext3_special_inode_operations = {
++      setxattr:       ext3_setxattr,          /* BKL held */
++      getxattr:       ext3_getxattr,          /* BKL held */
++      listxattr:      ext3_listxattr,         /* BKL held */
++      removexattr:    ext3_removexattr,       /* BKL held */
++};
++
+Index: linux-2.4.29/fs/ext3/super.c
+===================================================================
+--- linux-2.4.29.orig/fs/ext3/super.c  2005-05-03 17:23:54.104407960 +0300
++++ linux-2.4.29/fs/ext3/super.c       2005-05-03 18:00:16.805586944 +0300
+@@ -24,6 +24,7 @@
+ #include <linux/jbd.h>
+ #include <linux/ext3_fs.h>
+ #include <linux/ext3_jbd.h>
++#include <linux/ext3_xattr.h>
+ #include <linux/slab.h>
+ #include <linux/init.h>
+ #include <linux/locks.h>
+@@ -406,6 +407,7 @@
+       kdev_t j_dev = sbi->s_journal->j_dev;
+       int i;
++      ext3_xattr_put_super(sb);
+       journal_destroy(sbi->s_journal);
+       if (!(sb->s_flags & MS_RDONLY)) {
+               EXT3_CLEAR_INCOMPAT_FEATURE(sb, EXT3_FEATURE_INCOMPAT_RECOVER);
+@@ -504,6 +506,7 @@
+                         int is_remount)
+ {
+       unsigned long *mount_options = &sbi->s_mount_opt;
++      
+       uid_t *resuid = &sbi->s_resuid;
+       gid_t *resgid = &sbi->s_resgid;
+       char * this_char;
+@@ -516,6 +519,13 @@
+            this_char = strtok (NULL, ",")) {
+               if ((value = strchr (this_char, '=')) != NULL)
+                       *value++ = 0;
++#ifdef CONFIG_EXT3_FS_XATTR_USER
++              if (!strcmp (this_char, "user_xattr"))
++                      set_opt (*mount_options, XATTR_USER);
++              else if (!strcmp (this_char, "nouser_xattr"))
++                      clear_opt (*mount_options, XATTR_USER);
++              else
++#endif
+               if (!strcmp (this_char, "bsddf"))
+                       clear_opt (*mount_options, MINIX_DF);
+               else if (!strcmp (this_char, "nouid32")) {
+@@ -954,6 +964,12 @@
+       sbi->s_mount_opt = 0;
+       sbi->s_resuid = EXT3_DEF_RESUID;
+       sbi->s_resgid = EXT3_DEF_RESGID;
++
++      /* Default extended attribute flags */
++#ifdef CONFIG_EXT3_FS_XATTR_USER
++      /* set_opt(sbi->s_mount_opt, XATTR_USER); */
++#endif
++
+       if (!parse_options ((char *) data, &sb_block, sbi, &journal_inum, 0)) {
+               sb->s_dev = 0;
+               goto out_fail;
+@@ -1838,22 +1854,35 @@
+ static int __init init_ext3_fs(void)
+ {
++      int error;
+ #ifdef CONFIG_QUOTA
+       init_dquot_operations(&ext3_qops);
+       old_write_dquot = ext3_qops.write_dquot;
+       ext3_qops.write_dquot = ext3_write_dquot;
+ #endif
+-        return register_filesystem(&ext3_fs_type);
++      error = init_ext3_xattr();
++      if (error)
++              return error;
++      error = init_ext3_xattr_user();
++      if (error)
++              goto fail;
++      error = register_filesystem(&ext3_fs_type);
++      if (!error)
++              return 0;
++
++      exit_ext3_xattr_user();
++fail:
++      exit_ext3_xattr();
++      return error;
+ }
+ static void __exit exit_ext3_fs(void)
+ {
+       unregister_filesystem(&ext3_fs_type);
++      exit_ext3_xattr_user(); 
++      exit_ext3_xattr();
+ }
+-EXPORT_SYMBOL(ext3_force_commit);
+-EXPORT_SYMBOL(ext3_bread);
+-
+ MODULE_AUTHOR("Remy Card, Stephen Tweedie, Andrew Morton, Andreas Dilger, Theodore Ts'o and others");
+ MODULE_DESCRIPTION("Second Extended Filesystem with journaling extensions");
+ MODULE_LICENSE("GPL");
+Index: linux-2.4.29/fs/ext3/symlink.c
+===================================================================
+--- linux-2.4.29.orig/fs/ext3/symlink.c        2005-04-07 18:53:53.000000000 +0300
++++ linux-2.4.29/fs/ext3/symlink.c     2005-05-03 17:59:40.423117920 +0300
+@@ -20,6 +20,7 @@
+ #include <linux/fs.h>
+ #include <linux/jbd.h>
+ #include <linux/ext3_fs.h>
++#include <linux/ext3_xattr.h>
+ static int ext3_readlink(struct dentry *dentry, char *buffer, int buflen)
+ {
+@@ -33,7 +34,20 @@
+       return vfs_follow_link(nd, s);
+ }
++struct inode_operations ext3_symlink_inode_operations = {
++      readlink:       page_readlink,          /* BKL not held.  Don't need */
++      follow_link:    page_follow_link,       /* BKL not held.  Don't need */
++      setxattr:       ext3_setxattr,          /* BKL held */
++      getxattr:       ext3_getxattr,          /* BKL held */
++      listxattr:      ext3_listxattr,         /* BKL held */
++      removexattr:    ext3_removexattr,       /* BKL held */
++};
++
+ struct inode_operations ext3_fast_symlink_inode_operations = {
+       readlink:       ext3_readlink,          /* BKL not held.  Don't need */
+       follow_link:    ext3_follow_link,       /* BKL not held.  Don't need */
++      setxattr:       ext3_setxattr,          /* BKL held */
++      getxattr:       ext3_getxattr,          /* BKL held */
++      listxattr:      ext3_listxattr,         /* BKL held */
++      removexattr:    ext3_removexattr,       /* BKL held */
+ };
+Index: linux-2.4.29/fs/ext3/xattr.c
+===================================================================
+--- linux-2.4.29.orig/fs/ext3/xattr.c  2005-05-03 17:59:40.234146648 +0300
++++ linux-2.4.29/fs/ext3/xattr.c       2005-05-03 17:59:40.428117160 +0300
+@@ -0,0 +1,1225 @@
++/*
++ * linux/fs/ext3/xattr.c
++ *
++ * Copyright (C) 2001 by Andreas Gruenbacher, <a.gruenbacher@computer.org>
++ *
++ * Fix by Harrison Xing <harrison@mountainviewdata.com>.
++ * Ext3 code with a lot of help from Eric Jarman <ejarman@acm.org>.
++ * Extended attributes for symlinks and special files added per
++ *  suggestion of Luka Renko <luka.renko@hermes.si>.
++ */
++
++/*
++ * Extended attributes are stored on disk blocks allocated outside of
++ * any inode. The i_file_acl field is then made to point to this allocated
++ * block. If all extended attributes of an inode are identical, these
++ * inodes may share the same extended attribute block. Such situations
++ * are automatically detected by keeping a cache of recent attribute block
++ * numbers and hashes over the block's contents in memory.
++ *
++ *
++ * Extended attribute block layout:
++ *
++ *   +------------------+
++ *   | header           |
++ *   | entry 1          | |
++ *   | entry 2          | | growing downwards
++ *   | entry 3          | v
++ *   | four null bytes  |
++ *   | . . .            |
++ *   | value 1          | ^
++ *   | value 3          | | growing upwards
++ *   | value 2          | |
++ *   +------------------+
++ *
++ * The block header is followed by multiple entry descriptors. These entry
++ * descriptors are variable in size, and alligned to EXT3_XATTR_PAD
++ * byte boundaries. The entry descriptors are sorted by attribute name,
++ * so that two extended attribute blocks can be compared efficiently.
++ *
++ * Attribute values are aligned to the end of the block, stored in
++ * no specific order. They are also padded to EXT3_XATTR_PAD byte
++ * boundaries. No additional gaps are left between them.
++ *
++ * Locking strategy
++ * ----------------
++ * The VFS already holds the BKL and the inode->i_sem semaphore when any of
++ * the xattr inode operations are called, so we are guaranteed that only one
++ * processes accesses extended attributes of an inode at any time.
++ *
++ * For writing we also grab the ext3_xattr_sem semaphore. This ensures that
++ * only a single process is modifying an extended attribute block, even
++ * if the block is shared among inodes.
++ *
++ * Note for porting to 2.5
++ * -----------------------
++ * The BKL will no longer be held in the xattr inode operations.
++ */
++
++#include <linux/module.h>
++#include <linux/fs.h>
++#include <linux/locks.h>
++#include <linux/slab.h>
++#include <linux/ext3_jbd.h>
++#include <linux/ext3_fs.h>
++#include <linux/ext3_xattr.h>
++#include <linux/mbcache.h>
++#include <linux/quotaops.h>
++#include <asm/semaphore.h>
++#include <linux/compatmac.h>
++
++#define EXT3_EA_USER "user."
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
++# define mark_buffer_dirty(bh) mark_buffer_dirty(bh, 1)
++#endif
++
++#define HDR(bh) ((struct ext3_xattr_header *)((bh)->b_data))
++#define ENTRY(ptr) ((struct ext3_xattr_entry *)(ptr))
++#define FIRST_ENTRY(bh) ENTRY(HDR(bh)+1)
++#define IS_LAST_ENTRY(entry) (*(__u32 *)(entry) == 0)
++
++#ifdef EXT3_XATTR_DEBUG
++# define ea_idebug(inode, f...) do { \
++              printk(KERN_DEBUG "inode %s:%ld: ", \
++                      kdevname(inode->i_dev), inode->i_ino); \
++              printk(f); \
++              printk("\n"); \
++      } while (0)
++# define ea_bdebug(bh, f...) do { \
++              printk(KERN_DEBUG "block %s:%ld: ", \
++                      kdevname(bh->b_dev), bh->b_blocknr); \
++              printk(f); \
++              printk("\n"); \
++      } while (0)
++#else
++# define ea_idebug(f...)
++# define ea_bdebug(f...)
++#endif
++
++static int ext3_xattr_set2(handle_t *, struct inode *, struct buffer_head *,
++                         struct ext3_xattr_header *);
++
++#ifdef CONFIG_EXT3_FS_XATTR_SHARING
++
++static int ext3_xattr_cache_insert(struct buffer_head *);
++static struct buffer_head *ext3_xattr_cache_find(struct inode *,
++                                               struct ext3_xattr_header *);
++static void ext3_xattr_cache_remove(struct buffer_head *);
++static void ext3_xattr_rehash(struct ext3_xattr_header *,
++                            struct ext3_xattr_entry *);
++
++static struct mb_cache *ext3_xattr_cache;
++
++#else
++# define ext3_xattr_cache_insert(bh) 0
++# define ext3_xattr_cache_find(inode, header) NULL
++# define ext3_xattr_cache_remove(bh) while(0) {}
++# define ext3_xattr_rehash(header, entry) while(0) {}
++#endif
++
++/*
++ * If a file system does not share extended attributes among inodes,
++ * we should not need the ext3_xattr_sem semaphore. However, the
++ * filesystem may still contain shared blocks, so we always take
++ * the lock.
++ */
++
++DECLARE_MUTEX(ext3_xattr_sem);
++
++static inline int
++ext3_xattr_new_block(handle_t *handle, struct inode *inode,
++                   int * errp, int force)
++{
++      struct super_block *sb = inode->i_sb;
++      int goal = le32_to_cpu(EXT3_SB(sb)->s_es->s_first_data_block) +
++              EXT3_I(inode)->i_block_group * EXT3_BLOCKS_PER_GROUP(sb);
++
++      /* How can we enforce the allocation? */
++      int block = ext3_new_block(handle, inode, goal, 0, 0, errp);
++#ifdef OLD_QUOTAS
++      if (!*errp)
++              inode->i_blocks += inode->i_sb->s_blocksize >> 9;
++#endif
++      return block;
++}
++
++static inline int
++ext3_xattr_quota_alloc(struct inode *inode, int force)
++{
++      /* How can we enforce the allocation? */
++#ifdef OLD_QUOTAS
++      int error = DQUOT_ALLOC_BLOCK(inode->i_sb, inode, 1);
++      if (!error)
++              inode->i_blocks += inode->i_sb->s_blocksize >> 9;
++#else
++      int error = DQUOT_ALLOC_BLOCK(inode, 1);
++#endif
++      return error;
++}
++
++#ifdef OLD_QUOTAS
++
++static inline void
++ext3_xattr_quota_free(struct inode *inode)
++{
++      DQUOT_FREE_BLOCK(inode->i_sb, inode, 1);
++      inode->i_blocks -= inode->i_sb->s_blocksize >> 9;
++}
++
++static inline void
++ext3_xattr_free_block(handle_t *handle, struct inode * inode,
++                    unsigned long block)
++{
++      ext3_free_blocks(handle, inode, block, 1);
++      inode->i_blocks -= inode->i_sb->s_blocksize >> 9;
++}
++
++#else
++# define ext3_xattr_quota_free(inode) \
++      DQUOT_FREE_BLOCK(inode, 1)
++# define ext3_xattr_free_block(handle, inode, block) \
++      ext3_free_blocks(handle, inode, block, 1)
++#endif
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,18)
++
++static inline struct buffer_head *
++sb_bread(struct super_block *sb, int block)
++{
++      return bread(sb->s_dev, block, sb->s_blocksize);
++}
++
++static inline struct buffer_head *
++sb_getblk(struct super_block *sb, int block)
++{
++      return getblk(sb->s_dev, block, sb->s_blocksize);
++}
++
++#endif
++
++struct ext3_xattr_handler *ext3_xattr_handlers[EXT3_XATTR_INDEX_MAX];
++rwlock_t ext3_handler_lock = RW_LOCK_UNLOCKED;
++
++int
++ext3_xattr_register(int name_index, struct ext3_xattr_handler *handler)
++{
++      int error = -EINVAL;
++
++      if (name_index > 0 && name_index <= EXT3_XATTR_INDEX_MAX) {
++              write_lock(&ext3_handler_lock);
++              if (!ext3_xattr_handlers[name_index-1]) {
++                      ext3_xattr_handlers[name_index-1] = handler;
++                      error = 0;
++              }
++              write_unlock(&ext3_handler_lock);
++      }
++      return error;
++}
++
++void
++ext3_xattr_unregister(int name_index, struct ext3_xattr_handler *handler)
++{
++      if (name_index > 0 || name_index <= EXT3_XATTR_INDEX_MAX) {
++              write_lock(&ext3_handler_lock);
++              ext3_xattr_handlers[name_index-1] = NULL;
++              write_unlock(&ext3_handler_lock);
++      }
++}
++
++static inline const char *
++strcmp_prefix(const char *a, const char *a_prefix)
++{
++      while (*a_prefix && *a == *a_prefix) {
++              a++;
++              a_prefix++;
++      }
++      return *a_prefix ? NULL : a;
++}
++
++/*
++ * Decode the extended attribute name, and translate it into
++ * the name_index and name suffix.
++ */
++static inline struct ext3_xattr_handler *
++ext3_xattr_resolve_name(const char **name)
++{
++      struct ext3_xattr_handler *handler = NULL;
++      int i;
++
++      if (!*name)
++              return NULL;
++      read_lock(&ext3_handler_lock);
++      for (i=0; i<EXT3_XATTR_INDEX_MAX; i++) {
++              if (ext3_xattr_handlers[i]) {
++                      const char *n = strcmp_prefix(*name,
++                              ext3_xattr_handlers[i]->prefix);
++                      if (n) {
++                              handler = ext3_xattr_handlers[i];
++                              *name = n;
++                              break;
++                      }
++              }
++      }
++      read_unlock(&ext3_handler_lock);
++      return handler;
++}
++
++static inline struct ext3_xattr_handler *
++ext3_xattr_handler(int name_index)
++{
++      struct ext3_xattr_handler *handler = NULL;
++      if (name_index > 0 && name_index <= EXT3_XATTR_INDEX_MAX) {
++              read_lock(&ext3_handler_lock);
++              handler = ext3_xattr_handlers[name_index-1];
++              read_unlock(&ext3_handler_lock);
++      }
++      return handler;
++}
++
++/*
++ * Inode operation getxattr()
++ *
++ * dentry->d_inode->i_sem down
++ * BKL held [before 2.5.x]
++ */
++ssize_t
++ext3_getxattr(struct dentry *dentry, const char *name,
++            void *buffer, size_t size)
++{
++      struct ext3_xattr_handler *handler;
++      struct inode *inode = dentry->d_inode;
++
++      handler = ext3_xattr_resolve_name(&name);
++      if (!handler)
++              return -ENOTSUP;
++      return handler->get(inode, name, buffer, size);
++}
++
++/*
++ * Inode operation listxattr()
++ *
++ * dentry->d_inode->i_sem down
++ * BKL held [before 2.5.x]
++ */
++ssize_t
++ext3_listxattr(struct dentry *dentry, char *buffer, size_t size)
++{
++      return ext3_xattr_list(dentry->d_inode, buffer, size);
++}
++
++/*
++ * Inode operation setxattr()
++ *
++ * dentry->d_inode->i_sem down
++ * BKL held [before 2.5.x]
++ */
++int
++ext3_setxattr(struct dentry *dentry, const char *name,
++            const void *value, size_t size, int flags)
++{
++      struct ext3_xattr_handler *handler;
++      struct inode *inode = dentry->d_inode;
++
++      if (size == 0)
++              value = "";  /* empty EA, do not remove */
++      handler = ext3_xattr_resolve_name(&name);
++      if (!handler)
++              return -ENOTSUP;
++      return handler->set(inode, name, value, size, flags);
++}
++
++/*
++ * Inode operation removexattr()
++ *
++ * dentry->d_inode->i_sem down
++ * BKL held [before 2.5.x]
++ */
++int
++ext3_removexattr(struct dentry *dentry, const char *name)
++{
++      struct ext3_xattr_handler *handler;
++      struct inode *inode = dentry->d_inode;
++
++      handler = ext3_xattr_resolve_name(&name);
++      if (!handler)
++              return -ENOTSUP;
++      return handler->set(inode, name, NULL, 0, XATTR_REPLACE);
++}
++
++/*
++ * ext3_xattr_get()
++ *
++ * Copy an extended attribute into the buffer
++ * provided, or compute the buffer size required.
++ * Buffer is NULL to compute the size of the buffer required.
++ *
++ * Returns a negative error number on failure, or the number of bytes
++ * used / required on success.
++ */
++int
++ext3_xattr_get(struct inode *inode, int name_index, const char *name,
++             void *buffer, size_t buffer_size)
++{
++      struct buffer_head *bh = NULL;
++      struct ext3_xattr_entry *entry;
++      unsigned int block, size;
++      char *end;
++      int name_len, error;
++
++      ea_idebug(inode, "name=%d.%s, buffer=%p, buffer_size=%ld",
++                name_index, name, buffer, (long)buffer_size);
++
++      if (name == NULL)
++              return -EINVAL;
++      if (!EXT3_I(inode)->i_file_acl)
++              return -ENOATTR;
++      block = EXT3_I(inode)->i_file_acl;
++      ea_idebug(inode, "reading block %d", block);
++      bh = sb_bread(inode->i_sb, block);
++      if (!bh)
++              return -EIO;
++      ea_bdebug(bh, "b_count=%d, refcount=%d",
++              atomic_read(&(bh->b_count)), le32_to_cpu(HDR(bh)->h_refcount));
++      end = bh->b_data + bh->b_size;
++      if (HDR(bh)->h_magic != cpu_to_le32(EXT3_XATTR_MAGIC) ||
++          HDR(bh)->h_blocks != cpu_to_le32(1)) {
++bad_block:    ext3_error(inode->i_sb, "ext3_xattr_get",
++                      "inode %ld: bad block %d", inode->i_ino, block);
++              error = -EIO;
++              goto cleanup;
++      }
++      /* find named attribute */
++      name_len = strlen(name);
++
++      error = -ERANGE;
++      if (name_len > 255)
++              goto cleanup;
++      entry = FIRST_ENTRY(bh);
++      while (!IS_LAST_ENTRY(entry)) {
++              struct ext3_xattr_entry *next =
++                      EXT3_XATTR_NEXT(entry);
++              if ((char *)next >= end)
++                      goto bad_block;
++              if (name_index == entry->e_name_index &&
++                  name_len == entry->e_name_len &&
++                  memcmp(name, entry->e_name, name_len) == 0)
++                      goto found;
++              entry = next;
++      }
++      /* Check the remaining name entries */
++      while (!IS_LAST_ENTRY(entry)) {
++              struct ext3_xattr_entry *next =
++                      EXT3_XATTR_NEXT(entry);
++              if ((char *)next >= end)
++                      goto bad_block;
++              entry = next;
++      }
++      if (ext3_xattr_cache_insert(bh))
++              ea_idebug(inode, "cache insert failed");
++      error = -ENOATTR;
++      goto cleanup;
++found:
++      /* check the buffer size */
++      if (entry->e_value_block != 0)
++              goto bad_block;
++      size = le32_to_cpu(entry->e_value_size);
++      if (size > inode->i_sb->s_blocksize ||
++          le16_to_cpu(entry->e_value_offs) + size > inode->i_sb->s_blocksize)
++              goto bad_block;
++
++      if (ext3_xattr_cache_insert(bh))
++              ea_idebug(inode, "cache insert failed");
++      if (buffer) {
++              error = -ERANGE;
++              if (size > buffer_size)
++                      goto cleanup;
++              /* return value of attribute */
++              memcpy(buffer, bh->b_data + le16_to_cpu(entry->e_value_offs),
++                      size);
++      }
++      error = size;
++
++cleanup:
++      brelse(bh);
++
++      return error;
++}
++
++/*
++ * ext3_xattr_list()
++ *
++ * Copy a list of attribute names into the buffer
++ * provided, or compute the buffer size required.
++ * Buffer is NULL to compute the size of the buffer required.
++ *
++ * Returns a negative error number on failure, or the number of bytes
++ * used / required on success.
++ */
++int
++ext3_xattr_list(struct inode *inode, char *buffer, size_t buffer_size)
++{
++      struct buffer_head *bh = NULL;
++      struct ext3_xattr_entry *entry;
++      unsigned int block, size = 0;
++      char *buf, *end;
++      int error;
++
++      ea_idebug(inode, "buffer=%p, buffer_size=%ld",
++                buffer, (long)buffer_size);
++
++      if (!EXT3_I(inode)->i_file_acl)
++              return 0;
++      block = EXT3_I(inode)->i_file_acl;
++      ea_idebug(inode, "reading block %d", block);
++      bh = sb_bread(inode->i_sb, block);
++      if (!bh)
++              return -EIO;
++      ea_bdebug(bh, "b_count=%d, refcount=%d",
++              atomic_read(&(bh->b_count)), le32_to_cpu(HDR(bh)->h_refcount));
++      end = bh->b_data + bh->b_size;
++      if (HDR(bh)->h_magic != cpu_to_le32(EXT3_XATTR_MAGIC) ||
++          HDR(bh)->h_blocks != cpu_to_le32(1)) {
++bad_block:    ext3_error(inode->i_sb, "ext3_xattr_list",
++                      "inode %ld: bad block %d", inode->i_ino, block);
++              error = -EIO;
++              goto cleanup;
++      }
++      /* compute the size required for the list of attribute names */
++      for (entry = FIRST_ENTRY(bh); !IS_LAST_ENTRY(entry);
++           entry = EXT3_XATTR_NEXT(entry)) {
++              struct ext3_xattr_handler *handler;
++              struct ext3_xattr_entry *next =
++                      EXT3_XATTR_NEXT(entry);
++              if ((char *)next >= end)
++                      goto bad_block;
++
++              handler = ext3_xattr_handler(entry->e_name_index);
++              if (handler)
++                      size += handler->list(NULL, inode, entry->e_name,
++                                            entry->e_name_len);
++      }
++
++      if (ext3_xattr_cache_insert(bh))
++              ea_idebug(inode, "cache insert failed");
++      if (!buffer) {
++              error = size;
++              goto cleanup;
++      } else {
++              error = -ERANGE;
++              if (size > buffer_size)
++                      goto cleanup;
++      }
++
++      /* list the attribute names */
++      buf = buffer;
++      for (entry = FIRST_ENTRY(bh); !IS_LAST_ENTRY(entry);
++           entry = EXT3_XATTR_NEXT(entry)) {
++              struct ext3_xattr_handler *handler;
++
++              handler = ext3_xattr_handler(entry->e_name_index);
++              if (handler)
++                      buf += handler->list(buf, inode, entry->e_name,
++                                           entry->e_name_len);
++      }
++      error = size;
++
++cleanup:
++      brelse(bh);
++
++      return error;
++}
++
++/*
++ * If the EXT3_FEATURE_COMPAT_EXT_ATTR feature of this file system is
++ * not set, set it.
++ */
++static void ext3_xattr_update_super_block(handle_t *handle,
++                                        struct super_block *sb)
++{
++      if (EXT3_HAS_COMPAT_FEATURE(sb, EXT3_FEATURE_COMPAT_EXT_ATTR))
++              return;
++
++      lock_super(sb);
++      ext3_journal_get_write_access(handle, EXT3_SB(sb)->s_sbh);
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
++      EXT3_SB(sb)->s_feature_compat |= EXT3_FEATURE_COMPAT_EXT_ATTR;
++#endif
++      EXT3_SB(sb)->s_es->s_feature_compat |=
++              cpu_to_le32(EXT3_FEATURE_COMPAT_EXT_ATTR);
++      sb->s_dirt = 1;
++      ext3_journal_dirty_metadata(handle, EXT3_SB(sb)->s_sbh);
++      unlock_super(sb);
++}
++
++/*
++ * ext3_xattr_set()
++ *
++ * Create, replace or remove an extended attribute for this inode. Buffer
++ * is NULL to remove an existing extended attribute, and non-NULL to
++ * either replace an existing extended attribute, or create a new extended
++ * attribute. The flags XATTR_REPLACE and XATTR_CREATE
++ * specify that an extended attribute must exist and must not exist
++ * previous to the call, respectively.
++ *
++ * Returns 0, or a negative error number on failure.
++ */
++int
++ext3_xattr_set(handle_t *handle, struct inode *inode, int name_index,
++             const char *name, const void *value, size_t value_len, int flags)
++{
++      struct super_block *sb = inode->i_sb;
++      struct buffer_head *bh = NULL;
++      struct ext3_xattr_header *header = NULL;
++      struct ext3_xattr_entry *here, *last;
++      unsigned int name_len;
++      int block = EXT3_I(inode)->i_file_acl;
++      int min_offs = sb->s_blocksize, not_found = 1, free, error;
++      char *end;
++      
++      /*
++       * header -- Points either into bh, or to a temporarily
++       *           allocated buffer.
++       * here -- The named entry found, or the place for inserting, within
++       *         the block pointed to by header.
++       * last -- Points right after the last named entry within the block
++       *         pointed to by header.
++       * min_offs -- The offset of the first value (values are aligned
++       *             towards the end of the block).
++       * end -- Points right after the block pointed to by header.
++       */
++      
++      ea_idebug(inode, "name=%d.%s, value=%p, value_len=%ld",
++                name_index, name, value, (long)value_len);
++
++      if (IS_RDONLY(inode))
++              return -EROFS;
++      if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
++              return -EPERM;
++      if (value == NULL)
++              value_len = 0;
++      if (name == NULL)
++              return -EINVAL;
++      name_len = strlen(name);
++      if (name_len > 255 || value_len > sb->s_blocksize)
++              return -ERANGE;
++      down(&ext3_xattr_sem);
++
++      if (block) {
++              /* The inode already has an extended attribute block. */
++              bh = sb_bread(sb, block);
++              error = -EIO;
++              if (!bh)
++                      goto cleanup;
++              ea_bdebug(bh, "b_count=%d, refcount=%d",
++                      atomic_read(&(bh->b_count)),
++                      le32_to_cpu(HDR(bh)->h_refcount));
++              header = HDR(bh);
++              end = bh->b_data + bh->b_size;
++              if (header->h_magic != cpu_to_le32(EXT3_XATTR_MAGIC) ||
++                  header->h_blocks != cpu_to_le32(1)) {
++bad_block:            ext3_error(sb, "ext3_xattr_set",
++                              "inode %ld: bad block %d", inode->i_ino, block);
++                      error = -EIO;
++                      goto cleanup;
++              }
++              /* Find the named attribute. */
++              here = FIRST_ENTRY(bh);
++              while (!IS_LAST_ENTRY(here)) {
++                      struct ext3_xattr_entry *next = EXT3_XATTR_NEXT(here);
++                      if ((char *)next >= end)
++                              goto bad_block;
++                      if (!here->e_value_block && here->e_value_size) {
++                              int offs = le16_to_cpu(here->e_value_offs);
++                              if (offs < min_offs)
++                                      min_offs = offs;
++                      }
++                      not_found = name_index - here->e_name_index;
++                      if (!not_found)
++                              not_found = name_len - here->e_name_len;
++                      if (!not_found)
++                              not_found = memcmp(name, here->e_name,name_len);
++                      if (not_found <= 0)
++                              break;
++                      here = next;
++              }
++              last = here;
++              /* We still need to compute min_offs and last. */
++              while (!IS_LAST_ENTRY(last)) {
++                      struct ext3_xattr_entry *next = EXT3_XATTR_NEXT(last);
++                      if ((char *)next >= end)
++                              goto bad_block;
++                      if (!last->e_value_block && last->e_value_size) {
++                              int offs = le16_to_cpu(last->e_value_offs);
++                              if (offs < min_offs)
++                                      min_offs = offs;
++                      }
++                      last = next;
++              }
++
++              /* Check whether we have enough space left. */
++              free = min_offs - ((char*)last - (char*)header) - sizeof(__u32);
++      } else {
++              /* We will use a new extended attribute block. */
++              free = sb->s_blocksize -
++                      sizeof(struct ext3_xattr_header) - sizeof(__u32);
++              here = last = NULL;  /* avoid gcc uninitialized warning. */
++      }
++
++      if (not_found) {
++              /* Request to remove a nonexistent attribute? */
++              error = -ENOATTR;
++              if (flags & XATTR_REPLACE)
++                      goto cleanup;
++              error = 0;
++              if (value == NULL)
++                      goto cleanup;
++              else
++                      free -= EXT3_XATTR_LEN(name_len);
++      } else {
++              /* Request to create an existing attribute? */
++              error = -EEXIST;
++              if (flags & XATTR_CREATE)
++                      goto cleanup;
++              if (!here->e_value_block && here->e_value_size) {
++                      unsigned int size = le32_to_cpu(here->e_value_size);
++
++                      if (le16_to_cpu(here->e_value_offs) + size > 
++                          sb->s_blocksize || size > sb->s_blocksize)
++                              goto bad_block;
++                      free += EXT3_XATTR_SIZE(size);
++              }
++      }
++      free -= EXT3_XATTR_SIZE(value_len);
++      error = -ENOSPC;
++      if (free < 0)
++              goto cleanup;
++
++      /* Here we know that we can set the new attribute. */
++
++      if (header) {
++              if (header->h_refcount == cpu_to_le32(1)) {
++                      ea_bdebug(bh, "modifying in-place");
++                      ext3_xattr_cache_remove(bh);
++                      error = ext3_journal_get_write_access(handle, bh);
++                      if (error)
++                              goto cleanup;
++              } else {
++                      int offset;
++
++                      ea_bdebug(bh, "cloning");
++                      header = kmalloc(bh->b_size, GFP_KERNEL);
++                      error = -ENOMEM;
++                      if (header == NULL)
++                              goto cleanup;
++                      memcpy(header, HDR(bh), bh->b_size);
++                      header->h_refcount = cpu_to_le32(1);
++                      offset = (char *)header - bh->b_data;
++                      here = ENTRY((char *)here + offset);
++                      last = ENTRY((char *)last + offset);
++              }
++      } else {
++              /* Allocate a buffer where we construct the new block. */
++              header = kmalloc(sb->s_blocksize, GFP_KERNEL);
++              error = -ENOMEM;
++              if (header == NULL)
++                      goto cleanup;
++              memset(header, 0, sb->s_blocksize);
++              end = (char *)header + sb->s_blocksize;
++              header->h_magic = cpu_to_le32(EXT3_XATTR_MAGIC);
++              header->h_blocks = header->h_refcount = cpu_to_le32(1);
++              last = here = ENTRY(header+1);
++      }
++
++      if (not_found) {
++              /* Insert the new name. */
++              int size = EXT3_XATTR_LEN(name_len);
++              int rest = (char *)last - (char *)here;
++              memmove((char *)here + size, here, rest);
++              memset(here, 0, size);
++              here->e_name_index = name_index;
++              here->e_name_len = name_len;
++              memcpy(here->e_name, name, name_len);
++      } else {
++              /* Remove the old value. */
++              if (!here->e_value_block && here->e_value_size) {
++                      char *first_val = (char *)header + min_offs;
++                      int offs = le16_to_cpu(here->e_value_offs);
++                      char *val = (char *)header + offs;
++                      size_t size = EXT3_XATTR_SIZE(
++                              le32_to_cpu(here->e_value_size));
++                      memmove(first_val + size, first_val, val - first_val);
++                      memset(first_val, 0, size);
++                      here->e_value_offs = 0;
++                      min_offs += size;
++
++                      /* Adjust all value offsets. */
++                      last = ENTRY(header+1);
++                      while (!IS_LAST_ENTRY(last)) {
++                              int o = le16_to_cpu(last->e_value_offs);
++                              if (!last->e_value_block && o < offs)
++                                      last->e_value_offs =
++                                              cpu_to_le16(o + size);
++                              last = EXT3_XATTR_NEXT(last);
++                      }
++              }
++              if (value == NULL) {
++                      /* Remove this attribute. */
++                      if (EXT3_XATTR_NEXT(ENTRY(header+1)) == last) {
++                              /* This block is now empty. */
++                              error = ext3_xattr_set2(handle, inode, bh,NULL);
++                              goto cleanup;
++                      } else {
++                              /* Remove the old name. */
++                              int size = EXT3_XATTR_LEN(name_len);
++                              last = ENTRY((char *)last - size);
++                              memmove(here, (char*)here + size,
++                                      (char*)last - (char*)here);
++                              memset(last, 0, size);
++                      }
++              }
++      }
++
++      if (value != NULL) {
++              /* Insert the new value. */
++              here->e_value_size = cpu_to_le32(value_len);
++              if (value_len) {
++                      size_t size = EXT3_XATTR_SIZE(value_len);
++                      char *val = (char *)header + min_offs - size;
++                      here->e_value_offs =
++                              cpu_to_le16((char *)val - (char *)header);
++                      memset(val + size - EXT3_XATTR_PAD, 0,
++                             EXT3_XATTR_PAD); /* Clear the pad bytes. */
++                      memcpy(val, value, value_len);
++              }
++      }
++      ext3_xattr_rehash(header, here);
++
++      error = ext3_xattr_set2(handle, inode, bh, header);
++
++cleanup:
++      brelse(bh);
++      if (!(bh && header == HDR(bh)))
++              kfree(header);
++      up(&ext3_xattr_sem);
++
++      return error;
++}
++
++/*
++ * Second half of ext3_xattr_set(): Update the file system.
++ */
++static int
++ext3_xattr_set2(handle_t *handle, struct inode *inode,
++              struct buffer_head *old_bh, struct ext3_xattr_header *header)
++{
++      struct super_block *sb = inode->i_sb;
++      struct buffer_head *new_bh = NULL;
++      int error;
++
++      if (header) {
++              new_bh = ext3_xattr_cache_find(inode, header);
++              if (new_bh) {
++                      /*
++                       * We found an identical block in the cache.
++                       * The old block will be released after updating
++                       * the inode.
++                       */
++                      ea_bdebug(old_bh, "reusing block %ld",
++                              new_bh->b_blocknr);
++                      
++                      error = -EDQUOT;
++                      if (ext3_xattr_quota_alloc(inode, 1))
++                              goto cleanup;
++                      
++                      error = ext3_journal_get_write_access(handle, new_bh);
++                      if (error)
++                              goto cleanup;
++                      HDR(new_bh)->h_refcount = cpu_to_le32(
++                              le32_to_cpu(HDR(new_bh)->h_refcount) + 1);
++                      ea_bdebug(new_bh, "refcount now=%d",
++                              le32_to_cpu(HDR(new_bh)->h_refcount));
++              } else if (old_bh && header == HDR(old_bh)) {
++                      /* Keep this block. */
++                      new_bh = old_bh;
++                      (void)ext3_xattr_cache_insert(new_bh);
++              } else {
++                      /* We need to allocate a new block */
++                      int force = EXT3_I(inode)->i_file_acl != 0;
++                      int block = ext3_xattr_new_block(handle, inode,
++                                                       &error, force);
++                      if (error)
++                              goto cleanup;
++                      ea_idebug(inode, "creating block %d", block);
++
++                      new_bh = sb_getblk(sb, block);
++                      if (!new_bh) {
++getblk_failed:                        ext3_xattr_free_block(handle, inode, block);
++                              error = -EIO;
++                              goto cleanup;
++                      }
++                      lock_buffer(new_bh);
++                      error = ext3_journal_get_create_access(handle, new_bh);
++                      if (error) {
++                              unlock_buffer(new_bh);
++                              goto getblk_failed;
++                      }
++                      memcpy(new_bh->b_data, header, new_bh->b_size);
++                      mark_buffer_uptodate(new_bh, 1);
++                      unlock_buffer(new_bh);
++                      (void)ext3_xattr_cache_insert(new_bh);
++                      
++                      ext3_xattr_update_super_block(handle, sb);
++              }
++              error = ext3_journal_dirty_metadata(handle, new_bh);
++              if (error)
++                      goto cleanup;
++      }
++
++      /* Update the inode. */
++      EXT3_I(inode)->i_file_acl = new_bh ? new_bh->b_blocknr : 0;
++      inode->i_ctime = CURRENT_TIME;
++      ext3_mark_inode_dirty(handle, inode);
++      if (IS_SYNC(inode))
++              handle->h_sync = 1;
++
++      error = 0;
++      if (old_bh && old_bh != new_bh) {
++              /*
++               * If there was an old block, and we are not still using it,
++               * we now release the old block.
++              */
++              unsigned int refcount = le32_to_cpu(HDR(old_bh)->h_refcount);
++
++              error = ext3_journal_get_write_access(handle, old_bh);
++              if (error)
++                      goto cleanup;
++              if (refcount == 1) {
++                      /* Free the old block. */
++                      ea_bdebug(old_bh, "freeing");
++                      ext3_xattr_free_block(handle, inode, old_bh->b_blocknr);
++
++                      /* ext3_forget() calls bforget() for us, but we
++                         let our caller release old_bh, so we need to
++                         duplicate the handle before. */
++                      get_bh(old_bh);
++                      ext3_forget(handle, 1, inode, old_bh,old_bh->b_blocknr);
++              } else {
++                      /* Decrement the refcount only. */
++                      refcount--;
++                      HDR(old_bh)->h_refcount = cpu_to_le32(refcount);
++                      ext3_xattr_quota_free(inode);
++                      ext3_journal_dirty_metadata(handle, old_bh);
++                      ea_bdebug(old_bh, "refcount now=%d", refcount);
++              }
++      }
++
++cleanup:
++      if (old_bh != new_bh)
++              brelse(new_bh);
++
++      return error;
++}
++
++/*
++ * ext3_xattr_delete_inode()
++ *
++ * Free extended attribute resources associated with this inode. This
++ * is called immediately before an inode is freed.
++ */
++void
++ext3_xattr_delete_inode(handle_t *handle, struct inode *inode)
++{
++      struct buffer_head *bh;
++      unsigned int block = EXT3_I(inode)->i_file_acl;
++
++      if (!block)
++              return;
++      down(&ext3_xattr_sem);
++
++      bh = sb_bread(inode->i_sb, block);
++      if (!bh) {
++              ext3_error(inode->i_sb, "ext3_xattr_delete_inode",
++                      "inode %ld: block %d read error", inode->i_ino, block);
++              goto cleanup;
++      }
++      ea_bdebug(bh, "b_count=%d", atomic_read(&(bh->b_count)));
++      if (HDR(bh)->h_magic != cpu_to_le32(EXT3_XATTR_MAGIC) ||
++          HDR(bh)->h_blocks != cpu_to_le32(1)) {
++              ext3_error(inode->i_sb, "ext3_xattr_delete_inode",
++                      "inode %ld: bad block %d", inode->i_ino, block);
++              goto cleanup;
++      }
++      ext3_journal_get_write_access(handle, bh);
++      ea_bdebug(bh, "refcount now=%d", le32_to_cpu(HDR(bh)->h_refcount) - 1);
++      if (HDR(bh)->h_refcount == cpu_to_le32(1)) {
++              ext3_xattr_cache_remove(bh);
++              ext3_xattr_free_block(handle, inode, block);
++              ext3_forget(handle, 1, inode, bh, block);
++              bh = NULL;
++      } else {
++              HDR(bh)->h_refcount = cpu_to_le32(
++                      le32_to_cpu(HDR(bh)->h_refcount) - 1);
++              ext3_journal_dirty_metadata(handle, bh);
++              if (IS_SYNC(inode))
++                      handle->h_sync = 1;
++              ext3_xattr_quota_free(inode);
++      }
++      EXT3_I(inode)->i_file_acl = 0;
++
++cleanup:
++      brelse(bh);
++      up(&ext3_xattr_sem);
++}
++
++/*
++ * ext3_xattr_put_super()
++ *
++ * This is called when a file system is unmounted.
++ */
++void
++ext3_xattr_put_super(struct super_block *sb)
++{
++#ifdef CONFIG_EXT3_FS_XATTR_SHARING
++      mb_cache_shrink(ext3_xattr_cache, sb->s_dev);
++#endif
++}
++
++#ifdef CONFIG_EXT3_FS_XATTR_SHARING
++
++/*
++ * ext3_xattr_cache_insert()
++ *
++ * Create a new entry in the extended attribute cache, and insert
++ * it unless such an entry is already in the cache.
++ *
++ * Returns 0, or a negative error number on failure.
++ */
++static int
++ext3_xattr_cache_insert(struct buffer_head *bh)
++{
++      __u32 hash = le32_to_cpu(HDR(bh)->h_hash);
++      struct mb_cache_entry *ce;
++      int error;
++
++      ce = mb_cache_entry_alloc(ext3_xattr_cache);
++      if (!ce)
++              return -ENOMEM;
++      error = mb_cache_entry_insert(ce, bh->b_dev, bh->b_blocknr, &hash);
++      if (error) {
++              mb_cache_entry_free(ce);
++              if (error == -EBUSY) {
++                      ea_bdebug(bh, "already in cache (%d cache entries)",
++                              atomic_read(&ext3_xattr_cache->c_entry_count));
++                      error = 0;
++              }
++      } else {
++              ea_bdebug(bh, "inserting [%x] (%d cache entries)", (int)hash,
++                        atomic_read(&ext3_xattr_cache->c_entry_count));
++              mb_cache_entry_release(ce);
++      }
++      return error;
++}
++
++/*
++ * ext3_xattr_cmp()
++ *
++ * Compare two extended attribute blocks for equality.
++ *
++ * Returns 0 if the blocks are equal, 1 if they differ, and
++ * a negative error number on errors.
++ */
++static int
++ext3_xattr_cmp(struct ext3_xattr_header *header1,
++             struct ext3_xattr_header *header2)
++{
++      struct ext3_xattr_entry *entry1, *entry2;
++
++      entry1 = ENTRY(header1+1);
++      entry2 = ENTRY(header2+1);
++      while (!IS_LAST_ENTRY(entry1)) {
++              if (IS_LAST_ENTRY(entry2))
++                      return 1;
++              if (entry1->e_hash != entry2->e_hash ||
++                  entry1->e_name_len != entry2->e_name_len ||
++                  entry1->e_value_size != entry2->e_value_size ||
++                  memcmp(entry1->e_name, entry2->e_name, entry1->e_name_len))
++                      return 1;
++              if (entry1->e_value_block != 0 || entry2->e_value_block != 0)
++                      return -EIO;
++              if (memcmp((char *)header1 + le16_to_cpu(entry1->e_value_offs),
++                         (char *)header2 + le16_to_cpu(entry2->e_value_offs),
++                         le32_to_cpu(entry1->e_value_size)))
++                      return 1;
++
++              entry1 = EXT3_XATTR_NEXT(entry1);
++              entry2 = EXT3_XATTR_NEXT(entry2);
++      }
++      if (!IS_LAST_ENTRY(entry2))
++              return 1;
++      return 0;
++}
++
++/*
++ * ext3_xattr_cache_find()
++ *
++ * Find an identical extended attribute block.
++ *
++ * Returns a pointer to the block found, or NULL if such a block was
++ * not found or an error occurred.
++ */
++static struct buffer_head *
++ext3_xattr_cache_find(struct inode *inode, struct ext3_xattr_header *header)
++{
++      __u32 hash = le32_to_cpu(header->h_hash);
++      struct mb_cache_entry *ce;
++
++      if (!header->h_hash)
++              return NULL;  /* never share */
++      ea_idebug(inode, "looking for cached blocks [%x]", (int)hash);
++      ce = mb_cache_entry_find_first(ext3_xattr_cache, 0, inode->i_dev, hash);
++      while (ce) {
++              struct buffer_head *bh = sb_bread(inode->i_sb, ce->e_block);
++
++              if (!bh) {
++                      ext3_error(inode->i_sb, "ext3_xattr_cache_find",
++                              "inode %ld: block %ld read error",
++                              inode->i_ino, ce->e_block);
++              } else if (le32_to_cpu(HDR(bh)->h_refcount) >
++                         EXT3_XATTR_REFCOUNT_MAX) {
++                      ea_idebug(inode, "block %ld refcount %d>%d",ce->e_block,
++                              le32_to_cpu(HDR(bh)->h_refcount),
++                              EXT3_XATTR_REFCOUNT_MAX);
++              } else if (!ext3_xattr_cmp(header, HDR(bh))) {
++                      ea_bdebug(bh, "b_count=%d",atomic_read(&(bh->b_count)));
++                      mb_cache_entry_release(ce);
++                      return bh;
++              }
++              brelse(bh);
++              ce = mb_cache_entry_find_next(ce, 0, inode->i_dev, hash);
++      }
++      return NULL;
++}
++
++/*
++ * ext3_xattr_cache_remove()
++ *
++ * Remove the cache entry of a block from the cache. Called when a
++ * block becomes invalid.
++ */
++static void
++ext3_xattr_cache_remove(struct buffer_head *bh)
++{
++      struct mb_cache_entry *ce;
++
++      ce = mb_cache_entry_get(ext3_xattr_cache, bh->b_dev, bh->b_blocknr);
++      if (ce) {
++              ea_bdebug(bh, "removing (%d cache entries remaining)",
++                        atomic_read(&ext3_xattr_cache->c_entry_count)-1);
++              mb_cache_entry_free(ce);
++      } else 
++              ea_bdebug(bh, "no cache entry");
++}
++
++#define NAME_HASH_SHIFT 5
++#define VALUE_HASH_SHIFT 16
++
++/*
++ * ext3_xattr_hash_entry()
++ *
++ * Compute the hash of an extended attribute.
++ */
++static inline void ext3_xattr_hash_entry(struct ext3_xattr_header *header,
++                                       struct ext3_xattr_entry *entry)
++{
++      __u32 hash = 0;
++      char *name = entry->e_name;
++      int n;
++
++      for (n=0; n < entry->e_name_len; n++) {
++              hash = (hash << NAME_HASH_SHIFT) ^
++                     (hash >> (8*sizeof(hash) - NAME_HASH_SHIFT)) ^
++                     *name++;
++      }
++
++      if (entry->e_value_block == 0 && entry->e_value_size != 0) {
++              __u32 *value = (__u32 *)((char *)header +
++                      le16_to_cpu(entry->e_value_offs));
++              for (n = (le32_to_cpu(entry->e_value_size) +
++                   EXT3_XATTR_ROUND) >> EXT3_XATTR_PAD_BITS; n; n--) {
++                      hash = (hash << VALUE_HASH_SHIFT) ^
++                             (hash >> (8*sizeof(hash) - VALUE_HASH_SHIFT)) ^
++                             le32_to_cpu(*value++);
++              }
++      }
++      entry->e_hash = cpu_to_le32(hash);
++}
++
++#undef NAME_HASH_SHIFT
++#undef VALUE_HASH_SHIFT
++
++#define BLOCK_HASH_SHIFT 16
++
++/*
++ * ext3_xattr_rehash()
++ *
++ * Re-compute the extended attribute hash value after an entry has changed.
++ */
++static void ext3_xattr_rehash(struct ext3_xattr_header *header,
++                            struct ext3_xattr_entry *entry)
++{
++      struct ext3_xattr_entry *here;
++      __u32 hash = 0;
++      
++      ext3_xattr_hash_entry(header, entry);
++      here = ENTRY(header+1);
++      while (!IS_LAST_ENTRY(here)) {
++              if (!here->e_hash) {
++                      /* Block is not shared if an entry's hash value == 0 */
++                      hash = 0;
++                      break;
++              }
++              hash = (hash << BLOCK_HASH_SHIFT) ^
++                     (hash >> (8*sizeof(hash) - BLOCK_HASH_SHIFT)) ^
++                     le32_to_cpu(here->e_hash);
++              here = EXT3_XATTR_NEXT(here);
++      }
++      header->h_hash = cpu_to_le32(hash);
++}
++
++#undef BLOCK_HASH_SHIFT
++
++int __init
++init_ext3_xattr(void)
++{
++      ext3_xattr_cache = mb_cache_create("ext3_xattr", NULL,
++              sizeof(struct mb_cache_entry) +
++              sizeof(struct mb_cache_entry_index), 1, 61);
++      if (!ext3_xattr_cache)
++              return -ENOMEM;
++
++      return 0;
++}
++
++void
++exit_ext3_xattr(void)
++{
++      if (ext3_xattr_cache)
++              mb_cache_destroy(ext3_xattr_cache);
++      ext3_xattr_cache = NULL;
++}
++
++#else  /* CONFIG_EXT3_FS_XATTR_SHARING */
++
++int __init
++init_ext3_xattr(void)
++{
++      return 0;
++}
++
++void
++exit_ext3_xattr(void)
++{
++}
++
++#endif  /* CONFIG_EXT3_FS_XATTR_SHARING */
+Index: linux-2.4.29/fs/ext3/xattr_user.c
+===================================================================
+--- linux-2.4.29.orig/fs/ext3/xattr_user.c     2005-05-03 17:59:40.234146648 +0300
++++ linux-2.4.29/fs/ext3/xattr_user.c  2005-05-03 17:59:40.429117008 +0300
+@@ -0,0 +1,111 @@
++/*
++ * linux/fs/ext3/xattr_user.c
++ * Handler for extended user attributes.
++ *
++ * Copyright (C) 2001 by Andreas Gruenbacher, <a.gruenbacher@computer.org>
++ */
++
++#include <linux/module.h>
++#include <linux/string.h>
++#include <linux/fs.h>
++#include <linux/ext3_jbd.h>
++#include <linux/ext3_fs.h>
++#include <linux/ext3_xattr.h>
++
++#ifdef CONFIG_EXT3_FS_POSIX_ACL
++# include <linux/ext3_acl.h>
++#endif
++
++#define XATTR_USER_PREFIX "user."
++
++static size_t
++ext3_xattr_user_list(char *list, struct inode *inode,
++                   const char *name, int name_len)
++{
++      const int prefix_len = sizeof(XATTR_USER_PREFIX)-1;
++
++      if (!test_opt(inode->i_sb, XATTR_USER))
++              return 0;
++
++      if (list) {
++              memcpy(list, XATTR_USER_PREFIX, prefix_len);
++              memcpy(list+prefix_len, name, name_len);
++              list[prefix_len + name_len] = '\0';
++      }
++      return prefix_len + name_len + 1;
++}
++
++static int
++ext3_xattr_user_get(struct inode *inode, const char *name,
++                  void *buffer, size_t size)
++{
++      int error;
++
++      if (strcmp(name, "") == 0)
++              return -EINVAL;
++      if (!test_opt(inode->i_sb, XATTR_USER))
++              return -ENOTSUP;
++#ifdef CONFIG_EXT3_FS_POSIX_ACL
++      error = ext3_permission_locked(inode, MAY_READ);
++#else
++      error = permission(inode, MAY_READ);
++#endif
++      if (error)
++              return error;
++
++      return ext3_xattr_get(inode, EXT3_XATTR_INDEX_USER, name,
++                            buffer, size);
++}
++
++static int
++ext3_xattr_user_set(struct inode *inode, const char *name,
++                  const void *value, size_t size, int flags)
++{
++      handle_t *handle;
++      int error;
++
++      if (strcmp(name, "") == 0)
++              return -EINVAL;
++      if (!test_opt(inode->i_sb, XATTR_USER))
++              return -ENOTSUP;
++      if ( !S_ISREG(inode->i_mode) &&
++          (!S_ISDIR(inode->i_mode) || inode->i_mode & S_ISVTX))
++              return -EPERM;
++#ifdef CONFIG_EXT3_FS_POSIX_ACL
++      error = ext3_permission_locked(inode, MAY_WRITE);
++#else
++      error = permission(inode, MAY_WRITE);
++#endif
++      if (error)
++              return error;
++
++      handle = ext3_journal_start(inode, EXT3_XATTR_TRANS_BLOCKS);
++      if (IS_ERR(handle))
++              return PTR_ERR(handle);
++      error = ext3_xattr_set(handle, inode, EXT3_XATTR_INDEX_USER, name,
++                             value, size, flags);
++      ext3_journal_stop(handle, inode);
++
++      return error;
++}
++
++struct ext3_xattr_handler ext3_xattr_user_handler = {
++      prefix: XATTR_USER_PREFIX,
++      list:   ext3_xattr_user_list,
++      get:    ext3_xattr_user_get,
++      set:    ext3_xattr_user_set,
++};
++
++int __init
++init_ext3_xattr_user(void)
++{
++      return ext3_xattr_register(EXT3_XATTR_INDEX_USER,
++                                 &ext3_xattr_user_handler);
++}
++
++void
++exit_ext3_xattr_user(void)
++{
++      ext3_xattr_unregister(EXT3_XATTR_INDEX_USER,
++                            &ext3_xattr_user_handler);
++}
+Index: linux-2.4.29/fs/ext3/ext3-exports.c
+===================================================================
+--- linux-2.4.29.orig/fs/ext3/ext3-exports.c   2005-05-03 17:59:40.234146648 +0300
++++ linux-2.4.29/fs/ext3/ext3-exports.c        2005-05-03 18:00:08.195895816 +0300
+@@ -0,0 +1,13 @@
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/ext3_fs.h>
++#include <linux/ext3_jbd.h>
++#include <linux/ext3_xattr.h>
++
++EXPORT_SYMBOL(ext3_force_commit);
++EXPORT_SYMBOL(ext3_bread);
++EXPORT_SYMBOL(ext3_xattr_register);
++EXPORT_SYMBOL(ext3_xattr_unregister);
++EXPORT_SYMBOL(ext3_xattr_get);
++EXPORT_SYMBOL(ext3_xattr_list);
++EXPORT_SYMBOL(ext3_xattr_set);
+Index: linux-2.4.29/fs/jfs/jfs_xattr.h
+===================================================================
+--- linux-2.4.29.orig/fs/jfs/jfs_xattr.h       2005-04-07 18:53:29.000000000 +0300
++++ linux-2.4.29/fs/jfs/jfs_xattr.h    2005-05-03 17:59:40.431116704 +0300
+@@ -52,8 +52,10 @@
+ #define       END_EALIST(ealist) \
+       ((struct jfs_ea *) (((char *) (ealist)) + EALIST_SIZE(ealist)))
+-extern int __jfs_setxattr(struct inode *, const char *, void *, size_t, int);
+-extern int jfs_setxattr(struct dentry *, const char *, void *, size_t, int);
++extern int __jfs_setxattr(struct inode *, const char *, const void *, size_t,
++                        int);
++extern int jfs_setxattr(struct dentry *, const char *, const void *, size_t,
++                      int);
+ extern ssize_t __jfs_getxattr(struct inode *, const char *, void *, size_t);
+ extern ssize_t jfs_getxattr(struct dentry *, const char *, void *, size_t);
+ extern ssize_t jfs_listxattr(struct dentry *, char *, size_t);
+Index: linux-2.4.29/fs/jfs/xattr.c
+===================================================================
+--- linux-2.4.29.orig/fs/jfs/xattr.c   2005-04-07 18:52:32.000000000 +0300
++++ linux-2.4.29/fs/jfs/xattr.c        2005-05-03 17:59:40.433116400 +0300
+@@ -649,7 +649,7 @@
+ }
+ static int can_set_xattr(struct inode *inode, const char *name,
+-                       void *value, size_t value_len)
++                       const void *value, size_t value_len)
+ {
+       if (IS_RDONLY(inode))
+               return -EROFS;
+@@ -668,7 +668,7 @@
+       return permission(inode, MAY_WRITE);
+ }
+-int __jfs_setxattr(struct inode *inode, const char *name, void *value,
++int __jfs_setxattr(struct inode *inode, const char *name, const void *value,
+                  size_t value_len, int flags)
+ {
+       struct jfs_ea_list *ealist;
+@@ -807,7 +807,7 @@
+       return rc;
+ }
+-int jfs_setxattr(struct dentry *dentry, const char *name, void *value,
++int jfs_setxattr(struct dentry *dentry, const char *name, const void *value,
+                size_t value_len, int flags)
+ {
+       if (value == NULL) {    /* empty EA, do not remove */
+Index: linux-2.4.29/fs/mbcache.c
+===================================================================
+--- linux-2.4.29.orig/fs/mbcache.c     2005-05-03 17:59:40.235146496 +0300
++++ linux-2.4.29/fs/mbcache.c  2005-05-03 17:59:40.436115944 +0300
+@@ -0,0 +1,648 @@
++/*
++ * linux/fs/mbcache.c
++ * (C) 2001-2002 Andreas Gruenbacher, <a.gruenbacher@computer.org>
++ */
++
++/*
++ * Filesystem Meta Information Block Cache (mbcache)
++ *
++ * The mbcache caches blocks of block devices that need to be located
++ * by their device/block number, as well as by other criteria (such
++ * as the block's contents).
++ *
++ * There can only be one cache entry in a cache per device and block number.
++ * Additional indexes need not be unique in this sense. The number of
++ * additional indexes (=other criteria) can be hardwired at compile time
++ * or specified at cache create time.
++ *
++ * Each cache entry is of fixed size. An entry may be `valid' or `invalid'
++ * in the cache. A valid entry is in the main hash tables of the cache,
++ * and may also be in the lru list. An invalid entry is not in any hashes
++ * or lists.
++ *
++ * A valid cache entry is only in the lru list if no handles refer to it.
++ * Invalid cache entries will be freed when the last handle to the cache
++ * entry is released. Entries that cannot be freed immediately are put
++ * back on the lru list.
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++
++#include <linux/fs.h>
++#include <linux/slab.h>
++#include <linux/sched.h>
++#include <linux/cache_def.h>
++#include <linux/version.h>
++#include <linux/init.h>
++#include <linux/mbcache.h>
++
++
++#ifdef MB_CACHE_DEBUG
++# define mb_debug(f...) do { \
++              printk(KERN_DEBUG f); \
++              printk("\n"); \
++      } while (0)
++#define mb_assert(c) do { if (!(c)) \
++              printk(KERN_ERR "assertion " #c " failed\n"); \
++      } while(0)
++#else
++# define mb_debug(f...) do { } while(0)
++# define mb_assert(c) do { } while(0)
++#endif
++#define mb_error(f...) do { \
++              printk(KERN_ERR f); \
++              printk("\n"); \
++      } while(0)
++              
++MODULE_AUTHOR("Andreas Gruenbacher <a.gruenbacher@computer.org>");
++MODULE_DESCRIPTION("Meta block cache (for extended attributes)");
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
++MODULE_LICENSE("GPL");
++#endif
++
++EXPORT_SYMBOL(mb_cache_create);
++EXPORT_SYMBOL(mb_cache_shrink);
++EXPORT_SYMBOL(mb_cache_destroy);
++EXPORT_SYMBOL(mb_cache_entry_alloc);
++EXPORT_SYMBOL(mb_cache_entry_insert);
++EXPORT_SYMBOL(mb_cache_entry_release);
++EXPORT_SYMBOL(mb_cache_entry_takeout);
++EXPORT_SYMBOL(mb_cache_entry_free);
++EXPORT_SYMBOL(mb_cache_entry_dup);
++EXPORT_SYMBOL(mb_cache_entry_get);
++#if !defined(MB_CACHE_INDEXES_COUNT) || (MB_CACHE_INDEXES_COUNT > 0)
++EXPORT_SYMBOL(mb_cache_entry_find_first);
++EXPORT_SYMBOL(mb_cache_entry_find_next);
++#endif
++
++
++/*
++ * Global data: list of all mbcache's, lru list, and a spinlock for
++ * accessing cache data structures on SMP machines. The lru list is
++ * global across all mbcaches.
++ */
++
++static LIST_HEAD(mb_cache_list);
++static LIST_HEAD(mb_cache_lru_list);
++static spinlock_t mb_cache_spinlock = SPIN_LOCK_UNLOCKED;
++
++static inline int
++mb_cache_indexes(struct mb_cache *cache)
++{
++#ifdef MB_CACHE_INDEXES_COUNT
++      return MB_CACHE_INDEXES_COUNT;
++#else
++      return cache->c_indexes_count;
++#endif
++}
++
++/*
++ * What the mbcache registers as to get shrunk dynamically.
++ */
++
++static void
++mb_cache_memory_pressure(int priority, unsigned int gfp_mask);
++
++static struct cache_definition mb_cache_definition = {
++      "mb_cache",
++      mb_cache_memory_pressure
++};
++
++
++static inline int
++__mb_cache_entry_is_hashed(struct mb_cache_entry *ce)
++{
++      return !list_empty(&ce->e_block_list);
++}
++
++
++static inline void
++__mb_cache_entry_unhash(struct mb_cache_entry *ce)
++{
++      int n;
++
++      if (__mb_cache_entry_is_hashed(ce)) {
++              list_del_init(&ce->e_block_list);
++              for (n=0; n<mb_cache_indexes(ce->e_cache); n++)
++                      list_del(&ce->e_indexes[n].o_list);
++      }
++}
++
++
++static inline void
++__mb_cache_entry_forget(struct mb_cache_entry *ce, int gfp_mask)
++{
++      struct mb_cache *cache = ce->e_cache;
++
++      mb_assert(atomic_read(&ce->e_used) == 0);
++      if (cache->c_op.free && cache->c_op.free(ce, gfp_mask)) {
++              /* free failed -- put back on the lru list
++                 for freeing later. */
++              spin_lock(&mb_cache_spinlock);
++              list_add(&ce->e_lru_list, &mb_cache_lru_list);
++              spin_unlock(&mb_cache_spinlock);
++      } else {
++              kmem_cache_free(cache->c_entry_cache, ce);
++              atomic_dec(&cache->c_entry_count);
++      }
++}
++
++
++static inline void
++__mb_cache_entry_release_unlock(struct mb_cache_entry *ce)
++{
++      if (atomic_dec_and_test(&ce->e_used)) {
++              if (__mb_cache_entry_is_hashed(ce))
++                      list_add_tail(&ce->e_lru_list, &mb_cache_lru_list);
++              else {
++                      spin_unlock(&mb_cache_spinlock);
++                      __mb_cache_entry_forget(ce, GFP_KERNEL);
++                      return;
++              }
++      }
++      spin_unlock(&mb_cache_spinlock);
++}
++
++
++/*
++ * mb_cache_memory_pressure()  memory pressure callback
++ *
++ * This function is called by the kernel memory management when memory
++ * gets low.
++ *
++ * @priority: Amount by which to shrink the cache (0 = highes priority)
++ * @gfp_mask: (ignored)
++ */
++static void
++mb_cache_memory_pressure(int priority, unsigned int gfp_mask)
++{
++      LIST_HEAD(free_list);
++      struct list_head *l, *ltmp;
++      int count = 0;
++
++      spin_lock(&mb_cache_spinlock);
++      list_for_each(l, &mb_cache_list) {
++              struct mb_cache *cache =
++                      list_entry(l, struct mb_cache, c_cache_list);
++              mb_debug("cache %s (%d)", cache->c_name,
++                        atomic_read(&cache->c_entry_count));
++              count += atomic_read(&cache->c_entry_count);
++      }
++      mb_debug("trying to free %d of %d entries",
++                count / (priority ? priority : 1), count);
++      if (priority)
++              count /= priority;
++      while (count-- && !list_empty(&mb_cache_lru_list)) {
++              struct mb_cache_entry *ce =
++                      list_entry(mb_cache_lru_list.next,
++                                 struct mb_cache_entry, e_lru_list);
++              list_del(&ce->e_lru_list);
++              __mb_cache_entry_unhash(ce);
++              list_add_tail(&ce->e_lru_list, &free_list);
++      }
++      spin_unlock(&mb_cache_spinlock);
++      list_for_each_safe(l, ltmp, &free_list) {
++              __mb_cache_entry_forget(list_entry(l, struct mb_cache_entry,
++                                                 e_lru_list), gfp_mask);
++      }
++}
++
++
++/*
++ * mb_cache_create()  create a new cache
++ *
++ * All entries in one cache are equal size. Cache entries may be from
++ * multiple devices. If this is the first mbcache created, registers
++ * the cache with kernel memory management. Returns NULL if no more
++ * memory was available.
++ *
++ * @name: name of the cache (informal)
++ * @cache_op: contains the callback called when freeing a cache entry
++ * @entry_size: The size of a cache entry, including
++ *              struct mb_cache_entry
++ * @indexes_count: number of additional indexes in the cache. Must equal
++ *                 MB_CACHE_INDEXES_COUNT if the number of indexes is
++ *                 hardwired.
++ * @bucket_count: number of hash buckets
++ */
++struct mb_cache *
++mb_cache_create(const char *name, struct mb_cache_op *cache_op,
++              size_t entry_size, int indexes_count, int bucket_count)
++{
++      int m=0, n;
++      struct mb_cache *cache = NULL;
++
++      if(entry_size < sizeof(struct mb_cache_entry) +
++         indexes_count * sizeof(struct mb_cache_entry_index))
++              return NULL;
++
++      MOD_INC_USE_COUNT;
++      cache = kmalloc(sizeof(struct mb_cache) +
++                      indexes_count * sizeof(struct list_head), GFP_KERNEL);
++      if (!cache)
++              goto fail;
++      cache->c_name = name;
++      cache->c_op.free = NULL;
++      if (cache_op)
++              cache->c_op.free = cache_op->free;
++      atomic_set(&cache->c_entry_count, 0);
++      cache->c_bucket_count = bucket_count;
++#ifdef MB_CACHE_INDEXES_COUNT
++      mb_assert(indexes_count == MB_CACHE_INDEXES_COUNT);
++#else
++      cache->c_indexes_count = indexes_count;
++#endif
++      cache->c_block_hash = kmalloc(bucket_count * sizeof(struct list_head),
++                                    GFP_KERNEL);
++      if (!cache->c_block_hash)
++              goto fail;
++      for (n=0; n<bucket_count; n++)
++              INIT_LIST_HEAD(&cache->c_block_hash[n]);
++      for (m=0; m<indexes_count; m++) {
++              cache->c_indexes_hash[m] = kmalloc(bucket_count *
++                                               sizeof(struct list_head),
++                                               GFP_KERNEL);
++              if (!cache->c_indexes_hash[m])
++                      goto fail;
++              for (n=0; n<bucket_count; n++)
++                      INIT_LIST_HEAD(&cache->c_indexes_hash[m][n]);
++      }
++      cache->c_entry_cache = kmem_cache_create(name, entry_size, 0,
++              0 /*SLAB_POISON | SLAB_RED_ZONE*/, NULL, NULL);
++      if (!cache->c_entry_cache)
++              goto fail;
++
++      spin_lock(&mb_cache_spinlock);
++      list_add(&cache->c_cache_list, &mb_cache_list);
++      spin_unlock(&mb_cache_spinlock);
++      return cache;
++
++fail:
++      if (cache) {
++              while (--m >= 0)
++                      kfree(cache->c_indexes_hash[m]);
++              if (cache->c_block_hash)
++                      kfree(cache->c_block_hash);
++              kfree(cache);
++      }
++      MOD_DEC_USE_COUNT;
++      return NULL;
++}
++
++
++/*
++ * mb_cache_shrink()
++ *
++ * Removes all cache entires of a device from the cache. All cache entries
++ * currently in use cannot be freed, and thus remain in the cache.
++ *
++ * @cache: which cache to shrink
++ * @dev: which device's cache entries to shrink
++ */
++void
++mb_cache_shrink(struct mb_cache *cache, kdev_t dev)
++{
++      LIST_HEAD(free_list);
++      struct list_head *l, *ltmp;
++
++      spin_lock(&mb_cache_spinlock);
++      list_for_each_safe(l, ltmp, &mb_cache_lru_list) {
++              struct mb_cache_entry *ce =
++                      list_entry(l, struct mb_cache_entry, e_lru_list);
++              if (ce->e_dev == dev) {
++                      list_del(&ce->e_lru_list);
++                      list_add_tail(&ce->e_lru_list, &free_list);
++                      __mb_cache_entry_unhash(ce);
++              }
++      }
++      spin_unlock(&mb_cache_spinlock);
++      list_for_each_safe(l, ltmp, &free_list) {
++              __mb_cache_entry_forget(list_entry(l, struct mb_cache_entry,
++                                                 e_lru_list), GFP_KERNEL);
++      }
++}
++
++
++/*
++ * mb_cache_destroy()
++ *
++ * Shrinks the cache to its minimum possible size (hopefully 0 entries),
++ * and then destroys it. If this was the last mbcache, un-registers the
++ * mbcache from kernel memory management.
++ */
++void
++mb_cache_destroy(struct mb_cache *cache)
++{
++      LIST_HEAD(free_list);
++      struct list_head *l, *ltmp;
++      int n;
++
++      spin_lock(&mb_cache_spinlock);
++      list_for_each_safe(l, ltmp, &mb_cache_lru_list) {
++              struct mb_cache_entry *ce =
++                      list_entry(l, struct mb_cache_entry, e_lru_list);
++              if (ce->e_cache == cache) {
++                      list_del(&ce->e_lru_list);
++                      list_add_tail(&ce->e_lru_list, &free_list);
++                      __mb_cache_entry_unhash(ce);
++              }
++      }
++      list_del(&cache->c_cache_list);
++      spin_unlock(&mb_cache_spinlock);
++      list_for_each_safe(l, ltmp, &free_list) {
++              __mb_cache_entry_forget(list_entry(l, struct mb_cache_entry,
++                                                 e_lru_list), GFP_KERNEL);
++      }
++
++      if (atomic_read(&cache->c_entry_count) > 0) {
++              mb_error("cache %s: %d orphaned entries",
++                        cache->c_name,
++                        atomic_read(&cache->c_entry_count));
++      }
++
++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
++      /* We don't have kmem_cache_destroy() in 2.2.x */
++      kmem_cache_shrink(cache->c_entry_cache);
++#else
++      kmem_cache_destroy(cache->c_entry_cache);
++#endif
++      for (n=0; n < mb_cache_indexes(cache); n++)
++              kfree(cache->c_indexes_hash[n]);
++      kfree(cache->c_block_hash);
++      kfree(cache);
++
++      MOD_DEC_USE_COUNT;
++}
++
++
++/*
++ * mb_cache_entry_alloc()
++ *
++ * Allocates a new cache entry. The new entry will not be valid initially,
++ * and thus cannot be looked up yet. It should be filled with data, and
++ * then inserted into the cache using mb_cache_entry_insert(). Returns NULL
++ * if no more memory was available.
++ */
++struct mb_cache_entry *
++mb_cache_entry_alloc(struct mb_cache *cache)
++{
++      struct mb_cache_entry *ce;
++
++      atomic_inc(&cache->c_entry_count);
++      ce = kmem_cache_alloc(cache->c_entry_cache, GFP_KERNEL);
++      if (ce) {
++              INIT_LIST_HEAD(&ce->e_lru_list);
++              INIT_LIST_HEAD(&ce->e_block_list);
++              ce->e_cache = cache;
++              atomic_set(&ce->e_used, 1);
++      }
++      return ce;
++}
++
++
++/*
++ * mb_cache_entry_insert()
++ *
++ * Inserts an entry that was allocated using mb_cache_entry_alloc() into
++ * the cache. After this, the cache entry can be looked up, but is not yet
++ * in the lru list as the caller still holds a handle to it. Returns 0 on
++ * success, or -EBUSY if a cache entry for that device + inode exists
++ * already (this may happen after a failed lookup, if another process has
++ * inserted the same cache entry in the meantime).
++ *
++ * @dev: device the cache entry belongs to
++ * @block: block number
++ * @keys: array of additional keys. There must be indexes_count entries
++ *        in the array (as specified when creating the cache).
++ */
++int
++mb_cache_entry_insert(struct mb_cache_entry *ce, kdev_t dev,
++                    unsigned long block, unsigned int keys[])
++{
++      struct mb_cache *cache = ce->e_cache;
++      unsigned int bucket = (HASHDEV(dev) + block) % cache->c_bucket_count;
++      struct list_head *l;
++      int error = -EBUSY, n;
++
++      spin_lock(&mb_cache_spinlock);
++      list_for_each(l, &cache->c_block_hash[bucket]) {
++              struct mb_cache_entry *ce =
++                      list_entry(l, struct mb_cache_entry, e_block_list);
++              if (ce->e_dev == dev && ce->e_block == block)
++                      goto out;
++      }
++      __mb_cache_entry_unhash(ce);
++      ce->e_dev = dev;
++      ce->e_block = block;
++      list_add(&ce->e_block_list, &cache->c_block_hash[bucket]);
++      for (n=0; n<mb_cache_indexes(cache); n++) {
++              ce->e_indexes[n].o_key = keys[n];
++              bucket = keys[n] % cache->c_bucket_count;
++              list_add(&ce->e_indexes[n].o_list,
++                       &cache->c_indexes_hash[n][bucket]);
++      }
++out:
++      spin_unlock(&mb_cache_spinlock);
++      return error;
++}
++
++
++/*
++ * mb_cache_entry_release()
++ *
++ * Release a handle to a cache entry. When the last handle to a cache entry
++ * is released it is either freed (if it is invalid) or otherwise inserted
++ * in to the lru list.
++ */
++void
++mb_cache_entry_release(struct mb_cache_entry *ce)
++{
++      spin_lock(&mb_cache_spinlock);
++      __mb_cache_entry_release_unlock(ce);
++}
++
++
++/*
++ * mb_cache_entry_takeout()
++ *
++ * Take a cache entry out of the cache, making it invalid. The entry can later
++ * be re-inserted using mb_cache_entry_insert(), or released using
++ * mb_cache_entry_release().
++ */
++void
++mb_cache_entry_takeout(struct mb_cache_entry *ce)
++{
++      spin_lock(&mb_cache_spinlock);
++      mb_assert(list_empty(&ce->e_lru_list));
++      __mb_cache_entry_unhash(ce);
++      spin_unlock(&mb_cache_spinlock);
++}
++
++
++/*
++ * mb_cache_entry_free()
++ *
++ * This is equivalent to the sequence mb_cache_entry_takeout() --
++ * mb_cache_entry_release().
++ */
++void
++mb_cache_entry_free(struct mb_cache_entry *ce)
++{
++      spin_lock(&mb_cache_spinlock);
++      mb_assert(list_empty(&ce->e_lru_list));
++      __mb_cache_entry_unhash(ce);
++      __mb_cache_entry_release_unlock(ce);
++}
++
++
++/*
++ * mb_cache_entry_dup()
++ *
++ * Duplicate a handle to a cache entry (does not duplicate the cache entry
++ * itself). After the call, both the old and the new handle must be released.
++ */
++struct mb_cache_entry *
++mb_cache_entry_dup(struct mb_cache_entry *ce)
++{
++      atomic_inc(&ce->e_used);
++      return ce;
++}
++
++
++/*
++ * mb_cache_entry_get()
++ *
++ * Get a cache entry  by device / block number. (There can only be one entry
++ * in the cache per device and block.) Returns NULL if no such cache entry
++ * exists.
++ */
++struct mb_cache_entry *
++mb_cache_entry_get(struct mb_cache *cache, kdev_t dev, unsigned long block)
++{
++      unsigned int bucket = (HASHDEV(dev) + block) % cache->c_bucket_count;
++      struct list_head *l;
++      struct mb_cache_entry *ce;
++
++      spin_lock(&mb_cache_spinlock);
++      list_for_each(l, &cache->c_block_hash[bucket]) {
++              ce = list_entry(l, struct mb_cache_entry, e_block_list);
++              if (ce->e_dev == dev && ce->e_block == block) {
++                      if (!list_empty(&ce->e_lru_list))
++                              list_del_init(&ce->e_lru_list);
++                      atomic_inc(&ce->e_used);
++                      goto cleanup;
++              }
++      }
++      ce = NULL;
++
++cleanup:
++      spin_unlock(&mb_cache_spinlock);
++      return ce;
++}
++
++#if !defined(MB_CACHE_INDEXES_COUNT) || (MB_CACHE_INDEXES_COUNT > 0)
++
++static struct mb_cache_entry *
++__mb_cache_entry_find(struct list_head *l, struct list_head *head,
++                    int index, kdev_t dev, unsigned int key)
++{
++      while (l != head) {
++              struct mb_cache_entry *ce =
++                      list_entry(l, struct mb_cache_entry,
++                                 e_indexes[index].o_list);
++              if (ce->e_dev == dev && ce->e_indexes[index].o_key == key) {
++                      if (!list_empty(&ce->e_lru_list))
++                              list_del_init(&ce->e_lru_list);
++                      atomic_inc(&ce->e_used);
++                      return ce;
++              }
++              l = l->next;
++      }
++      return NULL;
++}
++
++
++/*
++ * mb_cache_entry_find_first()
++ *
++ * Find the first cache entry on a given device with a certain key in
++ * an additional index. Additonal matches can be found with
++ * mb_cache_entry_find_next(). Returns NULL if no match was found.
++ *
++ * @cache: the cache to search
++ * @index: the number of the additonal index to search (0<=index<indexes_count)
++ * @dev: the device the cache entry should belong to
++ * @key: the key in the index
++ */
++struct mb_cache_entry *
++mb_cache_entry_find_first(struct mb_cache *cache, int index, kdev_t dev,
++                        unsigned int key)
++{
++      unsigned int bucket = key % cache->c_bucket_count;
++      struct list_head *l;
++      struct mb_cache_entry *ce;
++
++      mb_assert(index < mb_cache_indexes(cache));
++      spin_lock(&mb_cache_spinlock);
++      l = cache->c_indexes_hash[index][bucket].next;
++      ce = __mb_cache_entry_find(l, &cache->c_indexes_hash[index][bucket],
++                                 index, dev, key);
++      spin_unlock(&mb_cache_spinlock);
++      return ce;
++}
++
++
++/*
++ * mb_cache_entry_find_next()
++ *
++ * Find the next cache entry on a given device with a certain key in an
++ * additional index. Returns NULL if no match could be found. The previous
++ * entry is atomatically released, so that mb_cache_entry_find_next() can
++ * be called like this:
++ *
++ * entry = mb_cache_entry_find_first();
++ * while (entry) {
++ *    ...
++ *    entry = mb_cache_entry_find_next(entry, ...);
++ * }
++ *
++ * @prev: The previous match
++ * @index: the number of the additonal index to search (0<=index<indexes_count)
++ * @dev: the device the cache entry should belong to
++ * @key: the key in the index
++ */
++struct mb_cache_entry *
++mb_cache_entry_find_next(struct mb_cache_entry *prev, int index, kdev_t dev,
++                       unsigned int key)
++{
++      struct mb_cache *cache = prev->e_cache;
++      unsigned int bucket = key % cache->c_bucket_count;
++      struct list_head *l;
++      struct mb_cache_entry *ce;
++
++      mb_assert(index < mb_cache_indexes(cache));
++      spin_lock(&mb_cache_spinlock);
++      l = prev->e_indexes[index].o_list.next;
++      ce = __mb_cache_entry_find(l, &cache->c_indexes_hash[index][bucket],
++                                 index, dev, key);
++      __mb_cache_entry_release_unlock(prev);
++      return ce;
++}
++
++#endif  /* !defined(MB_CACHE_INDEXES_COUNT) || (MB_CACHE_INDEXES_COUNT > 0) */
++
++static int __init init_mbcache(void)
++{
++      register_cache(&mb_cache_definition);
++      return 0;
++}
++
++static void __exit exit_mbcache(void)
++{
++      unregister_cache(&mb_cache_definition);
++}
++
++module_init(init_mbcache)
++module_exit(exit_mbcache)
++
+Index: linux-2.4.29/include/asm-arm/unistd.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-arm/unistd.h 2005-04-07 18:55:01.000000000 +0300
++++ linux-2.4.29/include/asm-arm/unistd.h      2005-05-03 17:59:40.438115640 +0300
+@@ -250,7 +250,6 @@
+ #define __NR_security                 (__NR_SYSCALL_BASE+223)
+ #define __NR_gettid                   (__NR_SYSCALL_BASE+224)
+ #define __NR_readahead                        (__NR_SYSCALL_BASE+225)
+-#if 0 /* allocated in 2.5 */
+ #define __NR_setxattr                 (__NR_SYSCALL_BASE+226)
+ #define __NR_lsetxattr                        (__NR_SYSCALL_BASE+227)
+ #define __NR_fsetxattr                        (__NR_SYSCALL_BASE+228)
+@@ -263,7 +262,6 @@
+ #define __NR_removexattr              (__NR_SYSCALL_BASE+235)
+ #define __NR_lremovexattr             (__NR_SYSCALL_BASE+236)
+ #define __NR_fremovexattr             (__NR_SYSCALL_BASE+237)
+-#endif
+ #define __NR_tkill                    (__NR_SYSCALL_BASE+238)
+ #if 0 /* allocated in 2.5 */
+ #define __NR_sendfile64                 (__NR_SYSCALL_BASE+239)
+Index: linux-2.4.29/include/asm-ppc64/unistd.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-ppc64/unistd.h       2005-04-07 18:52:47.000000000 +0300
++++ linux-2.4.29/include/asm-ppc64/unistd.h    2005-05-03 17:59:40.439115488 +0300
+@@ -218,6 +218,7 @@
+ #define __NR_mincore          206
+ #define __NR_gettid           207
+ #define __NR_tkill            208
++#endif
+ #define __NR_setxattr         209
+ #define __NR_lsetxattr                210
+ #define __NR_fsetxattr                211
+@@ -230,6 +231,7 @@
+ #define __NR_removexattr      218
+ #define __NR_lremovexattr     219
+ #define __NR_fremovexattr     220
++#if 0 /* Reserved syscalls */
+ #define __NR_futex            221
+ #define __NR_sched_setaffinity        222     
+ #define __NR_sched_getaffinity        223
+Index: linux-2.4.29/include/asm-s390/unistd.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-s390/unistd.h        2005-04-07 18:55:23.000000000 +0300
++++ linux-2.4.29/include/asm-s390/unistd.h     2005-05-03 17:59:40.440115336 +0300
+@@ -213,9 +213,18 @@
+ #define __NR_getdents64               220
+ #define __NR_fcntl64          221
+ #define __NR_readahead                222
+-/*
+- * Numbers 224-235 are reserved for posix acl
+- */
++#define __NR_setxattr         224
++#define __NR_lsetxattr                225
++#define __NR_fsetxattr                226
++#define __NR_getxattr         227
++#define __NR_lgetxattr                228
++#define __NR_fgetxattr                229
++#define __NR_listxattr                230
++#define __NR_llistxattr               231
++#define __NR_flistxattr               232
++#define __NR_removexattr      233
++#define __NR_lremovexattr     234
++#define __NR_fremovexattr     235
+ #define __NR_gettid           236
+ #define __NR_tkill            237
+Index: linux-2.4.29/include/asm-s390x/unistd.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-s390x/unistd.h       2005-04-07 18:54:22.000000000 +0300
++++ linux-2.4.29/include/asm-s390x/unistd.h    2005-05-03 17:59:40.441115184 +0300
+@@ -181,9 +181,18 @@
+ #define __NR_mincore            218
+ #define __NR_madvise            219
+ #define __NR_readahead                222
+-/*
+- * Numbers 224-235 are reserved for posix acl
+- */
++#define __NR_setxattr         224
++#define __NR_lsetxattr                225
++#define __NR_fsetxattr                226
++#define __NR_getxattr         227
++#define __NR_lgetxattr                228
++#define __NR_fgetxattr                229
++#define __NR_listxattr                230
++#define __NR_llistxattr               231
++#define __NR_flistxattr               232
++#define __NR_removexattr      233
++#define __NR_lremovexattr     234
++#define __NR_fremovexattr     235
+ #define __NR_gettid           236
+ #define __NR_tkill            237
+Index: linux-2.4.29/include/linux/cache_def.h
+===================================================================
+--- linux-2.4.29.orig/include/linux/cache_def.h        2005-05-03 17:59:40.235146496 +0300
++++ linux-2.4.29/include/linux/cache_def.h     2005-05-03 17:59:40.442115032 +0300
+@@ -0,0 +1,15 @@
++/*
++ * linux/cache_def.h
++ * Handling of caches defined in drivers, filesystems, ...
++ *
++ * Copyright (C) 2002 by Andreas Gruenbacher, <a.gruenbacher@computer.org>
++ */
++
++struct cache_definition {
++      const char *name;
++      void (*shrink)(int, unsigned int);
++      struct list_head link;
++};
++
++extern void register_cache(struct cache_definition *);
++extern void unregister_cache(struct cache_definition *);
+Index: linux-2.4.29/include/linux/errno.h
+===================================================================
+--- linux-2.4.29.orig/include/linux/errno.h    2005-04-07 18:54:43.000000000 +0300
++++ linux-2.4.29/include/linux/errno.h 2005-05-03 17:59:40.443114880 +0300
+@@ -23,4 +23,8 @@
+ #endif
++/* Defined for extended attributes */
++#define ENOATTR ENODATA               /* No such attribute */
++#define ENOTSUP EOPNOTSUPP    /* Operation not supported */
++
+ #endif
+Index: linux-2.4.29/include/linux/ext2_fs.h
+===================================================================
+--- linux-2.4.29.orig/include/linux/ext2_fs.h  2005-04-07 18:52:37.000000000 +0300
++++ linux-2.4.29/include/linux/ext2_fs.h       2005-05-03 17:59:40.445114576 +0300
+@@ -57,8 +57,6 @@
+  */
+ #define       EXT2_BAD_INO             1      /* Bad blocks inode */
+ #define EXT2_ROOT_INO          2      /* Root inode */
+-#define EXT2_ACL_IDX_INO       3      /* ACL inode */
+-#define EXT2_ACL_DATA_INO      4      /* ACL inode */
+ #define EXT2_BOOT_LOADER_INO   5      /* Boot loader inode */
+ #define EXT2_UNDEL_DIR_INO     6      /* Undelete directory inode */
+@@ -86,7 +84,6 @@
+ #else
+ # define EXT2_BLOCK_SIZE(s)           (EXT2_MIN_BLOCK_SIZE << (s)->s_log_block_size)
+ #endif
+-#define EXT2_ACLE_PER_BLOCK(s)                (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_acl_entry))
+ #define       EXT2_ADDR_PER_BLOCK(s)          (EXT2_BLOCK_SIZE(s) / sizeof (__u32))
+ #ifdef __KERNEL__
+ # define EXT2_BLOCK_SIZE_BITS(s)      ((s)->s_blocksize_bits)
+@@ -121,28 +118,6 @@
+ #endif
+ /*
+- * ACL structures
+- */
+-struct ext2_acl_header        /* Header of Access Control Lists */
+-{
+-      __u32   aclh_size;
+-      __u32   aclh_file_count;
+-      __u32   aclh_acle_count;
+-      __u32   aclh_first_acle;
+-};
+-
+-struct ext2_acl_entry /* Access Control List Entry */
+-{
+-      __u32   acle_size;
+-      __u16   acle_perms;     /* Access permissions */
+-      __u16   acle_type;      /* Type of entry */
+-      __u16   acle_tag;       /* User or group identity */
+-      __u16   acle_pad1;
+-      __u32   acle_next;      /* Pointer on next entry for the */
+-                                      /* same inode or on next free entry */
+-};
+-
+-/*
+  * Structure of a blocks group descriptor
+  */
+ struct ext2_group_desc
+@@ -314,6 +289,7 @@
+ #define EXT2_MOUNT_ERRORS_PANIC               0x0040  /* Panic on errors */
+ #define EXT2_MOUNT_MINIX_DF           0x0080  /* Mimics the Minix statfs */
+ #define EXT2_MOUNT_NO_UID32           0x0200  /* Disable 32-bit UIDs */
++#define EXT2_MOUNT_XATTR_USER         0x4000  /* Extended user attributes */
+ #define clear_opt(o, opt)             o &= ~EXT2_MOUNT_##opt
+ #define set_opt(o, opt)                       o |= EXT2_MOUNT_##opt
+@@ -410,6 +386,7 @@
+ #ifdef __KERNEL__
+ #define EXT2_SB(sb)   (&((sb)->u.ext2_sb))
++#define EXT2_I(inode) (&((inode)->u.ext2_i))
+ #else
+ /* Assume that user mode programs are passing in an ext2fs superblock, not
+  * a kernel struct super_block.  This will allow us to call the feature-test
+@@ -480,7 +457,7 @@
+ #define EXT2_FEATURE_INCOMPAT_META_BG         0x0010
+ #define EXT2_FEATURE_INCOMPAT_ANY             0xffffffff
+-#define EXT2_FEATURE_COMPAT_SUPP      0
++#define EXT2_FEATURE_COMPAT_SUPP      EXT2_FEATURE_COMPAT_EXT_ATTR
+ #define EXT2_FEATURE_INCOMPAT_SUPP    (EXT2_FEATURE_INCOMPAT_FILETYPE| \
+                                        EXT2_FEATURE_INCOMPAT_META_BG)
+ #define EXT2_FEATURE_RO_COMPAT_SUPP   (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \
+@@ -650,8 +627,10 @@
+ /* namei.c */
+ extern struct inode_operations ext2_dir_inode_operations;
++extern struct inode_operations ext2_special_inode_operations;
+ /* symlink.c */
++extern struct inode_operations ext2_symlink_inode_operations;
+ extern struct inode_operations ext2_fast_symlink_inode_operations;
+ #endif        /* __KERNEL__ */
+Index: linux-2.4.29/include/linux/ext2_xattr.h
+===================================================================
+--- linux-2.4.29.orig/include/linux/ext2_xattr.h       2005-05-03 17:59:40.236146344 +0300
++++ linux-2.4.29/include/linux/ext2_xattr.h    2005-05-03 17:59:40.446114424 +0300
+@@ -0,0 +1,157 @@
++/*
++  File: linux/ext2_xattr.h
++
++  On-disk format of extended attributes for the ext2 filesystem.
++
++  (C) 2001 Andreas Gruenbacher, <a.gruenbacher@computer.org>
++*/
++
++#include <linux/config.h>
++#include <linux/init.h>
++#include <linux/xattr.h>
++
++/* Magic value in attribute blocks */
++#define EXT2_XATTR_MAGIC              0xEA020000
++
++/* Maximum number of references to one attribute block */
++#define EXT2_XATTR_REFCOUNT_MAX               1024
++
++/* Name indexes */
++#define EXT2_XATTR_INDEX_MAX                  10
++#define EXT2_XATTR_INDEX_USER                 1
++#define EXT2_XATTR_INDEX_POSIX_ACL_ACCESS     2
++#define EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT    3
++
++struct ext2_xattr_header {
++      __u32   h_magic;        /* magic number for identification */
++      __u32   h_refcount;     /* reference count */
++      __u32   h_blocks;       /* number of disk blocks used */
++      __u32   h_hash;         /* hash value of all attributes */
++      __u32   h_reserved[4];  /* zero right now */
++};
++
++struct ext2_xattr_entry {
++      __u8    e_name_len;     /* length of name */
++      __u8    e_name_index;   /* attribute name index */
++      __u16   e_value_offs;   /* offset in disk block of value */
++      __u32   e_value_block;  /* disk block attribute is stored on (n/i) */
++      __u32   e_value_size;   /* size of attribute value */
++      __u32   e_hash;         /* hash value of name and value */
++      char    e_name[0];      /* attribute name */
++};
++
++#define EXT2_XATTR_PAD_BITS           2
++#define EXT2_XATTR_PAD                (1<<EXT2_XATTR_PAD_BITS)
++#define EXT2_XATTR_ROUND              (EXT2_XATTR_PAD-1)
++#define EXT2_XATTR_LEN(name_len) \
++      (((name_len) + EXT2_XATTR_ROUND + \
++      sizeof(struct ext2_xattr_entry)) & ~EXT2_XATTR_ROUND)
++#define EXT2_XATTR_NEXT(entry) \
++      ( (struct ext2_xattr_entry *)( \
++        (char *)(entry) + EXT2_XATTR_LEN((entry)->e_name_len)) )
++#define EXT2_XATTR_SIZE(size) \
++      (((size) + EXT2_XATTR_ROUND) & ~EXT2_XATTR_ROUND)
++
++#ifdef __KERNEL__
++
++# ifdef CONFIG_EXT2_FS_XATTR
++
++struct ext2_xattr_handler {
++      char *prefix;
++      size_t (*list)(char *list, struct inode *inode, const char *name,
++                     int name_len);
++      int (*get)(struct inode *inode, const char *name, void *buffer,
++                 size_t size);
++      int (*set)(struct inode *inode, const char *name, const void *buffer,
++                 size_t size, int flags);
++};
++
++extern int ext2_xattr_register(int, struct ext2_xattr_handler *);
++extern void ext2_xattr_unregister(int, struct ext2_xattr_handler *);
++
++extern int ext2_setxattr(struct dentry *, const char *, const void *, size_t, int);
++extern ssize_t ext2_getxattr(struct dentry *, const char *, void *, size_t);
++extern ssize_t ext2_listxattr(struct dentry *, char *, size_t);
++extern int ext2_removexattr(struct dentry *, const char *);
++
++extern int ext2_xattr_get(struct inode *, int, const char *, void *, size_t);
++extern int ext2_xattr_list(struct inode *, char *, size_t);
++extern int ext2_xattr_set(struct inode *, int, const char *, const void *, size_t, int);
++
++extern void ext2_xattr_delete_inode(struct inode *);
++extern void ext2_xattr_put_super(struct super_block *);
++
++extern int init_ext2_xattr(void) __init;
++extern void exit_ext2_xattr(void);
++
++# else  /* CONFIG_EXT2_FS_XATTR */
++#  define ext2_setxattr               NULL
++#  define ext2_getxattr               NULL
++#  define ext2_listxattr      NULL
++#  define ext2_removexattr    NULL
++
++static inline int
++ext2_xattr_get(struct inode *inode, int name_index,
++             const char *name, void *buffer, size_t size)
++{
++      return -ENOTSUP;
++}
++
++static inline int
++ext2_xattr_list(struct inode *inode, char *buffer, size_t size)
++{
++      return -ENOTSUP;
++}
++
++static inline int
++ext2_xattr_set(struct inode *inode, int name_index, const char *name,
++             const void *value, size_t size, int flags)
++{
++      return -ENOTSUP;
++}
++
++static inline void
++ext2_xattr_delete_inode(struct inode *inode)
++{
++}
++
++static inline void
++ext2_xattr_put_super(struct super_block *sb)
++{
++}
++
++static inline int
++init_ext2_xattr(void)
++{
++      return 0;
++}
++
++static inline void
++exit_ext2_xattr(void)
++{
++}
++
++# endif  /* CONFIG_EXT2_FS_XATTR */
++
++# ifdef CONFIG_EXT2_FS_XATTR_USER
++
++extern int init_ext2_xattr_user(void) __init;
++extern void exit_ext2_xattr_user(void);
++
++# else  /* CONFIG_EXT2_FS_XATTR_USER */
++
++static inline int
++init_ext2_xattr_user(void)
++{
++      return 0;
++}
++
++static inline void
++exit_ext2_xattr_user(void)
++{
++}
++
++# endif  /* CONFIG_EXT2_FS_XATTR_USER */
++
++#endif  /* __KERNEL__ */
++
+Index: linux-2.4.29/include/linux/ext3_fs.h
+===================================================================
+--- linux-2.4.29.orig/include/linux/ext3_fs.h  2005-05-03 17:23:54.107407504 +0300
++++ linux-2.4.29/include/linux/ext3_fs.h       2005-05-03 17:59:40.448114120 +0300
+@@ -63,8 +63,6 @@
+  */
+ #define       EXT3_BAD_INO             1      /* Bad blocks inode */
+ #define EXT3_ROOT_INO          2      /* Root inode */
+-#define EXT3_ACL_IDX_INO       3      /* ACL inode */
+-#define EXT3_ACL_DATA_INO      4      /* ACL inode */
+ #define EXT3_BOOT_LOADER_INO   5      /* Boot loader inode */
+ #define EXT3_UNDEL_DIR_INO     6      /* Undelete directory inode */
+ #define EXT3_RESIZE_INO                7      /* Reserved group descriptors inode */
+@@ -94,7 +92,6 @@
+ #else
+ # define EXT3_BLOCK_SIZE(s)           (EXT3_MIN_BLOCK_SIZE << (s)->s_log_block_size)
+ #endif
+-#define EXT3_ACLE_PER_BLOCK(s)                (EXT3_BLOCK_SIZE(s) / sizeof (struct ext3_acl_entry))
+ #define       EXT3_ADDR_PER_BLOCK(s)          (EXT3_BLOCK_SIZE(s) / sizeof (__u32))
+ #ifdef __KERNEL__
+ # define EXT3_BLOCK_SIZE_BITS(s)      ((s)->s_blocksize_bits)
+@@ -129,28 +126,6 @@
+ #endif
+ /*
+- * ACL structures
+- */
+-struct ext3_acl_header        /* Header of Access Control Lists */
+-{
+-      __u32   aclh_size;
+-      __u32   aclh_file_count;
+-      __u32   aclh_acle_count;
+-      __u32   aclh_first_acle;
+-};
+-
+-struct ext3_acl_entry /* Access Control List Entry */
+-{
+-      __u32   acle_size;
+-      __u16   acle_perms;     /* Access permissions */
+-      __u16   acle_type;      /* Type of entry */
+-      __u16   acle_tag;       /* User or group identity */
+-      __u16   acle_pad1;
+-      __u32   acle_next;      /* Pointer on next entry for the */
+-                                      /* same inode or on next free entry */
+-};
+-
+-/*
+  * Structure of a blocks group descriptor
+  */
+ struct ext3_group_desc
+@@ -344,6 +319,7 @@
+   #define EXT3_MOUNT_WRITEBACK_DATA   0x0C00  /* No data ordering */
+ #define EXT3_MOUNT_UPDATE_JOURNAL     0x1000  /* Update the journal format */
+ #define EXT3_MOUNT_NO_UID32           0x2000  /* Disable 32-bit UIDs */
++#define EXT3_MOUNT_XATTR_USER         0x4000  /* Extended user attributes */
+ /* Compatibility, for having both ext2_fs.h and ext3_fs.h included at once */
+ #ifndef _LINUX_EXT2_FS_H
+@@ -524,7 +500,7 @@
+ #define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV     0x0008 /* Journal device */
+ #define EXT3_FEATURE_INCOMPAT_META_BG         0x0010
+-#define EXT3_FEATURE_COMPAT_SUPP      0
++#define EXT3_FEATURE_COMPAT_SUPP      EXT2_FEATURE_COMPAT_EXT_ATTR
+ #define EXT3_FEATURE_INCOMPAT_SUPP    (EXT3_FEATURE_INCOMPAT_FILETYPE| \
+                                        EXT3_FEATURE_INCOMPAT_RECOVER| \
+                                        EXT3_FEATURE_INCOMPAT_META_BG)
+@@ -718,6 +694,7 @@
+ extern unsigned long ext3_count_free (struct buffer_head *, unsigned);
+ /* inode.c */
++extern int ext3_forget(handle_t *, int, struct inode *, struct buffer_head *, int);
+ extern struct buffer_head * ext3_getblk (handle_t *, struct inode *, long, int, int *);
+ extern struct buffer_head * ext3_bread (handle_t *, struct inode *, int, int, int *);
+@@ -787,8 +764,10 @@
+ /* namei.c */
+ extern struct inode_operations ext3_dir_inode_operations;
++extern struct inode_operations ext3_special_inode_operations;
+ /* symlink.c */
++extern struct inode_operations ext3_symlink_inode_operations;
+ extern struct inode_operations ext3_fast_symlink_inode_operations;
+Index: linux-2.4.29/include/linux/ext3_jbd.h
+===================================================================
+--- linux-2.4.29.orig/include/linux/ext3_jbd.h 2005-05-03 17:23:54.109407200 +0300
++++ linux-2.4.29/include/linux/ext3_jbd.h      2005-05-03 17:59:40.449113968 +0300
+@@ -30,13 +30,19 @@
+ #define EXT3_SINGLEDATA_TRANS_BLOCKS  8U
++/* Extended attributes may touch two data buffers, two bitmap buffers,
++ * and two group and summaries. */
++
++#define EXT3_XATTR_TRANS_BLOCKS               8
++
+ /* Define the minimum size for a transaction which modifies data.  This
+  * needs to take into account the fact that we may end up modifying two
+  * quota files too (one for the group, one for the user quota).  The
+  * superblock only gets updated once, of course, so don't bother
+  * counting that again for the quota updates. */
+-#define EXT3_DATA_TRANS_BLOCKS                (3 * EXT3_SINGLEDATA_TRANS_BLOCKS - 2)
++#define EXT3_DATA_TRANS_BLOCKS                (3 * EXT3_SINGLEDATA_TRANS_BLOCKS + \
++                                       EXT3_XATTR_TRANS_BLOCKS - 2)
+ extern int ext3_writepage_trans_blocks(struct inode *inode);
+Index: linux-2.4.29/include/linux/ext3_xattr.h
+===================================================================
+--- linux-2.4.29.orig/include/linux/ext3_xattr.h       2005-05-03 17:59:40.236146344 +0300
++++ linux-2.4.29/include/linux/ext3_xattr.h    2005-05-03 17:59:40.451113664 +0300
+@@ -0,0 +1,157 @@
++/*
++  File: linux/ext3_xattr.h
++
++  On-disk format of extended attributes for the ext3 filesystem.
++
++  (C) 2001 Andreas Gruenbacher, <a.gruenbacher@computer.org>
++*/
++
++#include <linux/config.h>
++#include <linux/init.h>
++#include <linux/xattr.h>
++
++/* Magic value in attribute blocks */
++#define EXT3_XATTR_MAGIC              0xEA020000
++
++/* Maximum number of references to one attribute block */
++#define EXT3_XATTR_REFCOUNT_MAX               1024
++
++/* Name indexes */
++#define EXT3_XATTR_INDEX_MAX                  10
++#define EXT3_XATTR_INDEX_USER                 1
++#define EXT3_XATTR_INDEX_POSIX_ACL_ACCESS     2
++#define EXT3_XATTR_INDEX_POSIX_ACL_DEFAULT    3
++
++struct ext3_xattr_header {
++      __u32   h_magic;        /* magic number for identification */
++      __u32   h_refcount;     /* reference count */
++      __u32   h_blocks;       /* number of disk blocks used */
++      __u32   h_hash;         /* hash value of all attributes */
++      __u32   h_reserved[4];  /* zero right now */
++};
++
++struct ext3_xattr_entry {
++      __u8    e_name_len;     /* length of name */
++      __u8    e_name_index;   /* attribute name index */
++      __u16   e_value_offs;   /* offset in disk block of value */
++      __u32   e_value_block;  /* disk block attribute is stored on (n/i) */
++      __u32   e_value_size;   /* size of attribute value */
++      __u32   e_hash;         /* hash value of name and value */
++      char    e_name[0];      /* attribute name */
++};
++
++#define EXT3_XATTR_PAD_BITS           2
++#define EXT3_XATTR_PAD                (1<<EXT3_XATTR_PAD_BITS)
++#define EXT3_XATTR_ROUND              (EXT3_XATTR_PAD-1)
++#define EXT3_XATTR_LEN(name_len) \
++      (((name_len) + EXT3_XATTR_ROUND + \
++      sizeof(struct ext3_xattr_entry)) & ~EXT3_XATTR_ROUND)
++#define EXT3_XATTR_NEXT(entry) \
++      ( (struct ext3_xattr_entry *)( \
++        (char *)(entry) + EXT3_XATTR_LEN((entry)->e_name_len)) )
++#define EXT3_XATTR_SIZE(size) \
++      (((size) + EXT3_XATTR_ROUND) & ~EXT3_XATTR_ROUND)
++
++#ifdef __KERNEL__
++
++# ifdef CONFIG_EXT3_FS_XATTR
++
++struct ext3_xattr_handler {
++      char *prefix;
++      size_t (*list)(char *list, struct inode *inode, const char *name,
++                     int name_len);
++      int (*get)(struct inode *inode, const char *name, void *buffer,
++                 size_t size);
++      int (*set)(struct inode *inode, const char *name, const void *buffer,
++                 size_t size, int flags);
++};
++
++extern int ext3_xattr_register(int, struct ext3_xattr_handler *);
++extern void ext3_xattr_unregister(int, struct ext3_xattr_handler *);
++
++extern int ext3_setxattr(struct dentry *, const char *, const void *, size_t, int);
++extern ssize_t ext3_getxattr(struct dentry *, const char *, void *, size_t);
++extern ssize_t ext3_listxattr(struct dentry *, char *, size_t);
++extern int ext3_removexattr(struct dentry *, const char *);
++
++extern int ext3_xattr_get(struct inode *, int, const char *, void *, size_t);
++extern int ext3_xattr_list(struct inode *, char *, size_t);
++extern int ext3_xattr_set(handle_t *handle, struct inode *, int, const char *, const void *, size_t, int);
++
++extern void ext3_xattr_delete_inode(handle_t *, struct inode *);
++extern void ext3_xattr_put_super(struct super_block *);
++
++extern int init_ext3_xattr(void) __init;
++extern void exit_ext3_xattr(void);
++
++# else  /* CONFIG_EXT3_FS_XATTR */
++#  define ext3_setxattr               NULL
++#  define ext3_getxattr               NULL
++#  define ext3_listxattr      NULL
++#  define ext3_removexattr    NULL
++
++static inline int
++ext3_xattr_get(struct inode *inode, int name_index, const char *name,
++             void *buffer, size_t size)
++{
++      return -ENOTSUP;
++}
++
++static inline int
++ext3_xattr_list(struct inode *inode, void *buffer, size_t size)
++{
++      return -ENOTSUP;
++}
++
++static inline int
++ext3_xattr_set(handle_t *handle, struct inode *inode, int name_index,
++             const char *name, const void *value, size_t size, int flags)
++{
++      return -ENOTSUP;
++}
++
++static inline void
++ext3_xattr_delete_inode(handle_t *handle, struct inode *inode)
++{
++}
++
++static inline void
++ext3_xattr_put_super(struct super_block *sb)
++{
++}
++
++static inline int
++init_ext3_xattr(void)
++{
++      return 0;
++}
++
++static inline void
++exit_ext3_xattr(void)
++{
++}
++
++# endif  /* CONFIG_EXT3_FS_XATTR */
++
++# ifdef CONFIG_EXT3_FS_XATTR_USER
++
++extern int init_ext3_xattr_user(void) __init;
++extern void exit_ext3_xattr_user(void);
++
++# else  /* CONFIG_EXT3_FS_XATTR_USER */
++
++static inline int
++init_ext3_xattr_user(void)
++{
++      return 0;
++}
++
++static inline void
++exit_ext3_xattr_user(void)
++{
++}
++
++#endif  /* CONFIG_EXT3_FS_XATTR_USER */
++
++#endif  /* __KERNEL__ */
++
+Index: linux-2.4.29/include/linux/fs.h
+===================================================================
+--- linux-2.4.29.orig/include/linux/fs.h       2005-05-03 17:23:53.736463896 +0300
++++ linux-2.4.29/include/linux/fs.h    2005-05-03 17:59:40.453113360 +0300
+@@ -915,7 +915,7 @@
+       int (*setattr) (struct dentry *, struct iattr *);
+       int (*setattr_raw) (struct inode *, struct iattr *);
+       int (*getattr) (struct dentry *, struct iattr *);
+-      int (*setxattr) (struct dentry *, const char *, void *, size_t, int);
++      int (*setxattr) (struct dentry *, const char *, const void *, size_t, int);
+       ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
+       ssize_t (*listxattr) (struct dentry *, char *, size_t);
+       int (*removexattr) (struct dentry *, const char *);
+Index: linux-2.4.29/include/linux/mbcache.h
+===================================================================
+--- linux-2.4.29.orig/include/linux/mbcache.h  2005-05-03 17:59:40.236146344 +0300
++++ linux-2.4.29/include/linux/mbcache.h       2005-05-03 17:59:40.454113208 +0300
+@@ -0,0 +1,69 @@
++/*
++  File: linux/mbcache.h
++
++  (C) 2001 by Andreas Gruenbacher, <a.gruenbacher@computer.org>
++*/
++
++/* Hardwire the number of additional indexes */
++#define MB_CACHE_INDEXES_COUNT 1
++
++struct mb_cache_entry;
++
++struct mb_cache_op {
++      int (*free)(struct mb_cache_entry *, int);
++};
++
++struct mb_cache {
++      struct list_head                c_cache_list;
++      const char                      *c_name;
++      struct mb_cache_op              c_op;
++      atomic_t                        c_entry_count;
++      int                             c_bucket_count;
++#ifndef MB_CACHE_INDEXES_COUNT
++      int                             c_indexes_count;
++#endif
++      kmem_cache_t                    *c_entry_cache;
++      struct list_head                *c_block_hash;
++      struct list_head                *c_indexes_hash[0];
++};
++
++struct mb_cache_entry_index {
++      struct list_head                o_list;
++      unsigned int                    o_key;
++};
++
++struct mb_cache_entry {
++      struct list_head                e_lru_list;
++      struct mb_cache                 *e_cache;
++      atomic_t                        e_used;
++      kdev_t                          e_dev;
++      unsigned long                   e_block;
++      struct list_head                e_block_list;
++      struct mb_cache_entry_index     e_indexes[0];
++};
++
++/* Functions on caches */
++
++struct mb_cache * mb_cache_create(const char *, struct mb_cache_op *, size_t,
++                                int, int);
++void mb_cache_shrink(struct mb_cache *, kdev_t);
++void mb_cache_destroy(struct mb_cache *);
++
++/* Functions on cache entries */
++
++struct mb_cache_entry *mb_cache_entry_alloc(struct mb_cache *);
++int mb_cache_entry_insert(struct mb_cache_entry *, kdev_t, unsigned long,
++                        unsigned int[]);
++void mb_cache_entry_rehash(struct mb_cache_entry *, unsigned int[]);
++void mb_cache_entry_release(struct mb_cache_entry *);
++void mb_cache_entry_takeout(struct mb_cache_entry *);
++void mb_cache_entry_free(struct mb_cache_entry *);
++struct mb_cache_entry *mb_cache_entry_dup(struct mb_cache_entry *);
++struct mb_cache_entry *mb_cache_entry_get(struct mb_cache *, kdev_t,
++                                        unsigned long);
++#if !defined(MB_CACHE_INDEXES_COUNT) || (MB_CACHE_INDEXES_COUNT > 0)
++struct mb_cache_entry *mb_cache_entry_find_first(struct mb_cache *cache, int,
++                                               kdev_t, unsigned int);
++struct mb_cache_entry *mb_cache_entry_find_next(struct mb_cache_entry *, int,
++                                              kdev_t, unsigned int);
++#endif
+Index: linux-2.4.29/kernel/ksyms.c
+===================================================================
+--- linux-2.4.29.orig/kernel/ksyms.c   2005-04-07 19:14:06.000000000 +0300
++++ linux-2.4.29/kernel/ksyms.c        2005-05-03 17:59:40.456112904 +0300
+@@ -11,6 +11,7 @@
+ #include <linux/config.h>
+ #include <linux/slab.h>
++#include <linux/cache_def.h>
+ #include <linux/module.h>
+ #include <linux/blkdev.h>
+ #include <linux/cdrom.h>
+@@ -92,6 +93,7 @@
+ EXPORT_SYMBOL(exit_files);
+ EXPORT_SYMBOL(exit_fs);
+ EXPORT_SYMBOL(exit_sighand);
++EXPORT_SYMBOL(copy_fs_struct);
+ /* internal kernel memory management */
+ EXPORT_SYMBOL(_alloc_pages);
+@@ -109,6 +111,8 @@
+ EXPORT_SYMBOL(kmem_cache_alloc);
+ EXPORT_SYMBOL(kmem_cache_free);
+ EXPORT_SYMBOL(kmem_cache_size);
++EXPORT_SYMBOL(register_cache);
++EXPORT_SYMBOL(unregister_cache);
+ EXPORT_SYMBOL(kmalloc);
+ EXPORT_SYMBOL(kfree);
+ EXPORT_SYMBOL(vfree);
+Index: linux-2.4.29/mm/vmscan.c
+===================================================================
+--- linux-2.4.29.orig/mm/vmscan.c      2005-04-07 18:52:37.000000000 +0300
++++ linux-2.4.29/mm/vmscan.c   2005-05-03 17:59:40.458112600 +0300
+@@ -18,6 +18,7 @@
+ #include <linux/kernel_stat.h>
+ #include <linux/swap.h>
+ #include <linux/swapctl.h>
++#include <linux/cache_def.h>
+ #include <linux/smp_lock.h>
+ #include <linux/pagemap.h>
+ #include <linux/init.h>
+@@ -34,6 +35,39 @@
+  */
+ int vm_passes = 60;
++static DECLARE_MUTEX(other_caches_sem);
++static LIST_HEAD(cache_definitions);
++
++void register_cache(struct cache_definition *cache)
++{
++      down(&other_caches_sem);
++      list_add(&cache->link, &cache_definitions);
++      up(&other_caches_sem);
++}
++
++void unregister_cache(struct cache_definition *cache)
++{
++      down(&other_caches_sem);
++      list_del(&cache->link);
++      up(&other_caches_sem);
++}
++
++static void shrink_other_caches(unsigned int priority, int gfp_mask)
++{
++      struct list_head *p;
++
++      if (down_trylock(&other_caches_sem))
++              return;
++
++      list_for_each_prev(p, &cache_definitions) {
++              struct cache_definition *cache =
++                      list_entry(p, struct cache_definition, link);
++
++              cache->shrink(priority, gfp_mask);
++      }
++      up(&other_caches_sem);
++}
++
+ /*
+  * "vm_cache_scan_ratio" is how much of the inactive LRU queue we will scan
+  * in one go. A value of 6 for vm_cache_scan_ratio implies that we'll
+@@ -544,6 +578,7 @@
+ #ifdef CONFIG_QUOTA
+                               shrink_dqcache_memory(vm_vfs_scan_ratio, gfp_mask);
+ #endif
++                              shrink_other_caches(vm_vfs_scan_ratio, gfp_mask);
+                               if (!*failed_swapout)
+                                       *failed_swapout = !swap_out(classzone);
+@@ -666,6 +701,7 @@
+ #ifdef CONFIG_QUOTA
+                       shrink_dqcache_memory(vm_vfs_scan_ratio, gfp_mask);
+ #endif
++                      shrink_other_caches(vm_vfs_scan_ratio, gfp_mask);
+                       if (!failed_swapout)
+                               failed_swapout = !swap_out(classzone);
+               } while (--tries);
index aa8c735..6a9810b 100644 (file)
@@ -1,3 +1,4 @@
+Version 46: Separate ->fs_private in struct file
 Version 45: more robust and general dev_read_only for failover (b=4834)  
 Version 44: fix link_path_walk_it() oops creating .foo in deleted "." (b=5548)
 Version 43: fix remove_suid to not crash 2.6, and do anything on 2.4 (b=5695)
@@ -19,6 +20,6 @@ Version 34: ext3 iopen assertion (b=2517), operations on deleted "." (b=2399)
 --- /dev/null  Fri Aug 30 17:31:37 2002
 +++ linux-2.4.18-18.8.0-l12-braam/include/linux/lustre_version.h       Thu Feb 13 07:58:33 2003
 @@ -0,0 +1 @@
-+#define LUSTRE_KERNEL_VERSION 45
++#define LUSTRE_KERNEL_VERSION 46
 
 _
index a8ad4a2..52ea4e1 100644 (file)
  
 --- linux-2.4.20-hp4-pnnl13/fs/nfsd/vfs.c~nfs_export_kernel-2.4.20-hp  2002-11-29 02:53:15.000000000 +0300
 +++ linux-2.4.20-hp4-pnnl13-alexey/fs/nfsd/vfs.c       2003-10-08 10:54:08.000000000 +0400
-@@ -77,6 +77,126 @@ struct raparms {
+@@ -77,6 +77,129 @@ struct raparms {
  static struct raparms *               raparml;
  static struct raparms *               raparm_cache;
  
 +      return err;
 +}
 +
++#ifndef O_OWNER_OVERRIDE
++#define O_OWNER_OVERRIDE 0200000000
++#endif
 +static int setattr_raw(struct inode *inode, struct iattr *iap)
 +{
 +      int err;
        err = nfserr_notsync;
        if (!check_guard || guardtime == inode->i_ctime) {
 -              err = notify_change(dentry, iap);
-+              if (dentry->d_inode->i_op && dentry->d_inode->i_op->setattr_raw)
++              if (dentry->d_inode->i_op &&dentry->d_inode->i_op->setattr_raw){
++                      if (acc_mode & MAY_OWNER_OVERRIDE)
++                              iap->ia_valid & O_OWNER_OVERRIDE;
 +                      err = setattr_raw(dentry->d_inode, iap);
-+              else
++              else
 +                      err = notify_change(dentry, iap);
                err = nfserrno(err);
        }
        int             err;
  
        /* If we get here, then the client has already done an "open", and (hopefully)
-@@ -473,6 +599,18 @@ nfsd_open(struct svc_rqst *rqstp, struct
+@@ -473,6 +599,15 @@ nfsd_open(struct svc_rqst *rqstp, struct
                filp->f_mode  = FMODE_READ;
        }
  
-+#ifndef O_OWNER_OVERRIDE
-+#define O_OWNER_OVERRIDE 0200000000
-+#endif
 +      intent_init(&it, IT_OPEN, (filp->f_flags & ~O_ACCMODE) | filp->f_mode |
 +                  O_OWNER_OVERRIDE);
 +
diff --git a/lustre/kernel_patches/patches/nfs_export_kernel-2.4.29.patch b/lustre/kernel_patches/patches/nfs_export_kernel-2.4.29.patch
new file mode 100644 (file)
index 0000000..4708b75
--- /dev/null
@@ -0,0 +1,743 @@
+Index: linux-2.4.29/fs/Makefile
+===================================================================
+--- linux-2.4.29.orig/fs/Makefile      2005-05-03 18:16:44.000000000 +0300
++++ linux-2.4.29/fs/Makefile   2005-05-03 18:46:09.301144016 +0300
+@@ -7,7 +7,8 @@
+ O_TARGET := fs.o
+-export-objs :=        filesystems.o open.o dcache.o buffer.o dquot.o inode.o
++export-objs :=        filesystems.o open.o dcache.o buffer.o dquot.o inode.o \
++              namei.o file_table.o
+ mod-subdirs :=        nls
+ obj-y :=      open.o read_write.o devices.o file_table.o buffer.o \
+Index: linux-2.4.29/fs/file_table.c
+===================================================================
+--- linux-2.4.29.orig/fs/file_table.c  2005-05-03 16:28:21.000000000 +0300
++++ linux-2.4.29/fs/file_table.c       2005-05-03 18:46:09.303143712 +0300
+@@ -82,7 +82,8 @@
+  * and call the open function (if any).  The caller must verify that
+  * inode->i_fop is not NULL.
+  */
+-int init_private_file(struct file *filp, struct dentry *dentry, int mode)
++int init_private_file_it(struct file *filp, struct dentry *dentry, int mode,
++                       struct lookup_intent *it)
+ {
+       memset(filp, 0, sizeof(*filp));
+       filp->f_mode   = mode;
+@@ -90,12 +91,20 @@
+       filp->f_dentry = dentry;
+       filp->f_uid    = current->fsuid;
+       filp->f_gid    = current->fsgid;
++      if (it)
++              filp->f_it = it;
+       filp->f_op     = dentry->d_inode->i_fop;
+       if (filp->f_op->open)
+               return filp->f_op->open(dentry->d_inode, filp);
+       else
+               return 0;
+ }
++EXPORT_SYMBOL(init_private_file_it);
++
++int init_private_file(struct file *filp, struct dentry *dentry, int mode)
++{
++      return init_private_file_it(filp, dentry, mode, NULL);
++}
+ void fastcall fput(struct file * file)
+ {
+Index: linux-2.4.29/fs/inode.c
+===================================================================
+--- linux-2.4.29.orig/fs/inode.c       2005-05-03 18:16:44.000000000 +0300
++++ linux-2.4.29/fs/inode.c    2005-05-03 18:51:36.389419040 +0300
+@@ -1139,9 +1139,10 @@
+       return inode;
+ }
+-struct inode *iget4_locked(struct super_block *sb, unsigned long ino, find_inode_t find_actor, void *opaque)
++static inline struct inode *ifind(struct super_block *sb, unsigned long ino,
++                                struct list_head *head,
++                                find_inode_t find_actor, void *opaque)
+ {
+-      struct list_head * head = inode_hashtable + hash(sb,ino);
+       struct inode * inode;
+       spin_lock(&inode_lock);
+@@ -1154,6 +1155,24 @@
+       }
+       spin_unlock(&inode_lock);
++      return NULL;
++}
++
++struct inode *ilookup4(struct super_block *sb, unsigned long ino,
++                     find_inode_t find_actor, void *opaque)
++{
++      struct list_head * head = inode_hashtable + hash(sb,ino);
++      return ifind(sb, ino, head, find_actor, opaque);
++}
++
++struct inode *iget4_locked(struct super_block *sb, unsigned long ino,
++                  find_inode_t find_actor, void *opaque)
++{
++      struct list_head * head = inode_hashtable + hash(sb,ino);
++      struct inode *inode = ifind(sb, ino, head, find_actor, opaque);
++      if (inode)
++              return inode;
++
+       /*
+        * get_new_inode() will do the right thing, re-trying the search
+        * in case it had to block at any point.
+Index: linux-2.4.29/fs/namei.c
+===================================================================
+--- linux-2.4.29.orig/fs/namei.c       2005-05-03 18:16:43.000000000 +0300
++++ linux-2.4.29/fs/namei.c    2005-05-03 18:46:09.310142648 +0300
+@@ -22,6 +22,7 @@
+ #include <linux/dnotify.h>
+ #include <linux/smp_lock.h>
+ #include <linux/personality.h>
++#include <linux/module.h>
+ #include <asm/namei.h>
+ #include <asm/uaccess.h>
+@@ -100,6 +101,7 @@
+               it->it_op_release(it);
+ }
++EXPORT_SYMBOL(intent_release);
+ /* In order to reduce some races, while at the same time doing additional
+  * checking and hopefully speeding things up, we copy filenames to the
+@@ -910,7 +912,8 @@
+ /* SMP-safe */
+-struct dentry * lookup_one_len(const char * name, struct dentry * base, int len)
++struct dentry * lookup_one_len_it(const char * name, struct dentry * base,
++                                int len, struct lookup_intent *it)
+ {
+       unsigned long hash;
+       struct qstr this;
+@@ -930,11 +933,16 @@
+       }
+       this.hash = end_name_hash(hash);
+-      return lookup_hash_it(&this, base, NULL);
++      return lookup_hash_it(&this, base, it);
+ access:
+       return ERR_PTR(-EACCES);
+ }
++struct dentry * lookup_one_len(const char * name, struct dentry * base, int len)
++{
++      return lookup_one_len_it(name, base, len, NULL);
++}
++
+ /*
+  *    namei()
+  *
+Index: linux-2.4.29/fs/nfsd/export.c
+===================================================================
+--- linux-2.4.29.orig/fs/nfsd/export.c 2005-05-03 16:28:21.000000000 +0300
++++ linux-2.4.29/fs/nfsd/export.c      2005-05-03 18:46:09.312142344 +0300
+@@ -223,6 +223,11 @@
+       inode = nd.dentry->d_inode;
+       dev = inode->i_dev;
+       ino = inode->i_ino;
++      if ((inode->i_sb->s_type->fs_flags & FS_NFSEXP_FSID) &&
++          !(nxp->ex_flags & NFSEXP_FSID)) {
++              nxp->ex_dev = inode->i_sb->s_dev;
++              nxp->ex_flags |= NFSEXP_FSID;
++      }
+       err = -EINVAL;
+       exp = exp_get(clp, dev, ino);
+Index: linux-2.4.29/fs/nfsd/nfsfh.c
+===================================================================
+--- linux-2.4.29.orig/fs/nfsd/nfsfh.c  2005-05-03 16:28:21.000000000 +0300
++++ linux-2.4.29/fs/nfsd/nfsfh.c       2005-05-03 18:46:09.315141888 +0300
+@@ -36,6 +36,13 @@
+       int sequence;           /* sequence counter */
+ };
++static struct dentry *lookup_it(struct inode *inode, struct dentry * dentry)
++{
++      if (inode->i_op->lookup_it)
++              return inode->i_op->lookup_it(inode, dentry, NULL, 0);
++      return inode->i_op->lookup(inode, dentry);
++}
++
+ /*
+  * A rather strange filldir function to capture
+  * the name matching the specified inode number.
+@@ -75,6 +82,8 @@
+       int error;
+       struct file file;
+       struct nfsd_getdents_callback buffer;
++      struct lookup_intent it;
++      struct file *filp = NULL;
+       error = -ENOTDIR;
+       if (!dir || !S_ISDIR(dir->i_mode))
+@@ -85,9 +94,37 @@
+       /*
+        * Open the directory ...
+        */
+-      error = init_private_file(&file, dentry, FMODE_READ);
++      if (dentry->d_op && dentry->d_op->d_revalidate_it) {
++              if ((dentry->d_flags & DCACHE_NFSD_DISCONNECTED) &&
++                  (dentry->d_parent == dentry) ) {
++                      it.it_op_release = NULL;
++                      /*
++                       * XXX Temporary Hack: Simulate init_private_file without
++                       * f_op->open for disconnected dentry as we don't have
++                       * actual dentry->d_name to revalidate in revalidate_it()
++                       */
++                      filp = &file;
++                      memset(filp, 0, sizeof(*filp));
++                      filp->f_mode   = FMODE_READ;
++                      atomic_set(&filp->f_count, 1);
++                      filp->f_dentry = dentry;
++                      filp->f_uid = current->fsuid;
++                      filp->f_gid = current->fsgid;
++                      filp->f_op = dentry->d_inode->i_fop;
++                      error = 0;
++              } else {
++                      intent_init(&it, IT_OPEN, 0);
++                      error = revalidate_it(dentry, &it);
++                      if (error)
++                              goto out;
++                      error = init_private_file_it(&file, dentry, FMODE_READ, &it);
++              }
++      } else {
++              error = init_private_file_it(&file, dentry, FMODE_READ, NULL);
++      }
+       if (error)
+               goto out;
++
+       error = -EINVAL;
+       if (!file.f_op->readdir)
+               goto out_close;
+@@ -113,9 +150,12 @@
+       }
+ out_close:
+-      if (file.f_op->release)
++      if (file.f_op->release && !filp)
+               file.f_op->release(dir, &file);
+ out:
++      if (dentry->d_op && dentry->d_op->d_revalidate_it &&
++          it.it_op_release && !filp)
++              intent_release(&it);
+       return error;
+ }
+@@ -274,7 +314,7 @@
+        * it is well connected.  But nobody returns different dentrys do they?
+        */
+       down(&child->d_inode->i_sem);
+-      pdentry = child->d_inode->i_op->lookup(child->d_inode, tdentry);
++      pdentry = lookup_it(child->d_inode, tdentry);
+       up(&child->d_inode->i_sem);
+       d_drop(tdentry); /* we never want ".." hashed */
+       if (!pdentry && tdentry->d_inode == NULL) {
+@@ -307,6 +347,8 @@
+                               pdentry->d_flags |= DCACHE_NFSD_DISCONNECTED;
+                               pdentry->d_op = child->d_op;
+                       }
++                      if (child->d_op && child->d_op->d_revalidate_it)
++                              pdentry->d_op = child->d_op;
+               }
+               if (pdentry == NULL)
+                       pdentry = ERR_PTR(-ENOMEM);
+@@ -464,6 +506,8 @@
+               struct dentry *pdentry;
+               struct inode *parent;
++              if (result->d_op && result->d_op->d_revalidate_it)
++                      dentry->d_op = result->d_op;
+               pdentry = nfsd_findparent(dentry);
+               err = PTR_ERR(pdentry);
+               if (IS_ERR(pdentry))
+@@ -670,6 +714,10 @@
+       inode = dentry->d_inode;
++      /* cache coherency for non-device filesystems */
++      if (inode->i_op && inode->i_op->revalidate_it)
++              inode->i_op->revalidate_it(dentry, NULL);
++
+       /* Type check. The correct error return for type mismatches
+        * does not seem to be generally agreed upon. SunOS seems to
+        * use EISDIR if file isn't S_IFREG; a comment in the NFSv3
+@@ -903,8 +951,9 @@
+               dentry->d_parent->d_name.name, dentry->d_name.name);
+       goto out;
+ out_uptodate:
+-      printk(KERN_ERR "fh_update: %s/%s already up-to-date!\n",
+-              dentry->d_parent->d_name.name, dentry->d_name.name);
++      if (!dentry->d_parent->d_inode->i_op->mkdir_raw)
++              printk(KERN_ERR "fh_update: %s/%s already up-to-date!\n",
++                    dentry->d_parent->d_name.name, dentry->d_name.name);
+       goto out;
+ }
+Index: linux-2.4.29/fs/nfsd/vfs.c
+===================================================================
+--- linux-2.4.29.orig/fs/nfsd/vfs.c    2005-05-03 16:28:21.000000000 +0300
++++ linux-2.4.29/fs/nfsd/vfs.c 2005-05-03 18:46:09.372133224 +0300
+@@ -77,6 +77,126 @@
+ static struct raparms *               raparml;
+ static struct raparms *               raparm_cache;
++static int link_raw(struct dentry *dold, struct dentry *ddir,
++                  struct dentry *dnew)
++{
++      int err;
++
++      struct nameidata old_nd = { .dentry = dold };
++      struct nameidata nd = { .dentry = ddir, .last = dnew->d_name };
++      struct inode_operations *op = nd.dentry->d_inode->i_op;
++      err = op->link_raw(&old_nd, &nd);
++      d_instantiate(dnew, dold->d_inode);
++      if (dold->d_inode->i_op && dold->d_inode->i_op->revalidate_it)
++              dold->d_inode->i_op->revalidate_it(dnew, NULL);
++
++      return err;
++}
++
++static int unlink_raw(struct dentry *dentry, char *fname, int flen,
++                    struct dentry *rdentry)
++{
++      int err;
++      struct qstr last = { .name = fname, .len = flen };
++      struct nameidata nd = { .dentry = dentry, .last = last };
++      struct inode_operations *op = nd.dentry->d_inode->i_op;
++      err = op->unlink_raw(&nd);
++      if (!err)
++              d_delete(rdentry);
++
++      return err;
++}
++
++static int rmdir_raw(struct dentry *dentry, char *fname, int flen,
++                   struct dentry *rdentry)
++{
++      int err;
++      struct qstr last = { .name = fname, .len = flen };
++      struct nameidata nd = { .dentry = dentry, .last = last };
++      struct inode_operations *op = nd.dentry->d_inode->i_op;
++      err = op->rmdir_raw(&nd);
++      if (!err) {
++              rdentry->d_inode->i_flags |= S_DEAD;
++              d_delete(rdentry);
++      }
++
++      return err;
++}
++
++static int symlink_raw(struct dentry *dentry, char *fname, int flen,
++                     char *path)
++{
++      int err;
++      struct qstr last = { .name = fname, .len = flen };
++      struct nameidata nd = { .dentry = dentry, .last = last };
++      struct inode_operations *op = nd.dentry->d_inode->i_op;
++      err = op->symlink_raw(&nd, path);
++
++      return err;
++}
++
++static int mkdir_raw(struct dentry *dentry, char *fname, int flen, int mode)
++{
++      int err;
++      struct qstr last = { .name = fname, .len = flen };
++      struct nameidata nd = { .dentry = dentry, .last = last };
++      struct inode_operations *op = nd.dentry->d_inode->i_op;
++      err = op->mkdir_raw(&nd, mode);
++
++      return err;
++}
++
++static int mknod_raw(struct dentry *dentry, char *fname, int flen, int mode,
++                   dev_t dev)
++{
++      int err;
++      struct qstr last = { .name = fname, .len = flen };
++      struct nameidata nd = { .dentry = dentry, .last = last };
++      struct inode_operations *op = nd.dentry->d_inode->i_op;
++      err = op->mknod_raw(&nd, mode, dev);
++
++      return err;
++}
++
++static int rename_raw(struct dentry *fdentry, struct dentry *tdentry,
++                    struct dentry *odentry, struct dentry *ndentry)
++{
++      int err;
++
++      struct nameidata old_nd = { .dentry = fdentry, .last = odentry->d_name};
++      struct nameidata new_nd = { .dentry = tdentry, .last = ndentry->d_name};
++      struct inode_operations *op = old_nd.dentry->d_inode->i_op;
++      err = op->rename_raw(&old_nd, &new_nd);
++      d_move(odentry, ndentry);
++
++      return err;
++}
++
++static int setattr_raw(struct inode *inode, struct iattr *iap)
++{
++      int err;
++
++      iap->ia_valid |= ATTR_RAW;
++      err = inode->i_op->setattr_raw(inode, iap);
++
++      return err;
++}
++
++int revalidate_it(struct dentry *dentry, struct lookup_intent *it)
++{
++      int err = 0;
++
++      if (dentry && dentry->d_op && dentry->d_op->d_revalidate_it) {
++              if (!dentry->d_op->d_revalidate_it(dentry, 0, it) &&
++                      !d_invalidate(dentry)) {
++                      err = -EINVAL;
++                      return err;
++              }
++      }
++
++      return err;
++}
++
+ /*
+  * Look up one component of a pathname.
+  * N.B. After this call _both_ fhp and resfh need an fh_put
+@@ -302,7 +422,10 @@
+       }
+       err = nfserr_notsync;
+       if (!check_guard || guardtime == inode->i_ctime) {
+-              err = notify_change(dentry, iap);
++              if (dentry->d_inode->i_op && dentry->d_inode->i_op->setattr_raw)
++                      err = setattr_raw(dentry->d_inode, iap);
++              else
++                      err = notify_change(dentry, iap);
+               err = nfserrno(err);
+       }
+       if (size_change) {
+@@ -429,6 +552,7 @@
+ {
+       struct dentry   *dentry;
+       struct inode    *inode;
++      struct lookup_intent it;
+       int             err;
+       /* If we get here, then the client has already done an "open", and (hopefully)
+@@ -475,6 +599,18 @@
+               filp->f_mode  = FMODE_READ;
+       }
++#ifndef O_OWNER_OVERRIDE
++#define O_OWNER_OVERRIDE 0200000000
++#endif
++      intent_init(&it, IT_OPEN, (filp->f_flags & ~O_ACCMODE) | filp->f_mode |
++                  O_OWNER_OVERRIDE);
++
++      err = revalidate_it(dentry, &it);
++      if (err)
++              goto out_nfserr;
++
++      filp->f_it = &it;
++
+       err = 0;
+       if (filp->f_op && filp->f_op->open) {
+               err = filp->f_op->open(inode, filp);
+@@ -490,6 +626,9 @@
+               }
+       }
+ out_nfserr:
++      if (it.it_op_release)
++              intent_release(&it);
++
+       if (err)
+               err = nfserrno(err);
+ out:
+@@ -837,7 +976,7 @@
+ {
+       struct dentry   *dentry, *dchild;
+       struct inode    *dirp;
+-      int             err;
++      int             err, error = -EOPNOTSUPP;
+       err = nfserr_perm;
+       if (!flen)
+@@ -853,20 +992,47 @@
+       dentry = fhp->fh_dentry;
+       dirp = dentry->d_inode;
++      switch (type) {
++      case S_IFDIR:
++              if (dirp->i_op->mkdir_raw)
++                      error = mkdir_raw(dentry, fname, flen, iap->ia_mode);
++              break;
++      case S_IFCHR:
++      case S_IFBLK:
++      case S_IFIFO:
++      case S_IFSOCK:
++      case S_IFREG:
++              if (dirp->i_op->mknod_raw) {
++                      if (type == S_IFREG)
++                              rdev = 0;
++                      error = mknod_raw(dentry, fname,flen,iap->ia_mode,rdev);
++              }
++              break;
++      default:
++              printk("nfsd: bad file type %o in nfsd_create\n", type);
++      }
++      if (error && error != -EOPNOTSUPP) {
++              err = error;
++              goto out_nfserr;
++      }
++
+       err = nfserr_notdir;
+-      if(!dirp->i_op || !dirp->i_op->lookup)
++      if (!dirp->i_op || !(dirp->i_op->lookup || dirp->i_op->lookup_it))
+               goto out;
+       /*
+        * Check whether the response file handle has been verified yet.
+        * If it has, the parent directory should already be locked.
+        */
+-      if (!resfhp->fh_dentry) {
+-              /* called from nfsd_proc_mkdir, or possibly nfsd3_proc_create */
+-              fh_lock(fhp);
++      if (!resfhp->fh_dentry || dirp->i_op->lookup_it) {
++              /* called from nfsd_proc_mkdir, or possibly nfsd3_proc_create
++               * and nfsd_proc_create in case of lustre */
++              if (!resfhp->fh_dentry)
++                      fh_lock(fhp);
+               dchild = lookup_one_len(fname, dentry, flen);
+               err = PTR_ERR(dchild);
+               if (IS_ERR(dchild))
+                       goto out_nfserr;
++              resfhp->fh_dentry = NULL;
+               err = fh_compose(resfhp, fhp->fh_export, dchild, fhp);
+               if (err)
+                       goto out;
+@@ -887,10 +1053,12 @@
+        * Make sure the child dentry is still negative ...
+        */
+       err = nfserr_exist;
+-      if (dchild->d_inode) {
+-              dprintk("nfsd_create: dentry %s/%s not negative!\n",
+-                      dentry->d_name.name, dchild->d_name.name);
+-              goto out; 
++      if (error == -EOPNOTSUPP) {
++              if (dchild->d_inode) {
++                      dprintk("nfsd_create: dentry %s/%s not negative!\n",
++                              dentry->d_name.name, dchild->d_name.name);
++                      goto out;
++              }
+       }
+       if (!(iap->ia_valid & ATTR_MODE))
+@@ -903,16 +1071,19 @@
+       err = nfserr_perm;
+       switch (type) {
+       case S_IFREG:
+-              err = vfs_create(dirp, dchild, iap->ia_mode);
++              if (error == -EOPNOTSUPP)
++                      err = vfs_create(dirp, dchild, iap->ia_mode);
+               break;
+       case S_IFDIR:
+-              err = vfs_mkdir(dirp, dchild, iap->ia_mode);
++              if (error == -EOPNOTSUPP)
++                      err = vfs_mkdir(dirp, dchild, iap->ia_mode);
+               break;
+       case S_IFCHR:
+       case S_IFBLK:
+       case S_IFIFO:
+       case S_IFSOCK:
+-              err = vfs_mknod(dirp, dchild, iap->ia_mode, rdev);
++              if (error == -EOPNOTSUPP)
++                      err = vfs_mknod(dirp, dchild, iap->ia_mode, rdev);
+               break;
+       default:
+               printk("nfsd: bad file type %o in nfsd_create\n", type);
+@@ -981,7 +1152,13 @@
+       /* Get all the sanity checks out of the way before
+        * we lock the parent. */
+       err = nfserr_notdir;
+-      if(!dirp->i_op || !dirp->i_op->lookup)
++      if (dirp->i_op->mknod_raw) {
++              err = mknod_raw(dentry, fname, flen, iap->ia_mode, 0);
++              if (err && err != -EOPNOTSUPP)
++                      goto out_nfserr;
++      }
++
++      if (!dirp->i_op || !(dirp->i_op->lookup || dirp->i_op->lookup_it))
+               goto out;
+       fh_lock(fhp);
+@@ -1032,6 +1209,8 @@
+               case NFS3_CREATE_GUARDED:
+                       err = nfserr_exist;
+               }
++              if (dirp->i_op->mknod_raw)
++                      err = 0;
+               goto out;
+       }
+@@ -1138,7 +1317,7 @@
+                               struct iattr *iap)
+ {
+       struct dentry   *dentry, *dnew;
+-      int             err, cerr;
++      int             err, cerr, error = -EOPNOTSUPP;
+       err = nfserr_noent;
+       if (!flen || !plen)
+@@ -1152,12 +1331,18 @@
+               goto out;
+       fh_lock(fhp);
+       dentry = fhp->fh_dentry;
++
++      if (dentry->d_inode->i_op->symlink_raw)
++              error = symlink_raw(dentry, fname, flen, path);
++
+       dnew = lookup_one_len(fname, dentry, flen);
+       err = PTR_ERR(dnew);
+       if (IS_ERR(dnew))
+               goto out_nfserr;
+-      err = vfs_symlink(dentry->d_inode, dnew, path);
++      err = error;
++      if (err == -EOPNOTSUPP || !dentry->d_inode->i_op->symlink_raw)
++              err = vfs_symlink(dentry->d_inode, dnew, path);
+       if (!err) {
+               if (EX_ISSYNC(fhp->fh_export))
+                       nfsd_sync_dir(dentry);
+@@ -1167,7 +1352,10 @@
+                               iap->ia_valid |= ATTR_CTIME;
+                               iap->ia_mode = (iap->ia_mode&S_IALLUGO)
+                                       | S_IFLNK;
+-                              err = notify_change(dnew, iap);
++                              if (dnew->d_inode->i_op && dnew->d_inode->i_op->setattr_raw)
++                                      err = setattr_raw(dnew->d_inode, iap);
++                              else
++                                      err = notify_change(dnew, iap);
+                               if (err)
+                                       err = nfserrno(err);
+                               else if (EX_ISSYNC(fhp->fh_export))
+@@ -1227,7 +1415,10 @@
+       dold = tfhp->fh_dentry;
+       dest = dold->d_inode;
+-      err = vfs_link(dold, dirp, dnew);
++      if (dirp->i_op->link_raw)
++              err = link_raw(dold, ddir, dnew);
++      else
++              err = vfs_link(dold, dirp, dnew);
+       if (!err) {
+               if (EX_ISSYNC(ffhp->fh_export)) {
+                       nfsd_sync_dir(ddir);
+@@ -1312,7 +1503,10 @@
+                       err = nfserr_perm;
+       } else
+ #endif
+-      err = vfs_rename(fdir, odentry, tdir, ndentry);
++      if (fdir->i_op->rename_raw)
++              err = rename_raw(fdentry, tdentry, odentry, ndentry);
++      else
++              err = vfs_rename(fdir, odentry, tdir, ndentry);
+       if (!err && EX_ISSYNC(tfhp->fh_export)) {
+               nfsd_sync_dir(tdentry);
+               nfsd_sync_dir(fdentry);
+@@ -1333,7 +1527,7 @@
+       fill_post_wcc(tfhp);
+       double_up(&tdir->i_sem, &fdir->i_sem);
+       ffhp->fh_locked = tfhp->fh_locked = 0;
+-      
++
+ out:
+       return err;
+ }
+@@ -1379,9 +1573,15 @@
+                       err = nfserr_perm;
+               } else
+ #endif
+-              err = vfs_unlink(dirp, rdentry);
++              if (dirp->i_op->unlink_raw)
++                      err = unlink_raw(dentry, fname, flen, rdentry);
++              else
++                      err = vfs_unlink(dirp, rdentry);
+       } else { /* It's RMDIR */
+-              err = vfs_rmdir(dirp, rdentry);
++              if (dirp->i_op->rmdir_raw)
++                      err = rmdir_raw(dentry, fname, flen, rdentry);
++              else
++                      err = vfs_rmdir(dirp, rdentry);
+       }
+       dput(rdentry);
+Index: linux-2.4.29/include/linux/fs.h
+===================================================================
+--- linux-2.4.29.orig/include/linux/fs.h       2005-05-03 18:16:44.000000000 +0300
++++ linux-2.4.29/include/linux/fs.h    2005-05-03 18:52:56.016313912 +0300
+@@ -93,6 +93,8 @@
+ #define FS_SINGLE     8 /* Filesystem that can have only one superblock */
+ #define FS_NOMOUNT    16 /* Never mount from userland */
+ #define FS_LITTER     32 /* Keeps the tree in dcache */
++#define FS_NFSEXP_FSID        64 /* Use file system specific fsid for
++                          * exporting non device filesystems. */
+ #define FS_ODD_RENAME 32768   /* Temporary stuff; will go away as soon
+                                 * as nfs_rename() will be cleaned up
+                                 */
+@@ -1124,6 +1126,9 @@
+                        struct nameidata *nd, struct lookup_intent *it);
+ extern struct file *dentry_open_it(struct dentry *dentry, struct vfsmount *mnt,
+                           int flags, struct lookup_intent *it);
++extern int revalidate_it(struct dentry *dentry, struct lookup_intent *it);
++extern int init_private_file_it(struct file *, struct dentry *dentry, int mode,
++                              struct lookup_intent *it);
+ extern int filp_close(struct file *, fl_owner_t id);
+ extern char * getname(const char *);
+@@ -1423,6 +1428,8 @@
+ extern int follow_down(struct vfsmount **, struct dentry **);
+ extern int follow_up(struct vfsmount **, struct dentry **);
+ extern struct dentry * lookup_one_len(const char *, struct dentry *, int);
++extern struct dentry * lookup_one_len_it(const char *, struct dentry *, int,
++                                       struct lookup_intent *);
+ extern struct dentry * lookup_hash(struct qstr *, struct dentry *);
+ #define user_path_walk(name,nd)        __user_walk(name, LOOKUP_FOLLOW|LOOKUP_POSITIVE, nd)
+ #define user_path_walk_link(name,nd) __user_walk(name, LOOKUP_POSITIVE, nd)
+@@ -1443,6 +1450,8 @@
+ extern struct inode * iget4_locked(struct super_block *, unsigned long,
+                                  find_inode_t, void *);
++ extern struct inode * ilookup4(struct super_block *, unsigned long,
++                              find_inode_t, void *);
+ static inline struct inode *iget4(struct super_block *sb, unsigned long ino,
+                                 find_inode_t find_actor, void *opaque)
+Index: linux-2.4.29/kernel/ksyms.c
+===================================================================
+--- linux-2.4.29.orig/kernel/ksyms.c   2005-05-03 18:16:44.000000000 +0300
++++ linux-2.4.29/kernel/ksyms.c        2005-05-03 18:52:05.377012256 +0300
+@@ -150,6 +150,7 @@
+ EXPORT_SYMBOL(iunique);
+ EXPORT_SYMBOL(ilookup);
+ EXPORT_SYMBOL(iget4_locked);
++EXPORT_SYMBOL(ilookup4);
+ EXPORT_SYMBOL(unlock_new_inode);
+ EXPORT_SYMBOL(iput);
+ EXPORT_SYMBOL(inode_init_once);
+@@ -164,6 +165,7 @@
+ EXPORT_SYMBOL(path_release);
+ EXPORT_SYMBOL(__user_walk);
+ EXPORT_SYMBOL(lookup_one_len);
++EXPORT_SYMBOL(lookup_one_len_it);
+ EXPORT_SYMBOL(lookup_hash);
+ EXPORT_SYMBOL(sys_close);
+ EXPORT_SYMBOL(dcache_lock);
diff --git a/lustre/kernel_patches/patches/nfs_export_kernel-2.4.29.patch-1 b/lustre/kernel_patches/patches/nfs_export_kernel-2.4.29.patch-1
new file mode 100644 (file)
index 0000000..7bc7e44
--- /dev/null
@@ -0,0 +1,730 @@
+Index: linux-2.4.29/fs/Makefile
+===================================================================
+--- linux-2.4.29.orig/fs/Makefile      2005-04-07 19:31:00.000000000 +0300
++++ linux-2.4.29/fs/Makefile   2005-05-03 15:59:07.943621928 +0300
+@@ -7,7 +7,8 @@
+ O_TARGET := fs.o
+-export-objs :=        filesystems.o open.o dcache.o buffer.o dquot.o inode.o
++export-objs :=        filesystems.o open.o dcache.o buffer.o dquot.o inode.o \
++              namei.o file_table.o
+ mod-subdirs :=        nls
+ obj-y :=      open.o read_write.o devices.o file_table.o buffer.o \
+Index: linux-2.4.29/fs/file_table.c
+===================================================================
+--- linux-2.4.29.orig/fs/file_table.c  2005-04-07 18:52:26.000000000 +0300
++++ linux-2.4.29/fs/file_table.c       2005-05-03 15:59:07.945621624 +0300
+@@ -82,7 +82,8 @@
+  * and call the open function (if any).  The caller must verify that
+  * inode->i_fop is not NULL.
+  */
+-int init_private_file(struct file *filp, struct dentry *dentry, int mode)
++int init_private_file_it(struct file *filp, struct dentry *dentry, int mode,
++                       struct lookup_intent *it)
+ {
+       memset(filp, 0, sizeof(*filp));
+       filp->f_mode   = mode;
+@@ -90,12 +91,20 @@
+       filp->f_dentry = dentry;
+       filp->f_uid    = current->fsuid;
+       filp->f_gid    = current->fsgid;
++      if (it)
++              filp->f_it = it;
+       filp->f_op     = dentry->d_inode->i_fop;
+       if (filp->f_op->open)
+               return filp->f_op->open(dentry->d_inode, filp);
+       else
+               return 0;
+ }
++EXPORT_SYMBOL(init_private_file_it);
++
++int init_private_file(struct file *filp, struct dentry *dentry, int mode)
++{
++      return init_private_file_it(filp, dentry, mode, NULL);
++}
+ void fastcall fput(struct file * file)
+ {
+Index: linux-2.4.29/fs/inode.c
+===================================================================
+--- linux-2.4.29.orig/fs/inode.c       2005-04-07 19:18:51.000000000 +0300
++++ linux-2.4.29/fs/inode.c    2005-05-03 16:02:40.198354304 +0300
+@@ -1154,6 +1154,24 @@
+       }
+       spin_unlock(&inode_lock);
++      return NULL;
++}
++
++struct inode *ilookup4(struct super_block *sb, unsigned long ino,
++                     find_inode_t find_actor, void *opaque)
++{
++      struct list_head * head = inode_hashtable + hash(sb,ino);
++      return ifind(sb, ino, head, find_actor, opaque);
++}
++
++static inline struct inode *ifind(struct super_block *sb, unsigned long ino,
++                                struct list_head *head,
++                                find_inode_t find_actor, void *opaque)
++{
++      struct inode *inode = ifind(sb, ino, head, find_actor, opaque);
++      if (inode)
++              return inode;
++
+       /*
+        * get_new_inode() will do the right thing, re-trying the search
+        * in case it had to block at any point.
+Index: linux-2.4.29/fs/namei.c
+===================================================================
+--- linux-2.4.29.orig/fs/namei.c       2005-04-07 19:14:06.000000000 +0300
++++ linux-2.4.29/fs/namei.c    2005-05-03 15:59:07.953620408 +0300
+@@ -22,6 +22,7 @@
+ #include <linux/dnotify.h>
+ #include <linux/smp_lock.h>
+ #include <linux/personality.h>
++#include <linux/module.h>
+ #include <asm/namei.h>
+ #include <asm/uaccess.h>
+@@ -100,6 +101,7 @@
+               it->it_op_release(it);
+ }
++EXPORT_SYMBOL(intent_release);
+ /* In order to reduce some races, while at the same time doing additional
+  * checking and hopefully speeding things up, we copy filenames to the
+@@ -910,7 +912,8 @@
+ /* SMP-safe */
+-struct dentry * lookup_one_len(const char * name, struct dentry * base, int len)
++struct dentry * lookup_one_len_it(const char * name, struct dentry * base,
++                                int len, struct lookup_intent *it)
+ {
+       unsigned long hash;
+       struct qstr this;
+@@ -930,11 +933,16 @@
+       }
+       this.hash = end_name_hash(hash);
+-      return lookup_hash_it(&this, base, NULL);
++      return lookup_hash_it(&this, base, it);
+ access:
+       return ERR_PTR(-EACCES);
+ }
++struct dentry * lookup_one_len(const char * name, struct dentry * base, int len)
++{
++      return lookup_one_len_it(name, base, len, NULL);
++}
++
+ /*
+  *    namei()
+  *
+Index: linux-2.4.29/fs/nfsd/export.c
+===================================================================
+--- linux-2.4.29.orig/fs/nfsd/export.c 2005-04-07 18:53:59.000000000 +0300
++++ linux-2.4.29/fs/nfsd/export.c      2005-05-03 15:59:07.955620104 +0300
+@@ -223,6 +223,11 @@
+       inode = nd.dentry->d_inode;
+       dev = inode->i_dev;
+       ino = inode->i_ino;
++      if ((inode->i_sb->s_type->fs_flags & FS_NFSEXP_FSID) &&
++          !(nxp->ex_flags & NFSEXP_FSID)) {
++              nxp->ex_dev = inode->i_sb->s_dev;
++              nxp->ex_flags |= NFSEXP_FSID;
++      }
+       err = -EINVAL;
+       exp = exp_get(clp, dev, ino);
+Index: linux-2.4.29/fs/nfsd/nfsfh.c
+===================================================================
+--- linux-2.4.29.orig/fs/nfsd/nfsfh.c  2005-04-07 18:53:14.000000000 +0300
++++ linux-2.4.29/fs/nfsd/nfsfh.c       2005-05-03 15:59:07.958619648 +0300
+@@ -36,6 +36,13 @@
+       int sequence;           /* sequence counter */
+ };
++static struct dentry *lookup_it(struct inode *inode, struct dentry * dentry)
++{
++      if (inode->i_op->lookup_it)
++              return inode->i_op->lookup_it(inode, dentry, NULL, 0);
++      return inode->i_op->lookup(inode, dentry);
++}
++
+ /*
+  * A rather strange filldir function to capture
+  * the name matching the specified inode number.
+@@ -75,6 +82,8 @@
+       int error;
+       struct file file;
+       struct nfsd_getdents_callback buffer;
++      struct lookup_intent it;
++      struct file *filp = NULL;
+       error = -ENOTDIR;
+       if (!dir || !S_ISDIR(dir->i_mode))
+@@ -85,9 +94,37 @@
+       /*
+        * Open the directory ...
+        */
+-      error = init_private_file(&file, dentry, FMODE_READ);
++      if (dentry->d_op && dentry->d_op->d_revalidate_it) {
++              if ((dentry->d_flags & DCACHE_NFSD_DISCONNECTED) &&
++                  (dentry->d_parent == dentry) ) {
++                      it.it_op_release = NULL;
++                      /*
++                       * XXX Temporary Hack: Simulate init_private_file without
++                       * f_op->open for disconnected dentry as we don't have
++                       * actual dentry->d_name to revalidate in revalidate_it()
++                       */
++                      filp = &file;
++                      memset(filp, 0, sizeof(*filp));
++                      filp->f_mode   = FMODE_READ;
++                      atomic_set(&filp->f_count, 1);
++                      filp->f_dentry = dentry;
++                      filp->f_uid = current->fsuid;
++                      filp->f_gid = current->fsgid;
++                      filp->f_op = dentry->d_inode->i_fop;
++                      error = 0;
++              } else {
++                      intent_init(&it, IT_OPEN, 0);
++                      error = revalidate_it(dentry, &it);
++                      if (error)
++                              goto out;
++                      error = init_private_file_it(&file, dentry, FMODE_READ, &it);
++              }
++      } else {
++              error = init_private_file_it(&file, dentry, FMODE_READ, NULL);
++      }
+       if (error)
+               goto out;
++
+       error = -EINVAL;
+       if (!file.f_op->readdir)
+               goto out_close;
+@@ -113,9 +150,12 @@
+       }
+ out_close:
+-      if (file.f_op->release)
++      if (file.f_op->release && !filp)
+               file.f_op->release(dir, &file);
+ out:
++      if (dentry->d_op && dentry->d_op->d_revalidate_it &&
++          it.it_op_release && !filp)
++              intent_release(&it);
+       return error;
+ }
+@@ -274,7 +314,7 @@
+        * it is well connected.  But nobody returns different dentrys do they?
+        */
+       down(&child->d_inode->i_sem);
+-      pdentry = child->d_inode->i_op->lookup(child->d_inode, tdentry);
++      pdentry = lookup_it(child->d_inode, tdentry);
+       up(&child->d_inode->i_sem);
+       d_drop(tdentry); /* we never want ".." hashed */
+       if (!pdentry && tdentry->d_inode == NULL) {
+@@ -307,6 +347,8 @@
+                               pdentry->d_flags |= DCACHE_NFSD_DISCONNECTED;
+                               pdentry->d_op = child->d_op;
+                       }
++                      if (child->d_op && child->d_op->d_revalidate_it)
++                              pdentry->d_op = child->d_op;
+               }
+               if (pdentry == NULL)
+                       pdentry = ERR_PTR(-ENOMEM);
+@@ -464,6 +506,8 @@
+               struct dentry *pdentry;
+               struct inode *parent;
++              if (result->d_op && result->d_op->d_revalidate_it)
++                      dentry->d_op = result->d_op;
+               pdentry = nfsd_findparent(dentry);
+               err = PTR_ERR(pdentry);
+               if (IS_ERR(pdentry))
+@@ -670,6 +714,10 @@
+       inode = dentry->d_inode;
++      /* cache coherency for non-device filesystems */
++      if (inode->i_op && inode->i_op->revalidate_it)
++              inode->i_op->revalidate_it(dentry, NULL);
++
+       /* Type check. The correct error return for type mismatches
+        * does not seem to be generally agreed upon. SunOS seems to
+        * use EISDIR if file isn't S_IFREG; a comment in the NFSv3
+@@ -903,8 +951,9 @@
+               dentry->d_parent->d_name.name, dentry->d_name.name);
+       goto out;
+ out_uptodate:
+-      printk(KERN_ERR "fh_update: %s/%s already up-to-date!\n",
+-              dentry->d_parent->d_name.name, dentry->d_name.name);
++      if (!dentry->d_parent->d_inode->i_op->mkdir_raw)
++              printk(KERN_ERR "fh_update: %s/%s already up-to-date!\n",
++                    dentry->d_parent->d_name.name, dentry->d_name.name);
+       goto out;
+ }
+Index: linux-2.4.29/fs/nfsd/vfs.c
+===================================================================
+--- linux-2.4.29.orig/fs/nfsd/vfs.c    2005-04-07 18:53:19.000000000 +0300
++++ linux-2.4.29/fs/nfsd/vfs.c 2005-05-03 15:59:07.965618584 +0300
+@@ -77,6 +77,126 @@
+ static struct raparms *               raparml;
+ static struct raparms *               raparm_cache;
++static int link_raw(struct dentry *dold, struct dentry *ddir,
++                  struct dentry *dnew)
++{
++      int err;
++
++      struct nameidata old_nd = { .dentry = dold };
++      struct nameidata nd = { .dentry = ddir, .last = dnew->d_name };
++      struct inode_operations *op = nd.dentry->d_inode->i_op;
++      err = op->link_raw(&old_nd, &nd);
++      d_instantiate(dnew, dold->d_inode);
++      if (dold->d_inode->i_op && dold->d_inode->i_op->revalidate_it)
++              dold->d_inode->i_op->revalidate_it(dnew, NULL);
++
++      return err;
++}
++
++static int unlink_raw(struct dentry *dentry, char *fname, int flen,
++                    struct dentry *rdentry)
++{
++      int err;
++      struct qstr last = { .name = fname, .len = flen };
++      struct nameidata nd = { .dentry = dentry, .last = last };
++      struct inode_operations *op = nd.dentry->d_inode->i_op;
++      err = op->unlink_raw(&nd);
++      if (!err)
++              d_delete(rdentry);
++
++      return err;
++}
++
++static int rmdir_raw(struct dentry *dentry, char *fname, int flen,
++                   struct dentry *rdentry)
++{
++      int err;
++      struct qstr last = { .name = fname, .len = flen };
++      struct nameidata nd = { .dentry = dentry, .last = last };
++      struct inode_operations *op = nd.dentry->d_inode->i_op;
++      err = op->rmdir_raw(&nd);
++      if (!err) {
++              rdentry->d_inode->i_flags |= S_DEAD;
++              d_delete(rdentry);
++      }
++
++      return err;
++}
++
++static int symlink_raw(struct dentry *dentry, char *fname, int flen,
++                     char *path)
++{
++      int err;
++      struct qstr last = { .name = fname, .len = flen };
++      struct nameidata nd = { .dentry = dentry, .last = last };
++      struct inode_operations *op = nd.dentry->d_inode->i_op;
++      err = op->symlink_raw(&nd, path);
++
++      return err;
++}
++
++static int mkdir_raw(struct dentry *dentry, char *fname, int flen, int mode)
++{
++      int err;
++      struct qstr last = { .name = fname, .len = flen };
++      struct nameidata nd = { .dentry = dentry, .last = last };
++      struct inode_operations *op = nd.dentry->d_inode->i_op;
++      err = op->mkdir_raw(&nd, mode);
++
++      return err;
++}
++
++static int mknod_raw(struct dentry *dentry, char *fname, int flen, int mode,
++                   dev_t dev)
++{
++      int err;
++      struct qstr last = { .name = fname, .len = flen };
++      struct nameidata nd = { .dentry = dentry, .last = last };
++      struct inode_operations *op = nd.dentry->d_inode->i_op;
++      err = op->mknod_raw(&nd, mode, dev);
++
++      return err;
++}
++
++static int rename_raw(struct dentry *fdentry, struct dentry *tdentry,
++                    struct dentry *odentry, struct dentry *ndentry)
++{
++      int err;
++
++      struct nameidata old_nd = { .dentry = fdentry, .last = odentry->d_name};
++      struct nameidata new_nd = { .dentry = tdentry, .last = ndentry->d_name};
++      struct inode_operations *op = old_nd.dentry->d_inode->i_op;
++      err = op->rename_raw(&old_nd, &new_nd);
++      d_move(odentry, ndentry);
++
++      return err;
++}
++
++static int setattr_raw(struct inode *inode, struct iattr *iap)
++{
++      int err;
++
++      iap->ia_valid |= ATTR_RAW;
++      err = inode->i_op->setattr_raw(inode, iap);
++
++      return err;
++}
++
++int revalidate_it(struct dentry *dentry, struct lookup_intent *it)
++{
++      int err = 0;
++
++      if (dentry && dentry->d_op && dentry->d_op->d_revalidate_it) {
++              if (!dentry->d_op->d_revalidate_it(dentry, 0, it) &&
++                      !d_invalidate(dentry)) {
++                      err = -EINVAL;
++                      return err;
++              }
++      }
++
++      return err;
++}
++
+ /*
+  * Look up one component of a pathname.
+  * N.B. After this call _both_ fhp and resfh need an fh_put
+@@ -302,7 +422,10 @@
+       }
+       err = nfserr_notsync;
+       if (!check_guard || guardtime == inode->i_ctime) {
+-              err = notify_change(dentry, iap);
++              if (dentry->d_inode->i_op && dentry->d_inode->i_op->setattr_raw)
++                      err = setattr_raw(dentry->d_inode, iap);
++              else
++                      err = notify_change(dentry, iap);
+               err = nfserrno(err);
+       }
+       if (size_change) {
+@@ -429,6 +552,7 @@
+ {
+       struct dentry   *dentry;
+       struct inode    *inode;
++      struct lookup_intent it;
+       int             err;
+       /* If we get here, then the client has already done an "open", and (hopefully)
+@@ -475,6 +599,18 @@
+               filp->f_mode  = FMODE_READ;
+       }
++#ifndef O_OWNER_OVERRIDE
++#define O_OWNER_OVERRIDE 0200000000
++#endif
++      intent_init(&it, IT_OPEN, (filp->f_flags & ~O_ACCMODE) | filp->f_mode |
++                  O_OWNER_OVERRIDE);
++
++      err = revalidate_it(dentry, &it);
++      if (err)
++              goto out_nfserr;
++
++      filp->f_it = &it;
++
+       err = 0;
+       if (filp->f_op && filp->f_op->open) {
+               err = filp->f_op->open(inode, filp);
+@@ -490,6 +626,9 @@
+               }
+       }
+ out_nfserr:
++      if (it.it_op_release)
++              intent_release(&it);
++
+       if (err)
+               err = nfserrno(err);
+ out:
+@@ -837,7 +976,7 @@
+ {
+       struct dentry   *dentry, *dchild;
+       struct inode    *dirp;
+-      int             err;
++      int             err, error = -EOPNOTSUPP;
+       err = nfserr_perm;
+       if (!flen)
+@@ -853,20 +992,47 @@
+       dentry = fhp->fh_dentry;
+       dirp = dentry->d_inode;
++      switch (type) {
++      case S_IFDIR:
++              if (dirp->i_op->mkdir_raw)
++                      error = mkdir_raw(dentry, fname, flen, iap->ia_mode);
++              break;
++      case S_IFCHR:
++      case S_IFBLK:
++      case S_IFIFO:
++      case S_IFSOCK:
++      case S_IFREG:
++              if (dirp->i_op->mknod_raw) {
++                      if (type == S_IFREG)
++                              rdev = 0;
++                      error = mknod_raw(dentry, fname,flen,iap->ia_mode,rdev);
++              }
++              break;
++      default:
++              printk("nfsd: bad file type %o in nfsd_create\n", type);
++      }
++      if (error && error != -EOPNOTSUPP) {
++              err = error;
++              goto out_nfserr;
++      }
++
+       err = nfserr_notdir;
+-      if(!dirp->i_op || !dirp->i_op->lookup)
++      if (!dirp->i_op || !(dirp->i_op->lookup || dirp->i_op->lookup_it))
+               goto out;
+       /*
+        * Check whether the response file handle has been verified yet.
+        * If it has, the parent directory should already be locked.
+        */
+-      if (!resfhp->fh_dentry) {
+-              /* called from nfsd_proc_mkdir, or possibly nfsd3_proc_create */
+-              fh_lock(fhp);
++      if (!resfhp->fh_dentry || dirp->i_op->lookup_it) {
++              /* called from nfsd_proc_mkdir, or possibly nfsd3_proc_create
++               * and nfsd_proc_create in case of lustre */
++              if (!resfhp->fh_dentry)
++                      fh_lock(fhp);
+               dchild = lookup_one_len(fname, dentry, flen);
+               err = PTR_ERR(dchild);
+               if (IS_ERR(dchild))
+                       goto out_nfserr;
++              resfhp->fh_dentry = NULL;
+               err = fh_compose(resfhp, fhp->fh_export, dchild, fhp);
+               if (err)
+                       goto out;
+@@ -887,10 +1053,12 @@
+        * Make sure the child dentry is still negative ...
+        */
+       err = nfserr_exist;
+-      if (dchild->d_inode) {
+-              dprintk("nfsd_create: dentry %s/%s not negative!\n",
+-                      dentry->d_name.name, dchild->d_name.name);
+-              goto out; 
++      if (error == -EOPNOTSUPP) {
++              if (dchild->d_inode) {
++                      dprintk("nfsd_create: dentry %s/%s not negative!\n",
++                              dentry->d_name.name, dchild->d_name.name);
++                      goto out;
++              }
+       }
+       if (!(iap->ia_valid & ATTR_MODE))
+@@ -903,16 +1071,19 @@
+       err = nfserr_perm;
+       switch (type) {
+       case S_IFREG:
+-              err = vfs_create(dirp, dchild, iap->ia_mode);
++              if (error == -EOPNOTSUPP)
++                      err = vfs_create(dirp, dchild, iap->ia_mode);
+               break;
+       case S_IFDIR:
+-              err = vfs_mkdir(dirp, dchild, iap->ia_mode);
++              if (error == -EOPNOTSUPP)
++                      err = vfs_mkdir(dirp, dchild, iap->ia_mode);
+               break;
+       case S_IFCHR:
+       case S_IFBLK:
+       case S_IFIFO:
+       case S_IFSOCK:
+-              err = vfs_mknod(dirp, dchild, iap->ia_mode, rdev);
++              if (error == -EOPNOTSUPP)
++                      err = vfs_mknod(dirp, dchild, iap->ia_mode, rdev);
+               break;
+       default:
+               printk("nfsd: bad file type %o in nfsd_create\n", type);
+@@ -981,7 +1152,13 @@
+       /* Get all the sanity checks out of the way before
+        * we lock the parent. */
+       err = nfserr_notdir;
+-      if(!dirp->i_op || !dirp->i_op->lookup)
++      if (dirp->i_op->mknod_raw) {
++              err = mknod_raw(dentry, fname, flen, iap->ia_mode, 0);
++              if (err && err != -EOPNOTSUPP)
++                      goto out_nfserr;
++      }
++
++      if (!dirp->i_op || !(dirp->i_op->lookup || dirp->i_op->lookup_it))
+               goto out;
+       fh_lock(fhp);
+@@ -1032,6 +1209,8 @@
+               case NFS3_CREATE_GUARDED:
+                       err = nfserr_exist;
+               }
++              if (dirp->i_op->mknod_raw)
++                      err = 0;
+               goto out;
+       }
+@@ -1138,7 +1317,7 @@
+                               struct iattr *iap)
+ {
+       struct dentry   *dentry, *dnew;
+-      int             err, cerr;
++      int             err, cerr, error = -EOPNOTSUPP;
+       err = nfserr_noent;
+       if (!flen || !plen)
+@@ -1152,12 +1331,18 @@
+               goto out;
+       fh_lock(fhp);
+       dentry = fhp->fh_dentry;
++
++      if (dentry->d_inode->i_op->symlink_raw)
++              error = symlink_raw(dentry, fname, flen, path);
++
+       dnew = lookup_one_len(fname, dentry, flen);
+       err = PTR_ERR(dnew);
+       if (IS_ERR(dnew))
+               goto out_nfserr;
+-      err = vfs_symlink(dentry->d_inode, dnew, path);
++      err = error;
++      if (err == -EOPNOTSUPP || !dentry->d_inode->i_op->symlink_raw)
++              err = vfs_symlink(dentry->d_inode, dnew, path);
+       if (!err) {
+               if (EX_ISSYNC(fhp->fh_export))
+                       nfsd_sync_dir(dentry);
+@@ -1167,7 +1352,10 @@
+                               iap->ia_valid |= ATTR_CTIME;
+                               iap->ia_mode = (iap->ia_mode&S_IALLUGO)
+                                       | S_IFLNK;
+-                              err = notify_change(dnew, iap);
++                              if (dnew->d_inode->i_op && dnew->d_inode->i_op->setattr_raw)
++                                      err = setattr_raw(dnew->d_inode, iap);
++                              else
++                                      err = notify_change(dnew, iap);
+                               if (err)
+                                       err = nfserrno(err);
+                               else if (EX_ISSYNC(fhp->fh_export))
+@@ -1227,7 +1415,10 @@
+       dold = tfhp->fh_dentry;
+       dest = dold->d_inode;
+-      err = vfs_link(dold, dirp, dnew);
++      if (dirp->i_op->link_raw)
++              err = link_raw(dold, ddir, dnew);
++      else
++              err = vfs_link(dold, dirp, dnew);
+       if (!err) {
+               if (EX_ISSYNC(ffhp->fh_export)) {
+                       nfsd_sync_dir(ddir);
+@@ -1312,7 +1503,10 @@
+                       err = nfserr_perm;
+       } else
+ #endif
+-      err = vfs_rename(fdir, odentry, tdir, ndentry);
++      if (fdir->i_op->rename_raw)
++              err = rename_raw(fdentry, tdentry, odentry, ndentry);
++      else
++              err = vfs_rename(fdir, odentry, tdir, ndentry);
+       if (!err && EX_ISSYNC(tfhp->fh_export)) {
+               nfsd_sync_dir(tdentry);
+               nfsd_sync_dir(fdentry);
+@@ -1333,7 +1527,7 @@
+       fill_post_wcc(tfhp);
+       double_up(&tdir->i_sem, &fdir->i_sem);
+       ffhp->fh_locked = tfhp->fh_locked = 0;
+-      
++
+ out:
+       return err;
+ }
+@@ -1379,9 +1573,15 @@
+                       err = nfserr_perm;
+               } else
+ #endif
+-              err = vfs_unlink(dirp, rdentry);
++              if (dirp->i_op->unlink_raw)
++                      err = unlink_raw(dentry, fname, flen, rdentry);
++              else
++                      err = vfs_unlink(dirp, rdentry);
+       } else { /* It's RMDIR */
+-              err = vfs_rmdir(dirp, rdentry);
++              if (dirp->i_op->rmdir_raw)
++                      err = rmdir_raw(dentry, fname, flen, rdentry);
++              else
++                      err = vfs_rmdir(dirp, rdentry);
+       }
+       dput(rdentry);
+Index: linux-2.4.29/include/linux/fs.h
+===================================================================
+--- linux-2.4.29.orig/include/linux/fs.h       2005-04-07 19:31:00.000000000 +0300
++++ linux-2.4.29/include/linux/fs.h    2005-05-03 16:05:36.094614008 +0300
+@@ -93,6 +93,8 @@
+ #define FS_SINGLE     8 /* Filesystem that can have only one superblock */
+ #define FS_NOMOUNT    16 /* Never mount from userland */
+ #define FS_LITTER     32 /* Keeps the tree in dcache */
++#define FS_NFSEXP_FSID        64 /* Use file system specific fsid for
++                          * exporting non device filesystems. */
+ #define FS_ODD_RENAME 32768   /* Temporary stuff; will go away as soon
+                                 * as nfs_rename() will be cleaned up
+                                 */
+@@ -1118,6 +1120,9 @@
+                        struct nameidata *nd, struct lookup_intent *it);
+ extern struct file *dentry_open_it(struct dentry *dentry, struct vfsmount *mnt,
+                           int flags, struct lookup_intent *it);
++extern int revalidate_it(struct dentry *dentry, struct lookup_intent *it);
++extern int init_private_file_it(struct file *, struct dentry *dentry, int mode,
++                              struct lookup_intent *it);
+ extern int filp_close(struct file *, fl_owner_t id);
+ extern char * getname(const char *);
+@@ -1417,6 +1422,8 @@
+ extern int follow_down(struct vfsmount **, struct dentry **);
+ extern int follow_up(struct vfsmount **, struct dentry **);
+ extern struct dentry * lookup_one_len(const char *, struct dentry *, int);
++extern struct dentry * lookup_one_len_it(const char *, struct dentry *, int,
++                                       struct lookup_intent *);
+ extern struct dentry * lookup_hash(struct qstr *, struct dentry *);
+ #define user_path_walk(name,nd)        __user_walk(name, LOOKUP_FOLLOW|LOOKUP_POSITIVE, nd)
+ #define user_path_walk_link(name,nd) __user_walk(name, LOOKUP_POSITIVE, nd)
+@@ -1437,6 +1444,8 @@
+ extern struct inode * iget4_locked(struct super_block *, unsigned long,
+                                  find_inode_t, void *);
++extern struct inode * ilookup4(struct super_block *, unsigned long,
++                             find_inode_t, void *);
+ static inline struct inode *iget4(struct super_block *sb, unsigned long ino,
+                                 find_inode_t find_actor, void *opaque)
+Index: linux-2.4.29/kernel/ksyms.c
+===================================================================
+--- linux-2.4.29.orig/kernel/ksyms.c   2005-04-07 19:31:00.000000000 +0300
++++ linux-2.4.29/kernel/ksyms.c        2005-05-03 16:04:09.445786632 +0300
+@@ -151,6 +151,7 @@
+ EXPORT_SYMBOL(ilookup);
+ EXPORT_SYMBOL(iget4_locked);
+ EXPORT_SYMBOL(unlock_new_inode);
++EXPORT_SYMBOL(ilookup4);
+ EXPORT_SYMBOL(iput);
+ EXPORT_SYMBOL(inode_init_once);
+ EXPORT_SYMBOL(__inode_init_once);
+@@ -164,6 +165,7 @@
+ EXPORT_SYMBOL(path_release);
+ EXPORT_SYMBOL(__user_walk);
+ EXPORT_SYMBOL(lookup_one_len);
++EXPORT_SYMBOL(lookup_one_len_it);
+ EXPORT_SYMBOL(lookup_hash);
+ EXPORT_SYMBOL(sys_close);
+ EXPORT_SYMBOL(dcache_lock);
diff --git a/lustre/kernel_patches/patches/nfs_statfs-toomanyfiles-rhel-2.4.patch b/lustre/kernel_patches/patches/nfs_statfs-toomanyfiles-rhel-2.4.patch
new file mode 100644 (file)
index 0000000..4bb8892
--- /dev/null
@@ -0,0 +1,30 @@
+Index: linux-2.4.21/fs/nfs/inode.c
+===================================================================
+--- linux-2.4.21.orig/fs/nfs/inode.c   2005-06-01 22:51:55.000000000 -0400
++++ linux-2.4.21/fs/nfs/inode.c        2005-06-01 23:38:54.883239912 -0400
+@@ -679,14 +679,21 @@
+               goto too_big;
+       if (TOOBIG(((res.abytes + blockres) >> blockbits)))
+               goto too_big;
+-      if (TOOBIG(res.tfiles) || TOOBIG(res.afiles))
+-              goto too_big;
+       buf->f_blocks = (res.tbytes + blockres) >> blockbits;
+       buf->f_bfree = (res.fbytes + blockres) >> blockbits;
+       buf->f_bavail = (res.abytes + blockres) >> blockbits;
+-      buf->f_files = res.tfiles;
+-      buf->f_ffree = res.afiles;
++
++      if (TOOBIG(res.tfiles))
++              buf->f_files = -1;
++      else
++              buf->f_files = res.tfiles;
++
++      if (TOOBIG(res.afiles))
++              buf->f_ffree = -1;
++      else
++              buf->f_ffree = res.afiles;
++
+       return 0;
+  too_big:
diff --git a/lustre/kernel_patches/patches/nfsd_iallocsem.patch b/lustre/kernel_patches/patches/nfsd_iallocsem.patch
new file mode 100644 (file)
index 0000000..96b9c71
--- /dev/null
@@ -0,0 +1,19 @@
+===== fs/nfsd/vfs.c 1.20 vs edited =====
+--- 1.20/fs/nfsd/vfs.c 2005-02-08 16:35:28 +02:00
++++ edited/fs/nfsd/vfs.c       2005-05-29 00:46:44 +03:00
+@@ -297,6 +297,7 @@
+       iap->ia_valid |= ATTR_CTIME;
+       if (iap->ia_valid & ATTR_SIZE) {
++              down_write(&inode->i_alloc_sem);
+               fh_lock(fhp);
+               size_change = 1;
+       }
+@@ -307,6 +308,7 @@
+       }
+       if (size_change) {
+               fh_unlock(fhp);
++              up_write(&inode->i_alloc_sem);
+               put_write_access(inode);
+       }
+       if (!err)
diff --git a/lustre/kernel_patches/patches/qsnet-rhel-2.4.patch b/lustre/kernel_patches/patches/qsnet-rhel-2.4.patch
new file mode 100644 (file)
index 0000000..45e0ac5
--- /dev/null
@@ -0,0 +1,93733 @@
+Index: linux-2.4.21/arch/i386/kernel/i386_ksyms.c
+===================================================================
+--- linux-2.4.21.orig/arch/i386/kernel/i386_ksyms.c    2005-06-01 22:51:51.000000000 -0400
++++ linux-2.4.21/arch/i386/kernel/i386_ksyms.c 2005-06-01 23:12:54.521450960 -0400
+@@ -220,3 +220,12 @@
+ EXPORT_SYMBOL_GPL(__PAGE_KERNEL);
+ extern unsigned long long __supported_pte_mask;
+ EXPORT_SYMBOL_GPL(__supported_pte_mask);
++
++extern asmlinkage long sys_open(const char *, int, int);
++EXPORT_SYMBOL(sys_open);
++extern asmlinkage off_t sys_lseek(unsigned int, off_t, unsigned int);
++EXPORT_SYMBOL(sys_lseek);
++extern asmlinkage long sys_poll(struct pollfd *, unsigned int, long);
++EXPORT_SYMBOL(sys_poll);
++extern asmlinkage long sys_kill(int, int);
++EXPORT_SYMBOL(sys_kill);
+Index: linux-2.4.21/arch/ia64/kernel/ia64_ksyms.c
+===================================================================
+--- linux-2.4.21.orig/arch/ia64/kernel/ia64_ksyms.c    2005-06-01 22:51:59.000000000 -0400
++++ linux-2.4.21/arch/ia64/kernel/ia64_ksyms.c 2005-06-01 23:14:43.773842072 -0400
+@@ -207,3 +207,13 @@
+ EXPORT_SYMBOL_GPL(show_mem);
+ EXPORT_SYMBOL_GPL(show_state);
+ EXPORT_SYMBOL_GPL(show_regs);
++
++#define __KERNEL_SYSCALLS__ 1
++#include <asm/unistd.h>
++EXPORT_SYMBOL(sys_open);
++extern asmlinkage off_t sys_lseek(unsigned int, off_t, unsigned int);
++EXPORT_SYMBOL(sys_lseek);
++extern asmlinkage long sys_poll(struct pollfd *, unsigned int, long);
++EXPORT_SYMBOL(sys_poll);
++extern asmlinkage long sys_kill(int, int);
++EXPORT_SYMBOL(sys_kill);
+Index: linux-2.4.21/arch/x86_64/kernel/x8664_ksyms.c
+===================================================================
+--- linux-2.4.21.orig/arch/x86_64/kernel/x8664_ksyms.c 2005-06-01 22:51:51.000000000 -0400
++++ linux-2.4.21/arch/x86_64/kernel/x8664_ksyms.c      2005-06-01 23:12:54.522450808 -0400
+@@ -215,6 +215,10 @@
+ EXPORT_SYMBOL(sys_exit);
+ EXPORT_SYMBOL(sys_open);
+ EXPORT_SYMBOL(sys_lseek);
++extern asmlinkage long sys_poll(struct pollfd *, unsigned int, long);
++EXPORT_SYMBOL(sys_poll);
++extern asmlinkage long sys_kill(int, int);
++EXPORT_SYMBOL(sys_kill);
+ EXPORT_SYMBOL(sys_delete_module);
+ EXPORT_SYMBOL(sys_sync);
+ EXPORT_SYMBOL(sys_pause);
+Index: linux-2.4.21/Documentation/Configure.help
+===================================================================
+--- linux-2.4.21.orig/Documentation/Configure.help     2005-06-01 23:12:39.856680344 -0400
++++ linux-2.4.21/Documentation/Configure.help  2005-06-01 23:12:54.547447008 -0400
+@@ -28030,6 +28030,54 @@
+   kernel tree does. Such modules that use library CRC32 functions
+   require M here.
++
++Enable support for Quadrics QsNet (QSNET)
++CONFIG_QSNET
++    Quadrics QsNet is a high bandwidth, ultra low latency cluster 
++    interconnect which provides both user and kernel programmers with
++    secure, direct access to the Quadrics network.
++         
++Elan 3 device driver (ELAN3)
++CONFIG_ELAN3
++   This is the main device driver for the Quadrics QsNet (Elan3) PCI
++   device. This is a high bandwidth, ultra low latency interconnect 
++   which provides both user and kernel programmers with secure, direct
++   access to the Quadrics network.
++         
++Elan 3 Kernel Comms (EP3)
++CONFIG_EP3
++   This modules implements the QsNet kernel communications layer. This
++   is used to layer kernel level facilities on top of the basic Elan3 
++   device driver. These can be used to implement subsystems such as
++   TCP/IP and remote filing systems over the QsNet interconnect.
++         
++Elan IP device (EIP)
++CONFIG_EIP
++   This is a network IP device driver for the Quadrics QsNet device.
++   It allows the TCP/IP protocol to be run over the Quadrics interconnect.
++
++Elan 4 device driver (ELAN4)
++CONFIG_ELAN4
++   This is the main device driver for the Quadrics QsNetII (Elan4) PCI-X 
++   device. This is a high bandwidth, ultra low latency interconnect which 
++   provides both user and kernel programmers with secure, direct access to 
++   the Quadrics network.
++Resource Management System support (RMS)
++CONFIG_RMS
++   This is a support module for the Quadrics RMS resource manager. It 
++   provides kernel services for monitoring and controlling user job 
++   execution, termination and cleanup.
++     
++Switch monitoring (JTAG)
++CONFIG_JTAG
++   The jtag interface is used to allow processes to send and retrieve jtag
++   information to a Quadrics QsNet Elite switch via the parallel port. 
++   The module requires a /dev/jtag[0-3] entry (usually there is only a 
++   /dev/jtag0) device and a particular device only allows one process at a
++   time to access this resource. 
++   For more information about JTag interface, please refer to the IEEE 
++   document on http://www.ieee.org 
++  
+ #
+ # A couple of things I keep forgetting:
+ #   capitalize: AppleTalk, Ethernet, DOS, DMA, FAT, FTP, Internet,
+Index: linux-2.4.21/drivers/net/Config.in
+===================================================================
+--- linux-2.4.21.orig/drivers/net/Config.in    2005-06-01 22:52:03.000000000 -0400
++++ linux-2.4.21/drivers/net/Config.in 2005-06-01 23:12:54.549446704 -0400
+@@ -272,6 +272,9 @@
+ endmenu
++# Quadrics QsNet
++source drivers/net/qsnet/Config.in
++
+ if [ "$CONFIG_PPC_ISERIES" = "y" ]; then
+    dep_tristate 'iSeries Virtual Ethernet driver support' CONFIG_VETH $CONFIG_PPC_ISERIES
+ fi
+Index: linux-2.4.21/drivers/net/Makefile
+===================================================================
+--- linux-2.4.21.orig/drivers/net/Makefile     2005-06-01 22:52:03.000000000 -0400
++++ linux-2.4.21/drivers/net/Makefile  2005-06-01 23:12:54.550446552 -0400
+@@ -8,7 +8,7 @@
+ obj-n           :=
+ obj-            :=
+-mod-subdirs     := appletalk arcnet fc irda tokenring pcmcia wireless wireless_old wan
++mod-subdirs     := appletalk arcnet fc irda tokenring pcmcia wireless wireless_old wan qsnet
+ O_TARGET := net.o
+@@ -48,6 +48,7 @@
+ subdir-$(CONFIG_DEV_APPLETALK) += appletalk
+ subdir-$(CONFIG_SK98LIN) += sk98lin
+ subdir-$(CONFIG_SKFP) += skfp
++subdir-$(CONFIG_QSNET) += qsnet
+ subdir-$(CONFIG_E100) += e100
+ subdir-$(CONFIG_E1000) += e1000
+ subdir-$(CONFIG_BONDING) += bonding
+Index: linux-2.4.21/drivers/net/qsnet/Config.in
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/Config.in      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/Config.in   2005-06-01 23:12:54.550446552 -0400
+@@ -0,0 +1,25 @@
++#
++# Config.in for Quadrics QsNet
++#
++# Copyright (c) 2004 Quadrics Ltd
++#
++# File: drivers/net/qsnet/Config.in
++#
++
++mainmenu_option next_comment
++comment "Quadrics QsNet device support"
++
++dep_tristate "Enable support for Quadrics QsNet"        CONFIG_QSNET    $CONFIG_PCI
++
++dep_tristate "Elan 3 device driver"                   CONFIG_ELAN3    $CONFIG_QSNET
++dep_tristate "Elan 4 device driver"                   CONFIG_ELAN4    $CONFIG_QSNET
++
++if [ "$CONFIG_ELAN3" = "$CONFIG_QSNET" ] || [ "$CONFIG_ELAN4" = "$CONFIG_QSNET" ]; then
++      dep_tristate "Elan Kernel Comms"                        CONFIG_EP       $CONFIG_QSNET
++fi
++dep_tristate "Elan IP device"                             CONFIG_EIP      $CONFIG_NET         $CONFIG_EP
++
++dep_tristate "Resource Management System support"        CONFIG_RMS      $CONFIG_QSNET
++dep_tristate "Switch monitoring"                         CONFIG_JTAG     $CONFIG_QSNET
++
++endmenu
+Index: linux-2.4.21/drivers/net/qsnet/eip/eip_linux.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/eip/eip_linux.c        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/eip/eip_linux.c     2005-06-01 23:12:54.553446096 -0400
+@@ -0,0 +1,1565 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: eip_linux.c,v 1.89.2.3 2004/12/20 16:54:05 mike Exp $"
++
++#include <qsnet/kernel.h>
++#include <qsnet/debug.h>
++
++#include <linux/module.h>
++
++#include <linux/init.h>
++#include <linux/list.h>
++#include <linux/netdevice.h>
++#include <linux/etherdevice.h>
++#include <linux/skbuff.h>
++#include <linux/kernel.h>
++#include <linux/proc_fs.h>
++#include <linux/time.h>
++#include <linux/version.h>
++
++#include <asm/uaccess.h>
++#include <asm/unaligned.h>
++
++#undef ASSERT
++#include <net/sock.h>
++#include <net/ip.h>
++
++
++
++#include <elan/epcomms.h>
++#include <elan/epsvc.h>
++
++#include "eip_linux.h"
++#include "eip_stats.h"
++
++#ifdef UNUSED
++static void eip_skb_display(struct sk_buff *);
++#endif
++static void eip_iph_display(struct iphdr *);
++#ifdef UNUSED
++static void eip_eiph_display(EIP_HEADER *);
++static void eip_packet_display(unsigned char *);
++#endif
++static void eip_tmd_display(EIP_TMD *);
++static void eip_tmd_head_display(EIP_TMD_HEAD *);
++static void eip_rmd_display(EIP_RMD *);
++static void eip_rmd_head_display(EIP_RMD_HEAD *);
++
++static void eip_rmd_reclaim(EIP_RMD *);
++
++static inline EP_NMH *eip_dma_reserve(int, int);
++static inline void __eip_tmd_load(EIP_TMD *, EP_RAILMASK *);
++static inline void __eip_tmd_unload(EIP_TMD *);
++static inline unsigned long eip_buff_alloc(int, int);
++static inline void eip_buff_free(unsigned long, int);
++static struct iphdr *eip_ipfrag_get(char *);
++static inline void eip_rmd_free(EIP_RMD *);
++static inline void eip_skb_load(EIP_RMD *);
++static inline void eip_skb_unload(EIP_RMD *);
++static inline void eip_rmd_requeue(EIP_RMD *);
++static EIP_RMD *eip_rmd_alloc(int, int);
++static int eip_rmd_alloc_replace(EIP_RMD *, int, int);
++static int eip_rmd_alloc_queue(int, int, int, int);
++static int eip_rmds_alloc(void);
++static void eip_rxhandler(EP_RXD *);
++static void eip_rx_tasklet(unsigned long);
++static inline void eip_tmd_init(EIP_TMD *, unsigned long, EIP_TMD_HEAD *, unsigned long, int);
++static inline EIP_TMD *eip_tmd_get(int);
++static inline void eip_tmd_put(EIP_TMD *);
++static inline void eip_tmd_load(EIP_TMD *);
++static inline void eip_tmd_unload(EIP_TMD *);
++static inline EIP_TMD *eip_tmd_alloc_queue(EIP_TMD *, EIP_TMD_HEAD *, int);
++static inline EIP_TMD *eip_tmd_alloc_queue_copybreak(EIP_TMD_HEAD *, int);
++static inline EIP_TMD *eip_tmd_alloc_queue_aggreg(EIP_TMD_HEAD *, int);
++static int eip_tmds_alloc(void);
++int eip_hard_start_xmit(struct sk_buff *, struct net_device *);
++static inline int eip_do_xmit(EIP_TMD *, EP_NMD *i, EP_PAYLOAD *);
++static void eip_txhandler(EP_TXD *, void *, EP_STATUS);
++static void eip_tx_tasklet(unsigned long);
++void eip_stop_queue(void);
++void eip_start_queue(void);
++static int eip_open(struct net_device *);
++static int eip_close(struct net_device *);
++static struct net_device_stats *eip_get_stats(struct net_device *);
++static int eip_change_mtu(struct net_device *, int);
++
++static int eip_rx_dropping = 0;
++static int eip_rx_tasklet_locked = 1;
++
++/* Global */
++struct timer_list eip_rx_tasklet_timer;
++      
++EIP_RX *eip_rx = NULL;
++EIP_TX *eip_tx = NULL;
++int  eip_checksum_state=CHECKSUM_NONE;
++
++int tmd_max = EIP_TMD_MAX_NR;
++int rmd_max = EIP_RMD_MAX_NR;
++int rx_envelope_nr = EIP_RX_ENVELOPE_NR;
++int rx_granularity = EIP_RX_GRANULARITY;
++int tx_copybreak_max = EIP_TX_COPYBREAK_MAX;
++EP_RAILMASK tx_railmask = EP_RAILMASK_ALL;
++int eipdebug = 0;
++
++#ifdef UNUSED
++static void eip_skb_display(struct sk_buff *skb)
++{
++      if (skb) {
++              __EIP_DBG_PRINTF("SKB [%p] : len %d truesize %d  proto %x pkt type %x cloned %d users %d summed %d\n", 
++                      skb, skb->len, skb->truesize, skb->protocol, skb->pkt_type, skb->cloned, atomic_read(&skb->users), skb->ip_summed);
++              __EIP_DBG_PRINTF("SKB [%p] : skb_shinfo dataref %d nr_frags %d frag_list[%p] (device %p)\n", skb,
++                       atomic_read(&skb_shinfo(skb)->dataref), skb_shinfo(skb)->nr_frags, skb_shinfo(skb)->frag_list, skb->dev);
++              __EIP_DBG_PRINTF("SKB [%p] : head[%p] data[%p] tail [%p] end [%p] data_len [%d]\n", skb, skb->head, skb->data, 
++                              skb->tail, skb->end, skb->data_len);
++              __EIP_DBG_PRINTF("SKB [%p] : Transport Layer h.(th, uh, icmph, raw)[%p]\n", skb, skb->h.th);
++              __EIP_DBG_PRINTF("SKB [%p] : Network Layer      nh.(iph, arph, raw)[%p]\n", skb, skb->nh.iph);
++              __EIP_DBG_PRINTF("SKB [%p] : Link Layer         mac.(ethernet, raw)[%p]\n", skb, skb->mac.ethernet);
++              return;
++      }
++      EIP_ERR_PRINTF("SKB IS NULL - NO SKB TO DISPLAY\n");
++}
++#endif
++static void eip_iph_display(struct iphdr *iph)
++{
++      if (iph) {
++              __EIP_DBG_PRINTF("IPH [%p] : version %d header len %d TOS 0x%x Total len %d\n", 
++                      iph, iph->version, iph->ihl, htons(iph->tos), htons(iph->tot_len));
++              __EIP_DBG_PRINTF("IPH [%p] : id %d frag flags 0x%x offset %d\n",
++                              iph, htons(iph->id), (iph->frag_off & htons(IP_CE | IP_DF | IP_MF)) >> 4, 
++                              (htons(iph->frag_off) << 3) & IP_OFFSET);
++              __EIP_DBG_PRINTF("IPH [%p] : TTL %d proto %d header checksum 0x%x\n", iph, iph->ttl, iph->protocol, iph->check);
++              __EIP_DBG_PRINTF("IPH [%p] : IP src %u.%u.%u.%u dest %u.%u.%u.%u\n", iph, 
++                               ((unsigned char *)&(iph->saddr))[0],((unsigned char *)&(iph->saddr))[1], ((unsigned char *)&(iph->saddr))[2],((unsigned char *)&(iph->saddr))[3],
++                               ((unsigned char *)&(iph->daddr))[0],((unsigned char *)&(iph->daddr))[1], ((unsigned char *)&(iph->daddr))[2],((unsigned char *)&(iph->daddr))[3]);
++              return;
++      }
++      EIP_ERR_PRINTF("IPH IS NULL - NO IPH TO DISPLAY\n");
++}
++#ifdef UNUSED
++static void eip_eiph_display(EIP_HEADER * eiph)
++{
++      if (eiph) {
++              __EIP_DBG_PRINTF("EIPH [%p] : dhost %04x.%04x.%04x sap %x\n", eiph, eiph->h_dhost.ip_bcast, eiph->h_dhost.ip_inst, 
++                              eiph->h_dhost.ip_addr, eiph->h_sap);
++              __EIP_DBG_PRINTF("EIPH [%p] : shost %04x.%04x.%04x \n", eiph, eiph->h_shost.ip_bcast, eiph->h_shost.ip_inst,
++                               eiph->h_shost.ip_addr);
++              return;
++      }
++      EIP_ERR_PRINTF("EIPH IS NULL - NO EIPH TO DISPLAY\n");
++}
++static void eip_packet_display(unsigned char *data)
++{
++      eip_eiph_display((EIP_HEADER *) data);
++      eip_iph_display((struct iphdr *) (data + EIP_HEADER_PAD + ETH_HLEN));
++}
++#endif
++static void eip_tmd_display(EIP_TMD * tmd)
++{
++      if (tmd) {
++              __EIP_DBG_PRINTF("\t\tTMD [%p] : next[%p] skb[%p] DVMA[%d]\n", tmd, tmd->chain.next, tmd->skb, tmd->dvma_idx);
++              if (tmd->dma_base)
++                      __EIP_DBG_PRINTF("TMD [%p] : head[%p] *data 0x%lx\n", tmd, tmd->head, *((unsigned long *) tmd->dma_base));
++              else
++                      __EIP_DBG_PRINTF("TMD [%p] : head[%p] NO DATA !!!\n", tmd, tmd->head);
++              __EIP_DBG_PRINTF("TMD [%p] : DMA(%lx,%d,%d) ebase[%x]\n",tmd,  tmd->dma_base, tmd->dma_len, tmd->nmd.nmd_len,
++                               tmd->nmd.nmd_addr);
++              return;
++      }
++      EIP_ERR_PRINTF("TMD IS NULL - NO TMD TO DISPLAY\n");
++      
++}
++static void eip_ipf_display(EIP_IPFRAG * ipf)
++{
++      if (ipf) {
++              __EIP_DBG_PRINTF("IPF[%p] : datagram len %d dma correction %d uts %lx frag_nr %d\n", ipf, ipf->datagram_len,
++                              ipf->dma_correction, ipf->timestamp.tv_usec, ipf->frag_nr);
++              eip_tmd_display((EIP_TMD *) ipf);
++              return;
++      }
++      EIP_ERR_PRINTF("IPF IS NULL - NO IPF TO DISPLAY\n");
++}
++
++static void eip_tmd_head_display(EIP_TMD_HEAD * head)
++{
++      if (head) {
++              __EIP_DBG_PRINTF("TMD HEAD [%p] : handle[%p] tmds[%p] %3.3d/%3.3d/%3.3d\n", head, head->handle, head->tmd, 
++                      EIP_STAT_QUEUED_GET(&head->stats), EIP_STAT_ALLOC_GET(&head->stats),
++                      eip_tx->tmd_max_nr);
++              return;
++      }
++      EIP_ERR_PRINTF("TMD HEAD IS NULL - NO TMD HEAD TO DISPLAY\n");
++}
++static void eip_rmd_display(EIP_RMD * rmd)
++{
++      if (rmd) {
++              __EIP_DBG_PRINTF("RMD [%p] : next[%p] rxd[%p] DVMA[%d]\n", rmd, rmd->chain.next, rmd->rxd, rmd->dvma_idx);
++              __EIP_DBG_PRINTF("RMD [%p] : head[%p]\n", rmd, rmd->head); 
++              __EIP_DBG_PRINTF("RMD [%p] : ebase[%x]\n", rmd,  rmd->nmd.nmd_addr); 
++              return;
++      }
++      EIP_ERR_PRINTF("RMD IS NULL - NO RMD TO DISPLAY\n");
++}
++static void eip_rmd_head_display(EIP_RMD_HEAD * head)
++{
++      if (head) {
++              __EIP_DBG_PRINTF("RMD HEAD [%p] : rcvr[%p] handle[%p] busy list[%p]\n", head, head->rcvr, head->handle, head->busy_list);
++              __EIP_DBG_PRINTF("RMD HEAD [%p] : %3.3d/%3.3d/%3.3d\n", head, 
++                              EIP_STAT_QUEUED_GET(&head->stats), EIP_STAT_ALLOC_GET(&head->stats), eip_rx->rmd_max_nr);
++              return;
++      }
++      EIP_ERR_PRINTF("RMD HEAD IS NULL - NO RMD HEAD TO DISPLAY\n");
++}
++
++/* END  - DISPLAY FUNCTIONS */
++static inline EP_NMH *eip_dma_reserve(int pages_nr, int perm)
++{
++      EP_NMH *handle = ep_dvma_reserve(eip_tx->ep_system, pages_nr, perm);
++      
++      if (handle)
++              EIP_DBG_PRINTF(EIP_DBG_EP_DVMA, "HANDLE [%p] %d pages of elan address space reserved\n", 
++                              handle, pages_nr);
++      else
++              EIP_ERR_PRINTF("cannot reserve %d page(s) of elan address space\n", pages_nr);
++
++      return handle;
++}
++
++static inline void __eip_tmd_load(EIP_TMD * tmd, EP_RAILMASK *rmask)
++{
++      EIP_ASSERT(tmd->nmd.nmd_len > 0);
++      
++      ep_dvma_load(eip_tx->ep_system, NULL, (caddr_t) tmd->dma_base, tmd->nmd.nmd_len, tmd->head->handle,
++                      tmd->dvma_idx, rmask, &tmd->nmd);
++}
++
++static inline void __eip_tmd_unload(EIP_TMD * tmd)
++{
++      EIP_ASSERT(tmd->nmd.nmd_addr && tmd->head->handle);
++      
++      ep_dvma_unload(eip_tx->ep_system, tmd->head->handle, &tmd->nmd);
++      tmd->nmd.nmd_addr = 0;
++}
++static inline unsigned long eip_buff_alloc(int buff_len, int gfp)
++{
++      unsigned long buff_base = (buff_len < PAGE_SIZE) ? 
++                              (unsigned long) kmalloc(buff_len, gfp) :
++                              __get_dma_pages(gfp, get_order(buff_len));
++      
++      if (likely(buff_base))
++              return buff_base;
++
++      EIP_ERR_PRINTF("cannot allocate %db of memory\n", buff_len);
++      return 0;
++}
++static inline void eip_buff_free(unsigned long buff_base, int buff_len)
++{
++      (buff_len < PAGE_SIZE) ?  kfree((void *) buff_base) :
++              free_pages(buff_base, get_order(buff_len));
++}
++static struct iphdr *eip_ipfrag_get(char *data)
++{
++      struct ethhdr *eh = (struct ethhdr *) (data);
++      struct iphdr *iph;
++
++      if (eh->h_proto == htons(ETH_P_IP)) {
++              iph = (struct iphdr *) ((char *) eh + ETH_HLEN);
++
++              /* EIP_DBG(eip_iph_display(iph)); */
++
++              if ((iph->frag_off & htons(IP_MF | IP_OFFSET)))
++                      return iph;
++      }
++      return NULL;
++}
++
++static inline void eip_rmd_free(EIP_RMD * rmd)
++{
++      EIP_ASSERT2(rmd->nmd.nmd_addr == 0, eip_rmd_display, rmd);
++      
++      if ( rmd->skb != NULL) 
++              kfree_skb (rmd->skb);
++      
++      kfree(rmd);
++
++      EIP_DBG_PRINTF(EIP_DBG_MEMFREE, "RMD [%p] : FREED\n", rmd);
++}
++static inline void eip_skb_load(EIP_RMD * rmd)
++{
++      EP_RAILMASK rmask = rmd->rxd ? ep_rxd_railmask (rmd->rxd) : 0;
++
++      EIP_ASSERT(skb_tailroom(rmd->skb) > 0);
++
++      ep_dvma_load(eip_tx->ep_system, NULL, (caddr_t) rmd->skb->data, skb_tailroom(rmd->skb), rmd->head->handle,
++                   rmd->dvma_idx, &rmask, &rmd->nmd);
++      
++      EIP_DBG_PRINTF(EIP_DBG_RMD_EP_DVMA, "RMD [%p] : LOADED\n", rmd);
++}
++static inline void eip_skb_unload(EIP_RMD * rmd)
++{
++      EIP_ASSERT(rmd->nmd.nmd_addr && rmd->head->handle);
++      
++      ep_dvma_unload(eip_tx->ep_system, rmd->head->handle, &rmd->nmd);
++      rmd->nmd.nmd_addr = 0;
++      
++      EIP_DBG_PRINTF(EIP_DBG_RMD_EP_DVMA, "RMD [%p] : UNLOADED\n", rmd);
++}
++static inline void eip_rmd_requeue(EIP_RMD * rmd)
++{
++      EIP_ASSERT(rmd->rxd);
++
++      rmd->chain.next    = NULL;
++
++      ep_requeue_receive(rmd->rxd, eip_rxhandler, rmd, &rmd->nmd, EP_NO_ALLOC|EP_NO_SLEEP );
++
++      atomic_inc(&rmd->head->stats);
++      
++      EIP_DBG_PRINTF(EIP_DBG_RMD_QUEUE, "RMD [%p] : REQUEUED\n", rmd);
++}
++static EIP_RMD * eip_rmd_alloc(int svc, int gfp)
++{
++      int buff_len = EIP_SVC_SMALLEST_LEN << svc;
++      EIP_RMD *rmd;
++      struct sk_buff *skb;
++
++      if (!(skb = alloc_skb((buff_len -  EIP_EXTRA), gfp)))
++              return NULL;
++      
++      skb_reserve(skb, 2);
++
++      if (!(rmd = (EIP_RMD *) kmalloc(buff_len, gfp))) {
++              kfree_skb(skb);
++              return NULL;
++      }
++
++      rmd->skb = skb;
++
++      rmd->chain.next = NULL;
++      rmd->rxd = NULL;
++      rmd->head = &eip_rx->head[svc];
++
++      return rmd;
++}
++
++static int eip_rmd_alloc_replace(EIP_RMD *rmd, int svc, int gfp) 
++{
++      struct sk_buff *skb,*old;
++      int buff_len = EIP_SVC_SMALLEST_LEN << svc;
++
++      if (!(skb = alloc_skb(buff_len, gfp)))
++              return 1;
++      
++      skb_reserve(skb, 2);
++
++      eip_skb_unload(rmd);
++
++      old      = rmd->skb;
++      rmd->skb = skb;
++
++      eip_skb_load(rmd);
++
++      eip_rmd_requeue(rmd);
++
++      kfree_skb(old);
++
++      return 0;
++}
++
++static int eip_rmd_alloc_queue(int svc, int dvma_idx, int gfp, int attr)
++{
++      EIP_RMD * rmd = eip_rmd_alloc(svc, gfp);
++
++      if (!rmd)
++              return 1;
++
++      EIP_STAT_ALLOC_ADD(&rmd->head->stats, 1);
++
++      rmd->dvma_idx = dvma_idx;
++      eip_skb_load(rmd);
++
++      EIP_DBG2(EIP_DBG_RMD, eip_rmd_display, rmd, "RMD [%p] : ALLOCATED for SVC 0x%x\n", rmd, svc);
++
++      if (ep_queue_receive(rmd->head->rcvr, eip_rxhandler, (void *) rmd, &rmd->nmd, attr) == ESUCCESS) {
++              atomic_inc(&rmd->head->stats);
++              EIP_DBG_PRINTF(EIP_DBG_RMD_QUEUE, "RMD [%p] : QUEUED on SVC 0x%x\n", rmd, svc);
++              return 0;
++      }
++      
++      EIP_ERR_PRINTF("RMD [%p] : couldn't be QUEUED on SVC 0x%x\n", rmd, svc);
++
++      EIP_STAT_ALLOC_SUB(&rmd->head->stats, 1);
++
++      eip_skb_unload(rmd);
++      eip_rmd_free(rmd);
++
++      return 1;
++}
++
++static int eip_rmds_alloc(void)
++{
++      int idx, svc;
++
++      eip_rx->irq_list = NULL;
++      eip_rx->irq_list_nr = 0;
++
++      for (svc = 0; svc < EIP_SVC_NR; svc++) {
++              eip_rx->head[svc].rcvr = ep_alloc_rcvr(eip_tx->ep_system, EIP_SVC_EP(svc), rx_envelope_nr);
++              if (!eip_rx->head[svc].rcvr) {
++                      EIP_ERR_PRINTF("Cannot install receiver for SVC 0x%x - maybe cable is disconnected\n", svc);
++                      return -EAGAIN;
++              }
++
++              eip_rx->head[svc].handle =
++                  eip_dma_reserve(EIP_DVMA_PAGES((EIP_SVC_SMALLEST_LEN << svc)) * eip_rx->rmd_max_nr,
++                                  EP_PERM_WRITE);
++              if (!eip_rx->head[svc].handle)
++                      return -ENOMEM;
++              
++              EIP_DBG(EIP_DBG_RMD_HEAD, eip_rmd_head_display, &eip_rx->head[svc]);
++
++              for (idx = 0; idx < EIP_RMD_NR; idx++) {
++                      if (eip_rmd_alloc_queue(svc, idx * EIP_DVMA_PAGES((EIP_SVC_SMALLEST_LEN << svc)), 
++                                              GFP_KERNEL, EP_NO_SLEEP))
++                              return -ENOMEM;
++              }
++      }
++      return 0;
++}
++static void eip_rmds_free(void)
++{
++      unsigned long flags;
++      EIP_RMD *rmd;
++      int svc; 
++      
++      spin_lock_irqsave(&eip_rx->lock, flags);
++      rmd = eip_rx->irq_list;
++      eip_rx->irq_list = NULL;
++      eip_rx->irq_list_nr = 0;
++      spin_unlock_irqrestore(&eip_rx->lock, flags);
++
++      eip_rmd_reclaim(rmd);
++      
++      for (svc = 0; svc < EIP_SVC_NR ; svc++) {
++              
++              while ((rmd = eip_rx->head[svc].busy_list)) {
++                      eip_rx->head[svc].busy_list = NULL;
++                      eip_rmd_reclaim(rmd);
++                      if (eip_rx->head[svc].busy_list) {
++                              EIP_DBG_PRINTF(EIP_DBG_RMD_QUEUE, "Still RMD [%p] on BUSY list SVC 0x%d - Scheduling\n", rmd, svc);     
++                              schedule();
++                      }
++              }
++
++              EIP_ASSERT(EIP_STAT_QUEUED_GET(&eip_rx->head[svc].stats) == EIP_STAT_ALLOC_GET(&eip_rx->head[svc].stats));
++              
++              EIP_DBG_PRINTF(EIP_DBG_GEN, "HEAD[%p] : FREEING RCVR [%p]\n", &eip_rx->head[svc],
++                              eip_rx->head[svc].rcvr);
++              
++              ep_free_rcvr(eip_rx->head[svc].rcvr);
++
++              EIP_DBG_PRINTF(EIP_DBG_EP_DVMA, "HEAD[%p] : RELEASING DVMA [%p]\n", &eip_rx->head[svc], 
++                              eip_rx->head[svc].handle);
++
++              ep_dvma_release(eip_tx->ep_system, eip_rx->head[svc].handle);
++      }
++
++}
++static int eip_rx_queues_low (void) {
++      int svc;
++      for (svc = 0; svc < EIP_SVC_NR; svc++) 
++              if (EIP_STAT_QUEUED_GET(&eip_rx->head[svc].stats)  < EIP_RMD_ALLOC_THRESH) 
++                      return (1);
++      return (0);
++}
++static void eip_rxhandler(EP_RXD * rxd)
++{
++      EIP_RMD *rmd            = (EIP_RMD *) ep_rxd_arg(rxd);
++      EP_STATUS ret           = ep_rxd_status(rxd);
++      EP_PAYLOAD * payload    = ep_rxd_payload(rxd);
++      unsigned long data      = (unsigned long) rmd->skb->data; 
++      int frag_nr             = 0;
++      int len;
++
++      struct sk_buff *skb;
++      static char count = 0;
++
++      atomic_dec(&rmd->head->stats);
++      rmd->rxd = rxd;
++
++      if (likely(ret == EP_SUCCESS)) {
++
++              rmd->head->dma++;
++
++              if ( eip_rx_dropping) {
++                  eip_rmd_requeue(rmd);
++                  return;
++              }
++
++              len = (payload) ? payload->Data[frag_nr++] : ep_rxd_len(rxd);
++
++              EIP_DBG(EIP_DBG_RMD, eip_rmd_display, rmd);
++
++again:
++              if ( (skb = skb_clone(rmd->skb, GFP_ATOMIC)) ) {
++                      unsigned int off = (data - (unsigned long) rmd->skb->data);
++
++                      /* have to set the length before calling
++                       * skb pull as it will not allow you to
++                       * pull past the end */
++
++                      skb_put (skb, off + len);
++                      skb_pull (skb, off);
++
++                      skb->protocol = eth_type_trans(skb, eip_rx->net_device);
++                      skb->ip_summed = eip_checksum_state;
++                      skb->dev = eip_rx->net_device;
++
++                      /* Fabien/David/Mike this is a hack/fix to allow aggrigation of packets to work.
++                       * The problem is ip_frag looks at the truesize to see if it is caching too much space.
++                       * As we are reusing a large skb (cloned) for a number of small fragments, they appear to take up alot of space.
++                       * so ip_frag dropped them after 4 frags (not good). So we lie and set the truesize to just bigger than the data. 
++                       */
++                      if (payload) 
++                              skb->truesize = SKB_DATA_ALIGN(skb->len + EIP_HEADER_PAD) +sizeof(struct sk_buff);
++
++              }
++              if ( (skb) && 
++                   (netif_rx(skb) != NET_RX_DROP)){
++
++                      eip_rx->bytes += len;
++                      
++                      if (payload && payload->Data[frag_nr] ) {
++                              data += EIP_IP_ALIGN(len);
++                              len   = payload->Data[frag_nr++];
++                              goto again;
++                      }
++                      eip_rx->packets += ++frag_nr;
++              } else if ( (eip_rx->dropped++ % 20) == 0)
++                              __EIP_DBG_PRINTK("Packet dropped by the TCP/IP stack - increase /proc/sys/net/core/netdev_max_backlog\n");
++      } else if (ret == EP_SHUTDOWN ) {
++              EIP_DBG2(EIP_DBG_RMD, eip_rmd_display, rmd, "ABORTING\n");
++                ep_complete_receive(rxd);
++                eip_skb_unload(rmd);
++              EIP_STAT_ALLOC_SUB(&rmd->head->stats, 1);
++                eip_rmd_free(rmd);
++              return;
++      } else {
++              EP_ENVELOPE *env = ep_rxd_envelope(rxd);
++              EP_NMD *nmd ;
++              
++              EIP_ERR_PRINTF("RMD[%p] : RECEIVE ret = %d\n", rmd, ret);
++
++              for (len = 0 ; len < env->nFrags ; len++) {
++                      nmd = &env->Frags[len];
++                      EIP_ERR_PRINTF("RMD[%p] : ep_frag #%d nmd_addr [%x] nmd_len %d\n", rmd, len, 
++                                      (unsigned int) nmd->nmd_addr, nmd->nmd_len);
++              }
++              eip_rx->errors++;
++              EIP_ASSERT2(atomic_read(&skb_shinfo(rmd->skb)->dataref) == 1, eip_rmd_display, rmd);
++      }
++
++      /* data is used to store the irq flags */
++      spin_lock_irqsave(&eip_rx->lock, data);
++      rmd->chain.next = eip_rx->irq_list;
++      eip_rx->irq_list = rmd;
++      eip_rx->irq_list_nr++;
++      spin_unlock_irqrestore(&eip_rx->lock, data);
++
++      if ( !timer_pending (&eip_rx_tasklet_timer)      /* the timer not already set                     */
++           && ( (count++ % eip_rx->sysctl_granularity) /* and either we have passed up a number of them */
++                || eip_rx_queues_low() ))              /* or we are low                                 */
++              mod_timer (&eip_rx_tasklet_timer, lbolt + 1);
++}
++
++/* dest ; if the buffer still reference on it mocve the rmd to the dest list */
++static void eip_rmd_reclaim(EIP_RMD *rmd) 
++{
++      EIP_RMD *rmd_next = rmd;
++      int dataref;
++
++      while (rmd_next) {
++              rmd = rmd_next;
++              rmd_next = rmd_next->chain.next;
++
++              dataref = atomic_read(&skb_shinfo(rmd->skb)->dataref);
++              EIP_ASSERT(dataref > 0);
++              
++              if (dataref == 1) {
++                      eip_rmd_requeue(rmd);
++              } else {
++                      rmd->chain.next = rmd->head->busy_list;
++                      rmd->head->busy_list = rmd;
++              }
++      }
++}
++static void eip_rx_tasklet(unsigned long arg)
++{
++      EIP_RMD *rmd, *rmd_next;
++      unsigned long flags;
++      short svc, queued;
++      int   needs_reschedule;
++
++      if (eip_rx_tasklet_locked) /* we dont want the tasklet to do anything when we are finishing */
++          return;
++
++      for (svc = 0; svc < EIP_SVC_NR; svc++) {
++              rmd = eip_rx->head[svc].busy_list;
++              eip_rx->head[svc].busy_list = NULL;
++              eip_rmd_reclaim(rmd);
++      }
++
++      spin_lock_irqsave(&eip_rx->lock, flags);
++      rmd = eip_rx->irq_list;
++      eip_rx->irq_list = NULL;
++      eip_rx->irq_list_nr = 0;
++      spin_unlock_irqrestore(&eip_rx->lock, flags);
++      
++      eip_rmd_reclaim(rmd);
++
++      needs_reschedule = 0;
++
++      for (svc = 0; svc < EIP_SVC_NR; svc++) {
++              /* the plan is : allocate some more if possible or steall some dvma space from those on the EIP_BUSY_LIST */
++              queued = EIP_STAT_QUEUED_GET(&eip_rx->head[svc].stats);
++
++              EIP_ASSERT(queued >= 0 && queued <= EIP_RMD_MAX_NR);    
++              
++              if (queued < EIP_RMD_ALLOC_THRESH) {
++                      short allocated = EIP_STAT_ALLOC_GET(&eip_rx->head[svc].stats);
++                      short how_many; 
++
++                      EIP_ASSERT(allocated >= 0 && allocated <= EIP_RMD_MAX_NR);
++                      
++                      if (likely(allocated < eip_rx->rmd_max_nr)) {
++
++                              how_many = (((allocated / EIP_RMD_ALLOC_STEP) + 1) * EIP_RMD_ALLOC_STEP);
++                              if (how_many > eip_rx->rmd_max_nr)
++                                      how_many = eip_rx->rmd_max_nr;
++
++                              for (; allocated < how_many &&  
++                                                      (eip_rmd_alloc_queue(svc, allocated * EIP_DVMA_PAGES((EIP_SVC_SMALLEST_LEN << svc)), 
++                                                                            GFP_ATOMIC, EP_NO_ALLOC|EP_NO_SLEEP) == 0) ; allocated++);
++                              if ( allocated != how_many ) {
++                                      eip_rx->reschedule++;
++                                      needs_reschedule = 1;
++                              }
++                      } else {
++                              /* steal how_many rmds and put them on the aside list */
++                              how_many = EIP_RMD_ALLOC_THRESH - queued;
++
++                              EIP_ASSERT(how_many >= 0 && how_many <= EIP_RMD_ALLOC_THRESH);
++                              
++                              rmd_next = eip_rx->head[svc].busy_list;
++                              eip_rx->head[svc].busy_list = NULL;
++
++                              while (how_many-- && rmd_next) {
++                                      rmd = rmd_next;
++                                      rmd_next = rmd_next->chain.next;
++
++                                      if (eip_rmd_alloc_replace(rmd, svc, GFP_ATOMIC)) {
++                                              rmd_next = rmd;
++                                              break;
++                                      }
++                              }
++                              eip_rx->head[svc].busy_list = rmd_next;
++                              if ( how_many )
++                                      needs_reschedule = 1;
++                      }
++              }
++      }
++      
++      if ( needs_reschedule && ( !timer_pending (&eip_rx_tasklet_timer)))
++              mod_timer (&eip_rx_tasklet_timer, lbolt + 2);
++}
++
++static inline void eip_tmd_init(EIP_TMD * tmd, unsigned long buff_base, EIP_TMD_HEAD * head, unsigned long buff_len,
++                              int dvma_idx)
++{
++      tmd->dvma_idx = dvma_idx;
++      tmd->dma_base = buff_base;
++      tmd->dma_len = -1;
++      tmd->skb = NULL;
++      tmd->head = head;
++      tmd->chain.next = NULL;
++
++      if (tmd->head != &eip_tx->head[EIP_TMD_STD]) {
++              tmd->nmd.nmd_len = buff_len;
++              eip_tmd_load(tmd);
++      } else  {
++              tmd->nmd.nmd_len  = -1;
++              tmd->nmd.nmd_addr = 0;
++      }
++}
++
++static inline EIP_TMD *eip_tmd_get(int id)
++{
++      unsigned long flags;
++      EIP_TMD *tmd = NULL;
++      spin_lock_irqsave(&eip_tx->lock, flags);
++      while ((tmd = eip_tx->head[id].tmd) == NULL) {
++              spin_unlock_irqrestore(&eip_tx->lock, flags);
++              if (ep_enable_txcallbacks(eip_tx->xmtr) == 0) {
++
++                      spin_lock_irqsave (&eip_tx->lock, flags);
++                      if (eip_tx->head[id].tmd == NULL) {
++                              __EIP_DBG_PRINTF("Cannot get a TMD on head %d ... stopping queue\n", id);
++                              
++                              eip_stop_queue ();
++                              
++                              spin_unlock_irqrestore (&eip_tx->lock, flags);
++
++                              return NULL;
++                      }
++                      spin_unlock_irqrestore (&eip_tx->lock, flags);
++              }
++
++              ep_disable_txcallbacks(eip_tx->xmtr);
++              spin_lock_irqsave(&eip_tx->lock, flags);
++      }
++      eip_tx->head[id].tmd = tmd->chain.next;
++      spin_unlock_irqrestore(&eip_tx->lock, flags);
++      atomic_dec(&tmd->head->stats);
++      return tmd;
++}
++
++static inline void eip_tmd_put(EIP_TMD * tmd)
++{
++      unsigned long flags;
++
++      tmd->skb = NULL;
++
++      spin_lock_irqsave(&eip_tx->lock, flags);
++      tmd->chain.next = tmd->head->tmd;
++      tmd->head->tmd = tmd;
++      spin_unlock_irqrestore(&eip_tx->lock, flags);
++      atomic_inc(&tmd->head->stats);
++
++      eip_start_queue();
++
++      EIP_DBG_PRINTF(EIP_DBG_TMD_QUEUE, "TMD [%p] : REQUEUED\n", tmd);
++}
++static inline void eip_tmd_load(EIP_TMD * tmd)
++{
++      EP_RAILMASK rmask = tx_railmask;
++
++      __eip_tmd_load(tmd, &rmask);
++      
++      EIP_DBG_PRINTF(EIP_DBG_EP_DVMA, "TMD [%p] : LOADED\n", tmd);
++}
++static inline void eip_tmd_unload(EIP_TMD * tmd)
++{
++      __eip_tmd_unload(tmd);
++      
++      EIP_DBG_PRINTF(EIP_DBG_EP_DVMA, "TMD [%p] : UNLOADED\n", tmd);
++}
++static inline void eip_tmd_free(EIP_TMD * tmd)
++{
++      eip_buff_free(tmd->dma_base, tmd->nmd.nmd_len);
++      
++      EIP_DBG_PRINTF(EIP_DBG_MEMFREE, "TMD [%p] : FREED\n", tmd);
++      
++      EIP_STAT_ALLOC_SUB(&tmd->head->stats, 1);
++}
++
++/* tmd on a separate block */
++static inline EIP_TMD *eip_tmd_alloc_queue(EIP_TMD * tmd, EIP_TMD_HEAD * head, int dvma_idx)
++{
++      eip_tmd_init(tmd, 0, head, -1, dvma_idx);
++
++      eip_tmd_put(tmd);
++
++      EIP_STAT_ALLOC_ADD(&tmd->head->stats, 1);
++      EIP_DBG(EIP_DBG_TMD, eip_tmd_display, tmd);
++      return tmd;
++}
++/* tmd on the buffer */
++static inline EIP_TMD *eip_tmd_alloc_queue_copybreak(EIP_TMD_HEAD * head, int dvma_idx)
++{
++      EIP_TMD *tmd;
++      unsigned long buff_base;
++
++      if (!(buff_base = eip_buff_alloc(tx_copybreak_max + sizeof(EIP_TMD), GFP_KERNEL)))
++              return NULL;
++
++      tmd = (EIP_TMD *) (buff_base + tx_copybreak_max);
++      eip_tmd_init(tmd, buff_base, head, tx_copybreak_max, dvma_idx);
++
++      eip_tmd_put(tmd);
++      EIP_STAT_ALLOC_ADD(&tmd->head->stats, 1);
++      EIP_DBG(EIP_DBG_TMD, eip_tmd_display, tmd);
++      return tmd;
++}
++
++/* ipf are on the buffer */
++static inline EIP_TMD *eip_tmd_alloc_queue_aggreg(EIP_TMD_HEAD * head, int dvma_idx)
++{
++      EIP_TMD *tmd;
++      unsigned long buff_base;
++
++      if (!(buff_base = eip_buff_alloc(EIP_SVC_BIGGEST_LEN, GFP_KERNEL)))
++              return NULL;
++
++      tmd = (EIP_TMD *) (buff_base + EIP_SVC_BIGGEST_LEN - sizeof(EIP_IPFRAG));
++      eip_tmd_init(tmd, buff_base, head, EIP_SVC_BIGGEST_LEN - sizeof(EIP_IPFRAG), dvma_idx);
++
++      eip_tmd_put(tmd);
++      EIP_STAT_ALLOC_ADD(&tmd->head->stats, 1);
++      EIP_DBG(EIP_DBG_TMD, eip_tmd_display, tmd);
++      return tmd;
++}
++
++static int eip_tmds_alloc()
++{
++      int i;
++      int page_nr;
++      EIP_TMD *tmd;
++
++      page_nr = EIP_DVMA_PAGES(tx_copybreak_max);
++
++      eip_tx->head[EIP_TMD_COPYBREAK].handle = eip_dma_reserve(page_nr * eip_tx->tmd_max_nr, EP_PERM_READ);
++      
++      EIP_DBG(EIP_DBG_TMD_HEAD, eip_tmd_head_display, &eip_tx->head[EIP_TMD_COPYBREAK]);
++
++      for (i = 0; i < EIP_TMD_NR; i++) {
++              if (!eip_tmd_alloc_queue_copybreak(&eip_tx->head[EIP_TMD_COPYBREAK], i * page_nr))
++                      return -ENOMEM;
++      }
++
++      eip_tx->head[EIP_TMD_STD].handle =
++          eip_dma_reserve(EIP_DVMA_PAGES(EIP_SVC_BIGGEST_LEN) * eip_tx->tmd_max_nr, EP_PERM_READ);
++      
++      EIP_DBG(EIP_DBG_TMD_HEAD, eip_tmd_head_display, &eip_tx->head[EIP_TMD_STD]);
++
++      tmd = kmalloc(sizeof(EIP_TMD) * EIP_TMD_NR, GFP_KERNEL);
++      if (!tmd) {
++              EIP_ERR_PRINTF("Cannot ALLOCATE %d of tmds\n", (int) sizeof(EIP_TMD) * EIP_TMD_NR);
++              return -ENOMEM;
++      }
++      
++      page_nr = EIP_DVMA_PAGES(EIP_SVC_BIGGEST_LEN);
++      
++      for (i = 0; i < EIP_TMD_NR; i++, tmd++) {
++              if (!eip_tmd_alloc_queue(tmd, &eip_tx->head[EIP_TMD_STD], i * page_nr))
++                      return -ENOMEM;
++      }
++
++      page_nr = EIP_DVMA_PAGES(EIP_SVC_BIGGEST_LEN);
++
++      eip_tx->head[EIP_TMD_AGGREG].handle = eip_dma_reserve(page_nr * eip_tx->tmd_max_nr, EP_PERM_READ);
++      EIP_DBG(EIP_DBG_TMD_HEAD, eip_tmd_head_display, &eip_tx->head[EIP_TMD_AGGREG]);
++
++      for (i = 0; i < EIP_TMD_NR; i++) {
++              if (!eip_tmd_alloc_queue_aggreg(&eip_tx->head[EIP_TMD_AGGREG], i * page_nr))
++                      return -ENOMEM;
++      }
++      return 0;
++}
++
++static void eip_tmds_free(void) 
++{
++      EIP_TMD *tmd;
++      EIP_TMD *tmd_next;
++      int i;
++      
++      ep_poll_transmits(eip_tx->xmtr);
++
++      for (i = 0 ; i < 3 ; i++) {
++again:
++              if (EIP_STAT_QUEUED_GET(&eip_rx->head[i].stats) < EIP_STAT_ALLOC_GET(&eip_rx->head[i].stats)) {
++                      EIP_DBG_PRINTF(EIP_DBG_TMD, "Polling XMTR [%p]\n", eip_tx->xmtr);       
++                      ep_poll_transmits(eip_tx->xmtr);
++                      goto again;
++              }
++      }
++      /* everything should be queued */
++        if ((tmd = eip_tx->head[EIP_TMD_COPYBREAK].tmd)) {
++            do {
++                      tmd_next = tmd->chain.next;
++                        eip_tmd_unload(tmd);
++                      
++                      EIP_DBG(EIP_DBG_TMD, eip_tmd_display, tmd);
++                      
++                        eip_tmd_free(tmd);
++            } while (tmd_next && (tmd = tmd_next));
++        }
++      
++      EIP_DBG_PRINTF(EIP_DBG_TMD_EP_DVMA, "HEAD[EIP_TMD_COPYBREAK] release DVMA [%p]\n",
++                      eip_tx->head[EIP_TMD_COPYBREAK].handle);        
++      
++        ep_dvma_release(eip_tx->ep_system, eip_tx->head[EIP_TMD_COPYBREAK].handle);
++      
++      /* these ones have been allocated as a block */
++      if ((tmd = eip_tx->head[EIP_TMD_STD].tmd)) {
++              do {
++                      if (tmd->dvma_idx == 0 ) {
++                              kfree(tmd);
++                              /* eip_tmd_free(tmd); */
++                              EIP_STAT_ALLOC_SUB(&tmd->head->stats, EIP_TMD_NR);
++                              tmd_next = NULL;
++                              EIP_DBG_PRINTF(EIP_DBG_TMD_EP_DVMA, "TMD HEAD[%p] : [EIP_TMD_STD] BLOCK FREED\n", tmd); 
++                      } else 
++                              tmd_next = tmd->chain.next;
++              } while (tmd_next && (tmd = tmd_next));
++      }
++      EIP_DBG_PRINTF(EIP_DBG_TMD_EP_DVMA, "HEAD[EIP_TMD_STD] release DVMA [%p]\n", 
++                      eip_tx->head[EIP_TMD_STD].handle);      
++      
++        ep_dvma_release(eip_tx->ep_system, eip_tx->head[EIP_TMD_STD].handle);
++      
++      if ((tmd = eip_tx->head[EIP_TMD_AGGREG].tmd)) {
++              do {
++                      tmd_next = tmd->chain.next;
++
++                      EIP_DBG(EIP_DBG_TMD, eip_tmd_display, tmd);
++                      
++                      eip_tmd_unload(tmd);
++                      eip_tmd_free(tmd);
++              } while (tmd_next && (tmd = tmd_next));
++      }
++      EIP_DBG_PRINTF(EIP_DBG_TMD_EP_DVMA, "TMD HEAD[%p] : [EIP_TMD_AGGREG] release DVMA\n", 
++                      eip_tx->head[EIP_TMD_AGGREG].handle);   
++      
++        ep_dvma_release(eip_tx->ep_system, eip_tx->head[EIP_TMD_AGGREG].handle);
++
++      ep_free_xmtr(eip_tx->xmtr);
++      EIP_DBG_PRINTF(EIP_DBG_TMD, "XMTR[%p] : FREED\n", eip_tx->xmtr);
++}
++
++static inline void eip_ipf_skb_add(EIP_IPFRAG * ipf, struct sk_buff *skb)
++{
++      int align = EIP_IP_ALIGN(skb->len);
++      
++      
++      if (ipf->dma_len == -1) {       /* like a virgin; touched for the very first time */
++              do_gettimeofday(&ipf->timestamp);
++              /* FIXE ME put that in release tmd code */
++              ipf->frag_nr            = 0;
++              ipf->dma_len            = 0;
++              ipf->datagram_len       = -1;
++              ipf->dma_correction     = 0;
++      }
++      
++      memcpy((void *) (ipf->dma_base + ipf->dma_len), skb->data, skb->len);
++      
++      if (ipf->datagram_len == -1) {
++              struct iphdr * iph = skb->nh.iph;
++              int offset = ntohs(iph->frag_off);
++
++              /* last one ? ;  offset & ~IP_OFFSET = IP fragment flags */
++              if (((offset & ~IP_OFFSET) & IP_MF) == 0) {
++                      offset &= IP_OFFSET;
++                      offset <<= 3;    
++                      ipf->datagram_len = offset + htons(iph->tot_len) - sizeof(struct iphdr);
++              }
++      }
++
++      skb->next                       = ipf->skb;
++      ipf->skb                        = skb;
++      ipf->payload.Data[ipf->frag_nr] = skb->len;
++      ipf->dma_len                   += align;
++      ipf->dma_correction            += align - skb->len  + ETH_HLEN + sizeof(struct iphdr);
++      /* FIXME ; Count got wrong if ip header has options */
++
++      ipf->frag_nr++;
++
++      EIP_DBG2(EIP_DBG_TMD, eip_ipf_display, ipf, "ADDED skb[%p] len %db ALIGNED(%db)\n", skb, skb->len, EIP_IP_ALIGN(skb->len));
++}
++
++#define eip_ipf_hasroom(ipf, skb) ((ipf->dma_len + EIP_IP_ALIGN(skb->len) < eip_tx->sysctl_ipfrag_copybreak))
++int eip_hard_start_xmit(struct sk_buff *skb, struct net_device *devnet) 
++{
++
++      EIP_TMD *tmd;
++      EP_NMD nmd;
++      struct iphdr *iph;
++      int j;
++
++      if (skb->destructor){
++              atomic_inc(&eip_tx->destructor);
++              tasklet_schedule(&eip_tx->tasklet);
++      } 
++
++      if (!(iph = eip_ipfrag_get(skb->data)) || (eip_tx->sysctl_aggregation == 0)) { /* not ip fragment */
++no_aggreg:
++              j = (skb->len < eip_tx->sysctl_copybreak) ? EIP_TMD_COPYBREAK : EIP_TMD_STD; /* j = head id */
++              
++              if (!(tmd = eip_tmd_get(j))) {
++                      if (skb->destructor)
++                              atomic_dec(&eip_tx->destructor);
++                      return 1;
++              }
++              
++              tmd->dma_len    = skb->len;
++              tmd->skb        = skb;
++              tmd->skb->next  = NULL;
++              tmd->chain.next = NULL;
++              
++              if (j == EIP_TMD_COPYBREAK) {
++                      memcpy((void *) tmd->dma_base, skb->data, skb->len);
++                      
++                      ep_nmd_subset(&nmd, &tmd->nmd, 0, skb->len);
++#ifdef EIP_MORE_STATS
++                      eip_tx->sent_copybreak++;
++#endif
++                      return eip_do_xmit(tmd, &nmd, NULL);
++              }
++              tmd->dma_base           = (unsigned long) skb->data;
++              tmd->nmd.nmd_len        = skb->len;
++              eip_tmd_load(tmd);
++
++#ifdef EIP_MORE_STATS
++              eip_tx->sent_std++;
++#endif
++              return eip_do_xmit(tmd, &tmd->nmd, NULL);
++      } else if ( skb->len > EIP_SVC_BIGGEST_LEN/2 ) { 
++              /* don't aggregate when we have a full mtu of data */
++              /* or more than 32k ; in this case it is cheaper   */
++              /* to just map the buffer and send it              */
++              goto no_aggreg;
++      } else {
++              EIP_IPFRAG *ipf = NULL;
++              unsigned long flags;
++              struct list_head *l;
++              struct iphdr *iph2;
++              int i;
++              __u16 id = iph->id;
++              __u32 saddr = iph->saddr;
++              __u32 daddr = iph->daddr;
++              __u8 protocol = iph->protocol;
++
++                      EIP_DBG(EIP_DBG_IPH, eip_iph_display, iph);
++
++              j = 0;
++
++              /* here we can't have full mtu size aggregated packet */
++              EIP_ASSERT_RET(skb->len < eip_tx->sysctl_ipfrag_copybreak, 0);
++
++              spin_lock_irqsave(&eip_tx->ipfraglock, flags);
++              list_for_each(l, &eip_tx->ipfrag) {
++                      ipf = list_entry(l, EIP_IPFRAG, list);
++                      iph2 = eip_ipfrag_get((char *) ipf->dma_base);
++                      
++                        EIP_ASSERT(iph2);
++                      
++                      if ((iph2->id == id) && 
++                                      (get_unaligned(&iph2->saddr) == saddr) && 
++                                      (get_unaligned(&iph2->daddr) == daddr) && 
++                                      (iph2->protocol == protocol)) {
++                              /* || timeout */
++                              if (eip_ipf_hasroom(ipf, skb)) {
++                                      
++                                      eip_ipf_skb_add(ipf, skb);
++                                      
++                                      if ((ipf->datagram_len != -1) && 
++                                                      (ipf->dma_len == (ipf->datagram_len + ipf->dma_correction) || 
++                                                       ipf->frag_nr == (128 / sizeof(uint32_t)))) {
++send_aggreg:
++                                              ipf->payload.Data[ipf->frag_nr] = 0;
++                                              list_del(&ipf->list);
++                                              eip_tx->ipfrag_count--;
++                                              spin_unlock_irqrestore(&eip_tx->ipfraglock, flags);
++                                      
++                                              ep_nmd_subset(&nmd, &ipf->nmd, 0, ipf->dma_len);
++                                              
++#ifdef EIP_MORE_STATS
++                                              eip_tx->sent_aggreg++;
++#endif
++                                              if ((i = eip_do_xmit((EIP_TMD *) ipf, &nmd, &ipf->payload)) != EP_SUCCESS)
++                                                      return i;
++                                              if (j)
++                                                      goto new;
++                                              return 0;
++                                      }
++                                      
++                                      spin_unlock_irqrestore(&eip_tx->ipfraglock, flags);
++                                      tasklet_schedule(&eip_tx->tasklet);
++                                      return 0;
++                              } else {
++                                      EIP_DBG_PRINTF(EIP_DBG_TMD, "IPF[%p] : FULL %db full - sending it\n", ipf, ipf->dma_len);
++                                      j = 1;
++                                      goto send_aggreg;
++                              }
++                      }
++              }
++              spin_unlock_irqrestore(&eip_tx->ipfraglock, flags);
++new:
++              if (!(ipf = (EIP_IPFRAG *) eip_tmd_get(EIP_TMD_AGGREG)))
++                      goto no_aggreg;
++
++              eip_ipf_skb_add(ipf, skb);
++              
++              spin_lock_irqsave(&eip_tx->ipfraglock, flags);
++              list_add_tail(&ipf->list, &eip_tx->ipfrag);
++              eip_tx->ipfrag_count++;
++              spin_unlock_irqrestore(&eip_tx->ipfraglock, flags);
++              tasklet_schedule(&eip_tx->tasklet);
++      }
++      return 0;
++}
++static int eip_do_xmit(EIP_TMD * tmd, EP_NMD *nmd, EP_PAYLOAD *payload)
++{
++      EIP_HEADER *eiph = (EIP_HEADER *) tmd->dma_base;
++      int         attr = EP_SET_DATA((EP_NO_SLEEP | EP_NO_INTERRUPT | EP_NO_FAILOVER), EP_TYPE_SVC_INDICATOR, EP_SVC_EIP);
++      unsigned long flags;
++      int svc, rnum;
++
++      SIZE_TO_SVC(nmd->nmd_len, svc);
++
++      EIP_DBG(EIP_DBG_TMD, eip_tmd_display, tmd);
++      /* EIP_DBG(eip_eiph_display(eiph)); */
++      
++      if (unlikely (eiph->h_dhost.ip_bcast))
++              rnum = ep_pickRail (EP_NMD_RAILMASK (nmd) & tx_railmask & ep_xmtr_availrails(eip_tx->xmtr));
++      else
++              rnum = ep_pickRail (EP_NMD_RAILMASK (nmd) & tx_railmask & ep_xmtr_noderails(eip_tx->xmtr, ntohs(eiph->h_dhost.ip_addr)));
++
++      if (rnum >= 0)
++              attr = EP_SET_PREFRAIL(attr, rnum);
++
++      /* add to inuse list  */
++      spin_lock_irqsave (&eip_tx->lock, flags);
++      list_add_tail (&tmd->chain.link, &eip_tx->inuse);
++      spin_unlock_irqrestore (&eip_tx->lock, flags);
++
++      /* ENOMEM EINVAL ECONNREFUSED ESUCCESS */
++      svc = (unlikely(eiph->h_dhost.ip_bcast)) ? 
++              ep_multicast_message(eip_tx->xmtr, -1, -1, NULL, EIP_SVC_EP(svc), attr | EP_NOT_MYSELF, eip_txhandler, tmd, payload, nmd, 1) :
++
++              ep_transmit_message(eip_tx->xmtr, ntohs(eiph->h_dhost.ip_addr), EIP_SVC_EP(svc),  attr, eip_txhandler, tmd, payload, nmd, 1);
++              
++      if (likely(svc == EP_SUCCESS))
++              return 0;
++      else if (svc == ENOMEM) {
++              EIP_ERR_PRINTF("%s", "Memory allocation error ...\n");
++              eip_tx->errors++;
++      }
++      else
++      {
++              /* EP_EINVAL occurs when the svc has a bad value or the iovec has too many frag; */
++              /* we don't use the latter option here                                        */
++              __EIP_DBG_PRINTF("TMD [%p] : DROPPED skb[%p] status = %d from ep_?_message\n", tmd, tmd->skb, svc);
++
++              eip_tx->dropped++;
++      }
++
++      eip_txhandler(NULL, tmd, -99);
++
++      /* Quadrics GNAT sw-elan/4397 - since we will "never" be able to send this packet to the */
++      /* destination node, we drop it and feign success - this has the same behaviour as an    */
++      /* ethernet where it sticks the packet on the wire, but no-one receives it.              */
++      return 0;
++}
++
++static void eip_txhandler(EP_TXD * txd, void *arg, EP_STATUS status)
++{
++      EIP_TMD *tmd = (EIP_TMD *) arg;
++      struct sk_buff *skb_next;
++      unsigned long flags;
++      int svc = 0;
++      
++      if (likely(status == EP_SUCCESS)) {
++              SIZE_TO_SVC(tmd->dma_len, svc);
++              eip_tx->dma[svc]++;
++              eip_tx->bytes += tmd->dma_len;
++              
++              if (tmd->head == &eip_tx->head[EIP_TMD_AGGREG]) {
++                      EIP_IPFRAG *ipf = (EIP_IPFRAG *) tmd;
++                      eip_tx->packets += ipf->frag_nr;
++              } else
++                      eip_tx->packets++;
++      } else {
++              if (tmd->head == &eip_tx->head[EIP_TMD_AGGREG]) {
++                      EIP_IPFRAG *ipf = (EIP_IPFRAG *) tmd;
++                      eip_tx->dropped += ipf->frag_nr;
++                      EIP_DBG_PRINTF(EIP_DBG_TMD, "txhandler aggreg packet dropped status = %d\n", status);
++              } else  {
++                      eip_tx->dropped++;
++                      EIP_DBG_PRINTF(EIP_DBG_TMD, "txhandler packet dropped status = %d\n", status);
++              }
++      }
++
++      if (tmd->head == &eip_tx->head[EIP_TMD_STD]) {
++              eip_tmd_unload(tmd);
++              tmd->dma_base = 0;
++              tmd->nmd.nmd_len = -1;
++      }
++              
++      tmd->dma_len = -1;
++      
++      svc = 0;
++      while (tmd->skb) {
++              svc++;
++              
++              if (tmd->skb->destructor)
++                      atomic_dec(&eip_tx->destructor);
++
++              skb_next = tmd->skb->next;
++              dev_kfree_skb_any(tmd->skb);
++              tmd->skb = skb_next;
++      }
++      EIP_DBG_PRINTF(EIP_DBG_TMD, "IPF/TMD [%p] : %d skb RELEASE/FREED\n", tmd, svc);
++
++      /* remove from inuse list  */
++      spin_lock_irqsave (&eip_tx->lock, flags);
++      list_del (&tmd->chain.link);
++      spin_unlock_irqrestore (&eip_tx->lock, flags);
++
++      eip_tmd_put(tmd);
++}
++
++static void eip_tx_tasklet(unsigned long arg)
++{
++      struct timeval now;
++      unsigned long flags;
++      EIP_IPFRAG *ipf, *ipfq = NULL;
++      EP_NMD nmd;
++      struct list_head *list;
++      struct list_head *tmp;
++      char resched = 0;
++      char poll = 1;
++      
++      do_gettimeofday(&now);
++      
++      spin_lock_irqsave(&eip_tx->ipfraglock, flags);
++      if (eip_tx->ipfrag_count) {
++              list_for_each_safe(list, tmp, &eip_tx->ipfrag) {
++                      ipf = list_entry(list, EIP_IPFRAG, list);
++                      /* delta = (((now.tv_sec - ipf->timestamp.tv_sec) * 1000000UL) + now.tv_usec) - ipf->timestamp.tv_usec; */
++                      if (((((now.tv_sec - ipf->timestamp.tv_sec) * 1000000UL) + now.tv_usec) - 
++                                      ipf->timestamp.tv_usec) >= (1000UL * eip_tx->sysctl_ipfrag_to)) {
++                              list_del(&ipf->list);
++                              eip_tx->ipfrag_count--;
++                              ipf->chain.next = (EIP_TMD *) ipfq;
++                              ipfq = ipf;
++                      }
++              }
++      }
++      if (eip_tx->ipfrag_count)
++              resched = 1;
++      spin_unlock_irqrestore(&eip_tx->ipfraglock, flags);
++
++      while (ipfq) {
++              poll = 0;
++
++              ep_nmd_subset(&nmd, &ipfq->nmd, 0, ipfq->dma_len);
++              
++              ipfq->payload.Data[ipfq->frag_nr] = 0;
++              
++#ifdef EIP_MORE_STATS
++              eip_tx->sent_aggreg++;
++#endif
++              ipf = (EIP_IPFRAG *) ipfq->chain.next;
++              eip_do_xmit((EIP_TMD *) ipfq, &nmd, &ipfq->payload);
++              ipfq = ipf;
++       }
++       
++       if (poll)
++               ep_poll_transmits(eip_tx->xmtr);
++
++       if (atomic_read(&eip_tx->destructor) || resched )
++               tasklet_schedule(&eip_tx->tasklet);
++}
++void eip_start_queue()
++{
++      if (netif_queue_stopped(eip_tx->net_device)) {
++              EIP_DBG_PRINTK(EIP_DBG_GEN, "Waking up %s queue\n", eip_tx->net_device->name);
++              netif_wake_queue(eip_tx->net_device);
++      }
++}
++void eip_stop_queue()
++{
++      EIP_DBG_PRINTK(EIP_DBG_GEN, "Stopping %s queue\n", eip_tx->net_device->name);
++      netif_stop_queue(eip_tx->net_device);
++}
++
++static int eip_open(struct net_device *devnet)
++{
++      if (devnet->flags & IFF_PROMISC)
++              EIP_DBG_PRINTK(EIP_DBG_GEN, "%s entering in promiscuous mode\n", devnet->name);
++
++      netif_start_queue(devnet);
++      EIP_DBG_PRINTK(EIP_DBG_GEN, "iface %s MAC %02x:%02x:%02x:%02x:%02x:%02x up\n",
++                      devnet->name, (devnet->dev_addr[0]) & 0xff,
++                      (devnet->dev_addr[1]) & 0xff, (devnet->dev_addr[2]) & 0xff, (devnet->dev_addr[3]) & 0xff,
++                      (devnet->dev_addr[4]) & 0xff, (devnet->dev_addr[5]) & 0xff);
++      return 0;
++}
++
++static int eip_close(struct net_device *devnet)
++{
++      if (devnet->flags & IFF_PROMISC)
++              EIP_DBG_PRINTK(EIP_DBG_GEN, "%s leaving promiscuous mode\n", devnet->name);
++
++      netif_stop_queue(devnet);
++
++      eip_rx_tasklet(0);
++
++      EIP_DBG_PRINTK(EIP_DBG_GEN, "iface %s MAC %02x:%02x:%02x:%02x:%02x:%02x down\n", 
++              devnet->name, (devnet->dev_addr[0]) & 0xff,
++              (devnet->dev_addr[1]) & 0xff, (devnet->dev_addr[2]) & 0xff, (devnet->dev_addr[3]) & 0xff,
++              (devnet->dev_addr[4]) & 0xff, (devnet->dev_addr[5]) & 0xff);
++      return 0;
++}
++
++static struct net_device_stats *eip_get_stats(struct net_device *devnet)
++{
++      static struct net_device_stats stats;
++
++      stats.rx_packets = eip_rx->packets;
++      stats.rx_bytes = eip_rx->bytes;
++      stats.rx_errors = eip_rx->errors;
++      stats.rx_dropped = eip_rx->dropped;
++
++      stats.tx_packets = eip_tx->packets;
++      stats.tx_bytes = eip_tx->bytes;
++      stats.tx_errors = eip_tx->errors;
++      stats.tx_dropped = eip_tx->dropped;
++      return &stats;
++}
++
++static int eip_change_mtu(struct net_device *devnet, int mtu)
++{
++      if (mtu <= EIP_MTU_MAX) {
++              EIP_DBG_PRINTK(EIP_DBG_GEN, "MTU size changed from %d to %d\n", devnet->mtu, mtu);
++              devnet->mtu = mtu;
++      }
++      return 0;
++}
++
++#ifdef MODULE
++int eip_init(void)
++{
++      struct net_device *devnet;
++      int errno = 0;
++
++      eip_rx_dropping = 0; 
++      eip_rx_tasklet_locked = 1;
++
++      /* timer up but not started */
++      init_timer (&eip_rx_tasklet_timer);
++      eip_rx_tasklet_timer.function = eip_rx_tasklet;
++      eip_rx_tasklet_timer.data     = (unsigned long) 0;
++      eip_rx_tasklet_timer.expires  = lbolt + hz;
++
++      devnet = alloc_etherdev(sizeof(EIP_RX) + sizeof(EIP_TX));
++      if (!devnet) {
++              EIP_ERR_PRINTF("Unable to ALLOCATE etherdev structure\n");
++              return -ENOMEM;
++      }
++      strcpy (devnet->name, "eip0");
++
++      EIP_DBG_PRINTK(EIP_DBG_GEN, "Enabling aggregation code\n");
++      devnet->change_mtu = eip_change_mtu;
++      devnet->mtu = EIP_MTU_MAX;
++      devnet->open = eip_open;
++      devnet->stop = eip_close;
++      devnet->hard_start_xmit = eip_hard_start_xmit;
++      devnet->get_stats = eip_get_stats;
++
++        /* devnet->features |= (NETIF_F_DYNALLOC); */
++        /* devnet->features = (NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA); */
++        /* devnet->features |= (NETIF_F_SG|NETIF_F_FRAGLIST|NETIF_F_HIGHDMA|NETIF_F_HW_CSUM); */
++
++      eip_rx = (EIP_RX *) devnet->priv;
++      eip_tx = (EIP_TX *) (eip_rx + 1);
++
++      /* instance 0 */
++      eip_tx->ep_system = ep_system();
++      if (eip_tx->ep_system == NULL) {
++              EIP_ERR_PRINTF("kernel comms for iface %s does not exist\n", devnet->name);
++              errno = -ENXIO;
++              goto out;
++      }
++      if (ep_waitfor_nodeid(eip_tx->ep_system) == ELAN_INVALID_NODE) {
++              EIP_ERR_PRINTF("network position not found\n");
++              errno = -EAGAIN;
++              goto out;
++      }
++      eip_tx->xmtr = ep_alloc_xmtr(eip_tx->ep_system);
++      if (!eip_tx->xmtr) {
++              EIP_ERR_PRINTF("Cannot create allocated transmitter - maybe cable is disconnected\n");
++              errno = -EAGAIN;
++              goto out;
++      }
++      /* assign MAC address */
++      *((int *) &devnet->dev_addr[4]) = htons(ep_nodeid(eip_tx->ep_system));
++      eip_rx->net_device = devnet;
++      eip_tx->net_device = devnet;
++
++      atomic_set(&eip_tx->destructor, 0);
++
++      if ((tmd_max >= EIP_TMD_MIN_NR) && (tmd_max <= EIP_TMD_MAX_NR)) {
++              EIP_DBG_PRINTF(EIP_DBG_GEN, "Setting tmd_max_nr to %d\n", tmd_max);
++              eip_tx->tmd_max_nr = tmd_max;
++      } else {
++              EIP_ERR_PRINTF("parameter error : %d <= tmd_max(%d) <= %d using default %d\n", 
++                              EIP_TMD_MIN_NR, tmd_max, EIP_TMD_MAX_NR, EIP_TMD_MAX_NR);
++              eip_tx->tmd_max_nr = EIP_TMD_MAX_NR;
++      }
++
++      if ((rmd_max >= EIP_RMD_MIN_NR) && (rmd_max <= EIP_RMD_MAX_NR)) {
++              EIP_DBG_PRINTF(EIP_DBG_GEN, "Setting rmd_max_nr to %d\n", rmd_max);
++              eip_rx->rmd_max_nr = rmd_max;
++      } else {
++              EIP_ERR_PRINTF("parameter error : %d <= rmd_max(%d) <= %d using default %d\n", EIP_RMD_MIN_NR,
++                         rmd_max, EIP_RMD_MAX_NR, EIP_RMD_MAX_NR);
++              eip_rx->rmd_max_nr = EIP_RMD_MAX_NR;
++      }
++
++      if ((rx_envelope_nr > 0) && (rx_envelope_nr <= 1024)) { /* > 1024 don't be silly */
++              EIP_DBG_PRINTK(EIP_DBG_GEN, "Setting rx_envelope_nr to %d\n", rx_envelope_nr);
++      } else {
++              EIP_ERR_PRINTF("parameter error : 0 < rx_envelope_nr(%d) <= 1024 using default %d\n",
++                         rx_envelope_nr, EIP_RX_ENVELOPE_NR);
++              rx_envelope_nr = EIP_RX_ENVELOPE_NR;
++      }
++
++      if (tx_copybreak_max <= EIP_TX_COPYBREAK_MAX) {
++              EIP_DBG_PRINTF(EIP_DBG_GEN, "Setting tx_copybreak_max to %d\n", tx_copybreak_max);
++      } else {
++              EIP_ERR_PRINTF("parameter error : tx_copybreak_max > %d using default %d\n",
++                         EIP_TX_COPYBREAK_MAX, EIP_TX_COPYBREAK_MAX);
++              tx_copybreak_max = EIP_TX_COPYBREAK_MAX;
++      }
++#ifdef EIP_MORE_STATS
++      eip_tx->sent_copybreak = 0;
++      eip_tx->sent_std = 0;
++      eip_tx->sent_aggreg = 0;
++#endif
++
++      eip_tx->ipfrag_count = 0;
++      eip_aggregation_set(1);
++      eip_rx_granularity_set(rx_granularity);
++      eip_tx_copybreak_set(EIP_TX_COPYBREAK);
++      eip_ipfrag_to_set(EIP_IPFRAG_TO);
++      eip_ipfrag_copybreak_set(EIP_IPFRAG_COPYBREAK);
++
++      spin_lock_init(&eip_tx->lock);
++      spin_lock_init(&eip_tx->ipfraglock);
++      spin_lock_init(&eip_rx->lock);
++      tasklet_init(&eip_rx->tasklet, eip_rx_tasklet, 0);
++      tasklet_init(&eip_tx->tasklet, eip_tx_tasklet, 0);
++      INIT_LIST_HEAD(&eip_tx->ipfrag);
++      INIT_LIST_HEAD(&eip_tx->inuse);
++
++      /* if we fail here cannot do much yet; waiting for rcvr remove code in ep. */
++      errno = eip_tmds_alloc();
++      if (errno)
++              goto out;
++
++      errno = eip_rmds_alloc();
++      if (errno)
++              goto out;
++
++      errno = eip_stats_init();
++      if (errno)
++              goto out;
++
++      if (ep_svc_indicator_set(eip_tx->ep_system, EP_SVC_EIP) != EP_SUCCESS) {
++              EIP_ERR_PRINTF("Cannot set the service indicator\n");
++              errno = -EINVAL;
++              goto out;
++      }
++
++      eip_rx_tasklet_locked = 0;
++      tasklet_schedule(&eip_rx->tasklet);
++
++      SET_MODULE_OWNER(eip_tx->net_device);
++
++      if (register_netdev(devnet)) {
++              printk("eip: failed to register netdev\n");
++              goto out;
++      }
++
++      EIP_DBG_PRINTK(EIP_DBG_GEN, "iface %s MAC %02x:%02x:%02x:%02x:%02x:%02x ready\n", 
++              devnet->name, (devnet->dev_addr[0]) & 0xff,
++              (devnet->dev_addr[1]) & 0xff, (devnet->dev_addr[2]) & 0xff, (devnet->dev_addr[3]) & 0xff,
++              (devnet->dev_addr[4]) & 0xff, (devnet->dev_addr[5]) & 0xff);
++
++      return 0;
++      out:
++      unregister_netdev(devnet);
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 25)
++      kfree(devnet);
++#else
++      free_netdev(devnet);
++#endif
++
++      return errno;
++}
++void eip_exit(void)
++{
++      int i;
++
++      eip_rx_dropping = 1;                /* means that new messages wont be sent to tcp stack */
++      eip_rx_tasklet_locked = 1;
++
++      netif_stop_queue(eip_tx->net_device);
++
++      if (ep_svc_indicator_clear(eip_tx->ep_system, EP_SVC_EIP) != EP_SUCCESS) {
++              EIP_ERR_PRINTF("Cannot unset the service indicator\n");
++      }
++
++      schedule_timeout(10);
++      
++      del_timer_sync (&eip_rx_tasklet_timer);
++
++      tasklet_disable(&eip_rx->tasklet);
++      tasklet_disable(&eip_tx->tasklet);
++
++      tasklet_kill(&eip_tx->tasklet);
++      tasklet_kill(&eip_rx->tasklet);
++
++        eip_rmds_free();
++        eip_tmds_free();
++
++      /* that things freed */
++      for (i = 0 ; i < EIP_SVC_NR ; i++) {
++              if ( EIP_STAT_ALLOC_GET(&eip_rx->head[i].stats) != 0 )
++                      EIP_ERR_PRINTF("%d RMDs not FREED on SVC[%d]\n", EIP_STAT_ALLOC_GET(&eip_rx->head[i].stats), i);
++      }
++      for (i = 0 ; i < 3 ; i++) {
++              if ( EIP_STAT_ALLOC_GET(&eip_tx->head[i].stats) != 0 )
++                      EIP_ERR_PRINTF("%d TMDs not freed on TX HEAD[%d]\n", EIP_STAT_ALLOC_GET(&eip_tx->head[i].stats), i);
++              
++      }
++      unregister_netdev(eip_tx->net_device);
++      kfree(eip_tx->net_device);
++      
++      eip_stats_cleanup();
++}
++
++module_init(eip_init);
++module_exit(eip_exit);
++
++MODULE_PARM(eipdebug, "i");
++MODULE_PARM_DESC(eipdebug, "Set debug flags");
++
++MODULE_PARM(rx_envelope_nr, "i");
++MODULE_PARM_DESC(rx_enveloppe_nr, "Number of allocated enveloppe on the rx side");
++
++MODULE_PARM(tx_copybreak_max, "i");
++MODULE_PARM_DESC(tx_copybreak_max, "Maximum size of the tx copybreak limit (default 512)");
++
++MODULE_PARM(tmd_max, "i");
++MODULE_PARM(rmd_max, "i");
++MODULE_PARM_DESC(tmd_max, "Maximun number of transmit buffers (default 64)");
++MODULE_PARM_DESC(rmd_max, "Maximun number of receive buffers (default 64)");
++
++MODULE_PARM(tx_railmask, "i");
++MODULE_PARM_DESC(tx_railmask, "Mask of which rails transmits can be queued on");
++
++MODULE_AUTHOR("Quadrics Ltd.");
++MODULE_DESCRIPTION("Elan IP driver");
++MODULE_LICENSE("GPL");
++#endif        /* MODULE */
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/eip/eip_linux.h
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/eip/eip_linux.h        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/eip/eip_linux.h     2005-06-01 23:12:54.554445944 -0400
+@@ -0,0 +1,399 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "$Id: eip_linux.h,v 1.46.2.1 2004/10/01 10:49:38 mike Exp $"
++
++#ifndef __EIP_LINUX_H
++#define __EIP_LINUX_H
++
++#define EIP_WATERMARK                 (0xfab1e)
++
++#define EIP_PAGES(s)                  (((s - 1) >> PAGE_SHIFT) + 1)
++#define EIP_DVMA_PAGES(s)             ((s < PAGE_SIZE) ? EIP_PAGES(s) + 1 : EIP_PAGES(s))
++
++#define EIP_SVC_SMALLEST_LEN          (1 << 9)        /* 512 */
++#define EIP_SVC_BIGGEST_LEN           (1 << 16)       /* 64k */
++
++#define EIP_SVC_SMALLEST              (0)
++#define EIP_SVC_BIGGEST                       (7)
++
++#define EIP_SVC_NR                    (8)
++#define EIP_SVC_EP(s)                 (s + EP_MSG_SVC_EIP512)
++
++#define EIP_STAT_ALLOC_SHIFT          (8)
++#define EIP_STAT_ALLOC_GET(atomicp)   ((int) atomic_read(atomicp) >> EIP_STAT_ALLOC_SHIFT)
++#define EIP_STAT_ALLOC_ADD(atomicp, v)        (atomic_add((v << EIP_STAT_ALLOC_SHIFT), atomicp))
++#define EIP_STAT_ALLOC_SUB(atomicp, v)        (atomic_sub((v << EIP_STAT_ALLOC_SHIFT), atomicp))
++
++#define EIP_STAT_QUEUED_MASK          (0xff)
++#define EIP_STAT_QUEUED_GET(atomicp)  ((int) atomic_read(atomicp) & EIP_STAT_QUEUED_MASK)
++
++#define EIP_RMD_NR                    (8)
++#define EIP_RMD_MIN_NR                        (8)
++#define EIP_RMD_MAX_NR                        (64)    /* should be < than (1 << EIP_STAT_ALLOC_SHIFT) */
++
++#define EIP_RMD_ALLOC_STEP            (8)
++#define EIP_RMD_ALLOC_THRESH          (16)
++
++#define EIP_RMD_ALLOC                 (1)
++#define EIP_RMD_REPLACE                       (0)
++
++#define EIP_TMD_NR                    (64)
++#define EIP_TMD_MIN_NR                        (16)
++#define EIP_TMD_MAX_NR                        (64)    /* should be < than (1 << EIP_STAT_ALLOC_SHIFT) */
++
++#define EIP_TMD_TYPE_NR                       (3)
++#define EIP_TMD_COPYBREAK             (0x0)
++#define EIP_TMD_STD                   (0x1)
++#define EIP_TMD_AGGREG                        (0x2)
++
++#define EIP_TX_COPYBREAK              (512)
++#define EIP_TX_COPYBREAK_MAX          (1024)
++
++#define EIP_IPFRAG_TO                 (50)    /* time out before a frag is sent in msec */
++#define EIP_IPFRAG_COPYBREAK          (EIP_SVC_BIGGEST_LEN - sizeof(EIP_IPFRAG) - EIP_HEADER_PAD)
++
++#define EIP_RX_ENVELOPE_NR            ((EIP_RMD_MAX_NR*EIP_SVC_NR)/2)
++#define EIP_RX_GRANULARITY            (1)
++
++#define EIP_IP_ALIGN(X)                       (((X) + (15)) & ~(15))
++#define EIP_EXTRA                     roundup (sizeof(EIP_RMD), 256)
++#define EIP_RCV_DMA_LEN(s)                    (s - EIP_EXTRA - EIP_HEADER_PAD)
++#define EIP_MTU_MAX                   (EIP_RCV_DMA_LEN(EIP_SVC_BIGGEST_LEN) - (ETH_HLEN))
++
++#define SIZE_TO_SVC(s, svc)                                                                   \
++      do {                                                                                    \
++                                      if (s <= EIP_RCV_DMA_LEN((1 << 9)))  {svc = 0;break;}   \
++                                      if (s <= EIP_RCV_DMA_LEN((1 << 10))) {svc = 1;break;}   \
++                                      if (s <= EIP_RCV_DMA_LEN((1 << 11))) {svc = 2;break;}   \
++                                      if (s <= EIP_RCV_DMA_LEN((1 << 12))) {svc = 3;break;}   \
++                                      if (s <= EIP_RCV_DMA_LEN((1 << 13))) {svc = 4;break;}   \
++                                      if (s <= EIP_RCV_DMA_LEN((1 << 14))) {svc = 5;break;}   \
++                                      if (s <= EIP_RCV_DMA_LEN((1 << 15))) {svc = 6;break;}   \
++                                      if (s <= EIP_RCV_DMA_LEN((1 << 16))) {svc = 7;break;}   \
++                                      svc = -666;                                             \
++                                      EIP_ASSERT(1 == 0);                                     \
++      } while (0)
++
++extern int eipdebug;
++#define EIP_ASSERT_ON 
++/* #define NO_DEBUG */
++
++
++/* ######################## */
++#ifdef NO_DEBUG
++#define __EIP_DBG_PRINTF(fmt, args...)
++#define EIP_DBG_PRINTF(flag, fmt, args...)
++#else
++
++#define EIP_DBG_RMD           0x1
++#define EIP_DBG_TMD           0x2
++#define EIP_DBG_RMD_HEAD      0x4
++#define EIP_DBG_TMD_HEAD      0x8
++#define EIP_DBG_EIPH          0x10
++#define EIP_DBG_IPH           0x20
++#define EIP_DBG_RMD_EP_DVMA   0x40
++#define EIP_DBG_TMD_EP_DVMA   0x80
++#define EIP_DBG_EP_DVMA               (EIP_DBG_RMD_EP_DVMA|EIP_DBG_TMD_EP_DVMA)
++#define EIP_DBG_MEMALLOC      0x100
++#define EIP_DBG_MEMFREE               0x200
++#define EIP_DBG_RMD_QUEUE     0x400
++#define EIP_DBG_TMD_QUEUE     0x800
++#define EIP_DBG_GEN           0x1000
++#define EIP_DBG_DEBUG         0x2000
++      
++#define __EIP_DBG_PRINTF(fmt, args...)        (qsnet_debugf (QSNET_DEBUG_BUFFER, " CPU #%d %s: " fmt, smp_processor_id(), __func__, ## args))
++#define EIP_DBG_PRINTF(flag, fmt, args...) (unlikely(eipdebug & flag) ? __EIP_DBG_PRINTF(fmt, ## args):(void)0)
++
++#define __EIP_DBG_PRINTK(fmt, args...)        (qsnet_debugf (QSNET_DEBUG_BUF_CON, " CPU #%d %s: " fmt, smp_processor_id(), __func__, ## args))
++#define EIP_DBG_PRINTK(flag, fmt, args...) (unlikely(eipdebug & flag) ? __EIP_DBG_PRINTF(fmt, ## args):(void)0)
++          
++#define EIP_ERR_PRINTF(fmt, args...)  __EIP_DBG_PRINTK("!!! ERROR !!! - " fmt, ## args)
++
++      
++#define EIP_DBG2(flag, fn, fn_arg, fmt, args...)                                                              \
++    if (unlikely(eipdebug & flag)) {                                                                          \
++          qsnet_debugf (QSNET_DEBUG_BUFFER, "+CPU #%d %s: " fmt, smp_processor_id(), __func__, ##args);       \
++            (void)(fn)(fn_arg);                                                                               \
++          qsnet_debugf (QSNET_DEBUG_BUFFER, "-CPU #%d %s: " fmt, smp_processor_id(), __func__, ##args);       \
++    }
++
++
++#define EIP_DBG(flag, fn, args...)                                                            \
++    if (unlikely(eipdebug & flag)) {                                                          \
++          qsnet_debugf (QSNET_DEBUG_BUFFER, "+CPU #%d %s\n", smp_processor_id(), __func__);   \
++            (void)(fn)(args);                                                                 \
++          qsnet_debugf (QSNET_DEBUG_BUFFER, "-CPU #%d %s :\n", smp_processor_id(), __func__); \
++    }
++#endif /* NO_DEBUG */
++
++
++#ifdef EIP_ASSERT_ON
++
++#define __EIP_ASSERT_PRINT(exp)                               \
++              eipdebug = 0xffff;                              \
++              EIP_ERR_PRINTF("ASSERT : %s, %s::%d\n",         \
++                     #exp, __BASE_FILE__, __LINE__);          
++
++#define EIP_ASSERT(exp)                                                       \
++              if (!(exp)) {                                           \
++                      __EIP_ASSERT_PRINT(exp);                        \
++                      netif_stop_queue(eip_tx->net_device);           \
++              }
++
++#define EIP_ASSERT2(exp, f, arg)                                      \
++      do {                                                            \
++              if (!(exp)) {                                           \
++                      __EIP_ASSERT_PRINT(exp);                        \
++                      f(arg);                                         \
++              }                                                       \
++      } while (0)
++
++#define EIP_ASSERT_BUG(exp)                                           \
++      do {                                                            \
++              if (!(exp)) {                                           \
++                      __EIP_ASSERT_PRINT(exp);                        \
++                      BUG();                                          \
++              }                                                       \
++      } while (0)
++
++#define EIP_ASSERT_GOTO(exp, label, f, arg)                           \
++      do {                                                            \
++              if (!(exp)) {                                           \
++                      __EIP_ASSERT_PRINT(exp);                        \
++                      f(arg);                                         \
++                      goto label;                                     \
++              }                                                       \
++      } while (0)
++
++#define EIP_ASSERT_RET(exp, ret)                                      \
++      do {                                                            \
++              if (!(exp)) {                                           \
++                      __EIP_ASSERT_PRINT(exp);                        \
++                      return ret;                                     \
++              }                                                       \
++      } while (0)
++
++#define EIP_ASSERT_RETURN(exp, f, arg)                                        \
++      do {                                                            \
++              if (!(exp)) {                                           \
++                      __EIP_ASSERT_PRINT(exp);                        \
++                      f(arg);                                         \
++                      return;                                         \
++              }                                                       \
++      } while (0)
++
++#define EIP_ASSERT_RETNULL(exp, f, arg)                                       \
++      do {                                                            \
++              if (!(exp)) {                                           \
++                      __EIP_ASSERT_PRINT(exp);                        \
++                      f(arg);                                         \
++                      return NULL;                                    \
++              }                                                       \
++      } while (0)
++
++#else
++
++#define EIP_ASSERT(exp)               do {} while(0)
++#define EIP_ASSERT_OUT(exp)           do {} while(0)
++#define EIP_ASSERT_RETURN(exp)                do {} while(0)
++#define EIP_ASSERT_RETNULL(exp)               do {} while(0)
++#define EIP_ASSERT_BUG(exp)           do {} while(0)
++
++#endif /* EIP_ASSERT */
++
++
++
++typedef struct {
++      u_short ip_bcast;
++      u_short ip_inst;
++      u_short ip_addr;
++} EIP_ADDRESS;
++
++typedef struct {
++      EIP_ADDRESS h_dhost;
++      EIP_ADDRESS h_shost;
++      u_short h_sap;
++} EIP_HEADER;
++#define EIP_HEADER_PAD                        (2)
++
++typedef struct eip_proc_fs {
++      const char *name;
++      struct proc_dir_entry **parent;
++      read_proc_t *read;
++      write_proc_t *write;
++      unsigned char allocated;
++      struct proc_dir_entry *entry;
++} EIP_PROC_FS;
++
++#define EIP_PROC_ROOT_DIR             "eip"
++
++#define EIP_PROC_DEBUG_DIR            "debug"
++#define EIP_PROC_DEBUG_RX_FLUSH               "rx_flush"
++#define EIP_PROC_DEBUG_TX_FLUSH               "tx_flush"
++
++#define EIP_PROC_AGGREG_DIR           "aggregation"
++#define EIP_PROC_AGGREG_ONOFF         "enable"
++#define EIP_PROC_AGGREG_TO            "timeout"
++#define EIP_PROC_AGGREG_COPYBREAK     "copybreak"
++
++#define EIP_PROC_TX_COPYBREAK         "tx_copybreak"
++#define EIP_PROC_STATS                        "stats"
++#define EIP_PROC_RX_GRAN              "rx_granularity"
++#define EIP_PROC_TX_RAILMASK          "tx_railmask"
++#define EIP_PROC_TMD_INUSE            "tmd_inuse"
++#define EIP_PROC_EIPDEBUG             "eipdebug"
++#define EIP_PROC_CHECKSUM               "checksum"
++
++/* RX */
++/* dma_len is used to keep the len of a received packet */
++/* nmd.nmd_len is the max dma that can be received      */
++/*                                                      */
++struct eip_rmd {
++      struct sk_buff *skb;
++
++      EP_NMD nmd;
++      u16 dvma_idx;
++
++      EP_RXD *rxd;
++      struct eip_rmd_head *head;
++      union {
++              struct list_head link;                          /* when on "busy" list */
++              struct eip_rmd  *next;                          /* all other lists */
++      } chain;
++};
++typedef struct eip_rmd EIP_RMD;
++struct eip_rmd_head {
++      EP_NMH *handle;
++
++      EP_RCVR *rcvr;
++      EIP_RMD *busy_list;
++
++      /* stats */
++      atomic_t stats;
++      unsigned long dma;
++};
++
++typedef struct eip_rmd_head EIP_RMD_HEAD;
++typedef struct eip_rx {
++      struct eip_rmd_head head[EIP_SVC_NR];
++
++      EIP_RMD *irq_list;
++      short    irq_list_nr;   
++
++      /* stats */
++      unsigned long packets;
++      unsigned long bytes;
++      unsigned long errors;
++      unsigned long dropped;
++      unsigned long reschedule;
++
++      spinlock_t lock;
++      struct tasklet_struct tasklet;
++      unsigned char rmd_max_nr;
++      unsigned char sysctl_granularity;
++      struct net_device *net_device;
++} EIP_RX;
++
++/* TX */
++/* dma_len_max is the maximum len for a given DMA                      */
++/* where mnd.nmd_len is the len of the packet to send ~> than skb->len */
++typedef struct eip_ipfrag_handle {
++      /* common with tmd */
++      unsigned long dma_base;
++      int dma_len;
++      EP_NMD nmd;
++      u16 dvma_idx;
++
++      struct sk_buff *skb;
++      struct eip_tmd_head *head;
++      union {
++              struct list_head link;                          /* when on "busy" list */
++              struct eip_tmd  *next;                          /* all other lists */
++      } chain;
++
++      /* private */
++      struct list_head list;
++      struct timeval timestamp;
++      unsigned int frag_nr;
++      int datagram_len; /* Ip data */
++      int dma_correction;
++      EP_PAYLOAD payload;
++} EIP_IPFRAG;
++
++struct eip_tmd {
++      unsigned long dma_base;
++      int dma_len;
++      EP_NMD nmd;
++      u16 dvma_idx;
++
++      struct sk_buff *skb;
++      struct eip_tmd_head *head;
++      union {
++              struct list_head link;                          /* when on "busy" list */
++              struct eip_tmd  *next;                          /* all other lists */
++      } chain;
++};
++
++struct eip_tmd_head {
++      EP_NMH *handle;
++
++      struct eip_tmd *tmd;
++      atomic_t stats;
++};
++
++typedef struct eip_tmd EIP_TMD;
++typedef struct eip_tmd_head EIP_TMD_HEAD;
++
++/* #define EIP_MORE_STATS */
++
++typedef struct eip_tx {
++      struct net_device *net_device;
++      EP_XMTR *xmtr;
++      EP_SYS *ep_system;
++
++      struct eip_tmd_head head[EIP_TMD_TYPE_NR];
++      struct list_head inuse;
++      atomic_t destructor;
++
++      /* stats */
++      unsigned long packets;
++      unsigned long bytes;
++      unsigned long errors;
++      unsigned long dropped;
++      unsigned long dma[EIP_SVC_NR];
++      
++#ifdef EIP_MORE_STATS
++      unsigned long sent_copybreak;
++      unsigned long sent_std;
++      unsigned long sent_aggreg;
++#endif
++
++      unsigned char tmd_max_nr;
++
++      unsigned short sysctl_copybreak;
++      unsigned short sysctl_ipfrag_to;
++      unsigned short sysctl_ipfrag_copybreak;
++      unsigned short sysctl_aggregation;
++
++      unsigned short ipfrag_count;
++      struct list_head ipfrag;
++      spinlock_t ipfraglock;
++
++      spinlock_t lock;
++      struct tasklet_struct tasklet;
++} EIP_TX;
++
++/* =============================================== */
++    /* unsigned long   multicast; */
++#endif                                /* __EIP_LINUX_H */
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/eip/eip_stats.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/eip/eip_stats.c        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/eip/eip_stats.c     2005-06-01 23:12:54.555445792 -0400
+@@ -0,0 +1,374 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++/*
++ * $Id: eip_stats.c,v 1.34.2.1 2005/01/26 14:31:56 mike Exp $
++ * $Source: /cvs/master/quadrics/eipmod/eip_stats.c,v $
++ */
++
++#include <qsnet/kernel.h>
++#include <linux/module.h>
++
++#include <elan/epcomms.h>
++
++#include <linux/netdevice.h>
++
++#include <linux/kernel.h>
++#include <linux/proc_fs.h>
++
++#include <asm/atomic.h>
++
++#include <qsnet/procfs_linux.h>
++
++#include "eip_linux.h"
++#include "eip_stats.h"
++
++extern EIP_RX *eip_rx;
++extern EIP_TX *eip_tx;
++extern int tx_copybreak_max;
++extern EP_RAILMASK tx_railmask;
++extern int  eip_checksum_state;
++extern void eip_stop_queue(void);
++extern void eip_start_queue(void);
++
++static int eip_stats_read(char *buf, char **start, off_t off, int count, int *eof, void *data)
++{
++      int i, outlen = 0;
++
++      *buf = '\0';
++      strcat(buf, "\n");
++      strcat(buf, "--------------------------------------------+------------+-----------------+\n");
++      strcat(buf, "    SKB/DMA    |               | Rx         | Tx         |  TMD TYPE       |\n");
++      strcat(buf, "--------------------------------------------+------------|-----------------+\n");
++
++      i = 0;
++      sprintf(buf + strlen(buf), " [%5d/%5d] | [%3.3d/%3.3d/%3.3d] | %10ld | %10ld | #1[%3.3d/%3.3d/%3.3d] |\n",
++              EIP_SVC_SMALLEST_LEN << i, (int) EIP_RCV_DMA_LEN((EIP_SVC_SMALLEST_LEN << i)),
++              EIP_STAT_QUEUED_GET(&eip_rx->head[i].stats), EIP_STAT_ALLOC_GET(&eip_rx->head[i].stats),
++              eip_rx->rmd_max_nr, eip_rx->head[i].dma, eip_tx->dma[i],
++              EIP_STAT_QUEUED_GET(&eip_tx->head[i].stats), EIP_STAT_ALLOC_GET(&eip_tx->head[i].stats),
++               eip_tx->tmd_max_nr);
++
++      i++;
++      sprintf(buf + strlen(buf), " [%5d/%5d] | [%3.3d/%3.3d/%3.3d] | %10ld | %10ld | #2[%3.3d/%3.3d/%3.3d] |\n",
++              EIP_SVC_SMALLEST_LEN << i, (int) EIP_RCV_DMA_LEN((EIP_SVC_SMALLEST_LEN << i)),
++              EIP_STAT_QUEUED_GET(&eip_rx->head[i].stats), EIP_STAT_ALLOC_GET(&eip_rx->head[i].stats),
++              eip_rx->rmd_max_nr, eip_rx->head[i].dma, eip_tx->dma[i],
++              EIP_STAT_QUEUED_GET(&eip_tx->head[i].stats), EIP_STAT_ALLOC_GET(&eip_tx->head[i].stats),
++              eip_tx->tmd_max_nr);
++
++      i++;
++      sprintf(buf + strlen(buf), " [%5d/%5d] | [%3.3d/%3.3d/%3.3d] | %10ld | %10ld | #3[%3.3d/%3.3d/%3.3d] |\n",
++              EIP_SVC_SMALLEST_LEN << i, (int) EIP_RCV_DMA_LEN((EIP_SVC_SMALLEST_LEN << i)),
++              EIP_STAT_QUEUED_GET(&eip_rx->head[i].stats), EIP_STAT_ALLOC_GET(&eip_rx->head[i].stats),
++              eip_rx->rmd_max_nr, eip_rx->head[i].dma, eip_tx->dma[i],
++              EIP_STAT_QUEUED_GET(&eip_tx->head[i].stats), EIP_STAT_ALLOC_GET(&eip_tx->head[i].stats),
++              eip_tx->tmd_max_nr);
++
++      i++;
++      sprintf(buf + strlen(buf), " [%5d/%5d] | [%3.3d/%3.3d/%3.3d] | %10ld | %10ld +-----------------+\n",
++              EIP_SVC_SMALLEST_LEN << i, (int) EIP_RCV_DMA_LEN((EIP_SVC_SMALLEST_LEN << i)),
++              EIP_STAT_QUEUED_GET(&eip_rx->head[i].stats), EIP_STAT_ALLOC_GET(&eip_rx->head[i].stats),
++              eip_rx->rmd_max_nr, eip_rx->head[i].dma, eip_tx->dma[i]);
++
++      i++;
++      sprintf(buf + strlen(buf), " [%5d/%5d] | [%3.3d/%3.3d/%3.3d] | %10ld | %10ld |\n",
++              EIP_SVC_SMALLEST_LEN << i, (int) EIP_RCV_DMA_LEN((EIP_SVC_SMALLEST_LEN << i)),
++              EIP_STAT_QUEUED_GET(&eip_rx->head[i].stats), EIP_STAT_ALLOC_GET(&eip_rx->head[i].stats),
++              eip_rx->rmd_max_nr, eip_rx->head[i].dma, eip_tx->dma[i]);
++
++      i++;
++      sprintf(buf + strlen(buf), " [%5d/%5d] | [%3.3d/%3.3d/%3.3d] | %10ld | %10ld |\n",
++              EIP_SVC_SMALLEST_LEN << i, (int) EIP_RCV_DMA_LEN((EIP_SVC_SMALLEST_LEN << i)),
++              EIP_STAT_QUEUED_GET(&eip_rx->head[i].stats), EIP_STAT_ALLOC_GET(&eip_rx->head[i].stats),
++              eip_rx->rmd_max_nr, eip_rx->head[i].dma, eip_tx->dma[i]);
++
++      i++;
++      sprintf(buf + strlen(buf), " [%5d/%5d] | [%3.3d/%3.3d/%3.3d] | %10ld | %10ld |\n",
++              EIP_SVC_SMALLEST_LEN << i, (int) EIP_RCV_DMA_LEN((EIP_SVC_SMALLEST_LEN << i)),
++              EIP_STAT_QUEUED_GET(&eip_rx->head[i].stats), EIP_STAT_ALLOC_GET(&eip_rx->head[i].stats),
++              eip_rx->rmd_max_nr, eip_rx->head[i].dma, eip_tx->dma[i]);
++
++      i++;
++      sprintf(buf + strlen(buf), " [%5d/%5d] | [%3.3d/%3.3d/%3.3d] | %10ld | %10ld |\n",
++              EIP_SVC_SMALLEST_LEN << i, (int) EIP_RCV_DMA_LEN((EIP_SVC_SMALLEST_LEN << i)),
++              EIP_STAT_QUEUED_GET(&eip_rx->head[i].stats), EIP_STAT_ALLOC_GET(&eip_rx->head[i].stats),
++              eip_rx->rmd_max_nr, eip_rx->head[i].dma, eip_tx->dma[i]);
++
++      strcat(buf, "--------------------------------------------+------------+\n");
++      sprintf(buf + strlen(buf), " RMD IRQ %4.4d                    %10lu | %10lu |\n",
++              eip_rx->irq_list_nr, 
++              eip_rx->packets, eip_tx->packets);
++      strcat(buf, "--------------------------------------------+------------+\n");
++
++#ifdef EIP_MORE_STATS
++      strcat(buf, "\n");
++      sprintf(buf + strlen(buf), " Copybreak %10ld Std %10ld Aggreg %10ld\n",
++                      eip_tx->sent_copybreak, eip_tx->sent_std, eip_tx->sent_aggreg);
++#endif
++
++
++      strcat(buf, "\n");
++      sprintf(buf + strlen(buf), "Rx bytes: %lu (%lu Mb) errors: %lu dropped: %lu reschedule: %lu\n",
++              eip_rx->bytes, eip_rx->bytes / (1024 * 1024), eip_rx->errors, eip_rx->dropped, eip_rx->reschedule);
++      sprintf(buf + strlen(buf), "Tx bytes: %lu (%lu Mb) errors: %lu dropped: %lu\n",
++              eip_tx->bytes, eip_tx->bytes / (1024 * 1024), eip_tx->errors, eip_tx->dropped);
++      strcat(buf, "\n");
++
++      outlen = strlen(buf);
++      ASSERT(outlen < PAGE_SIZE);
++      *eof = 1;
++      return outlen;
++}
++
++void eip_stats_dump(void)
++{
++    int eof;
++
++    char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
++
++    if (buf == NULL)
++    {
++      printk("no memory to produce eip_stats\n");
++      return;
++    }
++
++    eip_stats_read(buf, NULL, 0, 0, &eof, NULL);
++
++    printk(buf);
++
++    kfree(buf);
++}
++
++static int eip_stats_write(struct file *file, const char *buf, unsigned long count, void *data)
++{
++      int i;
++      unsigned long flags;
++
++      spin_lock_irqsave(&eip_rx->lock, flags);
++      eip_rx->packets = 0;
++      eip_rx->bytes = 0;
++      eip_rx->errors = 0;
++      eip_rx->dropped = 0;
++      eip_rx->reschedule = 0;
++      for (i = 0; i < EIP_SVC_NR; eip_rx->head[i].dma = 0, i++);
++      spin_unlock_irqrestore(&eip_rx->lock, flags);
++
++      spin_lock_irqsave(&eip_tx->lock, flags);
++      eip_tx->packets = 0;
++      eip_tx->bytes = 0;
++      eip_tx->errors = 0;
++      eip_tx->dropped = 0;
++#ifdef EIP_MORE_STATS
++      eip_tx->sent_copybreak = 0;
++      eip_tx->sent_std = 0;
++      eip_tx->sent_aggreg = 0;
++#endif
++      for (i = 0; i < EIP_SVC_NR; eip_tx->dma[i] = 0, i++);
++      spin_unlock_irqrestore(&eip_tx->lock, flags);
++
++      return count;
++}
++
++#define               eip_stats_var_write(name)                                                                       \
++static int eip_stats_##name##_write(struct file *file, const char *buf, unsigned long count, void *data)      \
++{                                                                                                             \
++      char * b = (char *) buf;                                                                                \
++      *(b + count) = '\0';                                                                                    \
++      eip_##name##_set((int) simple_strtoul(b, NULL, 10));                                                    \
++      return count;                                                                                           \
++}
++
++#define       eip_stats_var_read(name, var)                                                                   \
++static int eip_stats_##name##_read(char *buf, char **start, off_t off, int count, int *eof, void *data)               \
++{                                                                                                             \
++      sprintf(buf, "%d\n", var);                                                                              \
++      *eof = 1;                                                                                               \
++      return strlen(buf);                                                                                     \
++}
++
++
++#define               eip_stats_var_set(name, min, max, default, var)                                                                 \
++void eip_##name##_set(int i)                                                                                                  \
++{                                                                                                                             \
++      if ( (i >= min) && (i <= max)) {                                                                                        \
++              EIP_DBG_PRINTK(EIP_DBG_GEN, "Setting " #name " to %d\n", i);                                                    \
++              var =(unsigned short) i;                                                                                        \
++      }                                                                                                                       \
++      else {                                                                                                                  \
++              EIP_ERR_PRINTF("parameter error : %d <= " #name "(%d) <= %d using default %d\n", min, i, (int) max, (int) default);     \
++      }                                                                                                                       \
++}
++
++eip_stats_var_set(tx_copybreak, 0, tx_copybreak_max, EIP_TX_COPYBREAK, eip_tx->sysctl_copybreak);
++eip_stats_var_set(rx_granularity, 1, EIP_RMD_MIN_NR, EIP_RX_GRANULARITY, eip_rx->sysctl_granularity);
++eip_stats_var_set(tx_railmask, 0, EP_RAILMASK_ALL, EP_RAILMASK_ALL, tx_railmask);
++eip_stats_var_set(ipfrag_to, 0, (1 << 16), EIP_IPFRAG_TO, eip_tx->sysctl_ipfrag_to);
++eip_stats_var_set(aggregation, 0, 1, 1, eip_tx->sysctl_aggregation);
++eip_stats_var_set(ipfrag_copybreak, 0, EIP_IPFRAG_COPYBREAK, EIP_IPFRAG_COPYBREAK, eip_tx->sysctl_ipfrag_copybreak);
++/* eip_stats_var_set(eipdebug, 0, , 0, eipdebug); */
++
++eip_stats_var_read(aggregation, eip_tx->sysctl_aggregation);
++eip_stats_var_read(ipfrag_count, eip_tx->ipfrag_count);
++eip_stats_var_read(ipfrag_to, eip_tx->sysctl_ipfrag_to);
++eip_stats_var_read(ipfrag_copybreak, eip_tx->sysctl_ipfrag_copybreak);
++eip_stats_var_read(tx_copybreak, eip_tx->sysctl_copybreak);
++eip_stats_var_read(rx_granularity, eip_rx->sysctl_granularity);
++eip_stats_var_read(tx_railmask, tx_railmask);
++
++eip_stats_var_write(aggregation);
++eip_stats_var_write(ipfrag_to);
++eip_stats_var_write(ipfrag_copybreak);
++eip_stats_var_write(tx_copybreak);
++eip_stats_var_write(rx_granularity);
++eip_stats_var_write(tx_railmask);
++
++
++static int eip_checksum_write(struct file *file, const char *buf, unsigned long count, void *data)
++{
++      char * b = (char *) buf;
++      int    value;
++
++      *(b + count) = '\0';
++
++      value = (int) simple_strtoul(b, NULL, 10);
++      if  ((value >= CHECKSUM_NONE) && (value <= CHECKSUM_UNNECESSARY)) 
++              eip_checksum_state = value;
++      else 
++              EIP_ERR_PRINTF("%d <= checksum(%d) <= %d using old value %d\n", CHECKSUM_NONE, value, CHECKSUM_UNNECESSARY, eip_checksum_state);
++
++      return count;
++}
++
++static int eip_checksum_read(char *buf, char **start, off_t off, int count, int *eof, void *data)
++{
++      switch ( eip_checksum_state ) 
++      {
++      case 0  : sprintf(buf, "0 CHECKSUM_NONE\n");                      break;
++      case 1  : sprintf(buf, "1 CHECKSUM_HW\n");                        break;
++      case 2  : sprintf(buf, "2 CHECKSUM_UNNECESSARY\n");               break;
++      default : sprintf(buf, "%d INVALID VALUE\n", eip_checksum_state); break;
++      }
++      *eof = 1;
++      return strlen(buf);
++}
++
++static int eip_stats_eipdebug_read(char *buf, char **start, off_t off, int count, int *eof, void *data)
++{
++      *buf = '\0';
++      sprintf(buf + strlen(buf), "0x%x\n", eipdebug);
++      *eof = 1;
++      return strlen(buf);
++}
++static int eip_stats_eipdebug_write(struct file *file, const char *buf, unsigned long count, void *data)
++{
++      char * p = (char *) buf;
++      *(p + count - 1) = '\0';
++      eipdebug = simple_strtoul(p, NULL, 0);
++      __EIP_DBG_PRINTK("Setting eipdebug to 0x%x\n", eipdebug);
++      return count;
++}
++
++static int eip_stats_tmd_inuse_read(char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++      struct list_head *lp;
++      unsigned long flags;
++      unsigned int len = 0;
++
++      spin_lock_irqsave(&eip_tx->lock, flags);
++      list_for_each (lp, &eip_tx->inuse) {
++              EIP_TMD *tmd = list_entry (lp, EIP_TMD, chain.link);
++              EIP_HEADER *eiph = (EIP_HEADER *) tmd->dma_base;
++              
++                len += sprintf(page+len, "tmd=%p id=%d len=%d\n",
++                             tmd, eiph ? ntohs(eiph->h_dhost.ip_addr) : -1,
++                             tmd->dma_len);
++
++                if (len + 40 >= count)
++                        break;
++        }
++        spin_unlock_irqrestore(&eip_tx->lock, flags);
++
++      return qsnet_proc_calc_metrics (page, start, off, count, eof, len);
++}
++
++static int eip_stats_debug_rx_flush(struct file *file, const char *buf, unsigned long count, void *data)
++{
++      EIP_DBG_PRINTF(EIP_DBG_GEN, "Flushing rx ...\n");
++      tasklet_schedule(&eip_rx->tasklet);
++      return count;
++}
++static int eip_stats_debug_tx_flush(struct file *file, const char *buf, unsigned long count, void *data)
++{
++      EIP_DBG_PRINTF(EIP_DBG_GEN, "Flushing tx ... %d tmds reclaimed\n", ep_enable_txcallbacks(eip_tx->xmtr));
++      ep_disable_txcallbacks(eip_tx->xmtr);
++      tasklet_schedule(&eip_tx->tasklet);
++      return count;
++}
++
++#define EIP_PROC_PARENT_NR    (3)
++/* NOTE : the parents should be declared b4 the children */
++static EIP_PROC_FS eip_procs[] = {
++      /* {name, parent, read fn, write fn, allocated, entry}, */
++      {EIP_PROC_ROOT_DIR, &qsnet_procfs_root, NULL, NULL, 0, NULL},
++      {EIP_PROC_DEBUG_DIR, &eip_procs[0].entry, NULL, NULL, 0, NULL},
++      {EIP_PROC_AGGREG_DIR, &eip_procs[0].entry, NULL, NULL, 0, NULL},        /* end of parents */
++      {EIP_PROC_STATS, &eip_procs[0].entry, eip_stats_read, eip_stats_write, 0, NULL},
++      {EIP_PROC_TX_COPYBREAK, &eip_procs[0].entry, eip_stats_tx_copybreak_read, eip_stats_tx_copybreak_write, 0, NULL},
++      {EIP_PROC_RX_GRAN, &eip_procs[0].entry, eip_stats_rx_granularity_read, eip_stats_rx_granularity_write, 0, NULL},
++      {EIP_PROC_TX_RAILMASK, &eip_procs[0].entry, eip_stats_tx_railmask_read, eip_stats_tx_railmask_write, 0, NULL},
++      {EIP_PROC_TMD_INUSE, &eip_procs[0].entry, eip_stats_tmd_inuse_read, NULL, 0, NULL},
++      {EIP_PROC_EIPDEBUG, &eip_procs[0].entry, eip_stats_eipdebug_read, eip_stats_eipdebug_write, 0, NULL},
++      {EIP_PROC_CHECKSUM, &eip_procs[0].entry, eip_checksum_read, eip_checksum_write, 0, NULL},
++      {EIP_PROC_DEBUG_RX_FLUSH, &eip_procs[1].entry, NULL, eip_stats_debug_rx_flush, 0, NULL},
++      {EIP_PROC_DEBUG_TX_FLUSH, &eip_procs[1].entry, NULL, eip_stats_debug_tx_flush, 0, NULL},
++      {"ipfrag_count", &eip_procs[2].entry, eip_stats_ipfrag_count_read, NULL, 0, NULL},
++      {EIP_PROC_AGGREG_TO, &eip_procs[2].entry, eip_stats_ipfrag_to_read, eip_stats_ipfrag_to_write, 0, NULL},
++      {EIP_PROC_AGGREG_ONOFF, &eip_procs[2].entry, eip_stats_aggregation_read, eip_stats_aggregation_write, 0, NULL},
++      {EIP_PROC_AGGREG_COPYBREAK, &eip_procs[2].entry, eip_stats_ipfrag_copybreak_read, eip_stats_ipfrag_copybreak_write, 0, NULL},
++      {NULL, NULL, NULL, NULL, 1, NULL},
++};
++
++int eip_stats_init(void)
++{
++      int p;
++
++      for (p = 0; !eip_procs[p].allocated; p++) {
++              if (p < EIP_PROC_PARENT_NR)
++                      eip_procs[p].entry = proc_mkdir(eip_procs[p].name, *eip_procs[p].parent);
++              else
++                      eip_procs[p].entry = create_proc_entry(eip_procs[p].name, 0, *eip_procs[p].parent);
++
++              if (!eip_procs[p].entry) {
++                      EIP_ERR_PRINTF("%s\n", "Cannot allocate proc entry");
++                      eip_stats_cleanup();
++                      return -ENOMEM;
++              }
++
++              eip_procs[p].entry->owner = THIS_MODULE;
++              eip_procs[p].entry->write_proc = eip_procs[p].write;
++              eip_procs[p].entry->read_proc = eip_procs[p].read;
++              eip_procs[p].allocated = 1;
++      }
++      eip_procs[p].allocated = 0;
++      return 0;
++}
++
++void eip_stats_cleanup(void)
++{
++      int p;
++      for (p = 0; eip_procs[p].allocated; p++) {
++              EIP_DBG_PRINTF(EIP_DBG_GEN, "Removing %s from proc\n", eip_procs[p].name);
++              remove_proc_entry(eip_procs[p].name, *eip_procs[p].parent);
++      }
++}
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/eip/eip_stats.h
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/eip/eip_stats.h        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/eip/eip_stats.h     2005-06-01 23:12:54.555445792 -0400
+@@ -0,0 +1,22 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "$Id: eip_stats.h,v 1.14 2004/05/10 14:47:47 daniel Exp $"
++
++#ifndef __EIP_STATS_H
++#define       __EIP_STATS_H
++
++int eip_stats_init(void);
++void eip_stats_cleanup(void);
++void eip_rx_granularity_set(int);
++void eip_tx_copybreak_set(int);
++void eip_ipfrag_to_set(int);
++void eip_aggregation_set(int);
++void eip_ipfrag_copybreak_set(int);
++void eip_stats_dump(void);
++
++#endif                                /* __EIP_STATS_H */
+Index: linux-2.4.21/drivers/net/qsnet/eip/Makefile
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/eip/Makefile   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/eip/Makefile        2005-06-01 23:12:54.555445792 -0400
+@@ -0,0 +1,31 @@
++#
++# Makefile for Quadrics QsNet
++#
++# Copyright (c) 2002-2004 Quadrics Ltd
++#
++# File: drivers/net/qsnet/eip/Makefile
++#
++
++
++#
++
++#
++# Makefile for Quadrics QsNet
++#
++# Copyright (c) 2004 Quadrics Ltd.
++#
++# File: driver/net/qsnet/eip/Makefile
++#
++
++list-multi            := eip.o
++eip-objs      := eip_linux.o eip_stats.o
++export-objs           := 
++obj-$(CONFIG_EIP)     := eip.o
++
++eip.o : $(eip-objs)
++      $(LD) -r -o $@ $(eip-objs)
++
++EXTRA_CFLAGS          +=  -DDEBUG -DDEBUG_PRINTF -DDEBUG_ASSERT
++
++include $(TOPDIR)/Rules.make
++
+Index: linux-2.4.21/drivers/net/qsnet/eip/Makefile.conf
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/eip/Makefile.conf      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/eip/Makefile.conf   2005-06-01 23:12:54.555445792 -0400
+@@ -0,0 +1,10 @@
++# Flags for generating QsNet Linux Kernel Makefiles
++MODNAME               =       eip.o
++MODULENAME    =       eip
++KOBJFILES     =       eip_linux.o eip_stats.o
++EXPORT_KOBJS  =       
++CONFIG_NAME   =       CONFIG_EIP
++SGALFC                =       
++# EXTRALINES START
++
++# EXTRALINES END
+Index: linux-2.4.21/drivers/net/qsnet/eip/quadrics_version.h
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/eip/quadrics_version.h 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/eip/quadrics_version.h      2005-06-01 23:12:54.556445640 -0400
+@@ -0,0 +1 @@
++#define QUADRICS_VERSION "4.30qsnet"
+Index: linux-2.4.21/drivers/net/qsnet/elan/bitmap.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan/bitmap.c  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan/bitmap.c       2005-06-01 23:12:54.556445640 -0400
+@@ -0,0 +1,287 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: bitmap.c,v 1.5 2004/01/20 17:32:17 david Exp $"
++/*      $Source: /cvs/master/quadrics/elanmod/shared/bitmap.c,v $*/
++
++#if defined(__KERNEL__)
++#include <qsnet/kernel.h>
++#endif
++#include <qsnet/config.h>
++#include <elan/bitmap.h>
++
++/*
++ * Return the index of the first available bit in the 
++ * bitmap , or -1 for failure
++ */
++int
++bt_freebit (bitmap_t *bitmap, int nbits)
++{
++    int last = (--nbits) >> BT_ULSHIFT;
++    int maxbit;
++    int       i, j;
++
++    /* look for a word with a bit off */
++    for (i = 0; i <= last; i++)
++      if (bitmap[i] != ~((bitmap_t) 0))
++          break;
++
++    if (i <= last)
++    {
++      /* found an word with a bit off,  now see which bit it is */
++      maxbit = (i == last) ? (nbits & BT_ULMASK) : (BT_NBIPUL-1);
++      for (j = 0; j <= maxbit; j++)
++          if ((bitmap[i] & (1 << j)) == 0)
++              return ((i << BT_ULSHIFT) | j);
++    }
++    return (-1);
++    
++}
++
++/*
++ * bt_lowbit:
++ *     Return the index of the lowest set bit in the
++ *     bitmap, or -1 for failure.
++ */
++int
++bt_lowbit (bitmap_t *bitmap, int nbits)
++{
++    int last = (--nbits) >> BT_ULSHIFT;
++    int maxbit;
++    int i, j;
++    
++    /* look for a word with a bit on */
++    for (i = 0; i <= last; i++)
++      if (bitmap[i] != 0)
++          break;
++    if (i <= last)
++    {
++      /* found a word bit a bit on, now see which bit it is */
++      maxbit = (i == last) ? (nbits & BT_ULMASK) : (BT_NBIPUL-1);
++      for (j = 0; j <= maxbit; j++)
++          if (bitmap[i] & (1 << j))
++              return ((i << BT_ULSHIFT) | j);
++    }
++
++    return (-1);
++}
++
++/*
++ * Return the index of the first available bit in the 
++ * bitmap , or -1 for failure
++ */
++int
++bt_nextbit (bitmap_t *bitmap, int nbits, int last, int isset)
++{
++    int first = ((last+1) + BT_NBIPUL-1) >> BT_ULSHIFT;
++    int end   = (--nbits) >> BT_ULSHIFT;
++    int maxbit;
++    int       i, j;
++
++    /* look for bits before the first whole word */
++    if (((last+1) & BT_ULMASK) != 0)
++    {
++      maxbit = ((first-1) == last) ? (nbits & BT_ULMASK) : (BT_NBIPUL-1);
++      for (j = ((last+1) & BT_ULMASK); j <= maxbit; j++)
++          if ((bitmap[first-1] & (1 << j)) == (isset << j))
++              return (((first-1) << BT_ULSHIFT) | j);
++    }
++
++    /* look for a word with a bit off */
++    for (i = first; i <= end; i++)
++      if (bitmap[i] != (isset ? 0 : ~((bitmap_t) 0)))
++          break;
++
++    if (i <= end)
++    {
++      /* found an word with a bit off,  now see which bit it is */
++      maxbit = (i == end) ? (nbits & BT_ULMASK) : (BT_NBIPUL-1);
++      for (j = 0; j <= maxbit; j++)
++          if ((bitmap[i] & (1 << j)) == (isset << j))
++              return ((i << BT_ULSHIFT) | j);
++    }
++    return (-1);
++}
++
++void
++bt_copy (bitmap_t *a, bitmap_t *b, int nbits)
++{
++    int i;
++
++    for (i = 0; i < (nbits>>BT_ULSHIFT); i++)
++      b[i] = a[i];
++
++    for (i <<= BT_ULSHIFT; i < nbits; i++)
++      if (BT_TEST(a, i))
++          BT_SET(b,i);
++      else
++          BT_CLEAR(b,i);
++}
++
++void
++bt_zero (bitmap_t *bitmap, int nbits)
++{
++    int i;
++
++    for (i = 0; i < (nbits>>BT_ULSHIFT); i++)
++      bitmap[i] = 0;
++
++    for (i <<= BT_ULSHIFT; i < nbits; i++)
++      BT_CLEAR(bitmap,i);
++}
++
++void
++bt_fill (bitmap_t *bitmap, int nbits)
++{
++    int i;
++
++    for (i = 0; i < (nbits>>BT_ULSHIFT); i++)
++      bitmap[i] = ~((bitmap_t) 0);
++
++    for (i <<= BT_ULSHIFT; i < nbits; i++)
++      BT_SET(bitmap,i);
++}
++
++int
++bt_cmp (bitmap_t *a, bitmap_t *b, int nbits)
++{
++    int i;
++
++    for (i = 0; i < (nbits>>BT_ULSHIFT); i++)
++      if (a[i] != b[i])
++          return (1);
++
++    for (i <<= BT_ULSHIFT; i < nbits; i++)
++      if (BT_TEST (a, i) != BT_TEST(b, i))
++          return (1);
++    return (0);
++}
++
++void
++bt_intersect (bitmap_t *a, bitmap_t *b, int nbits)
++{
++    int i;
++    
++    for (i = 0; i < (nbits>>BT_ULSHIFT); i++)
++      a[i] &= b[i];
++
++    for (i <<= BT_ULSHIFT; i < nbits; i++)
++      if (BT_TEST (a, i) && BT_TEST (b, i))
++          BT_SET (a, i);
++      else
++          BT_CLEAR (a, i);
++}
++
++void
++bt_remove (bitmap_t *a, bitmap_t *b, int nbits)
++{
++    int i;
++
++    for (i = 0; i < (nbits>>BT_ULSHIFT); i++)
++      a[i] &= ~b[i];
++
++    for (i <<= BT_ULSHIFT; i < nbits; i++)
++      if (BT_TEST (b, i))
++          BT_CLEAR (a, i);
++}
++
++void
++bt_add (bitmap_t *a, bitmap_t *b, int nbits)
++{
++    int i;
++
++    for (i = 0; i < (nbits>>BT_ULSHIFT); i++)
++      a[i] |= b[i];
++
++    for (i <<= BT_ULSHIFT; i < nbits; i++)
++      if (BT_TEST(b, i))
++          BT_SET (a, i);
++}
++
++/*
++ * bt_spans : partition a spans partition b
++ *    == all bits set in 'b' are set in 'a'
++ */
++int
++bt_spans (bitmap_t *a, bitmap_t *b, int nbits)
++{
++    int i;
++    
++    for (i = 0; i < nbits; i++)
++      if (BT_TEST (b, i) && !BT_TEST (a, i))
++          return (0);
++    return (1);
++}
++
++/*
++ * bt_subset: copy [base,base+nbits-1] from 'a' to 'b'
++ */
++void
++bt_subset (bitmap_t *a, bitmap_t *b, int base, int nbits)
++{
++    int i;
++
++    for (i = 0; i < nbits; i++)
++    {
++      if (BT_TEST (a, base+i))
++          BT_SET(b,i);
++      else
++          BT_CLEAR (b,i);
++    }
++}
++
++void 
++bt_up (bitmap_t *a, bitmap_t *b, bitmap_t *c, int nbits)
++{
++    int i;
++    
++    for (i = 0; i < nbits; i++)
++    {
++      if (!BT_TEST (a, i) && BT_TEST (b, i))
++      {
++          BT_SET (c, i);
++        }
++      else
++      {
++          BT_CLEAR (c, i);
++        }
++    }
++}
++
++void 
++bt_down (bitmap_t *a, bitmap_t *b, bitmap_t *c, int nbits)
++{
++    int i;
++    
++    for (i = 0; i < nbits; i++)
++    {
++      if (BT_TEST (a, i) && !BT_TEST (b, i))
++      {
++          BT_SET (c, i);
++        }
++      else
++      {
++          BT_CLEAR (c, i);
++        }
++    }
++}
++
++int
++bt_nbits (bitmap_t *a, int nbits)
++{
++    int i, c;
++    for (i = 0, c = 0; i < nbits; i++)
++      if (BT_TEST (a, i))
++          c++;
++    return (c);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan/capability.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan/capability.c      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan/capability.c   2005-06-01 23:12:54.557445488 -0400
+@@ -0,0 +1,628 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: capability.c,v 1.13 2004/07/20 10:15:33 david Exp $"
++/*      $Source: /cvs/master/quadrics/elanmod/modsrc/capability.c,v $ */
++
++
++#include <qsnet/kernel.h>
++#include <elan/elanmod.h>
++
++static LIST_HEAD(elan_cap_list); 
++
++typedef struct elan_vp_struct
++{
++      struct list_head list;
++      ELAN_CAPABILITY  vp;
++} ELAN_VP_NODE_STRUCT;
++
++
++typedef struct elan_attached_struct
++{
++      void               *cb_args;
++      ELAN_DESTROY_CB  cb_func;
++} ELAN_ATTACHED_STRUCT;
++
++typedef struct elan_cap_node_struct
++{
++      struct list_head list;
++      ELAN_CAP_STRUCT     node;
++      ELAN_ATTACHED_STRUCT *attached[ELAN_MAX_RAILS];
++      struct list_head vp_list;
++} ELAN_CAP_NODE_STRUCT;
++
++
++ELAN_CAP_NODE_STRUCT *
++find_cap_node(ELAN_CAPABILITY *cap)
++{
++      struct list_head        *tmp;
++      ELAN_CAP_NODE_STRUCT *ptr=NULL;
++
++      list_for_each(tmp, &elan_cap_list) {
++              ptr = list_entry(tmp, ELAN_CAP_NODE_STRUCT , list);
++              /* is it an exact match */
++              if ( ELAN_CAP_TYPE_MATCH(&ptr->node.cap,cap) 
++                   && ELAN_CAP_GEOM_MATCH(&ptr->node.cap,cap)) {
++                      return ptr;
++              }
++      }
++      return ptr;
++};
++
++ELAN_VP_NODE_STRUCT *
++find_vp_node( ELAN_CAP_NODE_STRUCT *cap_node,ELAN_CAPABILITY *map)
++{
++      struct list_head       * tmp;
++      ELAN_VP_NODE_STRUCT * ptr = NULL;
++
++      list_for_each(tmp, &cap_node->vp_list) {
++              ptr = list_entry(tmp, ELAN_VP_NODE_STRUCT , list);
++              /* is it an exact match */
++              if ( ELAN_CAP_TYPE_MATCH(&ptr->vp,map) 
++                   && ELAN_CAP_GEOM_MATCH(&ptr->vp,map)){
++                      return ptr;
++              }
++      }
++      return ptr;
++}
++
++int 
++elan_validate_cap(ELAN_CAPABILITY *cap)
++{
++      char                      space[127];
++
++      ELAN_DEBUG1 (ELAN_DBG_VP,"elan_validate_cap %s\n",elan_capability_string(cap,space));
++
++      /* check versions */
++      if (cap->cap_version != ELAN_CAP_VERSION_NUMBER)
++      {
++              ELAN_DEBUG2 (ELAN_DBG_VP,"elan_validate_cap: (cap->Version != ELAN_CAP_VERSION) %d %d\n", cap->cap_version, ELAN_CAP_VERSION_NUMBER);
++              return (EINVAL);
++      }
++
++      /* check its not HWTEST */
++      if ( cap->cap_type & ELAN_CAP_TYPE_HWTEST )
++      {
++              ELAN_DEBUG0 (ELAN_DBG_VP,"elan_validate_cap: failed type = ELAN_CAP_TYPE_HWTEST \n");   
++              return (EINVAL);
++      }
++      
++      /* check its type */
++      switch (cap->cap_type & ELAN_CAP_TYPE_MASK)
++      {
++      case ELAN_CAP_TYPE_KERNEL :     
++              ELAN_DEBUG0 (ELAN_DBG_VP,"elan_validate_cap: failed type = ELAN_CAP_TYPE_KERNEL \n");   
++              return (EINVAL);
++
++              /* check it has a valid type */
++      case ELAN_CAP_TYPE_BLOCK:
++      case ELAN_CAP_TYPE_CYCLIC:
++              break;
++
++              /* all others are failed as well */
++      default:
++              ELAN_DEBUG1 (ELAN_DBG_VP,"elan_validate_cap: failed unknown type = %x \n", (cap->cap_type & ELAN_CAP_TYPE_MASK));       
++              return (EINVAL);
++      }
++      
++      if ((cap->cap_lowcontext == ELAN_CAP_UNINITIALISED) || (cap->cap_highcontext == ELAN_CAP_UNINITIALISED)
++          || (cap->cap_lownode == ELAN_CAP_UNINITIALISED) || (cap->cap_highnode    == ELAN_CAP_UNINITIALISED))
++      {
++              
++              ELAN_DEBUG4 (ELAN_DBG_VP,"elan_validate_cap: ELAN_CAP_UNINITIALISED   LowNode %d   HighNode %d   LowContext %d   highContext %d\n",
++                           cap->cap_lownode , cap->cap_highnode,
++                           cap->cap_lowcontext , cap->cap_highcontext);
++              return (EINVAL);
++      }       
++
++      if (cap->cap_lowcontext > cap->cap_highcontext)
++      {
++              ELAN_DEBUG2 (ELAN_DBG_VP,"elan_validate_cap: (cap->cap_lowcontext > cap->cap_highcontext) %d %d\n",cap->cap_lowcontext , cap->cap_highcontext);
++              return (EINVAL);
++      }
++      
++      if (cap->cap_lownode > cap->cap_highnode)
++      {
++              ELAN_DEBUG2 (ELAN_DBG_VP,"elan_validate_cap: (cap->cap_lownode > cap->cap_highnode) %d %d\n",cap->cap_lownode, cap->cap_highnode);
++              return (EINVAL);
++      }
++
++      if (cap->cap_mycontext != ELAN_CAP_UNINITIALISED) 
++      {
++              ELAN_DEBUG1 (ELAN_DBG_VP,"elan_validate_cap: failed cap->cap_mycontext is set %d  \n", cap->cap_mycontext);
++              return (EINVAL);
++      }
++
++
++      if ((ELAN_CAP_NUM_NODES(cap) * ELAN_CAP_NUM_CONTEXTS(cap)) > ELAN_MAX_VPS)
++      {
++              ELAN_DEBUG6 (ELAN_DBG_VP,"elan_validate_cap: too many vps  LowNode %d   HighNode %d   LowContext %d   highContext %d,  %d >% d\n",
++                           cap->cap_lownode , cap->cap_highnode,
++                           cap->cap_lowcontext , cap->cap_highcontext,
++                           (ELAN_CAP_NUM_NODES(cap) * ELAN_CAP_NUM_CONTEXTS(cap)),
++                           ELAN_MAX_VPS);
++              
++              return (EINVAL);
++      }
++
++      return (ESUCCESS);
++}
++
++int
++elan_validate_map(ELAN_CAPABILITY *cap, ELAN_CAPABILITY *map)
++{
++      ELAN_CAP_NODE_STRUCT * ptr  = NULL;
++      ELAN_VP_NODE_STRUCT  * vptr = NULL;
++      char space[256];
++
++      kmutex_lock(&elan_mutex);
++
++      ELAN_DEBUG0 (ELAN_DBG_VP,"elan_validate_map \n");
++      ELAN_DEBUG1 (ELAN_DBG_VP,"elan_validate_map cap = %s \n",elan_capability_string(cap,space));
++      ELAN_DEBUG1 (ELAN_DBG_VP,"elan_validate_map map = %s \n",elan_capability_string(map,space));
++
++      /* does cap exist    */
++      ptr = find_cap_node(cap);
++      if ( ptr == NULL ) 
++      {
++              ELAN_DEBUG0 (ELAN_DBG_VP,"elan_validate_map: cap not found \n");
++              kmutex_unlock(&elan_mutex);
++              return EINVAL;
++      }
++      /* is it active */
++      if ( ! ptr->node.active ) 
++      {
++              ELAN_DEBUG0 (ELAN_DBG_VP,"elan_validate_map: cap not active \n");
++              kmutex_unlock(&elan_mutex);
++              return EINVAL;
++      }
++
++      /* are they the same */
++      if ( ELAN_CAP_TYPE_MATCH(cap,map) 
++           && ELAN_CAP_GEOM_MATCH(cap,map)) 
++      {
++              ELAN_DEBUG0 (ELAN_DBG_VP,"elan_validate_map: cap == map  passed\n");
++              kmutex_unlock(&elan_mutex);
++              return ESUCCESS;
++      }
++
++      /* is map in map list */
++      vptr = find_vp_node(ptr, map);
++      if ( vptr == NULL ) 
++      {
++              ELAN_DEBUG0 (ELAN_DBG_VP,"elan_validate_map:  map not found\n");
++              kmutex_unlock(&elan_mutex);
++              return EINVAL;
++      }
++      
++      ELAN_DEBUG0 (ELAN_DBG_VP,"elan_validate_map:  map passed\n");
++      kmutex_unlock(&elan_mutex);
++      return ESUCCESS;
++}
++
++int
++elan_create_cap(ELAN_CAP_OWNER owner, ELAN_CAPABILITY *cap)
++{
++      char                      space[127];
++      struct list_head        * tmp;
++      ELAN_CAP_NODE_STRUCT * ptr = NULL;
++      int                       i, rail;
++
++      kmutex_lock(&elan_mutex);
++
++      ELAN_DEBUG1 (ELAN_DBG_VP,"elan_create_cap %s\n",elan_capability_string(cap,space));     
++
++      /* need to check that the cap does not over lap another one 
++         or is an exact match with only the userkey changing */
++      list_for_each(tmp, &elan_cap_list) {
++              ptr = list_entry(tmp, ELAN_CAP_NODE_STRUCT , list);
++
++              /* is it an exact match */
++              if ( ELAN_CAP_TYPE_MATCH(&ptr->node.cap,cap) 
++                   && ELAN_CAP_GEOM_MATCH(&ptr->node.cap,cap)
++                   && (&ptr->node.owner == owner)) {
++                      if ( ptr->node.active ) {
++                              /* dont inc attached count as its like a create */
++                              ptr->node.cap.cap_userkey = cap->cap_userkey;
++                              kmutex_unlock(&elan_mutex);
++                              return ESUCCESS;
++                      }
++                      else
++                      {
++                              kmutex_unlock(&elan_mutex);
++                              return EINVAL;
++                      }
++              }
++              
++              /* does it overlap, even with ones being destroyed */
++              if (elan_cap_overlap(&ptr->node.cap,cap))
++              {
++                      kmutex_unlock(&elan_mutex);
++                      return  EACCES;
++              }
++      }
++
++      /* create it */
++      KMEM_ALLOC(ptr, ELAN_CAP_NODE_STRUCT *, sizeof(ELAN_CAP_NODE_STRUCT), 1);
++      if (ptr == NULL)
++      {
++              kmutex_unlock(&elan_mutex);
++              return  ENOMEM;
++      }
++
++      /* create space for the attached array */
++      for(rail=0;rail<ELAN_MAX_RAILS;rail++)
++      {
++              ptr->attached[rail]=NULL;
++              if ( ELAN_CAP_IS_RAIL_SET(cap,rail) ) 
++              {
++                      KMEM_ALLOC(ptr->attached[rail], ELAN_ATTACHED_STRUCT *, sizeof(ELAN_ATTACHED_STRUCT) *  ELAN_CAP_NUM_CONTEXTS(cap), 1);
++                      if (ptr->attached[rail] == NULL) 
++                      {
++                              for(;rail>=0;rail--)
++                                      if ( ptr->attached[rail] )
++                                              KMEM_FREE(ptr->attached[rail], sizeof(ELAN_ATTACHED_STRUCT) *  ELAN_CAP_NUM_CONTEXTS(cap));
++
++                              KMEM_FREE(ptr, sizeof(ELAN_CAP_NODE_STRUCT));
++                              kmutex_unlock(&elan_mutex);
++                              return  ENOMEM;
++                      }
++                      /* blank the attached array */
++                      for(i=0;i<ELAN_CAP_NUM_CONTEXTS(cap);i++)
++                              ptr->attached[rail][i].cb_func = NULL;
++              }
++      }       
++      
++      ptr->node.owner     = owner;
++      ptr->node.cap       = *cap;
++      ptr->node.attached  = 1;    /* creator counts as attached */
++      ptr->node.active    = 1;
++      ptr->vp_list.next   = &(ptr->vp_list);
++      ptr->vp_list.prev   = &(ptr->vp_list);
++
++      list_add_tail(&ptr->list, &elan_cap_list);      
++
++      kmutex_unlock(&elan_mutex);
++      return  ESUCCESS;
++}
++
++void
++elan_destroy_cap_test(ELAN_CAP_NODE_STRUCT *cap_ptr)
++{
++      /* called by someone holding the mutex   */
++      struct list_head       * vp_tmp;
++      ELAN_VP_NODE_STRUCT * vp_ptr = NULL;
++      int                      rail;
++
++      /* check to see if it can be deleted now */
++      if ( cap_ptr->node.attached == 0 ) {
++              
++              ELAN_DEBUG0(ELAN_DBG_CAP,"elan_destroy_cap_test: attached == 0\n");     
++              
++              /* delete the vp list */
++              list_for_each(vp_tmp, &(cap_ptr->vp_list)) {
++                      vp_ptr = list_entry(vp_tmp, ELAN_VP_NODE_STRUCT , list);
++                      list_del(&vp_ptr->list);
++                      KMEM_FREE( vp_ptr, sizeof(ELAN_VP_NODE_STRUCT));
++              }
++              
++              list_del(&cap_ptr->list);
++
++              /* delete space for the attached array */
++              for(rail=0;rail<ELAN_MAX_RAILS;rail++)
++                      if (cap_ptr->attached[rail]) 
++                              KMEM_FREE(cap_ptr->attached[rail], sizeof(ELAN_ATTACHED_STRUCT) * ELAN_CAP_NUM_CONTEXTS(&(cap_ptr->node.cap)));
++                      
++              KMEM_FREE(cap_ptr, sizeof(ELAN_CAP_NODE_STRUCT));               
++      }
++}
++
++int
++elan_destroy_cap(ELAN_CAP_OWNER owner, ELAN_CAPABILITY *cap)
++{
++      char                      space[127];
++      struct list_head        * el;
++      struct list_head        * nel;
++      ELAN_CAP_NODE_STRUCT * ptr = NULL;
++      int                       i, rail;
++      int                       found = 0;
++
++      kmutex_lock(&elan_mutex);
++
++      ELAN_DEBUG1 (ELAN_DBG_CAP,"elan_destroy_cap %s\n",elan_capability_string(cap,space));   
++
++      list_for_each_safe (el, nel, &elan_cap_list) {
++              ptr = list_entry(el, ELAN_CAP_NODE_STRUCT , list);
++              
++              /* is it an exact match */
++              if ( (ptr->node.owner == owner )
++                   && (  (cap == NULL) 
++                         || (ELAN_CAP_TYPE_MATCH(&ptr->node.cap,cap) && ELAN_CAP_GEOM_MATCH(&ptr->node.cap,cap)))) {
++
++                      if ( ptr->node.active ) {
++
++                              /* mark as in active and dec attached count */
++                              ptr->node.active = 0;
++                              ptr->node.attached--;
++                              ptr->node.owner  = 0; /* no one own's it now */
++                              
++                              /* need to tell any one who was attached that this has been destroy'd */
++                              for(rail=0;rail<ELAN_MAX_RAILS;rail++)
++                                      if (ELAN_CAP_IS_RAIL_SET( &(ptr->node.cap), rail)) {
++                                              for(i=0;i< ELAN_CAP_NUM_CONTEXTS(&(ptr->node.cap));i++)
++                                                      if ( ptr->attached[rail][i].cb_func != NULL) 
++                                                              ptr->attached[rail][i].cb_func(ptr->attached[rail][i].cb_args, cap, NULL);
++                                      }
++                              
++                              /* now try to destroy it */
++                              elan_destroy_cap_test(ptr);
++                              
++                              /* found it */
++                              found = 1;
++                      }
++              }
++      }
++      
++      if ( found )
++      {
++              kmutex_unlock(&elan_mutex);
++              return ESUCCESS;
++      }
++
++      /* failed */
++      ELAN_DEBUG0(ELAN_DBG_CAP,"elan_destroy_cap: didnt find it \n"); 
++
++      kmutex_unlock(&elan_mutex);
++      return EINVAL;
++}
++
++int 
++elan_get_caps(uint *number_of_results, uint array_size, ELAN_CAP_STRUCT *caps)
++{
++      uint                      results = 0;
++      struct list_head        * tmp;
++      ELAN_CAP_NODE_STRUCT * ptr = NULL;
++      
++
++      kmutex_lock(&elan_mutex);
++
++      ELAN_DEBUG0(ELAN_DBG_CAP,"elan_get_caps\n");    
++
++      list_for_each(tmp, &elan_cap_list) {
++              ptr = list_entry(tmp, ELAN_CAP_NODE_STRUCT , list);
++              
++              copyout(&ptr->node, &caps[results], sizeof (ELAN_CAP_STRUCT));
++              
++              results++;
++              
++              if ( results >= array_size )
++              {
++                      copyout(&results, number_of_results, sizeof(uint));     
++                      kmutex_unlock(&elan_mutex);
++                      return ESUCCESS;
++              }
++      }
++
++      copyout(&results, number_of_results, sizeof(uint));     
++
++      kmutex_unlock(&elan_mutex);
++      return ESUCCESS;
++}
++
++int
++elan_create_vp(ELAN_CAP_OWNER owner, ELAN_CAPABILITY *cap, ELAN_CAPABILITY *map)
++{
++      ELAN_CAP_NODE_STRUCT * cap_ptr = NULL;
++      ELAN_VP_NODE_STRUCT  * vp_ptr  = NULL;
++      
++      kmutex_lock(&elan_mutex);
++
++
++      ELAN_DEBUG0(ELAN_DBG_CAP,"elan_create_vp\n");
++
++      /* the railmasks must match */
++      if ( cap->cap_railmask != map->cap_railmask)
++      {
++              kmutex_unlock(&elan_mutex);
++              return  EINVAL;
++      }
++
++      /* does the cap exist */
++      cap_ptr = find_cap_node(cap);
++      if ((cap_ptr == NULL) || ( cap_ptr->node.owner != owner ) || (! cap_ptr->node.active) )
++      {
++              kmutex_unlock(&elan_mutex);
++              return  EINVAL;
++      }
++      
++      /* is there already a mapping */
++      vp_ptr = find_vp_node(cap_ptr,map);
++      if ( vp_ptr != NULL) 
++      {
++              kmutex_unlock(&elan_mutex);
++              return  EINVAL;
++      }
++
++      /* create space for mapping */
++      KMEM_ALLOC(vp_ptr, ELAN_VP_NODE_STRUCT *, sizeof(ELAN_VP_NODE_STRUCT), 1);
++      if (vp_ptr == NULL)
++      {
++              kmutex_unlock(&elan_mutex);
++              return  ENOMEM;
++      }
++                      
++      /* copy map */
++      vp_ptr->vp = *map;
++      list_add_tail(&vp_ptr->list, &(cap_ptr->vp_list));      
++      kmutex_unlock(&elan_mutex);
++      return  ESUCCESS;
++}
++
++int
++elan_destroy_vp(ELAN_CAP_OWNER owner, ELAN_CAPABILITY *cap, ELAN_CAPABILITY *map)
++{
++      ELAN_CAP_NODE_STRUCT * cap_ptr = NULL;
++      ELAN_VP_NODE_STRUCT  * vp_ptr  = NULL;
++      int                       i, rail;
++
++      kmutex_lock(&elan_mutex);
++
++      ELAN_DEBUG0(ELAN_DBG_CAP,"elan_destroy_vp\n");  
++
++      cap_ptr = find_cap_node(cap);
++      if ((cap_ptr!=NULL) && (cap_ptr->node.owner == owner) && ( cap_ptr->node.active))
++      {               
++              vp_ptr = find_vp_node( cap_ptr, map );
++              if ( vp_ptr != NULL ) 
++              {
++                      list_del(&vp_ptr->list);
++                      KMEM_FREE(vp_ptr, sizeof(ELAN_VP_NODE_STRUCT));
++            
++                      /* need to tell those who are attached that map is nolonger in use */
++                      for(rail=0;rail<ELAN_MAX_RAILS;rail++)
++                              if (ELAN_CAP_IS_RAIL_SET(cap, rail))
++                              {
++                                      for(i=0;i< ELAN_CAP_NUM_CONTEXTS(&(cap_ptr->node.cap));i++)
++                                              if ( cap_ptr->attached[rail][i].cb_func != NULL) 
++                                                      cap_ptr->attached[rail][i].cb_func( cap_ptr->attached[rail][i].cb_args, cap, map);
++                              }
++
++                      kmutex_unlock(&elan_mutex);
++                      return  ESUCCESS;
++              }
++      }       
++      
++      /* didnt find it */
++      kmutex_unlock(&elan_mutex);
++      return  EINVAL;
++}
++
++int 
++elan_attach_cap(ELAN_CAPABILITY *cap, unsigned int rail, void *args, ELAN_DESTROY_CB func)
++{
++      char                  space[127];
++      struct list_head     *el;
++
++      ELAN_DEBUG1 (ELAN_DBG_CAP,"elan_attach_cap %s\n",elan_capability_string(cap,space));
++
++      /* currently must provide a call back, as null mean something */
++      if ( func == NULL)
++              return (EINVAL);
++
++      /* mycontext must be set and correct */
++      if ( ! ELAN_CAP_VALID_MYCONTEXT(cap))
++              return (EINVAL);
++
++      /* rail must be one of the rails in railmask */
++      if (((1 << rail) & cap->cap_railmask) == 0)
++              return (EINVAL);
++      
++      kmutex_lock(&elan_mutex);
++
++      list_for_each(el, &elan_cap_list) {
++              ELAN_CAP_NODE_STRUCT *cap_ptr = list_entry(el, ELAN_CAP_NODE_STRUCT , list);
++              
++              /* is it an exact match */
++              if (ELAN_CAP_MATCH(&cap_ptr->node.cap,cap) && cap_ptr->node.active) {
++                      unsigned int attached_index = cap->cap_mycontext - cap->cap_lowcontext;
++                      
++                      if ( cap_ptr->attached[rail][attached_index].cb_func != NULL ) /* only one per ctx per rail */
++                      {
++                              kmutex_unlock(&elan_mutex);
++                              return   EINVAL;
++                      }
++
++                      /* keep track of who attached as we might need to tell them when */
++                      /* cap or maps get destroyed                                     */
++                      cap_ptr->attached[rail][ attached_index ].cb_func = func;
++                      cap_ptr->attached[rail][ attached_index ].cb_args = args;
++                      cap_ptr->node.attached++;
++
++                      ELAN_DEBUG0(ELAN_DBG_CAP,"elan_attach_cap: passed\n");
++                      kmutex_unlock(&elan_mutex);
++                      return ESUCCESS;
++              }
++      }
++      
++      ELAN_DEBUG0(ELAN_DBG_CAP,"elan_attach_cap: failed to find \n");
++
++      /* didnt find one */
++      kmutex_unlock(&elan_mutex);
++      return EINVAL;
++}
++
++int 
++elan_detach_cap(ELAN_CAPABILITY *cap, unsigned int rail)
++{
++      struct list_head *el, *nel;
++      char              space[256];
++
++      kmutex_lock(&elan_mutex);
++
++      ELAN_DEBUG1(ELAN_DBG_CAP,"elan_detach_cap %s\n",elan_capability_string(cap,space));
++      list_for_each_safe (el, nel, &elan_cap_list) {
++              ELAN_CAP_NODE_STRUCT *ptr = list_entry (el, ELAN_CAP_NODE_STRUCT, list);
++
++              /* is it an exact match */
++              if (ELAN_CAP_TYPE_MATCH(&ptr->node.cap,cap) &&
++                  ELAN_CAP_GEOM_MATCH(&ptr->node.cap,cap) &&
++                  (ptr->node.cap.cap_railmask & cap->cap_railmask) == cap->cap_railmask) {
++              
++                      unsigned int attached_index = cap->cap_mycontext - cap->cap_lowcontext;
++
++                      if ( ptr->attached[rail][ attached_index ].cb_func == NULL ) 
++                              ELAN_DEBUG0(ELAN_DBG_CAP,"elanmod_detach_cap already removed \n");
++
++                      ptr->attached[rail][ attached_index ].cb_func = NULL;
++                      ptr->attached[rail][ attached_index ].cb_args = (void *)0;
++
++                      ptr->node.attached--;
++
++                      ELAN_DEBUG1(ELAN_DBG_CAP,"elanmod_detach_cap new attach count%d \n", ptr->node.attached);
++
++                      elan_destroy_cap_test(ptr);
++
++                      ELAN_DEBUG0(ELAN_DBG_CAP,"elan_detach_cap: success\n"); 
++
++                      kmutex_unlock(&elan_mutex);
++                      return  ESUCCESS;
++              }
++      }
++
++      ELAN_DEBUG0(ELAN_DBG_CAP,"elan_detach_cap: failed to find\n");
++      kmutex_unlock(&elan_mutex);
++      return  EINVAL;
++}
++
++int
++elan_cap_dump()
++{
++      struct list_head        * tmp;
++      ELAN_CAP_NODE_STRUCT * ptr = NULL;
++      
++      kmutex_lock(&elan_mutex);       
++      
++      list_for_each(tmp, &elan_cap_list) {
++              ptr = list_entry(tmp, ELAN_CAP_NODE_STRUCT , list);
++
++              ELAN_DEBUG2 (ELAN_DBG_ALL, "cap dump: owner %p type %x\n", ptr->node.owner, ptr->node.cap.cap_type);
++                      
++              ELAN_DEBUG5 (ELAN_DBG_ALL, "cap dump: LowNode %d   HighNode %d   LowContext %d   mycontext %d   highContext %d\n",
++                           ptr->node.cap.cap_lownode , ptr->node.cap.cap_highnode,
++                           ptr->node.cap.cap_lowcontext , ptr->node.cap.cap_mycontext, ptr->node.cap.cap_highcontext);
++
++      }
++
++      kmutex_unlock(&elan_mutex);
++      return  ESUCCESS;
++}
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan/capability_general.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan/capability_general.c      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan/capability_general.c   2005-06-01 23:12:54.558445336 -0400
+@@ -0,0 +1,446 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: capability_general.c,v 1.10 2004/02/25 13:47:59 daniel Exp $"
++/*      $Source: /cvs/master/quadrics/elanmod/shared/capability_general.c,v $ */
++
++#if defined(__KERNEL__)
++
++#include <qsnet/kernel.h>
++
++#else
++
++#include <stdlib.h>
++#include <stdio.h>
++#include <sys/param.h>
++
++#endif
++
++#include <elan/elanmod.h>
++
++
++void
++elan_nullcap (ELAN_CAPABILITY *cap)
++{
++      register int i;
++
++      for (i = 0; i < sizeof (cap->cap_userkey)/sizeof(cap->cap_userkey.key_values[0]); i++)
++              cap->cap_userkey.key_values[i] = ELAN_CAP_UNINITIALISED;
++    
++      cap->cap_lowcontext  = ELAN_CAP_UNINITIALISED;
++      cap->cap_highcontext = ELAN_CAP_UNINITIALISED;
++      cap->cap_mycontext   = ELAN_CAP_UNINITIALISED;
++      cap->cap_lownode     = ELAN_CAP_UNINITIALISED;
++      cap->cap_highnode    = ELAN_CAP_UNINITIALISED;
++      cap->cap_railmask    = ELAN_CAP_UNINITIALISED;
++      cap->cap_type        = ELAN_CAP_UNINITIALISED;
++      cap->cap_spare       = 0;
++      cap->cap_version     = ELAN_CAP_VERSION_NUMBER;
++      
++      for (i = 0; i < sizeof (cap->cap_bitmap)/sizeof (cap->cap_bitmap[0]); i++)
++              cap->cap_bitmap[i] = 0;
++}
++
++char *
++elan_capability_string (ELAN_CAPABILITY *cap, char *str)
++{
++      if (cap == NULL) 
++              sprintf (str, "[-.-.-.-] cap = NULL\n");
++      else
++              sprintf (str, "[%x.%x.%x.%x] Version %x Type %x \n"
++                       "Context %x.%x.%x Node %x.%x\n",
++                       cap->cap_userkey.key_values[0], cap->cap_userkey.key_values[1],
++                       cap->cap_userkey.key_values[2], cap->cap_userkey.key_values[3],
++                       cap->cap_version, cap->cap_type, 
++                       cap->cap_lowcontext, cap->cap_mycontext, cap->cap_highcontext,
++                       cap->cap_lownode, cap->cap_highnode);
++      
++      return (str);
++}
++
++ELAN_LOCATION
++elan_vp2location (u_int process, ELAN_CAPABILITY *cap)
++{
++      ELAN_LOCATION location;
++      int i, vp, node, context, nnodes, nctxs;
++
++      vp = 0;
++
++      location.loc_node    = ELAN_INVALID_NODE;
++      location.loc_context = -1;
++       
++      nnodes = cap->cap_highnode - cap->cap_lownode + 1;
++      nctxs  = cap->cap_highcontext - cap->cap_lowcontext + 1;
++       
++      switch (cap->cap_type & ELAN_CAP_TYPE_MASK)
++      {
++      case ELAN_CAP_TYPE_BLOCK:
++              for (node = 0, i = 0; node < nnodes; node++)
++              {
++                      for (context = 0; context < nctxs; context++)
++                      {
++                              if ((cap->cap_type & ELAN_CAP_TYPE_NO_BITMAP) || BT_TEST (cap->cap_bitmap, context + (node * nctxs)))
++                              {
++                                      if (vp == process)
++                                      {
++                                              /* Return relative indices within the capability box */
++                                              location.loc_node    = node;
++                                              location.loc_context = context;
++
++                                              return (location);
++                                      }
++                     
++                                      vp++;
++                              }
++                      }
++              }
++              break;
++      
++      case ELAN_CAP_TYPE_CYCLIC:
++              for (context = 0, i = 0; context < nctxs; context++)
++              {
++                      for (node = 0; node < nnodes; node++)
++                      {
++                              if ((cap->cap_type & ELAN_CAP_TYPE_NO_BITMAP) || BT_TEST (cap->cap_bitmap, node + (context * nnodes)))
++                              {
++                                      if (vp == process)
++                                      {
++                                              location.loc_node    = node;
++                                              location.loc_context = context;
++
++                                              return (location);
++                                      }
++                  
++                                      vp++;
++                              }
++                      }
++              }
++              break;
++      }
++    
++      return( location );
++}
++
++int
++elan_location2vp (ELAN_LOCATION location, ELAN_CAPABILITY *cap)
++{
++    int  vp, node, context, nnodes, nctxs;
++
++    nnodes = cap->cap_highnode - cap->cap_lownode + 1;
++    nctxs  = cap->cap_highcontext - cap->cap_lowcontext + 1;
++
++    vp = 0;
++    
++    switch (cap->cap_type & ELAN_CAP_TYPE_MASK)
++    {
++    case ELAN_CAP_TYPE_BLOCK:
++      for (node = 0 ; node < nnodes ; node++)
++      {
++          for (context = 0; context < nctxs; context++)
++          {
++              if ((cap->cap_type & ELAN_CAP_TYPE_NO_BITMAP) || BT_TEST (cap->cap_bitmap, context + (node * nctxs)))
++              {
++                  if ((location.loc_node == node) && (location.loc_context == context))
++                  {
++                      /* Found it ! */
++                      return( vp );
++                  }
++                  
++                  vp++;
++              }
++          }
++      }
++      break;
++      
++    case ELAN_CAP_TYPE_CYCLIC:
++      for (context = 0; context < nctxs; context++)
++      {
++          for (node = 0; node < nnodes; node++)
++          {
++              if ((cap->cap_type & ELAN_CAP_TYPE_NO_BITMAP) || BT_TEST (cap->cap_bitmap, node + (context * nnodes)))
++              {
++                  if ((location.loc_node == node) && (location.loc_context == context))
++                  {
++                      /* Found it ! */
++                      return( vp );
++                  }
++                  
++                  vp++;
++              }
++          }
++      }
++      break;
++    }
++    
++    /* Failed to find it */
++    return( -1 );
++}
++
++/* Return the number of processes as described by a capability */
++int
++elan_nvps (ELAN_CAPABILITY *cap)
++{
++      int i, c, nbits = ELAN_CAP_BITMAPSIZE(cap);
++
++      if (cap->cap_type & ELAN_CAP_TYPE_NO_BITMAP)
++              return (nbits);
++
++      for (i = 0, c = 0; i < nbits; i++)
++              if (BT_TEST (cap->cap_bitmap, i))
++                      c++;
++
++      return (c);
++}
++
++/* Return the number of local processes on a given node as described by a capability */
++int
++elan_nlocal (int node, ELAN_CAPABILITY *cap)
++{
++      int vp;
++      ELAN_LOCATION loc;
++      int nLocal = 0;
++
++      for (vp = 0; vp < elan_nvps(cap); vp++)
++      {
++              loc = elan_vp2location(vp, cap);
++              if (loc.loc_node == node)
++                      nLocal++;
++      }
++
++      return (nLocal);
++}
++
++/* Return the maximum number of local processes on any node as described by a capability */
++int
++elan_maxlocal (ELAN_CAPABILITY *cap)
++{
++      return(cap->cap_highcontext - cap->cap_lowcontext + 1);
++}
++
++/* Return the vps of the local processes on a given node as described by a capability */
++int
++elan_localvps (int node, ELAN_CAPABILITY *cap, int *vps, int size)
++{
++      int context;
++      ELAN_LOCATION loc;
++      int nLocal = 0;
++    
++      loc.loc_node = node;
++
++      for (context = 0; context < MIN(size, elan_maxlocal(cap)); context++)
++      {
++              loc.loc_context = context;
++      
++              /* Should return -1 if none found */
++              if ( (vps[context] = elan_location2vp( loc, cap )) != -1)
++                      nLocal++;
++      }
++
++      return (nLocal);
++}
++
++/* Return the number of rails that this capability utilises */
++int
++elan_nrails (ELAN_CAPABILITY *cap)
++{
++      int nrails = 0;
++      unsigned int railmask;
++
++      /* Test for a multi-rail capability */
++      if (cap->cap_type & ELAN_CAP_TYPE_MULTI_RAIL)
++      {
++              /* Grab rail bitmask from capability */
++              railmask = cap->cap_railmask;
++      
++              while (railmask)
++              {
++                      if (railmask & 1)
++                              nrails++;
++          
++                      railmask >>= 1;
++              }
++      }
++      else 
++              /* Default to just one rail */
++              nrails = 1;
++      
++      return (nrails);
++}
++
++/* Fill out an array giving the physical rail numbers utilised by a capability */
++int
++elan_rails (ELAN_CAPABILITY *cap, int *rails)
++{
++      int nrails, rail;
++      unsigned int railmask;
++
++      /* Test for a multi-rail capability */
++      if (cap->cap_type & ELAN_CAP_TYPE_MULTI_RAIL)
++      {
++              /* Grab rail bitmask from capability */
++              railmask = cap->cap_railmask;
++      
++              nrails = rail = 0;
++              while (railmask)
++              {
++                      if (railmask & 1)
++                              rails[nrails++] = rail;
++          
++                      rail++;
++                      railmask >>= 1;
++              }
++      }
++      else
++      {
++              /* Default to just one rail */
++              rails[0] = 0;
++              nrails = 1;
++      }
++
++      return( nrails );
++}
++
++int 
++elan_cap_overlap(ELAN_CAPABILITY *cap1, ELAN_CAPABILITY *cap2)
++{
++      /* by context */
++      if ( cap1->cap_highcontext < cap2->cap_lowcontext ) return (0);
++      if ( cap1->cap_lowcontext  > cap2->cap_highcontext) return (0);
++      
++      /* by node */
++      if ( cap1->cap_highnode < cap2->cap_lownode ) return (0);
++      if ( cap1->cap_lownode  > cap2->cap_highnode) return (0);
++
++      /* by rail */
++      /* they overlap if they have a rail in common */
++      return (cap1->cap_railmask & cap2->cap_railmask);
++}
++
++#if !defined(__KERNEL__)
++
++/* Fill out an array that hints at the best use of the rails on a
++ * per process basis. The library user can then decide whether or not
++ * to take this into account (e.g. TPORTs)
++ * All processes calling this fn will be returned the same information.
++ */
++int
++elan_prefrails(ELAN_CAPABILITY *cap, int *pref, int nvp)
++{
++      int i;
++      int nrails = elan_nrails(cap);
++      int maxlocal = elan_maxlocal(cap);
++
++      /* Test for a multi-rail capability */
++      if (! (cap->cap_type & ELAN_CAP_TYPE_MULTI_RAIL))
++      {
++              /* Default to just one rail */
++              for (i = 0; i < nvp; i++)
++                      pref[i] = 0;
++
++              return( 0 );
++      }
++
++      /*
++       * We allocate rails on a per node basis sharing our the rails
++       * equally amongst the local processes. However, if there is only
++       * one process per node and multiple rails, then we use a different
++       * algorithm where rails are allocated across all the processes in 
++       * a round-robin fashion
++       */
++    
++      if (maxlocal == 1)
++      {
++              /* Allocate rails in a round-robin manner */
++              for (i = 0; i < nvp; i++)
++                      *pref++ = i % nrails;
++      }
++      else
++      {
++              int node;
++              int *vps;
++              int nnodes = cap->cap_highnode - cap->cap_lownode + 1;
++
++              vps = (int *) malloc(sizeof(int)*maxlocal);
++
++              /* Grab the local process info for each node and allocate
++               * rails to those vps on an equal basis
++               */
++              for (node = 0; node < nnodes; node++)
++              {
++                      int nlocal;
++                      int pprail;
++
++                      /* Grab an array of local vps */
++                      nlocal = elan_localvps(node, cap, vps, maxlocal);
++          
++                      /* Calculate the number processes per rail */
++                      if ((pprail = nlocal/nrails) == 0)
++                              pprail = 1;
++
++                      /* Allocate processes to rails */
++                      for (i = 0; i < nlocal; i++)
++                      {
++                              pref[vps[i]] = (i / pprail) % nrails;
++                      }
++              }
++      
++              free(vps);
++      }
++
++      return( 0 );
++}
++
++void 
++elan_get_random_key(ELAN_USERKEY *key)
++{
++    int i;
++    for (i = 0; i < sizeof(key->key_values) / sizeof(key->key_values[0]); i++)
++      key->key_values[i] = lrand48();
++}
++
++int elan_lowcontext(ELAN_CAPABILITY *cap)
++{
++    return(cap->cap_lowcontext);
++}
++
++int elan_mycontext(ELAN_CAPABILITY *cap)
++{
++    return(cap->cap_mycontext);
++}
++
++int elan_highcontext(ELAN_CAPABILITY *cap)
++{
++    return(cap->cap_highcontext);
++}
++
++int elan_lownode(ELAN_CAPABILITY *cap)
++{
++    return(cap->cap_lownode);
++}
++
++int elan_highnode(ELAN_CAPABILITY *cap)
++{
++    return(cap->cap_highnode);
++}
++
++int elan_captype(ELAN_CAPABILITY *cap)
++{
++    return(cap->cap_type);
++}
++
++int elan_railmask(ELAN_CAPABILITY *cap)
++{
++    return(cap->cap_railmask);
++}
++
++#endif
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan/device.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan/device.c  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan/device.c       2005-06-01 23:12:54.559445184 -0400
+@@ -0,0 +1,147 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: device.c,v 1.5 2003/09/24 13:55:37 david Exp $"
++/*      $Source: /cvs/master/quadrics/elanmod/modsrc/device.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <elan/elanmod.h>
++
++static LIST_HEAD(elan_dev_list);
++
++ELAN_DEV_STRUCT *
++elan_dev_find (ELAN_DEV_IDX devidx)
++{
++      struct list_head   *tmp;
++      ELAN_DEV_STRUCT *ptr=NULL;
++
++      list_for_each(tmp, &elan_dev_list) {
++              ptr = list_entry(tmp, ELAN_DEV_STRUCT , node);
++              if (ptr->devidx == devidx) 
++                      return ptr;
++              if (ptr->devidx > devidx)
++                      return ERR_PTR(-ENXIO);
++      }
++      
++      return ERR_PTR(-EINVAL);
++}
++
++ELAN_DEV_STRUCT *
++elan_dev_find_byrail (unsigned short deviceid, unsigned rail)
++{
++      struct list_head   *tmp;
++      ELAN_DEV_STRUCT *ptr=NULL;
++
++      list_for_each(tmp, &elan_dev_list) {
++              ptr = list_entry(tmp, ELAN_DEV_STRUCT , node);
++
++              ELAN_DEBUG5 (ELAN_DBG_ALL,"elan_dev_find_byrail devidx %d - %04x %04x,  %d %d \n", ptr->devidx, 
++                           ptr->devinfo->dev_device_id, deviceid, ptr->devinfo->dev_rail, rail);
++
++              if (ptr->devinfo->dev_device_id == deviceid && ptr->devinfo->dev_rail == rail)
++                      return ptr;
++      }
++      
++      return NULL;
++}
++
++ELAN_DEV_IDX
++elan_dev_register (ELAN_DEVINFO *devinfo, ELAN_DEV_OPS *ops, void * user_data)
++{
++      ELAN_DEV_STRUCT *ptr;
++      ELAN_DEV_IDX        devidx = 0;
++      struct list_head   *tmp;
++
++        kmutex_lock(&elan_mutex);
++
++      /* is it already registered */
++      if ((ptr = elan_dev_find_byrail(devinfo->dev_device_id, devinfo->dev_rail)) != NULL) 
++      {
++              kmutex_unlock(&elan_mutex);
++              return EINVAL;
++      }
++
++      /* find a free device idx */
++      list_for_each (tmp, &elan_dev_list) {
++              if (list_entry (tmp, ELAN_DEV_STRUCT, node)->devidx != devidx)
++                      break;
++              devidx++;
++      }
++
++      /* create it and add */
++      KMEM_ALLOC(ptr, ELAN_DEV_STRUCT *, sizeof(ELAN_DEV_STRUCT), 1);
++      if (ptr == NULL)
++      {
++              kmutex_unlock(&elan_mutex);
++              return ENOMEM;
++      }
++
++      ptr->devidx    = devidx;
++      ptr->ops       = ops;
++      ptr->devinfo   = devinfo;
++      ptr->user_data = user_data;
++
++      /* insert this entry *before* the last entry we've found */
++      list_add_tail(&ptr->node, tmp);
++
++      kmutex_unlock(&elan_mutex);
++      return  ESUCCESS;
++}
++
++int
++elan_dev_deregister (ELAN_DEVINFO *devinfo)
++{
++      ELAN_DEV_STRUCT *target;
++
++      kmutex_lock(&elan_mutex);
++
++      if ((target = elan_dev_find_byrail (devinfo->dev_device_id, devinfo->dev_rail)) == NULL)
++      {
++              kmutex_unlock(&elan_mutex);
++              return  EINVAL;
++      }
++
++      list_del(&target->node);
++
++      /* delete target entry */
++      KMEM_FREE(target, sizeof(ELAN_DEV_STRUCT));
++
++      kmutex_unlock(&elan_mutex);
++      return  ESUCCESS;
++}
++
++int
++elan_dev_dump ()
++{
++      struct list_head   *tmp;
++      ELAN_DEV_STRUCT *ptr=NULL;
++
++      kmutex_lock(&elan_mutex);       
++
++      list_for_each(tmp, &elan_dev_list) {
++              ptr = list_entry(tmp, ELAN_DEV_STRUCT , node);
++
++              ELAN_DEBUG3 (ELAN_DBG_ALL,"dev dump: index %u rail %u elan%c\n", 
++                           ptr->devidx, ptr->devinfo->dev_rail, '3' + ptr->devinfo->dev_device_id);
++              ELAN_DEBUG5 (ELAN_DBG_ALL,"dev dump: Vid %x   Did %x  Rid %x  DR %d  DVal %x\n",
++                           ptr->devinfo->dev_vendor_id,
++                           ptr->devinfo->dev_device_id,
++                           ptr->devinfo->dev_revision_id,
++                           ptr->devinfo->dev_driver_version,
++                           ptr->devinfo->dev_num_down_links_value);
++
++      }
++
++      kmutex_unlock(&elan_mutex);
++      return ESUCCESS;
++}
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan/devinfo.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan/devinfo.c 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan/devinfo.c      2005-06-01 23:12:54.559445184 -0400
+@@ -0,0 +1,78 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: devinfo.c,v 1.5 2003/09/24 13:55:37 david Exp $"
++/*      $Source: /cvs/master/quadrics/elanmod/modsrc/devinfo.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <elan/elanmod.h>
++
++int 
++elan_get_devinfo(ELAN_DEV_IDX devidx, ELAN_DEVINFO *devinfo)
++{
++      ELAN_DEV_STRUCT *target;
++      int                 res;
++
++      kmutex_lock(&elan_mutex);
++
++      target = elan_dev_find (devidx);
++
++      if (IS_ERR (target))
++              res = PTR_ERR(target);
++      else
++      {
++              copyout(target->devinfo, devinfo, sizeof(ELAN_DEVINFO));
++              res = ESUCCESS;
++      }
++      
++      kmutex_unlock(&elan_mutex);
++      return res;
++}
++
++int 
++elan_get_position(ELAN_DEV_IDX devidx, ELAN_POSITION *position)
++{
++      ELAN_DEV_STRUCT *target;
++      int                 res;
++
++      kmutex_lock(&elan_mutex);
++
++      target = elan_dev_find(devidx);
++
++      if (IS_ERR (target))
++              res = PTR_ERR(target);
++      else
++              res = target->ops->get_position(target->user_data, position);
++      
++      kmutex_unlock(&elan_mutex);
++      return res;
++}
++
++int 
++elan_set_position(ELAN_DEV_IDX devidx, unsigned short nodeId, unsigned short numNodes)
++{
++      ELAN_DEV_STRUCT *target;
++      int                 res;
++
++      kmutex_lock(&elan_mutex);
++
++      target = elan_dev_find(devidx);
++
++      if (IS_ERR (target))
++              res = PTR_ERR (target);
++      else
++              res = target->ops->set_position(target->user_data, nodeId, numNodes);
++      
++      kmutex_unlock(&elan_mutex);
++      return res;
++}
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan/elanmod.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan/elanmod.c 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan/elanmod.c      2005-06-01 23:12:54.559445184 -0400
+@@ -0,0 +1,149 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++#ident "@(#)$Id: elanmod.c,v 1.11 2004/06/18 09:28:16 mike Exp $"
++/*      $Source: /cvs/master/quadrics/elanmod/modsrc/elanmod.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <elan/elanmod.h>
++
++kmutex_t  elan_mutex;
++
++int 
++elan_init()
++{
++      kmutex_init(&elan_mutex);
++      return (ESUCCESS);
++}
++
++int 
++elan_fini()
++{
++      kmutex_destroy(&elan_mutex);
++      return (ESUCCESS);
++}
++
++int 
++elanmod_classify_cap (ELAN_POSITION *position, ELAN_CAPABILITY *cap, unsigned use)
++{
++      if (cap->cap_version != ELAN_CAP_VERSION_NUMBER)
++      {
++              ELAN_DEBUG2 (ELAN_DBG_VP, "elanmod_classify_cap: (cap->Version != ELAN_CAP_VERSION) %d %d\n", cap->cap_version, ELAN_CAP_VERSION_NUMBER);
++              return (-EINVAL);
++      }
++      
++      if (cap->cap_lowcontext == ELAN_CAP_UNINITIALISED || cap->cap_highcontext == ELAN_CAP_UNINITIALISED)
++      {
++              ELAN_DEBUG3 (ELAN_DBG_VP, "elanmod_classify_cap: LowContext %d    HighContext %d MyContext %d\n",
++                           cap->cap_lowcontext , cap->cap_highcontext, cap->cap_mycontext);
++              return (-EINVAL);
++      }
++      
++      if (cap->cap_lowcontext > cap->cap_highcontext)
++      {
++              ELAN_DEBUG2 (ELAN_DBG_VP, "elanmod_classify_cap: (cap->cap_lowcontext > cap->cap_highcontext) %d %d\n",cap->cap_lowcontext , cap->cap_highcontext);
++              return (-EINVAL);
++      }
++      
++      
++      switch (cap->cap_type & ELAN_CAP_TYPE_MASK)
++      {
++      case ELAN_CAP_TYPE_BLOCK:
++      case ELAN_CAP_TYPE_CYCLIC:
++              if (position->pos_mode == ELAN_POS_UNKNOWN)
++              {
++                      ELAN_DEBUG0 (ELAN_DBG_VP, "elanmod_classify_cap: Position Unknown \n");
++                      return (-EAGAIN);
++              }
++              
++              if ( ! ( ELAN_USER_CONTEXT(cap->cap_lowcontext) && ELAN_USER_CONTEXT(cap->cap_highcontext)))
++              {
++                      ELAN_DEBUG4 (ELAN_DBG_VP, "elanmod_classify_cap:  USER_BASE_CONTEXT %d %d %d %d \n" ,  ELAN_USER_BASE_CONTEXT_NUM,cap->cap_lowcontext, cap->cap_highcontext ,ELAN_USER_TOP_CONTEXT_NUM);
++                      return (-EINVAL);
++              }
++              if (cap->cap_lownode == ELAN_CAP_UNINITIALISED)
++                      cap->cap_lownode = position->pos_nodeid;
++              if (cap->cap_highnode == ELAN_CAP_UNINITIALISED)
++                      cap->cap_highnode = position->pos_nodeid;
++              
++              if (cap->cap_lownode < 0 || cap->cap_highnode >= position->pos_nodes || cap->cap_lownode > cap->cap_highnode)
++              {
++                      ELAN_DEBUG3 ( ELAN_DBG_VP,"elanmod_classify_cap: low %d high %d pos %d \n" , cap->cap_lownode  ,cap->cap_highnode, position->pos_nodes);
++                      
++                      return (-EINVAL);
++              }
++              
++              if ((cap->cap_highnode < position->pos_nodeid) || (cap->cap_lownode > position->pos_nodeid))
++              {
++                      ELAN_DEBUG3 (ELAN_DBG_VP, "elanmod_classify_cap: node not i range low %d high %d this %d\n",
++                                   cap->cap_lownode, cap->cap_highnode, position->pos_nodeid);
++                      return (-EINVAL);
++              }
++
++              break;
++      default:
++              ELAN_DEBUG1 (ELAN_DBG_VP, "elanmod_classify_cap: cant decode type %x \n", cap->cap_type & ELAN_CAP_TYPE_MASK);
++              return (-EINVAL);
++
++      }
++
++      switch (use)
++      {
++      case ELAN_USER_ATTACH:
++      case ELAN_USER_DETACH:
++              if (cap->cap_mycontext == ELAN_CAP_UNINITIALISED)
++              {
++                      ELAN_DEBUG0 (ELAN_DBG_VP, "elanmod_classify_cap: cap->cap_mycontext == ELAN_CAP_UNINITIALISED");
++                      return (-EINVAL);
++              }
++      
++              if ((cap->cap_mycontext != ELAN_CAP_UNINITIALISED) && 
++                  (cap->cap_mycontext < cap->cap_lowcontext || cap->cap_mycontext > cap->cap_highcontext))
++              {
++                      ELAN_DEBUG3 (ELAN_DBG_VP, "elanmod_classify_cap: cap->cap_mycontext out of range %d %d %d \n", cap->cap_lowcontext,cap->cap_mycontext,cap->cap_highcontext);
++                      return (-EINVAL);
++              }   
++              break;
++
++      case ELAN_USER_P2P:
++              break;
++
++      case ELAN_USER_BROADCAST:
++              if (! (cap->cap_type & ELAN_CAP_TYPE_BROADCASTABLE)) {
++                      ELAN_DEBUG0 (ELAN_DBG_VP, "elanmod_classify_cap: use ELAN_USER_BROADCAST but cap not ELAN_CAP_TYPE_BROADCASTABLE\n");
++                      return (-EINVAL);
++              }
++              break;
++
++      default:
++              ELAN_DEBUG1 (ELAN_DBG_VP, "elanmod_classify_cap: unknown use (%d)\n",use);
++              return (-EINVAL);
++      }
++
++
++
++      /* is any ctxt an rms one ?? */
++      if (ELAN_RMS_CONTEXT(cap->cap_lowcontext) || ELAN_RMS_CONTEXT(cap->cap_highcontext))
++      {
++              /* so both low and high must be */
++              if (!(ELAN_RMS_CONTEXT(cap->cap_lowcontext) && ELAN_RMS_CONTEXT(cap->cap_highcontext))) 
++              {
++                      ELAN_DEBUG2 (ELAN_DBG_VP, "elanmod_classify_cap: not rms ctxt %x %x\n",cap->cap_lowcontext,cap->cap_highcontext );
++                      return (-EINVAL);
++              }
++              ELAN_DEBUG0 (ELAN_DBG_VP, "elanmod_classify_cap: returning ELAN_CAP_RMS\n");
++              return (ELAN_CAP_RMS);
++      }
++
++      ELAN_DEBUG0 (ELAN_DBG_VP, "elanmod_classify_cap: returning ELAN_CAP_OK\n");
++      return (ELAN_CAP_OK);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan/elanmod_linux.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan/elanmod_linux.c   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan/elanmod_linux.c        2005-06-01 23:12:54.560445032 -0400
+@@ -0,0 +1,410 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: elanmod_linux.c,v 1.16 2004/06/14 15:45:37 mike Exp $"
++/*      $Source: /cvs/master/quadrics/elanmod/modsrc/elanmod_linux.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan/elanmod.h>
++#include <elan/elanmod_linux.h>
++
++#include <linux/module.h>
++
++#include <linux/sysctl.h>
++#include <linux/init.h>
++
++#include <qsnet/procfs_linux.h>
++
++MODULE_AUTHOR("Quadrics Ltd.");
++MODULE_DESCRIPTION("Elan support module");
++
++MODULE_LICENSE("GPL");
++
++/* elanmod.c */
++EXPORT_SYMBOL(elanmod_classify_cap);
++
++/* bitmap.c */
++#include <elan/bitmap.h>
++
++EXPORT_SYMBOL(bt_freebit);
++EXPORT_SYMBOL(bt_lowbit); 
++EXPORT_SYMBOL(bt_nextbit);
++EXPORT_SYMBOL(bt_copy);
++EXPORT_SYMBOL(bt_zero); 
++EXPORT_SYMBOL(bt_fill); 
++EXPORT_SYMBOL(bt_cmp); 
++EXPORT_SYMBOL(bt_intersect);
++EXPORT_SYMBOL(bt_remove); 
++EXPORT_SYMBOL(bt_add); 
++EXPORT_SYMBOL(bt_spans);
++EXPORT_SYMBOL(bt_subset);  
++EXPORT_SYMBOL(bt_up);
++EXPORT_SYMBOL(bt_down);
++EXPORT_SYMBOL(bt_nbits);
++
++/* capability.c */
++EXPORT_SYMBOL(elan_nullcap);
++EXPORT_SYMBOL(elan_detach_cap);
++EXPORT_SYMBOL(elan_attach_cap);
++EXPORT_SYMBOL(elan_validate_map);
++
++/* stats.c */
++EXPORT_SYMBOL(elan_stats_register);
++EXPORT_SYMBOL(elan_stats_deregister);
++
++/* device.c */
++EXPORT_SYMBOL(elan_dev_deregister);
++EXPORT_SYMBOL(elan_dev_register);
++
++/* debug */
++int  elan_debug_mode = QSNET_DEBUG_BUFFER; 
++int  elan_debug_mask;
++
++static struct proc_dir_entry *elan_procfs_root;
++
++extern void elan_procfs_init(void);
++extern void elan_procfs_fini(void);
++
++static int elan_open    (struct inode *ino, struct file *fp);
++static int elan_release (struct inode *ino, struct file *fp);
++static int elan_ioctl   (struct inode *ino, struct file *fp, unsigned int cmd, unsigned long arg);
++
++static struct file_operations elan_fops = 
++{
++      ioctl:   elan_ioctl,
++      open:    elan_open,
++      release: elan_release,
++};
++
++static int __init elan_start(void)
++{
++      int res;
++
++      elan_procfs_init(); 
++
++      if ((res = elan_init()) != ESUCCESS)
++      {
++              elan_procfs_fini();
++              return (-res);
++      }
++
++      return (0);
++}
++
++static void __exit elan_exit(void)
++{
++      elan_fini();
++      elan_procfs_fini();
++}
++
++
++/* Declare the module init and exit functions */
++void
++elan_procfs_init()
++{
++      struct proc_dir_entry  *p;
++      
++      elan_procfs_root = proc_mkdir("elan",   qsnet_procfs_root);
++      
++      qsnet_proc_register_hex(elan_procfs_root, "debug_mask", &elan_debug_mask, 0);
++      qsnet_proc_register_hex(elan_procfs_root, "debug_mode", &elan_debug_mode, 0);
++
++      if ((p = create_proc_entry ("ioctl", 0, elan_procfs_root)) != NULL)
++      {
++              p->proc_fops = &elan_fops;
++              p->data      = 0;
++              p->owner     = THIS_MODULE;
++      }   
++}
++
++void
++elan_procfs_fini()
++{
++      remove_proc_entry ("debug_mask", elan_procfs_root);
++      remove_proc_entry ("debug_mode", elan_procfs_root);
++      
++      remove_proc_entry ("ioctl",   elan_procfs_root); 
++      remove_proc_entry ("version", elan_procfs_root);  
++      
++      remove_proc_entry ("elan",   qsnet_procfs_root);
++}
++
++module_init(elan_start);
++module_exit(elan_exit);
++
++static int
++elan_open (struct inode *inode, struct file *fp)
++{
++      MOD_INC_USE_COUNT;
++      fp->private_data = NULL;
++      return (0);
++}
++
++static int
++elan_release (struct inode *inode, struct file *fp)
++{
++      /* mark all caps owned by fp to be destroyed */
++      elan_destroy_cap(fp,NULL);
++
++      MOD_DEC_USE_COUNT;
++      return (0);
++}
++
++static int 
++elan_ioctl(struct inode *inode, struct file *fp, unsigned int cmd, unsigned long arg)
++{
++      int rep = 0;
++
++      switch (cmd) 
++      {
++      case ELANCTRL_STATS_GET_NEXT :
++      {
++              ELANCTRL_STATS_GET_NEXT_STRUCT args;
++
++              if (copy_from_user (&args, (void *) arg, sizeof (ELANCTRL_STATS_GET_NEXT_STRUCT)))
++                      return (-EFAULT); 
++
++              /* uses copyin/copyout */
++              if (elan_stats_get_next_index(args.statidx, args.next_statidx) != 0 ) 
++                      return (-EINVAL);       
++
++              break;
++      }
++      case ELANCTRL_STATS_FIND_INDEX :
++      {
++              ELANCTRL_STATS_FIND_INDEX_STRUCT args;
++
++              if (copy_from_user (&args, (void *) arg, sizeof (ELANCTRL_STATS_FIND_INDEX_STRUCT)))
++                      return (-EFAULT); 
++
++              /* uses copyin/copyout */
++              if (elan_stats_find_index(args.block_name, args.statidx, args.num_entries) != 0 ) 
++                      return (-EINVAL);       
++
++              break;
++      }
++      case ELANCTRL_STATS_GET_BLOCK_INFO :
++      {
++              ELANCTRL_STATS_GET_BLOCK_INFO_STRUCT args;
++              
++              if (copy_from_user (&args, (void *) arg, sizeof (ELANCTRL_STATS_GET_BLOCK_INFO_STRUCT)))
++                      return (-EFAULT);
++
++              /* uses copyin/copyout */
++              if (elan_stats_get_block_info(args.statidx, args.block_name, args.num_entries) != 0 ) 
++                      return (-EINVAL);
++              break;          
++      }
++      case ELANCTRL_STATS_GET_INDEX_NAME :
++      {
++              ELANCTRL_STATS_GET_INDEX_NAME_STRUCT args;
++              
++              if (copy_from_user (&args, (void *) arg, sizeof (ELANCTRL_STATS_GET_INDEX_NAME_STRUCT)))
++                      return (-EFAULT);
++
++              /* uses copyin/copyout */
++              if (elan_stats_get_index_name(args.statidx, args.index, args.name) != 0 )
++                      return (-EINVAL);
++              break;
++      }
++      case ELANCTRL_STATS_CLEAR_BLOCK :
++      {
++              ELANCTRL_STATS_CLEAR_BLOCK_STRUCT args;
++              
++              if (copy_from_user (&args, (void *) arg, sizeof (ELANCTRL_STATS_CLEAR_BLOCK_STRUCT)))
++                      return (-EFAULT);
++
++              /* statidx is not a pointer */
++              if (elan_stats_clear_block(args.statidx) != 0 )
++                      return (-EINVAL);
++              break;
++      }
++      case ELANCTRL_STATS_GET_BLOCK :
++      {
++              ELANCTRL_STATS_GET_BLOCK_STRUCT args;
++              
++              if (copy_from_user (&args, (void *) arg, sizeof (ELANCTRL_STATS_GET_BLOCK_STRUCT)))
++                      return (-EFAULT);
++
++              /* uses copyin/copyout */
++              if (elan_stats_get_block(args.statidx, args.entries, args.values) != 0 )
++                      return (-EINVAL);
++              break;
++      }
++      case ELANCTRL_GET_DEVINFO :
++      {
++              ELANCTRL_GET_DEVINFO_STRUCT args;
++              
++              if (copy_from_user (&args, (void *) arg, sizeof (ELANCTRL_GET_DEVINFO_STRUCT)))
++                      return (-EFAULT);
++
++              /* uses copyin/copyout */
++              if (elan_get_devinfo(args.devidx, args.devinfo) != 0 )
++                      return (-EINVAL);
++              break;          
++      }
++      case ELANCTRL_GET_POSITION :
++      {
++              ELANCTRL_GET_POSITION_STRUCT args;
++              
++              if (copy_from_user (&args, (void *) arg, sizeof (ELANCTRL_GET_POSITION_STRUCT)))
++                      return (-EFAULT); 
++
++              /* uses copyin/copyout */
++              if (elan_get_position(args.devidx, args.position) != 0 )
++                      return (-EINVAL);
++              break;          
++      }
++      case ELANCTRL_SET_POSITION :
++      {
++              ELANCTRL_SET_POSITION_STRUCT args;
++              
++              if (copy_from_user (&args, (void *) arg, sizeof (ELANCTRL_SET_POSITION_STRUCT)))
++                      return (-EFAULT);
++
++              /* uses copyin/copyout */
++              if (elan_set_position(args.devidx, args.nodeId, args.numNodes) != 0 )
++                      return (-EINVAL);       
++              break;          
++      }
++      case ELANCTRL_CREATE_CAP  :
++      {
++              ELANCTRL_CREATE_CAP_STRUCT *args;
++
++              /* get space for args */
++              KMEM_ALLOC(args, ELANCTRL_CREATE_CAP_STRUCT *, sizeof(ELANCTRL_CREATE_CAP_STRUCT), 1);
++              if (args == NULL)
++                      return(-ENOMEM);        
++
++              /* copy them */
++              if (copy_from_user (args, (void *) arg, sizeof (ELANCTRL_CREATE_CAP_STRUCT)))
++                      return (-EFAULT);
++              else 
++              {
++                      if ((elan_validate_cap(&args->cap) != 0) || (elan_create_cap(fp,&args->cap) != 0 )) 
++                              rep = (-EINVAL);
++              }
++
++              /* free the space */
++              KMEM_FREE(args, sizeof(ELANCTRL_CREATE_CAP_STRUCT));
++
++              break;          
++      }
++      case ELANCTRL_DESTROY_CAP  :
++      {
++              ELANCTRL_DESTROY_CAP_STRUCT *args;
++
++              /* get space for args */
++              KMEM_ALLOC(args, ELANCTRL_DESTROY_CAP_STRUCT *, sizeof(ELANCTRL_DESTROY_CAP_STRUCT), 1);
++              if (args == NULL)
++                      return(-ENOMEM);        
++
++              /* copy them */
++              if (copy_from_user (args, (void *) arg, sizeof (ELANCTRL_DESTROY_CAP_STRUCT)))
++                      rep = (-EFAULT);
++              else 
++              {
++                      if (elan_destroy_cap(fp, &args->cap) != 0 )
++                              rep = (-EINVAL);
++              }
++
++              /* free the space */
++              KMEM_FREE(args, sizeof(ELANCTRL_DESTROY_CAP_STRUCT));
++
++              break;          
++      }
++      case ELANCTRL_CREATE_VP  :
++      {
++              ELANCTRL_CREATE_VP_STRUCT *args;
++
++              /* get space for args */
++              KMEM_ALLOC(args, ELANCTRL_CREATE_VP_STRUCT *, sizeof(ELANCTRL_CREATE_VP_STRUCT), 1);
++              if (args == NULL)
++                      return(-ENOMEM);        
++
++              /* copy them */
++              if (copy_from_user (args, (void *) arg, sizeof (ELANCTRL_CREATE_VP_STRUCT)))
++                      return (-EFAULT);
++              else
++              {
++                      if ((elan_validate_cap( &args->map) != 0) || (elan_create_vp(fp, &args->cap, &args->map) != 0 ))
++                              rep = (-EINVAL);        
++              }
++
++              KMEM_FREE(args, sizeof(ELANCTRL_CREATE_VP_STRUCT ));
++
++              break;          
++      }
++      case ELANCTRL_DESTROY_VP  :
++      {
++              ELANCTRL_DESTROY_VP_STRUCT *args;
++
++              /* get space for args */
++              KMEM_ALLOC(args, ELANCTRL_DESTROY_VP_STRUCT *, sizeof(ELANCTRL_DESTROY_VP_STRUCT), 1);
++              if (args == NULL)
++                      return(-ENOMEM);        
++              
++              /* copy them */
++              if (copy_from_user (args, (void *) arg, sizeof (ELANCTRL_DESTROY_VP_STRUCT)))
++                      rep = (-EFAULT);
++              else 
++              {
++                      if (elan_destroy_vp(fp, &args->cap, &args->map) != 0 )
++                              rep = (-EINVAL);        
++              }
++
++              KMEM_FREE(args, sizeof(ELANCTRL_DESTROY_VP_STRUCT ));
++
++              break;          
++      }
++
++      case ELANCTRL_GET_CAPS  :
++      {
++              ELANCTRL_GET_CAPS_STRUCT args;
++              if (copy_from_user (&args, (void *) arg, sizeof (ELANCTRL_GET_CAPS_STRUCT)))
++                      return (-EFAULT);
++
++              /* uses copyin/copyout */
++              if (elan_get_caps(args.number_of_results, args.array_size, args.caps) != 0 )
++                      return (-EINVAL);
++              break;          
++      }
++      case ELANCTRL_DEBUG_DUMP :
++      {
++              elan_cap_dump();
++              elan_dev_dump();
++
++              break;
++      }
++      case ELANCTRL_DEBUG_BUFFER :
++      {
++              ELANCTRL_DEBUG_BUFFER_STRUCT args;
++
++              if (copy_from_user (&args, (void *) arg, sizeof (ELANCTRL_DEBUG_BUFFER_STRUCT)))
++                      return (-EFAULT);
++
++              /* uses copyin/copyout */
++              if ((args.size = qsnet_debug_buffer (args.buffer, args.size)) != -1 &&
++                  copy_to_user ((void *) arg, &args, sizeof (ELANCTRL_DEBUG_BUFFER_STRUCT)))
++                      return (-EFAULT);
++              break;
++      }
++      default:
++              return (-EINVAL);
++              break;
++      }
++
++      return (rep);
++}
++
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan/Makefile
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan/Makefile  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan/Makefile       2005-06-01 23:12:54.560445032 -0400
+@@ -0,0 +1,31 @@
++#
++# Makefile for Quadrics QsNet
++#
++# Copyright (c) 2002-2004 Quadrics Ltd
++#
++# File: drivers/net/qsnet/elan/Makefile
++#
++
++
++#
++
++#
++# Makefile for Quadrics QsNet
++#
++# Copyright (c) 2004 Quadrics Ltd.
++#
++# File: driver/net/qsnet/elan/Makefile
++#
++
++list-multi            := elan.o
++elan-objs     := elanmod.o device.o stats.o devinfo.o capability.o elanmod_linux.o capability_general.o bitmap.o
++export-objs           := elanmod_linux.o
++obj-$(CONFIG_QSNET)   := elan.o
++
++elan.o : $(elan-objs)
++      $(LD) -r -o $@ $(elan-objs)
++
++EXTRA_CFLAGS          +=  -DDEBUG -DDEBUG_PRINTF -DDEBUG_ASSERT
++
++include $(TOPDIR)/Rules.make
++
+Index: linux-2.4.21/drivers/net/qsnet/elan/Makefile.conf
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan/Makefile.conf     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan/Makefile.conf  2005-06-01 23:12:54.561444880 -0400
+@@ -0,0 +1,10 @@
++# Flags for generating QsNet Linux Kernel Makefiles
++MODNAME               =       elan.o
++MODULENAME    =       elan
++KOBJFILES     =       elanmod.o device.o stats.o devinfo.o capability.o elanmod_linux.o capability_general.o bitmap.o
++EXPORT_KOBJS  =       elanmod_linux.o 
++CONFIG_NAME   =       CONFIG_QSNET
++SGALFC                =       
++# EXTRALINES START
++
++# EXTRALINES END
+Index: linux-2.4.21/drivers/net/qsnet/elan/quadrics_version.h
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan/quadrics_version.h        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan/quadrics_version.h     2005-06-01 23:12:54.561444880 -0400
+@@ -0,0 +1 @@
++#define QUADRICS_VERSION "4.30qsnet"
+Index: linux-2.4.21/drivers/net/qsnet/elan/stats.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan/stats.c   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan/stats.c        2005-06-01 23:12:54.562444728 -0400
+@@ -0,0 +1,277 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: stats.c,v 1.6 2003/09/24 13:55:37 david Exp $"
++/*      $Source: /cvs/master/quadrics/elanmod/modsrc/stats.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <elan/elanmod.h>
++
++static LIST_HEAD(elan_stats_list);
++static ELAN_STATS_IDX elan_next_statidx=0;
++
++ELAN_STATS_STRUCT *
++elan_stats_find(ELAN_STATS_IDX statidx)
++{
++      struct list_head     *tmp;
++      ELAN_STATS_STRUCT *ptr=NULL;
++
++      list_for_each(tmp, &elan_stats_list) {
++              ptr = list_entry(tmp, ELAN_STATS_STRUCT , node);
++              if ( ptr->statidx == statidx ) 
++                      return ptr;
++      }
++
++      ELAN_DEBUG1 (ELAN_DBG_CTRL, "elan_stats_find failed %d\n", statidx);    
++      return NULL;
++}
++
++ELAN_STATS_STRUCT *
++elan_stats_find_by_name(caddr_t block_name)
++{
++      struct list_head     *tmp;
++      ELAN_STATS_STRUCT *ptr=NULL;
++
++      list_for_each(tmp, &elan_stats_list)    {
++              ptr = list_entry(tmp, ELAN_STATS_STRUCT , node);
++              if (!strcmp(ptr->block_name, block_name)) 
++              {
++                      ELAN_DEBUG3 (ELAN_DBG_CTRL, "elan_stats_find_by_name found %s (%d,%d)\n", block_name, ptr->statidx, ptr->num_entries);  
++                      return ptr;
++              }
++      }
++
++      ELAN_DEBUG1 (ELAN_DBG_CTRL, "elan_stats_find_by_name failed %s\n", block_name);
++      return NULL;
++}
++
++ELAN_STATS_STRUCT *
++elan_stats_find_next(ELAN_STATS_IDX statidx)
++{
++      struct list_head     *tmp;
++      ELAN_STATS_STRUCT *ptr=NULL;
++
++      list_for_each(tmp, &elan_stats_list) {
++              ptr = list_entry(tmp, ELAN_STATS_STRUCT , node);
++        
++              if ( ptr->statidx > statidx ) 
++                      return ptr;       
++      }       
++
++      return NULL;
++}
++
++int 
++elan_stats_get_next_index (ELAN_STATS_IDX statidx, ELAN_STATS_IDX *next_block)
++{
++      ELAN_STATS_STRUCT *target;
++      ELAN_STATS_IDX        next = 0;
++
++      kmutex_lock(&elan_mutex);
++
++      if ((target = elan_stats_find_next(statidx)) != NULL)
++              next = target->statidx;
++
++      copyout(&next, next_block, sizeof(ELAN_STATS_IDX) );
++
++      kmutex_unlock(&elan_mutex);
++      return 0;
++}
++
++int 
++elan_stats_find_index  (caddr_t  block_name, ELAN_STATS_IDX *statidx,  uint *num_entries)
++
++{
++      ELAN_STATS_STRUCT *target;
++      ELAN_STATS_IDX        index   = 0;
++      uint                  entries = 0;
++
++      kmutex_lock(&elan_mutex);
++
++      ELAN_DEBUG1(ELAN_DBG_CTRL, "elan_stats_find_index %s \n", block_name);
++
++      if ((target = elan_stats_find_by_name(block_name)) != NULL)
++      {
++              index   = target->statidx;
++              entries = target->num_entries;
++      }
++
++      ELAN_DEBUG3(ELAN_DBG_CTRL, "elan_stats_find_index found %d %d (target=%p)\n", index, entries, target);
++
++      copyout(&index,   statidx,     sizeof(ELAN_STATS_IDX));
++      copyout(&entries, num_entries, sizeof(uint));
++
++      kmutex_unlock(&elan_mutex);
++      return  ESUCCESS;
++}
++
++int 
++elan_stats_get_block_info (ELAN_STATS_IDX statidx, caddr_t  block_name, uint *num_entries)
++{
++      ELAN_STATS_STRUCT *target;
++      int                   res=EINVAL;
++
++      kmutex_lock(&elan_mutex);
++
++      ELAN_DEBUG1(ELAN_DBG_CTRL, "elan_stats_get_block_info statidx %d\n",statidx);
++
++      if ((target = elan_stats_find(statidx)) != NULL)
++      {
++              ELAN_DEBUG2(ELAN_DBG_CTRL, "elan_stats_get_block_info name %s entries %d\n",block_name, *num_entries);
++              
++              copyout( target->block_name,  block_name,  ELAN_STATS_NAME_MAX_LEN);
++              copyout(&target->num_entries, num_entries, sizeof(uint));
++
++              res = ESUCCESS;
++      }
++
++      kmutex_unlock(&elan_mutex);
++      return res;
++}
++
++int 
++elan_stats_get_index_name (ELAN_STATS_IDX statidx, uint index, caddr_t name)
++{
++      ELAN_STATS_STRUCT *target;
++      int                   res=EINVAL;
++
++      kmutex_lock(&elan_mutex);
++
++      ELAN_DEBUG2(ELAN_DBG_CTRL, "elan_stats_get_index_name statidx %d index %d\n",statidx, index);
++
++      if ((target = elan_stats_find(statidx)) != NULL)
++      {
++              if ( target->ops->elan_stats_get_name== NULL) 
++              {
++                      ELAN_DEBUG0(ELAN_DBG_CTRL, "elan_stats_get_index_name no callback\n");  
++                      kmutex_unlock(&elan_mutex);
++                      return  res;
++              }
++
++              if ((res = target->ops->elan_stats_get_name(target->arg, index, name)) == 0)
++                      ELAN_DEBUG1(ELAN_DBG_CTRL, "elan_stats_get_index_name name %s\n",name); 
++
++      }
++      kmutex_unlock(&elan_mutex);
++      return  res;
++}
++
++int 
++elan_stats_get_block (ELAN_STATS_IDX statidx, uint entries, ulong *values)
++{
++      ELAN_STATS_STRUCT *target;
++      int                   res=EINVAL;
++
++      kmutex_lock(&elan_mutex);
++
++      
++      if ((target = elan_stats_find(statidx)) != NULL)
++      {
++              if ( target->ops->elan_stats_get_block == NULL) 
++              {
++                      kmutex_unlock(&elan_mutex);
++                      return  res;
++              }
++
++              res = target->ops->elan_stats_get_block(target->arg, entries, values);
++      }
++
++      kmutex_unlock(&elan_mutex);
++      return  res;
++}
++
++int 
++elan_stats_clear_block (ELAN_STATS_IDX statidx)
++{
++      ELAN_STATS_STRUCT *target;
++      int                   res=EINVAL;
++
++      kmutex_lock(&elan_mutex);
++
++      if ((target = elan_stats_find(statidx)) != NULL)
++      {
++              if ( target->ops->elan_stats_clear_block == NULL) 
++              {
++                      kmutex_unlock(&elan_mutex);
++                      return  res;
++              }
++      
++              res = target->ops->elan_stats_clear_block(target->arg);
++      }
++      kmutex_unlock(&elan_mutex);
++      return  res;
++}
++
++void
++elan_stats_next_statidx(void)
++{
++      /* XXXXX need to put not in use check here incase we loop MRH */
++      /* tho its a bigish loop :)                                   */
++      elan_next_statidx++;
++      if (!elan_next_statidx)
++              elan_next_statidx++;
++}
++
++int 
++elan_stats_register (ELAN_STATS_IDX    *statidx, 
++                      char              *block_name, 
++                      uint               num_entries,
++                      ELAN_STATS_OPS *ops,
++                      void              *arg)
++{
++      ELAN_STATS_STRUCT *target;
++
++      kmutex_lock(&elan_mutex);
++
++      /* create it and add */
++      KMEM_ALLOC(target, ELAN_STATS_STRUCT *, sizeof(ELAN_STATS_STRUCT), 1);
++      if (target == NULL)
++      {
++              kmutex_unlock(&elan_mutex);
++              return  ENOMEM;
++      }
++
++      elan_stats_next_statidx();
++
++      *statidx = elan_next_statidx;
++
++      target->statidx     = elan_next_statidx;
++      target->num_entries = num_entries;
++      target->ops         = ops;
++      target->arg         = arg;
++      strcpy(target->block_name, block_name);
++      
++      list_add_tail(&target->node, &elan_stats_list);
++
++      kmutex_unlock(&elan_mutex);
++      return  0;
++}
++
++int
++elan_stats_deregister (ELAN_STATS_IDX statidx)
++{
++      ELAN_STATS_STRUCT *target;
++
++      kmutex_lock(&elan_mutex);
++      if ((target = elan_stats_find(statidx)) != NULL)
++      {
++
++              list_del(&target->node);
++              
++              /* delete target entry */
++              KMEM_FREE(target, sizeof(ELAN_STATS_STRUCT));
++      }
++      kmutex_unlock(&elan_mutex);
++
++      return  target == NULL ? EINVAL : 0;
++}
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan3/context.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan3/context.c        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan3/context.c     2005-06-01 23:12:54.565444272 -0400
+@@ -0,0 +1,2101 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: context.c,v 1.116.2.1 2004/11/12 14:24:18 mike Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/os/context.c,v $ */
++
++#include <qsnet/kernel.h>
++#include <qsnet/autoconf.h>
++#include <elan/elanmod.h>
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elanvp.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanctxt.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elandebug.h>
++#include <elan3/urom_addrs.h>
++#include <elan3/thread.h>
++#include <elan3/vmseg.h>
++#include <elan3/elan3ops.h>
++#include <elan3/elansyscall.h>
++/*
++ * Global variables configurable from /etc/system file
++ *     (OR /etc/sysconfigtab on Digital UNIX)
++ */
++int ntrapped_threads   = 64;
++int ntrapped_dmas      = 64;
++int ntrapped_events    = E3_NonSysCntxQueueSize + 128;
++int ntrapped_commands  = 64;
++int noverflow_commands = 1024;
++int nswapped_threads   = 64;
++int nswapped_dmas      = 64;
++
++#define NUM_HALTOPS   8
++
++void *SwapListsLockInfo;
++void *CmdLockInfo;
++
++static void HaltSwapContext (ELAN3_DEV *dev, void *arg);
++
++static char *OthersStateStrings[]  = {"others_running", "others_halting", "others_swapping", 
++                                    "others_halting_more", "others_swapping_more", "others_swapped"};
++
++ELAN3_CTXT *
++elan3_alloc (ELAN3_DEV *dev, int  kernel)
++{
++    ELAN3_CTXT    *ctxt;
++    int           i;
++    unsigned long flags;
++
++    PRINTF1 (DBG_DEVICE, DBG_FN, "elan3_alloc: %s\n", kernel ? "kernel" : "user");
++
++    KMEM_ZALLOC (ctxt, ELAN3_CTXT *, sizeof (ELAN3_CTXT), TRUE);
++    
++    if (ctxt == NULL)
++      return (NULL);
++
++    elan_nullcap (&ctxt->Capability);
++
++    ctxt->Device      = dev;
++    ctxt->OthersState = CTXT_OTHERS_SWAPPED;
++    ctxt->RefCnt      = 1;
++    ctxt->Position    = dev->Position;
++
++    if (kernel)
++      ctxt->Status = CTXT_DETACHED | CTXT_SWAPPED_OUT | CTXT_KERNEL;
++    else
++      ctxt->Status = CTXT_DETACHED | CTXT_SWAPPED_OUT | CTXT_NO_LWPS;
++
++    ctxt->Elan3mmu = elan3mmu_alloc (ctxt);
++
++    kcondvar_init (&ctxt->Wait);
++    kcondvar_init (&ctxt->CommandPortWait);
++    kcondvar_init (&ctxt->LwpWait);
++    kcondvar_init (&ctxt->HaltWait);
++
++    spin_lock_init (&ctxt->InputFaultLock);
++
++    kmutex_init (&ctxt->SwapListsLock);
++    kmutex_init (&ctxt->CmdPortLock);
++    kmutex_init (&ctxt->NetworkErrorLock);
++    kmutex_init (&ctxt->CmdLock);
++
++    krwlock_init (&ctxt->VpLock);
++
++    KMEM_GETPAGES (ctxt->FlagPage, ELAN3_FLAGSTATS *, 1, TRUE);
++    if (!ctxt->FlagPage)
++      goto error;
++    bzero ((char *) ctxt->FlagPage, PAGESIZE);
++
++    KMEM_ZALLOC (ctxt->CommandTraps, COMMAND_TRAP *,    sizeof (COMMAND_TRAP)    * ntrapped_commands, TRUE);
++    if (!ctxt->CommandTraps)
++      goto error;
++
++    KMEM_ZALLOC (ctxt->ThreadTraps,  THREAD_TRAP *,     sizeof (THREAD_TRAP)     * ntrapped_threads,  TRUE);
++    if (!ctxt->ThreadTraps)
++      goto error;
++
++    KMEM_ZALLOC (ctxt->DmaTraps,     DMA_TRAP *,        sizeof (DMA_TRAP)        * ntrapped_dmas,     TRUE);
++    if (!ctxt->DmaTraps)
++      goto error;
++
++    KMEM_ZALLOC (ctxt->EventCookies, EVENT_COOKIE *,    sizeof (EVENT_COOKIE)    * ntrapped_events,   TRUE);
++    if (!ctxt->EventCookies)
++      goto error;
++
++    KMEM_ZALLOC (ctxt->Commands,     CProcTrapBuf_BE *, sizeof (CProcTrapBuf_BE) * noverflow_commands,TRUE);
++    if (!ctxt->Commands)
++      goto error;
++
++    KMEM_ZALLOC (ctxt->SwapThreads,  E3_Addr *,         sizeof (E3_Addr)         * nswapped_threads,  TRUE);
++    if (!ctxt->SwapThreads)
++      goto error;
++
++    KMEM_ZALLOC (ctxt->SwapDmas,     E3_DMA_BE *,       sizeof (E3_DMA_BE)       * nswapped_dmas,     TRUE);
++    if (!ctxt->SwapDmas)
++      goto error;
++
++    /*
++     * "slop" is defined as follows :
++     *     number of entries REQUIRED to be left spare to consume all other traps
++     *     up until the time that the context can be swapped out.
++     *  
++     * CommandTrapQ : 1 command issued by main + 1 issued by the thread processor per elan
++     * ThreadTrapQ  : 2 from command + 2 input
++     * DmaTrapQ     : 2 from command + 2 input
++     * EventTrapQ   : 2 from command + 1 thread + 1 dma + 2 input + E3_NonSysCntxQueueSize
++     */
++    spin_lock_irqsave (&dev->IntrLock, flags);
++    ELAN3_QUEUE_INIT (ctxt->CommandTrapQ, ntrapped_commands,  2);
++    ELAN3_QUEUE_INIT (ctxt->ThreadTrapQ,  ntrapped_threads,   4);
++    ELAN3_QUEUE_INIT (ctxt->DmaTrapQ,     ntrapped_dmas,      4);
++    ELAN3_QUEUE_INIT (ctxt->EventCookieQ, ntrapped_events,    MIN(E3_NonSysCntxQueueSize + 6, ntrapped_events - 6));
++    ELAN3_QUEUE_INIT (ctxt->CommandQ,     noverflow_commands, 0);
++    ELAN3_QUEUE_INIT (ctxt->SwapThreadQ,  nswapped_threads,   0);
++    ELAN3_QUEUE_INIT (ctxt->SwapDmaQ,     nswapped_dmas,      0);
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++
++#if defined(DIGITAL_UNIX)
++    /* Allocate the segelan for the command port */
++    if (! kernel && elan3_segelan3_create (ctxt) == NULL)
++    {
++      elan3_detach(ctxt);
++      elan3_free (ctxt);
++      return ((ELAN3_CTXT *) NULL);
++    }
++#endif
++
++    /*
++     * Initialise the Input Fault list 
++     */
++    spin_lock (&ctxt->InputFaultLock);
++    for (i = 0; i < NUM_INPUT_FAULT_SAVE; i++)
++      ctxt->InputFaults[i].Next = (i == (NUM_INPUT_FAULT_SAVE-1)) ? NULL : &ctxt->InputFaults[i+1];
++    ctxt->InputFaultList = &ctxt->InputFaults[0];
++    spin_unlock (&ctxt->InputFaultLock);
++
++    ReserveHaltOperations (dev, NUM_HALTOPS, TRUE);
++    
++    if ((ctxt->RouteTable = AllocateRouteTable (ctxt->Device, ELAN3_MAX_VPS)) == NULL)
++    {
++      PRINTF0 (DBG_DEVICE, DBG_FN, "elan3_alloc: cannot map route table\n");
++      elan3_detach(ctxt);
++      elan3_free (ctxt);
++      return ((ELAN3_CTXT *) NULL);
++    }  
++
++    return (ctxt);
++
++
++ error:
++
++    elan3_detach(ctxt);
++    elan3_free (ctxt);
++    if (ctxt->FlagPage)
++      KMEM_FREEPAGES ((void *) ctxt->FlagPage, 1);
++    if (ctxt->CommandTraps)
++      KMEM_FREE ((void *) ctxt->CommandTraps, sizeof (COMMAND_TRAP)    * ntrapped_commands);
++    if (ctxt->ThreadTraps)
++      KMEM_FREE ((void *) ctxt->ThreadTraps,  sizeof (THREAD_TRAP)     * ntrapped_threads);
++    if (ctxt->DmaTraps)
++      KMEM_FREE ((void *) ctxt->DmaTraps,     sizeof (DMA_TRAP)        * ntrapped_dmas);
++    if (ctxt->EventCookies)
++      KMEM_FREE ((void *) ctxt->EventCookies, sizeof (EVENT_COOKIE)    * ntrapped_events);
++    if (ctxt->Commands)
++      KMEM_FREE ((void *) ctxt->Commands,     sizeof (CProcTrapBuf_BE) * noverflow_commands);
++    if (ctxt->SwapThreads)
++      KMEM_FREE ((void *) ctxt->SwapThreads,  sizeof (E3_Addr)         * nswapped_threads);
++    if (ctxt->SwapDmas)
++      KMEM_FREE ((void *) ctxt->SwapDmas,     sizeof (E3_DMA_BE)       * nswapped_dmas);
++
++    kcondvar_destroy (&ctxt->Wait);
++    kcondvar_destroy (&ctxt->CommandPortWait);
++    kcondvar_destroy (&ctxt->LwpWait);
++    kcondvar_destroy (&ctxt->HaltWait);
++
++    kmutex_destroy (&ctxt->SwapListsLock);
++    kmutex_destroy (&ctxt->CmdLock);
++    kmutex_destroy (&ctxt->NetworkErrorLock);
++    spin_lock_destroy  (&ctxt->InputFaultLock);
++
++    krwlock_destroy (&ctxt->VpLock);
++
++    KMEM_FREE (ctxt, sizeof (ELAN3_CTXT));
++
++    return (NULL);
++}
++
++void
++elan3_free (ELAN3_CTXT *ctxt)
++{
++    ELAN3_DEV     *dev = ctxt->Device;
++    NETERR_FIXUP *nef;
++    
++    PRINTF1 (ctxt, DBG_FN, "elan3_free: %p \n", ctxt);
++   
++    elan3_removevp (ctxt, ELAN3_INVALID_PROCESS);                     /* Remove any virtual process mappings */
++
++#if defined(DIGITAL_UNIX)
++    WaitForContext (ctxt);                                    /* wait for all references to this context to go away */
++#endif
++
++    if (ctxt->RouteTable)
++      FreeRouteTable (dev, ctxt->RouteTable);
++    ctxt->RouteTable = NULL;
++
++    elan3mmu_free (ctxt->Elan3mmu);                           /* free of our Elan3mmu  */
++
++    if (ctxt->Private)                                                /* Call back to "user" to free off  */
++      ELAN3_OP_FREE_PRIVATE (ctxt);                           /* private data */
++
++#if defined(DIGITAL_UNIX)
++    if (! CTXT_IS_KERNEL(ctxt))
++      elan3_segelan3_destroy (ctxt);                          /* Unmap the command port from the users address space. */
++#endif
++   
++    ReleaseHaltOperations (dev, NUM_HALTOPS);
++
++    if (ctxt->Input0Resolver)
++      CancelNetworkErrorResolver (ctxt->Input0Resolver);
++
++    if (ctxt->Input1Resolver)
++      CancelNetworkErrorResolver (ctxt->Input1Resolver);
++
++    while ((nef = ctxt->NetworkErrorFixups) != NULL)
++    {
++      ctxt->NetworkErrorFixups = nef->Next;
++
++      CompleteNetworkErrorFixup (ctxt, nef, ESRCH);
++    }
++
++    KMEM_FREEPAGES ((void *) ctxt->FlagPage, 1);
++
++    KMEM_FREE ((void *) ctxt->CommandTraps, sizeof (COMMAND_TRAP)    * ntrapped_commands);
++    KMEM_FREE ((void *) ctxt->ThreadTraps,  sizeof (THREAD_TRAP)     * ntrapped_threads);
++    KMEM_FREE ((void *) ctxt->DmaTraps,     sizeof (DMA_TRAP)        * ntrapped_dmas);
++    KMEM_FREE ((void *) ctxt->EventCookies, sizeof (EVENT_COOKIE)    * ntrapped_events);
++    KMEM_FREE ((void *) ctxt->Commands,     sizeof (CProcTrapBuf_BE) * noverflow_commands);
++    KMEM_FREE ((void *) ctxt->SwapThreads,  sizeof (E3_Addr)         * nswapped_threads);
++    KMEM_FREE ((void *) ctxt->SwapDmas,     sizeof (E3_DMA_BE)       * nswapped_dmas);
++
++    kcondvar_destroy (&ctxt->Wait);
++    kcondvar_destroy (&ctxt->CommandPortWait);
++    kcondvar_destroy (&ctxt->LwpWait);
++    kcondvar_destroy (&ctxt->HaltWait);
++
++    kmutex_destroy (&ctxt->SwapListsLock);
++    kmutex_destroy (&ctxt->CmdLock);
++    kmutex_destroy (&ctxt->NetworkErrorLock);
++    spin_lock_destroy  (&ctxt->InputFaultLock);
++
++    krwlock_destroy (&ctxt->VpLock);
++
++    KMEM_FREE (ctxt, sizeof (ELAN3_CTXT));
++}
++
++int 
++elan3_doattach(ELAN3_CTXT *ctxt, ELAN_CAPABILITY *cap)
++{
++    unsigned long pgnum = ((cap->cap_mycontext & MAX_ROOT_CONTEXT_MASK) * sizeof (E3_CommandPort)) / PAGE_SIZE;
++    unsigned long pgoff = ((cap->cap_mycontext & MAX_ROOT_CONTEXT_MASK) * sizeof (E3_CommandPort)) & (PAGE_SIZE-1);
++    ELAN3_DEV     *dev   = ctxt->Device;
++    int           res   = ESUCCESS;
++    unsigned long flags;
++
++    /* Map in the command port for this context */
++    if (MapDeviceRegister (dev, ELAN3_BAR_COMMAND_PORT, &ctxt->CommandPage, pgnum * PAGE_SIZE, PAGE_SIZE, &ctxt->CommandPageHandle) != ESUCCESS)
++    {
++      PRINTF0 (ctxt, DBG_FN, "elan3_doattach: MapDeviceRegister failed");
++      return (EINVAL);
++    }
++
++    ctxt->CommandPort = ctxt->CommandPage + pgoff;
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++
++    res = 0;
++    if (ELAN3_DEV_CTX_TABLE(dev,cap->cap_mycontext) != NULL)
++      res = EBUSY;
++    else
++    {
++      if ((res = elan3mmu_attach (ctxt->Device, cap->cap_mycontext, ctxt->Elan3mmu, 
++                                  ctxt->RouteTable->Table, ctxt->RouteTable->Size-1)) == 0)
++      {
++          ELAN3_DEV_CTX_TABLE(dev,cap->cap_mycontext) = ctxt;
++          ctxt->Capability                            = *cap;
++      }
++    }
++
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++
++    if (res == ESUCCESS)
++      elan3_swapin (ctxt, CTXT_DETACHED);
++    else 
++    {
++      UnmapDeviceRegister (dev, &ctxt->CommandPageHandle);
++      ctxt->CommandPage = (ioaddr_t) 0; 
++      ctxt->CommandPort = (ioaddr_t) 0;
++    }
++
++    return (res);
++}
++
++void
++elan3_destroy_callback( void * args, ELAN_CAPABILITY *cap, ELAN_CAPABILITY *map)
++{
++    if (map == NULL) 
++    {
++      /* the cap is being destroyed */
++      PRINTF0 (NULL, DBG_VP, "elan3_destroy_callback: the cap is being destroyed \n");
++    }
++    else
++    {
++      /* the map is being destroyed */
++      PRINTF0 (NULL, DBG_VP, "elan3_destroy_callback: the map is being destroyed \n");
++    }
++}
++
++int
++elan3_attach (ELAN3_CTXT *ctxt, ELAN_CAPABILITY *cap)
++{
++    ELAN3_DEV *dev = ctxt->Device;
++    int type;
++    int res;
++
++    switch (type = elan3_validate_cap (dev, cap, ELAN_USER_ATTACH))
++    {
++    case ELAN_CAP_OK:
++      /* nothing */
++      break;
++
++    case ELAN_CAP_RMS:
++      if ((res = elan_attach_cap(cap, dev->Devinfo.dev_rail, ctxt, elan3_destroy_callback)) != 0)
++          return res;
++      break;
++
++    default:
++      return (EINVAL);
++    }
++
++    if (((res = elan3_doattach(ctxt,cap)) != ESUCCESS) && (type == ELAN_CAP_RMS))
++      elan_detach_cap(cap, dev->Devinfo.dev_rail);
++
++    return res;
++}
++
++void
++elan3_detach ( ELAN3_CTXT *ctxt )
++{
++    ELAN3_DEV   *dev                 = ctxt->Device;
++    int need_to_call_elanmod_detach = 0;
++    unsigned long flags;
++
++    PRINTF1 (ctxt, DBG_FN, "elan3_detach: %p \n", ctxt );
++    
++    if (ctxt->Capability.cap_mycontext == ELAN_CAP_UNINITIALISED)
++    {
++      PRINTF0 (ctxt, DBG_FN, "elan3_detach: context not attached \n");
++      return ;
++    }
++
++    /* must you be in the ctx_table ?? */
++    
++    switch (ctxt->Capability.cap_type & ELAN_CAP_TYPE_MASK)
++    {
++    case ELAN_CAP_TYPE_BLOCK:
++    case ELAN_CAP_TYPE_CYCLIC:
++    {
++      if (ELAN3_SYSTEM_CONTEXT (ctxt->Capability.cap_mycontext))
++          return ;
++
++      if (! (ctxt->Capability.cap_type & ELAN_CAP_TYPE_HWTEST))
++          need_to_call_elanmod_detach = 1;
++
++      break;
++    } 
++    default:
++      return ;
++    }
++
++    elan3_swapout (ctxt, CTXT_DETACHED);
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++
++    elan3mmu_detach (dev, ctxt->Capability.cap_mycontext);
++    ELAN3_DEV_CTX_TABLE(dev,ctxt->Capability.cap_mycontext) = NULL;
++
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++
++    if (ctxt->CommandPage)
++    {
++      UnmapDeviceRegister (dev, &ctxt->CommandPageHandle);
++      ctxt->CommandPage = (ioaddr_t) 0;
++    }
++    
++    if (need_to_call_elanmod_detach) 
++      elan_detach_cap(&ctxt->Capability, dev->Devinfo.dev_rail);
++
++    elan_nullcap (&ctxt->Capability);
++
++}
++
++void
++elan3_dodetach ( ELAN3_CTXT *ctxt )
++{
++    ELAN3_DEV     *dev = ctxt->Device;
++    unsigned long flags;
++
++    PRINTF1 (ctxt, DBG_FN, "elan3_dodetach: %p \n", ctxt );
++    
++    if (ctxt->Capability.cap_mycontext == ELAN_CAP_UNINITIALISED)
++    {
++      PRINTF0 (ctxt, DBG_FN, "elan3_dodetach: context not attached \n");
++      return ;
++    }
++
++    elan3_swapout (ctxt, CTXT_DETACHED);
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++
++    elan3mmu_detach (dev, ctxt->Capability.cap_mycontext);
++    ELAN3_DEV_CTX_TABLE(dev,ctxt->Capability.cap_mycontext) = NULL;
++
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++
++    if (ctxt->CommandPage)
++    {
++      UnmapDeviceRegister (dev, &ctxt->CommandPageHandle);
++      ctxt->CommandPage = (ioaddr_t) 0;
++    }
++    
++    elan_nullcap (&ctxt->Capability);
++}
++
++void
++elan3_swapin (ELAN3_CTXT *ctxt, int reason)
++{
++    ELAN3_DEV *dev = ctxt->Device;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++
++    ASSERT (ctxt->Status & CTXT_SWAPPED_REASONS);
++
++    PRINTF3 (ctxt, DBG_SWAP, "elan3_swapin: status %x State %s reason %x\n", 
++           ctxt->Status, OthersStateStrings[ctxt->OthersState], reason);
++
++    while (ctxt->Status & CTXT_SWAPPING_OUT)                  /* In transition */
++      kcondvar_wait (&ctxt->LwpWait, &dev->IntrLock, &flags);
++
++    if (reason == CTXT_NO_LWPS && ctxt->LwpCount++ != 0)      /* Added another LWP */
++    {
++      spin_unlock_irqrestore (&dev->IntrLock, flags);
++      return;
++    }
++
++    if ((ctxt->Status & ~reason) & CTXT_SWAPPED_REASONS)
++      ctxt->Status &= ~reason;
++    else
++    {
++      ASSERT (ctxt->Status & CTXT_SWAPPED_OUT);
++      ASSERT (ctxt->OthersState == CTXT_OTHERS_SWAPPED);
++      
++      /*
++       * Will not be swapped out anymore, so ask the "user" to perform 
++       * any swapping in he needs before letting the context run again.
++       */
++      
++      ctxt->Status &= ~(CTXT_SWAPPED_OUT | CTXT_QUEUES_EMPTY | reason);
++      ctxt->OthersState = CTXT_OTHERS_RUNNING;
++
++      if (ctxt->Input0Trap.State == CTXT_STATE_OK && ctxt->Input1Trap.State == CTXT_STATE_OK)
++          SetInputterStateForContext (ctxt, 0, NULL);
++      
++      kcondvar_wakeupall (&ctxt->Wait, &dev->IntrLock);
++    }
++
++    PRINTF2 (ctxt, DBG_SWAP, "elan3_swapin: all done - status %x state %s\n",
++           ctxt->Status, OthersStateStrings[ctxt->OthersState]);
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++}
++
++
++void
++elan3_swapout (ELAN3_CTXT *ctxt, int reason)
++{
++    ELAN3_DEV     *dev = ctxt->Device;
++    int           cansleep;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++
++    PRINTF3 (ctxt, DBG_SWAP, "elan3_swapout: status %x state %s reason %x\n", 
++           ctxt->Status, OthersStateStrings[ctxt->OthersState], reason);
++
++    if (reason == CTXT_NO_LWPS)
++    {
++      if (--ctxt->LwpCount != 0)                              /* Still other LWPs running */
++      {
++          spin_unlock_irqrestore (&dev->IntrLock, flags);
++          return;
++      }
++
++      kcondvar_wakeupall (&ctxt->LwpWait, &dev->IntrLock);            /* Wakeup anyone waiting on LwpCount */
++    }
++    
++    ctxt->Status |= reason;
++    
++    while (ctxt->Status & CTXT_SWAPPING_OUT)                  /* wait for someone else to finish swapping */
++      kcondvar_wait (&ctxt->LwpWait, &dev->IntrLock, &flags);         /* out */
++
++    if (ctxt->Status & CTXT_SWAPPED_OUT)
++    {
++      if (reason == CTXT_NO_LWPS)                             /* Wakeup other thread waiting on LWP exit */
++          kcondvar_wakeupall (&ctxt->LwpWait, &dev->IntrLock);
++      
++      spin_unlock_irqrestore (&dev->IntrLock, flags);
++      return;
++    }
++    
++    /*
++     * mark the context as swapping out.
++     */
++    ctxt->Status |= CTXT_SWAPPING_OUT;
++    
++    if (reason != CTXT_FIXUP_NETERR)
++    {
++      /*
++       * Stop all of the lwps.
++       */
++      while (ctxt->LwpCount)
++      {
++          kcondvar_wakeupall (&ctxt->Wait, &dev->IntrLock);           /* Wake up any lwps */
++          kcondvar_wait (&ctxt->LwpWait, &dev->IntrLock, &flags);             /* then wait for them to enter elan3_swapout */
++      }
++    }
++    
++    StartSwapoutContext (ctxt, 0, NULL);
++    for (;;)
++    {
++      PRINTF0 (ctxt, DBG_SWAP, "elan3_swapout: HandleExceptions\n");
++
++      cansleep = (HandleExceptions(ctxt, &flags) == ESUCCESS);
++
++      PRINTF2 (ctxt, DBG_SWAP, "elan3_swapout: OthersState=%d cansleep=%d\n", ctxt->OthersState, cansleep);
++
++      if (ctxt->OthersState == CTXT_OTHERS_SWAPPED)
++          break;
++
++      if (cansleep)
++          kcondvar_wait (&ctxt->Wait, &dev->IntrLock, &flags);
++    }
++    PRINTF0 (ctxt, DBG_SWAP, "elan3_swapout: swapped out\n");
++    
++    ASSERT (ELAN3_QUEUE_EMPTY (ctxt->DmaTrapQ));
++    ASSERT (ELAN3_QUEUE_EMPTY (ctxt->ThreadTrapQ));
++
++    ctxt->Status |=  CTXT_SWAPPED_OUT;
++    ctxt->Status &= ~CTXT_SWAPPING_OUT;
++
++    kcondvar_wakeupall (&ctxt->LwpWait, &dev->IntrLock);
++
++    PRINTF2 (ctxt, DBG_SWAP, "elan3_swapout: all done - status %x state %s\n",
++           ctxt->Status, OthersStateStrings[ctxt->OthersState]);
++
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++}
++
++int
++elan3_pagefault (ELAN3_CTXT *ctxt, E3_FaultSave_BE *FaultSave, int npages)
++{
++    E3_Addr     elanAddr = FaultSave->s.FaultAddress;
++    int               writeable;
++    int               res;
++
++    PRINTF3 (ctxt, DBG_FAULT, "elan3_pagefault: elanAddr %08x FSR %08x : %s\n", elanAddr, FaultSave->s.FSR.Status,
++           FaultSave->s.FSR.s.ProtFault ? "protection fault" : "pte invalid");
++    
++    /* Look at the FSR to determine the fault type etc */
++    
++    if (FaultSave->s.FSR.Status == 0)                         /* this is a target abort/parity error, so look */
++    {                                                         /* at the PCI config space registers to determine  */
++      ElanBusError (ctxt->Device);
++      return (EFAULT);                                        
++    }
++    
++    if (FaultSave->s.FSR.s.AlignmentErr)                      /* Alignment errors are always fatal. */
++    {
++      PRINTF0 (ctxt, DBG_FAULT, "elan3_pagefault: Alignment error\n");
++      return (EFAULT);
++    }
++
++    if (FaultSave->s.FSR.s.WalkBadData)                               /* Memory ECC error during a walk */
++    {
++      PRINTF0 (ctxt, DBG_FAULT, "elan3_pagefault: Memory ECC error during walk\n");
++      return (EFAULT);
++    }
++
++    if (!FaultSave->s.FSR.s.ProtFault &&                      /* DMA memory type changed */
++      !FaultSave->s.FSR.s.Walking)
++    {
++      PRINTF0 (ctxt, DBG_FAULT, "elan3_pagefault: DMA memory type changed\n");
++      return (EFAULT);
++    }
++
++    ASSERT (FaultSave->s.FSR.s.ProtFault ?                    /* protection errors, should always have a valid pte */
++          (!FaultSave->s.FSR.s.Walking || !(FaultSave->s.FSR.s.Level==3) ||  FaultSave->s.FSR.s.FaultPte == ELAN3_ET_PTE) : 
++          FaultSave->s.FSR.s.FaultPte == ELAN3_ET_INVALID);   /* otherwise it must be an invalid pte */
++
++    /*
++     * Determine whether to fault for a 'write' from the access permissions we need, and not
++     * from the access type (WrAcc).
++     */
++    writeable = (FaultSave->s.FSR.s.AccTypePerm & (1 << FSR_WritePermBit));
++
++    /* Check that we have the right permissions for this access type. */
++    if ((res = elan3mmu_checkperm (ctxt->Elan3mmu, (elanAddr&PAGEMASK), npages*PAGESIZE, FaultSave->s.FSR.s.AccTypePerm)) != 0)
++    {
++      PRINTF1 (ctxt, DBG_FAULT, "elan3_pagefault: %s\n", (res == ENOMEM) ? "no protection mapping" : "protection error");
++      
++      return (res);
++    }
++
++    res = LoadElanTranslation (ctxt, (elanAddr&PAGEMASK), npages*PAGESIZE, FaultSave->s.FSR.s.ProtFault, writeable);
++
++    if (res == ESUCCESS)
++    {
++      BumpStat (ctxt->Device, PageFaults);
++      BumpUserStat (ctxt, PageFaults);
++    }
++
++    PRINTF1 (ctxt, DBG_FAULT, "elan3_pagefault: -> %d\n", res);
++
++    return (res);
++}
++
++void
++elan3_block_inputter (ELAN3_CTXT *ctxt, int block)
++{
++    ELAN3_DEV *dev = ctxt->Device;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++    
++    if (block)
++      ctxt->Status |= CTXT_USER_FILTERING;
++    else
++      ctxt->Status &= ~CTXT_USER_FILTERING;
++
++    if (ctxt->Capability.cap_mycontext != ELAN_CAP_UNINITIALISED)
++      SetInputterStateForContext (ctxt, 0, NULL);
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++}
++
++int
++FixupNetworkErrors (ELAN3_CTXT *ctxt, unsigned long *flags)
++{
++    ELAN3_DEV          *dev = ctxt->Device;
++    NETERR_FIXUP *nef;
++
++    ASSERT (SPINLOCK_HELD (&dev->IntrLock));
++    
++    if (ctxt->NetworkErrorFixups == NULL)
++      return (ESUCCESS);
++
++    spin_unlock_irqrestore (&dev->IntrLock, *flags);
++    
++    kmutex_lock (&ctxt->NetworkErrorLock);                    /* single thread while fixing up errors */
++    elan3_swapout (ctxt, CTXT_FIXUP_NETERR);
++
++    spin_lock_irqsave (&dev->IntrLock, *flags);
++    while ((nef = ctxt->NetworkErrorFixups) != NULL)
++    {
++      ctxt->NetworkErrorFixups = nef->Next;
++      spin_unlock_irqrestore (&dev->IntrLock, *flags);
++
++      if (ELAN3_OP_FIXUP_NETWORK_ERROR (ctxt, nef) == OP_FAILED)
++          CompleteNetworkErrorFixup (ctxt, nef, EINVAL);
++
++      spin_lock_irqsave (&dev->IntrLock, *flags);
++    }
++    spin_unlock_irqrestore (&dev->IntrLock, *flags);
++
++    elan3_swapin (ctxt, CTXT_FIXUP_NETERR);
++
++    kmutex_unlock (&ctxt->NetworkErrorLock);
++    spin_lock_irqsave (&dev->IntrLock, *flags);
++    return (EAGAIN);
++}
++
++int
++CompleteNetworkErrorResolver (ELAN3_CTXT *ctxt, INPUT_TRAP *trap, NETERR_RESOLVER *rvp)
++{
++    int state;
++
++    switch (rvp->Status)
++    {
++    case ESUCCESS:
++      /*
++       * the item still existed at the source - if it's a wait for EOP transaction
++       * then the source will retry - otherwise the remote event will have been
++       * cleared and we should execute it
++       */
++      PRINTF1 (ctxt, DBG_NETERR, "CompleteNetworkErrorResolver: ESUCCESS zero WaitForEopTransaction %p\n", trap->WaitForEopTransaction);
++
++      state = trap->WaitForEopTransaction ? CTXT_STATE_OK : CTXT_STATE_NEEDS_RESTART;
++
++      break;
++
++    case ESRCH:       
++      /*
++       * the item was not found at the source - we should always execute the transaction
++       * since it will never be resent
++       */
++      PRINTF1 (ctxt, DBG_NETERR, "CompleteNetworkErrorResolver: ESRCH execute WaitForEopTransaction %p\n", trap->WaitForEopTransaction);
++      state = CTXT_STATE_NEEDS_RESTART;
++      break;
++
++    default:                                                  /* other errors */
++      PRINTF1 (ctxt, DBG_NETERR, "CompleteNetworkErrorResolver: %d\n", rvp->Status);
++      if (ElanException (ctxt, EXCEPTION_NETWORK_ERROR, INPUT_PROC, trap, &rvp) == OP_HANDLED)
++          state = CTXT_STATE_NEEDS_RESTART;
++      else
++          state = CTXT_STATE_OK;
++      break;
++    }
++
++    FreeNetworkErrorResolver (rvp);
++
++    return (state);
++}
++
++int
++HandleExceptions (ELAN3_CTXT *ctxt, unsigned long *flags)
++{
++    ELAN3_DEV        *dev    = ctxt->Device;
++    THREAD_TRAP      tproc;
++    DMA_TRAP         dproc;
++    NETERR_RESOLVER *rvp;
++    int                    state;
++
++    if (ctxt->Status & CTXT_COMMAND_OVERFLOW_ERROR)
++    {
++      ctxt->Status &= ~CTXT_COMMAND_OVERFLOW_ERROR;
++      spin_unlock_irqrestore (&dev->IntrLock, *flags);
++      ElanException (ctxt, EXCEPTION_COMMAND_OVERFLOW, COMMAND_PROC, NULL);
++      spin_lock_irqsave (&dev->IntrLock, *flags);
++      return (EAGAIN);
++    }
++    
++    if (! ELAN3_QUEUE_BACK_EMPTY (ctxt->CommandTrapQ))
++    {
++      /* XXXX: unmap translations to the command port */
++
++      spin_unlock_irqrestore (&dev->IntrLock, *flags);
++      ResolveCProcTrap (ctxt);
++      spin_lock_irqsave (&dev->IntrLock, *flags);
++      return (EAGAIN);
++    }
++    
++    if (ctxt->Input0Trap.State == CTXT_STATE_TRAPPED)
++    {
++      ctxt->Input0Trap.State = CTXT_STATE_RESOLVING;
++
++      spin_unlock_irqrestore (&dev->IntrLock, *flags);
++      ResolveIProcTrap (ctxt, &ctxt->Input0Trap, &ctxt->Input0Resolver);
++      spin_lock_irqsave (&dev->IntrLock, *flags);
++      return (EAGAIN);
++    }
++
++    if (ctxt->Input1Trap.State == CTXT_STATE_TRAPPED)
++    {
++      ctxt->Input1Trap.State = CTXT_STATE_RESOLVING;
++
++      spin_unlock_irqrestore (&dev->IntrLock, *flags);
++      ResolveIProcTrap (ctxt, &ctxt->Input1Trap, &ctxt->Input1Resolver);
++      spin_lock_irqsave (&dev->IntrLock, *flags);
++      return (EAGAIN);
++    }
++
++    if ((rvp = ctxt->Input0Resolver) != NULL && rvp->Completed)
++    {
++      ASSERT (ctxt->Input0Trap.State == CTXT_STATE_NETWORK_ERROR);
++
++      ctxt->Input0Resolver = NULL;
++      
++      spin_unlock_irqrestore (&dev->IntrLock, *flags);
++      state = CompleteNetworkErrorResolver (ctxt, &ctxt->Input0Trap, rvp);
++      spin_lock_irqsave (&dev->IntrLock, *flags);
++      ctxt->Input0Trap.State = state;
++      return (EAGAIN);
++    }
++
++    if ((rvp = ctxt->Input1Resolver) != NULL && rvp->Completed)
++    {
++      ASSERT (ctxt->Input1Trap.State == CTXT_STATE_NETWORK_ERROR);
++
++      ctxt->Input1Resolver = NULL;
++      
++      spin_unlock_irqrestore (&dev->IntrLock, *flags);
++      state = CompleteNetworkErrorResolver (ctxt,&ctxt->Input1Trap, rvp);
++      spin_lock_irqsave (&dev->IntrLock, *flags);
++      ctxt->Input1Trap.State = state;
++      return (EAGAIN);
++    }
++
++    if (NextTProcTrap (ctxt, &tproc))
++    {
++      spin_unlock_irqrestore (&dev->IntrLock, *flags);
++      ResolveTProcTrap (ctxt, &tproc);
++      spin_lock_irqsave (&dev->IntrLock, *flags);
++      return (EAGAIN);
++    }
++    ctxt->Status &= ~CTXT_THREAD_QUEUE_FULL;
++
++    if (NextDProcTrap (ctxt, &dproc))
++    {
++      spin_unlock_irqrestore (&dev->IntrLock, *flags);
++      ResolveDProcTrap (ctxt, &dproc);
++      spin_lock_irqsave (&dev->IntrLock, *flags);
++      return (EAGAIN);
++    }
++    ctxt->Status &= ~CTXT_DMA_QUEUE_FULL;
++
++    /* Handle all event interrupts. */
++    if (! ELAN3_QUEUE_EMPTY (ctxt->EventCookieQ))
++    {
++      while (! ELAN3_QUEUE_EMPTY (ctxt->EventCookieQ))
++      {
++          E3_uint32 cookie = *ELAN3_QUEUE_FRONT (ctxt->EventCookieQ, ctxt->EventCookies);
++
++          ELAN3_QUEUE_REMOVE (ctxt->EventCookieQ);
++
++          spin_unlock_irqrestore (&dev->IntrLock, *flags);
++          if (ELAN3_OP_EVENT (ctxt, cookie, OP_LWP) != OP_DEFER)
++              spin_lock_irqsave (&dev->IntrLock, *flags);
++          else
++          {
++              spin_lock_irqsave (&dev->IntrLock, *flags);     /* place the cookie back on the queue. */
++                                                              /* note we place it on the front to ensure  */
++              ELAN3_QUEUE_ADD_FRONT (ctxt->EventCookieQ);     /* event ordering. */
++              *ELAN3_QUEUE_FRONT (ctxt->EventCookieQ, ctxt->EventCookies) = cookie;
++          }
++      }
++      return (EAGAIN);
++    }
++    ctxt->Status &= ~CTXT_EVENT_QUEUE_FULL;
++
++    if (! ELAN3_QUEUE_EMPTY (ctxt->SwapDmaQ))
++    {
++      while (! ELAN3_QUEUE_EMPTY (ctxt->SwapDmaQ))
++      {
++          E3_DMA_BE DmaDesc = *ELAN3_QUEUE_FRONT (ctxt->SwapDmaQ, ctxt->SwapDmas);
++
++          ELAN3_QUEUE_REMOVE (ctxt->SwapDmaQ);
++
++          spin_unlock_irqrestore (&dev->IntrLock, *flags);
++          RestartDmaDesc (ctxt, &DmaDesc);
++          spin_lock_irqsave (&dev->IntrLock, *flags);
++      }
++      return (EAGAIN);
++    }
++    
++    if (! ELAN3_QUEUE_EMPTY (ctxt->SwapThreadQ))
++    {
++      while (! ELAN3_QUEUE_EMPTY (ctxt->SwapThreadQ))
++      {
++          E3_Addr StackPointer = *ELAN3_QUEUE_FRONT (ctxt->SwapThreadQ, ctxt->SwapThreads);
++
++          ELAN3_QUEUE_REMOVE (ctxt->SwapThreadQ);
++
++          spin_unlock_irqrestore (&dev->IntrLock, *flags);
++          ReissueStackPointer (ctxt, StackPointer);
++          spin_lock_irqsave (&dev->IntrLock, *flags);
++      }
++      return (EAGAIN);
++    }
++    
++    switch (ctxt->OthersState)
++    {
++    case CTXT_OTHERS_SWAPPING:
++      if (! (ctxt->Status & CTXT_OTHERS_REASONS))
++          ctxt->OthersState = CTXT_OTHERS_RUNNING;
++      else
++          ctxt->OthersState = CTXT_OTHERS_SWAPPED;
++
++      PRINTF1 (ctxt, DBG_LWP, "HandleExceptions: OthersState : swapping -> %s\n", OthersStateStrings[ctxt->OthersState]);
++          
++      break;
++
++    case CTXT_OTHERS_SWAPPING_MORE:
++      ctxt->OthersState = CTXT_OTHERS_HALTING_MORE;
++      QueueHaltOperation (dev, 0, NULL, INT_DProcHalted | INT_TProcHalted, HaltSwapContext, ctxt);
++
++      PRINTF1 (ctxt, DBG_LWP, "HandleExceptions: OthersState : swapping_more -> %s\n", OthersStateStrings[ctxt->OthersState]);
++      break;
++    }
++    return (ESUCCESS);
++}
++
++int
++RestartContext (ELAN3_CTXT *ctxt, unsigned long *flags)
++{
++    ELAN3_DEV *dev = ctxt->Device;
++    int       res;
++
++    ASSERT (SPINLOCK_HELD (&dev->IntrLock));
++
++    PRINTF1 (ctxt, DBG_LWP, "RestartContext: status %x\n", ctxt->Status);
++
++    if (! (ctxt->Status & CTXT_OTHERS_REASONS))
++    {
++      if (! ELAN3_QUEUE_FRONT_EMPTY (ctxt->CommandTrapQ) || ! ELAN3_QUEUE_EMPTY(ctxt->CommandQ))
++      {
++          spin_unlock_irqrestore (&dev->IntrLock, *flags);
++          RestartCProcTrap (ctxt);
++          spin_lock_irqsave (&dev->IntrLock, *flags);
++          return (EAGAIN);
++      }
++
++      if (ctxt->Input0Trap.State == CTXT_STATE_NEEDS_RESTART)
++      {
++          ctxt->Input0Trap.State = CTXT_STATE_EXECUTING;
++
++          spin_unlock_irqrestore (&dev->IntrLock, *flags);
++          res = RestartIProcTrap (ctxt, &ctxt->Input0Trap);
++          spin_lock_irqsave (&dev->IntrLock, *flags);
++          
++          if (res == ESUCCESS)
++              ctxt->Input0Trap.State = CTXT_STATE_OK;
++          else
++              ctxt->Input0Trap.State = CTXT_STATE_NEEDS_RESTART;
++          return (EAGAIN);
++      }
++
++      if (ctxt->Input1Trap.State == CTXT_STATE_NEEDS_RESTART)
++      {
++          ctxt->Input1Trap.State = CTXT_STATE_EXECUTING;
++
++          spin_unlock_irqrestore (&dev->IntrLock, *flags);
++          res = RestartIProcTrap (ctxt, &ctxt->Input1Trap);
++          spin_lock_irqsave (&dev->IntrLock, *flags);
++
++          if (res == ESUCCESS)
++              ctxt->Input1Trap.State = CTXT_STATE_OK;
++          else
++              ctxt->Input1Trap.State = CTXT_STATE_NEEDS_RESTART;
++          return (EAGAIN);
++      }
++
++      if (SetEventsNeedRestart (ctxt))
++      {
++          spin_unlock_irqrestore (&dev->IntrLock, *flags);
++          RestartSetEvents (ctxt);
++          spin_lock_irqsave (&dev->IntrLock, *flags);
++          return (EAGAIN);
++      }
++
++      SetInputterStateForContext (ctxt, 0, NULL);
++
++      if (TProcNeedsRestart (ctxt))
++      {
++          spin_unlock_irqrestore (&dev->IntrLock, *flags);
++
++          LoadCommandPortTranslation (ctxt);
++          RestartTProcItems (ctxt);
++          spin_lock_irqsave (&dev->IntrLock, *flags);
++          return (EAGAIN);
++      }
++
++      if (DProcNeedsRestart (ctxt))
++      {
++          spin_unlock_irqrestore (&dev->IntrLock, *flags);
++          RestartDProcItems (ctxt);
++          spin_lock_irqsave (&dev->IntrLock, *flags);
++          return (EAGAIN);
++      }
++
++      if (ELAN3_QUEUE_EMPTY (ctxt->CommandTrapQ))
++      {
++          PRINTF1 (ctxt, DBG_LWP, "RestartContext: setting Command Flag at %p to 0\n", &ctxt->FlagPage->CommandFlag);
++
++          ctxt->FlagPage->CommandFlag = 0;
++
++          if (ctxt->Status & CTXT_WAITING_COMMAND)
++          {
++              PRINTF0 (ctxt, DBG_LWP, "RestartContext: waking up threads waiting for commandport\n");
++              
++              ctxt->Status &= ~CTXT_WAITING_COMMAND;
++              
++              kcondvar_wakeupall (&ctxt->CommandPortWait, &dev->IntrLock);
++          }
++      }
++    }
++
++    return (ESUCCESS);
++}
++
++static void
++HaltSwapContext (ELAN3_DEV *dev, void *arg)
++{
++    ELAN3_CTXT        *ctxt    = (ELAN3_CTXT *) arg;
++    int                     SysCntx = (ctxt->Capability.cap_mycontext & SYS_CONTEXT_BIT);
++    E3_ThreadQueue_BE thread;
++    E3_DMA_BE         dma;
++    sdramaddr_t       FPtr, BPtr;
++    sdramaddr_t             Base, Top;
++    u_int          *runCount;
++    unsigned long     flags;
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++
++    ASSERT (ctxt->OthersState == CTXT_OTHERS_HALTING || ctxt->OthersState == CTXT_OTHERS_HALTING_MORE);
++
++    PRINTF2 (ctxt, DBG_SWAP, "HaltSwapContext: status %x state %s\n", ctxt->Status, OthersStateStrings[ctxt->OthersState]);
++
++    if (! (ctxt->Status & CTXT_OTHERS_REASONS))
++    {
++      if (ctxt->OthersState == CTXT_OTHERS_HALTING_MORE)
++      {
++          runCount = SysCntx ? &dev->HaltAllCount : &dev->HaltNonContext0Count;
++
++          if (--(*runCount) == 0)
++              SetSchedStatusRegister (dev, 0, NULL);
++      }
++      ctxt->OthersState = CTXT_OTHERS_RUNNING;
++      
++      PRINTF0 (ctxt, DBG_SWAP, "HaltSwapContext: no more reason to swap -> others_running\n");
++
++      kcondvar_wakeupall (&ctxt->Wait, &dev->IntrLock);
++      spin_unlock_irqrestore (&dev->IntrLock, flags);
++      return;
++    }
++
++    /*
++     * Capture all other processors since we're not being responsive to 
++     * the command processor interrupt.
++     */
++    CAPTURE_CPUS();
++
++    if (SysCntx)
++    {
++      FPtr = read_reg32 (dev, TProc_SysCntx_FPtr);
++      BPtr = read_reg32 (dev, TProc_SysCntx_BPtr);
++      Base = dev->TAndQBase + offsetof (E3_TrapAndQueue, SysCntxThreadQueue[0]);
++      Top  = dev->TAndQBase + offsetof (E3_TrapAndQueue, SysCntxThreadQueue[E3_SysCntxQueueSize-1]);
++    }
++    else
++    {
++      FPtr  = read_reg32 (dev, TProc_NonSysCntx_FPtr);
++      BPtr  = read_reg32 (dev, TProc_NonSysCntx_BPtr);
++      Base  = dev->TAndQBase + offsetof (E3_TrapAndQueue, NonSysCntxThreadQueue[0]);
++      Top   = dev->TAndQBase + offsetof (E3_TrapAndQueue, NonSysCntxThreadQueue[E3_NonSysCntxQueueSize-1]);
++    }
++
++    while (FPtr != BPtr)
++    {
++      elan3_sdram_copyq_from_sdram (dev, FPtr, (void *) &thread, sizeof (E3_ThreadQueue_BE));
++      
++      if (thread.s.Context == ctxt->Capability.cap_mycontext)
++      {
++          if (ELAN3_QUEUE_FULL (ctxt->SwapThreadQ))
++              break;
++          
++          *ELAN3_QUEUE_BACK(ctxt->SwapThreadQ, ctxt->SwapThreads) = thread.s.Thread;
++          ELAN3_QUEUE_ADD (ctxt->SwapThreadQ);
++          
++          /*
++           * Remove this entry from the queue by replacing it with 
++           * the "magic" thread value.
++           *
++           * NOTE: we must preserve the SYS_CONTEXT_BIT since the Elan uses this
++           * to mark the approriate run queue as empty.
++           */
++          thread.s.Context = SysCntx ? SYS_CONTEXT_BIT : 0;
++          thread.s.Thread  = VanishingStackPointer;
++
++          elan3_sdram_copyq_to_sdram (dev, (void *) &thread, FPtr, sizeof (E3_ThreadQueue_BE));
++      }
++      
++      FPtr = (FPtr == Top) ? Base : FPtr + sizeof (E3_ThreadQueue);
++    }
++
++    ASSERT (elan3_sdram_readl (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProc.s.FSR)) == 0);
++    ASSERT (elan3_sdram_readl (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProcData0.s.FSR.Status)) == 0);
++    ASSERT (elan3_sdram_readl (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProcData1.s.FSR.Status)) == 0);
++    ASSERT (elan3_sdram_readl (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProcData2.s.FSR.Status)) == 0);
++    ASSERT (elan3_sdram_readl (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProcData3.s.FSR.Status)) == 0);
++
++    if (SysCntx)
++    {
++      FPtr  = read_reg32 (dev, DProc_SysCntx_FPtr);
++      BPtr  = read_reg32 (dev, DProc_SysCntx_BPtr);
++      Base  = dev->TAndQBase + offsetof (E3_TrapAndQueue, SysCntxDmaQueue[0]);
++      Top   = dev->TAndQBase + offsetof (E3_TrapAndQueue, SysCntxDmaQueue[E3_SysCntxQueueSize-1]);
++    }
++    else
++    {
++      FPtr  = read_reg32 (dev, DProc_NonSysCntx_FPtr);
++      BPtr  = read_reg32 (dev, DProc_NonSysCntx_BPtr);
++      Base  = dev->TAndQBase + offsetof (E3_TrapAndQueue, NonSysCntxDmaQueue[0]);
++      Top   = dev->TAndQBase + offsetof (E3_TrapAndQueue, NonSysCntxDmaQueue[E3_NonSysCntxQueueSize-1]);
++    }
++
++    while (FPtr != BPtr)
++    {
++      elan3_sdram_copyq_from_sdram (dev, FPtr, &dma, sizeof (E3_DMA_BE));
++          
++      if (dma.s.dma_u.s.Context == ctxt->Capability.cap_mycontext)
++      {
++          if (ELAN3_QUEUE_FULL (ctxt->SwapDmaQ))
++              break;
++          
++          *ELAN3_QUEUE_BACK (ctxt->SwapDmaQ, ctxt->SwapDmas) = dma;
++          ELAN3_QUEUE_ADD (ctxt->SwapDmaQ);
++
++          /*
++           * Remove the DMA from the queue by replacing it with one with
++           * zero size and no events.
++           *
++           * NOTE: we must preserve the SYS_CONTEXT_BIT since the Elan uses this
++           * to mark the approriate run queue as empty.
++           */
++          dma.s.dma_type            = ((SysCntx ? SYS_CONTEXT_BIT : 0) << 16);
++          dma.s.dma_size            = 0;
++          dma.s.dma_source          = (E3_Addr) 0;
++          dma.s.dma_dest            = (E3_Addr) 0;
++          dma.s.dma_destCookieVProc = (E3_Addr) 0;
++          dma.s.dma_srcEvent        = (E3_Addr) 0;
++          dma.s.dma_srcCookieVProc  = (E3_Addr) 0;
++
++          elan3_sdram_copyq_to_sdram (dev, &dma, FPtr, sizeof (E3_DMA_BE));
++      }
++
++      FPtr = (FPtr == Top) ? Base : FPtr + sizeof (E3_DMA);
++    }
++
++    /*
++     * Release the other processors now before signalling the LWP.
++     */
++    RELEASE_CPUS();
++
++    if (! ELAN3_QUEUE_FULL (ctxt->SwapDmaQ) && !ELAN3_QUEUE_FULL (ctxt->SwapThreadQ))
++    {
++      /*
++       * We've compleletly emptied the elan queues of items in this
++       * context, so we now mark it as fully swapped out.
++       */
++      if (ctxt->OthersState == CTXT_OTHERS_HALTING_MORE)
++      {
++          runCount = SysCntx ? &dev->HaltAllCount : &dev->HaltNonContext0Count;
++          
++          if (--(*runCount) == 0)
++              SetSchedStatusRegister (dev, 0, NULL);
++          
++      }
++      PRINTF0 (ctxt, DBG_SWAP, "HaltSwapContext: queues emptied -> others_swapping\n");
++
++      ctxt->OthersState = CTXT_OTHERS_SWAPPING;
++      kcondvar_wakeupall (&ctxt->Wait, &dev->IntrLock);
++    }
++    else
++    {
++      if (ctxt->OthersState == CTXT_OTHERS_HALTING)
++      {
++          runCount = SysCntx ? &dev->HaltAllCount : &dev->HaltNonContext0Count;
++          
++          if ((*runCount)++ == 0)
++              SetSchedStatusRegister (dev, 0, NULL);
++      }
++      PRINTF0 (ctxt, DBG_SWAP, "HaltSwapContext: queues not emptied -> others_swapping_more\n");
++
++      ctxt->OthersState = CTXT_OTHERS_SWAPPING_MORE;
++      kcondvar_wakeupone (&ctxt->Wait, &dev->IntrLock);
++    }
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++}
++
++void
++UnloadCommandPageMapping (ELAN3_CTXT *ctxt)
++{
++    /*
++     * Unload the Elan translations,  and flag the main processor to stall after 
++     * issueing its next command.
++     */
++    if (ctxt->CommandPageMapping != NULL && (ctxt->Status & CTXT_COMMAND_MAPPED_ELAN))
++    {
++      ELAN3MMU_RGN *rgn = elan3mmu_rgnat_main (ctxt->Elan3mmu, ctxt->CommandPageMapping);
++      
++      if (rgn != NULL)
++      {
++          E3_Addr eaddr = rgn->rgn_ebase + (ctxt->CommandPageMapping - rgn->rgn_mbase);
++          
++          PRINTF1 (ctxt, DBG_INTR, "UnloadCommandPageMapping: unmapping command port at addr %08x\n", eaddr);
++          
++          elan3mmu_unload (ctxt->Elan3mmu, eaddr, PAGESIZE, PTE_UNLOAD);
++      }
++      
++      ctxt->Status &= ~CTXT_COMMAND_MAPPED_ELAN;
++    }
++}
++
++void
++StartSwapoutContext (ELAN3_CTXT *ctxt, E3_uint32 Pend, E3_uint32 *Maskp)
++{
++    ELAN3_DEV   *dev     = ctxt->Device;
++    int               SysCntx = (ctxt->Capability.cap_mycontext & SYS_CONTEXT_BIT);
++    u_int      *runCount;
++
++    ASSERT (SPINLOCK_HELD (&dev->IntrLock));
++
++    PRINTF2 (ctxt, DBG_SWAP, "StartSwapoutContext: Status %x OthersState %s\n",
++           ctxt->Status, OthersStateStrings [ctxt->OthersState]);
++    /*
++     * Disable the inputters,  we should already have a reason for it.
++     */
++    SetInputterStateForContext (ctxt, Pend, Maskp);
++
++    UnloadCommandPageMapping (ctxt);
++
++    /* 
++     * Flag main processor to stall after issueing next command
++     */
++    PRINTF1 (ctxt, DBG_SWAP, "StartSwapoutContext: setting Command Flag at %p to 1\n", &ctxt->FlagPage->CommandFlag);
++
++    ctxt->FlagPage->CommandFlag = 1;
++
++    PRINTF1 (ctxt, DBG_SWAP, "StartSwapoutContext: OthersState=%d\n", ctxt->OthersState);
++
++    /*
++     * And queue a haltop to stop the queues and clear it out.
++     */
++    switch (ctxt->OthersState)
++    {
++    case CTXT_OTHERS_RUNNING:
++      PRINTF0 (ctxt, DBG_SWAP, "StartSwapoutContext: -> others_halting\n");
++
++      ctxt->OthersState = CTXT_OTHERS_HALTING;
++
++      QueueHaltOperation (dev, Pend, Maskp, INT_DProcHalted | INT_TProcHalted, HaltSwapContext, ctxt);
++      break;
++      
++    case CTXT_OTHERS_SWAPPING:
++      PRINTF0 (ctxt, DBG_SWAP, "StartSwapoutContext: -> others_swapping_more\n");
++      ctxt->OthersState = CTXT_OTHERS_SWAPPING_MORE;
++
++      runCount = SysCntx ? &dev->HaltAllCount : &dev->HaltNonContext0Count;
++          
++      if ((*runCount)++ == 0)
++          SetSchedStatusRegister (dev, Pend, Maskp);
++      break;
++    default:
++      PRINTF1 (ctxt, DBG_SWAP, "StartSwapoutContext: OthersState=%d\n", ctxt->OthersState);
++      break;
++    }
++}
++
++#if defined(DIGITAL_UNIX)
++/* temporary tweaks to priority bump */
++int lwp_do_prio = 1;
++int lwp_do_nxm = 1;
++int lwp_prio = BASEPRI_USER-1;
++#elif defined(LINUX)
++/* This is the default nice level for the helper LWP */
++int LwpNice = -1;
++#endif
++
++int
++elan3_lwp (ELAN3_CTXT *ctxt)
++{
++    ELAN3_DEV     *dev = ctxt->Device;
++    int                 res;
++    unsigned long flags;
++
++    PRINTF1 (ctxt, DBG_LWP, "elan3_lwp: started, context 0x%x\n", ctxt->Capability.cap_mycontext);
++
++#if defined(DIGITAL_UNIX)
++    {
++        thread_t mythread = current_thread();
++        if (lwp_do_prio && (lwp_do_nxm || !IS_NXM_TASK(mythread->task)))
++        {
++            mythread->priority = mythread->sched_pri = lwp_prio;
++            mythread->max_priority = BASEPRI_HIGHEST;
++            (void) thread_priority(mythread, lwp_prio, 0, 1);
++        }
++    }
++#elif defined(LINUX)
++    {
++      /* Do the priority trick for the helper LWP so that it
++       * runs in preferance to the user threads which may be
++       * burning CPU waiting for a trap to be fixed up
++       */
++#ifdef NO_O1_SCHED
++      if (LwpNice >= -20 && LwpNice < 20)
++          current->nice = LwpNice;
++#else
++      set_user_nice(current, LwpNice);
++#endif
++    }
++#endif
++
++    elan3_swapin (ctxt, CTXT_NO_LWPS);
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++
++    /* If we're swapped out, and not detached (or exiting) then wait until we're swapped back in */
++    /* since otherwise we could "spin" forever continually calling elan3_lwp() */
++    if ((ctxt->Status & CTXT_SWAPPED_REASONS) && ! (ctxt->Status & (CTXT_DETACHED|CTXT_EXITING)))
++      kcondvar_waitsig (&ctxt->Wait, &dev->IntrLock, &flags);
++
++    for (;;)
++    {
++#if defined(DIGITAL_UNIX)
++        if (thread_should_halt(current_thread()) || 
++            CURSIG_CHECK(task_to_proc(current_thread()->task), u.np_uthread))
++      {
++          PRINTF1 (ctxt, DBG_LWP, "elan3_lwp: exiting on %s\n", 
++                   thread_should_halt(current_thread()) ? "halt" : "signal");
++            break;
++      }
++#endif
++
++      if (ctxt->Status & CTXT_SWAPPED_REASONS)
++      {
++          PRINTF0 (ctxt, DBG_LWP, "elan3_lwp: exiting on swapped reasons\n");
++          break;
++      }
++
++      if (! (ctxt->inhibit))
++      {
++          if (FixupNetworkErrors (ctxt, &flags) == ESUCCESS &&
++              HandleExceptions (ctxt, &flags) == ESUCCESS &&
++              RestartContext (ctxt, &flags) == ESUCCESS)
++              {
++                  if (kcondvar_waitsig (&ctxt->Wait, &dev->IntrLock, &flags) == 0)
++                  {
++                      PRINTF0 (ctxt, DBG_LWP, "elan3_lwp: exiting by kcondvar_wait_sig()\n");
++                      break;
++                  }
++              }
++      }
++      else
++      {
++          printk("elan3_lwp :: skipping as inhibited\n");
++          if (kcondvar_waitsig (&ctxt->Wait, &dev->IntrLock, &flags) == 0)
++          {
++              PRINTF0 (ctxt, DBG_LWP, "elan3_lwp: exiting by kcondvar_wait_sig()\n");
++              break;
++          }
++      }
++
++    }
++
++    /* Return EINVAL to elan3_syscall_lwp() when we want it to exit */
++    res = (ctxt->Status & (CTXT_DETACHED|CTXT_EXITING)) ? EINVAL : 0;
++
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++    
++    elan3_swapout (ctxt, CTXT_NO_LWPS);
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++    FixupNetworkErrors (ctxt, &flags);
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++
++    return (res);
++}
++
++void
++SetInputterStateForContext (ELAN3_CTXT *ctxt, E3_uint32 Pend, E3_uint32 *Maskp)
++{
++    ELAN3_DEV  *dev          = NULL;
++    int        new_disabled = 0;
++    int              ctxnum;
++
++    ASSERT (ctxt != NULL);
++    dev  = ctxt->Device;
++    ASSERT (SPINLOCK_HELD (&dev->IntrLock));
++
++    new_disabled = (ctxt->Input0Trap.State != CTXT_STATE_OK ||
++                  ctxt->Input1Trap.State != CTXT_STATE_OK ||
++                  (ctxt->Status & CTXT_INPUTTER_REASONS) != 0);
++    
++
++    ctxnum   = ctxt->Capability.cap_mycontext;
++
++#ifndef __lock_lint  
++    PRINTF2 (ctxt , DBG_IPROC, "SetInputterState: ctxnum %x %s attached\n", ctxnum, ctxt->Disabled ? "disabled " : "");
++#endif /* __lock_lint */
++        
++    if (ctxt->Disabled != new_disabled)
++    {
++      PRINTF2 (ctxt, DBG_IPROC, "SetInputterState: ctxnum %x change %s\n", ctxnum, new_disabled ? "enabled to disabled" : "disabled to enabled");
++      
++      ctxt->Disabled = new_disabled;
++
++      /* synchronize the context filter for this context */
++      elan3mmu_set_context_filter (dev, ctxnum, new_disabled, Pend, Maskp);
++    }
++}
++
++int
++CheckCommandQueueFlushed (ELAN3_CTXT *ctxt, E3_uint32 cflags, int how, unsigned long *flags)
++{
++    ELAN3_DEV *dev    = ctxt->Device;
++    int       delay  = 1;
++    int i, SeenComQueueEmpty;
++
++    ASSERT (SPINLOCK_HELD (&dev->IntrLock));
++    ASSERT (cflags != DmaComQueueNotEmpty || dev->HaltDmaDequeueCount != 0);
++
++    /*
++     * Flush the command processor queues and poll the queue to see it it empties.
++     */
++    if (dev->FlushCommandCount++ == 0)
++      SetSchedStatusRegister (dev, 0, NULL);
++
++    /* 
++     * Ensure previous writes have been flushed through the write buffers
++     */
++    wmb(); mmiob();
++
++    /*
++     * If the command processor traps,  or it's taking too long to observe
++     * the queue as emtpy,  then we need to force the interrupt handler to 
++     * run for us.  So queue a halt operation for the dma processor.
++     */
++    SeenComQueueEmpty = !(read_reg32 (dev, ComQueueStatus) & cflags);
++    for (i = 20; i > 0 || (how & ISSUE_COMMAND_CANT_WAIT); i--)
++    {
++      if (SeenComQueueEmpty || (read_reg32 (dev, Exts.InterruptReg) & (INT_CProc | INT_ComQueue)))
++          break;
++      
++      mb();
++      DELAY (delay);
++
++      if ((delay <<= 1) == 0) delay = 1;
++
++      SeenComQueueEmpty = !(read_reg32 (dev, ComQueueStatus) & cflags);
++    }
++
++    if (--dev->FlushCommandCount == 0)
++      SetSchedStatusRegister (dev, 0, NULL);
++
++    /*
++     * If we've seen the command queue that we're interested in with nothing in it
++     * and the command processor has not trapped then the commands we've
++     * issued have been successfully processed.
++     */
++    if (SeenComQueueEmpty && ! (read_reg32 (dev, Exts.InterruptReg) & (INT_CProc | INT_ComQueue)))
++    {
++      PRINTF0 (ctxt, DBG_CMD, "CheckCommandQueueFlushed: observed dma queue empty and command proc not trapped\n");
++
++      if (cflags == DmaComQueueNotEmpty && --dev->HaltDmaDequeueCount == 0)
++          SetSchedStatusRegister (dev, 0, NULL);
++
++      return (ISSUE_COMMAND_OK);
++    }
++
++    if ((how & ISSUE_COMMAND_CANT_WAIT) != 0)
++      return (ISSUE_COMMAND_WAIT);
++    
++    /*
++     * Halt the dma processor and wait for it to halt,  if the command we've issued has
++     * trapped then the interrupt handler will have moved it to the context structure.
++     */
++    PRINTF0 (ctxt, DBG_CMD, "CheckCommandQueueFlushed: waiting for dproc to halt\n");
++    QueueHaltOperation (dev, 0, NULL, INT_DProcHalted, WakeupLwp, ctxt);
++    while (! ctxt->Halted)
++    {
++      PRINTF1 (ctxt, DBG_CMD, "CheckCommandQueueFlushed: waiting for Halted - %d\n", ctxt->Halted);
++
++      kcondvar_wait (&ctxt->HaltWait, &dev->IntrLock, flags);
++
++      PRINTF1 (ctxt, DBG_CMD, "CheckCommandQueueFlushed: woken for Halted - %d\n", ctxt->Halted);
++    }
++    ctxt->Halted = 0;
++    
++    PRINTF0 (ctxt, DBG_CMD, "CheckCommandQueueFlushed: dproc halted, checking for trap\n");
++    
++    if (cflags == DmaComQueueNotEmpty && --dev->HaltDmaDequeueCount == 0)
++      SetSchedStatusRegister (dev, 0, NULL);
++
++    return (ELAN3_QUEUE_BACK_EMPTY (ctxt->CommandTrapQ) ? ISSUE_COMMAND_OK : ISSUE_COMMAND_TRAPPED);
++}
++
++int
++WaitForCommandPort (ELAN3_CTXT *ctxt)
++{
++    ELAN3_DEV     *dev = ctxt->Device;
++    int                 res;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++
++    if (ctxt->Status & CTXT_DETACHED)
++      res = EINVAL;
++    else 
++    {
++      if (! ELAN3_QUEUE_EMPTY (ctxt->CommandTrapQ) || (ctxt->Status & CTXT_OTHERS_REASONS))
++      {
++          ctxt->Status |= CTXT_WAITING_COMMAND;
++          if (CTXT_IS_KERNEL(ctxt))
++              kcondvar_wait (&ctxt->CommandPortWait, &dev->IntrLock, &flags);
++          else 
++              kcondvar_waitsig (&ctxt->CommandPortWait, &dev->IntrLock, &flags);
++      }
++      
++      res = (!ELAN3_QUEUE_EMPTY(ctxt->CommandTrapQ) || (ctxt->Status & CTXT_OTHERS_REASONS)) ? EAGAIN : 0;
++    }
++      
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++
++    return (res);
++}
++
++static char *
++CommandName (int offset)
++{
++    switch (offset)
++    {
++    case offsetof (E3_CommandPort, PutDma):   return ("PutDma");
++    case offsetof (E3_CommandPort, GetDma):   return ("GetDma");
++    case offsetof (E3_CommandPort, RunThread):        return ("RunThread");
++    case offsetof (E3_CommandPort, WaitEvent0):       return ("WaitEvent0");
++    case offsetof (E3_CommandPort, WaitEvent1):       return ("WaitEvent1");
++    case offsetof (E3_CommandPort, SetEvent): return ("SetEvent");
++    default:                                  return ("Bad Command");
++    }
++}
++
++int
++IssueCommand (ELAN3_CTXT *ctxt, unsigned cmdoff, E3_Addr value, int cflags)
++{
++    ELAN3_DEV     *dev = ctxt->Device;
++    int                 res;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++
++    if ((! (cflags & ISSUE_COMMAND_FOR_CPROC) && !ELAN3_QUEUE_EMPTY (ctxt->CommandTrapQ)) || (ctxt->Status & CTXT_OTHERS_REASONS))
++    {
++      /*
++       * Cannot issue commands for non-cproc traps if command port is trapped, 
++       * nor if the dma/thread trap queues are full, or we're swapping out
++       */
++      PRINTF2 (ctxt, DBG_CMD, "IssueCommand: %s %08x -> ISSUE_COMMAND_RETRY\n",
++               CommandName (cmdoff), value);
++
++      res = ISSUE_COMMAND_RETRY;
++    }
++    else
++    {
++      PRINTF2 (ctxt, DBG_CMD, "IssueCommand: %s %08x -> ISSUE_COMMAND_OK\n",
++               CommandName (cmdoff), value);
++
++      mb();                                                   /* ensure writes to main memory completed */
++      writel (value, ctxt->CommandPort + cmdoff);             /* issue command */
++      mmiob();                                                /* and flush through IO writes */
++
++      res = ISSUE_COMMAND_OK;
++    }
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++    
++    return (res);
++}
++
++int
++IssueDmaCommand (ELAN3_CTXT *ctxt, E3_Addr value, void *item, int how)
++{
++    ELAN3_DEV     *dev    = ctxt->Device;
++    int                 res;
++    unsigned long flags;
++
++    /*
++     * Since we may be issuing a command that could trap, and we're interested in
++     * the outcome, the command port trap resolving code must be locked out.
++     */
++    kmutex_lock (&ctxt->CmdLock);
++    spin_lock_irqsave (&dev->IntrLock, flags);
++
++    if ((! (how & ISSUE_COMMAND_FOR_CPROC) && !ELAN3_QUEUE_EMPTY (ctxt->CommandTrapQ)) || (ctxt->Status & CTXT_OTHERS_REASONS))
++    {
++      PRINTF2 (ctxt, DBG_CMD, "IssueDmaCommand: PutDma %08x [%p] -> ISSUE_COMMAND_RETRY\n", value, item);
++
++      /*
++       * Cannot issue commands for non-cproc traps if command port is trapped, 
++       * nor if the dma/thread trap queues are full, or we're swapping out
++       */
++      spin_unlock_irqrestore (&dev->IntrLock, flags);
++      kmutex_unlock (&ctxt->CmdLock);
++      return (ISSUE_COMMAND_RETRY);
++    }
++    
++    ASSERT (item == NULL || ctxt->CommandPortItem == NULL);
++
++    /*
++     * Stop the DMA processor from removing entries from the 
++     * command port, and force the command processor to do this.
++     * This means that if a trap occurs then it will be the command
++     * processor that traps.
++     */
++    if (dev->HaltDmaDequeueCount++ == 0)
++      SetSchedStatusRegister (dev, 0, NULL);
++
++    PRINTF2 (ctxt, DBG_CMD, "IssueDmaCommand: PutDma %08x [%p]\n", value, item);
++
++    /*
++     * Always issue the DMA to the 'write' command,  since we've asserted HaltDmaDequeue
++     * the command processor will read the descriptor and transfer it to the run queue. 
++     * The command processor looks at the dma_direction field to determine whether it is
++     * a read or a write and whether to alter the dma_souce of the descriptr on the run 
++     * queue
++     */
++    mb();                                                     /* ensure writes to main memory ccompleted */
++    writel (value, ctxt->CommandPort + offsetof (E3_CommandPort, PutDma));
++    mmiob();                                                  /* and flush through IO writes */
++    
++    res = CheckCommandQueueFlushed (ctxt, DmaComQueueNotEmpty, how, &flags);
++
++    if (res == ISSUE_COMMAND_TRAPPED)
++    {
++      PRINTF2 (ctxt, DBG_CMD, "IssueDmaCommand: PutDma %08x [%p] -> ISSUE_COMMAND_TRAPPED\n", value, item);
++      /*
++       * Remember the item we're issueing so that if the command port traps the item will not
++       * get freed off until the descriptor has been read after the command trap has been fixed
++       * up.
++       */
++      if (item != NULL)
++          ctxt->CommandPortItem = item;
++    }
++
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++    kmutex_unlock (&ctxt->CmdLock);
++
++    return (res);
++}
++
++int
++WaitForDmaCommand (ELAN3_CTXT *ctxt, void *item, int how)
++{
++    ELAN3_DEV     *dev = ctxt->Device;
++    int           res;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++
++    res = CheckCommandQueueFlushed (ctxt, DmaComQueueNotEmpty, how, &flags);
++
++    if (res == ISSUE_COMMAND_TRAPPED && item != NULL)
++      ctxt->CommandPortItem = item;
++
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++    
++    return (res);
++}
++
++void
++FixupEventTrap (ELAN3_CTXT *ctxt, int proc, void *trap, E3_uint32 TrapType, E3_FaultSave_BE *FaultSaveArea, int flags)
++{
++    ASSERT (! CTXT_IS_KERNEL (ctxt));
++
++    /*
++     * This code re-issues the part of the set event that trapped.
++     */
++    switch (TrapType)
++    {
++    case MI_ChainedEventError:
++      ElanException (ctxt, EXCEPTION_CHAINED_EVENT, proc, trap, FaultSaveArea->s.EventAddress);
++      break;
++      
++
++    case MI_SetEventReadWait:
++      /*
++       * Fault occured on the read for the event location. Just re-issue
++       * setevent using EventAddress in E3_FaultSave
++       */
++      PRINTF1 (ctxt, DBG_EVENT, "FixupEventTrap: MI_SetEventReadWait: re-issuing setevent %08x\n", 
++               FaultSaveArea->s.EventAddress);
++      
++      ReissueEvent (ctxt, (E3_Addr) FaultSaveArea->s.EventAddress, flags);
++      break;
++
++    case MI_DoSetEvent:
++    {
++      /*
++       * Fault occured because the block write of a block copy event trapped.
++       * Must grab the event type, source and dest then simulate the block copy and then
++       * perform the set. Once the block copy is started the event location cannot be read
++       * again.
++       */
++      E3_Event *EventPtr  = (E3_Event *) elan3mmu_mainaddr (ctxt->Elan3mmu, FaultSaveArea->s.EventAddress);
++      E3_uint32 EventType = fuword (&EventPtr->ev_Type);
++      
++      /*
++       * Check that the event has the block copy bit
++       * set in it,  since we couldn't trap here if it
++       * didn't
++       */
++      if ((EventType & EV_TYPE_BCOPY) != EV_TYPE_BCOPY)
++      {
++          PRINTF1 (ctxt, DBG_EVENT, "FixupEventTrap: MI_DoSetEvent: Unexpected type=%x\n", EventType);
++          ElanException (ctxt, EXCEPTION_BAD_EVENT, proc, trap, FaultSaveArea, TrapType);
++          break;
++      }
++      
++      PRINTF1 (ctxt, DBG_EVENT, "FixupEventTrap: MI_DoSetEvent: RunEventType %x\n", EventType);
++
++      if (RunEventType (ctxt, FaultSaveArea, EventType))
++          ElanException (ctxt, EXCEPTION_BAD_EVENT, proc, trap, FaultSaveArea, TrapType);
++
++      break;
++    }
++    
++    case MI_ThreadUpdateNonSysCntxBack:
++    case MI_ThreadUpdateSysCntxBack:
++    {
++      /*
++       * Fault occured because the block write of a block copy event trapped.
++       * Must grab the event type, source and dest then simulate the block copy and then
++       * run the thread. Once the block copy is started the event location cannot be read
++       * again.
++       */
++      E3_Event *EventPtr = (E3_Event *) elan3mmu_mainaddr (ctxt->Elan3mmu, FaultSaveArea->s.EventAddress);
++      E3_uint32 EventType = fuword (&EventPtr->ev_Type);
++
++      /*
++       * Check for the correct EventPtr type
++       */
++      if ((EventType & (EV_TYPE_MASK_THREAD|EV_TYPE_MASK_BCOPY)) != (EV_TYPE_BCOPY | EV_TYPE_THREAD))
++      {
++          PRINTF1 (ctxt, DBG_EVENT, "FixupEventTrap: MI_ThreadUpdateCntx0Back: Unexpected type=%x for setevent trap. Should be thread\n", EventType);
++          ElanException (ctxt, EXCEPTION_BAD_EVENT, proc, trap, FaultSaveArea, TrapType);
++          break;
++      }
++      
++      PRINTF1 (ctxt, DBG_EVENT, "FixupEventTrap: MI_ThreadUpdateCntx0Back: RunEventType %x\n", EventType);
++      if (RunEventType (ctxt, FaultSaveArea, EventType))
++          ElanException (ctxt, EXCEPTION_BAD_EVENT, proc, trap, FaultSaveArea, TrapType);
++      break;
++    }
++    
++    case MI_EventIntUpdateBPtr:
++    {
++      /*
++       * Fault occured because the block write of a block copy event trapped.
++       * Must grab the event type, source and dest then simulate the block copy and then
++       * run the dma. Once the block copy is started the event location cannot be read
++       * again.
++       */
++      E3_Event *EventPtr = (E3_Event *) elan3mmu_mainaddr (ctxt->Elan3mmu, FaultSaveArea->s.EventAddress);
++      E3_uint32 EventType = fuword (&EventPtr->ev_Type);
++
++      /*
++       * Check for the correct EventPtr type
++       */
++      if ((EventType & (EV_TYPE_MASK_EVIRQ|EV_TYPE_MASK_BCOPY)) != (EV_TYPE_BCOPY | EV_TYPE_EVIRQ))
++      {
++          PRINTF1 (ctxt, DBG_EVENT, "FixupEventTrap: MI_EventIntUpdateBPtr: Unexpected type=%x\n", EventType);
++          ElanException (ctxt, EXCEPTION_BAD_EVENT, proc, trap, FaultSaveArea, TrapType);
++          break;
++      }
++
++      PRINTF1 (ctxt, DBG_EVENT, "FixupEventTrap: MI_EventIntUpdateBPtr: RunEventType %x\n", EventType);
++      if (RunEventType(ctxt, FaultSaveArea, EventType))
++          ElanException (ctxt, EXCEPTION_BAD_EVENT, proc, trap, FaultSaveArea, TrapType);
++      break;
++    }
++    
++    case MI_RunDmaDesc:
++    {
++      /*
++       * Fault occured because the block write of a block copy event trapped.
++       * Must grab the event type, source and dest then simulate the block copy and then
++       * run the dma. Once the block copy is started the event location cannot be read
++       * again.
++       */
++      E3_Event *EventPtr = (E3_Event *) elan3mmu_mainaddr (ctxt->Elan3mmu, FaultSaveArea->s.EventAddress);
++      E3_uint32 EventType = fuword (&EventPtr->ev_Type);
++
++      /*
++       * Check for the correct EventPtr type
++       */
++      if ((EventType & (EV_TYPE_MASK_DMA|EV_TYPE_MASK_BCOPY)) != (EV_TYPE_BCOPY | EV_TYPE_DMA))
++      {
++          PRINTF1 (ctxt, DBG_EVENT, "FixupEventTrap: MI_RunDmaDesc: Unexpected type=%x\n", EventType);
++          ElanException (ctxt, EXCEPTION_BAD_EVENT, proc, trap, FaultSaveArea, TrapType);
++          break;
++      }
++
++      PRINTF1 (ctxt, DBG_EVENT, "FixupEventTrap: MI_RunDmaDesc: RunEventType %x\n", EventType);
++      if (RunEventType(ctxt, FaultSaveArea, EventType))
++          ElanException (ctxt, EXCEPTION_BAD_EVENT, proc, trap, FaultSaveArea, TrapType);
++      break;
++    }
++    
++    case MI_WaitForCntxDmaDescRead:
++    case MI_WaitForNonCntxDmaDescRead:
++      /*
++       * Fault occured on the read of the dma descriptor. Run dma using the
++       * Fault Address in FaultSave.
++       */
++      PRINTF1 (ctxt, DBG_EVENT, "FixupEventTrap: MI_WaitForCntxDmaDescRead: re-issue dma at %08x\n", FaultSaveArea->s.FaultAddress);
++      
++      RestartDmaPtr (ctxt, FaultSaveArea->s.FaultAddress);
++      break;
++    
++    case MI_FinishedSetEvent:
++      /*
++       * Fault occured because the block write of a block copy event trapped.
++       * Simulate the block copy.
++       */
++      if (SimulateBlockCopy (ctxt, FaultSaveArea->s.EventAddress))
++          ElanException (ctxt, EXCEPTION_BAD_EVENT, proc, trap, FaultSaveArea, TrapType);
++      break;
++      
++    case MI_BlockCopyEvent:
++    case MI_BlockCopyWaitForReadData:
++    {
++      /*
++       * Fault occured on the read or write of the data for a block copy
++       * event. Simulate the block copy using EventAddress in E3_FaultSave. Must also sample
++       * the event type and then perform a run.
++       */
++      E3_Event *EventPtr = (E3_Event *) elan3mmu_mainaddr (ctxt->Elan3mmu, FaultSaveArea->s.EventAddress);
++      E3_uint32 EventType = fuword (&EventPtr->ev_Type);
++
++      PRINTF0 (ctxt, DBG_EVENT, "FixupEventTrap: MI_BlockCopyWaitForReadData: BCopy read fault in BCopy event. Simulating BCopy.\n");
++      
++      if (RunEventType(ctxt, FaultSaveArea, EventType))
++          ElanException (ctxt, EXCEPTION_BAD_EVENT, proc, trap, FaultSaveArea, TrapType);
++      break;
++    }
++    
++    case MI_EventQueueOverflow:
++    case MI_ThreadQueueOverflow:
++    case MI_DmaQueueOverflow:
++      /* XXXX: should handle queue overflow */
++      PRINTF0 (ctxt, DBG_EVENT, "FixupEventTrap: Queue overflow\n");
++
++      ElanException (ctxt, EXCEPTION_QUEUE_OVERFLOW, proc, trap, FaultSaveArea, TrapType);
++      break;
++
++    default:
++      ElanException (ctxt, EXCEPTION_BUS_ERROR, proc, trap, FaultSaveArea, TrapType);
++      break;
++    }
++}
++
++int
++SimulateBlockCopy (ELAN3_CTXT *ctxt, E3_Addr EventAddress)
++{
++    E3_Addr  SourcePtrElan;
++    E3_Addr  DestPtrElan;
++    unsigned DataType;
++    int      i;
++
++    if (ELAN3_OP_START_FAULT_CHECK (ctxt))
++    {
++      ELAN3_OP_END_FAULT_CHECK (ctxt);
++
++      ElanException (ctxt, EXCEPTION_FAULTED, EVENT_PROC, NULL, EventAddress);
++      return (TRUE);
++    }
++
++    SourcePtrElan = ELAN3_OP_LOAD32 (ctxt, EventAddress + offsetof (E3_BlockCopyEvent, ev_Source));
++    DestPtrElan   = ELAN3_OP_LOAD32 (ctxt, EventAddress + offsetof (E3_BlockCopyEvent, ev_Dest));
++    DataType      = DestPtrElan & EV_BCOPY_DTYPE_MASK;
++    DestPtrElan  &= ~EV_BCOPY_DTYPE_MASK;
++
++
++    PRINTF3 (ctxt, DBG_EVENT, "SimulateBlockCopy: Event %08x SourcePtr %08x DestPtr %08x\n",
++           EventAddress, SourcePtrElan, DestPtrElan);
++
++    if (SourcePtrElan & EV_WCOPY)
++      ELAN3_OP_STORE32 (ctxt, DestPtrElan, SourcePtrElan);
++    else
++    {
++      /*
++       * NOTE: since the block copy could be to sdram, we issue the writes backwards,
++       *       except we MUST ensure that the last item in the block is written last.
++       */
++#if defined(__LITTLE_ENDIAN__)
++      /*
++       * For little endian cpu's we don't need to worry about the data type.
++       */
++      for (i = E3_BLK_SIZE-(2*sizeof (E3_uint64)); i >= 0; i -= sizeof (E3_uint64))
++          ELAN3_OP_STORE64 (ctxt, DestPtrElan + i, ELAN3_OP_LOAD64 (ctxt, SourcePtrElan + i));
++
++      i = E3_BLK_SIZE - sizeof (E3_uint64);
++      ELAN3_OP_STORE64 (ctxt, DestPtrElan + i, ELAN3_OP_LOAD64 (ctxt, SourcePtrElan + i));
++#else
++      switch (DataType)
++      {
++      case EV_TYPE_BCOPY_BYTE:
++          for (i = E3_BLK_SIZE-(2*sizeof (E3_uint8)); i >= 0; i -= sizeof (E3_uint8))
++              ELAN3_OP_STORE8 (ctxt, DestPtrElan + i, ELAN3_OP_LOAD8 (ctxt, SourcePtrElan + i));
++          
++          i = E3_BLK_SIZE - sizeof (E3_uint8);
++          ELAN3_OP_STORE8 (ctxt, DestPtrElan + i, ELAN3_OP_LOAD8 (ctxt, SourcePtrElan + i));
++          break;
++
++      case EV_TYPE_BCOPY_HWORD: 
++          for (i = E3_BLK_SIZE-(2*sizeof (E3_uint16)); i >= 0; i -= sizeof (E3_uint16))
++              ELAN3_OP_STORE16 (ctxt, DestPtrElan + i, ELAN3_OP_LOAD16 (ctxt, SourcePtrElan + i));
++          
++          i = E3_BLK_SIZE - sizeof (E3_uint16);
++          ELAN3_OP_STORE16 (ctxt, DestPtrElan + i, ELAN3_OP_LOAD16 (ctxt, SourcePtrElan + i));
++          break;
++          
++      case EV_TYPE_BCOPY_WORD:  
++          for (i = E3_BLK_SIZE-(2*sizeof (E3_uint32)); i >= 0; i -= sizeof (E3_uint32))
++              ELAN3_OP_STORE32 (ctxt, DestPtrElan + i, ELAN3_OP_LOAD32 (ctxt, SourcePtrElan + i));
++          
++          i = E3_BLK_SIZE - sizeof (E3_uint32);
++          ELAN3_OP_STORE32 (ctxt, DestPtrElan + i, ELAN3_OP_LOAD32 (ctxt, SourcePtrElan + i));
++          break;
++          
++      case EV_TYPE_BCOPY_DWORD: 
++          for (i = E3_BLK_SIZE-(2*sizeof (E3_uint64)); i >= 0; i -= sizeof (E3_uint64))
++              ELAN3_OP_STORE64 (ctxt, DestPtrElan + i, ELAN3_OP_LOAD64 (ctxt, SourcePtrElan + i));
++          
++          i = E3_BLK_SIZE - sizeof (E3_uint64);
++          ELAN3_OP_STORE64 (ctxt, DestPtrElan + i, ELAN3_OP_LOAD64 (ctxt, SourcePtrElan + i));
++          break;
++      }
++#endif
++    }
++    ELAN3_OP_END_FAULT_CHECK (ctxt);
++
++    return (FALSE);
++}
++
++void
++ReissueEvent (ELAN3_CTXT *ctxt, E3_Addr addr, int flags)
++{
++    PRINTF1 (ctxt, DBG_CMD, "ReissueEvent : Event=%08x\n", addr);
++
++    if (IssueCommand (ctxt, offsetof (E3_CommandPort, SetEvent), addr, flags) == ISSUE_COMMAND_RETRY)
++    {
++      PRINTF1 (ctxt, DBG_CMD, "ReissueEvent: queue event %08x\n", addr);
++
++      kmutex_lock (&ctxt->SwapListsLock);
++      ctxt->ItemCount[LIST_SETEVENT]++;
++      ELAN3_OP_PUT_WORD_ITEM (ctxt, LIST_SETEVENT, addr);
++      kmutex_unlock (&ctxt->SwapListsLock);
++    }
++}
++
++int
++SetEventsNeedRestart (ELAN3_CTXT *ctxt)
++{
++    return (ctxt->ItemCount[LIST_SETEVENT] != 0);
++}
++
++void
++RestartSetEvents (ELAN3_CTXT *ctxt)
++{
++    void     *item;
++    E3_uint32 EventPointer;
++
++    kmutex_lock (&ctxt->SwapListsLock);
++    
++    while (ctxt->ItemCount[LIST_SETEVENT])
++    {
++      if (! ELAN3_OP_GET_WORD_ITEM (ctxt, LIST_SETEVENT, &item, &EventPointer))
++          ctxt->ItemCount[LIST_SETEVENT] = 0;
++      else
++      {
++          if (IssueCommand (ctxt, offsetof (E3_CommandPort, SetEvent), EventPointer, FALSE) == ISSUE_COMMAND_RETRY)
++          {
++              ELAN3_OP_PUTBACK_ITEM (ctxt, LIST_SETEVENT, item);
++              kmutex_unlock (&ctxt->SwapListsLock);
++              return;
++          }
++          
++          ctxt->ItemCount[LIST_SETEVENT]--;
++          ELAN3_OP_FREE_WORD_ITEM (ctxt, item);
++      }
++    }
++    kmutex_unlock (&ctxt->SwapListsLock);
++}
++
++int
++RunEventType(ELAN3_CTXT *ctxt, E3_FaultSave_BE *FaultSaveArea, E3_uint32 EventType)
++{
++    int failed = FALSE;
++
++    if ((EventType & EV_TYPE_BCOPY) != 0)
++      failed = SimulateBlockCopy(ctxt, FaultSaveArea->s.EventAddress);
++    
++    if ((EventType & EV_TYPE_MASK) == EV_TYPE_THREAD)
++      ReissueStackPointer (ctxt, EventType & ~(EV_TYPE_MASK_THREAD|EV_TYPE_MASK_BCOPY));
++    else if ((EventType & EV_TYPE_MASK) == EV_TYPE_DMA)
++      RestartDmaPtr (ctxt, EventType & ~(EV_TYPE_MASK_DMA|EV_TYPE_MASK_BCOPY));
++    else if ((EventType & EV_TYPE_EVIRQ) != 0)
++      QueueEventInterrupt (ctxt, EventType & ~(EV_TYPE_MASK_EVIRQ|EV_TYPE_MASK_BCOPY));
++    else /* Chained event */
++    {
++      if ((EventType & ~EV_TYPE_BCOPY) != 0) /* not null setevent */
++          ReissueEvent (ctxt, EventType & ~(EV_TYPE_MASK_CHAIN|EV_TYPE_MASK_BCOPY), FALSE);
++    }
++
++    return (failed);
++}
++
++void
++WakeupLwp (ELAN3_DEV *dev, void *arg)
++{
++    ELAN3_CTXT    *ctxt = (ELAN3_CTXT *) arg;
++    unsigned long flags;
++
++    PRINTF1 (ctxt, DBG_INTR, "WakeupLwp: %d\n", SPINLOCK_HELD (&dev->IntrLock));
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++    ctxt->Halted = 1;
++    kcondvar_wakeupone (&ctxt->HaltWait, &dev->IntrLock);
++
++    PRINTF0 (ctxt, DBG_INTR, "WakeupLwp: woken up context\n");
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++}
++
++void
++QueueEventInterrupt (ELAN3_CTXT *ctxt, E3_uint32 cookie)
++{
++    ELAN3_DEV     *dev = ctxt->Device;
++    unsigned long flags;
++
++    PRINTF1 (ctxt, DBG_EVENT, "QueueEventInterrupt: cookie %08x\n", cookie);
++
++    if (ELAN3_OP_EVENT (ctxt, cookie, OP_INTR) == OP_DEFER)
++    {
++      spin_lock_irqsave (&ctxt->Device->IntrLock, flags);
++
++      if (ELAN3_QUEUE_REALLY_FULL (ctxt->EventCookieQ))
++      {
++          ctxt->Status |= CTXT_COMMAND_OVERFLOW_ERROR;
++          StartSwapoutContext (ctxt, 0, NULL);
++      }
++      else
++      {
++          *(ELAN3_QUEUE_BACK (ctxt->EventCookieQ, ctxt->EventCookies)) = cookie;
++          
++          ELAN3_QUEUE_ADD (ctxt->EventCookieQ);
++          kcondvar_wakeupone (&ctxt->Wait, &dev->IntrLock);
++          if (ELAN3_QUEUE_FULL (ctxt->EventCookieQ))
++          {
++              ctxt->Status |= CTXT_EVENT_QUEUE_FULL;
++              StartSwapoutContext (ctxt, 0, NULL);
++          }
++      }
++      spin_unlock_irqrestore (&ctxt->Device->IntrLock, flags);
++    }
++}
++
++int
++ElanException (ELAN3_CTXT *ctxt, int type, int proc, void *trap, ...)
++{
++    int     res;
++    va_list ap;
++
++    va_start (ap, trap);
++
++    PRINTF2 (ctxt, DBG_FN, "ElanException: proc %d type %d\n", proc, type);
++
++    res = ELAN3_OP_EXCEPTION (ctxt, type, proc, trap, ap);
++
++    va_end (ap);
++    
++    return (res);
++}
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan3/context_linux.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan3/context_linux.c  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan3/context_linux.c       2005-06-01 23:12:54.566444120 -0400
+@@ -0,0 +1,228 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Limited.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: context_linux.c,v 1.28.2.2 2004/10/28 11:54:56 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/os/context_linux.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <qsnet/kpte.h>
++
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elanvp.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanctxt.h>
++#include <elan3/elandebug.h>
++#include <elan3/urom_addrs.h>
++#include <elan3/thread.h>
++
++int
++LoadElanTranslation (ELAN3_CTXT *ctxt, E3_Addr addr, int len, int protFault, int writeable)
++{
++    ELAN3MMU          *elan3mmu = ctxt->Elan3mmu;
++    ELAN3MMU_RGN      *rgn;
++    caddr_t           mainAddr;
++    int                       perm;
++    unsigned int        off;
++    unsigned long       flags;
++
++    ASSERT (PAGE_ALIGNED (addr) && PAGE_ALIGNED (len));
++
++    PRINTF (ctxt, DBG_FAULT, "LoadElanTranslation: addr %08x len %08x%s%s\n", 
++        addr, len, protFault ? " prot fault" : "", writeable ? " writeable" : "");
++
++    /* Ensure there's enough elan mmu tables for us to use */
++    elan3mmu_expand (elan3mmu, addr, len, PTBL_LEVEL_3, 0);
++
++    while (len > 0) 
++    {
++      /*
++       * Retrieve permission region and calculate main address
++       */
++      spin_lock (&elan3mmu->elan3mmu_lock);
++
++      rgn = elan3mmu_rgnat_elan (elan3mmu, addr);
++      if (rgn == NULL) {
++          PRINTF (ctxt, DBG_FAULT, "LoadElanTranslation: no permission region at %lx %p\n", 
++              (u_long) addr, rgn);
++          spin_unlock (&elan3mmu->elan3mmu_lock);
++          return (EFAULT);
++      }
++      mainAddr = rgn->rgn_mbase + (addr - rgn->rgn_ebase);
++
++      ASSERT (PAGE_ALIGNED ((unsigned long)mainAddr));
++
++      spin_unlock (&elan3mmu->elan3mmu_lock);
++
++      /*
++       * If we're tying to load a translation to the elan command port, 
++       * then don't do it now, but mark the context to have it reloaded
++       * just before we restart any threads. We do this because we don't
++       * want to call into the segment driver since we could then block
++       * waiting for the command port to become available.
++       */
++      if (mainAddr == ctxt->CommandPageMapping)
++      {
++          PRINTF (ctxt, DBG_FAULT, "LoadElanTranslation: addr=%08x maps command port\n", addr);
++
++          spin_lock_irqsave (&ctxt->Device->IntrLock, flags);
++          UnloadCommandPageMapping (ctxt);
++          spin_unlock_irqrestore (&ctxt->Device->IntrLock, flags);
++      }
++      else 
++      {
++          struct vm_area_struct *area;
++          struct mm_struct *mm = current->mm;
++          pte_t *ptep_ptr;
++          pte_t  ptep_value;
++
++          down_read (&current->mm->mmap_sem);
++
++          if ((area = find_vma_intersection(mm, (unsigned long)mainAddr, (unsigned long)mainAddr + PAGESIZE)) == NULL)
++          {
++              PRINTF (ctxt, DBG_FAULT, "LoadElanTranslation: %p no vma\n", mainAddr);
++              up_read (&current->mm->mmap_sem);
++              return EFAULT;
++          }
++
++          if (writeable && !(area->vm_flags & VM_WRITE)) 
++          {
++              PRINTF (ctxt, DBG_FAULT, "LoadElanTranslation: %p not writeable\n", mainAddr);
++              up_read (&current->mm->mmap_sem);
++              return EFAULT;
++          }
++          
++          spin_lock (&mm->page_table_lock);
++
++          /* dont deference the pointer after the unmap */
++          ptep_ptr = find_pte_map (mm, (unsigned long)mainAddr);  
++          if (ptep_ptr) {
++              ptep_value = *ptep_ptr;
++              pte_unmap(ptep_ptr);
++          }
++
++          PRINTF (ctxt, DBG_FAULT, "LoadElanTranslation: %p %s %s\n", 
++                  mainAddr, writeable ? "writeable" : "readonly", 
++                  !ptep_ptr ? "invalid" : pte_none(ptep_value) ? "none " : !pte_present(ptep_value) ? "swapped " : 
++                  writeable && !pte_write(ptep_value) ? "COW" : "OK");
++          
++          if (!ptep_ptr || pte_none(ptep_value) || !pte_present(ptep_value) || (writeable && !pte_write(ptep_value))) 
++          {  
++              spin_unlock (&mm->page_table_lock);
++
++              make_pages_present((unsigned long)mainAddr, (unsigned long)mainAddr + PAGE_SIZE);
++
++              spin_lock (&mm->page_table_lock);
++
++              /* dont deference the pointer after the unmap */
++              ptep_ptr = find_pte_map (mm, (unsigned long)mainAddr);  
++              if (ptep_ptr) {
++                  ptep_value = *ptep_ptr;
++                  pte_unmap(ptep_ptr);
++              }
++
++              if (!ptep_ptr || pte_none(ptep_value) || !pte_present(ptep_value) || (writeable && !pte_write(ptep_value))) 
++              {
++                  spin_unlock (&mm->page_table_lock);
++                  up_read (&current->mm->mmap_sem);
++                  return EFAULT;
++              }
++          } 
++
++          /* don't allow user write access to kernel pages if not kernel */
++          if (!pte_read(ptep_value))
++          {
++              spin_unlock (&mm->page_table_lock);
++              up_read (&current->mm->mmap_sem);
++              return EFAULT;
++          }
++
++          if (writeable)
++              pte_mkdirty(ptep_value);
++          pte_mkyoung (ptep_value);
++
++          /* now load the elan pte */
++          if (writeable)
++              perm  = rgn->rgn_perm;
++          else
++              perm = ELAN3_PERM_READONLY(rgn->rgn_perm & ELAN3_PTE_PERM_MASK) | (rgn->rgn_perm & ~ELAN3_PTE_PERM_MASK);
++
++          for (off = 0; off < PAGE_SIZE; off += ELAN3_PAGE_SIZE)
++              elan3mmu_pteload (elan3mmu, PTBL_LEVEL_3, addr + off, pte_phys(ptep_value) + off, perm, PTE_LOAD | PTE_NO_SLEEP);
++
++          spin_unlock (&mm->page_table_lock);
++          up_read (&current->mm->mmap_sem);
++      }
++
++      len -= PAGESIZE;
++      addr += PAGESIZE;
++    }
++    return (ESUCCESS);
++}
++
++
++/*
++ * LoadCommandPortTranslation:
++ *    explicitly load an elan translation to the command port.
++ *    but only do it if the command port is accessible.
++ *
++ *    we call this function just after we have restarted
++ *    and trapped commands,  since when a command traps
++ *    the elan translation to the command port is unloaded.
++ */
++void
++LoadCommandPortTranslation (ELAN3_CTXT *ctxt)
++{
++    ELAN3MMU     *elan3mmu = ctxt->Elan3mmu;
++    ELAN3MMU_RGN *rgn;
++    E3_Addr       addr;
++    int                 perm;
++    physaddr_t    phys;
++    unsigned int  off;
++    unsigned long flags;
++
++    PRINTF (ctxt, DBG_FAULT, "LoadCommandPortTranslation: SegAddr=%p Status=%x\n", ctxt->CommandPageMapping, ctxt->Status);
++
++    if (ctxt->CommandPageMapping != NULL  && !(ctxt->Status & CTXT_COMMAND_MAPPED_ELAN))
++    {
++      spin_lock (&elan3mmu->elan3mmu_lock);
++      
++      rgn = elan3mmu_rgnat_main (elan3mmu, ctxt->CommandPageMapping);
++      if (rgn == (ELAN3MMU_RGN *) NULL) 
++      {
++          PRINTF(ctxt, DBG_FAULT, "LoadCommandPortTranslation: no permission for command port\n");
++          spin_unlock (&elan3mmu->elan3mmu_lock);
++          return;
++      }
++      
++      addr = rgn->rgn_ebase + (ctxt->CommandPageMapping - rgn->rgn_mbase);
++      perm = rgn->rgn_perm;
++      phys = kmem_to_phys((caddr_t) ctxt->CommandPage);
++
++      spin_lock_irqsave (&ctxt->Device->IntrLock, flags);
++      if (ELAN3_QUEUE_EMPTY(ctxt->CommandTrapQ) && !(ctxt->Status & CTXT_OTHERS_REASONS))
++      {
++          PRINTF(ctxt, DBG_FAULT, "LoadCommandPortTranslation: load xlation addr=%08x phys=%llx perm=%d\n", 
++                 addr, (unsigned long long)phys, perm);
++
++          ctxt->Status |= CTXT_COMMAND_MAPPED_ELAN;
++
++          for (off = 0; off < PAGESIZE; off += ELAN3_PAGE_SIZE)
++              elan3mmu_pteload (elan3mmu, PTBL_LEVEL_3, addr + off, phys + off, perm, PTE_LOAD | PTE_NO_SLEEP);
++      }
++      spin_unlock_irqrestore (&ctxt->Device->IntrLock, flags);
++      
++      spin_unlock (&elan3mmu->elan3mmu_lock);
++    }
++}
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan3/cproc.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan3/cproc.c  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan3/cproc.c       2005-06-01 23:12:54.567443968 -0400
+@@ -0,0 +1,539 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: cproc.c,v 1.46 2004/02/10 15:05:10 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/os/cproc.c,v $ */
++
++
++#include <qsnet/kernel.h>
++
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elanvp.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanctxt.h>
++#include <elan3/elandebug.h>
++#include <elan3/urom_addrs.h>
++#include <elan3/vmseg.h>
++
++void
++HandleCProcTrap (ELAN3_DEV *dev, E3_uint32 Pend, E3_uint32 *Maskp)
++{
++    E3_FaultSave_BE     FaultSave;
++    CProcTrapBuf_BE   TrapBuf;
++    COMMAND_TRAP       *trap;
++    ELAN3_CTXT               *ctxt;
++    sdramaddr_t         CurrTrap;
++    sdramaddr_t         LastTrapAddr;
++    int               NTrapEntries;
++    int                       NewPend;
++    unsigned long       flags;
++
++    /* 
++     * Temporarily mask out the command processor interrupt, since
++     * we may cause it be re-asserted when we re-issue the commands
++     * from the overflow queue area.
++     */
++    DISABLE_INT_MASK (dev, INT_CProc | INT_ComQueue);
++
++    NewPend = read_reg32 (dev, Exts.InterruptReg);
++
++    do {
++      if (NewPend & INT_ComQueue)
++      {
++          if ((read_reg32 (dev, ComQueueStatus) & ComQueueError) != 0)
++          {
++              printk ("elan%d: InterruptReg=%x ComQueueStatus=%x\n", dev->Instance,
++                      read_reg32 (dev, Exts.InterruptReg), read_reg32 (dev, ComQueueStatus));
++              panic ("elan: command queue has overflowed !!");
++              /* NOTREACHED */
++          }
++
++          BumpStat (dev, ComQueueHalfFull);
++
++          /*
++           * Capture the other cpus and stop the threads processor then
++           * allow the command processor to eagerly flush the command queue.
++           */
++          dev->FlushCommandCount++; dev->HaltThreadCount++;
++          SetSchedStatusRegister (dev, Pend, Maskp);
++
++          CAPTURE_CPUS();
++
++          while ((read_reg32 (dev, ComQueueStatus) & ComQueueNotEmpty) != 0)
++              mb();
++          
++          /*
++           * Let the threads processor run again, and release the cross call.
++           */
++          RELEASE_CPUS();
++
++          dev->FlushCommandCount--; dev->HaltThreadCount--;
++          SetSchedStatusRegister (dev, Pend, Maskp);
++
++          /*
++           * Re-sample the interrupt register to see if the command processor
++           * has trapped while flushing the queue.  Preserve the INT_ComQueue
++           * bit, so we can clear the ComQueueStatus register later.
++           */
++          NewPend = (read_reg32 (dev, Exts.InterruptReg) | INT_ComQueue);
++      }
++      
++      CurrTrap = dev->CommandPortTraps[dev->CurrentCommandPortTrap];
++      
++      if (NewPend & INT_CProc)
++      {
++          BumpStat (dev, CProcTraps);
++
++          /*
++           * Copy the MMU Fault Save area and zero it out for future traps.
++           */
++          elan3_sdram_copyq_from_sdram (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, CProc), &FaultSave, sizeof (E3_FaultSave));
++          elan3_sdram_zeroq_sdram      (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, CProc), sizeof (E3_FaultSave));
++
++          /*
++           * First entry in the cproc trap save area is the value of Areg and Breg for the
++           * uWord before the address fault.
++           */
++          TrapBuf.Align64 = elan3_sdram_readq (dev, CurrTrap); CurrTrap += sizeof (TrapBuf.Align64);
++
++          ctxt = ELAN3_DEV_CTX_TABLE(dev, (TrapBuf.r.Breg >> 16));
++          if (ctxt == NULL)
++          {
++              PRINTF2 (DBG_DEVICE, DBG_INTR, "HandleCProcTrap: context invalid [%08x.%08x]\n", TrapBuf.r.Areg, TrapBuf.r.Breg);
++              BumpStat (dev, InvalidContext);
++          }
++          else
++          {
++              if (ELAN3_QUEUE_REALLY_FULL (ctxt->CommandTrapQ))
++              {
++                  if ((ctxt->Status & CTXT_COMMAND_OVERFLOW_ERROR) == 0)
++                  {
++                      ctxt->Status |= CTXT_COMMAND_OVERFLOW_ERROR;
++                      StartSwapoutContext (ctxt, Pend, Maskp);
++                  }
++              }
++              else
++              {
++                  trap = ELAN3_QUEUE_BACK (ctxt->CommandTrapQ, ctxt->CommandTraps);
++                  
++                  trap->FaultSave     = FaultSave;
++                  trap->Status.Status = read_reg32 (dev, Exts.CProcStatus.Status);
++                  trap->TrapBuf       = TrapBuf;
++                  
++                  /*
++                   * The command processor does not stop after it has trapped. It will continue
++                   * to save commands for other contexts into the commands port save area.
++                   * The valid context for the trap is held in FaultSave. As some of this
++                   * trap code uses the context in the status register the local copy must be
++                   * updated with the trap context.
++                   */
++                  trap->Status.s.Context = (TrapBuf.r.Breg >> 16);
++                  
++                  PRINTF4 (ctxt, DBG_INTR, "HandleCProcTrap: WakeupFnt=%x Cntx=%x SuspAddr=%x TrapType=%s\n",
++                           trap->Status.s.WakeupFunction, trap->Status.s.Context,
++                           trap->Status.s.SuspendAddr, MiToName(trap->Status.s.TrapType));
++                  PRINTF2 (ctxt, DBG_INTR, "HandleCProcTrap: Areg=%08x Breg=%08x\n", 
++                           trap->TrapBuf.r.Areg, trap->TrapBuf.r.Breg);
++                  
++                  if (ELAN3_OP_CPROC_TRAP (ctxt, trap) == OP_DEFER)
++                  {
++                      ELAN3_QUEUE_ADD (ctxt->CommandTrapQ);
++                      
++                      PRINTF1 (ctxt, DBG_INTR, "HandleCProcTrap: setting Command Flag at %p to 1\n", &ctxt->FlagPage->CommandFlag);
++                      
++                      ctxt->FlagPage->CommandFlag = 1;
++                      
++                      kcondvar_wakeupone (&ctxt->Wait, &dev->IntrLock);
++                  }
++              }
++
++              UnloadCommandPageMapping (ctxt);
++          }
++      }
++      
++      /*
++       * Now change the CommandPortTrap queue.
++       * Must stop the command processor, wait for it to stop, find the final
++       * entry in the current cproc trap save area, reset the comm port
++       * trap save address to the other queue, clear the command port interrupt and
++       * set it running normally again, and then let it go again. This is not very
++       * time critical but it would be a good idea to prevent a higher priority
++       * interrupt from slowing down the process to prevent to fifos filling.
++       */
++      spin_lock_irqsave (&dev->CProcLock, flags);
++
++      SET_SCHED_STATUS (dev, CProcStop);
++
++      while ((read_reg32 (dev, Exts.SchCntReg) & CProcStopped) == 0)
++      {
++          PRINTF0 (DBG_DEVICE, DBG_INTR, "HandleCProcTrap: waiting for command processor to stop\n");
++          mb();
++      }
++      
++      /*
++       * Remember how many entries are in the saved command queue,  and 
++       * re-initialise it, before restarting the command processor.
++       */
++      NTrapEntries = (read_reg32 (dev, CProc_TrapSave_Addr) - dev->CommandPortTraps[dev->CurrentCommandPortTrap])/sizeof (E3_uint64);
++      LastTrapAddr = dev->CommandPortTraps[dev->CurrentCommandPortTrap] + NTrapEntries*sizeof (TrapBuf);
++
++      dev->CurrentCommandPortTrap ^= 1;
++      write_reg32 (dev, CProc_TrapSave_Addr, dev->CommandPortTraps[dev->CurrentCommandPortTrap]);
++
++      PRINTF1 (DBG_DEVICE, DBG_INTR, "HandleCProcTrap: command trap queue has %d entries\n", NTrapEntries);
++
++      if (NTrapEntries > ELAN3_COMMAND_TRAP_SIZE/sizeof (E3_uint64))
++          panic ("HandleCProcTrap: command trap queue has overflowed\n");
++      
++      if (NewPend & INT_CProc)
++      {
++          /*
++           * Clear the CProc interrupt and set it running normally again. Nothing should
++           * be running now that could issue commands apart from this trap handler.
++           */
++          PULSE_SCHED_STATUS (dev, RestartCProc);
++      }
++      
++      if (NewPend & INT_ComQueue)
++      {
++          /*
++           * Write any value here to clear out the half full and error bits of the command
++           * overflow queues. This will also remove the overflow interrupt.
++           */
++          write_reg32 (dev, ComQueueStatus, 0);
++      }
++      
++      /*
++       * And let the command processor start again
++       */
++      CLEAR_SCHED_STATUS (dev, CProcStop);
++      
++      /*
++       * Now re-issue all the commands that were issued after the command port trapped.
++       * Should halt the dma processor and force command sto be put onto the run queues
++       * to ensure that a remote re-issued command is handled correctly. NOTE it is
++       * not necessary to wait for the dma processor to stop and this will reduce the
++       * performance impact. As CProcHalt is asserted all commands will be flushed
++       * to the queues.
++       */
++      dev->HaltDmaDequeueCount++; dev->FlushCommandCount++;
++      SetSchedStatusRegister (dev, Pend, Maskp);
++      
++      /*
++       * XXXX: should we do a capture/release if the trap overflow
++       *       area has a "large" number of commands in it,  since
++       *       we will just stuff them all back in, together with 
++       *       all those issued by the other cpus/thread processors.
++       */
++      while (CurrTrap != LastTrapAddr)
++      {
++          /* Read the next saved (but not trapped) command */
++          TrapBuf.Align64 = elan3_sdram_readq (dev, CurrTrap); CurrTrap += sizeof (TrapBuf);
++          
++
++          ctxt = ELAN3_DEV_CTX_TABLE(dev, (TrapBuf.s.ContextType >> 16));
++          
++          if (ctxt == NULL)
++          {
++              PRINTF1 (DBG_DEVICE, DBG_INTR, "HandleCProcTrap: context %x invalid\n", TrapBuf.s.ContextType >> 16);
++              BumpStat (dev, InvalidContext);
++          }
++          else
++          {
++              if (!ELAN3_QUEUE_EMPTY (ctxt->CommandTrapQ) || (ctxt->Status & CTXT_OTHERS_REASONS))
++              {
++                  PRINTF3 (ctxt, DBG_INTR, "HandleCProcTrap: save command %x context %x - %08x\n",
++                           (TrapBuf.s.ContextType>>3) & 0x3ff, TrapBuf.s.ContextType >> 17, TrapBuf.s.Addr);
++                  
++                  if (ELAN3_QUEUE_REALLY_FULL (ctxt->CommandQ))
++                  {
++                      ctxt->Status |= CTXT_COMMAND_OVERFLOW_ERROR;
++                      StartSwapoutContext (ctxt, Pend, Maskp);
++                  }
++                  else
++                  {
++                      *ELAN3_QUEUE_BACK(ctxt->CommandQ, ctxt->Commands) = TrapBuf;
++
++                      ELAN3_QUEUE_ADD (ctxt->CommandQ);
++                  }
++                  continue;
++              }
++              
++              /* Reissue the command to the command port for this context */
++              PRINTF2 (ctxt, DBG_INTR, "HandleCProcTrap: re-issue command %x - %08x\n",
++                       (TrapBuf.s.ContextType>>5) & 0xff, TrapBuf.s.Addr);
++              
++              mb();
++              if (ELAN3_OP_CPROC_REISSUE(ctxt, &TrapBuf) != OP_HANDLED)
++                  ((E3_uint32 *) ctxt->CommandPort)[(TrapBuf.s.ContextType>>5) & 0xff] = TrapBuf.s.Addr;
++              mmiob();
++          }
++      }
++      
++      while ((read_reg32 (dev, ComQueueStatus) & ComQueueNotEmpty) != 0)
++      {
++          PRINTF0 (DBG_DEVICE, DBG_INTR, "HandleCProcTrap: waiting for queues to empty after reissueing commands\n");
++          mb();
++      }
++      
++      dev->HaltDmaDequeueCount--; dev->FlushCommandCount--;
++      SetSchedStatusRegister (dev, Pend, Maskp);
++      
++      spin_unlock_irqrestore (&dev->CProcLock, flags);
++
++      /*
++       * Re-read the interrupt register and see if we've got another command
++       * port interrupt
++       */
++      NewPend = read_reg32 (dev, Exts.InterruptReg);
++    } while ((NewPend & (INT_CProc | INT_ComQueue)) != 0);
++
++
++    /*
++     * Re-enable the command processor interrupt as we've finished 
++     * polling it.
++     */
++    ENABLE_INT_MASK (dev, INT_CProc | INT_ComQueue);
++}
++
++void
++ResolveCProcTrap (ELAN3_CTXT *ctxt)
++{
++    ELAN3_DEV     *dev = ctxt->Device;
++    COMMAND_TRAP *trap;
++    int                 res;
++    unsigned long flags;
++
++    kmutex_lock (&ctxt->CmdLock);
++    spin_lock_irqsave (&dev->IntrLock, flags);
++
++    while (! ELAN3_QUEUE_BACK_EMPTY (ctxt->CommandTrapQ))
++    {
++      trap = ELAN3_QUEUE_MIDDLE(ctxt->CommandTrapQ, ctxt->CommandTraps);
++      spin_unlock_irqrestore (&dev->IntrLock, flags);
++
++      switch (trap->Status.s.TrapType)
++      {
++      case MI_EventIntUpdateBPtr:
++      case MI_ChainedEventError:
++      case MI_EventQueueOverflow:
++      case MI_ThreadQueueOverflow:
++      case MI_DmaQueueOverflow:
++          PRINTF1 (ctxt, DBG_CPROC, "ResolveCProcTrap: %s\n", MiToName (trap->Status.s.TrapType));
++          break;
++          
++      default:
++          /* All other traps are MMU related, we should have a fault address and FSR */
++          if ((res = elan3_pagefault (ctxt, &trap->FaultSave, 1)) != ESUCCESS)
++          {
++              PRINTF1 (ctxt, DBG_CPROC, "ResolveCProcTrap: elan3_pagefault failed for address %08x\n", 
++                       trap->FaultSave.s.FaultAddress);
++              ElanException (ctxt, EXCEPTION_INVALID_ADDR, COMMAND_PROC, trap, &trap->FaultSave, res);
++              
++              /* Set the trap type to 0 so the command does not get re-issued */
++              trap->Status.s.TrapType = 0;
++          }
++          break;
++      }
++      
++      spin_lock_irqsave (&dev->IntrLock, flags);
++
++      ELAN3_QUEUE_CONSUME (ctxt->CommandTrapQ);
++    }
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++    kmutex_unlock (&ctxt->CmdLock);
++}
++
++int
++RestartCProcTrap (ELAN3_CTXT *ctxt)
++{
++    ELAN3_DEV     *dev      = ctxt->Device;
++    COMMAND_TRAP  trap;
++    void       *item;
++    int                 res;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++
++    while (! ELAN3_QUEUE_FRONT_EMPTY (ctxt->CommandTrapQ))
++    {
++      trap = (*ELAN3_QUEUE_FRONT (ctxt->CommandTrapQ, ctxt->CommandTraps));
++      ELAN3_QUEUE_REMOVE (ctxt->CommandTrapQ);
++      spin_unlock_irqrestore (&dev->IntrLock, flags);
++      
++      BumpUserStat (ctxt, CProcTraps);
++
++      switch (trap.Status.s.TrapType)
++      {
++      case 0:
++          res = ISSUE_COMMAND_OK;
++          break;
++          
++      case MI_WaitForWaitEventDesc:
++          /*
++           * Fault occured on the read of wait event descriptor for wait event type 0.
++           * Fault already fixed. Just re-issue the wait command. Wait event descriptor addr
++           * is in the Areg save value.
++           */
++          PRINTF1 (ctxt, DBG_CPROC, "RestartCProcTrap: WaitEvent type0 desc read fault %08x\n", 
++                   trap.TrapBuf.r.Areg);
++          
++          res = IssueCommand (ctxt, offsetof (E3_CommandPort, WaitEvent0), trap.TrapBuf.r.Areg, ISSUE_COMMAND_FOR_CPROC);
++          break;
++
++      case MI_WaitForEventReadTy0:
++          /*
++           * Fault occured on the read of event location for wait event type 0.
++           * Fault already fixed. Just re-issue the wait command. Wait event descriptor addr
++           * is in the Areg save value.
++           */
++          PRINTF1 (ctxt, DBG_CPROC, "RestartCProcTrap: WaitEvent type0 event loc fault %08x\n",
++                   trap.TrapBuf.r.Areg);
++          
++          res = IssueCommand (ctxt, offsetof (E3_CommandPort, WaitEvent0), trap.TrapBuf.r.Areg, ISSUE_COMMAND_FOR_CPROC);
++          break;
++          
++      case MI_WaitForEventReadTy1:
++          /*
++           * Fault occured on the read of the event location for wait event type 1.
++           * Areg has the original ptr and count.
++           * Fault already fixed. Just re-issue the wait command using Areg and context.
++           */
++          PRINTF1 (ctxt, DBG_CPROC, "RestartCProcTrap: WaitEvent type1 event location read fault %08x\n",
++                   trap.TrapBuf.r.Areg);
++          res = IssueCommand (ctxt, offsetof (E3_CommandPort, WaitEvent1), trap.TrapBuf.r.Areg, ISSUE_COMMAND_FOR_CPROC);
++          break;
++          
++      case MI_WaitForCntxDmaDescRead:
++      case MI_WaitForNonCntxDmaDescRead:
++          /*
++           * Fault occured on the read of the dma descriptor. Run dma using the
++           * Fault Address in FaultSave.
++           */
++          PRINTF1 (ctxt, DBG_CPROC, "RestartCProcTrap: MI_WaitForCntxDmaDescRead: re-issue dma at %08x\n", 
++                   trap.FaultSave.s.FaultAddress);
++          
++          res = IssueDmaCommand (ctxt, trap.FaultSave.s.FaultAddress, NULL, ISSUE_COMMAND_FOR_CPROC);
++          break;
++          
++      default:
++          /*
++           * Assume the fault will be fixed by FixupEventTrap.
++           */
++          FixupEventTrap (ctxt, COMMAND_PROC, &trap, trap.Status.s.TrapType, &trap.FaultSave, ISSUE_COMMAND_FOR_CPROC);
++
++          res = ISSUE_COMMAND_OK;
++          break;
++      }
++
++      switch (res)
++      {
++      case ISSUE_COMMAND_OK:                                  /* command re-issued ok*/
++          break;
++
++      case ISSUE_COMMAND_TRAPPED:                             /* command trapped,  it will have been copied */
++          return (EAGAIN);                                    /* to the back of the trap queue */
++
++      case ISSUE_COMMAND_RETRY:                               /* didn't issue command, so place back at front for */
++          spin_lock_irqsave (&dev->IntrLock, flags);          /* later (after resolving other traps */
++
++          if (ELAN3_QUEUE_REALLY_FULL (ctxt->CommandTrapQ))
++              ctxt->Status |= CTXT_COMMAND_OVERFLOW_ERROR;
++          else
++          {
++              ELAN3_QUEUE_ADD_FRONT(ctxt->CommandTrapQ);
++              (*ELAN3_QUEUE_FRONT (ctxt->CommandTrapQ, ctxt->CommandTraps)) = trap;
++          }
++          spin_unlock_irqrestore (&dev->IntrLock, flags);
++          return (EAGAIN);
++
++      default:
++          return (EINVAL);
++      }
++      spin_lock_irqsave (&dev->IntrLock, flags);
++    } 
++
++    /*
++     * GNAT 5409 - if CommandPortItem was not NULL, but other reasons were set,
++     *             then we'd not free the CommandPortItem even though we'd re-
++     *             issued all trapped and overflowed commands.  Hence only return
++     *             without clearing CommandPortItem if we will be called again as
++     *             either CommandTrapQ or CommandQ is not empty.
++     */
++
++    /* Now run the overflowed commands for this context */
++    if (! ELAN3_QUEUE_EMPTY (ctxt->CommandQ))
++    {
++      if (! ELAN3_QUEUE_EMPTY (ctxt->CommandTrapQ) || (ctxt->Status & CTXT_OTHERS_REASONS))
++      {
++          PRINTF0 (ctxt, DBG_CPROC, "RestartCProcTrap: cannot issue overflowed commands\n");
++          spin_unlock_irqrestore (&dev->IntrLock, flags);
++          return (EAGAIN);
++      }
++
++      /*
++       * Just re-issue the commands,  if one traps then the remainder will 
++       * just get placed in the overflow queue again and the interrupt handler
++       * will copy them back in here.
++       *
++       * Stop the dma processor from taking commands,  since one of the commands
++       * could be a re-issued remote dma, which must be processed by the command
++       * processor.
++       */
++      
++      if (dev->HaltDmaDequeueCount++ == 0)
++          SetSchedStatusRegister (dev, 0, NULL);
++      
++      while (! ELAN3_QUEUE_EMPTY (ctxt->CommandQ))
++      {
++          CProcTrapBuf_BE *TrapBuf = ELAN3_QUEUE_FRONT (ctxt->CommandQ, ctxt->Commands);
++          
++          PRINTF2 (ctxt, DBG_CPROC, "RestartCProcTrap: re-issue command %x - %08x\n",
++                   (TrapBuf->s.ContextType>>5) & 0xff, TrapBuf->s.Addr);
++          mb();                                                       /* ensure writes to main memory completed */
++          ((E3_uint32 *) ctxt->CommandPort)[(TrapBuf->s.ContextType>>5) & 0xff] = TrapBuf->s.Addr;
++          mmiob();                                            /* and flush through IO writes */
++          
++          ELAN3_QUEUE_REMOVE (ctxt->CommandQ);
++      }
++      
++      /* observe the command processor having halted */
++      res = CheckCommandQueueFlushed (ctxt, DmaComQueueNotEmpty, 0, &flags);
++      
++      if (res != ISSUE_COMMAND_OK)
++      {
++          PRINTF0 (ctxt, DBG_CPROC, "RestartCProcTrap: trapped after issueing overflowed commands\n");
++          spin_unlock_irqrestore (&dev->IntrLock, flags);
++          return (EAGAIN);
++      }
++    }
++
++    /* remove the command port item, while holding the lock */
++    item = ctxt->CommandPortItem;
++    ctxt->CommandPortItem = NULL;
++
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++      
++    if (item != NULL)                                         /* Free of any item that may have been stored */
++    {                                                         /* because of the commandport trap */
++      PRINTF1 (ctxt, DBG_CPROC, "RestartCProcTrap: commandPortItem %p\n", item);
++
++      kmutex_lock (&ctxt->SwapListsLock);
++      ELAN3_OP_FREE_BLOCK_ITEM (ctxt, item);
++      kmutex_unlock (&ctxt->SwapListsLock);
++    }
++
++    return (ESUCCESS);
++}
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan3/dproc.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan3/dproc.c  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan3/dproc.c       2005-06-01 23:12:54.568443816 -0400
+@@ -0,0 +1,553 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: dproc.c,v 1.52 2003/09/24 13:57:25 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/os/dproc.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elanvp.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanctxt.h>
++#include <elan3/elandebug.h>
++#include <elan3/urom_addrs.h>
++#include <elan3/intrinsics.h>
++#include <elan3/dma.h>
++#include <elan3/vmseg.h>
++
++#define DMA_RETRY_FAIL_COUNT  8
++
++static void PrintUserDma (ELAN3_CTXT *ctxt, E3_Addr addr);
++
++int
++HandleDProcTrap (ELAN3_DEV *dev, E3_uint32 *RestartBits)
++{
++    DMA_TRAP   *trap   = dev->DmaTrap;
++
++    ASSERT(SPINLOCK_HELD (&dev->IntrLock));
++
++    /* Scoop out the trap information, before restarting the Elan */
++    trap->Status.Status = read_reg32 (dev, Exts.DProcStatus.Status);
++    
++    ASSERT(trap->Status.s.WakeupFunction == WakeupNever);
++
++    /* copy the normal dma access fault type */
++    elan3_sdram_copyq_from_sdram (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProc), &trap->FaultSave, sizeof (E3_FaultSave_BE));
++    
++    /* copy all 4 of the dma data fault type */
++    elan3_sdram_copyq_from_sdram (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProcData0), &trap->Data0, 4*sizeof (E3_FaultSave_BE));
++    
++    /* Copy the DMA descriptor */
++    copy_dma_regs (dev, &trap->Desc);
++    
++    /* Copy the packet info */
++    trap->PacketInfo.Value = read_reg32 (dev, Exts.Dmas.DmaRds.DMA_PacketInfo.Value);
++
++    /* update device statistics */
++    BumpStat (dev, DProcTraps);
++    switch (trap->Status.s.TrapType)
++    {
++    case MI_DmaPacketTimedOutOrPacketError:
++      if (trap->PacketInfo.s.PacketTimeout)
++          BumpStat (dev, DmaOutputTimeouts);
++      else if (trap->PacketInfo.s.PacketAckValue == C_ACK_ERROR)
++          BumpStat (dev, DmaPacketAckErrors);
++      break;
++      
++    case MI_DmaFailCountError:
++      BumpStat (dev, DmaRetries);
++      break;
++    }
++
++    /* Must now zero all the FSRs so that a subsequent fault can be seen */
++    elan3_sdram_zeroq_sdram (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProc), sizeof (E3_FaultSave));
++    elan3_sdram_zeroq_sdram (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProcData0), 4*sizeof (E3_FaultSave));
++          
++    *RestartBits |= RestartDProc;
++    return (TRUE);
++}
++
++void
++DeliverDProcTrap (ELAN3_DEV *dev, DMA_TRAP *dmaTrap, E3_uint32 Pend)
++{
++    ELAN3_CTXT            *ctxt;
++    E3_FaultSave_BE *FaultArea;
++    DMA_TRAP      *trap;
++    register int     i;
++
++    ASSERT(SPINLOCK_HELD (&dev->IntrLock));
++
++    ctxt = ELAN3_DEV_CTX_TABLE(dev, dmaTrap->Status.s.Context);
++
++    if (ctxt == NULL)
++    {
++      PRINTF1 (DBG_DEVICE, DBG_INTR, "DeliverDProcTrap: context %x invalid\n", dmaTrap->Status.s.Context);
++      BumpStat (dev, InvalidContext);
++    }
++    else
++    {
++      if (ELAN3_OP_DPROC_TRAP (ctxt, dmaTrap) == OP_DEFER)
++      {
++          if (ELAN3_QUEUE_REALLY_FULL (ctxt->DmaTrapQ))
++          {
++              ctxt->Status |= CTXT_COMMAND_OVERFLOW_ERROR;
++              StartSwapoutContext (ctxt, Pend, NULL);
++          }
++          else
++          {
++              trap = ELAN3_QUEUE_BACK (ctxt->DmaTrapQ, ctxt->DmaTraps);
++              
++              bcopy (dmaTrap, trap, sizeof (DMA_TRAP));
++              
++              PRINTF5 (ctxt, DBG_INTR, "DeliverDProcTrap: WakeupFnt=%x Cntx=%x SuspAddr=%x PacketInfo=%x TrapType=%s\n",
++                       trap->Status.s.WakeupFunction, trap->Status.s.Context, 
++                       trap->Status.s.SuspendAddr, trap->PacketInfo.Value, MiToName (trap->Status.s.TrapType));
++              PRINTF3 (ctxt, DBG_INTR, "                    FaultAddr=%x EventAddr=%x FSR=%x\n",
++                       trap->FaultSave.s.FaultAddress, trap->FaultSave.s.EventAddress,
++                       trap->FaultSave.s.FSR.Status);
++              for (i = 0, FaultArea = &trap->Data0; i < 4; i++, FaultArea++)
++                  PRINTF4 (ctxt, DBG_INTR, "                  %d FaultAddr=%x EventAddr=%x FSR=%x\n", i,
++                           FaultArea->s.FaultAddress, FaultArea->s.EventAddress, FaultArea->s.FSR.Status);
++              
++              PRINTF4 (ctxt, DBG_INTR, "                 type %08x size %08x source %08x dest %08x\n",
++                       trap->Desc.s.dma_type, trap->Desc.s.dma_size, trap->Desc.s.dma_source, trap->Desc.s.dma_dest);
++              PRINTF2 (ctxt, DBG_INTR, "                 Dest event %08x cookie/proc %08x\n",
++                       trap->Desc.s.dma_destEvent, trap->Desc.s.dma_destCookieVProc);
++              PRINTF2 (ctxt, DBG_INTR, "                 Source event %08x cookie/proc %08x\n",
++                       trap->Desc.s.dma_srcEvent, trap->Desc.s.dma_srcCookieVProc);
++              ELAN3_QUEUE_ADD (ctxt->DmaTrapQ);
++              kcondvar_wakeupone (&ctxt->Wait, &dev->IntrLock);
++              
++              if (ELAN3_QUEUE_FULL (ctxt->DmaTrapQ))
++              {
++                  PRINTF0 (ctxt, DBG_INTR, "DeliverDProcTrap: dma queue full, must swap out\n");
++                  ctxt->Status |= CTXT_DMA_QUEUE_FULL;
++                  
++                  StartSwapoutContext (ctxt, Pend, NULL);
++              }
++          }
++      }
++    }
++}
++
++int
++NextDProcTrap (ELAN3_CTXT *ctxt, DMA_TRAP *trap)
++{
++    ELAN3_DEV *dev = ctxt->Device;
++
++    ASSERT (SPINLOCK_HELD (&dev->IntrLock));
++    
++    if (ELAN3_QUEUE_EMPTY (ctxt->DmaTrapQ))
++      return (0);
++
++    *trap = *ELAN3_QUEUE_FRONT (ctxt->DmaTrapQ, ctxt->DmaTraps);
++    ELAN3_QUEUE_REMOVE (ctxt->DmaTrapQ);
++    
++    return (1);
++}
++
++void
++ResolveDProcTrap (ELAN3_CTXT *ctxt, DMA_TRAP *trap)
++{
++    E3_FaultSave_BE *FaultArea;
++    int                    FaultHandled = 0;
++    int                    res;
++    register int     i;
++    
++    PRINTF4 (ctxt, DBG_DPROC, "ResolveDProcTrap: WakeupFnt=%x Cntx=%x SuspAddr=%x TrapType=%s\n",
++           trap->Status.s.WakeupFunction, trap->Status.s.Context, 
++           trap->Status.s.SuspendAddr, MiToName (trap->Status.s.TrapType));
++    PRINTF3 (ctxt, DBG_DPROC, "                    FaultAddr=%x EventAddr=%x FSR=%x\n",
++           trap->FaultSave.s.FaultAddress, trap->FaultSave.s.EventAddress,
++           trap->FaultSave.s.FSR.Status);
++    for (i = 0, FaultArea = &trap->Data0; i < 4; i++, FaultArea++)
++      PRINTF4 (ctxt, DBG_DPROC, "                  %d FaultAddr=%x EventAddr=%x FSR=%x\n", i,
++               FaultArea->s.FaultAddress, FaultArea->s.EventAddress, FaultArea->s.FSR.Status);
++
++    PRINTF4 (ctxt, DBG_DPROC, "                  type %08x size %08x source %08x dest %08x\n",
++           trap->Desc.s.dma_type, trap->Desc.s.dma_size, trap->Desc.s.dma_source, trap->Desc.s.dma_dest);
++    PRINTF2 (ctxt, DBG_DPROC, "                  Dest event %08x cookie/proc %08x\n",
++           trap->Desc.s.dma_destEvent, trap->Desc.s.dma_destCookieVProc);
++    PRINTF2 (ctxt, DBG_DPROC, "                  Source event %08x cookie/proc %08x\n",
++           trap->Desc.s.dma_srcEvent, trap->Desc.s.dma_srcCookieVProc);
++    
++    BumpUserStat (ctxt, DProcTraps);
++
++    switch (trap->Status.s.TrapType)
++    {
++    case MI_DmaPacketTimedOutOrPacketError:
++      /*
++       * Faulted due to packet timeout or a PAckError.
++       * Reset fail count and reissue the same desc.
++       */
++      PRINTF0 (ctxt, DBG_DPROC, "ResolveDProcTrap: got a PAckError or the output timed out. Rescheduling dma.\n");
++      if (ElanException (ctxt, EXCEPTION_PACKET_TIMEOUT, DMA_PROC, trap) == OP_IGNORE)
++      {
++          BumpUserStat (ctxt, DmaRetries);
++
++          trap->Desc.s.dma_failCount = DMA_RETRY_FAIL_COUNT;
++
++          RestartDmaTrap (ctxt, trap);
++      }
++      return;
++
++    case MI_DmaFailCountError:
++      /*
++       * Faulted due to dma fail count.
++       * Reset fail count and reissue the same desc.
++       */
++      PRINTF1 (ctxt, DBG_DPROC, "ResolveDProcTrap: Reset dma fail count to %d\n", DMA_RETRY_FAIL_COUNT);
++      
++      if (ElanException (ctxt, EXCEPTION_DMA_RETRY_FAIL, DMA_PROC, trap) == OP_IGNORE)
++      {
++          BumpUserStat (ctxt, DmaRetries);
++
++          trap->Desc.s.dma_failCount = DMA_RETRY_FAIL_COUNT;
++
++          RestartDmaTrap (ctxt, trap);
++      }
++      return;
++
++    case MI_TimesliceDmaQueueOverflow:
++      PRINTF0 (ctxt, DBG_DPROC, "ResolveDProcTrap: dma timeslice queue overflow\n");
++      RestartDmaTrap (ctxt, trap);
++      return;
++      
++    case MI_UnimplementedError:
++      PRINTF0 (ctxt, DBG_DPROC, "ResolveDProcTrap: unimplemented dma trap\n");
++      if (ElanException (ctxt, EXCEPTION_UNIMPLEMENTED, DMA_PROC, trap) == OP_IGNORE)
++          RestartDmaTrap (ctxt, trap);
++      return;
++
++    case MI_EventQueueOverflow:
++    case MI_ThreadQueueOverflow:
++    case MI_DmaQueueOverflow:
++      PRINTF0 (ctxt, DBG_DPROC, "ResolveDProcTrap: trapped on a write set event.\n");
++      FixupEventTrap (ctxt, DMA_PROC, trap, trap->Status.s.TrapType, &trap->FaultSave, 0);
++      return;
++
++    case MI_RemoteDmaCommand:
++    case MI_RunDmaCommand:
++    case MI_DequeueNonSysCntxDma:
++    case MI_DequeueSysCntxDma:
++      /*
++       * The DMA processor has trapped due to outstanding prefetches from the previous 
++       * dma.  The "current" dma has not been consumed, so we just ignore the trap
++       */
++      return;
++
++    case MI_WaitForRemoteDescRead2:
++    case MI_ExecuteDmaDescriptorForRun:
++      /*
++       * The DMA processor has trapped while fetching the dma descriptor, so
++       * zero it out to not confuse the user on an error
++       */
++      bzero (&trap->Desc, sizeof (trap->Desc));
++      break;
++    }
++
++    /*
++     * All other uWords will have updated one of the fault areas,  so fix
++     * any faults found in them.  If there were no faults found then it 
++     * must have been a bus error
++     */
++    for (i = 0, FaultArea = &trap->Data0; i < 4; i++, FaultArea++)
++    {
++      if (FaultArea->s.FSR.Status != 0)
++      {
++          FaultHandled++;
++
++          ASSERT ((FaultArea->s.FSR.Status & FSR_SizeMask) == FSR_Block64 ||
++                  (FaultArea->s.FSR.Status & FSR_SizeMask) == FSR_Block32);
++          
++          ASSERT (FaultArea->s.FaultContext == trap->Status.s.Context);
++          
++          if (((trap->Desc.s.dma_source & PAGEOFFSET) >= (PAGESIZE-E3_BLK_SIZE)) &&
++              ((trap->Desc.s.dma_source & PAGEMASK) != ((trap->Desc.s.dma_source + trap->Desc.s.dma_size-1) & PAGEMASK)))
++          {
++              /* XXXX: dma started within last 64 bytes of the page
++               *       terminate the process if it has pagefaulted */
++              if (FaultArea->s.FaultAddress == (trap->Desc.s.dma_source & ~(E3_BLK_SIZE-1)))
++              {
++                  printk ("elan%d: invalid dma - context=%x source=%x\n", ctxt->Device->Instance, 
++                          ctxt->Capability.cap_mycontext, trap->Desc.s.dma_source);
++                  
++                  if (ElanException (ctxt, EXCEPTION_BAD_DMA, DMA_PROC, trap, NULL, 0) != OP_IGNORE)
++                      return;
++              }
++          }
++
++          if (trap->Desc.s.dma_size != 0 && (res = elan3_pagefault (ctxt, FaultArea, 1)) != ESUCCESS)
++          {
++              /* XXXX: Rev B Elans can prefetch data passed the end of the dma descriptor */
++              /*       if the fault relates to this, then just ignore it */
++              if (FaultArea->s.FaultAddress < (trap->Desc.s.dma_source+trap->Desc.s.dma_size) ||
++                  FaultArea->s.FaultAddress > (trap->Desc.s.dma_source+trap->Desc.s.dma_size+E3_BLK_SIZE*2))
++              {
++                  PRINTF1 (ctxt, DBG_DPROC, "ResolveDProcTrap: elan3_pagefault failed for address %x\n",
++                           FaultArea->s.FaultAddress);
++                  
++                  if (ElanException (ctxt, EXCEPTION_INVALID_ADDR, DMA_PROC, trap, FaultArea, res) != OP_IGNORE)
++                      return;
++              }
++          }
++      }
++    }
++    
++    if (trap->FaultSave.s.FSR.Status != 0)
++    {
++      FaultHandled++;
++
++      ASSERT (trap->FaultSave.s.FaultContext == trap->Status.s.Context);
++
++      if ((trap->FaultSave.s.FSR.Status & FSR_SizeMask) == FSR_RouteFetch)
++      {
++          res = ResolveVirtualProcess (ctxt, trap->FaultSave.s.FaultAddress & 0xffff); /* mask out cookie */
++
++          switch (res)
++          {
++          default:
++              if (ElanException (ctxt, EXCEPTION_INVALID_PROCESS, DMA_PROC, trap, trap->FaultSave.s.FaultAddress, res) != OP_IGNORE)
++                  return;
++              
++          case EAGAIN:
++              /* XXXX; wait on trail blazing code */
++
++          case 0:
++              break;
++          }
++      }
++      else
++      {
++          if ((res = elan3_pagefault (ctxt, &trap->FaultSave, 1)) != ESUCCESS)
++          {
++              PRINTF1 (ctxt, DBG_DPROC, "ResolveDProcTrap: elan3_pagefault failed for address %x\n",
++                       trap->FaultSave.s.FaultAddress);
++
++              if (ElanException (ctxt, EXCEPTION_INVALID_ADDR, DMA_PROC, trap, &trap->FaultSave, res) != OP_IGNORE)
++                  return;
++          }
++      }
++    }
++
++    if (! FaultHandled)
++    {
++      ElanBusError (ctxt->Device);
++
++      if (ElanException (ctxt, EXCEPTION_INVALID_ADDR, DMA_PROC, trap, &trap->FaultSave, EFAULT) != OP_IGNORE)
++          return;
++    }
++
++    switch (trap->Status.s.TrapType)
++    {
++    case MI_WaitForRemoteDescRead2:
++      /*
++       * Faulted while trying to read the dma descriptor for a read dma.
++       * Fix fault and re-issue using FaultAddress.
++       */
++      PRINTF1 (ctxt, DBG_DPROC, "ResolveDProcTrap: trapped reading a remote dma descriptor at %x.\n",
++               trap->FaultSave.s.FaultAddress);
++      
++      RestartDmaPtr (ctxt, trap->FaultSave.s.FaultAddress);
++      break;
++      
++    case MI_ExecuteDmaDescriptorForRun:
++      /*
++       * Faulted while trying to read the dma descriptor for a write dma.
++       * Fix fault and re-issue using FaultAddress.
++       */
++      PRINTF1 (ctxt, DBG_DPROC, "ResolveDProcTrap: trapped reading a write dma descriptor at %x.\n", 
++               trap->FaultSave.s.FaultAddress);
++      
++      RestartDmaPtr (ctxt, trap->FaultSave.s.FaultAddress);
++      break;
++      
++    case MI_WaitForRemoteRoutes1:
++    case MI_WaitForRemoteRoutes2:
++    case MI_SendRemoteDmaDesc:
++    case MI_SendDmaIdentify:
++    case MI_SendRemoteDmaRoutes2:
++    case MI_WaitForDmaRoutes1:
++    case MI_DmaLoop:
++    case MI_ExitDmaLoop:
++    case MI_GetDestEventValue:
++    case MI_SendFinalUnlockTrans:
++    case MI_SendNullSetEvent:
++    case MI_SendFinalSetEvent:
++    case MI_SendDmaEOP:
++      /*
++       * Faulted either fetching routes or fetching dma data.
++       * Fix fault and re-issue using FaultAddress.
++       */
++
++    case MI_SendEOPforRemoteDma:
++    case MI_LookAtRemoteAck:
++    case MI_FailedAckIfCCis0:
++      /*
++       * Possible fault when reading the remote desc into the dma data buffers
++       */
++      PRINTF0 (ctxt, DBG_DPROC, "ResolveDProcTrap:  trapped reading a dma data or fetching a route\n");
++      RestartDmaTrap (ctxt, trap);
++      break;
++      
++    case MI_DequeueSysCntxDma:
++    case MI_DequeueNonSysCntxDma:
++    case MI_RemoteDmaCommand:
++    case MI_RunDmaCommand:
++      /*
++       * It is possible that a dma can get back onto the queue while outstanding dma
++       * have not finished trapping. In this case the trap can be ignored as the dma
++       * state has been saved. It might trap again the next time it comes to the front
++       * of the queue and be fixed then.
++       */
++      PRINTF0 (ctxt, DBG_DPROC, "ResolveDProcTrap: trap after dma has finished. ignored\n");
++      break;
++      
++    default:
++      PRINTF0 (ctxt, DBG_DPROC, "ResolveDProcTrap: trapped on a write set event.\n");
++      FixupEventTrap (ctxt, DMA_PROC, trap, trap->Status.s.TrapType, &trap->FaultSave, 0);
++      break;
++    }
++}
++
++int
++DProcNeedsRestart (ELAN3_CTXT *ctxt)
++{
++    return (ctxt->ItemCount[LIST_DMA_PTR] != 0 ||
++          ctxt->ItemCount[LIST_DMA_DESC] != 0);
++}
++
++void
++RestartDProcItems (ELAN3_CTXT *ctxt)
++{
++    void      *item;
++    E3_Addr    value;
++    int              res;
++    
++    kmutex_lock (&ctxt->SwapListsLock);
++    while (ctxt->ItemCount[LIST_DMA_PTR])
++    {
++      if (! ELAN3_OP_GET_WORD_ITEM (ctxt, LIST_DMA_PTR, &item, &value))
++          ctxt->ItemCount[LIST_DMA_PTR] = 0;
++      else
++      {
++          PRINTF1 (ctxt, DBG_DPROC, "RestartDProc: issue write dma at %x\n", value);
++          PrintUserDma (ctxt, value);
++
++          res = IssueDmaCommand (ctxt, value, NULL, 0);
++          
++          if (res == ISSUE_COMMAND_RETRY)
++          {
++              ELAN3_OP_PUTBACK_ITEM (ctxt, LIST_DMA_PTR, item);
++              kmutex_unlock (&ctxt->SwapListsLock);
++              return;
++          }
++          
++          ctxt->ItemCount[LIST_DMA_PTR]--;
++          ELAN3_OP_FREE_WORD_ITEM (ctxt, item);
++      }
++    }
++    
++    while (ctxt->ItemCount[LIST_DMA_DESC])
++    {
++      if (! ELAN3_OP_GET_BLOCK_ITEM (ctxt, LIST_DMA_DESC, &item, &value))
++          ctxt->ItemCount[LIST_DMA_DESC] = 0;
++      else
++      {
++          PRINTF1 (ctxt, DBG_DPROC, "RestartDProc: issue dma desc at %x\n", value);
++          PrintUserDma (ctxt, value);
++
++          res = IssueDmaCommand (ctxt, value, item, 0);
++
++          switch (res)
++          {
++          case ISSUE_COMMAND_OK:
++              ctxt->ItemCount[LIST_DMA_DESC]--;
++              ELAN3_OP_FREE_BLOCK_ITEM (ctxt, item);
++              break;
++              
++          case ISSUE_COMMAND_RETRY:
++              ELAN3_OP_PUTBACK_ITEM (ctxt, LIST_DMA_DESC, item);
++              kmutex_unlock (&ctxt->SwapListsLock);
++              return;
++              
++          case ISSUE_COMMAND_TRAPPED:
++              ctxt->ItemCount[LIST_DMA_DESC]--;
++              /* The item will be freed off when the command port trap */
++              /* fixed up and the command successfully re-issued */
++              break;
++          }
++      }
++    }
++
++    kmutex_unlock (&ctxt->SwapListsLock);
++}
++
++void
++RestartDmaDesc(ELAN3_CTXT *ctxt, E3_DMA_BE *desc)
++{
++    kmutex_lock (&ctxt->SwapListsLock);
++    if (desc->s.dma_direction != DMA_WRITE)
++      desc->s.dma_direction = (desc->s.dma_direction & ~DMA_READ) | DMA_READ_REQUEUE;
++
++    ELAN3_OP_PUT_BLOCK_ITEM (ctxt, LIST_DMA_DESC, (E3_uint32 *) desc);
++    ctxt->ItemCount[LIST_DMA_DESC]++;
++
++    kmutex_unlock (&ctxt->SwapListsLock);
++}
++
++void
++RestartDmaTrap(ELAN3_CTXT *ctxt, DMA_TRAP *trap)
++{
++    /* Negative length DMAs are illegal, since they hangup the dma processor,
++     * if they got generated then they will have been spotted by PollForDmahungup,
++     * and delivered to us with a Dequeue  suspend address,
++     *
++     * GNAT sw-elan3/3908: Moved this check into this new function to avoid
++     * it sampling old or invalid register state
++     */
++    if (trap->Desc.s.dma_size > E3_MAX_DMA_SIZE)
++      ElanException (ctxt, EXCEPTION_BAD_DMA, DMA_PROC, trap, NULL, 0);
++    else
++      RestartDmaDesc (ctxt, &trap->Desc);
++}
++
++void
++RestartDmaPtr (ELAN3_CTXT *ctxt, E3_Addr ptr)
++{
++    kmutex_lock (&ctxt->SwapListsLock);
++    ELAN3_OP_PUT_WORD_ITEM (ctxt, LIST_DMA_PTR, ptr);
++    ctxt->ItemCount[LIST_DMA_PTR]++;
++    kmutex_unlock (&ctxt->SwapListsLock);
++}
++
++static void
++PrintUserDma (ELAN3_CTXT *ctxt, E3_Addr addr)
++{
++    E3_DMA *dma;
++
++    /* Dont call a function which takes locks unless we need to */
++    if (!(elan3_debug & DBG_DPROC))
++        return;
++
++    dma = (E3_DMA *) elan3mmu_mainaddr (ctxt->Elan3mmu, addr);
++
++    PRINTF4 (ctxt, DBG_DPROC, "DMA: type %08x size %08x source %08x dest %08x\n",
++           fuword ((int *) &dma->dma_type), fuword ((int *) &dma->dma_size), 
++           fuword ((int *) &dma->dma_source), fuword ((int *) &dma->dma_dest));
++    PRINTF4 (ctxt, DBG_DPROC, "DMA: Dest %08x %08x  Local %08x %08x\n",
++           fuword ((int *) &dma->dma_destEvent), fuword ((int *) &dma->dma_destCookieProc), 
++           fuword ((int *) &dma->dma_srcEvent), fuword ((int *) &dma->dma_srcCookieProc));
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan3/elan3mmu_generic.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan3/elan3mmu_generic.c       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan3/elan3mmu_generic.c    2005-06-01 23:12:54.573443056 -0400
+@@ -0,0 +1,3255 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: elan3mmu_generic.c,v 1.75.2.1 2004/12/14 10:19:51 mike Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/vm/elan3mmu_generic.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elanvp.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanctxt.h>
++#include <elan3/elandebug.h>
++#include <elan3/urom_addrs.h>
++#include <elan3/thread.h>
++
++#ifdef CONFIG_MPSAS
++#  define zero_all_ptbls
++#endif
++
++/*
++ * Debugging
++ */
++int   elan3mmu_debug = 0;
++
++#define       N_L3PTBL_MTX    (0x20)
++#define       N_L2PTBL_MTX    (0x40)
++#define       N_L1PTBL_MTX    (0x20)
++
++#define       L3PTBL_MTX_HASH(p) \
++      ((((uintptr_t)(p) >> 12) ^ ((uintptr_t)(p) >> 2)) & (N_L3PTBL_MTX - 1))
++static        spinlock_t l3ptbl_lock[N_L3PTBL_MTX];
++
++#define       L2PTBL_MTX_HASH(p)   \
++      ((((uintptr_t)(p) >> 12) ^ ((uintptr_t)(p) >> 2)) & (N_L2PTBL_MTX - 1))
++static        spinlock_t l2ptbl_lock[N_L2PTBL_MTX];
++
++#define       L1PTBL_MTX_HASH(p)   \
++      ((((uintptr_t)(p) >> 12) ^ ((uintptr_t)(p) >> 2)) & (N_L1PTBL_MTX - 1))
++static        spinlock_t l1ptbl_lock[N_L1PTBL_MTX];
++
++
++#define       BASE2VA(p)      ((E3_Addr)((p)->ptbl_base << 16))
++#define       VA2BASE(v)      ((u_short)(((uintptr_t)(v)) >> 16))
++
++ELAN3MMU_GLOBAL_STATS elan3mmu_global_stats;
++
++static void          elan3mmu_flush_context_filter (ELAN3_DEV *dev, void *);
++static void          elan3mmu_unload_loop (ELAN3MMU *elan3mmu, ELAN3_PTBL *ptbl, int first_valid, int nptes, int flags);
++
++static ELAN3_PTBL    *elan3mmu_create_ptbls (ELAN3_DEV *dev, int level, int attr, int keep);
++static ELAN3_PTBL    *elan3mmu_ta_to_ptbl (ELAN3MMU *elan3mmu, ELAN3_PTP *ptp);
++
++static ELAN3_PTBL    *elan3mmu_alloc_pte    (ELAN3_DEV *dev, ELAN3MMU *elan3mmu, int *idx);
++void                 elan3mmu_free_lXptbl  (ELAN3_DEV *dev, ELAN3_PTBL *ptbl);
++
++void                 elan3mmu_free_pte  (ELAN3_DEV *dev,  ELAN3MMU *elan3mmu,  ELAN3_PTBL *ptbl_ptr, int idx);
++
++static ELAN3_PTBL    *elan3mmu_alloc_l1ptbl (ELAN3_DEV *dev, int attr, ELAN3MMU *elan3mmu);
++static ELAN3_PTBL    *elan3mmu_alloc_l2ptbl (ELAN3_DEV *dev, int attr, ELAN3_PTBL *parent, ELAN3MMU *elan3mmu,
++                                          E3_Addr base, spinlock_t **plock, unsigned long *flags);
++static ELAN3_PTBL    *elan3mmu_alloc_l3ptbl (ELAN3_DEV *dev, int attr, ELAN3_PTBL *parent, ELAN3MMU *elan3mmu,
++                                          E3_Addr base, spinlock_t **plock, unsigned long *flags);
++
++static int         elan3mmu_steal_this_ptbl (ELAN3_DEV *dev, ELAN3_PTBL *l3ptbl);
++static ELAN3_PTBL    *elan3mmu_steal_l3ptbl (ELAN3_DEV *dev, int attr);
++
++static spinlock_t   *elan3mmu_ptbl_to_lock (int level, ELAN3_PTBL *ptbl);
++
++/*
++ * Encoding of MMU permissions against access type,
++ * to allow quick permission checking against access 
++ * type.
++ */
++u_char elan3mmu_permissionTable[] =
++{
++    0xcc,     /* 11001100 ELAN3_PERM_NULL        */
++    0x01,     /* 00000001 ELAN3_PERM_LOCALREAD   */
++    0x05,     /* 00000101 ELAN3_PERM_READ        */
++    0x33,     /* 00110011 ELAN3_PERM_NOREMOTE    */
++    0x37,     /* 00110111 ELAN3_PERM_REMOTEREAD  */
++    0x3f,     /* 00111111 ELAN3_PERM_REMOTEWRITE */
++    0xf7,     /* 11110111 ELAN3_PERM_REMOTEEVENT */
++    0xff,     /* 11111111 ELAN3_PERM_REMOTEALL          */
++} ;
++
++void
++elan3mmu_init()
++{
++    register int i;
++
++    HAT_PRINTF0 (1, "elan3mmu_init: initialising elan mmu\n");
++
++    for (i = 0; i < N_L1PTBL_MTX; i++)
++      spin_lock_init (&l1ptbl_lock[i]);
++
++    for (i = 0; i < N_L2PTBL_MTX; i++)
++      spin_lock_init (&l2ptbl_lock[i]);
++
++    for (i = 0; i < N_L3PTBL_MTX; i++)
++      spin_lock_init (&l3ptbl_lock[i]);
++
++    elan3mmu_global_stats.version = ELAN3MMU_STATS_VERSION;
++
++    elan3mmu_init_osdep();
++}
++
++void
++elan3mmu_fini()
++{
++    register int i;
++
++    HAT_PRINTF0 (1, "elan3mmu_fini: finalising elan mmu\n");
++
++    for (i = 0; i < N_L1PTBL_MTX; i++)
++      spin_lock_destroy (&l1ptbl_lock[i]);
++
++    for (i = 0; i < N_L2PTBL_MTX; i++)
++      spin_lock_destroy (&l2ptbl_lock[i]);
++
++    for (i = 0; i < N_L3PTBL_MTX; i++)
++      spin_lock_destroy (&l3ptbl_lock[i]);
++
++    elan3mmu_fini_osdep();
++}
++
++ELAN3MMU *
++elan3mmu_alloc (ELAN3_CTXT *ctxt)
++{
++    ELAN3MMU  *elan3mmu;
++    ELAN3_PTBL *l1ptbl;
++
++    ALLOC_ELAN3MMU (elan3mmu, TRUE);
++    
++    spin_lock_init (&elan3mmu->elan3mmu_lock);
++
++    spin_lock (&elan3mmu->elan3mmu_lock);                     /* lock_lint */
++
++    elan3mmu->elan3mmu_ergns    = NULL;
++    elan3mmu->elan3mmu_etail    = NULL;
++    elan3mmu->elan3mmu_ergnlast = NULL;
++    elan3mmu->elan3mmu_mrgns    = NULL;
++    elan3mmu->elan3mmu_mtail    = NULL;
++    elan3mmu->elan3mmu_mrgnlast = NULL;
++    elan3mmu->elan3mmu_ctxt     = ctxt;
++
++    spin_lock_init (&elan3mmu->elan3mmu_lXptbl_lock);
++    elan3mmu->elan3mmu_lXptbl   = NULL;
++
++    spin_unlock (&elan3mmu->elan3mmu_lock);                   /* lock_lint */
++
++    l1ptbl = elan3mmu_alloc_l1ptbl(ctxt->Device, 0, elan3mmu);
++
++    elan3mmu->elan3mmu_ctp      = (sdramaddr_t) 0;
++    elan3mmu->elan3mmu_dev      = ctxt->Device;
++    elan3mmu->elan3mmu_l1ptbl   = l1ptbl;
++
++    /* Ensure that there are at least some level 3 page tables,  since if a level 2 and */
++    /* a level 3 table are allocated together, then the level 3 is allocated with the NO_ALLOC */
++    /* flag,  thus there MUST be at least one that can be stolen or on the free list */
++    if (elan3mmu->elan3mmu_dev->Level[PTBL_LEVEL_3].PtblFreeList == NULL)
++      elan3mmu_create_ptbls (elan3mmu->elan3mmu_dev, PTBL_LEVEL_3, 0, 0);
++
++    HAT_PRINTF1 (1, "elan3mmu_alloc: elan3mmu %p\n", elan3mmu);
++
++    elan3mmu_alloc_osdep (elan3mmu);
++
++    return (elan3mmu);
++}
++
++void 
++elan3mmu_free (ELAN3MMU *elan3mmu)
++{
++    ELAN3MMU_RGN   *rgn;
++    ELAN3_PTBL           *l1ptbl;
++    spinlock_t           *l1lock;
++    unsigned long   l1flags;
++    unsigned long   flags;
++
++    HAT_PRINTF1 (1, "elan3mmu_free : elan3mmu %p\n", elan3mmu);
++    
++    /*
++     * Invalidate the level1 page table,  since it's already removed
++     * from the context table, there is no need to flush the tlb.
++     */
++    l1ptbl = elan3mmu->elan3mmu_l1ptbl;
++    elan3mmu->elan3mmu_l1ptbl = NULL;
++    
++    if (elan3mmu_lock_ptbl (l1ptbl, LK_PTBL_FAILOK, elan3mmu, (E3_Addr) 0, PTBL_LEVEL_1, &l1lock, &l1flags) == LK_PTBL_OK)
++    {
++      elan3mmu_l1inval (elan3mmu, l1ptbl, PTE_UNLOAD_NOFLUSH);
++      elan3mmu_free_l1ptbl (elan3mmu->elan3mmu_dev, l1ptbl, l1lock, l1flags);
++    }
++
++    /*
++     * Free of any permission regions.
++     */
++    spin_lock (&elan3mmu->elan3mmu_lock);                                     /* lock_lint */
++    while ((rgn = elan3mmu->elan3mmu_mrgns) != NULL)
++    {
++      spin_lock_irqsave (&elan3mmu->elan3mmu_dev->IntrLock, flags);           /* lock_lint */
++      elan3mmu_removergn_elan (elan3mmu, rgn->rgn_ebase);
++      elan3mmu_removergn_main (elan3mmu, rgn->rgn_mbase);
++      spin_unlock_irqrestore (&elan3mmu->elan3mmu_dev->IntrLock, flags);      /* lock_lint */
++      
++      FREE_ELAN3MMU_RGN (rgn);
++    }
++    elan3mmu->elan3mmu_mrgnlast = NULL;
++    elan3mmu->elan3mmu_ergnlast = NULL;
++
++    /* 
++     * Free the lXptbl list
++     */
++    ASSERT (elan3mmu->elan3mmu_lXptbl == NULL); /* XXXX MRH need to add list removal */
++ 
++    elan3mmu->elan3mmu_lXptbl = NULL;
++    spin_lock_destroy (&elan3mmu->elan3mmu_lXptbl_lock);
++
++
++    spin_unlock (&elan3mmu->elan3mmu_lock);                                   /* lock_lint */
++
++    spin_lock_destroy (&elan3mmu->elan3mmu_lock);
++
++    FREE_ELAN3MMU (elan3mmu);
++}
++
++/*================================================================================*/
++/* Interface routines to device driver */
++static void
++elan3mmu_flush_context_filter (ELAN3_DEV *dev, void *arg)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++    ASSERT ((read_reg32 (dev, Exts.InterruptReg) & (INT_DiscardingSysCntx | INT_DiscardingNonSysCntx)) == 
++          (INT_DiscardingSysCntx | INT_DiscardingNonSysCntx));
++
++    dev->FilterHaltQueued = 0;
++
++    write_reg32 (dev, Input_Context_Fil_Flush, 0);
++
++    HAT_PRINTF0 (1, "elan3mmu_flush_context_filter completed\n");
++
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++}
++
++void
++elan3mmu_set_context_filter (ELAN3_DEV *dev, int ctx, int disabled, E3_uint32 Pend, E3_uint32 *Maskp)
++{
++    int         mctx = ctx & MAX_ROOT_CONTEXT_MASK;
++    sdramaddr_t ctp  = dev->ContextTable + mctx * sizeof (E3_ContextControlBlock);
++
++    ASSERT (SPINLOCK_HELD (&dev->IntrLock));
++
++    ASSERT ((mctx < 32 || mctx >= ELAN3_KCOMM_BASE_CONTEXT_NUM) ? (ctx & SYS_CONTEXT_BIT) : ! (ctx & SYS_CONTEXT_BIT));
++
++    elan3_sdram_writel (dev, ctp + offsetof (E3_ContextControlBlock, filter), 
++                ((ctx & SYS_CONTEXT_BIT) ? E3_CCB_CNTX0 : 0) | (disabled ? E3_CCB_DISCARD_ALL : 0));
++
++    HAT_PRINTF4 (1, "elan3mmu_set_context_filter: ctx %x [%lx] -> %s (%x)\n", ctx, ctp,
++               disabled ? "up" : "down", elan3_sdram_readl (dev, ctp + offsetof (E3_ContextControlBlock, filter)));
++
++    /* queue a halt operation to flush the context filter while the inputter is halted */
++    if (dev->FilterHaltQueued == 0)
++    {
++      dev->FilterHaltQueued = 1;
++      QueueHaltOperation (dev, Pend, Maskp, INT_DiscardingSysCntx | INT_DiscardingNonSysCntx, 
++                          elan3mmu_flush_context_filter, NULL);
++    }
++}
++
++int
++elan3mmu_attach (ELAN3_DEV *dev, int ctx, ELAN3MMU *elan3mmu, sdramaddr_t routeTable, E3_uint32 routeMask)
++{
++    sdramaddr_t ctp;
++    ELAN3_PTP    trootptp;
++
++    ASSERT (SPINLOCK_HELD (&dev->IntrLock));
++
++    ctx &= MAX_ROOT_CONTEXT_MASK;                                             /* Mask out all high bits in context */
++    
++    if (ctx < 0 || ctx >= dev->ContextTableSize)
++      return (EINVAL);
++
++    ctp = dev->ContextTable + ctx * sizeof (E3_ContextControlBlock);
++    
++    trootptp = elan3_readptp (dev, ctp + offsetof (E3_ContextControlBlock, rootPTP));
++    
++    if (ELAN3_PTP_TYPE(trootptp) != ELAN3_ET_INVALID)
++      return (EBUSY);
++
++    elan3mmu->elan3mmu_ctp = ctp;
++    
++    trootptp = PTBL_TO_PTADDR (elan3mmu->elan3mmu_l1ptbl) | ELAN3_ET_PTP;
++    
++    HAT_PRINTF4 (1, "elan3mmu_attach: ctp at %08lx : trootptp=%08x VPT_ptr=%08lx VPT_mask=%08x\n",
++               ctp, trootptp, routeTable, routeMask);
++    
++    elan3_writeptp (dev, ctp + offsetof (E3_ContextControlBlock, rootPTP), trootptp);
++    elan3_writeptp (dev, ctp + offsetof (E3_ContextControlBlock, VPT_ptr), routeTable);
++    elan3_writeptp (dev, ctp + offsetof (E3_ContextControlBlock, VPT_mask), routeMask);
++    
++    return (ESUCCESS);
++}
++
++void
++elan3mmu_detach (ELAN3_DEV *dev, int ctx)
++{
++    ELAN3_PTP    invalidptp = ELAN3_INVALID_PTP;
++    sdramaddr_t ctp;
++    
++    ctx &= MAX_ROOT_CONTEXT_MASK;                                             /* Mask out all high bits in context */
++    
++    if (ctx < 0 || ctx >= dev->ContextTableSize)
++      return;
++    
++    ctp = dev->ContextTable + ctx * sizeof (E3_ContextControlBlock);
++    
++    HAT_PRINTF1 (1, "elan3mmu_detach: clearing ptp at %lx\n", ctp);
++    
++    elan3_writeptp (dev, ctp + offsetof (E3_ContextControlBlock, rootPTP), invalidptp);
++    elan3_writeptp (dev, ctp + offsetof (E3_ContextControlBlock, VPT_mask), 0);
++    elan3_writeptp (dev, ctp + offsetof (E3_ContextControlBlock, VPT_ptr), 0);
++    
++    ElanFlushTlb (dev);
++}
++
++int
++elan3mmu_reference (ELAN3MMU *elan3mmu, int ctx)
++{
++    ELAN3_DEV              *dev = elan3mmu->elan3mmu_dev;
++    sdramaddr_t            ctp;
++    E3_ContextControlBlock ccb;
++    ELAN3_PTP               trootptp;
++
++    ctx &= MAX_ROOT_CONTEXT_MASK;                                             /* Mask out all high bits in context */
++    
++    if (ctx < 0 || ctx >= dev->ContextTableSize)
++      return (EINVAL);
++
++    ctp = dev->ContextTable + ctx * sizeof (E3_ContextControlBlock);
++
++    trootptp = elan3_readptp (dev, ctp + offsetof (E3_ContextControlBlock, rootPTP));
++    
++    if (ELAN3_PTP_TYPE(trootptp) != ELAN3_ET_INVALID)
++      return (EBUSY);
++    
++    elan3_sdram_copyl_from_sdram (dev, elan3mmu->elan3mmu_ctp, &ccb, sizeof (E3_ContextControlBlock));
++    elan3_sdram_copyl_to_sdram (dev, &ccb, ctp, sizeof (E3_ContextControlBlock));
++    
++    return (ESUCCESS);
++    
++}
++/*================================================================================*/
++/* Elan permission regions. */
++
++/* elan address region management */
++ELAN3MMU_RGN *
++elan3mmu_findrgn_elan (ELAN3MMU *elan3mmu,
++                     E3_Addr addr, int tail)
++{
++    ELAN3MMU_RGN *next = NULL;
++    ELAN3MMU_RGN *rgn;
++    ELAN3MMU_RGN *hirgn;
++    ELAN3MMU_RGN *lorgn;
++    E3_Addr       base;
++    E3_Addr       lastaddr;
++    int                 forward;
++
++    ASSERT (SPINLOCK_HELD (&elan3mmu->elan3mmu_dev->IntrLock) || SPINLOCK_HELD (&elan3mmu->elan3mmu_lock));
++
++    if (elan3mmu->elan3mmu_ergns == NULL)
++      return (NULL);
++
++    rgn = elan3mmu->elan3mmu_ergnlast;
++    if (rgn == NULL)
++      rgn = elan3mmu->elan3mmu_ergns;
++
++    forward = 0;
++    if ((u_long) (base = rgn->rgn_ebase) < (u_long)addr)
++    {
++      if ((u_long)addr <= ((u_long) base + rgn->rgn_len - 1))
++          return (rgn);                                       /* ergnlast contained addr */
++
++      hirgn = elan3mmu->elan3mmu_etail;
++
++      if ((u_long) (lastaddr = (hirgn->rgn_ebase + hirgn->rgn_len - 1)) < (u_long) addr)
++          return (tail ? hirgn : NULL);                       /* addr is out of range */
++      
++      if ((u_long) (addr - base) > (u_long) (lastaddr - addr))
++          rgn = hirgn;
++      else
++      {
++          rgn = rgn->rgn_enext;
++          forward++;
++      }
++    }
++    else
++    {
++      lorgn = elan3mmu->elan3mmu_ergns;
++
++      if ((u_long)lorgn->rgn_ebase > (u_long) addr)
++          return (lorgn);                                     /* lowest regions is higher than addr */
++      if ((u_long)(addr - lorgn->rgn_ebase) < (u_long) (base - addr))
++      {
++          rgn = lorgn;                                        /* search forward from head */
++          forward++;
++      }
++    }
++    if (forward)
++    {
++      while ((u_long)(rgn->rgn_ebase + rgn->rgn_len - 1) < (u_long)addr)
++          rgn = rgn->rgn_enext;
++
++      if ((u_long)rgn->rgn_ebase <= (u_long)addr)
++          elan3mmu->elan3mmu_ergnlast = rgn;
++      return (rgn);
++    }
++    else
++    {
++      while ((u_long)rgn->rgn_ebase > (u_long)addr)
++      {
++          next = rgn;
++          rgn = rgn->rgn_eprev;
++      }
++
++      if ((u_long) (rgn->rgn_ebase + rgn->rgn_len - 1) < (u_long)addr)
++          return (next);
++      else
++      {
++          elan3mmu->elan3mmu_ergnlast = rgn;
++          return (rgn);
++      }
++    }
++}
++
++int
++elan3mmu_addrgn_elan (ELAN3MMU *elan3mmu, ELAN3MMU_RGN *nrgn)
++{
++    ELAN3MMU_RGN *rgn   = elan3mmu_findrgn_elan (elan3mmu, nrgn->rgn_ebase, 1);
++    E3_Addr       nbase = nrgn->rgn_ebase;
++    E3_Addr     ntop  = nbase + nrgn->rgn_len - 1; /* avoid wrap */
++    E3_Addr     base;
++
++    ASSERT (SPINLOCK_HELD (&elan3mmu->elan3mmu_dev->IntrLock) && SPINLOCK_HELD (&elan3mmu->elan3mmu_lock));
++
++    if (rgn == NULL)
++    {
++      elan3mmu->elan3mmu_ergns = elan3mmu->elan3mmu_etail = nrgn;
++      nrgn->rgn_enext = nrgn->rgn_eprev = NULL;
++    }
++    else
++    {
++      base = rgn->rgn_ebase;
++
++      if ((u_long)(base + rgn->rgn_len - 1) < (u_long)nbase)  /* top of region below requested address */
++      {                                                       /* so insert after region (and hence at end */
++          nrgn->rgn_eprev = rgn;                              /* of list */
++          nrgn->rgn_enext = NULL;
++          rgn->rgn_enext = elan3mmu->elan3mmu_etail = nrgn;
++      }
++      else
++      {
++          if ((u_long)nbase >= (u_long)base || (u_long)ntop >= (u_long)base)
++              return (-1);                                    /* overlapping region */
++
++          nrgn->rgn_enext = rgn;                              /* insert before region */
++          nrgn->rgn_eprev = rgn->rgn_eprev;
++          rgn->rgn_eprev  = nrgn;
++          if (elan3mmu->elan3mmu_ergns == rgn)
++              elan3mmu->elan3mmu_ergns = nrgn;
++          else
++              nrgn->rgn_eprev->rgn_enext = nrgn;
++      }
++    }
++    elan3mmu->elan3mmu_ergnlast = nrgn;
++    
++    return (0);
++}
++
++ELAN3MMU_RGN *
++elan3mmu_removergn_elan (ELAN3MMU *elan3mmu, E3_Addr addr)
++{
++    ELAN3MMU_RGN *rgn = elan3mmu_findrgn_elan (elan3mmu, addr, 0);
++    
++    ASSERT (SPINLOCK_HELD (&elan3mmu->elan3mmu_dev->IntrLock) && SPINLOCK_HELD (&elan3mmu->elan3mmu_lock));
++
++    if (rgn == NULL || rgn->rgn_ebase != addr)
++      return (NULL);
++    
++    elan3mmu->elan3mmu_ergnlast = rgn->rgn_enext;
++    if (rgn == elan3mmu->elan3mmu_etail)
++      elan3mmu->elan3mmu_etail = rgn->rgn_eprev;
++    else
++      rgn->rgn_enext->rgn_eprev = rgn->rgn_eprev;
++    
++    if (rgn == elan3mmu->elan3mmu_ergns)
++      elan3mmu->elan3mmu_ergns = rgn->rgn_enext;
++    else
++      rgn->rgn_eprev->rgn_enext = rgn->rgn_enext;
++
++    return (rgn);
++}
++
++ELAN3MMU_RGN *
++elan3mmu_rgnat_elan (ELAN3MMU *elan3mmu, E3_Addr addr)
++{
++    ELAN3MMU_RGN *rgn = elan3mmu_findrgn_elan (elan3mmu, addr, 0);
++    E3_Addr       base;
++
++    if (rgn != NULL && (u_long)(base = rgn->rgn_ebase) <= (u_long)addr && (u_long)addr <= (u_long)(base + rgn->rgn_len - 1))
++      return (rgn);
++    return (NULL);
++}
++
++/* main address region management */
++ELAN3MMU_RGN *
++elan3mmu_findrgn_main (ELAN3MMU *elan3mmu,
++                     caddr_t addr, int tail)
++{
++    ELAN3MMU_RGN *next = NULL;
++    ELAN3MMU_RGN *rgn;
++    ELAN3MMU_RGN *hirgn;
++    ELAN3MMU_RGN *lorgn;
++    caddr_t       lastaddr;
++    caddr_t       base;
++    int                 forward;
++
++    ASSERT (SPINLOCK_HELD (&elan3mmu->elan3mmu_dev->IntrLock) || SPINLOCK_HELD (&elan3mmu->elan3mmu_lock));
++
++    if (elan3mmu->elan3mmu_mrgns == NULL)
++      return (NULL);
++
++    rgn = elan3mmu->elan3mmu_mrgnlast;
++    if (rgn == NULL)
++      rgn = elan3mmu->elan3mmu_mrgns;
++
++    forward = 0;
++    if ((base = rgn->rgn_mbase) < addr)
++    {
++      if (addr <= (base + rgn->rgn_len - 1))
++          return (rgn);                                       /* ergnlast contained addr */
++
++      hirgn = elan3mmu->elan3mmu_mtail;
++      if ((lastaddr = hirgn->rgn_mbase + hirgn->rgn_len - 1) < addr)
++          return (tail ? hirgn : NULL);                       /* addr is out of range */
++      
++      if ((addr - base) > (lastaddr - addr))
++          rgn = hirgn;
++      else
++      {
++          rgn = rgn->rgn_mnext;
++          forward++;
++      }
++    }
++    else
++    {
++      lorgn = elan3mmu->elan3mmu_mrgns;
++      if (lorgn->rgn_mbase > addr)
++          return (lorgn);                                     /* lowest regions is higher than addr */
++      if ((addr - lorgn->rgn_mbase) < (base - addr))
++      {
++          rgn = lorgn;                                        /* search forward from head */
++          forward++;
++      }
++    }
++    if (forward)
++    {
++      while ((rgn->rgn_mbase + rgn->rgn_len - 1) < addr)
++          rgn = rgn->rgn_mnext;
++
++      if (rgn->rgn_mbase <= addr)
++          elan3mmu->elan3mmu_mrgnlast = rgn;
++      return (rgn);
++    }
++    else
++    {
++      while (rgn->rgn_mbase > addr)
++      {
++          next = rgn;
++          rgn = rgn->rgn_mprev;
++      }
++      if ((rgn->rgn_mbase + rgn->rgn_len - 1) < addr)
++          return (next);
++      else
++      {
++          elan3mmu->elan3mmu_mrgnlast = rgn;
++          return (rgn);
++      }
++    }
++}
++
++int
++elan3mmu_addrgn_main (ELAN3MMU *elan3mmu, ELAN3MMU_RGN *nrgn)
++{
++    ELAN3MMU_RGN *rgn   = elan3mmu_findrgn_main (elan3mmu, nrgn->rgn_mbase, 1);
++    caddr_t       nbase = nrgn->rgn_mbase;
++    caddr_t     ntop  = nbase + nrgn->rgn_len - 1;
++    caddr_t     base;
++
++    ASSERT (SPINLOCK_HELD (&elan3mmu->elan3mmu_dev->IntrLock) && SPINLOCK_HELD (&elan3mmu->elan3mmu_lock));
++
++    if (rgn == NULL)
++    {
++      elan3mmu->elan3mmu_mrgns = elan3mmu->elan3mmu_mtail = nrgn;
++      nrgn->rgn_mnext = nrgn->rgn_mprev = NULL;
++    }
++    else
++    {
++      base = rgn->rgn_mbase;
++
++      if ((base + rgn->rgn_len - 1) < nbase)                  /* top of region below requested address */
++      {                                                       /* so insert after region (and hence at end */
++          nrgn->rgn_mprev = rgn;                              /* of list */
++          nrgn->rgn_mnext = NULL;
++          rgn->rgn_mnext = elan3mmu->elan3mmu_mtail = nrgn;
++      }
++      else
++      {
++          if (nbase >= base || ntop >= base)
++              return (-1);                                    /* overlapping region */
++
++          nrgn->rgn_mnext = rgn;                              /* insert before region */
++          nrgn->rgn_mprev = rgn->rgn_mprev;
++          rgn->rgn_mprev  = nrgn;
++          if (elan3mmu->elan3mmu_mrgns == rgn)
++              elan3mmu->elan3mmu_mrgns = nrgn;
++          else
++              nrgn->rgn_mprev->rgn_mnext = nrgn;
++      }
++    }
++    elan3mmu->elan3mmu_mrgnlast = nrgn;
++    
++    return (0);
++}
++
++ELAN3MMU_RGN *
++elan3mmu_removergn_main (ELAN3MMU *elan3mmu, caddr_t addr)
++{
++    ELAN3MMU_RGN *rgn = elan3mmu_findrgn_main (elan3mmu, addr, 0);
++    
++    ASSERT (SPINLOCK_HELD (&elan3mmu->elan3mmu_dev->IntrLock) && SPINLOCK_HELD (&elan3mmu->elan3mmu_lock));
++
++    if (rgn == NULL || rgn->rgn_mbase != addr)
++      return (NULL);
++    
++    elan3mmu->elan3mmu_mrgnlast = rgn->rgn_mnext;
++    if (rgn == elan3mmu->elan3mmu_mtail)
++      elan3mmu->elan3mmu_mtail = rgn->rgn_mprev;
++    else
++      rgn->rgn_mnext->rgn_mprev = rgn->rgn_mprev;
++    
++    if (rgn == elan3mmu->elan3mmu_mrgns)
++      elan3mmu->elan3mmu_mrgns = rgn->rgn_mnext;
++    else
++      rgn->rgn_mprev->rgn_mnext = rgn->rgn_mnext;
++
++    return (rgn);
++}
++
++ELAN3MMU_RGN *
++elan3mmu_rgnat_main (ELAN3MMU *elan3mmu, caddr_t addr)
++{
++    ELAN3MMU_RGN *rgn = elan3mmu_findrgn_main (elan3mmu, addr, 0);
++    caddr_t       base;
++
++    if (rgn != NULL && (base = rgn->rgn_mbase) <= addr && addr <= (base + rgn->rgn_len - 1))
++      return (rgn);
++    return (NULL);
++}
++
++int
++elan3mmu_setperm (ELAN3MMU *elan3mmu,
++                caddr_t   maddr,
++                E3_Addr   eaddr,
++                u_int     len,
++                u_int     perm)
++{
++    ELAN3_DEV     *dev = elan3mmu->elan3mmu_dev;
++    ELAN3MMU_RGN *nrgn;
++    unsigned long  flags;
++
++    HAT_PRINTF4 (1, "elan3mmu_setperm: user %p elan %08x len %x perm %x\n", maddr, eaddr, len, perm);
++
++    if ((((uintptr_t) maddr) & PAGEOFFSET) || (eaddr & PAGEOFFSET) || (len & PAGEOFFSET)) 
++    {
++        HAT_PRINTF0 (1, "elan3mmu_setperm:  alignment failure\n");
++      return (EINVAL);
++    }
++
++    if (((uintptr_t) maddr + len - 1) < (uintptr_t) maddr || ((u_long)eaddr + len - 1) < (u_long)eaddr) 
++    {
++      HAT_PRINTF0 (1, "elan3mmu_setperm:  range failure\n");
++      return (EINVAL);
++    }
++
++    ALLOC_ELAN3MMU_RGN(nrgn, TRUE);
++    
++    spin_lock (&elan3mmu->elan3mmu_lock);
++    nrgn->rgn_mbase = maddr;
++    nrgn->rgn_ebase = eaddr;
++    nrgn->rgn_len   = len;
++    nrgn->rgn_perm  = perm;
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++    if (elan3mmu_addrgn_elan (elan3mmu, nrgn) < 0)
++    {
++      HAT_PRINTF0 (1, "elan3mmu_setperm:  elan address exists\n");
++      spin_unlock_irqrestore (&dev->IntrLock, flags);
++      spin_unlock (&elan3mmu->elan3mmu_lock);
++
++      FREE_ELAN3MMU_RGN (nrgn);
++      return (EINVAL);
++    }
++    
++    if (elan3mmu_addrgn_main (elan3mmu, nrgn) < 0)
++    {
++      HAT_PRINTF0 (1, "elan3mmu_setperm:  main address exists\n");
++      elan3mmu_removergn_elan (elan3mmu, eaddr);
++
++      spin_unlock_irqrestore (&dev->IntrLock, flags);
++      spin_unlock (&elan3mmu->elan3mmu_lock);
++
++      FREE_ELAN3MMU_RGN (nrgn);
++      return (EINVAL);
++    }
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++    spin_unlock (&elan3mmu->elan3mmu_lock);
++
++    return (ESUCCESS);
++}
++
++void
++elan3mmu_clrperm (ELAN3MMU *elan3mmu,
++                E3_Addr   addr,
++                u_int     len)
++{
++    E3_Addr       raddr;
++    E3_Addr       rtop;
++    ELAN3MMU_RGN *nrgn;
++    ELAN3MMU_RGN *rgn;
++    ELAN3MMU_RGN *rgn_next;
++    u_int       ssize;
++    unsigned long flags;
++    int                 res;
++
++    HAT_PRINTF2 (1, "elan3mmu_clrperm: elan %08x len %x\n", addr, len);
++
++    raddr = (addr & PAGEMASK);
++    rtop = ((addr + len - 1) & PAGEMASK) + PAGEOFFSET;
++
++    ALLOC_ELAN3MMU_RGN (nrgn, TRUE);
++
++    spin_lock (&elan3mmu->elan3mmu_lock);
++    
++    for (rgn = elan3mmu_findrgn_elan (elan3mmu, addr, 0); rgn != NULL; rgn = rgn_next)
++    {
++      if (rtop < rgn->rgn_ebase)                              /* rtop was in a gap */
++          break;
++      
++      rgn_next = rgn->rgn_enext;                              /* Save next region pointer */
++      
++      if (raddr <= rgn->rgn_ebase && rtop >= (rgn->rgn_ebase + rgn->rgn_len - 1)) 
++      {
++          /* whole region is cleared */
++          elan3mmu_unload (elan3mmu, rgn->rgn_ebase, rgn->rgn_len, PTE_UNLOAD);
++          
++          spin_lock_irqsave (&elan3mmu->elan3mmu_dev->IntrLock, flags);
++          elan3mmu_removergn_elan (elan3mmu, rgn->rgn_ebase);
++          elan3mmu_removergn_main (elan3mmu, rgn->rgn_mbase);
++          spin_unlock_irqrestore (&elan3mmu->elan3mmu_dev->IntrLock, flags);
++
++          FREE_ELAN3MMU_RGN (rgn);
++      }
++      else if (raddr <= rgn->rgn_ebase)
++      {
++          /* clearing at beginning, so shrink size and increment base ptrs */
++          ssize = rtop - rgn->rgn_ebase + 1;
++
++          elan3mmu_unload (elan3mmu, rgn->rgn_ebase, ssize, PTE_UNLOAD);
++          
++          spin_lock_irqsave (&elan3mmu->elan3mmu_dev->IntrLock, flags);
++          rgn->rgn_mbase += ssize;
++          rgn->rgn_ebase += ssize;
++          rgn->rgn_len   -= ssize;
++          spin_unlock_irqrestore (&elan3mmu->elan3mmu_dev->IntrLock, flags);
++          
++      }
++      else if (rtop >= (rgn->rgn_ebase + rgn->rgn_len - 1))
++      {
++          /* clearing at end, so just shrink length of region */
++          ssize = ((rgn->rgn_ebase + rgn->rgn_len - 1) - raddr) + 1;
++
++          elan3mmu_unload (elan3mmu, raddr, ssize, PTE_UNLOAD);
++
++          spin_lock_irqsave (&elan3mmu->elan3mmu_dev->IntrLock, flags);
++          rgn->rgn_len -= ssize;
++          spin_unlock_irqrestore (&elan3mmu->elan3mmu_dev->IntrLock, flags);
++      }
++      else
++      {
++          /* the section to go is in the middle,  so need to  */
++          /* split it into two regions */
++          elan3mmu_unload (elan3mmu, raddr, rtop - raddr + 1, PTE_UNLOAD);
++
++          spin_lock_irqsave (&elan3mmu->elan3mmu_dev->IntrLock, flags);
++
++          ASSERT (nrgn != NULL);
++
++          nrgn->rgn_mbase = rgn->rgn_mbase + (rtop - rgn->rgn_ebase + 1);;
++          nrgn->rgn_ebase = rtop + 1;
++          nrgn->rgn_len   = ((rgn->rgn_ebase + rgn->rgn_len - 1) - rtop);
++          nrgn->rgn_perm  = rgn->rgn_perm;
++
++          rgn->rgn_len = (raddr - rgn->rgn_ebase);            /* shrink original region */
++
++          res = elan3mmu_addrgn_elan (elan3mmu, nrgn);        /* insert new region */
++          ASSERT (res == 0);                                  /* which cannot fail */
++
++          res = elan3mmu_addrgn_main (elan3mmu, nrgn);        
++          ASSERT (res == 0);
++          spin_unlock_irqrestore (&elan3mmu->elan3mmu_dev->IntrLock, flags);
++
++          nrgn = NULL;
++      }
++    }
++    spin_unlock (&elan3mmu->elan3mmu_lock);
++
++    if (nrgn != NULL)
++      FREE_ELAN3MMU_RGN (nrgn);
++}
++
++int
++elan3mmu_checkperm (ELAN3MMU *elan3mmu,
++                  E3_Addr   addr,
++                  u_int     len,
++                  u_int     access)
++{
++    E3_Addr    raddr = (((E3_Addr) addr) & PAGEMASK);
++    u_int        rtop = ((addr + len - 1) & PAGEMASK) + PAGEOFFSET;
++    u_int      rsize = rtop - raddr + 1;
++    ELAN3MMU_RGN *rgn;
++
++    HAT_PRINTF3 (1, "elan3mmu_checkperm: user %08x len %x access %x\n", addr, len, access);
++    
++    
++    if ((raddr + rsize - 1) < raddr)
++      return (ENOMEM);
++    
++    spin_lock (&elan3mmu->elan3mmu_lock);
++    if ((rgn = elan3mmu_rgnat_elan (elan3mmu, raddr)) == (ELAN3MMU_RGN *) NULL)
++    {
++      spin_unlock (&elan3mmu->elan3mmu_lock);
++      return (ENOMEM);
++    }
++    else
++    {
++      register int ssize;
++      
++      for (; rsize != 0; rsize -= ssize, raddr += ssize)
++      {
++          if (raddr > (rgn->rgn_ebase + rgn->rgn_len - 1))
++          {
++              rgn  = rgn->rgn_enext;
++              
++              if (rgn == NULL || raddr != rgn->rgn_ebase)
++              {
++                  spin_unlock (&elan3mmu->elan3mmu_lock);
++                  return (ENOMEM);
++              }
++          }
++          if ((raddr + rsize - 1) > (rgn->rgn_ebase + rgn->rgn_len - 1))
++              ssize = ((rgn->rgn_ebase + rgn->rgn_len - 1) - raddr) + 1;
++          else
++              ssize = rsize;
++          
++          HAT_PRINTF4 (1, "elan3mmu_checkperm : rgn %x -> %x perm %x access %x\n",
++                       rgn->rgn_ebase, rgn->rgn_ebase + rgn->rgn_len, rgn->rgn_perm, access);
++
++          if (ELAN3_INCOMPAT_ACCESS (rgn->rgn_perm, access))
++          {
++              spin_unlock (&elan3mmu->elan3mmu_lock);
++              return (EACCES);
++          }
++      }
++    }
++    
++    spin_unlock (&elan3mmu->elan3mmu_lock);
++    
++    return (ESUCCESS);
++}
++
++caddr_t
++elan3mmu_mainaddr (ELAN3MMU *elan3mmu, E3_Addr addr)
++{
++    ELAN3MMU_RGN *rgn;
++    caddr_t     raddr;
++    
++    spin_lock (&elan3mmu->elan3mmu_lock);
++    if ((rgn = elan3mmu_rgnat_elan (elan3mmu, addr)) == (ELAN3MMU_RGN *) NULL)
++      raddr = NULL;
++    else
++      raddr = rgn->rgn_mbase + (addr - rgn->rgn_ebase);
++    spin_unlock (&elan3mmu->elan3mmu_lock);
++
++    return (raddr);
++}
++
++E3_Addr
++elan3mmu_elanaddr (ELAN3MMU *elan3mmu, caddr_t addr)
++{
++    ELAN3MMU_RGN *rgn;
++    E3_Addr       raddr;
++
++    spin_lock (&elan3mmu->elan3mmu_lock);
++    if ((rgn = elan3mmu_rgnat_main (elan3mmu, addr)) == (ELAN3MMU_RGN *) NULL)
++      raddr = (E3_Addr) 0;
++    else
++      raddr = rgn->rgn_ebase + (addr - rgn->rgn_mbase);
++    spin_unlock (&elan3mmu->elan3mmu_lock);
++
++    return (raddr);
++}
++
++void
++elan3mmu_displayrgns(ELAN3MMU *elan3mmu)
++{
++    ELAN3MMU_RGN *rgn;
++
++    spin_lock (&elan3mmu->elan3mmu_lock);
++    HAT_PRINTF0 (1, "elan3mmu_displayrgns: main regions\n");
++    for (rgn = elan3mmu->elan3mmu_mrgns; rgn; rgn = (rgn->rgn_mnext == elan3mmu->elan3mmu_mrgns) ? NULL : rgn->rgn_mnext)
++      HAT_PRINTF5 (1, "    RGN %p ebase %08x mbase %p len %08x perm %08x\n", rgn, rgn->rgn_ebase, rgn->rgn_mbase, rgn->rgn_len, rgn->rgn_perm);
++    HAT_PRINTF0 (1, "elan3mmu_displayrgns: elan regions\n");
++    for (rgn = elan3mmu->elan3mmu_ergns; rgn; rgn = (rgn->rgn_enext == elan3mmu->elan3mmu_ergns) ? NULL : rgn->rgn_enext)
++      HAT_PRINTF5 (1, "    RGN %p ebase %08x mbase %p len %08x perm %08x\n", rgn, rgn->rgn_ebase, rgn->rgn_mbase, rgn->rgn_len, rgn->rgn_perm);
++
++    spin_unlock (&elan3mmu->elan3mmu_lock);
++}
++
++/*============================================================================*/
++/* Private functions */
++#define ELAN3_PTE_IS_VALID(ptbl, pte) \
++          ((ptbl->ptbl_flags & PTBL_KERNEL) ? \
++          (pte&(~ELAN3_PTE_REF)) != elan3mmu_kernel_invalid_pte(ptbl->ptbl_elan3mmu) : \
++          ELAN3_PTE_VALID(pte))
++
++void
++elan3mmu_expand (ELAN3MMU *elan3mmu, E3_Addr addr, int len, int level, int attr)
++{
++    ELAN3_PTBL          *ptbl;
++    sdramaddr_t               pte;
++    spinlock_t               *lock;
++    u_int             span;
++    unsigned long       flags;
++
++    HAT_PRINTF3 (1, "elan3mmu_expand: elan3mmu %p %08x to %08x\n", elan3mmu, 
++               addr, addr + len);
++
++    for ( ; len != 0; addr += span, len -= span)
++    {
++      /* as we asked for level 3 we know its a pte */
++      pte = elan3mmu_ptealloc (elan3mmu, addr, level, &ptbl, &lock, attr, &flags);
++
++      switch (level)
++      {
++      case PTBL_LEVEL_3:
++          span = MIN(len, ELAN3_L3_PTSIZE - ((E3_Addr) addr & ELAN3_L3_PTOFFSET));
++          break;
++      case PTBL_LEVEL_2:
++          span = MIN(len, ELAN3_L2_PTSIZE - ((E3_Addr) addr & ELAN3_L2_PTOFFSET));
++          break;
++      default:
++          span = len;
++          break;
++      }
++      
++      if (pte != (sdramaddr_t) 0)
++          elan3mmu_unlock_ptbl (ptbl, lock, flags);
++    }
++}
++
++void
++elan3mmu_reserve (ELAN3MMU *elan3mmu, E3_Addr addr, u_int npages, sdramaddr_t *ptes)
++{
++    ELAN3_PTBL          *ptbl;
++    sdramaddr_t               pte;
++    spinlock_t               *lock;
++    u_int             span;
++    int                       len;
++    int                       i;
++    unsigned long       flags;
++
++    HAT_PRINTF3 (1, "elan3mmu_reserve: elan3mmu %p %08x to %08x\n", elan3mmu, 
++               addr, addr + (npages << ELAN3_PAGE_SHIFT));
++
++    for (len = (npages << ELAN3_PAGE_SHIFT); len != 0; addr += span, len -= span)
++    {
++      /* as we asked for level 3 we know its a pte */
++      pte = elan3mmu_ptealloc (elan3mmu, addr, 3, &ptbl, &lock, 0, &flags);
++
++      span = MIN(len, ELAN3_L3_PTSIZE - ((E3_Addr) addr & ELAN3_L3_PTOFFSET));
++      
++      if (ptes != NULL)
++      {
++          for (i = 0; i < span; i += ELAN3_PAGE_SIZE, pte += ELAN3_PTE_SIZE)
++              *ptes++ = pte;
++          ptbl->ptbl_valid += (span >> ELAN3_PAGE_SHIFT);
++
++          HAT_PRINTF4 (2, "elan3mmu_reserve: inc valid for level %d ptbl %p to %d   (%d)\n", 
++                   PTBL_LEVEL(ptbl->ptbl_flags), ptbl, ptbl->ptbl_valid, (span >> ELAN3_PAGE_SHIFT));
++
++      }
++
++      elan3mmu_unlock_ptbl (ptbl, lock, flags);
++    }
++}
++
++void
++elan3mmu_release (ELAN3MMU *elan3mmu, E3_Addr addr, u_int npages, sdramaddr_t *ptes)
++{
++    ELAN3_DEV           *dev = elan3mmu->elan3mmu_dev;
++    ELAN3_PTBL          *ptbl;
++    sdramaddr_t               pte;
++    ELAN3_PTE         tpte;
++    spinlock_t               *lock;
++    u_int             span;
++    int                       len;
++    int                       i;
++    int                       level;
++    unsigned long       flags;
++    
++    HAT_PRINTF3 (1, "elan3mmu_release: elan3mmu %p %08x to %08x\n", elan3mmu, 
++               addr, addr + (npages << ELAN3_PAGE_SHIFT));
++
++    if (ptes == NULL)
++      return;
++
++    tpte = elan3mmu_kernel_invalid_pte (elan3mmu);
++
++    for (len = (npages << ELAN3_PAGE_SHIFT); len != 0; addr += span, len -= span)
++    {
++      /* as we asked for level 3 we know its a pte */
++      pte = elan3mmu_ptefind(elan3mmu, addr, &level, &ptbl, &lock, &flags);
++      ASSERT (level == PTBL_LEVEL_3);
++
++      span = MIN(len, ELAN3_L3_PTSIZE - ((E3_Addr) addr & ELAN3_L3_PTOFFSET));
++
++
++      for (i = 0 ; i < span; i += ELAN3_PAGE_SIZE, pte += ELAN3_PTE_SIZE)
++          elan3_writepte (dev, pte, tpte);
++      ptbl->ptbl_valid -= (span >> ELAN3_PAGE_SHIFT);
++
++      HAT_PRINTF3 (2, "elan3mmu_release: inc valid for level %d ptbl %p to %d\n", 
++                   PTBL_LEVEL(ptbl->ptbl_flags), ptbl, ptbl->ptbl_valid);
++
++      elan3mmu_unlock_ptbl (ptbl, lock, flags);
++    }
++    ElanFlushTlb (elan3mmu->elan3mmu_dev);
++}
++
++void
++elan3mmu_pteload (ELAN3MMU *elan3mmu, int level, E3_Addr addr, physaddr_t paddr, int perm, int attr)
++    
++{
++    ELAN3_DEV     *dev;
++    ELAN3_PTBL    *ptbl;
++    spinlock_t   *lock;
++    unsigned long flags;
++    ELAN3_PTE      newpte;
++    ELAN3_PTE      oldpte;
++    sdramaddr_t   pte;
++
++    ASSERT((level == PTBL_LEVEL_2) || (level == PTBL_LEVEL_3));
++
++    /* Generate the new pte which we're going to load */
++    dev = elan3mmu->elan3mmu_dev;
++
++    newpte = elan3mmu_phys_to_pte (dev, paddr, perm);
++    
++    if (attr & PTE_LOAD_BIG_ENDIAN)
++      newpte |= ELAN3_PTE_BIG_ENDIAN;
++
++    HAT_PRINTF4 (1, "elan3mmu_pteload: elan3mmu %p level %d addr %x pte %llx\n", elan3mmu, level, addr, (long long) newpte);
++    HAT_PRINTF5 (1, "elan3mmu_pteload:%s%s%s perm=%d phys=%llx\n",
++               (newpte & ELAN3_PTE_LOCAL)  ? " local" : "",
++               (newpte & ELAN3_PTE_64_BIT)     ? " 64 bit" : "",
++               (newpte & ELAN3_PTE_BIG_ENDIAN) ? " big-endian" : " little-endian",
++               (u_int) (newpte & ELAN3_PTE_PERM_MASK) >> ELAN3_PTE_PERM_SHIFT,
++               (unsigned long long) (newpte & ELAN3_PTE_PFN_MASK));
++                
++    if (level == PTBL_LEVEL_3)
++      pte = elan3mmu_ptealloc (elan3mmu, addr, level, &ptbl, &lock, attr, &flags);
++    else
++    {
++      sdramaddr_t ptp = elan3mmu_ptealloc (elan3mmu, addr, level, &ptbl, &lock, attr, &flags);
++
++      pte = elan3mmu_ptp2pte (elan3mmu, ptp, level);
++
++      HAT_PRINTF3 (2, "elan3mmu_pteload: level %d ptp at %lx => pte at %lx\n", level, ptp, pte);
++    }
++
++    if (pte == (sdramaddr_t) 0)
++    {
++      ASSERT (level == PTBL_LEVEL_3 && (attr & (PTE_NO_SLEEP | PTE_NO_STEAL)) == (PTE_NO_SLEEP | PTE_NO_STEAL));
++      return;
++    }
++
++    ASSERT (ptbl->ptbl_elan3mmu == elan3mmu);
++    ASSERT (PTBL_LEVEL(ptbl->ptbl_flags) == level);
++    ASSERT (PTBL_IS_LOCKED (ptbl->ptbl_flags));
++    
++    oldpte = elan3_readpte (dev, pte);
++
++    HAT_PRINTF3 (2, "elan3mmu_pteload: modify pte at %lx from %llx to %llx\n", pte, (long long) oldpte, (long long) newpte);
++
++    if (ELAN3_PTE_IS_VALID(ptbl, oldpte))
++    {
++      ELAN3MMU_STAT(ptereload);
++
++      ASSERT ((newpte & ~((E3_uint64)ELAN3_PTE_PERM_MASK | ELAN3_RM_MASK)) == (oldpte & ~((E3_uint64)ELAN3_PTE_PERM_MASK | ELAN3_RM_MASK)));
++      
++      if ((newpte & ~ELAN3_RM_MASK) != (oldpte & ~ELAN3_RM_MASK))
++      {
++          /* We're modifying a valid translation, it must be mapping the same page */
++          /* so we use elan3_modifypte to not affect the referenced and modified bits */
++          elan3_modifypte (dev, pte, newpte);
++
++
++          ElanFlushTlb (elan3mmu->elan3mmu_dev);
++      }
++    }
++    else
++    {
++      ELAN3MMU_STAT(pteload);
++
++      ptbl->ptbl_valid++;
++
++      HAT_PRINTF3 (2, "elan3mmu_pteload: inc valid for level %d ptbl %p to %d\n", 
++                   PTBL_LEVEL(ptbl->ptbl_flags), ptbl, ptbl->ptbl_valid);
++
++      HAT_PRINTF2 (2, "elan3mmu_pteload: write pte %lx to %llx\n", pte, (long long) newpte);
++
++      elan3_writepte (dev, pte, newpte);
++
++      if (ptbl->ptbl_flags & PTBL_KERNEL)
++          ElanFlushTlb (elan3mmu->elan3mmu_dev);
++
++    }
++
++    elan3mmu_unlock_ptbl (ptbl, lock, flags);
++}
++
++void
++elan3mmu_unload (ELAN3MMU *elan3mmu, E3_Addr addr, u_int len, int attr)
++{
++    ELAN3_PTBL          *ptbl;
++    sdramaddr_t         ptp;
++    spinlock_t               *lock;
++    int                       level;
++    u_int             span;
++    unsigned long     flags;
++
++    HAT_PRINTF3(1, "elan3mmu_unload (elan3mmu %p addr %x -> %x)\n", elan3mmu, addr, addr+len-1);
++
++    for (; len != 0; addr += span, len -= span)
++    {
++      ptp  = elan3mmu_ptefind(elan3mmu, addr, &level, &ptbl, &lock, &flags);
++
++      span = MIN(len, ELAN3_L3_PTSIZE - ((E3_Addr) addr & ELAN3_L3_PTOFFSET));
++
++      if (ptp != (sdramaddr_t) 0)
++      {
++          HAT_PRINTF2 (2, "elan3mmu_unload: unload [%x,%x]\n", addr, addr + span);
++          
++          if ( level ==  PTBL_LEVEL_3 ) 
++              elan3mmu_unload_loop (elan3mmu, ptbl, ptp - PTBL_TO_PTADDR(ptbl), span >> ELAN3_PAGE_SHIFT, attr);
++          else
++          {
++              ELAN3_PTP    invalidptp = ELAN3_INVALID_PTP;
++              ELAN3_DEV   *dev = elan3mmu->elan3mmu_dev;
++              ELAN3_PTBL  *lXptbl;
++              ELAN3_PTP    tptp;
++              int         idx;
++
++              tptp = elan3_readptp (elan3mmu->elan3mmu_dev, ptp);
++
++              ASSERT (ELAN3_PTP_TYPE(tptp) == ELAN3_ET_PTE);
++
++              lXptbl = elan3mmu_ta_to_ptbl (elan3mmu, &tptp);
++              idx    = (PTP_TO_PT_PADDR(tptp) - PTBL_TO_PTADDR(lXptbl))/ELAN3_PTE_SIZE;
++
++              if ( level == PTBL_LEVEL_1) 
++                  span = MIN(len, ELAN3_L2_PTSIZE - ((E3_Addr) addr & ELAN3_L2_PTOFFSET));
++              else
++                  span = MIN(len, ELAN3_L3_PTSIZE - ((E3_Addr) addr & ELAN3_L3_PTOFFSET));
++
++              /* invalidate the ptp. */
++              elan3_writeptp (dev, ptp, invalidptp);
++              if (! (attr & PTE_UNLOAD_NOFLUSH))
++                  ElanFlushTlb (dev);     
++    
++              elan3mmu_free_pte ( dev, elan3mmu, lXptbl, idx); 
++
++              ptbl->ptbl_valid--;
++
++              HAT_PRINTF3 (2, "elan3mmu_unload: dec valid for level %d ptbl %p to %d\n", 
++                           PTBL_LEVEL(ptbl->ptbl_flags), ptbl, ptbl->ptbl_valid);     
++
++          }
++          elan3mmu_unlock_ptbl (ptbl, lock, flags);
++      }
++    }
++}
++
++static void
++elan3mmu_unload_loop (ELAN3MMU *elan3mmu, ELAN3_PTBL *ptbl, int first_valid, int nptes, int flags)
++{
++    ELAN3_DEV   *dev = elan3mmu->elan3mmu_dev;
++    sdramaddr_t pte;
++    ELAN3_PTE    tpte;
++    int         last_valid = first_valid + nptes;
++    int               i;
++    
++    HAT_PRINTF3 (1, "elan3mmu_unloadloop: ptbl %p entries [%d->%d]\n", ptbl, first_valid, last_valid);
++
++    ASSERT (PTBL_IS_LOCKED (ptbl->ptbl_flags));
++    ASSERT (PTBL_LEVEL(ptbl->ptbl_flags) == PTBL_LEVEL_3);
++    
++    pte = PTBL_TO_PTADDR(ptbl) + first_valid;
++    
++    for (i = first_valid; i < last_valid; i++, pte += ELAN3_PTE_SIZE)
++    {
++      if (ptbl->ptbl_valid == 0)
++          break;
++
++      tpte = elan3_readpte (dev, pte);
++      if (! ELAN3_PTE_IS_VALID(ptbl, tpte))
++          continue;
++      
++      elan3mmu_pteunload (ptbl, pte, flags, NO_MLIST_LOCK);
++    }
++}
++
++void
++elan3mmu_pteunload (ELAN3_PTBL *ptbl, sdramaddr_t pte, int flags, int got_mlist_lock)
++{
++    ELAN3_DEV   *dev = ptbl->ptbl_elan3mmu->elan3mmu_dev;
++    ELAN3_PTE    tpte;
++
++    ASSERT (PTBL_LEVEL (ptbl->ptbl_flags) == PTBL_LEVEL_3);
++    ASSERT (PTBL_IS_LOCKED (ptbl->ptbl_flags));
++
++    HAT_PRINTF2 (1, "elan3mmu_pteunload: ptbl %p pte %lx\n", ptbl, pte);
++
++    ELAN3MMU_STAT (pteunload);
++
++    elan3_invalidatepte (dev, pte);
++
++    if (! (flags & PTE_UNLOAD_NOFLUSH))
++      ElanFlushTlb (dev);
++    
++    tpte = ELAN3_INVALID_PTE;
++    elan3_writepte (dev, pte, tpte);
++    
++    if (ptbl->ptbl_flags & PTBL_KERNEL)
++    {
++      tpte = elan3mmu_kernel_invalid_pte(ptbl->ptbl_elan3mmu);
++
++      elan3_writepte (dev, pte, tpte);
++    }
++
++    ptbl->ptbl_valid--;
++
++    HAT_PRINTF3 (2, "elan3mmu_pteunload: dec valid for level %d ptbl %p to %d\n", 
++               PTBL_LEVEL(ptbl->ptbl_flags), ptbl, ptbl->ptbl_valid);
++
++}
++
++void
++elan3mmu_ptesync (ELAN3_PTBL *ptbl, sdramaddr_t pte, int flags, int got_mlist_lock)
++{
++
++}
++
++/*
++ * Create more page tables at a given level for this Elan.
++ */
++static ELAN3_PTBL *
++elan3mmu_create_ptbls (ELAN3_DEV *dev, int level, int attr, int keep)
++{
++    sdramaddr_t         pts;
++    ELAN3_PTBL    *ptbl;
++    ELAN3_PTBL    *first;
++    ELAN3_PTBL    *last;
++    ELAN3_PTBL_GR *ptg;
++    register int  i;
++    register int  inc;
++    
++    HAT_PRINTF1 (2, "elan3mmu_create_ptbls: create level %d ptbls\n", level);
++
++    pts = elan3_sdram_alloc (dev, PTBL_GROUP_SIZE);
++    if (pts == (sdramaddr_t) 0)
++    {
++      HAT_PRINTF0 (2, "elan3mmu_create_ptbls: cannot map elan pages\n");
++
++      ELAN3MMU_STAT (create_ptbl_failed);
++      return (NULL);
++    }
++    
++    HAT_PRINTF1 (2, "elan3mmu_create_ptbls: pts at %lx\n", pts);
++    
++    ALLOC_PTBL_GR (ptg, !(attr & PTE_NO_SLEEP));              /* Allocate the group of page tables */
++    if (ptg == NULL)                                          /* for this page */
++    {
++      HAT_PRINTF0 (2, "elan3mmu_create_ptbls: cannot allocate page table group\n");
++
++      elan3_sdram_free (dev, pts, PTBL_GROUP_SIZE);
++
++      ELAN3MMU_STAT (create_ptbl_failed);
++      return (NULL);
++    }
++
++    HAT_PRINTF1 (2, "elan3mmu_create_ptbls: ptg is %p\n", ptg);
++    
++    ElanSetPtblGr (dev, pts, ptg);
++    
++    HAT_PRINTF4 (2, "elan3mmu_create_ptbls: zeroing %d bytes at %lx, %d bytes at %p\n",
++               PTBL_GROUP_SIZE, pts, (int) sizeof (ELAN3_PTBL_GR), ptg);
++
++#ifndef zero_all_ptbls
++    elan3_sdram_zeroq_sdram (dev, pts, PTBL_GROUP_SIZE);              /* Ensure that all PTEs/PTPs are invalid */
++#endif
++    bzero ((caddr_t) ptg, sizeof (ELAN3_PTBL_GR));
++    
++    ptg->pg_addr  = pts;
++    ptg->pg_level = level;
++
++    ptbl = ptg->pg_ptbls;                                     /* Initialise the index in all page tables */
++    for (i = 0; i < PTBLS_PER_GROUP_MAX; i++)
++    {
++      ptbl->ptbl_index = (u_char) i;
++      ptbl->ptbl_next  = (ELAN3_PTBL *) 0xdeaddead;
++      ptbl++;
++    }
++    
++    switch (level)                                            /* Determine the number of ptbls we can  */
++    {                                                         /* allocate from this page, by jumping  */
++    case PTBL_LEVEL_X: inc = PTBLS_PER_PTBL_LX; break;                /* multiples of the smallest. */
++    case PTBL_LEVEL_1: inc = PTBLS_PER_PTBL_L1; break;
++    case PTBL_LEVEL_2: inc = PTBLS_PER_PTBL_L2; break;
++    case PTBL_LEVEL_3: inc = PTBLS_PER_PTBL_L3; break;
++    default:           inc = PTBLS_PER_PTBL_L3; break;
++    }
++
++    ptbl = ptg->pg_ptbls;                                     /* Chain them together */
++    for (i = 0; i < PTBLS_PER_GROUP_MAX; i += inc, ptbl += inc)
++      ptbl->ptbl_next = ptbl + inc;
++
++    first = ptg->pg_ptbls;                                    /* Determine list of */
++    last  = first + PTBLS_PER_GROUP_MAX - inc;                        /* ptbls to add to free list */
++    if (! keep)
++      ptbl = NULL;
++    else
++    {
++      ptbl  = first;
++      first = first->ptbl_next;
++    }
++    
++    spin_lock (&dev->Level[level].PtblLock);
++    dev->Level[level].PtblTotal     += PTBLS_PER_GROUP_MAX/inc;               /* Increment the counts */
++    dev->Level[level].PtblFreeCount += PTBLS_PER_GROUP_MAX/inc;
++
++    ELAN3MMU_SET_STAT (num_ptbl_level[level], dev->Level[level].PtblTotal);
++
++    if (keep)
++      dev->Level[level].PtblFreeCount--;
++    
++    last->ptbl_next = dev->Level[level].PtblFreeList;                 /* And add to free list */
++    dev->Level[level].PtblFreeList = first;
++    spin_unlock (&dev->Level[level].PtblLock);
++    
++    spin_lock (&dev->PtblGroupLock);
++    ptg->pg_next = dev->Level[level].PtblGroupList;
++    dev->Level[level].PtblGroupList = ptg;
++    spin_unlock (&dev->PtblGroupLock);
++
++    HAT_PRINTF1 (2, "elan3mmu_create_ptbls: returning ptbl %p\n", ptbl);
++    
++    return (ptbl);
++}
++
++static ELAN3_PTBL *
++elan3mmu_ta_to_ptbl (ELAN3MMU *elan3mmu, ELAN3_PTP *ptp)
++{
++    E3_Addr     ptpa  = PTP_TO_PT_PADDR(*ptp);
++    ELAN3_PTBL_GR *pg    = ElanGetPtblGr (elan3mmu->elan3mmu_dev, (sdramaddr_t)ptpa & ~(PTBL_GROUP_SIZE-1));
++    
++    return (pg->pg_ptbls + ((ptpa - pg->pg_addr) >> ELAN3_PT_SHIFT));
++}
++
++static ELAN3_PTBL *
++elan3mmu_alloc_lXptbl (ELAN3_DEV *dev, int attr,  ELAN3MMU *elan3mmu)
++{
++    ELAN3_PTBL *ptbl = NULL;
++
++    spin_lock (&dev->Level[PTBL_LEVEL_X].PtblLock);
++    if (dev->Level[PTBL_LEVEL_X].PtblFreeList)
++    {
++      ptbl = dev->Level[PTBL_LEVEL_X].PtblFreeList;
++
++      HAT_PRINTF1 (2, "elan3mmu_alloc_lXptbl: found ptbl %p on free list\n", ptbl);
++
++      dev->Level[PTBL_LEVEL_X].PtblFreeList = ptbl->ptbl_next;
++      dev->Level[PTBL_LEVEL_X].PtblFreeCount--;
++    }
++    spin_unlock (&dev->Level[PTBL_LEVEL_X].PtblLock);
++    
++    if (ptbl == NULL) 
++    {
++      ptbl = elan3mmu_create_ptbls (dev, PTBL_LEVEL_X, attr, 1);
++
++      HAT_PRINTF1 (2, "elan3mmu_alloc_lXptbl: created level X ptbl %p\n", ptbl);
++    }
++
++    if (ptbl == NULL)
++    {
++      if ((attr & PTE_NO_STEAL))
++      {
++          HAT_PRINTF0 (2, "elan3mmu_alloc_lXptbl: not allowed to steal ptbl for use at level 2\n");
++          return NULL;
++      }
++
++      ELAN3MMU_STAT(lX_alloc_l3);
++
++      ptbl = elan3mmu_steal_l3ptbl (dev, attr);
++      
++      HAT_PRINTF1 (2, "elan3mmu_alloc_lXptbl: stolen level3 ptbl %p used as level 2\n", ptbl);
++    }
++
++    ptbl->ptbl_elan3mmu = elan3mmu;
++    ptbl->ptbl_base     = 0;
++    ptbl->ptbl_parent   = 0;
++    ptbl->ptbl_flags    = PTBL_LEVEL_X | PTBL_ALLOCED;
++    
++    HAT_PRINTF2 (2, "elan3mmu_alloc_lXptbl: ptbl %p dev %p\n", ptbl, dev);
++
++#ifdef zero_all_ptbls
++    elan3_sdram_zero_sdarm (dev, PTBL_TO_PTADDR(ptbl), ELAN3_LX_ENTRIES*ELAN3_PTE_SIZE);
++#endif
++
++    return (ptbl);
++}
++
++static ELAN3_PTBL *
++elan3mmu_alloc_pte (ELAN3_DEV *dev, ELAN3MMU *elan3mmu, int *idx)
++{
++    ELAN3_PTBL   * ptbl_ptr;
++    int           index;
++
++    /* lock whilst looking for space */
++    spin_lock (&elan3mmu->elan3mmu_lXptbl_lock);
++    
++    /* walk the lXptbl list */
++    ptbl_ptr = elan3mmu->elan3mmu_lXptbl;
++    while ( ptbl_ptr != NULL ) 
++    {
++      /* does this ptlb have any free ones */
++      if (  (index = ptbl_ptr->ptbl_valid) < ELAN3_LX_ENTRIES) 
++      {
++          /*  better to search  from valid count as its likly to be free */
++          index = ptbl_ptr->ptbl_valid; 
++          do {
++              if ((ptbl_ptr->ptbl_base & (1 << index)) == 0)
++                  goto found;
++
++              /* move index on and wrap back to start if needed */
++              if ((++index) == ELAN3_LX_ENTRIES) 
++                  index = 0;
++          } while (index != ptbl_ptr->ptbl_valid);
++
++          panic ("elan3mmu_alloc_pte: has ptbl valid < 32 when but no free pte's");
++      }
++      ptbl_ptr = ptbl_ptr->ptbl_parent;
++    }
++      
++    /* unlock so we can create space */
++    spin_unlock (&elan3mmu->elan3mmu_lXptbl_lock); 
++
++    /* if create some more */
++    ptbl_ptr = elan3mmu_alloc_lXptbl(dev, 0, elan3mmu);
++
++    /* get the lock again */
++    spin_lock (&elan3mmu->elan3mmu_lXptbl_lock);
++      
++    /* add to front of list as its obviously got free ones on it */
++    ptbl_ptr->ptbl_parent     = elan3mmu->elan3mmu_lXptbl;
++    elan3mmu->elan3mmu_lXptbl = ptbl_ptr;
++
++    /* grap the first one */
++    index = 0;
++    
++ found:
++    ptbl_ptr->ptbl_base |= (1 << index);
++    ptbl_ptr->ptbl_valid++;
++
++    HAT_PRINTF3 (2, "elan3mmu_alloc_pte: inc valid for level %d ptbl %p to %d\n", 
++               PTBL_LEVEL(ptbl_ptr->ptbl_flags), ptbl_ptr, ptbl_ptr->ptbl_valid);
++
++    /* release the loc and return it */
++    spin_unlock (&elan3mmu->elan3mmu_lXptbl_lock); 
++
++    *idx = index;
++    return (ptbl_ptr);
++}
++
++static ELAN3_PTBL *
++elan3mmu_alloc_l1ptbl (ELAN3_DEV *dev, int attr, ELAN3MMU *elan3mmu)
++{
++    ELAN3_PTBL *ptbl = NULL;
++    ELAN3_PTBL *p;
++    int i,j;
++    
++    spin_lock (&dev->Level[PTBL_LEVEL_1].PtblLock);
++    if (dev->Level[PTBL_LEVEL_1].PtblFreeList)
++    {
++      ptbl = dev->Level[PTBL_LEVEL_1].PtblFreeList;
++      dev->Level[PTBL_LEVEL_1].PtblFreeList = ptbl->ptbl_next;
++      dev->Level[PTBL_LEVEL_1].PtblFreeCount--;
++    }
++    spin_unlock (&dev->Level[PTBL_LEVEL_1].PtblLock);
++    
++    if (ptbl == NULL)
++      ptbl = elan3mmu_create_ptbls (dev, PTBL_LEVEL_1, attr, 1);
++    
++    if (ptbl == NULL)
++      panic ("elan3mmu_alloc_l1ptbl: cannot alloc ptbl");
++    
++    for (p = ptbl, j = i = 0; i < PTBLS_PER_PTBL_L1; i++, p++)
++    {
++      p->ptbl_elan3mmu = elan3mmu;
++      p->ptbl_base     = VA2BASE (j);
++      p->ptbl_flags    = PTBL_LEVEL_1 | PTBL_GROUPED;
++      p->ptbl_parent   = NULL;
++      
++      j += L1_VA_PER_PTBL;
++    }
++    
++    /* Now mark the real page table as allocated */
++    /* level 1 ptbls are returned unlocked */
++    ptbl->ptbl_flags = PTBL_LEVEL_1 | PTBL_ALLOCED;
++    
++    HAT_PRINTF2 (2, "elan3mmu_alloc_l1ptbl: ptbl %p dev %p\n", ptbl, dev);
++
++#ifdef zero_all_ptbls
++    elan3_sdram_zeroq_sdram (dev, PTBL_TO_PTADDR(ptbl), ELAN3_L1_ENTRIES*ELAN3_PTP_SIZE);
++#endif
++
++    return (ptbl);
++}
++
++static ELAN3_PTBL *
++elan3mmu_alloc_l2ptbl (ELAN3_DEV *dev, int attr, ELAN3_PTBL *parent, ELAN3MMU *elan3mmu, E3_Addr base, spinlock_t **plock, unsigned long *flags)
++{
++    ELAN3_PTBL *ptbl = NULL;
++    ELAN3_PTBL *p;
++    int        i;
++    int        j;
++    unsigned long ptbl_flags;
++
++    spin_lock_irqsave (&dev->Level[PTBL_LEVEL_2].PtblLock, ptbl_flags);
++    if (dev->Level[PTBL_LEVEL_2].PtblFreeList)
++    {
++      ptbl = dev->Level[PTBL_LEVEL_2].PtblFreeList;
++
++      HAT_PRINTF1 (2, "elan3mmu_alloc_l2ptbl: found ptbl %p on free list\n", ptbl);
++
++      dev->Level[PTBL_LEVEL_2].PtblFreeList = ptbl->ptbl_next;
++      dev->Level[PTBL_LEVEL_2].PtblFreeCount--;
++    }
++    spin_unlock_irqrestore (&dev->Level[PTBL_LEVEL_2].PtblLock, ptbl_flags);
++    
++    if (ptbl == NULL) 
++    {
++      ptbl = elan3mmu_create_ptbls (dev, PTBL_LEVEL_2, attr, 1);
++
++      HAT_PRINTF1 (2, "elan3mmu_alloc_l2ptbl: created level 2 ptbl %p\n", ptbl);
++    }
++
++    if (ptbl == NULL)
++    {
++      if ((attr & PTE_NO_STEAL))
++      {
++          HAT_PRINTF0 (2, "elan3mmu_alloc_l2ptbl: not allowted to steal ptbl for use at level 2\n");
++          return (NULL);
++      }
++
++      ELAN3MMU_STAT(l2_alloc_l3);
++
++      ptbl = elan3mmu_steal_l3ptbl (dev, attr);
++      
++      HAT_PRINTF1 (2, "elan3mmu_alloc_l2ptbl: stolen level3 ptbl %p used as level 2\n", ptbl);
++    }
++    
++    *plock = elan3mmu_ptbl_to_lock (PTBL_LEVEL_2, ptbl);
++    spin_lock_irqsave (*plock, *flags);
++    
++    for (p = ptbl, j = i = 0; i < PTBLS_PER_PTBL_L2; i++, p++)
++    {
++      p->ptbl_elan3mmu = elan3mmu;
++      p->ptbl_base     = VA2BASE (base + j);
++      p->ptbl_flags    = PTBL_LEVEL_2 | PTBL_GROUPED;
++      p->ptbl_parent   = parent;
++      
++      j += L2_VA_PER_PTBL;
++    }
++    
++    ptbl->ptbl_flags  = PTBL_LEVEL_2 | PTBL_ALLOCED | PTBL_LOCKED;
++    
++    HAT_PRINTF3 (2, "elan3mmu_alloc_l2ptbl: ptbl %p dev %p base %x\n", ptbl, dev, base);
++
++#ifdef zero_all_ptbls
++    elan3_sdram_zero_sdarm (dev, PTBL_TO_PTADDR(ptbl), ELAN3_L2_ENTRIES*ELAN3_PTP_SIZE);
++#endif
++
++    return (ptbl);
++}
++
++static ELAN3_PTBL *
++elan3mmu_alloc_l3ptbl (ELAN3_DEV *dev, int attr, ELAN3_PTBL *parent, ELAN3MMU *elan3mmu, E3_Addr base, spinlock_t **plock, unsigned long *flags)
++{
++    ELAN3_PTBL *ptbl = NULL;
++    ELAN3_PTBL *p;
++    int              i;
++    int              j;
++    unsigned long ptbl_flags;
++
++    spin_lock_irqsave (&dev->Level[PTBL_LEVEL_3].PtblLock, ptbl_flags);
++    if (dev->Level[PTBL_LEVEL_3].PtblFreeList)
++    {
++      HAT_PRINTF1 (2, "elan3mmu_alloc_l3ptbl: found ptbl %p on free list\n", ptbl);
++
++      ptbl = dev->Level[PTBL_LEVEL_3].PtblFreeList;
++      dev->Level[PTBL_LEVEL_3].PtblFreeList = ptbl->ptbl_next;
++      dev->Level[PTBL_LEVEL_3].PtblFreeCount--;
++    }
++    spin_unlock_irqrestore (&dev->Level[PTBL_LEVEL_3].PtblLock, ptbl_flags);
++    
++    if (ptbl == NULL)
++    {
++      ptbl = elan3mmu_create_ptbls (dev, PTBL_LEVEL_3, attr, 1);
++
++      HAT_PRINTF1 (2, "elan3mmu_alloc_l3ptbl: created level 3 ptbl %p\n", ptbl);
++    }
++
++    if (ptbl == NULL)
++    {
++      if ((attr & PTE_NO_STEAL))
++      {
++          HAT_PRINTF0 (2, "elan3mmu_alloc_l3ptbl: not allowed to steal ptbl for use at level 3\n");
++          return (NULL);
++      }
++
++      ptbl = elan3mmu_steal_l3ptbl (dev, attr);
++
++      HAT_PRINTF1 (2, "elan3mmu_alloc_l3ptbl: stolen level3 ptbl %p\n", ptbl);
++    }
++    
++    *plock = elan3mmu_ptbl_to_lock (PTBL_LEVEL_3, ptbl);
++    spin_lock_irqsave (*plock,*flags);
++    
++    for (p = ptbl, j = i = 0; i < PTBLS_PER_PTBL_L3; i++, p++)
++    {
++      p->ptbl_elan3mmu = elan3mmu;
++      p->ptbl_base     = VA2BASE (base + j);
++      p->ptbl_flags    = PTBL_LEVEL_3 | PTBL_GROUPED;
++      p->ptbl_parent   = parent;
++      
++      j += L3_VA_PER_PTBL;
++    }
++    
++    ptbl->ptbl_flags = PTBL_LEVEL_3 | PTBL_ALLOCED | PTBL_LOCKED;
++    
++    HAT_PRINTF3 (2, "elan3mmu_alloc_l3ptbl: ptbl %p dev %p base %x\n", ptbl, dev, base);
++
++#ifdef zero_all_ptbls
++    elan3_sdram_zeroq_sdram (dev, PTBL_TO_PTADDR(ptbl), ELAN3_L3_ENTRIES*ELAN3_PTE_SIZE);
++#endif
++
++    return (ptbl);
++}
++
++void 
++elan3mmu_free_pte  (ELAN3_DEV *dev,  ELAN3MMU *elan3mmu,  ELAN3_PTBL *ptbl_ptr, int idx)
++{  
++    sdramaddr_t pte  = PTBL_TO_PTADDR (ptbl_ptr) | (idx * sizeof (ELAN3_PTE));
++    ELAN3_PTE    tpte = ELAN3_INVALID_PTE;
++    ELAN3_PTBL *prev;
++
++    /* ensure that the pte is invalid when free */
++    elan3_writepte (dev, pte, tpte);
++
++    /* lock whilst removing */
++    spin_lock (&elan3mmu->elan3mmu_lXptbl_lock);
++
++    HAT_PRINTF4 (2, "elan3mmu_free_pte idx %d   ptbl_ptr %p ptbl_base  %x  ptbl_ptr->ptbl_valid %d \n", 
++               idx, ptbl_ptr, ptbl_ptr->ptbl_base, ptbl_ptr->ptbl_valid);
++    /* make sure it was set */
++    ASSERT ( ptbl_ptr->ptbl_base & (1 << idx) ); 
++    ASSERT ( ptbl_ptr->ptbl_valid > 0  );
++
++    ptbl_ptr->ptbl_base &= ~(1 << idx);
++    ptbl_ptr->ptbl_valid--;
++
++    HAT_PRINTF3 (2, "elan3mmu_free_pte: dec valid for level %d ptbl %p to %d\n", 
++               PTBL_LEVEL(ptbl_ptr->ptbl_flags), ptbl_ptr, ptbl_ptr->ptbl_valid); 
++ 
++    /* was that the last one on this page */
++    if ( ! ptbl_ptr->ptbl_valid ) 
++    {
++      /* so no bits should be set then */
++      ASSERT ( ptbl_ptr->ptbl_base == 0 );
++
++      /* is this the first page ?? */
++      if ( elan3mmu->elan3mmu_lXptbl == ptbl_ptr ) 
++      {
++          /* make the list start at the second element */
++           elan3mmu->elan3mmu_lXptbl = ptbl_ptr->ptbl_parent;
++
++           /* put ptbl back on free list */
++           elan3mmu_free_lXptbl(dev, ptbl_ptr);
++
++           /* unlock and return */
++           spin_unlock (&elan3mmu->elan3mmu_lXptbl_lock);
++           return ;
++      }
++
++      /* scan thro list looking for this page */
++      prev = elan3mmu->elan3mmu_lXptbl;
++      while ( prev->ptbl_parent != NULL ) 
++      {
++          if ( prev->ptbl_parent == ptbl_ptr ) /* its the next one */
++          {
++              /* remove element from chain */
++              prev->ptbl_parent =  ptbl_ptr->ptbl_parent;
++
++              /* put ptbl back on free list */
++              elan3mmu_free_lXptbl(dev, ptbl_ptr);
++
++              /* unlock and return */
++              spin_unlock (&elan3mmu->elan3mmu_lXptbl_lock);
++              return ;
++          }           
++          prev = prev->ptbl_parent;
++      }
++      
++              panic ("elan3mmu_free_pte: failed to find ptbl in chain");
++      /* NOTREACHED */
++    }
++    
++    spin_unlock (&elan3mmu->elan3mmu_lXptbl_lock);
++}
++
++void
++elan3mmu_free_lXptbl (ELAN3_DEV *dev, ELAN3_PTBL *ptbl)
++{
++    ELAN3_PTBL_GR *ptg;
++
++    HAT_PRINTF2 (2, "elan3mmu_free_lXptbl: dev %p ptbl %p\n", dev, ptbl);
++
++    ASSERT (ptbl->ptbl_flags & PTBL_ALLOCED);
++    ASSERT ((ptbl->ptbl_flags & PTBL_KEEP) == 0);
++    ASSERT (PTBL_LEVEL(ptbl->ptbl_flags) == PTBL_LEVEL_X);
++    ASSERT (ptbl->ptbl_valid == 0);
++   
++    ptbl->ptbl_flags = 0;
++
++    ptg = PTBL_TO_GR(ptbl);
++
++    if (ptg->pg_level == PTBL_LEVEL_3)
++    {
++      ELAN3MMU_STAT(lX_freed_l3);
++
++      HAT_PRINTF1 (2, "elan3mmu_free_lXptbl: freeing stolen level 3 ptbl %p\n", ptbl);
++
++      /* this was really a level 3 ptbl which we had to steal */
++      spin_lock (&dev->Level[PTBL_LEVEL_3].PtblLock);
++      ptbl->ptbl_next = dev->Level[PTBL_LEVEL_3].PtblFreeList;
++      dev->Level[PTBL_LEVEL_3].PtblFreeList = ptbl;
++      dev->Level[PTBL_LEVEL_3].PtblFreeCount++;
++      spin_unlock (&dev->Level[PTBL_LEVEL_3].PtblLock);
++    }
++    else
++    {
++      spin_lock (&dev->Level[PTBL_LEVEL_X].PtblLock);
++      ptbl->ptbl_next = dev->Level[PTBL_LEVEL_X].PtblFreeList;
++      dev->Level[PTBL_LEVEL_X].PtblFreeList = ptbl;
++      dev->Level[PTBL_LEVEL_X].PtblFreeCount++;
++      spin_unlock (&dev->Level[PTBL_LEVEL_X].PtblLock);
++    }
++}
++
++void
++elan3mmu_free_l1ptbl (ELAN3_DEV *dev, ELAN3_PTBL *ptbl, spinlock_t *lock, unsigned long flags)
++{
++    HAT_PRINTF3 (2, "elan3mmu_free_l1ptbl: dev %p ptbl %p ptbl->ptbl_valid %x \n", dev, ptbl, ptbl->ptbl_valid);
++
++    ASSERT (ptbl->ptbl_flags & PTBL_ALLOCED);
++    ASSERT ((ptbl->ptbl_flags & PTBL_KEEP) == 0);
++    ASSERT (PTBL_LEVEL(ptbl->ptbl_flags) == PTBL_LEVEL_1);
++    ASSERT (ptbl->ptbl_valid == 0);
++    
++    HAT_PRINTF2 (2, "elan3mmu_free_l1ptbl: dev %p ptbl %p\n", dev, ptbl);
++
++    ptbl->ptbl_flags = 0;
++    spin_unlock (lock);
++    
++    spin_lock (&dev->Level[PTBL_LEVEL_1].PtblLock);
++    ptbl->ptbl_next = dev->Level[PTBL_LEVEL_1].PtblFreeList;
++    dev->Level[PTBL_LEVEL_1].PtblFreeList = ptbl;
++    dev->Level[PTBL_LEVEL_1].PtblFreeCount++;
++    spin_unlock (&dev->Level[PTBL_LEVEL_1].PtblLock);
++
++    local_irq_restore (flags);
++}
++
++void
++elan3mmu_free_l2ptbl (ELAN3_DEV *dev, ELAN3_PTBL *ptbl, spinlock_t *lock, unsigned long flags)
++{
++    ELAN3_PTBL_GR *ptg;
++
++    HAT_PRINTF2 (2, "elan3mmu_free_l2ptbl: dev %p ptbl %p\n", dev, ptbl);
++
++    ASSERT (PTBL_IS_LOCKED(ptbl->ptbl_flags));
++    ASSERT (ptbl->ptbl_flags & PTBL_ALLOCED);
++    ASSERT ((ptbl->ptbl_flags & PTBL_KEEP) == 0);
++    ASSERT (PTBL_LEVEL(ptbl->ptbl_flags) == PTBL_LEVEL_2);
++    ASSERT (ptbl->ptbl_valid == 0);
++   
++    ptbl->ptbl_flags = 0;
++    spin_unlock (lock);
++
++    ptg = PTBL_TO_GR(ptbl);
++
++    if (ptg->pg_level == PTBL_LEVEL_3)
++    {
++      ELAN3MMU_STAT(l2_freed_l3);
++
++      HAT_PRINTF1 (2, "elan3mmu_free_l2ptbl: freeing stolen level 3 ptbl %p\n", ptbl);
++
++      /* this was really a level 3 ptbl which we had to steal */
++      spin_lock (&dev->Level[PTBL_LEVEL_3].PtblLock);
++      ptbl->ptbl_next = dev->Level[PTBL_LEVEL_3].PtblFreeList;
++      dev->Level[PTBL_LEVEL_3].PtblFreeList = ptbl;
++      dev->Level[PTBL_LEVEL_3].PtblFreeCount++;
++      spin_unlock (&dev->Level[PTBL_LEVEL_3].PtblLock);
++    }
++    else
++    {
++      spin_lock (&dev->Level[PTBL_LEVEL_2].PtblLock);
++      ptbl->ptbl_next = dev->Level[PTBL_LEVEL_2].PtblFreeList;
++      dev->Level[PTBL_LEVEL_2].PtblFreeList = ptbl;
++      dev->Level[PTBL_LEVEL_2].PtblFreeCount++;
++      spin_unlock (&dev->Level[PTBL_LEVEL_2].PtblLock);
++    }  
++    local_irq_restore (flags);
++}
++
++void
++elan3mmu_free_l3ptbl (ELAN3_DEV *dev, ELAN3_PTBL *ptbl, spinlock_t *lock, unsigned long flags)
++{
++    ASSERT (PTBL_IS_LOCKED(ptbl->ptbl_flags));
++    ASSERT (ptbl->ptbl_flags & PTBL_ALLOCED);
++    ASSERT ((ptbl->ptbl_flags & PTBL_KEEP) == 0);
++    ASSERT (PTBL_LEVEL(ptbl->ptbl_flags) == PTBL_LEVEL_3);
++    ASSERT (ptbl->ptbl_valid == 0);
++    
++    HAT_PRINTF2 (2, "elan3mmu_free_l3ptbl: dev %p ptbl %p\n", dev, ptbl);
++
++    if (ptbl->ptbl_flags & PTBL_KERNEL)                               /* if the ptbl has been used by the kernel */
++    {                                                         /* then zero all the pte's, since they will */
++      elan3_sdram_zeroq_sdram (dev, PTBL_TO_PTADDR(ptbl), ELAN3_L3_ENTRIES*ELAN3_PTE_SIZE);
++    }
++
++    ptbl->ptbl_flags = 0;
++    spin_unlock (lock);
++    
++    spin_lock (&dev->Level[PTBL_LEVEL_3].PtblLock);
++    ptbl->ptbl_next = dev->Level[PTBL_LEVEL_3].PtblFreeList;
++    dev->Level[PTBL_LEVEL_3].PtblFreeList = ptbl;
++    dev->Level[PTBL_LEVEL_3].PtblFreeCount++;
++    spin_unlock (&dev->Level[PTBL_LEVEL_3].PtblLock);
++
++    local_irq_restore (flags);
++}
++
++void
++elan3mmu_kernel_l3ptbl (ELAN3_PTBL *ptbl)
++{
++    ELAN3_DEV   *dev  = ptbl->ptbl_elan3mmu->elan3mmu_dev;
++    sdramaddr_t pte  = PTBL_TO_PTADDR(ptbl);
++    ELAN3_PTE    tpte = elan3mmu_kernel_invalid_pte(ptbl->ptbl_elan3mmu);
++    int               i;
++
++    ptbl->ptbl_flags |= PTBL_KERNEL;
++    for (i = 0; i < ELAN3_L3_ENTRIES; i++, pte += ELAN3_PTE_SIZE)
++    {
++      elan3_writepte (dev, pte, tpte);
++    }
++}
++      
++#define PTBL_CAN_STEAL(flag)  (((flag) & (PTBL_KERNEL|PTBL_KEEP)) == 0 && (((flag) & PTBL_ALLOCED) && PTBL_LEVEL(flag) == PTBL_LEVEL_3))
++#define PTBL_MAY_STEAL(flag)  (((flag) & (PTBL_KERNEL|PTBL_KEEP|PTBL_LOCKED)) == 0 && (((flag) & PTBL_ALLOCED) && PTBL_LEVEL(flag) == PTBL_LEVEL_3))
++
++static int
++elan3mmu_steal_this_ptbl (ELAN3_DEV *dev, ELAN3_PTBL *l3ptbl)
++{
++    ELAN3_PTBL  *l2ptbl     = l3ptbl->ptbl_parent;
++    E3_Addr     l2addr     = BASE2VA(l2ptbl);
++    E3_Addr     l3addr     = BASE2VA(l3ptbl);
++    ELAN3_PTP    invalidptp = ELAN3_INVALID_PTP;
++    sdramaddr_t l2ptp;
++    spinlock_t *l2lock;
++    unsigned long l2flags;
++
++    HAT_PRINTF5 (1, "elan3mmu_steal_this_ptbl: l3ptbl %p (%x) l2ptbl %p (%x) l2addr %x\n",
++               l3ptbl, l3ptbl->ptbl_flags, l2ptbl, l2ptbl->ptbl_flags, l2addr);
++
++    if (PTBL_CAN_STEAL (l3ptbl->ptbl_flags) &&
++      elan3mmu_lock_ptbl (l2ptbl, LK_PTBL_NOWAIT, l3ptbl->ptbl_elan3mmu, l2addr, PTBL_LEVEL_2, &l2lock, &l2flags) == LK_PTBL_OK)
++    {
++      ELAN3MMU_STAT(stolen_ptbls);
++
++      /* Locked both L3 and L2 page tables. */
++      l2ptp = PTBL_TO_PTADDR (l2ptbl) + ELAN3_L2_INDEX(l3addr)*ELAN3_PTP_SIZE;
++      
++      /* detach the level 3 page table */
++      elan3_writeptp (dev, l2ptp, invalidptp);
++      ElanFlushTlb (dev);
++
++      l2ptbl->ptbl_valid--;
++
++      HAT_PRINTF3 (2, "elan3mmu_steal_this_ptbl: dec valid for level %d ptbl %p to %d\n", PTBL_LEVEL(l2ptbl->ptbl_flags), l2ptbl, l2ptbl->ptbl_valid); 
++
++      elan3mmu_unlock_ptbl (l2ptbl, l2lock, l2flags);
++
++      elan3mmu_unload_loop (l3ptbl->ptbl_elan3mmu, l3ptbl, 0, ELAN3_L3_ENTRIES, PTE_UNLOAD_NOFLUSH);
++
++      ASSERT (l3ptbl->ptbl_valid == 0);
++
++      l3ptbl->ptbl_flags = 0;
++      return (1);
++    }
++    return (0);
++}
++
++static ELAN3_PTBL *
++elan3mmu_steal_l3ptbl (ELAN3_DEV *dev, int attr)
++{
++    ELAN3_PTBL_GR     *ptg;
++    ELAN3_PTBL                *ptbl;
++    spinlock_t                *lock;
++    unsigned long        group_flags;
++    unsigned long        ptbl_flags;
++    register int       i;
++
++    HAT_PRINTF1 (2, "elan3mmu_steal_l3ptbl: attr %x\n", attr);
++
++    spin_lock_irqsave (&dev->PtblGroupLock, group_flags);
++
++    ptg = dev->Level3PtblGroupHand;
++
++    if (ptg == NULL)
++      ptg = dev->Level[PTBL_LEVEL_3].PtblGroupList;
++    
++    for (;;)
++    {
++      while (ptg)
++      {
++          for (i = 0, ptbl = ptg->pg_ptbls; i < PTBLS_PER_GROUP_MAX; i++, ptbl++)
++          {
++              if (PTBL_MAY_STEAL (ptbl->ptbl_flags) &&
++                  elan3mmu_lock_this_ptbl (ptbl, LK_PTBL_NOWAIT, &lock, &ptbl_flags) == LK_PTBL_OK)
++              {
++                  if (elan3mmu_steal_this_ptbl (dev, ptbl ))
++                  {
++                      HAT_PRINTF1 (2, "elan3mmu_steal_l3ptbl: stolen ptbl %p\n", ptbl);
++
++                      elan3mmu_unlock_ptbl (ptbl, lock,ptbl_flags);
++
++                      dev->Level3PtblGroupHand = ptg->pg_next;
++
++                      spin_unlock_irqrestore (&dev->PtblGroupLock, group_flags);
++
++                      return (ptbl);
++                  }
++                  elan3mmu_unlock_ptbl (ptbl, lock, ptbl_flags);
++              }
++          }
++          ptg = ptg->pg_next;
++      }
++      
++      if (dev->Level[PTBL_LEVEL_3].PtblFreeList)
++      {
++          spin_lock (&dev->Level[PTBL_LEVEL_3].PtblLock);
++          ptbl = dev->Level[PTBL_LEVEL_3].PtblFreeList;
++          if (ptbl != NULL)
++          {
++              dev->Level[PTBL_LEVEL_3].PtblFreeList = ptbl->ptbl_next;
++              dev->Level[PTBL_LEVEL_3].PtblFreeCount--;
++          }
++          spin_unlock (&dev->Level[PTBL_LEVEL_3].PtblLock);
++
++          if (ptbl != NULL)
++          {
++              HAT_PRINTF1 (2, "elan3mmu_steal_l3ptbl: found ptbl %p on free list\n", ptbl);
++              break;
++          }
++      }
++
++      ptbl = elan3mmu_create_ptbls (dev, PTBL_LEVEL_3, attr, 1);
++
++      if (ptbl != NULL)
++      {
++          HAT_PRINTF1 (2, "elan3mmu_steal_l3ptbl: created new ptbl %p\n", ptbl);
++          break;
++      }
++      
++      HAT_PRINTF0 (1, "elan3mmu_steal_l3ptbl: cannot find a ptbl, retrying\n");
++      ptg = dev->Level[PTBL_LEVEL_3].PtblGroupList;
++    }
++
++    spin_unlock (&dev->PtblGroupLock);
++    return (ptbl);
++}
++
++sdramaddr_t
++elan3mmu_ptefind (ELAN3MMU *elan3mmu, E3_Addr addr, int *level, 
++                ELAN3_PTBL **pptbl, spinlock_t **plock, unsigned long *flags)
++{
++    ELAN3_DEV   *dev = elan3mmu->elan3mmu_dev;
++    ELAN3_PTBL  *l1ptbl;
++    sdramaddr_t l1ptp;
++    ELAN3_PTP    tl1ptp;
++    E3_Addr     l1base;
++    ELAN3_PTBL  *l2ptbl;
++    sdramaddr_t l2ptp;
++    ELAN3_PTP    tl2ptp;
++    E3_Addr     l2base;
++    ELAN3_PTBL  *l3ptbl;
++    sdramaddr_t l3pte;
++    spinlock_t *l1lock;
++    spinlock_t *l2lock;
++    spinlock_t *l3lock;
++    unsigned long l1flags;
++    unsigned long l2flags;
++    unsigned long l3flags;
++
++    HAT_PRINTF2 (2, "elan3mmu_ptefind: elan3mmu %p addr %x\n", elan3mmu, addr);
++
++    l1ptbl = elan3mmu->elan3mmu_l1ptbl;
++    *level = 0;
++
++    if (l1ptbl == NULL)
++      return ((sdramaddr_t) NULL);
++
++    l1ptp  = PTBL_TO_PTADDR(l1ptbl) + ELAN3_L1_INDEX(addr)*ELAN3_PTP_SIZE;
++    l1base = ELAN3_L1_BASE(addr);
++    
++retryl1:
++    tl1ptp = elan3_readptp (dev, l1ptp);
++    
++    HAT_PRINTF4 (2, "elan3mmu_ptefind: l1ptbl %p l1ptp %lx l1base %x : tl1ptp %x\n", l1ptbl, l1ptp, l1base, tl1ptp);
++
++    switch (ELAN3_PTP_TYPE(tl1ptp))
++    {
++    case ELAN3_ET_PTE:
++      elan3mmu_lock_ptbl (l1ptbl, LK_PTBL_FAILOK, elan3mmu, addr, PTBL_LEVEL_1, &l1lock, &l1flags);
++
++      tl1ptp = elan3_readptp (dev, l1ptp);
++      if (ELAN3_PTP_TYPE(tl1ptp) != ELAN3_ET_PTE)
++      {
++          elan3mmu_unlock_ptbl (l1ptbl, l1lock, l1flags);
++          goto retryl1;
++      }
++      
++      *level = 1;
++      *pptbl = l1ptbl;
++      *plock = l1lock;
++      *flags = l1flags;
++      
++      /* return with l1lock */
++      return (l1ptp);  
++
++    case ELAN3_ET_INVALID:
++      return ((sdramaddr_t) 0);
++      
++    case ELAN3_ET_PTP:
++      break;
++
++    default:
++      panic ("elan3mmu_ptefind: found bad entry in level 1 page table");
++      /* NOTREACHED */
++    }
++    
++    HAT_PRINTF1 (2, "elan3mmu_ptefind: chain to level 2 ptbl from ptp %x\n", tl1ptp);
++
++    l2ptbl = elan3mmu_ta_to_ptbl (elan3mmu, &tl1ptp);
++    l2ptp  = PTBL_TO_PTADDR(l2ptbl) + ELAN3_L2_INDEX(addr)*ELAN3_PTP_SIZE;
++    l2base = ELAN3_L2_BASE(addr);
++    
++    tl2ptp = elan3_readptp (dev, l2ptp);
++    
++    HAT_PRINTF4 (2, "elan3mmu_ptefind: l2ptbl %p l2ptp %lx l2base %x : tl2ptp %x\n", l2ptbl, l2ptp, l2base, tl2ptp);
++
++    switch (ELAN3_PTP_TYPE(tl2ptp))
++    {
++    case ELAN3_ET_PTE:
++      switch (elan3mmu_lock_ptbl (l2ptbl, LK_PTBL_FAILOK, elan3mmu, addr, PTBL_LEVEL_2, &l2lock, &l2flags))
++      {
++      case LK_PTBL_OK:
++          tl2ptp = elan3_readptp (dev, l2ptp);
++          if (ELAN3_PTP_TYPE(tl2ptp) != ELAN3_ET_PTE)
++          {
++              elan3mmu_unlock_ptbl (l2ptbl, l2lock, l2flags);
++              goto retryl1;
++          }
++          
++          *level = 2;
++          *pptbl = l2ptbl;
++          *plock = l2lock;
++          *flags = l2flags;
++          
++          /* return with l2lock */
++          return (l2ptp); 
++          
++      case LK_PTBL_MISMATCH:
++          HAT_PRINTF6 (2, "elan3mmu_ptefind: PTBL_MISMATCH : ptbl %p flags %x elan3mmu %p base %x (%p %x)\n",
++                       l2ptbl, l2ptbl->ptbl_flags, l2ptbl->ptbl_elan3mmu, l2ptbl->ptbl_base, elan3mmu, addr);
++          
++          /*
++           * We've trogged down to this ptbl,  but someone has just
++           * stolen it,  so try all over again.
++           */
++          goto retryl1;
++          
++      default:
++          panic ("elan3mmu_ptefind: elan3mmu_lock_ptbl returned bad value");
++          /* NOTREACHED */
++      }
++    case ELAN3_ET_INVALID:
++      return ((sdramaddr_t) 0);
++      
++    case ELAN3_ET_PTP:
++      break;
++    default:
++      panic ("elan3mmu_ptefind: found bad entry in level 2 page table");
++      /* NOTREACHED */
++    }
++    
++    HAT_PRINTF1 (2, "elan3mmu_ptefind: chain to level 3 page table from ptp %x\n", tl2ptp);
++
++    l3ptbl = elan3mmu_ta_to_ptbl (elan3mmu, &tl2ptp);
++    l3pte  = PTBL_TO_PTADDR(l3ptbl) + ELAN3_L3_INDEX(addr)*ELAN3_PTE_SIZE;
++    
++    HAT_PRINTF2 (2, "elan3mmu_ptefind: l3ptbl %p l3pte %lx\n", l3ptbl, l3pte);
++               
++    switch (elan3mmu_lock_ptbl (l3ptbl, LK_PTBL_FAILOK, elan3mmu, addr, PTBL_LEVEL_3, &l3lock, &l3flags))
++    {
++    case LK_PTBL_OK:
++      *level = 3;
++      *plock = l3lock;
++      *pptbl = l3ptbl;
++      *flags = l3flags;
++
++      return (l3pte);
++      
++    case LK_PTBL_FAILED:
++      panic ("elan3mmu_ptefind: l3 lock failed");
++      /* NOTREACHED */
++
++    case LK_PTBL_MISMATCH:
++      HAT_PRINTF6 (2, "elan3mmu_ptefind: PTBL_MISMATCH : ptbl %p flags %x elan3mmu %p base %x (%p %x)\n",
++                   l3ptbl, l3ptbl->ptbl_flags, l3ptbl->ptbl_elan3mmu, l3ptbl->ptbl_base, elan3mmu, addr);
++                   
++      /*
++       * We've trogged down to this ptbl,  but someone has just
++       * stolen it,  so try all over again.
++       */
++      goto retryl1;
++      
++    default:
++      panic ("elan3mmu_ptefind: elan3mmu_lock_ptbl returned bad value");
++      /* NOTREACHED */
++    }
++    /* NOTREACHED */
++    return ((sdramaddr_t) 0);
++}
++
++sdramaddr_t 
++elan3mmu_ptp2pte (ELAN3MMU *elan3mmu, sdramaddr_t ptp, int level)
++{
++    ELAN3_PTP tptp = elan3_readptp (elan3mmu->elan3mmu_dev, ptp);
++
++    ASSERT (level != 3 && ELAN3_PTP_TYPE(tptp) == ELAN3_ET_PTE);
++
++    return PTP_TO_PT_PADDR(tptp);
++}
++
++sdramaddr_t
++elan3mmu_ptealloc (ELAN3MMU *elan3mmu, E3_Addr addr, int level, 
++                 ELAN3_PTBL **pptbl, spinlock_t **plock, int attr, unsigned long *flags)
++{
++    ELAN3_DEV   *dev     = elan3mmu->elan3mmu_dev;
++    ELAN3_PTBL  *l1ptbl;
++    ELAN3_PTBL  *lXptbl;
++    int         idx;
++    sdramaddr_t l1ptp;
++    ELAN3_PTP    tl1ptp;
++    E3_Addr     l1base;
++    spinlock_t *l1lock;
++    ELAN3_PTBL  *l2ptbl;
++    sdramaddr_t l2ptp;
++    ELAN3_PTP    tl2ptp;
++    E3_Addr     l2base;
++    spinlock_t *l2lock;
++    ELAN3_PTBL  *l3ptbl;
++    sdramaddr_t l3pte;
++    E3_Addr     l3base;
++    spinlock_t *l3lock;
++
++    unsigned long l1flags;
++    unsigned long l2flags;
++    unsigned long l3flags;
++
++    HAT_PRINTF2 (2, "elan3mmu_ptealloc: elan3mmu %p addr %x\n", elan3mmu, addr);
++
++    l1ptbl = elan3mmu->elan3mmu_l1ptbl;
++    if (l1ptbl == NULL)
++      return ((sdramaddr_t) 0);
++
++    l1ptp  = PTBL_TO_PTADDR(l1ptbl) + ELAN3_L1_INDEX(addr)*ELAN3_PTP_SIZE;
++    l1base = ELAN3_L1_BASE(addr);
++              
++retryl1:
++    tl1ptp = elan3_readptp (dev, l1ptp);
++
++    HAT_PRINTF5 (2, "elan3mmu_ptealloc: l1ptbl %p 1ptp %lx l1base %x (%x) : tl1ptp %x\n",
++               l1ptbl, l1ptp, l1base, l1ptbl->ptbl_base, tl1ptp);
++
++    switch (ELAN3_PTP_TYPE(tl1ptp))
++    {
++    case ELAN3_ET_PTE:
++      if (level == PTBL_LEVEL_1)
++      {
++          elan3mmu_lock_ptbl (l1ptbl, 0, elan3mmu, addr, PTBL_LEVEL_1, &l1lock, &l1flags);
++
++          tl1ptp = elan3_readptp (dev, l1ptp);
++          if (ELAN3_PTP_TYPE(tl1ptp) != ELAN3_ET_PTE)
++          {
++              elan3mmu_unlock_ptbl (l1ptbl, l1lock, l1flags);
++              goto retryl1;
++          }
++          
++          *pptbl = l1ptbl;
++          *plock = l1lock;
++          *flags = l1flags;
++
++          /* return holding l1lock */
++          return (l1ptp);
++      }
++      panic ("elan3mmu_ptealloc: found pte in level 1 page table");
++      /* NOTREACHED */
++
++    case ELAN3_ET_PTP:
++      if (level == PTBL_LEVEL_1)
++          panic ("elan3mmu_ptealloc: found PTP when loading a level 1 PTE\n");
++      break;
++
++    case ELAN3_ET_INVALID:
++      if (level == PTBL_LEVEL_1)
++      {
++          if ((lXptbl = elan3mmu_alloc_pte (dev, elan3mmu,  &idx)) == NULL)
++              return ((sdramaddr_t) 0);
++
++          elan3mmu_lock_ptbl (l1ptbl, 0, elan3mmu, addr, PTBL_LEVEL_1, &l1lock, &l1flags);
++
++          tl1ptp = elan3_readptp (dev, l1ptp);
++          if (ELAN3_PTP_TYPE(tl1ptp) != ELAN3_ET_INVALID)
++          {
++              /* raced with someone else, whose got there first */
++              elan3mmu_free_pte (dev, elan3mmu, lXptbl, idx);
++
++              /* drop the l1lock and retry */
++              elan3mmu_unlock_ptbl (l1ptbl, l1lock, l1flags);
++              goto retryl1;
++          }
++          
++          tl1ptp = PTBL_TO_PTADDR(lXptbl) | (idx * ELAN3_PTE_SIZE) | ELAN3_ET_PTE;
++          
++          elan3_writeptp (dev, l1ptp, tl1ptp);
++
++          *pptbl = l1ptbl;
++          *plock = l1lock;
++          *flags = l1flags;
++
++          /* return holding l1lock */
++          return (l1ptp);
++      }
++
++      if (level == PTBL_LEVEL_2)
++      {
++          if ((lXptbl = elan3mmu_alloc_pte (dev, elan3mmu, &idx)) == NULL)
++              return ((sdramaddr_t) 0);
++
++          if ((l2ptbl = elan3mmu_alloc_l2ptbl (dev, attr, l1ptbl, elan3mmu, ELAN3_L2_BASE(addr), &l2lock, &l2flags)) == NULL)
++          {
++              elan3mmu_free_pte (dev, elan3mmu, lXptbl, idx); 
++              return ((sdramaddr_t) 0);
++          }
++
++          /* Connect l2ptbl to the new LX pte */
++          l2ptp  = PTBL_TO_PTADDR(l2ptbl) + ELAN3_L2_INDEX(addr) * ELAN3_PTP_SIZE;
++          tl2ptp = PTBL_TO_PTADDR(lXptbl) | (idx * ELAN3_PTE_SIZE) | ELAN3_ET_PTE;
++
++          elan3_writeptp (dev, l2ptp, tl2ptp);
++
++          /* Now need to lock the l1 ptbl */
++          elan3mmu_unlock_ptbl (l2ptbl, l2lock, l2flags);
++
++          elan3mmu_lock_ptbl (l1ptbl, 0, elan3mmu, addr, PTBL_LEVEL_1, &l1lock, &l1flags);
++          elan3mmu_lock_ptbl (l2ptbl, 0, elan3mmu, addr, PTBL_LEVEL_2, &l2lock, &l2flags);
++
++          tl1ptp = elan3_readptp (dev, l1ptp);
++          if (ELAN3_PTP_TYPE(tl1ptp) != ELAN3_ET_INVALID)
++          {
++              HAT_PRINTF0 (2, "elan3mmu_ptealloc: beaten to it,  free l2 ptbl/lx pte\n");
++              
++              tl2ptp = ELAN3_INVALID_PTP;
++              elan3_writeptp (dev, l2ptp, tl2ptp);
++              
++              HAT_PRINTF2 (2, "elan3mmu_ptealloc: write level 2 ptp %lx to %x\n", l2ptp, tl2ptp);
++              HAT_PRINTF2 (2, "elan3mmu_ptealloc: freeing l2 ptbl %p (%x)\n", l2ptbl, l2ptbl->ptbl_flags);
++              
++              elan3mmu_free_l2ptbl (dev, l2ptbl, l2lock, l2flags);
++              elan3mmu_free_pte (dev, elan3mmu, lXptbl, idx);
++
++              elan3mmu_unlock_ptbl (l1ptbl, l1lock, l1flags);
++
++              goto retryl1;
++          }
++          
++          /* Now have L1 locked,  so install the L2 ptbl */
++          l1ptp  = PTBL_TO_PTADDR(l1ptbl) + ELAN3_L1_INDEX(addr)*ELAN3_PTP_SIZE;
++          tl1ptp = PTBL_TO_PTADDR(l2ptbl) | ELAN3_ET_PTP;
++          l1ptbl->ptbl_valid++;
++
++          HAT_PRINTF3 (2, "elan3mmu_ptealloc: inc valid for level %d ptbl %p to %d\n", 
++                       PTBL_LEVEL(l1ptbl->ptbl_flags), l1ptbl, l1ptbl->ptbl_valid);
++          
++          elan3_writeptp (dev, l1ptp, tl1ptp);
++          
++          HAT_PRINTF2 (2, "elan3mmu_ptealloc: write l1ptp %lx to %x\n", l1ptp, tl1ptp);
++
++          /* unordered unlock - lock l1ptbl, lock l2ptbl, unlock l1ptbl */
++          elan3mmu_unlock_ptbl (l1ptbl, l1lock, l2flags); /* need to unlock with the l2flags to keep irq order correct */
++
++          *pptbl = l2ptbl;
++          *plock = l2lock;
++          *flags = l1flags; /* return the l1flags here as we have released the l2flags already to keep order */
++
++          /* return holding l2lock */
++          return (l2ptp);
++      }
++
++      HAT_PRINTF0 (2, "elan3mmu_ptealloc: allocating level 2 and level 3 page tables\n");
++
++      /* Allocate a level 2 and level 3 page table and link them together */
++      if ((l2ptbl = elan3mmu_alloc_l2ptbl (dev, attr, l1ptbl, elan3mmu, ELAN3_L2_BASE(addr), &l2lock, &l2flags)) == NULL)
++          return ((sdramaddr_t) 0);
++
++      if ((l3ptbl = elan3mmu_alloc_l3ptbl (dev, attr | PTE_NO_SLEEP, l2ptbl, elan3mmu, ELAN3_L3_BASE(addr), &l3lock, &l3flags)) == NULL)
++      {
++          elan3mmu_unlock_ptbl (l2ptbl, l2lock, l2flags);
++          return ((sdramaddr_t) 0);
++      }
++
++      ASSERT (PTBL_IS_LOCKED (l2ptbl->ptbl_flags));
++      ASSERT (PTBL_LEVEL (l2ptbl->ptbl_flags) == PTBL_LEVEL_2);
++      ASSERT (PTBL_IS_LOCKED (l3ptbl->ptbl_flags));
++      ASSERT (PTBL_LEVEL (l3ptbl->ptbl_flags) == PTBL_LEVEL_3);
++
++      HAT_PRINTF6 (2, "elan3mmu_ptealloc: l2ptbl %p (%x,%x) l3ptbl %p (%x,%x)\n",
++                   l2ptbl, l2ptbl->ptbl_flags, l2ptbl->ptbl_base,
++                   l3ptbl, l3ptbl->ptbl_flags, l3ptbl->ptbl_base);
++
++      if (CTXT_IS_KERNEL (elan3mmu->elan3mmu_ctxt))
++      {
++          l2ptbl->ptbl_flags |= PTBL_KERNEL;
++          elan3mmu_kernel_l3ptbl (l3ptbl);
++      }
++      
++      /*
++       * Connect L3 ptbl to the new L2 ptbl.
++       */
++      l2ptp  = PTBL_TO_PTADDR(l2ptbl) + ELAN3_L2_INDEX(addr) * ELAN3_PTP_SIZE;
++      tl2ptp = PTBL_TO_PTADDR(l3ptbl) | ELAN3_ET_PTP;
++
++      l2ptbl->ptbl_valid = 1;
++
++      HAT_PRINTF3 (2, "elan3mmu_ptealloc: set valid for level %d ptbl %p to %d\n", 
++                   PTBL_LEVEL(l2ptbl->ptbl_flags), l2ptbl, l2ptbl->ptbl_valid);
++
++      HAT_PRINTF2 (2, "elan3mmu_ptealloc: write level 2 ptp %lx to %x\n", l2ptp, tl2ptp);
++
++      elan3_writeptp (dev, l2ptp, tl2ptp);
++
++      /* 
++       * Now need to lock the l1 ptbl - to maintain lock ordering
++       * we set the PTBL_KEEP bit to stop the l3 ptbl from being 
++       * stolen and drop the locks in the order we aquired them
++       */
++      l3ptbl->ptbl_flags |= PTBL_KEEP;
++
++      elan3mmu_unlock_ptbl (l3ptbl, l3lock, l3flags);
++      elan3mmu_unlock_ptbl (l2ptbl, l2lock, l2flags);
++
++      elan3mmu_lock_ptbl (l1ptbl, 0, elan3mmu, addr, PTBL_LEVEL_1, &l1lock, &l1flags);
++      elan3mmu_lock_ptbl (l3ptbl, 0, elan3mmu, addr, PTBL_LEVEL_3, &l3lock, &l3flags);
++          
++      l3ptbl->ptbl_flags &= ~PTBL_KEEP;
++         
++      /* Now have l1 and l3 ptbls locked,  so install the new l2 ptbl into the l1. */
++      tl1ptp = elan3_readptp (dev, l1ptp);
++
++      HAT_PRINTF2 (2, "elan3mmu_ptealloc: l1ptp %lx is %x\n", l1ptp, tl1ptp);
++
++      if (ELAN3_PTP_TYPE(tl1ptp) != ELAN3_ET_INVALID)
++      {
++          HAT_PRINTF0 (2, "elan3mmu_ptealloc: beaten to it,  free l2/l3 ptbls\n");
++
++          /* free off the level 3 page table */
++          HAT_PRINTF2 (2, "elan3mmu_ptealloc: freeing l3 ptbl %p (%x)\n", l3ptbl, l3ptbl->ptbl_flags);
++
++          l3ptbl->ptbl_flags &= ~PTBL_KEEP;
++          elan3mmu_free_l3ptbl (dev, l3ptbl, l3lock, l3flags);
++
++          /* and unlock the level 1 ptbl */
++          elan3mmu_unlock_ptbl (l1ptbl, l1lock, l1flags);
++          
++          /* lock the level 2 page table, and clear out the PTP, then free it */
++          (void) elan3mmu_lock_ptbl (l2ptbl, 0, elan3mmu, addr, PTBL_LEVEL_2, &l2lock, &l2flags);
++
++          HAT_PRINTF2 (2, "elan3mmu_ptealloc: locked l2 ptbl %p (%x)\n", l2ptbl, l2ptbl->ptbl_flags);
++          
++          tl2ptp = ELAN3_INVALID_PTP;
++          elan3_writeptp (dev, l2ptp, tl2ptp);
++          l2ptbl->ptbl_valid = 0;
++
++          HAT_PRINTF3 (2, "elan3mmu_ptealloc: set to 0 valid for level %d ptbl %p to %d\n", PTBL_LEVEL(l2ptbl->ptbl_flags), l2ptbl, l2ptbl->ptbl_valid); 
++
++          HAT_PRINTF2 (2, "elan3mmu_ptealloc: write level 2 ptp %lx to %x\n", l2ptp, tl2ptp);
++          HAT_PRINTF2 (2, "elan3mmu_ptealloc: freeing l2 ptbl %p (%x)\n", l2ptbl, l2ptbl->ptbl_flags);
++
++          elan3mmu_free_l2ptbl (dev, l2ptbl, l2lock, l2flags);
++
++          goto retryl1;
++      }
++      
++      HAT_PRINTF4 (2, "elan3mmu_ptealloc: l1ptbl is %p (%x), l3ptbl is %p (%x)\n", 
++                   l1ptbl, l1ptbl->ptbl_flags, l3ptbl, l3ptbl->ptbl_flags);
++
++      /* Now have L1 and L3 locked,  so install the L2 ptbl */
++      l1ptp  = PTBL_TO_PTADDR(l1ptbl) + ELAN3_L1_INDEX(addr)*ELAN3_PTP_SIZE;
++      tl1ptp = PTBL_TO_PTADDR(l2ptbl) | ELAN3_ET_PTP;
++      l1ptbl->ptbl_valid++;
++
++      HAT_PRINTF3 (2, "elan3mmu_ptealloc: inc valid for level %d ptbl %p to %d\n", 
++                   PTBL_LEVEL(l1ptbl->ptbl_flags), l1ptbl, l1ptbl->ptbl_valid);
++
++      elan3_writeptp (dev, l1ptp, tl1ptp);
++
++      HAT_PRINTF2 (2, "elan3mmu_ptealloc: write l1ptp %lx to %x\n", l1ptp, tl1ptp);
++
++      /* unordered unlock - lock l1ptbl, lock l3ptbl, unlock l1ptbl */
++      elan3mmu_unlock_ptbl (l1ptbl, l1lock, l3flags); /* free using l3flags to keep irq ordering */
++
++      l3pte = PTBL_TO_PTADDR (l3ptbl) + ELAN3_L3_INDEX(addr)*ELAN3_PTE_SIZE;
++
++      /* Level 3 ptbl is already locked,  so just return the pte */
++      *pptbl = l3ptbl;
++      *plock = l3lock;
++      *flags = l1flags; /* return l1flags to keep irq ordering */
++
++      return (l3pte);
++
++    default:
++      panic ("elan3mmu_ptealloc: found bad entry in level 1 page table");
++      /* NOTREACHED */
++    }
++
++    HAT_PRINTF1 (2, "elan3mmu_ptealloc: chain to level 2 ptbl from ptp %x\n", tl1ptp);
++
++    l2ptbl = elan3mmu_ta_to_ptbl (elan3mmu, &tl1ptp);
++    l2ptp  = PTBL_TO_PTADDR(l2ptbl) + ELAN3_L2_INDEX(addr)*ELAN3_PTP_SIZE;
++    l2base = ELAN3_L2_BASE(addr);
++
++    tl2ptp = elan3_readptp (dev, l2ptp);
++
++    HAT_PRINTF5 (2, "elan3mmu_ptealloc: l2ptbl %p l2ptp %lx l2base %x (%x) : tl2ptp %x\n",
++               l2ptbl, l2ptp, l2base, l2ptbl->ptbl_base, tl2ptp);
++
++    switch (ELAN3_PTP_TYPE(tl2ptp))
++    {
++    case ELAN3_ET_PTE:
++      if (level == PTBL_LEVEL_2) {
++          /* this is a pointer to a pte,  we should just return it */
++
++          switch (elan3mmu_lock_ptbl (l2ptbl, 0, elan3mmu, addr, PTBL_LEVEL_2, &l2lock, &l2flags))
++          {
++          case LK_PTBL_OK:
++              break;
++      
++          case LK_PTBL_FAILED:
++              panic ("elan3mmu_ptealloc: l2 lock failed");
++              /* NOTREACHED */
++              
++          case LK_PTBL_MISMATCH:
++              HAT_PRINTF6 (2, "elan3mmu_ptealloc: PTBL_MISMATCH : ptbl %p flags %x elan3mmu %p base %x (%p %x)\n",
++                           l2ptbl, l2ptbl->ptbl_flags, l2ptbl->ptbl_elan3mmu, l2ptbl->ptbl_base, elan3mmu, addr);
++              
++              /*
++               * We've trogged down to this ptbl,  but someone has just
++               * stolen it,  so try all over again.
++               */
++              goto retryl1;
++              
++          default:
++              panic ("elan3mmu_ptealloc: elan3mmu_lock_ptbl returned bad value");
++              /* NOTREACHED */
++          }
++
++
++          tl2ptp = elan3_readptp (dev, l2ptp);
++          if (ELAN3_PTP_TYPE(tl2ptp) != ELAN3_ET_PTE)
++          {
++              elan3mmu_unlock_ptbl (l2ptbl, l2lock, l2flags);
++              goto retryl1;
++          }
++
++          *pptbl = l2ptbl;
++          *plock = l2lock;
++          *flags = l2flags;
++
++          /* return holdind l2lock */
++          return (l2ptp);
++      }
++      panic ("elan3mmu: found pte in level 2 page table");
++      /* NOTREACHED */
++
++    case ELAN3_ET_PTP:
++      break;
++
++    case ELAN3_ET_INVALID:
++      if (level == PTBL_LEVEL_2) 
++      {
++          if ((lXptbl = elan3mmu_alloc_pte (dev, elan3mmu, &idx)) == NULL)
++              return ((sdramaddr_t) 0);
++
++          switch (elan3mmu_lock_ptbl (l2ptbl, 0, elan3mmu, addr, PTBL_LEVEL_2, &l2lock, &l2flags))
++          {
++          case LK_PTBL_OK:
++              break;
++      
++          case LK_PTBL_FAILED:
++              panic ("elan3mmu_ptealloc: l2 lock failed");
++              /* NOTREACHED */
++              
++          case LK_PTBL_MISMATCH:
++              HAT_PRINTF6 (2, "elan3mmu_ptealloc: PTBL_MISMATCH : ptbl %p flags %x elan3mmu %p base %x (%p %x)\n",
++                           l2ptbl, l2ptbl->ptbl_flags, l2ptbl->ptbl_elan3mmu, l2ptbl->ptbl_base, elan3mmu, addr);
++              
++              /*
++               * We've trogged down to this ptbl,  but someone has just
++               * stolen it,  so try all over again.
++               */
++              goto retryl1;
++              
++          default:
++              panic ("elan3mmu_ptealloc: elan3mmu_lock_ptbl returned bad value");
++              /* NOTREACHED */
++          }
++
++          tl2ptp = elan3_readptp (dev, l2ptp);
++          if (ELAN3_PTP_TYPE(tl2ptp) != ELAN3_ET_INVALID)
++          {
++              HAT_PRINTF0 (2, "elan3mmu_ptealloc: beaten to it,  free lx pte\n");
++
++              elan3mmu_free_pte (dev, elan3mmu, lXptbl, idx);
++
++              elan3mmu_unlock_ptbl (l2ptbl, l2lock, l2flags);
++              goto retryl1;
++          }
++
++          /* Connect l2ptbl to the new LX pte */
++          tl2ptp = PTBL_TO_PTADDR(lXptbl) | (idx * ELAN3_PTE_SIZE) | ELAN3_ET_PTE;
++                 
++          HAT_PRINTF3 (2, "elan3mmu_ptealloc: inc valid for level %d ptbl %p to %d\n", 
++                       PTBL_LEVEL(l2ptbl->ptbl_flags), l2ptbl, l2ptbl->ptbl_valid);
++          
++          elan3_writeptp (dev, l2ptp, tl2ptp);
++          
++          HAT_PRINTF2 (2, "elan3mmu_ptealloc: write l2ptp %lx to %x\n", l2ptp, tl2ptp);
++
++          *pptbl = l2ptbl;
++          *plock = l2lock;
++          *flags = l2flags;
++
++          /* return holding l2lock */
++          return (l2ptp);
++      }
++      HAT_PRINTF0 (2, "elan3mmu_ptealloc: allocate level 3 page table\n");
++
++      if ((l3ptbl = elan3mmu_alloc_l3ptbl (dev, attr, l2ptbl, elan3mmu, ELAN3_L3_BASE(addr), &l3lock, &l3flags)) == NULL)
++          return ((sdramaddr_t) 0);
++
++      if (CTXT_IS_KERNEL (elan3mmu->elan3mmu_ctxt))
++          elan3mmu_kernel_l3ptbl (l3ptbl);
++
++      /* 
++       * Now need to lock the l2 ptbl - to maintain lock ordering
++       * we set the PTBL_KEEP bit to stop the l3 ptbl from being 
++       * stolen and drop the locks in the order we aquired them
++       */
++      l3ptbl->ptbl_flags |= PTBL_KEEP;
++
++      elan3mmu_unlock_ptbl (l3ptbl, l3lock, l3flags);
++
++      if (elan3mmu_lock_ptbl (l2ptbl, LK_PTBL_FAILOK, elan3mmu, addr, PTBL_LEVEL_2, &l2lock, &l2flags) == LK_PTBL_MISMATCH)
++      {
++          HAT_PRINTF0 (2, "elan3mmu_ptealloc: l2ptbl freed, free l3 ptbl and try again\n");
++            
++          elan3mmu_lock_ptbl (l3ptbl, 0, elan3mmu, addr, PTBL_LEVEL_3, &l3lock, &l3flags);
++
++          /* free off the level 3 page table, and try again */
++          l3ptbl->ptbl_flags &= ~PTBL_KEEP;
++          elan3mmu_free_l3ptbl (dev, l3ptbl, l3lock, l3flags);
++          
++          goto retryl1;
++      }
++
++      elan3mmu_lock_ptbl (l3ptbl, 0, elan3mmu, addr, PTBL_LEVEL_3, &l3lock, &l3flags);
++
++      l3ptbl->ptbl_flags &= ~PTBL_KEEP;
++
++      /* Now have L2 and L3 ptbls locked, see if someone has beaten us to it. */
++      tl2ptp = elan3_readptp (dev, l2ptp);
++
++      HAT_PRINTF2 (2, "elan3mmu_ptealloc: l2ptp at %lx is %x\n", l2ptp, tl2ptp);
++
++      if (ELAN3_PTP_TYPE(tl2ptp) != ELAN3_ET_INVALID)
++      {
++          HAT_PRINTF0 (2, "elan3mmu_ptealloc: beaten to it, free l3 ptbl and try again\n");
++
++          /* free off the level 3 page table, and try again */
++          l3ptbl->ptbl_flags &= ~PTBL_KEEP;
++          elan3mmu_free_l3ptbl (dev, l3ptbl, l3lock, l3flags);
++          
++          /* Someone has allocated the ptbl before us */
++          elan3mmu_unlock_ptbl (l2ptbl, l2lock, l2flags);
++          
++          goto retryl1;
++      }
++
++      ASSERT (PTBL_IS_LOCKED (l2ptbl->ptbl_flags));
++
++      /* Install the L3 ptbl into the L2 one */
++      l2ptp  = PTBL_TO_PTADDR(l2ptbl) + ELAN3_L2_INDEX(addr)*ELAN3_PTP_SIZE;
++      tl2ptp = PTBL_TO_PTADDR(l3ptbl) | ELAN3_ET_PTP;
++      l2ptbl->ptbl_valid++;
++
++      HAT_PRINTF3 (2, "elan3mmu_ptealloc: inc valid for level %d ptbl %p to %d\n",
++                   PTBL_LEVEL(l2ptbl->ptbl_flags), l2ptbl, l2ptbl->ptbl_valid);
++
++      elan3_writeptp (dev, l2ptp, tl2ptp);
++
++      HAT_PRINTF2 (2, "elan3mmu_ptealloc: write level 2 ptp %lx to %x\n", l2ptp, tl2ptp);
++
++      /* unordered unlock - lock l2ptbl, lock l3ptbl, unlock l2ptbl */
++      elan3mmu_unlock_ptbl (l2ptbl, l2lock, l3flags); /* free with the l3flags to keep irq ordering */
++
++      l3pte = PTBL_TO_PTADDR(l3ptbl) + ELAN3_L3_INDEX(addr)*ELAN3_PTE_SIZE;
++      
++      /* Level 3 ptbl is already locked, so just return the pte */
++      *pptbl = l3ptbl;
++      *plock = l3lock;
++      *flags = l2flags; /* return l2flags to keep irq ordering */
++
++      return (l3pte);
++
++    default:
++      panic ("elan3mmu_ptealloc: found bad entry in level 2 page table");
++      /* NOTREACHED */
++    }
++
++    HAT_PRINTF1 (2, "elan3mmu_ptealloc: chain to level 3 page table from ptp %x\n", tl2ptp);
++
++    l3ptbl = elan3mmu_ta_to_ptbl (elan3mmu, &tl2ptp);
++    l3pte  = PTBL_TO_PTADDR(l3ptbl) + ELAN3_L3_INDEX(addr)*ELAN3_PTE_SIZE;
++    l3base = ELAN3_L3_BASE(addr);
++
++    HAT_PRINTF4 (2, "elan3mmu_ptealloc: l3ptbl %p 3pte %lx l3base %x (%x)\n",
++               l3ptbl, l3pte, l3base, l3ptbl->ptbl_base);
++               
++    if (elan3mmu_lock_ptbl (l3ptbl, LK_PTBL_FAILOK, elan3mmu, addr, PTBL_LEVEL_3, &l3lock, &l3flags) == LK_PTBL_OK)
++    {
++      *pptbl = l3ptbl;
++      *plock = l3lock;
++      *flags = l3flags;
++
++      return (l3pte);
++    }
++
++    /* got all the way down here,  but its been nicked before we could lock it */
++    /* so try all over again */
++    goto retryl1;
++}
++
++void
++elan3mmu_l1inval (ELAN3MMU *elan3mmu, ELAN3_PTBL *l1ptbl, int attr)
++{
++    ELAN3_DEV     *dev = elan3mmu->elan3mmu_dev;
++    ELAN3_PTP      invalidptp = ELAN3_INVALID_PTP;
++    ELAN3_PTP      tl1ptp;
++    sdramaddr_t   l1ptp;
++    E3_Addr       addr;
++    spinlock_t   *l2lock;
++    ELAN3_PTBL    *l2ptbl;
++    ELAN3_PTBL    *lXptbl;
++    int           idx;
++    int                 i;
++    int                 ret;
++    unsigned long flags;
++
++    l1ptp = PTBL_TO_PTADDR(l1ptbl);
++
++    HAT_PRINTF2 (1, "elan3mmu_l1inval: l1ptbl %p l1ptp %lx\n", l1ptbl, l1ptp);
++
++    for (i = 0, addr = 0; i < ELAN3_L1_ENTRIES; i++, l1ptp += ELAN3_PTP_SIZE)
++    {
++      tl1ptp = elan3_readptp (dev, l1ptp);
++      switch (ELAN3_PTP_TYPE(tl1ptp))
++      {
++      case ELAN3_ET_PTE:
++          lXptbl = elan3mmu_ta_to_ptbl (elan3mmu, &tl1ptp);
++          idx    = (PTP_TO_PT_PADDR(tl1ptp) - PTBL_TO_PTADDR(lXptbl))/ELAN3_PTE_SIZE;  
++
++          HAT_PRINTF3 (2, "elan3mmu_l1inval: l1ptbl %p : lXptbl %p idx %d\n",
++                       l1ptbl, lXptbl, idx);
++
++          /* invalidate the L1 pte. */
++          elan3_writeptp (dev, l1ptp, invalidptp);
++          if (! (attr & PTE_UNLOAD_NOFLUSH))
++              ElanFlushTlb (dev);         
++
++          l1ptbl->ptbl_valid--;
++          elan3mmu_free_pte ( dev, elan3mmu,  lXptbl, idx); 
++
++          HAT_PRINTF3 (2, "elan3mmu_l1inval: dec valid for level %d ptbl %p to %d\n",
++                   PTBL_LEVEL(l1ptbl->ptbl_flags), l1ptbl, l1ptbl->ptbl_valid);
++          
++          break;
++
++      case ELAN3_ET_PTP:
++          HAT_PRINTF5 (2, "elan3mmu_l1inval: l1ptbl %p : ptp %lx (%x) addr %x (%d)\n",
++                       l1ptbl, l1ptp, tl1ptp, addr, i);
++
++          /* invalidate the L1 ptp. */
++          elan3_writeptp (dev, l1ptp, invalidptp);
++          if (! (attr & PTE_UNLOAD_NOFLUSH))
++              ElanFlushTlb (dev);
++
++          /* invalidate the level 2 page table */
++          l2ptbl = elan3mmu_ta_to_ptbl (elan3mmu, &tl1ptp);
++          ret    = elan3mmu_l2inval (elan3mmu, l2ptbl, attr | PTE_UNLOAD_NOFLUSH, addr, &l2lock, &flags);
++
++          ASSERT ((l2ptbl->ptbl_flags & PTBL_KEEP) == 0);
++          
++          if (ret == LK_PTBL_OK)
++          {
++              if (((l2ptbl->ptbl_flags & PTBL_KEEP) == 0) && l2ptbl->ptbl_valid == 0)
++              {
++                  HAT_PRINTF1 (2, "elan3mmu_l1inval: free l2ptbl %p\n", l2ptbl);
++                  
++                  l1ptbl->ptbl_valid--;
++                  elan3mmu_free_l2ptbl (elan3mmu->elan3mmu_dev, l2ptbl, l2lock, flags);
++
++                  HAT_PRINTF3 (2, "elan3mmu_l1inval: dec valid for level %d ptbl %p to %d\n", 
++                               PTBL_LEVEL(l1ptbl->ptbl_flags), l1ptbl, l1ptbl->ptbl_valid);
++              }
++              else
++              {
++                  /* need to keep this page table,  so even though its now empty, */
++                  /* chain it back in */
++                  HAT_PRINTF1 (2, "elan3mmu_l1inval: keep l2ptbl %p\n", l2ptbl);
++
++                  elan3_writeptp (dev, l1ptp, tl1ptp);
++                  elan3mmu_unlock_ptbl (l2ptbl, l2lock, flags);
++              }    
++          }
++          else
++          {
++              l1ptbl->ptbl_valid--;
++
++              HAT_PRINTF3 (2, "elan3mmu_l1inval: dec valid for level %d ptbl %p to %d\n", 
++                           PTBL_LEVEL(l1ptbl->ptbl_flags), l1ptbl, l1ptbl->ptbl_valid);
++          }
++          break;
++          
++      case ELAN3_ET_INVALID:
++          break;
++
++      default:
++          panic ("elan3mmu_l1inval: found invalid entry in level 1 page table");
++          /* NOTREACHED */
++      }
++
++      if (l1ptbl->ptbl_valid == 0)
++          break;
++
++      addr += ELAN3_L1_SIZE;
++    }
++}
++
++int
++elan3mmu_l2inval (ELAN3MMU *elan3mmu, ELAN3_PTBL *l2ptbl, int attr, E3_Addr addr, spinlock_t **pl2lock, unsigned long *flags)
++{
++    ELAN3_DEV   *dev = elan3mmu->elan3mmu_dev;
++    ELAN3_PTP    invalidptp = ELAN3_INVALID_PTP;
++    ELAN3_PTP    tl2ptp;
++    sdramaddr_t l2ptp;
++    spinlock_t *l3lock;
++    unsigned long l3flags;
++    ELAN3_PTBL  *l3ptbl;
++    ELAN3_PTBL  *lXptbl;
++    int         idx;
++    int               i;
++    int               ret;
++
++    HAT_PRINTF2 (1, "elan3mmu_l2inval: l2ptbl %p addr %x\n", l2ptbl, addr);
++
++    ASSERT (PTBL_LEVEL (l2ptbl->ptbl_flags) == PTBL_LEVEL_2);
++    ASSERT (PTBL_LEVEL (l2ptbl->ptbl_parent->ptbl_flags) == PTBL_LEVEL_1);
++
++    ret = elan3mmu_lock_ptbl (l2ptbl, LK_PTBL_FAILOK, elan3mmu, addr, PTBL_LEVEL_2, pl2lock, flags);
++
++    ASSERT (ret == LK_PTBL_OK);
++    ASSERT (l2ptbl->ptbl_elan3mmu == elan3mmu);
++    ASSERT (l2ptbl->ptbl_parent->ptbl_elan3mmu == elan3mmu);
++
++    l2ptp = PTBL_TO_PTADDR(l2ptbl);
++
++    for (i = 0; i < ELAN3_L2_ENTRIES; i++, l2ptp += ELAN3_PTP_SIZE)
++    {
++      tl2ptp = elan3_readptp (dev, l2ptp);
++      switch (ELAN3_PTP_TYPE(tl2ptp))
++      {
++      case ELAN3_ET_PTE:
++          lXptbl = elan3mmu_ta_to_ptbl (elan3mmu, &tl2ptp);
++          idx    = (PTP_TO_PT_PADDR(tl2ptp) - PTBL_TO_PTADDR(lXptbl))/ELAN3_PTE_SIZE;  
++
++          HAT_PRINTF3 (2, "elan3mmu_l2inval: l2ptbl %p : lXptbl %p idx %d\n",
++                       l2ptbl, lXptbl, idx);
++
++          /* invalidate the L2 pte. */
++          elan3_writeptp (dev, l2ptp, invalidptp);
++          if (! (attr & PTE_UNLOAD_NOFLUSH))
++              ElanFlushTlb (dev);
++
++          l2ptbl->ptbl_valid--;
++          elan3mmu_free_pte ( dev, elan3mmu, lXptbl, idx); 
++
++          HAT_PRINTF3 (2, "elan3mmu_l2inval: dec valid for level %d ptbl %p to %d\n", PTBL_LEVEL(l2ptbl->ptbl_flags), l2ptbl, l2ptbl->ptbl_valid); 
++
++          break;
++          
++      case ELAN3_ET_PTP:
++          HAT_PRINTF5 (2, "elan3mmu_l2inval: l2ptbl %p : ptp %lx (%x) addr %x (%d)\n",
++                       l2ptbl, l2ptp, tl2ptp, addr, i);
++
++          /* invalidate the L2 ptp. */
++          elan3_writeptp (dev, l2ptp, invalidptp);
++          if (! (attr & PTE_UNLOAD_NOFLUSH))
++              ElanFlushTlb (dev);
++          
++          /* unload the level 3 page table */
++          l3ptbl = elan3mmu_ta_to_ptbl (elan3mmu, &tl2ptp);
++          ret = elan3mmu_l3inval (elan3mmu, l3ptbl, attr | PTE_UNLOAD_NOFLUSH, addr, &l3lock, &l3flags);
++          
++          if (ret == LK_PTBL_OK)
++          {
++              if ((l3ptbl->ptbl_flags & PTBL_KEEP) == 0 && l3ptbl->ptbl_valid == 0)
++              {
++                  /* decrement the valid count of the level 2 page table, and */
++                  /* free off the level 3 page table */
++                  HAT_PRINTF1 (2, "elan3mmu_l2inval: free l3ptbl %p\n", l3ptbl);
++
++                  l2ptbl->ptbl_valid--;
++                  elan3mmu_free_l3ptbl (elan3mmu->elan3mmu_dev, l3ptbl, l3lock, l3flags);
++
++                  HAT_PRINTF3 (2, "elan3mmu_l2inval: dec valid for level %d ptbl %p to %d\n", 
++                               PTBL_LEVEL(l2ptbl->ptbl_flags), l2ptbl, l2ptbl->ptbl_valid);
++              }
++              else
++              {
++                  /* need to keep this page table,  so even though its now empty, */
++                  /* chain it back in */
++                  HAT_PRINTF1 (2, "elan3mmu_l2inval: keep l3ptbl %p\n", l3ptbl);
++
++                  elan3_writeptp (dev, l2ptp, tl2ptp);
++                  elan3mmu_unlock_ptbl (l3ptbl, l3lock, l3flags);
++              }
++          }
++          else
++          {
++              l2ptbl->ptbl_valid--;
++
++              HAT_PRINTF3 (2, "elan3mmu_l2inval: dec valid for level %d ptbl %p to %d\n", 
++                           PTBL_LEVEL(l2ptbl->ptbl_flags), l2ptbl, l2ptbl->ptbl_valid);
++          }
++          break;
++          
++      case ELAN3_ET_INVALID:
++          break;
++
++      default:
++          panic ("elan3mmu_l2inval: found pte in level 2 page table");
++          /* NOTREACHED */
++      }
++
++      if (l2ptbl->ptbl_valid == 0)
++          break;
++
++      addr += ELAN3_L2_SIZE;
++    }
++
++    ASSERT (PTBL_IS_LOCKED(l2ptbl->ptbl_flags));
++
++    return (ret);
++}
++
++int 
++elan3mmu_l3inval (ELAN3MMU *elan3mmu, ELAN3_PTBL *l3ptbl, int attr, E3_Addr addr, spinlock_t **pl3lock, unsigned long *flags)
++{
++    int ret;
++
++    HAT_PRINTF3 (2, "elan3mmu_l3inval: l3ptbl %p parent %p addr %x\n", l3ptbl, l3ptbl->ptbl_parent, addr);
++
++    ASSERT (PTBL_IS_LOCKED (l3ptbl->ptbl_parent->ptbl_flags));
++    ASSERT (PTBL_LEVEL (l3ptbl->ptbl_parent->ptbl_flags) == PTBL_LEVEL_2);
++    ASSERT (l3ptbl->ptbl_parent->ptbl_elan3mmu == elan3mmu);
++    ASSERT (l3ptbl->ptbl_parent->ptbl_base == VA2BASE (ELAN3_L2_BASE(addr)));
++    
++    ret = elan3mmu_lock_ptbl (l3ptbl, LK_PTBL_FAILOK, elan3mmu, addr, PTBL_LEVEL_3, pl3lock, flags);
++
++    ASSERT (ret == LK_PTBL_OK);
++    ASSERT (PTBL_LEVEL (l3ptbl->ptbl_flags) == PTBL_LEVEL_3);
++
++    elan3mmu_unload_loop (elan3mmu, l3ptbl, 0, ELAN3_L3_ENTRIES, attr);
++
++    ASSERT (PTBL_IS_LOCKED (l3ptbl->ptbl_flags));
++
++    return (ret);
++ }
++
++int
++elan3mmu_lock_this_ptbl (ELAN3_PTBL *ptbl, int flag, spinlock_t **plock, unsigned long *flags)
++{
++    int         level = PTBL_LEVEL (ptbl->ptbl_flags);
++    spinlock_t *lock  = elan3mmu_ptbl_to_lock (level, ptbl);
++
++    local_irq_save (*flags);
++
++    if ((flag & LK_PTBL_NOWAIT) == 0)
++      spin_lock (lock);
++    else if (! spin_trylock (lock)) {
++      local_irq_restore (*flags);
++      return (LK_PTBL_FAILED);
++    }
++    
++    if (level != PTBL_LEVEL (ptbl->ptbl_flags))
++    {
++      spin_unlock (lock);     
++      local_irq_restore (*flags);
++      return (LK_PTBL_MISMATCH);
++    }
++
++    ptbl->ptbl_flags |= PTBL_LOCKED;
++    *plock = lock;
++    return (LK_PTBL_OK);
++}
++
++int
++elan3mmu_lock_ptbl (ELAN3_PTBL *ptbl, u_int flag, ELAN3MMU *elan3mmu, E3_Addr va, int level, spinlock_t **plock, unsigned long *flags)
++{
++    spinlock_t *lock = elan3mmu_ptbl_to_lock (level, ptbl);
++    int         res  = LK_PTBL_MISMATCH;
++
++    local_irq_save (*flags);
++    
++    if ((flag & LK_PTBL_NOWAIT) == 0)
++      spin_lock (lock);
++    else if (spin_trylock (lock) == 0) {
++      local_irq_restore(*flags);
++      return (LK_PTBL_FAILED);
++    }
++    
++    if (PTBL_LEVEL (ptbl->ptbl_flags) != level)
++    {
++      res = LK_PTBL_MISMATCH;
++      goto mismatch;
++    }
++    
++    /* We have the right mutex,  so check that its the ptbl we want. */
++    switch (level)
++    {
++    case PTBL_LEVEL_1: va = ELAN3_L1_BASE(va); break;
++    case PTBL_LEVEL_2: va = ELAN3_L2_BASE(va); break;
++    case PTBL_LEVEL_3: va = ELAN3_L3_BASE(va); break;
++    }
++
++    if (ptbl->ptbl_elan3mmu != elan3mmu || ptbl->ptbl_base != VA2BASE(va))
++    {
++      res = LK_PTBL_MISMATCH;
++      goto mismatch;
++    }
++
++    ASSERT ((ptbl->ptbl_flags & PTBL_LOCKED) == 0);
++    ptbl->ptbl_flags |= PTBL_LOCKED;
++
++    *plock = lock;
++    return (LK_PTBL_OK);
++
++mismatch:
++    if (! (flag & LK_PTBL_FAILOK))
++      panic ("elan3mmu: failed to lock ptbl\n");
++      
++    spin_unlock (lock);
++    local_irq_restore(*flags);
++    return (res);
++}
++
++void
++elan3mmu_unlock_ptbl (ELAN3_PTBL *ptbl, spinlock_t *lock, unsigned long flags)
++{
++    ptbl->ptbl_flags &= ~PTBL_LOCKED;
++    spin_unlock_irqrestore (lock,flags);
++}
++
++static spinlock_t *
++elan3mmu_ptbl_to_lock (int level, ELAN3_PTBL *ptbl)
++{
++    switch (level)
++    {
++    case PTBL_LEVEL_3: return (&l3ptbl_lock[L3PTBL_MTX_HASH(ptbl)]);
++    case PTBL_LEVEL_2: return (&l2ptbl_lock[L2PTBL_MTX_HASH(ptbl)]);
++    case PTBL_LEVEL_1: return (&l1ptbl_lock[L1PTBL_MTX_HASH(ptbl)]);
++    case PTBL_LEVEL_X: 
++      panic ("elan3mmu: ptbl_to_lock, bad level X");
++    default:
++      panic ("elan3mmu: ptbl_to_lock, bad level");
++      /* NOTREACHED */
++    }
++    return (NULL);
++}
++
++void
++elan3mmu_display (ELAN3MMU *elan3mmu, E3_Addr addr)
++{
++    ELAN3_DEV   *dev = elan3mmu->elan3mmu_dev;
++    ELAN3_PTBL  *l1ptbl;
++    sdramaddr_t l1ptp;
++    spinlock_t *l1lock;
++    ELAN3_PTE    tl1pte;
++    ELAN3_PTP    tl1ptp;
++    E3_Addr     l1base;
++    ELAN3_PTBL  *l2ptbl;
++    sdramaddr_t l2ptp;
++    ELAN3_PTE    tl2pte;
++    spinlock_t *l2lock;
++    ELAN3_PTP    tl2ptp;
++    E3_Addr     l2base;
++    ELAN3_PTBL  *l3ptbl;
++    sdramaddr_t l3pte;
++    ELAN3_PTE    tl3pte;
++    spinlock_t *l3lock;
++    ELAN3_PTBL  *lXptbl;
++    int         idx;
++    unsigned long flags;
++
++    elan3_debugf (NULL, DBG_HAT, "elan3mmu_display: elan3mmu %p addr %x\n", elan3mmu, addr);
++
++    l1ptbl = elan3mmu->elan3mmu_l1ptbl;
++
++    if (l1ptbl == NULL)
++      return;
++
++    l1ptp  = PTBL_TO_PTADDR(l1ptbl) + ELAN3_L1_INDEX(addr)*ELAN3_PTP_SIZE;
++    l1base = ELAN3_L1_BASE(addr);
++    
++    tl1ptp = elan3_readptp (dev, l1ptp);
++    elan3_debugf (NULL, DBG_HAT, "elan3mmu_display: l1ptbl %p l1ptp %lx l1base %x : tl1ptp %x\n", l1ptbl, l1ptp, l1base, tl1ptp);
++    
++    switch (ELAN3_PTP_TYPE(tl1ptp))
++    {
++    case ELAN3_ET_PTE:
++      elan3_debugf (NULL, DBG_HAT, "elan3mmu_display: level 1 page table for pte %x\n", tl1ptp);
++    
++      lXptbl = elan3mmu_ta_to_ptbl (elan3mmu, &tl1ptp);
++      idx    = (PTP_TO_PT_PADDR(tl1ptp) - PTBL_TO_PTADDR(lXptbl))/ELAN3_PTE_SIZE;  
++      
++      elan3_debugf (NULL, DBG_HAT, "elan3mmu_display: lXptbl %p idx %d\n",lXptbl, idx);
++
++      tl1pte = elan3_readpte (dev,(PTBL_TO_PTADDR (lXptbl) + idx * ELAN3_PTE_SIZE));
++
++      switch (elan3mmu_lock_ptbl (l1ptbl, LK_PTBL_FAILOK, elan3mmu, addr, PTBL_LEVEL_1, &l1lock, &flags))
++      {
++      case LK_PTBL_OK:
++          elan3mmu_unlock_ptbl (l1ptbl, l1lock, flags);
++          elan3_debugf (NULL, DBG_HAT, "elan3mmu_display: lvl 1 l1pte matches value %llx\n", (long long) tl1pte);
++          break;
++          
++      case LK_PTBL_FAILED:
++          panic ("elan3mmu_display: l1 lock failed");
++          /* NOTREACHED */
++          
++      case LK_PTBL_MISMATCH:
++          elan3_debugf (NULL, DBG_HAT, "elan3mmu_display: PTBL_MISMATCH : lvl 1 ptbl %p flags %x elan3mmu %p base %x (%p %x) %llx\n",
++                        l1ptbl, l1ptbl->ptbl_flags, l1ptbl->ptbl_elan3mmu, l1ptbl->ptbl_base, elan3mmu, addr, (long long)tl1pte);
++          
++          break;
++      default:
++          panic ("elan3mmu_display: lvl 1 elan3mmu_lock_ptbl returned bad value");
++          /* NOTREACHED */
++      }
++      return;
++      
++    case ELAN3_ET_INVALID:
++      return;
++      
++    case ELAN3_ET_PTP:
++      break;
++      
++    default:
++      panic ("elan3mmu_display: found bad entry in level 1 page table");
++      /* NOTREACHED */
++    }
++    
++    elan3_debugf (NULL, DBG_HAT, "elan3mmu_display: chain to level 2 ptbl from ptp %x\n", tl1ptp);
++    
++    l2ptbl = elan3mmu_ta_to_ptbl (elan3mmu, &tl1ptp);
++    l2ptp  = PTBL_TO_PTADDR(l2ptbl) + ELAN3_L2_INDEX(addr)*ELAN3_PTP_SIZE;
++    l2base = ELAN3_L2_BASE(addr);
++    
++    tl2ptp = elan3_readptp (dev, l2ptp);
++    elan3_debugf (NULL, DBG_HAT, "elan3mmu_display: l2ptbl %p l2ptp %lx l2base %x : tl2ptp %x\n",
++               l2ptbl, l2ptp, l2base, tl2ptp);
++    
++    switch (ELAN3_PTP_TYPE(tl2ptp))
++    {
++    case ELAN3_ET_PTE:
++      elan3_debugf (NULL, DBG_HAT, "elan3mmu_display: level 2 page table for pte %x\n", tl2ptp);
++    
++      lXptbl = elan3mmu_ta_to_ptbl (elan3mmu, &tl2ptp);
++      idx    = (PTP_TO_PT_PADDR(tl2ptp) - PTBL_TO_PTADDR(lXptbl))/ELAN3_PTE_SIZE;  
++      
++      elan3_debugf (NULL, DBG_HAT, "elan3mmu_display: lXptbl %p idx %d\n",lXptbl, idx);
++
++      tl2pte = elan3_readpte (dev,(PTBL_TO_PTADDR (lXptbl) + idx * ELAN3_PTE_SIZE));
++
++      switch (elan3mmu_lock_ptbl (l2ptbl, LK_PTBL_FAILOK, elan3mmu, addr, PTBL_LEVEL_2, &l2lock, &flags))
++      {
++      case LK_PTBL_OK:
++          elan3mmu_unlock_ptbl (l2ptbl, l2lock, flags);
++          elan3_debugf (NULL, DBG_HAT, "elan3mmu_display: lvl 2 l1pte matches value %llx\n", (long long)tl2pte);
++          break;
++          
++      case LK_PTBL_FAILED:
++          panic ("elan3mmu_display: l2 lock failed");
++          /* NOTREACHED */
++          
++      case LK_PTBL_MISMATCH:
++          elan3_debugf (NULL, DBG_HAT, "elan3mmu_display: PTBL_MISMATCH : lvl 2 ptbl %p flags %x elan3mmu %p base %x (%p %x) %llx\n",
++                        l2ptbl, l2ptbl->ptbl_flags, l2ptbl->ptbl_elan3mmu, l2ptbl->ptbl_base, elan3mmu, addr, (long long) tl2pte);
++          
++          break;
++      default:
++          panic ("elan3mmu_display: lvl 2 elan3mmu_lock_ptbl returned bad value");
++          /* NOTREACHED */
++      }
++      return;
++      
++    case ELAN3_ET_INVALID:
++      return;
++      
++    case ELAN3_ET_PTP:
++      break;
++
++    default:
++      panic ("elan3mmu_display: found bad entry in level 2 page table");
++      /* NOTREACHED */
++    }
++    
++    elan3_debugf (NULL, DBG_HAT, "elan3mmu_display: chain to level 3 page table from ptp %x\n", tl2ptp);
++    
++    l3ptbl = elan3mmu_ta_to_ptbl (elan3mmu, &tl2ptp);
++    l3pte  = PTBL_TO_PTADDR(l3ptbl) + ELAN3_L3_INDEX(addr)*ELAN3_PTE_SIZE;
++    
++    elan3_debugf (NULL, DBG_HAT, "elan3mmu_display: l3ptbl %p l3pte %lx\n",l3ptbl, l3pte);
++    
++    tl3pte = elan3_readpte (dev, l3pte);
++    switch (elan3mmu_lock_ptbl (l3ptbl, LK_PTBL_FAILOK, elan3mmu, addr, PTBL_LEVEL_3, &l3lock, &flags))
++    {
++    case LK_PTBL_OK:
++      elan3mmu_unlock_ptbl (l3ptbl, l3lock, flags);
++      elan3_debugf (NULL, DBG_HAT, "elan3mmu_display: l3pte matches value %llx\n", (long long) tl3pte);
++      break;
++      
++    case LK_PTBL_FAILED:
++      panic ("elan3mmu_display: l3 lock failed");
++      /* NOTREACHED */
++      
++    case LK_PTBL_MISMATCH:
++      elan3_debugf (NULL, DBG_HAT, "elan3mmu_display: PTBL_MISMATCH : ptbl %p flags %x elan3mmu %p base %x (%p %x) %llx\n",
++                   l3ptbl, l3ptbl->ptbl_flags, l3ptbl->ptbl_elan3mmu, l3ptbl->ptbl_base, elan3mmu, addr, (long long) tl3pte);
++      
++      break;
++      
++    default:
++      panic ("elan3mmu_display: elan3mmu_lock_ptbl returned bad value");
++      /* NOTREACHED */
++    }
++}
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan3/elan3mmu_linux.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan3/elan3mmu_linux.c 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan3/elan3mmu_linux.c      2005-06-01 23:12:54.574442904 -0400
+@@ -0,0 +1,284 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: elan3mmu_linux.c,v 1.50.2.3 2004/12/14 10:19:51 mike Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/vm/elan3mmu_linux.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <qsnet/kpte.h>
++
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elanvp.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanctxt.h>
++#include <elan3/elandebug.h>
++#include <elan3/urom_addrs.h>
++#include <elan3/thread.h>
++
++/*
++ * Strategy for syncing main <-> elan pte's:
++ * 
++ *   Install callbacks for linux flush_tlb_page(), flush_tlb_range(),
++ *   flush_tlb_all(), and flush_tlb_mm() so when a main PTE changes,
++ *   the elan translations, if any, are invalidated.  They can then be
++ *   faulted in again with the correct physical page, perms, etc., on demand. 
++ * 
++ *   Callbacks are stacked on the mm_struct, one per context.  We also stack
++ *   a ctxt pointer so we don't have to do lookups on every call.
++ *
++ *   Sanity check -- we clearly want to flush the elan PTEs in these 
++ *   situations, all of which are covered by tlb_flush_{page,range}()
++ *
++ *     1) kernel/vmscan.c::try_to_swap_out() swaps out a page
++ *
++ *     2) kernel/mremap.c::copy_one_pte() moves a page as a result of the 
++ *     mremap system call
++ * 
++ *     3) kernel/mprotect.c::change_pte_range() changes the permissions of a 
++ *     page as the result of the mprotect system call
++ *
++ * Other Notes: 
++ * 
++ *   Dirty a page in the mains page tables when it is faulted into the elan.
++ *   This way it will not be thrown away by the swapper.
++ * 
++ *   Pages write protected for COW are copied by elan3mmu_main_pagefault()
++ *   when a writeable translation is loaded into the elan.
++ */
++
++caddr_t            elan3mmu_kernel_invalid_space;
++ELAN3_PTE     elan3mmu_kernel_invalid_pte_val;
++
++void
++elan3mmu_init_osdep (void)
++{
++    pte_t *pte;
++
++    KMEM_GETPAGES (elan3mmu_kernel_invalid_space, caddr_t, 1, TRUE);
++
++    ASSERT(elan3mmu_kernel_invalid_space != NULL);
++
++    pte = find_pte_kernel ((unsigned long) elan3mmu_kernel_invalid_space);
++
++    elan3mmu_kernel_invalid_pte_val = ELAN3_PTE_64_BIT | (pte_phys(*pte) & ELAN3_PTE_PFN_MASK) | ELAN3_PERM_REMOTEREAD | ELAN3_ET_PTE;
++
++#ifdef __alpha
++    /*
++     * NOTE: Elan sign-extends bit 48 of the physical address, so if we need to
++     *       set any of bits 63:48, then we will set them all by setting bit 48/
++     */
++    if (alpha_mv.pci_dac_offset & 0xFFFF000000000000ull)
++        elan3mmu_kernel_invalid_pte_val |= (1ull << 48);
++    else
++      elan3mmu_kernel_invalid_pte_val |= alpha_mv.pci_dac_offset;
++#endif
++
++    HAT_PRINTF(0x10, "elan3mmu_invalid_space at %p phys=%llx pte=%llx\n", elan3mmu_kernel_invalid_space, 
++             (unsigned long long) pte_phys(*pte), (unsigned long long) elan3mmu_kernel_invalid_pte_val);
++}
++
++void
++elan3mmu_fini_osdep()
++{
++    KMEM_FREEPAGES (elan3mmu_kernel_invalid_space, 1);
++}
++
++void
++elan3mmu_alloc_osdep (ELAN3MMU *elan3mmu)
++{
++    elan3mmu->elan3mmu_coproc_mm = current->mm;
++}
++
++/*
++ * Convert physical page frame number to elan pte.
++ */
++ELAN3_PTE
++elan3mmu_phys_to_pte (ELAN3_DEV *dev, physaddr_t paddr, int perm)
++{
++    ELAN3_PTE newpte;
++    
++    ASSERT (paddr != 0);
++    
++    if ((paddr & dev->SdramPhysMask) == dev->SdramPhysBase)           /* SDRAM, turn on PTE_LOCAL bit */
++    {
++      PRINTF(NULL, DBG_HAT, "elan3mmu_phys_to_pte: phys %llx SDRAM\n", (unsigned long long) paddr);
++      
++      newpte = ELAN3_PTE_LOCAL | (paddr & ELAN3_PTE_PFN_MASK & ~dev->SdramPhysMask) | perm | ELAN3_ET_PTE;
++    }
++#if defined(LINUX_ALPHA)
++    else if ((paddr & dev->PciPhysMask) == dev->PciPhysBase)
++    {
++      PRINTF(NULL, DBG_HAT, "elan3mmu_phys_to_pte: phys %llx PCI\n", (unsigned long long) paddr);
++      newpte = ELAN3_PTE_64_BIT | (paddr & ELAN3_PTE_PFN_MASK & ~dev->PciPhysMask) | perm | ELAN3_ET_PTE;
++    }
++#endif
++    else                                              /* main memory, must convert to PCI view */
++    {
++      PRINTF(NULL, DBG_HAT, "elan3mmu_phys_to_pte: phys %llx is main memory\n", (unsigned long long) paddr);
++
++      /* main memory, just set the architecture specific PTE_BYPASS bit */
++      /* This requires the Tsunami chipset being programmed to support
++       * the monster window option. This is in linux-2.4.5 and later kernels 
++       * and is also patched into the RH 7.1/2.4.3-12 Alpha kernel
++       */
++      newpte = ELAN3_PTE_64_BIT | (paddr & ELAN3_PTE_PFN_MASK) | perm | ELAN3_ET_PTE;
++
++#ifdef __alpha
++      /*
++       * NOTE: Elan sign-extends bit 48 of the physical address, so if we need to
++       *       set any of bits 63:48, then we will set them all by setting bit 48/
++       */
++      if (alpha_mv.pci_dac_offset & 0xFFFF000000000000ull)
++            newpte |= (1ull << 48);
++        else
++          newpte |= alpha_mv.pci_dac_offset;
++#endif
++    }
++
++    if ( ELAN3_PERM_WRITEABLE( perm )) 
++      newpte |= ( ELAN3_PTE_MOD | ELAN3_PTE_REF );
++    else
++      newpte |= ( ELAN3_PTE_REF ) ;
++
++    return (newpte);
++}
++
++ELAN3_PTE
++elan3mmu_kernel_invalid_pte (ELAN3MMU *elan3mmu)
++{
++    if (elan3mmu->elan3mmu_dev->Devinfo.dev_revision_id == PCI_REVISION_ID_ELAN3_REVB)
++      return (elan3mmu_kernel_invalid_pte_val);
++    return (ELAN3_INVALID_PTE);
++}
++
++/* 
++ * Invalidate a range of addresses for specified context.
++ */
++void
++elan3mmu_pte_range_unload (ELAN3MMU *elan3mmu, struct mm_struct *mm, caddr_t addr, unsigned long len)
++{
++    E3_Addr       eaddr;
++    ELAN3MMU_RGN *rgn;
++    unsigned long span;
++
++    spin_lock (&elan3mmu->elan3mmu_lock);
++
++    for (; len; len -= span, addr += span)
++    {
++      rgn = elan3mmu_findrgn_main (elan3mmu, addr, 0);
++
++      if (rgn == NULL || (rgn->rgn_mbase + rgn->rgn_len) < addr)
++          span = len;
++      else if (rgn->rgn_mbase > addr)
++          span = MIN(len, rgn->rgn_mbase - addr);
++      else
++      {
++          span  = MIN(len, (rgn->rgn_mbase + rgn->rgn_len) - addr);
++          eaddr = rgn->rgn_ebase + (addr - rgn->rgn_mbase);
++
++            HAT_PRINTF(0x10, "  unloading eaddr %x main %p (%ld pages)\n", 
++            eaddr, addr, btopr(span));
++          elan3mmu_unload (elan3mmu, eaddr, span, PTE_UNLOAD);
++      }                       /* takes care of elan tlb flush also */
++    }
++
++    spin_unlock (&elan3mmu->elan3mmu_lock);
++}
++
++/*
++ *
++ */
++void
++elan3mmu_update_range (ELAN3MMU *elan3mmu, struct mm_struct *mm, caddr_t vaddr, E3_Addr eaddr, u_int len, u_int perm)
++{
++    u_int roperm = ELAN3_PERM_READONLY(perm & ELAN3_PTE_PERM_MASK) | (perm & ~ELAN3_PTE_PERM_MASK);
++    u_int off;
++
++    HAT_PRINTF3(1, "elan3mmu_update_range (elan3mmu %p addr %p -> %p)\n", elan3mmu, vaddr, vaddr+len-1);
++
++    while (len > 0)
++    {
++      pte_t *pte_ptr;
++      pte_t  pte_value;
++
++      pte_ptr = find_pte_map(mm, (unsigned long)vaddr);
++      if (pte_ptr) {
++          pte_value = *pte_ptr;
++          pte_unmap(pte_ptr);
++      }
++
++      HAT_PRINTF(0x10, "  elan3mmu_update_range %x (%p) %s\n", eaddr, vaddr, 
++              !pte_ptr ? "invalid" : pte_none(pte_value) ? "none " : !pte_present(pte_value) ? "swapped " : 
++              !pte_write(pte_value) ? "RO/COW" : "OK");
++      
++      if (pte_ptr && !pte_none(pte_value) && pte_present(pte_value))
++          for (off = 0; off < PAGE_SIZE; off += ELAN3_PAGE_SIZE)
++              elan3mmu_pteload (elan3mmu, PTBL_LEVEL_3, eaddr + off, pte_phys(pte_value) + off, pte_write(pte_value) ? perm : roperm, PTE_LOAD|PTE_NO_SLEEP|PTE_NO_STEAL);
++      vaddr += PAGESIZE;
++      eaddr += PAGESIZE;
++      len   -= PAGESIZE;
++    }
++}
++
++/* 
++ * Update a range of addresses for specified context.
++ */
++void
++elan3mmu_pte_range_update (ELAN3MMU *elan3mmu, struct mm_struct *mm,caddr_t vaddr, unsigned long len)
++{
++    E3_Addr       eaddr;
++    ELAN3MMU_RGN *rgn;
++    unsigned long span;
++
++    spin_lock (&elan3mmu->elan3mmu_lock);
++
++    for (; len; len -= span, vaddr += span)
++    {
++      rgn = elan3mmu_findrgn_main (elan3mmu, vaddr, 0);
++
++      if (rgn == NULL || (rgn->rgn_mbase + rgn->rgn_len) < vaddr)
++          span = len;
++      else if (rgn->rgn_mbase > vaddr)
++          span = MIN(len, rgn->rgn_mbase - vaddr);
++      else
++      {
++          span  = MIN(len, (rgn->rgn_mbase + rgn->rgn_len) - vaddr);
++          eaddr = rgn->rgn_ebase + (vaddr - rgn->rgn_mbase);
++
++            HAT_PRINTF(0x10, "  updating eaddr %u main %p (%ld pages)\n", 
++            eaddr, vaddr, btopr(span));
++          
++          elan3mmu_update_range(elan3mmu, mm, vaddr, eaddr, span, rgn->rgn_perm);
++      }                       
++    }
++
++    spin_unlock (&elan3mmu->elan3mmu_lock);
++}
++
++/* 
++ * Invalidate all ptes for the given context.
++ */
++void
++elan3mmu_pte_ctxt_unload(ELAN3MMU *elan3mmu)
++{
++    ELAN3_PTBL  *l1ptbl   = (elan3mmu ? elan3mmu->elan3mmu_l1ptbl : NULL);
++    spinlock_t *l1mtx;
++    unsigned long flags;
++
++    if (l1ptbl && elan3mmu_lock_ptbl (l1ptbl, LK_PTBL_FAILOK, elan3mmu, (E3_Addr) 0, 1, &l1mtx, &flags) == LK_PTBL_OK)
++    {
++      elan3mmu_l1inval(elan3mmu, elan3mmu->elan3mmu_l1ptbl, 0);
++      elan3mmu_unlock_ptbl (l1ptbl, l1mtx, flags);
++    }
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan3/elan3ops.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan3/elan3ops.c       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan3/elan3ops.c    2005-06-01 23:12:54.575442752 -0400
+@@ -0,0 +1,170 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: elan3ops.c,v 1.4 2003/09/24 13:57:25 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/os/elan3ops.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <elan/elanmod.h>
++
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elan3ops.h>
++
++extern ELAN_STATS_OPS elan3_device_stats_ops;
++
++ELAN_DEV_OPS elan3_dev_ops = {
++
++      get_position,
++      set_position,   
++
++      ELAN_DEV_OPS_VERSION
++};
++
++ELAN_STATS_OPS elan3_device_stats_ops = {
++      ELAN_STATS_OPS_VERSION,
++
++      stats_get_index_name,
++      stats_get_block,
++      stats_clear_block
++};
++
++static char *elan3_device_stats_names[ELAN3_NUM_STATS] = 
++{
++              "version field",                 /* not cleared */
++              "elan interrupts",
++              "tlb flushes",
++              "traps with invalid context",
++              "interrupts com queue half full",
++              "cproc traps",
++              "dproc traps",
++              "tproc traps",
++              "iproc traps",
++              "event interrupts",
++              "elan page faults",
++              "EopBadAcks",
++              "EopResets",
++              "InputterBadLength",
++              "InputterCRCDiscards",
++              "InputterCRCErrors",
++              "InputterCRCBad",       
++              "errors in dma data",
++              "errors after dma identify",
++              "errors after thread identify",
++              "dma retries",
++              "dma output timeouts",
++              "dma packet ack errors",
++              "forced tproc traps",
++              "too many instruction traps",
++              "output timeouts",
++              "packet ack errors",
++              "LockError",
++              "DeskewError",
++              "PhaseError",
++              "DataError",
++              "FifoOvFlow0",
++              "FifoOvFlow1",
++              "link error value on data error",
++              "correctable ecc errors",
++              "uncorrectable ecc errors",
++              "multiple ecc errors",
++              "sdram bytes free",              /* not cleared */
++              "longest interrupt in ticks",
++              "punts of event int's to thread",
++              "reschedules of event int's thread"
++};
++
++int 
++stats_get_index_name (void *arg, uint  index, caddr_t name)
++{
++      copyout (elan3_device_stats_names[index], name, strlen (elan3_device_stats_names[index]) + 1  /* with \0 */);
++
++      return (0);
++}
++
++int
++stats_get_block (void *arg, uint entries, ulong *value)
++{
++      ELAN3_DEV *dev = (ELAN3_DEV *) arg;
++
++      if ( entries >  ELAN3_NUM_STATS ) /* if space too big only send valid portion */
++              entries = ELAN3_NUM_STATS;
++      
++      copyout(&dev->Stats, value, sizeof(ulong) * entries);
++
++      return (0);
++}
++
++int 
++stats_clear_block (void *arg)
++{
++      ELAN3_DEV *dev = (ELAN3_DEV *) arg;
++      u_long   *ptr = (u_long *) &dev->Stats;
++      int                n;
++      
++      for (n = 0; n < ELAN3_NUM_STATS; n++)
++      {
++              switch (n) 
++              {
++              case offsetof (ELAN3_STATS, Version)/sizeof(u_long):
++              case offsetof (ELAN3_STATS, SdramBytesFree)/sizeof(u_long):
++                      break;
++              default:
++                      ptr[n] = (ulong)0;
++              }
++      }
++      return (0);
++}
++
++int 
++get_position (void *user_data, ELAN_POSITION *position)
++{
++      ELAN3_DEV *dev = (ELAN3_DEV *)user_data;
++
++      copyout(&dev->Position, position, sizeof(ELAN_POSITION));
++
++      return (0);     
++}
++
++int 
++set_position (void *user_data, unsigned short nodeId, unsigned short numNodes)
++{
++      ELAN3_DEV *dev = (ELAN3_DEV *)user_data;
++
++      if (ComputePosition (&dev->Position, nodeId, numNodes, dev->Devinfo.dev_num_down_links_value) != 0)
++              return (EINVAL);
++      
++      return (0);     
++}
++
++int
++elan3_register_dev_stats(ELAN3_DEV * dev) 
++{
++      char name[ELAN_STATS_NAME_MAX_LEN+1];
++
++      sprintf (name, ELAN3_STATS_DEV_FMT, dev->Instance);
++
++      elan_stats_register(&dev->StatsIndex,
++                             name,
++                             sizeof (elan3_device_stats_names)/sizeof (elan3_device_stats_names[0]),
++                             &elan3_device_stats_ops,
++                             (void *)dev);
++
++      return (0);
++}
++
++void
++elan3_deregister_dev_stats(ELAN3_DEV * dev) 
++{
++      elan_stats_deregister(dev->StatsIndex);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan3/elandebug.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan3/elandebug.c      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan3/elandebug.c   2005-06-01 23:12:54.575442752 -0400
+@@ -0,0 +1,151 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: elandebug.c,v 1.25 2003/09/24 13:57:25 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/os/elandebug.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elanvp.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanctxt.h>
++#include <elan3/elandebug.h>
++
++
++void
++elan3_debugf (void *p, unsigned int mode, char *fmt,...)
++{
++    char prefix[128];
++
++#if defined (DIGITAL_UNIX)
++#define PREFIX_FMT    "[%lx.%08x]"
++#define PREFIX_VAL    (int)CURTHREAD()
++#else
++#define PREFIX_FMT    "[%lx.%04d]"
++#define PREFIX_VAL    (current->pid)
++#endif
++
++    if ((unsigned long) p > DBG_NTYPES)
++    {
++      ELAN3_CTXT *ctxt = (ELAN3_CTXT *) p;
++
++        if (elan3_debug_display_ctxt && (ctxt->Capability.cap_mycontext & MAX_ROOT_CONTEXT_MASK) != elan3_debug_display_ctxt)
++            return;
++        if (elan3_debug_ignore_ctxt  && (ctxt->Capability.cap_mycontext & MAX_ROOT_CONTEXT_MASK) == elan3_debug_ignore_ctxt)
++            return;
++ 
++      if (ctxt->Capability.cap_mycontext == ELAN_CAP_UNINITIALISED)
++          sprintf (prefix, PREFIX_FMT " (XXX) ", lbolt, PREFIX_VAL);
++      else
++          sprintf (prefix, PREFIX_FMT " (%03x) ", lbolt, PREFIX_VAL,
++                   ctxt->Capability.cap_mycontext & MAX_ROOT_CONTEXT_MASK);
++    }
++    else
++    {
++      char *what;
++
++      if (elan3_debug_ignore_dev & (1 << ((unsigned long) p)))
++          return;
++
++      switch ((unsigned long) p)
++      {
++      case (int) DBG_DEVICE: what = "dev"; break;
++      case (int) DBG_KCOMM:  what = "kcm"; break;
++      case (int) DBG_ICS:    what = "ics"; break;
++      case (int) DBG_USER:   what = "usr"; break;
++      default:               what = NULL; break;
++      }
++          
++      if (what)
++          sprintf (prefix, PREFIX_FMT " [%s] ", lbolt,  PREFIX_VAL, what);
++      else
++          sprintf (prefix, PREFIX_FMT " [%3d] ", lbolt,  PREFIX_VAL, (int)(long)what);
++    }
++
++    {
++      va_list       ap;
++
++      va_start (ap, fmt);
++      qsnet_vdebugf ((((mode & elan3_debug_buffer)?QSNET_DEBUG_BUFFER:0)|((mode & elan3_debug_console)?QSNET_DEBUG_CONSOLE:0)) , prefix, fmt, ap);
++      va_end (ap);
++    }
++}
++
++
++void
++elan3_alloc_panicstate (ELAN3_DEV *dev, int allocsdram)
++{
++    register int bank;
++
++    if (dev->PanicState.RegPtr == NULL)
++      KMEM_ZALLOC (dev->PanicState.RegPtr, E3_Regs *, sizeof (E3_Regs), 1);
++
++    if (allocsdram)
++      for (bank = 0; bank < ELAN3_SDRAM_NUM_BANKS; bank++)
++          if (dev->PanicState.Sdram[bank] == NULL && dev->SdramBanks[bank].Size)
++              KMEM_ZALLOC (dev->PanicState.Sdram[bank], char *, dev->SdramBanks[bank].Size, 1);
++}
++
++void
++elan3_free_panicstate (ELAN3_DEV *dev)
++{
++    register int bank;
++
++    if (dev->PanicState.RegPtr != NULL)
++      KMEM_FREE (dev->PanicState.RegPtr, sizeof (E3_Regs));
++
++    for (bank = 0; bank < ELAN3_SDRAM_NUM_BANKS; bank++)
++      if (dev->PanicState.Sdram[bank] != NULL && dev->SdramBanks[bank].Size)
++          KMEM_FREE (dev->PanicState.Sdram[bank], dev->SdramBanks[bank].Size);
++
++    bzero (&dev->PanicState, sizeof (dev->PanicState));
++}
++
++void
++elan3_save_panicstate (ELAN3_DEV *dev)
++{
++    register int bank;
++    
++    if (dev->PanicState.RegPtr)
++    {
++      printk ("elan%d: saving state on panic .....\n", dev->Devinfo.dev_instance);
++
++      bcopy ((void *) dev->RegPtr, (void *) dev->PanicState.RegPtr, sizeof (E3_Regs));
++      
++      for (bank = 0; bank < ELAN3_SDRAM_NUM_BANKS; bank++)
++          if (dev->SdramBanks[bank].Size && dev->PanicState.Sdram[bank])
++              elan3_sdram_copyq_from_sdram (dev, (bank << ELAN3_SDRAM_BANK_SHIFT), dev->PanicState.Sdram[bank], dev->SdramBanks[bank].Size);
++      
++    }
++}
++
++int
++elan3_assfail (ELAN3_DEV *dev, char *string, char *file, int line)
++{
++    if (panicstr)
++      return (0);
++
++    printk ("elan: assertion failed '%s' File '%s' Line %d\n", string, file, line);
++
++#if defined(LINUX)
++    elan3_save_panicstate (dev);
++
++    panic ("elan: assertion failed '%s' File '%s' Line %d\n", string, file, line);
++#else
++    cmn_err (CE_PANIC, "elan: assertion failed '%s' File '%s' Line %d\n", string, file, line);
++#endif
++    /*NOTREACHED*/
++    return (0);
++}
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan3/elandev_generic.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan3/elandev_generic.c        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan3/elandev_generic.c     2005-06-01 23:12:54.578442296 -0400
+@@ -0,0 +1,1862 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: elandev_generic.c,v 1.111.2.3 2004/11/15 11:12:36 mike Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/os/elandev_generic.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <qsnet/kthread.h>
++
++#include <elan3/dma.h>
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elanvp.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanctxt.h>
++#include <elan3/elandebug.h>
++#include <elan3/elansyscall.h>
++#include <elan3/urom_addrs.h>
++#include <elan3/elan3ops.h>
++
++/*
++ * Module globals, configurable from system file.
++ */
++u_int  elan3_debug                  = 0;
++u_int  elan3_debug_console      = 0;
++u_int  elan3_debug_buffer           = -1;
++u_int  elan3_debug_ignore_dev       = 0;
++u_int  elan3_debug_ignore_kcomm     = 0;
++u_int  elan3_debug_ignore_ctxt      = 0;
++u_int  elan3_debug_display_ctxt     = 0;
++
++int    eventint_punt_loops;
++int    eventint_punt_ticks;
++int    eventint_resched_ticks;
++
++static void InitialiseDmaBuffers (ELAN3_DEV *dev, ioaddr_t CmdPort);
++static int  ProbeSdram (ELAN3_DEV *dev);
++static void InitialiseSdram (ELAN3_DEV *dev);
++static void ReEnableErrorInterrupts (void *arg);
++void        PollForDmaHungup (void *arg);
++static void elan3_event_interrupt (ELAN3_DEV *dev);
++
++/*
++ * BaseAddr is ptr to the start of a table aligned on a power of two byte address.
++ * SizePower must be in the range of 6 to 12. It defines the number of valid contexts as
++ * shown below.
++ *
++ * SizePower  Valid Contexts  Table size in bytes.
++ *     6           64               1k
++ *     7          128               2k
++ *     8          256               4K
++ *     9          512               8k
++ *    10         1024              16k
++ *    11         2048              32k
++ *    12         4096              64k
++ */
++#define GEN_CONTEXT_PTR(BaseAddr, SizePower) (((E3_uint32) BaseAddr) |        \
++                                            (~((1 << ((SizePower) - 6)) - 1) & 0x3f))
++
++int
++InitialiseElan (ELAN3_DEV *dev, ioaddr_t CmdPort)
++{
++    E3_IprocTrapHeader_BE   TrapCleanup[4];
++    E3_ContextControlBlock  ContextControlBlock;
++    sdramaddr_t             ptr;
++    int                           res;
++    int                           i;
++
++    eventint_punt_loops    = 100;
++    eventint_punt_ticks    = (hz/100);
++    eventint_resched_ticks = (hz/4);
++    
++    dev->Stats.Version     = ELAN3_STATS_VERSION;
++    dev->Position.pos_mode = ELAN_POS_UNKNOWN;
++
++    /*
++     * The elan should have already been reset, so the interrupt mask
++     * should be 0 and the schedule status register should be set to
++     * its initial state
++     */
++    ASSERT (dev->InterruptMask == 0);
++    ASSERT ((read_reg32 (dev, Exts.SchCntReg) & HaltStopAndExtTestMask) == Sched_Initial_Value);
++
++    /*
++     * Write any value here to clear out the half full and error bits of the command
++     * overflow queues.
++     */
++    write_reg32 (dev, ComQueueStatus, 0);
++
++    /* Initialise the cache tags before touching the SDRAM */
++    /* we initialise them to "map" the bottom of SDRAM */
++    for (i = 0; i < E3_NumCacheLines; i++)
++    {
++      write_cache_tag (dev, Tags[i][0].Value, 0x0000000000000000ULL);
++      write_cache_tag (dev, Tags[i][1].Value, 0x0000080000000000ULL);
++      write_cache_tag (dev, Tags[i][2].Value, 0x0000100000000000ULL);
++      write_cache_tag (dev, Tags[i][3].Value, 0x0000180000000000ULL);
++    }
++
++#ifndef CONFIG_MPSAS
++    for (i = 0; i < E3_NumCacheLines*(E3_CACHELINE_SIZE/sizeof(E3_uint64)); i++)
++    {
++      write_cache_set (dev, Set0[i], 0xcac1ecac1ecac1e0ULL);
++      write_cache_set (dev, Set1[i], 0xcac1ecac1ecac1e1ULL);
++      write_cache_set (dev, Set2[i], 0xcac1ecac1ecac1e2ULL);
++      write_cache_set (dev, Set3[i], 0xcac1ecac1ecac1e3ULL);
++    }
++#endif
++
++    if ((res = ProbeSdram(dev)) != ESUCCESS)
++      return (res);
++
++    /* Enable all cache sets before initialising the sdram allocators */
++    write_reg32 (dev, Cache_Control_Reg.ContReg, (dev->Cache_Control_Reg |= CONT_EN_ALL_SETS));
++
++    InitialiseSdram (dev);
++
++    dev->TAndQBase              = elan3_sdram_alloc (dev, ELAN3_TANDQ_SIZE);
++    dev->ContextTable           = elan3_sdram_alloc (dev, ELAN3_CONTEXT_SIZE);
++    dev->ContextTableSize       = ELAN3_NUM_CONTEXTS;
++    dev->CommandPortTraps[0]    = elan3_sdram_alloc (dev, ELAN3_COMMAND_TRAP_SIZE);
++    dev->CommandPortTraps[1]    = elan3_sdram_alloc (dev, ELAN3_COMMAND_TRAP_SIZE);
++    dev->CurrentCommandPortTrap = 0;
++
++    PRINTF3 (DBG_DEVICE, DBG_CONFIG, "InitialiseElan: ContextTable %08lx TAndQ %08lx CommandPortTrap %08lx\n",
++           dev->ContextTable, dev->TAndQBase, dev->CommandPortTraps[0]);
++
++    /* Allocate the thread amd dma trap areas */
++    KMEM_ZALLOC (dev->ThreadTrap, THREAD_TRAP *, sizeof (THREAD_TRAP), TRUE);
++    KMEM_ZALLOC (dev->DmaTrap, DMA_TRAP *, sizeof (DMA_TRAP), TRUE);
++
++    /* Allocate the ctxt table */
++    KMEM_ZALLOC (dev->CtxtTable,  ELAN3_CTXT **, dev->ContextTableSize * sizeof ( ELAN3_CTXT *), TRUE);
++
++    /* Initialise halt queue list */
++    dev->HaltOperationsTailpp   = &dev->HaltOperations;
++
++    /* From elan3/code/harness/elanstuff.c */
++    /* Init the clock. */
++    write_ureg64 (dev, Clock.NanoSecClock, 0);
++    
++    /* Init the instruction count reg. */
++    write_ureg32 (dev, InstCount.s.StatsCount, 0);
++    
++    /* Init the stats control reg. Must be done before the count regs.*/
++    write_ureg32 (dev, StatCont.StatsControl, 0);
++    
++    /* Init the stats count regs. */
++    write_ureg32 (dev, StatCounts[0].s.StatsCount, 0);
++    write_ureg32 (dev, StatCounts[1].s.StatsCount, 0);
++    write_ureg32 (dev, StatCounts[2].s.StatsCount, 0);
++    write_ureg32 (dev, StatCounts[3].s.StatsCount, 0);
++    write_ureg32 (dev, StatCounts[4].s.StatsCount, 0);
++    write_ureg32 (dev, StatCounts[5].s.StatsCount, 0);
++    write_ureg32 (dev, StatCounts[6].s.StatsCount, 0);
++    write_ureg32 (dev, StatCounts[7].s.StatsCount, 0);
++    
++    /*
++     * Initialise the Context_Ptr and Fault_Base_Ptr
++     */
++    write_reg32 (dev, Fault_Base_Ptr, dev->TAndQBase + offsetof(E3_TrapAndQueue, IProcSysCntx));
++    write_reg32 (dev, Context_Ptr, GEN_CONTEXT_PTR (dev->ContextTable, ELAN3_LN2_NUM_CONTEXTS));
++
++    /* scrub the TProc Registers */
++    for (i = 0; i < 8; i++)
++      write_reg32 (dev, Globals[i], 0xdeadbabe);
++    for (i = 0; i < 8; i++)
++      write_reg32 (dev, Outs[i], 0xdeadbabe);
++    for (i = 0; i < 8; i++)
++      write_reg32 (dev, Locals[i], 0xdeadbabe);
++    for (i = 0; i < 8; i++)
++      write_reg32 (dev, Ins[i], 0xdeadbabe);
++
++    /*
++     * Initialise the Queue pointers.  Arrange them so that the starting positions are
++     * farthest apart in one set of the cache. Thus 512 bytes apart,  but with cntx0
++     * thread the same as the interrupt queue.
++     */
++    write_reg32 (dev, TProc_NonSysCntx_FPtr, dev->TAndQBase + offsetof (E3_TrapAndQueue, NonSysCntxThreadQueue[0xc0]));
++    write_reg32 (dev, TProc_NonSysCntx_BPtr, dev->TAndQBase + offsetof (E3_TrapAndQueue, NonSysCntxThreadQueue[0xc0]));
++    write_reg32 (dev, TProc_SysCntx_FPtr,    dev->TAndQBase + offsetof (E3_TrapAndQueue, SysCntxThreadQueue[0x80]));
++    write_reg32 (dev, TProc_SysCntx_BPtr,    dev->TAndQBase + offsetof (E3_TrapAndQueue, SysCntxThreadQueue[0x80]));
++    
++    write_reg32 (dev, DProc_NonSysCntx_FPtr, dev->TAndQBase + offsetof (E3_TrapAndQueue, NonSysCntxDmaQueue[0]));
++    write_reg32 (dev, DProc_NonSysCntx_BPtr, dev->TAndQBase + offsetof (E3_TrapAndQueue, NonSysCntxDmaQueue[0]));
++    write_reg32 (dev, DProc_SysCntx_FPtr,    dev->TAndQBase + offsetof (E3_TrapAndQueue, SysCntxDmaQueue[0x10]));
++    write_reg32 (dev, DProc_SysCntx_BPtr,    dev->TAndQBase + offsetof (E3_TrapAndQueue, SysCntxDmaQueue[0x10]));
++    
++    dev->Event_Int_Queue_FPtr = dev->TAndQBase + offsetof (E3_TrapAndQueue, EventIntQueue[0x80]);
++    write_reg32 (dev, Event_Int_Queue_FPtr, dev->Event_Int_Queue_FPtr);
++    write_reg32 (dev, Event_Int_Queue_BPtr, dev->TAndQBase + offsetof (E3_TrapAndQueue, EventIntQueue[0x80]));
++    
++    
++    /* Initialise Input_Trap_Base to last 8 Kbytes of trap area, uCode adds the right offset */
++    write_reg32 (dev, Input_Trap_Base, dev->TAndQBase + offsetof (E3_TrapAndQueue, SysCntxThreadQueue[0]));
++    
++    /* Ptr to word used to save the SP to when a thread deschedules */
++    write_reg32 (dev, Thread_SP_Save_Ptr, dev->TAndQBase + offsetof (E3_TrapAndQueue, Thread_SP_Save));
++    
++    /* Initialise the command trap base */
++    write_reg32 (dev, CProc_TrapSave_Addr, dev->CommandPortTraps[0]);
++    
++    /* Initialise the set event tracing registers */
++    write_reg32 (dev, Event_Trace_Ptr, 0);
++    write_reg32 (dev, Event_Trace_Mask, 0);
++    
++    /* Initialise Tlb_Line_Value to zero. The TLB cannot be read while either the */
++    /* uCode or thread proc might be running. Must be set to 0. */
++    write_reg64 (dev, Tlb_Line_Value, 0);
++
++    /* Control register. Cache everything, Enable MMU, RefreshRate=3, CasLatency=1, StartSDR */
++    dev->Cache_Control_Reg |= CONT_MMU_ENABLE | CONT_EN_ALL_SETS | CONT_CACHE_ALL | CONT_ENABLE_ECC;
++
++#if ELAN3_PAGE_SHIFT == 13
++    dev->Cache_Control_Reg |= CONT_ENABLE_8K_PAGES;
++#endif
++
++    write_reg32 (dev, Cache_Control_Reg.ContReg,  dev->Cache_Control_Reg);
++
++    /*
++     * Initialise the context table to be discard for all contexts
++     */
++    ContextControlBlock.rootPTP  = 0;
++    ContextControlBlock.filter   = E3_CCB_DISCARD_ALL;
++    ContextControlBlock.VPT_mask = 0;
++    ContextControlBlock.VPT_ptr  = 0;
++
++    for (i = 0, ptr = dev->ContextTable; i < ELAN3_NUM_CONTEXTS; i++, ptr += sizeof (E3_ContextControlBlock))
++      elan3_sdram_copyl_to_sdram (dev, &ContextControlBlock, ptr, sizeof (E3_ContextControlBlock));
++
++    /* From elan3/code/trap_handler/init.c */
++    /*
++     * Initialise the Trap And Queue area in Elan SDRAM.
++     */
++    TrapCleanup[0].s.TrTypeCntx.TypeContext = 0;
++    TrapCleanup[0].s.TrAddr               = 0;
++    TrapCleanup[0].s.IProcTrapStatus.Status = CRC_STATUS_GOOD;
++    TrapCleanup[0].s.TrData0              = 0;
++    TrapCleanup[1].s.TrTypeCntx.TypeContext = 0;
++    TrapCleanup[1].s.TrAddr               = 0;
++    TrapCleanup[1].s.IProcTrapStatus.Status = CRC_STATUS_GOOD;
++    TrapCleanup[1].s.TrData0              = 0;
++    TrapCleanup[2].s.TrTypeCntx.TypeContext = 0;
++    TrapCleanup[2].s.TrAddr               = 0;
++    TrapCleanup[2].s.IProcTrapStatus.Status = CRC_STATUS_GOOD;
++    TrapCleanup[2].s.TrData0              = 0;
++    TrapCleanup[3].s.TrTypeCntx.TypeContext = 0;
++    TrapCleanup[3].s.TrAddr               = 0;
++    TrapCleanup[3].s.IProcTrapStatus.Status = CRC_STATUS_GOOD;
++    TrapCleanup[3].s.TrData0              = 0;
++
++    elan3_sdram_writel (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, IProcSysCntx.s.FaultContext),  0);
++    elan3_sdram_writel (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, IProcSysCntx.s.FSR.Status), 0);
++    elan3_sdram_writel (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, IProcNonSysCntx.s.FaultContext), 0);
++    elan3_sdram_writel (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, IProcNonSysCntx.s.FSR.Status), 0);
++    
++    /* Must now zero all the FSRs so that a subsequent Fault can be seen */ 
++    elan3_sdram_zeroq_sdram (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, CProc), 16);
++
++    elan3_sdram_zeroq_sdram (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProc), 16);
++    elan3_sdram_zeroq_sdram (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProcData0), 64);
++    
++    elan3_sdram_zeroq_sdram (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, TProc), 16);
++    elan3_sdram_zeroq_sdram (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, ThreadProcData), 16);
++    elan3_sdram_zeroq_sdram (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, ThreadProcInst), 16);
++    elan3_sdram_zeroq_sdram (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, ThreadProcOpen), 16);
++
++    elan3_sdram_copyq_to_sdram (dev, TrapCleanup, dev->TAndQBase + offsetof (E3_TrapAndQueue, VCh0_C0_TrHead[0]), 64);
++    elan3_sdram_copyq_to_sdram (dev, TrapCleanup, dev->TAndQBase + offsetof (E3_TrapAndQueue, VCh1_C0_TrHead[0]), 64);
++
++    elan3_sdram_copyq_to_sdram (dev, TrapCleanup, dev->TAndQBase + offsetof (E3_TrapAndQueue, VCh0_NonC0_TrHead[0]), 64);
++    elan3_sdram_copyq_to_sdram (dev, TrapCleanup, dev->TAndQBase + offsetof (E3_TrapAndQueue, VCh1_NonC0_TrHead[0]), 64);
++
++    InitialiseDmaBuffers(dev, CmdPort);
++
++    /* reserve a halt operation for flushing the context filter */
++    ReserveHaltOperations (dev, 1, TRUE);
++
++    /* Allow the Thread/Dma to run */
++    CLEAR_SCHED_STATUS (dev, HaltThread | HaltDmas);
++
++    /* Enable All Interrrupts */
++    SET_INT_MASK (dev, (INT_PciMemErr | INT_SDRamInt | INT_EventInterrupt | INT_LinkError | INT_ComQueue |
++                      INT_TProc | INT_CProc | INT_DProc | INT_IProcCh1NonSysCntx | 
++                      INT_IProcCh1SysCntx | INT_IProcCh0NonSysCntx | INT_IProcCh0SysCntx));
++
++    /* Take the link out of boundary scan */
++    SET_SCHED_LINK_VALUE (dev, 0, 0);
++    
++    /* And clear any link errors */
++    PULSE_SCHED_STATUS (dev, ClearLinkErrorInt);
++
++    /* XXXX: clear discard context 0,  AFTER setting up the kernel comms */
++    CLEAR_SCHED_STATUS (dev, DiscardSysCntxIn | DiscardNonSysCntxIn);
++
++    /* Start a thread to handle excessive Event Interrrupts */
++    if (kernel_thread_create (elan3_event_interrupt, (caddr_t) dev) == NULL)
++    {
++      panic ("InitialiseElan: cannot start elan3_event_interrupt\n");
++      return (EFAIL);
++    }
++    dev->EventInterruptThreadStarted = 1;
++
++    ReserveHaltOperations (dev, 1, TRUE);
++
++    PollForDmaHungup (dev);
++
++    /* register the device and stats with elanmod for RMS */
++    dev->DeviceIdx = elan_dev_register(&dev->Devinfo, &elan3_dev_ops, (void *) dev);
++    
++    elan3_register_dev_stats(dev);
++
++    return (ESUCCESS);
++}
++
++static void
++InitialiseDmaBuffers(ELAN3_DEV *dev, ioaddr_t CmdPort)
++{
++   register int i;
++
++   /* GNAT sw-elan3/3908:
++    * Clear down the power on state of the Dma_Desc registers to make sure we don't
++    * try and interpret them when a trap happens.
++    */
++   write_reg32 (dev, Dma_Desc.dma_type,            0);
++   write_reg32 (dev, Dma_Desc.dma_size,            0);
++   write_reg32 (dev, Dma_Desc.dma_source,          0);
++   write_reg32 (dev, Dma_Desc.dma_dest,            0);
++   write_reg32 (dev, Dma_Desc.dma_destEvent,       0);
++   write_reg32 (dev, Dma_Desc.dma_destCookieVProc, 0);
++   write_reg32 (dev, Dma_Desc.dma_srcEvent,        0);
++   write_reg32 (dev, Dma_Desc.dma_srcCookieVProc,  0);
++   
++   /*
++    * The following is a sequence of writes to remove X's from the dma buffers and 
++    * registers. It is only safe to write these registers after reset and before any
++    * dma's have been issued. The chip will NOT function corectly if they are written at
++    * any other time or in a different order.
++    */
++   write_reg64 (dev, Exts.Dmas.DmaWrs.LdAlignment, 0);
++   write_reg64 (dev, Exts.Dmas.DmaWrs.LdDmaType, 0);
++   write_reg64 (dev, Exts.Dmas.DmaWrs.ResetAckNLdBytesToWr, ((u_longlong_t)0x1000) << 32);
++   write_reg64 (dev, Exts.Dmas.DmaWrs.LdBytesToRd, ((u_longlong_t)0x100) << 32);
++
++   for (i=0;i<(4*8);i++)
++       write_reg64 (dev, Dma_Alignment_Port[0], 0);
++
++   /*
++    * This is used to clear out X's from some of the trap registers. This is required to
++    * prevent the first traps from possibly writting X's into the SDram and upsetting the
++    * ECC value. It requires that the trap save area registers have been set up but does
++    * not require any translations to be ready.
++    */
++   writel (-1, CmdPort + offsetof (E3_CommandPort, SetEvent));
++   while ((read_reg32 (dev, Exts.InterruptReg) & INT_CProc) == 0)
++   {
++       mb();
++       DELAY (1);
++   }
++
++   write_reg32 (dev, CProc_TrapSave_Addr, dev->CommandPortTraps[dev->CurrentCommandPortTrap]);
++   
++   PULSE_SCHED_STATUS(dev, RestartCProc);
++}
++
++void
++FinaliseElan (ELAN3_DEV *dev)
++{
++    ELAN3_PTBL_GR *ptg;
++    ELAN3_HALTOP  *op;
++    ELAN3_HALTOP  *chain = NULL;
++    int           bank;
++    int                 indx;
++    int                 size;
++    unsigned long flags;
++    int           level;
++
++    elan_stats_deregister (dev->StatsIndex);
++    elan_dev_deregister(&dev->Devinfo);
++
++    /* Cancel the dma poller */
++    cancel_timer_fn (&dev->DmaPollTimeoutId);
++
++    /* release it's halt operation */
++    ReleaseHaltOperations (dev, 1);
++
++    /* stop all kernel threads */
++    dev->ThreadsShouldStop = 1;
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++    while (dev->EventInterruptThreadStarted && !dev->EventInterruptThreadStopped)
++    {
++      kcondvar_wakeupall (&dev->IntrWait, &dev->IntrLock);
++      kcondvar_wait (&dev->IntrWait, &dev->IntrLock, &flags);
++    }
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++
++    /* Set the interrupt mask to 0 and the schedule control register to run nothing */
++    SET_INT_MASK (dev, 0);
++    SET_SCHED_STATUS (dev, DiscardNonSysCntxIn | DiscardSysCntxIn | HaltThread | HaltDmas);
++
++    /* Cancel any link error timeout */
++    if (timer_fn_queued(&dev->ErrorTimeoutId))
++      cancel_timer_fn (&dev->ErrorTimeoutId);
++
++    /* Free of and page tables that have been allocated */
++    spin_lock (&dev->PtblGroupLock);
++    for(level=0; level<4; level++) 
++    {
++      while ((ptg = dev->Level[level].PtblGroupList) != NULL)
++      {
++          dev->Level[level].PtblGroupList = ptg->pg_next;
++
++          elan3_sdram_free (dev, ptg->pg_addr, PTBL_GROUP_SIZE);
++          FREE_PTBL_GR(ptg);
++      }
++    }
++ 
++    spin_unlock (&dev->PtblGroupLock);
++
++    /* Free of all halt operations */
++    spin_lock_irqsave (&dev->FreeHaltLock, flags);
++    while ((op = dev->FreeHaltOperations) != NULL)
++    {
++      dev->FreeHaltOperations = op->Next;
++
++      /* Keep a list of 'freed' ops for later KMEM_FREE call */
++      op->Next = chain;
++      chain = op;
++    }
++    spin_unlock_irqrestore (&dev->FreeHaltLock, flags);
++
++    /* Have now dropped the spinlock - can call KMEM_FREE */
++    while ((op = chain) != NULL)
++    {
++      chain = op->Next;
++
++      KMEM_FREE (op, sizeof (ELAN3_HALTOP));
++    }
++      
++    /* Free of the ctxt table */
++    KMEM_FREE (dev->CtxtTable,  dev->ContextTableSize * sizeof (ELAN3_CTXT *));
++
++    /* Free of the thread and dma atrap areas */
++    KMEM_FREE (dev->ThreadTrap, sizeof (THREAD_TRAP));
++    KMEM_FREE (dev->DmaTrap, sizeof (DMA_TRAP));
++
++    /* Free of the memsegs and pages */
++    for (bank = 0; bank < ELAN3_SDRAM_NUM_BANKS; bank++)
++    {
++      if (dev->SdramBanks[bank].Size)
++      {
++          UnmapDeviceRegister (dev, &dev->SdramBanks[bank].Handle);
++
++          KMEM_FREE (dev->SdramBanks[bank].PtblGroups, sizeof (ELAN3_PTBL_GR *) * (dev->SdramBanks[bank].Size / PTBL_GROUP_SIZE));
++
++          for (indx = 0, size = SDRAM_MIN_BLOCK_SIZE; size <= dev->SdramBanks[bank].Size; indx++, size <<= 1)
++              KMEM_FREE (dev->SdramBanks[bank].Bitmaps[indx], sizeof (bitmap_t)*BT_BITOUL(dev->SdramBanks[bank].Size/size));
++      }
++    }
++    elan3_sdram_fini (dev);
++}
++
++#define INIT_PATTERN(offset)  (0xBEEC000000000011ull | ((u_longlong_t)(offset)) << 16)
++#define FREE_PATTERN(offset)  (0xBEEC000000000022ull | ((u_longlong_t)(offset)) << 16)
++
++static int
++ProbeSdram (ELAN3_DEV *dev)
++{
++    int                         Instance;
++    u_int               Bank;
++    int                         MemSpaceSize;
++    int                         BankMaxSize;
++    int                         BankOffset;
++    int                         BankSize;
++    ioaddr_t            BankBase;
++    ioaddr_t            PageBase;
++    ioaddr_t            PageBase1;
++    ioaddr_t            PageBase2;
++    DeviceMappingHandle   BankHandle;
++    DeviceMappingHandle   PageHandle;
++    DeviceMappingHandle   PageHandle1;
++    DeviceMappingHandle   PageHandle2;
++    register int          i;
++    u_longlong_t        value;
++    extern int            sdram_bank_limit;
++
++    /* NOTE: The Cache control register is set to only enable cache set 0 */
++    /*       and has ECC disabled */
++    Instance = dev->Instance;
++    
++    /* Determine the size of the SDRAM from the BAR register */
++    if (DeviceRegisterSize (dev, ELAN3_BAR_SDRAM, &MemSpaceSize) != ESUCCESS)
++    {
++      printk ("elan%d: cannot determine SDRAM size\n", Instance);
++      return (EFAIL);
++    }
++
++    elan3_sdram_init (dev);
++
++    BankMaxSize = MemSpaceSize / ELAN3_SDRAM_NUM_BANKS;
++
++    for (Bank = 0; Bank < ELAN3_SDRAM_NUM_BANKS; Bank++)
++    {
++      BankOffset = Bank * BankMaxSize;
++      
++      PRINTF3 (DBG_DEVICE, DBG_CONFIG, "elan%d: Probing RAM Bank %d (max size %08x)\n", Instance, Bank, BankMaxSize);
++      
++      /* Probe the memory bank by mapping two pages that are the size of the cache apart */
++      /* this guarantees that when we store the second pattern we displace the first pattern */
++      /* from the cache, also store the second pattern again the size of the cache up again */
++      /* to ensure that the SDRAM wires don't stay floating at pattern1 */
++
++      if (MapDeviceRegister (dev, ELAN3_BAR_SDRAM, &BankBase, BankOffset, PAGESIZE, &BankHandle) != ESUCCESS)
++      {
++          printk ("elan%d: Cannot probe memory bank %d\n", Instance, Bank);
++          continue;
++      }
++      
++      if (MapDeviceRegister (dev, ELAN3_BAR_SDRAM, &PageBase1, BankOffset + ELAN3_MAX_CACHE_SIZE, PAGESIZE, &PageHandle1) != ESUCCESS)
++      {
++          printk ("elan%d: Cannot probe memory bank %d\n", Instance, Bank);
++          UnmapDeviceRegister (dev, &BankHandle);
++          continue;
++      }
++
++      if (MapDeviceRegister (dev, ELAN3_BAR_SDRAM, &PageBase2, BankOffset + 2*ELAN3_MAX_CACHE_SIZE, PAGESIZE, &PageHandle2) != ESUCCESS)
++      {
++          printk ("elan%d: Cannot probe memory bank %d\n", Instance, Bank);
++          UnmapDeviceRegister (dev, &BankHandle);
++          UnmapDeviceRegister (dev, &PageHandle1);
++          continue;
++      }
++
++#define PATTERN0      (0x5555555555555555L)
++#define PATTERN1      (0xAAAAAAAAAAAAAAAAL)
++      writeq (PATTERN0, (u_longlong_t *) BankBase);
++      writeq (PATTERN1, (u_longlong_t *) PageBase1);
++      writeq (PATTERN1, (u_longlong_t *) PageBase2);
++
++      mmiob();
++
++      value = readq ((u_longlong_t *) BankBase);
++
++      if (value != PATTERN0)
++      {
++          UnmapDeviceRegister (dev, &BankHandle);
++          UnmapDeviceRegister (dev, &PageHandle1);
++          UnmapDeviceRegister (dev, &PageHandle2);
++          continue;
++      }
++
++      writeq (PATTERN1, (u_longlong_t *) BankBase);
++      writeq (PATTERN0, (u_longlong_t *) PageBase1);
++      writeq (PATTERN0, (u_longlong_t *) PageBase2);
++
++      mmiob();
++      
++      value = readq ((u_longlong_t *) BankBase);
++      if (value != PATTERN1)
++      {
++          UnmapDeviceRegister (dev, &BankHandle);
++          UnmapDeviceRegister (dev, &PageHandle1);
++          UnmapDeviceRegister (dev, &PageHandle2);
++          continue;
++      }
++      UnmapDeviceRegister (dev, &PageHandle1);
++      UnmapDeviceRegister (dev, &PageHandle2);
++
++      /* Bank is present, so work out its size,  we store tha maximum size at the base */
++      /* and then store the address at each address  on every power of two address until */
++      /* we reach the minimum mappable size (PAGESIZE), we then read back the value at the */
++      /* base to determine the bank size */
++      writeq ((u_longlong_t) BankMaxSize, (u_longlong_t *) BankBase);
++
++      for (BankSize = (BankMaxSize>>1); BankSize > PAGESIZE; BankSize >>= 1)
++      {
++          if (MapDeviceRegister (dev, ELAN3_BAR_SDRAM, &PageBase, BankOffset + BankSize, PAGESIZE, &PageHandle) == ESUCCESS)
++          {
++              writeq (BankSize, (u_longlong_t *) PageBase);
++              UnmapDeviceRegister (dev, &PageHandle);
++          }
++      }
++      mmiob();
++
++      BankSize = (u_long) readq ((u_longlong_t *) BankBase);
++      
++      if (sdram_bank_limit == 0 || BankSize <= (sdram_bank_limit * 1024 * 1024))
++          printk ("elan%d: memory bank %d is %dK\n", Instance, Bank, BankSize / 1024);
++      else
++      {
++          BankSize = (sdram_bank_limit * 1024 * 1024);
++          printk ("elan%d: limit memory bank %d to %dK\n", Instance, Bank, BankSize / 1024);
++      }
++
++      UnmapDeviceRegister (dev, &BankHandle);
++      
++      /* Now map all of this bank into the kernel */
++      if (MapDeviceRegister (dev, ELAN3_BAR_SDRAM, &BankBase, BankOffset, BankSize, &BankHandle) != ESUCCESS)
++      {
++          printk ("elan%d: Cannot initialise memory bank %d\n", Instance, Bank);
++          continue;
++      }
++      
++      dev->SdramBanks[Bank].Size    = BankSize;
++      dev->SdramBanks[Bank].Mapping = BankBase;
++      dev->SdramBanks[Bank].Handle  = BankHandle;
++
++#ifndef CONFIG_MPSAS
++      /* Initialise it for ECC */
++      preemptable_start {
++          for (i = 0; i < BankSize; i += 8)
++          {
++              elan3_sdram_writeq (dev, (Bank << ELAN3_SDRAM_BANK_SHIFT) | i, INIT_PATTERN(BankOffset+i));
++
++              preemptable_check();
++          }
++      } preemptable_end;
++#endif
++    }
++    
++    return (ESUCCESS);
++}
++
++static void
++InitialiseSdram (ELAN3_DEV *dev)
++{
++    int indx, size, b;
++
++    for (b = 0; b < ELAN3_SDRAM_NUM_BANKS; b++)
++    {
++      ELAN3_SDRAM_BANK *bank = &dev->SdramBanks[b];
++
++      if (bank->Size == 0)
++          continue;
++
++      /* allocate a ptbl group pointer for each possible ptbl group in this bank */
++      KMEM_ZALLOC (bank->PtblGroups, ELAN3_PTBL_GR **, sizeof (ELAN3_PTBL_GR *) * bank->Size/PTBL_GROUP_SIZE, TRUE);
++          
++      /* allocate the buddy allocator bitmaps */
++      for (indx = 0, size = SDRAM_MIN_BLOCK_SIZE; size <= bank->Size; indx++, size <<= 1)
++          KMEM_ZALLOC (bank->Bitmaps[indx], bitmap_t *, sizeof (bitmap_t)*BT_BITOUL(bank->Size/size), TRUE);
++          
++      /* and add it to the sdram buddy allocator */
++      elan3_sdram_add (dev, (b << ELAN3_SDRAM_BANK_SHIFT), (b << ELAN3_SDRAM_BANK_SHIFT) + bank->Size);
++    }
++}
++
++#include <elan3/vpd.h>
++
++int
++ReadVitalProductData (ELAN3_DEV *dev, int *CasLatency)
++{
++    DeviceMappingHandle RomHandle;
++    unsigned char      *RomBase;
++    unsigned char      *PCIDataPtr;
++    unsigned char      *VPDPtr;
++    unsigned char      *lim;
++    int                       type;
++    int                       i, len, len2;
++    char              name[3] = "XX";
++    char              value[256];
++    int                       finished = 0;
++
++    
++    /* default valud for CAS latency is 3 */
++    (*CasLatency) = CAS_LATENCY_3;
++
++    if (MapDeviceRegister (dev, ELAN3_BAR_EBUS, (ioaddr_t *) &RomBase, ELAN3_EBUS_ROM_OFFSET, ELAN3_EBUS_ROM_SIZE, &RomHandle) != ESUCCESS)
++    {
++      printk ("elan%d: Cannot map ROM\n", dev->Instance);
++      return (EFAIL);
++    }
++    
++    /* Check the ROM signature */
++    if (RomBase[0] != 0x55 || RomBase[1] != 0xAA)
++    {
++      printk ("elan%d: Invalid ROM signature %02x %02x\n", dev->Instance, RomBase[0], RomBase[1]);
++      return (ESUCCESS);
++    }
++    
++    PCIDataPtr = RomBase + ((RomBase[0x19] << 8) | RomBase[0x18]);
++
++    /* check the pci data structure */
++    if (PCIDataPtr[0] != 'P' || PCIDataPtr[1] != 'C' || PCIDataPtr[2] != 'I' || PCIDataPtr[3] != 'R')
++    {
++      printk ("elan%d: Invalid PCI Data structure\n", dev->Instance);
++      return (ESUCCESS);
++    }
++    
++    /* Extract the VPD pointer */
++    VPDPtr = RomBase + ((PCIDataPtr[9] << 8) | PCIDataPtr[8]);
++
++    if (VPDPtr == RomBase)
++    {
++      printk ("elan%d: No Vital Product Data\n", dev->Instance);
++      return (ESUCCESS);
++    }
++
++    while (! finished)
++    {
++      type = *VPDPtr++;
++      
++      if (type & LARGE_RESOURCE_BIT)
++      {
++          len = *(VPDPtr++);
++          len += *(VPDPtr++) << 8;
++
++          switch (type & ~LARGE_RESOURCE_BIT)
++          {
++          case LARGE_RESOURCE_STRING:
++              printk ("elan%d: ", dev->Instance);
++              for (i = 0; i < len; i++)
++                  printk ("%c", *VPDPtr++);
++              printk ("\n");
++              break;
++              
++          case LARGE_RESOURCE_VENDOR_DEFINED:
++              VPDPtr += len;
++              break;
++              
++          case LARGE_RESOURCE_VITAL_PRODUCT_DATA:
++              for (lim = VPDPtr + len; VPDPtr < lim; )
++              {
++                  name[0] = *VPDPtr++;
++                  name[1] = *VPDPtr++;
++                  len2    = *VPDPtr++;
++
++                  for (i = 0; i < len2 && VPDPtr < lim; i++)
++                      value[i] = *VPDPtr++;
++                  value[i] = '\0';
++
++                  if (! strcmp (name, "SN"))
++                      printk ("elan%d: Serial Number - %s\n", dev->Instance, value);
++
++                  if (! strcmp (name, "Z0"))
++                      (*CasLatency) = (strcmp (value, "CAS_LATENCY_2") ? CAS_LATENCY_3 : CAS_LATENCY_2);
++              }
++              break;
++              
++          default:
++              printk ("elan%d: unknown large resource %x\n", dev->Instance, type);
++              finished = 1;
++              break;
++          }
++      }
++      else
++      {
++          len = type & 0x7;
++
++          switch (type >> 3)
++          {
++          case SMALL_RESOURCE_COMPATIBLE_DEVICE_ID:
++              VPDPtr += len;
++              break;
++
++          case SMALL_RESOURCE_VENDOR_DEFINED:
++              VPDPtr += len;
++              break;
++              
++          case SMALL_RESOURCE_END_TAG:
++              finished = 1;
++              break;
++              
++          default:
++              printk ("elan%d: unknown small resource %x\n", dev->Instance, type >> 3);
++              finished = 1;
++              break;
++          }
++      }
++    }
++    
++    UnmapDeviceRegister (dev, &RomHandle);
++    return (ESUCCESS);
++}
++
++void
++ElanSetPtblGr (ELAN3_DEV *dev, sdramaddr_t offset, ELAN3_PTBL_GR *ptg)
++{
++    int bank = offset >> ELAN3_SDRAM_BANK_SHIFT;
++    
++    dev->SdramBanks[bank].PtblGroups[(offset & (ELAN3_SDRAM_BANK_SIZE-1)) / PTBL_GROUP_SIZE] = ptg;
++}
++
++ELAN3_PTBL_GR *
++ElanGetPtblGr (ELAN3_DEV *dev, sdramaddr_t offset)
++{
++    int bank = offset >> ELAN3_SDRAM_BANK_SHIFT;
++    
++    return (dev->SdramBanks[bank].PtblGroups[(offset & (ELAN3_SDRAM_BANK_SIZE-1)) / PTBL_GROUP_SIZE]);
++}
++
++void
++ElanFlushTlb (ELAN3_DEV *dev)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->TlbLock, flags);
++    BumpStat (dev, TlbFlushes);
++
++    write_reg32 (dev, Cache_Control_Reg.ContReg, dev->Cache_Control_Reg | MMU_FLUSH);
++    mmiob();
++    spin_unlock_irqrestore (&dev->TlbLock, flags);
++
++    while (! (read_reg32 (dev, Cache_Control_Reg.ContReg) & MMU_FLUSHED))
++      mb();
++}
++
++void
++KillNegativeDma (ELAN3_DEV *dev, void *arg)
++{
++    DMA_TRAP     *trap    = dev->DmaTrap;
++    E3_Status_Reg status;
++    sdramaddr_t   FPtr, BPtr;
++    sdramaddr_t   Base, Top;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++
++    ASSERT (read_reg32 (dev, Exts.InterruptReg) & INT_DProcHalted);
++
++    /* Initialise the trap to deliver to the offending user process */
++    trap->Status.Status   = read_reg32 (dev, Exts.DProcStatus.Status);
++    trap->PacketInfo.Value = 0;
++    
++    bzero (&trap->FaultSave, sizeof (trap->FaultSave));
++    bzero (&trap->Data0, sizeof (trap->Data0));
++    bzero (&trap->Data1, sizeof (trap->Data1));
++    bzero (&trap->Data2, sizeof (trap->Data2));
++    bzero (&trap->Data3, sizeof (trap->Data3));
++
++    /* run down the kernel dma run queue and panic on a -ve length dma */
++    FPtr  = read_reg32 (dev, DProc_SysCntx_FPtr);
++    BPtr  = read_reg32 (dev, DProc_SysCntx_BPtr);
++    Base  = dev->TAndQBase + offsetof (E3_TrapAndQueue, SysCntxDmaQueue[0]);
++    Top   = dev->TAndQBase + offsetof (E3_TrapAndQueue, SysCntxDmaQueue[E3_SysCntxQueueSize-1]);
++    
++    while (FPtr != BPtr)
++    {
++      elan3_sdram_copyq_from_sdram (dev, FPtr, &trap->Desc, sizeof (E3_DMA_BE));
++      
++      if (trap->Desc.s.dma_size > E3_MAX_DMA_SIZE)
++          panic ("KillNegativeDma: -ve sized kernel dma\n");
++
++      FPtr = (FPtr == Top) ? Base : FPtr + sizeof (E3_DMA);
++    }
++
++    /* run down the user dma run queue and "remove" and -ve length dma's */
++    FPtr  = read_reg32 (dev, DProc_NonSysCntx_FPtr);
++    BPtr  = read_reg32 (dev, DProc_NonSysCntx_BPtr);
++    Base  = dev->TAndQBase + offsetof (E3_TrapAndQueue, NonSysCntxDmaQueue[0]);
++    Top   = dev->TAndQBase + offsetof (E3_TrapAndQueue, NonSysCntxDmaQueue[E3_NonSysCntxQueueSize-1]);
++    
++    while (FPtr != BPtr)
++    {
++      elan3_sdram_copyq_from_sdram (dev, FPtr, &trap->Desc, sizeof (E3_DMA_BE));
++      
++      if (trap->Desc.s.dma_size > E3_MAX_DMA_SIZE)
++      {
++          PRINTF3 (NULL, DBG_INTR, "KillNegativeDma: remove dma - context %d size %d SuspendAddr %x\n", 
++                   trap->Desc.s.dma_u.s.Context, trap->Desc.s.dma_size, trap->Status.s.SuspendAddr);
++
++          trap->Status.s.TrapType = trap->Status.s.SuspendAddr;
++          trap->Status.s.Context  = trap->Desc.s.dma_u.s.Context;
++
++          DeliverDProcTrap (dev, trap, 0);
++
++          /*
++           * Remove the DMA from the queue by replacing it with one with
++           * zero size and no events.
++           *
++           * NOTE: we must preserve the SYS_CONTEXT_BIT since the Elan uses this
++           * to mark the approriate run queue as empty.
++           */
++          trap->Desc.s.dma_type            = 0;
++          trap->Desc.s.dma_size            = 0;
++          trap->Desc.s.dma_source          = (E3_Addr) 0;
++          trap->Desc.s.dma_dest            = (E3_Addr) 0;
++          trap->Desc.s.dma_destCookieVProc = (E3_Addr) 0;
++          trap->Desc.s.dma_srcEvent        = (E3_Addr) 0;
++          trap->Desc.s.dma_srcCookieVProc  = (E3_Addr) 0;
++
++          elan3_sdram_copyq_to_sdram (dev, &trap->Desc, FPtr, sizeof (E3_DMA_BE));
++      }
++
++      FPtr = (FPtr == Top) ? Base : FPtr + sizeof (E3_DMA);
++    }
++
++    status.Status = read_reg32 (dev, Exts.DProcStatus.Status);
++
++    if (status.s.SuspendAddr == MI_DequeueNonSysCntxDma || 
++      status.s.SuspendAddr == MI_DequeueSysCntxDma ||
++      status.s.SuspendAddr == MI_DmaLoop)
++    {
++      PRINTF0 (NULL, DBG_INTR, "KillNegativeDma: unlock dma processor\n");
++      write_reg32 (dev, Exts.Dmas.DmaWrs.LdAlignment, 0);
++      write_reg32 (dev, Exts.Dmas.DmaWrs.LdDmaType,   0);
++      mmiob();
++      
++      DELAY (10);
++      
++      write_reg32 (dev, Exts.Dmas.DmaWrs.LdAlignment, 0);
++      write_reg32 (dev, Exts.Dmas.DmaWrs.LdDmaType,   0);
++      mmiob();
++    }
++
++    PRINTF0 (NULL, DBG_INTR, "KillNegativeDma: dma processor restarted\n");
++
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++
++    schedule_timer_fn (&dev->DmaPollTimeoutId, PollForDmaHungup, (void *) dev, 1);
++}
++
++void
++ForceTProcTrap (ELAN3_DEV *dev, void *arg)
++{
++    printk ("elan%d: forced tproc trap .....\n", dev->Instance);
++
++    schedule_timer_fn (&dev->DmaPollTimeoutId, PollForDmaHungup, (void *) dev, 1);
++}
++
++void
++PollForDmaHungup (void *arg)
++{
++    ELAN3_DEV     *dev   = (ELAN3_DEV *) arg;
++    unsigned long flags;
++    E3_Status_Reg status;
++    E3_uint32     insn1, insn3;
++    register int  i;
++
++    if (read_reg32 (dev, Dma_Desc.dma_size) > E3_MAX_DMA_SIZE)
++    {
++      status.Status = read_reg32 (dev, Exts.DProcStatus);
++
++      PRINTF2 (NULL, DBG_INTR, "PollForDmaHungup: size %x SuspendAddr %x\n", read_reg32 (dev, Dma_Desc.dma_size), status.s.SuspendAddr);
++
++      if (status.s.SuspendAddr == MI_DequeueNonSysCntxDma || 
++          status.s.SuspendAddr == MI_DequeueSysCntxDma ||
++          status.s.SuspendAddr == MI_DmaLoop)
++      {
++          printk ("elan%d: PollForDmaHungup: size %x context %d SuspendAddr %x\n", 
++                  dev->Instance, read_reg32 (dev, Dma_Desc.dma_size),
++                  status.s.Context, status.s.SuspendAddr);
++      
++          PRINTF2 (NULL, DBG_INTR, "PollForDmaHungup: dma_size %x status %x\n",
++                   read_reg32 (dev, Dma_Desc.dma_size), status.Status);
++          
++          spin_lock_irqsave (&dev->IntrLock, flags);
++          QueueHaltOperation (dev, 0, NULL, INT_DProcHalted, KillNegativeDma, NULL);
++          spin_unlock_irqrestore (&dev->IntrLock, flags);
++          
++          return;
++      }
++    }
++
++    status.Status = read_reg32 (dev, Exts.TProcStatus);
++    if (status.s.WakeupFunction == WakeupStopped)
++    {
++      E3_uint32 PC = read_reg32 (dev, ExecutePC);
++
++      /* See if it's likely that the thread is really "stuck" on a waitevent/break 
++       * instruction ......... */
++      for (i = 0; i < 10; i++)
++      {
++          status.Status = read_reg32 (dev, Exts.TProcStatus);
++          insn1         = read_reg32 (dev, IBufferReg[1]);
++          insn3         = read_reg32 (dev, IBufferReg[3]);
++          
++          if (! (status.s.WakeupFunction == WakeupStopped && read_reg32 (dev, ExecutePC) == PC &&     /* stopping and it could be a break/waitevent */
++                 (insn1 == 0x81a00000 || insn3 == 0x81a00000 ||                                       /* break instruction */
++                  insn1 == 0x81b00000 || insn3 == 0x81b00000)))                                       /* waitevent instruction  */
++              break;
++      }
++
++      if (i == 10)
++      {
++          printk ("elan%d: forcing tproc trap from %s instruction at pc %x\n", dev->Instance, 
++                  (insn1 == 0x81a00000 || insn3 == 0x81a00000) ? "break" : "waitevent", PC);
++
++          spin_lock_irqsave (&dev->IntrLock, flags);
++          QueueHaltOperation (dev, 0, NULL, INT_TProcHalted, ForceTProcTrap, NULL);
++          spin_unlock_irqrestore (&dev->IntrLock, flags);
++          return;
++      }
++    }
++
++    schedule_timer_fn (&dev->DmaPollTimeoutId, PollForDmaHungup, (void *) dev, 10);
++}
++
++/*=======================================================================================*/
++/*
++ * Interrupt handler.
++ */
++static void
++ReEnableErrorInterrupts (void *arg)
++{
++    ELAN3_DEV     *dev = (ELAN3_DEV *) arg;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++
++    if ((dev->SchCntReg & LinkBoundaryScan) == 0)
++      ENABLE_INT_MASK (dev, INT_ErrorInterrupts);
++
++    PRINTF1 (DBG_DEVICE, DBG_INTR, "ReEnableErrorInterrupts: IntMask=%x\n", read_reg32 (dev, Exts.InterruptMask));
++
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++}
++
++void
++CheckForExcessiveErrorRate (ELAN3_DEV *dev)
++{
++    if (dev->ErrorTime == (lbolt/hz))
++    {
++      if (dev->ErrorsPerTick++ > 100)
++      {
++          PRINTF0 (DBG_DEVICE, DBG_INTR, "CheckForExcessiveErrorRate: too many links errors, disabling interrupt\n");
++
++          DISABLE_INT_MASK (dev, INT_ErrorInterrupts);
++
++          schedule_timer_fn (&dev->ErrorTimeoutId, ReEnableErrorInterrupts, (void *) dev, hz);
++      }
++    }
++    else
++    {
++      dev->ErrorTime     = (lbolt/hz);
++      dev->ErrorsPerTick = 0;
++    }
++}
++/*=======================================================================================*/
++/*
++ * Interrupt handler.
++ */
++static void
++HandlePciMemErr (ELAN3_DEV *dev)
++{
++    PRINTF0 (DBG_DEVICE, DBG_INTR, "HandlePciMemErr : masking out interrupt\n");
++    
++    ElanBusError (dev);
++    panic ("elan pci memory error\n");
++}
++
++static void
++HandleSDRamInterrupt (ELAN3_DEV *dev)
++{
++    E3_uint32     EccStatus0 = read_reg32 (dev, ECC_STATUS0);
++    E3_uint32     EccStatus1 = read_reg32 (dev, ECC_STATUS1);
++    unsigned long flags;
++
++    PRINTF5 (DBG_DEVICE, DBG_INTR, "elan: ECC error - Addr=%x UE=%x CE=%x ME=%x Syn=%x\n",
++           EccStatus0 & ECC_ADDR_MASK, EccStatus0 & ECC_UE_MASK, 
++           EccStatus0 & ECC_CE_MASK, EccStatus0 & ECC_ME_MASK, 
++           EccStatus1 & ECC_SYN_MASK);
++
++    if (EccStatus0 & (ECC_UE_MASK|ECC_CE_MASK))
++    {
++      printk ("elan%d: ECC memory error (Address=%08x Syndrome=%02x %s%s%s)\n",
++              dev->Instance, 
++              (EccStatus0 & ECC_ADDR_MASK), (EccStatus1 & ECC_SYN_MASK), 
++              (EccStatus0 & ECC_UE_MASK) ? "Uncorrectable "   : "",
++              (EccStatus0 & ECC_CE_MASK) ? "Correctable "     : "",
++              (EccStatus0 & ECC_ME_MASK) ? "Multiple Errors " : "");
++    }
++
++    if (EccStatus0 & ECC_UE_MASK)
++      panic ("elan: Uncorrectable ECC memory error");
++    if (EccStatus0 & ECC_CE_MASK)
++      BumpStat (dev, CorrectableErrors);
++    if (EccStatus0 & ECC_ME_MASK)
++      BumpStat (dev, MultipleErrors);
++
++    /*
++     * Clear the interrupt and reset the error flags.
++     * Note. Might loose an UE or CE if it occurs between reading the status and
++     *       clearing the interrupt. I don't think this matters very much as the
++     *          status reg will only be used to identify a bad simm.
++     */
++
++    spin_lock_irqsave (&dev->TlbLock, flags);
++    write_reg32 (dev, Cache_Control_Reg.ContReg, dev->Cache_Control_Reg | CLEAR_SDRAM_ERROR);
++    mmiob();
++    spin_unlock_irqrestore (&dev->TlbLock, flags);
++
++    CheckForExcessiveErrorRate (dev);
++}
++
++static int
++HandleEventInterrupt (ELAN3_DEV *dev, int nticks, unsigned long *flags)
++{
++    E3_uint32 Fptr  = dev->Event_Int_Queue_FPtr;
++    E3_uint32 Bptr  = read_reg32 (dev, Event_Int_Queue_BPtr);                                                 /* PCI read */
++    long      tlim  = lbolt + nticks;
++    long      count = 0;
++    ELAN3_CTXT *ctxt;
++
++    ASSERT (SPINLOCK_HELD (&dev->IntrLock));
++    ASSERT ((dev->InterruptMask & INT_EventInterrupt) == 0);
++          
++    while (Fptr != Bptr)
++    {
++      while (Fptr != Bptr)
++      {
++          E3_EventInt_BE  EvInt;
++          E3_uint32       Context;
++
++          /* If we're running in the interrupt handler and have seen a high
++           * rate of event interrupts then punt to the thread  - however on 
++           * Linux the elan interrupt handler can block the timer interrupt,
++           * and so lbolt (jiffies) is not incremented, hence we punt after
++           a number of loops instead */
++#if defined(LINUX)
++          if (in_interrupt() && ++count > eventint_punt_loops)
++              return (EAGAIN);
++#endif
++
++          if (nticks && ((int) (lbolt - tlim)) > 0)
++          {
++              PRINTF2 (DBG_DEVICE, DBG_INTR, "HandleEventInterrupt: Fptr %x Bptr %x punting to thread\n", Fptr, Bptr);
++              return (EAGAIN);
++          }
++
++          elan3_sdram_copyq_from_sdram (dev, Fptr, (void *) &EvInt, 8);                               /* PCI read */
++          
++          /* The context number is held in the top 16 bits of the EventContext */
++          Context = (EvInt.s.EventContext >> 16) & MAX_ROOT_CONTEXT_MASK;
++          
++          PRINTF2 (DBG_DEVICE, DBG_INTR, "HandleEventInterrupt: Context %d : Cookie %x\n", Context, EvInt.s.IntCookie);
++          
++          ctxt = ELAN3_DEV_CTX_TABLE(dev, Context);
++
++          /* Work out new fptr, and store it in the device, since we'll be dropping the IntrLock */
++          Fptr = E3_EVENT_INTQ_NEXT(Fptr);
++          dev->Event_Int_Queue_FPtr = Fptr;
++
++          if (ctxt == NULL)
++          {
++              PRINTF3 (DBG_DEVICE, DBG_INTR, "HandleEventInterrupt: Fptr %x Bptr %x context %d invalid\n",
++                       Fptr, Bptr, Context);
++              BumpStat (dev, InvalidContext);
++          }
++          else
++          {
++              BumpStat (dev, EventInterrupts);
++              
++              spin_unlock_irqrestore (&dev->IntrLock, *flags);
++              QueueEventInterrupt (ctxt, EvInt.s.IntCookie);
++              spin_lock_irqsave (&dev->IntrLock, *flags);
++          }
++          
++          /* Re-read the FPtr, since we've dropped the IntrLock */
++          Fptr = dev->Event_Int_Queue_FPtr;
++          
++          /* Store the new FPtr to the elan, this also clears the interrupt. */
++          write_reg32 (dev, Event_Int_Queue_FPtr, Fptr);                                      /* PCI write */
++
++          mmiob();
++      }
++
++      mb();
++      Bptr = read_reg32 (dev, Event_Int_Queue_BPtr);                                          /* PCI read */
++    }
++
++    return (ESUCCESS);
++}
++
++int
++SetLinkBoundaryScan (ELAN3_DEV *dev)
++{
++    int           res = ESUCCESS;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++    if ((dev->SchCntReg & LinkBoundaryScan) != 0)
++      res = EAGAIN;
++    else
++    {
++      PRINTF0 (DBG_DEVICE, DBG_BSCAN, "SetLinkBoundaryScan: setting link into boundary scan mode\n");
++
++      /*
++       * We're going to set the link into boundary scan mode,  so firstly
++       * set the inputters to discard everything.
++       */
++      if (dev->DiscardAllCount++ == 0)
++          SetSchedStatusRegister (dev, read_reg32 (dev, Exts.InterruptReg), NULL);
++
++      /*
++       * Now disable the error interrupts
++       */
++      DISABLE_INT_MASK (dev, INT_ErrorInterrupts);
++      
++      /*
++       * And set the link into boundary scan mode, and drive
++       * a reset token onto the link.
++       */
++      SET_SCHED_LINK_VALUE (dev, 1, LinkResetToken);
++    }
++
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++
++    return (res);
++}
++
++void
++ClearLinkBoundaryScan (ELAN3_DEV *dev)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++    if ((dev->SchCntReg & LinkBoundaryScan) != 0)
++    {
++      PRINTF0 (DBG_DEVICE, DBG_BSCAN, "ClearLinkBoundaryScan: taking link out of boundary scan mode\n");
++
++      /*
++       * Take the link out of boundary scan 
++       */
++      SET_SCHED_LINK_VALUE (dev, 0, 0);
++
++      /*
++       * Clear any link errors.
++       */
++      PULSE_SCHED_STATUS (dev, ClearLinkErrorInt);
++
++      /*
++       * Re-enable the error interrupts.
++       */
++      if (! timer_fn_queued(&dev->ErrorTimeoutId))
++          ENABLE_INT_MASK (dev, INT_ErrorInterrupts);
++
++      /*
++       * And stop the inputter from discarding all packets.
++       */
++      if (--dev->DiscardAllCount == 0)
++          SetSchedStatusRegister (dev, 0, NULL);
++    }
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++}
++
++int
++WriteBoundaryScanValue (ELAN3_DEV *dev, int value)
++{
++    int           res = 0;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++    if ((dev->SchCntReg & LinkBoundaryScan) != 0)
++    {
++      PRINTF1 (DBG_DEVICE, DBG_BSCAN, "WriteBoundaryScanValue: driving value 0x%x onto link\n", value);
++      SET_SCHED_LINK_VALUE (dev, 1, value);
++
++      res = read_reg32 (dev, Exts.LinkState);
++
++      PRINTF1 (DBG_DEVICE, DBG_BSCAN, "WriteBoundaryScanValue: return 0x%x\n", res);
++    }
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++
++    return (res);
++}
++
++int
++ReadBoundaryScanValue(ELAN3_DEV *dev, int link)
++{
++    int           res;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++    if ((dev->SchCntReg & LinkBoundaryScan) == 0)
++    {
++      PRINTF1 (DBG_DEVICE, DBG_BSCAN, "ReadBoundaryScanValue: set linkval 0x%x\n",  link);
++      SET_SCHED_LINK_VALUE (dev, 0, link);
++    }
++    res = read_reg32 (dev, Exts.LinkState);
++    PRINTF1 (DBG_DEVICE, DBG_BSCAN, "ReadBoundaryScanValue: return 0x%x\n", res);
++
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++
++    return (res);
++}
++
++static int
++ReadLinkVal (ELAN3_DEV *dev, int link)
++{
++    if ((dev->SchCntReg & LinkBoundaryScan) == 0)
++      SET_SCHED_LINK_VALUE (dev, 0, link);
++    
++    return (read_reg32 (dev, Exts.LinkState));
++}
++
++static void
++HandleLinkError (ELAN3_DEV *dev)
++{
++    E3_uint32 value = read_reg32 (dev, Exts.LinkErrorTypes);
++
++    PRINTF1 (DBG_DEVICE, DBG_LINKERR, "HandleLinkError: LinkErrorTypes %08x - clearing\n", value);
++    
++    if (value & LS_LockError)   BumpStat (dev, LockError);
++    if (value & LS_DeskewError) BumpStat (dev, DeskewError);
++    if (value & LS_PhaseError)  BumpStat (dev, PhaseError);
++    if (value & LS_DataError)   BumpStat (dev, DataError);
++    if (value & LS_FifoOvFlow0) BumpStat (dev, FifoOvFlow0);
++    if (value & LS_FifoOvFlow1) BumpStat (dev, FifoOvFlow1);
++
++    if (value & LS_DataError)
++      dev->Stats.LinkErrorValue = ReadLinkVal (dev, 12) | (ReadLinkVal (dev, 13) << 9);
++
++    PULSE_SCHED_STATUS (dev, ClearLinkErrorInt);
++
++    CheckForExcessiveErrorRate (dev);
++}
++
++static void
++HandleErrorInterrupt (ELAN3_DEV *dev, E3_uint32 Pend)
++{
++    if (Pend & INT_PciMemErr)
++      HandlePciMemErr (dev);
++    
++    if (Pend & INT_SDRamInt)
++      HandleSDRamInterrupt (dev);
++    
++    if (Pend & INT_LinkError)
++      HandleLinkError (dev);
++}
++      
++static void
++HandleAnyIProcTraps (ELAN3_DEV *dev, E3_uint32 Pend)
++{
++    E3_uint32      RestartBits = 0;
++    
++    if (Pend & INT_IProcCh0SysCntx)
++    {
++      HandleIProcTrap (dev, 0, Pend,
++                       dev->TAndQBase + offsetof (E3_TrapAndQueue, IProcSysCntx),
++                       dev->TAndQBase + offsetof (E3_TrapAndQueue, VCh0_C0_TrHead[0]),
++                       dev->TAndQBase + offsetof (E3_TrapAndQueue, VCh0_C0_TrData[0]));
++                       
++      RestartBits |= RestartCh0SysCntx;
++    }
++    
++    if (Pend & INT_IProcCh1SysCntx)
++    {
++      HandleIProcTrap (dev, 1, Pend,
++                       dev->TAndQBase + offsetof (E3_TrapAndQueue, IProcSysCntx),
++                       dev->TAndQBase + offsetof (E3_TrapAndQueue, VCh1_C0_TrHead[0]),
++                       dev->TAndQBase + offsetof (E3_TrapAndQueue, VCh1_C0_TrData[0]));
++                       
++      RestartBits |= RestartCh1SysCntx;
++    }
++
++    if (Pend & INT_IProcCh0NonSysCntx)
++    {
++      HandleIProcTrap (dev, 0, Pend,
++                       dev->TAndQBase + offsetof (E3_TrapAndQueue, IProcNonSysCntx),
++                       dev->TAndQBase + offsetof (E3_TrapAndQueue, VCh0_NonC0_TrHead[0]),
++                       dev->TAndQBase + offsetof (E3_TrapAndQueue, VCh0_NonC0_TrData[0]));
++
++      RestartBits |= RestartCh0NonSysCntx;
++    }
++    
++
++    if (Pend & INT_IProcCh1NonSysCntx)
++    {
++      HandleIProcTrap (dev, 1, Pend,
++                       dev->TAndQBase + offsetof (E3_TrapAndQueue, IProcNonSysCntx),
++                       dev->TAndQBase + offsetof (E3_TrapAndQueue, VCh1_NonC0_TrHead[0]),
++                       dev->TAndQBase + offsetof (E3_TrapAndQueue, VCh1_NonC0_TrData[0]));
++      RestartBits |= RestartCh1NonSysCntx;
++    }
++
++    PULSE_SCHED_STATUS (dev, RestartBits);
++}
++
++static void
++elan3_event_interrupt (ELAN3_DEV *dev)
++{
++    unsigned long flags;
++
++    kernel_thread_init("elan3_event_int");
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++    for (;;)
++    {
++      /* Make sure we never sleep with the EventInterrupt disabled */
++      if (! (dev->InterruptMask & INT_EventInterrupt))
++      {
++          if (HandleEventInterrupt (dev, eventint_resched_ticks, &flags) != ESUCCESS)
++              BumpStat (dev, EventRescheds);
++          
++          ENABLE_INT_MASK (dev, INT_EventInterrupt);
++      }
++
++      if (dev->ThreadsShouldStop)
++          break;
++
++      kcondvar_wait (&dev->IntrWait, &dev->IntrLock, &flags);
++    }
++    
++    dev->EventInterruptThreadStopped = 1;
++    kcondvar_wakeupall (&dev->IntrWait, &dev->IntrLock);
++
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++
++    kernel_thread_exit ();
++}
++
++int
++InterruptHandler (ELAN3_DEV *dev)
++{
++    E3_uint32     Mask;
++    E3_uint32     Pend;
++    E3_uint32     RestartBits;
++    int           deliverDProcTrap;
++    int                 deliverTProcTrap;
++    static long   lboltsave;
++    int           loop_count = 0; 
++    unsigned long flags;
++    int  tproc_delivered;
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++
++    BumpStat (dev, Interrupts);
++
++    Mask = dev->InterruptMask;
++    Pend = read_reg32 (dev, Exts.InterruptReg);                                               /* PCI read */
++
++    /* Save the lbolt so we know how long in do loop or in event handling */
++    lboltsave = lbolt;
++
++    if ((Pend & Mask) == INT_EventInterrupt)
++    {
++      DISABLE_INT_MASK (dev, INT_EventInterrupt);
++
++      if (HandleEventInterrupt (dev, eventint_punt_ticks, &flags) == ESUCCESS)
++          ENABLE_INT_MASK (dev, INT_EventInterrupt);
++      else
++      {
++          BumpStat (dev, EventPunts);
++
++          kcondvar_wakeupone (&dev->IntrWait, &dev->IntrLock);
++      }
++
++        if ((lbolt - lboltsave) > dev->Stats.LongestInterrupt)
++            dev->Stats.LongestInterrupt = (lbolt - lboltsave);
++      spin_unlock_irqrestore (&dev->IntrLock, flags);
++      return (ESUCCESS);
++    }
++
++    if ((Pend & Mask) == 0)
++    {
++      PRINTF3 (DBG_DEVICE, DBG_INTR, "InterruptHandler: Spurious Pend %x Mask %x SchedStatus %x\n", 
++               Pend, Mask, read_reg32 (dev, Exts.SchCntReg));
++
++        if ((lbolt - lboltsave) > dev->Stats.LongestInterrupt)
++            dev->Stats.LongestInterrupt = (lbolt - lboltsave);
++      spin_unlock_irqrestore (&dev->IntrLock, flags);
++      return (EFAIL);
++    }
++
++    PRINTF3 (DBG_DEVICE, DBG_INTR, "InterruptHandler: Pend %x Mask %08x SchedStatus %x\n", 
++           Pend, Mask, read_reg32 (dev, Exts.SchCntReg));
++
++    do {
++      loop_count++;
++      RestartBits = 0;
++
++      if (Pend & Mask & (INT_CProc | INT_ComQueue))
++          HandleCProcTrap (dev, Pend, &Mask);
++
++      tproc_delivered = 0;
++
++      if (Pend & Mask & INT_TProc) {
++          ELAN_REG_REC(Pend);
++          tproc_delivered = 1;
++          deliverTProcTrap = HandleTProcTrap (dev, &RestartBits);
++      }
++      else
++          deliverTProcTrap = 0;
++
++      if (Pend & Mask & INT_DProc)
++          deliverDProcTrap = HandleDProcTrap (dev, &RestartBits);
++      else
++          deliverDProcTrap = 0;
++
++      ASSERT ((RestartBits & RestartDProc) == 0 || (read_reg32 (dev, Exts.DProcStatus.Status) >> 29) == 4);
++      ASSERT ((RestartBits & RestartDProc) == 0 || elan3_sdram_readl (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProc.s.FSR.Status))      == 0);
++      ASSERT ((RestartBits & RestartDProc) == 0 || elan3_sdram_readl (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProcData0.s.FSR.Status)) == 0);
++      ASSERT ((RestartBits & RestartDProc) == 0 || elan3_sdram_readl (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProcData1.s.FSR.Status)) == 0);
++      ASSERT ((RestartBits & RestartDProc) == 0 || elan3_sdram_readl (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProcData2.s.FSR.Status)) == 0);
++      ASSERT ((RestartBits & RestartDProc) == 0 || elan3_sdram_readl (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProcData3.s.FSR.Status)) == 0);
++
++      PULSE_SCHED_STATUS (dev, RestartBits);          /* Restart any processors which had trapped. */
++      SET_INT_MASK (dev, Mask);                       /* And install the new interrupt mask */
++
++      if ((Pend & Mask & INT_TProc) && deliverTProcTrap)
++          DeliverTProcTrap (dev, dev->ThreadTrap, Pend);
++
++      if ((Pend & Mask & INT_DProc) && deliverDProcTrap)
++          DeliverDProcTrap (dev, dev->DmaTrap, Pend);
++
++      if (Pend & Mask & INT_Inputters)
++          HandleAnyIProcTraps (dev, Pend);
++      
++      if (Pend & Mask & INT_EventInterrupt)
++      {
++          DISABLE_INT_MASK (dev, INT_EventInterrupt);
++          
++          if (loop_count == 1 && HandleEventInterrupt (dev, eventint_punt_ticks, &flags) == ESUCCESS) /* always punt to the thread if we've */
++              ENABLE_INT_MASK (dev, INT_EventInterrupt);                                              /* been round the loop once */
++          else
++          {
++              BumpStat (dev, EventPunts);
++
++              kcondvar_wakeupone (&dev->IntrWait, &dev->IntrLock);
++          }
++      }
++
++      if (Pend & (INT_Halted | INT_Discarding))
++          ProcessHaltOperations (dev, Pend);
++
++      if (Pend & Mask & INT_ErrorInterrupts)
++          HandleErrorInterrupt (dev, Pend);
++
++      Mask = dev->InterruptMask;
++      Pend = read_reg32 (dev, Exts.InterruptReg);     /* PCI read */
++      
++      if (tproc_delivered)
++          ELAN_REG_REC(Pend);
++
++      PRINTF3 (DBG_DEVICE, DBG_INTR, "InterruptHandler: Pend %x Mask %08x SchedStatus %x\n", 
++               Pend, Mask, read_reg32 (dev, Exts.SchCntReg));
++    }  while ((Pend & Mask) != 0);
++
++    if ((lbolt - lboltsave) > dev->Stats.LongestInterrupt)
++        dev->Stats.LongestInterrupt = (lbolt - lboltsave);
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++
++    PRINTF2 (DBG_DEVICE, DBG_INTR, "InterruptHandler: lbolt is %lx; start lbolt is %lx\n", 
++           lbolt, lboltsave);
++
++    return (ESUCCESS);
++}
++
++void
++SetSchedStatusRegister (ELAN3_DEV *dev, E3_uint32 Pend, volatile E3_uint32 *Maskp)
++{
++    E3_uint32 HaltMask  = dev->HaltOperationsMask;
++    E3_uint32 Mask      = Maskp ? *Maskp : dev->InterruptMask;
++    E3_uint32 ClearBits = 0;
++    E3_uint32 SetBits   = 0;
++
++    PRINTF5 (DBG_DEVICE, DBG_INTR, "SetSchedStatusRegister: HaltOperationsMask=%x HaltAll=%d HaltDmaDequeue=%d HaltThread=%d DiscardAll=%d\n",
++           HaltMask, dev->HaltAllCount, dev->HaltDmaDequeueCount, dev->HaltThreadCount, dev->DiscardAllCount);
++
++    if (dev->FlushCommandCount)
++      SetBits |= FlushCommandQueues;
++    
++    if ((HaltMask & INT_DProcHalted) || dev->HaltAllCount)
++    {
++      SetBits |= HaltDmas | HaltDmaDequeue;
++      if (Pend & INT_DProcHalted)
++          Mask &= ~INT_DProcHalted;
++      else
++          Mask |= INT_DProcHalted;
++    }
++
++    if (dev->HaltDmaDequeueCount)
++    {
++      SetBits |= HaltDmaDequeue;
++      if (Pend & INT_DProcHalted)
++          Mask &= ~INT_DProcHalted;
++      else
++          Mask |= INT_DProcHalted;
++    }
++
++    if ((HaltMask & INT_TProcHalted) || dev->HaltAllCount || dev->HaltThreadCount)
++    {
++      SetBits |= HaltThread;
++      if (Pend & INT_TProcHalted)
++          Mask &= ~INT_TProcHalted;
++      else
++          Mask |= INT_TProcHalted;
++    }
++
++    if ((HaltMask & INT_DiscardingSysCntx) || dev->DiscardAllCount)
++    {
++      SetBits |= DiscardSysCntxIn;
++      if (Pend & INT_DiscardingSysCntx)
++          Mask &= ~INT_DiscardingSysCntx;
++      else
++          Mask |= INT_DiscardingSysCntx;
++    }
++
++    if ((HaltMask & INT_DiscardingNonSysCntx) || dev->DiscardNonContext0Count || dev->DiscardAllCount)
++    {
++      SetBits |= DiscardNonSysCntxIn;
++      if (Pend & INT_DiscardingNonSysCntx)
++          Mask &= ~INT_DiscardingNonSysCntx;
++      else
++          Mask |= INT_DiscardingNonSysCntx;
++    }
++
++    if (dev->HaltNonContext0Count)
++      SetBits |= StopNonSysCntxs;
++
++    ClearBits = SetBits ^ (FlushCommandQueues | HaltDmas | HaltDmaDequeue | HaltThread |
++                         DiscardSysCntxIn | DiscardNonSysCntxIn | StopNonSysCntxs);
++
++    PRINTF4 (DBG_DEVICE, DBG_INTR, "SetSchedStatusRegister: SetBits=%x InterruptMask=%x InterruptReg=%x Mask=%x\n",
++           SetBits, dev->InterruptMask, read_reg32 (dev, Exts.InterruptReg), Mask);
++
++    MODIFY_SCHED_STATUS (dev, SetBits, ClearBits);
++
++    if (Maskp)
++      *Maskp = Mask;                                          /* copyback new interrupt mask */
++    else
++      SET_INT_MASK(dev, Mask);
++}
++
++void
++FreeHaltOperation (ELAN3_DEV *dev, ELAN3_HALTOP *op)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->FreeHaltLock, flags);
++    op->Next = dev->FreeHaltOperations;
++    dev->FreeHaltOperations = op;
++    spin_unlock_irqrestore (&dev->FreeHaltLock, flags);
++}
++
++int
++ReserveHaltOperations (ELAN3_DEV *dev, int count, int cansleep)
++{
++    ELAN3_HALTOP   *op;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->FreeHaltLock, flags);
++    while ((dev->NumHaltOperations - dev->ReservedHaltOperations) < count)
++    {
++      spin_unlock_irqrestore (&dev->FreeHaltLock, flags);
++
++      KMEM_ZALLOC (op, ELAN3_HALTOP *, sizeof (ELAN3_HALTOP), cansleep);
++
++      if (op == NULL)
++          return (FALSE);
++
++      spin_lock_irqsave (&dev->FreeHaltLock, flags);
++
++      dev->NumHaltOperations++;
++
++      op->Next = dev->FreeHaltOperations;
++      dev->FreeHaltOperations = op;
++    }
++                  
++    dev->ReservedHaltOperations += count;
++    
++    spin_unlock_irqrestore (&dev->FreeHaltLock, flags);
++
++    return (TRUE);
++}
++
++void
++ReleaseHaltOperations (ELAN3_DEV *dev, int count)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->FreeHaltLock, flags);
++    dev->ReservedHaltOperations -= count;
++    spin_unlock_irqrestore (&dev->FreeHaltLock, flags);
++}
++
++void
++QueueHaltOperation (ELAN3_DEV *dev, E3_uint32 Pend, volatile E3_uint32 *Maskp, 
++                  E3_uint32 ReqMask, void (*Function)(ELAN3_DEV *, void *), void *Arguement)
++{
++    ELAN3_HALTOP *op;
++
++    ASSERT (SPINLOCK_HELD (&dev->IntrLock));
++    
++    spin_lock (&dev->FreeHaltLock);
++    op = dev->FreeHaltOperations;
++
++    ASSERT (op != NULL);
++
++    dev->FreeHaltOperations = op->Next;
++    spin_unlock (&dev->FreeHaltLock);
++
++    op->Mask      = ReqMask;
++    op->Function  = (void (*)(void *, void *))Function;
++    op->Arguement = Arguement;
++
++    dev->HaltOperationsMask |= ReqMask;                               /* Add our bits to the global bits needed. */
++    SetSchedStatusRegister (dev, Pend, Maskp);                        /* Set the control register and the interrupt mask */
++
++    /*
++     * If the condition is already satisfied, then SetSchedStatusRegister will
++     * have masked out the interrupt, so re-enable it now to take it straight
++     * away
++     */
++    if (Maskp == NULL)
++    {
++      if ((read_reg32 (dev, Exts.InterruptReg) & ReqMask) == ReqMask)
++          ENABLE_INT_MASK (dev, ReqMask);
++    }
++    else
++    {
++      if ((Pend & ReqMask) == ReqMask)
++          *Maskp |= ReqMask;
++    }
++
++    *dev->HaltOperationsTailpp = op;                          /* Queue at end of list, since ProcessHaltOperations */
++    dev->HaltOperationsTailpp = &op->Next;                    /* drops the IntrLock while running down the list */
++    op->Next = NULL;
++}
++                  
++void
++ProcessHaltOperations (ELAN3_DEV *dev, E3_uint32 Pend)
++{
++    E3_uint32     Mask;
++    ELAN3_HALTOP  *op;
++    ELAN3_HALTOP **prevp;
++    E3_uint32     haltMask;
++    ELAN3_HALTOP  *next;
++
++    PRINTF1 (DBG_DEVICE, DBG_INTR, "ProcessHaltOperations: Pend %x\n", Pend);
++
++    for (;;)
++    {
++      ELAN3_HALTOP  *head = NULL;
++      ELAN3_HALTOP **tailp = &head;
++
++      /*
++       * Generate a list of halt operations which can be called now.
++       */
++      for (haltMask = 0, prevp = &dev->HaltOperations; (op = *prevp) != NULL; )
++      {
++          if ((Pend & op->Mask) != op->Mask)
++          {
++              haltMask |= op->Mask;
++              prevp = &op->Next;
++          }
++          else
++          {
++              *prevp = op->Next;                              /* remove from list */
++              if (op->Next == NULL)
++                  dev->HaltOperationsTailpp = prevp;
++              
++              *tailp = op;                                    /* add to local list */
++              op->Next = NULL;
++              tailp = &op->Next;
++          }
++      }
++
++      if (head == NULL)                                       /* nothing to do, so update */
++      {                                                       /* the schedule status register */
++          dev->HaltOperationsMask = haltMask;                 /* and the interrupt mask */
++          SetSchedStatusRegister (dev, Pend, NULL);
++          return;
++      }
++
++      /*
++       * flush the command queues, before calling any operations
++       */
++      Mask = dev->InterruptMask;
++      
++      if (dev->FlushCommandCount++ == 0)
++          SetSchedStatusRegister (dev, Pend, &Mask);
++      
++      if ((read_reg32 (dev, ComQueueStatus) & ComQueueNotEmpty) != 0)
++      {
++          if (dev->HaltThreadCount++ == 0)
++              SetSchedStatusRegister (dev, Pend, &Mask);
++
++          CAPTURE_CPUS();
++
++          while ((read_reg32 (dev, ComQueueStatus) & ComQueueNotEmpty) != 0)
++              mb();
++
++          RELEASE_CPUS();
++                  
++          if (--dev->HaltThreadCount == 0)
++              SetSchedStatusRegister (dev, Pend, &Mask);
++      }
++              
++      if (read_reg32 (dev, Exts.InterruptReg) & INT_CProc)
++      {
++          PRINTF0 (DBG_DEVICE, DBG_INTR, "ProcessHaltOperations: command processor has trapped\n");
++          HandleCProcTrap (dev, Pend, &Mask);
++      }
++      
++      if (--dev->FlushCommandCount == 0)
++          SetSchedStatusRegister (dev, Pend, &Mask);
++      
++      PRINTF2 (DBG_DEVICE, DBG_INTR, "ProcessHaltOperations: interrupt mask %08x -> %08x\n", 
++               dev->InterruptMask, Mask);
++      
++      SET_INT_MASK (dev, Mask);
++      spin_unlock (&dev->IntrLock);
++
++      /*
++       * now process the list of operations
++       * we have
++       */
++      for (op = head; op != NULL; op = next)
++      {
++          next = op->Next;
++
++          op->Function (dev, op->Arguement);
++          
++          FreeHaltOperation (dev, op);
++      }
++
++      spin_lock (&dev->IntrLock);
++    }
++}
++
++int
++ComputePosition (ELAN_POSITION *pos, unsigned nodeId, unsigned numNodes, unsigned numDownLinksVal)
++{
++    int i, lvl, n;
++    char numDownLinks[ELAN_MAX_LEVELS];
++
++    if (nodeId >= numNodes)
++      return (EINVAL);
++
++    for (i = 0; i < ELAN_MAX_LEVELS; i++, numDownLinksVal >>= 4)
++      numDownLinks[i] = numDownLinksVal & 7;
++    
++    for (lvl = 0, n = numNodes; n > ((lvl % 3) == 2 ? 8 : 4) && lvl < ELAN_MAX_LEVELS; lvl++)
++    {
++      if (numDownLinks[lvl] == 0)
++          numDownLinks[lvl] = 4;
++      
++      if ((n % numDownLinks[lvl]) != 0)
++          return (EINVAL);
++      
++      n /= numDownLinks[lvl];
++    }
++
++    if (numDownLinks[lvl] == 0)
++      numDownLinks[lvl] = n;
++
++    if (numDownLinks[lvl] != n)
++      return (EINVAL);
++
++    for (i = 0; i <= lvl; i++)
++      pos->pos_arity[i] = numDownLinks[lvl - i];
++
++    pos->pos_nodes  = numNodes;
++    pos->pos_levels = lvl + 1;
++    pos->pos_nodeid = nodeId;
++    pos->pos_mode   = ELAN_POS_MODE_SWITCHED;
++
++    return (0);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan3/elandev_linux.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan3/elandev_linux.c  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan3/elandev_linux.c       2005-06-01 23:12:54.582441688 -0400
+@@ -0,0 +1,2302 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "$Id: elandev_linux.c,v 1.102.2.4 2004/12/20 16:55:17 mike Exp $"
++/*    $Source: /cvs/master/quadrics/elan3mod/elan3/os/elandev_linux.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <qsnet/kpte.h>
++
++#include <linux/config.h>
++#include <linux/mm.h>
++#include <linux/pci.h>
++#include <linux/reboot.h>
++#include <linux/notifier.h>
++
++#include <linux/init.h>
++#include <linux/module.h>
++
++#include <linux/pci.h>
++#include <linux/ptrack.h>
++
++#include <asm/uaccess.h>
++#include <asm/io.h>
++#include <asm/pgalloc.h>
++#include <asm/pgtable.h>
++
++#include <elan/devinfo.h>
++#include <elan/elanmod.h>
++
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elanvp.h>
++#include <elan3/elanio.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanctxt.h>
++#include <elan3/elandebug.h>
++#include <elan3/elansyscall.h>
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0)
++#error please use a 2.2 series kernel or newer
++#endif
++
++/* Minor numbers encoded as :
++ *   [5:0]    device number
++ *   [15:6]   function number
++ */
++#define ELAN3_DEVICE_MASK          0x3F
++
++#define ELAN3_MINOR_CONTROL      0
++#define ELAN3_MINOR_MEM          1
++#define ELAN3_MINOR_USER       2
++#define ELAN3_MINOR_SHIFT        6
++
++#define ELAN3_DEVICE(inode)   (MINOR(inode->i_rdev) & ELAN3_DEVICE_MASK)
++#define ELAN3_MINOR(inode)    (MINOR(inode->i_rdev) >> ELAN3_MINOR_SHIFT)
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
++#     define SetPageReserved(page)    set_bit(PG_reserved, &(page)->flags)
++#     define ClearPageReserved(page)  clear_bit(PG_reserved, &(page)->flags)
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,23)
++typedef void irqreturn_t;
++#endif
++#       define IRQ_NONE
++#       define IRQ_HANDLED
++#       define IRQ_RETVAL(x)
++#endif
++
++
++/*
++ * Function prototypes.
++ */
++static int    elanattach(int instance, struct pci_dev *pcidev);
++static int    elandetach(int instance);
++
++static int    elan3_open (struct inode *inode, struct file *file);
++static int    elan3_ioctl (struct inode *inode, struct file *file, 
++                           unsigned int cmd, unsigned long arg);
++static int    elan3_mmap (struct file *file, struct vm_area_struct *vm_area);
++static int    elan3_release (struct inode *inode, struct file *file);
++
++static int      elan3_reboot_event (struct notifier_block *self, unsigned long event, void *buffer);
++static int      elan3_panic_event (struct notifier_block *self, unsigned long event, void *buffer);
++
++static irqreturn_t InterruptHandlerWrapper(int irq, void *dev_id, struct pt_regs *regs);
++
++static int    ConfigurePci(ELAN3_DEV *dev);
++static int    ResetElan(ELAN3_DEV *dev, ioaddr_t intPalAddr);
++
++static void     elan3_shutdown_devices(int panicing);
++
++/*
++ * Globals. 
++ */
++static ELAN3_DEV      *elan3_devices[ELAN3_MAX_CONTROLLER];
++static int       NodeId = ELAN3_INVALID_NODE;
++static int       NumNodes;
++static int       DownLinks;
++static int       RandomRoutingDisabled;
++int              BackToBackMaster;
++int              BackToBackSlave;
++int              enable_sdram_writecombining;
++int            sdram_bank_limit;
++extern int       LwpNice;
++
++char *    elan_reg_rec_file [ELAN_REG_REC_MAX];
++int       elan_reg_rec_line [ELAN_REG_REC_MAX];
++long      elan_reg_rec_lbolt[ELAN_REG_REC_MAX];
++int       elan_reg_rec_cpu  [ELAN_REG_REC_MAX];
++E3_uint32 elan_reg_rec_reg  [ELAN_REG_REC_MAX];
++int       elan_reg_rec_index;
++
++MODULE_AUTHOR("Quadrics Ltd.");
++MODULE_DESCRIPTION("Elan3 Device Driver");
++
++MODULE_LICENSE("GPL");
++
++MODULE_PARM(NodeId,"i");
++MODULE_PARM(NumNodes,"i");
++MODULE_PARM(RandomRoutingDisabled,"i");
++MODULE_PARM(DownLinks,"i");
++MODULE_PARM(BackToBackMaster,"i");
++MODULE_PARM(BackToBackSlave,"i");
++MODULE_PARM(LwpNice, "i");
++MODULE_PARM(elan3_debug, "i");
++MODULE_PARM(elan3_debug_console, "i");
++MODULE_PARM(elan3_debug_buffer, "i");
++MODULE_PARM(elan3mmu_debug, "i");
++MODULE_PARM(sdram_bank_limit, "i");
++
++/* elan3/os/context.c */
++EXPORT_SYMBOL(elan3_alloc);
++EXPORT_SYMBOL(elan3_attach);
++EXPORT_SYMBOL(elan3_doattach);
++EXPORT_SYMBOL(elan3_free);
++EXPORT_SYMBOL(elan3_detach);
++EXPORT_SYMBOL(elan3_dodetach);
++EXPORT_SYMBOL(elan3_block_inputter);
++EXPORT_SYMBOL(CheckCommandQueueFlushed);
++
++/* elan3/os/sdram.c */
++EXPORT_SYMBOL(elan3_sdram_alloc);
++EXPORT_SYMBOL(elan3_sdram_free);
++EXPORT_SYMBOL(elan3_sdram_to_phys);
++EXPORT_SYMBOL(elan3_sdram_writeb);
++EXPORT_SYMBOL(elan3_sdram_writew);
++EXPORT_SYMBOL(elan3_sdram_writel);
++EXPORT_SYMBOL(elan3_sdram_writeq);
++EXPORT_SYMBOL(elan3_sdram_readb);
++EXPORT_SYMBOL(elan3_sdram_readw);
++EXPORT_SYMBOL(elan3_sdram_readl);
++EXPORT_SYMBOL(elan3_sdram_readq);
++EXPORT_SYMBOL(elan3_sdram_zerob_sdram);
++EXPORT_SYMBOL(elan3_sdram_zerow_sdram);
++EXPORT_SYMBOL(elan3_sdram_zerol_sdram);
++EXPORT_SYMBOL(elan3_sdram_zeroq_sdram);
++EXPORT_SYMBOL(elan3_sdram_copyb_to_sdram);
++EXPORT_SYMBOL(elan3_sdram_copyw_to_sdram);
++EXPORT_SYMBOL(elan3_sdram_copyl_to_sdram);
++EXPORT_SYMBOL(elan3_sdram_copyq_to_sdram);
++EXPORT_SYMBOL(elan3_sdram_copyb_from_sdram);
++EXPORT_SYMBOL(elan3_sdram_copyw_from_sdram);
++EXPORT_SYMBOL(elan3_sdram_copyl_from_sdram);
++EXPORT_SYMBOL(elan3_sdram_copyq_from_sdram);
++
++/* elan3/os/tproc.c */
++EXPORT_SYMBOL(DeliverTProcTrap);
++EXPORT_SYMBOL(HandleTProcTrap);
++EXPORT_SYMBOL(SaveThreadToStack);
++
++/* elan3/os/tprocinsts.c */
++EXPORT_SYMBOL(RollThreadToClose);
++
++/* elan3/os/iproc.c */
++EXPORT_SYMBOL(InspectIProcTrap);
++EXPORT_SYMBOL(IProcTrapString);
++EXPORT_SYMBOL(SimulateUnlockQueue);
++
++/* elan3/os/cproc.c */
++EXPORT_SYMBOL(HandleCProcTrap);
++
++/* elan3/os/route_table.c */
++EXPORT_SYMBOL(GenerateRoute);
++EXPORT_SYMBOL(LoadRoute);
++EXPORT_SYMBOL(InvalidateRoute);
++EXPORT_SYMBOL(ValidateRoute);
++EXPORT_SYMBOL(ClearRoute);
++EXPORT_SYMBOL(GenerateProbeRoute);
++EXPORT_SYMBOL(GenerateCheckRoute);
++
++/* elan3/os/elandev_generic.c */
++EXPORT_SYMBOL(elan3_debug);
++EXPORT_SYMBOL(QueueHaltOperation);
++EXPORT_SYMBOL(ReleaseHaltOperations);
++EXPORT_SYMBOL(ReserveHaltOperations);
++
++/* elan3/vm/elan3mmu_generic.c */
++EXPORT_SYMBOL(elan3mmu_pteload);
++EXPORT_SYMBOL(elan3mmu_unload);
++EXPORT_SYMBOL(elan3mmu_set_context_filter);
++EXPORT_SYMBOL(elan3mmu_reserve);
++EXPORT_SYMBOL(elan3mmu_attach);
++EXPORT_SYMBOL(elan3mmu_detach);
++EXPORT_SYMBOL(elan3mmu_release);
++/* elan3/vm/elan3mmu_linux.c */
++EXPORT_SYMBOL(elan3mmu_phys_to_pte);
++EXPORT_SYMBOL(elan3mmu_kernel_invalid_pte);
++
++/* elan3/os/elan3_debug.c */
++EXPORT_SYMBOL(elan3_debugf);
++
++/* elan3/os/minames.c */
++EXPORT_SYMBOL(MiToName);
++
++/* elan3/os/elandev_generic.c */
++EXPORT_SYMBOL(MapDeviceRegister);
++EXPORT_SYMBOL(UnmapDeviceRegister);
++
++EXPORT_SYMBOL(elan_reg_rec_lbolt);
++EXPORT_SYMBOL(elan_reg_rec_file);
++EXPORT_SYMBOL(elan_reg_rec_index);
++EXPORT_SYMBOL(elan_reg_rec_cpu);
++EXPORT_SYMBOL(elan_reg_rec_reg);
++EXPORT_SYMBOL(elan_reg_rec_line);
++
++/*
++ * Standard device entry points.
++ */
++#if defined(CONFIG_DUMP) || defined(CONFIG_DUMP_MODULE)
++
++#include <linux/dump.h>
++
++static int      elan3_dump_event (struct notifier_block *self, unsigned long event, void *buffer);
++
++static struct notifier_block elan3_dump_notifier = 
++{
++    notifier_call:    elan3_dump_event,
++    priority:         0,
++};
++
++static int
++elan3_dump_event (struct notifier_block *self, unsigned long event, void *buffer)
++{
++    if ( event == DUMP_BEGIN )
++      elan3_shutdown_devices (FALSE);
++
++    return (NOTIFY_DONE);
++}
++
++#endif
++
++static struct file_operations elan3_fops = {
++        ioctl:   elan3_ioctl,         /* ioctl */
++        mmap:    elan3_mmap,          /* mmap */
++        open:    elan3_open,          /* open */
++        release: elan3_release,               /* release */
++};
++
++static struct notifier_block elan3_reboot_notifier = 
++{
++    notifier_call:    elan3_reboot_event,
++    priority:         0,
++};
++
++static struct notifier_block elan3_panic_notifier = 
++{
++    notifier_call:    elan3_panic_event,
++    priority:         0,
++};
++
++ELAN3_DEV *
++elan3_device (int instance)
++{
++      if (instance < 0 || instance >= ELAN3_MAX_CONTROLLER)
++          return ((ELAN3_DEV *) NULL);
++      return elan3_devices[instance];
++}
++EXPORT_SYMBOL(elan3_device);
++
++/*
++ * Called at rmmod time.  elandetach() for each card + general cleanup.
++ */
++#ifdef MODULE
++static void __exit elan3_exit(void)
++{
++      int i;
++
++      printk("elan: preparing to remove module\n");
++
++#if defined(CONFIG_DUMP) || defined(CONFIG_DUMP_MODULE)
++      unregister_dump_notifier (&elan3_dump_notifier);
++#endif
++      unregister_reboot_notifier (&elan3_reboot_notifier);
++      notifier_chain_unregister (&panic_notifier_list, &elan3_panic_notifier);
++
++      /* call elandetach() for each device configured. */
++      for (i = 0; i < ELAN3_MAX_CONTROLLER; i++)
++              if (elan3_devices[i] != NULL)
++                      elandetach(i);
++
++      FinaliseNetworkErrorResolver();
++      elan3mmu_fini();
++
++      cookie_fini();
++      unregister_chrdev(ELAN3_MAJOR, ELAN3_NAME);
++
++      elan3_procfs_fini();
++
++      printk("elan: module removed\n");
++}
++
++/*
++ * Called at insmod time.  First we perform general driver initialization,
++ * then call elanattach() for each card.
++ */
++#ifdef MODULE
++static int __init elan3_init(void)
++#else
++__initfunc(int elan3_init(void))
++#endif
++{
++      int e;
++      int boards;
++      struct pci_dev *dev;
++      char revid;
++
++      elan_reg_rec_index=0;
++      {
++          int i;
++          for(i=0;i<ELAN_REG_REC_MAX;i++)
++              elan_reg_rec_file[i] = NULL;
++      }       
++
++      /* register major/minor num */
++      e = register_chrdev(ELAN3_MAJOR, ELAN3_NAME, &elan3_fops);
++      if (e < 0)
++              return e;
++
++      elan3_procfs_init ();
++
++      cookie_init();
++      elan3mmu_init();
++      InitialiseNetworkErrorResolver();
++
++      /* call elanattach() for each device found on PCI */
++      memset(elan3_devices, 0, sizeof(elan3_devices));
++      boards = 0;
++      for (dev = NULL; (dev = pci_find_device(PCI_VENDOR_ID_QUADRICS, PCI_DEVICE_ID_ELAN3, dev)) != NULL ;) 
++      {
++          pci_read_config_byte (dev, PCI_REVISION_ID, &revid);
++
++          if (revid == PCI_REVISION_ID_ELAN3_REVA)
++              printk ("elan at pci %s - RevA device not supported\n", dev->slot_name);
++          else
++          {
++              if (boards < ELAN3_MAX_CONTROLLER)
++                      /* Count successfully attached devices */ 
++                      boards += ((elanattach(boards, dev) == 0) ? 1 : 0);
++              else
++              {
++                  printk ("elan: max controllers = %d\n", ELAN3_MAX_CONTROLLER);
++                  break;
++              }
++          }
++      }
++#if defined(CONFIG_DUMP) || defined(CONFIG_DUMP_MODULE)
++      register_dump_notifier (&elan3_dump_notifier);
++#endif
++      register_reboot_notifier (&elan3_reboot_notifier);
++      notifier_chain_register (&panic_notifier_list, &elan3_panic_notifier);
++
++      return 0;
++}
++
++/* Declare the module init and exit functions */
++module_init(elan3_init);
++module_exit(elan3_exit);
++
++#endif
++
++static void
++elan3_shutdown_devices(int panicing)
++{
++    ELAN3_DEV *dev;
++    unsigned long flags;
++    register int i;
++
++    local_irq_save (flags);
++    for (i = 0; i < ELAN3_MAX_CONTROLLER; i++)
++    {
++      if ((dev = elan3_devices[i]) != NULL)
++      {
++          if (! panicing) spin_lock (&dev->IntrLock);
++          
++          printk(KERN_INFO "elan%d: forcing link into reset\n", dev->Instance);
++
++          /*
++           * We're going to set the link into boundary scan mode,  so firstly
++           * set the inputters to discard everything.
++           */
++          if (dev->DiscardAllCount++ == 0)
++              SetSchedStatusRegister (dev, read_reg32 (dev, Exts.InterruptReg), NULL);
++
++          dev->LinkShutdown = 1;
++          
++          /*
++           * Now disable the error interrupts
++           */
++          DISABLE_INT_MASK (dev, INT_ErrorInterrupts);
++          
++          /*
++           * And set the link into boundary scan mode, and drive
++           * a reset token onto the link.
++           */
++          SET_SCHED_LINK_VALUE (dev, 1, LinkResetToken);
++
++          if (! panicing) spin_unlock (&dev->IntrLock);
++      }
++    }
++    local_irq_restore (flags);
++}
++
++static int
++elan3_reboot_event (struct notifier_block *self, unsigned long event, void *buffer)
++{
++    if (! (event == SYS_RESTART || event == SYS_HALT || event == SYS_POWER_OFF))
++      return (NOTIFY_DONE);
++
++    elan3_shutdown_devices (FALSE);
++
++    return (NOTIFY_DONE);
++}
++
++static int
++elan3_panic_event (struct notifier_block *self, unsigned long event, void *buffer)
++{
++    elan3_shutdown_devices (TRUE);
++
++    return (NOTIFY_DONE);
++}
++
++#include <elan3/elan3ops.h>
++/*
++ * Called by init_module() for each card discovered on PCI.
++ */
++static int
++elanattach(int instance, struct pci_dev *pcidev)
++{
++      ELAN3_DEV *dev;
++      int ramSize;
++      int level;
++      ioaddr_t sdramAddr, cmdPortAddr, intPalAddr;
++      DeviceMappingHandle handle;
++
++      printk("elan%d: attach, irq=%d\n", instance, pcidev->irq);
++
++      /*
++       * Allocate the ELAN3_DEV structure.
++       */
++      KMEM_ZALLOC(dev, ELAN3_DEV *, sizeof(ELAN3_DEV), TRUE);
++      if (dev == NULL) {
++              printk ("elan%d: KMEM_ALLOC failed\n", instance);
++              return (-ENOMEM);
++      }
++      elan3_devices[instance] = dev;
++      dev->Osdep.pci = pcidev;
++
++      dev->Instance = instance;
++
++      /* Initialise the device information */
++      pci_read_config_word (pcidev, PCI_VENDOR_ID,   &dev->Devinfo.dev_vendor_id);
++      pci_read_config_word (pcidev, PCI_DEVICE_ID,   &dev->Devinfo.dev_device_id);
++      pci_read_config_byte (pcidev, PCI_REVISION_ID, &dev->Devinfo.dev_revision_id);
++
++      dev->Devinfo.dev_instance             = instance;
++      dev->Devinfo.dev_rail                 = instance;
++      dev->Devinfo.dev_driver_version       = 0;
++      dev->Devinfo.dev_num_down_links_value = DownLinks;
++
++      dev->Position.pos_mode                = ELAN_POS_UNKNOWN;
++      dev->Position.pos_random_disabled     = RandomRoutingDisabled;
++      
++      /*
++       * Set up PCI config regs.
++       */
++      if (ConfigurePci(dev) != ESUCCESS)
++          goto fail0;
++
++      /*
++       * Determine the PFnums of the SDRAM and command port
++       */
++      if (MapDeviceRegister(dev, ELAN3_BAR_SDRAM, &sdramAddr, 0, PAGESIZE, &handle) != ESUCCESS)
++          goto fail1;
++
++      DeviceRegisterSize(dev, ELAN3_BAR_SDRAM, &ramSize);
++      
++      dev->SdramPhysMask = ~((physaddr_t) ramSize - 1);
++      dev->SdramPhysBase = kmem_to_phys((void *) sdramAddr);
++
++      UnmapDeviceRegister (dev, &handle);
++
++#if defined(LINUX_ALPHA)
++      /*
++       * consider a physical address to be on the same pci bus
++       * as us if it's physical address is "close" to our sdram
++       * physical address.
++       * this is almost certainly incorrect for large memory (> 2Gb)
++       * i386 machines - and is only correct for alpha for 32 bit
++       * base address registers.
++       *
++       * Modified this to match the Tru64 driver value;
++       * i.e. PciPhysMask = 0xfffffffffffc0000
++       */
++#  define PCI_ADDR_MASK (0x7FFFFFFFl)
++
++      dev->PciPhysMask = ~PCI_ADDR_MASK;
++      dev->PciPhysBase = dev->SdramPhysBase & dev->PciPhysMask;
++#endif
++      /*
++       * Now reset the elan chip.
++       */
++      if (MapDeviceRegister(dev, ELAN3_BAR_REGISTERS, &dev->RegPtr, 0, 0, &dev->RegHandle) != ESUCCESS)
++          goto fail1;
++
++      if (MapDeviceRegister(dev, ELAN3_BAR_EBUS, &intPalAddr, ELAN3_EBUS_INTPAL_OFFSET, PAGESIZE,
++                            &handle) != ESUCCESS)
++          goto fail2;
++
++      ResetElan(dev, intPalAddr);     
++
++      UnmapDeviceRegister (dev, &handle);
++
++      /* 
++       * Initialise the device mutex's which must be accessible from the 
++       * interrupt handler.  
++       */
++      kcondvar_init (&dev->IntrWait);
++      spin_lock_init (&dev->IntrLock);
++      spin_lock_init (&dev->TlbLock);
++      spin_lock_init (&dev->CProcLock);
++      spin_lock_init (&dev->FreeHaltLock);
++      for(level=0; level<4; level++)
++          spin_lock_init (&dev->Level[level].PtblLock);
++      spin_lock_init (&dev->PtblGroupLock);
++
++      /*
++       * Add the interrupt handler,  
++       */
++      if (request_irq(dev->Osdep.pci->irq, InterruptHandlerWrapper, 
++          SA_SHIRQ, "elan3", dev) != 0) {
++              printk ("elan%d: request_irq failed\n", instance);
++              goto fail3;
++      }
++
++      if (MapDeviceRegister(dev, ELAN3_BAR_COMMAND_PORT, &cmdPortAddr, 0, PAGESIZE, &handle) != ESUCCESS)
++          goto fail4;
++      
++      if (InitialiseElan(dev, cmdPortAddr) == EFAIL) {
++              printk ("elan%d: InitialiseElan failed\n", instance);
++              UnmapDeviceRegister (dev, &handle);
++              goto fail4;
++      }
++      UnmapDeviceRegister (dev, &handle);
++
++      /* If our nodeid is defined, then set it now */
++      if (NodeId != ELAN3_INVALID_NODE && ComputePosition (&dev->Position, NodeId, NumNodes, DownLinks) == 0)
++      {
++          if (RandomRoutingDisabled & ((1 << (dev->Position.pos_levels-1))-1))
++              printk ("elan%d: NodeId=%d NodeLevel=%d NumNodes=%d (random routing disabled 0x%x)\n", 
++                      dev->Instance, dev->Position.pos_nodeid, dev->Position.pos_levels, dev->Position.pos_nodes, RandomRoutingDisabled);
++          else
++              printk ("elan%d: NodeId=%d NodeLevel=%d NumNodes=%d (random routing ok)\n",
++                      dev->Instance, dev->Position.pos_nodeid, dev->Position.pos_levels, dev->Position.pos_nodes);
++      }
++
++      if (BackToBackMaster || BackToBackSlave)
++      {
++          dev->Position.pos_mode     = ELAN_POS_MODE_BACKTOBACK;
++          dev->Position.pos_nodeid   = (BackToBackMaster == 0);
++          dev->Position.pos_nodes    = 2;
++          dev->Position.pos_levels   = 1;
++          dev->Position.pos_arity[0] = 2;
++
++          printk ("elan%d: back-to-back %s - elan node %d\n", dev->Instance,
++                  BackToBackMaster ? "master" : "slave", dev->Position.pos_nodeid);
++      }
++
++      elan3_procfs_device_init (dev);
++      
++      /* Success */
++      return (0);
++
++fail4:
++      free_irq(dev->Osdep.pci->irq, dev);
++
++fail3:
++      kcondvar_destroy (&dev->IntrWait);
++      spin_lock_destroy (&dev->IntrLock);
++      spin_lock_destroy (&dev->InfoLock);
++      spin_lock_destroy (&dev->TlbLock);
++      spin_lock_destroy (&dev->CProcLock);
++      spin_lock_destroy (&dev->FreeHaltLock);
++      spin_lock_destroy (&dev->Level1PtblLock);
++      spin_lock_destroy (&dev->Level2PtblLock);
++      spin_lock_destroy (&dev->Level3PtblLock);
++      spin_lock_destroy (&dev->PtblGroupLock);
++
++fail2:
++      UnmapDeviceRegister (dev, &dev->RegHandle);
++
++fail1:
++      pci_disable_device (dev->Osdep.pci);
++fail0:
++      KMEM_FREE(dev, sizeof(ELAN3_DEV));
++
++      elan3_devices[instance] = NULL;
++      
++      /* Failure */
++      return (-ENODEV);
++}
++
++/*
++ * Called by elan3_exit() for each board found on PCI.
++ */
++static int
++elandetach(int instance)
++{
++      ELAN3_DEV *dev = elan3_devices[instance];
++
++      printk("elan%d: detach\n", instance);
++
++      elan3_procfs_device_fini (dev);
++
++      FinaliseElan (dev);
++
++      UnmapDeviceRegister (dev, &dev->RegHandle);
++
++      free_irq(dev->Osdep.pci->irq, dev);
++
++      pci_disable_device(dev->Osdep.pci);
++
++      kcondvar_destroy (&dev->IntrWait);
++      spin_lock_destroy (&dev->IntrLock);
++      spin_lock_destroy (&dev->InfoLock);
++      spin_lock_destroy (&dev->TlbLock);
++      spin_lock_destroy (&dev->CProcLock);
++      spin_lock_destroy (&dev->FreeHaltLock);
++      spin_lock_destroy (&dev->Level1PtblLock);
++      spin_lock_destroy (&dev->Level2PtblLock);
++      spin_lock_destroy (&dev->Level3PtblLock);
++      spin_lock_destroy (&dev->PtblGroupLock);
++
++      KMEM_FREE(dev, sizeof(ELAN3_DEV));
++      elan3_devices[instance] = NULL; 
++
++      return 0;
++}
++
++/*
++ * generic ioctls - available on control and user devices.
++ */
++
++static int
++device_stats_ioctl (ELAN3_DEV *dev, unsigned long arg)
++{
++    ELAN3IO_STATS_STRUCT *args;
++
++    KMEM_ALLOC(args, ELAN3IO_STATS_STRUCT *, sizeof(ELAN3IO_STATS_STRUCT), TRUE);
++      
++    if (args == NULL)
++      return (-ENOMEM);
++
++    if (copy_from_user (args, (void *) arg, sizeof (ELAN3IO_STATS_STRUCT)))
++    {
++      KMEM_FREE(args, sizeof(ELAN3IO_STATS_STRUCT));
++      return (-EFAULT);
++    }
++
++    switch (args->which)
++    {
++    case ELAN3_SYS_STATS_DEVICE:
++      if (copy_to_user (args->ptr, &dev->Stats, sizeof (ELAN3_STATS)))
++      {
++          KMEM_FREE(args, sizeof(ELAN3IO_STATS_STRUCT));
++          return (-EFAULT);
++      }
++      KMEM_FREE(args, sizeof(ELAN3IO_STATS_STRUCT));
++      return (0);
++
++    case ELAN3_SYS_STATS_MMU:
++      if (copy_to_user (args->ptr, &elan3mmu_global_stats, sizeof (ELAN3MMU_GLOBAL_STATS)))
++      {
++          KMEM_FREE(args, sizeof(ELAN3IO_STATS_STRUCT));
++          return (-EFAULT);
++      }
++      KMEM_FREE(args, sizeof(ELAN3IO_STATS_STRUCT));
++      return (0);
++          
++    default:
++      KMEM_FREE(args, sizeof(ELAN3IO_STATS_STRUCT));
++      return (-EINVAL);
++    }
++}
++
++/*
++ * /dev/elan3/controlX - control device
++ *
++ */
++
++typedef struct control_private
++{
++    u_int             pr_boundary_scan;
++} CONTROL_PRIVATE;
++
++static int
++control_open (struct inode *inode, struct file *file)
++{
++    CONTROL_PRIVATE *pr;
++
++    KMEM_ALLOC(pr, CONTROL_PRIVATE *, sizeof (CONTROL_PRIVATE), TRUE);
++
++    if (pr == NULL)
++      return (-ENOMEM);
++
++    pr->pr_boundary_scan = 0;
++    
++    file->private_data = (void *) pr;
++
++    MOD_INC_USE_COUNT;
++
++    return (0);
++}
++
++static int
++control_release (struct inode *inode, struct file *file)
++{
++    ELAN3_DEV        *dev = elan3_devices[ELAN3_DEVICE(inode)];
++    CONTROL_PRIVATE *pr  = (CONTROL_PRIVATE *) file->private_data;
++
++    if (pr->pr_boundary_scan)
++      ClearLinkBoundaryScan(dev);
++
++    KMEM_FREE (pr, sizeof(CONTROL_PRIVATE));
++
++    MOD_DEC_USE_COUNT;
++    return (0);
++}
++
++static int
++control_ioctl (struct inode *inode, struct file *file, 
++             unsigned int cmd, unsigned long arg)
++{
++    ELAN3_DEV        *dev = elan3_devices[ELAN3_DEVICE(inode)];
++    CONTROL_PRIVATE *pr  = (CONTROL_PRIVATE *) file->private_data;
++    int                    res;
++
++    switch (cmd) 
++    {
++    case ELAN3IO_SET_BOUNDARY_SCAN:
++      if (SetLinkBoundaryScan (dev) == 0)
++          pr->pr_boundary_scan = 1;
++      return (0);
++
++    case ELAN3IO_CLEAR_BOUNDARY_SCAN:
++      if (pr->pr_boundary_scan == 0)
++          return (-EINVAL);
++
++      pr->pr_boundary_scan = 0;
++
++      ClearLinkBoundaryScan (dev);
++      return (0);
++
++    case ELAN3IO_READ_LINKVAL:
++    {
++      E3_uint32 val;
++
++      if (pr->pr_boundary_scan == 0)
++          return (-EINVAL);
++
++      if (copy_from_user(&val, (E3_uint32 *)arg, sizeof(E3_uint32)))
++          return (-EFAULT);
++
++      val = ReadBoundaryScanValue (dev, val);
++
++      if (copy_to_user((E3_uint32 *)arg, &val, sizeof(E3_uint32)))
++          return (-EFAULT);
++      return (0);
++    }
++      
++    case ELAN3IO_WRITE_LINKVAL:
++    {
++      E3_uint32 val;
++
++      if (pr->pr_boundary_scan == 0)
++          return (-EINVAL);
++
++      if (copy_from_user(&val, (E3_uint32 *)arg, sizeof(E3_uint32)))
++          return (-EFAULT);
++
++      val = WriteBoundaryScanValue (dev, val);
++
++      if (copy_to_user((E3_uint32 *)arg, &val, sizeof(E3_uint32)))
++          return (-EFAULT);
++      
++      return (0);
++    }
++
++    case ELAN3IO_SET_POSITION:
++    {
++      ELAN3IO_SET_POSITION_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN3IO_SET_POSITION_STRUCT)))
++          return (-EFAULT);
++      
++      if (ComputePosition (&dev->Position, args.nodeId, args.numNodes, dev->Devinfo.dev_num_down_links_value) != 0)
++          return (-EINVAL);
++
++      return (0);
++    }
++
++    case ELAN3IO_SET_DEBUG:
++    {
++      ELAN3IO_SET_DEBUG_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN3IO_SET_DEBUG_STRUCT)))
++          return (-EFAULT);
++
++      if (! strcmp (args.what, "elan3_debug"))
++          elan3_debug = args.value;
++      else if (! strcmp (args.what, "elan3_debug_console"))
++          elan3_debug_console = args.value;
++      else if (! strcmp (args.what, "elan3_debug_buffer"))
++          elan3_debug_buffer = args.value;
++      else if (! strcmp (args.what, "elan3_debug_ignore_dev"))
++          elan3_debug_ignore_dev = args.value;
++      else if (! strcmp (args.what, "elan3_debug_ignore_ctxt"))
++          elan3_debug_ignore_ctxt = args.value;
++      else if (! strcmp (args.what, "elan3mmu_debug"))
++          elan3mmu_debug = args.value;
++      
++      return (0);
++    }
++
++    case ELAN3IO_NETERR_SERVER:
++    {
++      ELAN3IO_NETERR_SERVER_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN3IO_NETERR_SERVER_STRUCT)))
++          return (-EFAULT);
++      
++      res = AddNeterrServerSyscall (args.elanid, args.addr, args.name, NULL);
++      return (set_errno (res));
++    }
++    
++    case ELAN3IO_NETERR_FIXUP:
++    {
++      NETERR_MSG *msg;
++
++      KMEM_ALLOC(msg, NETERR_MSG *, sizeof (NETERR_MSG), TRUE);
++
++      if (msg == NULL)
++          return (set_errno (ENOMEM));
++      
++      if (copy_from_user (msg, (void *) arg, sizeof (NETERR_MSG)))
++          res = EFAULT;
++      else
++          res = ExecuteNetworkErrorFixup (msg);
++
++      KMEM_FREE (msg, sizeof (NETERR_MSG));
++      return (set_errno (res));
++    }
++
++    case ELAN3IO_STATS:
++      return (device_stats_ioctl (dev, arg));
++
++    case ELAN3IO_GET_DEVINFO:
++    {
++      if (copy_to_user ((void *) arg, &dev->Devinfo, sizeof (ELAN_DEVINFO)))
++          return (-EFAULT);
++      return (0);
++    }
++
++    case ELAN3IO_GET_POSITION:
++    {
++      if (copy_to_user ((void *) arg, &dev->Position, sizeof (ELAN_POSITION)))
++          return (-EFAULT);
++      return (0);
++    }
++    default:
++      return (-EINVAL);
++    }
++}
++
++static int
++control_mmap (struct file *file, struct vm_area_struct *vma)
++{
++    ELAN3_DEV                *dev   = elan3_devices[ELAN3_DEVICE(file->f_dentry->d_inode)];
++    int               space = OFF_TO_SPACE(vma->vm_pgoff << PAGE_SHIFT);
++    int               off   = OFF_TO_OFFSET(vma->vm_pgoff << PAGE_SHIFT);
++    int               size;
++    ioaddr_t          addr;
++    DeviceMappingHandle handle;
++    physaddr_t                phys;
++    
++    if (space < ELAN3_BAR_SDRAM || space > ELAN3_BAR_EBUS)
++      return (-EINVAL);
++
++    if (off < 0 || DeviceRegisterSize (dev, space, &size) != ESUCCESS || off > size)
++      return (-EINVAL);
++
++    if (MapDeviceRegister(dev, space, &addr, off, PAGESIZE, &handle) != ESUCCESS)
++      return (-EINVAL);
++
++    phys = kmem_to_phys((caddr_t) addr);
++    UnmapDeviceRegister(dev, &handle);
++
++#ifdef NO_RMAP
++    if (remap_page_range(vma->vm_start, phys, vma->vm_end - vma->vm_start, vma->vm_page_prot))
++#else
++    if (remap_page_range(vma, vma->vm_start, phys, vma->vm_end - vma->vm_start, vma->vm_page_prot))
++#endif
++      return (-EAGAIN);
++
++    return (0);
++}
++
++/*
++ * /dev/elan3/sdramX - sdram access device
++ */
++typedef struct mem_page
++{
++    struct mem_page *pg_next;
++    sdramaddr_t      pg_addr;
++    u_long         pg_pgoff;
++    u_int          pg_ref;
++} MEM_PAGE;
++
++#define MEM_HASH_SIZE 32
++#define MEM_HASH(pgoff)       ((pgoff) & (MEM_HASH_SIZE-1))
++
++typedef struct mem_private
++{
++    ELAN3_DEV   *pr_dev;
++    MEM_PAGE   *pr_pages[MEM_HASH_SIZE];
++    spinlock_t  pr_lock;
++} MEM_PRIVATE;
++
++static void 
++mem_freepage (MEM_PRIVATE *pr, MEM_PAGE *pg)
++{
++    PRINTF (DBG_DEVICE, DBG_SEG, "mem_freepage: pr=%p pgoff=%lx pg=%p ref=%d\n", pr, pg->pg_pgoff, pg, pg->pg_ref);
++
++    elan3_sdram_free (pr->pr_dev, pg->pg_addr, PAGE_SIZE);
++    KMEM_FREE (pg, sizeof(MEM_PAGE));
++}
++
++static MEM_PAGE *
++mem_getpage (MEM_PRIVATE *pr, u_long pgoff, virtaddr_t addr)
++{
++    int       hashval = MEM_HASH (pgoff);
++    MEM_PAGE *npg = NULL;
++    MEM_PAGE *pg;
++
++    PRINTF (DBG_DEVICE, DBG_SEG, "mem_getpage: pr=%p pgoff=%lx addr=%lx\n", pr, pgoff, addr);
++    
++ again:
++    spin_lock (&pr->pr_lock);
++    for (pg = pr->pr_pages[hashval]; pg; pg = pg->pg_next)
++      if (pg->pg_pgoff == pgoff)
++          break;
++    
++    if (pg != NULL)
++    {
++      PRINTF (DBG_DEVICE, DBG_SEG, "mem_getpage: pr=%p pgoff=%lx addr=%lx -> found %p addr=%lx\n", pr, pgoff, addr, pg, pg->pg_addr);
++
++      pg->pg_ref++;
++      spin_unlock (&pr->pr_lock);
++
++      if (npg != NULL)                                        /* we'd raced and someone else had created */
++          mem_freepage (pr, npg);                             /* this page - so free of our new one*/
++      return (pg);
++    }
++    
++    if (npg != NULL)                                          /* didn't find the page, so inset the */
++    {                                                         /* new one we've just created */
++      npg->pg_next = pr->pr_pages[hashval];
++      pr->pr_pages[hashval] = npg;
++      
++      spin_unlock (&pr->pr_lock);
++      return (npg);
++    }
++    
++    spin_unlock (&pr->pr_lock);                                       /* drop spinlock before creating a new page */
++    
++    KMEM_ALLOC(npg, MEM_PAGE *, sizeof (MEM_PAGE), TRUE);
++
++    if (npg == NULL)
++      return (NULL);
++
++    if ((npg->pg_addr = elan3_sdram_alloc (pr->pr_dev, PAGE_SIZE)) == 0)
++    {
++      KMEM_FREE (npg, sizeof (MEM_PAGE));
++      return (NULL);
++    }
++
++    /* zero the page before returning it to the user */
++    elan3_sdram_zeroq_sdram (pr->pr_dev, npg->pg_addr, PAGE_SIZE);
++    
++    npg->pg_pgoff = pgoff;
++    npg->pg_ref   = 1;
++    
++    /* created a new page - so have to rescan before inserting it */
++    goto again;
++}
++
++static void
++mem_droppage (MEM_PRIVATE *pr, u_long pgoff, int dontfree)
++{
++    MEM_PAGE **ppg;
++    MEM_PAGE  *pg;
++
++    spin_lock (&pr->pr_lock);
++    for (ppg = &pr->pr_pages[MEM_HASH(pgoff)]; *ppg; ppg = &(*ppg)->pg_next)
++      if ((*ppg)->pg_pgoff == pgoff)
++          break;
++
++    pg = *ppg;
++
++    ASSERT (*ppg != NULL);
++    
++    PRINTF (DBG_DEVICE, DBG_SEG, "mem_droppage: pr=%p pgoff=%lx pg=%p ref=%d dontfree=%d\n", pr, pgoff, (*ppg), (*ppg)->pg_ref, dontfree);
++
++    if (--pg->pg_ref == 0 && !dontfree)
++    {
++      *ppg = pg->pg_next;
++
++      mem_freepage (pr, pg);
++    }
++
++    spin_unlock (&pr->pr_lock);
++}
++
++static int
++mem_open (struct inode *inode, struct file *file)
++{
++    ELAN3_DEV    *dev = elan3_devices[ELAN3_DEVICE(inode)];
++    MEM_PRIVATE *pr;
++    register int i;
++
++    KMEM_ALLOC(pr, MEM_PRIVATE *, sizeof (MEM_PRIVATE), TRUE);
++
++    if (pr == NULL)
++      return (-ENOMEM);
++
++    spin_lock_init (&pr->pr_lock);
++    pr->pr_dev = dev;
++    for (i = 0; i < MEM_HASH_SIZE; i++)
++      pr->pr_pages[i] = NULL;
++
++    file->private_data = (void *) pr;
++    
++    MOD_INC_USE_COUNT;
++    return (0);
++}
++
++static int
++mem_release (struct inode *node, struct file *file)
++{
++    MEM_PRIVATE *pr = (MEM_PRIVATE *) file->private_data;
++    MEM_PAGE    *pg, *next;
++    int          i;
++
++    /* free off any pages that we'd allocated */
++    spin_lock (&pr->pr_lock);
++    for (i = 0; i < MEM_HASH_SIZE; i++)
++    {
++      for (pg = pr->pr_pages[i]; pg; pg = next)
++      {
++          next = pg->pg_next;
++          mem_freepage (pr, pg);
++      }
++    }
++    spin_unlock (&pr->pr_lock);
++
++    KMEM_FREE (pr, sizeof (MEM_PRIVATE));
++
++    MOD_DEC_USE_COUNT;
++    return (0);
++}
++
++static int
++mem_ioctl (struct inode *inode, struct file *file, 
++                unsigned int cmd, unsigned long arg)
++{
++    return (-EINVAL);
++}
++
++static void mem_vma_open(struct vm_area_struct *vma)
++{
++    MEM_PRIVATE   *pr = (MEM_PRIVATE *) vma->vm_private_data;
++    unsigned long addr;
++    unsigned long pgoff;
++
++    PRINTF (DBG_DEVICE, DBG_SEG, "mem_vma_open: vm_mm=%p start=%lx end=%lx pgoff=%lx file=%p\n",
++          vma->vm_mm, vma->vm_start, vma->vm_end, vma->vm_pgoff, vma->vm_file);
++
++    preemptable_start {
++      for (addr = vma->vm_start, pgoff = vma->vm_pgoff; addr < vma->vm_end; addr += PAGE_SIZE, pgoff++) {
++          mem_getpage (pr, pgoff, addr);
++          preemptable_check();
++      }
++    } preemptable_end;
++}
++
++static void mem_vma_close(struct vm_area_struct *vma)
++{
++    MEM_PRIVATE  *pr  = (MEM_PRIVATE *) vma->vm_private_data;
++    unsigned long addr;
++    unsigned long pgoff;
++
++    PRINTF (DBG_DEVICE, DBG_SEG, "mem_vma_close: vm_mm=%p start=%lx end=%lx pgoff=%lx file=%p\n",
++          vma->vm_mm, vma->vm_start, vma->vm_end, vma->vm_pgoff, vma->vm_file);
++
++    /* NOTE: the call to close may not have the same vm_start/vm_end values as 
++     *       were passed into mmap()/open() - since if an partial unmap had occured
++     *       then the vma could have been shrunk or even split.
++     *
++     *       if a the vma is split then an vma_open() will be called for the top
++     *       portion - thus causing the reference counts to become incorrect.
++     *
++     * We drop the reference to any pages we're notified about - so they get freed
++     * earlier than when the device is finally released.
++     */
++    for (pgoff = vma->vm_pgoff, addr = vma->vm_start; addr < vma->vm_end; addr += PAGE_SIZE, pgoff++)
++      mem_droppage (pr, pgoff, 0);
++}
++
++static struct vm_operations_struct mem_vm_ops = {
++    open:             mem_vma_open,
++    close:            mem_vma_close,
++};
++
++static int
++mem_mmap (struct file *file, struct vm_area_struct *vma)
++{
++    MEM_PRIVATE  *pr = (MEM_PRIVATE *) file->private_data;
++    MEM_PAGE     *pg;
++    unsigned long addr;
++    unsigned long pgoff;
++
++    PRINTF (DBG_DEVICE, DBG_SEG, "mem_mmap: vm_mm=%p start=%lx end=%lx pgoff=%lx prot=%lx file=%p\n",
++          vma->vm_mm, vma->vm_start, vma->vm_end, vma->vm_pgoff, vma->vm_page_prot.pgprot , file);
++
++    preemptable_start {
++      for (addr = vma->vm_start, pgoff = vma->vm_pgoff; addr < vma->vm_end; addr += PAGE_SIZE, pgoff++)
++      {
++          if ((pg = mem_getpage (pr, pgoff, addr)) == NULL)
++              goto failed;
++          
++#ifdef LINUX_SPARC
++          pgprot_val(vma->vm_page_prot) &= ~(_PAGE_CACHE);
++          pgprot_val(vma->vm_page_prot) |= _PAGE_IE;
++#elif defined(pgprot_noncached)
++          vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
++#endif
++          
++#if defined(__ia64__)
++          if (enable_sdram_writecombining)
++              vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
++#endif
++          PRINTF (DBG_DEVICE, DBG_SEG, "mem_mmap: addr %lx -> pg=%p addr=%lx phys=%lx flags=%lx prot=%lx\n",
++                  addr, pg, pg->pg_addr, elan3_sdram_to_phys (pr->pr_dev, pg->pg_addr), vma->vm_flags, vma->vm_page_prot.pgprot);
++          
++#ifdef NO_RMAP
++          if (remap_page_range (addr, elan3_sdram_to_phys (pr->pr_dev, pg->pg_addr), PAGE_SIZE, vma->vm_page_prot))
++#else
++          if (remap_page_range (vma, addr, elan3_sdram_to_phys (pr->pr_dev, pg->pg_addr), PAGE_SIZE, vma->vm_page_prot))
++#endif
++          {
++              mem_droppage (pr, pgoff, 0);                    /* drop our reference to this page */
++              goto failed;
++          }
++
++          preemptable_check();
++      }
++    } preemptable_end;
++
++    /* Don't try to swap out Elan SDRAM pages.. */
++    vma->vm_flags |= VM_RESERVED;
++    
++    /*
++     * Don't dump SDRAM pages to a core file 
++     * (Pity I would really like to do this but it crashes in elf_core_dump() as
++     * it can only handle pages that are in the mem_map area (addy 11/01/2002))
++     */
++    vma->vm_flags |= VM_IO;
++
++    vma->vm_ops          = &mem_vm_ops;
++    vma->vm_file         = file;
++    vma->vm_private_data = (void *) pr;
++
++    return (0);
++
++ failed:
++    PRINTF (DBG_DEVICE, DBG_SEG, "mem_mmap: failed\n");
++
++    /* free of any pages we've already allocated/referenced */
++    while ((--pgoff) >= vma->vm_pgoff)
++      mem_droppage (pr, pgoff, 0);
++
++    return (-ENOMEM);
++}
++
++/*
++ * /dev/elan3/userX - control device
++ *
++ * "user_private" can be referenced from a number of places
++ *   1) the "file" structure.
++ *   2) the "mm" coproc ops
++ *   3) the "mmap" of the command port.
++ *
++ */
++typedef struct user_private
++{
++    spinlock_t        pr_lock;
++    atomic_t        pr_mappings;
++    atomic_t          pr_ref;
++    ELAN3_CTXT        *pr_ctxt;
++    struct mm_struct *pr_mm;
++    coproc_ops_t      pr_coproc;
++} USER_PRIVATE;
++
++static void
++user_free (USER_PRIVATE *pr)
++{
++    /* Have to unreserve the FlagPage or else we leak memory like a sieve! */
++    ClearPageReserved(pte_page(*find_pte_kernel((unsigned long) pr->pr_ctxt->FlagPage)));
++
++    elan3_detach(pr->pr_ctxt);
++    elan3_free (pr->pr_ctxt);
++
++    KMEM_FREE (pr, sizeof(USER_PRIVATE));
++
++    MOD_DEC_USE_COUNT;
++}
++
++static void
++user_coproc_release (void *arg, struct mm_struct *mm)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) arg;
++
++    PRINTF3 (pr->pr_ctxt, DBG_SEG, "user_coproc_release: ctxt=%p pr=%p ref=%d\n",
++           pr->pr_ctxt, pr, atomic_read (&pr->pr_ref));
++
++    elan3mmu_pte_ctxt_unload (pr->pr_ctxt->Elan3mmu);
++
++    pr->pr_mm = NULL;
++
++    if (atomic_dec_and_test (&pr->pr_ref))
++      user_free (pr);
++}
++
++static void
++user_coproc_sync_range (void *arg, struct mm_struct *mm, unsigned long start, unsigned long end)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) arg;
++
++    PRINTF2 (pr->pr_ctxt, DBG_SEG, "user_coproc_sync_range: start=%lx end=%lx\n", start, end);
++
++    ASSERT(start <= end);
++
++    elan3mmu_pte_range_unload(pr->pr_ctxt->Elan3mmu, mm, (caddr_t) start, end-start);
++}
++
++static void
++user_coproc_invalidate_range (void *arg, struct mm_struct *mm, unsigned long start, unsigned long end)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) arg;
++
++    PRINTF2 (pr->pr_ctxt, DBG_SEG, "user_coproc_invalidate_range: start=%lx end=%lx\n", start, end);
++
++    ASSERT(start <= end);
++
++    elan3mmu_pte_range_unload(pr->pr_ctxt->Elan3mmu, mm, (caddr_t) start, end-start);
++}
++
++static void
++user_coproc_update_range (void *arg, struct mm_struct *mm, unsigned long start, unsigned long end)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) arg;
++
++    ASSERT(start <= end && ((start & PAGEOFFSET) == 0) && ((end & PAGEOFFSET) == 0));
++
++    PRINTF2 (pr->pr_ctxt, DBG_SEG, "user_coproc_update_range: start=%lx end=%lx\n", start, end);
++
++    elan3mmu_pte_range_update (pr->pr_ctxt->Elan3mmu, mm,(caddr_t) start, end-start);
++}
++
++static void
++user_coproc_change_protection (void *arg, struct mm_struct *mm, unsigned long start, unsigned long end, pgprot_t newprot)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) arg;
++
++    PRINTF2 (pr->pr_ctxt, DBG_SEG, "user_coproc_change_protection: start=%lx end=%lx\n", start, end);
++
++    ASSERT(start <= end);
++
++    elan3mmu_pte_range_unload(pr->pr_ctxt->Elan3mmu, mm, (caddr_t) start, end-start);
++}
++
++static void
++user_coproc_sync_page (void *arg, struct vm_area_struct *vma, unsigned long addr)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) arg;
++
++    PRINTF1 (pr->pr_ctxt, DBG_SEG, "user_coproc_sync_page: addr=%lx\n", addr);
++
++    elan3mmu_pte_range_unload(pr->pr_ctxt->Elan3mmu, vma->vm_mm, (caddr_t) (addr & PAGE_MASK), PAGE_SIZE);
++}
++
++static void
++user_coproc_invalidate_page (void *arg, struct vm_area_struct *vma, unsigned long addr)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) arg;
++    
++    PRINTF1 (pr->pr_ctxt, DBG_SEG, "user_coproc_invalidate_page: addr=%lx\n", addr);
++
++    elan3mmu_pte_range_unload(pr->pr_ctxt->Elan3mmu, vma->vm_mm, (caddr_t) (addr & PAGE_MASK), PAGE_SIZE);
++}
++
++static void
++user_coproc_update_page (void *arg, struct vm_area_struct *vma, unsigned long addr)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) arg;
++
++    PRINTF1 (pr->pr_ctxt, DBG_SEG, "user_coproc_update_page: addr=%lx\n", addr);
++
++    elan3mmu_pte_range_update (pr->pr_ctxt->Elan3mmu,vma->vm_mm, (caddr_t) (addr & PAGE_MASK), PAGE_SIZE);
++}
++
++int
++user_ptrack_handler (void *arg, int phase, struct task_struct *child)
++{
++    USER_PRIVATE *pr   = (USER_PRIVATE *) arg;
++    ELAN3_CTXT    *ctxt = pr->pr_ctxt;
++
++    PRINTF5 (pr->pr_ctxt, DBG_FN, "user_ptrack_handler: ctxt=%p pr=%p ref=%d phase %d mm->ref %d\n", 
++           pr->pr_ctxt, pr, atomic_read (&pr->pr_ref), phase, atomic_read (&current->mm->mm_count));
++
++    if (phase == PTRACK_PHASE_EXIT)
++    {
++      /* this will force the helper thread to exit */
++      elan3_swapout (ctxt, CTXT_EXITING);
++      
++      if (atomic_dec_and_test (&pr->pr_ref))
++          user_free (pr);
++    }  
++    return PTRACK_FINISHED;
++}
++
++static int
++user_open (struct inode *inode, struct file *file)
++{
++    ELAN3_DEV     *dev = elan3_devices[ELAN3_DEVICE(inode)];
++    USER_PRIVATE *pr;
++    ELAN3_CTXT    *ctxt;
++
++    if (dev == NULL)
++      return (-ENXIO);
++
++    KMEM_ALLOC(pr, USER_PRIVATE *, sizeof (USER_PRIVATE), TRUE);
++
++    if (pr == NULL)
++      return (-ENOMEM);
++    
++    if ((ctxt = elan3_alloc (dev, 0)) == NULL)
++    {
++      KMEM_FREE (pr, sizeof (USER_PRIVATE));
++      return (-ENOMEM);
++    }
++
++    if (sys_init (ctxt) == NULL)
++    {
++      elan3_detach(ctxt);
++      elan3_free (ctxt);
++      KMEM_FREE (pr, sizeof (USER_PRIVATE));
++      return (-ENOMEM);
++    }
++
++    /* initialise refcnt to 3 - one for "file", one for XA handler, one for the coproc ops */
++    atomic_set (&pr->pr_ref, 3);
++
++    atomic_set (&pr->pr_mappings, 0);
++    spin_lock_init (&pr->pr_lock);
++
++    pr->pr_ctxt = ctxt;
++    pr->pr_mm   = current->mm;
++
++    /* register an ptrack handler to force the helper thread to exit when we do */
++    if (ptrack_register (user_ptrack_handler, pr) < 0)
++    {
++      elan3_detach(ctxt);
++      elan3_free (ctxt);
++      KMEM_FREE (pr, sizeof (USER_PRIVATE));
++      return (-ENOMEM);
++    }
++
++    /* register a coproc callback to notify us of translation changes */
++    
++    pr->pr_coproc.arg               = (void *) pr;
++    pr->pr_coproc.release           = user_coproc_release;
++    pr->pr_coproc.sync_range        = user_coproc_sync_range;
++    pr->pr_coproc.invalidate_range  = user_coproc_invalidate_range;
++    pr->pr_coproc.update_range      = user_coproc_update_range;
++    pr->pr_coproc.change_protection = user_coproc_change_protection;
++    pr->pr_coproc.sync_page         = user_coproc_sync_page;
++    pr->pr_coproc.invalidate_page   = user_coproc_invalidate_page;
++    pr->pr_coproc.update_page       = user_coproc_update_page;
++    
++    spin_lock (&current->mm->page_table_lock);
++    register_coproc_ops (current->mm, &pr->pr_coproc);
++    spin_unlock (&current->mm->page_table_lock);
++
++    file->private_data = (void *) pr;
++
++    PRINTF2 (pr->pr_ctxt, DBG_FN, "user_open: done ctxt=%p pr=%p\n", ctxt, pr);
++
++    MOD_INC_USE_COUNT;
++    return (0);
++}
++
++static int
++user_release (struct inode *inode, struct file *file)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) file->private_data;
++    
++    PRINTF3 (pr->pr_ctxt, DBG_FN, "user_release: ctxt=%p pr=%p ref=%d\n", pr->pr_ctxt, pr,
++           atomic_read (&pr->pr_ref));
++
++    if (atomic_dec_and_test (&pr->pr_ref))
++      user_free (pr);
++
++    return (0);
++}
++
++static int
++user_ioctl (struct inode *inode, struct file *file, 
++          unsigned int cmd, unsigned long arg)
++{
++    USER_PRIVATE *pr   = (USER_PRIVATE *) file->private_data;
++    ELAN3_CTXT    *ctxt = pr->pr_ctxt;
++    SYS_CTXT     *sctx = (SYS_CTXT *) ctxt->Private;
++    int           res  = 0;
++
++    if (current->mm != pr->pr_mm)
++      return (-EINVAL);
++    
++    PRINTF4 (ctxt, DBG_FN, "user_ioctl: ctxt=%p cmd=%x(%d) arg=%lx\n", ctxt, cmd, _IOC_NR(cmd), arg);
++
++    switch (cmd)
++    {
++    case ELAN3IO_FREE:
++      if (atomic_read (&pr->pr_mappings) > 0)
++          return (-EINVAL);
++      
++      spin_lock (&current->mm->page_table_lock);
++      if (pr->pr_mm != current->mm)
++          spin_unlock (&current->mm->page_table_lock);
++      else
++      {
++          unregister_coproc_ops (current->mm, &pr->pr_coproc);
++          spin_unlock (&current->mm->page_table_lock);
++
++          user_coproc_release (pr, current->mm);
++      }
++
++      if (ptrack_registered (user_ptrack_handler, pr))
++      {
++          ptrack_deregister (user_ptrack_handler, pr);
++          user_ptrack_handler (pr, PTRACK_PHASE_EXIT, NULL);
++      }
++      break;
++      
++    case ELAN3IO_ATTACH:
++    {
++      ELAN_CAPABILITY *cap;
++
++      KMEM_ALLOC(cap, ELAN_CAPABILITY *, sizeof (ELAN_CAPABILITY), TRUE);
++
++      if (cap == NULL)
++          return (set_errno (EFAULT));
++
++      if (copy_from_user (cap, (void *) arg, sizeof (ELAN_CAPABILITY)))
++          res = EFAULT;
++      else
++      {
++          if ((res = elan3_attach (ctxt, cap)) == 0)
++          {
++              if (copy_to_user ((void *) arg, cap, sizeof (ELAN_CAPABILITY)))
++              {
++                  elan3_detach (ctxt);
++                  res = EFAULT;
++              }
++          }
++      }
++      KMEM_FREE (cap, sizeof(ELAN_CAPABILITY));
++      break;
++    }
++    
++    case ELAN3IO_DETACH:
++      spin_lock (&pr->pr_lock);
++      if (atomic_read (&pr->pr_mappings) > 0)
++          res = EINVAL;
++      else
++          elan3_detach (ctxt);
++      spin_unlock (&pr->pr_lock);
++      break;
++
++    case ELAN3IO_ADDVP:
++    {
++      ELAN3IO_ADDVP_STRUCT *args;
++
++      KMEM_ALLOC(args, ELAN3IO_ADDVP_STRUCT *, sizeof (ELAN3IO_ADDVP_STRUCT), TRUE);
++
++      if (args == NULL)
++          return (set_errno (ENOMEM));
++      
++      if (copy_from_user (args, (void *) arg, sizeof (ELAN3IO_ADDVP_STRUCT)))
++          res = EFAULT;
++      else
++      {
++          if ( (res=elan3_addvp (ctxt, args->process, &args->capability)) != 0)
++              PRINTF0 (ctxt, DBG_FN, "ELAN3IO_ADDVP elan3_addvp failed \n");  
++      }
++
++      KMEM_FREE (args, sizeof (ELAN3IO_ADDVP_STRUCT));
++      break;
++    }
++
++    case ELAN3IO_REMOVEVP:
++      res = elan3_removevp (ctxt, arg);
++      break;
++      
++    case ELAN3IO_BCASTVP:
++    {
++      ELAN3IO_BCASTVP_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN3IO_BCASTVP_STRUCT)))
++          return (-EFAULT);
++      
++      res = elan3_addbcastvp (ctxt, args.process, args.lowvp, args.highvp);
++      break;
++    }
++
++    case ELAN3IO_LOAD_ROUTE:
++    {
++      ELAN3IO_LOAD_ROUTE_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN3IO_LOAD_ROUTE_STRUCT)))
++          return (-EFAULT);
++      
++      res = elan3_load_route (ctxt, args.process, args.flits);
++      break;
++    }
++
++    case ELAN3IO_CHECK_ROUTE:
++    {
++      ELAN3IO_CHECK_ROUTE_STRUCT args;
++
++      args.routeError = 0;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN3IO_LOAD_ROUTE_STRUCT)))
++          return (-EFAULT);
++      
++      if ((res = elan3_check_route (ctxt, args.process, args.flits, & args.routeError)) ==  ESUCCESS)
++      {
++          if (copy_to_user ( (void *) arg, &args,sizeof (ELAN3IO_LOAD_ROUTE_STRUCT)))
++              return (-EFAULT);
++      }
++      break;
++    }
++
++    case ELAN3IO_PROCESS_2_LOCATION:
++    {
++      ELAN3IO_PROCESS_2_LOCATION_STRUCT args;
++      ELAN_LOCATION                    loc;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN3IO_PROCESS_2_LOCATION_STRUCT)))
++          return (-EFAULT);
++
++      krwlock_write (&ctxt->VpLock);
++      loc = ProcessToLocation (ctxt, NULL, args.process , NULL);
++      krwlock_done (&ctxt->VpLock);
++
++      args.loc = loc;
++
++      if (copy_to_user ( (void *) arg, &args,sizeof (ELAN3IO_PROCESS_2_LOCATION_STRUCT)))
++          return (-EFAULT);
++
++      break;
++    }
++
++    case ELAN3IO_GET_ROUTE:
++    {
++      ELAN3IO_GET_ROUTE_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN3IO_GET_ROUTE_STRUCT)))
++          return (-EFAULT);
++      
++      if ((res = elan3_get_route (ctxt, args.process, args.flits)) ==  ESUCCESS)
++      {
++          if (copy_to_user ( (void *) arg, &args,sizeof (ELAN3IO_GET_ROUTE_STRUCT)))
++              return (-EFAULT);
++      }
++      break;
++    }
++
++    case ELAN3IO_RESET_ROUTE:
++    {
++      ELAN3IO_RESET_ROUTE_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN3IO_RESET_ROUTE_STRUCT)))
++          return (-EFAULT);
++      
++      res = elan3_reset_route (ctxt, args.process);
++      break;
++    }
++
++    case ELAN3IO_VP2NODEID:
++    {
++      ELAN3IO_VP2NODEID_STRUCT *vp2nodeId;
++      ELAN_LOCATION           location;
++
++      KMEM_ALLOC (vp2nodeId, ELAN3IO_VP2NODEID_STRUCT *, sizeof(ELAN3IO_VP2NODEID_STRUCT), TRUE);
++      if (vp2nodeId == NULL) 
++          return (set_errno (ENOMEM));
++
++      if (copy_from_user (vp2nodeId, (void *) arg, sizeof (ELAN3IO_VP2NODEID_STRUCT))) {
++          KMEM_FREE (vp2nodeId, sizeof(ELAN3IO_VP2NODEID_STRUCT));
++          return (-EFAULT);
++      }
++
++      krwlock_write (&ctxt->VpLock);
++      location = ProcessToLocation (ctxt, NULL, vp2nodeId->process , NULL);
++      krwlock_done (&ctxt->VpLock);
++
++      vp2nodeId->nodeId = location.loc_node;
++      if (copy_to_user ( (void *) arg, vp2nodeId, sizeof (ELAN3IO_VP2NODEID_STRUCT))) {
++          KMEM_FREE (vp2nodeId, sizeof(ELAN3IO_VP2NODEID_STRUCT));
++          return (-EFAULT);
++      }
++
++      KMEM_FREE (vp2nodeId, sizeof(ELAN3IO_VP2NODEID_STRUCT));
++
++      break;
++    }
++
++    case ELAN3IO_PROCESS:
++      return (elan3_process (ctxt));
++
++    case ELAN3IO_SETPERM:
++    {
++      ELAN3IO_SETPERM_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN3IO_SETPERM_STRUCT)))
++          return (-EFAULT);
++
++      res = elan3mmu_setperm (ctxt->Elan3mmu, args.maddr, args.eaddr, args.len, args.perm);
++      break;
++    }
++
++    case ELAN3IO_CLEARPERM:
++    {
++      ELAN3IO_CLEARPERM_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN3IO_CLEARPERM_STRUCT)))
++          return (-EFAULT);
++
++      elan3mmu_clrperm (ctxt->Elan3mmu, args.eaddr, args.len);
++      break;
++    }
++
++    case ELAN3IO_CHANGEPERM:
++    {
++      ELAN3IO_CHANGEPERM_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN3IO_CHANGEPERM_STRUCT)))
++          return (-EFAULT);
++
++      res = EINVAL;
++      break;
++    }
++
++    case ELAN3IO_HELPER_THREAD:
++      res = elan3_lwp (ctxt);
++      break;
++      
++    case ELAN3IO_WAITCOMMAND:
++      res = WaitForCommandPort (ctxt);
++      break;
++
++    case ELAN3IO_BLOCK_INPUTTER:
++      elan3_block_inputter (ctxt, arg);
++      break;
++
++    case ELAN3IO_SET_FLAGS:
++      sctx->Flags = arg;
++      break;
++
++    case ELAN3IO_SET_SIGNAL:
++      sctx->signal = arg;
++      break;
++
++    case ELAN3IO_WAITEVENT:
++      res = sys_waitevent (ctxt, (E3_Event *) arg);
++      break;
++
++    case ELAN3IO_ALLOC_EVENTCOOKIE:
++      res = cookie_alloc_cookie (sctx->Table, arg);
++      break;
++
++    case ELAN3IO_FREE_EVENTCOOKIE:
++      res = cookie_free_cookie (sctx->Table, arg);
++      break;
++
++    case ELAN3IO_ARM_EVENTCOOKIE:
++      res = cookie_arm_cookie (sctx->Table, arg);
++      break;
++
++    case ELAN3IO_WAIT_EVENTCOOKIE:
++      res = cookie_wait_cookie (sctx->Table, arg);
++      break;
++
++    case ELAN3IO_SWAPSPACE:
++      if (fuword (&((SYS_SWAP_SPACE *) arg)->Magic) != SYS_SWAP_MAGIC)
++          return (set_errno (EINVAL));
++      
++      ((SYS_CTXT *) ctxt->Private)->Swap = (SYS_SWAP_SPACE *) arg;
++      break;
++
++    case ELAN3IO_EXCEPTION_SPACE:
++      if (fuword (&((SYS_EXCEPTION_SPACE *) arg)->Magic) != SYS_EXCEPTION_MAGIC)
++          return (set_errno (EINVAL));
++
++      ((SYS_CTXT *) ctxt->Private)->Exceptions = (SYS_EXCEPTION_SPACE *) arg;
++      break;
++
++    case ELAN3IO_GET_EXCEPTION:
++    {
++      SYS_EXCEPTION *exception;
++
++      if (((SYS_CTXT *) ctxt->Private)->Exceptions == NULL)
++          return (set_errno (EINVAL));
++      
++      KMEM_ALLOC(exception, SYS_EXCEPTION *, sizeof (SYS_EXCEPTION), TRUE);
++
++      if (exception == NULL)
++          return (set_errno (ENOMEM));
++
++      if ((res = sys_getException (((SYS_CTXT *) ctxt->Private), exception)) == 0 &&
++          copy_to_user ((void *) arg, exception, sizeof (SYS_EXCEPTION)))
++          res = EFAULT;
++      
++      KMEM_FREE (exception, sizeof (SYS_EXCEPTION));
++      break;
++    }
++    
++    case ELAN3IO_UNLOAD:
++    {
++      ELAN3MMU             *elan3mmu = ctxt->Elan3mmu;
++      ELAN3IO_UNLOAD_STRUCT args;
++      int                   span;
++      unsigned long         flags;
++      E3_Addr               eaddr;
++      caddr_t               addr;
++      size_t                len;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN3IO_UNLOAD_STRUCT)))
++          return (-EFAULT);
++
++      addr = (caddr_t) args.addr;
++      len  = args.len;
++
++      if (((unsigned long) addr & PAGEMASK) || (len & PAGEMASK) || (len < 0))
++          return -EINVAL;
++
++      spin_lock_irqsave (&elan3mmu->elan3mmu_lock, flags);
++      for (; len; len -= span, addr += span)
++      {
++          ELAN3MMU_RGN *rgn = elan3mmu_findrgn_main (elan3mmu, addr, 0);
++          
++          if (rgn == NULL || (rgn->rgn_mbase + rgn->rgn_len) < addr)
++              span = len;
++          else if (rgn->rgn_mbase > addr)
++              span = MIN(len, rgn->rgn_mbase - addr);
++          else
++          {
++              span  = MIN(len, (rgn->rgn_mbase + rgn->rgn_len) - addr);
++              eaddr = rgn->rgn_ebase + (addr - rgn->rgn_mbase);
++              
++              elan3mmu_unload (elan3mmu, eaddr, span, PTE_UNLOAD);
++          }
++      }
++      spin_unlock_irqrestore (&elan3mmu->elan3mmu_lock, flags);
++      
++      return 0;
++    }
++
++    case ELAN3IO_GET_DEVINFO:
++    {
++      ELAN3IO_GET_DEVINFO_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN3IO_GET_DEVINFO_STRUCT)))
++          return (-EFAULT);
++      
++      if (copy_to_user ((void *) args.devinfo, &ctxt->Device->Devinfo, sizeof (ELAN_DEVINFO))) 
++          res = EFAULT;
++      break;
++    }
++
++    case ELAN3IO_GET_POSITION:
++    {
++      ELAN3IO_GET_POSITION_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN3IO_GET_POSITION_STRUCT)))
++          return (-EFAULT);   
++
++      if (copy_to_user ((void *) args.position, &ctxt->Device->Position, sizeof (ELAN_POSITION)))
++          res = EFAULT;
++      break;
++    }
++
++    default:
++      return (-EINVAL);
++    }
++
++    return (res ? set_errno (res) : 0);
++}
++
++static void user_vma_open(struct vm_area_struct *vma)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) vma->vm_private_data;
++
++    PRINTF (DBG_DEVICE, DBG_SEG, "user_vma_open: vm_mm=%p start=%lx end=%lx pgoff=%lx file=%p\n",
++          vma->vm_mm, vma->vm_start, vma->vm_end, vma->vm_pgoff, vma->vm_file);
++
++    if (vma->vm_pgoff == ELAN3IO_OFF_COMMAND_PAGE)
++      if (atomic_dec_and_test (&pr->pr_mappings))
++          pr->pr_ctxt->CommandPageMapping = NULL;
++}
++
++static void user_vma_close(struct vm_area_struct *vma)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) vma->vm_private_data;
++
++    PRINTF (DBG_DEVICE, DBG_SEG, "user_vma_close: vm_mm=%p start=%lx end=%lx pgoff=%lx file=%p\n",
++          vma->vm_mm, vma->vm_start, vma->vm_end, vma->vm_pgoff, vma->vm_file);
++
++    if (vma->vm_pgoff == ELAN3IO_OFF_COMMAND_PAGE)
++      atomic_inc (&pr->pr_mappings);
++}
++
++static struct vm_operations_struct user_vm_ops = {
++    open:             user_vma_open,
++    close:            user_vma_close,
++};
++
++static int
++user_mmap (struct file *file, struct vm_area_struct *vma)
++{
++    USER_PRIVATE  *pr   = (USER_PRIVATE *) file->private_data;
++    ELAN3_CTXT     *ctxt = pr->pr_ctxt; 
++    ioaddr_t       ioaddr;
++
++    /* 
++     * NOTE - since we need to maintain the reference count on
++     *        the user_private we only permit single page 
++     *        mmaps - this means that we will certainly see
++     *        the correct number of closes to maintain the
++     *        the reference count correctly.
++     */
++    
++    if ((vma->vm_end - vma->vm_start) != PAGE_SIZE)
++      return (-EINVAL);
++
++    PRINTF (DBG_DEVICE, DBG_SEG, "user_mmap: vm_mm=%p start=%lx end=%lx pgoff=%lx flags=%lx prot=%lx file=%p\n",
++          vma->vm_mm, vma->vm_start, vma->vm_end, vma->vm_pgoff, vma->vm_flags, vma->vm_page_prot.pgprot, vma->vm_file);
++
++    switch (vma->vm_pgoff)
++    {
++    default:
++      return (-EINVAL);
++      
++    case ELAN3IO_OFF_COMMAND_PAGE:
++      spin_lock (&pr->pr_lock);
++      if (ctxt->CommandPage == (ioaddr_t) 0 || atomic_read (&pr->pr_mappings) != 0)
++      {
++          PRINTF (DBG_DEVICE, DBG_SEG, "user_mmap: command port - %s\n", ctxt->CommandPort ? "already mapped" : "not attached");
++          spin_unlock (&pr->pr_lock);
++          return (-EINVAL);
++      }
++#ifdef LINUX_SPARC
++      pgprot_val(vma->vm_page_prot) &= ~(_PAGE_CACHE);
++      pgprot_val(vma->vm_page_prot) |= _PAGE_IE;
++#elif defined(pgprot_noncached)
++      vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
++#endif
++
++      PRINTF (DBG_DEVICE, DBG_SEG, "user_mmap: commandport at %lx phys %llx prot %lx\n", 
++              vma->vm_start, (unsigned long long) kmem_to_phys ((void *) ctxt->CommandPort), vma->vm_page_prot.pgprot);
++
++      /* Don't try to swap out physical pages.. */
++      vma->vm_flags |= VM_RESERVED;
++    
++      /*
++       * Don't dump addresses that are not real memory to a core file.
++       */
++      vma->vm_flags |= VM_IO;
++
++#ifdef NO_RMAP
++      if (remap_page_range (vma->vm_start, kmem_to_phys ((void *) ctxt->CommandPage), vma->vm_end - vma->vm_start, vma->vm_page_prot))
++#else 
++      if (remap_page_range (vma, vma->vm_start, kmem_to_phys ((void *) ctxt->CommandPage), vma->vm_end - vma->vm_start, vma->vm_page_prot))
++#endif
++      {
++          spin_unlock (&pr->pr_lock);
++          return (-EAGAIN);
++      }
++      ctxt->CommandPageMapping = (void *) vma->vm_start;
++      
++      atomic_inc (&pr->pr_mappings);
++      
++      spin_unlock (&pr->pr_lock);
++      break;
++
++    case ELAN3IO_OFF_UREG_PAGE:
++#ifdef LINUX_SPARC
++      pgprot_val(vma->vm_page_prot) &= ~(_PAGE_CACHE);
++      pgprot_val(vma->vm_page_prot) |= _PAGE_IE;
++#elif defined(pgprot_noncached)
++      vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
++#endif
++      ioaddr = ctxt->Device->RegPtr + (offsetof (E3_Regs, URegs) & PAGEMASK);
++
++      PRINTF (DBG_DEVICE, DBG_SEG, "user_mmap: user_regs at %lx phys %llx prot %lx\n", vma->vm_start, 
++              (unsigned long long) kmem_to_phys ((void *) ioaddr), vma->vm_page_prot.pgprot);
++
++      /* Don't try to swap out physical pages.. */
++      vma->vm_flags |= VM_RESERVED;
++    
++      /*
++       * Don't dump addresses that are not real memory to a core file.
++       */
++      vma->vm_flags |= VM_IO;
++
++#ifdef NO_RMAP
++      if (remap_page_range (vma->vm_start, kmem_to_phys ((void *) ioaddr),
++#else
++      if (remap_page_range (vma, vma->vm_start, kmem_to_phys ((void *) ioaddr),
++#endif
++                            vma->vm_end - vma->vm_start, vma->vm_page_prot))
++          return (-EAGAIN);
++      break;
++      
++    case ELAN3IO_OFF_FLAG_PAGE:
++      PRINTF (DBG_DEVICE, DBG_SEG, "user_mmap: flag page at %lx phys %llx\n", vma->vm_start, 
++              (unsigned long long) kmem_to_phys ((void *) ctxt->FlagPage));
++
++      /* we do not want to have this area swapped out, lock it */
++      vma->vm_flags |= VM_LOCKED;
++
++      /* Mark the page as reserved or else the remap_page_range() doesn't remap it */
++      SetPageReserved(pte_page(*find_pte_kernel((unsigned long) ctxt->FlagPage)));
++      
++#ifdef NO_RMAP
++      if (remap_page_range (vma->vm_start, kmem_to_phys ((void *) ctxt->FlagPage),
++#else
++      if (remap_page_range (vma, vma->vm_start, kmem_to_phys ((void *) ctxt->FlagPage),
++#endif
++                            vma->vm_end - vma->vm_start, vma->vm_page_prot))
++          return (-EAGAIN);
++      break;
++    }
++
++    ASSERT (vma->vm_ops == NULL);
++    
++    vma->vm_ops          = &user_vm_ops;
++    vma->vm_file         = file;
++    vma->vm_private_data = (void *) pr;
++    
++    return (0);
++}
++
++/* driver entry points */
++static int
++elan3_open (struct inode *inode, struct file *file)
++{
++    if (elan3_devices[ELAN3_DEVICE(inode)] == NULL)
++      return (-ENXIO);
++
++    PRINTF (DBG_DEVICE, DBG_FN, "elan3_open: device %d minor %d file=%p\n", ELAN3_DEVICE(inode), ELAN3_MINOR(inode), file);
++    
++    switch (ELAN3_MINOR (inode))
++    {
++    case ELAN3_MINOR_CONTROL:
++      return (control_open (inode, file));
++    case ELAN3_MINOR_MEM:
++      return (mem_open (inode, file));
++    case ELAN3_MINOR_USER:
++      return (user_open (inode, file));
++    default:
++      return (-ENXIO);
++    }
++}
++
++static int
++elan3_release (struct inode *inode, struct file *file)
++{
++    PRINTF (DBG_DEVICE, DBG_FN, "elan3_release: device %d minor %d file=%p\n", ELAN3_DEVICE(inode), ELAN3_MINOR(inode), file);
++    
++    switch (ELAN3_MINOR (inode))
++    {
++    case ELAN3_MINOR_CONTROL:
++      return (control_release (inode, file));
++    case ELAN3_MINOR_MEM:
++      return (mem_release (inode, file));
++    case ELAN3_MINOR_USER:
++      return (user_release (inode, file));
++    default:
++      return (-ENXIO);
++    }
++}
++
++static int
++elan3_ioctl (struct inode *inode, struct file *file, 
++           unsigned int cmd, unsigned long arg)
++{
++    switch (ELAN3_MINOR (inode))
++    {
++    case ELAN3_MINOR_CONTROL:
++      return (control_ioctl (inode, file, cmd, arg));
++    case ELAN3_MINOR_MEM:
++      return (mem_ioctl (inode, file, cmd, arg));
++    case ELAN3_MINOR_USER:
++      return (user_ioctl (inode, file, cmd, arg));
++    default:
++      return (-ENXIO);
++    }
++}
++
++
++static int
++elan3_mmap (struct file *file, struct vm_area_struct *vma)
++{
++    PRINTF (DBG_DEVICE, DBG_SEG, "elan3_mmap: instance %d minor %d start=%lx end=%lx pgoff=%lx flags=%lx prot=%lx\n", 
++          ELAN3_DEVICE (file->f_dentry->d_inode), ELAN3_MINOR (file->f_dentry->d_inode),
++          vma->vm_start, vma->vm_end, vma->vm_pgoff, vma->vm_flags, vma->vm_page_prot.pgprot);
++
++    switch (ELAN3_MINOR (file->f_dentry->d_inode))
++    {
++    case ELAN3_MINOR_CONTROL:
++      return (control_mmap (file, vma));
++    case ELAN3_MINOR_MEM:
++      return (mem_mmap (file, vma));
++    case ELAN3_MINOR_USER:
++      return (user_mmap (file, vma));
++    default:
++      return (-ENXIO);
++    }
++}
++
++static irqreturn_t
++InterruptHandlerWrapper(int irq, void *dev_id, struct pt_regs *regs)
++{
++    if (InterruptHandler ((ELAN3_DEV *)dev_id) == 0)
++      return IRQ_HANDLED;
++    else
++      return IRQ_NONE;
++}
++
++
++/* 
++ * Elan specific PCI configuration registers.
++ */
++
++#define PCI_CONF_PARITY_PHYS_LO         0x40
++#define PCI_CONF_PARITY_PHYS_HI         0x44
++#define PCI_CONF_PARITY_PHASE_ADDR      0x46
++#define PCI_CONF_PARITY_MASTER_TYPE     0x47
++#define PCI_CONF_ELAN3_CTRL              0x48
++ 
++#define ECTRL_EXTEND_LATENCY            (1 << 0)
++#define ECTRL_ENABLE_PREFETCH           (1 << 1)
++#define ECTRL_SOFTWARE_INTERNAL_RESET   (1 << 2)
++#define ECTRL_REDUCED_RETRY_RATE        (1 << 3)
++#define ECTRL_CLOCK_DIVIDE_RATE_SHIFT   4
++#define ECTRL_COMMS_DIVIDE_RATE_SHIFT   10
++#define ECTRL_FORCE_COMMSCLK_LOCAL      (1 << 14)
++
++/*
++ * Configure PCI.
++ */
++static int
++ConfigurePci(ELAN3_DEV *dev)
++{
++      struct pci_dev *pci = dev->Osdep.pci;
++      u32 rom_address;
++
++      if (pci_enable_device(pci))
++          return (ENXIO);
++
++      /* disable ROM */
++      pci_read_config_dword(pci, PCI_ROM_ADDRESS, &rom_address);
++      rom_address &= ~PCI_ROM_ADDRESS_ENABLE;
++      pci_write_config_dword(pci, PCI_ROM_ADDRESS, rom_address);
++      mb();
++
++      /* this is in 32-bit WORDS */
++      pci_write_config_byte(pci, PCI_CACHE_LINE_SIZE, (64 >> 2));
++      mb();
++
++      /* allow 40 ticks to respond, 16 data phases */
++      pci_write_config_byte(pci, PCI_LATENCY_TIMER, 255);
++      mb();
++
++      /* don't enable PCI_COMMAND_SERR--see note in elandev_dunix.c */
++      pci_write_config_word(pci, PCI_COMMAND, PCI_COMMAND_MEMORY 
++          | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE | PCI_COMMAND_PARITY);
++      mb();
++
++      return ESUCCESS;
++}
++
++/* 
++ * Reset chip to a known state.
++ */
++static int
++ResetElan(ELAN3_DEV *dev, ioaddr_t intPalAddr)
++{
++      struct pci_dev *pci = dev->Osdep.pci;
++      int instance = dev->Instance;
++      u32 val;
++      u8 revid;
++      int CasLatency;
++      int res;
++
++      /* determine rev of board */
++      pci_read_config_byte(pci, PCI_REVISION_ID, &revid);
++
++      /* GNAT 2328 - don't set ECTRL_ENABLE_PREFETCH on Elan rev A */
++      val = ECTRL_EXTEND_LATENCY | (39 << ECTRL_CLOCK_DIVIDE_RATE_SHIFT)
++          | (6 << ECTRL_COMMS_DIVIDE_RATE_SHIFT);
++      switch (revid) 
++      {
++              case PCI_REVISION_ID_ELAN3_REVA:
++                      printk("elan%d: is an elan3 (revision a) - not supported\n", instance);
++                      return (EFAIL);
++
++              case PCI_REVISION_ID_ELAN3_REVB:        
++                      val |= ECTRL_ENABLE_PREFETCH;
++                      if (BackToBackMaster)
++                              val |= ECTRL_FORCE_COMMSCLK_LOCAL;
++                      printk("elan%d: is an elan3 (revision b)\n", instance);
++                      break;
++              default:
++                      printk("elan%d: unsupported elan3 revision %d\n", 
++                          instance, revid);
++                      return EFAIL;
++      }
++      pci_write_config_dword(pci, PCI_CONF_ELAN3_CTRL, val);
++      mb();
++
++      /*
++       * GNAT: 2474
++       * Hit reset on the Elan, then we MUST initialise the schedule status
++       * register to drive reset on the link before the link can come out
++       * of reset (15 uS). We need to keep it like this until we've 
++       * initialised SDRAM
++       */
++      pci_read_config_dword(pci, PCI_CONF_ELAN3_CTRL, &val);
++      pci_write_config_dword(pci, PCI_CONF_ELAN3_CTRL, 
++          val | ECTRL_SOFTWARE_INTERNAL_RESET);
++      mb();
++
++      /* Read the Vital Product Data to determine the cas latency */
++      if ((res = ReadVitalProductData (dev, &CasLatency)) != ESUCCESS)
++          return (res);
++
++      /*
++       * Now clear the Software internal reset bit, and start the sdram
++       */
++      pci_write_config_dword(pci, PCI_CONF_ELAN3_CTRL, val);
++      mb();
++
++      /* 
++       * Enable SDRAM before sizing and initalising it for ECC.
++       * NOTE: don't enable all sets of the cache (yet), nor ECC 
++       */
++      dev->Cache_Control_Reg = (CasLatency | REFRESH_RATE_16US);
++
++      write_reg32 (dev, Cache_Control_Reg.ContReg, (dev->Cache_Control_Reg | SETUP_SDRAM));
++      mb();
++
++      INIT_SCHED_STATUS(dev, Sched_Initial_Value);
++
++      /*
++       * Set the interrupt mask to 0 and enable the interrupt PAL
++       * by writing any value to it.
++       */
++      SET_INT_MASK (dev, 0);
++      writeb (0, intPalAddr);
++ 
++      return ESUCCESS;
++}
++
++/*
++ * Determine the size of elan PCI address spaces.  EFAIL is returned if 
++ * unused or invalid BAR is specified, or if board reports I/O mapped space.
++ */
++int
++DeviceRegisterSize(ELAN3_DEV *dev, int rnumber, int *sizep)
++{
++      struct pci_dev *pdev = dev->Osdep.pci;
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
++      *sizep = pci_resource_size(pdev, rnumber);
++#else
++      *sizep = pci_resource_end(pdev, rnumber) - pci_resource_start(pdev, rnumber) + 1;
++#endif
++      return ESUCCESS;
++}
++
++/*
++ * Map PCI memory into kernel virtual address space.  On the alpha, 
++ * we just return appropriate kseg address, and Unmap is a no-op.
++ */
++int
++MapDeviceRegister(ELAN3_DEV *dev, int rnumber, ioaddr_t *addrp,
++                int off, int len, DeviceMappingHandle *handlep)
++{     
++      struct pci_dev *pdev = dev->Osdep.pci;
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
++      u64 base = pci_get_base_address(pdev, rnumber);
++      *addrp = (ioaddr_t) pci_base_to_kseg(base + off, pdev->bus->number);
++
++#else
++      if (len == 0)
++          len = pci_resource_end(pdev, rnumber) - pci_resource_start(pdev, rnumber) + 1;
++      
++      if (len == 0)
++          return (EINVAL);
++
++      *addrp = (ioaddr_t) ioremap_nocache (pci_resource_start(pdev, rnumber) + off, len);
++#endif
++
++      *handlep = (void *) *addrp;
++
++      return (*addrp ? ESUCCESS : ENOMEM);
++}
++void
++UnmapDeviceRegister(ELAN3_DEV *dev, DeviceMappingHandle *handlep)
++{
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
++    iounmap (*handlep);
++#endif
++}
++
++void
++ElanBusError (ELAN3_DEV *dev)
++{
++      struct pci_dev  *pci = dev->Osdep.pci;  
++      u8  phaseaddr, type;
++      u16 status, cmd, physhi;
++      u32 physlo;
++ 
++      printk("elan%d: bus error occured\n", dev->Instance);
++
++      pci_read_config_word (pci, PCI_STATUS,                  &status);
++      pci_read_config_word (pci, PCI_COMMAND,                 &cmd);
++      pci_read_config_dword(pci, PCI_CONF_PARITY_PHYS_LO,     &physlo);
++      pci_read_config_word (pci, PCI_CONF_PARITY_PHYS_HI,     &physhi);
++      pci_read_config_byte (pci, PCI_CONF_PARITY_PHASE_ADDR,  &phaseaddr); 
++      pci_read_config_byte (pci, PCI_CONF_PARITY_MASTER_TYPE, &type);
++
++#define PCI_CONF_STAT_FORMAT  "\20" \
++      "\6SIXTY_SIX_MHZ\7UDF\10FAST_BACK\11PARITY" \
++      "\14SIG_TARGET_ABORT\15REC_TARGET_ABORT\16REC_MASTER_ABORT" \
++      "\17SIG_SYSTEM_ERROR\20DETECTED_PARITY"
++
++      printk ("elan%d: status %x cmd %4x physaddr %04x%08x phase %x type %x\n",
++              dev->Instance, status, cmd, physhi, physlo, phaseaddr, type);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan3/elansyscall.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan3/elansyscall.c    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan3/elansyscall.c 2005-06-01 23:12:54.584441384 -0400
+@@ -0,0 +1,1230 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: elansyscall.c,v 1.99.2.1 2004/10/28 17:08:56 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/os/elansyscall.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <qsnet/autoconf.h>
++
++#include <elan/elanmod.h>
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elanvp.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanctxt.h>
++#include <elan3/elandebug.h>
++#include <elan3/elansyscall.h>
++#include <elan/devinfo.h>
++
++static int       sys_exception (ELAN3_CTXT *ctxt, int type, int proc, void *trap, va_list ap);
++static int       sys_getWordItem (ELAN3_CTXT *ctxt, int list, void **itemp, E3_uint32 *valuep);
++static int       sys_getBlockItem (ELAN3_CTXT *ctxt, int list, void **itemp, E3_Addr *valuep);
++static void      sys_putWordItem (ELAN3_CTXT *ctxt, int list, E3_uint32 value);
++static void      sys_putBlockItem (ELAN3_CTXT *ctxt, int list, E3_uint32 *ptr);
++static void      sys_putbackItem (ELAN3_CTXT *ctxt, int list, void *item);
++static void      sys_freeWordItem (ELAN3_CTXT *ctxt, void *item);
++static void      sys_freeBlockItem (ELAN3_CTXT *ctxt, void *item);
++static int       sys_countItems (ELAN3_CTXT *ctxt, int list);
++static int       sys_event (ELAN3_CTXT *ctxt, E3_uint32 cookie, int flag);
++static void      sys_swapin (ELAN3_CTXT *ctxt);
++static void      sys_swapout (ELAN3_CTXT *ctxt);
++static void      sys_freePrivate (ELAN3_CTXT *ctxt);
++static int       sys_fixupNetworkError (ELAN3_CTXT *ctxt, NETERR_FIXUP *nef);
++static int       sys_startFaultCheck (ELAN3_CTXT *ctxt);
++static void      sys_endFaultCheck (ELAN3_CTXT *ctxt);
++static E3_uint8  sys_load8 (ELAN3_CTXT *ctxt, E3_Addr addr);
++static void      sys_store8 (ELAN3_CTXT *ctxt, E3_Addr addr, E3_uint8 val);
++static E3_uint16 sys_load16 (ELAN3_CTXT *ctxt, E3_Addr addr);
++static void      sys_store16 (ELAN3_CTXT *ctxt, E3_Addr addr, E3_uint16 val);
++static E3_uint32 sys_load32 (ELAN3_CTXT *ctxt, E3_Addr addr);
++static void      sys_store32 (ELAN3_CTXT *ctxt, E3_Addr addr, E3_uint32 val);
++static E3_uint64 sys_load64 (ELAN3_CTXT *ctxt, E3_Addr addr);
++static void      sys_store64 (ELAN3_CTXT *ctxt, E3_Addr addr, E3_uint64 val);
++
++static ELAN3_OPS elan3_sys_ops = {
++    ELAN3_OPS_VERSION,                /* Version */
++
++    sys_exception,            /* Exception */
++    sys_getWordItem,          /* GetWordItem */
++    sys_getBlockItem,         /* GetBlockItem */
++    sys_putWordItem,          /* PutWordItem */
++    sys_putBlockItem,         /* PutBlockItem */
++    sys_putbackItem,          /* PutbackItem */
++    sys_freeWordItem,         /* FreeWordItem */
++    sys_freeBlockItem,                /* FreeBlockItem */
++    sys_countItems,           /* CountItems */
++    sys_event,                        /* Event */
++    sys_swapin,                       /* Swapin */
++    sys_swapout,              /* Swapout */
++    sys_freePrivate,          /* FreePrivate */
++    sys_fixupNetworkError,    /* FixupNetworkError */
++    NULL,                     /* DProcTrap */
++    NULL,                     /* TProcTrap */
++    NULL,                     /* IProcTrap */
++    NULL,                     /* CProcTrap */
++    NULL,                     /* CProcReissue */
++    sys_startFaultCheck,      /* StartFaultCheck */
++    sys_endFaultCheck,          /* EndFaultCheck */
++    sys_load8,                        /* Load8 */
++    sys_store8,                       /* Store8 */
++    sys_load16,                       /* Load16 */
++    sys_store16,              /* Store16 */
++    sys_load32,                       /* Load32 */
++    sys_store32,              /* Store32 */
++    sys_load64,                       /* Load64 */
++    sys_store64                       /* Store64 */
++};
++
++va_list null_valist;
++
++SYS_CTXT *
++sys_init (ELAN3_CTXT *ctxt)
++{
++    SYS_CTXT *sctx;
++
++    /* Allocate and initialise the context private data */
++    KMEM_ZALLOC (sctx, SYS_CTXT *, sizeof  (SYS_CTXT), TRUE);
++
++    if (sctx == NULL)
++      return ((SYS_CTXT *) NULL);
++
++    sctx->Swap    = NULL;
++    sctx->Armed   = 0;
++    sctx->Backoff = 1;
++    sctx->Table   = cookie_alloc_table ((unsigned long) ELAN3_MY_TASK_HANDLE(), 0);
++    sctx->signal  = SIGSEGV;
++
++    if (sctx->Table == NULL)
++    {
++      KMEM_FREE (sctx, sizeof (SYS_CTXT));
++      return ((SYS_CTXT *) NULL);
++    }
++
++    kmutex_init  (&sctx->Lock);
++    spin_lock_init (&sctx->WaitLock);
++    kcondvar_init (&sctx->NetworkErrorWait);
++    
++    /* Install my context operations and private data */
++    ctxt->Operations = &elan3_sys_ops;
++    ctxt->Private    = (void *) sctx;
++    
++    return (sctx);
++}
++
++/* returns -ve on error or ELAN_CAP_OK or ELAN_CAP_RMS */
++/* use = ELAN_USER_ATTACH, ELAN_USER_P2P, ELAN_USER_BROADCAST */
++int 
++elan3_validate_cap(ELAN3_DEV *dev, ELAN_CAPABILITY *cap ,int use)
++{
++     /* Don't allow a user process to attach to system context */
++    if (ELAN3_SYSTEM_CONTEXT (cap->cap_lowcontext) || ELAN3_SYSTEM_CONTEXT (cap->cap_highcontext)
++      || cap->cap_highcontext <= ELAN_USER_BASE_CONTEXT_NUM  || cap->cap_highcontext <= ELAN_USER_BASE_CONTEXT_NUM)
++    {
++      PRINTF2 (DBG_DEVICE, DBG_VP,"elan3_validate_cap: lctx %x hctx %x \n",cap->cap_lowcontext,  cap->cap_highcontext);
++      PRINTF3 (DBG_DEVICE, DBG_VP,"elan3_validate_cap: bit %x  low %x high %x\n", ((cap->cap_lowcontext) & SYS_CONTEXT_BIT),
++               E3_NUM_CONTEXT_0, ELAN3_KCOMM_BASE_CONTEXT_NUM);
++
++
++      PRINTF0 (DBG_DEVICE, DBG_VP,"elan3_validate_cap: user process cant attach to system cap\n");
++      return (-EINVAL);
++    }
++ 
++    if (cap->cap_type & ELAN_CAP_TYPE_HWTEST)
++    {
++      if (!(cap->cap_type & ELAN_CAP_TYPE_NO_BITMAP)) /* cant have a bit map */
++      {
++          PRINTF0 (DBG_DEVICE, DBG_VP, "elanmod_classify_cap: ELAN_CAP_TYPE_HWTEST must have ELAN_CAP_TYPE_NO_BITMAP\n");
++          return (-EINVAL);
++      }
++      
++      if (cap->cap_lowcontext != cap->cap_highcontext) 
++      {
++          PRINTF2 (DBG_DEVICE, DBG_VP, "elanmod_classify_cap: ELAN_CAP_TYPE_HWTEST (cap->cap_lowcontext != cap->cap_highcontext) %d %d\n",cap->cap_lowcontext , cap->cap_highcontext) ;
++          return (-EINVAL);
++      }
++      
++      if ( ! (ELAN3_HWTEST_CONTEXT(cap->cap_lowcontext) && ELAN3_HWTEST_CONTEXT(cap->cap_highcontext)))
++      {
++          PRINTF3 (DBG_DEVICE, DBG_VP, "elanmod_classify_cap: ELAN_CAP_TYPE_HWTEST HWTEST_BASE_CONTEXT %d %d %d \n" , ELAN3_HWTEST_BASE_CONTEXT_NUM,cap->cap_lowcontext ,ELAN3_HWTEST_TOP_CONTEXT_NUM);
++          return (-EINVAL);
++      }
++      
++      if (cap->cap_lownode != ELAN_CAP_UNINITIALISED || cap->cap_highnode != ELAN_CAP_UNINITIALISED)
++      {
++          PRINTF0 (DBG_DEVICE, DBG_VP, "elanmod_classify_cap: ELAN_CAP_TYPE_HWTEST nodes != ELAN_CAP_UNINITIALISED\n");
++          return (-EINVAL);
++      }
++
++      return ELAN_CAP_OK;
++    }
++
++    return elanmod_classify_cap(&dev->Position, cap, use);
++}
++
++int
++sys_waitevent (ELAN3_CTXT *ctxt, E3_Event *event)
++{
++    SYS_CTXT    *sctx = (SYS_CTXT *) ctxt->Private;
++    EVENT_COOKIE cookie;
++
++    if (ctxt->Device->Devinfo.dev_revision_id == PCI_REVISION_ID_ELAN3_REVA)
++      return (EINVAL);
++
++    cookie = fuword ((int *) &event->ev_Type) & ~(EV_TYPE_MASK_EVIRQ | EV_TYPE_MASK_BCOPY);
++
++    if (cookie_alloc_cookie (sctx->Table, cookie) != ESUCCESS)
++      return (EINVAL);
++
++    cookie_arm_cookie (sctx->Table, cookie);
++
++    if (fuword ((int *) &event->ev_Count) > 0)
++      cookie_wait_cookie (sctx->Table, cookie);
++    
++    cookie_free_cookie (sctx->Table, cookie);
++    
++    return (ESUCCESS);
++}
++
++static void *
++sys_getItem (SYS_SWAP_SPACE *sp, int list)
++{
++    void *itemp = (void *) fuptr_noerr ((void **) &sp->ItemListsHead[list]);
++    void *next;
++    
++    PRINTF4 (DBG_DEVICE, DBG_SYSCALL, "sys_getItem: sp=%p list=%d head=%p itemp=%p\n",
++           sp, list, &sp->ItemListsHead[list], itemp);
++    
++    if (itemp == NULL)
++      return (NULL);
++
++    next = (void *) fuptr_noerr ((void *) itemp);
++
++    suptr_noerr ((void *) &sp->ItemListsHead[list], (void *) next);
++    if (next == NULL)
++      suptr_noerr ((void *) &sp->ItemListsTailp[list], (void *)&sp->ItemListsHead[list]);
++    return (itemp);
++}
++
++static void
++sys_putItemBack (SYS_SWAP_SPACE *sp, int list, void *itemp)
++{
++    PRINTF4 (DBG_DEVICE, DBG_SYSCALL, "sys_putItemBack: sp=%p list=%d itemp=%p value=%08x\n",
++           sp, list, itemp, fuword_noerr ((int *) &((SYS_WORD_ITEM *) itemp)->Value));
++
++    suptr_noerr ((void **) itemp, NULL);                                                      /* item->Next = NULL */
++    suptr_noerr ((void **) fuptr_noerr ((void **) &sp->ItemListsTailp[list]), (void *)itemp);         /* *Tailp = item */
++    suptr_noerr ((void **) &sp->ItemListsTailp[list], (void *) itemp);                                /* Tailp = &item->Next */
++}
++
++static void
++sys_putItemFront (SYS_SWAP_SPACE *sp, int list, void *itemp)
++{
++    PRINTF4 (DBG_DEVICE, DBG_SYSCALL, "sys_putItemFront: sp=%p list=%d itemp=%p value=%08x\n",
++           sp, list, itemp, fuword_noerr ((int *) &((SYS_WORD_ITEM *) itemp)->Value));
++
++    suptr_noerr ((void **) itemp, fuptr_noerr ((void **) &sp->ItemListsHead[list]));          /* item->Next = Head */
++    suptr_noerr ((void **) &sp->ItemListsHead[list], (void *) itemp);                         /* Head = item */
++
++    if (fuptr_noerr ((void **) &sp->ItemListsTailp[list]) == (void *) &sp->ItemListsHead[list])       /* if (Tailp == &Head) */
++      suptr_noerr ((void **) &sp->ItemListsTailp[list], (void *) itemp);                      /*    Tailp = &Item->Next */
++}
++
++
++static int
++sys_getWordItem (ELAN3_CTXT *ctxt, int list, void **itemp, E3_uint32 *valuep)
++{
++    SYS_CTXT     *sctx = (SYS_CTXT *) ctxt->Private;
++    SYS_SWAP_SPACE *sp   = sctx->Swap;
++    SYS_WORD_ITEM  *item;
++    int                   res;
++    label_t       ljb;
++
++    kmutex_lock (&sctx->Lock);
++    
++    if (on_fault (&ljb))
++    {
++      no_fault();
++      kmutex_unlock (&sctx->Lock);
++      sys_exception (ctxt, EXCEPTION_SWAP_FAULT, list, (void *) NULL, null_valist);
++      return (0);
++    }
++
++    item = (SYS_WORD_ITEM *) sys_getItem (sp, list);
++
++    if (item == NULL)
++      res = 0;
++    else
++    {
++      if (list == LIST_DMA_PTR)
++          sctx->Armed = TRUE;
++
++      *itemp  = (void *) item;
++      *valuep = (E3_Addr) fuword_noerr ((E3_int32 *) &item->Value);
++
++      PRINTF3 (ctxt, DBG_SYSCALL, "sys_getWordItem: list=%d -> item=%p value=%08x\n", list, *itemp, *valuep);
++
++      res = 1;
++    }
++    
++    no_fault();
++    kmutex_unlock (&sctx->Lock);
++
++    return (res);
++}
++
++static int
++sys_getBlockItem (ELAN3_CTXT *ctxt, int list, void **itemp, E3_Addr *valuep)
++{
++    SYS_CTXT     *sctx = (SYS_CTXT *) ctxt->Private;
++    SYS_SWAP_SPACE *sp   = sctx->Swap;
++    SYS_BLOCK_ITEM *item;
++    int                   res;
++    label_t       ljb;
++
++    kmutex_lock (&sctx->Lock);
++    
++    if (on_fault (&ljb))
++    {
++      no_fault();
++      kmutex_unlock (&sctx->Lock);
++      sys_exception (ctxt, EXCEPTION_SWAP_FAULT, list, (void *) NULL, null_valist);
++      return (0);
++    }
++
++    item = sys_getItem (sp, list);
++
++    if (item == NULL)
++      res = 0;
++    else
++    {
++      E3_uint32 *dest = fuptr_noerr ((void **) &item->Pointer);
++
++      if (list == LIST_DMA_DESC)
++          sctx->Armed = TRUE;
++
++      *itemp  = (void *) item;
++      *valuep = elan3mmu_elanaddr (ctxt->Elan3mmu, (caddr_t) dest);
++
++      PRINTF3 (ctxt, DBG_SYSCALL, "sys_getBlockItem: list=%d -> item=%p addr=%08x\n", list, *itemp, *valuep);
++      PRINTF4 (ctxt, DBG_SYSCALL, "                  %08x %08x %08x %08x\n",
++               fuword_noerr ((int *) &dest[0]), fuword_noerr ((int *) &dest[1]), 
++               fuword_noerr ((int *) &dest[2]), fuword_noerr ((int *) &dest[3]));
++      PRINTF4 (ctxt, DBG_SYSCALL, "                  %08x %08x %08x %08x\n",
++               fuword_noerr ((int *) &dest[4]), fuword_noerr ((int *) &dest[5]),
++               fuword_noerr ((int *) &dest[6]), fuword_noerr ((int *) &dest[7]));
++
++      
++      res = 1;
++    }
++    
++    no_fault();
++    kmutex_unlock (&sctx->Lock);
++
++    return (res);
++}
++
++static void
++sys_putWordItem (ELAN3_CTXT *ctxt, int list, E3_Addr value)
++{
++    SYS_CTXT     *sctx = (SYS_CTXT *) ctxt->Private;
++    SYS_SWAP_SPACE *sp   = sctx->Swap;
++    SYS_WORD_ITEM  *item;
++    label_t       ljp;
++
++    kmutex_lock (&sctx->Lock);
++
++    PRINTF2 (ctxt,DBG_SYSCALL, "sys_putWordItem: list=%x value=%x\n", list, value);
++
++    if (on_fault (&ljp))
++    {
++      no_fault();
++      kmutex_unlock (&sctx->Lock);
++      
++      sys_exception (ctxt, EXCEPTION_SWAP_FAULT, list, (void *) NULL, null_valist);
++      return;
++    }
++
++    item = sys_getItem (sp, LIST_FREE_WORD);
++
++    PRINTF1 (ctxt, DBG_SYSCALL, "sys_putWordItem: item=%p\n", item);
++
++    if (item == NULL)
++    {
++      no_fault();
++      kmutex_unlock (&sctx->Lock);
++      
++      sys_exception (ctxt, EXCEPTION_SWAP_FAILED, list, (void *) NULL, null_valist);
++      return;
++    }
++    
++    PRINTF2 (ctxt, DBG_SYSCALL, "sys_putWordItem: storing value=%08x at %p\n", value, &item->Value);
++
++    PRINTF2 (ctxt, DBG_SYSCALL, "sys_putWordItem: item=%p value=%08x\n", item, value);
++
++    suword_noerr ((E3_int32 *) &item->Value, value);                                          /* write "value" into item */
++
++    sys_putItemBack (sp, list, item);
++
++    no_fault();
++    kmutex_unlock (&sctx->Lock);
++}
++
++static void
++sys_putBlockItem (ELAN3_CTXT *ctxt, int list, E3_uint32 *ptr)
++{
++    SYS_CTXT     *sctx = (SYS_CTXT *) ctxt->Private;
++    SYS_SWAP_SPACE *sp   = sctx->Swap;
++    SYS_BLOCK_ITEM *item;
++    label_t       ljp;
++    E3_uint32      *source;
++    E3_uint32      *dest;
++
++    PRINTF2 (ctxt, DBG_SYSCALL, "sys_putBlockItem: list=%x ptr=%p\n", list, ptr);
++
++    kmutex_lock (&sctx->Lock);
++    
++    if (on_fault (&ljp))
++    {
++      no_fault();
++      kmutex_unlock (&sctx->Lock);
++      
++      sys_exception (ctxt, EXCEPTION_SWAP_FAULT, list, (void *) NULL, null_valist);
++      return;
++    }
++
++    item = sys_getItem (sp, LIST_FREE_BLOCK);                 /* get an item from the freelist. */
++
++    if (item == NULL)
++    {
++      no_fault();
++      kmutex_unlock (&sctx->Lock);
++      
++      sys_exception (ctxt, EXCEPTION_SWAP_FAILED, list, (void *) NULL, null_valist);
++      return;
++    }
++
++    /*
++     * The block will have been read using 64 bit reads,  since we have
++     * to write it to user memory using 32 bit writes, we need to perform
++     * an endian swap on the Ultrasparc.
++     */
++    dest   = (E3_uint32 *) fuptr_noerr ((void **) &item->Pointer);
++    source = (E3_uint32 *) ptr;
++
++    PRINTF2 (ctxt, DBG_SYSCALL, "sys_putBlockItem: item=%p dest=%p\n",item, dest);
++    PRINTF4 (ctxt, DBG_SYSCALL, "                  %08x %08x %08x %08x\n",
++          source[0^WordEndianFlip], source[1^WordEndianFlip], source[2^WordEndianFlip], source[3^WordEndianFlip]);
++    PRINTF4 (ctxt, DBG_SYSCALL, "                  %08x %08x %08x %08x\n",
++           source[4^WordEndianFlip], source[5^WordEndianFlip], source[6^WordEndianFlip], source[7^WordEndianFlip]);
++
++    suword_noerr ((E3_int32 *) &dest[7], (E3_int32) source[7^WordEndianFlip]);
++    suword_noerr ((E3_int32 *) &dest[6], (E3_int32) source[6^WordEndianFlip]);
++    suword_noerr ((E3_int32 *) &dest[5], (E3_int32) source[5^WordEndianFlip]);
++    suword_noerr ((E3_int32 *) &dest[4], (E3_int32) source[4^WordEndianFlip]);
++    suword_noerr ((E3_int32 *) &dest[3], (E3_int32) source[3^WordEndianFlip]);
++    suword_noerr ((E3_int32 *) &dest[2], (E3_int32) source[2^WordEndianFlip]);
++    suword_noerr ((E3_int32 *) &dest[1], (E3_int32) source[1^WordEndianFlip]);
++    suword_noerr ((E3_int32 *) &dest[0], (E3_int32) source[0^WordEndianFlip]);
++
++    sys_putItemBack (sp, list, item);                         /* chain onto list of items. */
++
++    no_fault();
++    kmutex_unlock (&sctx->Lock);
++}
++
++static void
++sys_freeWordItem (ELAN3_CTXT *ctxt, void *itemp)
++{
++    SYS_CTXT     *sctx = (SYS_CTXT *) ctxt->Private;
++    SYS_SWAP_SPACE *sp   = sctx->Swap;
++    label_t       ljp;
++
++    kmutex_lock (&sctx->Lock);
++    
++    if (on_fault (&ljp))
++    {
++      no_fault();
++      kmutex_unlock (&sctx->Lock);
++      
++      sys_exception (ctxt, EXCEPTION_SWAP_FAULT, LIST_FREE_WORD, (void *) NULL, null_valist);
++      return;
++    }
++
++    sys_putItemBack (sp, LIST_FREE_WORD, itemp);
++
++    no_fault();
++    kmutex_unlock (&sctx->Lock);
++}
++
++static void
++sys_freeBlockItem (ELAN3_CTXT *ctxt, void *itemp)
++{
++    SYS_CTXT       *sctx = (SYS_CTXT *) ctxt->Private;
++    SYS_SWAP_SPACE *sp   = sctx->Swap;
++    SYS_BLOCK_ITEM *item = (SYS_BLOCK_ITEM *)itemp;
++    E3_uint32      *dest;
++    label_t       ljp;
++
++    kmutex_lock (&sctx->Lock);
++    
++    if (on_fault (&ljp))
++    {
++      no_fault();
++      kmutex_unlock (&sctx->Lock);
++      
++      sys_exception (ctxt, EXCEPTION_SWAP_FAULT, LIST_FREE_BLOCK, (void *) NULL, null_valist);
++      return;
++    }
++#ifdef DEBUG_PRINTF
++    dest = (E3_uint32 *) fuptr_noerr ((void **) &item->Pointer);
++
++    PRINTF2 (ctxt, DBG_SYSCALL, "sys_freeBlockItem: item=%p dest=%p\n", item, dest);
++    PRINTF4 (ctxt, DBG_SYSCALL, "                  %08x %08x %08x %08x\n",
++           fuword_noerr ((int *) &dest[0]), fuword_noerr ((int *) &dest[1]), 
++           fuword_noerr ((int *) &dest[2]), fuword_noerr ((int *) &dest[3]));
++    PRINTF4 (ctxt, DBG_SYSCALL, "                  %08x %08x %08x %08x\n",
++           fuword_noerr ((int *) &dest[4]), fuword_noerr ((int *) &dest[5]),
++           fuword_noerr ((int *) &dest[6]), fuword_noerr ((int *) &dest[7]));
++#endif
++
++    sys_putItemBack (sp, LIST_FREE_BLOCK, itemp);
++
++    no_fault();
++    kmutex_unlock (&sctx->Lock);
++}
++
++static void
++sys_putbackItem (ELAN3_CTXT *ctxt, int list, void *itemp)
++{
++    SYS_CTXT       *sctx = (SYS_CTXT *) ctxt->Private;
++    SYS_SWAP_SPACE *sp   = sctx->Swap;
++    label_t       ljp;
++
++    kmutex_lock (&sctx->Lock);
++    
++    if (on_fault (&ljp))
++    {
++      no_fault();
++      kmutex_unlock (&sctx->Lock);
++      
++      sys_exception (ctxt, EXCEPTION_SWAP_FAULT, list, (void *) NULL, null_valist);
++      return;
++    }
++
++    sys_putItemFront (sp, list, itemp);
++
++    no_fault();
++    kmutex_unlock (&sctx->Lock);
++}
++
++static int
++sys_countItems (ELAN3_CTXT *ctxt, int list)
++{
++    SYS_CTXT     *sctx  = (SYS_CTXT *) ctxt->Private;
++    SYS_SWAP_SPACE *sp    = sctx->Swap;
++    int                   count = 0;
++    void         *item;
++    label_t       ljb;
++
++    kmutex_lock (&sctx->Lock);
++    
++    if (on_fault (&ljb))
++    {
++      no_fault();
++      kmutex_unlock (&sctx->Lock);
++      sys_exception (ctxt, EXCEPTION_SWAP_FAULT, list, (void *) NULL, null_valist);
++      return (0);
++    }
++
++    for (item = (void *) fuptr_noerr ((void **) &sp->ItemListsHead[list]); 
++       item != NULL;
++       item = (void *) fuptr_noerr ((void **) item))
++    {
++      count++;
++    }
++
++    no_fault();
++    kmutex_unlock (&sctx->Lock);
++
++    return (count);
++}
++
++
++long sys_longTime;
++long sys_shortTime;
++int  sys_waitTicks;
++int  sys_maxBackoff;
++
++#define SYS_LONG_TIME         MAX((hz * 5) / 1000, 1)         /* 5 ms */
++#define SYS_SHORT_TIME                MAX((hz * 2) / 1000, 1)         /* 2 ms */
++#define SYS_WAIT_TICKS                MAX((hz * 1) / 1000, 1)         /* 1 ms  - backoff granularity */
++#define SYS_MAX_BACKOFF               MAX((hz * 5) / 1000, 1)         /* 5 ms  - max backoff for "nacked" packets*/
++#define SYS_TIMEOUT_BACKOFF   MAX((hz * 10) / 1000, 1)        /* 10 ms - backoff for output timeout (point to point) */
++#define SYS_BCAST_BACKOFF     MAX((hz * 50) / 1000, 1)        /* 50 ms - backoff for output timeout (broadcast) */
++#define SYS_NETERR_BACKOFF    MAX((hz * 10) / 1000, 1)        /* 10 ms - delay for network error in dma data */
++
++static void
++sys_backoffWait (ELAN3_CTXT *ctxt, int ticks)
++{
++    SYS_CTXT *sctx = (SYS_CTXT *) ctxt->Private;
++    long      t;
++
++    spin_lock (&sctx->WaitLock);
++
++    t = lbolt - sctx->Time;
++
++    if (sys_longTime   == 0) sys_longTime   = SYS_LONG_TIME;
++    if (sys_shortTime  == 0) sys_shortTime  = SYS_SHORT_TIME;
++    if (sys_waitTicks  == 0) sys_waitTicks  = SYS_WAIT_TICKS;
++    if (sys_maxBackoff == 0) sys_maxBackoff = SYS_MAX_BACKOFF;
++
++    if (t > sys_longTime)                                     /* It's a long time since the last trap */
++      sctx->Backoff = 0;                                      /* so set the backoff back down to 0 */
++
++    if (ticks)
++    {
++      PRINTF2 (ctxt, DBG_DPROC, "sys_backoffWait : Waiting - %d ticks [%lx]\n", ticks, t);
++      kcondvar_timedwait (&sctx->NetworkErrorWait, &sctx->WaitLock, NULL, lbolt + ticks);
++    }
++    else if (sctx->Armed)
++    {
++      if (t < sys_shortTime)                                  /* It's been a short time since the last */
++      {                                                       /* trap, so increase the backoff */
++          sctx->Backoff++;
++          
++          if (sctx->Backoff > sys_maxBackoff)
++              sctx->Backoff = sys_maxBackoff;
++      }
++
++      PRINTF2 (ctxt, DBG_DPROC, "sys_backoffWait : Waiting - %d [%lx]\n", sctx->Backoff, t);
++
++       if (sctx->Backoff)
++           kcondvar_timedwaitsig (&sctx->NetworkErrorWait, &sctx->WaitLock, NULL, lbolt + sctx->Backoff * sys_waitTicks);
++
++      sctx->Armed = 0;
++    }
++    else
++    {
++      PRINTF1 (ctxt, DBG_DPROC, "sys_backoffWait : Not Waiting - %d\n", sctx->Backoff);
++
++    }
++    sctx->Time = lbolt;
++
++    spin_unlock (&sctx->WaitLock);
++}
++
++static int
++trapSize (int proc)
++{
++    switch (proc)
++    {
++    case DMA_PROC:    return (sizeof (DMA_TRAP));
++    case THREAD_PROC: return (sizeof (THREAD_TRAP));
++    case COMMAND_PROC:        return (sizeof (COMMAND_TRAP));
++    case INPUT_PROC:  return (sizeof (INPUT_TRAP));
++    default:          return (0);
++    }
++}
++
++static int
++sys_exception (ELAN3_CTXT *ctxt, int type, int proc, void *trapp, va_list ap)
++{
++    SYS_CTXT *sctx  = (SYS_CTXT *) ctxt->Private;
++    int             res;
++
++    PRINTF2 (ctxt, DBG_SYSCALL, "sys_exception: type %d proc %d\n", type, proc);
++
++    switch (type)
++    {
++    case EXCEPTION_INVALID_ADDR:
++    {
++      E3_FaultSave_BE *faultSave = va_arg (ap, E3_FaultSave_BE *);
++      int              res       = va_arg (ap, int);
++      
++      sys_addException (sctx, type, proc, trapp, trapSize(proc), faultSave, res, 0);
++      break;
++    }
++    
++    case EXCEPTION_UNIMP_INSTR:
++    {
++      E3_uint32 instr = va_arg (ap, E3_uint32);
++      
++      sys_addException (sctx, type, proc, trapp, trapSize(proc), NULL, 0, instr);
++      break;
++    }
++    
++    case EXCEPTION_INVALID_PROCESS:
++    {
++      E3_uint32 vproc = va_arg (ap, E3_uint32);
++      int       res  = va_arg (ap, int);
++
++      switch (proc)
++      {
++      case DMA_PROC:
++          if (sctx->Flags & ELAN3_SYS_FLAG_DMA_BADVP)
++          {
++              DMA_TRAP *trap = (DMA_TRAP *) trapp;
++
++              if (trap->Desc.s.dma_direction != DMA_WRITE)
++                  trap->Desc.s.dma_srcEvent = trap->Desc.s.dma_destEvent;
++
++              trap->Desc.s.dma_direction       = DMA_WRITE;
++              trap->Desc.s.dma_size            = 0;
++              trap->Desc.s.dma_source          = (E3_Addr) 0;
++              trap->Desc.s.dma_dest            = (E3_Addr) 0;
++              trap->Desc.s.dma_destEvent       = (E3_Addr) 0;
++              trap->Desc.s.dma_destCookieVProc = 0;
++              trap->Desc.s.dma_srcCookieVProc  = 0;
++              
++              return (OP_IGNORE);
++          }
++          break;
++
++      case THREAD_PROC:
++          if (sctx->Flags & ELAN3_SYS_FLAG_THREAD_BADVP)
++          {
++              THREAD_TRAP *trap = (THREAD_TRAP *) trapp;
++
++              trap->TrapBits.s.PacketAckValue = E3_PAckError;
++              
++              return (OP_IGNORE);
++          }
++          break;
++      }
++          
++      sys_addException (sctx, type, proc, trapp, trapSize(proc), NULL, res, vproc);
++      break;
++    }
++    
++    case EXCEPTION_FAULTED:
++    {
++      E3_Addr addr = va_arg (ap, E3_Addr);
++
++      sys_addException (sctx, type, proc, trapp, trapSize(proc), NULL, 0, addr);
++      break;
++    }
++    
++    case EXCEPTION_QUEUE_OVERFLOW:
++    {
++      E3_FaultSave_BE *faultSave = va_arg (ap, E3_FaultSave_BE *);
++      int              trapType  = va_arg (ap, int);
++      
++      sys_addException (sctx, type, proc, trapp, trapSize(proc), faultSave, 0, trapType);
++      break;
++    }
++    
++    case EXCEPTION_COMMAND_OVERFLOW:
++    {
++      int count = va_arg (ap, int);
++      
++      sys_addException (sctx, type, proc, trapp, trapSize(proc), NULL, 0, count);
++      break;
++    }
++    
++    case EXCEPTION_CHAINED_EVENT:
++    {
++      E3_Addr addr = va_arg (ap, E3_Addr);
++      
++      sys_addException (sctx, type, proc, trapp, trapSize(proc), NULL, 0, addr);
++      break;
++    }
++    
++    case EXCEPTION_DMA_RETRY_FAIL:
++    case EXCEPTION_PACKET_TIMEOUT:
++      if (proc != DMA_PROC)
++          sys_backoffWait (ctxt, SYS_TIMEOUT_BACKOFF);
++      else
++      {
++          DMA_TRAP *trap = (DMA_TRAP *) trapp;
++          
++          if (sctx->Flags & ELAN3_SYS_FLAG_DMAFAIL)
++          {
++              E3_BlockCopyEvent *event;
++
++              if (trap->Desc.s.dma_direction != DMA_WRITE)
++                  trap->Desc.s.dma_srcEvent = trap->Desc.s.dma_destEvent;
++
++              /* change the source word to be E3_EVENT_FAILED */
++              if ((event = (E3_BlockCopyEvent *) elan3mmu_mainaddr (ctxt->Elan3mmu, trap->Desc.s.dma_srcEvent)) == NULL)
++              {
++                  sys_addException (sctx, type, proc, trapp, trapSize(proc), NULL, 0, 0);
++                  break;
++              }
++
++              suword (&event->ev_Source, E3_EVENT_FAILED);
++              wmb(); mmiob();
++              
++              trap->Desc.s.dma_direction       = DMA_WRITE;
++              trap->Desc.s.dma_size            = 0;
++              trap->Desc.s.dma_source          = (E3_Addr) 0;
++              trap->Desc.s.dma_dest            = (E3_Addr) 0;
++              trap->Desc.s.dma_destEvent       = (E3_Addr) 0;
++              trap->Desc.s.dma_destCookieVProc = 0;
++              trap->Desc.s.dma_srcCookieVProc  = 0;
++              
++              return (OP_IGNORE);
++          }
++
++          if (type == EXCEPTION_DMA_RETRY_FAIL)
++              sys_backoffWait (ctxt, 0);
++          else
++          {
++              ELAN_LOCATION location;
++
++              krwlock_read (&ctxt->VpLock);
++              location = ProcessToLocation (ctxt, NULL, trap->Desc.s.dma_direction == DMA_WRITE ? 
++                                            trap->Desc.s.dma_destVProc : trap->Desc.s.dma_srcVProc, NULL);
++              krwlock_done (&ctxt->VpLock);
++              
++              sys_backoffWait (ctxt, location.loc_node == ELAN3_INVALID_NODE ? SYS_BCAST_BACKOFF : SYS_TIMEOUT_BACKOFF);
++          }
++      }
++      return (OP_IGNORE);
++      
++    case EXCEPTION_NETWORK_ERROR:
++    {
++      INPUT_TRAP       *trap  = (INPUT_TRAP *) trapp;
++      NETERR_RESOLVER **rvpp  = va_arg (ap, NETERR_RESOLVER **);
++
++      ASSERT (trap->State == CTXT_STATE_NETWORK_ERROR);
++
++      if (! (sctx->Flags & ELAN3_SYS_FLAG_NETERR) && (trap->DmaIdentifyTransaction || trap->ThreadIdentifyTransaction))
++      {
++          if ((*rvpp) != (NETERR_RESOLVER *) NULL)
++              res = (*rvpp)->Status;
++          else if ((res = QueueNetworkErrorResolver (ctxt, trap, rvpp)) == ESUCCESS)
++          {
++              /* Successfully queued the network error resolver */
++              return (OP_HANDLED);
++          }
++
++          /* network error resolution has failed - either a bad cookie or */
++          /* an rpc error has occured */
++          sys_addException (sctx, type, proc, trapp, trapSize(proc), NULL, res, 0);
++      }
++      else
++      {
++          /* Must be an overlaped dma packet. Must wait long enough to
++           * ensure that the sending dma'er has tried to send the next
++           * packet and had it discarded. In the real world this should
++           * be greater than an output timeout. (About 8mSec) */
++          
++          sys_backoffWait (ctxt, SYS_NETERR_BACKOFF);
++          
++          /* set this inputter state to be ok, since we've been called 
++           * by the lwp it will lower the context filter for us, so 
++           * re-enabling the inputter,  note we don't need to execute
++           * any of the packet since the dma process will re-transmit
++           * it after receiving a nack for the next packet */
++          trap->State = CTXT_STATE_OK;
++          
++          return (OP_HANDLED);
++      }
++      break;
++    }
++    
++    default:
++      sys_addException (sctx, type, proc, trapp, trapSize(proc), NULL, 0, 0);
++      break;
++    }
++    
++    if (type != EXCEPTION_DEBUG)
++#ifdef LINUX
++#ifdef NO_NPTL
++      psignal (CURPROC()->p_opptr, sctx->signal);
++#else
++      psignal (CURPROC()->parent, sctx->signal);
++#endif
++#else
++      psignal (CURPROC(), sctx->signal);
++#endif
++    return (OP_HANDLED);
++}
++
++static int
++sys_event (ELAN3_CTXT *ctxt, E3_uint32 cookie, int flag)
++{
++    SYS_CTXT *sctx = (SYS_CTXT *) ctxt->Private;
++
++    cookie_fire_cookie (sctx->Table, cookie);
++
++    return (OP_HANDLED); 
++}
++
++static void
++sys_swapin (ELAN3_CTXT *ctxt)
++{
++    PRINTF0 (ctxt, DBG_SYSCALL, "sys_swapin\n");
++}
++
++static void
++sys_swapout (ELAN3_CTXT *ctxt)
++{
++    PRINTF0 (ctxt, DBG_SYSCALL, "sys_swapout\n");
++}
++
++static void
++sys_freePrivate (ELAN3_CTXT *ctxt)
++{
++    SYS_CTXT *sctx = (SYS_CTXT *) ctxt->Private;
++
++    cookie_free_table (sctx->Table);
++
++    kmutex_destroy (&sctx->Lock);
++    spin_lock_destroy (&sctx->WaitLock);
++    kcondvar_destroy (&sctx->NetworkErrorWait);
++
++    KMEM_FREE (sctx, sizeof (SYS_CTXT));
++    ctxt->Private = NULL;
++}
++
++static int
++sys_checkThisDma (ELAN3_CTXT *ctxt, NETERR_FIXUP *nef, E3_DMA *dma)
++{
++    E3_DmaType type;
++    E3_uint32  cookie;
++    E3_uint32  cvproc;
++    int              ignore;
++    int              match;
++
++    type.type = fuword_noerr ((int *) &dma->dma_type);
++
++    if (type.s.direction == DMA_WRITE)
++    {
++      cookie = fuword_noerr ((int *) &dma->dma_srcCookieVProc);
++      cvproc = fuword_noerr ((int *) &dma->dma_destCookieVProc);
++    }
++    else
++    {
++      cookie = fuword_noerr ((int *) &dma->dma_destCookieVProc);
++      cvproc = fuword_noerr ((int *) &dma->dma_srcCookieVProc);
++    }
++
++    PRINTF5 (ctxt, DBG_NETERR, "sys_checkThisDma: dir = %d cookie = %08x cvproc = %08x CookieVProc %08x DstProcess %04x\n",
++           type.s.direction, cookie, cvproc, nef->Message.CookieVProc, nef->Message.DstProcess);
++
++    /* A DMA matches a network errror fixup if it's going to the right place (or is a broadcast)
++     * and the approriate cookie matches, except that we ignore DMA's which don't have a destEvent
++     * since they don't have any atomic behaviour (though they still send the identify) */
++
++    ignore = (type.s.direction == DMA_WRITE && cookie == 0 &&
++            fuword_noerr ((int *) &dma->dma_destEvent) == 0);
++    match  = (nef->Message.CookieVProc == cookie &&
++            (nef->Message.DstProcess == (cvproc & DMA_PROCESS_MASK) || nef->Message.WaitForEop));
++
++    PRINTF2 (ctxt, DBG_NETERR, "  -> %s %s\n", ignore ? "ignore" : match ? "matched" : "not-matched", nef->Message.WaitForEop ? "wait for eop" : "");
++
++    if (match && !ignore && !nef->Message.WaitForEop)
++    {
++      PRINTF0 (ctxt, DBG_NETERR, "sys_checkThisDma: nuking the dma\n");
++
++      /* NOTE - we access the dma descriptor backwards since it could exist in sdram */
++      if (type.s.direction != DMA_WRITE)
++          suword_noerr ((int *) &dma->dma_srcEvent, 0);
++
++      suword_noerr ((int *) &dma->dma_destEvent, 0);
++      suword_noerr ((int *) &dma->dma_dest,      0);
++      suword_noerr ((int *) &dma->dma_source,    0);
++      suword_noerr ((int *) &dma->dma_size,      0);
++
++      if (type.s.direction != DMA_WRITE)
++          suword_noerr ((int *) &dma->dma_type, fuword_noerr ((int *) &dma->dma_type) & E3_DMA_CONTEXT_MASK);
++
++      wmb(); mmiob();
++    }
++
++    return (match && !ignore);
++}
++
++static int
++sys_fixupNetworkError (ELAN3_CTXT *ctxt, NETERR_FIXUP *nef)
++{
++    SYS_CTXT       *sctx    = (SYS_CTXT *) ctxt->Private;
++    SYS_SWAP_SPACE *sp      = sctx->Swap;
++    int                   matched = 0;
++    SYS_WORD_ITEM  *wordp;
++    SYS_BLOCK_ITEM *blockp;
++    label_t       ljb;
++    int                   res;
++
++    PRINTF3 (ctxt, DBG_NETERR, "sys_fixupnetworkError %08x %08x %08x\n", 
++           nef->Message.CookieAddr, nef->Message.CookieVProc, nef->Message.NextCookie);
++
++    if (nef->Message.CookieAddr == (E3_Addr) 0)                       /* It's a DMA which requires fixing up */
++    {
++      kmutex_lock (&sctx->Lock);
++
++      if (on_fault (&ljb))
++          res = EFAULT;
++      else
++      {
++          /* scan the dma ptr list */
++          for (wordp = (SYS_WORD_ITEM *) fuptr_noerr ((void **) &sp->ItemListsHead[LIST_DMA_PTR]);
++               wordp != NULL; 
++               wordp = (SYS_WORD_ITEM *) fuptr_noerr ((void **) &wordp->Next))
++          {
++              E3_uint32 value = fuword_noerr ((int *) &wordp->Value);
++              E3_DMA    *dma  = (E3_DMA *) elan3mmu_mainaddr (ctxt->Elan3mmu, value);
++
++              PRINTF3 (ctxt, DBG_NETERR, "sys_fixupnetworkError: check block item %p Value %08x dma %p\n", wordp, value, dma);
++
++              matched += sys_checkThisDma (ctxt, nef, dma);
++          }
++      
++          /* scan the dma desc list */
++          for (blockp = (SYS_BLOCK_ITEM *) fuptr_noerr ((void **) &sp->ItemListsHead[LIST_DMA_DESC]);
++               blockp != NULL; 
++               blockp = (SYS_BLOCK_ITEM *) fuptr_noerr ((void **) &blockp->Next))
++          {
++              E3_DMA *dma = (E3_DMA *) fuptr_noerr ((void *) &blockp->Pointer);
++              
++              PRINTF2 (ctxt, DBG_NETERR, "sys_fixupnetworkError: check block item %p Pointer %p\n", blockp, dma);
++
++              matched += sys_checkThisDma (ctxt, nef, dma);
++          }
++          
++          /* If we've still not found it, then check the command port item */
++          /* it MUST be present as a command waiting to be executed, as */
++          /* otherwise it could have already happened and we will claim to */
++          /* have found it, but not realy */
++          if (ctxt->CommandPortItem != NULL)
++          {
++              E3_DMA *dma = (E3_DMA *) fuptr_noerr ((void *) &((SYS_BLOCK_ITEM *) ctxt->CommandPortItem)->Pointer);
++
++              if (sys_checkThisDma (ctxt, nef, dma))
++              {
++                  printk ("!!! it's the command port item - need to ensure that the command exists\n");
++                  matched++;
++              }
++          }
++
++          res = matched ? ESUCCESS : ESRCH;
++      }
++      no_fault();
++      kmutex_unlock (&sctx->Lock);
++
++      if (matched > 1)
++          ElanException (ctxt, EXCEPTION_COOKIE_ERROR, DMA_PROC, NULL, NULL, nef->Message.CookieVProc);
++    }
++    else                                                      /* It's a thread which requires fixing up */
++    {
++      E3_int32  *cookiePtr = (E3_int32 *) elan3mmu_mainaddr (ctxt->Elan3mmu, nef->Message.CookieAddr);
++      E3_uint32  curval    = fuword_noerr (cookiePtr);
++
++      if (curval == nef->Message.CookieVProc)         /* thread doesn't think it's been done */
++      {
++          if (! nef->Message.WaitForEop)
++          {
++              suword_noerr (cookiePtr, nef->Message.NextCookie);
++              mb(); mmiob();
++          }
++          
++          res = ESUCCESS;
++      }
++      else                                                    /* thread thinks that it's been executed */
++      {
++          res = ESRCH;
++      }
++    }
++    
++    CompleteNetworkErrorFixup (ctxt, nef, res);
++
++    return (OP_HANDLED);
++}
++
++
++static int
++sys_startFaultCheck (ELAN3_CTXT *ctxt)
++{
++    return (0);
++}
++
++static void
++sys_endFaultCheck (ELAN3_CTXT *ctxt)
++{
++    wmb();
++}
++
++static E3_uint8
++sys_load8 (ELAN3_CTXT *ctxt, E3_Addr addr)
++{
++    E3_uint8 *maddr = (E3_uint8 *) elan3mmu_mainaddr (ctxt->Elan3mmu, addr);
++
++    return (fubyte_noerr (maddr));
++}
++
++static void
++sys_store8 (ELAN3_CTXT *ctxt, E3_Addr addr, E3_uint8 val)
++{
++    E3_uint8 *maddr = (E3_uint8 *) elan3mmu_mainaddr (ctxt->Elan3mmu, addr);
++
++    subyte_noerr (maddr, val);
++    wmb(); mmiob();
++}
++
++static E3_uint16
++sys_load16 (ELAN3_CTXT *ctxt, E3_Addr addr)
++{
++    E3_uint16 *maddr = (E3_uint16 *) elan3mmu_mainaddr (ctxt->Elan3mmu, addr);
++
++    return (fusword_noerr (maddr));
++}
++
++static void
++sys_store16 (ELAN3_CTXT *ctxt, E3_Addr addr, E3_uint16 val)
++{
++    E3_uint16 *maddr = (E3_uint16 *) elan3mmu_mainaddr (ctxt->Elan3mmu, addr);
++
++    susword_noerr (maddr, val);
++    wmb(); mmiob();
++}
++
++static E3_uint32
++sys_load32 (ELAN3_CTXT *ctxt, E3_Addr addr)
++{
++    E3_uint32 *maddr = (E3_uint32 *) elan3mmu_mainaddr (ctxt->Elan3mmu, addr);
++
++    return (fuword_noerr (maddr));
++}
++
++static void
++sys_store32 (ELAN3_CTXT *ctxt, E3_Addr addr, E3_uint32 val)
++{
++    E3_uint32 *maddr = (E3_uint32 *) elan3mmu_mainaddr (ctxt->Elan3mmu, addr);
++
++    suword_noerr (maddr, val);
++    wmb(); mmiob();
++}
++
++static E3_uint64
++sys_load64 (ELAN3_CTXT *ctxt, E3_Addr addr)
++{
++    E3_uint64 *maddr = (E3_uint64 *) elan3mmu_mainaddr (ctxt->Elan3mmu, addr);
++
++    return (fulonglong_noerr ((long long *) maddr));
++}
++
++static void
++sys_store64 (ELAN3_CTXT *ctxt, E3_Addr addr, E3_uint64 val)
++{
++    E3_uint64 *maddr = (E3_uint64 *) elan3mmu_mainaddr (ctxt->Elan3mmu, addr);
++
++    sulonglong_noerr ((long long *) maddr, val);
++    wmb(); mmiob();
++}
++
++
++void
++sys_addException (SYS_CTXT *sctx, int type, int proc, caddr_t trapp, int size,
++                E3_FaultSave_BE *faultSave, u_long res, u_long value)
++{
++    SYS_EXCEPTION      *ex_ptr;
++    int                       front;
++    int                       back;
++    int                       count;
++    label_t           ljp;
++
++    PRINTF4 (DBG_DEVICE, DBG_FN, "sys_addException: type %d proc %d res %ld value %ld\n",
++           type, proc, res, value);
++
++    KMEM_ZALLOC (ex_ptr, SYS_EXCEPTION *, sizeof  (SYS_EXCEPTION), TRUE);
++
++    if (ex_ptr != NULL)
++    {
++      bzero ((caddr_t) ex_ptr, sizeof (SYS_EXCEPTION));
++
++      ex_ptr->Type  = type;
++      ex_ptr->Proc  = proc;
++      ex_ptr->Res   = res;
++      ex_ptr->Value = value;
++      
++      if (trapp && size)
++          bcopy (trapp, (caddr_t) &ex_ptr->Union, size);
++      if (faultSave)
++          bcopy ((caddr_t) faultSave, (caddr_t) &ex_ptr->FaultArea, sizeof (E3_FaultSave_BE));
++    }
++
++    kmutex_lock (&sctx->Lock);
++    if (! on_fault (&ljp))
++    {
++      front = fuword_noerr (&sctx->Exceptions->Front);
++      back  = fuword_noerr (&sctx->Exceptions->Back);
++      count = fuword_noerr (&sctx->Exceptions->Count);
++
++      if (count <= 0 || front < 0 || back < 0 || front >= count || back >= count)
++          suword_noerr (&sctx->Exceptions->Overflow, fuword_noerr (&sctx->Exceptions->Overflow) + 1);
++      else if (((front+1) % count ) == back)
++          suword_noerr (&sctx->Exceptions->Overflow, fuword_noerr (&sctx->Exceptions->Overflow) + 1);
++      else
++      {
++          if (ex_ptr != NULL)
++              copyout_noerr ((caddr_t) ex_ptr, (caddr_t) &sctx->Exceptions->Exceptions[front], sizeof (SYS_EXCEPTION));
++          else
++          {
++              suword_noerr (&sctx->Exceptions->Exceptions[front].Type, EXCEPTION_ENOMEM);
++              suword_noerr (&sctx->Exceptions->Exceptions[front].Proc, 0);
++          }
++          suword_noerr (&sctx->Exceptions->Front, (front + 1) % count);
++      }
++
++      /* always reset the magic number in case it's been overwritten */
++      /* so that 'edb' can find the exception page in the core file */
++      suword_noerr (&sctx->Exceptions->Magic, SYS_EXCEPTION_MAGIC);
++    }
++    no_fault();
++    kmutex_unlock (&sctx->Lock);
++    
++    if (ex_ptr != NULL)
++      KMEM_FREE (ex_ptr, sizeof  (SYS_EXCEPTION));
++}
++
++int
++sys_getException (SYS_CTXT *sctx, SYS_EXCEPTION *ex)
++{
++    int     front;
++    int     back;
++    int     count;
++    int     res;
++    label_t ljp;
++
++    if (sctx->Exceptions == NULL)
++      return (EINVAL);
++
++    kmutex_lock (&sctx->Lock);
++    if (on_fault (&ljp))
++    {
++      no_fault();
++      kmutex_unlock (&sctx->Lock);
++      return (EFAULT);
++    }
++    
++    front = fuword_noerr (&sctx->Exceptions->Front);
++    back  = fuword_noerr (&sctx->Exceptions->Back);
++    count = fuword_noerr (&sctx->Exceptions->Count);
++
++    if (count <= 0 || front < 0 || back < 0 || front >= count || back >= count || back == front)
++      res = EINVAL;
++    else
++    {
++      copyin_noerr ((caddr_t) &sctx->Exceptions->Exceptions[back], (caddr_t) ex, sizeof (SYS_EXCEPTION));
++      suword_noerr (&sctx->Exceptions->Back, (back+1) % count);
++
++      res = ESUCCESS;
++    }
++    no_fault();
++    kmutex_unlock (&sctx->Lock);
++
++    return (res);
++}
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan3/eventcookie.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan3/eventcookie.c    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan3/eventcookie.c 2005-06-01 23:12:54.585441232 -0400
+@@ -0,0 +1,324 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: eventcookie.c,v 1.7 2003/08/13 10:03:03 fabien Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/os/eventcookie.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elanvp.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanctxt.h>
++#include <elan3/elandebug.h>
++#include <elan3/urom_addrs.h>
++#include <elan3/thread.h>
++#include <elan3/vmseg.h>
++
++static EVENT_COOKIE_TABLE *cookie_tables;
++static spinlock_t        cookie_table_lock;
++
++/*
++ * cookie_drop_entry:
++ *   drop the reference to a cookie held 
++ *   by the cookie table
++ */
++static void
++cookie_drop_entry (EVENT_COOKIE_ENTRY *ent)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&ent->ent_lock, flags);
++    if (--ent->ent_ref != 0)
++    {
++      ent->ent_fired = ent->ent_cookie;
++      kcondvar_wakeupall (&ent->ent_wait, &ent->ent_lock);
++
++      spin_unlock_irqrestore (&ent->ent_lock, flags);
++    }
++    else
++    {
++      spin_unlock_irqrestore (&ent->ent_lock, flags);
++
++      spin_lock_destroy (&ent->ent_lock);
++      kcondvar_destroy (&ent->ent_wait);
++
++      KMEM_FREE (ent, sizeof (EVENT_COOKIE_ENTRY));
++    }
++}
++
++void
++cookie_init()
++{
++    spin_lock_init (&cookie_table_lock);
++}
++
++void
++cookie_fini()
++{
++    spin_lock_destroy (&cookie_table_lock);
++}
++
++EVENT_COOKIE_TABLE *
++cookie_alloc_table (unsigned long task, unsigned long handle)
++{
++    EVENT_COOKIE_TABLE *tbl, *ntbl;
++
++    KMEM_ZALLOC (ntbl, EVENT_COOKIE_TABLE *, sizeof (EVENT_COOKIE_TABLE), TRUE);
++
++    if (ntbl == NULL)
++      return (NULL);
++
++    spin_lock (&cookie_table_lock);
++    
++    for (tbl = cookie_tables; tbl; tbl = tbl->tbl_next)
++      if (tbl->tbl_task == task && tbl->tbl_handle == handle)
++          break;
++    
++    if (tbl != NULL)
++      tbl->tbl_ref++;
++    else
++    {
++      spin_lock_init (&ntbl->tbl_lock);
++
++      ntbl->tbl_task    = task;
++      ntbl->tbl_handle  = handle;
++      ntbl->tbl_ref     = 1;
++      ntbl->tbl_entries = NULL;
++
++      if ((ntbl->tbl_next = cookie_tables) != NULL)
++          cookie_tables->tbl_prev = ntbl;
++      cookie_tables = ntbl;
++      ntbl->tbl_prev = NULL;
++    }
++    spin_unlock (&cookie_table_lock);
++
++    if (tbl == NULL)
++      return (ntbl);
++    else
++    {
++      KMEM_FREE (ntbl, sizeof (EVENT_COOKIE_TABLE));
++      return (tbl);
++    }    
++}
++
++void
++cookie_free_table (EVENT_COOKIE_TABLE *tbl)
++{
++    EVENT_COOKIE_ENTRY *ent;
++
++    spin_lock (&cookie_table_lock);
++    if (tbl->tbl_ref > 1)
++    {
++      tbl->tbl_ref--;
++      spin_unlock (&cookie_table_lock);
++      return;
++    }
++    
++    if (tbl->tbl_prev)
++      tbl->tbl_prev->tbl_next = tbl->tbl_next;
++    else
++      cookie_tables = tbl->tbl_next;
++    if (tbl->tbl_next)
++      tbl->tbl_next->tbl_prev = tbl->tbl_prev;
++    
++    spin_unlock (&cookie_table_lock);
++    
++    /* NOTE - table no longer visible to other threads
++     *        no need to aquire tbl_lock */
++    while ((ent = tbl->tbl_entries) != NULL)
++    {
++      if ((tbl->tbl_entries = ent->ent_next) != NULL)
++          ent->ent_next->ent_prev = NULL;
++      
++      cookie_drop_entry (ent);
++    }
++    spin_lock_destroy (&tbl->tbl_lock);
++
++    KMEM_FREE (tbl, sizeof (EVENT_COOKIE_TABLE));
++}
++
++int
++cookie_alloc_cookie (EVENT_COOKIE_TABLE *tbl, EVENT_COOKIE cookie)
++{
++    EVENT_COOKIE_ENTRY *ent, *nent;
++    unsigned long flags;
++
++    KMEM_ZALLOC (nent, EVENT_COOKIE_ENTRY *, sizeof (EVENT_COOKIE_ENTRY), TRUE);
++    
++    spin_lock_irqsave (&tbl->tbl_lock, flags);
++    for (ent = tbl->tbl_entries; ent; ent = ent->ent_next)
++      if (ent->ent_cookie == cookie)
++          break;
++
++    if (ent == NULL)
++    {
++      kcondvar_init (&nent->ent_wait);
++      spin_lock_init (&nent->ent_lock);
++
++      nent->ent_ref    = 1;
++      nent->ent_cookie = cookie;
++
++      if ((nent->ent_next = tbl->tbl_entries) != NULL)
++          tbl->tbl_entries->ent_prev = nent;
++      tbl->tbl_entries = nent;
++      nent->ent_prev = NULL;
++    }
++    spin_unlock_irqrestore (&tbl->tbl_lock, flags);
++
++    if (ent == NULL)
++      return (ESUCCESS);
++    else
++    {
++      KMEM_FREE (nent, sizeof (EVENT_COOKIE_ENTRY));
++      return (EINVAL);
++    }
++}
++
++int
++cookie_free_cookie (EVENT_COOKIE_TABLE *tbl, EVENT_COOKIE cookie)
++{
++    EVENT_COOKIE_ENTRY *ent;
++    unsigned long flags;
++
++    spin_lock_irqsave (&tbl->tbl_lock, flags);
++    for (ent = tbl->tbl_entries; ent; ent = ent->ent_next)
++      if (ent->ent_cookie == cookie)
++          break;
++    
++    if (ent == NULL)
++    {
++      spin_unlock_irqrestore (&tbl->tbl_lock, flags);
++      return (EINVAL);
++    }
++
++    if (ent->ent_prev == NULL)
++      tbl->tbl_entries = ent->ent_next;
++    else
++      ent->ent_prev->ent_next = ent->ent_next;
++
++    if (ent->ent_next != NULL)
++      ent->ent_next->ent_prev = ent->ent_prev;
++    
++    spin_unlock_irqrestore (&tbl->tbl_lock, flags);
++
++    cookie_drop_entry (ent);
++
++    return (ESUCCESS);
++}
++
++/*
++ * cookie_fire_cookie:
++ *    fire the cookie - this is called from the event interrupt.
++ */
++int
++cookie_fire_cookie (EVENT_COOKIE_TABLE *tbl, EVENT_COOKIE cookie)
++{
++    EVENT_COOKIE_ENTRY *ent;
++    unsigned long flags;
++
++    spin_lock_irqsave (&tbl->tbl_lock, flags);
++    for (ent = tbl->tbl_entries; ent; ent = ent->ent_next)
++      if (ent->ent_cookie == cookie)
++          break;
++    
++    if (ent == NULL)
++    {
++      spin_unlock_irqrestore (&tbl->tbl_lock, flags);
++      return (EINVAL);
++    }
++          
++    spin_lock (&ent->ent_lock);
++    ent->ent_fired = cookie;
++    kcondvar_wakeupall (&ent->ent_wait, &ent->ent_lock);
++    spin_unlock (&ent->ent_lock);
++
++    spin_unlock_irqrestore (&tbl->tbl_lock, flags);
++
++    return (ESUCCESS);
++}    
++
++/*
++ * cookie_wait_cookie:
++ *    deschedule on a cookie if it has not already fired.
++ *    note - if the cookie is removed from the table, then
++ *           we free it off when we're woken up.
++ */
++int
++cookie_wait_cookie (EVENT_COOKIE_TABLE *tbl, EVENT_COOKIE cookie)
++{
++    EVENT_COOKIE_ENTRY *ent;
++    unsigned long flags;
++    
++    spin_lock_irqsave (&tbl->tbl_lock, flags);
++    for (ent = tbl->tbl_entries; ent; ent = ent->ent_next)
++      if (ent->ent_cookie == cookie)
++          break;
++    
++    if (ent == NULL)
++    {
++      spin_unlock_irqrestore (&tbl->tbl_lock, flags);
++      return (EINVAL);
++    }
++
++    spin_lock (&ent->ent_lock);
++    spin_unlock (&tbl->tbl_lock);
++
++    if (ent->ent_fired != 0)
++    {
++      spin_unlock_irqrestore (&ent->ent_lock, flags);
++      return (ESUCCESS);
++    }
++
++    ent->ent_ref++;
++    kcondvar_waitsig (&ent->ent_wait, &ent->ent_lock, &flags);
++    
++    if (--ent->ent_ref > 0)
++      spin_unlock_irqrestore (&ent->ent_lock, flags);
++    else
++    {
++      spin_unlock_irqrestore (&ent->ent_lock, flags);
++      
++      spin_lock_destroy (&ent->ent_lock);
++      kcondvar_destroy (&ent->ent_wait);
++
++      KMEM_FREE (ent, sizeof (EVENT_COOKIE_ENTRY));
++    }
++    return (ESUCCESS);
++}
++
++int
++cookie_arm_cookie (EVENT_COOKIE_TABLE *tbl, EVENT_COOKIE cookie)
++{
++    EVENT_COOKIE_ENTRY *ent;
++    unsigned long flags;
++
++    spin_lock_irqsave (&tbl->tbl_lock, flags);
++    for (ent = tbl->tbl_entries; ent; ent = ent->ent_next)
++      if (ent->ent_cookie == cookie)
++          break;
++    
++    if (ent == NULL)
++    {
++      spin_unlock_irqrestore (&tbl->tbl_lock, flags);
++      return (EINVAL);
++    }
++          
++    spin_lock (&ent->ent_lock);
++    ent->ent_fired = 0;
++    spin_unlock (&ent->ent_lock);
++
++    spin_unlock_irqrestore (&tbl->tbl_lock, flags);
++
++    return (ESUCCESS);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan3/iproc.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan3/iproc.c  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan3/iproc.c       2005-06-01 23:12:54.586441080 -0400
+@@ -0,0 +1,925 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: iproc.c,v 1.47 2003/09/24 13:57:25 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/os/iproc.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elanvp.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanctxt.h>
++#include <elan3/elandebug.h>
++#include <elan3/urom_addrs.h>
++#include <elan3/trtype.h>
++#include <elan3/vmseg.h>
++
++
++static int TrSizeTable[] = {0, 8, 16, 32, 64};
++
++static void  ConvertTransactionToSetEvent (ELAN3_CTXT *ctxt, E3_IprocTrapHeader_BE *hdrp, E3_Addr Addr);
++static void  SimulateBlockWrite  (ELAN3_CTXT *ctxt, E3_IprocTrapHeader_BE *hdrp, E3_IprocTrapData_BE *datap);
++static void  SimulateWriteWord   (ELAN3_CTXT *ctxt, E3_IprocTrapHeader_BE *hdrp, E3_IprocTrapData_BE *datap);
++static void  SimulateWriteDWord  (ELAN3_CTXT *ctxt, E3_IprocTrapHeader_BE *hdrp, E3_IprocTrapData_BE *datap);
++static void  SimulateTraceRoute  (ELAN3_CTXT *ctxt, E3_IprocTrapHeader_BE *hdrp, E3_IprocTrapData_BE *datap);
++static void  BumpInputterStats (ELAN3_DEV *dev, E3_IprocTrapHeader_BE *hdrp);
++
++void
++HandleIProcTrap (ELAN3_DEV           *dev, 
++               int                 Channel,
++               E3_uint32           Pend,
++               sdramaddr_t         FaultSaveOff,
++               sdramaddr_t         TransactionsOff,
++               sdramaddr_t         DataOff)
++{
++    E3_IprocTrapHeader_BE Transaction0;
++    ELAN3_CTXT                 *ctxt;
++    INPUT_TRAP           *trap;
++    register int          i;
++
++    /*
++     * Read the 1st set of transactions, so we can determine the 
++     * context for the trap 
++     */
++    elan3_sdram_copyq_from_sdram (dev, TransactionsOff, (void *) &Transaction0, 16);
++    
++    BumpStat (dev, IProcTraps);
++    BumpInputterStats (dev, &Transaction0);
++
++    if (Transaction0.s.TrTypeCntx.s.TypeCntxInvalid)
++    {
++      /*
++       * The context is not valid. This will occur if the packet
++       * trapped for an EopError with no IdentTrans or an error corrupted the context
++       * giving a CRC error on the first transaction and the Ack had not been returned.
++       */
++      if (Transaction0.s.TrTypeCntx.s.LastTrappedTrans)
++      {
++          PRINTF0 (DBG_DEVICE, DBG_IPROC, "iproc: Error on EOP without a good context, ignoring trap\n");
++      }
++      else
++      {
++          /* Check that only crap has been received.  If not then die. */
++          if (! Transaction0.s.IProcTrapStatus.s.BadLength &&
++              (Transaction0.s.IProcTrapStatus.Status & CRC_MASK) == CRC_STATUS_GOOD)
++          {
++              printk ("iproc: Did not have a valid context for the trap area.\n");
++              printk ("iproc: TrTypeCntx=%x TrAddr=%x TrData0=%x IProcTrapStatus=%x\n",
++                       Transaction0.s.TrTypeCntx.TypeContext, Transaction0.s.TrAddr,
++                       Transaction0.s.TrData0, Transaction0.s.IProcTrapStatus.Status);
++              panic ("elan3: iproc did not have a valid context");
++              /* NOTREACHED */
++          }
++          PRINTF0 (DBG_DEVICE, DBG_IPROC, "iproc: First transaction is bad, ignoring trap\n");
++      }
++    }
++    else
++    {
++      ctxt = ELAN3_DEV_CTX_TABLE(dev, Transaction0.s.TrTypeCntx.s.Context);
++      
++      if (ctxt == NULL)
++      {
++          PRINTF1 (DBG_DEVICE, DBG_INTR, "HandleIProcTrap: context %x invalid\n", 
++                   Transaction0.s.TrTypeCntx.s.Context);
++
++          BumpStat (dev, InvalidContext);
++      }
++      else
++      {
++          trap = (Channel == 0) ? &ctxt->Input0Trap : &ctxt->Input1Trap;
++
++          ASSERT (trap->State == CTXT_STATE_OK);
++          
++          trap->Transactions[0] = Transaction0;
++
++          PRINTF1 (ctxt, DBG_INTR, "HandleIProcTrap: %s\n", IProcTrapString (&trap->Transactions[0], NULL));
++          /*
++           * Copy the rest of the transactions into the trap area.
++           */
++          for (i = 0; !(trap->Transactions[i].s.TrTypeCntx.s.LastTrappedTrans);)
++          {
++              if (++i >= MAX_TRAPPED_TRANS)
++              {
++                  trap->Overflow = 1;
++                  break;
++              }
++
++              elan3_sdram_copyq_from_sdram (dev, TransactionsOff + i*sizeof (E3_IprocTrapHeader), (void *) &trap->Transactions[i], 16);
++
++              PRINTF1 (ctxt, DBG_INTR, "                 %s\n", IProcTrapString (&trap->Transactions[i], NULL));
++
++              BumpInputterStats (dev, &trap->Transactions[i]);
++          }
++          
++          /*
++           * Remember the number of transactions we've copied.
++           */
++          trap->NumTransactions = i+1;
++
++          PRINTF1 (ctxt, DBG_INTR, "                 NumTransactions = %d\n", trap->NumTransactions);
++          
++          /*
++           * Copy all the data blocks in one go to let the Elan prefetcher work 
++           */
++          elan3_sdram_copyq_from_sdram (dev, DataOff, trap->DataBuffers, trap->NumTransactions*sizeof (E3_IprocTrapData));
++
++          /*
++           * Copy fault save area and clear out for next time round.
++           */
++          elan3_sdram_copyq_from_sdram (dev, FaultSaveOff, (void *) &trap->FaultSave, 16);
++          elan3_sdram_zeroq_sdram (dev, FaultSaveOff, 16);
++
++          if (ELAN3_OP_IPROC_TRAP (ctxt, trap, Channel) == OP_DEFER)
++          {
++              /*
++               * Mark the trap as valid and set the inputter state to 
++               * raise the context filter.
++               */
++              trap->State = CTXT_STATE_TRAPPED;
++              kcondvar_wakeupone (&ctxt->Wait, &dev->IntrLock);
++              
++              SetInputterStateForContext (ctxt, Pend, NULL);
++          }
++      }
++    }
++}
++
++void
++InspectIProcTrap (ELAN3_CTXT *ctxt, INPUT_TRAP *trap)
++{
++    int             i;
++    int             StatusValid;
++
++    trap->AckSent                 = 0;
++    trap->BadTransaction            = 0;
++    
++    trap->TrappedTransaction        = NULL;
++    trap->TrappedDataBuffer       = NULL;
++    trap->WaitForEopTransaction     = NULL;
++    trap->WaitForEopDataBuffer      = NULL;
++    trap->DmaIdentifyTransaction    = NULL;
++    trap->ThreadIdentifyTransaction = NULL;
++    trap->LockQueuePointer          = (E3_Addr) 0;
++    trap->UnlockQueuePointer        = (E3_Addr) 0;
++
++    /*
++     * Now scan all the transactions received 
++     */
++    for (i = 0; i < trap->NumTransactions ; i++)
++    {
++      E3_IprocTrapHeader_BE *hdrp = &trap->Transactions[i];
++      E3_IprocTrapData_BE   *datap = &trap->DataBuffers[i];
++
++      StatusValid = hdrp->s.TrTypeCntx.s.StatusRegValid != 0;
++      
++      if (StatusValid && hdrp->s.IProcTrapStatus.s.AckSent)   /* Remember if we've sent the ack back */
++          trap->AckSent = 1;
++      
++      if (hdrp->s.TrTypeCntx.s.LastTrappedTrans)              /* Check for EOP */
++      {
++          ASSERT (i == trap->NumTransactions - 1);
++
++          switch (hdrp->s.IProcTrapStatus.Status & E3_IPS_EopType)
++          {
++          case EOP_GOOD:
++              /* if we get an EOP_GOOD then the outputer should have received a PAckOk. */  
++              /* unless it was a flood, in which case someone must have sent an ack */
++              /* but not necessarily us */
++              break;
++
++          case EOP_BADACK:
++              BumpUserStat (ctxt, EopBadAcks);
++
++              /* if we get an EOP_BADACK then the outputer did not receive a PAckOk even if
++               * we sent a PAckOk. We can clear tinfo.AckSent. */
++              if (trap->AckSent == 1)
++              {
++                  PRINTF0 (ctxt, DBG_IPROC, "InspectIProcTrap: Network error destroyed PAckOk\n");
++                  trap->AckSent = 0;
++              }
++              break;
++
++          case EOP_ERROR_RESET:
++              BumpUserStat (ctxt, EopResets);
++
++              /* if we get an EOP_ERROR_RESET then the outputer may or may not have got a PAckOk. */
++              trap->BadTransaction = 1;
++              break;
++
++          default:
++              panic ("InspectIProcTrap: invalid EOP type in status register\n");
++              /* NOTREACHED */
++          }
++          continue;
++      }
++
++      PRINTF2 (ctxt, DBG_IPROC, "InspectIProcTrap: %2d: %s\n", i, IProcTrapString (hdrp, datap));
++      
++      if (! StatusValid)                                      /* We're looking at transactions stored before the trap */
++      {                                                       /* these should only be identifies and lock transactions */
++
++          if (hdrp->s.TrTypeCntx.s.Type & TR_WRITEBLOCK_BIT)
++              panic ("InspectIProcTrap: writeblock transaction found in input trap header before trap occured\n");
++
++          switch (hdrp->s.TrTypeCntx.s.Type & TR_OPCODE_TYPE_MASK)
++          {
++          case TR_LOCKQUEUE & TR_OPCODE_TYPE_MASK:
++              if (trap->LockQueuePointer)                             /* Already seen a LOCKQUEUE transaction in this packet, */
++              {                                               /* the user program should not have done this !! */
++                  ElanException (ctxt, EXCEPTION_BAD_PACKET, INPUT_PROC, trap);
++                  return;
++              }
++
++              trap->LockQueuePointer = (E3_Addr) hdrp->s.TrAddr;      /* Remember the queue pointer in case we need to unlock it */
++              break;
++
++          case TR_DMAIDENTIFY & TR_OPCODE_TYPE_MASK:
++              if (trap->DmaIdentifyTransaction ||             /* Already seen an identify transaction in this packet */
++                  trap->ThreadIdentifyTransaction)            /* the user program should not have done this */
++              {                                                       
++                  ElanException (ctxt, EXCEPTION_BAD_PACKET, INPUT_PROC, trap);
++                  return;
++              }
++              trap->DmaIdentifyTransaction = hdrp;
++              break;
++
++          case TR_THREADIDENTIFY & TR_OPCODE_TYPE_MASK:
++              if (trap->DmaIdentifyTransaction ||             /* Already seen an identify transaction in this packet */
++                  trap->ThreadIdentifyTransaction)            /* the user program should not have done this */
++              {                                                       
++                  ElanException (ctxt, EXCEPTION_BAD_PACKET, INPUT_PROC, trap);
++                  return;
++              }
++              trap->ThreadIdentifyTransaction = hdrp;
++              break;
++              
++          default:
++              panic ("InspectIProcTrap: invalid transaction found in input trap header before trap occured\n");
++              /* NOTREACHED */
++          }
++          continue;
++      }
++
++      if (StatusValid && trap->TrappedTransaction == NULL)    /* Remember the transaction which caused the */
++      {                                                       /* trap */
++          trap->TrappedTransaction = hdrp;
++          trap->TrappedDataBuffer  = datap;
++      }
++
++      if(hdrp->s.IProcTrapStatus.s.BadLength ||
++         ((hdrp->s.IProcTrapStatus.Status & CRC_MASK) == CRC_STATUS_ERROR) ||
++         ((hdrp->s.IProcTrapStatus.Status & CRC_MASK) == CRC_STATUS_BAD))
++      {
++          int j;
++          PRINTF0 (ctxt, DBG_IPROC, "InspectIProcTrap: transaction has a bad crc\n");
++          for (j=0; j<TRANS_DATA_WORDS; j+=4)
++             PRINTF5 (ctxt, DBG_IPROC, "InspectIProcTrap: Data %0d %8x %8x %8x %8x\n",
++                      j, datap->TrData[j], datap->TrData[j+1], datap->TrData[j+2], datap->TrData[j+3]);
++          trap->BadTransaction = 1;
++          continue;
++      }
++      
++      /* No more to do if it's a writeblock transaction */
++      if (hdrp->s.TrTypeCntx.s.Type & TR_WRITEBLOCK_BIT)
++          continue;
++
++      
++      if (GET_STATUS_TRAPTYPE(hdrp->s.IProcTrapStatus) == MI_InputDoTrap &&
++          (hdrp->s.TrTypeCntx.s.Type & TR_WAIT_FOR_EOP) != 0)
++      {
++          /*
++           * This is a wait for eop transaction that has trapped because the inputer
++           * then received a EopError. The next transaction saved should always be an
++           * EopError.
++           */
++          PRINTF0 (ctxt, DBG_IPROC, "InspectIProcTrap: got a trapped WaitForEop transaction due to EopError\n");
++          
++          trap->WaitForEopTransaction = hdrp;
++          trap->WaitForEopDataBuffer  = datap;
++          continue;
++      }
++
++      switch (hdrp->s.TrTypeCntx.s.Type & TR_OPCODE_TYPE_MASK)
++      {
++      case TR_UNLOCKQUEUE & TR_OPCODE_TYPE_MASK:
++          if (trap->UnlockQueuePointer)
++          {
++              ElanException (ctxt, EXCEPTION_BAD_PACKET, INPUT_PROC, trap);
++              return;
++          }
++          trap->UnlockQueuePointer = (E3_Addr) hdrp->s.TrAddr;
++          break;
++      }
++    }
++}
++
++void
++ResolveIProcTrap (ELAN3_CTXT *ctxt, INPUT_TRAP *trap, NETERR_RESOLVER **rvpp)
++{
++    ELAN3_DEV     *dev = ctxt->Device;
++    int           res;
++    unsigned long flags;
++
++    ASSERT (! CTXT_IS_KERNEL (ctxt));
++
++    BumpUserStat (ctxt, IProcTraps);
++
++    InspectIProcTrap (ctxt, trap);
++
++    /*
++     * fixup page fault if we've trapped because of one.
++     */
++    if (trap->FaultSave.s.FaultContext != 0)
++    {
++      /*
++       * If it's a WRITEBLOCK transaction, then see if we remember faulting
++       * before it, and try and prefault in a sensible amount past it.
++       */
++      int                fixedFault = FALSE;
++      INPUT_FAULT_SAVE  *entry;
++      INPUT_FAULT_SAVE **predp;
++      int                npages;
++
++      if ((trap->TrappedTransaction->s.TrTypeCntx.s.Type & TR_WRITEBLOCK_BIT) != 0 && /* a DMA packet */
++          trap->LockQueuePointer == (E3_Addr) 0 &&                                    /* but not a queueing DMA */
++          trap->TrappedTransaction->s.TrAddr != 0)                                    /* and not a DMA to 0 */
++      {
++          spin_lock (&ctxt->InputFaultLock);
++          
++          for (predp = &ctxt->InputFaultList; (entry = *predp)->Next != NULL ; predp = &entry->Next)
++          {
++              if (entry->Addr == trap->TrappedTransaction->s.TrAddr)
++                  break;
++          }
++          
++          *predp = entry->Next;
++          entry->Next = ctxt->InputFaultList;
++          ctxt->InputFaultList = entry;
++          
++          if (entry->Addr == trap->TrappedTransaction->s.TrAddr)
++          {
++              if ((entry->Count <<= 1) > MAX_INPUT_FAULT_PAGES)
++                  entry->Count = MAX_INPUT_FAULT_PAGES;
++          }
++          else
++          {
++              entry->Count = MIN_INPUT_FAULT_PAGES;
++          }
++          
++          entry->Addr = trap->TrappedTransaction->s.TrAddr + (entry->Count * PAGESIZE);
++          npages = entry->Count;
++          
++          spin_unlock (&ctxt->InputFaultLock);
++          
++          if (elan3_pagefault (ctxt, &trap->FaultSave, npages) != ESUCCESS)
++          {
++              PRINTF2 (ctxt, DBG_IPROC, "ResolveIProcTrap: pagefaulting %d pages at %08x - failed\n", 
++                       npages, trap->TrappedTransaction->s.TrAddr);
++          }
++          else
++          {
++              PRINTF2 (ctxt, DBG_IPROC, "ResolveIProcTrap: pagefaulting %d pages at %08x - succeeded\n", 
++                       npages, trap->TrappedTransaction->s.TrAddr);
++              
++              fixedFault = TRUE;
++          }
++      }
++
++      /* Workaround WRITEBLOCK transaction executed when LOCKQUEUE transaction missed */
++      /* the packet will have been nacked */
++      if ((trap->TrappedTransaction->s.TrTypeCntx.s.Type & TR_WRITEBLOCK_BIT) &&      /* a DMA packet */
++          trap->LockQueuePointer == 0 && trap->UnlockQueuePointer &&                  /* a queueing DMA */
++          trap->TrappedTransaction->s.TrAddr == trap->FaultSave.s.FaultAddress)       /* and missed lockqueue */
++      {
++          fixedFault = TRUE;
++      }
++
++      if (! fixedFault)
++      {
++          if ((res = elan3_pagefault (ctxt, &trap->FaultSave, 1)) != ESUCCESS)
++          {
++              PRINTF1 (ctxt, DBG_IPROC, "ResolveIProcTrap: elan3_pagefault failed at %x\n", 
++                       trap->FaultSave.s.FaultAddress);
++              ElanException (ctxt, EXCEPTION_INVALID_ADDR, INPUT_PROC, trap, &trap->FaultSave, res);
++              return;
++          }
++      }
++    }
++
++    if (! trap->AckSent && trap->LockQueuePointer)                    /* Queued DMA */
++    {                                                                 /* The ack was not sent, so the queue will be locked. */
++      SimulateUnlockQueue (ctxt, trap->LockQueuePointer, FALSE);      /* We must unlock it. */
++    }
++
++    if (trap->AckSent && trap->BadTransaction)
++    {
++      if (trap->DmaIdentifyTransaction)
++      {
++          PRINTF0 (ctxt, DBG_IPROC, "ResolveIProcTrap: Dma identify needs network resultion\n");
++
++          BumpStat (dev, DmaIdentifyNetworkErrors);
++          BumpUserStat (ctxt, DmaIdentifyNetworkErrors);
++
++          if (trap->WaitForEopTransaction)
++              PRINTF0 (ctxt, DBG_IPROC, "ResolveIProcTrap: have delayed wait for eop transaction\n");
++      }
++      else if (trap->ThreadIdentifyTransaction)
++      {
++          PRINTF0 (ctxt, DBG_IPROC, "ResolveIProcTrap: Thread identify needs network resolution\n");
++
++          BumpStat (dev, ThreadIdentifyNetworkErrors);
++          BumpUserStat (ctxt, ThreadIdentifyNetworkErrors);
++
++          if (trap->WaitForEopTransaction)
++              PRINTF0 (ctxt, DBG_IPROC, "ResolveIProcTrap: have delayed wait for eop transaction\n");
++      }
++      else
++      {
++          BumpStat (dev, DmaNetworkErrors);
++          BumpUserStat (ctxt, DmaNetworkErrors);
++      }
++    }
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++    
++    if (! trap->AckSent)
++    {
++      PRINTF0 (ctxt, DBG_IPROC, "ResolveIProcTrap: ack not sent, lowering context filter\n");
++
++      trap->State = CTXT_STATE_OK;
++    }
++    else
++    {
++      if (trap->BadTransaction)
++      {
++          PRINTF0 (ctxt, DBG_IPROC, "ResolveIProcTrap: ack sent, waiting on bad transaction\n");
++          trap->State = CTXT_STATE_NETWORK_ERROR;
++      }
++      else
++      {
++          PRINTF0 (ctxt, DBG_IPROC, "ResolveIProcTrap: ack sent, waiting on packet to be re-executed\n");
++          trap->State = CTXT_STATE_NEEDS_RESTART;
++      }
++    }
++
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++
++    if (trap->AckSent && trap->BadTransaction)
++      ElanException (ctxt, EXCEPTION_NETWORK_ERROR, INPUT_PROC, trap, rvpp);
++}
++
++int
++RestartIProcTrap (ELAN3_CTXT *ctxt, INPUT_TRAP *trap)
++{
++    PRINTF1 (ctxt, DBG_IPROC, "RestartIProc: %d transactions\n", trap->NumTransactions);
++
++    if (trap->TrappedTransaction == NULL)                     /* No transaction trapped - probably a network */
++      return (ESUCCESS);                                      /* error */
++
++    while (! trap->TrappedTransaction->s.TrTypeCntx.s.LastTrappedTrans)
++    {
++      E3_IprocTrapHeader_BE *hdrp = trap->TrappedTransaction;
++      E3_IprocTrapData_BE   *datap = trap->TrappedDataBuffer;
++      
++      ASSERT (hdrp->s.TrTypeCntx.s.StatusRegValid != 0);
++
++      PRINTF2 (ctxt, DBG_IPROC, "RestartIProc: TrType=0x%x Status=0x%x\n",
++               hdrp->s.TrTypeCntx.TypeContext, hdrp->s.IProcTrapStatus.Status);
++      
++      if ((hdrp->s.TrTypeCntx.s.Type & TR_WRITEBLOCK_BIT) != 0)
++      {
++          PRINTF1 (ctxt, DBG_IPROC, "RestartIProc: WRITEBLOCK : Addr %x\n", hdrp->s.TrAddr);
++          SimulateBlockWrite (ctxt, hdrp, datap);
++      }
++      else
++      {
++          switch (hdrp->s.TrTypeCntx.s.Type & TR_OPCODE_TYPE_MASK)
++          {
++          case TR_SETEVENT & TR_OPCODE_TYPE_MASK:
++              PRINTF1 (ctxt, DBG_IPROC, "RestartIProc: SETEVENT : %x\n", hdrp->s.TrAddr);
++
++              if (GET_STATUS_TRAPTYPE(hdrp->s.IProcTrapStatus) != MI_InputDoTrap)
++                  FixupEventTrap (ctxt, INPUT_PROC, trap, GET_STATUS_TRAPTYPE(hdrp->s.IProcTrapStatus), &trap->FaultSave, FALSE);
++              else if (hdrp->s.TrAddr)
++              {
++                  if (IssueCommand (ctxt, offsetof (E3_CommandPort, SetEvent), hdrp->s.TrAddr, FALSE) != ISSUE_COMMAND_OK)
++                      return (EAGAIN);
++              }
++              break;
++
++          case TR_WRITEWORD & TR_OPCODE_TYPE_MASK:
++              SimulateWriteWord (ctxt, hdrp, datap);
++              break;
++
++          case TR_WRITEDOUBLEWORD & TR_OPCODE_TYPE_MASK:
++              SimulateWriteDWord (ctxt, hdrp, datap);
++              break;
++              
++          case TR_UNLOCKQUEUE & TR_OPCODE_TYPE_MASK:
++              if (GET_STATUS_TRAPTYPE(hdrp->s.IProcTrapStatus) == MI_InputDoTrap)
++                  ElanException (ctxt, EXCEPTION_BAD_PACKET, INPUT_PROC, trap);
++              else
++              {
++                  switch (GET_STATUS_TRAPTYPE (hdrp->s.IProcTrapStatus))
++                  {
++                  case MI_WaitForUnLockDescRead:
++                      /*
++                       * Fault occured on the read of the queue descriptor - since the ack
++                       * has been sent we need to move the queue on one slot.
++                       */
++                      PRINTF0 (ctxt, DBG_IPROC, "RestartIProc: TR_UNLOCKQUEUE : desc read fault\n");
++
++                      SimulateUnlockQueue (ctxt, trap->LockQueuePointer, TRUE);
++                      
++                      if (IssueCommand (ctxt, offsetof (E3_CommandPort, SetEvent),
++                                        hdrp->s.TrAddr + E3_QUEUE_EVENT_OFFSET, FALSE) != ISSUE_COMMAND_OK)
++                      {
++                          /* Failed to issue setevent to complete queue unlock, since we've already unlocked */
++                          /* the queue, we should "convert" this transaction into a setevent transaction that */
++                          /* hasn't trapped */
++                          PRINTF0 (ctxt, DBG_IPROC, "RestartIProc: could not issue setevent for SimulateUnlockQueue\n");
++
++                          ConvertTransactionToSetEvent (ctxt, hdrp, hdrp->s.TrAddr + E3_QUEUE_EVENT_OFFSET);
++                          return (EAGAIN);
++                      }
++                      break;
++                      
++                  case MI_DoSetEvent:
++                      /*
++                       * Fault occured on either the write to unlock the queue or during 
++                       * processing of the event.  Test the fault address against the
++                       * queue address to find out which - in this case, since the ack
++                       * has been sent we need to move the queue on one slot.
++                       */
++                      if (trap->FaultSave.s.FaultAddress == trap->LockQueuePointer)
++                      {
++                          PRINTF0 (ctxt, DBG_IPROC, "RestartIProc: fixed unlock queue write to unlock fault\n");
++
++                          SimulateUnlockQueue (ctxt, trap->LockQueuePointer, TRUE);
++                          
++                          if (IssueCommand (ctxt, offsetof (E3_CommandPort, SetEvent),
++                                            hdrp->s.TrAddr + E3_QUEUE_EVENT_OFFSET, FALSE) != ISSUE_COMMAND_OK)
++                          {
++                              /* Failed to issue setevent to complete queue unlock, since we've already unlocked */
++                              /* the queue, we should "convert" this transaction into a setevent transaction that */
++                              /* hasn't trapped */
++                              PRINTF0 (ctxt, DBG_IPROC, "RestartIProc: could not issue setevent for SimulateUnlockQueue\n");
++                              
++                              ConvertTransactionToSetEvent (ctxt, hdrp, hdrp->s.TrAddr + E3_QUEUE_EVENT_OFFSET);
++                              return (EFAIL);
++                          }
++                          break;
++                      }
++                      /*DROPTHROUGH*/
++                      
++                  default:
++                      FixupEventTrap (ctxt, INPUT_PROC, trap, GET_STATUS_TRAPTYPE (hdrp->s.IProcTrapStatus),
++                                      &trap->FaultSave, FALSE);
++                      break;
++                  }
++                  trap->LockQueuePointer = trap->UnlockQueuePointer = 0;
++              }
++              break;
++
++          case TR_SENDDISCARD & TR_OPCODE_TYPE_MASK:
++              /* Just ignore send-discard transactions */
++              PRINTF0 (ctxt, DBG_IPROC, "RestartIProc: ignore SENDDISCARD\n");
++              break;
++
++          case TR_REMOTEDMA & TR_OPCODE_TYPE_MASK:
++              PRINTF0 (ctxt, DBG_IPROC, "RestartIProc: REMOTEDMA\n");         
++
++              /* modify the dma type since it will still be a "read" dma */
++              ((E3_DMA_BE *) datap)->s.dma_type &= ~(DMA_TYPE_READ | E3_DMA_CONTEXT_MASK);
++              ((E3_DMA_BE *) datap)->s.dma_type |= DMA_TYPE_ISREMOTE;
++
++              RestartDmaDesc (ctxt, (E3_DMA_BE *) datap);
++              break;
++
++          case TR_TRACEROUTE & TR_OPCODE_TYPE_MASK:
++              PRINTF0 (ctxt, DBG_IPROC, "RestartIProc: TRACEROUTE\n");
++              SimulateTraceRoute (ctxt, hdrp, datap);
++              break;
++
++          default:
++              ElanException (ctxt, EXCEPTION_BAD_PACKET, INPUT_PROC, trap);
++              break;
++          }
++      }
++
++      /*
++       * We've successfully processed this transaction, so move onto the 
++       * next one.
++       */
++      trap->TrappedTransaction++;
++      trap->TrappedDataBuffer++;
++    }
++    
++    return (ESUCCESS);
++}
++
++static void
++ConvertTransactionToSetEvent (ELAN3_CTXT *ctxt, E3_IprocTrapHeader_BE *hdrp, E3_Addr Addr)
++{
++    hdrp->s.TrTypeCntx.s.Type           = TR_SETEVENT;
++    hdrp->s.TrTypeCntx.s.StatusRegValid = 0;
++    hdrp->s.TrAddr                      = Addr;
++}
++
++void
++SimulateBlockWrite (ELAN3_CTXT *ctxt, E3_IprocTrapHeader_BE *hdrp, E3_IprocTrapData_BE *datap)
++{
++    void     *saddr  = (void *) ((unsigned long) datap + (hdrp->s.TrAddr & 0x3f));
++    unsigned  nbytes = (hdrp->s.TrTypeCntx.s.Type) & TR_PARTSIZE_MASK;
++    int       i;
++
++    if (nbytes == 0)
++      nbytes = sizeof (E3_IprocTrapData_BE);
++
++    if (ELAN3_OP_START_FAULT_CHECK (ctxt))
++    {
++      ELAN3_OP_END_FAULT_CHECK (ctxt);
++
++      PRINTF1 (ctxt, DBG_IPROC, "SimulateBlockWrite: faulted at %x\n", hdrp->s.TrAddr);
++      ElanException (ctxt, EXCEPTION_FAULTED, INPUT_PROC, NULL, hdrp->s.TrAddr);
++      return;
++    }
++
++    /*
++     * NOTE: since the block copy could be to sdram, we issue the writes backwards,
++     *       except we MUST ensure that the last item in the block is written last.
++     */
++    switch (((hdrp->s.TrTypeCntx.s.Type) >> TR_TYPE_SHIFT) & TR_TYPE_MASK)
++    {
++    case TR_TYPE_BYTE:                                                /* 8 bit */
++      for (i = nbytes - (2*sizeof (E3_uint8)); i >= 0; i -= sizeof (E3_uint8))
++          ELAN3_OP_STORE8 (ctxt, hdrp->s.TrAddr + i, ((E3_uint8 *) saddr)[i]);
++      i = nbytes - sizeof (E3_uint8);
++      ELAN3_OP_STORE8 (ctxt, hdrp->s.TrAddr + i, ((E3_uint8 *) saddr)[i]);
++      break;
++      
++    case TR_TYPE_SHORT:                                               /* 16 bit */
++      for (i = nbytes - (2*sizeof (E3_uint16)); i >= 0; i -= sizeof (E3_uint16))
++      ELAN3_OP_STORE16 (ctxt, hdrp->s.TrAddr + i, ((E3_uint16 *) saddr)[i]);
++      i = nbytes - sizeof (E3_uint16);
++      ELAN3_OP_STORE16 (ctxt, hdrp->s.TrAddr + i, ((E3_uint16 *) saddr)[i]);
++      break;
++      
++    case TR_TYPE_WORD:                                                /* 32 bit */
++      for (i = nbytes - (2*sizeof (E3_uint32)); i >= 0; i -= sizeof (E3_uint32))
++          ELAN3_OP_STORE32 (ctxt, hdrp->s.TrAddr + i, ((E3_uint32 *) saddr)[i]);
++      i = nbytes - sizeof (E3_uint32);
++      ELAN3_OP_STORE32 (ctxt, hdrp->s.TrAddr + i, ((E3_uint32 *) saddr)[i]);
++      break;
++      
++    case TR_TYPE_DWORD:                                               /* 64 bit  */
++      for (i = nbytes - (2*sizeof (E3_uint64)); i >= 0; i -= sizeof (E3_uint64))
++          ELAN3_OP_STORE64 (ctxt, hdrp->s.TrAddr + i, ((E3_uint64 *) saddr)[i]);
++      i = nbytes - sizeof (E3_uint64);
++      ELAN3_OP_STORE64 (ctxt, hdrp->s.TrAddr + i, ((E3_uint64 *) saddr)[i]);
++      break;
++    }
++    ELAN3_OP_END_FAULT_CHECK (ctxt);
++}
++
++void
++SimulateWriteWord (ELAN3_CTXT *ctxt, E3_IprocTrapHeader_BE *hdrp, E3_IprocTrapData_BE *datap)
++{
++    if (ELAN3_OP_START_FAULT_CHECK (ctxt))
++    {
++      ELAN3_OP_END_FAULT_CHECK (ctxt);
++
++      PRINTF1 (ctxt, DBG_IPROC, "SimulateWriteWord: faulted at %x\n", hdrp->s.TrAddr);
++      ElanException (ctxt, EXCEPTION_FAULTED, INPUT_PROC, NULL, hdrp->s.TrAddr);
++      return;
++    }
++
++    ELAN3_OP_STORE32 (ctxt, hdrp->s.TrAddr, ((E3_uint32 *) datap)[WordEndianFlip]);
++    ELAN3_OP_END_FAULT_CHECK (ctxt);
++}
++
++void
++SimulateWriteDWord (ELAN3_CTXT *ctxt, E3_IprocTrapHeader_BE *hdrp, E3_IprocTrapData_BE *datap)
++{
++    if (ELAN3_OP_START_FAULT_CHECK (ctxt))
++    {
++      ELAN3_OP_END_FAULT_CHECK (ctxt);
++
++      PRINTF1 (ctxt, DBG_IPROC, "SimulateWriteDWord: faulted at %x\n", hdrp->s.TrAddr);
++      ElanException (ctxt, EXCEPTION_FAULTED, INPUT_PROC, NULL, hdrp->s.TrAddr);
++      return;
++    }
++
++    ELAN3_OP_STORE64 (ctxt, hdrp->s.TrAddr, ((E3_uint64 *) datap)[0]);
++    ELAN3_OP_END_FAULT_CHECK (ctxt);
++}
++
++void
++SimulateTraceRoute (ELAN3_CTXT *ctxt, E3_IprocTrapHeader_BE *hdrp, E3_IprocTrapData_BE *datap)
++{
++    E3_uint32 *saddr  = (E3_uint32 *) ((unsigned long) datap + (hdrp->s.TrAddr & 0x3f));
++    unsigned   nwords = TrSizeTable[(hdrp->s.TrTypeCntx.s.Type >> TR_SIZE_SHIFT) & TR_SIZE_MASK] / sizeof (E3_uint32);
++    int        i;
++
++    if (ELAN3_OP_START_FAULT_CHECK (ctxt))
++    {
++      ELAN3_OP_END_FAULT_CHECK (ctxt);
++
++      PRINTF1 (ctxt, DBG_IPROC, "SimulateTraceRoute: faulted at %x\n", hdrp->s.TrAddr);
++      ElanException (ctxt, EXCEPTION_FAULTED, INPUT_PROC, NULL, hdrp->s.TrAddr);
++      return;
++    }
++    
++    for (i = nwords-2; i >= 0; i--)
++      ELAN3_OP_STORE32 (ctxt, hdrp->s.TrAddr + (i * sizeof (E3_uint32)), saddr[i ^ WordEndianFlip]);
++
++    i = nwords-1;
++    ELAN3_OP_STORE32 (ctxt, hdrp->s.TrAddr + (i * sizeof (E3_uint32)), saddr[i ^ WordEndianFlip]);
++
++    ELAN3_OP_END_FAULT_CHECK (ctxt);
++}
++
++void
++SimulateUnlockQueue (ELAN3_CTXT *ctxt, E3_Addr QueuePointer, int SentAck)
++{
++    E3_uint32 QueueLock;
++    E3_Addr   QueueBPTR;
++    E3_Addr   QueueFPTR;
++    E3_uint64 QueueStateAndBPTR;
++
++    if (ELAN3_OP_START_FAULT_CHECK (ctxt))
++    {
++      ELAN3_OP_END_FAULT_CHECK (ctxt);
++
++      PRINTF1 (ctxt, DBG_IPROC, "UnlockQueue: faulted with QueuePointer %x\n", QueuePointer);
++      ElanException (ctxt, EXCEPTION_FAULTED, INPUT_PROC, NULL, QueuePointer);
++      return;
++    }
++    
++    if (SentAck)
++    {
++      QueueBPTR = ELAN3_OP_LOAD32 (ctxt, QueuePointer + offsetof (E3_Queue, q_bptr));
++      QueueFPTR = ELAN3_OP_LOAD32 (ctxt, QueuePointer + offsetof (E3_Queue, q_fptr));
++
++      if (QueueBPTR == ELAN3_OP_LOAD32 (ctxt, QueuePointer + offsetof (E3_Queue, q_top)))     /* move on back pointer */
++          QueueBPTR = ELAN3_OP_LOAD32 (ctxt, QueuePointer + offsetof (E3_Queue, q_base));
++      else
++          QueueBPTR += ELAN3_OP_LOAD32 (ctxt, QueuePointer + offsetof (E3_Queue, q_size));
++      
++      QueueLock = ELAN3_OP_LOAD32 (ctxt, QueuePointer + offsetof (E3_Queue, q_state));
++
++      if (QueueBPTR == QueueFPTR)                             /* and set full bit if fptr == bptr */
++          QueueLock |= E3_QUEUE_FULL;
++      
++      QueueLock &= ~E3_QUEUE_LOCKED;
++      
++      QueueStateAndBPTR = (E3_uint64)QueueLock << 32 | QueueBPTR;
++
++      ELAN3_OP_STORE64 (ctxt, QueuePointer + offsetof (E3_Queue, q_state), QueueStateAndBPTR);
++    }
++    else
++    {
++      QueueLock = ELAN3_OP_LOAD32 (ctxt, QueuePointer + offsetof (E3_Queue, q_state));
++
++      QueueLock &= ~E3_QUEUE_LOCKED;
++      
++      ELAN3_OP_STORE32 (ctxt, QueuePointer + offsetof (E3_Queue, q_state), QueueLock);
++    }
++
++    no_fault();
++}
++
++static void
++BumpInputterStats (ELAN3_DEV *dev, E3_IprocTrapHeader_BE *hdrp)
++{
++    if (hdrp->s.TrTypeCntx.s.LastTrappedTrans)                        /* EOP */
++    {
++      switch (hdrp->s.IProcTrapStatus.Status & E3_IPS_EopType)
++      {
++      case EOP_BADACK:
++          BumpStat (dev, EopBadAcks);
++          break;
++      case EOP_ERROR_RESET:
++          BumpStat (dev, EopResets);
++          break;
++      }
++    }
++    else if (hdrp->s.TrTypeCntx.s.StatusRegValid)
++    {
++      /*
++       * Errors are tested in order of badness. i.e. badlength will prevent a BadCrc and so on...
++       */
++      if (hdrp->s.IProcTrapStatus.s.BadLength)
++          BumpStat (dev, InputterBadLength);
++      else if ((hdrp->s.IProcTrapStatus.Status & CRC_MASK) == CRC_STATUS_BAD)
++          BumpStat (dev, InputterCRCBad);
++      else if ((hdrp->s.IProcTrapStatus.Status & CRC_MASK) == CRC_STATUS_ERROR)
++          BumpStat (dev, InputterCRCErrors);
++      else if ((hdrp->s.IProcTrapStatus.Status & CRC_MASK) == CRC_STATUS_DISCARD)
++          BumpStat (dev, InputterCRCDiscards);
++    }
++}
++
++char *
++IProcTrapString (E3_IprocTrapHeader_BE *hdrp, E3_IprocTrapData_BE *datap)
++{
++    static char buffer[256];
++    static char typeString[256];
++    static char statusString[256];
++    char *ptr;
++    E3_Addr     Addr        = hdrp->s.TrAddr;
++    E3_uint32   Type        = hdrp->s.TrTypeCntx.s.Type;
++    E3_uint32   Context     = hdrp->s.TrTypeCntx.s.Context;
++    E3_uint32   StatusValid = hdrp->s.TrTypeCntx.s.StatusRegValid;
++    
++    if (hdrp->s.TrTypeCntx.s.LastTrappedTrans)
++    {
++      switch (hdrp->s.IProcTrapStatus.Status & E3_IPS_EopType)
++      {
++      case EOP_GOOD:          sprintf (typeString, "EOP GOOD"); break;
++      case EOP_BADACK:        sprintf (typeString, "EOP BADACK"); break;
++      case EOP_ERROR_RESET:   sprintf (typeString, "EOP ERROR RESET"); break;
++      default:                sprintf (typeString, "EOP - bad status"); break;
++      }
++      sprintf (buffer, "%15s Cntx=%08x", typeString, Context);
++    }
++    else
++    {
++      if (Type & TR_WRITEBLOCK_BIT)
++      {
++          switch ((Type >> TR_TYPE_SHIFT) & TR_TYPE_MASK)
++          {
++          case TR_TYPE_BYTE:  ptr = "Byte";    break;
++          case TR_TYPE_SHORT: ptr = "Short";   break;
++          case TR_TYPE_WORD:  ptr = "Word";    break;
++          case TR_TYPE_DWORD: ptr = "Double";  break;
++          default:            ptr = "Unknown"; break;
++          }
++          
++          sprintf (typeString, "WriteBlock Type=%s Size=%2d", ptr, Type & TR_PARTSIZE_MASK);
++      }
++      else
++      {
++          switch (Type & TR_OPCODE_TYPE_MASK)
++          {
++          case TR_SETEVENT & TR_OPCODE_TYPE_MASK:             sprintf (typeString, "Setevent"); break;
++          case TR_REMOTEDMA & TR_OPCODE_TYPE_MASK:            sprintf (typeString, "Remote DMA"); break;
++          case TR_LOCKQUEUE & TR_OPCODE_TYPE_MASK:            sprintf (typeString, "Lock Queue"); break;
++          case TR_UNLOCKQUEUE & TR_OPCODE_TYPE_MASK:          sprintf (typeString, "Unlock Queue"); break;
++          case TR_SENDDISCARD & TR_OPCODE_TYPE_MASK:          sprintf (typeString, "Send Discard"); break;
++          case TR_DMAIDENTIFY & TR_OPCODE_TYPE_MASK:          sprintf (typeString, "DMA Identify"); break;
++          case TR_THREADIDENTIFY & TR_OPCODE_TYPE_MASK:       sprintf (typeString, "Thread Identify"); break;
++          case TR_GTE & TR_OPCODE_TYPE_MASK:                  sprintf (typeString, "GTE"); break;
++          case TR_LT & TR_OPCODE_TYPE_MASK:                   sprintf (typeString, "LT"); break;
++          case TR_EQ & TR_OPCODE_TYPE_MASK:                   sprintf (typeString, "EQ"); break;
++          case TR_NEQ & TR_OPCODE_TYPE_MASK:                  sprintf (typeString, "NEQ"); break;
++          case TR_WRITEWORD & TR_OPCODE_TYPE_MASK:            sprintf (typeString, "Write Word"); break;
++          case TR_WRITEDOUBLEWORD & TR_OPCODE_TYPE_MASK:      sprintf (typeString, "Write Double"); break;
++          case TR_ATOMICADDWORD & TR_OPCODE_TYPE_MASK:        sprintf (typeString, "Atomic Add"); break;
++          case TR_TESTANDWRITE & TR_OPCODE_TYPE_MASK:         sprintf (typeString, "Test and Write"); break;
++          default:                                            sprintf (typeString, "Type=%d", Type & TR_OPCODE_TYPE_MASK); break;
++          }
++      }
++      sprintf (buffer, "%15s Addr=%08x Cntx=%08x", typeString, Addr, Context);
++      /*(Type & TR_SENDACK)      ? " Sendack" : "", */
++      /*(Type & TR_LAST_TRANS)   ? " LastTrans" : "", */
++      /*(Type & TR_WAIT_FOR_EOP) ? " WaitForEop" : ""); */
++    }
++    
++    if (StatusValid)
++    {
++      sprintf (statusString, " Type=%s %x", MiToName (hdrp->s.IProcTrapStatus.s.TrapType), hdrp->s.IProcTrapStatus.Status);
++      strcat (buffer, statusString);
++
++      if (hdrp->s.IProcTrapStatus.s.BadLength)
++          strcat (buffer, " BadLength");
++      switch (hdrp->s.IProcTrapStatus.Status & CRC_MASK)
++      {
++      case CRC_STATUS_DISCARD:
++          strcat (buffer, " CRC Discard");
++          break;
++      case CRC_STATUS_ERROR:
++          strcat (buffer, " CRC Error");
++          break;
++
++      case CRC_STATUS_BAD:
++          strcat (buffer, " CRC Bad");
++          break;
++      }
++    }
++
++    return (buffer);
++}
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan3/Makefile
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan3/Makefile 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan3/Makefile      2005-06-01 23:12:54.587440928 -0400
+@@ -0,0 +1,31 @@
++#
++# Makefile for Quadrics QsNet
++#
++# Copyright (c) 2002-2004 Quadrics Ltd
++#
++# File: drivers/net/qsnet/elan3/Makefile
++#
++
++
++#
++
++#
++# Makefile for Quadrics QsNet
++#
++# Copyright (c) 2004 Quadrics Ltd.
++#
++# File: driver/net/qsnet/elan3/Makefile
++#
++
++list-multi            := elan3.o
++elan3-objs    := context.o cproc.o dproc.o elandebug.o elandev_generic.o elansyscall.o eventcookie.o iproc.o sdram.o minames.o network_error.o route_table.o tproc.o tprocinsts.o routecheck.o virtual_process.o elan3ops.o context_linux.o elandev_linux.o procfs_linux.o tproc_linux.o elan3mmu_generic.o elan3mmu_linux.o
++export-objs           := elandev_linux.o procfs_linux.o
++obj-$(CONFIG_ELAN3)   := elan3.o
++
++elan3.o : $(elan3-objs)
++      $(LD) -r -o $@ $(elan3-objs)
++
++EXTRA_CFLAGS          +=  -DDEBUG -DDEBUG_PRINTF -DDEBUG_ASSERT
++
++include $(TOPDIR)/Rules.make
++
+Index: linux-2.4.21/drivers/net/qsnet/elan3/Makefile.conf
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan3/Makefile.conf    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan3/Makefile.conf 2005-06-01 23:12:54.587440928 -0400
+@@ -0,0 +1,10 @@
++# Flags for generating QsNet Linux Kernel Makefiles
++MODNAME               =       elan3.o
++MODULENAME    =       elan3
++KOBJFILES     =       context.o cproc.o dproc.o elandebug.o elandev_generic.o elansyscall.o eventcookie.o iproc.o sdram.o minames.o network_error.o route_table.o tproc.o tprocinsts.o routecheck.o virtual_process.o elan3ops.o context_linux.o elandev_linux.o procfs_linux.o tproc_linux.o elan3mmu_generic.o elan3mmu_linux.o
++EXPORT_KOBJS  =       elandev_linux.o procfs_linux.o
++CONFIG_NAME   =       CONFIG_ELAN3
++SGALFC                =       
++# EXTRALINES START
++
++# EXTRALINES END
+Index: linux-2.4.21/drivers/net/qsnet/elan3/minames.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan3/minames.c        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan3/minames.c     2005-06-01 23:12:54.587440928 -0400
+@@ -0,0 +1,38 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: minames.c,v 1.12 2003/06/07 15:57:49 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/os/minames.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <elan3/urom_addrs.h>
++
++caddr_t
++MiToName (int mi)
++{
++    static char space[32];
++    static struct {
++      int   mi;
++      char *name;
++    } info[] = {
++#include <elan3/minames.h>
++    };
++    register int i;
++
++
++    for (i = 0; i < sizeof(info)/sizeof(info[0]); i++)
++      if (info[i].mi == mi)
++          return (info[i].name);
++    sprintf (space, "MI %x", mi);
++    return (space);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan3/network_error.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan3/network_error.c  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan3/network_error.c       2005-06-01 23:12:54.589440624 -0400
+@@ -0,0 +1,777 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: network_error.c,v 1.32.2.1 2004/10/28 11:54:57 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/os/network_error.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <qsnet/kthread.h>
++
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elanvp.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanctxt.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elandebug.h>
++
++#ifdef DIGITAL_UNIX
++#include <sys/cred.h>
++#include <sys/mbuf.h>
++#include <sys/utsname.h>
++#include <net/if.h>
++#include <netinet/in.h>
++#include <netinet/in_var.h>
++
++#include <rpc/types.h>
++#include <rpc/auth.h>
++#include <rpc/xdr.h>
++#include <rpc/clnt.h>
++
++typedef xdrproc_t kxdrproc_t;
++#endif
++
++#ifdef LINUX
++#include <linux/sunrpc/types.h>
++#include <linux/sunrpc/auth.h>
++#include <linux/sunrpc/xdr.h>
++#include <linux/sunrpc/clnt.h>
++
++#include <linux/utsname.h>
++#define SYS_NMLN      __NEW_UTS_LEN
++#endif
++
++#include <elan3/neterr_rpc.h>
++
++spinlock_t      ResolveRequestLock;
++kcondvar_t      ResolveRequestWait;
++
++NETERR_RESOLVER  *ResolveRequestHead;
++NETERR_RESOLVER **ResolveRequestTailp = &ResolveRequestHead;
++int             ResolveRequestCount;
++int             ResolveRequestThreads;
++int             ResolveRequestMaxThreads = 4;
++int             ResolveRequestTimeout = 60;
++
++typedef struct neterr_server
++{
++    struct neterr_server *Next;
++    struct neterr_server *Prev;
++    unsigned            ElanId;
++
++    char               *Name;
++    int                         RefCount;
++    struct sockaddr_in    Addr;
++} NETERR_SERVER;
++
++#define NETERR_HASH_ENTRIES   64
++#define NETERR_HASH(elanid)   (((unsigned) elanid) % NETERR_HASH_ENTRIES)
++NETERR_SERVER *NeterrServerHash[NETERR_HASH_ENTRIES];
++kmutex_t       NeterrServerLock;
++
++static NETERR_SERVER *FindNeterrServer (int elanId);
++static void           DereferenceNeterrServer (NETERR_SERVER *server);
++static int            CallNeterrServer (NETERR_SERVER *server, NETERR_MSG *msg);
++
++void
++InitialiseNetworkErrorResolver ()
++{
++    spin_lock_init (&ResolveRequestLock);
++    kcondvar_init (&ResolveRequestWait);
++    
++    ResolveRequestHead  = NULL;
++    ResolveRequestTailp = &ResolveRequestHead;
++
++    kmutex_init (&NeterrServerLock);
++}
++
++void
++FinaliseNetworkErrorResolver ()
++{
++    spin_lock_destroy (&ResolveRequestLock);
++    kcondvar_destroy (&ResolveRequestWait);
++    
++    kmutex_destroy (&NeterrServerLock);
++}
++
++static NETERR_RESOLVER *
++AllocateNetworkErrorResolver (void)
++{
++    NETERR_RESOLVER *rvp;
++
++    KMEM_ZALLOC (rvp, NETERR_RESOLVER *, sizeof (NETERR_RESOLVER), TRUE);
++    spin_lock_init (&rvp->Lock);
++
++    return (rvp);
++}
++
++void
++FreeNetworkErrorResolver (NETERR_RESOLVER *rvp)
++{
++    spin_lock_destroy (&rvp->Lock);
++    KMEM_FREE (rvp, sizeof (NETERR_RESOLVER));
++}
++
++static void
++elan3_neterr_resolver (void)
++{
++    NETERR_RESOLVER *rvp;
++    NETERR_SERVER   *server;
++    int                    status;
++    unsigned long    flags;
++
++    kernel_thread_init("elan3_neterr_resolver");
++    spin_lock (&ResolveRequestLock);
++
++    while ((rvp = ResolveRequestHead) != NULL)
++    {
++      if ((ResolveRequestHead = rvp->Next) == NULL)
++          ResolveRequestTailp = &ResolveRequestHead;
++      
++      spin_unlock (&ResolveRequestLock);
++
++      PRINTF1 (DBG_DEVICE, DBG_NETERR, "elan3_neterr_resolver: rvp = %p\n", rvp);
++      PRINTF1 (DBG_DEVICE, DBG_NETERR, "                      Rail          %d\n", rvp->Message.Rail);
++      PRINTF1 (DBG_DEVICE, DBG_NETERR, "                      SrcCapability %s\n", CapabilityString (&rvp->Message.SrcCapability));
++      PRINTF1 (DBG_DEVICE, DBG_NETERR, "                      DstCapability %s\n", CapabilityString (&rvp->Message.DstCapability));
++      PRINTF1 (DBG_DEVICE, DBG_NETERR, "                      CookieAddr    %08x\n", rvp->Message.CookieAddr);
++      PRINTF1 (DBG_DEVICE, DBG_NETERR, "                      CookieVProc   %08x\n", rvp->Message.CookieVProc);
++      PRINTF1 (DBG_DEVICE, DBG_NETERR, "                      NextCookie    %08x\n", rvp->Message.NextCookie);
++      PRINTF1 (DBG_DEVICE, DBG_NETERR, "                      WaitForEop    %08x\n", rvp->Message.WaitForEop);
++      
++      if ((server = FindNeterrServer (rvp->Location.loc_node)) == NULL)
++          status = ECONNREFUSED;
++      else if (ResolveRequestTimeout && ((int)(lbolt - rvp->Timestamp)) > (ResolveRequestTimeout*HZ))
++      {
++          printk ("elan_neterr: rpc to '%s' timedout - context %d killed\n", server->Name, rvp->Message.SrcCapability.cap_mycontext);
++          status = ECONNABORTED;
++      }
++      else
++      {
++          status = CallNeterrServer (server, &rvp->Message);
++
++          DereferenceNeterrServer (server);
++      }
++      
++      if ((status == EINTR || status == ETIMEDOUT) && rvp->Ctxt != NULL)
++      {
++          PRINTF1 (DBG_DEVICE, DBG_NETERR, "elan3_neterr_resolver: retry rvp=%p\n", rvp);
++          spin_lock (&ResolveRequestLock);
++          rvp->Next = NULL;
++          *ResolveRequestTailp = rvp;
++          ResolveRequestTailp = &rvp->Next;
++      }
++      else
++      {
++          rvp->Status = status;
++          
++          spin_lock (&rvp->Lock);
++          
++          if (rvp->Ctxt != NULL)
++          {
++              PRINTF2 (rvp->Ctxt, DBG_NETERR, "elan3_neterr_resolver: completing rvp %p for ctxt %p\n", rvp, rvp->Ctxt);
++              spin_lock_irqsave (&rvp->Ctxt->Device->IntrLock, flags);
++              
++              rvp->Completed = TRUE;
++              
++              kcondvar_wakeupall (&rvp->Ctxt->Wait, &rvp->Ctxt->Device->IntrLock);
++              
++              /*
++               * drop the locks out of order since the rvp can get freeed
++               * as soon as we drop the IntrLock - so cannot reference the
++               * rvp after this.
++               */
++              
++              spin_unlock (&rvp->Lock);
++              spin_unlock_irqrestore (&rvp->Ctxt->Device->IntrLock, flags);
++          }
++          else
++          {
++              PRINTF2 (DBG_DEVICE, DBG_NETERR, "elan3_neterr_resolver: completing rvp %p for deceased ctxt %p\n", rvp, rvp->Ctxt);
++              spin_unlock (&rvp->Lock);
++              FreeNetworkErrorResolver (rvp);
++          }
++          
++          spin_lock (&ResolveRequestLock);
++          ResolveRequestCount--;
++      }
++    }
++
++    ResolveRequestThreads--;
++
++    spin_unlock (&ResolveRequestLock);
++    kernel_thread_exit();
++}
++
++int
++QueueNetworkErrorResolver (ELAN3_CTXT *ctxt, INPUT_TRAP *trap, NETERR_RESOLVER **rvpp)
++{
++    int                          isdma   = trap->DmaIdentifyTransaction != NULL;
++    E3_IprocTrapHeader_BE *hdrp    = isdma ? trap->DmaIdentifyTransaction : trap->ThreadIdentifyTransaction;
++    E3_uint32              process = isdma ? (hdrp->s.TrAddr & 0xFFFF) : (hdrp->s.TrData0 & 0xFFFF);
++    NETERR_RESOLVER       *rvp;
++
++    PRINTF2 (ctxt, DBG_NETERR, "QueueNetworkErrorResolver: process = %d %s\n", process, isdma ? "(dma)" : "(thread)");
++
++    if ((rvp = AllocateNetworkErrorResolver()) == NULL)
++    {
++      PRINTF0 (ctxt, DBG_NETERR, "QueueNetworkErrorResolver: cannot allocate resolver\n");
++      return (ENOMEM);
++    }
++
++    rvp->Message.Rail = ctxt->Device->Devinfo.dev_rail;
++
++    krwlock_read (&ctxt->VpLock);
++    rvp->Location = ProcessToLocation (ctxt, NULL, process, &rvp->Message.SrcCapability);
++    krwlock_done (&ctxt->VpLock);
++
++    if (rvp->Location.loc_node == ELAN3_INVALID_NODE)
++    {
++      PRINTF0 (ctxt, DBG_NETERR, "QueueNetworkErrorResolver: invalid elan id\n");
++
++      FreeNetworkErrorResolver (rvp);
++      return (EINVAL);
++    }
++
++    rvp->Message.DstCapability = ctxt->Capability;
++    rvp->Message.DstProcess    = elan3_process (ctxt);
++    rvp->Message.WaitForEop    = (trap->WaitForEopTransaction != NULL);
++
++    if (isdma)
++    {
++      rvp->Message.CookieAddr  = 0;
++      rvp->Message.CookieVProc = hdrp->s.TrAddr;
++      rvp->Message.NextCookie  = 0;
++    }
++    else
++    {
++      rvp->Message.CookieAddr  = hdrp->s.TrAddr;
++      rvp->Message.CookieVProc = hdrp->s.TrData0;
++      rvp->Message.NextCookie  = hdrp->s.TrData1;
++    }
++
++    rvp->Completed = FALSE;
++    rvp->Ctxt      = ctxt;
++    rvp->Timestamp = lbolt;
++
++    spin_lock (&ResolveRequestLock);
++
++    rvp->Next = NULL;
++    *ResolveRequestTailp = rvp;
++    ResolveRequestTailp = &rvp->Next;
++    ResolveRequestCount++;
++
++    kcondvar_wakeupone (&ResolveRequestWait, &ResolveRequestLock);
++
++    if (ResolveRequestCount < ResolveRequestThreads || ResolveRequestThreads >= ResolveRequestMaxThreads)
++      spin_unlock (&ResolveRequestLock);
++    else
++    {
++      ResolveRequestThreads++;
++
++      spin_unlock (&ResolveRequestLock);
++      if (kernel_thread_create (elan3_neterr_resolver, NULL) == NULL)
++      {
++          spin_lock (&ResolveRequestLock);
++          ResolveRequestThreads--;
++          spin_unlock (&ResolveRequestLock);
++          
++          if (ResolveRequestThreads == 0)
++          {
++              PRINTF0 (ctxt, DBG_NETERR, "QueueNetworkErrorResolver: cannot thread pool\n");
++
++              FreeNetworkErrorResolver (rvp);
++              return (ENOMEM);
++          }
++      }
++    }
++
++    *rvpp = rvp;
++    return (ESUCCESS);
++}
++
++void
++CancelNetworkErrorResolver (NETERR_RESOLVER *rvp)
++{
++    spin_lock (&rvp->Lock);
++
++    PRINTF2 (rvp->Ctxt, DBG_NETERR, "CancelNetworkErrorResolver: rvp=%p %s\n", rvp, rvp->Completed ? "Completed" : "Pending");
++
++    if (rvp->Completed)
++    {
++      spin_unlock (&rvp->Lock);
++      FreeNetworkErrorResolver (rvp);
++    }
++    else
++    {
++      rvp->Ctxt = NULL;
++      spin_unlock (&rvp->Lock);
++    }
++}
++
++static NETERR_FIXUP *
++AllocateNetworkErrorFixup (void)
++{
++    NETERR_FIXUP *nef;
++
++    KMEM_ZALLOC (nef, NETERR_FIXUP *, sizeof (NETERR_FIXUP), TRUE);
++
++    if (nef == (NETERR_FIXUP *) NULL)
++      return (NULL);
++
++    kcondvar_init (&nef->Wait);
++
++    return (nef);
++}
++
++static void
++FreeNetworkErrorFixup (NETERR_FIXUP *nef)
++{
++    kcondvar_destroy (&nef->Wait);
++    KMEM_FREE (nef, sizeof (NETERR_FIXUP));
++}
++
++int
++ExecuteNetworkErrorFixup (NETERR_MSG *msg)
++{
++    ELAN3_DEV      *dev;
++    ELAN3_CTXT          *ctxt;
++    NETERR_FIXUP  *nef;
++    NETERR_FIXUP **predp;
++    int                  rc;
++    unsigned long  flags;
++
++    PRINTF1 (DBG_DEVICE, DBG_NETERR, "ExecuteNetworkErrorFixup: msg = %p\n", msg);
++    PRINTF1 (DBG_DEVICE, DBG_NETERR, "                      Rail          %d\n", msg->Rail);
++    PRINTF1 (DBG_DEVICE, DBG_NETERR, "                      SrcCapability %s\n", CapabilityString (&msg->SrcCapability));
++    PRINTF1 (DBG_DEVICE, DBG_NETERR, "                      DstCapability %s\n", CapabilityString (&msg->DstCapability));
++    PRINTF1 (DBG_DEVICE, DBG_NETERR, "                      CookieAddr    %08x\n", msg->CookieAddr);
++    PRINTF1 (DBG_DEVICE, DBG_NETERR, "                      CookieVProc   %08x\n", msg->CookieVProc);
++    PRINTF1 (DBG_DEVICE, DBG_NETERR, "                      NextCookie    %08x\n", msg->NextCookie);
++    PRINTF1 (DBG_DEVICE, DBG_NETERR, "                      WaitForEop    %08x\n", msg->WaitForEop);
++      
++    if ((dev = elan3_device (msg->Rail)) == NULL)
++      return (ESRCH);
++
++    if ((nef = AllocateNetworkErrorFixup()) == NULL)
++      return (ENOMEM);
++
++    if (nef == (NETERR_FIXUP *) NULL)
++      return (ENOMEM);
++    
++    bcopy (msg, &nef->Message, sizeof (NETERR_MSG));
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++    
++    ctxt = ELAN3_DEV_CTX_TABLE(dev, msg->SrcCapability.cap_mycontext);
++
++    if (ctxt == NULL)
++      rc = ESRCH;
++    else if (!ELAN_CAP_MATCH (&msg->SrcCapability, &ctxt->Capability))
++      rc = EPERM;
++    else
++    { 
++      if (ctxt->Status & CTXT_NO_LWPS)
++          rc = EAGAIN;
++      else
++      {
++          for (predp = &ctxt->NetworkErrorFixups; *predp != NULL; predp = &(*predp)->Next)
++              ;
++          nef->Next = NULL;
++          *predp = nef;
++          
++          kcondvar_wakeupone (&ctxt->Wait, &dev->IntrLock);
++
++          while (! nef->Completed)
++              kcondvar_wait (&nef->Wait, &dev->IntrLock, &flags);
++
++          rc = nef->Status;
++      }
++    }
++    
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++
++    FreeNetworkErrorFixup (nef);
++
++    return (rc);
++}
++
++void
++CompleteNetworkErrorFixup (ELAN3_CTXT *ctxt, NETERR_FIXUP *nef, int status)
++{
++    ELAN3_DEV *dev = ctxt->Device;
++    unsigned long flags;
++
++    PRINTF2 (ctxt, DBG_NETERR, "CompleteNetworkErrorFixup: %p %d\n", nef, status);
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++
++    nef->Status = status;
++    nef->Completed = TRUE;
++    kcondvar_wakeupone (&nef->Wait, &dev->IntrLock);
++
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++}
++
++
++static NETERR_SERVER *
++NewNeterrServer (int elanId, struct sockaddr_in *addr, char *name)
++{
++    NETERR_SERVER *server;
++
++    KMEM_ZALLOC (server, NETERR_SERVER *, sizeof (NETERR_SERVER), TRUE);
++    KMEM_ALLOC  (server->Name, char *, strlen (name)+1, TRUE);
++
++    bcopy (addr, &server->Addr, sizeof (struct sockaddr_in));
++    bcopy (name, server->Name, strlen (name)+1);
++
++    server->ElanId   = elanId;
++    server->RefCount = 1;
++    
++    return (server);
++}
++
++static void
++DeleteNeterrServer (NETERR_SERVER *server)
++{
++    KMEM_FREE (server->Name, strlen(server->Name)+1);
++    KMEM_FREE (server, sizeof (NETERR_SERVER));
++}
++
++static NETERR_SERVER *
++FindNeterrServer (int elanId)
++{
++    NETERR_SERVER *server;
++    
++    kmutex_lock (&NeterrServerLock);
++    
++    for (server = NeterrServerHash[NETERR_HASH(elanId)]; server != NULL; server = server->Next)
++      if (server->ElanId == elanId)
++          break;
++
++    if (server != NULL)
++      server->RefCount++;
++    kmutex_unlock (&NeterrServerLock);
++
++    return (server);
++}
++
++static void
++DereferenceNeterrServer (NETERR_SERVER *server)
++{
++    kmutex_lock (&NeterrServerLock);
++    if ((--server->RefCount) == 0)
++      DeleteNeterrServer (server);
++    kmutex_unlock  (&NeterrServerLock);
++}
++
++int
++AddNeterrServer (int elanId, struct sockaddr_in *addr, char *name)
++{
++    NETERR_SERVER *server;
++    NETERR_SERVER *old;
++    int            hashval = NETERR_HASH(elanId);
++
++    server = NewNeterrServer (elanId, addr, name);
++    
++    if (server == NULL)
++      return (ENOMEM);
++    
++    kmutex_lock (&NeterrServerLock);
++    for (old = NeterrServerHash[hashval]; old != NULL; old = old->Next)
++      if (old->ElanId == elanId)
++          break;
++    
++    /* remove "old" server from hash table */
++    if (old != NULL)
++    {
++      if (old->Prev)
++          old->Prev->Next = old->Next;
++      else
++          NeterrServerHash[hashval] = old->Next;
++      if (old->Next)
++          old->Next->Prev = old->Prev;
++    }
++
++    /* insert "new" server into hash table */
++    if ((server->Next = NeterrServerHash[hashval]) != NULL)
++      server->Next->Prev = server;
++    server->Prev = NULL;
++    NeterrServerHash[hashval] = server;
++
++    kmutex_unlock (&NeterrServerLock);
++
++    if (old != NULL)
++      DereferenceNeterrServer (old);
++    
++    return (ESUCCESS);
++}
++
++int
++AddNeterrServerSyscall (int elanId, void *addrp, void *namep, char *unused)
++{
++    struct sockaddr_in addr;
++    char              *name;
++    int                error;
++    int                nob;
++
++    /* Sanity check the supplied elanId argument */
++    if (elanId < 0)
++      return ( set_errno(EINVAL) );
++
++    KMEM_ALLOC (name, caddr_t, SYS_NMLN, TRUE);
++    
++    if (copyin ((caddr_t) addrp, (caddr_t) &addr, sizeof (addr)) ||
++      copyinstr ((caddr_t) namep, name, SYS_NMLN, &nob))
++    {
++      error = EFAULT;
++    }
++    else
++    {
++      PRINTF2 (DBG_DEVICE, DBG_NETERR, "AddNeterrServer: '%s' at elanid %d\n", name, elanId);
++
++      error = AddNeterrServer (elanId, &addr, name);
++    }
++    KMEM_FREE (name, SYS_NMLN);
++
++    return (error ? set_errno(error) : ESUCCESS);
++}
++
++
++#if defined(DIGITAL_UNIX)
++static int
++CallNeterrServer (NETERR_SERVER *server, NETERR_MSG *msg)
++{
++    cred_t       *cr = crget();
++    struct rpc_err  rpcerr;
++    extern cred_t  *kcred;
++    struct timeval  wait;
++    enum clnt_stat  rc;
++    int                   status;
++    CLIENT         *clnt;
++    int             error;
++
++    PRINTF4 (DBG_DEVICE, DBG_NETRPC, "CallNeterrServer(%s) - family=%d port=%d addr=%08x\n", server->Name,
++           server->Addr.sin_family, server->Addr.sin_port, server->Addr.sin_addr.s_addr);
++
++    if ((clnt = clntkudp_create (&server->Addr, (struct sockaddr_in *)0, NETERR_PROGRAM, NETERR_VERSION, 1, cr)) == NULL)
++    {
++      PRINTF1 (DBG_DEVICE, DBG_NETRPC, "CallNeterrServer(%s): clntkudp_create error\n", server->Name);
++
++      return (ENOMEM);
++    }
++    
++    wait.tv_sec  = NETERR_RPC_TIMEOUT;
++    wait.tv_usec = 0;
++    
++    PRINTF2 (DBG_DEVICE, DBG_NETRPC, "CallNeterrServer(%s): CLNT_CALL timeout = %d\n", server->Name, NETERR_RPC_TIMEOUT);
++   
++    rc = CLNT_CALL(clnt, NETERR_FIXUP_RPC, xdr_neterr_msg, (void *)msg, xdr_int, (void *) &status, wait);
++
++    PRINTF3 (DBG_DEVICE, DBG_NETRPC, "CallNeterrServer(%s): CLNT_CALL -> %d (%s)\n", server->Name, rc, clnt_sperrno(rc));;
++
++    switch (rc)
++    {
++    case RPC_SUCCESS:
++      break;
++
++    case RPC_INTR:
++      status = EINTR;
++      break;
++
++    case RPC_TIMEDOUT:
++      status = ETIMEDOUT;
++      break;
++
++    default:
++      printf ("CallNeterrServer(%s): %s\n", server->Name, clnt_sperrno(status));
++      status = ENOENT;
++      break;
++    }
++
++    CLNT_DESTROY(clnt);
++
++    crfree(cr);
++    
++    ASSERT(rc == RPC_SUCCESS || status != 0);
++
++    PRINTF2 (DBG_DEVICE, DBG_NETRPC, "CallNeterrServer(%s): status=%d\n", server->Name, status);
++
++    return (status);
++}
++#endif
++
++#if defined(LINUX)
++
++#define xdrsize(type) ((sizeof(type) + 3) >> 2)
++
++static int
++xdr_error(struct rpc_rqst *req, u32 *p, void *dummy)
++{
++    return -EIO;
++}
++
++static int
++xdr_decode_int(struct rpc_rqst *req, u32 *p, int *res)
++{ 
++    *res = ntohl(*p++);
++    return 0;
++}
++
++#define XDR_capability_sz ((12 + BT_BITOUL(ELAN3_MAX_VPS)) * sizeof (u32))
++
++static int
++xdr_encode_capability(u32 *p, ELAN_CAPABILITY *cap)
++{
++    u32 *pp = p;
++
++    /* basic xdr unit is u32 - for opaque types we must round up to that */
++    memcpy(p, &cap->cap_userkey, sizeof(cap->cap_userkey));
++    p += xdrsize(cap->cap_userkey);
++
++    *p++ = htonl(cap->cap_version);
++    ((u16 *) (p++))[1] = htons(cap->cap_type);
++    *p++ = htonl(cap->cap_lowcontext);
++    *p++ = htonl(cap->cap_highcontext);
++    *p++ = htonl(cap->cap_mycontext);
++    *p++ = htonl(cap->cap_lownode);
++    *p++ = htonl(cap->cap_highnode);
++    *p++ = htonl(cap->cap_railmask);
++
++    memcpy(p, &cap->cap_bitmap[0], sizeof(cap->cap_bitmap));
++    p += xdrsize(cap->cap_bitmap);
++
++    ASSERT (((unsigned long) p - (unsigned long) pp) == XDR_capability_sz);
++
++    return (p - pp);
++}
++
++
++#define XDR_neterr_sz (((1 + 5) * sizeof (u32)) + (2*XDR_capability_sz))
++
++static int
++xdr_encode_neterr_msg(struct rpc_rqst *req, u32 *p, NETERR_MSG *msg)
++{
++    u32 *pp = p;
++
++    *p++ = htonl(msg->Rail);
++
++    p += xdr_encode_capability(p, &msg->SrcCapability);
++    p += xdr_encode_capability(p, &msg->DstCapability);
++
++    *p++ = htonl(msg->DstProcess);
++    *p++ = htonl(msg->CookieAddr);
++    *p++ = htonl(msg->CookieVProc);
++    *p++ = htonl(msg->NextCookie);
++    *p++ = htonl(msg->WaitForEop);
++
++    ASSERT (((unsigned long) p - (unsigned long) pp) == XDR_neterr_sz);
++
++    req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
++
++    return 0;
++}
++
++static struct rpc_procinfo neterr_procedures[2] = 
++{
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
++#     define RPC_ID_NULL      "neterr_null"
++#     define RPC_ID_FIXUP_RPC "neterr_fixup_rpc"
++#else
++#     define RPC_ID_NULL      NETERR_NULL_RPC
++#     define RPC_ID_FIXUP_RPC NETERR_FIXUP_RPC
++#endif
++    {         
++      RPC_ID_NULL,                    /* procedure name or number*/
++      (kxdrproc_t) xdr_error,         /* xdr encode fun */
++        (kxdrproc_t) xdr_error,       /* xdr decode fun */
++      0,                              /* req buffer size */
++      0,                              /* call count */
++    },
++    {         
++      RPC_ID_FIXUP_RPC,
++        (kxdrproc_t) xdr_encode_neterr_msg,
++        (kxdrproc_t) xdr_decode_int,
++      XDR_neterr_sz,
++      0,                      
++    },
++};
++
++static struct rpc_version neterr_version1 = 
++{
++    1,                        /* version */
++    2,                        /* number of procedures */
++    neterr_procedures /* procedures */
++};
++
++static struct rpc_version *neterr_version[] = 
++{
++    NULL,
++    &neterr_version1,
++};
++
++static struct rpc_stat neterr_stats;
++
++static struct rpc_program neterr_program = 
++{
++    NETERR_SERVICE,
++    NETERR_PROGRAM,
++    sizeof(neterr_version)/sizeof(neterr_version[0]),
++    neterr_version,
++    &neterr_stats,
++};
++
++static int
++CallNeterrServer (NETERR_SERVER *server, NETERR_MSG *msg)
++{
++    struct rpc_xprt   *xprt;
++    struct rpc_clnt   *clnt;
++    struct rpc_timeout to;
++    int                rc, status;
++    
++    PRINTF (DBG_DEVICE, DBG_NETRPC, "CallNeterrServer(%s)\n", server->Name);
++
++    xprt_set_timeout(&to, 1, NETERR_RPC_TIMEOUT * HZ);
++
++    if ((xprt = xprt_create_proto(IPPROTO_UDP, &server->Addr, &to)) == NULL)
++    {
++      PRINTF (DBG_DEVICE, DBG_NETRPC, "CallNeterrServer(%s) xprt_create_proto failed\n", server->Name);
++      return EFAIL;
++    }
++
++    if ((clnt = rpc_create_client(xprt, server->Name, &neterr_program, NETERR_VERSION, RPC_AUTH_NULL)) == NULL)
++    {
++      PRINTF (DBG_DEVICE, DBG_NETRPC, "CallNeterrServer(%s) rpc_create_client failed\n", server->Name);
++      xprt_destroy (xprt);
++      
++      return EFAIL;
++    }
++
++    clnt->cl_softrtry = 1;
++    clnt->cl_chatty   = 0;
++    clnt->cl_oneshot  = 1;
++    clnt->cl_intr     = 0;
++
++    if ((rc = rpc_call(clnt, NETERR_FIXUP_RPC, msg, &status, 0)) < 0)
++    {
++      /* RPC error has occured - determine whether we should retry */
++
++      status = ETIMEDOUT;
++    }
++
++    PRINTF (DBG_DEVICE, DBG_NETRPC, "CallNeterrServer(%s): -> %d\n", server->Name, status);
++
++    return (status);
++}
++
++#endif /* defined(LINUX) */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan3/procfs_linux.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan3/procfs_linux.c   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan3/procfs_linux.c        2005-06-01 23:12:54.589440624 -0400
+@@ -0,0 +1,195 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: procfs_linux.c,v 1.21 2003/09/24 13:57:25 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/os/procfs_linux.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elandebug.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanvp.h>
++
++#include <linux/module.h>
++#include <linux/ctype.h>
++
++#include <qsnet/procfs_linux.h>
++
++struct proc_dir_entry *elan3_procfs_root;
++struct proc_dir_entry *elan3_config_root;
++
++static int
++proc_read_position (char *page, char **start, off_t off,
++                  int count, int *eof, void *data)
++{
++    ELAN3_DEV *dev = (ELAN3_DEV *) data;
++    int       len;
++
++    if (dev->Position.pos_mode == ELAN_POS_UNKNOWN)
++      len = sprintf (page, "<unknown>\n");
++    else
++      len = sprintf (page, 
++                     "NodeId                 %d\n"
++                     "NumLevels              %d\n"
++                     "NumNodes               %d\n",
++                     dev->Position.pos_nodeid, dev->Position.pos_levels, dev->Position.pos_nodes);
++
++    return (qsnet_proc_calc_metrics (page, start, off, count, eof, len));
++}
++
++static int
++proc_write_position (struct file *file, const char *buf, unsigned long count, void *data)
++{
++    ELAN3_DEV *dev      = (ELAN3_DEV *) data;
++    unsigned  nodeid   = ELAN3_INVALID_NODE;
++    unsigned  numnodes = 0;
++    char     *page, *p;
++    int       res;
++
++    if (count == 0)
++      return (0);
++
++    if (count >= PAGE_SIZE)
++      return (-EINVAL);
++
++    if ((page = (char *) __get_free_page (GFP_KERNEL)) == NULL)
++      return (-ENOMEM);
++
++    MOD_INC_USE_COUNT;
++
++    if (copy_from_user (page, buf, count))
++      res = -EFAULT;
++    else
++    {
++      page[count] = '\0';
++      
++      if (page[count-1] == '\n')
++          page[count-1] = '\0';
++
++      if (! strcmp (page, "<unknown>"))
++      {
++          dev->Position.pos_mode      = ELAN_POS_UNKNOWN;
++          dev->Position.pos_nodeid    = ELAN3_INVALID_NODE;
++          dev->Position.pos_nodes     = 0;
++          dev->Position.pos_levels    = 0;
++      }
++      else
++      {
++          for (p = page; *p; )
++          {
++              while (isspace (*p))
++                  p++;
++              
++              if (! strncmp (p, "NodeId=", strlen("NodeId=")))
++                  nodeid   = simple_strtoul (p + strlen ("NodeId="), NULL, 0);
++              if (! strncmp (p, "NumNodes=", strlen ("NumNodes=")))
++                  numnodes = simple_strtoul (p + strlen ("NumNodes="), NULL, 0);
++              
++              while (*p && !isspace(*p))
++                  p++;
++          }
++
++          if (ComputePosition (&dev->Position, nodeid, numnodes, dev->Devinfo.dev_num_down_links_value) != 0)
++              printk ("elan%d: invalid values for NodeId=%d NumNodes=%d\n", dev->Instance, nodeid, numnodes);
++          else
++              printk ("elan%d: setting NodeId=%d NumNodes=%d NumLevels=%d\n", dev->Instance, dev->Position.pos_nodeid,
++                      dev->Position.pos_nodes, dev->Position.pos_levels);
++      }
++    }
++
++    MOD_DEC_USE_COUNT;
++    free_page ((unsigned long) page);
++
++    return (count);
++}
++
++
++void
++elan3_procfs_device_init (ELAN3_DEV *dev)
++{
++    struct proc_dir_entry *dir, *p;
++    char name[NAME_MAX];
++
++    sprintf (name, "device%d", dev->Instance);
++    dir = dev->Osdep.procdir = proc_mkdir (name, elan3_procfs_root);
++
++    if ((p = create_proc_entry ("position", 0, dir)) != NULL)
++    {
++      p->read_proc  = proc_read_position;
++      p->write_proc = proc_write_position;
++      p->data       = dev;
++      p->owner      = THIS_MODULE;
++    }
++
++}
++
++void
++elan3_procfs_device_fini (ELAN3_DEV *dev)
++{
++    struct proc_dir_entry *dir = dev->Osdep.procdir;
++    char name[NAME_MAX];
++
++    remove_proc_entry ("position", dir);
++
++    sprintf (name, "device%d", dev->Instance);
++    remove_proc_entry (name, elan3_procfs_root);
++}
++
++void
++elan3_procfs_init()
++{
++    extern int eventint_punt_loops;
++    extern int ResolveRequestTimeout;
++
++    elan3_procfs_root = proc_mkdir("elan3",  qsnet_procfs_root);
++
++    elan3_config_root = proc_mkdir("config", elan3_procfs_root);
++
++    qsnet_proc_register_hex (elan3_config_root, "elan3_debug",           &elan3_debug,           0);
++    qsnet_proc_register_hex (elan3_config_root, "elan3_debug_console",   &elan3_debug_console,   0);
++    qsnet_proc_register_hex (elan3_config_root, "elan3_debug_buffer",    &elan3_debug_buffer,    0);
++    qsnet_proc_register_hex (elan3_config_root, "elan3mmu_debug",      &elan3mmu_debug,      0);
++    qsnet_proc_register_int (elan3_config_root, "eventint_punt_loops", &eventint_punt_loops, 0);
++    qsnet_proc_register_int (elan3_config_root, "neterr_timeout",      &ResolveRequestTimeout, 0);
++
++#if defined(__ia64__)
++    {
++      extern int enable_sdram_writecombining;
++      qsnet_proc_register_int (elan3_config_root, "enable_sdram_writecombining", &enable_sdram_writecombining, 0);
++    }
++#endif
++}
++
++void
++elan3_procfs_fini()
++{
++#if defined(__ia64__)
++    remove_proc_entry ("enable_sdram_writecombining", elan3_config_root);
++#endif
++    remove_proc_entry ("neterr_timeout",      elan3_config_root);
++    remove_proc_entry ("eventint_punt_loops", elan3_config_root);
++    remove_proc_entry ("elan3mmu_debug",      elan3_config_root);
++    remove_proc_entry ("elan3_debug_buffer",    elan3_config_root);
++    remove_proc_entry ("elan3_debug_console",   elan3_config_root);
++    remove_proc_entry ("elan3_debug",           elan3_config_root);
++
++    remove_proc_entry ("config",  elan3_procfs_root);
++    remove_proc_entry ("version", elan3_procfs_root);
++ 
++    remove_proc_entry ("elan3",  qsnet_procfs_root);
++}
++
++EXPORT_SYMBOL(elan3_procfs_root);
++EXPORT_SYMBOL(elan3_config_root);
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan3/quadrics_version.h
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan3/quadrics_version.h       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan3/quadrics_version.h    2005-06-01 23:12:54.589440624 -0400
+@@ -0,0 +1 @@
++#define QUADRICS_VERSION "4.30qsnet"
+Index: linux-2.4.21/drivers/net/qsnet/elan3/routecheck.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan3/routecheck.c     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan3/routecheck.c  2005-06-01 23:12:54.590440472 -0400
+@@ -0,0 +1,313 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++/* ------------------------------------------------------------- */
++
++#include <qsnet/kernel.h>
++
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elanvp.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanctxt.h>
++#include <elan3/elandebug.h>
++#include <elan3/urom_addrs.h>
++#include <elan3/thread.h>
++#include <elan3/vmseg.h>
++
++/* ---------------------------------------------------------------------- */
++typedef struct elan3_net_location {
++    int netid;
++    int plane;
++    int level;
++} ELAN3_NET_LOCATION;
++/* ---------------------------------------------------------------------- */
++#define FLIT_LINK_ARRAY_MAX (ELAN3_MAX_LEVELS*2)
++/* ---------------------------------------------------------------------- */
++int 
++elan3_route_follow_link( ELAN3_CTXT *ctxt, ELAN3_NET_LOCATION *loc, int link)
++{
++    ELAN_POSITION *pos = &ctxt->Position;
++
++    if ((link<0) || (link>7)) 
++    {
++      PRINTF1 (ctxt, DBG_VP, "elan3_route_follow_link: link (%d) out of range \n",link);
++      return (ELAN3_ROUTE_INVALID);
++    }   
++
++    /* going up or down ? */
++    if ( link >= pos->pos_arity[loc->level] ) 
++    {
++      /* Up */
++      if (loc->level >= pos->pos_levels)
++          loc->plane = 0;
++      else
++      {
++          if ((loc->level == 1) && (pos->pos_arity[0]  == 8)) /* oddness in some machines ie 512 */
++              loc->plane = (16 * ( loc->plane / 8 )) + (4 * ( loc->plane % 4)) 
++                  +(link - pos->pos_arity[loc->level]);
++          else
++              loc->plane = (loc->plane * (8 - pos->pos_arity[loc->level]))
++                  +(link - pos->pos_arity[loc->level]);
++      }
++      loc->level--; 
++      if ( loc->level < 0 )
++      {
++          PRINTF0 (ctxt, DBG_VP, "elan3_route_follow_link: link goes off the top\n");
++          return (ELAN3_ROUTE_INVALID_LEVEL);
++      }
++      loc->netid = loc->netid / pos->pos_arity[loc->level];
++    }
++    else
++    {
++      /* going down */
++      if ((loc->level == 0) && (pos->pos_arity[0] == 8)) /* oddness in some machines ie 512 */
++          loc->netid = link % 2;
++      else
++          loc->netid =(loc->netid * pos->pos_arity[loc->level])+link;
++
++      loc->level++;
++      if (loc->level > pos->pos_levels)
++      {
++          PRINTF0 (ctxt, DBG_VP, "elan3_route_follow_link: link goes off the bottom\n");
++          return (ELAN3_ROUTE_INVALID_LEVEL); 
++      }
++
++      if ( loc->level >= (pos->pos_levels-1))
++          loc->plane = 0;
++      else
++          if ((loc->level == 1) && (pos->pos_arity[0] == 8)) /* oddness in some machines ie 512 */
++              loc->plane = (((loc->plane)>>2)*2) - ( ((loc->plane)>>2) & 3  ) + ((link<2)?0:4); /* ((p/4) % 4) */
++          else 
++              loc->plane = loc->plane/(8-pos->pos_arity[loc->level]);
++    }
++    return (ELAN3_ROUTE_SUCCESS);
++}
++/* ---------------------------------------------------------------------- */
++int /* assumes they are connected, really only used for finding the MyLink */
++elan3_route_get_mylink (ELAN_POSITION *pos, ELAN3_NET_LOCATION *locA, ELAN3_NET_LOCATION *locB)
++{
++    /* whats the My Link for locA to LocB */
++    if ( locA->level > locB->level ) 
++      return locB->plane - (locA->plane * (8 - pos->pos_arity[locA->level])) + pos->pos_arity[locA->level];
++    
++    return  locB->netid - (locA->netid * pos->pos_arity[locA->level]);
++}
++/* ---------------------------------------------------------------------- */
++#define FIRST_GET_HIGH_PRI(FLIT)    (FLIT & FIRST_HIGH_PRI)
++#define FIRST_GET_AGE(FLIT)         ((FLIT & FIRST_AGE(15))>>11)
++#define FIRST_GET_TIMEOUT(FLIT)     ((FLIT & FIRST_TIMEOUT(3))>>9)
++#define FIRST_GET_NEXT(FLIT)        ((FLIT & FIRST_PACKED(3))>>7)
++#define FIRST_GET_ROUTE(FLIT)       (FLIT & 0x7f)
++#define FIRST_GET_BCAST(FLIT)       (FLIT & 0x40)
++#define FIRST_GET_IS_INVALID(FLIT)  ((FLIT & 0x78) == 0x08)
++#define FIRST_GET_TYPE(FLIT)        ((FLIT & 0x30)>>4)
++#define PRF_GET_ROUTE(FLIT,N)       ((FLIT >> (N*4)) & 0x0F)
++#define PRF_GET_IS_MYLINK(ROUTE)    (ROUTE == PACKED_MYLINK)
++#define PRF_GET_IS_NORMAL(ROUTE)    (ROUTE & 0x8)
++#define PRF_GET_NORMAL_LINK(ROUTE)  (ROUTE & 0x7)
++#define PRF_MOVE_ON(INDEX,NEXT)     do { if (NEXT==3) {NEXT=0;INDEX++;} else {NEXT++; }} while (0);
++/* ---------------------------------------------------------------------- */
++int /* turn level needed or -1 if not possible */
++elan3_route_get_min_turn_level( ELAN_POSITION *pos, int nodeId)
++{
++    int l,range = 1;
++
++    for(l=pos->pos_levels-1;l>=0;l--)
++    {
++      range = range * pos->pos_arity[l];
++      
++      if ( ((pos->pos_nodeid - (pos->pos_nodeid % range)) <= nodeId ) 
++           && (nodeId <= (pos->pos_nodeid - (pos->pos_nodeid % range)+range -1))) 
++          return l;
++    }
++    return -1;
++}
++/* ---------------------------------------------------------------------- */
++int  
++elan3_route_check(ELAN3_CTXT *ctxt, E3_uint16 *flits, int destNodeId)
++{
++    ELAN3_NET_LOCATION lastLoc,currLoc;
++    int               err;
++    int               turnLevel;
++    int               goingDown;
++    int               lnk,index,next,val;
++    ELAN_POSITION    *pos = &ctxt->Position;
++   
++    /* is the dest possible */
++    if ( (destNodeId <0 ) || (destNodeId >= pos->pos_nodes))
++      return  (ELAN3_ROUTE_PROC_RANGE);
++
++    /* 
++     * walk the route, 
++     * - to see if we get there 
++     * - checking we dont turn around 
++     */
++    currLoc.netid = pos->pos_nodeid;         /* the elan */
++    currLoc.plane = 0;
++    currLoc.level = pos->pos_levels;
++
++    turnLevel = currLoc.level; /* track the how far the route goes in */
++    goingDown = 0;             /* once set we cant go up again ie only one change of direction */
++
++    /* move onto the network from the elan */
++    if ((err=elan3_route_follow_link(ctxt,&currLoc,4)) != ELAN3_ROUTE_SUCCESS) 
++    {
++      PRINTF0 (ctxt, DBG_VP, "elan3_route_check: initial elan3_route_follow_link failed\n");
++      return err;
++    }
++    /* do the first part of flit */
++    switch ( FIRST_GET_TYPE(flits[0]) ) 
++    {
++    case 0  /* sent */   : { lnk = (flits[0] & 0x7);                                break; }    
++    case PACKED_MYLINK         : { lnk = pos->pos_nodeid % pos->pos_arity[pos->pos_levels-1];    break; }
++    case PACKED_ADAPTIVE : { lnk = 7; /* all routes are the same just check one */    break; }
++    default : 
++      PRINTF1 (ctxt, DBG_VP, "elan3_route_check: unexpected first flit (%d)\n",flits[0]);
++      return (ELAN3_ROUTE_INVALID); 
++    }
++    
++    /* move along this link and check new location */
++    memcpy(&lastLoc,&currLoc,sizeof(ELAN3_NET_LOCATION)); /* keep track of last loc */
++    if ((err=elan3_route_follow_link(ctxt,&currLoc,lnk)) != ELAN3_ROUTE_SUCCESS ) 
++    {
++      PRINTF0 (ctxt, DBG_VP, "elan3_route_check: elan3_route_follow_link failed\n");
++      return err;
++    }
++    if ((currLoc.level > pos->pos_levels) || (currLoc.level < 0 )) 
++    { 
++      PRINTF0 (ctxt, DBG_VP, "elan3_route_check: route leaves machine\n");
++      return (ELAN3_ROUTE_INVALID_LEVEL);
++    }
++    if ( lastLoc.level < currLoc.level ) 
++    {
++      turnLevel = lastLoc.level;
++      goingDown = 1;
++    }
++    else 
++    {
++      if (turnLevel > currLoc.level)
++          turnLevel =  currLoc.level;
++      if  (goingDown) 
++      {
++          PRINTF0 (ctxt, DBG_VP, "elan3_route_check: route ocilated\n");
++          return (ELAN3_ROUTE_OCILATES);
++      }
++    }   
++
++    /* loop on doing the remaining flits */
++    index = 1;
++    next  = FIRST_GET_NEXT(flits[0]);
++    val   = PRF_GET_ROUTE(flits[index],next);
++    while(val)
++    {
++      if (PRF_GET_IS_NORMAL(val) ) 
++          lnk = PRF_GET_NORMAL_LINK(val);
++      else
++      {
++        switch ( val ) 
++        {
++        case  PACKED_MYLINK : 
++        {
++            lnk = elan3_route_get_mylink(pos, &currLoc,&lastLoc);
++            break;
++        }
++        default : 
++            PRINTF1 (ctxt, DBG_VP, "elan3_route_check: unexpected packed flit (%d)\n",val);
++            return (ELAN3_ROUTE_INVALID);
++        }
++      }
++
++      /* move along this link and check new location */
++      memcpy(&lastLoc,&currLoc,sizeof(ELAN3_NET_LOCATION)); /* keep track of last loc */
++      if ((err=elan3_route_follow_link(ctxt,&currLoc,lnk)) != ELAN3_ROUTE_SUCCESS) 
++          return err;
++      
++      if ((currLoc.level > pos->pos_levels ) || ( currLoc.level < 0 ))
++      { 
++          PRINTF0 (ctxt, DBG_VP, "elan3_route_check: route leaves machine\n");
++          return (ELAN3_ROUTE_INVALID_LEVEL);
++      }
++
++      if ( lastLoc.level < currLoc.level ) 
++          goingDown = 1;
++      else 
++      {
++          if (turnLevel > currLoc.level)
++              turnLevel =  currLoc.level;
++          if  (goingDown) 
++          {
++              PRINTF0 (ctxt, DBG_VP, "elan3_route_check: route ocilated\n");
++              return (ELAN3_ROUTE_OCILATES);
++          }
++      }   
++
++      /* move to next part of flit */
++      PRF_MOVE_ON(index,next);
++      if ( index >= MAX_FLITS)
++      {
++          PRINTF0 (ctxt, DBG_VP, "elan3_route_check: route too long\n");
++          return (ELAN3_ROUTE_TOO_LONG);
++      }
++      /* extract the new value */
++      val = PRF_GET_ROUTE(flits[index],next);
++    }
++
++    /* have we got to where we want ? */
++    if ((currLoc.level != pos->pos_levels) || (currLoc.netid != destNodeId))
++    {
++      PRINTF2 (ctxt, DBG_VP, "elan3_route_check: goes to %d instead of %d\n",currLoc.netid , destNodeId );
++      return (ELAN3_ROUTE_WRONG_DEST);
++    }
++
++    /*
++     * there is the case of src == dest 
++     * getTurnLevel returns pos->pos_levels, and turnLevel is (pos->pos_levels -1) 
++     * then we assume they really want to  go onto the network.
++     * otherwise we check that the turn at the appriate level
++     */
++    if ( (pos->pos_nodeid != destNodeId) || ( turnLevel != (pos->pos_levels -1)) )
++    {
++      int lev;
++      if ((lev = elan3_route_get_min_turn_level(pos,destNodeId)) == -1) 
++      {
++          PRINTF0 (ctxt, DBG_VP, "elan3_route_check: cant calculate turn level\n");
++          return (ELAN3_ROUTE_INVALID); /* not sure this can happen here as checks above should protect me */
++      }
++      if (turnLevel != lev) 
++      {
++          PRINTF2 (ctxt, DBG_VP, "elan3_route_check: turn level should be %d but is %d \n", lev, turnLevel);
++          return (ELAN3_ROUTE_TURN_LEVEL);
++      }
++    }
++    return (ELAN3_ROUTE_SUCCESS);
++}
++/* ---------------------------------------------------------------------- */
++int
++elan3_route_broadcast_check(ELAN3_CTXT *ctxt , E3_uint16 *flits, int lowNode, int highNode ) 
++{
++    E3_uint16 flitsTmp[MAX_FLITS];
++    int       nflits,i;
++    
++    nflits = GenerateRoute (&ctxt->Position, flitsTmp, lowNode, highNode, DEFAULT_ROUTE_TIMEOUT, DEFAULT_ROUTE_PRIORITY);
++    
++    for(i=0;i<nflits;i++)
++      if ( flitsTmp[i] != flits[i] ) 
++      {
++          PRINTF3 (ctxt, DBG_VP, "elan3_route_broadcast_check:  flit[%d] %d (should be %d)\n",i,flits[i],flitsTmp[i]);
++          return (ELAN3_ROUTE_INVALID);   
++      }
++    
++    return (ELAN3_ROUTE_SUCCESS);
++}
++/* ---------------------------------------------------------------------- */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan3/route_table.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan3/route_table.c    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan3/route_table.c 2005-06-01 23:12:54.591440320 -0400
+@@ -0,0 +1,560 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "$Id: route_table.c,v 1.23 2003/09/24 13:57:25 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/os/route_table.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elanvp.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanctxt.h>
++#include <elan3/elandebug.h>
++
++static sdramaddr_t
++AllocateLargeRoute (ELAN3_DEV *dev, ELAN3_ROUTE_TABLE *tbl, int ctxnum, E3_uint64 *smallRoute)
++{
++    int                 bit = -1;
++    ELAN3_ROUTES  *rent;
++    unsigned long flags;
++    
++    spin_lock_irqsave (&tbl->Lock, flags);
++    
++    for (rent = tbl->LargeRoutes; rent; rent = rent->Next)
++    {
++      if ((bit = bt_freebit (rent->Bitmap, NROUTES_PER_BLOCK)) != -1)
++          break;
++    }
++    
++    if (bit == -1)                                            /* No spare entries in large routes */
++    {                                                         /* so allocate a new page */
++      PRINTF0 (DBG_DEVICE, DBG_VP, "AllocateLargeRoute: allocate route entries\n");
++      
++      spin_unlock_irqrestore (&tbl->Lock, flags);
++
++      KMEM_ZALLOC(rent, ELAN3_ROUTES *, sizeof (ELAN3_ROUTES), TRUE);
++      
++      if (rent == (ELAN3_ROUTES *) NULL)
++          return ((sdramaddr_t) 0);
++      
++      rent->Routes = elan3_sdram_alloc (dev, PAGESIZE);
++      if (rent->Routes == (sdramaddr_t) 0)
++      {
++          KMEM_FREE (rent, sizeof (ELAN3_ROUTES));
++          return ((sdramaddr_t) 0);
++      }
++
++      spin_lock_irqsave (&tbl->Lock, flags);
++
++      /* Add to list of large routes */
++      rent->Next       = tbl->LargeRoutes;
++      tbl->LargeRoutes = rent;
++
++      /* and use entry 0 */
++      bit = 0;
++    }
++    
++    /* Set the bit in the bitmap to mark this route as allocated */
++    BT_SET (rent->Bitmap, bit);
++    
++    /* And generate the small route pointer and the pointer to the large routes */
++    (*smallRoute) = BIG_ROUTE_PTR(rent->Routes + (bit*NBYTES_PER_LARGE_ROUTE), ctxnum);
++
++    PRINTF4 (DBG_DEVICE, DBG_VP, "AllocateLargeRoute: rent %p using entry %d at %lx with route pointer %llx\n",
++           rent, bit, rent->Routes + (bit * NBYTES_PER_LARGE_ROUTE), (long long) (*smallRoute));
++
++    /* Invalidate the large route */
++    elan3_sdram_zeroq_sdram (dev, rent->Routes + (bit * NBYTES_PER_LARGE_ROUTE), NBYTES_PER_LARGE_ROUTE);
++
++    spin_unlock_irqrestore (&tbl->Lock, flags);
++
++    return (rent->Routes + (bit * NBYTES_PER_LARGE_ROUTE));
++}
++
++static void
++FreeLargeRoute (ELAN3_DEV *dev, ELAN3_ROUTE_TABLE *tbl, E3_uint64 smallRoute)
++{
++    E3_Addr      addr = (E3_Addr) (smallRoute & ((1ULL << ROUTE_CTXT_SHIFT)-1));
++    ELAN3_ROUTES *rent;
++
++    PRINTF1 (DBG_DEVICE, DBG_VP, "FreeLargeRoute: free route %llx\n", (long long) smallRoute);
++
++    ASSERT (SPINLOCK_HELD (&tbl->Lock));
++
++    for (rent = tbl->LargeRoutes; rent; rent = rent->Next)
++    {
++      if (rent->Routes <= addr && (rent->Routes + ROUTE_BLOCK_SIZE) > addr)
++      {
++          int indx = (addr - rent->Routes)/NBYTES_PER_LARGE_ROUTE;
++          
++          PRINTF2 (DBG_DEVICE, DBG_VP, "FreeLargeRoute: rent=%p indx=%d\n", rent, indx);
++          
++          BT_CLEAR(rent->Bitmap, indx);
++          return;
++      }
++    }
++
++    panic ("elan: FreeLargeRoute - route not found in large route tables");
++}
++
++static void
++FreeLargeRoutes (ELAN3_DEV *dev, ELAN3_ROUTE_TABLE *tbl)
++{
++    ELAN3_ROUTES *rent;
++
++    while ((rent = tbl->LargeRoutes) != NULL)
++    {
++      PRINTF1 (DBG_DEVICE, DBG_VP, "FreeLargeRoutes: free rent %p\n", rent);
++
++      tbl->LargeRoutes = rent->Next;
++
++      elan3_sdram_free (dev, rent->Routes, PAGESIZE);
++      
++      KMEM_FREE (rent, sizeof(ELAN3_ROUTES));
++    }
++}
++
++int
++GetRoute (ELAN3_DEV *dev, ELAN3_ROUTE_TABLE *tbl, int process, E3_uint16 *flits)
++{
++    E3_uint64 routeValue;
++    sdramaddr_t largeRouteOff;
++  
++    if (process < 0 || process >= tbl->Size)
++      return (EINVAL);
++
++    routeValue = elan3_sdram_readq (dev, tbl->Table + process * NBYTES_PER_SMALL_ROUTE);
++
++    if (routeValue & ROUTE_PTR)
++    {
++      largeRouteOff = (routeValue & ROUTE_PTR_MASK);
++      
++      routeValue = elan3_sdram_readq (dev, largeRouteOff + 0);
++      flits[0] = routeValue & 0xffff;
++      flits[1] = (routeValue >> 16)  & 0xffff;
++      flits[2] = (routeValue >> 32)  & 0xffff;
++      flits[3] = (routeValue >> 48)  & 0xffff;
++      
++      routeValue = elan3_sdram_readq (dev, largeRouteOff + 8);
++      flits[4] = routeValue & 0xffff;
++      flits[5] = (routeValue >> 16)  & 0xffff;
++      flits[6] = (routeValue >> 32)  & 0xffff;
++      flits[6] = (routeValue >> 48)  & 0xffff;
++    }
++    else
++    {
++      flits[0] = routeValue & 0xffff;
++      flits[1] = (routeValue >> 16)  & 0xffff;
++      flits[2] = (routeValue >> 32)  & 0xffff;
++    }
++
++    return (ESUCCESS);
++}
++
++ELAN3_ROUTE_TABLE *
++AllocateRouteTable (ELAN3_DEV *dev, int size)
++{
++    ELAN3_ROUTE_TABLE *tbl;
++
++    KMEM_ZALLOC (tbl, ELAN3_ROUTE_TABLE *, sizeof (ELAN3_ROUTE_TABLE), TRUE);
++
++    if (tbl == (ELAN3_ROUTE_TABLE *) NULL)
++      return (NULL);
++    
++    tbl->Size  = size;
++    tbl->Table = elan3_sdram_alloc (dev, size*NBYTES_PER_SMALL_ROUTE);
++
++    if (tbl->Table == 0)
++    {
++      KMEM_FREE (tbl, sizeof (ELAN3_ROUTE_TABLE));
++      return (NULL);
++    }
++    spin_lock_init (&tbl->Lock);
++
++    /* zero the route table */
++    elan3_sdram_zeroq_sdram (dev, tbl->Table, size*NBYTES_PER_SMALL_ROUTE);
++
++    return (tbl);
++}
++
++void
++FreeRouteTable (ELAN3_DEV *dev, ELAN3_ROUTE_TABLE *tbl)
++{
++    elan3_sdram_free (dev, tbl->Table, tbl->Size*NBYTES_PER_SMALL_ROUTE);
++
++    FreeLargeRoutes (dev, tbl);
++
++    spin_lock_destroy (&tbl->Lock);
++
++    KMEM_FREE (tbl, sizeof (ELAN3_ROUTE_TABLE));
++}
++
++int
++LoadRoute (ELAN3_DEV *dev, ELAN3_ROUTE_TABLE *tbl, int process, int ctxnum, int nflits, E3_uint16 *flits)
++{
++    E3_uint64   routeValue;
++    E3_uint64   largeRouteValue;
++    sdramaddr_t   largeRouteOff;
++    unsigned long flags;
++
++    if (process < 0 || process >= tbl->Size)
++      return (EINVAL);
++
++    PRINTF3 (DBG_DEVICE, DBG_VP, "LoadRoute: table %lx process %d ctxnum %x\n", tbl->Table ,process, ctxnum);
++
++    if (nflits < 4)
++    {
++      spin_lock_irqsave (&tbl->Lock, flags);
++
++      /* See if we're replacing a "large" route */
++      routeValue = elan3_sdram_readq (dev, tbl->Table + process * NBYTES_PER_SMALL_ROUTE);
++      if (routeValue & ROUTE_PTR)
++          FreeLargeRoute (dev, tbl, routeValue);
++
++      routeValue = SMALL_ROUTE(flits, ctxnum);
++
++      if ( routeValue &  ROUTE_PTR)
++          PRINTF0 (DBG_DEVICE, DBG_VP, "SHOULD BE  A SMALL ROUTE !!!!!!!\n");
++
++      PRINTF2 (DBG_DEVICE, DBG_VP, "LoadRoute: loading small route %d  %llx\n", process, (long long) routeValue);
++      elan3_sdram_writeq (dev, tbl->Table + process * NBYTES_PER_SMALL_ROUTE, routeValue);
++    }
++    else
++    {
++      E3_uint64 value0 = BIG_ROUTE0(flits);
++      E3_uint64 value1 = BIG_ROUTE1(flits);
++
++      if ((largeRouteOff = AllocateLargeRoute (dev, tbl, ctxnum, &largeRouteValue)) == (sdramaddr_t) 0)
++          return (ENOMEM);
++
++      spin_lock_irqsave (&tbl->Lock, flags);
++          
++      routeValue = elan3_sdram_readq (dev, tbl->Table + process * NBYTES_PER_SMALL_ROUTE);
++
++      if ((routeValue & ROUTE_PTR) == 0)
++          elan3_sdram_writeq (dev, tbl->Table + process * NBYTES_PER_SMALL_ROUTE, largeRouteValue);
++      else
++      {
++          FreeLargeRoute (dev, tbl, largeRouteValue);
++
++          largeRouteOff   = (routeValue & ROUTE_PTR_MASK);
++      }
++
++      PRINTF3 (DBG_DEVICE, DBG_VP, "LoadRoute: loading large route %d - %llx %llx\n", process, 
++               (long long) value0, (long long) value1);
++
++      elan3_sdram_writeq (dev, largeRouteOff + 0, value0);
++      elan3_sdram_writeq (dev, largeRouteOff + 8, value1);
++    }
++
++    spin_unlock_irqrestore (&tbl->Lock, flags);
++    return (ESUCCESS);
++}
++void
++InvalidateRoute (ELAN3_DEV *dev, ELAN3_ROUTE_TABLE *tbl, int process)
++{
++    E3_uint64 routeValue;
++    unsigned long flags;
++
++    if (process < 0 || process >= tbl->Size)
++      return;
++
++    spin_lock_irqsave (&tbl->Lock, flags);
++
++    /* unset ROUTE_VALID
++     * does not matter if its short or long, will check when we re-use it
++     */
++    routeValue = elan3_sdram_readq (dev, tbl->Table + process * NBYTES_PER_SMALL_ROUTE);
++    elan3_sdram_writeq (dev, tbl->Table + process * NBYTES_PER_SMALL_ROUTE, (routeValue & (~ROUTE_VALID)));
++
++    spin_unlock_irqrestore (&tbl->Lock, flags);
++}
++void
++ValidateRoute (ELAN3_DEV *dev, ELAN3_ROUTE_TABLE *tbl, int process)
++{
++    E3_uint64 routeValue;
++    unsigned long flags;
++
++    if (process < 0 || process >= tbl->Size)
++      return;
++
++    PRINTF2 (DBG_DEVICE, DBG_VP, "ValidateRoute: table %ld process %d  \n", tbl->Table ,process);
++
++    spin_lock_irqsave (&tbl->Lock, flags);
++
++    /* set ROUTE_VALID
++     */
++    routeValue = elan3_sdram_readq (dev, tbl->Table + process * NBYTES_PER_SMALL_ROUTE);
++    elan3_sdram_writeq (dev, tbl->Table + process * NBYTES_PER_SMALL_ROUTE, (routeValue | ROUTE_VALID));
++
++    spin_unlock_irqrestore (&tbl->Lock, flags);
++}
++void
++ClearRoute (ELAN3_DEV *dev, ELAN3_ROUTE_TABLE *tbl, int process)
++{
++    E3_uint64 routeValue;
++    unsigned long flags;
++
++    if (process < 0 || process >= tbl->Size)
++      return;
++
++    spin_lock_irqsave (&tbl->Lock, flags);
++
++    PRINTF2 (DBG_DEVICE, DBG_VP, "ClearRoute: table %ld process %d  \n", tbl->Table ,process);
++
++    routeValue = elan3_sdram_readq (dev, tbl->Table + process * NBYTES_PER_SMALL_ROUTE);
++
++    elan3_sdram_writeq (dev, tbl->Table + process * NBYTES_PER_SMALL_ROUTE, 0);
++
++    if (routeValue & ROUTE_PTR)
++      FreeLargeRoute (dev, tbl, routeValue);
++
++    spin_unlock_irqrestore (&tbl->Lock, flags);
++}
++
++static int
++ElanIdEqual (ELAN_POSITION *pos, int level, int ida, int idb)
++{
++    int l;
++
++    for (l = pos->pos_levels-1; l >= level; l--)
++    {
++      ida /= pos->pos_arity[l];
++      idb /= pos->pos_arity[l];
++    }
++      
++    return (ida == idb);
++}
++
++static int
++RouteDown (ELAN_POSITION *pos, int level, int elanid)
++{
++    int l;
++
++    for (l = (pos->pos_levels - 1); level < pos->pos_levels - 1; level++, l--)
++    { 
++      if (  pos->pos_arity[l] )
++          elanid /= pos->pos_arity[l];    
++    }
++    elanid %= pos->pos_arity[l];
++
++    return elanid;
++}
++
++static int
++InitPackedAndFlits (u_char *packed, E3_uint16 *flits)
++{
++    int rb = 0;
++
++    bzero ((caddr_t) packed, MAX_PACKED+4);
++    bzero ((caddr_t) flits, MAX_FLITS * sizeof (E3_uint16));
++
++    /* Initialise 4 bytes of packed, so that the "padding" */
++    /* NEVER terminates with 00, as this is recognised as */
++    /* as CRC flit */
++    packed[rb++] = 0xF;
++    packed[rb++] = 0xF;
++    packed[rb++] = 0xF;
++    packed[rb++] = 0xF;
++
++    return (rb);
++}
++
++static int
++PackThemRoutesUp (E3_uint16 *flits, u_char *packed, int rb, int timeout, int highPri)
++{
++    int i, nflits;
++
++    flits[0] |= FIRST_TIMEOUT(timeout);
++    if (highPri)
++      flits[0] |= FIRST_HIGH_PRI;
++
++    /* round up the number of route bytes to flits */
++    /* and subtract the 4 extra we've padded out with */
++    nflits = (rb-1)/4;
++    
++    for (i = nflits; i > 0; i--)
++    {
++      flits[i] = (packed[rb-1] << 12 |
++                  packed[rb-2] << 8  |
++                  packed[rb-3] << 4  |
++                  packed[rb-4] << 0);
++      rb -= 4;
++    }
++    
++    /* Now set the position of the first packed route  */
++    /* byte in the 2nd 16 bit flit, taking account of the */
++    /* 4 byte padding */
++    flits[0] |= FIRST_PACKED (4-rb);
++    
++    return (nflits+1);
++}
++
++int
++GenerateRoute (ELAN_POSITION *pos, E3_uint16 *flits, int lowid, int highid, int timeout, int highPri)
++{
++    int     broadcast  = (lowid != highid);
++    int     rb         = 0;
++    int           first      = 1;
++    int     noRandom   = 0;
++    int     level;
++    u_char  packed[MAX_PACKED+4];
++    int     numDownLinks;
++
++    rb = InitPackedAndFlits (packed, flits);
++
++    for (level = pos->pos_levels-1;                           /* Move up out of the elan */
++       level > 0 && ! (ElanIdEqual (pos, level, pos->pos_nodeid, lowid) &&
++                       ElanIdEqual (pos, level, pos->pos_nodeid, highid)); level--)
++    {
++      noRandom |= pos->pos_random_disabled & (1 << (pos->pos_levels-1-level));
++    }
++    
++    for (level = pos->pos_levels-1;                           /* Move up out of the elan */
++       level > 0 && ! (ElanIdEqual (pos, level, pos->pos_nodeid, lowid) &&
++                       ElanIdEqual (pos, level, pos->pos_nodeid, highid)); level--)
++    {
++      numDownLinks = pos->pos_arity [level];
++      if (first)
++      {
++          if (broadcast || noRandom)
++              flits[0] = FIRST_BCAST_TREE;
++          else
++          {
++              if (numDownLinks == 4) 
++                  flits[0] = FIRST_ADAPTIVE;
++              else
++                  flits[0] = FIRST_ROUTE( numDownLinks + ( lowid % (8-numDownLinks) ));
++          }
++          first = 0;
++      }
++      else
++      {
++          if (broadcast || noRandom)
++              packed[rb++] = PACKED_BCAST_TREE;
++          else 
++          {
++              if (numDownLinks == 4) 
++                  packed[rb++] = PACKED_ADAPTIVE;
++              else
++                  packed[rb++] = PACKED_ROUTE( numDownLinks + ( lowid % (8-numDownLinks) ));
++          }               
++      }
++    }
++    
++    while (level < pos->pos_levels)
++    {
++      int lowRoute  = RouteDown (pos, level, lowid);
++      int highRoute = RouteDown (pos, level, highid);
++
++      if (first)
++      {
++          if (broadcast)
++              flits[0] = FIRST_BCAST(highRoute, lowRoute);
++          else
++              flits[0] = FIRST_ROUTE(lowRoute);
++
++          first = 0;
++      }
++      else
++      {
++          if (broadcast)
++          {
++              packed[rb++] = PACKED_BCAST0(highRoute, lowRoute);
++              packed[rb++] = PACKED_BCAST1(highRoute, lowRoute);
++          }
++          else
++              packed[rb++] = PACKED_ROUTE(lowRoute);
++      }
++      
++      level++;
++    }
++
++#ifdef ELITE_REVA_SUPPORTED
++    if (broadcast && (pos->pos_levels == 3))
++    {
++      packed[rb++] = PACKED_BCAST0(0, 0);
++      packed[rb++] = PACKED_BCAST1(0, 0);
++    }
++#endif
++
++    return (PackThemRoutesUp (flits, packed, rb, timeout, highPri));
++}
++
++int
++GenerateCheckRoute (ELAN_POSITION *pos, E3_uint16 *flits, int level, int adaptive)
++{
++    int     notfirst = 0;
++    int     l, rb;
++    u_char  packed[MAX_PACKED+4];
++
++    rb = InitPackedAndFlits (packed, flits);
++
++    for (l = pos->pos_levels-1; l > level; l--)
++      if (! notfirst++)
++          flits[0] = adaptive ? FIRST_ADAPTIVE : FIRST_BCAST_TREE;
++      else
++          packed[rb++] = adaptive ? PACKED_ADAPTIVE : PACKED_BCAST_TREE;
++
++    if (! notfirst++ ) 
++      flits[0] = FIRST_MYLINK;
++    else
++      packed[rb++] = PACKED_MYLINK;
++
++    for (l++ /* consume mylink */; l < pos->pos_levels; l++)
++      if (! notfirst++)
++          flits[0] = FIRST_ROUTE (RouteDown (pos, l, pos->pos_nodeid));
++      else
++          packed[rb++] = PACKED_ROUTE (RouteDown (pos, l, pos->pos_nodeid));
++
++
++    return (PackThemRoutesUp (flits, packed, rb, DEFAULT_ROUTE_TIMEOUT, HIGH_ROUTE_PRIORITY));
++}
++
++
++/*
++ * In this case "level" is the number of levels counted from the bottom.
++ */
++int
++GenerateProbeRoute (E3_uint16 *flits, int nodeid, int level, int *linkup, int *linkdown, int adaptive )
++{
++    int           first = 1;
++    int     i, rb;
++    u_char  packed[MAX_PACKED+4];
++
++    rb = InitPackedAndFlits (packed, flits);
++
++    /* Generate "up" routes */
++    for (i = 0; i < level; i++)
++    {
++      if (first)
++          flits[0] = linkup ? FIRST_ROUTE(linkup[i]) : adaptive ? FIRST_ADAPTIVE : FIRST_BCAST_TREE;
++      else
++          packed[rb++] = linkup ? PACKED_ROUTE(linkup[i]) : adaptive ? PACKED_ADAPTIVE : PACKED_BCAST_TREE;
++      first = 0;
++    }
++
++    /* Generate a "to-me" route down */
++    if (first)
++      flits[0] = FIRST_MYLINK;
++    else
++      packed[rb++] = PACKED_MYLINK;
++
++    for (i = level-1; i >= 0; i--)
++      packed[rb++] =  PACKED_ROUTE(linkdown[i]);
++
++    return (PackThemRoutesUp (flits, packed, rb, DEFAULT_ROUTE_TIMEOUT, HIGH_ROUTE_PRIORITY));
++}
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan3/sdram.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan3/sdram.c  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan3/sdram.c       2005-06-01 23:12:54.593440016 -0400
+@@ -0,0 +1,807 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: sdram.c,v 1.17 2003/09/24 13:57:25 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/os/sdram.c,v $*/
++
++
++#include <qsnet/kernel.h>
++
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elandebug.h>
++
++/* sdram access functions */
++#define sdram_off_to_bank(dev,off)    (&dev->SdramBanks[(off) >> ELAN3_SDRAM_BANK_SHIFT])
++#define sdram_off_to_offset(dev,off)  ((off) & (ELAN3_SDRAM_BANK_SIZE-1))
++#define sdram_off_to_bit(dev,indx,off)        (sdram_off_to_offset(dev,off) >> (SDRAM_MIN_BLOCK_SHIFT+(indx)))
++
++#define sdram_off_to_mapping(dev,off) (sdram_off_to_bank(dev,off)->Mapping + sdram_off_to_offset(dev,off))
++    
++unsigned char
++elan3_sdram_readb (ELAN3_DEV *dev, sdramaddr_t off)
++{
++    return (readb ((unsigned char *) sdram_off_to_mapping(dev, off)));
++}
++
++unsigned short
++elan3_sdram_readw (ELAN3_DEV *dev, sdramaddr_t off)
++{
++    return (readw ((unsigned short *) sdram_off_to_mapping(dev, off)));
++}
++
++unsigned int
++elan3_sdram_readl (ELAN3_DEV *dev, sdramaddr_t off)
++{
++    return (readl ((unsigned int *) sdram_off_to_mapping(dev, off)));
++}
++
++unsigned long long
++elan3_sdram_readq (ELAN3_DEV *dev, sdramaddr_t off)
++{
++    return (readq ((unsigned long long *) sdram_off_to_mapping(dev, off)));
++}
++
++void
++elan3_sdram_writeb (ELAN3_DEV *dev, sdramaddr_t off, unsigned char val)
++{
++    writeb (val, (unsigned char *) sdram_off_to_mapping(dev, off));
++    wmb();
++}
++
++void
++elan3_sdram_writew (ELAN3_DEV *dev, sdramaddr_t off, unsigned short val)
++{
++    writew (val, (unsigned short *) sdram_off_to_mapping(dev, off));
++    wmb();
++}
++
++void
++elan3_sdram_writel (ELAN3_DEV *dev, sdramaddr_t off, unsigned int val)
++{
++    writel (val, (unsigned int *) sdram_off_to_mapping(dev, off));
++    wmb();
++}
++
++void
++elan3_sdram_writeq (ELAN3_DEV *dev, sdramaddr_t off, unsigned long long val)
++{
++    writeq (val, (unsigned long long *) sdram_off_to_mapping(dev, off));
++    wmb();
++}
++
++void
++elan3_sdram_copyb_from_sdram (ELAN3_DEV *dev, sdramaddr_t from, void *to, int nbytes)
++{
++    bcopy ((void *)sdram_off_to_mapping(dev, from), to, nbytes);
++}
++
++void
++elan3_sdram_copyw_from_sdram (ELAN3_DEV *dev, sdramaddr_t from, void *to, int nbytes)
++{
++#ifdef __LITTLE_ENDIAN__
++    bcopy ((void *)sdram_off_to_mapping(dev, from), to, nbytes);
++#else
++#error incorrect for big endian
++#endif
++}
++
++void
++elan3_sdram_copyl_from_sdram (ELAN3_DEV *dev, sdramaddr_t from, void *to, int nbytes)
++{
++#ifdef __LITTLE_ENDIAN__
++    bcopy ((void *)sdram_off_to_mapping(dev, from), to, nbytes);
++#else
++#error incorrect for big endian
++#endif
++}
++
++void
++elan3_sdram_copyq_from_sdram (ELAN3_DEV *dev, sdramaddr_t from, void *to, int nbytes)
++{
++#ifdef __LITTLE_ENDIAN__
++    bcopy ((void *)sdram_off_to_mapping(dev, from), to, nbytes);
++#else
++#error incorrect for big endian
++#endif
++}
++
++#define E3_WRITEBUFFER_SIZE           16
++#define E3_WRITEBUFFER_OFFSET(x)      (((unsigned long) x) & (E3_WRITEBUFFER_SIZE-1))
++#define E3_WRITEBUFFER_BASE(x)                (((unsigned long) x) & ~((unsigned long) (E3_WRITEBUFFER_SIZE-1)))
++
++void
++elan3_sdram_copyb_to_sdram (ELAN3_DEV *dev, void *from, sdramaddr_t to, int nbytes)
++{
++    virtaddr_t dbase = (virtaddr_t) sdram_off_to_mapping (dev, to);
++    virtaddr_t dlim  = (virtaddr_t) dbase + nbytes;
++    virtaddr_t slim  = (virtaddr_t) from + nbytes;
++    unsigned   nbase = E3_WRITEBUFFER_SIZE - E3_WRITEBUFFER_OFFSET (dbase);
++    unsigned   ntop  = E3_WRITEBUFFER_OFFSET (dlim - sizeof (uint8_t)) + sizeof (uint8_t);
++    int        i;
++
++    if (E3_WRITEBUFFER_BASE(dbase) == E3_WRITEBUFFER_BASE(dlim))
++    {
++      for (i = 0; i < nbytes/sizeof(uint8_t); i++)
++          writeb (((uint8_t *) from)[i], &((uint8_t *) dbase)[i]);
++      wmb();
++    }
++    else
++    {
++      if (ntop < E3_WRITEBUFFER_SIZE)
++      {
++          slim -= ntop;
++          dlim -= ntop;
++          
++          for (i = 0; i < ntop/sizeof(uint8_t); i++)
++              writeb (((uint8_t *) slim)[i], &((uint8_t *) dlim)[i]);
++          wmb();
++      }
++      
++      while (dlim >= (dbase + E3_WRITEBUFFER_SIZE))
++      {
++          dlim -= E3_WRITEBUFFER_SIZE;
++          slim -= E3_WRITEBUFFER_SIZE;
++
++          for (i = 0; i < E3_WRITEBUFFER_SIZE/sizeof (uint8_t); i++)
++              writeb (((uint8_t *) slim)[i], &((uint8_t *) dlim)[i]);
++          wmb();
++      }
++      
++      if (nbase < E3_WRITEBUFFER_SIZE)
++      {
++          for (i = 0; i < nbase/sizeof(uint8_t); i++)
++              writeb (((uint8_t *) from)[i], &((uint8_t *) dbase)[i]);
++          wmb();
++      }
++    }
++}
++
++void
++elan3_sdram_zerob_sdram (ELAN3_DEV *dev, sdramaddr_t to, int nbytes)
++{
++    virtaddr_t dbase = (virtaddr_t) sdram_off_to_mapping (dev, to);
++    virtaddr_t dlim  = (virtaddr_t) dbase + nbytes;
++    unsigned   nbase = E3_WRITEBUFFER_SIZE - E3_WRITEBUFFER_OFFSET (dbase);
++    unsigned   ntop  = E3_WRITEBUFFER_OFFSET (dlim - sizeof (uint8_t)) + sizeof (uint8_t);
++    int        i;
++
++    if (E3_WRITEBUFFER_BASE(dbase) == E3_WRITEBUFFER_BASE(dlim))
++    {
++      for (i = 0; i < nbytes/sizeof(uint8_t); i++)
++          writeb (0, &((uint8_t *) dbase)[i]);
++      wmb();
++    }
++    else
++    {
++      if (ntop < E3_WRITEBUFFER_SIZE)
++      {
++          dlim -= ntop;
++          
++          for (i = 0; i < ntop/sizeof(uint8_t); i++)
++              writeb (0, &((uint8_t *) dlim)[i]);
++          wmb();
++      }
++      
++      while (dlim >= (dbase + E3_WRITEBUFFER_SIZE))
++      {
++          dlim -= E3_WRITEBUFFER_SIZE;
++
++          writeq (0, &((uint64_t *) dlim)[0]);
++          writeq (0, &((uint64_t *) dlim)[1]);
++
++          wmb();
++      }
++      
++      if (nbase < E3_WRITEBUFFER_SIZE)
++      {
++          for (i = 0; i < nbase/sizeof(uint8_t); i++)
++              writeb (0, &((uint8_t *) dbase)[i]);
++          wmb();
++      }
++    }
++}
++
++void
++elan3_sdram_copyw_to_sdram (ELAN3_DEV *dev, void *from, sdramaddr_t to, int nbytes)
++{
++    virtaddr_t dbase = (virtaddr_t) sdram_off_to_mapping (dev, to);
++    virtaddr_t dlim  = (virtaddr_t) dbase + nbytes;
++    virtaddr_t slim  = (virtaddr_t) from + nbytes;
++    unsigned   nbase = E3_WRITEBUFFER_SIZE - E3_WRITEBUFFER_OFFSET (dbase);
++    unsigned   ntop  = E3_WRITEBUFFER_OFFSET (dlim - sizeof (uint16_t)) + sizeof (uint16_t);
++    int        i;
++
++    if (E3_WRITEBUFFER_BASE(dbase) == E3_WRITEBUFFER_BASE(dlim))
++    {
++      for (i = 0; i < nbytes/sizeof(uint16_t); i++)
++          writew (((uint16_t *) from)[i], &((uint16_t *) dbase)[i]);
++      wmb();
++    }
++    else
++    {
++      if (ntop < E3_WRITEBUFFER_SIZE)
++      {
++          slim -= ntop;
++          dlim -= ntop;
++
++          for (i = 0; i < ntop/sizeof(uint16_t); i++)
++              writew (((uint16_t *) slim)[i], &((uint16_t *) dlim)[i]);
++          wmb();
++      }
++      
++      while (dlim >= (dbase + E3_WRITEBUFFER_SIZE))
++      {
++          dlim -= E3_WRITEBUFFER_SIZE;
++          slim -= E3_WRITEBUFFER_SIZE;
++
++          writew (((uint16_t *) slim)[0], &((uint16_t *) dlim)[0]);
++          writew (((uint16_t *) slim)[1], &((uint16_t *) dlim)[1]);
++          writew (((uint16_t *) slim)[2], &((uint16_t *) dlim)[2]);
++          writew (((uint16_t *) slim)[3], &((uint16_t *) dlim)[3]);
++          writew (((uint16_t *) slim)[4], &((uint16_t *) dlim)[4]);
++          writew (((uint16_t *) slim)[5], &((uint16_t *) dlim)[5]);
++          writew (((uint16_t *) slim)[6], &((uint16_t *) dlim)[6]);
++          writew (((uint16_t *) slim)[7], &((uint16_t *) dlim)[7]);
++          wmb();
++      }
++      
++      if (nbase < E3_WRITEBUFFER_SIZE)
++      {
++          for (i = 0; i < nbase/sizeof(uint16_t); i++)
++              writew (((uint16_t *) from)[i], &((uint16_t *) dbase)[i]);
++          wmb();
++      }
++    }
++}
++
++void
++elan3_sdram_zerow_sdram (ELAN3_DEV *dev, sdramaddr_t to, int nbytes)
++{
++    virtaddr_t dbase = (virtaddr_t) sdram_off_to_mapping (dev, to);
++    virtaddr_t dlim  = (virtaddr_t) dbase + nbytes;
++    unsigned   nbase = E3_WRITEBUFFER_SIZE - E3_WRITEBUFFER_OFFSET (dbase);
++    unsigned   ntop  = E3_WRITEBUFFER_OFFSET (dlim - sizeof (uint16_t)) + sizeof (uint16_t);
++    int        i;
++
++    if (E3_WRITEBUFFER_BASE(dbase) == E3_WRITEBUFFER_BASE(dlim))
++    {
++      for (i = 0; i < nbytes/sizeof(uint16_t); i++)
++          writew (0, &((uint16_t *) dbase)[i]);
++      wmb();
++    }
++    else
++    {
++      if (ntop < E3_WRITEBUFFER_SIZE)
++      {
++          dlim -= ntop;
++          
++          for (i = 0; i < ntop/sizeof(uint16_t); i++)
++              writew (0, &((uint16_t *) dlim)[i]);
++          wmb();
++      }
++      
++      while (dlim >= (dbase + E3_WRITEBUFFER_SIZE))
++      {
++          dlim -= E3_WRITEBUFFER_SIZE;
++
++          writeq (0, &((uint64_t *) dlim)[0]);
++          writeq (0, &((uint64_t *) dlim)[1]);
++          wmb();
++      }
++      
++      if (nbase < E3_WRITEBUFFER_SIZE)
++      {
++          for (i = 0; i < nbase/sizeof(uint16_t); i++)
++              writew (0, &((uint16_t *) dbase)[i]);
++          wmb();
++      }
++    }
++}
++
++void
++elan3_sdram_copyl_to_sdram (ELAN3_DEV *dev, void *from, sdramaddr_t to, int nbytes)
++{
++    virtaddr_t dbase = (virtaddr_t) sdram_off_to_mapping (dev, to);
++    virtaddr_t dlim  = (virtaddr_t) dbase + nbytes;
++    virtaddr_t slim  = (virtaddr_t) from + nbytes;
++    unsigned   nbase = E3_WRITEBUFFER_SIZE - E3_WRITEBUFFER_OFFSET (dbase);
++    unsigned   ntop  = E3_WRITEBUFFER_OFFSET (dlim - sizeof (uint32_t)) + sizeof (uint32_t);
++    int        i;
++
++    if (E3_WRITEBUFFER_BASE(dbase) == E3_WRITEBUFFER_BASE(dlim))
++    {
++      for (i = 0; i < nbytes/sizeof(uint32_t); i++)
++          writel (((uint32_t *) from)[i], &((uint32_t *) dbase)[i]);
++      wmb();
++    }
++    else
++    {
++      if (ntop < E3_WRITEBUFFER_SIZE)
++      {
++          slim -= ntop;
++          dlim -= ntop;
++
++          for (i = 0; i < ntop/sizeof(uint32_t); i++)
++              writel (((uint32_t *) slim)[i], &((uint32_t *) dlim)[i]);
++          wmb();
++      }
++      
++      while (dlim >= (dbase + E3_WRITEBUFFER_SIZE))
++      {
++          dlim -= E3_WRITEBUFFER_SIZE;
++          slim -= E3_WRITEBUFFER_SIZE;
++
++          writel (((uint32_t *) slim)[0], &((uint32_t *) dlim)[0]);
++          writel (((uint32_t *) slim)[1], &((uint32_t *) dlim)[1]);
++          writel (((uint32_t *) slim)[2], &((uint32_t *) dlim)[2]);
++          writel (((uint32_t *) slim)[3], &((uint32_t *) dlim)[3]);
++          wmb();
++      }
++      
++      if (nbase < E3_WRITEBUFFER_SIZE)
++      {
++          for (i = 0; i < nbase/sizeof(uint32_t); i++)
++              writel (((uint32_t *) from)[i], &((uint32_t *) dbase)[i]);
++          wmb();
++      }
++    }
++}
++
++void
++elan3_sdram_zerol_sdram (ELAN3_DEV *dev, sdramaddr_t to, int nbytes)
++{
++    virtaddr_t dbase = (virtaddr_t) sdram_off_to_mapping (dev, to);
++    virtaddr_t dlim  = (virtaddr_t) dbase + nbytes;
++    unsigned   nbase = E3_WRITEBUFFER_SIZE - E3_WRITEBUFFER_OFFSET (dbase);
++    unsigned   ntop  = E3_WRITEBUFFER_OFFSET (dlim - sizeof (uint32_t)) + sizeof (uint32_t);
++    int        i;
++
++    if (E3_WRITEBUFFER_BASE(dbase) == E3_WRITEBUFFER_BASE(dlim))
++    {
++      for (i = 0; i < nbytes/sizeof(uint32_t); i++)
++          writel (0, &((uint32_t *) dbase)[i]);
++      wmb();
++    }
++    else
++    {
++      if (ntop < E3_WRITEBUFFER_SIZE)
++      {
++          dlim -= ntop;
++
++          for (i = 0; i < ntop/sizeof(uint32_t); i++)
++              writel (0, &((uint32_t *) dlim)[i]);
++          wmb();
++      }
++      
++      while (dlim >= (dbase + E3_WRITEBUFFER_SIZE))
++      {
++          dlim -= E3_WRITEBUFFER_SIZE;
++
++          writeq (0, &((uint64_t *) dlim)[0]);
++          writeq (0, &((uint64_t *) dlim)[1]);
++          wmb();
++      }
++      
++      if (nbase < E3_WRITEBUFFER_SIZE)
++      {
++          for (i = 0; i < nbase/sizeof(uint32_t); i++)
++              writel (0, &((uint32_t *) dbase)[i]);
++          wmb();
++      }
++    }
++}
++
++void
++elan3_sdram_copyq_to_sdram (ELAN3_DEV *dev, void *from, sdramaddr_t to, int nbytes)
++{
++    virtaddr_t dbase = (virtaddr_t) sdram_off_to_mapping (dev, to);
++    virtaddr_t dlim  = (virtaddr_t) dbase + nbytes;
++    virtaddr_t slim  = (virtaddr_t) from + nbytes;
++    unsigned   nbase = E3_WRITEBUFFER_SIZE - E3_WRITEBUFFER_OFFSET (dbase);
++    unsigned   ntop  = E3_WRITEBUFFER_OFFSET (dlim - sizeof (uint64_t)) + sizeof (uint64_t);
++
++    if (E3_WRITEBUFFER_BASE(dbase) == E3_WRITEBUFFER_BASE(dlim))
++    {
++      writeq (((uint64_t *) from)[0], &((uint64_t *) dbase)[0]);
++      wmb();
++    }
++    else
++    {
++      if (ntop < E3_WRITEBUFFER_SIZE)
++      {
++          slim -= ntop;
++          dlim -= ntop;
++
++          writeq (((uint64_t *) slim)[0], &((uint64_t *) dlim)[0]);
++          wmb();
++      }
++      
++      while (dlim >= (dbase + E3_WRITEBUFFER_SIZE))
++      {
++          dlim -= E3_WRITEBUFFER_SIZE;
++          slim -= E3_WRITEBUFFER_SIZE;
++
++          writeq (((uint64_t *) slim)[0], &((uint64_t *) dlim)[0]);
++          writeq (((uint64_t *) slim)[1], &((uint64_t *) dlim)[1]);
++          wmb();
++      }
++      
++      if (nbase < E3_WRITEBUFFER_SIZE)
++      {
++          writeq (((uint64_t *) from)[0], &((uint64_t *) dbase)[0]);
++          wmb();
++      }
++    }
++}
++
++void
++elan3_sdram_zeroq_sdram (ELAN3_DEV *dev, sdramaddr_t to, int nbytes)
++{
++    virtaddr_t dbase = (virtaddr_t) sdram_off_to_mapping (dev, to);
++    virtaddr_t dlim  = (virtaddr_t) dbase + nbytes;
++    unsigned   nbase = E3_WRITEBUFFER_SIZE - E3_WRITEBUFFER_OFFSET (dbase);
++    unsigned   ntop  = E3_WRITEBUFFER_OFFSET (dlim - sizeof (uint64_t)) + sizeof (uint64_t);
++
++    if (E3_WRITEBUFFER_BASE(dbase) == E3_WRITEBUFFER_BASE(dlim))
++    {
++      writeq (0, &((uint64_t *) dbase)[0]);
++      wmb();
++    }
++    else
++    {
++      if (ntop < E3_WRITEBUFFER_SIZE)
++      {
++          dlim -= ntop;
++
++          writeq (0, &((uint64_t *) dlim)[0]);
++          wmb();
++      }
++      
++      while (dlim >= (dbase + E3_WRITEBUFFER_SIZE))
++      {
++          dlim -= E3_WRITEBUFFER_SIZE;
++          
++          writeq (0, &((uint64_t *) dlim)[0]);
++          writeq (0, &((uint64_t *) dlim)[1]);
++          wmb();
++      }
++      
++      if (nbase < E3_WRITEBUFFER_SIZE)
++      {
++          writeq (0, &((uint64_t *) dbase)[0]);
++          wmb();
++      }
++    }
++}
++
++physaddr_t
++elan3_sdram_to_phys (ELAN3_DEV *dev, sdramaddr_t off)
++{
++#if defined(DIGITAL_UNIX)
++    return (KSEG_TO_PHYS (sdram_off_to_mapping (dev, off)));
++#elif defined(LINUX)
++    return (kmem_to_phys ((void *) sdram_off_to_mapping (dev, off)));
++#endif    
++}
++
++/* sdram buddy allocator */
++#define read_next(dev, block)         elan3_sdram_readl(dev, block + 0)
++#define read_prev(dev, block)         elan3_sdram_readl(dev, block + 4)
++#define write_next(dev, block, val)   (elan3_sdram_writel(dev, block + 0, val), val)
++#define write_prev(dev, block, val)   (elan3_sdram_writel(dev, block + 4, val), val)
++
++#define freelist_insert(dev,idx,block)\
++do {\
++    sdramaddr_t next = dev->SdramFreeLists[(idx)];\
++\
++    /*\
++     * block->prev = NULL;\
++     * block->next = next;\
++     * if (next != NULL)\
++     *    next->prev = block;\
++     * freelist = block;\
++     */\
++    write_prev (dev, block, (sdramaddr_t) 0);\
++    write_next (dev, block, next);\
++    if (next != (sdramaddr_t) 0)\
++      write_prev (dev, next, block);\
++    dev->SdramFreeLists[idx] = block;\
++\
++    dev->SdramFreeCounts[idx]++;\
++    dev->Stats.SdramBytesFree += (SDRAM_MIN_BLOCK_SIZE << idx);\
++} while (0)
++
++#define freelist_remove(dev,idx,block)\
++do {\
++    /*\
++     * if (block->prev)\
++     *     block->prev->next = block->next;\
++     * else\
++     *     dev->SdramFreeLists[idx] = block->next;\
++     * if (block->next)\
++     *     block->next->prev = block->prev;\
++     */\
++    sdramaddr_t blocknext = read_next (dev, block);\
++    sdramaddr_t blockprev = read_prev (dev, block);\
++\
++    if (blockprev)\
++      write_next (dev, blockprev, blocknext);\
++    else\
++      dev->SdramFreeLists[idx] = blocknext;\
++    if (blocknext)\
++      write_prev (dev, blocknext, blockprev);\
++\
++    dev->SdramFreeCounts[idx]--;\
++    dev->Stats.SdramBytesFree -= (SDRAM_MIN_BLOCK_SIZE << idx);\
++} while (0)
++
++#define freelist_removehead(dev,idx,block)\
++do {\
++    sdramaddr_t blocknext = read_next (dev, block);\
++\
++    if ((dev->SdramFreeLists[idx] = blocknext) != 0)\
++      write_prev (dev, blocknext, 0);\
++\
++    dev->SdramFreeCounts[idx]--;\
++    dev->Stats.SdramBytesFree -= (SDRAM_MIN_BLOCK_SIZE << idx);\
++} while (0)
++
++#if defined(DEBUG)
++static int
++display_blocks (ELAN3_DEV *dev, int indx, char *string)
++{
++    sdramaddr_t block;
++    int nbytes = 0;
++
++    printk ("%s - indx %d\n", string, indx);
++    for (block = dev->SdramFreeLists[indx]; block != (sdramaddr_t) 0; block = read_next (dev, block))
++    {
++      printk ("  %lx", block);
++      nbytes += (SDRAM_MIN_BLOCK_SIZE << indx);
++    }
++    printk ("\n");
++
++    return (nbytes);
++}
++
++
++void
++elan3_sdram_display (ELAN3_DEV *dev, char *string)
++{
++    int indx;
++    int nbytes = 0;
++
++    printk ("elan3_sdram_display: dev=%p\n", dev);
++    for (indx = 0; indx < SDRAM_NUM_FREE_LISTS; indx++)
++      if (dev->SdramFreeLists[indx] != (sdramaddr_t) 0)
++          nbytes += display_blocks (dev, indx, string);
++    printk ("\n%d bytes free\n", nbytes);
++}
++
++void
++elan3_sdram_verify (ELAN3_DEV *dev)
++{
++    int indx, size, nbits, i, b;
++    sdramaddr_t block;
++
++    for (indx = 0, size = SDRAM_MIN_BLOCK_SIZE; indx < SDRAM_NUM_FREE_LISTS; indx++, size <<= 1)
++    {
++      unsigned count = 0;
++
++      for (block = dev->SdramFreeLists[indx]; block; block = read_next (dev, block), count++)
++      {
++          ELAN3_SDRAM_BANK *bank = sdram_off_to_bank (dev, block);
++          unsigned         off  = sdram_off_to_offset (dev, block);
++          int              bit  = sdram_off_to_bit (dev, indx, block);
++
++          if ((block & (size-1)) != 0)
++              printk ("elan3_sdram_verify: block=%lx indx=%x - not aligned\n", block, indx);
++          
++          if (bank == NULL || off > bank->Size)
++              printk ("elan3_sdram_verify: block=%lx indx=%x - outside bank\n", block, indx);
++          else if (BT_TEST (bank->Bitmaps[indx], bit) == 0)
++              printk ("elan3_sdram_verify: block=%lx indx=%x - bit not set\n", block, indx);
++          else
++          {
++              for (i = indx-1, nbits = 2; i >= 0; i--, nbits <<= 1)
++              {
++                  bit = sdram_off_to_bit (dev, i, block);
++
++                  for (b = 0; b < nbits; b++)
++                      if (BT_TEST(bank->Bitmaps[i], bit + b))
++                          printk ("elan3_sdram_verify: block=%lx indx=%x - also free i=%d bit=%x\n", block, indx, i, bit+b);
++              }
++          }
++      }
++
++      if (dev->SdramFreeCounts[indx] != count)
++          printk ("elan3_sdram_verify: indx=%x expected %d got %d\n", indx, dev->SdramFreeCounts[indx], count);
++    }
++}
++
++#endif /* defined(DEBUG) */
++
++static void
++free_block (ELAN3_DEV *dev, sdramaddr_t block, int indx)
++{
++    ELAN3_SDRAM_BANK *bank = sdram_off_to_bank (dev, block);
++    unsigned       bit  = sdram_off_to_bit(dev, indx, block);
++    unsigned         size = SDRAM_MIN_BLOCK_SIZE << indx;
++
++    PRINTF3 (DBG_DEVICE, DBG_SDRAM, "free_block: block=%lx indx=%d bit=%x\n", block, indx, bit);
++
++    ASSERT ((block & (size-1)) == 0);
++    ASSERT (BT_TEST (bank->Bitmaps[indx], bit) == 0);
++    
++    while (BT_TEST (bank->Bitmaps[indx], bit ^ 1))
++    {
++      sdramaddr_t buddy = block ^ size;
++      
++      PRINTF3 (DBG_DEVICE, DBG_SDRAM, "free_block: merge block=%lx buddy=%lx indx=%d\n", block, buddy, indx);
++
++      BT_CLEAR (bank->Bitmaps[indx], bit ^ 1);
++
++      freelist_remove (dev, indx, buddy);
++      
++      block = (block < buddy) ? block : buddy;
++      indx++;
++      size <<= 1;
++      bit >>= 1;
++    }
++
++    PRINTF3 (DBG_DEVICE, DBG_SDRAM, "free_block: free block=%lx indx=%d bit=%x\n", block, indx, bit);
++
++    freelist_insert (dev, indx, block);
++
++    BT_SET (bank->Bitmaps[indx], bit);
++}
++
++void
++elan3_sdram_init (ELAN3_DEV *dev)
++{
++    int indx;
++
++    spin_lock_init (&dev->SdramLock);
++
++    for (indx = 0; indx < SDRAM_NUM_FREE_LISTS; indx++)
++    {
++      dev->SdramFreeLists[indx]  = (sdramaddr_t) 0;
++      dev->SdramFreeCounts[indx] = 0;
++    }
++}
++
++void
++elan3_sdram_fini (ELAN3_DEV *dev)
++{
++    spin_lock_destroy (&dev->SdramLock);
++}
++
++void
++elan3_sdram_add (ELAN3_DEV *dev, sdramaddr_t base, sdramaddr_t top)
++{
++    register int indx;
++    register unsigned long size;
++
++    /* align to the minimum block size */
++    base = (base + SDRAM_MIN_BLOCK_SIZE - 1) & ~((sdramaddr_t) SDRAM_MIN_BLOCK_SIZE-1);
++    top &= ~((sdramaddr_t) SDRAM_MIN_BLOCK_SIZE-1);
++
++    /* don't allow 0 as a valid "base" */
++    if (base == 0)
++      base = E3_CACHE_SIZE;
++
++    /* carve the bottom to the biggest boundary */
++    for (indx = 0, size = SDRAM_MIN_BLOCK_SIZE; indx < SDRAM_NUM_FREE_LISTS; indx++, size <<= 1)
++    {
++      if ((base & size) == 0)
++          continue;
++
++      if ((base + size) > top)
++          break;
++
++      free_block (dev, base, indx);
++      
++      base += size;
++    }
++
++    /* carve the top down to the biggest boundary */
++    for (indx = 0, size = SDRAM_MIN_BLOCK_SIZE; indx < SDRAM_NUM_FREE_LISTS; indx++, size <<= 1)
++    {
++      if ((top & size) == 0)
++          continue;
++
++      if ((top - size) < base)
++          break;
++
++      free_block (dev, (top - size), indx);
++      
++      top -= size;
++    }
++
++    /* now free of the space in between */
++    while (base < top)
++    {
++      free_block (dev, base, (SDRAM_NUM_FREE_LISTS-1));
++
++      base += SDRAM_MAX_BLOCK_SIZE;
++    }
++}
++
++sdramaddr_t
++elan3_sdram_alloc (ELAN3_DEV *dev, int nbytes)
++{
++    sdramaddr_t block;
++    register int i, indx;
++    unsigned long size;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->SdramLock, flags);
++
++    for (indx = 0, size = SDRAM_MIN_BLOCK_SIZE; size < nbytes; indx++, size <<= 1)
++      ;
++
++    PRINTF2 (DBG_DEVICE, DBG_SDRAM, "elan3_sdram_alloc: nbytes=%d indx=%d\n", nbytes, indx);
++
++    /* find the smallest block which is big enough for this allocation */
++    for (i = indx; i < SDRAM_NUM_FREE_LISTS; i++, size <<= 1)
++      if (dev->SdramFreeLists[i])
++          break;
++    
++    if (i == SDRAM_NUM_FREE_LISTS)
++    {
++      spin_unlock_irqrestore (&dev->SdramLock, flags);
++      return ((sdramaddr_t) 0);
++    }
++    
++    PRINTF2 (DBG_DEVICE, DBG_SDRAM, "elan3_sdram_alloc: use block=%lx indx=%d\n", dev->SdramFreeLists[i], i);
++
++    /* remove the block from the free list */
++    freelist_removehead (dev, i, (block = dev->SdramFreeLists[i]));
++
++    /* clear the approriate bit in the bitmap */
++    BT_CLEAR (sdram_off_to_bank (dev, block)->Bitmaps[i], sdram_off_to_bit (dev,i, block));
++
++    /* and split it up as required */
++    while (i-- > indx)
++      free_block (dev, block + (size >>= 1), i);
++
++    PRINTF1 (DBG_DEVICE, DBG_SDRAM, "elan3_sdram_alloc: return block=%lx\n", block);
++
++    spin_unlock_irqrestore (&dev->SdramLock, flags);
++
++    ASSERT ((block & ((SDRAM_MIN_BLOCK_SIZE << (indx))-1)) == 0);
++
++    return ((sdramaddr_t) block);
++}
++
++void
++elan3_sdram_free (ELAN3_DEV *dev, sdramaddr_t block, int nbytes)
++{
++    register int indx;
++    unsigned long size;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->SdramLock, flags);
++
++    for (indx = 0, size = SDRAM_MIN_BLOCK_SIZE; size < nbytes; indx++, size <<= 1)
++      ;
++
++    PRINTF2 (DBG_DEVICE, DBG_SDRAM, "elan3_sdram_free: indx=%d block=%lx\n", indx, block);
++    
++    free_block (dev, block, indx);
++
++    spin_unlock_irqrestore (&dev->SdramLock, flags);
++}
++
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan3/tproc.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan3/tproc.c  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan3/tproc.c       2005-06-01 23:12:54.594439864 -0400
+@@ -0,0 +1,778 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: tproc.c,v 1.51.2.1 2004/11/15 11:12:36 mike Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/os/tproc.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elanvp.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanctxt.h>
++#include <elan3/elandebug.h>
++#include <elan3/urom_addrs.h>
++#include <elan3/thread.h>
++#include <elan3/elansyscall.h>
++#include <elan3/threadsyscall.h>
++#include <elan3/intrinsics.h>
++#include <elan3/vmseg.h>
++
++int
++HandleTProcTrap (ELAN3_DEV *dev, E3_uint32 *RestartBits)
++{
++    THREAD_TRAP  *trap  = dev->ThreadTrap;
++    int           delay = 1;
++
++    ASSERT(SPINLOCK_HELD (&dev->IntrLock));
++
++    trap->Status.Status  = read_reg32 (dev, Exts.TProcStatus);
++    trap->sp             = read_reg32 (dev, Thread_Desc_SP);
++    trap->pc             = read_reg32 (dev, ExecutePC);
++    trap->npc            = read_reg32 (dev, ExecuteNPC);
++    trap->StartPC        = read_reg32 (dev, StartPC);
++    trap->mi             = GET_STATUS_TRAPTYPE(trap->Status);
++    trap->TrapBits.Bits  = read_reg32 (dev, TrapBits.Bits);
++    trap->DirtyBits.Bits = read_reg32 (dev, DirtyBits.Bits);
++
++    if ( ! (trap->Status.s.WakeupFunction == SleepOneTick) ) {
++      int p,i;
++      E3_uint32 reg = read_reg32 (dev, Exts.InterruptReg);    
++
++      ELAN_REG_REC(reg);
++      p = elan_reg_rec_index;
++      for(i=0;i<ELAN_REG_REC_MAX;i++) {
++          if (elan_reg_rec_file[i] != NULL ) 
++              printk("Elan Reg Record[%2d](%ld): cpu %d  reg %x [%d:%s]\n", p, elan_reg_rec_lbolt[p], elan_reg_rec_cpu[p], elan_reg_rec_reg[p],
++                     elan_reg_rec_line[p], elan_reg_rec_file[p]);
++          p = ( (p+1) % ELAN_REG_REC_MAX);
++      }
++    }
++    
++    ASSERT(trap->Status.s.WakeupFunction == SleepOneTick);
++
++    /* copy the four access fault areas */
++    elan3_sdram_copyq_from_sdram (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, TProc),          (void *) &trap->FaultSave, 16);
++    elan3_sdram_copyq_from_sdram (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, ThreadProcData), (void *) &trap->DataFaultSave, 16);
++    elan3_sdram_copyq_from_sdram (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, ThreadProcInst), (void *) &trap->InstFaultSave, 16);
++    elan3_sdram_copyq_from_sdram (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, ThreadProcOpen), (void *) &trap->OpenFaultSave, 16);
++    
++    /* copy the registers,  note the endian swap flips the odd registers into the even registers
++       and visa versa. */
++    copy_thread_regs (dev, trap->Registers);
++
++    /*
++     * If the output was open then the ack may not have returned yet. Must wait for the
++     * ack to become valid and update trap_dirty with the new value. Will simulate the
++     * instructions later.
++     */
++    if (trap->TrapBits.s.OutputWasOpen)
++    {
++      trap->TrapBits.Bits = read_reg32 (dev, TrapBits.Bits);
++      while (! trap->TrapBits.s.AckBufferValid)
++      {
++          PRINTF0 (DBG_DEVICE, DBG_INTR, "tproc: waiting for ack to become valid\n");
++          trap->TrapBits.Bits = read_reg32 (dev, TrapBits.Bits);
++          DELAY (delay);
++
++          if ((delay <<= 1) == 0) delay = 1;
++      }
++    }
++    
++    /* update device statistics */
++    BumpStat (dev, TProcTraps);
++    switch (trap->mi)
++    {
++    case MI_UnimplementedError:
++      if (trap->TrapBits.s.ForcedTProcTrap)
++          BumpStat (dev, ForcedTProcTraps);
++      if (trap->TrapBits.s.ThreadTimeout)
++      {
++          if (trap->TrapBits.s.PacketTimeout)
++              BumpStat (dev, ThreadOutputTimeouts);
++          else if (trap->TrapBits.s.PacketAckValue == E3_PAckError)
++              BumpStat (dev, ThreadPacketAckErrors);
++      }
++      if (trap->TrapBits.s.TrapForTooManyInsts)
++          BumpStat (dev, TrapForTooManyInsts);
++      break;
++    }
++
++    elan3_sdram_zeroq_sdram (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, TProc), 16);
++    elan3_sdram_zeroq_sdram (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, ThreadProcData), 16);
++    elan3_sdram_zeroq_sdram (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, ThreadProcInst), 16);
++    elan3_sdram_zeroq_sdram (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, ThreadProcOpen), 16);
++
++    *RestartBits |= RestartTProc;
++
++    return (TRUE);
++}
++
++void
++DeliverTProcTrap (ELAN3_DEV *dev, THREAD_TRAP *threadTrap, E3_uint32 Pend)
++{
++    ELAN3_CTXT             *ctxt;
++    THREAD_TRAP      *trap;
++
++    ASSERT(SPINLOCK_HELD (&dev->IntrLock));
++
++    ctxt = ELAN3_DEV_CTX_TABLE(dev, threadTrap->Status.s.Context);
++
++    if (ctxt == NULL)
++    {
++      PRINTF1 (DBG_DEVICE, DBG_INTR, "DeliverTProcTrap: context %x invalid\n", threadTrap->Status.s.Context);
++      BumpStat (dev, InvalidContext);
++    }
++    else
++    {
++      if (ELAN3_OP_TPROC_TRAP (ctxt, threadTrap) == OP_DEFER)
++      {
++          if (ELAN3_QUEUE_REALLY_FULL (ctxt->ThreadTrapQ))
++          {
++              ctxt->Status |= CTXT_COMMAND_OVERFLOW_ERROR;
++              StartSwapoutContext (ctxt, Pend, NULL);
++          }
++          else
++          {
++              trap = ELAN3_QUEUE_BACK (ctxt->ThreadTrapQ, ctxt->ThreadTraps);
++              
++              bcopy (threadTrap, trap, sizeof (THREAD_TRAP));
++              
++              PRINTF4 (ctxt, DBG_INTR, "DeliverTProcTrap: SP=%08x PC=%08x NPC=%08x StartPC %08x\n",
++                       trap->sp, trap->pc, trap->npc, trap->StartPC);
++              PRINTF3 (ctxt, DBG_INTR, "       mi=%s trap=%08x dirty=%08x\n",
++                       MiToName (trap->mi), trap->TrapBits.Bits, trap->DirtyBits.Bits);
++              PRINTF3 (ctxt, DBG_INTR, "       FaultSave : FaultAddress %08x EventAddress %08x FSR %08x\n",
++                       trap->FaultSave.s.FaultAddress, trap->FaultSave.s.EventAddress, trap->FaultSave.s.FSR.Status);
++              PRINTF3 (ctxt, DBG_INTR, "       DataFault : FaultAddress %08x EventAddress %08x FSR %08x\n",
++                       trap->DataFaultSave.s.FaultAddress, trap->DataFaultSave.s.EventAddress, trap->DataFaultSave.s.FSR.Status);
++              PRINTF3 (ctxt, DBG_INTR, "       InstFault : FaultAddress %08x EventAddress %08x FSR %08x\n",
++                       trap->InstFaultSave.s.FaultAddress, trap->InstFaultSave.s.EventAddress, trap->InstFaultSave.s.FSR.Status);
++              PRINTF3 (ctxt, DBG_INTR, "       OpenFault : FaultAddress %08x EventAddress %08x FSR %08x\n",
++                       trap->OpenFaultSave.s.FaultAddress, trap->OpenFaultSave.s.EventAddress, trap->OpenFaultSave.s.FSR.Status);
++              
++              PRINTF4 (ctxt, DBG_INTR, "       g0=%08x g1=%08x g2=%08x g3=%08x\n", 
++                       trap->Registers[REG_GLOBALS+(0^WordEndianFlip)], trap->Registers[REG_GLOBALS+(1^WordEndianFlip)], 
++                       trap->Registers[REG_GLOBALS+(2^WordEndianFlip)], trap->Registers[REG_GLOBALS+(3^WordEndianFlip)]);
++              PRINTF4 (ctxt, DBG_INTR, "       g4=%08x g5=%08x g6=%08x g7=%08x\n", 
++                       trap->Registers[REG_GLOBALS+(4^WordEndianFlip)], trap->Registers[REG_GLOBALS+(5^WordEndianFlip)], 
++                       trap->Registers[REG_GLOBALS+(6^WordEndianFlip)], trap->Registers[REG_GLOBALS+(7^WordEndianFlip)]);
++              PRINTF4 (ctxt, DBG_INTR, "       o0=%08x o1=%08x o2=%08x o3=%08x\n", 
++                       trap->Registers[REG_OUTS+(0^WordEndianFlip)], trap->Registers[REG_OUTS+(1^WordEndianFlip)], 
++                       trap->Registers[REG_OUTS+(2^WordEndianFlip)], trap->Registers[REG_OUTS+(3^WordEndianFlip)]);
++              PRINTF4 (ctxt, DBG_INTR, "       o4=%08x o5=%08x o6=%08x o7=%08x\n", 
++                       trap->Registers[REG_OUTS+(4^WordEndianFlip)], trap->Registers[REG_OUTS+(5^WordEndianFlip)], 
++                       trap->Registers[REG_OUTS+(6^WordEndianFlip)], trap->Registers[REG_OUTS+(7^WordEndianFlip)]);
++              PRINTF4 (ctxt, DBG_INTR, "       l0=%08x l1=%08x l2=%08x l3=%08x\n", 
++                       trap->Registers[REG_LOCALS+(0^WordEndianFlip)], trap->Registers[REG_LOCALS+(1^WordEndianFlip)],
++                       trap->Registers[REG_LOCALS+(2^WordEndianFlip)], trap->Registers[REG_LOCALS+(3^WordEndianFlip)]);
++              PRINTF4 (ctxt, DBG_INTR, "       l4=%08x l5=%08x l6=%08x l7=%08x\n", 
++                       trap->Registers[REG_LOCALS+(4^WordEndianFlip)], trap->Registers[REG_LOCALS+(5^WordEndianFlip)],
++                       trap->Registers[REG_LOCALS+(6^WordEndianFlip)], trap->Registers[REG_LOCALS+(7^WordEndianFlip)]);
++              PRINTF4 (ctxt, DBG_INTR, "       i0=%08x i1=%08x i2=%08x i3=%08x\n", 
++                       trap->Registers[REG_INS+(0^WordEndianFlip)], trap->Registers[REG_INS+(1^WordEndianFlip)],
++                       trap->Registers[REG_INS+(2^WordEndianFlip)], trap->Registers[REG_INS+(3^WordEndianFlip)]);
++              PRINTF4 (ctxt, DBG_INTR, "       i4=%08x i5=%08x i6=%08x i7=%08x\n", 
++                       trap->Registers[REG_INS+(4^WordEndianFlip)], trap->Registers[REG_INS+(5^WordEndianFlip)],
++                       trap->Registers[REG_INS+(6^WordEndianFlip)], trap->Registers[REG_INS+(7^WordEndianFlip)]);
++              
++              ELAN3_QUEUE_ADD (ctxt->ThreadTrapQ);
++              kcondvar_wakeupone (&ctxt->Wait, &dev->IntrLock);
++              
++              if (ELAN3_QUEUE_FULL (ctxt->ThreadTrapQ))
++              {
++                  PRINTF0 (ctxt, DBG_INTR, "DeliverTProcTrap: thread queue full,  must swap out\n");
++                  ctxt->Status |= CTXT_THREAD_QUEUE_FULL;
++                  
++                  StartSwapoutContext (ctxt, Pend, NULL);
++              }
++          }
++      }
++    }
++}
++
++int
++NextTProcTrap (ELAN3_CTXT *ctxt, THREAD_TRAP *trap)
++{
++    ELAN3_DEV *dev = ctxt->Device;
++
++    ASSERT (SPINLOCK_HELD (&dev->IntrLock));
++    
++    if (ELAN3_QUEUE_EMPTY (ctxt->ThreadTrapQ))
++      return (0);
++
++    *trap = *ELAN3_QUEUE_FRONT (ctxt->ThreadTrapQ, ctxt->ThreadTraps);
++    ELAN3_QUEUE_REMOVE (ctxt->ThreadTrapQ);
++    
++    return (1);
++}
++
++void
++ResolveTProcTrap (ELAN3_CTXT *ctxt, THREAD_TRAP *trap)
++{
++    int       i;
++    int       res;
++    E3_Addr   StackPointer;
++
++    PRINTF4 (ctxt, DBG_TPROC, "ResolveTProcTrap: SP=%08x PC=%08x NPC=%08x StartPC %08x\n",
++           trap->sp, trap->pc, trap->npc, trap->StartPC);
++    PRINTF3 (ctxt, DBG_TPROC, "       mi=%s trap=%08x dirty=%08x\n",
++           MiToName (trap->mi), trap->TrapBits.Bits, trap->DirtyBits.Bits);
++    PRINTF3 (ctxt, DBG_TPROC, "       FaultSave : FaultAddress %08x EventAddress %08x FSR %08x\n",
++           trap->FaultSave.s.FaultAddress, trap->FaultSave.s.EventAddress, trap->FaultSave.s.FSR.Status);
++    PRINTF3 (ctxt, DBG_TPROC, "       DataFault : FaultAddress %08x EventAddress %08x FSR %08x\n",
++           trap->DataFaultSave.s.FaultAddress, trap->DataFaultSave.s.EventAddress, trap->DataFaultSave.s.FSR.Status);
++    PRINTF3 (ctxt, DBG_TPROC, "       InstFault : FaultAddress %08x EventAddress %08x FSR %08x\n",
++           trap->InstFaultSave.s.FaultAddress, trap->InstFaultSave.s.EventAddress, trap->InstFaultSave.s.FSR.Status);
++    PRINTF3 (ctxt, DBG_TPROC, "       OpenFault : FaultAddress %08x EventAddress %08x FSR %08x\n",
++           trap->OpenFaultSave.s.FaultAddress, trap->OpenFaultSave.s.EventAddress, trap->OpenFaultSave.s.FSR.Status);
++    
++    PRINTF4 (ctxt, DBG_TPROC, "       g0=%08x g1=%08x g2=%08x g3=%08x\n", 
++           trap->Registers[REG_GLOBALS+(0^WordEndianFlip)], trap->Registers[REG_GLOBALS+(1^WordEndianFlip)], 
++           trap->Registers[REG_GLOBALS+(2^WordEndianFlip)], trap->Registers[REG_GLOBALS+(3^WordEndianFlip)]);
++    PRINTF4 (ctxt, DBG_TPROC, "       g4=%08x g5=%08x g6=%08x g7=%08x\n", 
++           trap->Registers[REG_GLOBALS+(4^WordEndianFlip)], trap->Registers[REG_GLOBALS+(5^WordEndianFlip)], 
++           trap->Registers[REG_GLOBALS+(6^WordEndianFlip)], trap->Registers[REG_GLOBALS+(7^WordEndianFlip)]);
++    PRINTF4 (ctxt, DBG_TPROC, "       o0=%08x o1=%08x o2=%08x o3=%08x\n", 
++           trap->Registers[REG_OUTS+(0^WordEndianFlip)], trap->Registers[REG_OUTS+(1^WordEndianFlip)], 
++           trap->Registers[REG_OUTS+(2^WordEndianFlip)], trap->Registers[REG_OUTS+(3^WordEndianFlip)]);
++    PRINTF4 (ctxt, DBG_TPROC, "       o4=%08x o5=%08x o6=%08x o7=%08x\n", 
++           trap->Registers[REG_OUTS+(4^WordEndianFlip)], trap->Registers[REG_OUTS+(5^WordEndianFlip)], 
++           trap->Registers[REG_OUTS+(6^WordEndianFlip)], trap->Registers[REG_OUTS+(7^WordEndianFlip)]);
++    PRINTF4 (ctxt, DBG_TPROC, "       l0=%08x l1=%08x l2=%08x l3=%08x\n", 
++           trap->Registers[REG_LOCALS+(0^WordEndianFlip)], trap->Registers[REG_LOCALS+(1^WordEndianFlip)],
++           trap->Registers[REG_LOCALS+(2^WordEndianFlip)], trap->Registers[REG_LOCALS+(3^WordEndianFlip)]);
++    PRINTF4 (ctxt, DBG_TPROC, "       l4=%08x l5=%08x l6=%08x l7=%08x\n", 
++           trap->Registers[REG_LOCALS+(4^WordEndianFlip)], trap->Registers[REG_LOCALS+(5^WordEndianFlip)],
++           trap->Registers[REG_LOCALS+(6^WordEndianFlip)], trap->Registers[REG_LOCALS+(7^WordEndianFlip)]);
++    PRINTF4 (ctxt, DBG_TPROC, "       i0=%08x i1=%08x i2=%08x i3=%08x\n", 
++           trap->Registers[REG_INS+(0^WordEndianFlip)], trap->Registers[REG_INS+(1^WordEndianFlip)],
++           trap->Registers[REG_INS+(2^WordEndianFlip)], trap->Registers[REG_INS+(3^WordEndianFlip)]);
++    PRINTF4 (ctxt, DBG_TPROC, "       i4=%08x i5=%08x i6=%08x i7=%08x\n", 
++           trap->Registers[REG_INS+(4^WordEndianFlip)], trap->Registers[REG_INS+(5^WordEndianFlip)],
++           trap->Registers[REG_INS+(6^WordEndianFlip)], trap->Registers[REG_INS+(7^WordEndianFlip)]);
++          
++
++    BumpUserStat (ctxt, TProcTraps);
++
++    switch (trap->mi)
++    {
++    case MI_UnimplementedError:
++    {
++      /*
++       * This occurs if the threads processor trapped. All other cases will be for the ucode
++       * thread trapping.
++       */
++      int restart = 1;
++      int skip    = 0;
++      
++      PRINTF1 (ctxt, DBG_TPROC, "TProc: Mi=Unimp. Using trap->TrapBits=%x\n", trap->TrapBits.Bits);
++      
++      /*
++       * Data Access Exception.
++       */
++      if (trap->TrapBits.s.DataAccessException)
++      {
++          ASSERT (CTXT_IS_KERNEL(ctxt) || trap->DataFaultSave.s.FSR.Status == 0 ||
++                  ctxt->Capability.cap_mycontext == trap->DataFaultSave.s.FaultContext);
++
++          PRINTF1 (ctxt, DBG_TPROC, "ResolveTProcTrap: DataAccessException %08x\n", trap->DataFaultSave.s.FaultAddress);
++
++          if ((res = elan3_pagefault (ctxt, &trap->DataFaultSave, 1)) != ESUCCESS)
++          {
++              PRINTF1 (ctxt, DBG_TPROC, "ResolveTProcTrap: elan3_pagefault failed for data %08x\n",
++                       trap->DataFaultSave.s.FaultAddress);
++
++              if (ElanException (ctxt, EXCEPTION_INVALID_ADDR, THREAD_PROC, trap, &trap->DataFaultSave, res) != OP_IGNORE)
++                  restart = 0;
++          }
++      }
++      
++      /* 
++       * Instruction Access Exception.
++       */
++      if (trap->TrapBits.s.InstAccessException)
++      {
++          ASSERT (CTXT_IS_KERNEL (ctxt) || trap->InstFaultSave.s.FSR.Status == 0 ||
++                  ctxt->Capability.cap_mycontext == trap->InstFaultSave.s.FaultContext);
++          
++          PRINTF1 (ctxt, DBG_TPROC, "ResolveTProcTrap: InstAccessException %08x\n", trap->InstFaultSave.s.FaultAddress);
++
++          if ((res = elan3_pagefault (ctxt, &trap->InstFaultSave, 1)) != ESUCCESS)
++          {
++              PRINTF1 (ctxt, DBG_TPROC, "ResolveTProcTrap: elan3_pagefault failed for inst %08x\n",
++                       trap->InstFaultSave.s.FaultAddress);
++
++              ElanException (ctxt, EXCEPTION_INVALID_ADDR, THREAD_PROC, trap, &trap->InstFaultSave, res);
++              restart = 0;
++          }
++      }
++      
++      /*
++       * Forced TProc trap/Unimplemented instruction
++       *
++       *  If there is a force tproc trap then don't look at 
++       *  the unimplemented instruction bit - since it can
++       *  be set in obscure circumstances.
++       */
++      if (trap->TrapBits.s.ForcedTProcTrap)
++          PRINTF0 (ctxt, DBG_TPROC, "ResolveTProcTrap: forced tproc trap, restarting\n");
++      else if (trap->TrapBits.s.Unimplemented)
++      {
++          E3_uint32 instr = ELAN3_OP_LOAD32 (ctxt, trap->pc & PC_MASK);
++
++          PRINTF1 (ctxt, DBG_TPROC, "ResolveTProcTrap: unimplemented instruction %08x\n", instr);
++
++          if ((instr & OPCODE_MASK) == OPCODE_Ticc &&
++              (instr & OPCODE_IMM)  == OPCODE_IMM &&
++              (Ticc_COND(instr)     == Ticc_TA))
++          {
++              switch (INSTR_IMM(instr))
++              {
++              case ELAN3_ELANCALL_TRAPNUM:
++                  /*
++                   * Since the thread cannot easily access the global variable which holds
++                   * the elan system call number, we provide a different trap for the elan
++                   * system call, and copy the system call number into %g1 before calling
++                   * ThreadSyscall().
++                   */
++                  BumpUserStat (ctxt, ThreadElanCalls);
++
++                  if (ThreadElancall (ctxt, trap, &skip) != ESUCCESS)
++                  {
++                      ElanException (ctxt, EXCEPTION_BAD_SYSCALL, THREAD_PROC, trap);
++                      restart = 0;
++                  }
++                  break;
++
++              case ELAN3_SYSCALL_TRAPNUM:
++                  BumpUserStat (ctxt, ThreadSystemCalls);
++
++                  if (ThreadSyscall (ctxt, trap, &skip) != ESUCCESS)
++                  {
++                      ElanException (ctxt, EXCEPTION_BAD_SYSCALL, THREAD_PROC, trap);
++                      restart = 0;
++                  }
++                  break;
++
++              case ELAN3_DEBUG_TRAPNUM:
++                  ElanException (ctxt, EXCEPTION_DEBUG, THREAD_PROC, trap);
++                  skip = 1;
++                  break;
++                  
++              case ELAN3_ABORT_TRAPNUM:
++              default:
++                  ElanException (ctxt, EXCEPTION_UNIMP_INSTR, THREAD_PROC, trap, instr);
++                  restart = 0;
++                  break;
++              }
++                  
++          }
++          else
++          {
++              ElanException (ctxt, EXCEPTION_UNIMP_INSTR, THREAD_PROC, trap, instr);
++              restart = 0;
++          }
++      }
++      
++      /*
++       * Faulted fetching routes.
++       */
++      if (trap->TrapBits.s.OpenRouteFetch)
++      {
++          PRINTF1 (ctxt, DBG_TPROC, "ResolveTProcTrap: OpenRouteFetch %08x\n", trap->OpenFaultSave.s.FaultAddress);
++          
++          if ((res = ResolveVirtualProcess (ctxt, trap->OpenFaultSave.s.FaultAddress)) != ESUCCESS &&
++              ElanException (ctxt, EXCEPTION_INVALID_PROCESS, THREAD_PROC, trap, trap->DataFaultSave.s.FaultAddress, res) != OP_IGNORE)
++          {
++              restart = 0;
++          }
++          else if (RollThreadToClose (ctxt, trap, E3_PAckDiscard) != ESUCCESS)        /* Force a discard */
++          {
++              restart = 0;
++          }
++      }
++      
++      /*
++       * Thread Timeout
++       */
++      if (trap->TrapBits.s.ThreadTimeout)
++      {
++          if (ElanException (ctxt, EXCEPTION_PACKET_TIMEOUT, THREAD_PROC, trap) != OP_IGNORE)
++              restart = 0;
++          else
++          {
++              PRINTF0 (ctxt, DBG_TPROC, "ResolveTProcTrap: timeout or PAckError!\n");
++              
++              /* Might deschedule the thread for a while or mark the link error here. */
++              if (! trap->TrapBits.s.OutputWasOpen && RollThreadToClose (ctxt, trap, trap->TrapBits.s.PacketAckValue) != ESUCCESS)
++              {
++                  restart = 0;
++              }
++          }
++      }
++      
++      /*
++       * Open exception
++       */
++      if (trap->TrapBits.s.OpenException)
++      {
++          PRINTF0 (ctxt, DBG_TPROC, "ResolveTProcTrap: open exception\n");
++          if (ElanException (ctxt, EXCEPTION_THREAD_KILLED, THREAD_PROC, trap) != OP_IGNORE)
++              restart = 0;
++      }
++      
++      /*
++       * Too many instructions.
++       */
++      if (trap->TrapBits.s.TrapForTooManyInsts)
++      {
++          PRINTF0 (ctxt, DBG_TPROC, "ResolveTProcTrap: too many instructions\n");
++          if (ElanException (ctxt, EXCEPTION_THREAD_KILLED, THREAD_PROC, trap) != OP_IGNORE)
++              restart = 0;
++      }
++      
++      if (restart)
++      {
++          /*
++           * If the output was open when the trap was taken then the trap code must move
++           * the PC on past the close instruction and simulate the effect of all the instructions
++           * that do not output onto the link. The value of the ack received is then used to
++           * simulate the close instruction.
++           */
++          if (trap->TrapBits.s.OutputWasOpen && RollThreadToClose(ctxt, trap, trap->TrapBits.s.PacketAckValue) != ESUCCESS)
++          {
++              /*
++               * Don't restart if we couldn't roll it forweards 
++               * to a close instruction.
++               */
++              break;
++          }
++
++          /*
++           * We must check back 3 instructions from the PC,  and if we see the
++           * c_close_cookie() sequence then we must execute the instructions to
++           * the end of it.
++           */
++          /* XXXX: code to be written */
++          
++          StackPointer = SaveThreadToStack (ctxt, trap, skip);
++          
++          ReissueStackPointer (ctxt, StackPointer);
++      }
++      
++      break;
++    }
++    
++    /*
++     * This case is different from the others as %o6 has been overwritten with
++     * the SP. The real PC can be read from StartPC and written back
++     * into %o6 on the stack.
++     */
++    case MI_TProcNext:                        /* Reading the outs block */
++    {
++      E3_Addr stack = (trap->sp & SP_MASK) - sizeof (E3_Stack);
++
++      if (ELAN3_OP_START_FAULT_CHECK (ctxt))
++      {
++          ELAN3_OP_END_FAULT_CHECK (ctxt);
++
++          PRINTF0 (ctxt, DBG_TPROC, "ResolveTProcTrap: faulted writing StartPc to o6\n");
++          ElanException (ctxt, EXCEPTION_CANNOT_SAVE_THREAD, THREAD_PROC, NULL);
++          break;
++      }
++      ELAN3_OP_STORE32 (ctxt, stack + offsetof (E3_Stack, Outs[6]), trap->StartPC & PC_MASK);
++      ELAN3_OP_END_FAULT_CHECK (ctxt);
++      /* DROPTHROUGH */
++    }
++    /*
++     * all of these will be generated when starting up a thread.
++     * Just re-issue the command after fixing the trap. The ucode keeps the startup
++     * from trap information in Thread_Desc_SP while it is still loading the regs.
++     */
++    case MI_WaitForGlobalsRead:               /* Reading the globals block (trap restart) */
++    case MI_WaitForNPCRead:           /* Reading the nPC, V and C (trap restart) */
++    case MI_WaitForPCload:            /* Reading the PC, N and Z (trap restart) */
++    case MI_WaitForInsRead:           /* Reading the ins block (trap restart) */
++    case MI_WaitForLocals:            /* Reading the ins block (trap restart) */
++    case MI_WaitForPCload2:           /* Reading the PC (normal thread start) */
++    case MI_WaitForSpStore:           /* Writing the SP to the outs block */
++      PRINTF2 (ctxt, DBG_TPROC, "ResolveTProcTrap: %s %08x\n", MiToName (trap->mi), trap->InstFaultSave.s.FaultAddress);
++
++      if ((res = elan3_pagefault (ctxt, &trap->FaultSave, 1)) != ESUCCESS)
++      {
++          PRINTF1 (ctxt, DBG_TPROC, "ResolveTProcTrap: elan3_pagefault failed at %08x\n",
++                   trap->FaultSave.s.FaultAddress);
++          if (ElanException (ctxt, EXCEPTION_INVALID_ADDR, THREAD_PROC, &trap->FaultSave, trap, res) != OP_IGNORE)
++              break;
++      }
++
++      ReissueStackPointer (ctxt, trap->sp);
++      break;
++      
++      /*
++       * These traps could occur after the threads proc has stopped (either for a wait,
++       * break, or suspend, but not a trap). Must simulate the uCode's job.
++       */
++    case MI_WaitForOutsWrite:         /* Writing the outs block */
++    case MI_WaitForNPCWrite:          /* Writing the nPC block */
++    {
++      E3_uint32 DeschedBits = (trap->TrapBits.Bits & E3_TProcDescheduleMask);
++      E3_Addr   stack       = (trap->sp & SP_MASK) - sizeof (E3_Stack);
++      
++      PRINTF1 (ctxt, DBG_TPROC, "ResolveTProcTrap: trapped on %s while stopping a thread\n", MiToName(trap->mi));
++      
++      /*
++       * Copy npc into o6.
++       */
++      trap->Registers[REG_OUTS+(6^WordEndianFlip)] = trap->npc;
++      
++      if (ELAN3_OP_START_FAULT_CHECK (ctxt))
++      {
++          ELAN3_OP_END_FAULT_CHECK (ctxt);
++
++          PRINTF0 (ctxt, DBG_TPROC, "ResolveTProcTrap: faulted writing outs to stack\n");
++          ElanException (ctxt, EXCEPTION_CANNOT_SAVE_THREAD, THREAD_PROC, NULL);
++          break;
++      }
++      
++      /*
++       * Now write the outs back to the stack. NOTE then endian flip is undone.
++       */
++      for (i = 0; i < 8; i++)
++          ELAN3_OP_STORE32 (ctxt, stack + offsetof (E3_Stack, Outs[i]), trap->Registers[REG_OUTS+(i^WordEndianFlip)]);
++      ELAN3_OP_END_FAULT_CHECK (ctxt);
++      
++      /*
++       * thread has been saved. Now find out why the thread proc stopped.
++       */
++      if (DeschedBits == E3_TProcDescheduleSuspend)
++      {
++          PRINTF0 (ctxt, DBG_TPROC, "ResolveTProcTrap: suspend instruction executed\n");
++          break;
++      }
++      
++      /*
++       * Break. Just reissue the command.
++       */
++      if (DeschedBits == E3_TProcDescheduleBreak)
++      {
++          PRINTF1 (ctxt, DBG_TPROC, "ResolveTProcTrap: break instruction, reissue sp %08x\n", trap->sp);
++          ReissueStackPointer (ctxt, trap->sp);
++          break;
++      }
++      
++      ASSERT (DeschedBits == E3_TProcDescheduleWait);
++       
++      /* DROPTHROUGH to fix up a wait event */
++    }
++    
++    /*
++     * Trapped here trying to execute a wait instruction. All the thread state has already
++     * been saved and the trap has been fixed so simplest thing to do is to start the
++     * thread up at the wait instruction again.
++     */
++    case MI_WaitForEventWaitAddr:     /* Reading back the %o0,%o1 pair for a
++                                         wait event instr. */
++    case MI_WaitForWaitEventAccess:   /* Locked dword read of the event location.
++                                                 Note that this read is done with write
++                                         permissions so we never get a trap on the write */
++    {
++      E3_Addr stack = (trap->sp & SP_MASK) - sizeof (E3_Stack);
++      
++      if ((res = elan3_pagefault (ctxt, &trap->FaultSave, 1)) != ESUCCESS)
++      {
++          PRINTF1 (ctxt, DBG_TPROC, "ResolveTProcTrap: elan3_pagefault failed at %08x\n", 
++                   trap->FaultSave.s.FaultAddress);
++          if (ElanException (ctxt, EXCEPTION_INVALID_ADDR, THREAD_PROC, trap, &trap->DataFaultSave, res) != OP_IGNORE)
++              break;
++      }
++
++      if (ELAN3_OP_START_FAULT_CHECK (ctxt))
++      {
++          ELAN3_OP_END_FAULT_CHECK (ctxt);
++
++          PRINTF0 (ctxt, DBG_TPROC, "ResolveTProcTrap: faulted writing pc to stack\n");
++          ElanException (ctxt, EXCEPTION_CANNOT_SAVE_THREAD, THREAD_PROC, NULL);
++          break;
++      }
++
++      ELAN3_OP_STORE32 (ctxt, stack + offsetof (E3_Stack, Outs[6]), trap->pc);
++      ELAN3_OP_END_FAULT_CHECK (ctxt);
++      
++      ReissueStackPointer (ctxt, trap->sp);
++      break;
++    }
++    
++    /*
++     * Assume the fault will be fixed by FixupEventTrap.
++     */
++    default:
++      FixupEventTrap (ctxt, THREAD_PROC, trap, trap->mi, &trap->FaultSave, 0);
++      break;
++    }
++}
++
++int
++TProcNeedsRestart (ELAN3_CTXT *ctxt)
++{
++    return (ctxt->ItemCount[LIST_THREAD] != 0);
++}
++
++void
++RestartTProcItems (ELAN3_CTXT *ctxt)
++{
++    void     *item;
++    E3_uint32 StackPointer;
++
++    kmutex_lock (&ctxt->SwapListsLock);
++    
++    while (ctxt->ItemCount[LIST_THREAD])
++    {
++      if (! ELAN3_OP_GET_WORD_ITEM (ctxt, LIST_THREAD, &item, &StackPointer))
++          ctxt->ItemCount[LIST_THREAD] = 0;
++      else
++      {
++          if (IssueCommand (ctxt, offsetof (E3_CommandPort, RunThread), StackPointer, 0) == ISSUE_COMMAND_RETRY)
++          {
++              ELAN3_OP_PUTBACK_ITEM (ctxt, LIST_THREAD, item);
++              kmutex_unlock (&ctxt->SwapListsLock);
++              return;
++          }
++          
++          ctxt->ItemCount[LIST_THREAD]--;
++          ELAN3_OP_FREE_WORD_ITEM (ctxt, item);
++      }
++    }
++    kmutex_unlock (&ctxt->SwapListsLock);
++}
++
++E3_Addr
++SaveThreadToStack (ELAN3_CTXT *ctxt, THREAD_TRAP *trap, int SkipInstruction)
++{
++    E3_Addr      stack = (trap->sp & SP_MASK) - sizeof (E3_Stack);
++    E3_Addr      orflag;
++    register int i;
++
++    /*
++     * When the thread deschedules normally, the N & Z flags are written 
++     * to the stack in o6, and the V & C flags are lost.
++     * Since the Elan will store the NPC into o6 (to skip the instruction), 
++     * the CC flags are visible to the trap handler in the trapped PC and NPC.   
++     * If the instruction needs to be re-executed then the CC flags need to be
++     * kept in the right place to be read in when the thread re-starts.
++     *
++     * PC  has N & Z from trapped NPC.
++     * NPC has V & C from trapped PC.
++     */
++    if (SkipInstruction)
++    {
++      trap->Registers[REG_OUTS+(6^WordEndianFlip)]    = trap->npc;
++      trap->Registers[REG_GLOBALS+(0^WordEndianFlip)] = ((trap->npc & PC_MASK) + 4) | (trap->pc & CC_MASK);
++    }
++    else
++    {
++      trap->Registers[REG_OUTS+(6^WordEndianFlip)]    = (trap->pc & PC_MASK) | (trap->npc & CC_MASK);
++      trap->Registers[REG_GLOBALS+(0^WordEndianFlip)] = (trap->npc & PC_MASK) | (trap->pc & CC_MASK);
++    }
++    
++    if (ELAN3_OP_START_FAULT_CHECK(ctxt))
++    {
++      PRINTF0 (ctxt, DBG_TPROC, "RestartThread: faulted writing out thread\n");
++      ELAN3_OP_END_FAULT_CHECK(ctxt);
++
++      ElanException (ctxt, EXCEPTION_CANNOT_SAVE_THREAD, THREAD_PROC, NULL);
++      return ((E3_Addr) 0);
++    }
++
++
++#ifdef DEBUG_PRINTF
++    PRINTF4 (ctxt, DBG_TPROC, "SaveThreadToStack: SP=%08x PC=%08x NPC=%08x DIRTY=%08x\n",
++           trap->sp, trap->pc, trap->npc, trap->DirtyBits.Bits);
++    if (trap->DirtyBits.s.GlobalsDirty)
++    {
++      PRINTF4 (ctxt, DBG_TPROC, "       g0=%08x g1=%08x g2=%08x g3=%08x\n", 
++               trap->Registers[REG_GLOBALS+(0^WordEndianFlip)], trap->Registers[REG_GLOBALS+(1^WordEndianFlip)], 
++               trap->Registers[REG_GLOBALS+(2^WordEndianFlip)], trap->Registers[REG_GLOBALS+(3^WordEndianFlip)]);
++      PRINTF4 (ctxt, DBG_TPROC, "       g4=%08x g5=%08x g6=%08x g7=%08x\n", 
++               trap->Registers[REG_GLOBALS+(4^WordEndianFlip)], trap->Registers[REG_GLOBALS+(5^WordEndianFlip)], 
++               trap->Registers[REG_GLOBALS+(6^WordEndianFlip)], trap->Registers[REG_GLOBALS+(7^WordEndianFlip)]);
++    }
++    if (trap->DirtyBits.s.OutsDirty)
++    {
++      PRINTF4 (ctxt, DBG_TPROC, "       o0=%08x o1=%08x o2=%08x o3=%08x\n", 
++               trap->Registers[REG_OUTS+(0^WordEndianFlip)], trap->Registers[REG_OUTS+(1^WordEndianFlip)], 
++               trap->Registers[REG_OUTS+(2^WordEndianFlip)], trap->Registers[REG_OUTS+(3^WordEndianFlip)]);
++      PRINTF4 (ctxt, DBG_TPROC, "       o4=%08x o5=%08x o6=%08x o7=%08x\n", 
++               trap->Registers[REG_OUTS+(4^WordEndianFlip)], trap->Registers[REG_OUTS+(5^WordEndianFlip)], 
++               trap->Registers[REG_OUTS+(6^WordEndianFlip)], trap->Registers[REG_OUTS+(7^WordEndianFlip)]);
++    }
++    if (trap->DirtyBits.s.LocalsDirty)
++    {
++      PRINTF4 (ctxt, DBG_TPROC, "       l0=%08x l1=%08x l2=%08x l3=%08x\n", 
++               trap->Registers[REG_LOCALS+(0^WordEndianFlip)], trap->Registers[REG_LOCALS+(1^WordEndianFlip)], 
++               trap->Registers[REG_LOCALS+(2^WordEndianFlip)], trap->Registers[REG_LOCALS+(3^WordEndianFlip)]);
++      PRINTF4 (ctxt, DBG_TPROC, "       l4=%08x l5=%08x l6=%08x l7=%08x\n", 
++               trap->Registers[REG_LOCALS+(4^WordEndianFlip)], trap->Registers[REG_LOCALS+(5^WordEndianFlip)], 
++               trap->Registers[REG_LOCALS+(6^WordEndianFlip)], trap->Registers[REG_LOCALS+(7^WordEndianFlip)]);
++    }
++    if (trap->DirtyBits.s.InsDirty)
++    {
++      PRINTF4 (ctxt, DBG_TPROC, "       i0=%08x i1=%08x i2=%08x i3=%08x\n", 
++               trap->Registers[REG_INS+(0^WordEndianFlip)], trap->Registers[REG_INS+(1^WordEndianFlip)], 
++               trap->Registers[REG_INS+(2^WordEndianFlip)], trap->Registers[REG_INS+(3^WordEndianFlip)]);
++      PRINTF4 (ctxt, DBG_TPROC, "       i4=%08x i5=%08x i6=%08x i7=%08x\n", 
++               trap->Registers[REG_INS+(4^WordEndianFlip)], trap->Registers[REG_INS+(5^WordEndianFlip)], 
++               trap->Registers[REG_INS+(6^WordEndianFlip)], trap->Registers[REG_INS+(7^WordEndianFlip)]);
++    }
++#endif 
++    
++    PRINTF1 (ctxt, DBG_TPROC, "flushing registers to stack %08x\n", stack);
++
++    /* 
++     * NOTE - store the register to the stack in reverse order, since the stack 
++     * will be allocated in sdram, and we cannot use the sdram accessing functions 
++     * here, as it is "mapped" in user-space.
++     */
++    for (i = 0; i < 8; i++)
++    {
++      if (trap->DirtyBits.s.GlobalsDirty & (1 << i))
++          ELAN3_OP_STORE32 (ctxt, stack + offsetof (E3_Stack, Globals[i]), trap->Registers[REG_GLOBALS+(i^WordEndianFlip)]);
++      if (trap->DirtyBits.s.OutsDirty & (1 << i))
++          ELAN3_OP_STORE32 (ctxt, stack + offsetof (E3_Stack, Outs[i]),    trap->Registers[REG_OUTS+(i^WordEndianFlip)]);
++      if (trap->DirtyBits.s.LocalsDirty & (1 << i))
++          ELAN3_OP_STORE32 (ctxt, stack + offsetof (E3_Stack, Locals[i]),  trap->Registers[REG_LOCALS+(i^WordEndianFlip)]);
++      if (trap->DirtyBits.s.InsDirty & (1 << i))
++          ELAN3_OP_STORE32 (ctxt, stack + offsetof (E3_Stack, Ins[i]),     trap->Registers[REG_INS+(i^WordEndianFlip)]);
++    }
++
++    /* always restore all registers */
++    orflag = ThreadRestartFromTrapBit | ThreadReloadAllRegs;
++    
++    ELAN3_OP_END_FAULT_CHECK (ctxt);
++
++    return (trap->sp | orflag);
++}
++
++void
++ReissueStackPointer (ELAN3_CTXT *ctxt, E3_Addr StackPointer)
++{
++    PRINTF1 (ctxt, DBG_TPROC, "ReissueStackPointer : Queue SP %08x\n", StackPointer);
++    
++    kmutex_lock (&ctxt->SwapListsLock);
++    ctxt->ItemCount[LIST_THREAD]++;
++    ELAN3_OP_PUT_WORD_ITEM (ctxt, LIST_THREAD, StackPointer);
++    kmutex_unlock (&ctxt->SwapListsLock);
++}
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan3/tprocinsts.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan3/tprocinsts.c     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan3/tprocinsts.c  2005-06-01 23:12:54.595439712 -0400
+@@ -0,0 +1,401 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: tprocinsts.c,v 1.20 2003/09/24 13:57:25 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/os/tprocinsts.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elanvp.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanctxt.h>
++#include <elan3/elandebug.h>
++#include <elan3/urom_addrs.h>
++#include <elan3/thread.h>
++#include <elan3/vmseg.h>
++#include <elan3/elan3mmu.h>
++
++#define MAXINSTR      256             /* # Instructions to look at while looking for close */
++
++static E3_uint32 ALU (ELAN3_CTXT *ctxt,
++                    E3_uint32 fcode, E3_uint32 X, E3_uint32 Y,
++                    E3_uint32 *Z, E3_uint32 *N, E3_uint32 *C, E3_uint32 *V);
++
++char *OpcodeNames[] =
++{
++   "ADD   ",
++   "AND   ",
++   "OR    ",
++   "XOR   ",
++   "SUB   ",
++   "ANDN  ",
++   "ORN   ",
++   "XNOR  ",
++   "ADDX  ",
++   "UNIP  ",
++   "UMUL  ",
++   "SMUL  ",
++   "SUBX  ",
++   "UNIP  ",
++   "UDIV  ",
++   "SDIV  ",
++   "ADDcc ",
++   "ANDcc ",
++   "ORcc  ",
++   "XORcc ",
++   "SUBcc ",
++   "ANDNcc",
++   "ORNcc ",
++   "XNORcc",
++   "ADDXcc",
++   "UNIPcc",
++   "UMULcc",
++   "SMULcc",
++   "SUBXcc",
++   "UNIPcc",
++   "UDIVcc",
++   "SDIVcc"
++};
++
++#define REGISTER_VALUE(trap, rN)              (((rN) == 0) ? 0 : (trap)->Registers[(rN)^WordEndianFlip])
++#define ASSIGN_REGISTER(trap, rN, value)      ((rN) != 0 ? trap->Registers[(rN)^WordEndianFlip] = (value) : 0)
++
++int
++RollThreadToClose (ELAN3_CTXT *ctxt, THREAD_TRAP *trap, E3_uint32 PAckVal)
++{
++    E3_Addr   pc      = (trap->pc & PC_MASK);
++    E3_Addr   npc     = (trap->npc & PC_MASK);
++    E3_uint32 Z       = (trap->npc & PSR_Z_BIT) ? 1 : 0;
++    E3_uint32 N       = (trap->npc & PSR_N_BIT) ? 1 : 0;
++    E3_uint32 C       = (trap->pc  & PSR_C_BIT) ? 1 : 0;
++    E3_uint32 V       = (trap->pc  & PSR_V_BIT) ? 1 : 0;
++    E3_uint32 instr;
++    E3_Addr   addr;
++
++    if (ELAN3_OP_START_FAULT_CHECK (ctxt))
++    {
++    failed:
++      ELAN3_OP_END_FAULT_CHECK (ctxt);
++
++      ElanException (ctxt, EXCEPTION_SIMULATION_FAILED, THREAD_PROC, trap);
++      return (EFAULT);
++    }
++
++    /*
++     * Thread trapped with output open, or while closing,
++     * so roll the PC forwards to the instruction after the
++     * next c_close, and execute that with the register
++     * specified in c_close set to the trap which occured.
++     * (This is not 1 which means an ACK)
++     */
++    PRINTF1 (ctxt, DBG_TPROC, "RollThreadToClose: roll pc %x to c_close\n", pc);
++    
++    for (;;)
++    {
++      instr = ELAN3_OP_LOAD32 (ctxt, pc);
++
++      PRINTF2 (ctxt, DBG_TPROC, "RollThreadToClose: PC=%x INSTR=%x\n", pc, instr);
++
++      switch (OPCODE_CLASS(instr))
++      {
++      case OPCODE_CLASS_0:
++          switch ((instr) & OPCODE_CLASS0_MASK)
++          {
++          case OPCODE_SETHI:
++              PRINTF3 (ctxt, DBG_TPROC, "PC %x : sethi r%d = %x\n", pc, INSTR_RD(instr), instr << 10);
++
++              ASSIGN_REGISTER (trap, INSTR_RD(instr), instr << 10);
++              break;
++
++          case OPCODE_SENDREG:
++              PRINTF1 (ctxt, DBG_TPROC, "PC %x : sendreg\n", pc);
++              break;
++              
++          case OPCODE_SENDMEM:
++              PRINTF1 (ctxt, DBG_TPROC, "PC %x : sendmem\n", pc);
++              break;
++              
++          case OPCODE_BICC:
++          {
++              int     DoBranch   = (instr >> 28) & 1;
++              int     CondBranch = 1;
++              E3_Addr OldnPC     = npc;
++
++              PRINTF5 (ctxt, DBG_TPROC, "PC %x : Bicc Z=%x N=%x C=%x V=%x ", pc, Z, N, C, V);
++              switch (instr & OPCODE_BICC_MASK)
++              {
++              case OPCODE_BICC_BN:    CondBranch = 0;                 break;
++              case OPCODE_BICC_BE:    DoBranch ^= Z;                  break;
++              case OPCODE_BICC_BLE:   DoBranch ^= Z | (N ^ V);        break;
++              case OPCODE_BICC_BL:    DoBranch ^= N ^ V;              break;
++              case OPCODE_BICC_BLEU:  DoBranch ^= C | Z;              break;
++              case OPCODE_BICC_BCS:   DoBranch ^= C;                  break;
++              case OPCODE_BICC_BNEG:  DoBranch ^= N;                  break;
++              case OPCODE_BICC_BVS:   DoBranch ^= V;                  break;
++              }
++
++              /* Do the branch */
++              if (DoBranch != 0)
++              {
++                  npc = pc + (((instr & 0x3fffff) << 2) |
++                              (((instr & 0x200000) != 0) ? 0xff000000 : 0));
++                  
++                  PRINTF2 (ctxt, DBG_TPROC, "PC %x : branch taken to %x\n", pc, npc);
++              }
++              else
++              {
++                  npc = npc + 4;
++                  PRINTF1 (ctxt, DBG_TPROC, "PC %x : branch not taken\n", pc);
++              }
++              pc = OldnPC;
++
++              /* Test if the next is annuled */
++              if (((instr & OPCODE_BICC_ANNUL) != 0) &
++                  ((DoBranch == 0) | (CondBranch == 0)))
++              {
++                  PRINTF1 (ctxt, DBG_TPROC, "PC %x : branch annulled\n", pc);
++
++                  pc = npc;
++                  npc += 4;
++              }
++
++              /*
++               * we've already consumed the instruction - so continue rather
++               * than break;
++               */
++              continue;
++          }
++          
++          default:
++              PRINTF2 (ctxt, DBG_TPROC, "PC %x : unknown class 0 instr %x\n", pc, instr);
++              goto failed;
++          }
++          break;
++
++      case OPCODE_CLASS_1:
++              PRINTF2 (ctxt, DBG_TPROC, "PC %x : unknown class 1 instr %x\n", pc, instr);
++              goto failed;
++              
++      case OPCODE_CLASS_2:
++      {
++          E3_uint32 X = REGISTER_VALUE (trap, INSTR_RS1(instr));
++          E3_uint32 Y = (instr & OPCODE_IMM) ? INSTR_IMM(instr) : REGISTER_VALUE (trap, INSTR_RS2(instr));
++          
++          if ((instr & OPCODE_NOT_ALUOP) == 0)
++          {
++              E3_uint32 fcode  = (instr >> OPCODE_FCODE_SHIFT) & OPCODE_FCODE_MASK;
++              E3_uint32 result = ALU (ctxt, fcode, X, Y, &Z, &N, &C, &V);
++
++              PRINTF5 (ctxt, DBG_TPROC, "PC %x : %s %x %x -> %x", pc, OpcodeNames[fcode], X, Y, result);
++              PRINTF4 (ctxt, DBG_TPROC, "        Z=%x N=%x C=%x V=%x\n", Z, N, C, V);
++              
++              ASSIGN_REGISTER (trap, INSTR_RD(instr), result);
++          }
++          else
++          {
++              switch (instr & OPCODE_MASK)
++              {
++              case OPCODE_OPEN:
++                  PRINTF1 (ctxt, DBG_TPROC, "PC %x : c_open\n", pc);
++                  break;
++
++              case OPCODE_CLOSE:
++                  PRINTF1 (ctxt, DBG_TPROC, "PC %x : c_close\n", pc);
++                  goto found_close;
++
++              case OPCODE_SLL:
++                  PRINTF1 (ctxt, DBG_TPROC, "PC %x : SLL\n", pc);
++
++                  ASSIGN_REGISTER (trap, INSTR_RD(instr), X << Y);
++                  break;
++                  
++              case OPCODE_SRL:
++                  PRINTF1 (ctxt, DBG_TPROC, "PC %x : SRL\n", pc);
++                  
++                  ASSIGN_REGISTER (trap, INSTR_RD(instr), X >> Y);
++                  break;
++                  
++              case OPCODE_SRA:
++                  PRINTF1 (ctxt, DBG_TPROC, "PC %x : SRA\n", pc);
++                  
++                  ASSIGN_REGISTER (trap, INSTR_RD(instr), X >> Y);
++                  break;
++                  
++              case OPCODE_BREAKTEST:
++                  PRINTF1 (ctxt, DBG_TPROC, "PC %x : BREAKTEST  not allowed while open\n", pc);
++                  goto failed;
++                  
++              case OPCODE_BREAK:
++                  PRINTF1 (ctxt, DBG_TPROC, "PC %x : BREAK not allowed while open\n", pc);
++                  goto failed;
++
++              case OPCODE_SUSPEND:
++                  PRINTF1 (ctxt, DBG_TPROC, "PC %x : SUSPEND not allowed while open\n", pc);
++                  goto failed;
++                  
++              case OPCODE_WAIT:
++                  PRINTF1 (ctxt, DBG_TPROC, "PC %x : WAIT not allowed while open\n", pc);
++                  goto failed;
++
++              default:
++                  PRINTF2 (ctxt, DBG_TPROC, "PC %x : unknown class 2 instr %x\n", pc, instr);
++                  goto failed;
++              }
++          }
++          break;
++      }
++      
++      case OPCODE_CLASS_3:
++      {
++          if ((instr & OPCODE_IMM) != 0)
++              addr = REGISTER_VALUE (trap, INSTR_RS1(instr)) + INSTR_IMM(instr);
++          else
++              addr = (REGISTER_VALUE (trap, INSTR_RS1(instr)) + 
++                      REGISTER_VALUE (trap, INSTR_RS2(instr)));
++
++          switch (instr & OPCODE_MASK)
++          {
++          case OPCODE_LD:
++              PRINTF3 (ctxt, DBG_TPROC, "PC %x : LD [%x], r%d\n", pc, addr, INSTR_RD(instr));
++              
++              ASSIGN_REGISTER (trap, INSTR_RD(instr), ELAN3_OP_LOAD32 (ctxt, addr));
++              break;
++              
++          case OPCODE_LDD:
++          case OPCODE_LDBLOCK16:
++          case OPCODE_LDBLOCK32:
++          case OPCODE_LDBLOCK64:
++              PRINTF2 (ctxt, DBG_TPROC, "PC %x : LDBLOCKx @ %x is not possible while output open\n", pc, addr);
++              goto failed;
++          
++          case OPCODE_ST:
++              PRINTF2 (ctxt, DBG_TPROC, "PC %x : ST @ %x\n", pc, addr);
++              
++              ELAN3_OP_STORE32 (ctxt, addr, REGISTER_VALUE (trap, INSTR_RD(instr)));
++              break;
++                            
++          case OPCODE_STD:
++          case OPCODE_STBLOCK16:
++          case OPCODE_STBLOCK32:
++          case OPCODE_STBLOCK64:
++              PRINTF2 (ctxt, DBG_TPROC, "PC %x : STD @ %x is not posisble while output open\n", pc, addr);
++              goto failed;
++
++          case OPCODE_SWAP:
++              PRINTF2 (ctxt, DBG_TPROC, "PC %x : SWAP @ %x is not posible while output open\n", pc, addr);
++              goto failed;
++              
++          default:
++              PRINTF2 (ctxt, DBG_TPROC, "PC %x : unknown class 3 instr %x\n", pc, instr);
++              goto failed;
++          }
++          break;
++      }}
++
++      pc = npc;
++      npc += 4;
++    }
++    
++found_close:
++    ELAN3_OP_END_FAULT_CHECK (ctxt);
++
++    PRINTF1 (ctxt, DBG_TPROC, "PC %x : c_close\n", pc);
++    
++    /*
++     * Found the new pc, and have the close instruction in *instr
++     */
++    ASSIGN_REGISTER (trap, INSTR_RD(instr), PAckVal);
++    
++    /*
++     * Move to instruction after close.
++    */
++    trap->pc = npc;
++    
++    /* Insert the value of Z and N from the close inst */
++    trap->npc = (npc + 4) | ((PAckVal == E3_PAckOk) ? 1 :
++                           (PAckVal == E3_PAckTestFail) ? 2 : 0);
++
++    return (ESUCCESS);
++}
++
++E3_uint32
++ALU (ELAN3_CTXT *ctxt,
++     E3_uint32 fcode, E3_uint32 X, E3_uint32 Y,
++     E3_uint32 *Z, E3_uint32 *N, E3_uint32 *C, E3_uint32 *V)
++{
++    E3_uint32 XMSB, YMSB, ZMSB, Cprime;
++    E3_uint32 Yprime;
++    E3_uint32 Result=0;
++    
++    Yprime = ((fcode >> 2) & 1) ? ~Y : Y;
++    Cprime = ((fcode >> 2) & 1) ^ (*C & ((fcode >> 3) & 1));
++    XMSB             = (X >> 31) & 1;
++    YMSB             = (Yprime >> 31) & 1;
++    /* mul or div */
++    if ((fcode & 0xa) == 0xa)
++    {
++      PRINTF0 (ctxt, DBG_TPROC, "ALU: tried a multiply or a divide\n");
++      return (0);
++    }
++
++    switch (fcode & 3)
++    {
++      /*ADD */
++    case 0:
++      Result = X + Yprime + Cprime ;
++      if ((fcode & 0x10) == 0)
++          return (Result);
++      
++      ZMSB   = Result >> 31;
++      *V = ((XMSB & YMSB & ~ZMSB) | (~XMSB &~YMSB &  ZMSB));
++      *C = ((fcode >> 2) & 1) ^ ( (XMSB & YMSB) | (~ZMSB & (XMSB | YMSB)));
++      break;
++      
++      /*AND */
++    case 1:
++      Result = X & Yprime ;
++      if ((fcode & 0x10) == 0)
++          return (Result);
++      
++      *V = 0;
++      *C = 0;
++      break;
++      
++      /*OR  */
++    case 2:
++      Result = X | Yprime ;
++      if ((fcode & 0x10) == 0)
++          return (Result);
++      
++      *V = 0;
++      *C = 0;
++      break;
++      
++      /*XOR */
++    case 3:
++      Result = X ^ Yprime ;
++      if ((fcode & 0x10) == 0)
++          return (Result);
++      
++      *V = 0;
++      *C = 0;
++      break;
++    }
++    
++    *Z = (Result == 0) ? 1 : 0;
++    *N = (Result >> 31) & 1;
++
++    return (Result);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan3/tproc_linux.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan3/tproc_linux.c    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan3/tproc_linux.c 2005-06-01 23:12:54.596439560 -0400
+@@ -0,0 +1,215 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "$Id: tproc_linux.c,v 1.19.2.1 2004/10/28 17:08:56 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/os/tproc_linux.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <qsnet/autoconf.h>
++
++#include <asm/mman.h>
++#include <linux/file.h>
++
++#ifdef NO_ABI
++#include <asm/poll.h>
++extern asmlinkage long sys_open(const char *, int, int);
++extern asmlinkage ssize_t sys_write(unsigned int, const char *, size_t);
++extern asmlinkage ssize_t sys_read(unsigned int, char *, size_t);
++extern asmlinkage off_t sys_lseek(unsigned int, off_t, unsigned int);
++extern asmlinkage long sys_poll(struct pollfd *, unsigned int, long);
++extern asmlinkage long sys_kill(int, int); 
++#else
++#     include <linux/syscall.h>
++#endif
++
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elanvp.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanctxt.h>
++#include <elan3/elandebug.h>
++#include <elan3/urom_addrs.h>
++#include <elan3/thread.h>
++#include <elan3/elansyscall.h>
++#include <elan3/threadsyscall.h>
++
++/*
++ * NOTE: system calls from kernel on Linux are different on alpha and i386 
++ *       on alpha they return -errno on failure 
++ *       on i386  they return -1 on failure and set errno 
++ */
++
++static void
++ReturnSyscall (THREAD_TRAP *trap, unsigned long rc, int *skip)
++{
++    if (rc >= (unsigned long) (-130))
++    {
++      trap->pc |= PSR_C_BIT;  /* clear carry to indicate failure */
++
++      trap->Registers[REG_OUTS+(0^WordEndianFlip)] = -rc;
++    } 
++    else 
++    {
++      trap->pc &= ~PSR_C_BIT; /* set carry to indicate success */
++      trap->Registers[REG_OUTS+(0^WordEndianFlip)] = rc;
++    }
++    trap->Registers[REG_OUTS+(1^WordEndianFlip)] = 0;
++    *skip = 1;
++}
++
++static void 
++dump_regs(ELAN3_CTXT *ctxt, THREAD_TRAP *trap)
++{
++    PRINTF (ctxt, DBG_TPROC, "               OUTS %08x %08x %08x %08x\n",
++      trap->Registers[REG_OUTS+(0^WordEndianFlip)], 
++      trap->Registers[REG_OUTS+(1^WordEndianFlip)],
++      trap->Registers[REG_OUTS+(2^WordEndianFlip)], 
++      trap->Registers[REG_OUTS+(3^WordEndianFlip)]);
++    PRINTF (ctxt, DBG_TPROC, "                    %08x %08x %08x %08x\n",
++      trap->Registers[REG_OUTS+(4^WordEndianFlip)], 
++      trap->Registers[REG_OUTS+(5^WordEndianFlip)],
++      trap->Registers[REG_OUTS+(6^WordEndianFlip)], 
++      trap->Registers[REG_OUTS+(7^WordEndianFlip)]);
++}
++
++int
++ThreadSyscall (ELAN3_CTXT *ctxt, THREAD_TRAP *trap, int *skip)
++{
++    int                 code;
++    caddr_t       maddr;
++    struct file  *file;
++    unsigned long rc;
++    int           i;
++    uintptr_t     av[6];
++    uintptr_t     ptr;
++   
++    PRINTF (ctxt, DBG_TPROC, "ThreadSyscall: PC %08x G1 %08x\n", 
++      trap->pc, trap->Registers[REG_GLOBALS+(1^WordEndianFlip)]);
++    dump_regs(ctxt, trap);
++
++    code = trap->Registers[REG_GLOBALS+(1^WordEndianFlip)];
++
++    /* Copy the system call arguments from %o0-%o5 */
++    for (i = 0; i < 6; i++)
++      av[i] = trap->Registers[REG_OUTS+(i^WordEndianFlip)];
++    
++    rc = (unsigned long) -EINVAL;
++
++    switch (code) {
++      case ELAN3_SYS_open:
++          maddr = elan3mmu_mainaddr (ctxt->Elan3mmu, (E3_Addr) av[0]);
++          if (maddr != NULL)
++              rc = sys_open((const char *)maddr, av[1], av[2]);
++          break;
++
++      case ELAN3_SYS_close:
++          rc = sys_close(av[0]);
++          break;
++
++      case ELAN3_SYS_write:
++          maddr = elan3mmu_mainaddr (ctxt->Elan3mmu, (E3_Addr) av[1]);
++          if (maddr != NULL)
++              rc = sys_write(av[0], (const char *)maddr, av[2]);
++          break;
++
++      case ELAN3_SYS_read:
++          maddr = elan3mmu_mainaddr (ctxt->Elan3mmu, (E3_Addr) av[1]);
++          if (maddr != NULL)
++              rc = sys_read(av[0], (char *)maddr, av[2]);
++          break;
++
++      case ELAN3_SYS_poll:
++          maddr = elan3mmu_mainaddr (ctxt->Elan3mmu, (E3_Addr) av[0]);
++          if (maddr != NULL)
++              rc = sys_poll((struct pollfd *)maddr, av[1], av[2]);
++          break;
++      
++      case ELAN3_SYS_lseek:
++          rc = sys_lseek(av[0], av[1], av[2]);
++          break;
++          
++      case ELAN3_SYS_mmap:
++          if ((E3_Addr) av[0] == (E3_Addr) 0)
++              maddr = NULL;
++          else if ((maddr = elan3mmu_mainaddr (ctxt->Elan3mmu, (E3_Addr) av[0])) == NULL)
++              break;
++      
++          file = NULL;
++          /* GNAT 5515: If *not* anonymous memory need to do fget */
++          if ((av[3] & MAP_ANONYMOUS) == 0 && (file = fget (av[4])) == NULL)
++          {
++              rc = -EBADF;
++              break;
++          }
++
++          down_write (&current->mm->mmap_sem);
++          ptr = do_mmap_pgoff (file, (unsigned long) maddr, av[1], av[2], av[3], av[5] >>PAGE_SHIFT);
++          up_write (&current->mm->mmap_sem);
++
++          if (file)
++              fput (file);
++          
++          if (IS_ERR((void *) ptr))
++              rc = PTR_ERR((void *) ptr);
++          else
++              rc = elan3mmu_elanaddr (ctxt->Elan3mmu, (caddr_t)ptr);
++
++          break;
++      
++      case ELAN3_SYS_munmap:
++          maddr = elan3mmu_mainaddr (ctxt->Elan3mmu, (E3_Addr) av[0]);
++
++#ifdef AC
++          if (maddr != NULL)
++              rc = do_munmap(current->mm, (unsigned long) maddr, av[1], 1);
++#else
++          if (maddr != NULL)
++              rc = do_munmap(current->mm, (unsigned long) maddr, av[1]);
++#endif
++          break;
++
++      case ELAN3_SYS_kill:
++          rc = sys_kill(av[0], av[1]);
++          break;
++
++      case ELAN3_SYS_getpid:
++          rc = current->pid;
++          break;
++
++      default:
++          return EINVAL;
++    }
++    ReturnSyscall(trap, rc, skip);
++    return ESUCCESS;
++}
++
++
++int
++ThreadElancall (ELAN3_CTXT *ctxt, THREAD_TRAP *trap, int *skip)
++{
++      int ret = ESUCCESS;
++
++      PRINTF (ctxt, DBG_TPROC, "ThreadElancall: PC %08x\n", trap->pc);
++      dump_regs(ctxt, trap);
++
++      /*
++       * Elan system call 'type' is passed in o0
++       */
++      switch (trap->Registers[REG_OUTS+(0^WordEndianFlip)]) 
++      {
++      default:
++              ret = EINVAL;
++              break;
++      }
++      return ret;
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan3/virtual_process.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan3/virtual_process.c        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan3/virtual_process.c     2005-06-01 23:12:54.597439408 -0400
+@@ -0,0 +1,884 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: virtual_process.c,v 1.68 2004/06/07 13:50:10 mike Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/os/virtual_process.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan/elanmod.h>
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elanvp.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanctxt.h>
++#include <elan3/elandebug.h>
++#include <elan3/urom_addrs.h>
++#include <elan3/thread.h>
++#include <elan3/vmseg.h>
++#include <elan3/elansyscall.h>
++
++static ELAN3_VPSEG *
++InstallSegment (ELAN3_CTXT *ctxt, int process, int entries)
++{
++    ELAN3_VPSEG **prevSeg, *seg;
++    int lastTop = -1;
++    int       top     = process + entries-1;
++
++    ASSERT (krwlock_is_write_locked (&ctxt->VpLock));
++
++    for (prevSeg = &ctxt->VpSegs; (seg = (*prevSeg)) != NULL; prevSeg = &seg->Next)
++    {
++      int thisTop = seg->Process + seg->Entries - 1;
++
++        if (process < seg->Process && (process <= lastTop || top >= seg->Process))
++      {
++          /*
++           * Overlaps with last segment, or this one 
++           */
++          return (NULL);
++      }
++      if (seg->Process > process)
++          break;
++      
++      lastTop = thisTop;
++    }
++    
++    KMEM_ZALLOC (seg, ELAN3_VPSEG *, sizeof (ELAN3_VPSEG), TRUE);
++    
++    if (seg == (ELAN3_VPSEG *) NULL)
++      return (NULL);
++    
++    seg->Process = process;
++    seg->Entries = entries;
++
++
++    PRINTF2 (ctxt, DBG_VP, "InstallSegment: add seg %p before %p\n", seg, *prevSeg);
++
++    seg->Next = *prevSeg;
++    *prevSeg = seg;
++
++    return (seg);
++}
++
++static int 
++RemoveSegment (ELAN3_CTXT *ctxt, ELAN3_VPSEG *seg)
++{
++    ELAN3_VPSEG **prevSeg, *thisSeg;
++
++    ASSERT (krwlock_is_write_locked (&ctxt->VpLock));
++
++    for (prevSeg = &ctxt->VpSegs; (thisSeg = (*prevSeg)) != NULL; prevSeg = &thisSeg->Next)
++    {
++      if (thisSeg == seg)
++          break;
++    }
++
++    if (thisSeg == (ELAN3_VPSEG *) NULL)
++      return (EINVAL);
++
++
++    PRINTF2 (ctxt, DBG_VP, "RemoveSegment: remove seg %p next %p\n", thisSeg, thisSeg->Next);
++
++    *prevSeg = thisSeg->Next;
++    
++    KMEM_FREE ((caddr_t) seg, sizeof (ELAN3_VPSEG));
++
++    return (ESUCCESS);
++}
++
++static ELAN3_VPSEG *
++FindSegment (ELAN3_CTXT *ctxt, int low, int high)
++{
++    ELAN3_VPSEG *seg;
++
++    ASSERT(krwlock_is_locked (&ctxt->VpLock));
++    
++    for (seg = ctxt->VpSegs; seg; seg = seg->Next)
++    {
++      if (seg->Process <= low && (seg->Process + seg->Entries) > high)
++          return (seg);
++    }
++
++    return ((ELAN3_VPSEG *) NULL);
++}
++
++ELAN_LOCATION
++ProcessToLocation (ELAN3_CTXT *ctxt, ELAN3_VPSEG *seg, int process, ELAN_CAPABILITY *cap)
++{
++    ELAN_LOCATION location;
++    int           nnodes,nctxs;
++    int           node,ctx,i;
++
++    ASSERT(krwlock_is_locked (&ctxt->VpLock));
++
++    location.loc_node    = ELAN3_INVALID_NODE;
++    location.loc_context = -1;
++
++    PRINTF3 (ctxt, DBG_VP, "ProcessToLocation: process %d seg %p cap %p\n", process, seg, cap);
++
++    if (seg == NULL)
++      seg = FindSegment (ctxt, process, process);
++
++    if (!seg || (seg->Type != ELAN3_VPSEG_P2P))
++      return (location);
++
++    cap    = &seg->SegCapability;
++    nnodes = ELAN_CAP_NUM_NODES (cap);
++    nctxs  = ELAN_CAP_NUM_CONTEXTS (cap);
++
++    switch (seg->SegCapability.cap_type & ELAN_CAP_TYPE_MASK)
++    {
++    case ELAN_CAP_TYPE_BLOCK:
++    {
++      int entries = ELAN_CAP_ENTRIES(cap);
++
++      for (node = 0, i = 0; node < nnodes && i < entries; node++)
++      {
++          for (ctx = 0; ctx < nctxs && i < entries; ctx++)
++          {
++              if (( seg->SegCapability.cap_type & ELAN_CAP_TYPE_NO_BITMAP) || BT_TEST (seg->SegCapability.cap_bitmap, ctx + (node * nctxs)))
++              {
++                  if (i++ == (process - seg->Process))
++                  { 
++                      location.loc_node    = seg->SegCapability.cap_lownode    + node;
++                      location.loc_context = seg->SegCapability.cap_lowcontext + ctx;
++                      goto found;
++                  }
++              }
++          }
++      }
++      break;
++    }
++    case ELAN_CAP_TYPE_CYCLIC:
++    {
++      int entries = ELAN_CAP_ENTRIES(cap);
++
++      for (ctx = 0, i = 0; ctx < nctxs && i < entries; ctx++)
++      {
++          for (node = 0; node < nnodes && i < entries; node++)
++          {
++              if ((seg->SegCapability.cap_type & ELAN_CAP_TYPE_NO_BITMAP) || BT_TEST (seg->SegCapability.cap_bitmap, node + (ctx * nnodes)))
++              {                                   
++                  if (i++ ==  (process - seg->Process))
++                  { 
++                      location.loc_node    = seg->SegCapability.cap_lownode    + node;
++                      location.loc_context = seg->SegCapability.cap_lowcontext + ctx;
++                      goto found;
++                  }
++              }
++          }
++      }
++      break;  
++    }
++    default:
++      break;
++    }
++       
++ found:
++    
++    PRINTF3 (ctxt, DBG_VP, "ProcessToLocation: process %d -> Node %d Context %d\n", process, location.loc_node,  location.loc_context);
++
++    if (cap != NULL)
++    {
++      bcopy ((caddr_t) &seg->SegCapability, (caddr_t) cap, sizeof (ELAN_CAPABILITY));
++      cap->cap_mycontext = location.loc_context;
++    }
++
++    return (location);
++}
++
++int
++LocationToProcess (ELAN3_CTXT *ctxt, ELAN3_VPSEG *seg, ELAN_LOCATION loc, ELAN_CAPABILITY *cap)
++{
++    int nnodes,nctxs;
++    int node,ctx,i;
++
++    if (seg == NULL)
++      return ELAN3_INVALID_PROCESS;
++
++    if (!seg || (seg->Type != ELAN3_VPSEG_P2P))
++      return ELAN3_INVALID_PROCESS;
++
++    nnodes = cap->cap_highnode - cap->cap_lownode + 1;
++    nctxs  = cap->cap_highcontext - cap->cap_lowcontext + 1;
++
++    switch (cap->cap_type & ELAN_CAP_TYPE_MASK)
++    {
++    case ELAN_CAP_TYPE_BLOCK:
++    {
++      int entries = ELAN_CAP_ENTRIES(cap);
++
++      for (node = 0, i = 0; node < nnodes && i < entries; node++)
++      {
++          for (ctx = 0; ctx < nctxs && i < entries; ctx++)
++          {
++              if ((cap->cap_type & ELAN_CAP_TYPE_NO_BITMAP) || BT_TEST (cap->cap_bitmap, ctx + (node * nctxs)))
++              {
++                  if ((loc.loc_node    == (cap->cap_lownode + node) ) 
++                      && (loc.loc_context == (cap->cap_lowcontext + ctx) ))
++                  {
++                      return (i + seg->Process);
++                  }
++                  i++;
++              }
++          }
++      }
++      break;
++    } 
++    case ELAN_CAP_TYPE_CYCLIC:
++    {
++      int entries = ELAN_CAP_ENTRIES(cap);
++
++      for (ctx = 0, i = 0; ctx < nctxs && i < entries; ctx++)
++      {
++          for (node = 0; node < nnodes && i < entries; node++)
++          {
++              if ((cap->cap_type & ELAN_CAP_TYPE_NO_BITMAP) || BT_TEST (cap->cap_bitmap, node + (ctx * nnodes)))
++              {
++                  if ((loc.loc_node   == (cap->cap_lownode + node) ) 
++                      && (loc.loc_context == (cap->cap_lowcontext + ctx) ))
++                  {
++                      return (i + seg->Process);
++                  }
++                  i++;
++                  
++              }
++          }
++      }
++      break;
++    } 
++    default:
++      break;
++    }
++       
++    return ELAN3_INVALID_PROCESS;
++}
++
++int
++elan3_addvp (ELAN3_CTXT *ctxt, int process, ELAN_CAPABILITY *cap)
++{
++    ELAN3_DEV      *dev = ctxt->Device;
++    ELAN_POSITION    *pos = &ctxt->Position;
++    ELAN3_VPSEG       *seg;
++    int                     i;
++    int                     nodeOff;
++    int                     ctxOff;
++    int                     nnodes;
++    int                     nctxs;
++    E3_uint16         flits[MAX_FLITS];
++    int               nflits;
++    int               entries;
++
++    PRINTF2 (ctxt, DBG_VP, "elan3_addvp: %d -> %s\n", process, CapabilityString (cap));
++
++    entries = ELAN_CAP_ENTRIES(cap);
++    if (entries <= 0 || (process + entries) > ELAN3_MAX_VPS)
++      return (EINVAL);
++
++    /*
++     * Scan the virtual process segment list, to add this entry, and ensure that
++     * the ranges don't overlap.
++     */
++    krwlock_write (&ctxt->VpLock);
++
++    /* check cap. */
++    switch (elan3_validate_cap (ctxt->Device, cap, ELAN_USER_P2P))
++    {
++    case ELAN_CAP_OK:
++      /* nothing */
++      break;
++
++    case ELAN_CAP_RMS:
++      if ( elan_validate_map(cap, cap) != ESUCCESS)
++      {
++          krwlock_done (&ctxt->VpLock);
++          return (EINVAL);
++      }
++      break;
++
++    default:
++      krwlock_done (&ctxt->VpLock);
++      return (EINVAL);
++    }
++
++    if ((seg = InstallSegment (ctxt, process, entries)) == NULL)
++    {
++      PRINTF0 (ctxt, DBG_VP, "elan3_addvp: failed to find a seg\n");
++      krwlock_done (&ctxt->VpLock);
++      return (EINVAL);
++    }
++    
++    seg->Type                        = ELAN3_VPSEG_P2P;
++    seg->SegCapability               = *cap;
++    seg->SegCapability.cap_mycontext = ELAN_CAP_UNINITIALISED;
++
++    PRINTF3 (ctxt, DBG_VP, "elan3_addvp: segment type %x  %d %d\n",
++          seg->SegCapability.cap_type, seg->Process, entries);
++
++
++    nnodes = cap->cap_highnode - cap->cap_lownode + 1;
++    nctxs  = cap->cap_highcontext - cap->cap_lowcontext + 1;
++
++    /* position not determined, so cannot load any routes, the hwtest
++     * process must explicitly set it's own routes */
++    
++    if (!(cap->cap_type & ELAN_CAP_TYPE_HWTEST) && (pos->pos_mode != ELAN_POS_UNKNOWN))
++    {
++      switch (cap->cap_type & ELAN_CAP_TYPE_MASK)
++      {
++      case ELAN_CAP_TYPE_BLOCK:
++          for (nodeOff = 0, i = 0; nodeOff < nnodes && i < entries; nodeOff++)
++          {
++              for (ctxOff = 0; ctxOff < nctxs && i < entries; ctxOff++)
++              {
++                  if ((cap->cap_type & ELAN_CAP_TYPE_NO_BITMAP) || BT_TEST (cap->cap_bitmap, ctxOff + (nodeOff * nctxs)))
++                  {
++                      /* Don't load a route if there's no switch and trying to talk to myself */
++                      if (pos->pos_mode == ELAN_POS_MODE_SWITCHED ||
++                          (pos->pos_mode == ELAN_POS_MODE_LOOPBACK && cap->cap_lownode + nodeOff == pos->pos_nodeid) ||
++                          (pos->pos_mode == ELAN_POS_MODE_BACKTOBACK && cap->cap_lownode + nodeOff != pos->pos_nodeid))
++                      {
++                          PRINTF3 (ctxt, DBG_VP, "elan3_addvp: virtual process %d -> node %d context %d\n",
++                                   seg->Process + i, cap->cap_lownode +nodeOff, cap->cap_lowcontext +ctxOff);
++                          
++                          nflits = GenerateRoute (pos, flits, cap->cap_lownode + nodeOff, cap->cap_lownode + nodeOff,
++                                                  DEFAULT_ROUTE_TIMEOUT, DEFAULT_ROUTE_PRIORITY);
++                          
++
++
++                          LoadRoute (dev, ctxt->RouteTable, seg->Process+i, cap->cap_lowcontext + ctxOff, nflits, flits);  
++                      }
++                      
++                      i++;
++                  }
++              }
++          }
++          break;
++          
++      case ELAN_CAP_TYPE_CYCLIC:
++          for (ctxOff = 0, i = 0; ctxOff < nctxs && i < entries; ctxOff++)
++          {
++              for (nodeOff = 0; nodeOff < nnodes && i < entries; nodeOff++)
++              {
++                  if ((cap->cap_type & ELAN_CAP_TYPE_NO_BITMAP) || BT_TEST (cap->cap_bitmap, nodeOff + (ctxOff * nnodes)))
++                  {
++                      /* Don't load a route if there's no switch and trying to talk to myself */
++                      if (pos->pos_mode == ELAN_POS_MODE_SWITCHED ||
++                          (pos->pos_mode == ELAN_POS_MODE_LOOPBACK && cap->cap_lownode + nodeOff == pos->pos_nodeid) ||
++                          (pos->pos_mode == ELAN_POS_MODE_BACKTOBACK && cap->cap_lownode + nodeOff != pos->pos_nodeid))
++                      {
++                          PRINTF3 (ctxt, DBG_VP, "elan3_addvp: virtual process %d -> node %d context %d\n",
++                                   seg->Process + i, cap->cap_lownode + nodeOff, cap->cap_lowcontext +ctxOff);
++                      
++                          nflits = GenerateRoute (pos, flits, cap->cap_lownode + nodeOff, cap->cap_lownode + nodeOff,
++                                                  DEFAULT_ROUTE_TIMEOUT, DEFAULT_ROUTE_PRIORITY);
++                          
++
++                          LoadRoute (dev, ctxt->RouteTable, seg->Process+i, cap->cap_lowcontext +ctxOff, nflits, flits);  
++                      } 
++                      i++;                
++                  }
++              }
++          }
++          break;      
++      default:
++          break;
++      }
++    }
++  
++    krwlock_done (&ctxt->VpLock);
++
++    return (ESUCCESS);
++}
++
++int
++elan3_removevp (ELAN3_CTXT *ctxt, int process)
++{
++    ELAN3_VPSEG *seg;
++    ELAN3_VPSEG *next;
++    int               i;
++
++    krwlock_write (&ctxt->VpLock);
++
++    PRINTF1 (ctxt, DBG_VP, "elan3_removevp: remove process %d\n", process);
++
++    if (process == ELAN3_INVALID_PROCESS)
++      seg = ctxt->VpSegs;
++    else
++      seg = FindSegment (ctxt, process, process);
++
++    if (seg == (ELAN3_VPSEG *) NULL)
++    {
++      krwlock_done (&ctxt->VpLock);
++      return (EINVAL);
++    }
++    
++    do {
++      PRINTF3 (ctxt, DBG_VP, "elan3_removevp: segment is %p [%x,%x]\n",
++               seg, seg->Process, seg->Process+seg->Entries);
++
++      for (i = 0; i < seg->Entries; i++)
++          ClearRoute (ctxt->Device, ctxt->RouteTable, seg->Process+i);
++
++        /* get Next pointer value before structure is free'd */
++        next = seg->Next;     
++      RemoveSegment (ctxt, seg);
++
++    } while (process == ELAN3_INVALID_PROCESS && (seg = next) != NULL);
++    
++    krwlock_done (&ctxt->VpLock);
++
++    return (ESUCCESS);
++}
++
++int
++elan3_addbcastvp (ELAN3_CTXT *ctxt, int process, int lowProc, int highProc)
++{
++    ELAN_POSITION *pos = &ctxt->Position;
++    ELAN3_VPSEG    *seg;
++    ELAN3_VPSEG    *aseg;
++    int            virtualProcess;
++    E3_uint64    routeValue;
++
++    PRINTF3 (ctxt, DBG_VP, "elan3_addbcastvp: process %d [%d,%d]\n", process, lowProc, highProc);
++
++    if (lowProc > highProc || pos->pos_mode != ELAN_POS_MODE_SWITCHED)
++      return (EINVAL);
++    
++    krwlock_write (&ctxt->VpLock);
++
++    if ((aseg = FindSegment (ctxt, lowProc, highProc)) == NULL || (aseg->Type != ELAN3_VPSEG_P2P))
++    {
++      PRINTF2 (ctxt, DBG_VP, "elan3_addbcastvp: process [%d,%d] does not map to p2p segment\n", lowProc, highProc);
++      krwlock_done (&ctxt->VpLock);
++      return (EINVAL);
++    }
++
++    /* check aseg->SegCapability */    
++    switch (elan3_validate_cap (ctxt->Device, &aseg->SegCapability, ELAN_USER_BROADCAST))
++    {
++    case ELAN_CAP_OK:
++      /* nothing */
++      break;
++      
++    case ELAN_CAP_RMS:
++      if ( elan_validate_map(&ctxt->Capability, &aseg->SegCapability) != ESUCCESS )
++      {
++          krwlock_done (&ctxt->VpLock);
++          return (EINVAL);
++      }
++      break;
++
++    default:
++      krwlock_done (&ctxt->VpLock);
++      return (EINVAL);
++    }
++
++    if ( ProcessToLocation (ctxt, aseg, lowProc,  NULL).loc_context != 
++       ProcessToLocation (ctxt, aseg, highProc, NULL).loc_context)
++    {
++      PRINTF2 (ctxt, DBG_VP, "elan3_addbcastvp: process [%d,%d] does not map to single context\n", lowProc, highProc);
++      krwlock_done (&ctxt->VpLock);
++      return (EINVAL);
++    }
++    
++    if ((seg = InstallSegment (ctxt, process, 1)) == NULL)
++    {
++      krwlock_done (&ctxt->VpLock);
++      return (EINVAL);
++    }
++
++    seg->Type        = ELAN3_VPSEG_BROADCAST;
++    seg->SegLowProc  = lowProc;
++    seg->SegHighProc = highProc;
++
++    PRINTF4 (ctxt, DBG_VP, "elan3_addbcastvp: installed seg %p Type %d LowProc %d HighProc %d\n",
++          seg, seg->Type, seg->SegLowProc, seg->SegHighProc);
++
++    for (virtualProcess = lowProc; virtualProcess <= highProc; virtualProcess++)
++    {
++      if (virtualProcess < 0 || virtualProcess >= ctxt->RouteTable->Size)
++          routeValue = 0;
++      else
++          routeValue = elan3_sdram_readq ( ctxt->Device, ctxt->RouteTable->Table + virtualProcess * NBYTES_PER_SMALL_ROUTE);
++      
++      if (! (routeValue & ROUTE_VALID))
++      {
++          PRINTF2 (ctxt, DBG_VP, "loadvp[%x]: broadcast %x not valid\n", 
++                   ctxt->Capability.cap_mycontext, virtualProcess);
++          break;
++      }
++    }
++          
++    if (virtualProcess > highProc)                    /* All vps now present */
++    {                                         /* so load up broadcast route */
++      E3_uint16     flits[MAX_FLITS];
++      ELAN_LOCATION low    = ProcessToLocation (ctxt, aseg, lowProc,   NULL);
++      ELAN_LOCATION high   = ProcessToLocation (ctxt, aseg, highProc,  NULL);
++      int           nflits = GenerateRoute (pos, flits, low.loc_node, high.loc_node, DEFAULT_ROUTE_TIMEOUT, DEFAULT_ROUTE_PRIORITY);
++      
++      PRINTF6 (ctxt, DBG_VP, "loadvp[%x]: broadcast %d -> %x.%x [%x.%x]\n", ctxt->Capability.cap_mycontext,
++               seg->Process, low.loc_node, high.loc_node, 
++               low.loc_context, high.loc_context);
++      
++      LoadRoute ( ctxt->Device, ctxt->RouteTable, seg->Process, low.loc_context, nflits, flits);
++    }
++
++    krwlock_done (&ctxt->VpLock);
++
++    return (ESUCCESS);
++}
++
++int
++elan3_process (ELAN3_CTXT *ctxt)
++{
++    int           res = ELAN3_INVALID_PROCESS;
++    ELAN3_VPSEG   *seg;
++    ELAN_LOCATION loc;
++
++    krwlock_write (&ctxt->VpLock);
++
++    loc.loc_node    = ctxt->Position.pos_nodeid;
++    loc.loc_context = ctxt->Capability.cap_mycontext;
++
++    for (seg = ctxt->VpSegs ; seg; seg = seg->Next)
++    {
++      if (seg->Type == ELAN3_VPSEG_P2P &&
++          seg->SegCapability.cap_lowcontext  <= ctxt->Capability.cap_mycontext &&
++          seg->SegCapability.cap_highcontext >= ctxt->Capability.cap_mycontext &&
++          seg->SegCapability.cap_lownode     <= ctxt->Position.pos_nodeid &&
++          seg->SegCapability.cap_highnode    >= ctxt->Position.pos_nodeid)
++      {
++          if ((res=LocationToProcess (ctxt,seg,loc,&ctxt->Capability)) != ELAN3_INVALID_PROCESS)
++          {
++               krwlock_done (&ctxt->VpLock);
++               return res;
++          }
++      }
++    }
++
++    krwlock_done (&ctxt->VpLock);
++
++    return (res);
++}
++
++int
++elan3_check_route (ELAN3_CTXT *ctxt, int process, E3_uint16 *flits, E3_uint32 *routeError)
++{
++    PRINTF5 (ctxt, DBG_VP, "elan3_check_route: vp=%d flits=%04x %04x %04x %04x\n",
++           process, flits[0], flits[1], flits[2], flits[3]);
++    PRINTF4 (ctxt, DBG_VP, "                            %04x %04x %04x %04x\n",
++           flits[4], flits[5], flits[6], flits[7]);
++
++    krwlock_read (&ctxt->VpLock);
++    *routeError=elan3_route_check(ctxt,flits,ProcessToLocation (ctxt, NULL, process, NULL).loc_node);
++    krwlock_done (&ctxt->VpLock);
++
++    return (ESUCCESS); /* the call is a success tho the errorcode may be set */
++}
++
++int
++elan3_load_route (ELAN3_CTXT *ctxt, int process, E3_uint16 *flits)
++{
++    ELAN3_VPSEG *seg;
++    int               res = 0;
++    int               nflits;
++    int         err;
++
++    PRINTF5 (ctxt, DBG_VP, "elan3_load_route: vp=%d flits=%04x %04x %04x %04x\n",
++           process, flits[0], flits[1], flits[2], flits[3]);
++    PRINTF4 (ctxt, DBG_VP, "                            %04x %04x %04x %04x\n",
++           flits[4], flits[5], flits[6], flits[7]);
++
++    krwlock_write (&ctxt->VpLock);
++
++    /* check the route is valid */
++    if (!(ctxt->Capability.cap_type & ELAN_CAP_TYPE_HWTEST))
++    {
++      /* must have already attached to define my context number */
++      if (ctxt->Capability.cap_mycontext == ELAN_CAP_UNINITIALISED)
++      {
++          krwlock_done (&ctxt->VpLock);
++          return (EINVAL);
++      }
++
++      if ((err=elan3_route_check(ctxt,flits,ProcessToLocation (ctxt, NULL, process, NULL).loc_node)) != ELAN3_ROUTE_SUCCESS)
++      {
++          krwlock_done (&ctxt->VpLock);
++          return (EINVAL);
++      }
++    }
++
++    if ((seg = FindSegment (ctxt, process, process)) == NULL || seg->Type != ELAN3_VPSEG_P2P)
++    {
++      krwlock_done (&ctxt->VpLock);
++      return (EINVAL);
++    }
++
++    /* Calculate number of flits in this route */
++    for (nflits = 0; nflits < MAX_FLITS && flits[nflits]; nflits++)
++      ;
++    
++    res = LoadRoute (ctxt->Device, ctxt->RouteTable, process, ProcessToLocation (ctxt, seg, process, NULL).loc_context, nflits, flits);
++
++    krwlock_done (&ctxt->VpLock);
++
++    return (res);
++}
++
++int
++elan3_get_route (ELAN3_CTXT *ctxt, int process, E3_uint16 *flits)
++{
++    ELAN3_VPSEG *seg;
++    int               res = 0;
++
++    PRINTF1 (ctxt, DBG_VP, "elan3_get_route: vp=%d \n",  process);
++
++    krwlock_write (&ctxt->VpLock);
++
++    if (ctxt->RouteTable == NULL)  /* is there a route table */
++    {
++      krwlock_done (&ctxt->VpLock);
++      return (EINVAL);
++    }
++
++    if ((seg = FindSegment (ctxt, process, process)) != NULL && seg->Type != ELAN3_VPSEG_P2P)
++    {
++      krwlock_done (&ctxt->VpLock);
++      return (EINVAL);
++    }
++    
++    if (seg == NULL)
++    {
++      krwlock_done (&ctxt->VpLock);
++      return (EINVAL);
++    }
++    
++    res = GetRoute (ctxt->Device, ctxt->RouteTable, process, flits);
++    
++    krwlock_done (&ctxt->VpLock);
++
++    return (res);
++}
++
++int
++elan3_reset_route (ELAN3_CTXT *ctxt, int process)
++{
++    E3_uint16     flits[MAX_FLITS];
++
++    PRINTF1 (ctxt, DBG_VP, "elan3_reset_route: vp=%d \n",  process);
++ 
++    GenerateRoute (&ctxt->Position, flits, process, process, DEFAULT_ROUTE_TIMEOUT, DEFAULT_ROUTE_PRIORITY);
++    
++    return elan3_load_route(ctxt,process,flits);
++}
++
++int
++ResolveVirtualProcess (ELAN3_CTXT *ctxt, int process)
++{
++    E3_uint16   flits[MAX_FLITS];
++    ELAN3_DEV     *dev = ctxt->Device;
++    int                 res = ESUCCESS;
++    ELAN3_VPSEG   *seg;
++    ELAN3_VPSEG   *aseg;
++    E3_uint64   routeValue;
++
++    krwlock_read (&ctxt->VpLock);
++
++    PRINTF1 (ctxt, DBG_VP, "ResolveVirtualProcess: vp=%d \n",  process);
++
++    if (ctxt->RouteTable == NULL || process < 0 || process >= ctxt->RouteTable->Size)
++    {
++      krwlock_done (&ctxt->VpLock);
++      return (EINVAL);
++    }
++
++    if (! (seg = FindSegment (ctxt, process, process)))
++    {
++      PRINTF1 (ctxt, DBG_VP, "ResolveVirtualProcess: cannot find segment for virtual process %d\n", process);
++      krwlock_done (&ctxt->VpLock);
++      return (EINVAL);
++    }
++    
++    /* check cap. */
++    switch (elan3_validate_cap (ctxt->Device, &seg->SegCapability, ((seg->Type == ELAN3_VPSEG_P2P) ? ELAN_USER_P2P : ELAN_USER_BROADCAST)))
++    {
++    case ELAN_CAP_OK:
++      /* nothing */
++      break;
++
++    case ELAN_CAP_RMS:
++      if ( elan_validate_map(&ctxt->Capability, &seg->SegCapability) != ESUCCESS)
++      {
++          krwlock_done (&ctxt->VpLock);
++          return (EINVAL);
++      }
++      break;
++
++    default:
++      krwlock_done (&ctxt->VpLock);
++      return (EINVAL);
++    }
++
++    BumpUserStat (ctxt, LoadVirtualProcess);
++
++    routeValue = elan3_sdram_readq (dev, ctxt->RouteTable->Table + process * NBYTES_PER_SMALL_ROUTE);
++    if (routeValue & ROUTE_VALID)                             /* Virtual process already */
++    {                                                         /* loaded */
++      krwlock_done (&ctxt->VpLock);
++      return (ESUCCESS);                      
++    }
++    
++    switch (seg->Type)
++    {
++    case ELAN3_VPSEG_P2P:
++      switch (seg->SegCapability.cap_type & ELAN_CAP_TYPE_MASK)
++      {
++      case ELAN_CAP_TYPE_BLOCK:
++      case ELAN_CAP_TYPE_CYCLIC:
++          if ((res = elan_validate_map (&ctxt->Capability,&seg->SegCapability)) == ESUCCESS &&
++              (res = GetRoute(dev, ctxt->RouteTable ,process,  flits)) == ESUCCESS)
++          {
++              if (elan3_route_check(ctxt, flits, ProcessToLocation (ctxt, seg, process, NULL).loc_node))
++                  res = EINVAL;
++              else
++                  ValidateRoute(dev, ctxt->RouteTable, process);
++          }
++          break;
++      default:
++          res = EINVAL;
++          break;
++      }
++      break;
++
++    case ELAN3_VPSEG_BROADCAST:
++      /* Find the segment that this broadcast range spans. */
++      aseg = FindSegment (ctxt, seg->SegLowProc, seg->SegHighProc);
++      
++      if (aseg == NULL || (aseg->Type != ELAN3_VPSEG_P2P) || !(aseg->SegCapability.cap_type & ELAN_CAP_TYPE_BROADCASTABLE))
++      {
++          PRINTF2 (ctxt, DBG_VP, "resolveVirtualProcess: %d -> EINVAL (%s)\n", process, 
++                   (aseg == NULL ? "no segment" : ((seg->Type != ELAN3_VPSEG_P2P) ? "not point to point" :
++                                                   "not broadcastable")));
++          res = EINVAL;
++          break;
++      }
++      
++      switch (aseg->SegCapability.cap_type & ELAN_CAP_TYPE_MASK)
++      {
++      case ELAN_CAP_TYPE_BLOCK:
++      case ELAN_CAP_TYPE_CYCLIC:
++      {
++          ELAN_LOCATION lowNode  = ProcessToLocation (ctxt,aseg,seg->SegLowProc  , NULL);
++          ELAN_LOCATION highNode = ProcessToLocation (ctxt,aseg,seg->SegHighProc , NULL);
++
++
++          if ((res = elan_validate_map (&ctxt->Capability,&aseg->SegCapability)) == ESUCCESS &&
++              (res=GetRoute(dev, ctxt->RouteTable ,process,  flits)) == ESUCCESS)
++          {
++              if (elan3_route_broadcast_check(ctxt,flits, lowNode.loc_node , highNode.loc_node ) != ELAN3_ROUTE_SUCCESS )
++                  res = EINVAL;
++              else
++                  ValidateRoute(dev, ctxt->RouteTable, process);
++          }
++          break;
++      }
++
++      default:
++          res = EINVAL;
++          break;
++      }
++    default:
++      res  = EINVAL;
++      break;
++    }
++
++    krwlock_done (&ctxt->VpLock);
++    return (res);
++}       
++
++void
++UnloadVirtualProcess (ELAN3_CTXT *ctxt, ELAN_CAPABILITY *cap)
++{
++    ELAN3_DEV        *dev  = ctxt->Device;
++    ELAN3_VPSEG      *seg;
++    ELAN_CAPABILITY *scap;
++    int              i;
++
++    for (seg = ctxt->VpSegs; seg; seg = seg->Next)
++    {
++      switch (seg->Type)
++      {
++      case ELAN3_VPSEG_P2P:
++          scap = &seg->SegCapability;
++          
++          if (cap == NULL || ELAN_CAP_MATCH (scap, cap))
++          {
++              PRINTF2 (ctxt, DBG_VP, "unloadvp: segment [%x.%x]\n", 
++                       seg->Process, seg->Process + seg->Entries-1);
++              
++              for (i = 0; i < seg->Entries; i++)
++                  InvalidateRoute (dev, ctxt->RouteTable, seg->Process+i);
++          }
++          break;
++
++      case ELAN3_VPSEG_BROADCAST:
++          for (i = 0; i < seg->Entries; i++)
++          {
++              ELAN3_VPSEG *aseg = FindSegment (ctxt, seg->SegLowProc, seg->SegHighProc);
++              
++              if (aseg != NULL && ELAN_CAP_MATCH(&aseg->SegCapability, cap))
++              {
++                  PRINTF1 (ctxt, DBG_VP, "unloadvp: broadcast vp %d\n", seg->Process);
++              
++                  InvalidateRoute (dev, ctxt->RouteTable, seg->Process+i);
++              }
++          }
++      }
++    }
++}
++
++caddr_t
++CapabilityString (ELAN_CAPABILITY *cap)
++{
++#define CAPSTR_LEN    200
++#define NCAPSTRS      4
++    static char       space[CAPSTR_LEN*NCAPSTRS];
++    static int        bufnum;
++    static spinlock_t lock;
++    static int              lockinitialised;
++    int                     num;
++    unsigned long     flags;
++
++    if (! lockinitialised)
++    {
++      spin_lock_init (&lock);
++      lockinitialised = 1;
++    }
++
++    spin_lock_irqsave (&lock, flags);
++    
++    if ((num = ++bufnum) == NCAPSTRS)
++      num = bufnum = 0;
++    spin_unlock_irqrestore (&lock, flags);
++
++    sprintf (space + (num * CAPSTR_LEN), "%4x %4x %4x %4x %4x %4x %4x [%x.%x.%x.%x]", cap->cap_type,
++           cap->cap_lownode, cap->cap_highnode, 
++           cap->cap_lowcontext, cap->cap_mycontext, cap->cap_highcontext,  ELAN_CAP_ENTRIES(cap),
++           cap->cap_userkey.key_values[0],  cap->cap_userkey.key_values[1],
++           cap->cap_userkey.key_values[2],  cap->cap_userkey.key_values[3]);
++
++    return (space + (num * CAPSTR_LEN));
++}
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan4/debug.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan4/debug.c  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan4/debug.c       2005-06-01 23:12:54.597439408 -0400
+@@ -0,0 +1,94 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: debug.c,v 1.16 2004/07/07 11:22:33 addy Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/debug.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan4/debug.h>
++#include <elan4/device.h>
++
++unsigned       elan4_debug           = 0;
++unsigned       elan4_debug_toconsole = 0;
++unsigned       elan4_debug_tobuffer  = DBG_ALL;
++
++unsigned       elan4_debug_display_ctxt;
++unsigned       elan4_debug_ignore_ctxt;
++unsigned       elan4_debug_ignore_type;
++
++void
++elan4_debug_init()
++{
++    if ((elan4_debug & elan4_debug_tobuffer) != 0)
++      qsnet_debug_alloc();
++}
++
++void
++elan4_debug_fini()
++{
++}
++
++void
++elan4_debugf (void *type, int mode, char *fmt,...)
++{
++    char    prefix[128];
++    int     where = 0;
++    va_list ap;
++
++    if ((mode & elan4_debug_tobuffer) != 0 || type == DBG_BUFFER)
++      where |= QSNET_DEBUG_BUFFER;
++    if ((mode & elan4_debug_toconsole) != 0 || type == DBG_CONSOLE)
++      where |= QSNET_DEBUG_CONSOLE;
++
++    if (where == 0)
++      return;
++    
++    if ((unsigned long) type > DBG_NTYPES)
++    {
++      ELAN4_CTXT *ctxt = (ELAN4_CTXT *) type;
++
++        if (elan4_debug_display_ctxt && ctxt->ctxt_num != elan4_debug_display_ctxt)
++            return;
++        if (elan4_debug_ignore_ctxt  && ctxt->ctxt_num == elan4_debug_ignore_ctxt)
++            return;
++
++      sprintf (prefix, "[%08ld.%04d] elan4 (%03x) ", lbolt,  current->pid, ctxt->ctxt_num);
++    }
++    else if ((unsigned long) type == (int) DBG_CONSOLE)
++      prefix[0] = '\0';
++    else
++    {
++      char *what;
++
++      if (elan4_debug_ignore_type & (1 << ((unsigned long) type)))
++          return;
++
++      switch ((unsigned long) type)
++      {
++      case (int) DBG_DEVICE: what = "dev"; break;
++      case (int) DBG_USER:   what = "usr"; break;
++      default:               what = NULL; break;
++      }
++          
++      if (what)
++          sprintf (prefix, "[%08ld.%04d] elan4 [%s] ", lbolt, current->pid, what);
++      else
++          sprintf (prefix, "[%08ld.%04d] elan4 [%3d] ", lbolt, current->pid, (int)(long)type);
++    }
++
++    va_start(ap,fmt);
++    qsnet_vdebugf (where, prefix, fmt, ap);
++    va_end (ap);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan4/device.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan4/device.c 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan4/device.c      2005-06-01 23:12:54.602438648 -0400
+@@ -0,0 +1,2805 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: device.c,v 1.87.6.3 2005/01/18 14:25:35 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/device.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <qsnet/kthread.h>
++
++#include <elan4/sdram.h>
++#include <elan4/debug.h>
++#include <elan4/device.h>
++#include <elan4/commands.h>
++#include <elan4/trtype.h>
++#include <elan4/neterr.h>
++
++#include <elan4/i2c.h>
++#include <elan3/vpd.h>
++
++/* allow this code to compile against an Eagle elanmod */
++#ifdef __ELANMOD_DEVICE_H
++#define ELAN_DEV_OPS          ELANMOD_DEV_OPS
++#define ELAN_DEV_OPS_VERSION  ELANMOD_DEV_OPS_VERSION
++#define elan_dev_register     elanmod_dev_register
++#define elan_dev_deregister   elanmod_dev_deregister
++#endif
++
++/* XXXX configurational defines */
++
++#if defined (CONFIG_MPSAS)
++#define HASH_0_SIZE_VAL                       (12 + 6)
++#define HASH_1_SIZE_VAL                       (2 + 6)
++#define CTXT_TABLE_SHIFT              8
++#define LN2_MAX_CQS                   8               /* 256 */
++#else
++#define HASH_0_SIZE_VAL                       (13 + 6)
++#define HASH_1_SIZE_VAL                       (2 + 6)
++#define CTXT_TABLE_SHIFT              12
++#define LN2_MAX_CQS                   10              /* 1024 */
++#endif
++
++unsigned int elan4_hash_0_size_val       = HASH_0_SIZE_VAL;
++unsigned int elan4_hash_1_size_val       = HASH_1_SIZE_VAL;
++unsigned int elan4_ctxt_table_shift      = CTXT_TABLE_SHIFT;
++unsigned int elan4_ln2_max_cqs           = LN2_MAX_CQS;
++unsigned int elan4_dmaq_highpri_size     = 2;                 /* 8192 entries */
++unsigned int elan4_threadq_highpri_size  = 1;                 /* 1024 entries */
++unsigned int elan4_dmaq_lowpri_size      = 2;                 /* 8192 entries */
++unsigned int elan4_threadq_lowpri_size   = 1;                 /* 1024 entries */
++unsigned int elan4_interruptq_size       = 0;                 /* 1024 entries */
++unsigned int elan4_mainint_punt_loops    = 1;
++unsigned int elan4_mainint_resched_ticks = 0;
++
++static int 
++elan4_op_get_position (void *arg, ELAN_POSITION *ptr)
++{
++    ELAN4_DEV     *dev = (ELAN4_DEV *)arg;
++    ELAN_POSITION  pos;
++
++    elan4_get_position (dev, &pos);
++
++    return copyout (&pos, ptr, sizeof (ELAN_POSITION));
++}
++
++static int 
++elan4_op_set_position (void *arg, unsigned short nodeid, unsigned short numnodes)
++{
++    /* XXXXX 
++
++       ELAN4_DEV *dev = (ELAN4_DEV *) arg;
++
++       compute_position (&pos, nodeid, numnode, num_down_links_value);
++
++       return elan4_set_position (dev, pos);
++    */
++    return EINVAL;
++}
++
++ELAN_DEV_OPS elan4_dev_ops = 
++{
++    elan4_op_get_position,
++    elan4_op_set_position,
++
++    ELAN_DEV_OPS_VERSION
++};
++
++static E4_uint32
++elan4_read_filter (ELAN4_DEV *dev, unsigned networkctx)
++{
++    return (elan4_sdram_readl (dev, dev->dev_ctxtable + (networkctx * sizeof (E4_ContextControlBlock)) + 
++                             offsetof (E4_ContextControlBlock, Filter)));
++}
++
++static void
++elan4_write_filter (ELAN4_DEV *dev, unsigned networkctx, E4_uint32 value)
++{
++    elan4_sdram_writel (dev, (dev->dev_ctxtable + (networkctx * sizeof (E4_ContextControlBlock)) +
++                      offsetof (E4_ContextControlBlock, Filter)), value);
++    pioflush_sdram(dev);
++}
++
++void
++elan4_set_schedstatus (ELAN4_DEV *dev, E4_uint32 intreg)
++{
++    E4_uint32 setbits  = 0;
++    E4_uint32 intmask  = 0;
++    E4_uint32 haltmask;
++    E4_uint32 next_sched;
++    E4_uint32 next_intmask;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->dev_intmask_lock, flags);
++
++    haltmask = (dev->dev_haltop_mask | dev->dev_haltop_active);
++
++    if ((haltmask & INT_DProcHalted) || dev->dev_halt_all_count || dev->dev_halt_dproc_count)
++      setbits |= SCH_DProcHalt;
++    
++    if ((haltmask & INT_TProcHalted) || dev->dev_halt_all_count || dev->dev_halt_tproc_count)
++      setbits |= SCH_TProcHalt;
++
++    if ((haltmask & INT_CProcHalted) || dev->dev_halt_all_count || dev->dev_halt_cproc_count)
++      setbits |= SCH_CProcHalt;
++
++    if ((haltmask & INT_DiscardingLowPri) || dev->dev_discard_all_count || dev->dev_discard_lowpri_count)
++      setbits |= SCH_DiscardLowPriInput;
++    
++    if ((haltmask & INT_DiscardingHighPri) || dev->dev_discard_all_count || dev->dev_discard_highpri_count)
++      setbits |= SCH_DiscardHighPriInput;
++    
++    if (dev->dev_halt_lowpri_count)
++      setbits |= SCH_StopLowPriQueues;
++    
++    if (haltmask & INT_DProcHalted) intmask |= INT_DProcHalted;
++    if (haltmask & INT_TProcHalted) intmask |= INT_TProcHalted;
++    if (haltmask & INT_CProcHalted) intmask |= INT_CProcHalted;
++    if (haltmask & INT_DiscardingLowPri) intmask |= INT_DiscardingLowPri;
++    if (haltmask & INT_DiscardingHighPri) intmask |= INT_DiscardingHighPri;
++
++    next_intmask = (dev->dev_intmask     & ~(INT_Halted | INT_Discarding)) | (intmask & ~intreg);
++    next_sched   = (dev->dev_schedstatus & ~(SCH_Halt | SCH_Discard))      | setbits;
++
++    PRINTF5 (DBG_DEVICE, DBG_REGISTER, "elan4_set_schedstatus: haltmask=%x setbits=%x intmask=%x next_sched=%x next_intmask=%x\n",
++           haltmask, setbits, intmask, next_sched, next_intmask);
++
++    CHANGE_INT_MASK (dev, next_intmask);
++    CHANGE_SCHED_STATUS (dev, next_sched);
++
++    spin_unlock_irqrestore (&dev->dev_intmask_lock, flags);
++}
++
++void
++elan4_queue_haltop (ELAN4_DEV *dev, ELAN4_HALTOP *op)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->dev_haltop_lock, flags);
++
++    /* add to the end of the halt operations list */
++    list_add_tail (&op->op_link, &dev->dev_haltop_list);
++
++    if ((dev->dev_haltop_mask & op->op_mask) != op->op_mask)
++    {
++      dev->dev_haltop_mask |= op->op_mask;
++      
++      elan4_set_schedstatus (dev, 0);
++    }
++    
++    spin_unlock_irqrestore (&dev->dev_haltop_lock, flags);
++}
++
++void
++elan4_queue_intop (ELAN4_DEV *dev, ELAN4_CQ *cq, ELAN4_INTOP *op)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->dev_intop_lock, flags);
++
++    op->op_cookie = INTOP_ONESHOT | ((dev->dev_intop_cookie++) & INTOP_VALUE_MASK);
++
++    list_add_tail (&op->op_link, &dev->dev_intop_list);
++
++    writeq ((op->op_cookie << E4_MAIN_INT_SHIFT) | INTERRUPT_CMD, cq->cq_mapping);
++
++    spin_unlock_irqrestore (&dev->dev_intop_lock, flags);
++}
++
++void
++elan4_register_intop (ELAN4_DEV *dev, ELAN4_INTOP *op)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->dev_intop_lock, flags);
++
++    op->op_cookie = INTOP_PERSISTENT | ((dev->dev_intop_cookie++) & INTOP_VALUE_MASK);
++
++    list_add_tail (&op->op_link, &dev->dev_intop_list);
++
++    spin_unlock_irqrestore (&dev->dev_intop_lock, flags);
++}
++
++void
++elan4_deregister_intop (ELAN4_DEV *dev, ELAN4_INTOP *op)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->dev_intop_lock, flags);
++    list_del (&op->op_link);
++    spin_unlock_irqrestore (&dev->dev_intop_lock, flags);
++}
++
++static __inline__ void
++__issue_dma_flushop_cmd (ELAN4_CQ *cq)
++{
++    writeq (DMA_ShMemWrite | RUN_DMA_CMD, cq->cq_mapping);
++    writeq (0 /* cookie */,               cq->cq_mapping);
++    writeq (0 /* vproc */,                cq->cq_mapping);
++    writeq (0 /* srcAddr */,              cq->cq_mapping);
++    writeq (0 /* dstAddr */,              cq->cq_mapping);
++    writeq (0 /* srcEvent */,             cq->cq_mapping);
++    writeq (0 /* dstEvent */,             cq->cq_mapping);
++    writeq (SET_EVENT_CMD,                cq->cq_mapping);
++}
++
++static void
++handle_dma_flushops_intop (ELAN4_DEV *dev, void *arg)
++{
++    unsigned int  hipri        = ((unsigned long) arg & 1);
++    E4_uint64     status       = dev->dev_dma_flushop[hipri].status;
++    ELAN4_CQ     *cq           = dev->dev_dma_flushop[hipri].cq;
++    sdramaddr_t   cqdesc       = dev->dev_cqaddr + (elan4_cq2num(cq) * sizeof (E4_CommandQueueDesc));
++    E4_uint64     queuePtrs    = elan4_sdram_readq (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_QueuePtrs));
++    E4_uint32     completedPtr = CQ_CompletedPtr(queuePtrs);
++    E4_uint32     size         = CQ_Size ((queuePtrs >> CQ_SizeShift) & CQ_SizeMask);
++    unsigned long flags;
++
++    /*
++     * Since we're called from a main interrupt which was issued through the approriate
++     * flushcq the command queue descriptor for dma flushing can no longer be in the 
++     * insert cache, nor can it be in the extractor (as it's trapped), hence it is
++     * safe to modify the completed pointer
++     */
++
++    spin_lock_irqsave (&dev->dev_haltop_lock, flags);
++
++    ASSERT (status != 0);
++
++    /* skip over either the DMA/SETEVENT or just the SETEVENT depending on the trap type */
++    if (CPROC_TrapType (status) == CommandProcDmaQueueOverflow)
++      completedPtr = (completedPtr & ~(size-1)) | ((completedPtr + 64) & (size - 1));
++    else
++      completedPtr = (completedPtr & ~(size-1)) | ((completedPtr + 8) & (size - 1));
++    
++    elan4_sdram_writel (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_QueuePtrs) + 4,
++                      ((queuePtrs >> 32) & ~CQ_PtrOffsetMask) | (completedPtr & CQ_PtrOffsetMask));
++    
++    elan4_restartcq (dev, dev->dev_dma_flushop[hipri].cq);
++
++    if (! list_empty (&dev->dev_dma_flushop[hipri].list))
++      __issue_dma_flushop_cmd (dev->dev_dma_flushop[hipri].cq);
++
++    dev->dev_dma_flushop[hipri].status = 0;
++    
++    spin_unlock_irqrestore (&dev->dev_haltop_lock, flags);
++
++}
++
++static void
++handle_dma_flushops (ELAN4_DEV *dev, E4_uint64 status, int cqnum)
++{
++    unsigned int       hipri  = (cqnum == elan4_cq2num(dev->dev_dma_flushop[1].cq) ? 1 : 0);
++    ELAN4_CQ          *cq     = dev->dev_dma_flushop[hipri].cq;
++    ELAN4_CQ          *flushq = dev->dev_flush_cq[elan4_cq2num(cq) & (COMMAND_INSERTER_CACHE_ENTRIES-1)];
++    struct list_head  *ops;
++    unsigned long      flags;
++    int                      qfull,count;
++    E4_uint64        queuePtrs;
++    LIST_HEAD(list);
++    
++    spin_lock_irqsave (&dev->dev_haltop_lock, flags);
++    
++    ASSERT (cqnum == elan4_cq2num (dev->dev_dma_flushop[hipri].cq));
++    ASSERT (! list_empty (&dev->dev_dma_flushop[hipri].list));
++    ASSERT (dev->dev_dma_flushop[hipri].status == 0);
++    
++    /* remove the whole list */
++    ops = dev->dev_dma_flushop[hipri].list.next;
++
++    list_del_init (&dev->dev_dma_flushop[hipri].list);
++    
++    /* and add it to our local list */
++    list_add_tail (&list, ops);
++    
++    /* now determine whether the queue was full - since it cannot be empty 
++     * then if the front and back pointers are the same then it is full */
++    queuePtrs = hipri ? read_reg64 (dev, DProcHighPriPtrs) : read_reg64 (dev, DProcLowPriPtrs);
++    qfull     = (E4_QueueFrontPointer (queuePtrs) == E4_QueueBackPointer (queuePtrs));
++    
++    if (CPROC_TrapType(status) == CommandProcDmaQueueOverflow && !qfull)
++      printk (" ******* queue overflow trap - but queue not full\n");
++
++    if (qfull && CPROC_TrapType(status) != CommandProcDmaQueueOverflow)
++      printk (" ****** queue full - but not overflow trap : %llx %llx %x\n", 
++              read_reg64 (dev, DProcLowPriPtrs), read_reg64 (dev, DProcHighPriPtrs), CPROC_TrapType(status));
++
++    /* Store the status register, this also indicates that the intop is pending */
++    dev->dev_dma_flushop[hipri].status = status;
++
++    spin_unlock_irqrestore (&dev->dev_haltop_lock, flags);
++
++    /* Issue a main interrupt command to the approriate flush command queue,
++     * which will then safely update the completed pointer to skip over the
++     * command which has trapped, also prevent any new commands to be issued
++     * to the command queue.
++     */
++    dev->dev_dma_flushop[hipri].intop.op_function = handle_dma_flushops_intop;
++    dev->dev_dma_flushop[hipri].intop.op_arg      = (void *) (unsigned long) hipri;
++
++    elan4_queue_intop (dev, flushq, &dev->dev_dma_flushop[hipri].intop);
++    
++    /* now execute all operations */
++    for (count = 0; ! list_empty (&list); count++)
++    {
++      ELAN4_DMA_FLUSHOP *op = list_entry (list.next, ELAN4_DMA_FLUSHOP, op_link);
++      
++      list_del (&op->op_link);
++      
++      (*op->op_function) (dev, op->op_arg, qfull);
++    }
++
++    /* finally release the "reasons" for halting */
++    spin_lock_irqsave (&dev->dev_haltop_lock, flags);
++    if ((dev->dev_halt_dproc_count -= count) == 0)
++      elan4_set_schedstatus (dev, 0);
++    spin_unlock_irqrestore (&dev->dev_haltop_lock, flags);
++      
++    return;
++}
++
++void
++elan4_queue_dma_flushop (ELAN4_DEV *dev, ELAN4_DMA_FLUSHOP *op, int hipri)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->dev_haltop_lock, flags);
++
++    if (dev->dev_halt_dproc_count++ == 0)                     /* ensure that the DMA processor cannot */
++      elan4_set_schedstatus (dev, 0);                         /* execute the DMA we issue. */
++
++    if (list_empty (&dev->dev_dma_flushop[hipri].list) && dev->dev_dma_flushop[hipri].status == 0)
++      __issue_dma_flushop_cmd (dev->dev_dma_flushop[hipri].cq);
++      
++    list_add_tail (&op->op_link, &dev->dev_dma_flushop[hipri].list);
++
++    spin_unlock_irqrestore (&dev->dev_haltop_lock, flags);
++}
++
++static void
++enable_elan_errors (void *arg)
++{
++    ELAN4_DEV *dev = (ELAN4_DEV *) arg;
++
++    ENABLE_INT_MASK (dev, INT_ErrorInterrupts);
++}
++
++#define ERROR_DISABLE_PERIOD  (hz/2)
++#define ERROR_SAMPLE_PERIOD   (hz/10)
++#define ERROR_LIMIT           (100)
++
++static __inline__ void
++check_error_rate (ELAN4_DEV *dev)
++{
++    if (dev->dev_error_time == (lbolt/ERROR_SAMPLE_PERIOD))
++    {
++        if (++dev->dev_errors_per_period >= ERROR_LIMIT && (dev->dev_intmask & INT_ErrorInterrupts))
++      {
++          DISABLE_INT_MASK (dev, INT_ErrorInterrupts);
++          
++          schedule_timer_fn (&dev->dev_error_timeoutid, enable_elan_errors, (void *) dev, ERROR_DISABLE_PERIOD);
++      }
++    }
++    else
++    {
++      dev->dev_error_time        = (lbolt/ERROR_SAMPLE_PERIOD);
++      dev->dev_errors_per_period = 0;
++    }
++}
++
++static __inline__ int
++handle_mainints (ELAN4_DEV *dev, int nticks, int nintr)
++{
++    E4_uint32 nfptr = dev->dev_interruptq_nfptr;
++    E4_uint32 bptr  = read_reg32 (dev, MainIntQueuePtrs.s.Back);
++    E4_uint32 qsize = E4_QueueSize(elan4_interruptq_size);
++    E4_uint32 qmask = qsize - 1;
++    long      tlim  = lbolt + nticks;
++    int       done = 0;
++    unsigned long flags;
++
++    do {
++      int todo  = ((bptr - nfptr) & qmask) / E4_MainIntEntrySize;
++
++      ASSERT (todo > 0);
++
++      PRINTF4 (DBG_DEVICE, DBG_MAININT, "handle_mainints: fptr %x nfptr %x bptr %x : %d todo\n", 
++               read_reg32 (dev, MainIntQueuePtrs.s.Front), nfptr, bptr, todo);
++
++      if (nintr >= 0 && (done + todo) > nintr)                /* punt because too may to do in interrupt */
++      {
++          PRINTF4 (DBG_DEVICE, DBG_MAININT, "handle_mainints: punting (done %d todo %d) (bptr %x fptr %x)\n",
++                   done, todo, bptr, read_reg32 (dev, MainIntQueuePtrs.s.Front));
++
++          return 1;
++      }
++
++      BucketDevStat (dev, s_mainints, todo, MainIntBuckets);
++
++      /* consume all the entries in the queue which we think are there */
++      do {
++          E4_uint64   value = elan4_sdram_readq (dev, nfptr);
++          ELAN4_CTXT *ctxt  = elan4_localctxt (dev, E4_MAIN_INT_CTX (value));
++          E4_uint32   fptr  = nfptr;
++
++          PRINTF2 (DBG_DEVICE, DBG_MAININT, "handle_mainints: process cookie %llx - write fptr=%x\n", value, nfptr);
++
++          if (ctxt == NULL)
++              PRINTF1 (DBG_DEVICE, DBG_INTR, "handle_mainints: context %d invalid\n", E4_MAIN_INT_CTX (value));
++          else
++              ctxt->ctxt_ops->op_interrupt (ctxt, E4_MAIN_INT_COOKIE(value));
++
++          /* compute the next queue front pointer, before updating the front pointer
++           * since we need to ensure that elan4_queue_mainintop doesn't see the queue
++           * as being empty if an extra interrupt is queued in between */
++          dev->dev_interruptq_nfptr = nfptr = (nfptr & ~qmask) | ((nfptr + sizeof (E4_uint64)) & qmask);
++    
++          /* update the queue front pointer, doing this will clear the
++           * interrupt for *all* interrupt cookies which have previously 
++           * been added to the queue */
++          write_reg32 (dev, MainIntQueuePtrs.s.Front, E4_QueueFrontValue (fptr, elan4_interruptq_size));
++          pioflush_reg (dev);
++      } while (bptr != nfptr);
++      
++      /* re-sample the back pointer and if it's different from the previous
++       * queue front pointer, then the queue has something on it again */
++      done += todo;
++      
++      if ((nticks > 0 && ((int) (lbolt - tlim)) > 0))         /* been executing for too long in thread */
++          return 1;
++
++      bptr = read_reg32 (dev, MainIntQueuePtrs.s.Back);
++
++      PRINTF3 (DBG_DEVICE, DBG_MAININT, "handle_mainints: resample : fptr %x nfptr %x bptr %x\n", 
++               read_reg32 (dev, MainIntQueuePtrs.s.Front), nfptr, bptr);
++
++      /* at this point we've made some space in the interrupt queue,
++       * so check to see if we've got anything to restart */
++      spin_lock_irqsave (&dev->dev_mainint_lock, flags);
++      while (! list_empty (&dev->dev_interruptq_list))
++      {
++          ELAN4_INTOP *op = list_entry (dev->dev_interruptq_list.next, ELAN4_INTOP, op_link);
++          
++          list_del (&op->op_link);
++
++          op->op_function (dev, op->op_arg);
++      }
++      spin_unlock_irqrestore (&dev->dev_mainint_lock, flags);
++
++    } while (bptr != nfptr);
++
++    return 0;
++}
++
++static void
++elan4_mainint_thread (ELAN4_DEV *dev)
++{
++    unsigned long flags;
++
++    kernel_thread_init ("elan4_mainint");
++    
++    spin_lock_irqsave (&dev->dev_mainint_lock, flags);
++    for (;;)
++    {
++      if (dev->dev_stop_threads)
++          break;
++      
++      if (! (dev->dev_intmask & INT_MainInterrupt))
++      {
++          spin_unlock_irqrestore (&dev->dev_mainint_lock, flags);
++          
++          if (handle_mainints (dev, elan4_mainint_resched_ticks, -1))
++              BumpDevStat (dev, s_mainint_rescheds);
++
++          spin_lock_irqsave (&dev->dev_mainint_lock, flags);
++          ENABLE_INT_MASK (dev, INT_MainInterrupt);
++      }
++      
++      kcondvar_wait (&dev->dev_mainint_wait, &dev->dev_mainint_lock, &flags);
++    }
++
++    dev->dev_mainint_stopped = 1;
++    kcondvar_wakeupall (&dev->dev_mainint_wait, &dev->dev_mainint_lock);
++
++    spin_unlock_irqrestore (&dev->dev_mainint_lock, flags);
++
++    kernel_thread_exit();
++}
++
++void
++elan4_queue_mainintop (ELAN4_DEV *dev, ELAN4_INTOP *op)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->dev_mainint_lock, flags);
++    if (dev->dev_interruptq_nfptr == read_reg32 (dev, MainIntQueuePtrs.s.Back))
++      op->op_function (dev, op->op_arg);
++    else
++      list_add_tail (&op->op_link, &dev->dev_interruptq_list);
++    spin_unlock_irqrestore (&dev->dev_mainint_lock, flags);
++}
++
++static __inline__ E4_uint32
++handle_cproc_trap (ELAN4_DEV *dev)
++{
++    E4_uint32   cqptr   = read_reg32 (dev, CommandControl.CommandQueueDescsBase) & E4_QueueDescPtrMask;
++    unsigned    cqnum   = ((cqptr - dev->dev_cqaddr) / sizeof (E4_CommandQueueDesc));
++    sdramaddr_t cqdesc  = dev->dev_cqaddr + (cqnum * sizeof (E4_CommandQueueDesc));
++    E4_uint64   control = elan4_sdram_readq (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_Control));
++    E4_uint64   status  = read_reg64 (dev, CProcStatus);
++    ELAN4_CTXT *ctxt    = elan4_localctxt (dev, CQ_Context (control));
++
++    PRINTF4 (DBG_DEVICE, DBG_INTR, "handle_cproc_trap: cqnum=%d status=%016llx control=%016llx TrapType\n", 
++           cqnum, status, control, CPROC_TrapType (status));
++    PRINTF4 (DBG_DEVICE, DBG_INTR, "                   %016llx %016llx %016llx %016llx\n",
++           elan4_sdram_readq (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_QueuePtrs)),
++           elan4_sdram_readq (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_HoldingValue)),
++           elan4_sdram_readq (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_AckBuffers)),
++           elan4_sdram_readq (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_Control)));
++
++    BumpDevStat (dev, s_cproc_traps);
++
++    ctxt->ctxt_ops->op_cproc_trap (ctxt, status, cqnum);
++
++    return (CPROC_TrapType (status) == CommandProcWaitTrap ? SCH_RestartCProc | SCH_RestartEProc : SCH_RestartCProc);
++}
++
++static __inline__ E4_uint32
++handle_dproc_trap (ELAN4_DEV *dev, int unit)
++{
++    E4_uint64   status  = (unit == 0) ? read_reg64 (dev, DProc0Status) : read_reg64 (dev, DProc1Status);
++    E4_uint32   restart = (unit == 0) ? SCH_RestartDma0Proc : SCH_RestartDma1Proc;
++    ELAN4_CTXT *ctxt    = elan4_localctxt (dev, DPROC_Context (status));
++    
++    PRINTF3 (DBG_DEVICE, DBG_INTR, "handle_dproc_trap: unit %d context %d%s\n", unit, DPROC_Context(status),
++           DPROC_PrefetcherFault(status) ? " (prefetcher)" : "");
++
++    if (DPROC_PrefetcherFault (status))
++      restart |= SCH_RestartDmaPrefetchProc;
++                    
++    BumpDevStat (dev, s_dproc_traps);
++
++    ctxt->ctxt_ops->op_dproc_trap (ctxt, status, unit);
++
++    return (restart);
++}
++
++static __inline__ E4_uint32
++handle_eproc_trap (ELAN4_DEV *dev)
++{
++    E4_uint64   status = read_reg64 (dev, EProcStatus);
++    ELAN4_CTXT *ctxt   = elan4_localctxt (dev, EPROC_Context (status));
++
++    BumpDevStat (dev, s_eproc_traps);
++
++    ctxt->ctxt_ops->op_eproc_trap (ctxt, status);
++
++    return (SCH_RestartEProc);
++}
++
++static __inline__ E4_uint32
++handle_tproc_trap (ELAN4_DEV *dev)
++{
++    E4_uint64   status = read_reg64 (dev, TProcStatus);
++    ELAN4_CTXT *ctxt   = elan4_localctxt (dev, TPROC_Context (status));
++
++    BumpDevStat (dev, s_tproc_traps);
++
++    ctxt->ctxt_ops->op_tproc_trap (ctxt, status);
++    
++    return (SCH_RestartTProc);
++}
++
++static __inline__ void
++handle_haltints (ELAN4_DEV *dev, E4_uint32 intreg)
++{
++    struct list_head  list   = LIST_HEAD_INIT(list);
++    E4_uint32         mask   = 0;
++    E4_uint32         active = 0;
++    struct list_head *entry;
++    struct list_head *next;
++    unsigned long     flags;
++
++    BumpDevStat (dev, s_haltints);
++
++    spin_lock_irqsave (&dev->dev_haltop_lock, flags);
++
++    list_for_each_safe (entry, next, &dev->dev_haltop_list) {
++      ELAN4_HALTOP *op = list_entry (entry, ELAN4_HALTOP, op_link);
++
++      PRINTF (DBG_DEVICE, DBG_INTR, "handle_haltints: op=%p op_mask=%x intreg=%x\n", op, op->op_mask, intreg);
++
++      if ((op->op_mask & intreg) != op->op_mask)
++          mask |= op->op_mask;
++      else
++      {
++          list_del (&op->op_link);                            /* remove from list */
++          list_add_tail (&op->op_link, &list);                /* add to local list */
++
++          active |= op->op_mask;
++      }
++    }
++
++    ASSERT (dev->dev_haltop_mask == (mask | active));
++
++    dev->dev_haltop_mask = mask;
++
++    if (list_empty (&list))
++      elan4_set_schedstatus (dev, intreg);
++    else
++    {
++      dev->dev_haltop_active = active;
++      spin_unlock_irqrestore (&dev->dev_haltop_lock, flags);
++
++      while (! list_empty (&list)) 
++      {
++          ELAN4_HALTOP *op = list_entry (list.next, ELAN4_HALTOP, op_link);
++          
++          list_del (&op->op_link);
++
++          (*op->op_function) (dev, op->op_arg);
++      }
++
++      spin_lock_irqsave (&dev->dev_haltop_lock, flags);
++      dev->dev_haltop_active = 0;
++
++      elan4_set_schedstatus (dev, 0);
++    }
++
++    spin_unlock_irqrestore (&dev->dev_haltop_lock, flags);
++}
++
++static __inline__ E4_uint32
++handle_iproc_trap (ELAN4_DEV *dev, unsigned unit)
++{
++    sdramaddr_t hdroff = dev->dev_inputtraparea + offsetof (E4_IprocTrapState, TrHeader[0][unit]);
++    E4_uint64   status = elan4_sdram_readq (dev, hdroff + offsetof (E4_IprocTrapHeader, IProcStatusCntxAndTrType));
++    E4_uint32   filter = elan4_read_filter (dev, IPROC_NetworkContext (status));
++    ELAN4_CTXT *ctxt   = elan4_localctxt (dev, filter & E4_FILTER_CONTEXT_MASK);
++
++    /*
++     * The context is not valid in the following case :
++     *     ack not been sent AND bad CRC/bad length.
++     *
++     *  NOTE TransCRCStatus and BadLength only valid if NOT an EopTrap.
++     */
++    ASSERT ((IPROC_GoodAckSent (status) & (1 << IPROC_InputterChan (status))) || IPROC_EOPTrap (status) ||
++          (IPROC_TransCRCStatus (status) == CRC_STATUS_GOOD && !IPROC_BadLength (status)));
++    
++    BumpDevStat (dev, s_iproc_traps);
++
++    ctxt->ctxt_ops->op_iproc_trap (ctxt, status, unit);
++
++    return (SCH_RestartCh0LowPriInput << unit);
++}
++
++void
++handle_pcimemerr (ELAN4_DEV *dev)
++{
++    elan4_pcierror (dev);
++
++    check_error_rate (dev);
++}
++
++void
++handle_sdramint (ELAN4_DEV *dev)
++{
++    E4_uint64 status = read_reg64 (dev, SDRamECCStatus);
++    char      errstr[200];
++    int             i;
++    int             Found = 0;
++
++    PRINTF0 (DBG_DEVICE, DBG_INTR, "handle_sdramint\n");
++
++    /* search for this error already being logged */
++    for (i = sizeof (dev->dev_sdramerrs)/sizeof (dev->dev_sdramerrs[0]) - 1; i >= 0; i--)
++        if ((dev->dev_sdramerrs[i] & 0x000fffffffffffffULL) == status)
++      {
++            Found = 1;
++          dev->dev_sdramerrs[i] += 10000000000000ULL; // Keep a count.
++          break;
++      }
++
++    /* stash the status for /proc */
++    if (!Found)
++    {
++      for (i = sizeof (dev->dev_sdramerrs)/sizeof (dev->dev_sdramerrs[0]) - 1; i > 0; i--)
++          dev->dev_sdramerrs[i] = dev->dev_sdramerrs[i-1];
++      dev->dev_sdramerrs[0] = status;
++    }
++
++    printk ("elan%d: ECC Error %s\n", dev->dev_instance, elan4_sdramerr2str (dev, status, errstr));
++
++    if (!ECC_UncorrectableErr(status) && !ECC_MultUncorrectErrs(status))
++      printk ("elan%d: ECC error data=%016llx\n", dev->dev_instance, elan4_sdram_readq (dev, ECC_Addr(status)));
++
++    if (ECC_CorrectableErr (status))
++      BumpDevStat (dev, s_correctable_errors);
++    if (ECC_MultCorrectErrs (status))
++      BumpDevStat (dev, s_multiple_errors);
++
++    if (ECC_UncorrectableErr(status))
++      panic ("elan%d: uncorrectable ECC error\n", dev->dev_instance);
++    if (ECC_MultUncorrectErrs(status))
++      panic ("elan%d: muliple uncorrectable ECC error\n", dev->dev_instance);
++    
++    PULSE_SYSCONTROL (dev, CONT_CLEAR_SDRAM_ERROR);
++
++    check_error_rate (dev);
++}
++
++static void
++clear_linkerr_led (void *arg)
++{
++    ELAN4_DEV *dev = (ELAN4_DEV *) arg;
++
++    write_i2c (dev, I2cStatus, read_i2c (dev, I2cStatus) | I2cCntl_ClearLinkError);
++}
++
++void
++handle_linkerror (ELAN4_DEV *dev)
++{
++    E4_uint32 LinkState;
++    E4_uint32 CurrState = read_reg32 (dev, LinkControlReg);
++
++    /* Set for reading errors. */
++    write_reg32 (dev, LinkControlReg,
++                 (CurrState = CurrState & ~((LCONT_TEST_CONTROL_MASK << LCONT_TEST_CONTROL_SHIFT) |
++                                          (LCONT_TEST_VALUE_MASK << LCONT_TEST_VALUE_SHIFT))));
++    LinkState = LCONT_LINK_STATE(CurrState = read_reg32 (dev, LinkControlReg));
++
++#ifdef DEBUG
++    {
++      E4_uint8 ErrorMsg[256], DataErrorVal[64];
++
++      strcpy (ErrorMsg, "handle_linkerror:");
++      if (LinkState & LS_LockError)    strcat (ErrorMsg, " LockError");
++      if (LinkState & LS_DeskewError)  strcat (ErrorMsg, " DeskewError");
++      if (LinkState & LS_PhaseError)   strcat (ErrorMsg, " PhaseError");
++      if (LinkState & LS_DataError)
++      {
++          E4_uint32 error[4];
++          E4_uint32 i;
++          strcat (ErrorMsg, " DataError");
++          /* Errors */
++          for(i = LRS_ErrorVal8to0; i <= LRS_ErrorVal35to27; i++)
++          {
++              write_reg32 (dev, LinkControlReg,
++                           CurrState | LCONT_TEST_VALUE(i) | (LCONT_READ_STATE << LCONT_TEST_CONTROL_SHIFT));
++              error[i - LRS_ErrorVal8to0] = LCONT_LINK_STATE(read_reg32 (dev, LinkControlReg));
++          }
++          sprintf (DataErrorVal, " Link State Error Val: %09llx %03x %03x %03x %03x", 
++                   (unsigned long long) ((error[0] & 0x1ffUL) | ((error[1] & 0x1ffUL) << 9)  |
++                                ((error[2] & 0x1ffUL) << 18) | ((error[3] & 0x1ffUL) << 27)),
++                   error[3], error[2], error[1], error[0]);
++          strcat (ErrorMsg, DataErrorVal);
++      }
++      if (LinkState & LS_FifoOvFlow0)  strcat (ErrorMsg, " FifoOvFlow0");
++      if (LinkState & LS_FifoOvFlow1)  strcat (ErrorMsg, " FifoOvFlow1");
++      if (LinkState & LS_Mod45Changed)         strcat (ErrorMsg, " Mod45Changed");
++      if (LinkState & LS_PAckNotSeenError) strcat (ErrorMsg, " PAckNotSeenError");
++      strcat (ErrorMsg, "\n");
++      PRINTF0 (DBG_DEVICE, DBG_INTR, ErrorMsg);
++    }
++#endif
++
++    BumpDevStat (dev, s_link_errors);
++    
++    if (LinkState & LS_LockError)      BumpDevStat (dev, s_lock_errors);
++    if (LinkState & LS_DeskewError)    BumpDevStat (dev, s_deskew_errors);
++    if (LinkState & LS_PhaseError)     BumpDevStat (dev, s_phase_errors);
++    if (LinkState & LS_DataError)      BumpDevStat (dev, s_data_errors);
++    if (LinkState & LS_FifoOvFlow0)    BumpDevStat (dev, s_fifo_overflow0);
++    if (LinkState & LS_FifoOvFlow1)    BumpDevStat (dev, s_fifo_overflow1);
++    if (LinkState & LS_Mod45Changed)   BumpDevStat (dev, s_mod45changed);
++    if (LinkState & LS_PAckNotSeenError) BumpDevStat (dev, s_pack_not_seen);
++
++    PULSE_SCHED_RESTART (dev, SCH_ClearLinkErrorInt);
++    
++    /* schedule a timer to clear the link error LED, so that it stays on 
++     * for a second for every link error that occurs */
++    if (dev->dev_devinfo.dev_revision_id != PCI_REVISION_ID_ELAN4_REVA && !timer_fn_queued (&dev->dev_linkerr_timeoutid))
++      schedule_timer_fn (&dev->dev_linkerr_timeoutid, clear_linkerr_led, (void *) dev, HZ);
++      
++    check_error_rate (dev);
++}
++
++void
++handle_linkportkeyfail (ELAN4_DEV *dev)
++{
++    PRINTF0 (DBG_DEVICE, DBG_INTR, "handle_linkportkeyfail\n");
++
++    BumpDevStat (dev, s_linkport_keyfail);
++
++    PULSE_SYSCONTROL (dev, CONT_CLEAR_LINKPORT_INT);
++    
++    check_error_rate (dev);
++}
++
++
++static __inline__ void
++__elan4_4msi0 (ELAN4_DEV *dev, E4_uint32 intreg, E4_uint32 intmask)
++{
++    unsigned long flags;
++
++    if (intreg & intmask & INT_MainInterrupt)
++    {
++      DISABLE_INT_MASK (dev, INT_MainInterrupt);
++
++      if (handle_mainints (dev, -1, elan4_mainint_punt_loops) == 0)
++          ENABLE_INT_MASK (dev, INT_MainInterrupt);
++      else
++      {
++          BumpDevStat (dev, s_mainint_punts);
++          
++          spin_lock_irqsave (&dev->dev_mainint_lock, flags);
++          kcondvar_wakeupone (&dev->dev_mainint_wait, &dev->dev_mainint_lock);
++          spin_unlock_irqrestore (&dev->dev_mainint_lock, flags);
++      }
++    }
++}
++
++static __inline__ void
++__elan4_4msi1 (ELAN4_DEV *dev, E4_uint32 intreg, E4_uint32 intmask)
++{
++    E4_uint32 restart = 0;
++
++    PRINTF1 (DBG_DEVICE, DBG_INTR, "__elan4_4msi1: %x\n", intreg);
++    
++    spin_lock (&dev->dev_trap_lock);
++    
++    if (intreg & intmask & INT_CProc)
++      restart |= handle_cproc_trap (dev);
++    if (intreg & intmask & INT_EProc) 
++      restart |= handle_eproc_trap (dev);
++    if (intreg & intmask & INT_Dma0Proc) 
++      restart |= handle_dproc_trap (dev, 0);
++    if (intreg & intmask & INT_Dma1Proc) 
++      restart |= handle_dproc_trap (dev, 1);
++    if (intreg & intmask & INT_TProc)
++      restart |= handle_tproc_trap (dev);
++    
++    PULSE_SCHED_RESTART (dev, restart);
++    
++    spin_unlock (&dev->dev_trap_lock);
++    
++    if (intreg & (INT_Halted|INT_Discarding))
++      handle_haltints (dev, intreg);
++}
++
++static __inline__ void
++__elan4_4msi2 (ELAN4_DEV *dev, E4_uint32 intreg, E4_uint32 intmask)
++{
++    E4_uint32 restart = 0;
++
++    PRINTF1 (DBG_DEVICE, DBG_INTR, "__elan4_4msi2: %x\n", intreg);
++    
++    spin_lock (&dev->dev_trap_lock);
++    if (intreg & intmask & INT_IProcCh0LowPri)
++      restart |= handle_iproc_trap (dev, 0);
++    
++    if (intreg & intmask & INT_IProcCh1LowPri)
++      restart |= handle_iproc_trap (dev, 1);
++    
++    if (intreg & intmask & INT_IProcCh0HighPri)
++      restart |= handle_iproc_trap (dev, 2);
++    
++    if (intreg & intmask & INT_IProcCh1HighPri)
++      restart |= handle_iproc_trap (dev, 3);
++    
++    PULSE_SCHED_RESTART (dev, restart);
++    
++    spin_unlock (&dev->dev_trap_lock);
++}
++
++static __inline__ void
++__elan4_4msi3 (ELAN4_DEV *dev, E4_uint32 intreg, E4_uint32 intmask)
++{
++    PRINTF1 (DBG_DEVICE, DBG_INTR, "__elan4_4msi3: %x\n", intreg);
++    
++    if (intreg & intmask & INT_PciMemErr)
++      handle_pcimemerr (dev);
++    
++    if (intreg & intmask & INT_SDRamInt)
++      handle_sdramint (dev);
++    
++    if (intreg & intmask & INT_LinkError)
++      handle_linkerror (dev);
++    
++    if (intreg & intmask & INT_LinkPortKeyFail)
++      handle_linkportkeyfail (dev);
++}
++
++int
++elan4_1msi0 (ELAN4_DEV *dev)
++{
++    E4_uint32 intmask = dev->dev_intmask;
++    E4_uint32 intreg;
++
++    if (intmask == 0 || ((intreg = read_reg32 (dev, InterruptReg)) & intmask) == 0)
++      return (0);
++
++    BumpDevStat (dev, s_interrupts);
++    
++    do {
++      PRINTF1 (DBG_DEVICE, DBG_INTR, "elan4_1msi0: %x\n", intreg);
++
++      if (intreg & intmask & INT_MSI0)
++          __elan4_4msi0(dev, intreg, intmask);
++      if (intreg & intmask & INT_MSI1)
++          __elan4_4msi1(dev, intreg, intmask);
++      if (intreg & intmask & INT_MSI2)
++          __elan4_4msi2(dev, intreg, intmask); 
++      if (intreg & intmask & INT_MSI3)
++          __elan4_4msi3(dev, intreg, intmask);
++
++      /* must ensure that the read of the interrupt mask
++       * completes before the read of the interrupt register
++       * since the main interrupt thread clears it's interrupt
++       * and then re-enables it in the interrupt mask. */
++      intmask = dev->dev_intmask;
++      mb();
++      intreg = read_reg32 (dev, InterruptReg);
++
++    } while ((intreg & intmask) != 0);
++
++    return (1);
++}
++
++/* local context management */
++int
++elan4_insertctxt (ELAN4_DEV *dev, ELAN4_CTXT *ctxt, ELAN4_TRAP_OPS *ops)
++{
++    unsigned long flags;
++    int tbl;
++
++    ctxt->ctxt_dev = dev;
++    ctxt->ctxt_ops = ops;
++
++    INIT_LIST_HEAD (&ctxt->ctxt_cqalist);
++    spin_lock_init (&ctxt->ctxt_mmulock);
++
++    for (tbl = 0; tbl < NUM_HASH_TABLES; tbl++)
++    {
++      KMEM_ZALLOC (ctxt->ctxt_mmuhash[tbl], ELAN4_HASH_ENTRY **,  dev->dev_hashsize[tbl] * sizeof (ELAN4_HASH_ENTRY *), 1);
++      
++      if (ctxt->ctxt_mmuhash[tbl] == NULL)
++      {
++          if (tbl != 0)
++              KMEM_FREE (ctxt->ctxt_mmuhash[0], dev->dev_hashsize[0] * sizeof (ELAN4_HASH_ENTRY *));
++          spin_lock_destroy (&ctxt->ctxt_mmulock);
++          return (-ENOMEM);
++      }
++    }
++
++    spin_lock_irqsave (&dev->dev_ctxt_lock, flags);
++
++    if ((ctxt->ctxt_num = bt_freebit (dev->dev_ctxmap, (1 << dev->dev_ctxtableshift))) >= 0)
++    {
++      /* chain onto the lists of all contexts */
++      list_add (&ctxt->ctxt_link, &dev->dev_ctxt_list);
++
++      BT_SET (dev->dev_ctxmap, ctxt->ctxt_num);
++    }
++    
++    spin_unlock_irqrestore (&dev->dev_ctxt_lock, flags);
++
++    return (ctxt->ctxt_num < 0 ? -ENOMEM : 0);
++}
++
++void
++elan4_removectxt (ELAN4_DEV *dev, ELAN4_CTXT *ctxt)
++{
++    unsigned long flags;
++    int tbl;
++
++    /* remove from list of contexts */
++    spin_lock_irqsave (&dev->dev_ctxt_lock, flags);
++
++    list_del (&ctxt->ctxt_link);
++
++    BT_CLEAR (dev->dev_ctxmap, ctxt->ctxt_num);
++
++    spin_unlock_irqrestore (&dev->dev_ctxt_lock, flags);
++
++    spin_lock_destroy (&ctxt->ctxt_info_lock);
++
++    for (tbl = 0; tbl < NUM_HASH_TABLES; tbl++)
++      KMEM_FREE (ctxt->ctxt_mmuhash[tbl],  dev->dev_hashsize[tbl] * sizeof (ELAN4_HASH_ENTRY *));
++
++    spin_lock_destroy (&ctxt->ctxt_mmulock);
++}
++
++ELAN4_CTXT *
++elan4_localctxt (ELAN4_DEV *dev, unsigned num)
++{
++    struct list_head *entry;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->dev_ctxt_lock, flags);
++
++    list_for_each (entry, &dev->dev_ctxt_list) {
++      ELAN4_CTXT *ctxt = list_entry (entry, ELAN4_CTXT, ctxt_link);
++      
++      if (ctxt->ctxt_num == num)
++      {
++          spin_unlock_irqrestore (&dev->dev_ctxt_lock, flags);
++          return (ctxt);
++      }
++    }
++    spin_unlock_irqrestore (&dev->dev_ctxt_lock, flags);
++
++    return ((ELAN4_CTXT *) NULL);
++}
++
++ELAN4_CTXT *
++elan4_networkctxt (ELAN4_DEV *dev, unsigned num)
++{
++    E4_uint32 filter = elan4_read_filter (dev, num);
++    
++    if ((filter & E4_FILTER_CONTEXT_MASK) == INVALID_CONTEXT)
++      return NULL;
++    else
++      return elan4_localctxt (dev, filter & E4_FILTER_CONTEXT_MASK);
++}
++
++/* network context management */
++int
++elan4_attach_filter (ELAN4_CTXT *ctxt, unsigned int ctxnum)
++{
++    ELAN4_DEV        *dev = ctxt->ctxt_dev;
++    int               res = 0;
++    E4_uint32         filter;
++    unsigned long     flags;
++    
++    spin_lock_irqsave (&dev->dev_ctxt_lock, flags);
++    
++    filter = elan4_read_filter (dev, ctxnum);
++    if ((filter & E4_FILTER_CONTEXT_MASK) != INVALID_CONTEXT)
++    {
++      PRINTF2 (ctxt, DBG_NETWORK_CTX, "elan4_attach_filter: ctx=%d filter=%x -> EBUSY\n", ctxnum, filter);
++      res = -EBUSY;
++    }
++    else
++    {
++      PRINTF1 (ctxt, DBG_NETWORK_CTX, "elan4_attach_filter: ctx=%d - SUCCESS\n", ctxnum);
++
++      elan4_write_filter (dev, ctxnum, ctxt->ctxt_num | E4_FILTER_DISCARD_ALL);
++      PULSE_SCHED_RESTART (dev, SCH_ContextFilterFlush);
++    }
++    spin_unlock_irqrestore (&dev->dev_ctxt_lock, flags);
++    
++    return (res);
++}
++
++void
++elan4_detach_filter (ELAN4_CTXT *ctxt, unsigned int ctxnum)
++{
++    ELAN4_DEV *dev = ctxt->ctxt_dev;
++
++    PRINTF1 (ctxt, DBG_NETWORK_CTX, "elan4_detach_filter: detach from network context %d\n", ctxnum);
++          
++    elan4_write_filter (dev, ctxnum, INVALID_CONTEXT | E4_FILTER_DISCARD_ALL);
++    PULSE_SCHED_RESTART (dev, SCH_ContextFilterFlush);
++}
++
++void
++elan4_set_filter (ELAN4_CTXT *ctxt, unsigned int ctxnum, E4_uint32 state)
++{
++    ELAN4_DEV *dev = ctxt->ctxt_dev;
++
++    PRINTF6 (ctxt, DBG_NETWORK_CTX, "elan4_set_filter: set filter state %x for network context %d <%s%s%s%s>\n", state, ctxnum,
++           (state & E4_FILTER_DISCARD_ALL) ? "discard,"  : "",
++           (state & E4_FILTER_ACKOK_ALL)   ? "ack-ok,"   : "",
++           (state & E4_FILTER_HIGH_PRI)    ? "high-pri," : "",
++           (state & E4_FILTER_STATS)       ? "stats,"    : "");
++          
++    elan4_write_filter (dev, ctxnum, ctxt->ctxt_num | state);
++    PULSE_SCHED_RESTART (dev, SCH_ContextFilterFlush);
++}
++
++void
++elan4_set_routetable (ELAN4_CTXT *ctxt, ELAN4_ROUTE_TABLE *tbl)
++{
++    ELAN4_DEV *dev   = ctxt->ctxt_dev;
++    E4_uint32  value = tbl ? (E4_VPT_VALID | E4_VPT_VALUE(tbl->tbl_entries, tbl->tbl_size)) : 0;
++    
++    /* and insert into the vp table */
++    elan4_sdram_writel (dev, (dev->dev_ctxtable + (ctxt->ctxt_num * sizeof (E4_ContextControlBlock)) +
++                      offsetof (E4_ContextControlBlock, VirtualProcessTable)), value);
++    pioflush_sdram(dev);
++
++    PULSE_SYSCONTROL (dev, CONT_ROUTE_FLUSH);
++}
++
++/* command queue management */
++ELAN4_CQA *
++elan4_getcqa (ELAN4_CTXT *ctxt, unsigned int idx)
++{
++    ELAN4_DEV *dev = ctxt->ctxt_dev;
++    struct list_head *el;
++
++    spin_lock (&dev->dev_cqlock);
++    list_for_each (el, &ctxt->ctxt_cqalist) {
++      ELAN4_CQA *cqa = list_entry (el, ELAN4_CQA, cqa_link);
++      
++      if (cqa->cqa_idx == idx)
++      {
++          cqa->cqa_ref++;
++
++          spin_unlock (&dev->dev_cqlock);
++          return cqa;
++      }
++    }
++    spin_unlock (&dev->dev_cqlock);
++    return NULL;
++}
++
++void
++elan4_putcqa (ELAN4_CTXT *ctxt, unsigned int idx)
++{
++    ELAN4_DEV *dev = ctxt->ctxt_dev;
++    struct list_head *el, *nel;
++
++    spin_lock (&dev->dev_cqlock);
++    list_for_each_safe (el, nel, &ctxt->ctxt_cqalist) {
++      ELAN4_CQA *cqa = list_entry (el, ELAN4_CQA, cqa_link);
++      
++      if (cqa->cqa_idx == idx)
++      {
++          if (--cqa->cqa_ref || bt_lowbit (cqa->cqa_bitmap, ELAN4_CQ_PER_CQA) != -1)
++              spin_unlock (&dev->dev_cqlock);
++          else
++          {
++              list_del (&cqa->cqa_link);
++              
++              BT_CLEAR (ctxt->ctxt_cqamap, cqa->cqa_idx);
++              BT_CLEAR (dev->dev_cqamap, cqa->cqa_cqnum/ELAN4_CQ_PER_CQA);
++              spin_unlock (&dev->dev_cqlock);
++              
++              KMEM_FREE (cqa, sizeof (ELAN4_CQA));
++          }
++          return;
++      }
++    }
++    spin_unlock (&dev->dev_cqlock);
++
++    printk ("elan4_putcqa: idx %d not found\n", idx);
++    BUG();
++}
++
++static ELAN4_CQ *
++elan4_getcq (ELAN4_CTXT *ctxt, unsigned int type)
++{
++    ELAN4_DEV        *dev = ctxt->ctxt_dev;
++    ELAN4_CQA        *cqa;
++    struct list_head *el;
++    int                     cidx, didx;
++
++    spin_lock (&dev->dev_cqlock);
++    list_for_each (el, &ctxt->ctxt_cqalist) {
++      cqa = list_entry (el, ELAN4_CQA, cqa_link);
++
++      if (cqa->cqa_type == type && (cidx = bt_freebit (cqa->cqa_bitmap, ELAN4_CQ_PER_CQA)) >=0)
++      {
++          BT_SET (cqa->cqa_bitmap, cidx);
++          
++          spin_unlock (&dev->dev_cqlock);
++          return &cqa->cqa_cq[cidx];
++      }
++    }
++    spin_unlock (&dev->dev_cqlock);
++
++    /* allocate a new cqa and it's chunk of command queue descriptors */
++    KMEM_ZALLOC (cqa, ELAN4_CQA *, sizeof (ELAN4_CQA), 1);
++    if (cqa == NULL)
++      return NULL;
++
++    spin_lock (&dev->dev_cqlock);
++    cidx = bt_freebit (ctxt->ctxt_cqamap, ELAN4_MAX_CQA);
++
++    /* On architectures which have MTRR registers for write-combinig
++     * the top command queues from dev->dev_cqreorder upwards are
++     * used for reordered queues.  Without MTRR registers any page
++     * sized group can use write combinig through the ptes. */
++#ifdef CONFIG_MTRR
++    if ((type & CQ_Reorder) != 0)
++      didx = bt_nextbit (dev->dev_cqamap, dev->dev_cqcount, dev->dev_cqreorder - 1, 0);
++    else
++      didx = bt_freebit (dev->dev_cqamap, dev->dev_cqreorder);
++#else
++    didx = bt_freebit (dev->dev_cqamap, dev->dev_cqcount);
++#endif
++
++    if (cidx < 0 || didx < 0)
++    {
++      spin_unlock (&dev->dev_cqlock);
++      KMEM_FREE (cqa, sizeof (ELAN4_CQA));
++      return NULL;
++    }
++
++    BT_SET (ctxt->ctxt_cqamap, cidx);
++    BT_SET (dev->dev_cqamap, didx);
++
++    cqa->cqa_idx   = cidx;
++    cqa->cqa_type  = type;
++    cqa->cqa_cqnum = (didx * ELAN4_CQ_PER_CQA);
++    
++    list_add_tail (&cqa->cqa_link, &ctxt->ctxt_cqalist);
++    
++    /* initialise the cqa struct */
++    for (cidx = 0; cidx < ELAN4_CQ_PER_CQA; cidx++)
++    {
++      cqa->cqa_cq[cidx].cq_idx = cidx;
++      cqa->cqa_cq[cidx].cq_cqa = cqa;
++    }
++
++    /* no mappings yet */
++    cqa->cqa_ref = 0;
++
++    /* we're going to return entry zero */
++    BT_SET (cqa->cqa_bitmap, 0);
++    spin_unlock (&dev->dev_cqlock);
++    
++    return &cqa->cqa_cq[0];
++}
++
++static void
++elan4_putcq (ELAN4_CTXT *ctxt, ELAN4_CQ *cq)
++{
++    ELAN4_DEV        *dev = ctxt->ctxt_dev;
++    ELAN4_CQA        *cqa = cq->cq_cqa;
++
++    spin_lock (&dev->dev_cqlock);
++
++    BT_CLEAR (cqa->cqa_bitmap, cq->cq_idx);
++
++    if (bt_lowbit (cqa->cqa_bitmap, ELAN4_CQ_PER_CQA) != -1 || cqa->cqa_ref)
++      spin_unlock (&dev->dev_cqlock);
++    else
++    {
++      list_del (&cqa->cqa_link);
++      
++      BT_CLEAR (ctxt->ctxt_cqamap, cqa->cqa_idx);
++      BT_CLEAR (dev->dev_cqamap, cqa->cqa_cqnum/ELAN4_CQ_PER_CQA);
++      spin_unlock (&dev->dev_cqlock);
++      
++      KMEM_FREE (cqa, sizeof (ELAN4_CQA));
++    }
++}
++
++ELAN4_CQ *
++elan4_alloccq (ELAN4_CTXT *ctxt, unsigned cqsize, unsigned perm, unsigned cqtype)
++{
++    ELAN4_DEV   *dev = ctxt->ctxt_dev;
++    ELAN4_CQ    *cq;
++    int         cqnum;
++    sdramaddr_t cqdesc;
++    unsigned    offset;
++    E4_uint64   value;
++
++    if ((cq = elan4_getcq (ctxt, cqtype)) == NULL)
++      return NULL;
++
++    cqnum = elan4_cq2num(cq);
++    
++    cq->cq_space = elan4_sdram_alloc (dev, CQ_Size(cqsize));
++    if (cq->cq_space == (virtaddr_t) 0)
++    {
++      elan4_putcq (ctxt, cq);
++      return (NULL);
++    }
++
++    cq->cq_size   = cqsize;
++    cq->cq_perm   = perm;
++    
++    /* and finally initialise the command queue descriptor */
++    cqdesc = dev->dev_cqaddr + (cqnum * sizeof (E4_CommandQueueDesc));
++
++    value  = CQ_QueuePtrsValue (cqsize, cq->cq_space, cq->cq_space);
++    if (dev->dev_devinfo.dev_revision_id == PCI_REVISION_ID_ELAN4_REVA)
++      value |= ((cqtype & CQ_Priority) ? CQ_RevA_Priority : 0);
++    else
++      value |= (((cqtype & CQ_Priority) ? CQ_RevB_Priority : 0) | 
++                ((cqtype & CQ_Reorder)  ? CQ_RevB_ReorderingQueue : CQ_RevB_32bitWriteQueue));
++
++    elan4_sdram_writeq (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_QueuePtrs), value);
++    elan4_sdram_writeq (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_HoldingValue), 0);
++    elan4_sdram_writeq (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_AckBuffers), 0);
++    elan4_sdram_writeq (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_Control), CQ_ControlValue (ctxt->ctxt_num, 2, perm));
++    pioflush_sdram (dev);
++
++    offset = (cqnum + dev->dev_cqoffset) * CQ_CommandMappingSize;
++
++    cq->cq_mapping = elan4_map_device (dev, ELAN4_BAR_REGISTERS, (offset & ~(PAGE_SIZE-1)), 
++                                     PAGE_SIZE, &cq->cq_handle) + (offset & (PAGE_SIZE-1));
++#ifdef CONFIG_MPSAS
++    if (ctxt == &dev->dev_ctxt)
++      return (cq);
++#endif
++
++    elan4_sdram_flushcache (dev, cq->cq_space, CQ_Size(cqsize));
++
++    return (cq);
++}
++    
++void
++elan4_freecq (ELAN4_CTXT *ctxt, ELAN4_CQ *cq)
++{
++    ELAN4_DEV *dev    = ctxt->ctxt_dev;
++    unsigned   offset = (elan4_cq2num(cq) + dev->dev_cqoffset) * CQ_CommandMappingSize;
++
++    elan4_flushcq (dev, cq);
++
++    elan4_unmap_device (dev, cq->cq_mapping - (offset & (PAGE_SIZE-1)), PAGE_SIZE, &cq->cq_handle);
++    elan4_sdram_free (dev, cq->cq_space, CQ_Size (cq->cq_size));
++
++    elan4_putcq (ctxt, cq);
++}
++
++void
++elan4_restartcq (ELAN4_DEV *dev, ELAN4_CQ *cq)
++{
++    sdramaddr_t   cqdesc = dev->dev_cqaddr + (elan4_cq2num(cq) * sizeof (E4_CommandQueueDesc));
++    int           hipri;
++    unsigned long flags;
++    
++    PRINTF1 (DBG_DEVICE, DBG_CPROC, "restartcq: restarting cq %p\n", cq);
++    
++    spin_lock_irqsave (&dev->dev_requeue_lock, flags);
++
++    while (read_reg32 (dev, CommandControl.CommandRequeuePtr) & E4_CommandRequeueBusy)
++      ;
++    
++    if (dev->dev_devinfo.dev_revision_id == PCI_REVISION_ID_ELAN4_REVA)
++      hipri = (elan4_sdram_readq (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_QueuePtrs)) & CQ_RevA_Priority) != 0;
++    else
++      hipri = (elan4_sdram_readq (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_QueuePtrs)) & CQ_RevB_Priority) != 0;
++    
++    if (hipri)
++    {
++      PRINTF1 (DBG_DEVICE, DBG_CPROC, "restartcq: restart cq %d as high pri\n", elan4_cq2num(cq));
++      write_reg32 (dev, CommandControl.CommandRequeuePtr, cqdesc | E4_CommandRequeueHighPri);
++    }
++    else
++    {
++      PRINTF1 (DBG_DEVICE, DBG_CPROC, "restartcq: restart cq %d as low pri\n", elan4_cq2num(cq));
++      write_reg32 (dev, CommandControl.CommandRequeuePtr, cqdesc);
++    }
++    pioflush_reg (dev);
++    
++    spin_unlock_irqrestore (&dev->dev_requeue_lock, flags);
++}
++
++static void
++flushcq_intop (ELAN4_DEV *dev, void *arg)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->dev_flush_lock, flags);
++    dev->dev_flush_finished |= (1 << (unsigned long) arg);
++    kcondvar_wakeupall (&dev->dev_flush_wait, &dev->dev_flush_lock);
++    spin_unlock_irqrestore (&dev->dev_flush_lock, flags);
++}
++void
++elan4_flushcq (ELAN4_DEV *dev, ELAN4_CQ *cq)
++{
++    int                 flushqnum = elan4_cq2num(cq) & (COMMAND_INSERTER_CACHE_ENTRIES-1);
++    ELAN4_CQ     *flushq    = dev->dev_flush_cq[flushqnum];
++    unsigned long flags;
++
++    PRINTF (DBG_DEVICE, DBG_FLUSH, "elan4_flushcq: cqnum=%d\n", elan4_cq2num(cq));
++
++    spin_lock_irqsave (&dev->dev_flush_lock, flags);
++
++    while (! (dev->dev_flush_finished & (1 << flushqnum)))
++      kcondvar_wait (&dev->dev_flush_wait, &dev->dev_flush_lock, &flags);
++    
++    dev->dev_flush_finished &= ~(1 << flushqnum);
++
++    dev->dev_flush_op[flushqnum].op_function = flushcq_intop;
++    dev->dev_flush_op[flushqnum].op_arg      = (void *) (unsigned long) flushqnum;
++    
++    elan4_queue_intop (dev, flushq, &dev->dev_flush_op[flushqnum]);
++
++    while (! (dev->dev_flush_finished & (1 << flushqnum)))
++      kcondvar_wait (&dev->dev_flush_wait, &dev->dev_flush_lock, &flags);
++    
++    spin_unlock_irqrestore (&dev->dev_flush_lock, flags);
++}
++
++void
++elan4_updatecq (ELAN4_DEV *dev, ELAN4_CQ *cq, unsigned perm, unsigned restart)
++{
++    sdramaddr_t cqdesc  = dev->dev_cqaddr + (elan4_cq2num(cq) * sizeof (E4_CommandQueueDesc));
++    E4_uint32   control = elan4_sdram_readl (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_Control));
++
++    /* Write the command queues control word, but ensure that the ChannelNotCompleted fields
++     * are not modified.   We use this to just alter the RestartCount/Permissions fields */
++
++    elan4_sdram_writel (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_Control), 
++                      CQ_ControlValue (CQ_Context (control), restart ? restart : CQ_RestartCount (control), perm));
++}
++
++/* instruction cache flush */
++static __inline__ void
++elan4_flush_icache_locked (ELAN4_DEV *dev)
++{
++    int i, j;
++
++    PRINTF0 (DBG_DEVICE, DBG_FLUSH, "elan4_flush_icache_locked: flushing icache\n");
++
++    for (i = 0; i < (E4_ICacheLines/E4_ICachePortSize); i++)
++    {
++        write_reg64 (dev, ICachePort_Cntl_Addr, i << E4_ICacheTagAddrShift);
++        for (j = 0; j < E4_ICachePortSize; j++)
++          write_reg64 (dev, ICachePort[j], E4_InvalidTagValue);
++    }
++
++    /*
++     * Initialise the top of the ICache Set0 with a instruction which will
++     * cause a know trap fingerprint so that the application can identify it
++     * and ignore the trap.
++     */
++    write_reg64 (dev, ICachePort_Cntl_Addr, E4_ICacheFixupOffset | E4_AccessICacheRams);
++
++    /* Errata 24: must ensure that the DCache is flushed after loading 
++     *            code for the thread processor. */
++    if (dev->dev_devinfo.dev_revision_id == PCI_REVISION_ID_ELAN4_REVA)
++      elan4_sdram_flushcache (dev, 0, E4_CacheSize);
++
++    pioflush_reg (dev);
++}
++
++static void
++device_iflush_haltop (ELAN4_DEV *dev, void *arg)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->dev_flush_lock, flags);
++    
++    elan4_flush_icache_locked (dev);
++
++    dev->dev_iflush_queued = 0;
++
++    kcondvar_wakeupall (&dev->dev_flush_wait, &dev->dev_flush_lock);
++    spin_unlock_irqrestore (&dev->dev_flush_lock, flags);
++}
++
++void
++elan4_flush_icache_halted (ELAN4_CTXT *ctxt)
++{
++    ELAN4_DEV *dev = ctxt->ctxt_dev;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->dev_flush_lock, flags);
++    
++    elan4_flush_icache_locked (dev);
++
++    spin_unlock_irqrestore (&dev->dev_flush_lock, flags);
++}
++
++void
++elan4_flush_icache (ELAN4_CTXT *ctxt)
++{
++    ELAN4_DEV *dev = ctxt->ctxt_dev;
++    unsigned long flags;
++    
++    spin_lock_irqsave (&dev->dev_flush_lock, flags);
++
++    PRINTF1 (DBG_DEVICE, DBG_FLUSH, "elan4_flush_icache: queued=%d\n", dev->dev_iflush_queued);
++
++    if (! dev->dev_iflush_queued)
++    {
++      dev->dev_iflush_queued = 1;
++      
++      elan4_queue_haltop (dev, &dev->dev_iflush_haltop);
++    }
++
++    while (dev->dev_iflush_queued)
++      kcondvar_wait (&dev->dev_flush_wait, &dev->dev_flush_lock, &flags);
++
++    spin_unlock_irqrestore (&dev->dev_flush_lock, flags);
++}
++
++/* device context operations */
++static void
++device_cproc_trap (ELAN4_CTXT *ctxt, E4_uint64 status, unsigned cqnum)
++{
++    ELAN4_DEV        *dev  = ctxt->ctxt_dev;
++    ELAN4_CPROC_TRAP *trap = &dev->dev_cproc_trap;
++
++    elan4_extract_cproc_trap (dev, status, trap, cqnum);
++
++    DBGCMD (DBG_DEVICE, DBG_FLUSH, elan4_display_cproc_trap (DBG_DEVICE, DBG_FLUSH, "device_cproc_trap", trap));
++
++    switch (CPROC_TrapType (trap->tr_status))
++    {
++    case CommandProcInterruptQueueOverflow:
++      PRINTF (ctxt, DBG_FLUSH, "device_cproc_trap: cqnum=%d\n", cqnum);
++
++      /* XXXX: we could either just hit restart (and hope) - or we could extract
++       *       the event interrupt cookie out and "complete" the command before
++       *       restarting it */
++      elan4_restartcq (dev, dev->dev_flush_cq[cqnum]);
++      return;
++
++    case CommandProcDmaQueueOverflow:
++    case CommandProcPermissionTrap:
++      handle_dma_flushops (dev, status, cqnum);
++      return;
++      
++    default:
++      printk ("device_cproc_trap: status=%llx control=%llx TrapType=%x cqnum=%d\n", (long long) trap->tr_status,
++              elan4_sdram_readq (dev, dev->dev_cqaddr + cqnum * sizeof (E4_CommandQueueDesc) +
++                                 offsetof (E4_CommandQueueDesc, CQ_Control)),
++              (int) CPROC_TrapType(trap->tr_status), cqnum);
++      panic ("device_cproc_trap");
++    }
++}
++
++static void
++device_tproc_trap (ELAN4_CTXT *ctxt, E4_uint64 status)
++{
++    ELAN4_TPROC_TRAP trap;
++
++    elan4_extract_tproc_trap (ctxt->ctxt_dev, status, &trap);
++
++    elan4_display_tproc_trap (DBG_CONSOLE, DBG_TRAP, "device_tproc_trap", &trap);
++    panic ("device_tproc_trap");
++}
++
++static void
++device_dproc_trap (ELAN4_CTXT *ctxt, E4_uint64 status, unsigned unit)
++{
++    ELAN4_DPROC_TRAP trap;
++
++    elan4_extract_dproc_trap (ctxt->ctxt_dev, status, &trap, unit);
++
++    elan4_display_dproc_trap (DBG_CONSOLE, DBG_TRAP, "device_dproc_trap", &trap);
++    panic ("device_dproc_trap");
++}
++
++static void
++device_interrupt (ELAN4_CTXT *ctxt, E4_uint64 cookie)
++{
++    ELAN4_DEV *dev = (ELAN4_DEV *) ctxt;
++    struct list_head *el,*nel;
++    unsigned long flags;
++
++    PRINTF (ctxt, DBG_FLUSH, "device_interrupt: cookie=%llx\n", cookie);
++
++    spin_lock_irqsave (&dev->dev_intop_lock, flags);
++    list_for_each_safe (el, nel, &dev->dev_intop_list) {
++      ELAN4_INTOP *op = list_entry (el, ELAN4_INTOP, op_link);
++
++      if (op->op_cookie == cookie)
++      {
++          if ((op->op_cookie & INTOP_TYPE_MASK) == INTOP_ONESHOT)
++              list_del (&op->op_link);
++
++          spin_unlock_irqrestore (&dev->dev_intop_lock, flags);
++          
++          (*op->op_function)(dev, op->op_arg);
++          return;
++      }
++    }
++    spin_unlock_irqrestore (&dev->dev_intop_lock, flags);
++
++    panic ("device_interrupt: interrupt cookie %llx not found\n", cookie);
++}
++
++static void
++device_iproc_trap (ELAN4_CTXT *ctxt, E4_uint64 status, unsigned unit)
++{
++    ELAN4_DEV *dev = ctxt->ctxt_dev;
++    ELAN4_IPROC_TRAP *trap = &dev->dev_iproc_trap;
++
++    elan4_extract_iproc_trap (dev, status, trap, unit);
++    elan4_inspect_iproc_trap (trap);
++
++    DBGCMD (ctxt, DBG_IPROC, elan4_display_iproc_trap (ctxt, DBG_IPROC, "device_iproc_trap", trap));
++
++    if (elan4_neterr_iproc_trap (dev, trap))
++      return;
++
++    elan4_display_iproc_trap (DBG_CONSOLE, DBG_TRAP, "device_iproc_trap", trap);
++    panic ("device_iproc_trap: unexpected trap\n");
++}
++
++ELAN4_TRAP_OPS device_trap_ops = 
++{
++    NULL,
++    device_cproc_trap,
++    device_dproc_trap,
++    device_tproc_trap,
++    device_iproc_trap,
++    device_interrupt,
++};
++
++/*
++ * elan4_initialise_device
++ *    initialise the ELAN4_DEV struct - spinlocks,cvs etc.
++ *    map the registers, sdram etc
++ */
++int
++elan4_initialise_device (ELAN4_DEV *dev)
++{
++    int i, bit;
++
++    if (elan4_mainint_resched_ticks == 0)
++      elan4_mainint_resched_ticks = (hz/4);
++
++    /* map the registers */
++    switch (dev->dev_devinfo.dev_revision_id)
++    {
++    case PCI_REVISION_ID_ELAN4_REVA:
++      dev->dev_regs = elan4_map_device (dev, ELAN4_BAR_REGISTERS, ELAN4_REVA_REG_OFFSET, ELAN4_REG_SIZE, &dev->dev_regs_handle);
++      
++      dev->dev_rom  = elan4_map_device (dev, ELAN4_BAR_REGISTERS, ELAN4_REVA_EBUS_OFFSET + ELAN4_REVA_EBUS_ROM_OFFSET, 
++                                        ELAN4_REVA_EBUS_ROM_SIZE, &dev->dev_rom_handle);
++      break;
++      
++    case PCI_REVISION_ID_ELAN4_REVB:
++      dev->dev_regs = elan4_map_device (dev, ELAN4_BAR_REGISTERS, ELAN4_REVB_REG_OFFSET, ELAN4_REG_SIZE, &dev->dev_regs_handle);
++      dev->dev_rom  = (ioaddr_t) 0;
++      dev->dev_i2c  = elan4_map_device (dev, ELAN4_BAR_REGISTERS, ELAN4_REVB_I2C_OFFSET, ELAN4_REVB_I2C_SIZE, &dev->dev_i2c_handle);
++      break;
++
++    default:
++      return -EINVAL;
++    }
++
++    /* XXXX: parse the ebus rom to determine the sdram configuration */
++    {
++      extern long long       sdram_cfg;
++
++      if (sdram_cfg == 0)
++          dev->dev_sdram_cfg = SDRAM_STARTUP_VALUE;
++      else
++          dev->dev_sdram_cfg = sdram_cfg;
++    }
++
++    for (bit = 0; ((1 << bit) & elan4_resource_len (dev, ELAN4_BAR_SDRAM)) == 0; bit++)
++      ;
++
++    switch ((dev->dev_sdram_cfg >> SDRAM_RamSize_SH) & 3)
++    {
++    case 0:                   /* 64Mbit, 128Mbit, 256Mbit, 512Mbit or 1Gbit (16-bit output) */
++      dev->dev_sdram_numbanks = 4; bit -= 2;
++      for (i = 0; i < dev->dev_sdram_numbanks; i++)
++      {
++          dev->dev_sdram_banks[i].b_base = (i << bit);
++          dev->dev_sdram_banks[i].b_size = (1 << bit);
++      }
++      break;
++
++    case 1:                   /*  64Mbit, 128Mbit, 256Mbit or 512Mbit (8-bit output) */
++      dev->dev_sdram_numbanks = 4; bit -= 2;
++      for (i = 0; i < dev->dev_sdram_numbanks; i++)
++      {
++          dev->dev_sdram_banks[i].b_base = ((i & 2) << (bit)) | ((i & 1) << (bit-1));
++          dev->dev_sdram_banks[i].b_size = (1 << bit);
++      }
++      break;
++      
++    case 2:                   /* 2Gbit (16-bit output) or 1Gbit (8-bit output) */
++      dev->dev_sdram_numbanks = 2; bit--;
++      for (i = 0; i < dev->dev_sdram_numbanks; i++)
++      {
++          dev->dev_sdram_banks[i].b_base = (i << bit);
++          dev->dev_sdram_banks[i].b_size = (1 << bit);
++      }
++      break;
++
++    case 3:                   /* 4Gbit (16-bit output) or 2Gbit (8-bit output) */
++      dev->dev_sdram_numbanks = 1;
++      dev->dev_sdram_banks[0].b_base = 0;
++      dev->dev_sdram_banks[0].b_size = (1 << bit);
++      break;
++    }
++
++    elan4_sdram_init (dev);
++
++    /* initialise locks for classes of interrupts */
++    spin_lock_init (&dev->dev_trap_lock);
++    spin_lock_init (&dev->dev_intop_lock);
++    spin_lock_init (&dev->dev_haltop_lock);
++    spin_lock_init (&dev->dev_mainint_lock);
++
++    /* initialise other locks */
++    spin_lock_init (&dev->dev_i2c_lock);
++
++    spin_lock_init (&dev->dev_mmulock);
++    spin_lock_init (&dev->dev_cqlock);
++    spin_lock_init (&dev->dev_ctxlock);
++
++    spin_lock_init (&dev->dev_intmask_lock);
++    spin_lock_init (&dev->dev_syscontrol_lock);
++
++    spin_lock_init (&dev->dev_ctxt_lock);
++    spin_lock_init (&dev->dev_flush_lock);
++    spin_lock_init (&dev->dev_requeue_lock);
++
++    kmutex_init (&dev->dev_lock);
++
++    kcondvar_init (&dev->dev_mainint_wait);
++    kcondvar_init (&dev->dev_flush_wait);
++
++    /* initialsie lists */
++    INIT_LIST_HEAD (&dev->dev_ctxt_list);
++    INIT_LIST_HEAD (&dev->dev_intop_list);
++    INIT_LIST_HEAD (&dev->dev_interruptq_list);
++    INIT_LIST_HEAD (&dev->dev_hc_list);
++    INIT_LIST_HEAD (&dev->dev_haltop_list);
++    INIT_LIST_HEAD (&dev->dev_dma_flushop[0].list);
++    INIT_LIST_HEAD (&dev->dev_dma_flushop[1].list);
++
++    dev->dev_state = ELAN4_STATE_STOPPED;
++
++    return (0);
++}
++
++void
++elan4_finalise_device (ELAN4_DEV *dev)
++{
++    kcondvar_destroy (&dev->dev_flush_wait);
++    kcondvar_destroy (&dev->dev_mainint_wait);
++
++    kmutex_destroy (&dev->dev_lock);
++
++    spin_lock_destroy (&dev->dev_requeue_lock);
++    spin_lock_destroy (&dev->dev_flush_lock);
++    spin_lock_destroy (&dev->dev_ctxt_lock);
++
++    spin_lock_destroy (&dev->dev_syscontrol_lock);
++    spin_lock_destroy (&dev->dev_intmask_lock);
++
++    spin_lock_destroy (&dev->dev_ctxlock);
++    spin_lock_destroy (&dev->dev_cqlock);
++    spin_lock_destroy (&dev->dev_mmulock);
++
++    spin_lock_destroy (&dev->dev_i2c_lock);
++
++    spin_lock_destroy (&dev->dev_mainint_lock);
++    spin_lock_destroy (&dev->dev_haltop_lock);
++    spin_lock_destroy (&dev->dev_intop_lock);
++    spin_lock_destroy (&dev->dev_trap_lock);
++
++    while (! list_empty (&dev->dev_hc_list))
++    {
++      ELAN4_HASH_CHUNK *hc = list_entry (dev->dev_hc_list.next, ELAN4_HASH_CHUNK, hc_link);
++      
++      list_del (&hc->hc_link);
++
++      KMEM_FREE(hc, sizeof (ELAN4_HASH_CHUNK));
++    }
++    
++    elan4_sdram_fini (dev);
++    
++    switch (dev->dev_devinfo.dev_revision_id)
++    {
++    case PCI_REVISION_ID_ELAN4_REVA:
++      elan4_unmap_device (dev, dev->dev_rom,  ELAN4_REVA_EBUS_ROM_SIZE, &dev->dev_rom_handle);
++      elan4_unmap_device (dev, dev->dev_regs, ELAN4_REG_SIZE, &dev->dev_regs_handle);
++      break;
++    case PCI_REVISION_ID_ELAN4_REVB:
++      elan4_unmap_device (dev, dev->dev_i2c,  ELAN4_REVB_I2C_SIZE, &dev->dev_i2c_handle);
++      elan4_unmap_device (dev, dev->dev_regs, ELAN4_REG_SIZE, &dev->dev_regs_handle);
++      break;
++    }
++}
++
++static void
++initialise_cache (ELAN4_DEV *dev)
++{
++    register int set, line;
++
++    /* Initialise the cache to "map" the bottom of sdram - we will use
++     * this space for cache flushing, so require the cache to be set
++     * up so that cachelines for this are in the correct set.
++     *
++     * XXXX: for MPSAS we set bit 28, to ensure that any access to 
++     *       sdram causes the line to be filled first to expunge any
++     *       Xs. */
++    for (set = 0; set < E4_NumCacheSets; set++)
++      for (line = 0; line < E4_NumCacheLines; line++)
++          write_tag (dev, Tags[set][line], (((E4_uint64) set) << 29) | (1 << 28) | (line << 16));
++}
++
++#ifndef CONFIG_MPSAS
++static void
++initialise_cache_tags (ELAN4_DEV *dev, unsigned addr)
++{
++    register int set, line;
++
++    /* Initialise the whole cache to hold sdram at "addr" as direct mapped */
++
++    for (set = 0; set < E4_NumCacheSets; set++)
++      for (line = 0; line < E4_NumCacheLines; line++)
++          write_tag (dev, Tags[set][line], addr | (set << 13) | (1 << 11));
++}
++
++static void
++initialise_ecc (ELAN4_DEV *dev, ELAN4_SDRAM_BANK *bank)
++{
++    register int i, addr;
++
++    if (dev->dev_devinfo.dev_revision_id == PCI_REVISION_ID_ELAN4_REVA)
++    {
++        initialise_cache_tags (dev, E4_CacheSize);
++        for (addr = 0; addr < bank->b_size; addr += E4_CacheSize)
++        {
++          for (i = 0; i < E4_CacheSize; i += sizeof (E4_uint64))
++              writeq (0xbeec000000000000ull | addr | i, bank->b_ioaddr + addr + i);
++          initialise_cache_tags (dev, addr);
++        }
++    }
++    else
++    {
++      /* Write the whole of this bank of sdram. */
++        for (addr = 0; addr < bank->b_size; addr += sizeof (E4_uint64))
++          writeq (0xbeec000000000000ull | addr, bank->b_ioaddr + addr);
++
++      /* Now flush out the top out of the cache */
++        for (addr = 0; addr < E4_CacheSize; addr += sizeof (E4_uint64))
++          writeq (0xbeec000000000000ull | addr, bank->b_ioaddr + addr);
++
++      /* Now read the top value of sdram to guarantee the write has occured before the ecc is enabled */
++      readq (bank->b_ioaddr + bank->b_size - sizeof (E4_uint64));
++    }
++}
++#endif
++
++#ifdef CONFIG_MPSAS
++static void
++do_initdma (ELAN4_DEV *dev)
++{
++#define VIRTUAL_ADDRESS       0x10000000ull
++    ELAN4_CQ  *cq  = dev->dev_flush_cq[0];
++    E4_uint64 value;
++    E4_uint32 intreg;
++    E4_uint64 status;
++
++    PRINTF (DBG_DEVICE, DBG_CONFIG, "elan: performing initialising dma\n");
++    
++    DISABLE_INT_MASK (dev, INT_Dma0Proc | INT_Dma1Proc);
++    
++    /* initialise the context filter */
++    elan4_attach_filter (&dev->dev_ctxt, 0);
++
++    /* now issue a DMA - we expect this to trap */
++    writeq (E4_DMA_TYPE_SIZE (128*4, DMA_DataTypeByte, 0, 0) | RUN_DMA_CMD, cq->cq_mapping + (0 << 3));
++    writeq (0,                                                              cq->cq_mapping + (1 << 3));
++    writeq (0,                                                              cq->cq_mapping + (2 << 3));
++    writeq (dev->dev_tproc_space,                                           cq->cq_mapping + (3 << 3));
++    writeq (dev->dev_tproc_space,                                           cq->cq_mapping + (4 << 3));
++    writeq (0,                                                              cq->cq_mapping + (5 << 3));
++    writeq (0,                                                              cq->cq_mapping + (6 << 3));
++    
++    /* spin waiting for it to trap - then restart the dma processor */
++    do {
++      value   = read_reg64 (dev, IntAndMaskReg);
++      intreg  = (value >> E4_INTERRUPT_REG_SHIFT);
++    } while ((intreg & (INT_Dma0Proc | INT_Dma1Proc)) == 0);
++    
++    /* check it trapped for the right reason */
++    status = (intreg & INT_Dma0Proc) ? read_reg64 (dev, DProc0Status) : read_reg64 (dev, DProc1Status);
++    
++    if (DPROC_PrefetcherFault (status) || (DPROC_TrapType(status) != DmaProcFailCountError && DPROC_TrapType(status) != DmaProcPacketAckError))
++    {
++      printk ("elan: bad dma trap, status = %lx\n", (long)status);
++      panic ("elan: bad dma trap\n");
++    }
++    
++    PULSE_SCHED_RESTART (dev, SCH_RestartDma0Proc | SCH_RestartDma1Proc | SCH_RestartDmaPrefetchProc);
++
++    elan4_detach _filter (&dev->dev_ctxt, 0);
++
++    ENABLE_INT_MASK (dev, INT_Dma0Proc | INT_Dma1Proc);
++#undef VIRTUAL_ADDRESS
++}
++#endif
++
++static int
++ebus_read_vpd (ELAN4_DEV *dev, unsigned char *data, unsigned int nob)
++{
++    unsigned int pci_data_ptr;
++    unsigned int vpd_ptr;
++    register int i;
++
++    if (read_ebus_rom (dev, 0) != 0x55 || read_ebus_rom (dev, 1) != 0xaa)
++    {
++      printk ("elan%d: invalid rom signature in ebus rom\n", dev->dev_instance);
++      return -EINVAL;
++    }
++
++    pci_data_ptr = (read_ebus_rom (dev, 0x19) << 8) | read_ebus_rom (dev, 0x18);
++
++    /* check the pci data structure */
++    if (read_ebus_rom (dev, pci_data_ptr + 0) != 'P' ||
++      read_ebus_rom (dev, pci_data_ptr + 1) != 'C' ||
++      read_ebus_rom (dev, pci_data_ptr + 2) != 'I' ||
++      read_ebus_rom (dev, pci_data_ptr + 3) != 'R')
++    {
++      printk ("elan%d: invalid pci data structure in ebus rom\n", dev->dev_instance);
++      return -EINVAL;
++    }
++    
++    /* extract the VPD pointer */
++    vpd_ptr = (read_ebus_rom (dev, pci_data_ptr + 9) << 8) | read_ebus_rom (dev, pci_data_ptr + 8);
++
++    if (vpd_ptr == 0)
++    {
++      printk ("elan%d: no vital product data in ebus rom\n", dev->dev_instance);
++      return -EINVAL;
++    }
++    
++    /* read the vpd data */
++    for (i = 0; i < nob; i++)
++      data[i] = read_ebus_rom (dev, vpd_ptr + i);
++
++    return 0;
++}
++
++int
++elan4_read_vpd (ELAN4_DEV *dev, unsigned char *tag, unsigned char *result) 
++{
++    unsigned char vpd[I2C_ELAN_EEPROM_VPD_SIZE];
++    unsigned char *ptr = vpd;
++    unsigned int   finished = 0;
++    unsigned char *lim;
++    unsigned char  name[3];
++    unsigned char  value[256];
++    unsigned char  type;
++    unsigned int   len, len2;
++    register int   i;
++
++    if (dev->dev_devinfo.dev_revision_id == PCI_REVISION_ID_ELAN4_REVA)
++    {
++      if (ebus_read_vpd (dev, vpd, I2C_ELAN_EEPROM_VPD_SIZE) < 0)
++      {
++          PRINTF1 (DBG_DEVICE, DBG_CONFIG, "elan%d: elan4_read_vpd, unable to read serial number from EBUS rom\n", dev->dev_instance);
++          return -EINVAL ;
++      }       
++    }
++    else
++    {
++      if (i2c_read_rom (dev, I2C_ELAN_EEPROM_VPD_BASEADDR, I2C_ELAN_EEPROM_VPD_SIZE, vpd) < 0)
++      {
++          PRINTF1 (DBG_DEVICE, DBG_CONFIG, "elan%d: elan4_read_vpd, unable to read serial number from I2C rom\n", dev->dev_instance);
++          return  -EINVAL;
++      }
++    }
++
++    result[0] = 0;
++    while (! finished)
++    {
++      type = *ptr++;
++      
++      if (type & LARGE_RESOURCE_BIT)
++      {
++          len = *(ptr++);
++          len += *(ptr++) << 8;
++          
++          switch (type & ~LARGE_RESOURCE_BIT)
++          {
++          case LARGE_RESOURCE_STRING:
++          case LARGE_RESOURCE_VENDOR_DEFINED:
++              ptr += len;
++              break;
++              
++          case LARGE_RESOURCE_VITAL_PRODUCT_DATA:
++              for (lim = ptr + len; ptr < lim; )
++              {
++                  name[0] = *ptr++;
++                  name[1] = *ptr++;
++                  name[2] = '\0';
++                  len2    = *ptr++;
++                  
++                  for (i = 0; i < len2 && ptr < lim; i++)
++                      value[i] = *ptr++;
++                  value[i] = '\0';
++                                  
++                  PRINTF3 (DBG_DEVICE, DBG_CONFIG, "elan%d: elan4_read_vpd, %s: $s\n", dev->dev_instance, name, value);
++
++                  if (tag != NULL) 
++                  { /* looking for just one tag */
++                      if (!strcmp (name, tag))
++                          strcpy(result, value);
++                  } 
++                  else 
++                  { /* get all tags */
++                      strcat(result,name);
++                      strcat(result,": ");
++                      strcat(result,value);
++                      strcat(result,"\n");
++                  }
++              }
++              break;
++              
++          default:
++              PRINTF2 (DBG_DEVICE, DBG_CONFIG, "elan%d: elan4_read_vpd, unknown large resource %x\n", dev->dev_instance, type);
++              finished = 1;
++              break;
++          }
++      }
++      else
++      {
++          len = type & 0x7;
++          
++          switch (type >> 3)
++          {
++          case SMALL_RESOURCE_COMPATIBLE_DEVICE_ID:
++              ptr += len;
++              break;
++              
++          case SMALL_RESOURCE_VENDOR_DEFINED:
++              ptr += len;
++              break;
++              
++          case SMALL_RESOURCE_END_TAG:
++              finished = 1;
++              break;
++              
++          default:
++              PRINTF2 (DBG_DEVICE, DBG_CONFIG, "elan%d: elan4_read_vpd, unknown small resource %x\n", dev->dev_instance, type >> 3);
++              finished = 1;
++              break;
++          }
++      }
++    }
++
++    if ( result[0] == 0 ) {
++      if ( tag != 0 ) 
++          PRINTF2 (DBG_DEVICE, DBG_CONFIG, "elan%d: elan4_read_vpd, failed to find tag %s\n", dev->dev_instance, tag);
++      else
++          PRINTF1 (DBG_DEVICE, DBG_CONFIG, "elan%d: elan4_read_vpd, failed to find any tags\n", dev->dev_instance);
++      return -EINVAL;
++    }
++
++    return (0);
++}
++
++int
++elan4_start_device (ELAN4_DEV *dev)
++{
++    E4_VirtualProcessEntry entry;
++    unsigned             pagesizeval[2];
++    unsigned             hashsizeval[2];
++    register int           i, j, tbl, res;
++    unsigned               attempts = 0;
++    E4_PAGE_SIZE_TABLE;
++    unsigned char          serial[256];
++
++    PRINTF (DBG_DEVICE, DBG_ALL, "elan4_start_device: entered\n");
++
++    dev->dev_state = ELAN4_STATE_STARTING;
++
++ tryagain:
++    /* Initialise the pci config space */
++    if ((res = elan4_pciinit (dev)) < 0)
++      return (res);
++
++    /* Display the serial number */
++    if (elan4_read_vpd (dev, "SN", serial))
++      printk("elan%d: SN: failed to read\n", dev->dev_instance);
++    else
++      printk("elan%d: SN: %s\n", dev->dev_instance, serial);
++
++    /* initialise the interrupt mask to zero */
++    SET_INT_MASK (dev, 0);
++
++    /* Initialise the device registers */
++    write_reg64 (dev, TlbLineValue, 0);
++    write_reg64 (dev, SysControlReg, 0);
++
++    /* Initialise the SDRAM using the configuration value from the ROM */
++    write_reg64 (dev, SDRamConfigReg, dev->dev_sdram_cfg | SDRAM_SETUP);
++
++    /* Setup the linkport registers */
++    write_reg64 (dev, LinkPortLock, 0);
++    write_reg64 (dev, LinkPortKey,  LINK_PORT_LOCK_VALUE);
++
++    /* Setup the tick rates, start the clock, and init the stats registers */
++    write_ureg32 (dev, ClockTickRate.s.TickRates, ELAN4_CLOCK_TICK_RATE);
++    write_ureg64 (dev, Clock, 0);
++    write_ureg32 (dev, InstCount.s.StatsCount, 0);
++    for (i = 0; i < 8; i++)
++      write_ureg32 (dev, StatCounts[i].s.StatsCount, 0);
++
++    /* Initialise the Link Control register - disable the TLB prefetcher on RevB
++     * as it can cause very occasional data corruption. */
++    if (dev->dev_devinfo.dev_revision_id == PCI_REVISION_ID_ELAN4_REVB)
++      write_reg32 (dev, LinkControlReg, LCONT_REVB_DISABLE_TLB_PREFETCH);
++    else
++      write_reg32 (dev, LinkControlReg, 0);
++
++    /* Initialise the Link Control Settings to set the PLL Reference Value */
++    write_reg32 (dev, LinkContSettings, 
++               (elan4_mod45disable ? LCONT_MOD45_DISABLE : 0) |
++               (3 << LCONT_CONFIG_PHASE_SHIFT) |
++               ((elan4_pll_div & LCONT_PLL_REF_VAL_BITS_MASK) << LCONT_PLL_REF_VAL_BITS_SHIFT) |
++               (LCONT_VOD_360 << LCONT_LVDS_VOLTAGE_BITS_SHIFT) |
++               (LCONT_TERM_AUTO_OHM << LCONT_LVDS_TERMINATION_SHIFT));
++
++    /* Clear the link error LED on RevB and above */
++    if (dev->dev_devinfo.dev_revision_id != PCI_REVISION_ID_ELAN4_REVA)
++      write_i2c (dev, I2cStatus, read_i2c (dev, I2cStatus) | I2cCntl_ClearLinkError);
++
++    initialise_cache (dev);
++
++    /* Initialise the MMU hash table parameters */
++    /* Select the largest elan pagesize which is spanned by the
++     * system pagesize for mmu table 0*/
++    for (i = 0; i < E4_PAGE_SIZE_TABLE_SIZE; i++)
++      if (PageSizeTable[i] > PAGE_SHIFT)
++          break;
++
++    pagesizeval[0] = i - 1;
++    hashsizeval[0] = elan4_hash_0_size_val;
++      
++    /* Select a suitable elan pagesize to match any "large" page
++     * support that the OS provides. */
++    pagesizeval[1] = PAGE_SIZE_4M;
++    hashsizeval[1] = elan4_hash_1_size_val;
++
++    for (tbl = 0; tbl < NUM_HASH_TABLES; tbl++)
++    {
++      dev->dev_pagesizeval[tbl]   = pagesizeval[tbl];
++      dev->dev_pageshift[tbl]     = PageSizeTable[pagesizeval[tbl]];
++      dev->dev_hashsize[tbl]      = (1 << hashsizeval[tbl])/sizeof (E4_HashTableEntry);
++      dev->dev_rsvd_hashmask[tbl] = ((1 << (27 - dev->dev_pageshift[tbl]))-1) & ~((1 << hashsizeval[tbl])-1);
++      dev->dev_rsvd_hashval[tbl]  = 0xFFFFFFFF;
++    }
++
++    PRINTF2 (DBG_DEVICE, DBG_CONFIG, "elan4_start_device: pageshifts %d,%d\n", dev->dev_pageshift[0], 
++           NUM_HASH_TABLES == 2 ? dev->dev_pageshift[1] : 0);
++
++    /* Initialise the control register to the desired value */
++    dev->dev_syscontrol = (CONT_EN_ALL_SETS | CONT_MMU_ENABLE | CONT_CACHE_ALL | CONT_2K_NOT_1K_DMA_PACKETS |
++                         (pagesizeval[0] << CONT_TABLE0_PAGE_SIZE_SHIFT) | (hashsizeval[0] << CONT_TABLE0_MASK_SIZE_SHIFT));
++
++    if (NUM_HASH_TABLES == 2)
++      dev->dev_syscontrol |= CONT_TWO_HASH_TABLES | (pagesizeval[1] << CONT_TABLE1_PAGE_SIZE_SHIFT) | (hashsizeval[1] << CONT_TABLE1_MASK_SIZE_SHIFT);
++
++    write_reg64 (dev, SysControlReg, dev->dev_syscontrol);
++
++    /* use direct mapped pci writes during sdram initialisation, since for 
++     * cache flushing to work, we need to ensure that the cacheflush page
++     * never gets lines into the incorrect cache set. */
++    SET_SYSCONTROL (dev, dev_direct_map_pci_writes, CONT_DIRECT_MAP_PCI_WRITES);
++
++    if (dev->dev_devinfo.dev_revision_id == PCI_REVISION_ID_ELAN4_REVB)
++      elan4_sdram_setup_delay_lines(dev);
++
++    for (i = res = 0; i < dev->dev_sdram_numbanks; i++)
++      if (dev->dev_sdram_banks[i].b_size)
++          res |= elan4_sdram_init_bank (dev, &dev->dev_sdram_banks[i]);
++
++    if (! res)
++    {
++      if (dev->dev_devinfo.dev_device_id == PCI_REVISION_ID_ELAN4_REVB && ++attempts < 5)
++      {
++          printk ("elan%d: sdram not working, resetting\n", dev->dev_instance);
++          goto tryagain;
++      }
++
++      printk ("elan%d: could not find any sdram banks\n", dev->dev_instance);
++      goto failed;
++    }
++
++#ifndef CONFIG_MPSAS
++    PRINTF0 (DBG_DEVICE, DBG_CONFIG, "elan4_start_device: initialising for ECC\n");
++
++    for (i = 0 ; i < dev->dev_sdram_numbanks; i++)
++      if (dev->dev_sdram_banks[i].b_ioaddr)
++          initialise_ecc (dev, &dev->dev_sdram_banks[i]);
++#endif
++
++    dev->dev_sdram_initial_ecc_val = read_reg64 (dev, SDRamECCStatus);
++
++    /* Now enable ECC after we've scrubbed the memory */
++    write_reg64 (dev, SDRamConfigReg, dev->dev_sdram_cfg | SDRAM_ENABLE_ECC);
++
++    /* clear any errors, and flush the tlb/route cache */
++    PULSE_SYSCONTROL (dev, CONT_TLB_FLUSH | CONT_ROUTE_FLUSH | CONT_CLEAR_LINKPORT_INT | CONT_CLEAR_SDRAM_ERROR);
++
++    write_ureg32 (dev, InstCount.s.StatsCount, 0);
++
++    /* Initialise the thread processor's register file */
++    for (i = 0; i < 64; i++)
++      write_reg64 (dev, TProcRegs[i], 0);
++
++    /* Initialise the thread processor's ICache tags */
++    for (i = 0; i < (E4_ICacheLines/E4_ICachePortSize); i++)
++    {
++        write_reg64 (dev, ICachePort_Cntl_Addr, i << E4_ICacheTagAddrShift);
++        for (j = 0; j < E4_ICachePortSize; j++)
++          write_reg64 (dev, ICachePort[j], E4_InvalidTagValue);
++    }
++
++    /*
++     * Initialise the ICache with a sethi %hi(addr << 7), %r0
++     * writing 8 64 bit values per loop of sethi %g0 values ending in 77 for something different??
++     */
++    for (i = 0; i < E4_ICacheSizeInBytes; i += (E4_ICachePortSize << 3))
++    {
++      write_reg64 (dev, ICachePort_Cntl_Addr, E4_AccessICacheRams | (i >> 3));
++
++      for (j = 0; j < E4_ICachePortSize; j++)
++          write_reg64 (dev, ICachePort[j], 
++                       (E4_uint64) (((E4_uint64)i << (4+7))    + ((E4_uint64)j << (1+7))    + (0x077)) |
++                       (E4_uint64) (((E4_uint64)i << (4+7+32)) + ((E4_uint64)j << (1+7+32)) + (0x0e7)) << 32);
++    }
++
++    /*
++     * Initialise the top of the ICache Set0 with a instruction which will
++     * cause a know trap fingerprint so that the application can identify it
++     * and ignore the trap.
++     */
++    write_reg64 (dev, ICachePort_Cntl_Addr, E4_ICacheFixupOffset | E4_AccessICacheRams);
++    for (i = 0; i < E4_ICachePortSize; i++)
++      write_reg64 (dev, ICachePort[i], E4_ICacheFixupInsn | (E4_ICacheFixupInsn << 32));
++
++    /* create the buddy allocator for SDRAM */
++    for (i = 0; i < dev->dev_sdram_numbanks; i++)
++      if (dev->dev_sdram_banks[i].b_ioaddr)
++          elan4_sdram_add_bank (dev, &dev->dev_sdram_banks[i]);
++
++    dev->dev_ctxtableshift        = elan4_ctxt_table_shift;
++    dev->dev_cqcount              = (1 << elan4_ln2_max_cqs);
++#ifdef CONFIG_MTRR
++    if (dev->dev_devinfo.dev_revision_id == PCI_REVISION_ID_ELAN4_REVB)
++      dev->dev_cqreorder = dev->dev_cqcount >> 1;
++    else
++      dev->dev_cqreorder = dev->dev_cqcount;
++#endif
++
++    /* allocate the sdram for cache flushing whilst still in direct mapped mode */
++    dev->dev_cacheflush_space = elan4_sdram_alloc (dev, E4_CacheSize);
++
++    /* and longer need direct mapped pci writes */
++    CLEAR_SYSCONTROL (dev, dev_direct_map_pci_writes, CONT_DIRECT_MAP_PCI_WRITES);
++
++    /* allocate the hash tables, command queues, context tables etc */
++    PRINTF0 (DBG_DEVICE, DBG_CONFIG, "elan4_start_device: allocating hash tables, command queueus, context tables\n");
++
++    dev->dev_comqlowpri       = elan4_sdram_alloc (dev, (1 << COMMAND_RUN_QUEUE_BITS));
++    dev->dev_comqhighpri      = elan4_sdram_alloc (dev, (1 << COMMAND_RUN_QUEUE_BITS));
++    dev->dev_cqaddr           = elan4_sdram_alloc (dev, sizeof (E4_CommandQueueDesc) * dev->dev_cqcount);
++    dev->dev_dmaqhighpri      = elan4_sdram_alloc (dev, E4_QueueSize(elan4_dmaq_highpri_size));
++    dev->dev_dmaqlowpri       = elan4_sdram_alloc (dev, E4_QueueSize(elan4_dmaq_lowpri_size));
++    dev->dev_threadqhighpri   = elan4_sdram_alloc (dev, E4_QueueSize(elan4_threadq_highpri_size));
++    dev->dev_threadqlowpri    = elan4_sdram_alloc (dev, E4_QueueSize(elan4_threadq_lowpri_size));
++    dev->dev_interruptq       = elan4_sdram_alloc (dev, E4_QueueSize(elan4_interruptq_size));
++
++    dev->dev_ctxtable         = elan4_sdram_alloc (dev, (1 << dev->dev_ctxtableshift) * sizeof (E4_ContextControlBlock));
++    dev->dev_faultarea        = elan4_sdram_alloc (dev, CUN_Entries * sizeof (E4_FaultSave));
++    dev->dev_inputtraparea    = elan4_sdram_alloc (dev, sizeof (E4_IprocTrapState));
++
++    dev->dev_sdrampages[0]    = elan4_sdram_alloc (dev, SDRAM_PAGE_SIZE);
++    dev->dev_sdrampages[1]    = elan4_sdram_alloc (dev, SDRAM_PAGE_SIZE);
++
++    for (tbl = 0; tbl < NUM_HASH_TABLES; tbl++)
++    {
++      dev->dev_hashtable[tbl] = elan4_sdram_alloc (dev, dev->dev_hashsize[tbl] * sizeof (E4_HashTableEntry));
++#ifndef CONFIG_MPSAS
++      /* Initialise hash tables to invalid (zero) */
++      elan4_sdram_zeroq_sdram (dev, dev->dev_hashtable[tbl], dev->dev_hashsize[tbl] * sizeof (E4_HashTableEntry));
++#endif
++    }
++
++    /* Initialise all context filters to discard */
++#ifdef CONFIG_MPSAS
++    if (sas_memset_dev (dev->dev_osdep.pdev, ELAN4_BAR_SDRAM, dev->dev_ctxtable, 
++                      E4_FILTER_DISCARD_ALL, (1 << (dev->dev_ctxtableshift-1))) < 0)
++    {
++      for (i = 0; i < (1 << dev->dev_ctxtableshift); i++)
++          elan4_write_filter (dev, i, E4_FILTER_DISCARD_ALL);
++    }
++#else
++    for (i = 0; i < (1 << dev->dev_ctxtableshift); i++)
++      elan4_write_filter (dev, i, E4_FILTER_DISCARD_ALL);
++#endif
++
++    PRINTF4 (DBG_DEVICE, DBG_CONFIG, "elan4_start_device: hashtables %x,%x, %x,%x\n", dev->dev_hashtable[0], 
++          dev->dev_hashsize[0], dev->dev_hashtable[1], dev->dev_hashsize[1]);
++
++    /* install the hash table pointers */
++    PRINTF0 (DBG_DEVICE, DBG_CONFIG, "elan4_start_device: initialise registers with table addresses\n");
++    write_reg64 (dev, MmuTableBasePtrs, (((E4_uint64) dev->dev_hashtable[0]) | ((E4_uint64) dev->dev_hashtable[1]) << 32));
++    write_reg64 (dev, MmuFaultAndRootCntxPtr, (((E4_uint64) dev->dev_ctxtableshift) | 
++                                             ((E4_uint64) dev->dev_ctxtable) |
++                                             ((E4_uint64) dev->dev_faultarea) << 32));
++    write_reg64 (dev, InputTrapAndFilter, (((E4_uint64) dev->dev_ctxtableshift) | 
++                                         ((E4_uint64) dev->dev_ctxtable) |
++                                         ((E4_uint64) dev->dev_inputtraparea) << 32));
++    /*
++     * The run ptrs have this format: (Front << 32) | Back
++     * The base for both the front and back is uses the high bits of the back pointer.
++     * So writting just the base value is good enough.
++     */
++    write_reg64 (dev, CommandLowPriRunPtrs,  dev->dev_comqlowpri);
++    write_reg64 (dev, CommandHighPriRunPtrs, dev->dev_comqhighpri);
++
++    /* Initialise the run queues */
++    write_reg64 (dev, DProcHighPriPtrs,       E4_QueueValue (dev->dev_dmaqhighpri,    elan4_dmaq_highpri_size));
++    write_reg64 (dev, DProcLowPriPtrs,        E4_QueueValue (dev->dev_dmaqlowpri,     elan4_dmaq_lowpri_size));
++    write_reg64 (dev, TProcHighPriPtrs,       E4_QueueValue (dev->dev_threadqhighpri, elan4_threadq_highpri_size));
++    write_reg64 (dev, TProcLowPriPtrs,        E4_QueueValue (dev->dev_threadqlowpri,  elan4_threadq_lowpri_size));
++
++    /* Initialise the interrupt queue as "empty" - this is actually with one entry on it */
++    write_reg64 (dev, MainIntQueuePtrs.Value, (((E4_uint64) E4_QueueFrontValue (dev->dev_interruptq, elan4_interruptq_size) << 32) |
++                                             ((E4_uint64) E4_QueueBackPointer(dev->dev_interruptq + E4_MainIntEntrySize))));
++    
++    dev->dev_interruptq_nfptr = dev->dev_interruptq + E4_MainIntEntrySize;
++
++    /*
++     * Flush the context filter before dropping the Discard all bits in the schedule status register.
++     * Also hit the SCH_RestartTProc to clear out X's from the trap state and
++     * hit the SCH_RestartDmaPrefetchProc to clear out X's from the prev register.
++     */
++    PULSE_SCHED_RESTART (dev, SCH_ContextFilterFlush | SCH_RestartTProc | SCH_RestartDmaPrefetchProc);
++
++    /* setup the schedule status register. */
++    SET_SCHED_STATUS (dev, SCH_CProcTimeout6p2us | SCH_DProcTimeslice512us);
++
++    /*
++     * Now initialise the inserter cache.s
++     * Bit 31 of the first word of the descriptor is a valid bit. This must be cleared.
++     * Bit 31 becomes a used bit in the descriptors in memory.
++     */
++    for (i = 0; i < COMMAND_INSERTER_CACHE_ENTRIES; i++)
++    {
++      write_reg32 (dev, CommandControl.CommandQueueDescsBase, i);     /* select a cache line */
++      write_reg64 (dev, CommandCacheTestPort, 0);                     /* Mark it invalid */
++    }
++    
++    /* Setup the pointer to the command descriptors */
++    /*   the table must be aligned on a CQ_CommandDescsAlignement boundary */
++    /*   since we've allocated a small table - we work out the offset of the */
++    /*   first entry in our table for mapping in the command ports later */
++    dev->dev_cqoffset = (dev->dev_cqaddr & (CQ_CommandDescsAlignment-1)) / sizeof (E4_CommandQueueDesc);
++
++    write_reg32 (dev, CommandControl.CommandQueueDescsBase, (dev->dev_cqaddr & ~(CQ_CommandDescsAlignment-1)) | COM_ENABLE_DEQUEUE);
++
++    /* allocate the bitmaps for cq,ctxt allocation */
++    KMEM_ZALLOC (dev->dev_cqamap, bitmap_t *, BT_BITOUL(dev->dev_cqcount/ELAN4_CQ_PER_CQA) * sizeof (bitmap_t), 1);
++    KMEM_ZALLOC (dev->dev_ctxmap, bitmap_t *, BT_BITOUL(1 << dev->dev_ctxtableshift) * sizeof (bitmap_t), 1);
++
++    if (dev->dev_cqamap == NULL || dev->dev_ctxmap == NULL)
++      goto failed;
++
++    /* Make every fourth context be invalid for ICache fixup.
++     * context 0 is also invalid - since it is used to indicate 
++     * an invalid tag. */
++    for (i = 0; i < (1 << dev->dev_ctxtableshift); i += 4)
++      BT_SET (dev->dev_ctxmap, i);
++    
++    /* initialise the halt operations */
++    dev->dev_haltop_mask   = 0;
++    dev->dev_haltop_active = 0;
++
++    /* allocate the hash table shadow structures - and place all blocks on the free lists */
++    for (tbl = 0; tbl < NUM_HASH_TABLES; tbl++)
++    {
++      KMEM_ZALLOC (dev->dev_mmuhash[tbl], ELAN4_HASH_ENTRY *,  dev->dev_hashsize[tbl] * sizeof (ELAN4_HASH_ENTRY), 1);
++      KMEM_ZALLOC (dev->dev_mmufree[tbl], ELAN4_HASH_ENTRY **, dev->dev_hashsize[tbl] * sizeof (ELAN4_HASH_ENTRY *), 1);
++
++      if (dev->dev_mmuhash[tbl] == NULL || dev->dev_mmufree[tbl] == NULL)
++          goto failed;
++
++      for (i = 0; i < dev->dev_hashsize[tbl]; i++)
++      {
++          dev->dev_mmuhash[tbl][i].he_entry = dev->dev_hashtable[tbl] + (i * sizeof (E4_HashTableEntry));
++          dev->dev_mmufree[tbl][i]          = &dev->dev_mmuhash[tbl][i];
++      }
++    }
++
++    /* setup the interrupt mask register */
++    SET_INT_MASK (dev, (INT_MSI0 | INT_MSI1 | INT_MSI2 | INT_MSI3) & ~(INT_Discarding | INT_Halted));
++
++    /* start a thread to handle excessive main interrupts */
++    if (kernel_thread_create (elan4_mainint_thread, (caddr_t) dev) == NULL)
++      goto failed;
++    dev->dev_mainint_started = 1;
++    
++    /* install the device context - and allocate the first 16 command queues */
++    if (elan4_insertctxt (dev, &dev->dev_ctxt, &device_trap_ops) != 0)
++      goto failed;
++
++    /* Allocate command queues, one for each entry in the inserter cache, 
++     * we'll use these queues to flush the insert cache */
++    for (i = 0; i < COMMAND_INSERTER_CACHE_ENTRIES; i++)
++    {
++      if ((dev->dev_flush_cq[i] = elan4_alloccq (&dev->dev_ctxt, CQ_Size1K, CQ_DmaStartEnableBit | CQ_InterruptEnableBit, 
++                                                 CQ_Priority)) == NULL)
++          goto failed;
++      
++      ASSERT (elan4_cq2num(dev->dev_flush_cq[i]) == i);
++
++      dev->dev_flush_finished |= (1 << i);
++    }
++
++    /* Allocate command queues for dma halt operations */
++    if ((dev->dev_dma_flushop[0].cq = elan4_alloccq (&dev->dev_ctxt, CQ_Size1K, CQ_DmaStartEnableBit, 0)) == NULL ||
++      (dev->dev_dma_flushop[1].cq = elan4_alloccq (&dev->dev_ctxt, CQ_Size1K, CQ_DmaStartEnableBit, CQ_Priority)) == NULL)
++      goto failed;
++
++#ifdef CONFIG_MPSAS
++    elan4_sdram_flushcache (dev, 0, E4_CacheSize);
++#endif
++
++    /* initialise halt operation for flushing the icache */
++    dev->dev_iflush_haltop.op_function = device_iflush_haltop;
++    dev->dev_iflush_haltop.op_arg      = dev;
++    dev->dev_iflush_haltop.op_mask     = INT_TProcHalted;
++
++    /* Allocate a route table, and create a valid route for vp==0, this is used
++     * when a DMA is removed from the dma run queue */
++    if ((dev->dev_routetable = elan4_alloc_routetable (dev, 0)) == NULL)
++      goto failed;
++
++    elan4_set_routetable (&dev->dev_ctxt, dev->dev_routetable);
++
++    entry.Values[0] = FIRST_MYLINK;
++    entry.Values[1] = 0;
++
++    elan4_write_route (dev, dev->dev_routetable, 0, &entry);
++
++    /* map the sdram pages into the elan */
++    dev->dev_tproc_suspend = DEVICE_TPROC_SUSPEND_ADDR;
++    dev->dev_tproc_space   = DEVICE_TPROC_SPACE_ADDR;
++
++    elan4mmu_pteload (&dev->dev_ctxt, 0, dev->dev_tproc_suspend, (dev->dev_sdrampages[0] >> PTE_PADDR_SHIFT) | PTE_SetPerm(PERM_LocExecute));
++    elan4mmu_pteload (&dev->dev_ctxt, 0, dev->dev_tproc_space,   (dev->dev_sdrampages[1] >> PTE_PADDR_SHIFT) | PTE_SetPerm(PERM_LocDataWrite));
++
++    /* and store the thread suspend sequence in it for use when a thread is removed from the run queue */
++    elan4_sdram_writel (dev, dev->dev_sdrampages[0], DEVICE_TPROC_SUSPEND_INSTR);
++
++#ifdef CONFIG_MPSAS
++    do_initdma (dev);
++#endif
++    
++    if (!elan4_neterr_init (dev))
++      goto failed;
++
++    elan4_configure_mtrr (dev);
++
++    /* finally register the device with elanmod for rms */
++    dev->dev_idx = elan_dev_register (&dev->dev_devinfo, &elan4_dev_ops, (void *) dev);
++
++    dev->dev_state = ELAN4_STATE_STARTED;
++
++    return (0);
++
++ failed:
++    printk ("elan%d: failed to start elan4 device - stopping\n", dev->dev_instance);
++
++    elan4_stop_device (dev);
++    return (-ENOMEM);
++}
++
++void
++elan4_stop_device (ELAN4_DEV *dev)
++{
++    unsigned long flags;
++    int i, tbl;
++
++    dev->dev_state = ELAN4_STATE_STOPPING;
++
++    elan_dev_deregister (&dev->dev_devinfo);
++
++    elan4_unconfigure_mtrr (dev);
++
++    elan4_neterr_destroy (dev);
++
++    if (dev->dev_tproc_suspend)
++      elan4mmu_unload_range (&dev->dev_ctxt, 0, dev->dev_tproc_suspend, 1 << dev->dev_pageshift[0]);
++
++    if (dev->dev_tproc_space)
++      elan4mmu_unload_range (&dev->dev_ctxt, 0, dev->dev_tproc_space,   1 << dev->dev_pageshift[0]);
++
++    if (dev->dev_routetable)
++    {
++      elan4_set_routetable (&dev->dev_ctxt, NULL);
++      elan4_free_routetable (dev, dev->dev_routetable);
++    }
++
++    for (i = 0; i < 2; i++)
++      if (dev->dev_dma_flushop[i].cq)
++          elan4_freecq (&dev->dev_ctxt, dev->dev_dma_flushop[i].cq);
++
++    /* free of the device context - and insert cache flushing command queues */
++    for (i = 0; i < COMMAND_INSERTER_CACHE_ENTRIES; i++)
++      if (dev->dev_flush_cq[i])
++          elan4_freecq (&dev->dev_ctxt, dev->dev_flush_cq[i]);
++
++    if (dev->dev_ctxt.ctxt_dev)
++      elan4_removectxt (dev, &dev->dev_ctxt);
++
++    /* stop the mainint thread */
++    spin_lock_irqsave (&dev->dev_mainint_lock, flags);
++    dev->dev_stop_threads = 1;
++
++    while (dev->dev_mainint_started && !dev->dev_mainint_stopped)
++    {
++      kcondvar_wakeupall (&dev->dev_mainint_wait, &dev->dev_mainint_lock);
++      kcondvar_wait (&dev->dev_mainint_wait, &dev->dev_mainint_lock, &flags);
++    }
++    dev->dev_mainint_started = dev->dev_mainint_stopped = 0;
++    spin_unlock_irqrestore (&dev->dev_mainint_lock, flags);
++
++    /* cancel any error interrupt timeouts */
++    if (timer_fn_queued (&dev->dev_error_timeoutid))
++      cancel_timer_fn (&dev->dev_error_timeoutid);
++
++    if (dev->dev_devinfo.dev_revision_id != PCI_REVISION_ID_ELAN4_REVA && timer_fn_queued (&dev->dev_linkerr_timeoutid))
++      cancel_timer_fn (&dev->dev_linkerr_timeoutid);
++    
++    /* reset the interrupt mask register to zero */
++    if (dev->dev_regs)
++      SET_INT_MASK (dev, 0);
++
++    for (tbl = 0; tbl < NUM_HASH_TABLES; tbl++)
++    {
++      if (dev->dev_mmuhash[tbl])
++          KMEM_FREE (dev->dev_mmuhash[tbl], dev->dev_hashsize[tbl] * sizeof (ELAN4_HASH_ENTRY));
++      if (dev->dev_mmufree[tbl])
++          KMEM_FREE (dev->dev_mmufree[tbl], dev->dev_hashsize[tbl] * sizeof (ELAN4_HASH_ENTRY *));
++      if (dev->dev_hashtable[tbl])
++          elan4_sdram_free (dev, dev->dev_hashtable[tbl], dev->dev_hashsize[tbl] * sizeof (E4_HashTableEntry));
++    }
++
++    if (dev->dev_cqamap)
++      KMEM_FREE (dev->dev_cqamap, BT_BITOUL (dev->dev_cqcount/ELAN4_CQ_PER_CQA) * sizeof (bitmap_t));
++    if (dev->dev_ctxmap)
++      KMEM_FREE (dev->dev_ctxmap, BT_BITOUL(1 << dev->dev_ctxtableshift) * sizeof (bitmap_t));
++
++    if (dev->dev_comqlowpri)
++      elan4_sdram_free (dev, dev->dev_comqlowpri,     (1 << COMMAND_RUN_QUEUE_BITS));
++    if (dev->dev_comqhighpri)
++      elan4_sdram_free (dev, dev->dev_comqhighpri,    (1 << COMMAND_RUN_QUEUE_BITS));
++    if (dev->dev_cqaddr)
++      elan4_sdram_free (dev, dev->dev_cqaddr,         sizeof (E4_CommandQueueDesc) * dev->dev_cqcount);
++    if (dev->dev_dmaqhighpri)
++      elan4_sdram_free (dev, dev->dev_dmaqhighpri,    E4_QueueSize(elan4_dmaq_highpri_size));
++    if (dev->dev_dmaqlowpri)
++      elan4_sdram_free (dev, dev->dev_dmaqlowpri,     E4_QueueSize(elan4_dmaq_lowpri_size));
++    if (dev->dev_threadqhighpri)
++      elan4_sdram_free (dev, dev->dev_threadqhighpri, E4_QueueSize(elan4_threadq_highpri_size));
++    if (dev->dev_threadqlowpri)
++      elan4_sdram_free (dev, dev->dev_threadqlowpri,  E4_QueueSize(elan4_threadq_lowpri_size));
++    if (dev->dev_interruptq)
++      elan4_sdram_free (dev, dev->dev_interruptq,     E4_QueueSize(elan4_interruptq_size));
++    
++    if (dev->dev_ctxtable)
++      elan4_sdram_free (dev, dev->dev_ctxtable,       (1 << dev->dev_ctxtableshift) * sizeof (E4_ContextControlBlock));
++    if (dev->dev_faultarea)
++      elan4_sdram_free (dev, dev->dev_faultarea,      CUN_Entries * sizeof (E4_FaultSave));
++    if (dev->dev_inputtraparea)
++      elan4_sdram_free (dev, dev->dev_inputtraparea,  sizeof (E4_IprocTrapState));
++
++    if (dev->dev_sdrampages[0])
++      elan4_sdram_free (dev, dev->dev_sdrampages[0],  SDRAM_PAGE_SIZE);
++    if (dev->dev_sdrampages[1])
++      elan4_sdram_free (dev, dev->dev_sdrampages[1],  SDRAM_PAGE_SIZE);
++
++    for (i = 0; i < dev->dev_sdram_numbanks; i++)
++      if (dev->dev_sdram_banks[i].b_ioaddr)
++              elan4_sdram_fini_bank (dev, &dev->dev_sdram_banks[i]);
++
++    elan4_pcifini (dev);
++
++    dev->dev_state = ELAN4_STATE_STOPPED;
++
++    if (dev->dev_ack_errors)
++        kfree(dev->dev_ack_errors);
++    if (dev->dev_dproc_timeout)
++        kfree(dev->dev_dproc_timeout);
++    if (dev->dev_cproc_timeout)
++        kfree(dev->dev_cproc_timeout);
++}
++
++static __inline__ int
++compute_arity (int lvl, unsigned n, char *arity)
++{
++    if (arity[lvl] == 0)
++    {
++      if (n <= 8)
++          arity[lvl] = n;
++      else
++          arity[lvl] = 4;
++    }
++
++    return (arity[lvl]);
++}
++
++int
++elan4_compute_position (ELAN_POSITION *pos, unsigned nodeid, unsigned numnodes, unsigned arityval)
++{
++    int i, lvl, n;
++    char arity[ELAN_MAX_LEVELS];
++
++    if (nodeid >= numnodes)
++      return -EINVAL;
++
++    for (i = 0; i < ELAN_MAX_LEVELS; i++, arityval >>= 4)
++      arity[i] = arityval & 7;
++    
++    for (lvl = 0, n = numnodes; n > compute_arity(lvl, n, arity) && lvl < ELAN_MAX_LEVELS; lvl++)
++    {
++      if ((n % arity[lvl]) != 0)
++          return -EINVAL;
++      
++      n /= arity[lvl];
++    }
++
++    if (arity[lvl] != n)
++      return -EINVAL;
++
++    for (i = 0; i <= lvl; i++)
++      pos->pos_arity[i] = arity[lvl - i];
++
++    pos->pos_nodes  = numnodes;
++    pos->pos_levels = lvl + 1;
++    pos->pos_nodeid = nodeid;
++    pos->pos_mode   = ELAN_POS_MODE_SWITCHED;
++
++    return 0;
++}
++
++int
++elan4_get_position (ELAN4_DEV *dev, ELAN_POSITION *pos)
++{
++    kmutex_lock (&dev->dev_lock);
++    *pos = dev->dev_position;
++    kmutex_unlock (&dev->dev_lock);
++
++    return (pos->pos_mode);
++}
++
++int
++elan4_set_position (ELAN4_DEV *dev, ELAN_POSITION *pos)
++{
++    int forceLocal = 0;
++    int nnodes, i;
++    unsigned int *ack_errors;
++    unsigned int *dproc_timeout;
++    unsigned int *cproc_timeout;
++
++    switch (pos->pos_mode)
++    {
++    case ELAN_POS_UNKNOWN:
++      break;
++      
++    case ELAN_POS_MODE_SWITCHED:
++      if (pos->pos_levels > ELAN_MAX_LEVELS)
++          return (-EINVAL);
++      
++      for (i = 0, nnodes = 1; i < pos->pos_levels; i++)
++      {
++
++          if (pos->pos_arity[i] <= 0 || (i == 0 ? pos->pos_arity[i] > 8 : pos->pos_arity[i] >= 8))  /* allow an 8 way top-switch */
++              return (-EINVAL);
++          
++          nnodes *= pos->pos_arity[i];
++      }
++
++      if (pos->pos_nodes > nnodes || pos->pos_nodeid >= pos->pos_nodes)
++          return (-EINVAL);
++      break;
++      
++    case ELAN_POS_MODE_LOOPBACK:
++      if (pos->pos_levels != 1 || pos->pos_nodes != 1 || pos->pos_nodeid != 0 || pos->pos_arity[0] != 1)
++          return (-EINVAL);
++
++      forceLocal = 1;
++      break;
++
++    case ELAN_POS_MODE_BACKTOBACK:
++      if (pos->pos_levels != 1 || pos->pos_nodes != 2 || pos->pos_nodeid >= 2 || pos->pos_arity[0] != 2)
++          return (-EINVAL);
++
++      forceLocal = (pos->pos_nodeid == 0);
++      break;
++
++    default:
++      return (-EINVAL);
++    }
++
++    ack_errors = kmalloc(pos->pos_nodes * sizeof(unsigned int), GFP_KERNEL);
++    if (!ack_errors)
++      return (-EINVAL);
++    memset(ack_errors, 0, pos->pos_nodes * sizeof(unsigned int));
++    dproc_timeout = kmalloc(pos->pos_nodes * sizeof(unsigned int), GFP_KERNEL);
++    if (!dproc_timeout) 
++    {
++        kfree(ack_errors);
++        return (-EINVAL);
++    }
++    memset(dproc_timeout, 0, pos->pos_nodes * sizeof(unsigned int));
++    cproc_timeout = kmalloc(pos->pos_nodes * sizeof(unsigned int), GFP_KERNEL);
++    if (!cproc_timeout)
++    {
++        kfree(ack_errors);
++        kfree(dproc_timeout);
++        return (-EINVAL);
++    }
++    memset(cproc_timeout, 0, pos->pos_nodes * sizeof(unsigned int));
++      
++    kmutex_lock (&dev->dev_lock);
++    dev->dev_position = *pos;
++    dev->dev_ack_errors = ack_errors;
++    dev->dev_dproc_timeout = dproc_timeout;
++    dev->dev_cproc_timeout = cproc_timeout;
++    spin_lock_init(&dev->dev_error_routes_lock);
++
++    if (forceLocal)
++      write_reg32 (dev, LinkContSettings, read_reg32 (dev, LinkContSettings) | LCONT_FORCE_COMMSCLK_LOCAL);
++    else
++      write_reg32 (dev, LinkContSettings, read_reg32 (dev, LinkContSettings) & ~LCONT_FORCE_COMMSCLK_LOCAL);
++
++    pioflush_reg (dev);
++    kmutex_unlock (&dev->dev_lock);
++
++    return (0);
++}
++
++void
++elan4_get_params (ELAN4_DEV *dev, ELAN_PARAMS *params, unsigned short *mask)
++{
++    kmutex_lock (&dev->dev_lock);
++
++    *mask = dev->dev_devinfo.dev_params_mask;
++    memcpy (params, &dev->dev_devinfo.dev_params, sizeof (ELAN_PARAMS));
++    
++    kmutex_unlock (&dev->dev_lock);
++}
++
++void
++elan4_set_params (ELAN4_DEV *dev, ELAN_PARAMS *params, unsigned short mask)
++{     
++    int i;
++
++    kmutex_lock (&dev->dev_lock);
++    for (i = 0; i < ELAN4_PARAM_COUNT; i++)
++      if (mask & (1 << i))
++          dev->dev_devinfo.dev_params.values[i] = params->values[i];
++    
++    dev->dev_devinfo.dev_params_mask |= mask;
++    kmutex_unlock (&dev->dev_lock);
++}
++
++
++EXPORT_SYMBOL(elan4_get_position);
++EXPORT_SYMBOL(elan4_set_position);
++
++EXPORT_SYMBOL(elan4_queue_haltop);
++EXPORT_SYMBOL(elan4_queue_dma_flushop);
++EXPORT_SYMBOL(elan4_queue_mainintop);
++
++EXPORT_SYMBOL(elan4_insertctxt);
++EXPORT_SYMBOL(elan4_removectxt);
++
++EXPORT_SYMBOL(elan4_attach_filter);
++EXPORT_SYMBOL(elan4_detach_filter);
++EXPORT_SYMBOL(elan4_set_filter);
++EXPORT_SYMBOL(elan4_set_routetable);
++
++EXPORT_SYMBOL(elan4_alloccq);
++EXPORT_SYMBOL(elan4_freecq);
++EXPORT_SYMBOL(elan4_restartcq);
++
++EXPORT_SYMBOL(elan4_flush_icache);
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan4/device_Linux.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan4/device_Linux.c   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan4/device_Linux.c        2005-06-01 23:12:54.606438040 -0400
+@@ -0,0 +1,2625 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: device_Linux.c,v 1.74.6.9 2005/01/18 14:44:11 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/device_Linux.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <qsnet/kthread.h>
++#include <qsnet/kpte.h>
++
++#include <asm/io.h>
++#include <asm/irq.h>
++#ifdef CONFIG_MTRR
++#include <asm/mtrr.h>
++#endif
++
++#include <linux/init.h>
++#include <linux/pci.h>
++#include <linux/module.h>
++#include <linux/reboot.h>
++#include <linux/notifier.h>
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
++#include <linux/wrapper.h>
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,23)
++typedef void irqreturn_t;
++#endif
++#       define IRQ_NONE
++#       define IRQ_HANDLED
++#endif
++
++#include <elan4/debug.h>
++#include <elan4/device.h>
++#include <elan4/user.h>
++#include <elan4/ioctl.h>
++#include <elan4/intcookie.h>
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
++#error please use a 2.4.0 series kernel or newer
++#endif
++
++
++#if defined(LINUX_SPARC) || defined(LINUX_PPC64)
++#define __io_remap_page_range(from,offset,size,prot)  remap_page_range(from,offset,size,prot)
++#define __remap_page_range(from,offset,size,prot)     remap_page_range(from,offset,size,prot)
++#elif defined(NO_RMAP)
++#define __io_remap_page_range(from,offset,size,prot)  io_remap_page_range(from,offset,size,prot)
++#define __remap_page_range(from,offset,size,prot)     remap_page_range(from,offset,size,prot)
++#else
++#define __io_remap_page_range(from,offset,size,prot)  io_remap_page_range(vma,from,offset,size,prot)
++#define __remap_page_range(from,offset,size,prot)     remap_page_range(vma,from,offset,size,prot)
++#endif
++
++#ifndef pgprot_noncached
++static inline pgprot_t pgprot_noncached(pgprot_t _prot)
++{
++      unsigned long prot = pgprot_val(_prot);
++#if defined(__powerpc__)
++      prot |= _PAGE_NO_CACHE | _PAGE_GUARDED;
++#elif defined(__sparc__)
++      prot &= ~(_PAGE_CACHE);
++      prot |= _PAGE_IE;
++#endif
++
++      return __pgprot(prot);
++}
++#endif
++
++#ifndef pgprot_writecombine
++static inline pgprot_t pgprot_writecombine (pgprot_t _prot)
++{
++    return _prot;
++}
++#endif
++
++#define ELAN4_DRIVER_VERSION          0x103           /* 16 bit value */
++
++/*
++ * Function prototypes.
++ */
++static int       elan4_attach_device (int instance, struct pci_dev *pdev);
++static void      elan4_detach_device (ELAN4_DEV *dev);
++
++static int       elan4_open (struct inode *inode, struct file *file);
++static int       elan4_release(struct inode *inode, struct file *file);
++static int       elan4_ioctl (struct inode *inode, struct file *file, 
++                              unsigned int cmd, unsigned long arg);
++static int       elan4_mmap (struct file *file, struct vm_area_struct *vm_area);
++
++static irqreturn_t elan4_irq (int irq, void *arg, struct pt_regs *regs);
++
++static void        elan4_shutdown_devices(int panicing);
++
++static int      disabled;                                     /* bitmask of which devices not to start */
++unsigned int  elan4_pll_cfg      = 0;
++int           elan4_pll_div      = 31;                        /* RevC PCB */
++int           elan4_mod45disable = 0;
++static int      optimise_pci_bus   = 1;                               /* 0 => don't, 1 => if ok, 2 => always */
++static int      default_features   = 0;                               /* default values for dev_features */
++
++long long       sdram_cfg = SDRAM_STARTUP_VALUE;
++static int      sdram_cfg_lo;
++static int    sdram_cfg_hi;
++int           sdram_bank_limit;
++
++MODULE_AUTHOR("Quadrics Ltd.");
++MODULE_DESCRIPTION("Elan 4 Device Driver");
++MODULE_LICENSE("GPL");
++
++MODULE_PARM(elan4_debug, "i");
++MODULE_PARM(elan4_debug_toconsole, "i");
++MODULE_PARM(elan4_debug_tobuffer, "i");
++MODULE_PARM(elan4_debug_mmu, "i");
++MODULE_PARM(elan4_pll_cfg, "i");
++MODULE_PARM(elan4_pll_div, "i");
++MODULE_PARM(elan4_mod45disable, "i");
++MODULE_PARM(optimise_pci_bus, "i");
++MODULE_PARM(default_features, "i");
++
++MODULE_PARM(disabled, "i");
++MODULE_PARM(sdram_cfg_lo, "i");
++MODULE_PARM(sdram_cfg_hi, "i");
++MODULE_PARM(sdram_bank_limit, "i");
++
++MODULE_PARM(elan4_hash_0_size_val, "i");
++MODULE_PARM(elan4_hash_1_size_val, "i");
++MODULE_PARM(elan4_ctxt_table_shift, "i");
++MODULE_PARM(elan4_ln2_max_cqs, "i");
++MODULE_PARM(elan4_dmaq_highpri_size, "i");
++MODULE_PARM(elan4_threadq_highpri_size, "i");
++MODULE_PARM(elan4_dmaq_lowpri_size, "i");
++MODULE_PARM(elan4_threadq_lowpri_size, "i");
++MODULE_PARM(elan4_interruptq_size, "i");
++
++MODULE_PARM(elan4_mainint_punt_loops, "i");
++MODULE_PARM(elan4_mainint_resched_ticks, "i");
++
++MODULE_PARM(user_p2p_route_options, "i");
++MODULE_PARM(user_bcast_route_options, "i");
++MODULE_PARM(user_dproc_retry_count, "i");
++MODULE_PARM(user_cproc_retry_count, "i");
++
++/*
++ * Standard device entry points.
++ */
++static struct file_operations elan4_fops = {
++    ioctl:   elan4_ioctl,
++    mmap:    elan4_mmap,
++    open:    elan4_open,
++    release: elan4_release,
++};
++
++ELAN4_DEV *elan4_devices[ELAN4_MAX_CONTROLLER];
++
++#if defined(CONFIG_DEVFS_FS)
++static devfs_handle_t devfs_handle;
++#endif
++
++
++#if defined(CONFIG_PPC64) || defined(CONFIG_SPARC64) || defined(CONFIG_X86_64)
++static int
++elan4_ioctl32_cmds[] =
++{     /* /dev/elan/control */
++    ELAN4IO_DEVINFO,
++    ELAN4IO_GET_POSITION,
++    ELAN4IO_SET_POSITION,
++    ELAN4IO_GET_PARAMS,
++    ELAN4IO_SET_PARAMS,
++
++    /* /dev/elan4/user */
++    ELAN4IO_POSITION,
++    ELAN4IO_FREE,
++    ELAN4IO_ATTACH,
++    ELAN4IO_DETACH,
++    ELAN4IO_BLOCK_INPUTTER,
++
++    ELAN4IO_ADD_P2PVP,
++    ELAN4IO_ADD_BCASTVP,
++    ELAN4IO_REMOVEVP,
++    ELAN4IO_SET_ROUTE,
++    ELAN4IO_RESET_ROUTE,
++    ELAN4IO_GET_ROUTE,
++    ELAN4IO_CHECK_ROUTE,
++
++    ELAN4IO_ALLOCCQ,
++    ELAN4IO_FREECQ,
++    ELAN4IO_SETPERM32,
++    ELAN4IO_CLRPERM32,
++    ELAN4IO_TRAPSIG,
++    ELAN4IO_TRAPHANDLER32,
++    ELAN4IO_REQUIRED_MAPPINGS,
++      
++    ELAN4IO_RESUME_EPROC_TRAP,
++    ELAN4IO_RESUME_CPROC_TRAP,
++    ELAN4IO_RESUME_DPROC_TRAP,
++    ELAN4IO_RESUME_TPROC_TRAP,
++    ELAN4IO_RESUME_IPROC_TRAP,
++
++    ELAN4IO_FLUSH_ICACHE,
++
++    ELAN4IO_STOP_CTXT,
++
++    ELAN4IO_ALLOC_INTCOOKIE,
++    ELAN4IO_FREE_INTCOOKIE,
++    ELAN4IO_ARM_INTCOOKIE,
++    ELAN4IO_WAIT_INTCOOKIE,
++
++    ELAN4IO_ALLOC_TRAP_QUEUES,
++    ELAN4IO_NETERR_MSG,
++    ELAN4IO_NETERR_TIMER,
++    ELAN4IO_NETERR_FIXUP,
++
++    ELAN4IO_DUMPCQ32,
++};
++
++static int      elan4_ioctl32 (unsigned int fd, unsigned int cmd, 
++                             unsigned long arg, struct file *file);
++#endif
++
++/*
++ * Standard device entry points.
++ */
++#if defined(CONFIG_DUMP) || defined(CONFIG_DUMP_MODULE)
++
++#include <linux/dump.h>
++
++static int
++elan4_dump_event (struct notifier_block *self, unsigned long event, void *buffer)
++{
++    if (event == DUMP_BEGIN)
++      elan4_shutdown_devices (FALSE);
++
++    return (NOTIFY_DONE);
++}
++static struct notifier_block elan4_dump_notifier = 
++{
++    notifier_call:    elan4_dump_event,
++    priority:         0,
++};
++
++#endif
++
++static int
++elan4_reboot_event (struct notifier_block *self, unsigned long event, void *buffer)
++{
++    if ((event == SYS_RESTART || event == SYS_HALT || event == SYS_POWER_OFF))
++      elan4_shutdown_devices (0);
++
++    return (NOTIFY_DONE);
++}
++
++static struct notifier_block elan4_reboot_notifier = 
++{
++    notifier_call:    elan4_reboot_event,
++    priority:         0,
++};
++
++static int
++elan4_panic_event (struct notifier_block *self, unsigned long event, void *buffer)
++{
++    elan4_shutdown_devices (1);
++
++    return (NOTIFY_DONE);
++}
++
++static struct notifier_block elan4_panic_notifier = 
++{
++    notifier_call:    elan4_panic_event,
++    priority:         0,
++};
++
++static int __init
++elan4_init (void)
++{
++    int             err;
++    struct pci_dev *pdev;
++    int                   count;
++#if defined(__ia64)
++    int             seenRevA = 0;
++#endif
++    
++    if ((err = register_chrdev (ELAN4_MAJOR, ELAN4_NAME, &elan4_fops)) < 0)
++      return (err);
++
++#if defined(CONFIG_DEVFS_FS)
++    devfs_handle = devfs_mk_dir (NULL, "elan4", NULL);
++#endif
++
++    intcookie_init();
++    elan4_debug_init();
++    elan4_procfs_init();
++    
++#ifdef CONFIG_MPSAS
++    sas_init();
++#endif
++
++    if (sdram_cfg_lo != 0 && sdram_cfg_hi != 0)
++      sdram_cfg = (((unsigned long long) sdram_cfg_hi) << 32) | ((unsigned long long) sdram_cfg_lo);
++
++    for (count = 0, pdev = NULL; (pdev = pci_find_device(PCI_VENDOR_ID_QUADRICS, PCI_DEVICE_ID_ELAN4, pdev)) != NULL ; count++)
++    {
++#if defined(__ia64)
++      unsigned char revid;
++      
++      pci_read_config_byte (pdev, PCI_REVISION_ID, &revid);
++
++      if (revid == PCI_REVISION_ID_ELAN4_REVA && seenRevA++ != 0 && pci_find_device (PCI_VENDOR_ID_HP, 0x122e, NULL))
++      {
++          printk ("elan: only a single elan4a supported on rx2600\n");
++          continue;
++      }
++#endif
++
++      if (count < ELAN4_MAX_CONTROLLER)
++          elan4_attach_device (count, pdev);
++    }
++
++    if (count >= ELAN4_MAX_CONTROLLER)
++      printk ("elan: found %d elan4 devices - only support %d\n", count, ELAN4_MAX_CONTROLLER);
++
++#if defined(CONFIG_PPC64) || defined(CONFIG_SPARC64) || defined(CONFIG_X86_64)
++    lock_kernel();
++    {
++      extern int register_ioctl32_conversion(unsigned int cmd, int (*handler)(unsigned int, unsigned int, unsigned long, struct file *));
++      register int i;
++      for (i = 0; i < sizeof (elan4_ioctl32_cmds)/sizeof(elan4_ioctl32_cmds[0]); i++)
++          register_ioctl32_conversion (elan4_ioctl32_cmds[i], elan4_ioctl32);
++    }
++    unlock_kernel();
++#endif
++
++#if defined(CONFIG_DUMP) || defined(CONFIG_DUMP_MODULE)
++    register_dump_notifier (&elan4_dump_notifier);
++#endif
++    register_reboot_notifier (&elan4_reboot_notifier);
++
++#if !defined(NO_PANIC_NOTIFIER)
++    notifier_chain_register (&panic_notifier_list, &elan4_panic_notifier);
++#endif
++
++    return (0);
++}
++
++#ifdef MODULE
++static void __exit
++elan4_exit (void)
++{
++    int i;
++
++#if defined(CONFIG_PPC64) || defined(CONFIG_SPARC64) || defined(CONFIG_X86_64)
++    lock_kernel();
++    {
++      extern void unregister_ioctl32_conversion(unsigned int cmd);
++
++      for (i = 0; i < sizeof (elan4_ioctl32_cmds)/sizeof(elan4_ioctl32_cmds[0]); i++)
++          unregister_ioctl32_conversion (elan4_ioctl32_cmds[i]);
++    }
++    unlock_kernel();
++#endif
++
++#if defined(CONFIG_DUMP) || defined(CONFIG_DUMP_MODULE)
++    unregister_dump_notifier (&elan4_dump_notifier);
++#endif
++    unregister_reboot_notifier (&elan4_reboot_notifier);
++
++#if !defined(NO_PANIC_NOTIFIER)
++    notifier_chain_unregister (&panic_notifier_list, &elan4_panic_notifier);
++#endif
++
++    for (i = 0; i < ELAN4_MAX_CONTROLLER; i++)
++      if (elan4_devices[i] != NULL)
++          elan4_detach_device (elan4_devices[i]);
++    
++    elan4_procfs_fini();
++    elan4_debug_fini();
++    intcookie_fini();
++
++#if defined(CONFIG_DEVFS_FS)
++    devfs_unregister (devfs_handle);
++#endif
++
++    unregister_chrdev(ELAN4_MAJOR, ELAN4_NAME);
++}
++
++module_init (elan4_init);
++module_exit (elan4_exit);
++
++#else
++__initcall (elan4_init);
++#endif
++
++/*
++ * Minor numbers encoded as :
++ *   [5:0]    device number
++ *   [15:6]   function number
++ */
++#define ELAN4_DEVICE_MASK     0x3F
++#define ELAN4_DEVICE(inode)   (MINOR((inode)->i_rdev) & ELAN4_DEVICE_MASK)
++
++#define ELAN4_MINOR_CONTROL   0
++#define ELAN4_MINOR_MEM               1
++#define ELAN4_MINOR_USER      2
++
++#define ELAN4_MINOR_SHIFT     6
++#define ELAN4_MINOR(inode)    (MINOR((inode)->i_rdev) >> ELAN4_MINOR_SHIFT)
++
++/*
++ * Called by init_module() for each card discovered on PCI.
++ */
++static int
++elan4_attach_device (int instance, struct pci_dev *pdev)
++{
++    ELAN4_DEV *dev;
++    int res;
++
++    if ((dev = (ELAN4_DEV *) kmalloc (sizeof (ELAN4_DEV), GFP_KERNEL)) == NULL)
++      return (-ENOMEM);
++    memset (dev, 0, sizeof (ELAN4_DEV));
++
++    /* setup os dependent section of ELAN4_DEV */
++    dev->dev_instance   = instance;
++    dev->dev_osdep.pdev = pdev;
++    dev->dev_features   = default_features;
++
++    /* initialise the devinfo */
++    pci_read_config_word (dev->dev_osdep.pdev, PCI_VENDOR_ID,   &dev->dev_devinfo.dev_vendor_id);
++    pci_read_config_word (dev->dev_osdep.pdev, PCI_DEVICE_ID,   &dev->dev_devinfo.dev_device_id);
++    pci_read_config_byte (dev->dev_osdep.pdev, PCI_REVISION_ID, &dev->dev_devinfo.dev_revision_id);
++
++    dev->dev_devinfo.dev_rail                 = instance;
++    dev->dev_devinfo.dev_driver_version       = ELAN4_DRIVER_VERSION;
++    dev->dev_devinfo.dev_num_down_links_value = 0;
++
++    dev->dev_position.pos_mode = ELAN_POS_UNKNOWN;
++
++    /* initialise the data structures and map the device */
++    if ((res = elan4_initialise_device (dev)) != 0)
++    {
++      kfree (dev);
++      return res;
++    }
++
++    /* add the interrupt handler */
++    if (request_irq (pdev->irq, elan4_irq, SA_SHIRQ, "elan4", dev) != 0)
++    {
++      elan4_finalise_device (dev);
++      kfree (dev);
++      return -ENXIO;
++    }
++
++    if (pci_request_regions(dev->dev_osdep.pdev, "elan4"))
++    {
++      free_irq (dev->dev_osdep.pdev->irq, dev);
++      kfree (dev);
++      return -ENODEV;
++    }
++
++#if defined(CONFIG_DEVFS_FS)
++    {
++      char name[16];
++      
++      sprintf (name, "control%d", dev->dev_instance);
++      dev->dev_osdep.devfs_control = devfs_register(devfs_handle, name, DEVFS_FL_NONE, ELAN4_MAJOR,
++                                                    dev->dev_instance | (ELAN4_MINOR_CONTROL << ELAN4_MINOR_SHIFT), S_IFCHR | S_IRUSR | S_IWUSR, 
++                                                    &elan4_fops, NULL);
++      sprintf (name, "sdram%d", dev->dev_instance);
++      dev->dev_osdep.devfs_sdram =  devfs_register(devfs_handle, name, DEVFS_FL_NONE, ELAN4_MAJOR,
++                                                   dev->dev_instance | (ELAN4_MINOR_MEM << ELAN4_MINOR_SHIFT), S_IFCHR | S_IRUSR|S_IWUSR | S_IRGRP|S_IWGRP | S_IROTH|S_IWOTH,
++                                                   &elan4_fops, NULL);
++      sprintf (name, "user%d", dev->dev_instance);
++      dev->dev_osdep.devfs_user =  devfs_register(devfs_handle, name, DEVFS_FL_NONE, ELAN4_MAJOR,
++                                                  dev->dev_instance | (ELAN4_MINOR_USER << ELAN4_MINOR_SHIFT), S_IFCHR | S_IRUSR|S_IWUSR | S_IRGRP|S_IWGRP | S_IROTH|S_IWOTH,
++                                                  &elan4_fops, NULL);
++    }
++#endif
++
++    /* add the procfs entry */
++    elan4_procfs_device_init (dev);
++
++    /* allow the device to be referenced now */
++    elan4_devices[instance] = dev;
++
++    if ((disabled & (1 << instance)) == 0)
++    {
++      if (elan4_start_device (dev) != 0)
++      {
++          printk ("elan%d: auto-start of device failed\n", dev->dev_instance);
++
++          elan4_detach_device (dev);
++          return (-ENXIO);
++      }
++      
++      dev->dev_state = ELAN4_STATE_STARTED;
++    }
++
++#if defined (__sparc)
++    printk ("elan%d: at pci %s (irq = %s)\n", instance, pdev->slot_name, __irq_itoa(pdev->irq));
++#else
++    printk ("elan%d: at pci %s (irq = %d)\n", instance, pdev->slot_name, pdev->irq);
++#endif
++
++    return (0);
++}
++
++/*
++ * Called by cleanup_module() for each board found on PCI.
++ */
++static void
++elan4_detach_device (ELAN4_DEV *dev)
++{
++    /* stop the chip and free of resources */
++    if (dev->dev_state == ELAN4_STATE_STARTED)
++      elan4_stop_device (dev);
++    
++    elan4_devices[dev->dev_instance] = NULL;
++
++#if defined(CONFIG_DEVFS_FS)
++    devfs_unregister (dev->dev_osdep.devfs_control);
++    devfs_unregister (dev->dev_osdep.devfs_sdram);
++    devfs_unregister (dev->dev_osdep.devfs_user);
++#endif
++
++    /* release the address space */
++    pci_release_regions (dev->dev_osdep.pdev);
++
++    /* release the interrupt */
++    free_irq (dev->dev_osdep.pdev->irq, dev);
++
++    /* remove the procfs entry */
++    elan4_procfs_device_fini (dev);
++
++    /* unmap the device and finalise the data structures */
++    elan4_finalise_device (dev);
++    
++    kfree (dev);
++}
++
++/*
++ * Maintain reference counts on the device
++ */
++ELAN4_DEV *
++elan4_reference_device (int instance, int state)
++{
++    ELAN4_DEV *dev = elan4_devices[instance];
++
++    if (dev == NULL)
++      return (NULL);
++
++    kmutex_lock (&dev->dev_lock);
++
++    if ((dev->dev_state & state) == 0)
++    {
++      kmutex_unlock (&dev->dev_lock);
++      return (NULL);
++    }
++
++    dev->dev_references++;
++    kmutex_unlock (&dev->dev_lock);
++
++#ifdef MODULE
++    MOD_INC_USE_COUNT;
++#endif
++
++#ifdef CONFIG_MPSAS
++    sas_set_position(dev);
++#endif
++
++    return (dev);
++}
++
++void
++elan4_dereference_device (ELAN4_DEV *dev)
++{
++    kmutex_lock (&dev->dev_lock);
++    dev->dev_references--;
++    kmutex_unlock (&dev->dev_lock);
++
++#ifdef MODULE
++    MOD_DEC_USE_COUNT;
++#endif
++}
++
++static void
++elan4_shutdown_devices(int panicing)
++{
++    ELAN4_DEV *dev;
++    unsigned long flags;
++    register int i;
++
++    local_irq_save (flags);
++    for (i = 0; i < ELAN4_MAX_CONTROLLER; i++)
++    {
++      if ((dev = elan4_devices[i]) != NULL)
++      {
++          printk(KERN_INFO "elan%d: forcing link into reset\n", dev->dev_instance);
++
++          /* set the inputters to discard everything */
++          if (! panicing) spin_lock (&dev->dev_haltop_lock);
++
++          if (dev->dev_discard_lowpri_count++ == 0)
++              elan4_set_schedstatus (dev, 0);
++          if (dev->dev_discard_highpri_count++ == 0)
++              elan4_set_schedstatus (dev, 0);
++
++          if (! panicing) spin_unlock (&dev->dev_haltop_lock);
++
++          /* ideally we'd like to halt all the outputters too,
++           * however this will prevent the kernel comms flushing
++           * to work correctly .....
++           */
++      }
++    }
++    local_irq_restore (flags);
++}
++
++/*
++ * /dev/elan4/controlX - control device
++ *
++ */
++static int
++control_open (struct inode *inode, struct file *file)
++{
++    ELAN4_DEV       *dev = elan4_reference_device (ELAN4_DEVICE(inode), ELAN4_STATE_STOPPED | ELAN4_STATE_STARTED);
++    CONTROL_PRIVATE *pr;
++    
++    if (dev == NULL)
++      return (-ENXIO);
++    
++    if ((pr = (CONTROL_PRIVATE *) kmalloc (sizeof (CONTROL_PRIVATE), GFP_KERNEL)) == NULL)
++    {
++      elan4_dereference_device (dev);
++      
++      return (-ENOMEM);
++    }
++
++    PRINTF (DBG_USER, DBG_FILE, "control_open: dev=%p pr=%p\n", dev, pr);
++
++    pr->pr_dev           = dev;
++    pr->pr_boundary_scan = 0;
++
++    file->private_data = (void *) pr;
++
++    return (0);
++}
++
++static int
++control_release (struct inode *inode, struct file *file)
++{
++    CONTROL_PRIVATE *pr  = (CONTROL_PRIVATE *) file->private_data;
++    ELAN4_DEV       *dev = pr->pr_dev;
++
++    PRINTF (DBG_DEVICE, DBG_FILE, "control_release: pr=%p\n", pr);
++
++    //if (pr->pr_boundary_scan)
++    //    elan4_clear_boundary_scan (dev, pr);
++
++    elan4_dereference_device (dev);
++
++    kfree (pr);
++
++    return (0);
++}
++
++static int
++control_ioctl (struct inode *inode, struct file *file, 
++                   unsigned int cmd, unsigned long arg)
++{
++    CONTROL_PRIVATE *pr  = (CONTROL_PRIVATE *) file->private_data;
++
++    PRINTF (DBG_DEVICE, DBG_FILE, "control_ioctl: cmd=%x arg=%lx\n", cmd, arg);
++
++    switch (cmd)
++    {
++    case ELAN4IO_DEVINFO:
++      if (copy_to_user ((void *) arg, &pr->pr_dev->dev_devinfo, sizeof (ELAN_DEVINFO)))
++          return (-EFAULT);
++      return (0);
++
++    case ELAN4IO_GET_POSITION:
++    {
++      ELAN_POSITION pos;
++
++      elan4_get_position (pr->pr_dev, &pos);
++
++      if (copy_to_user ((void *) arg, &pos, sizeof (ELAN_POSITION)))
++          return (-EFAULT);
++
++      return (0);
++    }
++
++    case ELAN4IO_SET_POSITION:
++    {
++      ELAN_POSITION pos;
++
++      if (copy_from_user (&pos, (void *) arg, sizeof (ELAN_POSITION)))
++          return (-EFAULT);
++      
++      return (elan4_set_position (pr->pr_dev, &pos));
++    }
++
++    case ELAN4IO_OLD_GET_PARAMS:
++    {
++      ELAN_PARAMS params;
++      unsigned short mask;
++
++      elan4_get_params (pr->pr_dev, &params, &mask);
++
++      if (copy_to_user ((void *) arg, &params, sizeof (ELAN_PARAMS)))
++          return (-EFAULT);
++
++      return (0);
++    }
++
++    case ELAN4IO_OLD_SET_PARAMS:
++    {
++      ELAN_PARAMS params;
++
++      if (copy_from_user (&params, (void *) arg, sizeof (ELAN_PARAMS)))
++          return (-EFAULT);
++      
++      elan4_set_params (pr->pr_dev, &params, 3);
++      
++      return (0);
++    }
++
++    case ELAN4IO_SET_PARAMS:
++    {
++      ELAN4IO_PARAMS_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_PARAMS_STRUCT)))
++          return (-EFAULT);
++      
++      elan4_set_params (pr->pr_dev, &args.p_params, args.p_mask);
++      
++      return (0);
++    }
++
++    case ELAN4IO_GET_PARAMS:
++    {
++      ELAN4IO_PARAMS_STRUCT args;
++
++      elan4_get_params (pr->pr_dev, &args.p_params, &args.p_mask);
++
++      if (copy_to_user ((void *) arg, &args, sizeof (ELAN_PARAMS)))
++          return (-EFAULT);
++
++      return (0);
++    }
++    }
++
++    return (-EINVAL);
++}
++
++static int
++control_mmap (struct file *file, struct vm_area_struct *vma)
++{
++    CONTROL_PRIVATE *pr  = (CONTROL_PRIVATE *) file->private_data;
++    unsigned       bar = OFF_TO_BAR (vma->vm_pgoff << PAGE_SHIFT);
++    unsigned long    off = OFF_TO_OFFSET (vma->vm_pgoff << PAGE_SHIFT);
++    long           len = vma->vm_end - vma->vm_start;
++
++    PRINTF (DBG_USER, DBG_FILE, "control_mmap: pr=%p bar=%x off=%x\n", pr, bar, off);
++
++    /* check bar number and translate the standard psuedo bars */
++    switch (bar)
++    {
++    case ELAN4_BAR_SDRAM:
++    case ELAN4_BAR_REGISTERS:
++      break;
++
++    default:
++      return (-EINVAL);
++    }
++
++    if (off < 0 || (off + len) > pci_resource_len (pr->pr_dev->dev_osdep.pdev, bar))
++      return (-EINVAL);
++
++    vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
++
++    if (__io_remap_page_range (vma->vm_start, pci_resource_start (pr->pr_dev->dev_osdep.pdev, bar) + off, len, vma->vm_page_prot))
++      return (-EAGAIN);
++
++    return (0);
++}
++
++/*
++ * /dev/elan4/sdramX - sdram access device
++ */
++static void 
++mem_freepage (MEM_PRIVATE *pr, MEM_PAGE *pg)
++{
++    PRINTF (DBG_USER, DBG_MEM, "mem_freepage: pr=%p pgoff=%lx pg=%p ref=%d\n", pr, pg->pg_pgoff, pg, pg->pg_ref);
++
++    elan4_sdram_free (pr->pr_dev, pg->pg_addr, SDRAM_PAGE_SIZE);
++    kfree (pg);
++}
++
++static MEM_PAGE *
++mem_getpage (MEM_PRIVATE *pr, unsigned long pgoff)
++{
++    int       hashval = MEM_HASH (pgoff);
++    MEM_PAGE *npg = NULL;
++    MEM_PAGE *pg;
++
++    ASSERT ((pgoff & SDRAM_PGOFF_OFFSET) == 0);
++
++    PRINTF (DBG_USER, DBG_MEM, "mem_getpage: pr=%p pgoff=%lx\n", pr, pgoff);
++    
++ again:
++    spin_lock (&pr->pr_lock);
++    for (pg = pr->pr_pages[hashval]; pg; pg = pg->pg_next)
++      if (pg->pg_pgoff == pgoff)
++          break;
++    
++    if (pg != NULL)
++    {
++      PRINTF (DBG_USER, DBG_MEM, "mem_getpage: pr=%p pgoff=%lx -> found %p addr=%x\n", pr, pgoff, pg, pg->pg_addr);
++
++      pg->pg_ref++;
++      spin_unlock (&pr->pr_lock);
++
++      if (npg != NULL)                                        /* we'd raced and someone else had created */
++          mem_freepage (pr, npg);                             /* this page - so free of our new one*/
++      return (pg);
++    }
++    
++    if (npg != NULL)                                          /* didn't find the page, so inset the */
++    {                                                         /* new one we've just created */
++      npg->pg_next = pr->pr_pages[hashval];
++      pr->pr_pages[hashval] = npg;
++      
++      spin_unlock (&pr->pr_lock);
++      return (npg);
++    }
++    
++    spin_unlock (&pr->pr_lock);                                       /* drop spinlock before creating a new page */
++    
++    if ((npg = (MEM_PAGE *) kmalloc (sizeof (MEM_PAGE), GFP_KERNEL)) == NULL)
++      return (NULL);
++    
++    if ((npg->pg_addr = elan4_sdram_alloc (pr->pr_dev, SDRAM_PAGE_SIZE)) == 0)
++    {
++      kfree (npg);
++      return (NULL);
++    }
++
++#ifndef CONFIG_MPSAS
++    /* zero the page before returning it to the user */
++    elan4_sdram_zeroq_sdram (pr->pr_dev, npg->pg_addr, SDRAM_PAGE_SIZE);
++#endif
++    
++    npg->pg_pgoff = pgoff;
++    npg->pg_ref   = 1;
++    
++    /* created a new page - so have to rescan before inserting it */
++    goto again;
++}
++
++static void
++mem_droppage (MEM_PRIVATE *pr, unsigned long pgoff, int dontfree)
++{
++    MEM_PAGE **ppg;
++    MEM_PAGE  *pg;
++
++    spin_lock (&pr->pr_lock);
++    for (ppg = &pr->pr_pages[MEM_HASH(pgoff)]; *ppg; ppg = &(*ppg)->pg_next)
++      if ((*ppg)->pg_pgoff == pgoff)
++          break;
++
++    pg = *ppg;
++
++    ASSERT (*ppg != NULL);
++
++    PRINTF (DBG_USER, DBG_MEM, "mem_droppage: pr=%p pgoff=%lx pg=%p ref=%d dontfree=%d\n", pr, pgoff, (*ppg), (*ppg)->pg_ref, dontfree);
++
++    if (--pg->pg_ref == 0 && !dontfree)
++    {
++      *ppg = pg->pg_next;
++
++      mem_freepage (pr, pg);
++    }
++
++    spin_unlock (&pr->pr_lock);
++}
++
++static int
++mem_open (struct inode *inode, struct file *file)
++{
++    ELAN4_DEV   *dev = elan4_reference_device (ELAN4_DEVICE(inode), ELAN4_STATE_STARTED);
++    MEM_PRIVATE *pr;
++    register int i;
++
++    if (dev == NULL)
++      return (-ENXIO);
++
++    if ((pr = (MEM_PRIVATE *) kmalloc (sizeof (MEM_PRIVATE), GFP_KERNEL)) == NULL)
++    {
++      elan4_dereference_device (dev);
++      return (-ENOMEM);
++    }
++
++    spin_lock_init (&pr->pr_lock);
++    pr->pr_dev = dev;
++    for (i = 0; i < MEM_HASH_SIZE; i++)
++      pr->pr_pages[i] = NULL;
++
++    file->private_data = (void *) pr;
++    
++    return (0);
++}
++
++static int
++mem_release (struct inode *node, struct file *file)
++{
++    MEM_PRIVATE *pr = (MEM_PRIVATE *) file->private_data;
++    MEM_PAGE    *pg, *next;
++    int          i;
++
++    /* free off any pages that we'd allocated */
++    spin_lock (&pr->pr_lock);
++    for (i = 0; i < MEM_HASH_SIZE; i++)
++    {
++      for (pg = pr->pr_pages[i]; pg; pg = next)
++      {
++          next = pg->pg_next;
++          mem_freepage (pr, pg);
++      }
++    }
++    spin_unlock (&pr->pr_lock);
++
++    elan4_dereference_device (pr->pr_dev);
++    kfree (pr);
++
++    return (0);
++}
++
++static int
++mem_ioctl (struct inode *inode, struct file *file, 
++                unsigned int cmd, unsigned long arg)
++{
++    return (-EINVAL);
++}
++
++static void 
++mem_vma_open (struct vm_area_struct *vma)
++{
++    MEM_PRIVATE   *pr = (MEM_PRIVATE *) vma->vm_private_data;
++    unsigned long addr;
++    unsigned long pgoff;
++
++    PRINTF (DBG_USER, DBG_MEM, "mem_vma_open: vm_mm=%p start=%lx end=%lx pgoff=%lx file=%p\n",
++          vma->vm_mm, vma->vm_start, vma->vm_end, vma->vm_pgoff, vma->vm_file);
++    
++    for (addr = vma->vm_start, pgoff = vma->vm_pgoff; addr < vma->vm_end; addr += PAGE_SIZE, pgoff++)
++      mem_getpage (pr, pgoff & SDRAM_PGOFF_MASK);
++}
++
++static void
++mem_vma_close (struct vm_area_struct *vma)
++{
++    MEM_PRIVATE  *pr  = (MEM_PRIVATE *) vma->vm_private_data;
++    unsigned long addr;
++    unsigned long pgoff;
++
++    PRINTF (DBG_USER, DBG_MEM, "mem_vma_close: vm_mm=%p start=%lx end=%lx pgoff=%lx file=%p\n",
++          vma->vm_mm, vma->vm_start, vma->vm_end, vma->vm_pgoff, vma->vm_file);
++
++    /* NOTE: the call to close may not have the same vm_start/vm_end values as 
++     *       were passed into mmap()/open() - since if an partial unmap had occurred
++     *       then the vma could have been shrunk or even split.
++     *
++     *       if a the vma is split then an vma_open() will be called for the top
++     *       portion - thus causing the reference counts to become incorrect.
++     *
++     * We drop the reference to any pages we're notified about - so they get freed
++     * earlier than when the device is finally released.
++     */
++    for (pgoff = vma->vm_pgoff, addr = vma->vm_start; addr < vma->vm_end; addr += PAGE_SIZE, pgoff++)
++      mem_droppage (pr, pgoff & SDRAM_PGOFF_MASK, 0);
++}
++
++struct vm_operations_struct mem_vm_ops = {
++    open:             mem_vma_open,
++    close:            mem_vma_close,
++};
++
++static int
++mem_mmap (struct file *file, struct vm_area_struct *vma)
++{
++    MEM_PRIVATE  *pr = (MEM_PRIVATE *) file->private_data;
++    MEM_PAGE     *pg;
++    unsigned long addr;
++    unsigned long pgoff;
++
++    PRINTF (DBG_USER, DBG_MEM, "mem_mmap: vm_mm=%p start=%lx end=%lx pgoff=%lx file=%p\n",
++          vma->vm_mm, vma->vm_start, vma->vm_end, vma->vm_pgoff, file);
++
++    for (addr = vma->vm_start, pgoff = vma->vm_pgoff; addr < vma->vm_end; addr += PAGE_SIZE, pgoff++)
++    {
++      if ((pg = mem_getpage (pr, pgoff & SDRAM_PGOFF_MASK)) == NULL)
++          goto failed;
++
++      PRINTF (DBG_USER, DBG_MEM, "mem_mmap: addr %lx -> pg=%p sdram=%x+%x bar=%lx\n",
++              addr, pg, pg->pg_addr, (pgoff & SDRAM_PGOFF_OFFSET) * PAGE_SIZE,
++              pci_resource_start (pr->pr_dev->dev_osdep.pdev, ELAN4_BAR_SDRAM));
++
++      vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
++
++      if (! (pr->pr_dev->dev_features & ELAN4_FEATURE_NO_WRITE_COMBINE))
++          vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
++
++      if (__io_remap_page_range (addr, pci_resource_start (pr->pr_dev->dev_osdep.pdev, ELAN4_BAR_SDRAM) +
++                               pg->pg_addr + (pgoff & SDRAM_PGOFF_OFFSET) * PAGE_SIZE, PAGE_SIZE, vma->vm_page_prot))
++      {
++          mem_droppage (pr, pgoff & SDRAM_PGOFF_MASK, 0); /* drop our reference to this page */
++          goto failed;
++      }
++
++#if defined(conditional_schedule)
++      conditional_schedule();
++#endif
++    }
++
++    /* Don't allow these pages to be swapped out of dumped */
++    vma->vm_flags |= (VM_RESERVED | VM_IO);
++
++    vma->vm_ops          = &mem_vm_ops;
++    vma->vm_file         = file;
++    vma->vm_private_data = (void *) pr;
++
++    return (0);
++
++ failed:
++    /* free of any pages we've already allocated/referenced */
++    while (pgoff-- > vma->vm_pgoff)
++      mem_droppage (pr, pgoff & SDRAM_PGOFF_MASK, 0);
++
++    return (-ENOMEM);
++}
++
++/*
++ * /dev/elan4/userX - control device
++ *
++ */
++static inline void
++user_private_free (USER_PRIVATE *pr)
++{
++    ELAN4_DEV *dev = pr->pr_uctx->uctx_ctxt.ctxt_dev;
++
++    ASSERT (atomic_read (&pr->pr_ref) == 0);
++
++    user_free (pr->pr_uctx);
++    kfree (pr);
++
++    elan4_dereference_device (dev);
++}
++
++static void
++user_coproc_release (void *arg, struct mm_struct *mm)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) arg;
++
++    PRINTF (pr->pr_uctx, DBG_IOPROC, "user_coproc_release: ref=%d\n", atomic_read (&pr->pr_ref));
++
++    elan4mmu_invalidate_ctxt (&pr->pr_uctx->uctx_ctxt);
++
++    pr->pr_mm = NULL;
++
++    if (atomic_dec_and_test (&pr->pr_ref))
++      user_private_free (pr);
++}
++
++static void
++user_coproc_sync_range (void *arg, struct mm_struct *mm, unsigned long start, unsigned long end)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) arg;
++
++    PRINTF (pr->pr_uctx, DBG_IOPROC, "user_coproc_sync_range: start=%lx end=%lx\n", start, end);
++
++    /* XXXX: this is intended to sync the modified bit from our page tables,
++     *       into the main cpu's modified bits - however since we do not
++     *       syncronize our modified bit on a coproc_invalidate_page() call,
++     *       then it could get lost if we modify the page after the last
++     *       modification and writepage has occurred. Hence we invalidate
++     *       all translations and allow it to refault.
++     */
++
++    user_unload_main (pr->pr_uctx, start, end - start);
++}
++
++static void
++user_coproc_invalidate_range (void *arg, struct mm_struct *mm, unsigned long start, unsigned long end)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) arg;
++
++    PRINTF (pr->pr_uctx, DBG_IOPROC, "user_coproc_invalidate_range: start=%lx end=%lx\n", start, end);
++
++    user_unload_main (pr->pr_uctx, start, end - start);
++}
++
++static void
++user_coproc_update_range (void *arg, struct mm_struct *mm, unsigned long start, unsigned long end)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) arg;
++
++    PRINTF (pr->pr_uctx, DBG_IOPROC, "user_coproc_update_range: start=%lx end=%lx\n", start, end);
++
++#if defined(CONFIG_MPSAS)
++    if (((end - start) >> PAGE_SHIFT) > 16)
++      return;
++#endif
++
++    user_update_main (pr->pr_uctx, mm, start, end - start);
++}
++
++static void
++user_coproc_change_protection (void *arg, struct mm_struct *mm, unsigned long start, unsigned long end, pgprot_t newprot)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) arg;
++
++    PRINTF (pr->pr_uctx, DBG_IOPROC, "user_coproc_change_protection: start=%lx end=%lx\n", start, end);
++
++    user_unload_main (pr->pr_uctx, start, end - start);
++}
++
++static void
++user_coproc_sync_page (void *arg, struct vm_area_struct *vma, unsigned long addr)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) arg;
++
++    PRINTF (pr->pr_uctx, DBG_IOPROC, "user_coproc_sync_page: addr=%lx\n", addr);
++
++    user_unload_main (pr->pr_uctx, addr & PAGE_MASK, PAGE_SIZE);
++}
++
++static void
++user_coproc_invalidate_page (void *arg, struct vm_area_struct *vma, unsigned long addr)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) arg;
++
++    PRINTF (pr->pr_uctx, DBG_IOPROC, "user_coproc_invalidate_page: addr=%lx\n", addr);
++
++    user_unload_main (pr->pr_uctx, addr & PAGE_MASK, PAGE_SIZE);
++}
++
++static void
++user_coproc_update_page (void *arg, struct vm_area_struct *vma, unsigned long addr)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) arg;
++
++    PRINTF (pr->pr_uctx, DBG_IOPROC, "user_coproc_update_page: addr=%lx\n", addr);
++
++    user_update_main (pr->pr_uctx, vma->vm_mm, addr & PAGE_MASK, PAGE_SIZE);
++}
++
++static int
++user_open (struct inode *inode, struct file *file)
++{
++    ELAN4_DEV    *dev;
++    USER_PRIVATE *pr;
++    USER_CTXT    *uctx;
++    
++    PRINTF (DBG_USER, DBG_FILE, "user_open: mm=%p users=%d count=%d\n", current->mm,
++          atomic_read (&current->mm->mm_users), atomic_read (&current->mm->mm_count));
++
++    if ((dev = elan4_reference_device (ELAN4_DEVICE(inode), ELAN4_STATE_STARTED)) == NULL)
++      return (-ENXIO);
++    
++    if ((pr = (USER_PRIVATE *) kmalloc (sizeof (USER_PRIVATE), GFP_KERNEL)) == NULL)
++    {
++      elan4_dereference_device (dev);
++      return (-ENOMEM);
++    }
++
++    uctx = user_alloc (dev);
++
++    if (IS_ERR(uctx))
++    {
++      elan4_dereference_device (dev);
++      kfree (pr);
++
++      return PTR_ERR(uctx);
++    }
++
++    /* initialise refcnt to 2 - one for "file", one for the coproc ops */
++    atomic_set (&pr->pr_ref, 2);
++
++    pr->pr_uctx = uctx;
++    pr->pr_mm   = current->mm;
++
++    /* register a coproc callback to notify us of translation changes */
++    pr->pr_coproc.arg               = (void *) pr;
++    pr->pr_coproc.release           = user_coproc_release;
++    pr->pr_coproc.sync_range        = user_coproc_sync_range;
++    pr->pr_coproc.invalidate_range  = user_coproc_invalidate_range;
++    pr->pr_coproc.update_range      = user_coproc_update_range;
++    pr->pr_coproc.change_protection = user_coproc_change_protection;
++    pr->pr_coproc.sync_page         = user_coproc_sync_page;
++    pr->pr_coproc.invalidate_page   = user_coproc_invalidate_page;
++    pr->pr_coproc.update_page       = user_coproc_update_page;
++    
++    spin_lock (&current->mm->page_table_lock);
++    register_coproc_ops (current->mm, &pr->pr_coproc);
++    spin_unlock (&current->mm->page_table_lock);
++
++    file->private_data = (void *) pr;
++
++    return (0);
++}
++
++static int
++user_release (struct inode *inode, struct file *file)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) file->private_data;
++
++    PRINTF (pr->pr_uctx, DBG_FILE, "user_release: ref=%d\n", atomic_read (&pr->pr_ref));
++
++    if (atomic_dec_and_test (&pr->pr_ref))
++      user_private_free (pr);
++
++    return (0);
++}
++
++static int
++user_ioctl (struct inode *inode, struct file *file, 
++          unsigned int cmd, unsigned long arg)
++{
++    USER_PRIVATE *pr   = (USER_PRIVATE *) file->private_data;
++    USER_CTXT    *uctx = pr->pr_uctx;
++    int           res  = 0;
++
++    PRINTF (uctx, DBG_FILE, "user_ioctl: cmd=%x arg=%lx\n", cmd, arg);
++
++    if (current->mm != pr->pr_mm)
++      return (-EINVAL);
++    
++    switch (cmd)
++    {
++    case ELAN4IO_DEVINFO:
++      if (copy_to_user ((void *) arg, &uctx->uctx_ctxt.ctxt_dev->dev_devinfo, sizeof (ELAN_DEVINFO)))
++          return (-EFAULT);
++      return (0);
++
++    case ELAN4IO_POSITION:
++    {
++      ELAN4_DEV *dev = uctx->uctx_ctxt.ctxt_dev;
++
++      if (copy_to_user ((void *) arg, &dev->dev_position, sizeof (ELAN_POSITION)))
++          return (-EFAULT);
++      return (0);
++    }
++
++    case ELAN4IO_FREE:
++      spin_lock (&current->mm->page_table_lock);
++      if (pr->pr_mm != current->mm)
++          spin_unlock (&current->mm->page_table_lock);
++      else
++      {
++          unregister_coproc_ops (current->mm, &pr->pr_coproc);
++          spin_unlock (&current->mm->page_table_lock);
++
++          user_coproc_release (pr, current->mm);
++      }
++      return (0);
++
++    case ELAN4IO_ATTACH:
++    {
++      ELAN_CAPABILITY *cap;
++
++      if ((cap = kmalloc (sizeof (ELAN_CAPABILITY), GFP_KERNEL)) == NULL)
++          return (-ENOMEM);
++
++      if (copy_from_user (cap, (void *) arg, sizeof (ELAN_CAPABILITY)))
++          res = -EFAULT;
++      else if ((res = user_attach (uctx, cap)) == 0 && 
++               copy_to_user ((void *) arg, cap, sizeof (ELAN_CAPABILITY)))
++      {
++          user_detach (uctx, cap);
++          res = -EFAULT;
++      }
++
++      kfree (cap);
++      return (res);
++    }
++
++    case ELAN4IO_DETACH:
++    {
++      ELAN_CAPABILITY *cap;
++
++      if ((cap = kmalloc (sizeof (ELAN_CAPABILITY), GFP_KERNEL)) == NULL)
++          return (-ENOMEM);
++
++      if (copy_from_user (cap, (void *) arg, sizeof (ELAN_CAPABILITY)))
++          res = -EFAULT;
++      else
++          user_detach (uctx, cap);
++
++      kfree (cap);
++      return (res);
++    }
++
++    case ELAN4IO_BLOCK_INPUTTER:
++      user_block_inputter (uctx, arg);
++      return (0);
++
++    case ELAN4IO_ADD_P2PVP:
++    {
++      ELAN4IO_ADD_P2PVP_STRUCT *args;
++      
++      if ((args = kmalloc (sizeof (ELAN4IO_ADD_P2PVP_STRUCT), GFP_KERNEL)) == NULL)
++          return (-ENOMEM);
++
++      if (copy_from_user (args, (void *) arg, sizeof (ELAN4IO_ADD_P2PVP_STRUCT)))
++          res = -EFAULT;
++      else 
++          res = user_add_p2pvp (uctx, args->vp_process, &args->vp_capability);
++      
++      kfree (args);
++      return (res);
++    }
++
++    case ELAN4IO_ADD_BCASTVP:
++    {
++      ELAN4IO_ADD_BCASTVP_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_ADD_BCASTVP_STRUCT)))
++          return (-EFAULT);
++
++      return (user_add_bcastvp (uctx, args.vp_process, args.vp_lowvp, args.vp_highvp));
++    }
++
++    case ELAN4IO_REMOVEVP:
++      return (user_removevp (uctx, arg));
++
++    case ELAN4IO_SET_ROUTE:
++    {
++      ELAN4IO_ROUTE_STRUCT args;
++      
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_ROUTE_STRUCT)))
++          return (-EFAULT);
++
++      return (user_set_route (uctx, args.rt_process, &args.rt_route));
++    }
++
++    case ELAN4IO_RESET_ROUTE:
++    {
++      ELAN4IO_ROUTE_STRUCT args;
++      
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_ROUTE_STRUCT)))
++          return (-EFAULT);
++
++      return (user_reset_route (uctx, args.rt_process));
++    }
++
++    case ELAN4IO_GET_ROUTE:
++    {
++      ELAN4IO_ROUTE_STRUCT args;
++      
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_ROUTE_STRUCT)))
++          return (-EFAULT);
++
++      if ((res = user_get_route (uctx, args.rt_process, &args.rt_route)) == 0 &&
++          copy_to_user ((void *) arg, &args, sizeof (ELAN4IO_ROUTE_STRUCT)))
++          res = -EFAULT;
++
++      return (res);
++    }
++
++    case ELAN4IO_CHECK_ROUTE:
++    {
++      ELAN4IO_ROUTE_STRUCT args;
++      
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_ROUTE_STRUCT)))
++          return (-EFAULT);
++
++      if ((res = user_check_route (uctx, args.rt_process, &args.rt_route, &args.rt_error)) == 0 &&
++          copy_to_user ((void *) arg, &args, sizeof (ELAN4IO_ROUTE_STRUCT)))
++          res = -EFAULT;
++
++      return (res);
++    }
++      
++    case ELAN4IO_ALLOCCQ:
++    {
++      ELAN4IO_ALLOCCQ_STRUCT args;
++      USER_CQ              *ucq;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_ALLOCCQ_STRUCT)))
++          return (-EFAULT);
++      
++      ucq = user_alloccq (uctx, args.cq_size & CQ_SizeMask, args.cq_perm & CQ_PermissionMask,
++                          (args.cq_type & ELAN4IO_CQ_TYPE_REORDER) ? UCQ_REORDER : 0);
++      if (IS_ERR (ucq))
++          return PTR_ERR (ucq);
++      
++      args.cq_indx = elan4_cq2idx (ucq->ucq_cq);
++      
++      if (copy_to_user ((void *) arg, &args, sizeof (ELAN4IO_ALLOCCQ_STRUCT)))
++      {
++          user_dropcq (uctx, ucq);
++          return (-EFAULT);
++      }
++      
++      /* don't drop the reference on the cq until the context is freed,
++       * or the caller explicitly frees the cq */
++      return (0);
++    }
++      
++    case ELAN4IO_FREECQ:
++    {
++      USER_CQ *ucq;
++      unsigned indx;
++
++      if (copy_from_user (&indx, (void *) arg, sizeof (unsigned)))
++          return (-EFAULT);
++
++      if ((ucq = user_findcq (uctx, indx)) == NULL)           /* can't free unallocated cq */
++          return (-EINVAL);
++      
++      user_dropcq (uctx, ucq);                                /* drop the reference we've just taken */
++
++      if ((ucq->ucq_flags & UCQ_SYSTEM))                      /* can't free device driver cq */
++          return (-EINVAL);
++
++      user_dropcq (uctx, ucq);                                /* and the one held from the alloccq call */
++
++      return (0);
++    }
++
++    case ELAN4IO_DUMPCQ:
++    {
++      ELAN4IO_DUMPCQ_STRUCT args;
++      ELAN4_DEV *dev = uctx->uctx_ctxt.ctxt_dev;
++      USER_CQ *ucq;
++      void *buf;
++      int i;
++      
++      if (copy_from_user (&args, (void *) arg, sizeof(ELAN4IO_DUMPCQ_STRUCT)))
++          return (-EFAULT);
++
++      if ((ucq = user_findcq (uctx, args.cq_indx)) == NULL)
++          return (-EINVAL);
++
++      if (args.bufsize)
++      {
++          E4_uint32 usedBufSize = min(args.cq_size, args.bufsize);
++
++          KMEM_ALLOC (buf, void *, usedBufSize, 0);
++
++          if (buf == NULL)
++              return (-ENOMEM);
++
++          for (i=0; i<usedBufSize; i+=sizeof(int))
++              ((int *)buf)[i/sizeof(int)] = elan4_sdram_readl(dev, ucq->ucq_cq->cq_space + i);
++
++          if (copy_to_user((void *)args.buffer, buf, usedBufSize))
++          {
++              KMEM_FREE(buf, args.bufsize);
++              return (-EFAULT);
++          }
++          KMEM_FREE(buf, usedBufSize);
++          args.bufsize = usedBufSize;
++      }
++
++      args.cq_size = CQ_Size(ucq->ucq_cq->cq_size);
++      args.cq_space = ucq->ucq_cq->cq_space;
++
++
++      if (copy_to_user((void *)arg, &args, sizeof(ELAN4IO_DUMPCQ_STRUCT)))
++      {
++          return (-EFAULT);
++      }
++      
++      user_dropcq (uctx, ucq); /* drop the reference we've just taken */
++
++      return (0);
++    }
++
++    case ELAN4IO_SETPERM:
++    {
++      ELAN4IO_PERM_STRUCT args;
++      
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_PERM_STRUCT)))
++          return (-EFAULT);
++
++      return (user_setperm (uctx, args.ps_maddr, args.ps_eaddr, args.ps_len, args.ps_perm));
++    }
++
++    case ELAN4IO_CLRPERM:
++    {
++      ELAN4IO_PERM_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_PERM_STRUCT)))
++          return (-EFAULT);
++
++      user_clrperm (uctx, args.ps_eaddr, args.ps_len);
++      return (0);
++    }
++    
++    case ELAN4IO_TRAPSIG:
++    {
++      ELAN4IO_TRAPSIG_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_TRAPSIG_STRUCT)))
++          return (-EFAULT);
++
++      pr->pr_uctx->uctx_trap_pid   = current->pid;
++      pr->pr_uctx->uctx_trap_signo = args.ts_signo;
++      
++      return (0);
++    }
++    
++    case ELAN4IO_TRAPHANDLER:
++    {
++      ELAN4IO_TRAPHANDLER_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_TRAPHANDLER_STRUCT)))
++          return (-EFAULT);
++
++      return (user_trap_handler (pr->pr_uctx, (ELAN4_USER_TRAP *)args.th_trapp, args.th_nticks));
++    }
++
++    case ELAN4IO_REQUIRED_MAPPINGS:
++    {
++      ELAN4IO_REQUIRED_MAPPINGS_STRUCT args;
++      
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_REQUIRED_MAPPINGS_STRUCT)))
++          return (-EFAULT);
++
++      pr->pr_uctx->uctx_upage_addr    = args.rm_upage_addr;
++      pr->pr_uctx->uctx_trestart_addr = args.rm_trestart_addr;
++
++      return (0);
++    }
++
++    case ELAN4IO_ALLOC_TRAP_QUEUES:
++    {
++      ELAN4IO_ALLOC_TRAP_QUEUES_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_ALLOC_TRAP_QUEUES_STRUCT)))
++          return (-EFAULT);
++
++      return (user_alloc_trap_queues (uctx, args.tq_ndproc_traps, args.tq_neproc_traps, 
++                                      args.tq_ntproc_traps, args.tq_nthreads, args.tq_ndmas));
++    }
++
++    case ELAN4IO_RESUME_EPROC_TRAP:
++    {
++      ELAN4IO_RESUME_EPROC_TRAP_STRUCT args;
++      
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_RESUME_EPROC_TRAP_STRUCT)))
++          return (-EFAULT);
++
++      return (user_resume_eproc_trap (pr->pr_uctx, args.rs_addr));
++    }
++
++    case ELAN4IO_RESUME_CPROC_TRAP:
++    {
++      ELAN4IO_RESUME_CPROC_TRAP_STRUCT args;
++      
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_RESUME_CPROC_TRAP_STRUCT)))
++          return (-EFAULT);
++
++      return (user_resume_cproc_trap (pr->pr_uctx, args.rs_indx));
++    }
++
++    case ELAN4IO_RESUME_DPROC_TRAP:
++    {
++      ELAN4IO_RESUME_DPROC_TRAP_STRUCT args;
++      
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_RESUME_DPROC_TRAP_STRUCT)))
++          return (-EFAULT);
++
++      return (user_resume_dproc_trap (pr->pr_uctx, &args.rs_desc));
++    }
++
++    case ELAN4IO_RESUME_TPROC_TRAP:
++    {
++      ELAN4IO_RESUME_TPROC_TRAP_STRUCT args;
++      
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_RESUME_TPROC_TRAP_STRUCT)))
++          return (-EFAULT);
++
++      return (user_resume_tproc_trap (pr->pr_uctx, &args.rs_regs));
++    }
++
++    case ELAN4IO_RESUME_IPROC_TRAP:
++    {
++      ELAN4IO_RESUME_IPROC_TRAP_STRUCT args;
++      
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_RESUME_IPROC_TRAP_STRUCT)))
++          return (-EFAULT);
++
++      return (user_resume_iproc_trap (pr->pr_uctx, args.rs_channel, args.rs_trans, 
++                                      &args.rs_header, &args.rs_data));
++    }
++
++    case ELAN4IO_FLUSH_ICACHE:
++      elan4_flush_icache (&uctx->uctx_ctxt);
++      return (0);
++
++    case ELAN4IO_STOP_CTXT:
++      if (arg)
++          user_swapout (uctx, UCTX_USER_STOPPED);
++      else
++          user_swapin (uctx, UCTX_USER_STOPPED);
++      return (0);
++
++    case ELAN4IO_ALLOC_INTCOOKIE_TABLE:
++    {
++      ELAN_CAPABILITY *cap;
++      INTCOOKIE_TABLE *tbl;
++
++      if ((cap = kmalloc (sizeof (ELAN_CAPABILITY), GFP_KERNEL)) == NULL)
++          return (-ENOMEM);
++
++      if (copy_from_user (cap, (void *) arg, sizeof (ELAN_CAPABILITY)))
++          res = -EFAULT;
++      else
++      {
++          tbl = intcookie_alloc_table(cap);
++
++          if (tbl == NULL)
++              res = -ENOMEM;
++          else
++          {
++              /* Install the intcookie table we've just created */
++              spin_lock (&uctx->uctx_spinlock);
++              if (uctx->uctx_intcookie_table != NULL)
++                  res = -EBUSY;
++              else
++                  uctx->uctx_intcookie_table = tbl;
++              spin_unlock (&uctx->uctx_spinlock);
++              
++              /* drop the table we created if there already was one */
++              if (res != 0)
++                  intcookie_free_table (tbl);
++          }
++      }
++
++      kfree (cap);
++
++      return (res);
++    }
++
++    case ELAN4IO_FREE_INTCOOKIE_TABLE:
++    {
++      INTCOOKIE_TABLE *tbl;
++
++      spin_lock (&uctx->uctx_spinlock);
++      tbl = uctx->uctx_intcookie_table;
++      uctx->uctx_intcookie_table = NULL;
++      spin_unlock (&uctx->uctx_spinlock);
++
++      if (tbl != NULL)
++          intcookie_free_table (tbl);
++
++      return (tbl == NULL ? -EINVAL : 0);
++    }
++
++    case ELAN4IO_ALLOC_INTCOOKIE:
++    {
++      /* For backwards compatibility with the old libs (pre 1.8.0)
++       * we allocate an intcookie table on the first cookie
++       * alloc if one hasn't be created already
++       */
++      if (uctx->uctx_intcookie_table == NULL)
++      {
++          ELAN_CAPABILITY *cap;
++          INTCOOKIE_TABLE *tbl;
++          
++          if ((cap = kmalloc (sizeof (ELAN_CAPABILITY), GFP_KERNEL)) == NULL)
++              return (-ENOMEM);
++
++          /* Create a dummy capability */
++          elan_nullcap(cap);
++
++          /* Must be unique for each process on a node */
++          cap->cap_mycontext = (int) ELAN4_TASK_HANDLE();
++
++          /* Create a new intcookie table */
++          tbl = intcookie_alloc_table(cap);
++
++          /* Hang intcookie table off uctx */
++          spin_lock (&uctx->uctx_spinlock);
++          if (uctx->uctx_intcookie_table == NULL)
++          {
++              uctx->uctx_intcookie_table = tbl;
++              spin_unlock (&uctx->uctx_spinlock);
++          }
++          else
++          {
++              spin_unlock (&uctx->uctx_spinlock);
++              intcookie_free_table(tbl);
++          }
++
++          kfree(cap);
++      }
++      
++      return (intcookie_alloc (uctx->uctx_intcookie_table, arg));
++    }
++
++    case ELAN4IO_FREE_INTCOOKIE:
++      if (uctx->uctx_intcookie_table == NULL)
++          return -EINVAL;
++      else
++          return (intcookie_free (uctx->uctx_intcookie_table, arg));
++
++    case ELAN4IO_ARM_INTCOOKIE:
++      if (uctx->uctx_intcookie_table == NULL)
++          return -EINVAL;
++      else
++          return (intcookie_arm (uctx->uctx_intcookie_table, arg));
++
++    case ELAN4IO_WAIT_INTCOOKIE:
++      if (uctx->uctx_intcookie_table == NULL)
++          return -EINVAL;
++      else
++          return (intcookie_wait (uctx->uctx_intcookie_table, arg));
++
++    case ELAN4IO_FIRE_INTCOOKIE:
++    {
++      ELAN4IO_FIRECAP_STRUCT *args;
++
++      if ((args = kmalloc (sizeof (ELAN4IO_FIRECAP_STRUCT), GFP_KERNEL)) == NULL)
++          return (-ENOMEM);
++
++      if (copy_from_user (args, (void *) arg, sizeof (ELAN4IO_FIRECAP_STRUCT)))
++          res = -EFAULT;
++      else
++          res = intcookie_fire_cap (&args->fc_capability, args->fc_cookie);
++      
++      kfree (args);
++
++      return (res);
++    }
++
++    case ELAN4IO_NETERR_MSG:
++    {
++      ELAN4IO_NETERR_MSG_STRUCT args;
++      
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_NETERR_MSG_STRUCT)))
++          return (-EFAULT);
++
++      return (user_send_neterr_msg (uctx, args.nm_vp, args.nm_nctx, args.nm_retries, &args.nm_msg));
++    }
++
++    case ELAN4IO_NETERR_TIMER:
++    {
++      unsigned long ticks = ((unsigned long) arg * HZ) / 1000;
++
++      PRINTF (uctx, DBG_NETERR, "elan4_neterr_timer: arg %ld inc %ld\n", arg, ticks);
++
++      mod_timer (&uctx->uctx_neterr_timer, (jiffies + (ticks > 0 ? ticks : 1)));
++      return 0;
++    }
++              
++    case ELAN4IO_NETERR_FIXUP:
++    {
++      ELAN4IO_NETERR_FIXUP_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_NETERR_FIXUP_STRUCT)))
++          return (-EFAULT);
++
++      if (args.nf_sten)
++          return (user_neterr_sten (uctx, args.nf_vp, args.nf_cookie, args.nf_waitforeop));
++      else
++          return (user_neterr_dma  (uctx, args.nf_vp, args.nf_cookie, args.nf_waitforeop));
++    }
++    default:
++      PRINTF (uctx, DBG_FILE, "user_ioctl: invalid ioctl %x\n", cmd);
++      return (-EINVAL);
++    }
++}
++
++static void
++user_vma_open (struct vm_area_struct *vma)
++{
++    USER_PRIVATE *pr   = (USER_PRIVATE *) vma->vm_private_data;
++    USER_CTXT    *uctx = pr->pr_uctx;
++    unsigned long addr;
++    unsigned long pgoff;
++
++    PRINTF (uctx, DBG_FILE, "user_vma_open: vm_mm=%p start=%lx end=%lx pgoff=%lx file=%p\n",
++          vma->vm_mm, vma->vm_start, vma->vm_end, vma->vm_pgoff, vma->vm_file);
++
++    for (addr = vma->vm_start, pgoff = vma->vm_pgoff; addr < vma->vm_end; addr += PAGE_SIZE, pgoff++)
++      elan4_getcqa (&uctx->uctx_ctxt, pgoff);
++}
++
++static void 
++user_vma_close (struct vm_area_struct *vma)
++{
++    USER_PRIVATE *pr   = (USER_PRIVATE *) vma->vm_private_data;
++    USER_CTXT    *uctx = pr->pr_uctx;
++    unsigned long addr;
++    unsigned long pgoff;
++
++    PRINTF (uctx, DBG_FILE, "user_vma_close: vm_mm=%p start=%lx end=%lx pgoff=%lx file=%p\n",
++          vma->vm_mm, vma->vm_start, vma->vm_end, vma->vm_pgoff, vma->vm_file);
++
++    /* NOTE: the same comments apply as mem_vma_close */
++    for (addr = vma->vm_start, pgoff = vma->vm_pgoff; addr < vma->vm_end; addr += PAGE_SIZE, pgoff++)
++      if (elan4_getcqa (&uctx->uctx_ctxt, pgoff) != NULL)
++      {
++          elan4_putcqa (&uctx->uctx_ctxt, pgoff);                     /* drop the reference we've just taken */
++          elan4_putcqa (&uctx->uctx_ctxt, pgoff);                     /* and the one held by the mmap */
++      }
++}
++
++struct vm_operations_struct user_vm_ops = {
++    open:             user_vma_open,
++    close:            user_vma_close,
++};
++
++static int
++user_mmap (struct file *file, struct vm_area_struct *vma)
++{
++    USER_PRIVATE *pr    = (USER_PRIVATE *) file->private_data;
++    USER_CTXT    *uctx  = pr->pr_uctx;
++    ELAN4_DEV     *dev   = uctx->uctx_ctxt.ctxt_dev;
++    ELAN4_CQA     *cqa;
++    unsigned long addr;
++    unsigned long pgoff;
++    int           res;
++    ioaddr_t      ioaddr;
++    
++    for (addr = vma->vm_start, pgoff = vma->vm_pgoff; addr < vma->vm_end; addr += PAGE_SIZE, pgoff++)
++    {
++      switch (pgoff)
++      {
++      default:
++          PRINTF (uctx, DBG_FILE, "user_mmap: command queue %ld mapping at %lx\n",  pgoff, addr);
++          
++          if ((cqa = elan4_getcqa (&uctx->uctx_ctxt, pgoff)) == NULL)
++          {
++              res = -EINVAL;
++              goto failed;
++          }
++
++          PRINTF (uctx, DBG_FILE, "user_mmap: cqa=%p idx=%d num=%d ref=%d\n", cqa, cqa->cqa_idx, cqa->cqa_cqnum, cqa->cqa_ref);
++    
++          vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
++
++          if (! (dev->dev_features & ELAN4_FEATURE_NO_WRITE_COMBINE) && (cqa->cqa_type & CQ_Reorder) != 0)
++              vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
++
++          PRINTF (uctx, DBG_FILE, "user_mmap: remap_page_range (%lx, %lx, %lx, %lx)\n",
++                  addr, pci_resource_start (dev->dev_osdep.pdev, ELAN4_BAR_REGISTERS) + 
++                  (cqa->cqa_cqnum + dev->dev_cqoffset) * CQ_CommandMappingSize, PAGE_SIZE,
++                  vma->vm_page_prot);
++
++          /* Don't allow these pages to be swapped out of dumped */
++          vma->vm_flags |= (VM_RESERVED | VM_IO);
++
++          if (__io_remap_page_range (addr, 
++                                     pci_resource_start (dev->dev_osdep.pdev, ELAN4_BAR_REGISTERS) + 
++                                     (cqa->cqa_cqnum + dev->dev_cqoffset) * CQ_CommandMappingSize,
++                                     PAGE_SIZE, vma->vm_page_prot))
++          {
++              PRINTF (uctx, DBG_FILE, "user_mmap: remap_page_range failed\n");
++
++              elan4_putcqa (&uctx->uctx_ctxt, pgoff);
++              res = -ENOMEM;
++              goto failed;
++          }
++          break;
++          
++      case ELAN4_OFF_USER_REGS:
++          vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
++
++          /* Don't allow these pages to be swapped out of dumped */
++          vma->vm_flags |= (VM_RESERVED | VM_IO);
++
++          switch (dev->dev_devinfo.dev_revision_id)
++          {
++          case PCI_REVISION_ID_ELAN4_REVA:
++              ioaddr = pci_resource_start (dev->dev_osdep.pdev, ELAN4_BAR_REGISTERS) + ELAN4_REVA_REG_OFFSET + offsetof(E4_Registers, uRegs);
++              break;
++              
++          case PCI_REVISION_ID_ELAN4_REVB:
++              ioaddr = pci_resource_start (dev->dev_osdep.pdev, ELAN4_BAR_REGISTERS) + ELAN4_REVB_REG_OFFSET + offsetof(E4_Registers, uRegs);
++              break;
++
++          default:
++              res = -EINVAL;
++              goto failed;
++          }
++
++          PRINTF (uctx, DBG_FILE, "user_mmap: user_regs at %lx ioaddr %lx prot %lx\n",
++                  addr, ioaddr, vma->vm_page_prot.pgprot);
++
++          if (__io_remap_page_range (addr,  (ioaddr & PAGEMASK), PAGE_SIZE, vma->vm_page_prot))
++          {                     
++              res = -EAGAIN;
++              goto failed;
++          }
++
++          break;
++          
++      case ELAN4_OFF_USER_PAGE:
++          PRINTF (uctx, DBG_FILE, "user_mmap: shared user page - kaddr=%lx uaddr=%lx phys=%lx\n", 
++                  uctx->uctx_upage, addr, kmem_to_phys (uctx->uctx_upage));
++
++          /* we do not want to have this area swapped out, lock it */
++          vma->vm_flags |= VM_LOCKED;
++          
++          /* Mark the page as reserved or else the remap_page_range() doesn't remap it */
++          SetPageReserved(pte_page(*find_pte_kernel((unsigned long) uctx->uctx_upage)));
++      
++          if (__remap_page_range (addr, kmem_to_phys (uctx->uctx_upage), PAGE_SIZE, vma->vm_page_prot))
++          {
++              PRINTF (uctx, DBG_FILE, "user_mmap: remap_page_range (user_page) failed\n");
++              res = -ENOMEM;
++              goto failed;
++          }
++          break;
++          
++      case ELAN4_OFF_TPROC_TRAMPOLINE:
++          vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
++
++          PRINTF (uctx, DBG_FILE, "user_mmap: tproc trampoline - kaddr=%lx uaddr=%lx phys=%lx\n", uctx->uctx_trampoline, addr, 
++                  pci_resource_start (dev->dev_osdep.pdev, ELAN4_BAR_SDRAM) + uctx->uctx_trampoline + (addr & (SDRAM_PGOFF_OFFSET << PAGE_SHIFT)));
++
++          /* Don't allow these pages to be swapped out of dumped */
++          vma->vm_flags |= (VM_RESERVED | VM_IO);
++
++          if (__io_remap_page_range (addr, pci_resource_start (dev->dev_osdep.pdev, ELAN4_BAR_SDRAM) + 
++                                     uctx->uctx_trampoline + (addr & (SDRAM_PGOFF_OFFSET << PAGE_SHIFT)),
++                                     PAGE_SIZE, vma->vm_page_prot))
++          {
++              PRINTF (uctx, DBG_FILE, "user_mmap: remap_page_range (tproc_trampoline) failed\n");
++              res = -ENOMEM;
++              goto failed;
++          }
++          break;
++          
++      case ELAN4_OFF_DEVICE_STATS:
++          printk ("user_mmap: device_stats\n");
++          break;
++      }
++      
++    }
++
++    ASSERT (vma->vm_ops == NULL);
++    
++    /* Don't try to swap out physical pages.. */
++    vma->vm_flags |= VM_RESERVED;
++    
++    /*
++     * Don't dump addresses that are not real memory to a core file.
++     */
++    vma->vm_flags |= VM_IO;
++
++    vma->vm_ops          = &user_vm_ops;
++    vma->vm_file         = file;
++    vma->vm_private_data = (void *) pr;
++    
++    return (0);
++
++ failed:
++    for (addr -= PAGE_SIZE, pgoff--; addr >= vma->vm_start; addr -= PAGE_SIZE, pgoff--)
++      elan4_putcqa (&uctx->uctx_ctxt, pgoff);         /* drop the reference we've just taken */
++    return (res);
++}
++
++/* driver entry points */
++static int
++elan4_open (struct inode *inode, struct file *file)
++{
++    PRINTF (DBG_USER, DBG_FILE, "elan4_open: device %d minor %d file=%p\n", ELAN4_DEVICE(inode), ELAN4_MINOR(inode), file);
++    
++    switch (ELAN4_MINOR (inode))
++    {
++    case ELAN4_MINOR_CONTROL:
++      return (control_open (inode, file));
++    case ELAN4_MINOR_MEM:
++      return (mem_open (inode, file));
++    case ELAN4_MINOR_USER:
++      return (user_open (inode, file));
++    default:
++      return (-ENXIO);
++    }
++}
++
++static int
++elan4_release (struct inode *inode, struct file *file)
++{
++    PRINTF (DBG_USER, DBG_FILE, "elan4_release: device %d minor %d file=%p\n", ELAN4_DEVICE(inode), ELAN4_MINOR(inode), file);
++    
++    switch (ELAN4_MINOR (inode))
++    {
++    case ELAN4_MINOR_CONTROL:
++      return (control_release (inode, file));
++    case ELAN4_MINOR_MEM:
++      return (mem_release (inode, file));
++    case ELAN4_MINOR_USER:
++      return (user_release (inode, file));
++    default:
++      return (-ENXIO);
++    }
++}
++
++static int
++elan4_ioctl (struct inode *inode, struct file *file, 
++           unsigned int cmd, unsigned long arg)
++{
++    PRINTF (DBG_USER, DBG_FILE, "elan4_ioctl: device %d minor %d cmd %x\n", ELAN4_DEVICE(inode), ELAN4_MINOR(inode), cmd);
++    
++    switch (ELAN4_MINOR (inode))
++    {
++    case ELAN4_MINOR_CONTROL:
++      return (control_ioctl (inode, file, cmd, arg));
++    case ELAN4_MINOR_MEM:
++      return (mem_ioctl (inode, file, cmd, arg));
++    case ELAN4_MINOR_USER:
++      return (user_ioctl (inode, file, cmd, arg));
++    default:
++      return (-ENXIO);
++    }
++}
++
++#if defined(CONFIG_PPC64) || defined(CONFIG_SPARC64) || defined(CONFIG_X86_64)
++static int
++elan4_ioctl32 (unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file)
++{
++    struct inode *inode = file->f_dentry->d_inode;
++    extern int sys_ioctl (unsigned int fd, unsigned int cmd, unsigned long arg);
++
++    PRINTF (DBG_USER, DBG_FILE, "elan4_ioctl32: device %d minor %d cmd %x\n", ELAN4_DEVICE(inode), ELAN4_MINOR(inode), cmd);
++    
++    if (ELAN4_MINOR (inode) == ELAN4_MINOR_USER)
++    {
++      USER_PRIVATE *pr    = (USER_PRIVATE *) file->private_data;
++      USER_CTXT    *uctx  = pr->pr_uctx;
++
++      if (current->mm != pr->pr_mm)
++          return -EINVAL;
++      
++      switch (cmd)
++      {
++      case ELAN4IO_SETPERM32:
++      {
++          ELAN4IO_PERM_STRUCT32 args;
++          
++          if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_PERM_STRUCT32)))
++              return (-EFAULT);
++          
++          PRINTF (DBG_USER, DBG_FILE, "user_ioctl32: setperm maddr=%x eaddr=%llx len=%llxx perm=%d\n",
++                  args.ps_maddr, args.ps_eaddr,args.ps_len, args.ps_perm);
++
++          return (user_setperm (uctx, args.ps_maddr, args.ps_eaddr, args.ps_len, args.ps_perm));
++      }
++      
++      case ELAN4IO_CLRPERM32:
++      {
++          ELAN4IO_PERM_STRUCT32 args;
++          
++          if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_PERM_STRUCT32)))
++              return (-EFAULT);
++          
++          PRINTF (DBG_USER, DBG_FILE, "user_ioctl32: clrperm eaddr=%llx len=%ll\n",
++                  args.ps_eaddr, args.ps_len);
++
++          user_clrperm (uctx, args.ps_eaddr, args.ps_len);
++          return (0);
++      }
++    
++      case ELAN4IO_TRAPHANDLER32:
++      {
++          ELAN4IO_TRAPHANDLER_STRUCT32 args;
++          
++          if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_TRAPHANDLER_STRUCT32)))
++              return (-EFAULT);
++          
++          PRINTF (DBG_USER, DBG_FILE, "user_ioctl32: traphandler trapp=%x nticks=%d\n",
++                  args.th_trapp, args.th_nticks);
++
++          return (user_trap_handler (pr->pr_uctx, (ELAN4_USER_TRAP *)(unsigned long)args.th_trapp, args.th_nticks));
++      }
++      }
++    }
++
++    PRINTF (DBG_USER, DBG_FILE, "elan4_ioctl32: fd=%d cmd=%x arg=%lx file=%p\n", fd, cmd, arg, file);
++    return (sys_ioctl (fd, cmd, arg));
++}
++#endif
++
++
++
++static int
++elan4_mmap (struct file *file, struct vm_area_struct *vma)
++{
++    PRINTF (DBG_USER, DBG_FILE, "elan4_mmap: instance %d minor %d start=%lx end=%lx pgoff=%lx\n", 
++          ELAN4_DEVICE (file->f_dentry->d_inode), ELAN4_MINOR (file->f_dentry->d_inode),
++          vma->vm_start, vma->vm_end, vma->vm_pgoff);
++
++    switch (ELAN4_MINOR (file->f_dentry->d_inode))
++    {
++    case ELAN4_MINOR_CONTROL:
++      return (control_mmap (file, vma));
++    case ELAN4_MINOR_MEM:
++      return (mem_mmap (file, vma));
++    case ELAN4_MINOR_USER:
++      return (user_mmap (file, vma));
++    default:
++      return (-ENXIO);
++    }
++}
++
++void
++elan4_update_intel_p64h2 (ELAN4_DEV *dev, struct pci_dev *bridge)
++{
++    u16 cnf;
++    
++    pci_read_config_word (bridge, 0x40 /* CNF */, &cnf);
++    
++    /* We expect the CNF register to be configured as follows
++     *
++     * [8]   == 1     PMODE PCI Mode
++     * [7:6] == 2/3   PFREQ PCI Frequency (100/133)
++     * [5]   == 0     RSDIS Restreaming Disable
++     * [4:3] == 0x    PP    Prefetch Policy
++     * [2]   == 0       DTD   Delayed Transaction Depth
++     * [1:0] == 10      MDT   MaximumDelaedTransactions
++     */
++    
++    if ((cnf & (1 << 8)) == 0)
++      printk ("elan%d: strangeness - elan reports PCI-X but P64H2 reports PCI mode !\n", dev->dev_instance);
++    else if ((cnf & 0xb7) != 0x82 && (cnf & 0xb7) != 0x84 && optimise_pci_bus < 2)
++      printk ("elan%d: P64H2 CNF is not configured as expected : RSDIS=%d PP=%d DTD=%d MDT=%d\n",
++              dev->dev_instance, (cnf >> 5) & 1, (cnf >> 3) & 3, (cnf >> 2) & 1, cnf & 3);
++    else
++    {
++      switch ((cnf >> 6) & 3)
++      {
++      case 2:                                         /* PCI-X 100 */
++          pci_write_config_word (bridge, 0xfc /* PC100 */, 0x7777);
++          
++          printk ("elan%d: optimise P64H2 : setting MDT=0, DTD=1, PFC=777 for PCI-X 100\n", dev->dev_instance);
++          
++          break;
++          
++      case 3:                                         /* PCI-X 133 */
++          pci_write_config_word (bridge, 0xfe /* PC133 */, 0x7777);
++          
++          printk ("elan%d: optimise P64H2 : setting MDT=0, DTD=1, PFC=777 for PCI-X 133\n", dev->dev_instance);
++          break;
++      }
++      
++      pci_write_config_word (bridge, 0x40 /* CNF */, (cnf & 0xfff8) | 0x4);   /* DTD=1 MDT=0 */
++    }
++}
++
++int
++elan4_optimise_intel_p64h2 (ELAN4_DEV *dev, struct pci_dev *pdev)
++{
++    struct pci_bus   *bus      = pdev->bus;
++    struct pci_dev   *bridge   = bus->self;
++    unsigned int      devcount = 0;
++    u8                revision;
++    u32               ectrl;
++    struct list_head *el;
++    
++    pci_read_config_dword (pdev, PCI_ELAN_CONTROL, &ectrl);
++
++    /* We can only run in PCI-Xmode with a B1 stepping P64H2 because of P64H2 Errata 3 */
++    pci_read_config_byte (bridge, PCI_REVISION_ID, &revision);
++    if (revision < 0x04)
++    {
++      if ((ectrl & ECTRL_INITIALISATION_MODE) != Pci2_2)
++      {
++          static const char *p64h2_stepping[4] = {"UNKNOWN", "UNKNOWN", "UNKNOWN", "B0"};
++
++          printk ("elan%d: unable to use device because of P64H2 Errata 3 on\n"
++                  "       %s stepping part and running in a PCI-X slot\n", 
++                  dev->dev_instance, p64h2_stepping[revision]);
++          return -EINVAL;
++      }
++    }
++    
++    /* We can only alter the bus configuration registers if the Elan is the only device
++     * on the bus ... */
++    list_for_each (el, &bus->devices) {
++      struct pci_dev *pcip = list_entry (el, struct pci_dev, bus_list);
++
++      if (pcip == pdev || (pcip->vendor == PCI_VENDOR_ID_INTEL && pcip->device == 0x1462 /* P64H2 HOTPLUG */))
++          continue;
++          
++      devcount++;
++    }
++
++    if (devcount > 0 || !list_empty (&bus->children))
++    {
++      printk ("elan%d: unable to optimise P64H2 settings as %s%s\n", dev->dev_instance,
++              (devcount > 0) ? "more than one device on bus" :  "",
++              ! list_empty (&bus->children) ? "has child buses" : "");
++      return 0;
++    }
++
++#ifdef __ia64
++    if ((ectrl & ECTRL_INITIALISATION_MODE) == PciX100to133MHz)
++    {
++      struct pci_dev *pcip;
++      unsigned int sioh_good      = 0;
++      unsigned int sioh_downgrade = 0;
++      unsigned int snc_good       = 0;
++      unsigned int snc_downgrade  = 0;
++      
++      /* Search for the associated SIOH and SNC on ia64,
++       * if we have a C2 SIOH and a C0/C1 SNC, then we can
++       * reconfigure the P64H2 as follows:
++       *    CNF:MDT   = 0
++       *    CNF:DTD   = 1
++       *    CNF:PC133 = 7777
++       *
++       * if not, then issue a warning that down rev parts
++       * affect bandwidth.
++       */
++      for (pcip = NULL; (pcip = pci_find_device (PCI_VENDOR_ID_INTEL, 0x500, pcip)); )
++      {
++          pci_read_config_byte (pcip, PCI_REVISION_ID, &revision);
++          
++          if (revision >= 0x21)
++              snc_good++;
++          else
++          {
++              printk ("elan%d: SNC revision %x (%s)\n", dev->dev_instance, revision,
++                      revision == 0x00 ? "A0" : revision == 0x01 ? "A1" : 
++                      revision == 0x02 ? "A2" : revision == 0x03 ? "A3" :
++                      revision == 0x10 ? "B0" : revision == 0x20 ? "C0" : 
++                      revision == 0x21 ? "C1" : "UNKNOWN");
++          
++              snc_downgrade++;
++          }
++      }
++
++      for (pcip = NULL; (pcip = pci_find_device (PCI_VENDOR_ID_INTEL, 0x510, pcip)) != NULL; )
++      {
++          pci_read_config_byte (pcip, PCI_REVISION_ID, &revision);
++          
++          
++          if (revision >= 0x22)
++              sioh_good++;
++          else
++          {
++              printk ("elan%d: SIOH revsision %x (%s)\n", dev->dev_instance, revision,
++                      revision == 0x10 ? "C0" : revision == 0x20 ? "C0" : 
++                      revision == 0x21 ? "C1" : revision == 0x22 ? "C2" : "UNKNOWN");
++
++              sioh_downgrade++;
++          }
++      }
++
++      if (optimise_pci_bus < 2 && (sioh_downgrade || snc_downgrade))
++          printk ("elan%d: unable to optimise as SNC/SIOH below required C1/C2 steppings\n", dev->dev_instance);
++      else if (optimise_pci_bus < 2 && (sioh_good == 0 || snc_good == 0))
++          printk ("elan%d: unable to optimise as cannot determine SNC/SIOH revision\n", dev->dev_instance);
++      else
++          elan4_update_intel_p64h2 (dev, bridge);
++    }
++#endif
++    
++#ifdef __i386
++    if ((ectrl & ECTRL_INITIALISATION_MODE) == PciX100to133MHz)
++      elan4_update_intel_p64h2 (dev, bridge);
++#endif            
++    return 0;
++}
++
++int
++elan4_optimise_intel_pxh (ELAN4_DEV *dev, struct pci_dev *pdev)
++{
++#ifdef __i386
++    printk ("elan%d: unable to use device on this platform in 32 bit mode\n", dev->dev_instance);
++
++    return -EINVAL;
++#endif
++
++    dev->dev_features |= ELAN4_FEATURE_NO_DWORD_READ;
++
++    return 0;
++}
++
++void
++elan4_optimise_serverworks_ciobx2 (ELAN4_DEV *dev)
++{
++    struct pci_dev *pdev = dev->dev_osdep.pdev;
++    struct pci_dev *pcip;
++    unsigned char   bus;
++    unsigned int    dor;
++    
++    /* Find the CIOBX2 for our bus number */
++    for (pcip = NULL; (pcip = pci_find_device (PCI_VENDOR_ID_SERVERWORKS, 0x0101, pcip)) != NULL;)
++    {
++      pci_read_config_byte (pcip, 0x44 /* BUSNUM */, &bus);
++      
++      if (pdev->bus->number == bus)
++      {
++          printk ("elan%d: optimise CIOBX2 : setting DOR to disable read pipe lining\n", dev->dev_instance);
++
++          pci_read_config_dword (pcip, 0x78 /* DOR */, &dor);
++          pci_write_config_dword (pcip, 0x78 /* DOR */, dor | (1 << 16));
++      }
++    }
++}
++
++int
++elan4_optimise_bus (ELAN4_DEV *dev)
++{
++    struct pci_dev *pdev = dev->dev_osdep.pdev;
++
++    if (pdev->bus && pdev->bus->self) 
++    {
++      struct pci_dev *bridge = pdev->bus->self;
++      
++      if (bridge->vendor == PCI_VENDOR_ID_INTEL && bridge->device == 0x1460 /* Intel P64H2 */)
++          return elan4_optimise_intel_p64h2 (dev, pdev);
++
++      if ((bridge->vendor == PCI_VENDOR_ID_INTEL && bridge->device == 0x0329) /* Intel 6700PXH Fn 0 */ ||
++          (bridge->vendor == PCI_VENDOR_ID_INTEL && bridge->device == 0x032a) /* Intel 6700PXH Fn 2 */ ||
++          (bridge->vendor == PCI_VENDOR_ID_INTEL && bridge->device == 0x032c) /* Intel 6702PXH */ ||
++          (bridge->vendor == PCI_VENDOR_ID_INTEL && bridge->device == 0x0320) /* Intel PXH-D */)
++          return elan4_optimise_intel_pxh (dev, pdev);
++    }
++
++    if (pci_find_device (PCI_VENDOR_ID_HP, 0x122e, NULL) != NULL)             /* on HP ZX1 set the relaxed ordering  */
++      dev->dev_pteval = PTE_RelaxedOrder;                                     /* bit to get better DMA bandwidth. */
++
++    if (pci_find_device (PCI_VENDOR_ID_SERVERWORKS, 0x0101, NULL) != NULL)    /* ServerWorks CIOBX2 */
++      elan4_optimise_serverworks_ciobx2 (dev);
++
++    return 0;
++}
++
++int
++elan4_pciinit (ELAN4_DEV *dev)
++{
++    int res;
++    u32 value;
++    u16 command;
++    u8 cacheline;
++    unsigned long flags;
++
++    if (optimise_pci_bus && (res = elan4_optimise_bus (dev)) <0)
++      return (res);
++
++    if ((res = pci_enable_device (dev->dev_osdep.pdev)) < 0)
++      return (res);
++
++    pci_read_config_dword (dev->dev_osdep.pdev, PCI_ELAN_CONTROL, &value);
++    if ((value & ECTRL_INITIALISATION_MODE) == Pci2_2)
++      printk ("elan%d: is an elan4%c (PCI-2.2)\n", dev->dev_instance, 'a' + dev->dev_devinfo.dev_revision_id);
++    else
++    {
++      switch (value & ECTRL_INITIALISATION_MODE)
++      {
++      case PciX50To66MHz:
++          printk ("elan%d: is an elan4%c (PCI-X 50-66)\n", dev->dev_instance, 'a' + dev->dev_devinfo.dev_revision_id);
++          break;
++          
++      case PciX66to100MHz:
++          printk ("elan%d: is an elan4%c (PCI-X 66-100)\n", dev->dev_instance, 'a' + dev->dev_devinfo.dev_revision_id);
++          break;
++          
++      case PciX100to133MHz:
++          printk ("elan%d: is an elan4%c (PCI-X 100-133)\n", dev->dev_instance, 'a' + dev->dev_devinfo.dev_revision_id);
++          break;
++          
++      default:
++          printk ("elan%d: Invalid PCI-X mode\n", dev->dev_instance);
++          return (-EINVAL);
++      }
++    }
++
++    /* initialise the elan pll control register */
++    pci_read_config_dword (dev->dev_osdep.pdev, PCI_ELAN_PLL_CONTROL, &value);
++
++    if (elan4_pll_cfg)
++    {
++      printk ("elan%d: setting pll control to %08x\n", dev->dev_instance, elan4_pll_cfg);
++
++      pci_write_config_dword (dev->dev_osdep.pdev, PCI_ELAN_PLL_CONTROL, elan4_pll_cfg);
++    }
++    else
++    {
++      if (dev->dev_devinfo.dev_revision_id == PCI_REVISION_ID_ELAN4_REVA)
++          pci_write_config_dword (dev->dev_osdep.pdev, PCI_ELAN_PLL_CONTROL, 
++                                  (value & ~ECTRL_SYS_CLOCK_RATIO_MASK) | ECTRL_SYS_CLOCK_RATIO_4_3);
++      else
++          pci_write_config_dword (dev->dev_osdep.pdev, PCI_ELAN_PLL_CONTROL, 
++                                  (value & ~ECTRL_SYS_CLOCK_RATIO_MASK) | ECTRL_SYS_CLOCK_RATIO_6_5 | SysPll_FeedForwardISel0 | SysPll_FeedForwardISel1);
++    } 
++
++    /* initialise the elan control register */
++    pci_read_config_dword (dev->dev_osdep.pdev, PCI_ELAN_CONTROL, &value);
++
++    value = ((15 << ECTRL_IPROC_HIGH_PRI_TIME_SHIFT) |
++           (15 << ECTRL_OTHER_HIGH_PRI_TIME_SHIFT) |
++           (value & ECTRL_28_NOT_30_BIT_LOCAL_BAR) |
++           (dev->dev_topaddrmode ? ECTRL_ExtraMasterAddrBits : 0) |
++           ECTRL_ENABLE_LATENCY_RESET | 
++           ECTRL_ENABLE_WRITEBURSTS | 
++           ECTRL_ENABLE_2_2READBURSTS);
++
++#ifdef LINUX_SPARC
++    value &= ~(ECTRL_ENABLE_LATENCY_RESET | ECTRL_ENABLE_WRITEBURSTS);
++#endif
++
++    pci_write_config_dword (dev->dev_osdep.pdev, PCI_ELAN_CONTROL, value | ECTRL_SOFTWARE_INTERNAL_RESET);
++
++    switch (dev->dev_devinfo.dev_revision_id)
++    {
++    case PCI_REVISION_ID_ELAN4_REVA:
++      /* Delay 10ms here if we've changed the sysclock ratio */
++      /* to allow the PLL to stabalise before proceeding */
++      udelay (10000);
++      break;
++      
++    case PCI_REVISION_ID_ELAN4_REVB:
++    {
++      unsigned char val = read_i2c (dev, I2cLedsValue);
++
++      /* On RevB we have to explicitly reset the PLLs */
++      pci_read_config_word (dev->dev_osdep.pdev, PCI_COMMAND, &command);
++
++      write_i2c (dev, I2cLedsValue, val | 0x80);
++      udelay (1000);
++
++      /* Issue the PLL counter reset and immediately inhibit all pci interaction 
++       * while the PLL is recovering. The write to the PCI_COMMAND register has 
++       * to occur within 50uS of the write to the i2c registers */
++      local_irq_save (flags);
++      write_i2c (dev, I2cLedsValue, val & ~0x80);
++      pci_write_config_word (dev->dev_osdep.pdev, PCI_COMMAND, (1 << 10) /* PCI_COMMAND_DISABLE_INT */);
++      local_irq_restore (flags);
++
++      /* Wait for the write to occur and for the PLL to regain lock */
++      udelay (20000); udelay (20000);
++
++      /* Re-enable pci interaction and clear any spurious errors deteced */
++      pci_write_config_word (dev->dev_osdep.pdev, PCI_STATUS, PCI_STATUS_DETECTED_PARITY | PCI_STATUS_SIG_SYSTEM_ERROR);
++      pci_write_config_word (dev->dev_osdep.pdev, PCI_COMMAND, command);
++      break;
++    }
++    }
++
++    pci_write_config_dword (dev->dev_osdep.pdev, PCI_ELAN_CONTROL, value);
++
++    /* Enable master accesses */
++    pci_set_master (dev->dev_osdep.pdev);
++
++    /* Verify that the memWrInvalidate bit is set */
++    pci_read_config_word (dev->dev_osdep.pdev, PCI_COMMAND, &command);
++    pci_read_config_byte (dev->dev_osdep.pdev, PCI_CACHE_LINE_SIZE, &cacheline);
++
++    if ((command & PCI_COMMAND_INVALIDATE) == 0)
++    {
++      printk ("elan%d: enable MemWrInvalidate (cacheline %d)\n",
++              dev->dev_instance, cacheline * 4);
++
++      pci_write_config_word (dev->dev_osdep.pdev, PCI_COMMAND, command | PCI_COMMAND_INVALIDATE);
++    }
++
++    return (0);
++}
++
++void
++elan4_pcifini (ELAN4_DEV *dev)
++{
++    u32 value;
++
++    pci_read_config_dword (dev->dev_osdep.pdev, PCI_ELAN_CONTROL, &value);
++    pci_write_config_dword (dev->dev_osdep.pdev, PCI_ELAN_CONTROL, value | ECTRL_SOFTWARE_INTERNAL_RESET);
++    pci_write_config_dword (dev->dev_osdep.pdev, PCI_ELAN_CONTROL, value);
++
++    pci_disable_device (dev->dev_osdep.pdev);
++}
++
++void
++elan4_pcierror (ELAN4_DEV *dev)
++{
++    struct pci_dev *pci = dev->dev_osdep.pdev;
++    u8  type;
++    u16 status, cmd;
++    u32 physlo, physhi, control;
++    
++    printk("elan%d: pci error has occurred\n", dev->dev_instance);
++
++    pci_read_config_word  (pci, PCI_STATUS,            &status);
++    pci_read_config_word  (pci, PCI_COMMAND,             &cmd);
++    pci_read_config_dword (pci, PCI_ELAN_CONTROL,      &control);
++
++    if (control & ECTRL_REC_SPLIT_COMP_MESSAGE)
++    {
++      u32 message, attr;
++      
++      pci_write_config_dword (pci, PCI_ELAN_CONTROL, control & ~ECTRL_SELECT_SPLIT_MESS_ATTR);
++      pci_read_config_dword (pci, PCI_ELAN_SPLIT_MESSAGE_VALUE, &message);
++      pci_write_config_dword (pci, PCI_ELAN_CONTROL, control | ECTRL_SELECT_SPLIT_MESS_ATTR);
++      pci_read_config_dword (pci, PCI_ELAN_SPLIT_MESSAGE_VALUE, &attr);
++
++      printk ("elan%d: pcierror - received split completion message - attr=%08x, message=%08x\n", 
++              dev->dev_instance, attr, message);
++
++      pci_write_config_dword (pci, PCI_ELAN_CONTROL, control | ECTRL_REC_SPLIT_COMP_MESSAGE); /* clear the error */
++    }
++    else
++    {
++      pci_read_config_dword (pci, PCI_ELAN_PARITY_ADDR_LO, &physlo);
++      pci_read_config_dword (pci, PCI_ELAN_PARITY_ADDR_HI, &physhi);
++      pci_read_config_byte  (pci, PCI_ELAN_PARITY_TYPE,    &type);
++      
++      printk ("elan%d: pcierror - status %x cmd %4x physaddr %08x%08x type %x\n", 
++              dev->dev_instance, status, cmd, physhi, physlo, type);
++      
++      if (status & PCI_STATUS_PARITY)
++          printk ("elan%d: parity error signalled (PERR)\n", dev->dev_instance);
++      if (status & PCI_STATUS_DETECTED_PARITY)
++          printk ("elan%d: detected parity error\n", dev->dev_instance);
++      if (status & PCI_STATUS_REC_MASTER_ABORT)
++          printk ("elan%d: received master abort\n", dev->dev_instance);
++      if (status & PCI_STATUS_REC_TARGET_ABORT)
++          printk ("elan%d: received target abort\n", dev->dev_instance);
++      if (status & PCI_STATUS_SIG_SYSTEM_ERROR)
++          printk ("elan%d: signalled SERR\n", dev->dev_instance);
++      if (status & PCI_STATUS_SIG_TARGET_ABORT)
++          printk ("elan%d: signalled target abort\n", dev->dev_instance);
++
++      pci_write_config_word (pci, PCI_STATUS, status);        /* clear the errors */
++    }
++
++    DISABLE_INT_MASK (dev, INT_PciMemErr);
++
++#ifdef notdef
++    panic ("elan%d: pcierror\n", dev->dev_instance);          /* better panic ! */
++#endif
++}
++
++static irqreturn_t
++elan4_irq (int irq, void *arg, struct pt_regs *regs)
++{
++    if (elan4_1msi0 ((ELAN4_DEV *) arg))
++          return IRQ_HANDLED;
++    else
++          return IRQ_NONE;
++}
++
++ioaddr_t
++elan4_map_device (ELAN4_DEV *dev, unsigned bar, unsigned off, unsigned size, ELAN4_MAP_HANDLE *handle)
++{
++    return (ioaddr_t) ioremap_nocache (pci_resource_start (dev->dev_osdep.pdev, bar) + off, size);
++}
++
++void
++elan4_unmap_device (ELAN4_DEV *dev, ioaddr_t ptr, unsigned size, ELAN4_MAP_HANDLE *handle)
++{
++    iounmap ((void *) ptr);
++}
++
++unsigned long
++elan4_resource_len (ELAN4_DEV *dev, unsigned bar)
++{
++    return (pci_resource_len (dev->dev_osdep.pdev, bar));
++}
++
++void
++elan4_configure_mtrr (ELAN4_DEV *dev)
++{
++#ifdef CONFIG_MTRR
++    if (! (dev->dev_features & ELAN4_FEATURE_NO_WRITE_COMBINE))
++    {
++      /* try and initialise the MTRR registers to enable write-combining */
++      dev->dev_osdep.sdram_mtrr = mtrr_add (pci_resource_start (dev->dev_osdep.pdev, ELAN4_BAR_SDRAM), 
++                                            pci_resource_len   (dev->dev_osdep.pdev, ELAN4_BAR_SDRAM),
++                                            MTRR_TYPE_WRCOMB, 1);
++      if (dev->dev_osdep.sdram_mtrr < 0)
++          printk ("elan%d: cannot configure MTRR for sdram\n", dev->dev_instance);
++      
++      if (dev->dev_devinfo.dev_revision_id == PCI_REVISION_ID_ELAN4_REVB)
++      {
++          dev->dev_osdep.regs_mtrr = mtrr_add (pci_resource_start (dev->dev_osdep.pdev, ELAN4_BAR_REGISTERS) + 
++                                               (dev->dev_cqoffset + dev->dev_cqreorder) * CQ_CommandMappingSize,
++                                               CQ_CommandMappingSize * (dev->dev_cqcount >> 1), 
++                                               MTRR_TYPE_WRCOMB, 1);
++          
++          if (dev->dev_osdep.regs_mtrr < 0)
++              printk ("elan%d: cannot configure MTRR for command ports\n", dev->dev_instance);
++      }
++    }
++#endif
++}
++
++void
++elan4_unconfigure_mtrr (ELAN4_DEV *dev)
++{
++#ifdef CONFIG_MTRR
++    if (! (dev->dev_features & ELAN4_FEATURE_NO_WRITE_COMBINE))
++    {
++      if (dev->dev_osdep.sdram_mtrr >=0 )
++          mtrr_del (dev->dev_osdep.sdram_mtrr, pci_resource_start (dev->dev_osdep.pdev, ELAN4_BAR_SDRAM), 
++                    pci_resource_len   (dev->dev_osdep.pdev, ELAN4_BAR_SDRAM));
++      
++      if (dev->dev_devinfo.dev_revision_id == PCI_REVISION_ID_ELAN4_REVB && dev->dev_osdep.regs_mtrr >= 0)
++          mtrr_del (dev->dev_osdep.regs_mtrr, 
++                    pci_resource_start (dev->dev_osdep.pdev, ELAN4_BAR_REGISTERS) + 
++                    (dev->dev_cqoffset + dev->dev_cqreorder) * CQ_CommandMappingSize,
++                    CQ_CommandMappingSize * (dev->dev_cqcount >> 1));
++    }
++#endif
++}
++
++EXPORT_SYMBOL(elan4_reference_device);
++EXPORT_SYMBOL(elan4_dereference_device);
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan4/i2c.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan4/i2c.c    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan4/i2c.c 2005-06-01 23:12:54.607437888 -0400
+@@ -0,0 +1,248 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: i2c.c,v 1.4 2004/01/07 13:37:45 jon Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/i2c.c,v $*/
++#include <qsnet/kernel.h>
++
++#include <elan4/sdram.h>
++#include <elan4/debug.h>
++#include <elan4/device.h>
++#include <elan4/commands.h>
++
++#include <elan4/i2c.h>
++#include <elan4/pci.h>
++#include <elan4/ioctl.h>
++#include <elan4/registers.h>
++
++#define I2C_POLL_LIMIT                8
++
++static int
++i2c_poll_busy (ELAN4_DEV *dev)
++{
++    int t    = 100;
++    int loop = 0;
++    volatile unsigned char val;
++
++    /* wait for any led I2C operation to finish */
++    while (((val = read_i2c (dev, I2cPortControl)) & I2cCntl_I2cPortBusy) && loop++ < I2C_POLL_LIMIT)
++    {
++      DELAY (t);
++      
++      if (t < 500000)
++          t <<= 1;
++    }
++    if (loop >= I2C_POLL_LIMIT)
++    {
++      printk ("elan%d: I2c has timed out waiting for I2cPortBusy to clear!\n", dev->dev_instance);
++      printk ("elan%d: I2cPortControl=%x I2cLedBase=%x I2cStatus=%x\n",
++              dev->dev_instance, val, read_i2c (dev, I2cLedBase), read_i2c (dev, I2cStatus));
++    }
++
++    return val;
++}
++
++static int
++i2c_poll_stopped (ELAN4_DEV *dev)
++{
++    int t    = 100;
++    int loop = 0;
++    unsigned char val=0, newval;
++
++    /* wait for any led I2C operation to finish. Must see it stopped at least twice */
++    while (!(((newval = read_i2c (dev, I2cPortControl)) & I2cCntl_I2cStopped) &&
++            (val & I2cCntl_I2cStopped)) &&
++            (loop++ < I2C_POLL_LIMIT))
++    {
++      DELAY (t);
++      
++      if (t < 500000)
++          t <<= 1;
++      val = newval;
++    }
++
++    return val;
++}
++
++int
++i2c_disable_auto_led_update (ELAN4_DEV *dev)
++{
++    spin_lock (&dev->dev_i2c_lock);
++
++    if (dev->dev_i2c_led_disabled++ == 0)
++    {
++      write_i2c (dev, I2cLedBase, read_i2c (dev, I2cLedBase) & ~I2cCntl_I2cUpdatingLedReg);
++
++      if (! (i2c_poll_stopped (dev) & I2cCntl_I2cStopped))
++      {
++          write_i2c (dev, I2cLedBase, read_i2c (dev, I2cLedBase) | I2cCntl_I2cUpdatingLedReg);
++          
++          spin_unlock (&dev->dev_i2c_lock);
++          
++          return -EAGAIN;
++      }
++      
++      write_i2c (dev, I2cStatus, read_i2c (dev, I2cStatus) & ~I2cCntl_SampleNewLedValues);
++    }
++
++    spin_unlock (&dev->dev_i2c_lock);
++
++    return 0;
++}
++
++void
++i2c_enable_auto_led_update (ELAN4_DEV *dev)
++{
++    spin_lock (&dev->dev_i2c_lock);
++    if (--dev->dev_i2c_led_disabled == 0)
++    {
++      write_i2c (dev, I2cLedBase, read_i2c (dev, I2cLedBase) | I2cCntl_I2cUpdatingLedReg);
++      write_i2c (dev, I2cStatus, read_i2c (dev, I2cStatus) | I2cCntl_SampleNewLedValues);
++    }
++
++    spin_unlock (&dev->dev_i2c_lock);
++}
++
++int
++i2c_write (ELAN4_DEV *dev, unsigned int address, unsigned int count, unsigned char *data)
++{
++    int i;
++
++    if (! (i2c_poll_busy (dev) & I2cCntl_I2cStopped))
++      return -EAGAIN;
++    
++    write_i2c (dev, I2cWrData,      I2C_WRITE_ADDR(address));
++    write_i2c (dev, I2cPortControl, I2cCntl_I2cPortWrite);
++    
++    if (i2c_poll_busy (dev) & I2cCntl_I2cPortAccFailed)
++      return -ENXIO;
++
++    for (i = 0; i < count; i++)
++    {
++      write_i2c (dev, I2cWrData, data[i]);
++      write_i2c (dev, I2cPortControl, I2cCntl_I2cPortWrite | (i == (count-1) ? I2cCntl_I2cPortGenStopBit : 0));
++    }
++
++    return 0;
++}
++
++int
++i2c_read (ELAN4_DEV *dev, unsigned int address, unsigned int count, unsigned char *data)
++{
++    int i;
++
++    if (! (i2c_poll_busy (dev) & I2cCntl_I2cStopped))
++      return -EAGAIN; /* not idle */ 
++
++    write_i2c (dev, I2cWrData,      I2C_READ_ADDR(address));
++    write_i2c (dev, I2cPortControl, I2cCntl_I2cPortWrite);
++
++    if (i2c_poll_busy (dev) & I2cCntl_I2cPortAccFailed)
++      return -ENXIO;
++    
++    for (i = 0; i < count; i++)
++    {
++      write_i2c (dev, I2cWrData, 0xff);
++      write_i2c (dev, I2cPortControl, I2cCntl_I2cPortRead | ((i == count-1) ? I2cCntl_I2cPortGenStopBit : 0));
++
++      i2c_poll_busy (dev);
++
++      data[i] = read_i2c (dev, I2cRdData);
++    }
++
++    return 0;
++}
++
++int
++i2c_writereg (ELAN4_DEV *dev, unsigned int address, unsigned int reg, unsigned int count, unsigned char *data)
++{
++    int i;
++
++    if (! (i2c_poll_busy (dev) & I2cCntl_I2cStopped))
++      return -EAGAIN; /* not idle */ 
++
++    write_i2c (dev, I2cWrData,      I2C_WRITE_ADDR(address));
++    write_i2c (dev, I2cPortControl, I2cCntl_I2cPortWrite);
++
++    if (i2c_poll_busy (dev) & I2cCntl_I2cPortAccFailed)
++      return -ENXIO;
++    
++    write_i2c (dev, I2cWrData,      reg);
++    write_i2c (dev, I2cPortControl, I2cCntl_I2cPortWrite);
++
++    if (i2c_poll_busy (dev) & I2cCntl_I2cPortAccFailed)
++      return -ENXIO;
++    
++    for (i = 0; i < count; i++)
++    {
++      write_i2c (dev, I2cWrData, data[i]);
++      write_i2c (dev, I2cPortControl, I2cCntl_I2cPortWrite | ((i == count-1) ? I2cCntl_I2cPortGenStopBit : 0));
++
++      if (i2c_poll_busy (dev) & I2cCntl_I2cPortAccFailed)
++          printk (" i2c_writereg: off %d failed\n", i);
++    }
++
++    return 0;
++}
++
++int
++i2c_readreg (ELAN4_DEV *dev, unsigned int address, unsigned int reg, unsigned int count, unsigned char *data)
++{
++    if (! (i2c_poll_busy (dev) & I2cCntl_I2cStopped))
++      return -EAGAIN; /* not idle */ 
++
++    write_i2c (dev, I2cWrData,      I2C_WRITE_ADDR(address));
++    write_i2c (dev, I2cPortControl, I2cCntl_I2cPortWrite);
++
++    if (i2c_poll_busy (dev) & I2cCntl_I2cPortAccFailed)
++      return -ENXIO;
++    
++    write_i2c (dev, I2cWrData,      reg);
++    write_i2c (dev, I2cPortControl, I2cCntl_I2cPortWrite | I2cCntl_I2cPortGenStopBit);
++
++    if (i2c_poll_busy (dev) & I2cCntl_I2cPortAccFailed)
++      return -ENXIO;
++
++    return i2c_read (dev, address, count, data);
++}
++
++int
++i2c_read_rom (ELAN4_DEV *dev, unsigned int addr, unsigned int len, unsigned char *data)
++{
++    unsigned int top = addr + len;
++    int res;
++
++    if ((res = i2c_disable_auto_led_update (dev)) == 0)
++    {
++      /* read the rom in chunks that don't span the block boundary */
++      while (addr < top)
++      {
++          unsigned int thisnob  = top - addr;
++          unsigned int blocknob = I2C_24LC16B_BLOCKSIZE - I2C_24LC16B_BLOCKOFFSET(addr);
++          
++          if (thisnob > blocknob)
++              thisnob = blocknob;
++
++          if ((res = i2c_readreg (dev, I2C_EEPROM_ADDR + I2C_24LC16B_BLOCKADDR(addr),
++                                  I2C_24LC16B_BLOCKOFFSET(addr), thisnob, data)) < 0)
++              break;
++          
++          addr += thisnob;
++          data += thisnob;
++      }
++
++      i2c_enable_auto_led_update (dev);
++    }
++    return res;
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan4/intcookie.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan4/intcookie.c      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan4/intcookie.c   2005-06-01 23:12:54.608437736 -0400
+@@ -0,0 +1,371 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: intcookie.c,v 1.14 2004/08/09 14:02:37 daniel Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/intcookie.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan4/debug.h>
++#include <elan4/types.h>
++#include <elan/capability.h>
++#include <elan4/intcookie.h>
++
++static INTCOOKIE_TABLE *intcookie_tables;
++static spinlock_t     intcookie_table_lock;
++
++/*
++ * intcookie_drop_entry:
++ *   drop the reference to a cookie held 
++ *   by the cookie table
++ */
++static void
++intcookie_drop_entry (INTCOOKIE_ENTRY *ent)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&ent->ent_lock, flags);
++    if (--ent->ent_ref != 0)
++    {
++      ent->ent_fired = ent->ent_cookie;
++      kcondvar_wakeupall (&ent->ent_wait, &ent->ent_lock);
++
++      spin_unlock_irqrestore (&ent->ent_lock, flags);
++    }
++    else
++    {
++      spin_unlock_irqrestore (&ent->ent_lock, flags);
++
++      spin_lock_destroy (&ent->ent_lock);
++      kcondvar_destroy (&ent->ent_wait);
++
++      KMEM_FREE (ent, sizeof (INTCOOKIE_ENTRY));
++    }
++}
++
++void
++intcookie_init()
++{
++    spin_lock_init (&intcookie_table_lock);
++}
++
++void
++intcookie_fini()
++{
++    spin_lock_destroy (&intcookie_table_lock);
++}
++
++INTCOOKIE_TABLE *
++intcookie_alloc_table (ELAN_CAPABILITY *cap)
++{
++    INTCOOKIE_TABLE *tbl, *ntbl;
++    ELAN_CAPABILITY *ncap;
++    
++    KMEM_ZALLOC (ntbl, INTCOOKIE_TABLE *, sizeof (INTCOOKIE_TABLE), 1);
++
++    if (ntbl == NULL)
++      return (NULL);
++
++    KMEM_ALLOC (ncap, ELAN_CAPABILITY *, ELAN_CAP_SIZE(cap), 1);
++
++    if (ncap == NULL)
++    {
++      KMEM_FREE (ntbl, sizeof (INTCOOKIE_TABLE));
++      return (NULL);
++    }
++
++    spin_lock (&intcookie_table_lock);
++    
++    for (tbl = intcookie_tables; tbl; tbl = tbl->tbl_next)
++      if (ELAN_CAP_MATCH (tbl->tbl_cap, cap) && tbl->tbl_cap->cap_mycontext == cap->cap_mycontext)
++          break;
++    
++    if (tbl != NULL)
++      tbl->tbl_ref++;
++    else
++    {
++      spin_lock_init (&ntbl->tbl_lock);
++
++      ntbl->tbl_cap     = ncap;
++      ntbl->tbl_ref     = 1;
++      ntbl->tbl_entries = NULL;
++      
++      /* Save supplied cap */
++      bcopy (cap, ncap, ELAN_CAP_SIZE(cap));
++
++      if ((ntbl->tbl_next = intcookie_tables) != NULL)
++          intcookie_tables->tbl_prev = ntbl;
++      intcookie_tables = ntbl;
++      ntbl->tbl_prev = NULL;
++    }
++    spin_unlock (&intcookie_table_lock);
++
++    if (tbl == NULL)
++      return (ntbl);
++    else
++    {
++      KMEM_FREE (ntbl, sizeof (INTCOOKIE_TABLE));
++      KMEM_FREE (ncap, ELAN_CAP_SIZE(cap));
++      return (tbl);
++    }    
++}
++
++void
++intcookie_free_table (INTCOOKIE_TABLE *tbl)
++{
++    INTCOOKIE_ENTRY *ent;
++
++    spin_lock (&intcookie_table_lock);
++    if (tbl->tbl_ref > 1)
++    {
++      tbl->tbl_ref--;
++      spin_unlock (&intcookie_table_lock);
++      return;
++    }
++    
++    if (tbl->tbl_prev)
++      tbl->tbl_prev->tbl_next = tbl->tbl_next;
++    else
++      intcookie_tables = tbl->tbl_next;
++    if (tbl->tbl_next)
++      tbl->tbl_next->tbl_prev = tbl->tbl_prev;
++    
++    spin_unlock (&intcookie_table_lock);
++    
++    /* NOTE - table no longer visible to other threads
++     *        no need to aquire tbl_lock */
++    while ((ent = tbl->tbl_entries) != NULL)
++    {
++      if ((tbl->tbl_entries = ent->ent_next) != NULL)
++          ent->ent_next->ent_prev = NULL;
++      
++      intcookie_drop_entry (ent);
++    }
++    spin_lock_destroy (&tbl->tbl_lock);
++
++    KMEM_FREE (tbl->tbl_cap, ELAN_CAP_SIZE(tbl->tbl_cap));
++    KMEM_FREE (tbl, sizeof (INTCOOKIE_TABLE));
++}
++
++int
++intcookie_alloc (INTCOOKIE_TABLE *tbl, ELAN4_INTCOOKIE cookie)
++{
++    INTCOOKIE_ENTRY *ent, *nent;
++    unsigned long flags;
++
++    KMEM_ZALLOC (nent, INTCOOKIE_ENTRY *, sizeof (INTCOOKIE_ENTRY), 1);
++
++    if (nent == NULL)
++      return (-ENOMEM);
++    
++    spin_lock_irqsave (&tbl->tbl_lock, flags);
++    for (ent = tbl->tbl_entries; ent; ent = ent->ent_next)
++      if (ent->ent_cookie == cookie)
++          break;
++
++    if (ent == NULL)
++    {
++      kcondvar_init (&nent->ent_wait);
++      spin_lock_init (&nent->ent_lock);
++
++      nent->ent_ref    = 1;
++      nent->ent_cookie = cookie;
++
++      if ((nent->ent_next = tbl->tbl_entries) != NULL)
++          tbl->tbl_entries->ent_prev = nent;
++      tbl->tbl_entries = nent;
++      nent->ent_prev = NULL;
++    }
++    spin_unlock_irqrestore (&tbl->tbl_lock, flags);
++
++    if (ent == NULL)
++      return (0);
++    else
++    {
++      KMEM_FREE (nent, sizeof (INTCOOKIE_ENTRY));
++      return (-EINVAL);
++    }
++}
++
++int
++intcookie_free (INTCOOKIE_TABLE *tbl, ELAN4_INTCOOKIE cookie)
++{
++    INTCOOKIE_ENTRY *ent;
++    unsigned long flags;
++
++    spin_lock_irqsave (&tbl->tbl_lock, flags);
++    for (ent = tbl->tbl_entries; ent; ent = ent->ent_next)
++      if (ent->ent_cookie == cookie)
++          break;
++    
++    if (ent == NULL)
++    {
++      spin_unlock_irqrestore (&tbl->tbl_lock, flags);
++      return (-EINVAL);
++    }
++
++    if (ent->ent_prev == NULL)
++      tbl->tbl_entries = ent->ent_next;
++    else
++      ent->ent_prev->ent_next = ent->ent_next;
++
++    if (ent->ent_next != NULL)
++      ent->ent_next->ent_prev = ent->ent_prev;
++    
++    spin_unlock_irqrestore (&tbl->tbl_lock, flags);
++
++    intcookie_drop_entry (ent);
++
++    return (0);
++}
++
++/*
++ * intcookie_fire_cookie:
++ *    fire the cookie - this is called from the event interrupt.
++ */
++int
++intcookie_fire (INTCOOKIE_TABLE *tbl, ELAN4_INTCOOKIE cookie)
++{
++    INTCOOKIE_ENTRY *ent;
++    unsigned long flags;
++
++    spin_lock_irqsave (&tbl->tbl_lock, flags);
++    for (ent = tbl->tbl_entries; ent; ent = ent->ent_next)
++      if (ent->ent_cookie == cookie)
++          break;
++    
++    if (ent == NULL)
++    {
++      spin_unlock_irqrestore (&tbl->tbl_lock, flags);
++      return (-EINVAL);
++    }
++          
++    spin_lock (&ent->ent_lock);
++    ent->ent_fired = cookie;
++    kcondvar_wakeupall (&ent->ent_wait, &ent->ent_lock);
++    spin_unlock (&ent->ent_lock);
++
++    spin_unlock_irqrestore (&tbl->tbl_lock, flags);
++
++    return (0);
++}    
++
++int
++intcookie_fire_cap (ELAN_CAPABILITY *cap, ELAN4_INTCOOKIE cookie)
++{
++    int res;
++    INTCOOKIE_TABLE *tbl;
++
++    spin_lock (&intcookie_table_lock);
++    
++    for (tbl = intcookie_tables; tbl; tbl = tbl->tbl_next)
++      if (ELAN_CAP_MATCH (tbl->tbl_cap, cap) && tbl->tbl_cap->cap_mycontext == cap->cap_mycontext)
++          break;
++    
++    if (tbl != NULL)
++      tbl->tbl_ref++;
++
++    spin_unlock (&intcookie_table_lock);
++
++    /* No matching table found */
++    if (tbl == NULL)
++      return (-EINVAL);
++
++    /* Fire the correct cookie */
++    res = intcookie_fire (tbl, cookie);
++
++    /* Decrement reference count (and free if necessary) */
++    intcookie_free_table (tbl);
++
++    return (res);
++}
++
++/*
++ * intcookie_wait_cookie:
++ *    deschedule on a cookie if it has not already fired.
++ *    note - if the cookie is removed from the table, then
++ *           we free it off when we're woken up.
++ */
++int
++intcookie_wait (INTCOOKIE_TABLE *tbl, ELAN4_INTCOOKIE cookie)
++{
++    INTCOOKIE_ENTRY *ent;
++    unsigned long flags;
++    int res;
++    
++    spin_lock_irqsave (&tbl->tbl_lock, flags);
++    for (ent = tbl->tbl_entries; ent; ent = ent->ent_next)
++      if (ent->ent_cookie == cookie)
++          break;
++    
++    if (ent == NULL)
++    {
++      spin_unlock_irqrestore (&tbl->tbl_lock, flags);
++      return (-EINVAL);
++    }
++
++    spin_lock (&ent->ent_lock);
++    spin_unlock (&tbl->tbl_lock);
++
++    if (ent->ent_fired != 0)
++    {
++      spin_unlock_irqrestore (&ent->ent_lock, flags);
++      return (0);
++    }
++
++    ent->ent_ref++;
++    kcondvar_waitsig (&ent->ent_wait, &ent->ent_lock, &flags);
++    
++    res = ent->ent_fired ? 0 : -EINTR;
++
++    if (--ent->ent_ref > 0)
++      spin_unlock_irqrestore (&ent->ent_lock, flags);
++    else
++    {
++      spin_unlock_irqrestore (&ent->ent_lock, flags);
++      
++      spin_lock_destroy (&ent->ent_lock);
++      kcondvar_destroy (&ent->ent_wait);
++
++      KMEM_FREE (ent, sizeof (INTCOOKIE_ENTRY));
++    }
++
++    return (res);
++}
++
++int
++intcookie_arm (INTCOOKIE_TABLE *tbl, ELAN4_INTCOOKIE cookie)
++{
++    INTCOOKIE_ENTRY *ent;
++    unsigned long flags;
++
++    spin_lock_irqsave (&tbl->tbl_lock, flags);
++    for (ent = tbl->tbl_entries; ent; ent = ent->ent_next)
++      if (ent->ent_cookie == cookie)
++          break;
++    
++    if (ent == NULL)
++    {
++      spin_unlock_irqrestore (&tbl->tbl_lock, flags);
++      return (-EINVAL);
++    }
++          
++    spin_lock (&ent->ent_lock);
++    ent->ent_fired = 0;
++    spin_unlock (&ent->ent_lock);
++
++    spin_unlock_irqrestore (&tbl->tbl_lock, flags);
++
++    return (0);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan4/Makefile
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan4/Makefile 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan4/Makefile      2005-06-01 23:12:54.608437736 -0400
+@@ -0,0 +1,31 @@
++#
++# Makefile for Quadrics QsNet
++#
++# Copyright (c) 2002-2004 Quadrics Ltd
++#
++# File: drivers/net/qsnet/elan4/Makefile
++#
++
++
++#
++
++#
++# Makefile for Quadrics QsNet
++#
++# Copyright (c) 2004 Quadrics Ltd.
++#
++# File: driver/net/qsnet/elan4/Makefile
++#
++
++list-multi            := elan4.o
++elan4-objs    := device.o i2c.o mmu.o sdram.o debug.o routetable.o trap.o user.o user_ddcq.o regions.o intcookie.o neterr.o device_Linux.o user_Linux.o procfs_Linux.o mmu_Linux.o
++export-objs           := device.o device_Linux.o mmu.o mmu_Linux.o procfs_Linux.o routetable.o sdram.o trap.o
++obj-$(CONFIG_ELAN4)   := elan4.o
++
++elan4.o : $(elan4-objs)
++      $(LD) -r -o $@ $(elan4-objs)
++
++EXTRA_CFLAGS          +=  -DDEBUG -DDEBUG_PRINTF -DDEBUG_ASSERT
++
++include $(TOPDIR)/Rules.make
++
+Index: linux-2.4.21/drivers/net/qsnet/elan4/Makefile.conf
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan4/Makefile.conf    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan4/Makefile.conf 2005-06-01 23:12:54.608437736 -0400
+@@ -0,0 +1,10 @@
++# Flags for generating QsNet Linux Kernel Makefiles
++MODNAME               =       elan4.o
++MODULENAME    =       elan4
++KOBJFILES     =       device.o i2c.o mmu.o sdram.o debug.o routetable.o trap.o user.o user_ddcq.o regions.o intcookie.o neterr.o device_Linux.o user_Linux.o procfs_Linux.o mmu_Linux.o
++EXPORT_KOBJS  =       device.o device_Linux.o mmu.o mmu_Linux.o procfs_Linux.o routetable.o sdram.o trap.o
++CONFIG_NAME   =       CONFIG_ELAN4
++SGALFC                =       
++# EXTRALINES START
++
++# EXTRALINES END
+Index: linux-2.4.21/drivers/net/qsnet/elan4/mmu.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan4/mmu.c    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan4/mmu.c 2005-06-01 23:12:54.610437432 -0400
+@@ -0,0 +1,854 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: mmu.c,v 1.29.6.2 2005/01/18 16:58:12 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/mmu.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <qsnet/kpte.h>
++
++#include <elan4/debug.h>
++#include <elan4/device.h>
++
++int elan4_debug_mmu;
++
++/* Permission table - see ELAN4 MMU documentation */
++u_char elan4_permtable[] =
++{
++   0x00, /* 0x000000 - Disable */
++   0x00, /* 0x000000 - Unused  */
++   0x01, /* 0x000001 - Local Data Read */
++   0x03, /* 0x000011 - Local Data Write */
++   0x11, /* 0x010001 - Local Read */
++   0x10, /* 0x010000 - Local Execute */
++   0x05, /* 0x000101 - Read Only */
++   0x13, /* 0x010011 - Local Write */
++   0x20, /* 0x100000 - Local Event Access */
++   0x23, /* 0x100011 - Local Event Write Ac */
++   0xa3, /* 1x100011 - Remote Ev Loc Write */
++   0xaf, /* 1x101111 - Remote All */
++   0x07, /* 0x000111 - Remote Read Only */
++   0x0d, /* 0x001101 - Remote Write Only */
++   0x0f, /* 0x001111 - Remote Read/Write */
++   0xbf, /* 1x111111 - No Fault */
++};
++
++u_char elan4_permreadonly[] = 
++{
++    PERM_Disabled,            /* PERM_Disabled */
++    PERM_Disabled,            /* PERM_Unused */
++    PERM_LocDataRead,         /* PERM_LocDataRead */
++    PERM_LocDataRead,         /* PERM_LocDataWrite */
++    PERM_LocRead,             /* PERM_LocRead */
++    PERM_LocExecute,          /* PERM_LocExecute */
++    PERM_ReadOnly,            /* PERM_ReadOnly */
++    PERM_LocRead,             /* PERM_LocWrite */
++    PERM_LocEventOnly,                /* PERM_LocEventOnly */
++    PERM_LocDataRead,         /* PERM_LocEventWrite */
++    PERM_LocDataRead,         /* PERM_RemoteEvent */
++    PERM_ReadOnly,            /* PERM_RemoteAll */
++    PERM_RemoteReadOnly,      /* PERM_RemoteReadOnly */
++    PERM_ReadOnly,            /* PERM_RemoteWriteLocRead */
++    PERM_ReadOnly,            /* PERM_DataReadWrite */
++    PERM_ReadOnly,            /* PERM_NoFault */
++};
++
++static void
++elan4mmu_synctag (ELAN4_DEV *dev, ELAN4_HASH_ENTRY *he, int tagidx)
++{
++    E4_uint64 value = (he->he_tag[tagidx] & HE_TAG_VALID) ? he->he_tag[tagidx] & (TAG_ADDRESS_MASK | TAG_CONTEXT_MASK) : INVALID_CONTEXT;
++    
++    if (he->he_next)
++      value |= ((tagidx == 0) ? 
++                ((he->he_next->he_entry >> TAG_CHAINPTR_HIGH_SHIFT) & TAG_CHAINPTR_30TO19_MASK) :
++                ((he->he_next->he_entry << TAG_CHAINPTR_LOW_SHIFT) & TAG_CHAINPTR_18TO6_MASK));
++    else if (tagidx == 0)
++      value |= TAG_CHAINPTR_30TO19_MASK;
++    
++    MPRINTF (DBG_DEVICE, 4, "elan4mmu_synctag: he=%p tagidx=%d he->he_tag=%llx -> value=%llx\n", he, tagidx, he->he_tag[tagidx], value);
++
++    elan4_sdram_writeq (dev, he->he_entry + E4MMU_TAG_OFFSET(tagidx), value);
++}
++
++static void
++elan4mmu_chain_hents (ELAN4_DEV *dev, ELAN4_HASH_ENTRY *phe, ELAN4_HASH_ENTRY *he)
++{
++    ASSERT ((elan4_sdram_readq (dev, phe->he_entry + E4MMU_TAG_OFFSET(0)) & TAG_CHAINPTR_30TO19_MASK) == TAG_CHAINPTR_30TO19_MASK);
++
++    elan4_sdram_writeq (dev, phe->he_entry + E4MMU_TAG_OFFSET(1),
++                      ((phe->he_tag[1] & (TAG_ADDRESS_MASK | TAG_CONTEXT_MASK)) | ((he->he_entry << TAG_CHAINPTR_LOW_SHIFT) & TAG_CHAINPTR_18TO6_MASK)));
++    elan4_sdram_writeq (dev, phe->he_entry + E4MMU_TAG_OFFSET(0),
++                      ((phe->he_tag[0] & (TAG_ADDRESS_MASK | TAG_CONTEXT_MASK)) | ((he->he_entry >> TAG_CHAINPTR_HIGH_SHIFT) & TAG_CHAINPTR_30TO19_MASK)));
++}
++
++static void
++elan4mmu_writepte (ELAN4_DEV *dev, ELAN4_HASH_ENTRY *he, int tagidx, int pteidx, E4_uint64 value)
++{
++    /*
++     * NOTE - we can only change a valid PTE if we're upgrading it's permissions,
++     * any other changes should have invalidated it first. */
++
++    MPRINTF (DBG_DEVICE, 4, "elan4mmu_writepte: he=%p tagidx=%d pteidx=%x value=%llx\n", he, tagidx, pteidx, (unsigned long long) value);
++
++    if (pteidx == 3)
++    {
++      elan4_sdram_writew (dev, he->he_entry + E4MMU_PTE3_WORD1_OFFSET(tagidx), (value >> 16) & 0xFFFF);
++      elan4_sdram_writew (dev, he->he_entry + E4MMU_PTE3_WORD2_OFFSET(tagidx), (value >> 32) & 0xFFFF);
++      elan4_sdram_writew (dev, he->he_entry + E4MMU_PTE3_WORD0_OFFSET(tagidx), (value >> 0)  & 0xFFFF);
++    }
++    else
++    {
++      elan4_sdram_writew (dev, he->he_entry + E4MMU_PTE_HIGH_OFFSET(tagidx, pteidx), (value >> 32) & 0xFFFF);
++      elan4_sdram_writel (dev, he->he_entry + E4MMU_PTE_LOW_OFFSET(tagidx, pteidx), value & 0xFFFFFFFF);
++    }
++}
++
++static void
++elan4mmu_invalidatepte (ELAN4_DEV *dev, ELAN4_HASH_ENTRY *he, int tagidx, int pteidx)
++{
++    if (pteidx == 3)
++      elan4_sdram_writeb (dev, he->he_entry + E4MMU_PTE3_WORD0_OFFSET(tagidx), PTE_SetPerm (PERM_Disabled));
++    else
++      elan4_sdram_writeb (dev, he->he_entry + E4MMU_PTE_LOW_OFFSET(tagidx, pteidx), PTE_SetPerm (PERM_Disabled));
++}
++
++static E4_uint64
++elan4mmu_readpte (ELAN4_DEV *dev, ELAN4_HASH_ENTRY *he, int tagidx, int pteidx)
++{
++    if (pteidx == 3)
++      return (((E4_uint64) elan4_sdram_readw (dev, he->he_entry + E4MMU_PTE3_WORD0_OFFSET(tagidx)) << 0)  |
++              ((E4_uint64) elan4_sdram_readw (dev, he->he_entry + E4MMU_PTE3_WORD1_OFFSET(tagidx)) << 16) |
++              ((E4_uint64) elan4_sdram_readw (dev, he->he_entry + E4MMU_PTE3_WORD2_OFFSET(tagidx)) << 32));
++    else
++      return ((E4_uint64) elan4_sdram_readl (dev, he->he_entry + E4MMU_PTE_LOW_OFFSET(tagidx, pteidx)) |
++              ((E4_uint64) elan4_sdram_readw (dev, he->he_entry + E4MMU_PTE_HIGH_OFFSET(tagidx, pteidx)) << 32));
++}
++
++
++void
++elan4mmu_flush_tlb (ELAN4_DEV *dev)
++{
++    PULSE_SYSCONTROL (dev, CONT_TLB_FLUSH);
++
++    while (read_reg64 (dev, SysControlReg) & CONT_TLB_FLUSH)
++      DELAY (1);
++}
++
++/*
++ * elanmmu_flush_tlb_hash - this flushes the hash copy entries and the elan
++ * tlb.  However after the write to the hash copy entry if the elan was
++ * in the process of walking, then it could write the hash copy with a valid
++ * entry which we had just invalidated. However once we've seen the tlb flushed
++ * then if the walk engine had done a write - then we need to invaldate the
++ * hash copy entries again and reflush the tlb.
++ *
++ * If we're invalidating a lot of hash blocks, then the chances are that the
++ * walk engine will perform a write - so we flush the tlb first, then invalidate
++ * the hash copy entries, then flush the tlb again.
++ */
++static void
++elan4mmu_flush_tlb_hash (ELAN4_DEV *dev, int tbl, unsigned baseidx, unsigned topidx)
++{
++    int       notmany = (abs(topidx - baseidx) < 5) ? 1 : 0;
++    int       hashidx;
++    E4_uint32 reg;
++
++    if (notmany)
++      PULSE_SYSCONTROL (dev, CONT_CLEAR_WALK_WROTE_TABLES);
++    else
++      elan4mmu_flush_tlb(dev);
++
++    do {
++      for (hashidx = baseidx; hashidx <= topidx; hashidx++)
++          if (dev->dev_mmuhash[tbl][hashidx].he_tag[0] & HE_TAG_COPY)
++          {
++              ASSERT ((dev->dev_mmuhash[tbl][hashidx].he_tag[0] & HE_TAG_VALID) == 0);
++              ASSERT ((dev->dev_mmuhash[tbl][hashidx].he_tag[1] & HE_TAG_VALID) == 0);
++
++              elan4mmu_synctag (dev, &dev->dev_mmuhash[tbl][hashidx], 0);
++              elan4mmu_synctag (dev, &dev->dev_mmuhash[tbl][hashidx], 1);
++          }
++      
++      PULSE_SYSCONTROL (dev, CONT_TLB_FLUSH);
++      
++      while ((reg = read_reg64 (dev, SysControlReg)) & CONT_TLB_FLUSH)
++          DELAY (1);
++      
++    } while (notmany-- && (reg & CONT_CLEAR_WALK_WROTE_TABLES) != 0);
++}
++
++void
++elan4mmu_display_hent (ELAN4_DEV *dev, ELAN4_HASH_ENTRY *he, int hashidx)
++{
++    int tagidx;
++
++    elan4_debugf (DBG_DEVICE, DBG_MMU, "elan4mmu_display_hent: hashidx=%d he=%p entry at %lx\n", hashidx, he, he->he_entry);
++    elan4_debugf (DBG_DEVICE, DBG_MMU, "                       next=%p prev=%p chain=%p,%p\n", he->he_next, he->he_prev, he->he_chain[0], he->he_chain[1]);
++    for (tagidx = 0; tagidx < 2; tagidx++)
++    {
++      E4_uint64 tag  = elan4_sdram_readq (dev, he->he_entry + E4MMU_TAG_OFFSET(tagidx));
++      E4_uint64 pte0 = elan4_sdram_readq (dev, he->he_entry + E4MMU_PTE_LOW_OFFSET(tagidx, 0));
++      E4_uint64 pte1 = elan4_sdram_readq (dev, he->he_entry + E4MMU_PTE_LOW_OFFSET(tagidx, 1));
++      E4_uint64 pte2 = elan4_sdram_readq (dev, he->he_entry + E4MMU_PTE_LOW_OFFSET(tagidx, 2));
++      E4_uint64 pte3 = ((pte0 >> 48) | (pte1 >> 32) | (pte2 >> 16));
++      
++      elan4_debugf (DBG_DEVICE, DBG_MMU, "                       Tag %d (%llx,%08x) context=%04x vaddr=%llx\n", tagidx, he->he_tag[tagidx], he->he_pte[tagidx], (int) (tag & TAG_CONTEXT_MASK), (tag & TAG_ADDRESS_MASK));
++      elan4_debugf (DBG_DEVICE, DBG_MMU, "                       Pte 0 - PPN=%llx PERM=%x TYPE=%x%s%s\n", (pte0 & PTE_PPN_MASK) >> PTE_PPN_SHIFT, 
++                    (int) (pte0 & PTE_PERM_MASK) >> PTE_PERM_SHIFT, (int)(pte0 & PTE_TYPE_MASK), (pte0 & PTE_MOD_MASK) ? " mod" : "", (pte0 & PTE_REF_MASK) ? " ref" : "");
++      elan4_debugf (DBG_DEVICE, DBG_MMU, "                       Pte 1 - PPN=%llx PERM=%x TYPE=%x%s%s\n", (pte1 & PTE_PPN_MASK) >> PTE_PPN_SHIFT, 
++                    (int) (pte1 & PTE_PERM_MASK) >> PTE_PERM_SHIFT, (int)(pte1 & PTE_TYPE_MASK), (pte1 & PTE_MOD_MASK) ? " mod" : "", (pte1 & PTE_REF_MASK) ? " ref" : "");
++      elan4_debugf (DBG_DEVICE, DBG_MMU, "                       Pte 2 - PPN=%llx PERM=%x TYPE=%x%s%s\n", (pte2 & PTE_PPN_MASK) >> PTE_PPN_SHIFT, 
++                    (int) (pte2 & PTE_PERM_MASK) >> PTE_PERM_SHIFT, (int)(pte2 & PTE_TYPE_MASK), (pte2 & PTE_MOD_MASK) ? " mod" : "", (pte2 & PTE_REF_MASK) ? " ref" : "");
++      elan4_debugf (DBG_DEVICE, DBG_MMU, "                       Pte 3 - PPN=%llx PERM=%x TYPE=%x%s%s\n", (pte3 & PTE_PPN_MASK) >> PTE_PPN_SHIFT, 
++                    (int) (pte3 & PTE_PERM_MASK) >> PTE_PERM_SHIFT, (int)(pte3 & PTE_TYPE_MASK), (pte3 & PTE_MOD_MASK) ? " mod" : "", (pte3 & PTE_REF_MASK) ? " ref" : "");
++    }
++}
++
++static __inline__ ELAN4_HASH_ENTRY *
++he_ctxt_next (ELAN4_HASH_ENTRY *he, int ctxnum)
++{
++    return ((he->he_tag[0] & TAG_CONTEXT_MASK) == ctxnum) ? he->he_chain[0] : he->he_chain[1];
++}
++
++static __inline__ ELAN4_HASH_ENTRY *
++he_ctxt_unlink (ELAN4_CTXT *ctxt, int tbl, int hashidx, ELAN4_HASH_ENTRY *prevhe, ELAN4_HASH_ENTRY *he, ELAN4_HASH_ENTRY *next)
++{
++    /* Check whether either tag is in use by this context */
++    if ((he->he_tag[0] & TAG_CONTEXT_MASK) == ctxt->ctxt_num || (he->he_tag[1] & TAG_CONTEXT_MASK) == ctxt->ctxt_num)
++      return he;
++
++    if (prevhe == NULL)
++      ctxt->ctxt_mmuhash[tbl][hashidx] = next;
++    else
++    {
++      /* previous he, ensure that both chain pointers are changed is this ctxt is using both tags */
++      ASSERT ((prevhe->he_tag[0] & TAG_CONTEXT_MASK) == ctxt->ctxt_num || (prevhe->he_tag[1] & TAG_CONTEXT_MASK) == ctxt->ctxt_num);
++
++      if ((prevhe->he_tag[0] & TAG_CONTEXT_MASK) == ctxt->ctxt_num)
++          prevhe->he_chain[0] = next;
++      if ((prevhe->he_tag[1] & TAG_CONTEXT_MASK) == ctxt->ctxt_num)
++          prevhe->he_chain[1] = next;
++    }
++
++    return prevhe;
++}
++
++void
++elan4mmu_display (ELAN4_CTXT *ctxt, int tbl, const char *tag)
++{
++    ELAN4_DEV *dev = ctxt->ctxt_dev;
++    ELAN4_HASH_ENTRY *he;
++    int hashidx;
++
++    for (hashidx = 0; hashidx < dev->dev_hashsize[tbl]; hashidx++)
++      for (he = ctxt->ctxt_mmuhash[tbl][hashidx]; he != NULL; he = he_ctxt_next (he, ctxt->ctxt_num))
++      {
++          elan4_debugf (DBG_DEVICE, DBG_MMU, "%s: hashidx=%d he=%p tags <%llx,%llx>\n", tag, hashidx, he,
++                        (he->he_tag[0] & TAG_CONTEXT_MASK) == ctxt->ctxt_num ? E4MMU_TAG2VADDR (he->he_tag[0], hashidx, dev->dev_pageshift[tbl], dev->dev_hashsize[tbl]-1) : 0,
++                        (he->he_tag[1] & TAG_CONTEXT_MASK) == ctxt->ctxt_num ? E4MMU_TAG2VADDR (he->he_tag[1], hashidx, dev->dev_pageshift[tbl], dev->dev_hashsize[tbl]-1) : 0);
++          elan4mmu_display_hent (dev, he, hashidx);
++      }
++}
++
++static ELAN4_HASH_ENTRY *
++elan4mmu_alloc_hent (ELAN4_DEV *dev, int tbl, int hashidx, E4_uint64 newtag, int *tagidx)
++{
++    ELAN4_HASH_ENTRY *he, *phe;
++    unsigned long flags;
++    int i;
++
++    spin_lock_irqsave (&dev->dev_mmulock, flags);
++
++    /* 2nd see if there are any partial free blocks */
++    if ((he = dev->dev_mmufree[tbl][hashidx]) != NULL)
++    {
++      *tagidx = ((he->he_tag[0] & TAG_CONTEXT_MASK) == INVALID_CONTEXT) ? 0 : 1;
++      
++      MPRINTF (DBG_DEVICE, 3, "elan4mmu_alloc_hent: allocate he=%p idx=%d%s\n", he, *tagidx, (he == &dev->dev_mmuhash[tbl][hashidx]) ? " hash-block" : "");
++      
++      he->he_tag[*tagidx] = newtag | HE_TAG_VALID;
++
++      elan4mmu_synctag (dev, he, *tagidx);
++      
++      if ((he->he_tag[(*tagidx) ^ 1] & TAG_CONTEXT_MASK) != INVALID_CONTEXT)
++      {
++          MPRINTF (DBG_DEVICE, 3, "elan4mmu_alloc_hent: block full - remove from freelist\n");
++          dev->dev_mmufree[tbl][hashidx] = he->he_chain[*tagidx];
++      }
++      
++      spin_unlock_irqrestore (&dev->dev_mmulock, flags);
++      return (he);
++    }
++    
++    if ((he = dev->dev_mmufreelist) != NULL)
++      dev->dev_mmufreelist = he->he_next;
++    else
++    {
++      ELAN4_HASH_CHUNK *hc;
++      sdramaddr_t       entry;
++
++      KMEM_ALLOC (hc, ELAN4_HASH_CHUNK *, sizeof (ELAN4_HASH_CHUNK), 0);
++      
++      if (hc == NULL)
++      {
++          spin_unlock_irqrestore (&dev->dev_mmulock, flags);
++          return ((ELAN4_HASH_ENTRY *) NULL);
++      }
++      
++      if ((entry = elan4_sdram_alloc (dev, sizeof (E4_HashTableEntry) * ELAN4_HENT_CHUNKS)) == (sdramaddr_t) 0)
++      {
++          spin_unlock_irqrestore (&dev->dev_mmulock, flags);
++
++          KMEM_FREE (hc, sizeof (ELAN4_HASH_CHUNK));
++          return ((ELAN4_HASH_ENTRY *) NULL);
++      }
++
++      list_add_tail (&hc->hc_link, &dev->dev_hc_list);
++
++      elan4_sdram_zeroq_sdram (dev, entry, sizeof (E4_HashTableEntry) * ELAN4_HENT_CHUNKS);
++
++      /* no initialise all chunks and chain all but the first onto the freelist */
++      for (i = 0; i < ELAN4_HENT_CHUNKS; i++, entry += sizeof (E4_HashTableEntry))
++      {
++          hc->hc_hents[i].he_entry = entry;
++
++          if (i == 0)
++              he = &hc->hc_hents[0];
++          else
++          {
++              hc->hc_hents[i].he_next = dev->dev_mmufreelist;
++              dev->dev_mmufreelist = &hc->hc_hents[i];
++          }
++      }
++    }
++
++    /* Initialise hash entry, using slot 0 */
++    *tagidx = 0;
++
++    he->he_next     = NULL;
++    he->he_prev     = NULL;
++    he->he_chain[0] = NULL;
++    he->he_chain[1] = NULL;
++    he->he_tag[0]   = newtag | HE_TAG_VALID;
++    he->he_tag[1]   = E4MMU_TAG(0, INVALID_CONTEXT);
++    he->he_pte[0]   = 0;
++    he->he_pte[1]   = 0;
++    
++    elan4mmu_synctag (dev, he, 0);
++    
++    /* add slot 1 to freelist */
++    he->he_chain[1] = dev->dev_mmufree[tbl][hashidx];
++    dev->dev_mmufree[tbl][hashidx] = he;
++    
++    /* add to mmuhash lists */
++    for (phe = &dev->dev_mmuhash[tbl][hashidx]; phe->he_next; phe = phe->he_next)
++      ;
++    phe->he_next = he;
++    he->he_prev  = phe;
++    he->he_next  = NULL;
++    
++    /* finally chain the hash block into the hash tables */
++    elan4mmu_chain_hents (dev, phe, he);
++    
++    spin_unlock_irqrestore (&dev->dev_mmulock, flags);
++    return (he);
++}
++
++static void
++elan4mmu_free_hent (ELAN4_DEV *dev, int tbl, int hashidx, ELAN4_HASH_ENTRY *he, int tagidx)
++{
++    unsigned long flags;
++    int pteidx;
++
++    /* Invalidate the tag, and zero all ptes */
++    for (pteidx = 0; pteidx < 4; pteidx++)
++      if (HE_GET_PTE(he, tagidx, pteidx))
++          elan4mmu_writepte (dev, he, tagidx, pteidx, 0);
++
++    spin_lock_irqsave (&dev->dev_mmulock, flags);
++
++    he->he_tag[tagidx] = E4MMU_TAG(0, INVALID_CONTEXT);
++    he->he_pte[tagidx] = 0;
++
++    elan4mmu_synctag (dev, he, tagidx);
++
++    if ((he->he_tag[tagidx^1] & TAG_CONTEXT_MASK) == INVALID_CONTEXT) /* Both tags are now free */
++    {
++      if (he == &dev->dev_mmuhash[tbl][hashidx])              /* it's the hash block entry */
++      {                                                       /* so as it's already on the freelist */
++          he->he_chain[tagidx] = he->he_chain[tagidx^1];      /* just copy it's chain pointers */
++
++          MPRINTF (DBG_DEVICE, 3, "elan4mmu_free_hent: tbl=%d hashidx=%x tagidx=%d he=%p => all free but hashblk\n", tbl, hashidx, tagidx, he);
++      }
++      else
++      {
++          MPRINTF (DBG_DEVICE, 3, "elan4mmu_free_hent: tbl=%d hashidx=%x tagidx=%d he=%p => all free\n", tbl, hashidx, tagidx, he);
++          
++          /* XXXX - should remove it from the hash table, and 
++          *         place back on the anonymous freelist */
++          he->he_chain[tagidx] = he->he_chain[tagidx^1];
++      }
++    }
++    else
++    {
++      /* Other tag still in use */
++      he->he_chain[tagidx] = dev->dev_mmufree[tbl][hashidx];
++      dev->dev_mmufree[tbl][hashidx] = he;
++
++      MPRINTF (DBG_DEVICE, 3, "elan4mmu_free_hent: tbl=%d hashidx=%x tagidx=%d he=%p => other tag in use\n", tbl, hashidx, tagidx, he);
++    }
++    spin_unlock_irqrestore (&dev->dev_mmulock, flags);
++}
++
++ELAN4_HASH_ENTRY *
++elan4mmu_ptealloc (ELAN4_CTXT *ctxt, int tbl, E4_Addr vaddr, unsigned int *tagidxp)
++{
++    ELAN4_DEV        *dev     = ctxt->ctxt_dev;
++    unsigned        ctxnum  = ctxt->ctxt_num;
++    unsigned          hashidx = E4MMU_HASH_INDEX (ctxnum, vaddr, dev->dev_pageshift[tbl], dev->dev_hashsize[tbl]-1);
++    E4_uint64         newtag  = E4MMU_TAG(vaddr, ctxnum);
++    ELAN4_HASH_ENTRY *he      = &dev->dev_mmuhash[tbl][hashidx];
++    unsigned        tagidx;
++
++    MPRINTF (ctxt, 2, "elan4mmu_ptealloc: tbl=%d ctxnum=%d vaddr=%llx -> hashidx %d\n", tbl, ctxnum, vaddr, hashidx);
++
++    /* 1st) check whether we're reloading an existing entry */
++    for (he = ctxt->ctxt_mmuhash[tbl][hashidx]; he != NULL; he = he_ctxt_next (he, ctxnum))
++    {
++      ASSERT ((he->he_tag[0] & TAG_CONTEXT_MASK) == ctxnum || (he->he_tag[1] & TAG_CONTEXT_MASK) == ctxnum);
++
++      for (tagidx = 0; tagidx < 2; tagidx++)
++      {
++          if ((he->he_tag[tagidx] & (TAG_ADDRESS_MASK | TAG_CONTEXT_MASK | HE_TAG_VALID)) == (newtag | HE_TAG_VALID))
++          {
++              MPRINTF (ctxt, 2, "elan4mmu_ptealloc: return old he %p tagidx %d\n", he, tagidx);
++
++              *tagidxp = tagidx;
++              return he;
++          }
++      }
++    }
++
++    if ((he = elan4mmu_alloc_hent (dev, tbl, hashidx, newtag, &tagidx)) == NULL)
++      return NULL;
++
++    /* chain onto context hash */
++    if ((he->he_tag[tagidx ^ 1] & TAG_CONTEXT_MASK) == ctxnum)        /* already chained using other link */
++    {                                                         /* so ensure both slots are chained the same */
++      he->he_chain[tagidx] = he->he_chain[tagidx^1];
++    }
++    else
++    {
++      he->he_chain[tagidx] = ctxt->ctxt_mmuhash[tbl][hashidx];
++      ctxt->ctxt_mmuhash[tbl][hashidx] = he;
++    }
++
++    MPRINTF (ctxt, 2, "elan4mmu_ptealloc: return new he %p tagidx %d\n", he, tagidx);
++
++    *tagidxp = tagidx;
++
++    return he;
++}
++
++int
++elan4mmu_pteload (ELAN4_CTXT *ctxt, int tbl, E4_Addr vaddr, E4_uint64 newpte)
++{
++    ELAN4_DEV        *dev     = ctxt->ctxt_dev;
++    unsigned          pteidx  = E4MMU_SHIFT_ADDR(vaddr, dev->dev_pageshift[tbl]) & 3;
++    unsigned        tagidx;
++    ELAN4_HASH_ENTRY *he;
++
++    MPRINTF (ctxt, 0, "elan4mmu_pteload: ctx=%d tbl=%d pteidx=%d vaddr=%llx pte=%llx\n", 
++          ctxt->ctxt_num, tbl, pteidx, (unsigned long long)vaddr, newpte);
++
++    spin_lock (&ctxt->ctxt_mmulock);
++
++    if ((he = elan4mmu_ptealloc (ctxt, tbl, vaddr, &tagidx)) == NULL)
++    {
++      spin_unlock (&ctxt->ctxt_mmulock);
++      return -ENOMEM;
++    }
++
++    MPRINTF (ctxt, 1, "elan4mmu_pteload: %s he=%p tagidx=%d pteidx=%d\n", HE_GET_PTE(he,0,pteidx) ? "reloading" : "loading", he, tagidx, pteidx);
++    
++    ASSERT (HE_GET_PTE(he,tagidx,pteidx) == 0 ||                                                      /* invalid -> valid */
++          (elan4mmu_readpte (dev, he, tagidx, pteidx) & PTE_PPN_MASK) == (newpte & PTE_PPN_MASK));    /* or same phys address */
++    
++    elan4mmu_writepte (dev, he, tagidx, pteidx, newpte);
++    
++    HE_SET_PTE(he, tagidx, pteidx, (newpte & PTE_PERM_TYPE_MASK));
++
++    spin_unlock (&ctxt->ctxt_mmulock);
++    return 0;
++}
++
++void
++elan4mmu_unload_range (ELAN4_CTXT *ctxt, int tbl, E4_Addr start, unsigned long len)
++{
++    ELAN4_DEV        *dev       = ctxt->ctxt_dev;
++    unsigned          ctxnum    = ctxt->ctxt_num;
++    unsigned long     tagspan   = (1 << (dev->dev_pageshift[tbl] + 2));
++    E4_Addr           end       = start + len - 1;
++    int                     needflush = 0;
++    unsigned          baseidx, topidx;
++    unsigned          hashidx, tagidx, pteidx;
++    ELAN4_HASH_ENTRY *he, *prevhe, *next;
++    
++    MPRINTF (ctxt, 0, "elan4mmu_unload_range: tbl=%d start=%llx end=%llx len=%lx\n", tbl, start, end, len);
++
++    /* determine how much of the hash table we've got to scan */
++    
++    /* GNAT 6760: When we have a Main page size which maps onto multiple Elan pages
++     * we need to do something a bit more clever here or else it takes ms per page invalidate
++     * This change helps in the meantime
++     */
++    /* if (len <= (1 << dev->dev_pageshift[tbl])) */
++    if (len <= PAGE_SIZE)
++    {
++      baseidx = E4MMU_HASH_INDEX (ctxnum, start, dev->dev_pageshift[tbl], dev->dev_hashsize[tbl]-1);
++      topidx  = E4MMU_HASH_INDEX (ctxnum, end,   dev->dev_pageshift[tbl], dev->dev_hashsize[tbl]-1);
++
++      if (baseidx != topidx)
++      {
++          /* GNAT 6760: Need to search whole of the hash table (slow!) */
++          baseidx = 0;
++          topidx  = dev->dev_hashsize[tbl] - 1;
++      }
++    }
++    else
++    {
++      baseidx = 0;
++      topidx  = dev->dev_hashsize[tbl] - 1;
++    }
++
++    MPRINTF (ctxt, 1, "elan4mmu_unload_range: baseidx=%d topidx=%d\n", baseidx, topidx);
++
++    spin_lock (&ctxt->ctxt_mmulock);
++
++    /* 1st - invalidate the tag for all hash blocks which are completely invalidated,
++     *       and remember the first/last hash blocks */
++    for (hashidx = baseidx; hashidx <= topidx; hashidx++)
++      for (he = ctxt->ctxt_mmuhash[tbl][hashidx]; he != NULL; he = he_ctxt_next (he, ctxnum))
++          for (tagidx = 0; tagidx < 2; tagidx++)
++              if ((he->he_tag[tagidx] & TAG_CONTEXT_MASK) == ctxnum)
++              {
++                  E4_Addr base = E4MMU_TAG2VADDR (he->he_tag[tagidx], hashidx, dev->dev_pageshift[tbl], dev->dev_hashsize[tbl]-1);
++                  E4_Addr top  = base + (tagspan -1);
++                  
++                  if (start < top && end > base)
++                  {
++                      unsigned bidx = (start <= base) ? 0 : (start & (tagspan-1)) >> dev->dev_pageshift[tbl];
++                      unsigned tidx = (end   >= top)  ? 3 : (end   & (tagspan-1)) >> dev->dev_pageshift[tbl];
++                      
++                      MPRINTF (ctxt, 1, "elan4mmu_unload_range: he=%p base=%llx top=%llx hashidx=%d bidx=%d tidx=%d\n", he, base, top, hashidx, bidx, tidx);
++                      
++                      for (pteidx = bidx; pteidx <= tidx; pteidx++)
++                          if (HE_GET_PTE(he, tagidx, pteidx))
++                          {
++                              elan4mmu_invalidatepte (dev, he, tagidx, pteidx);
++                              needflush = 1;
++                          }
++                  }
++                  else if (base >= start && top <= end)               /* hash entry completely spanned */
++                  {                                                   /* so invalidate the tag */
++                      MPRINTF (ctxt, 1, "elan4mmu_unload_range: he=%p base=%llx top=%llx spanned\n", he, base, top);
++
++                      he->he_tag[tagidx] &= ~HE_TAG_VALID;
++                      
++                      elan4mmu_synctag (dev, he, tagidx);
++                      needflush = 1;
++                  }
++              }
++
++    if (needflush)
++    {
++      /* 2nd invalidate the first/last hash blocks if they are partially invalidated
++       * and flush the tlb/hash copy blocks */
++      elan4mmu_flush_tlb_hash (dev, tbl, baseidx, topidx);
++      
++      /* 3rd free off the hash entries which are completely invalidated */
++      for (hashidx = baseidx; hashidx <= topidx; hashidx++)
++          for (prevhe = NULL, he = ctxt->ctxt_mmuhash[tbl][hashidx]; he != NULL; he = next)
++          {
++              next = he_ctxt_next (he, ctxnum);
++              
++              for (tagidx = 0; tagidx < 2; tagidx++)
++                  if ((he->he_tag[tagidx] & TAG_CONTEXT_MASK) == ctxnum)
++                  {
++                      E4_Addr base = E4MMU_TAG2VADDR (he->he_tag[tagidx], hashidx, dev->dev_pageshift[tbl], dev->dev_hashsize[tbl]-1);
++                      E4_Addr top  = base + (tagspan -1);
++                      
++                      if (start < top && end > base)
++                      {
++                          unsigned bidx = (start <= base) ? 0 : (start & (tagspan-1)) >> dev->dev_pageshift[tbl];
++                          unsigned tidx = (end   >= top)  ? 3 : (end   & (tagspan-1)) >> dev->dev_pageshift[tbl];
++                          
++                          MPRINTF (ctxt, 1, "elan4mmu_unload_range: he=%p base=%llx top=%llx bidx=%d tidx=%d\n", he, base, top, bidx, tidx);
++                          
++                          for (pteidx = bidx; pteidx <= tidx; pteidx++)
++                              if (HE_GET_PTE(he, tagidx, pteidx))
++                              {
++                                  HE_SET_PTE(he, tagidx, pteidx, 0);
++                                  
++                                  elan4mmu_writepte (dev, he, tagidx, pteidx, 0);
++                              }
++                      }
++                      
++                      if ((base >= start && top <= end) || he->he_pte[tagidx] == 0)   /* hash entry completely spanned or all pte's cleared */
++                      {                                                                       /* so invalidate the pte's and free it */
++                          
++                          MPRINTF (ctxt, 1, "elan4mmu_unload_range: he=%p base=%llx top=%llx spanned or empty\n", he, base, top);
++                          
++                          elan4mmu_free_hent (dev, tbl, hashidx, he, tagidx);
++                      }
++                  }
++              
++              prevhe = he_ctxt_unlink (ctxt, tbl, hashidx, prevhe, he, next);
++          }
++    }
++    spin_unlock (&ctxt->ctxt_mmulock);
++}
++
++void
++elan4mmu_invalidate_ctxt (ELAN4_CTXT *ctxt)
++{
++    ELAN4_DEV *dev    = ctxt->ctxt_dev;
++    int        ctxnum = ctxt->ctxt_num;
++    ELAN4_HASH_ENTRY *he;
++    int tbl, hashidx, tagidx;
++
++    MPRINTF (ctxt, 0, "elan4mmu_invalidate_ctxt: invalidating ctxnum=%d\n", ctxnum);
++
++    spin_lock (&ctxt->ctxt_mmulock);
++
++    /* 1st invalidate all tags belonging to me */
++    for (tbl = 0; tbl < NUM_HASH_TABLES; tbl++)
++      for (hashidx = 0; hashidx < dev->dev_hashsize[tbl]; hashidx++)
++          for (he = ctxt->ctxt_mmuhash[tbl][hashidx]; he != NULL; he = he_ctxt_next (he, ctxnum))
++              for (tagidx = 0; tagidx < 2; tagidx++)
++                  if ((he->he_tag[tagidx] & TAG_CONTEXT_MASK) == ctxnum) /* own tag block */
++                  {
++                      MPRINTF (ctxt, 1, "elan4mmu_invalidate_ctxt: he=%p addr=%llx hashidx=%d tagidx=%d\n", 
++                               he, he->he_tag[tagidx] & TAG_ADDRESS_MASK, hashidx, tagidx);
++
++                      he->he_tag[tagidx] &= ~HE_TAG_VALID;
++                      
++                      elan4mmu_synctag (dev, he, tagidx);
++                  }
++
++    /* 2nd flush the tlb & cached hash block */
++    elan4mmu_flush_tlb (dev);
++    
++    /* 3rd invalidate all pte's and free off the hash entries */
++    for (tbl = 0; tbl < NUM_HASH_TABLES; tbl++)
++      for (hashidx = 0; hashidx < dev->dev_hashsize[tbl]; hashidx++)
++          while ((he = ctxt->ctxt_mmuhash[tbl][hashidx]) != NULL)
++          {
++              ctxt->ctxt_mmuhash[tbl][hashidx] = he_ctxt_next (he, ctxnum);
++
++              for (tagidx = 0; tagidx < 2; tagidx++)
++                  if ((he->he_tag[tagidx] & TAG_CONTEXT_MASK) == ctxnum)
++                      elan4mmu_free_hent (dev, tbl, hashidx, he, tagidx);
++          }
++    spin_unlock (&ctxt->ctxt_mmulock);
++}
++
++ELAN4_HASH_CACHE *
++elan4mmu_reserve (ELAN4_CTXT *ctxt, int tbl, E4_Addr start, unsigned int npages, int cansleep)
++{
++    ELAN4_DEV        *dev      = ctxt->ctxt_dev;
++    E4_Addr           end      = start + (npages << dev->dev_pageshift[tbl]) - 1;
++    unsigned long     tagshift = dev->dev_pageshift[tbl] + 2;
++    E4_Addr           tagspan  = 1 << tagshift;
++    E4_Addr           base     = (start & ~(tagspan-1));
++    E4_Addr           top      = (end   & ~(tagspan-1)) + (tagspan-1);
++    unsigned int      nhes     = (top - base + 1) >> tagshift;
++    ELAN4_HASH_CACHE *hc;
++    unsigned int      tagidx,  pteidx;
++    E4_Addr           addr;
++    int                     i;
++    
++    MPRINTF (ctxt, 0, "elan4mmu_reserve: start=%llx npages=%d\n", start, npages);
++    MPRINTF (ctxt, 0, "         pageshift=%d tagspan=%lx base=%llx top=%llx end=%llx nhes=%d\n",
++           dev->dev_pageshift[tbl], tagspan, base, top, end, nhes);
++
++    KMEM_ALLOC (hc, ELAN4_HASH_CACHE *, offsetof (ELAN4_HASH_CACHE, hc_hes[nhes]), cansleep);
++
++    if (hc == NULL)
++      return NULL;
++
++    hc->hc_start = start;
++    hc->hc_end   = end;
++    hc->hc_tbl   = tbl;
++
++    spin_lock (&ctxt->ctxt_mmulock);
++    for (addr = base, i = 0; i < nhes; addr += tagspan, i++)
++    {
++      unsigned bidx = (i == 0)        ? (start & (tagspan-1)) >> dev->dev_pageshift[tbl] : 0;
++      unsigned tidx = (i == (nhes-1)) ? (end   & (tagspan-1)) >> dev->dev_pageshift[tbl] : 3;
++
++      
++      if ((hc->hc_hes[i] = elan4mmu_ptealloc (ctxt, tbl, addr & ~(tagspan-1), &tagidx)) == NULL)
++          goto failed;
++
++
++      MPRINTF (ctxt, 2, "elan4mmu_reserve: tbl=%d addr=%llx -> hashidx=%d tagidx=%d\n", tbl, addr & ~(tagspan-1), 
++               E4MMU_HASH_INDEX (ctxt->ctxt_num, (addr & ~(tagspan-1)), dev->dev_pageshift[tbl], dev->dev_hashsize[tbl]-1), tagidx);
++                       
++      for (pteidx = bidx; pteidx <= tidx; pteidx++)
++      {
++          ASSERT (HE_GET_PTE (hc->hc_hes[i], tagidx, pteidx) == 0);
++
++          MPRINTF (ctxt, 2, "elan4mmu_reserve: i=%d addr=%llx he=%p (tagidx=%d pteidx=%d)\n",
++                   i, addr, hc->hc_hes[i], tagidx, pteidx);
++
++          HE_SET_PTE (hc->hc_hes[i], tagidx, pteidx, PTE_PERM_TYPE_MASK);
++      }
++    }
++    spin_unlock (&ctxt->ctxt_mmulock);
++
++    return hc;
++
++ failed:
++    for (i--, addr -= tagspan; i >= 0; i--, addr -= tagspan)
++    {
++      unsigned bidx    = (i == 0) ? (start & (tagspan-1)) >> dev->dev_pageshift[tbl] : 0;
++      unsigned tidx    = (i == (nhes-1)) ? (end   & (tagspan-1)) >> dev->dev_pageshift[tbl] : 3;
++      unsigned hashidx = E4MMU_HASH_INDEX (ctxt->ctxt_num, addr, dev->dev_pageshift[tbl], dev->dev_hashsize[tbl]-1);
++      unsigned tagidx  = (addr == E4MMU_TAG2VADDR (hc->hc_hes[i]->he_tag[0], hashidx, dev->dev_pageshift[tbl], dev->dev_hashsize[tbl]-1)) ? 0 : 1;
++          
++      for (pteidx = bidx; pteidx <= tidx; pteidx++)
++          HE_SET_PTE(hc->hc_hes[i], tagidx, pteidx, 0);
++
++      if (hc->hc_hes[i]->he_pte[tagidx] == 0)
++          elan4mmu_free_hent (dev, tbl, hashidx, hc->hc_hes[i], tagidx);
++    }
++    spin_unlock (&ctxt->ctxt_mmulock);
++
++    KMEM_FREE (hc, offsetof (ELAN4_HASH_CACHE, hc_hes[nhes]));
++    
++    return NULL;
++}
++
++void
++elan4mmu_release (ELAN4_CTXT *ctxt, ELAN4_HASH_CACHE *hc)
++{
++    ELAN4_DEV        *dev      = ctxt->ctxt_dev;
++    E4_Addr         start    = hc->hc_start;
++    E4_Addr           end      = hc->hc_end;
++    unsigned long     tagshift = dev->dev_pageshift[hc->hc_tbl] + 2;
++    E4_Addr           tagspan  = 1 << tagshift;
++    E4_Addr           base     = (start & ~(tagspan-1));
++    E4_Addr           top      = (end   & ~(tagspan-1)) + (tagspan-1);
++    unsigned int      nhes     = (top - base + 1) >> tagshift;
++    ELAN4_HASH_ENTRY *prevhe, *he, *next;
++    E4_Addr           addr;
++    unsigned int      pteidx;
++    int                     i;
++
++    spin_lock (&ctxt->ctxt_mmulock);
++
++    MPRINTF (ctxt, 0, "elan4mmu_release: base=%llx top=%llx\n", base, top);
++
++    for (addr = base, i = 0; i < nhes; addr += tagspan, i++)
++    {
++      unsigned bidx    = (i == 0)        ? (start & (tagspan-1)) >> dev->dev_pageshift[hc->hc_tbl] : 0;
++      unsigned tidx    = (i == (nhes-1)) ? (end   & (tagspan-1)) >> dev->dev_pageshift[hc->hc_tbl] : 3;
++      unsigned hashidx = E4MMU_HASH_INDEX (ctxt->ctxt_num, addr, dev->dev_pageshift[hc->hc_tbl], dev->dev_hashsize[hc->hc_tbl]-1);
++      unsigned tagidx  = (addr == E4MMU_TAG2VADDR (hc->hc_hes[i]->he_tag[0], hashidx, dev->dev_pageshift[hc->hc_tbl], dev->dev_hashsize[hc->hc_tbl]-1)) ? 0 : 1;
++          
++      for (pteidx = bidx; pteidx <= tidx; pteidx++)
++      {
++          elan4mmu_invalidatepte (dev, hc->hc_hes[i], tagidx, pteidx);
++
++          HE_SET_PTE(hc->hc_hes[i], tagidx, pteidx, 0);
++      }
++
++      MPRINTF (ctxt, 2, "elan4mmu_release: i=%d addr=%llx he=%p (hashidx=%d tagidx=%d pteidx=%d) pte=%x\n",
++               i, addr, hc->hc_hes[i], hashidx, tagidx, pteidx, hc->hc_hes[i]->he_pte[tagidx]);
++
++      /* remove from context hash */
++      for (prevhe = NULL, he = ctxt->ctxt_mmuhash[hc->hc_tbl][hashidx], next = he_ctxt_next (he, ctxt->ctxt_num); he != hc->hc_hes[i]; he = next)
++          next = he_ctxt_next (he, ctxt->ctxt_num);
++      
++      if (hc->hc_hes[i]->he_pte[tagidx] == 0)
++          elan4mmu_free_hent (dev, hc->hc_tbl, hashidx, hc->hc_hes[i], tagidx);
++
++      prevhe = he_ctxt_unlink (ctxt, hc->hc_tbl, hashidx, prevhe, he, next);
++    }
++    spin_unlock (&ctxt->ctxt_mmulock);
++}
++
++void
++elan4mmu_set_pte (ELAN4_CTXT *ctxt, ELAN4_HASH_CACHE *hc, unsigned int idx, E4_uint64 newpte)
++{
++    ELAN4_DEV        *dev      = ctxt->ctxt_dev;
++    unsigned int      tbl      = hc->hc_tbl;
++    unsigned int      tagshift = dev->dev_pageshift[tbl] + 2;
++    E4_Addr           tagspan  = 1 << tagshift;
++    E4_Addr           addr     = hc->hc_start + (idx << dev->dev_pageshift[tbl]);
++    ELAN4_HASH_ENTRY *he       = hc->hc_hes[(addr - (hc->hc_start & ~(tagspan-1))) >> tagshift];
++    unsigned          pteidx   = E4MMU_SHIFT_ADDR(addr, dev->dev_pageshift[tbl]) & 3;
++    unsigned          tagidx   = he->he_tag[0] == (E4MMU_TAG (addr, ctxt->ctxt_num) | HE_TAG_VALID) ? 0 : 1;
++
++    MPRINTF (ctxt, 2, "elan4mmu_set_pte: idx=%d addr=%llx he=%p (tagidx=%d pteidx=%d) newpte=%llx\n", idx, addr, he, tagidx, pteidx, newpte);
++
++    ASSERT (he->he_tag[tagidx] == (E4MMU_TAG (addr, ctxt->ctxt_num) | HE_TAG_VALID));
++
++    elan4mmu_writepte (dev, he, tagidx, pteidx, newpte);
++}
++
++E4_uint64
++elan4mmu_get_pte (ELAN4_CTXT *ctxt, ELAN4_HASH_CACHE *hc, unsigned int idx)
++{
++    ELAN4_DEV        *dev      = ctxt->ctxt_dev;
++    unsigned int      tbl      = hc->hc_tbl;
++    unsigned int      tagshift = dev->dev_pageshift[tbl] + 2;
++    E4_Addr           tagspan  = 1 << tagshift;
++    E4_Addr           addr     = hc->hc_start + (idx << dev->dev_pageshift[tbl]);
++    ELAN4_HASH_ENTRY *he       = hc->hc_hes[(addr - (hc->hc_start & ~(tagspan-1))) >> tagshift];
++    unsigned          pteidx   = E4MMU_SHIFT_ADDR(addr, dev->dev_pageshift[tbl]) & 3;
++    unsigned          tagidx   = he->he_tag[0] == (E4MMU_TAG (addr, ctxt->ctxt_num) | HE_TAG_VALID) ? 0 : 1;
++
++    ASSERT (he->he_tag[tagidx] == (E4MMU_TAG (addr, ctxt->ctxt_num) | HE_TAG_VALID));
++
++    return elan4mmu_readpte (dev, he, tagidx, pteidx);
++}
++
++void
++elan4mmu_clear_pte (ELAN4_CTXT *ctxt, ELAN4_HASH_CACHE *hc, unsigned int idx)
++{
++    ELAN4_DEV        *dev      = ctxt->ctxt_dev;
++    unsigned int      tbl      = hc->hc_tbl;
++    unsigned int      tagshift = dev->dev_pageshift[tbl] + 2;
++    E4_Addr           tagspan  = 1 << tagshift;
++    E4_Addr           addr     = hc->hc_start + (idx << dev->dev_pageshift[tbl]);
++    ELAN4_HASH_ENTRY *he       = hc->hc_hes[(addr - (hc->hc_start & ~(tagspan-1))) >> tagshift];
++    unsigned          pteidx   = E4MMU_SHIFT_ADDR(addr, dev->dev_pageshift[tbl]) & 3;
++    unsigned          tagidx   = he->he_tag[0] == (E4MMU_TAG (addr, ctxt->ctxt_num) | HE_TAG_VALID) ? 0 : 1;
++
++    MPRINTF (ctxt, 2, "elan4mmu_clear_pte: idx=%d addr=%llx he=%p (tagidx=%d pteidx=%d)\n", idx, addr, he, tagidx, pteidx);
++
++    ASSERT (he->he_tag[tagidx] == (E4MMU_TAG (addr, ctxt->ctxt_num) | HE_TAG_VALID));
++
++    elan4mmu_invalidatepte (dev, he, tagidx, pteidx);
++}
++
++EXPORT_SYMBOL(elan4mmu_flush_tlb);
++EXPORT_SYMBOL(elan4mmu_pteload);
++EXPORT_SYMBOL(elan4mmu_unload_range);
++EXPORT_SYMBOL(elan4mmu_reserve);
++EXPORT_SYMBOL(elan4mmu_release);
++EXPORT_SYMBOL(elan4mmu_set_pte);
++EXPORT_SYMBOL(elan4mmu_get_pte);
++EXPORT_SYMBOL(elan4mmu_clear_pte);
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan4/mmu_Linux.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan4/mmu_Linux.c      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan4/mmu_Linux.c   2005-06-01 23:12:54.611437280 -0400
+@@ -0,0 +1,265 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: mmu_Linux.c,v 1.8 2004/05/10 14:10:46 daniel Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/mmu_Linux.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan4/debug.h>
++#include <elan4/device.h>
++
++#include <linux/pci.h>
++#include <linux/version.h>
++
++/*
++ * Convert a physical address into an pte.  This should generate a "local" pte for 
++ * physical addresses which are elan4 sdram or elan4 command queues.  For elan4
++ * registers and other addresses on the same bus, this should be the local pci 
++ * bus address.  All other addresses should access the physical address via the
++ * PCI bridge.
++ */
++
++#ifdef __alpha
++#define ioaddr2paddr(ioaddr)  virt_to_phys((void *) __ioremap(ioaddr, PAGE_SIZE))
++#elif defined(__ia64)
++#define ioaddr2paddr(ioaddr)  ((ioaddr) & ~__IA64_UNCACHED_OFFSET)
++#else
++#define ioaddr2paddr(ioaddr)  (ioaddr)
++#endif
++
++int
++elan4mmu_categorise_paddr (ELAN4_DEV *dev, physaddr_t *physp)
++{
++    physaddr_t sdram_base = ioaddr2paddr (pci_resource_start (dev->dev_osdep.pdev, ELAN4_BAR_SDRAM));
++    physaddr_t sdram_top  = ioaddr2paddr (pci_resource_end (dev->dev_osdep.pdev, ELAN4_BAR_SDRAM));
++    physaddr_t regs_base  = ioaddr2paddr (pci_resource_start (dev->dev_osdep.pdev, ELAN4_BAR_REGISTERS));
++    physaddr_t regs_top   = ioaddr2paddr (pci_resource_end (dev->dev_osdep.pdev, ELAN4_BAR_REGISTERS));
++    physaddr_t phys       = *physp;
++    int        iscommand;
++
++    if (phys >= sdram_base && phys <= sdram_top)
++    {
++      (*physp) = (phys ^ sdram_base);
++      return ELAN4MMU_PADDR_SDRAM;
++    }
++    
++    if (phys >= regs_base && phys < regs_top)
++    {
++      if (dev->dev_devinfo.dev_revision_id == PCI_REVISION_ID_ELAN4_REVA)
++          iscommand = (phys < (regs_base + ELAN4_REVA_REG_OFFSET));
++      else
++          iscommand = (phys < (regs_base + ELAN4_REVB_I2C_OFFSET));
++      
++      if (iscommand)
++      {
++          (*physp) = phys ^ regs_base;
++
++          return ELAN4MMU_PADDR_COMMAND;
++      }
++      else
++      {
++          // XXXX (*physp) = phys2bus (phys);
++
++          return ELAN4MMU_PADDR_LOCALPCI;
++      }
++    }
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
++    if (VALID_PAGE (virt_to_page (phys_to_virt (phys))))
++#else
++    if (virt_addr_valid (phys_to_virt (phys)))
++#endif
++      return ELAN4MMU_PADDR_PAGE;
++    
++    return ELAN4MMU_PADDR_OTHER;
++}
++
++int
++elan4mmu_sdram_aliascheck (ELAN4_CTXT *ctxt, E4_Addr addr, physaddr_t phys)
++{
++    ELAN4_DEV *dev = ctxt->ctxt_dev;
++
++    /*
++     * On MPSAS we don't allocate a large enough context table, so 
++     * if we see an address/context pair which would "alias" because
++     * they differ in unchecked hash bits to a previous pteload, 
++     * then we kill the application.
++     */
++    unsigned hashval = (E4MMU_SHIFT_ADDR(addr, (dev->dev_pageshift[0]) + 2) ^ E4MMU_CONTEXT_SCRAMBLE(ctxt->ctxt_num));
++    
++    if (dev->dev_rsvd_hashval[0] == 0xFFFFFFFF)
++      dev->dev_rsvd_hashval[0] = hashval & dev->dev_rsvd_hashmask[0];
++    
++    if ((hashval & dev->dev_rsvd_hashmask[0]) != dev->dev_rsvd_hashval[0])
++    {
++      printk ("elan4mmu_sdram_aliascheck: vaddr=%016llx ctxnum=%x -> [%x] overlaps %x - %x [hashidx=%x]\n", (unsigned long long) addr, 
++              ctxt->ctxt_num, hashval, hashval & dev->dev_rsvd_hashmask[0], dev->dev_rsvd_hashval[0],
++              E4MMU_HASH_INDEX (ctxt->ctxt_num, addr, dev->dev_pageshift[0], dev->dev_hashsize[0]-1));
++      
++      return 0;
++    }
++
++    if (((addr & (SDRAM_PGOFF_OFFSET << PAGE_SHIFT)) != (phys & (SDRAM_PGOFF_OFFSET << PAGE_SHIFT))))
++    {
++      printk ("elan4mmu_sdram_aliascheck: vaddr=%016llx incorrectly alias sdram at %lx\n", (unsigned long long) addr, 
++              phys ^ pci_resource_start (dev->dev_osdep.pdev, ELAN4_BAR_SDRAM));
++      return 0;
++    }
++
++    return 1;
++}
++
++int
++elan4mmu_alloc_topaddr (ELAN4_DEV *dev, physaddr_t paddr, unsigned type)
++{
++#if defined(__i386) && !defined(CONFIG_X86_PAE)
++    if (dev->dev_topaddrvalid == 0)
++    {
++      dev->dev_topaddrvalid = 1;
++
++      pci_write_config_word (dev->dev_osdep.pdev, PCI_ELAN_TOPPHYSADDR(0), 0);
++      pci_write_config_word (dev->dev_osdep.pdev, PCI_ELAN_TOPPHYSADDR(1), 0);
++      pci_write_config_word (dev->dev_osdep.pdev, PCI_ELAN_TOPPHYSADDR(2), 0);
++      pci_write_config_word (dev->dev_osdep.pdev, PCI_ELAN_TOPPHYSADDR(3), 0);
++    }
++    return (0);
++#else
++    register int i;
++    E4_uint16 match;
++
++    if (dev->dev_topaddrmode)                                 /* ExtraMasterAddrBits=1 => match {paddr[63:50],type[3:2]} */
++      match = ((paddr >> 48) & ~3) | ((type >> 2) & 3);
++    else                                                      /* ExtraMasterAddrBits=0 => match {paddr[63:48]} */
++      match = (paddr >> 48);
++    
++    MPRINTF (DBG_DEVICE, 2, "elan4mmu_alloc_topaddr: mode=%d paddr=%lx type=%x match=%x [%x %x.%x.%x.%x]\n",
++           dev->dev_topaddrmode, paddr, type, match, dev->dev_topaddrvalid,
++           dev->dev_topaddr[0], dev->dev_topaddr[1], dev->dev_topaddr[2], dev->dev_topaddr[3]);
++    
++    for (i = 0; i < 4; i++)
++      if ((dev->dev_topaddrvalid & (1 << i)) && dev->dev_topaddr[i] == match)
++          return (i);
++    
++    for (i = 0; i < 4; i++)
++    {
++      if ((dev->dev_topaddrvalid & (1 << i)) == 0)
++      {
++          MPRINTF (DBG_DEVICE, 2, "elan4mmu_alloc_topaddr: allocate slot %d for %x\n", i, match);
++
++          dev->dev_topaddrvalid |= (1 << i);
++          dev->dev_topaddr[i] = match;
++
++          pci_write_config_word (dev->dev_osdep.pdev, PCI_ELAN_TOPPHYSADDR(i), match);
++          return (i);
++      }
++    }
++
++    panic ("elan4mmu_alloc_topaddr: all topaddrs in use\n");
++    return (0);
++#endif
++}
++
++E4_uint64
++elan4mmu_phys2pte (ELAN4_DEV *dev, physaddr_t phys, unsigned perm)
++{
++    physaddr_t sdram_base = ioaddr2paddr (pci_resource_start (dev->dev_osdep.pdev, ELAN4_BAR_SDRAM));
++    physaddr_t sdram_top  = ioaddr2paddr (pci_resource_end (dev->dev_osdep.pdev, ELAN4_BAR_SDRAM));
++    physaddr_t regs_base  = ioaddr2paddr (pci_resource_start (dev->dev_osdep.pdev, ELAN4_BAR_REGISTERS));
++    physaddr_t regs_top   = ioaddr2paddr (pci_resource_end (dev->dev_osdep.pdev, ELAN4_BAR_REGISTERS));
++    int        iscommand;
++    E4_uint64  pte;
++    unsigned   type;
++    
++    if (phys >= sdram_base && phys <= sdram_top)
++    {
++      phys ^= sdram_base;
++      type  = PTE_SetPerm (perm);
++    }
++    else if (phys >= regs_base && phys < regs_top)
++    {
++      if (dev->dev_devinfo.dev_revision_id == PCI_REVISION_ID_ELAN4_REVA)
++          iscommand = (phys < (regs_base + ELAN4_REVA_REG_OFFSET));
++      else
++          iscommand = (phys < (regs_base + ELAN4_REVB_I2C_OFFSET));
++      
++      if (iscommand)
++      {
++          phys ^= regs_base;
++          type  = PTE_SetPerm (perm) | PTE_CommandQueue;
++      }
++      else
++      {
++          type = PTE_SetPerm (perm) | PTE_PciNotLocal;
++          // phys = phys2bus (phys);
++      }
++    }
++    else
++    {
++      type = PTE_SetPerm (perm) | PTE_PciNotLocal | dev->dev_pteval;
++
++#ifdef LINUX_SPARC
++      /* XXXX if not local pci bus, then or in the bypass bit */
++      phys |= 0xfffe000000000000;
++      type |= PTE_BigEndian;
++#endif
++
++
++#if defined(__alpha)
++      phys |= alpha_mv.pci_dac_offset;
++#endif
++    }
++
++    if ((type & PTE_PciNotLocal) == 0)
++      pte = (phys >> PTE_PADDR_SHIFT) | type;
++    else
++    {
++      unsigned topaddr = elan4mmu_alloc_topaddr (dev, phys, type);
++      
++      if (dev->dev_topaddrmode)
++          pte = (phys >> PTE_PADDR_SHIFT) | (type & ~0xc) | (topaddr << 2);
++      else
++          pte = ((phys >> PTE_PADDR_SHIFT) & ~PTE_TOPADDR_MASK) | (((E4_uint64) topaddr) << 45) | type;
++    }
++
++    return pte;
++}
++
++physaddr_t
++elan4mmu_pte2phys (ELAN4_DEV *dev, E4_uint64 pte)
++{
++    physaddr_t sdram_base = ioaddr2paddr (pci_resource_start (dev->dev_osdep.pdev, ELAN4_BAR_SDRAM));
++    physaddr_t regs_base  = ioaddr2paddr (pci_resource_start (dev->dev_osdep.pdev, ELAN4_BAR_REGISTERS));
++    physaddr_t phys;
++    
++    if (pte & PTE_PciNotLocal)
++    {
++      if (dev->dev_topaddrmode)
++          phys = ((physaddr_t)(dev->dev_topaddr[(pte >> 2) & 3] & 0xfffc) << 48) | ((pte & PTE_PPN_MASK) << PTE_PADDR_SHIFT);
++      else
++          phys = ((physaddr_t)(dev->dev_topaddr[(pte >> 45) & 3] & 0xffff) << 48)| ((pte & PTE_PPN_MASK & ~PTE_TOPADDR_MASK) << PTE_PADDR_SHIFT);
++
++#ifdef LINUX_SPARC    /* XXXX if not local pci bus, then or in the bypass bit */
++      phys ^= 0xfffe000000000000;
++#endif
++
++#if defined(__alpha)
++      phys ^= alpha_mv.pci_dac_offset;
++#endif
++      return phys;
++    }
++    
++    if (pte & PTE_CommandQueue)
++      return (regs_base | ((pte & PTE_PPN_MASK) << PTE_PADDR_SHIFT));
++    
++    /* sdram */
++    return (sdram_base | ((pte & PTE_PPN_MASK) << PTE_PADDR_SHIFT));
++}
++
++EXPORT_SYMBOL(elan4mmu_phys2pte);
++EXPORT_SYMBOL(elan4mmu_pte2phys);
+Index: linux-2.4.21/drivers/net/qsnet/elan4/neterr.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan4/neterr.c 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan4/neterr.c      2005-06-01 23:12:54.612437128 -0400
+@@ -0,0 +1,270 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: neterr.c,v 1.4.6.3 2004/11/05 13:11:17 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/neterr.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan4/sdram.h>
++#include <elan4/debug.h>
++#include <elan4/device.h>
++#include <elan4/commands.h>
++#include <elan4/trtype.h>
++#include <elan4/neterr.h>
++
++typedef struct neterr_inputq
++{
++    E4_InputQueue     inputq;                                 /* input queue */
++    E4_Event32                qevent;                                 /* input queue event */
++    E4_uint64         sent;                                   /* # messages sent (cq flow control)*/
++} NETERR_INPUTQ;
++
++#define NETERR_NSLOTS 64                                      /* single page of queue space (4Kb) */
++
++#define NETERR_RETRIES        16
++#define NETERR_CQ_SIZE        CQ_Size8K
++#define NETERR_CQ_MSGS        (CQ_Size(NETERR_CQ_SIZE) / (21*8))
++#define NETERR_VP_COUNT       64                                      /* this *must* be > NETERR_CQ_MSGS */
++#define NETERR_VP_BASE        1                                       /* use vp 1 upwards */
++
++void
++elan4_neterr_interrupt (ELAN4_DEV *dev, void *arg)
++{
++    E4_Addr          qfptr  = elan4_sdram_readq (dev, dev->dev_neterr_inputq + offsetof (NETERR_INPUTQ, inputq.q_fptr));
++    E4_Addr          qbptr  = elan4_sdram_readq (dev, dev->dev_neterr_inputq + offsetof (NETERR_INPUTQ, inputq.q_bptr));
++    E4_Addr          qfirst = DEVICE_NETERR_SLOTS_ADDR;
++    E4_Addr          qlast  = qfirst + (NETERR_NSLOTS-1) * ELAN4_NETERR_MSG_SIZE;
++    ELAN4_CQ        *cq     = dev->dev_neterr_intcq;
++    int              count  = 0;
++    ELAN4_CTXT      *ctxt;
++    ELAN4_NETERR_MSG msg;
++
++    while (qfptr != qbptr)
++    {
++      elan4_sdram_copyq_from_sdram (dev, dev->dev_neterr_slots + (qfptr - qfirst), &msg, ELAN4_NETERR_MSG_SIZE);
++
++      ctxt = elan4_networkctxt (dev, msg.msg_context);
++
++      if (ctxt != NULL && ctxt->ctxt_ops->op_neterrmsg)
++          ctxt->ctxt_ops->op_neterrmsg (ctxt, &msg);
++      else
++          PRINTF (DBG_DEVICE, DBG_NETERR, "elan4_neterr_interrupt: no process - sender %d.%d\n", msg.msg_sender.loc_node, msg.msg_sender.loc_context);
++
++      count++;
++
++      /* move on the from pointer */
++      qfptr = (qfptr == qlast) ? qfirst : qfptr + ELAN4_NETERR_MSG_SIZE;
++
++      elan4_sdram_writeq (dev, dev->dev_neterr_inputq + offsetof (NETERR_INPUTQ, inputq.q_fptr), qfptr);
++    }
++
++    if (count == 0)
++    {
++      printk ("elan4_neterr_interrupt: spurious\n");
++      return;
++    }
++
++    /* Issue the waitevent to the interrupt queue */
++    writeq (WAIT_EVENT_CMD | (DEVICE_NETERR_INPUTQ_ADDR + offsetof (NETERR_INPUTQ, qevent)),  cq->cq_mapping);
++    writeq (  E4_EVENT_INIT_VALUE (-32 * count, E4_EVENT_WRITE, E4_EVENT_DTYPE_LONG, 0),      cq->cq_mapping);
++    writeq (  DEVICE_NETERR_INTCQ_ADDR,                                                               cq->cq_mapping);
++    writeq (INTERRUPT_CMD | (dev->dev_neterr_intop.op_cookie << E4_MAIN_INT_SHIFT),           cq->cq_mapping);
++
++    pioflush_reg (dev);
++}
++
++int
++elan4_neterr_init (ELAN4_DEV *dev)
++{
++    unsigned int intqaddr;
++    E4_Addr    qfirst, qlast;
++    
++    if ((dev->dev_neterr_inputq = elan4_sdram_alloc (dev, SDRAM_PAGE_SIZE)) == 0)
++      return 0;
++
++    if ((dev->dev_neterr_slots = elan4_sdram_alloc (dev, roundup (NETERR_NSLOTS * ELAN4_NETERR_MSG_SIZE, SDRAM_PAGE_SIZE))) == 0)
++      return 0;
++
++    if ((dev->dev_neterr_msgcq = elan4_alloccq (&dev->dev_ctxt, NETERR_CQ_SIZE, CQ_STENEnableBit | CQ_WriteEnableBit, CQ_Priority)) == NULL)
++      return 0;
++
++    if ((dev->dev_neterr_intcq = elan4_alloccq (&dev->dev_ctxt, CQ_Size1K, CQ_WaitEventEnableBit | CQ_InterruptEnableBit, CQ_Priority)) == NULL)
++      return 0;
++
++    intqaddr = (dev->dev_cqoffset + elan4_cq2num (dev->dev_neterr_intcq)) * CQ_CommandMappingSize;
++    qfirst   = DEVICE_NETERR_SLOTS_ADDR;
++    qlast    = qfirst + (NETERR_NSLOTS-1) * ELAN4_NETERR_MSG_SIZE;
++
++    spin_lock_init (&dev->dev_neterr_lock);
++
++    /* Register an interrupt operation */
++    dev->dev_neterr_intop.op_function = elan4_neterr_interrupt;
++    dev->dev_neterr_intop.op_arg      = NULL;
++
++    elan4_register_intop (dev, &dev->dev_neterr_intop);
++
++    /* Initialise the inputq descriptor and event */
++    elan4_sdram_writeq (dev, dev->dev_neterr_inputq + offsetof (NETERR_INPUTQ, inputq.q_fptr), qfirst);
++    elan4_sdram_writeq (dev, dev->dev_neterr_inputq + offsetof (NETERR_INPUTQ, inputq.q_bptr), qfirst);
++    elan4_sdram_writeq (dev, dev->dev_neterr_inputq + offsetof (NETERR_INPUTQ, inputq.q_control), E4_InputQueueControl (qfirst, qlast, ELAN4_NETERR_MSG_SIZE));
++    elan4_sdram_writeq (dev, dev->dev_neterr_inputq + offsetof (NETERR_INPUTQ, inputq.q_event), DEVICE_NETERR_INPUTQ_ADDR + offsetof (NETERR_INPUTQ, qevent));
++    
++    elan4_sdram_writeq (dev, dev->dev_neterr_inputq + offsetof (NETERR_INPUTQ, qevent.ev_CountAndType), E4_EVENT_INIT_VALUE (-32, E4_EVENT_WRITE, E4_EVENT_DTYPE_LONG, 0));
++    elan4_sdram_writeq (dev, dev->dev_neterr_inputq + offsetof (NETERR_INPUTQ, qevent.ev_WritePtr), DEVICE_NETERR_INTCQ_ADDR);
++    elan4_sdram_writeq (dev, dev->dev_neterr_inputq + offsetof (NETERR_INPUTQ, qevent.ev_WriteValue), (dev->dev_neterr_intop.op_cookie << E4_MAIN_INT_SHIFT) | INTERRUPT_CMD);
++
++    elan4_sdram_writeq (dev, dev->dev_neterr_inputq + offsetof (NETERR_INPUTQ, sent), 0);
++
++    /* Map them all into the device context */
++    elan4mmu_pteload (&dev->dev_ctxt, 0, DEVICE_NETERR_INPUTQ_ADDR, (dev->dev_neterr_inputq >> PTE_PADDR_SHIFT) | PTE_SetPerm(PERM_RemoteAll));
++    elan4mmu_pteload (&dev->dev_ctxt, 0, DEVICE_NETERR_INTCQ_ADDR,  (intqaddr >> PTE_PADDR_SHIFT) | PTE_SetPerm(PERM_LocDataWrite) | PTE_CommandQueue);
++    elan4mmu_pteload (&dev->dev_ctxt, 0, DEVICE_NETERR_SLOTS_ADDR, (dev->dev_neterr_slots >> PTE_PADDR_SHIFT) | PTE_SetPerm(PERM_DataReadWrite));
++
++    /* finally attach to the neterr context */
++    if (elan4_attach_filter (&dev->dev_ctxt, ELAN4_NETERR_CONTEXT_NUM) != 0)
++      panic ("elan4_neterr_init: failed to attach to neterr context\n");
++
++    /* and drop the context filter */
++    elan4_set_filter (&dev->dev_ctxt, ELAN4_NETERR_CONTEXT_NUM, E4_FILTER_HIGH_PRI);
++
++    return 1;
++}
++
++void
++elan4_neterr_destroy (ELAN4_DEV *dev)
++{
++    if (dev->dev_neterr_intcq)
++    {
++      elan4_detach_filter (&dev->dev_ctxt, ELAN4_NETERR_CONTEXT_NUM);
++      
++      elan4mmu_unload_range (&dev->dev_ctxt, 0, DEVICE_NETERR_SLOTS_ADDR,  1 << dev->dev_pageshift[0]);
++      elan4mmu_unload_range (&dev->dev_ctxt, 0, DEVICE_NETERR_INTCQ_ADDR,  1 << dev->dev_pageshift[0]);
++      elan4mmu_unload_range (&dev->dev_ctxt, 0, DEVICE_NETERR_INPUTQ_ADDR, 1 << dev->dev_pageshift[0]);
++
++      spin_lock_destroy (&dev->dev_neterr_lock);
++    }
++
++    if (dev->dev_neterr_intcq)
++      elan4_freecq (&dev->dev_ctxt, dev->dev_neterr_intcq);
++    dev->dev_neterr_intcq = NULL;
++
++    if (dev->dev_neterr_msgcq)
++      elan4_freecq (&dev->dev_ctxt, dev->dev_neterr_msgcq);
++    dev->dev_neterr_msgcq = NULL;
++
++    if (dev->dev_neterr_slots)
++      elan4_sdram_free (dev, dev->dev_neterr_slots, roundup (NETERR_NSLOTS * ELAN4_NETERR_MSG_SIZE, SDRAM_PAGE_SIZE));
++    dev->dev_neterr_slots = 0;
++    
++    if (dev->dev_neterr_inputq)
++      elan4_sdram_free (dev, dev->dev_neterr_inputq, SDRAM_PAGE_SIZE);
++    dev->dev_neterr_inputq = 0;
++}
++
++int
++elan4_neterr_sendmsg (ELAN4_DEV *dev, unsigned int nodeid, unsigned int retries, ELAN4_NETERR_MSG *msg)
++{
++    ELAN4_CQ  *cq = dev->dev_neterr_msgcq;
++    E4_uint64  sent;
++    E4_VirtualProcessEntry route;
++    unsigned int vp;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->dev_neterr_lock, flags);
++
++    sent = elan4_sdram_readq (dev, dev->dev_neterr_inputq + offsetof (NETERR_INPUTQ, sent));
++
++    PRINTF (DBG_DEVICE, DBG_NETERR, "elan4_neterr_sendmsg: nodeid=%d retries=%d cookie=%llx sender=%d,%d%s\n", 
++          nodeid, retries, msg->msg_cookies[0], msg->msg_sender.loc_node, msg->msg_sender.loc_context,
++          (dev->dev_neterr_queued - sent) >= NETERR_CQ_MSGS ? " - no cq space" : "");
++
++    if ((dev->dev_neterr_queued - sent) >= NETERR_CQ_MSGS)
++    {
++      spin_unlock_irqrestore (&dev->dev_neterr_lock, flags);
++      return 0;
++    }
++
++    vp = NETERR_VP_BASE + (dev->dev_neterr_queued % NETERR_VP_COUNT);
++
++    if (elan4_generate_route (&dev->dev_position, &route, ELAN4_NETERR_CONTEXT_NUM, nodeid, nodeid, FIRST_SYSTEM_PACKET | FIRST_HIGH_PRI) < 0)
++    {
++      spin_unlock_irqrestore (&dev->dev_neterr_lock, flags);
++      return 0;
++    }
++
++    elan4_write_route (dev, dev->dev_routetable, vp, &route);
++
++    writeq ((GUARD_CMD | GUARD_CHANNEL(0) | GUARD_RESET(retries)),                            cq->cq_mapping);
++    writeq (NOP_CMD,                                                                          cq->cq_mapping);
++    
++    writeq (OPEN_STEN_PKT_CMD | OPEN_PACKET (0, PACK_OK | RESTART_COUNT_ZERO, vp),            cq->cq_mapping);
++    writeq (SEND_TRANS_CMD | (TR_INPUT_Q_GETINDEX << 16),                                     cq->cq_mapping);
++    writeq (  DEVICE_NETERR_INPUTQ_ADDR + offsetof (NETERR_INPUTQ, inputq),                   cq->cq_mapping);
++
++    writeq (SEND_TRANS_CMD | (TR_WRITE (64 >> 3, 0, TR_DATATYPE_DWORD) << 16),                        cq->cq_mapping);
++    writeq (  0 /* address */,                                                                        cq->cq_mapping);
++    writeq (  ((E4_uint64 *) msg)[0],                                                         cq->cq_mapping);
++    writeq (  ((E4_uint64 *) msg)[1],                                                         cq->cq_mapping);
++    writeq (  ((E4_uint64 *) msg)[2],                                                         cq->cq_mapping);
++    writeq (  ((E4_uint64 *) msg)[3],                                                         cq->cq_mapping);
++    writeq (  ((E4_uint64 *) msg)[4],                                                         cq->cq_mapping);
++    writeq (  ((E4_uint64 *) msg)[5],                                                         cq->cq_mapping);
++    writeq (  ((E4_uint64 *) msg)[6],                                                         cq->cq_mapping);
++    writeq (  ((E4_uint64 *) msg)[7],                                                         cq->cq_mapping);
++
++    writeq (SEND_TRANS_CMD | (TR_INPUT_Q_COMMIT << 16),                                               cq->cq_mapping);
++    writeq (  DEVICE_NETERR_INPUTQ_ADDR + offsetof (NETERR_INPUTQ, inputq),                   cq->cq_mapping);
++    writeq (  0 /* cookie */,                                                                 cq->cq_mapping);
++    
++    writeq (GUARD_CMD | GUARD_CHANNEL(0) | GUARD_RESET(NETERR_RETRIES),                               cq->cq_mapping);
++    writeq (WRITE_DWORD_CMD | (DEVICE_NETERR_INPUTQ_ADDR + offsetof (NETERR_INPUTQ, sent)),   cq->cq_mapping);
++    writeq (  ++dev->dev_neterr_queued,                                                               cq->cq_mapping);
++
++    pioflush_reg (dev);
++
++    spin_unlock_irqrestore (&dev->dev_neterr_lock, flags);
++
++    return 1;
++}
++
++int
++elan4_neterr_iproc_trap (ELAN4_DEV *dev, ELAN4_IPROC_TRAP *trap)
++{
++    E4_IprocTrapHeader *hdrp = &trap->tr_transactions[trap->tr_trappedTrans];
++    unsigned long flags;
++
++    switch (IPROC_TrapValue (hdrp->IProcStatusCntxAndTrType))
++    {
++    case InputEopErrorOnWaitForEop:
++    case InputEopErrorTrap:
++    case InputCrcErrorAfterPAckOk:
++      return 1;
++
++    case InputEventEngineTrapped:
++      printk ("elan%d: device_iproc_trap: InputEventEngineTrapped - Trans=%x TrAddr=%llx\n", 
++              dev->dev_instance, (int)IPROC_TransactionType (hdrp->IProcStatusCntxAndTrType), (long long) hdrp->TrAddr);
++
++      if ((IPROC_TransactionType (hdrp->IProcStatusCntxAndTrType) & TR_OPCODE_MASK) == (TR_INPUT_Q_COMMIT & TR_OPCODE_MASK) &&
++          hdrp->TrAddr == DEVICE_NETERR_INPUTQ_ADDR + offsetof (NETERR_INPUTQ, inputq))
++      {
++          spin_lock_irqsave (&dev->dev_neterr_lock, flags);
++          writeq ((DEVICE_NETERR_INPUTQ_ADDR + offsetof (NETERR_INPUTQ, qevent)) | SET_EVENT_CMD, dev->dev_neterr_msgcq->cq_mapping);
++          spin_unlock_irqrestore (&dev->dev_neterr_lock, flags);
++          return 1;
++      }
++      
++    default:
++      return 0;
++    }
++}
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan4/procfs_Linux.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan4/procfs_Linux.c   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan4/procfs_Linux.c        2005-06-01 23:12:54.613436976 -0400
+@@ -0,0 +1,1041 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: procfs_Linux.c,v 1.27.2.5 2005/01/18 14:36:17 david Exp $ $Name: QSNETMODULES-4-30_20050128 $"
++/*      $Source: /cvs/master/quadrics/elan4mod/procfs_Linux.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <linux/module.h>
++#include <linux/proc_fs.h>
++#include <linux/ctype.h>
++
++#include <qsnet/procfs_linux.h>
++
++#include <elan4/i2c.h>
++#include <elan4/debug.h>
++#include <elan4/device.h>
++#include <elan4/user.h>
++
++/*
++ *
++ * procfs format for elan4:
++ *
++ * /proc/qsnet/elan4/config
++ *    elan4_debug
++ *    elan4_debug_toconsole
++ *    elan4_debug_tobuffer
++ *    elan4_debug_display_ctxt
++ *    elan4_debug_ignore_ctxt
++ *    elan4_debug_ignore_type
++ *    elan4_debug_mmu
++ *    elan4_mainint_punt_loops
++ *    user_p2p_route_options
++ *    user_bcast_route_options
++ *
++ * /proc/qsnet/elan4/deviceN
++ *    stats
++ *    position
++ *    vpd
++ */
++
++struct proc_dir_entry *elan4_procfs_root;
++struct proc_dir_entry *elan4_config_root;
++
++/* borrowed from fs/proc/proc_misc - helper for proc_read_int */
++static int 
++proc_calc_metrics(char *page, char **start, off_t off, int count, int *eof, int len)
++{
++    if (len <= off+count) *eof = 1;
++    *start = page + off;
++    len -= off;
++    if (len>count) len = count;
++    if (len<0) len = 0;
++    return len;
++}
++
++static int
++proc_read_devinfo (char *page, char **start, off_t off,
++                  int count, int *eof, void *data)
++{
++    ELAN4_DEV *dev = (ELAN4_DEV *) data;
++    int        len = 0;
++
++    if (! dev)
++      len = sprintf (page, "<unknown>\n");
++    else
++    {
++      len += sprintf (page + len, "dev_vendor_id            0x%x\n", dev->dev_devinfo.dev_vendor_id);
++      len += sprintf (page + len, "dev_device_id            0x%x\n", dev->dev_devinfo.dev_vendor_id);
++      len += sprintf (page + len, "dev_revision_id          0x%x\n", dev->dev_devinfo.dev_revision_id);
++      len += sprintf (page + len, "dev_instance             0x%x\n", dev->dev_devinfo.dev_instance);
++      len += sprintf (page + len, "dev_rail                 0x%x\n", dev->dev_devinfo.dev_rail);
++      len += sprintf (page + len, "dev_driver_version       0x%x\n", dev->dev_devinfo.dev_driver_version);
++      len += sprintf (page + len, "dev_params_mask          0x%x\n", dev->dev_devinfo.dev_params_mask);
++      len += sprintf (page + len, "dev_params:                  \n");
++      len += sprintf (page + len, " 0  - PciCmdQPadFlag     0x%x\n", dev->dev_devinfo.dev_params.values[0]);
++      len += sprintf (page + len, " 1  - EventCopyWinPt     0x%x\n", dev->dev_devinfo.dev_params.values[1]);
++      len += sprintf (page + len, " 2  - PciWriteCombining  0x%x\n", dev->dev_devinfo.dev_params.values[2]);
++      len += sprintf (page + len, " 3  -                    0x%x\n", dev->dev_devinfo.dev_params.values[3]);
++      len += sprintf (page + len, " 4  -                    0x%x\n", dev->dev_devinfo.dev_params.values[4]);
++      len += sprintf (page + len, " 5  -                    0x%x\n", dev->dev_devinfo.dev_params.values[5]);
++      len += sprintf (page + len, " 6  -                    0x%x\n", dev->dev_devinfo.dev_params.values[6]);
++      len += sprintf (page + len, " 7  -                    0x%x\n", dev->dev_devinfo.dev_params.values[7]);
++      len += sprintf (page + len, " 8  -                    0x%x\n", dev->dev_devinfo.dev_params.values[8]);
++      len += sprintf (page + len, " 9  -                    0x%x\n", dev->dev_devinfo.dev_params.values[9]);
++      len += sprintf (page + len, " 10 -                    0x%x\n", dev->dev_devinfo.dev_params.values[10]);
++      len += sprintf (page + len, " 11 -                    0x%x\n", dev->dev_devinfo.dev_params.values[11]);
++      len += sprintf (page + len, "dev_num_down_links_value 0x%x\n", dev->dev_devinfo.dev_num_down_links_value);
++
++      len += sprintf (page + len, "features                 0x%x\n", dev->dev_features);
++    }
++
++    return (qsnet_proc_calc_metrics (page, start, off, count, eof, len));
++}
++
++static int
++proc_read_position (char *page, char **start, off_t off,
++                  int count, int *eof, void *data)
++{
++    ELAN4_DEV *dev = (ELAN4_DEV *) data;
++    int        len;
++
++    if (dev->dev_position.pos_mode == ELAN_POS_UNKNOWN)
++      len = sprintf (page, "<unknown>\n");
++    else
++      len = sprintf (page, 
++                     "NodeId                 %d\n"
++                     "NumLevels              %d\n"
++                     "NumNodes               %d\n",
++                     dev->dev_position.pos_nodeid, 
++                     dev->dev_position.pos_levels, 
++                     dev->dev_position.pos_nodes);
++
++    return (qsnet_proc_calc_metrics (page, start, off, count, eof, len));
++}
++
++static int
++proc_write_position (struct file *file, const char *buf, unsigned long count, void *data)
++{
++    ELAN4_DEV *dev     = (ELAN4_DEV *) data;
++    unsigned  nodeid   = ELAN_INVALID_NODE;
++    unsigned  numnodes = 0;
++    char     *page, *p;
++    int       res;
++    ELAN_POSITION pos;
++
++    if (count == 0)
++      return (0);
++
++    if (count >= PAGE_SIZE)
++      return (-EINVAL);
++
++    if ((page = (char *) __get_free_page (GFP_KERNEL)) == NULL)
++      return (-ENOMEM);
++
++    MOD_INC_USE_COUNT;
++
++    if (copy_from_user (page, buf, count))
++      res = -EFAULT;
++    else
++    {
++      page[count] = '\0';
++      
++      if (page[count-1] == '\n')
++          page[count-1] = '\0';
++
++      if (! strcmp (page, "<unknown>"))
++      {
++          pos.pos_mode      = ELAN_POS_UNKNOWN;
++          pos.pos_nodeid    = ELAN_INVALID_NODE;
++          pos.pos_nodes     = 0;
++          pos.pos_levels    = 0;
++      }
++      else
++      {
++          for (p = page; *p; )
++          {
++              while (isspace (*p))
++                  p++;
++              
++              if (! strncmp (p, "NodeId=", strlen("NodeId=")))
++                  nodeid   = simple_strtoul (p + strlen ("NodeId="), NULL, 0);
++              if (! strncmp (p, "NumNodes=", strlen ("NumNodes=")))
++                  numnodes = simple_strtoul (p + strlen ("NumNodes="), NULL, 0);
++              
++              while (*p && !isspace(*p))
++                  p++;
++          }
++
++          if (elan4_compute_position (&pos, nodeid, numnodes, dev->dev_devinfo.dev_num_down_links_value) != 0)
++              printk ("elan%d: invalid values for NodeId=%d NumNodes=%d\n", dev->dev_instance, nodeid, numnodes);
++          else
++          {
++              printk ("elan%d: setting NodeId=%d NumNodes=%d NumLevels=%d\n", dev->dev_instance, pos.pos_nodeid,
++                      pos.pos_nodes, pos.pos_levels);
++
++              if (elan4_set_position (dev, &pos) < 0)
++                  printk ("elan%d: failed to set device position\n", dev->dev_instance);
++          }
++      }
++    }
++
++    MOD_DEC_USE_COUNT;
++    free_page ((unsigned long) page);
++
++    return (count);
++}
++
++static int
++proc_read_temp (char *page, char **start, off_t off,
++              int count, int *eof, void *data)
++{
++    ELAN4_DEV    *dev = (ELAN4_DEV *) data;
++    unsigned char values[2];
++    int           len;
++
++    if (i2c_disable_auto_led_update (dev) < 0)
++      len = sprintf (page, "<unknown>");
++    else
++    {
++      if (i2c_read (dev, I2C_TEMP_ADDR, 2, values) < 0)
++          len = sprintf (page, "<not-present>");
++      else
++          len = sprintf (page, "%s%d%s\n", (values[0] & 0x80) ? "-" : "",
++                         (values[0] & 0x80) ? -((signed char)values[0]) - 1 : values[0],
++                         (values[1] & 0x80) ? ".5" : ".0");
++
++      i2c_enable_auto_led_update (dev);
++    }
++
++    return (qsnet_proc_calc_metrics (page, start, off, count, eof, len));
++}
++
++static int
++proc_read_eccerr (char *page, char **start, off_t off,
++                int count, int *eof, void *data)
++{
++    ELAN4_DEV    *dev = (ELAN4_DEV *) data;
++    char          errstr[200];
++    register int  i, len = 0;
++
++    *page = '\0';
++
++    for (i = 0; i < sizeof (dev->dev_sdramerrs)/sizeof(dev->dev_sdramerrs[0]); i++)
++      if (dev->dev_sdramerrs[i])
++          len += sprintf (page + len, "%s occured %0d times\n",
++                          elan4_sdramerr2str (dev, dev->dev_sdramerrs[i] & 0x000fffffffffffffULL, errstr),
++                          (int) (dev->dev_sdramerrs[i] >> 52) + 1);
++
++    return (qsnet_proc_calc_metrics (page, start, off, count, eof, len));
++}
++
++static int
++proc_read_vpd (char *page, char **start, off_t off,
++             int count, int *eof, void *data)
++{
++    ELAN4_DEV *dev = (ELAN4_DEV *) data;
++    int        len;
++
++    if ( elan4_read_vpd (dev, NULL, page) )
++      len = sprintf (page, "no vpd tags found\n");
++    else
++      len = strlen(page)+1;
++
++    return (qsnet_proc_calc_metrics (page, start, off, count, eof, len));
++}
++
++static struct device_info 
++{
++    char *name;
++    int (*read_func) (char *page, char **start, off_t off, int count, int *eof, void *data);
++    int (*write_func) (struct file *file, const char *buf, unsigned long count, void *data);
++    unsigned minrev;
++} device_info[] = {
++    {"devinfo",   proc_read_devinfo,  NULL,                0},
++    {"position",  proc_read_position, proc_write_position, 0},
++    {"temp",      proc_read_temp,     NULL,                1},
++    {"eccerr",    proc_read_eccerr,   NULL,                0},
++    {"vpd",       proc_read_vpd,      NULL,                0},
++};
++
++static int
++proc_read_link_stats (char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++    ELAN4_DEV *dev = (ELAN4_DEV *) data;
++    char      *p   = page;
++
++    p += sprintf (p, "%20s %ld\n", "link_errors", dev->dev_stats.s_link_errors);
++    p += sprintf (p, "%20s %ld\n", "lock_errors", dev->dev_stats.s_lock_errors);
++    p += sprintf (p, "%20s %ld\n", "deskew_errors", dev->dev_stats.s_deskew_errors);
++    p += sprintf (p, "%20s %ld\n", "phase_errors", dev->dev_stats.s_phase_errors);
++
++    p += sprintf (p, "%20s %ld\n", "data_errors", dev->dev_stats.s_data_errors);
++    p += sprintf (p, "%20s %ld\n", "fifo_overflow0", dev->dev_stats.s_fifo_overflow0);
++    p += sprintf (p, "%20s %ld\n", "fifo_overflow1", dev->dev_stats.s_fifo_overflow1);
++    p += sprintf (p, "%20s %ld\n", "mod45changed", dev->dev_stats.s_mod45changed);
++    p += sprintf (p, "%20s %ld\n", "pack_not_seen", dev->dev_stats.s_pack_not_seen);
++
++    p += sprintf (p, "%20s %ld\n", "linkport_keyfail", dev->dev_stats.s_linkport_keyfail);
++    p += sprintf (p, "%20s %ld\n", "eop_reset", dev->dev_stats.s_eop_reset);
++    p += sprintf (p, "%20s %ld\n", "bad_length", dev->dev_stats.s_bad_length);
++    p += sprintf (p, "%20s %ld\n", "crc_error", dev->dev_stats.s_crc_error);
++    p += sprintf (p, "%20s %ld\n", "crc_bad", dev->dev_stats.s_crc_bad);
++
++    p += sprintf (p, "%20s %ld\n", "cproc_timeout", dev->dev_stats.s_cproc_timeout);
++    p += sprintf (p, "%20s %ld\n", "dproc_timeout", dev->dev_stats.s_dproc_timeout);
++
++    return (proc_calc_metrics (page, start, off, count, eof, p - page));
++}
++
++static char *
++proc_sprintf_bucket_stat (char *p, char *name, unsigned long *stats, int *buckets)
++{
++    int i;
++
++    p += sprintf (p, "%20s ", name);
++
++    for (i = 0; i < ELAN4_DEV_STATS_BUCKETS-1; i++)
++      p += sprintf (p, "%ld(<=%d) ", stats[i], buckets[i]);
++    p += sprintf (p, "%ld(>%d)\n", stats[i], buckets[i-1]);
++
++    return p;
++}
++
++static int
++proc_read_intr_stats (char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++    ELAN4_DEV *dev = (ELAN4_DEV *) data;
++    char      *p   = page;
++
++    p += sprintf (p, "%20s %ld\n", "interrupts",       dev->dev_stats.s_interrupts);
++    p += sprintf (p, "%20s %ld\n", "haltints",         dev->dev_stats.s_haltints);
++
++    p += sprintf (p, "%20s %ld\n", "mainint_punts",    dev->dev_stats.s_mainint_punts);
++    p += sprintf (p, "%20s %ld\n", "mainint_rescheds", dev->dev_stats.s_mainint_rescheds);
++
++    p  = proc_sprintf_bucket_stat (p, "mainints", dev->dev_stats.s_mainints, MainIntBuckets);
++
++    return (proc_calc_metrics (page, start, off, count, eof, p - page));
++}
++
++static int
++proc_read_trap_stats (char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++    ELAN4_DEV *dev = (ELAN4_DEV *) data;
++    char      *p   = page;
++
++    p += sprintf (p, "%20s %ld\n", "cproc_traps", dev->dev_stats.s_cproc_traps);
++    p += sprintf (p, "%20s %ld\n", "dproc_traps", dev->dev_stats.s_dproc_traps);
++    p += sprintf (p, "%20s %ld\n", "eproc_traps", dev->dev_stats.s_eproc_traps);
++    p += sprintf (p, "%20s %ld\n", "iproc_traps", dev->dev_stats.s_iproc_traps);
++    p += sprintf (p, "%20s %ld\n", "tproc_traps", dev->dev_stats.s_tproc_traps);
++
++    return (proc_calc_metrics (page, start, off, count, eof, p - page));
++}
++
++static int
++proc_read_cproc_trap_stats (char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++    ELAN4_DEV *dev = (ELAN4_DEV *) data;
++    char      *p   = page;
++    int        i;
++    extern char *const CProcTrapNames[];
++
++    for (i = 0; i < sizeof (dev->dev_stats.s_cproc_trap_types)/sizeof(dev->dev_stats.s_cproc_trap_types[0]); i++)
++      p += sprintf (p, "%-40s %ld\n", CProcTrapNames[i], dev->dev_stats.s_cproc_trap_types[i]);
++
++    return (proc_calc_metrics (page, start, off, count, eof, p - page));
++}
++
++static int
++proc_read_dproc_trap_stats (char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++    ELAN4_DEV *dev = (ELAN4_DEV *) data;
++    char      *p   = page;
++    int        i;
++    extern char *const DProcTrapNames[];
++
++    for (i = 0; i < sizeof (dev->dev_stats.s_dproc_trap_types)/sizeof(dev->dev_stats.s_dproc_trap_types[0]); i++)
++      p += sprintf (p, "%-40s %ld\n", DProcTrapNames[i], dev->dev_stats.s_dproc_trap_types[i]);
++
++    return (proc_calc_metrics (page, start, off, count, eof, p - page));
++}
++
++static int
++proc_read_eproc_trap_stats (char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++    ELAN4_DEV *dev = (ELAN4_DEV *) data;
++    char      *p   = page;
++    int        i;
++    extern char *const EProcTrapNames[];
++
++    for (i = 0; i < sizeof (dev->dev_stats.s_eproc_trap_types)/sizeof(dev->dev_stats.s_eproc_trap_types[0]); i++)
++      p += sprintf (p, "%-40s %ld\n", EProcTrapNames[i], dev->dev_stats.s_eproc_trap_types[i]);
++
++    return (proc_calc_metrics (page, start, off, count, eof, p - page));
++}
++
++static int
++proc_read_iproc_trap_stats (char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++    ELAN4_DEV *dev = (ELAN4_DEV *) data;
++    char      *p   = page;
++    int        i;
++    extern char *const IProcTrapNames[];
++
++    for (i = 0; i < sizeof (dev->dev_stats.s_iproc_trap_types)/sizeof(dev->dev_stats.s_iproc_trap_types[0]); i++)
++      p += sprintf (p, "%-40s %ld\n", IProcTrapNames[i], dev->dev_stats.s_iproc_trap_types[i]);
++
++    return (proc_calc_metrics (page, start, off, count, eof, p - page));
++}
++
++static int
++proc_read_tproc_trap_stats (char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++    ELAN4_DEV *dev = (ELAN4_DEV *) data;
++    char      *p   = page;
++    int        i;
++    extern char *const TProcTrapNames[];
++
++    for (i = 0; i < sizeof (dev->dev_stats.s_tproc_trap_types)/sizeof(dev->dev_stats.s_tproc_trap_types[0]); i++)
++      p += sprintf (p, "%-40s %ld\n", TProcTrapNames[i], dev->dev_stats.s_tproc_trap_types[i]);
++
++    return (proc_calc_metrics (page, start, off, count, eof, p - page));
++}
++
++static int
++proc_read_sdram_stats (char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++    ELAN4_DEV *dev = (ELAN4_DEV *) data;
++    char      *p   = page;
++
++    p += sprintf (p, "%20s %ld\n",  "correctable_errors", dev->dev_stats.s_correctable_errors);
++    p += sprintf (p, "%20s %ld\n",  "multiple_errors",    dev->dev_stats.s_multiple_errors);
++    p += sprintf (p, "%20s %ldK\n", "sdram_bytes_free",   dev->dev_stats.s_sdram_bytes_free/1024);
++
++    return (proc_calc_metrics (page, start, off, count, eof, p - page));
++}
++
++void
++elan4_ringbuf_store(ELAN4_ROUTE_RINGBUF *ringbuf, E4_VirtualProcessEntry *route, ELAN4_DEV *dev)
++{
++    int newend;
++    unsigned long flags;
++
++    spin_lock_irqsave(&dev->dev_error_routes_lock, flags);
++    bcopy(route, &ringbuf->routes[ringbuf->end], sizeof(E4_VirtualProcessEntry));
++    newend = ringbuf->end + 1;
++    if (newend >= DEV_STASH_ROUTE_COUNT)
++        newend -= DEV_STASH_ROUTE_COUNT;
++    if (newend == ringbuf->start)
++        ringbuf->start += 1;
++    if (ringbuf->start >= DEV_STASH_ROUTE_COUNT)
++        ringbuf->start -= DEV_STASH_ROUTE_COUNT;
++    ringbuf->end = newend;
++    spin_unlock_irqrestore(&dev->dev_error_routes_lock, flags);
++}
++      
++static int
++proc_read_dproc_timeout_stats (char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++      ELAN4_DEV *dev = (ELAN4_DEV *) data;
++      char      *p   = page;
++      unsigned int *dproc_timeout;
++
++      dproc_timeout = dev->dev_dproc_timeout;
++
++      if (!dproc_timeout) 
++              p += sprintf (p, "No stats available\n");
++      else
++      {
++              int i;
++
++              for (i=0; i<dev->dev_position.pos_nodes; i++) 
++                      if (dproc_timeout[i] != 0) 
++                              p += sprintf (p, "Node %d: %u errors\n", i, dproc_timeout[i]);
++      }
++
++      return (proc_calc_metrics (page, start, off, count, eof, p - page));
++}
++
++static int
++elan4_route2str (E4_VirtualProcessEntry *route, char *routeStr)
++{
++    int        part = 0;
++    int        shift;
++    int        broadcast;
++    E4_uint64  value;
++    char      *ptr = routeStr;
++    int        b;
++
++    /* unpack first */
++    value = route->Values[part] & 0x7f;
++    if ( (value & 0x78) == 0) {
++        /* empty route */
++        strcpy(routeStr,"Invalid lead route");
++        return (-EINVAL);
++    }
++
++    if ( value & 0x40 ) {
++        /* broad cast */
++      strcpy(routeStr,"Broadcast");
++      return (-EINVAL);
++    } else {
++        switch ((value  & 0x30) >> 4) {
++        case 0: { *ptr++ = '0' + (value & 0x7); break; }
++        case 1: { *ptr++ = 'M';                 break; }
++        case 2: { *ptr++ = 'U';                 break; }
++        case 3: { *ptr++ = 'A';                 break; }
++        }
++    }
++
++    shift = 16;
++    broadcast = 0;
++    while ( 1 ) {
++        b =  (route->Values[part] >> shift) & 0xf;
++
++        if ( broadcast ) {
++            /* about to pick up the second byte of a broadcast pair */
++            broadcast = 0;
++        } else {
++            if ( b & 0x8) {
++                /*  output link */
++                 *ptr++ = '0' + (b & 0x7);
++            } else {
++                if ( b & 0x4) {
++                    /* broad cast */
++                    broadcast = 1;
++                } else {
++                    switch ( b & 0x3 ) {
++                    case 0: { *ptr++ =  0 ; return (0);     break; }
++                    case 1: { *ptr++ = 'M';                 break; }
++                    case 2: { *ptr++ = 'U';                 break; }
++                    case 3: { *ptr++ = 'A';                 break; }
++                    }
++                }
++            }
++        }
++
++        shift += 4; 
++        if ( part != 0 ) {
++            if ( shift > 36) {
++                /* too far, now in the crc value */
++                strcpy(routeStr,"Invalid route length");
++                return (-EINVAL);
++            }
++        } else { 
++            if ( shift >= 64) { 
++                /* move to the next 64 bits */
++                part = 1;
++                shift = 2;
++            }
++        }
++    }
++
++    /* never reached */
++    return (-EINVAL);
++}
++
++
++static int
++proc_read_dproc_timeout_routes (char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++      ELAN4_DEV *dev = (ELAN4_DEV *) data;
++      char      *p   = page;
++      ELAN4_ROUTE_RINGBUF *ringbuf;
++      char      routestr[33];
++
++      ringbuf = &dev->dev_dproc_timeout_routes;
++
++      if (!ringbuf) 
++              p += sprintf (p, "No stats available\n");
++      else
++      {
++              int start;
++              int end;
++              int i;
++              unsigned long flags;
++
++              memset(&routestr, 0, 33);
++
++              spin_lock_irqsave(&dev->dev_error_routes_lock, flags);
++              
++              start = ringbuf->start;
++              end = ringbuf->end;
++
++              if (end < start)
++                      end = DEV_STASH_ROUTE_COUNT;
++
++              for (i=start; i<end; i++) 
++              {
++                      elan4_route2str (&ringbuf->routes[i], routestr);
++                      p += sprintf (p, "Route %llx %llx->%s\n", ringbuf->routes[i].Values[0], ringbuf->routes[i].Values[1], routestr);
++              }
++
++              if (ringbuf->end < start)
++              {
++                      start = 0;
++                      end = ringbuf->end;
++                      for (i=start; i<end; i++)
++                      {
++                              elan4_route2str (&ringbuf->routes[i], routestr);
++                              p += sprintf (p, "Route %llx %llx->%s\n", ringbuf->routes[i].Values[0], ringbuf->routes[i].Values[1], routestr);
++                      }
++              }
++
++              spin_unlock_irqrestore(&dev->dev_error_routes_lock, flags);
++      }
++
++      return (proc_calc_metrics (page, start, off, count, eof, p - page));
++}
++
++
++static int
++proc_read_cproc_timeout_stats (char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++      ELAN4_DEV *dev = (ELAN4_DEV *) data;
++      char      *p   = page;
++      unsigned int *cproc_timeout;
++
++      cproc_timeout = dev->dev_cproc_timeout;
++
++      if (!cproc_timeout) 
++              p += sprintf (p, "No stats available\n");
++      else
++      {
++              int i;
++
++              for (i=0; i<dev->dev_position.pos_nodes; i++) 
++                      if (cproc_timeout[i] != 0) 
++                              p += sprintf (p, "Node %d: %u errors\n", i, cproc_timeout[i]);
++      }
++
++      return (proc_calc_metrics (page, start, off, count, eof, p - page));
++}
++
++static int
++proc_read_cproc_timeout_routes (char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++      ELAN4_DEV *dev = (ELAN4_DEV *) data;
++      char      *p   = page;
++      ELAN4_ROUTE_RINGBUF *ringbuf;
++      char      routestr[33];
++
++      ringbuf = &dev->dev_cproc_timeout_routes;
++
++      if (!ringbuf) 
++              p += sprintf (p, "No stats available\n");
++      else
++      {
++              int start;
++              int end;
++              int i;
++              unsigned long flags;
++
++              memset(&routestr, 0, 33);
++
++              spin_lock_irqsave(&dev->dev_error_routes_lock, flags);
++              
++              start = ringbuf->start;
++              end = ringbuf->end;
++
++              if (end < start)
++                      end = DEV_STASH_ROUTE_COUNT;
++
++              for (i=start; i<end; i++) 
++              {
++                      elan4_route2str (&ringbuf->routes[i], routestr);
++                      p += sprintf (p, "Route %llx %llx->%s\n", ringbuf->routes[i].Values[0], ringbuf->routes[i].Values[1], routestr);
++              }
++
++              if (ringbuf->end < start)
++              {
++                      start = 0;
++                      end = ringbuf->end;
++                      for (i=start; i<end; i++)
++                      {
++                              elan4_route2str (&ringbuf->routes[i], routestr);
++                              p += sprintf (p, "Route %llx %llx->%s\n", ringbuf->routes[i].Values[0], ringbuf->routes[i].Values[1], routestr);
++                      }
++              }
++
++              spin_unlock_irqrestore(&dev->dev_error_routes_lock, flags);
++      }
++
++      return (proc_calc_metrics (page, start, off, count, eof, p - page));
++}
++
++static int
++proc_read_traperr_stats (char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++      ELAN4_DEV *dev = (ELAN4_DEV *) data;
++      char      *p   = page;
++      unsigned int *ack_errors;
++
++      ack_errors = dev->dev_ack_errors;
++
++      if (!ack_errors) 
++              p += sprintf (p, "No stats available\n");
++      else
++      {
++              int i;
++
++              for (i=0; i<dev->dev_position.pos_nodes; i++) 
++                      if (ack_errors[i] != 0) 
++                              p += sprintf (p, "Node %d: %u errors\n", i, ack_errors[i]);
++      }
++
++      return (proc_calc_metrics (page, start, off, count, eof, p - page));
++}
++
++static int
++proc_read_ackerror_routes (char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++      ELAN4_DEV *dev = (ELAN4_DEV *) data;
++      char      *p   = page;
++      ELAN4_ROUTE_RINGBUF *ringbuf;
++      char      routestr[33];
++
++      ringbuf = &dev->dev_ack_error_routes;
++
++      if (!ringbuf) 
++              p += sprintf (p, "No stats available\n");
++      else
++      {
++              int start;
++              int end;
++              int i;
++              unsigned long flags;
++
++              memset(&routestr, 0, 33);
++
++              spin_lock_irqsave(&dev->dev_error_routes_lock, flags);
++              
++              start = ringbuf->start;
++              end = ringbuf->end;
++
++              if (end < start)
++                      end = DEV_STASH_ROUTE_COUNT;
++
++              for (i=start; i<end; i++) 
++              {
++                      elan4_route2str (&ringbuf->routes[i], routestr);
++                      p += sprintf (p, "Route %llx %llx->%s\n", ringbuf->routes[i].Values[0], ringbuf->routes[i].Values[1], routestr);
++              }
++
++              if (ringbuf->end < start)
++              {
++                      start = 0;
++                      end = ringbuf->end;
++                      for (i=start; i<end; i++)
++                      {
++                              elan4_route2str (&ringbuf->routes[i], routestr);
++                              p += sprintf (p, "Route %llx %llx->%s\n", ringbuf->routes[i].Values[0], ringbuf->routes[i].Values[1], routestr);
++                      }
++              }
++
++              spin_unlock_irqrestore(&dev->dev_error_routes_lock, flags);
++      }
++
++      return (proc_calc_metrics (page, start, off, count, eof, p - page));
++}
++
++static struct stats_info 
++{
++    char *name;
++    int (*read_func) (char *page, char **start, off_t off, int count, int *eof, void *data);
++    int (*write_func) (struct file *file, const char *buf, unsigned long count, void *data); 
++} stats_info[] = {
++    {"link",     proc_read_link_stats, NULL},
++    {"intr",     proc_read_intr_stats, NULL},
++    {"trap",     proc_read_trap_stats, NULL},
++    {"cproc",    proc_read_cproc_trap_stats, NULL},
++    {"dproc",    proc_read_dproc_trap_stats, NULL},
++    {"eproc",    proc_read_eproc_trap_stats, NULL},
++    {"iproc",    proc_read_iproc_trap_stats, NULL},
++    {"tproc",    proc_read_tproc_trap_stats, NULL},
++    {"sdram",    proc_read_sdram_stats, NULL},
++    {"trapdmaerr", proc_read_traperr_stats, NULL},
++    {"dproctimeout", proc_read_dproc_timeout_stats, NULL},
++    {"cproctimeout", proc_read_cproc_timeout_stats, NULL},
++    {"dproctimeoutroutes", proc_read_dproc_timeout_routes, NULL},
++    {"cproctimeoutroutes", proc_read_cproc_timeout_routes, NULL},
++    {"ackerrroutes", proc_read_ackerror_routes, NULL},
++};
++
++static int
++proc_read_sysconfig (char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++    ELAN4_DEV *dev        = (ELAN4_DEV *) data;
++    E4_uint32  syscontrol = dev->dev_syscontrol;
++    int              len       = 0;
++
++   *eof = 1;
++   if (off != 0)
++      return (0);
++
++    if (syscontrol & CONT_EN_ALL_SETS)
++      len += sprintf (page + len, "%sEN_ALL_SETS", len == 0 ? "" : " ");
++    if (syscontrol & CONT_MMU_ENABLE)
++      len += sprintf (page + len, "%sMMU_ENABLE", len == 0 ? "" : " ");
++    if (syscontrol & CONT_CACHE_HASH_TABLE)
++      len += sprintf (page + len, "%sCACHE_HASH_TABLE", len == 0 ? "" : " ");
++    if (syscontrol & CONT_CACHE_CHAINS)
++      len += sprintf (page + len, "%sCACHE_CHAINS", len == 0 ? "" : " ");
++    if (syscontrol & CONT_CACHE_ROOT_CNTX)
++      len += sprintf (page + len, "%sCACHE_ROOT_CNTX", len == 0 ? "" : " ");
++    if (syscontrol & CONT_CACHE_STEN_ROUTES)
++      len += sprintf (page + len, "%sCACHE_STEN_ROUTES", len == 0 ? "" : " ");
++    if (syscontrol & CONT_CACHE_DMA_ROUTES)
++      len += sprintf (page + len, "%sCACHE_DMA_ROUTES", len == 0 ? "" : " ");
++    if (syscontrol & CONT_INHIBIT_MAX_CHAIN_ITEMS)
++      len += sprintf (page + len, "%sINHIBIT_MAX_CHAIN_ITEMS", len == 0 ? "" : " ");
++
++    len += sprintf (page + len, "%sTABLE0_MASK_SIZE=%d", len == 0 ? "" : " ", (syscontrol >> CONT_TABLE0_MASK_SIZE_SHIFT) & PAGE_MASK_MASK);
++    len += sprintf (page + len, "%sTABLE0_PAGE_SIZE=%d", len == 0 ? "" : " ", (syscontrol >> CONT_TABLE0_PAGE_SIZE_SHIFT) & PAGE_SIZE_MASK);
++    len += sprintf (page + len, "%sTABLE1_MASK_SIZE=%d", len == 0 ? "" : " ", (syscontrol >> CONT_TABLE1_MASK_SIZE_SHIFT) & PAGE_MASK_MASK);
++    len += sprintf (page + len, "%sTABLE1_PAGE_SIZE=%d", len == 0 ? "" : " ", (syscontrol >> CONT_TABLE1_PAGE_SIZE_SHIFT) & PAGE_SIZE_MASK);
++
++    if (syscontrol & CONT_2K_NOT_1K_DMA_PACKETS)
++      len += sprintf (page + len, "%s2K_NOT_1K_DMA_PACKETS", len == 0 ? "" : " ");
++    if (syscontrol & CONT_ALIGN_ALL_DMA_PACKETS)
++      len += sprintf (page + len, "%sALIGN_ALL_DMA_PACKETS", len == 0 ? "" : " ");
++    if (syscontrol & CONT_DIRECT_MAP_PCI_WRITES)
++      len += sprintf (page + len, "%sDIRECT_MAP_PCI_WRITES", len == 0 ? "" : " ");
++
++    len += sprintf (page + len, "\n");
++
++   *start = page;
++   return (len);
++}
++
++static int
++proc_write_sysconfig (struct file *file, const char *ubuffer, unsigned long count, void *data)
++{
++    ELAN4_DEV *dev       = (ELAN4_DEV *) data;
++    unsigned long page   = __get_free_page (GFP_KERNEL);
++    char         *buffer = (char *)page;
++    int            add   = 0;
++    int            sub   = 0;
++    
++    count = MIN (count, PAGE_SIZE - 1);
++    if (copy_from_user (buffer, ubuffer, count))
++    {
++      free_page (page);
++      return (-EFAULT);
++    }
++   
++    buffer[count] = 0;                                /* terminate string */
++
++    while (*buffer != 0)
++    {
++      char *ptr;
++      char *end;
++      int   ch;
++      int   val;
++      int   op;
++
++      ch = *buffer;
++      if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n')
++      {
++          buffer++;
++          continue;
++      }
++      
++      op = *buffer;
++      if (op == '+' || op == '-')
++          buffer++;
++
++      for (end = buffer; *end != 0; end++)
++          if (*end == ' ' || *end == '\t' ||
++              *end == '\r' || *end == '\n')
++              break;
++      
++      if (end == buffer)
++          break;
++      
++      ch = *end;
++      *end = 0;
++
++      for (ptr = buffer; *ptr != 0; ptr++)
++          if ('a' <= *ptr && *ptr <= 'z')
++              *ptr = *ptr + 'A' - 'a';
++      
++      if (!strcmp (buffer, "EN_ALL_SETS"))
++          val = CONT_EN_ALL_SETS;
++      if (!strcmp (buffer, "CACHE_HASH_TABLE"))
++          val = CONT_CACHE_HASH_TABLE;
++      else if (!strcmp (buffer, "CACHE_CHAINS"))
++          val = CONT_CACHE_CHAINS;
++      else if (!strcmp (buffer, "CACHE_ROOT_CNTX"))
++          val = CONT_CACHE_ROOT_CNTX;
++      else if (!strcmp (buffer, "CACHE_STEN_ROUTES"))
++          val = CONT_CACHE_STEN_ROUTES;
++      else if (!strcmp (buffer, "CACHE_DMA_ROUTES"))
++          val = CONT_CACHE_DMA_ROUTES;
++      else if (!strcmp (buffer, "2K_NOT_1K_DMA_PACKETS"))
++          val = CONT_2K_NOT_1K_DMA_PACKETS;
++      else if (!strcmp (buffer, "ALIGN_ALL_DMA_PACKETS"))
++          val = CONT_ALIGN_ALL_DMA_PACKETS;
++      else
++          val = 0;
++
++      if (op == '+')
++          add |= val;
++      else if (op == '-')
++          sub |= val;
++
++      *end = ch;
++      buffer = end;
++    }
++
++    if ((add | sub) & CONT_EN_ALL_SETS)
++      elan4_sdram_flushcache (dev, 0, E4_CacheSize);
++
++    CHANGE_SYSCONTROL (dev, add, sub);
++   
++    if ((add | sub) & CONT_EN_ALL_SETS)
++      elan4_sdram_flushcache (dev, 0, E4_CacheSize);
++
++    free_page (page);
++    return (count);
++}
++
++static struct config_info 
++{
++    char *name;
++    int (*read_func) (char *page, char **start, off_t off, int count, int *eof, void *data);
++    int (*write_func) (struct file *file, const char *buf, unsigned long count, void *data); 
++} config_info[] = {
++    {"sysconfig",  proc_read_sysconfig, proc_write_sysconfig},
++};
++
++void
++elan4_procfs_device_init (ELAN4_DEV *dev)
++{
++    struct proc_dir_entry *p;
++    char name[NAME_MAX];
++    int i;
++
++    sprintf (name, "device%d", dev->dev_instance);
++    dev->dev_osdep.procdir  = proc_mkdir (name, elan4_procfs_root);
++
++    for (i = 0; i < sizeof (device_info)/sizeof (device_info[0]); i++)
++    {
++      if (dev->dev_devinfo.dev_revision_id < device_info[i].minrev)
++          continue;
++
++      if ((p = create_proc_entry (device_info[i].name, 0, dev->dev_osdep.procdir)) != NULL)
++      {
++          p->read_proc  = device_info[i].read_func;
++          p->write_proc = device_info[i].write_func;
++          p->data       = dev;
++          p->owner      = THIS_MODULE;
++      }
++    }
++
++    dev->dev_osdep.configdir = proc_mkdir ("config", dev->dev_osdep.procdir);
++    for (i = 0; i < sizeof (config_info)/sizeof (config_info[0]); i++)
++    {
++      if ((p = create_proc_entry (config_info[i].name, 0, dev->dev_osdep.configdir)) != NULL)
++      {
++          p->read_proc  = config_info[i].read_func;
++          p->write_proc = config_info[i].write_func;
++          p->data       = dev;
++          p->owner      = THIS_MODULE;
++      }
++    }
++
++    dev->dev_osdep.statsdir = proc_mkdir ("stats", dev->dev_osdep.procdir);
++    for (i = 0; i < sizeof (stats_info)/sizeof (stats_info[0]); i++)
++    {
++      if ((p = create_proc_entry (stats_info[i].name, 0, dev->dev_osdep.statsdir)) != NULL)
++      {
++          p->read_proc  = stats_info[i].read_func;
++          p->write_proc = stats_info[i].write_func;
++          p->data       = dev;
++          p->owner      = THIS_MODULE;
++      }
++    }
++}
++
++void
++elan4_procfs_device_fini (ELAN4_DEV *dev)
++{
++    char name[NAME_MAX];
++    int i;
++
++    for (i = 0; i < sizeof (stats_info)/sizeof (stats_info[0]); i++)
++      remove_proc_entry (stats_info[i].name, dev->dev_osdep.statsdir);
++    remove_proc_entry ("stats", dev->dev_osdep.procdir);
++
++    for (i = 0; i < sizeof (config_info)/sizeof (config_info[0]); i++)
++      remove_proc_entry (config_info[i].name, dev->dev_osdep.configdir);
++    remove_proc_entry ("config", dev->dev_osdep.procdir);
++
++    for (i = 0; i < sizeof (device_info)/sizeof (device_info[0]); i++)
++    {
++      if (dev->dev_devinfo.dev_revision_id < device_info[i].minrev)
++          continue;
++      
++      remove_proc_entry (device_info[i].name, dev->dev_osdep.procdir);
++    }
++
++    sprintf (name, "device%d", dev->dev_instance);
++    remove_proc_entry (name, elan4_procfs_root);
++}
++
++void
++elan4_procfs_init(void)
++{
++    elan4_procfs_root = proc_mkdir("elan4", qsnet_procfs_root);
++    elan4_config_root = proc_mkdir("config", elan4_procfs_root);
++
++    qsnet_proc_register_hex (elan4_config_root, "elan4_debug",              &elan4_debug,              0);
++    qsnet_proc_register_hex (elan4_config_root, "elan4_debug_toconsole",    &elan4_debug_toconsole,    0);
++    qsnet_proc_register_hex (elan4_config_root, "elan4_debug_tobuffer",     &elan4_debug_tobuffer,     0);
++    qsnet_proc_register_int (elan4_config_root, "elan4_debug_mmu",          &elan4_debug_mmu,          0);
++    qsnet_proc_register_int (elan4_config_root, "elan4_mainint_punt_loops", &elan4_mainint_punt_loops, 0);
++    qsnet_proc_register_hex (elan4_config_root, "user_p2p_route_options",   &user_p2p_route_options,   0);
++    qsnet_proc_register_hex (elan4_config_root, "user_bcast_route_options", &user_bcast_route_options, 0);
++    qsnet_proc_register_int (elan4_config_root, "user_dproc_retry_count",   &user_dproc_retry_count,    0);
++    qsnet_proc_register_int (elan4_config_root, "user_cproc_retry_count",   &user_cproc_retry_count,    0);
++    qsnet_proc_register_int (elan4_config_root, "num_fault_save",   &num_fault_save,    0);
++    qsnet_proc_register_int (elan4_config_root, "min_fault_pages",   &min_fault_pages,    0);
++    qsnet_proc_register_int (elan4_config_root, "max_fault_pages",   &max_fault_pages,    0);
++}
++
++void
++elan4_procfs_fini(void)
++{
++    remove_proc_entry ("max_fault_pages",          elan4_config_root);
++    remove_proc_entry ("min_fault_pages",          elan4_config_root);
++    remove_proc_entry ("num_fault_save",           elan4_config_root);
++    remove_proc_entry ("user_cproc_retry_count",   elan4_config_root);
++    remove_proc_entry ("user_dproc_retry_count",   elan4_config_root);
++    remove_proc_entry ("user_bcast_route_options", elan4_config_root);
++    remove_proc_entry ("user_p2p_route_options",   elan4_config_root);
++    remove_proc_entry ("elan4_mainint_punt_loops", elan4_config_root);
++    remove_proc_entry ("elan4_debug_mmu",          elan4_config_root);
++    remove_proc_entry ("elan4_debug_tobuffer",     elan4_config_root);
++    remove_proc_entry ("elan4_debug_toconsole",    elan4_config_root);
++    remove_proc_entry ("elan4_debug",              elan4_config_root);
++
++    remove_proc_entry ("config", elan4_procfs_root);
++    remove_proc_entry ("elan4", qsnet_procfs_root);
++}
++
++EXPORT_SYMBOL(elan4_procfs_root);
++EXPORT_SYMBOL(elan4_config_root);
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan4/quadrics_version.h
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan4/quadrics_version.h       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan4/quadrics_version.h    2005-06-01 23:12:54.614436824 -0400
+@@ -0,0 +1 @@
++#define QUADRICS_VERSION "4.30qsnet"
+Index: linux-2.4.21/drivers/net/qsnet/elan4/regions.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan4/regions.c        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan4/regions.c     2005-06-01 23:12:54.615436672 -0400
+@@ -0,0 +1,609 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: regions.c,v 1.18.2.1 2004/11/18 11:31:08 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/regions.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan4/debug.h>
++#include <elan4/device.h>
++#include <elan4/user.h>
++
++/*================================================================================*/
++/* elan address region management */
++USER_RGN *
++user_findrgn_elan (USER_CTXT *uctx, E4_Addr addr, int tail)
++{
++    USER_RGN *rgn;
++    USER_RGN *hirgn;
++    USER_RGN *lorgn;
++    E4_Addr   base;
++    E4_Addr   lastaddr;
++    int             forward;
++    
++    ASSERT (SPINLOCK_HELD (&uctx->uctx_rgnlock) || kmutex_is_locked (&uctx->uctx_rgnmutex));
++
++    if (uctx->uctx_ergns == NULL)
++      return (NULL);
++
++    rgn = uctx->uctx_ergnlast;
++    if (rgn == NULL)
++      rgn = uctx->uctx_ergns;
++
++    forward = 0;
++    if ((base = rgn->rgn_ebase) < addr)
++    {
++      if (addr <= (base + rgn->rgn_len - 1))
++          return (rgn);                                       /* ergnlast contained addr */
++      
++      hirgn = uctx->uctx_etail;
++      
++      if ((lastaddr = (hirgn->rgn_ebase + hirgn->rgn_len - 1)) < addr)
++          return (tail ? hirgn : NULL);                       /* addr is out of range */
++      
++      if ((addr - base) > (lastaddr - addr))
++          rgn = hirgn;
++      else
++      {
++          rgn = rgn->rgn_enext;
++          forward++;
++      }
++    }
++    else
++    {
++      lorgn = uctx->uctx_ergns;
++
++      if (lorgn->rgn_ebase > addr)
++          return (lorgn);                                     /* lowest regions is higher than addr */
++      if ((addr - lorgn->rgn_ebase) < (base - addr))
++      {
++          rgn = lorgn;                                        /* search forward from head */
++          forward++;
++      }
++    }
++    if (forward)
++    {
++      while ((rgn->rgn_ebase + rgn->rgn_len - 1) < addr)
++          rgn = rgn->rgn_enext;
++
++      if (rgn->rgn_ebase <= addr)
++          uctx->uctx_ergnlast = rgn;
++      return (rgn);
++    }
++    else
++    {
++      while (rgn->rgn_ebase > addr)
++          rgn = rgn->rgn_eprev;
++
++      if ((rgn->rgn_ebase + rgn->rgn_len - 1) < addr)
++          return (rgn->rgn_enext);
++      else
++      {
++          uctx->uctx_ergnlast = rgn;
++          return (rgn);
++      }
++    }
++}
++
++static int
++user_addrgn_elan (USER_CTXT *uctx, USER_RGN  *nrgn)
++{
++    USER_RGN *rgn   = user_findrgn_elan (uctx, nrgn->rgn_ebase, 1);
++    E4_Addr   nbase = nrgn->rgn_ebase;
++    E4_Addr   ntop  = nbase + nrgn->rgn_len - 1;
++    E4_Addr   base;
++
++    ASSERT (SPINLOCK_HELD (&uctx->uctx_rgnlock) && kmutex_is_locked (&uctx->uctx_rgnmutex));
++
++    if (rgn == NULL)
++    {
++      uctx->uctx_ergns = uctx->uctx_etail = nrgn;
++      nrgn->rgn_enext = nrgn->rgn_eprev = NULL;
++    }
++    else
++    {
++      base = rgn->rgn_ebase;
++      
++      if ((base + rgn->rgn_len - 1) < nbase)                  /* top of region below requested address */
++      {                                                       /* so insert after region (and hence at end */
++          nrgn->rgn_eprev = rgn;                              /* of list */
++          nrgn->rgn_enext = NULL;
++          rgn->rgn_enext = uctx->uctx_etail = nrgn;
++      }
++      else
++      {
++          if (nbase >= base || ntop >= base)                  /* overlapping region */
++              return (-1);
++          
++          nrgn->rgn_enext = rgn;                              /* insert before region */
++          nrgn->rgn_eprev = rgn->rgn_eprev;
++          rgn->rgn_eprev  = nrgn;
++          if (uctx->uctx_ergns == rgn)
++              uctx->uctx_ergns = nrgn;
++          else
++              nrgn->rgn_eprev->rgn_enext = nrgn;
++      }
++    }
++    uctx->uctx_ergnlast = nrgn;
++    
++    return (0);
++}
++
++static USER_RGN *
++user_removergn_elan (USER_CTXT *uctx, USER_RGN  *rgn)
++{
++    ASSERT (SPINLOCK_HELD (&uctx->uctx_rgnlock) && kmutex_is_locked (&uctx->uctx_rgnmutex));
++
++    uctx->uctx_ergnlast = rgn->rgn_enext;
++    if (rgn == uctx->uctx_etail)
++      uctx->uctx_etail = rgn->rgn_eprev;
++    else
++      rgn->rgn_enext->rgn_eprev = rgn->rgn_eprev;
++    
++    if (rgn == uctx->uctx_ergns)
++      uctx->uctx_ergns = rgn->rgn_enext;
++    else
++      rgn->rgn_eprev->rgn_enext = rgn->rgn_enext;
++
++    return (rgn);
++}
++
++USER_RGN *
++user_rgnat_elan (USER_CTXT *uctx, E4_Addr addr)
++{
++    USER_RGN *rgn = user_findrgn_elan (uctx, addr, 0);
++
++    if (rgn != NULL && rgn->rgn_ebase <= addr && addr <= (rgn->rgn_ebase + rgn->rgn_len - 1))
++      return (rgn);
++
++    return (NULL);
++}
++
++/* main address region management */
++USER_RGN *
++user_findrgn_main (USER_CTXT *uctx, virtaddr_t addr, int tail)
++{
++    USER_RGN  *rgn;
++    USER_RGN  *hirgn;
++    USER_RGN  *lorgn;
++    virtaddr_t lastaddr;
++    virtaddr_t base;
++    int              forward;
++    
++    ASSERT (SPINLOCK_HELD (&uctx->uctx_rgnlock) || kmutex_is_locked (&uctx->uctx_rgnmutex));
++
++    if (uctx->uctx_mrgns == NULL)
++      return (NULL);
++    
++    rgn = uctx->uctx_mrgnlast;
++    if (rgn == NULL)
++      rgn = uctx->uctx_mrgns;
++    
++    forward = 0;
++    if ((base = rgn->rgn_mbase) < addr)
++    {
++      if (addr <= (base + rgn->rgn_len - 1))
++          return (rgn);                                       /* ergnlast contained addr */
++      
++      hirgn = uctx->uctx_mtail;
++      if ((lastaddr = hirgn->rgn_mbase + hirgn->rgn_len - 1) < addr)
++          return (tail ? hirgn : NULL);                       /* addr is out of range */
++      
++      if ((addr - base) > (lastaddr - addr))
++          rgn = hirgn;
++      else
++      {
++          rgn = rgn->rgn_mnext;
++          forward++;
++      }
++    }
++    else
++    {
++      lorgn = uctx->uctx_mrgns;
++      if (lorgn->rgn_mbase > addr)
++          return (lorgn);                                     /* lowest regions is higher than addr */
++      if ((addr - lorgn->rgn_mbase) < (base - addr))
++      {
++          rgn = lorgn;                                        /* search forward from head */
++          forward++;
++      }
++    }
++    if (forward)
++    {
++      while ((rgn->rgn_mbase + rgn->rgn_len - 1) < addr)
++          rgn = rgn->rgn_mnext;
++
++      if (rgn->rgn_mbase <= addr)
++          uctx->uctx_mrgnlast = rgn;
++      return (rgn);
++    }
++    else
++    {
++      while (rgn->rgn_mbase > addr)
++          rgn = rgn->rgn_mprev;
++
++      if ((rgn->rgn_mbase + rgn->rgn_len - 1) < addr)
++          return (rgn->rgn_mnext);
++      else
++      {
++          uctx->uctx_mrgnlast = rgn;
++          return (rgn);
++      }
++    }
++}
++
++static int
++user_addrgn_main (USER_CTXT *uctx, USER_RGN *nrgn)
++{
++    USER_RGN  *rgn   = user_findrgn_main (uctx, nrgn->rgn_mbase, 1);
++    virtaddr_t nbase = nrgn->rgn_mbase;
++    virtaddr_t ntop  = nbase + nrgn->rgn_len - 1;
++    virtaddr_t base;
++
++    ASSERT (SPINLOCK_HELD (&uctx->uctx_rgnlock) && kmutex_is_locked (&uctx->uctx_rgnmutex));
++
++    if (rgn == NULL)
++    {
++      uctx->uctx_mrgns = uctx->uctx_mtail = nrgn;
++      nrgn->rgn_mnext = nrgn->rgn_mprev = NULL;
++    }
++    else
++    {
++      base = rgn->rgn_mbase;
++
++      if ((base + rgn->rgn_len - 1) < nbase)                  /* top of region below requested address */
++      {                                                       /* so insert after region (and hence at end */
++          nrgn->rgn_mprev = rgn;                              /* of list */
++          nrgn->rgn_mnext = NULL;
++          rgn->rgn_mnext = uctx->uctx_mtail = nrgn;
++      }
++      else
++      {
++          if (nbase >= base || ntop >= base)                  /* overlapping region */
++              return (-1);
++
++          nrgn->rgn_mnext = rgn;                              /* insert before region */
++          nrgn->rgn_mprev = rgn->rgn_mprev;
++          rgn->rgn_mprev  = nrgn;
++          if (uctx->uctx_mrgns == rgn)
++              uctx->uctx_mrgns = nrgn;
++          else
++              nrgn->rgn_mprev->rgn_mnext = nrgn;
++      }
++    }
++    uctx->uctx_mrgnlast = nrgn;
++    
++    return (0);
++}
++
++static USER_RGN *
++user_removergn_main (USER_CTXT *uctx, USER_RGN *rgn)
++{
++    ASSERT (SPINLOCK_HELD (&uctx->uctx_rgnlock) && kmutex_is_locked (&uctx->uctx_rgnmutex));
++
++    uctx->uctx_mrgnlast = rgn->rgn_mnext;
++    if (rgn == uctx->uctx_mtail)
++      uctx->uctx_mtail = rgn->rgn_mprev;
++    else
++      rgn->rgn_mnext->rgn_mprev = rgn->rgn_mprev;
++    
++    if (rgn == uctx->uctx_mrgns)
++      uctx->uctx_mrgns = rgn->rgn_mnext;
++    else
++      rgn->rgn_mprev->rgn_mnext = rgn->rgn_mnext;
++
++    return (rgn);
++}
++
++/* Remove whole region from both lists */
++static void
++user_removergn (USER_CTXT *uctx, USER_RGN *rgn)
++{
++    spin_lock (&uctx->uctx_rgnlock);
++
++    elan4mmu_unload_range (&uctx->uctx_ctxt, 0 /* XXXX tbl */, rgn->rgn_ebase, rgn->rgn_len);
++          
++    user_removergn_elan (uctx, rgn);
++    user_removergn_main (uctx, rgn);
++    
++    spin_unlock (&uctx->uctx_rgnlock);
++    
++    KMEM_FREE (rgn, sizeof (USER_RGN));
++}
++
++/* Remove all allocated regions */
++void
++user_freergns (USER_CTXT *uctx)
++{
++    kmutex_lock (&uctx->uctx_rgnmutex);
++
++    while (uctx->uctx_mrgns)
++      user_removergn(uctx, uctx->uctx_mrgns);
++
++    kmutex_unlock (&uctx->uctx_rgnmutex);
++
++    ASSERT (uctx->uctx_ergns == NULL);
++}
++
++USER_RGN *
++user_rgnat_main (USER_CTXT *uctx, virtaddr_t addr)
++{
++    USER_RGN *rgn = user_findrgn_main (uctx, addr, 0);
++    
++    if (rgn != NULL && rgn->rgn_mbase <= addr && addr <= (rgn->rgn_mbase + rgn->rgn_len - 1))
++      return (rgn);
++    return (NULL);
++}
++
++int
++user_setperm (USER_CTXT *uctx, virtaddr_t maddr, E4_Addr eaddr, unsigned long len, unsigned perm)
++{
++    USER_RGN *nrgn;
++
++    PRINTF4 (uctx, DBG_PERM, "user_setperm: user %lx elan %llx len %lx perm %x\n", maddr, (long long) eaddr, len, perm);
++
++    if ((maddr & PAGEOFFSET) || (eaddr & PAGEOFFSET) || (len & PAGEOFFSET))
++    {
++        PRINTF0 (uctx, DBG_PERM, "user_setperm:  alignment failure\n");
++      return (-EINVAL);
++    }
++    
++    if ((maddr + len - 1) <= maddr || (eaddr + len - 1) <= eaddr) 
++    {
++      PRINTF0 (uctx, DBG_PERM, "user_setperm:  range failure\n");
++      return (-EINVAL);
++    }
++
++    KMEM_ALLOC (nrgn, USER_RGN *, sizeof (USER_RGN), 1);
++    
++    if (nrgn == NULL)
++      return (-ENOMEM);
++
++    nrgn->rgn_mbase = maddr;
++    nrgn->rgn_ebase = eaddr;
++    nrgn->rgn_len   = len;
++    nrgn->rgn_perm  = perm;
++
++    kmutex_lock (&uctx->uctx_rgnmutex);
++    spin_lock (&uctx->uctx_rgnlock);
++
++    if (user_addrgn_elan (uctx, nrgn) < 0)
++    {
++      PRINTF0 (uctx, DBG_PERM, "user_setperm:  elan address exists\n");
++      spin_unlock (&uctx->uctx_rgnlock);
++      kmutex_unlock (&uctx->uctx_rgnmutex);
++
++      KMEM_FREE (nrgn, sizeof (USER_RGN));
++      return (-EINVAL);
++    }
++    
++    if (user_addrgn_main (uctx, nrgn) < 0)
++    {
++      PRINTF0 (uctx, DBG_PERM, "user_setperm:  main address exists\n");
++      user_removergn_elan (uctx, nrgn);
++      
++      spin_unlock (&uctx->uctx_rgnlock);
++      kmutex_unlock (&uctx->uctx_rgnmutex);
++
++      KMEM_FREE (nrgn, sizeof (USER_RGN));
++      return (-EINVAL);
++    }
++    spin_unlock (&uctx->uctx_rgnlock);
++
++    if ((perm & PERM_Preload))
++      user_preload_main (uctx, maddr, len);
++
++    kmutex_unlock (&uctx->uctx_rgnmutex);
++
++    return (0);
++}
++
++void
++user_clrperm (USER_CTXT *uctx, E4_Addr addr, unsigned long len)
++{
++    E4_Addr       raddr;
++    E4_Addr       rtop;
++    USER_RGN     *nrgn;
++    USER_RGN     *rgn;
++    USER_RGN     *rgn_next;
++    unsigned long ssize;
++    int                 res;
++
++    PRINTF2 (uctx, DBG_PERM, "user_clrperm: elan %llx len %lx\n", addr, len);
++
++    raddr = (addr & PAGEMASK);
++    rtop = ((addr + len - 1) & PAGEMASK) + (PAGESIZE-1);
++
++    kmutex_lock (&uctx->uctx_rgnmutex);
++    
++    for (rgn = user_findrgn_elan (uctx, addr, 0); rgn != NULL; rgn = rgn_next)
++    {
++      if (rtop < rgn->rgn_ebase)                              /* rtop was in a gap */
++          break;
++      
++      rgn_next = rgn->rgn_enext;                              /* Save next region pointer */
++      
++      PRINTF (uctx, DBG_PERM, "              elan %llx->%llx main %p->%p\n", 
++              rgn->rgn_ebase, rgn->rgn_ebase + rgn->rgn_len-1,
++              rgn->rgn_mbase, rgn->rgn_mbase + rgn->rgn_len-1);
++
++      if (raddr <= rgn->rgn_ebase && rtop >= (rgn->rgn_ebase + rgn->rgn_len - 1))
++      {
++          /* whole region is cleared */
++
++          PRINTF (uctx, DBG_PERM, "              whole region\n");
++          PRINTF (uctx, DBG_PERM, "              unload elan %llx->%llx\n", rgn->rgn_ebase, rgn->rgn_ebase + rgn->rgn_len-1);
++          user_removergn (uctx, rgn);
++      }
++      else if (raddr <= rgn->rgn_ebase)
++      {
++          /* clearing at beginning, so shrink size and increment base ptrs */
++          ssize = rtop - rgn->rgn_ebase + 1;
++          
++          PRINTF (uctx, DBG_PERM, "              clear at beginning %x\n", ssize);
++
++          spin_lock (&uctx->uctx_rgnlock);
++
++          PRINTF (uctx, DBG_PERM, "              unload elan %llx->%llx\n", rgn->rgn_ebase, rgn->rgn_ebase + ssize-1);
++          elan4mmu_unload_range (&uctx->uctx_ctxt, 0 /* XXXX tbl */, rgn->rgn_ebase, ssize);
++
++          rgn->rgn_mbase += ssize;
++          rgn->rgn_ebase += ssize;
++          rgn->rgn_len   -= ssize;
++          
++          spin_unlock(&uctx->uctx_rgnlock);
++      }
++      else if (rtop >= (rgn->rgn_ebase + rgn->rgn_len - 1))
++      {
++          /* clearing at end, so just shrink length of region */
++          ssize = (rgn->rgn_ebase + rgn->rgn_len - 1) - raddr + 1;
++
++          PRINTF (uctx, DBG_PERM, "              clear at end %x\n", ssize);
++
++          spin_lock (&uctx->uctx_rgnlock);
++
++          PRINTF (uctx, DBG_PERM, "              unload elan %llx->%llx\n", raddr, raddr+ssize-1);
++          elan4mmu_unload_range (&uctx->uctx_ctxt, 0 /* XXXX tbl */, raddr, ssize);
++
++          rgn->rgn_len -= ssize;
++
++          spin_unlock(&uctx->uctx_rgnlock);
++      }
++      else
++      {
++          /* the section to go is in the middle,  so need to  */
++          /* split it into two regions */
++          KMEM_ALLOC (nrgn, USER_RGN *, sizeof (USER_RGN), 1);
++
++          spin_lock (&uctx->uctx_rgnlock);
++
++          PRINTF (uctx, DBG_PERM, "              unload elan %llx->%llx\n", raddr, rtop);
++          elan4mmu_unload_range (&uctx->uctx_ctxt, 0 /* XXXX tbl */, raddr, rtop - raddr + 1);
++
++          nrgn->rgn_mbase = rgn->rgn_mbase + (rtop - rgn->rgn_ebase + 1);
++          nrgn->rgn_ebase = rtop + 1;
++          nrgn->rgn_len   = (rgn->rgn_ebase + rgn->rgn_len - 1) - rtop;
++          nrgn->rgn_perm  = rgn->rgn_perm;
++
++          PRINTF (uctx, DBG_PERM, "              new elan %llx->%llx main %p->%p\n", 
++                  nrgn->rgn_ebase, nrgn->rgn_ebase + nrgn->rgn_len-1,
++                  nrgn->rgn_mbase, nrgn->rgn_mbase + nrgn->rgn_len-1);
++
++          rgn->rgn_len = (raddr - rgn->rgn_ebase);            /* shrink original region */
++
++          PRINTF (uctx, DBG_PERM, "              old elan %llx->%llx main %p->%p\n", 
++                  rgn->rgn_ebase, rgn->rgn_ebase + rgn->rgn_len-1,
++                  rgn->rgn_mbase, rgn->rgn_mbase + rgn->rgn_len-1);
++
++          res = user_addrgn_elan (uctx, nrgn);                /* insert new region */
++          ASSERT (res == 0);                                  /* which cannot fail */
++
++          res = user_addrgn_main (uctx, nrgn);        
++          ASSERT (res == 0);
++
++          spin_unlock(&uctx->uctx_rgnlock);
++      }
++    }
++    kmutex_unlock (&uctx->uctx_rgnmutex);
++}
++
++int
++user_checkperm (USER_CTXT *uctx, E4_Addr raddr, unsigned long rsize, unsigned access)
++{
++    USER_RGN *rgn;
++
++    PRINTF3 (uctx, DBG_PERM, "user_checkperm: elan %lx len %lx access %x\n", raddr, rsize, access);
++    
++    if ((raddr + rsize - 1) < raddr)
++      return (-ENOMEM);
++    
++    kmutex_lock (&uctx->uctx_rgnmutex);
++    if ((rgn = user_rgnat_elan (uctx, raddr)) == (USER_RGN *) NULL)
++    {
++      kmutex_unlock (&uctx->uctx_rgnmutex);
++      return (-ENOMEM);
++    }
++    else
++    {
++      register int ssize;
++      
++      for (; rsize != 0; rsize -= ssize, raddr += ssize)
++      {
++          if (raddr > (rgn->rgn_ebase + rgn->rgn_len - 1))
++          {
++              rgn  = rgn->rgn_enext;
++              
++              if (rgn == NULL || raddr != rgn->rgn_ebase)
++              {
++                  kmutex_unlock (&uctx->uctx_rgnmutex);
++                  return (-ENOMEM);
++              }
++          }
++          if ((raddr + rsize - 1) > (rgn->rgn_ebase + rgn->rgn_len - 1))
++              ssize = ((rgn->rgn_ebase + rgn->rgn_len - 1) - raddr) + 1;
++          else
++              ssize = rsize;
++          
++          PRINTF4 (uctx, DBG_PERM, "user_checkperm : rgn %lx -> %lx perm %x access %x\n",
++                   rgn->rgn_ebase, rgn->rgn_ebase + (E4_Addr)rgn->rgn_len, rgn->rgn_perm, access);
++
++          if (ELAN4_INCOMPAT_ACCESS (rgn->rgn_perm, access))
++          {
++              kmutex_unlock (&uctx->uctx_rgnmutex);
++              return (-EACCES);
++          }
++      }
++    }
++    
++    kmutex_unlock (&uctx->uctx_rgnmutex);
++    
++    return (0);
++}
++
++virtaddr_t
++user_elan2main (USER_CTXT *uctx, E4_Addr addr)
++{
++    USER_RGN  *rgn;
++    virtaddr_t raddr;
++    
++    spin_lock (&uctx->uctx_rgnlock);
++    
++    if ((rgn = user_rgnat_elan (uctx, addr)) == (USER_RGN *) NULL)
++      raddr = (virtaddr_t) 0;
++    else
++      raddr = rgn->rgn_mbase + (addr - rgn->rgn_ebase);
++
++    spin_unlock (&uctx->uctx_rgnlock);
++
++    return (raddr);
++}
++
++E4_Addr
++user_main2elan (USER_CTXT *uctx, virtaddr_t addr)
++{
++    USER_RGN *rgn;
++    E4_Addr   raddr;
++
++    spin_lock (&uctx->uctx_rgnlock);
++    
++    if ((rgn = user_rgnat_main (uctx, addr)) == (USER_RGN *) NULL)
++      raddr = (virtaddr_t) 0;
++    else
++      raddr = rgn->rgn_ebase + (addr - rgn->rgn_mbase);
++    
++    spin_unlock (&uctx->uctx_rgnlock);
++
++    return (raddr);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan4/routetable.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan4/routetable.c     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan4/routetable.c  2005-06-01 23:12:54.615436672 -0400
+@@ -0,0 +1,249 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: routetable.c,v 1.15 2004/07/20 09:29:40 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/routetable.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan4/sdram.h>
++#include <elan4/debug.h>
++#include <elan4/device.h>
++
++ELAN4_ROUTE_TABLE *
++elan4_alloc_routetable (ELAN4_DEV *dev, unsigned size)
++{
++    ELAN4_ROUTE_TABLE *tbl;
++
++    KMEM_ZALLOC (tbl, ELAN4_ROUTE_TABLE *, sizeof (ELAN4_ROUTE_TABLE), 1);
++
++    if (tbl == (ELAN4_ROUTE_TABLE *) NULL)
++      return (NULL);
++    
++    tbl->tbl_size    = (size & E4_VPT_SIZE_MASK);
++    tbl->tbl_entries = elan4_sdram_alloc (dev, (E4_VPT_MIN_ENTRIES << tbl->tbl_size) * sizeof (E4_VirtualProcessEntry));
++
++    if (tbl->tbl_entries == 0)
++    {
++      KMEM_FREE (tbl, sizeof (ELAN4_ROUTE_TABLE));
++      return ((ELAN4_ROUTE_TABLE *) NULL);
++    }
++
++    spin_lock_init (&tbl->tbl_lock);
++
++    /* zero the route table */
++    elan4_sdram_zeroq_sdram (dev, tbl->tbl_entries, (E4_VPT_MIN_ENTRIES << tbl->tbl_size) * sizeof (E4_VirtualProcessEntry));
++
++    return (tbl);
++}
++
++void
++elan4_free_routetable (ELAN4_DEV *dev, ELAN4_ROUTE_TABLE *tbl)
++{
++    elan4_sdram_free (dev, tbl->tbl_entries, (E4_VPT_MIN_ENTRIES << tbl->tbl_size) * sizeof (E4_VirtualProcessEntry));
++    
++    spin_lock_destroy (&tbl->tbl_lock);
++
++    KMEM_FREE (tbl, sizeof (ELAN4_ROUTE_TABLE));
++}
++
++void
++elan4_write_route (ELAN4_DEV *dev, ELAN4_ROUTE_TABLE *tbl, unsigned vp, E4_VirtualProcessEntry *entry)
++{
++    ASSERT (vp < (E4_VPT_MIN_ENTRIES << tbl->tbl_size));
++    
++    elan4_sdram_writeq (dev, tbl->tbl_entries + (vp * sizeof (E4_VirtualProcessEntry)) + offsetof (E4_VirtualProcessEntry, Values[1]), entry->Values[1]);
++    elan4_sdram_writeq (dev, tbl->tbl_entries + (vp * sizeof (E4_VirtualProcessEntry)) + offsetof (E4_VirtualProcessEntry, Values[0]), entry->Values[0]);
++    pioflush_sdram (dev);
++}
++
++void
++elan4_read_route (ELAN4_DEV *dev, ELAN4_ROUTE_TABLE *tbl, unsigned vp, E4_VirtualProcessEntry *entry)
++{
++    ASSERT (vp < (E4_VPT_MIN_ENTRIES << tbl->tbl_size));
++    
++    entry->Values[0] = elan4_sdram_readq (dev, tbl->tbl_entries + (vp * sizeof (E4_VirtualProcessEntry)) + offsetof (E4_VirtualProcessEntry, Values[0]));
++    entry->Values[1] = elan4_sdram_readq (dev, tbl->tbl_entries + (vp * sizeof (E4_VirtualProcessEntry)) + offsetof (E4_VirtualProcessEntry, Values[1]));
++}
++
++void
++elan4_invalidate_route (ELAN4_DEV *dev, ELAN4_ROUTE_TABLE *tbl, unsigned vp)
++{
++    ASSERT (vp < (E4_VPT_MIN_ENTRIES << tbl->tbl_size));
++
++    elan4_sdram_writeq (dev, tbl->tbl_entries + (vp * sizeof (E4_VirtualProcessEntry)) + offsetof (E4_VirtualProcessEntry, Values[0]), 0);
++    elan4_sdram_writeq (dev, tbl->tbl_entries + (vp * sizeof (E4_VirtualProcessEntry)) + offsetof (E4_VirtualProcessEntry, Values[1]), 0);
++    pioflush_sdram (dev);
++}
++
++static void
++pack_them_routes (E4_VirtualProcessEntry *entry, E4_uint16 first, E4_uint8 *packed, unsigned ctx)
++{
++    E4_uint64 value0 = first;
++    E4_uint64 value1 = ROUTE_CTXT_VALUE(ctx);
++    E4_uint32 ThirdRouteBCastVal;
++    register int i;
++
++    for (i = 0; i < (ROUTE_NUM_PACKED >> 1); i++)
++    {
++      value0 |= ((E4_uint64) packed[i]) << ((i << 2) + ROUTE_PACKED_OFFSET);
++      value1 |= ((E4_uint64) packed[i+(ROUTE_NUM_PACKED >> 1)]) << ((i << 2));
++    }
++
++    /* DMA fix for large broadcast route values that fall into the double issue of route value 3 bug. */
++    /* NOTE - this is only required when the link is running in Mod45 mode, it could be automatically
++     *        disabled when Mod44 is detected */
++    
++    /* First seach for the alignment type. The bug is only sensitive to an odd bcast aligment on the 3rd word. */
++    for (i=4;i<16;i++)
++      if (((value0 >> (i*4)) & 0xc) == 4)
++          i++;
++    
++    if (i == 17)
++    {
++      ThirdRouteBCastVal = value1 & 0xcccccccc;
++      if      (((value1 & 0xfffff0000000ULL) == 0ULL) && (ThirdRouteBCastVal == 0x04444444))
++          value1 |= 0x140000000ULL;
++      else if (((value1 & 0xfffffff00000ULL) == 0ULL) && (ThirdRouteBCastVal == 0x00044444))
++          value1 |= 0x1400000ULL;
++      else if (((value1 & 0xfffffffff000ULL) == 0ULL) && (ThirdRouteBCastVal == 0x00000444))
++          value1 |= 0x14000ULL;
++      else if (((value1 & 0xfffffffffff0ULL) == 0ULL) && (ThirdRouteBCastVal == 0x00000004))
++          value1 |= 0x140ULL;
++    }
++    
++    entry->Values[0] = value0;
++    entry->Values[1] = value1;
++}
++
++int
++elan4_generate_route (ELAN_POSITION *pos, E4_VirtualProcessEntry *route, unsigned ctx, unsigned lowid, unsigned highid, unsigned options)
++{
++    unsigned int broadcast    = (lowid != highid);
++    unsigned int noadaptive   = 0;
++    int          padbcast     = 0;
++    E4_uint16    first;
++    int                rb;
++    E4_uint8     packed[ROUTE_NUM_PACKED];
++    int                level, llink, hlink;
++
++ regenerate_routes:
++    first = 0;
++    rb    = 0;
++
++    switch (pos->pos_mode)
++    {
++    case ELAN_POS_MODE_LOOPBACK:
++      if (lowid != highid || lowid != pos->pos_nodeid)
++          return (-EINVAL);
++      
++      route->Values[0] = FIRST_MYLINK;
++      route->Values[1] = ROUTE_CTXT_VALUE (ctx);
++      return (0);
++
++    case ELAN_POS_MODE_BACKTOBACK:
++      if (lowid != highid || lowid == pos->pos_nodeid)
++          return (-EINVAL);
++      
++      route->Values[0] = FIRST_MYLINK;
++      route->Values[1] = ROUTE_CTXT_VALUE (ctx);
++      return (0);
++
++    case ELAN_POS_MODE_SWITCHED:
++    {
++      unsigned char *arityp  = &pos->pos_arity[pos->pos_levels - 1];
++      unsigned int   spanned = *arityp;
++      unsigned int   broadcasting = 0;
++      
++      bzero (packed, sizeof (packed));
++
++      /* XXXX compute noadaptive ? */
++
++      for (level = 0; 
++           level < pos->pos_levels && ! ((pos->pos_nodeid / spanned) == (lowid / spanned) &&
++                                         (pos->pos_nodeid / spanned) ==  (highid / spanned)); 
++           level++, spanned *= *(--arityp))
++      {
++          if (first == 0)
++              first = (broadcast || noadaptive) ? FIRST_BCAST_TREE : FIRST_ADAPTIVE;
++          else if (broadcast && padbcast)
++          {
++              padbcast = 0;
++              packed[rb++] = PACKED_BCAST0(4, 4);
++              packed[rb++] = PACKED_BCAST1(4, 4);
++          }
++          else
++              packed[rb++] = (broadcast || noadaptive) ? PACKED_BCAST_TREE : PACKED_ADAPTIVE;    
++      }
++
++      while (level >= 0)
++      {
++          spanned /= *arityp;
++          
++          llink = (lowid  / spanned) % *arityp;
++          hlink = (highid / spanned) % *arityp;
++          
++          if (llink != hlink || broadcasting)
++          {
++              broadcasting = 1;
++              
++              if (first == 0)
++                  first = FIRST_BCAST (hlink, llink);
++              else
++              {
++                  packed[rb++] = PACKED_BCAST0(hlink, llink);
++                  
++                  if ((rb % 4) == 0 && PACKED_BCAST1(hlink, llink) == 0)
++                  {
++                      padbcast = 1;
++                      goto regenerate_routes;
++                  }
++                  
++                  packed[rb++] = PACKED_BCAST1(hlink, llink);
++              }
++          }
++          else
++          {
++              if (first == 0)
++                  first = FIRST_ROUTE(llink);
++              else
++                  packed[rb++] = PACKED_ROUTE(llink);
++          }
++          
++          level--;
++          arityp++;
++      }
++
++      pack_them_routes (route, first | (options & FIRST_OPTIONS_MASK), packed, ctx);
++      return (0);
++    }
++    }
++
++    return (-EINVAL);
++}
++
++int
++elan4_check_route (ELAN_POSITION *postiion, ELAN_LOCATION location, E4_VirtualProcessEntry *route, unsigned flags)
++{
++    /* XXXX - TBD */
++    return (0);
++}
++
++EXPORT_SYMBOL(elan4_alloc_routetable);
++EXPORT_SYMBOL(elan4_free_routetable);
++EXPORT_SYMBOL(elan4_write_route);
++EXPORT_SYMBOL(elan4_read_route);
++EXPORT_SYMBOL(elan4_invalidate_route);
++EXPORT_SYMBOL(elan4_generate_route);
++EXPORT_SYMBOL(elan4_check_route);
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan4/sdram.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan4/sdram.c  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan4/sdram.c       2005-06-01 23:12:54.617436368 -0400
+@@ -0,0 +1,1034 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: sdram.c,v 1.29.6.1 2004/11/29 11:39:13 addy Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/sdram.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan4/debug.h>
++#include <elan4/device.h>
++
++EXPORT_SYMBOL_GPL(elan4_sdram_readb);
++EXPORT_SYMBOL_GPL(elan4_sdram_readw);
++EXPORT_SYMBOL_GPL(elan4_sdram_readl);
++EXPORT_SYMBOL_GPL(elan4_sdram_readq);
++EXPORT_SYMBOL_GPL(elan4_sdram_writeb);
++EXPORT_SYMBOL_GPL(elan4_sdram_writew);
++EXPORT_SYMBOL_GPL(elan4_sdram_writel);
++EXPORT_SYMBOL_GPL(elan4_sdram_writeq);
++EXPORT_SYMBOL_GPL(elan4_sdram_zerob_sdram);
++EXPORT_SYMBOL_GPL(elan4_sdram_zerow_sdram);
++EXPORT_SYMBOL_GPL(elan4_sdram_zerol_sdram);
++EXPORT_SYMBOL_GPL(elan4_sdram_zeroq_sdram);
++EXPORT_SYMBOL_GPL(elan4_sdram_copyb_from_sdram);
++EXPORT_SYMBOL_GPL(elan4_sdram_copyw_from_sdram);
++EXPORT_SYMBOL_GPL(elan4_sdram_copyl_from_sdram);
++EXPORT_SYMBOL_GPL(elan4_sdram_copyq_from_sdram);
++EXPORT_SYMBOL_GPL(elan4_sdram_copyb_to_sdram);
++EXPORT_SYMBOL_GPL(elan4_sdram_copyw_to_sdram);
++EXPORT_SYMBOL_GPL(elan4_sdram_copyl_to_sdram);
++EXPORT_SYMBOL_GPL(elan4_sdram_copyq_to_sdram);
++EXPORT_SYMBOL_GPL(elan4_sdram_alloc);
++EXPORT_SYMBOL_GPL(elan4_sdram_free);
++EXPORT_SYMBOL_GPL(elan4_sdram_flushcache);
++
++#define SDRAM_MIN_BANK_SIZE           ((1 << 15) * 8)         /* 256 Kbytes */
++
++static inline ELAN4_SDRAM_BANK *
++sdramaddr_to_bank (ELAN4_DEV *dev, sdramaddr_t saddr)
++{
++    register int i;
++    
++    for (i = 0; i < dev->dev_sdram_numbanks; i++)
++    {
++      ELAN4_SDRAM_BANK *bank = &dev->dev_sdram_banks[i];
++
++      if (saddr >= bank->b_base && saddr < (bank->b_base + bank->b_size))
++          return (bank);
++    }
++    printk ("sdramaddr_to_bank: sdram address %lx not in a sdram bank\n", saddr);
++    BUG();
++
++    return (NULL);    /* NOTREACHED */
++}
++
++static inline int
++sdramaddr_to_bankoffset (ELAN4_DEV *dev, sdramaddr_t saddr)
++{
++    return (saddr & (sdramaddr_to_bank (dev, saddr)->b_size-1));
++}
++
++static inline int
++sdramaddr_to_bit(ELAN4_DEV *dev, int indx, sdramaddr_t saddr)
++{
++    return (sdramaddr_to_bankoffset(dev, saddr) >> (SDRAM_MIN_BLOCK_SHIFT+(indx)));
++}
++
++static inline ioaddr_t
++sdramaddr_to_ioaddr (ELAN4_DEV *dev, sdramaddr_t saddr)
++{
++    ELAN4_SDRAM_BANK *bank = sdramaddr_to_bank (dev, saddr);
++
++    return (bank->b_ioaddr + (saddr - bank->b_base));
++}
++
++unsigned char
++elan4_sdram_readb (ELAN4_DEV *dev, sdramaddr_t off)
++{
++    return (__elan4_readb (dev, sdramaddr_to_ioaddr(dev, off)));
++}
++
++unsigned short
++elan4_sdram_readw (ELAN4_DEV *dev, sdramaddr_t off)
++{
++    return (__elan4_readw (dev, sdramaddr_to_ioaddr(dev, off)));
++}
++
++unsigned int
++elan4_sdram_readl (ELAN4_DEV *dev, sdramaddr_t off)
++{
++    return (__elan4_readl (dev, sdramaddr_to_ioaddr(dev, off)));
++}
++
++unsigned long long
++elan4_sdram_readq (ELAN4_DEV *dev, sdramaddr_t off)
++{
++    return (readq (sdramaddr_to_ioaddr(dev, off)));
++}
++
++void
++elan4_sdram_writeb (ELAN4_DEV *dev, sdramaddr_t off, unsigned char val)
++{
++    writeb (val, sdramaddr_to_ioaddr(dev, off));
++
++    mb();
++}
++
++void
++elan4_sdram_writew (ELAN4_DEV *dev, sdramaddr_t off, unsigned short val)
++{
++    writew (val, sdramaddr_to_ioaddr(dev, off));
++
++    mb();
++}
++
++void
++elan4_sdram_writel (ELAN4_DEV *dev, sdramaddr_t off, unsigned int val)
++{
++    writel (val, sdramaddr_to_ioaddr(dev, off));
++
++    mb();
++}
++
++void
++elan4_sdram_writeq (ELAN4_DEV *dev, sdramaddr_t off, unsigned long long val)
++{
++    writeq (val, sdramaddr_to_ioaddr(dev, off));
++
++    mb();
++}
++
++void
++elan4_sdram_zerob_sdram (ELAN4_DEV *dev, sdramaddr_t to, int nbytes)
++{
++    ioaddr_t dest = sdramaddr_to_ioaddr (dev, to);
++    ioaddr_t lim  = dest + nbytes;
++
++    for (; dest < lim; dest += sizeof (u8))
++      writeb (0, dest);
++}
++
++void
++elan4_sdram_zerow_sdram (ELAN4_DEV *dev, sdramaddr_t to, int nbytes)
++{
++    ioaddr_t dest = sdramaddr_to_ioaddr (dev, to);
++    ioaddr_t lim  = dest + nbytes;
++
++    for (; dest < lim; dest += sizeof (u8))
++      writeb (0, dest);
++}
++
++void
++elan4_sdram_zerol_sdram (ELAN4_DEV *dev, sdramaddr_t to, int nbytes)
++{
++    ioaddr_t dest = sdramaddr_to_ioaddr (dev, to);
++    ioaddr_t lim  = dest + nbytes;
++
++    for (; dest < lim; dest += sizeof (u32))
++      writel (0, dest);
++}
++
++void
++elan4_sdram_zeroq_sdram (ELAN4_DEV *dev, sdramaddr_t to, int nbytes)
++{
++    ioaddr_t dest = sdramaddr_to_ioaddr (dev, to);
++    ioaddr_t lim  = dest + nbytes;
++
++#ifdef CONFIG_MPSAS
++    if (sas_memset_dev (dev->dev_osdep.pdev, ELAN4_BAR_SDRAM, to, 0, nbytes) == 0)
++      return;
++#endif
++
++    for (; dest < lim; dest += sizeof (u64))
++      writeq (0, dest);
++}
++
++void
++elan4_sdram_copyb_from_sdram (ELAN4_DEV *dev, sdramaddr_t from, void *to, int nbytes)
++{
++    ioaddr_t src  = sdramaddr_to_ioaddr (dev, from);
++    u8      *dest = (u8 *) to;
++    ioaddr_t lim  = src + nbytes;
++
++    for (; src < lim; src += sizeof (u8))
++      *dest++ = __elan4_readb (dev, src);
++}
++
++void
++elan4_sdram_copyw_from_sdram (ELAN4_DEV *dev, sdramaddr_t from, void *to, int nbytes)
++{
++    ioaddr_t src  = sdramaddr_to_ioaddr (dev, from);
++    u16     *dest = (u16 *) to;
++    ioaddr_t lim  = src + nbytes;
++
++    for (; src < lim; src += sizeof (u16))
++      *dest++ = __elan4_readw (dev, src);
++}
++
++void
++elan4_sdram_copyl_from_sdram (ELAN4_DEV *dev, sdramaddr_t from, void *to, int nbytes)
++{
++    ioaddr_t src  = sdramaddr_to_ioaddr (dev, from);
++    u32     *dest = (u32 *) to;
++    ioaddr_t lim  = src + nbytes;
++
++    for (; src < lim; src += sizeof (u32))
++      *dest++ = __elan4_readl (dev, src);
++}
++
++void
++elan4_sdram_copyq_from_sdram (ELAN4_DEV *dev, sdramaddr_t from, void *to, int nbytes)
++{
++    ioaddr_t src  = sdramaddr_to_ioaddr (dev, from);
++    u64     *dest = (u64 *) to;
++    ioaddr_t lim  = src + nbytes;
++
++#ifdef CONFIG_MPSAS
++    if (sas_copyfrom_dev (dev->dev_osdep.pdev, ELAN4_BAR_SDRAM, from, (unsigned long) to, nbytes) == 0)
++      return;
++#endif
++
++    for (; src < lim; src += sizeof (u64))
++      *dest++ = readq (src);
++}
++
++void
++elan4_sdram_copyb_to_sdram (ELAN4_DEV *dev, void *from, sdramaddr_t to, int nbytes)
++{
++    ioaddr_t dest = sdramaddr_to_ioaddr (dev, to);
++    u8      *src  = (u8 *) from;
++    ioaddr_t lim  = dest + nbytes;
++
++    for (; dest < lim; dest += sizeof (u8))
++      writeb (*src++, dest);
++
++    mb();
++}
++
++void
++elan4_sdram_copyw_to_sdram (ELAN4_DEV *dev, void *from, sdramaddr_t to, int nbytes)
++{
++    ioaddr_t dest = sdramaddr_to_ioaddr (dev, to);
++    u16     *src  = (u16 *) from;
++    ioaddr_t lim  = dest + nbytes;
++
++    for (; dest < lim; dest += sizeof (u16))
++      writew (*src++, dest);
++
++    mb();
++}
++
++void
++elan4_sdram_copyl_to_sdram (ELAN4_DEV *dev, void *from, sdramaddr_t to, int nbytes)
++{
++    ioaddr_t dest = sdramaddr_to_ioaddr (dev, to);
++    u32     *src  = (u32 *) from;
++    ioaddr_t lim  = dest + nbytes;
++
++    for (; dest < lim; dest += sizeof (u16))
++      writew (*src++, dest);
++
++    mb();
++}
++
++void
++elan4_sdram_copyq_to_sdram (ELAN4_DEV *dev, void *from, sdramaddr_t to, int nbytes)
++{
++    ioaddr_t dest = sdramaddr_to_ioaddr (dev, to);
++    u64     *src  = (u64 *) from;
++    ioaddr_t lim  = dest + nbytes;
++
++#ifdef CONFIG_MPSAS
++    if (sas_copyto_dev (dev->dev_osdep.pdev, ELAN4_BAR_SDRAM, to, (unsigned long) from, nbytes) == 0)
++      return;
++#endif
++
++    for (; dest < lim; dest += sizeof (u64))
++      writeq (*src++, dest);
++
++    mb();
++}
++
++/* sdram buddy allocator */
++typedef struct sdramblock
++{
++    sdramaddr_t       next;
++    sdramaddr_t prev;
++} sdramblock_t;
++
++static inline sdramaddr_t
++read_next (ELAN4_DEV *dev, sdramaddr_t block)
++{
++    return __elan4_readl (dev, sdramaddr_to_ioaddr (dev, block + offsetof (sdramblock_t, next)));
++}
++
++static inline sdramaddr_t
++read_prev (ELAN4_DEV *dev, sdramaddr_t block)
++{
++    return __elan4_readl (dev, sdramaddr_to_ioaddr (dev, block + offsetof (sdramblock_t, prev)));
++}
++
++static inline void
++write_next (ELAN4_DEV *dev, sdramaddr_t block, sdramaddr_t val)
++{
++    writel (val, sdramaddr_to_ioaddr (dev, block + offsetof (sdramblock_t, next)));
++}
++
++static inline void
++write_prev (ELAN4_DEV *dev, sdramaddr_t block, sdramaddr_t val)
++{
++    writel (val, sdramaddr_to_ioaddr (dev, block + offsetof (sdramblock_t, prev)));
++}
++
++static inline void
++freelist_insert (ELAN4_DEV *dev, int idx, sdramaddr_t block)
++{
++    sdramaddr_t next = dev->dev_sdram_freelists[(idx)];
++
++    /*
++     * block->prev = NULL;
++     * block->next = next;
++     * if (next != NULL)
++     *    next->prev = block;
++     * freelist = block;
++     */
++    write_prev (dev, block, (sdramaddr_t) 0);
++    write_next (dev, block, next);
++    if (next != (sdramaddr_t) 0)
++      write_prev (dev, next, block);
++    dev->dev_sdram_freelists[idx] = block;
++
++    dev->dev_sdram_freecounts[idx]++;
++    dev->dev_stats.s_sdram_bytes_free += (SDRAM_MIN_BLOCK_SIZE << idx);
++
++    mb();
++}
++
++static inline void
++freelist_remove (ELAN4_DEV *dev,int idx, sdramaddr_t block)
++{
++    /*
++     * if (block->prev)
++     *     block->prev->next = block->next;
++     * else
++     *     dev->dev_sdram_freelists[idx] = block->next;
++     * if (block->next)
++     *     block->next->prev = block->prev;
++     */
++    sdramaddr_t blocknext = read_next (dev, block);
++    sdramaddr_t blockprev = read_prev (dev, block);
++
++    if (blockprev)
++      write_next (dev, blockprev, blocknext);
++    else
++      dev->dev_sdram_freelists[idx] = blocknext;
++    if (blocknext)
++      write_prev (dev, blocknext, blockprev);
++
++    dev->dev_sdram_freecounts[idx]--;
++    dev->dev_stats.s_sdram_bytes_free -= (SDRAM_MIN_BLOCK_SIZE << idx);
++
++    mb();
++}
++
++static inline void
++freelist_removehead(ELAN4_DEV *dev, int idx, sdramaddr_t block)
++{
++    sdramaddr_t blocknext = read_next (dev, block);
++
++    if ((dev->dev_sdram_freelists[idx] = blocknext) != 0)
++      write_prev (dev, blocknext, 0);
++
++    dev->dev_sdram_freecounts[idx]--;
++    dev->dev_stats.s_sdram_bytes_free -= (SDRAM_MIN_BLOCK_SIZE << idx);
++
++    mb();
++}
++
++#ifdef DEBUG
++static int
++display_blocks (ELAN4_DEV *dev, int indx, char *string)
++{
++    sdramaddr_t block;
++    int nbytes = 0;
++
++    PRINTF (DBG_DEVICE, DBG_SDRAM, "%s - indx %d\n", string, indx);
++    for (block = dev->dev_sdram_freelists[indx]; block != (sdramaddr_t) 0; block = read_next (dev, block))
++    {
++      PRINTF (DBG_DEVICE, DBG_SDRAM, "  %x\n", block);
++      nbytes += (SDRAM_MIN_BLOCK_SIZE << indx);
++    }
++
++    return (nbytes);
++}
++
++void
++elan4_sdram_display (ELAN4_DEV *dev, char *string)
++{
++    int indx;
++    int nbytes = 0;
++    
++    PRINTF (DBG_DEVICE, DBG_SDRAM, "elan4_sdram_display: dev=%p\n", dev);
++    for (indx = 0; indx < SDRAM_NUM_FREE_LISTS; indx++)
++      if (dev->dev_sdram_freelists[indx] != (sdramaddr_t) 0)
++          nbytes += display_blocks (dev, indx, string);
++    PRINTF (DBG_DEVICE, DBG_SDRAM, "\n%d bytes free - %d pages free\n", nbytes, nbytes/SDRAM_PAGE_SIZE);
++}
++
++void
++elan4_sdram_verify (ELAN4_DEV *dev)
++{
++    int indx, size, nbits, i, b;
++    sdramaddr_t block;
++
++    for (indx = 0, size = SDRAM_MIN_BLOCK_SIZE; indx < SDRAM_NUM_FREE_LISTS; indx++, size <<= 1)
++    {
++      unsigned count = 0;
++
++      for (block = dev->dev_sdram_freelists[indx]; block; block = read_next (dev, block), count++)
++      {
++          ELAN4_SDRAM_BANK *bank = sdramaddr_to_bank (dev, block);
++          unsigned         off  = sdramaddr_to_bankoffset (dev, block);
++          int              bit  = sdramaddr_to_bit (dev, indx, block);
++
++          if ((block & (size-1)) != 0)
++              printk ("elan4_sdram_verify: block=%lx indx=%x - not aligned\n", block, indx);
++          
++          if (bank == NULL || off > bank->b_size)
++              printk ("elan4_sdram_verify: block=%lx indx=%x - outside bank\n", block, indx);
++          else if (BT_TEST (bank->b_bitmaps[indx], bit) == 0)
++              printk ("elan4_sdram_verify: block=%lx indx=%x - bit not set\n", block, indx);
++          else
++          {
++              for (i = indx-1, nbits = 2; i >= 0; i--, nbits <<= 1)
++              {
++                  bit = sdramaddr_to_bit (dev, i, block);
++
++                  for (b = 0; b < nbits; b++)
++                      if (BT_TEST(bank->b_bitmaps[i], bit + b))
++                          printk ("elan4_sdram_verify: block=%lx indx=%x - also free i=%d bit=%x\n", block, indx, i, bit+b);
++              }
++          }
++      }
++
++      if (dev->dev_sdram_freecounts[indx] != count)
++          printk ("elan4_sdram_verify: indx=%x expected %d got %d\n", indx, dev->dev_sdram_freecounts[indx], count);
++    }
++}
++
++#endif
++
++static void
++free_block (ELAN4_DEV *dev, sdramaddr_t block, int indx)
++{
++    ELAN4_SDRAM_BANK *bank = sdramaddr_to_bank (dev, block);
++    unsigned         bit  = sdramaddr_to_bit (dev, indx, block);
++    unsigned         size = SDRAM_MIN_BLOCK_SIZE << indx;
++
++    PRINTF3 (DBG_DEVICE, DBG_SDRAM, "free_block: block=%x indx=%d bit=%x\n", block, indx, bit);
++
++    ASSERT ((block & (size-1)) == 0);
++    ASSERT (BT_TEST (bank->b_bitmaps[indx], bit) == 0);
++
++    while (BT_TEST (bank->b_bitmaps[indx], bit ^ 1))
++    {
++      sdramaddr_t buddy = block ^ size;
++      
++      PRINTF3 (DBG_DEVICE, DBG_SDRAM, "free_block: merge block=%x buddy=%x indx=%d\n", block, buddy, indx);
++      
++      BT_CLEAR (bank->b_bitmaps[indx], bit ^ 1);
++      
++      freelist_remove (dev, indx, buddy);
++      
++      block = (block < buddy) ? block : buddy;
++      indx++;
++      size <<= 1;
++      bit >>= 1;
++    }
++    
++    PRINTF3 (DBG_DEVICE, DBG_SDRAM, "free_block: free block=%x indx=%d bit=%x\n", block, indx, bit);
++    
++    freelist_insert (dev, indx, block);
++    
++    BT_SET (bank->b_bitmaps[indx], bit);
++}
++
++void
++elan4_sdram_init (ELAN4_DEV *dev)
++{
++    int indx;
++
++    spin_lock_init (&dev->dev_sdram_lock);
++
++    for (indx = 0; indx < SDRAM_NUM_FREE_LISTS; indx++)
++    {
++      dev->dev_sdram_freelists[indx]  = (sdramaddr_t) 0;
++      dev->dev_sdram_freecounts[indx] = 0;
++    }
++}
++
++void
++elan4_sdram_fini (ELAN4_DEV *dev)
++{
++    spin_lock_destroy (&dev->dev_sdram_lock);
++}
++
++#ifdef CONFIG_MPSAS
++/* size of Elan SDRAM in simulation */
++#define SDRAM_used_addr_bits          (16)
++#define SDRAM_SIMULATION_BANK_SIZE    ((1 << SDRAM_used_addr_bits) * 8)       /* 128 kbytes */
++
++static int
++elan4_sdram_probe_bank (ELAN4_DEV *dev, ELAN4_SDRAM_BANK *bank)
++{
++    printk ("elan%d: memory bank %d is %d Kb\n", dev->dev_instance, (int) (bank - dev->dev_sdram_banks), (int) (SDRAM_SIMULATION_BANK_SIZE / 1024));
++
++    bank->b_size = SDRAM_SIMULATION_BANK_SIZE;
++
++    return 1;
++}
++
++#else
++
++static void
++initialise_cache_tags (ELAN4_DEV *dev, unsigned addr)
++{
++    register int set, line;
++
++    mb();
++
++    /* Initialise the whole cache to hold sdram at "addr" as direct mapped */
++
++    for (set = 0; set < E4_NumCacheSets; set++)
++      for (line = 0; line < E4_NumCacheLines; line++)
++          write_tag (dev, Tags[set][line], addr | (set << 13) | (1 << 11));
++
++    read_tag (dev, Tags[set][line]);  /* read it back to guarantee the memory system is quite again */
++    mb();
++}
++
++static __inline__ int
++sdram_GreyToBinary(int GreyVal, int NoOfBits)
++{
++    int Bit;
++    int BinaryVal=0;
++    for (Bit=(1 << (NoOfBits-1)); Bit != 0; Bit >>= 1)
++      BinaryVal ^= (GreyVal & Bit) ^ ((BinaryVal >> 1) & Bit);
++    return (BinaryVal);
++}
++
++static __inline__ int
++sdram_BinaryToGrey(int BinaryVal)
++{
++    return (BinaryVal ^ (BinaryVal >> 1));
++}
++
++void
++elan4_sdram_setup_delay_lines (ELAN4_DEV *dev)
++{
++    /* This is used to fix the SDRAM delay line values */
++    int i, AutoGenDelayValue=0;
++    int NewDelayValue;
++
++    if (dev->dev_sdram_cfg & SDRAM_FIXED_DELAY_ENABLE)          /* already setup. */
++      return;
++
++    /* now get an average of 10 dll values */
++    for (i=0;i<10;i++)
++       AutoGenDelayValue += sdram_GreyToBinary(SDRAM_GET_DLL_DELAY(read_reg64 (dev, SDRamConfigReg)),
++                                             SDRAM_FIXED_DLL_DELAY_BITS);
++
++    NewDelayValue = SDRAM_DLL_CORRECTION_FACTOR + (AutoGenDelayValue / 10); /* Mean of 10 values */
++
++    dev->dev_sdram_cfg = (dev->dev_sdram_cfg & ~(SDRAM_FIXED_DLL_DELAY_MASK << SDRAM_FIXED_DLL_DELAY_SHIFT)) |
++                        SDRAM_FIXED_DELAY_ENABLE | SDRAM_FIXED_DLL_DELAY(sdram_BinaryToGrey(NewDelayValue));
++
++    write_reg64 (dev, SDRamConfigReg, dev->dev_sdram_cfg);    /* Put back the new value */
++
++    pioflush_reg (dev);
++}
++
++static int
++elan4_sdram_probe_bank (ELAN4_DEV *dev, ELAN4_SDRAM_BANK *bank)
++{
++    unsigned long      mappedsize = bank->b_size;
++    ioaddr_t           ioaddr;
++    unsigned long long value, size;
++    register int       i;
++    extern int         sdram_bank_limit;
++
++    if (mappedsize > SDRAM_MAX_BLOCK_SIZE)
++      mappedsize = SDRAM_MAX_BLOCK_SIZE;
++
++    while ((ioaddr = elan4_map_device (dev, ELAN4_BAR_SDRAM, bank->b_base, mappedsize, &bank->b_handle)) == 0)
++    {
++      if (mappedsize <= (64*1024*1024))                       /* boards normally populated with 64mb, so winge if we can't see this much */
++          printk ("elan%d: could not map bank %d size %dMb\n", dev->dev_instance, (int)(bank - dev->dev_sdram_banks), (int)mappedsize/(1024*1024));
++
++      if ((mappedsize >>= 1) < (1024*1024))
++          return 0;
++    }
++
++    /* first probe to see if the memory bank is present */
++    if (dev->dev_devinfo.dev_revision_id == PCI_REVISION_ID_ELAN4_REVA)
++       initialise_cache_tags (dev, E4_CacheSize);
++
++    for (i = 0; i < 64; i++)
++    {
++      unsigned long long pattern = (1ull << i);
++
++      writeq (pattern, ioaddr);                                       /* write pattern at base  */
++
++        if (dev->dev_devinfo.dev_revision_id == PCI_REVISION_ID_ELAN4_REVA)
++          initialise_cache_tags (dev, 0);
++
++      writeq (~pattern, ioaddr + E4_CacheSize);                       /* write ~pattern at cachesize */
++
++        if (dev->dev_devinfo.dev_revision_id == PCI_REVISION_ID_ELAN4_REVA)
++         initialise_cache_tags (dev, E4_CacheSize);
++      
++      writeq (~pattern, ioaddr + 2*E4_CacheSize);                     /* write ~pattern at 2*cachesize */
++        if (dev->dev_devinfo.dev_revision_id == PCI_REVISION_ID_ELAN4_REVA)
++          initialise_cache_tags (dev, 2*E4_CacheSize);
++      
++      value = readq (ioaddr);                                 /* read pattern back at 0 */
++      
++      if (value != pattern)
++      {
++          printk ("elan%d: sdram bank %d not present\n", dev->dev_instance, (int) (bank - dev->dev_sdram_banks));
++          elan4_unmap_device (dev, ioaddr, mappedsize, &bank->b_handle);
++          return 0;
++      }
++    }
++    
++    /* sdram bank is present, so work out it's size.  We store the maximum size at the base
++     * and then store the address at each address on every power of two address until
++     * we reach the minimum mappable size (PAGESIZE), we then read back the value at the
++     * base to determine the bank size */
++    writeq (mappedsize, ioaddr);
++    if (dev->dev_devinfo.dev_revision_id == PCI_REVISION_ID_ELAN4_REVA)
++        initialise_cache_tags (dev, 0);
++
++    for (size = mappedsize >> 1; size > PAGE_SIZE; size >>= 1)
++    {
++      writeq (size, ioaddr + size);
++        if (dev->dev_devinfo.dev_revision_id == PCI_REVISION_ID_ELAN4_REVA)
++          initialise_cache_tags (dev, size);
++    }
++
++    if ((size = readq (ioaddr)) < SDRAM_MIN_BANK_SIZE)
++    {
++      printk ("elan%d: memory bank %d dubious\n", dev->dev_instance, (int) (bank - dev->dev_sdram_banks));
++      elan4_unmap_device (dev, ioaddr, mappedsize, &bank->b_handle);
++      return 0;
++    }
++
++    if (sdram_bank_limit == 0 || size <= (sdram_bank_limit * 1024 * 1024))
++      printk ("elan%d: memory bank %d is %d Mb\n", dev->dev_instance, (int) (bank - dev->dev_sdram_banks), (int) (size / (1024*1024)));
++    else
++    {
++      size = (sdram_bank_limit * 1024 * 1024);
++      printk ("elan%d: limit bank %d to %d Mb\n", dev->dev_instance, (int) (bank - dev->dev_sdram_banks), (int) (size / (1024*1024)));
++    }
++
++    bank->b_size = size;
++
++    elan4_unmap_device (dev, ioaddr, mappedsize, &bank->b_handle);
++    return 1;
++}
++#endif
++
++int
++elan4_sdram_init_bank (ELAN4_DEV *dev, ELAN4_SDRAM_BANK *bank)
++{
++    int indx, size;
++
++    bank->b_ioaddr = 0;
++
++    if (! elan4_sdram_probe_bank (dev, bank))
++      return 0;
++
++    if ((bank->b_ioaddr = elan4_map_device (dev, ELAN4_BAR_SDRAM, bank->b_base, bank->b_size, &bank->b_handle)) == (ioaddr_t) 0)
++    {
++      printk ("elan%d: could not map sdrambank %d\n", dev->dev_instance, (int) (bank - dev->dev_sdram_banks));
++      return 0;
++    }
++
++    for (indx = 0, size = SDRAM_MIN_BLOCK_SIZE; size <= bank->b_size; indx++, size <<= 1) /* allocate the buddy allocator bitmaps */
++      KMEM_ZALLOC (bank->b_bitmaps[indx], bitmap_t *, sizeof (bitmap_t) * BT_BITOUL(bank->b_size/size), 1);
++    
++    return 1;
++}
++
++void
++elan4_sdram_fini_bank (ELAN4_DEV *dev, ELAN4_SDRAM_BANK *bank)
++{
++    int indx, size;
++
++    for (indx = 0, size = SDRAM_MIN_BLOCK_SIZE; size <= bank->b_size; indx++, size <<= 1)
++      KMEM_FREE (bank->b_bitmaps[indx], sizeof (bitmap_t) * BT_BITOUL(bank->b_size/size));
++    
++    elan4_unmap_device (dev, bank->b_ioaddr, bank->b_size, &bank->b_handle);
++}
++
++void
++elan4_sdram_add_bank (ELAN4_DEV *dev, ELAN4_SDRAM_BANK *bank)
++{
++    sdramaddr_t base = bank->b_base;
++    sdramaddr_t top  = bank->b_base + bank->b_size;
++    register int indx;
++    register unsigned long size;
++
++    /* align to the minimum block size */
++    base = (base + SDRAM_MIN_BLOCK_SIZE - 1) & ~((sdramaddr_t) SDRAM_MIN_BLOCK_SIZE-1);
++    top &= ~((sdramaddr_t) SDRAM_MIN_BLOCK_SIZE-1);
++
++    /* don't allow 0 as a valid "base" */
++    if (base == 0)
++      base = SDRAM_MIN_BLOCK_SIZE;
++
++    /* carve the bottom to the biggest boundary */
++    for (indx = 0, size = SDRAM_MIN_BLOCK_SIZE; indx < SDRAM_NUM_FREE_LISTS; indx++, size <<= 1)
++    {
++      if ((base & size) == 0)
++          continue;
++
++      if ((base + size) > top)
++          break;
++
++      free_block (dev, base, indx);
++      
++      base += size;
++    }
++
++    /* carve the top down to the biggest boundary */
++    for (indx = 0, size = SDRAM_MIN_BLOCK_SIZE; indx < SDRAM_NUM_FREE_LISTS; indx++, size <<= 1)
++    {
++      if ((top & size) == 0)
++          continue;
++
++      if ((top - size) < base)
++          break;
++
++      free_block (dev, (top - size), indx);
++      
++      top -= size;
++    }
++
++    /* now free of the space in between */
++    while (base < top)
++    {
++      free_block (dev, base, (SDRAM_NUM_FREE_LISTS-1));
++
++      base += SDRAM_MAX_BLOCK_SIZE;
++    }
++}
++
++sdramaddr_t
++elan4_sdram_alloc (ELAN4_DEV *dev, int nbytes)
++{
++    sdramaddr_t block;
++    register int i, indx;
++    unsigned long size;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->dev_sdram_lock, flags);
++
++    for (indx = 0, size = SDRAM_MIN_BLOCK_SIZE; size < nbytes; indx++, size <<= 1)
++      ;
++
++    PRINTF2 (DBG_DEVICE, DBG_SDRAM, "elan4_sdram_alloc: nbytes=%d indx=%d\n", nbytes, indx);
++
++    /* need to split a bigger block up */
++    for (i = indx; i < SDRAM_NUM_FREE_LISTS; i++, size <<= 1)
++      if (dev->dev_sdram_freelists[i])
++          break;
++    
++    if (i == SDRAM_NUM_FREE_LISTS)
++    {
++      spin_unlock_irqrestore (&dev->dev_sdram_lock, flags);
++      printk ("elan4_sdram_alloc: %d bytes failed\n", nbytes);
++      return ((sdramaddr_t) 0);
++    }
++    
++    PRINTF2 (DBG_DEVICE, DBG_SDRAM, "elan4_sdram_alloc: use block=%x indx=%d\n", dev->dev_sdram_freelists[i], i);
++
++    /* remove the block from the free list */
++    freelist_removehead (dev, i, (block = dev->dev_sdram_freelists[i]));
++
++    /* clear the approriate bit in the bitmap */
++    BT_CLEAR (sdramaddr_to_bank (dev, block)->b_bitmaps[i], sdramaddr_to_bit (dev,i, block));
++
++    /* and split it up as required */
++    while (i-- > indx)
++      free_block (dev, block + (size >>= 1), i);
++
++    spin_unlock_irqrestore (&dev->dev_sdram_lock, flags);
++
++    ASSERT ((block & ((SDRAM_MIN_BLOCK_SIZE << (indx))-1)) == 0);
++
++#ifdef CONFIG_MPSAS
++    elan4_sdram_zeroq_sdram (dev, block, sizeof (sdramblock_t));
++#endif
++
++    return ((sdramaddr_t) block);
++}
++
++void
++elan4_sdram_free (ELAN4_DEV *dev, sdramaddr_t block, int nbytes)
++{
++    register int indx;
++    unsigned long size;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->dev_sdram_lock, flags);
++
++    for (indx = 0, size = SDRAM_MIN_BLOCK_SIZE; size < nbytes; indx++, size <<= 1)
++      ;
++
++    PRINTF2 (DBG_DEVICE, DBG_SDRAM, "elan4_sdram_free: indx=%d block=%x\n", indx, block);
++
++    free_block (dev, block, indx);
++
++    spin_unlock_irqrestore (&dev->dev_sdram_lock, flags);
++}
++
++void
++elan4_sdram_flushcache (ELAN4_DEV *dev, sdramaddr_t addr, int len)
++{
++    int set, off;
++
++    SET_SYSCONTROL (dev, dev_direct_map_pci_writes, CONT_DIRECT_MAP_PCI_WRITES);
++
++    /*
++     * if flushing more than a single set (8K), then you have to flush the whole cache.
++     *   NOTE - in the real world we will probably want to generate a burst across
++     *          the pci bus.
++     */
++    if (len >= E4_CacheSetSize)
++    {
++      PRINTF3 (DBG_DEVICE, DBG_SDRAM, "elan4_sdram_flushcache: addr=%x len=%x (%x) => whole cache\n", addr, len, addr + len);
++
++#ifdef CONFIG_MPSAS
++      elan4_sdram_zeroq_sdram (dev, dev->dev_cacheflush_space, E4_CacheSize);
++#else
++      for (set = 0; set < E4_NumCacheSets; set++)
++          for (off = 0; off < E4_CacheSetSize; off += E4_CacheLineSize)
++              elan4_sdram_writeq (dev, dev->dev_cacheflush_space + (set * E4_CacheSetSize) + off, 0);
++#endif
++    }
++    else
++    {
++      unsigned base    = addr & ~(E4_CACHELINE_SIZE-1);
++      unsigned top     = (addr + len + (E4_CACHELINE_SIZE-1)) & ~(E4_CACHELINE_SIZE-1);
++      unsigned baseoff = base & (E4_CacheSetSize-1);
++      unsigned topoff  = top  & (E4_CacheSetSize-1);
++
++      if ((base ^ top) & E4_CacheSetSize)                     /* wraps */
++      {
++          PRINTF7 (DBG_DEVICE, DBG_SDRAM, "elan4_sdram_flushcache: addr=%x len=%x (%x) => split cache (%x,%x %x,%x)\n", 
++                   addr, len, addr + len, 0, topoff, baseoff, E4_CacheSetSize);
++
++#ifdef CONFIG_MPSAS
++          for (set = 0; set < E4_NumCacheSets; set++)
++          {
++              elan4_sdram_zeroq_sdram (dev, dev->dev_cacheflush_space + (set * E4_CacheSetSize), topoff);
++              elan4_sdram_zeroq_sdram (dev, dev->dev_cacheflush_space + (set * E4_CacheSetSize) + baseoff, E4_CacheSetSize - baseoff);
++          }
++#else
++          for (set = 0; set < E4_NumCacheSets; set++)
++          {
++              for (off = 0; off < (top & (E4_CacheSetSize-1)); off += E4_CACHELINE_SIZE)
++                  elan4_sdram_writeq (dev, dev->dev_cacheflush_space + (set * E4_CacheSetSize) + off, 0);
++              
++              for (off = (base & (E4_CacheSetSize-1)); off < E4_CacheSetSize; off += E4_CACHELINE_SIZE)
++                  elan4_sdram_writeq (dev, dev->dev_cacheflush_space + (set * E4_CacheSetSize) + off, 0);
++          }
++#endif
++      }
++      else
++      {
++          PRINTF5 (DBG_DEVICE, DBG_SDRAM, "elan4_sdram_flushcache: addr=%x len=%x (%x) => part cache (%x,%x)\n", 
++                   addr, len, addr + len, baseoff, topoff);
++
++#ifdef CONFIG_MPSAS
++          for (set = 0; set < E4_NumCacheSets; set++)
++              elan4_sdram_zeroq_sdram (dev, dev->dev_cacheflush_space + (set * E4_CacheSetSize) + baseoff, topoff - baseoff);
++#else
++          for (set = 0; set < E4_NumCacheSets; set++)
++              for (off = (base & (E4_CacheSetSize-1)); off < (top & (E4_CacheSetSize-1)); off += E4_CACHELINE_SIZE)
++                  elan4_sdram_writeq (dev, dev->dev_cacheflush_space + (set * E4_CacheSetSize) + off, 0);
++#endif
++      }
++    }
++    pioflush_sdram (dev);
++    
++    CLEAR_SYSCONTROL (dev, dev_direct_map_pci_writes, CONT_DIRECT_MAP_PCI_WRITES);
++}
++
++static char *
++get_correctableErr_bitpos(uint SyndromeBits)
++{
++    switch (SyndromeBits)
++    {
++    case 0x00: return ("NoErr");
++    case 0x31: return ("00"); 
++    case 0x32: return ("01"); 
++    case 0xc4: return ("02"); 
++    case 0xc8: return ("03"); 
++    case 0x26: return ("04"); 
++    case 0x91: return ("05"); 
++    case 0x89: return ("06"); 
++    case 0x64: return ("07"); 
++    case 0xc1: return ("08"); 
++    case 0xf2: return ("09"); 
++    case 0x34: return ("10"); 
++    case 0xf8: return ("11"); 
++    case 0xf1: return ("12"); 
++    case 0xc2: return ("13"); 
++    case 0xf4: return ("14"); 
++    case 0x38: return ("15"); 
++    case 0xd6: return ("16"); 
++    case 0xa1: return ("17"); 
++    case 0x79: return ("18"); 
++    case 0xa4: return ("19"); 
++    case 0xd9: return ("20"); 
++    case 0xa2: return ("21"); 
++    case 0x76: return ("22"); 
++    case 0xa8: return ("23"); 
++    case 0xe6: return ("24"); 
++    case 0x51: return ("25"); 
++    case 0xb9: return ("26"); 
++    case 0x54: return ("27"); 
++    case 0xe9: return ("28"); 
++    case 0x52: return ("29"); 
++    case 0xb6: return ("30"); 
++    case 0x58: return ("31"); 
++    case 0x13: return ("32"); 
++    case 0x23: return ("33"); 
++    case 0x4c: return ("34"); 
++    case 0x8c: return ("35"); 
++    case 0x62: return ("36"); 
++    case 0x19: return ("37"); 
++    case 0x98: return ("38"); 
++    case 0x46: return ("39"); 
++    case 0x1c: return ("40"); 
++    case 0x2f: return ("41"); 
++    case 0x43: return ("42"); 
++    case 0x8f: return ("43"); 
++    case 0x1f: return ("44"); 
++    case 0x2c: return ("45"); 
++    case 0x4f: return ("46"); 
++    case 0x83: return ("47"); 
++    case 0x6d: return ("48"); 
++    case 0x1a: return ("49"); 
++    case 0x97: return ("50"); 
++    case 0x4a: return ("51"); 
++    case 0x9d: return ("52"); 
++    case 0x2a: return ("53"); 
++    case 0x67: return ("54"); 
++    case 0x8a: return ("55"); 
++    case 0x6e: return ("56"); 
++    case 0x15: return ("57"); 
++    case 0x9b: return ("58"); 
++    case 0x45: return ("59"); 
++    case 0x9e: return ("60"); 
++    case 0x25: return ("61"); 
++    case 0x6b: return ("62"); 
++    case 0x85: return ("63"); 
++    case 0x01: return ("C0"); 
++    case 0x02: return ("C1"); 
++    case 0x04: return ("C2"); 
++    case 0x08: return ("C3"); 
++    case 0x10: return ("C4"); 
++    case 0x20: return ("C5"); 
++    case 0x40: return ("C6"); 
++    case 0x80: return ("C7"); 
++
++    case 0x07: case 0x0b: case 0x0d: case 0x0e: case 0x3d: case 0x3e: case 0x70: case 0x7c: // T  
++    case 0xb0: case 0xbc: case 0xc7: case 0xcb: case 0xd0: case 0xd3: case 0xe0: case 0xe3: // T  
++       return ("triple");
++
++    case 0x0f: case 0x55: case 0x5a: case 0xa5: case 0xaa: case 0xf0: case 0xff: // Q  
++       return ("quadruple");
++
++    case 0x16: case 0x29: case 0x37: case 0x3b: case 0x49: case 0x57: case 0x5b: case 0x5d: case 0x5e: case 0x61: // M  
++    case 0x68: case 0x73: case 0x75: case 0x7a: case 0x7f: case 0x86: case 0x92: case 0x94: case 0xa7: case 0xab: // M  
++    case 0xad: case 0xae: case 0xb3: case 0xb5: case 0xba: case 0xbf: case 0xcd: case 0xce: case 0xd5: case 0xda: // M  
++    case 0xdc: case 0xdf: case 0xe5: case 0xea: case 0xec: case 0xef: case 0xf7: case 0xfb: case 0xfd: case 0xfe: // M  
++       return ("multiple");
++
++    default:  // all other cases
++       return ("double");
++    }
++}
++
++char *
++elan4_sdramerr2str (ELAN4_DEV *dev, E4_uint64 status, char *str)
++{
++    E4_uint64 StartupSyndrome    = dev->dev_sdram_initial_ecc_val;
++    int       RisingDQSsyndrome  = ((ECC_RisingDQSSyndrome(status) == ECC_RisingDQSSyndrome(StartupSyndrome)) ?
++                                  0 : ECC_RisingDQSSyndrome(status));
++    int             FallingDQSsyndrome = ((ECC_FallingDQSSyndrome(status) == ECC_FallingDQSSyndrome(StartupSyndrome)) ?
++                                  0 : ECC_FallingDQSSyndrome(status));
++    E4_uint64 Addr = ECC_Addr(status);
++    int       Bank = (Addr >> 6) & 3;
++    int       Cas  = ((Addr >> 3) & 7) | ((Addr >> (8 - 3)) & 0xf8) | ((Addr >> (25 - 8)) & 0x100) |
++                   ((Addr >> (27 - 9)) & 0x200) | ((Addr >> (29 - 10)) & 0xc00);
++    int       Ras  = ((Addr >> 13) & 0xfff) | ((Addr >> (26 - 12)) & 0x1000) | ((Addr >> (28 - 13)) & 0x2000) |
++                   ((Addr >> (30 - 14)) & 0x4000);
++
++    sprintf (str, "Addr=%07llx Bank=%x Ras=%x Cas=%x Falling DQS=%s Rising DQS=%s Syndrome=%x%s%s%s%s",               /* 41 + 16 + 8 + 15 + 24 + 13 + 22 + 10 + 10 == 151 */
++           (long long)Addr, Bank, Ras, Cas,
++           get_correctableErr_bitpos(FallingDQSsyndrome),
++           get_correctableErr_bitpos(RisingDQSsyndrome),
++           (int)ECC_Syndrome(status),
++           ECC_UncorrectableErr(status)   ? " Uncorrectable" : "",
++           ECC_MultUncorrectErrs(status)  ? " Multiple-Uncorrectable" : "",
++           ECC_CorrectableErr(status)     ? " Correctable" : "",
++           ECC_MultCorrectErrs(status)    ? " Multiple-Correctable" : "");
++
++    return str;
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan4/trap.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan4/trap.c   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan4/trap.c        2005-06-01 23:12:54.619436064 -0400
+@@ -0,0 +1,778 @@
++/*
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: trap.c,v 1.19.10.2 2004/11/03 14:24:32 duncant Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/trap.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan4/debug.h>
++#include <elan4/device.h>
++
++#include <elan4/trtype.h>
++#include <elan4/commands.h>
++
++char * const PermTypes[16] = 
++{
++    "Disabled",       "Unused",          "LocalDataRead", "LocalDataWrite",
++    "LocalRead",      "LocalExecute",    "ReadOnly",      "LocalWrite",
++    "LocalEventOnly", "LocalEventWrite", "RemoteEvent",   "RemoteAll",
++    "RemoteReadOnly", "RemoteWriteOnly", "DataReadWrite", "NoFault",
++};
++
++char * const AccTypes[] =
++{
++    "LocalDataRead ", "LocalDataWrite", "RemoteRead    ", "RemoteWrite   ",
++    "Execute       ", "LocalEvent    ", "Unused        ", "RemoteEvent   "
++};
++char * const DataTypes[] = {"Byte ", "HWord", "Word ", "DWord"};
++char * const PhysTypes[] = {"Special Read", "Special Write", "Physical Read", "Physical Write"};
++    
++char * const EProcTrapNames[] = {
++    "EventProcNoFault",
++    "EventProcAddressAlignment",
++    "EventProcMemoryFault",
++    "EventProcCountWrapError",
++};
++
++char * const CProcTrapNames[] = {
++    "CommandProcNoFault",
++    "CommandProcInserterError",
++    "CommandProcPermissionTrap",
++    "CommandProcSendTransInvalid",
++    "CommandProcSendTransExpected",
++    "CommandProcDmaQueueOverflow",
++    "CommandProcInterruptQueueOverflow",
++    "CommandProcMemoryFault",
++    "CommandProcRouteFetchFault",
++    "CommandProcFailCountZero",
++    "CommandProcAddressAlignment",
++    "CommandProcWaitTrap",
++    "CommandProcMultipleGuards",
++    "CommandProcOpenOnGuardedChan",
++    "CommandProcThreadQueueOverflow",
++    "CommandProcBadData",
++};
++
++char *const CProcInsertError[] = {
++    "No Error",
++    "Overflowed",
++    "Invalid Write Size",
++    "Invalid Write Order",
++};
++
++char * const DProcTrapNames[] = {
++    "DmaProcNoFault",
++    "DmaProcRouteFetchFault",
++    "DmaProcFailCountError",
++    "DmaProcPacketAckError",
++    "DmaProcRunQueueReadFault",
++    "DmaProcQueueOverFlow",
++};
++
++char *const IProcTrapNames[] = {
++    "InputNoFault",
++    "InputAddressAlignment",
++    "InputMemoryFault",
++    "InputInvalidTransType",
++    "InputDmaQueueOverflow",
++    "InputEventEngineTrapped",
++    "InputCrcErrorAfterPAckOk",
++    "InputEopErrorOnWaitForEop",
++    "InputEopErrorTrap",
++    "InputDiscardAfterAckOk",
++};
++
++char *const TProcTrapNames[] = {
++    "HaltThread",
++    "TrapForTooManyInstructions",
++    "InstAccessException",
++    "Unimplemented",
++    "DataAccessException",
++    "DataAlignmentError",
++    "TrapForUsingBadData",
++};
++
++#define declare_spaces(space, str)            char space[64]; do { int i; for (i = 0; i < strlen(str); i++) spaces[i] = ' '; space[i] = '\0'; } while (0)
++#define declare_prefix(space, spaces, str)    char space[64]; do { strcpy (space, spaces); strcat (space, str); } while (0)
++
++void
++elan4_display_farea (void *type, int mode, char *str, E4_FaultSave *farea)
++{
++    E4_uint32 FSR = FaultSaveFSR(farea->FSRAndFaultContext);
++
++    declare_spaces(spaces, str);
++    
++    elan4_debugf (type, mode, "%s Fault occurred at %016llx for context %4x\n", str,
++                farea->FaultAddress, FaultSaveContext(farea->FSRAndFaultContext));
++    
++    if (FSR & AT_VirtualWriteAccBit)                          /* Virtual write access */
++      elan4_debugf (type, mode, "%s FSR=%x: Virtual Write. DWSize=0x%x EndP=0x%x Access=%s DT=%s\n",
++                    spaces, FSR, FSR & AT_VirtualWriteSizeMask,
++                    (FSR >> AT_VirtualWriteEndPtrShift) & AT_VirtualWriteEndPtrMask,
++                    AccTypes[(FSR >> AT_PermBitsShift) & AT_PermBitsMask],
++                    DataTypes[(FSR >> AT_BlkDataTyShift) & AT_BlkDataTyMask]);
++    else if (FSR & AT_VirtualReadAccBit)                      /* Virtual read access */
++      elan4_debugf (type, mode, "%s FSR=%x: Virtual Read. DWSize=0x%x Access=%s DT=%s\n",
++                    spaces, FSR, FSR & AT_VirtualReadSizeMask,
++                    AccTypes[(FSR >> AT_PermBitsShift) & AT_PermBitsMask],
++                    DataTypes[(FSR >> AT_BlkDataTyShift) & AT_BlkDataTyMask]);
++    else
++      elan4_debugf (type, mode, "%s FSR=%x: %s. Size=0x%x\n", spaces,
++                    FSR, PhysTypes[(FSR >> AT_SelBitsShift) & AT_SelBitsMask],
++                    FSR & AT_OtherSizeMask);
++    elan4_debugf (type, mode, "%s FSR: %s %s%s %sWalking\n", spaces,
++                (FSR & AT_NonAlloc) ? "NonAlloc" : "Alloc",
++                (FSR & AT_DmaData) ? "Dma " : "",
++                (FSR & FSR_WalkForThread) ? "ThreadAcc" : "UnitsAcc",
++                (FSR & FSR_Walking) ? "" : "Not");
++    PRINTF (type, mode, "%s FSR: %s%sHashTable=%s\n", spaces,
++          (FSR & FSR_NoTranslationsFound) ? "NoTranslationsFound " : "",
++          (FSR & FSR_WalkingProtectionFault) ? "WalkingProtectionFault " : "",
++          (FSR & FSR_HashTable1) ? "1" : "0");
++    if (FSR & (FSR_RouteVProcErr | FSR_FaultForBadData))
++      elan4_debugf (type, mode, "%s FSR: %s%s\n", spaces,
++                    (FSR & FSR_RouteVProcErr) ? "RouteVProcErr " : "",
++                    (FSR & FSR_FaultForBadData) ? "FaultForBadData " : "");
++}
++
++void
++elan4_display_eproc_trap (void *type, int mode, char *str, ELAN4_EPROC_TRAP *trap)
++{
++    declare_spaces (spaces, str);
++
++    elan4_debugf (type, mode, "%s Status=%016llx %s EventAddr=%016llx CountAndType=%016llx\n", str,
++                trap->tr_status, EProcTrapNames[EPROC_TrapType(trap->tr_status)],
++                trap->tr_eventaddr, trap->tr_event.ev_CountAndType);
++    elan4_debugf (type, mode, "%s Param=%016llx.%016llx\n", spaces,
++                trap->tr_event.ev_Params[0], trap->tr_event.ev_Params[1]);
++
++    elan4_display_farea (type, mode, strcat (spaces, EPROC_Port0Fault(trap->tr_status) ? " EPROC0" : " EPROC1"), &trap->tr_faultarea);
++}
++
++void
++elan4_display_cproc_trap (void *type, int mode, char *str, ELAN4_CPROC_TRAP *trap)
++{
++    declare_spaces(spaces, str);
++
++    elan4_debugf (type, mode, "%s Status=%llx %s Command=%llx\n", str, trap->tr_status, 
++                CProcTrapNames[CPROC_TrapType(trap->tr_status)], trap->tr_command);
++    elan4_debugf (type, mode, "%s Desc=%016llx %016llx %016llx %016llx\n", str,
++                trap->tr_qdesc.CQ_QueuePtrs, trap->tr_qdesc.CQ_HoldingValue,
++                trap->tr_qdesc.CQ_AckBuffers, trap->tr_qdesc.CQ_Control);
++
++    switch (CPROC_TrapType (trap->tr_status))
++    {
++    case CommandProcInserterError:
++      elan4_debugf (type, mode, "%s   %s\n", str, CProcInsertError[CQ_RevB_ErrorType(trap->tr_qdesc.CQ_QueuePtrs)]);
++      break;
++
++    case CommandProcWaitTrap:
++      elan4_display_eproc_trap (type, mode, spaces, &trap->tr_eventtrap);
++      break;
++
++    default:
++      elan4_display_farea (type, mode, spaces, &trap->tr_faultarea);
++      break;
++    }
++}
++
++void
++elan4_display_dproc_trap (void *type, int mode, char *str, ELAN4_DPROC_TRAP *trap)
++{
++    declare_spaces (spaces, str);
++
++    elan4_debugf (type, mode, "%s status %llx - %s\n", str,
++                trap->tr_status, DProcTrapNames[DPROC_TrapType(trap->tr_status)]);
++
++    elan4_debugf (type, mode, "%s DESC %016llx %016llx %016llx %016llx\n", spaces, trap->tr_desc.dma_typeSize, 
++                trap->tr_desc.dma_cookie, trap->tr_desc.dma_vproc, trap->tr_desc.dma_srcAddr);
++    elan4_debugf (type, mode, "%s      %016llx %016llx %016llx\n", spaces, trap->tr_desc.dma_dstAddr, 
++                trap->tr_desc.dma_srcEvent, trap->tr_desc.dma_dstEvent);
++
++    if (DPROC_PrefetcherFault (trap->tr_status))
++      elan4_display_farea (type, mode, spaces, &trap->tr_prefetchFault);
++}
++
++void
++elan4_display_tproc_trap (void *type, int mode, char *str, ELAN4_TPROC_TRAP *trap)
++{
++    register int i;
++    declare_spaces (spaces, str);
++
++    elan4_debugf (type, mode, "%s PC=%016llx nPC=%016llx State=%016llx Status=%016llx -%s%s%s%s\n", str,
++                trap->tr_pc, trap->tr_npc, trap->tr_state, trap->tr_status, 
++                (trap->tr_state & TS_TrapForTooManyInstructions) ? " TrapForTooManyInstructions" : "",
++                (trap->tr_state & TS_Unimplemented)              ? " Unimplemented"              : "",
++                (trap->tr_state & TS_DataAlignmentError)         ? " DataAlignmentError"         : "",
++                (trap->tr_state & TS_InstAccessException)        ? " InstAccessException"        : "",
++                (trap->tr_state & TS_DataAccessException)        ? " DataAlignmentError"         : "");
++    
++    for (i = 0; i < 64; i += 4)
++      elan4_debugf (type, mode, "%s r%d - %016llx %016llx %016llx %016llx\n", spaces, i,
++                    trap->tr_regs[i], trap->tr_regs[i+1], trap->tr_regs[i+2], trap->tr_regs[i+3]);
++    
++    if (trap->tr_state & TS_InstAccessException)
++    {
++      declare_prefix (prefix, spaces, "Inst");
++
++      elan4_display_farea (type, mode, prefix, &trap->tr_instFault);
++    }
++
++    if (trap->tr_state & TS_DataAccessException)
++    {
++      declare_prefix (prefix, spaces, "Data");
++      elan4_display_farea (type, mode, prefix, &trap->tr_dataFault);
++    }
++}
++
++void
++elan4_display_iproc_trap (void *type, int mode, char *str, ELAN4_IPROC_TRAP *trap)
++{
++    register int i;
++    declare_spaces (spaces, str);
++
++    for (i = 0; i < trap->tr_numTransactions; i++)
++    {
++      E4_IprocTrapHeader *hdrp    = &trap->tr_transactions[i];
++      E4_uint64           status  = hdrp->IProcStatusCntxAndTrType;
++      E4_Addr             addr    = hdrp->TrAddr;
++      char               *typeString;
++      char                buffer[256];
++      char               *ptr = buffer;
++      
++      if (IPROC_EOPTrap(status))
++      {
++          switch (IPROC_EOPType(status))
++          {
++          case EOP_GOOD:        typeString = "EopGood";   break;
++          case EOP_BADACK:      typeString = "EopBadAck"; break;
++          case EOP_ERROR_RESET: typeString = "EopReset";  break;
++          default:              typeString = "EopBad";    break;
++          }
++          
++          ptr += sprintf (ptr, "%15s Cntx=%-6d", typeString, IPROC_NetworkContext(status));
++      }
++      else
++      {
++          if (IPROC_BadLength(status))
++              typeString = "BadLength";
++          else if (IPROC_TransCRCStatus(status) == CRC_STATUS_DISCARD)
++              typeString = "DiscardCrc";
++          else if (IPROC_TransCRCStatus(status) == CRC_STATUS_ERROR)
++              typeString = "ErrorCrc Remote Network error";
++          else if (IPROC_TransCRCStatus(status) == CRC_STATUS_BAD)
++              typeString = "BadCrc Cable error into this node.";
++          else
++          {
++              if ((IPROC_TransactionType(status) & TR_BLOCK_OPCODE_MASK) == TR_WRITEBLOCK)
++                  typeString = "WriteBlock";
++              else
++              {
++                  switch (IPROC_TransactionType(status) & TR_OPCODE_MASK)
++                  {
++                  case TR_SETEVENT_IDENTIFY & TR_OPCODE_MASK: typeString = "SetEvent";        break;
++                  case TR_REMOTEDMA & TR_OPCODE_MASK:         typeString = "RemoteDma";       break;
++                  case TR_SENDDISCARD & TR_OPCODE_MASK:       typeString = "SendDiscard";     break;
++                  case TR_GTE & TR_OPCODE_MASK:               typeString = "GTE";             break;
++                  case TR_LT & TR_OPCODE_MASK:                typeString = "LT";              break;
++                  case TR_EQ & TR_OPCODE_MASK:                typeString = "EQ";              break;
++                  case TR_NEQ & TR_OPCODE_MASK:               typeString = "NEQ";             break;
++                  case TR_IDENTIFY & TR_OPCODE_MASK:          typeString = "Idenfity";        break;
++                  case TR_ADDWORD & TR_OPCODE_MASK:           typeString = "AddWord";         break;
++                  case TR_INPUT_Q_COMMIT & TR_OPCODE_MASK:    typeString = "InputQCommit";    break;
++                  case TR_TESTANDWRITE & TR_OPCODE_MASK:      typeString = "TestAndWrite";    break;
++                  case TR_INPUT_Q_GETINDEX & TR_OPCODE_MASK:  typeString = "InputQGetIndex";  break;
++                  case TR_TRACEROUTE_TRANS & TR_OPCODE_MASK:  typeString = "TraceRoute";      break;
++                  default:                                    typeString = "Unknown";         break;
++                  }
++              }
++          }
++
++          ptr += sprintf (ptr, "%15s Cntx=%-6d Addr=%016llx", typeString, IPROC_NetworkContext(status), (unsigned long long) addr);
++      }
++      
++      
++      if (IPROC_TrapValue(status) != InputNoFault)
++      {
++          ptr += sprintf (ptr, " TrType=%2d ChanTrapped=%x GoodAck=%x BadAck=%x InputterChan=%d", IPROC_TrapValue(status),
++                          IPROC_ChannelTrapped(status), IPROC_GoodAckSent(status), IPROC_BadAckSent(status),
++                          IPROC_InputterChan(status));
++          if (IPROC_EOPTrap(status))
++              ptr += sprintf (ptr, " EOPType=%d", IPROC_EOPType(status));
++          else
++              ptr += sprintf (ptr, " %s%s%s%s", 
++                              IPROC_FirstTrans(status) ? " FirstTrans" : "",
++                              IPROC_LastTrans(status) ? " LastTrans" : "",
++                              (IPROC_TransactionType(status) & TR_WAIT_FOR_EOP) ? " WaitForEop" : "",
++                              (IPROC_GoodAckSent(status) &  (1 << IPROC_Channel(status))) ? " AckSent" : "");
++      }
++      
++      elan4_debugf (type, mode, "%s %s\n", str, buffer);
++
++      str = spaces;
++    }
++
++    elan4_display_farea (type, mode, spaces, &trap->tr_faultarea);
++}
++
++#define elan4_sdram_copy_faultarea(dev, unit, farea) \
++    elan4_sdram_copyq_from_sdram ((dev), (dev)->dev_faultarea + (unit) * sizeof (E4_FaultSave), (E4_uint64 *) farea, sizeof (E4_FaultSave));
++
++void
++elan4_extract_eproc_trap (ELAN4_DEV *dev, E4_uint64 status, ELAN4_EPROC_TRAP *trap, int iswaitevent)
++{
++    /* only one of the memory ports can fault at a time */
++    ASSERT (EPROC_TrapType(status) != EventProcMemoryFault || (EPROC_Port0Fault(status) ^ EPROC_Port1Fault(status)) == 1);
++
++    trap->tr_status = status;
++    
++    if (EPROC_Port0Fault(status))
++      elan4_sdram_copy_faultarea (dev, CUN_EventProc0, &trap->tr_faultarea);
++    if (EPROC_Port1Fault(status))
++      elan4_sdram_copy_faultarea (dev, CUN_EventProc1, &trap->tr_faultarea);
++
++    if (iswaitevent)
++    {
++      /*
++       * for waitevents the Event address is always taken from the command processor
++       * 
++       * if we trapped during the copy then we take the "Event" from the event processor
++       * since we need to complete the copy.  Otherwise we'll be reissuing the original
++       * command again
++       */
++      E4_uint32 fsr = FaultSaveFSR(trap->tr_faultarea.FSRAndFaultContext);
++
++      trap->tr_eventaddr = read_reg64 (dev, CommandHold) ^ WAIT_EVENT_CMD;
++
++      if (EPROC_TrapType(trap->tr_status) == EventProcMemoryFault && 
++          (AT_Perm(fsr) == AT_PermLocalDataRead || AT_Perm(fsr) == AT_PermLocalDataWrite))
++      {
++          trap->tr_event.ev_CountAndType = read_reg64 (dev, EventCountAndType);
++          trap->tr_event.ev_Params[0]    = read_reg64 (dev, EventParameters[0]);
++          trap->tr_event.ev_Params[1]    = read_reg64 (dev, EventParameters[1]);
++      }
++      else
++      {
++          trap->tr_event.ev_Params[0]    = read_reg64 (dev, CommandCopy[5]);
++          trap->tr_event.ev_CountAndType = read_reg64 (dev, CommandCopy[4]);
++          trap->tr_event.ev_Params[1]    = read_reg64 (dev, CommandCopy[6]);
++
++      }
++    }
++    else
++    {
++      trap->tr_eventaddr             = read_reg64 (dev, EventAddress);
++      trap->tr_event.ev_CountAndType = read_reg64 (dev, EventCountAndType);
++      trap->tr_event.ev_Params[0]    = read_reg64 (dev, EventParameters[0]);
++      trap->tr_event.ev_Params[1]    = read_reg64 (dev, EventParameters[1]);
++    }
++
++    BumpDevStat (dev, s_eproc_trap_types[EPROC_TrapType(status)]);
++}
++
++int 
++cproc_open_extract_vp (ELAN4_DEV *dev, ELAN4_CQ *cq)
++{
++      /* cq = ucq->ucq_cq */
++      if ((cq->cq_perm & CQ_STENEnableBit) != 0)
++      {
++            sdramaddr_t   cqdesc       = dev->dev_cqaddr + (elan4_cq2num(cq) * sizeof (E4_CommandQueueDesc));
++          E4_uint64     queuePtrs    = elan4_sdram_readq (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_QueuePtrs));
++          sdramaddr_t   insertPtr    = (queuePtrs & CQ_PtrMask);
++          sdramaddr_t   commandPtr   = CQ_CompletedPtr (queuePtrs);
++          unsigned int  cqSize       = CQ_Size ((queuePtrs >> CQ_SizeShift) & CQ_SizeMask);
++          E4_uint64     openCommand  = 0;
++
++          if (dev->dev_devinfo.dev_revision_id != PCI_REVISION_ID_ELAN4_REVA && (queuePtrs & CQ_RevB_ReorderingQueue))
++          {
++              E4_uint32 oooMask = elan4_sdram_readl (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_HoldingValue));
++
++              for (; (oooMask & 1) != 0; oooMask >>= 1)
++                  insertPtr = (insertPtr & ~(cqSize-1)) | ((insertPtr + sizeof (E4_uint64)) & (cqSize-1));
++          }
++
++          while (commandPtr != insertPtr)
++          {
++              E4_uint64    command = elan4_sdram_readq (dev, commandPtr);
++              unsigned int cmdSize;
++
++                switch (__categorise_command (command, &cmdSize))
++              {
++              case 0:
++                  (void) __whole_command (&commandPtr, insertPtr, cqSize, cmdSize);
++                  break;
++
++              case 1: /* open */
++                  return (command >> 32);
++                          
++                  break; /* Not reached */
++
++              case 2:
++                  if (openCommand == 0)
++                      (void) __whole_command (&commandPtr, insertPtr, cqSize, cmdSize);
++                  /* Else we should have stopped by now */
++                  else ASSERT(1==2);
++              case 3:
++                  printk ("cproc_open_extract_vp: invalid command %llx\n", command);
++                  return -1;
++              }
++          } /* while */
++      }
++
++      return -1;
++}
++
++void
++elan4_extract_cproc_trap (ELAN4_DEV *dev, E4_uint64 status, ELAN4_CPROC_TRAP *trap, unsigned cqnum)
++{
++    /* extract the state from the device */
++    elan4_sdram_copy_faultarea (dev, CUN_CommandProc, &trap->tr_faultarea);
++
++    trap->tr_status  = status;
++    trap->tr_command = read_reg64 (dev, CommandHold);
++    
++    elan4_sdram_copyq_from_sdram (dev, dev->dev_cqaddr + (cqnum * sizeof (E4_CommandQueueDesc)), &trap->tr_qdesc, sizeof (E4_CommandQueueDesc));
++
++    if (CPROC_TrapType (status) == CommandProcWaitTrap)
++      elan4_extract_eproc_trap (dev, read_reg64 (dev, EProcStatus), &trap->tr_eventtrap, 1);
++
++    BumpDevStat (dev, s_cproc_trap_types[CPROC_TrapType(status)]);
++
++    if (PackValue(trap->tr_qdesc.CQ_AckBuffers, 0) == PackTimeout || PackValue(trap->tr_qdesc.CQ_AckBuffers, 1) == PackTimeout)
++      BumpDevStat (dev, s_cproc_timeout);
++}
++
++void
++elan4_extract_dproc_trap (ELAN4_DEV *dev, E4_uint64 status, ELAN4_DPROC_TRAP *trap, unsigned unit)
++{
++    trap->tr_status = status;
++    
++    if (unit == 0)
++    {
++      trap->tr_desc.dma_typeSize   = read_reg64 (dev, Dma0Desc.dma_typeSize);
++      trap->tr_desc.dma_cookie     = read_reg64 (dev, Dma0Desc.dma_cookie);
++      trap->tr_desc.dma_vproc      = read_reg64 (dev, Dma0Desc.dma_vproc);
++      trap->tr_desc.dma_srcAddr    = read_reg64 (dev, Dma0Desc.dma_srcAddr);
++      trap->tr_desc.dma_dstAddr    = read_reg64 (dev, Dma0Desc.dma_dstAddr);
++      trap->tr_desc.dma_srcEvent   = read_reg64 (dev, Dma0Desc.dma_srcEvent);
++      trap->tr_desc.dma_dstEvent   = read_reg64 (dev, Dma0Desc.dma_dstEvent);
++      
++      elan4_sdram_copy_faultarea (dev, CUN_DProcPA0, &trap->tr_packAssemFault);
++    }
++    else
++    {
++      trap->tr_desc.dma_typeSize   = read_reg64 (dev, Dma1Desc.dma_typeSize);
++      trap->tr_desc.dma_cookie     = read_reg64 (dev, Dma1Desc.dma_cookie);
++      trap->tr_desc.dma_vproc      = read_reg64 (dev, Dma1Desc.dma_vproc);
++      trap->tr_desc.dma_srcAddr    = read_reg64 (dev, Dma1Desc.dma_srcAddr);
++      trap->tr_desc.dma_dstAddr    = read_reg64 (dev, Dma1Desc.dma_dstAddr);
++      trap->tr_desc.dma_srcEvent   = read_reg64 (dev, Dma1Desc.dma_srcEvent);
++      trap->tr_desc.dma_dstEvent   = read_reg64 (dev, Dma1Desc.dma_dstEvent);
++      
++      elan4_sdram_copy_faultarea (dev, CUN_DProcPA1, &trap->tr_packAssemFault);
++    }
++    
++    if (DPROC_PrefetcherFault (trap->tr_status))
++      elan4_sdram_copy_faultarea (dev, (CUN_DProcData0 | DPROC_FaultUnitNo(trap->tr_status)), &trap->tr_prefetchFault);
++
++    if (DPROC_PacketTimeout (trap->tr_status))
++      BumpDevStat (dev, s_dproc_timeout);
++
++    BumpDevStat (dev, s_dproc_trap_types[DPROC_TrapType(status)]);
++}    
++
++void
++elan4_extract_tproc_trap (ELAN4_DEV *dev, E4_uint64 status, ELAN4_TPROC_TRAP *trap)
++{
++    int i;
++
++    trap->tr_status = status;
++    trap->tr_state  = read_reg64 (dev, Thread_Trap_State);
++    trap->tr_pc     = read_reg64 (dev, PC_W);
++    trap->tr_npc    = read_reg64 (dev, nPC_W);
++    trap->tr_dirty  = read_reg64 (dev, DirtyBits);
++    trap->tr_bad    = read_reg64 (dev, BadBits);
++
++#ifdef CONFIG_MPSAS
++    if (sas_copyfrom_dev (dev->dev_osdep.pdev, ELAN4_BAR_REGISTERS, 
++                        ((dev->dev_devinfo.dev_revision_id == PCI_REVISION_ID_ELAN4_REVA) ? ELAN4_REVA_REG_OFFSET : ELAN4_REVB_REG_OFFSET) +
++                        offsetof (E4_Registers, Regs.TProcRegs), (unsigned long) &trap->tr_regs, 64*sizeof (E4_uint64)) < 0)
++    {
++      for (i = 0; i < 64; i++)
++          if (trap->tr_dirty & ((E4_uint64) 1 << i))
++              trap->tr_regs[i] = read_reg64 (dev, TProcRegs[i]);
++    }
++
++    for (i = 0; i < 64; i++)
++      if (! (trap->tr_dirty & ((E4_uint64) 1 << i)))
++          trap->tr_regs[i] = 0xdeadbabedeadbabeULL;
++#else
++    for (i = 0; i < 64; i++)
++    {
++      if (trap->tr_dirty & ((E4_uint64) 1 << i))
++          trap->tr_regs[i] = read_reg64 (dev, TProcRegs[i]);
++      else
++          trap->tr_regs[i] = 0xdeadbabedeadbabeULL;
++    }
++#endif
++    
++    if (trap->tr_state & TS_DataAccessException)
++      elan4_sdram_copy_faultarea (dev, CUN_TProcData0 | TS_DataPortNo (trap->tr_state), &trap->tr_dataFault);
++
++    if (trap->tr_state & TS_InstAccessException)
++      elan4_sdram_copy_faultarea (dev, CUN_TProcInst, &trap->tr_instFault);
++
++    for (i = 0; i < 7; i++)
++      if (trap->tr_state & (1 << i))
++          BumpDevStat (dev, s_tproc_trap_types[i]);
++}
++
++void
++elan4_extract_iproc_trap (ELAN4_DEV *dev, E4_uint64 status, ELAN4_IPROC_TRAP *trap, unsigned unit)
++{
++    sdramaddr_t hdroff  = dev->dev_inputtraparea + offsetof (E4_IprocTrapState, TrHeader[0][unit]);
++    sdramaddr_t dataoff = dev->dev_inputtraparea + offsetof (E4_IprocTrapState, TrData[0][unit]);
++    register int i, j;
++    int                 CurrUnitNo    = (unit >= 2) ? CUN_IProcHighPri : CUN_IProcLowPri;
++    sdramaddr_t CurrFaultArea = dev->dev_faultarea + (CurrUnitNo * sizeof (E4_FaultSave));
++
++    /* Finally copy the fault area */
++    elan4_sdram_copy_faultarea (dev, CurrUnitNo, &trap->tr_faultarea);
++
++    /*
++     * Clear out the fault save area after reading to allow a fault on the write of the back pointer of
++     * an InputQCommit to be obsurved if a simultaneous event proc trap occurs.
++     */
++    elan4_sdram_writeq (dev, CurrFaultArea + offsetof(E4_FaultSave, FSRAndFaultContext), 0x0ULL);
++    elan4_sdram_writeq (dev, CurrFaultArea + offsetof(E4_FaultSave, FaultAddress), 0x0ULL);
++
++    /* copy the transaction headers */
++    trap->tr_transactions[0].IProcStatusCntxAndTrType = status;
++    trap->tr_transactions[0].TrAddr                   = elan4_sdram_readq (dev, hdroff + offsetof (E4_IprocTrapHeader, TrAddr));
++    
++    for (i = 0; !IPROC_EOPTrap(trap->tr_transactions[i].IProcStatusCntxAndTrType);)
++    {
++      if (IPROC_BadLength (trap->tr_transactions[i].IProcStatusCntxAndTrType))
++          BumpDevStat (dev, s_bad_length);
++      else if (IPROC_TransCRCStatus (trap->tr_transactions[i].IProcStatusCntxAndTrType) == CRC_STATUS_BAD)
++          BumpDevStat (dev, s_crc_bad);
++      else if (IPROC_TransCRCStatus (trap->tr_transactions[i].IProcStatusCntxAndTrType) == CRC_STATUS_ERROR)
++          BumpDevStat (dev, s_crc_error);
++
++      BumpDevStat (dev, s_iproc_trap_types[IPROC_TrapValue (trap->tr_transactions[i].IProcStatusCntxAndTrType)]);
++
++      hdroff += NO_OF_INPUT_CHANNELS*sizeof (E4_IprocTrapHeader);
++
++      if (++i == MAX_TRAPPED_TRANS)
++          break;
++
++      elan4_sdram_copyq_from_sdram (dev, hdroff, &trap->tr_transactions[i], sizeof (E4_IprocTrapHeader));
++    }
++    
++    if (IPROC_EOPType (trap->tr_transactions[i].IProcStatusCntxAndTrType) == EOP_ERROR_RESET)
++      BumpDevStat (dev, s_eop_reset);
++
++    /* Remember the number of transactions we've copied */
++    trap->tr_numTransactions = i + 1;
++    
++    /* Copy all the data blocks in one go */
++    for (i = 0; i < MIN (trap->tr_numTransactions, MAX_TRAPPED_TRANS); i++, dataoff += NO_OF_INPUT_CHANNELS*sizeof (E4_IprocTrapData))
++    {
++      if (IPROC_BadLength(status) || IPROC_TransCRCStatus (status) != CRC_STATUS_GOOD)
++          elan4_sdram_copyq_from_sdram (dev, dataoff, trap->tr_dataBuffers[i].Data, TRANS_DATA_DWORDS*sizeof(E4_uint64));
++      else
++      {
++          int trtype  = IPROC_TransactionType(trap->tr_transactions[i].IProcStatusCntxAndTrType);
++          int ndwords = (trtype & TR_SIZE_MASK) >> TR_SIZE_SHIFT;
++
++          elan4_sdram_copyq_from_sdram (dev, dataoff, trap->tr_dataBuffers[i].Data, ndwords*sizeof(E4_uint64));
++
++          for (j = ndwords; j < TRANS_DATA_DWORDS; j++)
++              trap->tr_dataBuffers[i].Data[j] = 0xbeec0f212345678ull;
++      }
++    }
++    
++}
++
++void
++elan4_inspect_iproc_trap (ELAN4_IPROC_TRAP *trap)
++{
++    int i;
++
++    trap->tr_flags         = 0;
++    trap->tr_trappedTrans    = TR_TRANS_INVALID;
++    trap->tr_waitForEopTrans = TR_TRANS_INVALID;
++    trap->tr_identifyTrans   = TR_TRANS_INVALID;
++
++    if (trap->tr_numTransactions > MAX_TRAPPED_TRANS)
++      trap->tr_flags = TR_FLAG_TOOMANY_TRANS;
++
++    /*
++     * Now scan all the transactions received 
++     */
++    for (i = 0; i < MIN(trap->tr_numTransactions, MAX_TRAPPED_TRANS) ; i++)
++    {
++      E4_IprocTrapHeader *hdrp   = &trap->tr_transactions[i];
++      E4_uint64           status = hdrp->IProcStatusCntxAndTrType;
++
++      if (trap->tr_identifyTrans == TR_TRANS_INVALID)
++      {
++          switch (IPROC_TransactionType (status) & (TR_OPCODE_MASK | TR_SIZE_MASK))
++          {
++          case TR_IDENTIFY          & (TR_OPCODE_MASK | TR_SIZE_MASK):
++          case TR_REMOTEDMA         & (TR_OPCODE_MASK | TR_SIZE_MASK):
++          case TR_SETEVENT_IDENTIFY & (TR_OPCODE_MASK | TR_SIZE_MASK):
++          case TR_INPUT_Q_COMMIT    & (TR_OPCODE_MASK | TR_SIZE_MASK):
++          case TR_ADDWORD           & (TR_OPCODE_MASK | TR_SIZE_MASK):
++          case TR_TESTANDWRITE      & (TR_OPCODE_MASK | TR_SIZE_MASK):
++              trap->tr_identifyTrans = i;
++              break;
++          }
++      }
++
++      if (IPROC_TrapValue(status) == InputNoFault)            /* We're looking at transactions stored before the trap */
++          continue;                                           /* these should only be identifies */
++      
++      if (trap->tr_trappedTrans == TR_TRANS_INVALID)          /* Remember the transaction which caused the */
++          trap->tr_trappedTrans = i;                          /* trap */
++
++      if (IPROC_GoodAckSent (status) & (1 << IPROC_InputterChan (status)))
++          trap->tr_flags |= TR_FLAG_ACK_SENT;
++          
++      if (IPROC_EOPTrap(status))                              /* Check for EOP */
++      {
++          ASSERT (i == trap->tr_numTransactions - 1);
++
++          switch (IPROC_EOPType(status))
++          {
++          case EOP_GOOD:
++              /* if we get an EOP_GOOD then the outputer should have received a PAckOk. */  
++              /* unless it was a flood, in which case someone must have sent an ack */
++              /* but not necessarily us */
++              break;
++
++          case EOP_BADACK:
++              /* if we get an EOP_BADACK then the outputer did not receive a PAckOk even if
++               * we sent a PAckOk. WFlag this to ignore the AckSent. */
++              trap->tr_flags |= TR_FLAG_EOP_BAD;
++              break;
++
++          case EOP_ERROR_RESET:
++              /* if we get an EOP_ERROR_RESET then the outputer may or may not have got a PAckOk. */
++              trap->tr_flags |= TR_FLAG_EOP_ERROR;
++              break;
++
++          default:
++              printk ("elan4_inspect_iproc_trap: unknown eop type %d", IPROC_EOPType(status));
++              BUG();
++              /* NOTREACHED */
++          }
++          continue;
++      }
++      else
++      {
++          if (IPROC_BadLength(status) || (IPROC_TransCRCStatus (status) == CRC_STATUS_ERROR ||
++                                          IPROC_TransCRCStatus (status) == CRC_STATUS_BAD))
++          {
++              {
++                  register int j;
++                  if (IPROC_BadLength(status))
++                      PRINTF2 (DBG_DEVICE, DBG_INTR, "LinkError: Trapped on bad length data. status=%016llx Address=%016llx\n",
++                               status, hdrp->TrAddr);
++                  else
++                      PRINTF2 (DBG_DEVICE, DBG_INTR, "LinkError: Trapped with bad CRC. status=%016llx Address=%016llx\n",
++                               status, hdrp->TrAddr);
++                  for (j = 0; j < TRANS_DATA_DWORDS; j++)
++                      PRINTF2 (DBG_DEVICE, DBG_INTR, "LinkError: DataBuffers[%d] : %016llx\n", j, trap->tr_dataBuffers[i].Data[j]);
++              }
++
++              trap->tr_flags |= TR_FLAG_BAD_TRANS;
++              continue;
++          }
++          
++          if (IPROC_TransCRCStatus (status) == CRC_STATUS_DISCARD)
++              continue;
++
++          if ((((IPROC_TransactionType(status) & TR_BLOCK_OPCODE_MASK) == TR_WRITEBLOCK) ||
++               (IPROC_TransactionType(status) == TR_TRACEROUTE_TRANS)) &&
++              (trap->tr_flags & TR_FLAG_ACK_SENT) && trap->tr_identifyTrans == TR_TRANS_INVALID)
++          {
++              /* 
++               * Writeblock after the ack is sent without an identify transaction - this is 
++               * considered to be a DMA packet and requires the next packet to be nacked - since 
++               * the DMA processor will send this in a deterministic time and there's an upper 
++               * limit on the network latency (the output timeout) we just need to hold the context 
++               * filter up for a while.
++               */
++              trap->tr_flags |= TR_FLAG_DMA_PACKET;
++          }
++          
++          if (IPROC_LastTrans(status) && (IPROC_TransactionType(status) & TR_WAIT_FOR_EOP))
++          {
++              /*
++               * WaitForEop transactions - if we have to do network error fixup
++               * then we may need to execute/ignore this transaction dependant
++               * on whether the source will be resending it.
++               */
++              trap->tr_waitForEopTrans = i;
++          }
++
++          /*
++           * This is a special case caused by a minor input processor bug.
++           * If simultaneous InputMemoryFault and InputEventEngineTrapped occur then the chip will probably return
++           * InputEventEngineTrapped even though the write of the back pointer has not occured and must be done by
++           * the trap handler.
++           * In this case the fault address will equal q->q_bptr. If there has been only EventEngineTrap then the
++           * the fault address should be zero as the trap handler now always zeros this after every input trap.
++           */
++          if ((IPROC_TransactionType (status) & TR_OPCODE_MASK) == (TR_INPUT_Q_COMMIT & TR_OPCODE_MASK) &&
++              trap->tr_faultarea.FaultAddress == hdrp->TrAddr + offsetof(E4_InputQueue, q_bptr) &&
++              IPROC_TrapValue(status) == InputEventEngineTrapped)
++          {
++              hdrp->IProcStatusCntxAndTrType = (status & 0xFFFFFFF0FFFFFFFFull) | ((E4_uint64) InputMemoryFault << 32);
++          }
++      }
++
++      PRINTF (DBG_DEVICE, DBG_INTR, "inspect[%d] status=%llx TrapValue=%d -> flags %x\n", i, status, IPROC_TrapValue(status), trap->tr_flags);
++    }
++}
++
++E4_uint64
++elan4_trapped_open_command (ELAN4_DEV *dev, ELAN4_CQ *cq)
++{
++    sdramaddr_t cqdesc     = dev->dev_cqaddr + elan4_cq2num(cq) * sizeof (E4_CommandQueueDesc);
++    E4_uint64   cqcontrol  = elan4_sdram_readq (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_Control));
++    E4_uint32   extractOff = CQ_ExtractPtr (cqcontrol) & (CQ_Size(cq->cq_size)-1);
++    
++    if (extractOff == 0)
++      extractOff = CQ_Size(cq->cq_size) - sizeof (E4_uint64);
++    else
++      extractOff -= sizeof (E4_uint64);
++
++    return (elan4_sdram_readq (dev, cq->cq_space + extractOff));
++}
++
++EXPORT_SYMBOL(elan4_extract_eproc_trap);
++EXPORT_SYMBOL(elan4_display_eproc_trap);
++EXPORT_SYMBOL(elan4_extract_cproc_trap);
++EXPORT_SYMBOL(elan4_display_cproc_trap);
++EXPORT_SYMBOL(elan4_extract_dproc_trap);
++EXPORT_SYMBOL(elan4_display_dproc_trap);
++EXPORT_SYMBOL(elan4_extract_tproc_trap);
++EXPORT_SYMBOL(elan4_display_tproc_trap);
++EXPORT_SYMBOL(elan4_extract_iproc_trap);
++EXPORT_SYMBOL(elan4_inspect_iproc_trap);
++EXPORT_SYMBOL(elan4_display_iproc_trap);
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan4/user.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan4/user.c   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan4/user.c        2005-06-01 23:12:54.624435304 -0400
+@@ -0,0 +1,3352 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: user.c,v 1.68.2.9 2004/12/20 16:56:51 mike Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/user.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <qsnet/kpte.h>
++
++#include <elan/elanmod.h>
++#include <elan4/debug.h>
++#include <elan4/device.h>
++#include <elan4/user.h>
++
++#include <elan4/trtype.h>
++#include <elan4/commands.h>
++
++#include <stdarg.h>
++
++/* allow this code to compile against an Eagle elanmod */
++#ifdef __ELANMOD_DEVICE_H
++#define elan_attach_cap(cap,rnum,args,func)   elanmod_attach_cap(cap,args,func)
++#define elan_detach_cap(cap,rnum)             elanmod_detach_cap(cap)
++#endif
++
++#define NETERR_MSGS   16
++
++int user_p2p_route_options   = FIRST_TIMEOUT(3);
++int user_bcast_route_options = FIRST_TIMEOUT(3);
++int user_dproc_retry_count   = 15;
++int user_cproc_retry_count   = 2;
++
++int num_fault_save           = 30;
++int min_fault_pages          = 1;
++int max_fault_pages          = 128;
++
++static int
++user_validate_cap (USER_CTXT *uctx, ELAN_CAPABILITY *cap, unsigned use)
++{
++    /* Don't allow a user process to attach to system context */
++    if (ELAN4_SYSTEM_CONTEXT (cap->cap_lowcontext) || ELAN4_SYSTEM_CONTEXT (cap->cap_highcontext))
++    {
++      PRINTF3 (DBG_DEVICE, DBG_VP,"user_validate_cap: lctx %x hctx %x high %x\n", cap->cap_lowcontext, cap->cap_highcontext, ELAN4_KCOMM_BASE_CONTEXT_NUM);
++      PRINTF0 (DBG_DEVICE, DBG_VP,"user_validate_cap: user process cant attach to system cap\n");
++      return (EINVAL);
++    }
++    
++    return elanmod_classify_cap(&uctx->uctx_position, cap, use);
++}
++
++static __inline__ void
++__user_signal_trap (USER_CTXT *uctx)
++{
++    switch (uctx->uctx_trap_state)
++    {
++    case UCTX_TRAP_IDLE:
++      PRINTF (uctx, DBG_TRAP, "user_signal_trap: deliver signal %d to pid %d\n", uctx->uctx_trap_signo, uctx->uctx_trap_pid);
++
++      if (uctx->uctx_trap_signo)
++          kill_proc (uctx->uctx_trap_pid, uctx->uctx_trap_signo, 1);
++      break;
++
++    case UCTX_TRAP_SLEEPING:
++      PRINTF (uctx, DBG_TRAP, "user_signal_trap: wakeup sleeping trap handler\n");
++
++      kcondvar_wakeupone (&uctx->uctx_wait, &uctx->uctx_spinlock);
++      break;
++    }
++    uctx->uctx_trap_state = UCTX_TRAP_SIGNALLED;
++}
++
++static void
++user_signal_timer (unsigned long arg)
++{
++    USER_CTXT    *uctx = (USER_CTXT *) arg;
++    unsigned long flags;
++
++    PRINTF (uctx, DBG_TRAP, "user_signal_timer: state=%d pid=%d signal=%d (now %d start %d)\n",
++          uctx->uctx_trap_state, uctx->uctx_trap_pid, uctx->uctx_trap_signo, jiffies,
++          uctx->uctx_int_start);
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    __user_signal_trap (uctx);
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++}
++
++#define MAX_INTS_PER_TICK     50
++#define MIN_INTS_PER_TICK     20
++
++static void
++user_signal_trap (USER_CTXT *uctx)
++{
++    ASSERT (SPINLOCK_HELD (&uctx->uctx_spinlock));
++
++    PRINTF (uctx, DBG_TRAP, "user_signal_trap: state=%d pid=%d signal=%d%s\n", uctx->uctx_trap_state,
++          uctx->uctx_trap_pid, uctx->uctx_trap_signo, timer_pending(&uctx->uctx_int_timer) ? " (timer-pending)" : "");
++
++    uctx->uctx_int_count++;
++
++    if (timer_pending (&uctx->uctx_int_timer))
++      return;
++
++    if (uctx->uctx_int_count > ((int)(jiffies - uctx->uctx_int_start) * MAX_INTS_PER_TICK))
++    {
++      PRINTF (uctx, DBG_TRAP, "user_signal_trap: deferring signal for %d ticks (count %d ticks %d -> %d)\n", 
++              uctx->uctx_int_delay + 1, uctx->uctx_int_count, (int) (jiffies - uctx->uctx_int_start),
++              ((int)(jiffies - uctx->uctx_int_start) * MAX_INTS_PER_TICK));
++
++      /* We're interrupting too fast, so defer this signal */
++      uctx->uctx_int_timer.expires = jiffies + (++uctx->uctx_int_delay);
++
++      add_timer (&uctx->uctx_int_timer);
++    }
++    else
++    {
++      __user_signal_trap (uctx);
++
++      PRINTF (uctx, DBG_TRAP, "user_signal_trap: check signal for %d ticks (count %d ticks %d -> %d)\n", 
++              uctx->uctx_int_delay + 1, uctx->uctx_int_count, (int) (jiffies - uctx->uctx_int_start),
++              (int)(jiffies - uctx->uctx_int_start) * MIN_INTS_PER_TICK);
++          
++      if (uctx->uctx_int_count < ((int) (jiffies - uctx->uctx_int_start)) * MIN_INTS_PER_TICK)
++      {
++          PRINTF (uctx, DBG_TRAP, "user_signal_trap: reset interrupt throttle (count %d ticks %d)\n", 
++                  uctx->uctx_int_count, (int) (jiffies - uctx->uctx_int_start));
++
++          uctx->uctx_int_start = jiffies;
++          uctx->uctx_int_count = 0;
++          uctx->uctx_int_delay = 0;
++      }
++    }
++}
++
++static void
++user_neterr_timer (unsigned long arg)
++{
++    USER_CTXT *uctx = (USER_CTXT *) arg;
++    unsigned long flags;
++    
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++
++    uctx->uctx_status |= UCTX_NETERR_TIMER;
++    
++    user_signal_trap (uctx);
++
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++}
++
++static void
++user_flush_dma_runqueue (ELAN4_DEV *dev, USER_CTXT *uctx, int qfull)
++{
++    E4_uint64          qptrs = read_reg64 (dev, DProcLowPriPtrs);
++    E4_uint32          qsize = E4_QueueSize (E4_QueueSizeValue (qptrs));
++    E4_uint32          qfptr = E4_QueueFrontPointer (qptrs);
++    E4_uint32          qbptr = E4_QueueBackPointer (qptrs);
++    E4_DProcQueueEntry qentry;
++
++    while ((qfptr != qbptr) || qfull)
++    {
++      E4_uint64 typeSize = elan4_sdram_readq (dev, qfptr + offsetof (E4_DProcQueueEntry, Desc.dma_typeSize));
++
++      if (DMA_Context (typeSize) == uctx->uctx_ctxt.ctxt_num)
++      {
++          elan4_sdram_copyq_from_sdram (dev, qfptr, &qentry, sizeof (E4_DProcQueueEntry));
++
++          PRINTF4 (uctx, DBG_SWAP, "user_flush_dma_runqueue: %016llx %016llx %016llx %016llx\n", qentry.Desc.dma_typeSize, 
++                   qentry.Desc.dma_cookie, qentry.Desc.dma_vproc, qentry.Desc.dma_srcAddr);
++          PRINTF3 (uctx, DBG_SWAP, "                         %016llx %016llx %016llx\n", qentry.Desc.dma_dstAddr, 
++                   qentry.Desc.dma_srcEvent, qentry.Desc.dma_dstEvent);
++
++          if (RING_QUEUE_REALLY_FULL (uctx->uctx_dmaQ))
++              uctx->uctx_status |= UCTX_DPROC_QUEUE_OVERFLOW;
++          else
++          {
++              *RING_QUEUE_BACK (uctx->uctx_dmaQ, uctx->uctx_dmas) = qentry.Desc;
++              (void) RING_QUEUE_ADD (uctx->uctx_dmaQ);
++          }
++          
++          qentry.Desc.dma_typeSize = DMA_ShMemWrite | dev->dev_ctxt.ctxt_num;
++          qentry.Desc.dma_cookie   = 0;
++          qentry.Desc.dma_vproc    = 0;
++          qentry.Desc.dma_srcAddr  = 0;
++          qentry.Desc.dma_dstAddr  = 0;
++          qentry.Desc.dma_srcEvent = 0;
++          qentry.Desc.dma_dstEvent = 0;
++
++          elan4_sdram_copyq_to_sdram (dev, &qentry, qfptr, sizeof (E4_DProcQueueEntry));
++      }
++
++      qfptr = (qfptr & ~(qsize-1)) | ((qfptr + sizeof (E4_DProcQueueEntry)) & (qsize-1));
++      qfull = 0;
++    }
++}
++
++static void
++user_flush_thread_runqueue (ELAN4_DEV *dev, USER_CTXT *uctx, int qfull)
++{
++    E4_uint64          qptrs = read_reg64 (dev, TProcLowPriPtrs);
++    E4_uint32          qsize = E4_QueueSize (E4_QueueSizeValue (qptrs));
++    E4_uint32          qfptr = E4_QueueFrontPointer (qptrs);
++    E4_uint32          qbptr = E4_QueueBackPointer (qptrs);
++    E4_TProcQueueEntry qentry;
++
++    while ((qfptr != qbptr) || qfull)
++    {
++      E4_uint64 context = elan4_sdram_readq (dev, qfptr + offsetof (E4_TProcQueueEntry, Context));
++
++      if (TPROC_Context (context) == uctx->uctx_ctxt.ctxt_num)
++      {
++          elan4_sdram_copyq_from_sdram (dev, qfptr, &qentry, sizeof (E4_TProcQueueEntry));
++
++          PRINTF (uctx, DBG_SWAP, "user_flush_thread_runqueue: %016llx %016llx %016llx %016llx\n", qentry.Regs.Registers[0],
++                  qentry.Regs.Registers[1], qentry.Regs.Registers[2], qentry.Regs.Registers[3]);
++          PRINTF (uctx, DBG_SWAP, "                            %016llx %016llx %016llx\n", 
++                  qentry.Regs.Registers[4], qentry.Regs.Registers[5], qentry.Regs.Registers[6]);
++
++          if (RING_QUEUE_REALLY_FULL (uctx->uctx_threadQ))
++              uctx->uctx_status |= UCTX_TPROC_QUEUE_OVERFLOW;
++          else
++          {
++              *RING_QUEUE_BACK (uctx->uctx_threadQ, uctx->uctx_threads) = qentry.Regs;
++              (void) RING_QUEUE_ADD (uctx->uctx_threadQ);
++          }
++          
++          /* change the thread to execute the suspend sequence */
++          qentry.Regs.Registers[0] = dev->dev_tproc_suspend;
++          qentry.Regs.Registers[1] = dev->dev_tproc_space;
++          qentry.Context           = dev->dev_ctxt.ctxt_num;
++
++          elan4_sdram_copyq_to_sdram (dev, &qentry, qfptr, sizeof (E4_TProcQueueEntry));
++      }
++      
++      qfptr = (qfptr & ~(qsize-1)) | ((qfptr + sizeof (E4_TProcQueueEntry)) & (qsize-1));
++      qfull = 0;
++    }
++}
++
++static void
++user_flush_dmas (ELAN4_DEV *dev, void *arg, int qfull)
++{
++    USER_CTXT        *uctx = (USER_CTXT *) arg;
++    unsigned long     flags;
++    
++    ASSERT ((read_reg32 (dev, InterruptReg) & INT_DProcHalted) != 0);
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++
++    if ((uctx->uctx_status & (UCTX_SWAPPED_REASONS|UCTX_STOPPED_REASONS)) == 0)
++    {
++      PRINTF1 (uctx, DBG_SWAP, "user_flush_dmas: status %x - no more reasons\n", uctx->uctx_status);
++
++      uctx->uctx_status &= ~UCTX_STOPPING;
++
++      user_signal_trap (uctx);
++    }
++    else
++    {
++      user_flush_dma_runqueue (dev, uctx, qfull);
++
++      uctx->uctx_status = (uctx->uctx_status | UCTX_STOPPED) & ~UCTX_STOPPING;
++    
++      PRINTF1 (uctx, DBG_SWAP, "user_flush_dmas: statux %x - stopped\n", uctx->uctx_status);
++
++      kcondvar_wakeupall (&uctx->uctx_wait, &uctx->uctx_spinlock);
++    }
++
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++}
++
++static void
++user_flush (ELAN4_DEV *dev, void *arg)
++{
++    USER_CTXT        *uctx = (USER_CTXT *) arg;
++    struct list_head *entry;
++    unsigned long     flags;
++
++    ASSERT ((read_reg32 (dev, InterruptReg) & (INT_Halted|INT_Discarding)) == (INT_Halted|INT_Discarding));
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++
++    if ((uctx->uctx_status & (UCTX_SWAPPED_REASONS|UCTX_STOPPED_REASONS)) == 0)
++    {
++      PRINTF1 (uctx, DBG_SWAP, "user_flush: status %x - no more reasons\n", uctx->uctx_status);
++
++      uctx->uctx_status &= ~UCTX_STOPPING;
++
++      user_signal_trap (uctx);
++    }
++    else
++    {
++      PRINTF1 (uctx, DBG_SWAP, "user_flush: status %x - flushing context\n", uctx->uctx_status);
++
++      list_for_each (entry, &uctx->uctx_cqlist) {
++          USER_CQ *ucq = list_entry (entry, USER_CQ, ucq_link);
++
++          if (ucq->ucq_state == UCQ_RUNNING)
++          {
++              /* NOTE: since the inserter can still be running we modify the permissions
++               *       to zero then when the extractor starts up again it will trap */
++              PRINTF1 (uctx, DBG_SWAP, "user_flush: stopping cq indx=%d\n", elan4_cq2idx(ucq->ucq_cq));
++
++              elan4_updatecq (dev, ucq->ucq_cq, 0, 0);
++          }
++      }
++      
++      user_flush_thread_runqueue (dev, uctx, TPROC_LowRunQueueFull(read_reg64 (dev, TProcStatus)));
++
++      /* since we can't determine whether the dma run queue is full or empty, we use a dma
++       * halt operation to do the flushing - as the reason for halting the dma processor 
++       * will be released when we return, we keep it halted until the flush has completed */
++      elan4_queue_dma_flushop (dev, &uctx->uctx_dma_flushop, 0);
++
++      if (uctx->uctx_status & UCTX_EXITING)
++          elan4_flush_icache_halted (&uctx->uctx_ctxt);
++    }
++
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++}
++
++static void
++user_set_filter (USER_CTXT *uctx, E4_uint32 state)
++{
++    struct list_head *entry;
++
++    ASSERT (SPINLOCK_HELD (&uctx->uctx_spinlock));
++
++    list_for_each (entry, &uctx->uctx_cent_list) {
++      USER_CTXT_ENTRY *cent = list_entry (entry, USER_CTXT_ENTRY, cent_link);
++
++      elan4_set_filter (&uctx->uctx_ctxt, cent->cent_cap->cap_mycontext, state);
++    }
++}
++
++static void
++user_start_nacking (USER_CTXT *uctx, unsigned reason)
++{
++    PRINTF2 (uctx, DBG_SWAP, "user_start_nacking: status %x reason %x\n", uctx->uctx_status, reason);
++
++    ASSERT (SPINLOCK_HELD (&uctx->uctx_spinlock));
++
++    if (UCTX_NACKING(uctx))
++      uctx->uctx_status |= reason;
++    else
++    {
++      uctx->uctx_status |= reason;
++
++      user_set_filter (uctx, E4_FILTER_STATS | E4_FILTER_DISCARD_ALL);
++    }
++}
++
++static void
++user_stop_nacking (USER_CTXT *uctx, unsigned reason)
++{
++    PRINTF2 (uctx, DBG_SWAP, "user_stop_nacking: status %x reason %x\n", uctx->uctx_status, reason);
++    
++    ASSERT (SPINLOCK_HELD (&uctx->uctx_spinlock));
++    
++    uctx->uctx_status &= ~reason;
++    
++    if (! UCTX_NACKING (uctx))
++      user_set_filter (uctx, E4_FILTER_STATS);
++}
++
++static void
++user_start_stopping (USER_CTXT *uctx, unsigned reason)
++{
++    ELAN4_DEV *dev =uctx->uctx_ctxt.ctxt_dev;
++
++    PRINTF2 (uctx, DBG_SWAP, "user_start_stopping: status %x reason %x\n", uctx->uctx_status, reason);
++
++    ASSERT (! (uctx->uctx_status & UCTX_STOPPED));
++
++    user_start_nacking (uctx, reason);
++    
++    if ((uctx->uctx_status & UCTX_STOPPING) != 0)
++      return;
++    
++    uctx->uctx_status |= UCTX_STOPPING;
++
++    /* queue the halt operation to  remove all threads/dmas/cqs from the run queues */
++    /*    and also flush through the context filter change */
++    elan4_queue_haltop (dev, &uctx->uctx_haltop);
++}
++
++static void
++user_stop_stopping (USER_CTXT *uctx, unsigned reason)
++{
++    PRINTF2 (uctx, DBG_SWAP, "user_stop_stopping: status %x reason %x\n", uctx->uctx_status, reason);
++    
++    user_stop_nacking (uctx, reason);
++
++    if (UCTX_RUNNABLE (uctx))
++    {
++      uctx->uctx_status &= ~UCTX_STOPPED;
++
++      PRINTF1 (uctx, DBG_SWAP, "user_stop_stopping: no more reasons => %x\n", uctx->uctx_status);
++
++      user_signal_trap (uctx);
++    }
++}
++
++void
++user_swapout (USER_CTXT *uctx, unsigned reason)
++{
++    ELAN4_DEV *dev = uctx->uctx_ctxt.ctxt_dev;
++    unsigned long flags;
++    
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    
++    PRINTF2 (uctx, DBG_SWAP, "user_swapout: status %x reason %x\n", uctx->uctx_status, reason);
++    
++    user_start_nacking (uctx, reason);
++    
++    while (uctx->uctx_status & (UCTX_SWAPPING|UCTX_STOPPING) &&               /* wait for someone else to finish */
++         uctx->uctx_trap_count > 0)                                           /* and for trap handlers to notice */
++    {                                                                         /* and exit */
++      PRINTF1 (uctx, DBG_SWAP, "user_swapout: waiting for %d trap handlers to exit/previous swapout\n", uctx->uctx_trap_count);
++
++      kcondvar_wakeupall (&uctx->uctx_wait, &uctx->uctx_spinlock);
++      kcondvar_wait (&uctx->uctx_wait, &uctx->uctx_spinlock, &flags);
++    }
++
++    if (uctx->uctx_status & UCTX_SWAPPED)                                     /* already swapped out */
++    {
++      spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++      return;
++    }
++    
++    uctx->uctx_status |= (UCTX_SWAPPING|UCTX_STOPPING);                               /* mark the context as swapping & stopping */
++    
++    /* queue the halt operation to  remove all threads/dmas/cqs from the run queues */
++    /*    and also flush through the context filter change */
++    elan4_queue_haltop (dev, &uctx->uctx_haltop);
++    
++    while (! (uctx->uctx_status & UCTX_STOPPED))
++      kcondvar_wait (&uctx->uctx_wait, &uctx->uctx_spinlock, &flags);
++
++    /* all state has been removed from the elan - we can now "tidy" it up */
++
++    PRINTF0 (uctx, DBG_SWAP, "user_swapout: swapped out\n");
++    
++    uctx->uctx_status = (uctx->uctx_status & ~UCTX_SWAPPING) | UCTX_SWAPPED;
++    
++    kcondvar_wakeupall (&uctx->uctx_wait, &uctx->uctx_spinlock);
++
++    PRINTF1 (uctx, DBG_SWAP, "user_swapout: all done - status %x\n", uctx->uctx_status);
++
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++}
++
++void
++user_swapin (USER_CTXT *uctx, unsigned reason)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++
++    ASSERT (uctx->uctx_status & UCTX_SWAPPED_REASONS);
++
++    PRINTF2 (uctx, DBG_SWAP, "user_swapin: status %x reason %x\n", uctx->uctx_status, reason);
++
++    while (uctx->uctx_status & (UCTX_SWAPPING|UCTX_STOPPING))                 /* wait until other threads have */
++      kcondvar_wait (&uctx->uctx_wait, &uctx->uctx_spinlock, &flags);         /* completed their swap operation */
++
++    ASSERT (uctx->uctx_status & (UCTX_SWAPPED | UCTX_STOPPED));
++
++    user_stop_nacking (uctx, reason);
++
++    if (! (uctx->uctx_status & UCTX_SWAPPED_REASONS))
++    {
++      uctx->uctx_status &= ~UCTX_SWAPPED;
++
++      /* no longer swapped out - wakeup anyone sleeping waiting for swapin */
++      kcondvar_wakeupall (&uctx->uctx_wait, &uctx->uctx_spinlock);
++
++      if (! (uctx->uctx_status & UCTX_STOPPED_REASONS))
++      {
++          uctx->uctx_status &= ~UCTX_STOPPED;
++          user_signal_trap (uctx);
++      }
++    }
++
++    PRINTF1 (uctx, DBG_SWAP, "user_swapin: all done - status %x\n", uctx->uctx_status);
++
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++}
++
++void
++user_destroy_callback (void *arg, ELAN_CAPABILITY *cap, ELAN_CAPABILITY *map)
++{
++    USER_CTXT *uctx = (USER_CTXT *) arg;
++
++    PRINTF (uctx, DBG_VP, "user_destroy_callback: %s\n", map == NULL ? "cap destoyed" : "map destroyed");
++}
++
++int
++user_attach (USER_CTXT *uctx, ELAN_CAPABILITY *cap)
++{
++    ELAN4_DEV       *dev = uctx->uctx_ctxt.ctxt_dev;
++    USER_CTXT_ENTRY *cent;
++    unsigned long flags;
++    int ctype, res;
++    
++    if ((ctype = user_validate_cap (uctx, cap, ELAN_USER_ATTACH)) < 0)
++      return ctype;
++
++    if ((ctype == ELAN_CAP_RMS) && (res = elan_attach_cap (cap, dev->dev_devinfo.dev_rail, uctx, user_destroy_callback)) != 0)
++    {
++      /* NOTE: elan_attach_cap returns +ve errnos */
++      return -res;
++    }
++
++    KMEM_ALLOC (cent, USER_CTXT_ENTRY *, sizeof (USER_CTXT_ENTRY), 1);
++    if (cent == NULL)
++    {
++      if (ctype == ELAN_CAP_RMS)
++          elan_detach_cap (cap, dev->dev_devinfo.dev_rail);
++
++      return -ENOMEM;
++    }
++
++    KMEM_ALLOC (cent->cent_cap, ELAN_CAPABILITY *, ELAN_CAP_SIZE(cap), 1);
++    if (cent->cent_cap == NULL)
++    {
++      if (ctype == ELAN_CAP_RMS)
++          elan_detach_cap (cap, dev->dev_devinfo.dev_rail);
++
++      KMEM_FREE (cent, sizeof (USER_CTXT_ENTRY));
++      return -ENOMEM;
++    }
++
++    bcopy (cap, cent->cent_cap, ELAN_CAP_SIZE(cap));
++
++    if ((res = elan4_attach_filter (&uctx->uctx_ctxt, cap->cap_mycontext)) != 0)
++    {
++      if (ctype == ELAN_CAP_RMS)
++          elan_detach_cap (cap, dev->dev_devinfo.dev_rail);
++      
++      KMEM_FREE (cent->cent_cap, ELAN_CAP_SIZE (cap));
++      KMEM_FREE (cent, sizeof (USER_CTXT_ENTRY));
++
++      return res;
++    }
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++
++    list_add_tail (&cent->cent_link, &uctx->uctx_cent_list);
++
++    if (! UCTX_NACKING (uctx))
++      user_set_filter (uctx, E4_FILTER_STATS);
++
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++
++    return (0);
++    
++}
++
++void
++user_detach (USER_CTXT *uctx, ELAN_CAPABILITY *cap)
++{
++    ELAN4_DEV         *dev = uctx->uctx_ctxt.ctxt_dev;
++    struct list_head  *entry;
++    struct list_head  *next;
++    struct list_head   list;
++    unsigned long      flags;
++
++    INIT_LIST_HEAD (&list);
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    
++    PRINTF (uctx, DBG_NETWORK_CTX, cap ? "user_detach: network context %d\n" : "user_detach: all network contexts\n", cap ? cap->cap_mycontext : 0);
++
++    list_for_each_safe (entry, next, &uctx->uctx_cent_list) {
++      USER_CTXT_ENTRY *cent = list_entry (entry, USER_CTXT_ENTRY, cent_link);
++
++      if (cap == NULL || ELAN_CAP_MATCH (cap, cent->cent_cap))
++      {
++          PRINTF1 (uctx, DBG_NETWORK_CTX, "user_detach: detach from network context %d\n", cent->cent_cap->cap_mycontext);
++          
++          elan4_detach_filter (&uctx->uctx_ctxt, cent->cent_cap->cap_mycontext);
++
++          list_del (&cent->cent_link);
++          list_add_tail (&cent->cent_link, &list);
++      }
++    }
++
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++
++    while (! list_empty (&list))
++    {
++      USER_CTXT_ENTRY *cent = list_entry (list.next, USER_CTXT_ENTRY, cent_link);
++
++      list_del (&cent->cent_link);
++
++      if (user_validate_cap (uctx, cent->cent_cap, ELAN_USER_DETACH) == ELAN_CAP_RMS)
++          elan_detach_cap (cent->cent_cap, dev->dev_devinfo.dev_rail); 
++      
++      KMEM_FREE (cent->cent_cap, ELAN_CAP_SIZE (cent->cent_cap));
++      KMEM_FREE (cent, sizeof (USER_CTXT_ENTRY));
++    }
++}
++
++void
++user_block_inputter (USER_CTXT *uctx, unsigned blocked)
++{
++    unsigned long flags;
++    int isblocked;
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    
++    isblocked = (uctx->uctx_status & UCTX_USER_FILTERING);
++
++    if (blocked && !isblocked)
++      user_start_nacking (uctx, UCTX_USER_FILTERING);
++
++    if (!blocked && isblocked)
++      user_stop_nacking (uctx, UCTX_USER_FILTERING);
++
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++}
++
++static USER_VPSEG *
++user_install_vpseg (USER_CTXT *uctx, unsigned process, unsigned entries)
++{
++    struct list_head *entry;
++    USER_VPSEG       *seg;
++
++    ASSERT (kmutex_is_locked (&uctx->uctx_vpseg_lock));
++
++    list_for_each (entry, &uctx->uctx_vpseg_list) {
++      seg = list_entry (entry, USER_VPSEG, vps_link);
++
++      if (process <= (seg->vps_process + seg->vps_entries-1) && 
++          (process + entries - 1) >= seg->vps_process)
++          return ((USER_VPSEG *) NULL);
++    }
++
++    KMEM_ZALLOC (seg, USER_VPSEG *, sizeof (USER_VPSEG), 1);
++    
++    if (seg == (USER_VPSEG *) NULL)
++      return ((USER_VPSEG *) NULL);
++
++    seg->vps_process = process;
++    seg->vps_entries = entries;
++
++    list_add_tail (&seg->vps_link, &uctx->uctx_vpseg_list);
++
++    return (seg);
++}
++
++static void
++user_remove_vpseg (USER_CTXT *uctx, USER_VPSEG *seg)
++{
++    ASSERT (kmutex_is_locked (&uctx->uctx_vpseg_lock));
++
++    list_del (&seg->vps_link);
++    
++    switch (seg->vps_type)
++    {
++    case USER_VPSEG_P2P:
++      /* These pointers (union) are only valid for P2P segs */
++      if (seg->vps_p2p_routes)
++          KMEM_FREE (seg->vps_p2p_routes, sizeof (E4_VirtualProcessEntry) * seg->vps_entries);
++      
++      if (seg->vps_p2p_cap)
++          KMEM_FREE (seg->vps_p2p_cap, ELAN_CAP_SIZE(seg->vps_p2p_cap));
++
++      break;
++      
++    case USER_VPSEG_BCAST:
++      ;
++    }
++
++    KMEM_FREE (seg, sizeof (USER_VPSEG));
++}
++
++static USER_VPSEG *
++user_find_vpseg (USER_CTXT *uctx, unsigned low, unsigned high)
++{
++    struct list_head *entry;
++
++    ASSERT (kmutex_is_locked (&uctx->uctx_vpseg_lock));
++
++    list_for_each (entry, &uctx->uctx_vpseg_list) {
++      USER_VPSEG *seg = list_entry (entry, USER_VPSEG, vps_link);
++
++      if (seg->vps_process <= low && (seg->vps_process + seg->vps_entries) > high)
++          return (seg);
++    }
++
++    return ((USER_VPSEG *) NULL);
++}
++
++static ELAN_LOCATION 
++user_process2location (USER_CTXT *uctx, USER_VPSEG *seg, unsigned process)
++{
++    ELAN_LOCATION location;
++    int           nnodes, nctxs;
++    int           nodeOff, ctxOff, vpOff;
++
++    location.loc_node    = ELAN_INVALID_NODE;
++    location.loc_context = -1;
++
++    if (seg == NULL)
++      seg = user_find_vpseg (uctx, process, process);
++
++    if (seg == NULL || (seg->vps_type != USER_VPSEG_P2P))
++      return (location);
++
++    nnodes = ELAN_CAP_NUM_NODES (seg->vps_p2p_cap);
++    nctxs  = ELAN_CAP_NUM_CONTEXTS (seg->vps_p2p_cap);
++
++    switch (seg->vps_p2p_cap->cap_type & ELAN_CAP_TYPE_MASK)
++    {
++    case ELAN_CAP_TYPE_BLOCK:
++      for (nodeOff = 0, vpOff = 0; nodeOff < nnodes; nodeOff++)
++      {
++          for (ctxOff = 0; ctxOff < nctxs; ctxOff++)
++          {
++              if ((seg->vps_p2p_cap->cap_type & ELAN_CAP_TYPE_NO_BITMAP) || BT_TEST (seg->vps_p2p_cap->cap_bitmap, ctxOff + (nodeOff * nctxs)))
++              {
++                  if (vpOff++ == (process - seg->vps_process))
++                  { 
++                      location.loc_node    = seg->vps_p2p_cap->cap_lownode + nodeOff;
++                      location.loc_context = seg->vps_p2p_cap->cap_lowcontext + ctxOff;
++                      goto found;
++                  }
++              }
++          }
++      }
++      break;
++      
++    case ELAN_CAP_TYPE_CYCLIC:
++      for (ctxOff = 0, vpOff = 0; ctxOff < nctxs; ctxOff++)
++      {
++          for (nodeOff = 0; nodeOff < nnodes; nodeOff++)
++          {
++              if ((seg->vps_p2p_cap->cap_type & ELAN_CAP_TYPE_NO_BITMAP) || BT_TEST (seg->vps_p2p_cap->cap_bitmap, nodeOff + (ctxOff * nnodes)))
++              {                                   
++                  if (vpOff++ ==  (process - seg->vps_process))
++                  { 
++                      location.loc_node    = seg->vps_p2p_cap->cap_lownode + nodeOff;
++                      location.loc_context = seg->vps_p2p_cap->cap_lowcontext + ctxOff;
++                      goto found;
++                  }
++              }
++          }
++      }
++      break;  
++    }
++       
++ found:
++    return (location);
++}
++
++static unsigned 
++user_location2process (USER_CTXT *uctx, ELAN_LOCATION location)
++{
++    unsigned int      process = ELAN_INVALID_PROCESS;
++    struct list_head *entry;
++    int               nnodes, nctxs;
++    int               nodeOff, ctxOff, vpOff;
++
++    kmutex_lock (&uctx->uctx_vpseg_lock);
++    list_for_each (entry, &uctx->uctx_vpseg_list) {
++      USER_VPSEG *seg = list_entry (entry, USER_VPSEG, vps_link);
++
++      if (seg->vps_type != USER_VPSEG_P2P)
++          continue;
++
++      if (location.loc_node >= seg->vps_p2p_cap->cap_lownode && location.loc_node <= seg->vps_p2p_cap->cap_highnode &&
++          location.loc_context >= seg->vps_p2p_cap->cap_lowcontext && location.loc_context <= seg->vps_p2p_cap->cap_highcontext)
++      {
++          nnodes = ELAN_CAP_NUM_NODES (seg->vps_p2p_cap);
++          nctxs  = ELAN_CAP_NUM_CONTEXTS (seg->vps_p2p_cap);
++
++          switch (seg->vps_p2p_cap->cap_type & ELAN_CAP_TYPE_MASK)
++          {
++          case ELAN_CAP_TYPE_BLOCK:
++              for (nodeOff = 0, vpOff = 0; nodeOff < nnodes; nodeOff++)
++              {
++                  for (ctxOff = 0; ctxOff < nctxs; ctxOff++)
++                  {
++                      if ((seg->vps_p2p_cap->cap_type & ELAN_CAP_TYPE_NO_BITMAP) || BT_TEST (seg->vps_p2p_cap->cap_bitmap, ctxOff + (nodeOff * nctxs)))
++                      {
++                          if (location.loc_node == seg->vps_p2p_cap->cap_lownode + nodeOff &&
++                              location.loc_context == seg->vps_p2p_cap->cap_lowcontext + ctxOff)
++                          {
++                              process = seg->vps_process + vpOff;
++                              goto found;
++                          }
++                          vpOff++;
++                      }
++                  }
++              }
++              break;
++      
++          case ELAN_CAP_TYPE_CYCLIC:
++              for (ctxOff = 0, vpOff = 0; ctxOff < nctxs; ctxOff++)
++              {
++                  for (nodeOff = 0; nodeOff < nnodes; nodeOff++)
++                  {
++                      if ((seg->vps_p2p_cap->cap_type & ELAN_CAP_TYPE_NO_BITMAP) || BT_TEST (seg->vps_p2p_cap->cap_bitmap, nodeOff + (ctxOff * nnodes)))
++                      {
++                          if (location.loc_node == seg->vps_p2p_cap->cap_lownode + nodeOff &&
++                              location.loc_context == seg->vps_p2p_cap->cap_lowcontext + ctxOff)
++                          {
++                              process = seg->vps_process + vpOff;
++                              goto found;
++                          }
++                          vpOff++;
++                      }
++                  }
++              }
++              break;
++          }
++      }
++    }
++ found:
++    kmutex_unlock (&uctx->uctx_vpseg_lock);
++
++    return (process);
++}
++
++static void
++user_loadroute_vpseg (USER_CTXT *uctx, USER_VPSEG *seg, ELAN_POSITION *pos)
++{
++    ELAN4_DEV             *dev    = uctx->uctx_ctxt.ctxt_dev;
++    ELAN_CAPABILITY       *cap    = seg->vps_p2p_cap;
++    unsigned               nnodes = ELAN_CAP_NUM_NODES (cap);
++    unsigned               nctxs  = ELAN_CAP_NUM_CONTEXTS (cap);
++    E4_VirtualProcessEntry route;
++    unsigned             nodeOff;
++    unsigned             ctxOff;
++    unsigned             vpOff;
++
++    switch (cap->cap_type & ELAN_CAP_TYPE_MASK)
++    {
++    case ELAN_CAP_TYPE_BLOCK:
++      for (nodeOff = 0, vpOff = 0; nodeOff < nnodes; nodeOff++)
++      {
++          for (ctxOff = 0; ctxOff < nctxs; ctxOff++)
++          {
++              if ((cap->cap_type & ELAN_CAP_TYPE_NO_BITMAP) || BT_TEST (cap->cap_bitmap, ctxOff + (nodeOff * nctxs)))
++              {
++                  if (seg->vps_p2p_routes != NULL)
++                      route = seg->vps_p2p_routes[vpOff];
++                  else if (elan4_generate_route (&uctx->uctx_position, &route, cap->cap_lowcontext + ctxOff,
++                                                 cap->cap_lownode + nodeOff, cap->cap_lownode + nodeOff, user_p2p_route_options) < 0)
++                  {
++                      vpOff++;
++                      continue;
++                  }
++
++                  PRINTF5 (uctx, DBG_VP, "user_loadroute_vpseg: virtual process %d -> node %d context %d [%016llx.%016llx]\n",
++                           seg->vps_process + vpOff, cap->cap_lownode + nodeOff, cap->cap_lowcontext + ctxOff,
++                           route.Values[0], route.Values[1]);
++                  
++                  elan4_write_route (dev, uctx->uctx_routetable, seg->vps_process + vpOff, &route);
++                                            
++                  vpOff++;
++              }
++          }
++      }
++      break;
++
++    case ELAN_CAP_TYPE_CYCLIC:
++      for (ctxOff = 0, vpOff = 0; ctxOff < nctxs; ctxOff++)
++      {
++          for (nodeOff = 0; nodeOff < nnodes; nodeOff++)
++          {
++              if ((cap->cap_type & ELAN_CAP_TYPE_NO_BITMAP) || BT_TEST (cap->cap_bitmap, nodeOff + (ctxOff * nnodes)))
++              {
++                  if (seg->vps_p2p_routes != NULL)
++                      route = seg->vps_p2p_routes[vpOff];
++                  else if (elan4_generate_route (&uctx->uctx_position, &route, cap->cap_lowcontext + ctxOff,
++                                                 cap->cap_lownode + nodeOff, cap->cap_lownode + nodeOff, user_p2p_route_options) < 0)
++                  {
++                      vpOff++;
++                      continue;
++                  }
++
++                  PRINTF5 (uctx, DBG_VP, "user_loadroute_vpseg: virtual process %d -> node %d context %d [%016llx.%016llx]\n",
++                           seg->vps_process + vpOff, cap->cap_lownode + nodeOff, cap->cap_lowcontext + ctxOff,
++                           route.Values[0], route.Values[1]);
++                  
++                  elan4_write_route (dev, uctx->uctx_routetable, seg->vps_process + vpOff, &route);
++                                            
++                  vpOff++;
++              }
++          }
++      }
++      break;
++    }
++}
++
++static int
++user_loadroute_bcast (USER_CTXT *uctx, USER_VPSEG *seg)
++{
++    ELAN4_DEV             *dev = uctx->uctx_ctxt.ctxt_dev;
++    ELAN_POSITION         *pos = &uctx->uctx_position;
++    E4_VirtualProcessEntry route;
++    USER_VPSEG            *aseg;
++    int                    res;
++    ELAN_LOCATION          low;
++    ELAN_LOCATION          high;
++
++    if ((aseg = user_find_vpseg (uctx, seg->vps_bcast_lowvp, seg->vps_bcast_highvp)) == NULL || aseg->vps_type != USER_VPSEG_P2P)
++      return (-EINVAL);
++    
++#ifdef use_elanmod
++    if ((res = user_validate_cap (dev, aseg->vps_p2p_cap, ELAN_USER_BROADCAST)) < 0)
++      return (res);
++#endif
++    
++    low  = user_process2location (uctx, aseg, seg->vps_bcast_lowvp);
++    high = user_process2location (uctx, aseg, seg->vps_bcast_highvp);
++
++    if (low.loc_context != high.loc_context)
++      return (-EINVAL);
++
++    /* NOTE: if loopback can only broadcast to ourself - 
++     *       if back-to-back can only broadcast to other node */
++    if ((pos->pos_mode == ELAN_POS_MODE_LOOPBACK   && low.loc_node != high.loc_node && low.loc_node != pos->pos_nodeid) ||
++      (pos->pos_mode == ELAN_POS_MODE_BACKTOBACK && low.loc_node != high.loc_node && low.loc_node == pos->pos_nodeid))
++    {
++      return (-EINVAL);
++    }
++    
++    if ((res = elan4_generate_route (pos, &route, low.loc_context, low.loc_node, high.loc_node, user_bcast_route_options)) < 0)
++      return (res);
++
++    PRINTF (uctx, DBG_VP, "user_loadroute_bcast: virtual process %d -> nodes %d.%d context %d [%016llx.%016llx]\n",
++          seg->vps_process, low.loc_node, high.loc_node, low.loc_context, route.Values[0], route.Values[1]);
++    
++    elan4_write_route (dev, uctx->uctx_routetable, seg->vps_process, &route);
++    return (0);
++}
++
++int
++user_add_p2pvp (USER_CTXT *uctx, unsigned process, ELAN_CAPABILITY *cap)
++{
++    USER_VPSEG      *seg;
++    ELAN_CAPABILITY *ncap;
++    unsigned         entries;
++
++    if ((cap->cap_type & ELAN_CAP_TYPE_NO_BITMAP) == 0)
++      entries = bt_nbits (cap->cap_bitmap , ELAN_CAP_BITMAPSIZE(cap));
++    else
++      entries = ELAN_CAP_BITMAPSIZE(cap);
++    
++    if ((process + entries) > (E4_VPT_MIN_ENTRIES << uctx->uctx_routetable->tbl_size))
++      return (-EINVAL);
++
++    KMEM_ALLOC (ncap, ELAN_CAPABILITY *, ELAN_CAP_SIZE (cap), 1);
++
++    if (ncap == NULL)
++      return (-ENOMEM);
++    
++    bcopy (cap, ncap, ELAN_CAP_SIZE (cap));
++
++    kmutex_lock (&uctx->uctx_vpseg_lock);
++
++    if ((seg = user_install_vpseg (uctx, process, entries)) == NULL)
++    {
++      kmutex_unlock (&uctx->uctx_vpseg_lock);
++      return (-EINVAL);
++    }
++    
++    seg->vps_type       = USER_VPSEG_P2P;
++    seg->vps_p2p_cap    = ncap;
++    seg->vps_p2p_routes = NULL;
++
++    user_loadroute_vpseg (uctx, seg, &uctx->uctx_position);
++    
++    kmutex_unlock (&uctx->uctx_vpseg_lock);
++
++    return (0);
++}
++
++int
++user_add_bcastvp (USER_CTXT *uctx, unsigned process, unsigned lowvp, unsigned highvp)
++{
++    USER_VPSEG *seg;
++    int         res;
++
++    if (lowvp > highvp || process >= (E4_VPT_MIN_ENTRIES << uctx->uctx_routetable->tbl_size))
++      return (-EINVAL);
++
++    kmutex_lock (&uctx->uctx_vpseg_lock);
++
++    if ((seg = user_install_vpseg (uctx, process, 1)) == NULL)
++    {
++      kmutex_unlock (&uctx->uctx_vpseg_lock);
++      return (-EINVAL);
++    }
++
++    seg->vps_type         = USER_VPSEG_BCAST;
++    seg->vps_bcast_lowvp  = lowvp;
++    seg->vps_bcast_highvp = highvp;
++
++    if ((res = user_loadroute_bcast (uctx, seg)) < 0)
++      user_remove_vpseg (uctx, seg);
++
++    kmutex_unlock (&uctx->uctx_vpseg_lock);
++    return (res);
++}
++
++int
++user_removevp (USER_CTXT *uctx, unsigned process)
++{
++    USER_VPSEG *seg;
++
++    kmutex_lock (&uctx->uctx_vpseg_lock);
++    
++    if (process == ELAN_INVALID_PROCESS)
++      seg = list_entry (uctx->uctx_vpseg_list.next, USER_VPSEG, vps_link);
++    else
++      seg = user_find_vpseg (uctx, process, process);
++
++    if (seg == NULL)
++    {
++      kmutex_unlock (&uctx->uctx_vpseg_lock);
++      return (-EINVAL);
++    }
++
++    do {
++      ELAN4_DEV    *dev = uctx->uctx_ctxt.ctxt_dev;
++      int i;
++
++      for (i = 0; i < seg->vps_entries; i++)
++          elan4_invalidate_route (dev, uctx->uctx_routetable, seg->vps_process + i);
++
++      user_remove_vpseg (uctx, seg);
++
++    } while (process == ELAN_INVALID_PROCESS && (seg = list_entry (uctx->uctx_vpseg_list.next, USER_VPSEG, vps_link)) != NULL);
++
++    kmutex_unlock (&uctx->uctx_vpseg_lock);
++
++    return (0);
++}
++
++int
++user_set_route (USER_CTXT *uctx, unsigned process, E4_VirtualProcessEntry *route)
++{
++    ELAN4_DEV    *dev = uctx->uctx_ctxt.ctxt_dev;
++    USER_VPSEG   *seg;
++    ELAN_LOCATION location;
++
++    kmutex_lock (&uctx->uctx_vpseg_lock);
++
++    if ((seg = user_find_vpseg (uctx, process, process)) == NULL || seg->vps_type != USER_VPSEG_P2P)
++    {
++      kmutex_unlock (&uctx->uctx_vpseg_lock);
++      return (-EINVAL);
++    }
++
++    /* check that the route supplied is valid and goes to the correct place */
++    location = user_process2location (uctx, seg, process);
++
++    if (elan4_check_route (&uctx->uctx_position, location, route, 0) != 0)
++    {
++      kmutex_unlock (&uctx->uctx_vpseg_lock);
++      return (-EINVAL);
++    }
++
++    if (seg->vps_p2p_routes == NULL)
++      KMEM_ZALLOC (seg->vps_p2p_routes, E4_VirtualProcessEntry *, sizeof (E4_VirtualProcessEntry) * seg->vps_entries, 1);
++    
++    if (seg->vps_p2p_routes == NULL)
++    {
++      kmutex_unlock (&uctx->uctx_vpseg_lock);
++      return (-ENOMEM);
++    }
++    
++    seg->vps_p2p_routes[process - seg->vps_process].Values[0] = route->Values[0];
++    seg->vps_p2p_routes[process - seg->vps_process].Values[1] = ROUTE_CTXT_VALUE(location.loc_context) | (route->Values[1] & ~ROUTE_CTXT_MASK);
++    
++    PRINTF (uctx, DBG_ROUTE, "user_set_route: vp=%d -> %016llx%016llx\n", process, 
++          seg->vps_p2p_routes[process - seg->vps_process].Values[1], seg->vps_p2p_routes[process - seg->vps_process].Values[0]);
++
++    elan4_write_route (dev, uctx->uctx_routetable, process, &seg->vps_p2p_routes[process - seg->vps_process]);
++
++    kmutex_unlock (&uctx->uctx_vpseg_lock);
++
++    return (0);
++}
++
++int
++user_reset_route (USER_CTXT *uctx, unsigned process)
++{
++    ELAN4_DEV             *dev = uctx->uctx_ctxt.ctxt_dev;
++    E4_VirtualProcessEntry route;
++    ELAN_LOCATION          location;
++    USER_VPSEG            *seg;
++
++    kmutex_lock (&uctx->uctx_vpseg_lock);
++
++    if ((seg = user_find_vpseg (uctx, process, process)) == NULL || seg->vps_type != USER_VPSEG_P2P)
++    {
++      kmutex_unlock (&uctx->uctx_vpseg_lock);
++      return (-EINVAL);
++    }
++
++    if (seg->vps_p2p_routes != NULL)
++    {
++      seg->vps_p2p_routes[process - seg->vps_process].Values[0] = 0;
++      seg->vps_p2p_routes[process - seg->vps_process].Values[1] = 0;
++    }
++    
++    /* generate the default route to this location */
++    location = user_process2location (uctx, seg, process);
++
++    PRINTF (uctx, DBG_ROUTE, "user_reset_route: vp=%d\n", process);
++
++    if (elan4_generate_route (&uctx->uctx_position, &route, location.loc_context, location.loc_node, location.loc_node, 0) < 0)
++      elan4_invalidate_route (dev, uctx->uctx_routetable, process);
++    else
++      elan4_write_route (dev, uctx->uctx_routetable, process, &route);
++
++    kmutex_unlock (&uctx->uctx_vpseg_lock);
++
++    return (0);
++}
++
++int
++user_get_route (USER_CTXT *uctx, unsigned process, E4_VirtualProcessEntry *route)
++{
++    ELAN4_DEV  *dev = uctx->uctx_ctxt.ctxt_dev;
++    USER_VPSEG   *seg;
++    
++    kmutex_lock (&uctx->uctx_vpseg_lock);
++
++    if ((seg = user_find_vpseg (uctx, process, process)) == NULL || seg->vps_type != USER_VPSEG_P2P)
++    {
++      kmutex_unlock (&uctx->uctx_vpseg_lock);
++      return (-EINVAL);
++    }
++
++    elan4_read_route (dev, uctx->uctx_routetable, process, route);
++
++    kmutex_unlock (&uctx->uctx_vpseg_lock);
++    return (0);
++}
++
++int
++user_check_route (USER_CTXT *uctx, unsigned process, E4_VirtualProcessEntry *route, unsigned *error)
++{
++    ELAN4_DEV  *dev = uctx->uctx_ctxt.ctxt_dev;
++    USER_VPSEG *seg;
++    
++    kmutex_lock (&uctx->uctx_vpseg_lock);
++
++    if ((seg = user_find_vpseg (uctx, process, process)) == NULL || seg->vps_type != USER_VPSEG_P2P)
++    {
++      kmutex_unlock (&uctx->uctx_vpseg_lock);
++      return (-EINVAL);
++    }
++
++    elan4_read_route (dev, uctx->uctx_routetable, process, route);
++
++    *error = elan4_check_route (&uctx->uctx_position, user_process2location (uctx, seg, process), route, 0);
++
++    kmutex_unlock (&uctx->uctx_vpseg_lock);
++    return (0);
++}
++
++int
++user_send_neterr_msg (USER_CTXT *uctx, unsigned int vp, unsigned int nctx, unsigned int retries, ELAN4_NETERR_MSG *msg)
++{
++    USER_VPSEG   *seg;
++    ELAN_LOCATION location;
++    unsigned long flags;
++    int                 res, found = 0;
++    struct list_head *el;
++
++    kmutex_lock (&uctx->uctx_vpseg_lock);
++    /* determine the location of the virtual process */
++    if ((seg = user_find_vpseg (uctx, vp, vp)) == NULL)
++    {
++      PRINTF (uctx, DBG_NETERR, "user_send_neterr_msg: vp=%d has no vpseg\n", vp);
++
++      kmutex_unlock (&uctx->uctx_vpseg_lock);
++      return -EINVAL;
++    }
++
++    switch (seg->vps_type)
++    {
++    case USER_VPSEG_P2P:
++      location = user_process2location (uctx, seg, vp);
++      break;
++
++    case USER_VPSEG_BCAST:
++      PRINTF (uctx, DBG_NETERR, "user_send_neterr_msg: vp=%d is a bcast vp\n", vp);
++      kmutex_unlock (&uctx->uctx_vpseg_lock);
++      return -EINVAL;
++    }
++
++    /*  check that we're attached to the network context */
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    list_for_each (el , &uctx->uctx_cent_list) {
++      USER_CTXT_ENTRY *cent = list_entry (el, USER_CTXT_ENTRY, cent_link);
++      
++      if (cent->cent_cap->cap_mycontext == nctx)
++          found++;
++    }
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++    
++    if (! found)
++    {
++      PRINTF (uctx, DBG_NETERR, "user_send_neterr_msg: nctx=%d not attached\n", nctx);
++      kmutex_unlock (&uctx->uctx_vpseg_lock);
++
++      return -EINVAL;
++    }
++
++    /* Update the fields which the user might have "faked" */
++    msg->msg_context            = location.loc_context;
++    msg->msg_sender.loc_node    = uctx->uctx_position.pos_nodeid;
++    msg->msg_sender.loc_context = nctx;
++
++    res = elan4_neterr_sendmsg (uctx->uctx_ctxt.ctxt_dev, location.loc_node, retries, msg);
++
++    kmutex_unlock (&uctx->uctx_vpseg_lock);
++
++    return (res);
++}
++
++
++static int
++user_resolvevp (USER_CTXT *uctx, unsigned process)
++{
++    int                    res = 0;
++    USER_VPSEG            *seg;
++    ELAN_LOCATION          location;
++    E4_VirtualProcessEntry route;
++
++    PRINTF1 (uctx, DBG_VP, "user_resolvevp: process=%d\n", process);
++
++    kmutex_lock (&uctx->uctx_vpseg_lock);
++
++    if ((seg = user_find_vpseg (uctx, process, process)) == NULL)
++    {
++      kmutex_unlock (&uctx->uctx_vpseg_lock);
++      return (-EINVAL);
++    }
++
++    switch (seg->vps_type)
++    {
++    case USER_VPSEG_P2P:
++#ifdef use_elanmod
++      if ((res = user_validate_cap (uctx, seg->vps_p2p_cap, ELAN_USER_P2P)) != 0)
++          break;
++#endif
++
++      location = user_process2location (uctx, seg, process);
++
++      PRINTF (uctx, DBG_VP, "user_resolvevp: vp=%d -> node=%d ctx=%d\n", process, location.loc_node, location.loc_context);
++      
++      if (seg->vps_p2p_routes != NULL && seg->vps_p2p_routes[process - seg->vps_process].Values[0] != 0)
++          route = seg->vps_p2p_routes[process - seg->vps_process];
++      else if ((res = elan4_generate_route (&uctx->uctx_position, &route, location.loc_context, location.loc_node, location.loc_node, user_p2p_route_options)) < 0)
++          break;;
++      
++      elan4_write_route (uctx->uctx_ctxt.ctxt_dev, uctx->uctx_routetable, process, &route);
++      break;
++
++    case USER_VPSEG_BCAST:
++      res = user_loadroute_bcast (uctx, seg);
++      break;
++      
++    default:
++      res = -EINVAL;
++      break;
++    }
++
++    kmutex_unlock (&uctx->uctx_vpseg_lock);
++    return (res);
++}
++
++static void
++user_eproc_trap (ELAN4_CTXT *ctxt, E4_uint64 status)
++{
++    USER_CTXT    *uctx = (USER_CTXT *) ctxt;
++    unsigned long flags;
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++
++    if (RING_QUEUE_REALLY_FULL (uctx->uctx_eprocTrapQ) || (uctx->uctx_status & UCTX_STOPPED))
++    {
++      PRINTF (uctx, DBG_EPROC, "user_eproc_trap: %s\n", (uctx->uctx_status & UCTX_STOPPED) ? "context stopped" : "trap queue overflow");
++
++      uctx->uctx_status |= UCTX_EPROC_QUEUE_ERROR;
++    }
++    else
++    {
++      elan4_extract_eproc_trap (ctxt->ctxt_dev, status, RING_QUEUE_BACK (uctx->uctx_eprocTrapQ, uctx->uctx_eprocTraps), 0);
++      
++      DBGCMD (ctxt, DBG_EPROC, elan4_display_eproc_trap (ctxt, DBG_EPROC, "user_eproc_trap", RING_QUEUE_BACK(uctx->uctx_eprocTrapQ, uctx->uctx_eprocTraps)));
++      
++      if (RING_QUEUE_ADD (uctx->uctx_eprocTrapQ))
++          user_start_stopping (uctx, UCTX_EPROC_QUEUE_FULL);
++    }
++
++    user_signal_trap (uctx);
++
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++}
++
++static void
++user_cproc_trap (ELAN4_CTXT *ctxt, E4_uint64 status, unsigned cqnum)
++{
++    USER_CTXT        *uctx = (USER_CTXT *) ctxt;
++    USER_CQ          *ucq  = NULL;
++    struct list_head *entry;
++    unsigned long     flags;
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    
++    list_for_each (entry, &uctx->uctx_cqlist) {
++      ucq = list_entry (entry, USER_CQ, ucq_link);
++
++      if (elan4_cq2num(ucq->ucq_cq) == cqnum)
++          break;
++    }
++
++    ASSERT (ucq != NULL);
++
++    if (ucq->ucq_state != UCQ_RUNNING && CPROC_TrapType (status) == CommandProcInserterError)
++    {
++      PRINTF (ctxt, DBG_TRAP, "user_cproc_trap CommandProcInserterError\n");
++      ucq->ucq_errored++;
++    }
++    else
++    {
++      int vp;
++      
++      ASSERT (ucq->ucq_state == UCQ_RUNNING);
++
++      elan4_extract_cproc_trap (ctxt->ctxt_dev, status, &ucq->ucq_trap, cqnum);
++
++      DBGCMD (ctxt, DBG_CPROC, elan4_display_cproc_trap (ctxt, DBG_CPROC, "user_cproc_trap", &ucq->ucq_trap));
++
++      ucq->ucq_state = UCQ_TRAPPED;
++
++      if ((vp = cproc_open_extract_vp(uctx->uctx_ctxt.ctxt_dev, ucq->ucq_cq)) != -1)
++      {
++          E4_VirtualProcessEntry route;
++
++          elan4_read_route(uctx->uctx_ctxt.ctxt_dev, uctx->uctx_routetable, vp,  &route);
++          elan4_ringbuf_store(&uctx->uctx_ctxt.ctxt_dev->dev_cproc_timeout_routes, &route, uctx->uctx_ctxt.ctxt_dev);
++      }
++    }
++
++    user_signal_trap (uctx);
++      
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++}
++
++static void
++user_dproc_trap (ELAN4_CTXT *ctxt, E4_uint64 status, unsigned unit)
++{
++    USER_CTXT *uctx = (USER_CTXT *) ctxt;
++    unsigned long flags;
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++
++    if (RING_QUEUE_REALLY_FULL (uctx->uctx_dprocTrapQ) || (uctx->uctx_status & UCTX_STOPPED))
++    {
++      PRINTF (uctx, DBG_DPROC, "user_dproc_trap: %s\n", (uctx->uctx_status & UCTX_STOPPED) ? "context stopped" : "trap queue overflow");
++
++      uctx->uctx_status |= UCTX_DPROC_QUEUE_ERROR;
++    }
++    else
++    {
++      ELAN4_DPROC_TRAP *trap = RING_QUEUE_BACK (uctx->uctx_dprocTrapQ, uctx->uctx_dprocTraps);
++      
++      elan4_extract_dproc_trap (ctxt->ctxt_dev, status, trap, unit);
++
++      if ((DPROC_PacketTimeout (trap->tr_status)) || (DPROC_TrapType (trap->tr_status) == DmaProcFailCountError))
++      {
++          E4_VirtualProcessEntry route;
++
++            elan4_read_route(ctxt->ctxt_dev, uctx->uctx_routetable, trap->tr_desc.dma_vproc, &route);
++
++          if ((route.Values[0] != 0) || (route.Values[1] != 0))
++            {
++              elan4_ringbuf_store(&uctx->uctx_ctxt.ctxt_dev->dev_dproc_timeout_routes, &route, uctx->uctx_ctxt.ctxt_dev);
++          }
++      }
++      else if (DPROC_TrapType (trap->tr_status) == DmaProcPacketAckError)
++      {
++          E4_VirtualProcessEntry route;
++
++            elan4_read_route(ctxt->ctxt_dev, uctx->uctx_routetable, trap->tr_desc.dma_vproc, &route);
++
++          if ((route.Values[0] != 0) || (route.Values[1] != 0))
++            {
++              elan4_ringbuf_store(&uctx->uctx_ctxt.ctxt_dev->dev_ack_error_routes, &route, uctx->uctx_ctxt.ctxt_dev);
++           }
++      } 
++      else 
++      
++      DBGCMD (ctxt, DBG_DPROC, elan4_display_dproc_trap (ctxt, DBG_DPROC, "user_dproc_trap", trap));
++
++      if (!DPROC_PrefetcherFault (status) && DPROC_TrapType(status) == DmaProcFailCountError && !RING_QUEUE_FULL (uctx->uctx_dmaQ))
++      {
++          trap->tr_desc.dma_typeSize |= DMA_FailCount (user_dproc_retry_count);
++
++          *RING_QUEUE_BACK (uctx->uctx_dmaQ, uctx->uctx_dmas) = trap->tr_desc;
++    
++          (void) RING_QUEUE_ADD (uctx->uctx_dmaQ);
++      }
++      else
++      {
++          if (RING_QUEUE_ADD (uctx->uctx_dprocTrapQ))
++              user_start_stopping (uctx, UCTX_DPROC_QUEUE_FULL);
++      }
++    }
++
++    user_signal_trap (uctx);
++
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++}
++
++static void
++user_tproc_trap (ELAN4_CTXT *ctxt, E4_uint64 status)
++{
++    USER_CTXT *uctx = (USER_CTXT *) ctxt;
++    unsigned long flags;
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++
++    if (RING_QUEUE_REALLY_FULL (uctx->uctx_tprocTrapQ) || (uctx->uctx_status & UCTX_STOPPED))
++    {
++      PRINTF (uctx, DBG_TPROC, "user_tproc_trap: %s\n", (uctx->uctx_status & UCTX_STOPPED) ? "context stopped" : "trap queue overflow");
++
++      uctx->uctx_status |= UCTX_TPROC_QUEUE_ERROR;
++    }
++    else
++    {
++      elan4_extract_tproc_trap (ctxt->ctxt_dev, status, RING_QUEUE_BACK (uctx->uctx_tprocTrapQ, uctx->uctx_tprocTraps));
++      
++      DBGCMD (ctxt, DBG_TPROC, elan4_display_tproc_trap (ctxt, DBG_TPROC, "user_tproc_trap", RING_QUEUE_BACK (uctx->uctx_tprocTrapQ, uctx->uctx_tprocTraps)));
++      
++      if (RING_QUEUE_ADD (uctx->uctx_tprocTrapQ))
++          user_start_stopping (uctx, UCTX_TPROC_QUEUE_FULL);
++    }
++    user_signal_trap (uctx);
++
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++}
++
++static void
++user_iproc_trap (ELAN4_CTXT *ctxt, E4_uint64 status, unsigned unit)
++{
++    USER_CTXT       *uctx  = (USER_CTXT *) ctxt;
++    USER_IPROC_TRAP *utrap = &uctx->uctx_iprocTrap[unit & 1];
++    unsigned long    flags;
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++
++    ASSERT (utrap->ut_state == UTS_IPROC_RUNNING);
++
++    elan4_extract_iproc_trap (ctxt->ctxt_dev, status, &utrap->ut_trap, unit);
++    DBGCMD (ctxt, DBG_IPROC, elan4_display_iproc_trap (ctxt, DBG_IPROC, "user_iproc_trap", &utrap->ut_trap));
++
++    utrap->ut_state = UTS_IPROC_TRAPPED;
++
++    user_start_nacking (uctx, unit ? UCTX_IPROC_CH0_TRAPPED : UCTX_IPROC_CH1_TRAPPED);
++
++    user_signal_trap (uctx);
++
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++}
++
++static void
++user_interrupt (ELAN4_CTXT *ctxt, E4_uint64 cookie)
++{
++    USER_CTXT *uctx = (USER_CTXT *) ctxt;
++    unsigned long flags;
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++
++    PRINTF1 (uctx, DBG_TRAP, "user_interrupt: cookie=%llx\n", cookie);
++
++    switch (cookie)
++    {
++    case ELAN4_INT_COOKIE_DDCQ:
++      uctx->uctx_ddcq_intr--;
++
++      user_signal_trap (uctx);
++      break;
++
++    default:
++      if (uctx->uctx_intcookie_table == NULL || intcookie_fire (uctx->uctx_intcookie_table, cookie) != 0)
++      {
++          PRINTF2 (uctx, DBG_TRAP, "user_interrupt: cookie=%llx %s\n", cookie, uctx->uctx_intcookie_table ? "not found" : "no table");
++          uctx->uctx_status |= UCTX_EPROC_QUEUE_ERROR;
++          user_signal_trap (uctx);
++      }
++      break;
++    }
++
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++}
++
++static void
++user_neterrmsg (ELAN4_CTXT *ctxt, ELAN4_NETERR_MSG *msg)
++{
++    USER_CTXT *uctx = (USER_CTXT *) ctxt;
++    unsigned long flags;
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    
++    if (! RING_QUEUE_FULL (uctx->uctx_msgQ))
++    {
++      memcpy (RING_QUEUE_BACK (uctx->uctx_msgQ, uctx->uctx_msgs), msg, sizeof (ELAN4_NETERR_MSG));
++
++      (void) RING_QUEUE_ADD (uctx->uctx_msgQ);
++    
++      user_signal_trap (uctx);
++    }
++    
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++}
++
++ELAN4_TRAP_OPS user_trap_ops = 
++{
++    user_eproc_trap,
++    user_cproc_trap,
++    user_dproc_trap,
++    user_tproc_trap,
++    user_iproc_trap,
++    user_interrupt,
++    user_neterrmsg,
++};
++
++static int
++deliver_trap (ELAN4_USER_TRAP *utrapp, int type, unsigned proc, void *trap, ...)
++{
++    register int i, len;
++    va_list ap;
++
++    PRINTF (NULL, DBG_TRAP, "deliver_trap: type=%d proc=%d\n", type, proc);
++
++    switch (proc)
++    {
++    case UTS_CPROC:      len = sizeof (ELAN4_CPROC_TRAP); break;
++    case UTS_DPROC:      len = sizeof (ELAN4_DPROC_TRAP); break;
++    case UTS_EPROC:      len = sizeof (ELAN4_EPROC_TRAP); break;
++    case UTS_IPROC:      len = sizeof (ELAN4_IPROC_TRAP); break;
++    case UTS_TPROC:      len = sizeof (ELAN4_TPROC_TRAP); break;
++    case UTS_NETERR_MSG: len = sizeof (ELAN4_NETERR_MSG); break;
++    default:             len = 0; break;
++    }
++
++    if (put_user (type, &utrapp->ut_type) || put_user (proc, &utrapp->ut_proc) || copy_to_user (&utrapp->ut_trap, trap, len))
++      return (UTS_EFAULT);
++
++    va_start (ap, trap);
++    for (i = 0; i < sizeof (utrapp->ut_args)/sizeof (utrapp->ut_args[0]); i++)
++      if (put_user (va_arg (ap, unsigned long), &utrapp->ut_args[i]))
++          return (UTS_EFAULT);
++    va_end (ap);
++
++    return (type);
++}
++
++static int
++user_pagefault (USER_CTXT *uctx, E4_FaultSave *farea)
++{
++    E4_Addr      addr = farea->FaultAddress;
++    E4_uint32    fsr  = FaultSaveFSR(farea->FSRAndFaultContext);
++    FAULT_SAVE  *entry;
++    FAULT_SAVE **predp;
++    int count;
++
++    PRINTF2 (uctx, DBG_FAULT, "user_pagefault: addr=%llx fsr %x\n", (unsigned long long) addr, fsr);
++    
++    if ((fsr & FSR_FaultForBadData) != 0)                     /* Memory ECC error during walk */
++    {
++      PRINTF0 (uctx, DBG_FAULT, "user_pagefault: ECC error during walk\n");
++      return (-EFAULT);
++    }
++    
++    if ((fsr & FSR_FaultForMaxChainCount) != 0)                       /* Have walked a chain of 1024 items */
++    {
++      PRINTF0 (uctx, DBG_FAULT, "user_pagefault: pte chain too long\n");
++      return (-EFAULT);
++    }
++    
++    if (uctx->uctx_num_fault_save)
++    {
++        spin_lock (&uctx->uctx_fault_lock);
++        for( predp = &uctx->uctx_fault_list; (entry = *predp)->next != NULL; predp = &entry->next)
++        {
++          if (entry->addr == (addr & ~((E4_Addr) PAGE_SIZE-1)))
++              break;
++        }
++
++        *predp = entry->next;
++        entry->next = uctx->uctx_fault_list;
++        uctx->uctx_fault_list = entry;
++
++        if (entry->addr == (addr & ~((E4_Addr) PAGE_SIZE-1)))
++        {
++          if ((entry->count <<= 1) > max_fault_pages)
++              entry->count = max_fault_pages;
++        }
++        else
++          entry->count = min_fault_pages;
++
++        entry->addr = (addr & ~((E4_Addr) PAGE_SIZE-1))+(entry->count * PAGE_SIZE);
++        count = entry->count;
++        spin_unlock (&uctx->uctx_fault_lock);
++
++        if (user_load_range (uctx, addr & ~((E4_Addr) PAGE_SIZE-1), count * PAGESIZE, fsr) == 0)
++      return 0;
++
++      /* else pre-faulting has failed, try just this page */
++    }
++
++    return (user_load_range (uctx, addr & ~((E4_Addr) PAGE_SIZE-1), PAGE_SIZE, fsr));
++
++}
++
++static int
++queue_dma_for_retry (USER_CTXT *uctx, ELAN4_USER_TRAP *utrapp, E4_DMA *dma)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++
++    if (RING_QUEUE_FULL (uctx->uctx_dmaQ))
++    {
++      spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++      
++      return (deliver_trap (utrapp, UTS_QUEUE_OVERFLOW, UTS_NOPROC, NULL, UCTX_DPROC_QUEUE_OVERFLOW));
++    }
++
++    *RING_QUEUE_BACK (uctx->uctx_dmaQ, uctx->uctx_dmas) = *dma;
++    
++    (void) RING_QUEUE_ADD (uctx->uctx_dmaQ);
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++
++    return (UTS_FINISHED);
++}
++
++static int
++queue_thread_for_retry (USER_CTXT *uctx, ELAN4_USER_TRAP *utrapp, E4_ThreadRegs *regs)
++{
++    unsigned long flags;
++    
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++
++    if (RING_QUEUE_FULL (uctx->uctx_threadQ))
++    {
++      spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++
++      return (deliver_trap (utrapp, UTS_QUEUE_OVERFLOW, UTS_NOPROC, NULL, UCTX_TPROC_QUEUE_OVERFLOW));
++    }
++
++    *RING_QUEUE_BACK (uctx->uctx_threadQ, uctx->uctx_threads) = *regs;
++    (void) RING_QUEUE_ADD (uctx->uctx_threadQ);
++    
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++
++    return (UTS_FINISHED);
++}
++
++static int
++fixup_eproc_trap (USER_CTXT *uctx, ELAN4_EPROC_TRAP *trap, int waitevent)
++{
++    E4_FaultSave *farea = &trap->tr_faultarea;
++    E4_uint32     fsr   = FaultSaveFSR(farea->FSRAndFaultContext);
++    E4_uint64     CountAndType;
++    E4_uint64     CopySource;
++    E4_uint64     CopyDest;
++
++    /*
++     * Event processor can trap as follows :
++     *   1) Event location read               (faddr == event location & Event Permission)
++     *   2) Event location write      (faddr == event location & Event Permission)
++     *   3) Copy Source read          Read Access
++     *   4) Copy/Write dest write     other
++     *
++     *  NOTE - it is possible to see both 3) and 4) together - but only with physical errors.
++     */
++    if (AT_Perm(fsr) == AT_PermLocalDataRead || AT_Perm(fsr) == AT_PermLocalDataWrite)
++    {
++      /* 
++       * We complete the copy/write by issuing a waitevent 0 of the approriate type.
++       *   - NB mask off bottom bits of EventAddr in case of partial setevent
++       */
++      E4_uint64 EventAddr = trap->tr_eventaddr & ~((E4_uint64) E4_EVENT_ALIGN-1);
++
++      if (! user_ddcq_check (uctx, 4))
++          return (0);
++      
++      if ((trap->tr_event.ev_CountAndType & E4_EVENT_COPY_TYPE_MASK) == E4_EVENT_WRITE)
++      {
++          /* case 4) faulted on write word to destination */
++
++          CountAndType = trap->tr_event.ev_CountAndType & E4_EVENT_TYPE_MASK;
++          
++          PRINTF (uctx, DBG_TRAP, "fixup_eproc_trap: write Event=%llx CountAndType=%llx\n", EventAddr, CountAndType);
++          PRINTF (uctx, DBG_TRAP, "                  WritePtr=%llx WriteValue=%llx\n", 
++                  trap->tr_event.ev_WritePtr, trap->tr_event.ev_WriteValue);
++
++          user_ddcq_waitevent (uctx, EventAddr, CountAndType, trap->tr_event.ev_WritePtr, trap->tr_event.ev_WriteValue);
++      }
++      else
++      {
++          /* case 3) or case 4) faulted on read/write of copy */
++          if (AT_Perm (fsr) == AT_PermLocalDataRead)
++          {
++              CountAndType = (trap->tr_event.ev_CountAndType & E4_EVENT_DATA_TYPE_MASK) | EPROC_CopySize(trap->tr_status);
++              CopySource   = trap->tr_event.ev_CopySource - EVENT_COPY_BLOCK_SIZE;
++              CopyDest     = trap->tr_event.ev_CopyDest;
++          }
++          else
++          {
++              CountAndType = ((trap->tr_event.ev_CountAndType & E4_EVENT_DATA_TYPE_MASK) | 
++                              ((EPROC_CopySize(trap->tr_status) + EVENT_COPY_NDWORDS) & E4_EVENT_COPY_SIZE_MASK));
++              CopySource   = trap->tr_event.ev_CopySource - EVENT_COPY_BLOCK_SIZE;
++              CopyDest     = trap->tr_event.ev_CopyDest - EVENT_COPY_BLOCK_SIZE;
++          }
++          
++          PRINTF (uctx, DBG_TRAP, "fixup_eproc_trap: copy Event=%llx CountAndType=%llx\n", EventAddr, CountAndType);
++          PRINTF (uctx, DBG_TRAP, "                  CopySource=%llx CopyDest=%llx\n", CopySource, CopyDest);
++
++          user_ddcq_waitevent (uctx, EventAddr, CountAndType, CopySource, CopyDest);
++      }
++    }
++    else
++    {
++      E4_uint64 EventAddr  = trap->tr_eventaddr & ~((E4_uint64) E4_EVENT_ALIGN-1);
++      E4_uint32 EventCount = trap->tr_eventaddr & (E4_EVENT_ALIGN-1);
++
++      /* case 1) or 2) - just reissue the event */
++      if (! waitevent)
++          PRINTF (uctx, DBG_TRAP, "fixup_eproc_trap: setevent EventAddr=%llx EventCount=%x\n", EventAddr, EventCount);
++      else
++      {
++          PRINTF (uctx, DBG_TRAP, "fixup_eproc_trap: waitevent Event=%llx CountAndType=%llx\n", EventAddr, trap->tr_event.ev_CountAndType);
++          PRINTF (uctx, DBG_TRAP, "                  Param[0]=%llx Param[1]=%llx\n",
++                   trap->tr_event.ev_Params[0], trap->tr_event.ev_Params[1]);
++      }
++
++      if (! user_ddcq_check (uctx, waitevent ? 4 : 2))
++          return (0);
++      
++      if (waitevent)
++          user_ddcq_waitevent (uctx, EventAddr, trap->tr_event.ev_CountAndType, 
++                                trap->tr_event.ev_Params[0], trap->tr_event.ev_Params[1]);
++      else
++          user_ddcq_seteventn (uctx, EventAddr, EventCount);
++    }
++
++    return (1);
++}
++
++
++static int
++resolve_eproc_trap (USER_CTXT *uctx, ELAN4_USER_TRAP *utrapp, ELAN4_EPROC_TRAP *trap)
++{
++    switch (EPROC_TrapType (trap->tr_status))
++    {
++    case EventProcNoFault:
++      PRINTF (uctx, DBG_TRAP, "resolve_eproc_trap: EventProcNoFault\n");
++
++      return (UTS_FINISHED);
++      
++    case EventProcAddressAlignment:
++      return (deliver_trap (utrapp, UTS_ALIGNMENT_ERROR, UTS_EPROC, trap));
++
++    case EventProcMemoryFault:
++      PRINTF (uctx, DBG_TRAP, "resolve_eproc_trap: EventProcMemoryFault @ %llx\n", trap->tr_faultarea.FaultAddress);
++
++      if (user_pagefault (uctx, &trap->tr_faultarea) != 0)
++          return (deliver_trap (utrapp, UTS_INVALID_ADDR, UTS_EPROC, trap));
++
++      return (UTS_FINISHED);
++      
++    case EventProcCountWrapError:
++      return (deliver_trap (utrapp, UTS_BAD_TRAP, UTS_EPROC, trap));
++
++    default:
++      printk ("resolve_eproc_trap: bad trap type %d\n", EPROC_TrapType (trap->tr_status));
++      BUG();
++    }
++
++    return (UTS_FINISHED);
++}
++
++static int
++resolve_cproc_trap (USER_CTXT *uctx, ELAN4_USER_TRAP *utrapp, USER_CQ *ucq)
++{
++    ELAN4_DEV        *dev    = uctx->uctx_ctxt.ctxt_dev;
++    ELAN4_CPROC_TRAP *trap   = &ucq->ucq_trap;
++    E4_uint64         command;
++    int               res;
++
++    PRINTF2 (uctx, DBG_CPROC, "resolve_cproc_trap: cq %p is trapped - Status %lx\n", ucq, trap->tr_status);
++    
++    switch (CPROC_TrapType (trap->tr_status))
++    {
++    case CommandProcDmaQueueOverflow:
++      PRINTF (uctx, DBG_CPROC, "resolve_cproc_trap: CommandProcDmaQueueOverflow\n");
++      /*
++       * XXXX: should wait for the queue to become emptier if we're 
++       *       responsible for it being very full
++       */
++      ucq->ucq_state = UCQ_NEEDS_RESTART;
++      break;
++
++    case CommandProcInterruptQueueOverflow:
++      PRINTF (uctx, DBG_CPROC, "resolve_cproc_trap: CommandProcInterruptQueueOverflow\n");
++      /*
++       * XXXX: should wait for the queue to become emptier if we're
++       *       responsible for it being very full
++       */
++      ucq->ucq_state = UCQ_NEEDS_RESTART;
++      break;
++      
++    case CommandProcWaitTrap:
++      PRINTF0 (uctx, DBG_CPROC, "resolve_cproc_trap: CommandProcWaitTrap\n");
++      
++      if ((res = resolve_eproc_trap (uctx, utrapp, &trap->tr_eventtrap)) != UTS_FINISHED)
++      {
++          ucq->ucq_state = UCQ_STOPPED;
++
++          return (res);
++      }
++      
++      if (fixup_eproc_trap (uctx, &trap->tr_eventtrap, 1) == 0)
++          return UTS_RESCHEDULE;
++
++      ucq->ucq_state = UCQ_NEEDS_RESTART;
++      break;
++      
++    case CommandProcMemoryFault:
++      PRINTF1 (uctx, DBG_CPROC, "resolve_cproc_trap: CommandProcMemoryFault at %llx\n", trap->tr_faultarea.FaultAddress);
++      if (user_pagefault (uctx, &trap->tr_faultarea) != 0)
++      {
++          ucq->ucq_state = UCQ_STOPPED;
++
++          return (deliver_trap (utrapp, UTS_INVALID_ADDR, UTS_CPROC, trap, elan4_cq2idx(ucq->ucq_cq)));
++      }
++      
++      ucq->ucq_state = UCQ_NEEDS_RESTART;
++      break;
++      
++    case CommandProcRouteFetchFault:
++      command = elan4_trapped_open_command (dev, ucq->ucq_cq);
++      
++      PRINTF1 (uctx, DBG_CPROC, "resolve_cproc_trap: CommandProcRouteFetchFault to vp %d\n", (int) (command >> 32));
++      
++      if (user_resolvevp (uctx, (unsigned) (command >> 32)) != 0)
++      {
++          ucq->ucq_state = UCQ_STOPPED;
++
++          return (deliver_trap (utrapp, UTS_INVALID_VPROC, UTS_CPROC, trap, elan4_cq2idx(ucq->ucq_cq), (long) (command >> 32)));
++      }
++
++      ucq->ucq_state = UCQ_NEEDS_RESTART;
++      break;
++      
++    case CommandProcFailCountZero:
++      PRINTF0 (uctx, DBG_CPROC, "resolve_cproc_trap: CommandProcFailCountZero - reset failcount\n");
++      
++      /* NOTE - we must not modify the ChannelNotCompleted bits - so modify */
++      /*        the restart count with a part-word store */
++      elan4_updatecq (dev, ucq->ucq_cq, ucq->ucq_cq->cq_perm, user_cproc_retry_count);
++
++      ucq->ucq_state = UCQ_NEEDS_RESTART;
++      break;
++
++    case CommandProcAddressAlignment:
++      ucq->ucq_state = UCQ_STOPPED;
++
++      return (deliver_trap (utrapp, UTS_ALIGNMENT_ERROR, UTS_CPROC, trap, elan4_cq2idx(ucq->ucq_cq)));
++
++    case CommandProcPermissionTrap:
++    {
++      sdramaddr_t cqdesc = dev->dev_cqaddr + (elan4_cq2num(ucq->ucq_cq) * sizeof (E4_CommandQueueDesc));
++      E4_uint64   control = elan4_sdram_readq (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_Control));
++
++      PRINTF (uctx, DBG_CPROC, "resolve_cproc_trap: CommandProcPermissionTrap - %s\n",
++              (control & CQ_PermissionMask) != ucq->ucq_cq->cq_perm ? "resume from stop" : "permission denied");
++      
++      if ((control & CQ_PermissionMask) == ucq->ucq_cq->cq_perm)
++          return (deliver_trap (utrapp, UTS_PERMISSION_DENIED, UTS_CPROC, trap, elan4_cq2idx(ucq->ucq_cq)));
++
++      elan4_updatecq (dev, ucq->ucq_cq, ucq->ucq_cq->cq_perm, 0);
++
++      ucq->ucq_state = UCQ_NEEDS_RESTART;
++      break;
++    }
++    
++    case CommandProcBadData:
++      ucq->ucq_state = UCQ_STOPPED;
++
++      return (deliver_trap (utrapp, UTS_INVALID_COMMAND, UTS_CPROC, trap, elan4_cq2idx(ucq->ucq_cq)));
++
++    default:
++      ucq->ucq_state = UCQ_STOPPED;
++
++      return (deliver_trap (utrapp, UTS_BAD_TRAP, UTS_CPROC, trap, elan4_cq2idx(ucq->ucq_cq)));
++    }
++
++    return (UTS_FINISHED);
++}
++
++static int
++resolve_dproc_trap (USER_CTXT *uctx, ELAN4_USER_TRAP *utrapp, ELAN4_DPROC_TRAP *trap)
++{
++    ELAN_LOCATION location;
++    int node;
++
++    if (DPROC_PrefetcherFault (trap->tr_status))
++    {
++      PRINTF (uctx, DBG_DPROC, "resolve_dproc_trap: PrefetcherFault at %llx\n", trap->tr_prefetchFault.FaultAddress);
++
++      if (user_pagefault (uctx, &trap->tr_prefetchFault) != 0)
++          return (deliver_trap (utrapp, UTS_INVALID_ADDR, UTS_DPROC, trap));
++      
++      return (queue_dma_for_retry (uctx, utrapp, &trap->tr_desc));
++    }
++    
++    switch (DPROC_TrapType (trap->tr_status))
++    {
++    case DmaProcRouteFetchFault:
++      PRINTF (uctx, DBG_DPROC, "resolve_dproc_trap: DmaProcRouteFetchFault vp %d\n", trap->tr_desc.dma_vproc);
++
++      if (user_resolvevp (uctx, trap->tr_desc.dma_vproc) != 0)
++          return (deliver_trap (utrapp, UTS_INVALID_VPROC, UTS_DPROC, trap, trap->tr_desc.dma_vproc));
++      
++      return (queue_dma_for_retry (uctx, utrapp, &trap->tr_desc /* immediate */));
++      
++    case DmaProcFailCountError:
++      PRINTF (uctx, DBG_DPROC, "resolve_dproc_trap: DmaProcFailCountError - vp %d cookie %llx\n",
++              trap->tr_desc.dma_vproc, trap->tr_desc.dma_cookie);
++
++      trap->tr_desc.dma_typeSize |= DMA_FailCount (user_dproc_retry_count);
++
++      kmutex_lock (&uctx->uctx_vpseg_lock);
++      location = user_process2location(uctx, NULL, trap->tr_desc.dma_vproc);
++      kmutex_unlock (&uctx->uctx_vpseg_lock);
++      node = location.loc_node;
++
++        if ((node >= 0) && (node <= uctx->uctx_ctxt.ctxt_dev->dev_position.pos_nodes))
++        { 
++          kmutex_lock(&uctx->uctx_ctxt.ctxt_dev->dev_lock);
++          uctx->uctx_ctxt.ctxt_dev->dev_dproc_timeout[node]++;
++          kmutex_unlock(&uctx->uctx_ctxt.ctxt_dev->dev_lock);
++        }
++      
++      return (queue_dma_for_retry (uctx, utrapp, &trap->tr_desc /* XXXX - backoff for some time later */));
++
++    case DmaProcPacketAckError:
++      PRINTF (uctx, DBG_DPROC, "resolve_dproc_trap: DmaProcPacketAckError - %d%s\n", DPROC_PacketAckValue (trap->tr_status), 
++              DPROC_PacketTimeout (trap->tr_status) ? " timeout" : "");
++
++      kmutex_lock (&uctx->uctx_vpseg_lock);
++      location = user_process2location(uctx, NULL, trap->tr_desc.dma_vproc);
++      kmutex_unlock (&uctx->uctx_vpseg_lock);
++      node = location.loc_node;
++
++      if ((node >= 0) && (node <= uctx->uctx_ctxt.ctxt_dev->dev_position.pos_nodes))
++      {
++          kmutex_lock(&uctx->uctx_ctxt.ctxt_dev->dev_lock);
++          uctx->uctx_ctxt.ctxt_dev->dev_ack_errors[node]++;
++          kmutex_unlock(&uctx->uctx_ctxt.ctxt_dev->dev_lock);
++      }
++
++      return (queue_dma_for_retry (uctx, utrapp, &trap->tr_desc /* XXXX - backoff for some time later */));
++
++    case DmaProcQueueOverflow:
++      PRINTF (uctx, DBG_DPROC, "resolve_dproc_trap: DmaProcQueueOverflow\n");
++      return (queue_dma_for_retry (uctx, utrapp, &trap->tr_desc /* XXXX - backoff for some time later */));
++      
++    case DmaProcRunQueueReadFault:
++      return (deliver_trap (utrapp, UTS_BAD_TRAP, UTS_DPROC, trap));
++      
++    default:
++      printk ("resolve_dproc_trap: unknown trap type : %d\n", DPROC_TrapType(trap->tr_status));
++      BUG();
++    }
++    return UTS_FINISHED;
++}
++
++int
++resolve_tproc_trap (USER_CTXT *uctx, ELAN4_USER_TRAP *utrapp, ELAN4_TPROC_TRAP *trap)
++{
++    PRINTF (uctx, DBG_TPROC, "resolve_tproc_trap: trap state = %lx\n", trap->tr_state);
++
++    if (trap->tr_state & TS_TrapForTooManyInstructions)
++      return (deliver_trap (utrapp, UTS_BAD_TRAP, UTS_TPROC, trap));
++    
++    if (trap->tr_state & TS_Unimplemented)
++      return (deliver_trap (utrapp, UTS_UNIMP_INSTR, UTS_TPROC, trap));
++    
++    if (trap->tr_state & TS_DataAlignmentError)
++      return (deliver_trap (utrapp, UTS_ALIGNMENT_ERROR, UTS_TPROC, trap));
++    
++    if ((trap->tr_state & TS_InstAccessException) && user_pagefault (uctx, &trap->tr_instFault) != 0)
++      return (deliver_trap (utrapp, UTS_INVALID_ADDR, UTS_TPROC, trap));
++    
++    if ((trap->tr_state & TS_DataAccessException) && user_pagefault (uctx, &trap->tr_dataFault) != 0)
++      return (deliver_trap (utrapp, UTS_INVALID_ADDR, UTS_TPROC, trap));
++    
++    /* If we're restarting from trap - then just need to re-issue it */
++    if (trap->tr_pc == uctx->uctx_trestart_addr || (trap->tr_state & TS_TrappedFlag))
++    {
++      PRINTF (uctx, DBG_TPROC, "resolve_tproc_trap: trapped in trap code PC=%llx SP=%llx\n", trap->tr_pc, trap->tr_regs[1]);
++
++      trap->tr_regs[0] = uctx->uctx_trestart_addr;
++    }
++    else
++    {
++      E4_uint64 *sp = (E4_uint64 *) user_elan2main (uctx, trap->tr_regs[1]);
++      int        i, reload;
++
++      /* need to store the register on the stack see */
++      /*  lib_tproc_trampoline_elan4_thread.S for stack layout */
++#define TS_STACK_OFF(REG)     ((((REG)&7)) - (((REG)>>3)*8) - 8)
++      for (reload = 0, i = 0; i < 64; i++)
++      {
++          if (trap->tr_dirty & ((E4_uint64) 1 << i))
++          {
++              PRINTF (uctx, DBG_TPROC, "resolve_tproc_trap: %%r%d  [%016llx] -> %p\n", i, trap->tr_regs[i], &sp[TS_STACK_OFF(i)]);
++
++              sulonglong ((u64 *) &sp[TS_STACK_OFF(i)], trap->tr_regs[i]);
++              
++              reload |= (1 << (i >> 3));
++          }
++      }
++#undef TS_STACK_OFF
++
++      PRINTF (uctx, DBG_TPROC, "resolve_tproc_trap: pc %llx npc %llx\n", trap->tr_pc, trap->tr_npc);
++      PRINTF (uctx, DBG_TPROC, "resolve_tproc_trap: CC %x reload %x\n", (int) (trap->tr_state >> TS_XCCshift), reload);
++
++      trap->tr_regs[0] = uctx->uctx_trestart_addr;
++      trap->tr_regs[2] = trap->tr_pc;
++      trap->tr_regs[3] = trap->tr_npc;
++      trap->tr_regs[4] = (trap->tr_state >> TS_XCCshift) & TS_XCCmask;
++      trap->tr_regs[5] = reload;
++    }
++
++    return (queue_thread_for_retry (uctx, utrapp, (E4_ThreadRegs *) trap->tr_regs));
++}
++
++static int
++resolve_iproc_trap (USER_CTXT *uctx, ELAN4_USER_TRAP *utrapp, int channel)
++{
++    USER_IPROC_TRAP  *utrap = &uctx->uctx_iprocTrap[channel];
++    ELAN4_IPROC_TRAP *trap  = &utrap->ut_trap;
++    unsigned long     flags;
++
++    elan4_inspect_iproc_trap (trap);
++
++    if (trap->tr_flags & TR_FLAG_TOOMANY_TRANS)
++      return (deliver_trap (utrapp, UTS_INVALID_TRANS, UTS_IPROC, trap, channel));
++
++    ASSERT (trap->tr_trappedTrans >= 0 && trap->tr_trappedTrans < trap->tr_numTransactions);
++
++    switch (IPROC_TrapValue (trap->tr_transactions[trap->tr_trappedTrans].IProcStatusCntxAndTrType))
++    {
++    case InputMemoryFault:
++      if (user_pagefault (uctx, &trap->tr_faultarea) != 0)
++      {
++          utrap->ut_state = UTS_IPROC_STOPPED;
++          
++          return (deliver_trap (utrapp, UTS_INVALID_ADDR, UTS_IPROC, trap, channel));
++      }
++      break;
++
++    case InputDmaQueueOverflow:
++    case InputEventEngineTrapped:
++      /* nothing to do for these 2 - restarting will simulate the transactions */
++      break;
++
++    case InputEopErrorOnWaitForEop:
++    case InputEopErrorTrap:
++      break;
++
++    case InputCrcErrorAfterPAckOk:
++      PRINTF (DBG_DEVICE, DBG_IPROC, "InputCrcErrorAfterPAckOk: flags %x\n", trap->tr_flags);
++
++      ASSERT ((trap->tr_flags & TR_FLAG_ACK_SENT) && ((trap->tr_flags & (TR_FLAG_DMA_PACKET|TR_FLAG_BAD_TRANS)) ||
++                                                      ((trap->tr_flags & TR_FLAG_EOP_ERROR) && trap->tr_identifyTrans == TR_TRANS_INVALID)));
++      break;
++
++    case InputDiscardAfterAckOk:
++      return (deliver_trap (utrapp, UTS_INVALID_TRANS, UTS_IPROC, trap, channel));
++
++    case InputAddressAlignment:
++      return (deliver_trap (utrapp, UTS_ALIGNMENT_ERROR, UTS_IPROC, trap, channel));
++
++    case InputInvalidTransType:
++      return (deliver_trap (utrapp, UTS_INVALID_TRANS, UTS_IPROC, trap, channel));
++
++    default:
++      printk ("resolve_iproc_trap: unknown trap type %d\n", IPROC_TrapValue (trap->tr_transactions[trap->tr_trappedTrans].IProcStatusCntxAndTrType));
++      BUG();
++      /* NOTREACHED */
++    }
++
++    if (! (trap->tr_flags & TR_FLAG_ACK_SENT) || (trap->tr_flags & TR_FLAG_EOP_BAD))
++    {
++      spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++
++      utrap->ut_state = UTS_IPROC_RUNNING;
++
++      user_stop_nacking (uctx, channel ? UCTX_IPROC_CH0_TRAPPED : UCTX_IPROC_CH1_TRAPPED);
++      
++      spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++    }
++    else if ((trap->tr_flags & (TR_FLAG_DMA_PACKET | TR_FLAG_BAD_TRANS)) || ((trap->tr_flags & TR_FLAG_EOP_ERROR) && (trap->tr_identifyTrans == TR_TRANS_INVALID)))
++    {
++      /* 
++       * TR_FLAG_DMA_PACKET   means a DMA packet has faulted.
++       *
++       * TR_FLAG_BAD_TRANS    means we have a transaction with a bad CRC after the transaction
++       *                      which sent the ack - this implies it's an overlapped ack DMA packet
++       *
++       * TR_FLAG_EOP_ERROR    means we've received an EOP reset - if we hadn't seen an identify
++       *                      transaction then it's a DMA packet.
++       *
++       * To ensure that the DMA processor works correctly the next packet must be NACKed to 
++       * cause it to resend this one.
++       */
++      PRINTF (uctx, DBG_IPROC, "resolve_iproc_trap: %s during DMA packet\n",
++              (trap->tr_flags & TR_FLAG_BAD_TRANS) ? "BadTransaction" : (trap->tr_flags & TR_FLAG_EOP_ERROR) ? "EopError" : "trap");
++
++      spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++
++      if (trap->tr_flags & TR_FLAG_DMA_PACKET)
++      {
++          if (! (trap->tr_flags & TR_FLAG_BAD_TRANS))
++              utrap->ut_state = UTS_IPROC_EXECUTE_PACKET;
++          else
++          {
++              kcondvar_t waithere;
++
++              /* We must ensure that the next packet is always nacked, so
++               * we wait here for an output timeout before dropping the 
++               * context filter - we just pause here for 4 mS */
++              kcondvar_init (&waithere);
++              kcondvar_timedwait (&waithere, &uctx->uctx_spinlock, &flags, lbolt + (HZ/250) + 1);;
++              kcondvar_destroy (&waithere);
++
++              utrap->ut_state = UTS_IPROC_RUNNING;
++              
++              user_stop_nacking (uctx, channel ? UCTX_IPROC_CH0_TRAPPED : UCTX_IPROC_CH1_TRAPPED);
++          }
++      }
++      else
++      {
++          utrap->ut_state = UTS_IPROC_RUNNING;
++
++          user_stop_nacking (uctx, channel ? UCTX_IPROC_CH0_TRAPPED : UCTX_IPROC_CH1_TRAPPED);
++      }
++
++      spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++    }
++    else if (trap->tr_flags & TR_FLAG_EOP_ERROR)
++    {
++      PRINTF (uctx, DBG_IPROC, "resolve_iproc_trap: EopError with identify\n");
++
++      utrap->ut_state = UTS_IPROC_NETWORK_ERROR;
++    }
++    else
++    {
++      PRINTF (uctx, DBG_IPROC, "resolve_iproc_trap: execute packet\n");
++
++      utrap->ut_state = UTS_IPROC_EXECUTE_PACKET;
++    }
++
++    return UTS_FINISHED;
++}
++
++
++static int
++resolve_cproc_traps (USER_CTXT *uctx, ELAN4_USER_TRAP *utrapp)
++{
++    struct list_head *entry;
++    int res = UTS_FINISHED;
++
++    kmutex_lock (&uctx->uctx_cqlock);
++    list_for_each (entry, &uctx->uctx_cqlist) {
++      USER_CQ *ucq = list_entry (entry, USER_CQ, ucq_link);
++
++      if (ucq->ucq_state == UCQ_TRAPPED)
++      {
++          res = resolve_cproc_trap (uctx, utrapp, ucq);
++
++          if (res != UTS_FINISHED)
++              break;
++      }
++
++      if (ucq->ucq_errored)
++      {
++          ucq->ucq_errored = 0;
++          res = deliver_trap (utrapp, UTS_CPROC_ERROR, UTS_CPROC, &ucq->ucq_trap, elan4_cq2idx(ucq->ucq_cq));
++          break;
++      }
++    }
++    kmutex_unlock (&uctx->uctx_cqlock);
++
++    return (res);
++}
++
++static int
++resolve_eproc_traps (USER_CTXT *uctx, ELAN4_USER_TRAP *utrapp)
++{
++    unsigned long flags;
++    int res;
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    while (! RING_QUEUE_EMPTY (uctx->uctx_eprocTrapQ))
++    {
++      ELAN4_EPROC_TRAP trap = *RING_QUEUE_FRONT (uctx->uctx_eprocTrapQ, uctx->uctx_eprocTraps);
++
++      (void) RING_QUEUE_REMOVE (uctx->uctx_eprocTrapQ);
++
++      spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++
++      if ((res = resolve_eproc_trap (uctx, utrapp, &trap)) != UTS_FINISHED)
++          return (res);
++
++      if (fixup_eproc_trap (uctx, &trap, 0) == 0)
++      {
++          PRINTF (uctx, DBG_EPROC, "resolve_eproc_trap: could not fixup eproc trap - requeue it\n");
++
++          spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++          if (RING_QUEUE_REALLY_FULL(uctx->uctx_eprocTrapQ))
++              uctx->uctx_status |= UCTX_EPROC_QUEUE_OVERFLOW;
++          else
++          {
++              *RING_QUEUE_FRONT(uctx->uctx_eprocTrapQ, uctx->uctx_eprocTraps) = trap;
++          
++              (void) RING_QUEUE_ADD_FRONT(uctx->uctx_eprocTrapQ);
++          }
++          spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++
++          return UTS_RESCHEDULE;
++      }
++      
++      spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    }
++
++    if (uctx->uctx_status & UCTX_EPROC_QUEUE_FULL)
++      user_stop_stopping (uctx, UCTX_EPROC_QUEUE_FULL);
++
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++    return (UTS_FINISHED);
++}
++          
++static int
++resolve_dproc_traps (USER_CTXT *uctx, ELAN4_USER_TRAP *utrapp)
++{
++    unsigned long flags;
++    int res;
++    
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    while (! RING_QUEUE_EMPTY (uctx->uctx_dprocTrapQ))
++    {
++      ELAN4_DPROC_TRAP trap = *RING_QUEUE_FRONT(uctx->uctx_dprocTrapQ, uctx->uctx_dprocTraps);
++      
++      (void) RING_QUEUE_REMOVE (uctx->uctx_dprocTrapQ);
++
++      spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++
++      if ((res = resolve_dproc_trap (uctx, utrapp, &trap)) != UTS_FINISHED)
++          return (res);
++      
++      spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    }
++
++    if (uctx->uctx_status & UCTX_DPROC_QUEUE_FULL)
++      user_stop_stopping (uctx, UCTX_DPROC_QUEUE_FULL);
++    
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++    return (UTS_FINISHED);
++}
++
++static int
++resolve_tproc_traps (USER_CTXT *uctx, ELAN4_USER_TRAP *utrapp)
++{
++    unsigned long flags;
++    int res;
++    
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    while (! RING_QUEUE_EMPTY (uctx->uctx_tprocTrapQ))
++    {
++      ELAN4_TPROC_TRAP trap = *RING_QUEUE_FRONT(uctx->uctx_tprocTrapQ, uctx->uctx_tprocTraps);
++      
++      (void) RING_QUEUE_REMOVE (uctx->uctx_tprocTrapQ);
++
++      spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++
++      if ((res = resolve_tproc_trap (uctx, utrapp, &trap)) != UTS_FINISHED)
++          return (res);
++      
++      spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    }
++
++    if (uctx->uctx_status & UCTX_TPROC_QUEUE_FULL)
++      user_stop_stopping (uctx, UCTX_TPROC_QUEUE_FULL);
++
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++    return (UTS_FINISHED);
++}
++
++static int
++resolve_iproc_traps (USER_CTXT *uctx, ELAN4_USER_TRAP *utrapp)
++{
++    unsigned long flags;
++    int i, res;
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    for (i = 0; i < 2; i++)
++      if (uctx->uctx_iprocTrap[i].ut_state == UTS_IPROC_TRAPPED)
++      {
++          uctx->uctx_iprocTrap[i].ut_state = UTS_IPROC_RESOLVING;
++          spin_unlock_irqrestore(&uctx->uctx_spinlock, flags);
++          
++          if ((res = resolve_iproc_trap (uctx, utrapp, i)) != UTS_FINISHED)
++              return (res);
++          
++          spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++      }
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++    
++    return (UTS_FINISHED);
++}
++
++static int
++resolve_all_traps (USER_CTXT *uctx, ELAN4_USER_TRAP *utrapp)
++{
++    int res;
++
++    if ((res = resolve_iproc_traps (uctx, utrapp)) != UTS_FINISHED ||
++      (res = resolve_cproc_traps (uctx, utrapp)) != UTS_FINISHED ||
++      (res = resolve_eproc_traps (uctx, utrapp)) != UTS_FINISHED ||
++      (res = resolve_dproc_traps (uctx, utrapp)) != UTS_FINISHED ||
++      (res = resolve_tproc_traps (uctx, utrapp)) != UTS_FINISHED)
++      return (res);
++
++    if (uctx->uctx_status & UCTX_OVERFLOW_REASONS)
++      return (deliver_trap (utrapp, UTS_QUEUE_OVERFLOW, UTS_NOPROC, NULL, uctx->uctx_status));
++
++    if (uctx->uctx_status & UCTX_ERROR_REASONS)
++      return (deliver_trap (utrapp, UTS_QUEUE_ERROR, UTS_NOPROC, NULL, uctx->uctx_status));
++
++    return (UTS_FINISHED);
++}
++
++static int
++execute_iproc_traps (USER_CTXT *uctx, ELAN4_USER_TRAP *utrapp)
++{
++    unsigned long flags;
++    int i;
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    for (i = 0; i < 2; i++)
++      switch (uctx->uctx_iprocTrap[i].ut_state)
++      {
++      case UTS_IPROC_EXECUTE_PACKET:
++          uctx->uctx_iprocTrap[i].ut_state = UTS_IPROC_EXECUTING;
++          spin_unlock_irqrestore(&uctx->uctx_spinlock, flags);
++          
++          return (deliver_trap (utrapp, UTS_EXECUTE_PACKET, UTS_IPROC, &uctx->uctx_iprocTrap[i].ut_trap, i));
++
++      case UTS_IPROC_NETWORK_ERROR:
++          spin_unlock_irqrestore(&uctx->uctx_spinlock, flags);
++          
++          return (deliver_trap (utrapp, UTS_NETWORK_ERROR_TRAP, UTS_IPROC, &uctx->uctx_iprocTrap[i].ut_trap, i));
++      }
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++    
++    return (UTS_FINISHED);
++}
++
++static int
++progress_neterr (USER_CTXT *uctx, ELAN4_USER_TRAP *utrapp)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    if (! RING_QUEUE_EMPTY (uctx->uctx_msgQ))
++    {
++      ELAN4_NETERR_MSG msg = *RING_QUEUE_FRONT (uctx->uctx_msgQ, uctx->uctx_msgs);
++      
++      (void) RING_QUEUE_REMOVE (uctx->uctx_msgQ);
++      spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++      
++      return deliver_trap (utrapp, UTS_NETWORK_ERROR_MSG, UTS_NETERR_MSG, &msg, user_location2process (uctx, msg.msg_sender));
++    }
++    
++    if (uctx->uctx_status & UCTX_NETERR_TIMER)
++    {
++      uctx->uctx_status &= ~UCTX_NETERR_TIMER;
++
++      spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++      
++      return deliver_trap (utrapp, UTS_NETWORK_ERROR_TIMER, UTS_NOPROC, NULL);
++    }
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++    
++    return (UTS_FINISHED);
++}
++
++static void
++restart_command_queues (USER_CTXT *uctx)
++{
++    struct list_head *entry;
++
++    ASSERT (SPINLOCK_HELD (&uctx->uctx_spinlock));
++    
++    list_for_each (entry, &uctx->uctx_cqlist) {
++      USER_CQ *ucq = list_entry (entry, USER_CQ, ucq_link);
++      
++      if (ucq->ucq_state == UCQ_NEEDS_RESTART)
++      {
++          ucq->ucq_state = UCQ_RUNNING;
++          
++          elan4_restartcq (uctx->uctx_ctxt.ctxt_dev, ucq->ucq_cq);
++      }
++    }
++}
++
++static int
++restart_dmas (USER_CTXT *uctx)
++{
++    PRINTF (uctx, DBG_TRAP, "restart_dmas: back=%d front=%d\n", uctx->uctx_dmaQ.q_back, uctx->uctx_dmaQ.q_front);
++
++    while (! RING_QUEUE_EMPTY (uctx->uctx_dmaQ))
++    {
++      if (! user_ddcq_check (uctx, 7))
++          return (0);
++
++      user_ddcq_run_dma (uctx, RING_QUEUE_FRONT(uctx->uctx_dmaQ, uctx->uctx_dmas));
++      
++      (void) RING_QUEUE_REMOVE (uctx->uctx_dmaQ);
++    }
++
++    return (1);
++}
++
++static int
++restart_threads (USER_CTXT *uctx)
++{
++    PRINTF (uctx, DBG_TRAP, "restart_threads: back=%d front=%d\n", uctx->uctx_threadQ.q_back, uctx->uctx_threadQ.q_front);
++
++    while (! RING_QUEUE_EMPTY (uctx->uctx_threadQ))
++    {
++      if (! user_ddcq_check (uctx, 7))
++          return (0);
++
++      user_ddcq_run_thread (uctx, RING_QUEUE_FRONT(uctx->uctx_threadQ, uctx->uctx_threads));
++      
++      (void) RING_QUEUE_REMOVE (uctx->uctx_threadQ);
++    }
++
++    return (1);
++}
++
++int
++user_resume_eproc_trap (USER_CTXT *uctx, E4_Addr addr)
++{
++    PRINTF2 (uctx, DBG_RESUME, "user_resume_eproc_trap: addr=%llx -> %s\n", addr, user_ddcq_check(uctx, 2) ? "success" : "EAGAIN");
++
++    if (! user_ddcq_check (uctx, 2))
++      return (-EAGAIN);
++
++    user_ddcq_setevent (uctx, addr);
++
++    return (0);
++}
++
++int
++user_resume_cproc_trap (USER_CTXT *uctx, unsigned indx)
++{
++    struct list_head *entry;
++    unsigned long flags;
++
++    PRINTF1 (uctx, DBG_RESUME, "user_resume_cproc_trap: indx=%d\n", indx);
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++
++    list_for_each (entry, &uctx->uctx_cqlist) {
++      USER_CQ *ucq = list_entry (entry, USER_CQ, ucq_link);
++      
++      if (elan4_cq2idx(ucq->ucq_cq) == indx && ucq->ucq_state == UCQ_STOPPED && !(ucq->ucq_flags & UCQ_SYSTEM))
++      {
++          ucq->ucq_state = UCQ_NEEDS_RESTART;
++          
++          user_signal_trap (uctx);
++
++          spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++          return (0);
++      }
++    }
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++
++    return (-EINVAL);
++}
++
++int
++user_resume_dproc_trap (USER_CTXT *uctx, E4_DMA *dma)
++{
++    unsigned long flags;
++    int res = 0;
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    if (RING_QUEUE_FULL (uctx->uctx_dmaQ))
++      res = -ENOMEM;
++    else
++    {
++      *RING_QUEUE_BACK (uctx->uctx_dmaQ, uctx->uctx_dmas) = *dma;
++      (void) RING_QUEUE_ADD (uctx->uctx_dmaQ);
++
++      user_signal_trap (uctx);
++    }
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++    return (res);
++}
++
++int
++user_resume_tproc_trap (USER_CTXT *uctx, E4_ThreadRegs *regs)
++{
++    unsigned long flags;
++    int res = 0;
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    if (RING_QUEUE_FULL (uctx->uctx_threadQ))
++      res = -ENOMEM;
++    else
++    {
++      *RING_QUEUE_BACK (uctx->uctx_threadQ, uctx->uctx_threads) = *regs;
++      (void) RING_QUEUE_ADD (uctx->uctx_threadQ);
++
++      user_signal_trap (uctx);
++    }
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++    return (res);
++}
++
++int
++user_resume_iproc_trap (USER_CTXT *uctx, unsigned channel, unsigned trans,
++                      E4_IprocTrapHeader *hdrp, E4_IprocTrapData *datap)
++{
++    unsigned long flags;
++    int res = 0;
++
++    if (channel >= 2)
++      return (-EINVAL);
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    if (uctx->uctx_iprocTrap[channel].ut_state != UTS_IPROC_STOPPED &&
++      uctx->uctx_iprocTrap[channel].ut_state != UTS_IPROC_EXECUTING &&
++      uctx->uctx_iprocTrap[channel].ut_state != UTS_IPROC_NETWORK_ERROR)
++      res = -EINVAL;
++    else
++    {
++      ELAN4_IPROC_TRAP *trap = &uctx->uctx_iprocTrap[channel].ut_trap;
++
++      if (trans < trap->tr_numTransactions)
++      {
++          PRINTF1 (uctx, DBG_RESUME, "user_resume_iproc_trap: trans=%d -> execute\n", trans);
++
++          uctx->uctx_iprocTrap[channel].ut_state = UTS_IPROC_EXECUTE_PACKET;
++          trap->tr_trappedTrans                  = trans;
++          trap->tr_transactions[trans]           = *hdrp;
++          trap->tr_dataBuffers[trans]            = *datap;
++      }
++      else
++      {
++          PRINTF1 (uctx, DBG_RESUME, "user_resume_iproc_trap: trans=%d -> running\n", trans);
++
++          uctx->uctx_iprocTrap[channel].ut_state = UTS_IPROC_RUNNING;
++      
++          user_stop_nacking (uctx, channel ? UCTX_IPROC_CH0_TRAPPED : UCTX_IPROC_CH1_TRAPPED);
++      }
++    }
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++    
++    return (res);
++}
++
++int
++__categorise_command (E4_uint64 command, int *cmdSize)
++{
++    switch (command & 0x3)
++    {
++    case RUN_THREAD_CMD: *cmdSize = 7; break;
++      
++    default:
++      switch (command & 0x7)
++      {
++      case WRITE_DWORD_CMD: *cmdSize = 2; break;
++      case ADD_DWORD_CMD:   *cmdSize = 2; break;
++          
++      default:
++          switch (command & 0xF)
++          {
++          case OPEN_STEN_PKT_CMD:
++              *cmdSize = 1;
++              return 1;
++              
++          case COPY64_CMD:    *cmdSize = 2; break;
++          case GUARD_CMD:     *cmdSize = 1; break;
++          case INTERRUPT_CMD: *cmdSize = 1; break;
++          case RUN_DMA_CMD:   *cmdSize = 7; break;
++              
++          default:
++              switch (command & 0x1f)
++              {
++              case SEND_TRANS_CMD:
++                  *cmdSize = 2 + (((command >> 16) & TR_SIZE_MASK) >> TR_SIZE_SHIFT);
++                  return 2;
++                  
++              case SET_EVENT_CMD:    *cmdSize = 1; break;
++              case SET_EVENTN_CMD:   *cmdSize = 2; break;
++              case WAIT_EVENT_CMD:   *cmdSize = 4; break;
++
++              default:
++                  switch (command & 0x3f)
++                  {
++                  case NOP_CMD:            *cmdSize = 1; break;
++                  case MAKE_EXT_CLEAN_CMD: *cmdSize = 1; break;
++                  default:
++                      return 3;
++                  }
++                  break;
++              }
++          }
++      }
++    }
++
++    return 0;
++}
++
++int
++__whole_command (sdramaddr_t *commandPtr, sdramaddr_t insertPtr, unsigned int cqSize, unsigned int cmdSize)
++{
++    /* Move onto next command */
++    while (cmdSize-- && (*commandPtr) != insertPtr)
++      *commandPtr = ((*commandPtr) & ~(cqSize-1)) | (((*commandPtr) + sizeof (E4_uint64)) & (cqSize-1));
++
++    return cmdSize == -1;
++}
++
++int
++user_neterr_sten (USER_CTXT *uctx, unsigned int vp, E4_uint64 cookie, int waitforeop)
++{
++    ELAN4_DEV        *dev   = uctx->uctx_ctxt.ctxt_dev;
++    int                     found = 0;
++    struct list_head *el;
++
++    user_swapout (uctx, UCTX_NETERR_FIXUP);
++
++    kmutex_lock (&uctx->uctx_cqlock);
++    list_for_each (el, &uctx->uctx_cqlist) {
++      USER_CQ *ucq = list_entry (el, USER_CQ, ucq_link);
++      
++      if ((ucq->ucq_cq->cq_perm & CQ_STENEnableBit) != 0)
++      {
++          sdramaddr_t   cqdesc       = dev->dev_cqaddr + (elan4_cq2num(ucq->ucq_cq) * sizeof (E4_CommandQueueDesc));
++          E4_uint64     queuePtrs    = elan4_sdram_readq (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_QueuePtrs));
++          sdramaddr_t   insertPtr    = (queuePtrs & CQ_PtrMask);
++          sdramaddr_t   commandPtr   = CQ_CompletedPtr (queuePtrs);
++          unsigned int  cqSize       = CQ_Size ((queuePtrs >> CQ_SizeShift) & CQ_SizeMask);
++          E4_uint64     openCommand  = 0;
++
++          if (dev->dev_devinfo.dev_revision_id != PCI_REVISION_ID_ELAN4_REVA && (queuePtrs & CQ_RevB_ReorderingQueue))
++          {
++              E4_uint32 oooMask = elan4_sdram_readl (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_HoldingValue));
++              
++              for (; (oooMask & 1) != 0; oooMask >>= 1)
++                  insertPtr = (insertPtr & ~(cqSize-1)) | ((insertPtr + sizeof (E4_uint64)) & (cqSize-1));
++          }
++
++          while (commandPtr != insertPtr)
++          {
++              E4_uint64    command = elan4_sdram_readq (dev, commandPtr);
++              sdramaddr_t  identifyPtr;
++              unsigned int cmdSize;
++              
++              switch (__categorise_command (command, &cmdSize))
++              {
++              case 0:
++                  (void) __whole_command (&commandPtr, insertPtr, cqSize, cmdSize);
++                  break;
++                  
++              case 1:
++                  PRINTF (uctx, DBG_NETERR, "user_neterr_sten: cq=%d OPEN %llx\n", elan4_cq2num (ucq->ucq_cq), command);
++                  
++                  if ((command >> 32) == vp)
++                      openCommand = command;
++
++                  (void) __whole_command (&commandPtr, insertPtr, cqSize, cmdSize);
++                  break;
++                  
++              case 2:
++                  PRINTF (uctx, DBG_NETERR, "user_neterr_sten: cq=%d SENDTRANS %llx\n", elan4_cq2num (ucq->ucq_cq), command);
++                  
++                  if (openCommand == 0)
++                      (void) __whole_command (&commandPtr, insertPtr, cqSize, cmdSize);
++                  else
++                  {
++                      switch ((command >> 16) & (TR_OPCODE_MASK | TR_SIZE_MASK))
++                      {
++                      case TR_IDENTIFY  & (TR_OPCODE_MASK | TR_SIZE_MASK):
++                      case TR_REMOTEDMA & (TR_OPCODE_MASK | TR_SIZE_MASK):
++                          PRINTF (uctx, DBG_NETERR, "user_neterr_sten: TR_IDENTIFY/TR_REMOTEDMA\n");
++                          identifyPtr = (commandPtr & ~(cqSize-1)) | ((commandPtr + sizeof (E4_uint64)) & (cqSize-1));
++                          break;
++                          
++                      case TR_SETEVENT_IDENTIFY & (TR_OPCODE_MASK | TR_SIZE_MASK):
++                      case TR_INPUT_Q_COMMIT    & (TR_OPCODE_MASK | TR_SIZE_MASK):
++                          PRINTF (uctx, DBG_NETERR, "user_neterr_sten: TR_SETEVENT_IDENTIFY/TR_INPUT_Q_COMMIT\n");
++                          identifyPtr = (commandPtr & ~(cqSize-1)) | ((commandPtr + 2*sizeof (E4_uint64)) & (cqSize-1));
++                          break;
++                          
++                      case TR_ADDWORD & (TR_OPCODE_MASK | TR_SIZE_MASK):
++                          PRINTF (uctx, DBG_NETERR, "user_neterr_sten: TR_ADDWORD\n");
++                          identifyPtr = (commandPtr & ~(cqSize-1)) | ((commandPtr + 3*sizeof (E4_uint64)) & (cqSize-1));
++                          break;
++                          
++                      case TR_TESTANDWRITE & (TR_OPCODE_MASK | TR_SIZE_MASK):
++                          PRINTF (uctx, DBG_NETERR, "user_neterr_sten: TR_TESTANDWRITE\n");
++                          identifyPtr = (commandPtr & ~(cqSize-1)) | ((commandPtr + 4*sizeof (E4_uint64)) & (cqSize-1));
++                          break;
++                          
++                      default:
++                          identifyPtr = 0;
++                      }
++                      
++                      if (! __whole_command (&commandPtr, insertPtr, cqSize, cmdSize))
++                      {
++                          PRINTF (uctx, DBG_NETERR, "user_neterr_sten: not whole command\n");
++                          openCommand = 0;
++                      }
++
++                      else if (identifyPtr)
++                      {
++                          E4_uint64 tcookie = elan4_sdram_readq (dev, identifyPtr);
++                          
++                          PRINTF (uctx, DBG_NETERR, "user_neterr_sten: cookie=%llx [%llx]\n", tcookie, cookie);
++                          
++                          if (tcookie == cookie)
++                          {
++                              unsigned int vchan = (openCommand >> 4) & 0x1f;
++                              
++                              PRINTF (uctx, DBG_NETERR, "user_neterr_sten: cookie matches - vchan=%d\n", vchan);
++                              
++                              if (! waitforeop)
++                              {
++                                  /* Alter the CQ_AckBuffer for this channel to indicate an
++                                   * ack was received */
++                                  E4_uint64 value  = elan4_sdram_readq (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_AckBuffers));
++                                  E4_uint64 nvalue = ((value & ~((E4_uint64)0xf << ((vchan & 0xf) << 2))) |
++                                                      ((E4_uint64) PackOk << ((vchan & 0xf) << 2)));
++                                  
++                                  PRINTF (uctx, DBG_NETERR, "user_neterr_sten: CQ_AckBuffers %llx -> %llx\n", value, nvalue);
++                                  
++                                  elan4_sdram_writeq (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_AckBuffers), nvalue);
++                                  pioflush_sdram (dev);
++                              }
++                              
++                              found++;
++                          }
++                          openCommand = 0;
++                      }
++                      
++                      if ((command >> 16) & TR_LAST_AND_SEND_ACK)
++                          openCommand = 0;
++                  }
++                  break;
++                  
++              case 3:
++                  PRINTF (uctx, DBG_NETERR, "user_neterr_sten: invalid command %llx\n", command);
++                  kmutex_unlock (&uctx->uctx_cqlock);
++                  return -EINVAL;
++              }
++              
++          }
++      }
++    }
++    kmutex_unlock (&uctx->uctx_cqlock);
++
++    user_swapin (uctx, UCTX_NETERR_FIXUP);
++
++    return found;
++}
++
++int
++user_neterr_dma (USER_CTXT *uctx, unsigned int vp, E4_uint64 cookie, int waitforeop)
++{
++    unsigned long flags;
++    int found = 0;
++    int idx;
++
++    user_swapout (uctx, UCTX_NETERR_FIXUP);
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    RING_QUEUE_ITERATE (uctx->uctx_dmaQ, idx) {
++      E4_DMA *dma = &uctx->uctx_dmas[idx];
++
++      if (dma->dma_vproc == vp && dma->dma_cookie == cookie)
++      {
++          PRINTF (uctx, DBG_NETERR, "user_neterr_dma: dmaQ matches %s\n", waitforeop ? "waitforeop" : "remove remoteness");
++
++          if (! waitforeop) 
++          {
++              dma->dma_dstEvent = 0;
++              dma->dma_typeSize = DMA_ShMemWrite | DMA_Context (dma->dma_typeSize);
++          }
++          found++;
++      }
++    }
++
++    RING_QUEUE_ITERATE (uctx->uctx_dprocTrapQ, idx) {
++      ELAN4_DPROC_TRAP *trap = &uctx->uctx_dprocTraps[idx];
++
++      if (trap->tr_desc.dma_vproc == vp && trap->tr_desc.dma_cookie == cookie)
++      {
++          PRINTF (uctx, DBG_NETERR, "user_neterr_dma: dmaTrapQ matches %s\n", waitforeop ? "waitforeop" : "remove remoteness");
++
++          if (! waitforeop) 
++          {
++              trap->tr_desc.dma_dstEvent = 0;
++              trap->tr_desc.dma_typeSize = DMA_ShMemWrite | DMA_Context (trap->tr_desc.dma_typeSize);
++          }
++          found++;
++      }
++    }
++
++    /* The device driver command queue should be empty at this point ! */
++    if (user_ddcq_flush (uctx) == 0)
++      found = -EAGAIN;
++
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++
++    /* The device driver command queue should be empty at this point ! */
++    if (user_ddcq_flush (uctx) == 0)
++      found = -EAGAIN;
++    
++    user_swapin (uctx, UCTX_NETERR_FIXUP);
++
++    return found;
++}
++
++int
++user_trap_handler (USER_CTXT *uctx, ELAN4_USER_TRAP *utrapp, int nticks)
++{
++    unsigned long entered = jiffies;
++    unsigned int  need_reenter = 0;
++    unsigned long flags;
++    int           res;
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++
++    PRINTF1 (uctx, DBG_TRAP, "user_trap_handler: entered state=%d\n", uctx->uctx_trap_state);
++
++    uctx->uctx_trap_count++;
++    
++    for (;;)
++    {
++      if (uctx->uctx_status & UCTX_SWAPPED_REASONS)
++      {
++          PRINTF0 (uctx, DBG_TRAP, "user_trap_handler: exiting on swapped reasons\n");
++          
++          res = UTS_FINISHED;
++          goto no_more_to_do;
++      }
++
++      if ((long) (jiffies - entered) > HZ)
++      {
++          PRINTF0 (uctx, DBG_TRAP, "user_trap_handler: exiting for reschedule\n");
++          res = UTS_RESCHEDULE;
++          goto no_more_to_do;
++      }
++      
++      switch (uctx->uctx_trap_state)
++      {
++      case UCTX_TRAP_ACTIVE:
++          uctx->uctx_trap_state = UCTX_TRAP_SLEEPING;
++          
++          if (nticks == 0 || need_reenter || kcondvar_timedwaitsig (&uctx->uctx_wait, &uctx->uctx_spinlock, &flags, lbolt + nticks) != CV_RET_NORMAL)
++          {
++              PRINTF0 (uctx, DBG_TRAP, "user_trap_handler: exiting by kcondvar_timedwaitsig\n");
++
++              res = UTS_FINISHED;
++              goto no_more_to_do;
++          }
++
++          /* Have slept above, so resample entered */
++          entered = jiffies;
++          
++          uctx->uctx_trap_state = UCTX_TRAP_SIGNALLED;
++          continue;
++
++      case UCTX_TRAP_IDLE:
++      case UCTX_TRAP_SIGNALLED:
++          uctx->uctx_trap_state = UCTX_TRAP_ACTIVE;
++          break;
++      }
++      spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++
++      PRINTF2 (uctx, DBG_TRAP, "user_trap_handler: resolve traps - state=%d status=%x\n", uctx->uctx_trap_state, uctx->uctx_status);
++
++      switch ((res = resolve_all_traps (uctx, utrapp)))
++      {
++      case UTS_FINISHED:
++          break;
++          
++      case UTS_RESCHEDULE:
++          need_reenter++;
++          break;
++
++      default:
++          spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++          goto no_more_to_do;
++      }
++
++      spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++      if (! user_ddcq_flush (uctx))
++      {
++          PRINTF0 (uctx, DBG_TRAP, "user_trap_handler: ddcq not flushed - re-enter\n");
++          need_reenter++;
++          continue;
++      }
++      spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++
++      if ((res = progress_neterr (uctx, utrapp)) != UTS_FINISHED)
++      {
++          spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++          goto no_more_to_do;
++      }
++
++      if ((res = execute_iproc_traps (uctx, utrapp)) != UTS_FINISHED)
++      {
++          spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++          goto no_more_to_do;
++      }
++
++      PRINTF2 (uctx, DBG_TRAP, "user_trap_handler: restart items - state=%d status=%x\n", uctx->uctx_trap_state, uctx->uctx_status);
++
++      spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++      if (UCTX_RUNNABLE (uctx))
++      {
++          restart_command_queues (uctx);
++
++          if (! restart_threads (uctx) || ! restart_dmas (uctx))
++          {
++              PRINTF0 (uctx, DBG_TRAP, "user_trap_handler: ddcq full - re-enter\n");
++              need_reenter++;
++          }
++      }
++    }
++ no_more_to_do:
++    uctx->uctx_trap_state = UCTX_TRAP_IDLE;
++
++    /*
++     * Always ensure that the command queue is flushed with a flow control
++     * write, so that on the next trap we (hopefully) find it empty and so
++     * can immediately restart the context.   Also if we need to be re-enter
++     * the trap handler and don't have an interrupt outstanding, then issue
++     * one now.
++     */
++    user_ddcq_flush (uctx);
++    if (need_reenter && uctx->uctx_ddcq_intr == 0)
++    {
++      uctx->uctx_ddcq_intr++;
++      user_ddcq_intr (uctx);
++    }
++
++    if (--uctx->uctx_trap_count == 0 && (uctx->uctx_status & UCTX_SWAPPING))
++      kcondvar_wakeupall (&uctx->uctx_wait, &uctx->uctx_spinlock);
++
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++
++    PRINTF2 (uctx, DBG_TRAP, "user_trap_handler: finished state=%d res=%d\n", uctx->uctx_trap_state, res);
++
++    return (res == UTS_EFAULT ? -EFAULT : 0);
++}
++
++USER_CQ *
++user_alloccq (USER_CTXT *uctx, unsigned cqsize, unsigned perm, unsigned uflags)
++{
++    USER_CQ      *ucq;
++    unsigned long flags;
++
++    KMEM_ZALLOC (ucq, USER_CQ *, sizeof (USER_CQ), 1);
++
++    if (ucq == (USER_CQ *) NULL)
++      return ERR_PTR(-ENOMEM);
++    
++    /* NOTE - do not allow the user to create high-priority queues as we only flush through the low-priority run queues */
++    if ((ucq->ucq_cq = elan4_alloccq (&uctx->uctx_ctxt, cqsize, perm, (uflags & UCQ_REORDER) ? CQ_Reorder : 0)) == NULL)
++    {
++      KMEM_FREE (ucq, sizeof (USER_CQ));
++      
++      PRINTF2 (uctx, DBG_CQ, "user_alloccq: failed elan4_allocq cqsize %d uflags %x\n", cqsize, uflags);
++
++      return ERR_PTR(-ENOMEM);
++    }
++    
++    atomic_set (&ucq->ucq_ref, 1);
++
++    ucq->ucq_state = UCQ_RUNNING;
++    ucq->ucq_flags = uflags;
++    
++    PRINTF3 (uctx, DBG_CQ, "user_alloccq: ucq=%p idx=%d cqnum=%d\n", ucq, elan4_cq2idx (ucq->ucq_cq), elan4_cq2num(ucq->ucq_cq));
++
++    /* chain it onto the context */
++    kmutex_lock (&uctx->uctx_cqlock);
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    list_add (&ucq->ucq_link, &uctx->uctx_cqlist);
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++    kmutex_unlock (&uctx->uctx_cqlock);
++
++    return (ucq);
++}
++
++USER_CQ *
++user_findcq (USER_CTXT *uctx, unsigned idx)
++{
++    struct list_head *entry;
++    unsigned long flags;
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    list_for_each (entry, &uctx->uctx_cqlist) {
++      USER_CQ *ucq = list_entry (entry, USER_CQ, ucq_link);
++
++      if (elan4_cq2idx(ucq->ucq_cq) == idx)
++      {
++          atomic_inc (&ucq->ucq_ref);
++          spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++          return (ucq);
++      }
++    }
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++
++    return (NULL);
++}
++
++void
++user_dropcq (USER_CTXT *uctx, USER_CQ *ucq)
++{
++    unsigned long flags;
++
++    PRINTF2 (uctx, DBG_CQ, "user_dropcq: ucq=%p ref=%d\n", ucq, atomic_read (&ucq->ucq_ref));
++
++    kmutex_lock (&uctx->uctx_cqlock);
++    if (! atomic_dec_and_test (&ucq->ucq_ref))
++    {
++      kmutex_unlock (&uctx->uctx_cqlock);
++      return;
++    }
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    list_del (&ucq->ucq_link);
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++
++    kmutex_unlock (&uctx->uctx_cqlock);
++
++    elan4_freecq (&uctx->uctx_ctxt, ucq->ucq_cq);
++
++    KMEM_FREE (ucq, sizeof (USER_CQ));
++}
++
++int
++user_alloc_trap_queues (USER_CTXT *uctx, unsigned ndproc_traps, unsigned neproc_traps, 
++                      unsigned ntproc_traps, unsigned nthreads, unsigned ndmas)
++{
++    ELAN4_DPROC_TRAP *dprocs;
++    ELAN4_EPROC_TRAP *eprocs;
++    ELAN4_TPROC_TRAP *tprocs;
++    E4_DMA           *dmas;
++    E4_ThreadRegs    *threads;
++    ELAN4_NETERR_MSG *msgs;
++    unsigned long     flags;
++
++    int nmsgs = NETERR_MSGS;
++
++    /* bounds check the values that have been passed in */
++    if (ndproc_traps < 2 || ndproc_traps > 10000 ||
++      ntproc_traps < 1 || ntproc_traps > 100   ||
++      neproc_traps < 6 || neproc_traps > 10000 ||
++      nthreads     < 2 || nthreads     > 10000 ||
++      ndmas        < 2 || ndmas        > 10000)
++      return -EINVAL;
++
++    if (uctx->uctx_dmas != NULL)
++      return -EBUSY;
++
++    KMEM_ZALLOC (dprocs, ELAN4_DPROC_TRAP *, ndproc_traps * sizeof (ELAN4_DPROC_TRAP), 1);
++    KMEM_ZALLOC (eprocs, ELAN4_EPROC_TRAP *, neproc_traps * sizeof (ELAN4_EPROC_TRAP), 1);
++    KMEM_ZALLOC (tprocs, ELAN4_TPROC_TRAP *, ntproc_traps * sizeof (ELAN4_TPROC_TRAP), 1);
++    KMEM_ZALLOC (threads, E4_ThreadRegs *, nthreads * sizeof (E4_ThreadRegs), 1);
++    KMEM_ZALLOC (dmas, E4_DMA *, ndmas * sizeof (E4_DMA), 1);
++    KMEM_ZALLOC (msgs, ELAN4_NETERR_MSG *, nmsgs * sizeof (ELAN4_NETERR_MSG), 1);
++
++    if (dprocs == NULL || eprocs == NULL || tprocs == NULL || dmas == NULL || threads == NULL || msgs == NULL)
++    {
++      if (dprocs != NULL) KMEM_FREE (dprocs, ndproc_traps * sizeof (ELAN4_DPROC_TRAP));
++      if (eprocs != NULL) KMEM_FREE (eprocs, neproc_traps * sizeof (ELAN4_EPROC_TRAP));
++      if (tprocs != NULL) KMEM_FREE (tprocs, ntproc_traps * sizeof (ELAN4_TPROC_TRAP));
++      if (threads != NULL) KMEM_FREE (threads, nthreads * sizeof (E4_ThreadRegs));
++      if (dmas != NULL) KMEM_FREE (dmas, ndmas * sizeof (E4_DMA));
++      if (msgs != NULL) KMEM_FREE (msgs, nmsgs * sizeof (ELAN4_NETERR_MSG));
++      
++      return -ENOMEM;
++    }
++    
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++
++    uctx->uctx_dprocTraps = dprocs;
++    uctx->uctx_eprocTraps = eprocs;
++    uctx->uctx_tprocTraps = tprocs;
++    uctx->uctx_threads    = threads;
++    uctx->uctx_dmas       = dmas;
++    uctx->uctx_msgs       = msgs;
++
++    RING_QUEUE_INIT (uctx->uctx_dprocTrapQ, ndproc_traps, 1 /* 1 for 2nd dma */);
++    RING_QUEUE_INIT (uctx->uctx_tprocTrapQ, ntproc_traps, 0);
++    RING_QUEUE_INIT (uctx->uctx_eprocTrapQ, neproc_traps, 5 /* 1 for command, 2 for dma, 2 for inputter */);
++    RING_QUEUE_INIT (uctx->uctx_threadQ,    nthreads,     1);
++    RING_QUEUE_INIT (uctx->uctx_dmaQ,       ndmas,        1);
++    RING_QUEUE_INIT (uctx->uctx_msgQ,       nmsgs,        0);
++
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++    
++    return 0;
++}
++
++USER_CTXT *
++user_alloc (ELAN4_DEV *dev)
++{
++    USER_CTXT *uctx;
++    int res;
++    int i;
++
++    /* Allocate and initialise the context private data */
++    KMEM_ZALLOC (uctx, USER_CTXT *, sizeof  (USER_CTXT), 1);
++
++    if (uctx == NULL)
++      return ERR_PTR(-ENOMEM);
++
++    if (elan4_get_position (dev, &uctx->uctx_position) == ELAN_POS_UNKNOWN)
++    {
++      KMEM_FREE (uctx, sizeof (USER_CTXT));
++      return ERR_PTR(-EAGAIN);
++    }
++    
++    if ((res = elan4_insertctxt (dev, &uctx->uctx_ctxt, &user_trap_ops)) != 0)
++    {
++      KMEM_FREE (uctx, sizeof (USER_CTXT));
++      return ERR_PTR(res);
++    }
++
++    KMEM_GETPAGES (uctx->uctx_upage, ELAN4_USER_PAGE *, btopr (sizeof (ELAN4_USER_PAGE)), 1);
++    if (uctx->uctx_upage == NULL)
++    {
++      elan4_removectxt (dev, &uctx->uctx_ctxt);
++      KMEM_FREE (uctx, sizeof (USER_CTXT));
++      return ERR_PTR(-ENOMEM);
++    }
++    
++    if ((uctx->uctx_trampoline = elan4_sdram_alloc (dev, SDRAM_PAGE_SIZE)) == 0)
++    {
++      KMEM_FREEPAGES (uctx->uctx_upage, btopr (sizeof (ELAN4_USER_PAGE)));
++      elan4_removectxt (dev, &uctx->uctx_ctxt);
++
++      KMEM_FREE (uctx, sizeof (USER_CTXT));
++      return ERR_PTR(-ENOMEM);
++    }
++    
++    if ((uctx->uctx_routetable = elan4_alloc_routetable (dev, 4 /* 512 << 4 == 8192 entries */)) == NULL)
++    {
++      elan4_sdram_free (dev, uctx->uctx_trampoline, SDRAM_PAGE_SIZE);
++      KMEM_FREEPAGES (uctx->uctx_upage, btopr (sizeof (ELAN4_USER_PAGE)));
++      elan4_removectxt (dev, &uctx->uctx_ctxt);
++
++      KMEM_FREE (uctx, sizeof (USER_CTXT));
++      return ERR_PTR(-ENOMEM);
++    }
++
++    elan4_set_routetable (&uctx->uctx_ctxt, uctx->uctx_routetable);
++
++    /* initialise the trap and swap queues to be really full */
++    RING_QUEUE_INIT (uctx->uctx_dprocTrapQ, 0, 1);
++    RING_QUEUE_INIT (uctx->uctx_tprocTrapQ, 0, 1);
++    RING_QUEUE_INIT (uctx->uctx_eprocTrapQ, 0, 1);
++    RING_QUEUE_INIT (uctx->uctx_threadQ, 0, 1);
++    RING_QUEUE_INIT (uctx->uctx_dmaQ, 0, 1);
++
++    INIT_LIST_HEAD (&uctx->uctx_cent_list);
++    INIT_LIST_HEAD (&uctx->uctx_vpseg_list);
++    INIT_LIST_HEAD (&uctx->uctx_cqlist);
++
++    uctx->uctx_haltop.op_function = user_flush;
++    uctx->uctx_haltop.op_arg      = uctx;
++    uctx->uctx_haltop.op_mask     = INT_Halted|INT_Discarding;
++
++    uctx->uctx_dma_flushop.op_function = user_flush_dmas;
++    uctx->uctx_dma_flushop.op_arg      = uctx;
++
++    kmutex_init (&uctx->uctx_vpseg_lock);
++    kmutex_init (&uctx->uctx_cqlock);
++    kmutex_init (&uctx->uctx_rgnmutex);
++
++    spin_lock_init (&uctx->uctx_spinlock);
++    spin_lock_init (&uctx->uctx_rgnlock);
++    spin_lock_init (&uctx->uctx_fault_lock);
++
++    kcondvar_init (&uctx->uctx_wait);
++
++    if ((uctx->uctx_ddcq = user_alloccq (uctx, CQ_Size1K, CQ_EnableAllBits, UCQ_SYSTEM)) == NULL)
++    {
++      user_free (uctx);
++      return ERR_PTR(-ENOMEM);
++    }
++
++    uctx->uctx_trap_count = 0;
++    uctx->uctx_trap_state = UCTX_TRAP_IDLE;
++    uctx->uctx_status     = 0 /* UCTX_DETACHED | UCTX_SWAPPED | UCTX_STOPPED */;
++
++    init_timer (&uctx->uctx_int_timer);
++
++    uctx->uctx_int_timer.function = user_signal_timer;
++    uctx->uctx_int_timer.data     = (unsigned long) uctx;
++    uctx->uctx_int_start          = jiffies;
++    uctx->uctx_int_count          = 0;
++    uctx->uctx_int_delay          = 0;
++
++    init_timer (&uctx->uctx_neterr_timer);
++    uctx->uctx_neterr_timer.function = user_neterr_timer;
++    uctx->uctx_neterr_timer.data     = (unsigned long) uctx;
++
++    uctx->uctx_upage->upage_ddcq_completed = 0;
++    uctx->uctx_ddcq_completed              = 0;
++    uctx->uctx_ddcq_insertcnt              = 0;
++
++    uctx->uctx_num_fault_save = num_fault_save;
++    if (uctx->uctx_num_fault_save) 
++    {  
++      KMEM_ZALLOC (uctx->uctx_faults, FAULT_SAVE *, (sizeof(FAULT_SAVE) * uctx->uctx_num_fault_save), 1);
++        if ( uctx->uctx_faults == NULL) 
++      {
++          user_free (uctx);
++          return ERR_PTR(-ENOMEM);
++        }
++    
++        for (i = 0; i < uctx->uctx_num_fault_save; i++)
++          uctx->uctx_faults[i].next = (i == (uctx->uctx_num_fault_save-1) ? NULL : &uctx->uctx_faults[i+1]);
++
++    }
++    uctx->uctx_fault_list = uctx->uctx_faults;
++
++    return (uctx);
++}
++
++void
++user_free (USER_CTXT *uctx)
++{
++    ELAN4_DEV *dev = uctx->uctx_ctxt.ctxt_dev;
++
++    user_swapout (uctx, UCTX_EXITING);
++
++    /* Detach from all input contexts */
++    user_detach (uctx, NULL);
++
++    /* since we're single threaded here - (called from close()) */
++    /* we don't need to hold the lock to drop the command queues */
++    /* since they cannot be mapped into user space */
++    while (! list_empty (&uctx->uctx_cqlist))
++      user_dropcq (uctx, list_entry (uctx->uctx_cqlist.next, USER_CQ, ucq_link));
++
++    /* Free off all of vpseg_list */
++    kmutex_lock (&uctx->uctx_vpseg_lock);
++    while (! list_empty (&uctx->uctx_vpseg_list))
++      user_remove_vpseg(uctx, list_entry (uctx->uctx_vpseg_list.next, USER_VPSEG, vps_link));
++    kmutex_unlock (&uctx->uctx_vpseg_lock);
++    
++    if (timer_pending (&uctx->uctx_int_timer))
++      del_timer_sync (&uctx->uctx_int_timer);
++    
++    if (timer_pending (&uctx->uctx_neterr_timer))
++      del_timer_sync (&uctx->uctx_neterr_timer);
++
++    if (uctx->uctx_dprocTraps)
++      KMEM_FREE (uctx->uctx_dprocTraps, uctx->uctx_dprocTrapQ.q_size * sizeof (ELAN4_DPROC_TRAP));
++    if (uctx->uctx_tprocTraps)
++      KMEM_FREE (uctx->uctx_tprocTraps, uctx->uctx_tprocTrapQ.q_size * sizeof (ELAN4_TPROC_TRAP));
++    if (uctx->uctx_eprocTraps)
++      KMEM_FREE (uctx->uctx_eprocTraps, uctx->uctx_eprocTrapQ.q_size * sizeof (ELAN4_EPROC_TRAP));
++    if (uctx->uctx_dmas)
++      KMEM_FREE (uctx->uctx_dmas, uctx->uctx_dmaQ.q_size * sizeof (E4_DMA));
++    if (uctx->uctx_msgs)
++      KMEM_FREE (uctx->uctx_msgs, NETERR_MSGS * sizeof (ELAN4_NETERR_MSG));
++    if (uctx->uctx_threads)
++      KMEM_FREE (uctx->uctx_threads, uctx->uctx_threadQ.q_size * sizeof (E4_ThreadRegs));
++    if (uctx->uctx_faults)
++      KMEM_FREE (uctx->uctx_faults, (sizeof(FAULT_SAVE) * uctx->uctx_num_fault_save));
++
++    if (uctx->uctx_intcookie_table)
++      intcookie_free_table (uctx->uctx_intcookie_table);
++
++    elan4_set_routetable (&uctx->uctx_ctxt, NULL);
++    elan4_free_routetable (dev, uctx->uctx_routetable);
++
++    /* Free off all USER_RGNs */
++    user_freergns(uctx);
++
++    elan4_sdram_free (dev, uctx->uctx_trampoline, SDRAM_PAGE_SIZE);
++
++    /* Clear the PG_Reserved bit before free to avoid a memory leak */
++    ClearPageReserved(pte_page(*find_pte_kernel((unsigned long) uctx->uctx_upage)));
++    KMEM_FREEPAGES (uctx->uctx_upage, btopr (sizeof (ELAN4_USER_PAGE)));
++
++    elan4_removectxt (dev, &uctx->uctx_ctxt);
++
++    kcondvar_destroy (&uctx->uctx_wait);
++
++    spin_lock_destroy (&uctx->uctx_rgnlock);
++    spin_lock_destroy (&uctx->uctx_spinlock);
++
++    kmutex_destroy (&uctx->uctx_rgnmutex);
++    kmutex_destroy (&uctx->uctx_cqlock);
++    kmutex_destroy (&uctx->uctx_vpseg_lock);
++
++    KMEM_FREE (uctx, sizeof (USER_CTXT));
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan4/user_ddcq.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan4/user_ddcq.c      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan4/user_ddcq.c   2005-06-01 23:12:54.625435152 -0400
+@@ -0,0 +1,226 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: user_ddcq.c,v 1.15 2004/06/23 11:06:05 addy Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/user_ddcq.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan4/debug.h>
++#include <elan4/device.h>
++#include <elan4/user.h>
++#include <elan4/commands.h>
++
++#if PAGE_SIZE < CQ_CommandMappingSize
++#  define ELAN4_COMMAND_QUEUE_MAPPING PAGE_SIZE
++#else
++#  define ELAN4_COMMAND_QUEUE_MAPPING CQ_CommandMappingSize
++#endif
++
++/* The user device driver command queue is used for re-issuing 
++ * trapped items.  It is allocated as a 1K command queue, and
++ * we insert command flow writes event 256 words.
++ */
++#define USER_CTRLFLOW_COUNT   256
++
++/* Flow control of the device driver command queue is handled by periodically 
++ * inserting dword writes into the command stream.   When you need to know 
++ * that the queue has been flushed, then you insert an extra contorl flow
++ * write into the command queue.   Should the queue not be flushed, but the
++ * trap handler be returning to user space, then it will also insert and 
++ * extra interrupt command to ensure that it is re-entered after the queue
++ * has been flushed.
++ *
++ * Note - we account the space for the interrupt command on each control 
++ * flow write so that we do not overflow the queue even if we end up 
++ * inserting an interrupt for every command flow write.  In general only
++ * a single interrupt should get inserted....
++ */
++
++#define user_ddcq_command_write(value,off) do { \
++    PRINTF(uctx, DBG_DDCQ, "user_ddcq_command_write: cmdptr=%x off=%d value=%llx\n", cmdptr, off, value);\
++    writeq(value, cmdptr + (off << 3)); \
++} while (0)
++
++#define user_ddcq_command_space(uctx) \
++    ((CQ_Size (uctx->uctx_ddcq->ucq_cq->cq_size)>>3) - ((uctx)->uctx_ddcq_insertcnt - (uctx)->uctx_upage->upage_ddcq_completed))
++
++#define user_ddcq_command_flow_write(uctx) do { \
++   E4_uint64 iptr   = (uctx)->uctx_ddcq_insertcnt; \
++   ioaddr_t  cmdptr = (uctx)->uctx_ddcq->ucq_cq->cq_mapping + ((iptr<<3) & ((ELAN4_COMMAND_QUEUE_MAPPING >> 1)-1));\
++\
++    (uctx)->uctx_ddcq_completed = ((uctx)->uctx_ddcq_insertcnt += 3);\
++\
++    PRINTF (uctx, DBG_DDCQ, "user_ddcq_command_flow_write: completed=%llx [%llx] addr=%llx\n", (uctx)->uctx_ddcq_completed, \
++          (uctx)->uctx_upage->upage_ddcq_completed, (uctx)->uctx_upage_addr); \
++    user_ddcq_command_write (GUARD_CMD       | GUARD_ALL_CHANNELS,      0);\
++    user_ddcq_command_write (WRITE_DWORD_CMD | (uctx)->uctx_upage_addr, 1);\
++    user_ddcq_command_write ((uctx)->uctx_ddcq_completed,               2);\
++} while (0)
++
++#define user_ddcq_command_flow_intr(uctx) do { \
++   E4_uint64 iptr   = (uctx)->uctx_ddcq_insertcnt; \
++   ioaddr_t  cmdptr = (uctx)->uctx_ddcq->ucq_cq->cq_mapping + ((iptr<<3) & ((ELAN4_COMMAND_QUEUE_MAPPING >> 1)-1));\
++\
++    PRINTF (uctx, DBG_DDCQ, "user_ddcq_command_flow_intr: completed=%llx [%llx] addr=%llx\n", (uctx)->uctx_ddcq_completed, \
++          (uctx)->uctx_upage->upage_ddcq_completed, (uctx)->uctx_upage_addr); \
++    user_ddcq_command_write (INTERRUPT_CMD   | ELAN4_INT_COOKIE_DDCQ,   3);\
++} while (0)
++
++#define user_ddcq_command_prologue(uctx, count) do { \
++   E4_uint64 iptr   = (uctx)->uctx_ddcq_insertcnt; \
++   ioaddr_t  cmdptr = (uctx)->uctx_ddcq->ucq_cq->cq_mapping + ((iptr<<3) & ((ELAN4_COMMAND_QUEUE_MAPPING >> 1)-1));\
++   PRINTF(uctx, DBG_DDCQ, "user_ddcq_command_prologue: iptr=%llx cmdptr=%x\n", iptr, cmdptr);
++
++#define user_ddcq_command_epilogue(uctx, count, extra) \
++   (uctx)->uctx_ddcq_insertcnt = iptr + (count);\
++\
++   PRINTF(uctx, DBG_DDCQ, "user_ddcq_command_epilogue: iptr=%llx + %x + %x - completed %llx\n", iptr, count, extra, (uctx)->uctx_ddcq_completed);\
++   if (((iptr) + (count) + (extra)) > ((uctx)->uctx_ddcq_completed + USER_CTRLFLOW_COUNT))\
++       user_ddcq_command_flow_write(uctx); \
++} while (0)
++
++int
++user_ddcq_check (USER_CTXT *uctx, unsigned num)
++{
++    PRINTF (uctx, DBG_DDCQ, "user_check_ddcq: insert=%llx completed=%llx num=%d\n", 
++          uctx->uctx_ddcq_insertcnt, uctx->uctx_upage->upage_ddcq_completed, num);
++
++    /* Ensure that there is enough space for the command we want to issue,
++     * PLUS the guard/writeword for the control flow flush.
++     * PLUS the interrupt command for rescheduling */
++    if (user_ddcq_command_space (uctx) > (num + 4))
++    {
++      PRINTF (uctx, DBG_DDCQ, "user_ddcq_check: loads of space\n");
++
++      return (1);
++    }
++    
++    PRINTF (uctx, DBG_DDCQ, "user_ddcq_check: not enough space - reschedule\n");
++
++    uctx->uctx_trap_state = UCTX_TRAP_SIGNALLED;
++    return (0);
++}
++
++int
++user_ddcq_flush (USER_CTXT *uctx)
++{
++    ELAN4_DEV *dev = uctx->uctx_ctxt.ctxt_dev;
++    USER_CQ   *ucq = uctx->uctx_ddcq;
++
++    switch (ucq->ucq_state)
++    {
++    case UCQ_TRAPPED:
++      PRINTF (uctx, DBG_DDCQ, "user_ddcq_flush: command queue is trapped\n");
++      return (0);
++      
++    case UCQ_NEEDS_RESTART:
++      PRINTF (uctx, DBG_DDCQ, "user_ddcq_flush: restarting command queue\n");
++
++      if (UCTX_RUNNABLE (uctx))
++      {
++          ucq->ucq_state = UCQ_RUNNING;
++          elan4_restartcq (dev, ucq->ucq_cq);
++      }
++      break;
++    }
++
++    PRINTF (uctx, DBG_DDCQ, "user_ddcq_flush: insertcnt=%llx completed=%llx [%llx]\n", 
++          uctx->uctx_ddcq_insertcnt, uctx->uctx_ddcq_completed, uctx->uctx_upage->upage_ddcq_completed);
++
++    if (uctx->uctx_ddcq_completed != uctx->uctx_ddcq_insertcnt)
++      user_ddcq_command_flow_write (uctx);
++
++    return (uctx->uctx_ddcq_completed == uctx->uctx_upage->upage_ddcq_completed);
++}
++
++void
++user_ddcq_intr (USER_CTXT *uctx)
++{
++    user_ddcq_command_flow_intr (uctx);
++}
++
++void
++user_ddcq_run_dma (USER_CTXT *uctx, E4_DMA *dma)
++{
++    PRINTF (uctx, DBG_DDCQ, "user_ddcq_run_dma: cookie=%llx vproc=%llx\n",  dma->dma_cookie, dma->dma_vproc);
++
++    user_ddcq_command_prologue(uctx, 7) {
++
++      user_ddcq_command_write ((dma->dma_typeSize & ~DMA_ContextMask) | RUN_DMA_CMD, 0);
++      user_ddcq_command_write (dma->dma_cookie,                                      1);
++      user_ddcq_command_write (dma->dma_vproc,                                       2);
++      user_ddcq_command_write (dma->dma_srcAddr,                                     3);
++      user_ddcq_command_write (dma->dma_dstAddr,                                     4);
++      user_ddcq_command_write (dma->dma_srcEvent,                                    5);
++      user_ddcq_command_write (dma->dma_dstEvent,                                    6);
++
++    } user_ddcq_command_epilogue (uctx, 7, 0);
++}
++
++void
++user_ddcq_run_thread (USER_CTXT *uctx, E4_ThreadRegs *regs)
++{
++    PRINTF (uctx, DBG_DDCQ, "user_ddcq_run_thread: PC=%llx SP=%llx\n", regs->Registers[0], regs->Registers[1]);
++
++    user_ddcq_command_prologue(uctx, 7) {
++
++      user_ddcq_command_write (regs->Registers[0] | RUN_THREAD_CMD, 0);
++      user_ddcq_command_write (regs->Registers[1],                  1);
++      user_ddcq_command_write (regs->Registers[2],                  2);
++      user_ddcq_command_write (regs->Registers[3],                  3);
++      user_ddcq_command_write (regs->Registers[4],                  4);
++      user_ddcq_command_write (regs->Registers[5],                  5);
++      user_ddcq_command_write (regs->Registers[6],                  6);
++      
++    } user_ddcq_command_epilogue (uctx, 7, 0);
++}
++
++void
++user_ddcq_setevent (USER_CTXT *uctx, E4_Addr addr)
++{
++    user_ddcq_command_prologue (uctx, 1) {
++
++      user_ddcq_command_write (SET_EVENT_CMD | addr, 0);
++    
++    } user_ddcq_command_epilogue (uctx, 1, 0);
++}
++
++void
++user_ddcq_seteventn (USER_CTXT *uctx, E4_Addr addr, E4_uint32 count)
++{
++    PRINTF (uctx, DBG_DDCQ, "user_ddcq_seteventn: addr=%llx count=%lx\n", addr, count);
++
++    user_ddcq_command_prologue (uctx, 2) {
++
++      user_ddcq_command_write (SET_EVENTN_CMD, 0);
++      user_ddcq_command_write (addr | count,   1);
++
++    } user_ddcq_command_epilogue (uctx, 2, 0);
++}
++
++void
++user_ddcq_waitevent (USER_CTXT *uctx, E4_Addr addr, E4_uint64 CountAndType, E4_uint64 Param0, E4_uint64 Param1)
++{
++    PRINTF (uctx, DBG_DDCQ, "user_ddcq_waitevent: addr=%llx CountAndType=%llx Param=%llx,%llx\n", addr, CountAndType, Param0, Param1);
++
++    user_ddcq_command_prologue (uctx, 4) {
++
++      user_ddcq_command_write (WAIT_EVENT_CMD | addr, 0);
++      user_ddcq_command_write (CountAndType,          1);
++      user_ddcq_command_write (Param0,                2);
++      user_ddcq_command_write (Param1,                3);
++
++    } user_ddcq_command_epilogue (uctx, 4, 0);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/elan4/user_Linux.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/elan4/user_Linux.c     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/elan4/user_Linux.c  2005-06-01 23:12:54.626435000 -0400
+@@ -0,0 +1,377 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: user_Linux.c,v 1.25.2.4 2005/01/18 14:36:10 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/user_Linux.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <qsnet/kpte.h>
++
++#include <linux/pci.h>
++
++#include <elan4/debug.h>
++#include <elan4/device.h>
++#include <elan4/user.h>
++
++static int
++user_pteload (USER_CTXT *uctx, E4_Addr addr, physaddr_t phys, int perm)
++{
++    ELAN4_DEV *dev = uctx->uctx_ctxt.ctxt_dev;
++    E4_uint64  newpte = elan4mmu_phys2pte (dev, phys, perm);
++    
++    /*
++     * On MPSAS we don't allocate a large enough context table, so 
++     * if we see an address/context pair which would "alias" because
++     * they differ in unchecked hash bits to a previous pteload, 
++     * then we kill the application.
++     */
++    {
++      unsigned hashval = (E4MMU_SHIFT_ADDR(addr, (dev->dev_pageshift[0]) + 2) ^ E4MMU_CONTEXT_SCRAMBLE(uctx->uctx_ctxt.ctxt_num));
++      
++      if (dev->dev_rsvd_hashval[0] == 0xFFFFFFFF)
++          dev->dev_rsvd_hashval[0] = hashval & dev->dev_rsvd_hashmask[0];
++      
++      if ((hashval & dev->dev_rsvd_hashmask[0]) != dev->dev_rsvd_hashval[0])
++      {
++          printk ("user_pteload: vaddr=%016llx ctxnum=%x -> [%x] overlaps %x - %x [hashidx=%x]\n", (unsigned long long) addr, 
++                  uctx->uctx_ctxt.ctxt_num, hashval, hashval & dev->dev_rsvd_hashmask[0], dev->dev_rsvd_hashval[0],
++                  E4MMU_HASH_INDEX (uctx->uctx_ctxt.ctxt_num, addr, dev->dev_pageshift[0], dev->dev_hashsize[0]-1));
++          
++          return -EFAULT;
++      }
++    }
++
++    if ((newpte & (PTE_PciNotLocal | PTE_CommandQueue)) == 0 && 
++      ((addr & (SDRAM_PGOFF_OFFSET << PAGE_SHIFT)) != (phys & (SDRAM_PGOFF_OFFSET << PAGE_SHIFT))))
++    {
++      printk ("user_pteload: vaddr=%016llx incorrectly alias sdram at %lx\n", (unsigned long long) addr, 
++              phys ^ pci_resource_start (dev->dev_osdep.pdev, ELAN4_BAR_SDRAM));
++      return -EFAULT;
++    }
++
++    if (newpte & PTE_PciNotLocal)
++      PRINTF (uctx, DBG_FAULT, "user_pteload: addr=%llx -> pte=%llx (pci)\n", addr, newpte);
++    else if (newpte & PTE_CommandQueue)
++      PRINTF (uctx, DBG_FAULT, "user_pteload: addr=%llx -> pte=%llx (command)\n", addr, newpte);
++    else
++      PRINTF (uctx, DBG_FAULT, "user_pteload: addr=%llx -> pte=%llx (sdram)\n", addr, newpte);
++
++    elan4mmu_pteload (&uctx->uctx_ctxt, 0, addr, newpte);
++
++    return (0);
++}
++
++int
++user_load_range (USER_CTXT *uctx, E4_Addr eaddr, unsigned long nbytes, E4_uint32 fsr)
++{
++    ELAN4_DEV             *dev       = uctx->uctx_ctxt.ctxt_dev;
++    struct mm_struct      *mm        = current->mm;
++    int                    writeable = (AT_Perm(fsr) == AT_PermLocalDataWrite ||
++                                      AT_Perm(fsr) == AT_PermRemoteWrite    ||
++                                      AT_Perm(fsr) == AT_PermLocalEvent     ||
++                                      AT_Perm(fsr) == AT_PermRemoteEvent);
++    struct vm_area_struct *vma;
++    int                    i, perm;
++    unsigned long          len;
++    unsigned long          maddr;
++    physaddr_t                   phys;
++
++    kmutex_lock (&uctx->uctx_rgnmutex);
++
++    while (nbytes > 0)
++    {
++      USER_RGN *rgn = user_rgnat_elan (uctx, eaddr);
++
++      if (rgn == NULL || ELAN4_INCOMPAT_ACCESS (rgn->rgn_perm, AT_Perm (fsr)))
++      {
++          PRINTF (uctx, DBG_FAULT, "user_load_range: eaddr=%llx -> %s\n", eaddr, rgn == NULL ? "no mapping" : "no permission");
++
++          kmutex_unlock (&uctx->uctx_rgnmutex);
++          return (rgn == NULL ? -EFAULT : -EPERM);
++      }
++
++      if (writeable)
++          perm = rgn->rgn_perm;
++/* This is the correct code but it breaks the Eagle libraries (1.6.X) - backed out (addy 24.08.04)
++      else if (AT_Perm(fsr) == AT_PermExecute && (rgn->rgn_perm & PERM_Mask) != PERM_LocExecute)
++*/
++      else if (AT_Perm(fsr) == AT_PermExecute)
++          perm = PERM_LocRead | (rgn->rgn_perm & ~PERM_Mask);
++      else
++          perm = ELAN4_PERM_READONLY (rgn->rgn_perm & PERM_Mask) | (rgn->rgn_perm & ~PERM_Mask);
++
++      PRINTF (uctx, DBG_FAULT, "user_load_range: rgn=%p [%llx.%lx.%x]\n", rgn, rgn->rgn_ebase, rgn->rgn_mbase, rgn->rgn_len);
++
++      len = ((rgn->rgn_ebase + rgn->rgn_len) - eaddr);
++      if (len > nbytes)
++          len = nbytes;
++      nbytes -= len;
++      
++      maddr = rgn->rgn_mbase + (eaddr - rgn->rgn_ebase);
++
++      PRINTF (uctx, DBG_FAULT, "user_load_range: eaddr=%llx->%llx -> %lx->%lx len=%x perm=%x\n", eaddr, 
++              eaddr + len, maddr, maddr + len, len, perm);
++
++      down_read (&mm->mmap_sem);
++      while (len > 0)
++      {
++          if ((vma = find_vma_intersection (mm, maddr, maddr + PAGE_SIZE)) == NULL ||
++              (writeable && !(vma->vm_flags & VM_WRITE)))
++          {
++              PRINTF (DBG_USER, DBG_FAULT, "ctxt_pagefault: %s %lx\n", vma ? "no writeble at" : "no vma for", maddr);
++              up_read (&mm->mmap_sem);
++              kmutex_unlock (&uctx->uctx_rgnmutex);
++              return (-EFAULT);
++          }
++
++          spin_lock (&mm->page_table_lock);
++          {
++              pte_t *ptep_ptr;
++              pte_t  ptep_value;
++
++              ptep_ptr = find_pte_map (mm, maddr);
++              if (ptep_ptr) {
++                  ptep_value = *ptep_ptr;
++                  pte_unmap(ptep_ptr);
++              }
++
++              PRINTF (uctx, DBG_FAULT, "user_load_range: %lx %s %s\n", maddr, writeable ? "writeable" : "readonly", 
++                      !ptep_ptr ? "invalid" : pte_none(ptep_value) ? "none " : !pte_present(ptep_value) ? "swapped " : 
++                      writeable && !pte_write(ptep_value) ? "COW" : "OK");
++              
++              if (ptep_ptr == NULL || pte_none(ptep_value) || !pte_present(ptep_value) || (writeable && !pte_write(ptep_value)) || !pte_read (ptep_value))
++              {
++                  spin_unlock (&mm->page_table_lock);
++                  
++                  make_pages_present(maddr, maddr + PAGE_SIZE);
++                  
++                  spin_lock (&mm->page_table_lock);
++
++                  ptep_ptr = find_pte_map (mm, maddr);
++                  if (ptep_ptr) {
++                      ptep_value = *ptep_ptr;
++                      pte_unmap(ptep_ptr);
++                  }
++                  
++                  if (ptep_ptr == NULL || pte_none(ptep_value) || !pte_present(ptep_value) || (writeable && !pte_write(ptep_value)) || !pte_read (ptep_value))
++                  {   
++                      spin_unlock (&mm->page_table_lock);
++                      up_read (&mm->mmap_sem);
++                      kmutex_unlock (&uctx->uctx_rgnmutex);
++                      return (-EFAULT);
++                  }
++              } 
++              
++              if (writeable)
++                  pte_mkdirty(ptep_value);
++              pte_mkyoung (ptep_value);
++
++              phys = pte_phys (ptep_value);
++
++              for (i = 0; i < PAGE_SIZE; i += (1 << dev->dev_pageshift[0]))
++              {
++                  if (user_pteload (uctx, eaddr, phys, perm) < 0)
++                  {
++                      spin_unlock (&mm->page_table_lock);
++                      up_read (&mm->mmap_sem);
++                      kmutex_unlock (&uctx->uctx_rgnmutex);
++                      return (-EFAULT);
++                  }
++                  
++                  eaddr += (1 << dev->dev_pageshift[0]);
++                  phys  += (1 << dev->dev_pageshift[0]);
++              }
++          }
++          spin_unlock (&mm->page_table_lock);
++              
++          maddr += PAGE_SIZE;
++          len   -= PAGE_SIZE;
++      }
++      up_read (&mm->mmap_sem);
++    }
++    kmutex_unlock (&uctx->uctx_rgnmutex);
++
++    PRINTF (uctx, DBG_FAULT, "user_load_range: alldone\n");
++
++    return (0);
++}
++
++void
++user_preload_main (USER_CTXT *uctx, virtaddr_t addr, unsigned long len)
++{
++    virtaddr_t             lim = addr + len - 1;
++    struct vm_area_struct *vma;
++
++    down_read (&current->mm->mmap_sem);
++
++    if ((vma = find_vma (current->mm, addr)) != NULL)
++    {
++      do {
++          unsigned long start = vma->vm_start;
++          unsigned long end   = vma->vm_end;
++
++          if ((start-1) >= lim)
++              break;
++
++          if (start < addr) start = addr;
++          if ((end-1) > lim) end = lim+1;
++              
++          if (vma->vm_flags & VM_IO)
++              continue;
++
++          user_unload_main (uctx, start, end - start);
++
++          make_pages_present (start, end);
++
++          user_update_main (uctx, current->mm, start, end - start);
++
++      } while ((vma = find_vma (current->mm, vma->vm_end)) != NULL);
++    }
++    up_read (&current->mm->mmap_sem);
++}
++
++static void
++user_update_range (USER_CTXT *uctx, int tbl, struct mm_struct *mm, virtaddr_t maddr, E4_Addr eaddr, unsigned long len, int perm)
++{
++    ELAN4_DEV *dev    = uctx->uctx_ctxt.ctxt_dev;
++    int        roperm = ELAN4_PERM_READONLY(perm & PERM_Mask) | (perm & ~PERM_Mask);
++    int        nbytes;
++
++    while (len > 0)
++    {
++      pte_t *ptep_ptr;
++      pte_t  ptep_value;
++      
++      ptep_ptr = find_pte_map (mm, maddr);
++      if (ptep_ptr) {
++          ptep_value = *ptep_ptr;
++          pte_unmap(ptep_ptr);
++      }
++
++      PRINTF (uctx, DBG_IOPROC, "user_update_range: %llx (%lx) %s\n", eaddr, maddr, 
++              !ptep_ptr ? "invalid" : pte_none(ptep_value) ? "none " : !pte_present(ptep_value) ? "swapped " : 
++              !pte_write(ptep_value) ? "RO/COW" : "OK");
++      
++      if (ptep_ptr && !pte_none(ptep_value) && pte_present(ptep_value) && pte_read (ptep_value)) {
++          physaddr_t phys_value = pte_phys(ptep_value);
++          for (nbytes = 0; nbytes < PAGE_SIZE; nbytes += (1 << dev->dev_pageshift[0]))
++          {
++              user_pteload (uctx, eaddr, phys_value, pte_write (ptep_value) ? perm : roperm);
++
++              eaddr       += (1 << dev->dev_pageshift[0]);
++              phys_value  += (1 << dev->dev_pageshift[0]);
++          }
++      }
++
++      maddr += PAGE_SIZE;
++      len   -= PAGE_SIZE;
++    }
++}
++
++void
++user_update_main (USER_CTXT *uctx, struct mm_struct *mm, virtaddr_t start, unsigned long len)
++{
++    USER_RGN     *rgn;
++    unsigned long ssize;
++    virtaddr_t    end = start + len - 1;
++
++    spin_lock (&uctx->uctx_rgnlock);
++
++    PRINTF (uctx, DBG_IOPROC, "user_update_main: start=%lx end=%lx\n", start, end);
++
++    for (rgn = user_findrgn_main (uctx, start, 0); rgn != NULL; rgn = rgn->rgn_mnext)
++    {
++      if (end < rgn->rgn_mbase)
++          break;
++      
++      if (start <= rgn->rgn_mbase && end >= (rgn->rgn_mbase + rgn->rgn_len - 1)) 
++      {
++          PRINTF (uctx, DBG_IOPROC, "user_update_main: whole %lx -> %lx\n", rgn->rgn_mbase, rgn->rgn_mbase + rgn->rgn_len - 1);
++
++          user_update_range (uctx, 0 /* tbl */, mm, rgn->rgn_mbase, rgn->rgn_ebase, rgn->rgn_len, rgn->rgn_perm);
++      }
++      else if (start <= rgn->rgn_mbase)
++      {
++          ssize = end - rgn->rgn_mbase + 1;
++
++          PRINTF (uctx, DBG_IOPROC, "user_update_main: start %lx -> %lx\n", rgn->rgn_mbase, rgn->rgn_mbase + ssize);
++
++          user_update_range (uctx, 0 /* tbl */, mm, rgn->rgn_mbase, rgn->rgn_ebase, ssize, rgn->rgn_perm);
++      }
++      else if (end >= (rgn->rgn_mbase + rgn->rgn_len - 1))
++      {
++          ssize = (rgn->rgn_mbase + rgn->rgn_len) - start;
++
++          PRINTF (uctx, DBG_IOPROC, "user_update_main: end   %lx -> %lx\n", start, start + ssize);
++
++          user_update_range (uctx, 0 /* tbl */, mm, start, rgn->rgn_ebase + (start - rgn->rgn_mbase), ssize, rgn->rgn_perm);
++      }
++      else
++      {
++          PRINTF (uctx, DBG_IOPROC, "user_update_main: middle %lx -> %lx\n", start, end);
++
++          user_update_range (uctx, 0 /* tbl */, mm, start, rgn->rgn_ebase + (start - rgn->rgn_mbase), len, rgn->rgn_perm);
++      }
++    }
++    spin_unlock (&uctx->uctx_rgnlock);
++}
++
++void
++user_unload_main (USER_CTXT *uctx, virtaddr_t start, unsigned long len)
++{
++    USER_RGN     *rgn;
++    unsigned long ssize;
++    virtaddr_t    end = start + len - 1;
++
++    spin_lock (&uctx->uctx_rgnlock);
++
++    PRINTF (uctx, DBG_IOPROC, "user_unload_main: start=%lx end=%lx\n", start, end);
++
++    for (rgn = user_findrgn_main (uctx, start, 0); rgn != NULL; rgn = rgn->rgn_mnext)
++    {
++      if (end < rgn->rgn_mbase)
++          break;
++      
++      if (start <= rgn->rgn_mbase && end >= (rgn->rgn_mbase + rgn->rgn_len - 1))
++      {
++          PRINTF (uctx, DBG_IOPROC, "user_unload_main: whole %lx -> %lx\n", rgn->rgn_mbase, rgn->rgn_mbase + rgn->rgn_len - 1);
++
++          elan4mmu_unload_range (&uctx->uctx_ctxt, 0 /* tbl */, rgn->rgn_ebase, rgn->rgn_len);
++      }
++      else if (start <= rgn->rgn_mbase)
++      {
++          ssize = end - rgn->rgn_mbase + 1;
++
++          PRINTF (uctx, DBG_IOPROC, "user_unload_main: start %lx -> %lx\n", rgn->rgn_mbase, rgn->rgn_mbase + ssize);
++
++          elan4mmu_unload_range (&uctx->uctx_ctxt, 0 /* tbl */, rgn->rgn_ebase, ssize);
++      }
++      else if (end >= (rgn->rgn_mbase + rgn->rgn_len - 1))
++      {
++          ssize = (rgn->rgn_mbase + rgn->rgn_len) - start;
++          
++          PRINTF (uctx, DBG_IOPROC, "user_unload_main: end   %lx -> %lx\n", start, start + ssize);
++          
++          elan4mmu_unload_range (&uctx->uctx_ctxt, 0 /* tbl */, rgn->rgn_ebase + (start - rgn->rgn_mbase), ssize);
++      }
++      else
++      {
++
++          PRINTF (uctx, DBG_IOPROC, "user_unload_main: middle %lx -> %lx\n", start, end);
++
++          elan4mmu_unload_range (&uctx->uctx_ctxt, 0 /* tbl */, rgn->rgn_ebase + (start - rgn->rgn_mbase), len);
++      }
++    }
++    spin_unlock (&uctx->uctx_rgnlock);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/ep/asm_elan4_thread.S
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/asm_elan4_thread.S  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/asm_elan4_thread.S       2005-06-01 23:12:54.626435000 -0400
+@@ -0,0 +1,78 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: asm_elan4_thread.S,v 1.1 2003/09/23 13:55:11 david Exp $ $Name: QSNETMODULES-4-30_20050128 $"
++/*      $Source: /cvs/master/quadrics/epmod/asm_elan4_thread.S,v $*/
++
++#include <elan4/events.h>
++#include <elan4/commands.h>
++
++/*
++ * c_reschedule (E4_uint64 *commandport)
++ */           
++      .global c_reschedule
++c_reschedule:
++      add             %sp, -128, %sp
++      st64            %r16, [%sp]                     // preserve call preserved registers
++      st64            %r24, [%sp + 64]                // - see CALL_USED_REGISTERS.
++      mov             %r16,%r16                       // BUG FIX: E4 RevA
++      mov             %r24,%r24                       // BUG FIX: E4 RevA
++      nop                                             // BUG FIX: E4 RevA
++      nop                                             // BUG FIX: E4 RevA
++      
++      mov             %r7, %r18                       // (%r2) return pc
++1:    call            2f
++       mov            %sp, %r17                       // (%r1) SP
++2:    add             %r7, (3f-1b), %r16              // (%r0) PC
++      mov             NOP_CMD, %r23                   // "nop" command
++      st64suspend     %r16, [%r8]
++3:    ld64            [%sp], %r16
++      ld64            [%sp + 64], %r24                // restore call preserved register
++      jmpl            %r2+8, %r0                      // and return
++       add            %sp, 128, %sp
++      
++
++/*
++ * c_waitevent (E4_uint64 *commandport, E4_Event *event, E4_uint64 count)
++ */
++      .global c_waitevent
++c_waitevent:
++      add             %sp, -192, %sp
++      st64            %r16, [%sp + 64]                // preserve call preserved registers
++      st64            %r24, [%sp + 128]               // - see CALL_USED_REGISTERS.
++      mov             %r16,%r16                       // BUG FIX: E4 RevA
++      mov             %r24,%r24                       // BUG FIX: E4 RevA
++      nop                                             // BUG FIX: E4 RevA
++      nop                                             // BUG FIX: E4 RevA
++
++      mov             %r7, %r18                       // (%r2) return pc
++1:    call            2f
++       mov            %sp, %r17                       // (%r1) SP
++2:    add             %r7, (3f-1b), %r16              // (%r0) PC
++      st32            %r16, [%sp]                     // event source block
++      mov             MAKE_EXT_CLEAN_CMD, %r23        // "flush command queue desc" command
++      st8             %r23, [%sp+56]                  // event source block
++      mov             %r16,%r16                       // BUG FIX: E4 RevA
++      mov             %r23,%r23                       // BUG FIX: E4 RevA
++      nop                                             // BUG FIX: E4 RevA
++      nop                                             // BUG FIX: E4 RevA
++      
++
++      or              %r9, WAIT_EVENT_CMD, %r16
++      sll8            %r10, 32, %r17
++      or              %r17, E4_EVENT_TYPE_VALUE(E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, 8), %r17
++      mov             %sp, %r18
++      mov             %r8, %r19
++      
++      st32suspend     %r16, [%r8]
++      
++3:    ld64            [%sp + 64], %r16                // restore call preserved register
++      ld64            [%sp + 128], %r24
++      jmpl            %r2+8, %r0                      // and return
++       add            %sp, 192, %sp
++
+Index: linux-2.4.21/drivers/net/qsnet/ep/assym_elan4.h
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/assym_elan4.h       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/assym_elan4.h    2005-06-01 23:12:54.627434848 -0400
+@@ -0,0 +1,20 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: genassym_elan4.c,v 1.3 2004/04/25 11:26:07 david Exp $ $Name: QSNETMODULES-4-30_20050128 $"
++/*      $Source: /cvs/master/quadrics/epmod/genassym_elan4.c,v $*/
++
++/* Generated by genassym_elan4 - do not modify */
++
++#define EP4_RCVR_THREAD_STALL 0
++#define EP4_RCVR_PENDING_TAILP        128
++#define EP4_RCVR_PENDING_HEAD 136
++#define EP4_RCVR_DEBUG                176
++#define EP4_RXD_NEXT          664
++#define EP4_RXD_QUEUED                728
++#define EP4_RXD_DEBUG         944
+Index: linux-2.4.21/drivers/net/qsnet/ep/cm.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/cm.c        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/cm.c     2005-06-01 23:12:54.632434088 -0400
+@@ -0,0 +1,3000 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: cm.c,v 1.83.2.6 2005/01/13 12:37:57 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/cm.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++
++#include "kcomm_vp.h"
++#include "debug.h"
++#include "cm.h"
++#include <elan/epsvc.h>
++
++#include <qsnet/procfs_linux.h>
++
++#if defined(LINUX)
++#include "conf_linux.h"
++#endif
++
++int BranchingRatios[CM_MAX_LEVELS];
++
++int MachineId      = -1;
++int BrokenLevel    = -1;                      /* Simulates Broken Network */
++int RejoinCheck    = 1;
++int RejoinPanic    = 0;
++
++static int
++SegmentNo (CM_RAIL *cmRail, u_int nodeid, u_int lvl)
++{
++    int i;
++
++    ASSERT (lvl < cmRail->NumLevels);
++    
++    for (i = 0; i < lvl; i++)
++      nodeid /= cmRail->Levels[i].NumSegs;
++    
++    return (nodeid % cmRail->Levels[lvl].NumSegs);
++}
++
++static int
++ClusterIds (CM_RAIL *cmRail, int clvl, int *clmin, int *clmax)
++{
++    int clid  = cmRail->Rail->Position.pos_nodeid - cmRail->Levels[clvl].MinNodeId;
++
++    if (clvl == 0)
++      *clmin = *clmax = clid;
++    else
++    {
++      *clmin = cmRail->Levels[clvl - 1].MinNodeId - cmRail->Levels[clvl].MinNodeId;
++      *clmax = *clmin + cmRail->Levels[clvl - 1].NumNodes - 1;
++    }
++    return (clid);
++}
++
++#if defined(PER_CPU_TIMEOUT)
++static void
++__Schedule_Discovery (CM_RAIL *cmRail)                /* we urgently need to schedule discovery */
++{
++    cmRail->NextDiscoverTime = lbolt;
++
++    if (cmRail->NextRunTime == 0 || AFTER (cmRail->NextRunTime, cmRail->NextDiscoverTime))
++      cmRail->NextRunTime = cmRail->NextDiscoverTime;
++}
++
++static void
++__Schedule_Heartbeat (CM_RAIL *cmRail)
++{
++    cmRail->NextHeartbeatTime = lbolt;
++
++    if (cmRail->NextRunTime == 0 || AFTER (cmRail->NextRunTime, cmRail->NextHeartbeatTime))
++      cmRail->NextRunTime = cmRail->NextHeartbeatTime;
++}
++#else
++
++static void
++__Schedule_Timer (CM_RAIL *cmRail, long tick)
++{
++    if (! timer_pending (&cmRail->HeartbeatTimer) || AFTER (cmRail->NextRunTime, tick))
++    {
++      cmRail->NextRunTime = tick;
++
++      mod_timer (&cmRail->HeartbeatTimer, tick);
++    }
++}
++
++static void
++__Schedule_Discovery (CM_RAIL *cmRail)                /* we urgently need to schedule discovery */
++{
++    __Schedule_Timer (cmRail, cmRail->NextDiscoverTime = lbolt);
++}
++
++static void
++__Schedule_Heartbeat (CM_RAIL *cmRail)
++{
++    __Schedule_Timer (cmRail, cmRail->NextHeartbeatTime = lbolt);
++}
++#endif
++
++static int
++MsgBusy (CM_RAIL *cmRail, int msgNumber)
++{
++    switch (ep_outputq_state (cmRail->Rail, cmRail->MsgQueue, msgNumber))
++    {
++    case EP_OUTPUTQ_BUSY:                     /* still busy */
++      return 1;
++      
++    case EP_OUTPUTQ_FAILED:                   /* NACKed */
++    {
++#if defined(DEBUG_PRINTF)
++      CM_MSG  *msg  = ep_outputq_msg (cmRail->Rail, cmRail->MsgQueue, msgNumber);
++      uint8_t  type  = msg->Hdr.Type;
++      uint16_t nmaps = msg->Hdr.NumMaps;
++      int16_t  off   = msg->Payload.Statemaps[CM_MSG_MAP(0)].offset;
++      
++      CPRINTF4 (((type == CM_MSG_TYPE_DISCOVER_LEADER) || (type == CM_MSG_TYPE_DISCOVER_SUBORDINATE))  ? 6 : 3, /* we expect broadcasts to be NACKed */
++                "%s: msg %d type %d  failed%s\n", cmRail->Rail->Name, msgNumber, type, 
++                (type != CM_MSG_TYPE_HEARTBEAT) ? "" : nmaps == 0 ? ": null heartbeat" :
++                off == STATEMAP_RESET ? ": heartbeat with R statemaps" : ": heartbeat with statemaps");
++#endif
++      return 0;
++    }
++    
++    case EP_OUTPUTQ_FINISHED:
++      return 0;
++
++    default:
++      panic ("MsgBusy - bad return code from ep_outputq_state\n");
++      /* NOTREACHED */
++    }
++    return 0;
++}
++
++static void
++LaunchMessage (CM_RAIL *cmRail, int msgNumber, int vp, int qnum, int retries, int type, int lvl, int nmaps)
++{
++    CM_MSG *msg = ep_outputq_msg (cmRail->Rail, cmRail->MsgQueue, msgNumber);
++    CM_HDR *hdr = &msg->Hdr;
++
++    ASSERT (nmaps >= 0 && nmaps <= CM_MSG_MAXMAPS);
++    ASSERT (SPINLOCK_HELD (&cmRail->Lock));
++
++    hdr->Version   = CM_MSG_VERSION;
++    hdr->ParamHash = cmRail->ParamHash;
++    hdr->Timestamp = cmRail->Timestamp;
++    hdr->Checksum  = 0;
++    hdr->NodeId    = cmRail->Rail->Position.pos_nodeid;
++    hdr->MachineId = MachineId;
++    hdr->NumMaps   = nmaps;
++    hdr->Level     = lvl;
++    hdr->Type      = type;
++    hdr->Checksum  = CheckSum ((char *)msg + CM_MSG_BASE(nmaps), CM_MSG_SIZE(nmaps));
++
++    if (BrokenLevel != -1 && (lvl >= ((BrokenLevel >> (cmRail->Rail->Number*4)) & 0xf)))                      /* Simulate broken network? */
++       return;
++
++    if (ep_outputq_send (cmRail->Rail, cmRail->MsgQueue, msgNumber, 
++                       CM_MSG_SIZE(nmaps), vp, qnum, retries));
++      IncrStat (cmRail, LaunchMessageFail);
++}
++
++static int
++SendMessage (CM_RAIL *cmRail, int nodeId, int lvl, int type)
++{
++    int       msgNumber = CM_NUM_NODE_MSG_BUFFERS + cmRail->NextSpareMsg;
++    int n         = CM_NUM_SPARE_MSG_BUFFERS;
++    int retries;
++
++    ASSERT (type == CM_MSG_TYPE_IMCOMING ||   /* other types must use SendToSgmt */
++          type == CM_MSG_TYPE_REJOIN);
++   
++    while (n-- > 0 && MsgBusy (cmRail, msgNumber))    /* search for idle "spare" buffer */
++    {
++      if (++(cmRail->NextSpareMsg) == CM_NUM_SPARE_MSG_BUFFERS)
++          cmRail->NextSpareMsg = 0;
++      
++      msgNumber = CM_NUM_NODE_MSG_BUFFERS + cmRail->NextSpareMsg;
++    }
++
++    if (n == 0)                                       /* all "spare" message buffers busy */
++    {
++      CPRINTF3 (3, "%s: all spare message buffers busy: trying to send type %d to %d\n",
++                cmRail->Rail->Name, type, nodeId);
++      return (0);
++    }
++
++    /* NB IMCOMING may be echoed by MANY nodes, so we don't (and musn't) have any retries */
++    retries = (type == CM_MSG_TYPE_IMCOMING) ? 0 : CM_P2P_DMA_RETRIES;
++   
++    LaunchMessage (cmRail, msgNumber, EP_VP_NODE (nodeId), EP_SYSTEMQ_INTR, /* eager receive */
++                 retries, type, lvl, 0);
++   
++    if (++(cmRail->NextSpareMsg) == CM_NUM_SPARE_MSG_BUFFERS) /* check this one last next time */
++      cmRail->NextSpareMsg = 0;
++
++    return (1);
++}
++
++static int
++SendToSgmt (CM_RAIL *cmRail, CM_SGMT *sgmt, int type)
++{    
++    bitmap_t         seg;
++    int              offset;
++    int              nmaps;
++    int              sidx;
++    int              clvl;
++    
++    ASSERT (sgmt->Level <= cmRail->TopLevel);
++
++    if (MsgBusy (cmRail, sgmt->MsgNumber))            /* previous message still busy */
++    {
++      CPRINTF3 (3, "%s: node message buffer busy: trying to send type %d to %d\n",
++                cmRail->Rail->Name, type, sgmt->NodeId);
++      
++      return (0);
++    }
++
++    switch (type)
++    {
++    case CM_MSG_TYPE_RESOLVE_LEADER:
++    case CM_MSG_TYPE_DISCOVER_LEADER:
++      ASSERT (sgmt->State == CM_SGMT_ABSENT);
++      ASSERT (sgmt->Level == ((cmRail->Role == CM_ROLE_LEADER_CANDIDATE) ? cmRail->TopLevel : cmRail->TopLevel - 1));
++      ASSERT (sgmt->Level < cmRail->NumLevels);
++      ASSERT (sgmt->Sgmt == cmRail->Levels[sgmt->Level].MySgmt);
++      
++      /* broadcast to me and all my peers at this level (== my segment in the level above) */
++      sidx = (sgmt->Level == cmRail->NumLevels - 1) ? 0 : cmRail->Levels[sgmt->Level + 1].MySgmt;
++
++      LaunchMessage (cmRail, sgmt->MsgNumber, EP_VP_BCAST (sgmt->Level + 1, sidx), 
++                     EP_SYSTEMQ_INTR, 0,              /* eager rx; no retries */
++                     type, sgmt->Level, 0);
++      return (1);
++      
++    case CM_MSG_TYPE_DISCOVER_SUBORDINATE:
++      ASSERT (sgmt->Sgmt != cmRail->Levels[sgmt->Level].MySgmt);
++      ASSERT (sgmt->State == CM_SGMT_WAITING);
++      ASSERT (sgmt->Level > 0);                       /* broadcasting just to subtree */
++      
++      LaunchMessage (cmRail, sgmt->MsgNumber, EP_VP_BCAST (sgmt->Level, sgmt->Sgmt), 
++                     EP_SYSTEMQ_INTR, 0,              /* eager rx; no retries */
++                     CM_MSG_TYPE_DISCOVER_SUBORDINATE, sgmt->Level, 0);
++      return (1);
++      
++    case CM_MSG_TYPE_NOTIFY:
++      ASSERT (sgmt->State == CM_SGMT_PRESENT);
++      
++      LaunchMessage (cmRail, sgmt->MsgNumber, EP_VP_NODE (sgmt->NodeId),
++                     EP_SYSTEMQ_INTR, CM_P2P_DMA_RETRIES, /* eager rx; lots of retries */
++                     CM_MSG_TYPE_NOTIFY, sgmt->Level, 0);
++      return (1);
++      
++    case CM_MSG_TYPE_HEARTBEAT:
++    {
++      CM_MSG *msg = ep_outputq_msg (cmRail->Rail, cmRail->MsgQueue, sgmt->MsgNumber);
++      CM_HDR *hdr = &msg->Hdr;
++
++      ASSERT (sgmt->State == CM_SGMT_PRESENT);
++      
++      hdr->AckSeq = sgmt->AckSeq;
++   
++      if (!sgmt->MsgAcked)                    /* Current message not acknowledged */
++      {
++          /* must have been something significant to require an ack */
++          ASSERT (sgmt->SendMaps);
++          ASSERT (sgmt->NumMaps > 0);
++          
++          CPRINTF3 (3, "%s: retrying heartbeat to %d (%d entries)\n", cmRail->Rail->Name, sgmt->NodeId, sgmt->NumMaps);
++
++          IncrStat (cmRail, RetryHeartbeat);
++
++          nmaps = sgmt->NumMaps;
++      }
++      else
++      {
++          nmaps = 0;
++      
++          if (sgmt->SendMaps)                 /* can send maps */
++          {
++              for (clvl = sgmt->Level; clvl < cmRail->NumLevels; clvl++)
++              {
++                  if (!sgmt->Maps[clvl].OutputMapValid)
++                      continue;
++                  
++                  while ((offset = statemap_findchange (sgmt->Maps[clvl].OutputMap, &seg, 1)) >= 0)
++                  {
++                      CM_STATEMAP_ENTRY *map = &msg->Payload.Statemaps[CM_MSG_MAP(nmaps)];
++
++                      sgmt->Maps[clvl].SentChanges = 1;
++                      
++                      map->level  = clvl;
++                      map->offset = offset;
++                      map->seg[0] = seg & 0xffff;
++                      map->seg[1] = (seg >> 16) & 0xffff;
++#if (BT_ULSHIFT == 6)
++                      map->seg[2] = (seg >> 32) & 0xffff;
++                      map->seg[3] = (seg >> 48) & 0xffff;
++#elif (BT_ULSHIFT != 5)
++#error "Bad value for BT_ULSHIFT"
++#endif
++                      if (++nmaps == CM_MSG_MAXMAPS)
++                          goto msg_full;
++                  }
++
++                  if (sgmt->Maps[clvl].SentChanges)
++                  {
++                      CM_STATEMAP_ENTRY *map = &msg->Payload.Statemaps[CM_MSG_MAP(nmaps)];
++
++                      sgmt->Maps[clvl].SentChanges = 0;
++
++                      map->level  = clvl;
++                      map->offset = STATEMAP_NOMORECHANGES;
++                      
++                      if (++nmaps == CM_MSG_MAXMAPS)
++                          goto msg_full;
++                  }
++              }
++          }
++          
++          ASSERT (nmaps < CM_MSG_MAXMAPS);
++
++      msg_full:
++          sgmt->NumMaps = nmaps;              /* remember how many incase we retry */
++
++          if (nmaps == 0)                     /* no changes to send */
++              hdr->Seq = sgmt->MsgSeq;        /* this one can be dropped */
++          else
++          {
++              hdr->Seq = ++(sgmt->MsgSeq);    /* on to next message number */
++              sgmt->MsgAcked = 0;             /* need this one to be acked before I can send another */
++
++              IncrStat (cmRail, MapChangesSent);
++          }
++      }
++
++      LaunchMessage (cmRail, sgmt->MsgNumber, EP_VP_NODE (sgmt->NodeId), 
++                     EP_SYSTEMQ_POLLED,  CM_P2P_DMA_RETRIES, /* polled receive, lots of retries */
++                     CM_MSG_TYPE_HEARTBEAT, sgmt->Level, nmaps);
++
++      IncrStat (cmRail, HeartbeatsSent);
++
++      return (1);
++    }
++
++    default:                                  /* other types must use SendMessage */
++      printk ("SendToSgmt: invalid type %d\n", type);
++      ASSERT (0);
++
++      return (1);
++    }
++}
++
++static char *
++GlobalStatusString (statemap_t *map, int idx)
++{
++    char *strings[] = {"....", "S...", "C...", "R...", 
++                     ".s..", "Ss..", "Cs..", "Rs..", 
++                     "..r.", "S.r.", "C.r.", "R.r.", 
++                     ".sr.", "Ssr.", "Csr.", "Rsr.", 
++                     "...R", "S..R", "C..R", "R..R", 
++                     ".s.R", "Ss.R", "Cs.R", "Rs.R", 
++                     "..rR", "S.rR", "C.rR", "R.rR", 
++                     ".srR", "SsrR", "CsrR", "RsrR"};
++    
++    return (strings[statemap_getbits (map, idx * CM_GSTATUS_BITS, CM_GSTATUS_BITS)]);
++}
++
++static char *
++MapString (char *name, statemap_t *map, int nnodes, char *trailer)
++{
++    static char *space;
++    int          i;
++
++    if (space == NULL)
++      KMEM_ALLOC (space, char *, EP_MAX_NODES*(CM_GSTATUS_BITS+1), 0);
++
++    if (space == NULL)
++      return ("<cannot allocate memory>");
++    else
++    {
++      char *ptr = space;
++
++      sprintf (space, "%s ", name); ptr += strlen (ptr);
++      for (i = 0; i < nnodes; i++, ptr += strlen (ptr))
++          sprintf (ptr, "%s%s", i == 0 ? "" : ",", GlobalStatusString (map, i));
++      sprintf (ptr, " %s", trailer);
++      return (space);
++    }
++}
++
++void
++DisplayMap (DisplayInfo *di, CM_RAIL *cmRail, char *name, statemap_t *map, int nnodes, char *trailer)
++{
++    char  linebuf[256];
++    char *ptr = linebuf;
++    int   i;
++
++#define NODES_PER_LINE        32
++    for (i = 0; i < nnodes; i++)
++    {
++      if (ptr == linebuf)
++      {
++          sprintf (ptr, "%4d", i);
++          ptr += strlen (ptr);
++      }
++      
++      sprintf (ptr, ",%s", GlobalStatusString (map, i));
++      ptr += strlen (ptr);
++
++      if ((i % NODES_PER_LINE) == (NODES_PER_LINE-1) || (i == (nnodes-1)))
++      {
++          (di->func)(di->arg, "%s: %s %s %s\n", cmRail->Rail->Name, name, linebuf, trailer);
++          ptr = linebuf;
++      }
++    }
++#undef NODES_PER_LINE
++}
++
++void
++DisplayNodeMaps (DisplayInfo *di, CM_RAIL *cmRail)
++{
++    int   lvl;
++    int   clvl;
++    char  mapname[128];
++    
++    (di->func)(di->arg, "%s: Node %d maps...\n", cmRail->Rail->Name, cmRail->Rail->Position.pos_nodeid);
++
++    for (clvl = 0; clvl < cmRail->NumLevels; clvl++)
++    {
++      int nnodes = cmRail->Levels[clvl].NumNodes;
++
++      (di->func)(di->arg, "%s: Cluster level %d: Connected %ld - %s%s\n", 
++                 cmRail->Rail->Name, clvl, cmRail->Levels[clvl].Connected,
++                 cmRail->Levels[clvl].Online     ? "Online" : "Offline",
++                 cmRail->Levels[clvl].Restarting ? ", Restarting" : "");
++
++      for (lvl = 0; lvl < cmRail->TopLevel && lvl <= clvl; lvl++)
++      {
++          CM_LEVEL *level = &cmRail->Levels[lvl];
++
++          sprintf (mapname, "%10s%2d", "Level", lvl);
++          DisplayMap (di, cmRail, mapname, level->SubordinateMap[clvl], nnodes,
++                      level->SubordinateMapValid[clvl] ? "" : "(invalid)");
++      }
++
++      sprintf (mapname, "%12s", "Local");
++      DisplayMap (di, cmRail, mapname, cmRail->Levels[clvl].LocalMap, nnodes, "");
++
++      sprintf (mapname, "%12s", "Subtree");
++      DisplayMap (di, cmRail, mapname, cmRail->Levels[clvl].SubTreeMap, nnodes, 
++                  cmRail->Levels[clvl].SubTreeMapValid ? "" : "(invalid)");
++
++      sprintf (mapname, "%12s", "Global");
++      DisplayMap (di, cmRail, mapname, cmRail->Levels[clvl].GlobalMap, nnodes, 
++                  cmRail->Levels[clvl].GlobalMapValid ? "" : "(invalid)");
++
++      sprintf (mapname, "%12s", "LastGlobal");
++      DisplayMap (di, cmRail, mapname, cmRail->Levels[clvl].LastGlobalMap, nnodes, "");
++    }
++}
++
++void
++DisplayNodeSgmts (DisplayInfo *di, CM_RAIL *cmRail)
++{
++    int   lvl;
++    int   sidx;
++    
++    (di->func)(di->arg, "%s: Node %d segments...\n", cmRail->Rail->Name, cmRail->NodeId);
++    
++    for (lvl = 0; lvl <= cmRail->TopLevel && lvl < cmRail->NumLevels; lvl++)
++    {
++      (di->func)(di->arg, "   level %d: ", lvl);
++      
++      for (sidx = 0; sidx < ((lvl == cmRail->TopLevel) ? 1 : cmRail->Levels[lvl].NumSegs); sidx++)
++      {
++          CM_SGMT *sgmt = &cmRail->Levels[lvl].Sgmts[sidx];
++              
++          if (sgmt->State == CM_SGMT_PRESENT)
++              (di->func)(di->arg, "[%d, in: %d out: %d %s%s]", 
++                      sgmt->NodeId,
++                      sgmt->AckSeq,
++                      sgmt->MsgSeq,
++                      sgmt->MsgAcked ? "A" : "-",
++                      sgmt->SendMaps ? "!" : "-");
++          else
++              (di->func)(di->arg, "[%s]", (sgmt->State == CM_SGMT_ABSENT ? "absent" :
++                               sgmt->State == CM_SGMT_WAITING ? "waiting" :
++                               sgmt->State == CM_SGMT_COMING ? "coming" : "UNKNOWN"));
++      }
++      (di->func)(di->arg, "\n");
++    }
++}
++
++
++static void
++StartConnecting (CM_RAIL *cmRail, CM_SGMT *sgmt, int NodeId, int Timestamp)
++{
++    int clvl;
++
++    CPRINTF4 (2, "%s: lvl %d subtree %d node %d -> connecting\n", cmRail->Rail->Name, sgmt->Level, sgmt->Sgmt, NodeId);
++
++    /* Only reconnect the same guy if he was reborn */
++    ASSERT (sgmt->State != CM_SGMT_PRESENT ||
++          (sgmt->NodeId == NodeId && sgmt->Timestamp != Timestamp));
++   
++    /* After we've connected to a new peer, we wait to receive
++     * STATEMAP_RESET before we accumulate changes and we wait for a
++     * complete map to be received before we propagate changes to other
++     * nodes.
++     *
++     * If I'm the subordinate, I can start sending maps right away, since
++     * the leader is ready for them already.  If I'm the leader, I hold off
++     * sending maps until I've seen the subordinate's first heartbeat,
++     * because the subordinate might miss my NOTIFY message, still think
++     * she's a leader candidate and ignore my heartbeats.
++     */
++    sgmt->SendMaps = (sgmt->Level == cmRail->TopLevel); /* I can send maps to my leader (she NOTIFIED me) */
++
++    for (clvl = sgmt->Level; clvl < cmRail->NumLevels; clvl++)
++    {
++      statemap_reset (sgmt->Maps[clvl].CurrentInputMap);
++      statemap_reset (sgmt->Maps[clvl].InputMap);
++      statemap_reset (sgmt->Maps[clvl].OutputMap);
++      
++      sgmt->Maps[clvl].InputMapValid = 0;
++      sgmt->Maps[clvl].OutputMapValid = 0;
++      sgmt->Maps[clvl].SentChanges = 0;
++
++      if (sgmt->Level == cmRail->TopLevel)    /* connection to leader */
++      {
++          ASSERT (sgmt->Sgmt == 0);
++          ASSERT (cmRail->Role == CM_ROLE_SUBORDINATE);
++
++          if (cmRail->Levels[clvl].SubTreeMapValid) /* already got a subtree map to send up */
++          {
++              statemap_setmap (sgmt->Maps[clvl].OutputMap, cmRail->Levels[clvl].SubTreeMap);
++              sgmt->Maps[clvl].OutputMapValid = 1;
++
++              statemap_clearchanges (cmRail->Levels[clvl].SubTreeMap);
++          }
++      }
++      else                                    /* connection to subordinate */
++      {
++          ASSERT (sgmt->Sgmt != cmRail->Levels[sgmt->Level].MySgmt);
++
++          if (cmRail->Levels[clvl].GlobalMapValid) /* already got a global map to broadcast */
++          {
++              statemap_setmap (sgmt->Maps[clvl].OutputMap, cmRail->Levels[clvl].GlobalMap);
++              sgmt->Maps[clvl].OutputMapValid = 1;
++          }
++      }
++    }
++    
++    /* Initialise sequence counters */
++    sgmt->MsgSeq = sgmt->AckSeq = 0;
++    sgmt->MsgAcked = 1;                       /* ready to send a new sequenced message */
++   
++    sgmt->State      = CM_SGMT_PRESENT;
++    sgmt->NodeId     = NodeId;
++    sgmt->UpdateTick = lbolt;
++    sgmt->Timestamp  = Timestamp;
++}
++
++static void
++StartSubTreeDiscovery (CM_RAIL *cmRail, CM_SGMT *sgmt)
++{
++    sgmt->State = CM_SGMT_WAITING;
++    sgmt->UpdateTick = lbolt;
++    sgmt->WaitingTick = lbolt;
++
++    if (sgmt->Level > 0)
++      __Schedule_Discovery (cmRail);
++}
++
++void
++StartSubordinateDiscovery (CM_RAIL *cmRail)
++{
++    int       i;
++    int       lvl = cmRail->TopLevel - 1;
++    CM_LEVEL *level = &cmRail->Levels[lvl];
++
++    ASSERT (lvl >= 0 && lvl < cmRail->NumLevels);
++
++    for (i = 0; i < level->NumSegs; i++)
++    {
++        CM_SGMT *sgmt = &level->Sgmts[i];
++      
++      if (i != level->MySgmt)         /* No-one should connect here */
++          StartSubTreeDiscovery (cmRail, sgmt);
++    }
++}
++
++void
++StartLeaderDiscovery (CM_RAIL *cmRail)
++{
++    int       i;
++    int       clvl;
++    CM_LEVEL *level = &cmRail->Levels[cmRail->TopLevel];
++
++    ASSERT (cmRail->TopLevel < cmRail->NumLevels);
++
++    for (clvl = cmRail->TopLevel; clvl < cmRail->NumLevels; clvl++)
++    {
++        cmRail->Levels[clvl].GlobalMapValid = 0;
++      cmRail->Levels[clvl].SubTreeMapValid = 0;
++        level->SubordinateMapValid[clvl] = 0;
++    }
++
++    for (i = 0; i < level->NumSegs; i++)
++    {
++        CM_SGMT *sgmt = &level->Sgmts[i];
++      
++      sgmt->State = CM_SGMT_ABSENT;
++    }
++
++    cmRail->DiscoverStartTick = lbolt;
++    cmRail->Role = CM_ROLE_LEADER_CANDIDATE;
++   
++    __Schedule_Discovery (cmRail);
++}
++
++static void
++RaiseTopLevel (CM_RAIL *cmRail)
++{
++    ASSERT (cmRail->NumLevels != 0);
++    ASSERT (cmRail->TopLevel < cmRail->NumLevels);
++
++    CPRINTF2 (2, "%s: RaiseTopLevel %d\n", cmRail->Rail->Name, cmRail->TopLevel + 1);
++
++    if (++cmRail->TopLevel == cmRail->NumLevels)      /* whole machine leader? */
++       cmRail->Role = CM_ROLE_LEADER;
++    else
++       StartLeaderDiscovery (cmRail);         /* look for my leader */
++
++    StartSubordinateDiscovery (cmRail);               /* and any direct subordinates */
++}
++
++static void
++LowerTopLevel (CM_RAIL *cmRail, int lvl)
++{
++    ASSERT (cmRail->NumLevels != 0);
++    ASSERT (lvl < cmRail->NumLevels);
++
++    CPRINTF2 (2, "%s: LowerTopLevel %d\n", cmRail->Rail->Name, lvl);
++
++    if (lvl == 0)
++      cmRail->Timestamp = lbolt;
++
++    cmRail->TopLevel = lvl;
++
++    StartLeaderDiscovery (cmRail);            /* look for my leader */
++}
++
++static int
++IShouldLead (CM_RAIL *cmRail, CM_MSG *msg)
++{
++    /* NB, this function MUST be consistently calculated on any nodes, just
++     * from the info supplied in the message.  Otherwise leadership
++     * arbitration during concurrent discovery will fail.
++     */
++    return (cmRail->NodeId < msg->Hdr.NodeId);
++}
++
++static int
++SumCheck (CM_MSG *msg)
++{
++    CM_HDR   *hdr   = &msg->Hdr;
++    uint16_t  sum   = hdr->Checksum;
++    uint16_t  nmaps = hdr->NumMaps;
++
++    if (nmaps > CM_MSG_MAXMAPS) {
++      printk ("SumCheck: nmaps %d > CM_MSG_MAXMAPS\n", nmaps);
++      return 0;
++    }
++    
++    if ((hdr->Type != CM_MSG_TYPE_HEARTBEAT) && nmaps != 0) {
++      printk ("SumCheck: type(%d) not HEARTBEAT and nmaps(%d) != 0\n", hdr->Type, nmaps);
++      return 0;
++    }
++
++    hdr->Checksum = 0;
++    
++    if (CheckSum ((char *)msg + CM_MSG_BASE(nmaps), CM_MSG_SIZE(nmaps)) != sum) {
++      printk ("SumCheck: checksum failed %x %x\n", CheckSum ((char *)msg + CM_MSG_BASE(nmaps), CM_MSG_SIZE(nmaps)), sum);
++
++      return 0;
++    }
++      
++    return 1;
++}
++
++static void
++ProcessMessage (EP_RAIL *rail, void *arg, void *msgbuf)
++{
++    CM_RAIL      *cmRail = (CM_RAIL *) arg;
++    CM_MSG         *msg    = (CM_MSG *) msgbuf;
++    CM_HDR         *hdr    = &msg->Hdr;
++    int             lvl;
++    int             sidx;
++    CM_LEVEL       *level;
++    CM_SGMT        *sgmt;
++    bitmap_t        seg;
++    int             i;
++    int            delay;
++    static long    tlast;
++    static int     count;
++
++    /* Poll the message Version field until the message has completely
++     * arrived in main memory. */
++    for (delay = 1; hdr->Version == EP_SYSTEMQ_UNRECEIVED && delay < EP_SYSTEMQ_UNRECEIVED_TLIMIT; delay <<= 1)
++      DELAY (delay);
++
++    /* Display a message every 60 seconds if we see an "old" format message */
++    if (hdr->Version == EP_SYSTEMQ_UNRECEIVED && (((lbolt - tlast) > 60*HZ) ? (count = 0) : ++count) < 1)
++    {
++      printk ("%s: received old protocol message (type %d from node %d)\n", cmRail->Rail->Name, 
++              ((uint8_t *) msg)[20], ((uint16_t *) msg)[4]);
++
++      tlast = lbolt;
++      goto finished;
++    }
++
++    if (hdr->Version != CM_MSG_VERSION || hdr->ParamHash != cmRail->ParamHash || hdr->MachineId != MachineId)
++    {
++      CPRINTF8 (1, "%s: invalid message : Version %08x (%08x) ParamHash %08x (%08x) MachineId %04x (%04x) Nodeid %d\n", cmRail->Rail->Name,
++                hdr->Version, CM_MSG_VERSION, hdr->ParamHash, cmRail->ParamHash, hdr->MachineId, MachineId, hdr->NodeId);
++      goto finished;
++    }
++
++    if (!SumCheck (msg))
++    {
++      printk ("%s: checksum failed on msg from %d?\n", cmRail->Rail->Name, hdr->NodeId);
++      goto finished;
++    }
++    
++    if (hdr->NodeId == cmRail->NodeId)                /* ignore my own broadcast */       
++    {
++      CPRINTF3 (6, "%s: node %d type %d: ignored (MESSAGE FROM ME)\n", 
++                cmRail->Rail->Name, hdr->NodeId, hdr->Type);
++
++      if (hdr->Type != CM_MSG_TYPE_DISCOVER_LEADER && hdr->Type != CM_MSG_TYPE_RESOLVE_LEADER)
++          printk ("%s: node %d type %d: ignored (MESSAGE FROM ME)\n", 
++                  cmRail->Rail->Name, hdr->NodeId, hdr->Type);
++      goto finished;
++    }
++
++    lvl = hdr->Level;
++    level = &cmRail->Levels[lvl];
++
++    if (BrokenLevel != -1 && (lvl >= ((BrokenLevel >> (cmRail->Rail->Number*4)) & 0xf)))                      /* Simulate broken network? */
++       goto finished;
++    
++    if (lvl >= cmRail->NumLevels ||           /* from outer space  */
++      hdr->NodeId < level->MinNodeId ||       /* from outside this level's subtree */
++      hdr->NodeId >= level->MinNodeId + level->NumNodes)
++    {
++      printk ("%s: lvl %d node %d type %d: ignored (%s)\n", 
++              cmRail->Rail->Name, lvl, hdr->NodeId, hdr->Type, 
++              lvl >= cmRail->NumLevels ? "level too big for machine" : "outside subtree");
++      goto finished;
++    }
++
++    sidx = SegmentNo (cmRail, hdr->NodeId, lvl);
++    sgmt = &level->Sgmts[sidx];
++    
++    switch (hdr->Type)
++    {
++    case CM_MSG_TYPE_RESOLVE_LEADER:
++      if (lvl >= cmRail->TopLevel)
++      {
++          CPRINTF4 (6, "%s: lvl %d sidx %d node %d RESOLVE_LEADER: ignored (above my level)\n", 
++                    cmRail->Rail->Name, lvl, sidx, hdr->NodeId);
++          break;
++      }
++
++      /* someone else thinks they lead at the same level as me */
++      CPRINTF4 (1, "%s: lvl %d sidx %d node %d RESOLVE_LEADER: !REJOIN (putsch)\n", 
++                cmRail->Rail->Name, lvl, sidx, hdr->NodeId);
++      
++      printk ("%s: lvl %d sidx %d node %d RESOLVE_LEADER: !REJOIN (putsch)\n", 
++              cmRail->Rail->Name, lvl, sidx, hdr->NodeId);
++      
++
++      SendMessage (cmRail, hdr->NodeId, lvl, CM_MSG_TYPE_REJOIN);
++      break;
++      
++    case CM_MSG_TYPE_DISCOVER_LEADER:
++      if (lvl > cmRail->TopLevel)
++      {
++          CPRINTF4 (6, "%s: lvl %d sidx %d node %d DISCOVER_LEADER: ignored (above my level)\n", 
++                    cmRail->Rail->Name, lvl, sidx, hdr->NodeId);
++          break;
++      }
++
++      if (sidx == level->MySgmt)              /* someone I led thinks they lead some of my subtrees */
++      {
++          CPRINTF4 (1, "%s: lvl %d sidx %d node %d DISCOVER_LEADER: !REJOIN (putsch)\n", 
++                    cmRail->Rail->Name, lvl, sidx, hdr->NodeId);
++
++          printk ("%s: lvl %d sidx %d node %d DISCOVER_LEADER: !REJOIN (putsch)\n", 
++                  cmRail->Rail->Name, lvl, sidx, hdr->NodeId);
++
++          SendMessage (cmRail, hdr->NodeId, hdr->Level, CM_MSG_TYPE_REJOIN);
++          break;
++      }       
++
++      if (lvl < cmRail->TopLevel)                     /* I'm the leader of this level */
++      {
++          if (sgmt->State == CM_SGMT_PRESENT &&       /* someone thinks someone I lead is dead */
++              sgmt->NodeId != hdr->NodeId)
++          {
++              /* My subordinate's death could be noticed by one of her peers
++               * before I do.  If she _is_ dead, I'll notice before long and
++               * NOTIFY this discover.  If this discover completes before I
++               * detect my subordinate's death, the discovering node will
++               * try to take over from me, and then I'll RESET her.
++               */
++              CPRINTF4 (6, "%s: lvl %d sidx %d node %d DISCOVER_LEADER: ignored (got established subordinate)\n", 
++                        cmRail->Rail->Name, lvl, sidx, hdr->NodeId);
++              return;
++          }
++
++          if (sgmt->State != CM_SGMT_PRESENT || /* New connection */
++              sgmt->Timestamp != hdr->Timestamp) /* new incarnation */
++              StartConnecting (cmRail, sgmt, hdr->NodeId, hdr->Timestamp);
++        
++          CPRINTF4 (2, "%s: lvl %d sidx %d node %d DISCOVER_LEADER: !NOTIFY)\n", 
++                    cmRail->Rail->Name, lvl, sidx, hdr->NodeId);
++        
++          SendToSgmt (cmRail, sgmt, CM_MSG_TYPE_NOTIFY);
++          break;
++      }
++
++      ASSERT (lvl == cmRail->TopLevel);
++
++      if (cmRail->Role == CM_ROLE_SUBORDINATE)
++      {
++          /* I think my leader is alive, in which case she'll NOTIFY this
++           * DISCOVER.  If she's dead, I'll start to become a leader
++           * candidate and handle this appropriately.
++           */
++          CPRINTF3 (6, "%s: lvl %d node %d DISCOVER: ignored (I'm a subordinate)\n", 
++                    cmRail->Rail->Name, lvl, hdr->NodeId);
++          break;
++      }
++       
++      ASSERT (cmRail->Role == CM_ROLE_LEADER_CANDIDATE);
++
++      /* A peer at this level is bidding for leadership along with me */
++      if (IShouldLead (cmRail, msg))
++      {
++          CPRINTF3 (6, "%s: lvl %d node %d DISCOVER: but I should lead\n", 
++                    cmRail->Rail->Name, lvl, hdr->NodeId);
++
++          /* So there _is_ someone there; She'll be seeing my DISCOVER
++           * messages and extending her discovery period, so that when I
++           * become leader, I'll NOTIFY her.  In the meantime I'll flag her
++           * activity, so she remains WAITING.
++           */
++          sgmt->UpdateTick = lbolt;
++          break;
++      }
++       
++      /* Defer to sender... */
++      CPRINTF3 (6, "%s: lvl %d node %d DISCOVER: delaying me becoming leader\n", 
++                cmRail->Rail->Name, lvl, hdr->NodeId);
++       
++      StartLeaderDiscovery (cmRail);
++      break;
++
++    case CM_MSG_TYPE_DISCOVER_SUBORDINATE:
++      if (lvl <= cmRail->TopLevel)
++      {
++          CPRINTF3 (6, "%s: lvl %d node %d DISCOVER_SUBORDINATE: ignored (from my subtree)\n", 
++                    cmRail->Rail->Name, lvl, hdr->NodeId);
++          break;
++      }
++       
++      if (cmRail->Role != CM_ROLE_LEADER_CANDIDATE)
++      {
++          CPRINTF3 (6, "%s: lvl %d node %d DISCOVER_SUBORDINATE: ignored (I'm not looking for a leader)\n", 
++                    cmRail->Rail->Name, lvl, hdr->NodeId);
++          break;
++      }
++       
++      if (hdr->Level > cmRail->BroadcastLevel && AFTER (lbolt, cmRail->BroadcastLevelTick + EP_WITHDRAW_TIMEOUT))
++      {
++          CPRINTF3 (6, "%s: lvl %d node %d DISCOVER_SUBORDINATE: ignored (broadcast level too low)\n",
++                    cmRail->Rail->Name, lvl, hdr->NodeId);
++          break;
++      }
++
++      CPRINTF3 (2, "%s: lvl %d node %d DISCOVER_SUBORDINATE: !IMCOMING\n", 
++                cmRail->Rail->Name, lvl, hdr->NodeId);
++       
++      SendMessage (cmRail, hdr->NodeId, hdr->Level, CM_MSG_TYPE_IMCOMING);
++      break;
++
++    case CM_MSG_TYPE_IMCOMING:
++      if (lvl > cmRail->TopLevel ||           /* from peer or node above me */
++          sgmt->State == CM_SGMT_PRESENT ||   /* already got a subtree */
++          sgmt->State == CM_SGMT_ABSENT)      /* already written off this subtree */
++      {
++          CPRINTF4 (2, "%s: lvl %d sidx %d node %d IMCOMING: ignored\n", cmRail->Rail->Name, lvl, sidx, hdr->NodeId);
++          break;
++      }
++
++      CPRINTF4 (2, "%s: lvl %d sidx %d node %d IMCOMING: waiting...\n", cmRail->Rail->Name, lvl, sidx, hdr->NodeId);
++
++      sgmt->State = CM_SGMT_COMING;
++      sgmt->UpdateTick = lbolt;
++      break;
++       
++    case CM_MSG_TYPE_NOTIFY:
++      if (cmRail->Role != CM_ROLE_LEADER_CANDIDATE || /* I'm not looking for a leader */
++          lvl != cmRail->TopLevel)            /* at this level */
++      {
++          /* If this person really should be my leader, my existing leader
++           * will time out, and I'll discover this one. */
++          CPRINTF4 (2, "%s: lvl %d node %d NOTIFY: ignored (%s)\n", 
++                    cmRail->Rail->Name, lvl, hdr->NodeId,
++                    lvl < cmRail->TopLevel ? "already leader" : 
++                    lvl > cmRail->TopLevel ? "lvl too high" : "already subordinate");
++          break;
++      }
++
++      CPRINTF3 (2, "%s: lvl %d node %d NOTIFY: becoming subordinate\n", 
++                cmRail->Rail->Name, lvl, hdr->NodeId);
++       
++      cmRail->Role = CM_ROLE_SUBORDINATE;             /* Now I've found my level */
++      StartConnecting (cmRail, &level->Sgmts[0], hdr->NodeId, hdr->Timestamp);
++      break;
++
++    case CM_MSG_TYPE_HEARTBEAT:
++      if (lvl > cmRail->TopLevel)
++      {
++          CPRINTF3 (2, "%s: lvl %d node %d H/BEAT: ignored (lvl too high)\n", 
++                    cmRail->Rail->Name, lvl, hdr->NodeId);
++          break;
++      }
++
++      if (lvl == cmRail->TopLevel)                    /* heartbeat from my leader */
++      {
++          if (cmRail->Role == CM_ROLE_LEADER_CANDIDATE) /* but I've not got one */
++          {
++              /* I'm probably a new incarnation of myself; I'll keep doing
++               * discovery until my previous existence's leader NOTIFY's me.
++               * If I was this node's leader, she'll time me out (I'm not
++               * sending heartbeats to her) and we'll fight it out for
++               * leadership. */
++              CPRINTF3 (2, "%s: lvl %d node %d H/BEAT ignored (no leader)\n", 
++                        cmRail->Rail->Name, lvl, hdr->NodeId);
++              break;
++          }
++          sidx = 0;
++          sgmt = &level->Sgmts[0];
++      }
++      
++      if (sgmt->State != CM_SGMT_PRESENT ||   /* not fully connected with this guy */
++          sgmt->NodeId != hdr->NodeId ||      /* someone else impersonating my peer */
++          sgmt->Timestamp != hdr->Timestamp)  /* new incarnation of my peer */
++      {
++          CPRINTF4 (1, "%s: lvl %d sidx %d node %d H/BEAT: !REJOIN\n", 
++                    cmRail->Rail->Name, lvl, sidx, hdr->NodeId);
++        
++          printk ("%s: lvl %d sidx %d node %d H/BEAT: !REJOIN %s\n",
++                  cmRail->Rail->Name, lvl, sidx, hdr->NodeId,
++                  sgmt->State != CM_SGMT_PRESENT ? "not present" :
++                  sgmt->NodeId != hdr->NodeId ? "someone else" : "new incarnation");
++          
++          SendMessage (cmRail, hdr->NodeId, hdr->Level, CM_MSG_TYPE_REJOIN);
++          break;
++      }
++
++      if (!((hdr->Seq == sgmt->AckSeq) ||     /* NOT duplicate message or */
++            (hdr->Seq == (CM_SEQ)(sgmt->AckSeq + 1))) || /* expected message */
++          !((hdr->AckSeq == sgmt->MsgSeq) ||  /* NOT expected ack or */
++            (hdr->AckSeq == (CM_SEQ)(sgmt->MsgSeq - 1)))) /* duplicate ack */
++      {
++          CPRINTF9 (1, "%s: lvl %d sidx %d node %d type %d: H/BEAT !REJOIN (out-of-seq) M(%d,a%d) S%d,A%d\n", 
++                    cmRail->Rail->Name, lvl, sidx, hdr->NodeId, hdr->Type, 
++                    (int)hdr->Seq, (int)hdr->AckSeq, (int)sgmt->MsgSeq, (int)sgmt->AckSeq);
++       
++          printk ("%s: lvl %d sidx %d node %d type %d: H/BEAT !REJOIN (out-of-seq) M(%d,a%d) S%d,A%d\n", 
++                  cmRail->Rail->Name, lvl, sidx, hdr->NodeId, hdr->Type, 
++                  (int)hdr->Seq, (int)hdr->AckSeq, (int)sgmt->MsgSeq, (int)sgmt->AckSeq);
++       
++          SendMessage (cmRail, hdr->NodeId, hdr->Level, CM_MSG_TYPE_REJOIN);
++          break;
++      }
++
++      IncrStat (cmRail, HeartbeatsRcvd);
++
++      sgmt->UpdateTick = lbolt;
++      sgmt->SendMaps = 1;
++
++      if (sgmt->MsgSeq == hdr->AckSeq)                /* acking current message */
++          sgmt->MsgAcked = 1;                 /* can send the next one */
++
++      if (hdr->Seq == sgmt->AckSeq)           /* discard duplicate (or NULL heartbeat) */
++      {
++          CPRINTF6 (6, "%s: lvl %d sidx %d node %d type %d: %s H/BEAT\n", 
++                    cmRail->Rail->Name, lvl, sidx, hdr->NodeId, hdr->Type,
++                    hdr->NumMaps == 0 ? "null" : "duplicate");
++          break;
++      }
++
++      CPRINTF7 (6, "%s: lvl %d sidx %d node %d type %d: seq %d maps %d H/BEAT\n", 
++                cmRail->Rail->Name, lvl, sidx, hdr->NodeId, hdr->Type, hdr->Seq, hdr->NumMaps);
++
++      sgmt->AckSeq = hdr->Seq;                        /* ready to receive next one */
++       
++      for (i = 0; i < hdr->NumMaps; i++)
++      {
++          CM_STATEMAP_ENTRY *map  = &msg->Payload.Statemaps[CM_MSG_MAP(i)];
++          int                clvl = map->level;
++          
++          if (clvl < 0)                       /* end of message */
++              break;
++
++          if (clvl < sgmt->Level)             /* bad level */
++          {
++              CPRINTF6 (1, "%s: lvl %d sidx %d node %d type %d: H/BEAT !REJOIN (bad clevel %d)\n", 
++                        cmRail->Rail->Name, lvl, sidx, hdr->NodeId, hdr->Type, clvl);
++
++              SendMessage (cmRail, hdr->NodeId, hdr->Level, CM_MSG_TYPE_REJOIN);
++              goto finished;
++          }
++
++          if (map->offset == STATEMAP_NOMORECHANGES) /* end of atomic changes */
++          {
++              if (!sgmt->Maps[clvl].InputMapValid || /* not set InputMap yet */
++                  statemap_changed (sgmt->Maps[clvl].CurrentInputMap)) /* previously applied changes */
++              {
++                  CPRINTF3 (4, "%s: received new clvl %d map from %d\n", cmRail->Rail->Name, clvl, sgmt->NodeId);
++
++                  statemap_setmap (sgmt->Maps[clvl].InputMap, sgmt->Maps[clvl].CurrentInputMap);
++                  sgmt->Maps[clvl].InputMapValid = 1;
++
++                  statemap_clearchanges (sgmt->Maps[clvl].CurrentInputMap);
++              }
++              continue;
++          }
++          
++          seg = ((bitmap_t)map->seg[0])
++              | (((bitmap_t)map->seg[1]) << 16)
++#if (BT_ULSHIFT == 6)
++              | (((bitmap_t)map->seg[2]) << 32)
++              | (((bitmap_t)map->seg[3]) << 48)
++#elif (BT_ULSHIFT != 5)
++#error "Bad value for BT_ULSHIFT"
++#endif
++              ;
++          statemap_setseg (sgmt->Maps[clvl].CurrentInputMap, map->offset, seg);
++      }
++      break;
++
++    case CM_MSG_TYPE_REJOIN:
++      CPRINTF5 (1, "%s: lvl %d sidx %d node %d type %d: REJOIN\n",
++                cmRail->Rail->Name, lvl, sidx, hdr->NodeId, hdr->Type);
++      printk ("%s: lvl %d sidx %d node %d type %d: REJOIN\n", 
++              cmRail->Rail->Name, lvl, sidx, hdr->NodeId, hdr->Type);
++
++      LowerTopLevel (cmRail, 0);
++
++      IncrStat (cmRail, RejoinRequest);
++      break;
++
++    default:
++      printk ("%s: lvl=%d unknown message type %d\n", cmRail->Rail->Name, lvl, hdr->Type);
++      break;
++    }
++ finished:
++    hdr->Version = EP_SYSTEMQ_UNRECEIVED;
++}
++
++static void
++PollInputQueues (CM_RAIL *cmRail)
++{
++    ep_poll_inputq (cmRail->Rail, cmRail->IntrQueue, 0, ProcessMessage, cmRail);
++    ep_poll_inputq (cmRail->Rail, cmRail->PolledQueue, 0, ProcessMessage, cmRail);
++}
++
++static void
++IntrQueueCallback (EP_RAIL *rail, void *arg)
++{
++    CM_RAIL *cmRail = (CM_RAIL *) arg;
++    unsigned long flags;
++
++    /* If the lock is held, then don't bother spinning for it, 
++     * since the messages will be received at this, or the
++     * next heartbeat */
++    local_irq_save (flags);
++    if (spin_trylock (&cmRail->Lock))
++    {
++      if (AFTER (lbolt, cmRail->NextRunTime + MSEC2TICKS(CM_TIMER_SCHEDULE_TIMEOUT)))
++          printk ("%s: heartbeat timer stuck - scheduled\n", cmRail->Rail->Name);
++      else
++          ep_poll_inputq (rail, cmRail->IntrQueue, 0, ProcessMessage, cmRail);
++      spin_unlock (&cmRail->Lock);
++    }
++    local_irq_restore (flags);
++}
++
++char *
++sprintClPeers (char *str, CM_RAIL *cmRail, int clvl)
++{
++   int clLo     = cmRail->Levels[clvl].MinNodeId;
++   int clHi     = clLo + cmRail->Levels[clvl].NumNodes - 1;
++   int subClLo  = (clvl == 0) ? cmRail->NodeId : cmRail->Levels[clvl - 1].MinNodeId;
++   int subClHi  = subClLo + ((clvl == 0) ? 0 : cmRail->Levels[clvl - 1].NumNodes - 1);
++   
++   if (subClHi == clHi)
++      sprintf (str, "[%d-%d]", clLo, subClLo - 1);
++   else if (subClLo == clLo)
++      sprintf (str, "[%d-%d]", subClHi + 1, clHi);
++   else
++      sprintf (str, "[%d-%d][%d-%d]", clLo, subClLo - 1, subClHi + 1, clHi);
++
++   return (str);
++}
++
++static void
++RestartComms (CM_RAIL *cmRail, int clvl)
++{
++    int             base;
++    int             nodeId;
++    int             lstat;
++    int             numClNodes;
++    int             subClMin;
++    int             subClMax;
++    int             myClId;
++    int             thisClId;
++    
++    myClId     = ClusterIds (cmRail, clvl, &subClMin, &subClMax);
++    base       = myClId * CM_GSTATUS_BITS;
++    numClNodes = cmRail->Levels[clvl].NumNodes;
++
++    statemap_setbits (cmRail->Levels[clvl].LocalMap, base, 
++                    CM_GSTATUS_CLOSING | CM_GSTATUS_MAY_START | CM_GSTATUS_RESTART, CM_GSTATUS_BITS);
++    cmRail->Levels[clvl].Restarting = 1;
++
++    if (cmRail->Levels[clvl].Online)
++    {
++      cmRail->Levels[clvl].Online = 0;
++      
++      for (thisClId = 0; thisClId < numClNodes; thisClId++)
++      {
++          if (thisClId == subClMin)   /* skip sub-cluster; it's just someone in this cluster */
++          {                           /* that wants me to restart */
++              thisClId = subClMax;
++              continue;
++          }
++          
++          nodeId = cmRail->Levels[clvl].MinNodeId + thisClId;
++          base   = thisClId * CM_GSTATUS_BITS;
++          lstat  = statemap_getbits (cmRail->Levels[clvl].LocalMap, base, CM_GSTATUS_BITS);
++          
++          if ((lstat & CM_GSTATUS_ACK_MASK) == CM_GSTATUS_MAY_RUN)
++          {
++              switch (ep_disconnect_node (cmRail->Rail, nodeId))
++              {
++              case EP_NODE_CONNECTING:
++                  /* gstat must == RUNNING */
++                  cmRail->Levels[clvl].Connected--;
++                  break;
++              case EP_NODE_DISCONNECTED:
++                  /* CLOSING || STARTING || (lstat & RESTART) */
++                  break;
++              }
++          }
++      }
++    }
++}
++
++static void
++UpdateGlobalStatus (CM_RAIL *cmRail)
++{
++    char            clNodeStr[32];                            /* [%d-%d][%d-%d] */
++    int             nodeId;
++    int             offset;
++    int             base;
++    bitmap_t        gstat;
++    bitmap_t        lgstat;
++    bitmap_t        lstat;
++    int             clvl;
++    int             numClNodes;
++    int             subClMin;
++    int             subClMax;
++    int             myClId;
++    int             thisClId;
++    int             lastClId;
++
++    for (clvl = 0; clvl < cmRail->NumLevels; clvl++)
++    {
++      if (!cmRail->Levels[clvl].GlobalMapValid || /* not got the global map yet */
++          !statemap_changed (cmRail->Levels[clvl].GlobalMap)) /* no changes to respond to */
++      {
++          CPRINTF2 (6, "%s: Got invalid or unchanged clvl %d global map\n", cmRail->Rail->Name, clvl);
++          continue;
++      }
++      
++      CPRINTF2 (5, "%s: Got valid changed clvl %d global map\n", cmRail->Rail->Name, clvl);
++      
++      lastClId = -1;
++      myClId = ClusterIds (cmRail, clvl, &subClMin, &subClMax);
++      numClNodes = cmRail->Levels[clvl].NumNodes;
++      
++      while ((offset = statemap_findchange (cmRail->Levels[clvl].GlobalMap, &gstat, 1)) >= 0)
++      {
++          /*
++           * Check every node that this segment covers - however
++           * if the last node we checked in the previous segmemt
++           * is also the first node in this segment, then skip
++           * it.
++           */
++          if ((thisClId = (offset/CM_GSTATUS_BITS)) == lastClId)
++              thisClId++;
++          lastClId = (offset + BT_NBIPUL - 1)/CM_GSTATUS_BITS;
++          
++          /* check each node that might have changed */
++          for ( ; thisClId <= lastClId && thisClId < numClNodes; thisClId++)
++          {
++              base = thisClId * CM_GSTATUS_BITS;
++              nodeId = cmRail->Levels[clvl].MinNodeId + thisClId;
++
++              if (thisClId >= subClMin && thisClId <= subClMax) /* skip sub-cluster */
++                  continue;
++
++              /* This isn't me; I need to sense what this node is driving
++               * (just the starting and running bits) and respond
++               * appropriately...
++               */
++              lgstat = statemap_getbits (cmRail->Levels[clvl].LastGlobalMap, base, CM_GSTATUS_BITS) & CM_GSTATUS_STATUS_MASK;
++              gstat  = statemap_getbits (cmRail->Levels[clvl].GlobalMap,     base, CM_GSTATUS_BITS) & CM_GSTATUS_STATUS_MASK;
++
++              if (lgstat == gstat)            /* no change in peer state */
++                  continue;
++
++              CPRINTF5 (3, "%s: Node %d: lgstat %s, gstat %s, lstat %s\n", cmRail->Rail->Name, nodeId,
++                        GlobalStatusString (cmRail->Levels[clvl].LastGlobalMap, thisClId),
++                        GlobalStatusString (cmRail->Levels[clvl].GlobalMap, thisClId),
++                        GlobalStatusString (cmRail->Levels[clvl].LocalMap, thisClId));
++
++              /* What I'm currently driving as my acknowledgement */
++              lstat = statemap_getbits (cmRail->Levels[clvl].LocalMap, base, CM_GSTATUS_BITS);
++
++              switch (gstat)
++              {
++              case CM_GSTATUS_STARTING:
++                  if ((lgstat == CM_GSTATUS_ABSENT || lgstat == CM_GSTATUS_CLOSING) && lstat == CM_GSTATUS_MAY_START)
++                  {
++                      CPRINTF2 (1, "%s: ===================node %d STARTING\n", cmRail->Rail->Name, nodeId);
++                      
++                      ASSERT (cmRail->Rail->Nodes[nodeId].State == EP_NODE_DISCONNECTED);
++
++                      statemap_setbits (cmRail->Levels[clvl].LocalMap, base, CM_GSTATUS_MAY_RUN, CM_GSTATUS_BITS);
++                      continue;
++                  }
++                  break;
++                  
++              case CM_GSTATUS_RUNNING:
++                  if ((lgstat == CM_GSTATUS_ABSENT   && lstat == CM_GSTATUS_MAY_START) ||
++                      (lgstat == CM_GSTATUS_STARTING && lstat == CM_GSTATUS_MAY_RUN))
++                  {
++                      CPRINTF3 (1, "%s: ===================node %d%s RUNNING\n", cmRail->Rail->Name, nodeId,
++                                lgstat == CM_GSTATUS_ABSENT ? " Already" : "");
++
++                      ASSERT (cmRail->Rail->Nodes[nodeId].State == EP_NODE_DISCONNECTED);
++
++                      if (cmRail->Levels[clvl].Online)
++                      {
++                          ep_connect_node (cmRail->Rail, nodeId);
++
++                          cmRail->Levels[clvl].Connected++;
++                      }
++
++                      statemap_setbits (cmRail->Levels[clvl].LocalMap, base, CM_GSTATUS_MAY_RUN, CM_GSTATUS_BITS);
++                      continue;
++                  }
++                  break;
++                  
++              case CM_GSTATUS_CLOSING:
++                  CPRINTF4 (1, "%s: ===================node %d CLOSING%s%s\n", cmRail->Rail->Name, nodeId,
++                            (lstat & CM_GSTATUS_RESTART) ? " for Restart" : "",
++                            cmRail->Levels[clvl].Online ? "" : " (offline)");
++
++                  if ((lstat & CM_GSTATUS_ACK_MASK) == CM_GSTATUS_MAY_RUN)
++                  {
++                      switch (ep_disconnect_node (cmRail->Rail, nodeId))
++                      {
++                      case EP_NODE_CONNECTING:
++                          cmRail->Levels[clvl].Connected--;
++                          /* DROPTHROUGH */
++                      case EP_NODE_DISCONNECTED:
++                          lstat = CM_GSTATUS_MAY_START;
++                          break;
++                      }
++                  }
++
++                  if ((lstat & CM_GSTATUS_ACK_MASK) == CM_GSTATUS_MAY_START) /* clear restart if we've disconnected */
++                      statemap_setbits (cmRail->Levels[clvl].LocalMap, base, CM_GSTATUS_MAY_START, CM_GSTATUS_BITS);
++                  continue;
++                  
++              default:
++                  break;
++              }
++
++              /* "unexpected" state change forces me to ask her to restart */
++              if (! (lstat & CM_GSTATUS_RESTART))             /* not requesting restart already */
++              {
++                  CPRINTF5 (1, "%s: ===================node %d %s, old %s new %s\n", cmRail->Rail->Name, nodeId,
++                            (gstat == CM_GSTATUS_ABSENT)  ? "ABSENT" : "REQUEST RESTART", 
++                            GlobalStatusString (cmRail->Levels[clvl].LastGlobalMap, thisClId),
++                            GlobalStatusString (cmRail->Levels[clvl].GlobalMap, thisClId));
++
++                  /* request restart */
++                  if (cmRail->Levels[clvl].Online && lstat == CM_GSTATUS_MAY_RUN)
++                  {
++                      switch (ep_disconnect_node (cmRail->Rail, nodeId))
++                      {
++                      case EP_NODE_CONNECTING:
++                          cmRail->Levels[clvl].Connected--;
++                          /* DROPTHROUGH */
++                      case EP_NODE_DISCONNECTED:
++                          lstat = CM_GSTATUS_MAY_START;
++                          break;
++                      }
++                  }
++
++                  statemap_setbits (cmRail->Levels[clvl].LocalMap, base, lstat | CM_GSTATUS_RESTART, CM_GSTATUS_BITS);
++                  continue;
++              }
++
++              continue;
++          }
++      }
++    
++      /* Now check myself - see what everyone else thinks I'm doing */
++      base   = myClId * CM_GSTATUS_BITS;
++      lstat  = statemap_getbits (cmRail->Levels[clvl].LocalMap,  base, CM_GSTATUS_BITS);
++      gstat  = statemap_getbits (cmRail->Levels[clvl].GlobalMap, base, CM_GSTATUS_BITS);
++      lgstat = statemap_getbits (cmRail->Levels[clvl].LastGlobalMap, base, CM_GSTATUS_BITS);
++
++      if (lgstat == gstat)                    /* my state in this cluster hasn't changed */
++      {
++          CPRINTF3 (6, "%s: my clvl %d global status unchanged from %s\n", cmRail->Rail->Name,
++                    clvl, GlobalStatusString (cmRail->Levels[clvl].GlobalMap, myClId));
++          goto all_done;
++      }
++
++      if ((gstat & CM_GSTATUS_RESTART) != 0)  /* someone wants me to restart */
++      {
++          if ((lstat & CM_GSTATUS_STATUS_MASK) == CM_GSTATUS_CLOSING) /* I'm already restarting */
++              goto all_done;
++          
++          CPRINTF2 (1, "%s: ===================RESTART REQUEST from %s\n", cmRail->Rail->Name,
++                    sprintClPeers (clNodeStr, cmRail, clvl));
++          
++          printk ("%s: Restart Request from %s\n", cmRail->Rail->Name,
++                  sprintClPeers (clNodeStr, cmRail, clvl));
++          
++          RestartComms (cmRail, clvl);
++          goto all_done;
++      }
++      
++      CPRINTF6 (5, "%s: clvl %d: lgstat %s gstat %s, lstat %s%s\n", cmRail->Rail->Name, clvl,
++                GlobalStatusString (cmRail->Levels[clvl].LastGlobalMap, myClId),
++                GlobalStatusString (cmRail->Levels[clvl].GlobalMap, myClId),
++                GlobalStatusString (cmRail->Levels[clvl].LocalMap, myClId),
++                (gstat != lstat) ? " (IGNORED)" : "");
++                      
++      if (gstat != lstat)                     /* not everyone agrees with me */
++          goto all_done;
++
++      switch (lstat)
++      {
++      default:
++          ASSERT (0);                         /* I never drive this */
++          
++      case CM_GSTATUS_CLOSING | CM_GSTATUS_MAY_START: /* I can restart now (have seen restart go away) */
++          ASSERT (!cmRail->Levels[clvl].Online);
++          
++          CPRINTF2 (1,"%s: ===================NODES %s AGREE I MAY START\n", cmRail->Rail->Name,
++                    sprintClPeers (clNodeStr, cmRail, clvl));
++          printk ("%s: ===================NODES %s AGREE I MAY START\n", cmRail->Rail->Name,
++                  sprintClPeers (clNodeStr, cmRail, clvl));
++          
++          statemap_setbits (cmRail->Levels[clvl].LocalMap, base, 
++                            CM_GSTATUS_STARTING | CM_GSTATUS_MAY_RUN, CM_GSTATUS_BITS);
++          goto all_done;
++          
++      case CM_GSTATUS_STARTING | CM_GSTATUS_MAY_RUN:
++          ASSERT (!cmRail->Levels[clvl].Online);
++          
++          CPRINTF2 (1, "%s: ===================NODES %s AGREE I MAY RUN\n", cmRail->Rail->Name,
++                    sprintClPeers (clNodeStr, cmRail, clvl));
++          printk ("%s: ===================NODES %s AGREE I MAY RUN\n", cmRail->Rail->Name,
++                  sprintClPeers (clNodeStr, cmRail, clvl));
++          
++          statemap_setbits (cmRail->Levels[clvl].LocalMap, base, 
++                            CM_GSTATUS_RUNNING | CM_GSTATUS_MAY_RUN, CM_GSTATUS_BITS);
++          goto all_done;
++          
++      case CM_GSTATUS_RUNNING | CM_GSTATUS_MAY_RUN:
++          if (! cmRail->Levels[clvl].Online)
++          {
++              CPRINTF2 (1, "%s: ===================NODES %s AGREE I'M RUNNING\n", cmRail->Rail->Name,
++                        sprintClPeers (clNodeStr, cmRail, clvl));
++              printk ("%s: ===================NODES %s AGREE I'M RUNNING\n", cmRail->Rail->Name,
++                      sprintClPeers (clNodeStr, cmRail, clvl));
++              
++              cmRail->Levels[clvl].Online = 1;
++              
++              for (thisClId = 0; thisClId < numClNodes; thisClId++)
++              {
++                  if (thisClId == subClMin)   /* skip sub-cluster */
++                  {
++                      thisClId = subClMax;
++                      continue;
++                  }
++                  
++                  nodeId = cmRail->Levels[clvl].MinNodeId + thisClId;
++                  
++                  base  = thisClId * CM_GSTATUS_BITS;
++                  lstat = statemap_getbits (cmRail->Levels[clvl].LocalMap, base, CM_GSTATUS_BITS);
++                  gstat = statemap_getbits (cmRail->Levels[clvl].GlobalMap, base, CM_GSTATUS_BITS) & CM_GSTATUS_STATUS_MASK;
++                  
++                  /* Only connect to her if I see her as running and I'm not requesting her 
++                   * to restart - this means that I was offline when I saw her transition
++                   * to running and haven't seen her in a "bad" state since. */
++                  if (gstat == CM_GSTATUS_RUNNING && ! (lstat & CM_GSTATUS_RESTART))
++                  {
++                      CPRINTF5 (1, "%s: node %d lgstat %s gstat %s, lstat %s -> CONNECT\n", cmRail->Rail->Name, nodeId,
++                                GlobalStatusString (cmRail->Levels[clvl].LastGlobalMap, thisClId),
++                                GlobalStatusString (cmRail->Levels[clvl].GlobalMap, thisClId),
++                                GlobalStatusString (cmRail->Levels[clvl].LocalMap, thisClId));
++                      
++                      if (lstat == CM_GSTATUS_MAY_START)
++                          statemap_setbits (cmRail->Levels[clvl].LocalMap, base, CM_GSTATUS_MAY_RUN, CM_GSTATUS_BITS);
++
++                      ep_connect_node (cmRail->Rail, nodeId);
++
++                      cmRail->Levels[clvl].Connected++;
++                  }
++              }
++          }
++          goto all_done;
++      }
++
++    all_done:
++      statemap_setmap (cmRail->Levels[clvl].LastGlobalMap, cmRail->Levels[clvl].GlobalMap);
++    }
++}
++
++static void
++ReduceGlobalMap (CM_RAIL *cmRail, int clvl)
++{
++    int       lvl;
++    int       sidx;
++    int       recompute;
++    CM_LEVEL *level;
++    int       cTopLevel;
++    int       cRole;
++
++    if (clvl < cmRail->TopLevel)
++    {
++      cTopLevel = clvl + 1;
++      cRole = CM_ROLE_LEADER;
++    }
++    else
++    {
++      cTopLevel = cmRail->TopLevel;
++      cRole = cmRail->Role;
++    }
++    
++    /* Update cmRail->Levels[*].SubordinateMap[clvl] for all subordinate levels */
++    for (lvl = 0; lvl < cTopLevel; lvl++)
++    {
++      level = &cmRail->Levels[lvl];
++
++      /* We need to recompute this level's statemap if...
++       * . Previous level's statemap has changes to propagate OR
++       * . This level's statemap has not been computed yet OR
++       * . A subordinate at this level has sent me a change.
++       * Note that we can only do this if all subordinates from this
++       * level down are present with valid statemaps, or absent (i.e. not
++       * timing out).
++       */
++
++      ASSERT (lvl == 0 || cmRail->Levels[lvl - 1].SubordinateMapValid[clvl]);
++
++      recompute = !level->SubordinateMapValid[clvl] ||
++                  (lvl > 0 && statemap_changed (cmRail->Levels[lvl - 1].SubordinateMap[clvl]));
++        
++      for (sidx = 0; sidx < level->NumSegs; sidx++)
++      {
++          CM_SGMT *sgmt = &level->Sgmts[sidx];
++
++          if (!(sgmt->State == CM_SGMT_ABSENT || /* absent nodes contribute zeros */
++                (sgmt->State == CM_SGMT_PRESENT && /* present nodes MUST have received a map to contribute */
++                 sgmt->Maps[clvl].InputMapValid)))
++          {
++              CPRINTF5 (5, "%s: waiting for clvl %d lvl %d seg %d node %d\n", cmRail->Rail->Name,
++                        clvl, lvl, sidx, sgmt->NodeId);
++
++              /* Gotta wait for this guy, so we can't compute this level,
++               * or any higher levels. */
++              return;
++          }
++
++          if (statemap_changed (sgmt->Maps[clvl].InputMap))
++          {
++              ASSERT (sgmt->Maps[clvl].InputMapValid);
++
++              recompute = 1;
++
++              CPRINTF7 (5, "%s: %s clvl %d map from @ %d %d (%d) - %s\n",
++                        cmRail->Rail->Name, sgmt->State == CM_SGMT_ABSENT ? "newly absent" : "got new",
++                        clvl, lvl, sidx, sgmt->NodeId, 
++                        MapString ("Input", sgmt->Maps[clvl].InputMap, cmRail->Levels[clvl].NumNodes, ""));
++          }
++      }
++
++      if (recompute)
++      {
++          if (lvl == 0)
++              statemap_reset (cmRail->Levels[clvl].TmpMap);
++          else
++          {
++              ASSERT (cmRail->Levels[lvl - 1].SubordinateMapValid[clvl]);
++              
++              statemap_copy (cmRail->Levels[clvl].TmpMap, cmRail->Levels[lvl - 1].SubordinateMap[clvl]);
++              statemap_clearchanges (cmRail->Levels[lvl - 1].SubordinateMap[clvl]);
++          }
++       
++          for (sidx = 0; sidx < level->NumSegs; sidx++)
++          {
++              CM_SGMT *sgmt = &level->Sgmts[sidx];
++              
++              if (sgmt->State != CM_SGMT_ABSENT)      /* absent nodes contribute zeroes */
++              {
++                  ASSERT (sgmt->State == CM_SGMT_PRESENT);
++                  ASSERT (sgmt->Maps[clvl].InputMapValid);
++                  statemap_ormap (cmRail->Levels[clvl].TmpMap, sgmt->Maps[clvl].InputMap);
++              }
++              statemap_clearchanges (sgmt->Maps[clvl].InputMap);
++          }
++
++          statemap_setmap (level->SubordinateMap[clvl], cmRail->Levels[clvl].TmpMap);
++          level->SubordinateMapValid[clvl] = 1;
++
++          CPRINTF4 (5, "%s: recompute clvl %d level %d statemap - %s\n", cmRail->Rail->Name, clvl, lvl,
++                    MapString ("level", level->SubordinateMap[clvl], cmRail->Levels[clvl].NumNodes, ""));
++      }
++    }
++
++    if (cRole == CM_ROLE_LEADER_CANDIDATE)    /* don't know this cluster's leader yet */
++      return;
++
++    ASSERT (cTopLevel == 0 || cmRail->Levels[cTopLevel - 1].SubordinateMapValid[clvl]);
++
++    /* Update SubTreeMap */
++    
++    if (!cmRail->Levels[clvl].SubTreeMapValid || 
++      statemap_changed (cmRail->Levels[clvl].LocalMap) ||
++      (cTopLevel > 0 && statemap_changed (cmRail->Levels[cTopLevel - 1].SubordinateMap[clvl])))
++    {
++      statemap_copy (cmRail->Levels[clvl].TmpMap, cmRail->Levels[clvl].LocalMap);
++      statemap_clearchanges (cmRail->Levels[clvl].LocalMap);
++
++      if (cTopLevel > 0)
++      {
++          statemap_ormap (cmRail->Levels[clvl].TmpMap, cmRail->Levels[cTopLevel - 1].SubordinateMap[clvl]);
++          statemap_clearchanges (cmRail->Levels[cTopLevel - 1].SubordinateMap[clvl]);
++      }
++
++      statemap_setmap (cmRail->Levels[clvl].SubTreeMap, cmRail->Levels[clvl].TmpMap);
++      cmRail->Levels[clvl].SubTreeMapValid = 1;
++
++      CPRINTF3 (5, "%s: recompute clvl %d subtree map - %s\n", cmRail->Rail->Name, clvl,
++                MapString ("subtree", cmRail->Levels[clvl].SubTreeMap, cmRail->Levels[clvl].NumNodes, ""));
++    }
++
++    if (cRole == CM_ROLE_SUBORDINATE)         /* got a leader (Not me) */
++    {                                         /* => send SubTreeMap to her */
++      CM_SGMT *leader = &cmRail->Levels[cmRail->TopLevel].Sgmts[0];
++
++      ASSERT (leader->State == CM_SGMT_PRESENT);
++      ASSERT (cmRail->Levels[clvl].SubTreeMapValid);
++
++      if (!leader->Maps[clvl].OutputMapValid ||
++          statemap_changed (cmRail->Levels[clvl].SubTreeMap))
++      {
++          statemap_setmap (leader->Maps[clvl].OutputMap, cmRail->Levels[clvl].SubTreeMap);
++          leader->Maps[clvl].OutputMapValid = 1;
++
++          statemap_clearchanges (cmRail->Levels[clvl].SubTreeMap);
++
++          CPRINTF3 (5, "%s: sending clvl %d subtree map to leader (%d)\n", cmRail->Rail->Name, clvl, leader->NodeId);
++      }
++    }
++}
++
++void
++BroadcastGlobalMap (CM_RAIL *cmRail, int clvl)
++{
++    int       lvl;
++    int       sidx;
++    CM_LEVEL *level;
++    CM_SGMT  *leader;
++    int       cTopLevel;
++    int       cRole;
++
++    if (clvl < cmRail->TopLevel)
++    {
++      cTopLevel = clvl + 1;
++      cRole = CM_ROLE_LEADER;
++    }
++    else
++    {
++      cTopLevel = cmRail->TopLevel;
++      cRole = cmRail->Role;
++    }
++
++    switch (cRole)
++    {
++    default:
++      ASSERT (0);
++      
++    case CM_ROLE_LEADER_CANDIDATE:            /* don't know this cluster's leader yet */
++      return;
++
++    case CM_ROLE_LEADER:                      /* cluster leader: */
++      ASSERT (clvl < cmRail->TopLevel);               /* set GlobalMap from SubTreeMap */
++      
++      if (!cmRail->Levels[clvl].SubTreeMapValid)      /* can't set global map */
++          return;
++
++      if (cmRail->Levels[clvl].GlobalMapValid &&      /* already set global map */
++          !statemap_changed (cmRail->Levels[clvl].SubTreeMap)) /* no changes to propagate */
++          return;
++      
++      statemap_setmap (cmRail->Levels[clvl].GlobalMap, cmRail->Levels[clvl].SubTreeMap);
++      cmRail->Levels[clvl].GlobalMapValid = 1;
++      statemap_clearchanges (cmRail->Levels[clvl].SubTreeMap);
++
++      CPRINTF2 (5, "%s: whole cluster %d leader setting global map\n", cmRail->Rail->Name, clvl);
++
++      UpdateGlobalStatus (cmRail);
++      break;
++      
++    case CM_ROLE_SUBORDINATE:                 /* cluster subordinate: */
++      ASSERT (clvl >= cmRail->TopLevel);              /* receive GlobalMap from leader */
++      ASSERT (cmRail->TopLevel < cmRail->NumLevels);
++      
++      leader = &cmRail->Levels[cmRail->TopLevel].Sgmts[0];
++      ASSERT (leader->State == CM_SGMT_PRESENT);
++
++      if (!leader->Maps[clvl].InputMapValid)  /* can't set global map */
++          return;
++      
++      if (cmRail->Levels[clvl].GlobalMapValid &&      /* already set global map */
++          !statemap_changed (leader->Maps[clvl].InputMap)) /* no changes to propagate */
++          return;
++
++      statemap_setmap (cmRail->Levels[clvl].GlobalMap, leader->Maps[clvl].InputMap);
++      cmRail->Levels[clvl].GlobalMapValid = 1;
++      statemap_clearchanges (leader->Maps[clvl].InputMap);
++
++      CPRINTF3 (5, "%s: getting clvl %d global map from leader (%d)\n", cmRail->Rail->Name, clvl, leader->NodeId);
++
++      UpdateGlobalStatus (cmRail);
++      break;
++    }
++
++    CPRINTF3 (5, "%s: clvl %d %s\n", cmRail->Rail->Name, clvl,
++            MapString ("global", cmRail->Levels[clvl].GlobalMap, cmRail->Levels[clvl].NumNodes, ""));
++    
++    /* Broadcast global map to all subordinates */
++    for (lvl = 0; lvl < cTopLevel; lvl++)
++    {
++      level = &cmRail->Levels[lvl];
++      
++      for (sidx = 0; sidx < level->NumSegs; sidx++)
++      {
++          CM_SGMT *sgmt = &level->Sgmts[sidx];
++          
++          if (sgmt->State == CM_SGMT_PRESENT)
++          {
++              statemap_setmap (sgmt->Maps[clvl].OutputMap, cmRail->Levels[clvl].GlobalMap);
++              sgmt->Maps[clvl].OutputMapValid = 1;
++              
++              CPRINTF5 (5, "%s: sending clvl %d global map to subordinate %d %d (%d)\n", 
++                        cmRail->Rail->Name, clvl, lvl, sidx, sgmt->NodeId);
++          }
++      }
++    }
++}
++
++static void
++CheckPeerPulse (CM_RAIL *cmRail, CM_SGMT *sgmt)
++{
++    int clvl, sendRejoin;
++    
++    switch (sgmt->State)
++    {
++    case CM_SGMT_ABSENT:
++      break;
++
++    case CM_SGMT_WAITING:                     /* waiting for a subtree */
++      if (!AFTER (lbolt, sgmt->UpdateTick + MSEC2TICKS(CM_DISCOVER_TIMEOUT)))
++          break;
++      
++      CPRINTF3 (2, "%s: lvl %d subtree %d contains no live nodes\n", cmRail->Rail->Name, 
++                sgmt->Level, (int) (sgmt - &cmRail->Levels[sgmt->Level].Sgmts[0]));
++
++      sgmt->State = CM_SGMT_ABSENT;
++      for (clvl = sgmt->Level; clvl < cmRail->NumLevels; clvl++)
++      {
++          statemap_zero (sgmt->Maps[clvl].InputMap);          /* need to start propagating zeros (flags change) */
++          sgmt->Maps[clvl].InputMapValid = 1;         /* and must indicate that the map is now valid */
++      }
++      break;
++
++    case CM_SGMT_COMING:                              /* lost/waiting subtree sent me IMCOMING */
++      ASSERT (sgmt->Level > 0);                       /* we only do subtree discovery below our own level */
++
++      if (AFTER (lbolt, sgmt->WaitingTick + MSEC2TICKS(CM_WAITING_TIMEOUT)))
++      {
++          CPRINTF3 (1, "%s: lvl %d subtree %d waiting too long\n", cmRail->Rail->Name,
++                    sgmt->Level, (int) (sgmt - &cmRail->Levels[sgmt->Level].Sgmts[0]));
++          printk ("%s: lvl %d subtree %d waiting too long\n", cmRail->Rail->Name,
++                  sgmt->Level, (int) (sgmt - &cmRail->Levels[sgmt->Level].Sgmts[0]));
++
++          sgmt->State = CM_SGMT_ABSENT;
++          for (clvl = sgmt->Level; clvl < cmRail->NumLevels; clvl++)
++          {
++              statemap_zero (sgmt->Maps[clvl].InputMap);              /* need to start propagating zeros (flags change) */
++              sgmt->Maps[clvl].InputMapValid = 1;             /* and must indicate that the map is now valid */
++          }
++          break;
++      }
++
++      if (!AFTER (lbolt, sgmt->UpdateTick + MSEC2TICKS(CM_DISCOVER_TIMEOUT)))
++          break;
++
++      CPRINTF3 (2, "%s: lvl %d subtree %d hasn't connected yet\n", cmRail->Rail->Name,
++                sgmt->Level, (int) (sgmt - &cmRail->Levels[sgmt->Level].Sgmts[0]));
++
++      sgmt->State = CM_SGMT_WAITING;
++      sgmt->UpdateTick = lbolt;
++
++      if (sgmt->Level > 0)
++          __Schedule_Discovery (cmRail);
++      break;
++      
++    case CM_SGMT_PRESENT:
++      if (!AFTER (lbolt, sgmt->UpdateTick + MSEC2TICKS(CM_HEARTBEAT_TIMEOUT)))
++          break;
++
++      if (sgmt->Level == cmRail->TopLevel)            /* leader died */
++      {
++          sendRejoin = (sgmt->State == CM_SGMT_PRESENT && sgmt->AckSeq == 0);
++
++          CPRINTF4 (1, "%s: leader (%d) node %d JUST DIED%s\n", 
++                    cmRail->Rail->Name, sgmt->Level, sgmt->NodeId,
++                    sendRejoin ? ": !REJOIN" : "");
++          
++          printk ("%s: lvl %d leader (%d) JUST DIED%s\n", 
++                  cmRail->Rail->Name, sgmt->Level, sgmt->NodeId,
++                  sendRejoin ? ": !REJOIN" : "");
++      
++          if (sendRejoin)
++          {
++              /* she's not sent us any heartbeats even though she responded to a discover
++               * so tell her to rejoin the tree at the bottom, this will mean that she 
++               * has to run the heartbeat timer before being able to rejoin the tree. */
++              SendMessage (cmRail, sgmt->NodeId, sgmt->Level, CM_MSG_TYPE_REJOIN);
++          }
++
++          StartLeaderDiscovery (cmRail);
++          break;
++      }
++
++      sendRejoin = (sgmt->State == CM_SGMT_PRESENT && sgmt->AckSeq == 0);
++
++      CPRINTF5 (2, "%s: lvl %d subordinate %d (%d) JUST DIED%s\n", cmRail->Rail->Name, 
++                sgmt->Level, (int) (sgmt - &cmRail->Levels[sgmt->Level].Sgmts[0]), sgmt->NodeId,
++                sendRejoin ? ": !REJOIN" : "");
++      printk ("%s: lvl %d subordinate %d (%d) JUST DIED%s\n", cmRail->Rail->Name, 
++              sgmt->Level, (int) (sgmt - &cmRail->Levels[sgmt->Level].Sgmts[0]), sgmt->NodeId,
++              sendRejoin ? ": !REJOIN" : "");
++
++      if (sendRejoin)
++      {
++          /* she's not sent us any heartbeats even though she responded to a discover
++           * so tell her to rejoin the tree at the bottom, this will mean that she 
++           * has to run the heartbeat timer before being able to rejoin the tree. */
++          SendMessage (cmRail, sgmt->NodeId, sgmt->Level, CM_MSG_TYPE_REJOIN);
++      }
++
++      StartSubTreeDiscovery (cmRail, sgmt);
++      break;
++       
++    default:
++      ASSERT (0);
++    }
++}
++
++static void
++CheckPeerPulses (CM_RAIL *cmRail)
++{
++    int lvl;
++    int sidx;
++   
++    /* check children are alive */
++    for (lvl = 0; lvl < cmRail->TopLevel; lvl++)
++      for (sidx = 0; sidx < cmRail->Levels[lvl].NumSegs; sidx++)
++          CheckPeerPulse (cmRail, &cmRail->Levels[lvl].Sgmts[sidx]);
++
++    /* check leader is alive */
++    if (cmRail->Role == CM_ROLE_SUBORDINATE)
++    {
++      ASSERT (cmRail->TopLevel < cmRail->NumLevels);
++      ASSERT (cmRail->Levels[cmRail->TopLevel].Sgmts[0].State == CM_SGMT_PRESENT);
++      
++      CheckPeerPulse (cmRail, &cmRail->Levels[cmRail->TopLevel].Sgmts[0]);
++    }
++}
++
++static void
++SendHeartbeats (CM_RAIL *cmRail)
++{
++    int lvl;
++
++    /* Send heartbeats to my children */
++    for (lvl = 0; lvl < cmRail->TopLevel; lvl++)
++    {
++      CM_LEVEL *level = &cmRail->Levels[lvl];
++      int       sidx;
++       
++      for (sidx = 0; sidx < level->NumSegs; sidx++)
++      {
++          CM_SGMT *sgmt = &cmRail->Levels[lvl].Sgmts[sidx];
++
++          if (sgmt->State == CM_SGMT_PRESENT)
++              SendToSgmt (cmRail, sgmt, CM_MSG_TYPE_HEARTBEAT);
++      }
++    }
++
++    /* Send heartbeat to my leader */
++    if (cmRail->Role == CM_ROLE_SUBORDINATE)
++    {
++      ASSERT (cmRail->TopLevel < cmRail->NumLevels);
++      SendToSgmt (cmRail, &cmRail->Levels[cmRail->TopLevel].Sgmts[0], CM_MSG_TYPE_HEARTBEAT);
++    }
++}
++
++static int
++BroadcastDiscover (CM_RAIL *cmRail)
++{
++    int       sidx;
++    int             lvl;
++    int       msgType;
++    CM_LEVEL *level;
++    int       urgent;
++
++    ASSERT (cmRail->TopLevel <= cmRail->NumLevels);
++    ASSERT ((cmRail->Role == CM_ROLE_LEADER) ? (cmRail->TopLevel == cmRail->NumLevels) :
++          (cmRail->Role == CM_ROLE_SUBORDINATE) ? (cmRail->Levels[cmRail->TopLevel].Sgmts[0].State == CM_SGMT_PRESENT) :
++          (cmRail->Role == CM_ROLE_LEADER_CANDIDATE));
++
++    if (cmRail->Role != CM_ROLE_LEADER_CANDIDATE)     /* got a leader/lead whole machine */
++    {
++      urgent = 0;                             /* non-urgent leader discovery */
++      lvl = cmRail->TopLevel - 1;             /* on nodes I lead (resolves leader conflicts) */
++      msgType = CM_MSG_TYPE_RESOLVE_LEADER;
++    }
++    else
++    {
++      urgent = 1;                             /* urgent leader discovery */
++      lvl = cmRail->TopLevel;                 /* on nodes I'd like to lead */
++      msgType = CM_MSG_TYPE_DISCOVER_LEADER;
++    }
++
++    if (lvl >= 0)
++    {
++      if (lvl > cmRail->BroadcastLevel)
++      {
++          /* Unable to broadcast at this level in the spanning tree, so we 
++           * just continue doing discovery until we are able to broadcast */
++          CPRINTF4 (6, "%s: broadcast level %d too low to discover %d at level %d\n",
++                    cmRail->Rail->Name, cmRail->BroadcastLevel, msgType, lvl);
++
++          cmRail->DiscoverStartTick = lbolt;
++      }
++      else
++      {
++          level = &cmRail->Levels[lvl];
++          SendToSgmt (cmRail, &level->Sgmts[level->MySgmt], msgType);
++      }
++    }
++    
++    while (lvl > 0)
++    {
++      level = &cmRail->Levels[lvl];
++      
++      for (sidx = 0; sidx < level->NumSegs; sidx++)
++      {
++          CM_SGMT *sgmt = &level->Sgmts[sidx];
++       
++          if (sgmt->State == CM_SGMT_WAITING)
++          {
++              ASSERT (sidx != level->MySgmt);
++              /* Do subordinate discovery.  Existing subordinates will
++               * ignore it, but leader candidates will send IMCOMING.
++               * This is always urgent since we'll assume a subtree is
++               * absent if I don't get IMCOMING within the timeout.
++               */
++              SendToSgmt (cmRail, sgmt, CM_MSG_TYPE_DISCOVER_SUBORDINATE);
++              urgent = 1;
++          }
++      }
++      lvl--;
++    }
++   
++    return (urgent);
++}
++
++static void
++CheckBroadcast (CM_RAIL *cmRail)
++{
++    int  clvl;
++
++    for (clvl = cmRail->NumLevels-1; clvl >= 0 && cmRail->Rail->SwitchBroadcastLevel < cmRail->Levels[clvl].SwitchLevel; clvl--)
++      ;
++
++    if (cmRail->OfflineReasons || cmRail->Rail->System->Shutdown)
++      clvl = -1;
++
++    /* if the level at which we can broadcast drops, then we must rejoin the
++     * spanning tree at the highest level for which broadcast is good. */
++    if (cmRail->BroadcastLevel > clvl && clvl < (int)(cmRail->Role == CM_ROLE_LEADER ? cmRail->TopLevel - 1 : cmRail->TopLevel))
++    {
++      printk ("%s: REJOINING at level %d because %s\n", cmRail->Rail->Name, clvl+1, 
++              (cmRail->OfflineReasons & CM_OFFLINE_MANAGER) ? "of manager thread" :
++              (cmRail->OfflineReasons & CM_OFFLINE_PROCFS)  ? "force offline"  : 
++              cmRail->Rail->System->Shutdown ? "system shutdown" : "broadcast level changed");
++      LowerTopLevel (cmRail, clvl+1);
++    }
++    
++    if (cmRail->BroadcastLevel != clvl)
++    {
++      cmRail->BroadcastLevel     = clvl;
++      cmRail->BroadcastLevelTick = lbolt;
++    }
++
++    /* schedule the update thread, to withdraw from comms with 
++     * nodes "outside" of the valid broadcastable range. */
++    for (clvl = 0; clvl < cmRail->NumLevels; clvl++)
++    {
++      if (cmRail->BroadcastLevel < clvl)
++      {
++          if (AFTER (lbolt, cmRail->BroadcastLevelTick + EP_WITHDRAW_TIMEOUT) && 
++              !(cmRail->Levels[clvl].OfflineReasons & CM_OFFLINE_BROADCAST))
++          {
++              printk ("%s: Withdraw at Level %d\n", cmRail->Rail->Name, clvl);
++              cmRail->Levels[clvl].OfflineReasons |= CM_OFFLINE_BROADCAST;
++          }
++      }
++      else
++      {
++          if (cmRail->Levels[clvl].OfflineReasons & CM_OFFLINE_BROADCAST)
++          {
++              printk ("%s: Rejoin at Level %d\n", cmRail->Rail->Name, clvl);
++              cmRail->Levels[clvl].OfflineReasons &= ~CM_OFFLINE_BROADCAST;
++          }
++      }
++    }
++      
++}
++
++static void
++CheckManager (CM_RAIL *cmRail)
++{
++    long time,  state = ep_kthread_state (&cmRail->Rail->System->ManagerThread, &time);
++
++    if (state == KT_STATE_RUNNING && BEFORE (lbolt, time + MSEC2TICKS(CM_THREAD_RUNNING_TIMEOUT)))
++      state = KT_STATE_SLEEPING;
++    if (state != KT_STATE_SLEEPING && BEFORE (lbolt, time + MSEC2TICKS(CM_THREAD_SCHEDULE_TIMEOUT)))
++      state = KT_STATE_SLEEPING;
++
++    if ((cmRail->OfflineReasons & CM_OFFLINE_MANAGER) && state == KT_STATE_SLEEPING)
++    {
++      printk ("%s: manager thread unstuck\n", cmRail->Rail->Name);
++
++      cmRail->OfflineReasons &= ~CM_OFFLINE_MANAGER;
++    }
++
++    if (!(cmRail->OfflineReasons & CM_OFFLINE_MANAGER) && state != KT_STATE_SLEEPING)
++    {
++      printk ("%s: manager thread stuck - %s\n", cmRail->Rail->Name,
++              state == KT_STATE_SCHEDULED ? "scheduled" : 
++              state == KT_STATE_RUNNING ? "running" : 
++              state == KT_STATE_STALLED ? "stalled" : "unknown");
++
++      cmRail->OfflineReasons |= CM_OFFLINE_MANAGER;
++    }
++}
++
++static void
++CheckOfflineReasons (CM_RAIL *cmRail, int clvl)
++{
++    int subClMin, subClMax, myClId;
++    char clNodeStr[32];                               /* [%d-%d][%d-%d] */
++
++    if (cmRail->Levels[clvl].OfflineReasons)
++    {
++      if (cmRail->Levels[clvl].Online)
++      {
++          printk ("%s: Withdraw from %s\n", cmRail->Rail->Name, sprintClPeers (clNodeStr, cmRail, clvl));
++          
++          RestartComms (cmRail, clvl);
++      }
++    }
++    else
++    {
++      if (cmRail->Levels[clvl].Restarting && cmRail->Levels[clvl].Connected == 0)
++      {
++          printk ("%s: Rejoin with %s\n", cmRail->Rail->Name, sprintClPeers (clNodeStr, cmRail, clvl));
++
++          myClId = ClusterIds (cmRail, clvl, &subClMin, &subClMax);
++          
++          ASSERT (statemap_getbits (cmRail->Levels[clvl].LocalMap, myClId * CM_GSTATUS_BITS, CM_GSTATUS_BITS) == 
++                  (CM_GSTATUS_CLOSING | CM_GSTATUS_MAY_START | CM_GSTATUS_RESTART));
++    
++          statemap_setbits (cmRail->Levels[clvl].LocalMap, myClId * CM_GSTATUS_BITS,
++                            CM_GSTATUS_CLOSING | CM_GSTATUS_MAY_START, CM_GSTATUS_BITS);
++
++          cmRail->Levels[clvl].Restarting = 0;
++      }
++    }
++}
++
++void
++DoHeartbeatWork (CM_RAIL *cmRail)
++{
++    long now = lbolt;
++    int  clvl;
++
++    if ((RejoinCheck || RejoinPanic) &&
++      AFTER (now, cmRail->NextRunTime + MSEC2TICKS (CM_TIMER_SCHEDULE_TIMEOUT))) /* If I've been unresponsive for too long */
++    {
++      /* I'd better reconnect to the network because I've not been playing the game */
++      CPRINTF4 (1, "%s: REJOINING because I was too slow (heartbeat) [%ld,%ld,(%ld)]\n", cmRail->Rail->Name, now,  cmRail->NextRunTime, (long int)MSEC2TICKS (CM_TIMER_SCHEDULE_TIMEOUT));
++      printk ("%s: REJOINING because I was too slow (heartbeat) [%ld,%ld,(%ld)]\n", cmRail->Rail->Name, now,  cmRail->NextRunTime, (long int)MSEC2TICKS (CM_TIMER_SCHEDULE_TIMEOUT));
++      
++      LowerTopLevel (cmRail, 0);
++      
++      IncrStat (cmRail, RejoinTooSlow);
++      
++      if (RejoinPanic)
++          panic ("ep: REJOINING because I was too slow (heartbeat)\n");
++    }
++    
++    PollInputQueues (cmRail);
++    
++    if (cmRail->NextDiscoverTime && ! BEFORE (now, cmRail->NextDiscoverTime))
++    {
++      if (BroadcastDiscover (cmRail))         /* urgent discovery required? */
++          cmRail->NextDiscoverTime = now + MSEC2TICKS (CM_URGENT_DISCOVER_INTERVAL);
++      else
++          cmRail->NextDiscoverTime = now + MSEC2TICKS (CM_PERIODIC_DISCOVER_INTERVAL);
++      
++      if (cmRail->Role == CM_ROLE_LEADER_CANDIDATE && AFTER (now, cmRail->DiscoverStartTick + MSEC2TICKS (CM_DISCOVER_TIMEOUT)))
++          RaiseTopLevel (cmRail);
++    }
++    
++    if (cmRail->NextHeartbeatTime && ! BEFORE (now, cmRail->NextHeartbeatTime))
++    {
++      CheckPosition (cmRail->Rail);
++      CheckPeerPulses (cmRail);
++      CheckBroadcast (cmRail);
++      CheckManager (cmRail);
++      
++      for (clvl = 0; clvl < cmRail->NumLevels; clvl++)
++      {
++          CheckOfflineReasons (cmRail, clvl);
++          ReduceGlobalMap (cmRail, clvl);
++          BroadcastGlobalMap (cmRail, clvl);
++      }
++      
++      SendHeartbeats (cmRail);
++      
++      /* Compute the next heartbeat time, but "drift" it towards the last
++       * periodic discovery time we saw from the whole machine leader */
++      cmRail->NextHeartbeatTime = now + MSEC2TICKS (CM_HEARTBEAT_INTERVAL);
++    }
++
++    if (cmRail->NextDiscoverTime && AFTER (cmRail->NextHeartbeatTime, cmRail->NextDiscoverTime))
++      cmRail->NextRunTime = cmRail->NextDiscoverTime;
++    else 
++      cmRail->NextRunTime = cmRail->NextHeartbeatTime;
++}
++
++#define CM_SVC_INDICATOR_OFFSET(CMRAIL,CLVL,IND,NODEID)     ( ( CMRAIL->Levels[CLVL].NumNodes * CM_GSTATUS_BITS ) \
++                                                              + ( CMRAIL->Levels[CLVL].NumNodes * IND ) \
++                                                              + ( NODEID - CMRAIL->Levels[CLVL].MinNodeId ) )
++int
++cm_svc_indicator_set (EP_RAIL *rail, int svc_indicator)
++{
++    CM_RAIL *cmRail = rail->ClusterRail;
++    unsigned long flags;
++    int           clvl;
++
++    EPRINTF2 (DBG_SVC,"cm_svc_indicator_set: rail %p ind %d\n", rail, svc_indicator);
++
++    if (svc_indicator < 0 || svc_indicator >= EP_SVC_NUM_INDICATORS)
++    {
++      EPRINTF1 (DBG_SVC,"cm_svc_indicator_set: service indicator %d not registered\n", svc_indicator);
++      return (-1);
++    }
++
++    if (rail->State == EP_RAIL_STATE_UNINITIALISED) 
++      return (-2);
++    
++    spin_lock_irqsave (&cmRail->Lock, flags);
++    for (clvl = 0; clvl < cmRail->NumLevels; clvl++)  {
++      statemap_setbits (cmRail->Levels[clvl].LocalMap, CM_SVC_INDICATOR_OFFSET (cmRail, clvl, svc_indicator, cmRail->NodeId), 1, 1); 
++      EPRINTF3 (DBG_SVC,"cm_svc_indicator_set: clvl %d nodeId %d offset %d\n", clvl, cmRail->NodeId, CM_SVC_INDICATOR_OFFSET (cmRail, clvl, svc_indicator, cmRail->NodeId));
++    }
++    spin_unlock_irqrestore (&cmRail->Lock, flags);
++
++    return (0);
++}
++
++int
++cm_svc_indicator_clear (EP_RAIL *rail, int svc_indicator)
++{
++    CM_RAIL *cmRail = rail->ClusterRail;
++    unsigned long flags;
++    int           clvl;
++
++    EPRINTF2 (DBG_SVC, "cm_svc_indicator_clear: rail %p ind %d\n", rail, svc_indicator);
++
++    if (svc_indicator < 0 || svc_indicator >= EP_SVC_NUM_INDICATORS)
++    {
++      EPRINTF1 (DBG_SVC, "cm_svc_indicator_clear: service indicator %d not registered\n", svc_indicator);
++      return (-1);
++    }
++
++    if (rail->State == EP_RAIL_STATE_UNINITIALISED) 
++      return (-2);
++
++    spin_lock_irqsave (&cmRail->Lock, flags);
++    for (clvl = 0; clvl < cmRail->NumLevels; clvl++)  {
++      statemap_setbits (cmRail->Levels[clvl].LocalMap, CM_SVC_INDICATOR_OFFSET (cmRail, clvl, svc_indicator, cmRail->NodeId), 0, 1); 
++      EPRINTF3 (DBG_SVC, "cm_svc_indicator_clear: clvl %d nodeId %d offset %d\n", clvl, cmRail->NodeId, CM_SVC_INDICATOR_OFFSET (cmRail, clvl, svc_indicator, cmRail->NodeId));
++    }
++    spin_unlock_irqrestore (&cmRail->Lock, flags);
++
++    return (0);
++}
++
++int
++cm_svc_indicator_is_set (EP_RAIL *rail, int svc_indicator, int nodeId)
++{
++    CM_RAIL *cmRail = rail->ClusterRail;
++    unsigned long flags;
++    int           clvl;
++    bitmap_t      bits;
++
++    EPRINTF4 (DBG_SVC, "cm_svc_indicator_is_set: rail %p ind %d nodeId %d (me=%d)\n", rail, svc_indicator, nodeId, cmRail->NodeId);
++
++    if (svc_indicator < 0 || svc_indicator > EP_SVC_NUM_INDICATORS)
++    {
++      EPRINTF1 (DBG_SVC, "cm_svc_indicator_is_set: service indicator %d not registered\n", svc_indicator);
++      return (0);
++    }
++
++    if (rail->State == EP_RAIL_STATE_UNINITIALISED) 
++      return (0);
++    
++    spin_lock_irqsave (&cmRail->Lock, flags);
++    for (clvl = 0; clvl < cmRail->NumLevels; clvl++)
++      if (nodeId >= cmRail->Levels[clvl].MinNodeId && nodeId < (cmRail->Levels[clvl].MinNodeId + cmRail->Levels[clvl].NumNodes))
++          break;
++
++    if ( clvl == cmRail->NumLevels) { 
++      EPRINTF1 (DBG_SVC, "cm_svc_indicator_is_set: node out of range %d \n", nodeId); 
++      spin_unlock_irqrestore (&cmRail->Lock, flags);
++      return (0);
++    }
++
++    if ( cmRail->NodeId == nodeId ) 
++      bits = statemap_getbits (cmRail->Levels[clvl].LocalMap, CM_SVC_INDICATOR_OFFSET (cmRail, clvl, svc_indicator, nodeId), 1);
++    else
++      bits = statemap_getbits (cmRail->Levels[clvl].GlobalMap, CM_SVC_INDICATOR_OFFSET (cmRail, clvl, svc_indicator, nodeId), 1);
++
++    EPRINTF4 (DBG_SVC, "cm_svc_indicator_is_set: clvl %d nodeId %d offset %d %x\n", clvl, nodeId, CM_SVC_INDICATOR_OFFSET (cmRail, clvl, svc_indicator, nodeId), bits);
++
++    spin_unlock_irqrestore (&cmRail->Lock, flags);
++
++    return  ( (bits == 0) ? (0) : (1) );
++}
++
++int
++cm_svc_indicator_bitmap (EP_RAIL *rail, int svc_indicator, bitmap_t * bitmap, int low, int nnodes)
++{
++    /* or in the bit map */  
++    CM_RAIL      *cmRail = rail->ClusterRail;
++    int           nodeId, clvl;
++    bitmap_t      bits;
++    unsigned long flags;
++    int           clip_out_low, clip_out_high;
++    int           curr_low, curr_high;
++    int           check_low, check_high;
++
++    EPRINTF4 (DBG_SVC, "cm_svc_indicator_bitmap: rail %p ind %d low %d high %d\n", rail, svc_indicator, low, (low + nnodes));
++
++    if (svc_indicator < 0 || svc_indicator >= EP_SVC_NUM_INDICATORS)
++    {
++      EPRINTF1 (DBG_SVC, "cm_svc_indicator_bitmap: service indicator %d not registered\n", svc_indicator);
++      return (-1);
++    }
++
++    if (rail->State != EP_RAIL_STATE_RUNNING) 
++      return (-2);
++
++    spin_lock_irqsave (&cmRail->Lock, flags);
++    
++    clip_out_low = clip_out_high = -1; /* all in */
++    for (clvl = 0; clvl < cmRail->NumLevels; clvl++) {
++
++      /* curr_high/low is the range of the current lvl */
++      curr_low  = cmRail->Levels[clvl].MinNodeId;
++      curr_high = cmRail->Levels[clvl].MinNodeId + cmRail->Levels[clvl].NumNodes;
++
++      /* find out how much of low high is in this range and only check that part */
++      check_low  = ( low  < curr_low)  ? curr_low  : low; 
++      check_high = ( (low + nnodes) > curr_high) ? curr_high : (low + nnodes);
++
++      EPRINTF6 (DBG_SVC, "cm_svc_indicator_bitmap: curr(%d,%d) check(%d,%d) clip(%d,%d)\n", curr_low, curr_high, check_low, check_high, clip_out_low, clip_out_high);
++
++      for(nodeId = check_low; nodeId < check_high; nodeId++) {
++
++          if (  (clip_out_low <= nodeId) && (nodeId <= clip_out_high))
++              nodeId = clip_out_high; /* step over the cliped out section */
++          else {
++
++              if ( cmRail->NodeId == nodeId ) 
++                  bits = statemap_getbits (cmRail->Levels[clvl].LocalMap, CM_SVC_INDICATOR_OFFSET (cmRail, clvl, svc_indicator, nodeId), 1);
++              else
++                  bits = statemap_getbits (cmRail->Levels[clvl].GlobalMap, CM_SVC_INDICATOR_OFFSET (cmRail, clvl, svc_indicator, nodeId), 1);
++              
++              if ( bits ) {
++                  EPRINTF2 (DBG_SVC, "cm_svc_indicator_bitmap: its set nodeId %d (clvl %d)\n", nodeId, clvl);
++                  BT_SET ( bitmap , nodeId - low );
++              }
++          }
++      }
++
++      /* widen the clip out range */
++      clip_out_low  = curr_low;
++      clip_out_high = curr_high -1; 
++    }
++
++    spin_unlock_irqrestore (&cmRail->Lock, flags);      
++
++    return (0);
++}
++
++#if defined(PER_CPU_TIMEOUT)
++static void
++cm_percpu_timeout (void *arg)
++{
++    CM_RAIL          *cmRail = (CM_RAIL *) arg;
++    CM_TIMEOUT_DATA *hbd     = &cmRail->HeartbeatTimeoutsData[current_cpu()];
++    long             now     = lbolt;
++    unsigned         delay   = now - hbd->ScheduledAt;
++    unsigned long    flags;
++
++    if (delay > hbd->WorstDelay)
++      hbd->WorstDelay = delay;
++    if (hbd->BestDelay == 0 || delay < hbd->BestDelay)
++      hbd->BestDelay = delay;
++
++    if (cmRail->HeartbeatTimeoutsShouldStop)
++    {
++      spin_lock_irqsave (&cmRail->Lock, flags);
++      cmRail->HeartbeatTimeoutsStopped |= (1 << current_cpu());
++      kcondvar_wakeupall (&cmRail->HeartbeatTimeoutsWait, &cmRail->Lock);
++      spin_unlock_irqrestore (&cmRail->Lock, flags);
++      return;
++    }
++
++    if (cmRail->NextRunTime == 0 || AFTER (cmRail->NextRunTime, lbolt))
++      hbd->EarlyCount++;
++    else if (cmRail->HeartbeatTimeoutRunning)
++      hbd->MissedCount++;
++    else
++    {
++      local_irq_save (flags);
++      
++      if (! spin_trylock (&cmRail->HeartbeatTimeoutsLock))
++          hbd->WastedCount++;
++      else
++      {
++          cmRail->HeartbeatTimeoutRunning = 1;
++          hbd->WorkCount++;
++
++          spin_lock (&cmRail->Lock);
++
++          if ((delay = (lbolt - cmRail->NextRunTime)) > hbd->WorstHearbeatDelay)
++              hbd->WorstHearbeatDelay = delay;
++          if ((delay = (lbolt - now) > hbd->WorstLockDelay))
++              hbd->WorstLockDelay = delay;
++
++          DoHeartbeatWork (cmRail);
++
++          spin_unlock (&cmRail->Lock);
++          spin_unlock (&cmRail->HeartbeatTimeoutsLock);
++
++          cmRail->HeartbeatTimeoutRunning = 0;
++      }
++      local_irq_restore (flags);
++    }
++
++    hbd->ScheduledAt = lbolt + MSEC2TICKS (CM_PERCPU_TIMEOUT_INTERVAL);
++    timeout_cpu (cm_percpu_timeout, cmRail, MSECS2TICKS (CM_PERCPU_TIMEOUT_INTERVAL), CALLOUT_TYPE|CALLOUT_NOMALLOC);
++}
++
++static void
++StartPerCpuTimeouts (CM_RAIL *cmRail)
++{
++    register int c;
++
++    spin_lock_init (&cmRail->HeartbeatTimeoutsLock);
++
++    KMEM_ZALLOC (cmRail->HeartbeatTimeoutsData, CM_TIMEOUT_DATA *, ncpus * sizeof (CM_TIMEOUT_DATA), 1);
++
++    for (c = 0; c < cpus_in_box; c++)
++    {
++      if (cpu_to_processor (c))
++      {       
++          if (current_cpu() != c)
++          {
++              thread_bind (current_thread(), cpu_to_processor(c));
++              mpsleep (current_thread(), 0, "StartPerCpuTimeouts", 1, NULL, 0);
++
++              if (current_cpu() != c)
++                  panic ("ep: StartPerCpuTimeouts - failed to switch cpu\n");
++          }
++          
++          cmRail->HeartbeatTimeoutsStarted |= (1 << c);
++          cmRail->HeartbeatTimeoutsData[c].ScheduledAt = lbolt + c;
++
++          timeout_cpu (cm_percpu_timeout, cmRail, c, CALLOUT_TYPE|CALLOUT_NOMALLOC);
++      }
++    }
++
++    thread_bind(current_thread(), NULL);
++}
++
++static void
++StopPerCpuTimeouts (CM_RAIL *cmRail)
++{
++    register int c;
++    unsigned long flags;
++
++    cmRail->HeartbeatTimeoutsShouldStop = 1;
++
++    for (c = 0; c < cpus_in_box; c++)
++    {
++      if (cmRail->HeartbeatTimeoutsStarted & (1 << c))
++      {
++          printk ("%s: stopping cpu_timeout on cpu %d\n", cmRail->Rail->Name, c);
++
++          if (untimeout_cpu (cm_percpu_timeout, cmRail, c, CALLOUT_TYPE|CALLOUT_NOMALLOC, NULL))
++              cmRail->HeartbeatTimeoutsStopped |= (1 << c);
++      }
++    }
++    thread_bind(current_thread(), NULL);
++
++    spin_lock_irqsave (&cmRail->Lock, flags);
++    while (cmRail->HeartbeatTimeoutsStopped != cmRail->HeartbeatTimeoutsStarted)
++      kcondvar_wait (&cmRail->HeartbeatTimeoutsWait, &cmRail->Lock, &flags);
++    spin_unlock_irqrestore (&cmRail->Lock, flags);
++
++    cmRail->HeartbeatTimeoutsStarted    = 0;
++    cmRail->HeartbeatTimeoutsStopped    = 0;
++    cmRail->HeartbeatTimeoutsShouldStop = 0;
++
++    KMEM_FREE (cmRail->HeartbeatTimeoutsData, ncpus * sizeof (CM_TIMEOUT_DATA));
++
++    spin_lock_destroy (&cmRail->HeartbeatTimeoutsLock);
++}
++
++#else
++
++static void
++cm_heartbeat_timer (unsigned long arg)
++{
++    CM_RAIL *cmRail = (CM_RAIL *) arg;
++    unsigned long flags;
++
++    spin_lock_irqsave (&cmRail->Lock, flags);
++
++    ASSERT (cmRail->Rail->State == EP_RAIL_STATE_RUNNING);
++
++    DoHeartbeatWork (cmRail);
++    
++    __Schedule_Timer (cmRail, cmRail->NextRunTime);
++
++    spin_unlock_irqrestore (&cmRail->Lock, flags);
++}
++
++#endif /* defined(PER_CPU_TIMEOUT) */
++
++
++
++void
++DisplayRailDo (DisplayInfo *di, EP_RAIL *rail)
++{
++    CM_RAIL *cmRail = rail->ClusterRail;
++    unsigned long flags;
++    int  i, j;
++
++    if (rail->State != EP_RAIL_STATE_RUNNING)
++      return;
++
++    spin_lock_irqsave (&cmRail->Lock, flags);
++
++    (di->func)(di->arg, "NodeId=%d NodeLevel=%d NumLevels=%d NumNodes=%d\n", 
++          cmRail->NodeId, cmRail->TopLevel, cmRail->NumLevels, cmRail->Rail->Position.pos_nodes);
++    
++    (di->func)(di->arg, "[");
++
++    for (i = 0; i < cmRail->NumLevels; i++)
++    {
++      if (i > 0)
++          (di->func)(di->arg, ",");
++       
++      if (i < cmRail->TopLevel)
++      {
++          (di->func)(di->arg, "L ");
++        
++          for (j = 0; j < cmRail->Levels[i].NumSegs; j++)
++              switch (cmRail->Levels[i].Sgmts[j].State)
++              {
++              case CM_SGMT_PRESENT: (di->func)(di->arg, "p%-4d", cmRail->Levels[i].Sgmts[j].NodeId); break;
++              case CM_SGMT_WAITING: (di->func)(di->arg, "w%4s", ""); break;
++              case CM_SGMT_COMING:  (di->func)(di->arg, "c%4s", ""); break;
++              case CM_SGMT_ABSENT:  (di->func)(di->arg, ".%4s", ""); break;
++              default:              (di->func)(di->arg, "?%4s", ""); break;
++              }
++      }
++      else
++          switch (cmRail->Role)
++          {
++          case CM_ROLE_LEADER_CANDIDATE:      
++              (di->func)(di->arg,"l "); 
++              for (j = 0; j < cmRail->Levels[i].NumSegs; j++)
++                  (di->func)(di->arg,"     ");
++              break;
++        
++          case CM_ROLE_SUBORDINATE:       
++              switch (cmRail->Levels[i].Sgmts[0].State)
++              {
++              case CM_SGMT_PRESENT: (di->func)(di->arg, "p%-4d", cmRail->Levels[i].Sgmts[0].NodeId); break;
++              case CM_SGMT_WAITING: (di->func)(di->arg, "w%4s", ""); break;
++              case CM_SGMT_COMING:  (di->func)(di->arg, "c%4s", ""); break;
++              case CM_SGMT_ABSENT:  (di->func)(di->arg, ".%4s", ""); break;
++              default:              (di->func)(di->arg, "?%4s", ""); break;
++              }
++              for (j = 1; j < cmRail->Levels[i].NumSegs; j++)
++                  (di->func)(di->arg, "     ");
++              break;
++        
++          default:
++              (di->func)(di->arg, "####");
++              break;
++          }
++    }
++    (di->func)(di->arg, "]\n");
++
++    spin_unlock_irqrestore (&cmRail->Lock, flags);
++}
++
++void
++DisplayRail (EP_RAIL *rail) 
++{
++    if (rail->State == EP_RAIL_STATE_RUNNING)
++      DisplayRailDo (&di_ep_debug, rail);
++}
++
++void
++DisplayStatus (EP_RAIL *rail)
++{
++    if (rail->State == EP_RAIL_STATE_RUNNING)
++    {
++      CM_RAIL *cmRail = rail->ClusterRail;
++      unsigned long flags;
++      
++      spin_lock_irqsave (&cmRail->Lock, flags);
++      
++      DisplayNodeMaps (&di_ep_debug, cmRail);
++      
++      spin_unlock_irqrestore (&cmRail->Lock, flags);
++    }
++}
++
++void
++DisplaySegs (EP_RAIL *rail)
++{
++    if (rail->State == EP_RAIL_STATE_RUNNING)
++    {
++      CM_RAIL *cmRail = rail->ClusterRail;
++      unsigned long flags;
++      
++      spin_lock_irqsave (&cmRail->Lock, flags);
++      
++      DisplayNodeSgmts (&di_ep_debug, cmRail);
++      
++      spin_unlock_irqrestore (&cmRail->Lock, flags);
++    }
++}
++
++static void
++LoadBroadcastRoute (CM_RAIL *cmRail, int lvl, int sidx)
++{
++    EP_RAIL *rail  = cmRail->Rail;
++    int      nsegs = cmRail->Levels[0].NumSegs;
++    int      vp    = EP_VP_BCAST(lvl, sidx);
++    int      nodes = 1;
++    int      baseNode;
++    int      i;
++
++    ASSERT (lvl > 0 && lvl <= cmRail->NumLevels);
++    ASSERT (sidx == 0 || lvl < cmRail->NumLevels);
++
++    ASSERT (vp >= EP_VP_BCAST_BASE && vp < EP_VP_BCAST_BASE + EP_VP_BCAST_COUNT);
++
++    for (i = 1; i <= lvl; i++)
++    {
++      nodes *= nsegs;
++      nsegs = (i == cmRail->NumLevels) ? 1 : cmRail->Levels[i].NumSegs;
++    }
++
++    baseNode = ((cmRail->NodeId / (nodes * nsegs)) * nsegs + sidx) * nodes;
++
++    CPRINTF5 (2, "%s: broadcast vp lvl %d sidx %d [%d,%d]\n", 
++            cmRail->Rail->Name, lvl, sidx, baseNode, baseNode + nodes - 1);
++    
++    rail->Operations.LoadSystemRoute (rail, vp, baseNode, baseNode + nodes - 1);
++}
++
++static void
++LoadRouteTable (CM_RAIL *cmRail)
++{
++    EP_RAIL *rail = cmRail->Rail;
++    int      i, j;
++   
++   if (cmRail->NumNodes > EP_MAX_NODES)
++   {
++       printk ("More nodes (%d) than point-to-point virtual process table entries (%d)\n", cmRail->NumNodes, EP_MAX_NODES);
++       panic ("LoadRouteTable\n");
++   }
++
++   for (i = 0; i < cmRail->NumNodes; i++)
++       rail->Operations.LoadSystemRoute (rail, EP_VP_NODE(i), i, i);
++
++   /* Generate broadcast routes for subtrees */
++   for (i = 1; i < cmRail->NumLevels; i++)
++      for (j = 0; j < cmRail->Levels[i].NumSegs; j++)
++        LoadBroadcastRoute (cmRail, i, j);
++
++   /* Generate broadcast route for whole machine */
++   LoadBroadcastRoute (cmRail, cmRail->NumLevels, 0);
++
++   /* Finally invalidate all the data routes */
++   for (i = 0; i < cmRail->NumNodes; i++)
++       rail->Operations.UnloadNodeRoute (cmRail->Rail, i);
++}
++
++void
++cm_node_disconnected (EP_RAIL *rail, unsigned nodeId)
++{
++    CM_RAIL *cmRail = rail->ClusterRail;
++    int      base, lstat, lgstat;
++    int            clvl, subClMin, subClMax;
++    int      thisClId, myClId;
++    unsigned long flags;
++
++    ASSERT (nodeId != cmRail->NodeId);
++
++    spin_lock_irqsave (&cmRail->Lock, flags);
++    for (clvl = 0; clvl < cmRail->NumLevels; clvl++)
++      if (nodeId >= cmRail->Levels[clvl].MinNodeId && nodeId < (cmRail->Levels[clvl].MinNodeId + cmRail->Levels[clvl].NumNodes))
++          break;
++
++    myClId   = ClusterIds (cmRail, clvl, &subClMin, &subClMax);
++    thisClId = nodeId - cmRail->Levels[clvl].MinNodeId;
++    base     = thisClId * CM_GSTATUS_BITS;
++    lstat    = statemap_getbits (cmRail->Levels[clvl].LocalMap,  base, CM_GSTATUS_BITS);
++    lgstat   = statemap_getbits (cmRail->Levels[clvl].LastGlobalMap, base, CM_GSTATUS_BITS) & CM_GSTATUS_STATUS_MASK;
++
++    ASSERT ((lstat & CM_GSTATUS_ACK_MASK) == CM_GSTATUS_MAY_RUN);
++
++    CPRINTF7 (2, "%s: cm_node_disconnected: Node %d: clvl %d, lgstat %s, gstat %s, lstat %s -> %sMAY_START\n",
++            cmRail->Rail->Name, nodeId, clvl,
++            GlobalStatusString (cmRail->Levels[clvl].LastGlobalMap, thisClId),
++            GlobalStatusString (cmRail->Levels[clvl].GlobalMap, thisClId),
++            GlobalStatusString (cmRail->Levels[clvl].LocalMap, thisClId),
++            ((lgstat != CM_GSTATUS_CLOSING) && (lstat & CM_GSTATUS_RESTART)) ? "RESTART|" : "");
++    
++    switch (lgstat)
++    {
++    case CM_GSTATUS_CLOSING:
++      /* delayed ack of closing - set MAY_START and clear RESTART */
++      statemap_setbits (cmRail->Levels[clvl].LocalMap, base, CM_GSTATUS_MAY_START, CM_GSTATUS_BITS);
++      break;
++    case CM_GSTATUS_STARTING:
++    case CM_GSTATUS_RUNNING:
++      IASSERT (! cmRail->Levels[clvl].Online || lstat & CM_GSTATUS_RESTART);
++      break;
++    case CM_GSTATUS_ABSENT:
++      IASSERT (lstat & CM_GSTATUS_RESTART);
++    }
++
++    cmRail->Levels[clvl].Connected--;
++
++    spin_unlock_irqrestore (&cmRail->Lock, flags);
++}
++
++void
++cm_restart_node (EP_RAIL *rail, unsigned nodeId)
++{
++    CM_RAIL *cmRail = rail->ClusterRail;
++    int      base, lstat, lgstat;
++    int            clvl, subClMin, subClMax;
++    int      thisClId, myClId;
++    unsigned long flags;
++
++    spin_lock_irqsave (&cmRail->Lock, flags);
++    if (nodeId == rail->Position.pos_nodeid)
++    {
++      for (clvl = 0; clvl < cmRail->NumLevels; clvl++)
++          RestartComms (cmRail, clvl);
++    }
++    else
++    {
++      for (clvl = 0; clvl < cmRail->NumLevels; clvl++)
++          if (nodeId >= cmRail->Levels[clvl].MinNodeId && nodeId < (cmRail->Levels[clvl].MinNodeId + cmRail->Levels[clvl].NumNodes))
++              break;
++      
++      myClId   = ClusterIds (cmRail, clvl, &subClMin, &subClMax);
++      thisClId = nodeId - cmRail->Levels[clvl].MinNodeId;
++      base     = thisClId * CM_GSTATUS_BITS;
++      lstat    = statemap_getbits (cmRail->Levels[clvl].LocalMap,  base, CM_GSTATUS_BITS);
++      lgstat   = statemap_getbits (cmRail->Levels[clvl].LastGlobalMap,  base, CM_GSTATUS_BITS) & CM_GSTATUS_STATUS_MASK;
++
++      CPRINTF6 (2, "%s: cm_restart_node: Node %d: clvl %d, lgstat %s, gstat %s, lstat %s\n",
++                cmRail->Rail->Name, nodeId, clvl,
++                GlobalStatusString (cmRail->Levels[clvl].LastGlobalMap, thisClId),
++                GlobalStatusString (cmRail->Levels[clvl].GlobalMap, thisClId),
++                GlobalStatusString (cmRail->Levels[clvl].LocalMap, thisClId));
++      
++      if (lgstat != CM_GSTATUS_CLOSING)
++          statemap_setbits (cmRail->Levels[clvl].LocalMap, base, lstat | CM_GSTATUS_RESTART, CM_GSTATUS_BITS);
++    }
++    spin_unlock_irqrestore (&cmRail->Lock, flags);
++}
++
++void
++cm_force_offline (EP_RAIL *rail, int offline, unsigned int reason)
++{
++    CM_RAIL *cmRail = rail->ClusterRail;
++    unsigned long flags;
++
++    spin_lock_irqsave (&cmRail->Lock, flags);
++    if (offline)
++      cmRail->OfflineReasons |= reason;
++    else
++      cmRail->OfflineReasons &= ~reason;
++    spin_unlock_irqrestore (&cmRail->Lock, flags);
++}
++
++static void
++cm_remove_rail (EP_SUBSYS *subsys, EP_SYS *epsys, EP_RAIL *rail)
++{
++    CM_SUBSYS  *sys    = (CM_SUBSYS *) subsys;
++    CM_RAIL    *cmRail = sys->Rails[rail->Number];
++    int i, lvl, clvl;
++
++    cm_procfs_rail_fini (cmRail);
++
++    sys->Rails[rail->Number] = NULL;
++    rail->ClusterRail        = NULL;
++
++#if defined(PER_CPU_TIMEOUT)
++    StopPerCpuTimeouts (cmRail);
++#else
++    del_timer_sync (&cmRail->HeartbeatTimer);
++#endif
++    cmRail->NextRunTime      = 0;
++    cmRail->NextDiscoverTime = 0;
++    cmRail->NextHeartbeatTime = 0;
++    
++    for (clvl = 0; clvl < cmRail->NumLevels; clvl++)
++    {
++      for (lvl = 0; lvl <= clvl; lvl++)
++      {
++          CM_LEVEL *level = &cmRail->Levels[lvl];
++          
++          statemap_destroy (level->SubordinateMap[clvl]);
++          
++          for (i = 0; i < level->NumSegs; i++)
++          {
++              statemap_destroy (level->Sgmts[i].Maps[clvl].CurrentInputMap);
++              statemap_destroy (level->Sgmts[i].Maps[clvl].InputMap);
++              statemap_destroy (level->Sgmts[i].Maps[clvl].OutputMap);
++          }
++      }
++      
++      cmRail->Levels[clvl].Online = 0;
++      
++      statemap_destroy (cmRail->Levels[clvl].TmpMap);
++      statemap_destroy (cmRail->Levels[clvl].GlobalMap);
++      statemap_destroy (cmRail->Levels[clvl].LastGlobalMap);
++      statemap_destroy (cmRail->Levels[clvl].SubTreeMap);
++      statemap_destroy (cmRail->Levels[clvl].LocalMap);
++    }
++    
++    spin_lock_destroy (&cmRail->Lock);
++
++    ep_free_inputq (cmRail->Rail, cmRail->PolledQueue);
++    ep_free_inputq (cmRail->Rail, cmRail->IntrQueue);
++    ep_free_outputq (cmRail->Rail, cmRail->MsgQueue);
++
++    KMEM_FREE (cmRail, sizeof (CM_RAIL));
++}
++
++static int
++cm_add_rail (EP_SUBSYS *subsys, EP_SYS *epsys, EP_RAIL *rail)
++{
++    CM_SUBSYS     *sys = (CM_SUBSYS *) subsys;
++    ELAN_POSITION *pos = &rail->Position;
++    CM_RAIL       *cmRail;
++    int lvl, n, nn, clvl, span, i;
++    unsigned long flags;
++
++    KMEM_ZALLOC (cmRail, CM_RAIL *, sizeof (CM_RAIL), 1);
++
++    if (cmRail == NULL)
++      return (ENOMEM);
++    
++    cmRail->Rail     = rail;
++    cmRail->NodeId   = pos->pos_nodeid;
++    cmRail->NumNodes = pos->pos_nodes;
++
++    spin_lock_init (&cmRail->Lock);
++
++    if ((cmRail->IntrQueue   = ep_alloc_inputq (rail, EP_SYSTEMQ_INTR,   sizeof (CM_MSG), CM_INPUTQ_ENTRIES, IntrQueueCallback, cmRail)) == NULL ||
++      (cmRail->PolledQueue = ep_alloc_inputq (rail, EP_SYSTEMQ_POLLED, sizeof (CM_MSG), CM_INPUTQ_ENTRIES, NULL, 0)) == NULL ||
++      (cmRail->MsgQueue    = ep_alloc_outputq (rail, sizeof (CM_MSG), CM_NUM_MSG_BUFFERS)) == NULL)
++    {
++      goto failed;
++    }
++
++    /* point to first "spare" message buffer */
++    cmRail->NextSpareMsg = 0;
++
++    /* Compute the branching ratios from the switcy arity */
++    for (lvl = 0; lvl < CM_MAX_LEVELS; lvl++)
++      BranchingRatios[lvl] = (lvl < pos->pos_levels) ? pos->pos_arity[pos->pos_levels - lvl - 1] : 4;
++    
++    /* now determine the number of levels of hierachy we have */
++    /* and how many nodes per level there are */
++    for (lvl = 0, nn = 1, n = pos->pos_nodes; 
++       n > 1; 
++       nn *= BranchingRatios[lvl], n = n / BranchingRatios[lvl], lvl++)
++    {
++      int       nSegs = (n > BranchingRatios[lvl]) ? BranchingRatios[lvl] : n;
++      int       nNodes = nn * nSegs;
++      CM_LEVEL *level = &cmRail->Levels[lvl];
++
++      for (clvl = 0, span = pos->pos_arity[pos->pos_levels - clvl - 1]; 
++           span < nNodes && clvl < pos->pos_levels - 1;
++           clvl++, span *= pos->pos_arity[pos->pos_levels - clvl - 1])
++          ;
++      
++      level->SwitchLevel = clvl;
++      level->MinNodeId = (pos->pos_nodeid / nNodes) * nNodes;
++      level->NumNodes = nNodes;
++      level->NumSegs = nSegs;
++    }
++    
++    cmRail->NumLevels      = lvl;
++    cmRail->BroadcastLevel = lvl-1;
++
++    CPRINTF4 (2, "%s: NodeId=%d NumNodes=%d NumLevels=%d\n", 
++            rail->Name, pos->pos_nodeid, pos->pos_nodes, cmRail->NumLevels);
++
++    LoadRouteTable (cmRail);
++    
++    /* Init SGMT constants */
++    for (lvl = 0; lvl < cmRail->NumLevels; lvl++)
++    {
++      CM_LEVEL *level = &cmRail->Levels[lvl];
++
++      level->MySgmt = SegmentNo (cmRail, cmRail->NodeId, lvl);
++       
++      for (i = 0; i < CM_SGMTS_PER_LEVEL; i++)
++      {
++          CM_SGMT *sgmt = &level->Sgmts[i];
++        
++          sgmt->MsgNumber = lvl * CM_SGMTS_PER_LEVEL + i;
++          sgmt->Level = lvl;
++          sgmt->Sgmt = i;
++      }
++    }
++
++    /* Init maps for each cluster level */
++    for (clvl = 0; clvl < cmRail->NumLevels; clvl++)
++    {
++      int nNodes = cmRail->Levels[clvl].NumNodes;
++      int mapBits = (nNodes * CM_GSTATUS_BITS) + (nNodes * EP_SVC_NUM_INDICATORS);
++      int clmin;
++      int clmax;
++      int clid = ClusterIds (cmRail, clvl, &clmin, &clmax);
++
++      for (lvl = 0; lvl <= clvl; lvl++)
++      {
++          CM_LEVEL *level = &cmRail->Levels[lvl];
++
++          level->SubordinateMap[clvl] = statemap_create (mapBits);
++
++          for (i = 0; i < level->NumSegs; i++)
++          {
++              level->Sgmts[i].Maps[clvl].CurrentInputMap = statemap_create (mapBits);
++              level->Sgmts[i].Maps[clvl].InputMap        = statemap_create (mapBits);
++              level->Sgmts[i].Maps[clvl].OutputMap       = statemap_create (mapBits);
++          }
++      }
++      
++      cmRail->Levels[clvl].Online = 0;
++
++      cmRail->Levels[clvl].TmpMap        = statemap_create (mapBits);
++      cmRail->Levels[clvl].GlobalMap     = statemap_create (mapBits);
++      cmRail->Levels[clvl].LastGlobalMap = statemap_create (mapBits);
++      cmRail->Levels[clvl].SubTreeMap    = statemap_create (mapBits);
++      cmRail->Levels[clvl].LocalMap      = statemap_create (mapBits);
++
++      /* Flag everyone outside my next lower cluster as sensed offline... */
++      for (i = 0; i < clmin; i++)
++          statemap_setbits (cmRail->Levels[clvl].LocalMap, i * CM_GSTATUS_BITS, CM_GSTATUS_MAY_START, CM_GSTATUS_BITS);
++      
++      for (i = clmax + 1; i < nNodes; i++)
++          statemap_setbits (cmRail->Levels[clvl].LocalMap, i * CM_GSTATUS_BITS, CM_GSTATUS_MAY_START, CM_GSTATUS_BITS);
++      
++      /* ...and set my own state */
++      statemap_setbits (cmRail->Levels[clvl].LocalMap, clid * CM_GSTATUS_BITS,
++                        CM_GSTATUS_CLOSING | CM_GSTATUS_MAY_START, CM_GSTATUS_BITS);
++    }
++    
++    /* compute parameter hash to add to messages */
++    cmRail->ParamHash = EP_PROTOCOL_VERSION;
++    cmRail->ParamHash = cmRail->ParamHash * 127 + CM_PERIODIC_DISCOVER_INTERVAL;
++    cmRail->ParamHash = cmRail->ParamHash * 127 + CM_URGENT_DISCOVER_INTERVAL;
++    cmRail->ParamHash = cmRail->ParamHash * 127 + CM_HEARTBEAT_INTERVAL;
++    cmRail->ParamHash = cmRail->ParamHash * 127 + CM_P2P_DMA_RETRIES;
++    cmRail->ParamHash = cmRail->ParamHash * 127 + CM_P2P_MSG_RETRIES;
++    cmRail->ParamHash = cmRail->ParamHash * 127 + CM_BCAST_MSG_RETRIES;
++    cmRail->ParamHash = cmRail->ParamHash * 127 + CM_TIMER_SCHEDULE_TIMEOUT;
++    cmRail->ParamHash = cmRail->ParamHash * 127 + CM_HEARTBEAT_TIMEOUT;
++    cmRail->ParamHash = cmRail->ParamHash * 127 + CM_DISCOVER_TIMEOUT;
++    cmRail->ParamHash = cmRail->ParamHash * 127 + BT_NBIPUL;
++    cmRail->ParamHash = cmRail->ParamHash * 127 + CM_GSTATUS_BITS;
++    cmRail->ParamHash = cmRail->ParamHash * 127 + EP_SVC_NUM_INDICATORS;
++    cmRail->ParamHash = cmRail->ParamHash * 127 + cmRail->NumLevels;
++    cmRail->ParamHash = cmRail->ParamHash * 127 + cmRail->NumNodes;
++    for (i = 0; i < cmRail->NumLevels; i++)
++      cmRail->ParamHash = cmRail->ParamHash * 127 + BranchingRatios[i];
++    
++#if defined(PER_CPU_TIMEOUT)
++    StartPerCpuTimeouts (cmRail);
++#endif
++
++    spin_lock_irqsave (&cmRail->Lock, flags);
++
++#if !defined(PER_CPU_TIMEOUT)
++    /* Initialise the timer, but don't add it yet, since
++     * __Schedule_Heartbeat() will do this. */
++
++    init_timer (&cmRail->HeartbeatTimer);
++    
++    cmRail->HeartbeatTimer.function = cm_heartbeat_timer;
++    cmRail->HeartbeatTimer.data     = (unsigned long) cmRail;
++    cmRail->HeartbeatTimer.expires  = lbolt + hz;
++#endif
++
++    /* start sending heartbeats */
++    __Schedule_Heartbeat (cmRail);
++
++    /* start discovering who else is out there */
++    LowerTopLevel (cmRail, 0);
++
++    /* connect to myself straight away - I know I'm here */
++    ep_connect_node (rail, cmRail->NodeId);
++    
++    /* add to all rails */
++    sys->Rails[rail->Number] = cmRail;
++    rail->ClusterRail = (void *) cmRail;
++
++    spin_unlock_irqrestore (&cmRail->Lock, flags);
++
++    /* Enable the input queues */
++    ep_enable_inputq (rail, cmRail->PolledQueue);
++    ep_enable_inputq (rail, cmRail->IntrQueue);
++
++    /* Create the procfs entries */
++    cm_procfs_rail_init (cmRail);
++
++    return 0;
++
++ failed:
++    cm_remove_rail (subsys, epsys, rail);
++    return -ENOMEM;
++}
++
++static void
++cm_fini (EP_SUBSYS *subsys, EP_SYS *epsys)
++{
++    CM_SUBSYS *sys = (CM_SUBSYS *) subsys;
++
++    cm_procfs_fini(sys);
++    
++    KMEM_FREE (sys, sizeof (CM_SUBSYS));
++}
++
++int
++cm_init (EP_SYS *sys)
++{
++    CM_SUBSYS *subsys;
++
++    KMEM_ZALLOC (subsys, CM_SUBSYS *, sizeof (CM_SUBSYS), 1);
++
++    if (subsys == NULL)
++      return (ENOMEM);
++
++    subsys->Subsys.Sys        = sys;
++    subsys->Subsys.Name             = "cm";
++    subsys->Subsys.Destroy    = cm_fini;
++    subsys->Subsys.AddRail    = cm_add_rail;
++    subsys->Subsys.RemoveRail = cm_remove_rail;
++
++    ep_subsys_add (sys, &subsys->Subsys);
++
++    cm_procfs_init (subsys);
++
++    /*
++     * Initialise the machineid if it wasn't specified by
++     * the modules.conf file - otherwise truncate it to 
++     * 16 bits.
++     */
++    if (MachineId != -1)
++      MachineId = (uint16_t) MachineId;
++    else
++    {
++#if defined(LINUX_ALPHA)
++      MachineId = (uint16_t)((5 << 12) | HZ);
++#elif defined(LINUX_SPARC)
++      MachineId = (uint16_t)((4 << 12) | HZ);
++#elif defined(LINUX_I386)
++      MachineId = (uint16_t)((3 << 12) | HZ);
++#elif defined( LINUX_IA64)
++      MachineId = (uint16_t)((2 << 12) | HZ);
++#elif defined(LINUX_X86_64)
++      MachineId = (uint16_t)((1 << 12) | HZ);
++#else
++      MachineId = (uint16_t)((0 << 12) | HZ);
++#endif
++    }
++
++    return (0);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/ep/cm.h
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/cm.h        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/cm.h     2005-06-01 23:12:54.633433936 -0400
+@@ -0,0 +1,412 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN_CM_H
++#define __ELAN_CM_H
++
++#ident "@(#)$Id: cm.h,v 1.14.2.1 2004/11/12 10:54:50 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/cm.h,v $*/
++
++#include <elan/statemap.h>
++
++#if defined(DIGITAL_UNIX)
++/*
++ * On Tru64 - SMP doesn't mean Symmetric - cpu 0 is a master cpu and is responsible
++ * for handling all PCI interrupts and "funneled" operations.  When a kernel thread
++ * is made runnable, the scheduler will choose which cpu it will run on at that time,
++ * and will only execute a higher priority thread from another cpu's run queue when 
++ * it becomes totally idle (apparently also including user processes).  Also the 
++ * assert_wait_mesg_timo function uses a per-cpu timeout - these can only get executed
++ * at "preemptable" places - so again have no guarantee on when they will execute if
++ * they happen to be queued on a "hogged" cpu. The combination of these mean that the Tru64
++ * is incapable of scheduling a high priority kernel  thread within a deterministic time
++ * of when it should have become runnable - wonderfull.
++ *
++ * Hence the solution Compaq have proposed it to schedule a timeout onto all of the
++ * cpu's timeouts lists at the maximum frequency that we could want to execute code,
++ * then to handle the scheduling of work between these ourselves.  With a bit of luck
++ * ..... at least one cpu will be sufficiently unloaded to allow us to get a chance
++ * to do our important work.
++ *
++ * However ..... this still is not reliable, since timeouts under Tru64 are still 
++ * only run when the currently running kernel thread "co-operates" by calling one
++ * of a number of functions which is permitted to run the "lwc"s AND is not holding
++ * any spinlocks AND is running ai IPL 0.   However Compaq are unable to provide
++ * any upper limit on the time between the "lwc"'s being run and so it is possible
++ * for all 4 cpus to not run them for an unbounded time.
++ *
++ * The solution proposed is to use the RM_TEMP_BACKDOOR hook which was added to 
++ * hardclock() to "solve" this problem for Memory Channel.  However, since it
++ * is called within the clock interrupt it is not permissible to aquire any
++ * spinlocks, nor to run for "too long".  This means that it is not possible to
++ * call the heartbeat algorithm from this hook.  
++ *
++ * Our solution to these limitations is to use the hook to cause an elan interrupt 
++ * to be delivered, by issueing a mis-aligned SetEvent command - this causes the device 
++ * to trap and ep_cprocTrap() can then run the heartbeat code.  However there is a lock 
++ * order violation between the elan_dev::IntrLock and ep_dev::Lock, so we have to 
++ * use a trylock and if we fail, then hope that when the interrupt is delievered again
++ * some time later we will succeed.
++ *
++ * However this only works if the kernel is able to respond to the Elan interrupt,
++ * so we panic inside the RM_TEMP_BACKDOOR hook if the SetEvent's interrupt has
++ * not been taken for more than an CM_TIMER_SCHEDULE_TIMEOUT interval.
++ *
++ * In fact this is exactly the mechanism that other operating systems use to
++ * execute timeouts, since the hardclock interrupt posts a low priority 
++ * "soft interrupt" which "pre-eempts" the currently running thread and then
++ * executes the timeouts.To block timeouts you use splsoftclock() the same as 
++ * in Tru64.
++ */
++#define PER_CPU_TIMEOUT                       TRUE
++#endif
++
++
++#define CM_SGMTS_PER_LEVEL            8                       /* maximum nodes in each segment */
++#define CM_MAX_LEVELS                 6                       /* maximum depth of tree */
++
++/* message buffers/dmas/events etc */
++#define CM_NUM_NODE_MSG_BUFFERS               (CM_MAX_LEVELS * CM_SGMTS_PER_LEVEL) /* subordinates and leader */
++#define CM_NUM_SPARE_MSG_BUFFERS      8                       /* spare msg buffers for non-connected nodes */
++#define CM_NUM_MSG_BUFFERS            (CM_NUM_NODE_MSG_BUFFERS + CM_NUM_SPARE_MSG_BUFFERS)
++
++#define CM_INPUTQ_ENTRIES             128                     /* # entries in input queue */
++
++#define CM_PERIODIC_DISCOVER_INTERVAL (5000)          /* 5s (infrequent resolution of established leader conflicts) */
++#define CM_URGENT_DISCOVER_INTERVAL   (50)            /* 0.05s (more frequently than heartbeats 'cause they don't retry) */
++#define CM_HEARTBEAT_INTERVAL         (125)           /* 0.125s */
++#define CM_TIMER_SCHEDULE_TIMEOUT     (4000)          /* 4s     Maximum time before a timer that's secheduled to run gets to run (eg blocked in interrupt handlers etc) */
++#define CM_THREAD_SCHEDULE_TIMEOUT    (30000)         /* 30s    Maximum time before a thread that's scheduled to run gets to run */
++#define CM_THREAD_RUNNING_TIMEOUT     (30000)         /* 30s    Don't expect the manager thread to be running longer than this */
++
++#ifdef PER_CPU_TIMEOUT
++#define CM_PERCPU_TIMEOUT_INTERVAL    (50)            /* 0.05s (must be less than all above intervals) */
++#define CM_PACEMAKER_INTERVAL         (500)           /* 0.05s */
++
++#define CM_HEARTBEAT_OVERDUE          (250)           /* 0.25s Maximum time a timeout can be overdue before taking extreme action */
++#endif
++
++#define CM_P2P_DMA_RETRIES            31
++
++/* We expect at least 1 point-to-point message in CM_P2P_MSG_RETRIES
++ * attempts to send one to be successfully received */
++#define CM_P2P_MSG_RETRIES            8
++
++/* We expect at least 1 broadcast message in CM_BCAST_MSG_RETRIES attempts
++ * to send one to be successfully received. */
++#define CM_BCAST_MSG_RETRIES          40
++
++/* Heartbeat timeout allows for a node stalling and still getting its
++ * heartbeat. The 2 is to allow for unsynchronised polling times. */
++#define CM_HEARTBEAT_TIMEOUT          (CM_TIMER_SCHEDULE_TIMEOUT + (2 + CM_P2P_MSG_RETRIES) * CM_HEARTBEAT_INTERVAL)
++
++/* Discover timeout must be > CM_HEARTBEAT_TIMEOUT to guarantee that people
++ * who don't see discovery are considered dead by their leader.  This
++ * ensures that by the time a node "discovers" it is a leader of a segment,
++ * the previous leader of that segment will have been deemed to be dead by
++ * its the parent segment's leader */
++#define CM_DISCOVER_TIMEOUT           (CM_TIMER_SCHEDULE_TIMEOUT + (2 + CM_BCAST_MSG_RETRIES) * CM_URGENT_DISCOVER_INTERVAL)
++
++#define CM_WAITING_TIMEOUT            (CM_DISCOVER_TIMEOUT * 100)
++
++/*
++ * Convert all timeouts specified in mS into "ticks"
++ */
++#define MSEC2TICKS(MSEC)              (((MSEC)*HZ)/1000)
++
++
++/* statemap entry */
++typedef struct cm_state_entry
++{
++    int16_t           level;                  /* cluster level to apply to */
++    int16_t         offset;                   /* from statemap_findchange() */
++    uint16_t          seg[BT_NBIPUL/16];      /* ditto */
++} CM_STATEMAP_ENTRY;
++
++/* offset is >= 0 for a change to apply and */
++#define STATEMAP_NOMORECHANGES        (-1)            /* end of a set of updates */
++#define STATEMAP_RESET                (-2)            /* reset the target map */
++#define STATEMAP_NOOP         (-3)            /* null token */
++
++/* CM message format */
++typedef int8_t        CM_SEQ;                         /* heartbeat sequence numbers; at least 2 bits, signed */
++
++/*
++ * The message header is received into the last 64 byte block of 
++ * the input queue and the Version *MUST* be the last word of the 
++ * block to ensure that we can see that the whole of the message
++ * has reached main memory after we've seen the input queue pointer
++ * have been updated.
++ */
++typedef struct ep_cm_hdr
++{
++    uint32_t         Pad0;
++    uint32_t         Pad1;
++
++    uint8_t          Type;
++    uint8_t          Level;
++    CM_SEQ           Seq;                     /* precision at least 2 bits each*/
++    CM_SEQ           AckSeq;
++    
++    uint16_t         NumMaps;
++    uint16_t         MachineId;
++
++    uint16_t         NodeId;
++    uint16_t         Checksum;
++
++    uint32_t           Timestamp;
++    uint32_t           ParamHash;
++    uint32_t         Version;
++} CM_HDR;
++
++#define CM_HDR_SIZE       sizeof (CM_HDR)
++
++typedef struct cm_msg
++{
++    union {
++      CM_STATEMAP_ENTRY   Statemaps[1];               /* piggy-backed statemap updates start here */
++      uint8_t             Space[EP_SYSTEMQ_MSG_MAX - CM_HDR_SIZE];
++    } Payload;
++    
++    CM_HDR                Hdr;
++} CM_MSG;
++
++/* The maximum number of statemap entries that can fit within an EP_CM_MSG_BUFFER */
++#define CM_MSG_MAXMAPS                (offsetof (CM_MSG, Hdr) / sizeof (CM_STATEMAP_ENTRY))
++#define CM_MSG_MAP(mapno)     (CM_MSG_MAXMAPS - (mapno) - 1)
++
++/* The actual special message base & size, including 'nmaps' piggy-backed statemap entries */
++#define CM_MSG_BASE(nmaps)    (nmaps == 0 ? offsetof (CM_MSG, Hdr) : offsetof (CM_MSG, Payload.Statemaps[CM_MSG_MAXMAPS - nmaps]))
++#define CM_MSG_SIZE(nmaps)    (sizeof (CM_MSG) - CM_MSG_BASE(nmaps))
++
++#define CM_MSG_VERSION                                0xcad00005
++#define CM_MSG_TYPE_RESOLVE_LEADER            0
++#define CM_MSG_TYPE_DISCOVER_LEADER           1
++#define CM_MSG_TYPE_NOTIFY                    2
++#define CM_MSG_TYPE_DISCOVER_SUBORDINATE      3
++#define CM_MSG_TYPE_IMCOMING                  4
++#define CM_MSG_TYPE_HEARTBEAT                 5
++#define CM_MSG_TYPE_REJOIN                    6
++
++/* CM machine segment */
++typedef struct cm_sgmtMaps
++{
++    u_char       InputMapValid;                       /* Input map has been set */
++    u_char       OutputMapValid;              /* Output map has been set */
++    u_char       SentChanges;                 /* got an outstanding STATEMAP_NOMORECHANGES to send */
++    statemap_t  *OutputMap;                   /* state to send */
++    statemap_t  *InputMap;                    /* state received */
++    statemap_t  *CurrentInputMap;             /* state being received */
++} CM_SGMTMAPS;
++
++typedef struct cm_sgmt
++{
++   u_char       State;
++   u_char       SendMaps;
++   u_char       MsgAcked;
++   CM_SEQ     MsgSeq;
++   CM_SEQ     AckSeq;
++   u_int      NodeId;
++   long               UpdateTick;
++   long               WaitingTick;
++   uint32_t   Timestamp;
++   CM_SGMTMAPS  Maps[CM_MAX_LEVELS];          /* Maps[i] == state for cluster level i */
++   u_short      MsgNumber;                    /* msg buffer to use */
++   u_short    NumMaps;                        /* # maps in message buffer */
++   u_short      Level;
++   u_short      Sgmt;
++} CM_SGMT;
++
++#define CM_SGMT_ABSENT                0               /* no one there at all */
++#define CM_SGMT_WAITING               1               /* waiting for subtree to connect */
++#define CM_SGMT_COMING                2               /* expecting a subtree to reconnect */
++#define CM_SGMT_PRESENT               3               /* connected */
++
++typedef struct cm_level
++{
++    int              SwitchLevel;
++    u_int            MinNodeId;
++    u_int              NumNodes;
++    u_int              NumSegs;
++    u_int              MySgmt;
++   
++    /* SubordinateMap[i] == OR of all subordinate maps on this level and down for cluster level i */
++    u_char             SubordinateMapValid[CM_MAX_LEVELS];
++    statemap_t        *SubordinateMap[CM_MAX_LEVELS];
++
++    /* maps/flags for this cluster level */
++    u_int              Online:1;                              /* I've gone online (seen myself running) */
++    u_int            Restarting:1;                            /* driving my owm restart bit */
++    u_char           OfflineReasons;                          /* forced offline by broadcast */
++
++    u_char             GlobalMapValid;
++    u_char             SubTreeMapValid;
++    u_long           Connected;
++
++    statemap_t        *LocalMap;              /* state bits I drive */
++    statemap_t        *SubTreeMap;            /* OR of my and my subtree states */
++    statemap_t        *GlobalMap;             /* OR of all node states */
++    statemap_t        *LastGlobalMap;         /* last map I saw */
++    statemap_t        *TmpMap;                        /* scratchpad */
++
++    CM_SGMT          Sgmts[CM_SGMTS_PER_LEVEL];
++} CM_LEVEL;
++
++#define CM_ROLE_LEADER_CANDIDATE      0
++#define CM_ROLE_LEADER                        1
++#define CM_ROLE_SUBORDINATE           2
++
++/* global status bits */
++#define CM_GSTATUS_STATUS_MASK                0x03    /* bits nodes drive to broadcast their status */
++#define CM_GSTATUS_ABSENT             0x00    /* Off the network */
++#define CM_GSTATUS_STARTING           0x01    /* I'm waiting for everyone to see me online */
++#define CM_GSTATUS_RUNNING              0x03  /* up and running */
++#define CM_GSTATUS_CLOSING            0x02    /* I'm waiting for everyone to see me offline */
++
++#define CM_GSTATUS_ACK_MASK           0x0c    /* bits node drive to ack other status */
++#define CM_GSTATUS_MAY_START          0x04    /* Everyone thinks I may not start */
++#define CM_GSTATUS_MAY_RUN            0x08    /* Everyone thinks I may not run */
++
++#define CM_GSTATUS_RESTART            0x10    /* Someone thinks I should restart */
++#define CM_GSTATUS_BITS                       5
++
++#define CM_GSTATUS_BASE(node)         ((node) * CM_GSTATUS_BITS)
++
++#if defined(PER_CPU_TIMEOUT)
++typedef struct cm_timeout_data
++{
++    long              ScheduledAt;                            /* lbolt timeout was scheduled to run at */
++
++    unsigned long       EarlyCount;                           /* # times run early than NextRun */
++    unsigned long     MissedCount;                            /* # times run on time - but someone else was running it */
++    unsigned long       WastedCount;                          /* # times we failed to get the spinlock */
++    unsigned long     WorkCount;                              /* # times we're the one running */
++
++    unsigned long     WorstDelay;                             /* worst scheduling delay */
++    unsigned long     BestDelay;                              /* best scheduling delay */
++
++    unsigned long     WorstLockDelay;                         /* worst delay before getting rail->Lock */
++
++    unsigned long     WorstHearbeatDelay;                     /* worst delay before calling DoHeartbeatWork */
++} CM_TIMEOUT_DATA;
++#endif
++
++typedef struct cm_rail
++{
++    EP_RAIL         *Rail;                                    /* rail we're associated with */
++    struct list_head   Link;                                  /*   and linked on the CM_SUBSYS */
++
++    uint32_t         ParamHash;                               /* hash of critical parameters */
++    uint32_t           Timestamp;
++    long             DiscoverStartTick;                       /* when discovery start */
++
++    unsigned int       NodeId;                                        /* my node id */
++    unsigned int       NumNodes;                              /*   and number of nodes */
++    unsigned int       NumLevels;                             /* number of levels computed from machine size */
++    int                      BroadcastLevel;
++    long             BroadcastLevelTick;
++    unsigned int       TopLevel;                              /* level at which I'm not a leader */
++    unsigned char      Role;                                  /* state at TopLevel */
++
++    EP_INPUTQ       *PolledQueue;                             /* polled input queue */
++    EP_INPUTQ       *IntrQueue;                               /* intr input queue */
++    EP_OUTPUTQ              *MsgQueue;                                /* message  */
++    unsigned int       NextSpareMsg;                          /* next "spare" message buffer to use */
++
++    EP_CM_RAIL_STATS   Stats;                                 /* statistics */
++
++    kmutex_t         Mutex;
++    spinlock_t               Lock;
++    
++    long             NextHeartbeatTime;                       /* next time to check/send heartbeats */
++    long             NextDiscoverTime;                        /* next time to progress discovery  */
++    long             NextRunTime;                             /* the earlier of the above two or intr requires inputq poll*/
++
++    unsigned int       OfflineReasons;                                /* forced offline by procfs/manager thread stuck */
++
++#if defined(PER_CPU_TIMEOUT)
++    spinlock_t               HeartbeatTimeoutsLock;                   /* spinlock to sequentialise per-cpu timeouts */
++    long             HeartbeatTimeoutsStarted;                /* bitmap of which timeouts have started */
++    long             HeartbeatTimeoutsStopped;                /* bitmap of which timeouts have stopped */
++    long             HeartbeatTimeoutsShouldStop;             /* flag to indicate timeouts should stop */
++    kcondvar_t               HeartbeatTimeoutsWait;                   /* place to sleep waiting for timeouts to stop */
++    long             HeartbeatTimeoutRunning;                 /* someone is running the timeout - don't try for the lock */
++
++    long             HeartbeatTimeoutOverdue;                 /* heartbeat seen as overdue - interrupt requested */
++
++    CM_TIMEOUT_DATA   *HeartbeatTimeoutsData;                 /* per timeout data */
++#else
++    struct timer_list  HeartbeatTimer;                                /* timer for heartbeat/discovery */
++#endif
++
++    CM_LEVEL           Levels[CM_MAX_LEVELS];
++} CM_RAIL;
++
++/* OfflineReasons (both per-rail and  */
++#define CM_OFFLINE_BROADCAST          (1 << 0)
++#define CM_OFFLINE_PROCFS             (1 << 1)
++#define CM_OFFLINE_MANAGER            (1 << 2)
++
++typedef struct cm_subsys
++{
++    EP_SUBSYS         Subsys;
++    CM_RAIL            *Rails[EP_MAX_RAILS];
++} CM_SUBSYS;
++
++extern int  MachineId;
++
++extern void cm_node_disconnected (EP_RAIL *rail, unsigned nodeId);
++extern void cm_restart_node (EP_RAIL *rail, unsigned nodeId);
++extern void cm_restart_comms (CM_RAIL *cmRail);
++extern int  cm_init (EP_SYS *sys);
++
++extern void DisplayRail(EP_RAIL *rail);
++extern void DisplaySegs (EP_RAIL *rail);
++extern void DisplayStatus (EP_RAIL *rail);
++
++typedef struct proc_private
++{
++    struct nodeset_private *pr_next;
++    EP_RAIL                *pr_rail;
++    char                 *pr_data;
++    int                     pr_data_len;
++    unsigned              pr_off;
++    unsigned              pr_len;
++    DisplayInfo             pr_di;
++} PROC_PRIVATE;
++
++extern void    proc_character_fill (long mode, char *fmt, ...);
++extern int     proc_release (struct inode *inode, struct file *file);
++extern ssize_t proc_read (struct file *file, char *buf, size_t count, loff_t *ppos);
++
++
++extern void DisplayNodeMaps  (DisplayInfo *di, CM_RAIL *cmRail);
++extern void DisplayNodeSgmts (DisplayInfo *di, CM_RAIL *cmRail);
++extern void DisplayRailDo    (DisplayInfo *di, EP_RAIL *rail);
++
++extern int    cm_read_cluster(EP_RAIL *rail,char *page);
++extern void   cm_force_offline (EP_RAIL *rail, int offline, unsigned int reason);
++
++extern int    cm_svc_indicator_set      (EP_RAIL *rail, int svc_indicator);
++extern int    cm_svc_indicator_clear    (EP_RAIL *rail, int svc_indicator);
++extern int    cm_svc_indicator_is_set   (EP_RAIL *rail, int svc_indicator, int nodeId);
++extern int    cm_svc_indicator_bitmap   (EP_RAIL *rail, int svc_indicator, bitmap_t * bitmap, int low, int nnodes);
++
++/* cm_procfs.c */
++extern void   cm_procfs_init (CM_SUBSYS *subsys);
++extern void   cm_procfs_fini (CM_SUBSYS *subsys);
++extern void   cm_procfs_rail_init (CM_RAIL *rail);
++extern void   cm_procfs_rail_fini (CM_RAIL *rail);
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++#endif /* __ELAN_CM_H */
++
+Index: linux-2.4.21/drivers/net/qsnet/ep/cm_procfs.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/cm_procfs.c 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/cm_procfs.c      2005-06-01 23:12:54.633433936 -0400
+@@ -0,0 +1,254 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2005 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: cm_procfs.c,v 1.5 2004/05/14 09:23:13 daniel Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/cm_procfs.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++
++#include "kcomm_vp.h"
++#include "debug.h"
++#include "cm.h"
++#include <elan/epsvc.h>
++
++#include <qsnet/procfs_linux.h>
++
++extern char *sprintClPeers (char *str, CM_RAIL *cmRail, int clvl);
++
++static int
++proc_read_cluster(char *page, char **start, off_t off,
++              int count, int *eof, void *data)
++{
++    CM_RAIL *cmRail = (CM_RAIL *) data;
++    char    *p      = page;
++
++    page[0] = 0;
++
++    if (cmRail->Rail->State != EP_RAIL_STATE_RUNNING)
++      p += sprintf(p, "<not running>\n");
++    else
++    {
++      CM_LEVEL *cmLevel;
++      unsigned long flags;
++      int  i, j;
++      char clNodeStr[32]; /* [%d-%d][%d-%d] */
++      char seperate_with;
++
++      struct { int val; char *name; } bitvals[] = {
++          {CM_OFFLINE_BROADCAST, "Broadcast"},
++          {CM_OFFLINE_PROCFS,    "Offline"},
++          {CM_OFFLINE_MANAGER,   "Manager"}};
++      
++      spin_lock_irqsave (&cmRail->Lock, flags);
++      
++      for (i = 0; i < cmRail->NumLevels; i++)
++      {
++          cmLevel = &cmRail->Levels[i];
++          
++          p += sprintf(p, "%23s %7s ", sprintClPeers (clNodeStr, cmRail, i), cmLevel->Online?"Online":"Offline");
++          
++          if ((cmLevel->Online ) | ( cmLevel->Connected > 0))
++              p += sprintf(p, "Connected=%lu ", cmLevel->Connected);
++          
++          seperate_with = '<';
++          
++          if ( cmLevel->Restarting ) {
++              p += sprintf(p, "%cRestarting", seperate_with);
++              seperate_with = ',';
++          }
++          
++          if ( ! (cmLevel->GlobalMapValid & cmLevel->SubTreeMapValid )) {
++              p += sprintf(p, "%cMap Not Valid", seperate_with);
++              seperate_with = ',';
++          }
++          
++          if ( cmLevel->OfflineReasons ) {
++              for (j = 0; j < sizeof (bitvals)/sizeof(bitvals[0]); j++)
++                  if (cmLevel->OfflineReasons & bitvals[j].val) {
++                      p += sprintf(p, "%c%s", seperate_with, bitvals[j].name);
++                      seperate_with = ',';
++                  }
++          }
++          if ( cmRail->OfflineReasons ) {
++              for (j = 0; j < sizeof (bitvals)/sizeof(bitvals[0]); j++)
++                  if (cmRail->OfflineReasons & bitvals[j].val) {
++                      p += sprintf(p, "%c%s", seperate_with, bitvals[j].name);
++                      seperate_with = ',';
++                  }
++          }
++          
++          if ( seperate_with != '<' ) 
++              p += sprintf(p,">\n");
++          else
++              p += sprintf(p,"\n");
++      }
++      
++      spin_unlock_irqrestore (&cmRail->Lock, flags);
++    }
++
++    return qsnet_proc_calc_metrics (page, start, off, count, eof, p - page);
++}
++
++static struct rail_info
++{
++    char *name;
++    int (*read_func) (char *page, char **start, off_t off, int count, int *eof, void *data);
++    int (*write_func) (struct file *file, const char *buf, unsigned long count, void *data);
++} rail_info[] = {
++    {"cluster", proc_read_cluster, NULL},
++};
++
++struct proc_dir_entry *svc_indicators_root;
++
++typedef struct svc_indicator_data
++{
++    int       svc_indicator;
++    EP_RAIL  *rail;
++} SVC_INDICATOR_DATA;
++
++static SVC_INDICATOR_DATA svc_indicator_data[EP_SVC_NUM_INDICATORS][EP_MAX_RAILS];
++static char              *svc_indicator_names[EP_SVC_NUM_INDICATORS] = EP_SVC_NAMES;
++
++static int
++proc_read_svc_indicator_rail_bitmap (char *page, char **start, off_t off,
++                                   int count, int *eof, void *data)
++{
++    SVC_INDICATOR_DATA  *svc_data = (SVC_INDICATOR_DATA  *)data;
++    unsigned int       nnodes   = ep_numnodes (ep_system());
++    bitmap_t          *bitmap;
++
++    KMEM_ZALLOC (bitmap, bitmap_t *, (BT_BITOUL(EP_MAX_NODES) * sizeof (bitmap_t)), 1);
++
++    cm_svc_indicator_bitmap (svc_data->rail, svc_data->svc_indicator, bitmap, 0, nnodes);
++
++    ep_sprintf_bitmap (page, PAGESIZE, bitmap, 0, 0, nnodes);
++    
++    KMEM_FREE (bitmap, (BT_BITOUL(EP_MAX_NODES) * sizeof (bitmap_t)));
++    
++    strcat (page, "\n");
++
++    return (qsnet_proc_calc_metrics (page, start, off, count, eof, strlen(page)));
++}
++
++static int
++proc_read_svc_indicator_bitmap(char *page, char **start, off_t off,
++                             int count, int *eof, void *data)
++{
++    unsigned int         num      = (unsigned long) data;
++    EP_SYS              *sys      = ep_system();
++    unsigned int         nnodes   = ep_numnodes (sys);
++    bitmap_t            *bitmap;
++
++    KMEM_ALLOC(bitmap, bitmap_t *, (BT_BITOUL(EP_MAX_NODES) * sizeof (bitmap_t)), 1);
++     
++    ep_svc_indicator_bitmap (sys, num, bitmap, 0, nnodes);
++
++    ep_sprintf_bitmap (page, PAGESIZE, bitmap, 0, 0, nnodes);
++    
++    KMEM_FREE (bitmap, (BT_BITOUL(EP_MAX_NODES) * sizeof (bitmap_t)));
++    
++    strcat (page, "\n");
++
++    return (qsnet_proc_calc_metrics (page, start, off, count, eof, strlen(page)));
++}
++
++void
++cm_procfs_rail_init (CM_RAIL *cmRail)
++{
++    EP_RAIL *rail = cmRail->Rail;
++    struct proc_dir_entry *p;
++    int i;
++    
++    for (i = 0; i < sizeof (rail_info)/sizeof (rail_info[0]); i++)
++    {
++      if ((p = create_proc_entry (rail_info[i].name, 0, cmRail->Rail->ProcDir)) != NULL)
++      {
++          p->read_proc  = rail_info[i].read_func;
++          p->write_proc = rail_info[i].write_func;
++          p->data       = cmRail;
++          p->owner      = THIS_MODULE;
++      }
++    }
++
++    if ((rail->SvcIndicatorDir = proc_mkdir ("svc_indicators", cmRail->Rail->ProcDir)) != NULL)
++    {
++      for (i = 0; i < EP_SVC_NUM_INDICATORS; i++)
++      {
++          if ((p = create_proc_entry (svc_indicator_names[i], 0, rail->SvcIndicatorDir)) != NULL)
++          {
++              svc_indicator_data[i][rail->Number].svc_indicator = i;
++              svc_indicator_data[i][rail->Number].rail          = rail; 
++              
++              p->write_proc = NULL;
++              p->read_proc  = proc_read_svc_indicator_rail_bitmap;
++              p->data       = (void *)&svc_indicator_data[i][rail->Number];
++              p->owner      = THIS_MODULE;
++          }
++      }
++    }
++}
++
++void
++cm_procfs_rail_fini (CM_RAIL *cmRail)
++{
++    EP_RAIL *rail = cmRail->Rail;
++    int i;
++
++    if (rail->SvcIndicatorDir)
++    {
++      for (i = 0; i < EP_SVC_NUM_INDICATORS; i++)
++          remove_proc_entry (svc_indicator_names[i], rail->SvcIndicatorDir);
++
++      remove_proc_entry ("svc_indicators", cmRail->Rail->ProcDir);
++    }
++
++    for (i = 0; i < sizeof (rail_info)/sizeof (rail_info[0]); i++)
++      remove_proc_entry (rail_info[i].name, cmRail->Rail->ProcDir);
++}
++
++void
++cm_procfs_init (CM_SUBSYS *subsys)
++{
++    struct proc_dir_entry *p;
++    int i;
++
++    qsnet_proc_register_hex (ep_config_root, "machine_id",      &MachineId,      0);
++
++    if ((svc_indicators_root = proc_mkdir("svc_indicators", ep_procfs_root)) != NULL)
++    {
++      for (i = 0; i < EP_SVC_NUM_INDICATORS; i++)
++      {
++          if ((p = create_proc_entry (svc_indicator_names[i], 0, svc_indicators_root)) != NULL)
++          {
++              p->write_proc = NULL;
++              p->read_proc  = proc_read_svc_indicator_bitmap;
++              p->data       = (void *)(long) i;
++              p->owner      = THIS_MODULE;
++          }
++      }
++      
++    }
++}
++
++void
++cm_procfs_fini (CM_SUBSYS *subsys)
++{
++    int i;
++
++    if (svc_indicators_root)
++    {
++      for (i = 0; i < EP_SVC_NUM_INDICATORS; i++)
++          remove_proc_entry (svc_indicator_names[i], svc_indicators_root);
++      
++      remove_proc_entry ("svc_indicators",   ep_procfs_root);
++    }
++
++    remove_proc_entry ("machine_id",      ep_config_root);
++}
+Index: linux-2.4.21/drivers/net/qsnet/ep/commands_elan4.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/commands_elan4.c    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/commands_elan4.c 2005-06-01 23:12:54.634433784 -0400
+@@ -0,0 +1,173 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: commands_elan4.c,v 1.2 2003/10/23 15:07:53 david Exp $ $Name: QSNETMODULES-4-30_20050128 $"
++/*      $Source: /cvs/master/quadrics/epmod/commands_elan4.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++
++#include "kcomm_vp.h"
++#include "kcomm_elan4.h"
++#include "debug.h"
++
++#include <elan4/trtype.h>
++
++static __inline__ void
++elan4_command_write (ELAN4_CQ *cq, E4_uint64 val, unsigned off)
++{
++    writeq (val, cq->cq_mapping + offsetof (E4_CommandPort, Command[off]));
++}
++
++void
++elan4_nop_cmd (ELAN4_CQ *cq, E4_uint64 tag)
++{
++    elan4_command_write (cq, tag | NOP_CMD, 0);
++}
++
++void
++elan4_write_dword_cmd (ELAN4_CQ *cq, E4_Addr addr, E4_uint64 data)
++{
++    elan4_command_write (cq, addr | WRITE_DWORD_CMD, 0);
++    elan4_command_write (cq, data, 1);
++}
++
++void
++elan4_add_dword_cmd (ELAN4_CQ *cq, E4_Addr addr, E4_uint64 data)
++{
++    elan4_command_write (cq, addr | ADD_DWORD_CMD, 0);
++    elan4_command_write (cq, data,                 1);
++}
++
++void
++elan4_copy64_cmd (ELAN4_CQ *cq, E4_Addr from, E4_Addr to, E4_uint32 datatype)
++{
++    elan4_command_write (cq, from | (datatype << COPY64_DATA_TYPE_SHIFT) | COPY64_CMD, 0);
++    elan4_command_write (cq, to   | (datatype << COPY64_DATA_TYPE_SHIFT),              1);
++}
++
++void
++elan4_interrupt_cmd (ELAN4_CQ *cq, E4_uint64 cookie)
++{
++    elan4_command_write (cq, (cookie << E4_MAIN_INT_SHIFT) | INTERRUPT_CMD, 0);
++}
++
++
++void 
++elan4_run_thread_cmd (ELAN4_CQ *cq, E4_ThreadRegs *regs)
++{
++    elan4_command_write (cq, regs->Registers[0] | RUN_THREAD_CMD, 0);
++    elan4_command_write (cq, regs->Registers[1],                  1);
++    elan4_command_write (cq, regs->Registers[2],                  2);
++    elan4_command_write (cq, regs->Registers[3],                  3);
++    elan4_command_write (cq, regs->Registers[4],                  4);
++    elan4_command_write (cq, regs->Registers[5],                  5);
++    elan4_command_write (cq, regs->Registers[6],                  6);
++}
++
++void
++elan4_run_dma_cmd (ELAN4_CQ *cq, E4_DMA *dma)
++{
++    E4_uint64 *dmaptr = (E4_uint64 *) dma;
++
++    elan4_command_write (cq, dmaptr[0] | RUN_DMA_CMD, 0);
++    elan4_command_write (cq, dmaptr[1],               1);
++    elan4_command_write (cq, dmaptr[2],               2);
++    elan4_command_write (cq, dmaptr[3],               3);
++    elan4_command_write (cq, dmaptr[4],               4);
++    elan4_command_write (cq, dmaptr[5],               5);
++    elan4_command_write (cq, dmaptr[6],               6);
++}
++
++void
++elan4_set_event_cmd (ELAN4_CQ *cq, E4_Addr event)
++{
++    elan4_command_write (cq, event | SET_EVENT_CMD, 0);
++}
++
++void
++elan4_set_eventn_cmd (ELAN4_CQ *cq, E4_Addr event, E4_uint32 count)
++{
++    elan4_command_write (cq, SET_EVENTN_CMD,0);
++    elan4_command_write (cq, event | count, 1);
++}
++    
++void
++elan4_wait_event_cmd (ELAN4_CQ *cq, E4_Addr event, E4_uint64 candt, E4_uint64 param0, E4_uint64 param1)
++{
++    elan4_command_write (cq, event | WAIT_EVENT_CMD, 0);
++    elan4_command_write (cq, candt,                  1);
++    elan4_command_write (cq, param0,                 2);
++    elan4_command_write (cq, param1,                 3);
++}
++
++void
++elan4_open_packet (ELAN4_CQ *cq, E4_uint64 command)
++{
++    elan4_command_write (cq, command | OPEN_STEN_PKT_CMD, 0);
++}
++
++void
++elan4_guard (ELAN4_CQ *cq, E4_uint64 command)
++{
++    elan4_command_write (cq, command | GUARD_CMD, 0);
++}
++
++void
++elan4_sendtrans0 (ELAN4_CQ *cq, E4_uint16 trtype, E4_uint64 addr)
++{
++    elan4_command_write (cq, (trtype << 16) | SEND_TRANS_CMD, 0);
++    elan4_command_write (cq, addr,                              1);
++}
++
++void
++elan4_sendtrans1 (ELAN4_CQ *cq, E4_uint16 trtype, E4_uint64 addr, E4_uint64 p0)
++{
++    elan4_command_write (cq, (trtype << 16) | SEND_TRANS_CMD, 0);
++    elan4_command_write (cq, addr,                              1);
++    elan4_command_write (cq, p0,                                2);
++}
++
++void
++elan4_sendtrans2 (ELAN4_CQ *cq, E4_uint16 trtype, E4_uint64 addr, E4_uint64 p0, E4_uint64 p1)
++{
++    elan4_command_write (cq, (trtype << 16) | SEND_TRANS_CMD, 0);
++    elan4_command_write (cq, addr,                              1);
++    elan4_command_write (cq, p0,                                2);
++    elan4_command_write (cq, p1,                                3);
++}
++
++void
++elan4_sendtransn (ELAN4_CQ *cq, E4_uint16 trtype, E4_uint64 addr, ...)
++{
++    E4_uint32    ndword = ((trtype & TR_SIZE_MASK) >> TR_SIZE_SHIFT);
++    va_list      ap;
++    register int i;
++
++    elan4_command_write (cq, (trtype << 16) | SEND_TRANS_CMD, 0);
++    elan4_command_write (cq, addr,                              1);
++    
++    va_start (ap, addr);
++    for (i = 2; i < ndword+2; i++) 
++      elan4_command_write (cq, va_arg (ap, E4_uint64), i);
++    va_end (ap);
++}
++
++void
++elan4_sendtransp (ELAN4_CQ *cq, E4_uint16 trtype, E4_uint64 addr, E4_uint64 *ptr)
++{
++    E4_uint32    ndword = ((trtype &TR_SIZE_MASK) >> TR_SIZE_SHIFT);
++    register int i;
++
++    elan4_command_write (cq, (trtype << 16) | SEND_TRANS_CMD, 0);
++    elan4_command_write (cq, addr,                            1);
++    for (i = 2; i < ndword+2; i++)
++      elan4_command_write (cq, *ptr++, i);
++}
++
+Index: linux-2.4.21/drivers/net/qsnet/ep/conf_linux.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/conf_linux.c        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/conf_linux.c     2005-06-01 23:12:54.635433632 -0400
+@@ -0,0 +1,309 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: conf_linux.c,v 1.37.2.3 2005/01/18 14:47:35 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/conf_linux.c,v $ */
++
++#include <qsnet/kernel.h>
++#include <qsnet/autoconf.h>
++
++#include <elan/kcomm.h>
++#include <elan/epsvc.h>
++#include <elan/epcomms.h>
++
++#include "cm.h"
++
++#include "conf_linux.h"
++
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/reboot.h>
++#include <linux/notifier.h>
++
++/* Module parameters */
++unsigned int epdebug       = 0;
++unsigned int epdebug_console = 0;
++unsigned int epdebug_cmlevel = 0;
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++unsigned int epdebug_check_sum = 0;
++#endif
++int        disabled        = 0;
++int          sdram_assert    = 0;
++int          assfail_mode    = 0;
++int        txd_stabilise   = 7;
++int        portals_envelopes = 0;
++
++/* External module parameters */
++extern int    MaxSwitchLevels;
++extern int      RejoinCheck;
++extern int      RejoinPanic;
++extern int      PositionCheck;
++extern int      MachineId;
++
++/* Module globals */
++EP_SYS          epsys;
++
++#ifdef MODULE
++MODULE_AUTHOR("Quadrics Ltd");
++MODULE_DESCRIPTION("Elan Kernel Comms");
++
++MODULE_LICENSE("GPL");
++
++MODULE_PARM(epdebug,         "i");
++MODULE_PARM(epdebug_console, "i");
++MODULE_PARM(epdebug_cmlevel, "i");
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++MODULE_PARM(epdebug_check_sum, "i");
++#endif
++MODULE_PARM(disabled,        "i");
++
++MODULE_PARM(MachineId,       "i");
++MODULE_PARM(RejoinPanic,     "i");
++MODULE_PARM(RejoinCheck,     "i");
++MODULE_PARM(PositionCheck,   "i");
++MODULE_PARM(MaxSwitchLevels, "i");
++
++MODULE_PARM(sdram_assert,    "i");
++MODULE_PARM(assfail_mode,    "i");
++MODULE_PARM(txd_stabilise,   "i");
++MODULE_PARM(portals_envelopes,"i");
++
++/* epcomms.c large message service functions */
++EXPORT_SYMBOL(ep_alloc_xmtr);
++EXPORT_SYMBOL(ep_free_xmtr);
++EXPORT_SYMBOL(ep_transmit_message);
++EXPORT_SYMBOL(ep_multicast_message);
++EXPORT_SYMBOL(ep_transmit_rpc);
++
++EXPORT_SYMBOL(ep_alloc_rcvr);
++EXPORT_SYMBOL(ep_free_rcvr);
++EXPORT_SYMBOL(ep_queue_receive);
++EXPORT_SYMBOL(ep_requeue_receive);
++EXPORT_SYMBOL(ep_rpc_put);
++EXPORT_SYMBOL(ep_rpc_get);
++EXPORT_SYMBOL(ep_complete_rpc);
++EXPORT_SYMBOL(ep_complete_receive);
++
++EXPORT_SYMBOL(ep_poll_transmits);
++EXPORT_SYMBOL(ep_enable_txcallbacks);
++EXPORT_SYMBOL(ep_disable_txcallbacks);
++
++/* epcomms.c functions for accessing fields of rxds/txds */
++EXPORT_SYMBOL(ep_rxd_arg);
++EXPORT_SYMBOL(ep_rxd_len);
++EXPORT_SYMBOL(ep_rxd_isrpc);
++EXPORT_SYMBOL(ep_rxd_envelope);
++EXPORT_SYMBOL(ep_rxd_payload);
++EXPORT_SYMBOL(ep_rxd_node);
++EXPORT_SYMBOL(ep_rxd_status);
++EXPORT_SYMBOL(ep_rxd_statusblk);
++EXPORT_SYMBOL(ep_txd_node);
++EXPORT_SYMBOL(ep_txd_statusblk);
++
++/* kmap.c, nmh.c - handling mapping of pages into network memory */
++EXPORT_SYMBOL(ep_dvma_reserve);
++EXPORT_SYMBOL(ep_dvma_release);
++EXPORT_SYMBOL(ep_dvma_load);
++EXPORT_SYMBOL(ep_dvma_unload);
++EXPORT_SYMBOL(ep_nmd_subset);
++EXPORT_SYMBOL(ep_nmd_merge);
++
++EXPORT_SYMBOL(ep_system);
++
++/* kcomm.c */
++EXPORT_SYMBOL(ep_nodeid);
++EXPORT_SYMBOL(ep_numnodes);
++EXPORT_SYMBOL(ep_waitfor_nodeid);
++
++/* railhints.c */
++EXPORT_SYMBOL(ep_pickRail);
++EXPORT_SYMBOL(ep_xmtr_bcastrail);
++EXPORT_SYMBOL(ep_xmtr_prefrail);
++EXPORT_SYMBOL(ep_xmtr_availrails);
++EXPORT_SYMBOL(ep_xmtr_noderails);
++EXPORT_SYMBOL(ep_rcvr_prefrail);
++EXPORT_SYMBOL(ep_rcvr_availrails);
++EXPORT_SYMBOL(ep_rxd_railmask);
++
++EXPORT_SYMBOL(ep_svc_indicator_bitmap);
++EXPORT_SYMBOL(ep_svc_indicator_is_set);
++EXPORT_SYMBOL(ep_svc_indicator_clear);
++EXPORT_SYMBOL(ep_svc_indicator_set);
++
++/* cm.c */
++EXPORT_SYMBOL(cm_svc_indicator_clear);
++EXPORT_SYMBOL(cm_svc_indicator_set);
++EXPORT_SYMBOL(cm_svc_indicator_is_set);
++EXPORT_SYMBOL(cm_svc_indicator_bitmap);
++
++#endif
++
++EP_SYS *
++ep_system()
++{
++    return (&epsys);
++}
++
++void
++ep_mod_inc_usecount()
++{
++    MOD_INC_USE_COUNT;
++} 
++
++void
++ep_mod_dec_usecount()
++{
++    MOD_DEC_USE_COUNT;
++}
++
++#if defined(CONFIG_DUMP) || defined(CONFIG_DUMP_MODULE)
++
++#include <linux/dump.h>
++
++static int
++ep_dump_event (struct notifier_block *self, unsigned long event, void *buffer)
++{
++    if (event == DUMP_BEGIN)
++      ep_shutdown (&epsys);
++
++    return (NOTIFY_DONE);
++}
++static struct notifier_block ep_dump_notifier = 
++{
++    notifier_call:    ep_dump_event,
++    priority:         0,
++};
++
++#endif
++
++static int
++ep_reboot_event (struct notifier_block *self, unsigned long event, void *buffer)
++{
++    if ((event == SYS_RESTART || event == SYS_HALT || event == SYS_POWER_OFF))
++      ep_shutdown (&epsys);
++
++    return (NOTIFY_DONE);
++}
++
++static struct notifier_block ep_reboot_notifier = 
++{
++    notifier_call:    ep_reboot_event,
++    priority:         0,
++};
++
++static int
++ep_panic_event (struct notifier_block *self, unsigned long event, void *buffer)
++{
++    ep_shutdown (&epsys);
++
++    return (NOTIFY_DONE);
++}
++
++static struct notifier_block ep_panic_notifier = 
++{
++    notifier_call:    ep_panic_event,
++    priority:         0,
++};
++
++/*
++ * Module configuration. 
++ */
++#ifdef MODULE
++static int __init ep_init(void)
++#else
++__initfunc(int ep_init(void))
++#endif
++{
++    register int rmask = 0;
++
++    ep_procfs_init ();
++
++    ep_sys_init (&epsys);
++
++#if defined(CONFIG_ELAN4) || defined(CONFIG_ELAN4_MODULE)
++    rmask = ep4_create_rails (&epsys, disabled);
++#endif
++    
++    /* If we've brought up an elan4 rail, then disable all elan3 rails. */
++    if ((rmask & ~disabled) != 0)
++      disabled = ~rmask;
++
++#if defined(CONFIG_ELAN3) || defined(CONFIG_ELAN3_MODULE)
++    rmask = ep3_create_rails (&epsys, disabled);
++#endif
++
++#if defined(CONFIG_DUMP) || defined(CONFIG_DUMP_MODULE)
++    register_dump_notifier (&ep_dump_notifier);
++#endif
++    register_reboot_notifier (&ep_reboot_notifier);
++
++#if !defined(NO_PANIC_NOTIFIER)
++    notifier_chain_register (&panic_notifier_list, &ep_panic_notifier);
++#endif
++
++    return (0);
++}
++
++/*
++ * Module removal.
++ */
++#ifdef MODULE
++static void
++__exit ep_exit(void)
++{
++    register int i;
++
++#if defined(CONFIG_DUMP) || defined(CONFIG_DUMP_MODULE)
++    unregister_dump_notifier (&ep_dump_notifier);
++#endif
++    unregister_reboot_notifier (&ep_reboot_notifier);
++
++#if !defined(NO_PANIC_NOTIFIER)
++    notifier_chain_unregister (&panic_notifier_list, &ep_panic_notifier);
++#endif
++
++    for (i = 0; i < EP_MAX_RAILS; i++)
++    {
++      if (epsys.Rails[i])
++      {
++          switch (epsys.Rails[i]->State)
++          {
++          case EP_RAIL_STATE_UNINITIALISED:
++              break;
++
++          case EP_RAIL_STATE_STARTED:
++          case EP_RAIL_STATE_RUNNING:
++          case EP_RAIL_STATE_INCOMPATIBLE:
++              /* remove per-rail CM proc entries */
++              ep_stop_rail (epsys.Rails[i]);
++              break;
++          }
++
++          /* remove EP proc rail entries after per-rail CM entries */
++          ep_procfs_rail_fini (epsys.Rails[i]);
++          ep_destroy_rail (epsys.Rails[i]);
++      }
++    }
++
++    ep_sys_fini (&epsys);
++
++    ep_procfs_fini ();
++}
++
++/* Declare the module init and exit functions */
++module_init(ep_init);
++module_exit(ep_exit);
++
++#endif
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/ep/conf_linux.h
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/conf_linux.h        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/conf_linux.h     2005-06-01 23:12:54.635433632 -0400
+@@ -0,0 +1,29 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: conf_linux.h,v 1.6 2003/10/02 14:16:07 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/conf_linux.h,v $*/
++
++#ifndef __ELAN_CONF_LINUX_H
++#define __ELAN_CONF_LINUX_H
++
++extern void ep_procfs_init(void);
++extern void ep_procfs_fini(void);
++extern void ep_procfs_rail_init(EP_RAIL *rail);
++extern void ep_procfs_rail_fini(EP_RAIL *rail);
++
++extern void ep_procfs_svc_indicator_create(int svc_indicator, char *name);
++extern void ep_procfs_svc_indicator_remove(int svc_indicator, char *name);
++
++#endif /* __ELAN_CONF_LINUX_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/ep/debug.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/debug.c     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/debug.c  2005-06-01 23:12:54.635433632 -0400
+@@ -0,0 +1,145 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: debug.c,v 1.28.2.1 2004/11/12 10:54:50 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/debug.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++
++#include "debug.h"
++
++DisplayInfo di_ep_debug = {ep_debugf, DBG_DEBUG};
++
++/*
++ * Generate a partial bitmap string, for the bitmap from offset "off" for "count" bits,
++ * to allow for displaying of subsets, treat entry 0 of the bitmap as having value "base".
++ */
++int
++ep_sprintf_bitmap (char *str, unsigned nbytes, bitmap_t *bitmap, int base, int off, int nbits)
++{
++    char entry[12];                                           /* space for N-N */
++    register int i, j, len;
++    register int notstart = off;
++    register int notfirst = 0;
++    char *p = str;
++    
++    for (i = off; i < nbits; i++)
++    {
++      if (BT_TEST (bitmap, i))
++      {
++          for (j = i+1; j < nbits; j++)
++              if (! BT_TEST (bitmap, j))
++                  break;
++
++          if (j == (i+1))
++              len = (int)sprintf (entry, "%d", base + i);
++          else
++              len = (int)sprintf (entry, "%d-%d", base + i, base + j-1);
++          
++          /* NOTE the 2 is for: one for comma, one for (possible) closing bracket */
++          if ((p - str) <= (nbytes - (len+3)))
++              p += (int)sprintf (p, "%c%s", notfirst++ ? ',' : notstart ? ' ' : '[', entry);
++          else
++          {
++              /* no more space on this line, so move onto next */
++              sprintf (p, "%c", notfirst++ ? ',' : '[');
++
++              return (i);
++          }
++
++          i = j;
++      }
++    }
++    
++    if (!notfirst)
++      sprintf (str, "<empty>");
++    else
++      strcpy (p, "]");
++
++    return (-1);
++}
++
++void
++ep_display_bitmap (char *prefix, char *tag, bitmap_t *bitmap, unsigned base, unsigned nbits)
++{
++    /* Tru64 kernel printf() truncates lines at 128 bytes - the man pages for printf (9)
++     * do not mention this restriction, nor that it does not terminate the line with a
++     * carriage return, this  is pretty naff. 
++     * Linux has a similar limit though is much more generous at 1024 - and you can just 
++     * look at the code to see why this has been done.
++     *
++     * Our nodeset information could well be longer than 128 characters,  so we're going to 
++     * have to split it into a number of lines. */
++
++#define LINEBUF_SIZE          128
++    char *p, linebuf[LINEBUF_SIZE+1];                         /* +1 for null termination */
++    int i, noff, off = 0;
++
++    do {
++      if (off == 0)
++          p = linebuf + (int)sprintf (linebuf, "%s: %s ", prefix, tag);
++      else
++      {
++          p = linebuf + (int)sprintf (linebuf, "%s:  ", prefix);
++          for (i = 0; tag[i] != '\0'; i++)
++              *p++ = ' ';
++      }
++
++      noff = ep_sprintf_bitmap (p, &linebuf[LINEBUF_SIZE-1]-p, bitmap, base, off, nbits);
++
++      printk ("%s\n", linebuf);
++
++    } while ((off = noff) != -1);
++
++#undef LINEBUF_SIZE
++}
++
++void
++ep_debugf (long mode, char *fmt, ...)
++{
++   va_list ap;
++   char prefix[32];
++   
++   va_start (ap, fmt);
++#if defined(LINUX)
++   sprintf (prefix, "[%08d.%04d] ", (int) lbolt, current->pid);
++#else
++   sprintf (prefix, "[%08d.----] ", (int) lbolt);
++#endif
++   qsnet_vdebugf ((mode & epdebug_console ? QSNET_DEBUG_CONSOLE: 0) | QSNET_DEBUG_BUFFER, prefix, fmt, ap);
++   va_end (ap);
++}
++
++int
++ep_assfail (EP_RAIL *rail, const char *ex, const char *func, const char *file, const int line)
++{
++    qsnet_debugf (QSNET_DEBUG_BUFFER, "ep: assertion failure: %s, function: %s, file %s, line: %d\n", ex, func, file, line);
++    
++    printk (KERN_EMERG "ep: assertion failure: %s, function: %s, file %s, line: %d\n", ex, func, file, line);
++    
++    if (panicstr)
++      return (0);
++    
++    if (assfail_mode & 1)                             /* return to BUG() */
++      return 1;
++    
++    if (assfail_mode & 2)
++      panic ("ep: assertion failure: %s, function: %s, file %s, line: %d\n", ex, func, file, line);
++    if (assfail_mode & 4)
++      epdebug = 0;
++    
++    return 0;
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/ep/debug_elan4.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/debug_elan4.c       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/debug_elan4.c    2005-06-01 23:12:54.636433480 -0400
+@@ -0,0 +1,59 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: debug_elan4.c,v 1.1 2004/05/19 10:21:04 david Exp $ $Name: QSNETMODULES-4-30_20050128 $"
++/*      $Source: /cvs/master/quadrics/epmod/debug_elan4.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++
++#include "kcomm_vp.h"
++#include "kcomm_elan4.h"
++#include "conf_linux.h"
++#include "debug.h"
++
++static void
++ep4_display_ecqs (EP4_RAIL *rail)
++{
++    struct list_head *el;
++    unsigned long flags;
++    int i;
++
++    spin_lock_irqsave (&rail->r_ecq_lock, flags);
++    for (i = 0; i <EP4_NUM_ECQ; i++)
++    {
++      list_for_each (el, &rail->r_ecq_list[i]) {
++          EP4_ECQ *ecq = list_entry (el, EP4_ECQ, ecq_link);
++
++          ep_debugf (DBG_DEBUG, "ECQ: type %d: avail %d cqnum %d\n", i, ecq->ecq_avail, elan4_cq2num (ecq->ecq_cq));
++      }
++    }
++    spin_unlock_irqrestore (&rail->r_ecq_lock, flags);
++}
++
++void
++ep4_debug_rail (EP_RAIL *r)
++{
++    EP4_RAIL *rail = (EP4_RAIL *) r;
++    EP_SYS   *sys  = rail->r_generic.System;
++
++    ep_debugf (DBG_DEBUG, "ep%d: is elan4 %d rev %c\n", rail->r_generic.Number,
++             rail->r_generic.Devinfo.dev_instance, 'a' + rail->r_generic.Devinfo.dev_revision_id);
++
++    ep4_display_ecqs (rail);
++
++    ep_display_alloc (&sys->Allocator);
++    ep_display_rmap (sys->Allocator.ResourceMap);
++
++    ep_display_alloc (&rail->r_generic.ElanAllocator);
++    ep_display_alloc (&rail->r_generic.MainAllocator);
++
++    ep_display_rmap (rail->r_generic.ElanAllocator.ResourceMap);
++}
++
+Index: linux-2.4.21/drivers/net/qsnet/ep/debug.h
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/debug.h     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/debug.h  2005-06-01 23:12:54.636433480 -0400
+@@ -0,0 +1,109 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef _ELAN3_EPDEBUG_H
++#define _ELAN3_EPDEBUG_H
++
++#ident "$Id: debug.h,v 1.18.2.1 2004/11/12 10:54:50 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/debug.h,v $ */
++
++extern unsigned int epdebug;
++extern unsigned int epdebug_console;
++extern unsigned int epdebug_cmlevel;
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++extern unsigned int epdebug_check_sum;
++#endif
++#define DBG_CONFIG            0x00000001                      /* Module configuration */
++#define DBG_PROBE             0x00000002
++#define DBG_ROUTETABLE                0x00000004
++#define DBG_STATEMAP          0x00000008
++
++#define DBG_CM                        0x00000020
++#define DBG_XMTR              0x00000040
++#define DBG_RCVR              0x00000080
++#define DBG_FORWARD           0x00000100
++#define DBG_DISCON            0x00000200
++#define DBG_EPTRAP            0x00000400
++#define DBG_COMMAND           0x00000800
++#define DBG_RETRY             0x00001000
++#define DBG_DEBUG             0x00002000
++#define DBG_NETWORK_ERROR     0x00004000
++#define DBG_MSGSYS            0x00008000
++#define DBG_MANAGER           0x00010000
++#define DBG_KMAP              0x00020000
++#define DBG_FAILOVER          0x00040000
++#define DBG_MAPNMD            0x00080000
++#define DBG_KMSG              0x00100000
++#define DBG_SVC                 0x00200000
++#define DBG_STABILISE         0x00400000
++
++#if defined(DEBUG_PRINTF)
++
++#  define EPRINTF0(m,fmt)                     ((epdebug&(m)) ? ep_debugf(m,fmt)                     : (void)0)
++#  define EPRINTF1(m,fmt,a)                   ((epdebug&(m)) ? ep_debugf(m,fmt,a)                   : (void)0)
++#  define EPRINTF2(m,fmt,a,b)                 ((epdebug&(m)) ? ep_debugf(m,fmt,a,b)                 : (void)0)
++#  define EPRINTF3(m,fmt,a,b,c)                       ((epdebug&(m)) ? ep_debugf(m,fmt,a,b,c)               : (void)0)
++#  define EPRINTF4(m,fmt,a,b,c,d)             ((epdebug&(m)) ? ep_debugf(m,fmt,a,b,c,d)             : (void)0)
++#  define EPRINTF5(m,fmt,a,b,c,d,e)           ((epdebug&(m)) ? ep_debugf(m,fmt,a,b,c,d,e)           : (void)0)
++#  define EPRINTF6(m,fmt,a,b,c,d,e,f)         ((epdebug&(m)) ? ep_debugf(m,fmt,a,b,c,d,e,f)         : (void)0)
++#  define EPRINTF7(m,fmt,a,b,c,d,e,f,g)               ((epdebug&(m)) ? ep_debugf(m,fmt,a,b,c,d,e,f,g)       : (void)0)
++#  define EPRINTF8(m,fmt,a,b,c,d,e,f,g,h)     ((epdebug&(m)) ? ep_debugf(m,fmt,a,b,c,d,e,f,g,h)     : (void)0)
++#  define EPRINTF9(m,fmt,a,b,c,d,e,f,g,h,i)   ((epdebug&(m)) ? ep_debugf(m,fmt,a,b,c,d,e,f,g,h,i)   : (void)0)
++#  define EPRINTF10(m,fmt,a,b,c,d,e,f,g,h,i,j)        ((epdebug&(m)) ? ep_debugf(m,fmt,a,b,c,d,e,f,g,h,i,j) : (void)0)
++
++#  define CPRINTF0(lvl,fmt)                   (((lvl) <= epdebug_cmlevel) ? EPRINTF0(DBG_CM,fmt)                   : (void)0)
++#  define CPRINTF1(lvl,fmt,a)                 (((lvl) <= epdebug_cmlevel) ? EPRINTF1(DBG_CM,fmt,a)                 : (void)0)
++#  define CPRINTF2(lvl,fmt,a,b)                       (((lvl) <= epdebug_cmlevel) ? EPRINTF2(DBG_CM,fmt,a,b)               : (void)0)
++#  define CPRINTF3(lvl,fmt,a,b,c)             (((lvl) <= epdebug_cmlevel) ? EPRINTF3(DBG_CM,fmt,a,b,c)             : (void)0)
++#  define CPRINTF4(lvl,fmt,a,b,c,d)           (((lvl) <= epdebug_cmlevel) ? EPRINTF4(DBG_CM,fmt,a,b,c,d)           : (void)0)
++#  define CPRINTF5(lvl,fmt,a,b,c,d,e)         (((lvl) <= epdebug_cmlevel) ? EPRINTF5(DBG_CM,fmt,a,b,c,d,e)         : (void)0)
++#  define CPRINTF6(lvl,fmt,a,b,c,d,e,f)               (((lvl) <= epdebug_cmlevel) ? EPRINTF6(DBG_CM,fmt,a,b,c,d,e,f)       : (void)0)
++#  define CPRINTF7(lvl,fmt,a,b,c,d,e,f,g)     (((lvl) <= epdebug_cmlevel) ? EPRINTF7(DBG_CM,fmt,a,b,c,d,e,f,g)     : (void)0)
++#  define CPRINTF8(lvl,fmt,a,b,c,d,e,f,g,h)   (((lvl) <= epdebug_cmlevel) ? EPRINTF8(DBG_CM,fmt,a,b,c,d,e,f,g,h)   : (void)0)
++#  define CPRINTF9(lvl,fmt,a,b,c,d,e,f,g,h,i) (((lvl) <= epdebug_cmlevel) ? EPRINTF9(DBG_CM,fmt,a,b,c,d,e,f,g,h,i) : (void)0)
++
++#if defined __GNUC__
++extern void ep_debugf (long mode, char *fmt, ...) __attribute__ ((format (printf,2,3)));
++#else
++extern void ep_debugf (long mode, char *fmt, ...);
++#endif
++
++#else
++
++#  define EPRINTF0(m,fmt)                     (0)
++#  define EPRINTF1(m,fmt,a)                   (0)
++#  define EPRINTF2(m,fmt,a,b)                 (0)
++#  define EPRINTF3(m,fmt,a,b,c)                       (0)
++#  define EPRINTF4(m,fmt,a,b,c,d)             (0)
++#  define EPRINTF5(m,fmt,a,b,c,d,e)           (0)
++#  define EPRINTF6(m,fmt,a,b,c,d,e,f)         (0)
++#  define EPRINTF7(m,fmt,a,b,c,d,e,f,g)               (0)
++#  define EPRINTF8(m,fmt,a,b,c,d,e,f,g,h)     (0)
++#  define EPRINTF9(m,fmt,a,b,c,d,e,f,g,h,i)   (0)
++#  define EPRINTF9(m,fmt,a,b,c,d,e,f,g,h,i,j) (0)
++
++#  define CPRINTF0(lvl,fmt)                   (0)
++#  define CPRINTF1(lvl,fmt,a)                 (0)
++#  define CPRINTF2(lvl,fmt,a,b)                       (0)
++#  define CPRINTF3(lvl,fmt,a,b,c)             (0)
++#  define CPRINTF4(lvl,fmt,a,b,c,d)           (0)
++#  define CPRINTF5(lvl,fmt,a,b,c,d,e)         (0)
++#  define CPRINTF6(lvl,fmt,a,b,c,d,e,f)               (0)
++#  define CPRINTF7(lvl,fmt,a,b,c,d,e,f,g)     (0)
++#  define CPRINTF8(lvl,fmt,a,b,c,d,e,f,g,h)   (0)
++#  define CPRINTF9(lvl,fmt,a,b,c,d,e,f,g,h,i) (0)
++
++#endif /* DEBUG */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++#endif /* _ELAN3_EPDEBUG_H */
++
+Index: linux-2.4.21/drivers/net/qsnet/ep/epcomms_asm_elan4_thread.S
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/epcomms_asm_elan4_thread.S  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/epcomms_asm_elan4_thread.S       2005-06-01 23:12:54.637433328 -0400
+@@ -0,0 +1,133 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: epcomms_asm_elan4_thread.S,v 1.5 2004/04/25 11:25:43 david Exp $ $Name: QSNETMODULES-4-30_20050128 $"
++/*      $Source: /cvs/master/quadrics/epmod/epcomms_asm_elan4_thread.S,v $*/
++
++#include <elan4/events.h>
++#include <elan4/commands.h>
++
++#include "assym_elan4.h"
++
++/* XXXXX - registers.h */
++#define E4_MAIN_INT_SHIFT             14
++
++/*
++ * c_waitevent_interrupt (E4_uint64 *commandport, E4_Event *event, E4_uint64 count, E4_uint64 intcookie)
++ */
++      .global c_waitevent_interrupt
++c_waitevent_interrupt:
++      add             %sp, -192, %sp
++      st64            %r16, [%sp + 64]                // preserve call preserved registers
++      st64            %r24, [%sp + 128]               // - see CALL_USED_REGISTERS.
++      mov             %r16,%r16                       // BUG FIX: E4 RevA
++      mov             %r24,%r24                       // BUG FIX: E4 RevA
++      nop                                             // BUG FIX: E4 RevA
++      nop                                             // BUG FIX: E4 RevA
++
++      mov             %r7, %r18                       // (%r2) return pc
++1:    call            2f
++       mov            %sp, %r17                       // (%r1) SP
++2:    add             %r7, (3f-1b), %r16              // (%r0) PC
++      st32            %r16, [%sp]                     // event source block
++      mov             MAKE_EXT_CLEAN_CMD, %r23
++      st8             %r23, [%sp+56]                  // event source block
++      mov             %r16,%r16                       // BUG FIX: E4 RevA
++      mov             %r23,%r23                       // BUG FIX: E4 RevA
++      nop                                             // BUG FIX: E4 RevA
++      nop                                             // BUG FIX: E4 RevA
++      
++      or              %r9, WAIT_EVENT_CMD, %r16                                               ! WAIT_EVENT_CMD | event
++      sll8            %r10, 32, %r17
++      or              %r17, E4_EVENT_TYPE_VALUE(E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, 8), %r17  !   ev_CountAndType
++      mov             %sp, %r18                                                               !   ev_Source
++      mov             %r8, %r19                                                               !   ev_Dest
++      sll8            %r11, E4_MAIN_INT_SHIFT, %r20
++      or              %r20, INTERRUPT_CMD, %r20                                               ! INTERRUPT_CMD | (cookie << E4_MAIN_INT_SHIFT)
++      mov             NOP_CMD, %r21
++      mov             NOP_CMD, %r22
++      mov             NOP_CMD, %r23
++
++      st64suspend     %r16, [%r8]
++      
++3:    ld64            [%sp + 64], %r16                // restore call preserved register
++      ld64            [%sp + 128], %r24
++      jmpl            %r2+8, %r0                      // and return
++       add            %sp, 192, %sp
++
++
++#define EP4_RCVR_PENDING_STALLED              1               /* indicates thread has stalled for no descriptor (rcvr_pending_head) */
++
++#define RXD_DEBUG(VAL,RXD,TMP) \
++      mov     VAL, TMP; \
++      st8     TMP, [RXD + EP4_RXD_DEBUG]
++
++      
++      /*
++       * %r2  - rcvr elan
++       * %r3  - rxd elan
++       */
++      .global c_queue_rxd
++c_queue_rxd:
++      RXD_DEBUG(1, %r3, %r23)
++      
++      ld16    [%r2 + EP4_RCVR_PENDING_TAILP], %r18    /* r18 == tailp, r19 = head */
++      add     %r3, EP4_RXD_NEXT, %r4
++      
++      st8     %r0, [%r3 + EP4_RXD_NEXT]               /* rxd->rxd_next = NULL */
++      st8     %r4, [%r2 + EP4_RCVR_PENDING_TAILP]     /* tailp = &rxd->rxd_next */
++      st8     %r3, [%r18]                             /* *tailp = rxd */
++
++      cmp     %r19, EP4_RCVR_PENDING_STALLED          /* thread stalled ? */
++      beq     1f
++       mov    %r18, %r16                              /* must have used %r16, %r19, %r23 */
++      mov     %r3, %r23
++
++      RXD_DEBUG(2, %r3, %r23)
++      
++      st8suspend %r16, [%r3 + EP4_RXD_QUEUED]         /* no - mark as queued - all done */
++
++1:    st8     %r16, [%r3 + EP4_RXD_QUEUED]            /* mark as queued */
++
++      RXD_DEBUG(3, %r3, %r23)
++
++      mov     %r3, %r8                                /* return rxd from c_stall_thread */
++      ba      .epcomms_resume_thread                  /* resume the thread */
++       ld64   [%r2 + EP4_RCVR_THREAD_STALL], %r0
++
++      /*
++       *  c_stall_thread (EP4_RCVR_ELAN *rcvrElan)
++       */
++      .global c_stall_thread
++c_stall_thread:
++      add             %sp, -192, %sp
++      st64            %r16, [%sp + 64]                // preserve call preserved registers
++      st64            %r24, [%sp + 128]               // - see CALL_USED_REGISTERS.
++      mov             %r16,%r16                       // BUG FIX: E4 RevA
++      mov             %r24,%r24                       // BUG FIX: E4 RevA
++      nop                                             // BUG FIX: E4 RevA
++      nop                                             // BUG FIX: E4 RevA
++
++      mov             EP4_RCVR_PENDING_STALLED, %r9   // Mark rcvr as stalled
++      st8             %r9, [%r8 + EP4_RCVR_PENDING_HEAD]
++
++      // XXXX _ TBD should generate interrupt
++
++      mov             %r1, %r17                       // SP 
++      mov             %r7, %r23                       // return pc
++
++      st64suspend     %r16, [%r8 + EP4_RCVR_THREAD_STALL]
++      
++.epcomms_resume_thread:
++      /* %r8 == rxdElan */
++      
++      ld64            [%sp + 64], %r16                // restore call preserved register
++      ld64            [%sp + 128], %r24
++      jmpl            %r7+8, %r0                      // and return
++       add            %sp, 192, %sp
++
+Index: linux-2.4.21/drivers/net/qsnet/ep/epcomms.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/epcomms.c   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/epcomms.c        2005-06-01 23:12:54.637433328 -0400
+@@ -0,0 +1,484 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: epcomms.c,v 1.71.2.6 2004/11/30 12:02:16 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/epcomms.c,v $ */
++
++#include <qsnet/kernel.h>
++#include <qsnet/kthread.h>
++#include <qsnet/autoconf.h>
++
++#include <elan/kcomm.h>
++#include <elan/epsvc.h>
++#include <elan/epcomms.h>
++#include "cm.h"
++#include "debug.h"
++
++static void
++ep_comms_thread (void *arg)
++{
++    EP_COMMS_SUBSYS  *subsys = (EP_COMMS_SUBSYS *) arg;
++    struct list_head *el;
++
++    kernel_thread_init ("ep_comms");
++
++    /* since ep_alloc_xmtr() has incremented the module use count,
++     * we would be preventing the module from being unloaded, so
++     * we decrement the use count since this thread must terminate
++     * during unload of the module.
++     */
++    ep_mod_dec_usecount();
++
++    for (;;)
++    {
++      long nextRunTime = 0;
++
++      /* NOTE - subsys->Lock serializes us against flush/relocations
++       *        caused by rail nodeset transitions.
++       */
++      kmutex_lock (&subsys->Lock);
++      list_for_each (el, &subsys->Transmitters) {
++          nextRunTime = ep_check_xmtr (list_entry (el, EP_XMTR, Link), nextRunTime);
++      }
++
++      list_for_each (el, &subsys->Receivers) {
++          nextRunTime = ep_check_rcvr (list_entry (el, EP_RCVR, Link), nextRunTime);
++      }
++      kmutex_unlock (&subsys->Lock);
++
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++      ep_csum_rxds (subsys);  
++#endif
++      nextRunTime = ep_forward_rxds (subsys, nextRunTime);
++
++      if (ep_kthread_sleep (&subsys->Thread, nextRunTime) < 0)
++          break;
++    }
++
++    ep_mod_inc_usecount();
++
++    ep_kthread_stopped (&subsys->Thread);
++    kernel_thread_exit();
++}
++
++int
++ep_comms_add_rail (EP_SUBSYS *s, EP_SYS *sys, EP_RAIL *rail)
++{
++    EP_COMMS_SUBSYS  *subsys = (EP_COMMS_SUBSYS *) s;
++    EP_COMMS_RAIL    *commsRail;
++    struct list_head *el;
++
++    printk ("%s: vendorid=%x deviceid=%x\n", rail->Name, rail->Devinfo.dev_vendor_id, rail->Devinfo.dev_device_id);
++
++    switch (rail->Devinfo.dev_device_id)
++    {
++#if defined(CONFIG_ELAN3) || defined(CONFIG_ELAN3_MODULE)
++    case PCI_DEVICE_ID_ELAN3:
++      commsRail = ep3comms_add_rail (s, sys, rail);
++      break;
++#endif
++#if defined(CONFIG_ELAN4) || defined(CONFIG_ELAN4_MODULE)
++    case PCI_DEVICE_ID_ELAN4:
++      commsRail = ep4comms_add_rail (s, sys, rail);
++      break;
++#endif
++    default:
++      return 0;
++    }
++
++    if (commsRail == NULL)
++      return 1;
++
++    commsRail->Rail   = rail;
++    commsRail->Subsys = subsys;
++
++    kmutex_lock (&subsys->Lock);
++    list_add_tail (&commsRail->Link, &subsys->Rails);
++    
++    list_for_each (el, &subsys->Receivers) {
++      EP_RCVR *rcvr = list_entry (el, EP_RCVR, Link);
++
++      EP_RAIL_OP (commsRail, Rcvr.AddRail) (rcvr, commsRail);
++    }
++      
++    list_for_each (el, &subsys->Transmitters) {
++      EP_XMTR *xmtr = list_entry (el, EP_XMTR, Link);
++
++      EP_RAIL_OP (commsRail, Xmtr.AddRail) (xmtr, commsRail);
++    }
++
++    kmutex_unlock (&subsys->Lock);
++
++    return 0;
++}
++
++void
++ep_comms_del_rail (EP_SUBSYS *s, EP_SYS *sys, EP_RAIL *rail)
++{
++    EP_COMMS_SUBSYS  *subsys    = (EP_COMMS_SUBSYS *) s;
++    EP_COMMS_RAIL    *commsRail = NULL;
++    struct list_head *el;
++
++    kmutex_lock (&subsys->Lock);
++    /* find out rail entry and remove from system list */
++    list_for_each (el, &subsys->Rails) {
++      if ((commsRail = list_entry (el, EP_COMMS_RAIL, Link))->Rail == rail)
++          break;
++    }
++
++    list_del (&commsRail->Link);
++    
++    list_for_each (el, &subsys->Receivers) {
++      EP_RCVR *rcvr = list_entry (el, EP_RCVR, Link);
++
++      EP_RAIL_OP(commsRail, Rcvr.DelRail) (rcvr, commsRail);
++    }
++      
++    list_for_each (el, &subsys->Transmitters) {
++      EP_XMTR *xmtr = list_entry (el, EP_XMTR, Link);
++
++      EP_RAIL_OP(commsRail,Xmtr.DelRail) (xmtr, commsRail);
++    }
++
++    kmutex_unlock (&subsys->Lock);
++
++    EP_RAIL_OP (commsRail, DelRail) (commsRail);
++}
++
++void
++ep_comms_fini (EP_SUBSYS *s, EP_SYS *sys)
++{
++    EP_COMMS_SUBSYS *subsys = (EP_COMMS_SUBSYS *) s;
++
++    ep_kthread_stop (&subsys->Thread);
++    ep_kthread_destroy (&subsys->Thread);
++
++    if (subsys->ForwardXmtr)
++      ep_free_xmtr (subsys->ForwardXmtr);
++
++    spin_lock_destroy (&subsys->ForwardDescLock);
++
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++    spin_lock_destroy (&subsys->CheckSumDescLock);
++#endif
++
++    kmutex_destroy (&subsys->Lock);
++
++    KMEM_FREE (subsys, sizeof (EP_COMMS_SUBSYS));
++}
++
++int
++ep_comms_init (EP_SYS *sys)
++{
++    EP_COMMS_SUBSYS *subsys;
++
++    KMEM_ZALLOC (subsys, EP_COMMS_SUBSYS *, sizeof (EP_COMMS_SUBSYS), 1);
++
++    if (subsys == NULL)
++      return (ENOMEM);
++
++    INIT_LIST_HEAD (&subsys->Rails);
++    INIT_LIST_HEAD (&subsys->Receivers);
++    INIT_LIST_HEAD (&subsys->Transmitters);
++    INIT_LIST_HEAD (&subsys->ForwardDescList);
++
++    kmutex_init (&subsys->Lock);
++    spin_lock_init (&subsys->ForwardDescLock);
++
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++    INIT_LIST_HEAD (&subsys->CheckSumDescList);
++    spin_lock_init (&subsys->CheckSumDescLock);
++#endif
++
++    subsys->Subsys.Sys        = sys;
++    subsys->Subsys.Name             = "epcomms";
++    subsys->Subsys.Destroy    = ep_comms_fini;
++    subsys->Subsys.AddRail    = ep_comms_add_rail;
++    subsys->Subsys.RemoveRail = ep_comms_del_rail;
++
++    ep_subsys_add (sys, &subsys->Subsys);
++    ep_kthread_init (&subsys->Thread);
++
++    if ((subsys->ForwardXmtr = ep_alloc_xmtr (subsys->Subsys.Sys)) == NULL)
++      goto failed;
++
++    if (kernel_thread_create (ep_comms_thread, subsys) == NULL)
++      goto failed;
++    ep_kthread_started (&subsys->Thread);
++
++    return (0);
++
++ failed:
++    ep_subsys_del (sys, &subsys->Subsys);
++    ep_comms_fini (&subsys->Subsys, sys);
++
++    return (ENOMEM);
++}
++
++void
++ep_comms_display (EP_SYS *sys, char *how)
++{
++    EP_COMMS_SUBSYS  *subsys = (EP_COMMS_SUBSYS *) ep_subsys_find (sys, EPCOMMS_SUBSYS_NAME);
++    struct list_head *el;
++
++    if (how == NULL || !strncmp (how, "rail", 4))
++    {
++      kmutex_lock (&subsys->Lock);
++      list_for_each (el, &subsys->Rails) {
++          EP_COMMS_RAIL *commsRail = list_entry (el, EP_COMMS_RAIL, Link);
++
++          EP_RAIL_OP(commsRail, DisplayRail) (commsRail);
++      }
++      kmutex_unlock (&subsys->Lock);
++    }
++          
++    if (how == NULL || !strncmp (how, "xmtr", 4))
++      list_for_each (el, &subsys->Transmitters)
++          ep_display_xmtr (&di_ep_debug, list_entry (el, EP_XMTR, Link));
++
++    if (how == NULL || !strncmp (how, "rcvr", 4)) 
++      list_for_each (el, &subsys->Receivers)
++          ep_display_rcvr (&di_ep_debug, list_entry (el, EP_RCVR, Link), (how && how[4] == ',') ? 1 : 0);
++}
++
++int
++ep_svc_indicator_set (EP_SYS *epsys, int svc_indicator) 
++{
++    EP_COMMS_SUBSYS  *subsys;
++    struct list_head *el;
++
++    EPRINTF1 (DBG_SVC,"ep_svc_indicator_set: %d \n",svc_indicator);
++
++    if (svc_indicator < 0 || svc_indicator > EP_SVC_NUM_INDICATORS)
++      return (EP_EINVAL);
++
++    if ((subsys = (EP_COMMS_SUBSYS *) ep_subsys_find (epsys, "epcomms")) == NULL) {
++      EPRINTF0 (DBG_SVC,"ep_svc_indicator_set: ep_subsys_find failed\n");
++      return (EP_EINVAL);
++    }
++
++
++    kmutex_lock (&subsys->Lock); /* walking rails list and setting info on Rail */
++    list_for_each (el, &subsys->Rails) { 
++      EP_COMMS_RAIL *commsRail = list_entry (el, EP_COMMS_RAIL, Link);
++
++      cm_svc_indicator_set(commsRail->Rail, svc_indicator);
++    }
++    kmutex_unlock (&subsys->Lock);
++
++    EPRINTF1 (DBG_SVC,"ep_svc_indicator_set: %d success\n",svc_indicator);
++    return (EP_SUCCESS);
++}
++
++int
++ep_svc_indicator_clear (EP_SYS *epsys, int svc_indicator) 
++{
++    EP_COMMS_SUBSYS  *subsys;
++    struct list_head *el;
++
++    EPRINTF1 (DBG_SVC,"ep_svc_indicator_clear: %d \n",svc_indicator);
++
++    if (svc_indicator < 0 || svc_indicator >= EP_SVC_NUM_INDICATORS)
++      return (EP_EINVAL);
++
++    if ((subsys = (EP_COMMS_SUBSYS *) ep_subsys_find (epsys, "epcomms")) == NULL) {
++      EPRINTF0 (DBG_SVC,"ep_svc_indicator_clear: ep_subsys_find failed\n");
++      return (EP_EINVAL);
++    }
++
++    kmutex_lock (&subsys->Lock); /* walking rails list and setting info on Rail */
++    list_for_each (el, &subsys->Rails) { 
++      EP_COMMS_RAIL *commsRail = list_entry (el, EP_COMMS_RAIL, Link);
++
++      cm_svc_indicator_clear(commsRail->Rail, svc_indicator);
++    }
++    kmutex_unlock (&subsys->Lock);
++
++    EPRINTF1 (DBG_SVC,"ep_svc_indicator_clear: %d success\n",svc_indicator);
++    return (EP_SUCCESS);
++}
++
++int 
++ep_svc_indicator_is_set (EP_SYS *epsys, int svc_indicator, int nodeId) 
++{
++    EP_COMMS_SUBSYS  *subsys;
++    struct list_head *el;
++    int               set = 0;
++
++    EPRINTF2 (DBG_SVC,"ep_svc_indicator_is_set: svc %d node %d \n", svc_indicator, nodeId);
++
++    if ((subsys = (EP_COMMS_SUBSYS *) ep_subsys_find (epsys, "epcomms")) == NULL) {
++      EPRINTF0 (DBG_SVC,"ep_svc_indicator_is_set: ep_subsys_find failed\n");
++      return (0);
++    }
++
++    kmutex_lock (&subsys->Lock); /* walking rails list and setting info on Rail */
++    list_for_each (el, &subsys->Rails) { 
++      EP_COMMS_RAIL *commsRail = list_entry (el, EP_COMMS_RAIL, Link);
++
++      set |= cm_svc_indicator_is_set(commsRail->Rail, svc_indicator, nodeId);
++    }
++    kmutex_unlock (&subsys->Lock);
++
++    EPRINTF3 (DBG_SVC,"ep_svc_indicator_is_set: svc %d node %d returning %d\n", svc_indicator, nodeId, set);
++    return set;
++}
++
++int
++ep_svc_indicator_bitmap (EP_SYS *epsys, int svc_indicator, bitmap_t * bitmap, int low, int nnodes) 
++{
++    EP_COMMS_SUBSYS  *subsys;
++    struct list_head *el;
++
++    EPRINTF1 (DBG_SVC,"ep_svc_indicator_bitmap: svc %d\n", svc_indicator);
++
++    if (svc_indicator < 0 || svc_indicator >= EP_SVC_NUM_INDICATORS)
++      return (-1);
++
++    if ((subsys = (EP_COMMS_SUBSYS *) ep_subsys_find (epsys, "epcomms")) == NULL) {
++      EPRINTF0 (DBG_SVC,"ep_svc_indicator_bitmap: ep_subsys_find failed\n");
++      return (-2);
++    }
++
++    /* clear bitmap */
++    bt_zero (bitmap, nnodes);
++
++    kmutex_lock (&subsys->Lock); /* walking rails list and setting info on Rail */
++    list_for_each (el, &subsys->Rails) { 
++      EP_COMMS_RAIL *commsRail = list_entry (el, EP_COMMS_RAIL, Link);
++
++      /* this will or in each bit map */
++      cm_svc_indicator_bitmap (commsRail->Rail, svc_indicator, bitmap, low, nnodes);
++    }
++    kmutex_unlock (&subsys->Lock);
++
++    return (0);
++}
++
++int
++ep_xmtr_svc_indicator_bitmap (EP_XMTR *xmtr, int svc_indicator, bitmap_t * bitmap, int low, int nnodes) 
++{
++    int i;
++
++    EPRINTF1 (DBG_SVC,"ep_xmtr_svc_indicator_bitmap: svc %d\n", svc_indicator);
++
++    if (svc_indicator < 0 || svc_indicator >= EP_SVC_NUM_INDICATORS)
++      return (-1);
++
++    /* clear bitmap */
++    bt_zero (bitmap, nnodes);
++
++    for (i = 0; i < EP_MAX_RAILS; i++)
++    {
++      if (xmtr->RailMask & (1 << i) )
++      {
++          /* this will or in each bit map */
++          cm_svc_indicator_bitmap (xmtr->Rails[i]->CommsRail->Rail, svc_indicator, bitmap, low, nnodes);
++      }
++    }
++
++    return (0);
++}
++
++EP_RAILMASK
++ep_svc_indicator_railmask (EP_SYS *epsys, int svc_indicator, int nodeId)
++{
++    EP_COMMS_SUBSYS  *subsys;
++    struct list_head *el;
++    EP_RAILMASK       rmask=0;
++
++    if ((subsys = (EP_COMMS_SUBSYS *) ep_subsys_find (epsys, "epcomms")) == NULL)
++      return (rmask);
++
++    kmutex_lock (&subsys->Lock); /* walking rails list and reading info from Rail */
++    list_for_each (el, &subsys->Rails) { 
++      EP_COMMS_RAIL *commsRail = list_entry (el, EP_COMMS_RAIL, Link);
++
++      if ( cm_svc_indicator_is_set(commsRail->Rail, svc_indicator,nodeId))
++           rmask |= EP_RAIL2RAILMASK(commsRail->Rail->Number);
++    }
++    kmutex_unlock (&subsys->Lock);
++
++    return (rmask);
++}
++
++EP_RAILMASK
++ep_xmtr_svc_indicator_railmask (EP_XMTR *xmtr, int svc_indicator, int nodeId)
++{
++    EP_RAILMASK    rmask=0;
++    EP_COMMS_RAIL *commsRail;
++    int            i;
++
++    for (i = 0; i < EP_MAX_RAILS; i++)
++    {
++      if (xmtr->RailMask & (1 << i) )
++      {
++          commsRail = xmtr->Rails[i]->CommsRail;
++
++          if ( cm_svc_indicator_is_set(commsRail->Rail, svc_indicator,nodeId))
++              rmask |= EP_RAIL2RAILMASK(commsRail->Rail->Number);
++      }
++    }   
++    
++    EPRINTF3 (DBG_SVC, "ep_xmtr_svc_indicator_railmask: svc %d node %d mask 0x%x\n",  svc_indicator, nodeId, rmask);
++
++    return (rmask);
++}
++
++EP_RAILMASK
++ep_rcvr_railmask (EP_SYS *epsys, EP_SERVICE service)
++{
++    EP_COMMS_SUBSYS  *subsys;
++    EP_RAILMASK       rmask=0;
++    struct list_head *el;
++    
++    if ((subsys = (EP_COMMS_SUBSYS *) ep_subsys_find (epsys, "epcomms")) == NULL)
++      return (rmask);
++    
++    kmutex_lock (&subsys->Lock);
++    list_for_each (el, &subsys->Receivers) {
++      EP_RCVR *rcvr = list_entry (el, EP_RCVR, Link);
++ 
++      if (rcvr->Service == service)
++          rmask |= rcvr->RailMask; 
++    }
++    kmutex_unlock(&subsys->Lock);
++
++    return (rmask);
++}
++
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++uint32_t
++ep_calc_check_sum (EP_SYS *sys, EP_ENVELOPE *env, EP_NMD *nmd, int nFrags)
++{
++    EP_NMH   *nmh;
++    int       i;
++    uint16_t  check_data = 0;
++    uint16_t  check_env  = 0;
++
++    for (i = 0; i < nFrags; i++) {
++      /* find the nmh for this frag */
++      nmh = ep_nmh_find (&sys->MappingTable, &nmd[i]);
++
++      ASSERT( nmh != NULL);
++
++      /* add the next frag to the check sum */
++      check_data = nmh->nmh_ops->op_calc_check_sum (sys, nmh, &nmd[i], check_data);
++    }
++
++    check_env = rolling_check_sum ((char *) env, offsetof(EP_ENVELOPE, CheckSum), 0);
++
++    return (EP_ENVELOPE_CHECK_SUM | ( (check_env & 0x7FFF) << 16) | (check_data & 0xFFFF));
++}
++#endif
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/ep/epcomms_elan3.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/epcomms_elan3.c     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/epcomms_elan3.c  2005-06-01 23:12:54.638433176 -0400
+@@ -0,0 +1,191 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: epcomms_elan3.c,v 1.60 2004/08/03 11:34:34 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/epcomms_elan3.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++#include <elan/epsvc.h>
++#include <elan/epcomms.h>
++
++#include "kcomm_elan3.h"
++#include "epcomms_elan3.h"
++
++void
++ep3comms_flush_callback (void *arg, statemap_t *map)
++{
++    EP_COMMS_RAIL    *commsRail = (EP_COMMS_RAIL *) arg;
++    EP_COMMS_SUBSYS  *subsys    = commsRail->Subsys;
++    struct list_head *el;
++
++    kmutex_lock (&subsys->Lock);
++    list_for_each (el, &subsys->Transmitters) {
++      EP_XMTR *xmtr = list_entry (el, EP_XMTR, Link);
++      
++      if (xmtr->Rails[commsRail->Rail->Number])
++          ep3xmtr_flush_callback (xmtr, (EP3_XMTR_RAIL *) xmtr->Rails[commsRail->Rail->Number]);
++    }
++
++    list_for_each (el, &subsys->Receivers) {
++      EP_RCVR *rcvr = list_entry (el, EP_RCVR, Link);
++      
++      if (rcvr->Rails[commsRail->Rail->Number])
++          ep3rcvr_flush_callback (rcvr, (EP3_RCVR_RAIL *) rcvr->Rails[commsRail->Rail->Number]);
++    }
++    kmutex_unlock (&subsys->Lock);
++}
++
++void
++ep3comms_failover_callback (void *arg, statemap_t *map)
++{
++    EP_COMMS_RAIL    *commsRail = (EP_COMMS_RAIL *) arg;
++    EP_COMMS_SUBSYS  *subsys    = commsRail->Subsys;
++    struct list_head *el;
++
++    kmutex_lock (&subsys->Lock);
++    list_for_each (el, &subsys->Transmitters) {
++      EP_XMTR *xmtr = list_entry (el, EP_XMTR, Link);
++      
++      if (xmtr->Rails[commsRail->Rail->Number])
++          ep3xmtr_failover_callback (xmtr, (EP3_XMTR_RAIL *) xmtr->Rails[commsRail->Rail->Number]);
++    }
++
++    list_for_each (el, &subsys->Receivers) {
++      EP_RCVR *rcvr = list_entry (el, EP_RCVR, Link);
++      
++      if (rcvr->Rails[commsRail->Rail->Number])
++          ep3rcvr_failover_callback (rcvr, (EP3_RCVR_RAIL *) rcvr->Rails[commsRail->Rail->Number]);
++    }
++    kmutex_unlock (&subsys->Lock);
++}
++
++void
++ep3comms_disconnect_callback (void *arg, statemap_t *map)
++{
++    EP_COMMS_RAIL    *commsRail = (EP_COMMS_RAIL *) arg;
++    EP_COMMS_SUBSYS  *subsys    = commsRail->Subsys;
++    struct list_head *el;
++
++    kmutex_lock (&subsys->Lock);
++    list_for_each (el, &subsys->Transmitters) {
++      EP_XMTR *xmtr = list_entry (el, EP_XMTR, Link);
++      
++      if (xmtr->Rails[commsRail->Rail->Number])
++          ep3xmtr_disconnect_callback (xmtr, (EP3_XMTR_RAIL *) xmtr->Rails[commsRail->Rail->Number]);
++    }
++
++    list_for_each (el, &subsys->Receivers) {
++      EP_RCVR *rcvr = list_entry (el, EP_RCVR, Link);
++      
++      if (rcvr->Rails[commsRail->Rail->Number])
++          ep3rcvr_disconnect_callback (rcvr, (EP3_RCVR_RAIL *) rcvr->Rails[commsRail->Rail->Number]);
++    }
++    kmutex_unlock (&subsys->Lock);
++}
++
++EP_COMMS_RAIL *
++ep3comms_add_rail (EP_SUBSYS *s, EP_SYS *sys, EP_RAIL *r)
++{
++    EP3_RAIL         *rail   = (EP3_RAIL *) r;
++    ELAN3_DEV        *dev    = rail->Device;
++    EP3_COMMS_RAIL   *commsRail;
++    EP3_InputQueue    qdesc;
++    int i;
++
++    KMEM_ZALLOC (commsRail, EP3_COMMS_RAIL *, sizeof (EP3_COMMS_RAIL), TRUE);
++
++    if (commsRail == NULL)
++      return NULL;
++    
++    commsRail->Generic.Ops.DelRail          = ep3comms_del_rail;
++    commsRail->Generic.Ops.DisplayRail      = ep3comms_display_rail;
++    commsRail->Generic.Ops.Rcvr.AddRail     = ep3rcvr_add_rail;
++    commsRail->Generic.Ops.Rcvr.DelRail     = ep3rcvr_del_rail;
++    commsRail->Generic.Ops.Rcvr.Check       = ep3rcvr_check;
++    commsRail->Generic.Ops.Rcvr.QueueRxd    = ep3rcvr_queue_rxd;
++    commsRail->Generic.Ops.Rcvr.RpcPut      = ep3rcvr_rpc_put;
++    commsRail->Generic.Ops.Rcvr.RpcGet      = ep3rcvr_rpc_get;
++    commsRail->Generic.Ops.Rcvr.RpcComplete = ep3rcvr_rpc_complete;
++
++    commsRail->Generic.Ops.Rcvr.StealRxd    = ep3rcvr_steal_rxd;
++
++    commsRail->Generic.Ops.Rcvr.FillOutRailStats = ep3rcvr_fillout_rail_stats;
++
++    commsRail->Generic.Ops.Rcvr.DisplayRcvr = ep3rcvr_display_rcvr;
++    commsRail->Generic.Ops.Rcvr.DisplayRxd  = ep3rcvr_display_rxd;
++
++    commsRail->Generic.Ops.Xmtr.AddRail     = ep3xmtr_add_rail;
++    commsRail->Generic.Ops.Xmtr.DelRail     = ep3xmtr_del_rail;
++    commsRail->Generic.Ops.Xmtr.Check       = ep3xmtr_check;
++    commsRail->Generic.Ops.Xmtr.BindTxd     = ep3xmtr_bind_txd;
++    commsRail->Generic.Ops.Xmtr.UnbindTxd   = ep3xmtr_unbind_txd;
++    commsRail->Generic.Ops.Xmtr.PollTxd     = ep3xmtr_poll_txd;
++    commsRail->Generic.Ops.Xmtr.CheckTxdState = ep3xmtr_check_txd_state;
++
++    commsRail->Generic.Ops.Xmtr.DisplayXmtr = ep3xmtr_display_xmtr;
++    commsRail->Generic.Ops.Xmtr.DisplayTxd  = ep3xmtr_display_txd;
++
++    commsRail->Generic.Ops.Xmtr.FillOutRailStats = ep3xmtr_fillout_rail_stats;
++
++    /* Allocate the input queues at their fixed elan address */
++    if (! (commsRail->QueueDescs = ep_alloc_memory_elan (r, EP_EPCOMMS_QUEUE_BASE, roundup (EP_MSG_NSVC * sizeof (EP3_InputQueue), PAGESIZE), EP_PERM_ALL, 0)))
++    {
++      KMEM_FREE (commsRail, sizeof (EP3_COMMS_RAIL));
++      return NULL;
++    }
++
++    qdesc.q_state          = E3_QUEUE_FULL;
++    qdesc.q_base           = 0;
++    qdesc.q_top            = 0;
++    qdesc.q_fptr           = 0;
++    qdesc.q_bptr           = 0;
++    qdesc.q_size           = 0;
++    qdesc.q_event.ev_Count = 0;
++    qdesc.q_event.ev_Type  = 0;
++
++    /* Initialise all queue entries to be full */
++    for (i = 0; i < EP_MSG_NSVC; i++)
++      elan3_sdram_copyl_to_sdram (dev, &qdesc, commsRail->QueueDescs + (i * sizeof (EP3_InputQueue)), sizeof (EP3_InputQueue));
++
++    ep_register_callback (r, EP_CB_FLUSH_FILTERING, ep3comms_flush_callback,      commsRail);
++    ep_register_callback (r, EP_CB_FLUSH_FLUSHING,  ep3comms_flush_callback,      commsRail);
++    ep_register_callback (r, EP_CB_FAILOVER,        ep3comms_failover_callback,   commsRail);
++    ep_register_callback (r, EP_CB_DISCONNECTING,   ep3comms_disconnect_callback, commsRail);
++
++    return (EP_COMMS_RAIL *) commsRail;
++}
++
++void
++ep3comms_del_rail (EP_COMMS_RAIL *r)
++{
++    EP3_COMMS_RAIL *commsRail = (EP3_COMMS_RAIL *) r;
++    EP_RAIL        *rail      = commsRail->Generic.Rail;
++
++    ep_remove_callback (rail, EP_CB_FLUSH_FILTERING, ep3comms_flush_callback,      commsRail);
++    ep_remove_callback (rail, EP_CB_FLUSH_FLUSHING,  ep3comms_flush_callback,      commsRail);
++    ep_remove_callback (rail, EP_CB_FAILOVER,        ep3comms_failover_callback,   commsRail);
++    ep_remove_callback (rail, EP_CB_DISCONNECTING,   ep3comms_disconnect_callback, commsRail);
++
++    ep_free_memory_elan (rail, EP_EPCOMMS_QUEUE_BASE);
++
++    KMEM_FREE (commsRail, sizeof (EP3_COMMS_RAIL));
++}
++
++void
++ep3comms_display_rail (EP_COMMS_RAIL *r)
++{
++    
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/ep/epcomms_elan3.h
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/epcomms_elan3.h     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/epcomms_elan3.h  2005-06-01 23:12:54.639433024 -0400
+@@ -0,0 +1,330 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __EPCOMMS_ELAN3_H
++#define __EPCOMMS_ELAN3_H
++
++#ident "@(#)$Id: epcomms_elan3.h,v 1.27.2.1 2004/11/12 10:54:51 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/epcomms_elan3.h,v $ */
++
++#define EP3_DMAFAILCOUNT              3
++
++
++/* Main/Elan spinlock */
++typedef struct ep3_spinlock_elan 
++{
++    volatile E3_uint32        sl_lock;                /* main wants a lock */
++    volatile E3_uint32        sl_seq;                 /* thread owns this word */
++    /* NOTE: The lock/seq words must be within the same 32-byte Elan cache-line */
++    E3_uint64         sl_pad[14];             /* pad to 64-bytes */
++} EP3_SPINLOCK_ELAN;
++
++/* Declare this as a main memory cache block for efficiency */
++typedef struct ep3_spinlock_main {
++    volatile E3_uint32        sl_seq;                 /* copy of seq number updated by Elan */
++    volatile E3_uint32        sl_pad[15];             /* pad to 64-bytes */
++} EP3_SPINLOCK_MAIN;
++
++#if defined (__ELAN3__)
++
++extern void ep3_spinblock (EP3_SPINLOCK_ELAN *, EP3_SPINLOCK_MAIN *);
++
++#define EP3_SPINENTER(SLE,SL) \
++do {\
++      (SLE)->sl_seq++; \
++      if ((SLE)->sl_lock) \
++              ep3_spinblock(SLE, SL);\
++} while (0)
++
++#define EP3_SPINEXIT(SLE,SL) \
++do {\
++      (SL)->sl_seq = (SLE)->sl_seq;\
++} while (0)
++
++#else
++
++#define EP3_SPINENTER(DEV,SLE,SL) do { \
++    E3_uint32 seq; \
++\
++    mb();\
++    elan3_sdram_writel (DEV, (SLE) + offsetof (EP3_SPINLOCK_ELAN, sl_lock), 1);\
++    mb();\
++    seq = elan3_sdram_readl (DEV, (SLE) + offsetof (EP3_SPINLOCK_ELAN, sl_seq));\
++    while (seq != (SL)->sl_seq)\
++    {\
++      while ((SL)->sl_seq == (seq - 1))\
++      {\
++          mb();\
++\
++          DELAY (1); \
++      }\
++      seq = elan3_sdram_readl (DEV, (SLE) + offsetof (EP3_SPINLOCK_ELAN, sl_seq));\
++    }\
++} while (0)
++
++#define EP3_SPINEXIT(DEV,SLE,SL) do { \
++      wmb(); \
++      elan3_sdram_writel (DEV, (SLE) + offsetof (EP3_SPINLOCK_ELAN, sl_lock), 0);\
++      mmiob(); \
++} while (0)
++
++#endif /* ! __ELAN3__ */
++
++/* per-rail elan memory portion receive descriptor */
++typedef struct ep3_rxd_rail_elan
++{
++    E3_DMA            Dmas[EP_MAXFRAG+1];                     /* Dma's for fetching data/putting data & status blk */
++    E3_Event          ChainEvent[EP_MAXFRAG];                 /* Events to chain dmas */
++    E3_BlockCopyEvent DataEvent;                              /* message received block event */
++    E3_BlockCopyEvent DoneEvent;                              /* RPC status block event */
++    
++    EP_NMD            Data;                                   /* Network mapping handle for receive data */
++
++    E3_Addr           RxdMain;                                /* pointer to main memory portion */
++
++    E3_Addr           Next;                                   /* linked list when on pending list (elan address) */
++
++    E3_uint64         MainAddr;                               /* kernel address of ep_rxd_main */
++} EP3_RXD_RAIL_ELAN;
++
++#define EP3_RXD_RAIL_ELAN_SIZE        roundup (sizeof (EP3_RXD_RAIL_ELAN), E3_DMA_ALIGN)
++
++/* per-rail main memory portion of receive descriptor */
++typedef struct ep3_rxd_rail_main
++{
++    E3_uint32         DataEvent;                              /* dest for done event */
++    E3_uint32         DoneEvent;                              /* dest for done event */
++} EP3_RXD_RAIL_MAIN;
++
++#define EP3_RXD_RAIL_MAIN_SIZE        roundup (sizeof(EP3_RXD_RAIL_MAIN), sizeof (E3_uint32))
++
++#if !defined(__ELAN3__)
++/* Kernel memory portion of per-rail receive descriptor */
++typedef struct ep3_rxd_rail
++{
++    EP_RXD_RAIL               Generic;                                /* generic rxd rail */
++
++    EP3_COOKIE                DataCookie;                             /* Event cookie */
++    EP3_COOKIE                DoneCookie;                             /* Event cookie */
++    EP3_COOKIE                ChainCookie[EP_MAXFRAG];                /* Event cookie */
++
++    sdramaddr_t               RxdElan;                                /* per-rail elan receive descriptor */
++    E3_Addr           RxdElanAddr;                            /*   and elan address */
++
++    EP3_RXD_RAIL_MAIN  *RxdMain;                              /* per-rail main receive descriptor */
++    E3_Addr           RxdMainAddr;                            /*   and elan address */
++
++    EP_BACKOFF                Backoff;                                /* dma backoff */
++} EP3_RXD_RAIL;
++
++#define EP3_NUM_RXD_PER_BLOCK 16
++
++typedef struct ep3_rxd_rail_block
++{
++    struct list_head  Link;
++    
++    EP3_RXD_RAIL        Rxd[EP3_NUM_RXD_PER_BLOCK];
++} EP3_RXD_RAIL_BLOCK;
++
++#endif /* ! __ELAN3__ */
++
++typedef struct ep3_rcvr_rail_elan                             /* Elan memory service structure */
++{
++    EP3_SPINLOCK_ELAN  ThreadLock;                            /* elan memory portion of spin lock */
++    EP3_SPINLOCK_ELAN  PendingLock;                           /* spin lock for pending rx list */
++
++    E3_Addr          PendingDescs;                            /* list of pending receive descriptors */
++    E3_uint32          ThreadShouldHalt;                        /* marks that the thread should halt */
++
++    E3_uint64        MainAddr;                                /* kernel address of ep_rcvr (for StallThreadForNoDescs)*/
++} EP3_RCVR_RAIL_ELAN;
++
++typedef struct ep3_rcvr_rail_main                             /* Main memory service strucure */
++{
++    EP3_SPINLOCK_MAIN ThreadLock;                             /* main memory portion of spin lock */
++    EP3_SPINLOCK_MAIN PendingLock;                            /* spinlock for pending rx list */
++
++    volatile unsigned   PendingDescsTailp;                    /* next pointer of last receive descriptor on pending list */
++} EP3_RCVR_RAIL_MAIN;
++
++#if !defined(__ELAN3__)
++
++typedef struct ep3_rcvr_rail_stats
++{
++    unsigned long some_stat;
++} EP3_RCVR_RAIL_STATS;
++
++typedef struct ep3_rcvr_rail
++{
++    EP_RCVR_RAIL      Generic;                                /* generic portion */
++    
++    EP3_RCVR_RAIL_MAIN *RcvrMain;
++    E3_Addr           RcvrMainAddr;
++    sdramaddr_t         RcvrElan;
++    E3_Addr             RcvrElanAddr;
++
++    sdramaddr_t               InputQueueBase;                         /* base of receive queue */
++    E3_Addr           InputQueueAddr;                         /* elan address of receive queue */
++
++    E3_Addr           ThreadStack;                            /* Thread processor stack */
++    E3_Addr           ThreadWaiting;                          /* Elan thread is waiting as no receive descriptors pending (sp stored here ) */
++    E3_Addr           ThreadHalted;                           /* Elan thread is waiting as it was requested to halt */
++
++    struct list_head  FreeDescList;                           /* freelist of per-rail receive descriptors */
++    unsigned int      FreeDescCount;                          /*   and number on free list */
++    unsigned int        TotalDescCount;                               /*   total number created */
++    spinlock_t                FreeDescLock;                           /*   and lock for free list */
++    struct list_head    DescBlockList;                                /* list of receive descriptor blocks */
++
++    unsigned int        FreeDescWaiting;                      /* waiting for descriptors to be freed */
++    kcondvar_t                FreeDescSleep;                          /*   and sleep here */
++
++    unsigned int      CleanupWaiting;                         /* waiting for cleanup */
++    kcondvar_t                CleanupSleep;                           /*   and sleep here */
++
++    EP3_RCVR_RAIL_STATS stats;                                  /* elan3 specific rcvr_rail stats */
++} EP3_RCVR_RAIL;
++
++#endif /* ! __ELAN3__ */
++
++/* per-rail portion of transmit descriptor */
++typedef struct ep3_txd_rail_elan
++{
++    EP_ENVELOPE              Envelope;                                /* message envelope */
++    EP_PAYLOAD               Payload;                                 /* message payload */
++
++    E3_BlockCopyEvent EnveEvent;                              /* envelope event */
++    E3_BlockCopyEvent DataEvent;                              /* data transfer event */
++    E3_BlockCopyEvent DoneEvent;                              /* rpc done event */
++} EP3_TXD_RAIL_ELAN;
++
++#define EP3_TXD_RAIL_ELAN_SIZE        roundup (sizeof (EP3_TXD_RAIL_ELAN), E3_BLK_ALIGN)
++
++typedef struct ep3_txd_rail_main
++{
++    E3_uint32         EnveEvent;                              /* dest for envelope event */
++    E3_uint32         DataEvent;                              /* dest for data transfer event */
++    E3_uint32       DoneEvent;                                /* dest for rpc done event */
++} EP3_TXD_RAIL_MAIN;
++
++#define EP3_TXD_RAIL_MAIN_SIZE        roundup (sizeof(EP3_TXD_RAIL_MAIN), E3_BLK_ALIGN)
++
++#if !defined(__ELAN3__)
++
++typedef struct ep3_txd_rail
++{
++    EP_TXD_RAIL              Generic;                                 /* generic txd rail */
++
++    EP3_COOKIE               EnveCookie;                              /* Event cookies */
++    EP3_COOKIE               DataCookie;
++    EP3_COOKIE               DoneCookie;
++
++    sdramaddr_t              TxdElan;                                 /* Elan TX descriptor */
++    E3_Addr          TxdElanAddr;                             /*  and elan address */
++
++    EP3_TXD_RAIL_MAIN *TxdMain;                                       /* Elan Main memory tx descriptor */
++    E3_Addr          TxdMainAddr;                             /*  and elan address */
++
++    EP_BACKOFF               Backoff;                                 /* dma backoff */
++} EP3_TXD_RAIL;
++
++
++#define EP3_NUM_TXD_PER_BLOCK 16
++
++typedef struct ep3_txd_rail_block
++{
++    struct list_head  Link;
++    
++    EP3_TXD_RAIL      Txd[EP3_NUM_TXD_PER_BLOCK];
++} EP3_TXD_RAIL_BLOCK;
++
++typedef struct ep3_xmtr_rail_stats
++{
++    unsigned long some_stat;
++} EP3_XMTR_RAIL_STATS;
++
++typedef struct ep3_xmtr_rail
++{
++    EP_XMTR_RAIL      Generic;                                /* generic portion */
++
++    struct list_head  FreeDescList;                           /* freelist of per-rail receive descriptors */
++    unsigned int      FreeDescCount;                          /*   and number on free list */
++    unsigned int        TotalDescCount;
++    spinlock_t                FreeDescLock;                           /*   and lock for free list */
++    struct list_head    DescBlockList;                                /* list of receive descriptor blocks */
++
++    unsigned int        FreeDescWaiting;                      /* waiting for descriptors to be freed */
++    kcondvar_t          FreeDescSleep;                                /*   and sleep here */
++
++    EP3_XMTR_RAIL_STATS stats;                                  /* elan3 specific xmtr rail stats */
++} EP3_XMTR_RAIL;
++
++typedef struct ep3_comms_rail
++{
++    EP_COMMS_RAIL     Generic;                                /* generic comms rail */
++    sdramaddr_t               QueueDescs;                             /* input queue descriptors */
++} EP3_COMMS_RAIL;
++
++/* epcommxTx_elan3.c */
++extern void           ep3xmtr_flush_callback (EP_XMTR *xmtr, EP3_XMTR_RAIL *xmtrRail);
++extern void           ep3xmtr_failover_callback (EP_XMTR *xmtr, EP3_XMTR_RAIL *xmtrRail);
++extern void           ep3xmtr_disconnect_callback (EP_XMTR *xmtr, EP3_XMTR_RAIL *xmtrRail);
++
++/* epcommsRx_elan3.c */
++extern void         CompleteEnvelope (EP3_RAIL *rail, E3_Addr rxdMainAddr, E3_uint32 PAckVal);
++extern void           StallThreadForNoDescs (EP3_RAIL *rail, E3_Addr rcvrElanAddr, E3_Addr sp);
++extern void           StallThreadForHalted  (EP3_RAIL *rail, E3_Addr rcvrElanAddr, E3_Addr sp);
++
++extern void           ep3rcvr_flush_callback (EP_RCVR *rcvr, EP3_RCVR_RAIL *rcvrRail);
++extern void           ep3rcvr_failover_callback (EP_RCVR *rcvr, EP3_RCVR_RAIL *rcvrRail);
++extern void           ep3rcvr_disconnect_callback (EP_RCVR *rcvr, EP3_RCVR_RAIL *rcvrRail);
++
++/* epcomms_elan3.c */
++extern EP_COMMS_RAIL *ep3comms_add_rail (EP_SUBSYS *s, EP_SYS *sys, EP_RAIL *r);
++extern void           ep3comms_del_rail (EP_COMMS_RAIL *r);
++extern void           ep3comms_display_rail (EP_COMMS_RAIL *r);
++
++/* epcommsTx_elan3.c */
++extern int            ep3xmtr_bind_txd (EP_TXD *txd, EP_XMTR_RAIL *xmtrRail, unsigned int phase);
++extern void           ep3xmtr_unbind_txd (EP_TXD *txd, unsigned int phase);
++extern int            ep3xmtr_poll_txd (EP_XMTR_RAIL *xmtrRail, EP_TXD_RAIL *txdRail, int how);
++extern long           ep3xmtr_check (EP_XMTR_RAIL *xmtrRail, long nextRunTime);
++extern void           ep3xmtr_add_rail (EP_XMTR *xmtr, EP_COMMS_RAIL *commsRail);
++extern void           ep3xmtr_del_rail (EP_XMTR *xmtr, EP_COMMS_RAIL *commsRail);
++extern int            ep3xmtr_check_txd_state(EP_TXD *txd);
++
++extern void           ep3xmtr_display_xmtr (DisplayInfo *di, EP_XMTR_RAIL *xmtrRail);
++extern void           ep3xmtr_display_txd  (DisplayInfo *di, EP_TXD_RAIL *txdRail);
++
++extern void           ep3xmtr_fillout_rail_stats (EP_XMTR_RAIL *xmtr_rail, char *str);
++
++/* epcommsRx_elan3.c */
++extern int          ep3rcvr_queue_rxd (EP_RXD *rxd, EP_RCVR_RAIL *rcvrRail);
++extern void         ep3rcvr_rpc_put (EP_RXD *rxd, EP_NMD *local, EP_NMD *remote, unsigned nFrags);
++extern void         ep3rcvr_rpc_get (EP_RXD *rxd, EP_NMD *local, EP_NMD *remote, unsigned nFrags);
++extern void         ep3rcvr_rpc_complete (EP_RXD *rxd, EP_NMD *local, EP_NMD *remote, unsigned nFrags);
++
++extern EP_RXD      *ep3rcvr_steal_rxd (EP_RCVR_RAIL *rcvrRail);
++
++extern long         ep3rcvr_check (EP_RCVR_RAIL *rcvrRail, long nextRunTime);
++extern void           ep3rcvr_add_rail (EP_RCVR *rcvr, EP_COMMS_RAIL *rail);
++extern void           ep3rcvr_del_rail (EP_RCVR *rcvr, EP_COMMS_RAIL *rail);
++
++extern void           ep3rcvr_display_rcvr (DisplayInfo *di, EP_RCVR_RAIL *rcvrRail);
++extern void           ep3rcvr_display_rxd  (DisplayInfo *di, EP_RXD_RAIL *rxdRail);
++
++extern void           ep3rcvr_fillout_rail_stats (EP_RCVR_RAIL *rcvr_rail, char *str);
++
++#endif /* !defined(__ELAN3__) */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++#endif /* __EPCOMMS_ELAN3_H */
+Index: linux-2.4.21/drivers/net/qsnet/ep/epcomms_elan3_thread.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/epcomms_elan3_thread.c      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/epcomms_elan3_thread.c   2005-06-01 23:12:54.640432872 -0400
+@@ -0,0 +1,296 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: epcomms_elan3_thread.c,v 1.4 2004/01/20 11:03:15 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/epcomms_elan3_thread.c,v $ */
++
++//#include <qsnet/types.h>
++
++typedef char               int8_t;
++typedef unsigned char      uint8_t;
++typedef short              int16_t;
++typedef unsigned short     uint16_t;
++typedef int                int32_t;
++typedef unsigned int       uint32_t;
++typedef long long          int64_t;
++typedef unsigned long long uint64_t;
++
++#include <elan3/e3types.h>
++#include <elan3/events.h>
++#include <elan3/elanregs.h>
++#include <elan3/intrinsics.h>
++
++#include <elan/nmh.h>
++#include <elan/kcomm.h>
++#include <elan/epcomms.h>
++
++#include "kcomm_vp.h"
++#include "kcomm_elan3.h"
++#include "epcomms_elan3.h"
++
++#ifndef offsetof
++#define offsetof(s, m)                        (unsigned long)(&(((s *)0)->m))
++#endif
++
++EP3_RAIL_ELAN *rail;
++EP3_RCVR_RAIL_ELAN *r;
++EP3_RCVR_RAIL_MAIN *rm;
++
++void
++ep3comms_rcvr (EP3_RAIL_ELAN *rail, EP3_RCVR_RAIL_ELAN *rcvrElan, EP3_RCVR_RAIL_MAIN *rcvrMain, 
++            EP3_InputQueue *q, unsigned int *cookies)
++{
++    int           count = 1;
++    E3_Addr       nfptr = q->q_fptr + q->q_size;
++    E3_uint32     tmp;
++    int           i;
++    E3_Addr       buffer;
++    int                 len;
++    E3_DMA       *dma;
++    E3_Event     *event;
++
++    /* clear the queue state to allow envelopes to arrive */
++    q->q_state = 0;
++
++    for (;;)
++    {
++      if (! rcvrElan->ThreadShouldHalt)
++          c_waitevent ((E3_Event *) &q->q_event, count);                                              /* HALT POINT */
++
++      if (rcvrElan->ThreadShouldHalt && nfptr == q->q_bptr)
++      {
++          asm volatile ("mov %0, %%g1" : /* no outputs */ : "r" (rcvrElan));
++          asm volatile ("ta %0"        : /* no outputs */ : "i" (EP3_UNIMP_THREAD_HALTED));           /* HALT POINT */
++          continue;
++      }
++
++      count = 0;
++      do {
++          /* Process the message at nfptr */
++          EP_ENVELOPE      *env = (EP_ENVELOPE *) nfptr;
++          EP3_RXD_RAIL_ELAN *rxd;
++          int ack;
++          
++          EP3_SPINENTER(&rcvrElan->ThreadLock, &rcvrMain->ThreadLock);                                        /* HALT POINT */
++          
++          while ((rxd = (EP3_RXD_RAIL_ELAN *)rcvrElan->PendingDescs) == 0)
++          {
++              /* no receive descriptors, so trap to the kernel to wait
++               * for receive descriptor to be queued, we pass the rcvr
++               * in %g1, so that the trap handler can restart us. */
++              EP3_SPINEXIT(&rcvrElan->ThreadLock, &rcvrMain->ThreadLock);
++              asm volatile ("mov %0, %%g1" : /* no outputs */ : "r" (rcvrElan));
++              asm volatile ("ta %0"        : /* no outputs */ : "i" (EP3_UNIMP_TRAP_NO_DESCS));       /* HALT POINT */
++              EP3_SPINENTER(&rcvrElan->ThreadLock, &rcvrMain->ThreadLock);                            /* HALT POINT */
++          }
++
++          if (env->Version != EP_ENVELOPE_VERSION)
++          {
++              /* This envelope has been cancelled - so just consume it */
++              EP3_SPINEXIT(&rcvrElan->ThreadLock, &rcvrMain->ThreadLock);
++              goto consume_envelope;
++          }
++
++          dma   = rxd->Dmas;
++          event = rxd->ChainEvent;
++
++          if (EP_IS_MULTICAST(env->Attr))
++          {
++              dma->dma_type            = E3_DMA_TYPE (DMA_BYTE, DMA_READ, DMA_NORMAL, EP3_DMAFAILCOUNT);
++              dma->dma_size            = BT_BITOUL(EP_MAX_NODES) * sizeof (bitmap_t);
++              dma->dma_source          = env->TxdMain.nmd_addr + offsetof (EP_TXD_MAIN, Bitmap);
++              dma->dma_dest            = (E3_Addr) &((EP_RXD_MAIN *) rxd->RxdMain)->Bitmap;
++              dma->dma_destEvent       = (E3_Addr) event;
++              dma->dma_destCookieVProc = DMA_COOKIE_THREAD | DMA_COOKIE (cookies[env->NodeId], EP_VP_DATA (rail->NodeId));
++              dma->dma_srcEvent        = env->TxdRail + offsetof (EP3_TXD_RAIL_ELAN, DataEvent);
++              dma->dma_srcCookieVProc  = DMA_COOKIE_THREAD | DMA_REMOTE_COOKIE (cookies[env->NodeId], EP_VP_DATA (env->NodeId));
++              
++              event->ev_Count = 1;
++
++              dma++; event++;
++          }
++
++          if (env->nFrags == 0)
++          {
++              /* Generate a "get" DMA to accept the envelope and fire the rx handler */
++              dma->dma_type            = E3_DMA_TYPE(DMA_BYTE, DMA_READ, DMA_NORMAL, EP3_DMAFAILCOUNT);
++              dma->dma_size            = 0;
++              dma->dma_destEvent       = (E3_Addr) &rxd->DataEvent;
++              dma->dma_destCookieVProc = DMA_COOKIE_THREAD | DMA_COOKIE (cookies[env->NodeId], EP_VP_DATA (rail->NodeId));
++              dma->dma_srcEvent        = env->TxdRail + offsetof (EP3_TXD_RAIL_ELAN, DataEvent);
++              dma->dma_srcCookieVProc  = DMA_COOKIE_THREAD | DMA_REMOTE_COOKIE (cookies[env->NodeId], EP_VP_DATA (env->NodeId));
++              len = 0;
++          }
++          else
++          {
++              /* Generate the DMA chain to fetch the data */
++              for (i = 0, buffer = rxd->Data.nmd_addr, len = 0; i < env->nFrags; i++, dma++, event++)
++              {
++                  dma->dma_type            = E3_DMA_TYPE(DMA_BYTE, DMA_READ, DMA_NORMAL, EP3_DMAFAILCOUNT);
++                  dma->dma_size            = env->Frags[i].nmd_len;
++                  dma->dma_source          = env->Frags[i].nmd_addr;
++                  dma->dma_dest            = buffer;
++                  dma->dma_destEvent       = (E3_Addr) event;
++                  dma->dma_destCookieVProc = DMA_COOKIE_THREAD | DMA_COOKIE (cookies[env->NodeId], EP_VP_DATA (rail->NodeId));
++                  dma->dma_srcEvent        = env->TxdRail + offsetof (EP3_TXD_RAIL_ELAN, DataEvent);
++                  dma->dma_srcCookieVProc  = DMA_COOKIE_THREAD | DMA_REMOTE_COOKIE (cookies[env->NodeId], EP_VP_DATA (env->NodeId));
++                  
++                  event->ev_Count = 1;
++                  
++                  buffer += dma->dma_size;
++                  len    += dma->dma_size;
++              }
++              
++              /* Point the last dma at the done event */
++              (--dma)->dma_destEvent = (E3_Addr) &rxd->DataEvent;
++              
++              if (rxd->Data.nmd_len < len)
++              {
++                  /* The receive descriptor was too small for the message */
++                  /* complete the message anyway,  but don't transfer any */
++                  /* data,  we set the length to EP_MSG_TOO_BIG */
++                  for (i = 0, dma = rxd->Dmas; i < env->nFrags; i++, dma++)
++                      dma->dma_size = 0;
++                  
++                  len = EP_MSG_TOO_BIG;
++              }
++          }
++          
++          /* Store the received message length in the rxdElan for CompleteEnvelope */
++          rxd->Data.nmd_len = len;
++
++          /* Initialise %g1 with the  "rxd" so the trap handler can
++           * complete the envelope processing if we trap while sending the
++           * packet */
++          asm volatile ("mov %0, %%g1" : /* no outputs */ : "r" (rxd));
++
++          /* Generate a packet to start the data transfer */
++          c_open (EP_VP_DATA (env->NodeId));
++          c_sendtrans2 (TR_THREADIDENTIFY, rxd->Dmas->dma_destCookieVProc, 0, 0);
++          c_sendmem (TR_SENDACK | TR_REMOTEDMA, 0, rxd->Dmas); 
++          ack = c_close();
++          
++          /*
++           * If we trapped for an output timeout, then the trap handler will have
++           * completed processing this envelope and cleared the spinlock, so we just
++           * need to update the queue descriptor.
++           */
++          if (ack == EP3_PAckStolen)
++              goto consume_envelope;
++          
++          if (ack != E3_PAckOk)
++          {
++              /* our packet got nacked, so trap into the kernel so that
++               * it can complete processing of this envelope.
++               */
++              asm volatile ("ta %0" : /* no outputs */ : "i" (EP3_UNIMP_TRAP_PACKET_NACKED));         /* HALT POINT */
++              goto consume_envelope;
++          }
++
++          /* remove the RXD from the pending list */
++          EP3_SPINENTER (&rcvrElan->PendingLock, &rcvrMain->PendingLock);
++          if ((rcvrElan->PendingDescs = rxd->Next) == 0)
++              rcvrMain->PendingDescsTailp = 0;
++          EP3_SPINEXIT (&rcvrElan->PendingLock, &rcvrMain->PendingLock);
++
++          /* Copy the envelope information - as 5 64 byte chunks.
++           * We force the parameters in g5, g6 so that they aren't
++           * trashed by the loadblk32 into the locals/ins
++           */
++          if (EP_HAS_PAYLOAD(env->Attr))
++          { 
++              register void *src asm ("g5") = (void *) env;
++              register void *dst asm ("g6") = (void *)  &((EP_RXD_MAIN *) rxd->RxdMain)->Envelope;
++
++              asm volatile (
++                  "and     %%sp,63,%%g7               ! Calculate stack alignment\n"
++                  "add     %%g7,64,%%g7               ! Space to save the registers\n"
++                  "sub     %%sp,%%g7,%%sp             ! align stack\n" 
++                  "stblock64 %%l0,[%%sp]              ! save the locals and ins\n"
++
++                  "ldblock64 [%0 + 0],%%l0            ! load 64-byte block into locals/ins\n"         /* copy envelope */
++                  "stblock64 %%l0,[%1 + 0]            ! store 64-byte block from local/ins\n"
++                  "ldblock64 [%0 + 64],%%l0           ! load 64-byte block into locals/ins\n"
++                  "stblock64 %%l0,[%1 + 64]           ! store 64-byte block from local/ins\n"
++
++                  "ldblock64 [%0 + 128],%%l0          ! load 64-byte block into locals/ins\n"         /* copy payload */
++                  "stblock64 %%l0,[%1 + 128]          ! store 64-byte block from local/ins\n"
++                  "ldblock64 [%0 + 192],%%l0          ! load 64-byte block into locals/ins\n"
++                  "stblock64 %%l0,[%1 + 192]          ! store 64-byte block from local/ins\n"
++
++                  "ldblock64 [%%sp],%%l0              ! restore locals and ins\n"
++                  "add     %%sp,%%g7,%%sp             ! restore stack pointer\n"
++                  : /* outputs */
++                  : /* inputs */ "r" (src), "r" (dst)
++                  : /* clobbered */ "g5", "g6", "g7" );
++          }
++          else
++          { 
++              register void *src asm ("g5") = (void *) env;
++              register void *dst asm ("g6") = (void *)  &((EP_RXD_MAIN *) rxd->RxdMain)->Envelope;
++
++              asm volatile (
++                  "and     %%sp,63,%%g7               ! Calculate stack alignment\n"
++                  "add     %%g7,64,%%g7               ! Space to save the registers\n"
++                  "sub     %%sp,%%g7,%%sp             ! align stack\n" 
++                  "stblock64 %%l0,[%%sp]              ! save the locals and ins\n"
++
++                  "ldblock64 [%0 + 0],%%l0            ! load 64-byte block into locals/ins\n"
++                  "stblock64 %%l0,[%1 + 0]            ! store 64-byte block from local/ins\n"
++                  "ldblock64 [%0 + 64],%%l0           ! load 64-byte block into locals/ins\n"
++                  "stblock64 %%l0,[%1 + 64]           ! store 64-byte block from local/ins\n"
++
++                  "ldblock64 [%%sp],%%l0              ! restore locals and ins\n"
++                  "add     %%sp,%%g7,%%sp             ! restore stack pointer\n"
++                  : /* outputs */
++                  : /* inputs */ "r" (src), "r" (dst)
++                  : /* clobbered */ "g5", "g6", "g7" );
++          }
++
++          /* Store the message length to indicate that I've finished */
++          ((EP_RXD_MAIN *) rxd->RxdMain)->Len = rxd->Data.nmd_len;                                    /* PCI write  */
++          
++          EP3_SPINEXIT(&rcvrElan->ThreadLock, &rcvrMain->ThreadLock);
++
++      consume_envelope:
++          /* Sample the queue full bit *BEFORE* moving the fptr.
++           * Then only clear it if it was full before, otherwise,
++           * as soon as the fptr is moved on the queue could fill 
++           * up, and so clearing it could mark a full queue as 
++           * empty.
++           *
++           * While the full bit is set, the queue is in a 'steady
++           * state', so it is safe to set the q_state
++           * 
++           */
++          if (((tmp = q->q_state) & E3_QUEUE_FULL) == 0)
++              q->q_fptr = nfptr;                              /* update queue */
++          else
++          {
++              q->q_fptr = nfptr;                              /* update queue */
++              q->q_state = tmp &~E3_QUEUE_FULL;               /* and clear full flag */
++          }
++
++          count++;                                            /* bump message count */
++          if (nfptr == q->q_top)                              /* queue wrap */
++              nfptr = q->q_base;
++          else
++              nfptr += q->q_size;
++
++          c_break_busywait();                                 /* be nice              HALT POINT */
++
++      } while (nfptr != q->q_bptr);                           /* loop until Fptr == Bptr */
++    }
++}
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/ep/epcomms_elan4.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/epcomms_elan4.c     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/epcomms_elan4.c  2005-06-01 23:12:54.640432872 -0400
+@@ -0,0 +1,392 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: epcomms_elan4.c,v 1.11.2.1 2004/10/28 11:53:28 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/epcomms_elan4.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++#include <elan/epsvc.h>
++#include <elan/epcomms.h>
++
++#include "debug.h"
++#include "kcomm_elan4.h"
++#include "epcomms_elan4.h"
++
++static void
++ep4comms_flush_interrupt (EP4_RAIL *rail, void *arg)
++{
++    EP4_COMMS_RAIL *commsRail = (EP4_COMMS_RAIL *) arg;
++    unsigned long  flags;
++
++    spin_lock_irqsave (&commsRail->r_flush_lock, flags);
++    commsRail->r_flush_count = 0;
++    kcondvar_wakeupall (&commsRail->r_flush_sleep, &commsRail->r_flush_lock);
++    spin_unlock_irqrestore  (&commsRail->r_flush_lock, flags);
++}
++
++void
++ep4comms_flush_start (EP4_COMMS_RAIL *commsRail)
++{
++    kmutex_lock (&commsRail->r_flush_mutex);
++}
++
++void
++ep4comms_flush_wait (EP4_COMMS_RAIL *commsRail)
++{
++    unsigned long flags;
++
++    ep4_wait_event_cmd (commsRail->r_flush_mcq, 
++                      commsRail->r_elan_addr + offsetof (EP4_COMMS_RAIL_ELAN, r_flush_event),
++                      E4_EVENT_INIT_VALUE (-32 * commsRail->r_flush_count, E4_EVENT_WRITE, E4_EVENT_DTYPE_LONG, 0),
++                      commsRail->r_flush_ecq->ecq_addr, 
++                      INTERRUPT_CMD | (commsRail->r_flush_intcookie.int_val << E4_MAIN_INT_SHIFT));
++
++    spin_lock_irqsave (&commsRail->r_flush_lock, flags);
++    while (commsRail->r_flush_count != 0)
++      kcondvar_wait (&commsRail->r_flush_sleep, &commsRail->r_flush_lock, &flags);
++    spin_unlock_irqrestore (&commsRail->r_flush_lock, flags);
++    
++    kmutex_unlock (&commsRail->r_flush_mutex);
++}
++
++void
++ep4comms_flush_setevent (EP4_COMMS_RAIL *commsRail, ELAN4_CQ *cq)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&commsRail->r_flush_lock, flags);
++
++    elan4_set_event_cmd (cq, commsRail->r_elan_addr + offsetof (EP4_COMMS_RAIL_ELAN, r_flush_event));
++    
++    commsRail->r_flush_count++;
++    
++    spin_unlock_irqrestore (&commsRail->r_flush_lock, flags);
++}
++
++void
++ep4comms_flush_callback (void *arg, statemap_t *map)
++{
++    EP4_COMMS_RAIL   *commsRail = (EP4_COMMS_RAIL *) arg;
++    EP_COMMS_SUBSYS  *subsys    = commsRail->r_generic.Subsys;
++    EP4_RAIL       *rail      = (EP4_RAIL *) commsRail->r_generic.Rail;
++    unsigned int     rnum       = rail->r_generic.Number;
++    struct list_head *el;
++
++    /*
++     * We stall the retry thread from CB_FLUSH_FILTERING until
++     * we've finished CB_FLUSH_FLUSHING to ensure that sten 
++     * packets can not be being retried while we flush them
++     * through.
++     */
++    switch (rail->r_generic.CallbackStep)
++    {
++    case EP_CB_FLUSH_FILTERING:
++      ep_kthread_stall (&rail->r_retry_thread);
++
++      ep4comms_flush_start (commsRail);
++      break;
++
++    case EP_CB_FLUSH_FLUSHING:
++      break;
++    }
++
++    kmutex_lock (&subsys->Lock);
++    list_for_each (el, &subsys->Transmitters) {
++      EP_XMTR *xmtr = list_entry (el, EP_XMTR, Link);
++      
++      if (xmtr->Rails[rnum])
++          ep4xmtr_flush_callback (xmtr, (EP4_XMTR_RAIL *) xmtr->Rails[rnum]);
++    }
++
++    list_for_each (el, &subsys->Receivers) {
++      EP_RCVR *rcvr = list_entry (el, EP_RCVR, Link);
++      
++      if (rcvr->Rails[rnum])
++          ep4rcvr_flush_callback (rcvr, (EP4_RCVR_RAIL *) rcvr->Rails[rnum]);
++    }
++    kmutex_unlock (&subsys->Lock);
++
++    switch (rail->r_generic.CallbackStep)
++    {
++    case EP_CB_FLUSH_FILTERING:
++      ep4comms_flush_wait (commsRail);
++      break;
++
++    case EP_CB_FLUSH_FLUSHING:
++      ep_kthread_resume (&rail->r_retry_thread);
++      break;
++    }
++}
++
++void
++ep4comms_failover_callback (void *arg, statemap_t *map)
++{
++    EP_COMMS_RAIL    *commsRail = (EP_COMMS_RAIL *) arg;
++    EP_COMMS_SUBSYS  *subsys    = commsRail->Subsys;
++    unsigned int     rnum       = commsRail->Rail->Number;
++    struct list_head *el;
++
++    kmutex_lock (&subsys->Lock);
++    list_for_each (el, &subsys->Transmitters) {
++      EP_XMTR *xmtr = list_entry (el, EP_XMTR, Link);
++      
++      if (xmtr->Rails[rnum])
++          ep4xmtr_failover_callback (xmtr, (EP4_XMTR_RAIL *) xmtr->Rails[rnum]);
++    }
++
++    list_for_each (el, &subsys->Receivers) {
++      EP_RCVR *rcvr = list_entry (el, EP_RCVR, Link);
++      
++      if (rcvr->Rails[rnum])
++          ep4rcvr_failover_callback (rcvr, (EP4_RCVR_RAIL *) rcvr->Rails[rnum]);
++    }
++    kmutex_unlock (&subsys->Lock);
++}
++
++void
++ep4comms_disconnect_callback (void *arg, statemap_t *map)
++{
++    EP_COMMS_RAIL    *commsRail = (EP_COMMS_RAIL *) arg;
++    EP_COMMS_SUBSYS  *subsys    = commsRail->Subsys;
++    unsigned int     rnum       = commsRail->Rail->Number;
++    struct list_head *el;
++
++    kmutex_lock (&subsys->Lock);
++    list_for_each (el, &subsys->Transmitters) {
++      EP_XMTR *xmtr = list_entry (el, EP_XMTR, Link);
++      
++      if (xmtr->Rails[rnum])
++          ep4xmtr_disconnect_callback (xmtr, (EP4_XMTR_RAIL *) xmtr->Rails[rnum]);
++    }
++
++    list_for_each (el, &subsys->Receivers) {
++      EP_RCVR *rcvr = list_entry (el, EP_RCVR, Link);
++      
++      if (rcvr->Rails[rnum])
++          ep4rcvr_disconnect_callback (rcvr, (EP4_RCVR_RAIL *) rcvr->Rails[rnum]);
++    }
++    kmutex_unlock (&subsys->Lock);
++}
++
++void
++ep4comms_neterr_callback (EP4_RAIL *rail, void *arg, unsigned int nodeId, EP_NETERR_COOKIE *cookies)
++{
++    EP_COMMS_RAIL    *commsRail = (EP_COMMS_RAIL *) arg;
++    EP_COMMS_SUBSYS  *subsys    = commsRail->Subsys;
++    unsigned int     rnum       = commsRail->Rail->Number;
++    struct list_head *el;
++    
++    /* First - stall the retry thread, so that it will no longer restart 
++     *         any sten packets from the retry lists */
++    ep_kthread_stall (&rail->r_retry_thread);
++
++    ep4comms_flush_start ((EP4_COMMS_RAIL *) commsRail);
++
++    /* Second - flush through all command queues for xmtrs and rcvrs */
++    kmutex_lock (&subsys->Lock);
++    list_for_each (el, &subsys->Transmitters) {
++      EP_XMTR *xmtr = list_entry (el, EP_XMTR, Link);
++      
++      if (xmtr->Rails[rnum])
++          ep4xmtr_neterr_flush (xmtr, (EP4_XMTR_RAIL *) xmtr->Rails[rnum], nodeId, cookies);
++    }
++    
++    list_for_each (el, &subsys->Receivers) {
++      EP_RCVR *rcvr = list_entry (el, EP_RCVR, Link);
++      
++      if (rcvr->Rails[rnum])
++          ep4rcvr_neterr_flush (rcvr, (EP4_RCVR_RAIL *) rcvr->Rails[rnum], nodeId, cookies);
++    }
++    kmutex_unlock (&subsys->Lock);
++
++    /* Third - wait for flush to complete */
++    ep4comms_flush_wait ((EP4_COMMS_RAIL *) commsRail);
++    
++    /* Fourth - flush through all command queues */
++    ep4_flush_ecqs (rail);
++    
++    /* Fifth - search all the retry lists for the network error cookies */
++    kmutex_lock (&subsys->Lock);
++    list_for_each (el, &subsys->Transmitters) {
++      EP_XMTR *xmtr = list_entry (el, EP_XMTR, Link);
++      
++      if (xmtr->Rails[rnum])
++          ep4xmtr_neterr_check (xmtr, (EP4_XMTR_RAIL *) xmtr->Rails[rnum], nodeId, cookies);
++    }
++
++    list_for_each (el, &subsys->Receivers) {
++      EP_RCVR *rcvr = list_entry (el, EP_RCVR, Link);
++      
++      if (rcvr->Rails[rnum])
++          ep4rcvr_neterr_check (rcvr, (EP4_RCVR_RAIL *) rcvr->Rails[rnum], nodeId, cookies);
++    }
++    kmutex_unlock (&subsys->Lock);
++
++    ep_kthread_resume (&rail->r_retry_thread);
++}
++
++
++EP_COMMS_RAIL *
++ep4comms_add_rail (EP_SUBSYS *s, EP_SYS *sys, EP_RAIL *r)
++{
++    EP4_RAIL       *rail = (EP4_RAIL *)r;
++    ELAN4_DEV      *dev  = rail->r_ctxt.ctxt_dev;
++    EP4_COMMS_RAIL *commsRail;
++    E4_InputQueue   qdesc;
++    int i;
++
++    KMEM_ZALLOC (commsRail, EP4_COMMS_RAIL *,sizeof (EP4_COMMS_RAIL), 1);
++
++    if (commsRail == NULL)
++      return NULL;
++    
++    commsRail->r_generic.Ops.DelRail          = ep4comms_del_rail;
++    commsRail->r_generic.Ops.DisplayRail      = ep4comms_display_rail;
++    commsRail->r_generic.Ops.Rcvr.AddRail     = ep4rcvr_add_rail;
++    commsRail->r_generic.Ops.Rcvr.DelRail     = ep4rcvr_del_rail;
++    commsRail->r_generic.Ops.Rcvr.Check       = ep4rcvr_check;
++    commsRail->r_generic.Ops.Rcvr.QueueRxd    = ep4rcvr_queue_rxd;
++    commsRail->r_generic.Ops.Rcvr.RpcPut      = ep4rcvr_rpc_put;
++    commsRail->r_generic.Ops.Rcvr.RpcGet      = ep4rcvr_rpc_get;
++    commsRail->r_generic.Ops.Rcvr.RpcComplete = ep4rcvr_rpc_complete;
++
++    commsRail->r_generic.Ops.Rcvr.StealRxd    = ep4rcvr_steal_rxd;
++
++    commsRail->r_generic.Ops.Rcvr.DisplayRcvr = ep4rcvr_display_rcvr;
++    commsRail->r_generic.Ops.Rcvr.DisplayRxd  = ep4rcvr_display_rxd;
++
++    commsRail->r_generic.Ops.Rcvr.FillOutRailStats = ep4rcvr_fillout_rail_stats;
++
++    commsRail->r_generic.Ops.Xmtr.AddRail     = ep4xmtr_add_rail;
++    commsRail->r_generic.Ops.Xmtr.DelRail     = ep4xmtr_del_rail;
++    commsRail->r_generic.Ops.Xmtr.Check       = ep4xmtr_check;
++    commsRail->r_generic.Ops.Xmtr.BindTxd     = ep4xmtr_bind_txd;
++    commsRail->r_generic.Ops.Xmtr.UnbindTxd   = ep4xmtr_unbind_txd;
++    commsRail->r_generic.Ops.Xmtr.PollTxd     = ep4xmtr_poll_txd;
++    commsRail->r_generic.Ops.Xmtr.CheckTxdState = ep4xmtr_check_txd_state;
++
++    commsRail->r_generic.Ops.Xmtr.DisplayXmtr = ep4xmtr_display_xmtr;
++    commsRail->r_generic.Ops.Xmtr.DisplayTxd  = ep4xmtr_display_txd;
++
++    commsRail->r_generic.Ops.Xmtr.FillOutRailStats = ep4xmtr_fillout_rail_stats;
++
++    /* Allocate command queue space for flushing (1 dword for interrupt + 4 dwords for waitevent) */
++    if ((commsRail->r_flush_ecq = ep4_get_ecq (rail, EP4_ECQ_EVENT, 1)) == NULL)
++    {
++      KMEM_FREE (commsRail, sizeof (EP4_COMMS_RAIL));
++      return NULL;
++    }
++
++    if ((commsRail->r_flush_mcq = ep4_get_ecq (rail, EP4_ECQ_MAIN, 4)) == NULL)
++    {
++      ep4_put_ecq (rail, commsRail->r_flush_ecq, 1);
++      KMEM_FREE (commsRail, sizeof (EP4_COMMS_RAIL));
++      return NULL;
++    }
++
++    /* Allocate and initialise the elan memory part */
++    if ((commsRail->r_elan = ep_alloc_elan (r, EP4_COMMS_RAIL_ELAN_SIZE, 0, &commsRail->r_elan_addr)) == (sdramaddr_t) 0)
++    {
++      ep4_put_ecq (rail, commsRail->r_flush_mcq, 4);
++      ep4_put_ecq (rail, commsRail->r_flush_ecq, 1);
++      KMEM_FREE (commsRail, sizeof (EP4_COMMS_RAIL));
++      return NULL;
++    }
++
++    ep4_register_intcookie (rail, &commsRail->r_flush_intcookie, commsRail->r_elan_addr + offsetof (EP4_COMMS_RAIL_ELAN, r_flush_event),
++                          ep4comms_flush_interrupt, commsRail);
++
++    elan4_sdram_writeq (dev, commsRail->r_elan + offsetof (EP4_COMMS_RAIL_ELAN, r_flush_event.ev_CountAndType),
++                      E4_EVENT_INIT_VALUE (0, E4_EVENT_WRITE, E4_EVENT_DTYPE_LONG, 0));
++
++
++    /* Allocate and initialise all the queue desriptors as "full" with no event */
++    if ((commsRail->r_descs = ep_alloc_memory_elan (r, EP_EPCOMMS_QUEUE_BASE, roundup (EP_MSG_NSVC * EP_QUEUE_DESC_SIZE, SDRAM_PAGE_SIZE), EP_PERM_ALL, 0)) == (sdramaddr_t) 0)
++    {
++      ep_free_elan (r, commsRail->r_elan_addr, EP4_COMMS_RAIL_ELAN_SIZE);
++      ep4_put_ecq (rail, commsRail->r_flush_mcq, 4);
++      ep4_put_ecq (rail, commsRail->r_flush_ecq, 1);
++      KMEM_FREE (commsRail, sizeof (EP4_COMMS_RAIL));
++      return NULL;
++    }
++
++    qdesc.q_bptr    = 0;
++    qdesc.q_fptr    = 8;
++    qdesc.q_control = E4_InputQueueControl (qdesc.q_bptr,qdesc.q_fptr, 8);
++    qdesc.q_event   = 0;
++
++    for (i = 0; i < EP_MSG_NSVC; i++)
++      elan4_sdram_copyq_to_sdram (rail->r_ctxt.ctxt_dev, &qdesc, commsRail->r_descs + (i * EP_QUEUE_DESC_SIZE),
++                                  sizeof (E4_InputQueue));
++
++    kmutex_init (&commsRail->r_flush_mutex);
++    spin_lock_init (&commsRail->r_flush_lock);
++    kcondvar_init (&commsRail->r_flush_sleep);
++
++    ep_register_callback (r, EP_CB_FLUSH_FILTERING, ep4comms_flush_callback,      commsRail);
++    ep_register_callback (r, EP_CB_FLUSH_FLUSHING,  ep4comms_flush_callback,      commsRail);
++    ep_register_callback (r, EP_CB_FAILOVER,        ep4comms_failover_callback,   commsRail);
++    ep_register_callback (r, EP_CB_DISCONNECTING,   ep4comms_disconnect_callback, commsRail);
++
++    commsRail->r_neterr_ops.op_func = ep4comms_neterr_callback;
++    commsRail->r_neterr_ops.op_arg  = commsRail;
++    
++    ep4_add_neterr_ops (rail, &commsRail->r_neterr_ops);
++
++    return (EP_COMMS_RAIL *) commsRail;
++}
++
++void
++ep4comms_del_rail (EP_COMMS_RAIL *r)
++{
++    EP4_COMMS_RAIL *commsRail = (EP4_COMMS_RAIL *) r;
++    EP4_RAIL       *rail      = (EP4_RAIL *) commsRail->r_generic.Rail;
++
++    ep_remove_callback (&rail->r_generic, EP_CB_FLUSH_FILTERING, ep4comms_flush_callback,      commsRail);
++    ep_remove_callback (&rail->r_generic, EP_CB_FLUSH_FLUSHING,  ep4comms_flush_callback,      commsRail);
++    ep_remove_callback (&rail->r_generic, EP_CB_FAILOVER,        ep4comms_failover_callback,   commsRail);
++    ep_remove_callback (&rail->r_generic, EP_CB_DISCONNECTING,   ep4comms_disconnect_callback, commsRail);
++
++    kcondvar_destroy (&commsRail->r_flush_sleep);
++    spin_lock_destroy (&commsRail->r_flush_lock);
++    kmutex_destroy (&commsRail->r_flush_mutex);
++
++    ep_free_memory_elan (&rail->r_generic, EP_EPCOMMS_QUEUE_BASE);
++    ep_free_elan (&rail->r_generic, commsRail->r_elan_addr, EP4_COMMS_RAIL_ELAN_SIZE);
++
++    ep4_deregister_intcookie (rail, &commsRail->r_flush_intcookie);
++
++    ep4_put_ecq (rail, commsRail->r_flush_mcq, 4);
++    ep4_put_ecq (rail, commsRail->r_flush_ecq, 1);
++
++    KMEM_FREE (commsRail, sizeof (EP4_COMMS_RAIL));
++}
++
++void
++ep4comms_display_rail (EP_COMMS_RAIL *r)
++{
++    EP4_COMMS_RAIL *commsRail = (EP4_COMMS_RAIL *) r;
++    EP4_RAIL       *rail      = (EP4_RAIL *) commsRail->r_generic.Rail;
++    ELAN4_DEV      *dev       = rail->r_ctxt.ctxt_dev;
++    
++    ep4_display_rail (rail);
++
++    ep_debugf (DBG_DEBUG, "   flush count=%d mcq=%p ecq=%p event %llx.%llx.%llx\n", 
++             commsRail->r_flush_count, commsRail->r_flush_mcq, commsRail->r_flush_ecq,
++             elan4_sdram_readq (dev, commsRail->r_elan + offsetof (EP4_COMMS_RAIL_ELAN, r_flush_event.ev_CountAndType)),
++             elan4_sdram_readq (dev, commsRail->r_elan + offsetof (EP4_COMMS_RAIL_ELAN, r_flush_event.ev_WritePtr)),
++             elan4_sdram_readq (dev, commsRail->r_elan + offsetof (EP4_COMMS_RAIL_ELAN, r_flush_event.ev_WriteValue)));
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/ep/epcomms_elan4.h
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/epcomms_elan4.h     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/epcomms_elan4.h  2005-06-01 23:12:54.641432720 -0400
+@@ -0,0 +1,470 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __EPCOMMS_ELAN4_H
++#define __EPCOMMS_ELAN4_H
++
++#ident "@(#)$Id: epcomms_elan4.h,v 1.13.2.1 2004/11/12 10:54:51 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/epcomms_elan4.h,v $ */
++
++
++#include <elan4/types.h>
++
++/*
++ * Elan4 spinlocks are a pair of 64 bit words, one in elan sdram and one in main memory
++ * the sdram word holds the thread sequence number in the bottom 32 bits and the main
++ * lock in the top 32 bits.  The main memory word holds the sequence number only in
++ * it's bottom 32 bits */
++
++typedef volatile E4_uint64 EP4_SPINLOCK_MAIN;
++typedef volatile E4_uint64 EP4_SPINLOCK_ELAN;
++
++#define EP4_SPINLOCK_SEQ      0
++#define EP4_SPINLOCK_MLOCK    4
++
++#if defined(__elan4__)
++
++#define EP4_SPINENTER(CPORT,SLE,SLM) \
++do { \
++    register long tmp; \
++\
++    asm volatile ("ld4                [%1], %0\n" \
++                "inc          %0\n" \
++                "st4          %0, [%1]\n" \
++                "ld4          [%1 + 4], %0\n" \
++                "srl8,byte    %0, 4, %0\n" \
++                : /* outputs */ "=r" (tmp)  \
++                : /* inputs */ "r" (SLE), "r" (SLM)); \
++\
++    if (tmp) \
++      ep4_spinblock (CPORT,SLE, SLM); \
++} while (0)
++
++extern void ep4_spinblock(E4_uint64 *cport, EP4_SPINLOCK_ELAN *sle, EP4_SPINLOCK_MAIN *slm);
++
++#define EP4_SPINEXIT(CPORT,SLE,SLM) \
++do { \
++    register long tmp; \
++\
++    asm volatile ("ld4                [%1], %0\n" \
++                "st4          %0, [%2]\n" \
++                : /* outputs */ "=r" (tmp) \
++                : /* inputs */ "r" (SLE), "r" (SLM)); \
++} while (0)
++
++#else
++
++#define EP4_SPINENTER(DEV,SLE,SLM) \
++do { \
++    uint32_t seq; \
++\
++    mb(); \
++    elan4_sdram_writel (DEV, (SLE) + EP4_SPINLOCK_MLOCK, 1); \
++    mb(); \
++    while ((seq = elan4_sdram_readl (DEV, (SLE) + EP4_SPINLOCK_SEQ)) != *((uint32_t *) (SLM))) \
++    { \
++      while (*((uint32_t *) (SLM)) == (seq - 1)) \
++      { \
++          mb(); \
++          DELAY(1); \
++      } \
++    } \
++} while (0)
++
++#define EP4_SPINEXIT(DEV,SLE,SLM) \
++do { \
++    wmb(); \
++    elan4_sdram_writel (DEV, (SLE) + EP4_SPINLOCK_MLOCK, 0); \
++} while (0)
++
++#endif /* !defined(__elan4__) */
++
++#define EP4_STEN_RETRYCOUNT   16
++#define EP4_DMA_RETRYCOUNT    16
++
++typedef struct ep4_intr_cmd
++{
++    E4_uint64         c_write_cmd;
++    E4_uint64         c_write_value;
++    E4_uint64         c_intr_cmd;
++} EP4_INTR_CMD;
++
++#define       EP4_INTR_CMD_NDWORDS    (sizeof (EP4_INTR_CMD) / 8)
++
++typedef struct ep4_rxd_sten_cmd
++{
++    E4_uint64         c_open;
++
++    E4_uint64         c_trans;
++    E4_uint64         c_cookie;
++    E4_uint64         c_dma_typeSize;
++    E4_uint64         c_dma_cookie;
++    E4_uint64         c_dma_vproc;
++    E4_uint64         c_dma_srcAddr;
++    E4_uint64         c_dma_dstAddr;
++    E4_uint64         c_dma_srcEvent;
++    E4_uint64         c_dma_dstEvent;
++
++    E4_uint64         c_ok_guard;
++    E4_uint64         c_ok_write_cmd;
++    E4_uint64         c_ok_write_value;
++    
++    E4_uint64         c_fail_guard;
++    E4_uint64         c_fail_setevent;
++
++    E4_uint64         c_nop_cmd;
++} EP4_RXD_STEN_CMD;
++
++#define EP4_RXD_STEN_CMD_NDWORDS      (sizeof (EP4_RXD_STEN_CMD) / 8)
++
++typedef struct ep4_rxd_dma_cmd
++{
++    E4_uint64         c_dma_typeSize;
++    E4_uint64         c_dma_cookie;
++    E4_uint64         c_dma_vproc;
++    E4_uint64         c_dma_srcAddr;
++    E4_uint64         c_dma_dstAddr;
++    E4_uint64         c_dma_srcEvent;
++    E4_uint64         c_dma_dstEvent;
++    E4_uint64         c_nop_cmd;
++} EP4_RXD_DMA_CMD;
++
++#define EP4_RXD_DMA_CMD_NDWORDS               (sizeof (EP4_RXD_DMA_CMD) / 8)
++#define EP4_RXD_START_CMD_NDWORDS     (sizeof (E4_ThreadRegs) / 8)
++
++typedef struct ep4_rxd_rail_elan
++{
++    EP4_RXD_STEN_CMD    rxd_sten[EP_MAXFRAG+1];
++
++    EP4_INTR_CMD      rxd_done_cmd;                           /* command stream issued by done event (aligned to 64 bytes) */
++    E4_Addr           rxd_next;                               /* linked list when on pending list (pad to 32 bytes)*/
++    E4_Event32                rxd_failed;                             /* event set when sten packet fails */
++
++    EP4_INTR_CMD        rxd_failed_cmd;                               /* command stream issued by fail event (aligned to 64 bytes) */
++    E4_uint64         rxd_queued;                             /* rxd queuing thread has executed (pad to 32 bytes)*/
++
++    E4_Event32                rxd_start;                              /* event to set to fire off and event chain (used as chain[0]) */
++    E4_Event32                rxd_chain[EP_MAXFRAG];                  /* chained events (aligned to 32 bytes) */
++    E4_Event32                rxd_done;                               /* event to fire done command stream causing interrupt (used as chain[EP_MAXFRAG]) */
++
++    E4_Addr           rxd_rxd;                                /* elan address of EP4_RXD_MAIN */
++    E4_Addr           rxd_main;                               /* elan address of EP4_RXD_RAIL_MAIN */
++    E4_uint64         rxd_debug;                              /* thread debug value */
++
++    EP_NMD            rxd_buffer;                             /* Network mapping descriptor for receive data */
++} EP4_RXD_RAIL_ELAN;
++
++#define EP4_RXD_RAIL_ELAN_SIZE        roundup(sizeof (EP4_RXD_RAIL_ELAN), 64)
++
++typedef struct ep4_rxd_rail_main
++{
++    E4_uint64         rxd_sent[EP_MAXFRAG+1];                 /* sten packet sent */
++    E4_uint64         rxd_failed;                             /* sten packet failed */
++    E4_uint64         rxd_done;                               /* operation complete */
++
++    E4_Addr           rxd_scq;                                /* command port for scq */
++} EP4_RXD_RAIL_MAIN;
++
++#define EP4_RXD_RAIL_MAIN_SIZE        roundup(sizeof (EP4_RXD_RAIL_MAIN), 8)
++
++#if !defined(__elan4__)
++typedef struct ep4_rxd_rail
++{
++    EP_RXD_RAIL               rxd_generic;
++
++    struct list_head    rxd_retry_link;
++    unsigned long       rxd_retry_time;
++
++    EP4_INTCOOKIE     rxd_intcookie;
++
++    sdramaddr_t               rxd_elan;
++    EP_ADDR           rxd_elan_addr;
++    
++    EP4_RXD_RAIL_MAIN  *rxd_main;
++    EP_ADDR           rxd_main_addr;
++
++    EP4_ECQ          *rxd_ecq;                                /* cq with 128 bytes targetted by event */
++    EP4_ECQ          *rxd_scq;                                /* cq with 8 bytes targetted by main/thread store */
++} EP4_RXD_RAIL;
++
++#define EP4_NUM_RXD_PER_BLOCK 16
++
++typedef struct ep4_rxd_rail_block
++{
++    struct list_head  blk_link;
++    EP4_RXD_RAIL      blk_rxds[EP4_NUM_RXD_PER_BLOCK];
++} EP4_RXD_RAIL_BLOCK;
++
++#endif /* !defined(__elan4__) */
++
++typedef struct ep4_rcvr_rail_elan
++{
++    E4_uint64         rcvr_thread_stall[8];                   /* place for thread to stall */
++    E4_Event32                rcvr_qevent;                            /* Input queue event */
++    E4_Event32                rcvr_thread_halt;                       /* place for thread to halt */
++
++    volatile E4_Addr    rcvr_pending_tailp;                   /* list of pending rxd's (elan addr) */
++    volatile E4_Addr  rcvr_pending_head;                      /*   -- this pair aligned to 16 bytes */
++
++    EP4_SPINLOCK_ELAN rcvr_thread_lock;                       /* spinlock for thread processing loop */
++
++    E4_uint64         rcvr_stall_intcookie;                   /* interrupt cookie to use when requseted to halt */
++
++    E4_uint64         rcvr_qbase;                             /* base of input queue */
++    E4_uint64         rcvr_qlast;                             /* last item in input queue */
++
++    E4_uint64         rcvr_debug;                             /* thread debug value */
++} EP4_RCVR_RAIL_ELAN;
++
++typedef struct ep4_rcvr_rail_main
++{
++    EP4_SPINLOCK_MAIN   rcvr_thread_lock;                     /* spinlock for thread processing loop */
++} EP4_RCVR_RAIL_MAIN;
++
++#if !defined(__elan4__)
++
++typedef struct ep4_rcvr_rail_stats
++{
++    unsigned long some_stat;
++} EP4_RCVR_RAIL_STATS;
++
++typedef struct ep4_rcvr_rail
++{
++    EP_RCVR_RAIL      rcvr_generic;                           /* generic portion */
++    
++    sdramaddr_t               rcvr_elan;
++    EP_ADDR           rcvr_elan_addr;
++
++    EP4_RCVR_RAIL_MAIN *rcvr_main;
++    EP_ADDR           rcvr_main_addr;
++
++    sdramaddr_t               rcvr_slots;                             /* input queue slots */
++    EP_ADDR           rcvr_slots_addr;                        /*   and elan address */
++
++    EP_ADDR           rcvr_stack;                             /* stack for thread */
++
++    EP4_ECQ          *rcvr_ecq;                               /* command queue space for thread STEN packets */
++    EP4_ECQ          *rcvr_resched;                           /* command queue space to reschedule the thread */
++
++    struct list_head    rcvr_freelist;                                /* freelist of per-rail receive descriptors */
++    unsigned int        rcvr_freecount;                               /*   and number on free list */
++    unsigned int        rcvr_totalcount;                              /*   total number created */
++    spinlock_t          rcvr_freelock;                                /*   and lock for free list */
++    struct list_head    rcvr_blocklist;                               /* list of receive descriptor blocks */
++
++    unsigned int        rcvr_freewaiting;                     /* waiting for descriptors to be freed */
++    kcondvar_t                rcvr_freesleep;                         /*   and sleep here */
++
++    EP4_INTCOOKIE     rcvr_stall_intcookie;                   /* interrupt cookie for thread halt */
++    unsigned char     rcvr_thread_halted;                     /* thread has been halted */
++    unsigned char       rcvr_cleanup_waiting;                 /* waiting for cleanup */
++    kcondvar_t          rcvr_cleanup_sleep;                   /*   and sleep here */
++
++    EP4_RETRY_OPS     rcvr_retryops;
++
++    struct list_head    rcvr_retrylist;                               /* list of txd's to retry envelopes for */
++    struct list_head    rcvr_polllist;                                /* list of txd's to poll for completion */
++    spinlock_t          rcvr_retrylock;
++    
++    EP4_RCVR_RAIL_STATS rcvr_stats;                             /* elan4 specific rcvr_rail stats */
++
++} EP4_RCVR_RAIL;
++
++#endif /* !defined(__elan4__) */
++
++typedef struct ep4_txd_rail_elan
++{
++    EP4_INTR_CMD        txd_env_cmd;                          /* command stream for envelope event (64 byte aligned) */
++    E4_uint64         txd_pad0;                               /*  pad to 32 bytes */
++    E4_Event32                txd_env;                                /* event set when STEN packet fails */
++
++    EP4_INTR_CMD      txd_done_cmd;                           /* command stream for done event (64 byte aligned) */
++    E4_uint64         txd_pad1;                               /*  pad to 32 bytes */
++    E4_Event32                txd_done;                               /* event set when transmit complete */
++
++    E4_Event32                txd_data;                               /* event set when xmit completes (=> phase becomes passive) */
++} EP4_TXD_RAIL_ELAN;
++
++#define EP4_TXD_RAIL_ELAN_SIZE                roundup(sizeof(EP4_TXD_RAIL_ELAN), 64)
++
++typedef struct ep4_txd_rail_main
++{
++    E4_uint64         txd_env;
++    E4_uint64         txd_data;
++    E4_uint64         txd_done;
++} EP4_TXD_RAIL_MAIN;
++
++#define EP4_TXD_RAIL_MAIN_SIZE                roundup(sizeof(EP4_TXD_RAIL_MAIN), 8)
++
++#if !defined (__elan4__)
++typedef struct ep4_txd_rail
++{
++    EP_TXD_RAIL               txd_generic;
++
++    struct list_head    txd_retry_link;
++    unsigned long     txd_retry_time;
++
++    EP4_INTCOOKIE     txd_intcookie;
++
++    sdramaddr_t               txd_elan;
++    EP_ADDR           txd_elan_addr;
++    
++    EP4_TXD_RAIL_MAIN  *txd_main;
++    EP_ADDR           txd_main_addr;
++
++    EP4_ECQ          *txd_ecq;
++
++    E4_uint64         txd_cookie;
++} EP4_TXD_RAIL;
++
++#define EP4_NUM_TXD_PER_BLOCK 21
++
++typedef struct ep4_txd_rail_block
++{
++    struct list_head  blk_link;
++    EP4_TXD_RAIL      blk_txds[EP4_NUM_TXD_PER_BLOCK];
++} EP4_TXD_RAIL_BLOCK;
++
++typedef struct ep4_xmtr_rail_main
++{
++    E4_int64          xmtr_flowcnt;
++} EP4_XMTR_RAIL_MAIN;
++
++typedef struct ep4_xmtr_rail_stats
++{
++    unsigned long some_stat;
++} EP4_XMTR_RAIL_STATS;
++
++#define EP4_TXD_LIST_POLL     0
++#define EP4_TXD_LIST_STALLED  1
++#define EP4_TXD_LIST_RETRY    2
++#define EP4_TXD_NUM_LISTS     3
++typedef struct ep4_xmtr_rail
++{
++    EP_XMTR_RAIL      xmtr_generic;
++
++    EP4_XMTR_RAIL_MAIN *xmtr_main;
++    EP_ADDR           xmtr_main_addr;
++
++    struct list_head    xmtr_freelist;
++    unsigned int        xmtr_freecount;
++    unsigned int        xmtr_totalcount;
++    spinlock_t          xmtr_freelock;
++    struct list_head    xmtr_blocklist;
++    unsigned int        xmtr_freewaiting;
++    kcondvar_t                xmtr_freesleep;
++
++    EP4_INTCOOKIE     xmtr_intcookie;                         /* interrupt cookie for "polled" descriptors */
++
++    ELAN4_CQ           *xmtr_cq;
++    E4_int64          xmtr_flowcnt;
++
++    EP4_RETRY_OPS     xmtr_retryops;
++
++    struct list_head    xmtr_retrylist[EP4_TXD_NUM_LISTS];    /* list of txd's to retry envelopes for */
++    struct list_head    xmtr_polllist;                                /* list of txd's to poll for completion */
++    spinlock_t          xmtr_retrylock;
++
++    EP4_XMTR_RAIL_STATS stats;                                  /* elan4 specific xmtr rail stats */
++} EP4_XMTR_RAIL;
++
++#define EP4_XMTR_CQSIZE               CQ_Size64K                              /* size of command queue for xmtr */
++#define EP4_XMTR_FLOWCNT      (CQ_Size(EP4_XMTR_CQSIZE) / 512)        /* # of STEN packets which can fit in */
++
++typedef struct ep4_comms_rail_elan
++{
++    E4_Event32                r_flush_event;
++} EP4_COMMS_RAIL_ELAN;
++
++#define EP4_COMMS_RAIL_ELAN_SIZE      roundup(sizeof (EP4_COMMS_RAIL_ELAN), 32)
++
++typedef struct ep4_comms_rail
++{
++    EP_COMMS_RAIL     r_generic;                              /* generic comms rail */
++    sdramaddr_t               r_descs;                                /* input queue descriptors */
++
++    sdramaddr_t               r_elan;                                 /* elan portion */
++    EP_ADDR           r_elan_addr;
++
++    kmutex_t          r_flush_mutex;                          /* sequentialise flush usage */
++    EP4_INTCOOKIE       r_flush_intcookie;                    /* interrupt cookie to generate */
++
++    kcondvar_t                r_flush_sleep;                          /* place to sleep waiting */
++    spinlock_t                r_flush_lock;                           /*   and spinlock to use */
++
++    unsigned int      r_flush_count;                          /* # setevents issued */
++    EP4_ECQ          *r_flush_ecq;                            /* command queue for interrupt */
++    EP4_ECQ          *r_flush_mcq;                            /* command queeu to issue waitevent */
++
++    EP4_NETERR_OPS      r_neterr_ops;                         /* network error fixup ops */
++} EP4_COMMS_RAIL;
++
++/* epcommsTx_elan4.c */
++extern void           ep4xmtr_flush_callback (EP_XMTR *xmtr, EP4_XMTR_RAIL *xmtrRail);
++extern void           ep4xmtr_failover_callback (EP_XMTR *xmtr, EP4_XMTR_RAIL *xmtrRail);
++extern void           ep4xmtr_disconnect_callback (EP_XMTR *xmtr, EP4_XMTR_RAIL *xmtrRail);
++
++extern void         ep4xmtr_neterr_flush (EP_XMTR *xmtr, EP4_XMTR_RAIL *xmtrRail, unsigned int nodeId, EP_NETERR_COOKIE *cookies);
++extern void         ep4xmtr_neterr_check (EP_XMTR *xmtr, EP4_XMTR_RAIL *xmtrRail, unsigned int nodeId, EP_NETERR_COOKIE *cookies);
++
++/* epcommsRx_elan4.c */
++extern void           ep4rcvr_flush_callback (EP_RCVR *rcvr, EP4_RCVR_RAIL *rcvrRail);
++extern void           ep4rcvr_failover_callback (EP_RCVR *rcvr, EP4_RCVR_RAIL *rcvrRail);
++extern void           ep4rcvr_disconnect_callback (EP_RCVR *rcvr, EP4_RCVR_RAIL *rcvrRail);
++
++extern void         ep4rcvr_neterr_flush (EP_RCVR *rcvr, EP4_RCVR_RAIL *rcvrRail, unsigned int nodeId, EP_NETERR_COOKIE *cookies);
++extern void         ep4rcvr_neterr_check (EP_RCVR *rcvr, EP4_RCVR_RAIL *rcvrRail, unsigned int nodeId, EP_NETERR_COOKIE *cookies);
++
++/* epcomms_elan4.c */
++extern void           ep4comms_flush_start (EP4_COMMS_RAIL *commsRail);
++extern void           ep4comms_flush_wait (EP4_COMMS_RAIL *commsRail);
++extern void           ep4comms_flush_setevent (EP4_COMMS_RAIL *commsRail, ELAN4_CQ *cq);
++
++extern EP_COMMS_RAIL *ep4comms_add_rail (EP_SUBSYS *s, EP_SYS *sys, EP_RAIL *r);
++extern void           ep4comms_del_rail (EP_COMMS_RAIL *r);
++extern void         ep4comms_display_rail (EP_COMMS_RAIL *r);
++
++/* epcommsTx_elan4.c */
++extern int            ep4xmtr_bind_txd (EP_TXD *txd, EP_XMTR_RAIL *xmtrRail, unsigned int phase);
++extern void           ep4xmtr_unbind_txd (EP_TXD *txd, unsigned int phase);
++extern int            ep4xmtr_poll_txd (EP_XMTR_RAIL *xmtrRail, EP_TXD_RAIL *txdRail, int how);
++extern long           ep4xmtr_check (EP_XMTR_RAIL *xmtrRail, long nextRunTime);
++extern void           ep4xmtr_add_rail (EP_XMTR *xmtr, EP_COMMS_RAIL *commsRail);
++extern void           ep4xmtr_del_rail (EP_XMTR *xmtr, EP_COMMS_RAIL *commsRail);
++extern int            ep4xmtr_check_txd_state(EP_TXD *txd);
++
++extern void           ep4xmtr_display_xmtr (DisplayInfo *di, EP_XMTR_RAIL *xmtrRail);
++extern void           ep4xmtr_display_txd  (DisplayInfo *di, EP_TXD_RAIL *txdRail);
++
++extern void           ep4xmtr_fillout_rail_stats (EP_XMTR_RAIL *xmtr_rail, char *str);
++
++/* epcommsRx_elan4.c */
++extern int          ep4rcvr_queue_rxd (EP_RXD *rxd, EP_RCVR_RAIL *rcvrRail);
++extern void         ep4rcvr_rpc_put (EP_RXD *rxd, EP_NMD *local, EP_NMD *remote, unsigned nFrags);
++extern void         ep4rcvr_rpc_get (EP_RXD *rxd, EP_NMD *local, EP_NMD *remote, unsigned nFrags);
++extern void         ep4rcvr_rpc_complete (EP_RXD *rxd, EP_NMD *local, EP_NMD *remote, unsigned nFrags);
++
++extern EP_RXD      *ep4rcvr_steal_rxd (EP_RCVR_RAIL *rcvrRail);
++
++extern long         ep4rcvr_check (EP_RCVR_RAIL *rcvrRail, long nextRunTime);
++extern void           ep4rcvr_add_rail (EP_RCVR *rcvr, EP_COMMS_RAIL *rail);
++extern void           ep4rcvr_del_rail (EP_RCVR *rcvr, EP_COMMS_RAIL *rail);
++
++extern void           ep4rcvr_display_rcvr (DisplayInfo *di, EP_RCVR_RAIL *rcvrRail);
++extern void           ep4rcvr_display_rxd  (DisplayInfo *di, EP_RXD_RAIL *rxdRail);
++
++extern void           ep4rcvr_fillout_rail_stats (EP_RCVR_RAIL *rcvr_rail, char *str);
++
++#endif /* !defined(__elan4__) */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++#endif /* __EPCOMMS_ELAN4_H */
+Index: linux-2.4.21/drivers/net/qsnet/ep/epcomms_elan4_thread.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/epcomms_elan4_thread.c      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/epcomms_elan4_thread.c   2005-06-01 23:12:54.642432568 -0400
+@@ -0,0 +1,346 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: epcomms_elan4_thread.c,v 1.10.8.2 2004/09/28 10:36:51 david Exp $ $Name: QSNETMODULES-4-30_20050128 $"
++/*      $Source: /cvs/master/quadrics/epmod/epcomms_elan4_thread.c,v $*/
++
++//#include <qsnet/types.h>
++
++typedef char           int8_t;
++typedef unsigned char  uint8_t;
++typedef short          int16_t;
++typedef unsigned short uint16_t;
++typedef int            int32_t;
++typedef unsigned int   uint32_t;
++typedef long           int64_t;
++typedef unsigned long  uint64_t;
++
++#include <elan/nmh.h>
++#include <elan/kcomm.h>
++#include <elan/epcomms.h>
++
++#include <elan4/registers.h>
++
++#include "kcomm_vp.h"
++#include "kcomm_elan4.h"
++#include "epcomms_elan4.h"
++
++#include <elan4/trtype.h>
++
++/* assembler in epcomms_asm_elan4_thread.S */
++extern void               c_waitevent_interrupt (E4_uint64 *cport, E4_Event32 *event, E4_uint64 count, E4_uint64 intcookie);
++extern EP4_RXD_RAIL_ELAN *c_stall_thread (EP4_RCVR_RAIL_ELAN *rcvrRail);
++
++#define R32_to_R47            "%r32", "%r33", "%r34", "%r35", "%r36", "%r37", "%r38", "%r39", \
++                              "%r40", "%r41", "%r42", "%r43", "%r44", "%r45", "%r46", "%r47"
++#define R48_to_R63            "%r48", "%r49", "%r50", "%r51", "%r52", "%r53", "%r54", "%r55", \
++                              "%r56", "%r57", "%r58", "%r59", "%r60", "%r61", "%r62", "%r63"
++
++/* proto types for code in asm_elan4_thread.S */
++extern void c_waitevent (E4_uint64 *commandport, E4_Addr event, E4_uint64 count);
++extern void c_reschedule(E4_uint64 *commandport);
++
++static inline unsigned long
++c_load_u16(unsigned short *ptr)
++{
++    unsigned long value;
++
++    asm volatile ("ld2                [%1], %%r2\n"
++                "srl8,byte    %%r2, %1, %0\n"
++                "sll8         %0, 48, %0\n"
++                "srl8         %0, 48, %0\n"
++                : /* outputs */ "=r" (value) 
++                : /* inputs */ "r" (ptr)
++                : /* clobbered */ "%r2");
++    return value;
++}
++
++static inline unsigned long
++c_load_u32(unsigned int *ptr)
++{
++    unsigned long value;
++
++    asm volatile ("ld4                [%1], %%r2\n"
++                "srl8,byte    %%r2, %1, %0\n"
++                "sll8         %0, 32, %0\n"
++                "srl8         %0, 32, %0\n"
++                : /* outputs */ "=r" (value) 
++                : /* inputs */ "r" (ptr)
++                : /* clobbered */ "%r2");
++    return value;
++}
++
++static inline void
++c_store_u32(unsigned int *ptr, unsigned long value)
++{
++    asm volatile ("sll8,byte  %0, %1, %%r2\n"
++                "st4          %%r2, [%1]\n"
++                : /* no outputs */ 
++                : /* inputs */ "r" (value), "r" (ptr)
++                : /* clobbered */ "%r2");
++}
++
++/* Reschedule the current Elan thread to the back of the run queue 
++ * if there is another one ready to run */
++static inline void
++c_yield (E4_uint64 *commandport)
++{
++    unsigned long rval;
++
++    asm volatile ("breaktest %0" : /* outputs */ "=r" (rval) : /* inputs */);
++
++    if (rval  & ICC_SIGNED_BIT)
++      c_reschedule(commandport);
++}
++
++/* Reschedule the current thread if we're in danger of exceeding the 
++ * thread instruction count */
++static inline void
++c_insn_check(E4_uint64 *commandport)
++{
++    unsigned long rval;
++
++    asm volatile ("breaktest %0" : /* outputs */ "=r" (rval) : /* inputs */);
++
++    if (rval & ICC_ZERO_BIT)
++      c_reschedule(commandport);
++}
++
++void
++ep4_spinblock (E4_uint64 *cport, EP4_SPINLOCK_ELAN *sle, EP4_SPINLOCK_MAIN *slm)
++{
++    do {
++      unsigned long val = *sle & 0xfffffffff;
++
++      *slm = val;                                     /* Release my lock */
++      
++      while (*sle >> 32)                              /* Wait until the main */
++          c_yield(cport);                             /* releases the lock */
++      
++      c_store_u32 ((unsigned int *) sle, val + 1);    /* and try and relock */
++    } while (*sle >> 32);
++}
++
++#define RESCHED_AFTER_PKTS    ((CQ_Size(CQ_Size64K) / 128) - 1)
++
++void
++ep4comms_rcvr (EP4_RAIL_ELAN *rail, EP4_RCVR_RAIL_ELAN *rcvrElan, EP4_RCVR_RAIL_MAIN *rcvrMain,
++             E4_InputQueue *inputq, E4_uint64 *cport, E4_uint64 *resched)
++{
++    long count = 1;
++    long fptr  = inputq->q_fptr;
++
++    for (;;)
++    {
++      c_waitevent (cport, inputq->q_event, -count << 5);
++
++      count = 0;
++
++      while (fptr != inputq->q_bptr)
++      {
++          EP_ENVELOPE        *env      = (EP_ENVELOPE *) fptr;
++          unsigned long       nodeid   = c_load_u32 (&env->NodeId);
++          unsigned long       opencmd  = OPEN_STEN_PKT_CMD | OPEN_PACKET(0, PACK_OK | RESTART_COUNT_ZERO, EP_VP_DATA(nodeid));
++          unsigned long       vproc    = EP_VP_DATA(rail->r_nodeid);
++          EP_ATTRIBUTE        attr     = c_load_u32 (&env->Attr);
++          unsigned long       txdRail  = c_load_u32 (&env->TxdRail);
++          unsigned long       nFrags   = c_load_u32 (&env->nFrags);
++          E4_uint64           cookie   = rail->r_cookies[nodeid];
++          unsigned long       srcevent = (EP_IS_RPC(attr) ? txdRail + offsetof (EP4_TXD_RAIL_ELAN, txd_data) :
++                                          txdRail + offsetof (EP4_TXD_RAIL_ELAN, txd_done));
++          EP4_RXD_RAIL_ELAN  *rxdElan;
++          EP4_RXD_RAIL_MAIN  *rxdMain;
++          EP_RXD_MAIN        *rxd;
++          EP4_RXD_STEN_CMD   *sten;
++          E4_Event32         *event;
++          unsigned long       first;
++          unsigned long       buffer;
++          unsigned long       len;
++          unsigned long       i;
++
++          EP4_SPINENTER(resched, &rcvrElan->rcvr_thread_lock, &rcvrMain->rcvr_thread_lock);
++
++          if ((rxdElan = (EP4_RXD_RAIL_ELAN *) rcvrElan->rcvr_pending_head) == 0)
++          {
++              EP4_SPINEXIT (resched, &rcvrElan->rcvr_thread_lock, &rcvrMain->rcvr_thread_lock);
++
++              rxdElan = c_stall_thread (rcvrElan);
++
++              EP4_SPINENTER(resched, &rcvrElan->rcvr_thread_lock, &rcvrMain->rcvr_thread_lock);
++          }
++          
++          if (c_load_u32 (&env->Version) != EP_ENVELOPE_VERSION)              /* envelope has been cancelled */
++          {
++              EP4_SPINEXIT (resched, &rcvrElan->rcvr_thread_lock, &rcvrMain->rcvr_thread_lock);
++              goto consume_envelope;
++          }
++
++          rxd     = (EP_RXD_MAIN *) rxdElan->rxd_rxd;
++          rxdMain = (EP4_RXD_RAIL_MAIN *) rxdElan->rxd_main;
++          first   = (EP_MAXFRAG+1) - (( EP_IS_MULTICAST(attr) ? 1 : 0) + (nFrags == 0 ? 1 : nFrags));
++          sten    = &rxdElan->rxd_sten[first];
++          event   = &rxdElan->rxd_chain[first];
++
++          if (EP_IS_MULTICAST(attr))                          /* need to fetch broadcast bitmap */
++          {
++              sten->c_open          = opencmd;
++              sten->c_trans         = SEND_TRANS_CMD | ((TR_REMOTEDMA | TR_WAIT_FOR_EOP) << 16);
++              sten->c_cookie        = cookie | EP4_COOKIE_THREAD | EP4_COOKIE_STEN;
++              sten->c_dma_typeSize  = E4_DMA_TYPE_SIZE(BT_BITOUL(EP_MAX_NODES) * sizeof (bitmap_t), DMA_DataTypeWord, 0, EP4_DMA_RETRYCOUNT);
++              sten->c_dma_cookie    = cookie | EP4_COOKIE_THREAD | EP4_COOKIE_REMOTE | EP4_COOKIE_DMA | EP4_COOKIE_INC;
++              sten->c_dma_vproc     = vproc;
++              sten->c_dma_srcAddr   = c_load_u32 (&env->TxdMain.nmd_addr) + offsetof(EP_TXD_MAIN, Bitmap);
++              sten->c_dma_dstAddr   = (E4_Addr) &rxd->Bitmap;
++              sten->c_dma_srcEvent  = srcevent;
++              sten->c_dma_dstEvent  = (E4_Addr) event;
++
++              event->ev_CountAndType = E4_EVENT_INIT_VALUE(-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_RXD_STEN_CMD_NDWORDS);
++
++              cookie += (EP4_COOKIE_INC << 1);
++
++              sten++; event++;
++          }
++
++          if (nFrags == 0)
++          {
++              /* Generate an empty "get" DMA to accept the envelope and fire the rx handler */
++              sten->c_open          = opencmd;
++              sten->c_trans         = SEND_TRANS_CMD | ((TR_REMOTEDMA | TR_WAIT_FOR_EOP) << 16);
++              sten->c_cookie        = cookie | EP4_COOKIE_THREAD | EP4_COOKIE_STEN;
++              sten->c_dma_typeSize  = E4_DMA_TYPE_SIZE(0, DMA_DataTypeByte, 0, EP4_DMA_RETRYCOUNT);
++              sten->c_dma_cookie    = cookie | EP4_COOKIE_THREAD | EP4_COOKIE_REMOTE | EP4_COOKIE_DMA | EP4_COOKIE_INC;
++              sten->c_dma_vproc     = vproc;
++              sten->c_dma_srcEvent  = srcevent;
++              sten->c_dma_dstEvent  = (E4_Addr) event;
++
++              event->ev_CountAndType = E4_EVENT_INIT_VALUE(-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS);
++
++              len = 0;
++
++              cookie += (EP4_COOKIE_INC << 1);
++          }
++          else
++          {
++              /* Generate the DMA chain to fetch the data */
++              for (i = 0, buffer = c_load_u32 (&rxdElan->rxd_buffer.nmd_addr), len = 0; i < nFrags; i++)
++              {
++                  unsigned long fragLen = c_load_u32 (&env->Frags[i].nmd_len);
++
++                  sten->c_open          = opencmd;
++                  sten->c_trans         = SEND_TRANS_CMD | ((TR_REMOTEDMA | TR_WAIT_FOR_EOP) << 16);
++                  sten->c_cookie        = cookie | EP4_COOKIE_THREAD | EP4_COOKIE_STEN;
++                  sten->c_dma_typeSize  = E4_DMA_TYPE_SIZE(fragLen, DMA_DataTypeByte, 0, EP4_DMA_RETRYCOUNT);
++                  sten->c_dma_cookie    = cookie | EP4_COOKIE_THREAD | EP4_COOKIE_REMOTE | EP4_COOKIE_DMA | EP4_COOKIE_INC;
++                  sten->c_dma_vproc     = vproc;
++                  sten->c_dma_srcAddr   = c_load_u32 (&env->Frags[i].nmd_addr);
++                  sten->c_dma_dstAddr   = buffer;
++                  sten->c_dma_srcEvent  = srcevent;
++                  sten->c_dma_dstEvent  = (E4_Addr) event;
++                  
++                  event->ev_CountAndType = E4_EVENT_INIT_VALUE(-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_RXD_STEN_CMD_NDWORDS);
++                  
++                  buffer += fragLen;
++                  len    += fragLen;
++
++                  cookie += (EP4_COOKIE_INC << 1);
++
++                  sten++; event++;
++              }
++              
++              (--event)->ev_CountAndType = E4_EVENT_INIT_VALUE(-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS);
++
++              if (c_load_u32 (&rxdElan->rxd_buffer.nmd_len) < len)
++              {
++                  /* The receive descriptor was too small for the message */
++                  /* complete the message anyway,  but don't transfer any */
++                  /* data,  we set the length to EP_MSG_TOO_BIG */
++                  for (i = first, sten = &rxdElan->rxd_sten[first]; i <= EP_MAXFRAG; i++, sten++)
++                      sten->c_dma_typeSize = E4_DMA_TYPE_SIZE(0, DMA_DataTypeByte, 0, EP4_DMA_RETRYCOUNT);
++                  
++                  len = EP_MSG_TOO_BIG;
++              }
++          }
++          
++          /* Stuff the first STEN packet into the command queue, there's always enough space, 
++           * since we will insert a waitevent at least once for the queue size */
++          asm volatile ("ld64         [%0], %%r32\n"
++                        "ld64         [%0 + 64], %%r48\n"
++                        "st64         %%r32, [%1]\n"
++                        "st64         %%r48, [%1]\n"
++                        : /* no outputs */
++                        : /* inputs */ "r" (&rxdElan->rxd_sten[first]), "r" (cport)
++                        : /* clobbered */ R32_to_R47, R48_to_R63);
++
++          /* remove the RXD from the pending list */
++          if ((rcvrElan->rcvr_pending_head = rxdElan->rxd_next) == 0)
++              rcvrElan->rcvr_pending_tailp = (E4_Addr)&rcvrElan->rcvr_pending_head;
++
++          /* mark as not queued */
++          rxdElan->rxd_queued = 0;
++
++          /* copy down the envelope */
++          if (EP_HAS_PAYLOAD(attr))
++              asm volatile ("ld64     [%0],    %%r32\n"
++                            "ld64     [%0+64], %%r48\n"
++                            "st64     %%r32, [%1]\n"
++                            "ld64     [%0+128], %%r32\n"
++                            "st64     %%r48, [%1+64]\n"
++                            "ld64     [%0+192], %%r48\n"
++                            "st64     %%r32, [%1 + 128]\n"
++                            "st64     %%r48, [%1 + 192]\n"
++                            : /* no outputs */
++                            : /* inputs */    "r" (env), "r" (&rxd->Envelope)
++                            : /* clobbered */ R32_to_R47, R48_to_R63);
++ 
++          else
++              asm volatile ("ld64     [%0],    %%r32\n"
++                            "ld64     [%0+64], %%r48\n"
++                            "st64     %%r32, [%1]\n"
++                            "st64     %%r48, [%1+64]\n"
++                            : /* no outputs */
++                            : /* inputs */    "r" (env), "r" (&rxd->Envelope)
++                            : /* clobbered */ R32_to_R47, R48_to_R63);
++
++          /* Store the message length to indicate that I've finished */
++          c_store_u32 (&rxd->Len, len);
++          
++          /* Finally update the network error cookie */
++          rail->r_cookies[nodeid] = cookie;
++
++          EP4_SPINEXIT (resched, &rcvrElan->rcvr_thread_lock, &rcvrMain->rcvr_thread_lock);
++
++      consume_envelope:
++          if (fptr != rcvrElan->rcvr_qlast)
++              fptr += EP_INPUTQ_SIZE;
++          else
++              fptr = rcvrElan->rcvr_qbase;
++
++          if (! rcvrElan->rcvr_stall_intcookie)
++              inputq->q_fptr = fptr;
++
++          if (++count >= RESCHED_AFTER_PKTS)
++              break;
++
++          c_insn_check (cport);
++      }
++      
++      if (rcvrElan->rcvr_stall_intcookie)
++      {
++          c_waitevent_interrupt (cport, &rcvrElan->rcvr_thread_halt, -(1 << 5), rcvrElan->rcvr_stall_intcookie);
++          inputq->q_fptr = fptr;
++
++          count++;                                            /* one extra as we were given an extra set to wake us up */
++      }
++    }
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/ep/epcommsFwd.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/epcommsFwd.c        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/epcommsFwd.c     2005-06-01 23:12:54.643432416 -0400
+@@ -0,0 +1,310 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: epcommsFwd.c,v 1.12 2004/08/16 12:21:15 david Exp $ $Name: QSNETMODULES-4-30_20050128 $"
++/*      $Source: /cvs/master/quadrics/epmod/epcommsFwd.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++#include <elan/epsvc.h>
++#include <elan/epcomms.h>
++
++#include "debug.h"
++
++unsigned int epcomms_forward_limit = 8;
++
++static void
++GenerateTree (unsigned nodeId, unsigned lowId, unsigned  highId, bitmap_t *bitmap, 
++            unsigned *parentp, unsigned *childrenp, int *nchildrenp)
++{
++    int i;
++    int count;
++    int branch;
++    int nSub;
++    int branchIndex;
++    int parent;
++    int nBranch;
++    int rem;
++    int self;
++    int branchRatio;
++    int node;
++    int x, y, z;
++
++
++#ifdef DEBUG_PRINTF
++    {
++#define OVERFLOW "...]"
++#define LINESZ         128
++      char space[LINESZ+1];
++
++      if (ep_sprintf_bitmap (space, LINESZ-strlen(OVERFLOW), bitmap, 0, 0, (highId - lowId)+1) != -1)
++          strcat (space, OVERFLOW);
++
++      EPRINTF3 (DBG_FORWARD, "GenerateTree; elan node low=%d node high=%d bitmap=%s\n", lowId, highId, space);
++#undef OVERFLOW
++#undef LINESZ
++    }
++#endif
++
++    /* Count the number of nodes in the partition */
++    /* and work out which one I am */
++    for (count = 0, self = ELAN_INVALID_NODE, i = lowId; i <= highId; i++)
++    {
++      if (BT_TEST (bitmap, i-lowId))
++      {
++          if (i == nodeId)
++              self = count;
++          count++;
++      }
++    }
++
++    EPRINTF2 (DBG_FORWARD, "GenerateTree: count=%d self=%d\n", count, self);
++
++    if (count == 0 || self == ELAN_INVALID_NODE)
++    {
++      *parentp    = ELAN_INVALID_NODE;
++      *nchildrenp = 0;
++      return;
++    }
++
++    /* search for position in tree */
++    branchRatio = EP_TREE_ARITY;              /* branching ratio */
++    branch      = 0;                          /* start with process 0 */
++    nSub        = count;                      /* and whole tree */
++    branchIndex = -1;                         /* my branch # in parent */
++    parent      = -1;                         /* my parent's group index # */
++    
++    while (branch != self)                    /* descend process tree */
++    {                                         /* until I find myself */
++      parent = branch;
++      branch++;                               /* parent + 1 = first born */
++      nSub--;                                 /* set # descendents */
++      
++      rem  = nSub % branchRatio;
++      nSub = nSub / branchRatio + 1;
++      x = rem * nSub;
++      y = self - branch;
++      
++      if (y < x)                              /* my first 'rem' branches have */
++      {                                       /* 1 more descendent... */
++          branchIndex = y / nSub;
++          branch += branchIndex * nSub;
++      }
++      else                                    /* than the rest of my branches */
++      {
++          nSub--;
++          z = (y - x) / nSub;
++          branchIndex = rem + z;
++          branch += x + z * nSub;
++      }
++    }
++
++    branch++;                                 /* my first born */
++    nSub--;                                   /* total # of my descendents */
++    /* leaves + their parents may have # children < branchRatio */
++    nBranch = (nSub < branchRatio) ? nSub : branchRatio;      
++
++    EPRINTF2 (DBG_FORWARD, "GenerateTree: parent=%d nBranch=%d\n", parent, nBranch);
++
++    /* Now calculate the real elan id's of the parent and my children */
++    if (parent == -1)
++      *parentp = ELAN_INVALID_NODE;
++    else
++    {
++      for (i = lowId, node = 0; i <= highId; i++)
++      {
++          if (BT_TEST(bitmap, i-lowId))
++              if (node++ == parent)
++                  break;
++      }
++      *parentp = i;
++    }
++
++    for (i = lowId, branchIndex = 0, node = 0; branchIndex < nBranch && i <= highId; i++)
++    {
++      if (BT_TEST(bitmap, i-lowId))
++      {
++          if (node == branch)
++          {
++              branch = branch + nSub / branchRatio + ((branchIndex < (nSub % branchRatio)) ? 1 : 0);
++
++              childrenp[branchIndex++] = i;
++          }
++          node++;
++      }
++    }
++
++    *nchildrenp = branchIndex;
++}
++
++static void
++ForwardTxDone (EP_TXD *txd, void *arg, EP_STATUS status)
++{
++    EP_FWD_DESC     *desc   = (EP_FWD_DESC *) arg;
++    EP_RXD          *rxd    = desc->Rxd;
++    EP_COMMS_SUBSYS *subsys = rxd->Rcvr->Subsys;
++    unsigned long    flags;
++
++    /* XXXX: if transmit fails, could step to next node in this subtree ? */
++
++    spin_lock_irqsave (&subsys->ForwardDescLock, flags);
++
++    if (--desc->NumChildren > 0)
++      spin_unlock_irqrestore (&subsys->ForwardDescLock, flags);
++    else
++    {
++      rxd->Rcvr->ForwardRxdCount--;
++
++      spin_unlock_irqrestore (&subsys->ForwardDescLock, flags);
++
++      KMEM_FREE (desc, sizeof (EP_FWD_DESC));
++
++      rxd->Handler (rxd);
++    }
++}
++
++long
++ep_forward_rxds (EP_COMMS_SUBSYS *subsys, long nextRunTime)
++{
++    unsigned long flags;
++    int i, res;
++
++    spin_lock_irqsave (&subsys->ForwardDescLock, flags);
++    while (! list_empty (&subsys->ForwardDescList)) 
++    {
++      EP_RXD      *rxd     = (EP_RXD *) list_entry (subsys->ForwardDescList.next, EP_RXD, Link);
++      EP_RXD_MAIN *rxdMain = rxd->RxdMain;
++      EP_ENVELOPE *env     = &rxdMain->Envelope;
++      EP_FWD_DESC *desc;
++
++      EPRINTF2 (DBG_FORWARD, "ep: forwarding rxd %p to range %x\n", rxd, env->Range);
++
++      list_del (&rxd->Link);
++
++      rxd->Rcvr->ForwardRxdCount++;
++
++      spin_unlock_irqrestore (&subsys->ForwardDescLock, flags);
++
++      KMEM_ALLOC (desc, EP_FWD_DESC *, sizeof (EP_FWD_DESC), 1);
++
++      if (desc == NULL)
++      {
++          spin_lock_irqsave (&subsys->ForwardDescLock, flags);
++          rxd->Rcvr->ForwardRxdCount--;
++          spin_unlock_irqrestore (&subsys->ForwardDescLock, flags);
++
++          rxd->Handler (rxd);
++      }
++      else
++      {
++          /* compute the spanning tree for this message */
++          unsigned int destLo = EP_RANGE_LOW (env->Range);
++          unsigned int destHi = EP_RANGE_HIGH (env->Range);
++          unsigned int parent;
++
++          GenerateTree (subsys->Subsys.Sys->Position.pos_nodeid, destLo, destHi, rxdMain->Bitmap, &parent, desc->Children, &desc->NumChildren);
++          
++          if (desc->NumChildren == 0 || (epcomms_forward_limit && (rxd->Rcvr->ForwardRxdCount >= epcomms_forward_limit)))
++          {
++              EPRINTF5 (DBG_FORWARD, "ep; don't forward rxd %p to /%d (%d children/ %d forwarding (%d))\n",
++                        rxd, rxd->Rcvr->Service, desc->NumChildren, rxd->Rcvr->ForwardRxdCount, epcomms_forward_limit);
++
++              spin_lock_irqsave (&subsys->ForwardDescLock, flags);
++              rxd->Rcvr->ForwardRxdCount--;
++              spin_unlock_irqrestore (&subsys->ForwardDescLock, flags);
++
++              KMEM_FREE (desc, sizeof (EP_FWD_DESC));
++              
++              rxd->Handler (rxd);
++          }
++          else
++          {
++              ep_nmd_subset (&desc->Data, &rxd->Data, 0, ep_rxd_len (rxd));
++              desc->Rxd = rxd;
++
++              /* NOTE - cannot access 'desc' after last call to multicast, since it could complete
++               *        and free the desc before we access it again.  Hence the reverse loop. */
++              for (i = desc->NumChildren-1; i >= 0; i--)
++              {
++                  ASSERT (desc->Children[i] < subsys->Subsys.Sys->Position.pos_nodes);
++
++                  EPRINTF3 (DBG_FORWARD, "ep: forwarding rxd %p to node %d/%d\n", rxd, desc->Children[i], rxd->Rcvr->Service);
++
++                  if ((res = ep_multicast_forward (subsys->ForwardXmtr, desc->Children[i], rxd->Rcvr->Service, 0, 
++                                                   ForwardTxDone, desc, env, EP_HAS_PAYLOAD(env->Attr) ? &rxdMain->Payload : NULL,  
++                                                   rxdMain->Bitmap, &desc->Data, 1)) != EP_SUCCESS)
++                  {
++                      ep_debugf (DBG_FORWARD, "ep: ep_multicast_forward failed\n");
++                      ForwardTxDone (NULL, desc, res);
++                  }
++              }
++              
++          }
++      }
++
++      spin_lock_irqsave (&subsys->ForwardDescLock, flags);
++    }
++    spin_unlock_irqrestore (&subsys->ForwardDescLock, flags);
++
++    return (nextRunTime);
++}
++
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++void
++ep_csum_rxds (EP_COMMS_SUBSYS *subsys)
++{
++    unsigned long flags;
++ 
++    spin_lock_irqsave (&subsys->CheckSumDescLock, flags);
++    while (! list_empty (&subsys->CheckSumDescList)) 
++    {
++      EP_RXD      *rxd = (EP_RXD *) list_entry (subsys->CheckSumDescList.next, EP_RXD, CheckSumLink);
++      EP_ENVELOPE *env = &rxd->RxdMain->Envelope;
++
++      list_del_init (&rxd->CheckSumLink);
++      spin_unlock_irqrestore (&subsys->CheckSumDescLock, flags);
++
++      if (env->CheckSum) {
++          EP_NMD nmd;
++          uint32_t csum; 
++
++          ep_nmd_subset ( &nmd, &rxd->Data, 0, ep_rxd_len (rxd));
++
++          csum = ep_calc_check_sum(subsys->Subsys.Sys, env, &nmd, 1);
++          if ( env->CheckSum  != csum ) {
++              int f;
++      
++
++              printk("Check Sum Error: env(0x%x,0x%x) data(0x%x,0x%x)\n", ((csum >> 16) & 0x7FFF), ((env->CheckSum >> 16) & 0x7FFF), 
++                     (csum & 0xFFFF),  (env->CheckSum & 0xFFFF));
++              printk("Check Sum Error: Sent : NodeId %u Range 0x%x Service %u Version 0x%x Attr 0x%x\n", env->NodeId, env->Range, rxd->Rcvr->Service, env->Version, env->Attr);
++              printk("Check Sum Error: Sent : Xid Generation 0x%x Handle 0x%x Unique 0x%llx\n", env->Xid.Generation, env->Xid.Handle, env->Xid.Unique);
++              printk("Check Sum Error: Sent : TxdRail 0x%x TxdMain nmd_addr 0x%x  nmd_len %u  nmd_attr 0x%x\n",  env->TxdRail, env->TxdMain.nmd_addr, env->TxdMain.nmd_len, env->TxdMain.nmd_attr ); 
++              printk("Check Sum Error: Sent : nFrags %d \n", env->nFrags);
++              for(f=0;f<env->nFrags;f++)
++                  printk("Check Sum Error: Sent (%d): nmd_addr 0x%x   nmd_len %u   nmd_attr 0x%x\n", f,
++                         env->Frags[f].nmd_addr, env->Frags[f].nmd_len, env->Frags[f].nmd_attr);
++              printk("Check Sum Error: Recv : nmd_addr 0x%x   nmd_len %u   nmd_attr 0x%x\n",
++                     nmd.nmd_addr, nmd.nmd_len, nmd.nmd_attr);
++
++          }
++      }
++      ep_rxd_received_now(rxd);
++
++      spin_lock_irqsave (&subsys->CheckSumDescLock, flags);
++    }
++    spin_unlock_irqrestore (&subsys->CheckSumDescLock, flags);
++}
++#endif
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/ep/epcommsRx.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/epcommsRx.c 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/epcommsRx.c      2005-06-01 23:12:54.645432112 -0400
+@@ -0,0 +1,1205 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: epcommsRx.c,v 1.27.2.5 2004/11/30 12:02:16 mike Exp $ $Name: QSNETMODULES-4-30_20050128 $"
++/*      $Source: /cvs/master/quadrics/epmod/epcommsRx.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <elan/kcomm.h>
++#include <elan/epsvc.h>
++#include <elan/epcomms.h>
++
++#include "debug.h"
++
++unsigned int ep_rxd_lowat = 5;
++
++static int
++AllocateRxdBlock (EP_RCVR *rcvr, EP_ATTRIBUTE attr, EP_RXD **rxdp)
++{
++    EP_RXD_BLOCK *blk;
++    EP_RXD       *rxd;
++    EP_RXD_MAIN  *pRxdMain;
++    int                 i;
++    unsigned long flags;
++
++    KMEM_ZALLOC (blk, EP_RXD_BLOCK *, sizeof (EP_RXD_BLOCK), ! (attr & EP_NO_SLEEP));
++
++    if (blk == NULL)
++      return (ENOMEM);
++
++    if ((pRxdMain = ep_shared_alloc_main (rcvr->Subsys->Subsys.Sys, EP_RXD_MAIN_SIZE * EP_NUM_RXD_PER_BLOCK, attr, &blk->NmdMain)) == (sdramaddr_t) 0)
++    {
++      KMEM_FREE (blk, sizeof (EP_RXD_BLOCK));
++      return (ENOMEM);
++    }
++    
++    for (rxd = &blk->Rxd[0], i = 0; i < EP_NUM_RXD_PER_BLOCK; i++, rxd++)
++    {
++      rxd->Rcvr        = rcvr;
++      rxd->RxdMain     = pRxdMain;
++
++      ep_nmd_subset (&rxd->NmdMain, &blk->NmdMain, (i * EP_RXD_MAIN_SIZE), EP_RXD_MAIN_SIZE);
++
++      /* move onto next descriptor */
++      pRxdMain = (EP_RXD_MAIN *) ((unsigned long) pRxdMain + EP_RXD_MAIN_SIZE);
++    }
++
++    spin_lock_irqsave (&rcvr->FreeDescLock, flags);
++
++    list_add  (&blk->Link, &rcvr->DescBlockList);
++
++    rcvr->TotalDescCount += EP_NUM_RXD_PER_BLOCK;
++
++    for (i = rxdp ? 1 : 0; i < EP_NUM_RXD_PER_BLOCK; i++)
++    {
++      
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++      INIT_LIST_HEAD (&blk->Rxd[i].CheckSumLink);
++#endif
++
++      list_add (&blk->Rxd[i].Link, &rcvr->FreeDescList);
++      
++      rcvr->FreeDescCount++;
++
++      if (rcvr->FreeDescWanted)
++      {
++          rcvr->FreeDescWanted--;
++          kcondvar_wakeupone (&rcvr->FreeDescSleep, &rcvr->FreeDescLock);
++      }
++    }
++    spin_unlock_irqrestore (&rcvr->FreeDescLock, flags);
++    
++    if (rxdp)
++    {
++
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++      INIT_LIST_HEAD (&blk->Rxd[0].CheckSumLink);
++#endif
++             
++      *rxdp = &blk->Rxd[0];
++    }
++    return (ESUCCESS);
++}
++
++static void
++FreeRxdBlock (EP_RCVR *rcvr, EP_RXD_BLOCK *blk)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&rcvr->FreeDescLock, flags);
++
++    list_del (&blk->Link);
++
++    rcvr->TotalDescCount -= EP_NUM_RXD_PER_BLOCK;
++    rcvr->FreeDescCount -= EP_NUM_RXD_PER_BLOCK;
++
++    spin_unlock_irqrestore (&rcvr->FreeDescLock, flags);
++
++    ep_shared_free_main (rcvr->Subsys->Subsys.Sys, &blk->NmdMain);
++    KMEM_FREE (blk, sizeof (EP_RXD_BLOCK));
++}
++
++static EP_RXD *
++GetRxd (EP_RCVR *rcvr, EP_ATTRIBUTE attr)
++{
++    EP_RXD *rxd;
++    unsigned long flags;
++    int low_on_rxds;
++
++    spin_lock_irqsave (&rcvr->FreeDescLock, flags);
++
++    while (list_empty (&rcvr->FreeDescList))
++    {
++      if (! (attr & EP_NO_ALLOC))
++      {
++          spin_unlock_irqrestore (&rcvr->FreeDescLock, flags);
++
++          if (AllocateRxdBlock (rcvr, attr, &rxd) == ESUCCESS)
++              return (rxd);
++
++          spin_lock_irqsave (&rcvr->FreeDescLock, flags);
++      }
++
++      if (attr & EP_NO_SLEEP)
++      {
++          IncrStat (rcvr->Subsys, NoFreeRxds);
++          spin_unlock_irqrestore (&rcvr->FreeDescLock, flags);
++
++          ep_kthread_schedule (&rcvr->Subsys->Thread, lbolt);
++          return (NULL);
++      }
++
++      rcvr->FreeDescWanted++;
++      kcondvar_wait (&rcvr->FreeDescSleep, &rcvr->FreeDescLock, &flags);
++    }
++
++    rxd = list_entry (rcvr->FreeDescList.next, EP_RXD, Link);
++
++    list_del (&rxd->Link);
++
++    /* Wakeup the descriptor primer thread if there's not many left */
++    low_on_rxds = (--rcvr->FreeDescCount < ep_rxd_lowat);
++
++    spin_unlock_irqrestore (&rcvr->FreeDescLock, flags);
++
++    if (low_on_rxds)
++      ep_kthread_schedule (&rcvr->Subsys->Thread, lbolt);
++
++    return (rxd);
++}
++
++static void
++FreeRxd (EP_RCVR *rcvr, EP_RXD *rxd)
++{
++    unsigned long flags;
++
++    ASSERT (EP_XID_INVALID(rxd->MsgXid));
++
++    spin_lock_irqsave (&rcvr->FreeDescLock, flags);
++
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++    ASSERT(list_empty(&rxd->CheckSumLink));
++#endif
++   
++    list_add (&rxd->Link, &rcvr->FreeDescList);
++
++    rcvr->FreeDescCount++;
++
++    if (rcvr->FreeDescWanted)                                 /* someone waiting for a receive */
++    {                                                         /* descriptor, so wake them up */
++      rcvr->FreeDescWanted--;
++      kcondvar_wakeupone (&rcvr->FreeDescSleep, &rcvr->FreeDescLock);
++    }
++    
++    spin_unlock_irqrestore (&rcvr->FreeDescLock, flags);
++}
++
++int
++ep_queue_receive (EP_RCVR *rcvr, EP_RXH *handler, void *arg, EP_NMD *nmd, EP_ATTRIBUTE attr)
++{
++    EP_RCVR_RAIL *rcvrRail;
++    EP_RXD       *rxd;
++    int           rnum;
++    unsigned long flags;
++
++    if ((rxd = GetRxd (rcvr, attr)) == NULL)
++      return (ENOMEM);
++
++    rxd->Handler      = handler;
++    rxd->Arg          = arg;
++    rxd->Data         = *nmd;
++    rxd->RxdMain->Len = EP_RXD_PENDING;
++    
++    spin_lock_irqsave (&rcvr->Lock, flags);
++
++    list_add_tail (&rxd->Link, &rcvr->ActiveDescList);
++    
++    if (EP_IS_PREFRAIL_SET(attr))
++      rnum = EP_ATTR2PREFRAIL(attr);
++    else 
++      rnum = ep_rcvr_prefrail (rcvr, EP_NMD_RAILMASK(nmd));
++
++    if (rnum < 0 || !(EP_NMD_RAILMASK(nmd) & EP_RAIL2RAILMASK(rnum) & rcvr->RailMask))
++      rcvrRail = NULL;
++    else
++      rcvrRail = rcvr->Rails[rnum];
++
++    EPRINTF7 (DBG_RCVR,"ep_queue_receive: rxd=%p svc %d nmd=%08x,%d,%x rnum=%d rcvrRail=%p\n",
++            rxd, rcvr->Service, nmd->nmd_addr, nmd->nmd_len, nmd->nmd_attr, rnum, rcvrRail);
++
++    rxd->State = EP_RXD_RECEIVE_ACTIVE;
++
++    if (rcvrRail == NULL || !EP_RCVR_OP (rcvrRail, QueueRxd) (rxd, rcvrRail))
++    {
++      rxd->State = EP_RXD_RECEIVE_UNBOUND;
++
++      ep_kthread_schedule (&rcvr->Subsys->Thread, lbolt);
++    }
++
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++
++    return (ESUCCESS);
++}
++
++void
++ep_requeue_receive (EP_RXD *rxd, EP_RXH *handler, void *arg, EP_NMD *nmd, EP_ATTRIBUTE attr)
++{
++    EP_RCVR      *rcvr = rxd->Rcvr;
++    EP_SYS       *sys  = rcvr->Subsys->Subsys.Sys;
++    int           rnum = ep_pickRail(EP_NMD_RAILMASK(&rxd->Data));
++    EP_RCVR_RAIL *rcvrRail;
++    unsigned long flags;
++
++    ASSERT (rxd->RxdRail == NULL);
++
++    EPRINTF5 (DBG_RCVR,"ep_requeue_receive: rxd=%p svc %d nmd=%08x,%d,%x\n", 
++            rxd, rcvr->Service, nmd->nmd_addr, nmd->nmd_len, nmd->nmd_attr);
++
++    rxd->Handler      = handler;
++    rxd->Arg          = arg;
++    rxd->Data         = *nmd;
++    rxd->RxdMain->Len = EP_RXD_PENDING;
++    
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    
++    list_add_tail (&rxd->Link, &rcvr->ActiveDescList);
++
++    /*
++     * Rail selection: if they've asked for a particular rail, then use it, otherwise if
++     *                 the rail it was last received on is mapped for the nmd and is available
++     *                 then use that one, otherwise pick one that is mapped by the nmd.
++     */
++    if (EP_IS_PREFRAIL_SET(attr))
++      rnum = EP_ATTR2PREFRAIL(attr);
++    
++    if (rnum < 0 || ! (EP_RAIL2RAILMASK (rnum) & EP_NMD_RAILMASK(nmd) & ep_rcvr_availrails (rcvr)))
++      rnum = ep_rcvr_prefrail (rcvr, EP_NMD_RAILMASK(nmd));
++
++    if (rnum < 0)
++      rcvrRail = NULL;
++    else
++    {
++      rcvrRail = rcvr->Rails[rnum];
++
++      if (! (EP_NMD_RAILMASK(&rxd->Data) & EP_RAIL2RAILMASK(rnum)) && ep_nmd_map_rails (sys, &rxd->Data, EP_RAIL2RAILMASK(rnum)) < 0)
++          rcvrRail = NULL;
++    }
++
++    rxd->State = EP_RXD_RECEIVE_ACTIVE;
++
++    if (rcvrRail == NULL || !EP_RCVR_OP(rcvrRail, QueueRxd) (rxd, rcvrRail))
++    {
++      EPRINTF1 (DBG_RCVR, "ep_requeue_receive: rcvrRail=%p - setting unbound\n", rcvrRail);
++
++      rxd->State = EP_RXD_RECEIVE_UNBOUND;
++
++      ep_kthread_schedule (&rcvr->Subsys->Thread, lbolt);
++    }
++
++    if (rcvr->CleanupWaiting)
++      kcondvar_wakeupall (&rcvr->CleanupSleep, &rcvr->Lock);
++    rcvr->CleanupWaiting = 0;
++
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++}
++
++void
++
++ep_complete_receive (EP_RXD *rxd)
++{
++    EP_RCVR *rcvr = rxd->Rcvr;
++    unsigned long flags;
++
++    ASSERT (rxd->RxdRail == NULL && rxd->State == EP_RXD_COMPLETED);
++
++    FreeRxd (rcvr, rxd);
++
++    /* if we're waiting for cleanup, then wake them up */
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    if (rcvr->CleanupWaiting)
++      kcondvar_wakeupall (&rcvr->CleanupSleep, &rcvr->Lock);
++    rcvr->CleanupWaiting = 0;
++    spin_unlock_irqrestore (&rcvr->Lock, flags);   
++}
++
++int
++ep_rpc_put (EP_RXD *rxd, EP_RXH *handler, void *arg, EP_NMD *local, EP_NMD *remote, int nFrags)
++{
++    EP_RCVR      *rcvr = rxd->Rcvr;
++    EP_SYS       *sys  = rcvr->Subsys->Subsys.Sys;
++    EP_ENVELOPE  *env  = &rxd->RxdMain->Envelope;
++    unsigned long flags;
++
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    
++    if (rxd->State == EP_RXD_BEEN_ABORTED)
++    {
++      EPRINTF2 (DBG_RCVR, "ep_rpc_put: rcvr %p rxd %p completed because no rails available\n", rcvr, rxd);
++      
++      /* rxd no longer on active list - just free it */
++      /* off and return an error */
++      spin_unlock_irqrestore (&rcvr->Lock, flags);
++      
++      return EP_CONN_RESET;
++    }
++    else
++    {
++      EP_RXD_RAIL      *rxdRail   = rxd->RxdRail;
++      EP_RCVR_RAIL     *rcvrRail  = rxdRail->RcvrRail;
++      EP_COMMS_RAIL    *commsRail = rcvrRail->CommsRail;
++      EP_RAIL          *rail      = commsRail->Rail;
++      EP_NODE_RAIL     *nodeRail  = &rail->Nodes[env->NodeId];
++      int               i;
++      
++      /* Attempt to ensure that the local nmds are mapped */
++      for (i = 0; i < nFrags; i++)
++          if (! (EP_NMD_RAILMASK(&local[i]) & EP_RAIL2RAILMASK(rail->Number)))
++              ep_nmd_map_rails (sys, &local[i], EP_RAIL2RAILMASK(rail->Number));
++    
++      if (nodeRail->State == EP_NODE_CONNECTED &&                                                                     /* rail is connected */
++          (ep_nmd2railmask (local, nFrags) & ep_nmd2railmask (remote, nFrags) & EP_RAIL2RAILMASK (rail->Number)))     /* and NMDs valid for it */
++      {
++          rxd->State = EP_RXD_PUT_ACTIVE;
++
++          EP_RCVR_OP(rcvrRail, RpcPut) (rxd, local, remote, nFrags);
++      }
++      else
++      {
++          /* RPC completion cannot progress - either node is no longer connected on this 
++           * rail or some of the source/destination NMDs are not mapped on this rail.
++           * Save the NMDs into the RXD and schedule the thread to request mappings */
++          EPRINTF4 (DBG_RCVR, "%s: ep_rpc_put: rcvr %p rxd %p %s\n", rail->Name, rcvr, rxd,
++                    (nodeRail->State == EP_NODE_CONNECTED) ? "NMDs not valid on this rail" : "no longer connected on this rail");
++
++          rxd->State = EP_RXD_PUT_STALLED;
++
++          if (nodeRail->State == EP_NODE_CONNECTED)
++              ep_kthread_schedule (&rcvr->Subsys->Thread, lbolt);
++      }
++
++      /* install the handler */
++      rxd->Handler = handler;
++      rxd->Arg     = arg;
++      
++      /* store the arguements */
++      rxd->nFrags = nFrags;
++      for (i = 0; i < nFrags; i++)
++      {
++          rxd->Local[i]  = local[i];
++          rxd->Remote[i] = remote[i];
++      }
++    }
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++
++    return EP_SUCCESS;
++}
++
++int
++ep_rpc_get (EP_RXD *rxd, EP_RXH *handler, void *arg, EP_NMD *remote, EP_NMD *local, int nFrags)
++{
++    EP_RCVR      *rcvr = rxd->Rcvr;
++    EP_SYS       *sys  = rcvr->Subsys->Subsys.Sys;
++    EP_ENVELOPE  *env  = &rxd->RxdMain->Envelope;
++    unsigned long flags;
++
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    
++    if (rxd->State == EP_RXD_BEEN_ABORTED)
++    {
++      EPRINTF2 (DBG_RCVR, "ep_rpc_get: rcvr %p rxd %p completed because no rails available\n", rcvr, rxd);
++      
++      spin_unlock_irqrestore (&rcvr->Lock, flags);
++
++      return EP_CONN_RESET;
++    }
++    else
++    {
++      EP_RXD_RAIL      *rxdRail   = rxd->RxdRail;
++      EP_RCVR_RAIL     *rcvrRail  = rxdRail->RcvrRail;
++      EP_COMMS_RAIL    *commsRail = rcvrRail->CommsRail;
++      EP_RAIL          *rail      = commsRail->Rail;
++      EP_NODE_RAIL     *nodeRail  = &rail->Nodes[env->NodeId];
++      int               i;
++      
++      /* Attempt to ensure that the local nmds are mapped */
++      for (i = 0; i < nFrags; i++)
++          if (! (EP_NMD_RAILMASK(&local[i]) & EP_RAIL2RAILMASK(rail->Number)))
++              ep_nmd_map_rails (sys, &local[i], EP_RAIL2RAILMASK(rail->Number));
++
++      if (nodeRail->State == EP_NODE_CONNECTED &&                                                                     /* rail is connected */
++          (ep_nmd2railmask (local, nFrags) & ep_nmd2railmask (remote, nFrags) & EP_RAIL2RAILMASK (rail->Number)))     /* and NMDs valid for it */
++      {
++          rxd->State = EP_RXD_GET_ACTIVE;
++
++          EP_RCVR_OP (rcvrRail, RpcGet) (rxd, local, remote, nFrags);
++      }
++      else
++      {
++          /* RPC completion cannot progress - either node is no longer connected on this 
++           * node or some of the source/destination NMDs are not mapped on this rail.
++           * Save the NMDs into the RXD and schedule the thread to request mappings */
++          EPRINTF4 (DBG_RCVR, "%s: ep_rpc_get: rcvr %p rxd %p %s\n", rail->Name, rcvr, rxd, 
++                    (nodeRail->State == EP_NODE_CONNECTED) ? "NMDs not valid on this rail" : "no longer connected on this rail");
++          
++          rxd->State = EP_RXD_GET_STALLED;
++
++          if (nodeRail->State == EP_NODE_CONNECTED)
++              ep_kthread_schedule (&rcvr->Subsys->Thread, lbolt);
++      }
++
++      /* install the handler */
++      rxd->Handler = handler;
++      rxd->Arg     = arg;
++      
++      /* store the arguements */
++      rxd->nFrags = nFrags;
++      for (i = 0; i < nFrags; i++)
++      {
++          rxd->Local[i]  = local[i];
++          rxd->Remote[i] = remote[i];
++      }
++    }
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++    
++    return EP_SUCCESS;
++}
++
++int
++ep_complete_rpc (EP_RXD *rxd, EP_RXH *handler, void *arg, EP_STATUSBLK *blk, EP_NMD *local, EP_NMD *remote, int nFrags)
++{
++    EP_RCVR      *rcvr = rxd->Rcvr;
++    EP_SYS       *sys  = rcvr->Subsys->Subsys.Sys;
++    EP_ENVELOPE  *env  = &rxd->RxdMain->Envelope;
++    unsigned long flags;
++
++    spin_lock_irqsave (&rcvr->Lock, flags);
++
++    if (rxd->State == EP_RXD_BEEN_ABORTED)
++    {
++      EPRINTF2 (DBG_RCVR, "ep_complete_rpc: rcvr %p rxd %p completed because no rails available\n", rcvr, rxd);
++      
++      spin_unlock_irqrestore (&rcvr->Lock, flags);
++      return EP_CONN_RESET;
++    }
++    else
++    {
++      EP_RXD_RAIL      *rxdRail   = rxd->RxdRail;
++      EP_RCVR_RAIL     *rcvrRail  = rxdRail->RcvrRail;
++      EP_COMMS_RAIL    *commsRail = rcvrRail->CommsRail;
++      EP_RAIL          *rail      = commsRail->Rail;
++      EP_NODE_RAIL     *nodeRail  = &rail->Nodes[env->NodeId];
++      int               i;
++
++      if (blk == NULL)
++          bzero (&rxd->RxdMain->StatusBlk, sizeof (EP_STATUSBLK));
++      else
++          bcopy (blk, &rxd->RxdMain->StatusBlk, sizeof (EP_STATUSBLK));
++
++      /* Attempt to ensure that the local nmds are mapped */
++      for (i = 0; i < nFrags; i++)
++          if (! (EP_NMD_RAILMASK(&local[i]) & EP_RAIL2RAILMASK(rail->Number)))
++              ep_nmd_map_rails (sys, &local[i], EP_RAIL2RAILMASK(rail->Number));
++
++      if (nodeRail->State == EP_NODE_CONNECTED &&                                                                     /* rail is connected */
++          (ep_nmd2railmask (local, nFrags) & ep_nmd2railmask (remote, nFrags) & EP_RAIL2RAILMASK (rail->Number)))     /* and NMDs valid for it */
++      {
++          rxd->State = EP_RXD_COMPLETE_ACTIVE;
++
++          EP_RCVR_OP (rcvrRail, RpcComplete) (rxd, local, remote, nFrags);
++      }
++      else
++      {
++          /* RPC completion cannot progress - either node is no longer connected on this 
++           * node or some of the source/destination NMDs are not mapped on this rail.
++           * Save the NMDs into the RXD and schedule the thread to request mappings */
++          EPRINTF4 (DBG_RCVR, "%s: ep_complete_rpc: rcvr %p rxd %p %s\n", rail->Name, rcvr, rxd, 
++                    (nodeRail->State == EP_NODE_CONNECTED) ? "NMDs not valid on this rail" : "no longer connected on this rail");
++
++          rxd->State = EP_RXD_COMPLETE_STALLED;
++
++          if (nodeRail->State == EP_NODE_CONNECTED)
++              ep_kthread_schedule (&rcvr->Subsys->Thread, lbolt);
++      }
++
++      /* install the handler */
++      rxd->Handler = handler;
++      rxd->Arg     = arg;
++      
++      /* store the arguements */
++      rxd->nFrags = nFrags;
++      for (i = 0; i < nFrags; i++)
++      {
++          rxd->Local[i]  = local[i];
++          rxd->Remote[i] = remote[i];
++      }
++    }
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++      
++    return (ESUCCESS);
++}
++
++/* functions for accessing fields of rxds */
++void            *ep_rxd_arg(EP_RXD *rxd)              { return (rxd->Arg); }
++int              ep_rxd_len(EP_RXD *rxd)              { return (rxd->RxdMain->Len); }
++EP_STATUS      ep_rxd_status(EP_RXD *rxd)             { return (rxd->RxdMain->Len < 0 ? rxd->RxdMain->Len : EP_SUCCESS); }
++int              ep_rxd_isrpc(EP_RXD *rxd)            { return (EP_IS_RPC(rxd->RxdMain->Envelope.Attr) != 0); }
++EP_ENVELOPE     *ep_rxd_envelope(EP_RXD *rxd)                 { return (&rxd->RxdMain->Envelope); }
++EP_PAYLOAD      *ep_rxd_payload(EP_RXD *rxd)          { return (EP_HAS_PAYLOAD(rxd->RxdMain->Envelope.Attr) ? &rxd->RxdMain->Payload : NULL); }
++int              ep_rxd_node(EP_RXD *rxd)             { return (rxd->RxdMain->Envelope.NodeId); }
++EP_STATUSBLK    *ep_rxd_statusblk(EP_RXD *rxd)                { return (&rxd->RxdMain->StatusBlk); }
++EP_RAILMASK      ep_rxd_railmask(EP_RXD *rxd)         { return (rxd->Data.nmd_attr); }
++
++static void
++ProcessNmdMapResponse (EP_RCVR *rcvr, EP_RXD *rxd, EP_MANAGER_MSG *msg)
++{
++    EP_RXD_RAIL  *rxdRail  = rxd->RxdRail;
++    EP_RCVR_RAIL *rcvrRail = rxdRail->RcvrRail;
++    EP_RAIL      *rail     = rcvrRail->CommsRail->Rail;
++    EP_NODE_RAIL *nodeRail = &rail->Nodes[rxd->RxdMain->Envelope.NodeId];
++    int           i;
++
++    ASSERT (msg->Body.MapNmd.nFrags == rxd->nFrags);
++    
++    for (i = 0; i < rxd->nFrags; i++)
++      rxd->Remote[i] = msg->Body.MapNmd.Nmd[i];
++    
++    if (nodeRail->State == EP_NODE_CONNECTED &&       /* node is still connected on this rail */
++      (ep_nmd2railmask (rxd->Local, rxd->nFrags) & ep_nmd2railmask (rxd->Remote, rxd->nFrags) & EP_RAIL2RAILMASK (rail->Number)))     /* NMDs are now valid for this rail */
++    {
++      switch (rxd->State)
++      {
++      case EP_RXD_PUT_STALLED:
++          rxd->State = EP_RXD_PUT_ACTIVE;
++
++          EP_RCVR_OP(rcvrRail, RpcPut) (rxd, rxd->Local, rxd->Remote, rxd->nFrags);
++          break;
++
++      case EP_RXD_GET_STALLED:
++          rxd->State = EP_RXD_GET_ACTIVE;
++
++          EP_RCVR_OP(rcvrRail, RpcGet) (rxd, rxd->Local, rxd->Remote, rxd->nFrags);
++          break;
++          
++      case EP_RXD_COMPLETE_STALLED:
++          rxd->State = EP_RXD_COMPLETE_ACTIVE;
++
++          EP_RCVR_OP(rcvrRail, RpcComplete) (rxd, rxd->Local, rxd->Remote, rxd->nFrags);
++          break;
++
++      default:
++          panic ("ProcessNmdMapResponse: XID match but rxd in invalid state\n");
++          break;
++      }
++
++      rxd->NextRunTime = 0;
++    }
++    else
++      ep_debugf (DBG_MANAGER, "%s: ep_rcvr_xid_msg_handler: rcvr=%p rxd=%p - still cannot proceed\n", rail->Name, rcvr, rxd);
++}
++
++static void
++ProcessFailoverResponse (EP_RCVR *rcvr, EP_RXD *rxd, EP_MANAGER_MSG *msg)
++{
++    /* XXXX - TBD */
++#ifdef NOTYET
++    EP_COMMS_SUBSYS *subsys   = rcvr->Subsys;
++    EP_RXD_RAIL     *rxdRail  = rxd->RxdRail;
++    EP_RCVR_RAIL    *rcvrRail = rxdRail->RcvrRail;
++    EP_RAIL         *rail     = rcvrRail->CommsRail->Rail;
++    EP_RCVR_RAIL    *nRcvrRail;
++    EP_RXD_RAIL     *nRxdRail;
++
++    ASSERT (rxd->RxdMain->Envelope.Attr & EP_RPC);
++
++    EPRINTF6 (DBG_RCVR, "ep_rcvr_xid_msg_handler: rcvr=%p rxd=%p Xid=%016llx state %x.%x - txd on rail %d\n", rcvr, rxd, 
++            rxd->MsgXid.Unique, rxdRail->RxdMain->DataEvent, rxdRail->RxdMain->DoneEvent, msg->Body.FailoverTxd.Rail);
++
++    if ((nRcvrRail = rcvr->Rails[msg->Body.FailoverTxd.Rail]) == NULL ||
++      (nRcvrRail->Rcvr->RailMask & EP_RAIL2RAILMASK (rail->Number)) == NULL)
++    {
++      ep_debugf (DBG_MANAGER, "%s: ep_rcvr_xid_msg_handler: rcvr=%p rxd=%p - still cannot proceed\n", rail->Name, rcvr,rxd);
++      return;
++    }
++
++
++    nRxdRail = EP_RCVR_OP (nrcvrRail, GetRxd) (rcvr, nRcvrRail);
++
++
++    /* If the RPC was in progress, then rollback and mark it as flagged, 
++     * this will then get treated as though the NMDs were not mapped
++     * for the rail when the user initiated the operation.
++     */
++    switch (rxdRail->RxdMain->DataEvent)
++    {
++    case EP_EVENT_ACTIVE|EP_RXD_PHASE_PUT:
++    case EP_EVENT_FLAGGED|EP_RXD_PHASE_PUT:
++      ASSERT (rxdRail->RxdMain->DoneEvent == EP_EVENT_PRIVATE ||
++              rxdRail->RxdMain->DoneEvent == EP_EVENT_PENDING);
++      
++      nRxdRail->RxdMain->DataEvent = EP_EVENT_FLAGGED|EP_RXD_PHASE_PUT;
++      nRxdRail->RxdMain->DoneEvent = EP_EVENT_PENDING;
++      break;
++
++    case EP_EVENT_ACTIVE|EP_RXD_PHASE_GET:
++    case EP_EVENT_FLAGGED|EP_RXD_PHASE_GET:
++      ASSERT (rxdRail->RxdMain->DoneEvent == EP_EVENT_PRIVATE ||
++              rxdRail->RxdMain->DoneEvent == EP_EVENT_PENDING);
++      
++      nRxdRail->RxdMain->DataEvent = EP_EVENT_FLAGGED|EP_RXD_PHASE_GET;
++      nRxdRail->RxdMain->DoneEvent = EP_EVENT_PENDING;
++      break;
++
++    case EP_EVENT_PRIVATE:
++      switch (rxdRail->RxdMain->DoneEvent)
++      {
++      case EP_EVENT_ACTIVE|EP_RXD_PHASE_COMPLETE:
++      case EP_EVENT_FLAGGED|EP_RXD_PHASE_COMPLETE:
++          nRxdRail->RxdMain->DataEvent = EP_EVENT_PRIVATE;
++          nRxdRail->RxdMain->DoneEvent = EP_EVENT_FLAGGED|EP_RXD_PHASE_COMPLETE;
++          break;
++
++      case EP_EVENT_PENDING:
++          break;
++
++      default:
++          panic ("ep_rcvr_xid_msg_handler: rxd in invalid state\n");
++      }
++      break;
++
++    default:
++      panic ("ep_rcvr_xid_msg_handler: rxd in invalid staten");
++    }
++    
++    UnbindRxdFromRail (rxd, rxdRail);
++
++    /* Mark rxdRail as no longer active */
++    rxdRail->RxdMain->DataEvent = EP_EVENT_PRIVATE;
++    rxdRail->RxdMain->DoneEvent = EP_EVENT_PRIVATE;
++
++    sdram_writel (rail->Device, rxdRail->RxdElan + offsetof (EP_RXD_RAIL_ELAN, DataEvent.ev_Count), 0);
++    sdram_writel (rail->Device, rxdRail->RxdElan + offsetof (EP_RXD_RAIL_ELAN, DoneEvent.ev_Count), 0);
++    
++    FreeRxdRail (rcvrRail, rxdRail);
++
++    BindRxdToRail (rxd, nRxdRail);
++
++    ep_kthread_schedule (&subsys->Thread, lbolt);
++#endif
++}
++
++void
++ep_rcvr_xid_msg_handler (void *arg, EP_MANAGER_MSG *msg)
++{
++    EP_RCVR          *rcvr = (EP_RCVR *) arg;
++    struct list_head *el;
++    unsigned long     flags;
++
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    list_for_each (el, &rcvr->ActiveDescList) {
++      EP_RXD *rxd = list_entry (el,EP_RXD, Link);
++
++      if (EP_XIDS_MATCH (msg->Hdr.Xid, rxd->MsgXid))
++      {
++          EP_INVALIDATE_XID (rxd->MsgXid);
++
++          switch (msg->Hdr.Type)
++          {
++          case EP_MANAGER_MSG_TYPE_MAP_NMD_RESPONSE:
++              ProcessNmdMapResponse (rcvr, rxd, msg);
++              break;
++
++          case EP_MANAGER_MSG_TYPE_FAILOVER_RESPONSE:
++              ProcessFailoverResponse (rcvr, rxd, msg);
++              break;
++
++          default:
++              panic ("ep_rcvr_xid_msg_handler: XID match but invalid message type\n");
++          }
++      }
++    }
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++}
++
++
++EP_RCVR *
++ep_alloc_rcvr (EP_SYS *sys, EP_SERVICE svc, unsigned int nenvs)
++{
++    EP_COMMS_SUBSYS  *subsys;
++    EP_RCVR          *rcvr;
++    struct list_head *el;
++    extern int portals_envelopes;
++
++    if (portals_envelopes && (svc == EP_MSG_SVC_PORTALS_SMALL || svc == EP_MSG_SVC_PORTALS_LARGE))
++    {
++      printk ("ep: use %d envelopes rather than %d for portals %s message service\n", sys->Position.pos_nodes * 16, nenvs,
++              svc == EP_MSG_SVC_PORTALS_SMALL ? "small" : "large");
++
++      nenvs = portals_envelopes;
++    }
++
++    if ((subsys = (EP_COMMS_SUBSYS *) ep_subsys_find (sys, EPCOMMS_SUBSYS_NAME)) == NULL)
++      return (NULL);
++
++    KMEM_ZALLOC (rcvr, EP_RCVR *, sizeof (EP_RCVR), 1);
++
++    if (rcvr == NULL)
++      return (NULL);
++
++    rcvr->Subsys            = subsys;
++    rcvr->Service           = svc;
++    rcvr->InputQueueEntries = nenvs;
++    rcvr->FreeDescCount     = 0;
++    rcvr->TotalDescCount    = 0;
++    rcvr->ForwardRxdCount   = 0;
++
++    spin_lock_init (&rcvr->Lock);
++    INIT_LIST_HEAD (&rcvr->ActiveDescList);
++
++    kcondvar_init (&rcvr->CleanupSleep);
++    kcondvar_init (&rcvr->FreeDescSleep);
++    spin_lock_init (&rcvr->FreeDescLock);
++    INIT_LIST_HEAD (&rcvr->FreeDescList);
++    INIT_LIST_HEAD (&rcvr->DescBlockList);
++
++    ep_xid_cache_init (sys, &rcvr->XidCache);
++
++    rcvr->XidCache.MessageHandler = ep_rcvr_xid_msg_handler;
++    rcvr->XidCache.Arg            = rcvr;
++
++    kmutex_lock (&subsys->Lock);
++    /* See if this service is already in use */
++    list_for_each (el, &subsys->Receivers) {
++      EP_RCVR *rcvr = list_entry (el, EP_RCVR, Link);
++
++      if (rcvr->Service == svc)
++      {
++          KMEM_FREE (rcvr, sizeof (EP_RCVR));
++          kmutex_unlock (&subsys->Lock);   
++          return NULL;
++      }
++    }
++    
++    
++    list_add_tail (&rcvr->Link, &subsys->Receivers);
++
++    ep_procfs_rcvr_add(rcvr);
++
++    /* Now add all rails which are already started */
++    list_for_each (el, &subsys->Rails) { 
++      EP_COMMS_RAIL *commsRail = list_entry (el, EP_COMMS_RAIL, Link);
++
++      EP_RAIL_OP (commsRail, Rcvr.AddRail) (rcvr, commsRail);
++    }
++    kmutex_unlock (&subsys->Lock);   
++
++    ep_mod_inc_usecount();
++
++    return (rcvr);
++}
++
++void
++ep_free_rcvr (EP_RCVR *rcvr)
++{
++    EP_COMMS_SUBSYS  *subsys = rcvr->Subsys;
++    EP_SYS           *sys    = subsys->Subsys.Sys;
++    struct list_head  list;
++    struct list_head *el,*nel;
++    unsigned long flags;
++    
++    kmutex_lock (&subsys->Lock);
++    list_for_each (el, &subsys->Rails) { 
++      EP_COMMS_RAIL *commsRail = list_entry (el, EP_COMMS_RAIL, Link);
++      
++      EP_RAIL_OP (commsRail, Rcvr.DelRail) (rcvr, commsRail);
++    }
++
++    ep_procfs_rcvr_del(rcvr);
++
++    list_del (&rcvr->Link);
++    kmutex_unlock (&subsys->Lock);
++
++    INIT_LIST_HEAD (&list);
++
++    /* abort all rxds - should not be bound to a rail */
++    spin_lock_irqsave (&rcvr->Lock, flags);   
++    for (;;)
++    {
++      if (! list_empty (&rcvr->ActiveDescList))
++      {
++          list_for_each_safe (el, nel, &rcvr->ActiveDescList) {
++              EP_RXD *rxd = list_entry (el, EP_RXD, Link);
++              
++              ASSERT (rxd->RxdRail == NULL);
++              ASSERT (rxd->RxdMain->Len == EP_RXD_PENDING);
++              
++              rxd->State = EP_RXD_COMPLETED;
++              rxd->RxdMain->Len = EP_SHUTDOWN;
++              
++              list_del (&rxd->Link);
++              list_add_tail (&rxd->Link, &list);
++          }
++          spin_unlock_irqrestore (&rcvr->Lock, flags);
++          
++          while (! list_empty (&list))
++          {
++              EP_RXD *rxd = list_entry (list.next, EP_RXD, Link);
++              
++              list_del (&rxd->Link);
++              
++              if (rxd->Handler) 
++                  rxd->Handler (rxd);
++          }
++          spin_lock_irqsave (&rcvr->Lock, flags);   
++          continue;
++      }
++
++      if (rcvr->FreeDescCount == rcvr->TotalDescCount)
++          break;
++
++      rcvr->CleanupWaiting++;
++      kcondvar_wait (&rcvr->CleanupSleep, &rcvr->Lock, &flags);
++    }
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++
++    /* must all be in free list */
++    ASSERT( rcvr->FreeDescCount ==  rcvr->TotalDescCount);
++
++    while (! list_empty(& rcvr->DescBlockList) )
++      FreeRxdBlock (rcvr, list_entry (rcvr->DescBlockList.next, EP_RXD_BLOCK, Link));
++
++    /* had better be all gone now */
++    ASSERT((rcvr->FreeDescCount == 0) && (rcvr->TotalDescCount == 0));
++
++    ep_xid_cache_destroy (sys, &rcvr->XidCache);
++ 
++    spin_lock_destroy (&rcvr->Lock);
++    KMEM_FREE (rcvr, sizeof (EP_RCVR));
++
++    ep_mod_dec_usecount();
++}
++
++EP_RXD *
++StealRxdFromOtherRail (EP_RCVR *rcvr)
++{
++    EP_RXD         *rxd;
++    int               i;
++      
++    /* looking at the the rcvr railmask to find a rail to try to steal rxd from */
++    for (i = 0; i < EP_MAX_RAILS; i++) 
++      if (rcvr->RailMask & (1 << i) ) 
++          if ((rxd = EP_RCVR_OP (rcvr->Rails[i], StealRxd) (rcvr->Rails[i])) != NULL)
++              return rxd;
++
++    return NULL;
++}
++
++long
++CheckUnboundRxd (EP_RCVR *rcvr, EP_RXD *rxd, long nextRunTime)
++{
++    EP_SYS       *sys = rcvr->Subsys->Subsys.Sys;
++    EP_RCVR_RAIL *rcvrRail;
++    int           rnum;
++    
++    if ((rnum = ep_rcvr_prefrail (rcvr, EP_NMD_RAILMASK(&rxd->Data))) < 0)
++      rnum = ep_rcvr_prefrail (rcvr, ep_rcvr_availrails (rcvr));
++    
++    if ( rnum < 0 )   {
++      if (nextRunTime == 0 || AFTER (nextRunTime, lbolt + RESOURCE_RETRY_TIME))
++          nextRunTime = lbolt + RESOURCE_RETRY_TIME;
++      
++      return (nextRunTime);
++    }
++
++    ASSERT ( rnum >= 0 );
++
++    rcvrRail = rcvr->Rails[rnum];
++
++    ASSERT ( rcvrRail != NULL);
++
++    rxd->State = EP_RXD_RECEIVE_ACTIVE;
++
++    if ((!(EP_NMD_RAILMASK (&rxd->Data) & EP_RAIL2RAILMASK(rnum)) &&                  /* not mapped already and */
++       ep_nmd_map_rails (sys, &rxd->Data, EP_RAIL2RAILMASK(rnum)) == 0) ||            /* failed mapping, or */
++      !EP_RCVR_OP (rcvrRail, QueueRxd) (rxd, rcvrRail))                               /* failed to queue */
++    {
++      ASSERT (rxd->RxdRail == NULL);
++
++      EPRINTF4 (DBG_RCVR,"CheckUnboundRxd: rcvr=%p rxd=%p -> rnum=%d rcvrRail=%p (failed)\n", rcvr, rxd, rnum, rcvrRail);
++
++      rxd->State = EP_RXD_RECEIVE_UNBOUND;
++      
++      if (nextRunTime == 0 || AFTER (nextRunTime, lbolt + RESOURCE_RETRY_TIME))
++          nextRunTime = lbolt + RESOURCE_RETRY_TIME;
++    }
++
++    return (nextRunTime);
++}
++
++int
++CheckRxdNmdsMapped (EP_RCVR *rcvr, EP_RXD *rxd)
++{
++    EP_RXD_RAIL *rxdRail = rxd->RxdRail;
++    EP_RXD_MAIN *rxdMain = rxd->RxdMain;
++    EP_ENVELOPE *env     = &rxdMain->Envelope;
++    EP_SYS      *sys     = rcvr->Subsys->Subsys.Sys;
++    EP_RAIL     *rail    = rxdRail->RcvrRail->CommsRail->Rail;
++    int                i;
++
++    /* Try and map the local NMDs before checking to see if we can proceed */
++    if (! (ep_nmd2railmask (rxd->Local, rxd->nFrags) & EP_RAIL2RAILMASK (rail->Number)))
++    {
++      EPRINTF3 (DBG_MAPNMD, "%s: rcvr=%p rxd=%p RPC Local NMDs not mapped\n", rail->Name, rcvr, rxd);
++      
++      for (i = 0; i < rxd->nFrags; i++)
++          if (! (EP_NMD_RAILMASK(&rxd->Local[i]) & EP_RAIL2RAILMASK(rail->Number)))
++              if (ep_nmd_map_rails (sys, &rxd->Local[i], EP_RAIL2RAILMASK(rail->Number)))
++                  rxd->NextRunTime = lbolt + RESOURCE_RETRY_TIME;
++    }
++    
++    /* Try and map remote NMDs if they are not valid for this rail */
++    if (! (ep_nmd2railmask (rxd->Remote, rxd->nFrags) & EP_RAIL2RAILMASK (rail->Number)))
++    {
++      EP_MANAGER_MSG_BODY msgBody;
++
++      EPRINTF3 (DBG_MAPNMD, "%s: rcvr=%p rxd=%p RPC Remote NMDs not mapped\n", rail->Name, rcvr, rxd);
++
++      if (EP_XID_INVALID(rxd->MsgXid))
++          rxd->MsgXid = ep_xid_cache_alloc (sys, &rcvr->XidCache);
++
++      msgBody.MapNmd.nFrags   = rxd->nFrags;
++      msgBody.MapNmd.Railmask = EP_RAIL2RAILMASK (rail->Number);
++      for (i = 0; i < rxd->nFrags; i++)
++          msgBody.MapNmd.Nmd[i] = rxd->Remote[i];
++
++      if (ep_send_message (rail, env->NodeId, EP_MANAGER_MSG_TYPE_MAP_NMD_REQUEST, rxd->MsgXid, &msgBody) == 0)
++          rxd->NextRunTime = lbolt + MESSAGE_RETRY_TIME;
++      else
++          rxd->NextRunTime = lbolt + MSGBUSY_RETRY_TIME;
++
++      return 0;
++    }
++
++    if ((ep_nmd2railmask (rxd->Local, rxd->nFrags) & ep_nmd2railmask (rxd->Remote, rxd->nFrags) & EP_RAIL2RAILMASK (rail->Number)) != 0)
++    {
++      rxd->NextRunTime = 0;
++      return 1;
++    }
++
++    return 0;
++}
++
++long
++ep_check_rcvr (EP_RCVR *rcvr, long nextRunTime)
++{
++    struct list_head *el, *nel;
++    unsigned long     flags;
++    int               i;
++
++    /* Check to see if we're low on rxds */
++    if (rcvr->FreeDescCount < ep_rxd_lowat)
++      AllocateRxdBlock (rcvr, 0, NULL);
++
++    for (i = 0; i < EP_MAX_RAILS; i++) 
++      if (rcvr->RailMask & (1 << i) )
++          nextRunTime = EP_RCVR_OP (rcvr->Rails[i], Check) (rcvr->Rails[i], nextRunTime);
++
++    /* See if we have any rxd's which need to be handled */
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    list_for_each_safe (el, nel, &rcvr->ActiveDescList) {
++      EP_RXD      *rxd     = list_entry (el, EP_RXD, Link);
++      EP_RXD_MAIN *rxdMain = rxd->RxdMain;
++      EP_ENVELOPE *env     = &rxdMain->Envelope;
++      EP_RXD_RAIL *rxdRail = rxd->RxdRail;
++
++      if (rxdRail == NULL)
++          nextRunTime = CheckUnboundRxd (rcvr, rxd, nextRunTime);
++      else
++      {
++          EP_RCVR_RAIL *rcvrRail = rxdRail->RcvrRail;
++          EP_RAIL      *rail     = rcvrRail->CommsRail->Rail;
++
++          if (rxd->RxdMain->Len == EP_RXD_PENDING ||                          /* envelope not received yet */
++              rail->Nodes[env->NodeId].State != EP_NODE_CONNECTED)            /* will be failing over */
++              continue;
++
++          switch (rxd->State)
++          {
++          case EP_RXD_PUT_STALLED:
++              if (CheckRxdNmdsMapped (rcvr, rxd))
++              {
++                  rxd->State = EP_RXD_PUT_ACTIVE;
++
++                  EP_RCVR_OP (rcvrRail, RpcPut) (rxd, rxd->Local, rxd->Remote, rxd->nFrags);
++              }
++              break;
++
++          case EP_RXD_GET_STALLED:
++              if (CheckRxdNmdsMapped (rcvr, rxd))
++              {
++                  rxd->State = EP_RXD_GET_ACTIVE;
++
++                  EP_RCVR_OP (rcvrRail, RpcGet) (rxd, rxd->Local, rxd->Remote, rxd->nFrags);
++              }
++              break;
++          
++          case EP_RXD_COMPLETE_STALLED:
++              if (CheckRxdNmdsMapped (rcvr, rxd))
++              {
++                  rxd->State = EP_RXD_COMPLETE_ACTIVE;
++
++                  EP_RCVR_OP (rcvrRail, RpcComplete)(rxd, rxd->Local, rxd->Remote, rxd->nFrags);
++              }
++              break;
++          }
++              
++          if (rxd->NextRunTime && (nextRunTime == 0 || AFTER (nextRunTime, rxd->NextRunTime)))
++              nextRunTime = rxd->NextRunTime;
++      }
++    }
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++    
++    return (nextRunTime);
++}
++
++void
++ep_display_rxd (DisplayInfo *di, EP_RXD *rxd)
++{
++    EP_RXD_MAIN *rxdMain  = rxd->RxdMain;
++    EP_ENVELOPE *env      = &rxdMain->Envelope;
++    EP_RXD_RAIL *rxdRail  = rxd->RxdRail;
++    
++    (di->func)(di->arg, "  RXD: %p State=%x RxdMain=%p(%x.%x.%x) Data=%x.%x.%x %s\n", rxd,
++             rxd->State, rxd->RxdMain, rxd->NmdMain.nmd_addr, rxd->NmdMain.nmd_len,
++             rxd->NmdMain.nmd_attr, rxd->Data.nmd_addr, rxd->Data.nmd_len, rxd->Data.nmd_attr,
++             rxd->RxdMain->Len == EP_RXD_PENDING ? "Pending" : "Active");
++    (di->func)(di->arg, "      NodeId=%d Range=%d.%d TxdRail=%x TxdMain=%x.%x.%x nFrags=%d XID=%08x.%08x.%016llx\n",
++             env->NodeId,  EP_RANGE_LOW(env->Range), EP_RANGE_HIGH(env->Range), env->TxdRail, env->TxdMain.nmd_addr,
++             env->TxdMain.nmd_len, env->TxdMain.nmd_attr, env->nFrags, env->Xid.Generation, env->Xid.Handle, env->Xid.Unique);;
++    (di->func)(di->arg, "      Frag[0] %08x.%08x.%08x\n", env->Frags[0].nmd_addr, env->Frags[0].nmd_len, env->Frags[0].nmd_attr);
++    (di->func)(di->arg, "      Frag[1] %08x.%08x.%08x\n", env->Frags[1].nmd_addr, env->Frags[1].nmd_len, env->Frags[1].nmd_attr);
++    (di->func)(di->arg, "      Frag[2] %08x.%08x.%08x\n", env->Frags[2].nmd_addr, env->Frags[2].nmd_len, env->Frags[2].nmd_attr);
++    (di->func)(di->arg, "      Frag[3] %08x.%08x.%08x\n", env->Frags[3].nmd_addr, env->Frags[3].nmd_len, env->Frags[3].nmd_attr);
++
++    if (rxdRail) EP_RCVR_OP (rxdRail->RcvrRail, DisplayRxd) (di, rxdRail);
++}
++
++void
++ep_display_rcvr (DisplayInfo *di, EP_RCVR *rcvr, int full)
++{
++    int               freeCount    = 0;
++    int                     activeCount  = 0;
++    int                     pendingCount = 0;
++    int                     railCounts[EP_MAX_RAILS];
++    struct list_head *el;
++    int               i;
++    unsigned long     flags;
++
++    for (i = 0; i <EP_MAX_RAILS; i++)
++      railCounts[i] = 0;
++
++    spin_lock_irqsave (&rcvr->FreeDescLock, flags);
++    list_for_each (el, &rcvr->FreeDescList)
++      freeCount++;
++    spin_unlock_irqrestore (&rcvr->FreeDescLock, flags);
++
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    list_for_each (el, &rcvr->ActiveDescList) {
++      EP_RXD      *rxd     = list_entry (el, EP_RXD, Link);
++      EP_RXD_RAIL *rxdRail = rxd->RxdRail;
++
++      if (rxd->RxdMain->Len == EP_RXD_PENDING)
++          pendingCount++;
++      else
++          activeCount++;
++
++      if (rxdRail)
++          railCounts[rxdRail->RcvrRail->CommsRail->Rail->Number]++;
++    }
++
++    (di->func)(di->arg, "RCVR: rcvr=%p number=%d\n", rcvr, rcvr->Service);
++    (di->func)(di->arg, "      RXDS Free=%d (%d) Pending=%d Active=%d Rails=%d.%d.%d.%d\n",
++             freeCount, rcvr->FreeDescCount, pendingCount, activeCount, railCounts[0], railCounts[1],
++             railCounts[2], railCounts[3]);
++
++    for (i = 0; i < EP_MAX_RAILS; i++)
++      if (rcvr->Rails[i] != NULL)
++          EP_RCVR_OP (rcvr->Rails[i], DisplayRcvr) (di, rcvr->Rails[i]);
++
++    list_for_each (el, &rcvr->ActiveDescList) {
++      EP_RXD *rxd = list_entry (el, EP_RXD, Link);
++
++      if (rxd->RxdMain->Len != EP_RXD_PENDING || full)
++          ep_display_rxd (di, rxd);
++    }
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++}
++
++void
++ep_rxd_received_now(EP_RXD *rxd)
++{
++    EP_ENVELOPE   *env  = &rxd->RxdMain->Envelope;
++    EP_RCVR       *rcvr = rxd->Rcvr;
++    unsigned long  flags;
++
++    INC_STAT(rcvr->stats,rx);
++    ADD_STAT(rcvr->stats,rx_len, rxd->RxdMain->Len);
++
++    if (rxd->RxdMain->Len < 0 || !EP_IS_MULTICAST(env->Attr))
++    {
++      rxd->Handler (rxd);
++    }
++    else
++    {
++      EPRINTF5 (DBG_RCVR, "ep_rxd_received: forward rxd=%p Data=%08x.%08x.%08x len=%d\n", rxd, 
++                rxd->Data.nmd_addr, rxd->Data.nmd_len, rxd->Data.nmd_attr, ep_rxd_len(rxd));
++
++      spin_lock_irqsave (&rcvr->Subsys->ForwardDescLock, flags);
++      list_add_tail (&rxd->Link, &rcvr->Subsys->ForwardDescList);
++      spin_unlock_irqrestore (&rcvr->Subsys->ForwardDescLock, flags);
++      
++      ep_kthread_schedule (&rcvr->Subsys->Thread, lbolt);
++    } 
++}
++
++#if defined(CONFIG_EP_NO_CHECK_SUM)
++void
++ep_rxd_received(EP_RXD *rxd) 
++{
++   ep_rxd_received_now(rxd);
++}
++
++#else
++
++void
++ep_rxd_received(EP_RXD *rxd) 
++{
++  EP_ENVELOPE   *env  = &rxd->RxdMain->Envelope;
++
++  if (env->CheckSum) 
++      ep_rxd_queue_csum(rxd);
++  else 
++      ep_rxd_received_now(rxd);
++}
++
++void
++ep_rxd_queue_csum(EP_RXD *rxd)
++{
++    EP_RCVR       *rcvr = rxd->Rcvr;
++    unsigned long flags;
++
++    EPRINTF5 (DBG_RCVR, "ep_rxd_queue_csum: rxd=%p Data=%08x.%08x.%08x len=%d\n", rxd, 
++            rxd->Data.nmd_addr, rxd->Data.nmd_len, rxd->Data.nmd_attr, ep_rxd_len(rxd));
++    
++    spin_lock_irqsave (&rcvr->Subsys->CheckSumDescLock, flags);
++    list_add_tail (&rxd->CheckSumLink, &rcvr->Subsys->CheckSumDescList);
++    spin_unlock_irqrestore (&rcvr->Subsys->CheckSumDescLock, flags);
++    
++    ep_kthread_schedule (&rcvr->Subsys->Thread, lbolt);
++}
++#endif
++
++void
++ep_rcvr_fillout_stats(EP_RCVR *rcvr, char *str)
++{
++    sprintf(str+strlen(str),"Rx     %lu  %lu /sec\n",       GET_STAT_TOTAL(rcvr->stats,rx),      GET_STAT_PER_SEC(rcvr->stats,rx) );
++    sprintf(str+strlen(str),"MBytes %lu  %lu Mbytes/sec\n", GET_STAT_TOTAL(rcvr->stats,rx_len) / (1024*1024),  GET_STAT_PER_SEC(rcvr->stats,rx_len) / (1024*1024));
++}
++
++void
++ep_rcvr_rail_fillout_stats(EP_RCVR_RAIL *rcvr_rail, char *str)
++{
++    sprintf(str+strlen(str),"Rx     %lu  %lu /sec\n",       GET_STAT_TOTAL(rcvr_rail->stats,rx),      GET_STAT_PER_SEC(rcvr_rail->stats,rx) );
++    sprintf(str+strlen(str),"MBytes %lu  %lu Mbytes/sec\n", GET_STAT_TOTAL(rcvr_rail->stats,rx_len) / (1024*1024),  GET_STAT_PER_SEC(rcvr_rail->stats,rx_len) / (1024*1024));
++}
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/ep/epcommsRx_elan3.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/epcommsRx_elan3.c   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/epcommsRx_elan3.c        2005-06-01 23:12:54.649431504 -0400
+@@ -0,0 +1,1776 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: epcommsRx_elan3.c,v 1.19.2.3 2004/11/15 11:05:49 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/epcommsRx_elan3.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++#include <elan/epsvc.h>
++#include <elan/epcomms.h>
++
++#include "kcomm_vp.h"
++#include "kcomm_elan3.h"
++#include "epcomms_elan3.h"
++#include "debug.h"
++
++#define RCVR_TO_RAIL(rcvrRail)                ((EP3_RAIL *) ((EP_RCVR_RAIL *) rcvrRail)->CommsRail->Rail)
++#define RCVR_TO_DEV(rcvrRail)         (RCVR_TO_RAIL(rcvrRail)->Device)
++#define RCVR_TO_SUBSYS(rcvrRail)      (((EP_RCVR_RAIL *) rcvrRail)->Rcvr->Subsys)
++
++static void RxDataEvent (EP3_RAIL *rail, void *arg);
++static void RxDataRetry (EP3_RAIL *rail, void *arg, E3_DMA_BE *dma, int status);
++static void RxDataVerify (EP3_RAIL *rail, void *arg, E3_DMA_BE *dma);
++
++static EP3_COOKIE_OPS RxDataCookieOps = 
++{
++    RxDataEvent,
++    RxDataRetry,
++    NULL, /* DmaCancelled */
++    RxDataVerify,
++};
++
++static void RxDoneEvent (EP3_RAIL *rail, void *arg);
++static void RxDoneRetry (EP3_RAIL *rail, void *arg, E3_DMA_BE *dma, int status);
++static void RxDoneVerify (EP3_RAIL *rail, void *arg, E3_DMA_BE *dma);
++
++static EP3_COOKIE_OPS RxDoneCookieOps = 
++{
++    RxDoneEvent,
++    RxDoneRetry,
++    NULL, /* DmaCancelled */
++    RxDoneVerify,
++};
++
++static int
++AllocateRxdRailBlock (EP3_RCVR_RAIL *rcvrRail)
++{
++    EP3_RAIL        *rail = RCVR_TO_RAIL(rcvrRail);
++    ELAN3_DEV         *dev  = rail->Device;
++    EP3_RXD_RAIL_BLOCK *blk;
++    EP3_RXD_RAIL       *rxdRail;
++    sdramaddr_t        pRxdElan;
++    EP3_RXD_RAIL_MAIN  *pRxdMain;
++    E3_Addr          pRxdElanAddr;
++    E3_Addr          pRxdMainAddr;
++    E3_BlockCopyEvent  event;
++    int                      i, j;
++    unsigned long      flags;
++
++    KMEM_ZALLOC (blk, EP3_RXD_RAIL_BLOCK *, sizeof (EP3_RXD_RAIL_BLOCK), 1);
++    if (blk == NULL)
++      return 0;
++
++    if ((pRxdElan = ep_alloc_elan (&rail->Generic, EP3_RXD_RAIL_ELAN_SIZE * EP3_NUM_RXD_PER_BLOCK, 0, &pRxdElanAddr)) == (sdramaddr_t) 0)
++    {
++      KMEM_FREE (blk, sizeof (EP3_RXD_RAIL_BLOCK));
++      return 0;
++    }
++
++    if ((pRxdMain = ep_alloc_main (&rail->Generic, EP3_RXD_RAIL_MAIN_SIZE * EP3_NUM_RXD_PER_BLOCK, 0, &pRxdMainAddr)) == (sdramaddr_t) 0)
++    {
++      ep_free_elan (&rail->Generic, pRxdElanAddr, EP3_RXD_RAIL_ELAN_SIZE * EP3_NUM_RXD_PER_BLOCK);
++      KMEM_FREE (blk, sizeof (EP3_RXD_RAIL_BLOCK));
++      return 0;
++    }
++    
++    if (ReserveDmaRetries (rail, EP3_NUM_RXD_PER_BLOCK, 0) != ESUCCESS)
++    {
++      ep_free_main (&rail->Generic, pRxdMainAddr, EP3_RXD_RAIL_MAIN_SIZE * EP3_NUM_RXD_PER_BLOCK);
++      ep_free_elan (&rail->Generic, pRxdElanAddr, EP3_RXD_RAIL_ELAN_SIZE * EP3_NUM_RXD_PER_BLOCK);
++      KMEM_FREE (blk, sizeof (EP3_RXD_RAIL_BLOCK));
++      return 0;
++    }
++
++    for (rxdRail = &blk->Rxd[0], i = 0; i < EP3_NUM_RXD_PER_BLOCK; i++, rxdRail++)
++    {
++      rxdRail->Generic.RcvrRail = (EP_RCVR_RAIL *) rcvrRail;
++      rxdRail->RxdElan          = pRxdElan;
++      rxdRail->RxdElanAddr      = pRxdElanAddr;
++      rxdRail->RxdMain          = pRxdMain;
++      rxdRail->RxdMainAddr      = pRxdMainAddr;
++
++      elan3_sdram_writel (dev, pRxdElan + offsetof (EP3_RXD_RAIL_ELAN, RxdMain),  0);
++      elan3_sdram_writel (dev, pRxdElan + offsetof (EP3_RXD_RAIL_ELAN, Next),     0);
++      elan3_sdram_writeq (dev, pRxdElan + offsetof (EP3_RXD_RAIL_ELAN, MainAddr), (long) rxdRail);
++
++      for (j = 0; j < EP_MAXFRAG; j++)
++      {
++          RegisterCookie (&rail->CookieTable, &rxdRail->ChainCookie[j], pRxdElanAddr + offsetof (EP3_RXD_RAIL_ELAN, ChainEvent[j]), &RxDataCookieOps, (void *) rxdRail);
++
++          event.ev_Type  = EV_TYPE_DMA | (pRxdElanAddr + offsetof (EP3_RXD_RAIL_ELAN, Dmas[j+1]));
++          event.ev_Count = 0;
++
++          elan3_sdram_copyl_to_sdram (dev, &event, pRxdElan + offsetof (EP3_RXD_RAIL_ELAN, ChainEvent[j]), sizeof (E3_BlockCopyEvent));
++      }
++      
++      RegisterCookie (&rail->CookieTable, &rxdRail->DataCookie, pRxdElanAddr + offsetof (EP3_RXD_RAIL_ELAN, DataEvent), &RxDataCookieOps, (void *) rxdRail);
++      RegisterCookie (&rail->CookieTable, &rxdRail->DoneCookie, pRxdElanAddr + offsetof (EP3_RXD_RAIL_ELAN, DoneEvent), &RxDoneCookieOps, (void *) rxdRail);
++
++      EP3_INIT_COPY_EVENT (event, rxdRail->DataCookie, pRxdMainAddr + offsetof (EP3_RXD_RAIL_MAIN, DataEvent), 1);
++      elan3_sdram_copyl_to_sdram (dev, &event, pRxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent), sizeof (E3_BlockCopyEvent));
++
++      EP3_INIT_COPY_EVENT (event, rxdRail->DoneCookie, pRxdMainAddr + offsetof (EP3_RXD_RAIL_MAIN, DoneEvent), 1);
++      elan3_sdram_copyl_to_sdram (dev, &event, pRxdElan + offsetof (EP3_RXD_RAIL_ELAN, DoneEvent), sizeof (E3_BlockCopyEvent));
++      
++      pRxdMain->DataEvent = EP3_EVENT_FREE;
++      pRxdMain->DoneEvent = EP3_EVENT_FREE;
++
++      /* move onto next descriptor */
++      pRxdElan     += EP3_RXD_RAIL_ELAN_SIZE;
++      pRxdElanAddr += EP3_RXD_RAIL_ELAN_SIZE;
++      pRxdMain      = (EP3_RXD_RAIL_MAIN *) ((unsigned long) pRxdMain + EP3_RXD_RAIL_MAIN_SIZE);
++      pRxdMainAddr += EP3_RXD_RAIL_MAIN_SIZE;
++    }
++
++    spin_lock_irqsave (&rcvrRail->FreeDescLock, flags);
++
++    list_add  (&blk->Link, &rcvrRail->DescBlockList);
++    rcvrRail->TotalDescCount += EP3_NUM_RXD_PER_BLOCK;
++    rcvrRail->FreeDescCount  += EP3_NUM_RXD_PER_BLOCK;
++
++    for (i = 0; i < EP3_NUM_RXD_PER_BLOCK; i++)
++      list_add (&blk->Rxd[i].Generic.Link, &rcvrRail->FreeDescList);
++
++    spin_unlock_irqrestore (&rcvrRail->FreeDescLock, flags);
++    
++    return 1;
++}
++
++static void
++FreeRxdRailBlock (EP3_RCVR_RAIL *rcvrRail, EP3_RXD_RAIL_BLOCK *blk)
++{
++    EP3_RAIL     *rail = RCVR_TO_RAIL(rcvrRail);
++    EP3_RXD_RAIL *rxdRail;
++    unsigned long flags;
++    int i, j;
++
++    spin_lock_irqsave (&rcvrRail->FreeDescLock, flags);
++
++    list_del (&blk->Link);
++
++    rcvrRail->TotalDescCount -= EP3_NUM_RXD_PER_BLOCK;
++
++    for (rxdRail = &blk->Rxd[0], i = 0; i < EP3_NUM_RXD_PER_BLOCK; i++, rxdRail++)
++    {
++
++      rcvrRail->FreeDescCount--;
++
++      list_del (&rxdRail->Generic.Link);
++      
++      for (j = 0; j < EP_MAXFRAG; j++)
++          DeregisterCookie (&rail->CookieTable, &rxdRail->ChainCookie[j]);
++      
++      DeregisterCookie (&rail->CookieTable, &rxdRail->DataCookie);
++      DeregisterCookie (&rail->CookieTable, &rxdRail->DoneCookie);
++    }
++
++    spin_unlock_irqrestore (&rcvrRail->FreeDescLock, flags);
++
++    ReleaseDmaRetries (rail, EP3_NUM_RXD_PER_BLOCK);
++
++    ep_free_main (&rail->Generic, blk->Rxd[0].RxdMainAddr, EP3_RXD_RAIL_MAIN_SIZE * EP3_NUM_RXD_PER_BLOCK);
++    ep_free_elan (&rail->Generic, blk->Rxd[0].RxdElanAddr, EP3_RXD_RAIL_ELAN_SIZE * EP3_NUM_RXD_PER_BLOCK);
++
++    KMEM_FREE (blk, sizeof (EP3_RXD_RAIL_BLOCK));
++}
++
++static EP3_RXD_RAIL *
++GetRxdRail (EP3_RCVR_RAIL *rcvrRail)
++{
++    EP3_RXD_RAIL *rxdRail;
++    unsigned long flags;
++    int low_on_rxds;
++
++    spin_lock_irqsave (&rcvrRail->FreeDescLock, flags);
++
++    if (list_empty (&rcvrRail->FreeDescList))
++      rxdRail = NULL;
++    else
++    {
++      rxdRail = list_entry (rcvrRail->FreeDescList.next, EP3_RXD_RAIL, Generic.Link);
++
++      list_del (&rxdRail->Generic.Link);
++
++      rcvrRail->FreeDescCount--;
++    }
++
++    /* Wakeup the descriptor primer thread if there's not many left */
++    low_on_rxds = (rcvrRail->FreeDescCount < ep_rxd_lowat);
++
++    spin_unlock_irqrestore (&rcvrRail->FreeDescLock, flags);
++
++    if (low_on_rxds)
++      ep_kthread_schedule (&RCVR_TO_SUBSYS(rcvrRail)->Thread, lbolt);
++
++    return (rxdRail);
++}
++
++static void
++FreeRxdRail (EP3_RCVR_RAIL *rcvrRail, EP3_RXD_RAIL *rxdRail)
++{
++    unsigned long flags;
++
++#if defined(DEBUG_ASSERT)
++    {
++      EP_RAIL  *rail = (EP_RAIL *) RCVR_TO_RAIL(rcvrRail);
++      ELAN3_DEV *dev = RCVR_TO_DEV (rcvrRail);
++
++      EP_ASSERT (rail, rxdRail->Generic.RcvrRail == &rcvrRail->Generic);
++      
++      EP_ASSERT (rail, rxdRail->RxdMain->DataEvent == EP3_EVENT_PRIVATE);
++      EP_ASSERT (rail, rxdRail->RxdMain->DoneEvent == EP3_EVENT_PRIVATE);
++      EP_ASSERT (rail, SDRAM_ASSERT (elan3_sdram_readl (dev, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Count)) == 0));
++      EP_ASSERT (rail, SDRAM_ASSERT (elan3_sdram_readl (dev, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DoneEvent.ev_Count)) == 0));
++
++      rxdRail->RxdMain->DataEvent = EP3_EVENT_FREE;
++      rxdRail->RxdMain->DoneEvent = EP3_EVENT_FREE;
++    }
++#endif
++
++    spin_lock_irqsave (&rcvrRail->FreeDescLock, flags);
++    
++    list_add (&rxdRail->Generic.Link, &rcvrRail->FreeDescList);
++
++    rcvrRail->FreeDescCount++;
++
++    if (rcvrRail->FreeDescWaiting)
++    {
++      rcvrRail->FreeDescWaiting--;
++      kcondvar_wakeupall (&rcvrRail->FreeDescSleep, &rcvrRail->FreeDescLock);
++    }
++
++    spin_unlock_irqrestore (&rcvrRail->FreeDescLock, flags);
++}
++
++static void
++BindRxdToRail (EP_RXD *rxd, EP3_RXD_RAIL *rxdRail)
++{
++    EP3_RAIL *rail = RCVR_TO_RAIL (rxdRail->Generic.RcvrRail);
++
++    ASSERT (SPINLOCK_HELD (&rxd->Rcvr->Lock));
++
++    EPRINTF3 (DBG_RCVR, "%s: BindRxdToRail: rxd=%p rxdRail=%p\n",  rail->Generic.Name, rxd, rxdRail);
++
++    elan3_sdram_writel (rail->Device, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, RxdMain), rxd->NmdMain.nmd_addr);                       /* PCI write */
++
++    rxd->RxdRail         = &rxdRail->Generic;
++    rxdRail->Generic.Rxd = rxd;
++}
++
++static void
++UnbindRxdFromRail (EP_RXD *rxd, EP3_RXD_RAIL *rxdRail)
++{
++    EP3_RCVR_RAIL *rcvrRail = (EP3_RCVR_RAIL *) rxdRail->Generic.RcvrRail;
++
++    ASSERT (SPINLOCK_HELD (&rxd->Rcvr->Lock));
++    ASSERT (rxd->RxdRail == &rxdRail->Generic && rxdRail->Generic.Rxd == rxd);
++    
++    EPRINTF3 (DBG_RCVR, "%s: UnbindRxdFromRail: rxd=%p rxdRail=%p\n",  RCVR_TO_RAIL(rxdRail->Generic.RcvrRail)->Generic.Name, rxd, rxdRail);
++
++    rxd->RxdRail         = NULL;
++    rxdRail->Generic.Rxd = NULL;
++
++    if (rcvrRail->CleanupWaiting)
++      kcondvar_wakeupall (&rcvrRail->CleanupSleep, &rxd->Rcvr->Lock);
++    rcvrRail->CleanupWaiting = 0;
++}
++
++static void
++LockRcvrThread (EP3_RCVR_RAIL *rcvrRail)
++{
++    EP_COMMS_RAIL     *commsRail   = rcvrRail->Generic.CommsRail;
++    EP3_RAIL          *rail        = RCVR_TO_RAIL(rcvrRail);
++    ELAN3_DEV       *dev         = rail->Device;
++    sdramaddr_t        sle         = rcvrRail->RcvrElan + offsetof (EP3_RCVR_RAIL_ELAN, ThreadLock);
++    EP3_SPINLOCK_MAIN *sl          = &rcvrRail->RcvrMain->ThreadLock;
++    E3_uint32          RestartBits = 0;
++    int                delay       = 1;
++    E3_uint32          seq;
++    E3_uint32          reg;
++
++    ASSERT (SPINLOCK_HELD (&rcvrRail->Generic.Rcvr->Lock));
++
++    mb();
++    elan3_sdram_writel (dev, sle + offsetof (EP3_SPINLOCK_ELAN, sl_lock), 1);
++    mb();
++    seq = elan3_sdram_readl (dev, sle + offsetof (EP3_SPINLOCK_ELAN, sl_seq));
++    while (seq != sl->sl_seq)
++    {
++      while (sl->sl_seq == (seq - 1))
++      {
++          mb();
++
++          if ((read_reg32 (dev, Exts.InterruptReg) & (INT_TProc | INT_TProcHalted)) != 0 && spin_trylock (&dev->IntrLock))
++          {
++              reg=read_reg32 (dev, Exts.InterruptReg);
++              ELAN_REG_REC(reg);
++
++              if ((reg & (INT_TProc | INT_TProcHalted)) != 0&& 
++                  elan3_sdram_readl (dev, sle + offsetof (EP3_SPINLOCK_ELAN, sl_seq)) != sl->sl_seq)
++              {
++                  EPRINTF1 (DBG_RCVR, "%s: LockRcvrThread - thread trapped\n", rail->Generic.Name);
++
++                  /* The thread processor has *really* trapped, and the spinlock is still held.
++                   * thus is must have trapped due to a network error - we need to complete the
++                   * actions required for this envelope, since we may be spin-locking the receiver
++                   * to search the dma retry lists for a particular dma.  So must ensure that
++                   * if the thread had trapped then the dma has been queued onto the retry list
++                   * *before* we inspect them.
++                   */
++                  IncrStat (commsRail, LockRcvrTrapped);
++
++                  /* We're going to generate a spurious interrupt here - since we will
++                   * handle the thread processor trap directly */
++                  ELAN_REG_REC(reg);
++                  if (HandleTProcTrap (dev, &RestartBits))
++                  {
++                      /* NOTE - this is not an assert, since the "store" to unlock the lock could
++                       *        be held up on the PCI interface, whilst the thread processor has
++                       *        gone on and switched to a new thread, which has then trapped, and
++                       *        our read of the InterruptReg can overtake the unlock write.
++                       *
++                       * ASSERT (dev->ThreadTrap->Registers[REG_GLOBALS + (1^WordEndianFlip)] == 
++                       *         elan3_sdram_readl (dev, rcvr->RcvrElan + offsetof (EP_RCVR_ELAN, PendingRxDescsElan)));
++                       */
++
++                      PULSE_SCHED_STATUS (dev, RestartBits);
++
++                      DeliverTProcTrap (dev, dev->ThreadTrap, INT_TProc);
++                  }
++              }
++              spin_unlock (&dev->IntrLock);
++          }
++          
++          DELAY (delay); delay++;
++      }
++      seq = elan3_sdram_readl (dev, sle + offsetof (EP3_SPINLOCK_ELAN, sl_seq));
++    }
++}
++
++static void
++UnlockRcvrThread (EP3_RCVR_RAIL *rcvrRail)
++{
++    EP3_RAIL   *rail = RCVR_TO_RAIL(rcvrRail);
++    sdramaddr_t sle  = rcvrRail->RcvrElan + offsetof (EP3_RCVR_RAIL_ELAN, ThreadLock);
++
++    mb();
++    elan3_sdram_writel (rail->Device, sle + offsetof (EP3_SPINLOCK_ELAN, sl_lock), 0);
++    mmiob(); 
++}
++
++void
++CompleteEnvelope (EP3_RAIL *rail, E3_Addr rxdElanAddr, E3_uint32 PAckVal)
++{
++    ELAN3_DEV         *dev       = rail->Device;
++    sdramaddr_t        rxdElan   = ep_elan2sdram (&rail->Generic, rxdElanAddr);
++    EP3_RXD_RAIL      *rxdRail   = (EP3_RXD_RAIL *) (unsigned long) elan3_sdram_readq (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, MainAddr));
++    EP_RXD_MAIN       *rxdMain   = rxdRail->Generic.Rxd->RxdMain;
++    EP_ENVELOPE       *env       = &rxdMain->Envelope;
++    EP3_RCVR_RAIL     *rcvrRail  = (EP3_RCVR_RAIL *) rxdRail->Generic.RcvrRail;
++    EP_COMMS_RAIL     *commsRail = rcvrRail->Generic.CommsRail;
++    EP_RCVR           *rcvr      = rcvrRail->Generic.Rcvr;
++    sdramaddr_t        queue     = ((EP3_COMMS_RAIL *) commsRail)->QueueDescs + rcvr->Service * sizeof (EP3_InputQueue);
++    sdramaddr_t        sle       = rcvrRail->RcvrElan + offsetof (EP3_RCVR_RAIL_ELAN, ThreadLock);
++    EP3_SPINLOCK_MAIN *sl        = &rcvrRail->RcvrMain->ThreadLock;
++    int               nodeId;
++    EP_NODE_RAIL     *nodeRail;
++    E3_DMA_BE         dma;
++    E3_Addr           nfptr;
++    E3_Addr         next;
++
++    ASSERT (commsRail->Rail == &rail->Generic);
++    ASSERT (rxdElanAddr == elan3_sdram_readl (dev, rcvrRail->RcvrElan + offsetof (EP3_RCVR_RAIL_ELAN, PendingDescs)));
++
++    IncrStat (commsRail, CompleteEnvelope);
++
++    /* We don't need to aquire the NodeLock here (however we might be holding it),
++     * since this can only get called while the node is connected, or disconnecting.
++     * If the node is disconnecting, then we can get called from FlushDisconnecting()
++     * while holding the NodeLock - after we cannot get called again until the node 
++     * has reconnected from scratch.
++     */
++    /* Copy the envelope information */
++    nfptr = elan3_sdram_readl (dev, queue + offsetof (EP3_InputQueue, q_fptr));
++
++    if (nfptr == elan3_sdram_readl (dev, queue + offsetof (EP3_InputQueue, q_top)))
++      nfptr = elan3_sdram_readl (dev, queue + offsetof (EP3_InputQueue, q_base));
++    else
++      nfptr += elan3_sdram_readl (dev, queue + offsetof (EP3_InputQueue, q_size));
++
++    /* Copy the envelope and payload (unconditionally) */
++    elan3_sdram_copyl_from_sdram (dev, rcvrRail->InputQueueBase + (nfptr - rcvrRail->InputQueueAddr), env, EP_ENVELOPE_SIZE + EP_PAYLOAD_SIZE);
++
++    ASSERT (env->Version == EP_ENVELOPE_VERSION);
++
++    /* Copy the received message length */
++    rxdMain->Len = elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, Data.nmd_len));
++      
++    /* Remove the RXD from the pending desc list */
++    if ((next = elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, Next))) == 0)
++      rcvrRail->RcvrMain->PendingDescsTailp = 0;
++    elan3_sdram_writel (dev, rcvrRail->RcvrElan + offsetof (EP3_RCVR_RAIL_ELAN, PendingDescs), next);
++
++    /* Copy the DMA descriptor to queue on the approriate retry list */
++    elan3_sdram_copyq_from_sdram (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, Dmas[0]), &dma, sizeof (E3_DMA));       /* PCI read block */
++    
++    EP_ASSERT (&rail->Generic, dma.s.dma_direction == DMA_READ);;
++
++#if defined(DEBUG_ASSERT) && defined(DEBUG_SDRAM_ASSERT)
++    /* NOTE: not an assertion, since the thread packet could have successfully
++     *       transferred the "put" dma to the far side - which could then have
++     *       completed - but the far side will see a network error which will
++     *       cause the virtual circuit to be dropped by the far side and this 
++     *       DMA will be removed */
++    if (rxdRail->RxdMain->DataEvent != EP3_EVENT_ACTIVE ||
++      elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Count)) != 1)
++    {
++      printk ("CompleteEnvelope: suspicious dma : Node=%d DataBlock=%d Event=%d\n", 
++              env->NodeId, rxdRail->RxdMain->DataEvent, 
++              elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Count)));
++    }
++#endif
++    
++    EPRINTF6 (DBG_RCVR, "%s: CompleteEnvelope: rxd=%p NodeId=%d Xid=%llx Cookies=%08x,%08x\n", commsRail->Rail->Name,
++            rxdRail, env->NodeId, (long long) env->Xid.Unique, dma.s.dma_srcCookieVProc, dma.s.dma_destCookieVProc);
++
++    /* we MUST convert this into a DMA_READ_REQUEUE dma as if we don't the DMA descriptor will
++     * be read from the EP_RETRY_DMA rather than the original DMA - this can then get reused 
++     * and an incorrect DMA descriptor sent */
++    dma.s.dma_source    = rxdRail->RxdElanAddr + offsetof (EP3_RXD_RAIL_ELAN, Dmas[0]);
++    dma.s.dma_direction = (dma.s.dma_direction & ~DMA_READ) | DMA_READ_REQUEUE;
++    
++    nodeId   = EP_VP_TO_NODE(dma.s.dma_srcVProc);
++    nodeRail = &rail->Generic.Nodes[nodeId];
++
++    ASSERT (nodeRail->State >= EP_NODE_CONNECTED && nodeRail->State <= EP_NODE_LOCAL_PASSIVATE);
++
++    if (PAckVal != E3_PAckOk)
++    {
++      if (nodeRail->State == EP_NODE_CONNECTED)
++          QueueDmaForRetry (rail, &dma, EP_RETRY_LOW_PRI_RETRY);
++      else
++          QueueDmaOnStalledList (rail, &dma);
++    }
++
++    /* Finaly forcefully drop the spinlock for the thread */
++    sl->sl_seq = elan3_sdram_readl (dev, sle + offsetof (EP3_SPINLOCK_ELAN, sl_seq));
++
++    wmb();
++}
++
++void
++StallThreadForNoDescs (EP3_RAIL *rail, E3_Addr rcvrElanAddr, E3_Addr sp)
++{
++    ELAN3_DEV      *dev       = rail->Device;
++    sdramaddr_t    rcvrElan   = ep_elan2sdram (&rail->Generic, rcvrElanAddr);
++    EP3_RCVR_RAIL  *rcvrRail  = (EP3_RCVR_RAIL *) (unsigned long) elan3_sdram_readq (dev, rcvrElan + offsetof (EP3_RCVR_RAIL_ELAN, MainAddr));
++    EP_RCVR        *rcvr      = rcvrRail->Generic.Rcvr;
++    EP_COMMS_RAIL  *commsRail = rcvrRail->Generic.CommsRail;
++
++    EPRINTF3 (DBG_RCVR, "%s: StallThreadForNoDescs - rcvrRail=%p sp=%x\n", commsRail->Rail->Name, rcvrRail, sp);
++    
++    IncrStat (commsRail, StallThread);
++
++    /* NOTE: spin lock not required as thread is trapped */
++    
++    if (rcvrRail->RcvrMain->PendingDescsTailp != 0)
++    {
++      EPRINTF1 (DBG_RCVR, "%s: StallThreadForNoDescs - pending descriptors, wakeup thread\n", commsRail->Rail->Name);
++      
++      /*
++       * A receive buffer was queued after the thread had decided to go to 
++       * sleep, but before the event interrupt occured.  Just restart the
++       * thread to consume the envelope.
++       */
++      IssueRunThread (rail, sp);
++    }
++    else
++    {
++      EPRINTF1 (DBG_RCVR, "%s: StallThreadForNoDescs - set ThreadWaiting\n", commsRail->Rail->Name);
++      
++      IncrStat (commsRail, ThrdWaiting);
++
++      /* Mark the rcvr as waiting for a rxd, and schedule a call of ep_check_rcvr
++       * to attempt to "steal" a descriptor from a different rail */
++      rcvrRail->ThreadWaiting = sp;
++
++      ep_kthread_schedule (&rcvr->Subsys->Thread, lbolt);
++    }
++}
++
++void
++StallThreadForHalted (EP3_RAIL *rail, E3_Addr rcvrElanAddr, E3_Addr sp)
++{
++    ELAN3_DEV     *dev       = rail->Device;
++    sdramaddr_t    rcvrElan  = ep_elan2sdram (&rail->Generic, rcvrElanAddr);
++    EP3_RCVR_RAIL *rcvrRail  = (EP3_RCVR_RAIL *) (unsigned long) elan3_sdram_readq (dev, rcvrElan + offsetof (EP3_RCVR_RAIL_ELAN, MainAddr));
++    EP_RCVR       *rcvr      = rcvrRail->Generic.Rcvr;
++    unsigned long  flags     = 0;
++
++    spin_lock_irqsave (&rcvr->Lock, flags);
++
++    rcvrRail->ThreadHalted = sp;
++
++    EPRINTF2 (DBG_EPTRAP, "%s: StallThreadForHalted: sp=%08x\n", rail->Generic.Name, sp);
++
++    if (rcvrRail->CleanupWaiting)
++      kcondvar_wakeupone (&rcvrRail->CleanupSleep, &rcvr->Lock);
++    rcvrRail->CleanupWaiting = 0;
++
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++}
++/*
++ * RxDataEvent: arg == EP3_RXD_RAIL
++ *   Called on completion of receiving data.
++ */
++static void
++RxDataEvent (EP3_RAIL *rail, void *arg)
++{
++    EP3_RXD_RAIL  *rxdRail  = (EP3_RXD_RAIL *) arg;
++    EP3_RCVR_RAIL *rcvrRail = (EP3_RCVR_RAIL *) rxdRail->Generic.RcvrRail;
++    EP_RXD        *rxd      = rxdRail->Generic.Rxd;
++    EP_ENVELOPE   *env      = &rxd->RxdMain->Envelope;
++    EP_RCVR       *rcvr     = rxd->Rcvr;
++    ELAN3_DEV   *dev      = rail->Device;
++    unsigned long flags;
++    int delay = 1;
++
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    for (;;)
++    {
++      if (EP3_EVENT_FIRED (rxdRail->DataCookie, rxdRail->RxdMain->DataEvent))
++          break;
++
++      if (EP3_EVENT_FIRING (dev, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent), rxdRail->DataCookie, rxdRail->RxdMain->DataEvent))
++      {
++          if (delay > EP3_EVENT_FIRING_TLIMIT)
++              panic ("RxDataEvent: events set but block copy not completed\n");
++          DELAY(delay);
++          delay <<= 1;
++      }
++      else
++      {
++          printk ("%s: RxDataEvent: rxd %p not complete [%x,%x,%x]\n", rail->Generic.Name, rxd, rxdRail->RxdMain->DataEvent,
++                  elan3_sdram_readl (dev, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Count)),
++                  elan3_sdram_readl (dev, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Type)));
++          
++          spin_unlock_irqrestore (&rcvr->Lock, flags);
++          return;
++      }
++      mb();
++    }
++
++    /*
++     * Note, since the thread will have sent the "get" dma before copying the
++     * envelope, we must check that it has completed doing this,  if not then
++     * it might be that the thread trapped due to a network error, so we must
++     * spinlock against the thread 
++     */
++    if (rxd->RxdMain->Len == EP_RXD_PENDING)
++    {
++      LockRcvrThread (rcvrRail);
++      UnlockRcvrThread (rcvrRail);
++
++      ASSERT (env->Version == EP_ENVELOPE_VERSION && rxd->RxdMain->Len != EP_RXD_PENDING);
++    }
++
++    EPRINTF7 (DBG_RCVR, "%s: RxDataEvent: rxd=%p rxdRail=%p completed from elan node %d [XID=%llx] Length %d State %x\n", 
++            rail->Generic.Name, rxd, rxdRail, env->NodeId, (long long) env->Xid.Unique, rxd->RxdMain->Len, rxd->State);
++
++    EP_ASSERT (&rail->Generic, rxd->State == EP_RXD_RECEIVE_ACTIVE || rxd->State == EP_RXD_PUT_ACTIVE || rxd->State == EP_RXD_GET_ACTIVE);
++    EP_ASSERT (&rail->Generic, SDRAM_ASSERT (elan3_sdram_readl (dev, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Count)) == 0));     /* PCI read */
++    EP_ASSERT (&rail->Generic, rxdRail->RxdMain->DoneEvent == EP3_EVENT_PRIVATE);
++
++    rxdRail->RxdMain->DataEvent = EP3_EVENT_PRIVATE;
++    rxd->Data.nmd_attr          = EP_RAIL2RAILMASK (rail->Generic.Number);
++
++    if (rxd->RxdMain->Len >= 0 && EP_IS_RPC(env->Attr))
++      rxd->State = EP_RXD_RPC_IN_PROGRESS;
++    else
++    {
++      rxd->State = EP_RXD_COMPLETED;
++
++      /* remove from active list */
++      list_del (&rxd->Link);
++
++      UnbindRxdFromRail (rxd, rxdRail);
++      FreeRxdRail (rcvrRail, rxdRail);
++    }
++
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++    ep_rxd_received (rxd);
++
++}
++
++/* 
++ * RxDataRetry: arg == EP3_RXD_RAIL
++ *   Called on retry of "get" dma of large transmit data
++ *   and rpc_get/rpc_put and "put" of datavec of rpc completion.
++ */
++static void
++RxDataRetry (EP3_RAIL *rail, void *arg, E3_DMA_BE *dma, int status)
++{
++    EP3_RXD_RAIL  *rxdRail  = (EP3_RXD_RAIL *) arg;
++    EP_COMMS_RAIL *commsRail = rxdRail->Generic.RcvrRail->CommsRail;
++    EP_RXD        *rxd       = rxdRail->Generic.Rxd;
++
++#if defined(DEBUG_ASSERT)
++    RxDataVerify (rail, arg, dma);
++#endif
++
++    IncrStat (commsRail, RxDataRetry);
++
++    EPRINTF4 (DBG_RCVR, "%s: RxDataRetry: rcvr %p rxd %p [XID=%llx]\n", rail->Generic.Name, rxd->Rcvr, rxd, (long long) rxd->RxdMain->Envelope.Xid.Unique);
++
++    QueueDmaForRetry (rail, dma, EP_RETRY_LOW_PRI_RETRY + ep_backoff (&rxdRail->Backoff, EP_BACKOFF_DATA));
++}
++
++static void
++RxDataVerify (EP3_RAIL *rail, void *arg, E3_DMA_BE *dma)
++{
++#if defined(DEBUG_ASSERT)
++    EP3_RXD_RAIL   *rxdRail  = (EP3_RXD_RAIL *) arg;
++    EP_RXD        *rxd       = rxdRail->Generic.Rxd;
++
++    if (dma->s.dma_direction == DMA_WRITE)
++    {
++      EP_ASSERT (&rail->Generic, 
++                 (rxd->State == EP_RXD_RECEIVE_ACTIVE  && rxdRail->RxdMain->DataEvent == EP3_EVENT_ACTIVE && rxdRail->RxdMain->DoneEvent == EP3_EVENT_PRIVATE) ||
++                 (rxd->State == EP_RXD_PUT_ACTIVE      && rxdRail->RxdMain->DataEvent == EP3_EVENT_ACTIVE && rxdRail->RxdMain->DoneEvent == EP3_EVENT_PRIVATE) ||
++                 (rxd->State == EP_RXD_COMPLETE_ACTIVE && rxdRail->RxdMain->DataEvent == EP3_EVENT_PRIVATE && rxdRail->RxdMain->DoneEvent == EP3_EVENT_ACTIVE));
++      EP_ASSERT (&rail->Generic, SDRAM_ASSERT (rxd->State == EP_RXD_COMPLETE_ACTIVE ?
++                                               elan3_sdram_readl (rail->Device, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DoneEvent.ev_Count)) == 1:            /* PCI read */
++                                               elan3_sdram_readl (rail->Device, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Count)) == 1));          /* PCI read */
++    }
++    else
++    {
++      EP_ASSERT (&rail->Generic, dma->s.dma_direction == DMA_READ_REQUEUE);
++      
++#if defined(DEBUG_SDRAM_ASSERT)
++      /* NOTE: not an assertion, since the "get" DMA can still be running if
++       *       it's packet got a network error - and then the "put" from the
++       *       far side has completed - however the virtual circuit should
++       *       then be dropped by the far side and this DMA will be removed */
++      if (EP_VP_TO_NODE(dma->s.dma_srcVProc) != ep_rxd_node(rxd) || 
++          (rxd->State != EP_RXD_RECEIVE_ACTIVE && rxd->State != EP_RXD_GET_ACTIVE) ||
++          rxdRail->RxdMain->DataEvent != EP3_EVENT_ACTIVE ||
++          elan3_sdram_readl (rail->Device, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Count)) != 1)
++      {
++          EPRINTF6 (DBG_RCVR, "%s: RxDataRetry: suspicious dma : VProc=%d NodeId=%d State=%d DataBlock=%x Event=%d\n",  
++                    rail->Generic.Name, EP_VP_TO_NODE(dma->s.dma_srcVProc), ep_rxd_node(rxd), rxd->State, rxdRail->RxdMain->DataEvent, 
++                    elan3_sdram_readl (rail->Device, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Count)));
++      }
++#endif /* defined(DEBUG_SDRAM_ASSERT) */
++    }
++#endif /* DEBUG_ASSERT */
++}
++
++/*
++ * RxDoneEvent: arg == EP_RXD
++ *   Called on completion of large receive.
++ */
++static void
++RxDoneEvent (EP3_RAIL *rail, void *arg)
++{
++    EP3_RXD_RAIL  *rxdRail   = (EP3_RXD_RAIL *) arg;
++    EP3_RCVR_RAIL *rcvrRail  = (EP3_RCVR_RAIL *) rxdRail->Generic.RcvrRail;
++    EP_COMMS_RAIL *commsRail = rcvrRail->Generic.CommsRail;
++    EP_RXD        *rxd       = rxdRail->Generic.Rxd;
++    EP_RCVR       *rcvr      = rxd->Rcvr;
++    ELAN3_DEV   *dev       = rail->Device;
++    int            delay     = 1;
++    unsigned long  flags;
++
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    for (;;)
++    {
++      if (EP3_EVENT_FIRED (rxdRail->DoneCookie, rxdRail->RxdMain->DoneEvent))
++          break;
++      
++      if (EP3_EVENT_FIRING (dev, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DoneEvent), rxdRail->DoneCookie, rxdRail->RxdMain->DoneEvent))
++      {
++          if (delay > EP3_EVENT_FIRING_TLIMIT)
++              panic ("RxDoneEvent: events set but block copy not completed\n");
++          DELAY(delay);
++          delay <<= 1;
++      }
++      else
++      {
++          printk ("RxDoneEvent: rxd %p not complete [%x,%x.%x]\n", rxd, rxdRail->RxdMain->DoneEvent,
++                  elan3_sdram_readl (dev, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DoneEvent.ev_Count)),
++                  elan3_sdram_readl (dev, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DoneEvent.ev_Type)));
++          
++          spin_unlock_irqrestore (&rcvr->Lock, flags);
++          return;
++      }
++      mb();
++    }
++
++    EPRINTF4 (DBG_RCVR, "%s: RxDoneEvent: rxd %p completed from elan node %d [XID=%llx]\n", 
++            commsRail->Rail->Name, rxd, rxd->RxdMain->Envelope.NodeId, (long long) rxd->RxdMain->Envelope.Xid.Unique);
++    
++    IncrStat (commsRail, RxDoneEvent);
++
++    EP_ASSERT (&rail->Generic, rxdRail->RxdMain->DataEvent  == EP3_EVENT_PRIVATE);
++    EP_ASSERT (&rail->Generic, EP3_EVENT_FIRED (rxdRail->DoneCookie, rxdRail->RxdMain->DoneEvent));
++    EP_ASSERT (&rail->Generic, SDRAM_ASSERT (elan3_sdram_readl (dev, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Count)) == 0));     /* PCI read */
++    EP_ASSERT (&rail->Generic, SDRAM_ASSERT (elan3_sdram_readl (dev, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DoneEvent.ev_Count)) == 0));     /* PCI read */
++
++    /* mark rxd as private  */
++    rxdRail->RxdMain->DoneEvent = EP3_EVENT_PRIVATE;
++
++    /* remove from active list */
++    list_del (&rxd->Link);
++
++    UnbindRxdFromRail (rxd, rxdRail);
++    FreeRxdRail (rcvrRail, rxdRail);
++      
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++
++    rxd->Handler (rxd);
++}
++
++/* 
++ * RxDoneRetry: arg == EP_RXD
++ *   Called on retry of "put" of RPC completion status block
++ */
++static void
++RxDoneRetry (EP3_RAIL *rail, void *arg, E3_DMA_BE *dma, int status)
++{
++    EP3_RXD_RAIL  *rxdRail   = (EP3_RXD_RAIL *) arg;
++    EP_COMMS_RAIL *commsRail = rxdRail->Generic.RcvrRail->CommsRail;
++    EP_RXD        *rxd       = rxdRail->Generic.Rxd;
++
++#if defined(DEBUG_ASSERT)
++    RxDoneVerify (rail, arg, dma);
++#endif
++
++    IncrStat (commsRail, RxDoneRetry);
++
++    EPRINTF4 (DBG_RCVR, "%s: RxDoneRetry: rcvr %p rxd %p [XID=%llx]\n", commsRail->Rail->Name, rxd->Rcvr, rxd, (long long) rxd->RxdMain->Envelope.Xid.Unique);
++
++    QueueDmaForRetry (rail, dma, EP_RETRY_LOW_PRI_RETRY + ep_backoff (&rxdRail->Backoff, EP_BACKOFF_DONE));
++}
++
++static void
++RxDoneVerify (EP3_RAIL *rail, void *arg, E3_DMA_BE *dma)
++{
++#if defined(DEBUG_ASSERT)
++    EP3_RXD_RAIL *rxdRail = (EP3_RXD_RAIL *) arg;
++    EP_RXD       *rxd     = rxdRail->Generic.Rxd;
++
++    EP_ASSERT (&rail->Generic, dma->s.dma_direction == DMA_WRITE && EP_VP_TO_NODE(dma->s.dma_destVProc) == ep_rxd_node(rxd));
++    EP_ASSERT (&rail->Generic, rxd->State == EP_RXD_COMPLETE_ACTIVE && rxdRail->RxdMain->DoneEvent  == EP3_EVENT_ACTIVE);
++    EP_ASSERT (&rail->Generic, SDRAM_ASSERT (elan3_sdram_readl (rail->Device, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DoneEvent.ev_Count)) == 1));    /* PCI read */
++#endif /* defined(DEBUG_ASSERT) */
++}
++
++int
++ep3rcvr_queue_rxd (EP_RXD *rxd, EP_RCVR_RAIL *r)
++{
++    EP3_RCVR_RAIL *rcvrRail = (EP3_RCVR_RAIL *) r;
++    EP3_RAIL      *rail     = RCVR_TO_RAIL(rcvrRail);
++    ELAN3_DEV     *dev      = rail->Device;
++    EP3_RXD_RAIL  *rxdRail;
++
++    ASSERT ( SPINLOCK_HELD(&rxd->Rcvr->Lock));
++
++    if ((rxdRail = GetRxdRail (rcvrRail)) == NULL)
++      return 0;
++
++    /* Flush the Elan TLB if mappings have changed */
++    ep_perrail_dvma_sync (&rail->Generic);
++
++    elan3_sdram_writel (dev, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, Data.nmd_addr), rxd->Data.nmd_addr);             /* PCI write */
++    elan3_sdram_writel (dev, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, Data.nmd_len),  rxd->Data.nmd_len);              /* PCI write */
++    elan3_sdram_writel (dev, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, Data.nmd_attr), rxd->Data.nmd_attr);             /* PCI write */
++
++    /* Bind the rxdRail and rxd together */
++    BindRxdToRail (rxd, rxdRail);
++    
++    /* Mark as active */
++    elan3_sdram_writel (dev, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Count), 1);
++
++    rxdRail->RxdMain->DataEvent  = EP3_EVENT_ACTIVE;
++    rxdRail->RxdMain->DoneEvent  = EP3_EVENT_PRIVATE;
++
++    /* Interlock with StallThreadForNoDescs */
++    spin_lock (&dev->IntrLock);
++
++    EPRINTF4 (DBG_RCVR, "%s: ep3rcvr_queue_rxd: rcvr %p rxd %p rxdRail %p\n", rail->Generic.Name, rxd->Rcvr, rxd, rxdRail);
++
++    EP3_SPINENTER (dev, rcvrRail->RcvrElan + offsetof (EP3_RCVR_RAIL_ELAN, PendingLock), &rcvrRail->RcvrMain->PendingLock);
++
++    elan3_sdram_writel (dev, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, Next), 0);                                       /* PCI write */
++    if (rcvrRail->RcvrMain->PendingDescsTailp == 0)
++      elan3_sdram_writel (dev, rcvrRail->RcvrElan + offsetof (EP3_RCVR_RAIL_ELAN, PendingDescs), rxdRail->RxdElanAddr);       /* PCI write */
++    else
++      elan3_sdram_writel (dev, rcvrRail->RcvrMain->PendingDescsTailp, rxdRail->RxdElanAddr);                          /* PCI write */
++    rcvrRail->RcvrMain->PendingDescsTailp = rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, Next);
++    
++    EP3_SPINEXIT (dev, rcvrRail->RcvrElan + offsetof (EP3_RCVR_RAIL_ELAN, PendingLock), &rcvrRail->RcvrMain->PendingLock);
++
++    /* If the thread has paused because it was woken up with no receive buffer */
++    /* ready, then wake it up to process the one we've just added */
++    if (rcvrRail->ThreadWaiting)
++    {
++      EPRINTF1 (DBG_RCVR, "%s: DoReceive: ThreadWaiting - restart thread\n", rail->Generic.Name);
++
++      IssueRunThread (rail, rcvrRail->ThreadWaiting);
++
++      rcvrRail->ThreadWaiting = (E3_Addr) 0;
++    }
++
++    spin_unlock (&dev->IntrLock);
++
++    return 1;
++}
++
++void
++ep3rcvr_rpc_put (EP_RXD *rxd, EP_NMD *local, EP_NMD *remote, unsigned nFrags)
++{
++    EP3_RXD_RAIL      *rxdRail   = (EP3_RXD_RAIL *) rxd->RxdRail;
++    EP3_RCVR_RAIL     *rcvrRail  = (EP3_RCVR_RAIL *) rxdRail->Generic.RcvrRail;
++    EP3_RAIL          *rail      = RCVR_TO_RAIL (rcvrRail);
++    ELAN3_DEV         *dev       = rail->Device;
++
++    EP3_RXD_RAIL_MAIN *rxdMain   = rxdRail->RxdMain;
++    sdramaddr_t        rxdElan   = rxdRail->RxdElan;
++    EP_ENVELOPE       *env       = &rxd->RxdMain->Envelope;
++    E3_DMA_BE        dmabe;
++    int                      i, len;
++
++    EP_ASSERT (&rail->Generic, rxd->State == EP_RXD_PUT_ACTIVE);
++    EP_ASSERT (&rail->Generic, rxdMain->DataEvent == EP3_EVENT_PRIVATE && rxdMain->DoneEvent == EP3_EVENT_PRIVATE);
++    EP_ASSERT (&rail->Generic, SDRAM_ASSERT (elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Count)) == 0));      /* PCI read */
++    EP_ASSERT (&rail->Generic, SDRAM_ASSERT (elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, DoneEvent.ev_Count)) == 0));      /* PCI read */
++
++    /* Flush the Elan TLB if mappings have changed */
++    ep_perrail_dvma_sync (&rail->Generic);
++    
++    /* Generate the DMA chain to put the data in two loops to burst
++     * the data across the PCI bus */
++    for (len = 0, i = (nFrags-1), local += (nFrags-1), remote += (nFrags-1); i >= 0;   len += local->nmd_len, i--, local--, remote--)
++    {
++      dmabe.s.dma_type            = E3_DMA_TYPE(DMA_BYTE, DMA_WRITE, DMA_NORMAL, EP3_DMAFAILCOUNT);
++      dmabe.s.dma_size            = local->nmd_len;
++      dmabe.s.dma_source          = local->nmd_addr;
++      dmabe.s.dma_dest            = remote->nmd_addr;
++      dmabe.s.dma_destEvent       = (E3_Addr) 0;
++      dmabe.s.dma_destCookieVProc = EP_VP_DATA (env->NodeId);
++      if (i == (nFrags-1))
++          dmabe.s.dma_srcEvent    = rxdRail->RxdElanAddr + offsetof (EP3_RXD_RAIL_ELAN, DataEvent);
++      else
++          dmabe.s.dma_srcEvent    = rxdRail->RxdElanAddr + offsetof (EP3_RXD_RAIL_ELAN, ChainEvent[i]);
++      dmabe.s.dma_srcCookieVProc  = LocalCookie (rail, env->NodeId);
++      
++      EPRINTF9 (DBG_RCVR, "%s: ep3rcvr_rpc_put: rxd %p [XID=%llx] idx=%d Source=%08x Dest=%08x Len=%x Cookies=%x.%x\n", rail->Generic.Name, rxd,
++                (long long) env->Xid.Unique, i, local->nmd_addr, remote->nmd_addr, local->nmd_len, dmabe.s.dma_destCookieVProc, dmabe.s.dma_srcCookieVProc);
++      
++      if (i != 0)
++          elan3_sdram_copyq_to_sdram (dev, &dmabe, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, Dmas[i]), sizeof (E3_DMA)); /* PCI write block */
++    }
++    
++    for (i = 0; i < nFrags; i++)
++      elan3_sdram_writel (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, ChainEvent[i].ev_Count), 1);                            /* PCI write */
++    
++    /* Initialise the data event */
++    elan3_sdram_writel (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Count), 1);                  /* PCI write */
++    rxdMain->DataEvent = EP3_EVENT_ACTIVE;
++   
++    ASSERT (rail->Generic.Nodes[env->NodeId].State >= EP_NODE_CONNECTED && rail->Generic.Nodes[env->NodeId].State <= EP_NODE_LOCAL_PASSIVATE);
++
++    if (IssueDma (rail, &dmabe, EP_RETRY_LOW_PRI, FALSE) != ISSUE_COMMAND_OK)
++    {
++      /* Failed to issue the dma command, so copy the dma descriptor and queue it for retry */
++      EPRINTF2 (DBG_RCVR, "%s: ep3rcvr_rpc_put: queue rxd %p on retry thread\n", rail->Generic.Name, rxd);
++      
++      QueueDmaForRetry (rail, &dmabe, EP_RETRY_LOW_PRI);
++    }
++    
++    BucketStat (rxd->Rcvr->Subsys, RPCPut, len);
++}
++
++void
++ep3rcvr_rpc_get (EP_RXD *rxd, EP_NMD *local, EP_NMD *remote, unsigned nFrags)
++{
++    EP3_RXD_RAIL      *rxdRail   = (EP3_RXD_RAIL *) rxd->RxdRail;
++    EP3_RCVR_RAIL     *rcvrRail  = (EP3_RCVR_RAIL *) rxdRail->Generic.RcvrRail;
++    EP3_RAIL          *rail      = RCVR_TO_RAIL (rcvrRail);
++    ELAN3_DEV         *dev       = rail->Device;
++
++    EP3_RXD_RAIL_MAIN *rxdMain   = rxdRail->RxdMain;
++    sdramaddr_t        rxdElan   = rxdRail->RxdElan;
++    EP_ENVELOPE       *env       = &rxd->RxdMain->Envelope;
++    E3_DMA_BE        dmabe;
++    int                      i, len;
++
++    EP_ASSERT (&rail->Generic, rxd->State == EP_RXD_GET_ACTIVE);
++    EP_ASSERT (&rail->Generic, rxdMain->DataEvent == EP3_EVENT_PRIVATE && rxdMain->DoneEvent == EP3_EVENT_PRIVATE);
++    EP_ASSERT (&rail->Generic, SDRAM_ASSERT (elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Count)) == 0));      /* PCI read */
++    EP_ASSERT (&rail->Generic, SDRAM_ASSERT (elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, DoneEvent.ev_Count)) == 0));      /* PCI read */
++      
++    /* Flush the Elan TLB if mappings have changed */
++    ep_perrail_dvma_sync (&rail->Generic);
++    
++    /* Generate the DMA chain to get the data in two loops to burst
++     * the data across the PCI bus */
++    for (len = 0, i = (nFrags-1), remote += (nFrags-1), local += (nFrags-1); i >= 0;   len += remote->nmd_len, i--, remote--, local--)
++    {
++      dmabe.s.dma_type            = E3_DMA_TYPE(DMA_BYTE, DMA_READ, DMA_NORMAL, EP3_DMAFAILCOUNT);
++      dmabe.s.dma_size            = remote->nmd_len;
++      dmabe.s.dma_source          = remote->nmd_addr;
++      dmabe.s.dma_dest            = local->nmd_addr;
++      if (i == (nFrags-1))
++          dmabe.s.dma_destEvent   = rxdRail->RxdElanAddr + offsetof (EP3_RXD_RAIL_ELAN, DataEvent);
++      else
++          dmabe.s.dma_destEvent   = rxdRail->RxdElanAddr + offsetof (EP3_RXD_RAIL_ELAN, ChainEvent[i]);
++      dmabe.s.dma_destCookieVProc = LocalCookie (rail, env->NodeId);
++      dmabe.s.dma_srcEvent        = (E3_Addr) 0;
++      dmabe.s.dma_srcCookieVProc  = RemoteCookie (rail, env->NodeId);
++      
++      EPRINTF9 (DBG_RCVR, "%s: ep3rcvr_rpc_get rxd %p [XID=%llx] idx=%d Source=%08x Dest=%08x Len=%x Cookies=%x.%x\n", rail->Generic.Name, rxd,
++                (long long) env->Xid.Unique, i, remote->nmd_addr, local->nmd_addr, remote->nmd_len, dmabe.s.dma_destCookieVProc, 
++                dmabe.s.dma_srcCookieVProc);
++      
++      /* 
++       * Always copy down the dma descriptor, since we issue it as a READ_REQUEUE
++       * dma, and the elan will fetch the descriptor to send out of the link from
++       * the rxdElan->Dmas[i] location,  before issueing the DMA chain we modify
++       * the dma_source.
++       */
++      elan3_sdram_copyq_to_sdram (dev, &dmabe, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, Dmas[i]), sizeof (E3_DMA)); /* PCI write block */
++    }
++    
++    for (i = 0; i < nFrags; i++)
++      elan3_sdram_writel (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, ChainEvent[i].ev_Count), 1);                            /* PCI write */
++    
++    /* Initialise the data event */
++    elan3_sdram_writel (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Count), 1);                  /* PCI write */
++    rxdMain->DataEvent  = EP3_EVENT_ACTIVE;
++    
++    ASSERT (rail->Generic.Nodes[env->NodeId].State >= EP_NODE_CONNECTED && rail->Generic.Nodes[env->NodeId].State <= EP_NODE_LOCAL_PASSIVATE);
++
++    /* we MUST convert this into a DMA_READ_REQUEUE dma as if we don't the DMA descriptor will
++     * be read from the EP_RETRY_DMA rather than the orignal DMA - this can then get reused 
++     * and an incorrect DMA descriptor sent */
++    dmabe.s.dma_source    = rxdRail->RxdElanAddr + offsetof (EP3_RXD_RAIL_ELAN, Dmas[0]);
++    dmabe.s.dma_direction = (dmabe.s.dma_direction & ~DMA_READ) | DMA_READ_REQUEUE;
++    
++    if (IssueDma (rail, &dmabe, EP_RETRY_LOW_PRI, FALSE) != ISSUE_COMMAND_OK)
++    {
++      /* Failed to issue the dma command, so copy the dma descriptor and queue it for retry */
++      EPRINTF2 (DBG_RCVR, "%s: ep3rcvr_rpc_get: queue rxd %p on retry thread\n", rail->Generic.Name, rxd);
++      
++      QueueDmaForRetry (rail, &dmabe, EP_RETRY_LOW_PRI);
++    }
++
++    BucketStat (rxd->Rcvr->Subsys, RPCGet, len);
++}
++      
++void
++ep3rcvr_rpc_complete (EP_RXD *rxd, EP_NMD *local, EP_NMD *remote, unsigned nFrags)
++{
++    EP3_RXD_RAIL      *rxdRail   = (EP3_RXD_RAIL *) rxd->RxdRail;
++    EP3_RCVR_RAIL     *rcvrRail  = (EP3_RCVR_RAIL *) rxdRail->Generic.RcvrRail;
++    EP3_RAIL          *rail      = RCVR_TO_RAIL (rcvrRail);
++    ELAN3_DEV         *dev       = rail->Device;
++
++    EP3_RXD_RAIL_MAIN *rxdMain   = rxdRail->RxdMain;
++    sdramaddr_t        rxdElan   = rxdRail->RxdElan;
++    EP_ENVELOPE       *env       = &rxd->RxdMain->Envelope;
++    E3_DMA_BE        dmabe;
++    int                      i, len;
++    
++    EP_ASSERT (&rail->Generic, rxd->State == EP_RXD_COMPLETE_ACTIVE);
++    EP_ASSERT (&rail->Generic, rxdMain->DataEvent == EP3_EVENT_PRIVATE && rxdMain->DoneEvent == EP3_EVENT_PRIVATE);
++    EP_ASSERT (&rail->Generic, SDRAM_ASSERT (elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Count)) == 0));      /* PCI read */
++    EP_ASSERT (&rail->Generic, SDRAM_ASSERT (elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, DoneEvent.ev_Count)) == 0));      /* PCI read */
++
++    /* Flush the Elan TLB if mappings have changed */
++    ep_perrail_dvma_sync (&rail->Generic);
++    
++    /* Initialise the status block dma */
++    dmabe.s.dma_type            = E3_DMA_TYPE(DMA_BYTE, DMA_WRITE, DMA_NORMAL, EP3_DMAFAILCOUNT);
++    dmabe.s.dma_size            = sizeof (EP_STATUSBLK);
++    dmabe.s.dma_source          = rxd->NmdMain.nmd_addr + offsetof (EP_RXD_MAIN, StatusBlk);
++    dmabe.s.dma_dest            = env->TxdMain.nmd_addr + offsetof (EP_TXD_MAIN, StatusBlk);
++    dmabe.s.dma_destEvent       = env->TxdRail + offsetof (EP3_TXD_RAIL_ELAN, DoneEvent);
++    dmabe.s.dma_destCookieVProc = EP_VP_DATA(env->NodeId);
++    dmabe.s.dma_srcEvent        = rxdRail->RxdElanAddr + offsetof (EP3_RXD_RAIL_ELAN, DoneEvent);
++    dmabe.s.dma_srcCookieVProc  = LocalCookie (rail, env->NodeId);
++    
++    EPRINTF8 (DBG_RCVR, "%s: ep3rcvr_rpc_complete: rxd %p [XID=%llx] statusblk source=%08x dest=%08x len=%x Cookies=%x.%x\n", rail->Generic.Name, rxd,
++            (long long) env->Xid.Unique, dmabe.s.dma_source, dmabe.s.dma_dest, dmabe.s.dma_size, dmabe.s.dma_destCookieVProc, 
++            dmabe.s.dma_srcCookieVProc);
++
++    for (len = 0, i = EP_MAXFRAG, remote += (nFrags-1), local += (nFrags-1); i > EP_MAXFRAG-nFrags; len += local->nmd_len, i--, local--, remote--)
++    {
++      /* copy down previous dma */
++      elan3_sdram_copyq_to_sdram (dev, &dmabe, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, Dmas[i]),  sizeof (E3_DMA));    /* PCI write block */
++      
++      dmabe.s.dma_type            = E3_DMA_TYPE(DMA_BYTE, DMA_WRITE, DMA_NORMAL, EP3_DMAFAILCOUNT);
++      dmabe.s.dma_size            = local->nmd_len;
++      dmabe.s.dma_source          = local->nmd_addr;
++      dmabe.s.dma_dest            = remote->nmd_addr;
++      dmabe.s.dma_destEvent       = (E3_Addr) 0;
++      dmabe.s.dma_destCookieVProc = EP_VP_DATA (env->NodeId);
++      dmabe.s.dma_srcEvent        = rxdRail->RxdElanAddr + offsetof (EP3_RXD_RAIL_ELAN, ChainEvent[i-1]);
++      dmabe.s.dma_srcCookieVProc  = LocalCookie (rail, env->NodeId);
++      
++      EPRINTF9 (DBG_RCVR, "%s: ep3rcvr_rpc_complete: rxd %p [XID=%llx] idx=%d Source=%08x Dest=%08x Len=%x Cookies=%x.%x\n", rail->Generic.Name, rxd,
++                (long long) env->Xid.Unique, i, local->nmd_addr, remote->nmd_addr, local->nmd_len, dmabe.s.dma_destCookieVProc, 
++                dmabe.s.dma_srcCookieVProc);
++    }
++    
++    for (i = EP_MAXFRAG-nFrags; i < EP_MAXFRAG; i++)
++      elan3_sdram_writel (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, ChainEvent[i].ev_Count), 1);                            /* PCI write */
++    
++    /* Initialise the done event */
++    elan3_sdram_writel (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, DoneEvent.ev_Count), 1);                                  /* PCI write */
++    rxdMain->DoneEvent  = EP3_EVENT_ACTIVE;
++
++    ASSERT (rail->Generic.Nodes[env->NodeId].State >= EP_NODE_CONNECTED && rail->Generic.Nodes[env->NodeId].State <= EP_NODE_LOCAL_PASSIVATE);
++
++    if (IssueDma (rail, &dmabe, EP_RETRY_LOW_PRI, FALSE) != ISSUE_COMMAND_OK)
++    {
++      /* Failed to issue the dma command, so copy the dma descriptor and queue it for retry */
++      EPRINTF2 (DBG_RCVR, "%s: ep3rcvr_rpc_complete: queue rxd %p on retry thread\n", rail->Generic.Name, rxd);
++      
++      QueueDmaForRetry (rail, &dmabe, EP_RETRY_LOW_PRI);
++    }
++
++    BucketStat (rxd->Rcvr->Subsys, CompleteRPC, len);
++}
++      
++void
++ep3rcvr_add_rail (EP_RCVR *rcvr, EP_COMMS_RAIL *commsRail)
++{
++    EP3_RAIL          *rail   = (EP3_RAIL *) commsRail->Rail;
++    sdramaddr_t        qdescs = ((EP3_COMMS_RAIL *) commsRail)->QueueDescs;
++    EP3_RCVR_RAIL     *rcvrRail;
++    EP3_InputQueue     qdesc;
++    sdramaddr_t        stack;
++    unsigned long      flags;
++
++    KMEM_ZALLOC (rcvrRail, EP3_RCVR_RAIL *, sizeof (EP3_RCVR_RAIL), TRUE);
++
++    kcondvar_init (&rcvrRail->CleanupSleep);
++    spin_lock_init (&rcvrRail->FreeDescLock);
++    INIT_LIST_HEAD (&rcvrRail->FreeDescList);
++    INIT_LIST_HEAD (&rcvrRail->DescBlockList);
++
++    rcvrRail->Generic.CommsRail = commsRail;
++    rcvrRail->Generic.Rcvr      = rcvr;
++
++    rcvrRail->RcvrMain       = ep_alloc_main (&rail->Generic, sizeof (EP3_RCVR_RAIL_MAIN), 0, &rcvrRail->RcvrMainAddr);
++    rcvrRail->RcvrElan       = ep_alloc_elan (&rail->Generic, sizeof (EP3_RCVR_RAIL_ELAN), 0, &rcvrRail->RcvrElanAddr);
++    rcvrRail->InputQueueBase = ep_alloc_elan (&rail->Generic, EP_INPUTQ_SIZE * rcvr->InputQueueEntries, 0, &rcvrRail->InputQueueAddr);
++    stack                    = ep_alloc_elan (&rail->Generic, EP3_STACK_SIZE, 0, &rcvrRail->ThreadStack);
++
++    rcvrRail->TotalDescCount = 0;
++    rcvrRail->FreeDescCount  = 0;
++
++    /* Initialise the main/elan spin lock */
++    elan3_sdram_writel (rail->Device, rcvrRail->RcvrElan + offsetof (EP3_RCVR_RAIL_ELAN, ThreadLock.sl_lock), 0);
++    elan3_sdram_writel (rail->Device, rcvrRail->RcvrElan + offsetof (EP3_RCVR_RAIL_ELAN, ThreadLock.sl_seq),  0);
++
++    elan3_sdram_writel (rail->Device, rcvrRail->RcvrElan + offsetof (EP3_RCVR_RAIL_ELAN, PendingLock.sl_lock), 0);
++    elan3_sdram_writel (rail->Device, rcvrRail->RcvrElan + offsetof (EP3_RCVR_RAIL_ELAN, PendingLock.sl_seq), 0);
++    
++    /* Initialise the receive lists */
++    elan3_sdram_writel (rail->Device, rcvrRail->RcvrElan + offsetof (EP3_RCVR_RAIL_ELAN, PendingDescs), 0);
++    
++    /* Initialise the ThreadShould Halt */
++    elan3_sdram_writel (rail->Device, rcvrRail->RcvrElan + offsetof (EP3_RCVR_RAIL_ELAN, ThreadShouldHalt), 0);
++
++    /* Initialise pointer to the ep_rcvr_rail */
++    elan3_sdram_writeq (rail->Device, rcvrRail->RcvrElan + offsetof (EP3_RCVR_RAIL_ELAN, MainAddr), (unsigned long) rcvrRail);
++
++    /* Initialise elan visible main memory */
++    rcvrRail->RcvrMain->ThreadLock.sl_seq  = 0;
++    rcvrRail->RcvrMain->PendingLock.sl_seq = 0;
++    rcvrRail->RcvrMain->PendingDescsTailp  = 0;
++
++    /* initialise and copy down the input queue descriptor */
++    qdesc.q_state          = E3_QUEUE_FULL;
++    qdesc.q_base           = rcvrRail->InputQueueAddr;
++    qdesc.q_top            = rcvrRail->InputQueueAddr + (rcvr->InputQueueEntries-1) * EP_INPUTQ_SIZE;
++    qdesc.q_fptr           = rcvrRail->InputQueueAddr;
++    qdesc.q_bptr           = rcvrRail->InputQueueAddr + EP_INPUTQ_SIZE;
++    qdesc.q_size           = EP_INPUTQ_SIZE;
++    qdesc.q_event.ev_Count = 0;
++    qdesc.q_event.ev_Type  = 0;
++
++    elan3_sdram_copyl_to_sdram (rail->Device, &qdesc, qdescs + rcvr->Service * sizeof (EP3_InputQueue), sizeof (EP3_InputQueue));
++
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    rcvr->Rails[rail->Generic.Number] = &rcvrRail->Generic;
++    rcvr->RailMask |= EP_RAIL2RAILMASK (rail->Generic.Number);
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++
++    /* initialise and run the Elan thread to process the queue */
++    IssueRunThread (rail, ep3_init_thread (rail->Device, ep_symbol (&rail->ThreadCode, "ep3comms_rcvr"),
++                                         rcvrRail->ThreadStack, stack, EP3_STACK_SIZE, 5,
++                                         rail->RailElanAddr, rcvrRail->RcvrElanAddr, rcvrRail->RcvrMainAddr,
++                                         EP_MSGQ_ADDR(rcvr->Service),
++                                         rail->ElanCookies));
++}
++
++void
++ep3rcvr_del_rail (EP_RCVR *rcvr, EP_COMMS_RAIL *commsRail)
++{
++    EP3_RAIL         *rail     = (EP3_RAIL *) commsRail->Rail;
++    EP3_RCVR_RAIL    *rcvrRail = (EP3_RCVR_RAIL *) rcvr->Rails[rail->Generic.Number];  
++    unsigned long     flags;
++    struct list_head *el, *nel;
++
++    EPRINTF1 (DBG_RCVR, "%s: ep3rcvr_del_rail: removing rail\n", rail->Generic.Name);
++
++    /* flag the rail as no longer available */
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    rcvr->RailMask &= ~EP_RAIL2RAILMASK (rail->Generic.Number);
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++    
++    /* mark the input queue descriptor as full */
++    SetQueueLocked(rail, ((EP3_COMMS_RAIL *)commsRail)->QueueDescs + rcvr->Service * sizeof (EP3_InputQueue));
++
++    /* need to halt the thread first         */
++    /*   set ThreadShouldHalt in elan memory */
++    /*   then trigger the event              */
++    /*   and wait on haltWait                */
++    elan3_sdram_writel  (rail->Device, rcvrRail->RcvrElan + offsetof (EP3_RCVR_RAIL_ELAN, ThreadShouldHalt), TRUE);
++
++    IssueSetevent (rail,  EP_MSGQ_ADDR(rcvr->Service) + offsetof(EP3_InputQueue, q_event));
++
++    spin_lock_irqsave (&rcvr->Lock, flags);
++
++    while (rcvrRail->ThreadHalted == 0)
++    {
++      rcvrRail->CleanupWaiting++;
++      kcondvar_wait (&rcvrRail->CleanupSleep, &rcvr->Lock, &flags);
++    }
++
++    /* at this point the thread is halted and it has no envelopes */
++ 
++    /* we need to wait until all the rxd's in the list that are 
++     * bound to the rail we are removing are not pending 
++     */
++    for (;;)
++    {
++      int mustWait = 0;
++      
++      list_for_each (el, &rcvr->ActiveDescList) {
++          EP_RXD       *rxd     = list_entry (el,EP_RXD, Link);
++          EP3_RXD_RAIL *rxdRail = (EP3_RXD_RAIL *) rxd->RxdRail;
++
++          if (rxdRail && RXD_BOUND2RAIL (rxdRail, rcvrRail) && rxd->RxdMain->Len != EP_RXD_PENDING)
++          {
++              mustWait++;
++              break;
++          }
++      }
++      
++      if (! mustWait)
++          break;
++
++      EPRINTF1 (DBG_RCVR, "%s: ep3rcvr_del_rail: waiting for active rxd's to be returned\n", rail->Generic.Name);
++
++      rcvrRail->CleanupWaiting++;
++      kcondvar_wait (&rcvrRail->CleanupSleep, &rcvr->Lock, &flags);
++    }
++
++    /* at this point all rxd's in the list that are bound to the deleting rail are not pending */
++    list_for_each_safe (el, nel, &rcvr->ActiveDescList) {
++      EP_RXD       *rxd     = list_entry (el, EP_RXD, Link);
++      EP3_RXD_RAIL *rxdRail = (EP3_RXD_RAIL *) rxd->RxdRail;
++      
++      if (rxdRail && RXD_BOUND2RAIL (rxdRail, rcvrRail))
++      {
++          /* here we need to unbind the remaining rxd's */
++          rxdRail->RxdMain->DataEvent = EP3_EVENT_PRIVATE;
++          rxdRail->RxdMain->DoneEvent = EP3_EVENT_PRIVATE;
++ 
++          elan3_sdram_writel (rail->Device, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Count), 0);  /* PCI write */
++          elan3_sdram_writel (rail->Device, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DoneEvent.ev_Count), 0);  /* PCI write */
++
++          UnbindRxdFromRail (rxd, rxdRail);
++          FreeRxdRail(rcvrRail,  rxdRail );
++      }
++    }
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++    
++    /* wait for all rxd's for this rail to become free */
++    spin_lock_irqsave (&rcvrRail->FreeDescLock, flags);
++    while (rcvrRail->FreeDescCount != rcvrRail->TotalDescCount)
++    {
++      rcvrRail->FreeDescWaiting++;
++      kcondvar_wait (&rcvrRail->FreeDescSleep, &rcvrRail->FreeDescLock, &flags);
++    }
++    spin_unlock_irqrestore (&rcvrRail->FreeDescLock, flags);
++
++    /* can now remove the rail as it can no longer be used */
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    rcvr->Rails[rail->Generic.Number] = NULL;
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++
++    /* all the rxd's accociated with DescBlocks must be in the FreeDescList */
++    ASSERT (rcvrRail->TotalDescCount == rcvrRail->FreeDescCount);
++
++    /* run through the DescBlockList deleting them */
++    while (!list_empty (&rcvrRail->DescBlockList))
++      FreeRxdRailBlock (rcvrRail, list_entry(rcvrRail->DescBlockList.next, EP3_RXD_RAIL_BLOCK , Link));
++
++    /* it had better be empty after that */
++    ASSERT ((rcvrRail->TotalDescCount == 0) && (rcvrRail->TotalDescCount == rcvrRail->FreeDescCount));
++    
++    ep_free_elan (&rail->Generic, rcvrRail->ThreadStack, EP3_STACK_SIZE);
++    ep_free_elan (&rail->Generic, rcvrRail->InputQueueAddr, EP_INPUTQ_SIZE * rcvr->InputQueueEntries);
++    ep_free_elan (&rail->Generic, rcvrRail->RcvrElanAddr, sizeof (EP3_RCVR_RAIL_ELAN));
++    ep_free_main (&rail->Generic, rcvrRail->RcvrMainAddr, sizeof (EP3_RCVR_RAIL_MAIN));
++
++    KMEM_FREE (rcvrRail, sizeof (EP3_RCVR_RAIL));
++}
++
++EP_RXD *
++ep3rcvr_steal_rxd (EP_RCVR_RAIL *r)
++{
++    EP3_RCVR_RAIL *rcvrRail = (EP3_RCVR_RAIL *) r;
++    EP3_RAIL      *rail     = RCVR_TO_RAIL (rcvrRail);
++    EP_RCVR       *rcvr     = rcvrRail->Generic.Rcvr;
++    E3_Addr        rxdElanAddr;
++    unsigned long flags;
++
++    spin_lock_irqsave (&rcvr->Lock, flags);
++
++    LockRcvrThread (rcvrRail);
++    if ((rxdElanAddr = elan3_sdram_readl (rail->Device, rcvrRail->RcvrElan + offsetof (EP3_RCVR_RAIL_ELAN, PendingDescs))) != 0)
++    {
++      sdramaddr_t  rxdElan  = ep_elan2sdram (&rail->Generic, rxdElanAddr);
++      EP3_RXD_RAIL *rxdRail = (EP3_RXD_RAIL *) (unsigned long) elan3_sdram_readq (rail->Device, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, MainAddr));
++      EP_RXD      *rxd      = rxdRail->Generic.Rxd;
++      sdramaddr_t  next;
++      
++      EPRINTF2 (DBG_RCVR, "%s: StealRxdFromOtherRail stealing rxd %p\n", rail->Generic.Name, rail);
++      
++      /* Remove the RXD from the pending desc list */
++      if ((next = elan3_sdram_readl (rail->Device, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, Next))) == 0)
++          rcvrRail->RcvrMain->PendingDescsTailp = 0;
++      elan3_sdram_writel (rail->Device, rcvrRail->RcvrElan + offsetof (EP3_RCVR_RAIL_ELAN, PendingDescs), next);
++      UnlockRcvrThread (rcvrRail);
++      
++      UnbindRxdFromRail (rxd, rxdRail);
++      
++      spin_unlock_irqrestore (&rcvr->Lock, flags);
++      
++      /* Mark rxdRail as no longer active */
++      rxdRail->RxdMain->DataEvent = EP3_EVENT_PRIVATE;
++      rxdRail->RxdMain->DoneEvent = EP3_EVENT_PRIVATE;
++      elan3_sdram_writel (rail->Device, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Count), 0);
++      elan3_sdram_writel (rail->Device, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DoneEvent.ev_Count), 0);
++      
++      FreeRxdRail (rcvrRail, rxdRail);
++
++      return rxd;
++    }
++
++    UnlockRcvrThread (rcvrRail);
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++
++    return NULL;
++}
++
++long
++ep3rcvr_check (EP_RCVR_RAIL *r, long nextRunTime)
++{
++    EP3_RCVR_RAIL    *rcvrRail = (EP3_RCVR_RAIL *) r;
++    EP3_RAIL         *rail     = RCVR_TO_RAIL (rcvrRail);
++    EP_RCVR          *rcvr     = rcvrRail->Generic.Rcvr;
++    EP_COMMS_SUBSYS *subsys    = rcvr->Subsys;
++    EP_SYS           *sys       = subsys->Subsys.Sys;
++    EP_RXD           *rxd;
++    unsigned long     flags;
++
++    if (rcvrRail->FreeDescCount < ep_rxd_lowat && !AllocateRxdRailBlock (rcvrRail))
++    {
++      EPRINTF1 (DBG_RCVR,"%s: failed to grow rxd rail pool\n", rail->Generic.Name);
++              
++      if (nextRunTime == 0 || AFTER (nextRunTime, lbolt + RESOURCE_RETRY_TIME))
++          nextRunTime = lbolt + RESOURCE_RETRY_TIME;
++    }
++    
++    if (rcvrRail->ThreadWaiting && (rxd = StealRxdFromOtherRail (rcvr)) != NULL)
++    {
++      /* Map the receive buffer into this rail as well */
++      EPRINTF4 (DBG_RCVR, "%s: mapping rxd->Data (%08x.%08x.%08x) into this rails\n",
++                rail->Generic.Name, rxd->Data.nmd_addr,rxd->Data.nmd_len, rxd->Data.nmd_attr);
++
++      spin_lock_irqsave (&rcvr->Lock, flags);
++      if ((!(EP_NMD_RAILMASK (&rxd->Data) & EP_RAIL2RAILMASK(rail->Generic.Number)) &&                /* not already mapped and */
++           ep_nmd_map_rails (sys, &rxd->Data, EP_RAIL2RAILMASK(rail->Generic.Number)) == 0) ||        /* failed to map it */
++          ep3rcvr_queue_rxd (rxd, &rcvrRail->Generic))                                                /* or failed to queue it */
++      {
++          EPRINTF5 (DBG_RCVR,"%s: stolen rcvr=%p rxd=%p -> rnum=%d rcvrRail=%p (failed)\n", 
++                    rail->Generic.Name, rcvr, rxd, rail->Generic.Number, rcvrRail);
++              
++          if (nextRunTime == 0 || AFTER (nextRunTime, lbolt + RESOURCE_RETRY_TIME))
++              nextRunTime = lbolt + RESOURCE_RETRY_TIME;
++      }
++      spin_unlock_irqrestore (&rcvr->Lock, flags);
++    }
++    
++    return nextRunTime;
++}
++
++static void
++ep3rcvr_flush_filtering (EP_RCVR *rcvr, EP3_RCVR_RAIL *rcvrRail)
++{
++    EP3_COMMS_RAIL *commsRail = (EP3_COMMS_RAIL *) rcvrRail->Generic.CommsRail;
++    EP3_RAIL       *rail      = (EP3_RAIL *) commsRail->Generic.Rail;
++    ELAN3_DEV      *dev       = rail->Device;
++    sdramaddr_t    qdesc      = commsRail->QueueDescs + rcvr->Service*sizeof (EP3_InputQueue);
++    E3_Addr        qTop       = elan3_sdram_readl (dev, qdesc + offsetof (EP3_InputQueue, q_top));
++    E3_Addr        qBase      = elan3_sdram_readl (dev, qdesc + offsetof (EP3_InputQueue, q_base));
++    E3_Addr        qSize      = elan3_sdram_readl (dev,qdesc + offsetof (EP3_InputQueue, q_size));
++    E3_uint32      nfptr, qbptr;
++    unsigned long  flags;
++    
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    LockRcvrThread (rcvrRail);                                                                                /* PCI lock */
++
++    nfptr = elan3_sdram_readl (dev, qdesc + offsetof (EP3_InputQueue, q_fptr));
++    qbptr = elan3_sdram_readl (dev, qdesc + offsetof (EP3_InputQueue, q_bptr));
++    
++    if (nfptr == qTop)
++      nfptr = qBase;
++    else
++      nfptr += qSize;
++    
++    while (nfptr != qbptr)
++    {
++      unsigned nodeId = elan3_sdram_readl (dev, rcvrRail->InputQueueBase + (nfptr - rcvrRail->InputQueueAddr) + 
++                                     offsetof (EP_ENVELOPE, NodeId));
++      
++      EPRINTF3 (DBG_DISCON, "%s: ep3rcvr_flush_filtering: nodeId=%d State=%d\n", rail->Generic.Name, nodeId, rail->Generic.Nodes[nodeId].State);
++      
++      if (rail->Generic.Nodes[nodeId].State == EP_NODE_LOCAL_PASSIVATE)
++          elan3_sdram_writel (dev, rcvrRail->InputQueueBase + (nfptr - rcvrRail->InputQueueAddr) + 
++                        offsetof (EP_ENVELOPE, Version), 0);
++      
++      if (nfptr == qTop)
++          nfptr = qBase;
++      else
++          nfptr += qSize;
++    }
++    
++    UnlockRcvrThread (rcvrRail);                                                                              /* PCI unlock */
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++}
++
++static void
++ep3rcvr_flush_flushing (EP_RCVR *rcvr, EP3_RCVR_RAIL *rcvrRail)
++{
++    EP3_RAIL         *rail = RCVR_TO_RAIL (rcvrRail);
++    struct list_head *el, *nel;
++    unsigned long     flags;
++
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    LockRcvrThread (rcvrRail);                                                                                /* PCI lock */
++    
++    list_for_each_safe (el, nel, &rcvr->ActiveDescList) {
++      EP_RXD       *rxd      = list_entry (el, EP_RXD, Link);
++      EP3_RXD_RAIL *rxdRail  = (EP3_RXD_RAIL *) rxd->RxdRail;
++      EP_ENVELOPE  *env      = &rxd->RxdMain->Envelope;
++      EP_NODE_RAIL *nodeRail = &rail->Generic.Nodes[env->NodeId];
++
++      if (rxd->RxdMain->Len == EP_RXD_PENDING || !RXD_BOUND2RAIL(rxdRail,rcvrRail) || nodeRail->State != EP_NODE_LOCAL_PASSIVATE)
++          continue;
++      
++      EPRINTF6 (DBG_DISCON, "%s: ep3rcvr_flush_flushing: rcvr %p rxd %p state %x.%x elan node %d\n", rail->Generic.Name,
++                rcvr, rxd, rxdRail->RxdMain->DataEvent, rxdRail->RxdMain->DoneEvent, env->NodeId);
++      
++      switch (rxd->State)
++      {
++      case EP_RXD_FREE:
++          printk ("ep3rcvr_flush_flushing: rxd state is free but bound to a fail\n");
++          break;
++
++      case EP_RXD_RECEIVE_ACTIVE:
++          if (rxdRail->RxdMain->DataEvent == EP3_EVENT_ACTIVE)                /* incomplete message receive */
++          {
++              EPRINTF4 (DBG_RCVR, "%s: ep3rcvr_flush_flushing: rcvr %p rxd %p nodeId %d - passive\n", 
++                        rail->Generic.Name, rcvr, rxd, env->NodeId);
++              
++              nodeRail->MessageState |= EP_NODE_PASSIVE_MESSAGES;
++              continue;
++          }
++          break;
++          
++      default:
++          EP_ASSERT (&rail->Generic, EP_IS_RPC(env->Attr));
++
++          if (!EP3_EVENT_FIRED (rxdRail->DoneCookie, rxdRail->RxdMain->DoneEvent))    /* incomplete RPC */
++          {
++              EPRINTF4 (DBG_RCVR, "%s: ep3rcvr_flush_flushing: rcvr %p rxd %p nodeId %d - active\n", 
++                        rail->Generic.Name, rcvr, rxd, env->NodeId);
++              
++              EP_INVALIDATE_XID (rxd->MsgXid);                        /* Ignore any previous NMD map responses */
++              
++              nodeRail->MessageState |= EP_NODE_ACTIVE_MESSAGES;
++              continue;
++          }
++          break;
++
++      case EP_RXD_BEEN_ABORTED:
++          printk ("ep3rcvr_flush_flushing: rxd state is aborted but bound to a fail\n");
++          break;
++      }
++
++      EPRINTF4 (DBG_RCVR, "%s: ep3rcvr_flush_flushing: rcvr %p rxd %p nodeId %d - finished\n", 
++                rail->Generic.Name, rcvr, rxd, env->NodeId);
++    }    
++
++    UnlockRcvrThread (rcvrRail);                                                                      /* PCI unlock */
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++}
++
++void
++ep3rcvr_flush_callback (EP_RCVR *rcvr, EP3_RCVR_RAIL *rcvrRail)
++{
++    EP3_RAIL *rail = RCVR_TO_RAIL(rcvrRail);
++
++    switch (rail->Generic.CallbackStep)
++    {
++    case EP_CB_FLUSH_FILTERING:
++      ep3rcvr_flush_filtering (rcvr, rcvrRail);
++      break;
++
++    case EP_CB_FLUSH_FLUSHING:
++      ep3rcvr_flush_flushing (rcvr, rcvrRail);
++      break;
++    }
++}
++
++void
++ep3rcvr_failover_callback (EP_RCVR *rcvr, EP3_RCVR_RAIL *rcvrRail)
++{
++    EP_COMMS_SUBSYS  *subsys = rcvr->Subsys;
++    EP3_RAIL         *rail   = RCVR_TO_RAIL (rcvrRail);
++    ELAN3_DEV        *dev    = rail->Device;
++    struct list_head *el, *nel;
++    unsigned long     flags;
++#ifdef SUPPORT_RAIL_FAILOVER
++    EP_SYS           *sys    = subsys->Subsys.Sys;
++#endif
++   
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    LockRcvrThread (rcvrRail);                                                                                /* PCI lock */
++    
++    list_for_each_safe (el, nel, &rcvr->ActiveDescList) {
++      EP_RXD             *rxd      = list_entry (el, EP_RXD, Link);
++      EP3_RXD_RAIL       *rxdRail  = (EP3_RXD_RAIL *) rxd->RxdRail;
++      EP_ENVELOPE        *env      = &rxd->RxdMain->Envelope;
++      EP_NODE_RAIL       *nodeRail = &rail->Generic.Nodes[env->NodeId];
++#ifdef SUPPORT_RAIL_FAILOVER
++      EP_MANAGER_MSG_BODY msgBody;
++      EP_NODE            *node     = &sys->Nodes[env->NodeId];
++#endif
++      
++      if (rxd->RxdMain->Len == EP_RXD_PENDING || !RXD_BOUND2RAIL(rxdRail,rcvrRail) || nodeRail->State != EP_NODE_PASSIVATED)
++          continue;
++
++      EPRINTF6 (DBG_FAILOVER, "%s: ep3rcvr_failover_callback: rcvr %p rxd %p elan node %d state %x.%x\n", rail->Generic.Name, rcvr, rxd, env->NodeId,
++                rxdRail->RxdMain->DataEvent, rxdRail->RxdMain->DoneEvent);
++
++      switch (rxd->State)
++      {
++      case EP_RXD_FREE:
++          printk ("ep4rcvr_failover_callback: rxd state is free but bound to a fail\n");
++          break;
++
++      case EP_RXD_RECEIVE_ACTIVE:
++          if (rxdRail->RxdMain->DataEvent == EP3_EVENT_ACTIVE)                /* incomplete message receive */
++          {
++              EPRINTF4 (DBG_FAILOVER, "%s: ep3rcvr_failover_callback: rcvr %p rxd %p nodeId %d - unbind\n", rail->Generic.Name, rcvr, rxd, env->NodeId);
++              
++              UnbindRxdFromRail (rxd, rxdRail);
++              
++              /* clear the done flags - so that it will be ignored if an event interrupt is generated */
++              rxdRail->RxdMain->DataEvent = EP3_EVENT_PRIVATE;
++              rxdRail->RxdMain->DoneEvent = EP3_EVENT_PRIVATE;
++              
++              /* clear the data event - the done event should already be zero */
++              elan3_sdram_writel (dev, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Count), 0);       /* PCI write */
++              
++              FreeRxdRail (rcvrRail, rxdRail);
++              
++              /* epcomms thread will requeue on different rail */
++              ep_kthread_schedule (&subsys->Thread, lbolt);
++              continue;
++          }
++          break;
++
++      default:
++          EP_ASSERT (&rail->Generic, EP_IS_RPC(env->Attr));
++
++#ifdef SUPPORT_RAIL_FAILOVER
++          if (!EP3_EVENT_FIRED (rxdRail->DoneCookie, rxdRail->RxdMain->DoneEvent) && !(EP_IS_NO_FAILOVER(env->Attr)))         /* incomplete RPC, which can be failed over  */
++          {
++              EPRINTF7 (DBG_FAILOVER, "%s: ep3rcvr_failover_callback: rxd %p State %x.%x Xid %llxx MsgXid %llxx nodeId %d - failover\n", 
++                        rail->Generic.Name, rxd, rxdRail->RxdMain->DataEvent, rxdRail->RxdMain->DoneEvent, 
++                        (long long) env->Xid.Unique, (long long) rxd->MsgXid.Unique, env->NodeId);
++              
++              if (EP_XID_INVALID(rxd->MsgXid))
++                  rxd->MsgXid = ep_xid_cache_alloc (sys, &rcvr->XidCache);
++              
++              /* XXXX maybe only send the message if the node failover retry is now ? */
++              msgBody.Failover.Xid      = env->Xid;
++              msgBody.Failover.Railmask = node->ConnectedRails;
++              
++              ep_send_message (&rail->Generic, env->NodeId, EP_MANAGER_MSG_TYPE_FAILOVER_REQUEST, rxd->MsgXid, &msgBody);
++              
++              nodeRail->MessageState |= EP_NODE_ACTIVE_MESSAGES;
++              continue;
++          }
++#endif
++          break;
++
++      case EP_RXD_BEEN_ABORTED:
++          printk ("ep4rcvr_failover_callback: rxd state is aborted but bound to a fail\n");
++          break;
++      }
++
++      EPRINTF3 (DBG_FAILOVER, "%s: ep3rcvr_failover_callback: rxd %p nodeId %d - finished\n", rail->Generic.Name, rxd, env->NodeId);
++    }
++    
++    UnlockRcvrThread (rcvrRail);                                                                      /* PCI unlock */
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++}
++
++void
++ep3rcvr_disconnect_callback (EP_RCVR *rcvr, EP3_RCVR_RAIL *rcvrRail)
++{
++    EP3_RAIL         *rail = RCVR_TO_RAIL (rcvrRail);
++    ELAN3_DEV        *dev = rail->Device;
++    struct list_head *el, *nel;
++    struct list_head  rxdList;
++    unsigned long     flags;
++
++    INIT_LIST_HEAD (&rxdList);
++    
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    LockRcvrThread (rcvrRail);                                                                                /* PCI lock */
++    
++    list_for_each_safe (el, nel, &rcvr->ActiveDescList) {
++      EP_RXD       *rxd      = list_entry (el, EP_RXD, Link);
++      EP3_RXD_RAIL *rxdRail  = (EP3_RXD_RAIL *) rxd->RxdRail;
++      EP_ENVELOPE  *env      = &rxd->RxdMain->Envelope;
++      EP_NODE_RAIL *nodeRail = &rail->Generic.Nodes[env->NodeId];
++      
++      if (rxd->RxdMain->Len == EP_RXD_PENDING || !RXD_BOUND2RAIL(rxdRail,rcvrRail) || nodeRail->State != EP_NODE_DISCONNECTING)
++          continue;
++
++      EPRINTF4 (DBG_DISCON, "%s: ep3rcvr_disconnect_callback: rcvr %p rxd %p elan node %d\n", rail->Generic.Name, rcvr, rxd, env->NodeId);
++
++      switch (rxd->State)
++      {
++      case EP_RXD_FREE:
++          printk ("ep3rcvr_disconnect_callback: rxd state is free but bound to a fail\n");
++          break;
++
++      case EP_RXD_RECEIVE_ACTIVE:
++          if (rxdRail->RxdMain->DataEvent == EP3_EVENT_ACTIVE)                        /* incomplete message receive */
++          {
++              EPRINTF4 (DBG_RCVR, "%s: ep3rcvr_disconnect_callback: rcvr %p rxd %p nodeId %d - unbind\n", rail->Generic.Name, rcvr, rxd, env->NodeId);
++              
++              UnbindRxdFromRail (rxd, rxdRail);
++              
++              /* clear the done flags - so that it will be ignored if an event interrupt is generated */
++              rxdRail->RxdMain->DataEvent = EP3_EVENT_PRIVATE;
++              rxdRail->RxdMain->DoneEvent = EP3_EVENT_PRIVATE;
++              
++              /* clear the data event - the done event should already be zero */
++              elan3_sdram_writel (dev, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Count), 0);       /* PCI write */
++              
++              FreeRxdRail (rcvrRail, rxdRail);
++
++              /* remark it as pending if it was partially received */
++              rxd->RxdMain->Len = EP_RXD_PENDING;
++              
++              /* epcomms thread will requeue on different rail */
++              ep_kthread_schedule (&rcvr->Subsys->Thread, lbolt);
++              continue;
++          }
++          break;
++
++      default:
++          EP_ASSERT (&rail->Generic, EP_IS_RPC(env->Attr));
++
++          if (!EP3_EVENT_FIRED (rxdRail->DoneCookie, rxdRail->RxdMain->DoneEvent))    /* incomplete RPC */
++          {
++              EPRINTF4 (DBG_RCVR, "%s: ep3rcvr_disconnect_callback: rcvr %p rxd %p nodeId %d - not able to failover\n",
++                        rail->Generic.Name, rcvr, rxd, env->NodeId);
++          
++              /* Mark as no longer active */
++              rxdRail->RxdMain->DataEvent = EP3_EVENT_PRIVATE;
++              rxdRail->RxdMain->DoneEvent = EP3_EVENT_PRIVATE;
++              
++              elan3_sdram_writel (dev, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Count), 0);       /* PCI write */
++              elan3_sdram_writel (dev, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DoneEvent.ev_Count), 0);       /* PCI write */
++              
++              UnbindRxdFromRail (rxd, rxdRail);
++              FreeRxdRail (rcvrRail, rxdRail);
++
++              /* Ignore any previous NMD/failover responses */
++              EP_INVALIDATE_XID (rxd->MsgXid);
++              
++              /* Remove from active list */
++              list_del (&rxd->Link);
++              
++              if (rxd->State == EP_RXD_RPC_IN_PROGRESS)                               /* ownder by user .... */
++                  rxd->State = EP_RXD_BEEN_ABORTED;
++              else                                                                    /* queue for completion */
++              {
++                  rxd->RxdMain->Len = EP_CONN_RESET;                                  /* ensure ep_rxd_status() fails */
++                  list_add_tail (&rxd->Link, &rxdList);
++              }
++              continue;
++          }
++          break;
++
++      case EP_RXD_BEEN_ABORTED:
++          printk ("ep4rcvr_failover_callback: rxd state is aborted but bound to a fail\n");
++          break;
++      }
++          
++      EPRINTF4 (DBG_RCVR, "%s: ep3rcvr_disconnect_callback: rcvr %p rxd %p nodeId %d - finished\n", 
++                rail->Generic.Name, rcvr, rxd, env->NodeId);
++    }
++    
++    UnlockRcvrThread (rcvrRail);                                                                      /* PCI unlock */
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++
++    while (! list_empty (&rxdList)) 
++    {
++      EP_RXD *rxd = list_entry (rxdList.next, EP_RXD, Link);
++
++      list_del (&rxd->Link);
++
++      rxd->Handler (rxd);
++    }
++}
++
++void
++ep3rcvr_display_rxd (DisplayInfo *di, EP_RXD_RAIL *r)
++{
++    EP3_RXD_RAIL *rxdRail = (EP3_RXD_RAIL *) r;
++    sdramaddr_t   rxdElan = rxdRail->RxdElan;
++    EP3_RAIL     *rail    = RCVR_TO_RAIL (rxdRail->Generic.RcvrRail);
++    ELAN3_DEV    *dev     = rail->Device;
++
++    (di->func)(di->arg, "      ChainEvent=%x.%x %x.%x\n",
++             elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, ChainEvent[0].ev_Count)),
++             elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, ChainEvent[0].ev_Type)),
++             elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, ChainEvent[1].ev_Count)),
++             elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, ChainEvent[1].ev_Type)));
++    (di->func)(di->arg, "      ChainEvent=%x.%x %x.%x\n",
++             elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, ChainEvent[2].ev_Count)),
++             elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, ChainEvent[2].ev_Type)),
++             elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, ChainEvent[3].ev_Count)),
++             elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, ChainEvent[3].ev_Type)));
++    (di->func)(di->arg, "      DataEvent=%x.%x DoneEvent=%x.%x\n",
++             elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Count)),
++             elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Type)),
++             elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, DoneEvent.ev_Count)),
++             elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, DoneEvent.ev_Type)));
++    (di->func)(di->arg, "      Data=%x Len=%x\n",
++             elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, Data.nmd_addr)),
++             elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, Data.nmd_len)));
++}
++
++void
++ep3rcvr_display_rcvr (DisplayInfo *di, EP_RCVR_RAIL *r)
++{
++    EP3_RCVR_RAIL  *rcvrRail  = (EP3_RCVR_RAIL *) r;
++    EP3_COMMS_RAIL *commsRail = (EP3_COMMS_RAIL *) rcvrRail->Generic.CommsRail;
++    EP3_RAIL       *rail      = RCVR_TO_RAIL (rcvrRail);
++    ELAN3_DEV      *dev       = rail->Device;
++    sdramaddr_t     queue     = commsRail->QueueDescs + rcvrRail->Generic.Rcvr->Service * sizeof (EP3_InputQueue);
++    E3_Addr         qbase      = elan3_sdram_readl (dev, queue + offsetof (EP3_InputQueue, q_base));
++    E3_Addr         qtop       = elan3_sdram_readl (dev, queue + offsetof (EP3_InputQueue, q_top));
++    E3_uint32       qsize      = elan3_sdram_readl (dev, queue + offsetof (EP3_InputQueue, q_size));
++    int             freeCount  = 0;
++    int             blockCount = 0;
++    unsigned long   flags;
++    struct list_head *el;
++
++    spin_lock_irqsave (&rcvrRail->FreeDescLock, flags);
++    list_for_each (el, &rcvrRail->FreeDescList)
++      freeCount++;
++    list_for_each (el, &rcvrRail->DescBlockList)
++      blockCount++;
++    spin_unlock_irqrestore (&rcvrRail->FreeDescLock, flags);
++
++    (di->func)(di->arg, "                 Rail %d FreeDesc %d (%d) Total %d Blocks %d %s\n",
++             rail->Generic.Number, rcvrRail->FreeDescCount, freeCount, rcvrRail->TotalDescCount, blockCount, 
++             rcvrRail->ThreadWaiting ? "ThreadWaiting" : "");
++    
++    (di->func)(di->arg, "                 InputQueue state=%x bptr=%x size=%x top=%x base=%x fptr=%x\n",
++             elan3_sdram_readl (dev, queue + offsetof (EP3_InputQueue, q_state)),
++             elan3_sdram_readl (dev, queue + offsetof (EP3_InputQueue, q_bptr)),
++             elan3_sdram_readl (dev, queue + offsetof (EP3_InputQueue, q_size)),
++             elan3_sdram_readl (dev, queue + offsetof (EP3_InputQueue, q_top)),
++             elan3_sdram_readl (dev, queue + offsetof (EP3_InputQueue, q_base)),
++             elan3_sdram_readl (dev, queue + offsetof (EP3_InputQueue, q_fptr)));
++    (di->func)(di->arg, "                            event=%x.%x [%x.%x] wevent=%x.%x\n",
++             elan3_sdram_readl (dev, queue + offsetof (EP3_InputQueue, q_event.ev_Type)),
++             elan3_sdram_readl (dev, queue + offsetof (EP3_InputQueue, q_event.ev_Count)),
++             elan3_sdram_readl (dev, queue + offsetof (EP3_InputQueue, q_event.ev_Source)),
++             elan3_sdram_readl (dev, queue + offsetof (EP3_InputQueue, q_event.ev_Dest)),
++             elan3_sdram_readl (dev, queue + offsetof (EP3_InputQueue, q_wevent)),
++             elan3_sdram_readl (dev, queue + offsetof (EP3_InputQueue, q_wcount)));
++    
++    LockRcvrThread (rcvrRail);
++    {
++      E3_Addr     nfptr = elan3_sdram_readl (dev, queue + offsetof (EP3_InputQueue, q_fptr));
++      EP_ENVELOPE env;
++      
++      if (nfptr == qtop)
++          nfptr = qbase;
++      else
++          nfptr += qsize;
++
++      while (nfptr != elan3_sdram_readl (dev, queue + offsetof (E3_Queue, q_bptr)))
++      {
++          elan3_sdram_copyl_from_sdram (dev, rcvrRail->InputQueueBase + (nfptr - rcvrRail->InputQueueAddr),
++                                        &env, sizeof (EP_ENVELOPE));
++          
++          (di->func)(di->arg, "                 ENVELOPE Version=%x Attr=%x Xid=%08x.%08x.%016llx\n",
++                     env.Version, env.Attr, env.Xid.Generation, env.Xid.Handle, (long long) env.Xid.Unique);
++          (di->func)(di->arg, "                          NodeId=%x Range=%x TxdRail=%x TxdMain=%x.%x.%x\n",
++                     env.NodeId, env.Range, env.TxdRail, env.TxdMain.nmd_addr,
++                     env.TxdMain.nmd_len, env.TxdMain.nmd_attr);
++          
++          
++          if (nfptr == qtop)
++              nfptr = qbase;
++          else
++              nfptr += qsize;
++      }
++    }
++    UnlockRcvrThread (rcvrRail);
++}
++
++void
++ep3rcvr_fillout_rail_stats(EP_RCVR_RAIL *rcvr_rail, char *str) {
++    /* no stats here yet */
++    /* EP3_RCVR_RAIL * ep4rcvr_rail = (EP3_RCVR_RAIL *) rcvr_rail; */
++}
++
+Index: linux-2.4.21/drivers/net/qsnet/ep/epcommsRx_elan4.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/epcommsRx_elan4.c   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/epcommsRx_elan4.c        2005-06-01 23:12:54.653430896 -0400
+@@ -0,0 +1,1758 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: epcommsRx_elan4.c,v 1.30.2.2 2004/11/12 10:54:51 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/epcommsRx_elan4.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++#include <elan/epsvc.h>
++#include <elan/epcomms.h>
++
++#include "debug.h"
++#include "kcomm_vp.h"
++#include "kcomm_elan4.h"
++#include "epcomms_elan4.h"
++
++#include <elan4/trtype.h>
++
++#define RCVR_TO_COMMS(rcvrRail)               ((EP4_COMMS_RAIL *) ((EP_RCVR_RAIL *) rcvrRail)->CommsRail)
++#define RCVR_TO_RAIL(rcvrRail)                ((EP4_RAIL *) ((EP_RCVR_RAIL *) rcvrRail)->CommsRail->Rail)
++#define RCVR_TO_DEV(rcvrRail)         (RCVR_TO_RAIL(rcvrRail)->r_ctxt.ctxt_dev)
++#define RCVR_TO_SUBSYS(rcvrRail)      (((EP_RCVR_RAIL *) rcvrRail)->Rcvr->Subsys)
++
++#define RXD_TO_RCVR(txdRail)          ((EP4_RCVR_RAIL *) rxdRail->rxd_generic.RcvrRail)
++#define RXD_TO_RAIL(txdRail)          RCVR_TO_RAIL(RXD_TO_RCVR(rxdRail))
++
++static void rxd_interrupt (EP4_RAIL *rail, void *arg);
++
++static __inline__ void 
++__ep4_rxd_assert_free (EP4_RXD_RAIL *rxdRail, const char *file, const int line)
++{
++    EP4_RCVR_RAIL *rcvrRail = RXD_TO_RCVR(rxdRail);
++    ELAN4_DEV     *dev      = RCVR_TO_DEV(rcvrRail);
++    register int i, failed = 0;
++    
++    for (i = 0; i <= EP_MAXFRAG; i++)
++      if (((rxdRail)->rxd_main->rxd_sent[i] != EP4_STATE_FREE)) 
++          failed |= (1 << i);
++    
++    if (((rxdRail)->rxd_main->rxd_failed != EP4_STATE_FREE))
++      failed |= (1 << 5);
++    if (((rxdRail)->rxd_main->rxd_done   != EP4_STATE_FREE)) 
++      failed |= (1 << 6);
++    
++    if (sdram_assert)
++    {
++      if (((elan4_sdram_readq (RXD_TO_RAIL(rxdRail)->r_ctxt.ctxt_dev, (rxdRail)->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_start.ev_CountAndType)) >> 32) != 0)) 
++          failed |= (1 << 7);
++      for (i = 0; i < EP_MAXFRAG; i++)
++          if (((elan4_sdram_readq (dev, (rxdRail)->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[i].ev_CountAndType)) >> 32) != 0)) 
++              failed |= (1 << (8 + i));
++      if (((elan4_sdram_readq (dev, (rxdRail)->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done.ev_CountAndType)) >> 32) != 0)) 
++          failed |= (1 << 12);
++      if (((int)(elan4_sdram_readq (dev, (rxdRail)->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_failed.ev_CountAndType)) >> 32) != -32)) 
++          failed |= (1 << 13);
++    }
++
++    if (failed)
++    {
++      printk ("__ep4_rxd_assert_free: failed=%x rxdRail=%p %s - %d\n", failed, rxdRail, file, line);
++
++      ep_debugf (DBG_DEBUG, "__ep4_rxd_assert_free: failed=%x rxdRail=%p %s - %d\n", failed, rxdRail, file, line);
++      ep4rcvr_display_rxd (&di_ep_debug, &rxdRail->rxd_generic);
++
++      for (i = 0; i <= EP_MAXFRAG; i++)
++          (rxdRail)->rxd_main->rxd_sent[i] = EP4_STATE_FREE;
++
++      (rxdRail)->rxd_main->rxd_failed = EP4_STATE_FREE;
++      (rxdRail)->rxd_main->rxd_done   = EP4_STATE_FREE;
++
++      if (sdram_assert)
++      {
++          elan4_sdram_writew (RXD_TO_RAIL(rxdRail)->r_ctxt.ctxt_dev,
++                              (rxdRail)->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_start.ev_CountAndType) + 4, 0);
++
++          for (i = 0; i < EP_MAXFRAG; i++)
++              elan4_sdram_writew (dev, (rxdRail)->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[i].ev_CountAndType) + 4, 0);
++          elan4_sdram_writew (dev, (rxdRail)->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done.ev_CountAndType) + 4, 0);
++          elan4_sdram_writew (dev, (rxdRail)->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_failed.ev_CountAndType) + 4, -32);
++      }
++      EP_ASSFAIL (RCVR_TO_RAIL(rcvrRail), "__ep4_rxd_assert_free");
++    }
++}
++
++static __inline__ void
++__ep4_rxd_assert_pending(EP4_RXD_RAIL *rxdRail, const char *file, const int line)
++{ 
++    EP4_RCVR_RAIL *rcvrRail = RXD_TO_RCVR(rcvrRail);
++    register int failed = 0;
++
++    failed |= ((rxdRail)->rxd_main->rxd_done != EP4_STATE_ACTIVE);
++
++    if (failed)
++    {
++      printk ("__ep4_rxd_assert_pending: %s - %d\n", file, line);
++
++      ep_debugf (DBG_DEBUG, "__ep4_rxd_assert_pending: %s - %d\n", file, line);
++      ep4rcvr_display_rxd (&di_ep_debug, &rxdRail->rxd_generic);
++
++      (rxdRail)->rxd_main->rxd_done = EP4_STATE_ACTIVE;
++
++      EP_ASSFAIL (RCVR_TO_RAIL(rcvrRail), "__ep4_rxd_assert_pending");
++    }
++}
++
++static __inline__ void
++__ep4_rxd_assert_private(EP4_RXD_RAIL *rxdRail, const char *file, const int line)
++{
++    EP4_RCVR_RAIL *rcvrRail = RXD_TO_RCVR(rxdRail);
++    ELAN4_DEV     *dev      = RCVR_TO_DEV(rcvrRail);
++    register int failed = 0;
++
++    if (((rxdRail)->rxd_main->rxd_failed != EP4_STATE_ACTIVE)) failed |= (1 << 0);
++    if (((rxdRail)->rxd_main->rxd_done != EP4_STATE_PRIVATE))  failed |= (1 << 1);
++    
++    if (sdram_assert)
++    {
++      if (((elan4_sdram_readq (dev, (rxdRail)->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done.ev_CountAndType)) >> 32) != 0))           failed |= (1 << 2);
++      if (((int) (elan4_sdram_readq (dev, (rxdRail)->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_failed.ev_CountAndType)) >> 32) != -32)) failed |= (1 << 3);
++    }
++
++    if (failed)
++    {
++      printk ("__ep4_rxd_assert_private: %s - %d\n", file, line);
++
++      ep_debugf (DBG_DEBUG, "__ep4_rxd_assert_private: %s - %d\n", file, line);
++      ep4rcvr_display_rxd (&di_ep_debug, &rxdRail->rxd_generic);
++
++      (rxdRail)->rxd_main->rxd_failed = EP4_STATE_ACTIVE;
++      (rxdRail)->rxd_main->rxd_done   = EP4_STATE_PRIVATE;
++
++      if (sdram_assert)
++      {
++          elan4_sdram_writew (dev, (rxdRail)->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done.ev_CountAndType) + 4, 0);
++          elan4_sdram_writew (dev, (rxdRail)->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_failed.ev_CountAndType) + 4, -32);
++      }
++
++      EP_ASSFAIL (RCVR_TO_RAIL(rcvrRail), "__ep4_rxd_assert_private");
++    }
++}
++
++static __inline__ void
++__ep4_rxd_private_to_free (EP4_RXD_RAIL *rxdRail)
++{
++    register int i;
++
++    for (i = 0; i <= EP_MAXFRAG; i++)
++      rxdRail->rxd_main->rxd_sent[i] = EP4_STATE_FREE;
++ 
++    rxdRail->rxd_main->rxd_failed = EP4_STATE_FREE;
++    rxdRail->rxd_main->rxd_done   = EP4_STATE_FREE;
++}
++
++static __inline__ void
++__ep4_rxd_force_private (EP4_RXD_RAIL *rxdRail)
++{
++    EP4_RAIL  *rail = RXD_TO_RAIL(rxdRail);
++    ELAN4_DEV *dev  = rail->r_ctxt.ctxt_dev;
++
++    (rxdRail)->rxd_main->rxd_failed = EP4_STATE_ACTIVE;
++    (rxdRail)->rxd_main->rxd_done = EP4_STATE_PRIVATE;
++
++    if (sdram_assert) 
++      elan4_sdram_writeq (dev, (rxdRail)->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done.ev_CountAndType),
++                          E4_EVENT_INIT_VALUE(0, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++}
++
++#define EP4_RXD_ASSERT_FREE(rxdRail)          __ep4_rxd_assert_free(rxdRail, __FILE__, __LINE__)
++#define EP4_RXD_ASSERT_PENDING(rxdRail)               __ep4_rxd_assert_pending(rxdRail, __FILE__, __LINE__)
++#define EP4_RXD_ASSERT_PRIVATE(rxdRail)               __ep4_rxd_assert_private(rxdRail, __FILE__, __LINE__)
++#define EP4_RXD_PRIVATE_TO_FREE(rxdRail)      __ep4_rxd_private_to_free(rxdRail)
++#define EP4_RXD_FORCE_PRIVATE(rxdRail)                __ep4_rxd_force_private(rxdRail)
++
++static int
++alloc_rxd_block (EP4_RCVR_RAIL *rcvrRail)
++{
++    EP4_RAIL           *rail = RCVR_TO_RAIL (rcvrRail);
++    ELAN4_DEV          *dev  = rail->r_ctxt.ctxt_dev;
++    EP4_RXD_RAIL_BLOCK *blk;
++    EP4_RXD_RAIL_MAIN  *rxdMain;
++    EP_ADDR           rxdMainAddr;
++    sdramaddr_t               rxdElan;
++    EP_ADDR           rxdElanAddr;
++    EP4_RXD_RAIL       *rxdRail;
++    unsigned long       flags;
++    int                 i, j;
++
++    KMEM_ZALLOC (blk, EP4_RXD_RAIL_BLOCK *, sizeof (EP4_RXD_RAIL_BLOCK), 1);
++
++    if (blk == NULL)
++      return 0;
++
++    if ((rxdElan = ep_alloc_elan (&rail->r_generic, EP4_RXD_RAIL_ELAN_SIZE * EP4_NUM_RXD_PER_BLOCK, 0, &rxdElanAddr)) == (sdramaddr_t) 0)
++    {
++      KMEM_FREE (blk, sizeof (EP4_RXD_RAIL_BLOCK));
++      return 0;
++    }
++
++    if ((rxdMain = ep_alloc_main (&rail->r_generic, EP4_RXD_RAIL_MAIN_SIZE * EP4_NUM_RXD_PER_BLOCK, 0, &rxdMainAddr)) == (EP4_RXD_RAIL_MAIN *) NULL)
++    {
++      ep_free_elan (&rail->r_generic, rxdElanAddr, EP4_RXD_RAIL_ELAN_SIZE * EP4_NUM_RXD_PER_BLOCK);
++      KMEM_FREE (blk, sizeof (EP4_RXD_RAIL_BLOCK));
++      return 0;
++    }
++
++    if (ep4_reserve_dma_retries (rail, EP4_NUM_RXD_PER_BLOCK, 0) != 0)
++    {
++      ep_free_main (&rail->r_generic, blk->blk_rxds[0].rxd_main_addr, EP4_RXD_RAIL_MAIN_SIZE * EP4_NUM_RXD_PER_BLOCK);
++      ep_free_elan (&rail->r_generic, rxdElanAddr, EP4_RXD_RAIL_ELAN_SIZE * EP4_NUM_RXD_PER_BLOCK);
++      KMEM_FREE (blk, sizeof (EP4_RXD_RAIL_BLOCK));
++
++      return 0;
++    }
++
++    for (rxdRail = &blk->blk_rxds[0], i = 0; i < EP4_NUM_RXD_PER_BLOCK; i++, rxdRail++)
++    {
++      rxdRail->rxd_generic.RcvrRail = &rcvrRail->rcvr_generic;
++      rxdRail->rxd_elan             = rxdElan;
++      rxdRail->rxd_elan_addr        = rxdElanAddr;
++      rxdRail->rxd_main             = rxdMain;
++      rxdRail->rxd_main_addr        = rxdMainAddr;
++
++      /* reserve 128 bytes of "event" cq space for the chained STEN packets */
++      if ((rxdRail->rxd_ecq = ep4_get_ecq (rail, EP4_ECQ_EVENT, EP4_RXD_STEN_CMD_NDWORDS)) == NULL)
++          goto failed;
++
++      /* allocate a single word of "setevent" command space */
++      if ((rxdRail->rxd_scq = ep4_get_ecq (rail, EP4_ECQ_SINGLE, 1)) == NULL)
++      {
++          ep4_put_ecq (rail, rxdRail->rxd_ecq, EP4_RXD_STEN_CMD_NDWORDS);
++          goto failed;
++      }
++
++      /* initialise the completion events */
++      for (j = 0; j <= EP_MAXFRAG; j++)
++          rxdMain->rxd_sent[i] = EP4_STATE_FREE;
++
++      rxdMain->rxd_done   = EP4_STATE_FREE;
++      rxdMain->rxd_failed = EP4_STATE_FREE;
++
++      /* initialise the scq for the thread */
++      rxdMain->rxd_scq = rxdRail->rxd_scq->ecq_addr;
++
++      /* initialise the "start" event to copy the first STEN packet into the command queue */
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_start.ev_CountAndType),
++                          E4_EVENT_INIT_VALUE(0, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_RXD_START_CMD_NDWORDS));
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_start.ev_CopySource),
++                          rxdElanAddr + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[0]));
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_start.ev_CopyDest),
++                          rxdRail->rxd_ecq->ecq_addr);
++
++      /* initialise the "chain" events to copy the next STEN packet into the command queue */
++      for (j = 0; j < EP_MAXFRAG; j++)
++      {
++          elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[j].ev_CountAndType),
++                              E4_EVENT_INIT_VALUE(0, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_RXD_STEN_CMD_NDWORDS));
++          elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[j].ev_CopySource),
++                              rxdElanAddr + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[j+1]));
++          elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[j].ev_CopyDest),
++                              rxdRail->rxd_ecq->ecq_addr);
++      }
++
++      /* initialise the portions of the sten packets which don't change */
++      for (j = 0; j < EP_MAXFRAG+1; j++)
++      {
++          if (j < EP_MAXFRAG)
++              elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[j].c_dma_dstEvent),
++                                  rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[j]));
++          else
++              elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[j].c_dma_dstEvent),
++                                  rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done));
++
++          elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[j].c_ok_guard),
++                              GUARD_CMD | GUARD_CHANNEL (1) | GUARD_TEST(0, PACK_OK) | GUARD_RESET (EP4_STEN_RETRYCOUNT));
++          elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[j].c_ok_write_cmd),
++                              WRITE_DWORD_CMD | (rxdMainAddr + offsetof (EP4_RXD_RAIL_MAIN, rxd_sent[j])));
++          elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[j].c_ok_write_value),
++                              EP4_STATE_FINISHED);
++          elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[j].c_fail_guard),
++                              GUARD_CMD | GUARD_CHANNEL (1) | GUARD_TEST(0, RESTART_COUNT_ZERO) | GUARD_RESET (EP4_STEN_RETRYCOUNT));
++          elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[j].c_fail_setevent),
++                              SET_EVENT_CMD | (rxdElanAddr + offsetof (EP4_RXD_RAIL_ELAN, rxd_failed)));
++          elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[j].c_nop_cmd),
++                              NOP_CMD);
++      }
++
++      /* register a main interrupt cookie */
++      ep4_register_intcookie (rail, &rxdRail->rxd_intcookie, rxdElanAddr + offsetof (EP4_RXD_RAIL_ELAN, rxd_done),
++                              rxd_interrupt, rxdRail);
++
++      /* initialise the command stream for the done event */
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done_cmd.c_write_cmd),
++                          WRITE_DWORD_CMD | (rxdMainAddr + offsetof (EP4_RXD_RAIL_MAIN, rxd_done)));
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done_cmd.c_write_value),
++                          EP4_STATE_FINISHED);
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done_cmd.c_intr_cmd),
++                          INTERRUPT_CMD | (rxdRail->rxd_intcookie.int_val << E4_MAIN_INT_SHIFT));
++
++      /* initialise the command stream for the fail event */
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_failed_cmd.c_write_cmd),
++                          WRITE_DWORD_CMD | (rxdMainAddr + offsetof (EP4_RXD_RAIL_MAIN, rxd_failed)));
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_failed_cmd.c_write_value),
++                          EP4_STATE_FAILED);
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_failed_cmd.c_intr_cmd),
++                          INTERRUPT_CMD | (rxdRail->rxd_intcookie.int_val << E4_MAIN_INT_SHIFT));
++
++      /* initialise the done and fail events */
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done.ev_CountAndType),
++                          E4_EVENT_INIT_VALUE(0, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done.ev_CopySource),
++                          rxdElanAddr + offsetof (EP4_RXD_RAIL_ELAN, rxd_done_cmd));
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done.ev_CopyDest),
++                          rxdRail->rxd_ecq->ecq_addr);
++
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_failed.ev_CountAndType),
++                          E4_EVENT_INIT_VALUE(-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_failed.ev_CopySource),
++                          rxdElanAddr + offsetof (EP4_RXD_RAIL_ELAN, rxd_failed_cmd));
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_failed.ev_CopyDest),
++                          rxdRail->rxd_ecq->ecq_addr);
++      
++      /* initialise the pointer to the main memory portion */
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_main), 
++                          rxdMainAddr);
++
++      /* move onto next descriptor */
++      rxdElan     += EP4_RXD_RAIL_ELAN_SIZE;
++      rxdElanAddr += EP4_RXD_RAIL_ELAN_SIZE;
++      rxdMain      = (EP4_RXD_RAIL_MAIN *) ((unsigned long) rxdMain + EP4_RXD_RAIL_MAIN_SIZE);
++      rxdMainAddr += EP4_RXD_RAIL_MAIN_SIZE;
++    }
++
++    spin_lock_irqsave (&rcvrRail->rcvr_freelock, flags);
++
++    list_add  (&blk->blk_link, &rcvrRail->rcvr_blocklist);
++
++    rcvrRail->rcvr_totalcount += EP4_NUM_RXD_PER_BLOCK;
++    rcvrRail->rcvr_freecount  += EP4_NUM_RXD_PER_BLOCK;
++
++    for (i = 0; i < EP4_NUM_RXD_PER_BLOCK; i++)
++      list_add (&blk->blk_rxds[i].rxd_generic.Link, &rcvrRail->rcvr_freelist);
++
++    spin_unlock_irqrestore (&rcvrRail->rcvr_freelock, flags);
++
++    return 1;
++
++ failed:
++    while (--i >= 0)
++    {
++      rxdRail--;
++
++      ep4_put_ecq (rail, rxdRail->rxd_ecq, EP4_RXD_STEN_CMD_NDWORDS);
++      ep4_put_ecq (rail, rxdRail->rxd_scq, 1);
++
++      ep4_deregister_intcookie (rail, &rxdRail->rxd_intcookie);
++    }
++
++    ep4_release_dma_retries (rail, EP4_NUM_RXD_PER_BLOCK);
++    
++    ep_free_main (&rail->r_generic, blk->blk_rxds[0].rxd_main_addr, EP4_RXD_RAIL_MAIN_SIZE * EP4_NUM_RXD_PER_BLOCK);
++    ep_free_elan (&rail->r_generic, rxdElanAddr, EP4_RXD_RAIL_ELAN_SIZE * EP4_NUM_RXD_PER_BLOCK);
++    KMEM_FREE (blk, sizeof (EP4_RXD_RAIL_BLOCK));
++
++    return 0;
++}
++
++
++static void
++free_rxd_block (EP4_RCVR_RAIL *rcvrRail, EP4_RXD_RAIL_BLOCK *blk)
++{
++    EP4_RAIL     *rail = RCVR_TO_RAIL (rcvrRail);
++    EP4_RXD_RAIL *rxdRail;
++    unsigned long flags;
++    int           i;
++
++    spin_lock_irqsave (&rcvrRail->rcvr_freelock, flags);
++
++    list_del (&blk->blk_link);
++
++    rcvrRail->rcvr_totalcount -= EP4_NUM_RXD_PER_BLOCK;
++
++    for (rxdRail = &blk->blk_rxds[0], i = 0; i < EP4_NUM_RXD_PER_BLOCK; i++, rxdRail++)
++    {
++      rcvrRail->rcvr_freecount--;
++
++      ep4_put_ecq (rail, rxdRail->rxd_ecq, EP4_RXD_STEN_CMD_NDWORDS);
++      ep4_put_ecq (rail, rxdRail->rxd_scq, 1);
++
++      ep4_deregister_intcookie (rail, &rxdRail->rxd_intcookie);
++
++      list_del (&rxdRail->rxd_generic.Link);
++    }
++    spin_unlock_irqrestore (&rcvrRail->rcvr_freelock, flags);
++
++    ep4_release_dma_retries (rail, EP4_NUM_RXD_PER_BLOCK);
++
++    ep_free_main (&rail->r_generic, blk->blk_rxds[0].rxd_main_addr, EP4_RXD_RAIL_MAIN_SIZE * EP4_NUM_RXD_PER_BLOCK);
++    ep_free_elan (&rail->r_generic, blk->blk_rxds[0].rxd_elan_addr, EP4_RXD_RAIL_ELAN_SIZE * EP4_NUM_RXD_PER_BLOCK);
++
++    KMEM_FREE (blk, sizeof (EP4_RXD_RAIL_BLOCK));
++}
++
++static EP4_RXD_RAIL *
++get_rxd_rail (EP4_RCVR_RAIL *rcvrRail)
++{
++    EP_COMMS_SUBSYS  *subsys = RCVR_TO_SUBSYS(rcvrRail);
++    EP4_RXD_RAIL     *rxdRail;
++    unsigned long flags;
++    int low_on_rxds;
++
++    spin_lock_irqsave (&rcvrRail->rcvr_freelock, flags);
++
++    if (list_empty (&rcvrRail->rcvr_freelist))
++      rxdRail = NULL;
++    else
++    {
++      rxdRail = list_entry (rcvrRail->rcvr_freelist.next, EP4_RXD_RAIL, rxd_generic.Link);
++
++      EP4_RXD_ASSERT_FREE(rxdRail);
++
++      list_del (&rxdRail->rxd_generic.Link);
++
++      rcvrRail->rcvr_freecount--;
++    }
++    /* Wakeup the descriptor primer thread if there's not many left */
++    low_on_rxds = (rcvrRail->rcvr_freecount < ep_rxd_lowat);
++
++    spin_unlock_irqrestore (&rcvrRail->rcvr_freelock, flags);
++
++    if (low_on_rxds)
++      ep_kthread_schedule (&subsys->Thread, lbolt);
++
++    return (rxdRail);
++}
++
++static void
++free_rxd_rail (EP4_RCVR_RAIL *rcvrRail, EP4_RXD_RAIL *rxdRail)
++{
++    unsigned long flags;
++
++    EP4_RXD_ASSERT_FREE(rxdRail);
++
++    spin_lock_irqsave (&rcvrRail->rcvr_freelock, flags);
++    
++    list_add (&rxdRail->rxd_generic.Link, &rcvrRail->rcvr_freelist);
++
++    rcvrRail->rcvr_freecount++;
++
++    if (rcvrRail->rcvr_freewaiting)
++    {
++      rcvrRail->rcvr_freewaiting--;
++      kcondvar_wakeupall (&rcvrRail->rcvr_freesleep, &rcvrRail->rcvr_freelock);
++    }
++
++    spin_unlock_irqrestore (&rcvrRail->rcvr_freelock, flags);
++}
++
++static void
++bind_rxd_rail (EP_RXD *rxd, EP4_RXD_RAIL *rxdRail)
++{
++    EP4_RAIL *rail = RCVR_TO_RAIL (rxdRail->rxd_generic.RcvrRail);
++
++    ASSERT (SPINLOCK_HELD (&rxd->Rcvr->Lock));
++
++    EPRINTF3 (DBG_RCVR, "%s: bind_rxd_rail: rxd=%p rxdRail=%p\n",  rail->r_generic.Name, rxd, rxdRail);
++
++    elan4_sdram_writeq (rail->r_ctxt.ctxt_dev, rxdRail->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_rxd), rxd->NmdMain.nmd_addr);                     /* PCI write */
++
++    rxd->RxdRail             = &rxdRail->rxd_generic;
++    rxdRail->rxd_generic.Rxd = rxd;
++}
++
++static void
++unbind_rxd_rail (EP_RXD *rxd, EP4_RXD_RAIL *rxdRail)
++{
++    EP4_RCVR_RAIL *rcvrRail = (EP4_RCVR_RAIL *) rxdRail->rxd_generic.RcvrRail;
++    
++    ASSERT (SPINLOCK_HELD (&rxd->Rcvr->Lock));
++    ASSERT (rxd->RxdRail == &rxdRail->rxd_generic && rxdRail->rxd_generic.Rxd == rxd);
++
++    EP4_RXD_ASSERT_PRIVATE (rxdRail);
++
++    EPRINTF3 (DBG_RCVR, "%s: unbind_rxd_rail: rxd=%p rxdRail=%p\n",  RCVR_TO_RAIL(rcvrRail)->r_generic.Name, rxd, rxdRail);
++
++    rxd->RxdRail             = NULL;
++    rxdRail->rxd_generic.Rxd = NULL;
++
++    if (rcvrRail->rcvr_cleanup_waiting)
++      kcondvar_wakeupall (&rcvrRail->rcvr_cleanup_sleep, &rxd->Rcvr->Lock);
++    rcvrRail->rcvr_cleanup_waiting = 0;
++
++    EP4_RXD_PRIVATE_TO_FREE (rxdRail);
++}
++
++
++static void
++rcvr_stall_interrupt (EP4_RAIL *rail, void *arg)
++{
++    EP4_RCVR_RAIL *rcvrRail = (EP4_RCVR_RAIL *) arg;
++    EP_RCVR       *rcvr     = rcvrRail->rcvr_generic.Rcvr;
++    unsigned long  flags;
++
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    
++    EPRINTF1 (DBG_RCVR, "rcvr_stall_interrupt: rcvrRail %p thread halted\n", rcvrRail);
++
++    rcvrRail->rcvr_thread_halted = 1;
++
++    kcondvar_wakeupall (&rcvrRail->rcvr_cleanup_sleep, &rcvr->Lock);
++
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++}
++
++static void
++rcvr_stall_haltop (ELAN4_DEV *dev, void *arg)
++{
++    EP4_RCVR_RAIL  *rcvrRail  = (EP4_RCVR_RAIL *) arg;
++    EP4_COMMS_RAIL *commsRail = RCVR_TO_COMMS(rcvrRail);
++    EP_RCVR        *rcvr      = rcvrRail->rcvr_generic.Rcvr;
++    sdramaddr_t     qdesc     = ((EP4_COMMS_RAIL *) commsRail)->r_descs + (rcvr->Service * EP_QUEUE_DESC_SIZE);
++    E4_uint64       qbptr     = elan4_sdram_readq (dev, qdesc + offsetof (E4_InputQueue, q_bptr));
++
++    /* Mark the queue as full by writing the fptr */
++    if (qbptr == (rcvrRail->rcvr_slots_addr + EP_INPUTQ_SIZE * (rcvr->InputQueueEntries-1)))
++      elan4_sdram_writeq (dev, qdesc + offsetof (E4_InputQueue, q_fptr), rcvrRail->rcvr_slots_addr);
++    else
++      elan4_sdram_writeq (dev, qdesc + offsetof (E4_InputQueue, q_fptr), qbptr + EP_INPUTQ_SIZE);
++
++    /* Notify the thread that it should stall after processing any outstanding envelopes */
++    elan4_sdram_writeq (dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_stall_intcookie),
++                      rcvrRail->rcvr_stall_intcookie.int_val);
++
++    /* Issue a swtevent to the queue event to wake the thread up */
++    ep4_set_event_cmd (rcvrRail->rcvr_resched, rcvrRail->rcvr_elan_addr + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_qevent));
++}
++
++static void
++rxd_interrupt (EP4_RAIL *rail, void *arg)
++{
++    EP4_RXD_RAIL      *rxdRail  = (EP4_RXD_RAIL *) arg;
++    EP4_RCVR_RAIL     *rcvrRail = (EP4_RCVR_RAIL *) rxdRail->rxd_generic.RcvrRail;
++    EP_RCVR           *rcvr     = rcvrRail->rcvr_generic.Rcvr;
++    EP4_RXD_RAIL_MAIN *rxdMain  = rxdRail->rxd_main;
++    unsigned long      delay    = 1;
++    EP_RXD            *rxd;
++    EP_ENVELOPE       *env;
++    unsigned long      flags;
++
++    spin_lock_irqsave (&rcvr->Lock, flags);
++
++    for (;;)
++    {
++      if (rxdMain->rxd_done == EP4_STATE_FINISHED || rxdMain->rxd_failed == EP4_STATE_FAILED)
++          break;
++
++      /* The write to rxd_done could be held up in the PCI bridge even though
++       * we've seen the interrupt cookie.  Unlike elan3, there is no possibility
++       * of spurious interrupts since we flush the command queues on node 
++       * disconnection and the txcallback mechanism */
++      mb();
++
++      if (delay > EP4_EVENT_FIRING_TLIMIT)
++      {
++          spin_unlock_irqrestore (&rcvr->Lock, flags);
++
++          EP_ASSFAIL (RCVR_TO_RAIL(rcvrRail), "rxd_interrupt - not finished\n");
++          return;
++      }
++      DELAY(delay);
++      delay <<= 1;
++    }
++
++    if (rxdMain->rxd_done != EP4_STATE_FINISHED)
++    {
++      EPRINTF8 (DBG_RETRY, "%s: rxd_interrupt: rxdRail %p retry: done=%d failed=%d NodeId=%d XID=%08x.%08x.%016llx\n",
++                rail->r_generic.Name, rxdRail, (int)rxdMain->rxd_done, (int)rxdMain->rxd_failed, rxdRail->rxd_generic.Rxd->RxdMain->Envelope.NodeId,
++                rxdRail->rxd_generic.Rxd->RxdMain->Envelope.Xid.Generation, rxdRail->rxd_generic.Rxd->RxdMain->Envelope.Xid.Handle, 
++                rxdRail->rxd_generic.Rxd->RxdMain->Envelope.Xid.Unique);
++    
++      spin_lock (&rcvrRail->rcvr_retrylock);
++
++      rxdRail->rxd_retry_time = lbolt + EP_RETRY_LOW_PRI_TIME;                        /* XXXX backoff ? */
++
++      list_add_tail (&rxdRail->rxd_retry_link, &rcvrRail->rcvr_retrylist);
++
++      ep_kthread_schedule (&rail->r_retry_thread, rxdRail->rxd_retry_time);
++      spin_unlock (&rcvrRail->rcvr_retrylock);
++
++      spin_unlock_irqrestore (&rcvr->Lock, flags);
++      return;
++    }
++    
++    rxd = rxdRail->rxd_generic.Rxd;
++    env = &rxd->RxdMain->Envelope;
++
++    /*
++     * Note, since the thread will have sent the remote dma packet before copying 
++     * the envelope, we must check that it has completed doing this,  we do this
++     * by acquiring the spinlock against the thread which it only drops once it's
++     * completed.
++     */
++    if (rxd->RxdMain->Len == EP_RXD_PENDING)
++    {
++      EP4_SPINENTER (rail->r_ctxt.ctxt_dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_thread_lock),
++                     &rcvrRail->rcvr_main->rcvr_thread_lock);
++      
++      EP4_SPINEXIT (rail->r_ctxt.ctxt_dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_thread_lock),
++                    &rcvrRail->rcvr_main->rcvr_thread_lock);
++      
++      ASSERT (env->Version == EP_ENVELOPE_VERSION && rxd->RxdMain->Len != EP_RXD_PENDING);
++    }
++
++    EPRINTF8 (DBG_RCVR, "%s: rxd_interrupt: rxd %p finished from %d XID %08x.%08x.%016llx len %d attr %x\n", rail->r_generic.Name, 
++            rxd, rxd->RxdMain->Envelope.NodeId, rxd->RxdMain->Envelope.Xid.Generation, rxd->RxdMain->Envelope.Xid.Handle, 
++            rxd->RxdMain->Envelope.Xid.Unique,  rxd->RxdMain->Len, rxd->RxdMain->Envelope.Attr);
++
++    rxdMain->rxd_done  = EP4_STATE_PRIVATE;
++    rxd->Data.nmd_attr = EP_RAIL2RAILMASK (rail->r_generic.Number);
++
++    switch (rxd->State)
++    {
++    case EP_RXD_RECEIVE_ACTIVE:
++      if (rxd->RxdMain->Len >= 0 && EP_IS_RPC(env->Attr))
++          rxd->State = EP_RXD_RPC_IN_PROGRESS;
++      else
++      {
++          rxd->State = EP_RXD_COMPLETED;
++
++          /* remove from active list */
++          list_del (&rxd->Link);
++
++          unbind_rxd_rail (rxd, rxdRail);
++          free_rxd_rail (rcvrRail, rxdRail);
++      }
++
++      if (rxd->RxdMain->Len >= 0) {
++          INC_STAT(rcvrRail->rcvr_generic.stats,rx);
++          ADD_STAT(rcvrRail->rcvr_generic.stats,rx_len,rxd->RxdMain->Len);
++          INC_STAT(rail->r_generic.Stats,rx);
++          ADD_STAT(rail->r_generic.Stats,rx_len,rxd->RxdMain->Len);
++      }
++      spin_unlock_irqrestore (&rcvr->Lock, flags);
++      ep_rxd_received (rxd);
++
++      break;
++
++    case EP_RXD_PUT_ACTIVE:
++    case EP_RXD_GET_ACTIVE:
++      rxd->State = EP_RXD_RPC_IN_PROGRESS;
++      spin_unlock_irqrestore (&rcvr->Lock, flags);
++      
++      rxd->Handler (rxd);
++      break;
++
++    case EP_RXD_COMPLETE_ACTIVE:
++      rxd->State = EP_RXD_COMPLETED;
++
++      /* remove from active list */
++      list_del (&rxd->Link);
++
++      unbind_rxd_rail (rxd, rxdRail);
++      free_rxd_rail (rcvrRail, rxdRail);
++
++      spin_unlock_irqrestore (&rcvr->Lock, flags);
++
++      rxd->Handler(rxd);
++      break;
++
++    default:
++      spin_unlock_irqrestore (&rcvr->Lock, flags);
++
++      printk ("%s: rxd_interrupt: rxd %p in invalid state %d\n", rail->r_generic.Name, rxd, rxd->State);
++      /* NOTREACHED */
++    }
++}
++
++static void
++ep4rcvr_flush_filtering (EP_RCVR *rcvr, EP4_RCVR_RAIL *rcvrRail)
++{
++    EP4_COMMS_RAIL *commsRail = RCVR_TO_COMMS(rcvrRail);
++    EP4_RAIL       *rail      = RCVR_TO_RAIL(rcvrRail);
++    ELAN4_DEV      *dev       = rail->r_ctxt.ctxt_dev;
++    sdramaddr_t    qdesc      = commsRail->r_descs + (rcvr->Service * EP_QUEUE_DESC_SIZE);
++    E4_Addr        qbase      = rcvrRail->rcvr_slots_addr;
++    E4_Addr        qlast      = qbase + EP_INPUTQ_SIZE * (rcvr->InputQueueEntries-1);
++    E4_uint64      qfptr, qbptr;
++    unsigned long  flags;
++    
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    EP4_SPINENTER (dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_thread_lock), &rcvrRail->rcvr_main->rcvr_thread_lock);
++    
++    /* zip down the input queue and invalidate any envelope we find to a node which is locally passivated */
++    qfptr = elan4_sdram_readq (dev, qdesc + offsetof (E4_InputQueue, q_fptr));
++    qbptr = elan4_sdram_readq (dev, qdesc + offsetof (E4_InputQueue, q_bptr));
++
++    while (qfptr != qbptr)
++    {
++      unsigned int nodeId = elan4_sdram_readl (dev, rcvrRail->rcvr_slots + (qfptr - qbase) + offsetof (EP_ENVELOPE, NodeId));
++
++      EPRINTF3 (DBG_DISCON, "%s: ep4rcvr_flush_filtering: nodeId=%d State=%d\n", rail->r_generic.Name, nodeId, rail->r_generic.Nodes[nodeId].State);
++      
++      if (rail->r_generic.Nodes[nodeId].State == EP_NODE_LOCAL_PASSIVATE)
++          elan4_sdram_writel (dev,  rcvrRail->rcvr_slots + (qfptr - qbase) + offsetof (EP_ENVELOPE, Version), 0);
++      
++      if (qfptr != qlast)
++          qfptr += EP_INPUTQ_SIZE;
++      else
++          qfptr = qbase;
++    }
++
++    /* Insert an setevent command into the thread's command queue
++     * to ensure that all sten packets have completed */
++    elan4_guard (rcvrRail->rcvr_ecq->ecq_cq, GUARD_ALL_CHANNELS);
++    ep4comms_flush_setevent (commsRail, rcvrRail->rcvr_ecq->ecq_cq);
++    
++    EP4_SPINEXIT (dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_thread_lock), &rcvrRail->rcvr_main->rcvr_thread_lock);
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++}
++
++static void
++ep4rcvr_flush_flushing (EP_RCVR *rcvr, EP4_RCVR_RAIL *rcvrRail)
++{
++    EP4_RAIL         *rail = RCVR_TO_RAIL (rcvrRail);
++    ELAN4_DEV      *dev  = rail->r_ctxt.ctxt_dev;
++    struct list_head *el, *nel;
++    struct list_head  rxdList;
++    unsigned long     flags;
++
++    INIT_LIST_HEAD (&rxdList);
++    
++    /* remove any sten packates which are retrying to nodes which are being passivated */
++    spin_lock_irqsave (&rcvrRail->rcvr_retrylock, flags);
++    list_for_each_safe (el, nel, &rcvrRail->rcvr_retrylist) {
++      EP4_RXD_RAIL *rxdRail  = list_entry (el, EP4_RXD_RAIL, rxd_retry_link);
++      EP_ENVELOPE  *env      = &rxdRail->rxd_generic.Rxd->RxdMain->Envelope;
++      EP_NODE_RAIL *nodeRail = &rail->r_generic.Nodes[env->NodeId];
++
++      if (nodeRail->State == EP_NODE_LOCAL_PASSIVATE)
++      {
++          EPRINTF2 (DBG_XMTR, "%s; ep4rcvr_flush_flushing: removing rxdRail %p from retry list\n", rail->r_generic.Name, rxdRail);
++          
++          list_del (&rxdRail->rxd_retry_link);
++      }
++    }
++    spin_unlock_irqrestore (&rcvrRail->rcvr_retrylock, flags);
++
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    EP4_SPINENTER (dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_thread_lock), &rcvrRail->rcvr_main->rcvr_thread_lock);
++    
++    list_for_each_safe (el, nel, &rcvr->ActiveDescList) {
++      EP_RXD       *rxd      = list_entry (el, EP_RXD, Link);
++      EP4_RXD_RAIL *rxdRail  = (EP4_RXD_RAIL *) rxd->RxdRail;
++      EP_ENVELOPE  *env      = &rxd->RxdMain->Envelope;
++      EP_NODE_RAIL *nodeRail = &rail->r_generic.Nodes[env->NodeId];
++
++      if (rxd->RxdMain->Len == EP_RXD_PENDING || !RXD_BOUND2RAIL (rxdRail, rcvrRail) || nodeRail->State != EP_NODE_LOCAL_PASSIVATE)
++          continue;
++      
++      EPRINTF5 (DBG_DISCON, "%s: ep4rcvr_flush_flushing: rcvr %p rxd %p state %d elan node %d\n", 
++                rail->r_generic.Name, rcvr, rxd, (int)rxdRail->rxd_main->rxd_done, env->NodeId);
++      
++      switch (rxd->State)
++      {
++      case EP_RXD_FREE:
++          printk ("ep4rcvr_flush_flushing: rxd state is free but bound to a fail\n");
++          break;
++
++      case EP_RXD_RECEIVE_ACTIVE:
++          if (rxdRail->rxd_main->rxd_done == EP4_STATE_ACTIVE)                /* incomplete message receive */
++          {
++              EPRINTF4 (DBG_RCVR, "%s: ep4rcvr_flush_flushing: rcvr %p rxd %p nodeId %d - passive\n", 
++                        rail->r_generic.Name, rcvr, rxd, env->NodeId);
++              
++              nodeRail->MessageState |= EP_NODE_PASSIVE_MESSAGES;
++              continue;
++          }
++          break;
++          
++      default:
++          EP4_ASSERT (rail, EP_IS_RPC(env->Attr));
++
++          if (rxdRail->rxd_main->rxd_done == EP4_STATE_ACTIVE)                /* incomplete RPC */
++          {
++              EPRINTF4 (DBG_RCVR, "%s: ep4rcvr_flush_flushing: rcvr %p rxd %p nodeId %d - active\n", 
++                        rail->r_generic.Name, rcvr, rxd, env->NodeId);
++              
++              EP_INVALIDATE_XID (rxd->MsgXid);                        /* Ignore any previous NMD map responses */
++              
++              nodeRail->MessageState |= EP_NODE_ACTIVE_MESSAGES;
++              continue;
++          }
++          break;
++
++      case EP_RXD_BEEN_ABORTED:
++          printk ("ep4rcvr_flush_flushing: rxd state is aborted but bound to a fail\n");
++          break;
++      }
++
++      EPRINTF4 (DBG_RCVR, "%s: ep4rcvr_flush_flushing: rcvr %p rxd %p nodeId %d - finished\n", 
++                rail->r_generic.Name, rcvr, rxd, env->NodeId);
++    }    
++
++    EP4_SPINEXIT (dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_thread_lock), &rcvrRail->rcvr_main->rcvr_thread_lock);
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++}
++
++void
++ep4rcvr_flush_callback (EP_RCVR *rcvr, EP4_RCVR_RAIL *rcvrRail)
++{
++    EP4_RAIL *rail = RCVR_TO_RAIL(rcvrRail);
++
++    switch (rail->r_generic.CallbackStep)
++    {
++    case EP_CB_FLUSH_FILTERING:
++      ep4rcvr_flush_filtering (rcvr, rcvrRail);
++      break;
++
++    case EP_CB_FLUSH_FLUSHING:
++      ep4rcvr_flush_flushing (rcvr, rcvrRail);
++      break;
++    }
++}
++
++void
++ep4rcvr_failover_callback (EP_RCVR *rcvr, EP4_RCVR_RAIL *rcvrRail)
++{
++    EP_COMMS_SUBSYS  *subsys = rcvr->Subsys;
++    EP4_RAIL         *rail   = RCVR_TO_RAIL (rcvrRail);
++    ELAN4_DEV      *dev    = rail->r_ctxt.ctxt_dev;
++    struct list_head *el, *nel;
++    unsigned long     flags;
++#if SUPPORT_RAIL_FAILOVER
++    EP_SYS           *sys    = subsys->Subsys.Sys;
++#endif
++    
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    EP4_SPINENTER (dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_thread_lock), &rcvrRail->rcvr_main->rcvr_thread_lock);
++    
++    list_for_each_safe (el, nel, &rcvr->ActiveDescList) {
++      EP_RXD             *rxd      = list_entry (el, EP_RXD, Link);
++      EP4_RXD_RAIL       *rxdRail  = (EP4_RXD_RAIL *) rxd->RxdRail;
++      EP_ENVELOPE        *env      = &rxd->RxdMain->Envelope;
++      EP_NODE_RAIL       *nodeRail = &rail->r_generic.Nodes[env->NodeId];
++#if SUPPORT_RAIL_FAILOVER
++      EP_NODE            *node     = &sys->Nodes[env->NodeId];
++      EP_MANAGER_MSG_BODY msgBody;
++#endif
++      
++      if (rxd->RxdMain->Len == EP_RXD_PENDING || !RXD_BOUND2RAIL(rxdRail,rcvrRail) || nodeRail->State != EP_NODE_PASSIVATED)
++          continue;
++
++      EPRINTF5 (DBG_FAILOVER, "%s: ep4rcvr_failover_callback: rcvr %p rxd %p elan node %d state %d\n", 
++                rail->r_generic.Name, rcvr, rxd, env->NodeId, (int)rxdRail->rxd_main->rxd_done);
++
++      switch (rxd->State)
++      {
++      case EP_RXD_FREE:
++          printk ("ep4rcvr_failover_callback: rxd state is free but bound to a fail\n");
++          break;
++
++      case EP_RXD_RECEIVE_ACTIVE:
++          if (rxdRail->rxd_main->rxd_done == EP4_STATE_ACTIVE)                        /* incomplete message receive */
++          {
++              EPRINTF4 (DBG_FAILOVER, "%s: ep4rcvr_failover_callback: rcvr %p rxd %p nodeId %d - unbind\n", rail->r_generic.Name, rcvr, rxd, env->NodeId);
++
++              EP4_RXD_FORCE_PRIVATE(rxdRail);
++              
++              unbind_rxd_rail (rxd, rxdRail);
++
++              free_rxd_rail (rcvrRail, rxdRail);
++          
++              /* epcomms thread will requeue on different rail */
++              ep_kthread_schedule (&subsys->Thread, lbolt);
++              continue;
++          }
++          break;
++
++      default:
++          EP4_ASSERT (rail, EP_IS_RPC(env->Attr));
++
++#if SUPPORT_RAIL_FAILOVER
++          /* XXXX - no rail failover for now .... */
++          if (rxdRail->rxd_main->rxd_done == EP4_STATE_ACTIVE && !EP_IS_NO_FAILOVER(env->Attr))       /* incomplete RPC, which can be failed over */
++          {
++              EPRINTF6 (DBG_FAILOVER, "%s: ep4rcvr_failover_callback: rxd %p State %d Xid %llxx MsgXid %llxx nodeId %d - failover\n", 
++                        rail->r_generic.Name, rxd, rxd->State, env->Xid.Unique, rxd->MsgXid.Unique, env->NodeId);
++              
++              if (EP_XID_INVALID(rxd->MsgXid))
++                  rxd->MsgXid = ep_xid_cache_alloc (sys, &rcvr->XidCache);
++              
++              /* XXXX maybe only send the message if the node failover retry is now ? */
++              msgBody.Failover.Xid      = env->Xid;
++              msgBody.Failover.Railmask = node->ConnectedRails;
++              
++              ep_send_message (&rail->r_generic, env->NodeId, EP_MANAGER_MSG_TYPE_FAILOVER_REQUEST, rxd->MsgXid, &msgBody);
++              
++              nodeRail->MessageState |= EP_NODE_ACTIVE_MESSAGES;
++              continue;
++          }
++#endif
++          break;
++
++      case EP_RXD_BEEN_ABORTED:
++          printk ("ep4rcvr_failover_callback: rxd state is aborted but bound to a fail\n");
++          break;
++      }
++      EPRINTF3 (DBG_FAILOVER, "%s: ep4rcvr_failover_callback: rxd %p nodeId %d - finished\n", rail->r_generic.Name, rxd, env->NodeId);
++    }
++    
++    EP4_SPINEXIT (dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_thread_lock), &rcvrRail->rcvr_main->rcvr_thread_lock);
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++}
++
++void
++ep4rcvr_disconnect_callback (EP_RCVR *rcvr, EP4_RCVR_RAIL *rcvrRail)
++{
++    EP4_RAIL         *rail = RCVR_TO_RAIL (rcvrRail);
++    ELAN4_DEV        *dev = rail->r_ctxt.ctxt_dev;
++    struct list_head *el, *nel;
++    struct list_head  rxdList;
++    unsigned long     flags;
++
++    INIT_LIST_HEAD (&rxdList);
++    
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    EP4_SPINENTER (dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_thread_lock), &rcvrRail->rcvr_main->rcvr_thread_lock);
++    
++    list_for_each_safe (el, nel, &rcvr->ActiveDescList) {
++      EP_RXD       *rxd      = list_entry (el, EP_RXD, Link);
++      EP4_RXD_RAIL *rxdRail  = (EP4_RXD_RAIL *) rxd->RxdRail;
++      EP_ENVELOPE  *env      = &rxd->RxdMain->Envelope;
++      EP_NODE_RAIL *nodeRail = &rail->r_generic.Nodes[env->NodeId];
++      
++      if (rxd->RxdMain->Len == EP_RXD_PENDING || !RXD_BOUND2RAIL(rxdRail,rcvrRail) || nodeRail->State != EP_NODE_DISCONNECTING)
++          continue;
++
++      EPRINTF5 (DBG_DISCON, "%s: ep4rcvr_disconnect_callback: rcvr %p rxd %p elan node %d state %x\n", rail->r_generic.Name, rcvr, rxd, env->NodeId, rxd->State);
++
++      switch (rxd->State)
++      {
++      case EP_RXD_FREE:
++          printk ("ep4rcvr_disconnect_callback: rxd state is free but bound to a rail\n");
++          break;
++
++      case EP_RXD_RECEIVE_ACTIVE:
++          if (rxdRail->rxd_main->rxd_done == EP4_STATE_ACTIVE)                /* incomplete message receive */
++          {
++              EPRINTF4 (DBG_RCVR, "%s: ep4rcvr_disconnect_callback: rcvr %p rxd %p nodeId %d - unbind\n", rail->r_generic.Name, rcvr, rxd, env->NodeId);
++
++              EP4_RXD_FORCE_PRIVATE (rxdRail);
++              
++              unbind_rxd_rail (rxd, rxdRail);
++              free_rxd_rail (rcvrRail, rxdRail);
++              
++              /* remark it as pending if it was partially received */
++              rxd->RxdMain->Len = EP_RXD_PENDING;
++              
++              /* epcomms thread will requeue on different rail */
++              ep_kthread_schedule (&rcvr->Subsys->Thread, lbolt);
++              continue;
++          }
++          break;
++
++      default:
++          if (rxdRail->rxd_main->rxd_done == EP4_STATE_ACTIVE || rxdRail->rxd_main->rxd_done == EP4_STATE_PRIVATE)            /* incomplete RPC */
++          {
++              EPRINTF5 (DBG_RCVR, "%s: ep4rcvr_disconnect_callback: rcvr %p rxd %p nodeId %d state %x - not able to failover\n",
++                        rail->r_generic.Name, rcvr, rxd, env->NodeId, rxd->State);
++          
++              EP4_RXD_FORCE_PRIVATE (rxdRail);
++
++              unbind_rxd_rail (rxd, rxdRail);
++              free_rxd_rail (rcvrRail, rxdRail);
++
++              /* Ignore any previous NMD/failover responses */
++              EP_INVALIDATE_XID (rxd->MsgXid);
++              
++              /* Remove from active list */
++              list_del (&rxd->Link);
++              
++              if (rxd->State == EP_RXD_RPC_IN_PROGRESS)                               /* ownder by user .... */
++                  rxd->State = EP_RXD_BEEN_ABORTED;
++              else                                                                    /* queue for completion */
++              {
++                  rxd->RxdMain->Len = EP_CONN_RESET;                                  /* ensure ep_rxd_status() fails */
++                  list_add_tail (&rxd->Link, &rxdList);
++              }
++              continue;
++          }
++          break;
++
++      case EP_RXD_BEEN_ABORTED:
++          printk ("ep4rcvr_disconnect_callback: rxd state is aborted but bound to a rail\n");
++          break;
++      }
++
++      printk ("%s: ep4rcvr_disconnect_callback: rcvr %p rxd %p nodeId %d - finished\n", 
++                rail->r_generic.Name, rcvr, rxd, env->NodeId);
++      EPRINTF4 (DBG_RCVR, "%s: ep4rcvr_disconnect_callback: rcvr %p rxd %p nodeId %d - finished\n", 
++                rail->r_generic.Name, rcvr, rxd, env->NodeId);
++      ep4rcvr_display_rxd (&di_ep_debug, &rxdRail->rxd_generic);
++    }
++    
++    EP4_SPINEXIT (dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_thread_lock), &rcvrRail->rcvr_main->rcvr_thread_lock);
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++
++    while (! list_empty (&rxdList)) 
++    {
++      EP_RXD *rxd = list_entry (rxdList.next, EP_RXD, Link);
++
++      list_del (&rxd->Link);
++
++      rxd->Handler (rxd);
++    }
++}
++
++void
++ep4rcvr_neterr_flush (EP_RCVR *rcvr, EP4_RCVR_RAIL *rcvrRail, unsigned int nodeId, EP_NETERR_COOKIE *cookies)
++{
++    EP4_COMMS_RAIL *commsRail = RCVR_TO_COMMS(rcvrRail);
++    EP4_RAIL       *rail      = RCVR_TO_RAIL (rcvrRail);
++    ELAN4_DEV      *dev       = rail->r_ctxt.ctxt_dev;
++    unsigned long   flags;
++
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    EP4_SPINENTER (dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_thread_lock), &rcvrRail->rcvr_main->rcvr_thread_lock);
++
++    /* Insert an setevent command into the thread's command queue
++     * to ensure that all sten packets have completed */
++    elan4_guard (rcvrRail->rcvr_ecq->ecq_cq, GUARD_ALL_CHANNELS);
++    ep4comms_flush_setevent (commsRail, rcvrRail->rcvr_ecq->ecq_cq);
++    
++    EP4_SPINEXIT (dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_thread_lock), &rcvrRail->rcvr_main->rcvr_thread_lock);
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++}
++
++void
++ep4rcvr_neterr_check (EP_RCVR *rcvr, EP4_RCVR_RAIL *rcvrRail, unsigned int nodeId, EP_NETERR_COOKIE *cookies)
++{
++    EP4_RAIL         *rail = RCVR_TO_RAIL (rcvrRail);
++    ELAN4_DEV        *dev = rail->r_ctxt.ctxt_dev;
++    struct list_head *el;
++    unsigned long     flags;
++
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    EP4_SPINENTER (dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_thread_lock), &rcvrRail->rcvr_main->rcvr_thread_lock);
++    
++    list_for_each (el, &rcvr->ActiveDescList) {
++      EP_RXD       *rxd      = list_entry (el, EP_RXD, Link);
++      EP4_RXD_RAIL *rxdRail  = (EP4_RXD_RAIL *) rxd->RxdRail;
++      EP_ENVELOPE  *env      = &rxd->RxdMain->Envelope;
++
++      if (rxd->RxdMain->Len == EP_RXD_PENDING || !RXD_BOUND2RAIL(rxdRail,rcvrRail) || env->NodeId != nodeId)
++          continue;
++
++      if (rxd->State == EP_RXD_RECEIVE_ACTIVE || rxd->State == EP_RXD_GET_ACTIVE)
++      {
++          EP_NETERR_COOKIE cookie;
++          unsigned int     first, this;
++
++          if (rxd->State == EP_RXD_RECEIVE_ACTIVE)
++              first = (EP_MAXFRAG+1) - (( EP_IS_MULTICAST(env->Attr) ? 1 : 0) + (env->nFrags == 0 ? 1 : env->nFrags));
++          else
++              first = (EP_MAXFRAG+1) - rxd->nFrags;
++
++          for (this = first; this < (EP_MAXFRAG+1); this++)
++              if (rxdRail->rxd_main->rxd_sent[this] == EP4_STATE_ACTIVE)
++                  break;
++          
++          if (this > first)
++          {
++              /* Look at the last completed STEN packet and if it's neterr cookie matches, then change
++               * the rxd to look the same as if the sten packet had failed and then schedule it for retry */
++              cookie = elan4_sdram_readq (dev, rxdRail->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[--this].c_cookie));
++              
++              if (cookie == cookies[0] || cookie == cookies[1])
++              {
++                  EPRINTF5 (DBG_NETWORK_ERROR, "%s: ep4rcvr_neterr_check: cookie <%lld%s%s%s%s> matches rxd %p rxdRail %p this %d\n",
++                            rail->r_generic.Name, EP4_COOKIE_STRING(cookie), rxd, rxdRail, this);
++                  
++                  printk ("%s: ep4rcvr_neterr_check: cookie <%lld%s%s%s%s> matches rxd %p rxdRail %p this %d : time %ld\n",
++                          rail->r_generic.Name, EP4_COOKIE_STRING(cookie), rxd, rxdRail, this, rxdRail->rxd_retry_time);
++                  
++                  rxdRail->rxd_main->rxd_sent[this] = EP4_STATE_ACTIVE;
++                  rxdRail->rxd_main->rxd_failed     = EP4_STATE_FAILED;
++                  
++                  spin_lock (&rcvrRail->rcvr_retrylock);
++                  
++                  ASSERT (rxdRail->rxd_retry_time == 0);
++
++                  rxdRail->rxd_retry_time = lbolt + EP_RETRY_LOW_PRI_TIME;
++                      
++                  list_add_tail (&rxdRail->rxd_retry_link, &rcvrRail->rcvr_retrylist);
++                      
++                  ep_kthread_schedule (&rail->r_retry_thread, rxdRail->rxd_retry_time);
++                  
++                  spin_unlock (&rcvrRail->rcvr_retrylock);
++              }
++          }
++      }
++    }
++    EP4_SPINEXIT (dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_thread_lock), &rcvrRail->rcvr_main->rcvr_thread_lock);
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++}
++
++int
++ep4rcvr_queue_rxd (EP_RXD *rxd, EP_RCVR_RAIL *r)
++{
++    EP4_RCVR_RAIL *rcvrRail = (EP4_RCVR_RAIL *) r;
++    EP4_RAIL      *rail     = RCVR_TO_RAIL (rcvrRail);
++    ELAN4_DEV     *dev      = rail->r_ctxt.ctxt_dev;
++    EP4_RXD_RAIL  *rxdRail;
++    register int   i;
++
++    ASSERT (SPINLOCK_HELD(&rxd->Rcvr->Lock));
++
++    if ((rxdRail = get_rxd_rail (rcvrRail)) == NULL)
++      return 0;
++    
++    /* Flush the Elan TLB if mappings have changed */
++    ep_perrail_dvma_sync (&rail->r_generic);
++
++    EPRINTF6 (DBG_RCVR, "%s: ep4rcvr_queue_rxd: rcvr %p rxd %p rxdRail %p buffer %x len %x\n", 
++            rail->r_generic.Name, rxd->Rcvr, rxd, rxdRail, rxd->Data.nmd_addr, rxd->Data.nmd_len);
++
++    /* bind the rxdRail and rxd together */
++    bind_rxd_rail (rxd, rxdRail);
++
++    elan4_sdram_writel (dev, rxdRail->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_buffer.nmd_addr), rxd->Data.nmd_addr);      /* PCI write */
++    elan4_sdram_writel (dev, rxdRail->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_buffer.nmd_len),  rxd->Data.nmd_len);               /* PCI write */
++    elan4_sdram_writel (dev, rxdRail->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_buffer.nmd_attr), rxd->Data.nmd_attr);      /* PCI write */
++
++    /* Mark as active */
++    elan4_sdram_writeq (dev, rxdRail->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done.ev_CountAndType), 
++                      E4_EVENT_INIT_VALUE(-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++    
++    for (i = 0; i <= EP_MAXFRAG; i++)
++      rxdRail->rxd_main->rxd_sent[i] = EP4_STATE_ACTIVE;
++
++    rxdRail->rxd_main->rxd_failed = EP4_STATE_ACTIVE;
++    rxdRail->rxd_main->rxd_done = EP4_STATE_ACTIVE;
++
++    elan4_sdram_writeq (dev, rxdRail->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[0]) + 0x00, /* %r0 */
++                      ep_symbol (&rail->r_threadcode, "c_queue_rxd"));
++    elan4_sdram_writeq (dev, rxdRail->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[0]) + 0x10, /* %r2 */
++                      rcvrRail->rcvr_elan_addr);
++    elan4_sdram_writeq (dev, rxdRail->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[0]) + 0x18, /* %r3 */
++                      rxdRail->rxd_elan_addr);
++
++    elan4_sdram_writeq (dev, rxdRail->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_start.ev_CountAndType),
++                      E4_EVENT_INIT_VALUE(-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_RXD_START_CMD_NDWORDS));
++
++    ep4_set_event_cmd (rxdRail->rxd_scq, rxdRail->rxd_elan_addr + offsetof (EP4_RXD_RAIL_ELAN, rxd_start));
++
++    return 1;
++}
++
++void
++ep4rcvr_rpc_put (EP_RXD *rxd, EP_NMD *local, EP_NMD *remote, unsigned nFrags)
++{
++    EP4_RXD_RAIL    *rxdRail  = (EP4_RXD_RAIL *) rxd->RxdRail;
++    EP4_RCVR_RAIL   *rcvrRail = (EP4_RCVR_RAIL *) rxdRail->rxd_generic.RcvrRail;
++    EP4_RAIL        *rail     = RCVR_TO_RAIL (rcvrRail);
++    ELAN4_DEV     *dev      = RCVR_TO_DEV (rcvrRail);
++    sdramaddr_t     rxdElan   = rxdRail->rxd_elan;
++    EP_ENVELOPE    *env       = &rxd->RxdMain->Envelope;
++    unsigned long   first     = (EP_MAXFRAG+1) - nFrags;
++    EP4_RXD_DMA_CMD cmd;
++    register int    i, len;
++
++    EP4_ASSERT (rail, rxd->State == EP_RXD_PUT_ACTIVE);
++    EP4_ASSERT (rail, rxdRail->rxd_main->rxd_done == EP4_STATE_PRIVATE);
++    EP4_SDRAM_ASSERT (rail, rxdRail->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done.ev_CountAndType),
++                    E4_EVENT_INIT_VALUE (0, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++
++    /* Flush the Elan TLB if mappings have changed */
++    ep_perrail_dvma_sync (&rail->r_generic);
++
++    /* Generate the DMA chain to put the data */
++    for (i = 0, len = 0; i < nFrags; i++, len += local->nmd_len, local++, remote++)
++    {
++      cmd.c_dma_typeSize     = RUN_DMA_CMD | E4_DMA_TYPE_SIZE(local->nmd_len, DMA_DataTypeByte, 0, EP4_DMA_RETRYCOUNT);
++      cmd.c_dma_cookie       = ep4_neterr_cookie (rail, env->NodeId) | EP4_COOKIE_DMA;
++      cmd.c_dma_vproc        = EP_VP_DATA(env->NodeId);
++      cmd.c_dma_srcAddr      = local->nmd_addr;
++      cmd.c_dma_dstAddr      = remote->nmd_addr;
++      if (i == (nFrags-1))
++          cmd.c_dma_srcEvent = rxdRail->rxd_elan_addr + offsetof (EP4_RXD_RAIL_ELAN, rxd_done);
++      else
++          cmd.c_dma_srcEvent = rxdRail->rxd_elan_addr + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[first + i]);
++      cmd.c_dma_dstEvent     = 0;
++      cmd.c_nop_cmd          = NOP_CMD;
++
++      EPRINTF7 (DBG_RCVR, "%s: ep4rcvr_rpc_put: rxd %p [XID=%llx] idx=%d Source=%08x Dest=%08x Len=%x\n", 
++                rail->r_generic.Name, rxd, env->Xid.Unique, i, local->nmd_addr, remote->nmd_addr, local->nmd_len);
++      
++      elan4_sdram_copyq_to_sdram (dev, &cmd, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[first + i]), sizeof (EP4_RXD_DMA_CMD));
++    }
++
++    /* Initialise the event chain */
++    for (i = 0; i < nFrags-1; i++)
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[first + i]), 
++                          E4_EVENT_INIT_VALUE(-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_RXD_DMA_CMD_NDWORDS));
++
++    elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done),
++                      E4_EVENT_INIT_VALUE(-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++
++    for (i = 0; i <= EP_MAXFRAG; i++)
++      rxdRail->rxd_main->rxd_sent[i] = EP4_STATE_ACTIVE;
++
++    rxdRail->rxd_main->rxd_failed = EP4_STATE_ACTIVE;
++    rxdRail->rxd_main->rxd_done = EP4_STATE_ACTIVE;
++
++    /* Initialise the previous event to start the whole chain off */
++    elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[first - 1]),
++                      E4_EVENT_INIT_VALUE(-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_RXD_DMA_CMD_NDWORDS));
++
++    ASSERT (rail->r_generic.Nodes[env->NodeId].State >= EP_NODE_CONNECTED && rail->r_generic.Nodes[env->NodeId].State <= EP_NODE_LOCAL_PASSIVATE);
++
++    /* finally issue the setevent to start the whole chain */
++    ep4_set_event_cmd (rxdRail->rxd_scq, rxdRail->rxd_elan_addr + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[first - 1]));
++
++    BucketStat (rxd->Rcvr->Subsys, RPCPut, len);
++}    
++
++void
++ep4rcvr_rpc_get (EP_RXD *rxd, EP_NMD *local, EP_NMD *remote, unsigned nFrags)
++{
++    EP4_RXD_RAIL    *rxdRail  = (EP4_RXD_RAIL *) rxd->RxdRail;
++    EP4_RCVR_RAIL   *rcvrRail = (EP4_RCVR_RAIL *) rxdRail->rxd_generic.RcvrRail;
++    EP4_RAIL        *rail     = RCVR_TO_RAIL (rcvrRail);
++    ELAN4_DEV     *dev      = RCVR_TO_DEV (rcvrRail);
++    sdramaddr_t      rxdElan  = rxdRail->rxd_elan;
++    EP_ENVELOPE     *env      = &rxd->RxdMain->Envelope;
++    unsigned long    first    = (EP_MAXFRAG+1) - nFrags;
++    register int    i, len;
++
++    EP4_ASSERT (rail, rxd->State == EP_RXD_GET_ACTIVE);
++    EP4_ASSERT (rail, rxdRail->rxd_main->rxd_done == EP4_STATE_PRIVATE);
++    EP4_SDRAM_ASSERT (rail, rxdRail->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done.ev_CountAndType),
++                    E4_EVENT_INIT_VALUE (0, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++
++    /* Flush the Elan TLB if mappings have changed */
++    ep_perrail_dvma_sync (&rail->r_generic);
++
++    /* Generate the DMA chain to put the data */
++    for (i = 0, len = 0; i < nFrags; i++, len += local->nmd_len, local++, remote++)
++    {
++      EPRINTF7 (DBG_RCVR, "%s: ep4rcvr_rpc_get rxd %p [XID=%llx] idx=%d Source=%08x Dest=%08x Len=%x\n", 
++                rail->r_generic.Name, rxd, env->Xid.Unique, i, remote->nmd_addr, local->nmd_addr, remote->nmd_len);
++      
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[first + i].c_open),
++                          OPEN_STEN_PKT_CMD | OPEN_PACKET(0, PACK_OK | RESTART_COUNT_ZERO, EP_VP_DATA(env->NodeId)));
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[first + i].c_trans),
++                          SEND_TRANS_CMD | ((TR_REMOTEDMA | TR_WAIT_FOR_EOP) << 16));
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[first + i].c_cookie),
++                          ep4_neterr_cookie (rail, env->NodeId) | EP4_COOKIE_STEN);
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[first + i].c_dma_typeSize),
++                          E4_DMA_TYPE_SIZE (local->nmd_len, DMA_DataTypeByte, 0, EP4_DMA_RETRYCOUNT));
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[first + i].c_dma_cookie),
++                          ep4_neterr_cookie (rail, env->NodeId) | EP4_COOKIE_DMA);
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[first + i].c_dma_vproc),
++                          EP_VP_DATA (rail->r_generic.Position.pos_nodeid));
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[first + i].c_dma_srcAddr),
++                          remote->nmd_addr);
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[first + i].c_dma_dstAddr),
++                          local->nmd_addr);
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[first + i].c_dma_srcEvent),
++                          0);
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[first + i].c_dma_dstEvent),
++                          i == (nFrags-1) ? rxdRail->rxd_elan_addr + offsetof (EP4_RXD_RAIL_ELAN, rxd_done) : 
++                                            rxdRail->rxd_elan_addr + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[first + i]));
++    }
++
++    /* Initialise the event chain */
++    for (i = 0; i < nFrags-1; i++)
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[first + i]), 
++                          E4_EVENT_INIT_VALUE(-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_RXD_STEN_CMD_NDWORDS));
++
++    elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done),
++                      E4_EVENT_INIT_VALUE(-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++
++    for (i = 0; i <= EP_MAXFRAG; i++)
++      rxdRail->rxd_main->rxd_sent[i] = EP4_STATE_ACTIVE;
++
++    rxdRail->rxd_main->rxd_failed = EP4_STATE_ACTIVE;
++    rxdRail->rxd_main->rxd_done = EP4_STATE_ACTIVE;
++
++    /* Initialise the previous event to start the whole chain off */
++    elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[first - 1]),
++                      E4_EVENT_INIT_VALUE(-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_RXD_STEN_CMD_NDWORDS));
++
++    ASSERT (rail->r_generic.Nodes[env->NodeId].State >= EP_NODE_CONNECTED && rail->r_generic.Nodes[env->NodeId].State <= EP_NODE_LOCAL_PASSIVATE);
++
++    /* finally issue the setevent to start the whole chain */
++    ep4_set_event_cmd (rxdRail->rxd_scq, rxdRail->rxd_elan_addr + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[first - 1]));
++
++    BucketStat (rxd->Rcvr->Subsys, RPCPut, len);
++}
++
++void
++ep4rcvr_rpc_complete (EP_RXD *rxd, EP_NMD *local, EP_NMD *remote, unsigned nFrags)
++{
++    EP4_RXD_RAIL    *rxdRail  = (EP4_RXD_RAIL *) rxd->RxdRail;
++    EP4_RCVR_RAIL   *rcvrRail = (EP4_RCVR_RAIL *) rxdRail->rxd_generic.RcvrRail;
++    EP4_RAIL        *rail     = RCVR_TO_RAIL (rcvrRail);
++    ELAN4_DEV     *dev      = RCVR_TO_DEV (rcvrRail);
++    sdramaddr_t     rxdElan   = rxdRail->rxd_elan;
++    EP_ENVELOPE    *env       = &rxd->RxdMain->Envelope;
++    unsigned long   first     = (EP_MAXFRAG+1) - nFrags - 1;
++    EP4_RXD_DMA_CMD cmd;
++    register int    i, len;
++
++    EP4_ASSERT (rail, rxd->State == EP_RXD_COMPLETE_ACTIVE);
++    EP4_ASSERT (rail, rxdRail->rxd_main->rxd_done == EP4_STATE_PRIVATE);
++    EP4_SDRAM_ASSERT (rail, rxdRail->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done.ev_CountAndType),
++                    E4_EVENT_INIT_VALUE (0, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++
++    /* Flush the Elan TLB if mappings have changed */
++    ep_perrail_dvma_sync (&rail->r_generic);
++
++    /* Generate the DMA chain to put the data */
++    for (i = 0, len = 0; i < nFrags; i++, len += local->nmd_len, local++, remote++)
++    {
++      cmd.c_dma_typeSize = RUN_DMA_CMD | E4_DMA_TYPE_SIZE(local->nmd_len, DMA_DataTypeByte, 0, EP4_DMA_RETRYCOUNT);
++      cmd.c_dma_cookie   = ep4_neterr_cookie (rail, env->NodeId) | EP4_COOKIE_DMA;
++      cmd.c_dma_vproc    = EP_VP_DATA(env->NodeId);
++      cmd.c_dma_srcAddr  = local->nmd_addr;
++      cmd.c_dma_dstAddr  = remote->nmd_addr;
++      cmd.c_dma_srcEvent = rxdRail->rxd_elan_addr + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[first + i]);
++      cmd.c_dma_dstEvent = 0;
++      cmd.c_nop_cmd      = NOP_CMD;
++
++      EPRINTF7 (DBG_RCVR, "%s: ep4rcvr_rpc_complete: rxd %p [XID=%llx] idx=%d Source=%08x Dest=%08x Len=%x\n", 
++                rail->r_generic.Name, rxd, env->Xid.Unique, i, local->nmd_addr, remote->nmd_addr, local->nmd_len);
++
++      elan4_sdram_copyq_to_sdram (dev, &cmd, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[first + i]), sizeof (EP4_RXD_DMA_CMD));
++    }
++    
++    /* Initialise the status block dma */
++    cmd.c_dma_typeSize = RUN_DMA_CMD | E4_DMA_TYPE_SIZE(EP_STATUSBLK_SIZE, DMA_DataTypeByte, 0, EP4_DMA_RETRYCOUNT);
++    cmd.c_dma_cookie   = ep4_neterr_cookie (rail, env->NodeId) | EP4_COOKIE_DMA;
++    cmd.c_dma_vproc    = EP_VP_DATA(env->NodeId);
++    cmd.c_dma_srcAddr  = rxd->NmdMain.nmd_addr + offsetof (EP_RXD_MAIN, StatusBlk);
++    cmd.c_dma_dstAddr  = env->TxdMain.nmd_addr + offsetof (EP_TXD_MAIN, StatusBlk);
++    cmd.c_dma_srcEvent = rxdRail->rxd_elan_addr + offsetof (EP4_RXD_RAIL_ELAN, rxd_done);
++    cmd.c_dma_dstEvent = env->TxdRail + offsetof (EP4_TXD_RAIL_ELAN, txd_done);;
++    cmd.c_nop_cmd      = NOP_CMD;
++
++    EPRINTF6 (DBG_RCVR, "%s: ep4rcvr_rpc_complete: rxd %p [XID=%llx] statusblk source=%08x dest=%08x len=%x\n", 
++            rail->r_generic.Name, rxd, env->Xid.Unique, (int) cmd.c_dma_srcAddr, (int) cmd.c_dma_dstAddr, EP_STATUSBLK_SIZE);
++
++    elan4_sdram_copyq_to_sdram (dev, &cmd, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[EP_MAXFRAG]), sizeof (EP4_RXD_DMA_CMD));
++
++    /* Initialise the event chain */
++    for (i = 0; i < nFrags; i++)
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[first + i]), 
++                          E4_EVENT_INIT_VALUE(-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_RXD_DMA_CMD_NDWORDS));
++
++    elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done),
++                      E4_EVENT_INIT_VALUE(-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++
++    for (i = 0; i <= EP_MAXFRAG; i++)
++      rxdRail->rxd_main->rxd_sent[i] = EP4_STATE_ACTIVE;
++
++    rxdRail->rxd_main->rxd_failed = EP4_STATE_ACTIVE;
++    rxdRail->rxd_main->rxd_done = EP4_STATE_ACTIVE;
++
++    /* Initialise the previous event to start the whole chain off */
++    elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[first - 1]),
++                      E4_EVENT_INIT_VALUE(-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_RXD_DMA_CMD_NDWORDS));
++
++    ASSERT (rail->r_generic.Nodes[env->NodeId].State >= EP_NODE_CONNECTED && rail->r_generic.Nodes[env->NodeId].State <= EP_NODE_LOCAL_PASSIVATE);
++
++    /* finally issue the setevent to start the whole chain */
++    ep4_set_event_cmd (rxdRail->rxd_scq, rxdRail->rxd_elan_addr + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[first - 1]));
++
++    BucketStat (rxd->Rcvr->Subsys, CompleteRPC, len);
++}
++
++EP_RXD *
++ep4rcvr_steal_rxd (EP_RCVR_RAIL *r)
++{
++    /* XXXX - TBD */
++    return NULL;
++}
++
++long
++ep4rcvr_check (EP_RCVR_RAIL *r, long nextRunTime)
++{
++    EP4_RCVR_RAIL *rcvrRail = (EP4_RCVR_RAIL *) r;
++    EP4_RAIL      *rail     = RCVR_TO_RAIL (rcvrRail);
++
++    if (rcvrRail->rcvr_freecount < ep_rxd_lowat && !alloc_rxd_block (rcvrRail))
++    {
++      EPRINTF1 (DBG_RCVR,"%s: failed to grow rxd rail pool\n", rail->r_generic.Name);
++              
++      if (nextRunTime == 0 || AFTER (nextRunTime, lbolt + RESOURCE_RETRY_TIME))
++          nextRunTime = lbolt + RESOURCE_RETRY_TIME;
++    }
++    
++    return nextRunTime;
++}
++
++unsigned long
++ep4rcvr_retry (EP4_RAIL *rail, void *arg, unsigned long nextRunTime)
++{
++    EP4_RCVR_RAIL *rcvrRail = (EP4_RCVR_RAIL *) arg;
++    ELAN4_DEV     *dev      = RCVR_TO_DEV(rcvrRail);
++    unsigned long  flags;
++
++    spin_lock_irqsave (&rcvrRail->rcvr_retrylock, flags);
++    while (! list_empty (&rcvrRail->rcvr_retrylist))
++    {
++      EP4_RXD_RAIL *rxdRail = list_entry (rcvrRail->rcvr_retrylist.next, EP4_RXD_RAIL, rxd_retry_link);
++      EP_ENVELOPE  *env     = &rxdRail->rxd_generic.Rxd->RxdMain->Envelope;
++      unsigned int  first   = (EP_MAXFRAG+1) - ((env->Attr & EP_MULTICAST ? 1 : 0) + (env->nFrags == 0 ? 1 : env->nFrags));
++      
++      if (BEFORE (lbolt, rxdRail->rxd_retry_time))
++      {
++          if (nextRunTime == 0 || AFTER (nextRunTime, rxdRail->rxd_retry_time))
++              nextRunTime = rxdRail->rxd_retry_time;
++
++          break;
++      }
++
++      list_del (&rxdRail->rxd_retry_link);
++      rxdRail->rxd_retry_time = 0;
++
++      /* determine which sten packet to resubmit */
++      for (; first < (EP_MAXFRAG+1); first++)
++          if (rxdRail->rxd_main->rxd_sent[first] == EP4_STATE_ACTIVE)
++              break;
++
++      EPRINTF3 (DBG_RETRY, "%s: ep4rcvr_retry: rxdRail %p, reissuing sten[%d]\n", rail->r_generic.Name, rxdRail, first);
++
++      /* re-initialise the fail event */
++      elan4_sdram_writeq (dev, rxdRail->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_failed.ev_CountAndType),
++                          E4_EVENT_INIT_VALUE (-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++
++      rxdRail->rxd_main->rxd_failed = EP4_STATE_ACTIVE;
++
++      /* re-initialise the chain event to resubmit this sten packet */
++      elan4_sdram_writeq (dev, rxdRail->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[first-1].ev_CountAndType),
++                          E4_EVENT_INIT_VALUE(-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_RXD_STEN_CMD_NDWORDS));
++      
++      /* finally issue the setevent to start the chain again */
++      ep4_set_event_cmd (rxdRail->rxd_scq, rxdRail->rxd_elan_addr + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[first - 1]));
++    }
++    spin_unlock_irqrestore (&rcvrRail->rcvr_retrylock, flags);
++    
++    return nextRunTime;
++}
++
++void
++ep4rcvr_add_rail (EP_RCVR *rcvr, EP_COMMS_RAIL *commsRail)
++{
++    EP4_RAIL          *rail   = (EP4_RAIL *) commsRail->Rail;
++    ELAN4_DEV         *dev    = rail->r_ctxt.ctxt_dev;
++    sdramaddr_t        qdescs = ((EP4_COMMS_RAIL *) commsRail)->r_descs;
++    EP4_RCVR_RAIL     *rcvrRail;
++    E4_InputQueue      qdesc;
++    E4_ThreadRegs      tregs;
++    sdramaddr_t        stack;
++    unsigned long      flags;
++
++    KMEM_ZALLOC (rcvrRail, EP4_RCVR_RAIL *, sizeof (EP4_RCVR_RAIL), 1);
++
++    spin_lock_init (&rcvrRail->rcvr_freelock);
++    INIT_LIST_HEAD (&rcvrRail->rcvr_freelist);
++    INIT_LIST_HEAD (&rcvrRail->rcvr_blocklist);
++
++    kcondvar_init (&rcvrRail->rcvr_cleanup_sleep);
++    kcondvar_init (&rcvrRail->rcvr_freesleep);
++
++    INIT_LIST_HEAD (&rcvrRail->rcvr_retrylist);
++    spin_lock_init (&rcvrRail->rcvr_retrylock);
++
++    rcvrRail->rcvr_generic.CommsRail = commsRail;
++    rcvrRail->rcvr_generic.Rcvr      = rcvr;
++
++    rcvrRail->rcvr_main  = ep_alloc_main (&rail->r_generic, sizeof (EP4_RCVR_RAIL_MAIN), 0, &rcvrRail->rcvr_main_addr);
++    rcvrRail->rcvr_elan  = ep_alloc_elan (&rail->r_generic, sizeof (EP4_RCVR_RAIL_ELAN), 0, &rcvrRail->rcvr_elan_addr);
++    rcvrRail->rcvr_slots = ep_alloc_elan (&rail->r_generic, EP_INPUTQ_SIZE * rcvr->InputQueueEntries, 0, &rcvrRail->rcvr_slots_addr);
++    stack                = ep_alloc_elan (&rail->r_generic, EP4_STACK_SIZE, 0, &rcvrRail->rcvr_stack);
++
++    /* allocate a command queue for the thread to use, plus space for it to wait/reschedule */
++    rcvrRail->rcvr_ecq     = ep4_alloc_ecq (rail, CQ_Size64K);
++    rcvrRail->rcvr_resched = ep4_get_ecq (rail, EP4_ECQ_ATOMIC, 8);
++
++    ep4_register_intcookie (rail, &rcvrRail->rcvr_stall_intcookie, rcvrRail->rcvr_elan_addr + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_stall_intcookie),
++                          rcvr_stall_interrupt, rcvrRail);
++
++    /* Initialise the elan portion */
++    elan4_sdram_writeq (dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_qevent.ev_CountAndType), 0);
++    elan4_sdram_writeq (dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_thread_halt.ev_CountAndType), 0);
++    elan4_sdram_writeq (dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_thread_lock), 0);
++    elan4_sdram_writeq (dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_pending_tailp),
++                      rcvrRail->rcvr_elan_addr + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_pending_head));
++    elan4_sdram_writeq (dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_pending_head), 0);
++    elan4_sdram_writeq (dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_stall_intcookie), 0);
++    elan4_sdram_writeq (dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_qbase), rcvrRail->rcvr_slots_addr);
++    elan4_sdram_writeq (dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_qlast), 
++                      rcvrRail->rcvr_slots_addr + EP_INPUTQ_SIZE * (rcvr->InputQueueEntries-1));
++
++    /* Initialise the main memory portion */
++    rcvrRail->rcvr_main->rcvr_thread_lock = 0;
++
++    /* Install our retry handler */
++    rcvrRail->rcvr_retryops.op_func = ep4rcvr_retry;
++    rcvrRail->rcvr_retryops.op_arg  = rcvrRail;
++
++    ep4_add_retry_ops (rail, &rcvrRail->rcvr_retryops);
++
++    /* Update the queue desriptor */
++    qdesc.q_bptr    = rcvrRail->rcvr_slots_addr;
++    qdesc.q_fptr    = rcvrRail->rcvr_slots_addr;
++    qdesc.q_control = E4_InputQueueControl (rcvrRail->rcvr_slots_addr, rcvrRail->rcvr_slots_addr + (EP_INPUTQ_SIZE * (rcvr->InputQueueEntries-1)), EP_INPUTQ_SIZE);
++    qdesc.q_event   = rcvrRail->rcvr_elan_addr + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_qevent);
++
++    ep4_write_qdesc (rail, qdescs + (rcvr->Service * EP_QUEUE_DESC_SIZE), &qdesc);
++
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    rcvr->Rails[rail->r_generic.Number] = &rcvrRail->rcvr_generic;
++    rcvr->RailMask |= EP_RAIL2RAILMASK (rail->r_generic.Number);
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++
++    {
++      sdramaddr_t stackTop     = stack + EP4_STACK_SIZE;
++      E4_Addr     stackTopAddr = rcvrRail->rcvr_stack + EP4_STACK_SIZE;
++
++      ep4_init_thread (rail, &tregs, stackTop, stackTopAddr, ep_symbol (&rail->r_threadcode, "ep4comms_rcvr"), 6, 
++                       (E4_uint64) rail->r_elan_addr, (E4_uint64) rcvrRail->rcvr_elan_addr, (E4_uint64) rcvrRail->rcvr_main_addr,
++                       (E4_uint64) EP_MSGQ_ADDR(rcvr->Service), (E4_uint64) rcvrRail->rcvr_ecq->ecq_addr, (E4_uint64) rcvrRail->rcvr_resched->ecq_addr);
++    }
++    
++    /* Issue the command to the threads private command queue */
++    elan4_run_thread_cmd (rcvrRail->rcvr_ecq->ecq_cq, &tregs);
++
++    ep_procfs_rcvr_add_rail(&(rcvrRail->rcvr_generic));
++}
++
++void
++ep4rcvr_del_rail (EP_RCVR *rcvr, EP_COMMS_RAIL *commsRail)
++{
++    EP4_RAIL         *rail     = (EP4_RAIL *) commsRail->Rail;
++    EP4_RCVR_RAIL    *rcvrRail = (EP4_RCVR_RAIL *) rcvr->Rails[rail->r_generic.Number];  
++    ELAN4_HALTOP      haltop;
++    struct list_head *el, *nel;
++    unsigned long     flags;
++
++    ep_procfs_rcvr_del_rail(&(rcvrRail->rcvr_generic));
++
++    /* Run a halt operation to mark the input queue as full and
++     * request the thread to halt */
++    haltop.op_mask     = INT_DiscardingHighPri | INT_TProcHalted;
++    haltop.op_function = rcvr_stall_haltop;
++    haltop.op_arg      = rcvrRail;
++
++    elan4_queue_haltop (rail->r_ctxt.ctxt_dev, &haltop);
++
++    /* Wait for the thread to tell us it's processed the input queue */
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    while (! rcvrRail->rcvr_thread_halted)
++      kcondvar_wait (&rcvrRail->rcvr_cleanup_sleep, &rcvr->Lock, &flags);
++    rcvrRail->rcvr_thread_halted = 0;
++
++    /* flag the rail as no longer available */
++    rcvr->RailMask &= ~EP_RAIL2RAILMASK (rail->r_generic.Number);
++
++    /* wait for all active communications to terminate */
++    for (;;)
++    {
++      int mustWait = 0;
++
++      list_for_each (el, &rcvr->ActiveDescList) {
++          EP_RXD       *rxd     = list_entry (el, EP_RXD, Link);
++          EP4_RXD_RAIL *rxdRail = (EP4_RXD_RAIL *) rxd->RxdRail;
++          
++          if (rxdRail && RXD_BOUND2RAIL (rxdRail, rcvrRail) && rxd->RxdMain->Len != EP_RXD_PENDING)
++          {
++              mustWait++;
++              break;
++          }
++      }
++
++      if (! mustWait)
++          break;
++
++      rcvrRail->rcvr_cleanup_waiting++;
++      kcondvar_wait (&rcvrRail->rcvr_cleanup_sleep, &rcvr->Lock, &flags);
++    }
++
++    /* at this point all rxd's in the list that are bound to the deleting rail are pending */
++    list_for_each_safe (el, nel, &rcvr->ActiveDescList) {
++      EP_RXD       *rxd     = list_entry (el, EP_RXD, Link);
++      EP4_RXD_RAIL *rxdRail = (EP4_RXD_RAIL *) rxd->RxdRail;
++
++      if (rxdRail && RXD_BOUND2RAIL (rxdRail, rcvrRail))
++      {
++          EP4_RXD_ASSERT_PENDING (rxdRail);
++          EP4_RXD_FORCE_PRIVATE (rxdRail);
++
++          unbind_rxd_rail (rxd, rxdRail);
++          free_rxd_rail (rcvrRail, rxdRail);
++      }
++    }
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++
++    /* wait for all rxd's for this rail to become free */
++    spin_lock_irqsave (&rcvrRail->rcvr_freelock, flags);
++    while (rcvrRail->rcvr_freecount != rcvrRail->rcvr_totalcount)
++    {
++      rcvrRail->rcvr_freewaiting++;
++      kcondvar_wait (&rcvrRail->rcvr_freesleep, &rcvrRail->rcvr_freelock, &flags);
++    }
++    spin_unlock_irqrestore (&rcvrRail->rcvr_freelock, flags);
++
++    /* can now remove the rail as it can no longer be used */
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    rcvr->Rails[rail->r_generic.Number] = NULL;
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++
++    /* all the rxd's accociated with DescBlocks must be in the FreeDescList */
++    ASSERT (rcvrRail->rcvr_totalcount == rcvrRail->rcvr_freecount);
++
++    /* run through the DescBlockList deleting them */
++    while (!list_empty (&rcvrRail->rcvr_blocklist))
++      free_rxd_block (rcvrRail, list_entry(rcvrRail->rcvr_blocklist.next, EP4_RXD_RAIL_BLOCK , blk_link));
++
++    /* it had better be empty after that */
++    ASSERT ((rcvrRail->rcvr_totalcount == 0) && (rcvrRail->rcvr_totalcount == rcvrRail->rcvr_freecount));
++
++    ep4_remove_retry_ops (rail, &rcvrRail->rcvr_retryops);
++
++    ep4_deregister_intcookie (rail, &rcvrRail->rcvr_stall_intcookie);
++
++    ep4_put_ecq (rail, rcvrRail->rcvr_resched, 8);
++    ep4_free_ecq (rail, rcvrRail->rcvr_ecq);
++
++    ep_free_elan (&rail->r_generic, rcvrRail->rcvr_stack, EP4_STACK_SIZE);
++    ep_free_elan (&rail->r_generic, rcvrRail->rcvr_slots_addr, EP_INPUTQ_SIZE * rcvr->InputQueueEntries);
++    ep_free_elan (&rail->r_generic, rcvrRail->rcvr_elan_addr, sizeof (EP4_RCVR_RAIL_ELAN));
++    ep_free_main (&rail->r_generic, rcvrRail->rcvr_main_addr, sizeof (EP4_RCVR_RAIL_MAIN));
++
++    KMEM_FREE (rcvrRail, sizeof (EP4_RCVR_RAIL));
++}
++
++void
++ep4rcvr_display_rxd (DisplayInfo *di, EP_RXD_RAIL *r)
++{
++    EP4_RXD_RAIL *rxdRail = (EP4_RXD_RAIL *) r;
++    sdramaddr_t   rxdElan = rxdRail->rxd_elan;
++    EP4_RAIL     *rail    = RCVR_TO_RAIL (rxdRail->rxd_generic.RcvrRail);
++    ELAN4_DEV    *dev     = rail->r_ctxt.ctxt_dev;
++    int i;
++
++    (di->func)(di->arg, "    Rail %d rxd %p elan %lx(%x) main %p(%x) ecq %d scq %d debug %llx\n", rail->r_generic.Number,
++             rxdRail, rxdRail->rxd_elan, rxdRail->rxd_elan_addr, rxdRail->rxd_main, rxdRail->rxd_main_addr,
++             elan4_cq2num(rxdRail->rxd_ecq->ecq_cq), elan4_cq2num(rxdRail->rxd_scq->ecq_cq),
++             elan4_sdram_readq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_debug)));
++    (di->func)(di->arg, "          start    %016llx %016llx %016llx [%016llx %016llx]\n",
++             elan4_sdram_readq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_start.ev_CountAndType)),
++             elan4_sdram_readq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_start.ev_Params[0])),
++             elan4_sdram_readq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_start.ev_Params[1])),
++             elan4_sdram_readq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[0].c_cookie)),
++             elan4_sdram_readq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[0].c_dma_cookie)));
++             
++    for (i = 0; i < EP_MAXFRAG; i++)
++      (di->func)(di->arg, "          chain[%d] %016llx %016llx %016llx [%016llx %016llx]\n", i,
++                 elan4_sdram_readq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[i].ev_CountAndType)),
++                 elan4_sdram_readq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[i].ev_Params[0])),
++                 elan4_sdram_readq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[i].ev_Params[1])),
++                 elan4_sdram_readq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[i+1].c_cookie)),
++                 elan4_sdram_readq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[i+1].c_dma_cookie)));
++    (di->func)(di->arg, "          done    %016llx %016llx %016llx -> %016llx\n",
++             elan4_sdram_readq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done.ev_CountAndType)),
++             elan4_sdram_readq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done.ev_Params[0])),
++             elan4_sdram_readq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done.ev_Params[1])),
++             rxdRail->rxd_main->rxd_done);
++    (di->func)(di->arg, "          fail    %016llx %016llx %016llx -> %016llx\n",
++             elan4_sdram_readq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_failed.ev_CountAndType)),
++             elan4_sdram_readq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_failed.ev_Params[0])),
++             elan4_sdram_readq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_failed.ev_Params[1])),
++             rxdRail->rxd_main->rxd_failed);
++    (di->func)(di->arg, "          next %016llx queued %016llx main %016llx\n",
++             elan4_sdram_readq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_next)),
++             elan4_sdram_readq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_queued)),
++             elan4_sdram_readq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_main)));
++    (di->func)(di->arg, "          sent %016llx %016llx %016llx %016llx %016llx\n",
++             rxdRail->rxd_main->rxd_sent[0], rxdRail->rxd_main->rxd_sent[1], rxdRail->rxd_main->rxd_sent[2],
++             rxdRail->rxd_main->rxd_sent[3], rxdRail->rxd_main->rxd_sent[4]);
++}
++
++void
++ep4rcvr_display_rcvr (DisplayInfo *di, EP_RCVR_RAIL *r)
++{
++    EP_RCVR          *rcvr       = r->Rcvr;
++    EP4_RCVR_RAIL    *rcvrRail   = (EP4_RCVR_RAIL *) r;
++    EP4_COMMS_RAIL   *commsRail  = RCVR_TO_COMMS(rcvrRail);
++    EP4_RAIL         *rail       = RCVR_TO_RAIL (rcvrRail);
++    ELAN4_DEV        *dev        = rail->r_ctxt.ctxt_dev;
++    sdramaddr_t       rcvrElan   = rcvrRail->rcvr_elan;
++    sdramaddr_t       qdesc      = commsRail->r_descs + (rcvr->Service * EP_QUEUE_DESC_SIZE);
++    sdramaddr_t       event      = rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_qevent);
++    unsigned int      freeCount  = 0;
++    unsigned int      blockCount = 0;
++    struct list_head *el;
++    unsigned long     flags;
++    
++    spin_lock_irqsave (&rcvrRail->rcvr_freelock, flags);
++    list_for_each (el, &rcvrRail->rcvr_freelist)
++      freeCount++;
++    list_for_each (el, &rcvrRail->rcvr_blocklist)
++      blockCount++;
++    spin_unlock_irqrestore(&rcvrRail->rcvr_freelock, flags);
++
++    (di->func)(di->arg, "      Rail %d elan %lx(%x) main %p(%x) ecq %d resched %d debug %llx\n",
++             rail->r_generic.Number, rcvrRail->rcvr_elan, rcvrRail->rcvr_elan_addr,
++             rcvrRail->rcvr_main, rcvrRail->rcvr_main_addr, elan4_cq2num(rcvrRail->rcvr_ecq->ecq_cq),
++             elan4_cq2num (rcvrRail->rcvr_resched->ecq_cq),
++             elan4_sdram_readq (dev, rcvrElan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_debug)));
++    (di->func)(di->arg, "        free %d (%d) total %d blocks %d\n",
++             rcvrRail->rcvr_freecount, freeCount, rcvrRail->rcvr_totalcount, blockCount);
++    (di->func)(di->arg, "        spinlock %016llx %016llx\n", rcvrRail->rcvr_main->rcvr_thread_lock,
++             elan4_sdram_readq (dev, rcvrElan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_thread_lock)));
++    (di->func)(di->arg, "        queue: bptr %016llx fptr %016llx control %016llx (base %lx %x)\n",
++             elan4_sdram_readq (dev, qdesc + offsetof (E4_InputQueue, q_bptr)),
++             elan4_sdram_readq (dev, qdesc + offsetof (E4_InputQueue, q_fptr)),
++             elan4_sdram_readq (dev, qdesc + offsetof (E4_InputQueue, q_control)),
++             rcvrRail->rcvr_slots, rcvrRail->rcvr_slots_addr);
++    (di->func)(di->arg, "        event %016llx %016llx %016llx\n",
++             elan4_sdram_readq (dev, event + offsetof (E4_Event32, ev_CountAndType)),
++             elan4_sdram_readq (dev, event + offsetof (E4_Event32, ev_Params[0])),
++             elan4_sdram_readq (dev, event + offsetof (E4_Event32, ev_Params[1])));
++    (di->func)(di->arg, "        pending_tailp %016llx pending_head %016llx\n", 
++             elan4_sdram_readq (dev, rcvrElan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_pending_tailp)),
++             elan4_sdram_readq (dev, rcvrElan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_pending_head)));
++}
++
++void
++ep4rcvr_fillout_rail_stats(EP_RCVR_RAIL *rcvr_rail, char *str) {
++    /* no stats here yet */
++    /* EP4_RCVR_RAIL * ep4rcvr_rail = (EP4_RCVR_RAIL *) rcvr_rail; */
++}
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/ep/epcommsTx.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/epcommsTx.c 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/epcommsTx.c      2005-06-01 23:12:54.654430744 -0400
+@@ -0,0 +1,919 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: epcommsTx.c,v 1.25.2.5 2004/12/09 10:02:42 david Exp $ $Name: QSNETMODULES-4-30_20050128 $"
++/*      $Source: /cvs/master/quadrics/epmod/epcommsTx.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++#include <elan/epsvc.h>
++#include <elan/epcomms.h>
++
++#include "cm.h"
++#include "debug.h"
++
++unsigned int ep_txd_lowat = 5;
++
++static int
++AllocateTxdBlock (EP_XMTR *xmtr, EP_ATTRIBUTE attr, EP_TXD **txdp)
++{
++    EP_TXD_BLOCK *blk;
++    EP_TXD       *txd;
++    EP_TXD_MAIN  *pTxdMain;
++    int                 i;
++    unsigned long flags;
++
++    EPRINTF1 (DBG_XMTR, "AllocateTxdBlock: xmtr=%p\n", xmtr);
++
++    KMEM_ZALLOC (blk, EP_TXD_BLOCK *, sizeof (EP_TXD_BLOCK), ! (attr & EP_NO_SLEEP));
++
++    if (blk == NULL)
++      return -ENOMEM;
++
++    if ((pTxdMain = ep_shared_alloc_main (xmtr->Subsys->Subsys.Sys, EP_TXD_MAIN_SIZE * EP_NUM_TXD_PER_BLOCK, attr, &blk->NmdMain)) == (sdramaddr_t) 0)
++    {
++      KMEM_FREE (blk, sizeof (EP_TXD_BLOCK));
++      return -ENOMEM;
++    }
++
++    for (txd = &blk->Txd[0], i = 0; i < EP_NUM_TXD_PER_BLOCK; i++, txd++)
++    {
++      txd->Xmtr     = xmtr;
++      txd->TxdMain = pTxdMain;
++
++      ep_nmd_subset (&txd->NmdMain, &blk->NmdMain, (i * EP_TXD_MAIN_SIZE), EP_TXD_MAIN_SIZE);
++
++      /* move onto next descriptor */
++      pTxdMain = (EP_TXD_MAIN *) ((unsigned long) pTxdMain + EP_TXD_MAIN_SIZE);
++    }
++
++    spin_lock_irqsave (&xmtr->FreeDescLock, flags);
++
++    list_add  (&blk->Link, &xmtr->DescBlockList);
++    xmtr->TotalDescCount += EP_NUM_TXD_PER_BLOCK;
++
++    for (i = txdp ? 1 : 0; i < EP_NUM_TXD_PER_BLOCK; i++)
++    {
++      list_add (&blk->Txd[i].Link, &xmtr->FreeDescList);
++
++      xmtr->FreeDescCount++;
++
++      if (xmtr->FreeDescWanted)
++      {
++          xmtr->FreeDescWanted--;
++          kcondvar_wakeupone (&xmtr->FreeDescSleep, &xmtr->FreeDescLock);
++      }
++    }
++    spin_unlock_irqrestore (&xmtr->FreeDescLock, flags);
++    
++    if (txdp)
++      *txdp = &blk->Txd[0];
++
++    return 0;
++}
++
++static void
++FreeTxdBlock (EP_XMTR *xmtr, EP_TXD_BLOCK *blk)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&xmtr->FreeDescLock, flags);
++    list_del (&blk->Link);
++
++    xmtr->TotalDescCount -= EP_NUM_RXD_PER_BLOCK;
++    xmtr->FreeDescCount -= EP_NUM_RXD_PER_BLOCK;
++    spin_unlock_irqrestore (&xmtr->FreeDescLock, flags);
++
++    ep_shared_free_main (xmtr->Subsys->Subsys.Sys, &blk->NmdMain);
++    KMEM_FREE (blk, sizeof (EP_TXD_BLOCK));
++}
++
++static EP_TXD *
++GetTxd (EP_XMTR *xmtr, EP_ATTRIBUTE attr)
++{
++    EP_COMMS_SUBSYS *subsys = xmtr->Subsys;
++    EP_TXD          *txd;
++    int low_on_txds;
++    unsigned long flags;
++
++    spin_lock_irqsave (&xmtr->FreeDescLock, flags);
++
++    while (list_empty (&xmtr->FreeDescList))
++    {
++      if (! (attr & EP_NO_ALLOC))
++      {
++          spin_unlock_irqrestore (&xmtr->FreeDescLock, flags);
++
++          if (AllocateTxdBlock (xmtr, attr, &txd) == ESUCCESS)
++              return (txd);
++
++          spin_lock_irqsave (&xmtr->FreeDescLock, flags);
++      }
++
++      if (attr & EP_NO_SLEEP)
++      {
++          spin_unlock_irqrestore (&xmtr->FreeDescLock, flags);
++
++          return (NULL);
++      }
++
++      xmtr->FreeDescWanted++;
++      kcondvar_wait (&xmtr->FreeDescSleep, &xmtr->FreeDescLock, &flags);
++    }
++
++    txd = list_entry (xmtr->FreeDescList.next, EP_TXD, Link);
++
++    list_del (&txd->Link);
++
++    /* Wakeup the descriptor primer thread if there's not many left */
++    low_on_txds = (--xmtr->FreeDescCount < ep_txd_lowat);
++
++    spin_unlock_irqrestore (&xmtr->FreeDescLock, flags);
++
++    if (low_on_txds)
++      ep_kthread_schedule (&subsys->Thread, lbolt);
++
++    return (txd);
++}
++
++void
++FreeTxd (EP_XMTR *xmtr, EP_TXD *txd)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&xmtr->FreeDescLock, flags);
++    
++    list_add (&txd->Link, &xmtr->FreeDescList);
++
++    xmtr->FreeDescCount++;
++
++    if (xmtr->FreeDescWanted)                                 /* someone waiting for a receive */
++    {                                                         /* descriptor, so wake them up */
++      xmtr->FreeDescWanted--;
++      kcondvar_wakeupone (&xmtr->FreeDescSleep, &xmtr->FreeDescLock);
++    }
++    
++    spin_unlock_irqrestore (&xmtr->FreeDescLock, flags);
++}
++
++int
++TxdShouldStabalise (EP_TXD_RAIL *txdRail, EP_RAIL *rail)
++{
++    EP_TXD      *txd  = txdRail->Txd;
++    EP_XMTR     *xmtr = txd->Xmtr;
++    EP_ATTRIBUTE attr = txd->Envelope.Attr;
++    int                stabilise;
++    extern int   txd_stabilise;
++
++    switch (EP_ATTR2TYPE (attr)) 
++    {
++    case EP_TYPE_SVC_INDICATOR:                               /* is the rail in the current service indicator rail mask */
++      if ((txd_stabilise & 4) == 0)
++          return 0;
++
++      stabilise = (ep_xmtr_svc_indicator_railmask (xmtr, EP_ATTR2DATA (attr), txd->NodeId) & EP_RAIL2RAILMASK (rail->Number)) == 0;
++      break;
++
++    case EP_TYPE_TIMEOUT:
++      if ((txd_stabilise & 2) == 0)
++          return 0;
++
++      stabilise = AFTER(lbolt, txdRail->Txd->TimeStamp + EP_ATTR2DATA(attr));
++      break;
++
++    default:
++      if ((txd_stabilise & 1) == 0)
++          return 0;
++
++      stabilise = AFTER(lbolt, txdRail->Txd->TimeStamp + EP_DEFAULT_TIMEOUT);
++      break;
++    }
++
++    if (stabilise)
++    {
++      txd->Envelope.Attr = EP_SET_TXD_STABALISING(txd->Envelope.Attr);
++      txd->RetryTime     = lbolt;
++
++      ep_kthread_schedule (&xmtr->Subsys->Thread, lbolt);    
++    }
++
++    return stabilise;
++}
++
++void ep_xmtr_txd_stat(EP_XMTR *xmtr, EP_TXD *txd) 
++{
++    int f;
++    unsigned long size;
++    EP_TXD_RAIL *txdRail = txd->TxdRail;
++
++    size = 0;
++    for (f=0; f < txd->Envelope.nFrags; f++)
++      size += txd->Envelope.Frags[f].nmd_len;
++
++    INC_STAT(xmtr->stats,tx);
++    ADD_STAT(xmtr->stats,tx_len, size);  
++    
++    if ((txdRail != NULL) && (txdRail->XmtrRail != NULL)){
++      INC_STAT(txdRail->XmtrRail->stats,tx);
++      ADD_STAT(txdRail->XmtrRail->stats,tx_len, size); 
++      
++      if ((txdRail->XmtrRail->CommsRail != NULL) && ( txdRail->XmtrRail->CommsRail->Rail != NULL)) {
++          INC_STAT(txdRail->XmtrRail->CommsRail->Rail->Stats,tx);
++          ADD_STAT(txdRail->XmtrRail->CommsRail->Rail->Stats,tx_len, size);
++      }
++    }
++}
++
++static int
++PollActiveTransmitList (EP_XMTR *xmtr, int flag)
++{
++    struct list_head *el, *nel;
++    struct list_head list;
++    unsigned long flags;
++    int count;
++
++    INIT_LIST_HEAD (&list);
++
++    spin_lock_irqsave (&xmtr->Lock, flags);
++    list_for_each_safe (el, nel, &xmtr->ActiveDescList) {
++      EP_TXD      *txd     = list_entry (el, EP_TXD, Link);
++      EP_TXD_RAIL *txdRail = txd->TxdRail;
++      
++      if (txdRail == NULL)
++          continue;
++
++      ASSERT (txdRail->Txd == txd);
++      
++      if (EP_XMTR_OP (txdRail->XmtrRail,PollTxd) (txdRail->XmtrRail, txdRail, flags))
++      {
++          list_del (&txd->Link);                              /* remove from active transmit list */
++          list_add_tail (&txd->Link, &list);                  /* and add to list to call handlers */
++      }
++    }
++    
++    spin_unlock_irqrestore (&xmtr->Lock, flags);
++
++    for (count = 0; !list_empty (&list); count++)
++    {
++      EP_TXD *txd = list_entry (list.next, EP_TXD, Link);
++
++      list_del (&txd->Link);
++
++      txd->Handler (txd, txd->Arg, EP_SUCCESS);
++
++      FreeTxd (xmtr, txd);
++    }
++    return (count);
++}
++
++static inline void
++DoTransmit (EP_XMTR *xmtr, EP_TXD *txd)
++{
++    EP_RAILMASK   nmdRailMask = ep_nmd2railmask (txd->Envelope.Frags, txd->Envelope.nFrags);
++    EP_XMTR_RAIL *xmtrRail;
++    unsigned long flags;
++    int rnum;
++
++    spin_lock_irqsave (&xmtr->Lock, flags);
++
++    if (EP_IS_SVC_INDICATOR(txd->Envelope.Attr))
++      nmdRailMask = nmdRailMask & ep_xmtr_svc_indicator_railmask(xmtr, EP_ATTR2DATA(txd->Envelope.Attr), txd->NodeId);
++
++    if (EP_IS_PREFRAIL_SET(txd->Envelope.Attr))
++      rnum = EP_ATTR2PREFRAIL(txd->Envelope.Attr);
++    else 
++      rnum = ep_xmtr_prefrail (xmtr, nmdRailMask, txd->NodeId);
++    
++    if (rnum < 0 || !(nmdRailMask & EP_RAIL2RAILMASK(rnum)))
++      xmtrRail = NULL;
++    else
++      xmtrRail = xmtr->Rails[rnum];
++    
++    /* Allocate the XID while holding the xmtr->Lock from our XID cache */
++    txd->Envelope.Xid = ep_xid_cache_alloc (xmtr->Subsys->Subsys.Sys, &xmtr->XidCache);
++    
++    EPRINTF7 (DBG_XMTR, "ep: transmit txd %p to %d/%d: Xid %llx nFrags %d [%08x.%d]\n",
++            txd, txd->NodeId, txd->Service, (long long) txd->Envelope.Xid.Unique, 
++            txd->Envelope.nFrags, txd->Envelope.Frags[0].nmd_addr, txd->Envelope.Frags[0].nmd_len);
++
++    /* Store time transmit started to timeout if not received */
++    txd->TimeStamp = lbolt;
++    
++    /* Initialise the retry backoff */
++    txd->Backoff.type = EP_BACKOFF_FREE;
++
++    list_add_tail (&txd->Link, &xmtr->ActiveDescList);
++
++    if (xmtrRail == NULL || !EP_XMTR_OP(xmtrRail,BindTxd) (txd, xmtrRail, EP_TXD_PHASE_ACTIVE))
++      ep_kthread_schedule (&xmtr->Subsys->Thread, lbolt);
++    
++    spin_unlock_irqrestore (&xmtr->Lock, flags);
++
++    if (EP_IS_NO_INTERRUPT(txd->Envelope.Attr))
++      PollActiveTransmitList (xmtr, POLL_TX_LIST);
++}
++
++EP_STATUS
++ep_transmit_message (EP_XMTR *xmtr, unsigned int dest, EP_SERVICE service, EP_ATTRIBUTE attr, 
++                   EP_TXH *handler, void *arg, EP_PAYLOAD *payload, EP_NMD *nmd, int nFrags)
++{
++    EP_TXD       *txd;
++    int           i, len;
++
++    if (nFrags > EP_MAXFRAG || service > EP_MSG_NSVC)
++      return (EP_EINVAL);
++
++    if ((txd = GetTxd (xmtr, attr)) == NULL)
++      return (EP_ENOMEM);
++
++    txd->Handler = handler;
++    txd->Arg     = arg;
++    txd->Service = service;
++    txd->NodeId  = (unsigned short) dest;
++
++    /* Initialise the envelope */
++    txd->Envelope.Version   = EP_ENVELOPE_VERSION;
++    txd->Envelope.Attr      = EP_CLEAR_LOCAL_ATTR(attr);
++    txd->Envelope.Range     = EP_RANGE (dest, dest);
++    txd->Envelope.TxdMain   = txd->NmdMain;
++    txd->Envelope.nFrags    = nFrags;
++
++    for (i = len = 0; i < nFrags; len += nmd[i].nmd_len, i++)
++      txd->Envelope.Frags[i] = nmd[i];
++
++    if (payload)
++    {
++      txd->Envelope.Attr = EP_SET_HAS_PAYLOAD(txd->Envelope.Attr);
++
++      bcopy (payload, &txd->Payload, sizeof (EP_PAYLOAD));
++    }
++
++    DoTransmit (xmtr, txd);
++
++    BucketStat (xmtr->Subsys, DataXmit, len);
++
++    return (EP_SUCCESS);
++}
++
++EP_STATUS
++ep_multicast_message (EP_XMTR *xmtr, unsigned int destLo, unsigned int destHi, bitmap_t *bitmap, EP_SERVICE service, 
++                   EP_ATTRIBUTE attr, EP_TXH *handler, void *arg, EP_PAYLOAD *payload, EP_NMD *nmd, int nFrags)
++{
++    EP_SYS       *sys = xmtr->Subsys->Subsys.Sys;
++    EP_TXD       *txd;
++    int           nnodes;
++    int           i, len;
++    unsigned long flags;    
++
++    if (nFrags > EP_MAXFRAG || service > EP_MSG_NSVC)
++      return (EP_EINVAL);
++
++    if (destLo == -1) 
++      destLo = sys->Position.pos_nodeid & ~(EP_MAX_NODES-1);
++
++    if (destHi == -1 && (destHi = ((sys->Position.pos_nodeid + EP_MAX_NODES) & ~(EP_MAX_NODES-1)) - 1) >= sys->Position.pos_nodes)
++      destHi = sys->Position.pos_nodes-1;
++
++    nnodes = (destHi-destLo+1);
++
++    if ((txd = GetTxd (xmtr, attr)) == NULL)
++      return (EP_ENOMEM);
++
++    txd->Handler = handler;
++    txd->Arg     = arg;
++    txd->Service = service;
++
++    /* Initialise the envelope */
++    txd->Envelope.Version   = EP_ENVELOPE_VERSION;
++    txd->Envelope.Attr      = EP_SET_MULTICAST(EP_CLEAR_LOCAL_ATTR(attr));
++    txd->Envelope.Range     = EP_RANGE (destLo, destHi);
++    txd->Envelope.TxdMain   = txd->NmdMain;
++    txd->Envelope.nFrags    = nFrags;
++
++    for (i = len = 0; i < nFrags; len += nmd[i].nmd_len, i++)
++      txd->Envelope.Frags[i] = nmd[i];
++
++    if (payload)
++    {
++      txd->Envelope.Attr = EP_SET_HAS_PAYLOAD(txd->Envelope.Attr);
++
++      bcopy (payload, &txd->Payload, sizeof (EP_PAYLOAD));
++    }
++
++    spin_lock_irqsave (&sys->NodeLock, flags);
++    if (EP_IS_SVC_INDICATOR(attr)) 
++      ep_xmtr_svc_indicator_bitmap(xmtr, EP_ATTR2DATA(attr), txd->TxdMain->Bitmap, destLo, nnodes);
++    else
++      bt_subset (statemap_tobitmap(sys->NodeSet), txd->TxdMain->Bitmap, destLo, nnodes);
++    spin_unlock_irqrestore (&sys->NodeLock, flags);
++
++    if (bitmap != NULL)                                                                       /* bitmap supplied, so intersect it with */
++      bt_intersect (txd->TxdMain->Bitmap, bitmap, nnodes);                            /* the current node set map */
++    
++    if ((attr & EP_NOT_MYSELF) && destLo <= sys->Position.pos_nodeid && sys->Position.pos_nodeid <= destHi)
++      BT_CLEAR (txd->TxdMain->Bitmap, (sys->Position.pos_nodeid-destLo));                     /* clear myself if not wanted */
++
++    if ((i = bt_lowbit (txd->TxdMain->Bitmap, nnodes)) < 0)
++    {
++      FreeTxd (xmtr, txd);
++      return (EP_NODE_DOWN);
++    }
++
++    txd->NodeId = (unsigned short) i;
++
++    DoTransmit (xmtr, txd);
++
++    BucketStat (xmtr->Subsys, McastXmit, len);
++
++    return (EP_SUCCESS);
++}
++
++EP_STATUS
++ep_transmit_rpc (EP_XMTR *xmtr, unsigned int dest, EP_SERVICE service, EP_ATTRIBUTE attr,
++               EP_TXH *handler, void *arg, EP_PAYLOAD *payload, EP_NMD *nmd, int nFrags)
++{
++    EP_TXD       *txd;
++    int           i, len;
++
++    if (nFrags > EP_MAXFRAG || service > EP_MSG_NSVC)
++      return (EP_EINVAL);
++
++    if ((txd = GetTxd (xmtr, attr)) == NULL)
++      return (EP_ENOMEM);
++
++    txd->Handler = handler;
++    txd->Arg     = arg;
++    txd->Service = service;
++    txd->NodeId  = dest;
++
++    /* Initialise the envelope */
++    txd->Envelope.Version   = EP_ENVELOPE_VERSION;
++    txd->Envelope.Attr      = EP_SET_RPC(EP_CLEAR_LOCAL_ATTR(attr));    
++    txd->Envelope.Range     = EP_RANGE (dest, dest);
++    txd->Envelope.TxdMain   = txd->NmdMain;
++    txd->Envelope.nFrags    = nFrags;
++     
++    for (i = len = 0; i < nFrags; len += nmd[i].nmd_len, i++)
++      txd->Envelope.Frags[i] = nmd[i];
++
++    if (payload)
++    {
++      txd->Envelope.Attr = EP_SET_HAS_PAYLOAD(txd->Envelope.Attr);
++
++      bcopy (payload, &txd->Payload, sizeof (EP_PAYLOAD));
++    }
++
++    DoTransmit (xmtr, txd);
++
++    BucketStat (xmtr->Subsys, RPCXmit, len);
++
++    return (EP_SUCCESS);
++}
++
++EP_STATUS
++ep_multicast_forward (EP_XMTR *xmtr, unsigned int dest, EP_SERVICE service, EP_ATTRIBUTE attr, EP_TXH *handler, void *arg,
++                    EP_ENVELOPE *env,  EP_PAYLOAD *payload, bitmap_t *bitmap, EP_NMD *nmd, int nFrags)
++{
++    EP_TXD       *txd;
++    int           i, len;
++
++    if (nFrags > EP_MAXFRAG || service > EP_MSG_NSVC)
++      return (EP_EINVAL);
++
++    if ((txd = GetTxd (xmtr, attr)) == NULL)
++      return (EP_ENOMEM);
++
++    txd->Handler = handler;
++    txd->Arg     = arg;
++    txd->Service = service;
++    txd->NodeId  = (unsigned short) dest;
++
++    /* Initialise the envelope */
++    txd->Envelope.Version   = EP_ENVELOPE_VERSION;
++    txd->Envelope.Attr      = EP_SET_MULTICAST(EP_CLEAR_LOCAL_ATTR(attr));
++    txd->Envelope.Range     = env->Range;
++    txd->Envelope.TxdMain   = txd->NmdMain;
++    txd->Envelope.nFrags    = nFrags;
++
++    for (i = len = 0; i < nFrags; len += nmd[i].nmd_len, i++)
++      txd->Envelope.Frags[i] = nmd[i];
++
++    bt_copy (bitmap, txd->TxdMain->Bitmap, EP_RANGE_HIGH(env->Range) - EP_RANGE_LOW(env->Range) + 1);
++
++    if (payload)
++    {
++      txd->Envelope.Attr = EP_SET_HAS_PAYLOAD(txd->Envelope.Attr);
++
++      bcopy (payload, &txd->Payload, sizeof (EP_PAYLOAD));
++    }
++
++    DoTransmit (xmtr, txd);
++
++    BucketStat (xmtr->Subsys, McastXmit, len);
++
++    return (EP_SUCCESS);
++}
++
++int
++ep_poll_transmits (EP_XMTR *xmtr)
++{
++    return (PollActiveTransmitList (xmtr, POLL_TX_LIST));
++}
++
++int
++ep_enable_txcallbacks (EP_XMTR *xmtr)
++{
++    return (PollActiveTransmitList (xmtr, ENABLE_TX_CALLBACK));
++}
++
++int
++ep_disable_txcallbacks (EP_XMTR *xmtr)
++{
++    return (PollActiveTransmitList (xmtr, DISABLE_TX_CALLBACK));
++}
++
++/* functions for accessing fields of txds */
++int              ep_txd_node(EP_TXD *txd)             { return (txd->NodeId); }
++EP_STATUSBLK    *ep_txd_statusblk(EP_TXD *txd)                { return (&txd->TxdMain->StatusBlk); }
++
++void
++ep_xmtr_xid_msg_handler (void *arg, EP_MANAGER_MSG *msg)
++{
++    EP_XMTR          *xmtr = (EP_XMTR *) arg;
++    EP_SYS           *sys  = xmtr->Subsys->Subsys.Sys;
++    struct list_head *el,*nel;
++    unsigned long     flags;
++
++    switch (msg->Hdr.Type)
++    {
++    case EP_MANAGER_MSG_TYPE_FAILOVER_REQUEST:
++      spin_lock_irqsave (&xmtr->Lock, flags);
++      list_for_each (el, &xmtr->ActiveDescList) {
++          EP_TXD      *txd     = list_entry (el, EP_TXD, Link);
++          EP_TXD_RAIL *txdRail = txd->TxdRail;
++
++          if (txdRail != NULL && EP_XIDS_MATCH (msg->Body.Failover.Xid, txd->Envelope.Xid))
++          {
++              EP_XMTR_RAIL       *xmtrRail = txdRail->XmtrRail;
++              EP_RAIL            *rail     = xmtrRail->CommsRail->Rail;
++              EP_MANAGER_MSG_BODY msgBody;
++              int                 rnum;
++
++              if (! (msg->Body.Failover.Railmask & EP_RAIL2RAILMASK (rail->Number)))
++              {
++                  /* Need to failover this txd to a different rail, select a rail from
++                   * the set that she has asked us to use and which is connected to her
++                   * on this transmitter.   If there are no such rails, then in all probability
++                   * we're offline on all common rails and eventually she will see we have no
++                   * rails in common and abort the receive. */
++                  if ((rnum = ep_xmtr_prefrail (xmtr, msg->Body.Failover.Railmask, txd->NodeId)) < 0)
++                      ep_debugf (DBG_XMTR, "%s: ep_xmtr_xid_msg_handler: FAILOVER_REQUEST but can't determine rail (%04x,%04x,%d,%04x)\n",
++                                 rail->Name, msg->Body.Failover.Railmask, xmtr->RailMask, txd->NodeId, sys->Nodes[txd->NodeId].ConnectedRails);
++                  else
++                  {
++                      EP_XMTR_RAIL *nXmtrRail = xmtr->Rails[rnum];
++
++                      EPRINTF4 (DBG_XMTR, "%s: ep_xmtr_xid_msg_handler: FAILOVER_REQUEST txd=%p XID=%llx-> rail %d\n", rail->Name, txd, (long long) txd->Envelope.Xid.Unique, rnum);
++
++                      /* Bind the txd rail onto the new rail - it doesn't matter if we fail
++                       * as it will remain bound to the original rail */
++                      (void) EP_XMTR_OP (nXmtrRail, BindTxd) (txd, nXmtrRail, EP_TXD_PHASE_PASSIVE);
++                  }
++              }
++
++              /* Send a failover response including an envelope update */
++              msgBody.FailoverTxd.Rail     = rail->Number;
++              msgBody.FailoverTxd.Xid      = txd->Envelope.Xid;
++              msgBody.FailoverTxd.TxdRail  = txd->Envelope.TxdRail;
++
++              ep_send_message (rail, msg->Hdr.NodeId, EP_MANAGER_MSG_TYPE_FAILOVER_RESPONSE, msg->Hdr.Xid, &msgBody);
++          }
++      }
++      spin_unlock_irqrestore (&xmtr->Lock, flags);
++      break;
++
++    case EP_MANAGER_MSG_TYPE_GET_NODE_STATE_RESPONSE: {
++      int         txd_has_not_sent_envelope = 0;
++      EP_TXD      *txd            = NULL;
++      EP_TXD_RAIL *txdRail        = NULL;
++
++      if (msg->Body.NodeState.NetworkErrorState != 0)
++          ep_kthread_schedule (&xmtr->Subsys->Thread, lbolt + MESSAGE_RETRY_TIME);
++      else
++      {
++          spin_lock_irqsave (&xmtr->Lock, flags);
++          list_for_each_safe (el, nel, &xmtr->ActiveDescList) {
++              
++              txd     = list_entry (el, EP_TXD, Link);
++              txdRail = txd->TxdRail;
++              
++              if (txdRail != NULL && EP_XIDS_MATCH (msg->Hdr.Xid, txd->Envelope.Xid)) {
++                  txd_has_not_sent_envelope = EP_XMTR_OP(txdRail->XmtrRail,CheckTxdState)(txd);
++                  break;
++              }
++          }
++          
++          if (txd_has_not_sent_envelope) {
++              EPRINTF2 (DBG_STABILISE, "ep_xmtr_xid_msg_handler: GET_NODE_STATE_RESPONSE txd=%p XID=%llx not sent envelope\n",
++                        txd, (long long) txd->Envelope.Xid.Unique);
++
++              /* at this point it has finished stabalising */
++              txd->Envelope.Attr = EP_CLEAR_TXD_STABALISING(txd->Envelope.Attr);
++
++              /* store railmask into txd if not a service indicator or timeout */
++              if (EP_IS_NO_TYPE(txd->Envelope.Attr))
++                  txd->Envelope.Attr = EP_SET_DATA(txd->Envelope.Attr, EP_TYPE_RAILMASK, msg->Body.NodeState.Railmask);
++
++              spin_unlock_irqrestore (&xmtr->Lock, flags);
++              
++              /* TXD is now no longer bound to a rail , so let ep_check_xmtr() handle it */
++              ep_kthread_schedule (&xmtr->Subsys->Thread, lbolt);
++          }
++          else
++              spin_unlock_irqrestore (&xmtr->Lock, flags);    
++      }
++      break;
++    }
++    default:
++      panic ("ep_xmtr_xid_msg_handler: XID match but invalid message type\n");
++    }
++}
++
++EP_XMTR *
++ep_alloc_xmtr (EP_SYS *sys)
++{
++    EP_COMMS_SUBSYS   *subsys;
++    EP_XMTR          *xmtr;
++    struct list_head *el;
++
++    if ((subsys = (EP_COMMS_SUBSYS *) ep_subsys_find (sys, EPCOMMS_SUBSYS_NAME)) == NULL)
++      return (NULL);
++
++    KMEM_ZALLOC (xmtr, EP_XMTR *, sizeof (EP_XMTR), 1);
++
++    if (xmtr == NULL)
++      return (NULL);
++    
++    xmtr->Subsys = subsys;
++
++    spin_lock_init (&xmtr->Lock);
++    INIT_LIST_HEAD (&xmtr->ActiveDescList);
++    
++    kcondvar_init (&xmtr->FreeDescSleep);
++    spin_lock_init (&xmtr->FreeDescLock);
++    INIT_LIST_HEAD (&xmtr->FreeDescList);
++    INIT_LIST_HEAD (&xmtr->DescBlockList);
++
++    ep_xid_cache_init (sys, &xmtr->XidCache);
++
++    xmtr->XidCache.MessageHandler = ep_xmtr_xid_msg_handler;
++    xmtr->XidCache.Arg            = xmtr;
++
++    kmutex_lock (&subsys->Lock);
++    list_add_tail (&xmtr->Link, &subsys->Transmitters);
++
++    ep_procfs_xmtr_add(xmtr);
++
++    /* Now add all rails which are already started */
++    list_for_each (el, &subsys->Rails) { 
++      EP_COMMS_RAIL *commsRail = list_entry (el, EP_COMMS_RAIL, Link);
++
++      EP_RAIL_OP(commsRail, Xmtr.AddRail) (xmtr, commsRail);
++    }
++    kmutex_unlock (&subsys->Lock);
++
++    ep_mod_inc_usecount();
++
++    return (xmtr);
++}
++
++void
++ep_free_xmtr (EP_XMTR *xmtr)
++{
++    EP_COMMS_SUBSYS  *subsys = xmtr->Subsys;
++    EP_SYS           *sys    = subsys->Subsys.Sys;
++    struct list_head *el;
++    
++    kmutex_lock (&subsys->Lock);
++    list_for_each (el, &subsys->Rails) { 
++      EP_COMMS_RAIL *rail = list_entry (el, EP_COMMS_RAIL, Link);
++
++      EP_RAIL_OP(rail,Xmtr.DelRail) (xmtr, rail);
++    }
++
++    list_del (&xmtr->Link);
++    kmutex_unlock (&subsys->Lock);
++
++    /* all the desc's must be free */
++    ASSERT(xmtr->FreeDescCount == xmtr->TotalDescCount);
++
++    /* delete the descs */
++    while (!list_empty (&xmtr->DescBlockList))
++      FreeTxdBlock( xmtr, list_entry(xmtr->DescBlockList.next, EP_TXD_BLOCK , Link));
++
++    /* they had better all be gone now */
++    ASSERT((xmtr->FreeDescCount == 0) && (xmtr->TotalDescCount == 0));
++
++    ep_procfs_xmtr_del(xmtr);
++
++    ep_xid_cache_destroy (sys, &xmtr->XidCache);
++
++    spin_lock_destroy (&xmtr->Lock);
++    KMEM_FREE (xmtr, sizeof (EP_XMTR));
++
++    ep_mod_dec_usecount();
++}
++
++long
++ep_check_xmtr (EP_XMTR *xmtr, long nextRunTime)
++{
++    EP_COMMS_SUBSYS  *subsys = xmtr->Subsys;
++    EP_SYS           *sys    = subsys->Subsys.Sys;
++    struct list_head *el, *nel;
++    struct list_head  txdList;
++    unsigned long       flags;
++    int                 timed_out=0;
++    int                 i;
++    EP_MANAGER_MSG_BODY body;
++
++    INIT_LIST_HEAD (&txdList);
++
++    /* See if we have any txd's which need to be bound to a rail */
++    spin_lock_irqsave (&xmtr->Lock, flags);
++    list_for_each_safe (el, nel, &xmtr->ActiveDescList) {
++      EP_TXD      *txd      = list_entry (el, EP_TXD, Link);
++      EP_NODE     *node     = &sys->Nodes[txd->NodeId];
++      EP_RAILMASK nodeRails = node->ConnectedRails & xmtr->RailMask;
++      EP_ENVELOPE *env      = &txd->Envelope;
++
++      if (EP_IS_TXD_STABALISING(txd->Envelope.Attr)) 
++      {
++          ASSERT(txd->TxdRail != NULL);
++
++          if (AFTER (lbolt, txd->RetryTime))
++          {
++              EPRINTF6 (DBG_STABILISE, "ep_check_xmtr txd=%p txdRail=%p send get node state to %d Xid=%08x.%08x.%016llx\n",
++                        txd, txd->TxdRail, txd->NodeId, env->Xid.Generation, env->Xid.Handle, env->Xid.Unique);
++              
++              body.Service = txd->Service;
++              if (ep_send_message ( txd->TxdRail->XmtrRail->CommsRail->Rail, txd->NodeId, EP_MANAGER_MSG_TYPE_GET_NODE_STATE, env->Xid, &body) == 0)
++                  txd->RetryTime = lbolt + (MESSAGE_RETRY_TIME << ep_backoff (&txd->Backoff, EP_BACKOFF_STABILISE));
++              else
++                  txd->RetryTime = lbolt + MSGBUSY_RETRY_TIME;
++          }
++
++          ep_kthread_schedule (&subsys->Thread, txd->RetryTime);
++          continue;
++      }
++
++      if (txd->TxdRail != NULL)
++          continue;
++
++      switch (EP_ATTR2TYPE(txd->Envelope.Attr)) 
++      {
++      case EP_TYPE_SVC_INDICATOR: 
++      {
++          EP_RAILMASK       rmask=0;
++          struct list_head *tmp;
++
++          list_for_each (tmp, &subsys->Rails) { 
++              EP_COMMS_RAIL *commsRail = list_entry (tmp, EP_COMMS_RAIL, Link);
++              if ( cm_svc_indicator_is_set(commsRail->Rail, EP_ATTR2DATA(txd->Envelope.Attr), txd->NodeId))
++                  rmask |= EP_RAIL2RAILMASK(commsRail->Rail->Number);
++          } 
++          nodeRails &= rmask;
++          break;
++      }
++      case EP_TYPE_TIMEOUT:
++          timed_out = AFTER(lbolt, txd->TimeStamp + EP_ATTR2DATA(txd->Envelope.Attr)) ? (1) : (0);
++          break;
++      case EP_TYPE_RAILMASK:
++          nodeRails &= EP_ATTR2DATA(txd->Envelope.Attr);
++          break;
++      default:
++          timed_out = AFTER(lbolt, txd->TimeStamp +  EP_DEFAULT_TIMEOUT) ? (1) : (0);
++          break;
++      }
++
++      if (nodeRails == 0 || timed_out || (EP_IS_NO_FAILOVER(env->Attr) && EP_IS_PREFRAIL_SET(env->Attr) && 
++                                          (nodeRails & EP_RAIL2RAILMASK(EP_ATTR2PREFRAIL(env->Attr))) == 0))
++      {
++          EPRINTF5 (timed_out ? DBG_STABILISE : DBG_XMTR, "ep_check_xmtr: txd=%p XID=%llx to %d no rails connected or cannot failover (nodeRails=0x%x,timed_out=%d\n", 
++                    txd, (long long) env->Xid.Unique, txd->NodeId, nodeRails, timed_out);
++
++          list_del  (&txd->Link);
++          list_add_tail (&txd->Link, &txdList);
++      }
++      else
++      {
++          EP_XMTR_RAIL *xmtrRail;
++          int i, len, rnum;
++
++          if (EP_IS_PREFRAIL_SET(env->Attr) && (nodeRails & EP_RAIL2RAILMASK(EP_ATTR2PREFRAIL(env->Attr))))
++              rnum = EP_ATTR2PREFRAIL(env->Attr);
++          else
++              rnum = ep_pickRail (nodeRails);
++
++          EPRINTF3 (DBG_XMTR, "ep_check_xmtr: txd=%p XID=%llx mapping NMDs onto rail %d \n", txd, (long long) env->Xid.Unique, rnum);
++
++          for (i = len = 0; i < env->nFrags; i++, len += env->Frags[i].nmd_len)
++              ep_nmd_map_rails (sys, &env->Frags[i], nodeRails);
++
++          if ((xmtrRail = xmtr->Rails[rnum]) == NULL || 
++              !EP_XMTR_OP(xmtrRail,BindTxd) (txd, xmtrRail, EP_TXD_PHASE_ACTIVE))
++              ep_kthread_schedule (&subsys->Thread, lbolt + RESOURCE_RETRY_TIME);
++      }
++    }
++    spin_unlock_irqrestore (&xmtr->Lock, flags);
++
++    while (! list_empty (&txdList))
++    {
++      EP_TXD *txd = list_entry (txdList.next, EP_TXD, Link);
++      list_del (&txd->Link);
++
++      txd->Handler (txd, txd->Arg, EP_NODE_DOWN);
++      FreeTxd (xmtr, txd);
++    }
++
++    /* Check to see if we're low on txds */
++    if (xmtr->FreeDescCount < ep_txd_lowat)
++      AllocateTxdBlock (xmtr, 0, NULL);
++    
++    /* Then check each rail */
++    for (i = 0; i < EP_MAX_RAILS; i++) 
++      if (xmtr->RailMask & (1 << i) ) 
++          nextRunTime = EP_XMTR_OP (xmtr->Rails[i],Check) (xmtr->Rails[i], nextRunTime);
++    return (nextRunTime);
++}
++
++void
++ep_display_txd (DisplayInfo *di, EP_TXD *txd)
++{
++    EP_ENVELOPE *env     = &txd->Envelope;
++    EP_TXD_RAIL *txdRail = txd->TxdRail;
++
++    (di->func)(di->arg, "TXD: %p Version=%x Attr=%x Xid=%08x.%08x.%016llx\n", txd, 
++             env->Version, env->Attr, env->Xid.Generation, env->Xid.Handle, (long long) env->Xid.Unique);
++    (di->func)(di->arg,  "     NodeId=%d Range=%d.%d TxdRail=%x TxdMain=%x.%x.%x nFrags=%d\n",
++             env->NodeId, EP_RANGE_LOW(env->Range), EP_RANGE_HIGH(env->Range), env->TxdRail,
++             env->TxdMain.nmd_addr, env->TxdMain.nmd_len, env->TxdMain.nmd_attr, env->nFrags);
++    (di->func)(di->arg,  "       Frag[0] %08x.%08x.%08x\n", env->Frags[0].nmd_addr, env->Frags[0].nmd_len, env->Frags[0].nmd_attr);
++    (di->func)(di->arg,  "       Frag[1] %08x.%08x.%08x\n", env->Frags[1].nmd_addr, env->Frags[1].nmd_len, env->Frags[1].nmd_attr);
++    (di->func)(di->arg,  "       Frag[2] %08x.%08x.%08x\n", env->Frags[2].nmd_addr, env->Frags[2].nmd_len, env->Frags[2].nmd_attr);
++    (di->func)(di->arg,  "       Frag[3] %08x.%08x.%08x\n", env->Frags[3].nmd_addr, env->Frags[3].nmd_len, env->Frags[3].nmd_attr);
++
++    if (txdRail != NULL) EP_XMTR_OP (txdRail->XmtrRail, DisplayTxd) (di, txdRail);
++}
++
++void
++ep_display_xmtr (DisplayInfo *di, EP_XMTR *xmtr)
++{
++    int               freeCount   = 0;
++    int               activeCount = 0;
++    struct list_head *el;
++    int               i;
++    unsigned long     flags;
++
++    spin_lock_irqsave (&xmtr->FreeDescLock, flags);
++    list_for_each (el, &xmtr->FreeDescList)
++      freeCount++;
++    spin_unlock_irqrestore (&xmtr->FreeDescLock, flags);
++
++    spin_lock_irqsave (&xmtr->Lock, flags);
++    list_for_each (el, &xmtr->ActiveDescList)
++      activeCount++;
++    
++    (di->func)(di->arg, "ep_display_xmtr: xmtr=%p Free=%d Active=%d\n", xmtr, freeCount, activeCount);
++    for (i = 0; i < EP_MAX_RAILS; i++)
++      if (xmtr->Rails[i]) EP_XMTR_OP (xmtr->Rails[i], DisplayXmtr) (di, xmtr->Rails[i]);
++
++    list_for_each (el,&xmtr->ActiveDescList)
++      ep_display_txd (di, list_entry (el, EP_TXD, Link));
++    spin_unlock_irqrestore (&xmtr->Lock, flags);
++}
++
++void
++ep_xmtr_fillout_stats(EP_XMTR *xmtr, char *str)
++{
++    sprintf(str+strlen(str),"Tx     %lu  %lu /sec\n",       GET_STAT_TOTAL(xmtr->stats,tx),      GET_STAT_PER_SEC(xmtr->stats,tx) );
++    sprintf(str+strlen(str),"MBytes %lu  %lu Mbytes/sec\n", GET_STAT_TOTAL(xmtr->stats,tx_len) / (1024*1024),  GET_STAT_PER_SEC(xmtr->stats,tx_len) / (1024*1024));
++}
++
++void
++ep_xmtr_rail_fillout_stats(EP_XMTR_RAIL *xmtr_rail, char *str)
++{
++    sprintf(str+strlen(str),"Tx     %lu  %lu /sec\n",       GET_STAT_TOTAL(xmtr_rail->stats,tx),      GET_STAT_PER_SEC(xmtr_rail->stats,tx) );
++    sprintf(str+strlen(str),"MBytes %lu  %lu Mbytes/sec\n", GET_STAT_TOTAL(xmtr_rail->stats,tx_len) / (1024*1024),  GET_STAT_PER_SEC(xmtr_rail->stats,tx_len) / (1024*1024));
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/ep/epcommsTx_elan3.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/epcommsTx_elan3.c   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/epcommsTx_elan3.c        2005-06-01 23:12:54.657430288 -0400
+@@ -0,0 +1,1173 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: epcommsTx_elan3.c,v 1.17.2.2 2004/11/12 10:54:51 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/epcommsTx_elan3.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++#include <elan/epsvc.h>
++#include <elan/epcomms.h>
++
++#include "kcomm_vp.h"
++#include "kcomm_elan3.h"
++#include "epcomms_elan3.h"
++#include "debug.h"
++
++#define XMTR_TO_RAIL(xmtrRail)                ((EP3_RAIL *) ((EP_XMTR_RAIL *) xmtrRail)->CommsRail->Rail)
++#define XMTR_TO_DEV(xmtrRail)         (XMTR_TO_RAIL(xmtrRail)->Device)
++#define XMTR_TO_SUBSYS(xmtrRail)      (((EP_XMTR_RAIL *) xmtrRail)->Xmtr->Subsys)
++
++static void TxEnveEvent (EP3_RAIL *rail, void *arg);
++static void TxEnveRetry (EP3_RAIL *rail, void *arg, E3_DMA_BE *dma, int status);
++static void TxEnveVerify (EP3_RAIL *rail, void *arg, E3_DMA_BE *dma);
++
++static EP3_COOKIE_OPS EnveCookieOps =
++{
++    TxEnveEvent,
++    TxEnveRetry,
++    NULL, /* DmaCancelled */
++    TxEnveVerify
++};
++
++static void TxDataEvent (EP3_RAIL *rail, void *arg);
++static void TxDataRetry (EP3_RAIL *rail, void *arg, E3_DMA_BE *dma, int status);
++static void TxDataVerify (EP3_RAIL *rail, void *arg, E3_DMA_BE *dma);
++
++static EP3_COOKIE_OPS DataCookieOps =
++{
++    TxDataEvent,
++    TxDataRetry,
++    NULL, /* DmaCancelled */
++    TxDataVerify
++};
++
++static void TxDoneEvent (EP3_RAIL *dev, void *arg);
++static void TxDoneRetry (EP3_RAIL *dev, void *arg, E3_DMA_BE *dma, int status);
++static void TxDoneVerify (EP3_RAIL  *dev, void *arg, E3_DMA_BE *dma);
++
++static EP3_COOKIE_OPS DoneCookieOps = 
++{
++    TxDoneEvent,
++    TxDoneRetry,
++    NULL, /* DmaCancelled */
++    TxDoneVerify,
++} ;
++
++static int
++AllocateTxdRailBlock (EP3_XMTR_RAIL *xmtrRail)
++{
++    EP3_RAIL          *rail = XMTR_TO_RAIL (xmtrRail);
++    ELAN3_DEV         *dev = rail->Device;
++    EP3_TXD_RAIL_BLOCK *blk;
++    EP3_TXD_RAIL       *txdRail;
++    sdramaddr_t        pTxdElan;
++    EP3_TXD_RAIL_MAIN  *pTxdMain;
++    E3_Addr          pTxdElanAddr;
++    E3_Addr          pTxdMainAddr;
++    E3_BlockCopyEvent  event;
++    int                      i;
++    unsigned long      flags;
++
++    KMEM_ZALLOC (blk, EP3_TXD_RAIL_BLOCK *, sizeof (EP3_TXD_RAIL_BLOCK), 1);
++
++    if (blk == NULL)
++      return 0;
++
++    if ((pTxdElan = ep_alloc_elan (&rail->Generic, EP3_TXD_RAIL_ELAN_SIZE * EP3_NUM_TXD_PER_BLOCK, 0, &pTxdElanAddr)) == (sdramaddr_t) 0)
++    {
++      KMEM_FREE (blk, sizeof (EP3_TXD_RAIL_BLOCK));
++      return 0;
++    }
++
++    if ((pTxdMain = ep_alloc_main (&rail->Generic, EP3_TXD_RAIL_MAIN_SIZE * EP3_NUM_TXD_PER_BLOCK, 0, &pTxdMainAddr)) == (EP3_TXD_RAIL_MAIN *) NULL)
++    {
++      ep_free_elan (&rail->Generic, pTxdElanAddr, EP3_TXD_RAIL_ELAN_SIZE * EP3_NUM_TXD_PER_BLOCK);
++      KMEM_FREE (blk, sizeof (EP3_TXD_RAIL_BLOCK));
++      return 0;
++    }
++    
++    if (ReserveDmaRetries (rail, EP3_NUM_TXD_PER_BLOCK, 0) != ESUCCESS)
++    {
++      ep_free_main (&rail->Generic, pTxdMainAddr, EP3_TXD_RAIL_MAIN_SIZE * EP3_NUM_TXD_PER_BLOCK);
++      ep_free_elan (&rail->Generic, pTxdElanAddr, EP3_TXD_RAIL_ELAN_SIZE * EP3_NUM_TXD_PER_BLOCK);
++      KMEM_FREE (blk, sizeof (EP3_TXD_RAIL_BLOCK));
++      return 0;
++    }
++
++    for (txdRail = &blk->Txd[0], i = 0; i < EP3_NUM_TXD_PER_BLOCK; i++, txdRail++)
++    {
++      txdRail->Generic.XmtrRail = &xmtrRail->Generic;
++      txdRail->TxdElan          = pTxdElan;
++      txdRail->TxdElanAddr      = pTxdElanAddr;
++      txdRail->TxdMain          = pTxdMain;
++      txdRail->TxdMainAddr      = pTxdMainAddr;
++
++      RegisterCookie (&rail->CookieTable, &txdRail->EnveCookie, pTxdElanAddr + offsetof (EP3_TXD_RAIL_ELAN, EnveEvent), &EnveCookieOps, (void *) txdRail);
++      RegisterCookie (&rail->CookieTable, &txdRail->DataCookie, pTxdElanAddr + offsetof (EP3_TXD_RAIL_ELAN, DataEvent), &DataCookieOps, (void *) txdRail);
++      RegisterCookie (&rail->CookieTable, &txdRail->DoneCookie, pTxdElanAddr + offsetof (EP3_TXD_RAIL_ELAN, DoneEvent), &DoneCookieOps, (void *) txdRail);
++
++      EP3_INIT_COPY_EVENT (event, txdRail->EnveCookie, pTxdMainAddr + offsetof (EP3_TXD_RAIL_MAIN, EnveEvent), 0);
++      elan3_sdram_copyl_to_sdram (dev, &event, pTxdElan + offsetof (EP3_TXD_RAIL_ELAN, EnveEvent), sizeof (E3_BlockCopyEvent));
++
++      EP3_INIT_COPY_EVENT (event, txdRail->DataCookie, pTxdMainAddr + offsetof (EP3_TXD_RAIL_MAIN, DataEvent), 0);
++      elan3_sdram_copyl_to_sdram (dev, &event, pTxdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent), sizeof (E3_BlockCopyEvent));
++
++      EP3_INIT_COPY_EVENT (event, txdRail->DoneCookie, pTxdMainAddr + offsetof (EP3_TXD_RAIL_MAIN, DoneEvent), 0);
++      elan3_sdram_copyl_to_sdram (dev, &event, pTxdElan + offsetof (EP3_TXD_RAIL_ELAN, DoneEvent), sizeof (E3_BlockCopyEvent));
++      
++      pTxdMain->EnveEvent = EP3_EVENT_FREE;
++      pTxdMain->DataEvent = EP3_EVENT_FREE;
++      pTxdMain->DoneEvent = EP3_EVENT_FREE;
++
++      /* move onto next descriptor */
++      pTxdElan     += EP3_TXD_RAIL_ELAN_SIZE;
++      pTxdElanAddr += EP3_TXD_RAIL_ELAN_SIZE;
++      pTxdMain      = (EP3_TXD_RAIL_MAIN *) ((unsigned long) pTxdMain + EP3_TXD_RAIL_MAIN_SIZE);
++      pTxdMainAddr += EP3_TXD_RAIL_MAIN_SIZE;
++    }
++
++    spin_lock_irqsave (&xmtrRail->FreeDescLock, flags);
++
++    list_add  (&blk->Link, &xmtrRail->DescBlockList);
++    xmtrRail->TotalDescCount += EP3_NUM_TXD_PER_BLOCK;
++    xmtrRail->FreeDescCount  += EP3_NUM_TXD_PER_BLOCK;
++
++    for (i = 0; i < EP3_NUM_TXD_PER_BLOCK; i++)
++      list_add (&blk->Txd[i].Generic.Link, &xmtrRail->FreeDescList);
++
++    spin_unlock_irqrestore (&xmtrRail->FreeDescLock, flags);
++    
++    return 1;
++}
++
++static void
++FreeTxdRailBlock (EP3_XMTR_RAIL *xmtrRail, EP3_TXD_RAIL_BLOCK *blk)
++{
++    EP3_RAIL     *rail = XMTR_TO_RAIL(xmtrRail);
++    EP3_TXD_RAIL *txdRail;
++    unsigned long flags;
++    int i;
++
++    spin_lock_irqsave (&xmtrRail->FreeDescLock, flags);
++
++    list_del (&blk->Link);
++    
++    xmtrRail->TotalDescCount -= EP3_NUM_TXD_PER_BLOCK;
++    
++    for (txdRail = &blk->Txd[0], i = 0; i < EP3_NUM_TXD_PER_BLOCK; i++, txdRail++)
++    {
++      xmtrRail->FreeDescCount--;
++      
++      list_del (&txdRail->Generic.Link);
++      
++      DeregisterCookie (&rail->CookieTable, &txdRail->EnveCookie);
++      DeregisterCookie (&rail->CookieTable, &txdRail->DataCookie);
++      DeregisterCookie (&rail->CookieTable, &txdRail->DoneCookie);
++    }
++
++    spin_unlock_irqrestore (&xmtrRail->FreeDescLock, flags);
++
++    ReleaseDmaRetries (rail, EP3_NUM_TXD_PER_BLOCK);
++
++    ep_free_main (&rail->Generic, blk->Txd[0].TxdMainAddr, EP3_TXD_RAIL_MAIN_SIZE * EP3_NUM_TXD_PER_BLOCK);
++    ep_free_elan (&rail->Generic, blk->Txd[0].TxdElanAddr, EP3_TXD_RAIL_ELAN_SIZE * EP3_NUM_TXD_PER_BLOCK);
++    KMEM_FREE (blk, sizeof (EP3_TXD_RAIL_BLOCK));
++}
++
++static EP3_TXD_RAIL *
++GetTxdRail (EP3_XMTR_RAIL *xmtrRail)
++{
++    EP_COMMS_SUBSYS  *subsys = xmtrRail->Generic.Xmtr->Subsys;
++    EP3_TXD_RAIL     *txdRail;
++    int low_on_txds;
++    unsigned long flags;
++
++    spin_lock_irqsave (&xmtrRail->FreeDescLock, flags);
++
++    if (list_empty (&xmtrRail->FreeDescList))
++      txdRail = NULL;
++    else
++    {
++      txdRail = list_entry (xmtrRail->FreeDescList.next, EP3_TXD_RAIL, Generic.Link);
++
++#if defined(DEBUG)
++      {
++          EP_RAIL   *rail = xmtrRail->Generic.CommsRail->Rail;
++          ELAN3_DEV *dev  = ((EP3_RAIL *) rail)->Device;
++          
++          EP_ASSERT (rail, txdRail->TxdMain->EnveEvent == EP3_EVENT_FREE);
++          EP_ASSERT (rail, txdRail->TxdMain->DataEvent == EP3_EVENT_FREE);
++          EP_ASSERT (rail, txdRail->TxdMain->DoneEvent == EP3_EVENT_FREE);
++          EP_ASSERT (rail, SDRAM_ASSERT(elan3_sdram_readl (dev, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, EnveEvent.ev_Count)) == 0));
++          EP_ASSERT (rail, SDRAM_ASSERT(elan3_sdram_readl (dev, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent.ev_Count)) == 0));
++          EP_ASSERT (rail, SDRAM_ASSERT(elan3_sdram_readl (dev, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DoneEvent.ev_Count)) == 0));
++      }
++#endif
++      
++      list_del (&txdRail->Generic.Link);
++
++      xmtrRail->FreeDescCount--;
++    }
++    /* Wakeup the descriptor primer thread if there's not many left */
++    low_on_txds = (xmtrRail->FreeDescCount < ep_txd_lowat);
++
++    spin_unlock_irqrestore (&xmtrRail->FreeDescLock, flags);
++
++    if (low_on_txds)
++      ep_kthread_schedule (&subsys->Thread, lbolt);
++
++    return (txdRail);
++}
++
++static void
++FreeTxdRail (EP3_XMTR_RAIL *xmtrRail, EP3_TXD_RAIL *txdRail)
++{
++    unsigned long flags;
++
++#if defined(DEBUG_ASSERT)
++    {
++      EP_RAIL   *rail = xmtrRail->Generic.CommsRail->Rail;
++      ELAN3_DEV *dev  = ((EP3_RAIL *) rail)->Device;
++
++      EP_ASSERT (rail, txdRail->Generic.XmtrRail == &xmtrRail->Generic);
++      
++      EP_ASSERT (rail, txdRail->TxdMain->EnveEvent == EP3_EVENT_PRIVATE);
++      EP_ASSERT (rail, txdRail->TxdMain->DataEvent == EP3_EVENT_PRIVATE);
++      EP_ASSERT (rail, txdRail->TxdMain->DoneEvent == EP3_EVENT_PRIVATE);
++      EP_ASSERT (rail, SDRAM_ASSERT (elan3_sdram_readl (dev, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, EnveEvent.ev_Count)) == 0));
++      EP_ASSERT (rail, SDRAM_ASSERT (elan3_sdram_readl (dev, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent.ev_Count)) == 0));
++      EP_ASSERT (rail, SDRAM_ASSERT (elan3_sdram_readl (dev, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DoneEvent.ev_Count)) == 0));
++      
++      txdRail->TxdMain->EnveEvent = EP3_EVENT_FREE;
++      txdRail->TxdMain->DataEvent = EP3_EVENT_FREE;
++      txdRail->TxdMain->DoneEvent = EP3_EVENT_FREE;
++    }
++#endif
++
++    spin_lock_irqsave (&xmtrRail->FreeDescLock, flags);
++    
++    list_add (&txdRail->Generic.Link, &xmtrRail->FreeDescList);
++
++    xmtrRail->FreeDescCount++;
++
++    if (xmtrRail->FreeDescWaiting)
++    {
++      xmtrRail->FreeDescWaiting--;
++      kcondvar_wakeupall (&xmtrRail->FreeDescSleep, &xmtrRail->FreeDescLock);
++    }
++
++    spin_unlock_irqrestore (&xmtrRail->FreeDescLock, flags);
++}
++
++static void
++BindTxdToRail (EP_TXD *txd, EP3_TXD_RAIL *txdRail)
++{
++    ASSERT (SPINLOCK_HELD (&txd->Xmtr->Lock));
++
++    EPRINTF6 (DBG_XMTR, "%s: BindTxdToRail: txd=%p txdRail=%p XID=%08x.%08x.%016llx\n", 
++            XMTR_TO_RAIL(txdRail->Generic.XmtrRail)->Generic.Name, txd, txdRail, 
++            txd->Envelope.Xid.Generation, txd->Envelope.Xid.Handle, (long long) txd->Envelope.Xid.Unique);
++
++    txd->TxdRail = &txdRail->Generic;
++    txdRail->Generic.Txd = txd;
++}
++
++static void
++UnbindTxdFromRail (EP_TXD *txd, EP3_TXD_RAIL *txdRail)
++{
++    ASSERT (SPINLOCK_HELD (&txd->Xmtr->Lock));
++    ASSERT (txd->TxdRail == &txdRail->Generic && txdRail->Generic.Txd == txd);
++
++    EPRINTF6 (DBG_XMTR, "%s: UnbindTxdToRail: txd=%p txdRail=%p XID=%08x.%08x.%016llx\n", 
++            XMTR_TO_RAIL(txdRail->Generic.XmtrRail)->Generic.Name, txd, txdRail, 
++            txd->Envelope.Xid.Generation, txd->Envelope.Xid.Handle, (long long) txd->Envelope.Xid.Unique);
++    txd->TxdRail = NULL;
++    txdRail->Generic.Txd = NULL; 
++}
++
++/*
++ * TxEnveEvent: arg == EP_TXD
++ *    Called when envelope delivered
++ */
++static void
++TxEnveEvent (EP3_RAIL *rail, void *arg)
++{
++    panic ("TxEnveEvent");
++}
++
++/*
++ * TxEnveRetry: arg == EP3_TXD_RAIL
++ *    Called on retry of dma of large message envelope.
++ */
++static void
++TxEnveRetry (EP3_RAIL *rail, void *arg, E3_DMA_BE *dma, int status)
++{
++    EP3_TXD_RAIL  *txdRail  = (EP3_TXD_RAIL *) arg;
++    EP3_XMTR_RAIL *xmtrRail = (EP3_XMTR_RAIL *) txdRail->Generic.XmtrRail;
++    
++    EPRINTF3 (DBG_XMTR, "%s: TxEnveRetry: xmtr %p txd %p\n",  rail->Generic.Name, xmtrRail, txdRail);
++    
++    EP_ASSERT (&rail->Generic, txdRail->TxdMain->EnveEvent == EP3_EVENT_ACTIVE);
++    EP_ASSERT (&rail->Generic, SDRAM_ASSERT (elan3_sdram_readl (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, EnveEvent.ev_Count)) == 1)); /* PCI read */
++    EP_ASSERT (&rail->Generic, dma->s.dma_direction == DMA_WRITE && EP_VP_TO_NODE(dma->s.dma_destVProc) == txdRail->Generic.Txd->NodeId);
++
++    if (! TxdShouldStabalise (&txdRail->Generic, &rail->Generic))
++      QueueDmaForRetry (rail, dma, EP_RETRY_LOW_PRI_RETRY + ep_backoff (&txdRail->Backoff, EP_BACKOFF_ENVELOPE));
++    else
++      QueueDmaForRetry (rail, dma, EP_RETRY_STABALISING);     /* place dma on stabilising list for neterr fixup */
++}
++
++static void
++TxEnveVerify (EP3_RAIL *rail, void *arg, E3_DMA_BE *dma)
++{
++    EP3_TXD_RAIL *txdRail = (EP3_TXD_RAIL *) arg;
++    
++    EP_ASSERT (&rail->Generic, txdRail->TxdMain->EnveEvent == EP3_EVENT_ACTIVE);
++    EP_ASSERT (&rail->Generic, SDRAM_ASSERT (elan3_sdram_readl (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, EnveEvent.ev_Count)) == 1)); /* PCI read */
++    EP_ASSERT (&rail->Generic, dma->s.dma_direction == DMA_WRITE && EP_VP_TO_NODE(dma->s.dma_destVProc) == txdRail->Generic.Txd->NodeId);
++}
++
++/*
++ * TxDataEvent: arg == EP3_TXD
++ *    Called on completion of a large transmit.
++ */
++static void
++TxDataEvent (EP3_RAIL *rail, void *arg)
++{
++    EP3_TXD_RAIL      *txdRail  = (EP3_TXD_RAIL *) arg;
++    EP3_XMTR_RAIL     *xmtrRail = (EP3_XMTR_RAIL *) txdRail->Generic.XmtrRail;
++    EP_XMTR         *xmtr     = xmtrRail->Generic.Xmtr;
++    EP3_TXD_RAIL_MAIN *txdMain  = txdRail->TxdMain;
++    sdramaddr_t        txdElan  = txdRail->TxdElan;
++    int                delay    = 1;
++    EP_TXD            *txd;
++    unsigned long      flags;
++
++    spin_lock_irqsave (&xmtr->Lock, flags);
++    for (;;)
++    {
++      if (EP3_EVENT_FIRED (txdRail->DataCookie, txdMain->DataEvent))
++          break;
++
++      if (EP3_EVENT_FIRING (rail->Device, txdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent), txdRail->DataCookie, txdMain->DataEvent))                /* PCI read */
++      {
++          if (delay > EP3_EVENT_FIRING_TLIMIT)
++              panic ("TxDataEvent: events set but block copy not completed\n");
++          DELAY(delay);
++          delay <<= 1;
++      }
++      else
++      {
++          EPRINTF3 (DBG_XMTR, "%s: TxDataEvent: xmtr %p txd %p previously collecting by polling\n", 
++                    rail->Generic.Name, xmtrRail, txdRail);
++          spin_unlock_irqrestore (&xmtr->Lock, flags);
++          return;
++      }
++      mb();
++    }
++
++    if ((txd = txdRail->Generic.Txd) == NULL ||                       /* If there is no txd, or if the descriptor is marked */
++      !(EP_IS_INTERRUPT_ENABLED(txd->Envelope.Attr)) ||       /* as no interrupt, or been reused as an RPC, */
++      (EP_IS_RPC(txd->Envelope.Attr)))                        /* then we were either called as a result of a previous */
++    {                                                         /* tx which was completed by polling or as a result */
++      spin_unlock_irqrestore (&xmtr->Lock, flags);            /* of a EnableTxCallBack/DisableTxCallback */
++
++      EPRINTF4 (DBG_XMTR, "%s: TxDataEvent: xmtr %p txd %p recyled (%x)\n", 
++                rail->Generic.Name, xmtr, txd, txd ? txd->Envelope.Attr : 0);
++      return;
++    }
++
++    ASSERT (EP3_EVENT_FIRED (txdRail->EnveCookie, txdMain->EnveEvent));
++
++    EPRINTF5 (DBG_XMTR, "%s: TxDataEvent : xmtrRail=%p txdRail=%p tx=%p XID=%llx\n", 
++            rail->Generic.Name, xmtrRail, txdRail, txd, (long long) txd->Envelope.Xid.Unique);
++    
++    ep_xmtr_txd_stat(xmtr,txd);
++    
++    /* remove from active transmit lists */
++    list_del (&txd->Link);
++
++    UnbindTxdFromRail (txd, txdRail);
++    
++    /* clear the done flags for next time round */
++    txdMain->EnveEvent = EP3_EVENT_PRIVATE;
++    txdMain->DataEvent = EP3_EVENT_PRIVATE;
++    txdMain->DoneEvent = EP3_EVENT_PRIVATE;
++    
++    FreeTxdRail (xmtrRail, txdRail);
++
++    spin_unlock_irqrestore (&xmtr->Lock, flags);
++    
++    txd->Handler (txd, txd->Arg, EP_SUCCESS);
++    
++    FreeTxd (xmtr, txd);
++}
++
++/*
++ * TxDataRetry: arg == EP3_TXD
++ *    Called on retry of remote "put" dma of large transmit data.
++ */
++static void
++TxDataRetry (EP3_RAIL *rail, void *arg, E3_DMA_BE *dma, int status)
++{
++    EP3_TXD_RAIL  *txdRail  = (EP3_TXD_RAIL *) arg;
++    EP3_XMTR_RAIL *xmtrRail = (EP3_XMTR_RAIL *) txdRail->Generic.XmtrRail;
++    EP_TXD        *txd      = txdRail->Generic.Txd;
++
++    EP_ASSERT (&rail->Generic, ((txdRail->TxdMain->DataEvent == EP3_EVENT_ACTIVE && 
++                               SDRAM_ASSERT (elan3_sdram_readl (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent.ev_Count)) >= 1)) ||  /* PCI read */
++                              (EP3_EVENT_FIRED (txdRail->DataCookie, txdRail->TxdMain->DataEvent) &&
++                               SDRAM_ASSERT (elan3_sdram_readl (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent.ev_Count)) == 0))));  /* PCI read */
++    EP_ASSERT (&rail->Generic, dma->s.dma_direction == DMA_WRITE && EP_VP_TO_NODE(dma->s.dma_destVProc) == txd->NodeId);
++
++    EPRINTF5 (DBG_XMTR, "%s: TxDataRetry: xmtrRail=%p txdRail=%p txd=%p XID=%llx\n", 
++            rail->Generic.Name, xmtrRail, txdRail, txd, (long long) txd->Envelope.Xid.Unique);
++    
++    QueueDmaForRetry (rail, dma, EP_RETRY_LOW_PRI_RETRY + ep_backoff (&txdRail->Backoff, EP_BACKOFF_DATA));
++}
++
++static void
++TxDataVerify (EP3_RAIL *rail, void *arg, E3_DMA_BE *dma)
++{
++    EP3_TXD_RAIL *txdRail = (EP3_TXD_RAIL *) arg;
++    EP_TXD       *txd     = txdRail->Generic.Txd;
++
++    EP_ASSERT (&rail->Generic, ((txdRail->TxdMain->DataEvent == EP3_EVENT_ACTIVE && 
++                               SDRAM_ASSERT (elan3_sdram_readl (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent.ev_Count)) >= 1)) ||  /* PCI read */
++                              (EP3_EVENT_FIRED (txdRail->DataCookie, txdRail->TxdMain->DataEvent) &&
++                               SDRAM_ASSERT (elan3_sdram_readl (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent.ev_Count)) == 0))));  /* PCI read */
++    EP_ASSERT (&rail->Generic, dma->s.dma_direction == DMA_WRITE && EP_VP_TO_NODE(dma->s.dma_destVProc) == txd->NodeId);
++}
++
++/*
++ * TxDoneEvent: arg == EP3_TXD
++ *    Called on completion of a RPC.
++ */
++static void
++TxDoneEvent (EP3_RAIL *rail, void *arg)
++{
++    EP3_TXD_RAIL      *txdRail  = (EP3_TXD_RAIL *) arg;
++    EP3_XMTR_RAIL     *xmtrRail = (EP3_XMTR_RAIL *) txdRail->Generic.XmtrRail;
++    EP_XMTR         *xmtr     = xmtrRail->Generic.Xmtr;
++    int                delay   = 1;
++    EP_TXD          *txd;
++    unsigned long      flags;
++
++    spin_lock_irqsave (&xmtr->Lock, flags);
++
++    for (;;)
++    {
++      if (EP3_EVENT_FIRED (txdRail->DoneCookie, txdRail->TxdMain->DoneEvent) && 
++          EP3_EVENT_FIRED (txdRail->DataCookie, txdRail->TxdMain->DataEvent))
++          break;
++      
++      if (EP3_EVENT_FIRING (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DoneEvent), txdRail->DoneCookie, txdRail->TxdMain->DoneEvent) && 
++          EP3_EVENT_FIRING (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent), txdRail->DataCookie, txdRail->TxdMain->DataEvent))
++      {
++          if (delay > EP3_EVENT_FIRING_TLIMIT)
++              panic ("TxDoneEvent: events set but block copy not completed\n");
++          DELAY(delay);
++          delay <<= 1;
++      }
++      else
++      {
++          EPRINTF3 (DBG_XMTR, "%s: TxDoneEvent: xmtr %p txdRail %p previously collecting by polling\n", 
++                    rail->Generic.Name, xmtr, txdRail);
++          spin_unlock_irqrestore (&xmtr->Lock, flags);
++          return;
++      }
++      mb();
++    }
++
++    if ((txd = txdRail->Generic.Txd) == NULL ||                                                /* If there is no txd, or if the descriptor is marked */
++      !(EP_IS_INTERRUPT_ENABLED(txd->Envelope.Attr) || EP_IS_RPC(txd->Envelope.Attr))) /* marked as no interrupt, or been reused as an transmit, */
++    {                                                                                  /* then we were either called as a result of a previous */
++      spin_unlock_irqrestore (&xmtr->Lock, flags);                                     /* tx which was completed by polling or as a result */
++                                                                                       /* of a EnableTxCallBack/DisableTxCallback */
++
++      EPRINTF4 (DBG_XMTR, "%s: TxDoneEvent: xmtr %p txd %p recyled (%x)\n", 
++                rail->Generic.Name, xmtr, txd, txd ? txd->Envelope.Attr : 0);
++      return; 
++    }
++
++    EPRINTF5 (DBG_XMTR, "%s: TxDoneEvent: xmtrRail=%p txdRail=%p txd=%p XID=%llx\n", 
++            rail->Generic.Name, xmtrRail, txdRail, txd, (long long) txd->Envelope.Xid.Unique);
++
++    ep_xmtr_txd_stat(xmtr,txd);
++
++    /* remove from active transmit list */
++    list_del (&txd->Link);
++    
++    UnbindTxdFromRail (txd, txdRail);
++    
++    /* clear the done flags for next time round */
++    txdRail->TxdMain->EnveEvent = EP3_EVENT_PRIVATE;
++    txdRail->TxdMain->DataEvent = EP3_EVENT_PRIVATE;
++    txdRail->TxdMain->DoneEvent = EP3_EVENT_PRIVATE;
++    
++    FreeTxdRail (xmtrRail, txdRail);
++
++    spin_unlock_irqrestore (&xmtr->Lock, flags);
++          
++    if (txd->Handler)
++      txd->Handler (txd, txd->Arg, EP_SUCCESS);
++      
++    FreeTxd (xmtr, txd);
++}
++
++/*
++ * TxDoneRetry: arg == EP3_TXD
++ */
++static void
++TxDoneRetry (EP3_RAIL *rail, void *arg, E3_DMA_BE *dma, int status)
++{
++    panic ("TxDoneRetry");
++}
++
++static void
++TxDoneVerify (EP3_RAIL *rail, void *arg, E3_DMA_BE *dma)
++{
++    panic ("TxDoneVerify");
++}
++
++static void
++EnableTransmitCallback (EP_TXD *txd, EP3_TXD_RAIL *txdRail)
++{
++    ELAN3_DEV *dev = XMTR_TO_RAIL(txdRail->Generic.XmtrRail)->Device;
++
++    EPRINTF3 (DBG_XMTR, "%s: EnableTransmitCallback: txd %p txdRail %p\n", XMTR_TO_RAIL (txdRail->Generic.XmtrRail)->Generic.Name, txd, txdRail);
++
++    txd->Envelope.Attr = EP_SET_INTERRUPT_ENABLED(txd->Envelope.Attr);
++              
++    elan3_sdram_writel (dev, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, EnveEvent.ev_Type), EV_TYPE_BCOPY);
++      
++    if (EP_IS_RPC(txd->Envelope.Attr))
++    {
++      elan3_sdram_writel (dev, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent.ev_Type), EV_TYPE_BCOPY);
++      elan3_sdram_writel (dev, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DoneEvent.ev_Type),  EV_TYPE_BCOPY | EV_TYPE_EVIRQ | txdRail->DoneCookie.Cookie);
++    }
++    else
++    {
++      elan3_sdram_writel (dev, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent.ev_Type), EV_TYPE_BCOPY | EV_TYPE_EVIRQ | txdRail->DataCookie.Cookie);
++      elan3_sdram_writel (dev, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DoneEvent.ev_Type), EV_TYPE_BCOPY);
++    }
++}
++
++static void
++DisableTransmitCallback (EP_TXD *txd, EP3_TXD_RAIL *txdRail)
++{
++    ELAN3_DEV *dev = XMTR_TO_RAIL(txdRail->Generic.XmtrRail)->Device;
++
++    EPRINTF3 (DBG_XMTR, "%s: DisableTransmitCallback: txd %p txdRail %p\n", XMTR_TO_RAIL (txdRail->Generic.XmtrRail)->Generic.Name, txd, txdRail);
++
++    txd->Envelope.Attr = EP_CLEAR_INTERRUPT_ENABLED(txd->Envelope.Attr);
++
++    elan3_sdram_writel (dev, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, EnveEvent.ev_Type), EV_TYPE_BCOPY);
++    elan3_sdram_writel (dev, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent.ev_Type), EV_TYPE_BCOPY);
++    elan3_sdram_writel (dev, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DoneEvent.ev_Type), EV_TYPE_BCOPY);
++}
++
++static void
++InitialiseTxdRail (EP_TXD *txd, EP3_TXD_RAIL *txdRail, int phase)
++{
++    EP3_XMTR_RAIL *xmtrRail = (EP3_XMTR_RAIL *) txdRail->Generic.XmtrRail;
++    EP3_RAIL      *rail     = XMTR_TO_RAIL (xmtrRail);
++
++    /* Flush the Elan TLB if mappings have changed */
++    ep_perrail_dvma_sync (&rail->Generic);
++
++    /* Initialise the per-rail fields in the envelope */
++    txd->Envelope.TxdRail = txdRail->TxdElanAddr;
++    txd->Envelope.NodeId  = rail->Generic.Position.pos_nodeid;
++
++    /* Initialise the dma backoff */
++    txdRail->Backoff.type = EP_BACKOFF_FREE;
++
++    /* Initialise the per-rail events */
++    switch (phase)
++    {
++    case EP_TXD_PHASE_ACTIVE:
++      elan3_sdram_writel (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, EnveEvent.ev_Count), 1);
++      elan3_sdram_writel (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent.ev_Count), 
++                          (txd->Envelope.nFrags ? txd->Envelope.nFrags : 1) + (EP_IS_MULTICAST(txd->Envelope.Attr) ? 1 : 0));
++      
++      txdRail->TxdMain->EnveEvent = EP3_EVENT_ACTIVE;
++      txdRail->TxdMain->DataEvent = EP3_EVENT_ACTIVE;
++      break;
++      
++    case EP_TXD_PHASE_PASSIVE:
++      ASSERT (EP_IS_RPC(txd->Envelope.Attr));
++
++      elan3_sdram_writel (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, EnveEvent.ev_Count), 0);
++      elan3_sdram_writel (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent.ev_Count), 0);
++
++      txdRail->TxdMain->EnveEvent = txdRail->EnveCookie.Cookie;
++      txdRail->TxdMain->DataEvent = txdRail->DataCookie.Cookie;
++      break;
++    }
++
++    if (! EP_IS_RPC(txd->Envelope.Attr))
++      txdRail->TxdMain->DoneEvent = txdRail->DoneCookie.Cookie;
++    else
++    {
++      elan3_sdram_writel (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DoneEvent.ev_Count), 1);
++      txdRail->TxdMain->DoneEvent = EP3_EVENT_ACTIVE;
++    }
++
++    if (EP_IS_NO_INTERRUPT(txd->Envelope.Attr))
++      DisableTransmitCallback (txd, txdRail);
++    else
++      EnableTransmitCallback (txd, txdRail);
++
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++    if ( epdebug_check_sum ) 
++      txd->Envelope.CheckSum = ep_calc_check_sum( txd->Xmtr->Subsys->Subsys.Sys, &txd->Envelope, txd->Envelope.Frags, txd->Envelope.nFrags);
++    else
++#endif
++      txd->Envelope.CheckSum = 0;  
++
++    /* copy the envelope and payload if present down to sdram */
++    elan3_sdram_copyl_to_sdram (rail->Device, &txd->Envelope, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, Envelope), EP_ENVELOPE_SIZE);
++    
++    if (EP_HAS_PAYLOAD(txd->Envelope.Attr))
++      elan3_sdram_copyl_to_sdram (rail->Device, &txd->Payload, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, Payload), EP_PAYLOAD_SIZE);
++}
++
++void
++ep3xmtr_flush_callback (EP_XMTR *xmtr, EP3_XMTR_RAIL *xmtrRail)
++{
++    EP3_RAIL *rail = XMTR_TO_RAIL (xmtrRail);
++    struct list_head *el;
++    unsigned long flags;
++
++    switch (rail->Generic.CallbackStep)
++    {
++    case EP_CB_FLUSH_FILTERING:
++      /* only need to acquire/release the Lock to ensure that
++       * the node state transition has been noticed. */
++      spin_lock_irqsave (&xmtr->Lock, flags);
++      spin_unlock_irqrestore (&xmtr->Lock, flags);
++      break;
++
++    case EP_CB_FLUSH_FLUSHING:
++      spin_lock_irqsave (&xmtr->Lock, flags);
++      
++      list_for_each (el, &xmtr->ActiveDescList) {
++          EP_TXD       *txd      = list_entry (el, EP_TXD, Link);
++          EP3_TXD_RAIL *txdRail  = (EP3_TXD_RAIL *) txd->TxdRail;
++          EP_NODE_RAIL *nodeRail = &rail->Generic.Nodes[txd->NodeId];
++          
++          if (!TXD_BOUND2RAIL(txdRail, xmtrRail) || nodeRail->State != EP_NODE_LOCAL_PASSIVATE)
++              continue;
++          
++          if (EP_IS_RPC(txd->Envelope.Attr))
++          {
++              if (! EP3_EVENT_FIRED (txdRail->DataCookie, txdRail->TxdMain->DataEvent))
++                  nodeRail->MessageState |= EP_NODE_ACTIVE_MESSAGES;
++              else if (! EP3_EVENT_FIRED (txdRail->DoneCookie, txdRail->TxdMain->DoneEvent))
++                  nodeRail->MessageState |= EP_NODE_PASSIVE_MESSAGES;
++          }
++          else
++          {
++              if (! EP3_EVENT_FIRED (txdRail->DataCookie, txdRail->TxdMain->DataEvent))
++                  nodeRail->MessageState |= EP_NODE_ACTIVE_MESSAGES;
++          }
++      }
++      spin_unlock_irqrestore (&xmtr->Lock, flags);
++      break;
++
++    default:
++      panic ("ep3xmtr_flush_callback: invalid callback step\n");
++      break;
++    }
++}
++
++void
++ep3xmtr_failover_callback (EP_XMTR *xmtr, EP3_XMTR_RAIL *xmtrRail)
++{
++    EP3_RAIL         *rail   = XMTR_TO_RAIL (xmtrRail);
++    struct list_head  txdList;
++    struct list_head *el, *nel;
++    unsigned long flags;
++#ifdef SUPPORT_RAIL_FAILOVER
++    EP_COMMS_SUBSYS  *subsys = xmtr->Subsys;
++#endif
++
++    INIT_LIST_HEAD (&txdList);
++
++    spin_lock_irqsave (&xmtr->Lock, flags);
++    list_for_each_safe (el, nel, &xmtr->ActiveDescList) {
++      EP_TXD       *txd       = list_entry (el, EP_TXD, Link);
++      EP3_TXD_RAIL *txdRail   = (EP3_TXD_RAIL *) txd->TxdRail;
++      EP_NODE_RAIL *nodeRail = &rail->Generic.Nodes[txd->NodeId];
++          
++      /* Only progress relocation of txd's bound to this rail */
++      if (!TXD_BOUND2RAIL(txdRail, xmtrRail) || nodeRail->State != EP_NODE_PASSIVATED)
++          continue;
++      
++#ifdef SUPPORT_RAIL_FAILOVER
++      /* Transmit data not been sent, so just restart on different rail */
++      if (! EP3_EVENT_FIRED (txdRail->DataCookie, txdRail->TxdMain->DataEvent))
++      {
++          EPRINTF4 (DBG_XMTR, "%s: ep3xmtr_failover_callback - xmtr %p txd %p node %d unbind an retry\n", rail->Generic.Name, xmtr, txd, txd->NodeId);
++          
++          UnbindTxdFromRail (txd, txdRail);
++          
++          /* clear the done flags - so that it will be ignored if an event interrupt is generated */
++          txdRail->TxdMain->EnveEvent = EP3_EVENT_PRIVATE;
++          txdRail->TxdMain->DataEvent = EP3_EVENT_PRIVATE;
++          txdRail->TxdMain->DoneEvent = EP3_EVENT_PRIVATE;
++
++          /* reset all events, since non of them could have been set */
++          elan3_sdram_writel (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, EnveEvent.ev_Count), 0);                          /* PCI write */
++          elan3_sdram_writel (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent.ev_Count), 0);                          /* PCI write */
++          elan3_sdram_writel (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DoneEvent.ev_Count), 0);                          /* PCI write */
++          
++          FreeTxdRail (xmtrRail, txdRail);
++          
++          /* epcomms thread will restart on different rail */
++          ep_kthread_schedule (&subsys->Thread, lbolt);
++          continue;
++      }
++
++      if (EP_IS_RPC(txd->Envelope.Attr) && !EP3_EVENT_FIRED (txdRail->DoneCookie, txdRail->TxdMain->DoneEvent))
++      {
++          if (EP_IS_NO_FAILOVER(txd->Envelope.Attr))
++          {
++              EPRINTF4 (DBG_XMTR, "%s: ep3xmtr_failover_callback - xmtr %p txd %p node %d - not able to failover\n",
++                        rail->Generic.Name, xmtr, txd, txd->NodeId);
++
++              list_del (&txd->Link);
++              UnbindTxdFromRail (txd, txdRail);
++              
++              /* clear the done flags - so that it will be ignored if an event interrupt is generated */
++              txdRail->TxdMain->EnveEvent = EP3_EVENT_PRIVATE;
++              txdRail->TxdMain->DataEvent = EP3_EVENT_PRIVATE;
++              txdRail->TxdMain->DoneEvent = EP3_EVENT_PRIVATE;
++              
++              /* envelope and data events must have been set, so only clear the done event */
++              EP_ASSERT (&rail->Generic, SDRAM_ASSERT(elan3_sdram_readl (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, EnveEvent.ev_Count)) == 0));
++              EP_ASSERT (&rail->Generic, SDRAM_ASSERT(elan3_sdram_readl (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent.ev_Count)) == 0));
++
++              elan3_sdram_writel (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DoneEvent.ev_Count), 0);                              /* PCI write */
++              
++              FreeTxdRail (xmtrRail, txdRail);
++          
++              list_add_tail (&txd->Link, &txdList);
++              continue;
++          }
++          EPRINTF4 (DBG_XMTR, "%s: ep3xmtr_failover_callback - xmtr %p txd %p node %d passive\n", rail->Generic.Name, xmtr, txd, txd->NodeId);
++          
++          nodeRail->MessageState |= EP_NODE_PASSIVE_MESSAGES;
++          continue;
++      }
++
++      EPRINTF4 (DBG_XMTR, "%s: ep3xmtr_failover_callback - xmtr %p txd %p node %d completed\n", rail->Generic.Name, xmtr, txd, txd->NodeId);
++#endif
++
++    }
++    spin_unlock_irqrestore (&xmtr->Lock, flags);
++
++    while (! list_empty (&txdList)) 
++    {
++      EP_TXD *txd = list_entry (txdList.next, EP_TXD, Link);
++
++      list_del (&txd->Link);
++
++      txd->Handler (txd, txd->Arg, EP_CONN_RESET);
++      
++      FreeTxd (xmtr, txd);
++    }
++}
++
++
++void
++ep3xmtr_disconnect_callback (EP_XMTR *xmtr, EP3_XMTR_RAIL *xmtrRail)
++{
++    EP3_RAIL         *rail = XMTR_TO_RAIL (xmtrRail);
++    struct list_head *el, *nel;
++    struct list_head  txdList;
++    unsigned long flags;
++    
++    INIT_LIST_HEAD (&txdList);
++
++    spin_lock_irqsave (&xmtr->Lock, flags);
++
++    list_for_each_safe (el, nel, &xmtr->ActiveDescList) {
++      EP_TXD       *txd       = list_entry (el, EP_TXD, Link);
++      EP3_TXD_RAIL *txdRail   = (EP3_TXD_RAIL *) txd->TxdRail;
++      EP_NODE_RAIL *nodeRail = &rail->Generic.Nodes[txd->NodeId];
++          
++      if (!TXD_BOUND2RAIL(txdRail, xmtrRail) || nodeRail->State != EP_NODE_DISCONNECTING)
++          continue;
++      
++      if (EP3_EVENT_FIRED (txdRail->EnveCookie, txdRail->TxdMain->EnveEvent) &&
++          EP3_EVENT_FIRED (txdRail->DataCookie, txdRail->TxdMain->DataEvent) &&
++          EP3_EVENT_FIRED (txdRail->DoneCookie, txdRail->TxdMain->DoneEvent))
++      {
++          EPRINTF4 (DBG_XMTR, "%s: ep3xmtr_disconnect_callback - xmtr %p txd %p completed to node %d\n", rail->Generic.Name, xmtr, txd, txd->NodeId);
++          continue;
++      }
++
++      /* Remove from active list */
++      list_del (&txd->Link);
++      
++      UnbindTxdFromRail (txd, txdRail);
++      
++      /* clear the done flags - so that it will be ignored if an event interrupt is generated */
++      txdRail->TxdMain->EnveEvent = EP3_EVENT_PRIVATE;
++      txdRail->TxdMain->DataEvent = EP3_EVENT_PRIVATE;
++      txdRail->TxdMain->DoneEvent = EP3_EVENT_PRIVATE;
++      
++      /* reset the envelope and data events, since only they could have been set */
++      elan3_sdram_writel (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, EnveEvent.ev_Count), 0);                              /* PCI write */
++      elan3_sdram_writel (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent.ev_Count), 0);                              /* PCI write */
++      elan3_sdram_writel (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DoneEvent.ev_Count), 0);                              /* PCI write */
++      
++      FreeTxdRail (xmtrRail, txdRail);
++          
++      EPRINTF4 (DBG_XMTR, "%s: ep3xmtr_disconnect_callback - xmtr %p txd %p node %d not conected\n", rail->Generic.Name, xmtr, txd, txd->NodeId);
++
++      /* add to the list of txd's which are to be completed */
++      list_add_tail (&txd->Link, &txdList);
++    }
++    spin_unlock_irqrestore (&xmtr->Lock, flags);
++    
++    while (! list_empty (&txdList)) 
++    {
++      EP_TXD *txd = list_entry (txdList.next, EP_TXD, Link);
++
++      list_del (&txd->Link);
++
++      txd->Handler (txd, txd->Arg, EP_CONN_RESET);
++      
++      FreeTxd (xmtr, txd);
++    }
++}
++
++int
++ep3xmtr_poll_txd (EP_XMTR_RAIL *x, EP_TXD_RAIL *t, int how)
++{
++    EP3_XMTR_RAIL *xmtrRail = (EP3_XMTR_RAIL *) x;
++    EP3_TXD_RAIL  *txdRail  = (EP3_TXD_RAIL *) t;
++    EP_TXD        *txd      = txdRail->Generic.Txd;
++
++    switch (how)
++    {
++    case ENABLE_TX_CALLBACK:
++      if (EP_IS_NO_INTERRUPT(txd->Envelope.Attr))
++          EnableTransmitCallback (txd, txdRail);
++      break;
++
++    case DISABLE_TX_CALLBACK:
++      if (EP_IS_NO_INTERRUPT(txd->Envelope.Attr))
++          DisableTransmitCallback (txd, txdRail);
++      break;
++    }
++
++    if (EP3_EVENT_FIRED (txdRail->EnveCookie, txdRail->TxdMain->EnveEvent) &&
++      EP3_EVENT_FIRED (txdRail->DataCookie, txdRail->TxdMain->DataEvent) &&
++      EP3_EVENT_FIRED (txdRail->DoneCookie, txdRail->TxdMain->DoneEvent))
++    {
++      EPRINTF3 (DBG_XMTR, "%s: ep3xmtr_poll_txd: txd=%p XID=%llx completed\n", 
++                XMTR_TO_RAIL (xmtrRail)->Generic.Name, txd, (long long) txd->Envelope.Xid.Unique);
++
++      ep_xmtr_txd_stat(xmtrRail->Generic.Xmtr,txd);
++
++      UnbindTxdFromRail (txd, txdRail);
++      
++      /* clear the done flags - so that it will be ignored if an event interrupt is generated */
++      txdRail->TxdMain->EnveEvent = EP3_EVENT_PRIVATE;
++      txdRail->TxdMain->DataEvent = EP3_EVENT_PRIVATE;
++      txdRail->TxdMain->DoneEvent = EP3_EVENT_PRIVATE;
++      
++      FreeTxdRail (xmtrRail, txdRail);
++
++      return 1;
++    }
++
++    return 0;
++}
++
++int
++ep3xmtr_bind_txd (EP_TXD *txd, EP_XMTR_RAIL *x, unsigned int phase)
++{
++    EP3_XMTR_RAIL *xmtrRail = (EP3_XMTR_RAIL *) x;
++    EP3_RAIL      *rail     = XMTR_TO_RAIL (xmtrRail);
++    EP3_TXD_RAIL  *txdRail;
++    E3_DMA_BE      dmabe;
++
++    if ((txdRail = GetTxdRail (xmtrRail)) == NULL)
++      return 0;
++
++    switch (phase)
++    {
++    case EP_TXD_PHASE_ACTIVE:
++      if (rail->Generic.Nodes[txd->NodeId].State != EP_NODE_CONNECTED)
++      {
++          EPRINTF2 (DBG_XMTR, "%s: TransmitTxdOnRail: node %u not connected on this rail\n", rail->Generic.Name, txd->NodeId);
++
++          /* clear the done flags - so that it will be ignored if an event interrupt is generated */
++          txdRail->TxdMain->EnveEvent = EP3_EVENT_PRIVATE;
++          txdRail->TxdMain->DataEvent = EP3_EVENT_PRIVATE;
++          txdRail->TxdMain->DoneEvent = EP3_EVENT_PRIVATE;
++
++          /* reset all events, since non of them could have been set */
++          elan3_sdram_writel (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, EnveEvent.ev_Count), 0);                          /* PCI write */
++          elan3_sdram_writel (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent.ev_Count), 0);                          /* PCI write */
++          elan3_sdram_writel (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DoneEvent.ev_Count), 0);                          /* PCI write */
++
++          FreeTxdRail (xmtrRail, txdRail);
++          return 0;
++      }
++
++      InitialiseTxdRail (txd, txdRail, phase);
++
++      /* Initialise the dma descriptor */
++      dmabe.s.dma_type            = E3_DMA_TYPE (DMA_BYTE, DMA_WRITE, DMA_QUEUED, EP3_DMAFAILCOUNT);
++      dmabe.s.dma_size            = (EP_HAS_PAYLOAD(txd->Envelope.Attr) ? EP_INPUTQ_SIZE : EP_ENVELOPE_SIZE);
++      dmabe.s.dma_source          = txdRail->TxdElanAddr + offsetof (EP3_TXD_RAIL_ELAN, Envelope);
++      dmabe.s.dma_dest            = (E3_Addr) 0;
++      dmabe.s.dma_destEvent       = EP_MSGQ_ADDR(txd->Service);
++      dmabe.s.dma_destCookieVProc = EP_VP_DATA (txd->NodeId);
++      dmabe.s.dma_srcEvent        = txdRail->TxdElanAddr + offsetof (EP3_TXD_RAIL_ELAN, EnveEvent);
++      dmabe.s.dma_srcCookieVProc  = LocalCookie (rail, txd->NodeId);
++
++      EPRINTF8 (DBG_XMTR, "%s: TransmitTxdOnRail: txd=%p txdRail=%p @ %x XID=%llx dest=%u srcEvent=%x srcCookie=%x\n", rail->Generic.Name, 
++                txd, txdRail, txdRail->TxdElanAddr, (long long) txd->Envelope.Xid.Unique, txd->NodeId, dmabe.s.dma_srcEvent, dmabe.s.dma_srcCookieVProc);
++      
++      BindTxdToRail (txd, txdRail);
++      
++      if (IssueDma (rail, &dmabe, EP_RETRY_LOW_PRI, FALSE) != ISSUE_COMMAND_OK)
++          QueueDmaForRetry (rail, &dmabe, EP_RETRY_LOW_PRI);
++      break;
++
++    case EP_TXD_PHASE_PASSIVE:
++      InitialiseTxdRail (txd, txdRail, EP_TXD_PHASE_PASSIVE);                         /* initialise as passive (updated envelope) */
++      
++      EP_XMTR_OP (txd->TxdRail->XmtrRail, UnbindTxd) (txd, EP_TXD_PHASE_PASSIVE);     /* unbind from existing rail */
++
++      BindTxdToRail (txd, txdRail);                                                   /* and bind it to our new rail */
++      break;
++    }
++
++    return 1;
++}
++
++void
++ep3xmtr_unbind_txd (EP_TXD *txd, unsigned int phase)
++{
++    EP3_TXD_RAIL  *txdRail  = (EP3_TXD_RAIL *) txd->TxdRail;
++    EP3_XMTR_RAIL *xmtrRail = (EP3_XMTR_RAIL *) txdRail->Generic.XmtrRail;
++    EP3_RAIL      *rail     = XMTR_TO_RAIL (xmtrRail);
++
++    /* XXXX - TBD assertions on phase */
++
++    UnbindTxdFromRail (txd, txdRail);
++    
++    /* clear the done flags - so that it will be ignored if an event interrupt is generated */
++    txdRail->TxdMain->EnveEvent = EP3_EVENT_PRIVATE;
++    txdRail->TxdMain->DataEvent = EP3_EVENT_PRIVATE;
++    txdRail->TxdMain->DoneEvent = EP3_EVENT_PRIVATE;
++    
++    /* reset the envelope and data events, since only they could have been set */
++    elan3_sdram_writel (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, EnveEvent.ev_Count), 0);                                /* PCI write */
++    elan3_sdram_writel (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent.ev_Count), 0);                                /* PCI write */
++    elan3_sdram_writel (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DoneEvent.ev_Count), 0);                                /* PCI write */         
++    
++    FreeTxdRail (xmtrRail, txdRail);
++}
++
++long
++ep3xmtr_check (EP_XMTR_RAIL *x, long nextRunTime)
++{
++    EP3_XMTR_RAIL *xmtrRail = (EP3_XMTR_RAIL *) x;
++
++    if (xmtrRail->FreeDescCount < ep_txd_lowat && !AllocateTxdRailBlock(xmtrRail))
++    {
++      EPRINTF1 (DBG_RCVR,"%s: failed to grow txd rail pool\n", XMTR_TO_RAIL(xmtrRail)->Generic.Name);
++              
++      if (nextRunTime == 0 || AFTER (nextRunTime, lbolt + RESOURCE_RETRY_TIME))
++          nextRunTime = lbolt + RESOURCE_RETRY_TIME;
++    }
++    
++    return nextRunTime;
++}
++
++void
++ep3xmtr_add_rail (EP_XMTR *xmtr, EP_COMMS_RAIL *commsRail)
++{
++    EP3_XMTR_RAIL *xmtrRail;
++    unsigned long  flags;
++
++    KMEM_ZALLOC (xmtrRail, EP3_XMTR_RAIL *, sizeof (EP3_XMTR_RAIL), 1);
++
++    spin_lock_init (&xmtrRail->FreeDescLock);
++    kcondvar_init  (&xmtrRail->FreeDescSleep);
++    INIT_LIST_HEAD (&xmtrRail->FreeDescList);
++    INIT_LIST_HEAD (&xmtrRail->DescBlockList);
++
++    xmtrRail->Generic.CommsRail = commsRail;
++    xmtrRail->Generic.Xmtr      = xmtr;
++
++    spin_lock_irqsave (&xmtr->Lock, flags);
++
++    xmtr->Rails[commsRail->Rail->Number] = &xmtrRail->Generic;
++    xmtr->RailMask |= EP_RAIL2RAILMASK(commsRail->Rail->Number);
++
++    spin_unlock_irqrestore (&xmtr->Lock, flags);
++}
++
++void
++ep3xmtr_del_rail (EP_XMTR *xmtr, EP_COMMS_RAIL *commsRail)
++{
++    EP3_RAIL         *rail     = (EP3_RAIL *) commsRail->Rail;
++    EP3_XMTR_RAIL    *xmtrRail = (EP3_XMTR_RAIL *) xmtr->Rails[commsRail->Rail->Number];
++    unsigned long     flags;
++
++    /* rail mask set as not usable */
++    spin_lock_irqsave (&xmtr->Lock, flags);
++    xmtr->RailMask &= ~EP_RAIL2RAILMASK (rail->Generic.Number);
++    spin_unlock_irqrestore (&xmtr->Lock, flags);
++
++    /* wait for all txd's for this rail to become free */
++    spin_lock_irqsave (&xmtrRail->FreeDescLock, flags);
++    while (xmtrRail->FreeDescCount != xmtrRail->TotalDescCount)
++    {
++      xmtrRail->FreeDescWaiting++;
++      kcondvar_wait (&xmtrRail->FreeDescSleep, &xmtrRail->FreeDescLock, &flags);
++    }
++    spin_unlock_irqrestore (&xmtrRail->FreeDescLock, flags);
++
++    spin_lock_irqsave (&xmtr->Lock, flags);
++    xmtr->Rails[commsRail->Rail->Number] = NULL;
++    spin_unlock_irqrestore (&xmtr->Lock, flags);
++
++    /* need to free up the txd's and blocks */
++    /* all the txd's accociated with DescBlocks must be in the FreeDescList */
++    ASSERT (xmtrRail->TotalDescCount == xmtrRail->FreeDescCount);
++
++    /* run through the DescBlockList deleting them */
++    while (!list_empty (&xmtrRail->DescBlockList))
++      FreeTxdRailBlock (xmtrRail, list_entry(xmtrRail->DescBlockList.next, EP3_TXD_RAIL_BLOCK , Link));
++    
++    /* it had better be empty after that */
++    ASSERT ((xmtrRail->FreeDescCount == 0) && (xmtrRail->TotalDescCount == 0));
++
++    spin_lock_destroy (&xmtrRail->FreeDescLock);
++    kcondvar_destroy (&xmtrRail->FreeDescSleep);
++
++    KMEM_FREE (xmtrRail, sizeof (EP3_XMTR_RAIL));
++}
++
++void
++ep3xmtr_display_xmtr (DisplayInfo *di, EP_XMTR_RAIL *x)
++{
++    EP3_XMTR_RAIL *xmtrRail = (EP3_XMTR_RAIL *) x;
++    EP3_RAIL      *rail     = XMTR_TO_RAIL (xmtrRail);
++    struct list_head *el;
++    unsigned long flags;
++    int freeCount = 0;
++
++    spin_lock_irqsave (&xmtrRail->FreeDescLock, flags);
++    list_for_each (el, &xmtrRail->FreeDescList)
++      freeCount++;
++    spin_unlock_irqrestore (&xmtrRail->FreeDescLock, flags);
++
++    (di->func)(di->arg, "                 Rail=%d Free=%d Total=%d (%d)\n",
++              rail->Generic.Number, xmtrRail->FreeDescCount, xmtrRail->TotalDescCount, freeCount);
++}
++
++void
++ep3xmtr_display_txd (DisplayInfo *di, EP_TXD_RAIL *t)
++{
++    EP3_TXD_RAIL      *txdRail   = (EP3_TXD_RAIL *) t;
++    EP3_XMTR_RAIL     *xmtrRail  = (EP3_XMTR_RAIL *) txdRail->Generic.XmtrRail;
++    EP3_TXD_RAIL_MAIN *txdMain   = txdRail->TxdMain;
++    sdramaddr_t        txdElan   = txdRail->TxdElan;
++    EP3_RAIL          *rail      = (EP3_RAIL *) xmtrRail->Generic.CommsRail->Rail;
++    ELAN3_DEV         *dev       = rail->Device;
++    
++    (di->func)(di->arg, "     EnveEvent=%x DataEvent=%x DoneEvent=%x Rail=%s\n", 
++             txdMain->EnveEvent, txdMain->DataEvent, txdMain->DoneEvent, rail->Generic.Name);
++    (di->func)(di->arg, "     EnveEvent=%x.%x DataEvent=%x.%x DoneEvent=%x.%x\n",
++             elan3_sdram_readl (dev, txdElan + offsetof (EP3_TXD_RAIL_ELAN, EnveEvent.ev_Count)),
++             elan3_sdram_readl (dev, txdElan + offsetof (EP3_TXD_RAIL_ELAN, EnveEvent.ev_Type)),
++             elan3_sdram_readl (dev, txdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent.ev_Count)),
++             elan3_sdram_readl (dev, txdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent.ev_Type)),
++             elan3_sdram_readl (dev, txdElan + offsetof (EP3_TXD_RAIL_ELAN, DoneEvent.ev_Count)),
++             elan3_sdram_readl (dev, txdElan + offsetof (EP3_TXD_RAIL_ELAN, DoneEvent.ev_Type)));
++}
++
++int
++ep3xmtr_check_txd_state (EP_TXD *txd)
++{
++    EP3_TXD_RAIL  *txdRail   = (EP3_TXD_RAIL *) txd->TxdRail;
++    EP3_XMTR_RAIL *xmtrRail  = (EP3_XMTR_RAIL *) txdRail->Generic.XmtrRail;
++    EP3_RAIL      *rail      = XMTR_TO_RAIL (xmtrRail);
++    E3_Addr        enveEvent = txdRail->TxdElanAddr + offsetof (EP3_TXD_RAIL_ELAN, EnveEvent);
++    EP3_RETRY_DMA *retry = NULL;
++
++    struct list_head *el;
++    struct list_head *nel;
++    unsigned long     flags;
++
++    /*  is enevelope event is really not set */
++    if (EP3_EVENT_FIRED (txdRail->EnveCookie, txdRail->TxdMain->EnveEvent )) 
++      return (0);
++    
++    /* remove matching dma from stalled list */           
++    spin_lock_irqsave (&rail->DmaRetryLock, flags);
++    
++    list_for_each_safe(el, nel,  &rail->DmaRetries[EP_RETRY_STABALISING]) {
++      retry = list_entry (el, EP3_RETRY_DMA, Link);
++      
++      if ( retry->Dma.s.dma_srcEvent == enveEvent ) {
++          /* remove from retry list */
++          list_del (&retry->Link);
++          break; /* there can only be one */
++      } 
++    }
++    ASSERT ( retry != NULL); /* must find one in list */
++    ASSERT ( retry->Dma.s.dma_srcEvent == enveEvent ); /* better still be the right type then */    
++
++    /* add to free list */
++    list_add (&retry->Link, &rail->DmaRetryFreeList);
++
++    spin_unlock_irqrestore (&rail->DmaRetryLock, flags);    
++                      
++    UnbindTxdFromRail (txd, txdRail);
++      
++    /* clear the done flags - so that it will be ignored if an event interrupt is generated */
++    txdRail->TxdMain->EnveEvent = EP3_EVENT_PRIVATE;
++    txdRail->TxdMain->DataEvent = EP3_EVENT_PRIVATE;
++    txdRail->TxdMain->DoneEvent = EP3_EVENT_PRIVATE;
++    
++    /* reset the envelope and data events, since only they could have been set */
++    elan3_sdram_writel (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, EnveEvent.ev_Count), 0);                                /* PCI write */
++    elan3_sdram_writel (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent.ev_Count), 0);                                /* PCI write */
++    elan3_sdram_writel (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DoneEvent.ev_Count), 0);                                /* PCI write */         
++    
++    FreeTxdRail (xmtrRail, txdRail);
++
++    return (1);
++}
++
++void
++ep3xmtr_fillout_rail_stats(EP_XMTR_RAIL *xmtr_rail, char *str) {
++    /* no stats here yet */
++    /* EP3_XMTR_RAIL * ep3xmtr_rail = (EP3_XMTR_RAIL *) xmtr_rail; */
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/ep/epcommsTx_elan4.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/epcommsTx_elan4.c   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/epcommsTx_elan4.c        2005-06-01 23:12:54.659429984 -0400
+@@ -0,0 +1,1389 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: epcommsTx_elan4.c,v 1.26.2.4 2004/11/12 10:54:51 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/epcommsTx_elan4.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++#include <elan/epsvc.h>
++#include <elan/epcomms.h>
++
++#include "debug.h"
++#include "kcomm_vp.h"
++#include "kcomm_elan4.h"
++#include "epcomms_elan4.h"
++
++#include <elan4/trtype.h>
++
++#define XMTR_TO_COMMS(xmtrRail)               ((EP4_COMMS_RAIL *) ((EP_XMTR_RAIL *) xmtrRail)->CommsRail)
++#define XMTR_TO_RAIL(xmtrRail)                ((EP4_RAIL *) ((EP_XMTR_RAIL *) xmtrRail)->CommsRail->Rail)
++#define XMTR_TO_DEV(xmtrRail)         (XMTR_TO_RAIL(xmtrRail)->r_ctxt.ctxt_dev)
++#define XMTR_TO_SUBSYS(xmtrRail)      (((EP_XMTR_RAIL *) xmtrRail)->Xmtr->Subsys)
++
++#define TXD_TO_XMTR(txdRail)          ((EP4_XMTR_RAIL *) txdRail->txd_generic.XmtrRail)
++#define TXD_TO_RAIL(txdRail)          XMTR_TO_RAIL(TXD_TO_XMTR(txdRail))
++
++static void txd_interrupt (EP4_RAIL *rail, void *arg);
++static void poll_interrupt (EP4_RAIL *rail, void *arg);
++
++static __inline__ int
++on_list (struct list_head *ent, struct list_head *list)
++{
++    struct list_head *el;
++    unsigned int count = 0;
++    list_for_each (el, list) {
++      if (el == ent)
++          count++;
++    }
++    return count;
++}
++
++static __inline__ void
++__ep4_txd_assert_free (EP4_TXD_RAIL *txdRail, const char *file, const int line)
++{
++    EP4_XMTR_RAIL *xmtrRail = TXD_TO_XMTR (txdRail);
++    ELAN4_DEV     *dev      = XMTR_TO_DEV (xmtrRail);
++    register int   failed   = 0;
++    
++    if ((txdRail)->txd_retry_time     != 0)              failed |= (1 << 0);
++    if ((txdRail)->txd_main->txd_env  != EP4_STATE_FREE) failed |= (1 << 1);
++    if ((txdRail)->txd_main->txd_data != EP4_STATE_FREE) failed |= (1 << 2);
++    if ((txdRail)->txd_main->txd_done != EP4_STATE_FREE) failed |= (1 << 3);
++
++    if (sdram_assert)
++    {
++      if ((int)(elan4_sdram_readq (dev, (txdRail)->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_env.ev_CountAndType)) >> 32)  != -32) failed |= (1 << 4);
++      if ((int)(elan4_sdram_readq (dev, (txdRail)->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_data.ev_CountAndType)) >> 32) != 0)   failed |= (1 << 5);
++      if ((int)(elan4_sdram_readq (dev, (txdRail)->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_done.ev_CountAndType)) >> 32) != 0)   failed |= (1 << 6);
++    }
++
++    if (failed)
++    {
++      printk ("__ep4_txd_assert_free: failed=%x txdRail=%p at %s:%d\n", failed, txdRail, file, line);
++
++      ep_debugf (DBG_DEBUG, "__ep4_txd_assert_free: failed=%x txdRail=%p at %s:%d\n", failed, txdRail, file, line);
++      ep4xmtr_display_txd (&di_ep_debug, &txdRail->txd_generic);
++
++      (txdRail)->txd_retry_time     = 0;
++      (txdRail)->txd_main->txd_env  = EP4_STATE_FREE;
++      (txdRail)->txd_main->txd_data = EP4_STATE_FREE;
++      (txdRail)->txd_main->txd_done = EP4_STATE_FREE;
++
++      if (sdram_assert)
++      {
++          elan4_sdram_writel (dev, (txdRail)->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_env.ev_CountAndType)  + 4, -32);
++          elan4_sdram_writel (dev, (txdRail)->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_data.ev_CountAndType) + 4, 0);
++          elan4_sdram_writel (dev, (txdRail)->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_done.ev_CountAndType) + 4, 0);
++      }
++      EP_ASSFAIL (XMTR_TO_RAIL(xmtrRail), "__ep4_txd_assert_free");
++    }
++}
++
++static __inline__ void
++__ep4_txd_assert_finished (EP4_TXD_RAIL *txdRail, const char *file, const int line)
++{
++    EP4_XMTR_RAIL *xmtrRail = TXD_TO_XMTR (txdRail);
++    ELAN4_DEV     *dev      = XMTR_TO_DEV (xmtrRail);
++    register int   failed   = 0;
++    
++    if ((txdRail)->txd_retry_time     != 0)                  failed |= (1 << 0);
++    if ((txdRail)->txd_main->txd_env  != EP4_STATE_FINISHED) failed |= (1 << 1);
++    if ((txdRail)->txd_main->txd_data != EP4_STATE_FINISHED) failed |= (1 << 2);
++    if ((txdRail)->txd_main->txd_done != EP4_STATE_FINISHED) failed |= (1 << 3);
++    
++    if (sdram_assert)
++    {
++      if ((int)(elan4_sdram_readq (dev, (txdRail)->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_env.ev_CountAndType)) >> 32)  != -32) failed |= (1 << 4);
++      if ((int)(elan4_sdram_readq (dev, (txdRail)->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_data.ev_CountAndType)) >> 32) != 0)   failed |= (1 << 5);
++      if ((int)(elan4_sdram_readq (dev, (txdRail)->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_done.ev_CountAndType)) >> 32) != 0)   failed |= (1 << 6);
++    }
++
++    if (failed)
++    {
++      printk ("__ep4_txd_assert_finished: failed=%x txdRail=%p at %s:%d\n", failed, txdRail, file, line);
++
++      ep_debugf (DBG_DEBUG, "__ep4_txd_assert_finished: failed=%x txdRail=%p at %s:%d\n", failed, txdRail, file, line);
++      ep4xmtr_display_txd (&di_ep_debug, &txdRail->txd_generic);
++
++      (txdRail)->txd_retry_time     = 0;
++      (txdRail)->txd_main->txd_env  = EP4_STATE_FINISHED;
++      (txdRail)->txd_main->txd_data = EP4_STATE_FINISHED;
++      (txdRail)->txd_main->txd_done = EP4_STATE_FINISHED;
++
++      if (sdram_assert)
++      {
++          elan4_sdram_writel (dev, (txdRail)->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_env.ev_CountAndType)  + 4, -32);
++          elan4_sdram_writel (dev, (txdRail)->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_data.ev_CountAndType) + 4, 0);
++          elan4_sdram_writel (dev, (txdRail)->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_done.ev_CountAndType) + 4, 0);
++      }
++      EP_ASSFAIL (XMTR_TO_RAIL(xmtrRail), "__ep4_txd_assert_finished");
++    }
++}
++
++static __inline__ int
++__ep4_txd_assfail (EP4_TXD_RAIL *txdRail, const char *expr, const char *file, const int line)
++{
++    EP4_XMTR_RAIL *xmtrRail = TXD_TO_XMTR (txdRail);
++
++    printk ("__ep4_txd_assfail: %s:%d '%s'\n", file, line, expr);
++
++    ep_debugf (DBG_DEBUG, "__ep4_txd_assfail: %s:%d '%s'\n", file, line, expr);
++    ep4xmtr_display_txd (&di_ep_debug, &txdRail->txd_generic);
++
++    EP_ASSFAIL (XMTR_TO_RAIL (xmtrRail), "__ep4_txd_assfail");
++
++    return 0;
++}
++
++#define EP4_TXD_ASSERT(txdRail, EX)           ((void) ((EX) || (__ep4_txd_assfail(txdRail, #EX, __FILE__, __LINE__))))
++#define EP4_TXD_ASSERT_FREE(txdRail)          __ep4_txd_assert_free(txdRail, __FILE__, __LINE__)
++#define EP4_TXD_ASSERT_FINISHED(txdRail)      __ep4_txd_assert_finished(txdRail, __FILE__, __LINE__)
++
++static int
++alloc_txd_block (EP4_XMTR_RAIL *xmtrRail)
++{
++    EP4_RAIL           *rail = XMTR_TO_RAIL(xmtrRail);
++    ELAN4_DEV          *dev  = XMTR_TO_DEV(xmtrRail);
++    EP4_TXD_RAIL_BLOCK *blk;
++    EP4_TXD_RAIL_MAIN  *txdMain;
++    EP_ADDR           txdMainAddr;
++    sdramaddr_t               txdElan;
++    EP_ADDR           txdElanAddr;
++    EP4_TXD_RAIL       *txdRail;
++    unsigned long       flags;
++    int                 i;
++
++    KMEM_ZALLOC (blk, EP4_TXD_RAIL_BLOCK *, sizeof (EP4_TXD_RAIL_BLOCK), 1);
++
++    if (blk == NULL)
++      return 0;
++
++    if ((txdElan = ep_alloc_elan (&rail->r_generic, EP4_TXD_RAIL_ELAN_SIZE * EP4_NUM_TXD_PER_BLOCK, 0, &txdElanAddr)) == (sdramaddr_t) 0)
++    {
++      KMEM_FREE (blk, sizeof (EP4_TXD_RAIL_BLOCK));
++      return 0;
++    }
++
++    if ((txdMain = ep_alloc_main (&rail->r_generic, EP4_TXD_RAIL_MAIN_SIZE * EP4_NUM_TXD_PER_BLOCK, 0, &txdMainAddr)) == (EP4_TXD_RAIL_MAIN *) NULL)
++    {
++      ep_free_elan (&rail->r_generic, txdElanAddr, EP4_TXD_RAIL_ELAN_SIZE * EP4_NUM_TXD_PER_BLOCK);
++      KMEM_FREE (blk, sizeof (EP4_TXD_RAIL_BLOCK));
++      return 0;
++    }
++
++    if (ep4_reserve_dma_retries (rail, EP4_NUM_TXD_PER_BLOCK, 0) != 0)
++    {
++      ep_free_main (&rail->r_generic, blk->blk_txds[0].txd_main_addr, EP4_TXD_RAIL_MAIN_SIZE * EP4_NUM_TXD_PER_BLOCK);
++      ep_free_elan (&rail->r_generic, txdElanAddr, EP4_TXD_RAIL_ELAN_SIZE * EP4_NUM_TXD_PER_BLOCK);
++      KMEM_FREE (blk, sizeof (EP4_TXD_RAIL_BLOCK));
++      return 0;
++    }
++
++    for (txdRail = &blk->blk_txds[0], i = 0; i < EP4_NUM_TXD_PER_BLOCK; i++, txdRail++)
++    {
++      txdRail->txd_generic.XmtrRail = &xmtrRail->xmtr_generic;
++      txdRail->txd_elan             = txdElan;
++      txdRail->txd_elan_addr        = txdElanAddr;
++      txdRail->txd_main             = txdMain;
++      txdRail->txd_main_addr        = txdMainAddr;
++
++      /* We only need to reserve space for one command stream, since the sten packet
++       * can only be retrying *before* the dma source event is set.
++       * reserve bytes of "event" cq space for the completion write + interrupt */
++      if ((txdRail->txd_ecq = ep4_get_ecq (rail, EP4_ECQ_EVENT, EP4_INTR_CMD_NDWORDS)) == NULL)
++          goto failed;
++
++      /* register the main interrupt cookies */
++      ep4_register_intcookie (rail, &txdRail->txd_intcookie, txdElanAddr + offsetof (EP4_TXD_RAIL_ELAN, txd_done), txd_interrupt, txdRail);
++
++      /* initialise the events */
++      elan4_sdram_writeq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_env.ev_CountAndType),
++                          E4_EVENT_INIT_VALUE (-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++      elan4_sdram_writeq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_env.ev_CopySource),
++                          txdElanAddr + offsetof (EP4_TXD_RAIL_ELAN, txd_env_cmd));
++      elan4_sdram_writeq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_env.ev_CopyDest),
++                          txdRail->txd_ecq->ecq_addr);
++
++      elan4_sdram_writeq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_data.ev_CountAndType),
++                          E4_EVENT_INIT_VALUE (0, E4_EVENT_WRITE, E4_EVENT_DTYPE_LONG, 0));
++      elan4_sdram_writeq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_data.ev_WritePtr),
++                          txdMainAddr + offsetof (EP4_TXD_RAIL_MAIN, txd_data));
++      elan4_sdram_writeq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_data.ev_WriteValue),
++                          EP4_STATE_FINISHED);
++
++      elan4_sdram_writeq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_done.ev_CountAndType),
++                          E4_EVENT_INIT_VALUE (0, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++      elan4_sdram_writeq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_done.ev_CopySource),
++                          txdElanAddr + offsetof (EP4_TXD_RAIL_ELAN, txd_done_cmd));
++      elan4_sdram_writeq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_done.ev_CopyDest),
++                          txdRail->txd_ecq->ecq_addr);
++
++      /* Initialise the command streams */
++      elan4_sdram_writeq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_env_cmd.c_write_cmd),
++                          WRITE_DWORD_CMD | (txdMainAddr + offsetof (EP4_TXD_RAIL_MAIN, txd_env)));
++      elan4_sdram_writeq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_env_cmd.c_write_value),
++                          EP4_STATE_FAILED);
++      elan4_sdram_writeq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_env_cmd.c_intr_cmd),
++                          INTERRUPT_CMD | (txdRail->txd_intcookie.int_val << E4_MAIN_INT_SHIFT));
++
++      elan4_sdram_writeq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_done_cmd.c_write_cmd),
++                          WRITE_DWORD_CMD | (txdMainAddr + offsetof (EP4_TXD_RAIL_MAIN, txd_done)));
++      elan4_sdram_writeq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_done_cmd.c_write_value),
++                          EP4_STATE_FINISHED);
++      elan4_sdram_writeq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_done_cmd.c_intr_cmd),
++                          INTERRUPT_CMD | (txdRail->txd_intcookie.int_val << E4_MAIN_INT_SHIFT));
++
++      txdMain->txd_env  = EP4_STATE_FREE;
++      txdMain->txd_data = EP4_STATE_FREE;
++      txdMain->txd_done = EP4_STATE_FREE;
++
++      /* move onto next descriptor */
++      txdElan     += EP4_TXD_RAIL_ELAN_SIZE;
++      txdElanAddr += EP4_TXD_RAIL_ELAN_SIZE;
++      txdMain      = (EP4_TXD_RAIL_MAIN *) ((unsigned long) txdMain + EP4_TXD_RAIL_MAIN_SIZE);
++      txdMainAddr += EP4_TXD_RAIL_MAIN_SIZE;
++    }
++
++    spin_lock_irqsave (&xmtrRail->xmtr_freelock, flags);
++
++    list_add  (&blk->blk_link, &xmtrRail->xmtr_blocklist);
++
++    xmtrRail->xmtr_totalcount += EP4_NUM_TXD_PER_BLOCK;
++    xmtrRail->xmtr_freecount  += EP4_NUM_TXD_PER_BLOCK;
++
++    for (i = 0; i < EP4_NUM_TXD_PER_BLOCK; i++)
++      list_add (&blk->blk_txds[i].txd_generic.Link, &xmtrRail->xmtr_freelist);
++
++    spin_unlock_irqrestore (&xmtrRail->xmtr_freelock, flags);
++
++    return 1;
++
++ failed:
++    while (--i >= 0)
++    {
++      ep4_put_ecq (rail, txdRail->txd_ecq, EP4_INTR_CMD_NDWORDS);
++      ep4_deregister_intcookie (rail, &txdRail->txd_intcookie);
++    }
++    ep4_release_dma_retries (rail, EP4_NUM_TXD_PER_BLOCK);
++
++    ep_free_main (&rail->r_generic, blk->blk_txds[0].txd_main_addr, EP4_TXD_RAIL_MAIN_SIZE * EP4_NUM_TXD_PER_BLOCK);
++    ep_free_elan (&rail->r_generic, blk->blk_txds[0].txd_elan_addr, EP4_TXD_RAIL_ELAN_SIZE * EP4_NUM_TXD_PER_BLOCK);
++
++    KMEM_FREE (blk, sizeof (EP4_TXD_RAIL_BLOCK));
++
++    return 0;
++}
++
++static void
++free_txd_block (EP4_XMTR_RAIL *xmtrRail, EP4_TXD_RAIL_BLOCK *blk)
++{
++    EP4_RAIL     *rail = XMTR_TO_RAIL (xmtrRail);
++    EP4_TXD_RAIL *txdRail;
++    unsigned long flags;
++    int           i;
++
++    spin_lock_irqsave (&xmtrRail->xmtr_freelock, flags);
++
++    list_del (&blk->blk_link);
++
++    xmtrRail->xmtr_totalcount -= EP4_NUM_TXD_PER_BLOCK;
++
++    for (txdRail = &blk->blk_txds[0], i = 0; i < EP4_NUM_TXD_PER_BLOCK; i++, txdRail++)
++    {
++      xmtrRail->xmtr_freecount--;
++
++      ep4_put_ecq (rail, txdRail->txd_ecq, EP4_INTR_CMD_NDWORDS);
++
++      ep4_deregister_intcookie (rail, &txdRail->txd_intcookie);
++
++      list_del (&txdRail->txd_generic.Link);
++    }
++    spin_unlock_irqrestore (&xmtrRail->xmtr_freelock, flags);
++
++    ep4_release_dma_retries (rail, EP4_NUM_TXD_PER_BLOCK);
++
++    ep_free_main (&rail->r_generic, blk->blk_txds[0].txd_main_addr, EP4_TXD_RAIL_MAIN_SIZE * EP4_NUM_TXD_PER_BLOCK);
++    ep_free_elan (&rail->r_generic, blk->blk_txds[0].txd_elan_addr, EP4_TXD_RAIL_ELAN_SIZE * EP4_NUM_TXD_PER_BLOCK);
++
++    KMEM_FREE (blk, sizeof (EP4_TXD_RAIL_BLOCK));
++}
++
++static EP4_TXD_RAIL *
++get_txd_rail (EP4_XMTR_RAIL *xmtrRail)
++{
++    EP_COMMS_SUBSYS  *subsys = XMTR_TO_SUBSYS(xmtrRail);
++    EP4_TXD_RAIL     *txdRail;
++    unsigned long flags;
++    int low_on_txds;
++
++    spin_lock_irqsave (&xmtrRail->xmtr_freelock, flags);
++
++    if (list_empty (&xmtrRail->xmtr_freelist))
++      txdRail = NULL;
++    else
++    {
++      txdRail = list_entry (xmtrRail->xmtr_freelist.next, EP4_TXD_RAIL, txd_generic.Link);
++
++      EP4_TXD_ASSERT_FREE(txdRail);
++
++      list_del (&txdRail->txd_generic.Link);
++
++      xmtrRail->xmtr_freecount--;
++    }
++    /* Wakeup the descriptor primer thread if there's not many left */
++    low_on_txds = (xmtrRail->xmtr_freecount < ep_txd_lowat);
++
++    spin_unlock_irqrestore (&xmtrRail->xmtr_freelock, flags);
++
++    if (low_on_txds)
++      ep_kthread_schedule (&subsys->Thread, lbolt);
++
++
++    return (txdRail);
++}
++
++static void
++free_txd_rail (EP4_XMTR_RAIL *xmtrRail, EP4_TXD_RAIL *txdRail)
++{
++    unsigned long flags;
++
++    EP4_TXD_ASSERT_FREE(txdRail);
++
++    spin_lock_irqsave (&xmtrRail->xmtr_freelock, flags);
++    
++    list_add (&txdRail->txd_generic.Link, &xmtrRail->xmtr_freelist);
++
++    xmtrRail->xmtr_freecount++;
++
++    if (xmtrRail->xmtr_freewaiting)
++    {
++      xmtrRail->xmtr_freewaiting--;
++      kcondvar_wakeupall (&xmtrRail->xmtr_freesleep, &xmtrRail->xmtr_freelock);
++    }
++
++    spin_unlock_irqrestore (&xmtrRail->xmtr_freelock, flags);
++}
++
++static void
++bind_txd_rail (EP_TXD *txd, EP4_TXD_RAIL *txdRail)
++{
++    EPRINTF6 (DBG_XMTR, "%s: bind_txd_rail: txd=%p txdRail=%p XID=%08x.%08x.%016llx\n", 
++            XMTR_TO_RAIL(txdRail->txd_generic.XmtrRail)->r_generic.Name, txd, txdRail, 
++            txd->Envelope.Xid.Generation, txd->Envelope.Xid.Handle, txd->Envelope.Xid.Unique);
++
++    txd->TxdRail = &txdRail->txd_generic;
++    txdRail->txd_generic.Txd = txd;
++}
++
++static void
++unbind_txd_rail (EP_TXD *txd, EP4_TXD_RAIL *txdRail)
++{
++    EP4_TXD_ASSERT (txdRail, txd->TxdRail == &txdRail->txd_generic && txdRail->txd_generic.Txd == txd);
++
++    EPRINTF6 (DBG_XMTR, "%s: unbind_txd_rail: txd=%p txdRail=%p XID=%08x.%08x.%016llx\n", 
++            XMTR_TO_RAIL(txdRail->txd_generic.XmtrRail)->r_generic.Name, txd, txdRail, 
++            txd->Envelope.Xid.Generation, txd->Envelope.Xid.Handle, txd->Envelope.Xid.Unique);
++
++
++    txdRail->txd_generic.Txd = NULL; 
++    txd->TxdRail = NULL;
++}
++
++static void
++initialise_txd (EP_TXD *txd, EP4_TXD_RAIL *txdRail, unsigned int phase)
++{
++    EP4_XMTR_RAIL *xmtrRail = (EP4_XMTR_RAIL *) txdRail->txd_generic.XmtrRail;
++    EP4_RAIL      *rail     = XMTR_TO_RAIL (xmtrRail);
++    ELAN4_DEV     *dev      = rail->r_ctxt.ctxt_dev;
++    
++    /* Flush the Elan TLB if mappings have changed */
++    ep_perrail_dvma_sync (&rail->r_generic);
++    
++    /* Initialise the per-rail fields in the envelope */
++    txd->Envelope.TxdRail = txdRail->txd_elan_addr;
++    txd->Envelope.NodeId  = rail->r_generic.Position.pos_nodeid;
++
++    /* Allocate a network error fixup cookie */
++    txdRail->txd_cookie = ep4_neterr_cookie (rail, txd->NodeId) | EP4_COOKIE_STEN;
++
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++    if ( epdebug_check_sum ) 
++      txd->Envelope.CheckSum = ep_calc_check_sum( txd->Xmtr->Subsys->Subsys.Sys, &txd->Envelope, txd->Envelope.Frags, txd->Envelope.nFrags);
++    else
++#endif
++      txd->Envelope.CheckSum = 0;  
++
++    /* Initialise the per-rail events */
++    switch (phase)
++    {
++    case EP_TXD_PHASE_ACTIVE:
++    {
++      unsigned int nsets = (txd->Envelope.nFrags ? txd->Envelope.nFrags : 1) + ( EP_IS_MULTICAST(txd->Envelope.Attr) ? 1 : 0);
++
++      if (! EP_IS_RPC(txd->Envelope.Attr))
++      {
++          elan4_sdram_writeq (dev, txdRail->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_done.ev_CountAndType),
++                              E4_EVENT_INIT_VALUE (-32 * nsets, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++
++          txdRail->txd_main->txd_data = EP4_STATE_FINISHED;
++      }
++      else
++      {
++          elan4_sdram_writeq (dev, txdRail->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_data.ev_CountAndType),
++                              E4_EVENT_INIT_VALUE(-32 * nsets , E4_EVENT_WRITE, E4_EVENT_DTYPE_LONG, 0));
++          elan4_sdram_writeq (dev, txdRail->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_done.ev_CountAndType),
++                              E4_EVENT_INIT_VALUE (-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++
++          txdRail->txd_main->txd_data = EP4_STATE_ACTIVE;
++      }
++                 
++      txdRail->txd_main->txd_env  = EP4_STATE_ACTIVE;
++      txdRail->txd_main->txd_done = EP4_STATE_ACTIVE;
++      break;
++    }
++
++    case EP_TXD_PHASE_PASSIVE:
++      EP4_TXD_ASSERT (txdRail, EP_IS_RPC(txd->Envelope.Attr));
++      
++      txdRail->txd_main->txd_env  = EP4_STATE_FINISHED;
++      txdRail->txd_main->txd_data = EP4_STATE_FINISHED;
++      txdRail->txd_main->txd_done = EP4_STATE_ACTIVE;
++
++      elan4_sdram_writeq (dev, txdRail->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_done.ev_CountAndType),
++                          E4_EVENT_INIT_VALUE (-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++      break;
++    }
++
++   if (EP_IS_NO_INTERRUPT(txd->Envelope.Attr))
++      elan4_sdram_writeq (dev, txdRail->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_done_cmd.c_intr_cmd), NOP_CMD);
++}
++
++static void
++terminate_txd_rail (EP4_XMTR_RAIL *xmtrRail, EP4_TXD_RAIL *txdRail)
++{
++    EP4_SDRAM_ASSERT (TXD_TO_RAIL(txdRail),\
++                    (txdRail)->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_env.ev_CountAndType),\
++                    E4_EVENT_INIT_VALUE (-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));\
++
++    /* clear the done flags - so that it will be ignored if an event interrupt is generated */
++    txdRail->txd_main->txd_env  = EP4_STATE_FREE;
++    txdRail->txd_main->txd_data = EP4_STATE_FREE;
++    txdRail->txd_main->txd_done = EP4_STATE_FREE;
++
++#if defined(DEBUG_ASSERT)
++    if (sdram_assert)
++    {
++      ELAN4_DEV *dev = XMTR_TO_RAIL (xmtrRail)->r_ctxt.ctxt_dev;
++
++      elan4_sdram_writeq (dev, txdRail->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_data.ev_CountAndType),
++                          E4_EVENT_INIT_VALUE (0, E4_EVENT_WRITE, E4_EVENT_DTYPE_LONG, 0));
++      elan4_sdram_writeq (dev, txdRail->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_done.ev_CountAndType),
++                          E4_EVENT_INIT_VALUE (0, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++    }
++#endif 
++}
++
++static void
++defer_txd_rail (EP4_TXD_RAIL *txdRail)
++{
++    EP4_XMTR_RAIL   *xmtrRail = TXD_TO_XMTR(txdRail);
++    EP4_RAIL        *rail     = XMTR_TO_RAIL(xmtrRail);
++    ELAN4_DEV       *dev      = rail->r_ctxt.ctxt_dev;
++    EP_COMMS_SUBSYS *subsys   = XMTR_TO_SUBSYS(xmtrRail);
++
++    EPRINTF5 (DBG_XMTR, "%s: defer_txd_rail: xmtrRail=%p txdRail=%p env/data (%d,%d) not finished\n",
++            rail->r_generic.Name, xmtrRail, txdRail, (int)txdRail->txd_main->txd_env, (int)txdRail->txd_main->txd_data);
++                  
++    /* transmit has completed, but the data dma has not completed
++     * (because of network error fixup), we queue the txdRail onto a list
++     * to be polled for completion later.
++     */
++    if (txdRail->txd_retry_time)
++    {
++      EP4_TXD_ASSERT (txdRail, (on_list (&txdRail->txd_retry_link, &xmtrRail->xmtr_retrylist[EP4_TXD_LIST_RETRY]) == 1 ||
++                                on_list (&txdRail->txd_retry_link, &xmtrRail->xmtr_retrylist[EP4_TXD_LIST_STALLED]) == 1));
++
++      list_del (&txdRail->txd_retry_link);
++
++      txdRail->txd_main->txd_env = EP4_STATE_FINISHED;
++
++      /* re-initialise the envelope event */
++      elan4_sdram_writeq (dev, txdRail->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_env.ev_CountAndType),
++                          E4_EVENT_INIT_VALUE (-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++    }
++    
++    txdRail->txd_retry_time = lbolt;
++      
++    list_add_tail (&txdRail->txd_retry_link, &xmtrRail->xmtr_retrylist[EP4_TXD_LIST_POLL]);
++      
++    ep_kthread_schedule (&subsys->Thread, lbolt);
++}
++
++static void
++finalise_txd (EP_TXD *txd, EP4_TXD_RAIL *txdRail)
++{
++    EP4_XMTR_RAIL   *xmtrRail = TXD_TO_XMTR(txdRail);
++
++    EP4_TXD_ASSERT_FINISHED (txdRail);
++
++    unbind_txd_rail (txd, txdRail);
++    
++    terminate_txd_rail (xmtrRail, txdRail);
++    free_txd_rail (xmtrRail, txdRail);
++}
++
++static void
++txd_interrupt (EP4_RAIL *rail, void *arg)
++{
++    EP4_TXD_RAIL    *txdRail  = (EP4_TXD_RAIL *) arg;
++    EP4_XMTR_RAIL   *xmtrRail = TXD_TO_XMTR(txdRail);
++    EP_XMTR         *xmtr     = xmtrRail->xmtr_generic.Xmtr;
++    int              delay    = 1;
++    EP_TXD          *txd;
++    unsigned long    flags;
++
++    spin_lock_irqsave (&xmtr->Lock, flags);
++    for (;;)
++    {
++      if (txdRail->txd_main->txd_done == EP4_STATE_FINISHED || txdRail->txd_main->txd_env == EP4_STATE_FAILED)
++          break;
++      
++      /* The write to txd_done could be held up in the PCI bridge even though
++       * we've seen the interrupt cookie.  Unlike elan3, there is no possibility
++       * of spurious interrupts since we flush the command queues on node 
++       * disconnection and the txcallback mechanism */
++      mb();
++
++      if (delay > EP4_EVENT_FIRING_TLIMIT)
++      {
++          spin_unlock_irqrestore (&xmtr->Lock, flags);
++
++          EP_ASSFAIL (XMTR_TO_RAIL(xmtrRail), "txd_interrupt - not finished\n");
++          return;
++      }
++      DELAY (delay);
++      delay <<= 1;
++    }
++
++    txd = txdRail->txd_generic.Txd;
++
++    if (txdRail->txd_main->txd_env == EP4_STATE_FAILED)
++    {
++      spin_lock (&xmtrRail->xmtr_retrylock);
++
++      EP4_TXD_ASSERT (txdRail, txdRail->txd_retry_time == 0);                         /* cannot be on retry/poll list */
++      EP4_TXD_ASSERT (txdRail, txdRail->txd_main->txd_done != EP4_STATE_FINISHED);    /* data xfer cannot have finished */
++
++      if (TxdShouldStabalise (&txdRail->txd_generic, &rail->r_generic))
++      {
++          EPRINTF6 (DBG_STABILISE, "%s: txd_interrupt: stablise xmtrRail=%p txdRail=%p txd=%p XID=%llx dest=%u\n", rail->r_generic.Name,
++                    xmtrRail, txdRail, txd, txd->Envelope.Xid.Unique, txd->NodeId);
++
++          txdRail->txd_retry_time = lbolt;                    /* indicate on retry list */
++          
++          list_add_tail (&txdRail->txd_retry_link, &xmtrRail->xmtr_retrylist[EP4_TXD_LIST_STALLED]);
++      }
++      else
++      {
++          EPRINTF6 (DBG_RETRY, "%s: txd_interrupt: retry xmtrRail=%p txdRail=%p txd=%p XID=%llx dest=%u\n", rail->r_generic.Name,
++                    xmtrRail, txdRail, txd, txd->Envelope.Xid.Unique, txd->NodeId);
++
++          txdRail->txd_retry_time = lbolt + EP_RETRY_LOW_PRI_TIME;            /* XXXX: backoff ? */
++          
++          list_add_tail (&txdRail->txd_retry_link, &xmtrRail->xmtr_retrylist[EP4_TXD_LIST_RETRY]);
++          
++          ep_kthread_schedule (&rail->r_retry_thread, txdRail->txd_retry_time);
++      }
++      spin_unlock (&xmtrRail->xmtr_retrylock);
++
++      spin_unlock_irqrestore (&xmtr->Lock, flags);
++      return;
++    }
++
++    EP4_TXD_ASSERT (txdRail, txd != NULL && !(EP_IS_NO_INTERRUPT(txd->Envelope.Attr)));
++
++    EPRINTF6 (DBG_XMTR, "%s: txd_interrupt: xmtrRail=%p txdRail=%p txd=%p XID=%llx dest=%u\n", rail->r_generic.Name,
++            xmtrRail, txdRail, txd, txd->Envelope.Xid.Unique, txd->NodeId);
++           
++    if (txdRail->txd_main->txd_env != EP4_STATE_FINISHED || txdRail->txd_main->txd_data != EP4_STATE_FINISHED)
++    {
++      defer_txd_rail (txdRail);
++
++      spin_unlock_irqrestore (&xmtr->Lock, flags);
++    }
++    else
++    {
++      /* remove from active transmit list */
++      list_del (&txd->Link);
++
++      ep_xmtr_txd_stat(xmtr,txd);
++
++      finalise_txd (txd, txdRail);
++      
++      spin_unlock_irqrestore (&xmtr->Lock, flags);
++      
++      txd->Handler (txd, txd->Arg, EP_SUCCESS);
++      
++      FreeTxd (xmtr, txd);
++    }
++}
++
++static void
++poll_interrupt (EP4_RAIL *rail, void *arg)
++{
++    EP4_XMTR_RAIL   *xmtrRail = (EP4_XMTR_RAIL *) arg;
++
++    ep_poll_transmits (xmtrRail->xmtr_generic.Xmtr);
++}
++
++void
++issue_envelope_packet (EP4_XMTR_RAIL *xmtrRail, EP4_TXD_RAIL *txdRail)
++{
++    EP_TXD    *txd    = txdRail->txd_generic.Txd;
++    ELAN4_CQ  *cq     = xmtrRail->xmtr_cq;
++    E4_uint64 *blk0   = (E4_uint64 *) &txd->Envelope;
++    E4_uint64 *blk1   = EP_HAS_PAYLOAD(txd->Envelope.Attr) ? (E4_uint64 *) &txd->Payload : NULL;
++    E4_Addr    qaddr  = EP_MSGQ_ADDR(txd->Service);
++
++    EP4_SDRAM_ASSERT (TXD_TO_RAIL(txdRail),\
++                    (txdRail)->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_env.ev_CountAndType),\
++                    E4_EVENT_INIT_VALUE (-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));\
++
++    elan4_open_packet (cq, OPEN_PACKET (0, PACK_OK | RESTART_COUNT_ZERO, EP_VP_DATA(txd->NodeId)));
++    elan4_sendtrans0 (cq, TR_INPUT_Q_GETINDEX, EP_MSGQ_ADDR(txd->Service));
++          
++    /* send the payload if present */
++    if (blk0) elan4_sendtransp (cq, TR_WRITE(128 >> 3, 0, TR_DATATYPE_BYTE), 0,   blk0);
++    if (blk1) elan4_sendtransp (cq, TR_WRITE(128 >> 3, 0, TR_DATATYPE_BYTE), 128, blk1);
++
++    elan4_sendtrans1 (cq, TR_INPUT_Q_COMMIT, qaddr, txdRail->txd_cookie);
++
++    elan4_guard (cq, GUARD_CHANNEL (1) | GUARD_TEST(0, PACK_OK) | GUARD_RESET (EP4_STEN_RETRYCOUNT));
++    elan4_write_dword_cmd (cq, txdRail->txd_main_addr + offsetof (EP4_TXD_RAIL_MAIN, txd_env), EP4_STATE_FINISHED);
++          
++    elan4_guard (cq, GUARD_CHANNEL (1) | GUARD_TEST(0, RESTART_COUNT_ZERO) | GUARD_RESET (EP4_STEN_RETRYCOUNT));
++    elan4_set_event_cmd (cq, txdRail->txd_elan_addr + offsetof (EP4_TXD_RAIL_ELAN, txd_env));
++    
++    elan4_write_dword_cmd (cq, xmtrRail->xmtr_main_addr + offsetof (EP4_XMTR_RAIL_MAIN, xmtr_flowcnt), ++xmtrRail->xmtr_flowcnt);
++}
++
++void
++ep4xmtr_flush_callback (EP_XMTR *xmtr, EP4_XMTR_RAIL *xmtrRail)
++{
++    EP4_RAIL       *rail      = XMTR_TO_RAIL (xmtrRail);
++    EP4_COMMS_RAIL *commsRail = XMTR_TO_COMMS (xmtrRail);
++    struct list_head *el, *nel;
++    unsigned long flags;
++
++    switch (rail->r_generic.CallbackStep)
++    {
++    case EP_CB_FLUSH_FILTERING:
++      /* need to acquire/release the Lock to ensure that the node state
++       * transition has been noticed and no new envelopes are queued to 
++       * nodes which are passivating. */
++      spin_lock_irqsave (&xmtr->Lock, flags);
++
++      /* Then we insert a "setevent" into the command queue to flush
++       * through the envelopes which have already been submitted */
++      ep4comms_flush_setevent (commsRail, xmtrRail->xmtr_cq);
++
++      spin_unlock_irqrestore (&xmtr->Lock, flags);
++
++      break;
++
++    case EP_CB_FLUSH_FLUSHING:
++      /* remove any envelopes which are retrying to nodes which are going down */
++      spin_lock_irqsave (&xmtrRail->xmtr_retrylock, flags);
++      list_for_each_safe (el, nel, &xmtrRail->xmtr_retrylist[EP4_TXD_LIST_RETRY]) {
++          EP4_TXD_RAIL *txdRail  = list_entry (el, EP4_TXD_RAIL, txd_retry_link);
++          EP_TXD       *txd      = txdRail->txd_generic.Txd;
++          EP_NODE_RAIL *nodeRail = &rail->r_generic.Nodes[txd->NodeId];
++          
++          EP4_TXD_ASSERT (txdRail, txdRail->txd_main->txd_env == EP4_STATE_FAILED);
++          
++          if (nodeRail->State == EP_NODE_LOCAL_PASSIVATE)
++          {
++              EPRINTF2 (DBG_XMTR, "%s; ep4xmtr_flush_callback: removing txdRail %p from retry list\n", rail->r_generic.Name, txdRail);
++              
++              EP4_TXD_ASSERT (txdRail, txdRail->txd_retry_time != 0);
++
++              list_del (&txdRail->txd_retry_link);
++              list_add_tail (&txdRail->txd_retry_link, &xmtrRail->xmtr_retrylist[EP4_TXD_LIST_STALLED]);
++          }
++      }
++      spin_unlock_irqrestore (&xmtrRail->xmtr_retrylock, flags);
++
++      /* Determine whether we have active or passive messages to 
++       * any node which is passivating */
++      spin_lock_irqsave (&xmtr->Lock, flags);
++      list_for_each (el, &xmtr->ActiveDescList) {
++          EP_TXD       *txd      = list_entry (el, EP_TXD, Link);
++          EP4_TXD_RAIL *txdRail  = (EP4_TXD_RAIL *) txd->TxdRail;
++          EP_NODE_RAIL *nodeRail = &rail->r_generic.Nodes[txd->NodeId];
++          
++          if (txdRail == NULL || txdRail->txd_generic.XmtrRail != &xmtrRail->xmtr_generic || nodeRail->State != EP_NODE_LOCAL_PASSIVATE)
++              continue;
++          
++          EPRINTF5 (DBG_XMTR, "%s: flush txd=%p txdRail=%p data=%llx done=%llx\n", rail->r_generic.Name,
++                    txd, txdRail, txdRail->txd_main->txd_data, txdRail->txd_main->txd_done);
++
++          if (EP_IS_RPC(txd->Envelope.Attr))
++          {
++              if (txdRail->txd_main->txd_data == EP4_STATE_ACTIVE)
++                  nodeRail->MessageState |= EP_NODE_ACTIVE_MESSAGES;
++              else if (txdRail->txd_main->txd_data == EP4_STATE_ACTIVE)
++                  nodeRail->MessageState |= EP_NODE_PASSIVE_MESSAGES;
++          }
++          else
++          {
++              if (txdRail->txd_main->txd_data == EP4_STATE_ACTIVE)
++                  nodeRail->MessageState |= EP_NODE_ACTIVE_MESSAGES;
++          }
++      }
++      spin_unlock_irqrestore (&xmtr->Lock, flags);
++      break;
++
++    default:
++      panic ("ep4xmtr_flush_callback: invalid callback step\n");
++      break;
++    }
++}
++
++void
++ep4xmtr_failover_callback (EP_XMTR *xmtr, EP4_XMTR_RAIL *xmtrRail)
++{
++    EP4_RAIL         *rail   = XMTR_TO_RAIL (xmtrRail);
++    struct list_head  txdList;
++    struct list_head *el, *nel;
++    unsigned long flags;
++
++    INIT_LIST_HEAD (&txdList);
++
++    spin_lock_irqsave (&xmtr->Lock, flags);
++    list_for_each_safe (el, nel, &xmtr->ActiveDescList) {
++      EP_TXD       *txd       = list_entry (el, EP_TXD, Link);
++      EP4_TXD_RAIL *txdRail   = (EP4_TXD_RAIL *) txd->TxdRail;
++      EP_NODE_RAIL *nodeRail = &rail->r_generic.Nodes[txd->NodeId];
++          
++      /* Only progress relocation of txd's bound to this rail */
++      if (! TXD_BOUND2RAIL (txdRail, xmtrRail) || nodeRail->State != EP_NODE_PASSIVATED)
++          continue;
++      
++      /* XXXX - no rail failover for now ....*/
++
++      EPRINTF4 (DBG_XMTR, "%s: ep4xmtr_failover_callback - xmtr %p txd %p node %d completed\n", rail->r_generic.Name, xmtr, txd, txd->NodeId);
++    }
++    spin_unlock_irqrestore (&xmtr->Lock, flags);
++
++    while (! list_empty (&txdList)) 
++    {
++      EP_TXD *txd = list_entry (txdList.next, EP_TXD, Link);
++
++      list_del (&txd->Link);
++
++      txd->Handler (txd, txd->Arg, EP_CONN_RESET);
++      
++      FreeTxd (xmtr, txd);
++    }
++}
++
++
++void
++ep4xmtr_disconnect_callback (EP_XMTR *xmtr, EP4_XMTR_RAIL *xmtrRail)
++{
++    EP4_RAIL         *rail = XMTR_TO_RAIL (xmtrRail);
++    ELAN4_DEV        *dev  = rail->r_ctxt.ctxt_dev;
++    struct list_head *el, *nel;
++    struct list_head  txdList;
++    unsigned long flags;
++    
++    INIT_LIST_HEAD (&txdList);
++
++    spin_lock_irqsave (&xmtr->Lock, flags);
++
++    list_for_each_safe (el, nel, &xmtr->ActiveDescList) {
++      EP_TXD       *txd       = list_entry (el, EP_TXD, Link);
++      EP4_TXD_RAIL *txdRail   = (EP4_TXD_RAIL *) txd->TxdRail;
++      EP_NODE_RAIL *nodeRail = &rail->r_generic.Nodes[txd->NodeId];
++          
++      if ( ! TXD_BOUND2RAIL (txdRail, xmtrRail) || nodeRail->State != EP_NODE_DISCONNECTING)
++          continue;
++      
++      if (txdRail->txd_main->txd_done == EP4_STATE_ACTIVE)
++      {
++
++          EPRINTF8 (DBG_DISCON, "ep4xmtr_disconnect_callback: txdRail=%p : events %llx,%llx,%llx done %llx,%llx,%llx retry %lx\n",txdRail,
++                    elan4_sdram_readq (dev, txdRail->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_env.ev_CountAndType)),
++                    elan4_sdram_readq (dev, txdRail->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_data.ev_CountAndType)),
++                    elan4_sdram_readq (dev, txdRail->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_done.ev_CountAndType)),
++                    txdRail->txd_main->txd_env, txdRail->txd_main->txd_data, txdRail->txd_main->txd_done,
++                    txdRail->txd_retry_time);
++                     
++          if (txdRail->txd_retry_time)
++          {
++              /* re-initialise the envelope event */
++              elan4_sdram_writeq (dev, txdRail->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_env.ev_CountAndType),
++                                  E4_EVENT_INIT_VALUE (-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++              
++              EP4_TXD_ASSERT (txdRail, on_list (&txdRail->txd_retry_link, &xmtrRail->xmtr_retrylist[EP4_TXD_LIST_STALLED]) == 1);
++
++              txdRail->txd_retry_time  = 0;
++
++              list_del (&txdRail->txd_retry_link);
++          }
++
++          /* Remove from active list */
++          list_del (&txd->Link);
++      
++          unbind_txd_rail (txd, txdRail);
++
++          terminate_txd_rail (xmtrRail, txdRail);
++          free_txd_rail (xmtrRail, txdRail);
++          
++          EPRINTF4 (DBG_XMTR, "%s: ep4xmtr_disconnect_callback - xmtr %p txd %p node %d not conected\n", rail->r_generic.Name, xmtr, txd, txd->NodeId);
++
++          /* add to the list of txd's which are to be completed */
++          list_add_tail (&txd->Link, &txdList);
++      }
++    }
++    spin_unlock_irqrestore (&xmtr->Lock, flags);
++    
++    while (! list_empty (&txdList)) 
++    {
++      EP_TXD *txd = list_entry (txdList.next, EP_TXD, Link);
++
++      list_del (&txd->Link);
++
++      txd->Handler (txd, txd->Arg, EP_CONN_RESET);
++      
++      FreeTxd (xmtr, txd);
++    }
++}
++
++void
++ep4xmtr_neterr_flush (EP_XMTR *xmtr, EP4_XMTR_RAIL *xmtrRail, unsigned int nodeId, EP_NETERR_COOKIE *cookies)
++{
++    EP4_COMMS_RAIL   *commsRail = XMTR_TO_COMMS (xmtrRail);
++    unsigned long flags;
++    
++    spin_lock_irqsave (&xmtr->Lock, flags);
++
++    /* insert a "setevent" into the command queue to flush
++     * through the envelopes which have already been submitted */
++    ep4comms_flush_setevent (commsRail, xmtrRail->xmtr_cq);
++
++    spin_unlock_irqrestore (&xmtr->Lock, flags);
++}
++
++void
++ep4xmtr_neterr_check (EP_XMTR *xmtr, EP4_XMTR_RAIL *xmtrRail, unsigned int nodeId, EP_NETERR_COOKIE *cookies)
++{
++    EP4_RAIL *rail = XMTR_TO_RAIL (xmtrRail);
++    ELAN4_DEV *dev = rail->r_ctxt.ctxt_dev;
++    struct list_head *el;
++    unsigned long flags;
++    
++    spin_lock_irqsave (&xmtr->Lock, flags);
++    list_for_each (el, &xmtr->ActiveDescList) {
++      EP_TXD       *txd     = list_entry (el, EP_TXD, Link);
++      EP4_TXD_RAIL *txdRail = (EP4_TXD_RAIL *) txd->TxdRail;
++          
++      if ( ! TXD_BOUND2RAIL (txdRail, xmtrRail) || txd->NodeId != nodeId)
++          continue;
++      
++      /* The only non-dma associated with a txd is the initial sten packet, if it has been acked 
++       * and the neterr cookie matches, then change it to look like it's been acked since the
++       * INPUT_Q_COMMIT transaction has already been executed */
++      if (txdRail->txd_main->txd_env == EP4_STATE_FAILED && (txdRail->txd_cookie == cookies[0] || txdRail->txd_cookie == cookies[1]))
++      {
++          EPRINTF4 (DBG_NETWORK_ERROR, "%s: ep4xmtr_neterr_callback: cookie <%lld%s%s%s%s> matches txd %p txdRail %p\n", 
++                   rail->r_generic.Name, EP4_COOKIE_STRING(txdRail->txd_cookie), txd, txdRail);
++
++          EP4_TXD_ASSERT (txdRail, txdRail->txd_retry_time != 0);
++
++          txdRail->txd_main->txd_env = EP4_STATE_FINISHED;
++
++          /* re-initialise the envelope event */
++          elan4_sdram_writeq (dev, txdRail->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_env.ev_CountAndType),
++                              E4_EVENT_INIT_VALUE (-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++          
++          spin_lock (&xmtrRail->xmtr_retrylock);
++
++          EP4_TXD_ASSERT (txdRail, (on_list (&txdRail->txd_retry_link, &xmtrRail->xmtr_retrylist[EP4_TXD_LIST_RETRY]) == 1 ||
++                                    on_list (&txdRail->txd_retry_link, &xmtrRail->xmtr_retrylist[EP4_TXD_LIST_STALLED]) == 1));
++
++          txdRail->txd_retry_time = 0;
++
++          list_del (&txdRail->txd_retry_link);
++
++          spin_unlock (&xmtrRail->xmtr_retrylock);
++      }
++    }
++    spin_unlock_irqrestore (&xmtr->Lock, flags);
++}
++
++int
++ep4xmtr_poll_txd (EP_XMTR_RAIL *x, EP_TXD_RAIL *t, int how)
++{
++    EP4_XMTR_RAIL *xmtrRail = (EP4_XMTR_RAIL *) x;
++    ELAN4_DEV     *dev      = XMTR_TO_DEV (xmtrRail);
++    EP4_TXD_RAIL  *txdRail  = (EP4_TXD_RAIL *) t;
++    EP_TXD        *txd      = txdRail->txd_generic.Txd;
++
++    if (! EP_IS_NO_INTERRUPT(txd->Envelope.Attr))
++      return 0;
++
++    switch (how)
++    {
++    case ENABLE_TX_CALLBACK:
++      if (!EP_IS_INTERRUPT_ENABLED(txd->Envelope.Attr))
++      {
++          elan4_sdram_writeq (dev, txdRail->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_done_cmd.c_intr_cmd),
++                              INTERRUPT_CMD | (xmtrRail->xmtr_intcookie.int_val << E4_MAIN_INT_SHIFT));
++
++          txd->Envelope.Attr |= EP_INTERRUPT_ENABLED;
++      }
++      break;
++
++    case DISABLE_TX_CALLBACK:
++      if (EP_IS_INTERRUPT_ENABLED(txd->Envelope.Attr & EP_INTERRUPT_ENABLED))
++      {
++          elan4_sdram_writeq (dev, txdRail->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_done_cmd.c_intr_cmd), NOP_CMD);
++
++          txd->Envelope.Attr &= ~EP_INTERRUPT_ENABLED;
++      }
++    }
++    
++    if (txdRail->txd_main->txd_env == EP4_STATE_FINISHED && txdRail->txd_main->txd_data == EP4_STATE_FINISHED && txdRail->txd_main->txd_done == EP4_STATE_FINISHED)
++    {
++      EPRINTF3 (DBG_XMTR, "%s: ep4xmtr_poll_txd: txd=%p XID=%llx completed\n",
++                XMTR_TO_RAIL (xmtrRail)->r_generic.Name, txd, txd->Envelope.Xid.Unique);
++      
++      elan4_sdram_writeq (dev, txdRail->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_done_cmd.c_intr_cmd),
++                          INTERRUPT_CMD | (txdRail->txd_intcookie.int_val << E4_MAIN_INT_SHIFT));
++
++
++      ep_xmtr_txd_stat(xmtrRail->xmtr_generic.Xmtr,txd);
++
++      finalise_txd (txd, txdRail);
++
++      return 1;
++    }
++
++    return 0;
++}
++
++int
++ep4xmtr_bind_txd (EP_TXD *txd, EP_XMTR_RAIL *x, unsigned int phase)
++{
++    EP4_XMTR_RAIL *xmtrRail = (EP4_XMTR_RAIL *) x;
++    EP4_RAIL      *rail     = XMTR_TO_RAIL (xmtrRail);
++    EP4_TXD_RAIL  *txdRail;
++    unsigned long  flags;
++
++    if ((txdRail = get_txd_rail (xmtrRail)) == NULL)
++      return 0;
++    
++    switch (phase)
++    {
++    case EP_TXD_PHASE_ACTIVE:
++      if (rail->r_generic.Nodes[txd->NodeId].State != EP_NODE_CONNECTED)
++      {
++          EPRINTF2 (DBG_XMTR, "%s: ep4xmtr_bind_txd: node %u not connected on this rail\n", rail->r_generic.Name, txd->NodeId);
++
++          free_txd_rail (xmtrRail, txdRail);
++          return 0;
++      }
++
++      initialise_txd (txd, txdRail, EP_TXD_PHASE_ACTIVE);
++
++      bind_txd_rail (txd, txdRail);
++      
++      /* generate the STEN packet to transfer the envelope */
++      spin_lock_irqsave (&xmtrRail->xmtr_retrylock, flags);
++      if (((int) (xmtrRail->xmtr_flowcnt - xmtrRail->xmtr_main->xmtr_flowcnt)) < EP4_XMTR_FLOWCNT)
++          issue_envelope_packet (xmtrRail, txdRail);
++      else
++      {
++          txdRail->txd_retry_time = lbolt;
++
++          list_add_tail (&txdRail->txd_retry_link, &xmtrRail->xmtr_retrylist[EP4_TXD_LIST_RETRY]);
++
++          ep_kthread_schedule (&rail->r_retry_thread, txdRail->txd_retry_time);
++      }
++      spin_unlock_irqrestore (&xmtrRail->xmtr_retrylock, flags);
++      break;
++
++    case EP_TXD_PHASE_PASSIVE:
++      initialise_txd (txd, txdRail, EP_TXD_PHASE_PASSIVE);
++      
++      EP_XMTR_OP (txd->TxdRail->XmtrRail, UnbindTxd) (txd, EP_TXD_PHASE_PASSIVE);     /* unbind from existing rail */
++
++      bind_txd_rail (txd, txdRail);                                                   /* and bind it to our new rail */
++      break;
++    }
++
++    return 1;
++}
++
++void
++ep4xmtr_unbind_txd (EP_TXD *txd, unsigned int phase)
++{
++    /* XXXX - TBD */
++}
++
++long
++ep4xmtr_check (EP_XMTR_RAIL *x, long nextRunTime)
++{
++    EP4_XMTR_RAIL *xmtrRail = (EP4_XMTR_RAIL *) x;
++    EP_XMTR       *xmtr     = xmtrRail->xmtr_generic.Xmtr;
++    struct list_head  txdList;
++    struct list_head *el, *nel;
++    unsigned long flags;
++
++    INIT_LIST_HEAD (&txdList);
++
++    if (xmtrRail->xmtr_freecount < ep_txd_lowat && !alloc_txd_block (xmtrRail))
++    {
++      EPRINTF1 (DBG_RCVR,"%s: failed to grow txd rail pool\n", XMTR_TO_RAIL(xmtrRail)->r_generic.Name);
++              
++      if (nextRunTime == 0 || AFTER (nextRunTime, lbolt + RESOURCE_RETRY_TIME))
++          nextRunTime = lbolt + RESOURCE_RETRY_TIME;
++    }
++
++    spin_lock_irqsave (&xmtr->Lock, flags);
++    list_for_each_safe (el, nel, &xmtrRail->xmtr_retrylist[EP4_TXD_LIST_POLL]) {
++      EP4_TXD_RAIL *txdRail = list_entry (el, EP4_TXD_RAIL, txd_retry_link);
++
++      if (txdRail->txd_main->txd_env != EP4_STATE_FINISHED || txdRail->txd_main->txd_data != EP4_STATE_FINISHED)
++      {
++          ep_debugf (DBG_XMTR, "%s: ep4xmtr_check: xmtrRail=%p txdRail=%p env/data (%d,%d) not finished\n",
++                     XMTR_TO_RAIL(xmtrRail)->r_generic.Name, xmtrRail, txdRail, (int)txdRail->txd_main->txd_env, (int)txdRail->txd_main->txd_data);
++                  
++          nextRunTime = lbolt + HZ;
++      }
++      else
++      {
++          EP_TXD *txd = txdRail->txd_generic.Txd;
++
++          ep_debugf (DBG_XMTR, "%s: ep4xmtr_check: xmtrRail=%p txdRail=%p env/data (%d,%d) finished\n",
++                     XMTR_TO_RAIL(xmtrRail)->r_generic.Name, xmtrRail, txdRail, (int)txdRail->txd_main->txd_env, (int)txdRail->txd_main->txd_data);
++
++          EPRINTF5 (DBG_XMTR, "%s: ep4xmtr_check: xmtrRail=%p txdRail=%p env/data (%d,%d) finished\n",
++                    XMTR_TO_RAIL(xmtrRail)->r_generic.Name, xmtrRail, txdRail, (int)txdRail->txd_main->txd_env, (int)txdRail->txd_main->txd_data);
++          EPRINTF3  (DBG_XMTR, "%s:    done %x data %x\n", XMTR_TO_RAIL(xmtrRail)->r_generic.Name,
++                     txdRail->txd_elan_addr + offsetof (EP4_TXD_RAIL_ELAN, txd_done),
++                     txdRail->txd_elan_addr + offsetof (EP4_TXD_RAIL_ELAN, txd_data));
++
++          EP4_TXD_ASSERT (txdRail, txdRail->txd_retry_time != 0);
++
++          /* remove txd from active list and add to list to call handlers */
++          list_del (&txd->Link);
++          list_add_tail (&txd->Link, &txdList);
++
++          /* remove and free of txdRail */
++          txdRail->txd_retry_time = 0;
++          list_del (&txdRail->txd_retry_link);
++
++          finalise_txd (txd, txdRail);
++
++      }
++    }
++    spin_unlock_irqrestore (&xmtr->Lock, flags);
++
++    while (! list_empty (&txdList))
++    {
++      EP_TXD *txd = list_entry (txdList.next, EP_TXD, Link);
++
++      list_del (&txd->Link);
++
++      ep_xmtr_txd_stat (xmtr,txd);
++
++      txd->Handler (txd, txd->Arg, EP_SUCCESS);
++
++      FreeTxd (xmtr, txd);
++    }
++
++    return nextRunTime;
++}
++
++unsigned long
++ep4xmtr_retry (EP4_RAIL *rail, void *arg, unsigned long nextRunTime)
++{
++    EP4_XMTR_RAIL *xmtrRail = (EP4_XMTR_RAIL *) arg;
++    ELAN4_DEV     *dev      = XMTR_TO_DEV(xmtrRail);
++    unsigned long  flags;
++
++    spin_lock_irqsave (&xmtrRail->xmtr_retrylock, flags);
++    while (! list_empty (&xmtrRail->xmtr_retrylist[EP4_TXD_LIST_RETRY]))
++    {
++      EP4_TXD_RAIL *txdRail = list_entry (xmtrRail->xmtr_retrylist[EP4_TXD_LIST_RETRY].next, EP4_TXD_RAIL, txd_retry_link);
++
++      if (BEFORE (lbolt, txdRail->txd_retry_time))
++      {
++          if (nextRunTime == 0 || AFTER (nextRunTime, txdRail->txd_retry_time))
++              nextRunTime = txdRail->txd_retry_time;
++
++          break;
++      }
++
++      if (((int) (xmtrRail->xmtr_flowcnt - xmtrRail->xmtr_main->xmtr_flowcnt)) < EP4_XMTR_FLOWCNT)
++      {
++          txdRail->txd_retry_time = 0;
++
++          list_del (&txdRail->txd_retry_link);
++          
++          /* re-initialise the envelope event */
++          elan4_sdram_writeq (dev, txdRail->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_env.ev_CountAndType),
++                              E4_EVENT_INIT_VALUE (-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++          
++          EPRINTF3 (DBG_RETRY, "%s: ep4xmtr_retry: re-issue envelope packet to %d for txdRail=%p\n", 
++                    rail->r_generic.Name, txdRail->txd_generic.Txd->Envelope.NodeId, txdRail);
++          
++          txdRail->txd_main->txd_env = EP4_STATE_ACTIVE;
++          
++          issue_envelope_packet (xmtrRail, txdRail);
++      }
++      else
++      {
++          EPRINTF2 (DBG_RETRY, "%s: ep4xmtr_retry: cannot re-issue envelope packet to %d\n", rail->r_generic.Name, txdRail->txd_generic.Txd->Envelope.NodeId);
++
++          if (nextRunTime == 0 || AFTER (nextRunTime, txdRail->txd_retry_time))
++              nextRunTime = txdRail->txd_retry_time;
++
++          break;
++      }
++    }
++    spin_unlock_irqrestore (&xmtrRail->xmtr_retrylock, flags);
++    
++    return nextRunTime;
++}
++
++void
++ep4xmtr_add_rail (EP_XMTR *xmtr, EP_COMMS_RAIL *commsRail)
++{
++    EP4_RAIL         *rail   = (EP4_RAIL *) commsRail->Rail;
++    EP_COMMS_SUBSYS  *subsys = xmtr->Subsys;
++    EP4_XMTR_RAIL    *xmtrRail;
++    unsigned long     flags;
++    int                     i;
++
++    KMEM_ZALLOC (xmtrRail, EP4_XMTR_RAIL *, sizeof (EP4_XMTR_RAIL), 1);
++
++    spin_lock_init (&xmtrRail->xmtr_freelock);
++    kcondvar_init  (&xmtrRail->xmtr_freesleep);
++    INIT_LIST_HEAD (&xmtrRail->xmtr_freelist);
++    INIT_LIST_HEAD (&xmtrRail->xmtr_blocklist);
++
++    for (i = 0; i < EP4_TXD_NUM_LISTS; i++)
++      INIT_LIST_HEAD (&xmtrRail->xmtr_retrylist[i]);
++    spin_lock_init (&xmtrRail->xmtr_retrylock);
++
++    xmtrRail->xmtr_generic.CommsRail = commsRail;
++    xmtrRail->xmtr_generic.Xmtr      = xmtr;
++
++    xmtrRail->xmtr_main = ep_alloc_main (&rail->r_generic, sizeof (EP4_XMTR_RAIL_MAIN), 0, &xmtrRail->xmtr_main_addr);
++    xmtrRail->xmtr_cq   = elan4_alloccq (&rail->r_ctxt, EP4_XMTR_CQSIZE, CQ_EnableAllBits, CQ_Priority);
++
++    xmtrRail->xmtr_retryops.op_func = ep4xmtr_retry;
++    xmtrRail->xmtr_retryops.op_arg  = xmtrRail;
++
++    ep4_add_retry_ops (rail, &xmtrRail->xmtr_retryops);
++
++    ep4_register_intcookie (rail, &xmtrRail->xmtr_intcookie, xmtrRail->xmtr_main_addr,
++                          poll_interrupt, xmtrRail);
++
++    spin_lock_irqsave (&xmtr->Lock, flags);
++
++    xmtr->Rails[commsRail->Rail->Number] = &xmtrRail->xmtr_generic;
++    xmtr->RailMask |= EP_RAIL2RAILMASK(commsRail->Rail->Number);
++
++    spin_unlock_irqrestore (&xmtr->Lock, flags);
++
++    ep_kthread_schedule (&subsys->Thread, lbolt);
++
++    ep_procfs_xmtr_add_rail(&(xmtrRail->xmtr_generic));
++}
++
++void
++ep4xmtr_del_rail (EP_XMTR *xmtr, EP_COMMS_RAIL *commsRail)
++{
++    EP4_RAIL         *rail     = (EP4_RAIL *) commsRail->Rail;
++    EP4_XMTR_RAIL    *xmtrRail = (EP4_XMTR_RAIL *) xmtr->Rails[commsRail->Rail->Number];
++    unsigned long     flags;
++
++    /* rail mask set as not usable */
++    spin_lock_irqsave (&xmtr->Lock, flags);
++    xmtr->RailMask &= ~EP_RAIL2RAILMASK (rail->r_generic.Number);
++    spin_unlock_irqrestore (&xmtr->Lock, flags);
++
++    ep_procfs_xmtr_del_rail(&(xmtrRail->xmtr_generic));
++
++    /* wait for all txd's for this rail to become free */
++    spin_lock_irqsave (&xmtrRail->xmtr_freelock, flags);
++    while (xmtrRail->xmtr_freecount != xmtrRail->xmtr_totalcount)
++    {
++      xmtrRail->xmtr_freewaiting++;
++      kcondvar_wait (&xmtrRail->xmtr_freesleep, &xmtrRail->xmtr_freelock, &flags);
++    }
++    spin_unlock_irqrestore (&xmtrRail->xmtr_freelock, flags);
++
++    spin_lock_irqsave (&xmtr->Lock, flags);
++    xmtr->Rails[commsRail->Rail->Number] = NULL;
++    spin_unlock_irqrestore (&xmtr->Lock, flags);
++
++    /* all the txd's accociated with DescBlocks must be in the freelist */
++    ASSERT (xmtrRail->xmtr_totalcount == xmtrRail->xmtr_freecount);
++
++    /* run through the DescBlockList deleting them */
++    while (!list_empty (&xmtrRail->xmtr_blocklist))
++      free_txd_block (xmtrRail, list_entry(xmtrRail->xmtr_blocklist.next, EP4_TXD_RAIL_BLOCK , blk_link));
++    
++    /* it had better be empty after that */
++    ASSERT ((xmtrRail->xmtr_freecount == 0) && (xmtrRail->xmtr_totalcount == 0));
++
++    ep4_deregister_intcookie (rail, &xmtrRail->xmtr_intcookie);
++
++    ep4_remove_retry_ops (rail, &xmtrRail->xmtr_retryops);
++
++    elan4_freecq (&rail->r_ctxt, xmtrRail->xmtr_cq);
++    ep_free_main (&rail->r_generic, xmtrRail->xmtr_main_addr, sizeof (EP4_XMTR_RAIL_MAIN));
++
++    spin_lock_destroy (&xmtrRail->xmtr_retrylock);
++
++    spin_lock_destroy (&xmtrRail->xmtr_freelock);
++    kcondvar_destroy (&xmtrRail->xmtr_freesleep);
++
++    KMEM_FREE (xmtrRail, sizeof (EP4_XMTR_RAIL));
++}
++
++void
++ep4xmtr_display_xmtr (DisplayInfo *di, EP_XMTR_RAIL *x)
++{
++    EP4_XMTR_RAIL    *xmtrRail     = (EP4_XMTR_RAIL *) x;
++    EP4_RAIL         *rail         = XMTR_TO_RAIL (xmtrRail);
++    unsigned int      freeCount    = 0;
++    unsigned int      pollCount    = 0;
++    unsigned int      stalledCount = 0;
++    unsigned int      retryCount   = 0;
++    struct list_head *el;
++    unsigned long     flags;
++
++    spin_lock_irqsave (&xmtrRail->xmtr_freelock, flags);
++    list_for_each (el, &xmtrRail->xmtr_freelist)
++      freeCount++;
++    spin_unlock_irqrestore (&xmtrRail->xmtr_freelock, flags);
++
++    spin_lock_irqsave (&xmtrRail->xmtr_retrylock, flags);
++    list_for_each (el, &xmtrRail->xmtr_retrylist[EP4_TXD_LIST_POLL])
++      pollCount++;
++    list_for_each (el, &xmtrRail->xmtr_retrylist[EP4_TXD_LIST_STALLED])
++      stalledCount++;
++    list_for_each (el, &xmtrRail->xmtr_retrylist[EP4_TXD_LIST_RETRY])
++      retryCount++;
++    spin_unlock_irqrestore (&xmtrRail->xmtr_retrylock, flags);
++
++    (di->func)(di->arg, "        rail=%d free=%d total=%d (%d) (retry %d,%d,%d)\n",
++             rail->r_generic.Number, xmtrRail->xmtr_freecount, xmtrRail->xmtr_totalcount, 
++             freeCount, pollCount, stalledCount, retryCount);
++    (di->func)(di->arg, "        cq %d flowcnt %lld,%lld\n", elan4_cq2num (xmtrRail->xmtr_cq), xmtrRail->xmtr_flowcnt, xmtrRail->xmtr_main->xmtr_flowcnt);
++}
++
++void
++ep4xmtr_display_txd (DisplayInfo *di, EP_TXD_RAIL *t)
++{
++    EP4_TXD_RAIL      *txdRail  = (EP4_TXD_RAIL *) t;
++    EP4_XMTR_RAIL     *xmtrRail = TXD_TO_XMTR(txdRail);
++    EP4_TXD_RAIL_MAIN *txdMain  = txdRail->txd_main;
++    sdramaddr_t        txdElan  = txdRail->txd_elan;
++    EP4_RAIL          *rail     = XMTR_TO_RAIL (xmtrRail);
++    ELAN4_DEV         *dev      = XMTR_TO_DEV (xmtrRail);
++    char            *list     = "";
++    unsigned long      flags;
++
++    spin_lock_irqsave (&xmtrRail->xmtr_retrylock, flags);
++    if (txdRail->txd_retry_time)
++    {
++      if (on_list (&txdRail->txd_retry_link, &xmtrRail->xmtr_retrylist[EP4_TXD_LIST_POLL]))
++          list = " poll";
++      else if (on_list (&txdRail->txd_retry_link, &xmtrRail->xmtr_retrylist[EP4_TXD_LIST_STALLED]))
++          list = " stalled";
++      else if (on_list (&txdRail->txd_retry_link, &xmtrRail->xmtr_retrylist[EP4_TXD_LIST_RETRY]))
++          list = " retry";
++      else
++          list = " ERROR";
++    }
++    spin_unlock_irqrestore (&xmtrRail->xmtr_retrylock, flags);
++
++    (di->func)(di->arg, "      Rail %d txd %p elan %lx (%x) main %p (%x) cookie <%lld%s%s%s%s> ecq %d %s\n", rail->r_generic.Number,
++             txdRail, txdRail->txd_elan, txdRail->txd_elan_addr, txdRail->txd_main, txdRail->txd_main_addr, 
++             EP4_COOKIE_STRING(txdRail->txd_cookie), elan4_cq2num (txdRail->txd_ecq->ecq_cq), list);
++    
++    (di->func)(di->arg, "        env  %016llx %016llx %016llx -> %016llx\n",
++             elan4_sdram_readq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_env.ev_CountAndType)),
++             elan4_sdram_readq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_env.ev_Params[0])),
++             elan4_sdram_readq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_env.ev_Params[1])),
++             txdMain->txd_env);
++    (di->func)(di->arg, "        data %016llx %016llx %016llx -> %016llx\n",
++             elan4_sdram_readq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_data.ev_CountAndType)),
++             elan4_sdram_readq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_data.ev_Params[0])),
++             elan4_sdram_readq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_data.ev_Params[1])),
++             txdMain->txd_data);
++    (di->func)(di->arg, "        done %016llx %016llx %016llx -> %016llx\n",
++             elan4_sdram_readq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_done.ev_CountAndType)),
++             elan4_sdram_readq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_done.ev_Params[0])),
++             elan4_sdram_readq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_done.ev_Params[1])),
++             txdMain->txd_done);
++}
++
++int
++ep4xmtr_check_txd_state (EP_TXD *txd) 
++{
++    EP4_TXD_RAIL  *txdRail  = (EP4_TXD_RAIL *) txd->TxdRail;
++    EP4_XMTR_RAIL *xmtrRail = (EP4_XMTR_RAIL *) txdRail->txd_generic.XmtrRail;
++    ELAN4_DEV     *dev      = XMTR_TO_DEV (xmtrRail);
++    unsigned long  flags;
++
++    if (txdRail->txd_main->txd_env == EP4_STATE_FINISHED)
++      return 0;
++
++    EP4_TXD_ASSERT (txdRail, txdRail->txd_retry_time != 0);
++
++    spin_lock_irqsave (&xmtrRail->xmtr_retrylock, flags);
++    EP4_TXD_ASSERT (txdRail, on_list (&txdRail->txd_retry_link, &xmtrRail->xmtr_retrylist[EP4_TXD_LIST_STALLED]) == 1);
++
++    list_del (&txdRail->txd_retry_link);
++    txdRail->txd_retry_time  = 0;
++    spin_unlock_irqrestore (&xmtrRail->xmtr_retrylock, flags);
++    
++    /* re-initialise the envelope event */
++    elan4_sdram_writeq (dev, txdRail->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_env.ev_CountAndType),
++                      E4_EVENT_INIT_VALUE (-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++          
++    unbind_txd_rail (txd, txdRail);
++
++    terminate_txd_rail (xmtrRail, txdRail);
++    free_txd_rail (xmtrRail, txdRail);
++
++    return 1;
++}
++
++void
++ep4xmtr_fillout_rail_stats(EP_XMTR_RAIL *xmtr_rail, char *str) {
++    /* no stats here yet */
++    /* EP4_XMTR_RAIL * ep4xmtr_rail = (EP4_XMTR_RAIL *) xmtr_rail; */
++}
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/ep/ep_procfs.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/ep_procfs.c 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/ep_procfs.c      2005-06-01 23:12:54.660429832 -0400
+@@ -0,0 +1,331 @@
++
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: ep_procfs.c,v 1.5.6.3 2004/11/30 10:10:57 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/ep_procfs.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++#include <elan/epsvc.h>
++#include <elan/epcomms.h>
++
++#include "cm.h"
++#include "debug.h"
++#include "conf_linux.h"
++
++#include "kcomm_vp.h"
++#include "kcomm_elan4.h"
++#include "epcomms_elan4.h"
++
++#include <qsnet/procfs_linux.h>
++
++struct proc_dir_entry *ep_procfs_xmtr_root;
++struct proc_dir_entry *ep_procfs_rcvr_root;
++
++static int
++ep_proc_open (struct inode *inode, struct file *file)
++{
++    PROC_PRIVATE *pr;
++    int           pages = 4;
++
++    if ((pr = kmalloc (sizeof (PROC_PRIVATE), GFP_KERNEL)) == NULL)
++      return (-ENOMEM);
++    
++    do {      
++      pr->pr_data_len = PAGESIZE * pages;
++
++      KMEM_ZALLOC (pr->pr_data, char *, pr->pr_data_len, 1);
++      if (pr->pr_data == NULL) 
++      { 
++          pr->pr_len  = sprintf (pr->pr_data, "Out of Memory\n");
++          break;
++      } 
++      
++      pr->pr_off     = 0;
++      pr->pr_len     = 0;
++      pr->pr_data[0] = 0;
++      
++      pr->pr_di.func  = proc_character_fill;
++      pr->pr_di.arg   = (long)pr;
++      
++      if (!strcmp("debug_xmtr", file->f_dentry->d_iname)) 
++      {   
++          EP_XMTR *xmtr = (EP_XMTR *)(PDE(inode)->data);
++          ep_display_xmtr (&pr->pr_di, xmtr);
++      }
++      
++      if (!strcmp("debug_rcvr", file->f_dentry->d_iname)) 
++      {
++          EP_RCVR *rcvr = (EP_RCVR *)(PDE(inode)->data);
++          ep_display_rcvr (&pr->pr_di, rcvr, 0);
++      }
++      
++      if (!strcmp("debug_full", file->f_dentry->d_iname)) 
++      {
++          EP_RCVR *rcvr = (EP_RCVR *)(PDE(inode)->data);
++          ep_display_rcvr (&pr->pr_di, rcvr, 1);
++      }
++
++      if ( pr->pr_len < pr->pr_data_len) 
++          break; /* we managed to get all the output into the buffer */
++
++      pages++;
++      KMEM_FREE ( pr->pr_data,  pr->pr_data_len);
++    } while (1);
++      
++
++    file->private_data = (void *) pr;
++
++    MOD_INC_USE_COUNT;
++    return (0);
++}
++
++struct file_operations ep_proc_operations = 
++{
++    read:     proc_read,
++    open:     ep_proc_open,
++    release:  proc_release,
++};
++
++static int
++proc_read_rcvr_stats(char *page, char **start, off_t off,
++                   int count, int *eof, void *data)
++{
++    EP_RCVR *rcvr = (EP_RCVR *)data;
++    
++    if (rcvr == NULL) 
++      sprintf(page,"proc_read_rcvr_stats rcvr=NULL\n");
++    else {
++      page[0] = 0;
++      ep_rcvr_fillout_stats(rcvr,page);
++    }
++    return (qsnet_proc_calc_metrics (page, start, off, count, eof, strlen(page)));
++}
++
++static int
++proc_read_rcvr_rail_stats(char *page, char **start, off_t off,
++                   int count, int *eof, void *data)
++{
++    EP_RCVR_RAIL *rcvr_rail = (EP_RCVR_RAIL *)data;
++
++    if (rcvr_rail == NULL) {
++      strcpy(page,"proc_read_rcvr_rail_stats rcvr_rail=NULL");
++    } else {
++      page[0] = 0;
++      ep_rcvr_rail_fillout_stats(rcvr_rail, page);
++      EP_RCVR_OP(rcvr_rail,FillOutRailStats)(rcvr_rail,page);
++    }
++    return (qsnet_proc_calc_metrics (page, start, off, count, eof, strlen(page)));
++}
++
++void
++ep_procfs_rcvr_add(EP_RCVR *rcvr)
++{ 
++    /* ep/rcvr/service_number/stats       */
++    /* ep/rcvr/service_number/debug_rcvr  */
++    /* ep/rcvr/service_number/debug_full  */
++    struct proc_dir_entry *p;
++    char str[32];
++
++    sprintf(str,"%d", rcvr->Service);
++
++    rcvr->procfs_root = proc_mkdir (str, ep_procfs_rcvr_root);
++
++    if ((p = create_proc_entry ("stats", 0,  rcvr->procfs_root)) != NULL)
++    {
++      p->write_proc = NULL;
++      p->read_proc  = proc_read_rcvr_stats;
++      p->data       = rcvr;
++      p->owner      = THIS_MODULE;
++    }
++
++    if ((p = create_proc_entry ("debug_rcrv", 0, rcvr->procfs_root)) != NULL)
++    {
++      p->proc_fops = &ep_proc_operations;
++      p->owner     = THIS_MODULE;
++      p->data      = rcvr;
++    }
++
++    if ((p = create_proc_entry ("debug_full", 0, rcvr->procfs_root)) != NULL)
++    {
++      p->proc_fops = &ep_proc_operations;
++      p->owner     = THIS_MODULE;
++      p->data      = rcvr;
++    }
++}
++
++void
++ep_procfs_rcvr_del(EP_RCVR *rcvr)
++{  
++    char str[32];
++    sprintf(str,"%d", rcvr->Service);
++
++    remove_proc_entry ("stats",      rcvr->procfs_root);
++    remove_proc_entry ("debug_rcvr", rcvr->procfs_root);
++    remove_proc_entry ("debug_full", rcvr->procfs_root);
++
++    remove_proc_entry (str, ep_procfs_rcvr_root);
++}
++
++void 
++ep_procfs_rcvr_add_rail(EP_RCVR_RAIL *rcvrRail)
++{
++    /* ep/rcvr/service_number/railN/stats */
++
++    struct proc_dir_entry *p;
++    char str[32];
++    sprintf(str,"rail%d",rcvrRail->CommsRail->Rail->Number);
++
++    rcvrRail->procfs_root = proc_mkdir (str, rcvrRail->Rcvr->procfs_root);
++    
++    if ((p = create_proc_entry ("stats", 0,  rcvrRail->procfs_root)) != NULL)
++    {
++      p->write_proc = NULL;
++      p->read_proc  = proc_read_rcvr_rail_stats;
++      p->data       = rcvrRail;
++      p->owner      = THIS_MODULE;
++    } 
++}
++
++void 
++ep_procfs_rcvr_del_rail(EP_RCVR_RAIL *rcvrRail)
++{
++    char str[32];
++    sprintf(str,"rail%d",rcvrRail->CommsRail->Rail->Number);
++
++    remove_proc_entry ("stats", rcvrRail->procfs_root);
++
++    remove_proc_entry (str, rcvrRail->Rcvr->procfs_root);
++}
++
++
++
++
++static int
++proc_read_xmtr_stats(char *page, char **start, off_t off,
++                   int count, int *eof, void *data)
++{
++    EP_XMTR *xmtr = (EP_XMTR *)data;
++
++    if (xmtr == NULL) 
++      strcpy(page,"proc_read_xmtr_stats xmtr=NULL\n");
++    else {
++      page[0] = 0;
++      ep_xmtr_fillout_stats(xmtr, page);
++    }
++    return (qsnet_proc_calc_metrics (page, start, off, count, eof, strlen(page)));
++}
++
++static int
++proc_read_xmtr_rail_stats(char *page, char **start, off_t off,
++                   int count, int *eof, void *data)
++{
++    EP_XMTR_RAIL *xmtr_rail = (EP_XMTR_RAIL *)data;
++
++    if (xmtr_rail == NULL) 
++      strcpy(page,"proc_read_xmtr_rail_stats xmtr_rail=NULL\n");
++    else {
++      page[0] = 0;
++      ep_xmtr_rail_fillout_stats(xmtr_rail, page);
++      EP_XMTR_OP(xmtr_rail,FillOutRailStats)(xmtr_rail,page);
++    }
++    return (qsnet_proc_calc_metrics (page, start, off, count, eof, strlen(page)));
++}
++
++void
++ep_procfs_xmtr_add(EP_XMTR *xmtr)
++{ 
++    /* ep/xmtr/service_number/stats       */
++    /* ep/xmtr/service_number/debug_xmtr  */
++    struct proc_dir_entry *p;
++    char str[32];
++
++    sprintf(str,"%llx", (unsigned long long) (unsigned long)xmtr);
++
++    xmtr->procfs_root = proc_mkdir (str, ep_procfs_xmtr_root);
++
++    if ((p = create_proc_entry ("stats", 0,  xmtr->procfs_root)) != NULL)
++    {
++      p->write_proc = NULL;
++      p->read_proc  = proc_read_xmtr_stats;
++      p->data       = xmtr;
++      p->owner      = THIS_MODULE;
++    } 
++
++    if ((p = create_proc_entry ("debug_xmtr", 0, xmtr->procfs_root)) != NULL)
++    {
++      p->proc_fops = &ep_proc_operations;
++      p->owner     = THIS_MODULE;
++      p->data      = xmtr;
++    }
++}
++
++void
++ep_procfs_xmtr_del(EP_XMTR *xmtr)
++{  
++    char str[32];
++    sprintf(str,"%llx", (unsigned long long) (unsigned long)xmtr);
++
++    remove_proc_entry ("stats",      xmtr->procfs_root);
++    remove_proc_entry ("debug_xmtr", xmtr->procfs_root);
++
++    remove_proc_entry (str, ep_procfs_xmtr_root);
++}
++
++void 
++ep_procfs_xmtr_add_rail(EP_XMTR_RAIL *xmtrRail)
++{
++    /* ep/xmtr/service_number/railN/stats */
++    
++    struct proc_dir_entry *p;
++    char str[32];
++    sprintf(str,"rail%d",xmtrRail->CommsRail->Rail->Number);
++
++    xmtrRail->procfs_root = proc_mkdir (str, xmtrRail->Xmtr->procfs_root);
++
++    if ((p = create_proc_entry ("stats", 0,  xmtrRail->procfs_root)) != NULL)
++    {
++      p->write_proc = NULL;
++      p->read_proc  = proc_read_xmtr_rail_stats;
++      p->data       = xmtrRail;
++      p->owner      = THIS_MODULE;
++    } 
++}
++
++void 
++ep_procfs_xmtr_del_rail(EP_XMTR_RAIL *xmtrRail)
++{
++    char str[32];
++    sprintf(str,"rail%d",xmtrRail->CommsRail->Rail->Number);
++
++    remove_proc_entry ("stats", xmtrRail->procfs_root);
++
++    remove_proc_entry (str, xmtrRail->Xmtr->procfs_root);
++}
++
++void
++ep_procfs_rcvr_xmtr_init(void)
++{
++    ep_procfs_rcvr_root = proc_mkdir ("rcvr", ep_procfs_root);
++    ep_procfs_xmtr_root = proc_mkdir ("xmtr", ep_procfs_root); 
++}
++
++void
++ep_procfs_rcvr_xmtr_fini(void)
++{
++    remove_proc_entry ("rcvr", ep_procfs_root);
++    remove_proc_entry ("xmtr", ep_procfs_root);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/ep/kalloc.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/kalloc.c    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/kalloc.c 2005-06-01 23:12:54.661429680 -0400
+@@ -0,0 +1,677 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: kalloc.c,v 1.17.8.2 2004/12/14 10:19:14 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/kalloc.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++
++#include "debug.h"
++
++static void
++HashInPool (EP_ALLOC *alloc, EP_POOL *pool)
++{
++    int idx0 = HASH (pool->Handle.nmh_nmd.nmd_addr);
++    int idx1 = HASH (pool->Handle.nmh_nmd.nmd_addr + pool->Handle.nmh_nmd.nmd_len);
++
++    list_add (&pool->HashBase, &alloc->HashBase[idx0]);
++    list_add (&pool->HashTop, &alloc->HashTop[idx1]);
++}
++
++static void
++HashOutPool (EP_ALLOC *alloc, EP_POOL *pool)
++{
++    list_del (&pool->HashBase);
++    list_del (&pool->HashTop);
++}
++
++static EP_POOL *
++LookupPool (EP_ALLOC *alloc, EP_ADDR addr)
++{
++    struct list_head *el;
++    
++    list_for_each (el, &alloc->HashBase[HASH(addr)]) {
++      EP_POOL *pool = list_entry (el, EP_POOL, HashBase);
++      
++      if (pool->Handle.nmh_nmd.nmd_addr <= addr && addr < (pool->Handle.nmh_nmd.nmd_addr + pool->Handle.nmh_nmd.nmd_len))
++          return (pool);
++    }
++    
++    list_for_each (el, &alloc->HashTop[HASH(addr)]) {
++      EP_POOL *pool = list_entry (el, EP_POOL, HashTop);
++      
++      if (pool->Handle.nmh_nmd.nmd_addr <= addr && addr < (pool->Handle.nmh_nmd.nmd_addr + pool->Handle.nmh_nmd.nmd_len))
++          return (pool);
++    }
++    
++    return (NULL);
++}
++
++static EP_POOL *
++AllocatePool (EP_ALLOC *alloc, EP_ADDR addr, unsigned size, unsigned int perm, EP_ATTRIBUTE attr)
++{
++    EP_ADDR base = 0;
++    EP_POOL *pool;
++    EP_RAIL *rail;
++    int i, railmask = 0;
++    struct list_head *el;
++
++    KMEM_ZALLOC (pool, EP_POOL *, sizeof (EP_POOL), !(attr & EP_NO_SLEEP));
++    
++    if (pool == NULL)
++      return (NULL);
++    
++    if (addr != 0)
++      base = addr;
++    else
++    {
++      for (i = LN2_MIN_SIZE; i <= LN2_MAX_SIZE; i ++)
++      {
++          KMEM_ZALLOC (pool->Bitmaps[i - LN2_MIN_SIZE], bitmap_t *, BT_BITOUL(1 << (LN2_MAX_SIZE-i)) * sizeof (bitmap_t), !(attr & EP_NO_SLEEP));
++          if (pool->Bitmaps[i - LN2_MIN_SIZE] == NULL)
++              goto failed;
++      }
++    
++      if ((base = ep_rmalloc (alloc->ResourceMap, size, !(attr & EP_NO_SLEEP))) == 0)
++          goto failed;
++    }
++
++    switch (alloc->Type)
++    {
++    case EP_ALLOC_TYPE_PRIVATE_SDRAM:
++      rail = alloc->Data.Private.Rail;
++
++      if ((pool->Buffer.Sdram = rail->Operations.SdramAlloc (rail, base, size)) == 0)
++          goto failed;
++
++      ep_perrail_sdram_map (rail, base, pool->Buffer.Sdram, size, perm, attr);
++
++      pool->Handle.nmh_nmd.nmd_addr = base;
++      pool->Handle.nmh_nmd.nmd_len  = size;
++      break;
++      
++    case EP_ALLOC_TYPE_PRIVATE_MAIN:
++        KMEM_GETPAGES(pool->Buffer.Ptr, unsigned long, btop (size), !(attr & EP_NO_SLEEP));
++      if (pool->Buffer.Ptr == 0)
++          goto failed;
++
++      ep_perrail_kaddr_map (alloc->Data.Private.Rail, base, pool->Buffer.Ptr, size, perm, attr);
++
++      pool->Handle.nmh_nmd.nmd_addr = base;
++      pool->Handle.nmh_nmd.nmd_len  = size;
++      break;
++
++    case EP_ALLOC_TYPE_SHARED_MAIN:
++        KMEM_GETPAGES(pool->Buffer.Ptr, unsigned long, btop (size), !(attr & EP_NO_SLEEP));
++      if (pool->Buffer.Ptr == 0)
++          goto failed;
++
++      list_for_each (el, &alloc->Data.Shared.Rails) {
++          EP_RAIL *rail = list_entry (el, EP_RAIL_ENTRY, Link)->Rail;
++
++          ep_perrail_kaddr_map (rail, base, pool->Buffer.Ptr, size, perm, attr);
++
++          railmask |= (1 << rail->Number);
++      }
++      pool->Handle.nmh_nmd.nmd_addr = base;
++      pool->Handle.nmh_nmd.nmd_len  = size;
++      pool->Handle.nmh_nmd.nmd_attr = EP_NMD_ATTR (alloc->Data.Shared.System->Position.pos_nodeid, railmask);
++
++      ep_nmh_insert (&alloc->Data.Shared.System->MappingTable, &pool->Handle);
++      break;
++
++    default:
++      goto failed;
++    }
++
++    return (pool);
++    
++ failed:
++    if (addr == 0 && base)
++      ep_rmfree (alloc->ResourceMap, size, base);
++
++    for (i = LN2_MIN_SIZE; i <= LN2_MAX_SIZE; i ++)
++      if (pool->Bitmaps[i - LN2_MIN_SIZE] != NULL)
++          KMEM_FREE (pool->Bitmaps[i - LN2_MIN_SIZE], BT_BITOUL(1 << (LN2_MAX_SIZE - i)) * sizeof (bitmap_t));
++    
++    KMEM_FREE (pool, sizeof (EP_POOL));
++    return (NULL);
++}
++
++static void
++FreePool (EP_ALLOC *alloc, EP_POOL *pool)
++{
++    struct list_head *el;
++    int i;
++
++    switch (alloc->Type)
++    {
++    case EP_ALLOC_TYPE_PRIVATE_SDRAM:
++      ep_perrail_unmap (alloc->Data.Private.Rail, pool->Handle.nmh_nmd.nmd_addr, pool->Handle.nmh_nmd.nmd_len);
++
++      alloc->Data.Private.Rail->Operations.SdramFree (alloc->Data.Private.Rail, pool->Buffer.Sdram, pool->Handle.nmh_nmd.nmd_len);
++      break;
++      
++    case EP_ALLOC_TYPE_PRIVATE_MAIN:
++      ep_perrail_unmap (alloc->Data.Private.Rail, pool->Handle.nmh_nmd.nmd_addr, pool->Handle.nmh_nmd.nmd_len);
++
++      KMEM_FREEPAGES (pool->Buffer.Ptr, btop (pool->Handle.nmh_nmd.nmd_len));
++      break;
++
++    case EP_ALLOC_TYPE_SHARED_MAIN:
++      ep_nmh_remove (&alloc->Data.Shared.System->MappingTable, &pool->Handle);
++
++      list_for_each (el, &alloc->Data.Shared.Rails) {
++          EP_RAIL *rail = list_entry (el, EP_RAIL_ENTRY, Link)->Rail;
++
++          ep_perrail_unmap (rail, pool->Handle.nmh_nmd.nmd_addr, pool->Handle.nmh_nmd.nmd_len);
++      }
++
++      KMEM_FREEPAGES (pool->Buffer.Ptr, btop (pool->Handle.nmh_nmd.nmd_len));
++      break;
++    }
++    
++    if (pool->Bitmaps[0])
++    {
++      ep_rmfree (alloc->ResourceMap, pool->Handle.nmh_nmd.nmd_len, pool->Handle.nmh_nmd.nmd_addr);
++    
++      for (i = LN2_MIN_SIZE; i <= LN2_MAX_SIZE; i ++)
++          KMEM_FREE (pool->Bitmaps[i - LN2_MIN_SIZE], BT_BITOUL(1 << (LN2_MAX_SIZE - i)) * sizeof (bitmap_t));
++    }
++    
++    KMEM_FREE (pool, sizeof (EP_POOL));
++}
++
++static int
++AddRail (EP_ALLOC *alloc, EP_RAIL *rail)
++{
++    struct list_head *el;
++    EP_RAIL_ENTRY *l;
++    unsigned long flags;
++    int i;
++
++    ASSERT (alloc->Type == EP_ALLOC_TYPE_SHARED_MAIN);
++
++    KMEM_ZALLOC (l, EP_RAIL_ENTRY *, sizeof (EP_RAIL_ENTRY), 1);
++
++    if (l == NULL)
++      return (ENOMEM);
++
++    l->Rail = rail;
++
++    spin_lock_irqsave (&alloc->Lock, flags);
++    for (i = 0; i < NHASH; i++)
++    {
++      list_for_each (el, &alloc->HashBase[i]) {
++          EP_POOL *pool = list_entry (el, EP_POOL, HashBase);
++
++          ep_perrail_kaddr_map (rail, pool->Handle.nmh_nmd.nmd_addr, pool->Buffer.Ptr, 
++                                pool->Handle.nmh_nmd.nmd_len, EP_PERM_WRITE, EP_NO_SLEEP);
++
++          pool->Handle.nmh_nmd.nmd_attr |= EP_NMD_ATTR (0, 1 << rail->Number);
++      }
++    }
++
++    list_add (&l->Link, &alloc->Data.Shared.Rails);
++
++    spin_unlock_irqrestore (&alloc->Lock, flags); 
++    return (0);
++}
++
++static void
++RemoveRail (EP_ALLOC *alloc, EP_RAIL *rail)
++{
++    struct list_head *el;
++    unsigned long flags;
++    int i;
++
++    spin_lock_irqsave (&alloc->Lock, flags);
++    for (i = 0; i < NHASH; i++)
++    {
++      list_for_each (el, &alloc->HashBase[i]) {
++          EP_POOL *pool = list_entry (el, EP_POOL, HashBase);
++
++          ep_perrail_unmap (rail, pool->Handle.nmh_nmd.nmd_addr, pool->Handle.nmh_nmd.nmd_len);
++
++          pool->Handle.nmh_nmd.nmd_attr &= ~EP_NMD_ATTR (0, 1 << rail->Number);
++      }
++    }
++
++    list_for_each (el, &alloc->Data.Shared.Rails) {
++      EP_RAIL_ENTRY *tmp = list_entry (el, EP_RAIL_ENTRY, Link);
++      if (tmp->Rail == rail)
++      {
++          list_del (el);
++          KMEM_FREE(tmp, sizeof (EP_RAIL_ENTRY));
++          break;
++      }
++    }
++
++    spin_unlock_irqrestore (&alloc->Lock, flags);
++}
++
++static EP_POOL *
++AllocateBlock (EP_ALLOC *alloc, unsigned size, EP_ATTRIBUTE attr, int *offset)
++{
++    int block, j, k;
++    unsigned long flags;
++    EP_POOL *pool;
++
++
++    if (size > MAX_SIZE)
++    {
++      if ((attr & EP_NO_ALLOC) || (pool  = AllocatePool (alloc, 0, size, alloc->Perm, attr)) == NULL)
++          return (NULL);
++
++      spin_lock_irqsave (&alloc->Lock, flags);
++      HashInPool (alloc, pool);
++      spin_unlock_irqrestore (&alloc->Lock, flags);
++
++      *offset = 0;
++
++      return pool;
++    }
++
++    spin_lock_irqsave (&alloc->Lock, flags);
++
++    /* Round up size to next power of 2 */
++    for (k = LN2_MIN_SIZE; (1 << k) < size; k++)
++      ;
++    
++    /* k now has ln2 of the size to allocate. */
++    /* find the free list with the smallest block we can use*/
++    for (j = k; j <= LN2_MAX_SIZE && list_empty (&alloc->Freelists[j - LN2_MIN_SIZE]); j++)
++      ;
++    
++    /* j has ln2 of the smallest size block we can use */
++    if (j < LN2_MAX_SIZE)
++    {
++      int nbits = 1 << (LN2_MAX_SIZE-j);
++      
++      pool  = list_entry (alloc->Freelists[j - LN2_MIN_SIZE].next, EP_POOL, Link[j - LN2_MIN_SIZE]);
++      block = (bt_lowbit (pool->Bitmaps[j - LN2_MIN_SIZE], nbits) << j);
++      
++      BT_CLEAR (pool->Bitmaps[j - LN2_MIN_SIZE], block >> j);
++      
++      if (bt_lowbit (pool->Bitmaps[j - LN2_MIN_SIZE], nbits) == -1)
++          list_del (&pool->Link[j - LN2_MIN_SIZE]);
++    }
++    else
++    {
++      spin_unlock_irqrestore (&alloc->Lock, flags);
++      
++      if ((attr & EP_NO_ALLOC) || (pool  = AllocatePool (alloc, 0, MAX_SIZE, alloc->Perm, attr)) == NULL)
++          return (NULL);
++
++      block = 0;
++      j = LN2_MAX_SIZE;
++      
++      spin_lock_irqsave (&alloc->Lock, flags);
++      
++      HashInPool (alloc, pool);
++    }
++    
++    /* Split it until the buddies are the correct size, putting one
++     * buddy back on the free list and continuing to split the other */
++    while (--j >= k)
++    {
++      list_add (&pool->Link[j - LN2_MIN_SIZE], &alloc->Freelists[j - LN2_MIN_SIZE]);
++      
++      BT_SET (pool->Bitmaps[j - LN2_MIN_SIZE], block >> j);
++      
++      block += (1 << j);
++    }
++    spin_unlock_irqrestore (&alloc->Lock, flags);
++
++    *offset = block;
++
++    return (pool);
++}
++
++static void
++FreeBlock (EP_ALLOC *alloc, EP_ADDR addr, unsigned size)
++{
++    EP_POOL *pool;
++    int  k, block = 0;
++    unsigned long flags;
++    
++    spin_lock_irqsave (&alloc->Lock, flags);
++    /* Round up size to next power of 2 */
++    for (k = LN2_MIN_SIZE; (1 << k) < size; k++)
++      ;
++
++    /* Find the pool containing this block */
++    pool = LookupPool (alloc, addr);
++
++    /* It must exist */
++    ASSERT (pool != NULL);
++
++    /* If we're freeing a subset of it, then update the bitmaps */
++    if (size <= MAX_SIZE)
++    {
++      ASSERT (BT_TEST (pool->Bitmaps[k - LN2_MIN_SIZE], (addr - pool->Handle.nmh_nmd.nmd_addr) >> k) == 0);
++      
++      block = addr - pool->Handle.nmh_nmd.nmd_addr;
++      
++      while (k < LN2_MAX_SIZE && BT_TEST (pool->Bitmaps[k - LN2_MIN_SIZE], (block >> k) ^ 1))
++      {
++          BT_CLEAR (pool->Bitmaps[k - LN2_MIN_SIZE], (block >> k) ^ 1);
++          
++          if (bt_lowbit (pool->Bitmaps[k - LN2_MIN_SIZE], (1 << (LN2_MAX_SIZE - k))) == -1)
++              list_del (&pool->Link[k - LN2_MIN_SIZE]);
++          
++          k++;
++      }
++    }
++
++    if (k >= LN2_MAX_SIZE)
++    {
++      HashOutPool (alloc, pool);
++      spin_unlock_irqrestore (&alloc->Lock, flags);
++
++      FreePool (alloc, pool);
++    }
++    else
++    {
++      if (bt_lowbit (pool->Bitmaps[k - LN2_MIN_SIZE], (1 << (LN2_MAX_SIZE - k))) == -1)
++          list_add (&pool->Link[k - LN2_MIN_SIZE], &alloc->Freelists[k - LN2_MIN_SIZE]);
++
++      BT_SET (pool->Bitmaps[k - LN2_MIN_SIZE], block >> k);
++
++      spin_unlock_irqrestore (&alloc->Lock, flags);
++    }
++}
++
++static void
++InitialiseAllocator (EP_ALLOC *alloc, EP_ALLOC_TYPE type, unsigned int perm, EP_RMAP *rmap)
++{
++    int i;
++
++    spin_lock_init (&alloc->Lock);
++
++    alloc->Type        = type;
++    alloc->ResourceMap = rmap;
++    alloc->Perm        = perm;
++
++    for (i = 0; i < NHASH; i++)
++    {
++      (&alloc->HashBase[i])->next = &alloc->HashBase[i];
++
++      INIT_LIST_HEAD (&alloc->HashBase[i]);
++      INIT_LIST_HEAD (&alloc->HashTop[i]);
++    }
++    
++    for (i = 0; i < NUM_FREELISTS; i++)
++      INIT_LIST_HEAD (&alloc->Freelists[i]);
++}
++
++static void
++DestroyAllocator (EP_ALLOC *alloc)
++{
++    struct list_head *el, *next;
++    int i;
++
++    for (i = 0; i < NHASH; i++)
++    {
++      list_for_each_safe (el, next, &alloc->HashBase[i]) { 
++          EP_POOL *pool = list_entry (el, EP_POOL, HashBase);
++
++          printk ("!!DestroyAllocator: pool=%p type=%d addr=%x len=%x\n", pool, alloc->Type,
++                  pool->Handle.nmh_nmd.nmd_addr, pool->Handle.nmh_nmd.nmd_len);
++
++          list_del (&pool->HashBase);
++          list_del (&pool->HashTop);
++
++          // XXXX: FreePool (alloc, pool);
++      }
++    }
++
++    spin_lock_destroy (&alloc->Lock);
++}
++
++void
++ep_display_alloc (EP_ALLOC *alloc)
++{
++    struct list_head *el;
++    int i;
++    int npools = 0;
++    int nbytes = 0;
++    int nfree = 0;
++    unsigned long flags;
++
++    spin_lock_irqsave (&alloc->Lock, flags);
++
++    ep_debugf (DBG_DEBUG, "Kernel comms memory allocator %p type %d\n", alloc, alloc->Type);
++    for (i = 0; i < NHASH; i++)
++    {
++      list_for_each (el, &alloc->HashBase[i]) {
++          EP_POOL *pool = list_entry (el, EP_POOL, HashBase);
++
++          ep_debugf (DBG_DEBUG, "  POOL %4x: %p -> %x.%x\n", i, pool, pool->Handle.nmh_nmd.nmd_addr,
++                     pool->Handle.nmh_nmd.nmd_addr + pool->Handle.nmh_nmd.nmd_len);
++
++          npools++;
++          nbytes += pool->Handle.nmh_nmd.nmd_len;
++      }
++    }
++    
++    for (i = LN2_MIN_SIZE; i <= LN2_MAX_SIZE; i++)
++    {
++      int n = 0;
++
++      list_for_each (el, &alloc->Freelists[i - LN2_MIN_SIZE]) {
++          EP_POOL *pool  = list_entry (el, EP_POOL, Link[i - LN2_MIN_SIZE]);
++          int      nbits = bt_nbits (pool->Bitmaps[i - LN2_MIN_SIZE], 1 << (LN2_MAX_SIZE - i));
++
++          n += nbits;
++          nfree += (nbits << i);
++      }
++      
++      if (n != 0)
++          ep_debugf (DBG_DEBUG, "  SIZE %5d : num %d\n", (1 << i), n);
++    }
++    ep_debugf (DBG_DEBUG, "%d pools with %d bytes and %d bytes free\n", npools, nbytes, nfree);
++
++    spin_unlock_irqrestore (&alloc->Lock, flags);
++}
++
++/* per-rail allocators */
++void
++ep_alloc_init (EP_RAIL *rail)
++{
++    EP_RMAP *rmap = ep_rmallocmap (EP_PRIVATE_RMAP_SIZE, "PrivateMap", 1);
++
++    ep_rmfree (rmap, EP_PRIVATE_TOP-EP_PRIVATE_BASE, EP_PRIVATE_BASE);
++
++    InitialiseAllocator (&rail->ElanAllocator, EP_ALLOC_TYPE_PRIVATE_SDRAM, EP_PERM_ALL, rmap);
++    InitialiseAllocator (&rail->MainAllocator, EP_ALLOC_TYPE_PRIVATE_MAIN, EP_PERM_WRITE, rmap);
++
++    rail->ElanAllocator.Data.Private.Rail = rail;
++    rail->MainAllocator.Data.Private.Rail = rail;
++}
++
++void
++ep_alloc_fini (EP_RAIL *rail)
++{
++    EP_RMAP *rmap = rail->ElanAllocator.ResourceMap;
++
++    DestroyAllocator (&rail->ElanAllocator);
++    DestroyAllocator (&rail->MainAllocator);
++    
++    ep_rmfreemap (rmap);
++}
++
++sdramaddr_t
++ep_alloc_memory_elan (EP_RAIL *rail, EP_ADDR addr, unsigned size, unsigned int perm, EP_ATTRIBUTE attr)
++{
++    EP_POOL *pool = AllocatePool (&rail->ElanAllocator, addr, size, perm, attr);
++    unsigned long flags;
++
++    if (pool == NULL)
++      return (0);
++
++    spin_lock_irqsave (&rail->ElanAllocator.Lock, flags);
++    HashInPool (&rail->ElanAllocator, pool);
++    spin_unlock_irqrestore (&rail->ElanAllocator.Lock, flags);
++
++    return (pool->Buffer.Sdram);
++}
++
++void
++ep_free_memory_elan (EP_RAIL *rail, EP_ADDR addr)
++{
++    EP_POOL *pool;
++    unsigned long flags;
++
++    spin_lock_irqsave (&rail->ElanAllocator.Lock, flags);
++    pool = LookupPool (&rail->ElanAllocator, addr);
++    
++    HashOutPool (&rail->ElanAllocator, pool);
++    spin_unlock_irqrestore (&rail->ElanAllocator.Lock, flags);
++    
++    FreePool (&rail->ElanAllocator, pool);
++}
++
++sdramaddr_t
++ep_alloc_elan (EP_RAIL *rail, unsigned size, EP_ATTRIBUTE attr, EP_ADDR *addrp)
++{
++    int            offset;
++    EP_POOL *pool;
++
++    if ((pool = AllocateBlock (&rail->ElanAllocator, size, attr, &offset)) == NULL)
++      return (0);
++    
++    *addrp  = pool->Handle.nmh_nmd.nmd_addr + offset;
++
++    return (pool->Buffer.Sdram + offset);
++}
++
++void
++ep_free_elan (EP_RAIL *rail, EP_ADDR addr, unsigned size)
++{
++    FreeBlock (&rail->ElanAllocator, addr, size);
++}
++
++void *
++ep_alloc_main (EP_RAIL *rail, unsigned size, EP_ATTRIBUTE attr, EP_ADDR *addrp)
++{
++    int            offset;
++    EP_POOL *pool;
++
++    if ((pool = AllocateBlock (&rail->MainAllocator, size, attr, &offset)) == NULL)
++      return (NULL);
++    
++    *addrp  = pool->Handle.nmh_nmd.nmd_addr + offset;
++
++    return ((void *) ((unsigned long) pool->Buffer.Ptr + offset));
++}
++
++void
++ep_free_main (EP_RAIL *rail, EP_ADDR addr, unsigned size)
++{
++    FreeBlock (&rail->MainAllocator, addr, size);
++}
++
++sdramaddr_t
++ep_elan2sdram (EP_RAIL *rail, EP_ADDR addr)
++{
++    EP_POOL    *pool;
++    sdramaddr_t res;
++    unsigned long flags;
++
++    spin_lock_irqsave (&rail->ElanAllocator.Lock, flags);
++    if ((pool = LookupPool (&rail->ElanAllocator, addr)) == NULL)
++      res = 0;
++    else
++      res = pool->Buffer.Sdram + (addr - pool->Handle.nmh_nmd.nmd_addr);
++    spin_unlock_irqrestore (&rail->ElanAllocator.Lock, flags);
++
++    return (res);
++}
++
++void *
++ep_elan2main (EP_RAIL *rail, EP_ADDR addr)
++{
++    EP_POOL *pool;
++    void *res;
++    unsigned long flags;
++
++    spin_lock_irqsave (&rail->MainAllocator.Lock, flags);
++    if ((pool = LookupPool (&rail->MainAllocator, addr)) == NULL)
++      res = NULL;
++    else
++      res = (void *) ((unsigned long) pool->Buffer.Ptr + (addr - pool->Handle.nmh_nmd.nmd_addr));
++    spin_unlock_irqrestore (&rail->MainAllocator.Lock, flags);
++
++    return (res);
++}
++
++/* shared allocators */
++int
++ep_shared_alloc_add_rail (EP_SYS *sys, EP_RAIL *rail)
++{
++    return (AddRail (&sys->Allocator, rail));
++}
++
++void
++ep_shared_alloc_remove_rail (EP_SYS *sys, EP_RAIL *rail)
++{
++    RemoveRail (&sys->Allocator, rail);
++}
++
++void
++ep_shared_alloc_init (EP_SYS *sys)
++{
++    EP_RMAP *rmap = ep_rmallocmap (EP_SHARED_RMAP_SIZE, "shared_alloc_map", 1);
++
++    ep_rmfree (rmap, EP_SHARED_TOP - EP_SHARED_BASE, EP_SHARED_BASE);
++
++    InitialiseAllocator (&sys->Allocator, EP_ALLOC_TYPE_SHARED_MAIN, EP_PERM_WRITE, rmap);
++
++    INIT_LIST_HEAD (&sys->Allocator.Data.Shared.Rails);
++
++    sys->Allocator.Data.Shared.System = sys;
++}
++
++void
++ep_shared_alloc_fini (EP_SYS *sys)
++{
++    EP_RMAP *rmap = sys->Allocator.ResourceMap;
++
++    DestroyAllocator (&sys->Allocator);
++
++    ep_rmfreemap (rmap);
++}
++
++void *
++ep_shared_alloc_main (EP_SYS *sys, unsigned size, EP_ATTRIBUTE attr, EP_NMD *nmd)
++{
++    int offset;
++    EP_POOL *pool;
++
++    if ((pool = AllocateBlock (&sys->Allocator, size, attr, &offset)) == NULL)
++      return (NULL);
++
++    ep_nmd_subset (nmd, &pool->Handle.nmh_nmd, offset, size);
++
++    return ((void *) ((unsigned long) pool->Buffer.Ptr + offset));
++}
++
++void
++ep_shared_free_main (EP_SYS *sys, EP_NMD *nmd)
++{
++    FreeBlock (&sys->Allocator, nmd->nmd_addr, nmd->nmd_len);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/ep/kcomm.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/kcomm.c     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/kcomm.c  2005-06-01 23:12:54.664429224 -0400
+@@ -0,0 +1,1448 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: kcomm.c,v 1.50.2.9 2004/12/09 10:02:42 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/kcomm.c,v $ */
++
++#include <qsnet/kernel.h>
++#include <qsnet/kthread.h>
++
++#include <elan/kcomm.h>
++#include <elan/epsvc.h>
++#include <elan/epcomms.h>
++
++#include "cm.h"
++#include "debug.h"
++
++int MaxSwitchLevels = 5;                              /* Max 1024 sized machine */
++
++static char *NodeStateNames[EP_NODE_NUM_STATES] = 
++{
++    "Disconnected",
++    "Connecting",
++    "Connnected",
++    "LeavingConnected",
++    "LocalPassivate",
++    "RemotePassivate",
++    "Passivated",
++    "Disconnecting",
++};
++
++static void
++ep_xid_cache_fill (EP_SYS *sys, EP_XID_CACHE *cache)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&sys->XidLock, flags);
++
++    cache->Current = sys->XidNext;
++    cache->Last    = cache->Current + EP_XID_CACHE_CHUNKS-1;
++
++    sys->XidNext += EP_XID_CACHE_CHUNKS;
++
++    spin_unlock_irqrestore (&sys->XidLock, flags);
++}
++
++EP_XID
++ep_xid_cache_alloc (EP_SYS *sys, EP_XID_CACHE *cache)
++{
++    EP_XID xid;
++    
++    if (cache->Current == cache->Last)
++      ep_xid_cache_fill (sys, cache);
++
++    xid.Generation = sys->XidGeneration;
++    xid.Handle     = cache->Handle;
++    xid.Unique     = cache->Current++;
++
++    return (xid);
++}
++
++void
++ep_xid_cache_init (EP_SYS *sys, EP_XID_CACHE *cache)
++{
++    /* Stall manager thread - it doesn't lock the XidCacheList */
++    ep_kthread_stall (&sys->ManagerThread);
++
++    cache->Handle = ++sys->XidHandle;
++
++    list_add_tail (&cache->Link, &sys->XidCacheList);
++
++    ep_kthread_resume (&sys->ManagerThread);
++}
++
++void
++ep_xid_cache_destroy (EP_SYS *sys, EP_XID_CACHE *cache)
++{
++    /* Stall manager thread - it doesn't lock the XidCacheList */
++    ep_kthread_stall (&sys->ManagerThread);
++
++    list_del (&cache->Link);
++
++    ep_kthread_resume (&sys->ManagerThread);
++}
++
++EP_XID_CACHE *
++ep_xid_cache_find (EP_SYS *sys, EP_XID xid)
++{
++    struct list_head *el;
++
++    list_for_each (el, &sys->XidCacheList) {
++      EP_XID_CACHE *cache = list_entry (el, EP_XID_CACHE, Link);
++
++      if (sys->XidGeneration == xid.Generation && cache->Handle == xid.Handle)
++          return (cache);
++    }
++
++    return (NULL);
++}
++
++static int
++MsgBusy (EP_RAIL *rail, EP_OUTPUTQ *outputq, int slotNum)
++{
++    switch (rail->Operations.OutputQState (rail, outputq, slotNum))
++    {
++    case EP_OUTPUTQ_BUSY:                     /* still busy */
++      return 1;
++      
++    case EP_OUTPUTQ_FAILED:                   /* NACKed */
++    {
++#if defined(DEBUG_PRINTF)
++      EP_MANAGER_MSG *msg = rail->Operations.OutputQMsg (rail, outputq, slotNum);
++
++      EPRINTF4 (DBG_MANAGER, "%s: kcomm msg %d type %d to %d failed\n", rail->Name, slotNum, msg->Hdr.Type, msg->Hdr.DestId);
++#endif
++      break;
++    }
++    
++    case EP_OUTPUTQ_FINISHED:                 /* anything else is finished */
++      break;
++    }
++
++    return 0;
++}
++
++int
++ep_send_message (EP_RAIL *rail, int nodeId, int type, EP_XID xid, EP_MANAGER_MSG_BODY *body)
++{
++    EP_SYS         *sys  = rail->System;
++    EP_NODE        *node = &sys->Nodes[nodeId];
++    int             n    = EP_MANAGER_OUTPUTQ_SLOTS;
++    int             slotNum;
++    int             rnum;
++    EP_RAIL        *msgRail;
++    EP_MANAGER_MSG *msg;
++    unsigned long   flags;
++
++    ASSERT (! EP_XID_INVALID (xid));
++
++    if ((rnum = ep_pickRail (node->ConnectedRails)) >= 0)
++      msgRail = sys->Rails[rnum];
++    else
++    {
++      if (EP_MANAGER_MSG_TYPE_CONNECTED(type))
++      {
++          ep_debugf (DBG_MANAGER, "%s: no rails available, trying to send type %d to %d\n", rail->Name, type, nodeId);
++          return -EHOSTDOWN;
++      }
++
++      ep_debugf (DBG_MANAGER, "%s: no rails connected to %d - using receiving rail\n", rail->Name, nodeId);
++
++      msgRail = rail;
++    }
++    
++
++    spin_lock_irqsave (&msgRail->ManagerOutputQLock, flags);
++
++    slotNum = msgRail->ManagerOutputQNextSlot;
++
++    while (n-- > 0 && MsgBusy (msgRail, msgRail->ManagerOutputQ, slotNum))            /* search for idle message buffer */
++    {
++      if (++(msgRail->ManagerOutputQNextSlot) == EP_MANAGER_OUTPUTQ_SLOTS)
++          msgRail->ManagerOutputQNextSlot = 0;
++      
++      slotNum = msgRail->ManagerOutputQNextSlot;
++    }
++
++    if (n == 0)                                                       /* all message buffers busy */
++    {
++      spin_unlock_irqrestore (&msgRail->ManagerOutputQLock, flags);
++
++      ep_debugf (DBG_MANAGER, "%s: all message buffers busy: trying to send type %d to %d\n", msgRail->Name, type, nodeId);
++      return -EBUSY;
++    }
++
++    msg = msgRail->Operations.OutputQMsg (msgRail, msgRail->ManagerOutputQ, slotNum);
++    
++    EPRINTF7 (DBG_MANAGER, "%s: ep_send_message: type=%d nodeId=%d rail=%d xid=%08x.%08x.%016llx\n", 
++            msgRail->Name, type, nodeId, rail->Number, xid.Generation, xid.Handle, (long long) xid.Unique);
++
++    msg->Hdr.Version    = EP_MANAGER_MSG_VERSION;
++    msg->Hdr.Type       = type;
++    msg->Hdr.Rail       = rail->Number;
++    msg->Hdr.NodeId     = msgRail->Position.pos_nodeid;
++    msg->Hdr.DestId     = nodeId;
++    msg->Hdr.Xid        = xid;
++    msg->Hdr.Checksum   = 0;
++
++    if (body) bcopy (body, &msg->Body, sizeof (EP_MANAGER_MSG_BODY));
++
++    msg->Hdr.Checksum = CheckSum ((char *) msg, EP_MANAGER_MSG_SIZE);
++
++    if (rail->Operations.OutputQSend (msgRail, msgRail->ManagerOutputQ, slotNum, EP_MANAGER_MSG_SIZE,
++                                    nodeId, EP_SYSTEMQ_MANAGER, EP_MANAGER_OUTPUTQ_RETRIES) < 0)
++      IncrStat (msgRail, SendMessageFailed);
++    
++    if (++(msgRail->ManagerOutputQNextSlot) == EP_MANAGER_OUTPUTQ_SLOTS) /* check this one last next time */
++      msgRail->ManagerOutputQNextSlot = 0;
++
++    spin_unlock_irqrestore (&msgRail->ManagerOutputQLock, flags);
++
++    return 0;
++}
++
++void
++ep_panic_node (EP_SYS *sys, int nodeId, unsigned char *reason)
++{
++    EP_NODE            *node = &sys->Nodes[nodeId];
++    EP_MANAGER_MSG_BODY body;
++    EP_XID              xid;
++    kcondvar_t          sleep;
++    int                 rnum;
++    unsigned long       flags;
++
++    if (nodeId > sys->Position.pos_nodes)
++      return;
++
++    strncpy (body.PanicReason, reason, sizeof (body.PanicReason));
++
++    kcondvar_init (&sleep);
++    spin_lock_irqsave (&sys->NodeLock, flags);
++    for (;;)
++    {
++      if (node->ConnectedRails == 0)
++          break;
++
++      for (rnum = 0; rnum < EP_MAX_RAILS; rnum++)
++          if (node->ConnectedRails & (1 << rnum))
++              break;
++
++      xid = ep_xid_cache_alloc(sys, &sys->Rails[rnum]->XidCache);
++      
++      if (ep_send_message (sys->Rails[rnum], nodeId, EP_MANAGER_MSG_TYPE_REMOTE_PANIC, xid, &body) == 0)
++          break;
++
++      if (kcondvar_timedwaitsig (&sleep, &sys->NodeLock, &flags, lbolt + hz) == CV_RET_SIGPENDING)
++          break;
++    }
++    spin_unlock_irqrestore (&sys->NodeLock, flags);
++    kcondvar_destroy (&sleep);
++}
++
++static void
++ProcessNeterrRequest (EP_RAIL *msgRail, EP_RAIL *rail, EP_MANAGER_MSG *msg)
++{
++    EPRINTF4 (DBG_NETWORK_ERROR, "%s: process neterr request - node %d cookies %llx %llx\n", rail->Name, msg->Hdr.NodeId, msg->Body.Cookies[0], msg->Body.Cookies[1]);
++
++    rail->Operations.NeterrFixup (rail, msg->Hdr.NodeId, msg->Body.Cookies);
++    
++    ep_send_message (rail, msg->Hdr.NodeId, EP_MANAGER_MSG_TYPE_NETERR_RESPONSE, msg->Hdr.Xid, &msg->Body);
++}
++
++
++static void
++ProcessNeterrResponse (EP_RAIL *msgRail, EP_RAIL *rail, EP_MANAGER_MSG *msg)
++{
++    EP_SYS       *sys      = rail->System;
++    EP_NODE_RAIL *nodeRail = &rail->Nodes[msg->Hdr.NodeId];
++    unsigned long flags;
++
++    EPRINTF4 (DBG_NETWORK_ERROR, "%s: process neterr response - node %d cookies %llx %llx\n", rail->Name, msg->Hdr.NodeId, msg->Body.Cookies[0], msg->Body.Cookies[1]);
++
++    spin_lock_irqsave (&sys->NodeLock, flags);
++    if (EP_XIDS_MATCH (nodeRail->MsgXid, msg->Hdr.Xid))
++    {
++      EP_INVALIDATE_XID (nodeRail->MsgXid);
++
++      if (nodeRail->NetworkErrorCookies[0] != 0 && nodeRail->NetworkErrorCookies[0] == msg->Body.Cookies[0])
++          nodeRail->NetworkErrorCookies[0] = 0;
++
++      if (nodeRail->NetworkErrorCookies[1] != 0 && nodeRail->NetworkErrorCookies[1] == msg->Body.Cookies[1])
++          nodeRail->NetworkErrorCookies[1] = 0;
++      
++      if (nodeRail->NetworkErrorCookies[0] == 0 && nodeRail->NetworkErrorCookies[1] == 0)
++          nodeRail->NetworkErrorState &= ~EP_NODE_NETERR_ATOMIC_PACKET;
++    }
++    spin_unlock_irqrestore (&sys->NodeLock, flags);
++}
++
++
++static void
++ProcessGetNodeState (EP_RAIL *msgRail, EP_RAIL *rail, EP_MANAGER_MSG *msg)
++{
++    EP_NODE_RAIL *nodeRail = &rail->Nodes[msg->Hdr.NodeId];
++    unsigned int service = msg->Body.Service;
++
++    EPRINTF5 (DBG_MANAGER, "%s: ProcessGetNodeState: %s - %d %s%s\n",  msgRail->Name, rail->Name, msg->Hdr.NodeId,
++            NodeStateNames[nodeRail->State], nodeRail->NetworkErrorState ? " (NetworkError)" : "");
++
++    msg->Body.NodeState.State             = nodeRail->State;
++    msg->Body.NodeState.NetworkErrorState = nodeRail->NetworkErrorState;
++    msg->Body.NodeState.Railmask          = ep_rcvr_railmask (rail->System, service);
++
++    if (ep_send_message (rail, msg->Hdr.NodeId, EP_MANAGER_MSG_TYPE_GET_NODE_STATE_RESPONSE, msg->Hdr.Xid, &msg->Body) < 0)
++      printk ("%s: get node state for %s[%d] - failed to send response\n", msgRail->Name, rail->Name, msg->Hdr.NodeId);
++}
++
++static void
++ProcessFlushRequest (EP_RAIL *msgRail, EP_RAIL *rail, EP_MANAGER_MSG *msg)
++{
++    EP_NODE_RAIL *nodeRail = &rail->Nodes[msg->Hdr.NodeId];
++
++    EPRINTF5 (DBG_MANAGER, "%s: ProcessFlushRequest: %s - %d %s%s\n",  msgRail->Name, rail->Name, msg->Hdr.NodeId,
++            NodeStateNames[nodeRail->State], nodeRail->NetworkErrorState ? " (NetworkError)" : "");
++
++    switch (nodeRail->State)
++    {
++    case EP_NODE_REMOTE_PASSIVATE:
++      nodeRail->NextRunTime = lbolt + MSGBUSY_RETRY_TIME;     /* retransmit our flush request quickly */
++      EPRINTF3 (DBG_MANAGER, "%s: ProcessFlushRequest: NextRunTime -> %lx (%lx)\n", rail->Name, nodeRail->NextRunTime, lbolt);
++      /* DROPTHROUGH */
++
++    case EP_NODE_PASSIVATED:
++    case EP_NODE_DISCONNECTED:
++      if (nodeRail->NetworkErrorState != 0)
++          break;
++
++      if (ep_send_message (rail, msg->Hdr.NodeId, EP_MANAGER_MSG_TYPE_FLUSH_RESPONSE, msg->Hdr.Xid, NULL) < 0)
++          printk ("%s: flush request for %s[%d] - failed to send response\n", msgRail->Name, rail->Name, msg->Hdr.NodeId);
++      break;
++      
++    default:
++      EPRINTF4 (DBG_MANAGER, "%s: flush request for %s[%d] - node not in approriate state - %s\n", msgRail->Name, rail->Name, msg->Hdr.NodeId, NodeStateNames[nodeRail->State]);
++      break;
++    }
++}
++
++static void
++ProcessFlushResponse (EP_RAIL *msgRail, EP_RAIL *rail, EP_MANAGER_MSG *msg)
++{
++    EP_NODE_RAIL *nodeRail= &rail->Nodes[msg->Hdr.NodeId];
++
++    EPRINTF5 (DBG_MANAGER, "%s: ProcessFlushResponse: %s - %d %s%s\n",  msgRail->Name, rail->Name, msg->Hdr.NodeId,
++            NodeStateNames[nodeRail->State], EP_XIDS_MATCH (nodeRail->MsgXid, msg->Hdr.Xid) ? " (XIDS match)" : "");
++
++    if (nodeRail->State == EP_NODE_REMOTE_PASSIVATE && EP_XIDS_MATCH(nodeRail->MsgXid, msg->Hdr.Xid))
++    {
++      EP_INVALIDATE_XID (nodeRail->MsgXid);
++
++      printk ("%s: flush response from %d - move to passivated list\n", rail->Name, msg->Hdr.NodeId);
++      list_del (&nodeRail->Link);
++
++      /* Node is now passivated - attempt to failover  messages */
++      list_add_tail (&nodeRail->Link, &rail->PassivatedList);
++      nodeRail->State = EP_NODE_PASSIVATED;
++    }
++    else
++    {
++      printk ("%s: flush response from %d - not passivating (%s) or XIDs mismatch (%llx %llx)\n", rail->Name, 
++              msg->Hdr.NodeId, NodeStateNames[nodeRail->State], (long long) nodeRail->MsgXid.Unique, (long long) msg->Hdr.Xid.Unique);
++    }
++}
++
++static void
++ProcessMapNmdRequest (EP_RAIL *msgRail, EP_RAIL *rail, EP_MANAGER_MSG *msg)
++{
++    EP_SYS          *sys     = rail->System;
++    EP_MAP_NMD_BODY *msgBody = &msg->Body.MapNmd;
++    int              i;
++
++    EPRINTF4 (DBG_MANAGER, "%s: Map NMD request from %d for %d NMDs to railmask %x\n", rail->Name, msg->Hdr.NodeId, msgBody->nFrags, msgBody->Railmask);
++    
++    for (i = 0; i < msgBody->nFrags; i++)
++      ep_nmd_map_rails (sys, &msgBody->Nmd[i], msgBody->Railmask);
++    
++    /* Must flush TLBs before responding */
++    for (i = 0; i < EP_MAX_RAILS; i++)
++      if (sys->Rails[i] && sys->Rails[i]->TlbFlushRequired)
++          ep_perrail_dvma_sync (sys->Rails[i]);
++
++    if (ep_send_message (rail, msg->Hdr.NodeId, EP_MANAGER_MSG_TYPE_MAP_NMD_RESPONSE, msg->Hdr.Xid, &msg->Body) < 0)
++      printk ("%s: map nmd request for %s[%d] - failed to send response\n", msgRail->Name, rail->Name, msg->Hdr.NodeId);
++}
++
++static void
++ProcessXidMessage (EP_RAIL *msgRail, EP_MANAGER_MSG *msg, EP_XID xid)
++{
++    EP_XID_CACHE *xidCache = ep_xid_cache_find (msgRail->System, xid);
++
++    EPRINTF6 (DBG_MANAGER, "%s: ProcessXidMessage: XID=%08x.%0x8.%016llx -> %p(%p)\n",
++            msgRail->Name, xid.Generation, xid.Handle, (long long) xid.Unique,
++            xidCache  ? xidCache->MessageHandler : 0, xidCache  ? xidCache->Arg : 0);
++    
++    if (xidCache != NULL)
++      xidCache->MessageHandler (xidCache->Arg, msg);
++}
++
++static void
++ProcessMessage (EP_RAIL *msgRail, void *arg, void *msgbuf)
++{
++    EP_SYS         *sys  = msgRail->System;
++    EP_MANAGER_MSG *msg  = (EP_MANAGER_MSG *) msgbuf;
++    uint16_t        csum = msg->Hdr.Checksum;
++    EP_RAIL        *rail;
++
++    if (msg->Hdr.Version != EP_MANAGER_MSG_VERSION)
++      return;
++
++    msg->Hdr.Checksum= 0;
++    if (CheckSum ((char *) msg, EP_MANAGER_MSG_SIZE) != csum)
++    {
++      printk ("%s: checksum failed on msg from %d (%d) (%x != %x) ?\n", msgRail->Name, msg->Hdr.NodeId, msg->Hdr.Type, csum, CheckSum ((char *) msg, EP_MANAGER_MSG_SIZE));
++      return;
++    }
++
++    if ((rail = sys->Rails[msg->Hdr.Rail]) == NULL)
++    {
++      printk ("%s: rail no longer exists for msg from %d?\n", msgRail->Name, msg->Hdr.NodeId);
++      return;
++    }
++
++    EPRINTF7 (DBG_MANAGER, "%s: ProcessMessage (%s) type=%d node=%d XID=%08x.%0x8.%016llx\n", 
++            msgRail->Name, rail->Name, msg->Hdr.Type, msg->Hdr.NodeId,
++            msg->Hdr.Xid.Generation, msg->Hdr.Xid.Handle, msg->Hdr.Xid.Unique);
++
++    switch (msg->Hdr.Type)
++    {
++    case EP_MANAGER_MSG_TYPE_REMOTE_PANIC:
++      msg->Body.PanicReason[EP_PANIC_STRLEN] = '\0';          /* ensure string terminated */
++
++      printk ("%s: remote panic call from elan node %d - %s\n", msgRail->Name, msg->Hdr.NodeId, msg->Body.PanicReason);
++      panic ("ep: remote panic request\n");
++      break;
++
++    case EP_MANAGER_MSG_TYPE_NETERR_REQUEST:
++      ProcessNeterrRequest (msgRail, rail, msg);
++      break;
++
++    case EP_MANAGER_MSG_TYPE_NETERR_RESPONSE:
++      ProcessNeterrResponse (msgRail, rail, msg);
++      break;
++
++    case EP_MANAGER_MSG_TYPE_FLUSH_REQUEST:
++      ProcessFlushRequest (msgRail, rail, msg);
++      break;
++
++    case EP_MANAGER_MSG_TYPE_FLUSH_RESPONSE:
++      ProcessFlushResponse (msgRail, rail, msg);
++      break;
++
++    case EP_MANAGER_MSG_TYPE_MAP_NMD_REQUEST:
++      ProcessMapNmdRequest (msgRail, rail, msg);
++      break;
++
++    case EP_MANAGER_MSG_TYPE_MAP_NMD_RESPONSE:
++      ProcessXidMessage (msgRail, msg, msg->Hdr.Xid);
++      break;
++
++    case EP_MANAGER_MSG_TYPE_FAILOVER_REQUEST:
++      ProcessXidMessage (msgRail, msg, msg->Body.Failover.Xid);
++      break;
++
++    case EP_MANAGER_MSG_TYPE_FAILOVER_RESPONSE:
++      ProcessXidMessage (msgRail, msg, msg->Hdr.Xid);
++      break;
++       
++    case EP_MANAGER_MSG_TYPE_GET_NODE_STATE:
++      ProcessGetNodeState (msgRail, rail, msg);
++      break;
++
++    case EP_MANAGER_MSG_TYPE_GET_NODE_STATE_RESPONSE: 
++      ProcessXidMessage (msgRail, msg, msg->Hdr.Xid);
++      break;
++
++    default:
++      printk ("%s: Unknown message type %d from %d\n", msgRail->Name, msg->Hdr.Type, msg->Hdr.NodeId);
++      break;
++    }
++}
++
++
++static void
++ManagerQueueEvent (EP_RAIL *rail, void *arg)
++{
++    ep_kthread_schedule ((EP_KTHREAD *) arg, lbolt);
++}
++
++void
++UpdateConnectionState (EP_RAIL *rail, statemap_t *map)
++{
++    EP_SYS *sys = rail->System;
++    bitmap_t seg;
++    int offset, nodeId;
++    unsigned long flags;
++    
++    while ((offset = statemap_findchange (map, &seg, 1)) >= 0)
++    {
++      for (nodeId = offset; nodeId < (offset + BT_NBIPUL) && nodeId < rail->Position.pos_nodes; nodeId++)
++      {
++          EP_NODE      *node     = &sys->Nodes[nodeId];
++          EP_NODE_RAIL *nodeRail = &rail->Nodes[nodeId];
++
++          if (statemap_getbits (map, nodeId, 1))
++          {
++              spin_lock_irqsave (&sys->NodeLock, flags);
++
++              switch (nodeRail->State)
++              {
++              case EP_NODE_DISCONNECTED:
++                  EPRINTF2 (DBG_MANAGER, "%s: Node %d -> Disconnected \n", rail->Name, nodeId);
++                  break;
++
++              case EP_NODE_CONNECTING:
++                  EPRINTF2 (DBG_MANAGER, "%s: Node %d -> Connect\n", rail->Name, nodeId);
++                  
++                  /* load the route table entry *before*  setting the state
++                   * to connected, since DMA's can be initiated as soon as
++                   * the node is marked as connected */
++                  rail->Operations.LoadNodeRoute (rail, nodeId);
++                  
++                  nodeRail->State = EP_NODE_CONNECTED;
++                  
++                  statemap_setbits (rail->NodeSet, nodeId, 1, 1);
++                  if (statemap_getbits (sys->NodeSet, nodeId, 1) == 0)
++                      statemap_setbits (sys->NodeSet, nodeId, 1, 1);
++
++                  /* Add to rails connected to this node */
++                  node->ConnectedRails |= (1 << rail->Number);
++
++                  /* Finally lower the per-node context filter */
++                  rail->Operations.LowerFilter (rail, nodeId);
++                  break;
++                  
++              case EP_NODE_LEAVING_CONNECTED:
++                  EPRINTF2 (DBG_MANAGER, "%s: Node %d -> Local Passivate\n", rail->Name, nodeId);
++                  
++                  /* Raise the per-node context filter */
++                  rail->Operations.RaiseFilter (rail, nodeId);
++
++                  /* If it's resolving network errors it will be on the NodeNeterrList,
++                   * remove if from this list before placing it on the LocalPassivateList
++                   * as we'll resolve the network error later in RemotePassivate */
++                  if (nodeRail->NetworkErrorState)
++                      list_del (&nodeRail->Link);
++
++                  list_add_tail (&nodeRail->Link, &rail->LocalPassivateList);
++                  nodeRail->State = EP_NODE_LOCAL_PASSIVATE;
++
++                  /* Remove from rails connected to this node */
++                  node->ConnectedRails &= ~(1 << rail->Number);
++                  break;
++
++              default:
++                  printk ("%s: Node %d - in NodeChangeMap with state %d\n", rail->Name, nodeId, nodeRail->State);
++                  panic ("Node in NodeChangeMap with invalid state\n");
++                  break;
++              }
++              spin_unlock_irqrestore (&sys->NodeLock, flags);
++          }
++      }
++    }
++}
++
++void
++ProgressNetworkError (EP_RAIL *rail, EP_NODE_RAIL *nodeRail)
++{
++    EP_SYS             *sys    = rail->System;
++    int                 nodeId = nodeRail - rail->Nodes;
++    EP_MANAGER_MSG_BODY msg;
++
++    ASSERT (nodeRail->State >= EP_NODE_CONNECTED && nodeRail->State <= EP_NODE_REMOTE_PASSIVATE);
++
++    if (BEFORE (lbolt, nodeRail->NextRunTime))
++      return;
++
++    if (nodeRail->NetworkErrorState & EP_NODE_NETERR_DMA_PACKET)
++      nodeRail->NetworkErrorState &= ~EP_NODE_NETERR_DMA_PACKET;
++    
++    if (nodeRail->NetworkErrorState & EP_NODE_NETERR_ATOMIC_PACKET)
++    {
++      if (EP_XID_INVALID (nodeRail->MsgXid))
++          nodeRail->MsgXid = ep_xid_cache_alloc (sys, &rail->XidCache);
++
++      msg.Cookies[0] = nodeRail->NetworkErrorCookies[0];
++      msg.Cookies[1] = nodeRail->NetworkErrorCookies[1];
++      
++      EPRINTF4 (DBG_NETWORK_ERROR, "%s: progress neterr - node %d cookies %llx %llx\n", rail->Name, nodeId, msg.Cookies[0], msg.Cookies[1]);
++
++      if (ep_send_message (rail, nodeId, EP_MANAGER_MSG_TYPE_NETERR_REQUEST, nodeRail->MsgXid, &msg) == 0)
++          nodeRail->NextRunTime = lbolt + MESSAGE_RETRY_TIME;
++      else
++          nodeRail->NextRunTime = lbolt + MSGBUSY_RETRY_TIME;
++    }
++}
++
++long
++ProgressNodeLists (EP_RAIL *rail, long nextRunTime)
++{
++    EP_SYS           *sys = rail->System;
++    struct list_head *el, *nel;
++    unsigned long flags;
++
++    spin_lock_irqsave (&sys->NodeLock, flags);
++    list_for_each_safe (el, nel, &rail->NetworkErrorList) {
++      EP_NODE_RAIL *nodeRail = list_entry (el, EP_NODE_RAIL, Link);
++      int           nodeId   = nodeRail - rail->Nodes;
++
++      ProgressNetworkError (rail, nodeRail);
++
++      if (nodeRail->NetworkErrorState == 0)
++      {
++          EPRINTF2 (DBG_NETWORK_ERROR, "%s: lower context filter for node %d due to network error\n", rail->Name, nodeId);
++          printk ("%s: lower context filter for node %d due to network error\n", rail->Name, nodeId);
++
++          rail->Operations.LowerFilter (rail, nodeId);
++
++          list_del (&nodeRail->Link);
++          continue;
++      }
++      
++      if (nextRunTime == 0 || AFTER (nextRunTime, nodeRail->NextRunTime))
++          nextRunTime = nodeRail->NextRunTime;
++    }
++    spin_unlock_irqrestore (&sys->NodeLock, flags);
++
++    if (! list_empty (&rail->LocalPassivateList))
++    {
++      EPRINTF1 (DBG_MANAGER, "%s: Locally Passivating Nodes\n", rail->Name);
++      
++      /* We have disconnected from some nodes or have left ourselves
++       * flush through all communications and determine whether we
++       * need to perform rail failover */
++      rail->Operations.FlushFilters (rail);
++      
++      ep_call_callbacks (rail, EP_CB_FLUSH_FILTERING, rail->NodeSet);
++
++      rail->Operations.FlushQueues (rail);
++
++      ep_call_callbacks (rail, EP_CB_FLUSH_FLUSHING, rail->NodeSet);
++
++      while (! list_empty (&rail->LocalPassivateList))
++      {
++          EP_NODE_RAIL *nodeRail = list_entry (rail->LocalPassivateList.next, EP_NODE_RAIL, Link);
++          int           nodeId   = nodeRail - rail->Nodes;
++
++          list_del (&nodeRail->Link);
++
++          rail->Operations.UnloadNodeRoute (rail, nodeId);
++          
++          if (nodeRail->NetworkErrorState == 0 && nodeRail->MessageState == 0)
++          {
++              EPRINTF2 (DBG_MANAGER, "%s: Node %d -> Disconnecting\n", rail->Name, nodeId);
++
++              list_add_tail (&nodeRail->Link, &rail->DisconnectingList);
++              nodeRail->State = EP_NODE_DISCONNECTING;
++          }
++          else
++          {
++              EPRINTF2 (DBG_MANAGER, "%s: Node %d -> Remote Passivate\n", rail->Name, nodeId);
++
++              list_add_tail (&nodeRail->Link, &rail->RemotePassivateList);
++              nodeRail->State = EP_NODE_REMOTE_PASSIVATE;
++
++              if (nodeRail->NetworkErrorState == 0)
++                  nodeRail->NextRunTime = lbolt;
++          }
++      }
++
++      ep_call_callbacks (rail, EP_CB_PASSIVATED, rail->NodeSet);
++    }
++
++    list_for_each_safe (el, nel, &rail->RemotePassivateList) {
++      EP_NODE_RAIL *nodeRail = list_entry (el, EP_NODE_RAIL, Link);
++      int           nodeId   = nodeRail - rail->Nodes;
++      EP_NODE      *node     = &sys->Nodes[nodeId];
++
++      if (node->ConnectedRails == 0)                          /* no rails connected to this node (anymore) */
++      {
++          /*  Remove from this list */
++          list_del (&nodeRail->Link);
++
++          EPRINTF2 (DBG_MANAGER, "%s: Node %d, no rails, Remote Passivate -> Disconnecting\n", rail->Name, nodeId);
++
++          /* transition towards disconnected */
++          list_add_tail (&nodeRail->Link, &rail->DisconnectingList);
++          nodeRail->State = EP_NODE_DISCONNECTING;
++          continue;
++      }
++
++      EPRINTF6 (DBG_MANAGER, "%s: Node %d - %s NetworkErrorState=%x NextRunTime=%lx (%lx)\n",
++                rail->Name, nodeId, NodeStateNames[nodeRail->State], nodeRail->NetworkErrorState,
++                nodeRail->NextRunTime, nextRunTime);
++
++      if (nodeRail->NetworkErrorState)
++      {
++          ProgressNetworkError (rail, nodeRail);
++      }
++      else if (! BEFORE (lbolt, nodeRail->NextRunTime))
++      {
++          if (EP_XID_INVALID (nodeRail->MsgXid))
++              nodeRail->MsgXid = ep_xid_cache_alloc (sys, &rail->XidCache);
++
++          if (ep_send_message (rail, nodeId, EP_MANAGER_MSG_TYPE_FLUSH_REQUEST, nodeRail->MsgXid, NULL) == 0)
++              nodeRail->NextRunTime = lbolt + MESSAGE_RETRY_TIME;
++          else
++              nodeRail->NextRunTime = lbolt + MSGBUSY_RETRY_TIME;
++      }
++
++      if (nextRunTime == 0 || AFTER (nextRunTime, nodeRail->NextRunTime))
++          nextRunTime = nodeRail->NextRunTime;
++    }
++    
++    if (! list_empty (&rail->PassivatedList)) 
++    {
++      ep_call_callbacks (rail, EP_CB_FAILOVER, rail->NodeSet);
++
++      list_for_each_safe (el, nel, &rail->PassivatedList) {
++          EP_NODE_RAIL *nodeRail = list_entry (rail->PassivatedList.next, EP_NODE_RAIL, Link);
++          int           nodeId   = nodeRail - rail->Nodes;
++          EP_NODE      *node     = &sys->Nodes[nodeId];
++
++          ASSERT (nodeRail->NetworkErrorState == 0);
++
++          if (node->ConnectedRails == 0)
++          {
++              /*  Remove from this list */
++              list_del (&nodeRail->Link);
++
++              EPRINTF2 (DBG_MANAGER, "%s: Node %d, no rails, Passivated -> Disconnecting\n", rail->Name, nodeId);
++
++              /* transition towards disconnected */
++              list_add_tail (&nodeRail->Link, &rail->DisconnectingList);
++              nodeRail->State = EP_NODE_DISCONNECTING;
++              continue;
++          }
++          
++          EPRINTF6 (DBG_MANAGER, "%s: Node %d - %s NetworkErrorState=%x NextRunTime=%lx (%lx)\n",
++                    rail->Name, nodeId, NodeStateNames[nodeRail->State], nodeRail->NetworkErrorState,
++                    nodeRail->NextRunTime, nextRunTime);
++
++          if (nodeRail->MessageState == 0)
++          {
++              EPRINTF2 (DBG_MANAGER, "%s: Node %d, no messages, Passivated -> Disconnecting\n", rail->Name,nodeId);
++
++              list_del (&nodeRail->Link);
++              list_add_tail (&nodeRail->Link, &rail->DisconnectingList);
++              nodeRail->State = EP_NODE_DISCONNECTING;
++              continue;
++          }
++
++          nodeRail->MessageState = 0;
++          nodeRail->NextRunTime  = lbolt + FAILOVER_RETRY_TIME;
++
++          if (nextRunTime == 0 || AFTER (nextRunTime, nodeRail->NextRunTime))
++              nextRunTime = nodeRail->NextRunTime;
++      }
++    }
++
++    if (! list_empty (&rail->DisconnectingList))
++    {
++      ep_call_callbacks (rail, EP_CB_DISCONNECTING, rail->NodeSet);
++
++      while (! list_empty (&rail->DisconnectingList))
++      {
++          EP_NODE_RAIL *nodeRail = list_entry (rail->DisconnectingList.next, EP_NODE_RAIL, Link);
++          int           nodeId   = nodeRail - rail->Nodes;
++          EP_NODE      *node     = &sys->Nodes[nodeId];
++
++          EPRINTF2 (DBG_MANAGER, "%s: Node %d, Disconnecting -> Disconnected\n", rail->Name, nodeId);
++
++          list_del (&nodeRail->Link);
++
++          rail->Operations.NodeDisconnected (rail, nodeId);
++
++          /* Clear the network error state */
++          nodeRail->NextRunTime            = 0;
++          nodeRail->NetworkErrorState      = 0;
++          nodeRail->NetworkErrorCookies[0] = 0;
++          nodeRail->NetworkErrorCookies[1] = 0;
++
++          /* Clear the message state */
++          nodeRail->MessageState = 0;
++
++          cm_node_disconnected (rail, nodeId);
++
++          nodeRail->State = EP_NODE_DISCONNECTED;
++          
++          statemap_setbits (rail->NodeSet, nodeId, 0, 1);
++
++          if (node->ConnectedRails == 0)
++              statemap_setbits (sys->NodeSet, nodeId, 0, 1);
++      }
++
++      ep_call_callbacks (rail, EP_CB_DISCONNECTED, rail->NodeSet);
++    }
++
++    return (nextRunTime);
++}
++
++void
++DisplayNodes (EP_RAIL *rail)
++{
++    EP_SYS *sys = rail->System;
++    int i, state, count;
++    unsigned long flags;
++
++    spin_lock_irqsave (&sys->NodeLock, flags);
++
++    for (state = 0; state < EP_NODE_NUM_STATES; state++)
++    {
++      for (count = i = 0; i < rail->Position.pos_nodes; i++)
++      {
++          ASSERT (rail->Nodes[i].State < EP_NODE_NUM_STATES);
++
++          if (rail->Nodes[i].State == state)
++              if (state != EP_NODE_DISCONNECTED)
++                  printk ("%s %d", !count++ ? NodeStateNames[state] : "", i);
++      }
++      if (count)
++          printk ("%s (%d total)\n", state == EP_NODE_DISCONNECTED ? NodeStateNames[state] : "", count);
++    }
++    spin_unlock_irqrestore (&sys->NodeLock, flags);
++}
++
++static void
++PositionFound (EP_RAIL *rail, ELAN_POSITION *pos)
++{
++    EP_SYS           *sys = rail->System;
++    struct list_head *el;
++    int i;
++
++    /* only called from the ep_managage whilst rail->State == EP_RAIL_STATE_STARTED */
++    ASSERT ( rail->State == EP_RAIL_STATE_STARTED );
++
++#if defined(PER_CPU_TIMEOUT)
++    /*
++     * On Tru64 - if we're running in a "funnelled" thread, then we will be 
++     * unable to start the per-cpu timeouts, so if we return then eventually
++     * the ep_manager() thread will find the network position and we're
++     * in control of our own destiny.
++     */
++    if (THREAD_IS_FUNNELED(current_thread()))
++    {
++      ep_kthread_schedule (&sys->ManagerThread, lbolt);
++      return;
++    }
++#endif
++
++    sprintf (rail->Name, "ep%d[%d]", rail->Number, pos->pos_nodeid);
++
++    if (pos->pos_levels > MaxSwitchLevels)
++    {
++      for (i = 0; i < (pos->pos_levels - MaxSwitchLevels); i++)
++          pos->pos_nodes /= pos->pos_arity[i];
++
++      for (i = 0; i < MaxSwitchLevels; i++)
++          pos->pos_arity[i] = pos->pos_arity[i + (pos->pos_levels - MaxSwitchLevels)];
++
++      pos->pos_levels = MaxSwitchLevels;
++      pos->pos_nodeid = pos->pos_nodeid % pos->pos_nodes;
++                              
++      printk ("%s: limiting switch levels to %d\n", rail->Name, MaxSwitchLevels);
++      printk ("%s: nodeid=%d level=%d numnodes=%d\n", rail->Name, pos->pos_nodeid, pos->pos_levels, pos->pos_nodes);
++
++      sprintf (rail->Name, "ep%d[%d]", rail->Number, pos->pos_nodeid);
++    }
++
++    if (rail->Position.pos_mode != ELAN_POS_UNKNOWN && rail->Position.pos_nodeid != pos->pos_nodeid)
++    {
++      printk ("%s: NodeId has changed from %d to %d\n", rail->Name, rail->Position.pos_nodeid, pos->pos_nodeid);
++      panic ("ep: PositionFound: NodeId has changed\n");
++    }
++
++    if (sys->Position.pos_mode != ELAN_POS_UNKNOWN && (sys->Position.pos_nodeid != pos->pos_nodeid || sys->Position.pos_nodes != pos->pos_nodes))
++    {
++      printk ("%s: position incompatible - disabling rail\n", rail->Name);
++      rail->State = EP_RAIL_STATE_INCOMPATIBLE;
++      return;
++    }
++
++    if (sys->Position.pos_mode == ELAN_POS_UNKNOWN)
++    {
++      sys->Position = *pos;
++      sys->NodeSet  = statemap_create (pos->pos_nodes);
++      KMEM_ZALLOC (sys->Nodes, EP_NODE *, pos->pos_nodes * sizeof (EP_NODE), 1);
++    }
++
++    rail->Position             = *pos;
++    rail->SwitchBroadcastLevel = pos->pos_levels - 1;
++    rail->State                = EP_RAIL_STATE_RUNNING;
++
++    for (i = 0; i < pos->pos_levels; i++)
++    {
++      rail->SwitchProbeTick[i]   = lbolt;
++      rail->SwitchLast[i].uplink = 4;
++    }
++
++    rail->Operations.PositionFound (rail, pos);
++
++    INIT_LIST_HEAD (&rail->NetworkErrorList);
++    INIT_LIST_HEAD (&rail->LocalPassivateList);
++    INIT_LIST_HEAD (&rail->RemotePassivateList);
++    INIT_LIST_HEAD (&rail->PassivatedList);
++    INIT_LIST_HEAD (&rail->DisconnectingList);
++
++    rail->NodeSet       = statemap_create (rail->Position.pos_nodes);
++    rail->NodeChangeMap = statemap_create (rail->Position.pos_nodes);
++    rail->NodeChangeTmp = statemap_create (rail->Position.pos_nodes);
++
++    KMEM_ZALLOC (rail->Nodes, EP_NODE_RAIL *, rail->Position.pos_nodes * sizeof (EP_NODE_RAIL), 1);
++
++    for (i = 0; i < rail->Position.pos_nodes; i++)
++    {
++      spin_lock_init (&rail->Nodes[i].CookieLock);
++
++      INIT_LIST_HEAD (&rail->Nodes[i].StalledDmas);
++
++      rail->Nodes[i].State = EP_NODE_DISCONNECTED;
++    }
++
++    /* Notify all subsystems that a new rail has been enabled */
++    kmutex_lock (&sys->SubsysLock);
++    list_for_each (el, &sys->Subsystems) { 
++      EP_SUBSYS *subsys = list_entry (el, EP_SUBSYS, Link);
++
++      if (subsys->AddRail)
++          subsys->AddRail (subsys, sys, rail);
++
++      /* XXXX: what to do if the subsystem refused to add the rail ? */
++    }
++    kmutex_unlock (&sys->SubsysLock);
++
++    /* Now enable the manager input queue */
++    ep_enable_inputq (rail, rail->ManagerInputQ);
++}
++
++static void
++ep_manager (void *arg)
++{
++    EP_SYS            *sys = (EP_SYS *) arg;
++    struct list_head *el;
++    ELAN_POSITION     pos;
++    unsigned long     flags;
++
++    kernel_thread_init ("ep_manager");
++    kernel_thread_become_highpri();
++
++    for (;;)
++    {
++      long nextRunTime = lbolt + MSEC2TICKS(CM_THREAD_SCHEDULE_TIMEOUT);
++
++      list_for_each (el, &sys->ManagedRails) {
++          EP_RAIL *rail = list_entry (el, EP_RAIL, ManagerLink);
++
++          switch (rail->State)
++          {
++          case EP_RAIL_STATE_STARTED:
++              if (ProbeNetwork (rail, &pos) == 0)
++              {
++                  PositionFound (rail, &pos);
++                  break;
++              }
++
++              if (nextRunTime == 0 || AFTER (nextRunTime, lbolt + HZ))
++                  nextRunTime = lbolt + HZ;
++              break;
++
++          case EP_RAIL_STATE_RUNNING:
++              if (ep_poll_inputq (rail, rail->ManagerInputQ, 100, ProcessMessage, rail) >= 100)
++                  nextRunTime = lbolt;
++              
++              /* Handle any nodes which the cluster membership subsystem
++               * has indicated are to begin connecting or disconnecting */
++              spin_lock_irqsave (&sys->NodeLock, flags);
++              if (! statemap_changed (rail->NodeChangeMap))
++                  spin_unlock_irqrestore (&sys->NodeLock, flags);
++              else
++              {
++                  /*
++                   * Take a copy of the statemap, and zero all entries so
++                   * we only see new requests next time
++                   */
++                  statemap_copy (rail->NodeChangeTmp, rail->NodeChangeMap);
++                  statemap_zero (rail->NodeChangeMap);
++                  spin_unlock_irqrestore (&sys->NodeLock, flags);
++                  
++                  UpdateConnectionState (rail, rail->NodeChangeTmp);
++              }
++
++              nextRunTime = ProgressNodeLists (rail, nextRunTime);
++
++              if (statemap_changed (rail->NodeSet))
++              {
++                  ep_call_callbacks (rail, EP_CB_NODESET, rail->NodeSet);
++
++                  statemap_clearchanges (rail->NodeSet);
++              }
++              break;
++
++          case EP_RAIL_STATE_INCOMPATIBLE:
++              break;
++          }
++      }
++
++
++      EPRINTF5 (DBG_MANAGER, "ep_manager: sleep now=%lx nextRunTime=%lx (%ld) [%lx (%ld)]\n",
++                lbolt, nextRunTime, nextRunTime ? nextRunTime - lbolt : 0, sys->ManagerThread.next_run,
++                sys->ManagerThread.next_run ? sys->ManagerThread.next_run - lbolt : 0);
++
++      if (ep_kthread_sleep (&sys->ManagerThread, nextRunTime) < 0)
++          break;
++    }
++
++    ep_kthread_stopped (&sys->ManagerThread);
++    kernel_thread_exit();
++}
++
++void
++ep_connect_node (EP_RAIL *rail, int nodeId)
++{
++    EP_SYS       *sys  = rail->System;
++    EP_NODE_RAIL *node = &rail->Nodes[nodeId];
++    unsigned long flags;
++  
++    spin_lock_irqsave (&sys->NodeLock, flags);
++
++    EPRINTF2 (DBG_MANAGER, "%s: ep_connect_node: nodeId %d\n", rail->Name, nodeId);
++
++    ASSERT (node->State == EP_NODE_DISCONNECTED && statemap_getbits (rail->NodeChangeMap, nodeId, 1) == 0);
++    
++    node->State = EP_NODE_CONNECTING;
++
++    statemap_setbits (rail->NodeChangeMap, nodeId, 1, 1);
++
++    spin_unlock_irqrestore (&sys->NodeLock, flags);
++
++    ep_kthread_schedule (&sys->ManagerThread, lbolt);
++}
++
++int
++ep_disconnect_node (EP_RAIL *rail, int nodeId)
++{
++    EP_SYS       *sys  = rail->System;
++    EP_NODE_RAIL *node = &rail->Nodes[nodeId];
++    int                 state;
++    unsigned long flags;
++  
++    spin_lock_irqsave (&sys->NodeLock, flags);
++    
++    EPRINTF3 (DBG_MANAGER, "%s: ep_disconnect_node: nodeId %d - %s\n", rail->Name, nodeId, NodeStateNames[node->State]);
++
++    switch (state = node->State)
++    {
++    case EP_NODE_CONNECTING:
++      statemap_setbits (rail->NodeChangeMap, nodeId, 0, 1);
++
++      node->State = EP_NODE_DISCONNECTED;
++      break;
++      
++    case EP_NODE_CONNECTED:
++      statemap_setbits (rail->NodeChangeMap, nodeId, 1, 1);
++
++      node->State = EP_NODE_LEAVING_CONNECTED;
++      break;
++
++    case EP_NODE_LEAVING_CONNECTED:
++      /* no assert on NodeChangeMap as the map could have been taken but not acted on */
++      break;
++      
++    default:
++      ASSERT (statemap_getbits (rail->NodeChangeMap, nodeId, 1) == 0);
++      break;
++    }
++    spin_unlock_irqrestore (&sys->NodeLock, flags);
++
++    if (state == EP_NODE_CONNECTED)
++      ep_kthread_schedule (&sys->ManagerThread, lbolt);
++
++    return state;
++}
++
++int
++ep_manager_add_rail (EP_SYS *sys, EP_RAIL *rail)
++{
++    if ((rail->ManagerOutputQ = ep_alloc_outputq (rail, EP_MANAGER_MSG_SIZE, EP_MANAGER_OUTPUTQ_SLOTS)) == NULL)
++      return -ENOMEM;
++
++    if ((rail->ManagerInputQ = ep_alloc_inputq (rail, EP_SYSTEMQ_MANAGER, EP_MANAGER_MSG_SIZE, EP_MANAGER_INPUTQ_SLOTS,
++                                               ManagerQueueEvent, &sys->ManagerThread)) == NULL)
++    {
++      ep_free_outputq (rail, rail->ManagerOutputQ);
++      return -ENOMEM;
++    }
++
++    spin_lock_init (&rail->ManagerOutputQLock);
++
++    ep_xid_cache_init (sys, &rail->XidCache);
++
++    ep_kthread_stall (&sys->ManagerThread);
++    list_add_tail (&rail->ManagerLink, &sys->ManagedRails);
++    ep_kthread_resume (&sys->ManagerThread);
++
++    return (0);
++}
++
++void
++ep_manager_remove_rail (EP_SYS *sys, EP_RAIL *rail)
++{
++    if (rail->ManagerInputQ != NULL)
++    {
++      ep_kthread_stall (&sys->ManagerThread);
++      list_del (&rail->ManagerLink);
++      ep_kthread_resume (&sys->ManagerThread);
++
++      ep_xid_cache_destroy (sys, &rail->XidCache);
++
++      spin_lock_destroy (&rail->ManagerOutputQLock);
++
++      ep_disable_inputq (rail, rail->ManagerInputQ);
++      ep_free_inputq (rail, rail->ManagerInputQ);
++      ep_free_outputq (rail, rail->ManagerOutputQ);
++    }
++}
++
++int
++ep_manager_init (EP_SYS *sys)
++{
++    INIT_LIST_HEAD (&sys->ManagedRails);
++
++    ep_kthread_init (&sys->ManagerThread);
++
++    if (kernel_thread_create (ep_manager, (void *) sys) == 0)
++      return (ENOMEM);
++    
++    ep_kthread_started (&sys->ManagerThread);
++    
++    return (0);
++}
++
++void
++ep_manager_fini (EP_SYS *sys)
++{
++    ep_kthread_stop (&sys->ManagerThread);
++    ep_kthread_destroy (&sys->ManagerThread);
++}
++
++int
++ep_sys_init (EP_SYS *sys)
++{
++    kmutex_init (&sys->SubsysLock);   
++    kmutex_init (&sys->StartStopLock);
++    spin_lock_init (&sys->NodeLock);
++
++    INIT_LIST_HEAD (&sys->Subsystems);
++
++    /* initialise the xid allocators */
++    spin_lock_init (&sys->XidLock);
++    INIT_LIST_HEAD (&sys->XidCacheList);
++
++    /* initially don't know where we are in the network */
++    sys->Position.pos_mode = ELAN_POS_UNKNOWN;
++
++    /* initialise the network mapping descriptor hash tables */
++    ep_nmh_init (&sys->MappingTable);
++
++    /* intialise the shared allocators */
++    ep_shared_alloc_init (sys);
++
++    /* initialise the dvma space */
++    ep_dvma_init (sys);
++
++    /* intiialise the rail manager */
++    ep_manager_init (sys);
++
++    /* initialise all subsystems */
++    cm_init (sys);
++    ep_comms_init (sys);
++    //ep_msgsys_init (sys);
++
++    return (0);
++}
++
++void
++ep_sys_fini (EP_SYS *sys)
++{
++    /* Destroy the subsystems in the reverse order to their creation */
++    while (! list_empty (&sys->Subsystems))
++    {
++      EP_SUBSYS *subsys = list_entry (sys->Subsystems.prev, EP_SUBSYS, Link);
++
++      list_del (&subsys->Link);
++      
++      subsys->Destroy (subsys, sys);
++    }
++
++    ep_manager_fini(sys);
++    ep_dvma_fini (sys);
++    ep_shared_alloc_fini (sys);
++
++    ep_nmh_fini (&sys->MappingTable);
++
++    if (sys->Position.pos_mode != ELAN_POS_UNKNOWN) {
++      statemap_destroy (sys->NodeSet);
++      KMEM_FREE(sys->Nodes, sys->Position.pos_nodes * sizeof (EP_NODE));
++    }
++
++    spin_lock_destroy (&sys->XidLock);
++
++    spin_lock_destroy (&sys->NodeLock);
++    kmutex_destroy (&sys->SubsysLock);
++    kmutex_destroy (&sys->StartStopLock);
++}
++
++void
++ep_shutdown (EP_SYS *sys)
++{
++    sys->Shutdown = 1;
++}
++
++int
++ep_init_rail (EP_SYS *sys, EP_RAIL *rail)
++{
++    static int rnum;
++
++    rail->System              = sys;
++    rail->State               = EP_RAIL_STATE_UNINITIALISED;
++    rail->Number              = rnum++;
++    rail->Position.pos_mode   = ELAN_POS_UNKNOWN;
++    rail->Position.pos_nodeid = ELAN_INVALID_NODE;
++
++    rail->CallbackRegistered  = 0;
++
++    sprintf (rail->Name, "ep%d", rail->Number);
++
++    /* Initialise externally visible locks */
++    kmutex_init (&rail->CallbackLock);
++
++    ep_alloc_init (rail);
++
++    sys->Rails[rail->Number] = rail;
++
++    return 0;
++}
++
++void
++ep_destroy_rail (EP_RAIL *rail)
++{
++    ASSERT (rail->State == EP_RAIL_STATE_UNINITIALISED);
++
++    ep_alloc_fini (rail);
++
++    kmutex_destroy (&rail->CallbackLock);
++
++    rail->System->Rails[rail->Number] = NULL;
++
++    rail->Operations.DestroyRail (rail);
++}
++
++/* We need to traverse the Subsystems lists backwards
++ * but it's not defined in <linux/list.h> */
++#define list_for_each_backwards(pos,list) \
++      for (pos = (list)->prev; pos != (list); \
++           pos = (pos)->prev)
++
++void
++__ep_stop_rail (EP_RAIL *rail)
++{
++    /* called holding the sys->Lock */
++    EP_SYS           *sys = rail->System;
++    struct list_head *el;
++
++    rail->Operations.StallRail (rail);
++
++    /* Notify all subsystems that this rail is being stopped */
++    if (rail->State == EP_RAIL_STATE_RUNNING)
++    {
++      kmutex_lock (&sys->SubsysLock);
++      list_for_each_backwards (el, &sys->Subsystems) { 
++          EP_SUBSYS *subsys = list_entry (el, EP_SUBSYS, Link);
++          
++          if (subsys->RemoveRail)
++              subsys->RemoveRail (subsys, sys, rail);
++      }
++      kmutex_unlock (&sys->SubsysLock);
++
++      ep_manager_remove_rail (sys, rail);
++
++      KMEM_FREE (rail->Nodes, rail->Position.pos_nodes * sizeof (EP_NODE_RAIL));
++
++      statemap_destroy (rail->NodeChangeTmp);
++      statemap_destroy (rail->NodeChangeMap);
++      statemap_destroy (rail->NodeSet);
++    }
++
++    ep_dvma_remove_rail (sys, rail);
++    ep_shared_alloc_remove_rail (sys, rail);
++
++    rail->Operations.StopRail (rail);
++
++    rail->State = EP_RAIL_STATE_UNINITIALISED;
++}
++
++void
++ep_stop_rail (EP_RAIL *rail)
++{
++    EP_SYS *sys = rail->System;
++
++    /* stall ep_manager                      */
++    /* and remove the rail from the manaager */
++
++    ep_kthread_stall (&sys->ManagerThread);
++    if ( rail->State == EP_RAIL_STATE_STARTED ) 
++        ep_manager_remove_rail (sys, rail);
++    ep_kthread_resume (&sys->ManagerThread);
++
++    __ep_stop_rail (rail);
++}
++
++int
++ep_start_rail (EP_RAIL *rail)
++{
++    EP_SYS *sys = rail->System;
++
++    ASSERT (rail->State == EP_RAIL_STATE_UNINITIALISED);
++
++    if (rail->Operations.StartRail (rail) < 0)
++      return -ENXIO;
++    
++    kmutex_lock (&sys->StartStopLock);
++    /* Add this rail to the shared allocator */
++    if (ep_shared_alloc_add_rail (rail->System, rail))
++      goto failed;
++
++    /* Add this rail to dvma kmap */
++    if (ep_dvma_add_rail (rail->System, rail))
++      goto failed;
++
++    /* rail is now started */
++    rail->State = EP_RAIL_STATE_STARTED;
++
++    /* notify the rail manager of the new rail */
++    if (ep_manager_add_rail (rail->System, rail))
++      goto failed;
++
++    kmutex_unlock (&sys->StartStopLock);
++    return (ESUCCESS);
++
++ failed:
++    printk ("%s: start failed\n", rail->Name);
++    kmutex_unlock (&sys->StartStopLock);
++    __ep_stop_rail (rail);
++
++    return (ENOMEM);   
++}
++
++void
++ep_subsys_add (EP_SYS *sys, EP_SUBSYS *subsys)
++{
++    kmutex_lock (&sys->SubsysLock);
++    list_add_tail (&subsys->Link, &sys->Subsystems);
++    kmutex_unlock (&sys->SubsysLock);
++}
++
++void
++ep_subsys_del (EP_SYS *sys, EP_SUBSYS *subsys)
++{
++    kmutex_lock (&sys->SubsysLock);
++    list_del (&subsys->Link);
++    kmutex_unlock (&sys->SubsysLock);
++}
++
++EP_SUBSYS *
++ep_subsys_find (EP_SYS *sys, char *name)
++{
++    struct list_head *el;
++
++    ASSERT ( !in_interrupt());
++
++    kmutex_lock (&sys->SubsysLock); 
++    list_for_each (el, &sys->Subsystems) {
++      EP_SUBSYS *subsys = list_entry (el, EP_SUBSYS, Link);
++
++      if (! strcmp (subsys->Name, name))
++      {
++          kmutex_unlock (&sys->SubsysLock);
++          return (subsys);
++      }
++    }
++
++    kmutex_unlock (&sys->SubsysLock);
++    return (NULL);
++}
++
++int
++ep_waitfor_nodeid (EP_SYS *sys)
++{
++    int i, printed = 0;
++    kcondvar_t Sleep;
++    spinlock_t Lock;
++
++    kcondvar_init (&Sleep);
++    spin_lock_init (&Lock);
++
++#define TICKS_TO_WAIT (10*hz)
++#define TICKS_PER_LOOP        (hz/10)
++    for (i = 0; sys->Position.pos_mode == ELAN_POS_UNKNOWN && i < TICKS_TO_WAIT; i += TICKS_PER_LOOP)
++    {
++      if (! printed++)
++          printk ("ep: waiting for network position to be found\n");
++
++      spin_lock (&Lock);
++      kcondvar_timedwait (&Sleep, &Lock, NULL, lbolt + TICKS_PER_LOOP);
++      spin_unlock (&Lock);
++    }
++
++    if (sys->Position.pos_mode == ELAN_POS_UNKNOWN)
++      printk ("ep: network position not found after waiting\n");
++    else if (printed)
++      printk ("ep: network position found at nodeid %d\n", sys->Position.pos_nodeid);
++
++    spin_lock_destroy (&Lock);
++    kcondvar_destroy (&Sleep);
++
++    return (sys->Position.pos_mode == ELAN_POS_UNKNOWN ? ELAN_INVALID_NODE : sys->Position.pos_nodeid);
++}
++
++int
++ep_nodeid (EP_SYS *sys)
++{
++    return (sys->Position.pos_mode == ELAN_POS_UNKNOWN ? ELAN_INVALID_NODE : sys->Position.pos_nodeid);
++}
++
++int
++ep_numnodes (EP_SYS *sys)
++{
++    return (sys->Position.pos_nodes);
++}
++
++void
++ep_fillout_stats(EP_RAIL *r, char *str) 
++{
++    sprintf(str+strlen(str),"SendMessageFailed %lu NeterrAtomicPacket %lu NeterrDmaPacket %lu \n", r->Stats.SendMessageFailed, r->Stats.NeterrAtomicPacket, r->Stats.NeterrDmaPacket);
++    sprintf(str+strlen(str),"Rx %lu  %lu /sec\n",   GET_STAT_TOTAL(r->Stats,rx), GET_STAT_PER_SEC(r->Stats,rx) ); 
++    sprintf(str+strlen(str),"MBytes %lu  %lu MB/sec\n", GET_STAT_TOTAL(r->Stats,rx_len)/ (1024*1024), GET_STAT_PER_SEC(r->Stats,rx_len) / (1024*1024)); 
++    sprintf(str+strlen(str),"Tx %lu  %lu /sec\n",   GET_STAT_TOTAL(r->Stats,tx), GET_STAT_PER_SEC(r->Stats,tx) ); 
++    sprintf(str+strlen(str),"MBytes %lu  %lu MB/sec\n", GET_STAT_TOTAL(r->Stats,tx_len)/ (1024*1024), GET_STAT_PER_SEC(r->Stats,tx_len) / (1024*1024)); 
++}
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/ep/kcomm_elan3.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/kcomm_elan3.c       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/kcomm_elan3.c    2005-06-01 23:12:54.665429072 -0400
+@@ -0,0 +1,504 @@
++
++/*
++ *    Copyright (c) 2003 by Quadrics Ltd.
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: kcomm_elan3.c,v 1.31.8.3 2004/11/30 12:02:17 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/kcomm_elan3.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++
++#include "kcomm_vp.h"
++#include "kcomm_elan3.h"
++#include "conf_linux.h"
++
++extern EP_CODE threadcode_elan3;
++
++unsigned int
++ep3_create_rails (EP_SYS *sys, unsigned int disabled)
++{
++    unsigned int rmask = 0;
++    ELAN3_DEV   *dev;
++    EP_RAIL     *rail;
++    int          i;
++
++    for (i = 0; i < EP_MAX_RAILS; i++)
++    {
++      if ((dev = elan3_device (i)) != NULL)
++      {
++          if ((rail = ep3_create_rail (sys, dev)) != NULL)
++          {
++              if (disabled & (1 << rail->Number))
++                  printk ("%s: auto-start of device disabled by configuration\n", rail->Name);
++              else
++                  ep_start_rail (rail);
++              
++              ep_procfs_rail_init(rail);
++
++              rmask |= (1 << rail->Number);
++          }
++      }
++    }
++
++    return rmask;
++}
++
++EP_RAIL *
++ep3_create_rail (EP_SYS *sys, ELAN3_DEV *dev)
++{
++    EP3_RAIL *rail;
++    int       res;
++
++    KMEM_ZALLOC (rail, EP3_RAIL *, sizeof (EP3_RAIL), TRUE);
++
++    if (rail == NULL)
++      return (EP_RAIL *) NULL;
++
++    if ((res = ep_init_rail (sys, &rail->Generic)) != 0)
++    {
++      KMEM_FREE (rail, sizeof (EP3_RAIL));
++      return (EP_RAIL *) NULL;
++    }
++
++    rail->Device = dev;
++
++    /* Install our rail operations */
++    rail->Generic.Operations.DestroyRail      = ep3_destroy_rail;
++    rail->Generic.Operations.StartRail        = ep3_start_rail;
++    rail->Generic.Operations.StallRail        = ep3_stall_rail;
++    rail->Generic.Operations.StopRail         = ep3_stop_rail;
++
++    rail->Generic.Operations.SdramAlloc       = ep3_sdram_alloc;
++    rail->Generic.Operations.SdramFree        = ep3_sdram_free;
++    rail->Generic.Operations.SdramWriteb      = ep3_sdram_writeb;
++
++    rail->Generic.Operations.KaddrMap         = ep3_kaddr_map;
++    rail->Generic.Operations.SdramMap         = ep3_sdram_map;
++    rail->Generic.Operations.Unmap            = ep3_unmap;
++
++    rail->Generic.Operations.DvmaReserve      = ep3_dvma_reserve;
++    rail->Generic.Operations.DvmaRelease      = ep3_dvma_release;
++    rail->Generic.Operations.DvmaSetPte       = ep3_dvma_set_pte;
++    rail->Generic.Operations.DvmaReadPte      = ep3_dvma_read_pte;
++    rail->Generic.Operations.DvmaUnload       = ep3_dvma_unload;
++    rail->Generic.Operations.FlushTlb         = ep3_flush_tlb;
++
++    rail->Generic.Operations.ProbeRoute       = ep3_probe_route;
++    rail->Generic.Operations.PositionFound    = ep3_position_found;
++    rail->Generic.Operations.CheckPosition    = ep3_check_position;
++    rail->Generic.Operations.NeterrFixup      = ep3_neterr_fixup;
++
++    rail->Generic.Operations.LoadSystemRoute  = ep3_load_system_route;
++
++    rail->Generic.Operations.LoadNodeRoute    = ep3_load_node_route;
++    rail->Generic.Operations.UnloadNodeRoute  = ep3_unload_node_route;
++    rail->Generic.Operations.LowerFilter      = ep3_lower_filter;
++    rail->Generic.Operations.RaiseFilter      = ep3_raise_filter;
++    rail->Generic.Operations.NodeDisconnected = ep3_node_disconnected;
++
++    rail->Generic.Operations.FlushFilters     = ep3_flush_filters;
++    rail->Generic.Operations.FlushQueues      = ep3_flush_queues;
++
++    rail->Generic.Operations.AllocInputQ      = ep3_alloc_inputq;
++    rail->Generic.Operations.FreeInputQ       = ep3_free_inputq;
++    rail->Generic.Operations.EnableInputQ     = ep3_enable_inputq;
++    rail->Generic.Operations.DisableInputQ    = ep3_disable_inputq;
++    rail->Generic.Operations.PollInputQ       = ep3_poll_inputq;
++
++    rail->Generic.Operations.AllocOutputQ     = ep3_alloc_outputq;
++    rail->Generic.Operations.FreeOutputQ      = ep3_free_outputq;
++    rail->Generic.Operations.OutputQMsg       = ep3_outputq_msg;
++    rail->Generic.Operations.OutputQState     = ep3_outputq_state;
++    rail->Generic.Operations.OutputQSend      = ep3_outputq_send;
++
++    rail->Generic.Operations.FillOutStats     = ep3_fillout_stats;
++
++    rail->Generic.Devinfo = dev->Devinfo;
++
++    printk ("%s: connected via elan3 rev%c device %d\n", rail->Generic.Name,
++          'a' + dev->Devinfo.dev_revision_id, dev->Instance);
++
++    return (EP_RAIL *) rail;
++}
++
++void
++ep3_destroy_rail (EP_RAIL *r)
++{
++    EP3_RAIL *rail = (EP3_RAIL *) r;
++    
++    KMEM_FREE (rail, sizeof (EP3_RAIL));
++}
++
++static int
++ep3_attach_rail (EP3_RAIL *rail)
++{
++    ELAN3_DEV        *dev = rail->Device;
++    ELAN3_CTXT       *ctxt;
++    ELAN_CAPABILITY  *cap;
++    int               ctx;
++    unsigned long     flags;
++
++    if ((ctxt = elan3_alloc (dev, TRUE)) == (ELAN3_CTXT *) NULL)
++    {
++      printk ("%s: cannot allocate elan context\n", rail->Generic.Name);
++      return -ENXIO;
++    }
++    
++    ctxt->Operations = &ep3_elan3_ops;
++    ctxt->Private    = (void *) rail;
++    
++    /* Initialise a capability and attach to the elan*/
++    KMEM_ALLOC (cap, ELAN_CAPABILITY *, sizeof (ELAN_CAPABILITY), TRUE);
++    
++    elan_nullcap (cap);
++    
++    cap->cap_type        = ELAN_CAP_TYPE_KERNEL;
++    cap->cap_version     = ELAN_CAP_VERSION_NUMBER;
++    cap->cap_mycontext   = ELAN3_MRF_CONTEXT_NUM | SYS_CONTEXT_BIT;
++    cap->cap_lowcontext  = ELAN3_MRF_CONTEXT_NUM | SYS_CONTEXT_BIT;
++    cap->cap_highcontext = ELAN3_MRF_CONTEXT_NUM | SYS_CONTEXT_BIT;
++    cap->cap_railmask    = 1 << dev->Devinfo.dev_rail;
++    
++    /* Ensure the context filter is raised while we initialise */
++    elan3_block_inputter (ctxt, TRUE);
++
++    if (elan3_doattach (ctxt, cap) != 0)
++    {
++      printk ("%s: cannot attach to kernel context\n", rail->Generic.Name);
++
++      KMEM_FREE (cap, sizeof (ELAN_CAPABILITY));
++      elan3_free (ctxt);
++      return -ENXIO;
++    }
++    KMEM_FREE (cap, sizeof (ELAN_CAPABILITY));
++
++    /* now attach to all the kernel comms input/dmaring/data contexts */
++    spin_lock_irqsave (&dev->IntrLock, flags);
++
++    for (ctx = ELAN3_DMARING_BASE_CONTEXT_NUM; ctx <= ELAN3_DMARING_TOP_CONTEXT_NUM; ctx++)
++    {
++      /* place it in the info table.  NOTE: don't call elan3mmu_set_info, as this */
++      /* will queue the info again on the devices info list */
++      dev->CtxtTable[ctx] = ctxt;
++      
++      elan3mmu_set_context_filter (dev, ctx|SYS_CONTEXT_BIT, TRUE, 0, NULL);
++      elan3mmu_attach (dev, ctx, ctxt->Elan3mmu, ctxt->RouteTable->Table, ctxt->RouteTable->Size-1);
++    }
++
++    for (ctx = ELAN3_KCOMM_BASE_CONTEXT_NUM; ctx <= ELAN3_KCOMM_TOP_CONTEXT_NUM; ctx++)
++    {
++      /* place it in the info table.  NOTE: don't call elan3mmu_set_info, as this */
++      /* will queue the info again on the devices info list */
++      dev->CtxtTable[ctx] = ctxt;
++      
++      elan3mmu_set_context_filter (dev, ctx|SYS_CONTEXT_BIT, TRUE, 0, NULL);
++      elan3mmu_attach (dev, ctx, ctxt->Elan3mmu, ctxt->RouteTable->Table, ctxt->RouteTable->Size-1);
++    }
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++
++    /* Stash the ctxt,commandport, mmu and route table */
++    rail->Ctxt        = ctxt;
++    rail->CommandPort = ctxt->CommandPort;
++    rail->Elan3mmu    = ctxt->Elan3mmu;
++    rail->RouteTable  = ctxt->RouteTable;
++
++    return 0;
++}
++
++static void
++ep3_detach_rail (EP3_RAIL *rail)
++{
++    ELAN3_DEV *dev = rail->Device;
++    unsigned long flags;
++    int ctx;
++
++    /* detach from the elan */
++    spin_lock_irqsave (&dev->IntrLock, flags);
++
++    for (ctx = ELAN3_KCOMM_BASE_CONTEXT_NUM; ctx <= ELAN3_KCOMM_TOP_CONTEXT_NUM; ctx++)
++    {
++      dev->CtxtTable[ctx] = NULL;
++      elan3mmu_detach (dev, ctx);
++    }
++
++    for (ctx = ELAN3_DMARING_BASE_CONTEXT_NUM; ctx <= ELAN3_DMARING_TOP_CONTEXT_NUM; ctx++)
++    {
++      dev->CtxtTable[ctx] = NULL;
++      elan3mmu_detach (dev, ctx);
++    }
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++
++    elan3_dodetach(rail->Ctxt);
++    elan3_free (rail->Ctxt);
++
++    rail->Ctxt        = NULL;
++    rail->CommandPort = 0;
++    rail->Elan3mmu    = NULL;
++    rail->RouteTable  = NULL;
++}
++
++int
++ep3_start_rail (EP_RAIL *r)
++{
++    EP3_RAIL     *rail = (EP3_RAIL *) r;
++    int           i, res;
++    unsigned long flags;
++
++    if ((res = ep3_attach_rail (rail)) != 0)
++      return res;
++
++    spin_lock_init (&rail->CookieLock);
++    kmutex_init (&rail->HaltOpMutex);
++    kcondvar_init (&rail->HaltOpSleep);
++
++    /* Initialise event interrupt cookie table */
++    InitialiseCookieTable (&rail->CookieTable);
++
++    /* Load and map the thread code */
++    rail->ThreadCode = threadcode_elan3;
++    if (ep_loadcode (&rail->Generic, &rail->ThreadCode) != ESUCCESS)
++      goto failed;
++
++    /* Map the command port to be visible to the Elan */
++    ep3_ioaddr_map (&rail->Generic, EP3_COMMANDPORT_ADDR, rail->Ctxt->CommandPage, PAGESIZE, EP_PERM_WRITE);
++    rail->CommandPortAddr = EP3_COMMANDPORT_ADDR + (rail->Ctxt->CommandPort - rail->Ctxt->CommandPage);
++
++    /* Allocate the elan visible sdram/main memory */
++    if ((rail->RailElan = ep_alloc_elan (&rail->Generic, sizeof (EP3_RAIL_ELAN), 0, &rail->RailElanAddr)) == 0 ||
++      (rail->RailMain = ep_alloc_main (&rail->Generic, sizeof (EP3_RAIL_MAIN), 0, &rail->RailMainAddr)) == 0)
++    {
++      goto failed;
++    }
++
++    /* Allocate the system input queues at their fixed elan address */
++    if (! (rail->QueueDescs = ep_alloc_memory_elan (&rail->Generic, EP_SYSTEM_QUEUE_BASE, PAGESIZE, EP_PERM_ALL, 0)))
++      goto failed;
++
++    /* Initialise all queue entries to be full */
++    for (i = 0; i < EP_NUM_SYSTEMQ; i++)
++      elan3_sdram_writel (rail->Device, EP_SYSTEMQ_DESC(rail->QueueDescs, i) + offsetof (EP3_InputQueue, q_state), E3_QUEUE_FULL);
++
++    /* initialise the dma rings */
++    if (DmaRingsCreate (rail))
++      goto failed;
++    
++    if (InitialiseDmaRetries (rail))
++      goto failed;
++
++    if (ep3_init_probenetwork (rail))
++      goto failed;
++
++    /* can now drop the context filter for the system context */
++    spin_lock_irqsave (&rail->Device->IntrLock, flags);
++    elan3mmu_set_context_filter (rail->Device, ELAN3_MRF_CONTEXT_NUM|SYS_CONTEXT_BIT, FALSE, 0, NULL);
++    spin_unlock_irqrestore (&rail->Device->IntrLock, flags);
++
++    return 0;
++
++ failed:
++    printk ("ep3_start_rail: failed for rail %d\n", rail->Generic.Number);
++    ep3_stop_rail (&rail->Generic);
++
++    return -ENOMEM;
++}
++
++void
++ep3_stall_rail (EP_RAIL *r)
++{
++    EP3_RAIL     *rail = (EP3_RAIL *) r;
++    int           ctx;
++    unsigned long flags;
++
++    /* raise all the context filters */
++    spin_lock_irqsave (&rail->Device->IntrLock, flags);
++
++    for (ctx = ELAN3_KCOMM_BASE_CONTEXT_NUM; ctx <= ELAN3_KCOMM_TOP_CONTEXT_NUM; ctx++)
++      elan3mmu_set_context_filter (rail->Device, ctx|SYS_CONTEXT_BIT, TRUE, 0, NULL);
++
++    for (ctx = ELAN3_DMARING_BASE_CONTEXT_NUM; ctx <= ELAN3_DMARING_TOP_CONTEXT_NUM; ctx++)
++      elan3mmu_set_context_filter (rail->Device, ctx|SYS_CONTEXT_BIT, TRUE, 0, NULL);
++
++    elan3mmu_set_context_filter (rail->Device, ELAN3_MRF_CONTEXT_NUM|SYS_CONTEXT_BIT, TRUE, 0, NULL);
++
++    spin_unlock_irqrestore (&rail->Device->IntrLock, flags);
++}
++
++void
++ep3_stop_rail (EP_RAIL *r)
++{
++    EP3_RAIL *rail = (EP3_RAIL *) r;
++
++    ep3_destroy_probenetwork (rail);
++
++    if (rail->DmaRetryInitialised)
++      DestroyDmaRetries (rail);
++
++    DmaRingsRelease(rail);
++
++    if (rail->Generic.State == EP_RAIL_STATE_RUNNING)
++    {
++      KMEM_FREE (rail->MainCookies, rail->Generic.Position.pos_nodes * sizeof (E3_uint32));
++
++      ep_free_elan (&rail->Generic, rail->ElanCookies, rail->Generic.Position.pos_nodes * sizeof (E3_uint32));
++    }
++
++    if (rail->QueueDescs)
++      ep_free_memory_elan (&rail->Generic, EP_SYSTEM_QUEUE_BASE);
++    rail->QueueDescs = 0;
++
++    if (rail->RailMain)
++      ep_free_main (&rail->Generic, rail->RailMainAddr, sizeof (EP3_RAIL_MAIN));
++    rail->RailMain = 0;
++
++    if (rail->RailElan)
++      ep_free_elan (&rail->Generic, rail->RailElanAddr, sizeof (EP3_RAIL_ELAN));
++    rail->RailElan = 0;
++
++    ep_unloadcode (&rail->Generic, &rail->ThreadCode);
++
++    DestroyCookieTable (&rail->CookieTable);
++
++    ep_perrail_unmap (&rail->Generic, rail->Ctxt->CommandPage, PAGESIZE);
++
++    kcondvar_destroy (&rail->HaltOpSleep);
++    kmutex_destroy (&rail->HaltOpMutex);
++    spin_lock_destroy (&rail->CookieLock);
++
++    ep3_detach_rail (rail);
++}
++
++void
++ep3_position_found (EP_RAIL *r, ELAN_POSITION *pos)
++{
++    EP3_RAIL   *rail = (EP3_RAIL *) r;
++    sdramaddr_t addr;
++
++    rail->SwitchBroadcastLevelTick = lbolt;
++
++    elan3_sdram_writel (rail->Device, rail->RailElan + offsetof (EP3_RAIL_ELAN, NodeId), pos->pos_nodeid);
++
++    /* Allocate Network Identify cookie state */
++    KMEM_ZALLOC (rail->MainCookies, E3_uint32 *, pos->pos_nodes * sizeof (E3_uint32), 1);
++    
++    if (! (addr = ep_alloc_elan (&rail->Generic, pos->pos_nodes * sizeof (E3_uint32), 0, &rail->ElanCookies)))
++      panic ("ep: PositionFound: cannot allocate elan cookies array\n");
++
++    elan3_sdram_zeroq_sdram (rail->Device, addr, pos->pos_nodes * sizeof (E3_uint32));
++
++    ep3_probe_position_found (rail, pos);
++}
++
++sdramaddr_t
++ep3_sdram_alloc (EP_RAIL *r, EP_ADDR addr, unsigned size)
++{
++    return elan3_sdram_alloc (((EP3_RAIL *) r)->Device, size);
++}
++
++void
++ep3_sdram_free (EP_RAIL *r, sdramaddr_t addr, unsigned size)
++{
++    elan3_sdram_free (((EP3_RAIL *) r)->Device, addr, size);
++}
++
++void
++ep3_sdram_writeb (EP_RAIL *r, sdramaddr_t addr, unsigned char val)
++{
++    elan3_sdram_writeb (((EP3_RAIL *) r)->Device, addr, val);
++}
++
++void
++ep3_flush_tlb (EP_RAIL *r)
++{
++    EP3_RAIL *rail = (EP3_RAIL *) r;
++    ELAN3_DEV *dev = rail->Device;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->TlbLock, flags);
++    
++    IncrStat (dev, TlbFlushes);
++    
++    write_reg32 (dev, Cache_Control_Reg.ContReg, dev->Cache_Control_Reg | MMU_FLUSH);
++    mmiob ();
++    spin_unlock_irqrestore (&dev->TlbLock, flags);
++
++    while (! (read_reg32 (dev, Cache_Control_Reg.ContReg) & MMU_FLUSHED))
++      mb();
++}
++
++void
++ep3_load_system_route (EP_RAIL *r, unsigned vp, unsigned lowNode, unsigned highNode)
++{
++    EP3_RAIL  *rail = (EP3_RAIL *) r;
++    E3_uint16  flits[MAX_FLITS];
++    int        nflits;
++    
++    nflits = GenerateRoute (&rail->Generic.Position, flits, lowNode, highNode, DEFAULT_ROUTE_TIMEOUT, HIGH_ROUTE_PRIORITY);
++      
++    if (LoadRoute (rail->Device, rail->RouteTable, vp, ELAN3_MRF_CONTEXT_NUM|SYS_CONTEXT_BIT, nflits, flits) != 0)
++    {
++      /* XXXX: whilst LoadRoute() can fail - it is not likely. */
++      panic ("ep3_load_system_route: cannot load p2p route entry\n");
++    }
++}
++
++void
++ep3_load_node_route (EP_RAIL *r, unsigned nodeId)
++{
++    EP3_RAIL     *rail = (EP3_RAIL *) r;
++    E3_uint16     flits[MAX_FLITS];
++    int           nflits;
++
++    nflits = GenerateRoute (&rail->Generic.Position, flits, nodeId, nodeId, DEFAULT_ROUTE_TIMEOUT, DEFAULT_ROUTE_PRIORITY);
++
++    if (LoadRoute (rail->Device, rail->RouteTable, EP_VP_DATA(nodeId), EP3_CONTEXT_NUM(rail->Generic.Position.pos_nodeid), nflits, flits) != 0)
++      panic ("ep3_load_node_route: cannot load p2p data route entry\n");
++}
++
++void
++ep3_unload_node_route (EP_RAIL *r, unsigned nodeId)
++{
++    EP3_RAIL *rail = (EP3_RAIL *) r;
++
++    ClearRoute (rail->Device, rail->RouteTable, EP_VP_DATA(nodeId));
++}
++
++void
++ep3_lower_filter (EP_RAIL *r, unsigned nodeId)
++{
++    EP3_RAIL *rail = (EP3_RAIL *) r;
++    unsigned long flags;
++
++    spin_lock_irqsave (&rail->Device->IntrLock, flags);
++    elan3mmu_set_context_filter (rail->Device, EP3_CONTEXT_NUM(nodeId), 0, 0, NULL);
++    spin_unlock_irqrestore (&rail->Device->IntrLock, flags);
++}
++
++void
++ep3_raise_filter (EP_RAIL *r, unsigned nodeId)
++{
++    EP3_RAIL *rail = (EP3_RAIL *) r;
++    unsigned long flags;
++
++    spin_lock_irqsave (&rail->Device->IntrLock, flags);
++    elan3mmu_set_context_filter (rail->Device, EP3_CONTEXT_NUM(nodeId), 1, 0, NULL);
++    spin_unlock_irqrestore (&rail->Device->IntrLock, flags);
++}
++
++void
++ep3_node_disconnected (EP_RAIL *r, unsigned nodeId)
++{
++    FreeStalledDmas ((EP3_RAIL *) r, nodeId);
++}
++
++void
++ep3_fillout_stats(EP_RAIL *r, char *str) 
++{
++    /* no stats here yet */
++    /* EP3_RAIL *ep3rail = (EP3_RAIL *)r; */
++}
+Index: linux-2.4.21/drivers/net/qsnet/ep/kcomm_elan3.h
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/kcomm_elan3.h       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/kcomm_elan3.h    2005-06-01 23:12:54.666428920 -0400
+@@ -0,0 +1,431 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __EP_KCOMM_ELAN3_H
++#define __EP_KCOMM_ELAN3_H
++
++#ident "@(#)$Id: kcomm_elan3.h,v 1.50.8.3 2004/12/14 10:19:14 mike Exp $ $Name: QSNETMODULES-4-30_20050128 $"
++/*      $Source: /cvs/master/quadrics/epmod/kcomm_elan3.h,v $*/
++
++#if !defined(__ELAN3__)
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elanvp.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanctxt.h>
++#include <elan3/elandebug.h>
++#endif /* !defined(__ELAN3__) */
++
++#include <elan3/trtype.h>
++
++/* private address allocation */
++#define EP3_TEXT_BASE                 0xFF000000              /* base address for thread code (defined in makerules.elan3) */
++#define EP3_COMMANDPORT_ADDR          0xFFF00000              /* mapping address for elan command port */
++
++#define EP3_STACK_SIZE                        1024                    /* default thread code stack size */
++
++#define EP3_PACEMAKER_EVENTADDR               0xfeedbeef              /* mis-aligned address used by heartbeat pacemaker */
++
++/* context number allocation */
++#define EP3_CONTEXT_NUM(nodeId)               ((ELAN3_KCOMM_BASE_CONTEXT_NUM + (nodeId)) | SYS_CONTEXT_BIT)
++#define EP3_CONTEXT_ISDATA(ctx)               (((ctx) & MAX_ROOT_CONTEXT_MASK) >= ELAN3_KCOMM_BASE_CONTEXT_NUM && \
++                                       ((ctx) & MAX_ROOT_CONTEXT_MASK) <= ELAN3_KCOMM_TOP_CONTEXT_NUM)
++#define EP3_CONTEXT_TO_NODE(ctx)      (((ctx) & MAX_ROOT_CONTEXT_MASK) - ELAN3_KCOMM_BASE_CONTEXT_NUM)
++
++/* DMA issueing rings */
++#define EP3_RING_CRITICAL             0
++#define EP3_RING_CRITICAL_LEN         128
++#define EP3_RING_HIGH_PRI             1
++#define EP3_RING_HIGH_PRI_LEN         64
++#define EP3_RING_LOW_PRI              2
++#define EP3_RING_LOW_PRI_LEN          32
++#define EP3_NUM_RINGS                 3
++
++/* Value to "return" from c_close() when envelope handled  by the trap handler */
++#define EP3_PAckStolen                        4
++
++/* unimplemented instruction trap types for thread code */
++#define EP3_UNIMP_TRAP_NO_DESCS               0
++#define EP3_UNIMP_TRAP_PACKET_NACKED  1
++#define EP3_UNIMP_THREAD_HALTED               2
++#define EP3_NUM_UNIMP_TRAPS           3
++
++/* forward declarations */
++typedef struct ep3_rail       EP3_RAIL;
++
++/* block copy elan3 inputter queue - with waitvent0 */
++typedef struct ep3_inputqueue
++{
++    volatile E3_uint32        q_state;        /* queue is full=bit0, queue is locked=bit8 */
++    volatile E3_Addr  q_bptr;         /* block aligned ptr to current back item */
++    E3_uint32         q_size;         /* size of queue item; 0x1 <= size <= (0x40 * 5) */
++    E3_Addr           q_top;          /* block aligned ptr to last queue item */
++    E3_Addr           q_base;         /* block aligned ptr to first queue item */
++    volatile E3_Addr  q_fptr;         /* block aligned ptr to current front item */
++    E3_BlockCopyEvent q_event;        /* queue block copy event */
++    E3_uint32         q_pad[4];       /* pad to 64 bytes */
++    E3_Addr           q_wevent;       /* WaitEvent0 struct */
++    E3_int32          q_wcount;
++} EP3_InputQueue;
++
++
++#if !defined(__ELAN3__)
++
++/* dma retries types and retry times */
++typedef struct ep3_retry_dma
++{
++    struct list_head    Link;                                 /* chained on free/retry list */
++    long              RetryTime;                              /* "lbolt" to retry at */
++    E3_DMA_BE         Dma;                                    /* DMA (in main memory) */
++} EP3_RETRY_DMA;
++
++typedef struct ep3_dma_ring
++{
++    sdramaddr_t               pEvent;  
++    E3_Addr           epEvent;
++    
++    sdramaddr_t               pDma;     
++    E3_Addr           epDma; 
++    
++    E3_uint32        *pDoneBlk; 
++    E3_Addr           epDoneBlk; 
++    
++    int                       Entries;                                /* number of slots in array  */
++    int                       Position;                               /* current position in array */
++
++    ioaddr_t            CommandPort;
++    ioaddr_t          CommandPage;
++    DeviceMappingHandle CommandPageHandle;
++} EP3_DMA_RING;
++
++#define DMA_RING_EVENT(ring,n)                ((ring)->pEvent + (n)*sizeof (E3_BlockCopyEvent))
++#define DMA_RING_EVENT_ELAN(ring,n)   ((ring)->epEvent + (n)*sizeof (E3_BlockCopyEvent))
++
++#define DMA_RING_DMA(ring,n)          ((ring)->pDma   + (n)*sizeof (E3_DMA))
++#define DMA_RING_DMA_ELAN(ring,n)     ((ring)->epDma   + (n)*sizeof (E3_DMA))
++
++#define DMA_RING_DONE_ELAN(ring,n)    ((ring)->epDoneBlk + (n)*sizeof (E3_uint32))
++
++/* Event interrupt cookie operations and lookup table */
++typedef struct ep3_cookie_ops
++{
++    void      (*Event)       (EP3_RAIL *rail, void *arg);                             /* called from the interrupt handler when an event is "set" */
++    void      (*DmaRetry)    (EP3_RAIL *rail, void *arg, E3_DMA_BE *dma, int error);  /* called from the interrupt handler when a DMA is "nacked" */
++    void      (*DmaCancelled)(EP3_RAIL *rail, void *arg, E3_DMA_BE *dma);             /* called from the interrupt handler/flush disconnecting when cancelled. */
++    void      (*DmaVerify)   (EP3_RAIL *rail, void *arg, E3_DMA_BE *dma);             /* called from multiple places, to check dma is consistent with state. */
++} EP3_COOKIE_OPS;
++
++typedef struct ep3_cookie
++{
++    struct ep3_cookie  *Next;                                 /* Cookies are chained in hash table. */
++    E3_uint32         Cookie;                                 /* Cooke store in ev_Type */
++    EP3_COOKIE_OPS     *Operations;                           /* Cookie operations */
++    void             *Arg;                                    /* Users arguement. */
++} EP3_COOKIE;
++
++#define EP3_COOKIE_HASH_SIZE          (256)
++#define EP3_HASH_COOKIE(a)            ((((a) >> 3) ^ ((a) >> 7) ^ ((a) >> 11)) & (EP3_COOKIE_HASH_SIZE-1))
++
++typedef struct ep3_cookie_table
++{
++    spinlock_t                Lock;
++    EP3_COOKIE               *Entries[EP3_COOKIE_HASH_SIZE];
++} EP3_COOKIE_TABLE;
++
++#endif /* !defined(__ELAN3__) */
++
++#define EP3_EVENT_FREE                        ((1 << 4) | EV_WCOPY)
++#define EP3_EVENT_ACTIVE              ((2 << 4) | EV_WCOPY)
++/* DONE == Cookie */
++#define EP3_EVENT_FAILED              ((3 << 4) | EV_WCOPY)
++#define EP3_EVENT_PRIVATE             ((4 << 4) | EV_WCOPY)
++
++/* The event cookie can get posted (and seen) before the write has */
++/* hit main memory - in this case the event count is <= 0 and the block */
++/* will be marked as ACTIVE - but could transition to DONE at any time */
++/* Also for a word copy event, the value written into the "done" word */
++/* can be the event interrupt cookie rather than the "source" value */
++/* this happens since the uCode does not wait for the write to have */
++/* occured before overwriting TMP_0 with the cookie */
++#define EP3_EVENT_FIRING(edev, event, cookie, done) \
++      (((((done) & ~(EV_TYPE_BCOPY | EV_TYPE_MASK_EVIRQ)) == (cookie).Cookie) || (done) == EP3_EVENT_ACTIVE) && \
++       (int) elan3_sdram_readl (edev, (event) + offsetof (E3_BlockCopyEvent, ev_Count)) <= 0)
++#define EP3_EVENT_FIRED(cookie, done) \
++      (((done) & ~(EV_TYPE_BCOPY | EV_TYPE_MASK_EVIRQ)) == (cookie).Cookie)
++
++
++/* Time limit to wait while event is firing and block write has not occured */
++#define EP3_EVENT_FIRING_TLIMIT               16384                   /* 1023 uS */
++
++#define EP3_INIT_COPY_EVENT(event, cookie, dest, intr)                                                        \
++{                                                                                                     \
++      (event).ev_Count  = 0;                                                                          \
++      (event).ev_Type   = (intr) ? EV_TYPE_BCOPY | EV_TYPE_EVIRQ | (cookie).Cookie : EV_TYPE_BCOPY;   \
++      (event).ev_Source = (cookie).Cookie | EV_WCOPY;                                                 \
++      (event).ev_Dest   = (dest) | EV_TYPE_BCOPY_WORD;                                                \
++}
++
++#if !defined(__ELAN3__)
++
++/* Generic input queues which can be polled */
++typedef struct ep3_inputq
++{
++    EP3_COOKIE                q_cookie;
++    unsigned int      q_slotSize;
++    unsigned int      q_slotCount;
++
++    void             *q_slots;
++    EP_ADDR           q_slotsAddr;
++
++    EP_INPUTQ_CALLBACK *q_callback;
++    void             *q_arg;
++
++    sdramaddr_t               q_desc;
++    E3_Addr           q_descAddr;
++
++    E3_Addr           q_base;
++    E3_Addr           q_top;
++    E3_Addr           q_fptr;
++
++    E3_uint32         q_waitCount;
++} EP3_INPUTQ;
++
++typedef struct ep3_outputq
++{
++    EP3_COOKIE                q_cookie;
++
++    unsigned int      q_slotCount;                            /* # slots allocated */
++    unsigned int      q_slotSize;                             /* size of each slot (rounded up) */
++
++    sdramaddr_t               q_elan;
++    E3_Addr           q_elanAddr;
++
++    void             *q_main;
++    E3_Addr           q_mainAddr;
++} EP3_OUTPUTQ;
++
++#endif /* !defined(__ELAN3__) */
++
++/* per-rail elan memory portion of device */
++typedef struct ep3_rail_elan
++{
++    E3_uint16          ProbeSource0[TR_TRACEROUTE_ENTRIES];   /* 32 byte aligned */
++    E3_uint16          ProbeSource1[TR_TRACEROUTE_ENTRIES];
++
++    E3_BlockCopyEvent  ProbeDone;                             /* 16 byte aligned */
++    E3_Event           ProbeStart;                            /* 8 byte aligned */
++    
++    E3_uint32          ProbeType;                             /* 4 byte aligned */
++    E3_uint32          ProbeLevel;
++
++    E3_uint32          NodeId;
++} EP3_RAIL_ELAN;
++
++/* values for ProbeType */
++#define PROBE_SINGLE                  0
++#define PROBE_MULTIPLE                        1
++/* number of attempts for each type */
++#define PROBE_SINGLE_ATTEMPTS         10
++#define PROBE_SINGLE_TIMEOUTS         5
++#define PROBE_MULTIPLE_ATTEMPTS               20
++#define PROBE_MULTIPLE_TIMEOUTS               10
++
++/* per-rail elan memory portsion of device */
++typedef struct ep3_rail_main
++{
++    E3_uint16         ProbeDest0[TR_TRACEROUTE_ENTRIES];      /* 32 byte aligned */
++    E3_uint16         ProbeDest1[TR_TRACEROUTE_ENTRIES];
++    
++    E3_uint32         ProbeDone;                              /* 4 byte aligned */
++    E3_uint32         ProbeResult;
++    E3_uint32         ProbeLevel;
++} EP3_RAIL_MAIN;
++
++#if !defined(__ELAN3__)
++
++struct ep3_rail
++{
++    EP_RAIL           Generic;                                /* Generic rail */
++
++    ELAN3_DEV          *Device;                                       /* Elan device we're using */
++    ELAN3_CTXT               *Ctxt;                                   /* Elan context struct */
++    ioaddr_t            CommandPort;                          /* commandport from context */
++    E3_Addr           CommandPortAddr;                        /*  and address mapped into elan */
++
++    ELAN3_ROUTE_TABLE  *RouteTable;                           /* routetable from context */
++    ELAN3MMU         *Elan3mmu;                               /* elanmmu from context */
++
++    EP3_COOKIE_TABLE    CookieTable;                          /* Event cookie table */
++
++    EP_CODE           ThreadCode;                             /* copy of thread code */
++    unsigned int        CommandPortEventTrap;                 /* flag to indicate command port eventint queue overflow trap */
++
++    sdramaddr_t         RailElan;                             /* Elan visible main/sdram portions of */
++    E3_Addr             RailElanAddr;                         /* device structure */
++    EP3_RAIL_MAIN      *RailMain;
++    E3_Addr           RailMainAddr;
++
++    /* small system message queues */
++    sdramaddr_t               QueueDescs;                             /* Input Queue descriptors */
++
++    /* Network position prober */
++    E3_Addr           ProbeStack;                             /* Network position thread command structure */
++    EP3_COOKIE                ProbeCookie;                            /*   event cookie for Done event */
++    kcondvar_t                ProbeWait;                              /*   place to wait on probe thread */
++    spinlock_t                ProbeLock;                              /*     and lock */
++    volatile int        ProbeDone;                            /*     and flag to indicate it's done */
++
++    E3_uint16         ProbeDest0[TR_TRACEROUTE_ENTRIES];      /* last result of CheckNetworkPosition */
++    E3_uint16         ProbeDest1[TR_TRACEROUTE_ENTRIES];
++    E3_uint32         ProbeResult;
++
++    long              ProbeLevelTick[ELAN_MAX_LEVELS];
++    long              SwitchBroadcastLevelTick;
++
++    /* rings for issueing dmas */
++    EP3_DMA_RING        DmaRings[EP3_NUM_RINGS];
++
++    /* retry lists for dmas */
++    struct list_head    DmaRetries[EP_NUM_RETRIES];           /* Dma retry lists */
++    struct list_head    DmaRetryFreeList;                     /*   and free list */
++    u_int             DmaRetryCount;                          /*   and total retry count */
++    u_int             DmaRetryReserved;                       /*   and number reserved */
++    u_int             DmaRetryThreadShouldStall;              /*   count of reasons to stall retries */
++    u_int             DmaRetryThreadStarted:1;                /*   dma retry thread running */
++    u_int             DmaRetryThreadShouldStop:1;             /*     but should stop */
++    u_int             DmaRetryThreadStopped:1;                /*     and now it's stopped */
++    u_int             DmaRetryInitialised:1;                  /* have initialise dma retries */
++
++    spinlock_t                DmaRetryLock;                           /*   spinlock protecting lists */
++    kcondvar_t                DmaRetryWait;                           /*   place retry thread sleeps */
++    long              DmaRetryTime;                           /*   and when it will next wakeup */
++    unsigned int        DmaRetrySleeping;                     /*   and it's sleeping there */
++
++    /* Network Identify Cookies */
++    E3_uint32        *MainCookies;                            /* One cookie allocator per-node for main*/
++    E3_Addr           ElanCookies;                            /*   and one for elan */
++    spinlock_t                CookieLock;                             /* spinlock to protect main cookies */
++
++    /* Halt operation flags for flushing. */
++    kmutex_t            HaltOpMutex;                          /* serialize access to halt operations */
++    unsigned int      HaltOpCompleted;                        /* flag to indicate halt operation completed */
++    kcondvar_t                HaltOpSleep;                            /*   place to wait for it to complete */
++
++    /* Network error state */
++    kcondvar_t                NetworkErrorSleep;                      /* place to sleep for network error halt operation */
++    u_int             NetworkErrorFlushed;                    /*   and flag to indicate flushed */
++
++
++    EP3_RAIL_STATS    Stats;                                  /* statistics */
++};
++
++/* support.c */
++
++extern ELAN3_OPS  ep3_elan3_ops;
++
++extern E3_uint32    LocalCookie (EP3_RAIL *rail, unsigned int remoteNode);
++extern E3_uint32    RemoteCookie (EP3_RAIL *rail, unsigned int remoteNode);
++
++extern void         InitialiseCookieTable (EP3_COOKIE_TABLE *table);
++extern void         DestroyCookieTable (EP3_COOKIE_TABLE *table);
++extern void         RegisterCookie (EP3_COOKIE_TABLE *table, EP3_COOKIE *cookie, 
++                                  E3_Addr event, EP3_COOKIE_OPS *ops, void *arg);
++extern void         DeregisterCookie (EP3_COOKIE_TABLE *table, EP3_COOKIE *cookie);
++extern EP3_COOKIE   *LookupCookie (EP3_COOKIE_TABLE *table, uint32_t cookie);
++extern EP3_COOKIE   *LookupEventCookie (EP3_RAIL *rail, EP3_COOKIE_TABLE *table, E3_Addr);
++
++extern int          DmaRingsCreate (EP3_RAIL *rail);
++extern void         DmaRingsRelease (EP3_RAIL *rail);
++extern int          IssueDma (EP3_RAIL *rail, E3_DMA_BE *dma, int type, int retryThread);
++
++extern int          IssueWaitevent (EP3_RAIL *rail, E3_Addr value);
++extern void         IssueSetevent (EP3_RAIL *rail, E3_Addr value);
++extern void         IssueRunThread (EP3_RAIL *rail, E3_Addr value);
++extern long         DmaRetryTime (int type);
++extern int          InitialiseDmaRetries (EP3_RAIL *rail);
++extern void         DestroyDmaRetries (EP3_RAIL *rail);
++extern int          ReserveDmaRetries (EP3_RAIL *rail, int count, EP_ATTRIBUTE attr);
++extern void         ReleaseDmaRetries (EP3_RAIL *rail, int count);
++extern void         StallDmaRetryThread (EP3_RAIL *rail);
++extern void         ResumeDmaRetryThread (EP3_RAIL *rail);
++extern void         QueueDmaForRetry (EP3_RAIL *rail, E3_DMA_BE *dma, int interval);
++extern void         QueueDmaOnStalledList (EP3_RAIL *rail, E3_DMA_BE *dma);
++extern void         FreeStalledDmas (EP3_RAIL *rail, unsigned int nodeId);
++
++extern void         SetQueueLocked(EP3_RAIL *rail, sdramaddr_t qaddr);
++
++/* threadcode_elan3.c */
++extern E3_Addr    ep3_init_thread (ELAN3_DEV *dev, E3_Addr fn, E3_Addr addr, sdramaddr_t stack,
++                                 int stackSize, int nargs, ...);
++
++/* probenetwork.c */
++extern int        ep3_init_probenetwork (EP3_RAIL *rail);
++extern void       ep3_destroy_probenetwork (EP3_RAIL *rail);
++extern void       ep3_probe_position_found (EP3_RAIL *rail, ELAN_POSITION *pos);
++extern int        ep3_probe_route (EP_RAIL *r, int level, int sw, int nodeid, int *linkup, int *linkdown, int attempts, EP_SWITCH *lsw);
++extern int        ep3_check_position (EP_RAIL *rail);
++
++/* neterr_elan3.c */
++extern void       ep3_neterr_fixup (EP_RAIL *r, unsigned int nodeId, EP_NETERR_COOKIE *cookies);
++
++/* kcomm_elan3.c */
++extern EP_RAIL    *ep3_create_rail (EP_SYS *sys, ELAN3_DEV *dev);
++extern void        ep3_destroy_rail (EP_RAIL *rail);
++
++extern int         ep3_start_rail (EP_RAIL *rail);
++extern void        ep3_stall_rail (EP_RAIL *rail);
++extern void        ep3_stop_rail (EP_RAIL *rail);
++
++extern void      ep3_position_found (EP_RAIL *rail, ELAN_POSITION *pos);
++
++extern sdramaddr_t ep3_sdram_alloc (EP_RAIL *rail, EP_ADDR addr, unsigned int size);
++extern void        ep3_sdram_free (EP_RAIL *rail, sdramaddr_t addr, unsigned int size);
++extern void        ep3_sdram_writeb (EP_RAIL *rail, sdramaddr_t addr, unsigned char val);
++
++extern void        ep3_flush_tlb (EP_RAIL *r);
++extern void        ep3_load_system_route (EP_RAIL *r, unsigned int vp, unsigned int lowNode, unsigned int highNode);
++extern void        ep3_load_node_route (EP_RAIL *r, unsigned int nodeId);
++extern void        ep3_unload_node_route (EP_RAIL *r, unsigned int nodeId);
++extern void        ep3_lower_filter (EP_RAIL *r, unsigned int nodeId);
++extern void        ep3_raise_filter (EP_RAIL *rail, unsigned int nodeId);
++extern void        ep3_node_disconnected (EP_RAIL *r, unsigned int nodeId);
++
++extern void        ep3_fillout_stats(EP_RAIL *rail, char *str);
++
++/* kmap_elan3.c */
++extern void        ep3_kaddr_map (EP_RAIL *r, EP_ADDR eaddr, virtaddr_t kaddr, unsigned int len, unsigned int perm, int ep_attr);
++extern void        ep3_sdram_map (EP_RAIL *r, EP_ADDR eaddr, sdramaddr_t saddr, unsigned int len, unsigned int perm, int ep_attr);
++extern void        ep3_ioaddr_map (EP_RAIL *r, EP_ADDR eaddr, ioaddr_t ioaddr, unsigned int len, unsigned int perm);
++extern void        ep3_unmap (EP_RAIL *r, EP_ADDR eaddr, unsigned int len);
++extern void       *ep3_dvma_reserve (EP_RAIL *r, EP_ADDR eaddr, unsigned int npages);
++extern void        ep3_dvma_release (EP_RAIL *r, EP_ADDR eaddr, unsigned int npages, void *private);
++extern void        ep3_dvma_set_pte (EP_RAIL *r, void *private, unsigned int index, physaddr_t paddr, unsigned int perm);
++extern physaddr_t  ep3_dvma_read_pte (EP_RAIL *r, void *private, unsigned int index);
++extern void        ep3_dvma_unload (EP_RAIL *r, void *private, unsigned int index, unsigned int npages);
++
++/* kmsg_elan3.c */
++extern EP_INPUTQ  *ep3_alloc_inputq (EP_RAIL *r, unsigned int qnum, unsigned int slotSize, unsigned int slotCount,
++                                   EP_INPUTQ_CALLBACK *callback, void *arg);
++extern void        ep3_free_inputq (EP_RAIL *r, EP_INPUTQ *q);
++extern void        ep3_enable_inputq (EP_RAIL *r, EP_INPUTQ *q);
++extern void        ep3_disable_inputq (EP_RAIL *r, EP_INPUTQ *q);
++extern int         ep3_poll_inputq (EP_RAIL *r, EP_INPUTQ *q, int maxCount, EP_INPUTQ_HANDLER *handler, void *arg);
++extern EP_OUTPUTQ *ep3_alloc_outputq (EP_RAIL *r, unsigned int slotSize, unsigned int slotCount);
++extern void        ep3_free_outputq (EP_RAIL *r, EP_OUTPUTQ *q);
++extern void       *ep3_outputq_msg (EP_RAIL *r, EP_OUTPUTQ *q, unsigned int slotNum);
++extern int         ep3_outputq_state (EP_RAIL *r, EP_OUTPUTQ *q, unsigned int slotNum);
++extern int         ep3_outputq_send (EP_RAIL *r, EP_OUTPUTQ *q, unsigned int slotNum, unsigned int size,
++                                   unsigned int nodeId, unsigned int qnum, unsigned int retries);
++
++/* support_elan3.c */
++extern void        ep3_flush_filters (EP_RAIL *r);
++extern void        ep3_flush_queues (EP_RAIL *r);
++
++#endif /* !defined(__ELAN3__) */
++
++#endif /* __EP_KCOMM_ELAN3_H */
+Index: linux-2.4.21/drivers/net/qsnet/ep/kcomm_elan4.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/kcomm_elan4.c       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/kcomm_elan4.c    2005-06-01 23:12:54.667428768 -0400
+@@ -0,0 +1,526 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: kcomm_elan4.c,v 1.16.2.3 2004/11/30 12:02:17 mike Exp $ $Name: QSNETMODULES-4-30_20050128 $"
++/*      $Source: /cvs/master/quadrics/epmod/kcomm_elan4.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <qsnet/kthread.h>
++
++#include <elan/kcomm.h>
++
++#include "kcomm_vp.h"
++#include "kcomm_elan4.h"
++#include "conf_linux.h"
++
++extern EP_CODE threadcode_elan4;
++
++unsigned int
++ep4_create_rails (EP_SYS *sys, unsigned int disabled)
++{
++    unsigned int rmask = 0;
++    ELAN4_DEV   *dev;
++    EP_RAIL     *rail;
++    int          i;
++
++    for (i = 0; i < EP_MAX_RAILS; i++)
++    {
++      if ((dev = elan4_reference_device (i, ELAN4_STATE_STARTED)) != NULL)
++      {
++          if ((rail = ep4_create_rail (sys, dev)) == NULL)
++              elan4_dereference_device (dev);
++          else
++          {
++              if (disabled & (1 << rail->Number))
++                  printk ("%s: auto-start of device disabled by configuration\n", rail->Name);
++              else
++                  ep_start_rail (rail);
++              
++              ep_procfs_rail_init(rail);
++
++              rmask |= (1 << rail->Number);
++          }
++      }
++    }
++
++    if (rmask)
++      qsnet_debug_alloc();
++
++    return rmask;
++}
++
++EP_RAIL *
++ep4_create_rail (EP_SYS *sys, ELAN4_DEV *dev)
++{
++    EP4_RAIL *rail;
++    int res;
++
++    KMEM_ZALLOC (rail, EP4_RAIL *, sizeof (EP4_RAIL), 1);
++
++    if (rail == NULL)
++      return (EP_RAIL *) NULL;
++    
++    if ((res = ep_init_rail (sys, &rail->r_generic)) != 0)
++    {
++      KMEM_FREE (rail, sizeof (EP4_RAIL));
++      return (EP_RAIL *) NULL;
++    }
++
++    rail->r_ctxt.ctxt_dev = dev;
++
++    /* install our rail operations */
++    rail->r_generic.Operations.DestroyRail      = ep4_destroy_rail;
++    rail->r_generic.Operations.StartRail        = ep4_start_rail;
++    rail->r_generic.Operations.StallRail        = ep4_stall_rail;
++    rail->r_generic.Operations.StopRail         = ep4_stop_rail;    
++
++    rail->r_generic.Operations.SdramAlloc       = ep4_sdram_alloc;
++    rail->r_generic.Operations.SdramFree        = ep4_sdram_free;
++    rail->r_generic.Operations.SdramWriteb      = ep4_sdram_writeb;
++
++    rail->r_generic.Operations.KaddrMap         = ep4_kaddr_map;
++    rail->r_generic.Operations.SdramMap         = ep4_sdram_map;
++    rail->r_generic.Operations.Unmap            = ep4_unmap;
++
++    rail->r_generic.Operations.DvmaReserve    = ep4_dvma_reserve;
++    rail->r_generic.Operations.DvmaRelease    = ep4_dvma_release;
++    rail->r_generic.Operations.DvmaSetPte     = ep4_dvma_set_pte;
++    rail->r_generic.Operations.DvmaReadPte    = ep4_dvma_read_pte;
++    rail->r_generic.Operations.DvmaUnload     = ep4_dvma_unload;
++    rail->r_generic.Operations.FlushTlb               = ep4_flush_tlb;
++
++    rail->r_generic.Operations.ProbeRoute       = ep4_probe_route;
++
++    rail->r_generic.Operations.PositionFound    = ep4_position_found;
++    rail->r_generic.Operations.CheckPosition    = ep4_check_position;
++    rail->r_generic.Operations.NeterrFixup      = ep4_neterr_fixup;
++
++    rail->r_generic.Operations.LoadSystemRoute  = ep4_load_system_route;
++
++    rail->r_generic.Operations.LoadNodeRoute    = ep4_load_node_route;
++    rail->r_generic.Operations.UnloadNodeRoute  = ep4_unload_node_route;
++    rail->r_generic.Operations.LowerFilter    = ep4_lower_filter;
++    rail->r_generic.Operations.RaiseFilter    = ep4_raise_filter;
++    rail->r_generic.Operations.NodeDisconnected = ep4_node_disconnected;
++
++    rail->r_generic.Operations.FlushFilters     = ep4_flush_filters;
++    rail->r_generic.Operations.FlushQueues    = ep4_flush_queues;
++
++    rail->r_generic.Operations.AllocInputQ    = ep4_alloc_inputq;
++    rail->r_generic.Operations.FreeInputQ     = ep4_free_inputq;
++    rail->r_generic.Operations.EnableInputQ     = ep4_enable_inputq;
++    rail->r_generic.Operations.DisableInputQ    = ep4_disable_inputq;
++    rail->r_generic.Operations.PollInputQ     = ep4_poll_inputq;
++
++    rail->r_generic.Operations.AllocOutputQ     = ep4_alloc_outputq;
++    rail->r_generic.Operations.FreeOutputQ    = ep4_free_outputq;
++    rail->r_generic.Operations.OutputQMsg     = ep4_outputq_msg;
++    rail->r_generic.Operations.OutputQState     = ep4_outputq_state;
++    rail->r_generic.Operations.OutputQSend    = ep4_outputq_send;
++
++    rail->r_generic.Operations.FillOutStats     = ep4_fillout_stats;
++    rail->r_generic.Operations.Debug          = ep4_debug_rail;
++
++    rail->r_generic.Devinfo = dev->dev_devinfo;
++
++    printk ("%s: connected via elan4 rev%c device %d\n", rail->r_generic.Name,
++          'a' + dev->dev_devinfo.dev_revision_id, dev->dev_instance);
++
++    return (EP_RAIL *) rail;
++}
++
++void
++ep4_destroy_rail (EP_RAIL *r)
++{
++    EP4_RAIL *rail = (EP4_RAIL *) r;
++
++    elan4_dereference_device (rail->r_ctxt.ctxt_dev);
++
++    KMEM_FREE (rail, sizeof (EP4_RAIL));
++}
++
++static int
++ep4_attach_rail (EP4_RAIL *r)
++{
++    EP4_RAIL  *rail = (EP4_RAIL *) r;
++    ELAN4_DEV *dev  = rail->r_ctxt.ctxt_dev;
++    unsigned   ctx;
++
++    if (elan4_insertctxt (dev, &rail->r_ctxt, &ep4_trap_ops) != 0)
++      return -ENOMEM;
++    
++    if ((rail->r_routetable = elan4_alloc_routetable (dev, 4)) == NULL)       /* 512 << 4 == 8192 entries */
++    {
++      elan4_removectxt (dev, &rail->r_ctxt);
++      return -ENOMEM;
++    }
++    elan4_set_routetable (&rail->r_ctxt, rail->r_routetable);
++
++    /* Attach to the kernel comms nextwork context */
++    if (elan4_attach_filter (&rail->r_ctxt, ELAN4_KCOMM_CONTEXT_NUM) < 0)
++    {
++      elan4_free_routetable (dev, rail->r_routetable);
++      elan4_removectxt (dev, &rail->r_ctxt);
++
++      return -EBUSY;
++    }
++
++    for (ctx = ELAN4_KCOMM_BASE_CONTEXT_NUM; ctx <= ELAN4_KCOMM_TOP_CONTEXT_NUM; ctx++)
++      elan4_attach_filter (&rail->r_ctxt, ctx);
++
++    return 0;
++}
++
++static void
++ep4_detach_rail (EP4_RAIL *rail)
++{
++    ELAN4_DEV *dev = rail->r_ctxt.ctxt_dev;
++    unsigned   ctx;
++
++    elan4_detach_filter (&rail->r_ctxt, ELAN4_KCOMM_CONTEXT_NUM);
++
++    for (ctx = ELAN4_KCOMM_BASE_CONTEXT_NUM; ctx <= ELAN4_KCOMM_TOP_CONTEXT_NUM; ctx++)
++      elan4_detach_filter (&rail->r_ctxt, ctx);
++
++    if (rail->r_routetable)
++    {
++      elan4_set_routetable (&rail->r_ctxt, NULL);
++      elan4_free_routetable (dev, rail->r_routetable);
++    }
++
++    elan4_removectxt (dev, &rail->r_ctxt);
++}
++
++int
++ep4_start_rail (EP_RAIL *r)
++{
++    EP4_RAIL     *rail = (EP4_RAIL *) r;
++    ELAN4_DEV    *dev  = rail->r_ctxt.ctxt_dev;
++    E4_InputQueue qdesc;
++    int           i, res;
++
++    if ((res = ep4_attach_rail (rail)) < 0)
++      return res;
++
++    /* Initialise main interrupt cookie table */
++    spin_lock_init (&rail->r_intcookie_lock);
++    for (i = 0; i < EP4_INTCOOKIE_HASH_SIZE; i++)
++      INIT_LIST_HEAD (&rail->r_intcookie_hash[i]);
++
++    kmutex_init (&rail->r_haltop_mutex);
++    kcondvar_init (&rail->r_haltop_sleep);
++    spin_lock_init (&rail->r_haltop_lock);
++
++    spin_lock_init (&rail->r_cookie_lock);
++
++    INIT_LIST_HEAD (&rail->r_ecq_list[EP4_ECQ_EVENT]);
++    INIT_LIST_HEAD (&rail->r_ecq_list[EP4_ECQ_ATOMIC]);
++    INIT_LIST_HEAD (&rail->r_ecq_list[EP4_ECQ_SINGLE]);
++    INIT_LIST_HEAD (&rail->r_ecq_list[EP4_ECQ_MAIN]);
++    spin_lock_init (&rail->r_ecq_lock);
++
++    ep_kthread_init (&rail->r_retry_thread);
++    INIT_LIST_HEAD (&rail->r_retry_ops);
++
++    INIT_LIST_HEAD (&rail->r_neterr_ops);
++
++    kmutex_init (&rail->r_flush_mutex);
++    kcondvar_init (&rail->r_flush_sleep);
++
++    /* Allocate the elan visible sdram/main memory */
++    if ((rail->r_elan = ep_alloc_elan (&rail->r_generic, sizeof (EP4_RAIL_ELAN), 0, &rail->r_elan_addr)) == 0 ||
++      (rail->r_main = ep_alloc_main (&rail->r_generic, sizeof (EP4_RAIL_MAIN), 0, &rail->r_main_addr)) == 0)
++    {
++      goto failed;
++    }
++
++    for (i = 0; i < EP_NUM_SYSTEMQ; i++)
++      elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_qevents[i].ev_CountAndType), 0);
++
++    elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_flush_event.ev_CountAndType), E4_EVENT_INIT_VALUE (0, E4_EVENT_WRITE, E4_EVENT_DTYPE_LONG, 0));
++
++    /* Allocate the system input queues at their fixed elan address */
++    /*   avoid sdram address aliasing by allocating the min sdram pagesize */
++    if (! (rail->r_queuedescs= ep_alloc_memory_elan (&rail->r_generic, EP_SYSTEM_QUEUE_BASE, SDRAM_PAGE_SIZE, EP_PERM_ALL, 0)))
++      goto failed;
++
++    /* Initialise the input queue descriptor as "full" with no event */
++    qdesc.q_bptr    = 0;
++    qdesc.q_fptr    = 8;
++    qdesc.q_control = E4_InputQueueControl(qdesc.q_bptr, qdesc.q_fptr, 8);
++    qdesc.q_event   = 0;
++
++    for (i = 0; i < EP_NUM_SYSTEMQ; i++)
++      elan4_sdram_copyq_to_sdram (dev, &qdesc, EP_SYSTEMQ_DESC (rail->r_queuedescs, i), sizeof (E4_InputQueue));
++
++    /* Allocate the resource map for command queue mappings */
++    if ((rail->r_ecq_rmap = ep_rmallocmap (EP4_ECQ_RMAPSIZE, "r_ecq_rmap", 1)) == NULL)
++      goto failed;
++    
++    ep_rmfree (rail->r_ecq_rmap, EP4_ECQ_TOP - EP4_ECQ_BASE, EP4_ECQ_BASE);
++
++    /* register an interrupt cookie & allocate command queues for command queue flushing */
++    rail->r_flush_mcq = ep4_get_ecq (rail, EP4_ECQ_MAIN, 4);
++    rail->r_flush_ecq = ep4_get_ecq (rail, EP4_ECQ_EVENT, 1);
++
++    if (rail->r_flush_mcq == NULL || rail->r_flush_ecq == NULL)
++      goto failed;
++
++    ep4_register_intcookie (rail, &rail->r_flush_intcookie, rail->r_elan_addr + offsetof (EP4_RAIL_ELAN, r_flush_event), ep4_flush_interrupt, rail);
++
++    /* startup the retry thread */
++    if (kernel_thread_create (ep4_retry_thread, (void *) rail) == 0)
++      goto failed;
++    ep_kthread_started (&rail->r_retry_thread);
++    
++    ep4_initialise_dma_retries (rail);
++
++    if ((rail->r_event_ecq = ep4_alloc_ecq (rail, CQ_Size1K)) == NULL)
++      goto failed;
++    
++    rail->r_threadcode = threadcode_elan4;
++    if (ep_loadcode (&rail->r_generic, &rail->r_threadcode))
++      goto failed;
++
++    elan4_flush_icache (&rail->r_ctxt);
++
++    if (ep4_probe_init (rail))
++      goto failed;
++
++    /* can now drop the context filter for the system context */
++    elan4_set_filter (&rail->r_ctxt, ELAN4_KCOMM_CONTEXT_NUM, E4_FILTER_HIGH_PRI);
++
++    return 0;
++
++ failed:
++    printk ("ep4_start_rail: failed for rail '%s'\n", rail->r_generic.Name);
++    ep4_stop_rail (&rail->r_generic);
++
++    return -ENOMEM;
++}
++
++void
++ep4_stall_rail (EP_RAIL *r)
++{
++    EP4_RAIL *rail = (EP4_RAIL *) r;
++    unsigned  ctx;
++
++    /* Raise all the context filters */
++    elan4_set_filter (&rail->r_ctxt, ELAN4_KCOMM_CONTEXT_NUM, E4_FILTER_DISCARD_ALL);
++
++    for (ctx = ELAN4_KCOMM_BASE_CONTEXT_NUM; ctx <= ELAN4_KCOMM_TOP_CONTEXT_NUM; ctx++)
++      elan4_set_filter (&rail->r_ctxt, ctx, E4_FILTER_DISCARD_ALL);
++}
++
++void
++ep4_stop_rail (EP_RAIL *r)
++{
++    EP4_RAIL *rail = (EP4_RAIL *) r;
++
++    if (rail->r_generic.State == EP_RAIL_STATE_RUNNING) /* undo ep4_position_found() */
++    {
++      ELAN_POSITION *pos  = &rail->r_generic.Position;
++      EP_ADDR        addr = elan4_sdram_readq (rail->r_ctxt.ctxt_dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_cookies));
++
++      ep_free_elan (&rail->r_generic, addr, pos->pos_nodes * sizeof (E4_uint64));
++
++      KMEM_FREE (rail->r_cookies, pos->pos_nodes * sizeof (E4_uint64));
++    }
++
++    ep4_probe_destroy (rail);
++
++    ep_unloadcode (&rail->r_generic, &rail->r_threadcode);
++
++    if (rail->r_event_ecq)
++      ep4_free_ecq (rail, rail->r_event_ecq);
++    rail->r_event_ecq = NULL;
++
++    ep4_finalise_dma_retries (rail);
++
++    ep_kthread_stop (&rail->r_retry_thread);
++    ep_kthread_destroy (&rail->r_retry_thread);
++
++    if (rail->r_flush_intcookie.int_arg)
++      ep4_deregister_intcookie (rail, &rail->r_flush_intcookie);
++    rail->r_flush_intcookie.int_arg = NULL;
++
++    if (rail->r_flush_mcq)
++      ep4_put_ecq (rail, rail->r_flush_mcq, 4);
++    rail->r_flush_mcq = NULL;
++
++    if (rail->r_flush_ecq)
++      ep4_put_ecq (rail, rail->r_flush_ecq, 1);
++    rail->r_flush_ecq = NULL;
++
++    if (rail->r_ecq_rmap)
++      ep_rmfreemap (rail->r_ecq_rmap);
++    
++    if (rail->r_queuedescs)
++      ep_free_memory_elan (&rail->r_generic, EP_SYSTEM_QUEUE_BASE);
++    rail->r_queuedescs = 0;
++
++    if (rail->r_elan)
++      ep_free_elan (&rail->r_generic, rail->r_elan_addr, sizeof (EP4_RAIL_ELAN));
++    rail->r_elan = 0;
++
++    if (rail->r_main)
++      ep_free_main (&rail->r_generic, rail->r_main_addr, sizeof (EP4_RAIL_MAIN));
++    rail->r_main = NULL;
++
++    kcondvar_destroy (&rail->r_flush_sleep);
++    kmutex_destroy (&rail->r_flush_mutex);
++
++    spin_lock_destroy (&rail->r_ecq_lock);
++    spin_lock_destroy (&rail->r_cookie_lock);
++
++    spin_lock_destroy (&rail->r_haltop_lock);
++    kcondvar_destroy(&rail->r_haltop_sleep);
++    kmutex_destroy (&rail->r_haltop_mutex);
++    spin_lock_destroy (&rail->r_intcookie_lock);
++
++    ep4_detach_rail (rail);
++}
++
++void
++ep4_position_found (EP_RAIL *r, ELAN_POSITION *pos)
++{
++    EP4_RAIL   *rail = (EP4_RAIL *) r;
++    sdramaddr_t cookies;
++    EP_ADDR     addr;
++    int         i;
++
++    KMEM_ZALLOC (rail->r_cookies, E4_uint64 *, pos->pos_nodes * sizeof (E4_uint64), 1);
++
++    if (! (cookies = ep_alloc_elan (&rail->r_generic, pos->pos_nodes * sizeof (E4_uint64), 0, &addr)))
++      panic ("ep4_position_found: cannot allocate elan cookies array\n");
++
++    for (i = 0; i < pos->pos_nodes; i++)
++      elan4_sdram_writeq (rail->r_ctxt.ctxt_dev, cookies + (i * sizeof (E4_uint64)), 0);
++    
++    for (i = 0; i < pos->pos_nodes; i++)
++      rail->r_cookies[i] = 0;
++
++    elan4_sdram_writeq (rail->r_ctxt.ctxt_dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_nodeid), pos->pos_nodeid);
++    elan4_sdram_writeq (rail->r_ctxt.ctxt_dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_cookies), addr);
++
++    ep4_probe_position_found (rail, pos);
++}
++
++sdramaddr_t
++ep4_sdram_alloc (EP_RAIL *r, EP_ADDR addr, unsigned size)
++{
++    ELAN4_DEV *dev = ((EP4_RAIL *) r)->r_ctxt.ctxt_dev;
++
++    if (size >= SDRAM_PAGE_SIZE)
++      return elan4_sdram_alloc (dev, size);
++    else
++    {
++      sdramaddr_t block = elan4_sdram_alloc (dev, SDRAM_PAGE_SIZE);
++      sdramaddr_t sdram = block + (addr & (SDRAM_PAGE_SIZE-1));
++
++      /* free of the portion before sdram */
++      if (sdram > block)
++          elan4_sdram_free (dev, block, sdram - block);
++
++      /* free of the portion after sdram */
++      if ((block + SDRAM_PAGE_SIZE) > (sdram + size))
++          elan4_sdram_free (dev, sdram + size, block + SDRAM_PAGE_SIZE - (sdram + size));
++
++      return sdram;
++    }
++}
++
++void
++ep4_sdram_free (EP_RAIL *r, sdramaddr_t addr, unsigned size)
++{
++    elan4_sdram_free (((EP4_RAIL *) r)->r_ctxt.ctxt_dev, addr, size);
++}
++
++void
++ep4_sdram_writeb (EP_RAIL *r, sdramaddr_t addr, unsigned char val)
++{
++    elan4_sdram_writeb (((EP4_RAIL *) r)->r_ctxt.ctxt_dev, addr, val);
++}
++
++void
++ep4_flush_tlb (EP_RAIL *r)
++{
++    elan4mmu_flush_tlb (((EP4_RAIL *) r)->r_ctxt.ctxt_dev);
++}
++
++void
++ep4_load_system_route (EP_RAIL *r, unsigned vp, unsigned lowNode, unsigned highNode)
++{
++    EP4_RAIL  *rail = (EP4_RAIL *) r;
++    ELAN4_DEV *dev  = rail->r_ctxt.ctxt_dev;
++    E4_VirtualProcessEntry route;
++
++    if (elan4_generate_route (&rail->r_generic.Position, &route, ELAN4_KCOMM_CONTEXT_NUM, 
++                            lowNode, highNode, FIRST_SYSTEM_PACKET | FIRST_HIGH_PRI | FIRST_TIMEOUT(3)) < 0)
++    {
++      panic ("ep4_load_system_route: generate route failed\n");
++      /* NOTREACHED */
++    }
++
++    elan4_write_route (dev, rail->r_routetable, vp, &route);
++}
++
++void
++ep4_load_node_route (EP_RAIL *r, unsigned nodeId)
++{
++    EP4_RAIL  *rail = (EP4_RAIL *) r;
++    ELAN4_DEV *dev  = rail->r_ctxt.ctxt_dev;
++    E4_VirtualProcessEntry route;
++
++    if (elan4_generate_route (&rail->r_generic.Position, &route, EP4_CONTEXT_NUM(rail->r_generic.Position.pos_nodeid),
++                            nodeId, nodeId, FIRST_SYSTEM_PACKET | FIRST_TIMEOUT(3)) < 0)
++    {
++      panic ("ep4_load_node_route: generate route failed\n");
++      /* NOTREACHED */
++    }
++
++    elan4_write_route (dev, rail->r_routetable, EP_VP_DATA(nodeId), &route);
++}
++
++void
++ep4_unload_node_route (EP_RAIL *r, unsigned nodeId)
++{
++    EP4_RAIL  *rail = (EP4_RAIL *) r;
++    ELAN4_DEV *dev  = rail->r_ctxt.ctxt_dev;
++    
++    elan4_invalidate_route (dev, rail->r_routetable, EP_VP_DATA(nodeId));
++}
++
++void
++ep4_lower_filter (EP_RAIL *r, unsigned nodeId)
++{
++    EP4_RAIL *rail = (EP4_RAIL *) r;
++
++    elan4_set_filter (&rail->r_ctxt, EP4_CONTEXT_NUM(nodeId), E4_FILTER_HIGH_PRI);
++}
++
++void
++ep4_raise_filter (EP_RAIL *r, unsigned nodeId)
++{
++    EP4_RAIL *rail = (EP4_RAIL *) r;
++
++    elan4_set_filter (&rail->r_ctxt, EP4_CONTEXT_NUM(nodeId), E4_FILTER_DISCARD_ALL);
++}
++
++void
++ep4_node_disconnected (EP_RAIL *r, unsigned nodeId)
++{
++    ep4_free_stalled_dmas ((EP4_RAIL *) r, nodeId);
++}
++
++void
++ep4_fillout_stats(EP_RAIL *r, char *str) 
++{
++    /* no stats here yet */
++    /* EP4_RAIL *ep4rail = (EP4_RAIL *)r; */
++}
+Index: linux-2.4.21/drivers/net/qsnet/ep/kcomm_elan4.h
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/kcomm_elan4.h       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/kcomm_elan4.h    2005-06-01 23:12:54.668428616 -0400
+@@ -0,0 +1,443 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __EP_KCOMM_ELAN4_H
++#define __EP_KCOMM_ELAN4_H
++
++#ident "@(#)$Id: kcomm_elan4.h,v 1.16.2.2 2004/12/14 10:19:14 mike Exp $ $Name: QSNETMODULES-4-30_20050128 $"
++/*      $Source: /cvs/master/quadrics/epmod/kcomm_elan4.h,v $*/
++
++#include <elan4/types.h>
++
++#include <elan4/dma.h>
++#include <elan4/events.h>
++#include <elan4/commands.h>
++
++#if !defined(__elan4__)
++#include <elan4/device.h>
++#endif /* ! defined(__elan4__) */
++
++/* private address allocation */
++#define EP4_TEXT_BASE                 0xF8000000              /* base address for thread code (defined in makerules.elan4) */
++#define EP4_ECQ_BASE                  0xFF000000              /* address space for mapping command queues */
++#define EP4_ECQ_TOP                   0xFF800000
++
++#define EP4_ECQ_RMAPSIZE              128
++#define EP4_STACK_SIZE                        1024                    /* default thread code stack size */
++#define EP4_MAX_LEVELS                        8                       /* same as ELAN_MAX_LEVELS */
++
++/* context number allocation */
++#define EP4_CONTEXT_NUM(nodeId)               (ELAN4_KCOMM_BASE_CONTEXT_NUM + (nodeId))
++#define EP4_CONTEXT_ISDATA(ctx)               ((ctx) >= ELAN4_KCOMM_BASE_CONTEXT_NUM && \
++                                       (ctx) <= ELAN4_KCOMM_TOP_CONTEXT_NUM)
++#define EP4_CONTEXT_TO_NODE(ctx)      ((ctx) - ELAN4_KCOMM_BASE_CONTEXT_NUM)
++
++/*
++ * network error cookie format:
++ *  -------------------------------------------------
++ *  | unique cookie value | Remote | DMA | Location |
++ *  -------------------------------------------------
++ * [63:4] Cookie   - unique cookie number
++ * [3]    Thread   - cookie generated by thread code
++ * [2]          Remote   - cookie generated by remote end
++ * [1]    STEN     - cookie is for a STEN packet
++ * [0]    DMA      - cookie is for a DMA
++ */
++#define EP4_COOKIE_DMA                (1    << 0)
++#define EP4_COOKIE_STEN               (1    << 1)
++#define EP4_COOKIE_REMOTE     (1    << 2)
++#define EP4_COOKIE_THREAD     (1    << 3)
++#define EP4_COOKIE_INC                (1ull << 4)
++
++#define EP4_COOKIE_STRING(val)        ((val) & ~(EP4_COOKIE_INC-1)) >> 4, \
++                              ((val) & EP4_COOKIE_DMA)    ? ",dma" : "", \
++                              ((val) & EP4_COOKIE_REMOTE) ? ",remote" : "", \
++                              ((val) & EP4_COOKIE_THREAD) ? ",thread" : "", \
++                              ((val) & EP4_COOKIE_STEN)   ? ",sten" : ""
++/*
++ * Done "word" values 
++ */
++#define EP4_STATE_FREE                0
++#define EP4_STATE_ACTIVE      1
++#define EP4_STATE_FINISHED    2
++#define EP4_STATE_FAILED      3
++#define EP4_STATE_PRIVATE     4
++
++#define EP4_EVENT_FIRING_TLIMIT       16384                   /* 1023 uS */
++
++/* forward declarations */
++typedef struct ep4_rail       EP4_RAIL;
++
++#if !defined(__elan4__)
++
++typedef struct ep4_intcookie
++{
++    struct list_head           int_link;
++    E4_uint64                  int_val;
++    void                     (*int_callback)(EP4_RAIL *rail, void *arg);
++    void                      *int_arg;
++} EP4_INTCOOKIE;
++
++#define EP4_INTCOOKIE_HASH_SIZE       256
++#define EP4_INTCOOKIE_HASH(a)         ((((a) >> 3) ^ ((a) >> 7) ^ ((a) >> 11)) & (EP4_INTCOOKIE_HASH_SIZE-1))
++
++typedef struct ep4_ecq
++{
++    struct list_head  ecq_link;                               /* linked on r_ecq_list */
++    ELAN4_INTOP               ecq_intop;                              /* main interrupt op space */
++    ELAN4_CQ         *ecq_cq;                                 /* command queue */
++    E4_Addr           ecq_addr;                               /* address mapped into elan */
++    unsigned int      ecq_avail;                              /* # dwords still available */
++
++    spinlock_t                ecq_lock;                               /* spinlock for main accesses */
++    sdramaddr_t               ecq_event;                              /* event for flushing "event" queues */
++    EP_ADDR           ecq_event_addr;
++    struct ep4_ecq     *ecq_flushcq;                          /*  and command port to issue setevent to */
++} EP4_ECQ;
++
++#define EP4_ECQ_EVENT         0                               /* command queues targetted by multi-blocks events */
++#define EP4_ECQ_ATOMIC                1                               /* command queues targetted by atomic store operations */
++#define EP4_ECQ_SINGLE                2                               /* command queues targetted by single word commands from main */
++#define EP4_ECQ_MAIN          3                               /* command queues targetted by multi word commands from main */
++#define EP4_NUM_ECQ           4
++
++#define EP4_ECQ_Size(which)           ((which) == EP4_ECQ_EVENT  ? CQ_Size64K : \
++                                       (which) == EP4_ECQ_ATOMIC ? CQ_Size8K  : \
++                                       (which) == EP4_ECQ_SINGLE ? CQ_Size1K  : \
++                                       (which) == EP4_ECQ_MAIN   ? CQ_Size8K  : \
++                                       CQ_Size1K)
++
++typedef struct ep4_dma_retry
++{
++    struct list_head    retry_link;                           /* chained on free/retry list */
++    unsigned long     retry_time;                             /* "lbolt" to retry at */
++    E4_DMA            retry_dma;                              /* DMA (in main memory) */
++} EP4_DMA_RETRY;
++
++#define EP4_DMA_RETRY_CQSIZE          CQ_Size8K                               /* size of command queue for dma retry */
++#define EP4_DMA_RETRY_FLOWCNT         (CQ_Size(EP4_DMA_RETRY_CQSIZE)/72)      /* # of reissued DMA's which can fit in */
++
++typedef struct ep4_inputq
++{
++    EP4_INTCOOKIE     q_intcookie;
++    unsigned int      q_slotSize;
++    unsigned int      q_slotCount;
++
++    void             *q_slots;
++    EP_ADDR           q_slotsAddr;
++    
++    EP_INPUTQ_CALLBACK *q_callback;
++    void             *q_arg;
++
++    sdramaddr_t               q_desc;
++    EP_ADDR           q_descAddr;
++    EP_ADDR           q_eventAddr;
++    EP4_ECQ          *q_wcq;                                  /* command queue to issue waitevent to */
++    EP4_ECQ          *q_ecq;                                  /* command queue targetted by event to generate interrupt */
++
++    EP_ADDR           q_fptr;                                 /* cached current front pointer */
++    EP_ADDR           q_last;                                 /* elan addr for last queue slot  */
++
++    atomic_t          q_fired;                                /* atomic flag that interrupt received */
++    unsigned int      q_count;                                /* count of slots consumed */
++} EP4_INPUTQ;
++
++typedef struct ep4_outputq
++{
++    spinlock_t                q_lock;
++    unsigned int      q_slotCount;
++    unsigned int      q_slotSize;
++    unsigned int        q_dwords;
++    ELAN4_CQ         *q_cq;
++    void             *q_main;
++    EP_ADDR           q_mainAddr;
++    unsigned int      q_retries;
++} EP4_OUTPUTQ;
++
++#endif /* ! defined(__elan4__) */
++
++typedef struct ep4_check_sten
++{
++    E4_uint64         c_reset_event_cmd;                      /* WRITEDWORD to reset start event */
++    E4_uint64                 c_reset_event_value;
++
++    E4_uint64         c_open;                                 /* OPEN VP_PROBE(lvl) */
++    E4_uint64         c_trans_traceroute0;                    /* SENDTRANS TR_TRACEROUTE 0s */
++    E4_uint64         c_addr_traceroute0;
++    E4_uint64         c_data_traceroute0[8];
++    E4_uint64         c_trans_traceroute1;                    /* SENDTRANS TR_TRACEROUTE 1s */
++    E4_uint64         c_addr_traceroute1;
++    E4_uint64         c_data_traceroute1[8];
++    E4_uint64         c_trans_sendack;                        /* SENDTRANS SENDACK */
++    E4_uint64         c_addr_sendack;
++    
++    E4_uint64         c_guard_ok;                             /* GUARD OK - write level */
++    E4_uint64         c_writedword_ok;
++    E4_uint64         c_value_ok;
++    
++    E4_uint64         c_guard_fail;                           /* GUARD FAIL - chain setevent/write fail */
++    E4_uint64         c_setevent_fail;
++    E4_uint64         c_setevent_nop;
++    E4_uint64         c_nop_pad;
++} EP4_CHECK_STEN;
++
++#define EP4_CHECK_STEN_NDWORDS        (sizeof (EP4_CHECK_STEN) >> 3)
++
++typedef struct ep4_rail_elan
++{
++    EP4_CHECK_STEN    r_check_sten[EP4_MAX_LEVELS];
++    E4_Event32                r_check_fail;                                   /* Check failed (== r_check_start[-1]) */
++    E4_Event32          r_check_start[EP4_MAX_LEVELS];
++
++    E4_Event32                r_qevents[EP_NUM_SYSTEMQ];
++    E4_Event32                r_flush_event;
++
++    E4_uint64         r_nodeid;
++#ifdef __elan4__
++    E4_uint64        *r_cookies;
++#else
++    E4_Addr           r_cookies;
++#endif
++} EP4_RAIL_ELAN;
++
++#define TRACEROUTE_ENTRIES    16                      /* 2 * ELAN_MAX_LEVELS */
++#define TRACEROUTE_NDWORDS    (TRACEROUTE_ENTRIES/2)
++
++typedef struct ep4_rail_main
++{
++    E4_uint32         r_probe_dest0[TRACEROUTE_ENTRIES];
++    E4_uint32         r_probe_dest1[TRACEROUTE_ENTRIES];
++    E4_uint64         r_probe_result;
++    E4_uint64         r_probe_level;
++
++    E4_uint64           r_dma_flowcnt;                                /*  count of dma's queued */
++} EP4_RAIL_MAIN;
++
++#define EP4_PROBE_ACTIVE      (0xffff)
++#define EP4_PROBE_FAILED      (0xfffe)
++
++#if !defined(__elan4__)
++
++typedef struct ep4_retry_ops
++{
++    struct list_head  op_link;
++    unsigned long     (*op_func)(EP4_RAIL *rail, void *arg, unsigned long nextRunTime);
++    void             *op_arg;
++} EP4_RETRY_OPS;
++
++typedef struct ep4_neterr_ops
++{
++    struct list_head  op_link;
++    void            (*op_func) (EP4_RAIL *rail, void *arg, unsigned int nodeId, EP_NETERR_COOKIE *cookies);
++    void             *op_arg;
++} EP4_NETERR_OPS;
++
++struct ep4_rail
++{
++    EP_RAIL           r_generic;
++    ELAN4_CTXT                r_ctxt;
++    ELAN4_ROUTE_TABLE  *r_routetable;
++    
++    spinlock_t                r_intcookie_lock;
++    struct list_head    r_intcookie_hash[EP4_INTCOOKIE_HASH_SIZE];
++
++    sdramaddr_t               r_elan;
++    EP_ADDR           r_elan_addr;
++    EP4_RAIL_MAIN      *r_main;
++    EP_ADDR           r_main_addr;
++    
++    EP_CODE           r_threadcode;                           /* copy of thread code */
++
++    sdramaddr_t               r_queuedescs;                           /* systemq queue descriptors */
++
++    E4_uint64        *r_cookies;                              /* network error cookies */
++    spinlock_t          r_cookie_lock;                                /*    and spin lock */
++
++    kcondvar_t                r_probe_wait;                           /* network position probing */
++    spinlock_t                r_probe_lock;
++    volatile int      r_probe_done;
++    EP4_INTCOOKIE     r_probe_intcookie;
++    EP4_ECQ           *r_probe_cq;
++    E4_uint32         r_probe_source0[TRACEROUTE_ENTRIES];
++    E4_uint32         r_probe_source1[TRACEROUTE_ENTRIES];
++
++    kmutex_t          r_haltop_mutex;                         /* halt/flush operations */
++    ELAN4_HALTOP      r_haltop;
++    ELAN4_DMA_FLUSHOP   r_flushop;
++    kcondvar_t                r_haltop_sleep;
++    spinlock_t                r_haltop_lock;
++
++    struct list_head    r_ecq_list[EP4_NUM_ECQ];              /* list of statically allocated command queues */
++    EP_RMAP          *r_ecq_rmap;                             /* resource map for command queue mappings */
++    spinlock_t          r_ecq_lock;                           /* spinlock for list/space management */
++
++    kmutex_t          r_flush_mutex;                          /* serialize command queue flushing */
++    unsigned long     r_flush_count;                          /* # setevents issued for flushing */
++    EP4_ECQ          *r_flush_mcq;                            /*   and command queue for waitevent */
++    EP4_ECQ            *r_flush_ecq;                          /*   and command queue for interrupt */
++    EP4_INTCOOKIE       r_flush_intcookie;                    /*   and interrupt cookie */
++    kcondvar_t          r_flush_sleep;                                /*   and place to sleep ... */
++
++    EP_KTHREAD                r_retry_thread;                         /* retry thread */
++    struct list_head    r_retry_ops;                          /*  list of retry operations */
++
++    EP4_RETRY_OPS       r_dma_ops;                            /* dma retry operations */
++    EP4_ECQ          *r_dma_ecq;                              /*   command queue to reissue DMAs */
++    E4_uint64           r_dma_flowcnt;                                /*   count of dma's reissued */
++    struct list_head    r_dma_retrylist[EP_NUM_RETRIES];      /*   retry lists  */
++    struct list_head    r_dma_freelist;                               /*   and free list */
++    spinlock_t          r_dma_lock;                           /*   and spinlock to protect lists */
++    unsigned long       r_dma_allocated;                      /*   # retries allocated*/
++    unsigned long       r_dma_reserved;                               /*   # retries reserved */
++
++    EP4_ECQ          *r_event_ecq;                            /* command queue for occasional setevents */
++
++    struct list_head    r_neterr_ops;                         /* list of neterr fixup operations */
++
++    ELAN4_IPROC_TRAP    r_iproc_trap;
++    ELAN4_TPROC_TRAP    r_tproc_trap;
++} ;
++
++#define EP4_CTXT_TO_RAIL(ctxt)        ((EP4_RAIL *) (((unsigned long) (ctxt)) - offsetof (EP4_RAIL, r_ctxt)))
++
++#if defined(DEBUG_ASSERT)
++#define EP4_ASSERT(rail,EXPR)                 EP_ASSERT(&((rail)->r_generic), EXPR)
++#define EP4_SDRAM_ASSERT(rail,off,value)      EP4_ASSERT(rail, (sdram_assert ? elan4_sdram_readq ((rail)->r_ctxt.ctxt_dev, (off)) == (value) : 1))
++#else
++#define EP4_ASSERT(rail,EXPR)
++#define EP4_SDRAM_ASSERT(rail,off,value)
++#endif
++
++/* kcomm_elan4.c */
++extern EP_RAIL    *ep4_create_rail (EP_SYS *sys, ELAN4_DEV *dev);
++extern void        ep4_destroy_rail (EP_RAIL *rail);
++
++extern int         ep4_start_rail (EP_RAIL *rail);
++extern void        ep4_stall_rail (EP_RAIL *rail);
++extern void        ep4_stop_rail (EP_RAIL *rail);
++
++extern void      ep4_debug_rail (EP_RAIL *rail);
++
++extern void        ep4_position_found (EP_RAIL *rail, ELAN_POSITION *pos);
++
++extern sdramaddr_t ep4_sdram_alloc (EP_RAIL *rail, EP_ADDR addr, unsigned int size);
++extern void        ep4_sdram_free (EP_RAIL *rail, sdramaddr_t addr, unsigned int size);
++extern void        ep4_sdram_writeb (EP_RAIL *rail, sdramaddr_t addr, unsigned char val);
++
++extern void        ep4_flush_tlb (EP_RAIL *r);
++extern void        ep4_load_system_route (EP_RAIL *r, unsigned int vp, unsigned int lowNode, unsigned int highNode);
++extern void        ep4_load_node_route (EP_RAIL *r, unsigned int nodeId);
++extern void        ep4_unload_node_route (EP_RAIL *r, unsigned int nodeId);
++extern void        ep4_lower_filter (EP_RAIL *r, unsigned int nodeId);
++extern void        ep4_raise_filter (EP_RAIL *rail, unsigned int nodeId);
++extern void        ep4_node_disconnected (EP_RAIL *r, unsigned int nodeId);
++
++/* kmap_elan4.c */
++extern void        ep4_kaddr_map (EP_RAIL *r, EP_ADDR eaddr, virtaddr_t kaddr, unsigned int len, unsigned int perm, int ep_attr);
++extern void        ep4_sdram_map (EP_RAIL *r, EP_ADDR eaddr, sdramaddr_t saddr, unsigned int len, unsigned int perm, int ep_attr);
++extern void        ep4_ioaddr_map (EP_RAIL *r, EP_ADDR eaddr, ioaddr_t ioaddr, unsigned int len, unsigned int perm);
++extern void        ep4_unmap (EP_RAIL *r, EP_ADDR eaddr, unsigned int len);
++extern void       *ep4_dvma_reserve (EP_RAIL *r, EP_ADDR eaddr, unsigned int npages);
++extern void        ep4_dvma_release (EP_RAIL *r, EP_ADDR eaddr, unsigned int npages, void *private);
++extern void        ep4_dvma_set_pte (EP_RAIL *r, void *private, unsigned int index, physaddr_t paddr, unsigned int perm);
++extern physaddr_t  ep4_dvma_read_pte (EP_RAIL *r, void *private, unsigned int index);
++extern void        ep4_dvma_unload (EP_RAIL *r, void *private, unsigned int index, unsigned int npages);
++
++/* kmsg_elan4.c */
++extern EP_INPUTQ  *ep4_alloc_inputq (EP_RAIL *r, unsigned int qnum, unsigned int slotSize, unsigned int slotCount,
++                                   EP_INPUTQ_CALLBACK *callback, void *arg);
++extern void        ep4_free_inputq (EP_RAIL *r, EP_INPUTQ *q);
++extern void        ep4_enable_inputq (EP_RAIL *r, EP_INPUTQ *q);
++extern void        ep4_disable_inputq (EP_RAIL *r, EP_INPUTQ *q);
++extern int         ep4_poll_inputq (EP_RAIL *r, EP_INPUTQ *q, int maxCount, EP_INPUTQ_HANDLER *handler, void *arg);
++extern EP_OUTPUTQ *ep4_alloc_outputq (EP_RAIL *r, unsigned int slotSize, unsigned int slotCount);
++extern void        ep4_free_outputq (EP_RAIL *r, EP_OUTPUTQ *q);
++extern void       *ep4_outputq_msg (EP_RAIL *r, EP_OUTPUTQ *q, unsigned int slotNum);
++extern int         ep4_outputq_state (EP_RAIL *r, EP_OUTPUTQ *q, unsigned int slotNum);
++extern int         ep4_outputq_send (EP_RAIL *r, EP_OUTPUTQ *q, unsigned int slotNum, unsigned int size,
++                                   unsigned int nodeId, unsigned int qnum, unsigned int retries);
++
++/* probenetwork_elan4.c */
++extern int         ep4_probe_init (EP4_RAIL *r);
++extern void        ep4_probe_destroy (EP4_RAIL *r);
++extern void        ep4_probe_position_found (EP4_RAIL *rail, ELAN_POSITION *pos);
++extern int         ep4_probe_route (EP_RAIL *r, int level, int sw, int nodeid, int *linkup, int *linkdown, int attempts, EP_SWITCH *lsw);
++extern int         ep4_check_position (EP_RAIL *rail);
++
++/* support_elan4.c */
++extern ELAN4_TRAP_OPS ep4_trap_ops;
++extern void           ep4_register_intcookie (EP4_RAIL *rail, EP4_INTCOOKIE *cp, E4_uint64 cookie, void (*callback)(EP4_RAIL *r, void *arg), void *arg);
++extern void           ep4_deregister_intcookie (EP4_RAIL *rail, EP4_INTCOOKIE *cp);
++extern EP4_INTCOOKIE *ep4_lookup_intcookie (EP4_RAIL *rail, E4_uint64 cookie);
++extern E4_uint64      ep4_neterr_cookie (EP4_RAIL *rail, unsigned int node);
++
++extern void           ep4_flush_filters (EP_RAIL *r);
++extern void           ep4_flush_queues (EP_RAIL *r);
++extern void         ep4_write_qdesc (EP4_RAIL *rail, sdramaddr_t qaddr, E4_InputQueue *qdesc);
++
++extern EP4_ECQ       *ep4_alloc_ecq (EP4_RAIL *rail, unsigned int cqsize);
++extern void           ep4_free_ecq (EP4_RAIL *rail, EP4_ECQ *ecq);
++extern EP4_ECQ             *ep4_get_ecq (EP4_RAIL *rail, unsigned int which, unsigned int ndwords);
++extern void           ep4_put_ecq (EP4_RAIL *rail, EP4_ECQ *ecq, unsigned int ndwords);
++
++extern void           ep4_nop_cmd (EP4_ECQ *ecq, E4_uint64 tag);
++extern void           ep4_set_event_cmd (EP4_ECQ *ecq, E4_Addr event);
++extern void           ep4_wait_event_cmd (EP4_ECQ *ecq, E4_Addr event, E4_uint64 candt, E4_uint64 param0, E4_uint64 param1);
++
++extern void           ep4_flush_interrupt (EP4_RAIL *rail, void *arg);
++extern void           ep4_flush_ecqs (EP4_RAIL *rail);
++
++extern void           ep4_init_thread (EP4_RAIL *rail, E4_ThreadRegs *regs, sdramaddr_t stackTop, 
++                                     EP_ADDR stackAddr, E4_Addr startpc, int nargs,...);
++
++extern void           ep4_initialise_dma_retries (EP4_RAIL *rail);
++extern void           ep4_finalise_dma_retries (EP4_RAIL *rail);
++extern int            ep4_reserve_dma_retries (EP4_RAIL *rail, unsigned int count, unsigned int attr);
++extern void         ep4_release_dma_retries(EP4_RAIL *rail, unsigned int count);
++extern void           ep4_queue_dma_retry (EP4_RAIL *rail, E4_DMA *dma, int interval);
++extern void           ep4_queue_dma_stalled (EP4_RAIL *rail, E4_DMA *dma);
++extern void           ep4_free_stalled_dmas (EP4_RAIL *rail, unsigned int nodeId);
++extern void           ep4_display_rail (EP4_RAIL *rail);
++
++extern void           ep4_add_retry_ops (EP4_RAIL *rail, EP4_RETRY_OPS *ops);
++extern void           ep4_remove_retry_ops (EP4_RAIL *rail, EP4_RETRY_OPS *ops);
++extern void           ep4_retry_thread (EP4_RAIL *rail);
++
++/* neterr_elan4.c */
++extern void           ep4_add_neterr_ops (EP4_RAIL *rail, EP4_NETERR_OPS *ops);
++extern void           ep4_remove_neterr_ops (EP4_RAIL *rail, EP4_NETERR_OPS *ops);
++extern void           ep4_neterr_fixup (EP_RAIL *r, unsigned int nodeId, EP_NETERR_COOKIE *cookies);
++
++/* commands_elan4.c */
++extern void           elan4_nop_cmd (ELAN4_CQ *cq, E4_uint64 tag);
++extern void           elan4_write_dword_cmd (ELAN4_CQ *cq, E4_Addr addr, E4_uint64 data);
++extern void           elan4_add_dword_cmd (ELAN4_CQ *cq, E4_Addr addr, E4_uint64 data);
++extern void           elan4_copy64_cmd (ELAN4_CQ *cq, E4_Addr from, E4_Addr to, E4_uint32 datatype);
++extern void           elan4_interrupt_cmd (ELAN4_CQ *cq, E4_uint64 cookie);
++extern void           elan4_run_thread_cmd (ELAN4_CQ *cq, E4_ThreadRegs *regs);
++extern void           elan4_run_dma_cmd (ELAN4_CQ *cq, E4_DMA *dma);
++extern void           elan4_set_event_cmd (ELAN4_CQ *cq, E4_Addr event);
++extern void           elan4_set_eventn_cmd (ELAN4_CQ *cq, E4_Addr event, E4_uint32 count);
++extern void           elan4_wait_event_cmd (ELAN4_CQ *cq, E4_Addr event, E4_uint64 candt, E4_uint64 param0, E4_uint64 param1);
++extern void           elan4_open_packet (ELAN4_CQ *cq, E4_uint64 command);
++extern void           elan4_guard (ELAN4_CQ *cq, E4_uint64 command);
++extern void           elan4_sendtrans0 (ELAN4_CQ *cq, E4_uint16 trtype, E4_uint64 addr);
++extern void           elan4_sendtrans1 (ELAN4_CQ *cq, E4_uint16 trtype, E4_uint64 addr, E4_uint64 p0);
++extern void           elan4_sendtrans2 (ELAN4_CQ *cq, E4_uint16 trtype, E4_uint64 addr, E4_uint64 p0, E4_uint64 p1);
++extern void           elan4_sendtransn (ELAN4_CQ *cq, E4_uint16 trtype, E4_uint64 addr, ...);
++extern void           elan4_sendtransp (ELAN4_CQ *cq, E4_uint16 trtype, E4_uint64 addr, E4_uint64 *ptr);
++
++extern void           ep4_add_retry_ops (EP4_RAIL *rail, EP4_RETRY_OPS *ops);
++extern void         ep4_remove_retry_ops (EP4_RAIL *rail, EP4_RETRY_OPS *ops);
++extern void           ep4_retry_thread (EP4_RAIL *rail);
++
++extern void           ep4_fillout_stats(EP_RAIL *rail, char *str);
++
++#endif /* ! defined(__elan4__) */
++
++#endif /* __EP_KCOMM_ELAN4_H */
+Index: linux-2.4.21/drivers/net/qsnet/ep/kcomm_vp.h
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/kcomm_vp.h  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/kcomm_vp.h       2005-06-01 23:12:54.668428616 -0400
+@@ -0,0 +1,36 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __EP_KCOMM_VP_H
++#define __EP_KCOMM_VP_H
++
++#ident "@(#)$Id: kcomm_vp.h,v 1.2 2004/03/24 11:32:56 david Exp $ $Name: QSNETMODULES-4-30_20050128 $"
++/*      $Source: /cvs/master/quadrics/epmod/kcomm_vp.h,v $*/
++
++#define EP_MAX_NODES                  2048                    /* Max nodes we support */
++
++/* virtual process allocation */
++#define EP_VP_NODE_BASE                       (0)
++#define EP_VP_DATA_BASE                       (EP_VP_NODE_BASE + EP_MAX_NODES)
++#define EP_VP_PROBE_BASE              (EP_VP_DATA_BASE + EP_MAX_NODES)
++#define EP_VP_PROBE_COUNT             ELAN_MAX_LEVELS
++
++#define EP_VP_BCAST_BASE              (EP_VP_PROBE_BASE + EP_VP_PROBE_COUNT)
++#define EP_VP_BCAST_COUNT             (CM_SGMTS_PER_LEVEL * (CM_MAX_LEVELS - 1) + 1)
++
++#define EP_VP_NODE(nodeId)            (EP_VP_NODE_BASE + (nodeId))
++#define EP_VP_DATA(nodeId)            (EP_VP_DATA_BASE + (nodeId))
++#define EP_VP_PROBE(lvl)              (EP_VP_PROBE_BASE + (lvl))
++#define EP_VP_BCAST(lvl,sgmt)         (EP_VP_BCAST_BASE + ((lvl) - 1)*CM_SGMTS_PER_LEVEL + (sgmt))
++
++#define EP_VP_TO_NODE(vp)             ((vp) & (EP_MAX_NODES-1))
++#define EP_VP_ISDATA(vp)              ((vp) >= EP_VP_DATA_BASE && (vp) < (EP_VP_DATA_BASE + EP_MAX_NODES))
++
++#endif /* __EP_KCOMM_VP_H */
++
++
+Index: linux-2.4.21/drivers/net/qsnet/ep/kmap.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/kmap.c      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/kmap.c   2005-06-01 23:12:54.669428464 -0400
+@@ -0,0 +1,561 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: kmap.c,v 1.10.6.2 2004/12/14 10:19:14 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/kmap.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <qsnet/kpte.h>
++
++#include <elan/kcomm.h>
++
++#include "debug.h"
++
++#if defined(DIGITAL_UNIX)
++#  define kernel_map                  (first_task->map)
++#  define vaddr_to_phys(map, addr)    (pmap_extract (vm_map_pmap ((vm_map_t) map), (unsigned long) addr))
++#elif defined(LINUX)
++#  define kernel_map                  get_kern_mm()
++#  define vaddr_to_phys(map, addr)    (kmem_to_phys(addr))
++#elif defined(SOLARIS)
++#  define kernel_map                  &kas
++#  define vaddr_to_phys(map,addr)     ptob(hat_getpfnum (((struct as *) map)->a_hat, (caddr_t) addr))
++#endif
++
++void
++ep_perrail_kaddr_map (EP_RAIL *rail, EP_ADDR eaddr, virtaddr_t kaddr, unsigned long len,  unsigned int perm, int ep_attr)
++{
++    rail->Operations.KaddrMap (rail, eaddr, kaddr, len, perm, ep_attr);
++}
++
++void
++ep_perrail_sdram_map (EP_RAIL *rail, EP_ADDR eaddr, sdramaddr_t saddr, unsigned long len, unsigned int perm, int ep_attr)
++{
++    rail->Operations.SdramMap (rail, eaddr, saddr, len, perm, ep_attr);
++}
++
++void
++ep_perrail_unmap (EP_RAIL *rail, EP_ADDR eaddr, unsigned long len)
++{
++    rail->Operations.Unmap (rail, eaddr, len);
++}
++
++void
++ep_perrail_dvma_sync (EP_RAIL *rail)
++{
++    if (rail->TlbFlushRequired)
++    {
++      rail->TlbFlushRequired = 0;
++
++      rail->Operations.FlushTlb (rail);
++    }
++}
++
++
++static int ep_dvma_map_rails (EP_SYS *sys, EP_NMH *nmh, EP_NMD *nmd, EP_RAILMASK mask);
++
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++static uint16_t ep_dvma_calc_check_sum (EP_SYS *sys, EP_NMH *nmh, EP_NMD *nmd, uint16_t check_sum);
++#endif
++
++EP_NMH_OPS ep_dvma_nmh_ops = 
++{
++    ep_dvma_map_rails,
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++    ep_dvma_calc_check_sum
++#endif
++};
++
++extern void
++ep_dvma_init (EP_SYS *sys)
++{
++    EP_DVMA_STATE *d = &sys->DvmaState;
++
++    kmutex_init (&d->dvma_lock);
++    
++    INIT_LIST_HEAD (&d->dvma_handles);
++    INIT_LIST_HEAD (&d->dvma_rails);
++
++    d->dvma_rmap = ep_rmallocmap (EP_DVMA_RMAP_SIZE, "dvma_rmap", 1);
++
++    ep_rmfree (d->dvma_rmap, EP_DVMA_TOP - EP_DVMA_BASE, EP_DVMA_BASE);
++}
++
++extern void
++ep_dvma_fini (EP_SYS *sys)
++{
++    EP_DVMA_STATE *d = &sys->DvmaState;
++
++    ep_rmfreemap (d->dvma_rmap);
++
++    kmutex_destroy (&d->dvma_lock);
++}
++    
++extern int
++ep_dvma_add_rail (EP_SYS *sys, EP_RAIL *rail)
++{
++    EP_DVMA_STATE    *d = &sys->DvmaState;
++    EP_RAIL_ENTRY    *l;
++    struct list_head *el;
++
++    KMEM_ZALLOC (l, EP_RAIL_ENTRY *, sizeof (EP_RAIL_ENTRY), 1);
++
++    if (l == NULL)
++      return (ENOMEM);
++
++    kmutex_lock (&d->dvma_lock);
++
++    l->Rail = rail;
++
++    list_add_tail (&l->Link, &d->dvma_rails);
++
++    list_for_each (el, &d->dvma_handles) {
++      EP_DVMA_NMH *desc   = list_entry (el, EP_DVMA_NMH, dvma_link);
++      int          npages = desc->dvma_nmh.nmh_nmd.nmd_len >> PAGESHIFT;
++
++      desc->dvma_rails[rail->Number] = rail;
++      desc->dvma_railmask |= ( 1 << rail->Number);
++
++      desc->dvma_private[rail->Number] = rail->Operations.DvmaReserve (rail, desc->dvma_nmh.nmh_nmd.nmd_addr, npages);
++    }
++
++    kmutex_unlock (&d->dvma_lock);
++    return (0);
++}
++
++extern void
++ep_dvma_remove_rail (EP_SYS *sys, EP_RAIL *rail)
++{
++    EP_DVMA_STATE    *d = &sys->DvmaState;
++    struct list_head *el;
++
++    kmutex_lock (&d->dvma_lock);
++    
++    list_for_each (el, &d->dvma_handles) {
++      EP_DVMA_NMH *desc   = list_entry (el, EP_DVMA_NMH, dvma_link);
++      int          npages = desc->dvma_nmh.nmh_nmd.nmd_len >> PAGESHIFT;
++
++      desc->dvma_rails[rail->Number] = NULL;
++      desc->dvma_railmask &= ~(1 << rail->Number);
++
++      rail->Operations.DvmaRelease (rail, desc->dvma_nmh.nmh_nmd.nmd_addr, npages, desc->dvma_private[rail->Number]);
++    }
++
++    list_for_each (el, &d->dvma_rails) {
++      EP_RAIL_ENTRY *tmp = list_entry (el, EP_RAIL_ENTRY, Link);
++
++      if (tmp->Rail == rail)
++      {
++          list_del (el);
++
++          KMEM_FREE (tmp, sizeof (EP_RAIL_ENTRY));
++          break;
++      }
++    }
++    kmutex_unlock (&d->dvma_lock);
++}
++
++EP_NMH *
++ep_dvma_reserve (EP_SYS *sys, unsigned npages, unsigned perm)
++{
++    EP_DVMA_STATE    *d = &sys->DvmaState;
++    EP_DVMA_NMH      *desc;
++    EP_ADDR         addr;
++    struct list_head *el;
++    int               i;
++
++    KMEM_ZALLOC (desc, EP_DVMA_NMH *, offsetof (EP_DVMA_NMH, dvma_attrs[npages]), 1);
++    
++    if (desc == NULL)
++      return (NULL);
++
++    if ((addr = ep_rmalloc (d->dvma_rmap, npages << PAGESHIFT, 0)) == 0)
++    {
++
++      KMEM_FREE (desc, sizeof (EP_DVMA_NMH));
++      return (NULL);
++    }
++
++    spin_lock_init (&desc->dvma_lock);
++
++    desc->dvma_perm = perm;
++
++    kmutex_lock (&d->dvma_lock);
++    /* reserve the mapping resource */
++    list_for_each (el, &d->dvma_rails) {
++      EP_RAIL *rail = list_entry (el, EP_RAIL_ENTRY, Link)->Rail;
++
++      EPRINTF4 (DBG_KMAP, "%s: ep_dvma_reserve desc=%p npages=%d rail=%p\n", rail->Name, desc, npages, rail);
++
++      if ((desc->dvma_private[rail->Number] = rail->Operations.DvmaReserve (rail, addr, npages)) == NULL)
++      {
++          printk ("%s: !!ep_dvma_reserve - rail->DvmaReserve failed\n", rail->Name);
++          goto failed;
++      }
++
++      desc->dvma_rails[rail->Number] = rail;
++      desc->dvma_railmask |= (1 << rail->Number);
++    }
++
++    /* insert into the network mapping handle table */
++    desc->dvma_nmh.nmh_nmd.nmd_addr = addr;
++    desc->dvma_nmh.nmh_nmd.nmd_len  = npages << PAGESHIFT;
++    desc->dvma_nmh.nmh_nmd.nmd_attr = EP_NMD_ATTR (sys->Position.pos_nodeid, 0);
++    desc->dvma_nmh.nmh_ops          = &ep_dvma_nmh_ops;
++
++    ep_nmh_insert (&sys->MappingTable, &desc->dvma_nmh);
++
++    list_add (&desc->dvma_link, &d->dvma_handles);
++
++    kmutex_unlock (&d->dvma_lock);
++
++    return (&desc->dvma_nmh);
++
++ failed:
++
++    kmutex_unlock (&d->dvma_lock);
++
++    for (i = 0; i < EP_MAX_RAILS; i++)
++      if (desc->dvma_rails[i] != NULL)
++          desc->dvma_rails[i]->Operations.DvmaRelease (desc->dvma_rails[i], addr, npages, desc->dvma_private[i]);
++
++    ep_rmfree (d->dvma_rmap, npages << PAGESHIFT, addr);
++
++    KMEM_FREE (desc, sizeof (EP_DVMA_NMH));
++    return (NULL);
++}
++
++void
++ep_dvma_release (EP_SYS *sys, EP_NMH *nmh)
++{
++    EP_DVMA_STATE *d      = &sys->DvmaState;
++    EP_DVMA_NMH   *desc   = (EP_DVMA_NMH *) nmh;
++    EP_ADDR        addr   = nmh->nmh_nmd.nmd_addr;
++    int            npages = nmh->nmh_nmd.nmd_len >> PAGESHIFT;
++    EP_RAIL       *rail;
++    int            i;
++
++    kmutex_lock (&d->dvma_lock);
++
++    list_del (&desc->dvma_link);
++    
++    ep_nmh_remove (&sys->MappingTable, nmh);
++
++    for (i = 0; i < EP_MAX_RAILS; i++)
++      if ((rail = desc->dvma_rails[i]) != NULL)
++          rail->Operations.DvmaRelease (rail, addr, npages, desc->dvma_private[i]);
++
++    ep_rmfree (d->dvma_rmap, npages << PAGESHIFT, addr);
++
++    KMEM_FREE (desc, offsetof (EP_DVMA_NMH, dvma_attrs[npages]));
++
++    kmutex_unlock (&d->dvma_lock);
++}
++
++void
++ep_dvma_load (EP_SYS *sys, void *map, caddr_t vaddr, unsigned len, EP_NMH *nmh, unsigned index, EP_RAILMASK *hints, EP_NMD *subset)
++{
++    EP_DVMA_NMH *desc = (EP_DVMA_NMH *) nmh;
++    unsigned     offset = (unsigned long) vaddr & PAGEOFFSET;
++    unsigned     npages = btopr (len + offset);
++    EP_ADDR      addr   = nmh->nmh_nmd.nmd_addr + (index << PAGESHIFT);
++    int                rmask  = *hints;
++    EP_RAIL     *rail;
++    register int i, rnum;
++    unsigned long flags;
++
++    EPRINTF7 (DBG_KMAP, "ep_dvma_load: map=%p vaddr=%p len=%x nmh=%p(%x,%x) index=%d\n",
++            map, vaddr, len, nmh, nmh->nmh_nmd.nmd_addr, nmh->nmh_nmd.nmd_len, index);
++
++    /* If no rail specified, then map into all rails */
++    if (rmask == 0)
++      rmask = desc->dvma_railmask;
++
++    ASSERT ((index + npages) <= (nmh->nmh_nmd.nmd_len >> PAGESHIFT));
++
++    /* If not map specified then use the kernel map */
++    if (map == NULL)
++      map = kernel_map;
++
++    spin_lock_irqsave (&desc->dvma_lock, flags);
++    /* Now map each of the specified pages (backwards) */
++
++    vaddr = (vaddr - offset) + (npages-1)*PAGESIZE;
++    for (i = npages-1; i >= 0; i--, vaddr -= PAGESIZE)
++    {
++      physaddr_t paddr = vaddr_to_phys (map, vaddr);
++      
++      for (rnum = 0; rnum < EP_MAX_RAILS; rnum++)
++      {
++          if (! (rmask & (1 << rnum)) || (rail = desc->dvma_rails[rnum]) == NULL)
++              rmask &= ~(1 << rnum);
++          else
++          {
++              rail->Operations.DvmaSetPte (rail, desc->dvma_private[rnum], index + i, paddr, desc->dvma_perm);
++
++              desc->dvma_attrs[index + i] |= (1 << rnum);
++          }
++      }
++    }
++
++    for (rnum = 0; rnum < EP_MAX_RAILS; rnum++)
++      if ((rmask & (1 << rnum)) && (rail = desc->dvma_rails[rnum]) != NULL)
++          rail->TlbFlushRequired = 1;
++
++    spin_unlock_irqrestore (&desc->dvma_lock, flags);
++
++    /* Construct the network mapping handle to be returned. */
++    subset->nmd_addr = addr + offset;
++    subset->nmd_len  = len;
++    subset->nmd_attr = EP_NMD_ATTR(sys->Position.pos_nodeid, rmask);
++}
++
++void
++ep_dvma_unload (EP_SYS *sys, EP_NMH *nmh, EP_NMD *nmd)
++{
++    EP_DVMA_NMH *desc = (EP_DVMA_NMH *) nmh;
++    unsigned     offset = nmd->nmd_addr & PAGEOFFSET;
++    unsigned     npages = btopr (nmd->nmd_len + offset);
++    unsigned     index  = (nmd->nmd_addr - nmh->nmh_nmd.nmd_addr) >> PAGESHIFT;
++    EP_RAIL     *rail;
++    int          rnum;
++    int          rmask;
++    register int i;
++    unsigned long flags;
++    
++    spin_lock_irqsave (&desc->dvma_lock, flags);
++
++    /* compute which rails we need to unload on */
++    for (rmask = 0, i = 0; i < npages; i++)
++    {
++      rmask |= desc->dvma_attrs[index + i];
++      
++      desc->dvma_attrs[index + i] = 0;
++    }
++    
++    for (rnum = 0; rnum < EP_MAX_RAILS; rnum++)
++      if ((rmask & (1 << rnum)) && (rail = desc->dvma_rails[rnum]) != NULL)
++          rail->Operations.DvmaUnload (rail, desc->dvma_private[rnum], index, npages);
++
++    spin_unlock_irqrestore (&desc->dvma_lock, flags);
++}
++
++int
++ep_dvma_map_rails (EP_SYS *sys, EP_NMH *nmh, EP_NMD *nmd, EP_RAILMASK mask)
++{
++    EP_DVMA_NMH *desc = (EP_DVMA_NMH *) nmh;
++    unsigned     offset = nmd->nmd_addr & PAGEOFFSET;
++    unsigned     npages = btopr (nmd->nmd_len + offset);
++    unsigned     index  = (nmd->nmd_addr - nmh->nmh_nmd.nmd_addr) >> PAGESHIFT;
++    int          r, rnum;
++    register int i;
++    unsigned long flags;
++
++    spin_lock_irqsave (&desc->dvma_lock, flags);
++
++    EPRINTF4 (DBG_KMAP, "ep_dvma_map_rails: nmd=%08x.%08x.%08x mask=%04x\n", nmd->nmd_addr, nmd->nmd_len, nmd->nmd_attr, mask);
++
++    if ((mask &= desc->dvma_railmask) == 0)
++    {
++      printk ("ep_dvma_map_rails: no intersecting rails %04x.%04x\n", mask, desc->dvma_railmask);
++      spin_unlock_irqrestore (&desc->dvma_lock, flags);
++      return (-1);
++    }
++
++    for (i = npages-1; i >= 0; i--)
++    {
++      int pgidx = (index + i);
++
++      for (rnum = 0; rnum < EP_MAX_RAILS; rnum++)
++          if (desc->dvma_attrs[pgidx] & (1 << rnum))
++              break;
++      
++      if (rnum == EP_MAX_RAILS)
++      {
++          EPRINTF3 (DBG_KMAP, "ep_dvma_map_rails: nmh=%p idx=%x [%08x] not ptes valid\n", nmh, pgidx, 
++                    nmh->nmh_nmd.nmd_addr + ((pgidx) << PAGESHIFT));
++          mask = 0;
++      }
++      else
++      {
++          EP_RAIL   *rail  = desc->dvma_rails[rnum];
++          physaddr_t paddr = rail->Operations.DvmaReadPte (rail, desc->dvma_private[rnum], pgidx);
++          
++          EPRINTF5 (DBG_KMAP, "%s: ep_dvma_map_rails: nmh=%p idx=%x [%08x] paddr %llx\n", rail->Name, nmh, pgidx,
++                    nmh->nmh_nmd.nmd_addr + (pgidx << PAGESHIFT), (long long) paddr);
++          
++          for (r = 0; r < EP_MAX_RAILS; r++)
++          {
++              if ((mask & (1 << r)) == 0)
++                  continue;
++              
++              if ((desc->dvma_attrs[pgidx] & (1 << r)) == 0)
++              {
++                  EPRINTF5 (DBG_KMAP, "%s: ep_dvma_map_rails: nmh=%p idx=%x [%08x] paddr=%llx\n",
++                            desc->dvma_rails[rnum]->Name, nmh, pgidx, nmh->nmh_nmd.nmd_addr + (pgidx << PAGESHIFT), 
++                            (long long) paddr);
++                  
++                  rail->Operations.DvmaSetPte (rail, desc->dvma_private[rnum], pgidx, paddr, desc->dvma_perm);
++                  
++                  desc->dvma_attrs[pgidx] |= (1 << r);
++              }
++          }
++      }
++    }
++
++    for (rnum = 0; rnum < EP_MAX_RAILS; rnum++)
++      if ((mask & (1 << rnum)) != 0)
++          desc->dvma_rails[rnum]->TlbFlushRequired = 1;
++
++    EPRINTF4 (DBG_KMAP, "ep_dvma_map_rails: nmd=%08x.%08x.%08x|%04x\n", nmd->nmd_addr, nmd->nmd_len, nmd->nmd_attr, mask);
++
++    /* Finally update the network memory descriptor */
++    nmd->nmd_attr |= mask;
++
++    spin_unlock_irqrestore (&desc->dvma_lock, flags);
++
++    return (0);
++}
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++#include <linux/highmem.h>
++
++/* Generic rolling checksum algorithm */
++uint16_t
++rolling_check_sum (char *msg, int nob, uint16_t sum)
++{
++    while (nob-- > 0)
++      sum = sum * 13 + *msg++;
++
++    return (sum);
++}
++
++#if ! defined(NO_RMAP)
++void  
++unmap_phys_address(unsigned long phys_addr)
++{
++    unsigned long pfn = (phys_addr >> PAGE_SHIFT);
++    
++    if (pfn_valid(pfn)) 
++      kunmap(pfn_to_page(pfn));
++}
++
++void * 
++map_phys_address(unsigned long phys_addr)
++{
++    unsigned long pfn = (phys_addr >> PAGE_SHIFT);
++    
++    if (pfn_valid(pfn)) 
++      return  kmap(pfn_to_page(pfn));
++    
++    return NULL;
++}
++#else
++void  
++unmap_phys_address(unsigned long phys_addr)
++{
++    struct page *p = virt_to_page(__va(phys_addr));
++    
++    if (VALID_PAGE(p)) 
++      kunmap(p);
++}
++
++void * 
++map_phys_address(unsigned long phys_addr)
++{
++    struct page *p = virt_to_page(__va(phys_addr));
++                              
++    if (VALID_PAGE(p)) 
++      return  kmap(p);
++    
++    return NULL;
++}
++#endif
++
++uint16_t
++ep_dvma_calc_check_sum (EP_SYS *sys, EP_NMH *nmh, EP_NMD *nmd, uint16_t check_sum)
++{
++    /* cant be called from an interupt */
++
++    EP_DVMA_NMH *desc = (EP_DVMA_NMH *) nmh;
++    unsigned     offset = nmd->nmd_addr & PAGEOFFSET;
++    unsigned     npages = btopr (nmd->nmd_len + offset);
++    unsigned     index  = (nmd->nmd_addr - nmh->nmh_nmd.nmd_addr) >> PAGESHIFT;
++    unsigned     start, len;
++    int          rnum;
++    register int i;
++    unsigned long flags;
++    EP_RAIL      *rail;
++
++
++    spin_lock_irqsave (&desc->dvma_lock, flags);
++
++    EPRINTF3 (DBG_KMAP, "ep_dvma_calc_check_sum: nmd=%08x.%08x.%08x \n", nmd->nmd_addr, nmd->nmd_len, nmd->nmd_attr);
++ 
++    /* find a rail */
++    for (rnum = 0; rnum < EP_MAX_RAILS; rnum++)
++      if (desc->dvma_attrs[index] & (1 << rnum))
++          break;
++      
++    ASSERT (rnum != EP_MAX_RAILS);
++ 
++    rail = desc->dvma_rails[rnum];
++
++    for (i = 0; i <= (npages-1); i++)
++    {
++      int        pgidx = (index + i);
++      physaddr_t paddr = rail->Operations.DvmaReadPte (rail, desc->dvma_private[rnum], pgidx);
++      void *     virt;
++
++      spin_unlock_irqrestore (&desc->dvma_lock, flags); /* unlock for check sum calc */
++
++      virt = map_phys_address(paddr);
++
++      if (!virt)
++          printk("ep_dvma_calc_check_sum: virt = NULL ! \n");
++      else {
++          if ( i == 0 ) {
++              /* last bit of the first page */
++              start =  (nmd->nmd_addr & (PAGESIZE - 1)) ;
++              len   =  PAGESIZE - start;
++              if ( len > nmd->nmd_len) /* less than the remaining page */ 
++                  len =  nmd->nmd_len;
++          } else {
++              if ( i != (npages-1)) {
++                  /* all of the middle pages    */
++                  start = 0;
++                  len   = PAGESIZE;
++              } else {
++                  /* first bit of the last page */
++                  start = 0;
++                  len   = ((nmd->nmd_addr + nmd->nmd_len -1) & (PAGESIZE -1)) +1;
++              }
++          }
++
++          check_sum = rolling_check_sum (((char *)virt)+start, len, check_sum);
++          unmap_phys_address(paddr);
++   
++          /* re aquire the lock */
++          spin_lock_irqsave (&desc->dvma_lock, flags);
++      }
++      
++      EPRINTF5 (DBG_KMAP, "%s: ep_dvma_calc_check_sum: nmh=%p idx=%x [%08x] paddr %llx\n", rail->Name, nmh, pgidx,
++                nmh->nmh_nmd.nmd_addr + (pgidx << PAGESHIFT), (long long) paddr);     
++    }
++
++    EPRINTF4 (DBG_KMAP, "ep_dvma_calc_check_sum: nmd=%08x.%08x.%08x = %d\n", nmd->nmd_addr, nmd->nmd_len, nmd->nmd_attr, check_sum);
++
++    spin_unlock_irqrestore (&desc->dvma_lock, flags);
++
++    return (check_sum);
++}
++#endif
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/ep/kmap_elan3.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/kmap_elan3.c        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/kmap_elan3.c     2005-06-01 23:12:54.670428312 -0400
+@@ -0,0 +1,209 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: kmap_elan3.c,v 1.3.8.1 2004/12/14 10:19:14 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/kmap_elan3.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elanvp.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanctxt.h>
++#include <elan3/elandebug.h>
++
++#include <elan/kcomm.h>
++
++#include "kcomm_elan3.h"
++
++#if defined(DIGITAL_UNIX)
++#  define kernel_map                  (first_task->map)
++#  define vaddr_to_phys(map, addr)    (pmap_extract (vm_map_pmap ((vm_map_t) map), (unsigned long) addr))
++#elif defined(LINUX)
++#  define kernel_map                  get_kern_mm()
++#  define vaddr_to_phys(map, addr)    (kmem_to_phys(addr))
++#elif defined(SOLARIS)
++#  define kernel_map                  &kas
++#  define vaddr_to_phys(map,addr)     ptob(hat_getpfnum (((struct as *) map)->a_hat, (caddr_t) addr))
++#endif
++
++#define ELAN3_PTES_PER_PAGE           (PAGESIZE/ELAN3_PAGE_SIZE)
++
++#if defined(__LITTLE_ENDIAN__)
++#define PERM_ENDIAN   0
++#else
++#define PERM_ENDIAN   ELAN3_PTE_BIG_ENDIAN
++#endif
++
++static unsigned int main_permtable[] = 
++{
++    ELAN3_PERM_REMOTEALL,             /* EP_PERM_EXECUTE */
++    ELAN3_PERM_REMOTEREAD,            /* EP_PERM_READ */
++    ELAN3_PERM_REMOTEWRITE,           /* EP_PERM_WRITE */
++    ELAN3_PERM_REMOTEWRITE,           /* EP_PERM_ALL */
++};
++
++static unsigned int sdram_permtable[] = 
++{
++    ELAN3_PERM_REMOTEREAD,            /* EP_PERM_EXECUTE */
++    ELAN3_PERM_REMOTEREAD,            /* EP_PERM_READ */
++    ELAN3_PERM_REMOTEWRITE,           /* EP_PERM_WRITE */
++    ELAN3_PERM_REMOTEALL,             /* EP_PERM_ALL */
++};
++
++static unsigned int io_permtable[] = 
++{
++    ELAN3_PERM_LOCAL_READ,            /* EP_PERM_EXECUTE */
++    ELAN3_PERM_REMOTEREAD,            /* EP_PERM_READ */
++    ELAN3_PERM_REMOTEWRITE,           /* EP_PERM_WRITE */
++    ELAN3_PERM_REMOTEWRITE,           /* EP_PERM_ALL */
++};
++
++void
++ep3_kaddr_map (EP_RAIL *r, EP_ADDR eaddr, virtaddr_t kaddr, unsigned len, unsigned int perm, int ep_attr)
++{
++    EP3_RAIL    *rail   = (EP3_RAIL *) r;
++    unsigned     npages = len >> PAGESHIFT;
++    int          i;
++    unsigned int off;
++
++    ASSERT ((eaddr & PAGEOFFSET) == 0 && (kaddr & PAGEOFFSET) == 0 && (len & PAGEOFFSET) == 0);
++
++    for (i = 0; i < npages; i++)
++    {
++      physaddr_t paddr = vaddr_to_phys (kernel_map, (void *) kaddr);
++
++      for (off = 0; off < PAGESIZE; off += ELAN3_PAGE_SIZE)
++          elan3mmu_pteload (rail->Elan3mmu, PTBL_LEVEL_3, eaddr + off, paddr + off, 
++                            main_permtable[perm], PTE_LOAD_LOCK | PTE_LOAD_NOSYNC | ((ep_attr & EP_NO_SLEEP) ? PTE_NO_SLEEP : 0));
++
++      eaddr += PAGESIZE;
++      kaddr += PAGESIZE;
++    }
++}
++
++void
++ep3_sdram_map (EP_RAIL *r, EP_ADDR eaddr, sdramaddr_t saddr, unsigned len, unsigned int perm, int ep_attr)
++{
++    EP3_RAIL    *rail   = (EP3_RAIL *) r;
++    unsigned     npages = len >> PAGESHIFT;
++    int          i;
++    unsigned int off;
++
++    ASSERT ((eaddr & PAGEOFFSET) == 0 && (saddr & PAGEOFFSET) == 0 && (len & PAGEOFFSET) == 0);
++
++    for (i = 0; i < npages; i++)
++    {
++      physaddr_t paddr = elan3_sdram_to_phys (rail->Device, saddr);
++
++      for (off = 0; off < PAGESIZE; off += ELAN3_PAGE_SIZE)
++          elan3mmu_pteload (rail->Elan3mmu, PTBL_LEVEL_3, eaddr+off, paddr+off, 
++                            sdram_permtable[perm], PTE_LOAD_LOCK | PTE_LOAD_NOSYNC | ((ep_attr & EP_NO_SLEEP) ? PTE_NO_SLEEP : 0) );
++
++      eaddr += PAGESIZE;
++      saddr += PAGESIZE;
++    }
++}
++
++void
++ep3_ioaddr_map (EP_RAIL *r, EP_ADDR eaddr, ioaddr_t ioaddr, unsigned len, unsigned int perm)
++{
++    EP3_RAIL    *rail   = (EP3_RAIL *) r;
++    unsigned     npages = len >> PAGESHIFT;
++    int          i;
++    unsigned int off;
++
++    ASSERT ((eaddr & PAGEOFFSET) == 0 && (ioaddr & PAGEOFFSET) == 0 && (len & PAGEOFFSET) == 0);
++
++    for (i = 0; i < npages; i++)
++    {
++      physaddr_t paddr = vaddr_to_phys (kernel_map, (void *) ioaddr);
++
++      for (off = 0; off < PAGESIZE; off += ELAN3_PAGE_SIZE)
++          elan3mmu_pteload (rail->Elan3mmu, PTBL_LEVEL_3, eaddr + off, paddr + off, 
++                            io_permtable[perm], PTE_LOAD_LOCK | PTE_LOAD_NOSYNC);
++
++      eaddr += PAGESIZE;
++      ioaddr += PAGESIZE;
++    }
++}
++void
++ep3_unmap (EP_RAIL *r, EP_ADDR eaddr, unsigned len)
++{
++    EP3_RAIL *rail = (EP3_RAIL *) r;
++
++    ASSERT ((eaddr & PAGEOFFSET) == 0 && (len & PAGEOFFSET) == 0);
++
++    elan3mmu_unload (rail->Elan3mmu, eaddr, len, PTE_UNLOAD_UNLOCK | PTE_UNLOAD_NOSYNC);
++}
++
++void *
++ep3_dvma_reserve (EP_RAIL *r, EP_ADDR eaddr, unsigned npages)
++{
++    EP3_RAIL *rail = (EP3_RAIL *) r;
++    void     *private;
++
++    KMEM_ALLOC (private, void *, npages * ELAN3_PTES_PER_PAGE * sizeof (sdramaddr_t), 1);
++    
++    if (private == NULL)
++      return NULL;
++    
++    elan3mmu_reserve (rail->Elan3mmu, eaddr, npages * ELAN3_PTES_PER_PAGE, (sdramaddr_t *) private);
++
++    return private;
++}
++
++void
++ep3_dvma_release (EP_RAIL *r, EP_ADDR eaddr, unsigned npages, void *private)
++{
++    EP3_RAIL *rail = (EP3_RAIL *) r;
++
++    elan3mmu_release (rail->Elan3mmu, eaddr, npages * ELAN3_PTES_PER_PAGE, (sdramaddr_t *) private);
++
++    KMEM_FREE (private, npages * ELAN3_PTES_PER_PAGE * sizeof (sdramaddr_t));
++}
++
++void
++ep3_dvma_set_pte (EP_RAIL *r, void *private, unsigned index, physaddr_t paddr, unsigned int perm)
++{
++    ELAN3_DEV    *dev  = ((EP3_RAIL *) r)->Device;
++    sdramaddr_t *ptep = &((sdramaddr_t *) private)[index * ELAN3_PTES_PER_PAGE];
++    int          off;
++
++    for (off =0 ; off < PAGESIZE; off += ELAN3_PAGE_SIZE)
++    { 
++      ELAN3_PTE newpte = elan3mmu_phys_to_pte (dev, paddr + off, main_permtable[perm]) | ELAN3_PTE_REF | ELAN3_PTE_MOD;
++
++      elan3_writepte (dev, *ptep, newpte);
++
++      ptep++;
++    }
++}
++
++physaddr_t
++ep3_dvma_read_pte (EP_RAIL *r, void *private, unsigned index)
++{
++    EP3_RAIL    *rail = (EP3_RAIL *) r;
++    sdramaddr_t *ptep = &((sdramaddr_t *) private)[index * ELAN3_PTES_PER_PAGE];
++    ELAN3_PTE     pte  = elan3_readpte (rail->Device, *ptep);
++
++    return pte & ELAN3_PTE_PFN_MASK;
++}
++
++void
++ep3_dvma_unload (EP_RAIL *r, void *private, unsigned index, unsigned npages)
++{
++    EP3_RAIL    *rail = (EP3_RAIL *) r;
++    sdramaddr_t *ptep = &((sdramaddr_t *) private)[index * ELAN3_PTES_PER_PAGE];
++    ELAN3_PTE     tpte = elan3mmu_kernel_invalid_pte (rail->Elan3mmu);
++    int i;
++
++    for (i = (npages * ELAN3_PTES_PER_PAGE) - 1; i >= 0; i--)
++      elan3_writepte (rail->Device, ptep[i], tpte);
++}
+Index: linux-2.4.21/drivers/net/qsnet/ep/kmap_elan4.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/kmap_elan4.c        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/kmap_elan4.c     2005-06-01 23:12:54.670428312 -0400
+@@ -0,0 +1,226 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: kmap_elan4.c,v 1.7.8.2 2004/12/14 10:19:14 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/kmap_elan4.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++
++#include "debug.h"
++#include "kcomm_elan4.h"
++
++#if defined(DIGITAL_UNIX)
++#  define kernel_map                  (first_task->map)
++#  define vaddr_to_phys(map, addr)    (pmap_extract (vm_map_pmap ((vm_map_t) map), (unsigned long) addr))
++#elif defined(LINUX)
++#  define kernel_map                  get_kern_mm()
++#  define vaddr_to_phys(map, addr)    (kmem_to_phys(addr))
++#elif defined(SOLARIS)
++#  define kernel_map                  &kas
++#  define vaddr_to_phys(map,addr)     ptob(hat_getpfnum (((struct as *) map)->a_hat, (caddr_t) addr))
++#endif
++
++static unsigned int main_permtable[] = 
++{
++    PERM_Unused,                      /* EP_PERM_EXECUTE */
++    PERM_RemoteReadOnly,              /* EP_PERM_READ */
++    PERM_DataReadWrite,                       /* EP_PERM_WRITE */
++    PERM_DataReadWrite,                       /* EP_PERM_ALL */
++};
++
++static unsigned int sdram_permtable[] = 
++{
++    PERM_LocExecute,                  /* EP_PERM_EXECUTE */
++    PERM_RemoteReadOnly,              /* EP_PERM_READ */
++    PERM_DataReadWrite,                       /* EP_PERM_WRITE */
++    PERM_RemoteAll,                   /* EP_PERM_ALL */
++};
++
++static unsigned int io_permtable[] = 
++{
++    PERM_Unused,                      /* EP_PERM_EXECUTE */
++    PERM_RemoteReadOnly,              /* EP_PERM_READ */
++    PERM_DataReadWrite,                       /* EP_PERM_WRITE */
++    PERM_Unused,                      /* EP_PERM_ALL */
++};
++
++void
++ep4_kaddr_map (EP_RAIL *r, EP_ADDR eaddr, virtaddr_t kaddr, unsigned int len, unsigned int perm, int ep_attr)
++{
++    EP4_RAIL    *rail   = (EP4_RAIL *) r;
++    ELAN4_DEV   *dev    = rail->r_ctxt.ctxt_dev;
++    unsigned int npages = len >> PAGESHIFT;
++    int          i;
++    unsigned int off;
++
++    ASSERT ((eaddr & PAGEOFFSET) == 0 && (kaddr & PAGEOFFSET) == 0 && (len & PAGEOFFSET) == 0);
++
++    for (i = 0; i < npages; i++)
++    {
++      physaddr_t paddr = vaddr_to_phys (kernel_map, (void *) kaddr);
++
++      for (off = 0; off < PAGESIZE; off += (1 << dev->dev_pageshift[0]))
++      {
++          E4_uint64 newpte = elan4mmu_phys2pte (dev, paddr + off, main_permtable[perm]);
++
++          elan4mmu_pteload (&rail->r_ctxt, 0, eaddr + off, newpte);
++      }
++
++      eaddr += PAGESIZE;
++      kaddr += PAGESIZE;
++    }
++}
++
++void
++ep4_sdram_map (EP_RAIL *r, EP_ADDR eaddr, sdramaddr_t saddr, unsigned int len, unsigned int perm, int ep_attr)
++{
++    EP4_RAIL    *rail   = (EP4_RAIL *) r;
++    ELAN4_DEV   *dev    = rail->r_ctxt.ctxt_dev;
++    unsigned int npages = len >> PAGESHIFT;
++    int          i;
++    unsigned int off;
++
++    ASSERT ((eaddr & PAGEOFFSET) == 0 && (saddr & PAGEOFFSET) == 0 && (len & PAGEOFFSET) == 0);
++
++    if ((eaddr & (SDRAM_PGOFF_OFFSET << PAGE_SHIFT)) != (saddr & (SDRAM_PGOFF_OFFSET << PAGE_SHIFT)))
++      printk ("ep4_sdram_map: eaddr=%x saddr=%lx - incorrectly alised\n", eaddr, saddr);
++
++    for (i = 0; i < npages; i++)
++    {
++      for (off = 0; off < PAGESIZE; off += (1 << dev->dev_pageshift[0]))
++      {
++          E4_uint64 newpte = ((saddr + off) >> PTE_PADDR_SHIFT) | PTE_SetPerm (sdram_permtable[perm]);
++
++          elan4mmu_pteload (&rail->r_ctxt, 0, eaddr + off, newpte);
++      }
++
++      eaddr += PAGESIZE;
++      saddr += PAGESIZE;
++    }
++}
++
++void
++ep4_ioaddr_map (EP_RAIL *r, EP_ADDR eaddr, ioaddr_t ioaddr, unsigned int len, unsigned int perm)
++{
++    EP4_RAIL    *rail   = (EP4_RAIL *) r;
++    ELAN4_DEV   *dev    = rail->r_ctxt.ctxt_dev;
++    unsigned int npages = len >> PAGESHIFT;
++    int          i;
++    unsigned int off;
++
++    ASSERT ((eaddr & PAGEOFFSET) == 0 && (ioaddr & PAGEOFFSET) == 0 && (len & PAGEOFFSET) == 0);
++
++    for (i = 0; i < npages; i++)
++    {
++      physaddr_t paddr = vaddr_to_phys (kernel_map, (void *) ioaddr);
++
++      for (off = 0; off < PAGESIZE; off += (1 << dev->dev_pageshift[0]))
++      {
++          E4_uint64 newpte = elan4mmu_phys2pte (dev, paddr + off, io_permtable[perm]);
++
++          elan4mmu_pteload (&rail->r_ctxt, 0, eaddr + off, newpte);
++      }
++
++      eaddr += PAGESIZE;
++      ioaddr += PAGESIZE;
++    }
++}
++void
++ep4_unmap (EP_RAIL *r, EP_ADDR eaddr, unsigned int len)
++{
++    EP4_RAIL *rail = (EP4_RAIL *) r;
++
++    ASSERT ((eaddr & PAGEOFFSET) == 0 && (len & PAGEOFFSET) == 0);
++
++    elan4mmu_unload_range (&rail->r_ctxt, 0, eaddr, len);
++}
++
++void *
++ep4_dvma_reserve (EP_RAIL *r, EP_ADDR eaddr, unsigned int npages)
++{
++    EP4_RAIL  *rail = (EP4_RAIL *) r;
++    ELAN4_DEV *dev  = rail->r_ctxt.ctxt_dev;
++
++    EPRINTF3 (DBG_KMAP, "ep4_dvma_reserve: eaddr=%x npages=%d (=> %d)\n", eaddr, npages, (npages << (PAGE_SHIFT - dev->dev_pageshift[0])));
++
++    return elan4mmu_reserve (&rail->r_ctxt, 0, (E4_Addr) eaddr, (npages << (PAGE_SHIFT - dev->dev_pageshift[0])), 0);
++}
++
++void
++ep4_dvma_release (EP_RAIL *r, EP_ADDR eaddr, unsigned int npages, void *private)
++{
++    EP4_RAIL *rail = (EP4_RAIL *) r;
++
++    EPRINTF3 (DBG_KMAP, "ep4_dvma_release: eaddr=%x npages=%d private=%p\n", eaddr, npages, private);
++
++    elan4mmu_release (&rail->r_ctxt, (ELAN4_HASH_CACHE *) private);
++}
++
++void
++ep4_dvma_set_pte (EP_RAIL *r, void *private, unsigned int index, physaddr_t paddr, unsigned int perm)
++{
++    EP4_RAIL     *rail  = (EP4_RAIL *) r;
++    ELAN4_DEV    *dev   = rail->r_ctxt.ctxt_dev;
++    unsigned int  off;
++    unsigned long flags;
++
++    EPRINTF3 (DBG_KMAP, "ep4_dvma_set_pte: index %x -> eaddr %llx paddr %llx\n", 
++            index, ((ELAN4_HASH_CACHE *) private)->hc_start + (index * PAGE_SIZE), (long long) paddr);
++
++    local_irq_save (flags);
++    for (off = 0; off < PAGESIZE; off += (1 << dev->dev_pageshift[0]))
++    {
++      E4_uint64 newpte = elan4mmu_phys2pte (dev, paddr + off, main_permtable[perm]);
++
++      elan4mmu_set_pte (&rail->r_ctxt, (ELAN4_HASH_CACHE *) private, (index << (PAGE_SHIFT - dev->dev_pageshift[0])) +
++                        (off >> dev->dev_pageshift[0]), newpte);
++    }
++    local_irq_restore (flags);
++}
++
++physaddr_t
++ep4_dvma_read_pte (EP_RAIL *r, void *private, unsigned int index)
++{
++    EP4_RAIL     *rail  = (EP4_RAIL *) r;
++    ELAN4_DEV    *dev   = rail->r_ctxt.ctxt_dev;
++    E4_uint64     pte;
++    unsigned long flags;
++
++    local_irq_save (flags);
++    pte = elan4mmu_get_pte (&rail->r_ctxt, (ELAN4_HASH_CACHE *) private, index << (PAGE_SHIFT - dev->dev_pageshift[0]));
++    local_irq_restore (flags);
++
++    return elan4mmu_pte2phys (dev, pte);
++}
++
++void
++ep4_dvma_unload (EP_RAIL *r, void *private, unsigned int index, unsigned int npages)
++{
++    EP4_RAIL  *rail  = (EP4_RAIL *) r;
++    ELAN4_DEV *dev   = rail->r_ctxt.ctxt_dev;
++    EP_ADDR    eaddr = ((ELAN4_HASH_CACHE *) private)->hc_start + (index * PAGE_SIZE);
++    unsigned long idx = (index << (PAGE_SHIFT - dev->dev_pageshift[0]));
++    unsigned long lim = idx + (npages << (PAGE_SHIFT - dev->dev_pageshift[0]));
++    unsigned long flags;
++
++    EPRINTF5 (DBG_KMAP, "ep4_dvma_unload: eaddr %x -> %lx : index=%d idx=%ld lim=%ld\n", 
++            eaddr, (unsigned long)(eaddr + (npages * PAGE_SIZE)), index, idx, lim);
++
++    local_irq_save (flags);
++    for (; idx < lim; idx++)
++      elan4mmu_clear_pte (&rail->r_ctxt, (ELAN4_HASH_CACHE *) private, idx);
++    local_irq_restore (flags);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/ep/kmsg_elan3.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/kmsg_elan3.c        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/kmsg_elan3.c     2005-06-01 23:12:54.671428160 -0400
+@@ -0,0 +1,345 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: kmsg_elan3.c,v 1.3.8.1 2004/09/30 09:52:37 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/kmsg_elan3.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++
++#include "kcomm_vp.h"
++#include "kcomm_elan3.h"
++#include "debug.h"
++
++static void
++ep3_inputq_event (EP3_RAIL *rail, void *arg)
++{
++    EP3_INPUTQ *inputq = (EP3_INPUTQ *) arg;
++    
++    (*inputq->q_callback)((EP_RAIL *)rail, inputq->q_arg);
++}
++
++static EP3_COOKIE_OPS ep3_inputq_cookie_ops = 
++{
++    ep3_inputq_event,
++};
++
++EP_INPUTQ *
++ep3_alloc_inputq (EP_RAIL *r, unsigned qnum, unsigned slotSize, unsigned slotCount,
++                EP_INPUTQ_CALLBACK *callback, void *arg)
++{
++    EP3_RAIL      *rail = (EP3_RAIL *) r;
++    EP3_INPUTQ    *inputq;
++    EP3_InputQueue qdesc;
++    void          *slots;
++    int            i;
++
++    ASSERT ((slotSize & (EP_SYSTEMQ_MSG_ALIGN-1)) == 0);
++
++    KMEM_ALLOC (inputq, EP3_INPUTQ *, sizeof (EP3_INPUTQ), TRUE);
++
++    if (inputq == NULL)
++      return (EP_INPUTQ *) NULL;
++    
++    if ((slots = ep_alloc_main (&rail->Generic, slotSize * slotCount, 0, &inputq->q_slotsAddr)) == NULL)
++    {
++      KMEM_FREE (inputq, sizeof (EP3_INPUTQ));
++      return (EP_INPUTQ *) NULL;
++    }
++
++    inputq->q_slotSize  = slotSize;
++    inputq->q_slotCount = slotCount;
++    inputq->q_callback  = callback;
++    inputq->q_arg       = arg;
++    inputq->q_slots     = slots;
++
++    /* Initialise all the slots to be "unreceived" */
++    for (i = 0; i < slotCount; i++)
++      ((uint32_t *) ((unsigned long) slots + (i+1) * slotSize))[-1] = EP_SYSTEMQ_UNRECEIVED;
++    
++    inputq->q_base     = inputq->q_slotsAddr;
++    inputq->q_top      = inputq->q_base + (slotCount-1) * slotSize;
++    inputq->q_fptr     = inputq->q_base;
++    inputq->q_desc     = EP_SYSTEMQ_DESC(rail->QueueDescs, qnum);
++    inputq->q_descAddr = EP_SYSTEMQ_ADDR (qnum);
++
++    if (callback)
++      RegisterCookie (&rail->CookieTable, &inputq->q_cookie, inputq->q_descAddr, &ep3_inputq_cookie_ops, inputq);
++
++    /* Initialise the input queue descriptor */
++    qdesc.q_state          = E3_QUEUE_FULL;
++    qdesc.q_bptr           = inputq->q_base + slotSize;
++    qdesc.q_fptr           = inputq->q_fptr;
++    qdesc.q_base           = inputq->q_base;
++    qdesc.q_top            = inputq->q_top;
++    qdesc.q_size           = slotSize;
++    qdesc.q_event.ev_Count = 1;
++    qdesc.q_event.ev_Type  = callback ? EV_TYPE_EVIRQ | inputq->q_cookie.Cookie : 0;
++    qdesc.q_wevent         = inputq->q_descAddr + offsetof (EP3_InputQueue, q_event);
++    qdesc.q_wcount         = 0;
++
++    /* copy the queue descriptor down to sdram */
++    elan3_sdram_copyl_to_sdram (rail->Device, &qdesc, inputq->q_desc, sizeof (EP3_InputQueue));
++
++    return (EP_INPUTQ *) inputq;
++}
++
++void
++ep3_free_inputq (EP_RAIL *r, EP_INPUTQ *q)
++{
++    EP3_RAIL   *rail   = (EP3_RAIL *) r;
++    EP3_INPUTQ *inputq = (EP3_INPUTQ *) q;
++
++    ep_free_main (&rail->Generic, inputq->q_slotsAddr, inputq->q_slotSize * inputq->q_slotCount);
++
++    if (inputq->q_callback)
++      DeregisterCookie (&rail->CookieTable, &inputq->q_cookie);
++
++    KMEM_FREE (inputq, sizeof (EP3_INPUTQ));
++}
++
++void
++ep3_enable_inputq (EP_RAIL *r, EP_INPUTQ *q)
++{
++    EP3_RAIL   *rail   = (EP3_RAIL *) r;
++    EP3_INPUTQ *inputq = (EP3_INPUTQ *) q;
++
++    elan3_sdram_writel (rail->Device, inputq->q_desc + offsetof (EP3_InputQueue, q_state), 0);
++}
++
++void
++ep3_disable_inputq (EP_RAIL *r, EP_INPUTQ *q)
++{
++    EP3_RAIL      *rail   = (EP3_RAIL *) r;
++    EP3_INPUTQ    *inputq = (EP3_INPUTQ *) q;
++    EP3_InputQueue qdesc;
++
++    /* mark the queue as locked */
++    SetQueueLocked (rail, inputq->q_desc);
++
++    /* re-initialise the queue as empty */
++    qdesc.q_state          = E3_QUEUE_FULL;
++    qdesc.q_bptr           = (E3_Addr) inputq->q_base + inputq->q_slotSize;
++    qdesc.q_fptr           = inputq->q_fptr;
++    qdesc.q_base           = inputq->q_base;
++    qdesc.q_top            = inputq->q_top;
++    qdesc.q_size           = inputq->q_slotSize;
++    qdesc.q_event.ev_Count = 1;
++    qdesc.q_event.ev_Type  = inputq->q_callback ? EV_TYPE_EVIRQ | inputq->q_cookie.Cookie : 0;
++    qdesc.q_wevent         = inputq->q_descAddr + offsetof (EP3_InputQueue, q_event);
++    qdesc.q_wcount         = 0;
++
++    /* copy the queue descriptor down to sdram */
++    elan3_sdram_copyl_to_sdram (rail->Device, &qdesc, inputq->q_desc, sizeof (EP3_InputQueue));
++}
++
++int
++ep3_poll_inputq (EP_RAIL *r, EP_INPUTQ *q, int maxCount, EP_INPUTQ_HANDLER *handler, void *arg)
++{
++    EP3_RAIL          *rail   = (EP3_RAIL *) r;
++    EP3_INPUTQ        *inputq = (EP3_INPUTQ *) q;
++    sdramaddr_t        qdesc  = inputq->q_desc;
++    E3_Addr            nfptr;
++    int                count = 0;
++    E3_uint32          state;
++    int                      delay;
++
++ run_again_because_of_eventqueue_overflow:
++    nfptr = inputq->q_fptr + inputq->q_slotSize;
++    if (nfptr > inputq->q_top)                                        
++      nfptr = inputq->q_base;
++
++    while (nfptr != elan3_sdram_readl (rail->Device, qdesc + offsetof (EP3_InputQueue, q_bptr)))                      /* PCI read */
++    {
++      unsigned long slot = (unsigned long) inputq->q_slots + (nfptr - inputq->q_base);
++
++      /* Poll the final word of the message until the message has completely
++       * arrived in main memory. */
++      for (delay = 1; ((uint32_t *) (slot + inputq->q_slotSize))[-1] == EP_SYSTEMQ_UNRECEIVED && delay < EP_SYSTEMQ_UNRECEIVED_TLIMIT; delay <<= 1)
++          DELAY (delay);
++
++      /* Call the message handler */
++      (*handler) (r, arg, (void *) slot);
++      
++      state = elan3_sdram_readl (rail->Device, qdesc + offsetof (EP3_InputQueue, q_state));                           /* PCI read */
++      if ((state & E3_QUEUE_FULL) == 0)
++          elan3_sdram_writel (rail->Device, qdesc + offsetof (EP3_InputQueue, q_fptr), nfptr);                        /* PCI write */
++      else
++      {
++          elan3_sdram_writel (rail->Device, qdesc + offsetof (EP3_InputQueue, q_fptr), nfptr);                        /* PCI write */
++          elan3_sdram_writel (rail->Device, qdesc + offsetof (EP3_InputQueue, q_state), (state & ~E3_QUEUE_FULL));    /* PCI write */
++      }
++      inputq->q_fptr = nfptr;
++      
++      nfptr += roundup (inputq->q_slotSize, E3_BLK_ALIGN);
++      if (nfptr > inputq->q_top)
++          nfptr = inputq->q_base;
++
++      if (++count >= maxCount && maxCount)
++          break;
++    }
++    
++    if (inputq->q_callback && count != 0)
++    {
++      if (count != inputq->q_waitCount)
++          elan3_sdram_writel (rail->Device, qdesc + offsetof (EP3_InputQueue, q_wcount), inputq->q_waitCount = count);
++
++      if (IssueWaitevent (rail, inputq->q_descAddr + offsetof (EP3_InputQueue, q_wevent)) == ISSUE_COMMAND_TRAPPED)
++          goto run_again_because_of_eventqueue_overflow;
++    }
++
++    return count;
++}
++
++#define Q_EVENT(q,slotNum)            ((q)->q_elan      + (slotNum) * sizeof (E3_BlockCopyEvent))
++#define Q_EVENT_ADDR(q,slotNum)               ((q)->q_elanAddr  + (slotNum) * sizeof (E3_BlockCopyEvent))
++#define Q_MSG(q,slotNum)      (void *)((q)->q_main      + (slotNum) * (q)->q_slotSize)
++#define Q_MSG_ADDR(q,slotNum)         ((q)->q_mainAddr  + (slotNum) * (q)->q_slotSize)
++#define Q_DONE(q,slotNum)     (*((int *)((q)->q_main      + (q)->q_slotCount * (q)->q_slotSize + (slotNum) * sizeof (E3_uint32))))
++#define Q_DONE_ADDR(q,slotNum)                ((q)->q_mainAddr  + (q)->q_slotCount * (q)->q_slotSize + (slotNum) * sizeof (E3_uint32))
++
++#define Q_ELAN_SIZE(q)                        ((q)->q_slotCount * sizeof (E3_BlockCopyEvent))
++#define Q_MAIN_SIZE(q)                        ((q)->q_slotCount * ((q)->q_slotSize + sizeof (E3_uint32)))
++
++static void
++ep3_outputq_retry (EP3_RAIL *rail, void *arg, E3_DMA_BE *dma, int error)
++{
++    E3_DMA_BE    *dmabe = (E3_DMA_BE *) dma;
++    sdramaddr_t   event = ep_elan2sdram (&rail->Generic, dmabe->s.dma_srcEvent);
++    E3_Addr       done  = elan3_sdram_readl (rail->Device, event + offsetof (E3_BlockCopyEvent, ev_Dest));
++    E3_uint32    *donep = ep_elan2main (&rail->Generic, done & ~EV_BCOPY_DTYPE_MASK);
++
++    EPRINTF1 (DBG_KMSG, "ep3_ouputq_retry: donep at %p -> FAILED\n", donep);
++    
++    *donep = EP3_EVENT_FAILED;
++}
++
++static EP3_COOKIE_OPS ep3_outputq_cookie_ops =
++{
++    NULL, /* Event */
++    ep3_outputq_retry,
++    NULL, /* DmaCancelled */
++    NULL, /* DmaVerify */
++};
++
++EP_OUTPUTQ *
++ep3_alloc_outputq (EP_RAIL *r, unsigned slotSize, unsigned slotCount)
++{
++    EP3_RAIL         *rail = (EP3_RAIL *) r;
++    EP3_OUTPUTQ      *outputq;
++    int               i;
++    E3_BlockCopyEvent event;
++
++    ASSERT ((slotSize & (EP_SYSTEMQ_MSG_ALIGN-1)) == 0);
++
++    KMEM_ALLOC (outputq, EP3_OUTPUTQ *, sizeof (EP3_OUTPUTQ), 1);
++
++    if (outputq == NULL)
++      return NULL;
++
++    outputq->q_slotCount = slotCount;
++    outputq->q_slotSize  = slotSize;
++
++    outputq->q_elan = ep_alloc_elan (r, Q_ELAN_SIZE(outputq), 0, &outputq->q_elanAddr);
++
++    if (outputq->q_elan == (sdramaddr_t) 0)
++    {
++      KMEM_FREE (outputq, sizeof (EP3_OUTPUTQ));
++      return NULL;
++    }
++
++    outputq->q_main = ep_alloc_main (r, Q_MAIN_SIZE(outputq), 0, &outputq->q_mainAddr);
++
++    if (outputq->q_main == (void *) NULL)
++    {
++      ep_free_elan (r, outputq->q_elanAddr, Q_ELAN_SIZE(outputq));
++      KMEM_FREE (outputq, sizeof (EP3_OUTPUTQ));
++      return NULL;
++    }
++
++    RegisterCookie (&rail->CookieTable, &outputq->q_cookie, outputq->q_elanAddr, &ep3_outputq_cookie_ops, outputq);
++
++    for (i = 0; i < slotCount; i++)
++    {
++      EP3_INIT_COPY_EVENT (event, outputq->q_cookie, Q_DONE_ADDR(outputq, i), 0);
++
++      Q_DONE(outputq, i) = outputq->q_cookie.Cookie;
++      
++      elan3_sdram_copyl_to_sdram (rail->Device, &event, Q_EVENT(outputq, i), sizeof (E3_BlockCopyEvent));
++    }
++
++    return (EP_OUTPUTQ *) outputq;
++}
++
++void
++ep3_free_outputq (EP_RAIL *r, EP_OUTPUTQ *q)
++{
++    EP3_RAIL    *rail    = (EP3_RAIL *) r;
++    EP3_OUTPUTQ *outputq = (EP3_OUTPUTQ *) q;
++
++    DeregisterCookie (&rail->CookieTable, &outputq->q_cookie);
++    
++    ep_free_main (r, outputq->q_mainAddr, Q_MAIN_SIZE(outputq));
++    ep_free_elan (r, outputq->q_elanAddr, Q_ELAN_SIZE(outputq));
++ 
++    KMEM_FREE (outputq, sizeof (EP3_OUTPUTQ));
++}
++
++void *
++ep3_outputq_msg (EP_RAIL *r, EP_OUTPUTQ *q, unsigned slotNum)
++{
++    return Q_MSG ((EP3_OUTPUTQ *) q, slotNum);
++}
++
++int
++ep3_outputq_state (EP_RAIL *r, EP_OUTPUTQ *q, unsigned slotNum)
++{
++    switch (Q_DONE((EP3_OUTPUTQ *) q, slotNum))
++    {
++    case EP3_EVENT_ACTIVE:
++      return EP_OUTPUTQ_BUSY;
++      
++    case EP3_EVENT_FAILED:
++      return EP_OUTPUTQ_FAILED;
++      
++    default:
++      return EP_OUTPUTQ_FINISHED;
++    }
++}
++
++int
++ep3_outputq_send (EP_RAIL *r, EP_OUTPUTQ *q, unsigned slotNum, unsigned size, 
++                unsigned vp, unsigned qnum, unsigned retries)
++{
++    EP3_RAIL    *rail    = (EP3_RAIL *) r;
++    EP3_OUTPUTQ *outputq = (EP3_OUTPUTQ *) q;
++    unsigned     base    = outputq->q_slotSize - roundup (size, E3_BLK_ALIGN);
++    E3_DMA_BE    dmabe;
++
++    dmabe.s.dma_type          = E3_DMA_TYPE(DMA_BYTE, DMA_WRITE, DMA_QUEUED, retries);
++    dmabe.s.dma_size            = roundup (size, E3_BLK_ALIGN);
++    dmabe.s.dma_source          = Q_MSG_ADDR(outputq, slotNum) + base;
++    dmabe.s.dma_dest            = base;
++    dmabe.s.dma_destEvent       = EP_SYSTEMQ_ADDR(qnum);
++    dmabe.s.dma_destCookieVProc = vp;
++    dmabe.s.dma_srcEvent        = Q_EVENT_ADDR(outputq, slotNum);
++    dmabe.s.dma_srcCookieVProc  = 0;
++
++    Q_DONE(outputq, slotNum) = EP3_EVENT_ACTIVE;
++    
++    elan3_sdram_writel (rail->Device, Q_EVENT(outputq, slotNum), 1);
++
++    if (IssueDma (rail, &dmabe, EP_RETRY_CRITICAL, FALSE) != ISSUE_COMMAND_OK)
++    {
++      Q_DONE(outputq, slotNum) = EP3_EVENT_FAILED;
++      return FALSE;
++    }
++
++    return TRUE;
++}
+Index: linux-2.4.21/drivers/net/qsnet/ep/kmsg_elan4.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/kmsg_elan4.c        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/kmsg_elan4.c     2005-06-01 23:12:54.672428008 -0400
+@@ -0,0 +1,416 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: kmsg_elan4.c,v 1.8.6.1 2004/09/30 09:52:37 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/kmsg_elan4.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++
++#include "debug.h"
++#include "kcomm_vp.h"
++#include "kcomm_elan4.h"
++
++#include <elan4/trtype.h>
++
++static void
++ep4_inputq_interrupt (EP4_RAIL *rail, void *arg)
++{
++    EP4_INPUTQ *inputq = (EP4_INPUTQ *) arg;
++
++    /* mark the queue as "fired" to cause a single waitevent
++     * to be issued next time the queue is polled */
++    atomic_inc (&inputq->q_fired);
++    
++    (*inputq->q_callback)(&rail->r_generic, inputq->q_arg);
++}
++
++EP_INPUTQ *
++ep4_alloc_inputq (EP_RAIL *r, unsigned qnum, unsigned slotSize, unsigned slotCount,
++                EP_INPUTQ_CALLBACK *callback, void *arg)
++{
++    EP4_RAIL     *rail = (EP4_RAIL *) r;
++    EP4_INPUTQ   *inputq;
++    E4_Event32    qevent;
++    void         *slots;
++    int           i;
++
++    ASSERT ((slotSize & (EP_SYSTEMQ_MSG_ALIGN-1)) == 0);
++
++    KMEM_ALLOC (inputq, EP4_INPUTQ *, sizeof (EP4_INPUTQ), 1);
++
++    if (inputq == NULL)
++      return (EP_INPUTQ *) NULL;
++    
++    if ((slots = ep_alloc_main (&rail->r_generic, slotSize * slotCount, 0, &inputq->q_slotsAddr)) == NULL)
++    {
++      KMEM_FREE (inputq, sizeof (EP4_INPUTQ));
++      return (EP_INPUTQ *) NULL;
++    }
++
++    inputq->q_slotSize  = slotSize;
++    inputq->q_slotCount = slotCount;
++    inputq->q_callback  = callback;
++    inputq->q_arg       = arg;
++    inputq->q_slots     = slots;
++
++    /* Initialise all the slots to be "unreceived" */
++    for (i = 0; i < slotCount; i++)
++      ((uint32_t *) ((unsigned long) slots + (i+1) * slotSize))[-1] = EP_SYSTEMQ_UNRECEIVED;
++    
++    inputq->q_last      = inputq->q_slotsAddr + (slotCount-1) * slotSize;
++    inputq->q_fptr      = inputq->q_slotsAddr;
++    inputq->q_desc      = EP_SYSTEMQ_DESC (rail->r_queuedescs, qnum);
++    inputq->q_descAddr  = EP_SYSTEMQ_ADDR (qnum);
++    inputq->q_eventAddr = rail->r_elan_addr + offsetof (EP4_RAIL_ELAN, r_qevents[qnum]);
++
++    if (callback)
++    {
++      if ((inputq->q_ecq = ep4_get_ecq (rail, EP4_ECQ_EVENT, 1)) == 0)
++      {
++          ep_free_main (&rail->r_generic, inputq->q_slotsAddr, inputq->q_slotSize * inputq->q_slotCount);
++
++          KMEM_FREE (inputq, sizeof (EP4_INPUTQ));
++          return (EP_INPUTQ *) NULL;
++      }
++
++      if ((inputq->q_wcq = ep4_get_ecq (rail, EP4_ECQ_MAIN, 4)) == 0)
++      {
++          ep4_put_ecq (rail, inputq->q_ecq, 1);
++          ep_free_main (&rail->r_generic, inputq->q_slotsAddr, inputq->q_slotSize * inputq->q_slotCount);
++
++          KMEM_FREE (inputq, sizeof (EP4_INPUTQ));
++          return (EP_INPUTQ *) NULL;
++      }
++
++      ep4_register_intcookie (rail, &inputq->q_intcookie, inputq->q_descAddr, ep4_inputq_interrupt, inputq);
++
++      inputq->q_count = 0;
++
++      atomic_set (&inputq->q_fired, 0);
++
++      /* Initialise the queue event */
++      qevent.ev_CountAndType = E4_EVENT_INIT_VALUE (callback ? -32 : 0, E4_EVENT_WRITE, E4_EVENT_DTYPE_LONG, 0);
++      qevent.ev_WritePtr     = inputq->q_ecq->ecq_addr;
++      qevent.ev_WriteValue   = (inputq->q_intcookie.int_val << E4_MAIN_INT_SHIFT) | INTERRUPT_CMD;
++    }
++
++    /* copy the event down to sdram */
++    elan4_sdram_copyq_to_sdram (rail->r_ctxt.ctxt_dev, &qevent, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_qevents[qnum]), sizeof (E4_Event32));
++
++    return (EP_INPUTQ *) inputq;
++}
++
++void
++ep4_free_inputq (EP_RAIL *r, EP_INPUTQ *q)
++{
++    EP4_RAIL   *rail   = (EP4_RAIL *) r;
++    EP4_INPUTQ *inputq = (EP4_INPUTQ *) q;
++
++    ep_free_main (&rail->r_generic, inputq->q_slotsAddr, inputq->q_slotSize * inputq->q_slotCount);
++
++    if (inputq->q_callback)
++    {
++      ep4_deregister_intcookie (rail, &inputq->q_intcookie);
++      ep4_put_ecq (rail, inputq->q_ecq, 1);
++      ep4_put_ecq (rail, inputq->q_wcq, 4);
++    }
++
++    KMEM_FREE (inputq, sizeof (EP4_INPUTQ));
++}
++
++void
++ep4_enable_inputq (EP_RAIL *r, EP_INPUTQ *q)
++{
++    EP4_RAIL     *rail     = (EP4_RAIL *) r;
++    EP4_INPUTQ   *inputq   = (EP4_INPUTQ *) q;
++    EP_ADDR       lastSlot = inputq->q_slotsAddr + (inputq->q_slotCount-1) * inputq->q_slotSize;
++    E4_InputQueue qdesc;
++
++    qdesc.q_bptr    = inputq->q_slotsAddr;
++    qdesc.q_fptr    = inputq->q_slotsAddr;
++    qdesc.q_control = E4_InputQueueControl (inputq->q_slotsAddr, lastSlot, inputq->q_slotSize);
++    qdesc.q_event   = inputq->q_callback ? inputq->q_eventAddr : 0;
++
++    /* copy the queue descriptor down to sdram */
++    ep4_write_qdesc (rail, inputq->q_desc, &qdesc);
++
++    EPRINTF5 (DBG_KMSG,  "ep_enable_inputq: %x - %016llx %016llx %016llx %016llx\n", (int) inputq->q_descAddr,
++          elan4_sdram_readq (rail->r_ctxt.ctxt_dev, inputq->q_desc + 0),
++          elan4_sdram_readq (rail->r_ctxt.ctxt_dev, inputq->q_desc + 8),
++          elan4_sdram_readq (rail->r_ctxt.ctxt_dev, inputq->q_desc + 16),
++          elan4_sdram_readq (rail->r_ctxt.ctxt_dev, inputq->q_desc + 24));
++}
++
++void
++ep4_disable_inputq (EP_RAIL *r, EP_INPUTQ *q)
++{
++    EP4_RAIL     *rail   = (EP4_RAIL *) r;
++    EP4_INPUTQ   *inputq = (EP4_INPUTQ *) q;
++    E4_InputQueue qdesc;
++
++    /* Initialise the input queue descriptor as "full" with no event */
++    qdesc.q_bptr    = 0;
++    qdesc.q_fptr    = 8;
++    qdesc.q_control = E4_InputQueueControl(qdesc.q_bptr, qdesc.q_fptr, 8);
++    qdesc.q_event   = 0;
++
++    /* copy the queue descriptor down to sdram */
++    ep4_write_qdesc (rail, inputq->q_desc, &qdesc);
++}
++
++int
++ep4_poll_inputq (EP_RAIL *r, EP_INPUTQ *q, int maxCount, EP_INPUTQ_HANDLER *handler, void *arg)
++{
++    EP4_RAIL   *rail   = (EP4_RAIL *) r;
++    ELAN4_DEV  *dev    = rail->r_ctxt.ctxt_dev; 
++    EP4_INPUTQ *inputq = (EP4_INPUTQ *) q;
++    sdramaddr_t qdesc = inputq->q_desc;
++    E4_Addr     fptr  = inputq->q_fptr;
++    E4_Addr     bptr  = elan4_sdram_readl (dev, qdesc + offsetof (E4_InputQueue, q_bptr));
++    int               count = 0;
++    int         delay;
++
++    while (bptr != 0 && fptr != bptr)
++    {
++      while (fptr != bptr)
++      {
++          unsigned long slot = (unsigned long) inputq->q_slots + (fptr - inputq->q_slotsAddr);
++          
++          /* Poll the final word of the message until the message has completely
++           * arrived in main memory. */
++          for (delay = 1; ((uint32_t *) (slot + inputq->q_slotSize))[-1] == EP_SYSTEMQ_UNRECEIVED && delay < EP_SYSTEMQ_UNRECEIVED_TLIMIT; delay <<= 1)
++              DELAY (delay);
++          
++          EPRINTF4(DBG_KMSG, "ep4_poll_inputq: %x slot %d of %d [%08x]\n", (int)inputq->q_descAddr,
++                   ((int)(fptr - inputq->q_slotsAddr))/inputq->q_slotSize, 
++                   inputq->q_slotCount, ((uint32_t *) (slot + inputq->q_slotSize))[-1]);
++          
++          /* Call the message handler */
++          (*handler) (r, arg, (void *) slot);
++          
++          /* reset the last word of the slot to "unreceived" */
++          ((uint32_t *) (slot + inputq->q_slotSize))[-1] = EP_SYSTEMQ_UNRECEIVED;
++          
++          /* move on the front pointer */
++          fptr = (fptr == inputq->q_last) ? inputq->q_slotsAddr : fptr + inputq->q_slotSize;
++          
++          elan4_sdram_writel (dev, qdesc + offsetof (E4_InputQueue, q_fptr), fptr);
++          
++          inputq->q_count++;
++          
++          if (++count >= maxCount && maxCount)
++          {
++              inputq->q_fptr = fptr;
++
++              return count;
++          }
++      }
++
++      bptr = elan4_sdram_readl (dev, qdesc + offsetof (E4_InputQueue, q_bptr));
++    }
++
++    inputq->q_fptr = fptr;
++
++    /* Only insert a single wait event command if the callback has
++     * occured, otherwise just acrue the count as we've just periodically
++     * polled it.
++     */
++    if (inputq->q_callback && atomic_read (&inputq->q_fired))
++    {
++      atomic_dec (&inputq->q_fired);
++
++      ep4_wait_event_cmd (inputq->q_wcq, inputq->q_eventAddr,
++                          E4_EVENT_INIT_VALUE (-inputq->q_count << 5, E4_EVENT_WRITE, E4_EVENT_DTYPE_LONG, 0),
++                          inputq->q_ecq->ecq_addr,
++                          (inputq->q_intcookie.int_val << E4_MAIN_INT_SHIFT) | INTERRUPT_CMD);
++
++      inputq->q_count = 0;
++    }
++
++    return count;
++}
++
++#define Q_MSG(q,slotNum)         (unsigned long)((q)->q_main      + (slotNum) * (q)->q_slotSize)
++#define Q_MSG_ADDR(q,slotNum)                 ((q)->q_mainAddr  + (slotNum) * (q)->q_slotSize)
++#define Q_DONE(q,slotNum)        *((E4_uint64 *)((q)->q_main      + (q)->q_slotCount * (q)->q_slotSize + (slotNum) * sizeof (E4_uint64)))
++#define Q_DONE_ADDR(q,slotNum)                        ((q)->q_mainAddr  + (q)->q_slotCount * (q)->q_slotSize + (slotNum) * sizeof (E4_uint64))
++
++#define Q_MAIN_SIZE(q)                        ((q)->q_slotCount * ((q)->q_slotSize + sizeof (E4_uint64)))
++
++#define Q_DONE_VAL(val,cnt)           ((cnt) << 16 | (val))
++#define Q_DONE_RET(done)              ((int) ((done) & 0xffff))
++#define Q_DONE_CNT(done)              ((int) ((done) >> 16))
++
++EP_OUTPUTQ *
++ep4_alloc_outputq (EP_RAIL *r, unsigned slotSize, unsigned slotCount)
++{
++    EP4_RAIL    *rail = (EP4_RAIL *) r;
++    EP4_OUTPUTQ *outputq;
++    int          i;
++
++    ASSERT ((slotSize & (EP_SYSTEMQ_MSG_ALIGN-1)) == 0);
++
++    KMEM_ALLOC (outputq, EP4_OUTPUTQ *, sizeof (EP4_OUTPUTQ), 1);
++
++    if (outputq == NULL)
++      return NULL;
++
++    spin_lock_init (&outputq->q_lock);
++
++    outputq->q_slotCount = slotCount;
++    outputq->q_slotSize  = slotSize;
++    outputq->q_main      = ep_alloc_main (r, Q_MAIN_SIZE(outputq), 0, &outputq->q_mainAddr);
++
++    if (outputq->q_main == (E4_uint64 *) NULL)
++    {
++      KMEM_FREE (outputq, sizeof (EP_OUTPUTQ));
++      return NULL;
++    }
++
++    outputq->q_cq = elan4_alloccq (&rail->r_ctxt, CQ_Size64K, CQ_STENEnableBit | CQ_WriteEnableBit, CQ_Priority);
++
++    if (outputq->q_cq == (ELAN4_CQ *) NULL)
++    {
++      ep_free_main (&rail->r_generic, outputq->q_mainAddr, Q_MAIN_SIZE(outputq));
++
++      KMEM_FREE (outputq, sizeof (EP_OUTPUTQ));
++    }
++
++    outputq->q_dwords = CQ_Size (outputq->q_cq->cq_size) >> 3;
++
++    /* mark all the queue slots as finished */
++    for (i = 0; i < slotCount; i++)
++      Q_DONE(outputq, i) = Q_DONE_VAL (EP_OUTPUTQ_FINISHED, 0);
++
++    return (EP_OUTPUTQ *) outputq;
++}
++
++void
++ep4_free_outputq (EP_RAIL *r, EP_OUTPUTQ *q)
++{
++    EP4_RAIL    *rail    = (EP4_RAIL *) r;
++    EP4_OUTPUTQ *outputq = (EP4_OUTPUTQ *) q;
++
++    elan4_freecq (&rail->r_ctxt, outputq->q_cq);
++
++    ep_free_main (&rail->r_generic, outputq->q_mainAddr, Q_MAIN_SIZE(outputq));
++
++    spin_lock_destroy (&outputq->q_lock);
++
++    KMEM_FREE (outputq, sizeof (EP4_OUTPUTQ));
++}
++
++void *
++ep4_outputq_msg (EP_RAIL *r, EP_OUTPUTQ *q, unsigned slotNum)
++{
++    return (void *) Q_MSG ((EP4_OUTPUTQ *) q, slotNum);
++}
++
++int
++ep4_outputq_state (EP_RAIL *r, EP_OUTPUTQ *q, unsigned slotNum)
++{
++    EPRINTF2 (DBG_KMSG, "ep4_outputq_state: slotNum %d state %x\n", slotNum, (int)Q_DONE((EP4_OUTPUTQ *) q, slotNum));
++
++    return Q_DONE_RET(Q_DONE((EP4_OUTPUTQ *)q, slotNum));
++}
++
++int
++ep4_outputq_send (EP_RAIL *r, EP_OUTPUTQ *q, unsigned slotNum, unsigned size, 
++                unsigned vp, unsigned qnum, unsigned retries)
++{
++    EP4_OUTPUTQ *outputq = (EP4_OUTPUTQ *) q;
++    unsigned int nbytes  = roundup (size, 32);
++    unsigned int base    = outputq->q_slotSize - nbytes;
++    unsigned int i, dwords;
++    unsigned long flags;
++    E4_uint64 val;
++
++    spin_lock_irqsave (&outputq->q_lock, flags);
++
++    EPRINTF4 (DBG_KMSG, "ep4_outputq_send: slotNum=%d size=%d vp=%d qnum=%d\n", slotNum, size, vp, qnum);
++
++    /* compute command queue size as follows - each slot uses
++     *     overhead:  14 dwords +
++     *           data > 128 ? 36 dwords
++     *     data > 64  ? 18 dwords
++     *     data > 32  ? 10 dwords
++     *     else         6  dwords
++     */
++    dwords = 14 + (size > 128 ? 36 :
++                 size > 64  ? 18 :
++                 size  ? 10 : 6);
++
++    outputq->q_dwords += Q_DONE_CNT (Q_DONE(outputq, slotNum));
++
++    if (dwords > outputq->q_dwords)
++    {
++      /* attempt to reclaim command queue space from other slots */
++      i = slotNum;
++      do {
++          if (++i == outputq->q_slotCount)
++              i = 0;
++          
++          val = Q_DONE(outputq, i);
++
++          if ((Q_DONE_RET (val) == EP_OUTPUTQ_FINISHED || Q_DONE_RET (val) == EP_OUTPUTQ_FAILED) && Q_DONE_CNT(val) > 0)
++          {
++              outputq->q_dwords += Q_DONE_CNT (val);
++
++              Q_DONE(outputq, i) = Q_DONE_VAL(Q_DONE_RET(val), 0);
++          }
++      } while (i != slotNum && dwords > outputq->q_dwords);
++    }
++
++    if (dwords > outputq->q_dwords)
++    {
++      spin_unlock_irqrestore (&outputq->q_lock, flags);
++      
++      EPRINTF0 (DBG_KMSG, "ep4_outputq_state: no command queue space\n");
++      return 0;
++    }
++
++    outputq->q_dwords -= dwords;
++
++    Q_DONE(outputq, slotNum) = Q_DONE_VAL (EP_OUTPUTQ_BUSY, dwords);
++
++    if (outputq->q_retries != retries)
++    {
++      elan4_guard (outputq->q_cq, GUARD_CHANNEL(1) | GUARD_RESET(outputq->q_retries = retries));
++      elan4_nop_cmd (outputq->q_cq, 0);
++    }
++
++    /* transfer the top "size" bytes from message buffer to top of input queue */
++    elan4_open_packet (outputq->q_cq, OPEN_PACKET (0, PACK_OK | RESTART_COUNT_ZERO, vp));
++    elan4_sendtrans0 (outputq->q_cq, TR_INPUT_Q_GETINDEX, EP_SYSTEMQ_ADDR(qnum));
++
++    /* send upto EP_SYSTEMQ_MSG_MAX (256) bytes of message to the top of the slot */
++    if (size > 128)
++    {
++      elan4_sendtransp (outputq->q_cq, TR_WRITE (128 >> 3, 0, TR_DATATYPE_DWORD), base + 0,   (void *) (Q_MSG(outputq, slotNum) + base + 0));
++      elan4_sendtransp (outputq->q_cq, TR_WRITE (128 >> 3, 0, TR_DATATYPE_DWORD), base + 128, (void *) (Q_MSG(outputq, slotNum) + base + 128));
++    }
++    else if (size > 64)
++      elan4_sendtransp (outputq->q_cq, TR_WRITE (128 >> 3, 0, TR_DATATYPE_DWORD), base, (void *) (Q_MSG(outputq, slotNum) + base));
++    else if (size > 32)
++      elan4_sendtransp (outputq->q_cq, TR_WRITE (64 >> 3, 0, TR_DATATYPE_DWORD),  base, (void *) (Q_MSG(outputq, slotNum) + base));
++    else
++      elan4_sendtransp (outputq->q_cq, TR_WRITE (32 >> 3, 0, TR_DATATYPE_DWORD),  base, (void *) (Q_MSG(outputq, slotNum) + base));
++    elan4_sendtrans1 (outputq->q_cq, TR_INPUT_Q_COMMIT, EP_SYSTEMQ_ADDR(qnum), 0 /* no cookie */);
++
++    elan4_guard (outputq->q_cq, GUARD_CHANNEL (1) | GUARD_TEST(0, PACK_OK) | GUARD_RESET (outputq->q_retries));
++    elan4_write_dword_cmd (outputq->q_cq, Q_DONE_ADDR(outputq, slotNum), Q_DONE_VAL (EP_OUTPUTQ_FINISHED, dwords));
++
++    elan4_guard (outputq->q_cq, GUARD_CHANNEL (1) | GUARD_TEST(0, RESTART_COUNT_ZERO) | GUARD_RESET (outputq->q_retries));
++    elan4_write_dword_cmd (outputq->q_cq, Q_DONE_ADDR(outputq, slotNum), Q_DONE_VAL (EP_OUTPUTQ_FAILED, dwords));
++
++    spin_unlock_irqrestore (&outputq->q_lock, flags);
++
++    return 1;
++}
+Index: linux-2.4.21/drivers/net/qsnet/ep/kthread.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/kthread.c   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/kthread.c        2005-06-01 23:12:54.672428008 -0400
+@@ -0,0 +1,186 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: kthread.c,v 1.5 2004/05/19 08:54:57 david Exp $ $Name: QSNETMODULES-4-30_20050128 $"
++/*      $Source: /cvs/master/quadrics/epmod/kthread.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan/kthread.h>
++
++void
++ep_kthread_init (EP_KTHREAD *kt)
++{
++      spin_lock_init (&kt->lock);
++      kcondvar_init (&kt->wait);
++      
++      kt->next_run     = 0;
++      kt->should_stall = 0;
++      kt->started      = 0;
++      kt->should_stop  = 0;
++      kt->stopped      = 0;
++      kt->state        = KT_STATE_RUNNING;
++}
++
++void
++ep_kthread_destroy (EP_KTHREAD *kt)
++{
++      spin_lock_destroy (&kt->lock);
++      kcondvar_destroy (&kt->wait);
++}
++
++void
++ep_kthread_started (EP_KTHREAD *kt)
++{
++      unsigned long flags;
++      
++      spin_lock_irqsave (&kt->lock, flags);
++      kt->started = 1;
++      spin_unlock_irqrestore(&kt->lock, flags);
++}
++
++void
++ep_kthread_stopped (EP_KTHREAD *kt)
++{
++      unsigned long flags;
++      
++      spin_lock_irqsave (&kt->lock, flags);
++      kt->stopped = 1;
++      kcondvar_wakeupall (&kt->wait, &kt->lock);
++      spin_unlock_irqrestore(&kt->lock, flags);
++}
++
++int
++ep_kthread_should_stall (EP_KTHREAD *kth)
++{
++      return (kth->should_stall);
++}
++
++int
++ep_kthread_sleep (EP_KTHREAD *kt, long next_run)
++{
++      unsigned long flags;
++
++      spin_lock_irqsave (&kt->lock, flags);
++      if (next_run && (kt->next_run == 0 || BEFORE (next_run, kt->next_run)))
++              kt->next_run = next_run;
++
++      if (kt->should_stop)
++      {
++              spin_unlock_irqrestore (&kt->lock, flags);
++              return (-1);
++      }
++      
++      do {
++              if (kt->should_stall)
++                      kcondvar_wakeupall (&kt->wait, &kt->lock);
++
++              kt->state = KT_STATE_SLEEPING;
++              kt->running = 0;
++              if (kt->should_stall || kt->next_run == 0)
++                      kcondvar_wait (&kt->wait, &kt->lock, &flags);
++              else
++                      kcondvar_timedwait (&kt->wait,&kt->lock, &flags, kt->next_run);
++              kt->state = KT_STATE_RUNNING;
++              kt->running = lbolt;
++      } while (kt->should_stall);
++      kt->next_run = 0;
++      spin_unlock_irqrestore (&kt->lock, flags);
++      
++      return (0);
++}
++
++void
++ep_kthread_schedule (EP_KTHREAD *kt, long tick)
++{
++      unsigned long flags;
++      
++      spin_lock_irqsave (&kt->lock, flags);
++      if (kt->next_run == 0 || BEFORE (tick, kt->next_run))
++      {
++              kt->next_run = tick;
++              if (!kt->should_stall && kt->state == KT_STATE_SLEEPING)
++              {
++                      kt->state = KT_STATE_SCHEDULED;
++                      kcondvar_wakeupone (&kt->wait, &kt->lock);
++              }
++      }
++      spin_unlock_irqrestore (&kt->lock, flags);
++}
++
++void
++ep_kthread_stall (EP_KTHREAD *kt)
++{
++      unsigned long flags;
++      
++      spin_lock_irqsave (&kt->lock, flags);
++      if (kt->should_stall++ == 0)
++              kcondvar_wakeupall (&kt->wait, &kt->lock);
++
++      while (kt->state != KT_STATE_SLEEPING)
++              kcondvar_wait (&kt->wait, &kt->lock, &flags);
++      spin_unlock_irqrestore (&kt->lock, flags);
++}
++
++void
++ep_kthread_resume (EP_KTHREAD *kt)
++{
++      unsigned long flags;
++
++      spin_lock_irqsave (&kt->lock, flags);
++      if (--kt->should_stall == 0)
++      {
++              kt->state = KT_STATE_SCHEDULED;
++              kcondvar_wakeupone (&kt->wait, &kt->lock);
++      }
++      spin_unlock_irqrestore (&kt->lock, flags);
++}
++
++void
++ep_kthread_stop (EP_KTHREAD *kt)
++{
++      unsigned long flags;
++      
++      spin_lock_irqsave (&kt->lock, flags);
++      kt->should_stop = 1;
++      while (kt->started && !kt->stopped)
++      {
++              kcondvar_wakeupall (&kt->wait, &kt->lock);
++              kcondvar_wait (&kt->wait, &kt->lock, &flags);
++      }
++      spin_unlock_irqrestore (&kt->lock, flags);
++}
++
++int
++ep_kthread_state (EP_KTHREAD *kt, long *time)
++{
++      unsigned long flags;
++      int res = KT_STATE_SLEEPING;
++
++      spin_lock_irqsave (&kt->lock, flags);
++
++      if (kt->next_run) {
++              *time = kt->next_run;
++              res   = kt->should_stall ? KT_STATE_STALLED : KT_STATE_SCHEDULED;
++      }
++
++      if (kt->running) {
++              *time = kt->running;
++              res   = KT_STATE_RUNNING;
++      }
++
++      spin_unlock_irqrestore (&kt->lock, flags);
++      
++      return res;
++}
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/ep/kthread.h
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/kthread.h   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/kthread.h        2005-06-01 23:12:54.673427856 -0400
+@@ -0,0 +1,53 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN3_KTHREAD_H
++#define __ELAN3_KTHREAD_H
++
++#ident "@(#)$Id: kthread.h,v 1.4 2004/05/06 14:24:08 david Exp $ $Name: QSNETMODULES-4-30_20050128 $"
++/*      $Source: /cvs/master/quadrics/epmod/kthread.h,v $*/
++
++typedef struct ep_kthread
++{
++      kcondvar_t      wait;                                   /* place to sleep */
++      spinlock_t      lock;                                   /* and lock */
++      long            next_run;                               /* tick when thread should next run */
++      long            running;                                /* tick when thread started to run */
++      unsigned short  should_stall;
++      unsigned char   state;
++      unsigned int    started:1;
++      unsigned int    should_stop:1;
++      unsigned int    stopped:1;
++} EP_KTHREAD;
++
++#define KT_STATE_SLEEPING             0
++#define KT_STATE_SCHEDULED            1
++#define KT_STATE_RUNNING              2
++#define KT_STATE_STALLED              3
++
++#define AFTER(a, b)                   ((((long)(a)) - ((long)(b))) > 0)
++#define BEFORE(a,b)                   ((((long)(a)) - ((long)(b))) < 0)
++
++extern void ep_kthread_init (EP_KTHREAD *kt);
++extern void ep_kthread_destroy (EP_KTHREAD *kt);
++extern void ep_kthread_started (EP_KTHREAD *kt);
++extern void ep_kthread_stopped (EP_KTHREAD *kt);
++extern int  ep_kthread_should_stall (EP_KTHREAD *kth);
++extern int  ep_kthread_sleep (EP_KTHREAD *kth, long next_run);
++extern void ep_kthread_schedule (EP_KTHREAD *kt, long when);
++extern void ep_kthread_stall (EP_KTHREAD *kth);
++extern void ep_kthread_resume (EP_KTHREAD *kt);
++extern void ep_kthread_stop (EP_KTHREAD *kt);
++extern int  ep_kthread_state (EP_KTHREAD *kt, long *time);
++#endif /* __ELAN3_KTHREAD_H */
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/ep/Makefile
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/Makefile    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/Makefile 2005-06-01 23:12:54.673427856 -0400
+@@ -0,0 +1,33 @@
++#
++# Makefile for Quadrics QsNet
++#
++# Copyright (c) 2002-2004 Quadrics Ltd
++#
++# File: drivers/net/qsnet/ep/Makefile
++#
++
++
++ep3-$(CONFIG_ELAN3)   := kcomm_elan3.o kmsg_elan3.o kmap_elan3.o neterr_elan3.o probenetwork_elan3.o support_elan3.o threadcode_elan3.o threadcode_elan3_Linux.o epcomms_elan3.o epcommsTx_elan3.o epcommsRx_elan3.o
++ep4-$(CONFIG_ELAN4)   := kcomm_elan4.o kmsg_elan4.o kmap_elan4.o neterr_elan4.o probenetwork_elan4.o commands_elan4.o debug_elan4.o support_elan4.o threadcode_elan4_Linux.o epcomms_elan4.o epcommsTx_elan4.o epcommsRx_elan4.o
++#
++
++#
++# Makefile for Quadrics QsNet
++#
++# Copyright (c) 2004 Quadrics Ltd.
++#
++# File: driver/net/qsnet/ep/Makefile
++#
++
++list-multi            := ep.o
++ep-objs       := cm.o debug.o kalloc.o kcomm.o kmap.o kthread.o neterr.o nmh.o probenetwork.o railhints.o rmap.o statemap.o support.o threadcode.o epcomms.o epcommsRx.o epcommsTx.o epcommsFwd.o conf_linux.o procfs_linux.o ep_procfs.o cm_procfs.o $(ep3-$(CONFIG_EP)) $(ep4-$(CONFIG_EP))
++export-objs           := conf_linux.o
++obj-$(CONFIG_EP)      := ep.o
++
++ep.o : $(ep-objs)
++      $(LD) -r -o $@ $(ep-objs)
++
++EXTRA_CFLAGS          +=  -DDEBUG -DDEBUG_PRINTF -DDEBUG_ASSERT
++
++include $(TOPDIR)/Rules.make
++
+Index: linux-2.4.21/drivers/net/qsnet/ep/Makefile.conf
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/Makefile.conf       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/Makefile.conf    2005-06-01 23:12:54.673427856 -0400
+@@ -0,0 +1,12 @@
++# Flags for generating QsNet Linux Kernel Makefiles
++MODNAME               =       ep.o
++MODULENAME    =       ep
++KOBJFILES     =       cm.o debug.o kalloc.o kcomm.o kmap.o kthread.o neterr.o nmh.o probenetwork.o railhints.o rmap.o statemap.o support.o threadcode.o epcomms.o epcommsRx.o epcommsTx.o epcommsFwd.o conf_linux.o procfs_linux.o ep_procfs.o cm_procfs.o \$\(ep3-\$\(CONFIG_EP\)\) \$\(ep4-\$\(CONFIG_EP\)\)
++EXPORT_KOBJS  =       conf_linux.o
++CONFIG_NAME   =       CONFIG_EP
++SGALFC                =       
++# EXTRALINES START
++
++ep3-$(CONFIG_ELAN3)   := kcomm_elan3.o kmsg_elan3.o kmap_elan3.o neterr_elan3.o probenetwork_elan3.o support_elan3.o threadcode_elan3.o threadcode_elan3_Linux.o epcomms_elan3.o epcommsTx_elan3.o epcommsRx_elan3.o
++ep4-$(CONFIG_ELAN4)   := kcomm_elan4.o kmsg_elan4.o kmap_elan4.o neterr_elan4.o probenetwork_elan4.o commands_elan4.o debug_elan4.o support_elan4.o threadcode_elan4_Linux.o epcomms_elan4.o epcommsTx_elan4.o epcommsRx_elan4.o
++# EXTRALINES END
+Index: linux-2.4.21/drivers/net/qsnet/ep/neterr.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/neterr.c    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/neterr.c 2005-06-01 23:12:54.674427704 -0400
+@@ -0,0 +1,82 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: neterr.c,v 1.25.8.1 2004/11/12 10:54:51 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/neterr.c,v $ */
++
++#include <qsnet/kernel.h>
++#include <elan/kcomm.h>
++
++#include "debug.h"
++
++void
++ep_queue_network_error (EP_RAIL *rail, int nodeId, int what, int channel, EP_NETERR_COOKIE cookie)
++{
++    EP_SYS       *sys      = rail->System;
++    EP_NODE_RAIL *nodeRail = &rail->Nodes[nodeId];
++    unsigned long flags;
++
++    spin_lock_irqsave (&sys->NodeLock, flags);
++
++    ASSERT (nodeRail->State >= EP_NODE_CONNECTED && nodeRail->State <= EP_NODE_LOCAL_PASSIVATE);
++    
++    if (nodeRail->NetworkErrorState == 0)
++    {
++      EPRINTF2 (DBG_NETWORK_ERROR, "%s: raise context filter for node %d due to network error\n", rail->Name, nodeId);
++      printk ("%s: raise context filter for node %d due to network error\n", rail->Name, nodeId);
++      
++      rail->Operations.RaiseFilter (rail, nodeId);
++      
++      if (nodeRail->State == EP_NODE_LOCAL_PASSIVATE)
++          printk ("%s: node %d is flushing - deferring network error fixup\n", rail->Name, nodeId);
++      else
++          list_add_tail (&nodeRail->Link, &rail->NetworkErrorList);
++    }
++    
++    switch (what)
++    {
++    case EP_NODE_NETERR_ATOMIC_PACKET:
++      ASSERT (nodeRail->NetworkErrorCookies[channel] == 0);
++      
++      /* Need to raise the approriate context filter for this node,
++       * and periodically send a neterr fixup message to it until 
++       * we receive an ack from it
++       */
++      IncrStat (rail, NeterrAtomicPacket);
++      
++      nodeRail->NetworkErrorCookies[channel] = cookie;
++      
++      nodeRail->NetworkErrorState |= EP_NODE_NETERR_ATOMIC_PACKET;
++      nodeRail->MsgXid             = ep_xid_cache_alloc (sys, &rail->XidCache);
++      
++      EPRINTF3 (DBG_NETWORK_ERROR, "%s: atomic packet destroyed - node %d cookie %llx\n", rail->Name, nodeId, cookie);
++
++      printk ("%s: atomic packet destroyed - node %d cookie %llx\n", rail->Name, nodeId, cookie);
++      break;
++
++    case EP_NODE_NETERR_DMA_PACKET:
++      /* Must be an overlapped dma packet, raise the context filter,
++       * and hold it up for a NETWORK_ERROR_TIMEOUT */
++      IncrStat (rail, NeterrDmaPacket);
++      
++      nodeRail->NetworkErrorState |= EP_NODE_NETERR_DMA_PACKET;
++      break;
++    }
++
++    nodeRail->NextRunTime = lbolt + NETWORK_ERROR_TIMEOUT;
++    
++    spin_unlock_irqrestore (&sys->NodeLock, flags);
++
++    ep_kthread_schedule (&sys->ManagerThread, nodeRail->NextRunTime);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++
+Index: linux-2.4.21/drivers/net/qsnet/ep/neterr_elan3.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/neterr_elan3.c      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/neterr_elan3.c   2005-06-01 23:12:54.674427704 -0400
+@@ -0,0 +1,326 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: neterr_elan3.c,v 1.24 2003/11/17 13:26:45 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/neterr_elan3.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++
++#include "kcomm_vp.h"
++#include "kcomm_elan3.h"
++#include "debug.h"
++
++typedef struct neterr_halt_args
++{
++    EP3_RAIL       *Rail;
++    unsigned int      NodeId;
++    EP_NETERR_COOKIE *Cookies;
++} NETERR_HALT_ARGS;
++
++static int
++DmaMatchesCookie (EP3_RAIL *rail, E3_DMA_BE *dma, int nodeId, EP_NETERR_COOKIE *cookies, char *where)
++{
++    E3_uint32     cvproc;
++    E3_uint32     cookie;
++    
++    if (dma->s.dma_direction == DMA_WRITE)
++    {
++      cvproc = dma->s.dma_destCookieVProc;
++      cookie = dma->s.dma_srcCookieVProc;
++    }
++    else
++    {
++      cvproc = dma->s.dma_srcCookieVProc;
++      cookie = dma->s.dma_destCookieVProc;
++    }
++    
++    EPRINTF6 (DBG_NETWORK_ERROR, "%s: Neterr - %s: DMA %08x %08x %08x %08x\n", rail->Generic.Name, where,
++            dma->s.dma_type, dma->s.dma_size, dma->s.dma_source, dma->s.dma_dest);
++    EPRINTF5 (DBG_NETWORK_ERROR, "%s:                     %08x %08x %08x %08x\n", rail->Generic.Name,
++            dma->s.dma_destEvent, dma->s.dma_destCookieVProc, dma->s.dma_srcEvent, dma->s.dma_srcCookieVProc);
++
++    if (EP_VP_ISDATA((cvproc & DMA_PROCESS_MASK)) && EP_VP_TO_NODE(cvproc & DMA_PROCESS_MASK) == nodeId)
++    {
++      /*
++       * This is a DMA going to the node which has a network fixup
++       * request pending, so check if the cookie matches.
++       */
++      if ((cookie == cookies[0] || cookie == cookies[1]) /* && !WaitForEop */)
++      {
++          EPRINTF3 (DBG_NETWORK_ERROR, "%s: match cookie %08x on %s\n", rail->Generic.Name, cookie, where);
++          
++          return (TRUE);
++      }
++    }
++
++    return (FALSE);
++}
++
++
++static void
++NetworkErrorHaltOperation (ELAN3_DEV *dev, void *arg)
++{
++    NETERR_HALT_ARGS *args = (NETERR_HALT_ARGS *) arg;
++    EP3_RAIL         *rail = args->Rail;
++    EP_SYS           *sys  = rail->Generic.System;
++    sdramaddr_t       FPtr, BPtr;
++    sdramaddr_t       Base, Top;
++    E3_DMA_BE         dma;
++    unsigned long     flags;
++
++    spin_lock_irqsave (&sys->NodeLock, flags);
++
++    ASSERT (elan3_sdram_readl (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProc.s.FSR)) == 0);
++    ASSERT (elan3_sdram_readl (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProcData0.s.FSR.Status)) == 0);
++    ASSERT (elan3_sdram_readl (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProcData1.s.FSR.Status)) == 0);
++    ASSERT (elan3_sdram_readl (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProcData2.s.FSR.Status)) == 0);
++    ASSERT (elan3_sdram_readl (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProcData3.s.FSR.Status)) == 0);
++    
++    FPtr  = read_reg32 (dev, DProc_SysCntx_FPtr);
++    BPtr =  read_reg32 (dev, DProc_SysCntx_BPtr);
++    Base  = dev->TAndQBase + offsetof (E3_TrapAndQueue, SysCntxDmaQueue[0]);
++    Top   = dev->TAndQBase + offsetof (E3_TrapAndQueue, SysCntxDmaQueue[E3_SysCntxQueueSize-1]);
++    
++    while (FPtr != BPtr)
++    {
++      elan3_sdram_copyq_from_sdram (dev, FPtr, &dma, sizeof (E3_DMA_BE));
++      
++      if (DmaMatchesCookie (rail, &dma, args->NodeId, args->Cookies, "runq "))
++      {
++          /*
++           * Transfer the DMA to the node, it's source event will 
++           * get executed later.
++           */
++          QueueDmaOnStalledList (rail, &dma);
++          
++          /*
++           * Remove the DMA from the queue by replacing it with one with
++           * zero size and no events.
++           *
++           * NOTE: we must preserve the SYS_CONTEXT_BIT since the Elan uses this
++           * to mark the approriate run queue as empty.
++           */
++          dma.s.dma_type            = (SYS_CONTEXT_BIT << 16);
++          dma.s.dma_size            = 0;
++          dma.s.dma_source          = (E3_Addr) 0;
++          dma.s.dma_dest            = (E3_Addr) 0;
++          dma.s.dma_destEvent       = (E3_Addr) 0;
++          dma.s.dma_destCookieVProc = 0;
++          dma.s.dma_srcEvent        = (E3_Addr) 0;
++          dma.s.dma_srcCookieVProc  = 0;
++          
++          elan3_sdram_copyq_to_sdram (dev, &dma, FPtr, sizeof (E3_DMA_BE));
++      }
++
++      FPtr = (FPtr == Top) ? Base : FPtr + sizeof (E3_DMA);
++    }
++
++    rail->NetworkErrorFlushed = TRUE;
++    kcondvar_wakeupall (&rail->NetworkErrorSleep, &sys->NodeLock);
++
++    spin_unlock_irqrestore (&sys->NodeLock, flags);
++}
++
++void
++ep3_neterr_fixup (EP_RAIL *r, unsigned int nodeId, EP_NETERR_COOKIE *cookies)
++{
++    EP3_RAIL        *rail        = (EP3_RAIL *) r;
++    EP_SYS          *sys         = rail->Generic.System;
++    ELAN3_DEV       *dev         = rail->Device;
++    EP_NODE_RAIL    *nodeRail    = &rail->Generic.Nodes[nodeId];
++    E3_DMA_BE        dmabe;
++    EP3_COOKIE      *cp;
++    E3_uint32        vp;
++    NETERR_HALT_ARGS args;
++    struct list_head *el, *nel, matchedList;
++    int              i;
++    unsigned long    flags;
++
++    INIT_LIST_HEAD (&matchedList);
++
++    StallDmaRetryThread (rail);
++
++    args.Rail       = rail;
++    args.NodeId     = nodeId;
++    args.Cookies    = cookies;
++
++    spin_lock_irqsave (&rail->Device->IntrLock, flags);
++    QueueHaltOperation (rail->Device, 0, NULL, INT_TProcHalted | INT_DProcHalted, NetworkErrorHaltOperation, &args);
++    spin_unlock_irqrestore (&rail->Device->IntrLock, flags);
++    
++    spin_lock_irqsave (&sys->NodeLock, flags);
++    while (! rail->NetworkErrorFlushed)
++      kcondvar_wait (&rail->NetworkErrorSleep, &sys->NodeLock, &flags);
++    rail->NetworkErrorFlushed = FALSE;
++    
++    spin_lock (&rail->DmaRetryLock);
++    for (i = EP_RETRY_BASE; i < EP_NUM_RETRIES; i++)
++    {
++      list_for_each_safe (el, nel, &rail->DmaRetries[i]) {
++          EP3_RETRY_DMA *retry = list_entry (el, EP3_RETRY_DMA, Link);
++
++          if (DmaMatchesCookie (rail, &retry->Dma, nodeId, cookies, "retry"))
++          {
++              /* remove from retry list */
++              list_del (&retry->Link);
++
++              /* add to list of dmas which matched */
++              list_add_tail (&retry->Link, &matchedList);
++          }
++      }
++    }
++    
++    list_for_each_safe (el, nel, &nodeRail->StalledDmas) {
++      EP3_RETRY_DMA *retry = list_entry (el, EP3_RETRY_DMA, Link);
++      
++      if (DmaMatchesCookie (rail, &retry->Dma, nodeId, cookies, "stalled"))
++      {
++          /* remove from retry list */
++          list_del (&retry->Link);
++          
++          /* add to list of dmas which matched */
++          list_add_tail (&retry->Link, &matchedList);
++      }
++    }
++    
++    spin_unlock (&rail->DmaRetryLock);
++    spin_unlock_irqrestore (&sys->NodeLock, flags);
++    
++    ResumeDmaRetryThread (rail);
++
++    /* Now "set" the source event of any write DMA's */
++    while (! list_empty (&matchedList))
++    {
++      EP3_RETRY_DMA *retry = list_entry (matchedList.next, EP3_RETRY_DMA, Link);
++      
++      list_del (&retry->Link);
++
++      if (retry->Dma.s.dma_direction == DMA_WRITE && retry->Dma.s.dma_srcEvent)
++      {
++          sdramaddr_t event = ep_elan2sdram (&rail->Generic, retry->Dma.s.dma_srcEvent);
++
++          /* Block local interrupts, since we need to atomically
++           * decrement the event count and perform the word write
++           */
++          local_irq_save (flags);
++          {
++              E3_uint32 type  = elan3_sdram_readl (dev, event + offsetof (E3_Event, ev_Type));
++              E3_uint32 count = elan3_sdram_readl (dev, event + offsetof (E3_Event, ev_Count));
++
++              elan3_sdram_writel (dev, event + offsetof (E3_Event, ev_Count), count - 1);
++
++              if (count == 1)
++              {
++                  if (type & EV_TYPE_MASK_BCOPY)
++                  {
++                      E3_Addr srcVal  = elan3_sdram_readl (dev, event + offsetof (E3_BlockCopyEvent, ev_Source));
++                      E3_Addr dstAddr = elan3_sdram_readl (dev, event + offsetof (E3_BlockCopyEvent, ev_Dest)) & ~EV_BCOPY_DTYPE_MASK;
++
++                      ASSERT ((srcVal & EV_WCOPY) != 0);
++                      
++                      EPRINTF3 (DBG_NETWORK_ERROR, "%s: neterr perform event word write at %08x with %08x\n", rail->Generic.Name, dstAddr, srcVal);
++
++                      ELAN3_OP_STORE32 (rail->Ctxt, dstAddr, srcVal);
++                  }
++
++                  if ((type & ~EV_TYPE_MASK_BCOPY) != 0)
++                  {
++                      if ((type & EV_TYPE_MASK_CHAIN) == EV_TYPE_CHAIN)
++                      {
++                          printk ("%s: event at %08x - chained event %x is invalid\n", rail->Generic.Name, retry->Dma.s.dma_srcEvent, type);
++                          panic ("ep: neterr invalid event type\n");
++                      }
++                      else if ((type & EV_TYPE_MASK_EVIRQ) == EV_TYPE_EVIRQ)
++                      {
++                          EPRINTF2 (DBG_NETWORK_ERROR, "%s: neterr event interrupt - cookie %08x\n", rail->Generic.Name, (type & ~(EV_TYPE_MASK_EVIRQ|EV_TYPE_MASK_BCOPY)));
++                          
++                          cp = LookupCookie (&rail->CookieTable, (type & ~(EV_TYPE_MASK_EVIRQ|EV_TYPE_MASK_BCOPY)));
++                          
++                          if (cp->Operations->Event)
++                              cp->Operations->Event(rail, cp->Arg);
++                      }
++                      else if ((type & EV_TYPE_MASK_DMA) == EV_TYPE_DMA)
++                      {
++                          sdramaddr_t dma = ep_elan2sdram (&rail->Generic, (type & ~EV_TYPE_MASK2));
++                          
++                          EPRINTF2 (DBG_NETWORK_ERROR, "%s: neterr chained dma - %08x\n", rail->Generic.Name, (type & ~EV_TYPE_MASK2));
++                          
++                          elan3_sdram_copyq_from_sdram (dev, dma, &dmabe, sizeof (E3_DMA));
++                          
++                          if (dmabe.s.dma_direction == DMA_WRITE)
++                          {
++                              vp = dmabe.s.dma_destVProc;
++                              cp = LookupEventCookie (rail, &rail->CookieTable, dmabe.s.dma_srcEvent);
++                          }
++                          else
++                          {
++                              vp = dmabe.s.dma_srcVProc;
++                              cp = LookupEventCookie (rail, &rail->CookieTable, dmabe.s.dma_destEvent);
++                              
++                              /* we MUST convert this into a DMA_READ_REQUEUE dma as if we don't the 
++                               * DMA descriptor will be read from the EP_RETRY_DMA rather than the 
++                               * original DMA - this can then get reused and an incorrect DMA 
++                               * descriptor sent 
++                               * eventp->ev_Type contains the dma address with type in the lower bits 
++                               */ 
++                          
++                              dmabe.s.dma_source    = (type & ~EV_TYPE_MASK2);
++                              dmabe.s.dma_direction = (dmabe.s.dma_direction & ~DMA_READ) | DMA_READ_REQUEUE;
++                          }
++                      
++                          ASSERT (EP_VP_ISDATA(vp));
++                      
++                          nodeRail = &rail->Generic.Nodes[EP_VP_TO_NODE(vp)];
++
++                          switch (nodeRail->State)
++                          {
++                          case EP_NODE_CONNECTED:
++                          case EP_NODE_LEAVING_CONNECTED:
++                              if (cp != NULL)
++                                  cp->Operations->DmaRetry (rail, cp->Arg, &dmabe, EAGAIN);
++                              else
++                              {
++                                  ASSERT (dmabe.s.dma_direction == DMA_WRITE && dmabe.s.dma_srcEvent == 0 && dmabe.s.dma_isRemote);
++                              
++                                  QueueDmaForRetry (rail, &dmabe, EP_RETRY_ANONYMOUS);
++                              }
++                              break;
++
++                          case EP_NODE_LOCAL_PASSIVATE:
++                              QueueDmaOnStalledList (rail, &dmabe);
++                              break;
++
++                          default:
++                              panic ("ep: neterr incorrect state for node\n");
++                          }
++                      }
++                      else if ((type & EV_TYPE_MASK_THREAD) == EV_TYPE_THREAD)
++                      {
++                          printk ("%s: event at %08x - thread waiting %x is invalid\n", rail->Generic.Name, retry->Dma.s.dma_srcEvent, type);
++                          panic ("ep: neterr invalid event type\n");
++                      }
++                  }
++              }
++          }
++          local_irq_restore(flags);
++      }
++      
++      /* add to free list */
++      spin_lock_irqsave (&rail->DmaRetryLock, flags);
++      list_add (&retry->Link, &rail->DmaRetryFreeList);
++      spin_unlock_irqrestore (&rail->DmaRetryLock, flags);
++    }
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++
+Index: linux-2.4.21/drivers/net/qsnet/ep/neterr_elan4.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/neterr_elan4.c      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/neterr_elan4.c   2005-06-01 23:12:54.675427552 -0400
+@@ -0,0 +1,251 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: neterr_elan4.c,v 1.2 2003/11/24 17:57:24 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/neterr_elan4.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++
++#include "kcomm_vp.h"
++#include "kcomm_elan4.h"
++#include "debug.h"
++
++struct neterr_desc
++{
++    EP4_RAIL         *rail;
++    unsigned int      nodeid;
++    EP_NETERR_COOKIE *cookies;
++    int                     done;
++} ;
++
++static int
++dma_matches_cookie (EP4_RAIL *rail, E4_uint64 vproc, E4_uint64 cookie, unsigned int nodeId, EP_NETERR_COOKIE *cookies, const char *where)
++{
++    if ((EP_VP_ISDATA (vproc) && EP_VP_TO_NODE (vproc) == nodeId) && (cookie == cookies[0] || cookie == cookies[1]))
++    {
++      EPRINTF3 (DBG_NETWORK_ERROR, "%s: match cookie %016llx on %s\n", rail->r_generic.Name, cookie, where);
++
++      return 1;
++    }
++    return 0;
++}
++
++static void
++ep4_neterr_dma_flushop (ELAN4_DEV *dev, void *arg, int qfull)
++{
++    struct neterr_desc *desc  = (struct neterr_desc *) arg;
++    EP4_RAIL           *rail  = desc->rail;
++    E4_uint64           qptrs = read_reg64 (dev, DProcHighPriPtrs);
++    E4_uint32           qsize = E4_QueueSize (E4_QueueSizeValue (qptrs));
++    E4_uint32           qfptr = E4_QueueFrontPointer (qptrs);
++    E4_uint32           qbptr = E4_QueueBackPointer (qptrs);
++    E4_DProcQueueEntry  qentry;
++    unsigned long       flags;
++
++    while ((qfptr != qbptr) || qfull)
++    {
++      E4_uint64 cookie = elan4_sdram_readq (dev, qfptr + offsetof (E4_DProcQueueEntry, Desc.dma_cookie));
++      E4_uint64 vproc  = elan4_sdram_readq (dev, qfptr + offsetof (E4_DProcQueueEntry, Desc.dma_vproc));
++
++      if (dma_matches_cookie (rail, vproc, cookie, desc->nodeid, desc->cookies, "runq "))
++      {
++          elan4_sdram_copyq_from_sdram (dev, qfptr, &qentry, sizeof (E4_DProcQueueEntry));
++
++          ep4_queue_dma_stalled (rail, &qentry.Desc);
++
++          /* Replace the dma with one which will "disappear" */
++          qentry.Desc.dma_typeSize = DMA_ShMemWrite | dev->dev_ctxt.ctxt_num;
++          qentry.Desc.dma_cookie   = 0;
++          qentry.Desc.dma_vproc    = 0;
++          qentry.Desc.dma_srcAddr  = 0;
++          qentry.Desc.dma_dstAddr  = 0;
++          qentry.Desc.dma_srcEvent = 0;
++          qentry.Desc.dma_dstEvent = 0;
++
++          elan4_sdram_copyq_to_sdram (dev, &qentry, qfptr, sizeof (E4_DProcQueueEntry));
++      }
++      
++      qfptr = (qfptr & ~(qsize-1)) | ((qfptr + sizeof (E4_DProcQueueEntry)) & (qsize-1));
++      qfull = 0;
++    }
++
++    spin_lock_irqsave (&rail->r_haltop_lock, flags);
++    desc->done = 1;
++    kcondvar_wakeupall (&rail->r_haltop_sleep, &rail->r_haltop_lock);
++    spin_unlock_irqrestore (&rail->r_haltop_lock, flags);
++}
++
++static void
++ep4_neterr_dma_haltop (ELAN4_DEV *dev, void *arg)
++{
++    struct neterr_desc *desc = (struct neterr_desc *) arg;
++
++    elan4_queue_dma_flushop (dev, &desc->rail->r_flushop, 1);
++}
++
++void
++ep4_neterr_fixup_dmas (EP4_RAIL *rail, unsigned int nodeId, EP_NETERR_COOKIE *cookies)
++{
++    EP_NODE_RAIL      *nodeRail = &rail->r_generic.Nodes[nodeId];
++    struct neterr_desc desc;
++    struct list_head   matchedList;
++    struct list_head  *el, *nel;
++    unsigned long      flags;
++    register int       i;
++
++    desc.rail    = rail;
++    desc.nodeid  = nodeId;
++    desc.cookies = cookies;
++    desc.done    = 0;
++
++    INIT_LIST_HEAD (&matchedList);
++
++    /* First -  stall the retry thread, so that it will no longer restart
++     *          any dma's from the retry list */
++    ep_kthread_stall (&rail->r_retry_thread);
++    
++    /* Second - flush through all command queues targetted by events, thread etc */
++    ep4_flush_ecqs (rail);
++    
++    /* Third - queue a halt operation to flush through all DMA's which are executing
++     *         or on the run queues */
++    kmutex_lock (&rail->r_haltop_mutex);
++    
++    rail->r_haltop.op_mask      = INT_DProcHalted;
++    rail->r_haltop.op_function  = ep4_neterr_dma_haltop;
++    rail->r_haltop.op_arg       = &desc;
++
++    rail->r_flushop.op_function = ep4_neterr_dma_flushop;
++    rail->r_flushop.op_arg      = &desc;
++    
++    elan4_queue_haltop (rail->r_ctxt.ctxt_dev, &rail->r_haltop);
++
++    spin_lock_irqsave (&rail->r_haltop_lock, flags);
++    while (! desc.done)
++      kcondvar_wait (&rail->r_haltop_sleep, &rail->r_haltop_lock, &flags);
++    spin_unlock_irqrestore (&rail->r_haltop_lock, flags);
++    kmutex_unlock (&rail->r_haltop_mutex);
++
++    /* Fourth - run down the dma retry lists and move all entries to the cancelled
++     *          list.  Any dma's which were on the run queues have already been
++     *          moved there */
++    spin_lock_irqsave (&rail->r_dma_lock, flags);
++    for (i = EP_RETRY_BASE; i < EP_NUM_RETRIES; i++)
++    {
++      list_for_each_safe (el,nel, &rail->r_dma_retrylist[i]) {
++          EP4_DMA_RETRY *retry    = list_entry (el, EP4_DMA_RETRY, retry_link);
++          
++          if (dma_matches_cookie (rail, retry->retry_dma.dma_vproc, retry->retry_dma.dma_cookie, nodeId, cookies, "retry"))
++          {
++              /* remove from retry list */
++              list_del (&retry->retry_link);
++              
++              /* add to list of dmas which matched */
++              list_add_tail (&retry->retry_link, &matchedList);
++          }
++      }
++    }
++    
++    list_for_each_safe (el, nel, &nodeRail->StalledDmas) {
++      EP4_DMA_RETRY *retry = list_entry (el, EP4_DMA_RETRY, retry_link);
++      
++      if (dma_matches_cookie (rail, retry->retry_dma.dma_vproc, retry->retry_dma.dma_cookie, nodeId, cookies, "stalled"))
++      {
++          /* remove from retry list */
++          list_del (&retry->retry_link);
++          
++          /* add to list of dmas which matched */
++          list_add_tail (&retry->retry_link, &matchedList);
++      }
++    }
++    spin_unlock_irqrestore (&rail->r_dma_lock, flags);
++    
++    /* Now "set" the source event of any put DMA#'s we can use the dma 
++     * retry command queue as the retry thread is stalled */
++    while (! list_empty (&matchedList))
++    {
++      EP4_DMA_RETRY *retry = list_entry (matchedList.next, EP4_DMA_RETRY, retry_link);
++      
++      list_del (&retry->retry_link);
++
++      elan4_set_event_cmd (rail->r_dma_ecq->ecq_cq, retry->retry_dma.dma_srcEvent);
++
++      spin_lock_irqsave (&rail->r_dma_lock, flags);
++      list_add (&retry->retry_link, &rail->r_dma_freelist);
++      spin_unlock_irqrestore (&rail->r_dma_lock, flags);
++    }
++
++    /* Flush through the command queues to ensure that all the setevents have executed */
++    ep4_flush_ecqs (rail);
++
++    /* Finally - allow the retry thread to run again */
++    ep_kthread_resume (&rail->r_retry_thread);
++}
++
++void
++ep4_add_neterr_ops (EP4_RAIL *rail, EP4_NETERR_OPS *ops)
++{
++    /* we're called from the ManagerThread, so no need to stall it */
++    list_add_tail (&ops->op_link, &rail->r_neterr_ops);
++}
++void
++ep4_remove_neterr_ops (EP4_RAIL *rail, EP4_NETERR_OPS *ops)
++{
++    EP_SYS *sys = rail->r_generic.System;
++
++    ep_kthread_stall (&sys->ManagerThread);
++    list_del (&ops->op_link);
++    ep_kthread_resume (&sys->ManagerThread);
++}
++
++void
++ep4_neterr_fixup_sten (EP4_RAIL *rail, unsigned int nodeId, EP_NETERR_COOKIE *cookies)
++{
++    struct list_head *el;
++
++    list_for_each (el, &rail->r_neterr_ops) {
++      EP4_NETERR_OPS *op = list_entry (el, EP4_NETERR_OPS, op_link);
++
++      (op->op_func) (rail, op->op_arg, nodeId, cookies);
++    }
++}
++
++void
++ep4_neterr_fixup (EP_RAIL *r, unsigned int nodeId, EP_NETERR_COOKIE *cookies)
++{
++    EP4_RAIL *rail = (EP4_RAIL *) r;
++
++    /* network error cookies can come from the following :
++     *
++     *   DMA  engine
++     *     if a DMA matches a network error cookie, then we just need to 
++     *     execute the local setevent *before* returning.
++     *
++     *   STEN packet
++     *     if the STEN packet was generated with as a WAIT_FOR_EOP
++     *     and it's not present on the retry lists, then re-create
++     *     it.
++     *
++     */
++    EPRINTF4 (DBG_NETWORK_ERROR, "%s: ep4_neterr_fixup: node %d cookies <%lld%s%s%s%s> <%lld%s%s%s%s>\n",
++            rail->r_generic.Name, nodeId, EP4_COOKIE_STRING(cookies[0]), EP4_COOKIE_STRING(cookies[1]));
++
++    if ((cookies[0] & EP4_COOKIE_DMA) || (cookies[1] & EP4_COOKIE_DMA))
++      ep4_neterr_fixup_dmas (rail, nodeId, cookies);
++
++    if ((cookies[0] & EP4_COOKIE_STEN) || (cookies[1] & EP4_COOKIE_STEN))
++      ep4_neterr_fixup_sten (rail, nodeId, cookies);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++
+Index: linux-2.4.21/drivers/net/qsnet/ep/nmh.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/nmh.c       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/nmh.c    2005-06-01 23:12:54.676427400 -0400
+@@ -0,0 +1,181 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++#ident "@(#)$Id: nmh.c,v 1.6 2004/01/05 13:48:08 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/nmh.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++
++#define EP_NMD_SPANS(nmd, base, top)  ((nmd)->nmd_addr <= (base) &&  \
++                                       ((nmd)->nmd_addr + (nmd)->nmd_len - 1) >= (top))
++
++#define EP_NMD_OVERLAPS(nmd, addr, len)       ((nmd)->nmd_addr <= ((addr) + (len)) && \
++                                       ((nmd)->nmd_addr + (nmd)->nmd_len - 1) >= (addr))
++
++#define EP_NMH_HASH(tbl,idx,addr)     ((addr) % (tbl)->tbl_size[idx])
++
++int
++ep_nmh_init (EP_NMH_TABLE *tbl)
++{
++    int i, idx, hsize = 1;
++
++    for (idx = EP_NMH_NUMHASH-1; idx >= 0; idx--, hsize <<= 1)
++    {
++      tbl->tbl_size[idx] = (hsize < EP_NMH_HASHSIZE) ? hsize : EP_NMH_HASHSIZE;
++
++      KMEM_ZALLOC (tbl->tbl_hash[idx], struct list_head *, sizeof (struct list_head) * tbl->tbl_size[idx], 1);
++      
++      if (tbl->tbl_hash == NULL)
++      {
++          while (++idx < EP_NMH_NUMHASH)
++              KMEM_FREE (tbl->tbl_hash[idx], sizeof (struct list_head) * tbl->tbl_size[idx]);
++          return (ENOMEM);
++      }
++
++      for (i = 0; i < tbl->tbl_size[idx]; i++)
++          INIT_LIST_HEAD (&tbl->tbl_hash[idx][i]);
++    }
++
++    return (0);
++}
++
++void
++ep_nmh_fini (EP_NMH_TABLE *tbl)
++{
++    int idx;
++
++    for (idx = 0; idx < EP_NMH_NUMHASH; idx++)
++      if (tbl->tbl_hash[idx])
++          KMEM_FREE (tbl->tbl_hash[idx], sizeof (struct list_head) * tbl->tbl_size[idx]);
++    
++    bzero (tbl, sizeof (EP_NMH_TABLE));
++}
++
++void
++ep_nmh_insert (EP_NMH_TABLE *tbl, EP_NMH *nmh)
++{
++    EP_ADDR base = nmh->nmh_nmd.nmd_addr;
++    EP_ADDR top  = base + nmh->nmh_nmd.nmd_len - 1;
++    int     idx;
++
++    for (idx = 0, base >>= 12, top >>= 12; base != top && idx < EP_NMH_NUMHASH; idx++, base >>= 1, top >>= 1)
++      ;
++
++    list_add_tail (&nmh->nmh_link, &tbl->tbl_hash[idx][EP_NMH_HASH(tbl, idx, base)]);
++}
++
++void
++ep_nmh_remove (EP_NMH_TABLE *tbl, EP_NMH *nmh)
++{
++    list_del (&nmh->nmh_link);
++}
++
++EP_NMH *
++ep_nmh_find (EP_NMH_TABLE *tbl, EP_NMD *nmd)
++{
++    EP_ADDR           base = nmd->nmd_addr;
++    EP_ADDR           top  = base + nmd->nmd_len - 1;
++    int               idx;
++    struct list_head *le;
++    
++    for (idx = 0, base >>= 12, top >>= 12; base != top && idx < EP_NMH_NUMHASH; idx++, base >>= 1, top >>= 1)
++      ;
++    
++    for (; idx < EP_NMH_NUMHASH; idx++, base >>= 1, top >>= 1) {
++
++      list_for_each (le, &tbl->tbl_hash[idx][EP_NMH_HASH(tbl, idx, base)]) {
++          EP_NMH *nmh = list_entry (le, EP_NMH, nmh_link);
++
++          if (EP_NMD_SPANS (&nmh->nmh_nmd, nmd->nmd_addr, nmd->nmd_addr + nmd->nmd_len - 1))
++              return (nmh);
++      }
++    }
++
++    return (0);
++}
++
++void
++ep_nmd_subset (EP_NMD *subset, EP_NMD *nmd, unsigned off, unsigned len)
++{
++    ASSERT ((off + len - 1) <= nmd->nmd_len);
++
++    subset->nmd_addr = nmd->nmd_addr + off;
++    subset->nmd_len  = len;
++    subset->nmd_attr = nmd->nmd_attr;
++}
++
++int
++ep_nmd_merge (EP_NMD *merged, EP_NMD *a, EP_NMD *b)
++{
++    if (EP_NMD_NODEID (a) != EP_NMD_NODEID (b))                       /* not generated on the same node */
++      return 0;
++    
++    if ((EP_NMD_RAILMASK (a) & EP_NMD_RAILMASK (b)) == 0)     /* no common rails */
++      return 0;
++    
++    if (b->nmd_addr == (a->nmd_addr + a->nmd_len))
++    {
++      if (merged != NULL)
++      {
++          merged->nmd_addr = a->nmd_addr;
++          merged->nmd_len  = a->nmd_len + b->nmd_len;
++          merged->nmd_attr = EP_NMD_ATTR(EP_NMD_NODEID(a), EP_NMD_RAILMASK(a) & EP_NMD_RAILMASK(b));
++      }
++      return 1;
++    }
++    
++    if (a->nmd_addr == (b->nmd_addr + b->nmd_len))
++    {
++      if (merged != NULL)
++      {
++          merged->nmd_addr = b->nmd_addr;
++          merged->nmd_len  = b->nmd_len + a->nmd_len;
++          merged->nmd_attr = EP_NMD_ATTR(EP_NMD_NODEID(b), EP_NMD_RAILMASK(a) & EP_NMD_RAILMASK(b));
++      }
++      
++      return 1;
++    }
++
++    return 0;
++}
++
++int
++ep_nmd_map_rails (EP_SYS *sys, EP_NMD *nmd, unsigned railmask)
++{
++    EP_NMH *nmh = ep_nmh_find (&sys->MappingTable, nmd);
++
++    if (nmh == NULL)
++    {
++      printk ("ep_nmd_map_rails: nmd=%08x.%08x.%08x cannot be found\n",
++              nmd->nmd_addr, nmd->nmd_len, nmd->nmd_attr);
++      return (-1);
++    }
++
++    return (nmh->nmh_ops->op_map_rails (sys, nmh, nmd, railmask));
++}
++
++EP_RAILMASK
++ep_nmd2railmask (EP_NMD *frags, int nFrags)
++{
++    EP_RAILMASK mask;
++
++    if (nFrags == 0)
++      return ((EP_RAILMASK)-1);
++    
++    for (mask = EP_NMD_RAILMASK(frags); --nFrags; )
++      mask &= EP_NMD_RAILMASK(++frags);
++
++    return (mask);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/ep/probenetwork.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/probenetwork.c      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/probenetwork.c   2005-06-01 23:12:54.677427248 -0400
+@@ -0,0 +1,446 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: probenetwork.c,v 1.43 2004/04/19 15:43:15 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/probenetwork.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++#include "debug.h"
++
++int PositionCheck = 1;
++
++#define NUM_DOWN_FROM_VAL(NumDownLinksVal, level)     (((NumDownLinksVal) >> ((level) << 2)) & 0xF)
++
++int
++ProbeNetwork (EP_RAIL *rail, ELAN_POSITION *pos)
++{
++    int               lvl, i;
++    int               level;
++    int               nodeid;
++    int               numnodes;
++    int                     randomRoutingDisabled;
++    int               sw;
++    int               nacks;
++    int               nowayup;
++    int                     nalias;
++    int                     upmask;
++    int                     partial;
++    int                     link;
++    int                     invalid;
++    int                     linkdown[ELAN_MAX_LEVELS];
++    int                     linkup[ELAN_MAX_LEVELS];
++    EP_SWITCH        *switches[ELAN_MAX_LEVELS];
++    int               switchCount[ELAN_MAX_LEVELS+1];
++    int               lowestBcast;
++    int               numUpLinks[ELAN_MAX_LEVELS];
++    int               routedown [ELAN_MAX_LEVELS];
++
++    EPRINTF1 (DBG_PROBE, "%s: ProbeNetwork started\n", rail->Name);
++
++    switchCount[0] = 1;
++    numUpLinks [0] = 4;
++
++    for (level = 0; level < ELAN_MAX_LEVELS; level++)
++    {
++      int ndown  = NUM_DOWN_FROM_VAL (rail->Devinfo.dev_num_down_links_value, level);
++
++      KMEM_ZALLOC (switches[level], EP_SWITCH *, sizeof (EP_SWITCH) * switchCount[level], 1);
++
++      for (sw = 0, nacks = 0, nowayup = 0, lowestBcast=7; sw < switchCount[level]; sw++)
++      {
++          EP_SWITCH *lsw  = &switches[level][sw];
++          int        good = 1;
++          int        tsw;
++
++          for (nodeid = 0,tsw = sw, lvl = level-1 ; lvl >= 0 ; lvl--)
++          {
++              EP_SWITCH *lsw;
++              int        link = (8-numUpLinks[lvl]) + (tsw % numUpLinks[lvl]);
++
++              tsw  = tsw / numUpLinks[lvl];
++              lsw  = &switches[lvl][tsw];
++
++              if (lsw->present == 0 || (lsw->lnr & (1 << link)))
++              {
++                  EPRINTF4 (DBG_PROBE, "lvl %d sw %d present=%d lnr=%x\n", lvl, sw, lsw->present, lsw->lnr);
++                  good = 0;
++              }
++              
++              linkup[lvl]   = link;
++              linkdown[lvl] = lsw->link;
++
++              if ( lvl ) nodeid = ((nodeid + linkdown[lvl]) * (8-numUpLinks[lvl-1]));
++              else       nodeid += linkdown[0];
++
++          }
++          
++          /* 
++           * don't bother probing routes which we we've already seen are unreachable 
++           * because a link upwards was in reset or the switch previously nacked us.
++           */
++          if (! good)
++          {
++              lsw->present = 0;
++
++              nacks++;
++              nowayup++;
++
++              continue;
++          }
++
++          lsw->present = rail->Operations.ProbeRoute (rail, level, sw, nodeid, linkup, linkdown, 5, lsw);
++
++          if (! lsw->present)
++          {
++              EPRINTF3 (DBG_PROBE, "%s: level %d switch %d - unexpected nack\n", rail->Name, level, sw);
++
++              nacks++;
++              nowayup++;
++          }
++          else
++          {
++              EPRINTF5 (DBG_PROBE, "%s: level %d switch %d - link %d bcast %d\n", rail->Name, level, sw, lsw->link, lsw->bcast);
++
++              if (level == 2 && rail->Devinfo.dev_device_id == PCI_DEVICE_ID_ELAN3)
++              {
++                  /* If we see broadcast top as 7, and we came in on a low link, then we can't
++                   * determine whether we're in a 128 way or a un-configured 64u64d switch, so
++                   * we treat it as a 64u64d and detect the 128 way case by "going over the top" 
++                   * below. Unless we've been told what it really is by NumDownLinksVal.
++                   */
++                  if (lsw->bcast == 7 && lsw->link < 4)
++                      lsw->bcast = ndown ? (ndown - 1) : 3;
++              }
++
++              if ( lowestBcast > lsw->bcast ) 
++                  lowestBcast = lsw->bcast;
++
++              if (lsw->link > (ndown ? (ndown-1) : (lowestBcast == 7 ? 3 : lowestBcast)))
++              {
++                  /* We've arrived on a "up-link" - this could be either
++                   * we're in the top half of a x8 top-switch - or we're
++                   * in the bottom half and have gone "over the top". We
++                   * differentiate these cases since the switches below
++                   * a x8 top-switch will have broadcast top set to 3, 
++                   * and the x8 topswitch have broadcast top set to 7.
++                   */
++                  if (lsw->bcast == 7)
++                      nowayup++;
++                  else
++                  {
++                      EPRINTF2 (DBG_PROBE, "%s: level %d - gone over the top\n",
++                                rail->Name, level);
++
++                      if (level > 0)
++                      {
++                          KMEM_FREE (switches[level], sizeof (EP_SWITCH) * switchCount[level] );
++                          level--;
++                      }
++                      
++                      numUpLinks[level] = 0;
++                      goto finished;
++                  }
++              }
++
++          }
++      }
++
++      numUpLinks[level]    = ndown ? (8 - ndown) : (7 - lowestBcast);
++      switchCount[level+1] = switchCount[level] *  numUpLinks[level];
++      
++      /* Now we know which links are uplinks, we can see whether there is
++       * any possible ways up */
++      upmask = (ndown ? (0xFF << ndown) & 0xFF : (0xFF << (8 - numUpLinks[level])) & 0xFF);
++
++      for (sw = 0; sw < switchCount[level]; sw++)
++      {
++          EP_SWITCH *lsw  = &switches[level][sw];
++
++          if (lsw->present && lsw->link <= (ndown ? (ndown-1) : (lowestBcast == 7 ? 3 : lowestBcast)) && (switches[level][sw].lnr & upmask) == upmask)
++              nowayup++;
++      }
++
++      EPRINTF7 (DBG_PROBE, "%s: level %d - sw=%d nacks=%d nowayup=%d bcast=%d numup=%d\n", 
++                rail->Name, level, sw, nacks, nowayup, lowestBcast, numUpLinks[level]);
++
++      if (nacks == sw)
++      {
++          static bitmap_t printed[BT_BITOUL(EP_MAX_RAILS)];
++
++          if (! BT_TEST (printed, rail->Number))
++              printk ("%s: cannot determine network position\n", rail->Name);
++          BT_SET (printed, rail->Number);
++          goto failed;
++      }
++
++      if (nowayup == sw)
++          goto finished;
++    }
++    
++    printk ("%s: exceeded number of levels\n", rail->Name);
++    level = ELAN_MAX_LEVELS - 1;
++
++ failed:
++    
++    for (lvl = 0; lvl <= level; lvl++)
++      KMEM_FREE (switches[lvl], sizeof (EP_SWITCH) * switchCount[lvl] );
++
++    return -EAGAIN;
++
++ finished:
++    /* we've successfully probed the network - now calculate our node 
++     * positon and what level of random routing is possible */
++    nalias = 1;
++    for (lvl = 0, invalid = 0, partial = 0, randomRoutingDisabled = 0; lvl <= level; lvl++)
++    {
++      int ndown  = NUM_DOWN_FROM_VAL (rail->Devinfo.dev_num_down_links_value, lvl);
++      int upmask = ndown ? (0xFF << ndown) & 0xFF : 0xF0;
++
++      for (sw = 0, nalias = 0; sw < switchCount[lvl]; sw++)
++      {
++          EP_SWITCH *lsw = &switches[lvl][sw];
++          
++          /* You can only use adaptive routing if links 4-7 are uplinks, and at least one of them is
++           * not in reset.   Otherwise you can randomly select an "uplink" if all the uplinks are not
++           * in reset. */
++          if (lsw->present && ((upmask == 0xF0) ? (lsw->lnr & upmask) == upmask : (lsw->lnr & upmask) != 0))
++              randomRoutingDisabled |= (1 << lvl);
++          
++          if (!lsw->present)
++              partial++;
++          else
++          {
++              if (lsw->invalid)
++              {
++                  printk ("%s: invalid switch detected (level %d switch %d)\n", rail->Name, lvl, sw);
++                  invalid++;
++              }
++              
++              for (i = 0; i < nalias; i++)
++                  if (linkdown[i] == lsw->link)
++                      break;
++              if (i == nalias)
++                  linkdown[nalias++] = lsw->link;
++          }
++      }
++      
++      link = linkdown[0];
++      for (i = 1; i < nalias; i++)
++          if (linkdown[i] < link)
++              link = linkdown[i];
++
++      if (nalias > 1 && lvl != level)
++      {
++          printk ("%s: switch aliased below top level (level %d)\n", rail->Name, lvl);
++          invalid++;
++      }
++      
++      routedown[lvl] = link;
++   }
++
++    for (lvl = 0; lvl <= level; lvl++) 
++      KMEM_FREE (switches[lvl], sizeof (EP_SWITCH) * switchCount[lvl] );
++
++    if (invalid)
++    {
++      printk ("%s: invalid switch configuration\n", rail->Name);
++      return (EINVAL);
++    }
++
++    /* Handle the aliasing case where a 16 way is used as multiple smaller switches */
++    if (nalias == 1)
++      level++;
++    else if (nalias == 2)                                     /* a 16 way as 2x8 ways */
++      numUpLinks[level++] = 6;                                /*   only 2 down links */
++    else if (nalias > 4)                                      /* a 16 way as 8x2 ways */
++      numUpLinks[level-1] = 6;
++    
++    /* 
++     * Compute my nodeid and number of nodes in the machine
++     * from the routedown and the number of downlinks at each level.
++     */
++    for(nodeid=0, lvl = level - 1; lvl >= 0; lvl--)
++    {
++      if (lvl) nodeid = ((nodeid + routedown[lvl]) * (8-numUpLinks[lvl-1]));  
++      else     nodeid += routedown[0];
++    }
++
++    for (numnodes = 1, lvl = 0; lvl < level; lvl++)
++      numnodes *= (8 - numUpLinks[lvl]);
++
++    sprintf (rail->Name, "ep%d[%d]", rail->Number, nodeid);
++
++    if (randomRoutingDisabled & ((1 << (level-1))-1))
++      printk ("%s: nodeid=%d level=%d numnodes=%d (random routing disabled 0x%x)\n", 
++              rail->Name, nodeid, level, numnodes, randomRoutingDisabled);
++    else if (partial)
++      printk ("%s: nodeid=%d level=%d numnodes=%d (random routing ok)\n",
++              rail->Name, nodeid, level, numnodes);
++    else
++      printk ("%s: nodeid=%d level=%d numnodes=%d\n",
++              rail->Name, nodeid, level, numnodes);
++
++    pos->pos_mode              = ELAN_POS_MODE_SWITCHED;
++    pos->pos_nodeid              = nodeid;
++    pos->pos_levels              = level;
++    pos->pos_nodes               = numnodes;
++    pos->pos_random_disabled     = randomRoutingDisabled;
++
++    for(lvl = 0; lvl < level; lvl++)
++      pos->pos_arity[level -lvl - 1] = (8-numUpLinks[lvl]);
++    pos->pos_arity[level] = 1;                                /* XXXX why does this need to be 1 ? */
++    
++    return 0;
++}
++
++/*
++ * broadcast top is invalid if it is not set to the number of downlinks-1,
++ * or at the topmost level it is less than ndown-1.
++ */
++#define BCAST_TOP_INVALID(lvl, bcast, ndown)  ((lvl) == 0 ? (bcast) < ((ndown)-1) : (bcast) != ((ndown) - 1))
++
++void
++CheckPosition (EP_RAIL *rail)
++{
++    ELAN_POSITION *pos     = &rail->Position;
++    unsigned int   nodeid  = pos->pos_nodeid;
++    unsigned int   invalid = 0;
++    unsigned int   changed = 0;
++    int lvl, slvl;
++
++    if (! PositionCheck)
++      return;
++
++    if (rail->Operations.CheckPosition(rail))         /* is update ready for this rail */
++    {
++      EPRINTF2 (DBG_ROUTETABLE, "%s: check position: SwitchProbeLevel=%d\n", rail->Name, rail->SwitchProbeLevel);
++
++      for (lvl = 0, slvl = pos->pos_levels-1; lvl <= rail->SwitchProbeLevel; lvl++, slvl--)
++      {
++          EP_SWITCHSTATE *state  = &rail->SwitchState[lvl];
++          EP_SWITCHSTATE *lstate = &rail->SwitchLast[lvl];
++          unsigned int    ndown  = pos->pos_arity[slvl];
++          unsigned int    upmask = (0xFF << ndown) & 0xFF;
++          unsigned int    mylink = nodeid % ndown;
++          unsigned int    error  = 0;
++          unsigned int    binval = 0;
++
++          nodeid /= ndown;
++
++          /*
++           * broadcast top is invalid if it is not set to the number of downlinks-1,
++           * or at the topmost level it is less than ndown-1.
++           */
++          if (BCAST_TOP_INVALID(lvl, state->bcast, ndown) || (state->LNR & upmask) == upmask)
++          {
++              /* no way up from here - we'd better be at the top */
++              if (lvl != (pos->pos_levels-1))
++              {
++                  if (state->bcast != (ndown-1))
++                      printk ("%s: invalid broadcast top %d at level %d\n", rail->Name, state->bcast, lvl);
++                  else if ((state->LNR & upmask) == upmask && (lstate->LNR & upmask) == upmask)
++                      printk ("%s: no way up to switch at level %d (turned off ?)\n", rail->Name, lvl+1);
++              }
++              else
++              {
++                  if (state->linkid != mylink)
++                      printk ("%s: moved at top level was connected to link %d now connected to %d\n", rail->Name, mylink, state->linkid);
++              }
++
++              if (state->linkid != mylink)
++                  error++;
++              
++              if (BCAST_TOP_INVALID (lvl, state->bcast, ndown))
++                  binval++;
++          }
++          else
++          {
++              if (state->linkid != mylink)
++              {
++                  if (state->linkid != rail->SwitchLast[lvl].linkid)
++                      printk ("%s: moved at lvl %d was connected to link %d now connected to %d\n", rail->Name, lvl, mylink, state->linkid);
++                      
++                  error++;
++              }
++          }
++
++          if (error == 0 && invalid == 0)
++              rail->SwitchProbeTick[lvl] = lbolt;
++          
++          EPRINTF10 (DBG_ROUTETABLE, "%s:   lvl=%d (slvl=%d) linkid=%d bcast=%d lnr=%02x uplink=%d : error=%d binval=%d invalid=%d\n", 
++                     rail->Name, lvl, slvl, state->linkid, state->bcast, state->LNR, state->uplink, error, binval, invalid);
++
++          invalid |= (error | binval);
++      }
++      
++      for (lvl = 0; lvl < rail->SwitchProbeLevel; lvl++)
++          if (rail->SwitchState[lvl].uplink != rail->SwitchLast[lvl].uplink)
++              changed++;
++
++      if (changed)
++      {
++          printk ("%s: broadcast tree has changed from", rail->Name);
++          for (lvl = 0; lvl < rail->SwitchProbeLevel; lvl++)
++              printk ("%c%d", lvl == 0 ? ' ' : ',', rail->SwitchLast[lvl].uplink);
++
++          for (lvl = 0; lvl < rail->SwitchProbeLevel; lvl++)
++              printk ("%s%d", lvl == 0 ? " to " : ",", rail->SwitchState[lvl].uplink);
++          printk ("\n");
++      }
++
++      if (rail->SwitchProbeLevel > 0)
++          bcopy (rail->SwitchState, rail->SwitchLast, rail->SwitchProbeLevel * sizeof (EP_SWITCHSTATE));
++    }
++
++    for (lvl = 0; lvl < pos->pos_levels; lvl++)
++    {
++      EPRINTF4 (DBG_ROUTETABLE, "%s: level %d lbolt=%lx ProbeLevelTick=%lx\n",
++                rail->Name, lvl, lbolt, rail->SwitchProbeTick[lvl]);
++      
++      if (AFTER (lbolt, rail->SwitchProbeTick[lvl] + EP_POSITION_TIMEOUT))
++      {
++          if (lvl < rail->SwitchBroadcastLevel+1)
++          {
++              if (lvl == 0)
++                  printk ("%s: cable disconnected\n", rail->Name);
++              else
++                  printk ("%s: broadcast level has dropped to %d (should be %d)\n",
++                          rail->Name, lvl, rail->Position.pos_levels);
++          }
++          break;
++      }
++    }
++    
++    if (lvl > rail->SwitchBroadcastLevel+1)
++    {
++      if (rail->SwitchBroadcastLevel < 0)
++          printk ("%s: cable reconnected\n", rail->Name);
++      if (lvl == rail->Position.pos_levels)
++          printk ("%s: broadcast level has recovered\n", rail->Name);
++      else
++          printk ("%s: broadcast level has recovered to %d (should be %d)\n", 
++                  rail->Name, lvl, rail->Position.pos_levels);
++    }
++    
++    if (rail->SwitchBroadcastLevel != (lvl - 1))
++    {
++      EPRINTF2 (DBG_ROUTETABLE, "%s: setting SwitchBroadcastLevel to %d\n", rail->Name, lvl-1);
++      
++      rail->SwitchBroadcastLevel     = lvl - 1;
++      rail->SwitchBroadcastLevelTick = lbolt;
++    }
++}
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/ep/probenetwork_elan3.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/probenetwork_elan3.c        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/probenetwork_elan3.c     2005-06-01 23:12:54.677427248 -0400
+@@ -0,0 +1,298 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: probenetwork_elan3.c,v 1.40 2004/04/15 12:30:08 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/probenetwork_elan3.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++
++#include "kcomm_vp.h"
++#include "kcomm_elan3.h"
++#include "debug.h"
++
++#include <elan3/intrinsics.h>
++
++static void ep3_probe_event (EP3_RAIL *rail, void *arg);
++static EP3_COOKIE_OPS ep3_probe_ops = 
++{
++    ep3_probe_event
++} ;
++
++int
++ep3_init_probenetwork (EP3_RAIL *rail)
++{
++    sdramaddr_t             stack;
++    E3_Addr           sp;
++    E3_BlockCopyEvent event;
++    int               i;
++
++    if (! (stack = ep_alloc_elan (&rail->Generic, EP3_STACK_SIZE, 0, &rail->ProbeStack)))
++      return -ENOMEM;
++
++    spin_lock_init (&rail->ProbeLock);
++    kcondvar_init (&rail->ProbeWait);
++
++    /* Initialise the probe command structure */
++    for (i = 0; i < TR_TRACEROUTE_ENTRIES; i++)
++      elan3_sdram_writew (rail->Device, rail->RailElan + offsetof (EP3_RAIL_ELAN, ProbeSource0[i]), 0);
++    for (i = 0; i < TR_TRACEROUTE_ENTRIES; i++)
++      elan3_sdram_writew (rail->Device, rail->RailElan + offsetof (EP3_RAIL_ELAN, ProbeSource1[i]), 1);
++    
++    RegisterCookie (&rail->CookieTable, &rail->ProbeCookie, rail->RailElanAddr + offsetof (EP3_RAIL_ELAN, ProbeDone), &ep3_probe_ops, rail);
++    
++    elan3_sdram_writel (rail->Device, rail->RailElan + offsetof (EP3_RAIL_ELAN, ProbeStart.ev_Type), 0);
++    elan3_sdram_writel (rail->Device, rail->RailElan + offsetof (EP3_RAIL_ELAN, ProbeStart.ev_Count), 0);
++
++    EP3_INIT_COPY_EVENT (event, rail->ProbeCookie, rail->RailMainAddr + offsetof (EP3_RAIL_MAIN, ProbeDone), 1);
++    elan3_sdram_copyl_to_sdram (rail->Device, &event, rail->RailElan + offsetof (EP3_RAIL_ELAN, ProbeDone), sizeof (E3_BlockCopyEvent));
++
++    rail->RailMain->ProbeDone = EP3_EVENT_FREE;
++
++    sp = ep3_init_thread (rail->Device, ep_symbol (&rail->ThreadCode, "kcomm_probe"),
++                        rail->ProbeStack, stack, EP3_STACK_SIZE,
++                        3, rail->CommandPortAddr, rail->RailElanAddr, rail->RailMainAddr);
++    
++    IssueRunThread (rail, sp);
++
++    return 0;
++}
++
++void
++ep3_destroy_probenetwork (EP3_RAIL *rail)
++{
++    if (rail->ProbeStack == (sdramaddr_t) 0)
++      return;
++
++    /* XXXX: ensure that the network probe thread is stopped */
++
++    DeregisterCookie (&rail->CookieTable, &rail->ProbeCookie);
++
++    kcondvar_destroy (&rail->ProbeWait);
++    spin_lock_destroy (&rail->ProbeLock);
++    
++    ep_free_elan (&rail->Generic, rail->ProbeStack, EP3_STACK_SIZE);
++}
++
++static void
++ep3_probe_event (EP3_RAIL *rail, void *arg)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&rail->ProbeLock, flags);
++    rail->ProbeDone = 1;
++    kcondvar_wakeupone (&rail->ProbeWait, &rail->ProbeLock);
++    spin_unlock_irqrestore (&rail->ProbeLock, flags);
++}
++
++int
++ep3_probe_route (EP_RAIL *r, int level, int sw, int nodeid, int *linkup, int *linkdown, int attempts, EP_SWITCH *lsw)
++{
++    EP3_RAIL      *rail     = (EP3_RAIL *) r;
++    EP3_RAIL_MAIN *railMain = rail->RailMain;
++    sdramaddr_t    railElan = rail->RailElan;
++    E3_uint16      flits[MAX_FLITS];
++    E3_uint32      result;
++    int                  nflits;
++    unsigned long  flags;
++
++    spin_lock_irqsave (&rail->ProbeLock, flags);
++
++    nflits = GenerateProbeRoute ( flits, nodeid, level, linkup, linkdown, 0);
++          
++    if (LoadRoute (rail->Device, rail->RouteTable, EP_VP_PROBE(level), ELAN3_MRF_CONTEXT_NUM|SYS_CONTEXT_BIT, nflits, flits) != 0)
++    {
++      EPRINTF0 (DBG_ROUTETABLE, "ProbeRoute: cannot load route entry\n");
++      spin_unlock_irqrestore (&rail->ProbeLock, flags);
++      return (EINVAL);
++    }
++
++    do {
++      /* Initialise the probe source to include our partially computed nodeid */
++      elan3_sdram_writew (rail->Device, railElan + offsetof (EP3_RAIL_ELAN, ProbeSource0[TR_TRACEROUTE_ENTRIES-1]), nodeid);
++      elan3_sdram_writew (rail->Device, railElan + offsetof (EP3_RAIL_ELAN, ProbeSource1[TR_TRACEROUTE_ENTRIES-1]), nodeid);
++
++      /* Initialise the count result etc */
++      elan3_sdram_writel (rail->Device, railElan + offsetof (EP3_RAIL_ELAN, ProbeType), PROBE_SINGLE);
++      elan3_sdram_writel (rail->Device, railElan + offsetof (EP3_RAIL_ELAN, ProbeLevel), level);
++
++      railMain->ProbeResult  = -1;
++          
++      /* Clear the receive area */
++      bzero (railMain->ProbeDest0, sizeof (railMain->ProbeDest0));
++      bzero (railMain->ProbeDest1, sizeof (railMain->ProbeDest1));
++    
++      /* Re-arm the completion event */
++      elan3_sdram_writel (rail->Device, railElan + offsetof (EP3_RAIL_ELAN, ProbeDone.ev_Count), 1);
++      railMain->ProbeDone = EP3_EVENT_ACTIVE;
++      rail->ProbeDone = 0;
++
++      /* And wakeup the thread to do the probe */
++      IssueSetevent (rail, rail->RailElanAddr + offsetof (EP3_RAIL_ELAN, ProbeStart));
++
++      /* Now wait for it to complete */
++      while (! rail->ProbeDone)
++          kcondvar_wait (&rail->ProbeWait, &rail->ProbeLock, &flags);
++
++      /* wait for block copy event to flush write buffers */
++      while (! EP3_EVENT_FIRED (rail->ProbeCookie, railMain->ProbeDone))
++          if (! EP3_EVENT_FIRING(rail->Device, railElan + offsetof (EP3_RAIL_ELAN, ProbeDone), rail->ProbeCookie, railMain->ProbeDone))
++              panic ("ProbeRoute: network probe event failure\n");
++
++      result = railMain->ProbeResult;
++
++      if (result == C_ACK_ERROR)
++          kcondvar_timedwait (&rail->ProbeWait, &rail->ProbeLock, &flags, lbolt + (hz/8));
++      
++      railMain->ProbeDone = EP3_EVENT_FREE;
++
++    } while (result != C_ACK_OK && --attempts);
++
++    if (result == C_ACK_OK)
++    {
++      if (railMain->ProbeDest0[TR_TRACEROUTE_ENTRIES - ((2*level)+1) - 1] != nodeid ||
++          railMain->ProbeDest1[TR_TRACEROUTE_ENTRIES - ((2*level)+1) - 1] != nodeid)
++      {
++          printk ("%s: lost nodeid at level %d switch %d - %d != %d\n", rail->Generic.Name, level, sw,
++                  railMain->ProbeDest0[TR_TRACEROUTE_ENTRIES - ((2*level)+1) - 1], nodeid);
++
++          result = C_ACK_ERROR;
++      }
++      else
++      {
++          E3_uint16 val0 = railMain->ProbeDest0[TR_TRACEROUTE_ENTRIES - level - 1];
++          E3_uint16 val1 = railMain->ProbeDest1[TR_TRACEROUTE_ENTRIES - level - 1];
++              
++          EPRINTF7 (DBG_PROBE, "%s: level %d switch %d - linkid=%d bcast=%d LNR=%02x%s\n", 
++                    rail->Generic.Name, level, sw, TR_TRACEROUTE0_LINKID(val0),
++                    TR_TRACEROUTE1_BCAST_TOP(val1), TR_TRACEROUTE0_LNR(val0),
++                    TR_TRACEROUTE0_REVID(val0) ? "" : " RevA Part");
++          
++          lsw->lnr     = TR_TRACEROUTE0_LNR(val0);
++          lsw->link    = TR_TRACEROUTE0_LINKID(val0);
++          lsw->bcast   = TR_TRACEROUTE1_BCAST_TOP(val1);
++          lsw->invalid = (TR_TRACEROUTE0_REVID(val0) == 0);
++      }
++    }
++    spin_unlock_irqrestore (&rail->ProbeLock, flags);
++    
++    return (result == C_ACK_OK);
++}
++
++void
++ep3_probe_position_found (EP3_RAIL *rail, ELAN_POSITION *pos)
++{
++    E3_uint16  flits[MAX_FLITS];
++    int        lvl, nflits;
++    
++    for (lvl = 0; lvl < pos->pos_levels; lvl++)
++    {
++      nflits = GenerateCheckRoute (pos, flits, pos->pos_levels - lvl - 1, 0);
++
++      if (LoadRoute (rail->Device, rail->Ctxt->RouteTable, EP_VP_PROBE(lvl), ELAN3_MRF_CONTEXT_NUM|SYS_CONTEXT_BIT, nflits, flits) != 0)
++          panic ("ep3_probe_position_found: cannot load probe route entry\n");
++    }
++    
++    /* Initialise the traceroute source data with our nodeid */
++    elan3_sdram_writew (rail->Device, rail->RailElan + offsetof (EP3_RAIL_ELAN, ProbeSource0[TR_TRACEROUTE_ENTRIES-1]), pos->pos_nodeid);
++    elan3_sdram_writew (rail->Device, rail->RailElan + offsetof (EP3_RAIL_ELAN, ProbeSource1[TR_TRACEROUTE_ENTRIES-1]), pos->pos_nodeid);
++}
++
++int
++ep3_check_position (EP_RAIL *r)
++{
++    EP3_RAIL      *rail     = (EP3_RAIL *) r;
++    EP3_RAIL_MAIN *railMain = rail->RailMain;
++    sdramaddr_t    railElan = rail->RailElan;
++    ELAN_POSITION *pos      = &rail->Generic.Position;
++    unsigned int   level    = rail->RailMain->ProbeLevel;
++    unsigned int   updated  = EP3_EVENT_FIRED (rail->ProbeCookie, railMain->ProbeDone);
++    unsigned int   lvl;
++
++    if (updated)
++    {
++      if (railMain->ProbeResult != C_ACK_OK)
++      {
++          EPRINTF2 (DBG_PROBE, "%s: CheckNetworkPosition: packet nacked result=%d\n", rail->Generic.Name, railMain->ProbeResult); 
++          
++          rail->Generic.SwitchProbeLevel = -1;
++      }
++      else
++      {
++          E3_uint16 val0 = railMain->ProbeDest0[TR_TRACEROUTE_ENTRIES - 2*(level+1)];
++          E3_uint16 val1 = railMain->ProbeDest1[TR_TRACEROUTE_ENTRIES - 2*(level+1)];
++
++          if (val0 != pos->pos_nodeid || val1 != pos->pos_nodeid)
++          {
++              static unsigned long printed = 0;
++
++              /* We've received a packet from another node - this probably means
++               * that we've moved */
++              if ((lbolt - printed) > (HZ*10))
++              {
++                  printk ("%s: ep3_check_position - level %d lost nodeid\n", rail->Generic.Name, level);
++                  printed = lbolt;
++              }
++
++              rail->Generic.SwitchProbeLevel = -1;
++          }
++          else
++          {
++              for (lvl = 0; lvl <= level; lvl++)
++              {
++                  E3_uint16 val0 = railMain->ProbeDest0[TR_TRACEROUTE_ENTRIES - ((2*level) - lvl + 1)];
++                  E3_uint16 val1 = railMain->ProbeDest1[TR_TRACEROUTE_ENTRIES - ((2*level) - lvl + 1)];
++
++                  rail->Generic.SwitchState[lvl].linkid = TR_TRACEROUTE0_LINKID(val0);
++                  rail->Generic.SwitchState[lvl].LNR    = TR_TRACEROUTE0_LNR(val0);
++                  rail->Generic.SwitchState[lvl].bcast  = TR_TRACEROUTE1_BCAST_TOP(val1);
++                  rail->Generic.SwitchState[lvl].uplink = 4;
++
++                  EPRINTF5 (DBG_PROBE, " --- lvl %d: linkid=%d LNR=%x bcast=%d uplink=%d\n", lvl, rail->Generic.SwitchState[lvl].linkid,
++                            rail->Generic.SwitchState[lvl].LNR, rail->Generic.SwitchState[lvl].bcast ,rail->Generic.SwitchState[lvl].uplink);
++              }
++              rail->Generic.SwitchProbeLevel = level;
++          }
++      }
++
++      railMain->ProbeDone = EP3_EVENT_FREE;
++    }
++
++    if (railMain->ProbeDone == EP3_EVENT_FREE)
++    {
++      if (rail->Generic.SwitchBroadcastLevel == rail->Generic.Position.pos_levels-1)
++          level = rail->Generic.Position.pos_levels - 1;
++      else
++          level = rail->Generic.SwitchBroadcastLevel + 1;
++
++      EPRINTF2 (DBG_PROBE, "%s: ep3_check_postiion: level %d\n", rail->Generic.Name, level);
++
++      /* Initialise the count result etc */
++      elan3_sdram_writel (rail->Device, railElan + offsetof (EP3_RAIL_ELAN, ProbeType), PROBE_MULTIPLE);
++      elan3_sdram_writel (rail->Device, railElan + offsetof (EP3_RAIL_ELAN, ProbeLevel), level);
++
++      railMain->ProbeResult = -1;
++      railMain->ProbeLevel  = -1;
++      
++      /* Clear the receive area */
++      bzero (railMain->ProbeDest0, sizeof (railMain->ProbeDest0));
++      bzero (railMain->ProbeDest1, sizeof (railMain->ProbeDest1));
++      
++      /* Re-arm the completion event */
++      elan3_sdram_writel (rail->Device, railElan + offsetof (EP3_RAIL_ELAN, ProbeDone.ev_Type), EV_TYPE_BCOPY);
++      elan3_sdram_writel (rail->Device, railElan + offsetof (EP3_RAIL_ELAN, ProbeDone.ev_Count), 1);
++
++      railMain->ProbeDone = EP3_EVENT_ACTIVE;
++      
++      IssueSetevent (rail, rail->RailElanAddr + offsetof (EP3_RAIL_ELAN, ProbeStart));
++    }
++
++    return updated;
++}
++
+Index: linux-2.4.21/drivers/net/qsnet/ep/probenetwork_elan3_thread.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/probenetwork_elan3_thread.c 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/probenetwork_elan3_thread.c      2005-06-01 23:12:54.678427096 -0400
+@@ -0,0 +1,98 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: probenetwork_elan3_thread.c,v 1.19 2004/03/24 11:32:56 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/probenetwork_elan3_thread.c,v $*/
++
++#include <elan3/e3types.h>
++#include <elan3/events.h>
++#include <elan3/elanregs.h>
++#include <elan3/intrinsics.h>
++
++#include "kcomm_vp.h"
++#include "kcomm_elan3.h"
++
++static int
++kcomm_probe_vp (EP3_RAIL_ELAN *railElan, EP3_RAIL_MAIN *railMain, int vp, int attempts, int timeouts)
++{
++    int rc;
++
++    /* Since we use %g1 to hold the "rxd" so the trap handler can
++     * complete the envelope processing - we pass zero to indicate we're
++     * not a receiver thread */
++    asm volatile ("mov %g0, %g1");
++
++    while (attempts && timeouts)
++    {
++      c_open (vp);
++      c_sendmem (TR_TRACEROUTE, &railMain->ProbeDest0, &railElan->ProbeSource0);
++      c_sendmem (TR_TRACEROUTE, &railMain->ProbeDest1, &railElan->ProbeSource1);
++      c_sendtrans0 (TR_SENDACK | TR_SETEVENT, (E3_Addr) 0);
++      
++      switch (rc = c_close())
++      {
++      case C_ACK_OK:
++          return (C_ACK_OK);
++          
++      case C_ACK_DISCARD:
++          attempts--;
++          break;
++
++      default:                                        /* output timeout */
++          timeouts--;
++      }
++
++      c_break_busywait();
++    }
++
++    return (timeouts == 0 ? C_ACK_ERROR : C_ACK_DISCARD);
++}
++
++void
++kcomm_probe (E3_CommandPort *cport, EP3_RAIL_ELAN *railElan, EP3_RAIL_MAIN *railMain)
++{
++    int level;
++
++    for (;;)
++    {
++      c_waitevent (&railElan->ProbeStart, 1);
++
++      switch (railElan->ProbeType)
++      {
++      case PROBE_SINGLE:
++          railMain->ProbeResult = kcomm_probe_vp (railElan, railMain, EP_VP_PROBE(railElan->ProbeLevel),
++                                                  PROBE_SINGLE_ATTEMPTS, PROBE_SINGLE_TIMEOUTS);
++
++          cport->SetEvent = (E3_Addr) &railElan->ProbeDone;
++          break;
++
++      case PROBE_MULTIPLE:
++          for (level = railElan->ProbeLevel; level >= 0; level--)
++          {
++              if (kcomm_probe_vp (railElan, railMain, EP_VP_PROBE(level),
++                                  PROBE_MULTIPLE_ATTEMPTS, PROBE_MULTIPLE_TIMEOUTS) == C_ACK_OK)
++              {
++                  railMain->ProbeLevel  = level;
++                  railMain->ProbeResult = C_ACK_OK;
++                  break;
++              }
++
++              c_break_busywait();
++          }
++          cport->SetEvent = (E3_Addr) &railElan->ProbeDone;
++          break;
++      }
++
++    }
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/ep/probenetwork_elan4.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/probenetwork_elan4.c        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/probenetwork_elan4.c     2005-06-01 23:12:54.679426944 -0400
+@@ -0,0 +1,396 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: probenetwork_elan4.c,v 1.9 2004/08/19 11:05:03 david Exp $ $Name: QSNETMODULES-4-30_20050128 $"
++/*      $Source: /cvs/master/quadrics/epmod/probenetwork_elan4.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++
++#include "kcomm_vp.h"
++#include "kcomm_elan4.h"
++#include "debug.h"
++
++#include <elan4/trtype.h>
++#include <elan4/commands.h>
++
++static void
++probe_interrupt (EP4_RAIL *rail, void *arg)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&rail->r_probe_lock, flags);
++    rail->r_probe_done = 1;
++    kcondvar_wakeupone (&rail->r_probe_wait, &rail->r_probe_lock);
++    spin_unlock_irqrestore (&rail->r_probe_lock, flags);
++}
++
++int
++ep4_probe_init (EP4_RAIL *rail)
++{
++    spin_lock_init (&rail->r_probe_lock);
++    kcondvar_init (&rail->r_probe_wait);
++
++    rail->r_probe_cq = ep4_alloc_ecq (rail, CQ_Size1K);
++
++    if (rail->r_probe_cq == NULL)
++      return -ENOMEM;
++
++    ep4_register_intcookie (rail, &rail->r_probe_intcookie, rail->r_elan_addr, probe_interrupt, rail);
++
++    return 0;
++}
++
++void
++ep4_probe_destroy (EP4_RAIL *rail)
++{
++    if (rail->r_probe_cq)
++      ep4_free_ecq (rail, rail->r_probe_cq);
++
++    if (rail->r_probe_intcookie.int_arg == NULL)
++      return;
++    ep4_deregister_intcookie (rail, &rail->r_probe_intcookie);
++
++    kcondvar_destroy (&rail->r_probe_wait);
++    spin_lock_destroy (&rail->r_probe_lock);
++}
++
++#define LINKDOWN(nodeid, level)       ((nodeid >> (level << 1)) & 3)
++#define PROBE_PATTERN0(nodeid)        (0xaddebabe ^ nodeid)
++#define PROBE_PATTERN1(nodeid)  (0xfeedbeef ^ nodeid)
++
++#define EP4_PROBE_RETRIES     4
++
++int
++ep4_probe_route (EP_RAIL *r, int level, int sw, int nodeid, int *linkup, int *linkdown, int attempts, EP_SWITCH *lsw)
++{
++    EP4_RAIL      *rail  = (EP4_RAIL *) r;
++    EP4_RAIL_MAIN *rmain = rail->r_main;
++    E4_uint16      first = 0;
++    int                  rb    = 0;
++
++    E4_uint8  packed[ROUTE_NUM_PACKED];
++    E4_VirtualProcessEntry route;
++    unsigned long flags;
++    int i;
++
++    for (i = 0; i < ROUTE_NUM_PACKED; i++)
++      packed[i] = 0;
++
++    /* Generate "up" routes */
++    for (i = 0; i < level; i++)
++      if (first == 0)
++          first = linkup ? FIRST_ROUTE(linkup[i]) : FIRST_ADAPTIVE;
++      else
++          packed[rb++] = linkup ? PACKED_ROUTE(linkup[i]) : PACKED_ADAPTIVE;
++    
++    /* Generate a "to-me" route down */
++    if (first == 0)
++      first = FIRST_MYLINK;
++    else
++      packed[rb++] = PACKED_MYLINK;
++    
++    /* Generate the "down" routes */
++    for (i = level-1; i >= 0; i--)
++      packed[rb++] = linkdown ? PACKED_ROUTE(linkdown[i]) : PACKED_ROUTE(LINKDOWN(nodeid, i));
++    
++    /* Pack up the routes into the virtual process entry */
++    route.Values[0] = first | FIRST_HIGH_PRI | FIRST_SYSTEM_PACKET | FIRST_TIMEOUT(3);
++    route.Values[1] = ROUTE_CTXT_VALUE(ELAN4_KCOMM_CONTEXT_NUM);
++
++    for (i = 0; i < (ROUTE_NUM_PACKED >> 1); i++)
++    {
++      route.Values[0] |= ((E4_uint64) packed[i]) << ((i << 2) + ROUTE_PACKED_OFFSET);
++      route.Values[1] |= ((E4_uint64) packed[i+(ROUTE_NUM_PACKED >> 1)]) << ((i << 2));
++    }
++
++    elan4_write_route (rail->r_ctxt.ctxt_dev, rail->r_routetable, EP_VP_PROBE(level), &route);
++    
++    while (attempts--)
++    {
++      rail->r_probe_done = 0;
++
++      /* generate the STEN packet - note we use a datatype of dword as we're copying to elan in dwords
++       *   NB - no flow control is required, since the max packet size is less than the command queue
++       *        size and it's dedicated for network probing.
++       */
++      
++      elan4_guard   (rail->r_probe_cq->ecq_cq, GUARD_CHANNEL(1) | GUARD_RESET(EP4_PROBE_RETRIES));
++      elan4_nop_cmd (rail->r_probe_cq->ecq_cq, 0);
++      
++      elan4_open_packet (rail->r_probe_cq->ecq_cq, OPEN_STEN_PKT_CMD | OPEN_PACKET(0, PACK_OK | RESTART_COUNT_ZERO, EP_VP_PROBE(level)));
++      elan4_sendtransn  (rail->r_probe_cq->ecq_cq, TR_TRACEROUTE(TRACEROUTE_NDWORDS),
++                         rail->r_main_addr + offsetof (EP4_RAIL_MAIN, r_probe_dest0),
++                         0x0000000000000000ull, 0x0000000000000000ull, 0x0000000000000000ull, 0x0000000000000000ull, 
++                         0x0000000000000000ull, 0x0000000000000000ull, 0x0000000000000000ull, 0x0000000000000000ull | ((E4_uint64)PROBE_PATTERN0(nodeid) << 32));
++      elan4_sendtransn  (rail->r_probe_cq->ecq_cq, TR_TRACEROUTE(TRACEROUTE_NDWORDS),
++                         rail->r_main_addr + offsetof (EP4_RAIL_MAIN, r_probe_dest1),
++                         0x0000000100000001ull, 0x0000000100000001ull, 0x0000000100000001ull, 0x0000000100000001ull, 
++                         0x0000000100000001ull, 0x0000000100000001ull, 0x0000000100000001ull, 0x0000000000000001ull | ((E4_uint64)PROBE_PATTERN1(nodeid) << 32));
++      elan4_sendtrans0  (rail->r_probe_cq->ecq_cq, TR_NOP_TRANS | TR_LAST_AND_SEND_ACK, 0);
++
++      elan4_guard           (rail->r_probe_cq->ecq_cq, GUARD_CHANNEL(1) | GUARD_TEST(0, PACK_OK) | GUARD_RESET(EP4_PROBE_RETRIES));
++      elan4_write_dword_cmd (rail->r_probe_cq->ecq_cq, rail->r_main_addr + offsetof (EP4_RAIL_MAIN, r_probe_result), EP4_STATE_FINISHED);
++
++      elan4_guard            (rail->r_probe_cq->ecq_cq, GUARD_CHANNEL(1) | GUARD_TEST(0, RESTART_COUNT_ZERO) | GUARD_RESET(EP4_PROBE_RETRIES));
++      elan4_write_dword_cmd  (rail->r_probe_cq->ecq_cq, rail->r_main_addr + offsetof (EP4_RAIL_MAIN, r_probe_result), EP4_STATE_FAILED);
++
++      elan4_interrupt_cmd   (rail->r_probe_cq->ecq_cq,  rail->r_probe_intcookie.int_val);
++
++      spin_lock_irqsave (&rail->r_probe_lock, flags);
++      while (! rail->r_probe_done)
++          kcondvar_wait (&rail->r_probe_wait, &rail->r_probe_lock, &flags);
++      spin_unlock_irqrestore (&rail->r_probe_lock, flags);
++
++      if (rmain->r_probe_result == EP4_STATE_FINISHED)
++      {
++          if (rmain->r_probe_dest0[TRACEROUTE_ENTRIES - ((2*level)+1) - 1] != PROBE_PATTERN0(nodeid) ||
++              rmain->r_probe_dest1[TRACEROUTE_ENTRIES - ((2*level)+1) - 1] != PROBE_PATTERN1(nodeid))
++          {
++              printk ("%s: lost nodeid at level %d switch %d - %d != %d\n", rail->r_generic.Name, level, sw,
++                      rmain->r_probe_dest0[TRACEROUTE_ENTRIES - ((2*level)+1) - 1], PROBE_PATTERN0(nodeid));
++          }
++          else
++          {
++              E4_uint32 val0 = rmain->r_probe_dest0[TRACEROUTE_ENTRIES - level - 1];
++              E4_uint32 val1 = rmain->r_probe_dest1[TRACEROUTE_ENTRIES - level - 1];
++              
++              lsw->lnr     = TR_TRACEROUTE0_LNR(val0);
++              lsw->link    = TR_TRACEROUTE0_LINKID(val0);
++              lsw->bcast   = TR_TRACEROUTE1_BCAST_TOP(val1);
++              lsw->invalid = 0;
++
++              return 1;
++          }
++      }
++
++      rmain->r_probe_result = EP4_STATE_FREE;
++    }
++
++    return 0;
++}
++
++
++void
++ep4_probe_position_found (EP4_RAIL *rail, ELAN_POSITION *pos)
++{
++    ELAN4_DEV  *dev  = rail->r_ctxt.ctxt_dev;
++    int         lvl;
++
++    for (lvl = 0; lvl < pos->pos_levels; lvl++)
++    {
++      /* Initialise the "probe" route to use the broadcast tree */
++      ELAN_POSITION *pos     = &rail->r_generic.Position;
++      unsigned char *arityp  = &pos->pos_arity[pos->pos_levels - 1];
++      unsigned int   spanned = *arityp;
++      E4_uint16      first   = 0;
++      int            rb      = 0;
++      
++      E4_uint8  packed[ROUTE_NUM_PACKED];
++      E4_VirtualProcessEntry route;
++      int i;
++      
++      for (i = 0; i < ROUTE_NUM_PACKED; i++)
++          packed[i] = 0;
++
++      /* Generate "up" routes */
++      for (i = 0; i < lvl; i++, spanned *= *(--arityp))
++      {
++          if (first == 0)
++              first = FIRST_BCAST_TREE;
++          else
++              packed[rb++] = PACKED_BCAST_TREE;
++      }
++
++      /* Generate a "to-me" route down */
++      if (first == 0)
++          first = FIRST_MYLINK;
++      else
++          packed[rb++] = PACKED_MYLINK;
++
++      spanned /= *arityp++;
++
++      /* Generate the "down" routes */
++      for (i = lvl-1; i >= 0; i--)
++      {
++          spanned /= *arityp;
++          packed[rb++] = PACKED_ROUTE((pos->pos_nodeid / spanned) % *arityp);
++          arityp++;
++      }
++
++    
++      /* Pack up the routes into the virtual process entry */
++      route.Values[0] = first | FIRST_HIGH_PRI | FIRST_SYSTEM_PACKET | FIRST_TIMEOUT(3);
++      route.Values[1] = ROUTE_CTXT_VALUE(ELAN4_KCOMM_CONTEXT_NUM);
++      
++      for (i = 0; i < (ROUTE_NUM_PACKED >> 1); i++)
++      {
++          route.Values[0] |= ((E4_uint64) packed[i]) << ((i << 2) + ROUTE_PACKED_OFFSET);
++          route.Values[1] |= ((E4_uint64) packed[i+(ROUTE_NUM_PACKED >> 1)]) << ((i << 2));
++      }
++      
++      elan4_write_route (rail->r_ctxt.ctxt_dev, rail->r_routetable, EP_VP_PROBE(lvl), &route);
++      
++      /* Initialise "start" event for this level */
++      elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_start[lvl].ev_CountAndType),
++                          E4_EVENT_INIT_VALUE (-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_CHECK_STEN_NDWORDS));
++      elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_start[lvl].ev_CopySource),
++                          rail->r_elan_addr + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl]));
++      elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_start[lvl].ev_CopyDest),
++                          rail->r_probe_cq->ecq_addr);
++
++      /* Initiailise command stream - reset the start event */
++      elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_reset_event_cmd),
++                          WRITE_DWORD_CMD | (rail->r_elan_addr + offsetof (EP4_RAIL_ELAN, r_check_start[lvl])));
++      elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_reset_event_value),
++                          E4_EVENT_INIT_VALUE (-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_CHECK_STEN_NDWORDS));
++
++      /* Initiailise command stream - sten traceroute packet */
++      elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_open),
++                          OPEN_STEN_PKT_CMD | OPEN_PACKET (0, PACK_OK | RESTART_COUNT_ZERO, EP_VP_PROBE(lvl)));
++
++      /* Initiailise command stream - traceroute 0 */
++      elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_trans_traceroute0),
++                          SEND_TRANS_CMD | (TR_TRACEROUTE(TRACEROUTE_NDWORDS) << 16));
++      elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_addr_traceroute0),
++                          rail->r_main_addr + offsetof (EP4_RAIL_MAIN, r_probe_dest0));
++      for (i = 0; i < (TRACEROUTE_NDWORDS-1); i++)
++          elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_data_traceroute0[i]),
++                              0x0000000000000000ull);
++      elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_data_traceroute0[i]),
++                          0x0000000000000000ull | ((E4_uint64) PROBE_PATTERN0(pos->pos_nodeid) << 32));
++
++      /* Initiailise command stream - traceroute 1 */
++      elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_trans_traceroute1),
++                          SEND_TRANS_CMD | (TR_TRACEROUTE(TRACEROUTE_NDWORDS) << 16));
++      elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_addr_traceroute1),
++                          rail->r_main_addr + offsetof (EP4_RAIL_MAIN, r_probe_dest1));
++      for (i = 0; i < (TRACEROUTE_NDWORDS-1); i++)
++          elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_data_traceroute1[i]),
++                              0x0000000100000001ull);
++      elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_data_traceroute1[i]),
++                          0x0000000000000001ull | ((E4_uint64) PROBE_PATTERN1(pos->pos_nodeid) << 32));
++
++      /* Initiailise command stream - null sendack */
++      elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_trans_sendack),
++                          SEND_TRANS_CMD | ((TR_NOP_TRANS | TR_LAST_AND_SEND_ACK) << 16));
++      elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_addr_sendack),
++                          0);
++      
++      /* Initiailise command stream - guard ok, write done */
++      elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_guard_ok),
++                          GUARD_CMD | GUARD_CHANNEL(1) | GUARD_TEST(0, PACK_OK) | GUARD_RESET(EP4_PROBE_RETRIES));
++      elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_writedword_ok),
++                          WRITE_DWORD_CMD | (rail->r_main_addr + offsetof (EP4_RAIL_MAIN, r_probe_level)));
++      elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_value_ok),
++                          lvl);
++
++      /* Initiailise command stream - guard fail, chain to next or write done */
++      elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_guard_fail),
++                          GUARD_CMD | GUARD_CHANNEL(1) | GUARD_TEST(0, RESTART_COUNT_ZERO) | GUARD_RESET(EP4_PROBE_RETRIES));
++
++      if (lvl > 0)
++      {
++          elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_setevent_fail),
++                              SET_EVENT_CMD | (rail->r_elan_addr + offsetof (EP4_RAIL_ELAN, r_check_start[lvl-1])));
++          elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_setevent_nop),
++                              NOP_CMD);
++      }
++      else
++      {
++          elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_setevent_fail),
++                              WRITE_DWORD_CMD | (rail->r_main_addr + offsetof (EP4_RAIL_MAIN, r_probe_level)));
++          elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_setevent_nop),
++                              EP4_PROBE_FAILED);
++      }
++      elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_nop_pad),
++                          NOP_CMD);
++    }
++
++    
++    rail->r_main->r_probe_level = EP4_PROBE_ACTIVE;
++
++    mb();
++    ep4_set_event_cmd (rail->r_probe_cq, rail->r_elan_addr + offsetof (EP4_RAIL_ELAN, r_check_start[pos->pos_levels-1]));
++}
++
++int
++ep4_check_position (EP_RAIL *r)
++{
++    EP4_RAIL      *rail = (EP4_RAIL *) r;
++    ELAN_POSITION *pos  = &rail->r_generic.Position;
++    unsigned int level  = rail->r_main->r_probe_level;
++    unsigned int lvl;
++
++    EPRINTF2 (DBG_PROBE, "%s: ep4_check_position: level=%lld\n", rail->r_generic.Name, rail->r_main->r_probe_level);
++
++    if (rail->r_main->r_probe_level != EP4_PROBE_ACTIVE)
++    {
++      if (rail->r_main->r_probe_level == EP4_PROBE_FAILED)
++      {
++          EPRINTF1 (DBG_PROBE, "%s: ep4_check_position: packets all nacked\n", rail->r_generic.Name);
++
++          rail->r_generic.SwitchProbeLevel = -1;
++      }
++      else
++      {
++          E4_uint32 val0  = rail->r_main->r_probe_dest0[TRACEROUTE_ENTRIES - 2*(level+1)];
++          E4_uint32 val1  = rail->r_main->r_probe_dest1[TRACEROUTE_ENTRIES - 2*(level+1)];
++
++          if (val0 != PROBE_PATTERN0 (pos->pos_nodeid) || val1 != PROBE_PATTERN1 (pos->pos_nodeid))
++          {
++              static unsigned long printed = 0;
++
++              /* We've received a packet from another node - this probably means
++               * that we've moved */
++              if ((lbolt - printed) > (HZ*10))
++              {
++                  printk ("%s: ep4_check_position - level %d lost nodeid\n", rail->r_generic.Name, level);
++                  printed = lbolt;
++              }
++
++              rail->r_generic.SwitchProbeLevel = -1;
++          }
++          else
++          {
++              for (lvl = 0 ; lvl <= level; lvl++)
++              {
++                  E4_uint32 uval0  = rail->r_main->r_probe_dest0[TRACEROUTE_ENTRIES - lvl - 1];
++                  E4_uint32 dval0  = rail->r_main->r_probe_dest0[TRACEROUTE_ENTRIES - ((2*level) - lvl + 1)];
++                  E4_uint32 dval1  = rail->r_main->r_probe_dest1[TRACEROUTE_ENTRIES - ((2*level) - lvl + 1)];
++
++                  rail->r_generic.SwitchState[lvl].linkid = TR_TRACEROUTE0_LINKID (dval0);
++                  rail->r_generic.SwitchState[lvl].LNR    = TR_TRACEROUTE0_LNR(dval0);
++                  rail->r_generic.SwitchState[lvl].bcast  = TR_TRACEROUTE1_BCAST_TOP (dval1);
++                  rail->r_generic.SwitchState[lvl].uplink = TR_TRACEROUTE0_LINKID (uval0);
++
++                  EPRINTF5 (DBG_PROBE, " --- lvl %d: linkid=%d LNR=%x bcast=%d uplink=%d\n", lvl, rail->r_generic.SwitchState[lvl].linkid,
++                            rail->r_generic.SwitchState[lvl].LNR, rail->r_generic.SwitchState[lvl].bcast ,rail->r_generic.SwitchState[lvl].uplink);
++
++              }
++
++              rail->r_generic.SwitchProbeLevel = level;
++          }
++      }
++
++      rail->r_main->r_probe_level = EP4_PROBE_ACTIVE;
++      mb();
++
++      if (rail->r_generic.SwitchBroadcastLevel == rail->r_generic.Position.pos_levels-1)
++          level = rail->r_generic.Position.pos_levels - 1;
++      else
++          level = rail->r_generic.SwitchBroadcastLevel + 1;
++
++      ep4_set_event_cmd (rail->r_probe_cq, rail->r_elan_addr + offsetof (EP4_RAIL_ELAN, r_check_start[level]));
++
++      return 1;
++    }
++
++    return 0;
++}
+Index: linux-2.4.21/drivers/net/qsnet/ep/procfs_linux.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/procfs_linux.c      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/procfs_linux.c   2005-06-01 23:12:54.680426792 -0400
+@@ -0,0 +1,693 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: procfs_linux.c,v 1.53.2.4 2005/01/18 14:18:42 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/procfs_linux.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++#include <elan/epsvc.h>
++#include <elan/epcomms.h>
++
++#include "cm.h"
++#include "debug.h"
++#include "conf_linux.h"
++#include <linux/module.h>
++#include <linux/wait.h>
++#include <linux/poll.h>
++
++#include <qsnet/procfs_linux.h>
++
++struct proc_dir_entry *ep_procfs_root;
++struct proc_dir_entry *ep_config_root;
++
++/*
++ * We provide a slightly "special" interface for /proc/elan/device%d/nodeset,
++ * so that it can be included in a "poll" system call.  On each "read" on the
++ * file, we generate a new nodeset if a) the previous one has been completely
++ * read and b) if it has changed since it was generated.
++ *
++ * Unfortunately ... this doesn't allow "tail -f" to work, since this uses
++ * fstat() on the fd, as we only hold the last nodeset string, we could not
++ * handle the case where two processes were reading a different rates.
++ * We could maybe have implemented this as a "sliding window", so that we 
++ * add a new nodeset string, when it has changed and someone reads past 
++ * end of the last one.   Then if someone read from before out "window"
++ * we would produce "padding" data.  The problem with this, is that a 
++ * simple "cat" on /proc/elan/device%d/nodeset will read the whole "file"
++ * which will be mostly padding !
++ *
++ * Just to not that the purpose of this interface is:
++ *    1) to allow cat /proc/elan/device%d/nodeset to show the current
++ *       nodeset.
++ *    2) to allow rms (or similar) to poll() on the file, and when the
++ *       nodeset changes read a new one.
++ *
++ * so ... we don't bother solving the troublesome "tail -f" problem.
++ */
++
++typedef struct nodeset_private
++{
++    struct nodeset_private *pr_next;
++    EP_RAIL                *pr_rail;
++    unsigned              pr_changed;
++    char                 *pr_page;
++    unsigned              pr_off;
++    unsigned              pr_len;
++} NODESET_PRIVATE;
++
++NODESET_PRIVATE   *ep_nodeset_list;
++wait_queue_head_t  ep_nodeset_wait;
++spinlock_t         ep_nodeset_lock;
++
++static int
++proc_write_state(struct file *file, const char *buffer,
++               unsigned long count, void *data)
++{
++    EP_RAIL *rail = (EP_RAIL *) data;
++    char    tmpbuf[128];
++    int     res;
++
++    if (count > sizeof (tmpbuf)-1)
++      return (-EINVAL);
++    
++    MOD_INC_USE_COUNT;
++    
++    if (copy_from_user (tmpbuf, buffer, count))
++      res = -EFAULT;
++    else 
++    {
++      tmpbuf[count] = '\0';   
++
++      if (tmpbuf[count-1] == '\n')
++          tmpbuf[count-1] = '\0';
++
++      if (! strcmp (tmpbuf, "start") && rail->State == EP_RAIL_STATE_UNINITIALISED)
++          ep_start_rail (rail);
++      
++      if (! strcmp (tmpbuf, "stop") && rail->State > EP_RAIL_STATE_UNINITIALISED)
++          ep_stop_rail (rail);
++      
++      if (! strcmp (tmpbuf, "offline") && rail->State > EP_RAIL_STATE_UNINITIALISED)
++          cm_force_offline (rail, 1, CM_OFFLINE_PROCFS);
++
++      if (! strcmp (tmpbuf, "online") && rail->State > EP_RAIL_STATE_UNINITIALISED)
++          cm_force_offline (rail, 0, CM_OFFLINE_PROCFS);
++
++      if (! strncmp (tmpbuf, "restart=", 8) && rail->State == EP_RAIL_STATE_RUNNING)
++          cm_restart_node (rail, simple_strtol (tmpbuf + 8, NULL, 0));
++
++      if (! strncmp (tmpbuf, "panic=", 6))
++          ep_panic_node (rail->System, simple_strtol(tmpbuf + 6, NULL, 0),
++                         strchr (tmpbuf, ',') ? strchr(tmpbuf, ',') + 1 : "remote panic request");
++
++      if (! strncmp (tmpbuf, "raise=", 6) && rail->State > EP_RAIL_STATE_UNINITIALISED)
++          rail->Operations.RaiseFilter (rail, simple_strtol (tmpbuf + 6, NULL, 0));
++
++      if (! strncmp (tmpbuf, "lower=", 6) && rail->State > EP_RAIL_STATE_UNINITIALISED)
++          rail->Operations.LowerFilter (rail, simple_strtol (tmpbuf + 6, NULL, 0));
++      
++      res = count;
++    }
++
++    MOD_DEC_USE_COUNT;
++
++    return (res);
++}
++
++static int
++proc_read_state(char *page, char **start, off_t off,
++              int count, int *eof, void *data)
++{
++    EP_RAIL *rail = (EP_RAIL *) data;
++    int     len;
++
++    switch (rail->State)
++    {
++    case EP_RAIL_STATE_UNINITIALISED:
++      len = sprintf (page, "uninitialised\n");
++      break;
++    case EP_RAIL_STATE_STARTED:
++      len = sprintf (page, "started\n");
++      break;
++    case EP_RAIL_STATE_RUNNING:
++      len = sprintf (page, "running NodeId=%d NumNodes=%d\n", rail->Position.pos_nodeid, rail->Position.pos_nodes);
++      break;
++    case EP_RAIL_STATE_INCOMPATIBLE:
++      len = sprintf (page, "incompatible NodeId=%d NumNodes=%d\n", rail->Position.pos_nodeid, rail->Position.pos_nodes);
++      break;
++    default:
++      len = sprintf (page, "<unknown>\n");
++      break;
++    }
++
++    return (qsnet_proc_calc_metrics (page, start, off, count, eof, len));
++}
++
++static int
++proc_write_display(struct file *file, const char *buffer,
++                 unsigned long count, void *data)
++{
++    EP_RAIL *rail = (EP_RAIL *) data;
++    char    tmpbuf[128];
++    int     res;
++
++    if (count > sizeof (tmpbuf)-1)
++      return (-EINVAL);
++    
++    MOD_INC_USE_COUNT;
++    
++    if (copy_from_user (tmpbuf, buffer, count))
++      res = -EFAULT;
++    else 
++    {
++      tmpbuf[count] = '\0';   
++
++      if (tmpbuf[count-1] == '\n')
++          tmpbuf[count-1] = '\0';
++
++      if (! strcmp (tmpbuf, "rail"))
++          DisplayRail (rail);
++      if (! strcmp (tmpbuf, "segs"))
++          DisplaySegs (rail);
++      if (! strcmp (tmpbuf, "nodes"))
++          DisplayNodes (rail);
++      if (! strcmp (tmpbuf, "status"))
++          DisplayStatus (rail);
++      if (! strcmp (tmpbuf, "debug") && rail->Operations.Debug)
++          rail->Operations.Debug (rail);
++      if (! strncmp (tmpbuf, "epcomms", 7))
++          ep_comms_display (rail->System, tmpbuf[7] == '=' ? tmpbuf + 8 : NULL);
++      res = count;
++    }
++
++    MOD_DEC_USE_COUNT;
++
++    return (res);
++}
++
++static int
++proc_read_display(char *page, char **start, off_t off,
++                int count, int *eof, void *data)
++{
++    int len = sprintf (page, "<unreadable>\n");
++    
++    return (qsnet_proc_calc_metrics (page, start, off, count, eof, len));
++}
++
++
++static int
++proc_read_stats(char *page, char **start, off_t off,
++              int count, int *eof, void *data)
++{
++    EP_RAIL *rail = (EP_RAIL *) data;
++
++    if ( rail == NULL ) {
++      strcpy(page,"proc_read_stats rail=NULL\n");
++    } else {
++      page[0] = 0;
++      ep_fillout_stats(rail, page);
++      rail->Operations.FillOutStats (rail, page);
++    }
++    return (qsnet_proc_calc_metrics (page, start, off, count, eof, strlen(page)));
++}
++
++static int
++proc_read_devinfo(char *page, char **start, off_t off,
++                int count, int *eof, void *data)
++{
++    EP_RAIL       *rail    = (EP_RAIL *) data;
++    ELAN_DEVINFO  *devinfo = &rail->Devinfo;
++    ELAN_POSITION *pos     = &rail->Position;
++    char          *p       = page;
++    
++    switch (devinfo->dev_device_id)
++    {
++    case PCI_DEVICE_ID_ELAN3:
++      p += sprintf (p, "ep%d is elan3 %d rev %c\n", rail->Number, 
++                    devinfo->dev_instance, 'a' + devinfo->dev_revision_id);
++      break;
++      
++    case PCI_DEVICE_ID_ELAN4:
++      p += sprintf (p, "ep%d is elan4 %d rev %c\n", rail->Number, 
++                    devinfo->dev_instance, 'a' + devinfo->dev_revision_id);
++      break;
++    default:
++      p += sprintf (p, "ep%d is unkown %x/%x\n", rail->Number, devinfo->dev_vendor_id, devinfo->dev_device_id);
++      break;
++    }
++
++    if (rail->State == EP_RAIL_STATE_RUNNING)
++      p += sprintf (p, "ep%d nodeid %d numnodes %d\n", rail->Number, pos->pos_nodeid, pos->pos_nodes);
++
++    return (qsnet_proc_calc_metrics (page, start, off, count, eof, p - page));
++}
++
++static struct rail_info
++{
++    char *name;
++    int (*read_func) (char *page, char **start, off_t off, int count, int *eof, void *data);
++    int (*write_func) (struct file *file, const char *buf, unsigned long count, void *data);
++} rail_info[] = {
++    {"state",   proc_read_state,   proc_write_state},
++    {"display", proc_read_display, proc_write_display},
++    {"stats",   proc_read_stats,   NULL},
++    {"devinfo", proc_read_devinfo, NULL},
++};
++
++static int
++nodeset_open (struct inode *inode, struct file *file)
++{
++    NODESET_PRIVATE *pr;
++
++    if ((pr = kmalloc (sizeof (NODESET_PRIVATE), GFP_KERNEL)) == NULL)
++      return (-ENOMEM);
++    
++    pr->pr_changed = 1;
++    pr->pr_off     = 0;
++    pr->pr_len     = 0;
++    pr->pr_page    = NULL;
++    pr->pr_rail    = (EP_RAIL *)( PDE(inode)->data );
++
++    spin_lock (&ep_nodeset_lock);
++    pr->pr_next = ep_nodeset_list;
++    ep_nodeset_list = pr;
++    spin_unlock (&ep_nodeset_lock);
++
++    file->private_data = (void *) pr;
++
++    MOD_INC_USE_COUNT;
++    return (0);
++}
++
++static int
++nodeset_release (struct inode *inode, struct file *file)
++{
++    NODESET_PRIVATE *pr = (NODESET_PRIVATE *) file->private_data;
++    NODESET_PRIVATE **ppr;
++
++    spin_lock (&ep_nodeset_lock);
++    for (ppr = &ep_nodeset_list; (*ppr) != pr; ppr = &(*ppr)->pr_next)
++      ;
++    (*ppr) = pr->pr_next;
++    spin_unlock (&ep_nodeset_lock);
++
++    if (pr->pr_page)
++      free_page ((unsigned long) pr->pr_page);
++    kfree (pr);
++    
++    MOD_DEC_USE_COUNT;
++    return (0);
++}
++
++static ssize_t
++nodeset_read (struct file *file, char *buf, size_t count, loff_t *ppos)
++{
++    NODESET_PRIVATE *pr  = (NODESET_PRIVATE *) file->private_data;
++    EP_RAIL          *rail = pr->pr_rail;
++    int              error;
++    unsigned long    flags;
++
++    if (!pr->pr_changed && pr->pr_off >= pr->pr_len)
++      return (0);
++
++    if ((error = verify_area (VERIFY_WRITE, buf, count)) != 0)
++      return (error);
++
++    if (pr->pr_page == NULL && (pr->pr_page = (char *) __get_free_page (GFP_KERNEL)) == NULL)
++      return (-ENOMEM);
++
++    if (pr->pr_off >= pr->pr_len)
++    {
++      kmutex_lock (&rail->CallbackLock);
++      if (rail->State == EP_RAIL_STATE_RUNNING)
++      {
++          spin_lock_irqsave (&rail->System->NodeLock, flags);
++          ep_sprintf_bitmap (pr->pr_page, PAGESIZE, statemap_tobitmap(rail->NodeSet), 0, 0, rail->Position.pos_nodes);
++          spin_unlock_irqrestore (&rail->System->NodeLock, flags);
++
++          if (rail->SwitchBroadcastLevel == -1)
++              strcat (pr->pr_page, "<disconnected>");
++          else if (rail->SwitchBroadcastLevel < (rail->Position.pos_levels-1))
++              sprintf (pr->pr_page + strlen (pr->pr_page), "<%d>", rail->SwitchBroadcastLevel);
++          strcat (pr->pr_page, "\n");
++      }
++      else
++          strcpy (pr->pr_page, "<not running>\n");
++      kmutex_unlock (&rail->CallbackLock);
++
++      pr->pr_len     = strlen (pr->pr_page);
++      pr->pr_off     = 0;
++      pr->pr_changed = 0;
++    }
++
++    if (count >= (pr->pr_len - pr->pr_off))
++      count = pr->pr_len - pr->pr_off;
++
++    copy_to_user (buf, pr->pr_page + pr->pr_off, count);
++
++    pr->pr_off += count;
++    *ppos      += count;
++
++    if (pr->pr_off >= pr->pr_len)
++    {
++      free_page ((unsigned long) pr->pr_page);
++      pr->pr_page = NULL;
++    }
++
++    return (count);
++}
++
++static unsigned int
++nodeset_poll (struct file *file, poll_table *wait)
++{
++    NODESET_PRIVATE *pr = (NODESET_PRIVATE *) file->private_data;
++
++    poll_wait (file, &ep_nodeset_wait, wait);
++    if (pr->pr_changed || pr->pr_off < pr->pr_len)
++      return (POLLIN | POLLRDNORM);
++    return (0);
++}
++
++static void 
++nodeset_callback (void *arg, statemap_t *map)
++{
++    EP_RAIL         *rail = (EP_RAIL *) arg;
++    NODESET_PRIVATE *pr;
++
++    ep_display_bitmap (rail->Name, "Nodeset", statemap_tobitmap(map), 0, ep_numnodes(rail->System));
++
++    spin_lock (&ep_nodeset_lock);
++    for (pr = ep_nodeset_list; pr; pr = pr->pr_next)
++      if (pr->pr_rail == rail)
++          pr->pr_changed = 1;
++    spin_unlock (&ep_nodeset_lock);
++
++    wake_up_interruptible (&ep_nodeset_wait);
++}
++
++void
++proc_character_fill (long mode, char *fmt, ...)
++{
++    int len;
++    va_list ap;
++    PROC_PRIVATE *private = (PROC_PRIVATE *)mode;
++    
++    /* is the buffer already full */
++    if (private->pr_len >= private->pr_data_len) 
++      return;
++    
++    /* attempt to fill up to the remaining space */
++    va_start (ap, fmt);
++    len = vsnprintf ( & private->pr_data[private->pr_len], (private->pr_data_len - private->pr_len), fmt, ap);
++    va_end (ap);
++    
++    if (len < 0 ) 
++    {
++      /* we have reached the end of buffer and need to fail all future writes
++       * the caller can check (pr_len >= pr_data_len) and recall with more space 
++       */
++      private->pr_len = private->pr_data_len;
++      return;
++    }
++    
++    /* move the length along */
++    private->pr_len += len;   
++}
++
++int
++proc_release (struct inode *inode, struct file *file)
++{
++    PROC_PRIVATE *pr = (PROC_PRIVATE *) file->private_data;
++    
++    if (pr->pr_data)
++      KMEM_FREE (pr->pr_data, pr->pr_data_len);
++    kfree (pr);
++    
++    MOD_DEC_USE_COUNT;
++    return (0);
++}
++
++ssize_t
++proc_read (struct file *file, char *buf, size_t count, loff_t *ppos)
++{
++    PROC_PRIVATE *pr  = (PROC_PRIVATE *) file->private_data;
++    int           error;
++
++    if (pr->pr_off >= pr->pr_len)
++      return (0);
++
++    if ((error = verify_area (VERIFY_WRITE, buf, count)) != 0)
++      return (error);
++
++    if (count >= (pr->pr_len - pr->pr_off))
++      count = pr->pr_len - pr->pr_off;
++
++    copy_to_user (buf, pr->pr_data + pr->pr_off, count);
++
++    pr->pr_off += count;
++    *ppos      += count;
++
++    return (count);
++}
++
++static int
++proc_open (struct inode *inode, struct file *file)
++{
++    PROC_PRIVATE *pr;
++    CM_RAIL      *cmRail;
++    int           pages = 4;
++    unsigned long flags;
++
++    if ((pr = kmalloc (sizeof (PROC_PRIVATE), GFP_KERNEL)) == NULL)
++      return (-ENOMEM);
++    
++    pr->pr_rail = (EP_RAIL *)(PDE(inode)->data);
++      
++    do {      
++      pr->pr_data_len = PAGESIZE * pages;
++
++      KMEM_ZALLOC (pr->pr_data, char *, pr->pr_data_len, 1);
++      if (pr->pr_data == NULL) 
++      { 
++          pr->pr_len  = sprintf (pr->pr_data, "Out of Memory\n");
++          break;
++      } 
++      
++      pr->pr_off     = 0;
++      pr->pr_len     = 0;
++      pr->pr_data[0] = 0;
++      
++      if (pr->pr_rail->State != EP_RAIL_STATE_RUNNING) 
++      { 
++          pr->pr_len  = sprintf (pr->pr_data, "Rail not Running\n");
++          break;
++      } 
++      else 
++      {
++          pr->pr_di.func  = proc_character_fill;
++          pr->pr_di.arg   = (long)pr;
++
++          if (!strcmp("maps", file->f_dentry->d_iname)) 
++          {
++              cmRail = pr->pr_rail->ClusterRail;
++
++              spin_lock_irqsave (&cmRail->Lock, flags);
++              DisplayNodeMaps (&pr->pr_di, cmRail);   
++              spin_unlock_irqrestore (&cmRail->Lock, flags);  
++          }
++
++          if (!strcmp("segs", file->f_dentry->d_iname)) 
++          {
++              cmRail = pr->pr_rail->ClusterRail;
++              
++              spin_lock_irqsave (&cmRail->Lock, flags);       
++              DisplayNodeSgmts (&pr->pr_di, cmRail);
++              spin_unlock_irqrestore (&cmRail->Lock, flags);
++          }
++
++          if (!strcmp("tree", file->f_dentry->d_iname)) 
++              DisplayRailDo (&pr->pr_di, pr->pr_rail);
++      }
++
++      if ( pr->pr_len < pr->pr_data_len) 
++          break; /* we managed to get all the output into the buffer */
++
++      pages++;
++      KMEM_FREE ( pr->pr_data,  pr->pr_data_len);
++    } while (1);
++      
++
++    file->private_data = (void *) pr;
++
++    MOD_INC_USE_COUNT;
++    return (0);
++}
++
++struct file_operations proc_nodeset_operations = 
++{
++    read:     nodeset_read,
++    poll:     nodeset_poll,
++    open:     nodeset_open,
++    release:  nodeset_release,
++};
++
++struct file_operations proc_operations = 
++{
++    read:     proc_read,
++    open:     proc_open,
++    release:  proc_release,
++};
++
++void
++ep_procfs_rail_init (EP_RAIL *rail)
++{
++    struct proc_dir_entry *dir;
++    struct proc_dir_entry *p;
++    char                   name[10];
++    int                    i;
++
++    sprintf (name, "rail%d", rail->Number);
++
++    if ((dir = rail->ProcDir = proc_mkdir (name, ep_procfs_root)) == NULL)
++      return;
++
++    for (i = 0; i < sizeof (rail_info)/sizeof (rail_info[0]); i++)
++    {
++      if ((p = create_proc_entry (rail_info[i].name, 0, dir)) != NULL)
++      {
++          p->read_proc  = rail_info[i].read_func;
++          p->write_proc = rail_info[i].write_func;
++          p->data       = rail;
++          p->owner      = THIS_MODULE;
++      }
++    }
++
++    if ((p = create_proc_entry ("nodeset", 0, dir)) != NULL)
++    {
++      p->proc_fops = &proc_nodeset_operations;
++      p->owner     = THIS_MODULE;
++      p->data      = rail;
++
++      rail->CallbackRegistered = 1;
++      ep_register_callback (rail, EP_CB_NODESET, nodeset_callback, rail);
++    }
++     
++    if ((p = create_proc_entry ("maps", 0, dir)) != NULL)
++    {
++      p->proc_fops = &proc_operations;
++      p->owner     = THIS_MODULE;
++      p->data      = rail;    
++    }
++    
++    if ((p = create_proc_entry ("segs", 0, dir)) != NULL)
++    {
++      p->proc_fops = &proc_operations;
++      p->owner     = THIS_MODULE;
++      p->data      = rail;
++    }
++    
++    if ((p = create_proc_entry ("tree", 0, dir)) != NULL)
++    {
++      p->proc_fops = &proc_operations;
++      p->owner     = THIS_MODULE;
++      p->data      = rail;
++    }
++
++}
++
++void
++ep_procfs_rail_fini (EP_RAIL *rail)
++{
++    struct proc_dir_entry *dir = rail->ProcDir;
++    char name[10];
++    int  i;
++
++    if (dir == NULL)
++      return;
++
++    if (rail->CallbackRegistered)
++    {
++      ep_remove_callback (rail, EP_CB_NODESET, nodeset_callback, rail);
++
++      remove_proc_entry ("nodeset", dir);
++    }
++
++    remove_proc_entry ("maps",    dir);
++    remove_proc_entry ("segs",    dir);
++    remove_proc_entry ("tree",    dir);
++
++    for (i = 0; i < sizeof (rail_info)/sizeof (rail_info[0]); i++)
++      remove_proc_entry (rail_info[i].name, dir);
++
++    sprintf (name, "rail%d", rail->Number);
++    remove_proc_entry (name, ep_procfs_root);
++}
++
++#include "quadrics_version.h"
++static char     quadrics_version[] = QUADRICS_VERSION;
++
++void
++ep_procfs_init()
++{
++    extern int txd_stabilise;
++    extern int MaxSwitchLevels;
++
++    spin_lock_init (&ep_nodeset_lock);
++    init_waitqueue_head (&ep_nodeset_wait);
++
++    ep_procfs_root = proc_mkdir ("ep", qsnet_procfs_root);
++    ep_config_root = proc_mkdir ("config", ep_procfs_root);
++
++    qsnet_proc_register_str (ep_procfs_root, "version", quadrics_version, 1);
++
++    qsnet_proc_register_hex (ep_config_root, "epdebug",               &epdebug,               0);
++    qsnet_proc_register_hex (ep_config_root, "epdebug_console",       &epdebug_console,       0);
++    qsnet_proc_register_hex (ep_config_root, "epdebug_cmlevel",       &epdebug_cmlevel,       0);
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++    qsnet_proc_register_hex (ep_config_root, "epdebug_check_sum",     &epdebug_check_sum,     0);
++#endif
++    qsnet_proc_register_hex (ep_config_root, "epcomms_forward_limit", &epcomms_forward_limit, 0);
++    qsnet_proc_register_int (ep_config_root, "txd_stabilise",         &txd_stabilise,         0);
++    qsnet_proc_register_int (ep_config_root, "assfail_mode",          &assfail_mode,          0);
++    qsnet_proc_register_int (ep_config_root, "max_switch_levels",     &MaxSwitchLevels,       1);
++
++    ep_procfs_rcvr_xmtr_init();
++}
++
++void
++ep_procfs_fini(void)
++{
++    ep_procfs_rcvr_xmtr_fini();
++
++    remove_proc_entry ("max_switch_levels",     ep_config_root);
++    remove_proc_entry ("assfail_mode",          ep_config_root);
++    remove_proc_entry ("txd_stabilise",         ep_config_root);
++    remove_proc_entry ("epcomms_forward_limit", ep_config_root);
++
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++    remove_proc_entry ("epdebug_check_sum",     ep_config_root);
++#endif
++    remove_proc_entry ("epdebug_cmlevel",       ep_config_root);
++    remove_proc_entry ("epdebug_console",       ep_config_root);
++    remove_proc_entry ("epdebug",               ep_config_root);
++
++    remove_proc_entry ("version", ep_procfs_root);
++    
++    remove_proc_entry ("config", ep_procfs_root);
++    remove_proc_entry ("ep", qsnet_procfs_root);
++
++    spin_lock_destroy (&ep_nodeset_lock);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/ep/quadrics_version.h
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/quadrics_version.h  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/quadrics_version.h       2005-06-01 23:12:54.680426792 -0400
+@@ -0,0 +1 @@
++#define QUADRICS_VERSION "4.30qsnet"
+Index: linux-2.4.21/drivers/net/qsnet/ep/railhints.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/railhints.c 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/railhints.c      2005-06-01 23:12:54.680426792 -0400
+@@ -0,0 +1,103 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: railhints.c,v 1.5 2004/02/06 22:37:06 david Exp $ $Name: QSNETMODULES-4-30_20050128 $"
++/*      $Source: /cvs/master/quadrics/epmod/railhints.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++#include <elan/epsvc.h>
++#include <elan/epcomms.h>
++
++#include "debug.h"
++
++int
++ep_pickRail(EP_RAILMASK railmask)
++{
++    static volatile int lastGlobal;
++    int i, rnum, last = lastGlobal;
++
++    /* Pick a single rail out of the railmask */
++    for (i = 0; i < EP_MAX_RAILS; i++)
++      if (railmask & (1 << ((last + i) % EP_MAX_RAILS)))
++          break;
++
++    if (i == EP_MAX_RAILS)
++      return (-1);
++
++    rnum = (last + i) % EP_MAX_RAILS;
++
++    lastGlobal = (rnum + 1) % EP_MAX_RAILS;
++
++    ASSERT (railmask & (1 << rnum));
++
++    return (rnum);
++}
++
++int
++ep_xmtr_bcastrail (EP_XMTR *xmtr, EP_RAILMASK allowedRails)
++{
++    /* Retrun a single rail out of allowed mask with the best connectivity for broadcast. */
++    return (ep_pickRail (allowedRails & xmtr->RailMask));
++}
++
++int
++ep_xmtr_prefrail (EP_XMTR *xmtr, EP_RAILMASK allowedRails, unsigned nodeId)
++{
++    EP_NODE *node = &xmtr->Subsys->Subsys.Sys->Nodes[nodeId];
++
++    EPRINTF5 (DBG_XMTR, "ep_xmtr_prefrail: xmtr=%p allowedRails=%x nodeId=%d xmtr->RailMaks=%x Connected=%x\n", 
++            xmtr, allowedRails, nodeId, xmtr->RailMask, node->ConnectedRails);
++
++    /* Return a single rail which is currently connected to nodeId (limited to rails
++     * in allowedmask) - if more than one rail is possible, then round-robin between 
++     * them */
++    return (ep_pickRail (allowedRails & xmtr->RailMask & node->ConnectedRails));
++}
++
++EP_RAILMASK
++ep_xmtr_availrails (EP_XMTR *xmtr)
++{
++    /* Return which rails can be used to transmit one. */
++
++    return (xmtr->RailMask);
++}
++
++EP_RAILMASK
++ep_xmtr_noderails (EP_XMTR *xmtr, unsigned nodeId)
++{
++    EP_NODE *node = &xmtr->Subsys->Subsys.Sys->Nodes[nodeId];
++
++    /* Return which rails can be used to transmit to this node. */
++
++    return (xmtr->RailMask & node->ConnectedRails);
++}
++
++int
++ep_rcvr_prefrail (EP_RCVR *rcvr, EP_RAILMASK allowedRails)
++{
++    /* Return the "best" rail for queueing a receive buffer out on - this will be a
++     * rail with ThreadWaiting set or the rail with the least descriptors queued
++     * on it. */
++    
++    return (ep_pickRail (allowedRails & rcvr->RailMask));
++}
++
++EP_RAILMASK
++ep_rcvr_availrails (EP_RCVR *rcvr)
++{
++    /* Return which rails can be used to queue receive buffers. */
++    return (rcvr->RailMask);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/ep/rmap.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/rmap.c      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/rmap.c   2005-06-01 23:12:54.681426640 -0400
+@@ -0,0 +1,365 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: rmap.c,v 1.15 2004/05/19 10:24:38 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/rmap.c,v $ */
++
++#include <qsnet/kernel.h>
++#include <elan/rmap.h>
++
++#include "debug.h"
++
++void
++ep_display_rmap (EP_RMAP *mp)
++{
++    EP_RMAP_ENTRY *bp;
++    unsigned long flags;
++    
++    spin_lock_irqsave (&mp->m_lock, flags);
++    ep_debugf (DBG_DEBUG, "map: %s size %d free %d\n", mp->m_name, mp->m_size, mp->m_free);
++    for (bp = &mp->m_map[0]; bp->m_size; bp++)
++      ep_debugf (DBG_DEBUG, "   [%lx - %lx]\n", bp->m_addr, bp->m_addr+bp->m_size-1);
++    spin_unlock_irqrestore (&mp->m_lock, flags);
++}
++
++void
++ep_mapinit (EP_RMAP *mp, char *name, u_int mapsize)
++{
++    spin_lock_init (&mp->m_lock);
++    kcondvar_init (&mp->m_wait);
++    
++    /* The final segment in the array has size 0 and acts as a delimiter
++     * we insure that we never use segments past the end of the array by
++     * maintaining a free segment count in m_free.  When excess segments
++     * occur we discard some resources */
++    
++    mp->m_size = mapsize;
++    mp->m_free = mapsize;
++    mp->m_name = name;
++    
++    bzero (mp->m_map, sizeof (EP_RMAP_ENTRY) * (mapsize+1));
++}
++
++EP_RMAP *
++ep_rmallocmap (size_t mapsize, char *name, int cansleep)
++{
++    EP_RMAP *mp;
++
++    KMEM_ZALLOC (mp, EP_RMAP *, sizeof (EP_RMAP) + mapsize*sizeof (EP_RMAP_ENTRY), cansleep);
++
++    if (mp != NULL)
++      ep_mapinit (mp, name, mapsize);
++
++    return (mp);
++}
++
++void
++ep_rmfreemap (EP_RMAP *mp)
++{
++    spin_lock_destroy (&mp->m_lock);
++    kcondvar_destroy (&mp->m_wait);
++    
++    KMEM_FREE (mp, sizeof (EP_RMAP) + mp->m_size * sizeof (EP_RMAP_ENTRY));
++}
++
++static u_long
++ep_rmalloc_locked (EP_RMAP *mp, size_t size)
++{
++    EP_RMAP_ENTRY *bp;
++    u_long            addr;
++    
++    ASSERT (size > 0);
++    ASSERT (SPINLOCK_HELD (&mp->m_lock));
++
++    for (bp = &mp->m_map[0]; bp->m_size; bp++)
++    {
++      if (bp->m_size >= size)
++      {
++          addr = bp->m_addr;
++          bp->m_addr += size;
++          
++          if ((bp->m_size -= size) == 0)
++          {
++              /* taken all of this slot - so shift the map down */
++              do {
++                  bp++;
++                  (bp-1)->m_addr = bp->m_addr;
++              } while (((bp-1)->m_size = bp->m_size) != 0);
++
++              mp->m_free++;
++          }
++          return (addr);
++      }
++    }
++
++    return (0);
++}
++
++u_long
++ep_rmalloc (EP_RMAP *mp, size_t size, int cansleep)
++{
++    unsigned long addr;
++    unsigned long flags;
++
++    spin_lock_irqsave (&mp->m_lock, flags);
++    while ((addr = ep_rmalloc_locked (mp, size)) == 0 && cansleep)
++    {
++      mp->m_want = 1;
++      kcondvar_wait (&mp->m_wait, &mp->m_lock, &flags);
++    }
++
++    spin_unlock_irqrestore (&mp->m_lock, flags);
++
++    return (addr);
++}
++
++
++
++u_long
++ep_rmalloc_constrained (EP_RMAP *mp, size_t size, u_long alo, u_long ahi, u_long align, int cansleep)
++{
++    EP_RMAP_ENTRY *bp, *bp2, *lbp;
++    unsigned long addr=0;
++    size_t        delta;
++    int           ok;
++    unsigned long flags;
++
++    spin_lock_irqsave (&mp->m_lock, flags);
++ again:
++    for (bp = &mp->m_map[0]; bp->m_size; bp++)
++    {
++      delta = 0;
++      
++      if (alo < bp->m_addr)
++      {
++          addr = bp->m_addr;
++          
++          if (addr & (align-1))
++              addr = (addr + (align-1)) & ~(align-1);
++          
++          delta = addr - bp->m_addr;
++          
++          if (ahi >= bp->m_addr + bp->m_size)
++              ok = (bp->m_size >= (size + delta));
++          else
++              ok = ((bp->m_addr + size + delta) <= ahi);
++      }
++      else
++      {
++          addr = alo;
++          if (addr & (align-1))
++              addr = (addr + (align-1)) & ~(align-1);
++          delta = addr - bp->m_addr;
++          
++          if (ahi >= bp->m_addr + bp->m_size)
++              ok = ((alo + size + delta) <= (bp->m_addr + bp->m_size));
++          else
++              ok = ((alo + size + delta) <= ahi);
++      }
++
++      if (ok)
++          break;
++    } 
++    
++    if (bp->m_size == 0)
++    {
++      if (cansleep)
++      {
++          mp->m_want = 1;
++          kcondvar_wait (&mp->m_wait, &mp->m_lock, &flags);
++          goto again;
++      }
++      spin_unlock_irqrestore (&mp->m_lock, flags);
++      return (0);
++    }
++
++    /* found an approriate map entry - so take the bit out which we want */
++    if (bp->m_addr == addr) 
++    {
++      if (bp->m_size == size) 
++      {
++          /* allocate entire segment and compress map */
++          bp2 = bp;
++          while (bp2->m_size) 
++          {
++              bp2++;
++              (bp2-1)->m_addr = bp2->m_addr;
++              (bp2-1)->m_size = bp2->m_size;
++          }
++          mp->m_free++;
++      }
++      else 
++      {
++          /* take from start of segment */
++          bp->m_addr += size;
++          bp->m_size -= size;
++      }
++    }
++    else 
++    {
++      if (bp->m_addr + bp->m_size == addr + size) 
++      {
++          /* take from end of segment */
++          bp->m_size -= size;
++      }
++      else 
++      {
++          /* split the segment loosing the last entry if there's no space */
++          if (mp->m_free == 0) 
++          {
++              /* find last map entry */
++              for (lbp = bp; lbp->m_size != 0; lbp++)
++                  ;
++              lbp--;
++              
++              if (lbp->m_size > (lbp-1)->m_size)
++                  lbp--;
++              
++              printk ("%s: lost resource map entry [%lx, %lx]\n",
++                      mp->m_name, lbp->m_addr, lbp->m_addr + lbp->m_size);
++              
++              *lbp = *(lbp+1);
++              (lbp+1)->m_size = 0;
++              
++              mp->m_free++;
++          }
++          
++          for (bp2 = bp; bp2->m_size != 0; bp2++)
++              continue;
++          
++          for (bp2--; bp2 > bp; bp2--)
++          {
++              (bp2+1)->m_addr = bp2->m_addr;
++              (bp2+1)->m_size = bp2->m_size;
++          }
++
++          mp->m_free--;
++          
++          (bp+1)->m_addr = addr + size;
++          (bp+1)->m_size = bp->m_addr + bp->m_size - (addr + size);
++          bp->m_size = addr - bp->m_addr;
++      }
++    }
++
++    spin_unlock_irqrestore (&mp->m_lock, flags);
++    return (addr);
++}
++
++void
++ep_rmfree (EP_RMAP *mp, size_t size, u_long addr)
++{
++    EP_RMAP_ENTRY *bp;
++    unsigned long t;
++    unsigned long flags;
++
++    spin_lock_irqsave (&mp->m_lock, flags);
++
++    ASSERT (addr != 0 && size > 0);
++      
++again:
++    /* find the piece of the map which starts after the returned space
++     * or the end of the map */
++    for (bp = &mp->m_map[0]; bp->m_addr <= addr && bp->m_size != 0; bp++)
++      ;
++
++    /* bp points to the piece to the right of where we want to go */
++    
++    if (bp > &mp->m_map[0] && (bp-1)->m_addr + (bp-1)->m_size >= addr) 
++    {
++      /* merge with piece on the left */
++      
++      ASSERT ((bp-1)->m_addr + (bp-1)->m_size <= addr);
++      
++      (bp-1)->m_size += size;
++      
++      ASSERT (bp->m_size == 0 || addr+size <= bp->m_addr);
++      
++      if (bp->m_size && (addr + size) == bp->m_addr)
++      {
++          /* merge witht he piece on the right by 
++           * growing the piece on the left and shifting
++           * the map down */
++          
++          ASSERT ((addr + size) <= bp->m_addr);
++          
++          (bp-1)->m_size += bp->m_size;
++          while (bp->m_size) 
++          {
++              bp++;
++              (bp-1)->m_addr = bp->m_addr;
++              (bp-1)->m_size = bp->m_size;
++          }
++          
++          mp->m_free++;
++      }
++    }
++    else if (addr + size >= bp->m_addr && bp->m_size)
++    {
++      /* merge with piece to the right */
++      
++      ASSERT ((addr + size) <= bp->m_addr);
++      
++      bp->m_addr -= size;
++      bp->m_size += size;
++    }
++    else
++    {
++      /* doesn't join with left or right - check for map
++         overflow and discard the smallest of the last or
++         next to last entries */
++
++      if (mp->m_free == 0)
++      {
++          EP_RMAP_ENTRY *lbp;
++          
++          /* find last map entry */
++          for (lbp = bp; lbp->m_size != 0; lbp++)
++              ;
++          lbp--;
++          
++          if (lbp->m_size > (lbp-1)->m_size)
++              lbp--;
++          
++          printk ("%s: lost resource map entry [%lx, %lx]\n", 
++                  mp->m_name, lbp->m_addr, lbp->m_addr + lbp->m_size);
++          
++          *lbp = *(lbp+1);
++          (lbp+1)->m_size = 0;
++
++          mp->m_free++;
++          goto again;
++      }
++
++      /* make a new entry and push the remaining ones up */
++      do {
++          t = bp->m_addr;
++          bp->m_addr = addr;
++          addr = t;
++          t = bp->m_size;
++          bp->m_size = size;
++          bp++;
++      } while ((size = t) != 0);
++
++      mp->m_free--;
++    }
++    
++    /* if anyone blocked on rmalloc failure, wake 'em up */
++    if (mp->m_want)
++    {
++      mp->m_want = 0;
++      kcondvar_wakeupall (&mp->m_wait, &mp->m_lock);
++    }
++
++    spin_unlock_irqrestore (&mp->m_lock, flags);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/ep/spinlock_elan3_thread.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/spinlock_elan3_thread.c     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/spinlock_elan3_thread.c  2005-06-01 23:12:54.681426640 -0400
+@@ -0,0 +1,44 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: spinlock_elan3_thread.c,v 1.9 2003/10/07 13:22:38 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/spinlock_elan3_thread.c,v $ */
++
++#include <qsnet/types.h>
++
++#include <elan3/e3types.h>
++#include <elan3/events.h>
++#include <elan3/elanregs.h>
++#include <elan3/intrinsics.h>
++
++#include <elan/nmh.h>
++#include <elan/kcomm.h>
++#include <elan/epcomms.h>
++
++#include "kcomm_elan3.h"
++#include "epcomms_elan3.h"
++
++void
++ep3_spinblock (EP3_SPINLOCK_ELAN *sle, EP3_SPINLOCK_MAIN *sl)
++{
++    do {
++      sl->sl_seq = sle->sl_seq;                       /* Release my lock */
++      
++      while (sle->sl_lock)                            /* Wait until the main */
++          c_break();                                  /* releases the lock */
++      
++      sle->sl_seq++;                                  /* and try and relock */
++    } while (sle->sl_lock);
++}
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/ep/statemap.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/statemap.c  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/statemap.c       2005-06-01 23:12:54.682426488 -0400
+@@ -0,0 +1,385 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: statemap.c,v 1.11.8.1 2004/11/18 12:05:00 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/statemap.c,v $ */
++
++#include <qsnet/kernel.h>
++#include <elan/statemap.h>
++
++/******************************** global state bitmap stuff **********************************/
++static int
++statemap_setmapbit (bitmap_t *map, int offset, int bit)
++{
++   bitmap_t *e    = &map[offset >> BT_ULSHIFT];
++   bitmap_t  mask = ((bitmap_t)1) << (offset & BT_ULMASK);
++   int       rc = ((*e) & mask) != 0;
++   
++   if (bit)
++   {
++      *e |= mask;
++      return (!rc);
++   }
++
++   *e &= ~mask;
++   return (rc);
++}
++
++static int
++statemap_firstsegbit (bitmap_t seg)
++{
++   int            bit = 0;
++   
++   if (seg == 0)
++      return (-1);
++
++#if (BT_ULSHIFT == 6)
++   if ((seg & 0xffffffffL) == 0)
++   {
++      seg >>= 32;
++      bit += 32;
++   }
++#elif (BT_ULSHIFT != 5)
++# error "Unexpected value of BT_ULSHIFT"
++#endif
++
++   if ((seg & 0xffff) == 0)
++   {
++      seg >>= 16;
++      bit += 16;
++   }
++      
++   if ((seg & 0xff) == 0)
++   {
++      seg >>= 8;
++      bit += 8;
++   }
++      
++   if ((seg & 0xf) == 0)
++   {
++      seg >>= 4;
++      bit += 4;
++   }
++      
++   if ((seg & 0x3) == 0)
++   {
++      seg >>= 2;
++      bit += 2;
++   }
++
++   return (((seg & 0x1) == 0) ? bit + 1 : bit);
++}
++
++bitmap_t
++statemap_getseg (statemap_t *map, unsigned int offset)
++{
++   ASSERT (offset < map->size);
++   ASSERT ((offset & BT_ULMASK) == 0);
++
++   return (map->bitmap[offset >> BT_ULSHIFT]);
++}
++
++void
++statemap_setseg (statemap_t *map, unsigned int offset, bitmap_t seg)
++{
++   ASSERT (offset < map->size);
++   ASSERT ((offset & BT_ULMASK) == 0);
++
++   offset >>= BT_ULSHIFT;
++   if (map->bitmap[offset] == seg)
++      return;
++
++   map->bitmap[offset] = seg;
++
++   if (statemap_setmapbit (map->changemap2, offset,       1) &&
++       statemap_setmapbit (map->changemap1, offset >>= BT_ULSHIFT, 1))
++      statemap_setmapbit (map->changemap0, offset >>= BT_ULSHIFT, 1);
++}
++
++bitmap_t
++statemap_getbits (statemap_t *map, unsigned int offset, int nbits)
++{
++   int      index = offset >> BT_ULSHIFT;
++   bitmap_t mask  = (nbits == BT_NBIPUL) ? (bitmap_t) -1 : (((bitmap_t)1) << nbits) - 1;
++   
++   ASSERT (nbits <= BT_NBIPUL);
++   ASSERT (offset + nbits <= map->size);
++
++   offset &= BT_ULMASK;
++   if (offset + nbits <= BT_NBIPUL)
++      return ((map->bitmap[index] >> offset) & mask);
++   
++   return (((map->bitmap[index] >> offset) |
++          (map->bitmap[index + 1] << (BT_NBIPUL - offset))) & mask);
++}
++
++void
++statemap_setbits (statemap_t *map, unsigned int offset, bitmap_t bits, int nbits)
++{
++   int      index = offset >> BT_ULSHIFT;
++   bitmap_t mask;
++   bitmap_t seg;
++   bitmap_t newseg;
++
++   ASSERT (nbits <= BT_NBIPUL);
++   ASSERT (offset + nbits <= map->size);
++
++   offset &= BT_ULMASK;
++   if (offset + nbits <= BT_NBIPUL)
++   {
++      mask = ((nbits == BT_NBIPUL) ? -1 : ((((bitmap_t)1) << nbits) - 1)) << offset;
++      seg = map->bitmap[index];
++      newseg = ((bits << offset) & mask) | (seg & ~mask);
++      
++      if (seg == newseg)
++       return;
++   
++      map->bitmap[index] = newseg;
++      
++      if (statemap_setmapbit (map->changemap2, index,       1) &&
++        statemap_setmapbit (map->changemap1, index >>= BT_ULSHIFT, 1))
++       statemap_setmapbit (map->changemap0, index >>= BT_ULSHIFT, 1);
++      return;
++   }
++   
++   mask = ((bitmap_t)-1) << offset;
++   seg = map->bitmap[index];
++   newseg = ((bits << offset) & mask) | (seg & ~mask);
++
++   if (seg != newseg)
++   {
++      map->bitmap[index] = newseg;
++      
++      if (statemap_setmapbit (map->changemap2, index,       1) &&
++        statemap_setmapbit (map->changemap1, index >> BT_ULSHIFT, 1))
++       statemap_setmapbit (map->changemap0, index >> (2 * BT_ULSHIFT), 1);
++   }
++   
++   index++;
++   offset = BT_NBIPUL - offset;
++   mask = (((bitmap_t)1) << (nbits - offset)) - 1;
++   seg = map->bitmap[index];
++   newseg = ((bits >> offset) & mask) | (seg & ~mask);
++   
++   if (seg == newseg)
++      return;
++   
++   map->bitmap[index] = newseg;
++   
++   if (statemap_setmapbit (map->changemap2, index,       1) &&
++       statemap_setmapbit (map->changemap1, index >>= BT_ULSHIFT, 1))
++      statemap_setmapbit (map->changemap0, index >>= BT_ULSHIFT, 1);
++}
++
++void
++statemap_zero (statemap_t *dst)
++{
++   int       size       = dst->size;
++   int       offset     = 0;
++   bitmap_t *changemap0 = dst->changemap0;
++   bitmap_t *changemap1 = dst->changemap1;
++   bitmap_t *changemap2 = dst->changemap2;
++   bitmap_t *dstmap     = dst->bitmap;
++   bitmap_t  bit0;
++   bitmap_t  bit1;
++   bitmap_t  bit2;
++
++   for (bit0 = 1; offset < size; bit0 <<= 1, changemap1++)
++   {
++      for (bit1 = 1; bit1 != 0 && offset < size; bit1 <<= 1, changemap2++)
++      {
++       for (bit2 = 1; bit2 != 0 && offset < size; bit2 <<= 1, dstmap++, offset += BT_NBIPUL)
++       {
++           *dstmap = 0;
++           *changemap2 |= bit2;
++       }
++       *changemap1 |= bit1;
++      }
++      *changemap0 |= bit0;
++   }
++}
++   
++void
++statemap_setmap (statemap_t *dst, statemap_t *src)
++{
++   int       size       = dst->size;
++   int       offset     = 0;
++   bitmap_t *changemap0 = dst->changemap0;
++   bitmap_t *changemap1 = dst->changemap1;
++   bitmap_t *changemap2 = dst->changemap2;
++   bitmap_t *dstmap     = dst->bitmap;
++   bitmap_t *srcmap     = src->bitmap;
++   bitmap_t  bit0;
++   bitmap_t  bit1;
++   bitmap_t  bit2;
++
++   ASSERT (src->size == size);
++   
++   for (bit0 = 1; offset < size; bit0 <<= 1, changemap1++)
++   {
++      for (bit1 = 1; bit1 != 0 && offset < size; bit1 <<= 1, changemap2++)
++      {
++       for (bit2 = 1; bit2 != 0 && offset < size; bit2 <<= 1, dstmap++, srcmap++, offset += BT_NBIPUL)
++          if (*dstmap != *srcmap)
++          {
++             *dstmap = *srcmap;
++             *changemap2 |= bit2;
++          }
++       if (*changemap2 != 0)
++          *changemap1 |= bit1;
++      }
++      if (*changemap1 != 0)
++       *changemap0 |= bit0;
++   }
++}
++
++void
++statemap_ormap (statemap_t *dst, statemap_t *src)
++{
++   int       size       = dst->size;
++   int       offset     = 0;
++   bitmap_t *changemap0 = dst->changemap0;
++   bitmap_t *changemap1 = dst->changemap1;
++   bitmap_t *changemap2 = dst->changemap2;
++   bitmap_t *dstmap     = dst->bitmap;
++   bitmap_t *srcmap     = src->bitmap;
++   bitmap_t  bit0;
++   bitmap_t  bit1;
++   bitmap_t  bit2;
++   bitmap_t  seg;
++
++   ASSERT (src->size == size);
++   
++   for (bit0 = 1; offset < size; bit0 <<= 1, changemap1++)
++   {
++      for (bit1 = 1; bit1 != 0 && offset < size; bit1 <<= 1, changemap2++)
++      {
++       for (bit2 = 1; bit2 != 0 && offset < size; bit2 <<= 1, dstmap++, srcmap++, offset += BT_NBIPUL)
++       {
++          seg = *dstmap | *srcmap;
++          if (*dstmap != seg)
++          {
++             *dstmap = seg;
++             *changemap2 |= bit2;
++          }
++       }
++       if (*changemap2 != 0)
++          *changemap1 |= bit1;
++      }
++      if (*changemap1 != 0)
++       *changemap0 |= bit0;
++   }
++}
++
++int
++statemap_findchange (statemap_t *map, bitmap_t *newseg, int clearchange)
++{
++   int          bit0;
++   bitmap_t    *cm1;
++   int          bit1;
++   bitmap_t    *cm2;
++   int          bit2;
++   unsigned int offset;
++
++   bit0 = statemap_firstsegbit (*(map->changemap0));
++   if (bit0 < 0)
++      return (-1);
++
++   offset = bit0;
++   cm1 = map->changemap1 + offset;
++   bit1 = statemap_firstsegbit (*cm1);
++   ASSERT (bit1 >= 0);
++
++   offset = (offset << BT_ULSHIFT) + bit1;
++   cm2 = map->changemap2 + offset;
++   bit2 = statemap_firstsegbit (*cm2);
++   ASSERT (bit2 >= 0);
++   
++   offset = (offset << BT_ULSHIFT) + bit2;
++   *newseg = map->bitmap[offset];
++
++   if (clearchange &&
++       (*cm2 &= ~(((bitmap_t)1) << bit2)) == 0 &&
++       (*cm1 &= ~(((bitmap_t)1) << bit1)) == 0)
++      map->changemap0[0] &= ~(((bitmap_t)1) << bit0);
++
++   return (offset << BT_ULSHIFT);
++}
++
++int
++statemap_changed (statemap_t *map)
++{
++   return ((*(map->changemap0) != 0));
++}
++
++void
++statemap_reset (statemap_t *map)
++{
++   bzero (map->changemap0, map->changemap_nob + map->bitmap_nob);
++}
++
++void
++statemap_copy (statemap_t *dst, statemap_t *src)
++{
++   ASSERT (dst->size == src->size);
++   bcopy (src->changemap0, dst->changemap0, src->changemap_nob + src->bitmap_nob);
++}
++
++void
++statemap_clearchanges (statemap_t *map)
++{
++   if (statemap_changed (map))
++      bzero (map->changemap0, map->changemap_nob);
++}
++
++bitmap_t *
++statemap_tobitmap (statemap_t *map)
++{
++    return (map->bitmap);
++}
++
++statemap_t *
++statemap_create (int size)
++{
++   int   struct_entries     = (sizeof (statemap_t) * 8 + (BT_NBIPUL-1)) >> BT_ULSHIFT;
++   int   bitmap_entries     = (size + (BT_NBIPUL-1)) >> BT_ULSHIFT;
++   int   changemap2_entries = (bitmap_entries + (BT_NBIPUL-1)) >> BT_ULSHIFT;
++   int   changemap1_entries = (changemap2_entries + (BT_NBIPUL-1)) >> BT_ULSHIFT;
++   int   changemap0_entries = (changemap1_entries + (BT_NBIPUL-1)) >> BT_ULSHIFT;
++   int   changemap_entries  = changemap0_entries + changemap1_entries + changemap2_entries;
++   int   nob                = (struct_entries + bitmap_entries + changemap_entries) * sizeof (bitmap_t);
++   statemap_t *map;
++
++   ASSERT ((1 << BT_ULSHIFT) == BT_NBIPUL);
++   ASSERT (changemap0_entries == 1);
++
++   KMEM_ZALLOC (map, statemap_t *, nob, 1);
++
++   map->size = size;
++   map->nob  = nob;
++   map->changemap_nob = changemap_entries * sizeof (bitmap_t);
++   map->bitmap_nob = bitmap_entries * sizeof (bitmap_t);
++   map->changemap0 = ((bitmap_t *)map) + struct_entries;
++   map->changemap1 = map->changemap0 + changemap0_entries;
++   map->changemap2 = map->changemap1 + changemap1_entries;
++   map->bitmap     = map->changemap2 + changemap2_entries;
++
++   return (map);
++}
++
++void
++statemap_destroy (statemap_t *map)
++{
++   KMEM_FREE (map, map->nob);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/ep/statusmon.h
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/statusmon.h 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/statusmon.h      2005-06-01 23:12:54.682426488 -0400
+@@ -0,0 +1,44 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: statusmon.h,v 1.6 2003/10/07 13:22:38 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/statusmon.h,v $*/
++
++#ifndef __ELAN3_STATUSMON_H
++#define __ELAN3_STATUSMON_H
++
++typedef struct statusmon_node
++{
++    u_int     NodeId;
++    u_int     State;
++} STATUSMON_SGMT;
++
++typedef struct statusmon_level
++{
++    unsigned     Width;
++    STATUSMON_SGMT Nodes[CM_SGMTS_PER_LEVEL];
++} STATUSMON_LEVEL;
++
++typedef struct statusmon_msg
++{
++    unsigned      Type;
++    unsigned      NodeId;
++    unsigned      NumLevels;
++    unsigned      TopLevel;
++    unsigned        Role;
++    STATUSMON_LEVEL Levels[CM_MAX_LEVELS];
++} STATUSMON_MSG;
++
++
++#endif /* __ELAN3_STATUSMON_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/ep/support.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/support.c   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/support.c        2005-06-01 23:12:54.683426336 -0400
+@@ -0,0 +1,109 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: support.c,v 1.37.8.1 2004/09/30 15:01:53 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/support.c,v $ */
++
++#include <qsnet/kernel.h>
++#include <elan/kcomm.h>
++
++/****************************************************************************************/
++/*
++ * Nodeset/flush callbacks.
++ */
++int
++ep_register_callback (EP_RAIL *rail, unsigned idx, void (*routine)(void *, statemap_t *), void *arg)
++{
++    EP_CALLBACK *cb;
++    
++    KMEM_ALLOC (cb, EP_CALLBACK *, sizeof (EP_CALLBACK), 1);
++    
++    cb->Routine = routine;
++    cb->Arg     = arg;
++
++    kmutex_lock (&rail->CallbackLock);
++    cb->Next = rail->CallbackList[idx];
++    rail->CallbackList[idx] = cb;
++    kmutex_unlock (&rail->CallbackLock);
++    
++    return (ESUCCESS);
++}
++
++void
++ep_remove_callback (EP_RAIL *rail, unsigned idx, void (*routine)(void *, statemap_t *), void *arg)
++{
++    EP_CALLBACK  *cb;
++    EP_CALLBACK **predp;
++
++    kmutex_lock (&rail->CallbackLock);
++    for (predp = &rail->CallbackList[idx]; (cb = *predp); predp = &cb->Next)
++      if (cb->Routine == routine && cb->Arg == arg)
++          break;
++
++    if (cb == NULL)
++      panic ("ep_remove_member_callback");
++    
++    *predp = cb->Next;
++    kmutex_unlock (&rail->CallbackLock);
++    
++    KMEM_FREE (cb, sizeof (EP_CALLBACK));
++}
++
++void
++ep_call_callbacks (EP_RAIL *rail, unsigned idx, statemap_t *map)
++{
++    EP_CALLBACK *cb;
++
++    kmutex_lock (&rail->CallbackLock);
++
++    rail->CallbackStep = idx;
++
++    for (cb = rail->CallbackList[idx]; cb; cb = cb->Next) {
++      (cb->Routine) (cb->Arg, map);
++    }
++    kmutex_unlock (&rail->CallbackLock);
++}
++
++unsigned int
++ep_backoff (EP_BACKOFF *backoff, int type)
++{
++    static int bcount[EP_NUM_BACKOFF] = {1, 16, 32, 64, 128, 256, 512, 1024};
++    
++    if (backoff->type != type)
++    {
++      backoff->type  = type;
++      backoff->indx  = 0;
++      backoff->count = 0;
++    }
++
++    if (++backoff->count > bcount[backoff->indx] && backoff->indx < (EP_NUM_BACKOFF-1))
++    {
++      backoff->indx++;
++      backoff->count = 0;
++    }
++
++    return (backoff->indx);
++}
++
++/* Generic checksum algorithm */
++uint16_t
++CheckSum (char *msg, int nob)
++{
++    uint16_t sum = 0;
++   
++    while (nob-- > 0)
++      sum = sum * 13 + *msg++;
++
++    return (sum);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/ep/support_elan3.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/support_elan3.c     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/support_elan3.c  2005-06-01 23:12:54.687425728 -0400
+@@ -0,0 +1,2111 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: support_elan3.c,v 1.42.8.3 2004/11/12 10:54:51 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/support_elan3.c,v $ */
++
++#include <qsnet/kernel.h>
++#include <qsnet/kthread.h>
++
++#include <elan/kcomm.h>
++#include <elan/epsvc.h>
++#include <elan/epcomms.h>
++
++#include "kcomm_vp.h"
++#include "kcomm_elan3.h"
++#include "epcomms_elan3.h"
++#include "debug.h"
++
++#include <elan3/thread.h>
++#include <elan3/urom_addrs.h>
++
++/****************************************************************************************/
++#define DMA_RING_NEXT_POS(ring)      ((ring)->Position+1 == ring->Entries ? 0 : ((ring)->Position+1))
++#define DMA_RING_PREV_POS(ring,pos)  ((pos) == 0 ? (ring)->Entries-1 : (pos) - 1)
++
++static int 
++DmaRingCreate (EP3_RAIL *rail, EP3_DMA_RING *ring, int ctxnum, int entries)
++{
++    unsigned long pgnum = (ctxnum * sizeof (E3_CommandPort)) / PAGE_SIZE;
++    unsigned long pgoff = (ctxnum * sizeof (E3_CommandPort)) & (PAGE_SIZE-1);
++    int           s;    
++        
++    /* set up the initial position */
++    ring->Entries  = entries;
++    ring->Position = 0;
++    
++    if (! (ring->pEvent = ep_alloc_elan (&rail->Generic, entries * sizeof (E3_BlockCopyEvent), 0, &ring->epEvent)))
++    {
++      ring->CommandPort = (ioaddr_t) NULL;
++      return (ENOMEM);
++    }
++    
++    if (! (ring->pDma = ep_alloc_elan (&rail->Generic, entries * sizeof (E3_DMA), 0, &ring->epDma)))
++    {
++      ep_free_elan (&rail->Generic, ring->epEvent, entries * sizeof (E3_BlockCopyEvent));
++
++      ring->CommandPort = (ioaddr_t) NULL;
++      return (ENOMEM);
++    }
++    
++    if (! (ring->pDoneBlk = ep_alloc_main (&rail->Generic, entries * sizeof (E3_uint32), 0, &ring->epDoneBlk)))
++    {
++      ep_free_elan (&rail->Generic, ring->epEvent, entries * sizeof (E3_BlockCopyEvent));
++      ep_free_elan (&rail->Generic, ring->epDma,   entries * sizeof (E3_DMA));
++
++      ring->CommandPort = (ioaddr_t) NULL;
++      return (ENOMEM);
++    }
++    
++    if (MapDeviceRegister (rail->Device, ELAN3_BAR_COMMAND_PORT, &ring->CommandPage, pgnum * PAGE_SIZE, PAGE_SIZE, &ring->CommandPageHandle) != ESUCCESS)
++    {
++      ep_free_elan (&rail->Generic, ring->epEvent,   entries * sizeof (E3_BlockCopyEvent));
++      ep_free_elan (&rail->Generic, ring->epDma,     entries * sizeof (E3_DMA));
++      ep_free_main (&rail->Generic, ring->epDoneBlk, entries * sizeof (E3_uint32));
++
++      ring->CommandPort = (ioaddr_t) NULL;
++      return (ENOMEM);
++    }
++    ring->CommandPort = ring->CommandPage + pgoff;
++      
++    for (s = 0; s < entries; s++)
++    {
++      /* setup the event */
++      elan3_sdram_writel(rail->Device, DMA_RING_EVENT(ring,s) + offsetof(E3_BlockCopyEvent,ev_Type),   
++                         EV_TYPE_BCOPY | EV_TYPE_DMA | DMA_RING_DMA_ELAN(ring, s));
++      elan3_sdram_writel(rail->Device, DMA_RING_EVENT(ring,s) + offsetof(E3_BlockCopyEvent,ev_Source), DMA_RING_DMA_ELAN(ring,s)  | EV_WCOPY);
++      elan3_sdram_writel(rail->Device, DMA_RING_EVENT(ring,s) + offsetof(E3_BlockCopyEvent,ev_Dest),   DMA_RING_DONE_ELAN(ring,s) | EV_TYPE_BCOPY_WORD );         
++
++      /* need to set all the doneBlks to appear that they have completed */
++      ring->pDoneBlk[s] = DMA_RING_DMA_ELAN(ring,s)  | EV_WCOPY;
++    }
++
++    return 0; /* success */
++}
++
++static void
++DmaRingRelease(EP3_RAIL *rail, EP3_DMA_RING *ring)
++{
++    if (ring->CommandPage != (ioaddr_t) 0)
++    {
++      UnmapDeviceRegister(rail->Device, &ring->CommandPageHandle);
++
++      ep_free_elan (&rail->Generic, ring->epEvent,   ring->Entries * sizeof (E3_BlockCopyEvent));
++      ep_free_elan (&rail->Generic, ring->epDma,     ring->Entries * sizeof (E3_DMA));
++      ep_free_main (&rail->Generic, ring->epDoneBlk, ring->Entries * sizeof (E3_uint32));
++    }
++    ring->CommandPage = (ioaddr_t) 0;
++}
++
++void 
++DmaRingsRelease (EP3_RAIL *rail)
++{
++    DmaRingRelease (rail, &rail->DmaRings[EP3_RING_CRITICAL]);
++    DmaRingRelease (rail, &rail->DmaRings[EP3_RING_HIGH_PRI]);
++    DmaRingRelease (rail, &rail->DmaRings[EP3_RING_LOW_PRI]);
++}
++
++int 
++DmaRingsCreate (EP3_RAIL *rail)
++{
++    if (DmaRingCreate (rail, &rail->DmaRings[EP3_RING_CRITICAL], ELAN3_DMARING_BASE_CONTEXT_NUM + EP3_RING_CRITICAL, EP3_RING_CRITICAL_LEN) ||
++      DmaRingCreate (rail, &rail->DmaRings[EP3_RING_HIGH_PRI], ELAN3_DMARING_BASE_CONTEXT_NUM + EP3_RING_HIGH_PRI, EP3_RING_HIGH_PRI_LEN) ||
++      DmaRingCreate (rail, &rail->DmaRings[EP3_RING_LOW_PRI],  ELAN3_DMARING_BASE_CONTEXT_NUM + EP3_RING_LOW_PRI,  EP3_RING_LOW_PRI_LEN))
++    {
++      DmaRingsRelease (rail);
++      return (ENOMEM);
++    }
++  
++    return 0;
++}
++
++static int 
++DmaRingNextSlot (EP3_DMA_RING *ring)
++{
++    int pos  = ring->Position;
++    int npos = DMA_RING_NEXT_POS(ring);
++
++    if (ring->pDoneBlk[npos] == EP3_EVENT_ACTIVE)
++      return (-1);
++    
++    ring->pDoneBlk[pos] = EP3_EVENT_ACTIVE;
++
++    ring->Position = npos; /* move on one */
++
++    return (pos);
++}
++
++
++/****************************************************************************************/
++/*
++ * Dma/event command issueing - these handle cproc queue overflow traps.
++ */
++static int
++DmaRunQueueSizeCheck (EP3_RAIL *rail, E3_uint32 len)
++{
++    E3_uint64  FandBPtr = read_reg64 (rail->Device, DProc_SysCntx_FPtr);
++    E3_uint32  FPtr, BPtr;
++    E3_uint32  qlen;
++
++#if (BYTE_ORDER == LITTLE_ENDIAN) || defined(__LITTLE_ENDIAN__)
++    FPtr = (FandBPtr & 0xFFFFFFFFull);
++    BPtr = (FandBPtr >> 32);
++#else
++    FPtr = (FandBPtr >> 32);
++    BPtr = (FandBPtr & 0xFFFFFFFFull);
++#endif
++    
++    qlen = (((BPtr - FPtr)/sizeof (E3_DMA)) & (E3_SysCntxQueueSize-1));
++    
++    if      (qlen < 4)   IncrStat (rail, DmaQueueLength[0]);
++    else if (qlen < 8)   IncrStat (rail, DmaQueueLength[1]);
++    else if (qlen < 16)  IncrStat (rail, DmaQueueLength[2]);
++    else if (qlen < 32)  IncrStat (rail, DmaQueueLength[3]);
++    else if (qlen < 64)  IncrStat (rail, DmaQueueLength[4]);
++    else if (qlen < 128) IncrStat (rail, DmaQueueLength[5]);
++    else if (qlen < 240) IncrStat (rail, DmaQueueLength[6]);
++    else                 IncrStat (rail, DmaQueueLength[7]);
++      
++    return (qlen < len);
++}
++
++int
++IssueDma (EP3_RAIL *rail, E3_DMA_BE * dmabe, int type, int retryThread)
++{
++    ELAN3_DEV     *dev = rail->Device;
++    EP3_RETRY_DMA *retry;
++    EP3_DMA_RING  *ring;
++    int           slot;
++    int           i, res;
++    unsigned long flags;
++
++    ASSERT (dmabe->s.dma_direction == DMA_WRITE || dmabe->s.dma_direction == DMA_READ_REQUEUE);
++
++    ASSERT (! EP_VP_ISDATA(dmabe->s.dma_destVProc) ||
++          (dmabe->s.dma_direction == DMA_WRITE ? 
++           EP_VP_TO_NODE(dmabe->s.dma_srcVProc) == rail->Generic.Position.pos_nodeid :
++           EP_VP_TO_NODE(dmabe->s.dma_destVProc) == rail->Generic.Position.pos_nodeid));
++    
++    /*
++     * If we're not the retry thread - then don't issue this DMA
++     * if there are any already queued on the retry lists with
++     * higher or equal priority than this one that are ready to
++     * retry.
++     */
++    if (! retryThread)
++    {
++      for (i = EP_RETRY_BASE; i < type; i++)
++      {
++          if (list_empty (&rail->DmaRetries[i]))
++              continue;
++
++          retry = list_entry (rail->DmaRetries[i].next, EP3_RETRY_DMA, Link);
++              
++          if (AFTER (lbolt, retry->RetryTime))
++          {
++              IncrStat (rail, IssueDmaFail[type]);
++              return (ISSUE_COMMAND_RETRY);
++          }
++      }
++    }
++
++    /*
++     * Depending on the type of DMA we're issuing - throttle back
++     * issueing of it if the DMA run queue is too full.  This then
++     * prioritises the "special" messages and completing data 
++     * transfers which have matched a receive buffer.
++     */
++
++    if (type >= EP_RETRY_LOW_PRI_RETRY)
++    {
++      if (! DmaRunQueueSizeCheck (rail, E3_SysCntxQueueSize / 2))
++      {
++          IncrStat (rail, IssueDmaFail[type]);
++          return (ISSUE_COMMAND_RETRY);
++      }
++      ring = &rail->DmaRings[EP3_RING_LOW_PRI];
++    } 
++    else if (type == EP_RETRY_LOW_PRI)
++    {
++      if (! DmaRunQueueSizeCheck (rail, E3_SysCntxQueueSize / 3))
++      {
++          IncrStat (rail, IssueDmaFail[type]);
++          return (ISSUE_COMMAND_RETRY);
++      }
++      ring = &rail->DmaRings[EP3_RING_LOW_PRI];
++    }
++    else if (type >= EP_RETRY_HIGH_PRI)
++      ring = &rail->DmaRings[EP3_RING_HIGH_PRI];
++    else
++      ring = &rail->DmaRings[EP3_RING_CRITICAL];
++
++    local_irq_save (flags);
++    if (! spin_trylock (&dev->CProcLock))
++    {
++      IncrStat (rail, IssueDmaFail[type]);
++
++      res = ISSUE_COMMAND_RETRY;
++    }
++    else
++    {
++      if ((slot = DmaRingNextSlot (ring)) == -1)
++      {
++          IncrStat (rail, IssueDmaFail[type]);
++          
++          res = ISSUE_COMMAND_RETRY;
++      }
++      else
++      {
++          EPRINTF4 (DBG_COMMAND, "IssueDma: type %08x size %08x Elan source %08x Elan dest %08x\n",
++                    dmabe->s.dma_type, dmabe->s.dma_size, dmabe->s.dma_source, dmabe->s.dma_dest);
++          EPRINTF2 (DBG_COMMAND, "          dst event %08x cookie/proc %08x\n",
++                    dmabe->s.dma_destEvent, dmabe->s.dma_destCookieVProc);
++          EPRINTF2 (DBG_COMMAND, "          src event %08x cookie/proc %08x\n",
++                    dmabe->s.dma_srcEvent, dmabe->s.dma_srcCookieVProc);
++
++          elan3_sdram_copyq_to_sdram (dev,  dmabe,  DMA_RING_DMA(ring, slot), sizeof (E3_DMA));                       /* PCI write block */
++          elan3_sdram_writel (dev, DMA_RING_EVENT(ring, slot) + offsetof (E3_BlockCopyEvent, ev_Count), 1);   /* PCI write */
++          
++          mb();                                                               /* ensure writes to main memory completed */
++          writel (DMA_RING_EVENT_ELAN(ring,slot), ring->CommandPort + offsetof (E3_CommandPort, SetEvent));
++          mmiob();                                                            /* and flush through IO writes */
++          
++          res = ISSUE_COMMAND_OK;
++      }
++      spin_unlock (&dev->CProcLock);
++    }
++    local_irq_restore (flags);
++
++    return (res);
++}
++
++int
++IssueWaitevent (EP3_RAIL *rail, E3_Addr value)
++{
++    ELAN3_DEV     *dev   = rail->Device;
++    int           res;
++    unsigned long flags;
++    
++    spin_lock_irqsave (&dev->IntrLock, flags);
++
++    ASSERT (rail->CommandPortEventTrap == FALSE);
++
++    /*
++     * Disable the command processor interrupts, so that we don't see
++     * spurious interrupts appearing.
++     */
++    DISABLE_INT_MASK (dev, INT_CProc | INT_ComQueue);
++
++    EPRINTF1 (DBG_COMMAND, "IssueWaitevent: %08x\n", value);
++
++    mb();                                                             /* ensure writes to main memory completed */
++    writel (value, rail->CommandPort + offsetof (E3_CommandPort, WaitEvent0));
++    mmiob();                                                          /* and flush through IO writes */
++    
++    do {
++      res = CheckCommandQueueFlushed (rail->Ctxt, EventComQueueNotEmpty, ISSUE_COMMAND_CANT_WAIT, &flags);
++
++      EPRINTF1 (DBG_COMMAND, "IssueWaitevent: CheckCommandQueueFlushed -> %d\n", res);
++
++      if (res == ISSUE_COMMAND_WAIT)
++          HandleCProcTrap (dev, 0, NULL);
++    } while (res != ISSUE_COMMAND_OK);
++
++    if (! rail->CommandPortEventTrap)
++      res = ISSUE_COMMAND_OK;
++    else
++    {
++      rail->CommandPortEventTrap = FALSE;
++      res = ISSUE_COMMAND_TRAPPED;
++    }
++
++    EPRINTF1 (DBG_COMMAND, "IssueWaitevent: -> %d\n", res);
++
++    /*
++     * Re-enable the command processor interrupt as we've finished 
++     * polling it.
++     */
++    ENABLE_INT_MASK (dev, INT_CProc | INT_ComQueue);
++
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++
++    return (res);
++}
++
++void
++IssueSetevent (EP3_RAIL *rail, E3_Addr value)
++{
++    EPRINTF1 (DBG_COMMAND, "IssueSetevent: %08x\n", value);
++
++    mb();                                                             /* ensure writes to main memory completed */
++    writel (value, rail->CommandPort + offsetof (E3_CommandPort, SetEvent));
++    mmiob();                                                          /* and flush through IO writes */
++}
++
++void
++IssueRunThread (EP3_RAIL *rail, E3_Addr value)
++{
++    EPRINTF1 (DBG_COMMAND, "IssueRunThread: %08x\n", value);
++
++    mb();                                                             /* ensure writes to main memory completed */
++    writel (value, rail->CommandPort + offsetof (E3_CommandPort, RunThread));
++    mmiob();                                                          /* and flush through IO writes */
++}
++
++/****************************************************************************************/
++/*
++ * DMA retry list management
++ */
++static unsigned DmaRetryTimes[EP_NUM_RETRIES]; 
++
++static void
++ep3_dma_retry (EP3_RAIL *rail)
++{
++    EP3_COOKIE    *cp;
++    int            res;
++    int                  vp;
++    unsigned long  flags;
++    int            i;
++
++    kernel_thread_init("ep3_dma_retry");
++
++    spin_lock_irqsave (&rail->DmaRetryLock, flags);
++
++    for (;;)
++    {
++      long yieldAt   = lbolt + (hz/10);
++      long retryTime = 0;
++
++      if (rail->DmaRetryThreadShouldStop)
++          break;
++      
++      for (i = EP_RETRY_BASE; i < EP_NUM_RETRIES; i++)
++      {
++          while (! list_empty (&rail->DmaRetries[i]))
++          {
++              EP3_RETRY_DMA *retry = list_entry (rail->DmaRetries[i].next, EP3_RETRY_DMA, Link);
++
++              if (! AFTER (lbolt, retry->RetryTime))
++                  break;
++              
++              if (rail->DmaRetryThreadShouldStall || AFTER (lbolt, yieldAt))
++                  goto cant_do_more;
++
++              EPRINTF2 (DBG_RETRY, "%s: DmaRetryThread: retry %p\n", rail->Generic.Name, retry);
++              EPRINTF5 (DBG_RETRY, "%s:                 %08x %08x %08x %08x\n",
++                        rail->Generic.Name, retry->Dma.s.dma_type, retry->Dma.s.dma_size, retry->Dma.s.dma_source, retry->Dma.s.dma_dest);
++              EPRINTF5 (DBG_RETRY, "%s:                 %08x %08x %08x %08x\n",
++                        rail->Generic.Name, retry->Dma.s.dma_destEvent, retry->Dma.s.dma_destCookieVProc,
++                        retry->Dma.s.dma_srcEvent, retry->Dma.s.dma_srcCookieVProc);
++#if defined(DEBUG)
++              if (retry->Dma.s.dma_direction == DMA_WRITE)
++                  cp = LookupEventCookie (rail, &rail->CookieTable, retry->Dma.s.dma_srcEvent);
++              else
++                  cp = LookupEventCookie (rail, &rail->CookieTable, retry->Dma.s.dma_destEvent);
++
++              ASSERT (cp != NULL || (retry->Dma.s.dma_srcEvent == 0 && retry->Dma.s.dma_direction == DMA_WRITE && retry->Dma.s.dma_isRemote));
++              
++              if (cp && cp->Operations->DmaVerify)
++                  cp->Operations->DmaVerify (rail, cp->Arg, &retry->Dma);
++#endif
++
++#if defined(DEBUG_ASSERT)
++              if (retry->Dma.s.dma_direction == DMA_WRITE)
++                  vp = retry->Dma.s.dma_destVProc;
++              else
++                  vp = retry->Dma.s.dma_srcVProc;
++
++              ASSERT (!EP_VP_ISDATA(vp) || 
++                      (rail->Generic.Nodes[EP_VP_TO_NODE(vp)].State >= EP_NODE_CONNECTED &&
++                       rail->Generic.Nodes[EP_VP_TO_NODE(vp)].State <= EP_NODE_LOCAL_PASSIVATE));
++#endif
++              spin_unlock_irqrestore (&rail->DmaRetryLock, flags);
++              res = IssueDma (rail, &(retry->Dma), i, TRUE);
++              spin_lock_irqsave (&rail->DmaRetryLock, flags);
++              
++              if (res != ISSUE_COMMAND_OK)
++                  goto cant_do_more;
++              
++              /* Command issued, so remove from list, and add to free list */
++              list_del (&retry->Link);
++              list_add (&retry->Link, &rail->DmaRetryFreeList);
++          }
++      }
++    cant_do_more:
++      
++      for (i = EP_RETRY_BASE; i < EP_NUM_RETRIES; i++)
++      {
++          if (!list_empty (&rail->DmaRetries[i]))
++          {
++              EP3_RETRY_DMA *retry = list_entry (rail->DmaRetries[i].next, EP3_RETRY_DMA, Link);
++
++              retryTime = retryTime ? MIN(retryTime, retry->RetryTime) : retry->RetryTime;
++          }
++      }
++
++      if (retryTime && !AFTER (retryTime, lbolt))
++          retryTime = lbolt + 1;
++
++      do {
++          EPRINTF3 (DBG_RETRY, "%s: ep_cm_retry: %s %lx\n", rail->Generic.Name, rail->DmaRetryThreadShouldStall ? "stalled" : "sleeping", retryTime);
++          
++          if (rail->DmaRetryTime == 0 || (retryTime != 0 && retryTime < rail->DmaRetryTime))
++              rail->DmaRetryTime = retryTime;
++          
++          rail->DmaRetrySleeping = TRUE;
++          
++          if (rail->DmaRetryThreadShouldStall)                                        /* wakeup threads waiting in StallDmaRetryThread */
++              kcondvar_wakeupall (&rail->DmaRetryWait, &rail->DmaRetryLock);  /* for us to really go to sleep for good. */
++
++          if (rail->DmaRetryTime == 0 || rail->DmaRetryThreadShouldStall)
++              kcondvar_wait (&rail->DmaRetryWait, &rail->DmaRetryLock, &flags);
++          else
++              kcondvar_timedwait (&rail->DmaRetryWait, &rail->DmaRetryLock, &flags, rail->DmaRetryTime);
++
++          rail->DmaRetrySleeping = FALSE;
++
++      } while (rail->DmaRetryThreadShouldStall);
++
++      rail->DmaRetryTime = 0;
++    }
++
++    rail->DmaRetryThreadStopped = 1;
++    kcondvar_wakeupall (&rail->DmaRetryWait, &rail->DmaRetryLock);
++    spin_unlock_irqrestore (&rail->DmaRetryLock, flags);
++
++    kernel_thread_exit();
++}
++
++void
++StallDmaRetryThread (EP3_RAIL *rail)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&rail->DmaRetryLock, flags);
++    rail->DmaRetryThreadShouldStall++;
++
++    while (! rail->DmaRetrySleeping)
++      kcondvar_wait (&rail->DmaRetryWait, &rail->DmaRetryLock, &flags);
++    spin_unlock_irqrestore (&rail->DmaRetryLock, flags);
++}
++
++void 
++ResumeDmaRetryThread (EP3_RAIL *rail)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&rail->DmaRetryLock, flags);
++
++    ASSERT (rail->DmaRetrySleeping);
++
++    if (--rail->DmaRetryThreadShouldStall == 0)
++    {
++      rail->DmaRetrySleeping = 0;
++      kcondvar_wakeupone (&rail->DmaRetryWait, &rail->DmaRetryLock);
++    }
++    spin_unlock_irqrestore (&rail->DmaRetryLock, flags);
++}
++
++int
++InitialiseDmaRetries (EP3_RAIL *rail)
++{
++    int i;
++
++    spin_lock_init (&rail->DmaRetryLock);
++    kcondvar_init (&rail->DmaRetryWait);
++
++    for (i = 0; i < EP_NUM_RETRIES; i++)
++      INIT_LIST_HEAD (&rail->DmaRetries[i]);
++
++    INIT_LIST_HEAD (&rail->DmaRetryFreeList);
++
++    DmaRetryTimes[EP_RETRY_HIGH_PRI]  = EP_RETRY_HIGH_PRI_TIME;
++
++    for (i =0 ; i < EP_NUM_BACKOFF; i++)
++      DmaRetryTimes[EP_RETRY_HIGH_PRI_RETRY+i] = EP_RETRY_HIGH_PRI_TIME << i;
++    
++    DmaRetryTimes[EP_RETRY_LOW_PRI] = EP_RETRY_LOW_PRI_TIME;
++
++    for (i =0 ; i < EP_NUM_BACKOFF; i++)
++      DmaRetryTimes[EP_RETRY_LOW_PRI_RETRY+i] = EP_RETRY_LOW_PRI_TIME << i;
++    
++    DmaRetryTimes[EP_RETRY_ANONYMOUS] = EP_RETRY_ANONYMOUS_TIME;
++    DmaRetryTimes[EP_RETRY_NETERR]    = EP_RETRY_NETERR_TIME;
++
++    rail->DmaRetryInitialised = 1;
++
++    if (kernel_thread_create (ep3_dma_retry, (void *) rail) == 0)
++    {
++      spin_lock_destroy (&rail->DmaRetryLock);
++      return (ENOMEM);
++    }
++
++    rail->DmaRetryThreadStarted = 1;
++
++    return (ESUCCESS);
++}
++
++void
++DestroyDmaRetries (EP3_RAIL *rail)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&rail->DmaRetryLock, flags);
++    rail->DmaRetryThreadShouldStop = 1;
++    while (rail->DmaRetryThreadStarted && !rail->DmaRetryThreadStopped)
++    {
++      kcondvar_wakeupall (&rail->DmaRetryWait, &rail->DmaRetryLock);
++      kcondvar_wait (&rail->DmaRetryWait, &rail->DmaRetryLock, &flags);
++    }
++    rail->DmaRetryThreadStarted = 0;
++    rail->DmaRetryThreadStopped = 0;
++    rail->DmaRetryThreadShouldStop = 0;
++    rail->DmaRetryInitialised = 0;
++
++    spin_unlock_irqrestore (&rail->DmaRetryLock, flags);
++
++    /* Everyone should have given back their retry dma's by now */
++    ASSERT (rail->DmaRetryReserved == 0);
++
++    while (! list_empty (&rail->DmaRetryFreeList))
++    {
++      EP3_RETRY_DMA *retry = list_entry (rail->DmaRetryFreeList.next, EP3_RETRY_DMA, Link);
++      
++      list_del (&retry->Link);
++
++      KMEM_FREE (retry, sizeof (EP3_RETRY_DMA));
++    }
++
++    kcondvar_destroy (&rail->DmaRetryWait);
++    spin_lock_destroy (&rail->DmaRetryLock);
++}
++
++int
++ReserveDmaRetries (EP3_RAIL *rail, int count, EP_ATTRIBUTE attr)
++{
++    EP3_RETRY_DMA *retry;
++    int                 remaining = count;
++    unsigned long flags;
++
++    spin_lock_irqsave (&rail->DmaRetryLock, flags);
++    
++    if (remaining <= (rail->DmaRetryCount - rail->DmaRetryReserved))
++    {
++      rail->DmaRetryReserved += remaining;
++
++      spin_unlock_irqrestore (&rail->DmaRetryLock, flags);
++      return (ESUCCESS);
++    }
++
++    remaining -= (rail->DmaRetryCount - rail->DmaRetryReserved);
++
++    rail->DmaRetryReserved = rail->DmaRetryCount;
++
++    spin_unlock_irqrestore (&rail->DmaRetryLock, flags);
++
++    while (remaining)
++    {
++      KMEM_ALLOC (retry, EP3_RETRY_DMA *, sizeof (EP3_RETRY_DMA), !(attr & EP_NO_SLEEP));
++      
++      if (retry == NULL)
++          goto failed;
++
++      /* clear E3_DMA */
++      bzero((char *)(&(retry->Dma.s)), sizeof(E3_DMA));
++
++      remaining--; 
++
++      spin_lock_irqsave (&rail->DmaRetryLock, flags);
++
++      list_add (&retry->Link, &rail->DmaRetryFreeList);
++
++      rail->DmaRetryCount++;
++      rail->DmaRetryReserved++;
++
++      spin_unlock_irqrestore (&rail->DmaRetryLock, flags);
++    }
++    return (ESUCCESS);
++
++ failed:
++    spin_lock_irqsave (&rail->DmaRetryLock, flags);
++    rail->DmaRetryReserved -= (count - remaining);
++    spin_unlock_irqrestore (&rail->DmaRetryLock, flags);
++    return (ENOMEM);
++}
++
++void
++ReleaseDmaRetries (EP3_RAIL *rail, int count)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&rail->DmaRetryLock, flags);
++    rail->DmaRetryReserved -= count;
++    spin_unlock_irqrestore (&rail->DmaRetryLock, flags);
++}
++
++void
++QueueDmaForRetry (EP3_RAIL *rail, E3_DMA_BE *dma, int interval)
++{
++    EP3_RETRY_DMA *retry;
++    unsigned long flags;
++
++    /*
++     * When requeueing DMAs they must never be "READ" dma's since
++     * these would fetch the DMA descriptor from the retryn descriptor
++     */
++    ASSERT (dma->s.dma_direction == DMA_WRITE || dma->s.dma_direction == DMA_READ_REQUEUE);
++    ASSERT (dma->s.dma_direction == DMA_WRITE ? 
++          EP_VP_TO_NODE(dma->s.dma_srcVProc) == rail->Generic.Position.pos_nodeid :
++          EP_VP_TO_NODE(dma->s.dma_destVProc) == rail->Generic.Position.pos_nodeid);
++
++    spin_lock_irqsave (&rail->DmaRetryLock, flags);
++    
++    EP_ASSERT (&rail->Generic, !list_empty (&rail->DmaRetryFreeList));
++
++    /* take an item of the free list */
++    retry = list_entry (rail->DmaRetryFreeList.next, EP3_RETRY_DMA, Link);
++
++    list_del (&retry->Link);
++    
++    EPRINTF5 (DBG_RETRY, "%s: QueueDmaForRetry: %08x %08x %08x %08x\n", rail->Generic.Name,
++            dma->s.dma_type, dma->s.dma_size, dma->s.dma_source, dma->s.dma_dest);
++    EPRINTF5 (DBG_RETRY, "%s:                   %08x %08x %08x %08x\n",rail->Generic.Name,
++           dma->s.dma_destEvent, dma->s.dma_destCookieVProc,
++           dma->s.dma_srcEvent, dma->s.dma_srcCookieVProc);
++
++    /* copy the DMA into the retry descriptor */
++    retry->Dma.s.dma_type            = dma->s.dma_type;
++    retry->Dma.s.dma_size            = dma->s.dma_size;
++    retry->Dma.s.dma_source          = dma->s.dma_source;
++    retry->Dma.s.dma_dest            = dma->s.dma_dest;
++    retry->Dma.s.dma_destEvent       = dma->s.dma_destEvent;
++    retry->Dma.s.dma_destCookieVProc = dma->s.dma_destCookieVProc;
++    retry->Dma.s.dma_srcEvent        = dma->s.dma_srcEvent;
++    retry->Dma.s.dma_srcCookieVProc  = dma->s.dma_srcCookieVProc;
++
++    retry->RetryTime = lbolt + DmaRetryTimes[interval];
++
++    /* chain onto the end of the approriate retry list */
++    list_add_tail (&retry->Link, &rail->DmaRetries[interval]);
++
++    /* now wakeup the retry thread */
++    if (rail->DmaRetryTime == 0 || retry->RetryTime < rail->DmaRetryTime)
++      rail->DmaRetryTime = retry->RetryTime;
++    
++    if (rail->DmaRetrySleeping && !rail->DmaRetryThreadShouldStall)
++    {
++      rail->DmaRetrySleeping = 0;
++      kcondvar_wakeupone (&rail->DmaRetryWait, &rail->DmaRetryLock);
++    }
++
++    spin_unlock_irqrestore (&rail->DmaRetryLock, flags);
++}
++
++void
++QueueDmaOnStalledList (EP3_RAIL *rail, E3_DMA_BE *dma)
++{
++    EP_NODE_RAIL *nodeRail = &rail->Generic.Nodes[dma->s.dma_direction == DMA_WRITE ? 
++                                                EP_VP_TO_NODE(dma->s.dma_srcVProc) :
++                                                EP_VP_TO_NODE(dma->s.dma_destVProc)];
++    EP3_RETRY_DMA *retry;
++    unsigned long flags;
++
++    /*
++     * When requeueing DMAs they must never be "READ" dma's since
++     * these would fetch the DMA descriptor from the retryn descriptor
++     */
++    ASSERT (dma->s.dma_direction == DMA_WRITE || dma->s.dma_direction == DMA_READ_REQUEUE);
++    ASSERT (dma->s.dma_direction == DMA_WRITE ? 
++          EP_VP_TO_NODE(dma->s.dma_srcVProc) == rail->Generic.Position.pos_nodeid :
++          EP_VP_TO_NODE(dma->s.dma_destVProc) == rail->Generic.Position.pos_nodeid);
++
++    spin_lock_irqsave (&rail->DmaRetryLock, flags);
++    
++    EP_ASSERT (&rail->Generic, !list_empty (&rail->DmaRetryFreeList));
++
++    /* take an item of the free list */
++    retry = list_entry (rail->DmaRetryFreeList.next, EP3_RETRY_DMA, Link);
++
++    list_del (&retry->Link);
++    
++    EPRINTF5 (DBG_RETRY, "%s: QueueDmaOnStalledList: %08x %08x %08x %08x\n", rail->Generic.Name,
++            dma->s.dma_type, dma->s.dma_size, dma->s.dma_source, dma->s.dma_dest);
++    EPRINTF5 (DBG_RETRY, "%s:                        %08x %08x %08x %08x\n", rail->Generic.Name,
++            dma->s.dma_destEvent, dma->s.dma_destCookieVProc,
++            dma->s.dma_srcEvent, dma->s.dma_srcCookieVProc);
++
++    /* copy the DMA into the retry descriptor */
++    retry->Dma.s.dma_type            = dma->s.dma_type;
++    retry->Dma.s.dma_size            = dma->s.dma_size;
++    retry->Dma.s.dma_source          = dma->s.dma_source;
++    retry->Dma.s.dma_dest            = dma->s.dma_dest;
++    retry->Dma.s.dma_destEvent       = dma->s.dma_destEvent;
++    retry->Dma.s.dma_destCookieVProc = dma->s.dma_destCookieVProc;
++    retry->Dma.s.dma_srcEvent        = dma->s.dma_srcEvent;
++    retry->Dma.s.dma_srcCookieVProc  = dma->s.dma_srcCookieVProc;
++
++    /* chain onto the node cancelled dma list */
++    list_add_tail (&retry->Link, &nodeRail->StalledDmas);
++
++    spin_unlock_irqrestore (&rail->DmaRetryLock, flags);
++}
++
++void
++FreeStalledDmas (EP3_RAIL *rail, unsigned int nodeId)
++{
++    EP_NODE_RAIL *nodeRail = &rail->Generic.Nodes[nodeId];
++    struct list_head *el, *nel;
++    unsigned long flags;
++
++    spin_lock_irqsave (&rail->DmaRetryLock, flags);
++    list_for_each_safe (el, nel, &nodeRail->StalledDmas) {
++      list_del (el);
++      list_add (el, &rail->DmaRetryFreeList);
++    }
++    spin_unlock_irqrestore (&rail->DmaRetryLock, flags);
++}
++
++/****************************************************************************************/
++/*
++ * Connection management.
++ */
++static void
++DiscardingHaltOperation (ELAN3_DEV *dev, void *arg)
++{
++    EP3_RAIL *rail = (EP3_RAIL *) arg;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++    rail->HaltOpCompleted = 1;
++    kcondvar_wakeupall (&rail->HaltOpSleep, &dev->IntrLock);
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++}
++ 
++typedef struct {
++     EP3_RAIL  *rail;
++    sdramaddr_t qaddr;
++} SetQueueFullData;
++ 
++static void
++SetQueueLockedOperation (ELAN3_DEV *dev, void *arg)
++{
++    SetQueueFullData *data =  (SetQueueFullData *) arg;
++    unsigned long     flags;     
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++
++    elan3_sdram_writel  (dev, data->qaddr, E3_QUEUE_LOCKED | elan3_sdram_readl(dev, data->qaddr));
++   
++    data->rail->HaltOpCompleted = 1;
++    kcondvar_wakeupall (&data->rail->HaltOpSleep, &dev->IntrLock);
++
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++}
++
++static void
++FlushDmaQueuesHaltOperation (ELAN3_DEV *dev, void *arg)
++{
++    EP3_RAIL      *rail    = (EP3_RAIL *) arg;
++    sdramaddr_t    FPtr, BPtr;
++    sdramaddr_t          Base, Top;
++    E3_DMA_BE      dma;
++    EP_NODE_RAIL  *node;
++    int            vp;
++    unsigned long  flags;
++
++    ASSERT (elan3_sdram_readl (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProc.s.FSR)) == 0);
++    ASSERT (elan3_sdram_readl (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProcData0.s.FSR.Status)) == 0);
++    ASSERT (elan3_sdram_readl (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProcData1.s.FSR.Status)) == 0);
++    ASSERT (elan3_sdram_readl (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProcData2.s.FSR.Status)) == 0);
++    ASSERT (elan3_sdram_readl (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProcData3.s.FSR.Status)) == 0);
++    
++    FPtr  = read_reg32 (dev, DProc_SysCntx_FPtr);
++    BPtr =  read_reg32 (dev, DProc_SysCntx_BPtr);
++    Base  = dev->TAndQBase + offsetof (E3_TrapAndQueue, SysCntxDmaQueue[0]);
++    Top   = dev->TAndQBase + offsetof (E3_TrapAndQueue, SysCntxDmaQueue[E3_SysCntxQueueSize-1]);
++    
++    while (FPtr != BPtr)
++    {
++      elan3_sdram_copyq_from_sdram (dev, FPtr, &dma, sizeof (E3_DMA_BE));
++      
++      EPRINTF5 (DBG_DISCON, "%s: FlushDmaQueuesHaltOperation: %08x %08x %08x %08x\n", rail->Generic.Name,
++                dma.s.dma_type, dma.s.dma_size, dma.s.dma_source, dma.s.dma_dest);
++      EPRINTF5 (DBG_DISCON, "%s:                              %08x %08x %08x %08x\n", rail->Generic.Name,
++                dma.s.dma_destEvent, dma.s.dma_destCookieVProc,
++               dma.s.dma_srcEvent, dma.s.dma_srcCookieVProc);
++      
++      ASSERT ((dma.s.dma_u.s.Context & SYS_CONTEXT_BIT) != 0);
++
++      if (dma.s.dma_direction == DMA_WRITE)
++          vp = dma.s.dma_destVProc;
++      else
++          vp = dma.s.dma_srcVProc;
++      
++      node = &rail->Generic.Nodes[EP_VP_TO_NODE(vp)];
++
++      ASSERT (!EP_VP_ISDATA(vp) || (node->State >= EP_NODE_CONNECTED && node->State <= EP_NODE_LOCAL_PASSIVATE));
++
++      if (EP_VP_ISDATA(vp) && node->State == EP_NODE_LOCAL_PASSIVATE)
++      {
++          /*
++           * This is a DMA going to the node which is being removed, 
++           * so move it onto the node dma list where it will get
++           * handled later.
++           */
++          EPRINTF1 (DBG_DISCON, "%s: FlushDmaQueuesHaltOperation: move dma to cancelled list\n", rail->Generic.Name);
++         
++          if (dma.s.dma_direction != DMA_WRITE)
++          {
++              /* for read dma's set the DMA_READ_REQUEUE bits as the dma_source has been 
++               * modified by the elan to point at the dma in the rxd where it was issued
++               * from */
++              dma.s.dma_direction = (dma.s.dma_direction & ~DMA_READ) | DMA_READ_REQUEUE;
++          }
++          
++          QueueDmaOnStalledList (rail, &dma);
++          
++          /*
++           * Remove the DMA from the queue by replacing it with one with
++           * zero size and no events.
++           *
++           * NOTE: we must preserve the SYS_CONTEXT_BIT since the Elan uses this
++           * to mark the approriate run queue as empty.
++           */
++          dma.s.dma_type            = (SYS_CONTEXT_BIT << 16);
++          dma.s.dma_size            = 0;
++          dma.s.dma_source          = (E3_Addr) 0;
++          dma.s.dma_dest            = (E3_Addr) 0;
++          dma.s.dma_destEvent       = (E3_Addr) 0;
++          dma.s.dma_destCookieVProc = 0;
++          dma.s.dma_srcEvent        = (E3_Addr) 0;
++          dma.s.dma_srcCookieVProc  = 0;
++          
++          elan3_sdram_copyq_to_sdram (dev, &dma, FPtr, sizeof (E3_DMA_BE));
++      }
++
++      FPtr = (FPtr == Top) ? Base : FPtr + sizeof (E3_DMA);
++    }
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++    rail->HaltOpCompleted = 1;
++    kcondvar_wakeupall (&rail->HaltOpSleep, &dev->IntrLock);
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++}
++
++void
++SetQueueLocked (EP3_RAIL *rail, sdramaddr_t qaddr)
++{
++    ELAN3_DEV        *dev = rail->Device;
++    SetQueueFullData  data;
++    unsigned long     flags;
++    
++    /* Ensure that the context filter changes have been seen by halting
++     * then restarting the inputters - this also ensures that any setevent
++     * commands used to issue dma's have completed and any trap has been
++     * handled. */
++    data.rail  = rail;
++    data.qaddr = qaddr;
++
++    kmutex_lock (&rail->HaltOpMutex);
++    spin_lock_irqsave (&dev->IntrLock, flags);
++    QueueHaltOperation (dev, 0, NULL, INT_DiscardingSysCntx | INT_TProcHalted, SetQueueLockedOperation, &data);
++
++    while (! rail->HaltOpCompleted)
++      kcondvar_wait (&rail->HaltOpSleep, &dev->IntrLock, &flags);
++    rail->HaltOpCompleted = 0;
++
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++    kmutex_unlock (&rail->HaltOpMutex);
++}
++
++void
++ep3_flush_filters (EP_RAIL *r)
++{
++    EP3_RAIL *rail = (EP3_RAIL *) r;
++    ELAN3_DEV *dev  = rail->Device;
++    unsigned long flags;
++
++    /* Ensure that the context filter changes have been seen by halting
++     * then restarting the inputters - this also ensures that any setevent
++     * commands used to issue dma's have completed and any trap has been
++     * handled. */
++    kmutex_lock (&rail->HaltOpMutex);
++    spin_lock_irqsave (&dev->IntrLock, flags);
++    QueueHaltOperation (dev, 0, NULL, INT_DiscardingSysCntx, DiscardingHaltOperation, rail);
++    
++    while (! rail->HaltOpCompleted)
++      kcondvar_wait (&rail->HaltOpSleep, &dev->IntrLock, &flags);
++    rail->HaltOpCompleted = 0;
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++    kmutex_unlock (&rail->HaltOpMutex);
++}
++
++void
++ep3_flush_queues (EP_RAIL *r)
++{
++    EP3_RAIL         *rail = (EP3_RAIL *) r;
++    ELAN3_DEV         *dev  = rail->Device;
++    struct list_head *el;
++    struct list_head *nel;
++    EP_NODE_RAIL     *node;
++    unsigned long flags;
++    int vp, i;
++
++    ASSERT (NO_LOCKS_HELD);
++    
++    /* First - stall the dma retry thread, so that it will no longer
++     *         restart any dma's from the rety lists. */
++    StallDmaRetryThread (rail);
++
++    /* Second - queue a halt operation to flush through all DMA's which are executing
++     *          or on the run queue. */
++    kmutex_lock (&rail->HaltOpMutex);
++    spin_lock_irqsave (&dev->IntrLock, flags);
++    QueueHaltOperation (dev, 0, NULL, INT_DProcHalted | INT_TProcHalted, FlushDmaQueuesHaltOperation, rail);
++    while (! rail->HaltOpCompleted)
++      kcondvar_wait (&rail->HaltOpSleep, &dev->IntrLock, &flags);
++    rail->HaltOpCompleted = 0;
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++    kmutex_unlock (&rail->HaltOpMutex);
++
++    /* Third - run down the dma retry lists and move all entries to the cancelled
++     *         list.  Any dma's which were on the run queues have already been
++     *         moved there */
++    spin_lock_irqsave (&rail->DmaRetryLock, flags);
++    for (i = EP_RETRY_BASE; i < EP_NUM_RETRIES; i++)
++    {
++      list_for_each_safe (el, nel, &rail->DmaRetries[i]) {
++          EP3_RETRY_DMA *retry = list_entry (el, EP3_RETRY_DMA, Link);
++
++          if (retry->Dma.s.dma_direction == DMA_WRITE)
++              vp = retry->Dma.s.dma_destVProc;
++          else
++              vp = retry->Dma.s.dma_srcVProc;
++          
++          node = &rail->Generic.Nodes[EP_VP_TO_NODE(vp)];
++          
++          ASSERT (!EP_VP_ISDATA(vp) || (node->State >= EP_NODE_CONNECTED && node->State <= EP_NODE_LOCAL_PASSIVATE));
++
++          if (EP_VP_ISDATA(vp) && node->State == EP_NODE_LOCAL_PASSIVATE)
++          {
++              EPRINTF5 (DBG_DISCON, "%s: FlushDmaQueues: %08x %08x %08x %08x\n",rail->Generic.Name,
++                        retry->Dma.s.dma_type, retry->Dma.s.dma_size, retry->Dma.s.dma_source, retry->Dma.s.dma_dest);
++              EPRINTF5 (DBG_DISCON, "%s:                 %08x %08x %08x %08x\n", rail->Generic.Name,
++                        retry->Dma.s.dma_destEvent, retry->Dma.s.dma_destCookieVProc,
++                        retry->Dma.s.dma_srcEvent, retry->Dma.s.dma_srcCookieVProc);
++
++              list_del (&retry->Link);
++
++              list_add_tail (&retry->Link, &node->StalledDmas);
++          }
++      }
++    }
++    spin_unlock_irqrestore (&rail->DmaRetryLock, flags);
++
++    /* Finally - allow the dma retry thread to run again */
++    ResumeDmaRetryThread (rail);
++}
++
++/****************************************************************************************/
++/* NOTE - we require that all cookies are non-zero, which is 
++ *        achieved because EP_VP_DATA() is non-zero for all
++ *        nodes */
++E3_uint32
++LocalCookie (EP3_RAIL *rail, unsigned remoteNode)
++{
++    E3_uint32     cookie;
++    unsigned long flags;
++
++    spin_lock_irqsave (&rail->CookieLock, flags);
++    cookie = DMA_COOKIE (rail->MainCookies[remoteNode], EP_VP_DATA(rail->Generic.Position.pos_nodeid));
++    spin_unlock_irqrestore (&rail->CookieLock, flags);
++
++    /* Main processor cookie for srcCookie - this is what is sent
++     * to the remote node along with the setevent from the put
++     * or the dma descriptor for a get */
++    return (cookie);
++}
++
++E3_uint32
++RemoteCookie (EP3_RAIL *rail, u_int remoteNode)
++{
++    uint32_t      cookie;
++    unsigned long flags;
++
++    spin_lock_irqsave (&rail->CookieLock, flags);
++    cookie = DMA_REMOTE_COOKIE (rail->MainCookies[remoteNode], EP_VP_DATA(remoteNode));
++    spin_unlock_irqrestore (&rail->CookieLock, flags);
++
++    /* Main processor cookie for dstCookie - this is the cookie
++     * that the "remote put" dma uses for it's setevent packets for
++     * a get dma */
++    
++    return (cookie);
++}
++
++/****************************************************************************************/
++/*
++ * Event Cookie management.
++ *
++ *   We find the ep_cookie in one of two ways:
++ *     1) for block copy events
++ *          the cookie value is stored in the ev_Source - for EVIRQ events
++ *          it is also stored in the ev_Type
++ *     2) for normal events
++ *          we just use the event address.
++ */
++void 
++InitialiseCookieTable (EP3_COOKIE_TABLE *table)
++{
++    register int i;
++    
++    spin_lock_init (&table->Lock);
++    
++    for (i = 0; i < EP3_COOKIE_HASH_SIZE; i++)
++      table->Entries[i] = NULL;
++}
++
++void
++DestroyCookieTable (EP3_COOKIE_TABLE *table)
++{
++    register int i;
++
++    for (i = 0; i < EP3_COOKIE_HASH_SIZE; i++)
++      if (table->Entries[i])
++          printk ("DestroyCookieTable: entry %d not empty\n", i);
++
++    spin_lock_destroy (&table->Lock);
++}
++
++void
++RegisterCookie (EP3_COOKIE_TABLE *table, EP3_COOKIE *cp, E3_uint32 cookie, EP3_COOKIE_OPS *ops, void *arg)
++{
++    EP3_COOKIE *tcp;
++    int hashval = EP3_HASH_COOKIE(cookie);
++    unsigned long flags;
++
++    spin_lock_irqsave (&table->Lock, flags);
++    
++    cp->Operations = ops;
++    cp->Arg        = arg;
++    cp->Cookie     = cookie;
++    
++#if defined(DEBUG)
++    /* Check that the cookie is unique */
++    for (tcp = table->Entries[hashval]; tcp; tcp = tcp->Next)
++      if (tcp->Cookie == cookie)
++          panic ("RegisterEventCookie: non unique cookie\n");
++#endif
++    cp->Next = table->Entries[hashval];
++    
++    table->Entries[hashval] = cp;
++    
++    spin_unlock_irqrestore (&table->Lock, flags);
++}
++
++void
++DeregisterCookie (EP3_COOKIE_TABLE *table, EP3_COOKIE *cp)
++{
++    EP3_COOKIE **predCookiep;
++    unsigned long flags;
++
++    spin_lock_irqsave (&table->Lock, flags);
++    
++    for (predCookiep = &table->Entries[EP3_HASH_COOKIE (cp->Cookie)]; *predCookiep; predCookiep = &(*predCookiep)->Next)
++    {
++      if (*predCookiep == cp)
++      {
++          *predCookiep = cp->Next;
++          break;
++      }
++    }
++
++    spin_unlock_irqrestore (&table->Lock, flags);
++
++    cp->Operations = NULL;
++    cp->Arg        = NULL;
++    cp->Cookie     = 0;
++    cp->Next       = NULL;
++}
++
++EP3_COOKIE *
++LookupCookie (EP3_COOKIE_TABLE *table, E3_Addr cookie)
++{
++    EP3_COOKIE *cp;
++    unsigned long flags;
++
++    spin_lock_irqsave (&table->Lock, flags);
++    
++    for (cp = table->Entries[EP3_HASH_COOKIE(cookie)]; cp; cp = cp->Next)
++      if (cp->Cookie == cookie)
++          break;
++    
++    spin_unlock_irqrestore (&table->Lock, flags);
++    return (cp);
++}
++
++EP3_COOKIE *
++LookupEventCookie (EP3_RAIL *rail, EP3_COOKIE_TABLE *table, E3_Addr eaddr)
++{
++    sdramaddr_t event;
++    E3_uint32 type;
++
++    if ((event = ep_elan2sdram (&rail->Generic, eaddr)) != (sdramaddr_t) 0)
++    {
++      type = elan3_sdram_readl (rail->Device, event + offsetof (E3_BlockCopyEvent, ev_Type));
++
++      if (type & EV_TYPE_BCOPY)
++          return (LookupCookie (table, elan3_sdram_readl (rail->Device, event + offsetof (E3_BlockCopyEvent, ev_Source)) & ~EV_WCOPY));
++      else
++          return (LookupCookie (table, eaddr));
++    }
++
++    return (NULL);
++}
++
++/****************************************************************************************/
++/*
++ * Elan context operations - note only support interrupt ops.
++ */
++static int        ep3_event     (ELAN3_CTXT *ctxt, E3_uint32 cookie, int flag);
++static int        ep3_dprocTrap (ELAN3_CTXT *ctxt, DMA_TRAP *trap);
++static int        ep3_tprocTrap (ELAN3_CTXT *ctxt, THREAD_TRAP *trap);
++static int        ep3_iprocTrap (ELAN3_CTXT *ctxt, INPUT_TRAP *trap, int chan);
++static int        ep3_cprocTrap (ELAN3_CTXT *ctxt, COMMAND_TRAP *trap);
++static int        ep3_cprocReissue (ELAN3_CTXT *ctxt, CProcTrapBuf_BE *tbuf);
++
++static E3_uint8   ep3_load8 (ELAN3_CTXT *ctxt, E3_Addr addr);
++static void       ep3_store8 (ELAN3_CTXT *ctxt, E3_Addr addr, E3_uint8 val);
++static E3_uint16  ep3_load16 (ELAN3_CTXT *ctxt, E3_Addr addr);
++static void       ep3_store16 (ELAN3_CTXT *ctxt, E3_Addr addr, E3_uint16 val);
++static E3_uint32  ep3_load32 (ELAN3_CTXT *ctxt, E3_Addr addr);
++static void       ep3_store32 (ELAN3_CTXT *ctxt, E3_Addr addr, E3_uint32 val);
++static E3_uint64  ep3_load64 (ELAN3_CTXT *ctxt, E3_Addr addr);
++static void       ep3_store64 (ELAN3_CTXT *ctxt, E3_Addr addr, E3_uint64 val);
++
++ELAN3_OPS ep3_elan3_ops = 
++{
++    ELAN3_OPS_VERSION,                /* Version */
++    
++    NULL,                     /* Exception */
++    NULL,                     /* GetWordItem */
++    NULL,                     /* GetBlockItem */
++    NULL,                     /* PutWordItem */
++    NULL,                     /* PutBlockItem */
++    NULL,                     /* PutbackItem */
++    NULL,                     /* FreeWordItem */
++    NULL,                     /* FreeBlockItem */
++    NULL,                     /* CountItems */
++    ep3_event,                        /* Event */
++    NULL,                     /* SwapIn */
++    NULL,                     /* SwapOut */
++    NULL,                     /* FreePrivate */
++    NULL,                     /* FixupNetworkError */
++    ep3_dprocTrap,            /* DProcTrap */
++    ep3_tprocTrap,            /* TProcTrap */
++    ep3_iprocTrap,            /* IProcTrap */
++    ep3_cprocTrap,            /* CProcTrap */
++    ep3_cprocReissue,         /* CProcReissue */
++    NULL,                     /* StartFaultCheck */
++    NULL,                     /* EndFaulCheck */
++    ep3_load8,                        /* Load8 */
++    ep3_store8,                       /* Store8 */
++    ep3_load16,                       /* Load16 */
++    ep3_store16,              /* Store16 */
++    ep3_load32,                       /* Load32 */
++    ep3_store32,              /* Store32 */
++    ep3_load64,                       /* Load64 */
++    ep3_store64,              /* Store64 */
++};
++
++static int
++ep3_event (ELAN3_CTXT *ctxt, E3_uint32 cookie, int flag)
++{
++    EP3_RAIL  *rail = (EP3_RAIL *) ctxt->Private;
++    EP3_COOKIE *cp   = LookupCookie (&rail->CookieTable, cookie);
++    
++    if (cp == NULL)
++    {
++      printk ("ep3_event: cannot find event cookie for %x\n", cookie);
++      return (OP_HANDLED);
++    }
++    
++    if (cp->Operations->Event)
++      cp->Operations->Event(rail, cp->Arg);
++    
++    return (OP_HANDLED);
++}
++
++/* Trap interface */
++int
++ep3_dprocTrap (ELAN3_CTXT *ctxt, DMA_TRAP *trap)
++{
++    EP3_RAIL        *rail = (EP3_RAIL *) ctxt->Private;
++    ELAN3_DEV        *dev = rail->Device;
++    EP3_COOKIE       *cp;
++    E3_FaultSave_BE *FaultArea;
++    E3_uint16        vp;
++    int                    validTrap;
++    int                    numFaults;
++    int                    i;
++    sdramaddr_t      event;
++    E3_uint32        type;
++    sdramaddr_t      dma;
++    E3_DMA_BE        dmabe;
++    int              status = EAGAIN;
++
++    EPRINTF4 (DBG_EPTRAP, "ep3_dprocTrap: WakeupFnt=%x Cntx=%x SuspAddr=%x TrapType=%s\n",
++            trap->Status.s.WakeupFunction, trap->Status.s.Context, 
++            trap->Status.s.SuspendAddr, MiToName (trap->Status.s.TrapType));
++    EPRINTF4 (DBG_EPTRAP, "              type %08x size %08x source %08x dest %08x\n",
++            trap->Desc.s.dma_type, trap->Desc.s.dma_size, trap->Desc.s.dma_source, trap->Desc.s.dma_dest);
++    EPRINTF2 (DBG_EPTRAP, "              Dest event %08x cookie/proc %08x\n",
++            trap->Desc.s.dma_destEvent, trap->Desc.s.dma_destCookieVProc);
++    EPRINTF2 (DBG_EPTRAP, "              Source event %08x cookie/proc %08x\n",
++            trap->Desc.s.dma_srcEvent, trap->Desc.s.dma_srcCookieVProc);
++
++    ASSERT (trap->Status.s.Context & SYS_CONTEXT_BIT);
++
++    switch (trap->Status.s.TrapType)
++    {
++    case MI_DmaPacketTimedOutOrPacketError:
++      if (trap->Desc.s.dma_direction == DMA_WRITE)
++          vp = trap->Desc.s.dma_destVProc;
++      else
++          vp = trap->Desc.s.dma_srcVProc;
++      
++      if (! trap->PacketInfo.s.PacketTimeout)
++          status = ETIMEDOUT;
++      else
++      {
++          status = EHOSTDOWN;
++
++          /* XXXX: dma timedout - might want to "restart" tree ? */
++      }
++      goto retry_dma;
++
++    case MI_DmaFailCountError:
++      goto retry_dma;
++
++    case MI_TimesliceDmaQueueOverflow:
++      IncrStat (rail, DprocDmaQueueOverflow);
++
++      goto retry_dma;
++
++    case MI_RemoteDmaCommand:
++    case MI_RunDmaCommand:
++    case MI_DequeueNonSysCntxDma:
++    case MI_DequeueSysCntxDma:
++      /*
++       * The DMA processor has trapped due to outstanding prefetches from the previous 
++       * dma.  The "current" dma has not been consumed, so we just ignore the trap
++       */
++      return (OP_HANDLED);
++      
++    case MI_EventQueueOverflow:
++      IncrStat (rail, DprocEventQueueOverflow);
++
++      if ((event = ep_elan2sdram (&rail->Generic, trap->Desc.s.dma_srcEvent)) != (sdramaddr_t) 0 &&
++          ((type  = elan3_sdram_readl (dev, event + offsetof(E3_Event,ev_Type))) & EV_TYPE_MASK_EVIRQ) == EV_TYPE_EVIRQ)
++      {
++          spin_unlock (&ctxt->Device->IntrLock);
++          ep3_event (ctxt, (type & ~(EV_TYPE_MASK_EVIRQ | EV_TYPE_MASK_BCOPY)), OP_LWP);
++          spin_lock (&ctxt->Device->IntrLock);
++      }
++      return (OP_HANDLED);
++      
++    case MI_DmaQueueOverflow:
++      IncrStat (rail, DprocDmaQueueOverflow);
++
++      if ((event = ep_elan2sdram (&rail->Generic, trap->Desc.s.dma_srcEvent)) != (sdramaddr_t) 0 &&
++          ((type = elan3_sdram_readl (dev, event + offsetof (E3_Event, ev_Type))) & EV_TYPE_MASK_DMA) == EV_TYPE_DMA &&
++          (dma  = ep_elan2sdram (&rail->Generic, (type & ~EV_TYPE_MASK2))) != (sdramaddr_t) 0)
++      {
++          elan3_sdram_copyq_from_sdram (dev, dma, &dmabe, sizeof (E3_DMA));
++          
++          /* We only chain together DMA's of the same direction, so since
++           * we took a DmaQueueOverflow trap - this means that DMA which
++           * trapped was a WRITE dma - hence the one we chain to must also
++           * be a WRITE dma.
++           */
++          ASSERT (dmabe.s.dma_direction == DMA_WRITE);
++          
++          cp = LookupEventCookie (rail, &rail->CookieTable, dmabe.s.dma_srcEvent);
++
++#ifdef DEBUG_ASSERT
++          {
++              E3_uint16     vp       = dmabe.s.dma_destVProc;
++              EP_NODE_RAIL *nodeRail = &rail->Generic.Nodes[EP_VP_TO_NODE(vp)];
++
++              ASSERT (cp != NULL && (!EP_VP_ISDATA(vp) || (nodeRail->State >= EP_NODE_CONNECTED && nodeRail->State <= EP_NODE_LOCAL_PASSIVATE)));
++          }
++#endif
++          cp->Operations->DmaRetry (rail, cp->Arg, &dmabe, EAGAIN);
++          
++          return (OP_HANDLED);
++      }
++
++      panic ("ep3_dprocTrap\n");
++      return (OP_HANDLED);
++
++    default:
++      break;
++    }
++
++    /* If it's a dma which traps past the end of the source, then */
++    /* just re-issue it */
++    numFaults = validTrap = (trap->FaultSave.s.FSR.Status != 0);
++    for (i = 0, FaultArea = &trap->Data0; i < 4; i++, FaultArea++)
++    {
++      if (FaultArea->s.FSR.Status != 0)
++      {
++          numFaults++;
++
++          /* XXXX: Rev B Elans can prefetch data past the end of the dma descriptor */
++          /*       if the fault relates to this, then just ignore it */
++          if (FaultArea->s.FaultAddress >= (trap->Desc.s.dma_source+trap->Desc.s.dma_size))
++          {
++              static int i;
++              if (i < 10 && i++ < 10)
++                  printk ("ep3_dprocTrap: Rev B prefetch trap error %08x %08x\n",
++                           FaultArea->s.FaultAddress, (trap->Desc.s.dma_source+trap->Desc.s.dma_size));
++              continue;
++          }
++
++          validTrap++;
++      }
++    }
++
++    /*
++     * NOTE: for physical errors (uncorrectable ECC/PCI parity errors) the FSR will
++     *       be zero - hence we will not see any faults - and none will be valid, 
++     *       so only ignore a Rev B prefetch trap if we've seen some faults. Otherwise
++     *       we can reissue a DMA which has already sent it's remote event !
++     */
++    if (numFaults != 0 && validTrap == 0)
++    {
++    retry_dma:
++      if (trap->Desc.s.dma_direction == DMA_WRITE)
++      {
++          vp = trap->Desc.s.dma_destVProc;
++          cp = LookupEventCookie (rail, &rail->CookieTable, trap->Desc.s.dma_srcEvent);
++      }
++      else
++      {
++          ASSERT (EP3_CONTEXT_ISDATA(trap->Desc.s.dma_queueContext) || trap->Desc.s.dma_direction == DMA_READ_REQUEUE);
++
++          vp = trap->Desc.s.dma_srcVProc;
++          cp = LookupEventCookie (rail, &rail->CookieTable, trap->Desc.s.dma_destEvent);
++
++          /* for read dma's set the DMA_READ_REQUEUE bits as the dma_source has been 
++           * modified by the elan to point at the dma in the rxd where it was issued
++           * from */
++          trap->Desc.s.dma_direction = (trap->Desc.s.dma_direction & ~DMA_READ) | DMA_READ_REQUEUE;
++      }
++
++#ifdef DEBUG_ASSERT
++      {
++          EP_NODE_RAIL *nodeRail = &rail->Generic.Nodes[EP_VP_TO_NODE(vp)];
++
++          ASSERT (!EP_VP_ISDATA(vp) || (nodeRail->State >= EP_NODE_CONNECTED && nodeRail->State <= EP_NODE_LOCAL_PASSIVATE));
++      }
++#endif
++      
++      if (cp != NULL)
++          cp->Operations->DmaRetry (rail, cp->Arg, &trap->Desc, status);
++      else
++      {
++          ASSERT (trap->Desc.s.dma_direction == DMA_WRITE && trap->Desc.s.dma_srcEvent == 0 && trap->Desc.s.dma_isRemote);
++
++          QueueDmaForRetry (rail, &trap->Desc, EP_RETRY_ANONYMOUS);
++      }
++
++      return (OP_HANDLED);
++    }
++    
++    printk ("ep3_dprocTrap: WakeupFnt=%x Cntx=%x SuspAddr=%x TrapType=%s\n",
++           trap->Status.s.WakeupFunction, trap->Status.s.Context, 
++           trap->Status.s.SuspendAddr, MiToName (trap->Status.s.TrapType));
++    printk ("                    FaultAddr=%x EventAddr=%x FSR=%x\n",
++           trap->FaultSave.s.FaultAddress, trap->FaultSave.s.EventAddress,
++           trap->FaultSave.s.FSR.Status);
++    for (i = 0, FaultArea = &trap->Data0; i < 4; i++, FaultArea++)
++      printk ("                  %d FaultAddr=%x EventAddr=%x FSR=%x\n", i,
++               FaultArea->s.FaultAddress, FaultArea->s.EventAddress, FaultArea->s.FSR.Status);
++    
++    printk ("                  type %08x size %08x source %08x dest %08x\n",
++           trap->Desc.s.dma_type, trap->Desc.s.dma_size, trap->Desc.s.dma_source, trap->Desc.s.dma_dest);
++    printk ("                  Dest event %08x cookie/proc %08x\n",
++           trap->Desc.s.dma_destEvent, trap->Desc.s.dma_destCookieVProc);
++    printk ("                  Source event %08x cookie/proc %08x\n",
++           trap->Desc.s.dma_srcEvent, trap->Desc.s.dma_srcCookieVProc);
++
++//    panic ("ep3_dprocTrap");
++
++    return (OP_HANDLED);
++}
++
++int
++ep3_tprocTrap (ELAN3_CTXT *ctxt, THREAD_TRAP *trap)
++{
++    EP3_RAIL *rail = (EP3_RAIL *) ctxt->Private;
++
++    EPRINTF6 (DBG_EPTRAP, "ep3_tprocTrap: SP=%08x PC=%08x NPC=%08x DIRTY=%08x TRAP=%08x MI=%s\n",
++            trap->sp, trap->pc, trap->npc, trap->DirtyBits.Bits, trap->TrapBits.Bits, MiToName (trap->mi));
++    EPRINTF4 (DBG_EPTRAP, "              g0=%08x g1=%08x g2=%08x g3=%08x\n", 
++            trap->Registers[REG_GLOBALS+(0^WordEndianFlip)], trap->Registers[REG_GLOBALS+(1^WordEndianFlip)], 
++            trap->Registers[REG_GLOBALS+(2^WordEndianFlip)], trap->Registers[REG_GLOBALS+(3^WordEndianFlip)]);
++    EPRINTF4 (DBG_EPTRAP, "              g4=%08x g5=%08x g6=%08x g7=%08x\n", 
++            trap->Registers[REG_GLOBALS+(4^WordEndianFlip)], trap->Registers[REG_GLOBALS+(5^WordEndianFlip)], 
++            trap->Registers[REG_GLOBALS+(6^WordEndianFlip)], trap->Registers[REG_GLOBALS+(7^WordEndianFlip)]);
++    EPRINTF4 (DBG_EPTRAP, "              o0=%08x o1=%08x o2=%08x o3=%08x\n", 
++            trap->Registers[REG_OUTS+(0^WordEndianFlip)], trap->Registers[REG_OUTS+(1^WordEndianFlip)], 
++            trap->Registers[REG_OUTS+(2^WordEndianFlip)], trap->Registers[REG_OUTS+(3^WordEndianFlip)]);
++    EPRINTF4 (DBG_EPTRAP, "              o4=%08x o5=%08x o6=%08x o7=%08x\n", 
++            trap->Registers[REG_OUTS+(4^WordEndianFlip)], trap->Registers[REG_OUTS+(5^WordEndianFlip)], 
++            trap->Registers[REG_OUTS+(6^WordEndianFlip)], trap->Registers[REG_OUTS+(7^WordEndianFlip)]);
++    EPRINTF4 (DBG_EPTRAP, "              l0=%08x l1=%08x l2=%08x l3=%08x\n", 
++            trap->Registers[REG_LOCALS+(0^WordEndianFlip)], trap->Registers[REG_LOCALS+(1^WordEndianFlip)], 
++            trap->Registers[REG_LOCALS+(2^WordEndianFlip)], trap->Registers[REG_LOCALS+(3^WordEndianFlip)]);
++    EPRINTF4 (DBG_EPTRAP, "              l4=%08x l5=%08x l6=%08x l7=%08x\n", 
++            trap->Registers[REG_LOCALS+(4^WordEndianFlip)], trap->Registers[REG_LOCALS+(5^WordEndianFlip)], 
++            trap->Registers[REG_LOCALS+(6^WordEndianFlip)], trap->Registers[REG_LOCALS+(7^WordEndianFlip)]);
++    EPRINTF4 (DBG_EPTRAP, "              i0=%08x i1=%08x i2=%08x i3=%08x\n", 
++            trap->Registers[REG_INS+(0^WordEndianFlip)], trap->Registers[REG_INS+(1^WordEndianFlip)], 
++            trap->Registers[REG_INS+(2^WordEndianFlip)], trap->Registers[REG_INS+(3^WordEndianFlip)]);
++    EPRINTF4 (DBG_EPTRAP, "              i4=%08x i5=%08x i6=%08x i7=%08x\n", 
++            trap->Registers[REG_INS+(4^WordEndianFlip)], trap->Registers[REG_INS+(5^WordEndianFlip)], 
++            trap->Registers[REG_INS+(6^WordEndianFlip)], trap->Registers[REG_INS+(7^WordEndianFlip)]);
++    
++    ASSERT (trap->Status.s.Context & SYS_CONTEXT_BIT);
++
++    switch (trap->mi)
++    {
++    case MI_UnimplementedError:
++      if (trap->TrapBits.s.ForcedTProcTrap)
++      {
++          ASSERT (trap->TrapBits.s.OutputWasOpen == 0);
++          
++          EPRINTF0 (DBG_EPTRAP, "ep3_tprocTrap: ForcedTProcTrap\n");
++
++          IssueRunThread (rail, SaveThreadToStack (ctxt, trap, FALSE));
++          return (OP_HANDLED);
++      }
++
++      if (trap->TrapBits.s.ThreadTimeout)
++      {
++          EPRINTF0 (DBG_EPTRAP, "ep3_tprocTrap: ThreadTimeout\n");
++
++          if (trap->Registers[REG_GLOBALS + (1^WordEndianFlip)] == 0)
++              RollThreadToClose (ctxt, trap, trap->TrapBits.s.PacketAckValue);
++          else
++          {
++              CompleteEnvelope (rail, trap->Registers[REG_GLOBALS + (1^WordEndianFlip)], trap->TrapBits.s.PacketAckValue);
++
++              RollThreadToClose (ctxt, trap, EP3_PAckStolen);
++          }
++              
++          IssueRunThread (rail, SaveThreadToStack (ctxt, trap, FALSE));
++          return (OP_HANDLED);
++      }
++
++      if (trap->TrapBits.s.Unimplemented)
++      {
++          E3_uint32 instr = ELAN3_OP_LOAD32 (ctxt, trap->pc & PC_MASK);
++
++          PRINTF1 (ctxt, DBG_EPTRAP, "ep3_tprocTrap: unimplemented instruction %08x\n", instr);
++
++          if ((instr & OPCODE_MASK) == OPCODE_Ticc &&
++              (instr & OPCODE_IMM)  == OPCODE_IMM &&
++              (Ticc_COND(instr)     == Ticc_TA))
++          {
++              switch (INSTR_IMM(instr))
++              {
++              case EP3_UNIMP_TRAP_NO_DESCS:
++                  StallThreadForNoDescs (rail, trap->Registers[REG_GLOBALS + (1^WordEndianFlip)], 
++                                         SaveThreadToStack (ctxt, trap, TRUE));
++                  return (OP_HANDLED);
++
++              case EP3_UNIMP_TRAP_PACKET_NACKED:
++                  CompleteEnvelope (rail, trap->Registers[REG_GLOBALS + (1^WordEndianFlip)], E3_PAckDiscard);
++
++                  IssueRunThread (rail, SaveThreadToStack (ctxt, trap, TRUE));
++                  return (OP_HANDLED);
++
++              case EP3_UNIMP_THREAD_HALTED: 
++                  StallThreadForHalted (rail, trap->Registers[REG_GLOBALS + (1^WordEndianFlip)], 
++                                        SaveThreadToStack (ctxt, trap, TRUE));
++                  return (OP_HANDLED);
++
++              default:
++                  break;
++                  
++              }
++          }
++      }
++      break;
++
++    default:
++      break;
++    }
++
++    /* All other traps should not happen for kernel comms */
++    printk ("ep3_tprocTrap: SP=%08x PC=%08x NPC=%08x DIRTY=%08x TRAP=%08x MI=%s\n",
++           trap->sp, trap->pc, trap->npc, trap->DirtyBits.Bits,
++           trap->TrapBits.Bits, MiToName (trap->mi));
++    printk ("              FaultSave : FaultAddress %08x EventAddress %08x FSR %08x\n",
++           trap->FaultSave.s.FaultAddress, trap->FaultSave.s.EventAddress, trap->FaultSave.s.FSR.Status);
++    printk ("              DataFault : FaultAddress %08x EventAddress %08x FSR %08x\n",
++           trap->DataFaultSave.s.FaultAddress, trap->DataFaultSave.s.EventAddress, trap->DataFaultSave.s.FSR.Status);
++    printk ("              InstFault : FaultAddress %08x EventAddress %08x FSR %08x\n",
++           trap->InstFaultSave.s.FaultAddress, trap->InstFaultSave.s.EventAddress, trap->InstFaultSave.s.FSR.Status);
++    printk ("              OpenFault : FaultAddress %08x EventAddress %08x FSR %08x\n",
++           trap->OpenFaultSave.s.FaultAddress, trap->OpenFaultSave.s.EventAddress, trap->OpenFaultSave.s.FSR.Status);
++
++    if (trap->DirtyBits.s.GlobalsDirty)
++    {
++      printk ("              g0=%08x g1=%08x g2=%08x g3=%08x\n", 
++               trap->Registers[REG_GLOBALS+(0^WordEndianFlip)], trap->Registers[REG_GLOBALS+(1^WordEndianFlip)], 
++               trap->Registers[REG_GLOBALS+(2^WordEndianFlip)], trap->Registers[REG_GLOBALS+(3^WordEndianFlip)]);
++      printk ("              g4=%08x g5=%08x g6=%08x g7=%08x\n", 
++               trap->Registers[REG_GLOBALS+(4^WordEndianFlip)], trap->Registers[REG_GLOBALS+(5^WordEndianFlip)], 
++               trap->Registers[REG_GLOBALS+(6^WordEndianFlip)], trap->Registers[REG_GLOBALS+(7^WordEndianFlip)]);
++    }
++    if (trap->DirtyBits.s.OutsDirty)
++    {
++      printk ("              o0=%08x o1=%08x o2=%08x o3=%08x\n", 
++               trap->Registers[REG_OUTS+(0^WordEndianFlip)], trap->Registers[REG_OUTS+(1^WordEndianFlip)], 
++               trap->Registers[REG_OUTS+(2^WordEndianFlip)], trap->Registers[REG_OUTS+(3^WordEndianFlip)]);
++      printk ("              o4=%08x o5=%08x o6=%08x o7=%08x\n", 
++               trap->Registers[REG_OUTS+(4^WordEndianFlip)], trap->Registers[REG_OUTS+(5^WordEndianFlip)], 
++               trap->Registers[REG_OUTS+(6^WordEndianFlip)], trap->Registers[REG_OUTS+(7^WordEndianFlip)]);
++    }
++    if (trap->DirtyBits.s.LocalsDirty)
++    {
++      printk ("              l0=%08x l1=%08x l2=%08x l3=%08x\n", 
++               trap->Registers[REG_LOCALS+(0^WordEndianFlip)], trap->Registers[REG_LOCALS+(1^WordEndianFlip)], 
++               trap->Registers[REG_LOCALS+(2^WordEndianFlip)], trap->Registers[REG_LOCALS+(3^WordEndianFlip)]);
++      printk ("              l4=%08x l5=%08x l6=%08x l7=%08x\n", 
++               trap->Registers[REG_LOCALS+(4^WordEndianFlip)], trap->Registers[REG_LOCALS+(5^WordEndianFlip)], 
++               trap->Registers[REG_LOCALS+(6^WordEndianFlip)], trap->Registers[REG_LOCALS+(7^WordEndianFlip)]);
++    }
++    if (trap->DirtyBits.s.InsDirty)
++    {
++      printk ("              i0=%08x i1=%08x i2=%08x i3=%08x\n", 
++               trap->Registers[REG_INS+(0^WordEndianFlip)], trap->Registers[REG_INS+(1^WordEndianFlip)], 
++               trap->Registers[REG_INS+(2^WordEndianFlip)], trap->Registers[REG_INS+(3^WordEndianFlip)]);
++      printk ("              i4=%08x i5=%08x i6=%08x i7=%08x\n", 
++               trap->Registers[REG_INS+(4^WordEndianFlip)], trap->Registers[REG_INS+(5^WordEndianFlip)], 
++               trap->Registers[REG_INS+(6^WordEndianFlip)], trap->Registers[REG_INS+(7^WordEndianFlip)]);
++    }
++    
++//    panic ("ep3_tprocTrap");
++
++    return (OP_HANDLED);
++}
++
++int
++ep3_iprocTrap (ELAN3_CTXT *ctxt, INPUT_TRAP *trap, int channel)
++{
++    EP3_RAIL      *rail = (EP3_RAIL *) ctxt->Private;
++    ELAN3_DEV      *dev = ctxt->Device;
++    EP3_COOKIE    *cp;
++    sdramaddr_t    event;
++    E3_uint32      type;
++    sdramaddr_t    dma;
++    E3_DMA_BE      dmabe;
++
++    ASSERT (trap->Transactions[0].s.TrTypeCntx.s.Context & SYS_CONTEXT_BIT);
++
++    /*
++     * first process the trap to determine the cause
++     */
++    InspectIProcTrap (ctxt, trap);
++
++    if (! trap->AckSent && trap->LockQueuePointer)            /* Must be a network error in a queueing DMA */
++    {                                                         /* packet - unlock the queue */
++      IncrStat (rail, QueueingPacketTrap);
++
++      SimulateUnlockQueue (ctxt, trap->LockQueuePointer, FALSE);
++      return (OP_HANDLED);
++    }
++
++    if (trap->AckSent && trap->BadTransaction)
++    {
++      spin_unlock (&dev->IntrLock);
++
++      /* NOTE - no network error fixup is necessary for system context
++       *        messages since they are idempotent and are single packet 
++       *        dmas
++       */
++      if (EP3_CONTEXT_ISDATA (trap->Transactions[0].s.TrTypeCntx.s.Context))
++      {
++          int nodeId = EP3_CONTEXT_TO_NODE(trap->Transactions[0].s.TrTypeCntx.s.Context);
++          
++          if (trap->DmaIdentifyTransaction)
++              ep_queue_network_error (&rail->Generic, nodeId, EP_NODE_NETERR_ATOMIC_PACKET, channel, trap->DmaIdentifyTransaction->s.TrAddr);
++          else if (trap->ThreadIdentifyTransaction)
++              ep_queue_network_error (&rail->Generic, nodeId, EP_NODE_NETERR_ATOMIC_PACKET, channel, trap->ThreadIdentifyTransaction->s.TrAddr);
++          else
++              ep_queue_network_error (&rail->Generic, nodeId, EP_NODE_NETERR_DMA_PACKET, channel, 0);
++      }
++
++      spin_lock (&dev->IntrLock);
++      return (OP_HANDLED);
++    }
++    
++    if (trap->AckSent)
++    {
++      if (trap->TrappedTransaction == NULL)
++          return (OP_HANDLED);
++      
++      while (! trap->TrappedTransaction->s.TrTypeCntx.s.LastTrappedTrans)
++      {
++          E3_IprocTrapHeader_BE *hdrp  = trap->TrappedTransaction;
++          E3_IprocTrapData_BE   *datap = trap->TrappedDataBuffer;
++          
++          ASSERT (hdrp->s.TrTypeCntx.s.StatusRegValid != 0);
++          
++          if ((hdrp->s.TrTypeCntx.s.Type & TR_WRITEBLOCK_BIT) != 0)
++          {
++              printk ("ep3_iprocTrap: WRITEBLOCK : Addr %x\n", hdrp->s.TrAddr);
++//            panic ("ep3_iprocTrap\n");
++          }
++          else
++          {
++              switch (hdrp->s.TrTypeCntx.s.Type & TR_OPCODE_TYPE_MASK)
++              {
++              case TR_SETEVENT & TR_OPCODE_TYPE_MASK:
++                  switch (GET_STATUS_TRAPTYPE (hdrp->s.IProcTrapStatus))
++                  {
++                  case MI_DmaQueueOverflow:
++                      IncrStat (rail, IprocDmaQueueOverflow);
++
++                      if ((event = ep_elan2sdram (&rail->Generic, hdrp->s.TrAddr)) != (sdramaddr_t) 0 &&
++                          ((type = elan3_sdram_readl (dev, event + offsetof (E3_Event, ev_Type))) & EV_TYPE_MASK_DMA) == EV_TYPE_DMA &&
++                          (dma  = ep_elan2sdram (&rail->Generic, (type & ~EV_TYPE_MASK2))) != (sdramaddr_t) 0)
++                      {
++                          elan3_sdram_copyq_from_sdram (dev, dma, &dmabe, sizeof (E3_DMA));
++                          
++                          if (dmabe.s.dma_direction == DMA_WRITE)
++                              cp = LookupEventCookie (rail, &rail->CookieTable, dmabe.s.dma_srcEvent);
++                          else
++                          {
++                              cp = LookupEventCookie (rail, &rail->CookieTable, dmabe.s.dma_destEvent);
++                              
++                              /* we MUST convert this into a DMA_READ_REQUEUE dma as if we don't the 
++                               * DMA descriptor will be read from the EP3_RETRY_DMA rather than the 
++                               * original DMA - this can then get reused and an incorrect DMA 
++                               * descriptor sent 
++                               * eventp->ev_Type contains the dma address with type in the lower bits 
++                               */ 
++                              
++                              dmabe.s.dma_source    = (type & ~EV_TYPE_MASK2);
++                              dmabe.s.dma_direction = (dmabe.s.dma_direction & ~DMA_READ) | DMA_READ_REQUEUE;
++                          }
++
++#ifdef DEBUG_ASSERT
++                          {
++                              E3_uint16     vp       = (dmabe.s.dma_direction == DMA_WRITE ? dmabe.s.dma_destVProc : dmabe.s.dma_srcVProc);
++                              EP_NODE_RAIL *nodeRail = &rail->Generic.Nodes[EP_VP_TO_NODE(vp)];
++
++                              ASSERT (!EP_VP_ISDATA(vp) || (nodeRail->State >= EP_NODE_CONNECTED && nodeRail->State <= EP_NODE_LOCAL_PASSIVATE));
++                          }
++#endif
++
++                          if (cp != NULL)
++                              cp->Operations->DmaRetry (rail, cp->Arg, &dmabe, EAGAIN);
++                          else
++                          {
++                              ASSERT (dmabe.s.dma_direction == DMA_WRITE && dmabe.s.dma_srcEvent == 0 && dmabe.s.dma_isRemote);
++                              
++                              QueueDmaForRetry (rail, &dmabe, EP_RETRY_ANONYMOUS);
++                          }
++                          break;
++                      }
++
++                      printk ("ep3_iprocTrap: SETEVENT : %x - cannot find dma to restart\n", hdrp->s.TrAddr);
++//                    panic ("ep3_iprocTrap\n");
++                      break;
++
++                  case MI_EventQueueOverflow:
++                  {
++                      sdramaddr_t event;
++                      E3_uint32   type;
++
++                      IncrStat (rail, IprocEventQueueOverflow);
++
++                      if ((event = ep_elan2sdram (&rail->Generic, hdrp->s.TrAddr)) != (sdramaddr_t) 0 &&
++                          ((type = elan3_sdram_readl (dev, event + offsetof (E3_Event, ev_Type))) & EV_TYPE_MASK_EVIRQ) == EV_TYPE_EVIRQ)
++                      {
++                          spin_unlock (&dev->IntrLock);
++                          ep3_event (ctxt, (type & ~(EV_TYPE_MASK_EVIRQ|EV_TYPE_MASK_BCOPY)), OP_LWP);
++                          spin_lock (&dev->IntrLock);
++
++                          break;
++                      }
++
++                      printk ("ep3_iprocTrap: SETEVENT : %x - cannot find event\n", hdrp->s.TrAddr);
++//                    panic ("ep3_iprocTrap\n");
++                      break;
++                  }
++
++                  default:
++                      printk ("ep3_iprocTrap: SETEVENT : %x MI=%x\n", hdrp->s.TrAddr, GET_STATUS_TRAPTYPE(hdrp->s.IProcTrapStatus));
++//                    panic ("ep3_iprocTrap\n");
++                      break;
++                  }
++                  break;
++                  
++              case TR_SENDDISCARD & TR_OPCODE_TYPE_MASK:
++                  /* Just ignore send-discard transactions */
++                  break;
++                  
++              case TR_REMOTEDMA & TR_OPCODE_TYPE_MASK:
++              {
++                  E3_DMA_BE *dmap = (E3_DMA_BE *) datap;
++
++                  if (GET_STATUS_TRAPTYPE(hdrp->s.IProcTrapStatus) != MI_DmaQueueOverflow)
++                  {
++                      printk ("ep3_iprocTrap: MI=%x\n", GET_STATUS_TRAPTYPE(hdrp->s.IProcTrapStatus));
++                      break;
++                  }
++
++                  IncrStat (rail, IprocDmaQueueOverflow);
++
++                  cp = LookupEventCookie (rail, &rail->CookieTable, dmap->s.dma_srcEvent);
++
++                  /* modify the dma type since it will still be a "read" dma */
++                  dmap->s.dma_type = (dmap->s.dma_type & ~DMA_TYPE_READ) | DMA_TYPE_ISREMOTE;
++
++#ifdef DEBUG_ASSERT
++                  {
++                      E3_uint16     vp       = dmap->s.dma_destVProc;
++                      EP_NODE_RAIL *nodeRail = &rail->Generic.Nodes[EP_VP_TO_NODE(vp)];
++                      
++                      ASSERT (!EP_VP_ISDATA(vp) || (nodeRail->State >= EP_NODE_CONNECTED && nodeRail->State <= EP_NODE_LOCAL_PASSIVATE));
++                  }
++#endif
++                  if (cp != NULL)
++                      cp->Operations->DmaRetry (rail, cp->Arg, dmap, EAGAIN);
++                  else
++                  {
++                      ASSERT (dmap->s.dma_direction == DMA_WRITE && dmap->s.dma_srcEvent == 0 && dmap->s.dma_isRemote);
++                      
++                      QueueDmaForRetry (rail, dmap, EP_RETRY_ANONYMOUS);
++                  }
++                  break;
++              }   
++              default:
++                  printk ("ep3_iprocTrap: %s\n", IProcTrapString (hdrp, datap));
++                  break;
++              }
++          }
++          
++          /*
++           * We've successfully processed this transaction, so move onto the 
++           * next one.
++           */
++          trap->TrappedTransaction++;
++          trap->TrappedDataBuffer++;
++      }
++
++      return (OP_HANDLED);
++    }
++    
++    /* Workaround WRITEBLOCK transaction executed when LOCKQUEUE transaction missed */
++    if ((trap->TrappedTransaction->s.TrTypeCntx.s.Type & TR_WRITEBLOCK_BIT) &&        /* a DMA packet */
++      trap->LockQueuePointer == 0 && trap->UnlockQueuePointer &&              /* a queueing DMA */
++      trap->TrappedTransaction->s.TrAddr == trap->FaultSave.s.FaultAddress)   /* and missed lockqueue */
++    {
++      printk ("ep3_iprocTrap: missed lockqueue transaction for queue %x\n", trap->UnlockQueuePointer);
++      return (OP_HANDLED);
++    }
++
++    if (trap->FaultSave.s.FaultContext != 0)
++      printk ("ep3_iprocTrap: pagefault at %08x in context %x\n", 
++              trap->FaultSave.s.FaultAddress, trap->FaultSave.s.FaultContext);
++
++//    panic ("ep3_iprocTrap: unexpected inputter trap\n");
++    
++    return (OP_HANDLED);
++}
++
++/*
++ * Command processor trap
++ *   kernel comms should only be able to generate
++ *   queue overflow traps
++ */
++int
++ep3_cprocTrap (ELAN3_CTXT *ctxt, COMMAND_TRAP *trap)
++{
++    EP3_RAIL     *rail   = (EP3_RAIL *) ctxt->Private;
++    int           ctxnum = (trap->TrapBuf.r.Breg >> 16) & MAX_ROOT_CONTEXT_MASK;
++    ELAN3_DEV     *dev    = rail->Device;
++    EP3_DMA_RING  *ring;
++    EP3_COOKIE   *cp;
++    E3_DMA_BE     dmabe;
++    int           vp, slot;
++    unsigned long flags;
++
++    switch (trap->Status.s.TrapType)
++    {
++    case MI_DmaQueueOverflow:
++      IncrStat (rail, CprocDmaQueueOverflow);
++
++      /* Use the context number that the setevent was issued in,
++       * to find the appropriate dma ring, then since they are guaranteed
++       * to be issued in order, we just search backwards till we find the
++       * last one which has completed its word copy - this must be the
++       * one which had caused the DmaQueueOverflow trap ! */
++
++      ASSERT (ctxnum >= ELAN3_DMARING_BASE_CONTEXT_NUM && ctxnum < (ELAN3_DMARING_BASE_CONTEXT_NUM+EP3_NUM_RINGS));
++
++      spin_lock_irqsave (&dev->CProcLock, flags);
++
++      ring = &rail->DmaRings[ctxnum - ELAN3_DMARING_BASE_CONTEXT_NUM];
++      slot = DMA_RING_PREV_POS(ring, ring->Position);
++      
++      while (ring->pDoneBlk[slot] == EP3_EVENT_ACTIVE)
++          slot = DMA_RING_PREV_POS(ring, slot);
++      
++      elan3_sdram_copyq_from_sdram (rail->Device , DMA_RING_DMA(ring,slot), &dmabe, sizeof (E3_DMA));
++
++#if defined(DEBUG_ASSERT)
++      while (slot != DMA_RING_PREV_POS(ring, ring->Position))
++      {
++          ASSERT (ring->pDoneBlk[slot] != EP3_EVENT_ACTIVE);
++          
++          slot = DMA_RING_PREV_POS(ring, slot);
++      }
++#endif
++      spin_unlock_irqrestore (&dev->CProcLock, flags);
++
++      if (dmabe.s.dma_direction == DMA_WRITE)
++          cp = LookupEventCookie (rail, &rail->CookieTable, dmabe.s.dma_srcEvent);
++      else
++      {
++          ASSERT (dmabe.s.dma_direction = DMA_READ_REQUEUE);
++
++          cp = LookupEventCookie (rail, &rail->CookieTable, dmabe.s.dma_destEvent);
++      }
++
++#if defined(DEBUG_ASSERT)
++      if (dmabe.s.dma_direction == DMA_WRITE)
++          vp = dmabe.s.dma_destVProc;
++      else
++          vp = dmabe.s.dma_srcVProc;
++      
++      ASSERT (!EP_VP_ISDATA(vp) || (rail->Generic.Nodes[EP_VP_TO_NODE(vp)].State >= EP_NODE_CONNECTED &&
++                                    rail->Generic.Nodes[EP_VP_TO_NODE(vp)].State <= EP_NODE_LOCAL_PASSIVATE));
++#endif
++
++      if (cp != NULL)
++          cp->Operations->DmaRetry (rail, cp->Arg, &dmabe, EAGAIN);
++      else
++      {
++          ASSERT (dmabe.s.dma_direction == DMA_WRITE && dmabe.s.dma_srcEvent == 0 && dmabe.s.dma_isRemote);
++          
++          QueueDmaForRetry (rail, &dmabe, EP_RETRY_ANONYMOUS);
++      }
++      
++      return (OP_HANDLED);
++
++    case MI_EventQueueOverflow:
++      ASSERT (ctxnum == ELAN3_MRF_CONTEXT_NUM);
++
++      IncrStat (rail, CprocEventQueueOverflow);
++      
++      rail->CommandPortEventTrap = TRUE;
++      return (OP_HANDLED);
++      
++#if defined(PER_CPU_TIMEOUT)
++    case MI_SetEventReadWait:
++      if (ctxnum == ELAN3_MRF_CONTEXT_NUM && trap->FaultSave.s.EventAddress == EP_PACEMAKER_EVENTADDR)
++      {
++          HeartbeatPacemaker (rail);
++          return (OP_HANDLED);
++      }
++#endif
++
++    default:
++      printk ("ep3_cprocTrap : Context=%x Status=%x TrapType=%x\n", ctxnum, trap->Status.Status, trap->Status.s.TrapType);
++      printk ("               FaultAddr=%x EventAddr=%x FSR=%x\n",
++               trap->FaultSave.s.FaultAddress, trap->FaultSave.s.EventAddress,
++               trap->FaultSave.s.FSR.Status);
++      break;
++    }
++
++//    panic ("ep3_cprocTrap");
++
++    return (OP_HANDLED);
++}
++
++static int
++ep3_cprocReissue (ELAN3_CTXT *ctxt, CProcTrapBuf_BE *tbuf)
++{
++    EP3_RAIL   *rail    = (EP3_RAIL *) ctxt->Private;
++    unsigned  cmdoff = (tbuf->s.ContextType >> 5) & 0xFF;
++    int       ctxnum = (tbuf->s.ContextType >> 16) & MAX_ROOT_CONTEXT_MASK;
++    
++    if (ctxnum >= ELAN3_DMARING_BASE_CONTEXT_NUM && ctxnum < (ELAN3_DMARING_BASE_CONTEXT_NUM+EP3_NUM_RINGS))
++    {
++      EP3_DMA_RING *ring = &rail->DmaRings[ctxnum - ELAN3_DMARING_BASE_CONTEXT_NUM];
++
++      ASSERT ((cmdoff << 2) == offsetof (E3_CommandPort, SetEvent)); /* can only be setevent commands! */
++      ASSERT (tbuf->s.Addr >= DMA_RING_EVENT_ELAN(ring,0) && tbuf->s.Addr < DMA_RING_EVENT_ELAN(ring, ring->Entries));
++      
++      writel (tbuf->s.Addr, ring->CommandPort + (cmdoff << 2));
++    }
++    else
++    {
++      ASSERT (ctxnum == ELAN3_MRF_CONTEXT_NUM);
++
++      writel (tbuf->s.Addr, ctxt->CommandPort + (cmdoff << 2));
++    }
++    
++    return (OP_HANDLED);
++}
++
++static E3_uint8
++ep3_load8 (ELAN3_CTXT *ctxt, E3_Addr addr)
++{
++    EP3_RAIL    *rail  = (EP3_RAIL *) ctxt->Private;
++    ELAN3_DEV    *dev = ctxt->Device;
++    sdramaddr_t offset;
++    E3_uint8   *ptr;
++
++    if ((offset = ep_elan2sdram (&rail->Generic, addr)) != 0)
++      return (elan3_sdram_readb (dev, offset));
++    if ((ptr = ep_elan2main (&rail->Generic, addr)) != NULL)
++      return (*ptr);
++
++    printk ("ep3_load8: %08x\n", addr);
++    return (0);
++}
++
++static void
++ep3_store8 (ELAN3_CTXT *ctxt, E3_Addr addr, E3_uint8 val)
++{
++    EP3_RAIL   *rail  = (EP3_RAIL *) ctxt->Private;
++    ELAN3_DEV   *dev = ctxt->Device;
++    sdramaddr_t offset;
++    E3_uint8   *ptr;
++
++    if ((offset = ep_elan2sdram (&rail->Generic, addr)) != 0)
++      elan3_sdram_writeb (dev, offset, val);
++    else if ((ptr = ep_elan2main (&rail->Generic, addr)) != 0)
++      *ptr = val;
++    else
++      printk ("ep3_store8 %08x\n", addr);
++}
++
++static E3_uint16
++ep3_load16 (ELAN3_CTXT *ctxt, E3_Addr addr)
++{
++    EP3_RAIL   *rail  = (EP3_RAIL *) ctxt->Private;
++    ELAN3_DEV   *dev = ctxt->Device;
++    sdramaddr_t offset;
++    E3_uint16  *ptr;
++
++    if ((offset = ep_elan2sdram (&rail->Generic, addr)) != 0)
++      return (elan3_sdram_readw (dev, offset));
++    if ((ptr = ep_elan2main (&rail->Generic, addr)) != 0)
++      return (*ptr);
++
++    printk ("ep3_load16 %08x\n", addr);
++    return (0);
++}
++
++static void
++ep3_store16 (ELAN3_CTXT *ctxt, E3_Addr addr, E3_uint16 val)
++{
++    EP3_RAIL   *rail  = (EP3_RAIL *) ctxt->Private;
++    ELAN3_DEV   *dev = ctxt->Device;
++    sdramaddr_t offset;
++    E3_uint16  *ptr;
++
++    if ((offset = ep_elan2sdram (&rail->Generic, addr)) != 0)
++      elan3_sdram_writew (dev, offset, val);
++    else if ((ptr = ep_elan2main (&rail->Generic, addr)) != 0)
++      *ptr = val;
++    else
++      printk ("ep3_store16 %08x\n", addr);
++}
++
++static E3_uint32
++ep3_load32 (ELAN3_CTXT *ctxt, E3_Addr addr)
++{
++    EP3_RAIL   *rail  = (EP3_RAIL *) ctxt->Private;
++    ELAN3_DEV   *dev = ctxt->Device;
++    sdramaddr_t offset;
++    E3_uint32  *ptr;
++
++    if ((offset = ep_elan2sdram (&rail->Generic, addr)) != 0)
++      return (elan3_sdram_readl(dev, offset));
++    if ((ptr = ep_elan2main (&rail->Generic, addr)) != 0)
++      return (*ptr);
++    
++    printk ("ep3_load32 %08x\n", addr);
++    return (0);
++}
++
++static void
++ep3_store32 (ELAN3_CTXT *ctxt, E3_Addr addr, E3_uint32 val)
++{
++    EP3_RAIL   *rail  = (EP3_RAIL *) ctxt->Private;
++    ELAN3_DEV   *dev = ctxt->Device;
++    sdramaddr_t offset;
++    E3_uint32  *ptr;
++
++    if ((offset = ep_elan2sdram (&rail->Generic, addr)) != 0)
++      elan3_sdram_writel (dev, offset, val);
++    else if ((ptr = ep_elan2main (&rail->Generic, addr)) != 0)
++      *ptr = val;
++    else
++      printk ("ep3_store32 %08x\n", addr);
++}
++
++static E3_uint64
++ep3_load64 (ELAN3_CTXT *ctxt, E3_Addr addr)
++{
++    EP3_RAIL   *rail  = (EP3_RAIL *) ctxt->Private;
++    ELAN3_DEV   *dev = ctxt->Device;
++    sdramaddr_t offset;
++    E3_uint64  *ptr;
++
++    if ((offset = ep_elan2sdram (&rail->Generic, addr)) != 0)
++      return (elan3_sdram_readq (dev, offset));
++    if ((ptr = ep_elan2main (&rail->Generic, addr)) != 0)
++      return (*ptr);
++
++    printk ("ep3_load64 %08x\n", addr);
++    return (0);
++}
++
++static void
++ep3_store64 (ELAN3_CTXT *ctxt, E3_Addr addr, E3_uint64 val)
++{
++    EP3_RAIL   *rail  = (EP3_RAIL *) ctxt->Private;
++    ELAN3_DEV   *dev = ctxt->Device;
++    sdramaddr_t offset;
++    E3_uint64  *ptr;
++
++    if ((offset = ep_elan2sdram (&rail->Generic, addr)) != 0)
++      elan3_sdram_writeq (dev, offset, val);
++    else if ((ptr = ep_elan2main (&rail->Generic, addr)) != 0)
++      *ptr = val;
++    else
++      printk ("ep3_store64 %08x\n", addr);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/ep/support_elan4.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/support_elan4.c     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/support_elan4.c  2005-06-01 23:12:54.689425424 -0400
+@@ -0,0 +1,1184 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: support_elan4.c,v 1.18.2.3 2004/11/18 12:05:00 david Exp $ $Name: QSNETMODULES-4-30_20050128 $"
++/*      $Source: /cvs/master/quadrics/epmod/support_elan4.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <qsnet/kthread.h>
++
++#include <elan/kcomm.h>
++
++#include "kcomm_vp.h"
++#include "kcomm_elan4.h"
++#include "debug.h"
++
++#include <elan4/trtype.h>
++#include <elan4/debug.h>
++
++void
++ep4_register_intcookie (EP4_RAIL *rail, EP4_INTCOOKIE *cp, E4_uint64 cookie, void (*callback)(EP4_RAIL *r, void *arg), void *arg)
++{
++    unsigned long flags;
++    
++    cp->int_val      = cookie;
++    cp->int_callback = callback;
++    cp->int_arg      = arg;
++      
++    spin_lock_irqsave (&rail->r_intcookie_lock, flags);
++    list_add_tail (&cp->int_link, &rail->r_intcookie_hash[EP4_INTCOOKIE_HASH(cookie)]);
++    spin_unlock_irqrestore (&rail->r_intcookie_lock, flags);
++}
++
++void
++ep4_deregister_intcookie (EP4_RAIL *rail, EP4_INTCOOKIE *cp)
++{
++    unsigned long flags;
++    
++    spin_lock_irqsave (&rail->r_intcookie_lock, flags);
++    list_del (&cp->int_link);
++    spin_unlock_irqrestore (&rail->r_intcookie_lock, flags);
++}
++
++
++EP4_INTCOOKIE *
++ep4_lookup_intcookie (EP4_RAIL *rail, E4_uint64 cookie)
++{
++    struct list_head *el;
++    unsigned long flags;
++
++    spin_lock_irqsave (&rail->r_intcookie_lock, flags);
++    list_for_each (el, &rail->r_intcookie_hash[EP4_INTCOOKIE_HASH(cookie)]) {
++      EP4_INTCOOKIE *cp = list_entry (el, EP4_INTCOOKIE, int_link);
++
++      if (cp->int_val == cookie)
++      {
++          spin_unlock_irqrestore (&rail->r_intcookie_lock, flags);
++          return cp;
++      }
++    }
++    spin_unlock_irqrestore (&rail->r_intcookie_lock, flags);
++    return NULL;
++}
++
++E4_uint64
++ep4_neterr_cookie (EP4_RAIL *rail, unsigned int node)
++{
++    E4_uint64      cookie;
++    unsigned long  flags;
++
++    spin_lock_irqsave (&rail->r_cookie_lock, flags);
++    cookie = rail->r_cookies[node];
++
++    rail->r_cookies[node] += EP4_COOKIE_INC;
++    
++    spin_unlock_irqrestore (&rail->r_cookie_lock, flags);
++
++    return cookie;
++}
++
++void
++ep4_eproc_trap (ELAN4_CTXT *ctxt, E4_uint64 status)
++{
++    EP4_RAIL        *rail = EP4_CTXT_TO_RAIL (ctxt);
++    ELAN4_EPROC_TRAP trap;
++
++    elan4_extract_eproc_trap (ctxt->ctxt_dev, status, &trap, 0);
++
++    if (epdebug & DBG_EPTRAP)
++      elan4_display_eproc_trap (DBG_BUFFER, 0, "ep4_eproc_trap", &trap);
++
++    switch (EPROC_TrapType (status))
++    {
++    case EventProcNoFault:
++      EPRINTF1 (DBG_EPTRAP, "%s: EventProcNoFault\n", rail->r_generic.Name);
++      return;
++
++    default:
++      printk ("%s: unhandled eproc trap %d\n", rail->r_generic.Name, EPROC_TrapType (status));
++      elan4_display_eproc_trap (DBG_CONSOLE, 0, "ep4_eproc_trap", &trap);
++    }
++}
++
++void
++ep4_cproc_trap (ELAN4_CTXT *ctxt, E4_uint64 status, unsigned cqnum)
++{
++    EP4_RAIL        *rail = EP4_CTXT_TO_RAIL (ctxt);
++    ELAN4_CPROC_TRAP trap;
++    struct list_head *el;
++    register int      i;
++
++    elan4_extract_cproc_trap (ctxt->ctxt_dev, status, &trap, cqnum);
++
++    if (epdebug & DBG_EPTRAP)
++      elan4_display_cproc_trap (DBG_BUFFER, 0, "ep4_cproc_trap", &trap);
++      
++    switch (CPROC_TrapType (status))
++    {
++    case CommandProcInterruptQueueOverflow:
++      /*
++       * Try and handle a bunch of elan main interrupts
++       */
++      for (i = 0; i <EP4_NUM_ECQ; i++) {
++          list_for_each (el, &rail->r_ecq_list[i]) {
++              EP4_ECQ *ecq = list_entry (el, EP4_ECQ, ecq_link);
++          
++              if (elan4_cq2num (ecq->ecq_cq) == cqnum)
++              {
++                  printk ("%s: defer command queue %d after trap %x\n",
++                          rail->r_generic.Name, cqnum, CPROC_TrapType (status));
++      
++                  elan4_queue_mainintop (ctxt->ctxt_dev, &ecq->ecq_intop);
++                  return;
++              }
++          }
++      }
++      break;
++
++    case CommandProcDmaQueueOverflow:
++    case CommandProcThreadQueueOverflow:
++      for (i = 0; i <EP4_NUM_ECQ; i++) {
++          list_for_each (el, &rail->r_ecq_list[i]) {
++              EP4_ECQ *ecq = list_entry (el, EP4_ECQ, ecq_link);
++          
++              if (elan4_cq2num (ecq->ecq_cq) == cqnum)
++              {
++                  printk ("%s: restart command queue %d after trap %x\n",
++                          rail->r_generic.Name, cqnum, CPROC_TrapType (status));
++
++                  elan4_restartcq (ctxt->ctxt_dev, ecq->ecq_cq);
++                  return;
++              }
++          }
++      }
++      break;
++    }
++
++    printk ("%s: unhandled cproc trap %d for cqnum %d\n", rail->r_generic.Name, CPROC_TrapType (status), cqnum);
++    elan4_display_cproc_trap (DBG_CONSOLE, 0, "ep4_cproc_trap", &trap);
++}
++
++void
++ep4_dproc_trap (ELAN4_CTXT *ctxt, E4_uint64 status, unsigned unit)
++{
++    EP4_RAIL        *rail = EP4_CTXT_TO_RAIL (ctxt);
++    ELAN4_DPROC_TRAP trap;
++
++    elan4_extract_dproc_trap (ctxt->ctxt_dev, status, &trap, unit);
++
++    if (epdebug & DBG_EPTRAP)
++      elan4_display_dproc_trap (DBG_BUFFER, 0, "ep4_dproc_trap", &trap);
++
++    if (! DPROC_PrefetcherFault (trap.tr_status))
++    {
++      switch (DPROC_TrapType (trap.tr_status))
++      {
++      case DmaProcFailCountError:
++          goto retry_this_dma;
++
++      case DmaProcPacketAckError:
++          goto retry_this_dma;
++
++      case DmaProcQueueOverflow:
++          goto retry_this_dma;
++      }
++    }
++
++    printk ("%s: unhandled dproc trap\n", rail->r_generic.Name);
++    elan4_display_dproc_trap (DBG_CONSOLE, 0, "ep4_dproc_trap", &trap);
++    return;
++
++ retry_this_dma:
++    /*XXXX implement backoff .... */
++
++    ep4_queue_dma_retry (rail, &trap.tr_desc, EP_RETRY_LOW_PRI);
++}
++
++void
++ep4_tproc_trap (ELAN4_CTXT *ctxt, E4_uint64 status)
++{
++    EP4_RAIL         *rail = EP4_CTXT_TO_RAIL (ctxt);
++    ELAN4_TPROC_TRAP *trap = &rail->r_tproc_trap;
++
++    elan4_extract_tproc_trap (ctxt->ctxt_dev, status, trap);
++
++    if (epdebug & DBG_EPTRAP)
++      elan4_display_tproc_trap (DBG_BUFFER, 0, "ep4_tproc_trap", trap);
++      
++    printk ("%s: unhandled tproc trap\n", rail->r_generic.Name);
++    elan4_display_tproc_trap (DBG_CONSOLE, 0, "ep4_tproc_trap", trap);
++}
++
++void
++ep4_iproc_trap (ELAN4_CTXT *ctxt, E4_uint64 status, unsigned unit)
++{
++    EP4_RAIL         *rail = EP4_CTXT_TO_RAIL (ctxt);
++    ELAN4_IPROC_TRAP *trap = &rail->r_iproc_trap;
++
++    elan4_extract_iproc_trap (ctxt->ctxt_dev, status, trap, unit);
++
++    if (epdebug & DBG_EPTRAP)
++      elan4_display_iproc_trap (DBG_BUFFER, 0, "ep4_iproc_trap", trap);
++      
++    elan4_inspect_iproc_trap (trap);
++
++    switch (IPROC_TrapValue (trap->tr_transactions[trap->tr_trappedTrans].IProcStatusCntxAndTrType))
++    {
++    case InputDmaQueueOverflow:
++      ep4_queue_dma_retry (rail, (E4_DMA *) &trap->tr_dataBuffers[trap->tr_trappedTrans], EP_RETRY_LOW_PRI);
++      return;
++
++    case InputEventEngineTrapped:
++    {
++      E4_IprocTrapHeader *hdrp = &trap->tr_transactions[trap->tr_trappedTrans];
++      sdramaddr_t         inputq;
++      E4_Addr             event;
++
++      /* XXXX: flow control on the command queue which we issue to is 
++       * rather difficult, we don't want to have space for an event 
++       * for each possible context, nor the mechanism to hold the 
++       * context filter up until the event has been executed.  Given
++       * that the event engine will be restarted by this same interrupt
++       * and we're using high priority command queues, then we just use
++       * a single small command queue for this.
++       */
++      switch (IPROC_TransactionType(hdrp->IProcStatusCntxAndTrType) & TR_OPCODE_MASK)
++      {
++      case TR_SETEVENT & TR_OPCODE_MASK:
++          if (hdrp->TrAddr != 0)
++              ep4_set_event_cmd (rail->r_event_ecq, hdrp->TrAddr);
++          return;
++
++      case TR_INPUT_Q_COMMIT & TR_OPCODE_MASK:
++          if ((inputq = ep_elan2sdram (&rail->r_generic, hdrp->TrAddr)) == 0)
++              printk ("%s: TR_INPUT_Q_COMMIT at %llx is not sdram\n", rail->r_generic.Name, hdrp->TrAddr);
++          else
++          {
++              if ((event = elan4_sdram_readq (rail->r_ctxt.ctxt_dev, inputq + offsetof (E4_InputQueue, q_event))) != 0)
++                  ep4_set_event_cmd (rail->r_event_ecq, event);
++              return;
++          }
++      }
++      break;
++    }
++
++    case InputEopErrorOnWaitForEop:
++    case InputEopErrorTrap:
++    case InputCrcErrorAfterPAckOk:
++      if (! (trap->tr_flags & TR_FLAG_ACK_SENT) || (trap->tr_flags & TR_FLAG_EOP_BAD))
++          return;
++      
++      if (EP4_CONTEXT_ISDATA (IPROC_NetworkContext (status)))
++      {
++          unsigned int nodeId = EP4_CONTEXT_TO_NODE (IPROC_NetworkContext (status));
++
++          if ((trap->tr_flags & (TR_FLAG_DMA_PACKET | TR_FLAG_BAD_TRANS)) || 
++              ((trap->tr_flags & TR_FLAG_EOP_ERROR) && (trap->tr_identifyTrans == TR_TRANS_INVALID)))
++          {
++              printk ("%s: network error on dma packet from node %d\n", rail->r_generic.Name, nodeId);
++
++              ep_queue_network_error (&rail->r_generic, EP4_CONTEXT_TO_NODE(IPROC_NetworkContext (status)), EP_NODE_NETERR_DMA_PACKET, unit & 1, 0);
++              return;
++          }
++          
++          if (trap->tr_flags & TR_FLAG_EOP_ERROR)
++          {
++              E4_uint64        status = trap->tr_transactions[trap->tr_identifyTrans].IProcStatusCntxAndTrType;
++              EP_NETERR_COOKIE cookie = 0;
++
++              switch (IPROC_TransactionType (status) & TR_OPCODE_MASK)
++              {
++              case TR_SETEVENT_IDENTIFY & TR_OPCODE_MASK:
++                  if (IPROC_TrapValue(status) == InputNoFault)
++                      cookie = trap->tr_transactions[trap->tr_identifyTrans].TrAddr;
++                  else
++                      cookie = trap->tr_dataBuffers[trap->tr_identifyTrans].Data[0];
++                  printk ("%s: network error on setevent <%lld%s%s%s%s> from node %d\n", rail->r_generic.Name, EP4_COOKIE_STRING(cookie), nodeId);
++                  break;
++
++              case TR_INPUT_Q_COMMIT & TR_OPCODE_MASK:
++                  if (IPROC_TrapValue(status) == InputNoFault)
++                      cookie = trap->tr_transactions[trap->tr_identifyTrans].TrAddr;
++                  else
++                      cookie = trap->tr_dataBuffers[trap->tr_identifyTrans].Data[0];
++                  printk ("%s: network error on queue commit <%lld%s%s%s%s> from node %d\n", rail->r_generic.Name, EP4_COOKIE_STRING(cookie), nodeId);
++                  break;
++                  
++              case TR_REMOTEDMA & TR_OPCODE_MASK:
++                  cookie = trap->tr_transactions[trap->tr_identifyTrans].TrAddr;
++                  printk ("%s: network error on remote dma <%lld%s%s%s%s> from node %d\n", rail->r_generic.Name, EP4_COOKIE_STRING(cookie), nodeId);
++                  break;
++
++              case TR_IDENTIFY & TR_OPCODE_MASK:
++                  cookie = trap->tr_transactions[trap->tr_identifyTrans].TrAddr;
++                  printk ("%s: network error on identify <%lld%s%s%s%s> from node %d\n", rail->r_generic.Name, EP4_COOKIE_STRING(cookie), nodeId);
++                  break;
++
++              default:
++                  panic ("%s: unknown identify transaction type %x for eop error from node %d\n", rail->r_generic.Name,
++                          IPROC_TransactionType (trap->tr_transactions[trap->tr_identifyTrans].IProcStatusCntxAndTrType), nodeId);
++                  break;
++              }
++
++              ep_queue_network_error (&rail->r_generic, nodeId, EP_NODE_NETERR_ATOMIC_PACKET, unit & 1, cookie);
++          }
++      }
++      return;
++    }
++
++    printk ("%s: unhandled iproc trap\n", rail->r_generic.Name);
++    elan4_display_iproc_trap (DBG_CONSOLE, 0, "ep4_iproc_trap", trap);
++}
++
++void
++ep4_interrupt (ELAN4_CTXT *ctxt, E4_uint64 cookie)
++{
++    EP4_RAIL      *rail = EP4_CTXT_TO_RAIL (ctxt);
++    EP4_INTCOOKIE *cp  = ep4_lookup_intcookie (rail, cookie);
++
++    if (cp == NULL)
++    {
++      printk ("ep4_interrupt: cannot find event cookie for %016llx\n", (long long) cookie);
++      return;
++    }
++
++    cp->int_callback (rail, cp->int_arg);
++}
++
++ELAN4_TRAP_OPS ep4_trap_ops = 
++{
++    ep4_eproc_trap,
++    ep4_cproc_trap,
++    ep4_dproc_trap,
++    ep4_tproc_trap,
++    ep4_iproc_trap,
++    ep4_interrupt,
++};
++
++void
++ep4_flush_filters (EP_RAIL *r)
++{
++    /* nothing to do here as elan4_set_filter() flushes the context filter */
++}
++
++struct flush_queues_desc
++{
++    EP4_RAIL      *rail;
++    volatile int   done;
++} ;
++
++static void
++ep4_flush_queues_flushop (ELAN4_DEV *dev, void *arg, int qfull)
++{
++    struct flush_queues_desc *desc  = (struct flush_queues_desc *) arg;
++    EP4_RAIL                 *rail  = desc->rail;
++    E4_uint64               qptrs = read_reg64 (dev, DProcHighPriPtrs);
++    E4_uint32                 qsize = E4_QueueSize (E4_QueueSizeValue (qptrs));
++    E4_uint32                 qfptr = E4_QueueFrontPointer (qptrs);
++    E4_uint32                 qbptr = E4_QueueBackPointer (qptrs);
++    E4_DProcQueueEntry        qentry;
++    unsigned long             flags;
++
++    while ((qfptr != qbptr) || qfull)
++    {
++      E4_uint64 typeSize = elan4_sdram_readq (dev, qfptr + offsetof (E4_DProcQueueEntry, Desc.dma_typeSize));
++      
++      if (DMA_Context (qentry.Desc.dma_typeSize) == rail->r_ctxt.ctxt_num)
++      {
++          E4_uint64     vp       = elan4_sdram_readq (dev, qfptr + offsetof (E4_DProcQueueEntry, Desc.dma_vproc));
++          EP_NODE_RAIL *nodeRail = &rail->r_generic.Nodes[EP_VP_TO_NODE(vp)];
++          
++          EP4_ASSERT (rail, !EP_VP_ISDATA(vp) || (nodeRail->State >= EP_NODE_CONNECTED && nodeRail->State <= EP_NODE_LOCAL_PASSIVATE));
++          
++          if (EP_VP_ISDATA(vp) && nodeRail->State == EP_NODE_LOCAL_PASSIVATE)
++          {
++              /*
++               * This is a DMA going to the node which is being removed, 
++               * so move it onto the node dma list where it will get
++               * handled later.
++               */
++              qentry.Desc.dma_typeSize = typeSize;
++              qentry.Desc.dma_cookie   = elan4_sdram_readq (dev, qfptr + offsetof (E4_DProcQueueEntry, Desc.dma_cookie));
++              qentry.Desc.dma_vproc    = vp;
++              qentry.Desc.dma_srcAddr  = elan4_sdram_readq (dev, qfptr + offsetof (E4_DProcQueueEntry, Desc.dma_srcAddr));
++              qentry.Desc.dma_dstAddr  = elan4_sdram_readq (dev, qfptr + offsetof (E4_DProcQueueEntry, Desc.dma_dstAddr));
++              qentry.Desc.dma_srcEvent = elan4_sdram_readq (dev, qfptr + offsetof (E4_DProcQueueEntry, Desc.dma_srcEvent));
++              qentry.Desc.dma_dstEvent = elan4_sdram_readq (dev, qfptr + offsetof (E4_DProcQueueEntry, Desc.dma_dstEvent));
++              
++              EPRINTF4 (DBG_RETRY, "ep4_flush_dmas: %016llx %016llx %016llx %016llx\n", qentry.Desc.dma_typeSize, 
++                        qentry.Desc.dma_cookie, qentry.Desc.dma_vproc, qentry.Desc.dma_srcAddr);
++              EPRINTF3 (DBG_RETRY, "                %016llx %016llx %016llx\n", qentry.Desc.dma_dstAddr, 
++                        qentry.Desc.dma_srcEvent, qentry.Desc.dma_dstEvent);
++              
++              ep4_queue_dma_stalled (rail, &qentry.Desc);
++              
++              qentry.Desc.dma_typeSize = DMA_ShMemWrite | dev->dev_ctxt.ctxt_num;
++              qentry.Desc.dma_cookie   = 0;
++              qentry.Desc.dma_vproc    = 0;
++              qentry.Desc.dma_srcAddr  = 0;
++              qentry.Desc.dma_dstAddr  = 0;
++              qentry.Desc.dma_srcEvent = 0;
++              qentry.Desc.dma_dstEvent = 0;
++              
++              elan4_sdram_copyq_to_sdram (dev, &qentry, qfptr, sizeof (E4_DProcQueueEntry));
++          }
++      }
++
++      qfptr = (qfptr & ~(qsize-1)) | ((qfptr + sizeof (E4_DProcQueueEntry)) & (qsize-1));
++      qfull = 0;
++    }
++
++    spin_lock_irqsave (&rail->r_haltop_lock, flags);
++    desc->done = 1;
++    kcondvar_wakeupall (&rail->r_haltop_sleep, &rail->r_haltop_lock);
++    spin_unlock_irqrestore (&rail->r_haltop_lock, flags);
++}
++
++static void
++ep4_flush_queues_haltop (ELAN4_DEV *dev, void *arg)
++{
++    struct flush_queues_desc *desc = (struct flush_queues_desc *) arg;
++
++    elan4_queue_dma_flushop (dev, &desc->rail->r_flushop, 1);
++}
++
++void
++ep4_flush_queues (EP_RAIL *r)
++{
++    EP4_RAIL *rail = (EP4_RAIL *) r;
++    struct flush_queues_desc desc;
++    struct list_head *el, *nel;
++    unsigned long flags;
++    int i;
++
++    /* initialise descriptor */
++    desc.rail  = rail;
++    desc.done  = 0;
++
++    /* First -  stall the dma retry thread, so that it will no longer restart
++     *          any dma's from the retry list */
++    ep_kthread_stall (&rail->r_retry_thread);
++
++    /* Second - flush through all command queues targetted by events, thread etc */
++    ep4_flush_ecqs (rail);
++
++    /* Third - queue a halt operation to flush through all DMA's which are executing
++     *         or on the run queues */
++    kmutex_lock (&rail->r_haltop_mutex);
++
++    rail->r_haltop.op_mask      = INT_DProcHalted;
++    rail->r_haltop.op_function  = ep4_flush_queues_haltop;
++    rail->r_haltop.op_arg       = &desc;
++
++    rail->r_flushop.op_function = ep4_flush_queues_flushop;
++    rail->r_flushop.op_arg      = &desc;
++    
++    elan4_queue_haltop (rail->r_ctxt.ctxt_dev, &rail->r_haltop);
++
++    spin_lock_irqsave (&rail->r_haltop_lock, flags);
++    while (! desc.done)
++      kcondvar_wait (&rail->r_haltop_sleep, &rail->r_haltop_lock, &flags);
++    spin_unlock_irqrestore (&rail->r_haltop_lock, flags);
++    kmutex_unlock (&rail->r_haltop_mutex);
++
++    /* Fourth - run down the dma retry lists and move all entries to the cancelled
++     *          list.  Any dma's which were on the run queues have already been
++     *          moved there */
++    spin_lock_irqsave (&rail->r_dma_lock, flags);
++    for (i = EP_RETRY_BASE; i < EP_NUM_RETRIES; i++)
++    {
++      list_for_each_safe (el,nel, &rail->r_dma_retrylist[i]) {
++          EP4_DMA_RETRY *retry    = list_entry (el, EP4_DMA_RETRY, retry_link);
++          EP_NODE_RAIL  *nodeRail = &rail->r_generic.Nodes[EP_VP_TO_NODE(retry->retry_dma.dma_vproc)];
++
++          EP4_ASSERT (rail, nodeRail->State >= EP_NODE_CONNECTED && nodeRail->State <= EP_NODE_LOCAL_PASSIVATE);
++
++          if (nodeRail->State == EP_NODE_LOCAL_PASSIVATE)
++          {
++              list_del (&retry->retry_link);
++              list_add_tail (&retry->retry_link, &nodeRail->StalledDmas);
++          }
++      }
++    }
++    spin_unlock_irqrestore (&rail->r_dma_lock, flags);
++    
++    /* Finally - allow the retry thread to run again */
++    ep_kthread_resume (&rail->r_retry_thread);
++}
++
++struct write_qdesc_desc
++{
++    EP4_RAIL      *rail;
++    sdramaddr_t    qaddr;
++    E4_InputQueue *qdesc;
++    volatile int   done;
++} ;
++
++static void
++ep4_write_qdesc_haltop (ELAN4_DEV *dev, void *arg)
++{
++    struct write_qdesc_desc *desc = (struct write_qdesc_desc *) arg;
++    EP4_RAIL                *rail = desc->rail;
++    unsigned long            flags;
++
++    elan4_sdram_copyq_to_sdram (dev, desc->qdesc, desc->qaddr, sizeof (E4_InputQueue));
++
++    spin_lock_irqsave (&rail->r_haltop_lock, flags);
++    desc->done = 1;
++    kcondvar_wakeupall (&rail->r_haltop_sleep, &rail->r_haltop_lock);
++    spin_unlock_irqrestore (&rail->r_haltop_lock, flags);
++}
++
++void
++ep4_write_qdesc (EP4_RAIL *rail, sdramaddr_t qaddr, E4_InputQueue *qdesc)
++{
++    struct write_qdesc_desc desc;
++    unsigned long flags;
++
++    /* initialise descriptor */
++    desc.rail  = rail;
++    desc.qaddr = qaddr;
++    desc.qdesc = qdesc;
++    desc.done  = 0;
++
++    kmutex_lock (&rail->r_haltop_mutex);
++
++    rail->r_haltop.op_mask     = INT_DiscardingHighPri;
++    rail->r_haltop.op_function = ep4_write_qdesc_haltop;
++    rail->r_haltop.op_arg      = &desc;
++    
++    elan4_queue_haltop (rail->r_ctxt.ctxt_dev, &rail->r_haltop);
++
++    spin_lock_irqsave (&rail->r_haltop_lock, flags);
++    while (! desc.done)
++      kcondvar_wait (&rail->r_haltop_sleep, &rail->r_haltop_lock, &flags);
++    spin_unlock_irqrestore (&rail->r_haltop_lock, flags);
++    
++    kmutex_unlock (&rail->r_haltop_mutex);
++}
++#define CQ_SIZE_NWORDS ((CQ_Size (ecq->ecq_cq->cq_size) >> 3) - 8)    /* available number of dwords (less enough to flush) */
++EP4_ECQ *
++ep4_alloc_ecq (EP4_RAIL *rail, unsigned cqsize)
++{
++    EP4_ECQ *ecq;
++    unsigned long pgoff;
++
++    /* no space available, so allocate a new entry */
++    KMEM_ZALLOC (ecq, EP4_ECQ *, sizeof (EP4_ECQ), 1);
++
++    if (ecq == NULL)
++      return 0;
++
++    if ((ecq->ecq_cq = elan4_alloccq (&rail->r_ctxt, cqsize, CQ_EnableAllBits, CQ_Priority)) == NULL)
++    {
++      KMEM_FREE (ecq, sizeof (EP4_ECQ));
++      return 0;
++    }
++
++    pgoff = (ecq->ecq_cq->cq_mapping & (PAGE_SIZE-1));
++
++    ecq->ecq_addr  = ep_rmalloc (rail->r_ecq_rmap, PAGESIZE, 0) + pgoff;
++    ecq->ecq_avail = CQ_SIZE_NWORDS;                  /* available number of dwords (less enough to flush) */
++
++    ecq->ecq_intop.op_function = (ELAN4_HALTFN *) elan4_restartcq;
++    ecq->ecq_intop.op_arg      = ecq->ecq_cq;
++
++    ep4_ioaddr_map (&rail->r_generic, ecq->ecq_addr - pgoff, ecq->ecq_cq->cq_mapping - pgoff, PAGESIZE, EP_PERM_WRITE);
++
++    spin_lock_init (&ecq->ecq_lock);
++
++    return ecq;
++}
++
++void
++ep4_free_ecq (EP4_RAIL *rail, EP4_ECQ *ecq)
++{
++    unsigned long pgoff = (ecq->ecq_cq->cq_mapping & (PAGE_SIZE-1));
++
++    spin_lock_destroy (&ecq->ecq_lock);
++
++    ep4_unmap (&rail->r_generic, ecq->ecq_addr - pgoff, PAGESIZE);
++    ep_rmfree (rail->r_ecq_rmap, PAGESIZE, ecq->ecq_addr - pgoff);
++
++    elan4_freecq (&rail->r_ctxt, ecq->ecq_cq);
++    
++    KMEM_FREE (ecq, sizeof (EP4_ECQ));
++}
++
++EP4_ECQ *
++ep4_get_ecq (EP4_RAIL *rail, unsigned which, unsigned ndwords)
++{
++    ELAN4_DEV *dev = rail->r_ctxt.ctxt_dev;
++    struct list_head *el;
++    unsigned long flags;
++    EP4_ECQ *ecq;
++    
++    spin_lock_irqsave (&rail->r_ecq_lock, flags);
++    list_for_each (el, &rail->r_ecq_list[which]) {
++      EP4_ECQ *ecq = list_entry (el, EP4_ECQ, ecq_link);
++
++      if (ecq->ecq_avail >= ndwords)
++      {
++          ecq->ecq_avail -= ndwords;
++
++          spin_unlock_irqrestore (&rail->r_ecq_lock, flags);
++
++          return ecq;
++      }
++    }
++    spin_unlock_irqrestore (&rail->r_ecq_lock, flags);
++
++    if ((ecq = ep4_alloc_ecq (rail, EP4_ECQ_Size (which))) == NULL)
++      return NULL;
++
++    if (which == EP4_ECQ_EVENT)
++    {
++      if ((ecq->ecq_event = ep_alloc_elan (&rail->r_generic, sizeof (E4_Event32), 0, &ecq->ecq_event_addr)) == 0)
++      {
++          ep4_free_ecq (rail, ecq);
++          return NULL;
++      }
++      
++      elan4_sdram_writeq (dev, ecq->ecq_event + offsetof (E4_Event32, ev_CountAndType),
++                          E4_EVENT_INIT_VALUE (0, E4_EVENT_WRITE, E4_EVENT_DTYPE_LONG, 0));
++      elan4_sdram_writeq (dev, ecq->ecq_event + offsetof (E4_Event32, ev_WritePtr),
++                          ecq->ecq_addr);
++      elan4_sdram_writeq (dev, ecq->ecq_event + offsetof (E4_Event32, ev_WriteValue),
++                          SET_EVENT_CMD | (rail->r_elan_addr + offsetof (EP4_RAIL_ELAN, r_flush_event)));
++      
++      if ((ecq->ecq_flushcq = ep4_get_ecq (rail, EP4_ECQ_SINGLE, 1)) == NULL)
++      {
++          ep_free_elan (&rail->r_generic, ecq->ecq_event_addr, sizeof (E4_Event32));
++          ep4_free_ecq (rail, ecq);
++          return NULL;
++      }
++    }
++
++    spin_lock_irqsave (&rail->r_ecq_lock, flags);
++    list_add (&ecq->ecq_link, &rail->r_ecq_list[which]);
++
++    ecq->ecq_avail -= ndwords;
++    spin_unlock_irqrestore (&rail->r_ecq_lock, flags);
++
++    return ecq;
++}
++
++void
++ep4_put_ecq (EP4_RAIL *rail, EP4_ECQ *ecq, unsigned ndwords)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&rail->r_ecq_lock, flags);
++
++    ecq->ecq_avail += ndwords;
++    
++    if (ecq->ecq_avail !=  CQ_SIZE_NWORDS) 
++      spin_unlock_irqrestore (&rail->r_ecq_lock, flags);
++    else
++    {
++      list_del (&ecq->ecq_link);
++      spin_unlock_irqrestore (&rail->r_ecq_lock, flags);
++      
++      if (ecq->ecq_flushcq)
++          ep4_put_ecq (rail, ecq->ecq_flushcq, 1);
++      if (ecq->ecq_event_addr)
++          ep_free_elan (&rail->r_generic, ecq->ecq_event_addr, sizeof (E4_Event32));
++
++      ep4_free_ecq (rail, ecq);
++    }
++}
++
++void
++ep4_nop_cmd (EP4_ECQ *ecq, E4_uint64 tag)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&ecq->ecq_lock, flags);
++    elan4_nop_cmd (ecq->ecq_cq, tag);
++    spin_unlock_irqrestore (&ecq->ecq_lock, flags);
++    
++}
++
++void
++ep4_set_event_cmd (EP4_ECQ *ecq, E4_Addr event)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&ecq->ecq_lock, flags);
++    elan4_set_event_cmd (ecq->ecq_cq, event);
++    spin_unlock_irqrestore (&ecq->ecq_lock, flags);
++}
++
++void
++ep4_wait_event_cmd (EP4_ECQ *ecq, E4_Addr event, E4_uint64 candt, E4_uint64 param0, E4_uint64 param1)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&ecq->ecq_lock, flags);
++    elan4_wait_event_cmd (ecq->ecq_cq, event, candt, param0, param1);
++    spin_unlock_irqrestore (&ecq->ecq_lock, flags);
++}
++
++void
++ep4_flush_interrupt (EP4_RAIL *rail, void *arg)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&rail->r_ecq_lock, flags);
++    rail->r_flush_count = 0;
++    kcondvar_wakeupone (&rail->r_flush_sleep, &rail->r_ecq_lock);
++    spin_unlock_irqrestore (&rail->r_ecq_lock, flags);
++}
++
++void
++ep4_flush_ecqs (EP4_RAIL *rail)
++{
++    ELAN4_DEV *dev = rail->r_ctxt.ctxt_dev;
++    struct list_head *el;
++    unsigned long flags;
++    int i;
++
++    kmutex_lock (&rail->r_flush_mutex);
++
++    EP4_SDRAM_ASSERT (rail, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_flush_event), E4_EVENT_INIT_VALUE (0, E4_EVENT_WRITE, E4_EVENT_DTYPE_LONG,0));
++
++    spin_lock_irqsave (&rail->r_ecq_lock, flags);
++    /* first flush all the "event" queues */
++    list_for_each (el, &rail->r_ecq_list[EP4_ECQ_EVENT]) {
++      EP4_ECQ *ecq = list_entry (el, EP4_ECQ, ecq_link);
++
++      elan4_sdram_writeq (dev, ecq->ecq_event + offsetof (E4_Event32, ev_CountAndType),
++                          E4_EVENT_INIT_VALUE (-32, E4_EVENT_WRITE, E4_EVENT_DTYPE_LONG, 0));
++
++      ep4_set_event_cmd (ecq->ecq_flushcq, ecq->ecq_event_addr);
++
++      rail->r_flush_count++;
++    }
++
++    /* next issue the setevents to all the other queues */
++    for (i = EP4_ECQ_ATOMIC; i <EP4_NUM_ECQ; i++)
++    {
++      list_for_each (el,&rail->r_ecq_list[i]) {
++          EP4_ECQ *ecq = list_entry (el, EP4_ECQ, ecq_link);
++
++          ep4_set_event_cmd (ecq, rail->r_elan_addr + offsetof (EP4_RAIL_ELAN, r_flush_event));
++
++          rail->r_flush_count++;
++      }
++    }
++
++    /* issue the waitevent command */
++    ep4_wait_event_cmd (rail->r_flush_mcq,  rail->r_elan_addr + offsetof (EP4_RAIL_ELAN, r_flush_event),
++                      E4_EVENT_INIT_VALUE (-32 * rail->r_flush_count, E4_EVENT_WRITE, E4_EVENT_DTYPE_LONG,0),
++                      rail->r_flush_ecq->ecq_addr,
++                      INTERRUPT_CMD | (rail->r_flush_intcookie.int_val << E4_MAIN_INT_SHIFT));
++    
++    while (rail->r_flush_count)
++      kcondvar_wait (&rail->r_flush_sleep, &rail->r_ecq_lock, &flags);
++    
++    spin_unlock_irqrestore (&rail->r_ecq_lock, flags);
++
++    EP4_SDRAM_ASSERT (rail, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_flush_event), E4_EVENT_INIT_VALUE (0, E4_EVENT_WRITE, E4_EVENT_DTYPE_LONG,0));
++
++    kmutex_unlock (&rail->r_flush_mutex);
++}
++
++void
++ep4_init_thread (EP4_RAIL *rail, E4_ThreadRegs *regs, sdramaddr_t stackTop, 
++               EP_ADDR stackAddr, E4_Addr startpc, int nargs,...)
++{
++    sdramaddr_t   sp = stackTop - roundup (nargs * sizeof (E4_uint64), E4_STACK_ALIGN);
++    int           i;
++    va_list       ap;
++    
++    /*
++     * the thread start code expects the following :
++     *   %r1 = stack pointer
++     *   %r6 = frame pointer
++     *   %r2 = function to call
++     *
++     *   function args are store on stack above %sp
++     */
++
++    va_start(ap, nargs);
++    for (i = 0; i < nargs; i++)
++      elan4_sdram_writeq (rail->r_ctxt.ctxt_dev, sp + (i * sizeof (E4_uint64)), va_arg (ap, E4_uint64));
++    va_end (ap);
++    
++    regs->Registers[0] = ep_symbol (&rail->r_threadcode, ".thread_start");            /* %r0 - PC */
++    regs->Registers[1] = stackAddr - (stackTop - sp);                                 /* %r1 - stack pointer */
++    regs->Registers[2] = startpc;                                                     /* %r2 - start pc */
++    regs->Registers[3] = 0;
++    regs->Registers[4] = 0;
++    regs->Registers[5] = 0;
++    regs->Registers[6] = stackTop;                                                    /* %r6 - frame pointer */ 
++}
++
++/* retransmission thread */
++
++void
++ep4_add_retry_ops (EP4_RAIL *rail, EP4_RETRY_OPS *ops)
++{
++    ep_kthread_stall (&rail->r_retry_thread);
++    list_add_tail (&ops->op_link, &rail->r_retry_ops);
++    ep_kthread_resume (&rail->r_retry_thread);
++}
++
++void
++ep4_remove_retry_ops (EP4_RAIL *rail, EP4_RETRY_OPS *ops)
++{
++    ep_kthread_stall (&rail->r_retry_thread);
++    list_del (&ops->op_link);
++    ep_kthread_resume (&rail->r_retry_thread);
++}
++
++void
++ep4_retry_thread (EP4_RAIL *rail)
++{
++    struct list_head *el;
++
++    kernel_thread_init ("ep4_retry");
++    
++    for (;;)
++    {
++      long nextRunTime = 0;
++
++      list_for_each (el, &rail->r_retry_ops) {
++          EP4_RETRY_OPS *ops = list_entry (el, EP4_RETRY_OPS, op_link);
++
++          nextRunTime = ops->op_func (rail, ops->op_arg, nextRunTime);
++      }
++
++      if (ep_kthread_sleep (&rail->r_retry_thread, nextRunTime) < 0)
++          break;
++    }
++
++    ep_kthread_stopped (&rail->r_retry_thread);
++
++    kernel_thread_exit();
++}
++
++/* DMA retransmission */
++static unsigned ep4_dma_retry_times[EP_NUM_RETRIES];
++
++static unsigned long
++ep4_retry_dmas (EP4_RAIL *rail, void *arg, unsigned long nextRunTime)
++{
++    unsigned long yieldAt = lbolt + (hz/10);
++    unsigned long flags;
++    int           i;
++
++    for (i = EP_RETRY_BASE; i < EP_NUM_RETRIES; i++)
++    {
++      while (! list_empty (&rail->r_dma_retrylist[i]))
++      {
++          EP4_DMA_RETRY *retry = list_entry (rail->r_dma_retrylist[i].next, EP4_DMA_RETRY, retry_link);
++          
++          if (! AFTER(lbolt, retry->retry_time))
++              break;
++
++          if (ep_kthread_should_stall (&rail->r_retry_thread) || AFTER (lbolt, yieldAt))
++              goto cant_do_more;
++          
++          EPRINTF3 (DBG_RETRY, "%s: ep4_retry_dmas: flowcnt %llx %llx\n", rail->r_generic.Name, rail->r_dma_flowcnt, rail->r_main->r_dma_flowcnt);
++
++          if ((rail->r_dma_flowcnt - rail->r_main->r_dma_flowcnt) > EP4_DMA_RETRY_FLOWCNT)
++          {
++              printk ("ep4_retry_dmas: flowcnt %llx %llx\n", rail->r_dma_flowcnt, rail->r_main->r_dma_flowcnt);
++
++              goto cant_do_more;
++          }
++
++          EPRINTF4 (DBG_RETRY, "%s: ep4_retry_dmas: %016llx %016llx %016llx\n", rail->r_generic.Name,
++                    retry->retry_dma.dma_typeSize, retry->retry_dma.dma_cookie, retry->retry_dma.dma_vproc);
++          EPRINTF5 (DBG_RETRY, "%s:                  %016llx %016llx %016llx %016llx\n", rail->r_generic.Name,
++                    retry->retry_dma.dma_srcAddr, retry->retry_dma.dma_dstAddr, retry->retry_dma.dma_srcEvent, 
++                    retry->retry_dma.dma_dstEvent);
++
++          elan4_run_dma_cmd (rail->r_dma_ecq->ecq_cq, &retry->retry_dma);
++          elan4_write_dword_cmd (rail->r_dma_ecq->ecq_cq, rail->r_main_addr + offsetof (EP4_RAIL_MAIN, r_dma_flowcnt), ++rail->r_dma_flowcnt);
++
++          spin_lock_irqsave (&rail->r_dma_lock, flags);
++          list_del (&retry->retry_link);
++          list_add (&retry->retry_link, &rail->r_dma_freelist);
++          spin_unlock_irqrestore (&rail->r_dma_lock, flags);
++      }
++    }
++ cant_do_more:
++
++    /* re-compute the next retry time */
++    for (i = EP_RETRY_BASE; i < EP_NUM_RETRIES; i++)
++    {
++      if (! list_empty (&rail->r_dma_retrylist[i]))
++      {
++          EP4_DMA_RETRY *retry = list_entry (rail->r_dma_retrylist[i].next, EP4_DMA_RETRY, retry_link);
++
++          SET_NEXT_RUN_TIME (nextRunTime, retry->retry_time);
++      }
++    }
++
++    return nextRunTime;
++}
++
++void
++ep4_initialise_dma_retries (EP4_RAIL *rail)
++{
++    int i;
++
++    spin_lock_init (&rail->r_dma_lock);
++    
++    for (i = 0; i < EP_NUM_RETRIES; i++)
++      INIT_LIST_HEAD (&rail->r_dma_retrylist[i]);
++    
++    INIT_LIST_HEAD (&rail->r_dma_freelist);
++    
++    rail->r_dma_ecq = ep4_alloc_ecq (rail, EP4_DMA_RETRY_CQSIZE);
++    
++    rail->r_dma_allocated = 0;
++    rail->r_dma_reserved  = 0;
++
++    ep4_dma_retry_times[EP_RETRY_HIGH_PRI] = EP_RETRY_HIGH_PRI_TIME;
++
++    for (i =0 ; i < EP_NUM_BACKOFF; i++)
++      ep4_dma_retry_times[EP_RETRY_HIGH_PRI_RETRY+i] = EP_RETRY_HIGH_PRI_TIME << i;
++    
++    ep4_dma_retry_times[EP_RETRY_LOW_PRI] = EP_RETRY_LOW_PRI_TIME;
++
++    for (i =0 ; i < EP_NUM_BACKOFF; i++)
++      ep4_dma_retry_times[EP_RETRY_LOW_PRI_RETRY+i] = EP_RETRY_LOW_PRI_TIME << i;
++    
++    ep4_dma_retry_times[EP_RETRY_ANONYMOUS] = EP_RETRY_ANONYMOUS_TIME;
++    ep4_dma_retry_times[EP_RETRY_NETERR]    = EP_RETRY_NETERR_TIME;
++
++    rail->r_dma_ops.op_func = ep4_retry_dmas;
++    rail->r_dma_ops.op_arg  = NULL;
++
++    ep4_add_retry_ops (rail, &rail->r_dma_ops);
++}
++
++void
++ep4_finalise_dma_retries (EP4_RAIL *rail)
++{
++    ep4_remove_retry_ops (rail, &rail->r_dma_ops);
++
++    /* Everyone should have given back their retry dma's by now */
++    EP4_ASSERT (rail, rail->r_dma_reserved == 0);
++
++    while (! list_empty (&rail->r_dma_freelist))
++    {
++      EP4_DMA_RETRY *retry = list_entry (rail->r_dma_freelist.next, EP4_DMA_RETRY, retry_link);
++
++      list_del (&retry->retry_link);
++
++      KMEM_FREE (retry, sizeof (EP4_DMA_RETRY));
++    }
++
++    ep4_free_ecq (rail, rail->r_dma_ecq);
++
++    spin_lock_destroy (&rail->r_dma_lock);
++}
++
++int
++ep4_reserve_dma_retries (EP4_RAIL *rail, unsigned int count, EP_ATTRIBUTE attr)
++{
++    EP4_DMA_RETRY *retry;
++    unsigned int   remaining = count;
++    unsigned long  flags;
++
++    spin_lock_irqsave (&rail->r_dma_lock, flags);
++
++    if (remaining <= (rail->r_dma_allocated - rail->r_dma_reserved))
++    {
++      rail->r_dma_reserved += remaining;
++
++      spin_unlock_irqrestore (&rail->r_dma_lock, flags);
++
++      return 0;
++    }
++    
++    remaining -= (rail->r_dma_allocated - rail->r_dma_reserved);
++
++    rail->r_dma_reserved = rail->r_dma_allocated;
++
++    spin_unlock_irqrestore (&rail->r_dma_lock, flags);
++
++    while (remaining > 0)
++    {
++      KMEM_ALLOC (retry, EP4_DMA_RETRY *, sizeof (EP4_DMA_RETRY), !(attr & EP_NO_SLEEP));
++
++      if (retry == NULL)
++          goto failed;
++      
++      remaining--;
++
++      spin_lock_irqsave (&rail->r_dma_lock, flags);
++      list_add (&retry->retry_link, &rail->r_dma_freelist);
++
++      rail->r_dma_allocated++;
++      rail->r_dma_reserved++;
++      spin_unlock_irqrestore (&rail->r_dma_lock, flags);
++    }
++
++    return 0;
++
++ failed:
++    spin_lock_irqsave (&rail->r_dma_lock, flags);
++    rail->r_dma_reserved -= (count - remaining);
++    spin_unlock_irqrestore (&rail->r_dma_lock, flags);
++
++    return 1;
++}
++
++void
++ep4_release_dma_retries (EP4_RAIL *rail, unsigned int count)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&rail->r_dma_lock, flags);
++    rail->r_dma_reserved -= count;
++    spin_unlock_irqrestore (&rail->r_dma_lock, flags);
++}
++
++void
++ep4_queue_dma_retry (EP4_RAIL *rail, E4_DMA *dma, int interval)
++{
++    EP4_DMA_RETRY *retry;
++    unsigned long  flags;
++    
++    spin_lock_irqsave (&rail->r_dma_lock, flags);
++
++    EP4_ASSERT (rail, !list_empty (&rail->r_dma_freelist));
++    
++    /* take an item of the free list */
++    retry = list_entry (rail->r_dma_freelist.next, EP4_DMA_RETRY, retry_link);
++
++    list_del (&retry->retry_link);
++    
++    EPRINTF5 (DBG_RETRY, "%s: ep4_queue_dma_retry: %016llx %016llx %016llx %016llx\n", rail->r_generic.Name,
++            dma->dma_typeSize, dma->dma_cookie, dma->dma_vproc, dma->dma_srcAddr);
++    EPRINTF5 (DBG_RETRY, "%s:                      %016llx %016llx %016llx (%d)\n", rail->r_generic.Name,
++            dma->dma_dstAddr, dma->dma_srcEvent, dma->dma_dstEvent, interval);
++
++    retry->retry_dma.dma_typeSize = dma->dma_typeSize;
++    retry->retry_dma.dma_cookie   = dma->dma_cookie;
++    retry->retry_dma.dma_vproc    = dma->dma_vproc;
++    retry->retry_dma.dma_srcAddr  = dma->dma_srcAddr;
++    retry->retry_dma.dma_dstAddr  = dma->dma_dstAddr;
++    retry->retry_dma.dma_srcEvent = dma->dma_srcEvent;
++    retry->retry_dma.dma_dstEvent = dma->dma_dstEvent;
++
++    retry->retry_time             = lbolt + ep4_dma_retry_times[interval];
++
++    /* chain onto the end of the approriate retry list */
++    list_add_tail (&retry->retry_link, &rail->r_dma_retrylist[interval]);
++
++    ep_kthread_schedule (&rail->r_retry_thread, retry->retry_time);
++
++    spin_unlock_irqrestore (&rail->r_dma_lock, flags);
++}
++
++void
++ep4_queue_dma_stalled (EP4_RAIL *rail, E4_DMA *dma)
++{
++    EP_NODE_RAIL  *nodeRail = &rail->r_generic.Nodes[EP_VP_TO_NODE(dma->dma_vproc)];
++    EP4_DMA_RETRY *retry;
++    unsigned long  flags;
++    
++    spin_lock_irqsave (&rail->r_dma_lock, flags);
++
++    EP4_ASSERT (rail, !list_empty (&rail->r_dma_freelist));
++    
++    /* take an item of the free list */
++    retry = list_entry (rail->r_dma_freelist.next, EP4_DMA_RETRY, retry_link);
++
++    list_del (&retry->retry_link);
++    
++    EPRINTF5 (DBG_RETRY, "%s: ep4_queue_dma_stalled: %016llx %016llx %016llx %016llx\n", rail->r_generic.Name,
++            dma->dma_typeSize, dma->dma_cookie, dma->dma_vproc, dma->dma_srcAddr);
++    EPRINTF4 (DBG_RETRY, "%s:                        %016llx %016llx %016llx\n", rail->r_generic.Name,
++            dma->dma_dstAddr, dma->dma_srcEvent, dma->dma_dstEvent);
++
++    retry->retry_dma.dma_typeSize = dma->dma_typeSize;
++    retry->retry_dma.dma_cookie   = dma->dma_cookie;
++    retry->retry_dma.dma_vproc    = dma->dma_vproc;
++    retry->retry_dma.dma_srcAddr  = dma->dma_srcAddr;
++    retry->retry_dma.dma_dstAddr  = dma->dma_dstAddr;
++    retry->retry_dma.dma_srcEvent = dma->dma_srcEvent;
++    retry->retry_dma.dma_dstEvent = dma->dma_dstEvent;
++
++    /* chain onto the node cancelled dma list */
++    list_add_tail (&retry->retry_link, &nodeRail->StalledDmas);
++
++    spin_unlock_irqrestore (&rail->r_dma_lock, flags);
++}
++
++void
++ep4_free_stalled_dmas (EP4_RAIL *rail, unsigned int nodeId)
++{
++    EP_NODE_RAIL *nodeRail = &rail->r_generic.Nodes[nodeId];
++    struct list_head *el, *nel;
++    unsigned long flags;
++
++    spin_lock_irqsave (&rail->r_dma_lock, flags);
++    list_for_each_safe (el, nel, &nodeRail->StalledDmas) {
++      list_del (el);
++      list_add (el, &rail->r_dma_freelist);
++    }
++    spin_unlock_irqrestore (&rail->r_dma_lock, flags);
++}
++
++void
++ep4_display_rail (EP4_RAIL *rail)
++{
++    ELAN4_DEV        *dev = rail->r_ctxt.ctxt_dev;
++    struct list_head *el;
++    register int      i;
++    unsigned long     flags;
++
++    ep_debugf (DBG_DEBUG, "%s: vendorid=%x deviceid=%x\n", rail->r_generic.Name, 
++             rail->r_generic.Devinfo.dev_vendor_id, rail->r_generic.Devinfo.dev_device_id);
++
++    spin_lock_irqsave (&rail->r_ecq_lock, flags);
++    for (i = 0; i < EP4_NUM_ECQ; i++)
++    {
++      list_for_each (el, &rail->r_ecq_list[i]) {
++          EP4_ECQ *ecq = list_entry (el, EP4_ECQ, ecq_link);
++          
++          if (i == EP4_ECQ_EVENT)
++              ep_debugf (DBG_DEBUG, "   ECQ[%d] ecq=%p cqnum=%d addr=%llx avail=%d event=%llx,%llx,%llx\n",
++                         i, ecq, elan4_cq2num (ecq->ecq_cq), ecq->ecq_addr, ecq->ecq_avail,
++                         elan4_sdram_readq (dev, ecq->ecq_event + offsetof (E4_Event32, ev_CountAndType)),
++                         elan4_sdram_readq (dev, ecq->ecq_event + offsetof (E4_Event32, ev_WriteValue)),
++                         elan4_sdram_readq (dev, ecq->ecq_event + offsetof (E4_Event32, ev_WritePtr)));
++
++          else
++              ep_debugf (DBG_DEBUG, "   ECQ[%d] ecq=%p cqnum=%d addr=%llx avail=%d\n",
++                         i, ecq, elan4_cq2num (ecq->ecq_cq), ecq->ecq_addr, ecq->ecq_avail);
++      }
++    }
++    spin_unlock_irqrestore (&rail->r_ecq_lock, flags);
++
++    ep_debugf (DBG_DEBUG, "   flush count=%ld mcq=%p ecq=%p event %llx.%llx.%llx\n", 
++             rail->r_flush_count, rail->r_flush_mcq, rail->r_flush_ecq,
++             elan4_sdram_readq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_flush_event.ev_CountAndType)),
++             elan4_sdram_readq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_flush_event.ev_WritePtr)),
++             elan4_sdram_readq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_flush_event.ev_WriteValue)));
++    
++    spin_lock_irqsave (&rail->r_dma_lock, flags);
++    for (i = 0; i < EP_NUM_RETRIES; i++)
++    {
++      list_for_each (el, &rail->r_dma_retrylist[i]) {
++          EP4_DMA_RETRY *retry = list_entry (el, EP4_DMA_RETRY, retry_link);
++          
++          ep_debugf (DBG_DEBUG, "    RETRY[%d] typeSize %llx cookie %llx vproc %llx events %llx %llx\n",
++                     i, retry->retry_dma.dma_typeSize, retry->retry_dma.dma_cookie,
++                     retry->retry_dma.dma_vproc, retry->retry_dma.dma_srcEvent, retry->retry_dma.dma_dstEvent);
++      }
++    }
++    spin_unlock_irqrestore (&rail->r_dma_lock, flags);
++}
+Index: linux-2.4.21/drivers/net/qsnet/ep/threadcode.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/threadcode.c        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/threadcode.c     2005-06-01 23:12:54.689425424 -0400
+@@ -0,0 +1,146 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: threadcode.c,v 1.11 2003/10/07 13:22:38 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/threadcode.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++
++EP_ADDR
++ep_symbol (EP_CODE *code, char *name)
++{
++    EP_SYMBOL *s = code->symbols;
++    
++    while (s->name && strcmp (s->name, name))
++      s++;
++    
++    return (s->name ? s->value : (EP_ADDR) 0);
++}
++
++int
++ep_loadcode (EP_RAIL *rail, EP_CODE *code)
++{
++    register int i;
++
++    EP_ADDR  _stext  = ep_symbol (code, "_stext");
++    EP_ADDR  _etext  = ep_symbol (code, "_etext");
++    EP_ADDR  _sdata  = ep_symbol (code, "_sdata");
++    EP_ADDR  _edata  = ep_symbol (code, "_edata");
++    EP_ADDR  _end    = ep_symbol (code, "_end");
++    EP_ADDR  _rodata = roundup (_etext, sizeof (uint64_t));
++
++    if (_stext == (EP_ADDR) 0 || _etext == (EP_ADDR) 0 ||
++      _sdata == (EP_ADDR) 0 || _edata == (EP_ADDR) 0 ||
++      _end == (EP_ADDR) 0)
++    {
++      printk ("ep_loadcode: symbols not defined correctly for code at %p\n", code);
++      return (EINVAL);
++    }
++
++    /*
++     * Include the rodata in the text segment
++     */
++    _etext = _rodata + code->rodata_size;
++
++    /*
++     * If _etext is in the same page as _sdata,  then allocate a contiguous
++     * chunk of memory and map it as read/write. otherwise allocate two chunks
++     * and map the code in as read-only.
++     */
++    if ((_etext & PAGEMASK) == (_sdata & PAGEMASK))
++    {
++      code->ntext  = btopr (_end - (_stext & PAGEMASK));
++      code->pptext = ep_alloc_memory_elan (rail, _stext & PAGEMASK, ptob (code->ntext), EP_PERM_EXECUTE, 0);
++
++      if (code->pptext == (sdramaddr_t) 0)
++          return (ENOMEM);
++      
++      code->_stext  = code->pptext + (_stext & PAGEOFFSET);
++      code->_rodata = code->_stext + (_rodata - _stext);
++      code->_sdata  = code->_stext + (_sdata - _stext);
++    }
++    else
++    {
++      code->ntext  = btopr (_etext - (_stext & PAGEMASK));
++      code->ndata  = btopr (_end - (_sdata & PAGEMASK));
++
++      if (code->ntext)
++      {
++          code->pptext = ep_alloc_memory_elan (rail, _stext & PAGEMASK, ptob (code->ntext), EP_PERM_EXECUTE, 0);
++
++          if (code->pptext == (sdramaddr_t) 0)
++              return (ENOMEM);
++
++          code->_stext  = code->pptext + (_stext & PAGEOFFSET);
++          code->_rodata = code->_stext + (_rodata - _stext);
++      }
++      
++      if (code->ndata)
++      {
++          code->ppdata = ep_alloc_memory_elan (rail, _sdata & PAGEMASK, ptob (code->ndata), EP_PERM_WRITE, 0);
++
++          if (code->ppdata == (sdramaddr_t) 0)
++          {
++              if (code->ntext) ep_free_memory_elan (rail, _sdata & PAGEMASK);
++              code->ntext = 0;
++
++              return (ENOMEM);
++          }
++          
++          code->_sdata = code->ppdata + (_sdata & PAGEOFFSET);
++      }
++    }
++    
++#ifdef __LITTLE_ENDIAN__
++#  define Flip        3
++#else
++#  define Flip  0
++#endif
++
++    /*
++     * Now copy the text and rodata into the SDRAM
++     * this is linked into the module to be byte 
++     * copied to the SDRAM, since we want to copy
++     * with word accesses we have to do the byte
++     * assembly correctly.
++     */
++    for (i = 0; i < code->text_size; i++)
++      rail->Operations.SdramWriteb (rail, code->_stext + i, code->text[i^Flip]);
++
++    for (i = 0; i < code->rodata_size; i++)
++      rail->Operations.SdramWriteb (rail, code->_rodata + i, code->rodata[i^Flip]);
++    
++    /*
++     * And the initialised data segment.
++     */
++    for (i = 0; i < code->data_size; i++)
++      rail->Operations.SdramWriteb (rail, code->_sdata + i, code->data[i^Flip]);
++
++    return (ESUCCESS);
++}
++
++void
++ep_unloadcode (EP_RAIL *rail, EP_CODE *code)
++{
++    EP_ADDR  _stext = ep_symbol (code, "_stext");
++    EP_ADDR  _sdata = ep_symbol (code, "_sdata");
++
++    if (code->pptext)
++      ep_free_memory_elan (rail, _stext & PAGEMASK);
++    if (code->ppdata)
++      ep_free_memory_elan (rail, _sdata & PAGEMASK);
++    code->pptext = code->ppdata = 0;
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/ep/threadcode_elan3.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/threadcode_elan3.c  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/threadcode_elan3.c       2005-06-01 23:12:54.690425272 -0400
+@@ -0,0 +1,85 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: threadcode_elan3.c,v 1.11 2003/10/07 13:22:38 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/threadcode_elan3.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++
++#include "kcomm_elan3.h"
++#include "debug.h"
++
++#include <elan3/thread.h>
++
++E3_Addr
++ep3_init_thread (ELAN3_DEV  *dev,
++               E3_Addr     fn,                                /* Elan address of function */
++               E3_Addr     addr,                              /* Elan address of stack */
++               sdramaddr_t stack,                             /* sdram address of stack */
++               int           stackSize,                       /* stack size (in bytes) */
++               int           nargs,
++               ...)
++{
++    sdramaddr_t  frame;
++    sdramaddr_t  regs;
++    sdramaddr_t  argsp;
++    int                i;
++    va_list      ap;
++
++    /*
++     * Align the stack pointer at the top of the stack and leave space for a stack frame
++     */
++    stack = ((stack + stackSize) & ~(E3_STACK_ALIGN-1)) - sizeof (E3_Frame);
++    addr  = ((addr  + stackSize) & ~(E3_STACK_ALIGN-1)) - sizeof (E3_Frame);
++
++    va_start (ap, nargs);
++
++    if (nargs > 6)
++    {
++      stack -= (((nargs*sizeof (E3_uint32))+E3_STACK_ALIGN-1) & ~(E3_STACK_ALIGN-1));
++      addr  -= (((nargs*sizeof (E3_uint32))+E3_STACK_ALIGN-1) & ~(E3_STACK_ALIGN-1));
++    }
++    
++    frame  = stack;
++    regs   = stack - sizeof (E3_OutsRegs);
++
++    /*
++     * Initialise the registers, and stack frame.
++     */
++    elan3_sdram_writel (dev, regs + offsetof (E3_OutsRegs, o[6]), fn);
++    elan3_sdram_writel (dev, regs + offsetof (E3_OutsRegs, o[7]), 0);
++    
++    if (nargs <= 6)
++    {
++      for (i = 0; i < nargs; i++)
++          elan3_sdram_writel (dev, regs + offsetof (E3_OutsRegs, o[i]), va_arg (ap, E3_uint32));
++    }
++    else
++    {
++      for (i = 0; i < 6; i++)
++          elan3_sdram_writel (dev, regs + offsetof (E3_OutsRegs, o[i]), va_arg (ap, E3_uint32));
++      
++      for (argsp = frame + offsetof (E3_Frame, fr_argx[0]); i < nargs; i++, argsp += sizeof (E3_uint32))
++          elan3_sdram_writel (dev, argsp, va_arg (ap, int));
++    }
++
++    elan3_sdram_writel (dev, frame + offsetof (E3_Frame, fr_savefp), 0);
++    elan3_sdram_writel (dev, frame + offsetof (E3_Frame, fr_savepc), 0);
++
++    va_end (ap);
++
++    return (addr);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/ep/threadcode_elan3_Linux.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/threadcode_elan3_Linux.c    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/threadcode_elan3_Linux.c 2005-06-01 23:12:54.690425272 -0400
+@@ -0,0 +1,112 @@
++/* --------------------------------------------------------*/
++/* MACHINE GENERATED ELAN CODE                             */
++#include <qsnet/kernel.h>
++#include <elan/kcomm.h>
++#include "kcomm_elan3.h"
++static uint32_t threadcode_elan3_text[] = {
++0x80a0239c, 0x00001082, 0x00e0a280, 0x47008002, 0x0020a380, 0x20600288, 0x20200286, 0x43008002, 
++0x00000001, 0x0a006081, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 
++0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 
++0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 
++0x00000001, 0x00000001, 0xa800c613, 0xa300c609, 0x0020108a, 0x0080900b, 0x00006885, 0x0580a080, 
++0x06008002, 0x02a0a080, 0x06008022, 0xffff0296, 0x04008010, 0xff3f0398, 0x1f008010, 0x00201090, 
++0x00007081, 0x1600801c, 0x00000001, 0x60a0239c, 0x00a0a3c0, 0x20a0a3f0, 0x40a0a3e0, 0x00c03f3f, 
++0xf8e017be, 0x04e08f80, 0x06008012, 0x00000001, 0x00c01ffc, 0x0000a081, 0x06008010, 0x40a083e0, 
++0x14e007be, 0x00c01ffc, 0x0000a081, 0x40a083e0, 0x20a083f0, 0x00a083c0, 0x60a0039c, 0x00e0a280, 
++0xbfffbf12, 0x0020a380, 0x03008012, 0x02201090, 0x03201090, 0x08e0c381, 0x80a0039c, 0xe0a0239c, 
++0x60a023de, 0x80a0a3e0, 0xa0a0a3f0, 0x080010b8, 0x090010b0, 0x0a0010b2, 0x04000037, 0x402006b4, 
++0x50200690, 0x01201092, 0x20a0239c, 0x00a0a3f0, 0x00c03f3f, 0x8ce117be, 0x04e08f80, 0x06008012, 
++0x00000001, 0x00c01ff8, 0x0000b081, 0x06008010, 0x00a083f0, 0x14e007be, 0x00c01ff8, 0x0000b081, 
++0x00a083f0, 0x20a0039c, 0x582006d0, 0x0020a280, 0x05008002, 0x0900a280, 0x10008002, 0x50200690, 
++0xeaffbf30, 0x5c2006d4, 0x18001090, 0x19001092, 0x1b800294, 0x0a201096, 0x8affff7f, 0x05201098, 
++0x446026d0, 0x302027f4, 0xdfffbf10, 0x50200690, 0xfdffbf10, 0x446026c0, 0x5c2006e0, 0x0020a480, 
++0xf9ffbf06, 0x18001090, 0x19001092, 0x1b000494, 0x14201096, 0x7bffff7f, 0x0a201098, 0x0020a280, 
++0xf4ffbf22, 0x486026e0, 0x00007081, 0x1600801c, 0x00000001, 0x60a0239c, 0x00a0a3c0, 0x20a0a3f0, 
++0x40a0a3e0, 0x00c03f3f, 0x60e217be, 0x04e08f80, 0x06008012, 0x00000001, 0x00c01ffc, 0x0000a081, 
++0x06008010, 0x40a083e0, 0x14e007be, 0x00c01ffc, 0x0000a081, 0x40a083e0, 0x20a083f0, 0x00a083c0, 
++0x60a0039c, 0xff3f84a0, 0xe0ffbf1c, 0x18001090, 0xd5ffbf30, 0x60a003de, 0x80a083e0, 0xa0a083f0, 
++0x08e0c381, 0xe0a0039c, 0x00a1239c, 0x60a023de, 0x80a0a3e0, 0xa0a0a3f0, 0x44a123d0, 0x090010b0, 
++0x0a0010b6, 0x0b0010b8, 0x0c0010b4, 0x012010ba, 0xdca023fa, 0x142007d2, 0x082007d0, 0x084002b2, 
++0x000027c0, 0xf42006d0, 0x0020a280, 0x15008032, 0xf42006d0, 0x18200790, 0xdca003d2, 0x20a0239c, 
++0x00a0a3f0, 0x00c03f3f, 0x20e317be, 0x04e08f80, 0x06008012, 0x00000001, 0x00c01ff8, 0x0000b081, 
++0x06008010, 0x00a083f0, 0x14e007be, 0x00c01ff8, 0x0000b081, 0x00a083f0, 0x20a0039c, 0xf42006d0, 
++0x0020a280, 0x0a008022, 0xdca023c0, 0x042007d0, 0x0840a680, 0x06008032, 0xdca023c0, 0x18001082, 
++0x0220d091, 0xe1ffbf10, 0xf42006d0, 0x06008010, 0x190010a2, 0x042006d0, 0x00c026d0, 0x18001082, 
++0x0020d091, 0x042006d0, 0x01200290, 0x042026d0, 0x000006d0, 0x0020a280, 0x04008002, 0x18001090, 
++0x4f010040, 0x1b001092, 0xf02006e0, 0x0020a480, 0xf1ffbf02, 0x40b03611, 0x004004d2, 0x01201290, 
++0x0840a280, 0x0e018012, 0x10001096, 0x046004d0, 0x01208a80, 0x33008002, 0xa0200484, 0x0c2610ba, 
++0x000024fa, 0x00211090, 0x042024d0, 0x246004d0, 0x80200290, 0x082024d0, 0xec2004d0, 0x00210290, 
++0x0c2024d0, 0x102024c4, 0x186004d2, 0x02602a93, 0x098006d0, 0x0001003b, 0x1d000290, 0x098026d0, 
++0xc0ff3f3b, 0x1d000a90, 0x44a103fa, 0x606007d2, 0x00680292, 0x09001290, 0x4000003b, 0x1d001290, 
++0x142024d0, 0x206004d0, 0x10210290, 0x182024d0, 0x186004d0, 0x02202a91, 0x088006d2, 0x0001003b, 
++0x1d400292, 0x088026d2, 0xc0ff3f3b, 0x1d400a92, 0x186004d0, 0x00280290, 0x80000015, 0x0a001290, 
++0x08401292, 0x4000003b, 0x1d401292, 0x1c2024d2, 0x01201090, 0xa02024d0, 0x20200496, 0xa8200484, 
++0x306004d0, 0x0020a280, 0x2b008012, 0x00201098, 0x0c2610ba, 0x00c022fa, 0x04e022c0, 0xc0200490, 
++0x10e022d0, 0x186004d2, 0x02602a93, 0x098006d0, 0x0001003b, 0x1d000290, 0x098026d0, 0xc0ff3f3b, 
++0x1d000a90, 0x44a103fa, 0x606007d2, 0x00680292, 0x09001290, 0x4000003b, 0x1d001290, 0x14e022d0, 
++0x206004d0, 0x10210290, 0x18e022d0, 0x186004d0, 0x02202a91, 0x088006d2, 0x0001003b, 0x1d400292, 
++0x088026d2, 0xc0ff3f3b, 0x1d400a92, 0x186004d0, 0x00280290, 0x80000015, 0x0a001290, 0x08401292, 
++0x4000003b, 0x1d401292, 0x1ce022d2, 0x4f008010, 0x0020109a, 0x0c00109a, 0x306004d0, 0x0840a380, 
++0x3b00801a, 0xe02004c6, 0x0c2610ba, 0x00c022fa, 0x01202b91, 0x0c000290, 0x02202a91, 0x08400490, 
++0x382002d2, 0x04e022d2, 0x342002d0, 0x08e022d0, 0x0ce022c6, 0x10e022c4, 0x186004d0, 0x02202a91, 
++0x088006d2, 0x0001003b, 0x1d400292, 0x088026d2, 0xc0ff3f3b, 0x1d400a92, 0x44a103fa, 0x606007d0, 
++0x00280290, 0x08401292, 0x4000003b, 0x1d401292, 0x14e022d2, 0x206004d0, 0x10210290, 0x18e022d0, 
++0x186004d0, 0x02202a91, 0x088006d4, 0x0001003b, 0x1d800294, 0x088026d4, 0xc0ff3f3b, 0x1d800a94, 
++0x186004d0, 0x00280290, 0x80000013, 0x09001290, 0x08801294, 0x4000003b, 0x1d801294, 0x1ce022d4, 
++0x01201090, 0x008020d0, 0x04e002d0, 0x08c00086, 0x0840039a, 0x01200398, 0x20e00296, 0x306004d0, 
++0x0800a380, 0xc9ffbf0a, 0x08a00084, 0xc0200490, 0xf0ff22d0, 0xe42004d0, 0x0d00a280, 0x0b00801a, 
++0x00201098, 0x04008010, 0x10001096, 0x01200398, 0x20e00296, 0x306004d0, 0x0800a380, 0xfcffbf2a, 
++0x04e022c0, 0xfc3f109a, 0xe42024da, 0x10001082, 0x186004d0, 0x00280290, 0x08006081, 0x00000001, 
++0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 
++0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 
++0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00201098, 
++0x0c00109a, 0x142004fa, 0xec00823b, 0x3080d61b, 0x00006891, 0x0420a280, 0x3b008002, 0x0c00a280, 
++0x04008002, 0x00000001, 0x0120d091, 0x36008030, 0x7c2006d0, 0x01200290, 0x7c2026d0, 0x782006d0, 
++0x0020a280, 0x04008002, 0x78200690, 0x64000040, 0x40e00692, 0xf02004d0, 0x0020a280, 0x03008012, 
++0xf02026d0, 0x80e026c0, 0x7c2006d0, 0x40e026d0, 0x046004d0, 0x04208a80, 0x13008002, 0x1100108a, 
++0xec2004cc, 0x3fa00b8e, 0x40e0018e, 0x0780239c, 0x0080bbe0, 0x006099e0, 0x00a0b9e0, 0x406099e0, 
++0x40a0b9e0, 0x806099e0, 0x80a0b9e0, 0xc06099e0, 0xc0a0b9e0, 0x00809be0, 0x0780039c, 0x0e008010, 
++0xec2004d2, 0xec2004cc, 0x3fa00b8e, 0x40e0018e, 0x0780239c, 0x0080bbe0, 0x006099e0, 0x00a0b9e0, 
++0x406099e0, 0x40a0b9e0, 0x00809be0, 0x0780039c, 0xec2004d2, 0xe42004d0, 0x886222d0, 0x042006d0, 
++0x00c026d0, 0x000007d0, 0x01208a80, 0x05008012, 0x00000001, 0x142027f2, 0x06008010, 0xdca003fa, 
++0x142027f2, 0xfe3f0a90, 0x000027d0, 0xdca003fa, 0x016007ba, 0xdca023fa, 0x0c2007d0, 0x0840a680, 
++0x04008032, 0x082007d0, 0x03008010, 0x102007f2, 0x084006b2, 0x00007081, 0x1600801c, 0x00000001, 
++0x60a0239c, 0x00a0a3c0, 0x20a0a3f0, 0x40a0a3e0, 0x02c03f3f, 0x8ce017be, 0x04e08f80, 0x06008012, 
++0x00000001, 0x00c01ffc, 0x0000a081, 0x06008010, 0x40a083e0, 0x14e007be, 0x00c01ffc, 0x0000a081, 
++0x40a083e0, 0x20a083f0, 0x00a083c0, 0x60a0039c, 0x042007d0, 0x0840a680, 0xb3febf12, 0x190010a2, 
++0x8afebf10, 0xf42006d0, 0x60a003de, 0x80a083e0, 0xa0a083f0, 0x08e0c381, 0x00a1039c, 0x80a0239c, 
++0x042002c4, 0x004022c4, 0x18008030, 0x00007081, 0x16008012, 0x00000001, 0x60a0239c, 0x00a0a3c0, 
++0x20a0a3f0, 0x40a0a3e0, 0x02c03f3f, 0x24e117be, 0x04e08f80, 0x06008012, 0x00000001, 0x00c01ffc, 
++0x0000a081, 0x06008010, 0x40a083e0, 0x14e007be, 0x00c01ffc, 0x0000a081, 0x40a083e0, 0x20a083f0, 
++0x00a083c0, 0x60a0039c, 0x000002c4, 0x00a0a080, 0xe7ffbf12, 0x00000001, 0x042002c4, 0x01a00084, 
++0x042022c4, 0x000002c4, 0x00a0a080, 0xddffbf12, 0x00000001, 0x08e0c381, 0x80a0039c, };
++#define threadcode_elan3_text_size 0x97c
++static uint32_t threadcode_elan3_data[] = {
++0};
++#define threadcode_elan3_data_size 0x0
++static uint32_t threadcode_elan3_rodata[] = {
++0};
++#define threadcode_elan3_rodata_size 0x0
++static EP_SYMBOL threadcode_elan3_symbols[] = {
++    {"__bss_start", 0xff00297c},
++    {"_edata", 0xff00297c},
++    {"_end", 0xff002988},
++    {"_etext", 0xff00097c},
++    {"_sdata", 0xff00297c},
++    {"_stext", 0xff000000},
++    {"ep3_spinblock", 0xff0008dc},
++    {"ep3comms_rcvr", 0xff0002a8},
++    {"kcomm_probe", 0xff00013c},
++    {"r", 0xff00297c},
++    {"rail", 0xff002984},
++    {"rm", 0xff002980},
++    {0, 0}};
++EP_CODE threadcode_elan3 = {
++   (unsigned char *) threadcode_elan3_text,
++   threadcode_elan3_text_size,
++   (unsigned char *) threadcode_elan3_data,
++   threadcode_elan3_data_size,
++   (unsigned char *) threadcode_elan3_rodata,
++   threadcode_elan3_rodata_size,
++   threadcode_elan3_symbols,
++};
+Index: linux-2.4.21/drivers/net/qsnet/ep/threadcode_elan4_Linux.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/ep/threadcode_elan4_Linux.c    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/ep/threadcode_elan4_Linux.c 2005-06-01 23:12:54.691425120 -0400
+@@ -0,0 +1,112 @@
++/* --------------------------------------------------------*/
++/* MACHINE GENERATED ELAN CODE                             */
++#include <qsnet/kernel.h>
++#include <elan/kcomm.h>
++#include "kcomm_elan4.h"
++static uint32_t threadcode_elan4_text[] = {
++0x00a00087, 0xc04060cb, 0x00003080, 0x80001080, 0x02606180, 0x02004032, 0x807f60cb, 0x04606180, 
++0x02004032, 0x407f60d3, 0x08606180, 0x02004032, 0x007f60db, 0x10606180, 0x02004032, 0xc07e60e3, 
++0x20606180, 0x02004032, 0x807e60eb, 0x40606180, 0x02004032, 0x407e60f3, 0x80606180, 0x02004032, 
++0x007e60fb, 0x40001180, 0xc3801080, 0xc07f60c3, 0x20002000, 0x20002000, 0x20002000, 0x20002000, 
++0x407f8001, 0x4060c0c7, 0x4860c0d0, 0x5060c0d1, 0x5860c0d2, 0x6060c0d3, 0x6860c0d4, 0x00208292, 
++0x00608291, 0x00a08294, 0xff3f8088, 0x1c381293, 0xc04044c8, 0x13004290, 0xc000c5d0, 0x08004030, 
++0x00001088, 0x04204288, 0x0020b200, 0x04004003, 0x00208080, 0x9c010040, 0x00a08488, 0xc04044c8, 
++0x20381288, 0x0020b200, 0xf6ff7f13, 0x01208408, 0x11161282, 0x804094c2, 0xc04044c8, 0x20381288, 
++0x0020b200, 0xebff7f13, 0x00208080, 0x406040c7, 0x486040d0, 0x506040d1, 0x586040d2, 0x606040d3, 
++0x686040d4, 0x08e00180, 0xc0608001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 
++0x807e8001, 0x4060c0c7, 0x4860c0d0, 0x5060c0d1, 0x5860c0d2, 0x6060c0d3, 0x6860c0d4, 0x7060c0d5, 
++0x7860c0d6, 0x8060c0d7, 0x8860c0d8, 0x9060c0d9, 0x9860c0da, 0xa060c0db, 0xa860c0dc, 0xb060c0dd, 
++0xb860c0de, 0xc060c0df, 0x8061c0c8, 0x00608296, 0x00a0829a, 0x9861c0cb, 0xa061c0cc, 0xa861c0cd, 
++0x01208088, 0x3861c0c8, 0x08e042d2, 0x386140c9, 0x0900900a, 0xa06140c8, 0x986140cb, 0x18e042c9, 
++0x72010040, 0x05b4128a, 0x0020808c, 0x3861c0cc, 0x986140c9, 0xc04042c8, 0x0880b400, 0x39014003, 
++0xffff3f08, 0x90a0851c, 0xe023829f, 0x20f4179f, 0x10e3879f, 0xffff3f08, 0xe023829e, 0x20b4179e, 
++0x03a3879e, 0xffff3f08, 0xe023829d, 0x2074179d, 0x0363879d, 0x00a08495, 0x18a08408, 0x800012c2, 
++0x089a109b, 0x20f4169b, 0x20f8169b, 0x00e88609, 0x20741289, 0x01120008, 0x0a381288, 0x08408297, 
++0x45208088, 0x06341288, 0x806140ca, 0xc88042c8, 0x00288218, 0x04a08408, 0x800012c2, 0x089a1088, 
++0x20341288, 0x20381288, 0x00281299, 0x20a08408, 0x800012c2, 0x089a108a, 0x20b4128a, 0x20b8128a, 
++0x30a08408, 0x800012c2, 0x089a1093, 0x20f41493, 0x20f81493, 0x03f41689, 0x806140cb, 0x2922808c, 
++0x0334138c, 0xccc042c8, 0xc90042d1, 0x02604688, 0x0020b200, 0x03004002, 0x60a08214, 0x80a08214, 
++0x90a08509, 0x804012c8, 0x01208208, 0x804092c8, 0x046012c8, 0x043a1288, 0x0020b200, 0x04004003, 
++0xa86140c8, 0x67ffff7f, 0x00a0868a, 0x88a045d0, 0x0020b400, 0x12004013, 0x00208080, 0x800017c8, 
++0x808096c8, 0x72010040, 0x00a08588, 0x00208290, 0x90a08509, 0x804012c8, 0x01208208, 0x804092c8, 
++0x046012c8, 0x043a1288, 0x0020b200, 0x04004003, 0xa86140c8, 0x53ffff7f, 0x00a0868a, 0x804015c2, 
++0x159a1089, 0x20741289, 0x20781289, 0x40b03608, 0x01208288, 0x0840b200, 0x06004023, 0xa02344c4, 
++0x800017c8, 0x808096c8, 0xbb004010, 0xa8a045c8, 0x01604688, 0x00281288, 0x08009008, 0x00e0b400, 
++0x05004003, 0x3f381289, 0x13408209, 0x03004010, 0x05208088, 0x04208088, 0x09009220, 0x07341889, 
++0x0900840b, 0x05341888, 0x0023820a, 0x01604688, 0x0020b200, 0x1d004002, 0x0a00840c, 0xc900c4d7, 
++0x40c40f08, 0x09208288, 0x08e0c2c8, 0x0a608488, 0x10e0c2c8, 0x81001008, 0x0a341288, 0x18e0c2c8, 
++0x1d608488, 0x20e0c2c8, 0x28e0c2d8, 0x24608508, 0x800012c2, 0x089a1088, 0x20341288, 0x20381288, 
++0x80208208, 0x30e0c2c8, 0x00218108, 0x38e0c2c8, 0x40e0c2d4, 0x48e0c2cc, 0xca00c4df, 0x20608411, 
++0x80e0820b, 0x2020830c, 0x00e0b400, 0x13004013, 0x0020808e, 0xc0c0c2d7, 0x40c40f09, 0x09608289, 
++0x08e0c2c9, 0x0a608488, 0x10e0c2c8, 0x00040008, 0x18e0c2c8, 0x1d608488, 0x20e0c2c8, 0x28e0c2d8, 
++0x40e0c2d4, 0x48e0c2cc, 0xc000c3de, 0x00208083, 0x4c004010, 0x20608411, 0xb8238408, 0x800012c2, 
++0x089a108f, 0x20f4138f, 0x20f8138f, 0x00208083, 0x13c0b000, 0x2e00401b, 0x40c40f08, 0x092082a2, 
++0x00040021, 0xffff3f08, 0xe023828d, 0x2074138d, 0x1063838d, 0x0e808309, 0x0e408209, 0x02741289, 
++0x1540820a, 0x38a0820a, 0x808012c2, 0x0a9a108a, 0x20b4128a, 0x20b8128a, 0xc0c0c2d7, 0x08e0c2e2, 
++0x0a608488, 0x10e0c2c8, 0x20b41288, 0x21008288, 0x18e0c2c8, 0x1d608488, 0x20e0c2c8, 0x28e0c2d8, 
++0x15408209, 0x34608209, 0x804012c2, 0x099a1089, 0x20741289, 0x20781289, 0x30e0c2c9, 0x38e0c2cf, 
++0x40e0c2d4, 0x48e0c2cc, 0xc000c3cd, 0x0ac0830f, 0x0ac08003, 0x20608411, 0x80e0820b, 0x01a0830e, 
++0x1380b300, 0xdcff7f0b, 0x2020830c, 0xe03f830c, 0xc000c3dd, 0xbc238408, 0x800012c2, 0x089a1088, 
++0x20341288, 0x20381288, 0x0300b200, 0x0d00401b, 0x07341888, 0x0020888e, 0x0420b800, 0x08004019, 
++0x0800840b, 0x00040008, 0x18e0c2c8, 0x01a0830e, 0x04a0b300, 0xfdff7f09, 0x80e0820b, 0xfc3f8083, 
++0x07341888, 0x08008408, 0xa06140ca, 0xc00062e3, 0x402062f3, 0xc080e2e3, 0xc080e2f3, 0x982244c8, 
++0x88a0c5c8, 0x88a045c8, 0x0020b200, 0x05004013, 0x04604688, 0x88a08508, 0x80a0c5c8, 0x04604688, 
++0x0020b200, 0x0c004002, 0xd822c4c0, 0xc04065e3, 0x406065f3, 0xc000e1e3, 0x806065e3, 0x4020e1f3, 
++0xc06065f3, 0x8020e1e3, 0xc020e1f3, 0x07004010, 0x88228108, 0xc04065e3, 0x406065f3, 0xc000e1e3, 
++0x4020e1f3, 0x88228108, 0x08d61082, 0x800092c2, 0x03f41689, 0x806140cb, 0x2922808c, 0x0334138c, 
++0xccc042c8, 0xc900c2d1, 0x800017c8, 0x808096c8, 0xa8a045c8, 0x0880b400, 0x03004013, 0x00a18412, 
++0xa0a045d2, 0x98a045c8, 0x0020b200, 0x05004013, 0x386140c9, 0x986140c8, 0x0820c2d2, 0x386140c9, 
++0x01608209, 0xfe61b200, 0x0e004015, 0x3861c0c9, 0x00001088, 0x02204288, 0x0020b200, 0x05004003, 
++0x986140ca, 0x28000040, 0xa06140c8, 0x986140ca, 0xc08042c8, 0x0880b400, 0xd8fe7f13, 0x00a08495, 
++0x98a045cb, 0x00e0b200, 0xbafe7f03, 0x386140c9, 0xa06140c8, 0x60a08509, 0x48000040, 0xe03f808a, 
++0x986140cb, 0x08e0c2d2, 0x386140cc, 0x0120830c, 0xaffe7f10, 0x3861c0cc, 0x406040c7, 0x486040d0, 
++0x506040d1, 0x586040d2, 0x606040d3, 0x686040d4, 0x706040d5, 0x786040d6, 0x806040d7, 0x886040d8, 
++0x906040d9, 0x986040da, 0xa06040db, 0xa86040dc, 0xb06040dd, 0xb86040de, 0xc06040df, 0x08e00180, 
++0x80618001, 0x807f8001, 0xc040e0d3, 0x4060e0db, 0x00208490, 0x00208698, 0x00208080, 0x00208080, 
++0x00e08192, 0x02000040, 0x00608091, 0x14e08110, 0x17208097, 0xc000f2d3, 0xc04060d3, 0x406060db, 
++0x08a00080, 0x80608001, 0x407f8001, 0x4060e0d3, 0x8060e0db, 0x00208490, 0x00208698, 0x00208080, 
++0x00208080, 0x00e08192, 0x02000040, 0x00608091, 0x40e08110, 0xc040e0d1, 0x37208097, 0x3860c0d7, 
++0x00208490, 0x00e08597, 0x00208080, 0x00208080, 0x1f608290, 0x20b41291, 0x08638491, 0x00608092, 
++0x00208293, 0xc000f2d1, 0x406060d3, 0x806060db, 0x08a00080, 0xc0608001, 0x407f8001, 0x4060e0d3, 
++0x8060e0db, 0x00208490, 0x00208698, 0x00208080, 0x00208080, 0x00e08192, 0x02000040, 0x00608091, 
++0x54e08110, 0xc040e0d1, 0x37208097, 0x3860c0d7, 0x00208490, 0x00e08597, 0x00208080, 0x00208080, 
++0x1f608290, 0x20b41291, 0x08638491, 0x00608092, 0x00208293, 0x0ef41294, 0x0d208594, 0x17208095, 
++0x17208096, 0x17208097, 0xc000f2d3, 0x406060d3, 0x806060db, 0x08a00080, 0xc0608001, 0x01208097, 
++0xb0e3c0d7, 0x80a060d2, 0x98e28004, 0x98e2c0c0, 0x80a0c0c4, 0xc080c4c3, 0x01e0b400, 0x06004002, 
++0x00a08490, 0x00e08097, 0x02208097, 0xb0e3c0d7, 0xd8e2d0d0, 0xd8e2c0d0, 0x03208097, 0xb0e3c0d7, 
++0x00e08088, 0x0e004010, 0x00a060c3, 0x407f8001, 0x4060e0d3, 0x8060e0db, 0x00208490, 0x00208698, 
++0x00208080, 0x00208080, 0x01208089, 0x8820c2c9, 0x00608091, 0x00e08197, 0x0020f2d3, 0x406060d3, 
++0x806060db, 0x08e00180, 0xc0608001, };
++#define threadcode_elan4_text_size 0x90c
++static uint32_t threadcode_elan4_data[] = {
++0};
++#define threadcode_elan4_data_size 0x0
++static uint32_t threadcode_elan4_rodata[] = {
++0};
++#define threadcode_elan4_rodata_size 0x0
++static EP_SYMBOL threadcode_elan4_symbols[] = {
++    {".thread_restart", 0x00000000f800000c},
++    {".thread_start", 0x00000000f8000000},
++    {"__bss_start", 0x00000000f810090c},
++    {"_edata", 0x00000000f810090c},
++    {"_end", 0x00000000f8100910},
++    {"_etext", 0x00000000f800090c},
++    {"_sdata", 0x00000000f810090c},
++    {"_stext", 0x00000000f8000000},
++    {"c_queue_rxd", 0x00000000f800087c},
++    {"c_reschedule", 0x00000000f8000744},
++    {"c_stall_thread", 0x00000000f80008cc},
++    {"c_waitevent", 0x00000000f8000788},
++    {"c_waitevent_interrupt", 0x00000000f80007f8},
++    {"ep4_spinblock", 0x00000000f8000080},
++    {"ep4comms_rcvr", 0x00000000f8000140},
++    {0, 0}};
++EP_CODE threadcode_elan4 = {
++   (unsigned char *) threadcode_elan4_text,
++   threadcode_elan4_text_size,
++   (unsigned char *) threadcode_elan4_data,
++   threadcode_elan4_data_size,
++   (unsigned char *) threadcode_elan4_rodata,
++   threadcode_elan4_rodata_size,
++   threadcode_elan4_symbols,
++};
+Index: linux-2.4.21/drivers/net/qsnet/jtag/jtagdrv.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/jtag/jtagdrv.c 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/jtag/jtagdrv.c      2005-06-01 23:12:54.692424968 -0400
+@@ -0,0 +1,451 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: jtagdrv.c,v 1.12 2003/06/07 16:02:35 david Exp $"
++/*      $Source: /cvs/master/quadrics/jtagmod/jtagdrv.c,v $*/
++
++#include <qsnet/types.h>
++
++#include "jtagdrv.h"
++#include <jtag/jtagio.h>
++
++int
++jtagdrv_strobe_data (JTAG_DEV *dev, u_char data)
++{
++    u_char dsr;
++
++    PRINTF (DBG_ECPP, ("jtagdrv_strobe_data: %s %s %s -> ", (data & LPT_DATA_TRST) ? "TRST" : "trst", 
++                     (data & LPT_DATA_TDI) ? "TDI" : "tdi", (data & LPT_DATA_TMS) ? "TMS" : "tms"));
++
++
++    LPT_WRITE_DATA (dev, data); DELAY(5);                     /* Drive NEW values on data wires */
++    LPT_WRITE_CTRL (dev, LPT_CTRL_TCLK); DELAY(5);            /* Drive strobe low */
++    LPT_READ_STAT  (dev, dsr); DELAY(5);                      /* Sample TDI from ring */
++    LPT_WRITE_CTRL (dev, 0); DELAY(5);                                /* Drive strobe high */
++
++    PRINTF (DBG_ECPP, ("%s\n", (dsr & LPT_STAT_PE) ? "TDO" : "tdo"));
++
++    return ((dsr & LPT_STAT_PE) ? 1 : 0);
++}
++
++void
++jtagdrv_select_ring (JTAG_DEV *dev, u_int ring)
++{
++    PRINTF (DBG_ECPP, ("jtagdrv_select_ring: ring=0x%x\n", ring));
++
++    LPT_WRITE_CTRL (dev, 0); DELAY(5);                                /* Drive strobe and TCLK high */
++    LPT_WRITE_DATA (dev, ring);       DELAY(5);                       /* Drive ring address */
++    LPT_WRITE_CTRL (dev, LPT_CTRL_RCLK); DELAY(5);            /* Drive strobe low */
++    LPT_WRITE_CTRL (dev, 0); DELAY(5);                                /* Drive strobe high */
++}
++
++void
++jtagdrv_reset (JTAG_DEV *dev)
++{
++    register int i;
++
++    for (i = 0; i < 5; i++)
++      jtagdrv_strobe_data (dev, LPT_DATA_TRST | LPT_DATA_TMS);                /* 5 clocks to Reset from any state */
++    jtagdrv_strobe_data (dev, LPT_DATA_TRST);                                 /* to Run-Test/Idle */
++}
++
++void
++jtagdrv_shift_ir (JTAG_DEV *dev, u_char *value, int nbits)
++{
++    register int i;
++    register int bit;
++
++    jtagdrv_strobe_data (dev, LPT_DATA_TRST | LPT_DATA_TMS);                  /* to Select DR-Scan */
++    jtagdrv_strobe_data (dev, LPT_DATA_TRST | LPT_DATA_TMS);                  /* to Select IR-Scan */
++    jtagdrv_strobe_data (dev, LPT_DATA_TRST);                                 /* to Capture-IR */
++    jtagdrv_strobe_data (dev, LPT_DATA_TRST);                                 /* to Shift-IR */
++    
++    for (i = 0; i < nbits; i++)
++    {
++      /* strobe through the instruction bits,  asserting TMS on the last bit */
++
++      if (i == (nbits-1))
++          bit = jtagdrv_strobe_data (dev, LPT_DATA_TRST | LPT_DATA_TMS | (JTAG_BIT(value, i) ? LPT_DATA_TDI : 0));
++      else
++          bit = jtagdrv_strobe_data (dev, LPT_DATA_TRST | (JTAG_BIT(value, i) ? LPT_DATA_TDI : 0));
++      
++      if (bit)
++          JTAG_SET_BIT(value, i);
++      else
++          JTAG_CLR_BIT(value, i);
++    }
++    
++    jtagdrv_strobe_data (dev, LPT_DATA_TRST | LPT_DATA_TMS);                  /* to Update-IR */
++    jtagdrv_strobe_data (dev, LPT_DATA_TRST);                                 /* to Run-Test/Idle */
++}
++
++
++void
++jtagdrv_shift_dr (JTAG_DEV *dev, u_char *value, int nbits)
++{
++    register int i;
++    register int bit;
++
++    jtagdrv_strobe_data (dev, LPT_DATA_TRST | LPT_DATA_TMS);                  /* to Select DR-Scan */
++    jtagdrv_strobe_data (dev, LPT_DATA_TRST);                                 /* to Capture-DR */
++    jtagdrv_strobe_data (dev, LPT_DATA_TRST);                                 /* to Shift-DR */
++    
++    for (i = 0; i < nbits; i++)
++    {
++      /* strobe through the data bits,  asserting TMS on the last bit */
++
++      if (i == (nbits-1))
++          bit = jtagdrv_strobe_data (dev, LPT_DATA_TRST | LPT_DATA_TMS | (JTAG_BIT(value, i) ? LPT_DATA_TDI : 0));
++      else
++          bit = jtagdrv_strobe_data (dev, LPT_DATA_TRST | (JTAG_BIT(value, i) ? LPT_DATA_TDI : 0));
++      
++      if (bit)
++          JTAG_SET_BIT(value, i);
++      else
++          JTAG_CLR_BIT(value, i);
++    }
++    
++    jtagdrv_strobe_data (dev, LPT_DATA_TRST | LPT_DATA_TMS);                  /* to Update-DR */
++    jtagdrv_strobe_data (dev, LPT_DATA_TRST);                                 /* to Run-Test/Idle */
++}
++
++static int
++jtagdrv_i2c_start (JTAG_DEV *dev)
++{
++    u_char dsr;
++    int i;
++
++    PRINTF (DBG_ECPP, ("jtagdrv_i2c_start\n"));
++    
++    /* Issue a stop sequence */
++    LPT_WRITE_CTRL (dev,  LPT_CTRL_SCLK); DELAY(1);           /* SCLK low */
++    LPT_WRITE_DATA (dev, 0); DELAY(5);                                /* SDA low */
++    LPT_WRITE_CTRL (dev, 0); DELAY(5);                                /* SCLK high */
++    LPT_WRITE_DATA (dev, LPT_DATA_SDA); DELAY(5);             /* SDA high */
++    
++    /* sample the line to see if we're idle */
++    LPT_READ_STAT (dev, dsr);                                 /* sample SDA */
++    if ((dsr & LPT_STAT_SDA) == 0)                            /* Cannot start if SDA already driven */
++    {
++      PRINTF (DBG_ECPP, ("jtagdrv_i2c_start: cannot start - sda driven low\n"));
++
++      for (i = 0; i < 16 ; i++)
++      {
++          LPT_WRITE_CTRL (dev, LPT_CTRL_SCLK); DELAY(5);      /* SCLK low */
++          LPT_WRITE_CTRL (dev, 0); DELAY(5);                  /* SCLK high */
++          LPT_READ_STAT  (dev, dsr);
++          
++          if (dsr & LPT_STAT_SDA)
++          {
++              PRINTF (DBG_ECPP, ("jtagdrv_i2c_start - stopped after %d clocks\n", i));
++              break;
++          }
++      }
++
++      if ((dsr & LPT_STAT_SDA) == 0)
++      {
++          PRINTF (DBG_ECPP, ("jtagdrv_i2c_start - cannot start - not idle\n"));
++          return (0);
++      }
++
++      /* seen SDA float high, so issue a stop sequence */
++      LPT_WRITE_CTRL (dev, LPT_CTRL_SCLK); DELAY(1);          /* SCLK low */
++      LPT_WRITE_DATA (dev, 0); DELAY(5);                      /* SDA low */
++      LPT_WRITE_CTRL (dev, 0); DELAY(5);                      /* SCLK high */
++      LPT_WRITE_DATA (dev, LPT_DATA_SDA); DELAY(5);           /* SDA high */
++    }
++
++    LPT_WRITE_DATA (dev, 0); DELAY(4);                                /* drive SDA low */
++    return (1);
++}
++
++static void
++jtagdrv_i2c_stop (JTAG_DEV *dev)
++{
++    u_char dsr;
++    int    i;
++
++    PRINTF (DBG_ECPP, ("jtagdrv_i2c_stop\n"));
++
++    LPT_WRITE_CTRL (dev, LPT_CTRL_SCLK); DELAY(1);            /* SCLK low */
++    LPT_WRITE_DATA (dev, 0); DELAY(5);                                /* SDA low */
++    LPT_WRITE_CTRL (dev, 0); DELAY(5);                                /* SCLK high */
++    LPT_WRITE_DATA (dev, LPT_DATA_SDA); DELAY(5);             /* SDA high */
++
++    /* 
++     * bug fix for temperature sensor chip
++     * if it's still driving SDA, then clock
++     * it until it stops driving it 
++     */
++    LPT_READ_STAT (dev, dsr);
++    if ((dsr & LPT_STAT_SDA) == 0)
++    {
++      PRINTF (DBG_ECPP, ("jtagdrv_i2c_stop - slave not stodeved\n"));
++      for (i = 0; i < 16 ; i++)
++      {
++          LPT_WRITE_CTRL (dev, LPT_CTRL_SCLK); DELAY(5);      /* SCLK low */
++          LPT_WRITE_CTRL (dev, 0); DELAY(5);                  /* SCLK high */
++          LPT_READ_STAT  (dev, dsr);
++          
++          if (dsr & LPT_STAT_SDA)
++              break;
++      }
++      PRINTF (DBG_ECPP, ("jtagdrv_i2c_stop - stodeved after %d clocks\n", i));
++    }
++}
++
++static int
++jtagdrv_i2c_strobe (JTAG_DEV *dev, u_char data)
++{
++    u_char dsr;
++    
++    PRINTF (DBG_ECPP, ("jtagdrv_i2c_strobe : %s", (data & LPT_DATA_SDA) ? "SDA" : "sda"));
++
++    LPT_WRITE_CTRL (dev, LPT_CTRL_SCLK); DELAY(1);            /* SCLK low */
++    LPT_WRITE_DATA (dev, data);       DELAY(5);                       /* write data */
++    LPT_WRITE_CTRL (dev, 0);                                  /* SCLK high */
++    LPT_READ_STAT  (dev, dsr); DELAY(4);                      /* Sample SDA */
++
++    PRINTF (DBG_ECPP, (" -> %s\n", (dsr & LPT_STAT_SDA) ? "SDA" : "sda"));
++
++    return ((dsr & LPT_STAT_SDA) ? 1 : 0);
++}
++
++static int
++jtagdrv_i2c_get_ack (JTAG_DEV *dev)
++{
++    u_char dsr;
++
++    LPT_WRITE_CTRL (dev, LPT_CTRL_SCLK); DELAY(1);            /* SCLK low */
++    LPT_WRITE_DATA (dev, LPT_DATA_SDA);       DELAY(5);               /* SDA high */
++    LPT_WRITE_CTRL (dev, 0);                                  /* SCLK high */
++    LPT_READ_STAT  (dev, dsr); DELAY(4);                      /* Sample SDA */
++
++    PRINTF (DBG_ECPP, ("jtagdrv_i2c_get_ack -> %s\n", (dsr & LPT_STAT_SDA) ? "no ack" : "ack"));
++    
++    return ((dsr & LPT_STAT_SDA) ? 0 : 1);
++}
++
++static int
++jtagdrv_i2c_drive_ack (JTAG_DEV *dev, int nack)
++{
++    u_char dsr;
++
++    LPT_WRITE_CTRL (dev, LPT_CTRL_SCLK); DELAY(1);            /* SCLK low */
++    LPT_WRITE_DATA (dev, nack ? LPT_DATA_SDA : 0); DELAY(5);  /* SDA low for ack, high for nack */
++    LPT_WRITE_CTRL (dev, 0);                                  /* SCLK high */
++    LPT_READ_STAT  (dev, dsr); DELAY(4);                      /* Sample SDA for ack */
++
++    PRINTF (DBG_ECPP, ("jtagdrv_i2c_drive_ack %d -> %s\n", nack, (dsr & LPT_STAT_SDA) ? "done" : "more"));
++    
++    return ((dsr & LPT_STAT_SDA) ? 1 : 0);
++}
++
++static void
++jtagdrv_i2c_shift_addr (JTAG_DEV *dev, u_int address, int readNotWrite)
++{
++    register int i;
++
++    PRINTF (DBG_ECPP, ("jtagdrv_i2c_shift_addr: %x\n", address));
++
++    for (i = I2C_ADDR_LEN-1; i >= 0; i--)
++      jtagdrv_i2c_strobe (dev, (address & (1 << i)) ? LPT_DATA_SDA : 0);
++    
++    jtagdrv_i2c_strobe (dev, readNotWrite ? LPT_DATA_SDA : 0);
++}
++
++static u_char
++jtagdrv_i2c_shift_data (JTAG_DEV *dev, u_char data)
++{
++    register int i;
++    u_char val = 0;
++    
++    PRINTF (DBG_ECPP, ("jtagdrv_i2c_shift_data : %02x\n", data));
++
++    for (i = I2C_DATA_LEN-1; i >= 0; i--)
++      if (jtagdrv_i2c_strobe (dev, data & (1 << i) ? LPT_DATA_SDA : 0))
++          val |= (1 << i);
++
++    PRINTF (DBG_ECPP, ("jtagdrv_i2c_shift_data : -> %02x\n", val));
++
++    return (val);
++}
++
++int
++jtagdrv_i2c_write (JTAG_DEV *dev, u_int address, u_int count, u_char *data)
++{
++    register int i;
++
++    PRINTF (DBG_FN, ("jtagdrv_i2c_write: address=%x count=%d data=%02x\n", address, count, data[0]));
++
++    if (! jtagdrv_i2c_start (dev))
++      return (I2C_OP_NOT_IDLE);
++
++    jtagdrv_i2c_shift_addr (dev, address, 0);
++    
++    if (! jtagdrv_i2c_get_ack (dev))
++    {
++      PRINTF (DBG_FN, ("jtagdrv_i2c_write: no ack on address phase\n"));
++
++      jtagdrv_i2c_stop (dev);
++      return (I2C_OP_NO_DEVICE);
++    }
++    
++    for (i = 0; i < count; i++)
++    {
++      jtagdrv_i2c_shift_data (dev, data[i]);
++      
++      if (! jtagdrv_i2c_get_ack (dev))
++      {
++          PRINTF (DBG_FN, ("jtagdrv_i2c_write: no ack on data phase %d\n", i));
++
++          jtagdrv_i2c_stop (dev);
++          return (I2C_OP_WRITE_TO_BIG);
++      }
++    }
++
++    jtagdrv_i2c_stop (dev);
++    return (I2C_OP_SUCCESS);
++}
++
++int
++jtagdrv_i2c_read (JTAG_DEV *dev, u_int address, u_int count, u_char *data)
++{
++    register int i;
++
++    PRINTF (DBG_FN, ("jtagdrv_i2c_read: address=%x count=%d\n", address, count));
++
++    if (! jtagdrv_i2c_start (dev))
++      return (I2C_OP_NOT_IDLE);
++
++    jtagdrv_i2c_shift_addr (dev, address, 1);
++    
++    if (! jtagdrv_i2c_get_ack (dev))
++    {
++      PRINTF (DBG_FN, ("jtagdrv_i2c_read: no ack on address phase\n"));
++
++      jtagdrv_i2c_stop (dev);
++      return (I2C_OP_NO_DEVICE);
++    }
++    
++    for (i = 0; i < count; i++)
++    {
++      data[i] = jtagdrv_i2c_shift_data (dev, 0xff);
++
++      jtagdrv_i2c_drive_ack (dev, (i == (count-1) ? 1 : 0));
++    }
++
++    jtagdrv_i2c_stop (dev);
++    
++    return (I2C_OP_SUCCESS);
++}
++
++int
++jtagdrv_i2c_writereg (JTAG_DEV *dev, u_int address, u_int intaddress, u_int count, u_char *data)
++{
++    register int i;
++
++    PRINTF (DBG_FN, ("jtagdrv_i2c_writereg: address=%x count=%d\n", address, count));
++
++    if (! jtagdrv_i2c_start (dev))
++      return (I2C_OP_NOT_IDLE);
++
++    jtagdrv_i2c_shift_addr (dev, address, 0);
++    
++    if (! jtagdrv_i2c_get_ack (dev))
++    {
++      PRINTF (DBG_FN, ("jtagdrv_i2c_writereg: no ack on address phase\n"));
++
++      jtagdrv_i2c_stop (dev);
++      return (I2C_OP_NO_DEVICE);
++    }
++    
++    jtagdrv_i2c_shift_data (dev, intaddress);
++    
++    if (! jtagdrv_i2c_get_ack (dev))
++    {
++      PRINTF (DBG_FN, ("jtagdrv_i2c_writereg: no ack on intaddress phase\n"));
++      jtagdrv_i2c_stop (dev);
++      return (I2C_OP_NO_DEVICE);
++    }
++    
++    for (i = 0; i < count; i++)
++    {
++      jtagdrv_i2c_shift_data (dev, data[i]);
++      if (! jtagdrv_i2c_get_ack (dev))
++      {
++          PRINTF (DBG_FN, ("jtagdrv_i2c_writedate: no ack on byte %d\n", i));
++          jtagdrv_i2c_stop (dev);
++          return (I2C_OP_WRITE_TO_BIG);
++      }
++    }
++    
++    jtagdrv_i2c_stop (dev);
++    return (I2C_OP_SUCCESS);
++}
++
++int
++jtagdrv_i2c_readreg (JTAG_DEV *dev, u_int address, u_int intaddress, u_int count, u_char *data)
++{
++    PRINTF (DBG_FN, ("jtagdrv_i2c_readreg: address=%x count=%d\n", address, count));
++
++    if (! jtagdrv_i2c_start (dev))
++      return (I2C_OP_NOT_IDLE);
++
++    jtagdrv_i2c_shift_addr (dev, address, 0);
++    
++    if (! jtagdrv_i2c_get_ack (dev))
++    {
++      PRINTF (DBG_FN, ("jtagdrv_i2c_readreg: no ack on address phase\n"));
++
++      jtagdrv_i2c_stop (dev);
++      return (I2C_OP_NO_DEVICE);
++    }
++    
++    jtagdrv_i2c_shift_data (dev, intaddress);
++    
++    if (! jtagdrv_i2c_get_ack (dev))
++    {
++      PRINTF (DBG_FN, ("jtagdrv_i2c_readreg: no ack on intaddress phase\n"));
++      jtagdrv_i2c_stop (dev);
++      return (I2C_OP_NO_DEVICE);
++    }
++
++    jtagdrv_i2c_stop (dev);
++
++    return (jtagdrv_i2c_read (dev, address, count, data));
++}
++
++void
++jtagdrv_i2c_clock_shift (JTAG_DEV *dev, u_int t, u_int n, u_int m)
++{
++    int i;
++
++    for (i = 2; i >= 0; i--)
++    {
++      LPT_WRITE_DATA (dev, ((t & (1 << i)) ? LPT_DATA_TDI : 0)); DELAY(1);                    /* clock low  | data */
++      LPT_WRITE_DATA (dev, ((t & (1 << i)) ? LPT_DATA_TDI : 0) | LPT_DATA_TMS); DELAY(1);     /* clock high | data */
++    }
++
++    for (i = 1; i >= 0; i--)
++    {
++      LPT_WRITE_DATA (dev, ((n & (1 << i)) ? LPT_DATA_TDI : 0)); DELAY(1);                    /* clock low  | data */
++      LPT_WRITE_DATA (dev, ((n & (1 << i)) ? LPT_DATA_TDI : 0)| LPT_DATA_TMS); DELAY(1);      /* clock high | data */
++    }    
++
++    for (i = 6; i >= 0; i--)
++    {
++      LPT_WRITE_DATA (dev, ((m & (1 << i)) ? LPT_DATA_TDI : 0)); DELAY(1);                    /* clock low  | data */
++      LPT_WRITE_DATA (dev, ((m & (1 << i)) ? LPT_DATA_TDI : 0) | LPT_DATA_TMS); DELAY(1);     /* clock high | data */
++    }    
++
++    LPT_WRITE_DATA (dev, 0); DELAY(1);                                                                /* clock low  | 0 */
++
++    LPT_WRITE_CTRL (dev, LPT_CTRL_TCLK); DELAY(1);                                            /* strobe low */
++    LPT_WRITE_CTRL (dev, 0); DELAY(1);                                                                /* strobe low */
++}
++
+Index: linux-2.4.21/drivers/net/qsnet/jtag/jtagdrv.h
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/jtag/jtagdrv.h 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/jtag/jtagdrv.h      2005-06-01 23:12:54.692424968 -0400
+@@ -0,0 +1,57 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __JTAGDRV_COMMON_H
++#define __JTAGDRV_COMMON_H
++
++#ident "@(#)$Id: jtagdrv.h,v 1.5 2002/08/09 11:18:37 addy Exp $"
++/*      $Source: /cvs/master/quadrics/jtagmod/jtagdrv.h,v $*/
++
++#include <qsnet/config.h>
++
++/* include OS specific header file */
++#if defined(LINUX)
++#  include "jtagdrv_Linux.h"
++#elif defined(DIGITAL_UNIX)
++#  include "jtagdrv_OSF1.h"
++#elif defined(QNX)
++#  include "jtagdrv_QNX.h"
++#else
++#  error cannot determint os type
++#endif
++
++extern int jtagdebug;
++
++#define DBG_CFG               (1 << 0)
++#define DBG_OPEN      (1 << 1)
++#define DBG_IOCTL     (1 << 2)
++#define DBG_ECPP      (1 << 3)
++#define DBG_FN                (1 << 4)
++
++#define DRIVER_NAME    "jtag"
++
++#if defined(LINUX)
++#define PRINTF(n,X)   ((n) & jtagdebug ? (void) printk X : (void) 0)
++#define PRINTMSG(fmt, arg...) printk(KERN_INFO DRIVER_NAME ": " fmt, ##arg)
++#else
++#define PRINTF(n,X)   ((n) & jtagdebug ? (void) printf X : (void) 0)
++#define PRINTMSG(M, A)        printf ("jtag: " M, A)
++#endif
++
++extern void jtagdrv_select_ring (JTAG_DEV *pp, u_int ring);
++extern void jtagdrv_reset (JTAG_DEV *pp);
++extern void jtagdrv_shift_ir (JTAG_DEV *pp, u_char *value, int nbits);
++extern void jtagdrv_shift_dr (JTAG_DEV *pp, u_char *value, int nbits);
++
++extern int  jtagdrv_i2c_write (JTAG_DEV *pp, u_int address, u_int count, u_char *data);
++extern int  jtagdrv_i2c_read (JTAG_DEV *pp, u_int address, u_int count, u_char *data);
++extern int  jtagdrv_i2c_writereg (JTAG_DEV *pp, u_int address, u_int intaddress, u_int count, u_char *data);
++extern int  jtagdrv_i2c_readreg (JTAG_DEV *pp, u_int address, u_int intaddress, u_int count, u_char *data);
++extern void jtagdrv_i2c_clock_shift (JTAG_DEV *pp, u_int t, u_int n, u_int m);
++
++
++#endif /* __JTAGDRV_COMMON_H */
+Index: linux-2.4.21/drivers/net/qsnet/jtag/jtagdrv_Linux.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/jtag/jtagdrv_Linux.c   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/jtag/jtagdrv_Linux.c        2005-06-01 23:12:54.693424816 -0400
+@@ -0,0 +1,319 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++/*
++ * $Id: jtagdrv_Linux.c,v 1.18 2004/01/06 11:15:46 fabien Exp $
++ * $Source: /cvs/master/quadrics/jtagmod/jtagdrv_Linux.c,v $
++ */
++
++#include "jtagdrv.h"
++#include <jtag/jtagio.h>
++
++#include <linux/module.h>
++#include <linux/ioport.h>
++
++MODULE_AUTHOR("Quadrics Ltd.");
++MODULE_DESCRIPTION("JTAG Parallel port QsNet switch interface");
++
++MODULE_LICENSE("GPL");
++
++#define MAJOR_INSTANCE        0       /* 0 is dynamic assign of device major  */ 
++#define MAX_JTAG_DEV  4
++
++int jtag_major = MAJOR_INSTANCE;
++int jtagdebug  = 0;
++MODULE_PARM(jtag_major, "i");
++MODULE_PARM(jtagdebug, "i");
++
++JTAG_DEV      jtag_devs[MAX_JTAG_DEV];
++
++int io[MAX_JTAG_DEV]= { 0, };
++MODULE_PARM(io, "1-4i");
++
++
++/* The fops functions */
++int jtag_open(struct inode *, struct file *);
++int jtag_close(struct inode *, struct file *);
++int jtag_ioctl(struct inode *, struct file *, unsigned int, unsigned long );
++
++struct file_operations jtag_fops = {
++    ioctl:   jtag_ioctl,
++    open:    jtag_open,
++    release: jtag_close,
++};
++
++int
++jtag_probe(void)
++{
++      int i=0;        
++      int default_io = 1;
++      JTAG_DEV *dev;
++      unsigned char value=0xff;
++
++      /* see if there are any user supplied io addr */
++      for ( i = 0; i < MAX_JTAG_DEV; i++) {
++              if ( io[i] != 0x00)
++                      default_io = 0;
++              jtag_devs[i].base = io[i];
++      }
++      
++      if ( default_io ) {
++              jtag_devs[0].base = 0x3bc;
++              jtag_devs[1].base = 0x378;
++              jtag_devs[2].base = 0x278;
++              jtag_devs[3].base = 0x268;
++      }
++
++      for ( i = 0 ; i < MAX_JTAG_DEV; i++) {
++              if ( jtag_devs[i].base == 0x3bc ) 
++                      jtag_devs[i].region = 3;
++              else
++                      jtag_devs[i].region = 8;
++              jtag_devs[i].present = 0;
++      }       
++
++
++      if( default_io )
++      {
++              for( i = 0 ; i < MAX_JTAG_DEV; i++) {
++                      dev=&(jtag_devs[i]);
++                      if(dev->base && request_region(dev->base, dev->region, "jtag")) {
++                              LPT_WRITE(dev, 0,0);
++                              LPT_READ(dev, 0,value);
++                              if ( value != 0xff) {
++                                      PRINTMSG("(%d , %d) present, io=0x%04lx\n",jtag_major,i,dev->base);
++                      
++                                      dev->present=1; 
++                              }
++                              else
++                                      release_region(dev->base, dev->region);
++                      }
++              }
++              return 0;
++       }     
++       else /* Force the region to be present, this makes the PCI parallel cards work */
++       {
++              for( i = 0 ; i < MAX_JTAG_DEV; i++) 
++              {
++                        dev=&(jtag_devs[i]);
++                        if(dev->base && request_region(dev->base, dev->region, "jtag") && (dev->base != 0)) 
++                      {
++                                PRINTMSG("(%d , %d) forced by user, io=0x%04lx\n",jtag_major,i,dev->base);
++                                        dev->present=1;
++                      }       
++                        else  
++                      {
++                                if( dev->base != 0)
++                                      release_region(dev->base, dev->region);
++                      }
++              }
++                return 0;
++       }
++}
++
++int init_module(void)
++{
++      int result,i;
++      result = register_chrdev(jtag_major, DRIVER_NAME, &jtag_fops);
++      if (result < 0) {
++              PRINTMSG("Couldn't register char device err == %d\n",jtag_major);
++              return -1;
++      }
++
++      if ( jtag_major == 0 ) 
++              jtag_major = result;
++
++      for ( i = 0; i < MAX_JTAG_DEV; i++) {
++              jtag_devs[i].base=io[i];        
++      }
++
++      jtag_probe();
++
++      PRINTMSG("Registered character device, major == %d\n",jtag_major);      
++      return 0;
++}     
++
++void cleanup_module(void)
++{
++      int i=0;
++
++      for( i = 0; i < MAX_JTAG_DEV; i++) {
++              if( jtag_devs[i].present)       
++                      release_region(jtag_devs[i].base, jtag_devs[i].region);
++      }
++                      
++      unregister_chrdev(jtag_major, DRIVER_NAME);
++      PRINTMSG("Unloaded char device\n");
++}
++
++
++int
++jtag_open (struct inode *inode, struct file *filp)
++{
++    int unit = MINOR(inode->i_rdev);
++    JTAG_DEV *dev = &jtag_devs[unit];
++
++    if (unit < 0 || unit > MAX_JTAG_DEV || !dev->present)
++      return (-ENXIO);
++
++    /*
++     * Only allow a single open at a time 
++     */
++    if (dev->open)
++      return (-EBUSY);
++    dev->open = 1;
++    
++    /*
++     * Initialise the hardware registers
++     */
++   
++    LPT_WRITE (dev, LPT_CTRL, 0);
++    DELAY(50);
++    LPT_WRITE (dev, LPT_CTRL, LPT_CTRL_INIT);
++
++    MOD_INC_USE_COUNT;
++
++    return (0);
++}
++
++int
++jtag_close(struct inode *inode, struct file *filp)
++{
++  
++    int unit = MINOR(inode->i_rdev);
++    JTAG_DEV *dev = &jtag_devs[unit];
++    
++    if (unit < 0 || unit > MAX_JTAG_DEV || !dev->present)
++      return (-ENXIO);
++    
++    dev->open = 0;
++
++    MOD_DEC_USE_COUNT;
++
++    return (0);
++}
++
++int
++jtag_ioctl (struct inode *inode, struct file *filp, unsigned int io_cmd, unsigned long io_data)
++{
++    int                  unit = MINOR(inode->i_rdev);
++    JTAG_DEV             *dev = &jtag_devs[unit];
++    JTAG_RESET_ARGS      *resetargs;
++    JTAG_SHIFT_ARGS      *shiftargs;
++    I2C_ARGS           *i2cargs;
++    I2C_CLOCK_SHIFT_ARGS *clockargs;
++    u_char             *buf;
++    int                         freq;
++
++    if (unit < 0 || unit > MAX_JTAG_DEV || !dev->present)
++      return (-ENXIO);
++    
++    PRINTF (DBG_IOCTL, ("jtag_ioctl: device %d cmd=%x\n", unit, io_cmd));
++
++    switch (io_cmd)
++    {
++    case JTAG_RESET:
++      resetargs = (JTAG_RESET_ARGS *) io_data;
++
++      if (! VALID_JTAG_RING (resetargs->ring))
++          return (-EINVAL);
++      
++      jtagdrv_select_ring (dev, resetargs->ring);
++      jtagdrv_reset (dev);
++      return (0);
++      
++    case JTAG_SHIFT_IR:
++    case JTAG_SHIFT_DR:
++      shiftargs = (JTAG_SHIFT_ARGS *) io_data;
++      
++      if (! VALID_JTAG_RING (shiftargs->ring) || shiftargs->nbits > (JTAG_MAX_DATA_LEN*JTAG_MAX_CHIPS)) {
++          return (-EFAULT);
++              }
++
++      buf = (u_char *) kmalloc (JTAG_NBYTES(shiftargs->nbits), GFP_KERNEL);
++
++      if (buf == (u_char *) NULL)
++          return (-ENOMEM);
++      
++      if (copy_from_user (buf, shiftargs->value, JTAG_NBYTES(shiftargs->nbits)))
++      {
++          kfree(buf);
++          return (-EFAULT);
++      }
++
++
++      jtagdrv_select_ring (dev, shiftargs->ring);
++
++      if (io_cmd == JTAG_SHIFT_IR)
++          jtagdrv_shift_ir (dev, buf, shiftargs->nbits);
++      else
++          jtagdrv_shift_dr (dev, buf, shiftargs->nbits);
++      
++      if (copy_to_user (shiftargs->value, buf, JTAG_NBYTES (shiftargs->nbits)))
++      {
++          kfree (buf);
++          return (-EFAULT);
++      }
++
++      kfree (buf);
++      return (0);
++
++    case I2C_WRITE:
++    case I2C_READ:
++    case I2C_WRITEREG:
++    case I2C_READREG:
++      i2cargs = (I2C_ARGS *) io_data;
++
++      if (! VALID_I2C_RING(i2cargs->ring) || i2cargs->count > I2C_MAX_DATA_LEN)
++          return (-EFAULT);
++
++      jtagdrv_select_ring (dev, RING_I2C_BIT | i2cargs->ring);
++      switch (io_cmd)
++      {
++      case I2C_WRITE:
++          i2cargs->ok = jtagdrv_i2c_write (dev, i2cargs->device, i2cargs->count, i2cargs->data);
++          break;
++
++      case I2C_READ:
++          i2cargs->ok = jtagdrv_i2c_read (dev, i2cargs->device, i2cargs->count, i2cargs->data);
++          break;
++
++      case I2C_WRITEREG:
++          i2cargs->ok = jtagdrv_i2c_writereg (dev, i2cargs->device, i2cargs->reg, i2cargs->count, i2cargs->data);
++          break;
++
++      case I2C_READREG:
++          i2cargs->ok = jtagdrv_i2c_readreg (dev, i2cargs->device, i2cargs->reg, i2cargs->count, i2cargs->data);
++          break;
++      }
++      return (0);
++
++    case I2C_CLOCK_SHIFT:
++      clockargs = (I2C_CLOCK_SHIFT_ARGS *) io_data;
++
++      freq = (10 * clockargs->m / (1 << (((clockargs->n + 1) & 3))));
++      
++      /* validate the value, and initialise the ring */
++      if (clockargs->t != 0 || clockargs->n > 3 || clockargs->m > 127)
++          return (-EINVAL);
++      
++      jtagdrv_select_ring (dev, RING_I2C_BIT | RING_CLOCK_SHIFT);
++      jtagdrv_i2c_clock_shift (dev, clockargs->t, clockargs->n, clockargs->m);
++      jtagdrv_select_ring (dev, 0);
++      return (0);
++
++    default:
++      return (-EINVAL);
++    }
++    return (-EINVAL);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/jtag/jtagdrv_Linux.h
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/jtag/jtagdrv_Linux.h   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/jtag/jtagdrv_Linux.h        2005-06-01 23:12:54.693424816 -0400
+@@ -0,0 +1,174 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: jtagdrv_Linux.h,v 1.3 2002/08/09 11:18:37 addy Exp $"
++/*      $Source: /cvs/master/quadrics/jtagmod/jtagdrv_Linux.h,v $*/
++
++#ifndef __JTAGDRV_LINUX_H
++#define __JTAGDRV_LINUX_H
++
++#include <qsnet/kernel.h>
++#include <asm/io.h>
++
++typedef struct jtag_dev
++{
++    unsigned long     base;
++    int                 region;
++
++    u_int             present:1;
++    u_int             open:1;
++} JTAG_DEV;
++
++/*
++**
++**                    Hardware Defines
++**
++*/
++
++/*
++ * Assume that bit 4 of the Control Register is set to 1 (by default) 
++ * to enable the printer port (CS3).
++ *
++ * The default base address is 3BC-3BF. 
++ */
++
++#define LPT0  0x3BC                   /* CSR Base Address - note this can
++                                       * change depending on the setting
++                                       * in the Control Register 0.
++                                       *
++                                       * LPT1 0x378
++                                       * LPT2 0x278
++                                       * LPT3 0x268
++                                      */
++
++/*
++ *    Register offsets from the port base address
++ */
++
++#define LPT_REGISTER_0        0
++#define LPT_REGISTER_1        1
++#define LPT_REGISTER_2        2
++#define LPT_REGISTER_3        0x400
++#define LPT_REGISTER_4        0x401
++#define LPT_REGISTER_5        0x402
++
++/*
++ *    Chip control registers
++ */
++                                      /* Base address for Super I/O National*/
++
++#define SIO_BASE_ADDR 0x26e           /* Semiconductor PC87332VLJ combo-chip*/
++#define CR4_REG               0x04            /* index 4, printer control reg 4 */
++
++#define LPT_EPP               0x01            /* Enable bit for epp */
++#define LPT_ECP               0x04            /* Enable bit for ecp */
++
++/*
++ *    Registers for use with centronics, nibble and byte modes.
++ */
++
++#define LPT_DATA      LPT_REGISTER_0          /* line printer port data */
++#define LPT_STAT      LPT_REGISTER_1          /* LPT port status        */
++#define LPT_CTRL      LPT_REGISTER_2          /* LPT port control       */
++
++/*
++ *    Registers for use with ECP mode.
++ */ 
++
++#define LPT_DFIFO     LPT_REGISTER_3          /* r/w fifo register    */
++#define LPT_CFGB      LPT_REGISTER_4          /* Configuration B      */
++#define LPT_ECR               LPT_REGISTER_5          /* Exteded control      */
++
++/*
++ * Bit assignments for ECR register.
++ */
++
++      /* Bits 0-4 */
++
++#define LPT_ECR_EMPTY 0x01            /* FIFO is empty */
++#define LPT_ECR_FULL  0x02            /* FIFO is full */
++#define LPT_ECR_SERV  0x04            /* Service bit */
++#define LPT_ECR_DMA   0x08            /* DMA enable */
++#define LPT_ECR_nINTR 0x10            /* Interrupt disable */
++
++      /*
++       * Bits 5-7 are ECR modes.
++       */
++
++#define LPT_ECR_PAR   0x20            /* Parallel port FIFO mode */
++#define LPT_ECR_ECP   0x60            /* ECP mode */
++#define LPT_ECR_CFG   0xE0            /* Configuration mode */
++#define LPT_ECR_CLEAR ~0xE0           /* Cear mode bits */
++
++/*
++ * Bit assignments for the parallel port STATUS register:
++ */
++
++#define LPT_STAT_BIT0 0X1     /* Reserved. Bit always set.            */
++#define LPT_STAT_BIT1 0X2     /* Reserved. Bit always set.            */
++#define LPT_STAT_IRQ  0x4     /* interrupt status bit                 */
++#define LPT_STAT_ERROR        0x8     /* set to 0 to indicate error           */
++#define LPT_STAT_SLCT 0x10    /* status of SLCT lead from printer     */
++#define LPT_STAT_PE   0x20    /* set to 1 when out of paper           */
++#define LPT_STAT_ACK  0x40    /* acknowledge - set to 0 when ready    */
++#define LPT_STAT_nBUSY        0x80    /* busy status bit, 0=busy, 1=ready     */
++
++/*
++ * Bit assignments for the parallel port CONTROL register:
++ */
++
++#define LPT_CTRL_nSTROBE      0x1     /* Printer Strobe Control       */
++#define LPT_CTRL_nAUTOFD      0x2     /* Auto Feed Control            */
++#define LPT_CTRL_INIT         0x4     /* Initialize Printer Control   */
++#define LPT_CTRL_nSLCTIN      0x8     /* 0=select printer, 1=not selected */
++#define LPT_CTRL_IRQ          0x10    /* Interrupt Request Enable Control */
++#define LPT_CTRL_DIR          0x20    /* Direction control            */
++#define LPT_CTRL_BIT6         0X40    /* Reserved. Bit always set.    */
++#define LPT_CTRL_BIT7         0X80    /* Reserved. Bit always set.    */
++
++
++#define LPT_WRITE(dev, regname, value)        do { outb(value, (dev)->base + regname); } while (0)
++#define LPT_READ(dev, regname,value)  do { value = inb((dev)->base + regname); } while (0)
++
++
++
++/* Standard register access macros */
++#define LPT_WRITE_CTRL(dev, value)    LPT_WRITE(dev, LPT_CTRL, LPT_CTRL_INIT | value)
++#define LPT_WRITE_DATA(dev, value)    LPT_WRITE(dev, LPT_DATA, value)
++#define LPT_READ_STAT(dev, value)     LPT_READ(dev, LPT_STAT, value)
++
++/*
++ * The jtag signals are connected to the parallel port as follows :
++ *
++ *  TRST      bit 0
++ *  TDI               bit 1
++ *  TMS               bit 2
++ *  TCLK      AFX
++ *  TDO               PE
++ */
++#define LPT_DATA_TRST 1
++#define LPT_DATA_TDI  2
++#define LPT_DATA_TMS  4
++#define LPT_CTRL_TCLK LPT_CTRL_nAUTOFD
++#define LPT_STAT_TDO  LPT_STAT_PE
++
++/*
++ * The I2C signals are connected as follows :
++ */
++#define LPT_DATA_SDA  2
++#define LPT_CTRL_SCLK LPT_CTRL_nAUTOFD
++#define LPT_STAT_SDA  LPT_STAT_PE
++
++/*
++ * The ring selection signals are as follows :
++ *  addr      bit 0-7
++ *  clock     nSLCTIN
++ */
++#define LPT_CTRL_RCLK LPT_CTRL_nSLCTIN
++
++
++#endif /* __JTAGDRV_LINUX_H */
+Index: linux-2.4.21/drivers/net/qsnet/jtag/Makefile
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/jtag/Makefile  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/jtag/Makefile       2005-06-01 23:12:54.694424664 -0400
+@@ -0,0 +1,31 @@
++#
++# Makefile for Quadrics QsNet
++#
++# Copyright (c) 2002-2004 Quadrics Ltd
++#
++# File: drivers/net/qsnet/jtag/Makefile
++#
++
++
++#
++
++#
++# Makefile for Quadrics QsNet
++#
++# Copyright (c) 2004 Quadrics Ltd.
++#
++# File: driver/net/qsnet/jtag/Makefile
++#
++
++list-multi            := jtag.o
++jtag-objs     := jtagdrv_Linux.o jtagdrv.o
++export-objs           := 
++obj-$(CONFIG_JTAG)    := jtag.o
++
++jtag.o : $(jtag-objs)
++      $(LD) -r -o $@ $(jtag-objs)
++
++EXTRA_CFLAGS          +=  -DDEBUG -DDEBUG_PRINTF -DDEBUG_ASSERT
++
++include $(TOPDIR)/Rules.make
++
+Index: linux-2.4.21/drivers/net/qsnet/jtag/Makefile.conf
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/jtag/Makefile.conf     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/jtag/Makefile.conf  2005-06-01 23:12:54.694424664 -0400
+@@ -0,0 +1,10 @@
++# Flags for generating QsNet Linux Kernel Makefiles
++MODNAME               =       jtag.o
++MODULENAME    =       jtag
++KOBJFILES     =       jtagdrv_Linux.o jtagdrv.o
++EXPORT_KOBJS  =       
++CONFIG_NAME   =       CONFIG_JTAG
++SGALFC                =       
++# EXTRALINES START
++
++# EXTRALINES END
+Index: linux-2.4.21/drivers/net/qsnet/jtag/quadrics_version.h
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/jtag/quadrics_version.h        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/jtag/quadrics_version.h     2005-06-01 23:12:54.694424664 -0400
+@@ -0,0 +1 @@
++#define QUADRICS_VERSION "4.30qsnet"
+Index: linux-2.4.21/drivers/net/qsnet/Makefile
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/Makefile       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/Makefile    2005-06-01 23:12:54.695424512 -0400
+@@ -0,0 +1,17 @@
++#
++# Makefile for Quadrics QsNet
++#
++# Copyright (c) 2003 Quadrics Ltd.
++#
++# File: driver/net/qsnet/Makefile
++#
++
++subdir-$(CONFIG_QSNET)  += qsnet elan
++subdir-$(CONFIG_ELAN3)  += elan3
++subdir-$(CONFIG_ELAN4)  += elan4
++subdir-$(CONFIG_EP)     += ep
++subdir-$(CONFIG_EIP)    += eip
++subdir-$(CONFIG_RMS)    += rms
++subdir-$(CONFIG_JTAG)   += jtag
++
++include $(TOPDIR)/Rules.make
+Index: linux-2.4.21/drivers/net/qsnet/qsnet/debug.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/qsnet/debug.c  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/qsnet/debug.c       2005-06-01 23:12:54.696424360 -0400
+@@ -0,0 +1,583 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: debug.c,v 1.21 2004/08/19 08:09:57 david Exp $"
++/*      $Source: /cvs/master/quadrics/qsnet/debug.c,v $ */
++
++#include <qsnet/kernel.h>
++#include <qsnet/debug.h>
++#include <qsnet/procfs_linux.h>
++
++caddr_t        qsnet_debug_buffer_ptr = NULL;
++int          qsnet_debug_front      = 0;
++int          qsnet_debug_back       = 0;
++int            qsnet_debug_lost_lines = 0;
++int          qsnet_debug_disabled   = 0;
++
++int          qsnet_debug_line_size  = 256;
++int          qsnet_debug_num_lines  = 8192;
++
++int          qsnet_assfail_mode     = 1;                      /* default to BUG() */
++
++int            qsnet_debug_running    = 0;
++int            kqsnet_debug_running   = 0;
++
++static spinlock_t qsnet_debug_lock;
++static kcondvar_t qsnet_debug_wait;
++static char       qsnet_debug_buffer_space[8192];
++
++#define QSNET_DEBUG_PREFIX_MAX_SIZE   32
++#define QSNET_DEBUG_MAX_WORDWRAP      15
++
++/* must be larger than  QSNET_DEBUG_PREFIX_MAX_SIZE +  QSNET_DEBUG_MAX_WORDWRAP + 2 */
++#if defined(DIGITAL_UNIX) 
++#define QSNET_DEBUG_CONSOLE_WIDTH 80
++#elif defined(LINUX)
++#define QSNET_DEBUG_CONSOLE_WIDTH 128
++#endif
++
++#define isspace(CH)    ((CH==' ') | (CH=='\t') | (CH=='\n'))
++
++#ifdef LINUX
++#define ALLOC_DEBUG_BUFFER(ptr)               do { (ptr) = (void *)__get_free_pages (GFP_KERNEL, get_order (qsnet_debug_num_lines * qsnet_debug_line_size)); } while (0)
++#define FREE_DEBUG_BUFFER(ptr)                free_pages ((unsigned long) ptr, get_order (qsnet_debug_num_lines * qsnet_debug_line_size))
++#else
++#define ALLOC_DEBUG_BUFFER(ptr)               KMEM_ALLOC (ptr, caddr_t, qsnet_debug_num_lines * qsnet_debug_line_size, 1)
++#define FREE_DEBUG_BUFFER(ptr)                KMEM_FREE (ptr, qsnet_debug_num_lines * qsnet_debug_line_size)
++#endif
++
++void
++qsnet_debug_init ()
++{
++      spin_lock_init (&qsnet_debug_lock);
++      kcondvar_init  (&qsnet_debug_wait);
++
++      qsnet_debug_front      = 0;
++      qsnet_debug_back       = 0;
++      qsnet_debug_lost_lines = 0;
++
++      if (qsnet_debug_line_size < (QSNET_DEBUG_PREFIX_MAX_SIZE + QSNET_DEBUG_MAX_WORDWRAP + 2))
++              qsnet_debug_line_size = 256;
++
++      qsnet_debug_running    = 1;
++
++      qsnet_proc_register_int (qsnet_procfs_config, "assfail_mode", &qsnet_assfail_mode, 0);
++}
++
++void
++qsnet_debug_fini()
++{
++      if (!qsnet_debug_running) return;
++
++      remove_proc_entry ("assfail_mode", qsnet_procfs_config);
++
++      spin_lock_destroy (&qsnet_debug_lock);
++      kcondvar_destroy  (&qsnet_debug_wait);
++      
++      if (qsnet_debug_buffer_ptr)
++              FREE_DEBUG_BUFFER (qsnet_debug_buffer_ptr);
++
++      qsnet_debug_buffer_ptr     = NULL;
++      qsnet_debug_lost_lines = 0;     
++      qsnet_debug_running    = 0;     
++}
++
++void
++qsnet_debug_disable(int val)
++{
++      qsnet_debug_disabled = val;
++}
++
++void
++qsnet_debug_alloc()
++{
++      caddr_t ptr;
++      unsigned long flags;
++
++      if (!qsnet_debug_running) return;
++
++      if (qsnet_debug_buffer_ptr == NULL)
++      {
++              ALLOC_DEBUG_BUFFER (ptr);
++
++              if (ptr != NULL)
++              {
++                      spin_lock_irqsave (&qsnet_debug_lock, flags);
++                      if (qsnet_debug_buffer_ptr == NULL)
++                      {
++                              qsnet_debug_buffer_ptr = ptr;
++                              spin_unlock_irqrestore (&qsnet_debug_lock, flags);
++                      }
++                      else
++                      {
++                              spin_unlock_irqrestore (&qsnet_debug_lock, flags);
++
++                              FREE_DEBUG_BUFFER (ptr);
++                      }
++              }
++      }
++      
++}
++
++static void 
++qsnet_prefix_debug(unsigned int mode, char *prefix, char *buffer) 
++{
++      /* assumes caller has lock */
++
++      int  prefixlen = strlen(prefix);
++      char pref[QSNET_DEBUG_PREFIX_MAX_SIZE];
++      int  prefix_done = 0;
++
++      if (!qsnet_debug_running) return;
++
++      if (qsnet_debug_disabled)
++              return;
++
++      if (prefixlen >= QSNET_DEBUG_PREFIX_MAX_SIZE) 
++      {
++              strncpy(pref,prefix,QSNET_DEBUG_PREFIX_MAX_SIZE -2);
++              strcpy (&pref[QSNET_DEBUG_PREFIX_MAX_SIZE-5],"... ");
++
++              prefix = pref;
++                prefixlen = strlen(prefix);
++      }
++
++#ifdef CONFIG_MPSAS
++      {
++              char *p;
++#define TRAP_PUTCHAR_B                        (0x17a - 256)
++#define SAS_PUTCHAR(c)                        do {\
++                      register int o0 asm ("o0") = (c);\
++\
++                      asm volatile ("ta %0; nop" \
++                                    : /* no outputs */\
++                                    : /* inputs */ "i" (TRAP_PUTCHAR_B), "r" (o0)\
++                                    : /* clobbered */ "o0");\
++\
++                      if (o0 == '\n') {\
++                              o0 = '\r';\
++\
++                              asm volatile ("ta %0; nop" \
++                                            : /* no outputs */\
++                                            : /* inputs */ "i" (TRAP_PUTCHAR_B), "r" (o0)\
++                                            : /* clobbered */ "o0");\
++                      }\
++              } while(0)
++
++              for (p = prefix; *p; p++)
++                      SAS_PUTCHAR (*p);
++
++              for (p = buffer; *p; p++)
++                      SAS_PUTCHAR (*p);
++      }
++#else
++      if (mode & QSNET_DEBUG_BUFFER)
++      {
++              if (qsnet_debug_buffer_ptr == NULL)
++                      qsnet_debug_lost_lines++;
++              else
++              {                   
++                      caddr_t base = &qsnet_debug_buffer_ptr[qsnet_debug_line_size * qsnet_debug_back];
++                      caddr_t lim  = base + qsnet_debug_line_size - 2;
++                      caddr_t p;
++              
++                      p = buffer; 
++                      prefix_done = 0;
++                      while (*p) 
++                      {
++                              /* sort out prefix */
++                              if ( prefix_done++ ) 
++                              {
++                                      int i;
++                                      for(i=0;i<prefixlen;i++)
++                                              base[i] = ' ';
++                                      /* memset(base,' ',prefixlen); */
++                              }
++                              else
++                                      strcpy(base,prefix);
++                              base += prefixlen; /* move the base on */
++
++                              /* copy data */
++                              for ( ; *p && (base < lim); )
++                                      *base++ = *p++;
++
++                              /* if line split then add \n */
++                              if ((base == lim) && (*base != '\n'))
++                              {
++                                      char *ptr;
++                                      int   count;
++
++                                      *base = '\n';
++                                      /* we added a \n cos it was end of line put next char was \n */
++                                      if (*p == '\n') 
++                                              p++;
++                                      else
++                                      {
++                                              /* lets see if we can back track and find a white space to break on */
++                                              ptr = base-1;
++                                              count = 1;
++                                              while ( ( !isspace(*ptr) ) && ( count < QSNET_DEBUG_MAX_WORDWRAP ))
++                                              {
++                                                      count++;
++                                                      ptr--;
++                                              }
++
++                                              if ( isspace(*ptr) ) 
++                                              {
++                                                      /* found somewhere to wrap to */
++                                                      p -= (count-1); /* need to loose the white space */
++                                                      base = ptr;
++                                                      *base = '\n';
++                                              }
++                                      }
++                                      base++;
++                              }
++                              *base = '\0';
++
++                              /* move on pointers */
++                              qsnet_debug_back = (++qsnet_debug_back == qsnet_debug_num_lines) ? 0 : qsnet_debug_back;            
++                              if (qsnet_debug_back == qsnet_debug_front)
++                              {
++                                      qsnet_debug_lost_lines++;
++                                      qsnet_debug_front = (++qsnet_debug_front == qsnet_debug_num_lines) ? 0 : qsnet_debug_front;
++                              }
++                              base  = &qsnet_debug_buffer_ptr[qsnet_debug_line_size * qsnet_debug_back];
++                              lim  =  base + qsnet_debug_line_size - 2;
++                      }
++                      kcondvar_wakeupone (&qsnet_debug_wait, &qsnet_debug_lock);
++              }
++      }
++
++      if (mode & QSNET_DEBUG_CONSOLE)
++      {
++              int     remaining = QSNET_DEBUG_CONSOLE_WIDTH - prefixlen;
++              caddr_t p;
++              char    line[QSNET_DEBUG_CONSOLE_WIDTH +2];
++              int     len;
++          
++              strcpy (pref,prefix);
++              prefix_done = 0;
++
++              p = buffer;
++              while ( *p )
++              {
++                      /* use the prefix only once */
++                      if  ( prefix_done++ > 0 ) 
++                              {
++                                      int i;
++                                      for(i=0;i<prefixlen;i++)
++                                              pref[i] = ' ';
++                                      /* memset(perf,' ',prefixlen); */
++                              }       
++
++                      len=strlen(p);
++                      if (len > remaining) len = remaining;
++                
++                      strncpy(line, p, len);
++                      line[len] = 0;
++                      p += len;
++                  
++                      /* word wrap */
++                      if ((len == remaining) && *p && !isspace(*p))
++                      {
++                              /* lets see if we can back track and find a white space to break on */
++                              char * ptr = &line[len-1];
++                              int    count = 1;
++
++                              while ( ( !isspace(*ptr) ) && ( count < QSNET_DEBUG_MAX_WORDWRAP ))
++                              {
++                                      count++;
++                                      ptr--;
++                              }
++
++                              if ( isspace(*ptr) ) 
++                              {
++                                      /* found somewhere to wrap to */
++                                      p -= (count-1); /* need to loose the white space */
++                                      len -= count;
++                              }               
++                      }
++
++                      if (line[len-1] != '\n' ) 
++                      {
++                              line[len] = '\n';
++                              line[len+1] = 0;
++                      }
++
++                      /* we put a \n in so dont need another one next */
++                      if ( *p == '\n')
++                              p++;
++
++#if defined(DIGITAL_UNIX)
++                      {
++                              char *pr;
++
++                              for (pr = pref; *pr; pr++)
++                                      cnputc (*pr);
++
++                              for (pr = line; *pr; pr++)
++                                      cnputc (*pr); 
++                      }
++#elif defined(LINUX)
++                      printk("%s%s",pref,line);
++#endif
++              }
++      }
++#endif /* CONFIG_MPSAS */
++}
++
++void
++qsnet_vdebugf (unsigned int mode, char *prefix, char *fmt, va_list ap)
++{
++      unsigned long flags;
++
++      if (!qsnet_debug_running) return;
++
++      spin_lock_irqsave (&qsnet_debug_lock, flags);
++
++      qsnet_debug_buffer_space[0] = '\0';
++
++#if defined(DIGITAL_UNIX)
++      prf (qsnet_debug_buffer_space+strlen(qsnet_debug_buffer_space), NULL, fmt, ap);
++#elif defined(LINUX)
++      vsprintf (qsnet_debug_buffer_space+strlen(qsnet_debug_buffer_space), fmt, ap);
++#endif
++
++      if (prefix == NULL)
++              printk ("qsnet_vdebugf: prefix==NULL\n");
++      else
++              qsnet_prefix_debug(mode, prefix, qsnet_debug_buffer_space);
++
++      spin_unlock_irqrestore (&qsnet_debug_lock, flags);
++}
++
++void kqsnet_debugf(char *fmt,...)
++{
++      if ( kqsnet_debug_running ) {
++              va_list ap;
++              char string[20];
++              
++              sprintf (string, "mm=%p:", current->mm);
++              va_start(ap, fmt);
++              qsnet_vdebugf(QSNET_DEBUG_BUFFER, string, fmt, ap);
++              va_end(ap);
++      }       
++}
++void 
++qsnet_debugf(unsigned int mode, char *fmt,...)
++{
++      va_list       ap;
++      unsigned long flags;
++
++      if (!qsnet_debug_running) return;
++
++      spin_lock_irqsave (&qsnet_debug_lock, flags);
++
++      qsnet_debug_buffer_space[0] = '\0';
++
++      va_start (ap, fmt);
++#if defined(DIGITAL_UNIX)
++      prf (qsnet_debug_buffer_space+strlen(qsnet_debug_buffer_space), NULL, fmt, ap);
++#elif defined(LINUX)
++      vsprintf (qsnet_debug_buffer_space+strlen(qsnet_debug_buffer_space), fmt, ap);
++#endif
++      va_end (ap);
++
++      qsnet_prefix_debug(mode, "", qsnet_debug_buffer_space); 
++
++      spin_unlock_irqrestore (&qsnet_debug_lock, flags);
++}
++
++int
++qsnet_debug_buffer (caddr_t ubuffer, int len)
++{
++      caddr_t buffer, ptr, base;
++      int     remain, len1;
++      unsigned long flags;
++      static  char qsnet_space[65536];
++
++      if (!qsnet_debug_running) return (0);
++
++      if (len < qsnet_debug_line_size)
++              return (-1);
++
++      if (len > (qsnet_debug_line_size * qsnet_debug_num_lines))
++              len = qsnet_debug_line_size * qsnet_debug_num_lines;
++    
++      if ( len > 65536 ) {
++              KMEM_ZALLOC (buffer, caddr_t, len, 1);
++      } else 
++              buffer = qsnet_space;
++
++      if (buffer == NULL)
++              return (-1);
++
++      if (qsnet_debug_buffer_ptr == NULL)
++              qsnet_debug_alloc();
++
++      if (qsnet_debug_buffer_ptr == NULL)
++      {
++              if ( len > 65536 )
++                      KMEM_FREE (buffer, len);
++              return (-1);
++      }
++
++      spin_lock_irqsave (&qsnet_debug_lock, flags);
++    
++      while (!qsnet_debug_lost_lines && (qsnet_debug_back == qsnet_debug_front))
++              if (kcondvar_waitsig (&qsnet_debug_wait, &qsnet_debug_lock, &flags) == 0)
++                      break;
++    
++      ptr    = buffer;
++      remain = len;
++
++      if (qsnet_debug_lost_lines)
++      {
++              qsnet_debug_lost_lines = 0;
++              strcpy (ptr, "Debug Buffer has overflowed!!\n");
++              len1 = strlen (ptr);
++
++              remain -= len1;
++              ptr    += len1;
++      }
++
++      while (qsnet_debug_front != qsnet_debug_back)
++      {
++              /* copy the line from DebugFront */
++              base = &qsnet_debug_buffer_ptr[qsnet_debug_front*qsnet_debug_line_size];
++
++              len1 = strlen (base);
++
++              if (len1 > remain)
++                      break;
++      
++              bcopy (base, ptr, len1);
++      
++              ptr += len1;
++              remain -= len1;
++
++              qsnet_debug_front = (++qsnet_debug_front == qsnet_debug_num_lines) ? 0 : qsnet_debug_front;
++      }
++
++      spin_unlock_irqrestore (&qsnet_debug_lock, flags);
++
++      len1 = ptr - buffer;
++
++      if (len1 != 0 && copyout (buffer, ubuffer, len1))
++              len1 = -1;
++
++      if ( len > 65536 )
++              KMEM_FREE (buffer, len);
++   
++      return (len1);
++}
++
++void
++qsnet_debug_buffer_on() 
++{
++      if (qsnet_debug_buffer_ptr == NULL)
++              qsnet_debug_alloc();
++}
++
++void 
++qsnet_debug_buffer_clear()
++{
++      unsigned long flags;
++
++      qsnet_debug_buffer_on();
++      
++      if (qsnet_debug_buffer_ptr != NULL){
++              spin_lock_irqsave (&qsnet_debug_lock, flags);
++              qsnet_debug_front      = 0;
++              qsnet_debug_back       = 0;
++              qsnet_prefix_debug(QSNET_DEBUG_BUFFER,"Clear","");
++              spin_unlock_irqrestore (&qsnet_debug_lock, flags);      
++      }
++}
++
++void 
++qsnet_debug_buffer_mark(char *str)
++{
++      unsigned long flags;    
++
++      qsnet_debug_buffer_on();
++
++      if (qsnet_debug_buffer_ptr != NULL) {
++              spin_lock_irqsave (&qsnet_debug_lock, flags);
++              qsnet_prefix_debug(QSNET_DEBUG_BUFFER,"Mark",str);
++              spin_unlock_irqrestore (&qsnet_debug_lock, flags);
++      }
++}
++int
++qsnet_debug_dump ()
++{
++      unsigned long flags;
++
++      if (!qsnet_debug_running) return (0);
++
++      if (qsnet_debug_buffer_ptr == NULL)
++              qsnet_debug_alloc();
++
++      if (qsnet_debug_buffer_ptr == NULL)
++              return (-1);
++
++      spin_lock_irqsave (&qsnet_debug_lock, flags);
++
++      while (qsnet_debug_front != qsnet_debug_back)
++      {
++              printk ("%s", &qsnet_debug_buffer_ptr[qsnet_debug_front*qsnet_debug_line_size]);
++
++              qsnet_debug_front = (++qsnet_debug_front == qsnet_debug_num_lines) ? 0 : qsnet_debug_front;
++      }
++
++      if (qsnet_debug_lost_lines)
++              printk ("\n**** Debug buffer has lost %d lines\n****\n",qsnet_debug_lost_lines);
++
++      spin_unlock_irqrestore (&qsnet_debug_lock, flags);
++
++      return (0);
++}
++
++int
++qsnet_debug_kmem (void *handle)
++{
++      if (!qsnet_debug_running) return (0);
++
++#ifdef KMEM_DEBUG
++      qsnet_kmem_display(handle);
++#endif
++      return (0);
++}
++
++int
++qsnet_assfail (char *ex, const char *func, char *file, int line)
++{
++      qsnet_debugf (QSNET_DEBUG_BUFFER, "qsnet: assertion failure: %s, function: %s, file %s, line: %d\n", ex, func, file, line);
++
++      printk (KERN_EMERG "qsnet: assertion failure: %s, function: %s, file %s, line: %d\n", ex, func, file, line);
++
++      if (panicstr)
++              return (0);
++
++      if (qsnet_assfail_mode & 1)                             /* return to BUG() */
++              return 1;
++
++      if (qsnet_assfail_mode & 2)
++              panic ("qsnet: assertion failure: %s, function: %s, file %s, line: %d\n", ex, func, file, line);
++      if (qsnet_assfail_mode & 4)
++              qsnet_debug_disable (1);
++
++      return 0;
++
++}
++
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/qsnet/i686_mmx.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/qsnet/i686_mmx.c       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/qsnet/i686_mmx.c    2005-06-01 23:12:54.696424360 -0400
+@@ -0,0 +1,99 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: i686_mmx.c,v 1.11 2004/01/05 12:08:25 mike Exp $"
++/*      $Source: /cvs/master/quadrics/qsnet/i686_mmx.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#if defined(LINUX_I386)
++
++#include <linux/config.h>
++#include <linux/sched.h>
++#include <asm/processor.h>
++#include <asm/i387.h>
++
++int mmx_disabled = 0;
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
++/* These functions are lifted from arch/i386/kernel/i387.c
++ * and MUST be kept in step with the kernel (currently 2.4.17)
++ * alternatively we should export the kernel_fpu_begin() function
++ */
++static inline void __save_init_fpu( struct task_struct *tsk )
++{
++      if ( cpu_has_fxsr ) {
++              asm volatile( "fxsave %0 ; fnclex"
++                            : "=m" (tsk->thread.i387.fxsave) );
++      } else {
++              asm volatile( "fnsave %0 ; fwait"
++                            : "=m" (tsk->thread.i387.fsave) );
++      }
++      tsk->flags &= ~PF_USEDFPU;
++}
++#if defined(MODULE)
++void kernel_fpu_begin(void)
++{
++      struct task_struct *tsk = current;
++
++      if (tsk->flags & PF_USEDFPU) {
++              __save_init_fpu(tsk);
++              return;
++      }
++      clts();
++}
++#endif
++#endif
++
++extern inline int
++mmx_preamble(void)
++{
++    if (mmx_disabled || in_interrupt())
++      return (0);
++
++    kernel_fpu_begin();
++
++    return (1);
++}
++
++extern inline void
++mmx_postamble(void)
++{
++    kernel_fpu_end();
++}
++
++extern u64
++qsnet_readq (volatile u64 *ptr)
++{
++    u64 value;
++
++    if (! mmx_preamble())
++      value = *ptr;
++    else
++    {
++      asm volatile ("movq (%0), %%mm0\n"
++                    "movq %%mm0, (%1)\n"
++                    : : "r" (ptr), "r" (&value) : "memory");
++      mmx_postamble();
++    }
++    return (value);
++}
++
++void
++qsnet_writeq(u64 value, volatile u64 *ptr)
++{
++    if (! mmx_preamble())
++      *ptr = value;
++    else
++    {
++      asm volatile ("movq (%0), %%mm0\n"
++                    "movq %%mm0, (%1)\n"
++                    : : "r" (&value), "r" (ptr) : "memory");
++      mmx_postamble();
++    }
++}
++#endif
+Index: linux-2.4.21/drivers/net/qsnet/qsnet/kernel_linux.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/qsnet/kernel_linux.c   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/qsnet/kernel_linux.c        2005-06-01 23:12:54.697424208 -0400
+@@ -0,0 +1,856 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: kernel_linux.c,v 1.71.2.3 2004/11/04 11:03:47 david Exp $"
++/*      $Source: /cvs/master/quadrics/qsnet/kernel_linux.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <qsnet/ctrl_linux.h>
++#include <qsnet/kpte.h>
++
++#include <linux/sysctl.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++
++#include <qsnet/procfs_linux.h>
++
++#include <linux/smp.h>                /* for smp_call_function() prototype */
++#include <linux/smp_lock.h>
++#include <linux/mm.h>
++
++#include <linux/highmem.h>
++
++extern int mmx_disabled;
++extern int qsnet_debug_line_size;
++extern int qsnet_debug_num_lines;
++
++gid_t                qsnet_procfs_gid;
++struct proc_dir_entry *qsnet_procfs_root;
++struct proc_dir_entry *qsnet_procfs_config;
++
++MODULE_AUTHOR("Quadrics Ltd.");
++MODULE_DESCRIPTION("QsNet Kernel support code");
++
++MODULE_LICENSE("GPL");
++
++#if defined(LINUX_I386)
++MODULE_PARM(mmx_disabled, "i");
++#endif
++
++MODULE_PARM(qsnet_debug_line_size, "i");
++MODULE_PARM(qsnet_debug_num_lines, "i");
++
++MODULE_PARM(qsnet_procfs_gid, "i");
++
++#ifdef KMEM_DEBUG
++EXPORT_SYMBOL(qsnet_kmem_alloc_debug);
++EXPORT_SYMBOL(qsnet_kmem_free_debug);
++#else
++EXPORT_SYMBOL(qsnet_kmem_alloc);
++EXPORT_SYMBOL(qsnet_kmem_free);
++#endif
++
++EXPORT_SYMBOL(qsnet_kmem_display);
++EXPORT_SYMBOL(kmem_to_phys);
++
++EXPORT_SYMBOL(cpu_hold_all);
++EXPORT_SYMBOL(cpu_release_all);
++
++#if defined(LINUX_I386)
++EXPORT_SYMBOL(qsnet_readq);
++EXPORT_SYMBOL(qsnet_writeq);
++#endif
++
++/* debug.c */
++EXPORT_SYMBOL(qsnet_debugf);
++EXPORT_SYMBOL(kqsnet_debugf);
++EXPORT_SYMBOL(qsnet_vdebugf);
++EXPORT_SYMBOL(qsnet_debug_buffer);
++EXPORT_SYMBOL(qsnet_debug_alloc);
++EXPORT_SYMBOL(qsnet_debug_dump);
++EXPORT_SYMBOL(qsnet_debug_kmem);
++EXPORT_SYMBOL(qsnet_debug_disable);
++
++EXPORT_SYMBOL(qsnet_assfail);
++
++EXPORT_SYMBOL(qsnet_procfs_gid);
++EXPORT_SYMBOL(qsnet_procfs_root);
++
++static int qsnet_open    (struct inode *ino, struct file *fp);
++static int qsnet_release (struct inode *ino, struct file *fp);
++static int qsnet_ioctl   (struct inode *ino, struct file *fp, unsigned int cmd, unsigned long arg);
++
++static struct file_operations qsnet_ioctl_fops = 
++{
++      ioctl:   qsnet_ioctl,
++      open:    qsnet_open,
++      release: qsnet_release,
++};
++
++static int
++qsnet_open (struct inode *inode, struct file *fp)
++{
++      MOD_INC_USE_COUNT;
++      fp->private_data = NULL;
++      return (0);
++}
++
++static int
++qsnet_release (struct inode *inode, struct file *fp)
++{
++      MOD_DEC_USE_COUNT;
++      return (0);
++}
++
++static int 
++qsnet_ioctl(struct inode *inode, struct file *fp, unsigned int cmd, unsigned long arg)
++{
++      int res=0;
++
++      switch (cmd) 
++      {
++      case QSNETIO_DEBUG_KMEM:
++      {
++              QSNETIO_DEBUG_KMEM_STRUCT args;
++
++              if (copy_from_user (&args, (void *) arg, sizeof (QSNETIO_DEBUG_KMEM_STRUCT)))
++                      return (-EFAULT);
++
++              /* doesnt use handle as a pointer */
++              qsnet_kmem_display(args.handle);
++              break;
++      }
++
++      case QSNETIO_DEBUG_DUMP : 
++      {
++              res = qsnet_debug_dump();
++              break;
++      }
++
++      case QSNETIO_DEBUG_BUFFER :
++      {
++              QSNETIO_DEBUG_BUFFER_STRUCT args;
++
++              if (copy_from_user (&args, (void *) arg, sizeof (QSNETIO_DEBUG_BUFFER_STRUCT)))
++                      return (-EFAULT);
++
++              /* qsnet_debug_buffer uses copyout */
++              if ((res = qsnet_debug_buffer (args.addr, args.len)) != -1)
++              {
++                      args.len = res;
++                      if (copy_to_user ((void *) arg, &args, sizeof (QSNETIO_DEBUG_BUFFER_STRUCT)))
++                              return (-EFAULT);
++                      res = 0;
++              }
++              break;
++      }
++      default:
++              res = EINVAL;
++              break;
++      }
++
++      return ((res == 0) ? 0 : -res);
++}
++
++#ifdef KMEM_DEBUG
++static int qsnet_kmem_open    (struct inode *ino, struct file *fp);
++static int qsnet_kmem_release (struct inode *ino, struct file *fp);
++static ssize_t qsnet_kmem_read (struct file *file, char *buf, size_t count, loff_t *ppos);
++
++static struct file_operations qsnet_kmem_fops = 
++{
++      open:    qsnet_kmem_open,
++      release: qsnet_kmem_release,
++      read:    qsnet_kmem_read,
++};
++
++typedef struct qsnet_private_space
++{
++      char * space;
++      int    size;
++      struct qsnet_private_space *next;
++} QSNET_PRIVATE_SPACE;
++
++typedef struct qsnet_private  
++{
++      QSNET_PRIVATE_SPACE *space_chain;
++        QSNET_PRIVATE_SPACE *current_space;
++      int                  current_pos;
++
++} QSNET_PRIVATE;
++
++#define QSNET_KMEM_DEBUG_LINE_SIZE ((int)512)
++#define QSNET_PRIVATE_PAGE_SIZE    ((int)(4*1024))
++
++static int qsnet_kmem_fill(QSNET_PRIVATE *pd);
++
++void
++destroy_chain(QSNET_PRIVATE * pd)
++{
++      QSNET_PRIVATE_SPACE *mem, *next;
++      
++      if (pd == NULL) return;
++
++      for(mem = pd->space_chain ; mem != NULL; )
++      {
++              next = mem->next; 
++              if ( mem->space ) 
++                      kfree ( mem->space);
++              kfree(mem);
++              mem = next;
++      }
++      kfree (pd);
++}
++
++QSNET_PRIVATE *
++make_chain(int len)
++{
++      QSNET_PRIVATE       * pd;
++      QSNET_PRIVATE_SPACE * mem;
++      int                   i;
++
++      /* make the private data block */
++      if ((pd = kmalloc (sizeof (QSNET_PRIVATE), GFP_KERNEL)) == NULL)
++              return NULL;
++      pd->space_chain = NULL;
++
++      /* first make the holders */
++      for(i=0;i<len;i++)
++      {
++              if ((mem = kmalloc (sizeof (QSNET_PRIVATE_SPACE), GFP_KERNEL)) == NULL)
++              {
++                      destroy_chain(pd);
++                      return (NULL);
++              }
++              mem->next  = pd->space_chain;
++              mem->size  = 0;
++              mem->space = 0;
++              pd->space_chain = mem;
++
++              /* now add the space */
++              if ((mem->space = kmalloc (QSNET_PRIVATE_PAGE_SIZE, GFP_KERNEL)) == NULL)
++              {
++                      destroy_chain(pd);
++                      return (NULL);
++              }                       
++
++              mem->space[0] = 0;
++
++      }
++
++      pd->current_space = pd->space_chain;
++      pd->current_pos   = 0;
++
++      return pd;
++}
++
++static int
++qsnet_kmem_open (struct inode *inode, struct file *fp)
++{
++      MOD_INC_USE_COUNT;
++      fp->private_data = NULL;
++      return (0);
++}
++
++static int
++qsnet_kmem_release (struct inode *inode, struct file *fp)
++{
++      if ( fp->private_data )
++      {
++              QSNET_PRIVATE * pd = (QSNET_PRIVATE *) fp->private_data;
++
++              /* free the space */
++              if (pd->space_chain)
++                      kfree (pd->space_chain);        
++
++              /* free struct */
++              kfree (pd);
++      }
++      MOD_DEC_USE_COUNT;
++      return (0);
++}
++
++static ssize_t
++qsnet_kmem_read (struct file *file, char *buf, size_t count, loff_t *ppos)
++{
++      QSNET_PRIVATE * pd = (QSNET_PRIVATE *) file->private_data;
++      int             error;
++      int             output_count;
++      int             num_of_links=10;
++
++      /* make a buffer to output count bytes in */
++      if ((error = verify_area (VERIFY_WRITE, buf, count)) != 0)
++              return (error);
++
++      if ( pd == NULL) 
++      {
++              /* first time */
++
++              /* ok we have to guess at how much space we are going to need  */
++              /* if it fails we up the space and carry try again             */
++              /* we have to do it this way as we cant get more memory whilst */
++              /* holding the lock                                            */
++              if ((pd = make_chain(num_of_links)) == NULL)
++                      return (-ENOMEM);       
++
++              while ( qsnet_kmem_fill(pd) ) 
++              {
++                      destroy_chain(pd);
++                      num_of_links += 10;
++                      if ((pd = make_chain(num_of_links)) == NULL)
++                              return (-ENOMEM);       
++              }
++
++              /* we have the space and filled it */
++              file->private_data = (void *)pd;        
++      }
++              
++      /* output buffer */
++      if ( pd->current_pos >= pd->current_space->size )
++              return (0); /* finished */
++
++      output_count = pd->current_space->size - pd->current_pos;
++      if ( output_count > count ) 
++              output_count = count;
++
++      copy_to_user(buf, (pd->current_space->space + pd->current_pos), output_count);
++
++      pd->current_pos += output_count;
++      ppos            += output_count;
++
++      /* just check to see if we have finished the current space */
++      if ( pd->current_pos >= pd->current_space->size )
++      {
++              if ( pd->current_space->next )
++              {
++                      pd->current_space = pd->current_space->next;
++                      pd->current_pos   = 0;
++              }
++      }       
++
++      return (output_count);
++}
++#endif /* KMEM_DEBUG */
++
++static int
++proc_write_qsnetdebug(struct file *file, const char *buffer,
++                    unsigned long count, void *data)
++{
++      char    tmpbuf[128];
++      int     res;
++      
++      if (count > sizeof (tmpbuf)-1)
++              return (-EINVAL);
++      
++      MOD_INC_USE_COUNT;
++      
++      if (copy_from_user (tmpbuf, buffer, count))
++              res = -EFAULT;
++      else 
++      {
++              tmpbuf[count] = '\0';   
++              
++              if (tmpbuf[count-1] == '\n')
++                      tmpbuf[count-1] = '\0';
++              
++              if (! strcmp (tmpbuf, "on"))
++                      qsnet_debug_buffer_on();
++
++              if (! strcmp (tmpbuf, "clear"))
++                      qsnet_debug_buffer_clear();
++
++              if (! strncmp (tmpbuf, "mark",4))
++                      qsnet_debug_buffer_mark( &tmpbuf[4] );
++              
++              res = count;
++      }
++      
++      MOD_DEC_USE_COUNT;
++      
++      return (res);
++}
++
++static int
++proc_read_qsnetdebug(char *page, char **start, off_t off,
++                   int count, int *eof, void *data)
++{
++      int len = sprintf (page, "echo command > /proc/qsnet/config/qsnetdebug\ncommand = on | off | clear | mark text\n");
++      return (qsnet_proc_calc_metrics (page, start, off, count, eof, len));
++}
++
++#include "quadrics_version.h"
++extern int kqsnet_debug_running;
++static char       quadrics_version[] = QUADRICS_VERSION;
++
++static int __init qsnet_init(void)
++{
++      struct proc_dir_entry *p;
++
++      if ((qsnet_procfs_root = proc_mkdir ("qsnet", 0)) == NULL)
++      {
++              printk ("qsnet: failed to create /proc/qsnet \n");
++              return (-ENXIO);
++      }
++      
++      if ((p = create_proc_entry ("ioctl", S_IRUGO|S_IWUSR|S_IWGRP, qsnet_procfs_root)) == NULL)
++      {
++              printk ("qsnet: failed to register /proc/qsnet/ioctl\n");
++              return (-ENXIO);
++      }
++      p->proc_fops = &qsnet_ioctl_fops;
++      p->owner     = THIS_MODULE;
++      p->data      = NULL;
++      p->gid       = qsnet_procfs_gid;
++
++      qsnet_proc_register_str (qsnet_procfs_root, "version", quadrics_version, S_IRUGO);
++
++      if ((qsnet_procfs_config = proc_mkdir ("config", qsnet_procfs_root)) == NULL)
++      {
++              printk ("qsnet: failed to create /proc/qsnet/config \n");
++              return (-ENXIO);
++      }
++
++#ifdef KMEM_DEBUG
++      if ((p = create_proc_entry ("kmem_debug", S_IRUGO|S_IWUSR|S_IWGRP, qsnet_procfs_config)) == NULL)
++      {
++              printk ("qsnet: failed to register /proc/qsnet/config/kmem_debug\n");
++              return (-ENXIO);
++      }
++      p->proc_fops = &qsnet_kmem_fops;
++      p->owner     = THIS_MODULE;
++      p->data      = NULL;
++      p->gid       = qsnet_procfs_gid;
++#endif                
++      
++      qsnet_debug_init(); 
++
++      qsnet_proc_register_int (qsnet_procfs_config, "kqsnet_debug_running", &kqsnet_debug_running, 0);
++
++      if ((p = create_proc_entry ("qsnetdebug", S_IRUGO|S_IWUSR|S_IWGRP, qsnet_procfs_config)) == NULL)
++      {
++              printk ("qsnet: failed to register /proc/qsnet/config/qsnetdebug\n");
++              return (-ENXIO);
++      }
++      p->read_proc  = proc_read_qsnetdebug;
++      p->write_proc = proc_write_qsnetdebug;
++      p->owner      = THIS_MODULE;
++      p->data       = NULL;
++      p->gid        = qsnet_procfs_gid;
++      
++      return (0);
++}
++
++static void __exit qsnet_exit(void)
++{
++#ifdef KMEM_DEBUG
++      qsnet_kmem_display(0);
++#endif
++      qsnet_debug_fini();
++
++      remove_proc_entry ("qsnetdebug",           qsnet_procfs_config);
++      remove_proc_entry ("kqsnet_debug_running", qsnet_procfs_config);
++#ifdef KMEM_DEBUG
++      remove_proc_entry ("kmem_debug",           qsnet_procfs_config);
++#endif
++      remove_proc_entry ("config",               qsnet_procfs_root);
++
++      remove_proc_entry ("version", qsnet_procfs_root);
++      remove_proc_entry ("ioctl",   qsnet_procfs_root);
++
++      remove_proc_entry ("qsnet", 0);
++}
++
++/* Declare the module init and exit functions */
++module_init(qsnet_init);
++module_exit(qsnet_exit);
++
++#ifdef KMEM_DEBUG
++/*
++ * Kernel memory allocation.  We maintain our own list of allocated mem
++ * segments so we can free them on module cleanup.
++ * 
++ * We use kmalloc for allocations less than one page in size; vmalloc for
++ * larger sizes.
++ */
++
++typedef struct {
++      struct list_head list;
++      void            *ptr;
++      int             len;
++      int             used_vmalloc;
++      void            *owner;
++      void            *caller;
++      unsigned int     time;
++      int              line;
++      char             filename[20];
++} kmalloc_t;
++
++static LIST_HEAD(kmalloc_head);
++
++static spinlock_t     kmalloc_lock = SPIN_LOCK_UNLOCKED;
++
++/*
++ * Kernel memory allocation.  We use kmalloc for allocations less 
++ * than one page in size; vmalloc for larger sizes.
++ */
++
++static int
++qsnet_kmem_fill(QSNET_PRIVATE *pd)
++{
++      kmalloc_t *kp;
++      struct list_head *lp;
++      unsigned long flags;
++      char str[QSNET_KMEM_DEBUG_LINE_SIZE];
++      QSNET_PRIVATE_SPACE * current_space;
++      int                   current_pos;
++      int                   len;
++      current_space = pd->space_chain;
++      current_pos   = 0;
++      
++      
++      current_space->space[0] = 0;    
++      spin_lock_irqsave(&kmalloc_lock, flags);
++      for (lp = kmalloc_head.next; lp != &kmalloc_head;  lp = lp->next) {
++              kp = list_entry(lp, kmalloc_t, list);
++              
++              /* make the next line */
++              sprintf(str,"%p %d %d %p %p %u %d %s\n",
++                      kp->ptr, kp->len, kp->used_vmalloc, kp->caller, kp->owner, kp->time, kp->line, kp->filename);
++              len = strlen(str);
++              
++              /* does it fit on the current page */
++              if ( (current_pos + len + 1) >=  QSNET_PRIVATE_PAGE_SIZE)
++              {
++                      /* move onto next page */
++                      if ((current_space = current_space->next) == NULL)
++                      {
++                              /* run out of space !!!! */
++                              spin_unlock_irqrestore(&kmalloc_lock, flags);
++                              return (1);
++                      }
++                      current_space->space[0] = 0;    
++                      current_pos = 0;
++              }
++              strcat( current_space->space + current_pos, str);
++              current_pos += len;
++
++              /* remember how much we wrote to this page */
++              current_space->size = current_pos;
++
++      }
++      spin_unlock_irqrestore(&kmalloc_lock, flags);
++
++      return (0);
++}
++
++void * 
++qsnet_kmem_alloc_debug(int len, int cansleep, int zerofill, char *file, int line)
++{
++      void *new;
++      unsigned long flags;
++      kmalloc_t *kp;
++
++      if (len < PAGE_SIZE || !cansleep)
++              new = kmalloc(len, cansleep ? GFP_KERNEL : GFP_ATOMIC);
++      else
++              new = vmalloc(len);
++
++      if (len >= PAGE_SIZE)
++              ASSERT(PAGE_ALIGNED((uintptr_t) new));
++
++      if (new && zerofill)
++              memset(new,0,len);
++
++      /* record allocation */
++      kp = kmalloc(sizeof(kmalloc_t), cansleep ? GFP_KERNEL : GFP_ATOMIC);
++      ASSERT(kp != NULL);
++      kp->len = len;
++      kp->ptr = new;
++      kp->used_vmalloc = (len >= PAGE_SIZE || cansleep);
++      kp->owner  = current;
++      kp->caller = __builtin_return_address(0);
++      kp->time = lbolt;
++      kp->line = line;
++      len = strlen(file);
++
++      if (len > 18) 
++              strcpy(kp->filename,&file[len-18]);
++      else
++              strcpy(kp->filename,file);
++
++      spin_lock_irqsave(&kmalloc_lock, flags);
++      list_add(&kp->list, &kmalloc_head);
++      spin_unlock_irqrestore(&kmalloc_lock, flags);
++
++      return new;
++}
++
++void 
++qsnet_kmem_free_debug(void *ptr, int len, char *file, int line)
++{
++      unsigned long flags;
++      kmalloc_t *kp;
++      struct list_head *lp;
++
++      spin_lock_irqsave(&kmalloc_lock, flags);
++      for (lp = kmalloc_head.next; lp != &kmalloc_head; lp = lp->next) {
++              kp = list_entry(lp, kmalloc_t, list);
++              if (kp->ptr == ptr) {
++                      if (kp->len != len)
++                              printk("qsnet_kmem_free_debug(%p) ptr %p len %d mismatch: expected %d caller %p owner %p (%s:%d)\n",
++                                     current, ptr, len, kp->len, __builtin_return_address(0), kp->caller, file, line);
++                      list_del(lp);
++                      kfree(kp); /* free off descriptor */
++                      break;
++              }
++      }
++      spin_unlock_irqrestore(&kmalloc_lock, flags);
++
++      if (lp == &kmalloc_head) /* segment must be found */
++      {
++              printk( "qsnet_kmem_free_debug(%p) ptr %p len %d not found: caller %p (%s:%d)\n",
++                      current, ptr, len, __builtin_return_address(0), file, line);
++      }
++
++      if ((((unsigned long) ptr) >= VMALLOC_START && ((unsigned long) ptr) < VMALLOC_END)) 
++              vfree (ptr);
++      else
++              kfree (ptr);
++}
++
++#else /* !KMEM_DEBUG */
++
++void * 
++qsnet_kmem_alloc(int len, int cansleep, int zerofill)
++{
++      void *new;
++
++      if (len < PAGE_SIZE || !cansleep)
++              new = kmalloc(len, cansleep ? GFP_KERNEL : GFP_ATOMIC);
++      else
++              new = vmalloc(len);
++
++      if (len >= PAGE_SIZE)
++              ASSERT(PAGE_ALIGNED((unsigned long) new));
++
++      if (new && zerofill)
++              memset(new,0,len);
++
++      return new;
++}
++
++void 
++qsnet_kmem_free(void *ptr, int len)
++{
++      if ((((unsigned long) ptr) >= VMALLOC_START && ((unsigned long) ptr) < VMALLOC_END)) 
++              vfree (ptr);
++      else
++              kfree (ptr);
++}
++#endif /* !KMEM_DEBUG */
++
++void
++qsnet_kmem_display(void *handle)
++{
++#ifdef KMEM_DEBUG
++      kmalloc_t *kp;
++      struct list_head *lp;
++      unsigned long flags;
++      int count = 0, totsize = 0;
++
++      spin_lock_irqsave(&kmalloc_lock, flags);
++      for (lp = kmalloc_head.next; lp != &kmalloc_head;  lp = lp->next) {
++              kp = list_entry(lp, kmalloc_t, list);
++
++              if (!handle || handle == kp->owner)
++              {
++                      printk("qsnet_kmem_display(%p): mem %p len %d unfreed caller %p (%p) \n",
++                             handle, kp->ptr, kp->len, kp->caller, kp->owner);
++                  
++                      count++;
++                      totsize += kp->len;
++              }
++      }
++      spin_unlock_irqrestore(&kmalloc_lock, flags);
++
++      printk("qsnet_kmem_display(%p): %d bytes left in %d objects\n", handle, totsize, count);
++#endif
++}
++
++physaddr_t
++kmem_to_phys(void *ptr)
++{
++      virtaddr_t virt = (virtaddr_t) ptr;
++      physaddr_t phys;
++      pte_t     *pte;
++
++      if ((virt >= VMALLOC_START && virt < VMALLOC_END))
++      {
++              pte = find_pte_kernel(virt);
++              ASSERT(pte && !pte_none(*pte));
++              phys = pte_phys(*pte) + (virt & (PAGE_SIZE-1));
++      }
++#if defined(PKMAP_BASE)
++      else if (virt >= PKMAP_BASE && virt < (PKMAP_BASE + LAST_PKMAP * PAGE_SIZE))
++      {
++              pte = find_pte_kernel(virt);
++              ASSERT(pte && !pte_none(*pte));
++              phys = pte_phys(*pte) + (virt & (PAGE_SIZE-1));
++      }
++#endif
++#if defined(__ia64)
++      else if (virt >= __IA64_UNCACHED_OFFSET && virt < PAGE_OFFSET)
++      {
++              /* ia64 non-cached KSEG */
++              phys = ((physaddr_t) ptr - __IA64_UNCACHED_OFFSET);
++      }
++#endif
++      else /* otherwise it's KSEG */
++      {
++              phys = __pa(virt);
++      }
++          
++#if defined(CONFIG_ALPHA_GENERIC) || (defined(CONFIG_ALPHA_EV6) && !defined(USE_48_BIT_KSEG))
++      /* 
++       * with TS_BIAS as bit 40 - the tsunami pci space is mapped into
++       * the kernel at 0xfffff500.00000000 however we need to convert
++       * this to the true physical address 0x00000800.00000000.
++       *
++       * there is no need for PHYS_TWIDDLE since we knew we'd get a kernel
++       * virtual address already and handled this with __pa().
++       */
++      if (phys & (1ul << 40)) {
++              phys &= ~(1ul << 40);   /*   clear bit 40 (kseg I/O select) */
++              phys |= (1ul << 43);    /*   set   bit 43 (phys I/O select) */
++      }
++#endif
++      return phys;
++}
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
++
++EXPORT_SYMBOL(pci_resource_size);
++EXPORT_SYMBOL(pci_get_base_address);
++EXPORT_SYMBOL(pci_base_to_kseg);
++
++
++/*
++ * PCI stuff.  
++ *
++ * XXX pci_base_to_kseg() and pci_kseg_to_phys() are problematic
++ * in that they may not work on non-Tsunami (DS20, ES40, etc) 
++ * architectures, and may not work in non-zero PCI bus numbers.
++ */
++
++unsigned long 
++pci_get_base_address(struct pci_dev *pdev, int index)
++{
++      unsigned long base;
++
++      ASSERT(index >= 0 && index <= 5);
++      /* borrowed in part from drivers/scsi/sym53c8xx.c */
++      base = pdev->base_address[index++];
++
++#if BITS_PER_LONG > 32
++      if ((base & 0x7) == 0x4)
++              base |= (((unsigned long)pdev->base_address[index]) << 32);
++#endif
++      return base;
++}
++
++unsigned long 
++pci_resource_size(struct pci_dev *pdev, int index)
++{
++      u32 addr, mask, size;
++
++      static u32 bar_addr[] = {
++              PCI_BASE_ADDRESS_0, 
++              PCI_BASE_ADDRESS_1, 
++              PCI_BASE_ADDRESS_2,
++              PCI_BASE_ADDRESS_3, 
++              PCI_BASE_ADDRESS_4, 
++              PCI_BASE_ADDRESS_5, 
++      };
++      ASSERT(index >= 0 && index <= 5);
++
++      /* algorithm from Rubini book */
++      pci_read_config_dword (pdev,    bar_addr[index], &addr);
++      pci_write_config_dword(pdev,    bar_addr[index], ~0);
++      pci_read_config_dword (pdev,    bar_addr[index], &mask);
++      pci_write_config_dword(pdev,    bar_addr[index], addr);
++
++      mask &= PCI_BASE_ADDRESS_MEM_MASK;
++      size = ~mask + 1;
++      return size;
++}
++
++/*
++ * Convert BAR register value to KSEG address.
++ */
++void *
++pci_base_to_kseg(u64 baddr, int bus)
++{
++      u64 kseg;
++
++      /* XXX tsunami specific */
++      baddr &= ~(u64)0x100000000;  /* mask out hose bit */
++      kseg = TSUNAMI_MEM(bus) + baddr;
++      return (void *)kseg; 
++}
++
++#endif /* LINUX_VERSION_CODE <= KERNEL_VERSION(2,4,0) */
++
++/*
++ * Spin the other CPU's in an SMP system.
++ * smp_call_function() needed to be exported to modules.  It will be
++ * papered over in <linux/smp.h> if running on a non-SMP box.
++ */
++static spinlock_t hold_lock = SPIN_LOCK_UNLOCKED;
++
++#if 0
++static void cpu_hold(void *unused)
++{
++      spin_lock(&hold_lock);
++      spin_unlock(&hold_lock);
++}
++#endif
++
++void cpu_hold_all(void)
++{
++      spin_lock(&hold_lock);
++
++#if 0
++      {
++              int res;
++              int retries = 10; 
++          
++              /* XXXXX: cannot call smp_call_function() from interrupt context */
++          
++              do {
++                      /* only request blocking retry if not in interrupt context */
++                      res = smp_call_function(cpu_hold, NULL, !in_interrupt(), 0);
++                      if (res)
++                              mdelay(5);
++              } while (res && retries--);
++          
++              if (res)
++                      printk("cpu_hold_all: IPI timeout\n");
++      }
++#endif
++}
++
++void cpu_release_all(void)
++{
++      spin_unlock(&hold_lock);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/qsnet/Makefile
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/qsnet/Makefile 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/qsnet/Makefile      2005-06-01 23:12:54.697424208 -0400
+@@ -0,0 +1,31 @@
++#
++# Makefile for Quadrics QsNet
++#
++# Copyright (c) 2002-2004 Quadrics Ltd
++#
++# File: drivers/net/qsnet/qsnet/Makefile
++#
++
++
++#
++
++#
++# Makefile for Quadrics QsNet
++#
++# Copyright (c) 2004 Quadrics Ltd.
++#
++# File: driver/net/qsnet/qsnet/Makefile
++#
++
++list-multi            := qsnet.o
++qsnet-objs    := debug.o kernel_linux.o i686_mmx.o
++export-objs           := kernel_linux.o
++obj-$(CONFIG_QSNET)   := qsnet.o
++
++qsnet.o : $(qsnet-objs)
++      $(LD) -r -o $@ $(qsnet-objs)
++
++EXTRA_CFLAGS          +=  -DDEBUG -DDEBUG_PRINTF -DDEBUG_ASSERT
++
++include $(TOPDIR)/Rules.make
++
+Index: linux-2.4.21/drivers/net/qsnet/qsnet/Makefile.conf
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/qsnet/Makefile.conf    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/qsnet/Makefile.conf 2005-06-01 23:12:54.698424056 -0400
+@@ -0,0 +1,10 @@
++# Flags for generating QsNet Linux Kernel Makefiles
++MODNAME               =       qsnet.o
++MODULENAME    =       qsnet
++KOBJFILES     =       debug.o kernel_linux.o i686_mmx.o
++EXPORT_KOBJS  =       kernel_linux.o
++CONFIG_NAME   =       CONFIG_QSNET
++SGALFC                =       
++# EXTRALINES START
++
++# EXTRALINES END
+Index: linux-2.4.21/drivers/net/qsnet/qsnet/qsnetkmem_linux.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/qsnet/qsnetkmem_linux.c        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/qsnet/qsnetkmem_linux.c     2005-06-01 23:12:54.698424056 -0400
+@@ -0,0 +1,325 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: qsnetkmem_linux.c,v 1.3 2003/08/13 10:03:27 fabien Exp $"
++/*      $Source: /cvs/master/quadrics/qsnet/qsnetkmem_linux.c,v $*/
++
++/* macro macros */
++#define MACRO_BEGIN     do {
++#define MACRO_END       } while (0)
++#define offsetof(T,F) ((int )&(((T *)0)->F))
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <ctype.h>
++#include <sys/types.h>
++#include <errno.h>
++#include <unistd.h>
++#include <string.h>
++#include <qsnet/config.h>
++#include <qsnet/list.h>
++#include <qsnet/procfs_linux.h>
++#include <signal.h>
++#include <sys/wait.h>
++
++#define LIST_HEAD_INIT(name) { &(name), &(name) }
++
++#define LIST_HEAD(name) \
++      struct list_head name = LIST_HEAD_INIT(name)
++
++typedef struct {
++      struct list_head list;
++      void            *ptr;
++      int             len;
++      int             used_vmalloc;
++      void            *owner;
++      void            *caller;
++      unsigned int     time;
++      int              mark;
++      int              line;
++      char             file[256];
++      
++} kmalloc_t;
++
++
++static LIST_HEAD(current_kmem);
++static LIST_HEAD(stored_kmem);
++
++void
++count_kmem(struct list_head * list, long * count, long * size )
++{
++      long              c,s;
++      struct list_head *tmp;
++      kmalloc_t        *kmem_ptr = NULL;
++
++
++      c = s = 0L;
++
++      list_for_each(tmp, list) {
++              kmem_ptr = list_entry(tmp, kmalloc_t , list);
++              c++;
++              s += kmem_ptr->len;
++      }       
++
++      *count = c;
++      *size  = s;
++}
++
++void
++clear_kmem(struct list_head * list)
++{
++      struct list_head *tmp,*tmp2;
++      kmalloc_t        *kmem_ptr = NULL;
++
++      list_for_each_safe(tmp, tmp2, list) {
++              kmem_ptr = list_entry(tmp, kmalloc_t , list);
++              list_del_init(&kmem_ptr->list);
++              free( kmem_ptr );
++      }
++}
++
++void 
++move_kmem(struct list_head * dest, struct list_head *src)
++{
++      struct list_head *tmp,*tmp2;
++      kmalloc_t        *kp= NULL;
++
++      list_for_each_safe(tmp, tmp2, src) {
++              kp = list_entry(tmp, kmalloc_t , list);
++              list_del_init(&kp->list);
++
++/*
++              printf("mem %p len %d (vm=%d)  caller %p owner %p (%s:%d)\n",
++                     kp->ptr, kp->len, kp->used_vmalloc, kp->caller, kp->owner, kp->file, kp->line);
++*/                
++
++              list_add_tail(&kp->list, dest);
++      }
++}
++
++void
++read_kmem(struct list_head * list)
++{
++      FILE      * fd;
++      char        line[1024];
++      int         line_size = 100;
++      char      * rep;
++      kmalloc_t * kp;
++
++      clear_kmem(list);
++
++      fd = fopen(QSNET_PROCFS_KMEM_DEBUG,"r");
++      if ( fd == NULL) 
++      {
++              printf("No Kmem Debug\n");
++              return;
++      }
++
++      rep = fgets(line,line_size, fd);
++
++      while ( rep != NULL ) 
++      {
++              kp = malloc(sizeof(kmalloc_t));
++
++              sscanf(line,"%p %d %d %p %p %u %d %s\n",
++                     &kp->ptr, &kp->len, &kp->used_vmalloc, &kp->caller, &kp->owner, &kp->time, &kp->line, &kp->file[0]);
++
++/*
++              printf(">>%s<<\n",line);
++              printf("%p %d %d %p %p %u %d %s\n",
++                     kp->ptr, kp->len, kp->used_vmalloc, kp->caller, kp->owner, kp->time, kp->line, kp->file);
++*/
++
++              list_add_tail(&kp->list, list);
++
++              rep = fgets(line,line_size, fd);
++      }
++      fclose(fd);
++}
++
++void
++mark_kmem(struct list_head * list, int mark)
++{
++      struct list_head *tmp;
++      kmalloc_t        *kp = NULL;
++
++      list_for_each(tmp, list) {
++              kp = list_entry(tmp, kmalloc_t , list);
++
++              kp->mark = mark;
++      }
++}
++
++kmalloc_t *
++find_kmem(kmalloc_t * value, struct list_head * list)
++{
++      struct list_head *tmp;
++      kmalloc_t        *kp = NULL;
++
++      
++      list_for_each(tmp, list) {
++              kp = list_entry(tmp, kmalloc_t , list);
++              if ( (kp->ptr == value->ptr)
++                   && (kp->len == value->len)
++                   && (kp->used_vmalloc  == value->used_vmalloc )
++                   && (kp->owner  == value->owner )
++                   && (kp->caller  == value->caller )
++                   && (kp->time  == value->time )
++                   && (kp->line  == value->line )
++                   && !(strcmp(kp->file,value->file) ))
++                      return kp;
++      }       
++      return NULL;
++}
++
++void 
++diff_kmem(struct list_head *curr, struct list_head *stored)
++{
++      struct list_head *tmp;
++      kmalloc_t        *kp = NULL;
++      long              c,s;
++
++      mark_kmem(stored,  0);
++      mark_kmem(curr,    0);
++      
++      list_for_each(tmp, stored) {
++              kp = list_entry(tmp, kmalloc_t , list);
++              if (find_kmem( kp, curr) != NULL) 
++                      kp->mark = 1;
++      }
++      
++      list_for_each(tmp, curr) {
++              kp = list_entry(tmp, kmalloc_t , list);
++              if (find_kmem( kp, stored) != NULL) 
++                      kp->mark = 1;
++      }               
++
++      c=s=0L;
++      list_for_each(tmp, stored) {
++              kp = list_entry(tmp, kmalloc_t , list);
++              if (kp->mark != 1)
++              {
++                      printf("-- mem %p len %d (vm=%d)  caller %p owner %p (%s:%d)\n",
++                             kp->ptr, kp->len, kp->used_vmalloc, kp->caller, kp->owner, kp->file, kp->line);
++                      c++;
++                      s+= kp->len;
++              }
++      }
++      printf("-- %4ld %10ld \n",c,s);
++      
++      c=s=0L;
++      list_for_each(tmp, curr) {
++              kp = list_entry(tmp, kmalloc_t , list);
++              if (kp->mark != 1)
++              {
++                      printf("++ mem %p len %d (vm=%d)  caller %p owner %p (%s:%d)\n",
++                             kp->ptr, kp->len, kp->used_vmalloc, kp->caller, kp->owner, kp->file, kp->line);
++                      c++;
++                      s+= kp->len;
++              }
++      }               
++      printf("++ %4ld %10ld \n",c,s);
++}
++
++
++void
++print_kmem(struct list_head * list)
++{
++      struct list_head *tmp;
++      kmalloc_t        *kp = NULL;
++
++      list_for_each(tmp, list) {
++              kp = list_entry(tmp, kmalloc_t , list);
++
++              printf("mem %p len %d (vm=%d)  caller %p owner %p (%s:%d)\n",
++                     kp->ptr, kp->len, kp->used_vmalloc, kp->caller, kp->owner, kp->file, kp->line);
++                  
++      }
++}
++
++void 
++print_cmds()
++{
++      long c,s;
++
++      printf("q : quits \n");
++      printf("r : read\n");
++      printf("c : print current\n");
++      printf("o : print stored\n");
++      printf("s : store\n");
++
++      count_kmem(&current_kmem, &c, &s );
++      printf("\ncurrent : %4ld %10ld\n", c , s);
++ 
++      count_kmem(&stored_kmem, &c, &s );
++      printf("store   : %4ld %10ld\n", c , s);
++ 
++}
++
++int
++main()
++{
++      char            line[128];
++      int             line_size=127;
++      int             len;
++
++
++      while (1)
++      {
++              
++              printf(">> ");
++              fgets(line,line_size, stdin);
++      
++              
++              len = strlen( line ) -1;
++              if ( len ) 
++              {
++                      switch ( tolower(line[0]) ) 
++                      {
++                      case 'q':
++                              exit(0);
++
++                      case 'r' :
++                              read_kmem(&current_kmem);
++                              break;
++
++                      case 'c' :
++                              print_kmem(&current_kmem);
++                              break;
++
++                      case 'o' :
++                              print_kmem(&stored_kmem);
++                              break;
++
++                      case 's' :
++                              clear_kmem(&stored_kmem);
++                              move_kmem(&stored_kmem, &current_kmem);
++                              break;
++
++                      case 'd' :
++                              diff_kmem(&current_kmem, &stored_kmem);
++                              break;
++
++                      default:
++                              print_cmds();   
++                      }
++
++              
++                      
++              }
++              else
++                      print_cmds();
++      }
++
++}
++
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.21/drivers/net/qsnet/qsnet/quadrics_version.h
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/qsnet/quadrics_version.h       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/qsnet/quadrics_version.h    2005-06-01 23:12:54.699423904 -0400
+@@ -0,0 +1 @@
++#define QUADRICS_VERSION "4.30qsnet"
+Index: linux-2.4.21/drivers/net/qsnet/rms/Makefile
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/rms/Makefile   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/rms/Makefile        2005-06-01 23:12:54.699423904 -0400
+@@ -0,0 +1,31 @@
++#
++# Makefile for Quadrics QsNet
++#
++# Copyright (c) 2002-2004 Quadrics Ltd
++#
++# File: drivers/net/qsnet/rms/Makefile
++#
++
++
++#
++
++#
++# Makefile for Quadrics QsNet
++#
++# Copyright (c) 2004 Quadrics Ltd.
++#
++# File: driver/net/qsnet/rms/Makefile
++#
++
++list-multi            := rms.o
++rms-objs      := rms_kern.o rms_kern_Linux.o
++export-objs           := 
++obj-$(CONFIG_RMS)     := rms.o
++
++rms.o : $(rms-objs)
++      $(LD) -r -o $@ $(rms-objs)
++
++EXTRA_CFLAGS          +=  -DDEBUG -DDEBUG_PRINTF -DDEBUG_ASSERT
++
++include $(TOPDIR)/Rules.make
++
+Index: linux-2.4.21/drivers/net/qsnet/rms/Makefile.conf
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/rms/Makefile.conf      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/rms/Makefile.conf   2005-06-01 23:12:54.699423904 -0400
+@@ -0,0 +1,10 @@
++# Flags for generating QsNet Linux Kernel Makefiles
++MODNAME               =       rms.o
++MODULENAME    =       rms
++KOBJFILES     =       rms_kern.o rms_kern_Linux.o
++EXPORT_KOBJS  =       
++CONFIG_NAME   =       CONFIG_RMS
++SGALFC                =       
++# EXTRALINES START
++
++# EXTRALINES END
+Index: linux-2.4.21/drivers/net/qsnet/rms/quadrics_version.h
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/rms/quadrics_version.h 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/rms/quadrics_version.h      2005-06-01 23:12:54.700423752 -0400
+@@ -0,0 +1 @@
++#define QUADRICS_VERSION "4.30qsnet"
+Index: linux-2.4.21/drivers/net/qsnet/rms/rms_kern.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/rms/rms_kern.c 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/rms/rms_kern.c      2005-06-01 23:12:54.702423448 -0400
+@@ -0,0 +1,1757 @@
++/*
++ * Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ * For licensing information please see the supplied COPYING file
++ *
++ * rms_kern.c:    RMS kernel module
++ *
++ * $Source: /cvs/master/quadrics/rmsmod/rms_kern.c,v $
++ */
++
++#ident "@(#)$Id: rms_kern.c,v 1.62.2.4 2005/01/18 11:05:45 duncan Exp $"
++
++#include <stddef.h>
++#include <qsnet/kernel.h>
++#include <qsnet/autoconf.h>
++#include <rms/rmscall.h>
++
++/*
++ * extend stats added in version 5
++ */
++#define RMS_MODVERSION 5
++
++#if defined(SOLARIS)
++
++#define CURUID() CURPROC()->p_cred->cr_uid
++#define RMS_NCPUS() 4
++#define PROC_STRUCT proc
++
++#include <sys/time.h>
++
++#elif defined(LINUX)
++
++#ifdef PROCESS_ACCT 
++#define TIMEVAL_TO_MSEC(tv) ((tv)->tv_sec * 1000 + (tv)->tv_usec / 1000)
++#define TIMEVAL_TO_CT(tv)   ((tv)->tv_sec * HZ + (tv)->tv_usec / (1000000L / HZ))
++#endif
++
++#ifdef RSS_ATOMIC
++#define PROC_RSS(proc)        ((proc)->mm ? atomic_read(&(proc)->mm->rss) : 0)
++#else
++#define PROC_RSS(proc)        ((proc)->mm ? (proc)->mm->rss : 0)
++#endif
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
++#     define  RMS_NCPUS()     smp_num_cpus
++#else
++#     define RMS_NCPUS()      num_online_cpus()
++#endif
++
++#define CURUID()      CURPROC()->uid
++#define p_pid         pid
++#define PROC_STRUCT   task_struct
++
++/* care needed with conversion to millisecs on 32-bit Linux */
++#ifdef LINUX
++#ifdef LINUX_I386
++#define CT_TO_MSEC(x) ct_to_msec(x)
++
++uint64_t ct_to_msec(clock_t t)
++{
++    uint64_t msecs;
++    if (t < 2000000)
++    {
++      t = (1000 * t)/HZ;
++      msecs = t;
++    }
++    else
++    {
++      t = t / HZ;
++      msecs = t * 1000;
++    }
++    return(msecs);
++}
++
++#else
++#define CT_TO_MSEC(x) (((x) * 1000)/HZ)
++#endif
++#endif
++
++#ifndef FALSE
++#define FALSE         (0)
++#define TRUE          (!FALSE)
++#endif
++
++#include <linux/time.h>
++#include <linux/proc_fs.h>
++#include <linux/ptrack.h>
++
++#include <linux/module.h>
++
++#elif defined(DIGITAL_UNIX)
++
++#define CURUID() CURPROC()->p_ruid
++extern  int ncpus;
++#define RMS_NCPUS() ncpus
++#define PROC_STRUCT proc
++#define TIMEVAL_TO_MSEC(tv) ((tv)->tv_sec * 1000 + (tv)->tv_usec / 1000)
++
++#include <sys/time.h>
++
++#else
++#error cannot determine operating system
++#endif
++
++int shm_cleanup(void);
++
++struct cap_desc {
++
++    struct cap_desc *next;
++    int              index;   /* index of capability in program */
++    ELAN_CAPABILITY  cap;     /* elan capability */
++
++};
++
++struct proc_desc {
++    
++    struct proc_desc    *next;
++    struct PROC_STRUCT  *proc;
++    struct prg_desc     *program;     /* controlling program         */
++    int                  mycap;               /* index of my capability      */
++    int                  myctx;               /* context number for process  */
++    int                  flags;
++    int                  vp;          /* elan virtual process number */
++};
++
++struct prg_desc {
++    
++    struct prg_desc  *next;           
++    int               id;     /* program id                          */
++    int               flags;  /* program status flags                */
++    uid_t             uid;    /* user id                             */
++    int               ncpus;  /* number of cpus allocated to program */
++    int               nprocs; /* number of processes in program      */
++    struct proc_desc *pdescs; /* processes in this program           */
++    int               ncaps;  /* number of capabilities              */
++    struct cap_desc  *caps;   /* elan capabilities                   */
++    char             *corepath;       /* core path for parallel program      */
++    int               psid;   /* processor set id                    */
++
++    uint64_t       cutime;    /* user time accumulated by children   */
++    uint64_t       cstime;    /* system time accumulated by children */
++    uint64_t       start_time;        /* time program created                */
++    uint64_t       end_time;  /* time last process exited            */
++    uint64_t       sched_time;        /* last time job was scheduled         */
++    uint64_t       accum_atime;       /* allocated time last deschedule      */
++    uint64_t       memint;    /* accumulated memory integral         */
++    uint64_t       ebytes;    /* data transferred by the Elan(s)     */
++    uint64_t       exfers;    /* number of Elan data transfers       */
++    long           maxrss;    /* maximum size to date                */
++    long           majflt;
++    
++#ifdef LINUX
++    struct proc_dir_entry *proc_entry;
++#endif
++
++};
++
++#if defined(LINUX)
++static int rms_ptrack_callback (void *arg, int phase, struct task_struct *child);
++#else
++static void rms_xd_callback(void *arg, int phase, void *ctask);
++static void rms_xa_callback (void *arg, int phase, void *ctask);
++#endif
++
++static void prgsignal(struct prg_desc *program, int signo);
++static uint64_t gettime(void);
++static void freeProgram(struct prg_desc *program);
++
++static struct prg_desc *programs = 0;
++
++kmutex_t rms_lock;
++
++int rms_init(void)
++{
++    kmutex_init (&rms_lock);
++
++    DBG(printk("rms: initialising\n"));
++
++    return(ESUCCESS);
++}
++
++int rms_reconfigure(void)
++{
++    return(ESUCCESS);
++}
++
++int rms_programs_registered(void)
++{
++    /*
++    ** Called when trying to unload rms.mod will not succeed
++    ** if programs registered
++    */
++ 
++   struct prg_desc *program, **pp;
++
++   kmutex_lock(&rms_lock);
++
++   for (program = programs; program; program = program->next)
++   {
++      if (program->nprocs != 0)
++      {
++            kmutex_unlock(&rms_lock);
++            return(EBUSY);
++      }
++   }
++
++   /*
++   ** We have traversed the programs list and no processes registered
++   ** Now free the memory
++   */
++      
++    pp = &programs;
++    while ((program = *pp) != NULL)
++    {
++        *pp = program->next;
++        freeProgram(program);
++    }
++    kmutex_unlock(&rms_lock);
++   
++    return(ESUCCESS);
++
++}
++
++int rms_fini(void)
++{
++    /*
++     * don't allow an unload if there are programs registered
++     */
++    if (rms_programs_registered())
++        return(EBUSY);
++
++    kmutex_destroy (&rms_lock);
++
++    DBG(printk("rms: removed\n"));
++
++    return(ESUCCESS);
++}
++
++#ifdef LINUX
++
++extern struct proc_dir_entry *rms_procfs_programs;
++
++/*
++ * display one pid per line if there isn't enough space 
++ * for another pid then add "...\n" and stop 
++ */
++int pids_callback(char* page, char** start, off_t off, int count, int* eof, void* data)
++{
++    struct prg_desc *program = (struct prg_desc *)data;
++    struct proc_desc *pdesc;
++    char *ptr = page;
++    int bytes = 0, nb;
++
++    kmutex_lock(&rms_lock);
++    
++    for (pdesc = program->pdescs; pdesc; pdesc = pdesc->next)
++    {
++      if (bytes > count - 15)
++      {
++          bytes += sprintf(ptr,"...\n");
++          break;
++      }
++        nb = sprintf(ptr, "%d %d\n", pdesc->proc->p_pid, pdesc->vp);
++      bytes += nb;
++      ptr += nb;
++    }
++    kmutex_unlock(&rms_lock);
++    
++    return(bytes);
++}
++
++int status_callback(char* page, char** start, off_t off, int count, int* eof, void* data)
++{
++    struct prg_desc *program = (struct prg_desc *)data;
++    int bytes;
++    if (program->flags & PRG_KILLED)
++      bytes = sprintf(page, "killed\n");
++    else
++      bytes = sprintf(page, "running\n");
++    return(bytes);
++}
++
++void rms_create_proc_entry(struct prg_desc *program)
++{
++    struct proc_dir_entry *p;
++    char name[32];
++
++    if (rms_procfs_programs)
++    {
++      sprintf(name,"%d", program->id);
++      if ((program->proc_entry = proc_mkdir(name, rms_procfs_programs)) != NULL)
++      {
++          if ((p = create_proc_entry ("pids", S_IRUGO, program->proc_entry)) != NULL)
++          {
++              p->owner = THIS_MODULE;
++              p->data = program;
++              p->read_proc = pids_callback;
++          }
++          if ((p = create_proc_entry ("status", S_IRUGO, program->proc_entry)) != NULL)
++          {
++              p->owner = THIS_MODULE;
++              p->data = program;
++              p->read_proc = status_callback;
++          }
++      }
++    }
++}
++
++void rms_remove_proc_entry(struct prg_desc *program)
++{
++    char name[32];
++    if (rms_procfs_programs)
++    {
++      if (program->proc_entry)
++      {
++          remove_proc_entry ("pids", program->proc_entry);
++          remove_proc_entry ("status", program->proc_entry);
++      }
++      sprintf(name,"%d", program->id);
++      remove_proc_entry (name, rms_procfs_programs);
++    }
++}
++
++#endif
++
++/*
++ * find a program from its index/pid
++ *
++ * Duncan:  make the lookup more efficient for large numbers of programs/processes
++ */
++static struct prg_desc *findProgram(const int id)
++{
++    struct prg_desc *program;
++    for (program = programs; program; program = program->next)
++      if (program->id == id)
++          return(program);
++    return(0);
++}
++
++static struct proc_desc *findProcess(const int pid)
++{
++    struct prg_desc *program;
++    struct proc_desc *pdesc;
++    for (program = programs; program; program = program->next)
++      for (pdesc = program->pdescs; pdesc; pdesc = pdesc->next)
++          if (pdesc->proc->p_pid == pid)
++              return(pdesc);
++    return(0);
++}
++
++static void freeProgram(struct prg_desc *program)
++{
++    struct proc_desc *pdesc;
++    struct cap_desc *cdesc;
++
++#ifdef LINUX
++    rms_remove_proc_entry(program);
++#endif
++
++    while ((pdesc = program->pdescs) != NULL)
++    {
++      program->pdescs = pdesc->next;
++      KMEM_FREE(pdesc, sizeof(struct proc_desc));
++    }
++
++    while ((cdesc = program->caps) != NULL)
++    {
++      program->caps = cdesc->next;
++      KMEM_FREE(cdesc, sizeof(struct cap_desc));
++    }
++
++    if (program->corepath)
++      KMEM_FREE(program->corepath, MAXCOREPATHLEN + 1);
++
++    KMEM_FREE(program, sizeof(struct prg_desc));
++
++#ifdef LINUX
++    MOD_DEC_USE_COUNT;
++#endif
++}
++
++/*
++ * rms_prgcreate
++ *
++ * create a new program description
++ */
++int rms_prgcreate(int id, uid_t uid, int cpus)
++{
++    struct prg_desc *program;
++    struct proc_desc *pdesc;
++    
++    DBG(printk("rms_prgcreate :: program %d pid %d uid %d cpus %d\n", id, CURPROC()->p_pid, uid, cpus));
++    
++    /*
++     * parallel programs are created as root by the rmsd as it forks the loader
++     */
++    if (CURUID())
++      return(EACCES);
++    
++    /*
++     * program ids must be unique
++     */
++    kmutex_lock(&rms_lock);
++    program = findProgram(id);
++    kmutex_unlock(&rms_lock);
++    if (program)
++      return(EINVAL);
++
++    /*
++     * create a new program description
++     */
++    KMEM_ALLOC(program, struct prg_desc *, sizeof(struct prg_desc), TRUE);
++    if (!program)
++      return(ENOMEM);
++
++    program->id = id;
++    program->flags = PRG_RUNNING;
++    program->ncpus = cpus;
++    program->nprocs = 1;
++    program->uid = uid;
++    program->ncaps = 0;
++    program->caps = 0;
++    program->corepath = 0;
++    program->psid = 0;
++    program->start_time = program->sched_time = gettime();
++    program->end_time = 0;
++    program->accum_atime = 0;
++    program->cutime = 0;
++    program->cstime = 0;
++    program->maxrss = 0;
++    program->memint = 0;
++    program->majflt = 0;
++    program->ebytes = 0;
++    program->exfers = 0;
++
++    KMEM_ALLOC(pdesc, struct proc_desc *, sizeof(struct proc_desc), TRUE);
++    if (!pdesc)
++      return(ENOMEM);
++
++    pdesc->proc = CURPROC();
++    pdesc->next = 0;
++    pdesc->mycap = ELAN_CAP_UNINITIALISED;
++    pdesc->myctx = ELAN_CAP_UNINITIALISED;
++    pdesc->vp = -1;           /* rmsloader */
++    pdesc->program = program;
++    program->pdescs = pdesc;
++    
++#ifdef LINUX
++    rms_create_proc_entry(program);
++#endif
++    
++    kmutex_lock(&rms_lock);
++
++#if defined(LINUX)
++    if (ptrack_register (rms_ptrack_callback, NULL) != 0)
++    {
++      kmutex_unlock(&rms_lock);
++        KMEM_FREE(pdesc,sizeof(struct proc_desc));
++        KMEM_FREE(program,sizeof(struct prg_desc));
++      return(ENOMEM);
++    }
++#else
++    /*
++     * install a fork handler
++     */
++    if (HANDLER_REGISTER((void *)(unsigned long)rms_xa_callback, NULL, XA_FORK | XA_EXIT | XA_IOF | XA_KOF | XA_KOE) == NULL)
++    {
++      kmutex_unlock(&rms_lock);
++        KMEM_FREE(pdesc,sizeof(struct proc_desc));
++        KMEM_FREE(program,sizeof(struct prg_desc));
++      return(ENOMEM);
++    }
++#endif
++
++    program->next = programs;
++    programs = program;
++    
++#ifdef LINUX
++    MOD_INC_USE_COUNT;
++#endif
++    
++    kmutex_unlock(&rms_lock);
++    return(ESUCCESS);
++}
++
++
++/*
++ * rms_prgdestroy
++ *
++ * destroy a program description
++ */
++int rms_prgdestroy(int id)
++{
++    struct prg_desc *program, **pp;
++    int status = ESRCH;
++
++    /*
++     * parallel programs are created and destroyed by the rmsd
++     */
++    if (CURUID())
++      return(EACCES);
++
++    kmutex_lock(&rms_lock);
++    
++    pp = &programs;
++    while ((program = *pp) != NULL)
++    {
++      if (program->id == id)
++      {
++          if (program->nprocs == 0)
++          {
++              DBG(printk("rms_prgdestro :: removing program %d\n", program->id));
++              *pp = program->next;
++              freeProgram(program);
++              status = ESUCCESS;
++          }
++          else
++          {
++              DBG(printk("rms_prgdestro :: failed to remove program %d: %d\n", program->id, program->nprocs));
++              status = ECHILD;
++              pp = &program->next;
++          }
++      }
++      else
++          pp = &program->next;
++    }
++    
++    kmutex_unlock(&rms_lock);
++    return(status);
++}
++
++/*
++ * rms_prgids
++ */
++int rms_prgids(int maxids, int *prgids, int *nprgs)
++{
++    struct prg_desc *program;
++    int count = 0, *buf, *bufp;
++    int status = ESUCCESS;
++
++    if (maxids < 1)
++        return(EINVAL);
++
++    kmutex_lock(&rms_lock);
++
++    for (program = programs; program; program = program->next)
++        count++;
++    count = MIN(count, maxids);
++
++    if (count > 0)
++    {
++        KMEM_ALLOC(buf, int *, count * sizeof(int), TRUE);
++      if (buf)
++      {                  
++          for (program = programs, bufp=buf; bufp < buf + count; 
++               program = program->next)
++              *bufp++ = program->id;
++      
++          if (copyout(buf, prgids, sizeof(int) * count))
++              status = EFAULT;
++
++          KMEM_FREE(buf, count * sizeof(int));
++      }
++      else
++          status = ENOMEM;
++    }
++    
++    if (copyout(&count, nprgs, sizeof(int)))
++      status = EFAULT;
++
++    kmutex_unlock(&rms_lock);
++    
++    return(status);
++}
++
++/*
++ * rms_prginfo
++ */
++int rms_prginfo(int id, int maxpids, pid_t *pids, int *nprocs)
++{
++    struct prg_desc *program;
++    struct proc_desc *pdesc;
++    pid_t *pidp, *buf;
++    int status = ESUCCESS;
++
++    kmutex_lock(&rms_lock);
++
++    if ((program = findProgram(id)) != NULL)
++    {
++      if (program->nprocs > 0)
++      {
++          KMEM_ALLOC(buf, pid_t *, program->nprocs * sizeof(pid_t), TRUE);
++          if (buf)
++          {
++              for (pidp = buf, pdesc = program->pdescs; pdesc; pdesc = pdesc->next)
++                  *pidp++ = pdesc->proc->p_pid;
++              
++              if (copyout(buf, pids, sizeof(pid_t) * MIN(program->nprocs, maxpids)))
++                  status = EFAULT;
++              
++              KMEM_FREE(buf, program->nprocs * sizeof(pid_t));
++          }
++          else
++              status = ENOMEM;
++      }
++      
++      if (copyout(&program->nprocs, nprocs, sizeof(int)))
++          status = EFAULT;
++    }
++    else
++      status = ESRCH;
++
++    kmutex_unlock(&rms_lock);
++    
++    return(status);
++}
++
++/*
++ * rmsmod always used to use psignal but this doesn't work
++ * on Linux 2.6.7 so we have changed to kill_proc
++ */
++static void prgsignal(struct prg_desc *program, int signo)
++{
++    struct proc_desc *pdesc;
++    for (pdesc = program->pdescs; pdesc; pdesc = pdesc->next)
++      kill_proc(pdesc->proc->p_pid, signo, 1);
++}
++
++
++int rms_prgsignal(int id, int signo)
++{
++    struct prg_desc *program;
++    int status = ESUCCESS;
++    
++    kmutex_lock(&rms_lock);
++    
++    if ((program = findProgram(id)) != NULL)
++    {
++      if (CURUID() == 0 || CURUID() == program->uid)
++      {
++          prgsignal(program, signo);
++          if (signo == SIGKILL)
++              program->flags |= PRG_KILLED;
++      }
++      else
++          status = EACCES;
++    }
++    else
++      status = ESRCH;
++    
++    kmutex_unlock(&rms_lock);
++    
++    return(status);
++}
++
++int rms_prgaddcap(int id, int index, ELAN_CAPABILITY *cap)
++{
++    struct prg_desc *program;
++    struct cap_desc *cdesc;
++    int status = ESUCCESS;
++
++    if (cap == NULL)
++        return(EINVAL);
++
++    kmutex_lock(&rms_lock);
++    if ((program = findProgram(id)) != NULL)
++    {
++      KMEM_ALLOC(cdesc, struct cap_desc *, sizeof(struct cap_desc), TRUE);
++      if (cdesc)
++      {
++          cdesc->index = index;
++          if (copyin(cap, &cdesc->cap, sizeof(ELAN_CAPABILITY)))
++          {
++              KMEM_FREE(cdesc, sizeof(struct cap_desc));
++              status = EFAULT;
++          }
++          else
++          {
++              DBG(printk("rms_prgaddcap :: program %d index %d context %d<-->%d\n",
++                         program->id, index, cdesc->cap.cap_lowcontext, cdesc->cap.cap_highcontext));
++              cdesc->next = program->caps;
++              program->caps = cdesc;
++              program->ncaps++;
++          }
++      }
++      else
++          status = ENOMEM;
++    }
++    else
++      status = ESRCH;
++
++    kmutex_unlock(&rms_lock);
++    return(status);
++}
++
++static uint64_t gettime(void)
++{
++    uint64_t now;
++
++#if defined(SOLARIS)
++    timespec_t tv;
++    gethrestime(&tv);
++    now = tv.tv_sec * 1000 + tv.tv_nsec / 1000000;
++#elif defined(LINUX)
++    struct timeval tv;
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,17)
++    get_fast_time(&tv);
++#else
++    do_gettimeofday(&tv);
++#endif
++    now = tv.tv_sec * 1000 + tv.tv_usec / 1000;
++#elif defined(DIGITAL_UNIX)
++    struct timeval tv;
++    microtime(&tv);
++    now = tv.tv_sec * 1000 + tv.tv_usec / 1000;
++#endif
++
++    return(now);
++}
++
++#ifdef DIGITAL_UNIX
++
++int rms_getrusage(struct proc_desc *pdesc, struct rusage *ru)
++{
++    task_t   task;
++    thread_t thread;
++    
++    if (!pdesc->proc)
++      return(-1);
++    
++    /*
++     * locking required unless called from the current proc
++     */
++    if (pdesc->proc != CURPROC())
++    {
++      if (!P_REF(pdesc->proc))
++          return(-1);
++      
++      task = proc_to_task(pdesc->proc);
++      if (!task) 
++      {
++          P_UNREF(pdesc->proc);
++          DBG(printk("rms_getrusage :: process (%d) has no task\n", pdesc->proc->p_pid));
++          return(-1);
++      }
++
++      task_reference(task);
++      task_lock(task);
++      
++      if (!queue_empty(&task->thread_list))
++          thread = (thread_t) queue_first(&task->thread_list);
++      else 
++      {
++          task_unlock(task);
++          task_deallocate(task);
++          P_UNREF(pdesc->proc);
++          return(-1);
++      }
++      
++      thread_reference(thread);
++      task_unlock(task);
++    }
++    
++    *ru = proc_to_utask(pdesc->proc)->uu_ru;
++    task_get_rusage(ru, proc_to_task(pdesc->proc));
++    
++    if (pdesc->proc != CURPROC())
++    {
++      task_deallocate(task);
++      thread_deallocate(thread);
++      P_UNREF(pdesc->proc);
++    }
++    return(0);
++}
++
++#endif
++
++/*
++ * new stats collection interface, 64-bit with addition of Elan stats
++ */
++int rms_prggetstats(int id, prgstats_t *stats)
++{
++#ifdef DIGITAL_UNIX
++    long ruixrss, ruidrss, ruisrss, rumaxrss, rumajflt;
++#endif
++    struct prg_desc *program = 0;
++    struct proc_desc *pdesc;
++    int status = ESUCCESS;
++    prgstats_t totals;
++    uint64_t now = gettime();
++#if defined(SOLARIS)
++    clock_t utime, stime;
++#elif defined(LINUX)
++    uint64_t utime, stime;
++#endif
++
++    long maxrss;
++
++    kmutex_lock(&rms_lock);
++    
++    if (id < 0)
++    {
++      if ((pdesc = findProcess(CURPROC()->p_pid)) != NULL)
++          program = pdesc->program;
++    }
++    else
++      program = findProgram(id);
++
++    if (program)
++    {
++      if (CURUID() == 0 || CURUID() == program->uid)
++      {
++          totals.flags = program->flags;
++          totals.ncpus = program->ncpus;
++          maxrss = 0;
++
++          if (program->nprocs > 0)
++              totals.etime = now - program->start_time;
++          else
++              totals.etime = program->end_time - program->start_time;
++          
++          totals.atime = program->accum_atime;
++          if (program->flags & PRG_RUNNING)
++              totals.atime += program->ncpus * (now - program->sched_time);
++          
++#if defined(SOLARIS)
++          utime = stime = 0;
++          for (pdesc = program->pdescs; pdesc; pdesc = pdesc->next)
++          {
++              utime += pdesc->proc->p_utime;
++              stime += pdesc->proc->p_stime;
++          }
++          totals.utime = TICK_TO_MSEC(utime);
++          totals.stime = TICK_TO_MSEC(stime);
++
++#elif defined(LINUX)
++          utime = stime = 0;
++          totals.memint = program->memint;
++          totals.pageflts = program->majflt;
++
++          for (pdesc = program->pdescs; pdesc; pdesc = pdesc->next)
++          {
++#ifdef  PROCESS_ACCT
++      DBG(printk("rms_prggetsta :: process %d utime %ld clks stime %ld clks\n", 
++                              pdesc->proc->p_pid, TIMEVAL_TO_CT(&pdesc->proc->utime),
++                              TIMEVAL_TO_CT(&pdesc->proc->stime)));                 
++      utime += TIMEVAL_TO_CT(&pdesc->proc->utime);                  
++      stime += TIMEVAL_TO_CT(&pdesc->proc->stime);                  
++#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
++              DBG(printk("rms_prggetsta :: process %d utime %ld clks stime %ld clks\n", 
++                         pdesc->proc->p_pid, pdesc->proc->times.tms_utime, 
++                         pdesc->proc->times.tms_stime));
++              utime += pdesc->proc->times.tms_utime;
++              stime += pdesc->proc->times.tms_stime;
++#else
++              DBG(printk("rms_prggetsta :: process %d utime %ld clks stime %ld clks\n", 
++                         pdesc->proc->p_pid, pdesc->proc->utime, pdesc->proc->stime));
++              utime += pdesc->proc->utime;
++              stime += pdesc->proc->stime;
++#endif
++
++              totals.pageflts += pdesc->proc->maj_flt; 
++
++              maxrss += PROC_RSS(pdesc->proc) >> (20 - PAGE_SHIFT);
++          }
++
++          /* convert user and system times to millisecs */
++          totals.utime = CT_TO_MSEC(utime);
++          totals.stime = CT_TO_MSEC(stime);
++          
++#elif defined(DIGITAL_UNIX)
++          totals.utime = totals.stime = 0;
++          totals.memint = program->memint;
++          totals.pageflts = program->majflt;
++
++          for (pdesc = program->pdescs; pdesc; pdesc = pdesc->next)
++          {
++              struct rusage ru;
++              if (rms_getrusage(pdesc, &ru) < 0)
++                  continue;
++              
++              totals.utime += TIMEVAL_TO_MSEC(&ru.ru_utime);
++              totals.stime += TIMEVAL_TO_MSEC(&ru.ru_stime);
++              
++              /* convert maxrss to megabytes */
++              rumaxrss = ru.ru_maxrss >> 10;
++              rumajflt = ru.ru_majflt;
++              totals.pageflts += rumajflt;
++              
++              /*
++               * memory intergals are still broken in 5.1
++               */
++              
++#ifdef FIXED_MEMINIT
++              
++              /* convert from pages * clock ticks to Mbytes * secs */
++              ruixrss = (ru.ru_ixrss >> (20 - PAGE_SHIFT)) / hz;
++              ruidrss = (ru.ru_idrss >> (20 - PAGE_SHIFT)) / hz;
++              ruisrss = (ru.ru_isrss >> (20 - PAGE_SHIFT)) / hz;
++              
++              DBG(printk("rms_prggetsta :: process %d mem %d int %d %d %d flt %d\n", pdesc->proc->p_pid, 
++                         rumaxrss, ruixrss, ruidrss, ruisrss, rumajflt));
++              
++              totals.memint += ruixrss + ruidrss + ruisrss;
++#else
++              DBG(printk("rms_prggetsta :: process %d mem %d flt %d\n", pdesc->proc->p_pid, rumaxrss, rumajflt));
++              totals.memint = 0;
++#endif
++              maxrss += rumaxrss;
++          }
++#endif /* DIGITAL_UNIX */
++
++          if (maxrss > program->maxrss)
++              program->maxrss = maxrss;
++          
++          totals.utime += program->cutime;
++          totals.stime += program->cstime;
++          totals.mem = program->maxrss;
++          totals.ebytes = program->ebytes;
++          totals.exfers = program->exfers;
++
++          DBG(printk("rms_prggetsta :: program %d mem %d flt %d\n", program->id, totals.mem, totals.pageflts));
++          
++          if (copyout(&totals, stats, sizeof(prgstats_t)))
++              status = EFAULT;
++      }
++      else
++          status = EACCES;
++    }
++    else
++      status = ESRCH;
++    
++    kmutex_unlock(&rms_lock);
++    return(status);
++}
++
++/*
++ * preserve the old stats stats collection interface
++ */
++
++int rms_prggetoldstats(int id, prgstats_old_t *stats)
++{
++#ifdef DIGITAL_UNIX
++    long ruixrss, ruidrss, ruisrss, rumaxrss, rumajflt;
++#endif
++    struct prg_desc *program = 0;
++    struct proc_desc *pdesc;
++    int status = ESUCCESS;
++    prgstats_old_t totals;
++    uint64_t now = gettime();
++#if defined(SOLARIS) || defined(LINUX)
++    clock_t utime, stime;
++#endif
++    long maxrss;
++
++    kmutex_lock(&rms_lock);
++    
++    if (id < 0)
++    {
++      if ((pdesc = findProcess(CURPROC()->p_pid)) != NULL)
++          program = pdesc->program;
++    }
++    else
++      program = findProgram(id);
++
++    if (program)
++    {
++      if (CURUID() == 0 || CURUID() == program->uid)
++      {
++          totals.flags = program->flags;
++          totals.ncpus = program->ncpus;
++          maxrss = 0;
++
++          if (program->nprocs > 0)
++              totals.etime = now - program->start_time;
++          else
++              totals.etime = program->end_time - program->start_time;
++          
++          totals.atime = program->accum_atime;
++          if (program->flags & PRG_RUNNING)
++              totals.atime += program->ncpus * (now - program->sched_time);
++          
++#if defined(SOLARIS)
++          utime = stime = 0;
++          for (pdesc = program->pdescs; pdesc; pdesc = pdesc->next)
++          {
++              utime += pdesc->proc->p_utime;
++              stime += pdesc->proc->p_stime;
++          }
++          totals.utime = TICK_TO_MSEC(utime);
++          totals.stime = TICK_TO_MSEC(stime);
++
++#elif defined(LINUX)
++          utime = stime = 0;
++          totals.memint = program->memint;
++          totals.pageflts = program->majflt;
++
++          for (pdesc = program->pdescs; pdesc; pdesc = pdesc->next)
++          {
++#ifdef  PROCESS_ACCT
++      DBG(printk("rms_getoldsta :: process %d utime %ld clks stime %ld clks\n", 
++                              pdesc->proc->p_pid, TIMEVAL_TO_CT(&pdesc->proc->utime),
++                              TIMEVAL_TO_CT(&pdesc->proc->stime)));                 
++      utime += TIMEVAL_TO_CT(&pdesc->proc->utime);                  
++      stime += TIMEVAL_TO_CT(&pdesc->proc->stime);                  
++#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
++              DBG(printk("rms_getoldsta :: process %d utime %ld clks stime %ld clks\n", 
++                         pdesc->proc->p_pid, pdesc->proc->times.tms_utime, 
++                         pdesc->proc->times.tms_stime));
++              utime += pdesc->proc->times.tms_utime;
++              stime += pdesc->proc->times.tms_stime;
++#else
++              DBG(printk("rms_getoldsta :: process %d utime %ld clks stime %ld clks\n", 
++                         pdesc->proc->p_pid, pdesc->proc->utime, pdesc->proc->stime));
++              utime += pdesc->proc->utime;
++              stime += pdesc->proc->stime;
++#endif
++
++              totals.pageflts += pdesc->proc->maj_flt; 
++              maxrss += PROC_RSS(pdesc->proc) >> (20 - PAGE_SHIFT);
++          }
++
++          /* convert user and system times to millisecs */
++          totals.utime = CT_TO_MSEC(utime);
++          totals.stime = CT_TO_MSEC(stime);
++          
++#elif defined(DIGITAL_UNIX)
++          totals.utime = totals.stime = 0;
++          totals.memint = program->memint;
++          totals.pageflts = program->majflt;
++
++          for (pdesc = program->pdescs; pdesc; pdesc = pdesc->next)
++          {
++              struct rusage ru;
++              if (rms_getrusage(pdesc, &ru) < 0)
++                  continue;
++              
++              totals.utime += TIMEVAL_TO_MSEC(&ru.ru_utime);
++              totals.stime += TIMEVAL_TO_MSEC(&ru.ru_stime);
++              
++              /* convert maxrss to megabytes */
++              rumaxrss = ru.ru_maxrss >> 10;
++              rumajflt = ru.ru_majflt;
++              totals.pageflts += rumajflt;
++              
++              /*
++               * memory intergals are still broken in 5.1
++               */
++              
++#ifdef FIXED_MEMINIT
++              
++              /* convert from pages * clock ticks to Mbytes * secs */
++              ruixrss = (ru.ru_ixrss >> (20 - PAGE_SHIFT)) / hz;
++              ruidrss = (ru.ru_idrss >> (20 - PAGE_SHIFT)) / hz;
++              ruisrss = (ru.ru_isrss >> (20 - PAGE_SHIFT)) / hz;
++              
++              DBG(printk("rms_getoldsta :: process %d mem %d int %d %d %d flt %d\n", pdesc->proc->p_pid, 
++                         rumaxrss, ruixrss, ruidrss, ruisrss, rumajflt));
++              
++              totals.memint += ruixrss + ruidrss + ruisrss;
++#else
++              DBG(printk("rms_getoldsta :: process %d mem %d flt %d\n", pdesc->proc->p_pid, rumaxrss, rumajflt));
++              totals.memint = 0;
++#endif
++              maxrss += rumaxrss;
++          }
++#endif /* DIGITAL_UNIX */
++
++          if (maxrss > program->maxrss)
++              program->maxrss = maxrss;
++          
++          totals.utime += program->cutime;
++          totals.stime += program->cstime;
++          totals.mem = program->maxrss;
++          
++          DBG(printk("rms_getoldsta :: program %d mem %d flt %d\n", program->id, totals.mem, totals.pageflts));
++          
++          if (copyout(&totals, stats, sizeof(prgstats_old_t)))
++              status = EFAULT;
++      }
++      else
++          status = EACCES;
++    }
++    else
++      status = ESRCH;
++    
++    kmutex_unlock(&rms_lock);
++    return(status);
++}
++
++
++int rms_prgsuspend(int id)
++{
++    struct prg_desc *program;
++    int status = ESUCCESS;
++
++    kmutex_lock(&rms_lock);
++    
++    if ((program = findProgram(id)) != NULL)
++    {
++      if (CURUID() == 0 || CURUID() == program->uid)
++      {
++          program->flags &= ~PRG_RUNNING;
++          program->flags |=  PRG_SUSPEND;
++          program->accum_atime += program->ncpus * (gettime() - program->sched_time);
++
++          /* suspend/resume just use signals for now */
++          prgsignal(program, SIGSTOP);
++      }
++      else
++          status = EACCES;
++    }
++    else
++      status = ESRCH;
++
++    kmutex_unlock(&rms_lock);
++    return(status);
++}
++
++int rms_prgresume(int id)
++{
++    struct prg_desc *program;
++    int status = ESUCCESS;
++
++    kmutex_lock(&rms_lock);
++    
++    if ((program = findProgram(id)) != NULL)
++    {
++      if (CURUID() == 0 || CURUID() == program->uid)
++      {
++          program->flags &= ~PRG_SUSPEND;
++          program->flags |=  PRG_RUNNING;
++          program->sched_time = gettime();
++          prgsignal(program, SIGCONT);
++      }
++      else
++          status = EACCES;
++    }
++    else
++      status = ESRCH;
++
++    kmutex_unlock(&rms_lock);
++    return(status);
++}
++
++
++int rms_ncaps(int *ncaps)
++{
++    struct proc_desc *pdesc;
++    int status = ESUCCESS;
++    
++    kmutex_lock(&rms_lock);
++    if ((pdesc = findProcess(CURPROC()->p_pid)) != NULL)
++    {
++      if (copyout(&pdesc->program->ncaps, ncaps, sizeof(int)))
++          status = EFAULT;
++    }
++    else
++      status = ESRCH;
++
++    kmutex_unlock(&rms_lock);
++    return(status);
++}
++
++int rms_getprgid(pid_t pid, int *id)
++{
++    struct proc_desc *pdesc;
++    int status = ESUCCESS;
++    
++    if (pid == 0)
++      pid = CURPROC()->p_pid;
++    
++    kmutex_lock(&rms_lock);
++    if ((pdesc = findProcess(pid)) != NULL)
++    {
++      if (copyout(&pdesc->program->id, id, sizeof(int)))
++          status = EFAULT;
++    }
++    else
++      status = ESRCH;
++
++    kmutex_unlock(&rms_lock);
++    return(status);
++}
++
++int rms_setcap(int index, int ctx)
++{
++    struct proc_desc *pdesc;
++    struct cap_desc *cdesc;
++    int status = EINVAL;
++    
++    DBG(printk("rms_setcap    :: process %d cap %d ctx %d\n",CURPROC()->p_pid,index,ctx));
++
++    kmutex_lock(&rms_lock);
++    if ((pdesc = findProcess(CURPROC()->p_pid)) != NULL)
++    {
++      for (cdesc = pdesc->program->caps; cdesc; cdesc = cdesc->next)
++          if (cdesc->index == index && 0 <= ctx && ctx <= (cdesc->cap.cap_highcontext - cdesc->cap.cap_lowcontext + 1))
++          {
++              pdesc->mycap = index;
++              pdesc->myctx = cdesc->cap.cap_lowcontext + ctx;
++              status = ESUCCESS;
++          }
++    }
++    else
++      status = ESRCH;
++
++    kmutex_unlock(&rms_lock);
++    return(status);
++}
++
++
++int rms_mycap(int *index)
++{
++    struct proc_desc *pdesc;
++    int status = ESUCCESS;
++    
++    DBG(printk("rms_mycap :: process %d\n", CURPROC()->p_pid));
++    
++    kmutex_lock(&rms_lock);
++    if ((pdesc = findProcess(CURPROC()->p_pid)) != NULL)
++    {
++      DBG(printk("rms_mycap :: found process %d mycap = %d\n", CURPROC()->p_pid, pdesc->mycap));
++      if (copyout(&pdesc->mycap, index, sizeof(int)))
++          status = EFAULT;
++    }
++    else
++      status = ESRCH;
++
++    kmutex_unlock(&rms_lock);
++    return(status);
++}
++
++int rms_getcap(int index, ELAN_CAPABILITY *cap)
++{
++    struct proc_desc *pdesc;
++    struct cap_desc *cdesc;
++    int status = ESUCCESS;
++    
++    kmutex_lock(&rms_lock);
++    if ((pdesc = findProcess(CURPROC()->p_pid)) != NULL)
++    {
++      for (cdesc = pdesc->program->caps; cdesc; cdesc = cdesc->next)
++          if (cdesc->index == index)
++              break;
++      
++      if (cdesc)
++      {
++          /* tell each process about its own context */
++          cdesc->cap.cap_mycontext = pdesc->myctx;
++          
++          if (copyout(&cdesc->cap, cap, ELAN_CAP_SIZE(&cdesc->cap)))
++              status = EFAULT;
++          
++          DBG(printk("rms_getcap    :: program %d index %d context %d<-->%d\n", pdesc->program->id, 
++                     cdesc->index, cdesc->cap.cap_lowcontext, cdesc->cap.cap_highcontext));
++      }
++      else
++          status = EINVAL;
++    }
++    else
++      status = ESRCH;
++    
++    kmutex_unlock(&rms_lock);
++    return(status);
++}
++
++
++static int
++rms_fork_callback (struct PROC_STRUCT *curproc, struct PROC_STRUCT *child)
++{
++    struct prg_desc *program;
++    struct proc_desc *parent;
++    struct proc_desc *pdesc = NULL;
++
++    kmutex_lock(&rms_lock);
++    
++    DBG(printk("rms_fork_func :: phase is fork pid %d child %d\n", curproc->p_pid, child->p_pid));
++
++    /*
++     * find the process that forked
++     */
++    if ((parent = findProcess(curproc->p_pid)) != NULL)
++    {
++      program = parent->program;
++      
++      DBG(printk("rms_fork_func :: program is %d flags %d\n", program->id, program->flags));
++      
++      /*
++       * processes can be blocked in fork while prgsignal is in progress
++       * so check to see if the PRG_KILLED flag is set
++       */
++      if (program->flags & PRG_KILLED)
++          DBG(printk("rms_fork_func :: fork handler called after program killed\n"));
++      else
++      {
++          /*
++           * create a new process description and add to program
++           */
++          KMEM_ALLOC(pdesc, struct proc_desc *, sizeof(struct proc_desc), TRUE);
++          if (pdesc)
++          {
++              pdesc->next = program->pdescs;
++              program->pdescs = pdesc;
++              pdesc->proc = child;
++              pdesc->mycap = parent->mycap;
++              pdesc->myctx = parent->myctx;
++              pdesc->program = program;
++              pdesc->vp = -1;              /* assigned by elaninitdone */
++              program->nprocs++;
++          }
++          else
++              printk("rms_fork_func :: memory allocation failed\n");
++      }
++    }
++    else
++      DBG(printk("rms_fork_func :: no program\n"));
++    
++    kmutex_unlock (&rms_lock);
++
++    return pdesc == NULL;
++}
++
++static void
++rms_exit_callback (struct PROC_STRUCT *curproc)
++{
++    struct prg_desc *program;
++    struct proc_desc *pdesc, **pdescp, *p;
++#ifdef DIGITAL_UNIX
++    struct rusage ru;
++#endif
++    long maxrss;
++
++    kmutex_lock(&rms_lock);
++    
++    DBG(printk("rms_exit_func :: process %d exiting\n", curproc->p_pid));
++
++    /*
++     * find the process that exited and accumulate 
++     * resource usage in its parent program
++     */
++    for (program = programs, pdesc = 0; program && !pdesc; program = program->next)
++    {
++      pdescp = &program->pdescs;
++      while ((pdesc = *pdescp) != NULL)
++      {
++          if (pdesc->proc->p_pid == curproc->p_pid)
++          {
++              /*
++               * keep track of the resources used
++               */
++#if defined(SOLARIS)
++              program->cutime += TICK_TO_MSEC(pdesc->proc->p_utime);
++              program->cstime += TICK_TO_MSEC(pdesc->proc->p_stime);
++              
++#elif defined(LINUX)
++#ifdef PROCESS_ACCT
++      DBG(printk("rms_exit_func :: process %d exit utime %ld clks stime %ld clks\n",
++                              pdesc->proc->p_pid,
++                              TIMEVAL_TO_CT(&pdesc->proc->utime),                
++                              TIMEVAL_TO_CT(&pdesc->proc->stime)));              
++      program->cutime += TIMEVAL_TO_MSEC(&pdesc->proc->utime);      
++      program->cstime += TIMEVAL_TO_MSEC(&pdesc->proc->stime);      
++#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)                           
++              DBG(printk("rms_exit_func :: process %d exit utime %ld clks stime %ld clks\n", 
++                         pdesc->proc->p_pid, pdesc->proc->times.tms_utime, 
++                         pdesc->proc->times.tms_stime));
++              
++              program->cutime += CT_TO_MSEC(pdesc->proc->times.tms_utime);
++              program->cstime += CT_TO_MSEC(pdesc->proc->times.tms_stime);
++#else
++              DBG(printk("rms_exit_func :: process %d exit utime %ld clks stime %ld clks\n", 
++                         pdesc->proc->p_pid, pdesc->proc->utime, pdesc->proc->stime));
++              
++              program->cutime += CT_TO_MSEC(pdesc->proc->utime);
++              program->cstime += CT_TO_MSEC(pdesc->proc->stime);
++#endif
++              program->majflt += pdesc->proc->maj_flt;
++              maxrss = PROC_RSS(pdesc->proc) >> (20 - PAGE_SHIFT);
++              
++#elif defined(DIGITAL_UNIX)
++              if (rms_getrusage(pdesc, &ru) == 0)
++              {
++                  program->cutime += TIMEVAL_TO_MSEC(&ru.ru_utime);
++                  program->cstime += TIMEVAL_TO_MSEC(&ru.ru_stime);
++                  program->majflt += ru.ru_majflt;
++                  
++                  /* convert maxrss to megabytes */
++                  maxrss = ru.ru_maxrss >> 10;
++              }
++#endif
++              
++              /*
++               * shared memory segment cleanup
++               */
++#if defined(DIGITAL_UNIX)
++              rms_shmcleanup(-1);
++#elif defined(LINUX)
++              shm_cleanup();
++#endif 
++              
++              /* 
++               * remove process from program 
++               */
++              *pdescp = pdesc->next;
++              KMEM_FREE(pdesc, sizeof(struct proc_desc));
++              program->nprocs--;
++              
++              /*
++               * update the memory high water mark for the program
++               */
++              for (p = program->pdescs; p; p = p->next)
++              {
++#if defined(DIGITAL_UNIX)
++                  if (rms_getrusage(p, &ru) < 0)
++                      continue;
++                  
++                  /* convert maxrss to megabytes */
++                  maxrss += ru.ru_maxrss >> 10;
++                  
++#elif defined(LINUX)                  
++                  maxrss += PROC_RSS(p->proc) >> (20 - PAGE_SHIFT);
++#endif
++              }
++              if (maxrss > program->maxrss)
++                  program->maxrss = maxrss;
++              
++              DBG(printk("rms_exit_func :: program %d procs %d mem %ld\n", program->id, program->nprocs, program->maxrss));
++              
++              /*
++               * final update to the program if this is the last process
++               */
++              if (program->nprocs == 0)
++              {
++                  program->end_time = gettime();
++                  program->flags &= ~PRG_RUNNING;
++                  program->accum_atime += program->ncpus * (program->end_time - program->sched_time);
++                  DBG(printk("rms_exit_func :: last process has gone\n"));
++              }
++              break;
++          }
++          else
++              pdescp = &pdesc->next;
++      }
++    }
++    kmutex_unlock  (&rms_lock);
++}
++
++#if defined(LINUX)
++static int
++rms_ptrack_callback (void *arg, int phase, struct task_struct *child)
++{
++    switch (phase)
++    {
++    case PTRACK_PHASE_CLONE:
++      if (rms_fork_callback (current, child))
++          return PTRACK_DENIED;
++      else
++          return PTRACK_INNHERIT;
++
++    case PTRACK_PHASE_CLONE_FAIL:
++      DBG(printk("rms_fork_func :: fork failed pid %d child %d\n", current->p_pid, child->p_pid));
++      rms_exit_callback(child);
++      break;
++
++    case PTRACK_PHASE_EXIT:
++      rms_exit_callback(current);
++      break;
++    }
++    return PTRACK_FINISHED;
++}
++
++#else
++
++static void
++rms_xa_callback (void *arg, int phase, void *ctask)
++{
++    switch (phase)
++    {
++    case XA_FORK:
++      if (rms_fork_callback (CURPROC(), (struct PROC_STRUCT *)task_to_proc(ctask)))
++          psignal(task_to_proc(ctask), SIGKILL);
++      break;
++    case XA_EXIT:
++      rms_exit_callback (CURPROC());
++      break;
++    }
++}
++
++#endif
++
++#ifdef DIGITAL_UNIX
++
++/*
++ * NB: These functions will only work on steelos.
++ */
++
++/*
++ * rms_setcorepath
++ *
++ * set a path at which to dump core if the task aborts  
++ *
++ * enhanced core file names must be enabled for this to work
++ */
++int rms_setcorepath(char *corepath)
++{
++    int    length;
++    char  *path;
++    int    status; 
++    struct proc_desc *pdesc;
++    
++    /* 
++     * access restricted - we don't want users moving
++     * their corepath and generating a huge I/O load
++     */
++    if (CURUID())
++      return(EACCES);
++    
++    if (!(pdesc = findProcess(CURPROC()->p_pid)))
++      return(ESRCH);
++    
++    if (pdesc->program->corepath)
++      return(EEXIST);
++    
++    KMEM_ALLOC(path, char *, MAXCOREPATHLEN + 1, TRUE);
++    if (path == 0)
++      return(ENOMEM);
++    
++    if (copyinstr(corepath, path, MAXCOREPATHLEN, &length))
++      return(EFAULT);
++    
++    path[length] = 0;
++    status = add_corepath(path);
++    
++    DBG(printk("rms_setcorepa :: id %d corepath %s status %d\n", pdesc->program->id, path, status));
++    
++    if (status == ESUCCESS)
++      pdesc->program->corepath = path;
++    else
++      KMEM_FREE(path, MAXCOREPATHLEN + 1);
++    
++    return(status);
++}
++
++static int find_corepath(pid_t pid, char *path, int len)
++{
++    struct proc *procp;
++    struct utask *utask;
++    int status = ESUCCESS;
++
++    procp = pfind(pid);
++    if (procp == NULL)
++        return(ENOENT);
++    
++    utask = proc_to_utask(procp);
++    
++    if (utask->uu_coredir)
++        bcopy(utask->uu_coredir,path,len);
++    else
++        status = ENOENT;
++    
++    /* pfind takes out a reference */
++    P_UNREF(procp);
++
++    return(status);
++}
++
++int rms_getcorepath(pid_t pid, char *corepath, int maxlen)
++{
++    char src[MAXCOREPATHLEN];
++    int len;
++    int status;
++    
++    if (maxlen < 2)
++      return(EINVAL);
++    
++    len = MIN(maxlen, MAXCOREPATHLEN);
++    
++    status = find_corepath(pid, src, len);
++    
++    if (status == ESUCCESS)
++        len = strlen(src)+1;
++    else if (status == ENOENT) 
++    {
++      len = 2;
++      src[0] = '.';
++        src[1] = '\0';
++        status = ESUCCESS;
++    }
++    
++    if (copyout(src, corepath, len))
++      return(EFAULT);
++    
++    return(status);
++}
++
++#endif
++
++/*
++ * rms_elaninitdone - mark a process as having successfully completed elan initialisation
++ */
++int rms_elaninitdone(int vp)
++{
++    int status = ESUCCESS;
++    struct proc_desc *pdesc;
++    
++    DBG(printk("rms_elaninit  :: process %d vp %d\n", CURPROC()->p_pid, vp));
++    
++    kmutex_lock(&rms_lock);
++    if ((pdesc = findProcess(CURPROC()->p_pid)) != NULL)
++      pdesc->vp = vp;
++    else
++      status = ESRCH;
++    kmutex_unlock(&rms_lock);
++    return(status);
++}
++
++
++/*
++ * rms_prgelanpids - return the ids of processes that have completed elan initialisation
++ */
++int rms_prgelanpids(int id, int maxpids, int *vps, pid_t *pids, int *npids)
++{
++    struct prg_desc *program;
++    struct proc_desc *pdesc;
++    pid_t *pidbuf;
++    int status = ESUCCESS, count = 0, *vpbuf;
++    
++    DBG(printk("rms_elanpids  :: process %d id %d\n", CURPROC()->p_pid, id));
++    
++    kmutex_lock(&rms_lock);
++    
++    if ((program = findProgram(id)) != NULL)
++    {
++      if (program->nprocs > 0)
++      {
++          KMEM_ALLOC(pidbuf, pid_t *, program->nprocs * sizeof(pid_t), TRUE);
++          KMEM_ALLOC(vpbuf, int *, program->nprocs * sizeof(int), TRUE);
++          if (pidbuf && vpbuf)
++          {
++              for (pdesc = program->pdescs; pdesc; pdesc = pdesc->next)
++                  if (pdesc->vp >= 0)
++                  {
++                      pidbuf[count] = pdesc->proc->p_pid;
++                      vpbuf[count] = pdesc->vp;
++                      count++;
++                  }
++          
++              if (count > 0 && (copyout(pidbuf, pids, sizeof(pid_t) * MIN(count, maxpids)) ||
++                                copyout(vpbuf, vps, sizeof(int) * MIN(count, maxpids))))
++                  status = EFAULT;
++              
++              KMEM_FREE(pidbuf, program->nprocs * sizeof(pid_t));
++              KMEM_FREE(vpbuf, program->nprocs * sizeof(int));
++          }
++          else
++              status = ENOMEM;
++      }
++
++      if (copyout(&count, npids, sizeof(int)))
++          status = EFAULT;
++    }
++    else
++      status = ESRCH;
++
++    kmutex_unlock(&rms_lock);
++    
++    return(status);
++
++}
++
++int rms_setpset(int psid)
++{
++    struct prg_desc *program;
++    struct proc_desc *pdesc;
++    int status = ESUCCESS;
++
++    if (CURUID())
++      return(EACCES);
++
++    kmutex_lock(&rms_lock);
++    
++    if ((pdesc = findProcess(CURPROC()->p_pid)) != NULL)
++    {
++      program = pdesc->program;
++      program->psid = psid;
++    }
++    else
++      status = ESRCH;
++
++    kmutex_unlock(&rms_lock);
++    return(status);
++}
++
++
++int rms_getpset(int id, int *psid)
++{
++    struct prg_desc *program;
++    int status = ESUCCESS;
++    
++    kmutex_lock(&rms_lock);
++    if ((program = findProgram(id)) != NULL)
++    {
++      if (copyout(&program->psid, psid, sizeof(int)))
++          status = EFAULT;
++    }
++    else
++      status = ESRCH;
++    
++    kmutex_unlock(&rms_lock);
++    return(status);
++}
++
++int
++rms_setelanstats(int id, uint64_t ebytes, uint64_t exfers)
++{
++    struct prg_desc *program;
++    int status = ESUCCESS;
++    
++    DBG(printk("rms_setelanst :: process %d id %d\n", CURPROC()->p_pid, id));
++
++    kmutex_lock(&rms_lock);
++    if ((program = findProgram(id)) != NULL)
++    {
++      if (CURUID() == 0 || CURUID() == program->uid)
++      {
++          program->ebytes = ebytes;
++          program->exfers = exfers;
++      }
++      else
++          status = EACCES;
++    }
++    else
++      status = ESRCH;
++    
++    kmutex_unlock(&rms_lock);
++    return(status);
++}
++
++rms_modversion()
++{
++    return(RMS_MODVERSION);
++}
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++
++
++
++
++
++
++
+Index: linux-2.4.21/drivers/net/qsnet/rms/rms_kern_Linux.c
+===================================================================
+--- linux-2.4.21.orig/drivers/net/qsnet/rms/rms_kern_Linux.c   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/drivers/net/qsnet/rms/rms_kern_Linux.c        2005-06-01 23:12:54.703423296 -0400
+@@ -0,0 +1,430 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "$Id: rms_kern_Linux.c,v 1.20 2004/05/14 08:55:57 duncan Exp $"
++/*      $Source: /cvs/master/quadrics/rmsmod/rms_kern_Linux.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <linux/sysctl.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/proc_fs.h>
++
++#include <rms/rmscall.h>
++#include <rms/rmsio.h>
++
++MODULE_AUTHOR("Quadrics Ltd");
++MODULE_DESCRIPTION("RMS support module");
++MODULE_LICENSE("GPL");
++
++int rms_debug = 0;
++
++ctl_table rms_table[] = {
++    {
++      .ctl_name = 1,
++      .procname = "rms_debug",
++      .data     = &rms_debug,
++      .maxlen   = sizeof(int),
++      .mode     = 0644,
++      .child    = NULL,
++      .proc_handler = &proc_dointvec,
++    },
++    {0}
++};
++
++ctl_table rms_root_table[] = {
++    {
++      .ctl_name = CTL_DEBUG,
++      .procname = "rms",
++      .data     = NULL,
++      .maxlen   = 0,
++      .mode     = 0555,
++      .child    = rms_table,
++    },
++    {0}
++};
++
++static struct ctl_table_header *rms_sysctl_header;
++
++static int rms_open (struct inode *ino, struct file *fp);
++static int rms_release (struct inode *ino, struct file *fp);
++static int rms_ioctl (struct inode *inode, struct file *fp, unsigned int cmd, unsigned long arg);
++
++#if defined(CONFIG_PPC64) || defined(CONFIG_SPARC64) || defined(CONFIG_X86_64)
++static int
++rms_ioctl32_cmds[] =
++{
++    RMSIO_GETPRGID32,
++    RMSIO_GETCAP32
++};
++
++static int      rms_ioctl32 (unsigned int fd, unsigned int cmd, 
++                           unsigned long arg, struct file *file);
++#endif
++
++static struct file_operations rms_fops =
++{
++    .owner   = THIS_MODULE,
++    .ioctl   = rms_ioctl,
++    .open    = rms_open,
++    .release = rms_release,
++};
++
++struct proc_dir_entry *rms_procfs_programs;
++static struct proc_dir_entry *rms_procfs_root;
++
++int version_callback(char* page, char** start, off_t off, int count, int* eof, void* data)
++{
++    return(sprintf(page, "$Id: rms_kern_Linux.c,v 1.20 2004/05/14 08:55:57 duncan Exp $\n"));
++}
++
++static int __init rms_start(void)
++{
++    struct proc_dir_entry *p;
++    int res;
++
++    if ((rms_sysctl_header = register_sysctl_table(rms_root_table, 1)) == 0)
++    {
++      printk ("rmsmod: failed to register sysctl table\n");
++      return (-ENXIO);
++    }
++    
++    if ((rms_procfs_root = proc_mkdir("rms",  NULL)) == NULL ||
++      (rms_procfs_programs = proc_mkdir("programs",  rms_procfs_root)) == NULL ||
++      (p = create_proc_entry ("control", S_IRUGO, rms_procfs_root)) == NULL)
++    {
++      unregister_sysctl_table (rms_sysctl_header);
++      printk ("rmsmod: failed to register /proc/rms\n");
++      return (-ENXIO);
++    }
++    p->proc_fops = &rms_fops;
++    p->owner     = THIS_MODULE;
++    p->data      = NULL;
++
++    if ((p = create_proc_entry ("version", S_IRUGO, rms_procfs_root)) != NULL)
++    {
++      p->owner = THIS_MODULE;
++      p->data = NULL;
++      p->read_proc = version_callback;
++    }
++
++    if ((res = rms_init()) != ESUCCESS)
++    {
++      remove_proc_entry ("programs", rms_procfs_root);
++      remove_proc_entry ("control", rms_procfs_root);
++      remove_proc_entry ("rms", NULL);
++      unregister_sysctl_table (rms_sysctl_header);
++      return (-res);
++    }
++
++#if defined(CONFIG_PPC64) || defined(CONFIG_SPARC64) || defined(CONFIG_X86_64)
++    lock_kernel();
++    {
++      extern int register_ioctl32_conversion(unsigned int cmd, int (*handler)(unsigned int, unsigned int, unsigned long, struct file *));
++      register int i;
++      for (i = 0; i < sizeof (rms_ioctl32_cmds)/sizeof(rms_ioctl32_cmds[0]); i++)
++          register_ioctl32_conversion (rms_ioctl32_cmds[i], rms_ioctl32);
++    }
++    unlock_kernel();
++#endif
++    return (0);
++}
++
++static void __exit rms_exit(void)
++{
++    rms_fini();
++
++#if defined(CONFIG_PPC64) || defined(CONFIG_SPARC64) || defined(CONFIG_X86_64)
++    lock_kernel();
++    {
++      extern void unregister_ioctl32_conversion(unsigned int cmd);
++      register int i;
++
++      for (i = 0; i < sizeof (rms_ioctl32_cmds)/sizeof(rms_ioctl32_cmds[0]); i++)
++          unregister_ioctl32_conversion (rms_ioctl32_cmds[i]);
++    }
++    unlock_kernel();
++#endif
++
++    remove_proc_entry ("version", rms_procfs_root);
++    remove_proc_entry ("programs", rms_procfs_root);
++    remove_proc_entry ("control", rms_procfs_root);
++    remove_proc_entry ("rms", NULL);
++    unregister_sysctl_table(rms_sysctl_header);
++}
++
++/* Declare the module init and exit functions */
++module_init(rms_start);
++module_exit(rms_exit);
++
++static int
++rms_open (struct inode *inode, struct file *fp)
++{
++    MOD_INC_USE_COUNT;
++    fp->private_data = NULL;
++
++    return (0);
++}
++
++static int
++rms_release (struct inode *inode, struct file *fp)
++{
++    MOD_DEC_USE_COUNT;
++    return (0);
++}
++
++static int 
++rms_ioctl(struct inode *inode, struct file *fp, unsigned int cmd, unsigned long arg)
++{
++    int res;
++
++    switch (cmd) 
++    {
++/* no corepath support in Linux yet */
++#if 0
++    case RMSIO_SETCOREPATH:
++      res = rms_setcorepath((caddr_t)arg);
++      break;
++      
++    case RMSIO_GETCOREPATH:
++    {
++      RMSIO_GETCOREPATH_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (args)))
++          return (-EFAULT);
++
++      res = rms_getcorepath(args.pid, args.corepath, args.maxlen);
++      break;
++    }
++#endif
++      
++    case RMSIO_PRGCREATE:
++    {
++      RMSIO_PRGCREATE_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (args)))
++          return (-EFAULT);
++
++      res = rms_prgcreate(args.id, args.uid, args.cpus);
++      break;
++    }
++
++    case RMSIO_PRGDESTROY:
++      res = rms_prgdestroy(arg);
++      break;
++      
++    case RMSIO_PRGIDS:
++    {
++      RMSIO_PRGIDS_STRUCT args;
++      
++      if (copy_from_user (&args, (void *) arg, sizeof (args)))
++          return (-EFAULT);
++
++      res = rms_prgids(args.maxids, args.prgids, args.nprgs);
++      break;
++    }
++
++    case RMSIO_PRGINFO:
++    {
++      RMSIO_PRGINFO_STRUCT args;
++      
++      if (copy_from_user (&args, (void *) arg, sizeof (args)))
++          return (-EFAULT);
++
++      res = rms_prginfo(args.id, args.maxpids, args.pids, args.nprocs);
++      break;
++    }
++      
++    case RMSIO_PRGSIGNAL:
++    {
++      RMSIO_PRGSIGNAL_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (args)))
++          return (-EFAULT);
++
++      res = rms_prgsignal(args.id, args.signo);
++      break;
++    }
++      
++    case RMSIO_PRGADDCAP:
++    {
++      RMSIO_PRGADDCAP_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (args)))
++          return (-EFAULT);
++
++      res = rms_prgaddcap(args.id, args.index, args.cap);
++      break;
++    }
++
++    case RMSIO_SETCAP:
++    {
++      RMSIO_SETCAP_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (args)))
++          return (-EFAULT);
++
++      res = rms_setcap(args.index, args.ctx);
++      break;
++    }
++      
++    case RMSIO_NCAPS:
++      res = rms_ncaps((int *)arg);
++      break;
++      
++    case RMSIO_GETPRGID:
++    {
++      RMSIO_GETPRGID_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (args)))
++          return (-EFAULT);
++
++      res = rms_getprgid(args.pid, args.id);
++      break;
++    }
++
++    case RMSIO_GETMYCAP:
++      res = rms_mycap((int *)arg);
++      break;
++      
++    case RMSIO_GETCAP:
++    {
++      RMSIO_GETCAP_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (args)))
++          return (-EFAULT);
++
++      res = rms_getcap(args.index, args.cap);
++      break;
++    }
++
++    case RMSIO_PRGGETSTATS:
++    {
++      RMSIO_PRGGETSTATS_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (args)))
++          return (-EFAULT);
++
++      res = rms_prggetoldstats(args.id, args.stats);
++      break;
++    }
++
++    case RMSIO_PRGGETSTATS2:
++    {
++      RMSIO_PRGGETSTATS2_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (args)))
++          return (-EFAULT);
++
++      res = rms_prggetstats(args.id, args.stats);
++      break;
++    }
++
++    case RMSIO_PRGSUSPEND:
++      res = rms_prgsuspend(arg);
++      break;
++      
++    case RMSIO_PRGRESUME:
++      res = rms_prgresume(arg);
++      break;
++
++    case RMSIO_ELANINITDONE:
++      res = rms_elaninitdone(arg);
++      break;
++
++    case RMSIO_PRGELANPIDS:
++    {
++      RMSIO_PRGELANPIDS_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (args)))
++          return (-EFAULT);
++
++      res = rms_prgelanpids(args.id, args.maxpids, args.vps, args.pids, args.npids);
++      break;
++    }
++
++    case RMSIO_SETELANSTATS:
++    {
++      RMSIO_SETELANSTATS_STRUCT args;
++      elanstats_t estats;
++
++      if (copy_from_user(&args, (void *)arg, sizeof(args)) ||
++          copy_from_user(&estats, (void *)args.estats, sizeof(estats)))
++          return(-EFAULT);
++      
++      res = rms_setelanstats(args.id, estats.ebytes, estats.exfers);
++      break;
++    }
++
++    case RMSIO_MODVERSION:
++    {
++      RMSIO_MODVERSION_STRUCT args;
++      int version = rms_modversion();
++      
++      if (copy_from_user (&args, (void *)arg, sizeof (args)))
++          return (-EFAULT);
++      
++      if (copyout(&version, args.version, sizeof(int)))
++          res = EFAULT;
++      else
++          res = ESUCCESS;
++
++      break;
++    }
++
++    default:
++      res = EINVAL;
++      break;
++    }
++
++    return ((res == 0) ? 0 : -res);
++}
++
++#if defined(CONFIG_PPC64) || defined(CONFIG_SPARC64) || defined(CONFIG_X86_64)
++static int
++rms_ioctl32 (unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file)
++{
++    int res;
++
++    switch (cmd)
++    {
++    case RMSIO_GETPRGID32:
++    {
++      RMSIO_GETPRGID_STRUCT32 args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (args)))
++          return (-EFAULT);
++
++      res = rms_getprgid(args.pid, (int *)(unsigned long) args.idptr);
++      break;
++    }
++      
++    case RMSIO_GETCAP32:
++    {
++      RMSIO_GETCAP_STRUCT32 args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (args)))
++          return (-EFAULT);
++
++      res = rms_getcap(args.index, (ELAN_CAPABILITY *)(unsigned long) args.capptr);
++      break;
++    }
++
++    default:
++      return (sys_ioctl (fd, cmd, arg));
++    }
++
++    return ((res == 0) ? 0 : -res);
++}
++#endif
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/fs/exec.c
+===================================================================
+--- linux-2.4.21.orig/fs/exec.c        2005-06-01 22:58:09.044063984 -0400
++++ linux-2.4.21/fs/exec.c     2005-06-01 23:12:54.704423144 -0400
+@@ -51,6 +51,7 @@
+ #ifdef CONFIG_KMOD
+ #include <linux/kmod.h>
+ #endif
++#include <linux/ptrack.h>
+ int core_uses_pid;
+ char core_pattern[65] = "core";
+@@ -1125,6 +1126,10 @@
+       if (retval < 0) 
+               goto out; 
++
++      /* Notify any ptrack callbacks of the process exec */
++      ptrack_call_callbacks (PTRACK_PHASE_EXEC, NULL);
++      
+       retval = search_binary_handler(&bprm,regs);
+       if (retval >= 0)
+               /* execve success */
+Index: linux-2.4.21/include/elan/bitmap.h
+===================================================================
+--- linux-2.4.21.orig/include/elan/bitmap.h    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan/bitmap.h 2005-06-01 23:12:54.704423144 -0400
+@@ -0,0 +1,74 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __QSNET_BITMAP_H
++#define __QSNET_BITMAP_H
++
++#ident "$Id: bitmap.h,v 1.5 2004/01/20 17:32:15 david Exp $"
++/*      $Source: /cvs/master/quadrics/elanmod/modsrc/bitmap.h,v $ */
++
++typedef unsigned int                  bitmap_t;
++
++#define BT_NBIPUL                     32                      /* n bits per bitmap_t */
++#define BT_ULSHIFT                    5                       /* log 2 BT_NBIPUL to extract word index */
++#define BT_ULMASK                     0x1f                    /* to extract bit index */
++
++#define BT_WIM(bitmap,bitindex)               ((bitmap)[(bitindex) >> BT_ULSHIFT])            /* word in map */
++#define BT_BIW(bitindex)              (1 << ((bitindex) & BT_ULMASK))         /* bit in word */
++
++/* BT_BITOUL -- n bits to n words */
++#define BT_BITOUL(nbits)              (((nbits) + BT_NBIPUL -1) / BT_NBIPUL)
++
++#define BT_TEST(bitmap,bitindex)      ((BT_WIM((bitmap), (bitindex)) & BT_BIW(bitindex)) ? 1 : 0)
++#define BT_SET(bitmap,bitindex)               do { BT_WIM((bitmap), (bitindex)) |= BT_BIW(bitindex); } while (0)
++#define BT_CLEAR(bitmap,bitindex)     do { BT_WIM((bitmap), (bitindex)) &= ~BT_BIW(bitindex); } while (0)
++
++/* return first free bit in the bitmap, or -1 for failure */
++extern int  bt_freebit (bitmap_t *bitmap, int nbits);
++
++/* return the index of the lowest set bit in the bitmap or -1 for failure */
++extern int bt_lowbit (bitmap_t *bitmap, int nbits);
++
++/* return the index of the next set/clear bit in the bitmap or -1 for failure */
++extern int bt_nextbit (bitmap_t *bitmap, int nbits, int last, int isset);
++
++/* copy/zero/fill/compare a bit map */
++extern void bt_copy (bitmap_t *a, bitmap_t *b, int nbits);
++extern void bt_zero (bitmap_t *a, int nbits);
++extern void bt_fill (bitmap_t *a, int nbits);
++extern int  bt_cmp (bitmap_t *a, bitmap_t *b, int nbits);
++
++/* intersect bitmap 'a' with bitmap 'b' and return in 'a' */
++extern void bt_intersect (bitmap_t *a, bitmap_t *b, int nbits);
++
++/* remove/add bitmap 'b' from bitmap 'a' */
++extern void bt_remove (bitmap_t *a, bitmap_t *b, int nbits);
++extern void bt_add (bitmap_t *a, bitmap_t *b, int nbits);
++
++/* check whether bitmap 'a' spans bitmap 'b' */
++extern int  bt_spans (bitmap_t *a, bitmap_t *b, int nbits);
++
++/* copy [base,base+nbits-1] from 'a' to 'b' */
++extern void bt_subset (bitmap_t *a, bitmap_t *b, int base, int nbits);
++
++/* find bits clear in 'a' and set in 'b', put result in 'c' */
++extern void bt_up (bitmap_t *a, bitmap_t *b, bitmap_t *c, int nbits);
++
++/* find bits set in 'a' and clear in 'b', put result in 'c' */
++extern void bt_down (bitmap_t *a, bitmap_t *b, bitmap_t *c, int nbits);
++
++/* return number of bits set in bitmap */
++extern int  bt_nbits (bitmap_t *a, int nbits);
++
++
++#endif /* __QSNET_BITMAP_H */
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.21/include/elan/capability.h
+===================================================================
+--- linux-2.4.21.orig/include/elan/capability.h        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan/capability.h     2005-06-01 23:12:54.705422992 -0400
+@@ -0,0 +1,197 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Limited.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: capability.h,v 1.16 2004/07/20 10:15:33 david Exp $"
++/*      $Source: /cvs/master/quadrics/elanmod/modsrc/capability.h,v $*/
++
++#ifndef __ELAN_CAPABILITY_H
++#define __ELAN_CAPABILITY_H
++
++#include <elan/bitmap.h>
++
++/* Maximum number of rails */
++#define ELAN_MAX_RAILS          (31)
++/* Maximum number of virtual processes we support */
++#define ELAN_MAX_VPS          (16384)
++
++/* Number of words in a bitmap capability */
++#define ELAN_BITMAPSIZE               BT_BITOUL(ELAN_MAX_VPS)
++
++/* Guaranteed invalid values */
++#define ELAN_INVALID_PROCESS  (0x7fffffff)            /* A GUARANTEED invalid process # */
++#define ELAN_INVALID_NODE     (0xFFFF)
++#define ELAN_INVALID_CONTEXT  (0xFFFF)
++
++/* Number of values in a user key */
++#define ELAN_USERKEY_ENTRIES  4
++
++typedef void * ELAN_CAP_OWNER;
++
++/* 
++ * When used in userspace this is relative to the base of
++ * the capabality but is an absolute location for kernel space.
++ */
++typedef struct elan_location
++{
++      unsigned short loc_node;
++      unsigned short loc_context;
++} ELAN_LOCATION;
++
++typedef struct elan_userkey
++{
++      unsigned        key_values[ELAN_USERKEY_ENTRIES];
++} ELAN_USERKEY;
++
++typedef struct elan_capability
++{
++      ELAN_USERKEY    cap_userkey;                            /* User defined protection */
++
++      int             cap_version;                            /* Version number */
++      unsigned short  cap_type;                               /* Capability Type */
++      unsigned short  cap_spare;                              /* spare was cap_elan_type */
++
++      int             cap_lowcontext;                         /* low context number in block */
++      int             cap_highcontext;                        /* high context number in block */
++      int             cap_mycontext;                          /* my context number */
++    
++      int             cap_lownode;                            /* low elan id of group */
++      int             cap_highnode;                           /* high elan id of group */
++
++      unsigned int    cap_railmask;                           /* which rails this capability is valid for */
++      
++      bitmap_t        cap_bitmap[ELAN_BITMAPSIZE];            /* Bitmap of process to processor translation */
++} ELAN_CAPABILITY;
++
++#define ELAN_CAP_UNINITIALISED                (-1)
++
++#define ELAN_CAP_VERSION_NUMBER               (0x00010002)
++
++#define ELAN_CAP_NUM_NODES(cap)               ((cap)->cap_highnode - (cap)->cap_lownode + 1)
++#define ELAN_CAP_NUM_CONTEXTS(cap)    ((cap)->cap_highcontext - (cap)->cap_lowcontext + 1)
++
++/* using or defining our own MIN/MAX had confilicts with dunix so we define ELAN_ ones */
++#define ELAN_MIN(a,b) ((a) > (b) ? (b) : (a))
++#define ELAN_MAX(a,b) ((a) > (b) ? (a) : (b))
++#define ELAN_CAP_BITMAPSIZE(cap)      (ELAN_MAX (ELAN_MIN (ELAN_CAP_NUM_NODES(cap) * ELAN_CAP_NUM_CONTEXTS(cap), ELAN_MAX_VPS), 0))
++
++#define ELAN_CAP_SIZE(cap)            (offsetof (ELAN_CAPABILITY, cap_bitmap[BT_BITOUL(ELAN_CAP_BITMAPSIZE(cap))]))
++#define ELAN_CAP_ENTRIES(cap)           (((cap)->cap_type & ELAN_CAP_TYPE_NO_BITMAP) ? ELAN_CAP_BITMAPSIZE((cap)) : bt_nbits((cap)->cap_bitmap, ELAN_CAP_BITMAPSIZE((cap))))
++
++#define ELAN_CAP_IS_RAIL_SET(cap,rail)  ((cap)->cap_railmask & (1<<rail))
++
++#define ELAN_CAP_KEY_MATCH(cap1,cap2) ((cap1)->cap_userkey.key_values[0] == (cap2)->cap_userkey.key_values[0] && \
++                                       (cap1)->cap_userkey.key_values[1] == (cap2)->cap_userkey.key_values[1] && \
++                                       (cap1)->cap_userkey.key_values[2] == (cap2)->cap_userkey.key_values[2] && \
++                                       (cap1)->cap_userkey.key_values[3] == (cap2)->cap_userkey.key_values[3])
++
++#define ELAN_CAP_TYPE_MATCH(cap1,cap2)  ((cap1)->cap_version           == (cap2)->cap_version           && \
++                                       (cap1)->cap_type              == (cap2)->cap_type)
++
++#define ELAN_CAP_GEOM_MATCH(cap1,cap2)        ((cap1)->cap_lowcontext        == (cap2)->cap_lowcontext        && \
++                                       (cap1)->cap_highcontext       == (cap2)->cap_highcontext       && \
++                                       (cap1)->cap_lownode           == (cap2)->cap_lownode           && \
++                                       (cap1)->cap_highnode          == (cap2)->cap_highnode          && \
++                                         (cap1)->cap_railmask          == (cap2)->cap_railmask          && \
++                                       !bcmp (&(cap1)->cap_bitmap[0], &(cap2)->cap_bitmap[0],            \
++                                              BT_BITOUL(ELAN_CAP_BITMAPSIZE(cap1)*sizeof(bitmap_t))))
++
++#define ELAN_CAP_MATCH(cap1,cap2)     (ELAN_CAP_KEY_MATCH (cap1, cap2)  && \
++                                       ELAN_CAP_TYPE_MATCH (cap1, cap2) && \
++                                       ELAN_CAP_GEOM_MATCH (cap1, cap2))
++
++#define ELAN_CAP_VALID_MYCONTEXT(cap)   (    ((cap)->cap_lowcontext  != ELAN_CAP_UNINITIALISED)     \
++                                        && ((cap)->cap_mycontext   != ELAN_CAP_UNINITIALISED)     \
++                                        && ((cap)->cap_highcontext != ELAN_CAP_UNINITIALISED)     \
++                                        && ((cap)->cap_lowcontext <= (cap)->cap_mycontext)        \
++                                        && ((cap)->cap_mycontext <= (cap)->cap_highcontext)) 
++
++/*
++ * Definitions for type 
++ */
++#define ELAN_CAP_TYPE_BLOCK           1               /* Block distribution */
++#define ELAN_CAP_TYPE_CYCLIC          2               /* Cyclic distribution */
++#define ELAN_CAP_TYPE_KERNEL          3               /* Kernel capability */
++
++#define ELAN_CAP_TYPE_MASK            (0xFFF)         /* Mask for type */
++
++/* OR these bits in for extra features */
++#define ELAN_CAP_TYPE_HWTEST          (1 << 12)       /* Hardware test capability type */
++#define ELAN_CAP_TYPE_MULTI_RAIL      (1 << 13)       /* "new" multi rail capability */
++#define ELAN_CAP_TYPE_NO_BITMAP               (1 << 14)       /* don't use bit map */
++#define ELAN_CAP_TYPE_BROADCASTABLE   (1 << 15)       /* broadcastable */
++
++
++extern void          elan_nullcap     (ELAN_CAPABILITY *cap);
++extern char         *elan_capability_string (ELAN_CAPABILITY *cap, char *str);
++extern ELAN_LOCATION elan_vp2location (unsigned process, ELAN_CAPABILITY *cap);
++extern int           elan_location2vp (ELAN_LOCATION location, ELAN_CAPABILITY *cap);
++extern int           elan_nvps        (ELAN_CAPABILITY *cap);
++extern int           elan_nlocal      (int node, ELAN_CAPABILITY *cap);
++extern int           elan_maxlocal    (ELAN_CAPABILITY *cap);
++extern int           elan_localvps    (int node, ELAN_CAPABILITY *cap, int *vps, int size);
++extern int           elan_nrails      (ELAN_CAPABILITY *cap);
++extern int           elan_rails       (ELAN_CAPABILITY *cap, int *rails);
++extern int           elan_cap_overlap (ELAN_CAPABILITY *cap1, ELAN_CAPABILITY *cap2);
++
++/*
++ * capability creation/access fns provide for running
++ * new libelan code on old OS releases
++ */
++extern int elan_lowcontext(ELAN_CAPABILITY *cap);
++extern int elan_mycontext(ELAN_CAPABILITY *cap);
++extern int elan_highcontext(ELAN_CAPABILITY *cap);
++extern int elan_lownode(ELAN_CAPABILITY *cap);
++extern int elan_highnode(ELAN_CAPABILITY *cap);
++extern int elan_captype(ELAN_CAPABILITY *cap);
++extern int elan_railmask(ELAN_CAPABILITY *cap);
++
++extern int elan_getenvCap (ELAN_CAPABILITY *cap, int index);
++extern ELAN_CAPABILITY *elan_createCapability(void);
++extern ELAN_CAPABILITY *elan_copyCapability(ELAN_CAPABILITY *from, int ctxShift);
++extern int elan_generateCapability(char *string);
++
++typedef struct elan_cap_struct
++{
++      ELAN_CAP_OWNER   owner;
++      ELAN_CAPABILITY  cap;
++
++      unsigned int     attached; /* count of people attached */
++      unsigned int     active;   /* ie not being destroyed   */
++} ELAN_CAP_STRUCT;
++
++#if ! defined(__KERNEL__)
++extern void          elan_get_random_key(ELAN_USERKEY *key);
++extern int           elan_prefrails(ELAN_CAPABILITY *cap, int *pref, int nvp);
++#endif
++
++#if defined(__KERNEL__)
++/* capability.c */
++extern int elan_validate_cap  (ELAN_CAPABILITY *cap);
++extern int elan_validate_map  (ELAN_CAPABILITY *cap, ELAN_CAPABILITY *map);
++
++extern int elan_create_cap  (ELAN_CAP_OWNER owner, ELAN_CAPABILITY *cap);
++extern int elan_destroy_cap (ELAN_CAP_OWNER owner, ELAN_CAPABILITY *cap);
++extern int elan_create_vp   (ELAN_CAP_OWNER owner, ELAN_CAPABILITY *cap, ELAN_CAPABILITY *map);
++extern int elan_destroy_vp  (ELAN_CAP_OWNER owner, ELAN_CAPABILITY *cap, ELAN_CAPABILITY *map);
++
++typedef       void (*ELAN_DESTROY_CB)(void *args, ELAN_CAPABILITY *cap, ELAN_CAPABILITY *map);
++
++extern int elan_attach_cap  (ELAN_CAPABILITY *cap, unsigned int rail, void *args, ELAN_DESTROY_CB callback);
++extern int elan_detach_cap  (ELAN_CAPABILITY *cap, unsigned int rail);
++
++extern int elan_get_caps    (uint *number_of_results, uint array_size, ELAN_CAP_STRUCT *caps);
++extern int elan_cap_dump    (void);
++#endif /* __KERNEL__ */
++
++
++#endif /* __ELAN_CAPABILITY_H */
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.21/include/elan/cm.h
+===================================================================
+--- linux-2.4.21.orig/include/elan/cm.h        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan/cm.h     2005-06-01 23:12:54.706422840 -0400
+@@ -0,0 +1,412 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN_CM_H
++#define __ELAN_CM_H
++
++#ident "@(#)$Id: cm.h,v 1.14.2.1 2004/11/12 10:54:50 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/cm.h,v $*/
++
++#include <elan/statemap.h>
++
++#if defined(DIGITAL_UNIX)
++/*
++ * On Tru64 - SMP doesn't mean Symmetric - cpu 0 is a master cpu and is responsible
++ * for handling all PCI interrupts and "funneled" operations.  When a kernel thread
++ * is made runnable, the scheduler will choose which cpu it will run on at that time,
++ * and will only execute a higher priority thread from another cpu's run queue when 
++ * it becomes totally idle (apparently also including user processes).  Also the 
++ * assert_wait_mesg_timo function uses a per-cpu timeout - these can only get executed
++ * at "preemptable" places - so again have no guarantee on when they will execute if
++ * they happen to be queued on a "hogged" cpu. The combination of these mean that the Tru64
++ * is incapable of scheduling a high priority kernel  thread within a deterministic time
++ * of when it should have become runnable - wonderfull.
++ *
++ * Hence the solution Compaq have proposed it to schedule a timeout onto all of the
++ * cpu's timeouts lists at the maximum frequency that we could want to execute code,
++ * then to handle the scheduling of work between these ourselves.  With a bit of luck
++ * ..... at least one cpu will be sufficiently unloaded to allow us to get a chance
++ * to do our important work.
++ *
++ * However ..... this still is not reliable, since timeouts under Tru64 are still 
++ * only run when the currently running kernel thread "co-operates" by calling one
++ * of a number of functions which is permitted to run the "lwc"s AND is not holding
++ * any spinlocks AND is running ai IPL 0.   However Compaq are unable to provide
++ * any upper limit on the time between the "lwc"'s being run and so it is possible
++ * for all 4 cpus to not run them for an unbounded time.
++ *
++ * The solution proposed is to use the RM_TEMP_BACKDOOR hook which was added to 
++ * hardclock() to "solve" this problem for Memory Channel.  However, since it
++ * is called within the clock interrupt it is not permissible to aquire any
++ * spinlocks, nor to run for "too long".  This means that it is not possible to
++ * call the heartbeat algorithm from this hook.  
++ *
++ * Our solution to these limitations is to use the hook to cause an elan interrupt 
++ * to be delivered, by issueing a mis-aligned SetEvent command - this causes the device 
++ * to trap and ep_cprocTrap() can then run the heartbeat code.  However there is a lock 
++ * order violation between the elan_dev::IntrLock and ep_dev::Lock, so we have to 
++ * use a trylock and if we fail, then hope that when the interrupt is delievered again
++ * some time later we will succeed.
++ *
++ * However this only works if the kernel is able to respond to the Elan interrupt,
++ * so we panic inside the RM_TEMP_BACKDOOR hook if the SetEvent's interrupt has
++ * not been taken for more than an CM_TIMER_SCHEDULE_TIMEOUT interval.
++ *
++ * In fact this is exactly the mechanism that other operating systems use to
++ * execute timeouts, since the hardclock interrupt posts a low priority 
++ * "soft interrupt" which "pre-eempts" the currently running thread and then
++ * executes the timeouts.To block timeouts you use splsoftclock() the same as 
++ * in Tru64.
++ */
++#define PER_CPU_TIMEOUT                       TRUE
++#endif
++
++
++#define CM_SGMTS_PER_LEVEL            8                       /* maximum nodes in each segment */
++#define CM_MAX_LEVELS                 6                       /* maximum depth of tree */
++
++/* message buffers/dmas/events etc */
++#define CM_NUM_NODE_MSG_BUFFERS               (CM_MAX_LEVELS * CM_SGMTS_PER_LEVEL) /* subordinates and leader */
++#define CM_NUM_SPARE_MSG_BUFFERS      8                       /* spare msg buffers for non-connected nodes */
++#define CM_NUM_MSG_BUFFERS            (CM_NUM_NODE_MSG_BUFFERS + CM_NUM_SPARE_MSG_BUFFERS)
++
++#define CM_INPUTQ_ENTRIES             128                     /* # entries in input queue */
++
++#define CM_PERIODIC_DISCOVER_INTERVAL (5000)          /* 5s (infrequent resolution of established leader conflicts) */
++#define CM_URGENT_DISCOVER_INTERVAL   (50)            /* 0.05s (more frequently than heartbeats 'cause they don't retry) */
++#define CM_HEARTBEAT_INTERVAL         (125)           /* 0.125s */
++#define CM_TIMER_SCHEDULE_TIMEOUT     (4000)          /* 4s     Maximum time before a timer that's secheduled to run gets to run (eg blocked in interrupt handlers etc) */
++#define CM_THREAD_SCHEDULE_TIMEOUT    (30000)         /* 30s    Maximum time before a thread that's scheduled to run gets to run */
++#define CM_THREAD_RUNNING_TIMEOUT     (30000)         /* 30s    Don't expect the manager thread to be running longer than this */
++
++#ifdef PER_CPU_TIMEOUT
++#define CM_PERCPU_TIMEOUT_INTERVAL    (50)            /* 0.05s (must be less than all above intervals) */
++#define CM_PACEMAKER_INTERVAL         (500)           /* 0.05s */
++
++#define CM_HEARTBEAT_OVERDUE          (250)           /* 0.25s Maximum time a timeout can be overdue before taking extreme action */
++#endif
++
++#define CM_P2P_DMA_RETRIES            31
++
++/* We expect at least 1 point-to-point message in CM_P2P_MSG_RETRIES
++ * attempts to send one to be successfully received */
++#define CM_P2P_MSG_RETRIES            8
++
++/* We expect at least 1 broadcast message in CM_BCAST_MSG_RETRIES attempts
++ * to send one to be successfully received. */
++#define CM_BCAST_MSG_RETRIES          40
++
++/* Heartbeat timeout allows for a node stalling and still getting its
++ * heartbeat. The 2 is to allow for unsynchronised polling times. */
++#define CM_HEARTBEAT_TIMEOUT          (CM_TIMER_SCHEDULE_TIMEOUT + (2 + CM_P2P_MSG_RETRIES) * CM_HEARTBEAT_INTERVAL)
++
++/* Discover timeout must be > CM_HEARTBEAT_TIMEOUT to guarantee that people
++ * who don't see discovery are considered dead by their leader.  This
++ * ensures that by the time a node "discovers" it is a leader of a segment,
++ * the previous leader of that segment will have been deemed to be dead by
++ * its the parent segment's leader */
++#define CM_DISCOVER_TIMEOUT           (CM_TIMER_SCHEDULE_TIMEOUT + (2 + CM_BCAST_MSG_RETRIES) * CM_URGENT_DISCOVER_INTERVAL)
++
++#define CM_WAITING_TIMEOUT            (CM_DISCOVER_TIMEOUT * 100)
++
++/*
++ * Convert all timeouts specified in mS into "ticks"
++ */
++#define MSEC2TICKS(MSEC)              (((MSEC)*HZ)/1000)
++
++
++/* statemap entry */
++typedef struct cm_state_entry
++{
++    int16_t           level;                  /* cluster level to apply to */
++    int16_t         offset;                   /* from statemap_findchange() */
++    uint16_t          seg[BT_NBIPUL/16];      /* ditto */
++} CM_STATEMAP_ENTRY;
++
++/* offset is >= 0 for a change to apply and */
++#define STATEMAP_NOMORECHANGES        (-1)            /* end of a set of updates */
++#define STATEMAP_RESET                (-2)            /* reset the target map */
++#define STATEMAP_NOOP         (-3)            /* null token */
++
++/* CM message format */
++typedef int8_t        CM_SEQ;                         /* heartbeat sequence numbers; at least 2 bits, signed */
++
++/*
++ * The message header is received into the last 64 byte block of 
++ * the input queue and the Version *MUST* be the last word of the 
++ * block to ensure that we can see that the whole of the message
++ * has reached main memory after we've seen the input queue pointer
++ * have been updated.
++ */
++typedef struct ep_cm_hdr
++{
++    uint32_t         Pad0;
++    uint32_t         Pad1;
++
++    uint8_t          Type;
++    uint8_t          Level;
++    CM_SEQ           Seq;                     /* precision at least 2 bits each*/
++    CM_SEQ           AckSeq;
++    
++    uint16_t         NumMaps;
++    uint16_t         MachineId;
++
++    uint16_t         NodeId;
++    uint16_t         Checksum;
++
++    uint32_t           Timestamp;
++    uint32_t           ParamHash;
++    uint32_t         Version;
++} CM_HDR;
++
++#define CM_HDR_SIZE       sizeof (CM_HDR)
++
++typedef struct cm_msg
++{
++    union {
++      CM_STATEMAP_ENTRY   Statemaps[1];               /* piggy-backed statemap updates start here */
++      uint8_t             Space[EP_SYSTEMQ_MSG_MAX - CM_HDR_SIZE];
++    } Payload;
++    
++    CM_HDR                Hdr;
++} CM_MSG;
++
++/* The maximum number of statemap entries that can fit within an EP_CM_MSG_BUFFER */
++#define CM_MSG_MAXMAPS                (offsetof (CM_MSG, Hdr) / sizeof (CM_STATEMAP_ENTRY))
++#define CM_MSG_MAP(mapno)     (CM_MSG_MAXMAPS - (mapno) - 1)
++
++/* The actual special message base & size, including 'nmaps' piggy-backed statemap entries */
++#define CM_MSG_BASE(nmaps)    (nmaps == 0 ? offsetof (CM_MSG, Hdr) : offsetof (CM_MSG, Payload.Statemaps[CM_MSG_MAXMAPS - nmaps]))
++#define CM_MSG_SIZE(nmaps)    (sizeof (CM_MSG) - CM_MSG_BASE(nmaps))
++
++#define CM_MSG_VERSION                                0xcad00005
++#define CM_MSG_TYPE_RESOLVE_LEADER            0
++#define CM_MSG_TYPE_DISCOVER_LEADER           1
++#define CM_MSG_TYPE_NOTIFY                    2
++#define CM_MSG_TYPE_DISCOVER_SUBORDINATE      3
++#define CM_MSG_TYPE_IMCOMING                  4
++#define CM_MSG_TYPE_HEARTBEAT                 5
++#define CM_MSG_TYPE_REJOIN                    6
++
++/* CM machine segment */
++typedef struct cm_sgmtMaps
++{
++    u_char       InputMapValid;                       /* Input map has been set */
++    u_char       OutputMapValid;              /* Output map has been set */
++    u_char       SentChanges;                 /* got an outstanding STATEMAP_NOMORECHANGES to send */
++    statemap_t  *OutputMap;                   /* state to send */
++    statemap_t  *InputMap;                    /* state received */
++    statemap_t  *CurrentInputMap;             /* state being received */
++} CM_SGMTMAPS;
++
++typedef struct cm_sgmt
++{
++   u_char       State;
++   u_char       SendMaps;
++   u_char       MsgAcked;
++   CM_SEQ     MsgSeq;
++   CM_SEQ     AckSeq;
++   u_int      NodeId;
++   long               UpdateTick;
++   long               WaitingTick;
++   uint32_t   Timestamp;
++   CM_SGMTMAPS  Maps[CM_MAX_LEVELS];          /* Maps[i] == state for cluster level i */
++   u_short      MsgNumber;                    /* msg buffer to use */
++   u_short    NumMaps;                        /* # maps in message buffer */
++   u_short      Level;
++   u_short      Sgmt;
++} CM_SGMT;
++
++#define CM_SGMT_ABSENT                0               /* no one there at all */
++#define CM_SGMT_WAITING               1               /* waiting for subtree to connect */
++#define CM_SGMT_COMING                2               /* expecting a subtree to reconnect */
++#define CM_SGMT_PRESENT               3               /* connected */
++
++typedef struct cm_level
++{
++    int              SwitchLevel;
++    u_int            MinNodeId;
++    u_int              NumNodes;
++    u_int              NumSegs;
++    u_int              MySgmt;
++   
++    /* SubordinateMap[i] == OR of all subordinate maps on this level and down for cluster level i */
++    u_char             SubordinateMapValid[CM_MAX_LEVELS];
++    statemap_t        *SubordinateMap[CM_MAX_LEVELS];
++
++    /* maps/flags for this cluster level */
++    u_int              Online:1;                              /* I've gone online (seen myself running) */
++    u_int            Restarting:1;                            /* driving my owm restart bit */
++    u_char           OfflineReasons;                          /* forced offline by broadcast */
++
++    u_char             GlobalMapValid;
++    u_char             SubTreeMapValid;
++    u_long           Connected;
++
++    statemap_t        *LocalMap;              /* state bits I drive */
++    statemap_t        *SubTreeMap;            /* OR of my and my subtree states */
++    statemap_t        *GlobalMap;             /* OR of all node states */
++    statemap_t        *LastGlobalMap;         /* last map I saw */
++    statemap_t        *TmpMap;                        /* scratchpad */
++
++    CM_SGMT          Sgmts[CM_SGMTS_PER_LEVEL];
++} CM_LEVEL;
++
++#define CM_ROLE_LEADER_CANDIDATE      0
++#define CM_ROLE_LEADER                        1
++#define CM_ROLE_SUBORDINATE           2
++
++/* global status bits */
++#define CM_GSTATUS_STATUS_MASK                0x03    /* bits nodes drive to broadcast their status */
++#define CM_GSTATUS_ABSENT             0x00    /* Off the network */
++#define CM_GSTATUS_STARTING           0x01    /* I'm waiting for everyone to see me online */
++#define CM_GSTATUS_RUNNING              0x03  /* up and running */
++#define CM_GSTATUS_CLOSING            0x02    /* I'm waiting for everyone to see me offline */
++
++#define CM_GSTATUS_ACK_MASK           0x0c    /* bits node drive to ack other status */
++#define CM_GSTATUS_MAY_START          0x04    /* Everyone thinks I may not start */
++#define CM_GSTATUS_MAY_RUN            0x08    /* Everyone thinks I may not run */
++
++#define CM_GSTATUS_RESTART            0x10    /* Someone thinks I should restart */
++#define CM_GSTATUS_BITS                       5
++
++#define CM_GSTATUS_BASE(node)         ((node) * CM_GSTATUS_BITS)
++
++#if defined(PER_CPU_TIMEOUT)
++typedef struct cm_timeout_data
++{
++    long              ScheduledAt;                            /* lbolt timeout was scheduled to run at */
++
++    unsigned long       EarlyCount;                           /* # times run early than NextRun */
++    unsigned long     MissedCount;                            /* # times run on time - but someone else was running it */
++    unsigned long       WastedCount;                          /* # times we failed to get the spinlock */
++    unsigned long     WorkCount;                              /* # times we're the one running */
++
++    unsigned long     WorstDelay;                             /* worst scheduling delay */
++    unsigned long     BestDelay;                              /* best scheduling delay */
++
++    unsigned long     WorstLockDelay;                         /* worst delay before getting rail->Lock */
++
++    unsigned long     WorstHearbeatDelay;                     /* worst delay before calling DoHeartbeatWork */
++} CM_TIMEOUT_DATA;
++#endif
++
++typedef struct cm_rail
++{
++    EP_RAIL         *Rail;                                    /* rail we're associated with */
++    struct list_head   Link;                                  /*   and linked on the CM_SUBSYS */
++
++    uint32_t         ParamHash;                               /* hash of critical parameters */
++    uint32_t           Timestamp;
++    long             DiscoverStartTick;                       /* when discovery start */
++
++    unsigned int       NodeId;                                        /* my node id */
++    unsigned int       NumNodes;                              /*   and number of nodes */
++    unsigned int       NumLevels;                             /* number of levels computed from machine size */
++    int                      BroadcastLevel;
++    long             BroadcastLevelTick;
++    unsigned int       TopLevel;                              /* level at which I'm not a leader */
++    unsigned char      Role;                                  /* state at TopLevel */
++
++    EP_INPUTQ       *PolledQueue;                             /* polled input queue */
++    EP_INPUTQ       *IntrQueue;                               /* intr input queue */
++    EP_OUTPUTQ              *MsgQueue;                                /* message  */
++    unsigned int       NextSpareMsg;                          /* next "spare" message buffer to use */
++
++    EP_CM_RAIL_STATS   Stats;                                 /* statistics */
++
++    kmutex_t         Mutex;
++    spinlock_t               Lock;
++    
++    long             NextHeartbeatTime;                       /* next time to check/send heartbeats */
++    long             NextDiscoverTime;                        /* next time to progress discovery  */
++    long             NextRunTime;                             /* the earlier of the above two or intr requires inputq poll*/
++
++    unsigned int       OfflineReasons;                                /* forced offline by procfs/manager thread stuck */
++
++#if defined(PER_CPU_TIMEOUT)
++    spinlock_t               HeartbeatTimeoutsLock;                   /* spinlock to sequentialise per-cpu timeouts */
++    long             HeartbeatTimeoutsStarted;                /* bitmap of which timeouts have started */
++    long             HeartbeatTimeoutsStopped;                /* bitmap of which timeouts have stopped */
++    long             HeartbeatTimeoutsShouldStop;             /* flag to indicate timeouts should stop */
++    kcondvar_t               HeartbeatTimeoutsWait;                   /* place to sleep waiting for timeouts to stop */
++    long             HeartbeatTimeoutRunning;                 /* someone is running the timeout - don't try for the lock */
++
++    long             HeartbeatTimeoutOverdue;                 /* heartbeat seen as overdue - interrupt requested */
++
++    CM_TIMEOUT_DATA   *HeartbeatTimeoutsData;                 /* per timeout data */
++#else
++    struct timer_list  HeartbeatTimer;                                /* timer for heartbeat/discovery */
++#endif
++
++    CM_LEVEL           Levels[CM_MAX_LEVELS];
++} CM_RAIL;
++
++/* OfflineReasons (both per-rail and  */
++#define CM_OFFLINE_BROADCAST          (1 << 0)
++#define CM_OFFLINE_PROCFS             (1 << 1)
++#define CM_OFFLINE_MANAGER            (1 << 2)
++
++typedef struct cm_subsys
++{
++    EP_SUBSYS         Subsys;
++    CM_RAIL            *Rails[EP_MAX_RAILS];
++} CM_SUBSYS;
++
++extern int  MachineId;
++
++extern void cm_node_disconnected (EP_RAIL *rail, unsigned nodeId);
++extern void cm_restart_node (EP_RAIL *rail, unsigned nodeId);
++extern void cm_restart_comms (CM_RAIL *cmRail);
++extern int  cm_init (EP_SYS *sys);
++
++extern void DisplayRail(EP_RAIL *rail);
++extern void DisplaySegs (EP_RAIL *rail);
++extern void DisplayStatus (EP_RAIL *rail);
++
++typedef struct proc_private
++{
++    struct nodeset_private *pr_next;
++    EP_RAIL                *pr_rail;
++    char                 *pr_data;
++    int                     pr_data_len;
++    unsigned              pr_off;
++    unsigned              pr_len;
++    DisplayInfo             pr_di;
++} PROC_PRIVATE;
++
++extern void    proc_character_fill (long mode, char *fmt, ...);
++extern int     proc_release (struct inode *inode, struct file *file);
++extern ssize_t proc_read (struct file *file, char *buf, size_t count, loff_t *ppos);
++
++
++extern void DisplayNodeMaps  (DisplayInfo *di, CM_RAIL *cmRail);
++extern void DisplayNodeSgmts (DisplayInfo *di, CM_RAIL *cmRail);
++extern void DisplayRailDo    (DisplayInfo *di, EP_RAIL *rail);
++
++extern int    cm_read_cluster(EP_RAIL *rail,char *page);
++extern void   cm_force_offline (EP_RAIL *rail, int offline, unsigned int reason);
++
++extern int    cm_svc_indicator_set      (EP_RAIL *rail, int svc_indicator);
++extern int    cm_svc_indicator_clear    (EP_RAIL *rail, int svc_indicator);
++extern int    cm_svc_indicator_is_set   (EP_RAIL *rail, int svc_indicator, int nodeId);
++extern int    cm_svc_indicator_bitmap   (EP_RAIL *rail, int svc_indicator, bitmap_t * bitmap, int low, int nnodes);
++
++/* cm_procfs.c */
++extern void   cm_procfs_init (CM_SUBSYS *subsys);
++extern void   cm_procfs_fini (CM_SUBSYS *subsys);
++extern void   cm_procfs_rail_init (CM_RAIL *rail);
++extern void   cm_procfs_rail_fini (CM_RAIL *rail);
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++#endif /* __ELAN_CM_H */
++
+Index: linux-2.4.21/include/elan/compat.h
+===================================================================
+--- linux-2.4.21.orig/include/elan/compat.h    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan/compat.h 2005-06-01 23:12:54.706422840 -0400
+@@ -0,0 +1,23 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: compat.h,v 1.1 2003/12/03 13:18:48 david Exp $ $Name: QSNETMODULES-4-30_20050128 $"
++/*      $Source: /cvs/master/quadrics/elanmod/modsrc/compat.h,v $*/
++
++#ifndef __ELAN_COMPAT_H
++#define __ELAN_COMPAT_H
++
++#define ELANMOD_STATS_MAP     ELAN_STATS_MAP
++
++#endif  /* __ELAN_COMPAT_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/include/elan/device.h
+===================================================================
+--- linux-2.4.21.orig/include/elan/device.h    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan/device.h 2005-06-01 23:12:54.707422688 -0400
+@@ -0,0 +1,62 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Limited.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: device.h,v 1.5 2003/09/24 13:55:37 david Exp $"
++/*      $Source: /cvs/master/quadrics/elanmod/modsrc/device.h,v $*/
++
++#ifndef __ELAN_DEVICE_H
++#define __ELAN_DEVICE_H
++
++/* non-kernel headings */
++typedef unsigned int ELAN_DEV_IDX;
++
++#if defined(__KERNEL__)
++
++/* device callbacks */
++#define ELAN_DEV_OPS_VERSION ((u_int)1)
++
++typedef struct elan_dev_ops
++{
++      /* dev info */
++      int (*get_position)          (void *user_data, ELAN_POSITION *position);
++      int (*set_position)          (void *user_data, unsigned short nodeId, unsigned short numNodes);
++
++      /* cap */
++
++      u_int  ops_version;
++} ELAN_DEV_OPS;
++
++typedef struct elan_dev_struct
++{
++      struct list_head node;
++
++      ELAN_DEV_IDX     devidx;
++      ELAN_DEVINFO    *devinfo;
++      void            *user_data;
++      ELAN_DEV_OPS *ops;
++} ELAN_DEV_STRUCT;
++
++/* device.c */
++extern ELAN_DEV_IDX         elan_dev_register   (ELAN_DEVINFO    *devinfo, 
++                                                  ELAN_DEV_OPS *ops,
++                                                  void            *userdata);
++extern int                  elan_dev_deregister (ELAN_DEVINFO *devinfo);
++
++extern ELAN_DEV_STRUCT * elan_dev_find       (ELAN_DEV_IDX devidx);
++
++extern ELAN_DEV_STRUCT * elan_dev_find_byrail(unsigned short deviceid, unsigned rail);
++extern int                  elan_dev_dump       (void);
++
++#endif /* __KERNEL__ */
++
++#endif /* __ELAN_DEVICE_H */
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.21/include/elan/devinfo.h
+===================================================================
+--- linux-2.4.21.orig/include/elan/devinfo.h   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan/devinfo.h        2005-06-01 23:12:54.707422688 -0400
+@@ -0,0 +1,81 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Limited.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: devinfo.h,v 1.11 2004/03/12 14:27:39 david Exp $"
++/*      $Source: /cvs/master/quadrics/elanmod/modsrc/devinfo.h,v $*/
++
++#ifndef __ELAN_DEVINFO_H
++#define __ELAN_DEVINFO_H
++
++#define ELAN_MAX_LEVELS                       8                       /* maximum number of levels in switch network */
++
++typedef struct elan_position
++{
++      unsigned        pos_mode;                               /* mode we're operating in */
++      unsigned        pos_nodeid;                             /* port this device connected to */
++      unsigned        pos_levels;                             /* number of levels to top switch */
++      unsigned        pos_nodes;                              /* number of nodes in the machine */
++      unsigned        pos_random_disabled;                    /* levels at which "random" routing is not possible */
++      unsigned char   pos_arity[ELAN_MAX_LEVELS];             /* number of downlinks per switch level */
++} ELAN_POSITION;
++
++#define ELAN4_PARAM_PCI_PADDING_FLAGS         0               /* A bit field, representing good places to burst across the pci                      */
++#define ELAN4_PARAM_EVENT_COPY_WIN            1               /* The num of cmds when it becomes quicker to send via event copy than write directly */
++#define ELAN4_PARAM_WRITE_COMBINING           2               /* If set the device supports bursts accesses across the pci bus                      */
++#define ELAN4_PARAM_COUNT                     12
++
++typedef struct elan_params
++{
++      unsigned        values[ELAN4_PARAM_COUNT];
++} ELAN_PARAMS;
++
++/* values for pos_mode */
++#define ELAN_POS_UNKNOWN              0                       /* network position unknown */
++#define ELAN_POS_MODE_SWITCHED                1                       /* connected to a switch */
++#define ELAN_POS_MODE_LOOPBACK                2                       /* loopback connector */
++#define ELAN_POS_MODE_BACKTOBACK      3                       /* cabled back-to-back to another node */
++
++typedef struct elan_devinfo
++{
++      unsigned short  dev_vendor_id;                          /* pci vendor id */
++      unsigned short  dev_device_id;                          /* pci device id */
++      unsigned char   dev_revision_id;                        /* pci revision id */
++      unsigned char   dev_instance;                           /* device instance number */
++      unsigned char   dev_rail;                               /* device rail number */
++
++      unsigned short  dev_driver_version;                     /* device driver version */
++      unsigned short  dev_params_mask;                        /* mask for valid entries in dev_params array */
++      ELAN_PARAMS     dev_params;                             /* device parametization */
++
++      unsigned        dev_num_down_links_value;               /* MRH hint as to machine size NEEDS coding XXXXX */
++} ELAN_DEVINFO;
++
++#define PCI_VENDOR_ID_QUADRICS                0x14fc
++#define PCI_DEVICE_ID_ELAN3           0x0000
++#define   PCI_REVISION_ID_ELAN3_REVA  0x0000
++#define   PCI_REVISION_ID_ELAN3_REVB  0x0001
++#define PCI_DEVICE_ID_ELAN4           0x0001
++#define   PCI_REVISION_ID_ELAN4_REVA  0x0000
++#define   PCI_REVISION_ID_ELAN4_REVB  0x0001
++
++#if defined(__KERNEL__)
++/* devinfo.c */
++#include <elan/capability.h>
++#include <elan/device.h>
++extern int elan_get_devinfo  (ELAN_DEV_IDX devidx, ELAN_DEVINFO  *devinfo);
++extern int elan_get_position (ELAN_DEV_IDX devidx, ELAN_POSITION *position);
++extern int elan_set_position (ELAN_DEV_IDX devidx, unsigned short nodeId, unsigned short numNodes);
++#endif /* __KERNEL__ */
++
++
++#endif /* __ELAN_DEVINFO_H */
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.21/include/elan/elanmoddebug.h
+===================================================================
+--- linux-2.4.21.orig/include/elan/elanmoddebug.h      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan/elanmoddebug.h   2005-06-01 23:12:54.707422688 -0400
+@@ -0,0 +1,63 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef _ELAN_DEBUG_H
++#define _ELAN_DEBUG_H
++
++
++#ident "$Id: elanmoddebug.h,v 1.5 2003/09/24 13:55:37 david Exp $"
++/*      $Source: /cvs/master/quadrics/elanmod/modsrc/elanmoddebug.h,v $ */
++
++#if defined(__KERNEL__)
++
++/* 0 | QSNET_DEBUG_BUFFER | QSNET_DEBUG_CONSOLE */
++extern int elan_debug_mode; 
++extern int elan_debug_mask;
++
++#define ELAN_DBG_VP           0x00000001
++#define ELAN_DBG_CAP            0x00000002
++#define ELAN_DBG_CTRL           0x00000004
++#define ELAN_DBG_SYS_FN         0x00000008
++#define ELAN_DBG_ALL          0xffffffff
++
++
++#if defined(DEBUG_PRINTF)
++#  define ELAN_DEBUG0(m,fmt)                  ((elan_debug_mask&(m)) ? qsnet_debugf(elan_debug_mode,fmt)             : (void)0)
++#  define ELAN_DEBUG1(m,fmt,a)                        ((elan_debug_mask&(m)) ? qsnet_debugf(elan_debug_mode,fmt,a)           : (void)0)
++#  define ELAN_DEBUG2(m,fmt,a,b)              ((elan_debug_mask&(m)) ? qsnet_debugf(elan_debug_mode,fmt,a,b)         : (void)0)
++#  define ELAN_DEBUG3(m,fmt,a,b,c)            ((elan_debug_mask&(m)) ? qsnet_debugf(elan_debug_mode,fmt,a,b,c)       : (void)0)
++#  define ELAN_DEBUG4(m,fmt,a,b,c,d)          ((elan_debug_mask&(m)) ? qsnet_debugf(elan_debug_mode,fmt,a,b,c,d)     : (void)0)
++#  define ELAN_DEBUG5(m,fmt,a,b,c,d,e)                ((elan_debug_mask&(m)) ? qsnet_debugf(elan_debug_mode,fmt,a,b,c,d,e)   : (void)0)
++#  define ELAN_DEBUG6(m,fmt,a,b,c,d,e,f)      ((elan_debug_mask&(m)) ? qsnet_debugf(elan_debug_mode,fmt,a,b,c,d,e,f) : (void)0)
++#ifdef __GNUC__
++#  define ELAN_DEBUG(m,args...)                       ((elan_debug_mask&(m)) ? qsnet_debugf(elan_debug_mode, ##args)         : (void)0)
++#endif
++
++#else
++
++#  define ELAN_DEBUG0(m,fmt)                  (0)
++#  define ELAN_DEBUG1(m,fmt,a)                        (0)
++#  define ELAN_DEBUG2(m,fmt,a,b)              (0)
++#  define ELAN_DEBUG3(m,fmt,a,b,c)            (0)
++#  define ELAN_DEBUG4(m,fmt,a,b,c,d)          (0)
++#  define ELAN_DEBUG5(m,fmt,a,b,c,d,e)                (0)
++#  define ELAN_DEBUG6(m,fmt,a,b,c,d,e,f)      (0)
++#ifdef __GNUC__
++#  define ELAN_DEBUG(m,args...)
++#endif
++
++#endif /* DEBUG_PRINTF */
++
++
++#endif /* __KERNEL__ */
++#endif /* _ELAN_DEBUG_H */
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.21/include/elan/elanmod.h
+===================================================================
+--- linux-2.4.21.orig/include/elan/elanmod.h   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan/elanmod.h        2005-06-01 23:12:54.708422536 -0400
+@@ -0,0 +1,59 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Limited.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: elanmod.h,v 1.10 2004/06/18 09:28:16 mike Exp $"
++/*      $Source: /cvs/master/quadrics/elanmod/modsrc/elanmod.h,v $*/
++
++#ifndef __ELAN_MOD_H
++#define __ELAN_MOD_H
++
++#include <elan/devinfo.h>
++#include <elan/device.h>
++#include <elan/capability.h>
++#include <elan/stats.h>
++
++#if defined(__KERNEL__)
++
++#include <elan/elanmoddebug.h>
++
++extern kmutex_t elan_mutex;
++
++/* elan_general.c */
++extern int elan_init(void);
++extern int elan_fini(void);
++
++/* return codes, -ve => errno, +ve => success */
++#define ELAN_CAP_OK  (0)
++#define ELAN_CAP_RMS (1)
++
++#define ELAN_USER_ATTACH    (1)
++#define ELAN_USER_DETACH    (2)
++#define ELAN_USER_P2P       (3)
++#define ELAN_USER_BROADCAST (4)
++
++extern int elanmod_classify_cap (ELAN_POSITION *position, ELAN_CAPABILITY *cap, unsigned use);
++
++#define ELAN_USER_BASE_CONTEXT_NUM    0x000                   /* first user allowable context */
++#define ELAN_USER_TOP_CONTEXT_NUM     0x7FF                   /* last user allowable context */
++
++#define ELAN_RMS_BASE_CONTEXT_NUM     0x400                   /* reserved for RMS allocation */
++#define ELAN_RMS_TOP_CONTEXT_NUM      0x7FF
++
++#define ELAN_USER_CONTEXT(ctx)        ((ctx) >= ELAN_USER_BASE_CONTEXT_NUM && \
++                                       (ctx) <= ELAN_USER_TOP_CONTEXT_NUM)    
++
++#define ELAN_RMS_CONTEXT(ctx)          ((ctx) >= ELAN_RMS_BASE_CONTEXT_NUM && \
++                                       (ctx) <= ELAN_RMS_TOP_CONTEXT_NUM)    
++#endif /* __KERNEL__ */
++
++#endif /* __ELAN_MOD_H */
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.21/include/elan/elanmod_linux.h
+===================================================================
+--- linux-2.4.21.orig/include/elan/elanmod_linux.h     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan/elanmod_linux.h  2005-06-01 23:12:54.708422536 -0400
+@@ -0,0 +1,140 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: elanmod_linux.h,v 1.6 2003/09/29 15:36:20 mike Exp $"
++/*      $Source: /cvs/master/quadrics/elanmod/modsrc/elanmod_linux.h,v $*/
++
++#ifndef __ELAN_MOD_LINUX_H
++#define __ELAN_MOD_LINUX_H
++
++#define ELANCRTL_USER_BASE            0x40
++
++/* stats */
++typedef struct elanctrl_stats_get_next_struct
++{
++      ELAN_STATS_IDX   statidx; 
++      ELAN_STATS_IDX  *next_statidx; /* return value */
++} ELANCTRL_STATS_GET_NEXT_STRUCT;
++#define ELANCTRL_STATS_GET_NEXT   _IOR   ('e', ELANCRTL_USER_BASE + 0,  ELANCTRL_STATS_GET_NEXT_STRUCT)
++
++typedef struct elanctrl_stats_find_index_struct
++{
++      caddr_t          block_name;
++      ELAN_STATS_IDX  *statidx; /* return value */
++      uint        *num_entries; /* return value */
++} ELANCTRL_STATS_FIND_INDEX_STRUCT;
++#define ELANCTRL_STATS_FIND_INDEX   _IOR   ('e', ELANCRTL_USER_BASE + 1,  ELANCTRL_STATS_FIND_INDEX_STRUCT)
++
++typedef struct elanctrl_stats_get_block_info_struct
++{
++      ELAN_STATS_IDX  statidx; 
++      caddr_t       block_name; /* return value */
++      uint        *num_entries; /* return value */
++} ELANCTRL_STATS_GET_BLOCK_INFO_STRUCT;
++#define ELANCTRL_STATS_GET_BLOCK_INFO   _IOR   ('e', ELANCRTL_USER_BASE + 2, ELANCTRL_STATS_GET_BLOCK_INFO_STRUCT)
++
++typedef struct elanctrl_stats_get_index_name_struct
++{
++      ELAN_STATS_IDX statidx; 
++      uint           index;
++      caddr_t        name; /* return value */
++} ELANCTRL_STATS_GET_INDEX_NAME_STRUCT;
++#define ELANCTRL_STATS_GET_INDEX_NAME   _IOR   ('e', ELANCRTL_USER_BASE + 3, ELANCTRL_STATS_GET_INDEX_NAME_STRUCT)
++
++typedef struct elanctrl_stats_clear_block_struct
++{
++      ELAN_STATS_IDX statidx; 
++} ELANCTRL_STATS_CLEAR_BLOCK_STRUCT;
++#define ELANCTRL_STATS_CLEAR_BLOCK   _IOR   ('e', ELANCRTL_USER_BASE + 4, ELANCTRL_STATS_CLEAR_BLOCK_STRUCT)
++
++typedef struct elanctrl_stats_get_block_struct
++{
++      ELAN_STATS_IDX statidx; 
++      uint           entries;  
++      ulong         *values; /* return values */
++} ELANCTRL_STATS_GET_BLOCK_STRUCT;
++#define ELANCTRL_STATS_GET_BLOCK        _IOR   ('e', ELANCRTL_USER_BASE + 5, ELANCTRL_STATS_GET_BLOCK_STRUCT)
++
++
++typedef struct elanctrl_get_devinfo_struct
++{
++      ELAN_DEV_IDX  devidx; 
++      ELAN_DEVINFO *devinfo; /* return values */
++} ELANCTRL_GET_DEVINFO_STRUCT;
++#define ELANCTRL_GET_DEVINFO        _IOR   ('e', ELANCRTL_USER_BASE + 6, ELANCTRL_GET_DEVINFO_STRUCT)
++
++typedef struct elanctrl_get_position_struct
++{
++      ELAN_DEV_IDX   devidx; 
++      ELAN_POSITION *position; /* return values */
++} ELANCTRL_GET_POSITION_STRUCT;
++#define ELANCTRL_GET_POSITION        _IOR   ('e', ELANCRTL_USER_BASE + 7, ELANCTRL_GET_POSITION_STRUCT)
++
++typedef struct elanctrl_set_position_struct
++{
++      ELAN_DEV_IDX   devidx; 
++      unsigned short nodeId;
++      unsigned short numNodes;
++} ELANCTRL_SET_POSITION_STRUCT;
++#define ELANCTRL_SET_POSITION        _IOR   ('e', ELANCRTL_USER_BASE + 8, ELANCTRL_SET_POSITION_STRUCT)
++
++typedef struct elanctrl_create_cap_struct
++{
++      ELAN_CAPABILITY cap;
++} ELANCTRL_CREATE_CAP_STRUCT;
++#define ELANCTRL_CREATE_CAP             _IOW   ('e', ELANCRTL_USER_BASE + 9, ELANCTRL_CREATE_CAP_STRUCT)
++
++typedef struct elanctrl_destroy_cap_struct
++{
++      ELAN_CAPABILITY cap;
++} ELANCTRL_DESTROY_CAP_STRUCT;
++#define ELANCTRL_DESTROY_CAP             _IOW   ('e', ELANCRTL_USER_BASE + 10, ELANCTRL_DESTROY_CAP_STRUCT)
++
++typedef struct elanctrl_create_vp_struct
++{
++      ELAN_CAPABILITY cap;
++      ELAN_CAPABILITY map;
++} ELANCTRL_CREATE_VP_STRUCT;
++#define ELANCTRL_CREATE_VP             _IOW   ('e', ELANCRTL_USER_BASE + 11, ELANCTRL_CREATE_VP_STRUCT)
++
++typedef struct elanctrl_destroy_vp_struct
++{
++      ELAN_CAPABILITY cap;
++      ELAN_CAPABILITY map;
++} ELANCTRL_DESTROY_VP_STRUCT;
++#define ELANCTRL_DESTROY_VP          _IOW   ('e', ELANCRTL_USER_BASE + 12, ELANCTRL_DESTROY_VP_STRUCT)
++
++#define ELANCTRL_DEBUG_DUMP          _IO    ('e', ELANCRTL_USER_BASE + 13)
++
++typedef struct elanctrl_get_caps_struct
++{
++      uint            *number_of_results;
++      uint             array_size;
++      ELAN_CAP_STRUCT *caps;
++} ELANCTRL_GET_CAPS_STRUCT;
++#define ELANCTRL_GET_CAPS          _IOW   ('e', ELANCRTL_USER_BASE + 14, ELANCTRL_GET_CAPS_STRUCT)
++
++
++typedef struct elanctrl_debug_buffer_struct
++{
++      caddr_t buffer;
++      int     size;
++} ELANCTRL_DEBUG_BUFFER_STRUCT;
++#define ELANCTRL_DEBUG_BUFFER _IOW ('e', ELANCRTL_USER_BASE + 15, ELANCTRL_DEBUG_BUFFER_STRUCT)
++
++#define ELANMOD_PROCFS_IOCTL      "/proc/qsnet/elan/ioctl"
++#define ELANMOD_PROCFS_VERSION    "/proc/qsnet/elan/version"
++#define ELANMOD_PROCFS_DEBUG_MASK "/proc/qsnet/elan/debug_mask"
++#define ELANMOD_PROCFS_DEBUG_MODE "/proc/qsnet/elan/debug_mode"
++
++#endif /* __ELAN_MOD_LINUX_H */
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.21/include/elan/elanmod_subsystem.h
+===================================================================
+--- linux-2.4.21.orig/include/elan/elanmod_subsystem.h 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan/elanmod_subsystem.h      2005-06-01 23:12:54.708422536 -0400
+@@ -0,0 +1,138 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Limited.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN_SUBSYSTEM_H
++#define __ELAN_SUBSYSTEM_H
++
++#include <sys/types.h>
++#include <sys/param.h>
++
++#if defined( __KERNEL__) 
++int elan_configure(
++    cfg_op_t op,
++    caddr_t  indata,
++    ulong    indata_size,
++    caddr_t  outdata,
++    ulong    outdata_size);
++#endif
++
++#define ELAN_KMOD_CODE(x)     ((x)+CFG_OP_SUBSYS_MIN)
++#define ELAN_MAX_KMOD_CODES 100
++
++#define ELAN_SUBSYS "elan"
++
++#define ELAN_STATS_GET_NEXT   0x01
++typedef struct {
++      ELAN_STATS_IDX statidx;
++      ELAN_STATS_IDX *next_statidx;   
++} elan_stats_get_next_struct;
++
++
++#define ELAN_STATS_FIND_INDEX   0x02
++typedef struct {
++      caddr_t          block_name;
++      ELAN_STATS_IDX  *statidx; /* return value */
++      uint        *num_entries; /* return value */
++} elan_stats_find_index_struct;
++
++#define ELAN_STATS_GET_BLOCK_INFO  0x03
++typedef struct {
++      ELAN_STATS_IDX  statidx; 
++      caddr_t       block_name; /* return value */
++      uint        *num_entries; /* return value */
++} elan_stats_get_block_info_struct;
++
++#define ELAN_STATS_GET_INDEX_NAME  0x04
++typedef struct {
++      ELAN_STATS_IDX statidx; 
++      uint           index;
++      caddr_t        name; /* return value */
++} elan_stats_get_index_name_struct;
++
++#define ELAN_STATS_CLEAR_BLOCK  0x05
++typedef struct {
++      ELAN_STATS_IDX statidx; 
++} elan_stats_clear_block_struct;
++
++#define ELAN_STATS_GET_BLOCK     0x06
++typedef struct 
++{
++      ELAN_STATS_IDX statidx; 
++      uint           entries;  
++      ulong         *values; /* return values */
++} elan_stats_get_block_struct;
++
++#define ELAN_GET_DEVINFO     0x07
++typedef struct 
++{
++      ELAN_DEV_IDX  devidx; 
++      ELAN_DEVINFO *devinfo; /* return values */
++} elan_get_devinfo_struct;
++
++#define ELAN_GET_POSITION  0x08
++typedef struct {
++      ELAN_DEV_IDX   devidx; 
++      ELAN_POSITION *position; /* return values */
++} elan_get_position_struct;
++
++#define ELAN_SET_POSITION   0x09
++typedef struct {
++      ELAN_DEV_IDX   devidx; 
++      unsigned short nodeId;
++      unsigned short numNodes;
++} elan_set_position_struct;
++
++#define ELAN_CREATE_CAP  0x0a
++typedef struct {
++      ELAN_CAPABILITY cap;
++} elan_create_cap_struct;
++
++#define ELAN_DESTROY_CAP    0x0b
++typedef struct {
++      ELAN_CAPABILITY cap;
++} elan_destroy_cap_struct;
++
++#define ELAN_CREATE_VP   0x0c
++typedef struct {
++      ELAN_CAPABILITY cap;
++      ELAN_CAPABILITY map;
++} elan_create_vp_struct;
++
++#define ELAN_DESTROY_VP    0x0d
++typedef struct {
++      ELAN_CAPABILITY cap;
++      ELAN_CAPABILITY map;
++} elan_destroy_vp_struct;
++
++
++#define ELAN_DEBUG_DUMP   0x0e
++
++#define ELAN_GET_CAPS    0x0f
++typedef struct {
++      uint            *number_of_results;
++      uint             array_size;
++      ELAN_CAP_STRUCT *caps;
++} elan_get_caps_struct;
++
++#define ELAN_DEBUG_BUFFER 0x10
++typedef struct {
++      caddr_t addr;
++      int     len;
++} elan_debug_buffer_struct;
++
++#define ELANMOD_PROCFS_IOCTL      "/proc/qsnet/elan/ioctl"
++#define ELANMOD_PROCFS_VERSION    "/proc/qsnet/elan/version"
++#define ELANMOD_PROCFS_DEBUG_MASK "/proc/qsnet/elan/debug_mask"
++#define ELANMOD_PROCFS_DEBUG_MODE "/proc/qsnet/elan/debug_mode"
++
++#endif /* __ELAN_SUBSYSTEM_H */
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.21/include/elan/epcomms.h
+===================================================================
+--- linux-2.4.21.orig/include/elan/epcomms.h   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan/epcomms.h        2005-06-01 23:12:54.710422232 -0400
+@@ -0,0 +1,635 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN_EPCOMMS_H
++#define __ELAN_EPCOMMS_H
++
++#ident "$Id: epcomms.h,v 1.44.2.2 2004/11/12 10:54:50 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/epcomms.h,v $ */
++
++#include <elan/kcomm.h>
++#include <elan/bitmap.h>
++
++#define EPCOMMS_SUBSYS_NAME   "epcomms"
++
++/* message service numbers */
++#define EP_MSG_SVC_EIP512             0x00                            /* Quadrics EIP services */
++#define EP_MSG_SVC_EIP1K              0x01
++#define EP_MSG_SVC_EIP2K              0x02
++#define EP_MSG_SVC_EIP4K              0x03
++#define EP_MSG_SVC_EIP8K              0x04
++#define EP_MSG_SVC_EIP16K             0x05
++#define EP_MSG_SVC_EIP32K             0x06
++#define EP_MSG_SVC_EIP64K             0x07
++#define EP_MSG_SVC_EIP128K            0x08
++
++#define EP_MSG_SVC_PFS                        0x09                            /* Quadrics PFS rpc service */
++
++#define EP_MSG_SVC_PORTALS_SMALL      0x10                            /* Lustre Portals */
++#define EP_MSG_SVC_PORTALS_LARGE      0x11
++
++#define EP_MSG_NSVC                   0x40                            /* Max number of services */
++
++#define EP_MSGQ_ADDR(qnum)            (EP_EPCOMMS_QUEUE_BASE + (qnum) * EP_QUEUE_DESC_SIZE)
++
++/*
++ * EP_ENVELOPE
++ *   Messages are sent by sending an envelope to the destination
++ *   describing the source buffers to transfer.  The receiving thread
++ *   then allocates a receive buffer and fetches the data by issuing
++ *   "get" dmas.
++ *
++ * NOTE:  envelopes are not explicitly converted to network byte order
++ *        since they are always transferred little endian as they are
++ *        copied to/from elan memory using word operations.
++ */
++typedef struct ep_envelope
++{
++    uint32_t    Version;                                      /* Protocol version field */
++
++    EP_ATTRIBUTE  Attr;                                       /* Attributes */
++
++    EP_XID      Xid;                                          /* transaction id */
++
++    uint32_t    NodeId;                                       /* Source processor */
++    uint32_t    Range;                                        /* range we're sending to (high << 16 | low) */
++
++    EP_ADDR     TxdRail;                                      /* address of per-rail txd */
++    EP_NMD      TxdMain;                                      /* address of main memory portion of txd */
++
++    uint32_t      nFrags;                                     /* # fragments */
++    EP_NMD      Frags[EP_MAXFRAG];                            /* network mapping handles of source data */
++
++    uint32_t      CheckSum;                                     /* holds the check sum value when active 
++                                                               * must be after all members to be checksum'd
++                                                               */
++
++    uint32_t    Pad[6];                                       /* Pad to 128 bytes */
++} EP_ENVELOPE;
++
++#define EP_ENVELOPE_VERSION           0xdac10001
++#define EP_ENVELOPE_SIZE              roundup (sizeof (EP_ENVELOPE), EP_BLK_SIZE)
++
++/*
++ * RPC payload - this small amount of data is transfered in
++ * the envelope for RPCs
++ */
++typedef struct ep_payload
++{
++    uint32_t  Data[128/sizeof(uint32_t)];
++} EP_PAYLOAD;
++
++#define EP_PAYLOAD_SIZE                       roundup (sizeof (EP_PAYLOAD), EP_BLK_SIZE)
++
++#define EP_INPUTQ_SIZE                        (EP_ENVELOPE_SIZE + EP_PAYLOAD_SIZE)
++
++/*
++ * EP_STATUSBLK
++ *   RPC completion transfers a status block to the client.
++ */
++typedef struct ep_statusblk
++{
++    uint32_t  Data[128/sizeof(uint32_t)];
++} EP_STATUSBLK;
++
++#define EP_STATUSBLK_SIZE             roundup (sizeof(EP_STATUSBLK), EP_BLK_SIZE)
++
++#define EP_RANGE(low,high)            ((high) << 16 | (low))
++#define EP_RANGE_LOW(range)           ((range) & 0xFFFF)
++#define EP_RANGE_HIGH(range)          (((range) >> 16) & 0xFFFF)
++
++/* return codes from functions, + 'res' parameter to txd callback, ep_rxd_status() */
++typedef enum
++{
++    EP_SUCCESS                = 0,                                    /* message sent/received successfully */
++    EP_RXD_PENDING    = -1,                                   /* rxd not completed by thread */
++    EP_CONN_RESET     = -2,                                   /* virtual circuit reset */
++    EP_NODE_DOWN      = -3,                                   /* node down - transmit not attempted */
++    EP_MSG_TOO_BIG      = -4,                                 /* received message larger than buffer */
++    EP_ENOMEM         = -5,                                   /* memory alloc failed */
++    EP_EINVAL         = -6,                                   /* invalid parameters */
++    EP_SHUTDOWN               = -7,                                   /* receiver is being shut down */
++} EP_STATUS;
++
++/* forward declarations */
++typedef struct ep_rxd                 EP_RXD;
++typedef struct ep_txd                 EP_TXD;
++typedef struct ep_rcvr_rail   EP_RCVR_RAIL;
++typedef struct ep_rcvr                EP_RCVR;
++typedef struct ep_xmtr_rail   EP_XMTR_RAIL;
++typedef struct ep_xmtr                EP_XMTR;
++typedef struct ep_comms_rail    EP_COMMS_RAIL;
++typedef struct ep_comms_subsys  EP_COMMS_SUBSYS;
++
++typedef struct ep_rcvr_stats          EP_RCVR_STATS;
++typedef struct ep_xmtr_stats          EP_XMTR_STATS;
++typedef struct ep_rcvr_rail_stats     EP_RCVR_RAIL_STATS;
++typedef struct ep_xmtr_rail_stats     EP_XMTR_RAIL_STATS;
++
++typedef void (EP_RXH)(EP_RXD *rxd);                           /* callback function from receive completion */
++typedef void (EP_TXH)(EP_TXD *txd, void *arg, EP_STATUS res); /* callback function from transmit completion  */
++
++/* Main memory portion shared descriptor */
++typedef struct ep_rxd_main
++{
++    EP_ENVELOPE               Envelope;                               /* 128 byte aligned envelope */
++    EP_PAYLOAD                Payload;                                /* 128 byte aligned payload */
++    bitmap_t          Bitmap[BT_BITOUL(EP_MAX_NODES)];        /* broadcast bitmap */
++    EP_STATUSBLK      StatusBlk;                              /* RPC status block to return */
++    uint64_t          Next;                                   /* linked list when on active list (main address) */
++    int32_t           Len;                                    /* Length of message received */
++} EP_RXD_MAIN;
++
++#define EP_RXD_MAIN_SIZE      roundup (sizeof (EP_RXD_MAIN), EP_BLK_SIZE)
++
++/* Phases for message/rpc */
++#ifndef __ELAN__
++
++/* Kernel memory portion of per-rail receive descriptor */
++typedef struct ep_rxd_rail
++{
++    struct list_head    Link;                                 /* linked on freelist */
++    EP_RCVR_RAIL       *RcvrRail;                             /* rvcr we're associated with */
++    
++    EP_RXD           *Rxd;                                    /* receive descriptor we're bound to */
++} EP_RXD_RAIL;
++
++#define RXD_BOUND2RAIL(rxdRail,rcvrRail)      ((rxdRail) != NULL && ((EP_RXD_RAIL *) (rxdRail))->RcvrRail == (EP_RCVR_RAIL *) rcvrRail)
++
++struct ep_rxd
++{
++    struct list_head  Link;                                   /* linked on free/active list */
++    EP_RCVR          *Rcvr;                                   /* owning receiver */
++
++    EP_RXD_MAIN              *RxdMain;                                /* shared main memory portion. */
++    EP_NMD            NmdMain;                                /*  and network mapping descriptor */
++
++    EP_RXD_RAIL              *RxdRail;                                /* per-rail rxd we're bound to */
++    
++    EP_RXH           *Handler;                                /* completion function */
++    void             *Arg;                                    /*    and arguement */
++
++    unsigned int      State;                                  /* RXD status (active,stalled,failed) */
++
++    EP_NMD            Data;                                   /* network mapping descriptor for user buffer */
++
++    int                       nFrags;                                 /* network mapping descriptor for put/get/complete */
++    EP_NMD            Local[EP_MAXFRAG];
++    EP_NMD            Remote[EP_MAXFRAG];
++
++    long              NextRunTime;                            /* time to resend failover/map requests */
++    EP_XID            MsgXid;                                 /*   and transaction id */
++
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++    struct list_head  CheckSumLink;                           /* linked on check sum list */
++#endif
++};
++
++#define EP_NUM_RXD_PER_BLOCK  16
++
++/* rxd->State */
++#define EP_RXD_FREE           0
++
++#define EP_RXD_RECEIVE_UNBOUND        1
++#define EP_RXD_RECEIVE_ACTIVE 2
++
++#define EP_RXD_PUT_ACTIVE     3
++#define EP_RXD_PUT_STALLED    4
++#define EP_RXD_GET_ACTIVE     5
++#define EP_RXD_GET_STALLED    6
++
++#define EP_RXD_COMPLETE_ACTIVE        7
++#define EP_RXD_COMPLETE_STALLED       8
++
++#define EP_RXD_RPC_IN_PROGRESS        9
++#define EP_RXD_COMPLETED      10      
++
++#define EP_RXD_BEEN_ABORTED   11                              /* rxd was aborted while in a private state */
++
++typedef struct ep_rxd_block
++{
++    struct list_head  Link;
++
++    EP_NMD            NmdMain;
++
++    EP_RXD            Rxd[EP_NUM_RXD_PER_BLOCK];
++} EP_RXD_BLOCK;
++
++struct ep_rcvr_rail_stats 
++{
++    EP_STATS_COUNT rx;
++    EP_STATS_COUNT rx_len;
++};
++
++struct ep_rcvr_rail
++{
++    EP_RCVR          *Rcvr;                                   /* associated receiver */
++    EP_COMMS_RAIL      *CommsRail;                            /* comms rail */
++
++    struct proc_dir_entry *procfs_root;                         /* root of this rcvr_rail's procfs entry */
++    EP_RCVR_RAIL_STATS     stats;                               /* generic rcvr_rail stats */
++};
++
++struct ep_rcvr_stats
++{
++    EP_STATS_COUNT rx;
++    EP_STATS_COUNT rx_len;
++};
++
++struct ep_rcvr
++{
++    struct list_head  Link;                                   /* queued on subsystem */
++    EP_COMMS_SUBSYS  *Subsys;                                 /* kernel comms subsystem */
++    EP_SERVICE        Service;                                /* service number */
++
++    unsigned int      InputQueueEntries;                      /* # entries on receive queue */
++
++    EP_RAILMASK             RailMask;                                 /* bitmap of which rails are available */
++    EP_RCVR_RAIL     *Rails[EP_MAX_RAILS];
++
++    spinlock_t              Lock;                                     /* spinlock for rails/receive lists */
++
++    struct list_head  ActiveDescList;                         /* List of pending/active receive descriptors */
++
++    EP_XID_CACHE      XidCache;                                       /* XID cache (protected by Lock) */
++
++    struct list_head  FreeDescList;                           /* List of free receive descriptors */
++    unsigned int      FreeDescCount;                          /*   and number on free list */
++    unsigned int      TotalDescCount;                           /*   total number created */
++    spinlock_t              FreeDescLock;                             /*   and lock for free list */
++    kcondvar_t              FreeDescSleep;                            /*   with place to sleep for rx desc */
++    int                     FreeDescWanted;                           /*   and flag */
++    struct list_head  DescBlockList;
++
++    unsigned int      ForwardRxdCount;                                /* count of rxd's being forwarded */
++    unsigned int      CleanupWaiting;                         /* waiting for cleanup */
++    kcondvar_t              CleanupSleep;                             /*   and place to sleep */
++
++    struct proc_dir_entry *procfs_root;                         /* place where this rcvr's proc entry is */
++    EP_RCVR_STATS          stats;                                    
++};
++
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++#define EP_ENVELOPE_CHECK_SUM      (1<<31)
++extern uint32_t ep_calc_check_sum (EP_SYS *sys, EP_ENVELOPE *env, EP_NMD *nmd, int nFrags);
++#endif
++
++#endif /* ! __ELAN__ */
++
++typedef struct ep_txd_main
++{
++    EP_STATUSBLK      StatusBlk;                              /* RPC status block */
++    bitmap_t          Bitmap[BT_BITOUL(EP_MAX_NODES)];                /* broadcast bitmap */
++} EP_TXD_MAIN;
++
++#define EP_TXD_MAIN_SIZE      roundup (sizeof (EP_TXD_MAIN), EP_BLK_SIZE)
++
++#ifndef __ELAN__
++typedef struct ep_txd_rail
++{
++    struct list_head  Link;                                   /* linked on freelist */
++    EP_XMTR_RAIL     *XmtrRail;                                       /* xmtr we're associated with */
++
++    EP_TXD         *Txd;                                      /* txd we're bound to */
++} EP_TXD_RAIL;
++
++#define TXD_BOUND2RAIL(rxdRail,xmtrRail)      ((txdRail) != NULL && ((EP_TXD_RAIL *) (txdRail))->XmtrRail == (EP_XMTR_RAIL *) xmtrRail)
++
++struct ep_txd
++{
++    struct list_head  Link;                                   /* linked on free/active list */
++    EP_XMTR        *Xmtr;                                     /* service we're associated with */
++
++    EP_TXD_MAIN            *TxdMain;                                  /* shared main memory portion */
++    EP_NMD          NmdMain;                                  /*   and network mapping descriptor */
++
++    EP_TXD_RAIL      *TxdRail;                                        /* per-rail txd for this phase */
++
++    EP_TXH         *Handler;                                  /* completion function */
++    void           *Arg;                                      /*    and arguement */
++    
++    unsigned short    NodeId;                                 /* node transmit is to. */
++    EP_SERVICE        Service;                                        /*    and seervice */
++
++    long              TimeStamp;                                 /* time we where created at, to find sends taking too long */
++    long            RetryTime;
++    EP_BACKOFF              Backoff;
++
++    EP_ENVELOPE             Envelope;                                 /* envelope for transmit */
++    EP_PAYLOAD              Payload;                                  /* payload for transmit */
++};
++
++#define EP_NUM_TXD_PER_BLOCK  16
++
++/* "phase" parameter to BindTxd */
++#define EP_TXD_PHASE_ACTIVE           1
++#define EP_TXD_PHASE_PASSIVE          2
++
++typedef struct ep_txd_block
++{
++    struct list_head  Link;
++    EP_NMD            NmdMain;
++    EP_TXD            Txd[EP_NUM_TXD_PER_BLOCK];              /* transmit descriptors */
++} EP_TXD_BLOCK;
++
++struct ep_xmtr_rail_stats
++{
++    EP_STATS_COUNT tx;
++    EP_STATS_COUNT tx_len;
++};
++
++struct ep_xmtr_rail
++{
++    EP_COMMS_RAIL      *CommsRail;                            /* associated comms rail */
++    EP_XMTR          *Xmtr;                                   /* associated transmitter */
++
++    struct proc_dir_entry *procfs_root;                         /* place where this xmtr's proc entry is */
++
++    EP_XMTR_RAIL_STATS     stats;
++};
++
++struct ep_xmtr_stats
++{
++    EP_STATS_COUNT tx;
++    EP_STATS_COUNT tx_len;
++};
++
++struct ep_xmtr
++{
++    struct list_head  Link;                                   /* Linked on subsys */
++    EP_COMMS_SUBSYS  *Subsys;                                 /* kernel comms subsystem */
++
++    EP_RAILMASK             RailMask;                                 /* bitmap of which rails are available */
++    EP_XMTR_RAIL     *Rails[EP_MAX_RAILS];                    /* per-rail state */
++
++    spinlock_t              Lock;                                     /* lock for active descriptor list */
++
++    struct list_head  ActiveDescList;                         /* list of active transmit descriptors */
++
++    EP_XID_CACHE      XidCache;                                       /* XID cache (protected by Lock) */
++
++    struct list_head  FreeDescList;                           /* List of free receive descriptors */
++    unsigned int      FreeDescCount;                          /*   and number on free list */
++    unsigned int      TotalDescCount;
++    spinlock_t              FreeDescLock;                             /*   and lock for free list */
++    kcondvar_t              FreeDescSleep;                            /*   with place to sleep for rx desc */
++    int                     FreeDescWanted;                           /*   and flag */
++    struct list_head  DescBlockList;
++
++    struct proc_dir_entry *procfs_root;                         /* place where this rcvr's proc entry is */
++    EP_XMTR_STATS          stats;   
++};
++
++/* forward descriptor */
++#define EP_TREE_ARITY         3
++
++typedef struct ep_fwd_desc
++{
++    struct list_head    Link;                                 /* linked on forward/free lists */
++    EP_RXD           *Rxd;                                    /* rxd to forward */
++    EP_NMD            Data;                                   /* nmd of subset of receive buffer */
++    unsigned          NumChildren;                            /*   number of places we're forwarding */
++    unsigned          Children[EP_TREE_ARITY];
++} EP_FWD_DESC;
++
++typedef struct ep_comms_ops
++{
++    void           (*DelRail) (EP_COMMS_RAIL *rail);
++    void           (*DisplayRail) (EP_COMMS_RAIL *rail);
++
++    struct {
++      void         (*AddRail) (EP_RCVR *rcvr, EP_COMMS_RAIL *rail);
++      void         (*DelRail) (EP_RCVR *rcvr, EP_COMMS_RAIL *rail);
++
++      long         (*Check) (EP_RCVR_RAIL *rcvrRail, long nextRunTime);
++
++      int          (*QueueRxd) (EP_RXD *rxd, EP_RCVR_RAIL *rcvrRail);
++      void         (*RpcPut)(EP_RXD *rxd, EP_NMD *local, EP_NMD *remote, unsigned nFrags);
++      void         (*RpcGet)(EP_RXD *rxd, EP_NMD *local, EP_NMD *remote, unsigned nFrags);
++      void         (*RpcComplete)(EP_RXD *rxd, EP_NMD *local, EP_NMD *remote, unsigned nFrags);
++
++      EP_RXD      *(*StealRxd)(EP_RCVR_RAIL *rcvrRail);
++
++      void         (*DisplayRcvr) (DisplayInfo *di, EP_RCVR_RAIL *rcvrRail);
++      void         (*DisplayRxd)  (DisplayInfo *di, EP_RXD_RAIL *rxdRail);
++
++      void         (*FillOutRailStats) (EP_RCVR_RAIL *rcvr_rail, char *str);
++
++    } Rcvr;
++
++    struct {
++      void         (*AddRail) (EP_XMTR *xmtr, EP_COMMS_RAIL *rail);
++      void         (*DelRail) (EP_XMTR *xmtr, EP_COMMS_RAIL *rail);
++
++      long         (*Check) (EP_XMTR_RAIL *xmtrRail, long nextRunTime);
++      
++      int          (*BindTxd) (EP_TXD *txd, EP_XMTR_RAIL *xmtrRail, unsigned int phase);
++      void         (*UnbindTxd) (EP_TXD *txd, unsigned int phase);
++      int          (*PollTxd) (EP_XMTR_RAIL *xmtrRail, EP_TXD_RAIL *txdRail, int how);
++
++      void         (*DisplayXmtr) (DisplayInfo *di, EP_XMTR_RAIL *xmtrRail);
++      void         (*DisplayTxd)  (DisplayInfo *di, EP_TXD_RAIL *txdRail);
++
++      int          (*CheckTxdState) (EP_TXD *txd);
++
++      void         (*FillOutRailStats) (EP_XMTR_RAIL *xmtr_rail, char *str);
++
++    } Xmtr;
++} EP_COMMS_OPS;
++
++#define EP_RAIL_OP(commsRail, Which)  (commsRail)->Ops.Which
++#define EP_RCVR_OP(rcvrRail, Which)   (rcvrRail)->CommsRail->Ops.Rcvr.Which
++#define EP_XMTR_OP(xmtrRail, Which)   (xmtrRail)->CommsRail->Ops.Xmtr.Which
++
++/* "how" parameter to PollTxd */
++#define POLL_TX_LIST          0
++#define ENABLE_TX_CALLBACK    1
++#define DISABLE_TX_CALLBACK   2
++
++struct ep_comms_rail
++{
++    struct list_head  Link;                                   /* Linked on subsys */
++    EP_RAIL          *Rail;                                   /* kernel comms rail */
++    EP_COMMS_SUBSYS    *Subsys;
++    EP_COMMS_OPS        Ops;
++
++    EP_COMMS_RAIL_STATS Stats;                                        /* statistics */
++};
++
++struct ep_comms_subsys
++{
++    EP_SUBSYS         Subsys;                                 /* is a kernel comms subsystem */
++
++    kmutex_t          Lock;                                   /* global lock */
++
++    EP_COMMS_STATS    Stats;                                  /* statistics */
++
++    struct list_head  Rails;                                  /* list of all rails */
++
++    struct list_head    Receivers;                            /* list of receivers */
++    struct list_head  Transmitters;                           /* and transmitters */
++
++    /* forward/allocator thread */
++    EP_KTHREAD                Thread;                                 /* place thread sleeps */
++
++    /* message passing "broadcast" forward lists */
++    spinlock_t                ForwardDescLock;                        /* Lock for broadcast forwarding */
++    struct list_head    ForwardDescList;                      /* List of rxd's to forward */
++
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++    spinlock_t                CheckSumDescLock;                       /* Lock for CheckSums */
++    struct list_head    CheckSumDescList;                     /* List of rxd's to be CheckSumed */
++#endif
++
++    EP_XMTR          *ForwardXmtr;                            /* and transmitter to forward with */
++};
++
++/* epcomms.c subsystem initialisation */
++extern unsigned int   epcomms_forward_limit;
++
++extern int          ep_comms_init (EP_SYS *sys);
++extern void           ep_comms_display (EP_SYS *sys, char *how);
++extern EP_RAILMASK    ep_rcvr_railmask (EP_SYS *epsys, EP_SERVICE service);
++
++/* epcomms_elan3.c */
++extern EP_COMMS_RAIL *ep3comms_add_rail (EP_SUBSYS *s, EP_SYS *sys, EP_RAIL *rail);
++
++/* epcomms_elan4.c */
++extern EP_COMMS_RAIL *ep4comms_add_rail (EP_SUBSYS *s, EP_SYS *sys, EP_RAIL *rail);
++
++/* epcommsTx.c */
++extern int            TxdShouldStabalise (EP_TXD_RAIL *txdRail, EP_RAIL *rail);
++extern void         FreeTxd (EP_XMTR *xmtr, EP_TXD *txd);
++
++extern unsigned int   ep_txd_lowat;
++extern long           ep_check_xmtr (EP_XMTR *xmtr, long nextRunTime);
++extern void           ep_display_xmtr (DisplayInfo *di, EP_XMTR *xmtr);
++extern void           ep_xmtr_flush_callback (EP_XMTR *xmtr, EP_XMTR_RAIL *xmtrRail);
++extern void           ep_xmtr_reloc_callback (EP_XMTR *xmtr, EP_XMTR_RAIL *xmtrRail);
++
++extern void           ep_xmtr_fillout_stats      (EP_XMTR      *xmtr,      char *str);
++extern void           ep_xmtr_rail_fillout_stats (EP_XMTR_RAIL *xmtr_rail, char *str);
++
++extern void           ep_xmtr_txd_stat (EP_XMTR *xmtr, EP_TXD *txd);
++
++/* epcommsRx.c */
++extern EP_RXD        *StealRxdFromOtherRail (EP_RCVR *rcvr);
++
++extern unsigned int   ep_rxd_lowat;
++extern long         ep_check_rcvr (EP_RCVR *rcvr, long nextRunTime);
++extern void           ep_rcvr_flush_callback (EP_RCVR *rcvr, EP_RCVR_RAIL *rcvrRail);
++extern void           ep_rcvr_reloc_callback (EP_RCVR *rcvr, EP_RCVR_RAIL *rcvrRail);
++extern void           ep_display_rcvr (DisplayInfo *di, EP_RCVR *rcvr, int full);
++
++extern long           ep_forward_rxds (EP_COMMS_SUBSYS *subsys, long nextRunTime);
++
++extern void           ep_rcvr_fillout_stats      (EP_RCVR      *rcvr,      char *str);
++extern void           ep_rcvr_rail_fillout_stats (EP_RCVR_RAIL *rcvr_rail, char *str);
++
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++extern void           ep_csum_rxds    (EP_COMMS_SUBSYS *subsys);
++extern void           ep_rxd_queue_csum (EP_RXD *rxd);
++#endif
++
++extern void           ep_rxd_received     (EP_RXD *rxd);
++extern void           ep_rxd_received_now (EP_RXD *rxd);
++
++/* ep_procfs.c */
++extern struct proc_dir_entry *ep_procfs_root;
++
++extern void ep_procfs_rcvr_xmtr_init(void);
++extern void ep_procfs_rcvr_xmtr_fini(void);
++
++extern void ep_procfs_rcvr_add(EP_RCVR *rcvr);
++extern void ep_procfs_rcvr_del(EP_RCVR *rcvr);
++
++extern void ep_procfs_rcvr_add_rail(EP_RCVR_RAIL *rcvrRail);
++extern void ep_procfs_rcvr_del_rail(EP_RCVR_RAIL *rcvrRail);
++
++extern void ep_procfs_xmtr_add(EP_XMTR *xmtr);
++extern void ep_procfs_xmtr_del(EP_XMTR *xmtr);
++
++extern void ep_procfs_xmtr_add_rail(EP_XMTR_RAIL *xmtrRail);
++extern void ep_procfs_xmtr_del_rail(EP_XMTR_RAIL *xmtrRail);
++
++
++/* Public Interface */
++
++
++/* epcomms.c message xmtr functions */
++extern EP_XMTR       *ep_alloc_xmtr (EP_SYS *sys);
++extern void           ep_free_xmtr (EP_XMTR *xmtr);
++
++extern EP_STATUS      ep_transmit_message (EP_XMTR *xmtr, unsigned int dest, EP_SERVICE service, EP_ATTRIBUTE attr, 
++                                         EP_TXH *handler, void *arg, EP_PAYLOAD *payload,
++                                         EP_NMD *nmd, int nFrag);
++extern EP_STATUS      ep_multicast_message (EP_XMTR *xmtr, unsigned int destLo, unsigned int destHi, bitmap_t *bitmap, 
++                                          EP_SERVICE service, EP_ATTRIBUTE attr, EP_TXH *handler, void *arg, 
++                                          EP_PAYLOAD *payload, EP_NMD *nmd, int nFrag);
++extern EP_STATUS      ep_transmit_rpc (EP_XMTR *xmtr, unsigned int dest, EP_SERVICE service, EP_ATTRIBUTE attr, 
++                                     EP_TXH *handler, void *arg, EP_PAYLOAD *payload,
++                                     EP_NMD *nmd, int nFrag);
++extern EP_STATUS      ep_multicast_forward (EP_XMTR *xmtr, unsigned int dest, EP_SERVICE service, EP_ATTRIBUTE attr, 
++                                          EP_TXH *handler, void *arg, EP_ENVELOPE *env, EP_PAYLOAD *payload, 
++                                          bitmap_t *bitmap, EP_NMD *nmd, int nFrags);
++
++/* epcomms.c functions for use with polled transmits */
++extern int            ep_poll_transmits (EP_XMTR *xmtr);
++extern int            ep_enable_txcallbacks (EP_XMTR *xmtr);
++extern int            ep_disable_txcallbacks (EP_XMTR *xmtr);
++
++/* epcomms.c message rcvr functions */
++extern EP_RCVR       *ep_alloc_rcvr (EP_SYS *sys, EP_SERVICE svc, unsigned int nenvelopes);
++extern void         ep_free_rcvr (EP_RCVR *rcvr);
++
++extern EP_STATUS      ep_queue_receive (EP_RCVR *rcvr, EP_RXH *handler, void *arg, EP_NMD *nmd, EP_ATTRIBUTE attr);
++extern void         ep_requeue_receive (EP_RXD *rxd, EP_RXH *handler, void *arg, EP_NMD *nmd, EP_ATTRIBUTE attr);
++extern EP_STATUS      ep_rpc_put (EP_RXD *rxd, EP_RXH *handler, void *arg, EP_NMD *from, EP_NMD *to, int nFrags);
++extern EP_STATUS      ep_rpc_get (EP_RXD *rxd, EP_RXH *handler, void *arg, EP_NMD *from, EP_NMD *to, int nFrags);
++extern EP_STATUS      ep_complete_rpc (EP_RXD *rxd, EP_RXH *handler, void *arg, EP_STATUSBLK *blk, 
++                                     EP_NMD *from, EP_NMD *to, int nFrags);
++extern void         ep_complete_receive (EP_RXD *rxd);
++
++/* railhints.c */
++extern int            ep_xmtr_bcastrail (EP_XMTR *xmtr, EP_RAILMASK allowedRails);
++extern int            ep_xmtr_prefrail (EP_XMTR *xmtr, EP_RAILMASK allowedRails, unsigned nodeId);
++extern EP_RAILMASK    ep_xmtr_availrails (EP_XMTR *xmtr);
++extern EP_RAILMASK    ep_xmtr_noderails (EP_XMTR *xmtr, unsigned nodeId);
++extern int            ep_rcvr_prefrail (EP_RCVR *rcvr, EP_RAILMASK allowedRails);
++extern EP_RAILMASK    ep_rcvr_availrails (EP_RCVR *rcvr);
++extern EP_RAILMASK    ep_rxd_railmask (EP_RXD *rxd);
++
++/* epcomms.c functions for accessing fields of rxds */
++extern void          *ep_rxd_arg(EP_RXD *rxd);
++extern int            ep_rxd_len(EP_RXD *rxd);
++extern EP_STATUS      ep_rxd_status(EP_RXD *rxd);
++extern int            ep_rxd_isrpc(EP_RXD *rxd);
++extern EP_ENVELOPE   *ep_rxd_envelope(EP_RXD *rxd);
++extern EP_PAYLOAD    *ep_rxd_payload(EP_RXD *rxd);
++extern int            ep_rxd_node(EP_RXD *rxd);
++extern EP_STATUSBLK  *ep_rxd_statusblk(EP_RXD *rxd);
++
++/* functions for accessing fields of txds */
++extern int            ep_txd_node(EP_TXD *txd);
++extern EP_STATUSBLK  *ep_txd_statusblk(EP_TXD *txd);
++
++/* functions for controlling how many processes are using module */
++extern void              ep_mod_dec_usecount (void);
++extern void              ep_mod_inc_usecount (void);
++
++extern EP_RAILMASK ep_xmtr_svc_indicator_railmask (EP_XMTR *xmtr, int svc_indicator, int nodeId);
++extern int ep_xmtr_svc_indicator_bitmap (EP_XMTR *xmtr, int svc_indicator, bitmap_t * bitmap, int low, int nnodes);
++
++#endif /* ! __ELAN__ */
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++#endif /* __ELAN_EPCOMMS_H */
++
+Index: linux-2.4.21/include/elan/epsvc.h
+===================================================================
+--- linux-2.4.21.orig/include/elan/epsvc.h     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan/epsvc.h  2005-06-01 23:12:54.710422232 -0400
+@@ -0,0 +1,36 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN_EPSVC_H
++#define __ELAN_EPSVC_H
++
++#ident "@(#)$Id: epsvc.h,v 1.9 2004/02/13 10:03:27 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/epsvc.h,v $ */
++
++
++#define EP_SVC_NUM_INDICATORS       8
++#define EP_SVC_INDICATOR_MAX_NAME  32
++
++#define EP_SVC_EIP    0
++#define EP_SVC_NAMES  {"eip", "1", "2", "3", "4", "5", "6", "7"};
++
++#if defined(__KERNEL__)
++extern int         ep_svc_indicator_set      (EP_SYS *epsys, int svc_indicator);
++extern int         ep_svc_indicator_clear    (EP_SYS *epsys, int svc_indicator);
++extern int         ep_svc_indicator_is_set   (EP_SYS *epsys, int svc_indicator, int nodeId);
++extern int         ep_svc_indicator_bitmap   (EP_SYS *epsys, int svc_indicator, bitmap_t * bitmap, int low, int nnodes);
++extern EP_RAILMASK ep_svc_indicator_railmask (EP_SYS *epsys, int svc_indicator, int nodeId);
++#endif
++
++#endif /* __ELAN_EPSVC_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/include/elan/kalloc.h
+===================================================================
+--- linux-2.4.21.orig/include/elan/kalloc.h    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan/kalloc.h 2005-06-01 23:12:54.710422232 -0400
+@@ -0,0 +1,108 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN3_KALLOC_H
++#define __ELAN3_KALLOC_H
++
++#ident "$Id: kalloc.h,v 1.11 2004/05/19 10:23:59 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/kalloc.h,v $ */
++
++#include <elan/rmap.h>
++
++/*
++ * Memory allocator
++ */
++#define LN2_MIN_SIZE  6                                       /* 64 bytes */
++#define LN2_MAX_SIZE  16                                      /* 64k bytes */
++#define NUM_FREELISTS (LN2_MAX_SIZE-LN2_MIN_SIZE + 1)
++#define MIN_SIZE      (1 << LN2_MIN_SIZE)
++#define MAX_SIZE      (1 << LN2_MAX_SIZE)
++
++#define HASHSHIFT     LN2_MAX_SIZE
++#define NHASH         32
++#define HASH(addr)    (((addr) >> HASHSHIFT) & (NHASH-1))
++
++typedef enum
++{
++    EP_ALLOC_TYPE_PRIVATE_SDRAM,
++    EP_ALLOC_TYPE_PRIVATE_MAIN,
++    EP_ALLOC_TYPE_SHARED_MAIN,
++} EP_ALLOC_TYPE;
++
++typedef struct ep_pool
++{
++    EP_NMH              Handle;                               /* network mapping handle */
++
++    struct list_head    HashBase;                             /* linked on hash lists */
++    struct list_head    HashTop;                              /* linked on hash lists */
++
++    struct list_head    Link[NUM_FREELISTS];                  /* linked on free lists */
++    bitmap_t           *Bitmaps[NUM_FREELISTS];               /* bitmaps for each size */
++
++    union {
++      sdramaddr_t     Sdram;
++      unsigned long   Ptr;
++    } Buffer;
++} EP_POOL;
++
++typedef struct ep_alloc
++{
++    spinlock_t             Lock;
++    
++    EP_ALLOC_TYPE    Type;
++    unsigned int     Perm;
++
++    EP_RMAP        *ResourceMap;
++
++    struct list_head HashBase[NHASH];
++    struct list_head HashTop[NHASH];
++    struct list_head Freelists[NUM_FREELISTS];
++
++    union {
++      struct {
++          EP_SYS             *System;
++          struct list_head    Rails;
++      } Shared;
++      
++      struct {
++          EP_RAIL            *Rail;
++      } Private;
++    } Data;
++} EP_ALLOC;
++
++extern void           ep_display_alloc (EP_ALLOC *alloc);
++
++extern void           ep_alloc_init (EP_RAIL *rail);
++extern void           ep_alloc_fini (EP_RAIL *rail);
++
++extern sdramaddr_t    ep_alloc_memory_elan (EP_RAIL *rail, EP_ADDR addr, unsigned size, unsigned int perm, EP_ATTRIBUTE attr);
++extern void           ep_free_memory_elan (EP_RAIL *rail, EP_ADDR addr);
++
++extern sdramaddr_t    ep_alloc_elan (EP_RAIL *rail, unsigned size, EP_ATTRIBUTE attr, EP_ADDR *addrp);
++extern void           ep_free_elan (EP_RAIL *rail, EP_ADDR addr, unsigned size);
++extern void          *ep_alloc_main (EP_RAIL *rail, unsigned size, EP_ATTRIBUTE attr, EP_ADDR *addr);
++extern void           ep_free_main (EP_RAIL *rail, EP_ADDR addr, unsigned size);
++
++extern sdramaddr_t    ep_elan2sdram (EP_RAIL *rail, EP_ADDR addr);
++extern void            *ep_elan2main (EP_RAIL *rail, EP_ADDR addr);
++
++extern void           ep_shared_alloc_init (EP_SYS *sys);
++extern void           ep_shared_alloc_fini (EP_SYS *sys);
++extern int            ep_shared_alloc_add_rail (EP_SYS *sys, EP_RAIL *rail);
++extern void           ep_shared_alloc_remove_rail (EP_SYS *sys, EP_RAIL *rail);
++
++extern void          *ep_shared_alloc_main (EP_SYS *sys, unsigned size, EP_ATTRIBUTE attr, EP_NMD *nmd);
++extern void           ep_shared_free_main (EP_SYS *sys, EP_NMD *nmd);
++
++#endif /* __ELAN_KALLOC_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/include/elan/kcomm.h
+===================================================================
+--- linux-2.4.21.orig/include/elan/kcomm.h     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan/kcomm.h  2005-06-01 23:12:54.712421928 -0400
+@@ -0,0 +1,839 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN_KCOMM_H
++#define __ELAN_KCOMM_H
++
++#ident "$Id: kcomm.h,v 1.71.2.8 2004/12/14 10:19:14 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/kcomm.h,v $*/
++#define EP_KCOMM_MAJOR_VERSION                3
++#define EP_KCOMM_MINOR_VERSION                1
++
++#define EP_PROTOCOL_VERSION           1                       /* CM/KCOMM protocol revision */
++
++#define EP_MAX_NODES                  2048                    /* Max nodes we support */
++#define EP_MAX_RAILS                  16                      /* max number of rails (we use an unsigned short for bitmaps !) */
++#define EP_MAXFRAG                    4                       /* max number of fragments */
++
++#define EP_BLK_SIZE                   64                      /* align objects for elan access */
++
++/* Elan virtual address address space */
++#define EP_SYSTEM_QUEUE_BASE          0x00010000              /* Base address for system queues */
++#define EP_MSGSYS_QUEUE_BASE          0x00020000              /* Base address for msgsys queues */
++#define EP_EPCOMMS_QUEUE_BASE         0x00030000              /* Base address for message queues */
++#define EP_DVMA_BASE                  0x10000000              /* elan address range for dvma mapping. */
++#define EP_DVMA_TOP                   0xE0000000
++
++#define EP_SHARED_BASE                        0xE0000000              /* shared main/elan allocators */
++#define EP_SHARED_TOP                 0xF0000000
++
++#define EP_PRIVATE_BASE                       0xF0000000              /* private main/elan allocators */
++#define EP_PRIVATE_TOP                        0xF8000000
++
++#define EP_DVMA_RMAP_SIZE             1024                    /* size of resource map for dvma address space */
++#define EP_SHARED_RMAP_SIZE           1024                    /* size of resource map for shared address space */
++#define EP_PRIVATE_RMAP_SIZE          1024                    /* size of resource map for private address space */
++
++/* Input queue descriptors fit into 64 bytes */
++#define EP_QUEUE_DESC_SIZE            64
++
++/* Timeouts for checking network position */
++#define EP_POSITION_TIMEOUT           (4*HZ)          /* 1s   time to notice CheckNetworkPosition changes */
++#define EP_WITHDRAW_TIMEOUT           (2*HZ)          /* 2s   time before withdrawing from unreachable nodes */
++
++/* Time to try again due to resource failue (eg malloc etc) */
++#define RESOURCE_RETRY_TIME           (HZ/20)
++
++/* Time to retransmit message when send failed */
++#define MSGBUSY_RETRY_TIME            (HZ/20)
++
++/* Time between retransmits of messages network flush requests */
++#define MESSAGE_RETRY_TIME            (HZ/5)
++
++/* time to hold the context filter up to ensure that the
++ * next packet of a dma is guaranteed to get nacked (8mS) */
++#define NETWORK_ERROR_TIMEOUT         (1 + roundup (HZ * 8 / 1000, 1))
++
++/* Time between retransmits of message failover requests */
++#define FAILOVER_RETRY_TIME           (HZ/5)
++
++/* compute earliest time */
++#define SET_NEXT_RUN_TIME(nextRunTime, time) \
++do { \
++    if ((nextRunTime) == 0 || AFTER(nextRunTime, (time)))\
++      (nextRunTime) = (time);\
++} while (0)
++
++/* DMA retry backoff/priorities/issue rings */
++#define EP_NUM_BACKOFF                        8
++#define EP_RETRY_STABALISING            0
++#define EP_RETRY_BASE                 1
++
++#define EP_RETRY_CRITICAL             EP_RETRY_BASE
++#define EP_RETRY_HIGH_PRI             (EP_RETRY_CRITICAL + 1)
++#define EP_RETRY_HIGH_PRI_TIME                (1)
++#define EP_RETRY_HIGH_PRI_RETRY               (EP_RETRY_HIGH_PRI + 1)
++#define EP_RETRY_HIGH_PRI_RETRY_TIME  (2)
++#define EP_RETRY_LOW_PRI              (EP_RETRY_HIGH_PRI_RETRY + EP_NUM_BACKOFF)
++#define EP_RETRY_LOW_PRI_TIME         (2)
++#define EP_RETRY_LOW_PRI_RETRY                (EP_RETRY_LOW_PRI + 1)
++#define EP_RETRY_LOW_PRI_RETRY_TIME   (4)
++#define EP_RETRY_ANONYMOUS            (EP_RETRY_LOW_PRI_RETRY + EP_NUM_BACKOFF)
++#define EP_RETRY_ANONYMOUS_TIME               (10)
++#define EP_RETRY_NETERR                       (EP_RETRY_ANONYMOUS + EP_NUM_BACKOFF)
++#define EP_RETRY_NETERR_TIME          (10)
++#define EP_NUM_RETRIES                        (EP_RETRY_NETERR + 1)
++
++typedef unsigned short EP_SERVICE;
++
++/* EP_ATTRIBUTE 32 bits 
++ *
++ * 0-2
++ *   for initial call :-
++ *     0 (0x1) EP_NO_ALLOC                            used once
++ *     1 (0x2) EP_NO_SLEEP                            used once
++ *     2 (0x4) EP_NOT_MYSELF                          used once
++ *
++ *   when stored and transmited :-
++ *     0 (0x0) EP_MULTICAST                           envelope
++ *     1 (0x2) EP_RPC                                 envelope
++ *     2 (0x4) EP_HAS_PAYLOAD                         envelope
++ *
++ * 3-11
++ *     3   (0x08) EP_PREFRAIL_SET                     preserved
++ *     4-7 (0xf0) Pref Rail
++ *     8  (0x100) EP_NO_INTERUPT
++ *     9  (0x200) EP_NO_FAILOVER
++ *
++ *    10 (0x400) EP_INTERRUPT_ENABLED                 internal
++ *    11 (0x800) EP_TXD_STABALISING                   internal
++ *
++ * 12-13 Not Used.
++ * 
++ * 14-15 (0xC000) Data Type.                          passed in
++ *    00 none. 
++ *    01 Service Indicator.
++ *    10 TimeOut.
++ *    11 RailMask
++ *          
++ * 16-31 (0x10000)  Data.  Service Indicator, TimeOut, RailMask, Pref Rail.
++ *         
++*/
++
++typedef uint32_t EP_ATTRIBUTE;
++
++#define EP_LOCAL_ATTR_MASK 0x07
++#define EP_CLEAR_LOCAL_ATTR(ATTR)  ( (ATTR) & ~EP_LOCAL_ATTR_MASK )
++
++#define EP_NO_ALLOC      0x01 /* Don't call allocators if no free descriptors */
++#define EP_NO_SLEEP      0x02 /* Don't sleep if no free descriptors */
++#define EP_NOT_MYSELF    0x04 /* Don't send multicast to me */
++
++#define EP_MULTICAST         0x01     /* Message is a multicast */
++#define EP_RPC               0x02     /* Wait for RPC reply */
++#define EP_HAS_PAYLOAD_BIT   0x04     /* transfer payload */
++
++
++#define EP_PREFRAIL_SET  0x08 /* preferred rail is set (otherwise pick one from the NMDs) */
++
++#define EP_PREFRAIL_SHIFT  (4)
++#define EP_PREFRAIL_MASK   0xf0
++#define EP_IS_PREFRAIL_SET(ATTR)      (((ATTR) &  EP_PREFRAIL_SET) != 0)
++#define EP_CLEAR_PREFRAIL(ATTR)       (((ATTR) & ~EP_PREFRAIL_SET) & ~EP_PREFRAIL_MASK) 
++#define EP_SET_PREFRAIL(ATTR,RAIL)    (EP_CLEAR_PREFRAIL(ATTR) | (((RAIL) <<  EP_PREFRAIL_SHIFT ) &  EP_PREFRAIL_MASK ) |  EP_PREFRAIL_SET)
++
++
++#define EP_ATTR2PREFRAIL(ATTR)            (((ATTR) & EP_PREFRAIL_MASK) >> EP_PREFRAIL_SHIFT)
++
++
++#define EP_INTERRUPT_ENABLED 0x400    /* event interrupt enabled on EP_NO_INTERRUPT */
++#define EP_TXD_STABALISING   0x800      /* flag to indicate this is attempting to stabalise */
++
++#define EP_IS_MULTICAST(ATTR)                 (((ATTR) &  EP_MULTICAST) != 0)
++#define EP_SET_MULTICAST(ATTR)                ( (ATTR) |  EP_MULTICAST)
++#define EP_CLEAR_MULTICAST(ATTR)              ( (ATTR) & ~EP_MULTICAST)
++
++#define EP_IS_RPC(ATTR)                       (((ATTR) &  EP_RPC) != 0)
++#define EP_SET_RPC(ATTR)                      ( (ATTR) |  EP_RPC)
++#define EP_CLEAR_RPC(ATTR)                    ( (ATTR) & ~EP_RPC)
++
++#define EP_HAS_PAYLOAD(ATTR)                  (((ATTR) &  EP_HAS_PAYLOAD_BIT) != 0)
++#define EP_SET_HAS_PAYLOAD(ATTR)              ( (ATTR) |  EP_HAS_PAYLOAD_BIT)
++#define EP_CLEAR_HAS_PAYLOAD(ATTR)            ( (ATTR) & ~EP_HAS_PAYLOAD_BIT)
++
++#define EP_IS_INTERRUPT_ENABLED(ATTR)         (((ATTR) &  EP_INTERRUPT_ENABLED) != 0)
++#define EP_SET_INTERRUPT_ENABLED(ATTR)        ( (ATTR) |  EP_INTERRUPT_ENABLED)
++#define EP_CLEAR_INTERRUPT_ENABLED(ATTR)      ( (ATTR) & ~EP_INTERRUPT_ENABLED)
++
++#define EP_IS_TXD_STABALISING(ATTR)           (((ATTR) &  EP_TXD_STABALISING) != 0)
++#define EP_SET_TXD_STABALISING(ATTR)          ( (ATTR) |  EP_TXD_STABALISING)
++#define EP_CLEAR_TXD_STABALISING(ATTR)        ( (ATTR) & ~EP_TXD_STABALISING)
++
++#define EP_NO_INTERRUPT      0x100    /* Don't generate completion interrupt (tx) */
++#define EP_NO_FAILOVER       0x200    /* don't attempt rail failover, just abort */
++
++#define EP_IS_NO_INTERRUPT(ATTR)    (((ATTR) &  EP_NO_INTERRUPT) != 0)
++#define EP_SET_NO_INTERRUPT(ATTR)   ( (ATTR) |  EP_NO_INTERRUPT)
++#define EP_CLEAR_NO_INTERRUPT(ATTR) ( (ATTR) & ~EP_NO_INTERRUPT)
++
++#define EP_IS_NO_FAILOVER(ATTR)    (((ATTR) &  EP_NO_FAILOVER) != 0)
++#define EP_SET_NO_FAILOVER(ATTR)   ( (ATTR) |  EP_NO_FAILOVER)
++#define EP_CLEAR_NO_FAILOVER(ATTR) ( (ATTR) & ~EP_NO_FAILOVER)
++
++#define EP_TYPE_MASK           0xC000
++#define EP_TYPE_SVC_INDICATOR  0x4000
++#define EP_TYPE_TIMEOUT        0x8000
++#define EP_TYPE_RAILMASK       0xC000
++
++#define EP_ATTR2TYPE(ATTR)        ( (ATTR) & EP_TYPE_MASK )
++
++#define EP_IS_SVC_INDICATOR(ATTR) (EP_ATTR2TYPE(ATTR) == EP_TYPE_SVC_INDICATOR)
++#define EP_IS_TIMEOUT(ATTR)       (EP_ATTR2TYPE(ATTR) == EP_TYPE_TIMEOUT)
++#define EP_IS_RAILMASK(ATTR)      (EP_ATTR2TYPE(ATTR) == EP_TYPE_RAILMASK)
++#define EP_IS_NO_TYPE(ATTR)       (EP_ATTR2TYPE(ATTR) == 0)
++
++#define EP_DATA_SHIFT          (16)
++#define EP_DATA_MASK           0xffff0000
++
++#define EP_ATTR2DATA(ATTR)     (((ATTR) & EP_DATA_MASK) >> EP_DATA_SHIFT)
++#define EP_DATA2ATTR(DATA)     (((DATA) <<  EP_DATA_SHIFT) & EP_DATA_MASK)
++
++#define EP_CLEAR_DATA(ATTR)    (((ATTR) & ~EP_TYPE_MASK) & ~EP_DATA_MASK)
++#define EP_SET_DATA(ATTR,TYPE,DATA)   (EP_CLEAR_DATA(ATTR) | ((TYPE) & EP_TYPE_MASK) | (((DATA) <<  EP_DATA_SHIFT) & EP_DATA_MASK))
++
++#define EP_DEFAULT_TIMEOUT     (HZ*30)
++
++#if !defined(offsetof)
++#define offsetof(s, m)                (unsigned long)(&(((s *)0)->m))
++#endif
++#if !defined(roundup)
++#define roundup(x, y)                 ((((x)+((y)-1))/(y))*(y))
++#endif
++
++/* 
++ * Message transaction ID's - these are unique 64 bts 
++ * numbers which include the initial rail number.
++ */
++typedef struct ep_xid
++{
++    uint32_t  Generation;
++    uint32_t  Handle;
++    uint64_t  Unique;
++} EP_XID;
++
++#define EP_INVALIDATE_XID(xid)        ((xid).Generation = (xid).Handle = (xid).Unique = 0)
++
++#define EP_XID_INVALID(xid)   ((xid).Generation == 0 && (xid).Handle == 0 && (xid).Unique == 0)
++#define EP_XIDS_MATCH(a,b)    ((a).Generation == (b).Generation && (a).Handle == (b).Handle && (a).Unique == (b).Unique)
++
++typedef struct ep_backoff
++{
++    unsigned char     type;
++    unsigned char     indx;
++    unsigned short    count;
++} EP_BACKOFF;
++
++/* values for "type" */
++#define EP_BACKOFF_FREE               0
++#define EP_BACKOFF_ENVELOPE   1
++#define EP_BACKOFF_FETCH      2
++#define EP_BACKOFF_DATA               3
++#define EP_BACKOFF_DONE               4
++#define EP_BACKOFF_STABILISE  5
++
++#ifndef __ELAN__
++
++/* forward declaration of types */
++typedef struct ep_rail        EP_RAIL;
++typedef struct ep_sys EP_SYS;
++
++#include <elan/nmh.h>
++#include <elan/kmap.h>
++#include <elan/statemap.h>
++#include <elan/kalloc.h>
++#include <elan/kthread.h>
++#include <elan/kcomm_stats.h>
++#include <elan/devinfo.h>
++
++typedef struct ep_callback
++{
++    struct ep_callback *Next;
++    void              (*Routine)(void *, statemap_t *);
++    void             *Arg;
++} EP_CALLBACK;
++
++#define EP_CB_FLUSH_FILTERING         0
++#define EP_CB_FLUSH_FLUSHING          1
++#define EP_CB_PASSIVATED              2
++#define EP_CB_FAILOVER                        3
++#define EP_CB_DISCONNECTING           4
++#define EP_CB_DISCONNECTED            5
++#define EP_CB_NODESET                 6
++#define EP_CB_COUNT                   7
++
++#endif /* !defined(__ELAN__) */
++
++/* Small unreliable system message queues */
++#define EP_SYSTEMQ_INTR                       0                       /* input queue for cluster membership generating an interrupt */
++#define EP_SYSTEMQ_POLLED             1                       /* input queue for cluster membership polled on clock tick */
++#define EP_SYSTEMQ_MANAGER            2                       /* input queue for manager messages */
++#define EP_NUM_SYSTEMQ                        64
++
++#define EP_SYSTEMQ_ADDR(qnum)         (EP_SYSTEM_QUEUE_BASE + (qnum) * EP_QUEUE_DESC_SIZE)
++#define EP_SYSTEMQ_DESC(base,qnum)    ((base) + (qnum) * EP_QUEUE_DESC_SIZE)
++
++#define EP_SYSTEMQ_MSG_ALIGN          64                      /* message sizes aligned to 64 byte boundaries */
++#define EP_SYSTEMQ_MSG_MAX            (4*64)                  /* max message size */
++
++/* Special flag for Version field to indicate message not
++ * seen in main memory yet and time limit to poll for it */
++#define EP_SYSTEMQ_UNRECEIVED                 0xdeadbabe
++#define EP_SYSTEMQ_UNRECEIVED_TLIMIT          16384                   /* 1023 uS */
++
++#ifndef __ELAN__
++
++typedef void (EP_INPUTQ_HANDLER) (EP_RAIL *rail, void *arg, void *msg);
++typedef void (EP_INPUTQ_CALLBACK) (EP_RAIL *rail, void *arg);
++
++typedef struct ep_inputq
++{
++    unsigned long     q_hidden;                               /* implementation hidden as ep3 or ep4 */
++} EP_INPUTQ;
++
++typedef struct ep_outputq
++{
++    unsigned long     q_hidden;                               /* implementation hidden as ep3 or ep4 */
++} EP_OUTPUTQ;
++
++/* returned values for ep_outputq_state */
++#define EP_OUTPUTQ_BUSY               0
++#define EP_OUTPUTQ_FAILED     1
++#define EP_OUTPUTQ_FINISHED   2
++
++typedef struct ep_switch
++{
++    unsigned    present:1;
++    unsigned  invalid:1;
++    unsigned  link:3;
++    unsigned  bcast:3;
++    unsigned  lnr;
++} EP_SWITCH;
++
++/*
++ * Network error fixup, flush, relocation messges
++ */
++typedef struct ep_map_nmd_body
++{
++    uint32_t          nFrags;
++    EP_RAILMASK               Railmask;
++    EP_NMD            Nmd[EP_MAXFRAG];
++} EP_MAP_NMD_BODY;
++
++typedef struct ep_failover_body
++{
++    EP_XID            Xid;
++    EP_RAILMASK               Railmask;
++} EP_FAILOVER_BODY;
++
++typedef struct ep_failover_txd
++{
++    EP_XID            Xid;
++    uint32_t          Rail;
++    EP_ADDR           TxdRail;
++} EP_FAILOVER_TXD;
++
++typedef uint64_t EP_NETERR_COOKIE;
++
++#define EP_PANIC_STRLEN               31
++
++typedef struct ep_node_state
++{
++    unsigned char       State;
++    unsigned char       NetworkErrorState;
++    EP_RAILMASK         Railmask;
++} EP_NODE_STATE;
++
++#define EP_MANAGER_MSG_SIZE           (2 * EP_SYSTEMQ_MSG_ALIGN)
++
++typedef struct ep_manager_msg_hdr
++{
++    EP_XID            Xid;                                    /* Message transaction id */
++
++    uint16_t          NodeId;                                 /* Originating node number */
++    uint16_t          DestId;                                 /* destination node id */
++
++    uint16_t          Checksum;                               /* Message checksum */
++    uint8_t           Rail;                                   /* Rail message associated with */
++    uint8_t           Type;                                   /* Message type */
++
++    uint32_t          Pad;                                    /* pad to 32 bytes */
++
++    uint32_t          Version;                                /* Message Version */
++} EP_MANAGER_MSG_HDR;
++
++typedef union ep_manager_msg_body
++{
++    unsigned char       Space[EP_MANAGER_MSG_SIZE - sizeof (EP_MANAGER_MSG_HDR)];
++
++    EP_NETERR_COOKIE  Cookies[2];                             /* EP_MSG_TYPE_NETERR */
++    EP_MAP_NMD_BODY   MapNmd;                                 /* EP_MSG_TYPE_MAP_NMD */
++    EP_FAILOVER_BODY  Failover;                               /* EP_MSG_TYPE_FAILOVER_REQUEST */
++    EP_FAILOVER_TXD   FailoverTxd;                            /* EP_MSG_TYPE_FAILOVER_RESPONSE */
++    unsigned char       PanicReason[EP_PANIC_STRLEN+1];               /* EP_MSG_TYPE_REMOTE_PANIC */
++    EP_NODE_STATE       NodeState;                              /* EP_MSG_TYPE_GET_NODE_STATE_RESPONSE */   
++    EP_SERVICE          Service;                                /* EP_MSG_TYPE_GET_NODE_STATE */
++} EP_MANAGER_MSG_BODY;
++
++typedef struct ep_manager_msg
++{
++    EP_MANAGER_MSG_BODY Body;
++    EP_MANAGER_MSG_HDR  Hdr;
++} EP_MANAGER_MSG;
++
++#define EP_MANAGER_MSG_VERSION                                0xcad01000
++#define EP_MANAGER_MSG_TYPE_REMOTE_PANIC              0x00
++#define EP_MANAGER_MSG_TYPE_NETERR_REQUEST            0x01
++#define EP_MANAGER_MSG_TYPE_NETERR_RESPONSE           0x02
++#define EP_MANAGER_MSG_TYPE_FLUSH_REQUEST             0x03
++#define EP_MANAGER_MSG_TYPE_FLUSH_RESPONSE            0x04
++#define EP_MANAGER_MSG_TYPE_MAP_NMD_REQUEST           0x05
++#define EP_MANAGER_MSG_TYPE_MAP_NMD_RESPONSE          0x06
++#define EP_MANAGER_MSG_TYPE_FAILOVER_REQUEST          0x07
++#define EP_MANAGER_MSG_TYPE_FAILOVER_RESPONSE         0x08
++#define EP_MANAGER_MSG_TYPE_GET_NODE_STATE              0x09
++#define EP_MANAGER_MSG_TYPE_GET_NODE_STATE_RESPONSE     0x0a
++
++/* Message types which should only be sent when a rail is connected */
++#define EP_MANAGER_MSG_TYPE_CONNECTED(type)           (((type) & 1) == 1)
++
++#define EP_MANAGER_OUTPUTQ_SLOTS      128                     /* # entries in outputq */
++#define EP_MANAGER_INPUTQ_SLOTS               128                     /* # entries in inputq */
++#define EP_MANAGER_OUTPUTQ_RETRIES    31                      /* # retries for manager messages */
++
++/* XID's are allocated from a cache, which doesn't
++ * require locking since it relies on the caller to
++ * manage the locking for us.
++ */
++typedef struct ep_xid_cache
++{
++    struct list_head  Link;
++
++    uint32_t          Handle;                                 /* my XID cache handle */
++    uint64_t          Current;                                /* range of XID.Unique we can allocate from */
++    uint64_t          Last;
++
++    void            (*MessageHandler)(void *arg, EP_MANAGER_MSG *);
++    void             *Arg;
++} EP_XID_CACHE;
++
++#define EP_XID_CACHE_CHUNKS   (10000)
++
++typedef struct ep_node_rail
++{
++    struct list_head    Link;                                 /* can be linked on work lists */
++
++    unsigned char       State;                                        /* node connection state */
++    unsigned char     NetworkErrorState;                      /* reasons for keeping the context filter up */
++    unsigned char     MessageState;                           /* state of messages during passivate/relocate */
++
++    EP_XID            MsgXid;                                 /* neterr/flush transaction id */
++    long              NextRunTime;                            /* time to drop context filter for destroyed dma packet, or to send next request */
++    EP_NETERR_COOKIE  NetworkErrorCookies[2];                 /* identify cookie for destroyed atomic packet */
++
++    uint32_t          Cookie;                                 /* per-node network error cookie */
++    spinlock_t                CookieLock;                             /* and spinlock for it. */
++
++    struct list_head    StalledDmas;                          /* list of stalled DMAs */
++} EP_NODE_RAIL;
++
++#define EP_NODE_DISCONNECTED          0                       /* node is disconnected */
++#define EP_NODE_CONNECTING            1                       /* awaiting connection */
++#define EP_NODE_CONNECTED             2                       /* node is connected */
++#define EP_NODE_LEAVING_CONNECTED     3                       /* node is starting to disconnect */
++#define EP_NODE_LOCAL_PASSIVATE       4                       /* flushing context filter/run queues */
++#define EP_NODE_REMOTE_PASSIVATE      5                       /* stalling for neterr flush */
++#define EP_NODE_PASSIVATED            6                       /* relocating active/passive messages */
++#define EP_NODE_DISCONNECTING         7                       /* entering disconncted - abort remaining comms */
++#define EP_NODE_NUM_STATES            8
++
++#define EP_NODE_NETERR_ATOMIC_PACKET  (1 << 0)
++#define EP_NODE_NETERR_DMA_PACKET     (1 << 1)
++
++#define EP_NODE_PASSIVE_MESSAGES      (1 << 0)
++#define EP_NODE_ACTIVE_MESSAGES               (1 << 1)
++
++/*
++ * Kernel thread code is loaded as a table.
++ */
++typedef struct ep_symbol
++{
++    char   *name;
++    EP_ADDR value;
++} EP_SYMBOL;
++
++typedef struct ep_code
++{
++    u_char        *text;
++    u_int        text_size;
++    u_char        *data;
++    u_int        data_size;
++    u_char        *rodata;
++    u_int        rodata_size;
++    EP_SYMBOL     *symbols;
++    
++    int                  ntext;
++    sdramaddr_t    pptext;
++    EP_ADDR      etext;
++    sdramaddr_t   _stext;
++    sdramaddr_t         _rodata;
++
++    int                  ndata;
++    sdramaddr_t    ppdata;
++    EP_ADDR      edata;
++    sdramaddr_t   _sdata;
++} EP_CODE;
++
++typedef struct ep_switchstate
++{
++    unsigned char       linkid;
++    unsigned char       LNR;
++    unsigned char       bcast;
++    unsigned char       uplink;
++} EP_SWITCHSTATE;
++
++typedef struct ep_rail_ops
++{
++    void      (*DestroyRail) (EP_RAIL *rail);
++
++    int       (*StartRail) (EP_RAIL *rail);
++    void      (*StallRail) (EP_RAIL *rail);
++    void      (*StopRail) (EP_RAIL *rail);
++
++    sdramaddr_t (*SdramAlloc) (EP_RAIL *rail, EP_ADDR addr, unsigned size);
++    void        (*SdramFree) (EP_RAIL *rail, sdramaddr_t addr, unsigned size);
++    void        (*SdramWriteb) (EP_RAIL *rail, sdramaddr_t addr, unsigned char val);
++    
++    void      (*KaddrMap) (EP_RAIL *rail, EP_ADDR eaddr, virtaddr_t kaddr, unsigned len, unsigned int perm, int ep_attr);
++    void      (*SdramMap) (EP_RAIL *rail, EP_ADDR eaddr, sdramaddr_t saddr, unsigned len, unsigned int perm, int ep_attr);
++    void      (*Unmap) (EP_RAIL *rail, EP_ADDR eaddr, unsigned len);
++
++    void       *(*DvmaReserve) (EP_RAIL *rail, EP_ADDR eaddr, unsigned npages);
++    void      (*DvmaRelease) (EP_RAIL *rail, EP_ADDR eaddr, unsigned npages, void *private);
++    void      (*DvmaSetPte) (EP_RAIL *rail, void *private, unsigned index, physaddr_t phys, unsigned int perm);
++    physaddr_t        (*DvmaReadPte) (EP_RAIL *rail, void *private, unsigned index);
++    void      (*DvmaUnload)(EP_RAIL *rail, void *private, unsigned index, unsigned npages);
++    void      (*FlushTlb) (EP_RAIL *rail);
++
++    int       (*ProbeRoute) (EP_RAIL *r, int level, int sw, int nodeid, int *linkup, 
++                             int *linkdown, int attempts, EP_SWITCH *lsw);
++    void      (*PositionFound) (EP_RAIL *rail, ELAN_POSITION *pos);
++    int               (*CheckPosition) (EP_RAIL *rail);
++    void      (*NeterrFixup) (EP_RAIL *rail, unsigned int nodeId, EP_NETERR_COOKIE *cookies);
++
++    void      (*LoadSystemRoute) (EP_RAIL *rail, unsigned int vp, unsigned int lowNode, unsigned int highNode);
++
++    void      (*LoadNodeRoute) (EP_RAIL *rail, unsigned nodeId);
++    void      (*UnloadNodeRoute) (EP_RAIL *rail, unsigned nodeId);
++    void      (*LowerFilter) (EP_RAIL *rail, unsigned nodeId);
++    void      (*RaiseFilter) (EP_RAIL *rail, unsigned nodeId);
++    void      (*NodeDisconnected) (EP_RAIL *rail, unsigned nodeId);
++
++    void      (*FlushFilters) (EP_RAIL *rail);
++    void      (*FlushQueues) (EP_RAIL *rail);
++
++
++    EP_INPUTQ  *(*AllocInputQ) (EP_RAIL *rail, unsigned qnum, unsigned slotSize, unsigned slotCount,
++                              void (*callback)(EP_RAIL *rail, void *arg), void *arg);
++    void      (*FreeInputQ) (EP_RAIL *rail, EP_INPUTQ *q);
++    void      (*EnableInputQ) (EP_RAIL *rail, EP_INPUTQ *q);
++    void      (*DisableInputQ) (EP_RAIL *rail, EP_INPUTQ *q);
++    int               (*PollInputQ) (EP_RAIL *rail, EP_INPUTQ *q, int maxCount, EP_INPUTQ_HANDLER *handler, void *arg);
++
++    EP_OUTPUTQ *(*AllocOutputQ) (EP_RAIL *rail, unsigned slotSize, unsigned slotCount);
++    void      (*FreeOutputQ) (EP_RAIL *rail, EP_OUTPUTQ *outputq);
++    void       *(*OutputQMsg) (EP_RAIL *rail, EP_OUTPUTQ *outputq, unsigned slotNum);
++    int         (*OutputQState) (EP_RAIL *rail, EP_OUTPUTQ *outputq, unsigned slotNum);
++    int               (*OutputQSend) (EP_RAIL *rail, EP_OUTPUTQ *outputq, unsigned slotNum, unsigned size,
++                              unsigned vp, unsigned qnum, unsigned retries);
++
++    void        (*FillOutStats) (EP_RAIL *rail, char *str);
++    void      (*Debug) (EP_RAIL *rail);
++
++} EP_RAIL_OPS;
++
++#define ep_alloc_inputq(rail,qnum,slotSize,slotCount,callback,arg) \
++      (rail)->Operations.AllocInputQ(rail,qnum,slotSize,slotCount,callback,arg)
++#define ep_free_inputq(rail,inputq) \
++      (rail)->Operations.FreeInputQ(rail,inputq)
++#define ep_enable_inputq(rail,inputq) \
++      (rail)->Operations.EnableInputQ(rail,inputq)
++#define ep_disable_inputq(rail,inputq) \
++      (rail)->Operations.DisableInputQ(rail,inputq)
++#define ep_poll_inputq(rail,inputq,maxCount,handler,arg) \
++      (rail)->Operations.PollInputQ(rail,inputq,maxCount,handler,arg)
++#define ep_alloc_outputq(rail,slotSize,slotCount)\
++      (rail)->Operations.AllocOutputQ(rail,slotSize,slotCount)
++#define ep_free_outputq(rail,outputq)\
++      (rail)->Operations.FreeOutputQ(rail,outputq)
++#define ep_outputq_msg(rail,outputq,slotNum)\
++      (rail)->Operations.OutputQMsg(rail,outputq,slotNum)
++#define ep_outputq_state(rail,outputq,slotNum)\
++      (rail)->Operations.OutputQState(rail,outputq,slotNum)
++#define ep_outputq_send(rail,outputq,slotNum,size,vp,qnum,retries)\
++      (rail)->Operations.OutputQSend(rail,outputq,slotNum,size,vp,qnum,retries)
++
++struct ep_rail
++{
++    EP_SYS           *System;                                 /* "system" we've attached to */
++
++    unsigned char     Number;                                 /* Rail number */
++    unsigned char       State;                                        /* Rail state */
++    char              Name[32];                               /* Rail name */
++
++    struct list_head    ManagerLink;                          /* linked on ManagedRails list */
++
++    ELAN_DEVINFO      Devinfo;                                /* Device information for this rail */
++    ELAN_POSITION       Position;                             /* Position on switch device is connected to */
++
++    EP_RAIL_OPS               Operations;                             /* device specific operations */
++    EP_RAIL_STATS     Stats;                                  /* statistics */
++
++    EP_ALLOC            ElanAllocator;                                /* per-rail elan memory allocator */
++    EP_ALLOC            MainAllocator;                                /* per-rail main memory allocator */
++
++    unsigned          TlbFlushRequired;                       /* lazy TLB flushing */
++
++    int               SwitchBroadcastLevel;                   /* current switch level ok for broadcast */
++    unsigned long       SwitchBroadcastLevelTick;
++
++    int                       SwitchProbeLevel;                       /* result of last switch probe */
++    EP_SWITCHSTATE      SwitchState[ELAN_MAX_LEVELS];
++    EP_SWITCHSTATE      SwitchLast[ELAN_MAX_LEVELS];
++    unsigned long       SwitchProbeTick[ELAN_MAX_LEVELS];
++    
++    /* Node disconnecting/connecting state */
++    EP_CALLBACK        *CallbackList[EP_CB_COUNT];            /* List of callbacks */
++    kmutex_t          CallbackLock;                           /*   and lock for it. */
++    unsigned          CallbackStep;                           /*  step through UpdateConnectionState. */
++
++    /* back pointer for cluster membership */
++    void             *ClusterRail;
++
++    /* Per node state for message passing */
++    EP_NODE_RAIL       *Nodes;                                        /* array of per-node state */
++    statemap_t         *NodeSet;                              /* per-rail statemap of connected nodes */
++    statemap_t               *NodeChangeMap;                          /* statemap of nodes to being connected/disconnected */
++    statemap_t               *NodeChangeTmp;                          /*   and temporary copies */
++
++    struct list_head    NetworkErrorList;                     /* list of nodes resolving network errors */
++    struct list_head    LocalPassivateList;                   /* list of nodes in state LOCAL_PASSIVATE */
++    struct list_head    RemotePassivateList;                  /* list of nodes waiting for remote network error flush */
++    struct list_head    PassivatedList;                               /* list of nodes performing message relocation */
++    struct list_head    DisconnectingList;                    /* list of nodes transitioning to disconnected */
++
++    EP_XID_CACHE      XidCache;                               /* XID cache for node messages (single threaded access) */
++
++    /* Manager messages */
++    EP_INPUTQ        *ManagerInputQ;
++    EP_OUTPUTQ               *ManagerOutputQ;
++    unsigned          ManagerOutputQNextSlot;
++    spinlock_t                ManagerOutputQLock;
++
++    /* /proc entries */
++    struct proc_dir_entry *ProcDir;
++    struct proc_dir_entry *SvcIndicatorDir;
++    int                    CallbackRegistered;
++};
++
++/* values for State */
++#define EP_RAIL_STATE_UNINITIALISED   0                       /* device uninitialised */
++#define EP_RAIL_STATE_STARTED         1                       /* device started but network position unknown */
++#define EP_RAIL_STATE_RUNNING         2                       /* device started and position known */
++#define EP_RAIL_STATE_INCOMPATIBLE    3                       /* device started, but position incompatible */
++
++typedef struct ep_rail_entry
++{
++    struct list_head  Link;
++    EP_RAIL          *Rail;
++} EP_RAIL_ENTRY;
++
++typedef struct ep_subsys
++{
++    EP_SYS           *Sys;
++
++    struct list_head  Link;                                   /* Linked on sys->Subsystems */
++    char             *Name;                                   /* Name to lookup */
++    
++    void             (*Destroy)    (struct ep_subsys *subsys, EP_SYS *sys);
++
++    int                      (*AddRail)    (struct ep_subsys *subsys, EP_SYS *sys, EP_RAIL *rail);
++    void             (*RemoveRail) (struct ep_subsys *subsys, EP_SYS *sys, EP_RAIL *rail);
++} EP_SUBSYS;
++
++typedef struct ep_node
++{
++    EP_RAILMASK               ConnectedRails;
++} EP_NODE;
++
++struct ep_sys
++{
++    EP_RAIL         *Rails[EP_MAX_RAILS];                     /* array of all available devices */
++
++    kmutex_t       StartStopLock;                             /* lock for starting stopping rails */
++
++    ELAN_POSITION    Position;                                        /* primary node position */
++
++    EP_NMH_TABLE     MappingTable;                            /* Network mapping handle table */
++
++    EP_ALLOC       Allocator;                                 /* shared main memory allocator */
++
++    EP_DVMA_STATE    DvmaState;                                       /* dvma state */
++
++    kmutex_t       SubsysLock;                               /* lock on the Subsytems list */
++    struct list_head Subsystems;                              /* list of subsystems */
++
++    /* device manager state */
++    struct list_head ManagedRails;                            /* list of managed devices */
++    EP_KTHREAD       ManagerThread;                           /* place for manager thread to sleep */
++
++    /* global node state */
++    spinlock_t             NodeLock;                                  /* spinlock for node state (including per-device node state) */
++    EP_NODE       *Nodes;                                     /* system wide node state */
++    statemap_t      *NodeSet;                                 /* system wide nodeset */
++    struct list_head NodesetCallbackList;                     /* list of "callbacks" */
++
++    /* Transaction Id */
++    struct list_head XidCacheList;                            /* list of XID caches */
++    uint32_t       XidGeneration;                             /* XID generation number (distinguishes reboots) */
++    uint32_t       XidHandle;                                 /* XID handles (distinguishes XID caches) */
++    uint64_t       XidNext;                                   /* next XID to prime cache */
++    spinlock_t             XidLock;                                   /*   and it's spinlock  */
++
++    /* Shutdown/Panic */
++    unsigned int     Shutdown;                                        /* node has shutdown/panic'd */
++};
++
++#if defined(DEBUG_ASSERT)
++extern int ep_assfail (EP_RAIL *rail, const char *string, const char *func, const char *file, const int line);
++extern int sdram_assert;
++extern int assfail_mode;
++
++#define EP_ASSERT(rail, EX)   do { \
++    if (!(EX) && ep_assfail ((EP_RAIL *) (rail), #EX, __FUNCTION__, __FILE__, __LINE__)) { \
++      BUG(); \
++    } \
++} while (0)
++#define EP_ASSFAIL(rail,EX)   do { \
++   if (ep_assfail ((EP_RAIL *) (rail), EX, __FUNCTION__, __FILE__, __LINE__)) { \
++      BUG(); \
++    } \
++} while (0)
++#define SDRAM_ASSERT(EX)      (sdram_assert ? (EX) : 1)
++#else
++#define EP_ASSERT(rail, EX)   ((void) 0)
++#define EP_ASSFAIL(rail,str)  ((void) 0)
++#define SDRAM_ASSERT(EX)      (1)
++#endif
++
++/* conf_osdep.c */
++extern EP_SYS    *ep_system(void);
++extern void       ep_mod_dec_usecount (void);
++extern void       ep_mod_inc_usecount (void);
++
++/* procfs_osdep.c */
++extern struct proc_dir_entry *ep_procfs_root;
++extern struct proc_dir_entry *ep_config_root;
++
++/* kcomm.c */
++extern int        ep_sys_init (EP_SYS *sys);
++extern void       ep_sys_fini (EP_SYS *sys);
++extern void     ep_shutdown (EP_SYS *sys);
++extern int        ep_init_rail (EP_SYS *sys, EP_RAIL *rail);
++extern void       ep_destroy_rail (EP_RAIL *rail);
++extern int        ep_start_rail (EP_RAIL *rail);
++extern void       ep_stop_rail (EP_RAIL *rail);
++
++extern void       ep_connect_node (EP_RAIL *rail, int nodeId);
++extern int        ep_disconnect_node (EP_RAIL *rail, int nodeId);
++
++extern EP_XID     ep_xid_cache_alloc (EP_SYS *sys, EP_XID_CACHE *cache);
++extern void       ep_xid_cache_init (EP_SYS *sys, EP_XID_CACHE *cache);
++extern void       ep_xid_cache_destroy (EP_SYS *sys, EP_XID_CACHE *cache);
++
++extern int        ep_send_message (EP_RAIL *rail, int nodeId, int type, EP_XID xid, EP_MANAGER_MSG_BODY *body);
++
++extern void       ep_panic_node (EP_SYS *sys, int nodeId, unsigned char *reason);
++
++extern void     ep_subsys_add (EP_SYS *sys, EP_SUBSYS *subsys);
++extern void     ep_subsys_del (EP_SYS *sys, EP_SUBSYS *subsys);
++extern EP_SUBSYS *ep_subsys_find (EP_SYS *sys, char *name);
++
++extern void       DisplayNodes (EP_RAIL *rail);
++
++extern void       ep_fillout_stats(EP_RAIL *rail, char *str);
++
++/* neterr.c */
++extern void       ep_queue_network_error (EP_RAIL *rail, int nodeId, int what, int channel, EP_NETERR_COOKIE cookie);
++
++/* kcomm_elan3.c */
++extern unsigned int ep3_create_rails (EP_SYS *sys, unsigned int disabled);
++
++/* kcomm_elan4.c */
++extern unsigned int ep4_create_rails (EP_SYS *sys, unsigned int disabled);
++
++/* probenetwork.c */
++extern int      ProbeNetwork (EP_RAIL *rail, ELAN_POSITION *pos);
++extern void     CheckPosition (EP_RAIL *rail);
++
++extern uint16_t   CheckSum (char *msg, int nob);
++
++/* threadcode.c */
++extern EP_ADDR    ep_symbol (EP_CODE *code, char *name);
++extern int        ep_loadcode (EP_RAIL *rail, EP_CODE *code);
++extern void       ep_unloadcode (EP_RAIL *rail, EP_CODE *code);
++
++/* Public interface */
++/* debug.c */
++extern int              ep_sprintf_bitmap (char *str, unsigned nbytes, bitmap_t *bitmap, int base, int count, int off);
++extern void             ep_display_bitmap (char *prefix, char *tag, bitmap_t *bitmap, unsigned base, unsigned nbits);
++
++/* epcomms.c */
++extern int              ep_waitfor_nodeid (EP_SYS *sys);
++extern int              ep_nodeid (EP_SYS *sys);
++extern int              ep_numnodes (EP_SYS *sys);
++
++/* railhints.c */
++extern int              ep_pickRail(EP_RAILMASK railmask);
++
++/* support.c */
++extern int              ep_register_nodeset_callback (EP_SYS *sys, void (*routine)(void *, statemap_t *), void *arg);
++extern void             ep_remove_nodeset_callback (EP_SYS *sys, void (*routine)(void *, statemap_t *), void *arg);
++extern void             ep_call_nodeset_callbacks (EP_SYS *sys, statemap_t *map);
++
++extern int              ep_register_callback (EP_RAIL *rail, unsigned idx, void (*routine)(void *, statemap_t *), void *arg);
++extern void             ep_remove_callback (EP_RAIL *rail, unsigned idx, void (*routine)(void *, statemap_t *), void *arg);
++extern void             ep_call_callbacks (EP_RAIL *rail, unsigned idx, statemap_t *);
++extern unsigned int     ep_backoff (EP_BACKOFF *backoff, int type);
++
++#endif /* !__ELAN__ */
++
++typedef struct display_info {
++    void (*func)(long, char *, ...);
++    long arg;
++} DisplayInfo;
++
++extern DisplayInfo di_ep_debug;
++
++
++#endif /* __ELAN_KCOMM_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/include/elan/kcomm_stats.h
+===================================================================
+--- linux-2.4.21.orig/include/elan/kcomm_stats.h       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan/kcomm_stats.h    2005-06-01 23:12:54.712421928 -0400
+@@ -0,0 +1,153 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __EP_EPSTATS_H
++#define __EP_EPSTATS_H
++
++#ident "$Id: kcomm_stats.h,v 1.4.8.1 2004/11/12 10:54:51 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/kcomm_stats.h,v $ */
++
++#define EP_BUCKET_SLOTS                       8
++
++#define BucketStat(obj,stat,size)     ((size) < 128   ? (obj)->Stats.stat[0]++ : \
++                                       (size) < 512   ? (obj)->Stats.stat[1]++ : \
++                                       (size) < 1024  ? (obj)->Stats.stat[2]++ : \
++                                       (size) < 8192  ? (obj)->Stats.stat[3]++ : \
++                                       (size) < 16384 ? (obj)->Stats.stat[4]++ : \
++                                       (size) < 32768 ? (obj)->Stats.stat[5]++ : \
++                                       (size) < 65536 ? (obj)->Stats.stat[6]++ : \
++                                       (obj)->Stats.stat[7]++)
++#define IncrStat(obj,stat)            ((obj)->Stats.stat++)
++
++
++#define EP3_NUM_DMA_FAIL              11      /* NOTE - the same as EP_NUM_RETRIES */
++
++#define ADD_STAT(STATS,STAT,VALUE) { unsigned long now = lbolt;\
++   STATS.STAT.total += VALUE; \
++   if  ( ( now - STATS.STAT.last_time ) > HZ ) { \
++ STATS.STAT.last_per_sec = ( STATS.STAT.total - STATS.STAT.last_count)/ ( (( now - STATS.STAT.last_time ) + (HZ/2)) / HZ);\
++ STATS.STAT.last_time = now; \
++ STATS.STAT.last_count = STATS.STAT.total; \
++   }} \
++
++#define INC_STAT(STATS,STAT) ADD_STAT(STATS,STAT,1)
++
++#define GET_STAT_PER_SEC(STATS, STAT) (  (( lbolt - STATS.STAT.last_time ) < (HZ * 5)) ? STATS.STAT.last_per_sec : 0 )
++#define GET_STAT_TOTAL(STATS, STAT) ( STATS.STAT.total )
++
++struct ep_stats_count 
++{
++    unsigned long total;
++    unsigned long last_time;
++    unsigned long last_count;
++    unsigned long last_per_sec;
++};
++
++typedef struct ep_stats_count         EP_STATS_COUNT;
++
++typedef struct ep3_rail_stats
++{
++    unsigned long     IssueDmaFail[EP3_NUM_DMA_FAIL];
++
++    unsigned long     DmaQueueLength[EP_BUCKET_SLOTS];
++    unsigned long     CprocDmaQueueOverflow;
++    unsigned long     DprocDmaQueueOverflow;
++    unsigned long     IprocDmaQueueOverflow;
++    unsigned long     CprocEventQueueOverflow;
++    unsigned long     DprocEventQueueOverflow;
++    unsigned long     IprocEventQueueOverflow;
++
++    unsigned long     QueueingPacketTrap;
++    unsigned long     DmaIdentifyTrap;
++    unsigned long     ThreadIdentifyTrap;
++    unsigned long     DmaPacketTrap;
++} EP3_RAIL_STATS;
++
++typedef struct ep4_rail_stats
++{
++    unsigned long       somestatsgohere;
++} EP4_RAIL_STATS;
++
++typedef struct ep_rail_stats
++{
++    unsigned long     SendMessageFailed;
++    unsigned long     NeterrAtomicPacket;
++    unsigned long       NeterrDmaPacket;
++
++    EP_STATS_COUNT      rx;
++    EP_STATS_COUNT      rx_len;
++
++    EP_STATS_COUNT      tx;
++    EP_STATS_COUNT      tx_len;
++
++} EP_RAIL_STATS;
++
++typedef struct ep_cm_rail_stats
++{
++    /* cluster membership statistics */
++    unsigned long     HeartbeatsSent;
++    unsigned long     HeartbeatsRcvd;
++    
++    unsigned long     RetryHeartbeat;
++    unsigned long     RejoinRequest;
++    unsigned long     RejoinTooSlow;
++    unsigned long     LaunchMessageFail;
++    unsigned long     MapChangesSent;
++
++    /* Heartbeat scheduling stats */
++    unsigned long     HeartbeatOverdue;
++} EP_CM_RAIL_STATS;
++
++typedef struct ep_comms_rail_stats
++{
++    /* kernel comms large message statistics */
++    unsigned long     TxEnveEvent;
++    unsigned long     TxDataEvent;
++    unsigned long     TxDoneEvent;
++    unsigned long     RxDoneEvent;
++    unsigned long     MulticastTxDone;
++    unsigned long     QueueReceive;
++
++    unsigned long     TxEnveRetry;
++    unsigned long     TxDataRetry;
++    unsigned long     TxDoneRetry;
++    unsigned long     RxThrdEvent;
++    unsigned long     RxDataRetry;
++    unsigned long     RxDoneRetry;
++    unsigned long     StallThread;
++    unsigned long     ThrdWaiting;
++    unsigned long     CompleteEnvelope;
++
++    unsigned long     NoFreeTxds;
++    unsigned long     NoFreeRxds;
++
++    unsigned long     LockRcvrTrapped;
++} EP_COMMS_RAIL_STATS;
++
++typedef struct ep_comms_stats
++{
++    unsigned long     DataXmit[8];
++    unsigned long     McastXmit[8];
++    unsigned long     RPCXmit[8];
++    unsigned long     RPCPut[8];
++    unsigned long     RPCGet[8];
++    unsigned long     CompleteRPC[8];
++    unsigned long     RxData[8];
++    unsigned long     RxMcast[8];
++
++    unsigned long     NoFreeTxds;
++    unsigned long     NoFreeRxds;
++} EP_COMMS_STATS;
++
++#endif /* __EP_EPSTATS_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/include/elan/kmap.h
+===================================================================
+--- linux-2.4.21.orig/include/elan/kmap.h      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan/kmap.h   2005-06-01 23:12:54.713421776 -0400
+@@ -0,0 +1,68 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN_KMAP_H
++#define __ELAN_KMAP_H
++
++#ident "$Id: kmap.h,v 1.3.8.1 2004/12/14 10:19:14 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/kmap.h,v $ */
++
++#include <elan/rmap.h>
++
++extern void ep_perrail_kaddr_map (EP_RAIL *rail, EP_ADDR eaddr, virtaddr_t vaddr, unsigned long len, unsigned int perm, int ep_attr);
++extern void ep_perrail_sdram_map (EP_RAIL *rail, EP_ADDR eaddr, sdramaddr_t saddr, unsigned long len, unsigned int perm, int ep_attr);
++extern void ep_perrail_unmap (EP_RAIL *rail, EP_ADDR eaddr, unsigned long len);
++extern void ep_perrail_dvma_sync (EP_RAIL *rail);
++
++typedef struct ep_dvma_nmh
++{
++    EP_NMH            dvma_nmh;
++    
++    struct list_head  dvma_link;                              /* chained on ep_dvma_state */
++    unsigned          dvma_perm;                              /* permissions for region */
++
++    spinlock_t                dvma_lock;
++    EP_RAILMASK               dvma_railmask;                          /* bitmap of rails */
++    EP_RAIL          *dvma_rails[EP_MAX_RAILS];               /* assoicated rails */
++    void             *dvma_private[EP_MAX_RAILS];             /* pointers to rail private data */
++    unsigned int        dvma_attrs[1];                                /* bitmap of which rails pages are loaded NOTE - max 32 rails */
++} EP_DVMA_NMH;
++
++/* values for dvma_perm */
++#define EP_PERM_EXECUTE               0
++#define EP_PERM_READ          1
++#define EP_PERM_WRITE         2
++#define EP_PERM_ALL           3
++
++typedef struct ep_dvma_state
++{
++    kmutex_t          dvma_lock;
++    struct list_head    dvma_handles;
++    struct list_head    dvma_rails;
++    EP_RMAP          *dvma_rmap;
++} EP_DVMA_STATE;
++
++extern void    ep_dvma_init (EP_SYS *sys);
++extern void    ep_dvma_fini (EP_SYS *sys);
++extern EP_NMH *ep_dvma_reserve (EP_SYS *sys, unsigned npages, unsigned perm);
++extern void    ep_dvma_release (EP_SYS *sys, EP_NMH *nmh);
++extern void    ep_dvma_load (EP_SYS *sys, void *map, caddr_t vaddr, unsigned len, 
++                           EP_NMH *nmh, unsigned index, EP_RAILMASK *hints, EP_NMD *subset);
++extern void    ep_dvma_unload (EP_SYS *sys, EP_NMH *nmh, EP_NMD *nmd);
++  
++extern void    ep_dvma_remove_rail (EP_SYS *sys, EP_RAIL *rail);
++extern int     ep_dvma_add_rail (EP_SYS *sys, EP_RAIL *rail);
++
++extern uint16_t rolling_check_sum (char *msg, int nob, uint16_t sum);
++
++#endif /* __ELAN_KMAP_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/include/elan/kmsg.h
+===================================================================
+--- linux-2.4.21.orig/include/elan/kmsg.h      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan/kmsg.h   2005-06-01 23:12:54.713421776 -0400
+@@ -0,0 +1,14 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN_KMSG_H
++#define __ELAN_KMSG_H
++
++#ident "@(#)$Id: kmsg.h,v 1.1 2003/09/23 13:55:12 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/kmsg.h,v $ */
++
++#endif /* __ELAN_KMSG_H */
+Index: linux-2.4.21/include/elan/kthread.h
+===================================================================
+--- linux-2.4.21.orig/include/elan/kthread.h   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan/kthread.h        2005-06-01 23:12:54.713421776 -0400
+@@ -0,0 +1,53 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN3_KTHREAD_H
++#define __ELAN3_KTHREAD_H
++
++#ident "@(#)$Id: kthread.h,v 1.4 2004/05/06 14:24:08 david Exp $ $Name: QSNETMODULES-4-30_20050128 $"
++/*      $Source: /cvs/master/quadrics/epmod/kthread.h,v $*/
++
++typedef struct ep_kthread
++{
++      kcondvar_t      wait;                                   /* place to sleep */
++      spinlock_t      lock;                                   /* and lock */
++      long            next_run;                               /* tick when thread should next run */
++      long            running;                                /* tick when thread started to run */
++      unsigned short  should_stall;
++      unsigned char   state;
++      unsigned int    started:1;
++      unsigned int    should_stop:1;
++      unsigned int    stopped:1;
++} EP_KTHREAD;
++
++#define KT_STATE_SLEEPING             0
++#define KT_STATE_SCHEDULED            1
++#define KT_STATE_RUNNING              2
++#define KT_STATE_STALLED              3
++
++#define AFTER(a, b)                   ((((long)(a)) - ((long)(b))) > 0)
++#define BEFORE(a,b)                   ((((long)(a)) - ((long)(b))) < 0)
++
++extern void ep_kthread_init (EP_KTHREAD *kt);
++extern void ep_kthread_destroy (EP_KTHREAD *kt);
++extern void ep_kthread_started (EP_KTHREAD *kt);
++extern void ep_kthread_stopped (EP_KTHREAD *kt);
++extern int  ep_kthread_should_stall (EP_KTHREAD *kth);
++extern int  ep_kthread_sleep (EP_KTHREAD *kth, long next_run);
++extern void ep_kthread_schedule (EP_KTHREAD *kt, long when);
++extern void ep_kthread_stall (EP_KTHREAD *kth);
++extern void ep_kthread_resume (EP_KTHREAD *kt);
++extern void ep_kthread_stop (EP_KTHREAD *kt);
++extern int  ep_kthread_state (EP_KTHREAD *kt, long *time);
++#endif /* __ELAN3_KTHREAD_H */
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.21/include/elan/nmh.h
+===================================================================
+--- linux-2.4.21.orig/include/elan/nmh.h       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan/nmh.h    2005-06-01 23:12:54.714421624 -0400
+@@ -0,0 +1,95 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN3_NMH_H
++#define __ELAN3_NMH_H
++
++#ident "@(#)$Id: nmh.h,v 1.7 2004/01/06 10:29:55 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/nmh.h,v $*/
++
++
++/* Forward declarations */
++typedef struct ep_nmd         EP_NMD;
++typedef struct ep_nmh_ops     EP_NMH_OPS;
++typedef struct ep_nmh         EP_NMH;
++
++/* Railmask held in 16 bit field (packs with nodeId into NMD */
++typedef uint16_t              EP_RAILMASK;
++
++#define EP_RAIL2RAILMASK(rnum)        (1 << (rnum))
++#define EP_RAILMASK_ALL               0xffff
++
++/* kernel comms elan network address */
++typedef uint32_t              EP_ADDR;
++
++/* network mapping descriptor - this is returned to the user from a map operation,
++ * and is what is passed to all communication functions */
++struct ep_nmd
++{
++    EP_ADDR   nmd_addr;                                       /* base address */
++    uint32_t  nmd_len;                                        /* size in bytes */
++    uint32_t  nmd_attr;                                       /* nodeid << 16 | railmask */
++};
++
++#define EP_NMD_ATTR(nodeid,railmask)  (((nodeid) << 16) | (railmask))
++#define EP_NMD_NODEID(nmd)            ((nmd)->nmd_attr >> 16)
++#define EP_NMD_RAILMASK(nmd)          ((nmd)->nmd_attr & EP_RAILMASK_ALL)
++
++#if !defined(__ELAN__)
++
++struct ep_nmh_ops
++{
++    int          (*op_map_rails) (EP_SYS *sys, EP_NMH *nmh, EP_NMD *nmd, EP_RAILMASK mask);   /* add mappings to different rail(s) */
++
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++    uint16_t (*op_calc_check_sum) (EP_SYS *sys, EP_NMH *nmh, EP_NMD *nmd, uint16_t check_sum); /* calculates check sum              */
++#endif
++};
++
++struct ep_nmh
++{
++    EP_NMD         nmh_nmd;                                   /* public field */
++    struct list_head nmh_link;                                        /* linked on hash table */
++    EP_NMH_OPS            *nmh_ops;                                   /* operations to perform on object */
++};
++
++#define EP_NMH_NUMHASH                        (32 - 11 + 1)           /* one hash table for each power of 2 above pagesize */
++#define EP_NMH_HASHSIZE                       (64)                    /* max size of each hash table */
++
++typedef struct ep_nmh_table
++{
++    struct list_head *tbl_hash[EP_NMH_NUMHASH];
++    unsigned        tbl_size[EP_NMH_NUMHASH];
++} EP_NMH_TABLE;
++
++extern int         ep_nmh_init (EP_NMH_TABLE *tbl);
++extern void        ep_nmh_fini (EP_NMH_TABLE *tbl);
++
++extern void        ep_nmh_insert (EP_NMH_TABLE *tbl, EP_NMH *nmd);
++extern void        ep_nmh_remove (EP_NMH_TABLE *tbl, EP_NMH *nmd);
++extern EP_NMH     *ep_nmh_find (EP_NMH_TABLE *tbl, EP_NMD *nmh);
++
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++extern uint32_t    ep_nmd_calc_data_check_sum(EP_SYS *sys, EP_NMD *nmd, int nFrags);
++#endif
++
++/* Public interface */
++extern EP_RAILMASK ep_nmd2railmask (EP_NMD *frags, int nFrags);
++extern void        ep_nmd_subset (EP_NMD *subset, EP_NMD *nmd, unsigned off, unsigned len);
++extern int       ep_nmd_merge (EP_NMD *merged, EP_NMD *a, EP_NMD *b);
++extern int         ep_nmd_map_rails (EP_SYS *sys, EP_NMD *nmd, unsigned railmask);
++
++#endif /* __ELAN__ */
++
++#endif /* __ELAN3_NMH_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/include/elan/rmap.h
+===================================================================
+--- linux-2.4.21.orig/include/elan/rmap.h      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan/rmap.h   2005-06-01 23:12:54.714421624 -0400
+@@ -0,0 +1,49 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN_RMAP_H
++#define __ELAN_RMAP_H
++
++#ident "$Id: rmap.h,v 1.8 2004/05/19 10:24:40 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/rmap.h,v $ */
++
++
++typedef struct ep_rmap_entry 
++{
++    size_t    m_size;
++    u_long    m_addr;
++} EP_RMAP_ENTRY;
++
++typedef struct ep_rmap 
++{
++    spinlock_t             m_lock;
++    kcondvar_t       m_wait;
++    u_int          m_size;
++    u_int          m_free;
++    u_int          m_want;
++    char            *m_name;
++    EP_RMAP_ENTRY    m_map[1];
++} EP_RMAP;
++
++extern void        ep_display_rmap (EP_RMAP *map);
++
++extern void          ep_rmapinit (EP_RMAP *rmap, char *name, u_int mapsize);
++extern unsigned long ep_rmalloc (EP_RMAP *rmap, size_t size, int cansleep);
++extern unsigned long ep_rmalloc_constrained (EP_RMAP *mp, size_t size, unsigned long alo, unsigned long ahi, unsigned long align, int cansleep);
++extern void          ep_rmfree (EP_RMAP *rmap, size_t size, unsigned long addr);
++extern unsigned long ep_rmget (EP_RMAP *rmap, size_t size, unsigned long addr);
++extern EP_RMAP      *ep_rmallocmap (size_t size, char *name, int cansleep);
++extern void          ep_rmfreemap (EP_RMAP *map);
++
++#endif /* __ELAN3_RMAP_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/include/elan/statemap.h
+===================================================================
+--- linux-2.4.21.orig/include/elan/statemap.h  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan/statemap.h       2005-06-01 23:12:54.714421624 -0400
+@@ -0,0 +1,52 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN_STATEMAP_H
++#define __ELAN_STATEMAP_H
++
++#ident "$Id: statemap.h,v 1.8 2003/10/07 13:22:38 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/statemap.h,v $ */
++
++#include <elan/bitmap.h>
++
++/******************************** global state bitmap stuff **********************************/
++typedef struct
++{
++   unsigned int size;
++   unsigned int nob;
++   unsigned int changemap_nob;
++   unsigned int bitmap_nob;
++   bitmap_t    *changemap0;
++   bitmap_t    *changemap1;
++   bitmap_t    *changemap2;
++   bitmap_t    *bitmap;
++} statemap_t;
++
++extern bitmap_t             statemap_getseg (statemap_t *map, unsigned int offset);
++extern void           statemap_setseg (statemap_t *map, unsigned int offset, bitmap_t seg);
++extern bitmap_t       statemap_getbits (statemap_t *map, unsigned int offset, int nbits);
++extern void           statemap_setbits (statemap_t *map, unsigned int offset, bitmap_t bits, int nbits);
++extern void           statemap_zero (statemap_t *map);
++extern void           statemap_setmap (statemap_t *dst, statemap_t *src);
++extern void           statemap_ormap (statemap_t *dst, statemap_t *src);
++extern int          statemap_findchange (statemap_t *map, bitmap_t *newseg, int clearchange);
++extern int            statemap_changed (statemap_t *map);
++extern void           statemap_reset (statemap_t *map);
++extern void           statemap_copy (statemap_t *dst, statemap_t *src);
++extern void           statemap_clearchanges (statemap_t *map);
++extern bitmap_t      *statemap_tobitmap (statemap_t *map);
++extern statemap_t    *statemap_create (int size);
++extern void           statemap_destroy (statemap_t *map);
++
++#endif /* __ELAN_STATEMAP_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/include/elan/stats.h
+===================================================================
+--- linux-2.4.21.orig/include/elan/stats.h     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan/stats.h  2005-06-01 23:12:54.715421472 -0400
+@@ -0,0 +1,85 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Limited.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: stats.h,v 1.5 2003/09/24 13:55:37 david Exp $"
++/*      $Source: /cvs/master/quadrics/elanmod/modsrc/stats.h,v $*/
++
++#ifndef __ELAN_STATS_H
++#define __ELAN_STATS_H
++
++
++/* non-kernel headings */
++#define ELAN_STATS_NAME_MAX_LEN ((uint)64)
++typedef unsigned int ELAN_STATS_IDX;
++
++typedef struct elan_stats_map
++{
++      char entry_name[ELAN_STATS_NAME_MAX_LEN];
++      int  index;
++} ELAN_STATS_MAP;
++
++#if defined(__KERNEL__)
++
++/* stats callbacks */
++#define ELAN_STATS_OPS_VERSION ((u_int)1)
++typedef struct elan_stats_ops
++{
++      u_int  ops_version;
++
++      int (*elan_stats_get_name)    (void * arg, uint index,   caddr_t  name);
++      int (*elan_stats_get_block)   (void * arg, uint entries, ulong   *values);
++      int (*elan_stats_clear_block) (void * arg);
++
++} ELAN_STATS_OPS;
++
++typedef struct elan_stats_struct
++{
++      struct list_head   node;
++
++      ELAN_STATS_IDX     statidx;
++      char               block_name[ELAN_STATS_NAME_MAX_LEN];
++      uint               num_entries;
++      ELAN_STATS_OPS *ops;
++      void              *arg;
++
++} ELAN_STATS_STRUCT;
++
++/* stats.c */
++extern int                   elan_stats_register   (ELAN_STATS_IDX    *statidx, 
++                                                     char              *block_name, 
++                                                     uint               num_entries,
++                                                     ELAN_STATS_OPS *ops,
++                                                     void              *arg);
++
++extern int                   elan_stats_deregister  (ELAN_STATS_IDX  statidx);
++extern ELAN_STATS_STRUCT *elan_stats_find        (ELAN_STATS_IDX  statidx);
++extern ELAN_STATS_STRUCT *elan_stats_find_by_name(caddr_t         block_name);
++extern ELAN_STATS_STRUCT *elan_stats_find_next   (ELAN_STATS_IDX  statidx);
++
++
++/* elan_stats.c */
++extern int elan_stats_get_next_index (ELAN_STATS_IDX statidx, ELAN_STATS_IDX *next_statidx);
++
++extern int elan_stats_find_index     (caddr_t  block_name, ELAN_STATS_IDX *statidx, uint *num_entries);
++
++extern int elan_stats_get_block_info (ELAN_STATS_IDX statidx, caddr_t block_name, uint *num_entries);
++
++extern int elan_stats_get_index_name (ELAN_STATS_IDX statidx, uint index, caddr_t name);
++
++extern int elan_stats_get_block      (ELAN_STATS_IDX statidx, uint entries, ulong   *values);
++
++extern int elan_stats_clear_block    (ELAN_STATS_IDX statidx);
++
++#endif /* __KERNEL__ */
++
++#endif /* __ELAN_STATS_H */
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.21/include/elan3/compat.h
+===================================================================
+--- linux-2.4.21.orig/include/elan3/compat.h   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan3/compat.h        2005-06-01 23:12:54.715421472 -0400
+@@ -0,0 +1,177 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: compat.h,v 1.4 2004/06/09 09:07:03 mike Exp $ $Name: QSNETMODULES-4-30_20050128 $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/compat.h,v $*/
++
++#ifndef __ELAN3_COMPAT_H
++#define __ELAN3_COMPAT_H
++
++/* compatibility header to allow Eagle branch QSNETLIBS 
++ * to compile against head kernel */
++
++#define ELAN_EAGLE_COMPAT
++
++/* vmseg.h */
++#define ELAN_FLAGSTATS                                ELAN3_FLAGSTATS
++
++/* uregs.h */
++#define ELAN_STATS_NAME                               ELAN3_STATS_NAME
++#define elan3_stats_names                     elan_stats_names
++
++/* spinlock.h */
++#define ELAN_SPINLOCK                         ELAN3_SPINLOCK
++#define ELAN_SPINLOCK_MAIN                    ELAN3_SPINLOCK_MAIN
++#define ELAN_SPINLOCK_ELAN                    ELAN3_SPINLOCK_ELAN
++#define ELAN_ME_SPINENTER                     ELAN3_ME_SPINENTER
++#define ELAN_ME_FORCEENTER                    ELAN3_ME_FORCEENTER
++#define ELAN_ME_SPINEXIT                      ELAN3_ME_SPINEXIT
++#define ELAN_SPINENTER                                ELAN3_SPINENTER
++#define ELAN_SPINEXIT                         ELAN3_SPINEXIT
++#define elan3_me_spinblock                    elan_me_spinblock
++#define elan3_spinenter                               elan_spinenter
++
++/* elanio.h */
++#define ELANIO_CONTROL_PATHNAME                       ELAN3IO_CONTROL_PATHNAME
++#define ELANIO_USER_PATHNAME                  ELAN3IO_USER_PATHNAME
++#define ELANIO_SDRAM_PATHNAME                 ELAN3IO_SDRAM_PATHNAME
++#define ELANIO_MAX_PATHNAMELEN                        ELAN3IO_MAX_PATHNAMELEN
++
++#define ELANIO_SET_BOUNDARY_SCAN              ELAN3IO_SET_BOUNDARY_SCAN
++#define ELANIO_CLEAR_BOUNDARY_SCAN            ELAN3IO_CLEAR_BOUNDARY_SCAN
++#define ELANIO_READ_LINKVAL                   ELAN3IO_READ_LINKVAL
++#define ELANIO_WRITE_LINKVAL                  ELAN3IO_WRITE_LINKVAL
++#define ELANIO_SET_DEBUG_STRUCT                       ELAN3IO_SET_DEBUG_STRUCT
++#define ELANIO_SET_DEBUG                      ELAN3IO_SET_DEBUG
++#define ELANIO_DEBUG_BUFFER_STRUCT            ELAN3IO_DEBUG_BUFFER_STRUCT
++#define ELANIO_DEBUG_BUFFER                   ELAN3IO_DEBUG_BUFFER
++#define ELANIO_NETERR_SERVER_STRUCT           ELAN3IO_NETERR_SERVER_STRUCT
++#define ELANIO_NETERR_SERVER                  ELAN3IO_NETERR_SERVER
++#define ELANIO_NETERR_FIXUP                   ELAN3IO_NETERR_FIXUP
++
++#define ELANIO_FREE                           ELAN3IO_FREE
++#define ELANIO_ATTACH                         ELAN3IO_ATTACH
++#define ELANIO_DETACH                         ELAN3IO_DETACH
++#define ELANIO_ADDVP_STRUCT                   ELAN3IO_ADDVP_STRUCT
++#define ELANIO_ADDVP                          ELAN3IO_ADDVP
++#define ELANIO_REMOVEVP                               ELAN3IO_REMOVEVP
++#define ELANIO_BCASTVP_STRUCT                 ELAN3IO_BCASTVP_STRUCT
++#define ELANIO_BCASTVP                                ELAN3IO_BCASTVP
++#define ELANIO_LOAD_ROUTE_STRUCT              ELAN3IO_LOAD_ROUTE_STRUCT
++#define ELANIO_LOAD_ROUTE                     ELAN3IO_LOAD_ROUTE
++#define ELANIO_PROCESS                                ELAN3IO_PROCESS
++#define ELANIO_SETPERM_STRUCT                 ELAN3IO_SETPERM_STRUCT
++#define ELANIO_SETPERM                                ELAN3IO_SETPERM
++#define ELANIO_CLEARPERM_STRUCT                       ELAN3IO_CLEARPERM_STRUCT
++#define ELANIO_CLEARPERM                      ELAN3IO_CLEARPERM
++#define ELANIO_CHANGEPERM_STRUCT              ELAN3IO_CHANGEPERM_STRUCT
++#define ELANIO_CHANGEPERM                     ELAN3IO_CHANGEPERM
++#define ELANIO_HELPER_THREAD                  ELAN3IO_HELPER_THREAD
++#define ELANIO_WAITCOMMAND                    ELAN3IO_WAITCOMMAND
++#define ELANIO_BLOCK_INPUTTER                 ELAN3IO_BLOCK_INPUTTER
++#define ELANIO_SET_FLAGS                      ELAN3IO_SET_FLAGS
++#define ELANIO_WAITEVENT                      ELAN3IO_WAITEVENT
++#define ELANIO_ALLOC_EVENTCOOKIE              ELAN3IO_ALLOC_EVENTCOOKIE
++#define ELANIO_FREE_EVENTCOOKIE                       ELAN3IO_FREE_EVENTCOOKIE
++#define ELANIO_ARM_EVENTCOOKIE                        ELAN3IO_ARM_EVENTCOOKIE
++#define ELANIO_WAIT_EVENTCOOKIE                       ELAN3IO_WAIT_EVENTCOOKIE
++#define ELANIO_SWAPSPACE                      ELAN3IO_SWAPSPACE
++#define ELANIO_EXCEPTION_SPACE                        ELAN3IO_EXCEPTION_SPACE
++#define ELANIO_GET_EXCEPTION                  ELAN3IO_GET_EXCEPTION
++#define ELANIO_UNLOAD_STRUCT                  ELAN3IO_UNLOAD_STRUCT
++#define ELANIO_UNLOAD                         ELAN3IO_UNLOAD
++#define ELANIO_GET_ROUTE_STRUCT                       ELAN3IO_GET_ROUTE_STRUCT
++#define ELANIO_GET_ROUTE                      ELAN3IO_GET_ROUTE
++#define ELANIO_RESET_ROUTE_STRUCT             ELAN3IO_RESET_ROUTE_STRUCT
++#define ELANIO_RESET_ROUTE                    ELAN3IO_RESET_ROUTE
++#define ELANIO_CHECK_ROUTE_STRUCT             ELAN3IO_CHECK_ROUTE_STRUCT
++#define ELANIO_CHECK_ROUTE                    ELAN3IO_CHECK_ROUTE
++#define ELANIO_VP2NODEID_STRUCT                       ELAN3IO_VP2NODEID_STRUCT
++#define ELANIO_VP2NODEID                      ELAN3IO_VP2NODEID
++#define ELANIO_SET_SIGNAL                     ELAN3IO_SET_SIGNAL
++#define ELANIO_PROCESS_2_LOCATION_STRUCT      ELAN3IO_PROCESS_2_LOCATION_STRUCT
++#define ELANIO_PROCESS_2_LOCATION             ELAN3IO_PROCESS_2_LOCATION
++#define ELANIO_GET_DEVINFO_STRUCT             ELAN3IO_GET_DEVINFO_STRUCT
++#define ELANIO_GET_DEVINFO                    ELAN3IO_GET_DEVINFO
++#define ELANIO_GET_POSITION_STRUCT            ELAN3IO_GET_POSITION_STRUCT
++#define ELANIO_GET_POSITION                   ELAN3IO_GET_POSITION
++#define ELANIO_STATS_STRUCT                   ELAN3IO_STATS_STRUCT
++#define ELANIO_STATS                          ELAN3IO_STATS
++#  define ELAN_SYS_STATS_DEVICE                       ELAN3_SYS_STATS_DEVICE
++#  define ELAN_SYS_STATS_ELAN3MMU             ELAN3_SYS_STATS_MMU
++
++#define ELANIO_OFF_FLAG_PAGE                  ELAN3IO_OFF_FLAG_PAGE
++#define ELANIO_OFF_UREG_PAGE                  ELAN3IO_OFF_UREG_PAGE
++#define ELANIO_OFF_COMMAND_PAGE                       ELAN3IO_OFF_COMMAND_PAGE
++
++
++/* elanvp.h */
++#define ELAN_ROUTE_SUCCESS                    ELAN3_ROUTE_SUCCESS
++#define ELAN_ROUTE_SYSCALL_FAILED             ELAN3_ROUTE_SYSCALL_FAILED
++#define ELAN_ROUTE_INVALID                    ELAN3_ROUTE_INVALID
++#define ELAN_ROUTE_TOO_LONG                   ELAN3_ROUTE_TOO_LONG
++#define ELAN_ROUTE_LOAD_FAILED                        ELAN3_ROUTE_LOAD_FAILED
++#define ELAN_ROUTE_PROC_RANGE                 ELAN3_ROUTE_PROC_RANGE
++#define ELAN_ROUTE_INVALID_LEVEL              ELAN3_ROUTE_INVALID_LEVEL
++#define ELAN_ROUTE_OCILATES                   ELAN3_ROUTE_OCILATES
++#define ELAN_ROUTE_WRONG_DEST                 ELAN3_ROUTE_WRONG_DEST
++#define ELAN_ROUTE_TURN_LEVEL                 ELAN3_ROUTE_TURN_LEVEL
++#define ELAN_ROUTE_NODEID_UNKNOWN             ELAN3_ROUTE_NODEID_UNKNOWN
++
++/* elandev.h */
++#define ELAN_STATS                            ELAN3_STATS
++#define ELAN_STATS_VERSION                    ELAN3_STATS_VERSION
++
++/* perm.h */
++#define ELAN_PERM_NOREMOTE                    ELAN3_PERM_NOREMOTE
++#define ELAN_PERM_LOCAL_READ                  ELAN3_PERM_LOCAL_READ
++#define ELAN_PERM_REMOTEALL                   ELAN3_PERM_REMOTEALL
++
++/* threadsyscall.h */
++#define ELAN_ABORT_TRAPNUM                    ELAN3_ABORT_TRAPNUM
++#define ELAN_ELANCALL_TRAPNUM                 ELAN3_ELANCALL_TRAPNUM
++#define ELAN_SYSCALL_TRAPNUM                  ELAN3_SYSCALL_TRAPNUM
++#define ELAN_SYS_close                                ELAN3_SYS_close
++#define ELAN_SYS_getpid                               ELAN3_SYS_getpid
++#define ELAN_SYS_ioctl                                ELAN3_SYS_ioctl
++#define ELAN_SYS_kill                         ELAN3_SYS_kill
++#define ELAN_SYS_lseek                                ELAN3_SYS_lseek
++#define ELAN_SYS_mmap                         ELAN3_SYS_mmap
++#define ELAN_SYS_munmap                               ELAN3_SYS_munmap
++#define ELAN_SYS_open                         ELAN3_SYS_open
++#define ELAN_SYS_poll                         ELAN3_SYS_poll
++#define ELAN_SYS_read                         ELAN3_SYS_read
++#define ELAN_SYS_write                                ELAN3_SYS_write
++#define ELAN_T_SYSCALL_CODE                   ELAN3_T_SYSCALL_CODE
++#define ELAN_T_SYSCALL_ERRNO                  ELAN3_T_SYSCALL_ERRNO
++
++/* elansyscall.h */
++#define ELAN_SYS_FLAG_DMA_BADVP                       ELAN3_SYS_FLAG_DMA_BADVP
++#define ELAN_SYS_FLAG_THREAD_BADVP            ELAN3_SYS_FLAG_THREAD_BADVP
++#define ELAN_SYS_FLAG_DMAFAIL                 ELAN3_SYS_FLAG_DMAFAIL
++#define ELAN_SYS_FLAG_NETERR                  ELAN3_SYS_FLAG_NETERR
++
++/* intrinsics.h */
++#define elan_copy64w                          elan3_copy64w
++#define elan_read64dw                         elan3_read64dw
++#define elan_write64dw                                elan3_write64dw
++
++#ifndef ELAN_POLL_EVENT
++#define ELAN_POLL_EVENT                               ELAN3_POLL_EVENT
++#endif
++#ifndef ELAN_WAIT_EVENT
++#define ELAN_WAIT_EVENT                               ELAN3_WAIT_EVENT
++#endif
++
++#endif /* __ELAN3_COMPAT_H */
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++
+Index: linux-2.4.21/include/elan3/dma.h
+===================================================================
+--- linux-2.4.21.orig/include/elan3/dma.h      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan3/dma.h   2005-06-01 23:12:54.716421320 -0400
+@@ -0,0 +1,213 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN3_DMA_H
++#define __ELAN3_DMA_H
++
++#ident "$Id: dma.h,v 1.38 2002/08/21 12:43:27 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/dma.h,v $ */
++
++#include <elan3/e3types.h>
++#include <elan3/events.h>
++
++/* Alignment for a DMA descriptor */
++#define E3_DMA_ALIGN          (32)
++
++/* The maximum size a DMA can be (i.e. < 2GB) */
++#define E3_MAX_DMA_SIZE               0x7fffffff
++
++/* This macro returns TRUE if a fixup for the ELAN_REVB_BUG_2 problem is required 
++ * i.e. if the DMA begins in the last 64-bytes of a page and its size causes it to enter the
++ * next page, hence causing the Elan to issue 2 (64-byte) block reads to different pages.
++ * See GNAT hw-elan3/3263
++ */
++#define E3_DMA_REVB_BUG_2(SIZE, ADDR, PAGESIZE)       \
++      ( (((int) (ADDR) & (PAGESIZE-64)) == (PAGESIZE-64)) && (-(((int) (ADDR) | ~(PAGESIZE-1))) < (SIZE)) )
++
++/* There is a point where a dma runs quicker from main memory than
++ * when running from sdram and having to copy all the data down
++ * first.
++ */
++#define E3_DMA_SDRAM_CUTOFF   128
++
++typedef union _e3_DmaType
++{
++    E3_uint32 type;
++    struct
++    {
++#if defined(__LITTLE_ENDIAN__)
++      E3_uint32 dataType:2;   /* Bits 0 to 1   */
++      E3_uint32 direction:3;  /* Bit  4 to 2   */
++      E3_uint32 opCode:4;     /* Bits 5 to 8   */
++      E3_uint32 failCount:6;  /* Bits 9 to 14  */
++      E3_uint32 isRemote:1;   /* Bit  15       */
++      E3_uint32 Context:13;   /* Bits 16 to 28 */
++      E3_uint32 :3;           /* Bits 29 to 31 */
++#else
++      E3_uint32 :3;           /* Bits 29 to 31 */
++      E3_uint32 Context:13;   /* Bits 16 to 28 */
++      E3_uint32 isRemote:1;   /* Bit  15       */
++      E3_uint32 failCount:6;  /* Bits 9 to 14  */
++      E3_uint32 opCode:4;     /* Bits 5 to 8   */
++      E3_uint32 direction:3;  /* Bit  4 to 2   */
++      E3_uint32 dataType:2;   /* Bits 0 to 1   */
++#endif
++    } s;
++} E3_DmaType;
++
++#define E3_DMA_CONTEXT_MASK   (ALL_CONTEXT_BITS << 16)
++
++#define E3_DMA_CONTEXT(type)  (((type) >> 16) & ALL_CONTEXT_BITS)
++#define E3_DMA_ISREMOTE(type) (((type) >> 15) & 1)
++#define E3_DMA_FAILCOUNT(type)        (((type) >> 9) & 0x3F)
++#define E3_DMA_OPCODE(type)   (((type) >> 5) & 0xF)
++#define E3_DMA_DIRECTION(type)        (((type) >> 2) & 0x7)
++#define EP_DMA_DATATYPE(type) (((type) >> 0) & 0x3)
++
++#define E3_DMA_TYPE(dataType, direction, opCode, failCount) \
++    (((dataType) & 0x3) | (((direction) & 7) << 2) | (((opCode) & 0xF) << 5) | (((failCount) & 0x3F) << 9))
++
++
++typedef union _e3_CookieVProc
++{
++    E3_uint32 cookie_vproc;
++    struct
++    {
++#if defined(__LITTLE_ENDIAN__)
++      E3_uint32 vproc:16;                     /* Bit  15 to 0  */
++      E3_uint32 cookie:16;                    /* Bits 31 to 16 */
++#else
++      E3_uint32 cookie:16;                    /* Bits 31 to 16 */
++      E3_uint32 vproc:16;                     /* Bit  15 to 0  */
++#endif
++    } s;
++} E3_CookieVProc;
++
++#define E3_DMA_COOKIE_PROC(Cookie, VProc)  (((VProc) & 0xffff) | (((Cookie) << 16)))
++
++#define DMA_COOKIE_MASK               (0xffff0000)
++#define DMA_PROCESS_MASK      (0x0000ffff)
++
++/* We use the bottom bit of the cookie to
++ * distinguish main/thread generated cookies
++ */
++#define DMA_COOKIE_THREAD     (0x01 << 16)
++
++/* We use the next bit of the cookie to
++ * distinguish locally/remotely generated cookies 
++ */
++#define DMA_COOKIE_REMOTE     (0x02 << 16)
++
++/* Assign and increment cookie (NB: we have reserved the bottom two bits)
++ */
++#define DMA_COOKIE(COOKIE, VPROC)      ((((COOKIE) += (0x4 << 16)) & DMA_COOKIE_MASK) | VPROC)
++#define DMA_REMOTE_COOKIE(COOKIE, VPROC) ((((COOKIE) += (0x4 << 16)) & DMA_COOKIE_MASK) | DMA_COOKIE_REMOTE | VPROC)
++
++#define DMA_COOKIE_REFRESH(COOKIEVP, COOKIE)                          \
++do {                                                                  \
++      COOKIEVP &= ~DMA_COOKIE_MASK;           /* Clear cookie */      \
++      COOKIEVP |= DMA_COOKIE(COOKIE,0);       /* Assign new cookie */ \
++} while (0)
++
++typedef struct e3_dma
++{
++    E3_DmaType                dma_u;
++    E3_uint32         dma_size;
++    E3_Addr           dma_source;
++    E3_Addr           dma_dest;
++    E3_Addr           dma_destEvent;
++    E3_CookieVProc    dma_destCookieProc;
++    E3_Addr           dma_srcEvent;
++    E3_CookieVProc    dma_srcCookieProc;
++} E3_DMA;
++
++
++/*
++ * Word-swapped version of DMA descriptor.
++ * This is used by the UltraSPARC code to format the descriptor
++ * in main memory before block-copying it down to Elan SDRAM.
++ * In the process it does a dword (64-bit) conversion and so swaps
++ * the word order on a double-word pair basis
++ */
++typedef struct e3_dma_swapped
++{
++    E3_uint32         dma_size;
++    E3_DmaType                dma_u;
++    E3_Addr           dma_dest;
++    E3_Addr           dma_source;
++    E3_CookieVProc    dma_destCookieProc;
++    E3_Addr           dma_destEvent;
++    E3_CookieVProc    dma_srcCookieProc;
++    E3_Addr           dma_srcEvent;
++} E3_DMA_SWAPPED;
++
++/* Define a Main memory structure for DMA desc based on Endianess of machine */
++#if defined(__LITTLE_ENDIAN__)
++#define E3_DMA_MAIN E3_DMA
++#else
++#define E3_DMA_MAIN E3_DMA_SWAPPED;
++#endif
++
++#define dma_type       dma_u.type
++#define dma_failCount    dma_u.s.failCount
++#define dma_isRemote     dma_u.s.isRemote
++#define dma_opCode       dma_u.s.opCode
++#define dma_direction    dma_u.s.direction
++#define dma_dataType     dma_u.s.dataType
++#define dma_queueContext dma_u.s.Context
++
++#define dma_destCookieVProc   dma_destCookieProc.cookie_vproc
++#define dma_destVProc       dma_destCookieProc.s.vproc
++#define dma_destCookie              dma_destCookieProc.s.cookie
++#define dma_srcCookieVProc    dma_srcCookieProc.cookie_vproc
++#define dma_srcVProc        dma_srcCookieProc.s.vproc
++#define dma_srcCookie       dma_srcCookieProc.s.cookie
++
++/*
++ * Values for dma_opCode
++ */
++#define DMA_NORMAL                    0
++#define DMA_QUEUED                    1
++#define DMA_NORMAL_BROADCAST          2
++#define DMA_QUEUED_BROADCAST          3
++#define DMA_NORMAL_UNSAFE             4
++#define DMA_QUEUED_UNSAFE             5
++#define DMA_NORMAL_BROADCAST_UNSAFE   6
++#define DMA_QUEUED_BROADCAST_UNSAFE   7
++
++/*
++ * Values for dma_direction
++ */
++#define DMA_WRITE             0
++#define DMA_READ_REQUEUE      1
++#define DMA_READ              3
++#define DMA_READ_BROADCAST    7
++
++/*
++ * Values for dma_dataType
++ */
++#define DMA_BYTE              0
++#define DMA_HALFWORD          1
++#define DMA_WORD              2
++#define DMA_DOUBLE            3
++
++/* OUT OF DATE ?
++  #define DMA_OPCODE_SHIFT    3
++  #define DMA_FAILCOUNT_SHIFT 9
++*/
++#define DMA_TYPE_ISREMOTE     (1 << 15)
++#define DMA_TYPE_READ         (3 << 2)
++#define DMA_TYPE_READ_REQUEUE (1 << 2)
++#define DMA_TYPE_DIRECTION_MASK       (3 << 2)
++
++#endif /* __ELAN3_DMA_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/include/elan3/e3types.h
+===================================================================
+--- linux-2.4.21.orig/include/elan3/e3types.h  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan3/e3types.h       2005-06-01 23:12:54.716421320 -0400
+@@ -0,0 +1,82 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN3_E3TYPES_H
++#define __ELAN3_E3TYPES_H
++
++#ident "$Id: e3types.h,v 1.18 2002/08/09 11:23:33 addy Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/e3types.h,v $ */
++
++#include <qsnet/config.h>
++/*
++ * "flip" values for correctly indexing into
++ * block data which was copied from the Elan
++ * using 64 bit accesses.
++ */
++#if defined(__LITTLE_ENDIAN__)
++#  define ByteEndianFlip  0
++#  define ShortEndianFlip 0
++#  define WordEndianFlip  0
++#else
++#  define ByteEndianFlip  7
++#  define ShortEndianFlip 3
++#  define WordEndianFlip  1
++#endif
++
++
++#ifndef _ASM
++
++typedef signed int       E3_int;
++typedef unsigned int             E3_uint;
++
++typedef signed char      E3_int8;
++typedef unsigned char            E3_uint8;
++
++typedef signed short     E3_int16;
++typedef unsigned short           E3_uint16;
++
++typedef signed int       E3_int32;
++typedef unsigned int             E3_uint32;
++
++#ifdef __ELAN3__
++typedef signed long long   E3_int64;
++typedef unsigned long long E3_uint64;
++#ifdef _MAIN_LP64
++/* NOTE: If the Main is 64-bit we declare the Elan thread's
++ * E3_uintptr to be 64-bits too
++ */
++typedef unsigned long long E3_uintptr;
++#else
++typedef unsigned long      E3_uintptr;
++#endif
++
++#else
++
++#ifdef _LP64
++typedef signed long        E3_int64;
++typedef unsigned long      E3_uint64;
++typedef unsigned long      E3_uintptr;
++#else /* _ILP32 */
++typedef signed long long   E3_int64;
++typedef unsigned long long E3_uint64;
++typedef unsigned long      E3_uintptr;
++#endif
++
++#endif /* __ELAN3__ */
++
++/* 32-bit Elan3 address */
++typedef E3_uint32        E3_Addr;
++
++#endif /* _ASM */
++
++#endif /* __ELAN3_E3TYPES_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/include/elan3/elan3mmu.h
+===================================================================
+--- linux-2.4.21.orig/include/elan3/elan3mmu.h 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan3/elan3mmu.h      2005-06-01 23:12:54.717421168 -0400
+@@ -0,0 +1,346 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN3_ELAN3MMU_H
++#define __ELAN3_ELAN3MMU_H
++
++#ident "$Id: elan3mmu.h,v 1.40.2.1 2004/12/14 10:19:48 mike Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/elan3mmu.h,v $*/
++
++
++#include <elan3/pte.h>
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++typedef struct elan3mmu_global_stats
++{
++    int               version;
++    int               pteload;
++    int               pteunload;
++    int               ptereload;
++
++    int               streamable_alloc;
++    int               streamable_free;
++    int               streamable_alloc_failed;
++
++    int               num_ptbl_level[4]; /* number of level N  ptbls */
++
++    int               create_ptbl_failed;                     /* count of ptbl creation failure */
++
++    int         lX_alloc_l3;                          /* count of l3 ptbls used as lX */
++    int         lX_freed_l3;                          /* count of lX ptbls freed as l3 */
++
++    int               l2_alloc_l3;                            /* count of l3 ptbls used as l2 */
++    int               l2_freed_l3;                            /* count of l2 ptbls freed as l3 */
++
++    int               stolen_ptbls;                           /* count of l3 ptbls stolen */
++} ELAN3MMU_GLOBAL_STATS;
++
++#define ELAN3MMU_STATS_VERSION                1
++
++#define ELAN3MMU_STAT(what)           (elan3mmu_global_stats.what++)
++#define ELAN3MMU_SET_STAT(what,count) (elan3mmu_global_stats.what = count)
++
++#ifdef __KERNEL__
++
++#define ELAN3_PT_SHIFT        (ELAN3_L2_SHIFT + 2)
++
++typedef struct elan3_ptbl
++{
++    struct elan3_ptbl  *ptbl_parent;                          /* Parent page table, or next on freelist */
++    struct elan3mmu    *ptbl_elan3mmu;                        /* elan3mmu we're allocated for */
++    E3_Addr             ptbl_base;                            /* Virtual address we're mapping */
++    u_char              ptbl_index;                           /* Index in ptbl group */
++    u_char              ptbl_valid;                           /* Number of valid entries */
++    u_char              ptbl_flags;                           /* Flags, defined below. */
++    u_char              ptbl_spare;
++} ELAN3_PTBL;
++
++#define ptbl_next     ptbl_parent                             /* Parent pointer is next pointer when on free list */
++
++#define PTBL_LEVEL_X            0x00
++#define PTBL_LEVEL_1          0x01
++#define PTBL_LEVEL_2          0x02
++#define PTBL_LEVEL_3          0x03
++#define PTBL_LEVEL_MASK               0x03
++#define PTBL_LOCKED           0x04                            /* Page table is locked,  protects all fields */
++#define PTBL_KEEP             0x08                            /* This ptbl is not to be stolen */
++#define PTBL_ALLOCED          0x10                            /* This ptbl has been allocated, and is not free */
++#define PTBL_GROUPED          0x20                            /* This ptbl is a member of a group of ptbls */
++#define PTBL_KERNEL           0x80                            /* This ptbl is allocated for the kernel */
++
++#define PTBL_LEVEL(flags)     ((flags) & PTBL_LEVEL_MASK)
++#define PTBL_IS_LOCKED(flags) (((flags) & (PTBL_LOCKED|PTBL_ALLOCED)) == (PTBL_LOCKED|PTBL_ALLOCED))
++
++#if ELAN3_PAGE_SHIFT == 13
++#  define PTBL_GROUP_SIZE     8192                            /* page table groups are 8k bytes */
++#  define PTBLS_PER_GROUP_L1  8                               /* Number of level 1 tables in a group */
++#  define PTBLS_PER_GROUP_L2  32                              /*   ... level 2 */
++#  define PTBLS_PER_GROUP_L3  32                              /*   ... level 3 */
++#  define PTBLS_PER_GROUP_LX  32                              /*   ... level X */
++#  define PTBLS_PER_GROUP_MAX 32                              /*  max of l1,l2,l3,lX */
++#else
++#  define PTBL_GROUP_SIZE     4096                            /* page table groups are 4k bytes */
++#  define PTBLS_PER_GROUP_L1  4                               /* Number of level 1 tables in a group */
++#  define PTBLS_PER_GROUP_L2  16                              /*   ... level 2 */
++#  define PTBLS_PER_GROUP_L3  8                               /*   ... level 3 */
++#  define PTBLS_PER_GROUP_LX  16                              /*   ... level X */
++#  define PTBLS_PER_GROUP_MAX 16                              /*  max of l1,l2,l3,lX */
++#endif
++
++#define HMES_PER_GROUP                (PTBLS_PER_GROUP_L3*ELAN3_L3_ENTRIES)
++
++#if ELAN3_PAGE_SHIFT == 13
++#  define PTBLS_PER_PTBL_L1   4                               /* 256 PTPs */
++#  define PTBLS_PER_PTBL_L2   1                               /* 64 PTPs */
++#  define PTBLS_PER_PTBL_L3   1                               /* 32 PTEs */
++#else
++#  define PTBLS_PER_PTBL_L1   4                               /* 256 PTPs */
++#  define PTBLS_PER_PTBL_L2   1                               /* 64 PTPs */
++#  define PTBLS_PER_PTBL_L3   2                               /* 64 PTEs */
++#endif
++
++#define ELAN3_LX_ENTRIES     (32) 
++#define PTBLS_PER_PTBL_LX   (1)       
++
++#define L1_VA_PER_PTBL        (ELAN3_L1_SIZE*(ELAN3_L1_ENTRIES/PTBLS_PER_PTBL_L1))    /* 4 ptbl for L1 */
++#define L2_VA_PER_PTBL        (ELAN3_L2_SIZE*(ELAN3_L2_ENTRIES/PTBLS_PER_PTBL_L2))    /* 1 ptbl for L2 */
++#define L3_VA_PER_PTBL        (ELAN3_L3_SIZE*(ELAN3_L3_ENTRIES/PTBLS_PER_PTBL_L3))    /* 1 ptbl for L3 */
++
++typedef struct elan3_ptbl_gr
++{
++    struct elan3_ptbl_gr *pg_next;                            /* Next in list. */
++    int                        pg_level;                              /* Level PG allocated for */
++    sdramaddr_t                pg_addr;                               /* sdram offset of ptes/ptps */    
++    ELAN3_PTBL                 pg_ptbls[PTBLS_PER_GROUP_MAX];         /* The actual page tables */
++} ELAN3_PTBL_GR;
++
++
++/*
++ * The elan3mmu structure is the mmu dependant hardware address translation
++ * structure linked to the address space structure to show the translatioms
++ * provided by the elan for an address sapce.
++ *
++ * We also have a doubly linked list of 'regions' which allow the 
++ * elan3mmu code to determine the access permissions for the elan 
++ * dependant on the virtual address that the translation is being
++ * loaded at.
++ */
++
++typedef struct elan3mmu_rgn
++{
++    struct elan3mmu_rgn *rgn_mnext;                           /* Doubly linked list of regions */
++    struct elan3mmu_rgn *rgn_mprev;                           /*   sorted on main address */ 
++    caddr_t            rgn_mbase;                             /* main address of base of region */
++
++    struct elan3mmu_rgn *rgn_enext;                           /* Doubly linked list of regions */
++    struct elan3mmu_rgn *rgn_eprev;                           /*   sorted on elan address */
++    E3_Addr            rgn_ebase;                             /* elan address of base of region */
++
++    u_int              rgn_len;                               /* length of region */
++    u_int              rgn_perm;                              /* elan access permission */
++} ELAN3MMU_RGN;
++
++typedef struct elan3mmu
++{
++    spinlock_t                    elan3mmu_lock;                      /* spinlock lock for regions */
++    ELAN3MMU_RGN           *elan3mmu_mrgns;                   /* Doubly linked list of memory regions */
++    ELAN3MMU_RGN         *elan3mmu_mtail;                     /* Last memory region on list */
++    ELAN3MMU_RGN         *elan3mmu_mrgnlast;                  /* Last region 'hit' */
++
++    ELAN3MMU_RGN           *elan3mmu_ergns;                   /* Doubly linked list of memory regions */
++    ELAN3MMU_RGN         *elan3mmu_etail;                     /* Last memory region on list */
++    ELAN3MMU_RGN         *elan3mmu_ergnlast;                  /* Last region 'hit' */
++
++    struct elan3_dev        *elan3mmu_dev;                    /* Elan device we're using. */
++    struct elan3_ctxt    *elan3mmu_ctxt;                      /* Elan ctxt we're associated with */
++
++    sdramaddr_t             elan3mmu_ctp;                     /* Context table entry for our context */
++    ELAN3_PTBL                   *elan3mmu_l1ptbl;                    /* Level 1 Page table (first of 4) */
++
++    spinlock_t                    elan3mmu_lXptbl_lock;               /* spinlock for level X table list */
++    ELAN3_PTBL              *elan3mmu_lXptbl;                    /* Level X Page table list         */
++
++#ifdef LINUX
++    struct mm_struct       *elan3mmu_coproc_mm;                       /* Linux mm we're mapping */
++#endif
++} ELAN3MMU;
++
++_NOTE(LOCK_ORDER(elan3mmu::elan3mmu_lock elan3_dev::IntrLock))
++
++_NOTE(MUTEX_PROTECTS_DATA(elan3mmu::elan3mmu_lock,
++                        elan3mmu::elan3mmu_mrgns elan3mmu::elan3mmu_mtail
++                        elan3mmu::elan3mmu_ergns elan3mmu::elan3mmu_etail))
++/* protected by dev->IntrLock for read by device driver */
++_NOTE(DATA_READABLE_WITHOUT_LOCK(elan3mmu::elan3mmu_mrgns elan3mmu::elan3mmu_mtail
++                               elan3mmu::elan3mmu_ergns elan3mmu::elan3mmu_etail))
++
++_NOTE(SCHEME_PROTECTS_DATA("only set to valid region", 
++                         elan3mmu::elan3mmu_ergnlast elan3mmu::elan3mmu_mrgnlast))
++
++_NOTE(MUTEX_PROTECTS_DATA(elan3_dev::IntrLock,
++                        elan3mmu::elan3mmu_l1ptbl 
++                        elan3mmu::elan3mmu_ctp 
++                        elan3mmu::elan3mmu_dev))
++
++_NOTE(DATA_READABLE_WITHOUT_LOCK(elan3mmu::elan3mmu_l1ptbl
++                               elan3mmu::elan3mmu_ctp 
++                               elan3mmu::elan3mmu_dev))
++
++/*
++ * Macros for accessing ptes/ptbls/ptbl_grs
++ */
++
++#define OFFSETOF(object,member)       /* calculate offset of structure member */ \
++      ((size_t) (&(((object *)0)->member)))
++#define PTBL_TO_GR(ptbl)      /* convert ptbl to ptbl group */ \
++      ((ELAN3_PTBL_GR *) ((caddr_t) ((ptbl) - (ptbl)->ptbl_index) - OFFSETOF(ELAN3_PTBL_GR,pg_ptbls[0])))
++#define PTBL_TO_PTADDR(ptbl)  /* convert ptbl to a ptp pointing at it */ \
++        (PTBL_TO_GR(ptbl)->pg_addr + ((ptbl)->ptbl_index<<ELAN3_PT_SHIFT))
++#define PTE_TO_HME(ptbl,pte)  /* convert pte to corresponding hme */ \
++        (PTBL_TO_GR(ptbl)->pg_hmes + ((pte) - (ELAN3_PTE *) PTBL_TO_GR(ptbl)->pg_vaddr))
++#define HME_TO_PTE(ptebl,hme) /* convert hme to corresponding pte */ \
++        ((ELAN3_PTE *) PTBL_TO_GR(ptbl)->pg_vaddr + ((hme) - (PTBL_TO_GR(ptbl)->pg_hmes)))
++
++
++/* Flags for lock_ptbl */
++#define LK_PTBL_NOWAIT                0x1
++#define LK_PTBL_FAILOK                0x2
++
++/* Return values for lock_ptbl */
++#define LK_PTBL_OK            0x0
++#define LK_PTBL_MISMATCH      0x1
++#define LK_PTBL_FAILED                0x2
++
++/* Flags for elan3mmu_ptesync */
++#define       NO_MLIST_LOCK   0
++#define       MLIST_LOCKED    1
++
++/* Flags for elan3mmu_pteload */
++#define PTE_LOAD              0x00
++#define PTE_LOAD_LOCK         0x01                            /* translation should be locked */
++#define PTE_LOAD_NOSYNC               0x02                            /* ref/mod bits should not be sync'ed to page */
++#define PTE_NO_SLEEP            0x04                            /* true if we cant sleep */
++#define PTE_NO_STEAL          0x08                            /* true if we don't want to steal ptbls */
++
++#define PTE_LOAD_ENDIAN_MASK  0x10                            /* mask for endian-ness */
++#define PTE_LOAD_LITTLE_ENDIAN        0x00                            /* translation is to little-endian memory */
++#define PTE_LOAD_BIG_ENDIAN   0x10                            /* translation is to big-endian memory */
++
++
++/* Flags for elan3mmu_unload */
++#define PTE_UNLOAD            0x00
++#define PTE_UNLOAD_UNLOCK     0x01
++#define PTE_UNLOAD_NOFLUSH    0x02
++#define PTE_UNLOAD_NOSYNC     0x04
++
++extern int        elan3mmu_debug;
++#ifdef DEBUG_PRINTF
++#  define HAT_PRINTF0(n,msg)           ((elan3mmu_debug & n) ? (void) elan3_debugf (NULL, DBG_HAT, msg)             : (void) 0)
++#  define HAT_PRINTF1(n,msg,a)                 ((elan3mmu_debug & n) ? (void) elan3_debugf (NULL, DBG_HAT, msg,a)           : (void) 0)
++#  define HAT_PRINTF2(n,msg,a,b)       ((elan3mmu_debug & n) ? (void) elan3_debugf (NULL, DBG_HAT, msg,a,b)         : (void) 0)
++#  define HAT_PRINTF3(n,msg,a,b,c)     ((elan3mmu_debug & n) ? (void) elan3_debugf (NULL, DBG_HAT, msg,a,b,c)       : (void) 0)
++#  define HAT_PRINTF4(n,msg,a,b,c,d)   ((elan3mmu_debug & n) ? (void) elan3_debugf (NULL, DBG_HAT, msg,a,b,c,d)     : (void) 0)
++#  define HAT_PRINTF5(n,msg,a,b,c,d,e)         ((elan3mmu_debug & n) ? (void) elan3_debugf (NULL, DBG_HAT, msg,a,b,c,d,e)   : (void) 0)
++#  define HAT_PRINTF6(n,msg,a,b,c,d,e,f) ((elan3mmu_debug & n) ? (void) elan3_debugf (NULL, DBG_HAT, msg,a,b,c,d,e,f) : (void) 0)
++#  ifdef LINUX
++#    define HAT_PRINTF(n,args...)        ((elan3mmu_debug & n) ? (void) elan3_debugf(NULL, DBG_HAT, ##args) : (void) 0)
++#  endif
++#else
++#  define HAT_PRINTF0(n,msg)
++#  define HAT_PRINTF1(n,msg,a)
++#  define HAT_PRINTF2(n,msg,a,b)
++#  define HAT_PRINTF3(n,msg,a,b,c)
++#  define HAT_PRINTF4(n,msg,a,b,c,d)
++#  define HAT_PRINTF5(n,msg,a,b,c,d,e)
++#  define HAT_PRINTF6(n,msg,a,b,c,d,e,f)
++#  ifdef LINUX
++#    define HAT_PRINTF(n,args...)
++#  endif
++#endif
++
++/* elan3mmu_generic.c */
++extern ELAN3MMU_GLOBAL_STATS elan3mmu_global_stats;
++
++extern void        elan3mmu_init (void);
++extern void        elan3mmu_fini (void);
++
++extern ELAN3MMU          *elan3mmu_alloc (struct elan3_ctxt *ctxt);
++extern void       elan3mmu_free (ELAN3MMU *elan3mmu);
++
++extern void          elan3mmu_set_context_filter (ELAN3_DEV *dev, int ctx, int disabled, E3_uint32 Pend, E3_uint32 *Maskp);
++extern int         elan3mmu_attach (ELAN3_DEV *dev, int ctx, ELAN3MMU *elan3mmu, sdramaddr_t routeTable, E3_uint32 routeMask);
++extern void        elan3mmu_detach (ELAN3_DEV *dev, int ctx);
++
++extern ELAN3MMU_RGN *elan3mmu_findrgn_elan (ELAN3MMU *elan3mmu, E3_Addr addr, int tail);
++extern int           elan3mmu_addrgn_elan (ELAN3MMU *elan3mmu, ELAN3MMU_RGN *nrgn);
++extern ELAN3MMU_RGN *elan3mmu_removergn_elan (ELAN3MMU *elan3mmu, E3_Addr addr);
++extern ELAN3MMU_RGN *elan3mmu_rgnat_elan (ELAN3MMU *elan3mmu, E3_Addr addr);
++extern ELAN3MMU_RGN *elan3mmu_findrgn_main (ELAN3MMU *elan3mmu, caddr_t addr, int tail);
++extern int           elan3mmu_addrgn_main (ELAN3MMU *elan3mmu, ELAN3MMU_RGN *nrgn);
++extern ELAN3MMU_RGN *elan3mmu_removergn_main (ELAN3MMU *elan3mmu, caddr_t addr);
++extern ELAN3MMU_RGN *elan3mmu_rgnat_main (ELAN3MMU *elan3mmu, caddr_t addr);
++
++extern int         elan3mmu_setperm (ELAN3MMU *elan3mmu, caddr_t maddr, E3_Addr eaddr, u_int len, u_int perm);
++extern void        elan3mmu_clrperm (ELAN3MMU *elan3mmu, E3_Addr addr, u_int len);
++extern int         elan3mmu_checkperm (ELAN3MMU *elan3mmu, E3_Addr addr, u_int len, u_int access);
++extern caddr_t             elan3mmu_mainaddr (ELAN3MMU *elan3mmu, E3_Addr addr);
++extern E3_Addr             elan3mmu_elanaddr (ELAN3MMU *elan3mmu, caddr_t addr);
++
++extern void          elan3mmu_expand (ELAN3MMU *elan3mmu, E3_Addr addr, int len, int level, int attr);
++extern void          elan3mmu_reserve (ELAN3MMU *elan3mmu, E3_Addr addr, u_int npages, sdramaddr_t *);
++extern void          elan3mmu_release (ELAN3MMU *elan3mmu, E3_Addr addr, u_int npages, sdramaddr_t *);
++
++extern void          elan3mmu_pteload (ELAN3MMU *elan3mmu, int level, E3_Addr addr, physaddr_t paddr, int perm, int attr);
++extern void        elan3mmu_unload (ELAN3MMU *elan3mmu, E3_Addr addr, u_int len, int flags);
++extern void        elan3mmu_sync (ELAN3MMU *elan3mmu, E3_Addr addr, u_int len, u_int clearflag);
++extern void        elan3mmu_pteunload (ELAN3_PTBL *ptbl, sdramaddr_t pte, int flags, int got_mlist_lock);
++extern void        elan3mmu_ptesync (ELAN3_PTBL *ptbl, sdramaddr_t pte, int flags, int got_mlist_lock);
++extern sdramaddr_t   elan3mmu_ptp2pte (ELAN3MMU *elan3mmu, sdramaddr_t ptp, int level);
++extern sdramaddr_t   elan3mmu_ptefind (ELAN3MMU *elan3mmu, E3_Addr, int *level, ELAN3_PTBL **pptbl, spinlock_t **plock, unsigned long *flags);
++extern sdramaddr_t   elan3mmu_ptealloc (ELAN3MMU *elan3mmu, E3_Addr, int level, ELAN3_PTBL **pptbl, spinlock_t **plock, int attr, unsigned long *flags);
++extern void        elan3mmu_l1inval (ELAN3MMU *elan3mmu, ELAN3_PTBL *l1ptbl, int flags);
++extern int           elan3mmu_l2inval (ELAN3MMU *elan3mmu, ELAN3_PTBL *l2ptbl, int flags, E3_Addr addr, spinlock_t **pl2lock, unsigned long *lock_flags);
++extern int           elan3mmu_l3inval (ELAN3MMU *elan3mmu, ELAN3_PTBL *l3ptbl, int flags, E3_Addr addr, spinlock_t **pl3lock, unsigned long *lock_flags);
++
++extern void          elan3mmu_free_l1ptbl (ELAN3_DEV *dev, ELAN3_PTBL *ptbl, spinlock_t *lock, unsigned long flags);
++extern void          elan3mmu_free_l2ptbl (ELAN3_DEV *dev, ELAN3_PTBL *ptbl, spinlock_t *lock, unsigned long flags);
++extern void          elan3mmu_free_l3ptbl (ELAN3_DEV *dev, ELAN3_PTBL *ptbl, spinlock_t *lock, unsigned long flags);
++
++extern int         elan3mmu_lock_this_ptbl (ELAN3_PTBL *ptbl, int flag, spinlock_t **plock, unsigned long *flags);
++extern int           elan3mmu_lock_ptbl (ELAN3_PTBL *ptbl, u_int flag, ELAN3MMU *elan3mmu, E3_Addr va, int level, spinlock_t **plock, unsigned long *flags);
++extern void        elan3mmu_unlock_ptbl (ELAN3_PTBL *ptbl, spinlock_t *lock, unsigned long flags);
++
++/* elan3mmu_osdep.c */
++extern void        elan3mmu_init_osdep (void);
++extern void        elan3mmu_fini_osdep (void);
++extern void        elan3mmu_alloc_osdep (ELAN3MMU *elan3mmu);
++extern void        elan3mmu_free_osdep (ELAN3MMU *elan3mmu);
++extern ELAN3_PTE     elan3mmu_phys_to_pte (ELAN3_DEV *dev, physaddr_t paddr, int perm);
++extern ELAN3_PTE     elan3mmu_kernel_invalid_pte (ELAN3MMU *elan3mmu);
++
++#if defined (DIGITAL_UNIX)
++#  include <elan3/elan3mmu_dunix.h>
++#elif defined (LINUX)
++#  include <elan3/elan3mmu_linux.h>
++#endif
++
++#endif /* __KERNEL__ */
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* __ELAN3_ELAN3MMU_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/include/elan3/elan3mmu_linux.h
+===================================================================
+--- linux-2.4.21.orig/include/elan3/elan3mmu_linux.h   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan3/elan3mmu_linux.h        2005-06-01 23:12:54.717421168 -0400
+@@ -0,0 +1,39 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN3_MMU_LINUX_H
++#define __ELAN3_MMU_LINUX_H
++
++#ident "$Id: elan3mmu_linux.h,v 1.12 2003/09/24 13:57:24 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/elan3mmu_linux.h,v $*/
++
++/* XXX copy of elan3mmu_dunix.h */
++
++#define ALLOC_ELAN3MMU(ptr,cansleep)          KMEM_ALLOC(ptr, ELAN3MMU *, sizeof (ELAN3MMU), cansleep)
++#define ALLOC_PTBL_GR(ptr,cansleep)           KMEM_ALLOC(ptr, ELAN3_PTBL_GR *, sizeof (ELAN3_PTBL_GR), cansleep)
++#define ALLOC_ELAN3MMU_RGN(ptr,cansleep)      KMEM_ALLOC(ptr, ELAN3MMU_RGN *, sizeof (ELAN3MMU_RGN), cansleep)
++#define ALLOC_HMENTS(ptr,cansleep)            KMEM_ALLOC((ptr,ELAN3_HMENT *, sizeof (ELAN3_HMENT), cansleep)
++
++#define FREE_ELAN3MMU(ptr)                    KMEM_FREE(ptr,sizeof (ELAN3MMU))
++#define FREE_PTBL_GR(ptr)                     KMEM_FREE(ptr,sizeof (ELAN3_PTBL_GR))
++#define FREE_ELAN3MMU_RGN(ptr)                        KMEM_FREE(ptr,sizeof (ELAN3MMU_RGN))
++#define FREE_HMENTS(ptr)                      KMEM_FREE(ptr,sizeof (ELAN3_HMENT))
++
++extern void        elan3mmu_init_osdep(void);
++extern void        elan3mmu_fini_osdep(void);
++
++extern void          elan3mmu_pte_range_unload (ELAN3MMU *elan3mmu, struct mm_struct *mm, caddr_t addr, unsigned long len);
++extern void          elan3mmu_pte_range_update (ELAN3MMU *elan3mmu, struct mm_struct *mm, caddr_t addr, unsigned long len);
++extern void          elan3mmu_pte_ctxt_unload(ELAN3MMU *elan3mmu);
++
++#endif
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/include/elan3/elan3ops.h
+===================================================================
+--- linux-2.4.21.orig/include/elan3/elan3ops.h 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan3/elan3ops.h      2005-06-01 23:12:54.718421016 -0400
+@@ -0,0 +1,42 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++/* $Id: elan3ops.h,v 1.3 2003/09/24 13:57:24 david Exp $ */
++/* $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/elan3ops.h,v $ */
++
++#ifndef _ELAN3_OPS_H
++#define _ELAN3_OPS_H
++
++int get_position          (void *arg, ELAN_POSITION *position);
++int set_position          (void *arg, unsigned short nodeId, unsigned short numNodes);
++
++int elan3mod_create_cap   (void *arg, ELAN_CAP_OWNER owner, ELAN_CAPABILITY *cap);
++int elan3mod_destroy_cap  (void *arg, ELAN_CAP_OWNER owner, ELAN_CAPABILITY *cap);
++
++int elan3mod_create_vp    (void *arg, ELAN_CAP_OWNER owner, ELAN_CAPABILITY *cap, ELAN_CAPABILITY *map);
++int elan3mod_destroy_vp   (void *arg, ELAN_CAP_OWNER owner, ELAN_CAPABILITY *cap, ELAN_CAPABILITY *map);
++
++int elan3mod_attach_cap   (void *arg_ctxt, ELAN_CAPABILITY *cap);
++int elan3mod_detach_cap   (void *arg_ctxt);
++
++extern ELAN_DEV_OPS elan3_dev_ops;
++
++int stats_get_index_name  (void *arg, uint index, caddr_t name);
++int stats_get_block       (void *arg, uint entries, ulong *value);
++int stats_clear_block     (void *arg);
++
++int elan3_register_dev_stats   (ELAN3_DEV * dev);
++void elan3_deregister_dev_stats (ELAN3_DEV * dev);
++
++
++#endif /* __ELAN3_OPS_H */
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.21/include/elan3/elanctxt.h
+===================================================================
+--- linux-2.4.21.orig/include/elan3/elanctxt.h 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan3/elanctxt.h      2005-06-01 23:12:54.719420864 -0400
+@@ -0,0 +1,856 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef _ELAN3_ELANCTXT_H
++#define _ELAN3_ELANCTXT_H
++
++#ident "$Id: elanctxt.h,v 1.81 2003/09/24 13:57:24 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/elanctxt.h,v $*/
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <elan3/elanregs.h>
++#include <elan3/vmseg.h>
++
++#define BumpUserStat(ctxt, stat)      ((ctxt)->FlagPage->stat++)
++
++#if defined(__LITTLE_ENDIAN__)
++
++typedef union _CProcTrapBuf
++{
++    E3_uint64 Align64;
++    struct
++    {
++      E3_uint32 Areg;
++      E3_uint32 Breg;
++    } r;
++    struct
++    {
++      E3_uint32 Addr;
++      E3_uint32 ContextType;
++    } s;
++} CProcTrapBuf_BE;
++
++typedef E3_EventInt        E3_EventInt_BE;
++typedef E3_IprocTrapHeader E3_IprocTrapHeader_BE;
++typedef E3_IprocTrapData   E3_IprocTrapData_BE;
++typedef E3_FaultSave     E3_FaultSave_BE;
++
++typedef union
++{
++    E3_uint64 Align64;
++    E3_DMA      s;
++} E3_DMA_BE;
++
++typedef E3_ThreadQueue     E3_ThreadQueue_BE;
++
++#else
++
++/* "Big-Endian" data structures copied by 64 bit loads, these are 32 bit word flipped */
++/* from the corresponding data structure. */
++
++typedef union _CProcTrapBuf
++{
++    E3_uint64 Align64;
++    struct
++    {
++      E3_uint32 Breg;
++      E3_uint32 Areg;
++    } r;
++    struct
++    {
++      E3_uint32 ContextType;
++      E3_uint32 Addr;
++    } s;
++} CProcTrapBuf_BE;
++
++typedef union _E3_EventInt_BE
++{
++    E3_uint64   Align64;
++    struct {
++      E3_uint32 EventContext; /* Bits 16 to 28 */
++      E3_uint32 IntCookie;
++    } s;
++} E3_EventInt_BE;
++
++typedef union _E3_IprocTrapHeader_BE
++{
++   E3_uint64           Align64;
++
++   struct
++   {
++      E3_uint32                TrAddr;
++      E3_TrTypeCntx    TrTypeCntx;
++      union
++      {
++       E3_IProcStatus_Reg u_IProcStatus;
++       E3_uint32          u_TrData1;
++      } ipsotd;
++      E3_uint32                TrData0;
++   } s;
++} E3_IprocTrapHeader_BE;
++
++typedef E3_IprocTrapData E3_IprocTrapData_BE;
++
++typedef union _E3_FaultSave_be
++{
++    E3_uint64                 Align64;
++    struct {
++      volatile E3_uint32      FaultContext;
++      E3_FaultStatusReg       FSR;
++      volatile E3_uint32      EventAddress;
++      volatile E3_uint32      FaultAddress;
++    } s;
++} E3_FaultSave_BE;
++
++typedef union _e3_dma_be
++{
++    E3_uint64         Align64;
++    struct {
++      E3_uint32       dma_size;
++      E3_DmaType      dma_u;
++      E3_Addr         dma_dest;
++      E3_Addr         dma_source;
++      E3_CookieVProc  dma_destCookieProc;
++      E3_Addr         dma_destEvent;
++      E3_CookieVProc  dma_srcCookieProc;
++      E3_Addr         dma_srcEvent;
++    } s;
++} E3_DMA_BE;
++
++typedef union _E3_ThreadQueue_BE
++{
++   E3_uint64  Align64;
++   struct
++   {
++       /* copied by 64 bit copy from elan to main */
++       E3_uint32 :3;          /* Bits 29 to 31 */
++       E3_uint32 Context:13;  /* Bits 16 to 28 */
++       E3_uint32 :16;         /* Bits 0  to 15 */
++       E3_Addr         Thread;        /* Bits 32 to 63 */
++   } s;
++} E3_ThreadQueue_BE;
++
++#endif /* defined(LITTLE_ENDIAN) || defined(__LITTLE_ENDIAN__) */
++
++typedef struct neterr_msg
++{
++    E3_uint32         Rail;                                   /* Rail error received on */
++    ELAN_CAPABILITY   SrcCapability;                          /* Capability of source of packet */
++    ELAN_CAPABILITY   DstCapability;                          /* Capability of dest of packet */
++
++    E3_uint32         DstProcess;                             /* Virtual Process of dest of packet */
++    E3_Addr           CookieAddr;                             /* Cookie Address (or NULL for DMA) */
++    E3_uint32         CookieVProc;                            /* Cookie and VP (identifies DMA) */
++    E3_uint32         NextCookie;                             /* Next Cookie value (for thread) */
++    E3_uint32         WaitForEop;                             /* Wait for EOP transaction */
++} NETERR_MSG;
++
++#ifdef __KERNEL__
++
++/*
++ * Associated with each input channel can be a network error
++ * resolver structure, which can be queued on the network 
++ * error resolver threads to perform RPCs to the other kernels
++ * when a network error occurs with an identify transaction
++ * included
++ */
++typedef struct neterr_resolver
++{
++    struct neterr_resolver *Next;
++
++    spinlock_t                    Lock;
++
++    struct elan3_ctxt       *Ctxt;
++    ELAN_LOCATION         Location;
++
++    int                           Completed;
++    int                           Status;
++    long                  Timestamp;
++
++    NETERR_MSG                    Message;
++} NETERR_RESOLVER;
++
++
++typedef struct neterr_fixup
++{
++    struct neterr_fixup          *Next;
++
++    kcondvar_t                    Wait;
++    int                           Completed;
++    int                           Status;
++
++    NETERR_MSG                    Message;
++} NETERR_FIXUP;
++
++#endif /* __KERNEL__ */
++
++/* Each of the following structures must be padded to a whole */
++/* number of 64 bit words since the kernel uses 64 bit load/stores */
++/* to transfer the elan register state. */
++typedef struct command_trap
++{
++    E3_Status_Reg     Status;                                 /* 4  bytes */
++    E3_uint32         Pad;                                    /* 4  bytes */
++    E3_FaultSave_BE   FaultSave;                              /* 16 bytes */
++    CProcTrapBuf_BE           TrapBuf;                                /* 8  bytes */
++} COMMAND_TRAP;
++
++typedef struct thread_trap
++{
++    E3_uint32         Registers[32];                          /* 128 bytes */
++#define REG_GLOBALS   0
++#define REG_OUTS      8
++#define REG_LOCALS    16
++#define REG_INS               24
++
++    E3_FaultSave_BE   FaultSave;                              /* 16 bytes */
++    E3_FaultSave_BE     DataFaultSave;                                /* 16 bytes */
++    E3_FaultSave_BE     InstFaultSave;                                /* 16 bytes */
++    E3_FaultSave_BE     OpenFaultSave;                                /* 16 bytes */
++    
++    E3_Status_Reg     Status;                                 /* 4 bytes */
++
++    E3_Addr           pc;                                     /* 4 bytes */
++    E3_Addr           npc;                                    /* 4 bytes */
++    E3_Addr           StartPC;                                /* 4 bytes */
++    E3_Addr           sp;                                     /* 4 bytes */
++    E3_uint32         mi;                                     /* 4 bytes */
++    E3_TrapBits               TrapBits;                               /* 4 bytes */
++    E3_DirtyBits      DirtyBits;                              /* 4 bytes */
++} THREAD_TRAP;
++
++typedef struct dma_trap
++{
++    E3_DMA_BE         Desc;                                   /* 32 bytes */
++    E3_FaultSave_BE   FaultSave;                              /* 16 bytes */
++    E3_FaultSave_BE   Data0;                                  /* 16 bytes */
++    E3_FaultSave_BE   Data1;                                  /* 16 bytes */
++    E3_FaultSave_BE   Data2;                                  /* 16 bytes */
++    E3_FaultSave_BE   Data3;                                  /* 16 bytes */
++    E3_Status_Reg     Status;                                 /* 4 bytes */
++    E3_DmaInfo                PacketInfo;                             /* 4 bytes */
++} DMA_TRAP;
++
++typedef struct input_trap
++{
++    E3_uint32            State;                               /* 4 bytes */
++    E3_Status_Reg        Status;                              /* 4 bytes */
++    E3_FaultSave_BE      FaultSave;                           /* 16 bytes */
++    
++    u_int                NumTransactions;                     /* 4 bytes */
++    u_int                Overflow;                            /* 4 bytes */
++    u_int                AckSent;                             /* 4 bytes */
++    u_int                BadTransaction;                      /* 4 bytes */
++
++    E3_IprocTrapHeader_BE *TrappedTransaction;                        /* 4 bytes */
++    E3_IprocTrapData_BE   *TrappedDataBuffer;                 /* 4 bytes */
++    E3_IprocTrapHeader_BE *WaitForEopTransaction;             /* 4 bytes */
++    E3_IprocTrapData_BE   *WaitForEopDataBuffer;              /* 4 bytes */
++    E3_IprocTrapHeader_BE *DmaIdentifyTransaction;            /* 4 bytes */
++    E3_IprocTrapHeader_BE *ThreadIdentifyTransaction;         /* 4 bytes */
++    E3_Addr              LockQueuePointer;                    /* 4 bytes */
++    E3_Addr              UnlockQueuePointer;                  /* 4 bytes */
++
++    E3_IprocTrapHeader_BE  Transactions[MAX_TRAPPED_TRANS];   /* n * 8 bytes */
++    E3_IprocTrapData_BE          DataBuffers[MAX_TRAPPED_TRANS];      /* n * 64 bytes */
++} INPUT_TRAP;
++
++typedef struct input_fault_save
++{
++    struct input_fault_save *Next;
++    E3_Addr                Addr;
++    E3_uint32              Count;
++} INPUT_FAULT_SAVE;
++
++#define NUM_INPUT_FAULT_SAVE  32
++#define MIN_INPUT_FAULT_PAGES 8
++#define MAX_INPUT_FAULT_PAGES 128
++
++typedef E3_uint32 EVENT_COOKIE;
++
++#ifdef __KERNEL__
++
++typedef struct event_cookie_entry
++{
++    struct event_cookie_entry *ent_next;
++    struct event_cookie_entry *ent_prev;
++
++    spinlock_t                       ent_lock;
++    unsigned                 ent_ref;
++
++    EVENT_COOKIE             ent_cookie;
++    EVENT_COOKIE             ent_fired;
++    kcondvar_t                       ent_wait;
++} EVENT_COOKIE_ENTRY;
++
++typedef struct event_cookie_table
++{
++    struct event_cookie_table *tbl_next;
++    struct event_cookie_table *tbl_prev;
++
++    unsigned long              tbl_task;
++    unsigned long              tbl_handle;
++
++    spinlock_t                       tbl_lock;
++    unsigned                 tbl_ref;
++    EVENT_COOKIE_ENTRY        *tbl_entries;
++} EVENT_COOKIE_TABLE;
++
++#define NBYTES_PER_SMALL_ROUTE        8
++#define NBYTES_PER_LARGE_ROUTE        16
++
++#define ROUTE_BLOCK_SIZE      ELAN3_PAGE_SIZE
++#define NROUTES_PER_BLOCK     (ROUTE_BLOCK_SIZE/NBYTES_PER_LARGE_ROUTE)
++
++typedef struct elan3_routes
++{
++    struct elan3_routes               *Next;                                  /* Can be chained together */
++
++    sdramaddr_t                        Routes;                                /* sdram offset of route entries */
++    bitmap_t                   Bitmap[BT_BITOUL(NROUTES_PER_BLOCK)];  /* Bitmap of which entries are used */
++} ELAN3_ROUTES; 
++
++
++typedef struct elan3_route_table
++{
++    spinlock_t                 Lock;                          /* Route lock */
++    sdramaddr_t                Table;                         /* Kernel address for route table */
++    u_int              Size;                          /* # entries in route table */
++
++    ELAN3_ROUTES      *LargeRoutes;                   /* Large routes */
++} ELAN3_ROUTE_TABLE;
++
++typedef struct elan3_vpseg
++{
++    struct elan3_vpseg                *Next;
++    int                                Process;                       /* Virtual process */
++    int                                Entries;                       /*  and # processes */
++    int                                Type;                          /* Type of cookie */
++
++    union
++    {
++      
++      ELAN_CAPABILITY Capability;                     /* Capability of remote segment */
++#  define SegCapability               SegUnion.Capability
++      struct {
++          u_short             LowProc;                        /* Base process number */
++          u_short             HighProc;                       /*   and high process number */
++#  define SegLowProc          SegUnion.BROADCAST.LowProc
++#  define SegHighProc         SegUnion.BROADCAST.HighProc
++      } BROADCAST;
++    } SegUnion;
++} ELAN3_VPSEG;
++
++#define ELAN3_VPSEG_UNINT     0                               /* Unitialised */
++#define ELAN3_VPSEG_P2P               1                               /* Point to Point */
++#define ELAN3_VPSEG_BROADCAST 2                               /* Broadcast */
++
++#define NUM_LISTS     7                                       /* Number of "swap" lists */
++
++typedef struct elan3_ctxt
++{
++    struct elan3_ctxt    *Next;                                       /* can be queued on a task */
++    struct elan3_ctxt    *Prev;
++
++    CtxtHandle                 Handle;                                /* user handle */
++    int                        RefCnt;                                /* reference count */
++
++    ELAN3MMU          *Elan3mmu;                              /* elan3mmu allocated for Elan translations */
++
++    struct elan3_ops     *Operations;                         /* User supplied helper functions */
++    void              *Private;                               /* Users private pointer */
++
++    int                        Status;                                /* Status (guarded by dev_mutex) */
++    int                        OthersState;                           /* State of halt queueing for dma/thread */
++    int                        LwpCount;                              /* Number of lwp's running */
++
++    ELAN3_DEV         *Device;                                /* Elan device */
++
++    ELAN_CAPABILITY    Capability;                            /* Capability I've attached as */
++    ELAN_POSITION      Position;                              /* Position when I was created */
++    
++    ELAN3_VPSEG               *VpSegs;                                /* List of virtual process segments */
++    ELAN3_ROUTE_TABLE    *RouteTable;
++
++    krwlock_t          VpLock;                                /* Reader/writer lock for vp list */
++    kmutex_t           SwapListsLock;                         /* mutex to lock swap lists */
++    kmutex_t           CmdLock;                               /* mutex to lock trapped dma command */
++    kmutex_t           CmdPortLock;                           /* mutex to load/unload commandport xlation */
++
++    kcondvar_t                 Wait;                                  /* Condition variable to sleep on */
++    kcondvar_t                 CommandPortWait;                       /* Condition variable to wait for commandport */
++    kcondvar_t                 LwpWait;                               /* Condition variable to wait for lwps to stop */
++    kcondvar_t                 HaltWait;                              /* Condition variable to wait for halt */
++    int                        Halted;                                /*  and flag for halt cv */
++
++    caddr_t            CommandPageMapping;                    /* user virtual address for command page mapping */
++    ioaddr_t             CommandPage;                         /* Elan command port mapping page */
++    DeviceMappingHandle  CommandPageHandle;                   /* DDI Handle */
++    ioaddr_t           CommandPort;                           /* Elan command port */
++    void              *CommandPortItem;                       /* Item we're re-issuing to commandport */
++
++    ELAN3_FLAGSTATS      *FlagPage;                           /* Page visible to user process */
++
++    COMMAND_TRAP      *CommandTraps;                          /* Command port traps */
++    ELAN3_SPLIT_QUEUE     CommandTrapQ;
++                                                                 
++    CProcTrapBuf_BE   *Commands;                              /* Overflowed commands */
++    ELAN3_QUEUE           CommandQ;
++
++    THREAD_TRAP               *ThreadTraps;                           /* Thread processor traps */
++    ELAN3_QUEUE                ThreadTrapQ;
++    
++    DMA_TRAP          *DmaTraps;                              /* Dma processor tra[ed */
++    ELAN3_QUEUE                DmaTrapQ;
++
++    INPUT_TRAP                 Input0Trap;                            /* Inputter channel 0 trap */
++    INPUT_TRAP                 Input1Trap;                            /* Inputter channel 1 trap */
++    NETERR_RESOLVER   *Input0Resolver;                        /* Inputter channel 0 network error resolver */
++    NETERR_RESOLVER   *Input1Resolver;                        /* Inputter channel 1 network error resolver */
++
++    INPUT_FAULT_SAVE   InputFaults[NUM_INPUT_FAULT_SAVE];     /* stored writeblock addresses */
++    INPUT_FAULT_SAVE    *InputFaultList;                      /* organized in list for LRU */
++    spinlock_t                 InputFaultLock;                        /* and lock for list */
++
++    kmutex_t           NetworkErrorLock;
++    NETERR_FIXUP      *NetworkErrorFixups;
++
++    EVENT_COOKIE        *EventCookies;                                /* Event cookies. */
++    ELAN3_QUEUE                EventCookieQ;
++
++    E3_Addr           *SwapThreads;                           /* Swapped Thread Queue */
++    ELAN3_QUEUE                SwapThreadQ;
++
++    E3_DMA_BE         *SwapDmas;                              /* Swapped Dmas Queue */
++    ELAN3_QUEUE                SwapDmaQ;
++
++    int                        ItemCount[NUM_LISTS];                  /* Count of items on each swap list */
++    int                        inhibit;                               /* if set lwp not to reload translations */
++
++    int                  Disabled;
++} ELAN3_CTXT;
++
++_NOTE(MUTEX_PROTECTS_DATA(elan3_dev::IntrLock, 
++                        elan3_ctxt::Status elan3_ctxt::OthersState
++                        elan3_ctxt::CommandTrapQ elan3_ctxt::CommandQ elan3_ctxt::ThreadTrapQ elan3_ctxt::DmaTrapQ 
++                        elan3_ctxt::Input0Trap elan3_ctxt::Input1Trap elan3_ctxt::EventCookieQ elan3_ctxt::SwapThreadQ 
++                        elan3_ctxt::SwapDmaQ elan3_ctxt::CommandPortItem elan3_ctxt::LwpCount))
++_NOTE(MUTEX_PROTECTS_DATA(elan3_ctxt::SwapListsLock, 
++                        elan3_ctxt::ItemCount))
++_NOTE(RWLOCK_PROTECTS_DATA(elan3_ctxt::VpLock, 
++                         elan3_ctxt::VpSegs elan3_vpseg::Next elan3_vpseg::Process 
++                         elan3_vpseg::Entries elan3_vpseg::Type))
++
++_NOTE(DATA_READABLE_WITHOUT_LOCK(elan3_ctxt::ItemCount elan3_ctxt::Status elan3_ctxt::CommandPortItem))
++
++_NOTE(LOCK_ORDER(elan3_ctxt::SwapListsLock elan3_ctxt::CmdLock elan3_dev::IntrLock))
++_NOTE(LOCK_ORDER(elan3_ctxt::SwapListsLock as::a_lock))                       /* implicit by pagefault */
++
++#define CTXT_DETACHED                         (1 << 0)                /* Context is detached. */
++#define CTXT_NO_LWPS                          (1 << 1)                /* No lwp's to handle faults */
++#define CTXT_EXITING                          (1 << 2)                /* User process is exiting */
++
++#define CTXT_SWAPPING_OUT                     (1 << 3)                /* Context is swapping out */
++#define CTXT_SWAPPED_OUT                      (1 << 4)                /* Context is swapped out */
++
++#define CTXT_SWAP_FREE                                (1 << 5)                /* Swap buffer is free */
++#define CTXT_SWAP_VALID                               (1 << 6)                /* Swap buffer has queue entries in it */
++
++#define CTXT_DMA_QUEUE_FULL                   (1 << 7)                /* Dma trap queue is full */
++#define CTXT_THREAD_QUEUE_FULL                        (1 << 8)                /* Thread trap queue is full */
++#define CTXT_EVENT_QUEUE_FULL                 (1 << 9)                /* Event interrupt queue is full */
++#define CTXT_COMMAND_OVERFLOW_ERROR           (1 << 10)               /* Trap queue overflow */
++
++#define CTXT_SWAP_WANTED                      (1 << 11)               /* Some one wanted to swap */
++#define CTXT_WAITING_SWAPIN                   (1 << 12)               /* Someone waiting on swapin */
++
++#define CTXT_WAITING_COMMAND                  (1 << 13)               /* swgelan waiting on command port */
++#define CTXT_COMMAND_MAPPED_MAIN              (1 << 14)               /* segelan has mapped command port */
++
++#define CTXT_QUEUES_EMPTY                     (1 << 15)               /* dma/thread run queues are empty */
++#define CTXT_QUEUES_EMPTYING                  (1 << 16)               /* dma/thread run queues are being emptied */
++
++#define CTXT_USER_FILTERING                   (1 << 17)               /* user requested context filter */
++
++#define CTXT_KERNEL                           (1 << 18)               /* context is a kernel context */
++#define CTXT_COMMAND_MAPPED_ELAN              (1 << 19)               /* command port is mapped for elan */
++#define CTXT_FIXUP_NETERR                     (1 << 20)               /* fixing up a network error */
++
++
++#define CTXT_SWAPPED_REASONS          (CTXT_NO_LWPS   |               \
++                                       CTXT_DETACHED  |               \
++                                       CTXT_EXITING   |               \
++                                       CTXT_FIXUP_NETERR)
++
++#define CTXT_OTHERS_REASONS           (CTXT_EVENT_QUEUE_FULL  |       \
++                                       CTXT_DMA_QUEUE_FULL    |       \
++                                       CTXT_THREAD_QUEUE_FULL |       \
++                                       CTXT_COMMAND_OVERFLOW_ERROR |  \
++                                       CTXT_SWAPPED_REASONS)
++
++#define CTXT_INPUTTER_REASONS         (CTXT_USER_FILTERING |          \
++                                       CTXT_OTHERS_REASONS)
++
++#define CTXT_COMMAND_MAPPED           (CTXT_COMMAND_MAPPED_MAIN |     \
++                                       CTXT_COMMAND_MAPPED_ELAN)
++
++#define CTXT_IS_KERNEL(ctxt)          ((ctxt)->Status & CTXT_KERNEL)
++
++/*
++ * State values for ctxt_inputterState/ctxt_commandportStats
++ */
++#define CTXT_STATE_OK                 0
++#define CTXT_STATE_TRAPPED            1               /* Inputter channel 0 trapped */
++#define CTXT_STATE_RESOLVING          2               /* An LWP is resolving the trap */
++#define CTXT_STATE_NEEDS_RESTART      3               /* Th trapped packet needs to be executed */
++#define CTXT_STATE_NETWORK_ERROR      4               /* We're waiting on an RPC for the identify transaction */
++#define CTXT_STATE_EXECUTING          5               /* An LWP is executing the trapped packet */
++
++/*
++ * State values for OthersState.
++ */
++#define CTXT_OTHERS_RUNNING           0
++#define CTXT_OTHERS_HALTING           1
++#define CTXT_OTHERS_SWAPPING          2
++#define CTXT_OTHERS_HALTING_MORE      3
++#define CTXT_OTHERS_SWAPPING_MORE     4
++#define CTXT_OTHERS_SWAPPED           5
++
++typedef struct elan3_ops
++{
++    u_int  Version;
++
++    int        (*Exception)   (ELAN3_CTXT *ctxt, int type, int proc, void *trap, va_list ap);
++
++    /* swap item list functions */
++    int  (*GetWordItem)               (ELAN3_CTXT *ctxt, int list, void **itemp, E3_uint32 *valuep);
++    int  (*GetBlockItem)      (ELAN3_CTXT *ctxt, int list, void **itemp, E3_Addr *valuep);
++    void (*PutWordItem)               (ELAN3_CTXT *ctxt, int list, E3_Addr value);
++    void (*PutBlockItem)      (ELAN3_CTXT *ctxt, int list, E3_uint32 *ptr);
++    void (*PutbackItem)               (ELAN3_CTXT *ctxt, int list, void *item);
++    void (*FreeWordItem)      (ELAN3_CTXT *ctxt, void *item);
++    void (*FreeBlockItem)     (ELAN3_CTXT *ctxt, void *item);
++    int  (*CountItems)                (ELAN3_CTXT *ctxt, int list);
++
++    /* event interrupt cookie */
++    int  (*Event)             (ELAN3_CTXT *ctxt, E3_uint32 cookie, int flag);
++
++    /* swapin/swapout functions. */
++    void (*Swapin)            (ELAN3_CTXT *ctxt);
++    void (*Swapout)           (ELAN3_CTXT *ctxt);
++
++    /* Free of private data */
++    void (*FreePrivate)               (ELAN3_CTXT *ctxt);
++
++    /* Fixup a network error */
++    int  (*FixupNetworkError) (ELAN3_CTXT *ctxt, NETERR_FIXUP *nef);
++
++    /* Interrupt handler trap interface */
++    int  (*DProcTrap)         (ELAN3_CTXT *ctxt, DMA_TRAP *trap);
++    int  (*TProcTrap)         (ELAN3_CTXT *ctxt, THREAD_TRAP *trap);
++    int        (*IProcTrap)           (ELAN3_CTXT *ctxt, INPUT_TRAP *trap, int chan);
++    int        (*CProcTrap)           (ELAN3_CTXT *ctxt, COMMAND_TRAP *trap);
++    int  (*CProcReissue)        (ELAN3_CTXT *ctxt, CProcTrapBuf_BE *TrapBuf);
++
++    /* User memory access functions */
++    int             (*StartFaultCheck)(ELAN3_CTXT *ctxt);
++    void      (*EndFaultCheck)  (ELAN3_CTXT *ctxt);
++
++    E3_uint8  (*Load8)                (ELAN3_CTXT *ctxt, E3_Addr addr);
++    void      (*Store8)               (ELAN3_CTXT *ctxt, E3_Addr addr, E3_uint8 val);
++    E3_uint16 (*Load16)               (ELAN3_CTXT *ctxt, E3_Addr addr);
++    void      (*Store16)      (ELAN3_CTXT *ctxt, E3_Addr addr, E3_uint16 val);
++    E3_uint32 (*Load32)               (ELAN3_CTXT *ctxt, E3_Addr addr);
++    void      (*Store32)      (ELAN3_CTXT *ctxt, E3_Addr addr, E3_uint32 val);
++    E3_uint64 (*Load64)               (ELAN3_CTXT *ctxt, E3_Addr addr);
++    void      (*Store64)      (ELAN3_CTXT *ctxt, E3_Addr addr, E3_uint64 val);
++
++} ELAN3_OPS;
++
++#define ELAN3_OPS_VERSION     0xdeef0001
++
++/*
++ * Flags for ops_event.
++ */
++#define OP_INTR                       0               /* Called from interrupt handler */
++#define OP_LWP                        1               /* Called from "lwp" */
++
++/*
++ * Return codes for "ops" functions.
++ */
++#define OP_DEFER              0               /* Defer to next lower interrupt */
++#define OP_IGNORE             1               /* No event hander, so ignore it */
++#define OP_HANDLED            2               /* Handled event (resume thread) */
++#define OP_FAILED             3               /* Failed */
++
++#define ELAN3_CALL_OP(ctxt,fn)                                ((ctxt)->Operations && (ctxt)->Operations->fn) ? (ctxt)->Operations->fn 
++
++#define ELAN3_OP_EXCEPTION(ctxt,type,proc,trap,ap)    (ELAN3_CALL_OP(ctxt,Exception)    (ctxt,type,proc,trap,ap)      : OP_IGNORE)
++#define ELAN3_OP_GET_WORD_ITEM(ctxt,list,itemp,valuep)        (ELAN3_CALL_OP(ctxt,GetWordItem)  (ctxt,list,itemp,valuep)      : 0)
++#define ELAN3_OP_GET_BLOCK_ITEM(ctxt,list,itemp,valuep)       (ELAN3_CALL_OP(ctxt,GetBlockItem) (ctxt,list,itemp,valuep)      : 0)
++#define ELAN3_OP_PUT_WORD_ITEM(ctxt,list,value)               (ELAN3_CALL_OP(ctxt,PutWordItem)  (ctxt,list,value)             : (void)0)
++#define ELAN3_OP_PUT_BLOCK_ITEM(ctxt,list,ptr)                (ELAN3_CALL_OP(ctxt,PutBlockItem) (ctxt,list,ptr)               : (void)0)
++#define ELAN3_OP_PUTBACK_ITEM(ctxt,list,item)         (ELAN3_CALL_OP(ctxt,PutbackItem)  (ctxt,list,item)              : (void)0)
++#define ELAN3_OP_FREE_WORD_ITEM(ctxt,item)            (ELAN3_CALL_OP(ctxt,FreeWordItem) (ctxt,item)                   : (void)0)
++#define ELAN3_OP_FREE_BLOCK_ITEM(ctxt,item)           (ELAN3_CALL_OP(ctxt,FreeBlockItem)(ctxt,item)                   : (void)0)
++#define ELAN3_OP_COUNT_ITEMS(ctxt,list)                       (ELAN3_CALL_OP(ctxt,CountItems)(ctxt,list)                      : 0)
++#define ELAN3_OP_EVENT(ctxt,cookie,flag)              (ELAN3_CALL_OP(ctxt,Event)(ctxt,cookie,flag)                    : OP_IGNORE)
++#define ELAN3_OP_SWAPIN(ctxt)                         (ELAN3_CALL_OP(ctxt,Swapin)(ctxt)                               : (void)0)
++#define ELAN3_OP_SWAPOUT(ctxt)                                (ELAN3_CALL_OP(ctxt,Swapout)(ctxt)                              : (void)0)
++#define ELAN3_OP_FREE_PRIVATE(ctxt)                   (ELAN3_CALL_OP(ctxt,FreePrivate)(ctxt)                          : (void)0)
++#define ELAN3_OP_FIXUP_NETWORK_ERROR(ctxt, nef)               (ELAN3_CALL_OP(ctxt,FixupNetworkError)(ctxt,nef)                        : OP_FAILED)
++
++#define ELAN3_OP_DPROC_TRAP(ctxt, trap)                       (ELAN3_CALL_OP(ctxt,DProcTrap)(ctxt,trap)                       : OP_DEFER)
++#define ELAN3_OP_TPROC_TRAP(ctxt, trap)                       (ELAN3_CALL_OP(ctxt,TProcTrap)(ctxt,trap)                       : OP_DEFER)
++#define ELAN3_OP_IPROC_TRAP(ctxt, trap, chan)         (ELAN3_CALL_OP(ctxt,IProcTrap)(ctxt,trap,chan)                  : OP_DEFER)
++#define ELAN3_OP_CPROC_TRAP(ctxt, trap)                       (ELAN3_CALL_OP(ctxt,CProcTrap)(ctxt,trap)                       : OP_DEFER)
++#define ELAN3_OP_CPROC_REISSUE(ctxt,tbuf)             (ELAN3_CALL_OP(ctxt,CProcReissue)(ctxt, tbuf)                   : OP_DEFER)
++
++#define ELAN3_OP_START_FAULT_CHECK(ctxt)              (ELAN3_CALL_OP(ctxt,StartFaultCheck)(ctxt)                      : 0)
++#define ELAN3_OP_END_FAULT_CHECK(ctxt)                        (ELAN3_CALL_OP(ctxt,EndFaultCheck)(ctxt)                                : (void)0)
++#define ELAN3_OP_LOAD8(ctxt,addr)                     (ELAN3_CALL_OP(ctxt,Load8)(ctxt,addr)                           : 0)
++#define ELAN3_OP_STORE8(ctxt,addr,val)                        (ELAN3_CALL_OP(ctxt,Store8)(ctxt,addr,val)                      : (void)0)
++#define ELAN3_OP_LOAD16(ctxt,addr)                    (ELAN3_CALL_OP(ctxt,Load16)(ctxt,addr)                          : 0)
++#define ELAN3_OP_STORE16(ctxt,addr,val)                       (ELAN3_CALL_OP(ctxt,Store16)(ctxt,addr,val)                     : (void)0)
++#define ELAN3_OP_LOAD32(ctxt,addr)                    (ELAN3_CALL_OP(ctxt,Load32)(ctxt,addr)                          : 0)
++#define ELAN3_OP_STORE32(ctxt,addr,val)                       (ELAN3_CALL_OP(ctxt,Store32)(ctxt,addr,val)                     : (void)0)
++#define ELAN3_OP_LOAD64(ctxt,addr)                    (ELAN3_CALL_OP(ctxt,Load64)(ctxt,addr)                          : 0)
++#define ELAN3_OP_STORE64(ctxt,addr,val)                       (ELAN3_CALL_OP(ctxt,Store64)(ctxt,addr,val)                     : (void)0)
++
++#endif /* __KERNEL__ */
++
++/* "list" arguement to ops functions */
++#define LIST_DMA_PTR          0
++#define LIST_DMA_DESC         1
++#define LIST_THREAD                   2
++#define LIST_COMMAND          3
++#define LIST_SETEVENT         4
++#define LIST_FREE_WORD                5
++#define LIST_FREE_BLOCK               6
++
++#define MAX_LISTS             7
++
++#if defined(__KERNEL__) && MAX_LISTS != NUM_LISTS
++#  error Check NUM_LISTS == MAX_LISTS
++#endif
++
++/*
++ * Values for the 'type' field to PostException().
++ */
++#define EXCEPTION_INVALID_ADDR                1               /* FaultArea, res */
++#define EXCEPTION_UNIMP_INSTR         2               /* instr */
++#define EXCEPTION_INVALID_PROCESS     3               /* proc, res */
++#define EXCEPTION_SIMULATION_FAILED   4               /* */
++#define EXCEPTION_UNIMPLEMENTED               5               /* */
++#define EXCEPTION_SWAP_FAULT          6               /* */
++#define EXCEPTION_SWAP_FAILED         7               /* */
++#define EXCEPTION_BAD_PACKET          8               /* */
++#define EXCEPTION_FAULTED             9               /* addr */
++#define EXCEPTION_QUEUE_OVERFLOW      10              /* FaultArea, TrapType */
++#define EXCEPTION_COMMAND_OVERFLOW    11              /* count */
++#define EXCEPTION_DMA_RETRY_FAIL      12              /* */
++#define EXCEPTION_CHAINED_EVENT               13              /* EventAddr */
++#define EXCEPTION_THREAD_KILLED               14              /* */
++#define EXCEPTION_CANNOT_SAVE_THREAD  15
++#define EXCEPTION_BAD_SYSCALL         16              /* */
++#define EXCEPTION_DEBUG                       17
++#define EXCEPTION_BAD_EVENT           18              /* */
++#define EXCEPTION_NETWORK_ERROR               19              /* rvp */
++#define EXCEPTION_BUS_ERROR           20
++#define EXCEPTION_COOKIE_ERROR                21
++#define EXCEPTION_PACKET_TIMEOUT      22
++#define EXCEPTION_BAD_DMA             23              /* */
++#define EXCEPTION_ENOMEM              24
++
++/*
++ * Values for the 'proc' field to ElanException().
++ */
++#define COMMAND_PROC                  1
++#define THREAD_PROC                   2
++#define DMA_PROC                      3
++#define INPUT_PROC                    4
++#define EVENT_PROC                    5
++
++/* Flags to IssueDmaCommand */
++#define ISSUE_COMMAND_FOR_CPROC               1
++#define ISSUE_COMMAND_CANT_WAIT               2
++
++/* Return code from IssueDmaCommand.*/
++#define ISSUE_COMMAND_OK              0
++#define ISSUE_COMMAND_TRAPPED         1
++#define ISSUE_COMMAND_RETRY           2
++#define ISSUE_COMMAND_WAIT            3
++
++#ifdef __KERNEL__
++
++extern ELAN3_CTXT *elan3_alloc(ELAN3_DEV *dev, int kernel);
++extern void      elan3_free      (ELAN3_CTXT *ctxt);
++
++extern int       elan3_attach    (ELAN3_CTXT *ctxt, ELAN_CAPABILITY *cap);
++extern int         elan3_doattach  (ELAN3_CTXT *ctxt, ELAN_CAPABILITY *cap);
++extern void      elan3_detach    (ELAN3_CTXT *ctxt);
++extern void        elan3_dodetach  (ELAN3_CTXT *ctxt);
++
++extern int       elan3_addvp     (ELAN3_CTXT *ctxt, int process, ELAN_CAPABILITY *cap);
++extern int       elan3_removevp  (ELAN3_CTXT *ctxt, int process);
++extern int       elan3_addbcastvp(ELAN3_CTXT *ctxt, int process, int base, int count);
++
++extern int         elan3_process   (ELAN3_CTXT *ctxt);
++
++extern int       elan3_load_route (ELAN3_CTXT *ctxt, int process, E3_uint16 *flits);
++extern int       elan3_check_route(ELAN3_CTXT *ctxt, int process, E3_uint16 *flits, E3_uint32 *routeError);
++
++extern int       elan3_lwp       (ELAN3_CTXT *ctxt);
++
++extern void      elan3_swapin (ELAN3_CTXT *ctxt, int reason);
++extern void      elan3_swapout (ELAN3_CTXT *ctxt, int reason);
++extern int         elan3_pagefault (ELAN3_CTXT *ctxt, E3_FaultSave_BE *FaultSave, int npages);
++extern void        elan3_block_inputter (ELAN3_CTXT *ctxt, int block);
++
++
++extern E3_Addr     elan3_init_thread (ELAN3_DEV *dev, E3_Addr fn, E3_Addr addr, sdramaddr_t stack, int stackSize, int nargs, ...);
++
++extern void      SetInputterState (ELAN3_CTXT *ctxt, E3_uint32 Pend, E3_uint32 *Maskp);
++extern void      SetInputterStateForContext (ELAN3_CTXT *ctxt, E3_uint32 Pend, E3_uint32 *Maskp);
++extern void        UnloadCommandPageMapping (ELAN3_CTXT *ctxt);
++extern void      StartSwapoutContext (ELAN3_CTXT *ctxt, E3_uint32 Pend, E3_uint32 *Maskp);
++
++extern int       HandleExceptions (ELAN3_CTXT *ctxt, unsigned long *flags);
++extern int       RestartContext (ELAN3_CTXT *ctxt, unsigned long *flags);
++extern int         CheckCommandQueueFlushed (ELAN3_CTXT *ctxt, E3_uint32 cflags, int how, unsigned long *flags);
++extern int       IssueCommand (ELAN3_CTXT *ctxt, unsigned cmdoff, E3_Addr value, int flags);
++extern int       IssueDmaCommand (ELAN3_CTXT *ctxt, E3_Addr value, void *item, int flags);
++extern int         WaitForDmaCommand (ELAN3_CTXT *ctxt, void *item, int flags);
++extern void      FixupEventTrap (ELAN3_CTXT *ctxt, int proc, void *trap, E3_uint32 TrapType, 
++                                 E3_FaultSave_BE *FaultSaveArea, int flags);
++extern int       SimulateBlockCopy (ELAN3_CTXT *ctxt, E3_Addr EventAddress);
++extern void      ReissueEvent (ELAN3_CTXT *ctxt, E3_Addr addr,int flags);
++extern int         SetEventsNeedRestart (ELAN3_CTXT *ctxt);
++extern void        RestartSetEvents (ELAN3_CTXT *ctxt);
++extern int       RunEventType (ELAN3_CTXT *ctxt, E3_FaultSave_BE *FaultSaveArea, E3_uint32 EventType);
++extern void        WakeupLwp (ELAN3_DEV *dev, void *arg);
++extern void      QueueEventInterrupt (ELAN3_CTXT *ctxt, E3_uint32 cookie);
++extern int         WaitForCommandPort (ELAN3_CTXT *ctxt);
++
++extern int       ElanException (ELAN3_CTXT *ctxt, int type, int proc, void *trap, ...);
++
++/* context_osdep.c */
++extern int       LoadElanTranslation (ELAN3_CTXT *ctxt, E3_Addr elanAddr, int len, int protFault, int writeable);
++extern void      LoadCommandPortTranslation (ELAN3_CTXT *ctxt);
++
++#if defined(DIGITAL_UNIX)
++/* seg_elan.c */
++extern caddr_t           elan3_segelan3_create (ELAN3_CTXT *ctxt);
++extern void      elan3_segelan3_destroy (ELAN3_CTXT *ctxt);
++extern int         elan3_segelan3_map (ELAN3_CTXT *ctxt);
++extern void        elan3_segelan3_unmap (ELAN3_CTXT *ctxt);
++
++/* seg_elanmem.c */
++extern int       elan3_segelanmem_create (ELAN3_DEV *dev, unsigned object, unsigned off, vm_offset_t *addrp, int len);
++#endif /* defined(DIGITAL_UNIX) */
++
++/* route_table.c */
++extern ELAN3_ROUTE_TABLE *AllocateRouteTable (ELAN3_DEV *dev, int size);
++extern void              FreeRouteTable  (ELAN3_DEV *dev, ELAN3_ROUTE_TABLE *tbl);
++extern int               LoadRoute       (ELAN3_DEV *dev, ELAN3_ROUTE_TABLE *tbl, int vp, int ctxnum, int nflits, E3_uint16 *flits);
++extern int               GetRoute        (ELAN3_DEV *dev, ELAN3_ROUTE_TABLE *tbl, int process, E3_uint16 *flits);
++extern void            InvalidateRoute (ELAN3_DEV *dev, ELAN3_ROUTE_TABLE *tbl, int vp);
++extern void            ValidateRoute   (ELAN3_DEV *dev, ELAN3_ROUTE_TABLE *tbl, int vp);
++extern void            ClearRoute      (ELAN3_DEV *dev, ELAN3_ROUTE_TABLE *tbl, int vp);
++
++extern int               GenerateRoute (ELAN_POSITION *pos, E3_uint16 *flits, int lowid, int highid, int timeout, int highPri);
++extern int               GenerateProbeRoute (E3_uint16 *flits, int nodeid, int level, int *linkup, int *linkdown, int adaptive);
++extern int               GenerateCheckRoute (ELAN_POSITION *pos, E3_uint16 *flits, int level, int adaptive);
++
++/* virtual_process.c */
++extern ELAN_LOCATION  ProcessToLocation     (ELAN3_CTXT *ctxt, ELAN3_VPSEG *seg, int process, ELAN_CAPABILITY *cap);
++extern int          ResolveVirtualProcess (ELAN3_CTXT *ctxt, int process);
++extern caddr_t        CapabilityString      (ELAN_CAPABILITY *cap);
++extern void           UnloadVirtualProcess  (ELAN3_CTXT *ctxt, ELAN_CAPABILITY *cap);
++
++extern int           elan3_get_route   (ELAN3_CTXT *ctxt, int process, E3_uint16 *flits);
++extern int           elan3_reset_route (ELAN3_CTXT *ctxt, int process);
++
++/* cproc.c */
++extern int      NextCProcTrap (ELAN3_CTXT *ctxt, COMMAND_TRAP *trap);
++extern void     ResolveCProcTrap (ELAN3_CTXT *ctxt);
++extern int      RestartCProcTrap (ELAN3_CTXT *ctxt);
++
++/* iproc.c */
++extern void       InspectIProcTrap (ELAN3_CTXT *ctxt, INPUT_TRAP *trap);
++extern void     ResolveIProcTrap (ELAN3_CTXT *ctxt, INPUT_TRAP *trap, NETERR_RESOLVER **rvp);
++extern int      RestartIProcTrap (ELAN3_CTXT *ctxt, INPUT_TRAP *trap);
++extern char      *IProcTrapString (E3_IprocTrapHeader_BE *hdrp, E3_IprocTrapData *datap);
++extern void       SimulateUnlockQueue (ELAN3_CTXT *ctxt, E3_Addr QueuePointer, int SentAck);
++
++/* tproc.c */
++extern int      NextTProcTrap (ELAN3_CTXT *ctxt, THREAD_TRAP *trap);
++extern void     ResolveTProcTrap (ELAN3_CTXT *ctxt, THREAD_TRAP *trap);
++extern int      TProcNeedsRestart (ELAN3_CTXT *ctxt);
++extern void     RestartTProcItems (ELAN3_CTXT *ctxt);
++extern E3_Addr    SaveThreadToStack (ELAN3_CTXT *ctxt, THREAD_TRAP *trap, int SkipInstruction);
++extern void       ReissueStackPointer (ELAN3_CTXT *ctxt, E3_Addr StackPointer);
++
++/* tprocinsts.c */
++extern int        RollThreadToClose (ELAN3_CTXT *ctxt, THREAD_TRAP *trap, E3_uint32 PAckVal);
++
++/* tproc_osdep.c */
++extern int        ThreadSyscall (ELAN3_CTXT *ctxt, THREAD_TRAP *trap, int *skip);
++extern int      ThreadElancall (ELAN3_CTXT *ctxt, THREAD_TRAP *trap, int *skip);
++
++/* dproc.c */
++extern int      NextDProcTrap (ELAN3_CTXT *ctxt, DMA_TRAP *trap);
++extern void     ResolveDProcTrap (ELAN3_CTXT *ctxt, DMA_TRAP *trap);
++extern int      DProcNeedsRestart (ELAN3_CTXT *ctxt);
++extern void     RestartDProcItems (ELAN3_CTXT *ctxt);
++extern void       RestartDmaDesc (ELAN3_CTXT *ctxt, E3_DMA_BE *desc);
++extern void       RestartDmaTrap (ELAN3_CTXT *ctxt, DMA_TRAP *trap);
++extern void     RestartDmaPtr (ELAN3_CTXT *ctxt, E3_Addr ptr);
++
++/* network_error.c */
++extern void       InitialiseNetworkErrorResolver (void);
++extern void       FinaliseNetworkErrorResolver (void);
++extern int        QueueNetworkErrorResolver (ELAN3_CTXT *ctxt, INPUT_TRAP *trap, NETERR_RESOLVER **rvpp);
++extern void     FreeNetworkErrorResolver (NETERR_RESOLVER *rvp);
++extern void       CancelNetworkErrorResolver (NETERR_RESOLVER *rvp);
++extern int      ExecuteNetworkErrorFixup (NETERR_MSG *msg);
++extern void     CompleteNetworkErrorFixup (ELAN3_CTXT *ctxt, NETERR_FIXUP *nef, int status);
++
++extern int        AddNeterrServerSyscall (int elanId, void *configp, void *addrp, char *namep);
++
++/* eventcookie.c */
++extern void                cookie_init(void);
++extern void                cookie_fini(void);
++extern EVENT_COOKIE_TABLE *cookie_alloc_table (unsigned long task, unsigned long handle);
++extern void                cookie_free_table (EVENT_COOKIE_TABLE *tbl);
++extern int                 cookie_alloc_cookie (EVENT_COOKIE_TABLE *tbl, EVENT_COOKIE cookie);
++extern int                 cookie_free_cookie (EVENT_COOKIE_TABLE *tbl, EVENT_COOKIE cookie);
++extern int                 cookie_fire_cookie (EVENT_COOKIE_TABLE *tbl, EVENT_COOKIE cookie);
++extern int                 cookie_wait_cookie (EVENT_COOKIE_TABLE *tbl, EVENT_COOKIE cookie);
++extern int                 cookie_arm_cookie (EVENT_COOKIE_TABLE *tbl, EVENT_COOKIE cookie);
++
++/* routecheck.c */
++extern int elan3_route_check          (ELAN3_CTXT *ctxt, E3_uint16 *flits, int destNode);
++extern int elan3_route_broadcast_check(ELAN3_CTXT *ctxt, E3_uint16 *flitsA, int lowNode, int highNode);
++
++
++#endif /* __KERNEL__ */
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* _ELAN3_ELANCTXT_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/include/elan3/elandebug.h
+===================================================================
+--- linux-2.4.21.orig/include/elan3/elandebug.h        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan3/elandebug.h     2005-06-01 23:12:54.720420712 -0400
+@@ -0,0 +1,106 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef _ELAN3_ELANDEBUG_H
++#define _ELAN3_ELANDEBUG_H
++
++#ident "$Id: elandebug.h,v 1.38 2003/09/24 13:57:24 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/elandebug.h,v $ */
++
++#if defined(__KERNEL__)
++
++extern u_int elan3_debug;
++extern u_int elan3_debug_console;
++extern u_int elan3_debug_buffer;
++extern u_int elan3_debug_ignore_dev;
++extern u_int elan3_debug_ignore_kcomm;
++extern u_int elan3_debug_ignore_ctxt;
++extern u_int elan3_debug_display_ctxt;
++
++#define DBG_CONFIG    0x00000001                      /* Module configuration */
++#define DBG_HAT               0x00000002
++#define DBG_FN                0x00000004
++#define DBG_SEG               0x00000008
++#define DBG_INTR      0x00000010
++#define DBG_LWP               0x00000020
++#define DBG_FAULT     0x00000040
++#define DBG_EVENT     0x00000080
++#define DBG_CPROC     0x00000100
++#define DBG_TPROC     0x00000200
++#define DBG_DPROC     0x00000400
++#define DBG_IPROC     0x00000800
++#define DBG_SWAP      0x00001000
++#define DBG_CMD               0x00002000
++#define DBG_VP                0x00004000
++#define DBG_SYSCALL   0x00008000
++#define DBG_BSCAN     0x00010000
++#define DBG_LINKERR   0x00020000
++#define DBG_NETERR    0x00040000
++#define DBG_NETRPC    0x00080000
++#define DBG_EVENTCOOKIE 0x00100000
++#define DBG_SDRAM     0x00200000
++
++#define DBG_EP                0x10000000
++#define DBG_EPCONSOLE 0x20000000
++
++#define DBG_EIP               0x40000000
++#define DBG_EIPFAIL   0x80000000
++
++#define DBG_ALL               0xffffffff
++
++/* values to pass as "ctxt" rather than a "ctxt" pointer */
++#define DBG_DEVICE    ((void *) 0)
++#define DBG_KCOMM     ((void *) 1)
++#define DBG_ICS               ((void *) 2)
++#define DBG_USER      ((void *) 3)
++#define DBG_NTYPES    64
++
++#if defined(DEBUG_PRINTF)
++#  define DBG(m,fn)                           ((elan3_debug&(m)) ? (void)(fn) : (void)0)
++#  define PRINTF0(ctxt,m,fmt)                 ((elan3_debug&(m)) ? elan3_debugf(ctxt,m,fmt)             : (void)0)
++#  define PRINTF1(ctxt,m,fmt,a)                       ((elan3_debug&(m)) ? elan3_debugf(ctxt,m,fmt,a)           : (void)0)
++#  define PRINTF2(ctxt,m,fmt,a,b)             ((elan3_debug&(m)) ? elan3_debugf(ctxt,m,fmt,a,b)         : (void)0)
++#  define PRINTF3(ctxt,m,fmt,a,b,c)           ((elan3_debug&(m)) ? elan3_debugf(ctxt,m,fmt,a,b,c)       : (void)0)
++#  define PRINTF4(ctxt,m,fmt,a,b,c,d)         ((elan3_debug&(m)) ? elan3_debugf(ctxt,m,fmt,a,b,c,d)     : (void)0)
++#  define PRINTF5(ctxt,m,fmt,a,b,c,d,e)               ((elan3_debug&(m)) ? elan3_debugf(ctxt,m,fmt,a,b,c,d,e)   : (void)0)
++#  define PRINTF6(ctxt,m,fmt,a,b,c,d,e,f)     ((elan3_debug&(m)) ? elan3_debugf(ctxt,m,fmt,a,b,c,d,e,f) : (void)0)
++#ifdef __GNUC__
++#  define PRINTF(ctxt,m,args...)              ((elan3_debug&(m)) ? elan3_debugf(ctxt,m, ##args)         : (void)0)
++#endif
++
++#else
++
++#  define DBG(m, fn)                          do { ; } while (0)
++#  define PRINTF0(ctxt,m,fmt)                 do { ; } while (0)
++#  define PRINTF1(ctxt,m,fmt,a)                       do { ; } while (0)
++#  define PRINTF2(ctxt,m,fmt,a,b)             do { ; } while (0)
++#  define PRINTF3(ctxt,m,fmt,a,b,c)           do { ; } while (0)
++#  define PRINTF4(ctxt,m,fmt,a,b,c,d)         do { ; } while (0)
++#  define PRINTF5(ctxt,m,fmt,a,b,c,d,e)               do { ; } while (0)
++#  define PRINTF6(ctxt,m,fmt,a,b,c,d,e,f)     do { ; } while (0)
++#ifdef __GNUC__
++#  define PRINTF(ctxt,m,args...)              do { ; } while (0)
++#endif
++
++#endif /* DEBUG_PRINTF */
++
++#ifdef __GNUC__
++extern void       elan3_debugf (void *ctxt, unsigned int mode, char *fmt, ...)
++    __attribute__ ((format (printf,3,4)));
++#else
++extern void       elan3_debugf (void *ctxt, unsigned int mode, char *fmt, ...);
++#endif
++
++
++#endif /* __KERNEL__ */
++#endif /* _ELAN3_ELANDEBUG_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/include/elan3/elandev.h
+===================================================================
+--- linux-2.4.21.orig/include/elan3/elandev.h  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan3/elandev.h       2005-06-01 23:12:54.721420560 -0400
+@@ -0,0 +1,581 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN3_ELANDEV_H
++#define __ELAN3_ELANDEV_H
++
++#ident "$Id: elandev.h,v 1.74.2.2 2004/12/10 11:10:19 mike Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/elandev.h,v $ */
++
++#include <elan/bitmap.h>
++#include <elan/devinfo.h>
++#include <elan/stats.h>
++
++#if defined(DIGITAL_UNIX)
++#  include <elan3/elandev_dunix.h>
++#elif defined(LINUX)
++#  include <elan3/elandev_linux.h>
++#elif defined(SOLARIS)
++#  include <elan3/elandev_solaris.h>
++#endif
++
++#ifndef TRUE
++#  define TRUE 1
++#endif
++#ifndef FALSE
++#  define FALSE 0
++#endif
++
++/*
++ * Elan base address registers defined as follows :
++ */
++#define ELAN3_BAR_SDRAM               0
++#define ELAN3_BAR_COMMAND_PORT        1
++#define ELAN3_BAR_REGISTERS   2
++#define ELAN3_BAR_EBUS                3
++
++/* Macro to generate 'offset' to mmap "mem" device */
++#define OFF_TO_SPACE(off)     ((off) >> 28)
++#define OFF_TO_OFFSET(off)    ((off) & 0x0FFFFFFF)
++#define GEN_OFF(space,off)    (((space) << 28) | ((off) & 0x0FFFFFFF))
++
++#ifdef __KERNEL__
++
++/*
++ * Elan EBUS is configured as follows :
++ */
++#define ELAN3_EBUS_ROM_OFFSET         0x000000                /* rom */
++#define ELAN3_EBUS_INTPAL_OFFSET      0x180000                /* interrupt pal (write only) */
++
++#define ELAN3_EBUS_ROM_SIZE           0x100000
++
++/*
++ * Elan SDRAM is arranged as follows :
++ */
++#define ELAN3_TANDQ_SIZE              0x0020000               /* Trap And Queue Size */
++#define ELAN3_CONTEXT_SIZE            0x0010000               /* Context Table Size */
++#define ELAN3_COMMAND_TRAP_SIZE               0x0010000               /* Command Port Trap Size */
++
++#ifdef MPSAS
++#define ELAN3_LN2_NUM_CONTEXTS        8                               /* Support 256 contexts */
++#else
++#define ELAN3_LN2_NUM_CONTEXTS        12                              /* Support 4096 contexts */
++#endif
++#define ELAN3_NUM_CONTEXTS    (1 << ELAN3_LN2_NUM_CONTEXTS)   /* Entries in context table */
++
++#define ELAN3_SDRAM_NUM_BANKS 4                               /* Elan supports 4 Banks of Sdram */
++#define ELAN3_SDRAM_BANK_SHIFT        26                              /* each of which can be 64 mbytes ? */
++#define ELAN3_SDRAM_BANK_SIZE (1 << ELAN3_SDRAM_BANK_SHIFT)
++
++#define ELAN3_MAX_CACHE_SIZE  (64 * 1024)                     /* Maximum cache size */
++#define ELAN3_CACHE_SIZE      (64 * 4 * E3_CACHELINE_SIZE)    /* Elan3 has 8K cache */
++
++#ifndef offsetof
++#define offsetof(s, m)                (size_t)(&(((s *)0)->m))
++#endif
++
++/*
++ * circular queue and macros to access members.
++ */
++typedef struct
++{
++    u_int     q_back;                 /* Next free space */
++    u_int     q_front;                /* First object to remove */
++    u_int     q_size;                 /* Size of queue */
++    u_int     q_count;                /* Current number of entries */
++    u_int     q_slop;                 /* FULL <=> (count+slop) == size */
++} ELAN3_QUEUE;
++
++typedef struct 
++{
++    u_int     q_back;                 /* Next free space */
++    u_int     q_middle;               /* Middle pointer */
++    u_int     q_front;                /* First object to remove */
++    u_int     q_size;                 /* Size of queue */
++    u_int     q_count;                /* Current number of entries */
++    u_int     q_slop;                 /* FULL <=> (count+slop) == size */
++} ELAN3_SPLIT_QUEUE;
++
++#define ELAN3_QUEUE_INIT(q,num,slop)  ((q).q_size = (num), (q).q_slop = (slop)+1, (q).q_front = (q).q_back = 0, (q).q_count = 0)
++#define ELAN3_QUEUE_FULL(q)           ((q).q_count == ((q).q_size - (q).q_slop))
++#define ELAN3_QUEUE_REALLY_FULL(q)    ((q).q_count == (q).q_size - 1)
++#define ELAN3_QUEUE_EMPTY(q)          ((q).q_count == 0)
++#define ELAN3_QUEUE_FRONT_EMPTY(q)    ((q).q_front == (q).q_middle)
++#define ELAN3_QUEUE_BACK_EMPTY(q)     ((q).q_middle == (q).q_back)
++#define ELAN3_QUEUE_ADD(q)            ((q).q_back = ((q).q_back+1) % (q).q_size, (q).q_count++)
++#define ELAN3_QUEUE_REMOVE(q)         ((q).q_front = ((q).q_front+1) % (q).q_size, (q).q_count--)
++#define ELAN3_QUEUE_ADD_FRONT(q)              ((q).q_front = ((q).q_front-1) % (q).q_size, (q).q_count++)
++#define ELAN3_QUEUE_CONSUME(q)                ((q).q_middle = ((q).q_middle+1) % (q).q_size)
++#define ELAN3_QUEUE_FRONT(q,qArea)    (&(qArea)[(q).q_front])
++#define ELAN3_QUEUE_MIDDLE(q,qArea)   (&(qArea)[(q).q_middle])
++#define ELAN3_QUEUE_BACK(q,qArea)     (&(qArea)[(q).q_back])
++
++#define SDRAM_MIN_BLOCK_SHIFT 10
++#define SDRAM_NUM_FREE_LISTS  17                              /* allows max 64Mb block */
++#define SDRAM_MIN_BLOCK_SIZE  (1 << SDRAM_MIN_BLOCK_SHIFT)
++#define SDRAM_MAX_BLOCK_SIZE  (SDRAM_MIN_BLOCK_SIZE << (SDRAM_NUM_FREE_LISTS-1))
++#define SDRAM_FREELIST_TRIGGER        32
++
++typedef struct elan3_sdram_bank
++{
++    u_int             Size;                                   /* Size of bank of memory */
++
++    ioaddr_t          Mapping;                                /* Where mapped in the kernel */
++    DeviceMappingHandle Handle;                                       /* and mapping handle */
++
++    struct elan3_ptbl_gr **PtblGroups;
++    
++    bitmap_t         *Bitmaps[SDRAM_NUM_FREE_LISTS];
++} ELAN3_SDRAM_BANK;
++
++typedef struct elan3_haltop
++{
++    struct elan3_haltop         *Next;                                /* Chain to next in list. */
++    E3_uint32            Mask;                                /* Interrupt mask to see before calling function */
++    
++    void               (*Function)(void *, void *);           /* Function to call */
++    void                *Arguement;                           /* Arguement to pass to function */
++} ELAN3_HALTOP;
++
++#define HALTOP_BATCH  32
++
++#endif /* __KERNEL__ */
++
++typedef struct elan3_stats
++{
++    u_long    Version;                                        /* version field */
++    u_long    Interrupts;                                     /* count of elan interrupts */
++    u_long    TlbFlushes;                                     /* count of tlb flushes */
++    u_long    InvalidContext;                                 /* count of traps with invalid context */
++    u_long    ComQueueHalfFull;                               /* count of interrupts due to com queue being half full */
++
++    u_long    CProcTraps;                                     /* count of cproc traps */
++    u_long    DProcTraps;                                     /* count of dproc traps */
++    u_long    TProcTraps;                                     /* cound of tproc traps */
++    u_long    IProcTraps;                                     /* count of iproc traps */
++    u_long    EventInterrupts;                                /* count of event interrupts */
++
++    u_long    PageFaults;                                     /* count of elan page faults */
++
++    /* inputter related */
++    u_long    EopBadAcks;                                     /* count of EOP_BAD_ACKs */
++    u_long    EopResets;                                      /* count of EOP_ERROR_RESET */
++    u_long      InputterBadLength;                            /* count of BadLength */
++    u_long      InputterCRCDiscards;                          /* count of CRC_STATUS_DISCARD */
++    u_long      InputterCRCErrors;                            /* count of CRC_STATUS_ERROR */
++    u_long      InputterCRCBad;                                       /* count of CRC_STATUS_BAD */
++    u_long    DmaNetworkErrors;                               /* count of errors in dma data */
++    u_long    DmaIdentifyNetworkErrors;                       /* count of errors after dma identify */
++    u_long    ThreadIdentifyNetworkErrors;                    /* count of errors after thread identify */
++
++    /* dma related */
++    u_long    DmaRetries;                                     /* count of dma retries (due to retry fail count) */    
++    u_long    DmaOutputTimeouts;                              /* count of dma output timeouts */
++    u_long    DmaPacketAckErrors;                             /* count of dma packet ack errors */
++
++    /* thread related */
++    u_long    ForcedTProcTraps;                               /* count of forced tproc traps */
++    u_long    TrapForTooManyInsts;                            /* count of too many instruction traps */
++    u_long    ThreadOutputTimeouts;                           /* count of thread output timeouts */
++    u_long       ThreadPacketAckErrors;                               /* count of thread packet ack errors */
++
++    /* link related */
++    u_long    LockError;                                      /* count of RegPtr->Exts.LinkErrorTypes:LS_LockError */
++    u_long    DeskewError;                                    /* count of RegPtr->Exts.LinkErrorTypes:LS_DeskewError */
++    u_long    PhaseError;                                     /* count of RegPtr->Exts.LinkErrorTypes:LS_PhaseError */
++    u_long    DataError;                                      /* count of RegPtr->Exts.LinkErrorTypes:LS_DataError */
++    u_long    FifoOvFlow0;                                    /* count of RegPtr->Exts.LinkErrorTypes:LS_FifoOvFlow0 */
++    u_long    FifoOvFlow1;                                    /* count of RegPtr->Exts.LinkErrorTypes:LS_FifoOvFlow1 */
++    u_long    LinkErrorValue;                                 /* link error value on data error */
++
++    /* memory related */
++    u_long    CorrectableErrors;                              /* count of correctable ecc errors */
++    u_long    UncorrectableErrors;                            /* count of uncorrectable ecc errors */
++    u_long       MultipleErrors;                                      /* count of multiple ecc errors */
++    u_long    SdramBytesFree;                                 /* count of sdram bytes free */
++    
++    /* Interrupt related */
++    u_long    LongestInterrupt;                               /* length of longest interrupt in ticks */
++
++    u_long    EventPunts;                                     /* count of punts of event interrupts to thread */
++    u_long    EventRescheds;                                  /* count of reschedules of event interrupt thread */
++} ELAN3_STATS;
++
++#define ELAN3_STATS_VERSION   (ulong)2
++#define ELAN3_NUM_STATS               (sizeof (ELAN3_STATS)/sizeof (u_long))
++
++#define ELAN3_STATS_DEV_FMT   "elan3_stats_dev_%d"
++
++#ifdef __KERNEL__
++
++#define BumpStat(dev,stat)    ((dev)->Stats.stat++)
++
++typedef struct elan3_level_ptbl_block
++{
++    spinlock_t                    PtblLock;                           /* Page table freelist lock */
++    int                           PtblTotal;                          /* Count of level N page tables allocated */
++    int                           PtblFreeCount;                      /* Count of free level N page tables */
++    struct elan3_ptbl    *PtblFreeList;                       /* Free level N page tables */
++    struct elan3_ptbl_gr         *PtblGroupList;                      /* List of Groups of level N page tables */
++} ELAN3_LEVEL_PTBL_BLOCK;
++ 
++typedef struct elan3_dev
++{
++    ELAN3_DEV_OSDEP       Osdep;                              /* OS specific entries */
++    int                           Instance;                           /* Device number */
++    ELAN_DEVINFO            Devinfo;                          
++    ELAN_POSITION         Position;                           /* position in switch network (for user code) */
++    ELAN_DEV_IDX          DeviceIdx;                          /* device index registered with elanmod */
++
++    int                           ThreadsShouldStop;                  /* flag that kernel threads should stop */
++
++    spinlock_t                    IntrLock;
++    spinlock_t                    TlbLock;
++    spinlock_t                    CProcLock;
++    kcondvar_t                    IntrWait;                           /* place event interrupt thread sleeps */
++    unsigned              EventInterruptThreadStarted:1;      /* event interrupt thread started */
++    unsigned              EventInterruptThreadStopped:1;      /* event interrupt thread stopped */
++    
++    DeviceMappingHandle           RegHandle;                          /* DDI Handle */
++    ioaddr_t              RegPtr;                             /* Elan Registers */
++
++    volatile E3_uint32            InterruptMask;                      /* copy of RegPtr->InterruptMask */
++    volatile E3_uint32            Event_Int_Queue_FPtr;               /* copy of RegPtr->Event_Int_Queue_FPtr */
++    volatile E3_uint32      SchCntReg;                                /* copy of RegPtr->SchCntReg */
++    volatile E3_uint32      Cache_Control_Reg;                        /* true value for RegPtr->Cache_Control_Reg */
++    
++    ELAN3_SDRAM_BANK      SdramBanks[ELAN3_SDRAM_NUM_BANKS];  /* Elan sdram banks */
++    spinlock_t                    SdramLock;                          /* Sdram allocator */
++    sdramaddr_t                   SdramFreeLists[SDRAM_NUM_FREE_LISTS];
++    unsigned              SdramFreeCounts[SDRAM_NUM_FREE_LISTS];
++              
++    sdramaddr_t                   TAndQBase;                          /* Trap and Queue area */
++    sdramaddr_t                   ContextTable;                       /* Elan Context Table */
++    u_int                 ContextTableSize;                   /* # entries in context table */
++
++    struct elan3_ctxt      **CtxtTable;                         /* array of ctxt pointers or nulls */
++
++    sdramaddr_t                   CommandPortTraps[2];                /* Command port trap overflow */
++    int                           CurrentCommandPortTrap;             /* Which overflow queue we're using */
++    
++    u_int                 HaltAllCount;                       /* Count of reasons to halt context 0 queues */
++    u_int                 HaltNonContext0Count;               /* Count of reasons to halt non-context 0 queues */
++    u_int                 HaltDmaDequeueCount;                /* Count of reasons to halt dma from dequeuing */
++    u_int                 HaltThreadCount;                    /* Count of reasons to halt the thread processor */
++    u_int                 FlushCommandCount;                  /* Count of reasons to flush command queues */
++    u_int                 DiscardAllCount;                    /* Count of reasons to discard context 0 */
++    u_int                 DiscardNonContext0Count;            /* Count of reasons to discard non context 0 */
++
++    struct thread_trap           *ThreadTrap;                         /* Thread Processor trap space */
++    struct dma_trap      *DmaTrap;                            /* DMA Processor trap space */
++
++    spinlock_t                    FreeHaltLock;                       /* Lock for haltop free list */
++    ELAN3_HALTOP                 *FreeHaltOperations;                 /* Free list of haltops */
++    u_int                 NumHaltOperations;                  /* Number of haltops allocated */
++    u_int                 ReservedHaltOperations;             /* Number of haltops reserved */
++
++    ELAN3_HALTOP                 *HaltOperations;                     /* List of operations to call */
++    ELAN3_HALTOP                **HaltOperationsTailpp;               /* Pointer to last "next" pointer in list */
++    E3_uint32             HaltOperationsMask;                 /* Or of all bits in list of operations */
++
++    physaddr_t                    SdramPhysBase;                      /* Physical address of SDRAM */
++    physaddr_t                    SdramPhysMask;                      /* and mask of significant bits */ 
++    
++    physaddr_t                    PciPhysBase;                        /* physical address of local PCI segment */
++    physaddr_t                    PciPhysMask;                        /* and mask of significant bits */
++
++    long                  ErrorTime;                          /* lbolt at last error (link,ecc etc) */
++    long                  ErrorsPerTick;                      /* count of errors for this tick */
++    timer_fn_t                    ErrorTimeoutId;                     /* id of timeout when errors masked out */
++    timer_fn_t                    DmaPollTimeoutId;                   /* id of timeout to poll for "bad" dmas */
++    int                           FilterHaltQueued;
++
++    /*
++     * HAT layer specific entries.
++     */
++    ELAN3_LEVEL_PTBL_BLOCK   Level[4];
++    spinlock_t                    PtblGroupLock;                      /* Lock for Page Table group lists */
++    struct elan3_ptbl_gr    *Level3PtblGroupHand;             /* Hand for ptbl stealing */
++
++    /*
++     * Per-Context Information structures.
++     */
++    struct elan3_info    *Infos;                              /* List of "infos" for this device */
++
++    char                    LinkShutdown;                       /* link forced into reset by panic/shutdown/dump */
++
++    /*
++     * Device statistics.
++     */
++    ELAN3_STATS                   Stats;
++    ELAN_STATS_IDX          StatsIndex;
++
++    struct {
++      E3_Regs            *RegPtr;
++      char               *Sdram[ELAN3_SDRAM_NUM_BANKS];
++    } PanicState;
++} ELAN3_DEV;
++
++#define ELAN3_DEV_CTX_TABLE(dev,ctxtn) ( (dev)->CtxtTable[ (ctxtn) &  MAX_ROOT_CONTEXT_MASK] )
++
++/* macros for accessing dev->RegPtr.Tags/Sets. */
++#define write_cache_tag(dev,what,val) writeq (val, dev->RegPtr + offsetof (E3_Regs, Tags.what))
++#define read_cache_tag(dev,what)      readq (dev->RegPtr + offsetof (E3_Regs, Tags.what))
++#define write_cache_set(dev,what,val) writeq (val, dev->RegPtr + offsetof (E3_Regs, Sets.what))
++#define read_cache_set(dev,what)      readq (dev->RegPtr + offsetof (E3_Regs, Sets.what))
++
++/* macros for accessing dev->RegPtr.Regs. */
++#define write_reg64(dev,what,val)     writeq (val, dev->RegPtr + offsetof (E3_Regs, Regs.what))
++#define write_reg32(dev,what,val)     writel (val, dev->RegPtr + offsetof (E3_Regs, Regs.what))
++#define read_reg64(dev,what)          readq (dev->RegPtr + offsetof (E3_Regs, Regs.what))
++#define read_reg32(dev,what)          readl (dev->RegPtr + offsetof (E3_Regs, Regs.what))
++
++/* macros for accessing dev->RegPtr.uRegs. */
++#define write_ureg64(dev,what,val)    writeq (val, dev->RegPtr + offsetof (E3_Regs, URegs.what))
++#define write_ureg32(dev,what,val)    writel (val, dev->RegPtr + offsetof (E3_Regs, URegs.what))
++#define read_ureg64(dev,what)         readq (dev->RegPtr + offsetof (E3_Regs, URegs.what))
++#define read_ureg32(dev,what)         readl (dev->RegPtr + offsetof (E3_Regs, URegs.what))
++
++/* macros for accessing dma descriptor/thread regs */
++#define copy_dma_regs(dev, desc) \
++MACRO_BEGIN \
++    register int i;  \
++    for (i = 0; i < sizeof (E3_DMA)/sizeof(E3_uint64); i++) \
++      ((E3_uint64 *) desc)[i] = readq (dev->RegPtr + offsetof (E3_Regs, Regs.Dma_Desc) + i*sizeof (E3_uint64)); \
++MACRO_END
++
++#define copy_thread_regs(dev, regs) \
++MACRO_BEGIN \
++    register int i;  \
++    for (i = 0; i < (32*sizeof (E3_uint32))/sizeof(E3_uint64); i++) \
++      ((E3_uint64 *) regs)[i] = readq (dev->RegPtr + offsetof (E3_Regs, Regs.Globals[0]) + i*sizeof (E3_uint64)); \
++MACRO_END
++
++_NOTE(MUTEX_PROTECTS_DATA(elan3_dev::IntrLock, 
++                        _E3_DataBusMap::Exts _E3_DataBusMap::Input_Context_Fil_Flush
++                        elan3_dev::CurrentCommandPortTrap elan3_dev::HaltAllCount elan3_dev::HaltDmaDequeueCount
++                        elan3_dev::FlushCommandCount elan3_dev::DiscardAllCount elan3_dev::DiscardNonContext0Count
++                        elan3_dev::HaltOperations elan3_dev::HaltOperationsMask))
++_NOTE(MUTEX_PROTECTS_DATA(elan3_dev::TlbLock, 
++                        _E3_DataBusMap::Cache_Control_Reg))
++_NOTE(MUTEX_PROTECTS_DATA(elan3_dev::InfoLock, 
++                        elan3_dev::Infos elan3_dev::InfoTable))
++_NOTE(MUTEX_PROTECTS_DATA(elan3_dev::FreeHaltLock, 
++                        elan3_dev::FreeHaltOperations elan3_dev::NumHaltOperations elan3_dev::ReservedHaltOperations))
++_NOTE(MUTEX_PROTECTS_DATA(elan3_dev::PageFreeListLock, 
++                        elan3_dev::PageFreeList elan3_dev::PageFreeListSize))
++_NOTE(MUTEX_PROTECTS_DATA(elan3_dev::Level1PtblLock,
++                        elan3_dev::Level1PtblTotal elan3_dev::Level1PtblFreeCount elan3_dev::Level1PtblFreeList))
++_NOTE(MUTEX_PROTECTS_DATA(elan3_dev::Level2PtblLock,
++                        elan3_dev::Level2PtblTotal elan3_dev::Level2PtblFreeCount elan3_dev::Level2PtblFreeList))
++_NOTE(MUTEX_PROTECTS_DATA(elan3_dev::Level3PtblLock,
++                        elan3_dev::Level3PtblTotal elan3_dev::Level3PtblFreeCount elan3_dev::Level3PtblFreeList))
++_NOTE(MUTEX_PROTECTS_DATA(elan3_dev::PtblGroupLock,
++                        elan3_dev::Level1PtblGroupList elan3_dev::Level2PtblGroupList elan3_dev::Level3PtblGroupList))
++
++_NOTE(DATA_READABLE_WITHOUT_LOCK(elan3_dev::InfoTable elan3_dev::Level1PtblFreeList
++                               elan3_dev::Level2PtblFreeList elan3_dev::Level3PtblFreeList))
++
++_NOTE(LOCK_ORDER(elan3_dev::InfoLock elan3_dev::IntrLock))
++_NOTE(LOCK_ORDER(as::a_lock elan3_dev::InfoLock))
++_NOTE(LOCK_ORDER(as::a_lock elan3_dev::IntrLock))
++
++#define SET_INT_MASK(dev,Mask)                MACRO_BEGIN write_reg32 (dev, Exts.InterruptMask, ((dev)->InterruptMask = (Mask)));  mmiob(); MACRO_END
++#define ENABLE_INT_MASK(dev, bits)    MACRO_BEGIN write_reg32 (dev, Exts.InterruptMask, ((dev->InterruptMask |= (bits)))); mmiob(); MACRO_END
++#define DISABLE_INT_MASK(dev, bits)   MACRO_BEGIN write_reg32 (dev, Exts.InterruptMask, ((dev->InterruptMask &= ~(bits)))); mmiob(); MACRO_END
++
++#define INIT_SCHED_STATUS(dev, val) \
++MACRO_BEGIN \
++      (dev)->SchCntReg = (val); \
++      write_reg32 (dev, Exts.SchCntReg, (dev)->SchCntReg); \
++      mmiob(); \
++MACRO_END
++
++#define SET_SCHED_STATUS(dev, val) \
++MACRO_BEGIN \
++      ASSERT (((val) & HaltStopAndExtTestMask) == (val)); \
++      (dev)->SchCntReg |= (val); \
++      write_reg32 (dev, Exts.SchCntReg, (dev)->SchCntReg); \
++        mmiob (); \
++MACRO_END
++
++#define CLEAR_SCHED_STATUS(dev, val) \
++MACRO_BEGIN \
++      ASSERT (((val) & HaltStopAndExtTestMask) == (val)); \
++      (dev)->SchCntReg &= ~(val); \
++      write_reg32 (dev, Exts.SchCntReg, (dev)->SchCntReg); \
++        mmiob(); \
++MACRO_END
++
++#define MODIFY_SCHED_STATUS(dev, SetBits, ClearBits) \
++MACRO_BEGIN \
++      ASSERT ((((SetBits)|(ClearBits)) & HaltStopAndExtTestMask) == ((SetBits)|(ClearBits))); \
++      (dev)->SchCntReg = (((dev)->SchCntReg | (SetBits)) & ~(ClearBits)); \
++      write_reg32 (dev, Exts.SchCntReg, (dev)->SchCntReg); \
++      mmiob(); \
++MACRO_END
++
++#define PULSE_SCHED_STATUS(dev, RestartBits) \
++MACRO_BEGIN \
++      ASSERT (((RestartBits) & HaltStopAndExtTestMask) == 0); \
++      write_reg32 (dev, Exts.SchCntReg, (dev)->SchCntReg | (RestartBits)); \
++      mmiob(); \
++MACRO_END
++
++#define SET_SCHED_LINK_VALUE(dev, enabled, val) \
++MACRO_BEGIN \
++      (dev)->SchCntReg = (((dev)->SchCntReg & HaltAndStopMask) | ((enabled) ? LinkBoundaryScan : 0) | LinkSetValue(val, 0)); \
++      write_reg32 (dev, Exts.SchCntReg, (dev)->SchCntReg); \
++      mmiob(); \
++MACRO_END
++
++#ifdef DEBUG_ASSERT
++#  define ELAN3_ASSERT(dev, EX)       ((void)((EX) || elan3_assfail(dev, #EX, __FILE__, __LINE__)))
++#else
++#  define ELAN3_ASSERT(dev, EX)
++#endif
++
++/* elandev_generic.c */
++extern int        InitialiseElan (ELAN3_DEV *dev, ioaddr_t CmdPort);
++extern void       FinaliseElan (ELAN3_DEV *dev);
++extern int        InterruptHandler (ELAN3_DEV *dev);
++extern void       PollForDmaHungup (void *arg);
++
++extern int        SetLinkBoundaryScan (ELAN3_DEV *dev);
++extern void       ClearLinkBoundaryScan (ELAN3_DEV *dev);
++extern int        WriteBoundaryScanValue (ELAN3_DEV *dev, int value);
++extern int        ReadBoundaryScanValue(ELAN3_DEV *dev, int link);
++
++extern int        ReadVitalProductData (ELAN3_DEV *dev, int *CasLatency);
++
++extern struct elan3_ptbl_gr *ElanGetPtblGr (ELAN3_DEV *dev, sdramaddr_t offset);
++extern void       ElanSetPtblGr (ELAN3_DEV *dev, sdramaddr_t offset, struct elan3_ptbl_gr *ptg);
++
++extern void       ElanFlushTlb (ELAN3_DEV *dev);
++
++extern void       SetSchedStatusRegister (ELAN3_DEV *dev, E3_uint32 Pend, volatile E3_uint32 *Maskp);
++extern void     FreeHaltOperation (ELAN3_DEV *dev, ELAN3_HALTOP *op);
++extern int      ReserveHaltOperations (ELAN3_DEV *dev, int count, int cansleep);
++extern void     ReleaseHaltOperations (ELAN3_DEV *dev, int count);
++extern void     ProcessHaltOperations (ELAN3_DEV *dev, E3_uint32 Pend);
++extern void     QueueHaltOperation (ELAN3_DEV *dev, E3_uint32 Pend, volatile E3_uint32 *Maskp,
++                                    E3_uint32 ReqMask, void (*Function)(ELAN3_DEV *, void *), void *Arguement);
++
++extern int        ComputePosition (ELAN_POSITION *pos, unsigned NodeId, unsigned NumNodes, unsigned numDownLinksVal);
++
++extern caddr_t          MiToName (int mi);
++extern void     ElanBusError (ELAN3_DEV *dev);
++
++extern void     TriggerLsa (ELAN3_DEV *dev);
++
++extern ELAN3_DEV  *elan3_device (int instance);
++extern int      DeviceRegisterSize (ELAN3_DEV *dev, int rnumber, int *sizep);
++extern int      MapDeviceRegister (ELAN3_DEV *dev, int rnumber, ioaddr_t *addrp, int offset, 
++                                   int len, DeviceMappingHandle *handlep);
++extern void       UnmapDeviceRegister (ELAN3_DEV *dev, DeviceMappingHandle *handlep);
++
++
++/* sdram.c */
++/* sdram accessing functions - define 4 different types for 8,16,32,64 bit accesses */
++extern unsigned char      elan3_sdram_readb (ELAN3_DEV *dev, sdramaddr_t ptr);
++extern unsigned short     elan3_sdram_readw (ELAN3_DEV *dev, sdramaddr_t ptr);
++extern unsigned int       elan3_sdram_readl (ELAN3_DEV *dev, sdramaddr_t ptr);
++extern unsigned long long elan3_sdram_readq (ELAN3_DEV *dev, sdramaddr_t ptr);
++extern void               elan3_sdram_writeb (ELAN3_DEV *dev, sdramaddr_t ptr, unsigned char val);
++extern void               elan3_sdram_writew (ELAN3_DEV *dev, sdramaddr_t ptr, unsigned short val);
++extern void               elan3_sdram_writel (ELAN3_DEV *dev, sdramaddr_t ptr, unsigned int val);
++extern void               elan3_sdram_writeq (ELAN3_DEV *dev, sdramaddr_t ptr, unsigned long long val);
++
++extern void             elan3_sdram_zerob_sdram (ELAN3_DEV *dev, sdramaddr_t ptr, int nbytes);
++extern void             elan3_sdram_zerow_sdram (ELAN3_DEV *dev, sdramaddr_t ptr, int nbytes);
++extern void             elan3_sdram_zerol_sdram (ELAN3_DEV *dev, sdramaddr_t ptr, int nbytes);
++extern void             elan3_sdram_zeroq_sdram (ELAN3_DEV *dev, sdramaddr_t ptr, int nbytes);
++
++extern void               elan3_sdram_copyb_from_sdram (ELAN3_DEV *dev, sdramaddr_t from, void *to, int nbytes);
++extern void               elan3_sdram_copyw_from_sdram (ELAN3_DEV *dev, sdramaddr_t from, void *to, int nbytes);
++extern void               elan3_sdram_copyl_from_sdram (ELAN3_DEV *dev, sdramaddr_t from, void *to, int nbytes);
++extern void               elan3_sdram_copyq_from_sdram (ELAN3_DEV *dev, sdramaddr_t from, void *to, int nbytes);
++extern void               elan3_sdram_copyb_to_sdram (ELAN3_DEV *dev, void *from, sdramaddr_t to, int nbytes);
++extern void               elan3_sdram_copyw_to_sdram (ELAN3_DEV *dev, void *from, sdramaddr_t to, int nbytes);
++extern void               elan3_sdram_copyl_to_sdram (ELAN3_DEV *dev, void *from, sdramaddr_t to, int nbytes);
++extern void               elan3_sdram_copyq_to_sdram (ELAN3_DEV *dev, void *from, sdramaddr_t to, int nbytes);
++
++extern void             elan3_sdram_init (ELAN3_DEV *dev);
++extern void               elan3_sdram_fini (ELAN3_DEV *dev);
++extern void             elan3_sdram_add (ELAN3_DEV *dev, sdramaddr_t base, sdramaddr_t top);
++extern sdramaddr_t        elan3_sdram_alloc (ELAN3_DEV *dev, int nbytes);
++extern void               elan3_sdram_free (ELAN3_DEV *dev, sdramaddr_t ptr, int nbytes);
++extern physaddr_t         elan3_sdram_to_phys (ELAN3_DEV *dev, sdramaddr_t addr);
++
++/* cproc.c */
++extern void     HandleCProcTrap (ELAN3_DEV *dev, E3_uint32 Pend, E3_uint32 *Mask);
++
++/* iproc.c */
++extern void     HandleIProcTrap (ELAN3_DEV *dev, int Channel, E3_uint32 Pend, sdramaddr_t FaultSaveOff, 
++                                 sdramaddr_t TransactionsOff, sdramaddr_t DataOff);
++
++/* tproc.c */
++extern int      HandleTProcTrap (ELAN3_DEV *dev, E3_uint32 *RestartBits);
++extern void     DeliverTProcTrap (ELAN3_DEV *dev, struct thread_trap *threadTrap, E3_uint32 Pend);
++
++/* dproc.c */
++extern int      HandleDProcTrap (ELAN3_DEV *dev, E3_uint32 *RestartBits);
++extern void     DeliverDProcTrap (ELAN3_DEV *dev, struct dma_trap *dmaTrap, E3_uint32 Pend);
++
++#if defined(LINUX)
++/* procfs_linux.h */
++extern struct proc_dir_entry *elan3_procfs_root;
++extern struct proc_dir_entry *elan3_config_root;
++
++extern void elan3_procfs_init(void);
++extern void elan3_procfs_fini(void);
++extern void elan3_procfs_device_init (ELAN3_DEV *dev);
++extern void elan3_procfs_device_fini (ELAN3_DEV *dev);
++#endif /* defined(LINUX) */
++
++/* elan3_osdep.c */
++extern int        BackToBackMaster;
++extern int        BackToBackSlave;
++
++#define ELAN_REG_REC_MAX (100)
++#define ELAN_REG_REC(REG)  {                                         \
++elan_reg_rec_file [elan_reg_rec_index] = __FILE__;                   \
++elan_reg_rec_line [elan_reg_rec_index] = __LINE__;                   \
++elan_reg_rec_reg  [elan_reg_rec_index] = REG;                        \
++elan_reg_rec_cpu  [elan_reg_rec_index] = smp_processor_id();         \
++elan_reg_rec_lbolt[elan_reg_rec_index] = lbolt;                      \
++elan_reg_rec_index = ((elan_reg_rec_index+1) % ELAN_REG_REC_MAX);}
++
++extern char *    elan_reg_rec_file [ELAN_REG_REC_MAX];
++extern int       elan_reg_rec_line [ELAN_REG_REC_MAX];
++extern long      elan_reg_rec_lbolt[ELAN_REG_REC_MAX];
++extern int       elan_reg_rec_cpu  [ELAN_REG_REC_MAX];
++extern E3_uint32 elan_reg_rec_reg  [ELAN_REG_REC_MAX];
++extern int       elan_reg_rec_index;
++ 
++#endif /* __KERNEL__ */
++
++
++#define ELAN3_PROCFS_ROOT          "/proc/qsnet/elan3"
++#define ELAN3_PROCFS_VERSION       "/proc/qsnet/elan3/version"
++#define ELAN3_PROCFS_DEBUG         "/proc/qsnet/elan3/config/elandebug"
++#define ELAN3_PROCFS_DEBUG_CONSOLE "/proc/qsnet/elan3/config/elandebug_console"
++#define ELAN3_PROCFS_DEBUG_BUFFER  "/proc/qsnet/elan3/config/elandebug_buffer"
++#define ELAN3_PROCFS_MMU_DEBUG     "/proc/qsnet/elan3/config/elan3mmu_debug"
++#define ELAN3_PROCFS_PUNT_LOOPS    "/proc/qsnet/elan3/config/eventint_punt_loops"
++
++#define ELAN3_PROCFS_DEVICE_STATS_FMT    "/proc/qsnet/elan3/device%d/stats"
++#define ELAN3_PROCFS_DEVICE_POSITION_FMT "/proc/qsnet/elan3/device%d/position"
++#define ELAN3_PROCFS_DEVICE_NODESET_FMT  "/proc/qsnet/elan3/device%d/nodeset"
++
++#endif /* __ELAN3_ELANDEV_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/include/elan3/elandev_linux.h
+===================================================================
+--- linux-2.4.21.orig/include/elan3/elandev_linux.h    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan3/elandev_linux.h 2005-06-01 23:12:54.721420560 -0400
+@@ -0,0 +1,56 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELANDEV_LINUX_H
++#define __ELANDEV_LINUX_H
++
++#ident "$Id: elandev_linux.h,v 1.11 2003/09/24 13:57:24 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/elandev_linux.h,v $*/
++
++#ifdef __KERNEL__
++#include <linux/mm.h>
++#include <linux/sched.h>
++#include <linux/pci.h>
++#endif
++
++#define ELAN3_MAJOR              60
++#define ELAN3_NAME               "elan3"
++#define ELAN3_MAX_CONTROLLER     16                      /* limited to 4 bits */
++ 
++#define ELAN3_MINOR_DEVNUM(m)    ((m) & 0x0f)            /* card number */
++#define ELAN3_MINOR_DEVFUN(m)    (((m) >> 4) & 0x0f)     /* function */
++#define ELAN3_MINOR_CONTROL      0                       /* function values */
++#define ELAN3_MINOR_MEM          1
++#define ELAN3_MINOR_USER              2
++ 
++typedef void                  *DeviceMappingHandle;
++
++/* task and ctxt handle types */
++typedef struct mm_struct      *TaskHandle;
++typedef int                   CtxtHandle;
++ 
++#define ELAN3_MY_TASK_HANDLE()        (current->mm)
++#define KERNEL_TASK_HANDLE()  (get_kern_mm())
++ 
++/*
++ * OS-dependent component of ELAN3_DEV struct.
++ */
++typedef struct elan3_dev_osdep
++{
++      struct pci_dev  *pci;                   /* PCI config data */
++      int             ControlDeviceOpen;      /* flag to indicate control */
++                                              /*   device open */
++       struct proc_dir_entry *procdir;
++} ELAN3_DEV_OSDEP;
++
++#endif /* __ELANDEV_LINUX_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/include/elan3/elanio.h
+===================================================================
+--- linux-2.4.21.orig/include/elan3/elanio.h   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan3/elanio.h        2005-06-01 23:12:54.722420408 -0400
+@@ -0,0 +1,226 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN3_ELAN3IO_H
++#define __ELAN3_ELAN3IO_H
++
++#ident "$Id: elanio.h,v 1.19 2003/12/08 15:40:26 mike Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/elanio.h,v $*/
++
++#define ELAN3IO_CONTROL_PATHNAME      "/dev/elan3/control%d"
++#define ELAN3IO_MEM_PATHNAME  "/dev/elan3/mem%d"
++#define ELAN3IO_USER_PATHNAME "/dev/elan3/user%d"
++#define ELAN3IO_SDRAM_PATHNAME   "/dev/elan3/sdram%d"
++#define ELAN3IO_MAX_PATHNAMELEN       32
++
++/* ioctls on /dev/elan3/control */
++#define ELAN3IO_CONTROL_BASE          0
++
++#define ELAN3IO_SET_BOUNDARY_SCAN     _IO   ('e', ELAN3IO_CONTROL_BASE + 0)
++#define ELAN3IO_CLEAR_BOUNDARY_SCAN   _IO   ('e', ELAN3IO_CONTROL_BASE + 1)
++#define ELAN3IO_READ_LINKVAL          _IOWR ('e', ELAN3IO_CONTROL_BASE + 2, E3_uint32)
++#define ELAN3IO_WRITE_LINKVAL         _IOWR ('e', ELAN3IO_CONTROL_BASE + 3, E3_uint32)
++
++typedef struct elanio_set_debug_struct
++{
++    char      what[32];
++    u_long    value;
++} ELAN3IO_SET_DEBUG_STRUCT;
++#define ELAN3IO_SET_DEBUG             _IOW  ('e', ELAN3IO_CONTROL_BASE + 4, ELAN3IO_SET_DEBUG_STRUCT)
++
++typedef struct elanio_debug_buffer_struct
++{
++    caddr_t   addr;
++    size_t    len;
++} ELAN3IO_DEBUG_BUFFER_STRUCT;
++#define ELAN3IO_DEBUG_BUFFER          _IOWR ('e', ELAN3IO_CONTROL_BASE + 5, ELAN3IO_DEBUG_BUFFER_STRUCT)
++
++typedef struct elanio_neterr_server_struct
++{
++    u_int             elanid;
++    void             *addr;
++    char             *name;
++} ELAN3IO_NETERR_SERVER_STRUCT;
++#define ELAN3IO_NETERR_SERVER         _IOW  ('e', ELAN3IO_CONTROL_BASE + 6, ELAN3IO_NETERR_SERVER_STRUCT)
++#define ELAN3IO_NETERR_FIXUP          _IOWR ('e', ELAN3IO_CONTROL_BASE + 7, NETERR_MSG)
++
++typedef struct elanio_set_position_struct
++{
++    u_int             device;
++    unsigned short      nodeId;
++    unsigned short      numNodes;
++} ELAN3IO_SET_POSITION_STRUCT;
++#define ELAN3IO_SET_POSITION          _IOW ('e', ELAN3IO_CONTROL_BASE + 8, ELAN3IO_SET_POSITION_STRUCT)
++
++#if defined(LINUX)
++
++/* ioctls on /dev/elan3/sdram */
++#define ELAN3IO_SDRAM_BASE            20
++
++/* ioctls on /dev/elan3/user */
++#define ELAN3IO_USER_BASE             30
++
++#define ELAN3IO_FREE                  _IO  ('e', ELAN3IO_USER_BASE + 0)
++
++#define ELAN3IO_ATTACH                        _IOWR('e', ELAN3IO_USER_BASE + 1, ELAN_CAPABILITY)
++#define ELAN3IO_DETACH                        _IO  ('e', ELAN3IO_USER_BASE + 2)
++
++typedef struct elanio_addvp_struct
++{
++    u_int             process;
++    ELAN_CAPABILITY     capability;
++} ELAN3IO_ADDVP_STRUCT;
++#define ELAN3IO_ADDVP                 _IOWR('e', ELAN3IO_USER_BASE + 3, ELAN3IO_ADDVP_STRUCT)
++#define ELAN3IO_REMOVEVP                      _IOW ('e', ELAN3IO_USER_BASE + 4, int)
++
++typedef struct elanio_bcastvp_struct
++{
++    u_int             process;
++    u_int             lowvp;
++    u_int             highvp;
++} ELAN3IO_BCASTVP_STRUCT;
++#define ELAN3IO_BCASTVP                       _IOW ('e', ELAN3IO_USER_BASE + 5, ELAN3IO_BCASTVP_STRUCT)
++
++typedef struct elanio_loadroute_struct
++{
++    u_int             process;
++    E3_uint16         flits[MAX_FLITS];
++} ELAN3IO_LOAD_ROUTE_STRUCT;
++#define ELAN3IO_LOAD_ROUTE            _IOW ('e', ELAN3IO_USER_BASE + 6, ELAN3IO_LOAD_ROUTE_STRUCT)
++
++#define ELAN3IO_PROCESS                       _IO  ('e', ELAN3IO_USER_BASE + 7)
++
++typedef struct elanio_setperm_struct
++{
++    caddr_t           maddr;
++    E3_Addr           eaddr;
++    size_t            len;
++    int                       perm;
++} ELAN3IO_SETPERM_STRUCT;
++#define ELAN3IO_SETPERM                       _IOW ('e', ELAN3IO_USER_BASE + 8, ELAN3IO_SETPERM_STRUCT)
++
++typedef struct elanio_clearperm_struct
++{
++    E3_Addr           eaddr;
++    size_t            len;
++} ELAN3IO_CLEARPERM_STRUCT;
++#define ELAN3IO_CLEARPERM             _IOW ('e', ELAN3IO_USER_BASE + 9, ELAN3IO_CLEARPERM_STRUCT)
++
++typedef struct elanio_changeperm_struct
++{
++    E3_Addr           eaddr;
++    size_t            len;
++    int                       perm;
++} ELAN3IO_CHANGEPERM_STRUCT;
++#define ELAN3IO_CHANGEPERM            _IOW ('e', ELAN3IO_USER_BASE + 10, ELAN3IO_CHANGEPERM_STRUCT)
++
++
++#define ELAN3IO_HELPER_THREAD         _IO  ('e', ELAN3IO_USER_BASE + 11)
++#define ELAN3IO_WAITCOMMAND           _IO  ('e', ELAN3IO_USER_BASE + 12)
++#define ELAN3IO_BLOCK_INPUTTER                _IOW ('e', ELAN3IO_USER_BASE + 13, int)
++#define ELAN3IO_SET_FLAGS             _IOW ('e', ELAN3IO_USER_BASE + 14, int)
++
++#define ELAN3IO_WAITEVENT             _IOW ('e', ELAN3IO_USER_BASE + 15, E3_Event)
++#define ELAN3IO_ALLOC_EVENTCOOKIE     _IOW ('e', ELAN3IO_USER_BASE + 16, EVENT_COOKIE)
++#define ELAN3IO_FREE_EVENTCOOKIE              _IOW ('e', ELAN3IO_USER_BASE + 17, EVENT_COOKIE)
++#define ELAN3IO_ARM_EVENTCOOKIE               _IOW ('e', ELAN3IO_USER_BASE + 18, EVENT_COOKIE)
++#define ELAN3IO_WAIT_EVENTCOOKIE              _IOW ('e', ELAN3IO_USER_BASE + 19, EVENT_COOKIE)
++
++#define ELAN3IO_SWAPSPACE             _IOW ('e', ELAN3IO_USER_BASE + 20, SYS_SWAP_SPACE)
++#define ELAN3IO_EXCEPTION_SPACE               _IOW ('e', ELAN3IO_USER_BASE + 21, SYS_EXCEPTION_SPACE)
++#define ELAN3IO_GET_EXCEPTION         _IOR ('e', ELAN3IO_USER_BASE + 22, SYS_EXCEPTION)
++
++typedef struct elanio_unload_struct
++{
++    void      *addr;
++    size_t     len;
++} ELAN3IO_UNLOAD_STRUCT;
++#define ELAN3IO_UNLOAD                        _IOW ('e', ELAN3IO_USER_BASE + 23, ELAN3IO_UNLOAD_STRUCT)
++
++
++
++typedef struct elanio_getroute_struct
++{
++    u_int             process;
++    E3_uint16         flits[MAX_FLITS];
++} ELAN3IO_GET_ROUTE_STRUCT;
++#define ELAN3IO_GET_ROUTE             _IOW ('e', ELAN3IO_USER_BASE + 24, ELAN3IO_GET_ROUTE_STRUCT)
++
++typedef struct elanio_resetroute_struct
++{
++    u_int             process;
++} ELAN3IO_RESET_ROUTE_STRUCT;
++#define ELAN3IO_RESET_ROUTE           _IOW ('e', ELAN3IO_USER_BASE + 25, ELAN3IO_RESET_ROUTE_STRUCT)
++
++typedef struct elanio_checkroute_struct
++{
++    u_int             process;
++    E3_uint32           routeError;
++    E3_uint16         flits[MAX_FLITS];
++} ELAN3IO_CHECK_ROUTE_STRUCT;
++#define ELAN3IO_CHECK_ROUTE           _IOW ('e', ELAN3IO_USER_BASE + 26, ELAN3IO_CHECK_ROUTE_STRUCT)
++
++typedef struct elanio_vp2nodeId_struct
++{
++    u_int             process;
++    unsigned short      nodeId;
++    ELAN_CAPABILITY   cap;
++} ELAN3IO_VP2NODEID_STRUCT;
++#define ELAN3IO_VP2NODEID     _IOWR('e', ELAN3IO_USER_BASE + 27, ELAN3IO_VP2NODEID_STRUCT)
++
++#define ELAN3IO_SET_SIGNAL    _IOW ('e', ELAN3IO_USER_BASE + 28, int)
++
++typedef struct elanio_process_2_location_struct
++{
++    u_int             process;
++    ELAN_LOCATION       loc;
++} ELAN3IO_PROCESS_2_LOCATION_STRUCT;
++#define ELAN3IO_PROCESS_2_LOCATION    _IOW ('e', ELAN3IO_USER_BASE + 29, ELAN3IO_PROCESS_2_LOCATION_STRUCT)
++
++
++
++/* ioctls on all device */
++#define ELAN3IO_GENERIC_BASE          100
++typedef struct elanio_get_devinfo_struct
++{
++    ELAN_DEVINFO *devinfo;
++} ELAN3IO_GET_DEVINFO_STRUCT;
++#define ELAN3IO_GET_DEVINFO           _IOR ('e', ELAN3IO_GENERIC_BASE + 0, ELAN_DEVINFO)
++
++typedef struct elanio_get_position_struct
++{
++    ELAN_POSITION *position;
++} ELAN3IO_GET_POSITION_STRUCT;
++#define ELAN3IO_GET_POSITION             _IOR ('e', ELAN3IO_GENERIC_BASE + 1, ELAN_POSITION)
++
++typedef struct elanio_stats_struct
++{
++    int               which;
++    void       *ptr;
++} ELAN3IO_STATS_STRUCT;
++#define ELAN3IO_STATS                 _IOR ('e', ELAN3IO_GENERIC_BASE + 2, ELAN3IO_STATS_STRUCT)
++#  define ELAN3_SYS_STATS_DEVICE      0
++#  define ELAN3_SYS_STATS_MMU         1
++
++/* offsets on /dev/elan3/control */
++
++/* offsets on /dev/elan3/mem */
++
++/* page numbers on /dev/elan3/user */
++#define ELAN3IO_OFF_COMMAND_PAGE              0
++#define ELAN3IO_OFF_FLAG_PAGE         1
++#define ELAN3IO_OFF_UREG_PAGE         2
++
++#endif /* LINUX */
++
++#endif /* __ELAN3_ELAN3IO_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/include/elan3/elanregs.h
+===================================================================
+--- linux-2.4.21.orig/include/elan3/elanregs.h 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan3/elanregs.h      2005-06-01 23:12:54.724420104 -0400
+@@ -0,0 +1,1063 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++/*
++ * Header file for internal slave mapping of the ELAN3 registers
++ */
++
++#ifndef _ELAN3_ELANREGS_H
++#define _ELAN3_ELANREGS_H
++
++#ident "$Id: elanregs.h,v 1.87 2004/04/22 12:27:21 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/elanregs.h,v $*/
++
++#include <elan3/e3types.h>
++#include <elan3/dma.h>
++#include <elan3/elanuregs.h>
++
++#define MAX_ROOT_CONTEXT_MASK 0xfff
++#define SYS_CONTEXT_BIT               0x1000
++#define ALL_CONTEXT_BITS      (MAX_ROOT_CONTEXT_MASK | SYS_CONTEXT_BIT)
++#define ROOT_TAB_OFFSET(Cntxt)        (((Cntxt) & MAX_ROOT_CONTEXT_MASK) << 4)
++#define CLEAR_SYS_BIT(Cntxt)  ((Cntxt) & ~SYS_CONTEXT_BIT)
++
++#define E3_CACHELINE_SIZE     (32)
++#define E3_CACHE_SIZE         (8192)
++
++typedef volatile struct _E3_CacheSets
++{
++   E3_uint64  Set0[256];      /* 2k bytes per set */
++   E3_uint64  Set1[256];      /* 2k bytes per set */
++   E3_uint64  Set2[256];      /* 2k bytes per set */
++   E3_uint64  Set3[256];      /* 2k bytes per set */
++} E3_CacheSets;
++
++typedef union e3_cache_tag
++{
++   E3_uint64  Value;
++   struct {
++#if defined(__LITTLE_ENDIAN__)
++       E3_uint32 pad2:8;              /* Undefined value when read */
++       E3_uint32 LineError:1;         /* A line error has occured */
++       E3_uint32 Modified:1;          /* Cache data is modified */
++       E3_uint32 FillPending:1;               /* Pipelined fill occuring*/
++       E3_uint32 AddrTag27to11:17;    /* Tag address bits 27 to 11 */
++       E3_uint32 pad1:4;              /* Undefined value when read */
++       E3_uint32 pad0;                        /* Undefined value when read */
++#else
++       E3_uint32 pad0;                        /* Undefined value when read */
++       E3_uint32 pad1:4;              /* Undefined value when read */
++       E3_uint32 AddrTag27to11:17;    /* Tag address bits 27 to 11 */
++       E3_uint32 FillPending:1;               /* Pipelined fill occuring*/
++       E3_uint32 Modified:1;          /* Cache data is modified */
++       E3_uint32 LineError:1;         /* A line error has occured */
++       E3_uint32 pad2:8;              /* Undefined value when read */
++#endif
++   } s;
++} E3_CacheTag;
++
++#define E3_NumCacheLines      64
++#define E3_NumCacheSets               4
++
++typedef volatile struct _E3_CacheTags
++{
++   E3_CacheTag Tags[E3_NumCacheLines][E3_NumCacheSets];       /* 2k bytes per set */
++} E3_CacheTags;
++
++typedef union E3_IProcStatus_Reg
++{
++    E3_uint32 Status;
++    struct
++    {
++#if defined(__LITTLE_ENDIAN__)
++      E3_uint32 TrapType:8;           /* iprocs trap ucode address */
++      E3_uint32 SuspendAddr:8;        /* iprocs suspend address */
++      E3_uint32 EopType:2;            /* Type of Eop Received */
++      E3_uint32 QueueingPacket:1;     /* receiving a queueing packet */
++      E3_uint32 AckSent:1;            /* a packet ack has been sent */
++      E3_uint32 Reject:1;             /* a packet nack has been sent */
++      E3_uint32 CrcStatus:2;          /* Crc Status value */
++      E3_uint32 BadLength:1;          /* Eop was received in a bad place */
++      E3_uint32 Chan1:1;              /* This packet received on v chan1 */
++      E3_uint32 First:1;              /* This is the first transaction in the packet */
++      E3_uint32 Last:1;               /* This is the last transaction in the packet */
++      E3_uint32 Unused:2;
++      E3_uint32 WakeupFunction:3;     /* iprocs wakeup function */
++#else
++      E3_uint32 WakeupFunction:3;     /* iprocs wakeup function */
++      E3_uint32 Unused:2;
++      E3_uint32 Last:1;               /* This is the last transaction in the packet */
++      E3_uint32 First:1;              /* This is the first transaction in the packet */
++      E3_uint32 Chan1:1;              /* This packet received on v chan1 */
++      E3_uint32 BadLength:1;          /* Eop was received in a bad place */
++      E3_uint32 CrcStatus:2;          /* Crc Status value */
++      E3_uint32 Reject:1;             /* a packet nack has been sent */
++      E3_uint32 AckSent:1;            /* a packet ack has been sent */
++      E3_uint32 QueueingPacket:1;     /* receiving a queueing packet */
++      E3_uint32 EopType:2;            /* Type of Eop Received */
++      E3_uint32 SuspendAddr:8;        /* iprocs suspend address */
++      E3_uint32 TrapType:8;           /* iprocs trap ucode address */
++#endif
++    } s;
++} E3_IProcStatus_Reg;
++
++#define CRC_STATUS_GOOD    (0 << 21)
++#define CRC_STATUS_DISCARD (1 << 21)
++#define CRC_STATUS_ERROR   (2 << 21)
++#define CRC_STATUS_BAD     (3 << 21)
++
++#define CRC_MASK         (3 << 21)
++
++#define EOP_GOOD         (1 << 16)
++#define EOP_BADACK       (2 << 16)
++#define EOP_ERROR_RESET          (3 << 16)
++
++#define E3_IPS_LastTrans      (1 << 26)
++#define E3_IPS_FirstTrans     (1 << 25)
++#define E3_IPS_VChan1         (1 << 24)
++#define E3_IPS_BadLength      (1 << 23)
++#define E3_IPS_CrcMask                (3 << 21)
++#define E3_IPS_Rejected               (1 << 20)
++#define E3_IPS_AckSent                (1 << 19)
++#define E3_IPS_QueueingPacket (1 << 18)
++#define E3_IPS_EopType                (3 << 16)
++
++typedef union E3_Status_Reg
++{
++    E3_uint32 Status;
++    struct
++    {
++#if defined(__LITTLE_ENDIAN__)
++      E3_uint32 TrapType:8;           /* procs trap ucode address */
++      E3_uint32 SuspendAddr:8;        /* procs suspend address */
++      E3_uint32 Context:13;           /* procs current context */
++      E3_uint32 WakeupFunction:3;     /* procs wakeup function */
++#else
++      E3_uint32 WakeupFunction:3;     /* procs wakeup function */
++      E3_uint32 Context:13;           /* procs current context */
++      E3_uint32 SuspendAddr:8;        /* procs suspend address */
++      E3_uint32 TrapType:8;           /* procs trap ucode address */
++#endif
++    } s;
++} E3_Status_Reg;
++
++/* values for WakeupFunction */
++#define SleepOneTick                  0
++#define WakeupToSendTransOrEop                1
++#define SleepOneTickThenRunnable      2
++#define WakeupNever                   4
++/* extra dma wakeup functions */
++#define WakupeToSendTransOrEop                1
++#define WakeupForPacketAck            3
++#define WakeupToSendTrans             5
++/* extra thread wakup function */
++#define WakeupStopped                 3
++/* extra cproc wakup function */
++#define WakeupSetEvent                        3
++
++#define GET_STATUS_CONTEXT(Ptr)      ((Ptr.Status >> 16) & 0x1fff)
++#define GET_STATUS_SUSPEND_ADDR(Ptr) ((Ptr.Status >> 8) & 0xff)
++#define GET_STATUS_TRAPTYPE(Ptr)     ((E3_uint32)(Ptr.Status & 0xff))
++
++/*
++ * Interrupt register bits
++ */
++#define INT_PciMemErr                 (1<<15) /* Pci memory access error */
++#define INT_SDRamInt                  (1<<14) /* SDRam ECC interrupt */
++#define INT_EventInterrupt            (1<<13) /* Event Interrupt */
++#define INT_LinkError                 (1<<12) /* Link Error */
++#define INT_ComQueue                  (1<<11) /* a comm queue half full */
++#define INT_TProcHalted                       (1<<10) /* Tproc Halted */
++#define INT_DProcHalted                       (1<<9) /* Dmas Halted */
++#define INT_DiscardingNonSysCntx      (1<<8) /* Inputters Discarding Non-SysCntx */
++#define INT_DiscardingSysCntx         (1<<7) /* Inputters Discarding SysCntx */
++#define INT_TProc                     (1<<6) /* tproc interrupt */
++#define INT_CProc                     (1<<5) /* cproc interrupt */
++#define INT_DProc                     (1<<4) /* dproc interrupt */
++#define INT_IProcCh1NonSysCntx                (1<<3) /* iproc non-SysCntx interrupt */
++#define INT_IProcCh1SysCntx           (1<<2) /* iproc SysCntx interrupt */
++#define INT_IProcCh0NonSysCntx                (1<<1) /* iproc non-SysCntx interrupt */
++#define INT_IProcCh0SysCntx           (1<<0) /* iproc SysCntx interrupt */
++
++#define INT_Inputters         (INT_IProcCh0SysCntx | INT_IProcCh0NonSysCntx | INT_IProcCh1SysCntx | INT_IProcCh1NonSysCntx)
++#define INT_Discarding        (INT_DiscardingSysCntx | INT_DiscardingNonSysCntx)
++#define INT_Halted            (INT_DProcHalted | INT_TProcHalted)
++#define INT_ErrorInterrupts   (INT_PciMemErr | INT_SDRamInt | INT_LinkError)
++
++/*
++ * Link state bits.
++ */
++#define LS_LinkNotReady       (1 << 0) /* Link is in reset or recovering from an error */
++#define LS_Locked     (1 << 1) /* Linkinput PLL is locked */
++#define LS_LockError  (1 << 2) /* Linkinput PLL was unable to lock onto the input clock. */
++#define LS_DeskewError        (1 << 3) /* Linkinput was unable to Deskew all the inputs. (Broken wire?) */
++#define LS_PhaseError (1 << 4) /* Linkinput Phase alignment error. */
++#define LS_DataError  (1 << 5) /* Received value was neither good data or a token. */
++#define LS_FifoOvFlow0        (1 << 6) /* Channel 0 input fifo overflowed. */
++#define LS_FifoOvFlow1        (1 << 7) /* Channel 1 input fifo overflowed. */
++
++/*
++ * Link State Constant defines, used for writing to LinkSetValue
++ */
++
++#define LRS_DataDel0          0x0
++#define LRS_DataDel1          0x1
++#define LRS_DataDel2          0x2
++#define LRS_DataDel3          0x3
++#define LRS_DataDel4          0x4
++#define LRS_DataDel5          0x5
++#define LRS_DataDel6          0x6
++#define LRS_DataDel7          0x7
++#define LRS_DataDel8          0x8
++#define LRS_PllDelValue               0x9
++#define LRS_ClockEven         0xA
++#define LRS_ClockOdd          0xB
++#define LRS_ErrorLSW          0xC
++#define LRS_ErrorMSW          0xD
++#define LRS_FinCoarseDeskew   0xE
++#define LRS_LinkInValue               0xF
++#define LRS_NumLinkDels         0x10
++
++#define LRS_Pllfast             0x40
++ 
++union Sched_Status
++{
++    E3_uint32 Status;
++    struct
++    {
++#if defined(__LITTLE_ENDIAN__)
++      E3_uint32 StopNonSysCntxs:1;
++      E3_uint32 FlushCommandQueues:1;
++      E3_uint32 HaltDmas:1;
++      E3_uint32 HaltDmaDequeue:1;
++      E3_uint32 HaltThread:1;
++      E3_uint32 CProcStop:1;
++      E3_uint32 DiscardSysCntxIn:1;
++      E3_uint32 DiscardNonSysCntxIn:1;
++      E3_uint32 RestartCh0SysCntx:1;
++      E3_uint32 RestartCh0NonSysCntx:1;
++      E3_uint32 RestartCh1SysCntx:1;
++      E3_uint32 RestartCh1NonSysCntx:1;
++      E3_uint32 RestartDProc:1;
++      E3_uint32 RestartTProc:1;
++      E3_uint32 RestartCProc:1;
++      E3_uint32 ClearLinkErrorInt:1;
++      E3_uint32 :3;
++      E3_uint32 LinkSetValue:10; 
++      E3_uint32 FixLinkDelays:1;
++      E3_uint32 LinkBoundaryScan:1;
++#else
++      E3_uint32 LinkBoundaryScan:1;
++      E3_uint32 FixLinkDelays:1;
++      E3_uint32 LinkSetValue:10; 
++      E3_uint32 :3;
++      E3_uint32 ClearLinkErrorInt:1;
++      E3_uint32 RestartCProc:1;
++      E3_uint32 RestartTProc:1;
++      E3_uint32 RestartDProc:1;
++      E3_uint32 RestartCh1NonSysCntx:1;
++      E3_uint32 RestartCh1SysCntx:1;
++      E3_uint32 RestartCh0NonSysCntx:1;
++      E3_uint32 RestartCh0SysCntx:1;
++      E3_uint32 DiscardNonSysCntxIn:1;
++      E3_uint32 DiscardSysCntxIn:1;
++      E3_uint32 CProcStop:1;
++      E3_uint32 HaltThread:1;
++      E3_uint32 HaltDmaDequeue:1;
++      E3_uint32 HaltDmas:1;
++      E3_uint32 FlushCommandQueues:1;
++      E3_uint32 StopNonSysCntxs:1;
++#endif
++    } s;
++};
++
++#define LinkBoundaryScan      ((E3_uint32) 1<<31) /* Clears the link error interrupt */
++#define FixLinkDelays         ((E3_uint32) 1<<30) /* Clears the link error interrupt */
++#define LinkSetValue(Val, OldVal) ((E3_uint32) (((Val) & 0x3ff) << 20) | ((OldVal) & ((~0x3ff) << 20)))
++
++#define ClearLinkErrorInt     ((E3_uint32) 1<<16) /* Clears the link error interrupt */
++#define RestartCProc          ((E3_uint32) 1<<15) /* Clears command proc interrupt */
++#define RestartTProc          ((E3_uint32) 1<<14) /* Clears thread interrupt */
++#define RestartDProc          ((E3_uint32) 1<<13) /* Clears dma0 interrupt */
++#define RestartCh1NonSysCntx  ((E3_uint32) 1<<12) /* Clears interrupt */
++#define RestartCh1SysCntx     ((E3_uint32) 1<<11) /* Clears interrupt */
++#define RestartCh0NonSysCntx  ((E3_uint32) 1<<10) /* Clears interrupt */
++#define RestartCh0SysCntx     ((E3_uint32) 1<<9) /* Clears interrupt */
++#define CProcStopped          ((E3_uint32) 1<<9) /* Read value only */
++
++#define TraceSetEvents                ((E3_uint32) 1<<8)
++#define DiscardNonSysCntxIn   ((E3_uint32) 1<<7)
++#define DiscardSysCntxIn      ((E3_uint32) 1<<6)
++#define CProcStop             ((E3_uint32) 1<<5) /* Will empty all the command port queues. */
++#define HaltThread            ((E3_uint32) 1<<4) /* Will stop the thread proc and clear the tproc command queue */
++#define HaltDmaDequeue                ((E3_uint32) 1<<3) /* Will stop the dmaers starting new dma's. */
++#define HaltDmas              ((E3_uint32) 1<<2) /* Will stop the dmaers and clear the dma command queues */
++#define FlushCommandQueues    ((E3_uint32) 1<<1) /* Causes the command ports to be flushed. */
++#define StopNonSysCntxs               ((E3_uint32) 1<<0) /* Prevents a non-SysCntx from starting. */
++
++/* Initial value of schedule status register */
++#define LinkResetToken                0x00F
++
++#define Sched_Initial_Value   (LinkBoundaryScan | (LinkResetToken << 20) | \
++                               DiscardSysCntxIn | DiscardNonSysCntxIn | HaltThread | HaltDmas)
++
++#define StopDmaQueues      (HaltDmaDequeue | HaltDmas | \
++                            DiscardNonSysCntxIn | DiscardSysCntxIn)
++#define CheckDmaQueueStopped (INT_DiscardingNonSysCntx | INT_DiscardingSysCntx | INT_DProcHalted)
++
++#define HaltStopAndExtTestMask        0xfff001ff
++#define HaltAndStopMask               0x000001ff
++
++
++#define DmaComQueueNotEmpty   (1<<0)
++#define ThreadComQueueNotEmpty        (1<<1)
++#define EventComQueueNotEmpty (1<<2)
++#define DmaComQueueHalfFull   (1<<3)
++#define ThreadComQueueHalfFull        (1<<4)
++#define EventComQueueHalfFull (1<<5)
++#define DmaComQueueError      (1<<6)
++#define ThreadComQueueError   (1<<7)
++#define EventComQueueError    (1<<8)
++
++#define ComQueueNotEmpty      (DmaComQueueNotEmpty | ThreadComQueueNotEmpty | EventComQueueNotEmpty)
++#define ComQueueError         (DmaComQueueError | ThreadComQueueError | EventComQueueError)
++
++typedef union _E3_DmaInfo
++{
++    E3_uint32  Value;
++    struct
++    {
++#if defined(__LITTLE_ENDIAN__)
++      E3_uint32 DmaOutputOpen:1;      /* The packet is currently open */
++      E3_uint32 :7;
++      E3_uint32 TimeSliceCount:2;     /* Time left to timeslice */
++      E3_uint32 UseRemotePriv:1;      /* Set for remote read dmas */
++      E3_uint32 DmaLastPacket:1;      /* Set for the last packet of a dma */
++      E3_uint32 PacketAckValue:2;     /* Packet ack type. Valid if AckBufferValid set. */
++      E3_uint32 PacketTimeout:1;      /* Packet timeout. Sent an EopError. Valid if AckBufferValid set. */
++      E3_uint32 AckBufferValid:1;     /* Packet ack is valid. */
++      E3_uint32 :16;                  /* read as Zero */
++#else
++      E3_uint32 :16;                  /* read as Zero */
++      E3_uint32 AckBufferValid:1;     /* Packet ack is valid. */
++      E3_uint32 PacketTimeout:1;      /* Packet timeout. Sent an EopError. Valid if AckBufferValid set. */
++      E3_uint32 PacketAckValue:2;     /* Packet ack type. Valid if AckBufferValid set. */
++      E3_uint32 DmaLastPacket:1;      /* Set for the last packet of a dma */
++      E3_uint32 UseRemotePriv:1;      /* Set for remote read dmas */
++      E3_uint32 TimeSliceCount:2;     /* Time left to timeslice */
++      E3_uint32 :7;
++      E3_uint32 DmaOutputOpen:1;      /* The packet is currently open */
++#endif
++    } s;
++} E3_DmaInfo;
++
++typedef volatile struct _E3_DmaRds
++{
++   E3_uint32          DMA_Source4to0AndTwoReads;
++   E3_uint32          pad13;
++   E3_uint32          DMA_BytesToRead;
++   E3_uint32          pad14;
++   E3_uint32          DMA_MinusPacketSize;
++   E3_uint32          pad15;
++   E3_uint32          DMA_MaxMinusPacketSize;
++   E3_uint32          pad16;
++   E3_uint32          DMA_DmaOutputOpen;
++   E3_uint32          pad16a;
++   E3_DmaInfo         DMA_PacketInfo;
++   E3_uint32          pad17[7];
++   E3_uint32          IProcTrapBase;
++   E3_uint32          pad18;
++   E3_uint32          IProcBlockTrapBase;
++   E3_uint32          pad19[11];
++} E3_DmaRds;
++   
++typedef volatile struct _E3_DmaWrs
++{
++   E3_uint64          pad0;
++   E3_uint64          LdAlignment;
++   E3_uint64          ResetAckNLdBytesToWr;
++   E3_uint64          SetAckNLdBytesToWr;
++   E3_uint64          LdBytesToRd;
++   E3_uint64          LdDmaType;
++   E3_uint64          SendRoutes;
++   E3_uint64          SendEop;
++   E3_uint64          pad1[8];
++} E3_DmaWrs;
++
++typedef volatile struct _E3_Exts
++{
++   E3_uint32          CurrContext;                            /* 0x12a00 */
++   E3_uint32          pad0;
++   E3_Status_Reg      DProcStatus;                            /* 0x12a08 */
++   E3_uint32          pad1;
++   E3_Status_Reg      CProcStatus;                            /* 0x12a10 */
++   E3_uint32          pad2;
++   E3_Status_Reg      TProcStatus;                            /* 0x12a18 */
++   E3_uint32          pad3;
++   E3_IProcStatus_Reg IProcStatus;                            /* 0x12a20 */
++   E3_uint32          pad4[3];
++
++   E3_uint32          IProcTypeContext;                       /* 0x12a30 */
++   E3_uint32          pad5;
++   E3_uint32          IProcTransAddr;                         /* 0x12a38 */
++   E3_uint32          pad6;
++   E3_uint32          IProcCurrTransData0;                    /* 0x12a40 */
++   E3_uint32          pad7;
++   E3_uint32          IProcCurrTransData1;                    /* 0x12a48 */
++   E3_uint32          pad8;
++
++   E3_uint32          SchCntReg;                              /* 0x12a50 */
++   E3_uint32          pad9;
++   E3_uint32          InterruptReg;                           /* 0x12a58 */
++   E3_uint32          pad10;
++   E3_uint32          InterruptMask;                          /* 0x12a60 */
++   E3_uint32          pad11;
++   E3_uint32          LinkErrorTypes;                         /* 0x12a68 */
++   E3_uint32          pad12[3];
++   E3_uint32          LinkState;      /* a read here returens the DataDel value for the */
++                                      /* link that has just been defined by a write to */
++                                      /* Regs.Exts.SchCntReg.LinkSetValue */
++   E3_uint32          pad13;
++
++   union                                                      /* 0x12a80 */
++   {
++      E3_DmaWrs               DmaWrs;
++      E3_DmaRds               DmaRds;
++   } Dmas;
++} E3_Exts;
++
++typedef union com_port_entry
++{
++    E3_uint64  type;
++    struct
++    {
++      E3_uint32 Address;              /* Command VAddr */
++#if defined(__LITTLE_ENDIAN__)
++      E3_uint32 Context0Issue:1;      /* Issue was for context 0 */
++      E3_uint32 EventNotCommand:1;    /* Issue address bit 3 */
++      E3_uint32 RemoteDesc:1;         /* Issue address bit 5 */
++      E3_uint32 :13;                  /* read as Zero */
++      E3_uint32 Context:12;           /* Command Context */
++      E3_uint32 :4;                   /* read as Zero */
++#else
++      E3_uint32 :4;                   /* read as Zero */
++      E3_uint32 Context:12;           /* Command Context */
++      E3_uint32 :13;                  /* read as Zero */
++      E3_uint32 RemoteDesc:1;         /* Issue address bit 5 */
++      E3_uint32 EventNotCommand:1;    /* Issue address bit 3 */
++      E3_uint32 Context0Issue:1;      /* Issue was for context 0 */
++#endif
++    } s;
++} E3_ComPortEntry;
++
++/* control reg bits */
++#define CONT_MMU_ENABLE               (1 << 0) /* bit 0 enables mmu */
++#define CONT_ENABLE_8K_PAGES  (1 << 1) /* When set smallest page is 8k instead of 4k. */
++#define CONT_EN_ALL_SETS      (1 << 2) /* enable cache */
++#define CONT_CACHE_LEVEL0     (1 << 3) /* cache context table */
++#define CONT_CACHE_LEVEL1     (1 << 4) /* cache up level 1 PTD/PTE */
++#define CONT_CACHE_LEVEL2     (1 << 5) /* cache up level 2 PTD/PTE */
++#define CONT_CACHE_LEVEL3     (1 << 6) /* cache up level 3 PTD/PTE */
++#define CONT_CACHE_TRAPS      (1 << 7) /* cache up traps */
++#define CONT_CACHE_LEV0_ROUTES        (1 << 8) /* cache up small routes */
++#define CONT_CACHE_LEV1_ROUTES        (1 << 9) /* cache up large routes */
++#define CONT_CACHE_ALL                (CONT_CACHE_LEVEL0 | CONT_CACHE_LEVEL1 | CONT_CACHE_LEVEL2 | \
++                               CONT_CACHE_LEVEL3 | CONT_CACHE_TRAPS | \
++                               CONT_CACHE_LEV0_ROUTES | CONT_CACHE_LEV1_ROUTES)
++
++#define CONT_SYNCHRONOUS      (1 << 10) /* PCI running sync */
++#define CONT_SER              (1 << 11) /* Single bit output (Elan1 SER bit) */
++#define CONT_SIR              (1 << 12) /* Writing 1 resets elan. */
++
++#define CONT_PSYCHO_MODE      (1 << 13) /* Enables all the perversion required by psycho */
++#define CONT_ENABLE_ECC               (1 << 14) /* Enables error detecting on the ECC */
++#define CONT_SDRAM_TESTING    (1 << 15) /* Switches to test mode for checking EEC data bits */
++
++/* defines SDRam CasLatency. Once set will not change again unless reset is reasserted. */
++/* 1 = Cas Latency is 3, 0 = Cas Latency is 2 */
++#define CAS_LATENCY_2         (0 << 16)
++#define CAS_LATENCY_3         (1 << 16)
++#define REFRESH_RATE_2US      (0 << 17) /* defines 2us SDRam Refresh rate. */
++#define REFRESH_RATE_4US      (1 << 17) /* defines 4us SDRam Refresh rate. */
++#define REFRESH_RATE_8US      (2 << 17) /* defines 8us SDRam Refresh rate. */
++#define REFRESH_RATE_16US     (3 << 17) /* defines 16us SDRam Refresh rate. */
++
++#define CONT_PCI_ERR          (1 << 19) /* Read 1 if PCI Error */
++#define CONT_CLEAR_PCI_ERROR  (1 << 19) /* Clears an PCI error. */
++
++/* Will cause the PCI error bit to become set. This is used to force the threads proc
++   and the uProc to start to stall. */
++#define CONT_SET_PCI_ERROR    (1 << 20)
++
++/* Writes SDram control reg when set. Also starts SDram memory system refreshing. */
++#define SETUP_SDRAM           (1 << 21)
++
++/* Flushes the tlb */
++#define MMU_FLUSH             (1 << 22)
++/* and read back when it's finished */
++#define MMU_FLUSHED           (1 << 0)
++
++/* Clears any ECC error detected by SDRam interface */
++#define CLEAR_SDRAM_ERROR     (1 << 23)
++
++#define ECC_ADDR_MASK         0x0ffffff8
++#define ECC_UE_MASK           0x1 
++#define ECC_CE_MASK           0x2
++#define ECC_ME_MASK           0x4 
++#define ECC_SYN_MASK          0xff
++
++/* define page table entry bit fields */
++#define TLB_PageSizeBits      (3 << 0)
++#define TLB_ACCBits           (7 << 2)
++#define TLB_LocalBit          (1 << 5)
++#define TLB_PCI64BitTargetBit (1 << 6)
++#define TLB_PCIBigEndianBit   (1 << 7)
++
++#define TLB_ModifiedBit               (1 << 55)
++#define TLB_ReferencedBit     (1 << 63)
++
++/* Used to read values from the tlb. */
++#define TLB_TlbReadCntBitsSh  56
++#define TLB_UseSelAddrSh      (1ULL << 60)
++#define TLB_WriteTlbLine      (1ULL << 61)
++
++#define TLB_SEL_LINE(LineNo) (TLB_UseSelAddrSh | \
++                            ((E3_uint64)((LineNo) & 0xf) << TLB_TlbReadCntBitsSh))
++
++typedef union _E3_CacheContReg
++{
++    E3_uint32 ContReg;
++    struct
++    {
++#if defined(__LITTLE_ENDIAN__)
++      E3_uint32 MMU_Enable:1;         /* wr 1 to enable the MMU */
++      E3_uint32 Set8kPages:1;         /* wr 1 smallest page is 8k. */
++      E3_uint32 EnableAllSets:1;      /* wr 1 All the cache sets are enabled */
++      E3_uint32 Cache_Level0:1;       /* wr 1 lev0 page tabs will be cached */
++      E3_uint32 Cache_Level1:1;       /* wr 1 lev1 page tabs will be cached */
++      E3_uint32 Cache_Level2:1;       /* wr 1 lev2 page tabs will be cached */
++      E3_uint32 Cache_Level3:1;       /* wr 1 lev3 page tabs will be cached */
++      E3_uint32 Cache_Traps:1;        /* wr 1 trap info will be cached */
++      E3_uint32 Cache_Lev0_Routes:1;  /* wr 1 small routes will be cached */
++      E3_uint32 Cache_Lev1_Routes:1;  /* wr 1 big routes will be cached */
++      E3_uint32 PCI_Synchronous:1;    /* Pci and sys clocks are running synchronously*/
++      E3_uint32 SER:1;                /* 1 bit output port */
++      E3_uint32 SIR:1;                /* write 1 will reset elan */
++      E3_uint32 PsychoMode:1;         /* Enables psycho perversion mode. */
++      E3_uint32 CasLatency:1;         /* 1=cas latency=3, 1=cas latency=2 */
++      E3_uint32 RefreshRate:2;        /* 0=2us, 1=4us, 2=8us, 3=16us */
++      E3_uint32 Pci_Err:1;            /* pci error. Write 1 clears err */
++      E3_uint32 Set_Pci_Error:1;      /* Will simulate an Pci error */
++      E3_uint32 StartSDRam:1;         /* Starts the sdram subsystem */
++      E3_uint32 FlushTlb:1;           /* Flush the contence of the tlb */
++      E3_uint32 :11;
++#else
++      E3_uint32 :11;
++      E3_uint32 FlushTlb:1;           /* Flush the contence of the tlb */
++      E3_uint32 StartSDRam:1;         /* Starts the sdram subsystem */
++      E3_uint32 Set_Pci_Error:1;      /* Will simulate an Pci error */
++      E3_uint32 Pci_Err:1;            /* pci error. Write 1 clears err */
++      E3_uint32 RefreshRate:2;        /* 0=2us, 1=4us, 2=8us, 3=16us */
++      E3_uint32 CasLatency:1;         /* 1=cas latency=3, 1=cas latency=2 */
++      E3_uint32 PsychoMode:1;         /* Enables psycho perversion mode. */
++      E3_uint32 SIR:1;                /* write 1 will reset elan */
++      E3_uint32 SER:1;                /* 1 bit output port */
++      E3_uint32 PCI_Synchronous:1;    /* Pci and sys clocks are running synchronously*/
++      E3_uint32 Cache_Lev1_Routes:1;  /* wr 1 big routes will be cached */
++      E3_uint32 Cache_Lev0_Routes:1;  /* wr 1 small routes will be cached */
++      E3_uint32 Cache_Traps:1;        /* wr 1 trap info will be cached */
++      E3_uint32 Cache_Level3:1;       /* wr 1 lev3 page tabs will be cached */
++      E3_uint32 Cache_Level2:1;       /* wr 1 lev2 page tabs will be cached */
++      E3_uint32 Cache_Level1:1;       /* wr 1 lev1 page tabs will be cached */
++      E3_uint32 Cache_Level0:1;       /* wr 1 lev0 page tabs will be cached */
++      E3_uint32 EnableAllSets:1;      /* wr 1 All the cache sets are enabled */
++      E3_uint32 Set8kPages:1;         /* wr 1 smallest page is 8k. */
++      E3_uint32 MMU_Enable:1;         /* wr 1 to enable the MMU */
++#endif
++    } s;
++} E3_CacheContReg;
++
++typedef union _E3_TrapBits
++{
++    volatile E3_uint32 Bits;
++    struct
++    {
++#if defined(__LITTLE_ENDIAN__)
++      E3_uint32 ForcedTProcTrap:1;     /* The theads proc has been halted */
++      E3_uint32 InstAccessException:1; /* An instruction access exception */
++      E3_uint32 Unimplemented:1;       /* Unimplemented instruction executed */
++      E3_uint32 DataAccessException:1; /* A data access exception */  
++
++      E3_uint32 ThreadTimeout:1;       /* The threads outputer has timed out */
++      E3_uint32 OpenException:1;       /* Invalid sequence of open, sendtr or close */
++      E3_uint32 OpenRouteFetch:1;      /* Fault while fetching routes for previous open*/
++      E3_uint32 TrapForTooManyInsts:1; /* Thread has been executing for too long */
++      
++      E3_uint32 PacketAckValue:2;      /* Packet ack type. Valid if AckBufferValid set. */
++      E3_uint32 PacketTimeout:1;       /* Packet timeout. Sent an EopError. Valid if AckBufferValid set. */
++
++      E3_uint32 AckBufferValid:1;      /* The PacketAckValue bits are valid */
++      E3_uint32 OutputWasOpen:1;       /* The output was open when tproc trapped */
++      E3_uint32 TProcDeschedule:2;     /* The reason the tproc stopped running. */
++      E3_uint32 :17;
++#else
++      E3_uint32 :17;
++      E3_uint32 TProcDeschedule:2;     /* The reason the tproc stopped running. */
++      E3_uint32 OutputWasOpen:1;       /* The output was open when tproc trapped */
++      E3_uint32 AckBufferValid:1;      /* The PacketAckValue bits are valid */
++      
++      E3_uint32 PacketTimeout:1;       /* Packet timeout. Sent an EopError. Valid if AckBufferValid set. */
++      E3_uint32 PacketAckValue:2;      /* Packet ack type. Valid if AckBufferValid set. */
++      
++      E3_uint32 TrapForTooManyInsts:1; /* Thread has been executing for too long */
++      E3_uint32 OpenRouteFetch:1;      /* Fault while fetching routes for previous open*/
++      E3_uint32 OpenException:1;       /* Invalid sequence of open, sendtr or close */
++      E3_uint32 ThreadTimeout:1;       /* The threads outputer has timed out */
++
++      E3_uint32 DataAccessException:1; /* A data access exception */
++      E3_uint32 Unimplemented:1;       /* Unimplemented instruction executed */
++      E3_uint32 InstAccessException:1; /* An instruction access exception */
++      E3_uint32 ForcedTProcTrap:1;     /* The theads proc has been halted */
++#endif
++    } s;
++} E3_TrapBits;
++
++typedef union _E3_DirtyBits
++{
++    volatile E3_uint32 Bits;
++    struct
++    {
++#if defined(__LITTLE_ENDIAN__)
++      E3_uint32 GlobalsDirty:8;
++      E3_uint32 OutsDirty:8;          /* will always read as dirty. */
++      E3_uint32 LocalsDirty:8;
++      E3_uint32 InsDirty:8;
++#else
++      E3_uint32 InsDirty:8;
++      E3_uint32 LocalsDirty:8;
++      E3_uint32 OutsDirty:8;          /* will always read as dirty. */
++      E3_uint32 GlobalsDirty:8;
++#endif
++    } s;
++} E3_DirtyBits;
++
++#define E3_TProcDescheduleMask    0x6000
++#define E3_TProcDescheduleWait    0x2000
++#define E3_TProcDescheduleSuspend 0x4000
++#define E3_TProcDescheduleBreak   0x6000
++
++#define E3_TrapBitsMask         0x7fff
++
++#define ThreadRestartFromTrapBit      1
++#define ThreadReloadAllRegs           2
++
++#define E3_PAckOk     0
++#define E3_PAckTestFail       1
++#define E3_PAckDiscard        2
++#define E3_PAckError  3
++
++typedef volatile struct _E3_DataBusMap
++{
++   E3_uint64           Dma_Alignment_Port[8];         /* 0x00002800 */
++   E3_uint32           pad0[0x30];                    /* 0x00002840 */
++
++   E3_uint32           Input_Trans0_Data[0x10];       /* 0x00002900 */
++   E3_uint32           Input_Trans1_Data[0x10];
++   E3_uint32           Input_Trans2_Data[0x10];
++   E3_uint32           Input_Trans3_Data[0x10];
++
++/* this is the start of the exts directly addressable from the ucode. */
++   E3_Exts             Exts;                          /* 0x00002a00 */
++
++/* this is the start of the registers directly addressable from the ucode. */
++   E3_DMA              Dma_Desc;                      /* 0x00002b00 */
++
++   E3_uint32           Dma_Last_Packet_Size;          /* 0x00002b20 */
++   E3_uint32           Dma_This_Packet_Size;          /* 0x00002b24 */
++   E3_uint32           Dma_Tmp_Source;                /* 0x00002b28 */
++   E3_uint32           Dma_Tmp_Dest;                  /* 0x00002b2c */
++
++   E3_Addr             Thread_SP_Save_Ptr;    /* points to the thread desched save word. */
++   E3_uint32           Dma_Desc_Size_InProg;          /* 0x00002b34 */
++
++   E3_uint32           Thread_Desc_SP;                /* 0x00002b38 */
++   E3_uint32           Thread_Desc_Context;           /* 0x00002b3c */
++
++   E3_uint32           uCode_TMP[0x10];               /* 0x00002b40 */
++
++   E3_uint32           TProc_NonSysCntx_FPtr;         /* 0x00002b80 */
++   E3_uint32           TProc_NonSysCntx_BPtr;         /* 0x00002b84 */
++   E3_uint32           TProc_SysCntx_FPtr;            /* 0x00002b88 */
++   E3_uint32           TProc_SysCntx_BPtr;            /* 0x00002b8c */
++   E3_uint32           DProc_NonSysCntx_FPtr;         /* 0x00002b90 */
++   E3_uint32           DProc_NonSysCntx_BPtr;         /* 0x00002b94 */
++   E3_uint32           DProc_SysCntx_FPtr;            /* 0x00002b98 */
++   E3_uint32           DProc_SysCntx_BPtr;            /* 0x00002b9c */
++
++   E3_uint32           Input_Trap_Base;               /* 0x00002ba0 */
++   E3_uint32           Input_Queue_Offset;            /* 0x00002ba4 */
++   E3_uint32           CProc_TrapSave_Addr;           /* 0x00002ba8 */
++   E3_uint32           Input_Queue_Addr;              /* 0x00002bac */
++   E3_uint32           uCode_TMP10;                   /* 0x00002bb0 */
++   E3_uint32           uCode_TMP11;                   /* 0x00002bb4 */
++   E3_uint32           Event_Trace_Ptr;               /* 0x00002bb8 */
++   E3_uint32           Event_Trace_Mask;              /* 0x00002bbc */
++
++   E3_ComPortEntry     DmaComQueue[3];                /* 0x00002bc0 */
++
++   E3_uint32           Event_Int_Queue_FPtr;          /* 0x00002bd8 */
++   E3_uint32           Event_Int_Queue_BPtr;          /* 0x00002bdc */
++
++   E3_ComPortEntry     ThreadComQueue[2];             /* 0x00002be0 */
++   E3_ComPortEntry     SetEventComQueue[2];           /* 0x00002bf0 */
++
++   E3_uint32           pad1[96];                      /* 0x00002c00 */
++   E3_uint32           ComQueueStatus;                /* 0x00002d80 */
++   E3_uint32           pad2[31];                      /* 0x00002d84 */
++
++/* These are the internal registers of the threads proc. */
++   E3_uint32           Globals[8];                    /* 0x00002e00 */
++   E3_uint32           Outs[8];
++   E3_uint32           Locals[8];
++   E3_uint32           Ins[8];
++
++   E3_uint32           pad3[16];
++
++   E3_uint32           IBufferReg[4];
++
++   E3_uint32           ExecuteNPC;
++   E3_uint32           ExecutePC;
++
++   E3_uint32           StartPC;
++   E3_uint32           pad4;
++
++   E3_uint32           StartnPC;
++   E3_uint32           pad5;
++
++   E3_TrapBits                 TrapBits;
++   E3_DirtyBits                DirtyBits;
++   E3_uint64           LoadDataReg;
++   E3_uint64           StoreDataReg;
++
++   E3_uint32           ECC_STATUS0;
++   E3_uint32           ECC_STATUS1;
++   E3_uint32           pad6[0xe];
++
++/* Pci slave port regs */
++   E3_uint32           PciSlaveReadCache[0x10];
++
++   E3_uint32           Fault_Base_Ptr;
++   E3_uint32           pad7;
++   E3_uint32           Context_Ptr;
++   E3_uint32           pad8;
++   E3_uint32           Input_Context_Filter;      /* write only, No data */
++   E3_uint32           Input_Context_Fil_Flush;   /* write only, No data */
++   E3_CacheContReg     Cache_Control_Reg;
++   E3_uint32           pad9;
++
++   E3_uint64           Tlb_Line_Value;
++   
++   E3_uint32           Walk_Datareg1;
++   E3_uint32           Walk_VAddr_Tab_Base;
++   E3_uint32           Walk_Datareg;
++   E3_uint32           Walk_ContextReg;
++   E3_uint32           Walk_FaultAddr;
++   E3_uint32           Walk_EventAddr;
++
++/* outputers output cont ext registers. */
++   E3_uint64           Dma_Route_012345_Context;
++   E3_uint64           pad10;
++   E3_uint64           Dma_Route_01234567;
++   E3_uint64           Dma_Route_89ABCDEF;
++
++   E3_uint64           Thread_Route_012345_Context;
++   E3_uint64           pad11;
++   E3_uint64           Thread_Route_01234567;
++   E3_uint64           Thread_Route_89ABCDEF;
++} E3_DataBusMap;
++
++typedef volatile struct _E3_Regs
++{
++   E3_CacheSets                 Sets;                         /* 0x00000000 */
++   E3_CacheTags                 Tags;                         /* 0x00002000 */
++   E3_DataBusMap        Regs;                         /* 0x00002800 */
++   E3_uint32            pad1[0x400];
++   E3_User_Regs         URegs;
++} E3_Regs;
++
++#define MAX_TRAPPED_TRANS     16
++#define TRANS_DATA_WORDS      16
++#define TRANS_DATA_BYTES      64
++
++/*
++ * Event interrupt
++ */
++typedef volatile union _E3_EventInt
++{
++   E3_uint64   ForceAlign;
++   struct {
++       E3_uint32 IntCookie;
++       E3_uint32 EventContext;        /* Bits 16 to 28 */
++    } s;
++} E3_EventInt;
++
++#define GET_EVENT_CONTEXT(Ptr) ((Ptr->s.EventContext >> 16) & MAX_ROOT_CONTEXT_MASK)
++
++typedef volatile union _E3_ThreadQueue
++{
++   E3_uint64  ForceAlign;
++   struct
++   {
++       E3_Addr         Thread;
++#if defined(__LITTLE_ENDIAN__)
++       E3_uint32 :16;         /* Bits 0  to 15 */
++       E3_uint32 Context:13;  /* Bits 16 to 28 */
++       E3_uint32 :3;          /* Bits 29 to 31 */
++#else
++       E3_uint32 :3;          /* Bits 29 to 31 */
++       E3_uint32 Context:13;  /* Bits 16 to 28 */
++       E3_uint32 :16;         /* Bits 0  to 15 */
++#endif
++   } s;
++} E3_ThreadQueue;
++
++typedef volatile union _E3_FaultStatusReg
++{
++   E3_uint32 Status;
++   struct
++   {
++#if defined(__LITTLE_ENDIAN__)
++      E3_uint32 AccTypePerm:3;        /* Access permission. See below. Bits 0 to 2 */
++      E3_uint32 AccSize:4;    /* Access size. See below for different types. Bits 3 to 6 */
++      E3_uint32 WrAcc:1;      /* Access was a write. Bit 7 */
++      E3_uint32 NonAllocAcc:1;        /* Access was a cache non allocate type. Bit 8 */
++      E3_uint32 BlkDataType:2;        /* Data size used for endian flips. Bits 9 to 10 */
++      E3_uint32 RdLine:1;     /* Access was a dma read line. Bit 11 */
++      E3_uint32 RdMult:1;     /* Access was a dma read multiple. Bit 12 */
++      E3_uint32 Walking:1;    /* The fault occued when walking. Bit 13 */
++      E3_uint32 Level:2;      /* Page table level when the fault occued. Bits 14 to 15 */
++      E3_uint32 ProtFault:1;  /* A protection fault occured. Bit 16 */
++      E3_uint32 FaultPte:2;   /* Page table type when the fault occured. Bit 17 */
++      E3_uint32 AlignmentErr:1;       /* Address alignment did not match the access size. Bit 19 */
++      E3_uint32 VProcSizeErr:1;       /* VProc number is out of range. Bit 20 */
++      E3_uint32 WalkBadData:1;        /* Memory CRC error during a walk. Bit 21 */
++      E3_uint32 :10;          /* Bits 22 to 31 */
++#else
++      E3_uint32 :10;          /* Bits 22 to 31 */
++      E3_uint32 WalkBadData:1;        /* Memory CRC error during a walk. Bit 21 */
++      E3_uint32 VProcSizeErr:1;       /* VProc number is out of range. Bit 20 */
++      E3_uint32 AlignmentErr:1;       /* Address alignment did not match the access size. Bit 19 */
++      E3_uint32 FaultPte:2;   /* Page table type when the fault occured. Bit 17 */
++      E3_uint32 ProtFault:1;  /* A protection fault occured. Bit 16 */
++      E3_uint32 Level:2;      /* Page table level when the fault occued. Bits 14 to 15 */
++      E3_uint32 Walking:1;    /* The fault occued when walking. Bit 13 */
++      E3_uint32 RdMult:1;     /* Access was a dma read multiple. Bit 12 */
++      E3_uint32 RdLine:1;     /* Access was a dma read line. Bit 11 */
++      E3_uint32 BlkDataType:2;        /* Data size used for endian flips. Bits 9 to 10 */
++      E3_uint32 NonAllocAcc:1;        /* Access was a cache non allocate type. Bit 8 */
++      E3_uint32 WrAcc:1;      /* Access was a write. Bit 7 */
++      E3_uint32 AccSize:4;    /* Access size. See below for different types. Bits 3 to 6 */
++      E3_uint32 AccTypePerm:3;        /* Access permission. See below. Bits 0 to 2 */
++#endif
++   } s;
++} E3_FaultStatusReg;
++
++typedef union _E3_FaultSave
++{
++   E3_uint64           ForceAlign;
++   struct {
++      E3_FaultStatusReg        FSR;
++      volatile E3_uint32 FaultContext;
++      volatile E3_uint32 FaultAddress;
++      volatile E3_uint32 EventAddress;
++   } s;
++} E3_FaultSave;
++
++/* MMU fault status reg bit positions. */
++#define FSR_WritePermBit      0       /* 1=Write access perm, 0=Read access perm */
++#define FSR_RemotePermBit     1       /* 1=Remote access perm, 0=local access perm */
++#define FSR_EventPermBit      2       /* 1=Event access perm, 0=data access perm */
++#define FSR_Size0Bit          3
++#define FSR_Size1Bit          4
++#define FSR_Size2Bit          5
++#define FSR_Size3Bit          6
++#define FSR_WriteAccBit               7       /* 1=Write access, 0=Read access. */
++#define FSR_NonAllocBit               8       /* 1=Do not fill cache with this data */
++#define FSR_BlkDataTy0Bit     9
++#define FSR_BlkDataTy1Bit     10
++#define FSR_ReadLineBit               11
++#define FSR_ReadMultipleBit   12
++
++#define FSR_PermMask          (0xf << FSR_WritePermBit)
++#define FSR_SizeMask          (0xf << FSR_Size0Bit)
++#define FSR_AccTypeMask               (3 << FSR_WriteAccBit)
++#define FSR_BlkDataTyMask     (3 << FSR_BlkDataTy0Bit)
++#define FSR_PciAccTyMask      (3 << FSR_ReadLineBit)
++#define FSR_Walking           (0x1 << 13)
++#define FSR_Level_Mask                (0x3 << 14)
++#define FSR_ProtFault         (0x1 << 16)
++#define FSR_FaultPTEType      (0x2 << 17)
++#define FSR_AddrSizeError     (0x1 << 19)
++#define FSR_VProcSizeError    (0x1 << 20)
++#define FSR_WalkBadData               (0x1 << 21)
++
++#define FSR_PermRead          0
++#define FSR_PermWrite         1
++#define FSR_PermRemoteRead    2
++#define FSR_PermRemoteWrite   3
++#define FSR_PermEventRd               4
++#define FSR_PermEventWr               5
++#define FSR_PermRemoteEventRd 6
++#define FSR_PermRemoteEventWr 7
++
++/* AT size values for each access type */
++#define FSR_Word              (0x0 << FSR_Size0Bit)
++#define FSR_DWord             (0x1 << FSR_Size0Bit)
++#define FSR_QWord             (0x2 << FSR_Size0Bit)
++#define FSR_Block32           (0x3 << FSR_Size0Bit)
++#define FSR_ReservedBlock     (0x6 << FSR_Size0Bit)
++#define FSR_Block64           (0x7 << FSR_Size0Bit)
++#define FSR_GetCntxFilter     (0x8 << FSR_Size0Bit)
++#define FSR_QueueDWord                (0x9 << FSR_Size0Bit)
++#define FSR_RouteFetch                (0xa << FSR_Size0Bit)
++#define FSR_QueueBlock                (0xb << FSR_Size0Bit)
++#define FSR_Block32PartWrite  (0xe << FSR_Size0Bit)
++#define FSR_Block64PartWrite  (0xf << FSR_Size0Bit)
++
++#define FSR_AllocRead         (0 << FSR_WriteAccBit)
++#define FSR_AllocWrite                (1 << FSR_WriteAccBit)
++#define FSR_NonAllocRd                (2 << FSR_WriteAccBit)
++#define FSR_NonAllocWr                (3 << FSR_WriteAccBit)
++
++#define FSR_TypeByte          (0 << FSR_BlkDataTy0Bit)
++#define FSR_TypeHWord         (1 << FSR_BlkDataTy0Bit)
++#define FSR_TypeWord          (2 << FSR_BlkDataTy0Bit)
++#define FSR_TypeDWord         (3 << FSR_BlkDataTy0Bit)
++
++typedef union E3_TrTypeCntx
++{
++   E3_uint32 TypeContext;
++   struct
++   {
++#if defined(__LITTLE_ENDIAN__)
++      E3_uint32 Type:16;              /* Transaction type field */
++      E3_uint32 Context:13;           /* Transaction context */
++      E3_uint32 TypeCntxInvalid:1;    /* Bit  29 */
++      E3_uint32 StatusRegValid:1;     /* Bit  30 */
++      E3_uint32 LastTrappedTrans:1;   /* Bit  31 */
++#else
++      E3_uint32 LastTrappedTrans:1;   /* Bit  31 */
++      E3_uint32 StatusRegValid:1;     /* Bit  30 */
++      E3_uint32 TypeCntxInvalid:1;    /* Bit  29 */
++      E3_uint32 Context:13;           /* Transaction context */
++      E3_uint32 Type:16;              /* Transaction type field */
++#endif
++   } s;
++} E3_TrTypeCntx;
++
++#define GET_TRAP_TYPE(Ptr)    (Ptr.TypeContext & 0xfff)
++#define GET_TRAP_CONTEXT(Ptr) ((Ptr.TypeContext >> 16) & 0x1fff)
++
++/* Words have been swapped for big endian access when fetched with dword access from elan.*/
++typedef union _E3_IprocTrapHeader
++{
++   E3_uint64  forceAlign;
++
++   struct
++   {
++      E3_TrTypeCntx    TrTypeCntx;
++      E3_uint32                TrAddr;
++      E3_uint32                TrData0;
++      union
++      {
++       E3_IProcStatus_Reg u_IProcStatus;
++       E3_uint32          u_TrData1;
++      } ipsotd;
++   } s;
++} E3_IprocTrapHeader;
++
++#define IProcTrapStatus ipsotd.u_IProcStatus
++#define TrData1               ipsotd.u_TrData1
++
++typedef struct E3_IprocTrapData
++{
++   E3_uint32 TrData[TRANS_DATA_WORDS];
++} E3_IprocTrapData;
++
++/*
++ * 64 kbytes of elan local memory. Must be aligned on a 64k boundary
++ */
++#define E3_NonSysCntxQueueSize        0x400
++#define E3_SysCntxQueueSize   0x100
++
++typedef struct _E3_TrapAndQueue
++{
++   E3_DMA             NonSysCntxDmaQueue[E3_NonSysCntxQueueSize];                     /* 0x000000 */
++   E3_DMA             SysCntxDmaQueue[E3_SysCntxQueueSize];                           /* 0x008000 */
++   E3_EventInt                EventIntQueue[E3_NonSysCntxQueueSize];                          /* 0x00A000 */
++   E3_ThreadQueue     NonSysCntxThreadQueue[E3_NonSysCntxQueueSize];                  /* 0x00C000 */  
++   E3_ThreadQueue     SysCntxThreadQueue[E3_SysCntxQueueSize];                        /* 0x00E000 */
++   E3_FaultSave               IProcSysCntx;                                                   /* 0x00E800 */
++   E3_Addr            Thread_SP_Save;                                                 /* 0x00E810 */
++   E3_uint32          dummy0[3];                                                      /* 0x00E814 */
++   E3_FaultSave               ThreadProcData;                                                 /* 0x00E820 */
++   E3_FaultSave               ThreadProcInst;                                                 /* 0x00E830 */
++   E3_FaultSave               dummy1[2];                                                      /* 0x00E840 */  
++   E3_FaultSave               ThreadProcOpen;                                                 /* 0x00E860 */
++   E3_FaultSave               dummy2;                                                         /* 0x00E870 */
++   E3_FaultSave               IProcNonSysCntx;                                                /* 0x00E880 */
++   E3_FaultSave               DProc;                                                          /* 0x00E890 */
++   E3_FaultSave               CProc;                                                          /* 0x00E8A0 */
++   E3_FaultSave               TProc;                                                          /* 0x00E8B0 */
++   E3_FaultSave               DProcData0;                                                     /* 0x00E8C0 */
++   E3_FaultSave               DProcData1;                                                     /* 0x00E8D0 */
++   E3_FaultSave               DProcData2;                                                     /* 0x00E8E0 */
++   E3_FaultSave               DProcData3;                                                     /* 0x00E8F0 */
++   E3_uint32          dummy3[0xc0];                                                   /* 0x00E900 */
++   E3_IprocTrapHeader VCh0_C0_TrHead[MAX_TRAPPED_TRANS];
++   E3_IprocTrapHeader VCh0_NonC0_TrHead[MAX_TRAPPED_TRANS];
++   E3_IprocTrapHeader VCh1_C0_TrHead[MAX_TRAPPED_TRANS];
++   E3_IprocTrapHeader VCh1_NonC0_TrHead[MAX_TRAPPED_TRANS];
++   E3_IprocTrapData   VCh0_C0_TrData[MAX_TRAPPED_TRANS];
++   E3_IprocTrapData   VCh0_NonC0_TrData[MAX_TRAPPED_TRANS];
++   E3_IprocTrapData   VCh1_C0_TrData[MAX_TRAPPED_TRANS];
++   E3_IprocTrapData   VCh1_NonC0_TrData[MAX_TRAPPED_TRANS];
++   E3_uint64          DmaOverflowQueueSpace[0x1000];
++   E3_uint64          ThreadOverflowQueueSpace[0x800];
++   E3_uint64          EventOverflowQueueSpace[0x800];
++} E3_TrapAndQueue;
++
++
++typedef struct _E3_ContextControlBlock 
++{
++   E3_uint32  rootPTP;
++   E3_uint32  filter;
++   E3_uint32  VPT_ptr;
++   E3_uint32  VPT_mask;
++} E3_ContextControlBlock;
++
++#define E3_CCB_CNTX0          (0x20000000)
++#define E3_CCB_DISCARD_ALL    (0x40000000)
++#define E3_CCB_ACKOK_ALL      (0x80000000)
++#define E3_CCB_MASK           (0xc0000000)
++
++#define E3_NUM_CONTEXT_0      (0x20)
++
++/* Macros to manipulate event queue pointers */
++/*     generate index in EventIntQueue */
++#define E3_EVENT_INTQ_INDEX(fptr)     (((fptr) & 0x1fff) >> 3)
++/*     generate next fptr */
++#define E3_EVENT_INTQ_NEXT(fptr)      ((((fptr) + 8) & ~0x4000) | 0x2000)
++
++
++#endif /* notdef _ELAN3_ELANREGS_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/include/elan3/elansyscall.h
+===================================================================
+--- linux-2.4.21.orig/include/elan3/elansyscall.h      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan3/elansyscall.h   2005-06-01 23:12:54.724420104 -0400
+@@ -0,0 +1,124 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN3_ELANSYSCALL_H
++#define __ELAN3_ELANSYSCALL_H
++
++#ident "$Id: elansyscall.h,v 1.34 2004/06/07 13:50:06 mike Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/elansyscall.h,v $*/
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#ifndef _ASM
++
++typedef struct sys_word_item
++{
++    struct sys_word_item *Next;
++    E3_uint32           Value;
++} SYS_WORD_ITEM;
++
++typedef struct sys_block_item
++{
++    struct sys_block_item *Next;
++    E3_uint32           *Pointer;
++} SYS_BLOCK_ITEM;
++
++typedef struct sys_swap_space
++{
++    int                Magic;
++    void      *ItemListsHead[MAX_LISTS];
++    void       **ItemListsTailp[MAX_LISTS];
++} SYS_SWAP_SPACE;
++
++typedef struct sys_exception
++{
++    int                       Type;
++    int                       Proc;
++    u_long            Res;
++    u_long            Value;
++    E3_FaultSave_BE   FaultArea;
++    
++    union
++    {
++      DMA_TRAP        Dma;
++      THREAD_TRAP     Thread;
++      COMMAND_TRAP    Command;
++      INPUT_TRAP      Input;
++    }                 Union;
++} SYS_EXCEPTION;
++
++typedef struct sys_exception_space
++{
++    struct sys_exception_space *Next;
++    int                               Magic;
++    int                               Front;
++    int                               Back;
++    int                               Count;
++    int                               Overflow;
++    SYS_EXCEPTION             Exceptions[1];
++} SYS_EXCEPTION_SPACE;
++
++#ifdef __KERNEL__
++
++typedef struct sys_ctxt
++{
++    SYS_SWAP_SPACE      *Swap;
++    SYS_EXCEPTION_SPACE *Exceptions;
++    kmutex_t           Lock;
++
++    spinlock_t                 WaitLock;
++    kcondvar_t                 NetworkErrorWait;
++
++    int                        Armed;
++    int                        Backoff;
++    long               Time;
++
++    u_long             Flags;
++    int                  signal;
++
++    EVENT_COOKIE_TABLE  *Table;
++} SYS_CTXT;
++
++extern SYS_CTXT *sys_init (ELAN3_CTXT *ctxt);
++extern int       sys_waitevent (ELAN3_CTXT *ctxt, E3_Event *event);
++extern void      sys_addException (SYS_CTXT *sctx, int type, int proc, caddr_t ptr, int size, 
++                                 E3_FaultSave_BE *, u_long res, u_long value);
++extern int       sys_getException (SYS_CTXT *sctx, SYS_EXCEPTION *ex);
++
++/* returns -ve error or ELAN_CAP_OK or ELAN_CAP_RMS */
++/* use = ELAN_USER_ATTACH, ELAN_USER_P2P, ELAN_USER_BROADCAST */
++extern int  elan3_validate_cap (ELAN3_DEV *dev, ELAN_CAPABILITY *cap ,int use);
++
++#endif /* __KERNEL__ */
++
++#endif /* _ASM */
++
++/* values for "Flags" */
++#define ELAN3_SYS_FLAG_DMA_BADVP              1
++#define ELAN3_SYS_FLAG_THREAD_BADVP   2
++#define ELAN3_SYS_FLAG_DMAFAIL                4
++#define ELAN3_SYS_FLAG_NETERR         8
++
++#define SYS_SWAP_MAGIC                0xB23C52DF
++#define SYS_EXCEPTION_MAGIC   0xC34D63E0
++
++#define EXCEPTION_GLOBAL_STRING       "elan3_exceptions"
++#define EXCEPTION_ABORT_STRING  "elan3_abortstring"
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* __ELAN3_ELANSYSCALL_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/include/elan3/elanuregs.h
+===================================================================
+--- linux-2.4.21.orig/include/elan3/elanuregs.h        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan3/elanuregs.h     2005-06-01 23:12:54.725419952 -0400
+@@ -0,0 +1,295 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN3_ELANUREGS_H
++#define __ELAN3_ELANUREGS_H
++
++#ident "$Id: elanuregs.h,v 1.10 2003/09/24 13:57:24 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/elanuregs.h,v $*/
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++/*
++ * Statistic control reg values
++ * Each 4-bit nibble of the control word specifies what statistic
++ * is to be recorded in each of the 8 statistic counters
++ */
++
++/* Count reg 0 */
++#define STC_INPUT_TRANSACTIONS                0
++#define STP_DMA_EOP_WAIT_ACK          1
++#define STP_THREAD_RUNNING            2
++#define STP_UCODE_WAIT_MEM            3
++#define STC_CACHE_WRITE_BACKS         4
++#define STC_PCI_SLAVE_READS           5
++#define STC_REG0_UNUSED6              6
++#define STP_REG0_UNUSED7              7
++
++#define STATS_REG0_NAMES {            \
++        "STC_INPUT_TRANSACTIONS",     \
++        "STP_DMA_EOP_WAIT_ACK",       \
++        "STP_THREAD_RUNNING",         \
++        "STP_UCODE_WAIT_MEM",         \
++        "STC_CACHE_WRITE_BACKS",      \
++        "STC_PCI_SLAVE_READS",        \
++        "STC_REG0_UNUSED6",           \
++        "STP_REG0_UNUSED7"            \
++}
++
++/* Count reg 1 */
++#define STC_INPUT_WRITE_BLOCKS                (0 << 4)
++#define STP_DMA_DATA_TRANSMITTING     (1 << 4)
++#define STP_THEAD_WAITING_INST                (2 << 4)
++#define STC_REG1_UNUSED3              (3 << 4)
++#define STP_FETCHING_ROUTES           (4 << 4)
++#define STC_REG1_UNUSED5              (5 << 4)
++#define STC_PCI_SLAVE_WRITES          (6 << 4)
++#define STP_PCI_SLAVE_READ_WAITING    (7 << 4)
++
++#define STATS_REG1_NAMES {            \
++      "STC_INPUT_WRITE_BLOCKS",               \
++        "STP_DMA_DATA_TRANSMITTING",  \
++        "STP_THEAD_WAITING_INST",     \
++        "STC_REG1_UNUSED3",           \
++        "STP_FETCHING_ROUTES",        \
++        "STC_REG1_UNUSED5",           \
++        "STC_PCI_SLAVE_WRITES",       \
++        "STP_PCI_SLAVE_READ_WAITING"  \
++}
++
++/* Count reg 2 */
++#define STC_INPUT_PKTS                        (0 << 8)
++#define STP_DMA_WAITING_MEM           (1 << 8)
++#define STP_THREAD_WAIT_OPEN_PKT      (2 << 8)
++#define STC_REG2_UNUSED3              (3 << 8)
++#define STC_ROUTE_FETCHES             (4 << 8)
++#define STC_CACHE_NON_ALLOC_MISSES    (5 << 8)
++#define STC_REG2_UNUSED6              (6 << 8)
++#define STP_PCI_SLAVE_WRITE_WAITING   (7 << 8)
++
++#define STATS_REG2_NAMES {            \
++      "STC_INPUT_PKTS",                       \
++        "STP_DMA_WAITING_MEM",        \
++        "STP_THREAD_WAIT_OPEN_PKT",   \
++        "STC_REG2_UNUSED3",           \
++        "STC_ROUTE_FETCHES",          \
++        "STC_CACHE_NON_ALLOC_MISSES", \
++        "STC_REG2_UNUSED6",           \
++        "STP_PCI_SLAVE_WRITE_WAITING" \
++}
++
++/* Count reg 3 */
++#define STC_INPUT_PKTS_REJECTED               (0 << 12)
++#define STP_DMA_WAIT_NETWORK_BUSY     (1 << 12)
++#define STP_THREAD_WAIT_PACK          (2 << 12)
++#define STP_UCODE_BLOCKED_UCODE               (3 << 12)
++#define STC_TLB_HITS                  (4 << 12)
++#define STC_REG3_UNUSED5              (5 << 12)
++#define STC_PCI_MASTER_READS          (6 << 12)
++#define STP_PCI_MASTER_WRITE_WAITING  (7 << 12)
++
++#define STATS_REG3_NAMES {            \
++      "STC_INPUT_PKTS_REJECTED",      \
++        "STP_DMA_WAIT_NETWORK_BUSY",  \
++        "STP_THREAD_WAIT_PACK",       \
++        "STP_UCODE_BLOCKED_UCODE",    \
++        "STC_TLB_HITS",               \
++        "STC_REG3_UNUSED5",           \
++        "STC_PCI_MASTER_READS",       \
++        "STP_PCI_MASTER_WRITE_WAITING"\
++}
++
++/* Count reg 4 */
++#define STP_INPUT_DATA_TRANSMITTING   (0 << 16)
++#define STC_DMA_NON_CTX0_PKTS         (1 << 16)
++#define STP_THREAD_EOP_WAIT_ACK               (2 << 16)
++#define STP_UCODE_DPROC_RUNNING               (3 << 16)
++#define STC_TLB_MEM_WALKS             (4 << 16)
++#define STC_REG4_UNUSED5              (5 << 16)
++#define STC_PCI_MASTER_WRITES         (6 << 16)
++#define STP_PCI_MASTER_READ_WAITING   (7 << 16)
++
++#define STATS_REG4_NAMES {            \
++      "STP_INPUT_DATA_TRANSMITTING",  \
++        "STC_DMA_NON_CTX0_PKTS",      \
++        "STP_THREAD_EOP_WAIT_ACK",    \
++        "STP_UCODE_DPROC_RUNNING",    \
++        "STC_TLB_MEM_WALKS",          \
++        "STC_REG4_UNUSED5",           \
++        "STC_PCI_MASTER_WRITES",      \
++        "STP_PCI_MASTER_READ_WAITING" \
++}
++
++/* Count reg 5 */
++#define STP_INPUT_WAITING_NETWORK_DATA        (0 << 20)
++#define STC_DMA_NON_CTX0_PKTS_REJECTED        (1 << 20)
++#define STP_THREAD_WAITING_DATA               (2 << 20)
++#define STP_UCODE_CPROC_RUNNING               (3 << 20)
++#define STP_THREAD_TRANSMITTING_DATA  (4 << 20)
++#define STP_PCI_WAITING_MAIN          (5 << 20)
++#define STC_REG5_UNUSED6              (6 << 20)
++#define STC_REG5_UNUSED7              (7 << 20)
++
++#define STATS_REG5_NAMES {                    \
++      "STP_INPUT_WAITING_NETWORK_DATA",               \
++        "STC_DMA_NON_CTX0_PKTS_REJECTED",     \
++        "STP_THREAD_WAITING_DATA",            \
++        "STP_UCODE_CPROC_RUNNING",            \
++        "STP_THREAD_TRANSMITTING_DATA",       \
++        "STP_PCI_WAITING_MAIN",               \
++        "STC_REG5_UNUSED6",                   \
++        "STC_REG5_UNUSED7"                    \
++}
++
++/* Count reg 6 */
++#define STP_INPUT_WAITING_MEMORY      (0 << 24)
++#define STC_DMA_CTX0_PKTS             (1 << 24)
++#define STP_THREAD_WAITING_MEMORY     (2 << 24)
++#define STP_UCODE_TPROC_RUNNING               (3 << 24)
++#define STC_CACHE_HITS                        (4 << 24)
++#define STP_PCI_WAITING_ELAN          (5 << 24)
++#define STC_REG6_UNUSED4              (6 << 24)
++#define STC_REG6_UNUSED7              (7 << 24)
++
++#define STATS_REG6_NAMES {            \
++      "STP_INPUT_WAITING_MEMORY",     \
++        "STC_DMA_CTX0_PKTS",          \
++        "STP_THREAD_WAITING_MEMORY",  \
++        "STP_UCODE_TPROC_RUNNING",    \
++        "STC_CACHE_HITS",             \
++        "STP_PCI_WAITING_ELAN",       \
++        "STC_REG6_UNUSED4",           \
++        "STC_REG6_UNUSED7"            \
++}
++
++/* Count reg 7 */
++#define STC_INPUT_CTX_FILTER_FILL     (0 << 28)       
++#define STC_DMA_CTX0_PKTS_REJECTED    (1 << 28)
++#define STP_THREAD_WAIT_NETWORK_BUSY  (2 << 28)
++#define STP_UCODE_IPROC_RUNNING               (3 << 28)
++#define STP_TLB_MEM_WALKING           (4 << 28)
++#define STC_CACHE_ALLOC_MISSES                (5 << 28)
++#define STP_PCI_DATA_TRANSFER         (6 << 28)
++#define STC_REG7_UNUSED7              (7 << 28)
++
++#define STATS_REG7_NAMES {            \
++      "STC_INPUT_CTX_FILTER_FILL",    \
++        "STC_DMA_CTX0_PKTS_REJECTED", \
++        "STP_THREAD_WAIT_NETWORK_BUSY",\
++        "STP_UCODE_IPROC_RUNNING",    \
++        "STP_TLB_MEM_WALKING",        \
++        "STC_CACHE_ALLOC_MISSES",     \
++        "STP_PCI_DATA_TRANSFER",      \
++        "STC_REG7_UNUSED7"            \
++}
++
++#define STATS_REG_NAMES { \
++    STATS_REG0_NAMES, \
++    STATS_REG1_NAMES, \
++    STATS_REG2_NAMES, \
++    STATS_REG3_NAMES, \
++    STATS_REG4_NAMES, \
++    STATS_REG5_NAMES, \
++    STATS_REG6_NAMES, \
++    STATS_REG7_NAMES, \
++}
++
++extern const char *elan3_stats_names[8][8];
++
++#define ELAN3_STATS_NAME(COUNT, CONTROL) (elan3_stats_names[(COUNT)][(CONTROL) & 7])
++
++typedef volatile union e3_StatsControl
++{
++   E3_uint32 StatsControl;
++   struct
++   {
++#if defined(__LITTLE_ENDIAN__)
++      E3_uint32 StatCont0:4;
++      E3_uint32 StatCont1:4;
++      E3_uint32 StatCont2:4;
++      E3_uint32 StatCont3:4;
++      E3_uint32 StatCont4:4;
++      E3_uint32 StatCont5:4;
++      E3_uint32 StatCont6:4;
++      E3_uint32 StatCont7:4;
++#else
++      E3_uint32 StatCont7:4;
++      E3_uint32 StatCont6:4;
++      E3_uint32 StatCont5:4;
++      E3_uint32 StatCont4:4;
++      E3_uint32 StatCont3:4;
++      E3_uint32 StatCont2:4;
++      E3_uint32 StatCont1:4;
++      E3_uint32 StatCont0:4;
++#endif
++   } s;
++} E3_StatsControl;
++
++typedef volatile union e3_StatsCount
++{
++   E3_uint64    ClockStat; 
++   struct
++   {
++       E3_uint32 ClockLSW;    /* read only */
++       E3_uint32 StatsCount;
++   } s;
++} E3_StatsCount;
++
++typedef volatile union e3_clock
++{
++   E3_uint64 NanoSecClock;
++   struct
++   {
++      E3_uint32 ClockLSW;
++      E3_uint32 ClockMSW;
++   } s;
++} E3_Clock;
++#define E3_TIME( X ) ((X).NanoSecClock)
++
++typedef volatile struct _E3_User_Regs
++{
++   E3_StatsCount      StatCounts[8];
++   E3_StatsCount      InstCount;
++   E3_uint32          pad0;
++   E3_StatsControl    StatCont;
++   E3_Clock           Clock;
++   E3_uint32          pad1[0x7ea];
++} E3_User_Regs;
++
++typedef volatile struct _E3_CommandPort 
++{
++   E3_Addr            PutDma;         /* 0x000 */
++   E3_uint32          Pad1;
++   E3_Addr            GetDma;         /* 0x008 */
++   E3_uint32          Pad2;
++   E3_Addr            RunThread;      /* 0x010 */
++   E3_uint32          Pad3[3];
++   E3_Addr            WaitEvent0;     /* 0x020 */
++   E3_uint32          Pad4;
++   E3_Addr            WaitEvent1;     /* 0x028 */
++   E3_uint32          Pad5;
++   E3_Addr            SetEvent;       /* 0x030 */
++   E3_uint32          Pad6[3];
++   E3_uint32          Pad7[0x7f0];    /* Fill out to an 8K page */
++} E3_CommandPort;
++/* Should have the new structures for the top four pages of the elan3 space */
++
++#define E3_COMMANDPORT_SIZE   (sizeof (E3_CommandPort))
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* __ELAN3_ELANUREGS_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/include/elan3/elanvp.h
+===================================================================
+--- linux-2.4.21.orig/include/elan3/elanvp.h   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan3/elanvp.h        2005-06-01 23:12:54.726419800 -0400
+@@ -0,0 +1,165 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef _ELAN3_ELANVP_H
++#define _ELAN3_ELANVP_H
++
++#ident "$Id: elanvp.h,v 1.45 2004/06/18 09:28:06 mike Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/elanvp.h,v $ */
++
++#include <elan3/e3types.h>
++#include <elan/bitmap.h>
++#include <elan/capability.h>
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++/*
++ * Context number allocation.
++ * [0-31]     system contexts
++ * [32-63]    hardware test
++ * [64-1023]  available
++ * [1024-2047]        RMS allocatable
++ * [2048-4095]        kernel comms data contexts
++ */
++#define ELAN3_KCOMM_CONTEXT_NUM               0x001                   /* old kernel comms context (system) */
++#define ELAN3_CM_CONTEXT_NUM          0x002                   /* new cluster member ship comms context (system) */
++#define ELAN3_MRF_CONTEXT_NUM         0x003                   /* multi-rail kernel comms context */
++#define ELAN3_DMARING_BASE_CONTEXT_NUM        0x010                   /* 16 contexts for dma ring issue (system) */
++#define ELAN3_DMARING_TOP_CONTEXT_NUM 0x01f
++
++#define ELAN3_HWTEST_BASE_CONTEXT_NUM 0x020                   /* reserved for hardware test */
++#define ELAN3_HWTEST_TOP_CONTEXT_NUM  0x03f
++
++#define ELAN3_KCOMM_BASE_CONTEXT_NUM  0x800                   /* kernel comms data transfer contexts */
++#define ELAN3_KCOMM_TOP_CONTEXT_NUM   0xfff
++
++#define ELAN3_HWTEST_CONTEXT(ctx)             ((ctx) >= ELAN3_HWTEST_BASE_CONTEXT_NUM && \
++                                       (ctx) <= ELAN3_HWTEST_TOP_CONTEXT_NUM)    
++
++#define ELAN3_SYSTEM_CONTEXT(ctx)     (((ctx) & SYS_CONTEXT_BIT) != 0 || \
++                                       (ctx) < E3_NUM_CONTEXT_0 || \
++                                       (ctx) >= ELAN3_KCOMM_BASE_CONTEXT_NUM)
++
++/* Maximum number of virtual processes */
++#define ELAN3_MAX_VPS         (16384)
++
++#define ELAN3_INVALID_PROCESS (0x7fffffff)            /* A GUARANTEED invalid process # */
++#define ELAN3_INVALID_NODE    (0xFFFF)
++#define ELAN3_INVALID_CONTEXT (0xFFFF)
++
++
++
++#if defined(__KERNEL__) && !defined(__ELAN3__)
++
++/*
++ * Contexts are accessible via Elan capabilities,
++ * for each context that can be "attached" to there
++ * is a ELAN3_CTXT_INFO structure created by its
++ * "owner".  This also "remembers" all remote 
++ * segments that have "blazed" a trail to it.
++ *
++ * If the "owner" goes away the soft info is 
++ * destroyed when it is no longer "attached" or 
++ * "referenced" by a remote segment.
++ *
++ * If the owner changes the capability, then 
++ * the soft info must be not "referenced" or 
++ * "attached" before a new process can "attach"
++ * to it.
++ */
++
++_NOTE(MUTEX_PROTECTS_DATA(elan3_dev::InfoLock,
++                        elan3_info::Next elan3_info::Prev elan3_info::Device elan3_info::Owner
++                        elan3_info::Capability elan3_info::AttachedCapability elan3_info::Context))
++_NOTE(MUTEX_PROTECTS_DATA(elan3_dev::IntrLock,
++                        elan3_info::Nacking elan3_info::Disabled))
++_NOTE(DATA_READABLE_WITHOUT_LOCK(elan3_info::Context elan3_info::Device elan3_info::Capability))
++
++#endif /* __KERNEL__ */
++
++#define LOW_ROUTE_PRIORITY    0
++#define HIGH_ROUTE_PRIORITY   1
++
++#define DEFAULT_ROUTE_TIMEOUT 3
++#define DEFAULT_ROUTE_PRIORITY        LOW_ROUTE_PRIORITY
++
++
++/* a small route is 4 flits (8 bytes), a big route  */
++/* is 8 flits (16 bytes) - each packed route is 4 bits */
++/* so giving us a maximum of 28 as flit0 does not contain */
++/* packed routes */
++#define MAX_FLITS             8
++#define MAX_PACKED            28
++
++/* bit definitions for 64 bit route pointer */
++#define ROUTE_VALID           (1ULL << 63)
++#define ROUTE_PTR             (1ULL << 62)
++#define ROUTE_CTXT_SHIFT      48
++#define ROUTE_PTR_MASK                ((1ull << ROUTE_CTXT_SHIFT)-1)
++#define ROUTE_GET_CTXT          ((VAL >> ROUTE_CTXT_SHIFT) & 0x3fff )
++
++#define SMALL_ROUTE(flits, context)   (((E3_uint64) (flits)[0] <<  0) | ((E3_uint64) (flits)[1] << 16) | \
++                                       ((E3_uint64) (flits)[2] << 32) | ((E3_uint64) (context) << ROUTE_CTXT_SHIFT) | \
++                                       ROUTE_VALID)
++
++#define BIG_ROUTE_PTR(paddr, context) ((E3_uint64) (paddr) | ((E3_uint64) context << ROUTE_CTXT_SHIFT) | ROUTE_VALID | ROUTE_PTR)
++
++#define BIG_ROUTE0(flits)             (((E3_uint64) (flits)[0] <<  0) | ((E3_uint64) (flits)[1] << 16) | \
++                                       ((E3_uint64) (flits)[2] << 32) | ((E3_uint64) (flits)[3] << 48))
++#define BIG_ROUTE1(flits)             (((E3_uint64) (flits)[4] <<  0) | ((E3_uint64) (flits)[5] << 16) | \
++                                       ((E3_uint64) (flits)[6] << 32) | ((E3_uint64) (flits)[7] << 48))
++
++
++/* defines for first flit of a route */
++#define FIRST_HIGH_PRI                (1 << 15)
++#define FIRST_AGE(Val)                ((Val) << 11)
++#define FIRST_TIMEOUT(Val)    ((Val) << 9)
++#define FIRST_PACKED(X)               ((X) << 7)
++#define FIRST_ROUTE(Val)      (Val)
++#define FIRST_ADAPTIVE                (0x30)
++#define FIRST_BCAST_TREE      (0x20)
++#define FIRST_MYLINK          (0x10)
++#define FIRST_BCAST(Top, Bot) (0x40 | ((Top) << 3) | (Bot))
++
++/* defines for 3 bit packed entries for subsequent flits */
++#define PACKED_ROUTE(Val)     (8 | (Val))
++#define PACKED_ADAPTIVE               (3)
++#define PACKED_BCAST_TREE     (2)
++#define PACKED_MYLINK         (1)
++#define PACKED_BCAST0(Top,Bot)        (4 | (Bot & 3))
++#define PACKED_BCAST1(Top,Bot)        ((Top << 1) | (Bot >> 2))
++
++/* ---------------------------------------------------------- 
++ * elan3_route functions 
++ * return ELAN3_ROUTE_xxx codes
++ * ---------------------------------------------------------- */
++
++#define ELAN3_ROUTE_SUCCESS        (0x00)
++#define ELAN3_ROUTE_SYSCALL_FAILED (0x01)
++#define ELAN3_ROUTE_INVALID        (0x02)
++#define ELAN3_ROUTE_TOO_LONG       (0x04)
++#define ELAN3_ROUTE_LOAD_FAILED    (0x08)
++#define ELAN3_ROUTE_PROC_RANGE     (0x0f)
++#define ELAN3_ROUTE_INVALID_LEVEL  (0x10)
++#define ELAN3_ROUTE_OCILATES       (0x20)
++#define ELAN3_ROUTE_WRONG_DEST     (0x40)
++#define ELAN3_ROUTE_TURN_LEVEL     (0x80)
++#define ELAN3_ROUTE_NODEID_UNKNOWN (0xf0)
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* _ELAN3_ELANVP_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/include/elan3/events.h
+===================================================================
+--- linux-2.4.21.orig/include/elan3/events.h   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan3/events.h        2005-06-01 23:12:54.726419800 -0400
+@@ -0,0 +1,183 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef _ELAN3_EVENTS_H
++#define _ELAN3_EVENTS_H
++
++#ident "$Id: events.h,v 1.45 2003/09/24 13:57:24 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/events.h,v $*/
++
++/*
++ * Alignments for events, event queues and blockcopy blocks.
++ */
++#define E3_EVENT_ALIGN                (8)
++#define E3_QUEUE_ALIGN                (32)
++#define E3_BLK_ALIGN          (64)
++#define E3_BLK_SIZE           (64)
++#define E3_BLK_PATTERN                (0xfeedface)
++
++#define E3_EVENT_FREE         ((0 << 4) | EV_WCOPY)
++#define E3_EVENT_PENDING      ((1 << 4) | EV_WCOPY)
++#define E3_EVENT_ACTIVE               ((2 << 4) | EV_WCOPY)
++#define E3_EVENT_FIRED                ((3 << 4) | EV_WCOPY)
++#define E3_EVENT_FAILED               ((4 << 4) | EV_WCOPY)
++#define E3_EVENT_DONE         ((5 << 4) | EV_WCOPY)
++#define E3_EVENT_PRIVATE      ((6 << 4) | EV_WCOPY)
++
++/*
++ * Event values and masks
++ *
++ * Block Copy event   xxxxxxxxxxxxxxxx1
++ * Chained event      30 bit ptr ....0x
++ * Event interrupt    29 bit cookie 01x
++ * Dma event          28 bit ptr   011x
++ * thread event               28 bit ptr   111x
++ */
++#define EV_CLEAR              (0x00000000)
++#define EV_TYPE_BCOPY         (0x00000001)
++#define EV_TYPE_CHAIN         (0x00000000)
++#define EV_TYPE_EVIRQ         (0x00000002)
++#define EV_TYPE_DMA           (0x00000006)
++#define EV_TYPE_THREAD                (0x0000000e)
++
++#define EV_TYPE_BCOPY_BYTE    (0)
++#define EV_TYPE_BCOPY_HWORD   (1)
++#define EV_TYPE_BCOPY_WORD    (2)
++#define EV_TYPE_BCOPY_DWORD   (3)
++
++/*
++ * Data type is in the lowest two bits of the Dest pointer.
++ */
++#define EV_BCOPY_DTYPE_MASK   (3)
++#define EV_WCOPY              (1)     /* [DestWord] = Source */
++#define EV_BCOPY              (0)     /* [DestBlock] = [SourceBlock] */
++
++#define EV_TYPE_MASK          (0x0000000e)
++#define EV_TYPE_MASK_BCOPY    (0x00000001)
++#define EV_TYPE_MASK_CHAIN    (0x00000002)
++#define EV_TYPE_MASK_EVIRQ    (0x00000006)
++#define EV_TYPE_MASK_DMA      (0x0000000e)
++#define EV_TYPE_MASK_THREAD   (0x0000000e)
++#define EV_TYPE_MASK2         (0x0000000f)
++
++/*
++ * Min/Max size for Elan queue entries 
++ */
++#define E3_QUEUE_MIN  E3_BLK_SIZE
++#define E3_QUEUE_MAX  (E3_BLK_SIZE * 5)
++
++/*
++ * Elan queue state bits
++ */
++#define E3_QUEUE_FULL (1<<0)
++#define E3_QUEUE_LOCKED       (1<<8)
++
++#ifndef _ASM
++
++typedef union _E3_Event
++{
++   E3_uint64  ev_Int64;
++   struct {
++      volatile E3_int32       u_Count;
++      E3_uint32               u_Type;
++   } ev_u;
++} E3_Event;
++
++typedef union _E3_BlockCopyEvent
++{
++   E3_uint64 ev_ForceAlign;
++   struct E3_BlockCopyEvent_u {
++      volatile E3_int32       u_Count;
++      E3_uint32               u_Type;
++      E3_Addr         u_Source;
++      E3_Addr         u_Dest;   /* lowest bits are the data type for endian conversion */
++   } ev_u;
++} E3_BlockCopyEvent;
++
++#define ev_Type   ev_u.u_Type
++#define ev_Count  ev_u.u_Count
++#define ev_Source ev_u.u_Source
++#define ev_Dest   ev_u.u_Dest
++
++typedef union _E3_WaitEvent0
++{
++   E3_uint64            we_ForceAlign;
++   struct {
++      E3_Addr         u_EventLoc;
++      E3_int32        u_WaitCount;
++   } we_u;
++} E3_WaitEvent0;
++#define we_EventLoc we_u.u_EventLoc
++#define we_WaitCount we_u.u_WaitCount
++
++typedef union _E3_Event_Blk
++{
++    E3_uint8  eb_Bytes[E3_BLK_SIZE];
++    E3_uint32 eb_Int32[E3_BLK_SIZE/sizeof (E3_uint32)];
++    E3_uint64 eb_Int64[E3_BLK_SIZE/sizeof (E3_uint64)];
++} E3_Event_Blk;
++
++/* We make eb_done the last word of the blk
++ * so that we can guarantee the rest of the blk is
++ * correct when this value is set.
++ * However, when the TPORT code copies the envelope
++ * info into the blk, it uses a dword endian type.
++ * Thus we must correct for this when initialising
++ * the pattern in the Elan SDRAM blk (eeb_done)
++ */
++#define eb_done eb_Int32[15]
++#define eeb_done eb_Int32[15^WordEndianFlip]
++
++#define EVENT_WORD_READY(WORD) (*((volatile E3_uint32 *) WORD) != 0)
++#define EVENT_BLK_READY(BLK) (((volatile E3_Event_Blk *) (BLK))->eb_done != 0)
++#define EVENT_READY(EVENT)   (((volatile E3_Event *) (EVENT))->ev_Count <= 0)
++
++#define ELAN3_WAIT_EVENT (0)
++#define ELAN3_POLL_EVENT (-1)
++
++#define SETUP_EVENT_TYPE(ptr,typeval) (((unsigned long)(ptr)) | (typeval))
++
++#define E3_RESET_BCOPY_BLOCK(BLK)                                                     \
++      do {                                                                            \
++              (BLK)->eb_done = 0;                                                     \
++      } while (0)
++
++typedef struct e3_queue
++{
++   volatile E3_uint32 q_state;        /* queue is full=bit0, queue is locked=bit8 */
++   volatile E3_Addr   q_bptr;         /* block aligned ptr to current back item */
++   E3_uint32          q_size;         /* size of queue item; 0x1 <= size <= (0x40 * 5) */
++   E3_Addr            q_top;          /* block aligned ptr to last queue item */
++   E3_Addr            q_base;         /* block aligned ptr to first queue item */
++   volatile E3_Addr   q_fptr;         /* block aligned ptr to current front item */
++   E3_Event           q_event;        /* queue event */
++} E3_Queue;
++
++typedef struct e3_blockcopy_queue
++{
++   volatile E3_uint32 q_state;        /* queue is full=bit0, queue is locked=bit8 */
++   volatile E3_Addr   q_bptr;         /* block aligned ptr to current back item */
++   E3_uint32          q_size;         /* size of queue item; 0x1 <= size <= (0x40 * 5) */
++   E3_Addr            q_top;          /* block aligned ptr to last queue item */
++   E3_Addr            q_base;         /* block aligned ptr to first queue item */
++   volatile E3_Addr   q_fptr;         /* block aligned ptr to current front item */
++   E3_BlockCopyEvent  q_event;        /* queue event */
++   E3_uint32          q_pad[6];
++} E3_BlockCopyQueue;
++
++#define E3_QUEUE_EVENT_OFFSET 24
++#define QUEUE_FULL(Q)         ((Q)->q_state & E3_QUEUE_FULL)          
++
++#endif /* ! _ASM */
++
++#endif /* _ELAN3_EVENTS_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/include/elan3/intrinsics.h
+===================================================================
+--- linux-2.4.21.orig/include/elan3/intrinsics.h       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan3/intrinsics.h    2005-06-01 23:12:54.727419648 -0400
+@@ -0,0 +1,320 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Limited.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef _ELAN3_INTRINSICS_H
++#define _ELAN3_INTRINSICS_H
++
++#ident "$Id: intrinsics.h,v 1.35 2003/09/24 13:57:24 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/intrinsics.h,v $ */
++
++#include <elan3/e3types.h>
++#include <elan3/events.h>
++
++/* 
++ * This file contains definitions of the macros for accessing the QSW
++ * specific instructions, as if they were functions.
++ * The results from the function 
++ */
++
++#define C_ACK_OK      0                       /* return from c_close() */
++#define C_ACK_TESTFAIL        1                       /* return from c_close() */
++#define C_ACK_DISCARD 2                       /* return from c_close() */
++#define C_ACK_ERROR   3                       /* return from c_close() */
++
++/*
++ * Elan asi's for tproc block accesses
++ */
++#define EASI_BYTE     0
++#define EASI_HALF     1
++#define EASI_WORD     2
++#define EASI_DOUBLE   3
++
++#if defined(__ELAN3__) && !defined (_ASM)
++
++extern inline void c_abort(void) 
++{
++    asm volatile (".word 0x0000               ! die you thread you " : : );
++}
++
++extern inline void c_suspend(void) 
++{
++    asm volatile (
++      "set 1f, %%i7                   ! RevB bug fix. get address of the wakeup inst\n"
++      "andcc %%i7,0x4,%%g0            ! RevB bug fix. check alignment\n"
++      "bne 1f                         ! RevB bug fix. jump to other alignment\n"
++      "nop                            ! RevB bug fix. delay slot\n"
++      "ldd [%%i7],%%i6                ! RevB bug fix. data fetch of instructions\n"
++      "suspend                        ! do the real suspend\n"
++      "1: add %%i7,5*4,%%i7           ! RevB bug fix. Point i7 to first ldblock\n"
++      "ldd [%%i7],%%i6                ! RevB bug fix. data fetch of instructions\n"
++      "suspend                        ! do the real suspend\n" : : );
++}
++
++extern inline int c_close(void) 
++{
++    register int rc asm("o0");
++
++    asm volatile ("close %0" : "=r" (rc) : );
++
++    return (rc);
++}
++
++extern inline int c_close_cookie(volatile E3_uint32 *cookiep, E3_uint32 next)
++{
++    register int rc asm("o0");
++
++    asm volatile ("close      %0              ! close the packet\n"
++                "bz,a         1f              ! ack received\n"
++                "st           %1, [%2]        ! update cookie on ack\n"
++                "1:                           ! label for not-ack\n"
++                : "=r" (rc) : "r" (next), "r" (cookiep));
++
++    return (rc);
++}
++
++extern inline void c_break_busywait(void)
++{
++    asm volatile (
++      "breaktest                      ! test to see if break necessary\n"
++      "bpos 1f                        ! no other thread ready\n"
++      "nop                            ! delay slot\n"
++      "sub     %%sp,3*8*4,%%sp        ! Space to save the registers\n"
++      "stblock %%g0,[%%sp+0]          ! save the globals\n"
++      "stblock %%i0,[%%sp+8*4]        ! save the ins\n"
++      "stblock %%l0,[%%sp+16*4]       ! save the locals\n"
++      "set 2f, %%i7                   ! RevB bug fix. get address of the wakeup inst\n"
++      "andcc %%i7,0x4,%%g0            ! RevB bug fix. check alignment\n"
++      "bne 3f                         ! RevB bug fix. jump to other alignment\n"
++      "nop                            ! RevB bug fix. delay slot\n"
++      "ldd [%%i7],%%i6                ! RevB bug fix. data fetch of instructions\n"
++      "break                          ! do the real break\n"
++      "2: b 4f                        ! RevB bug fix. Branch over other alignment case\n"
++      " ldblock [%%sp+16*4],%%l0      ! RevB bug fix. restore locals in delay slot\n"
++      "3: add %%i7,5*4,%%i7           ! RevB bug fix. Point i7 to first ldblock\n"
++      "ldd [%%i7],%%i6                ! RevB bug fix. data fetch of instructions\n"
++      "break                          ! do the real break\n"
++      "ldblock [%%sp+16*4],%%l0       ! restore locals\n"
++      "4: ldblock [%%sp+8*4], %%i0    ! restore ins\n"
++      "ldblock [%%sp+0],%%g0          ! restore globals\n"
++      "add     %%sp,3*8*4,%%sp        ! restore stack pointer\n"
++      "1: " : : );
++}
++
++extern inline void c_break(void)
++{
++    asm volatile (
++      "breaktest                      ! test to see if break necessary\n"
++      "bne 1f                         ! haven't exceeded our inst count yet\n"
++      "nop                            ! delay slot\n"
++      "sub     %%sp,3*8*4,%%sp        ! Space to save the registers\n"
++      "stblock %%g0,[%%sp+0]          ! save the globals\n"
++      "stblock %%i0,[%%sp+8*4]        ! save the ins\n"
++      "stblock %%l0,[%%sp+16*4]       ! save the locals\n"
++      "set 2f, %%i7                   ! RevB bug fix. get address of the wakeup inst\n"
++      "andcc %%i7,0x4,%%g0            ! RevB bug fix. check alignment\n"
++      "bne 3f                         ! RevB bug fix. jump to other alignment\n"
++      "nop                            ! RevB bug fix. delay slot\n"
++      "ldd [%%i7],%%i6                ! RevB bug fix. data fetch of instructions\n"
++      "break                          ! do the real break\n"
++      "2: b 4f                        ! RevB bug fix. Branch over other alignment case\n"
++      " ldblock [%%sp+16*4],%%l0      ! RevB bug fix. restore locals in delay slot\n"
++      "3: add %%i7,5*4,%%i7           ! RevB bug fix. Point i7 to first ldblock\n"
++      "ldd [%%i7],%%i6                ! RevB bug fix. data fetch of instructions\n"
++      "break                          ! do the real break\n"
++      "ldblock [%%sp+16*4],%%l0       ! restore locals\n"
++      "4: ldblock [%%sp+8*4], %%i0    ! restore ins\n"
++      "ldblock [%%sp+0],%%g0          ! restore globals\n"
++      "add     %%sp,3*8*4,%%sp        ! restore stack pointer\n"
++      "1: " : : );
++}
++
++extern inline void c_open( const int arg ) 
++{
++    asm volatile ("open %0" : : "r" (arg) );
++    asm volatile ("nop; nop; nop; nop");
++    asm volatile ("nop; nop; nop; nop");
++    asm volatile ("nop; nop; nop; nop");
++    asm volatile ("nop; nop; nop; nop");
++    asm volatile ("nop; nop; nop; nop");
++    asm volatile ("nop; nop; nop; nop");
++}
++
++extern inline void c_waitevent( volatile E3_Event *const ptr,
++                              const int count) 
++{
++    register volatile E3_Event *a_unlikely asm("o0") = ptr;
++    register int a_very_unlikely asm("o1") = count;
++
++    asm volatile (
++        "sub     %%sp,1*8*4,%%sp      ! Space to save the registers\n"
++        "stblock %%i0,[%%sp+0]                ! save the ins\n"
++      "set    2f, %%i7                ! RevB bug fix. get address of the wakeup inst\n"
++      "andcc %%i7,0x4,%%g0            ! RevB bug fix. check alignment\n"
++      "bne 3f                         ! RevB bug fix. jump to other alignment\n"
++      "nop                            ! RevB bug fix. delay slot\n"
++      "ldd [%%i7],%%i4                ! RevB bug fix. data fetch of instructions\n"
++        "waitevent                    ! do the business\n"
++      "2: b 4f                        ! RevB bug fix. Branch over other alignment case\n"
++        "  ldblock [%%sp+0],%%i0      ! RevB bug fix. restore ins in delay slot\n"
++      "3: add %%i7,5*4,%%i7           ! RevB bug fix. Point i7 to first ldblock\n"
++      "ldd [%%i7],%%i4                ! RevB bug fix. data fetch of instructions\n"
++        "waitevent                    ! do the business\n"
++        "ldblock [%%sp+0],%%i0                ! restore ins\n"
++        "4: add     %%sp,1*8*4,%%sp   ! restore stack pointer\n"
++        : /* no outputs */
++        : /* inputs */ "r" (a_unlikely), "r" (a_very_unlikely)
++        : /* clobbered */ "g0", "g1", "g2", "g3", "g4", "g5", "g6", "g7",
++                                "l0", "l1", "l2", "l3", "l4", "l5", "l6", "l7" );
++
++}
++
++#define c_sendtrans0(type,dest)                       \
++      asm volatile ("sendtrans %0, %%g0, %1" : : "i" (type), "r" (dest))
++
++#define c_sendtrans1(type,dest,arg)           \
++      asm volatile ("sendtrans %0, %2, %1" : : "i" (type), "r" (dest), "r" (arg))
++
++#define c_sendtrans2(type,dest,arg1,arg2)     \
++      do {                                    \
++           register const unsigned long a_unlikely_1 asm("o4") = arg1;                        \
++           register const unsigned long a_unlikely_2 asm("o5") = arg2;                        \
++           asm volatile ("sendtrans %0, %2, %1"                                       \
++               : : "i" (type), "r" (dest), "r" (a_unlikely_1), "r" (a_unlikely_2));   \
++      } while(0)
++
++#define c_sendmem(type,dest,ptr)              \
++      asm volatile ("sendtrans %0, [%2], %1" : : "i" (type), "r" (dest), "r" (ptr))
++
++/* Copy a single 64-byte block (src blk is read using a BYTE endian type) */
++extern inline void elan3_copy64b(void *src, void *dst)
++{
++    /* Copy 64 bytes using ldblock/stblock
++     * We save and restore the locals/ins because if we don't gcc
++     * really makes a bad job of optimisising the rest of the thread code!
++     *
++     * We force the parameters in g5, g6 so that they aren't
++     * trashed by the loadblk32 into the locals/ins
++     */
++    register void *tmp1 asm("g5") = src;
++    register void *tmp2 asm("g6") = dst;
++
++    asm volatile (
++      "and     %%sp,63,%%g7           ! Calculate stack alignment\n"
++      "sub     %%sp,2*8*4,%%sp        ! Space to save the registers\n"
++      "sub     %%sp,%%g7,%%sp         ! align stack\n" 
++      "stblock64 %%l0,[%%sp]          ! save the locals and ins\n"
++      "ldblock64a [%0]%2,%%l0         ! load 64-byte block into locals/ins\n"
++      "stblock64a %%l0,[%1]%2         ! store 64-byte block from local/ins\n"
++      "ldblock64 [%%sp],%%l0          ! restore locals and ins\n"
++      "add     %%sp,%%g7, %%sp        ! undo alignment\n"
++      "add     %%sp,2*8*4,%%sp        ! restore stack pointer\n"
++      : /* outputs */
++      : /* inputs */ "r" (tmp1), "r" (tmp2), "n" (EASI_BYTE)
++      : /* clobbered */ "g5", "g6", "g7" );
++}
++
++/* Copy a single 64-byte block (src blk is read using a WORD endian type) */
++extern inline void elan3_copy64w(void *src, void *dst)
++{
++    /* Copy 64 bytes using ldblock/stblock
++     * We save and restore the locals/ins because if we don't gcc
++     * really makes a bad job of optimisising the rest of the thread code!
++     *
++     * We force the parameters in g5, g6 so that they aren't
++     * trashed by the loadblk32 into the locals/ins
++     */
++    register void *tmp1 asm("g5") = src;
++    register void *tmp2 asm("g6") = dst;
++
++    asm volatile (
++      "and     %%sp,63,%%g7           ! Calculate stack alignment\n"
++      "sub     %%sp,2*8*4,%%sp        ! Space to save the registers\n"
++      "sub     %%sp,%%g7,%%sp         ! align stack\n" 
++      "stblock64 %%l0,[%%sp]          ! save the locals and ins\n"
++      "ldblock64a [%0]%2,%%l0         ! load 64-byte block into locals/ins\n"
++      "stblock64a %%l0,[%1]%2         ! store 64-byte block from local/ins\n"
++      "ldblock64 [%%sp],%%l0          ! restore locals and ins\n"
++      "add     %%sp,%%g7, %%sp        ! undo alignment\n"
++      "add     %%sp,2*8*4,%%sp        ! restore stack pointer\n"
++      : /* outputs */
++      : /* inputs */ "r" (tmp1), "r" (tmp2), "n" (EASI_WORD)
++      : /* clobbered */ "g5", "g6", "g7" );
++}
++
++/* Read a 64-bit value with a WORD (32-bit) endian type */
++extern inline E3_uint64 elan3_read64w( volatile E3_uint64 *const ptr )
++{
++    E3_uint64 result;
++
++    asm volatile (
++      "ldblock8a [%1]%2, %0\n"
++      : /* outputs */ "=r" (result)
++      : /* inputs */ "r" (ptr), "n" (EASI_WORD) );
++
++    return( result );
++}
++
++/* Read a 64-bit value with a DOUBLEWORD (64-bit) endian type */
++extern inline E3_uint64 elan3_read64dw( volatile E3_uint64 *const ptr )
++{
++    E3_uint64 result;
++
++    asm volatile (
++      "ldblock8a [%1]%2, %0\n"
++      : /* outputs */ "=r" (result)
++      : /* inputs */ "r" (ptr), "n" (EASI_DOUBLE) );
++
++    return( result );
++}
++
++/* Write a 32-bit value with a WORD (32-bit) endian type */
++extern inline void elan3_write64w( volatile E3_uint64 *const ptr, E3_uint64 value )
++{
++    asm volatile (
++      "stblock8a %1, [%0]%2\n"
++      : /* no outputs */
++      : /* inputs */ "r" (ptr), "r" (value), "n" (EASI_WORD) );
++}
++
++/* Write a 64-bit value with a DOUBLEWORD (64-bit) endian type */
++extern inline void elan3_write64dw( volatile E3_uint64 *const ptr, E3_uint64 value )
++{
++    asm volatile (
++      "stblock8a %1, [%0]%2\n"
++      : /* no outputs */
++      : /* inputs */ "r" (ptr), "r" (value), "n" (EASI_DOUBLE) );
++}
++
++extern inline E3_uint32 c_swap(volatile E3_uint32 *source, E3_uint32 result)
++{
++   asm volatile("swap [%1],%0\n"
++              : "=r" (result)
++              : "r" (source) ,"0" (result)
++              : "memory");
++   return result;
++}
++
++extern inline E3_uint32 c_swap_save(volatile E3_uint32 *source, const E3_uint32 result)
++{
++   register E3_uint32 a_unlikely;
++   asm volatile("" : "=r" (a_unlikely) : );
++
++   asm volatile("mov %2,%0; swap [%1],%0\n"
++              : "=r" (a_unlikely)
++              : "r" (source) ,"r" (result), "0" (a_unlikely)
++              : "memory");
++   return a_unlikely;
++}
++#endif /* (__ELAN3__) && !(_ASM) */
++
++#endif /* _ELAN3_INTRINSICS_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/include/elan3/minames.h
+===================================================================
+--- linux-2.4.21.orig/include/elan3/minames.h  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan3/minames.h       2005-06-01 23:12:54.728419496 -0400
+@@ -0,0 +1,256 @@
++{MI_WaitForRemoteDescRead,    "MI_WaitForRemoteDescRead"},
++{MI_WaitForRemoteDescRead2,   "MI_WaitForRemoteDescRead2"},
++{MI_WaitForRemoteDescRead2_seq1,      "MI_WaitForRemoteDescRead2_seq1"},
++{MI_SendRemoteDmaRoutes,      "MI_SendRemoteDmaRoutes"},
++{MI_IProcTrapped,     "MI_IProcTrapped"},
++{MI_DProcTrapped,     "MI_DProcTrapped"},
++{MI_CProcTrapped,     "MI_CProcTrapped"},
++{MI_TProcTrapped,     "MI_TProcTrapped"},
++{MI_TestWhichDmaQueue,        "MI_TestWhichDmaQueue"},
++{MI_TestWhichDmaQueue_seq1,   "MI_TestWhichDmaQueue_seq1"},
++{MI_InputRemoteDmaUpdateBPtr, "MI_InputRemoteDmaUpdateBPtr"},
++{MI_FixupQueueContextAndRemoteBit,    "MI_FixupQueueContextAndRemoteBit"},
++{MI_FixupQueueContextAndRemoteBit_seq1,       "MI_FixupQueueContextAndRemoteBit_seq1"},
++{MI_FixupQueueContextAndRemoteBit_seq2,       "MI_FixupQueueContextAndRemoteBit_seq2"},
++{MI_FixupQueueContextAndRemoteBit_seq3,       "MI_FixupQueueContextAndRemoteBit_seq3"},
++{MI_FixupQueueContextAndRemoteBit_seq4,       "MI_FixupQueueContextAndRemoteBit_seq4"},
++{MI_RunDmaCommand,    "MI_RunDmaCommand"},
++{MI_DoSendRemoteDmaDesc,      "MI_DoSendRemoteDmaDesc"},
++{MI_DequeueNonSysCntxDma,     "MI_DequeueNonSysCntxDma"},
++{MI_WaitForRemoteDescRead1,   "MI_WaitForRemoteDescRead1"},
++{MI_RemoteDmaCommand, "MI_RemoteDmaCommand"},
++{MI_WaitForRemoteRoutes,      "MI_WaitForRemoteRoutes"},
++{MI_DequeueSysCntxDma,        "MI_DequeueSysCntxDma"},
++{MI_ExecuteDmaDescriptorForQueue,     "MI_ExecuteDmaDescriptorForQueue"},
++{MI_ExecuteDmaDescriptor1,    "MI_ExecuteDmaDescriptor1"},
++{MI_ExecuteDmaDescriptor1_seq1,       "MI_ExecuteDmaDescriptor1_seq1"},
++{MI_ExecuteDmaDescriptor1_seq2,       "MI_ExecuteDmaDescriptor1_seq2"},
++{MI_ExecuteDmaDescriptor1_seq3,       "MI_ExecuteDmaDescriptor1_seq3"},
++{MI_GetNewSizeInProg, "MI_GetNewSizeInProg"},
++{MI_GetNewSizeInProg_seq1,    "MI_GetNewSizeInProg_seq1"},
++{MI_FirstBlockRead,   "MI_FirstBlockRead"},
++{MI_ExtraFirstBlockRead,      "MI_ExtraFirstBlockRead"},
++{MI_UnimplementedError,       "MI_UnimplementedError"},
++{MI_UpdateDescriptor, "MI_UpdateDescriptor"},
++{MI_UpdateDescriptor_seq1,    "MI_UpdateDescriptor_seq1"},
++{MI_UpdateDescriptor_seq2,    "MI_UpdateDescriptor_seq2"},
++{MI_UpdateDescriptor_seq3,    "MI_UpdateDescriptor_seq3"},
++{MI_UpdateDescriptor_seq4,    "MI_UpdateDescriptor_seq4"},
++{MI_UpdateDescriptor_seq5,    "MI_UpdateDescriptor_seq5"},
++{MI_GetNextSizeInProg,        "MI_GetNextSizeInProg"},
++{MI_DoStopThisDma,    "MI_DoStopThisDma"},
++{MI_DoStopThisDma_seq1,       "MI_DoStopThisDma_seq1"},
++{MI_GenNewBytesToRead,        "MI_GenNewBytesToRead"},
++{MI_WaitForEventReadTy1,      "MI_WaitForEventReadTy1"},
++{MI_WaitUpdateEvent,  "MI_WaitUpdateEvent"},
++{MI_WaitUpdateEvent_seq1,     "MI_WaitUpdateEvent_seq1"},
++{MI_DoSleepOneTickThenRunable,        "MI_DoSleepOneTickThenRunable"},
++{MI_RunEvent, "MI_RunEvent"},
++{MI_EnqueueThread,    "MI_EnqueueThread"},
++{MI_CheckContext0,    "MI_CheckContext0"},
++{MI_EnqueueDma,       "MI_EnqueueDma"},
++{MI_CprocTrapping,    "MI_CprocTrapping"},
++{MI_CprocTrapping_seq1,       "MI_CprocTrapping_seq1"},
++{MI_WaitForRemoteRoutes1,     "MI_WaitForRemoteRoutes1"},
++{MI_SetEventCommand,  "MI_SetEventCommand"},
++{MI_DoSetEvent,       "MI_DoSetEvent"},
++{MI_DoRemoteSetEventNowOrTrapQueueingDma,     "MI_DoRemoteSetEventNowOrTrapQueueingDma"},
++{MI_DoRemoteSetEventNowOrTrapQueueingDma_seq1,        "MI_DoRemoteSetEventNowOrTrapQueueingDma_seq1"},
++{MI_SendRemoteDmaRoutes2,     "MI_SendRemoteDmaRoutes2"},
++{MI_WaitForRemoteRoutes2,     "MI_WaitForRemoteRoutes2"},
++{MI_WaitEventCommandTy0,      "MI_WaitEventCommandTy0"},
++{MI_DequeueNonSysCntxDma2,    "MI_DequeueNonSysCntxDma2"},
++{MI_WaitEventCommandTy1,      "MI_WaitEventCommandTy1"},
++{MI_WaitEventCommandTy1_seq1, "MI_WaitEventCommandTy1_seq1"},
++{MI_DequeueNonSysCntxThread,  "MI_DequeueNonSysCntxThread"},
++{MI_DequeueSysCntxDma1,       "MI_DequeueSysCntxDma1"},
++{MI_DequeueSysCntxThread,     "MI_DequeueSysCntxThread"},
++{MI_TestNonSysCntxDmaQueueEmpty,      "MI_TestNonSysCntxDmaQueueEmpty"},
++{MI_TestNonSysCntxDmaQueueEmpty_seq1, "MI_TestNonSysCntxDmaQueueEmpty_seq1"},
++{MI_TestNonSysCntxDmaQueueEmpty_seq2, "MI_TestNonSysCntxDmaQueueEmpty_seq2"},
++{MI_RunThreadCommand, "MI_RunThreadCommand"},
++{MI_SetEventWaitForLastAcess, "MI_SetEventWaitForLastAcess"},
++{MI_SetEventReadWait, "MI_SetEventReadWait"},
++{MI_SetEventReadWait_seq1,    "MI_SetEventReadWait_seq1"},
++{MI_TestEventType,    "MI_TestEventType"},
++{MI_TestEventType_seq1,       "MI_TestEventType_seq1"},
++{MI_TestEventBit2,    "MI_TestEventBit2"},
++{MI_DmaDescOrBlockCopyOrChainedEvent, "MI_DmaDescOrBlockCopyOrChainedEvent"},
++{MI_RunThread,        "MI_RunThread"},
++{MI_RunThread1,       "MI_RunThread1"},
++{MI_RunThread1_seq1,  "MI_RunThread1_seq1"},
++{MI_IncDmaSysCntxBPtr,        "MI_IncDmaSysCntxBPtr"},
++{MI_IncDmaSysCntxBPtr_seq1,   "MI_IncDmaSysCntxBPtr_seq1"},
++{MI_IncDmaSysCntxBPtr_seq2,   "MI_IncDmaSysCntxBPtr_seq2"},
++{MI_WaitForCntxDmaDescRead,   "MI_WaitForCntxDmaDescRead"},
++{MI_FillInContext,    "MI_FillInContext"},
++{MI_FillInContext_seq1,       "MI_FillInContext_seq1"},
++{MI_WriteNewDescToQueue,      "MI_WriteNewDescToQueue"},
++{MI_WriteNewDescToQueue_seq1, "MI_WriteNewDescToQueue_seq1"},
++{MI_TestForQueueWrap, "MI_TestForQueueWrap"},
++{MI_TestForQueueWrap_seq1,    "MI_TestForQueueWrap_seq1"},
++{MI_TestQueueIsFull,  "MI_TestQueueIsFull"},
++{MI_TestQueueIsFull_seq1,     "MI_TestQueueIsFull_seq1"},
++{MI_TestQueueIsFull_seq2,     "MI_TestQueueIsFull_seq2"},
++{MI_CheckPsychoShitFixup,     "MI_CheckPsychoShitFixup"},
++{MI_PsychoShitFixupForcedRead,        "MI_PsychoShitFixupForcedRead"},
++{MI_PrepareDMATimeSlice,      "MI_PrepareDMATimeSlice"},
++{MI_PrepareDMATimeSlice_seq1, "MI_PrepareDMATimeSlice_seq1"},
++{MI_TProcRestartFromTrapOrTestEventBit2,      "MI_TProcRestartFromTrapOrTestEventBit2"},
++{MI_TProcRestartFromTrapOrTestEventBit2_seq1, "MI_TProcRestartFromTrapOrTestEventBit2_seq1"},
++{MI_WaitForGlobalsRead,       "MI_WaitForGlobalsRead"},
++{MI_WaitForNPCRead,   "MI_WaitForNPCRead"},
++{MI_EventInterrupt,   "MI_EventInterrupt"},
++{MI_EventInterrupt_seq1,      "MI_EventInterrupt_seq1"},
++{MI_EventInterrupt_seq2,      "MI_EventInterrupt_seq2"},
++{MI_EventInterrupt_seq3,      "MI_EventInterrupt_seq3"},
++{MI_TestSysCntxDmaQueueEmpty, "MI_TestSysCntxDmaQueueEmpty"},
++{MI_TestSysCntxDmaQueueEmpty_seq1,    "MI_TestSysCntxDmaQueueEmpty_seq1"},
++{MI_TestIfRemoteDesc, "MI_TestIfRemoteDesc"},
++{MI_DoDmaLocalSetEvent,       "MI_DoDmaLocalSetEvent"},
++{MI_DoDmaLocalSetEvent_seq1,  "MI_DoDmaLocalSetEvent_seq1"},
++{MI_DoDmaLocalSetEvent_seq2,  "MI_DoDmaLocalSetEvent_seq2"},
++{MI_DmaLoop1, "MI_DmaLoop1"},
++{MI_ExitDmaLoop,      "MI_ExitDmaLoop"},
++{MI_ExitDmaLoop_seq1, "MI_ExitDmaLoop_seq1"},
++{MI_RemoteDmaTestPAckType,    "MI_RemoteDmaTestPAckType"},
++{MI_PacketDiscardOrTestFailRecIfCCis0,        "MI_PacketDiscardOrTestFailRecIfCCis0"},
++{MI_PacketDiscardOrTestFailRecIfCCis0_seq1,   "MI_PacketDiscardOrTestFailRecIfCCis0_seq1"},
++{MI_TestNackFailIsZero2,      "MI_TestNackFailIsZero2"},
++{MI_TestNackFailIsZero3,      "MI_TestNackFailIsZero3"},
++{MI_DmaFailCountError,        "MI_DmaFailCountError"},
++{MI_TestDmaForSysCntx,        "MI_TestDmaForSysCntx"},
++{MI_TestDmaForSysCntx_seq1,   "MI_TestDmaForSysCntx_seq1"},
++{MI_TestDmaForSysCntx_seq2,   "MI_TestDmaForSysCntx_seq2"},
++{MI_TestAeqB2,        "MI_TestAeqB2"},
++{MI_TestAeqB2_seq1,   "MI_TestAeqB2_seq1"},
++{MI_GetNextDmaDescriptor,     "MI_GetNextDmaDescriptor"},
++{MI_DequeueSysCntxDma2,       "MI_DequeueSysCntxDma2"},
++{MI_InputSetEvent,    "MI_InputSetEvent"},
++{MI_PutBackSysCntxDma,        "MI_PutBackSysCntxDma"},
++{MI_PutBackSysCntxDma_seq1,   "MI_PutBackSysCntxDma_seq1"},
++{MI_PutBackSysCntxDma_seq2,   "MI_PutBackSysCntxDma_seq2"},
++{MI_InputRemoteDma,   "MI_InputRemoteDma"},
++{MI_InputRemoteDma_seq1,      "MI_InputRemoteDma_seq1"},
++{MI_WaitOneTickForWakeup1,    "MI_WaitOneTickForWakeup1"},
++{MI_SendRemoteDmaDesc,        "MI_SendRemoteDmaDesc"},
++{MI_InputLockQueue,   "MI_InputLockQueue"},
++{MI_CloseTheTrappedPacketIfCCis1,     "MI_CloseTheTrappedPacketIfCCis1"},
++{MI_CloseTheTrappedPacketIfCCis1_seq1,        "MI_CloseTheTrappedPacketIfCCis1_seq1"},
++{MI_PostDmaInterrupt, "MI_PostDmaInterrupt"},
++{MI_InputUnLockQueue, "MI_InputUnLockQueue"},
++{MI_WaitForUnLockDescRead,    "MI_WaitForUnLockDescRead"},
++{MI_SendEOPforRemoteDma,      "MI_SendEOPforRemoteDma"},
++{MI_LookAtRemoteAck,  "MI_LookAtRemoteAck"},
++{MI_InputWriteBlockQueue,     "MI_InputWriteBlockQueue"},
++{MI_WaitForSpStore,   "MI_WaitForSpStore"},
++{MI_TProcNext,        "MI_TProcNext"},
++{MI_TProcStoppedRunning,      "MI_TProcStoppedRunning"},
++{MI_InputWriteBlock,  "MI_InputWriteBlock"},
++{MI_RunDmaOrDeqNonSysCntxDma, "MI_RunDmaOrDeqNonSysCntxDma"},
++{MI_ExecuteDmaDescriptorForRun,       "MI_ExecuteDmaDescriptorForRun"},
++{MI_ConfirmQueueLock, "MI_ConfirmQueueLock"},
++{MI_DmaInputIdentify, "MI_DmaInputIdentify"},
++{MI_TProcStoppedRunning2,     "MI_TProcStoppedRunning2"},
++{MI_TProcStoppedRunning2_seq1,        "MI_TProcStoppedRunning2_seq1"},
++{MI_TProcStoppedRunning2_seq2,        "MI_TProcStoppedRunning2_seq2"},
++{MI_ThreadInputIdentify,      "MI_ThreadInputIdentify"},
++{MI_InputIdWriteAddrAndType3, "MI_InputIdWriteAddrAndType3"},
++{MI_IProcTrappedWriteStatus,  "MI_IProcTrappedWriteStatus"},
++{MI_FinishTrappingEop,        "MI_FinishTrappingEop"},
++{MI_InputTestTrans,   "MI_InputTestTrans"},
++{MI_TestAeqB3,        "MI_TestAeqB3"},
++{MI_ThreadUpdateNonSysCntxBack,       "MI_ThreadUpdateNonSysCntxBack"},
++{MI_ThreadQueueOverflow,      "MI_ThreadQueueOverflow"},
++{MI_RunContext0Thread,        "MI_RunContext0Thread"},
++{MI_RunContext0Thread_seq1,   "MI_RunContext0Thread_seq1"},
++{MI_RunContext0Thread_seq2,   "MI_RunContext0Thread_seq2"},
++{MI_RunDmaDesc,       "MI_RunDmaDesc"},
++{MI_RunDmaDesc_seq1,  "MI_RunDmaDesc_seq1"},
++{MI_RunDmaDesc_seq2,  "MI_RunDmaDesc_seq2"},
++{MI_TestAeqB, "MI_TestAeqB"},
++{MI_WaitForNonCntxDmaDescRead,        "MI_WaitForNonCntxDmaDescRead"},
++{MI_DmaQueueOverflow, "MI_DmaQueueOverflow"},
++{MI_BlockCopyEvent,   "MI_BlockCopyEvent"},
++{MI_BlockCopyEventReadBlock,  "MI_BlockCopyEventReadBlock"},
++{MI_BlockCopyWaitForReadData, "MI_BlockCopyWaitForReadData"},
++{MI_InputWriteWord,   "MI_InputWriteWord"},
++{MI_TraceSetEvents,   "MI_TraceSetEvents"},
++{MI_TraceSetEvents_seq1,      "MI_TraceSetEvents_seq1"},
++{MI_TraceSetEvents_seq2,      "MI_TraceSetEvents_seq2"},
++{MI_InputWriteDoubleWd,       "MI_InputWriteDoubleWd"},
++{MI_SendLockTransIfCCis1,     "MI_SendLockTransIfCCis1"},
++{MI_WaitForDmaRoutes1,        "MI_WaitForDmaRoutes1"},
++{MI_LoadDmaContext,   "MI_LoadDmaContext"},
++{MI_InputTestAndSetWord,      "MI_InputTestAndSetWord"},
++{MI_InputTestAndSetWord_seq1, "MI_InputTestAndSetWord_seq1"},
++{MI_GetDestEventValue,        "MI_GetDestEventValue"},
++{MI_SendDmaIdentify,  "MI_SendDmaIdentify"},
++{MI_InputAtomicAddWord,       "MI_InputAtomicAddWord"},
++{MI_LoadBFromTransD0, "MI_LoadBFromTransD0"},
++{MI_ConditionalWriteBackCCTrue,       "MI_ConditionalWriteBackCCTrue"},
++{MI_WaitOneTickForWakeup,     "MI_WaitOneTickForWakeup"},
++{MI_SendFinalUnlockTrans,     "MI_SendFinalUnlockTrans"},
++{MI_SendDmaEOP,       "MI_SendDmaEOP"},
++{MI_GenLastAddrForPsycho,     "MI_GenLastAddrForPsycho"},
++{MI_FailedAckIfCCis0, "MI_FailedAckIfCCis0"},
++{MI_FailedAckIfCCis0_seq1,    "MI_FailedAckIfCCis0_seq1"},
++{MI_WriteDmaSysCntxDesc,      "MI_WriteDmaSysCntxDesc"},
++{MI_TimesliceDmaQueueOverflow,        "MI_TimesliceDmaQueueOverflow"},
++{MI_DequeueNonSysCntxThread1, "MI_DequeueNonSysCntxThread1"},
++{MI_DequeueNonSysCntxThread1_seq1,    "MI_DequeueNonSysCntxThread1_seq1"},
++{MI_TestThreadQueueEmpty,     "MI_TestThreadQueueEmpty"},
++{MI_ClearThreadQueueIfCC,     "MI_ClearThreadQueueIfCC"},
++{MI_DequeueSysCntxThread1,    "MI_DequeueSysCntxThread1"},
++{MI_DequeueSysCntxThread1_seq1,       "MI_DequeueSysCntxThread1_seq1"},
++{MI_TProcStartUpGeneric,      "MI_TProcStartUpGeneric"},
++{MI_WaitForPCload2,   "MI_WaitForPCload2"},
++{MI_WaitForNPCWrite,  "MI_WaitForNPCWrite"},
++{MI_WaitForEventWaitAddr,     "MI_WaitForEventWaitAddr"},
++{MI_WaitForWaitEventAccess,   "MI_WaitForWaitEventAccess"},
++{MI_WaitForWaitEventAccess_seq1,      "MI_WaitForWaitEventAccess_seq1"},
++{MI_WaitForWaitEventDesc,     "MI_WaitForWaitEventDesc"},
++{MI_WaitForEventReadTy0,      "MI_WaitForEventReadTy0"},
++{MI_SendCondTestFail, "MI_SendCondTestFail"},
++{MI_InputMoveToNextTrans,     "MI_InputMoveToNextTrans"},
++{MI_ThreadUpdateSysCntxBack,  "MI_ThreadUpdateSysCntxBack"},
++{MI_FinishedSetEvent, "MI_FinishedSetEvent"},
++{MI_EventIntUpdateBPtr,       "MI_EventIntUpdateBPtr"},
++{MI_EventQueueOverflow,       "MI_EventQueueOverflow"},
++{MI_MaskLowerSource,  "MI_MaskLowerSource"},
++{MI_DmaLoop,  "MI_DmaLoop"},
++{MI_SendNullSetEvent, "MI_SendNullSetEvent"},
++{MI_SendFinalSetEvent,        "MI_SendFinalSetEvent"},
++{MI_TestNackFailIsZero1,      "MI_TestNackFailIsZero1"},
++{MI_DmaPacketTimedOutOrPacketError,   "MI_DmaPacketTimedOutOrPacketError"},
++{MI_NextPacketIsLast, "MI_NextPacketIsLast"},
++{MI_TestForZeroLengthDma,     "MI_TestForZeroLengthDma"},
++{MI_WaitForPCload,    "MI_WaitForPCload"},
++{MI_ReadInIns,        "MI_ReadInIns"},
++{MI_WaitForInsRead,   "MI_WaitForInsRead"},
++{MI_WaitForLocals,    "MI_WaitForLocals"},
++{MI_WaitForOutsWrite, "MI_WaitForOutsWrite"},
++{MI_WaitForWaitEvWrBack,      "MI_WaitForWaitEvWrBack"},
++{MI_WaitForLockRead,  "MI_WaitForLockRead"},
++{MI_TestQueueLock,    "MI_TestQueueLock"},
++{MI_InputIdWriteAddrAndType,  "MI_InputIdWriteAddrAndType"},
++{MI_InputIdWriteAddrAndType2, "MI_InputIdWriteAddrAndType2"},
++{MI_ThreadInputIdentify2,     "MI_ThreadInputIdentify2"},
++{MI_WriteIntoTrapArea0,       "MI_WriteIntoTrapArea0"},
++{MI_GenQueueBlockWrAddr,      "MI_GenQueueBlockWrAddr"},
++{MI_InputDiscardFreeLock,     "MI_InputDiscardFreeLock"},
++{MI_WriteIntoTrapArea1,       "MI_WriteIntoTrapArea1"},
++{MI_WriteIntoTrapArea2,       "MI_WriteIntoTrapArea2"},
++{MI_ResetBPtrToBase,  "MI_ResetBPtrToBase"},
++{MI_InputDoTrap,      "MI_InputDoTrap"},
++{MI_RemoteDmaCntxt0Update,    "MI_RemoteDmaCntxt0Update"},
++{MI_ClearQueueLock,   "MI_ClearQueueLock"},
++{MI_IProcTrappedBlockWriteData,       "MI_IProcTrappedBlockWriteData"},
++{MI_FillContextFilter,        "MI_FillContextFilter"},
++{MI_IProcTrapped4,    "MI_IProcTrapped4"},
++{MI_RunSysCntxDma,    "MI_RunSysCntxDma"},
++{MI_ChainedEventError,        "MI_ChainedEventError"},
++{MI_InputTrappingEOP, "MI_InputTrappingEOP"},
++{MI_CheckForRunIfZero,        "MI_CheckForRunIfZero"},
++{MI_TestForBreakOrSuspend,    "MI_TestForBreakOrSuspend"},
++{MI_SwapForRunable,   "MI_SwapForRunable"},
+Index: linux-2.4.21/include/elan3/neterr_rpc.h
+===================================================================
+--- linux-2.4.21.orig/include/elan3/neterr_rpc.h       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan3/neterr_rpc.h    2005-06-01 23:12:54.728419496 -0400
+@@ -0,0 +1,68 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN3_NETERR_RPC_H
++#define __ELAN3_NETERR_RPC_H
++
++#ident "$Id: neterr_rpc.h,v 1.20 2003/06/26 16:05:22 fabien Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/neterr_rpc.h,v $*/
++
++#define NETERR_SERVICE        "neterr-srv"
++#define NETERR_PROGRAM        ((u_long) 170002)
++#define NETERR_VERSION        ((u_long) 1)
++
++#define NETERR_NULL_RPC               0
++#define NETERR_FIXUP_RPC      1
++
++/* network error rpc timeout */
++#define NETERR_RPC_TIMEOUT    5
++
++/*
++ * XDR functions for Tru64 and Linux in userspace. 
++ *  NB Linux kernelspace xdr routines are in network_error.
++ *     and *must* be kept consistent.
++ */
++#if defined(DIGITAL_UNIX) || !defined(__KERNEL__)
++bool_t
++xdr_capability (XDR *xdrs, void *arg)
++{
++    ELAN_CAPABILITY *cap = (ELAN_CAPABILITY *) arg;
++
++    return (xdr_opaque (xdrs, (caddr_t) &cap->cap_userkey, sizeof (cap->cap_userkey)) &&
++          xdr_int (xdrs, &cap->cap_version) &&
++          xdr_u_short (xdrs, &cap->cap_type) &&
++          xdr_int (xdrs, &cap->cap_lowcontext) &&
++          xdr_int (xdrs, &cap->cap_highcontext) &&
++          xdr_int (xdrs, &cap->cap_mycontext) &&
++          xdr_int (xdrs, &cap->cap_lownode) &&
++          xdr_int (xdrs, &cap->cap_highnode) &&
++          xdr_u_int (xdrs, &cap->cap_railmask) &&
++          xdr_opaque (xdrs, (caddr_t) &cap->cap_bitmap[0], sizeof (cap->cap_bitmap)));
++}
++
++bool_t
++xdr_neterr_msg (XDR *xdrs, void *req)
++{
++    NETERR_MSG *msg = (NETERR_MSG *) req;
++
++    return (xdr_u_int (xdrs, &msg->Rail) &&
++          xdr_capability (xdrs, &msg->SrcCapability) &&
++          xdr_capability (xdrs, &msg->DstCapability) &&
++          xdr_u_int (xdrs, &msg->DstProcess) &&
++          xdr_u_int (xdrs, &msg->CookieAddr) &&
++          xdr_u_int (xdrs, &msg->CookieVProc) &&
++          xdr_u_int (xdrs, &msg->NextCookie) &&
++          xdr_u_int (xdrs, &msg->WaitForEop));
++}
++#endif /* INCLUDE_XDR_INLINE */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++#endif /* __ELAN3_NETERR_RPC_H */
+Index: linux-2.4.21/include/elan3/perm.h
+===================================================================
+--- linux-2.4.21.orig/include/elan3/perm.h     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan3/perm.h  2005-06-01 23:12:54.728419496 -0400
+@@ -0,0 +1,29 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN3_PERM_H
++#define __ELAN3_PERM_H
++
++#ident "$Id: perm.h,v 1.7 2003/09/24 13:57:24 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/perm.h,v $*/
++
++#define ELAN3_PERM_NULL               0x00
++#define ELAN3_PERM_LOCAL_READ 0x04
++#define ELAN3_PERM_READ               0x08
++#define ELAN3_PERM_NOREMOTE   0x0c
++#define ELAN3_PERM_REMOTEREAD 0x10
++#define ELAN3_PERM_REMOTEWRITE        0x14
++#define ELAN3_PERM_REMOTEEVENT        0x18
++#define ELAN3_PERM_REMOTEALL  0x1c
++
++#endif /* __ELAN3_PERM_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/include/elan3/pte.h
+===================================================================
+--- linux-2.4.21.orig/include/elan3/pte.h      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan3/pte.h   2005-06-01 23:12:54.729419344 -0400
+@@ -0,0 +1,139 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN3_PTE_H
++#define __ELAN3_PTE_H
++
++#ident "$Id: pte.h,v 1.26 2003/09/24 13:57:24 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/pte.h,v $*/
++
++#ifdef        __cplusplus
++extern          "C"
++{
++#endif
++
++#include <elan3/e3types.h>
++#include <elan3/perm.h>
++
++typedef E3_uint64 ELAN3_PTE;
++typedef E3_uint32 ELAN3_PTP;
++
++#define ELAN3_PTE_SIZE                (8)
++#define ELAN3_PTP_SIZE                (4)
++
++#define ELAN3_PTE_REF         ((E3_uint64) 1 << 63)           /* 63      - referenced bit */
++#define ELAN3_PTE_MOD         ((E3_uint64) 1 << 55)           /* 55      - modified bit */
++#define ELAN3_RM_MASK         (ELAN3_PTE_REF | ELAN3_PTE_MOD)
++
++#define ELAN3_PTE_PFN_MASK    0x0000fffffffff000ull           /* [12:48] - Physical address */
++
++#define ELAN3_PTE_BIG_ENDIAN  0x80                            /* 7       - big endian */
++#define ELAN3_PTE_64_BIT              0x40                            /* 6       - 64 bit pci address */
++#define ELAN3_PTE_LOCAL               0x20                            /* 5       - local sdram */
++
++#define ELAN3_PTE_PERM_MASK   0x1c                            /* [2:4]   - Permissions */
++#define ELAN3_PTE_PERM_SHIFT     2
++
++#define ELAN3_ET_MASK         0x3
++#define ELAN3_ET_INVALID              0x0                                     /* [0:1] */
++#define ELAN3_ET_PTP          0x1
++#define ELAN3_ET_PTE          0x2
++
++#define ELAN3_INVALID_PTP     ((ELAN3_PTP) 0)
++#define ELAN3_INVALID_PTE     ((ELAN3_PTE) 0)
++
++#define ELAN3_PTP_TYPE(ptp)   ((ptp) & ELAN3_ET_MASK)
++#define ELAN3_PTE_TYPE(pte)   ((pte) & ELAN3_ET_MASK)
++#define ELAN3_PTE_PERM(pte)   ((pte) & ELAN3_PTE_PERM_MASK)
++#define ELAN3_PTE_VALID(pte)  (((pte) & ELAN3_ET_MASK) == ELAN3_ET_PTE)
++#define ELAN3_PTE_ISREF(pte)  ((pte) & ELAN3_PTE_REF)
++#define ELAN3_PTE_ISMOD(pte)  ((pte) & ELAN3_PTE_MOD)
++#define ELAN3_PTE_WRITEABLE(pte)      (ELAN3_PERM_WRITEABLE(ELAN3_PTE_PERM(pte)))
++
++#define ELAN3_PERM_WRITEABLE(perm)    ((perm) == ELAN3_PERM_NOREMOTE || (perm) > ELAN3_PERM_REMOTEREAD)
++#define ELAN3_PERM_REMOTE(perm)               ((perm) > ELAN3_PERM_NOREMOTE)
++
++#define ELAN3_PERM_READONLY(perm)     ((perm) == ELAN3_PERM_NOREMOTE ? ELAN3_PERM_LOCAL_READ : \
++                                       (perm) > ELAN3_PERM_REMOTEREAD ? ELAN3_PERM_READ : (perm))
++#if PAGE_SHIFT == 12
++#  define ELAN3_PAGE_SHIFT    12
++#else
++#  define ELAN3_PAGE_SHIFT    13
++#endif
++
++#define ELAN3_PAGE_SIZE               (1 << ELAN3_PAGE_SHIFT)
++#define ELAN3_PAGE_OFFSET     (ELAN3_PAGE_SIZE-1)
++#define ELAN3_PAGE_MASK               (~ELAN3_PAGE_OFFSET)
++
++#if ELAN3_PAGE_SHIFT == 13
++#  define ELAN3_L3_SHIFT              5
++#else
++#  define ELAN3_L3_SHIFT              6
++#endif
++#define ELAN3_L2_SHIFT                6
++#define ELAN3_L1_SHIFT                8
++
++/* Number of entries in a given level ptbl */
++#define ELAN3_L3_ENTRIES              (1 << ELAN3_L3_SHIFT)
++#define ELAN3_L2_ENTRIES              (1 << ELAN3_L2_SHIFT)
++#define ELAN3_L1_ENTRIES              (1 << ELAN3_L1_SHIFT)
++
++/* Virtual address spanned by each entry */
++#define ELAN3_L3_SIZE         (1 << (ELAN3_PAGE_SHIFT))
++#define ELAN3_L2_SIZE         (1 << (ELAN3_L3_SHIFT+ELAN3_PAGE_SHIFT))
++#define ELAN3_L1_SIZE         (1 << (ELAN3_L3_SHIFT+ELAN3_L2_SHIFT+ELAN3_PAGE_SHIFT))
++
++/* Virtual address size of page table */
++#define ELAN3_L1_PTSIZE          (ELAN3_L1_ENTRIES * ELAN3_L1_SIZE)
++#define ELAN3_L3_PTSIZE               (ELAN3_L3_ENTRIES * ELAN3_L3_SIZE)
++#define ELAN3_L2_PTSIZE               (ELAN3_L2_ENTRIES * ELAN3_L2_SIZE)
++
++/* Mask for offset into page table */
++#define ELAN3_L1_PTOFFSET        ((ELAN3_L1_SIZE*ELAN3_L1_ENTRIES)-1)
++#define ELAN3_L3_PTOFFSET     ((ELAN3_L3_SIZE*ELAN3_L3_ENTRIES)-1)
++#define ELAN3_L2_PTOFFSET     ((ELAN3_L2_SIZE*ELAN3_L2_ENTRIES)-1)
++
++#define ELAN3_L1_INDEX(addr)  (((E3_Addr) (addr) & 0xFF000000) >> (ELAN3_L2_SHIFT+ELAN3_L3_SHIFT+ELAN3_PAGE_SHIFT))
++#define ELAN3_L2_INDEX(addr)  (((E3_Addr) (addr) & 0x00FD0000) >> (ELAN3_L3_SHIFT+ELAN3_PAGE_SHIFT))
++#define ELAN3_L3_INDEX(addr)  (((E3_Addr) (addr) & 0x0003F000) >> ELAN3_PAGE_SHIFT)
++
++#define       ELAN3_L1_BASE(addr)     (((E3_Addr)(addr)) & 0x00000000)
++#define       ELAN3_L2_BASE(addr)     (((E3_Addr)(addr)) & 0xFF000000)
++#define       ELAN3_L3_BASE(addr)     (((E3_Addr)(addr)) & 0xFFFC0000)
++
++/* Convert a page table pointer entry to the PT */
++#define PTP_TO_PT_PADDR(ptp)  ((E3_Addr)(ptp & 0xFFFFFFFC))
++
++#ifdef __KERNEL__
++/*
++ * incompatible access for permission macro.
++ */
++extern  u_char  elan3mmu_permissionTable[8];
++#define ELAN3_INCOMPAT_ACCESS(perm,access) (! (elan3mmu_permissionTable[(perm)>>ELAN3_PTE_PERM_SHIFT] & (1 << (access))))
++
++#define elan3_readptp(dev, ptp)               (elan3_sdram_readl (dev, ptp))
++#define elan3_writeptp(dev, ptp, value)       (elan3_sdram_writel (dev, ptp, value))
++#define elan3_readpte(dev, pte)               (elan3_sdram_readq (dev, pte))
++#define elan3_writepte(dev,pte, value)        (elan3_sdram_writeq (dev, pte, value))
++
++#define elan3_invalidatepte(dev, pte) (elan3_sdram_writel (dev, pte, 0))
++#define elan3_modifypte(dev,pte,new)  (elan3_sdram_writel (dev, pte, (int) (new)))
++#define elan3_clrref(dev,pte)         (elan3_sdram_writeb (dev, pte + 7)
++
++#endif        /* __KERNEL__ */
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* __ELAN3_PTE_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/include/elan3/spinlock.h
+===================================================================
+--- linux-2.4.21.orig/include/elan3/spinlock.h 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan3/spinlock.h      2005-06-01 23:12:54.729419344 -0400
+@@ -0,0 +1,195 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef _ELAN3_SPINLOCK_
++#define _ELAN3_SPINLOCK_
++
++#ident "$Id: spinlock.h,v 1.31 2003/09/24 13:57:24 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/spinlock.h,v $*/
++
++/*
++ * This spinlock is designed for main/elan processor interactions.
++ * The lock is split over Elan/Main memory in such a way that
++ * we don't end up busy-polling over the PCI.
++ * In the Elan memory we have two words; one is a sequence number
++ * and the other is a lock word for main.
++ * In main memory we have a copy of the sequence number which main polls when it is
++ * waiting for the Elan to drop the lock. Main polls this word until it becomes
++ * equal to the sequence number it sampled.
++ * The Elan drops the lock by writing the current sequence number to main memory.
++ * It is coded to always give priority to the Elan thread, and so when both go for the
++ * lock, main will back off first.
++ *
++ * 18/3/98
++ * This has been extended to avoid a starvation case where both the main and thread claim the
++ * lock and so both backoff (thread does a break). So now, main attempts to claim the
++ * lock by writing 'mainLock' then samples the 'sl_seq' and if it has the lock
++ * it sets 'mainGotLock'. The thread will now see the 'sl_mainLock' set, but will only
++ * backoff with a c_break_busywait() if 'mainGotLock' is set too.
++ */
++typedef struct elan3_spinlock_elan {
++    union {
++      volatile E3_uint64      mainLocks;              /* main writes this dble word */
++      struct {
++          volatile E3_uint32  mainLock;               /* main wants a lock */
++          volatile E3_uint32  mainGotLock;            /* main has the lock */
++      } s;
++    } sl_u;
++    volatile E3_uint32                sl_seq;                 /* thread owns this word */
++    volatile E3_uint32        sl_mainWait;            /* performance counter */
++    volatile E3_uint32                sl_elanWait;            /* performance counter */
++    volatile E3_uint32                sl_elanBusyWait;        /* performance counter */
++    /* NOTE: The lock/seq words must be within the same 32-byte Elan cache-line */
++    E3_uint64                   sl_pad[5];            /* pad to 64-bytes */
++} ELAN3_SPINLOCK_ELAN;
++
++#define sl_mainLocks sl_u.mainLocks
++#define sl_mainLock  sl_u.s.mainLock
++#define sl_mainGotLock sl_u.s.mainGotLock
++
++#define SL_MAIN_RECESSIVE     1
++#define SL_MAIN_DOMINANT      2
++
++/* Declare this as a main memory cache block for efficiency */
++typedef union elan3_spinlock_main {
++    volatile E3_uint32                sl_seq;                 /* copy of seq number updated by Elan */
++    volatile E3_uint32                sl_Int32[E3_BLK_SIZE/sizeof (E3_uint32)];
++} ELAN3_SPINLOCK_MAIN;
++
++/* Main/Main or Elan/Elan lock word */
++typedef volatile int  ELAN3_SPINLOCK;
++
++#ifdef __ELAN3__
++
++/* Main/Elan interlock */
++
++#define ELAN3_ME_SPINENTER(SLE,SL) do {\
++                              asm volatile ("! elan3_spinlock store barrier");\
++                      (SLE)->sl_seq++; \
++                      if ((SLE)->sl_mainLock) \
++                        elan3_me_spinblock(SLE, SL);\
++                      asm volatile ("! elan3_spinlock store barrier");\
++              } while (0)
++#define ELAN3_ME_SPINEXIT(SLE,SL) do {\
++                      asm volatile ("! elan3_spinlock store barrier");\
++                      (SL)->sl_seq = (SLE)->sl_seq;\
++                      asm volatile ("! elan3_spinlock store barrier");\
++              } while (0)
++
++
++/* Elan/Elan interlock */
++#define ELAN3_SPINENTER(L)    do {\
++                         asm volatile ("! store barrier");\
++                         if (c_swap ((L), 1)) elan3_spinenter(L);\
++                         asm volatile ("! store barrier");\
++                      } while (0)
++#define ELAN3_SPINEXIT(L)     do {\
++                         asm volatile ("! store barrier");\
++                         c_swap((L), 0);\
++                         asm volatile ("! store barrier");\
++                      } while (0)
++
++extern void elan3_me_spinblock (ELAN3_SPINLOCK_ELAN *sle, ELAN3_SPINLOCK_MAIN *sl);
++extern void elan3_spinenter (ELAN3_SPINLOCK *l);
++
++#else                    
++
++/* Main/Elan interlock */
++#ifdef DEBUG
++#define ELAN3_ME_SPINENTER(SDRAM,SLE,SL) do {\
++                      register E3_int32 maxLoops = 0x7fffffff;        \
++                      register E3_uint32 seq;\
++                      elan3_write32_sdram(SDRAM, (SLE) + offsetof(ELAN3_SPINLOCK_ELAN, sl_mainLock), SL_MAIN_RECESSIVE); \
++                      MEMBAR_STORELOAD(); \
++                      seq = elan3_read32_sdram(SDRAM, (SLE) + offsetof(ELAN3_SPINLOCK_ELAN, sl_seq)); \
++                      while (seq != (SL)->sl_seq) {\
++                          elan3_write32_sdram(SDRAM, (SLE) + offsetof(ELAN3_SPINLOCK_ELAN, sl_mainLock), 0); \
++                          while ((SL)->sl_seq == (seq-1) && maxLoops--) ; \
++                          if (maxLoops < 0) { \
++                              printf("Failed to get ME lock %lx/%lx seq %d sle_seq %d sl_seq %d\n", \
++                                     SL, SLE, seq, \
++                                     elan3_read32_sdram(SDRAM, (SLE) + offsetof(ELAN3_SPINLOCK_ELAN, sl_seq)), \
++                                     (SL)->sl_seq); \
++                          } \
++                          elan3_write32_sdram(SDRAM, (SLE) + offsetof(ELAN3_SPINLOCK_ELAN, sl_mainLock), SL_MAIN_RECESSIVE); \
++                          MEMBAR_STORELOAD(); \
++                          seq = elan3_read32_sdram(SDRAM, (SLE) + offsetof(ELAN3_SPINLOCK_ELAN, sl_seq)); \
++                      }\
++                      elan3_write32_sdram(SDRAM, (SLE) + offsetof(ELAN3_SPINLOCK_ELAN, sl_mainGotLock), 1); \
++                      MEMBAR_LOADLOAD();\
++              } while (0)
++#else
++#define ELAN3_ME_SPINENTER(SDRAM,SLE,SL) do {\
++                      register E3_uint32 seq;\
++                      elan3_write32_sdram(SDRAM, SLE + offsetof(ELAN3_SPINLOCK_ELAN, sl_mainLock), SL_MAIN_RECESSIVE); \
++                      MEMBAR_STORELOAD(); \
++                      seq = elan3_read32_sdram(SDRAM, SLE + offsetof(ELAN3_SPINLOCK_ELAN, sl_seq)); \
++                      while (seq != (SL)->sl_seq) {\
++                          elan3_write32_sdram(SDRAM, SLE + offsetof(ELAN3_SPINLOCK_ELAN, sl_mainLock), 0); \
++                          while ((SL)->sl_seq == (seq-1)) ; \
++                          elan3_write32_sdram(SDRAM, SLE + offsetof(ELAN3_SPINLOCK_ELAN, sl_mainLock), SL_MAIN_RECESSIVE); \
++                          MEMBAR_STORELOAD(); \
++                          seq = elan3_read32_sdram(SDRAM, SLE + offsetof(ELAN3_SPINLOCK_ELAN, sl_seq)); \
++                      }\
++                      elan3_write32_sdram(SDRAM, SLE + offsetof(ELAN3_SPINLOCK_ELAN, sl_mainGotLock), 1); \
++                      MEMBAR_LOADLOAD();\
++              } while (0)
++#endif
++#define ELAN3_ME_FORCEENTER(SDRAM,SLE,SL) do { \
++      register E3_uint32 seq; \
++      MEMBAR_STORELOAD(); \
++      elan3_write32_sdram(SDRAM, SLE + offsetof(ELAN3_SPINLOCK_ELAN, sl_mainLock), SL_MAIN_DOMINANT); \
++      MEMBAR_STORELOAD(); \
++      seq = elan3_read32_sdram(SDRAM, SLE + offsetof(ELAN3_SPINLOCK_ELAN, sl_seq)); \
++      while (seq != (SL)->sl_seq) \
++      { \
++              /* NOTE: we MUST call elan3_usecspin here for kernel comms */\
++              while ((SL)->sl_seq == (seq)-1) \
++                      elan3_usecspin (1); \
++              seq = elan3_read32_sdram(SDRAM, SLE + offsetof(ELAN3_SPINLOCK_ELAN, sl_seq)); \
++      } \
++      elan3_write32_sdram(SDRAM, SLE + offsetof(ELAN3_SPINLOCK_ELAN, sl_mainGotLock), 1); \
++      MEMBAR_LOADLOAD(); \
++} while (0)
++
++#define ELAN3_ME_TRYENTER(SDRAM,SLE,SL,SEQ) do { \
++    elan3_write32_sdram(SDRAM, SLE + offsetof(ELAN3_SPINLOCK_ELAN, sl_mainLock), SL_MAIN_RECESSIVE); \
++    MEMBAR_STORELOAD(); \
++    SEQ = elan3_read32_sdram(SDRAM, SLE + offsetof(ELAN3_SPINLOCK_ELAN, sl_seq)); \
++} while (0)
++
++#define ELAN3_ME_CHECKENTER(SDRAM,SLE,SL,SEQ) do { \
++    if ((SEQ) == ((SL)->sl_seq)) { \
++       elan3_write32_sdram(SDRAM, SLE + offsetof(ELAN3_SPINLOCK_ELAN, sl_mainGotLock), 1); \
++       MEMBAR_LOADLOAD();\
++    } \
++    else ELAN3_ME_SPINENTER(SLE,SL); \
++} while (0)
++      
++#define ELAN3_ME_SPINEXIT(SDRAM,SLE,SL) do {\
++                      MEMBAR_STORESTORE(); \
++                      elan3_write64_sdram(SDRAM, SLE + offsetof(ELAN3_SPINLOCK_ELAN, sl_mainLocks), 0); \
++                      MEMBAR_STORESTORE(); \
++              } while (0)
++
++
++/* Main/Main */
++#define ELAN3_SPINENTER(L)    do {\
++                         while (c_swap ((L), 1)) ; \
++                      } while (0)
++#define ELAN3_SPINEXIT(L)     do {\
++                         c_swap((L), 0);\
++                      } while (0)
++#endif /* _ELAN3_ */
++
++#endif /* _ELAN3_SPINLOCK_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/include/elan3/thread.h
+===================================================================
+--- linux-2.4.21.orig/include/elan3/thread.h   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan3/thread.h        2005-06-01 23:12:54.730419192 -0400
+@@ -0,0 +1,137 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef _ELAN3_THREAD_H
++#define _ELAN3_THREAD_H
++
++#ident "$Id: thread.h,v 1.17 2002/08/09 11:23:34 addy Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/thread.h,v $*/
++
++/* Alignment for a stack frame */
++#define E3_STACK_ALIGN                (64)
++
++typedef struct _E3_Frame {
++    E3_uint32        fr_local[8];             /* saved locals (not used) */
++    E3_uint32        fr_arg[6];               /* saved arguements o0 -> o5 */
++    E3_Addr          fr_savefp;               /* saved frame pointer o6 */
++    E3_Addr          fr_savepc;               /* saved program counter o7 */
++    E3_Addr          fr_stret;                /* stuct return addr */
++    E3_uint32        fr_argd[6];              /* arg dump area */
++    E3_uint32        fr_argx[1];              /* array of args past the sixth */
++} E3_Frame;
++
++typedef struct _E3_Stack {
++    E3_uint32         Locals[8];
++    E3_uint32         Ins[8];
++    E3_uint32         Globals[8];
++    E3_uint32         Outs[8];
++} E3_Stack;
++
++typedef struct _E3_OutsRegs {
++   E3_uint32  o[8];                           /* o6 == pc, o7 == fptr */
++} E3_OutsRegs;
++
++/*
++ * "Magic" value for stack pointer to be ignored.
++ */
++#define VanishingStackPointer 0x42
++
++
++/*
++ * When the Elan traps the N & Z CC bits are held in the NPC
++ * and the V & C bits are in the PC
++ */
++#define PSR_C_BIT     (1)
++#define PSR_V_BIT     (2)
++#define PSR_Z_BIT     (1)
++#define PSR_N_BIT     (2)
++#define CC_MASK               (3)
++#define PC_MASK       (~3)
++#define SP_MASK               (~3)
++
++/*
++ * Threads processor Opcodes.
++ */
++#define OPCODE_MASK           (0xC1F80000)
++#define OPCODE_IMM            (1 << 13)
++
++#define OPCODE_CLASS(instr)   ((instr) & 0xC0000000)
++#define OPCODE_CLASS_0                0x00000000
++#define OPCODE_CLASS_1                0x40000000
++#define OPCODE_CLASS_2                0x80000000
++#define OPCODE_CLASS_3                0xC0000000
++
++#define OPCODE_CPOP           0x81B00000
++#define OPCODE_Ticc           0x81D00000
++
++#define OPCODE_FCODE_SHIFT    19
++#define OPCODE_FCODE_MASK     0x1f
++#define OPCODE_NOT_ALUOP      0x01000000
++
++#define OPCODE_SLL            0x81280000
++#define OPCODE_SRL            0x81300000
++#define OPCODE_SRA            0x81380000
++
++#define OPCODE_OPEN           0x81600000
++#define OPCODE_CLOSE          0x81680000
++#define OPCODE_BREAKTEST      0x81700000
++
++#define OPCODE_BREAK          0x81a00000
++#define OPCODE_SUSPEND                0x81a80000
++#define OPCODE_WAIT           0x81b00000
++
++#define OPCODE_JMPL           0x81c00000
++
++#define OPCODE_LD             0xC0000000
++#define OPCODE_LDD            0xC0180000
++
++#define OPCODE_LDBLOCK16      0xC0900000
++#define OPCODE_LDBLOCK32      0xC0800000
++#define OPCODE_LDBLOCK64      0xC0980000
++
++#define OPCODE_ST             0xC0200000
++#define OPCODE_STD            0xC0380000
++
++#define OPCODE_SWAP           0xC0780000
++
++#define OPCODE_STBLOCK16      0xC0b00000
++#define OPCODE_STBLOCK32      0xC0a00000
++#define OPCODE_STBLOCK64      0xC0b80000
++
++#define OPCODE_CLASS0_MASK    0xC1C00000
++#define OPCODE_SETHI          0x01000000
++#define OPCODE_BICC           0x00800000
++#define OPCODE_SENDREG                0x01800000
++#define OPCODE_SENDMEM                0x01c00000
++
++#define OPCODE_BICC_BN                0x00000000
++#define OPCODE_BICC_BE                0x02000000
++#define OPCODE_BICC_BLE               0x04000000
++#define OPCODE_BICC_BL                0x06000000
++#define OPCODE_BICC_BLEU      0x08000000
++#define OPCODE_BICC_BCS               0x0A000000
++#define OPCODE_BICC_BNEG      0x0C000000
++#define OPCODE_BICC_BVS               0x0E000000
++
++#define OPCODE_BICC_MASK      0x0E000000
++#define OPCODE_BICC_ANNUL     0x20000000
++
++#define INSTR_RS2(instr)      (((instr) >>  0) & 0x1F)
++#define INSTR_RS1(instr)      (((instr) >> 14) & 0x1F)
++#define INSTR_RD(instr)               (((instr) >> 25) & 0x1F)
++#define INSTR_IMM(instr)      (((instr) & 0x1000) ? ((instr) & 0xFFF) | 0xFFFFF000 : (instr) & 0xFFF)
++
++#define Ticc_COND(instr)      INSTR_RD(instr)
++#define Ticc_TA                       8
++
++#endif /* _ELAN3_THREAD_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/include/elan3/threadlinkage.h
+===================================================================
+--- linux-2.4.21.orig/include/elan3/threadlinkage.h    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan3/threadlinkage.h 2005-06-01 23:12:54.730419192 -0400
+@@ -0,0 +1,103 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN3_THREADLINKAGE_H
++#define       __ELAN3_THREADLINKAGE_H
++
++#ident "$Id: threadlinkage.h,v 1.6 2002/08/09 11:23:34 addy Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/threadlinkage.h,v $*/
++
++#ifdef        __cplusplus
++extern "C" {
++#endif
++
++#if defined(_ASM) || defined(__LANGUAGE_ASSEMBLY__)
++
++/*
++ * Macro to define weak symbol aliases. These are similar to the ANSI-C
++ *    #pragma weak name = _name
++ * except a compiler can determine type. The assembler must be told. Hence,
++ * the second parameter must be the type of the symbol (i.e.: function,...)
++ */
++#define       ANSI_PRAGMA_WEAK(sym, stype)    \
++      .weak   sym; \
++      .type sym, #stype; \
++/* CSTYLED */ \
++sym   = _/**/sym
++
++/*
++ * ENTRY provides the standard procedure entry code
++ */
++#define       ENTRY(x) \
++      .section        ".text"; \
++      .align  4; \
++      .global x; \
++x:
++
++/*
++ * ENTRY2 is identical to ENTRY but provides two labels for the entry point.
++ */
++#define       ENTRY2(x, y) \
++      .section        ".text"; \
++      .align  4; \
++      .global x, y; \
++/* CSTYLED */ \
++x:    ; \
++y:
++
++
++/*
++ * ALTENTRY provides for additional entry points.
++ */
++#define       ALTENTRY(x) \
++      .global x; \
++x:
++
++/*
++ * DGDEF and DGDEF2 provide global data declarations.
++ *
++ * DGDEF provides a word aligned word of storage.
++ *
++ * DGDEF2 allocates "sz" bytes of storage with **NO** alignment.  This
++ * implies this macro is best used for byte arrays.
++ *
++ * DGDEF3 allocates "sz" bytes of storage with "algn" alignment.
++ */
++#define       DGDEF2(name, sz) \
++      .section        ".data"; \
++      .global name; \
++      .size   name, sz; \
++name:
++
++#define       DGDEF3(name, sz, algn) \
++      .section        ".data"; \
++      .align  algn; \
++      .global name; \
++      .size   name, sz; \
++name:
++
++#define       DGDEF(name)     DGDEF3(name, 4, 4)
++
++/*
++ * SET_SIZE trails a function and set the size for the ELF symbol table.
++ */
++#define       SET_SIZE(x) \
++      .size   x, (.-x)
++
++#endif /* _ASM || __LANGUAGE_ASSEMBLY__ */
++
++#ifdef        __cplusplus
++}
++#endif
++
++#endif        /* __ELAN3_THREADLINKAGE_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/include/elan3/threadsyscall.h
+===================================================================
+--- linux-2.4.21.orig/include/elan3/threadsyscall.h    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan3/threadsyscall.h 2005-06-01 23:12:54.730419192 -0400
+@@ -0,0 +1,64 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN3_SYSCALL_H
++#define __ELAN3_SYSCALL_H
++
++#ident "$Id: threadsyscall.h,v 1.12 2003/09/24 13:57:24 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/threadsyscall.h,v $*/
++
++/* 
++ * This file contains the system calls supported from the Elan.
++ */
++#define ELAN3_DEBUG_TRAPNUM   5       /* thread debugging trap */
++#define ELAN3_ABORT_TRAPNUM   6       /* bad abort trap */
++#define ELAN3_ELANCALL_TRAPNUM        7       /* elansyscall trap */
++#define ELAN3_SYSCALL_TRAPNUM 8       /* new syscall trap */
++
++#define ELAN3_T_SYSCALL_CODE  0       /* offsets in struct elan3_t_syscall */
++#define ELAN3_T_SYSCALL_ERRNO 4
++
++#define ELAN3_SYS_open                1
++#define ELAN3_SYS_close               2
++#define ELAN3_SYS_write               3
++#define ELAN3_SYS_read                4
++#define ELAN3_SYS_poll                5
++#define ELAN3_SYS_ioctl               6
++#define ELAN3_SYS_lseek               7
++#define ELAN3_SYS_mmap                8
++#define ELAN3_SYS_munmap      9
++#define ELAN3_SYS_kill                10
++#define ELAN3_SYS_getpid      11
++
++#if !defined(SYS_getpid) && defined(__NR_getxpid) 
++#define SYS_getpid __NR_getxpid               /* for linux */
++#endif
++
++#if !defined(_ASM) && !defined(__LANGUAGE_ASSEMBLY__)
++
++extern int     elan3_t_open (const char *, int, ...);
++extern ssize_t elan3_t_write (int, const void *, unsigned);
++extern ssize_t elan3_t_read(int, void *, unsigned);
++extern int     elan3_t_ioctl(int, int, ...);
++extern int     elan3_t_close(int);
++extern off_t   elan3_t_lseek(int filedes, off_t offset, int whence);
++
++extern caddr_t elan3_t_mmap(caddr_t, size_t, int, int, int, off_t);
++extern int     elan3_t_munmap(caddr_t, size_t);
++
++extern int     elan3_t_getpid(void);
++extern void    elan3_t_abort(char *str);
++
++#endif /* !_ASM && ! __LANGUAGE_ASSEMBLY__ */
++
++#endif /* __ELAN3_SYSCALL_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/include/elan3/trtype.h
+===================================================================
+--- linux-2.4.21.orig/include/elan3/trtype.h   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan3/trtype.h        2005-06-01 23:12:54.731419040 -0400
+@@ -0,0 +1,116 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef _ELAN3_TRTYPE_H
++#define _ELAN3_TRTYPE_H
++
++#ident "$Id: trtype.h,v 1.13 2002/08/09 11:23:34 addy Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/trtype.h,v $ */
++
++/*<15>        ackNow  */
++#define TR_SENDACK    (1 << 15)
++
++#define TR_SIZE_SHIFT 12
++#define TR_SIZE_MASK  7
++
++/*<14:12> Size        0, 1, 2, 4, 8, 16, 32, 64  Double Words
++          Bit 14 is forced to zero currently so that only size 0, 1, 2, 4 are
++        allowed    */
++
++#define TR_SIZE0      (0 << TR_SIZE_SHIFT)
++#define TR_SIZE1      (1 << TR_SIZE_SHIFT)
++#define TR_SIZE2      (2 << TR_SIZE_SHIFT)
++#define TR_SIZE4      (3 << TR_SIZE_SHIFT)
++#define TR_SIZE8      (4 << TR_SIZE_SHIFT)
++
++#define TR_64_BIT_ADDR        (1 << 11)
++#define TR_LAST_TRANS (1 << 10)
++
++#define TR_WRITEBLOCK_BIT     (1 << 9)
++#define TR_WRITEBLOCK         (TR_WRITEBLOCK_BIT | TR_SIZE8)
++
++
++#define TR_WRITEBLOCK_SIZE    64
++
++/*
++ * write-block
++ */
++/*    WriteBlock      <8:7>   Data type
++                      <6:0>   Part write size */
++#define TR_TYPE_SHIFT 7
++#define TR_TYPE_MASK  ((1 << 2) - 1)
++
++#define TR_TYPE_BYTE  0
++#define TR_TYPE_SHORT 1
++#define TR_TYPE_WORD  2
++#define TR_TYPE_DWORD 3
++
++#define TR_PARTSIZE_MASK ((1 << 7) -1)
++
++#define TR_WAIT_FOR_EOP       (1 << 8)
++
++/*
++ * trace-route format 
++ */
++#define TR_TRACEROUTE0_CHANID(val)            ((val) & 1)                     /* 0     Chan Id */
++#define TR_TRACEROUTE0_LINKID(val)            (((val) >> 1) & 7)              /* 1:3   Link Id */
++#define TR_TRACEROUTE0_REVID(val)             (((val) >> 4) & 7)              /* 4:6   Revision ID */
++#define TR_TRACEROUTE0_BCAST_TOP_PIN(val)     (((val) >> 7) & 1)              /* 7     Broadcast Top Pin (REV B) */
++#define TR_TRACEROUTE0_LNR(val)                       ((val) >> 8)                    /* 8:15  Global Link Not Ready */
++
++#define TR_TRACEROUTE1_PRIO(val)              ((val & 0xF))                   /* 0:3   Arrival Priority (REV A) */
++#define TR_TRACEROUTE1_AGE(val)                       (((val) >> 4) & 0xF)            /* 4:7   Priority Held(Age) (REV A) */
++#define TR_TRACEROUTE1_ROUTE_SELECTED(val)    ((val) & 0xFF)                  /* 0:7   Arrival age (REV B) */
++#define TR_TRACEROUTE1_BCAST_TOP(val)         (((val) >> 8) & 7)              /* 8:10  Broadcast Top */
++#define TR_TRACEROUTE1_ADAPT(val)             (((val) >> 12) & 3)             /* 12:13 This Adaptive Value (REV A) */
++#define TR_TRACEROUTE1_BCAST_BOT(val)         (((val) >> 12) & 7)             /* 12:14 Broadcast Bottom (REV B) */
++
++#define TR_TRACEROUTE2_ARRIVAL_AGE(val)               ((val) & 0xF)                   /* 0:3   Arrival Age (REV B) */
++#define TR_TRACEROUTE2_CURR_AGE(val)          (((val) >> 4) & 0xF)            /* 4:7   Current Age (REV B) */
++#define TR_TRACEROUTE2_BUSY(val)              (((val) >> 8) & 0xFF)           /* 8:15  Busy (REV B) */
++
++#define TR_TRACEROUTE_SIZE    32
++#define TR_TRACEROUTE_ENTRIES (TR_TRACEROUTE_SIZE/2)
++
++/*
++ * non-write block
++ */
++#define TR_OPCODE_MASK                (((1 << 8) - 1) |                       \
++                               (TR_SIZE_MASK << TR_SIZE_SHIFT) |      \
++                               TR_WRITEBLOCK_BIT)
++
++#define TR_NOP_TRANS          (0x0 | TR_SIZE0)
++#define TR_SETEVENT           (0x0 | TR_SIZE0 | TR_SENDACK | TR_LAST_TRANS)
++#define TR_REMOTEDMA          (0x1 | TR_SIZE4 | TR_SENDACK | TR_LAST_TRANS)
++#define TR_LOCKQUEUE          (0x2 | TR_SIZE0)
++#define TR_UNLOCKQUEUE                (0x3 | TR_SIZE0 | TR_SENDACK | TR_LAST_TRANS)
++
++#define TR_SENDDISCARD                (0x4 | TR_SIZE0)
++#define TR_TRACEROUTE         (0x5 | TR_SIZE4)
++
++#define TR_DMAIDENTIFY                (0x6 | TR_SIZE0)
++#define TR_THREADIDENTIFY     (0x7 | TR_SIZE1)
++
++#define TR_GTE                        (0x8 | TR_SIZE1)
++#define TR_LT                 (0x9 | TR_SIZE1)
++#define TR_EQ                 (0xA | TR_SIZE1)
++#define TR_NEQ                        (0xB | TR_SIZE1)
++
++#define TR_WRITEWORD          (0xC | TR_SIZE1)
++#define TR_WRITEDOUBLEWORD    (0xD | TR_SIZE1)
++#define TR_TESTANDWRITE       (0xE | TR_SIZE1)
++#define TR_ATOMICADDWORD      (0xF | TR_SIZE1 | TR_SENDACK | TR_LAST_TRANS)
++#define TR_OPCODE_TYPE_MASK   0xff
++
++
++#endif /* notdef _ELAN3_TRTYPE_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/include/elan3/urom_addrs.h
+===================================================================
+--- linux-2.4.21.orig/include/elan3/urom_addrs.h       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan3/urom_addrs.h    2005-06-01 23:12:54.731419040 -0400
+@@ -0,0 +1,262 @@
++#define MI_WaitForRemoteDescRead 0x0
++#define MI_WaitForRemoteDescRead2 0x1
++#define MI_WaitForRemoteDescRead2_seq1 0x2
++#define MI_SendRemoteDmaRoutes 0x3
++#define MI_IProcTrapped 0x4
++#define MI_DProcTrapped 0x5
++#define MI_CProcTrapped 0x6
++#define MI_TProcTrapped 0x7
++#define MI_TestWhichDmaQueue 0x8
++#define MI_TestWhichDmaQueue_seq1 0x9
++#define MI_InputRemoteDmaUpdateBPtr 0xa
++#define MI_FixupQueueContextAndRemoteBit 0xb
++#define MI_FixupQueueContextAndRemoteBit_seq1 0xc
++#define MI_FixupQueueContextAndRemoteBit_seq2 0xd
++#define MI_FixupQueueContextAndRemoteBit_seq3 0xe
++#define MI_FixupQueueContextAndRemoteBit_seq4 0xf
++#define MI_RunDmaCommand 0x10
++#define MI_DoSendRemoteDmaDesc 0x11
++#define MI_DequeueNonSysCntxDma 0x12
++#define MI_WaitForRemoteDescRead1 0x13
++#define MI_RemoteDmaCommand 0x14
++#define MI_WaitForRemoteRoutes 0x15
++#define MI_DequeueSysCntxDma 0x16
++#define MI_ExecuteDmaDescriptorForQueue 0x17
++#define MI_ExecuteDmaDescriptor1 0x18
++#define MI_ExecuteDmaDescriptor1_seq1 0x19
++#define MI_ExecuteDmaDescriptor1_seq2 0x1a
++#define MI_ExecuteDmaDescriptor1_seq3 0x1b
++#define MI_GetNewSizeInProg 0x1c
++#define MI_GetNewSizeInProg_seq1 0x1d
++#define MI_FirstBlockRead 0x1e
++#define MI_ExtraFirstBlockRead 0x1f
++#define MI_UnimplementedError 0x20
++#define MI_UpdateDescriptor 0x21
++#define MI_UpdateDescriptor_seq1 0x22
++#define MI_UpdateDescriptor_seq2 0x23
++#define MI_UpdateDescriptor_seq3 0x24
++#define MI_UpdateDescriptor_seq4 0x25
++#define MI_UpdateDescriptor_seq5 0x26
++#define MI_GetNextSizeInProg 0x27
++#define MI_DoStopThisDma 0x28
++#define MI_DoStopThisDma_seq1 0x29
++#define MI_GenNewBytesToRead 0x2a
++#define MI_WaitForEventReadTy1 0x2b
++#define MI_WaitUpdateEvent 0x2c
++#define MI_WaitUpdateEvent_seq1 0x2d
++#define MI_DoSleepOneTickThenRunable 0x2e
++#define MI_RunEvent 0x2f
++#define MI_EnqueueThread 0x30
++#define MI_CheckContext0 0x31
++#define MI_EnqueueDma 0x32
++#define MI_CprocTrapping 0x33
++#define MI_CprocTrapping_seq1 0x34
++#define MI_WaitForRemoteRoutes1 0x35
++#define MI_SetEventCommand 0x36
++#define MI_DoSetEvent 0x37
++#define MI_DoRemoteSetEventNowOrTrapQueueingDma 0x38
++#define MI_DoRemoteSetEventNowOrTrapQueueingDma_seq1 0x39
++#define MI_SendRemoteDmaRoutes2 0x3a
++#define MI_WaitForRemoteRoutes2 0x3b
++#define MI_WaitEventCommandTy0 0x3c
++#define MI_DequeueNonSysCntxDma2 0x3d
++#define MI_WaitEventCommandTy1 0x3e
++#define MI_WaitEventCommandTy1_seq1 0x3f
++#define MI_DequeueNonSysCntxThread 0x40
++#define MI_DequeueSysCntxDma1 0x41
++#define MI_DequeueSysCntxThread 0x42
++#define MI_TestNonSysCntxDmaQueueEmpty 0x43
++#define MI_TestNonSysCntxDmaQueueEmpty_seq1 0x44
++#define MI_TestNonSysCntxDmaQueueEmpty_seq2 0x45
++#define MI_RunThreadCommand 0x46
++#define MI_SetEventWaitForLastAcess 0x47
++#define MI_SetEventReadWait 0x48
++#define MI_SetEventReadWait_seq1 0x49
++#define MI_TestEventType 0x4a
++#define MI_TestEventType_seq1 0x4b
++#define MI_TestEventBit2 0x4c
++#define MI_DmaDescOrBlockCopyOrChainedEvent 0x4d
++#define MI_RunThread 0x4e
++#define MI_RunThread1 0x4f
++#define MI_RunThread1_seq1 0x50
++#define MI_IncDmaSysCntxBPtr 0x51
++#define MI_IncDmaSysCntxBPtr_seq1 0x52
++#define MI_IncDmaSysCntxBPtr_seq2 0x53
++#define MI_WaitForCntxDmaDescRead 0x54
++#define MI_FillInContext 0x55
++#define MI_FillInContext_seq1 0x56
++#define MI_WriteNewDescToQueue 0x57
++#define MI_WriteNewDescToQueue_seq1 0x58
++#define MI_TestForQueueWrap 0x59
++#define MI_TestForQueueWrap_seq1 0x5a
++#define MI_TestQueueIsFull 0x5b
++#define MI_TestQueueIsFull_seq1 0x5c
++#define MI_TestQueueIsFull_seq2 0x5d
++#define MI_CheckPsychoShitFixup 0x5e
++#define MI_PsychoShitFixupForcedRead 0x5f
++#define MI_PrepareDMATimeSlice 0x60
++#define MI_PrepareDMATimeSlice_seq1 0x61
++#define MI_TProcRestartFromTrapOrTestEventBit2 0x62
++#define MI_TProcRestartFromTrapOrTestEventBit2_seq1 0x63
++#define MI_WaitForGlobalsRead 0x64
++#define MI_WaitForNPCRead 0x65
++#define MI_EventInterrupt 0x66
++#define MI_EventInterrupt_seq1 0x67
++#define MI_EventInterrupt_seq2 0x68
++#define MI_EventInterrupt_seq3 0x69
++#define MI_TestSysCntxDmaQueueEmpty 0x6a
++#define MI_TestSysCntxDmaQueueEmpty_seq1 0x6b
++#define MI_TestIfRemoteDesc 0x6c
++#define MI_DoDmaLocalSetEvent 0x6d
++#define MI_DoDmaLocalSetEvent_seq1 0x6e
++#define MI_DoDmaLocalSetEvent_seq2 0x6f
++#define MI_DmaLoop1 0x70
++#define MI_ExitDmaLoop 0x71
++#define MI_ExitDmaLoop_seq1 0x72
++#define MI_RemoteDmaTestPAckType 0x73
++#define MI_PacketDiscardOrTestFailRecIfCCis0 0x74
++#define MI_PacketDiscardOrTestFailRecIfCCis0_seq1 0x75
++#define MI_TestNackFailIsZero2 0x76
++#define MI_TestNackFailIsZero3 0x77
++#define MI_DmaFailCountError 0x78
++#define MI_TestDmaForSysCntx 0x79
++#define MI_TestDmaForSysCntx_seq1 0x7a
++#define MI_TestDmaForSysCntx_seq2 0x7b
++#define MI_TestAeqB2 0x7c
++#define MI_TestAeqB2_seq1 0x7d
++#define MI_GetNextDmaDescriptor 0x7e
++#define MI_DequeueSysCntxDma2 0x7f
++#define MI_InputSetEvent 0x80
++#define MI_PutBackSysCntxDma 0x81
++#define MI_PutBackSysCntxDma_seq1 0x82
++#define MI_PutBackSysCntxDma_seq2 0x83
++#define MI_InputRemoteDma 0x84
++#define MI_InputRemoteDma_seq1 0x85
++#define MI_WaitOneTickForWakeup1 0x86
++#define MI_SendRemoteDmaDesc 0x87
++#define MI_InputLockQueue 0x88
++#define MI_CloseTheTrappedPacketIfCCis1 0x89
++#define MI_CloseTheTrappedPacketIfCCis1_seq1 0x8a
++#define MI_PostDmaInterrupt 0x8b
++#define MI_InputUnLockQueue 0x8c
++#define MI_WaitForUnLockDescRead 0x8d
++#define MI_SendEOPforRemoteDma 0x8e
++#define MI_LookAtRemoteAck 0x8f
++#define MI_InputWriteBlockQueue 0x90
++#define MI_WaitForSpStore 0x91
++#define MI_TProcNext 0x92
++#define MI_TProcStoppedRunning 0x93
++#define MI_InputWriteBlock 0x94
++#define MI_RunDmaOrDeqNonSysCntxDma 0x95
++#define MI_ExecuteDmaDescriptorForRun 0x96
++#define MI_ConfirmQueueLock 0x97
++#define MI_DmaInputIdentify 0x98
++#define MI_TProcStoppedRunning2 0x99
++#define MI_TProcStoppedRunning2_seq1 0x9a
++#define MI_TProcStoppedRunning2_seq2 0x9b
++#define MI_ThreadInputIdentify 0x9c
++#define MI_InputIdWriteAddrAndType3 0x9d
++#define MI_IProcTrappedWriteStatus 0x9e
++#define MI_FinishTrappingEop 0x9f
++#define MI_InputTestTrans 0xa0
++#define MI_TestAeqB3 0xa1
++#define MI_ThreadUpdateNonSysCntxBack 0xa2
++#define MI_ThreadQueueOverflow 0xa3
++#define MI_RunContext0Thread 0xa4
++#define MI_RunContext0Thread_seq1 0xa5
++#define MI_RunContext0Thread_seq2 0xa6
++#define MI_RunDmaDesc 0xa7
++#define MI_RunDmaDesc_seq1 0xa8
++#define MI_RunDmaDesc_seq2 0xa9
++#define MI_TestAeqB 0xaa
++#define MI_WaitForNonCntxDmaDescRead 0xab
++#define MI_DmaQueueOverflow 0xac
++#define MI_BlockCopyEvent 0xad
++#define MI_BlockCopyEventReadBlock 0xae
++#define MI_BlockCopyWaitForReadData 0xaf
++#define MI_InputWriteWord 0xb0
++#define MI_TraceSetEvents 0xb1
++#define MI_TraceSetEvents_seq1 0xb2
++#define MI_TraceSetEvents_seq2 0xb3
++#define MI_InputWriteDoubleWd 0xb4
++#define MI_SendLockTransIfCCis1 0xb5
++#define MI_WaitForDmaRoutes1 0xb6
++#define MI_LoadDmaContext 0xb7
++#define MI_InputTestAndSetWord 0xb8
++#define MI_InputTestAndSetWord_seq1 0xb9
++#define MI_GetDestEventValue 0xba
++#define MI_SendDmaIdentify 0xbb
++#define MI_InputAtomicAddWord 0xbc
++#define MI_LoadBFromTransD0 0xbd
++#define MI_ConditionalWriteBackCCTrue 0xbe
++#define MI_WaitOneTickForWakeup 0xbf
++#define MI_SendFinalUnlockTrans 0xc0
++#define MI_SendDmaEOP 0xc1
++#define MI_GenLastAddrForPsycho 0xc2
++#define MI_FailedAckIfCCis0 0xc3
++#define MI_FailedAckIfCCis0_seq1 0xc4
++#define MI_WriteDmaSysCntxDesc 0xc5
++#define MI_TimesliceDmaQueueOverflow 0xc6
++#define MI_DequeueNonSysCntxThread1 0xc7
++#define MI_DequeueNonSysCntxThread1_seq1 0xc8
++#define MI_TestThreadQueueEmpty 0xc9
++#define MI_ClearThreadQueueIfCC 0xca
++#define MI_DequeueSysCntxThread1 0xcb
++#define MI_DequeueSysCntxThread1_seq1 0xcc
++#define MI_TProcStartUpGeneric 0xcd
++#define MI_WaitForPCload2 0xce
++#define MI_WaitForNPCWrite 0xcf
++#define MI_WaitForEventWaitAddr 0xd0
++#define MI_WaitForWaitEventAccess 0xd1
++#define MI_WaitForWaitEventAccess_seq1 0xd2
++#define MI_WaitForWaitEventDesc 0xd3
++#define MI_WaitForEventReadTy0 0xd4
++#define MI_SendCondTestFail 0xd5
++#define MI_InputMoveToNextTrans 0xd6
++#define MI_ThreadUpdateSysCntxBack 0xd7
++#define MI_FinishedSetEvent 0xd8
++#define MI_EventIntUpdateBPtr 0xd9
++#define MI_EventQueueOverflow 0xda
++#define MI_MaskLowerSource 0xdb
++#define MI_DmaLoop 0xdc
++#define MI_SendNullSetEvent 0xdd
++#define MI_SendFinalSetEvent 0xde
++#define MI_TestNackFailIsZero1 0xdf
++#define MI_DmaPacketTimedOutOrPacketError 0xe0
++#define MI_NextPacketIsLast 0xe1
++#define MI_TestForZeroLengthDma 0xe2
++#define MI_WaitForPCload 0xe3
++#define MI_ReadInIns 0xe4
++#define MI_WaitForInsRead 0xe5
++#define MI_WaitForLocals 0xe6
++#define MI_WaitForOutsWrite 0xe7
++#define MI_WaitForWaitEvWrBack 0xe8
++#define MI_WaitForLockRead 0xe9
++#define MI_TestQueueLock 0xea
++#define MI_InputIdWriteAddrAndType 0xeb
++#define MI_InputIdWriteAddrAndType2 0xec
++#define MI_ThreadInputIdentify2 0xed
++#define MI_WriteIntoTrapArea0 0xee
++#define MI_GenQueueBlockWrAddr 0xef
++#define MI_InputDiscardFreeLock 0xf0
++#define MI_WriteIntoTrapArea1 0xf1
++#define MI_WriteIntoTrapArea2 0xf2
++#define MI_ResetBPtrToBase 0xf3
++#define MI_InputDoTrap 0xf4
++#define MI_RemoteDmaCntxt0Update 0xf5
++#define MI_ClearQueueLock 0xf6
++#define MI_IProcTrappedBlockWriteData 0xf7
++#define MI_FillContextFilter 0xf8
++#define MI_IProcTrapped4 0xf9
++#define MI_RunSysCntxDma 0xfa
++#define MI_ChainedEventError 0xfb
++#define MI_InputTrappingEOP 0xfc
++#define MI_CheckForRunIfZero 0xfd
++#define MI_TestForBreakOrSuspend 0xfe
++#define MI_SwapForRunable 0xff
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/include/elan3/vmseg.h
+===================================================================
+--- linux-2.4.21.orig/include/elan3/vmseg.h    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan3/vmseg.h 2005-06-01 23:12:54.732418888 -0400
+@@ -0,0 +1,75 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef _VM_SEG_ELAN3_H
++#define _VM_SEG_ELAN3_H
++
++#ident "$Id: vmseg.h,v 1.20 2003/09/24 13:57:24 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/vmseg.h,v $*/
++
++#include <elan3/elanuregs.h>
++
++/*
++ * This segment maps Elan registers,  it is fixed size and has 8K 
++ * pages split up as follows
++ *
++ *    ----------------------------------------
++ *    |    Performance Counters (read-only)  |
++ *    ----------------------------------------
++ *    |    Flag Page (read-only)           |
++ *    ----------------------------------------
++ *    |    Command Port                            |
++ *    ----------------------------------------
++ */
++typedef volatile struct elan3_flagstats 
++{
++    u_int     CommandFlag;
++    u_int     PageFaults;
++    u_int     CProcTraps;
++    u_int     DProcTraps;
++    u_int     TProcTraps;
++    u_int     IProcTraps;
++    u_int     EopBadAcks;
++    u_int     EopResets;
++    u_int     DmaNetworkErrors;
++    u_int     DmaIdentifyNetworkErrors;
++    u_int     ThreadIdentifyNetworkErrors;
++    u_int     DmaRetries;
++    u_int     ThreadSystemCalls;
++    u_int     ThreadElanCalls;
++    u_int     LoadVirtualProcess;
++} ELAN3_FLAGSTATS;
++
++#ifdef DIGITAL_UNIX
++typedef volatile union elan3_flagpage
++{
++    u_char       Padding[8192];
++    ELAN3_FLAGSTATS Stats;
++} ELAN3_FLAGPAGE;
++
++typedef volatile struct elan3_vmseg
++{
++    E3_CommandPort CommandPort;
++    ELAN3_FLAGPAGE  FlagPage;
++    E3_User_Regs   UserRegs;
++} ELAN3_VMSEG;
++
++#define SEGELAN3_SIZE   (sizeof (ELAN3_VMSEG))
++
++#define SEGELAN3_COMMAND_PORT 0
++#define SEGELAN3_FLAG_PAGE    1
++#define SEGELAN3_PERF_COUNTERS        2
++
++#endif /* DIGITAL_UNIX */
++
++#endif        /* _VM_SEG_ELAN3_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/include/elan3/vpd.h
+===================================================================
+--- linux-2.4.21.orig/include/elan3/vpd.h      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan3/vpd.h   2005-06-01 23:12:54.732418888 -0400
+@@ -0,0 +1,47 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "$Id: vpd.h,v 1.5 2002/08/09 11:23:34 addy Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/vpd.h,v $*/
++
++#ifndef __ELAN3_VPD_H
++#define __ELAN3_VPD_H
++
++#define LARGE_RESOURCE_BIT                    0x80
++
++#define SMALL_RESOURCE_COMPATIBLE_DEVICE_ID   0x3
++#define SMALL_RESOURCE_VENDOR_DEFINED         0xE
++#define SMALL_RESOURCE_END_TAG                        0xF
++
++#define LARGE_RESOURCE_STRING                 0x2
++#define LARGE_RESOURCE_VENDOR_DEFINED         0x4
++#define LARGE_RESOURCE_VITAL_PRODUCT_DATA     0x10
++
++#define VPD_PART_NUMBER                       "PN"
++#define VPD_FRU_PART_NUMBER           "FN"
++#define VPD_EC_LEVEL                  "EC"
++#define VPD_MANUFACTURE_ID            "MN"
++#define VPD_SERIAL_NUMBER             "SN"
++
++#define VPD_LOAD_ID                   "LI"
++#define VPD_ROM_LEVEL                 "RL"
++#define VPD_ALTERABLE_ROM_LEVEL               "RM"
++#define VPD_NETWORK_ADDRESS           "NA"
++#define VPD_DEVICE_DRIVER_LEVEL               "DD"
++#define VPD_DIAGNOSTIC_LEVEL          "DG"
++#define VPD_LOADABLE_MICROCODE_LEVEL  "LL"
++#define VPD_VENDOR_ID                 "VI"
++#define VPD_FUNCTION_NUMBER           "FU"
++#define VPD_SUBSYSTEM_VENDOR_ID               "SI"
++
++#endif /* __ELAN3_VPD_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/include/elan4/commands.h
+===================================================================
+--- linux-2.4.21.orig/include/elan4/commands.h 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan4/commands.h      2005-06-01 23:12:54.733418736 -0400
+@@ -0,0 +1,247 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN4_COMMANDS_H
++#define __ELAN4_COMMANDS_H
++
++#ident "$Id: commands.h,v 1.29 2004/06/16 15:45:02 addy Exp $"
++/*      $Source: /cvs/master/quadrics/elan4hdr/commands.h,v $*/
++
++/*
++ * This header file describes the command format for the Elan 4
++ *    See CommandFormat.doc
++ */
++
++/*
++ * Number of channels in traced elanlib_trace.c
++ */
++#define TRACE_MAX_CHANNELS    2
++
++/*
++ * Define encoding for the commands issued into the command queues
++ */
++#define RUN_THREAD_CMD       0x00
++#define OPEN_STEN_PKT_CMD    0x01
++#define WRITE_DWORD_CMD      0x02
++#define ADD_DWORD_CMD        0x03
++#define COPY64_CMD           0x05
++#define GUARD_CMD            0x06
++#define SET_EVENT_CMD        0x07
++#define SEND_TRANS_CMD       0x09
++#define INTERRUPT_CMD        0x0d
++#define RUN_DMA_CMD          0x0e
++#define SET_EVENTN_CMD       0x0f
++#define NOP_CMD                    0x17
++#define MAKE_EXT_CLEAN_CMD   0x37
++#define WAIT_EVENT_CMD       0x1f
++
++/*
++ * Define the portion of the data word the user is NOT
++ * allowed to use. This varies with Commmand type
++ */
++#define RUN_THREAD_CMD_MASK       0x03
++#define OPEN_STEN_PKT_CMD_MASK    0x0f
++#define WRITE_DWORD_CMD_MASK      0x07
++#define ADD_DWORD_CMD_MASK        0x07
++#define COPY64_CMD_MASK           0x0f
++#define GUARD_CMD_MASK            0x0f
++#define SET_EVENT_CMD_MASK        0x1f
++#define SEND_TRANS_CMD_MASK       0x1f
++#define INTERRUPT_CMD_MASK        0x0f
++#define RUN_DMA_CMD_MASK          0x0f
++#define SET_EVENTN_CMD_MASK       0x1f
++#define NOP_CMD_MASK            0x3f
++#define MAKE_EXT_CLEAN_MASK     0x3f
++#define WAIT_EVENT_CMD_MASK       0x1f
++
++#define COPY64_DATA_TYPE_SHIFT    0x4
++#define COPY64_DTYPE_BYTE       (0 << COPY64_DATA_TYPE_SHIFT)
++#define COPY64_DTYPE_SHORT      (1 << COPY64_DATA_TYPE_SHIFT)
++#define COPY64_DTYPE_WORD       (2 << COPY64_DATA_TYPE_SHIFT)
++#define COPY64_DTYPE_LONG       (3 << COPY64_DATA_TYPE_SHIFT)
++
++/*
++ * SET_EVENTN - word 1 has following form
++ * [63:5]     Event Address
++ * [4:0]      Part Set Value.
++ */
++#define SET_EVENT_PART_SET_MASK      0x1f
++
++/* OPEN_STEN_PKT_CMD 
++ *   [63:32]  Vproc
++ *   [31]     Use Test
++ *   [30:28]  unused
++ *   [27:21]  Test Acceptable PAck code
++ *   [20:16]  Test Ack Channel Number
++ *   [15:9]   Acceptable PAck code
++ *   [8:4]    Ack Channel Number (1 bit on Elan4)
++ *   [3:0]    Command type
++ */
++/* Acceptable PAck code */
++#define PACK_OK                       (1 << 0)
++#define PACK_TESTFAIL         (1 << 1)
++#define PACK_DISCARD          (1 << 2)
++#define RESTART_COUNT_ZERO    (1 << 3)
++#define PACK_ERROR            (1 << 7)
++#define PACK_TIMEOUT          (1 << 8)
++
++/*
++ *#ifndef USE_DIRTY_COMMANDS
++ *#define USE_DIRTY_COMMANDS
++ *#endif
++ */
++#ifdef USE_DIRTY_COMMANDS
++#define OPEN_PACKET_USED_MASK    0x00000000780f00e0ULL
++#define SEND_TRANS_USED_MASK     0xffffffff0000fff0ULL
++#define COPY64_WRITE_USED_MASK   0x000000000000000fULL
++#define MAIN_INT_USED_MASK       0x0000000000003ff0ULL
++#define GUARD_USED_MASK          0xfffffe007000fde0ULL
++#define DMA_TYPESIZE_USED_MASK   0x000000000000fff0ULL
++#define SETEVENTN_USED_MASK      0xffffffffffffffe0ULL
++#define NOP_USED_MASK            0xffffffffffffffc0ULL
++#define EXT_CLEAN_USED_MASK      0xffffffffffffffc0ULL
++#define WAIT_CNT_TYPE_USED_MASK  0x00000000fffff800ULL
++#else
++#define OPEN_PACKET_USED_MASK    0x0ULL
++#define SEND_TRANS_USED_MASK     0x0ULL
++#define COPY64_WRITE_USED_MASK   0x0ULL
++#define MAIN_INT_USED_MASK       0x0ULL
++#define GUARD_USED_MASK          0x0ULL
++#define DMA_TYPESIZE_USED_MASK   0x0ULL
++#define SETEVENTN_USED_MASK      0x0ULL
++#define NOP_USED_MASK            0x0ULL
++#define EXT_CLEAN_USED_MASK      0x0ULL
++#define WAIT_CNT_TYPE_USED_MASK  0x0ULL
++#endif
++
++#define OPEN_PACKET(chan, code, vproc) \
++      ((((chan) & 1) << 4) | (((code) & 0x7f) << 9) | ((E4_uint64)(vproc) << 32) | OPEN_STEN_PKT_CMD)
++
++#define OPEN_PACKET_TEST(chan, code, vproc, tchan, tcode) \
++      ((((chan) & 1) << 4) | (((code) & 0x7f) << 9) | ((E4_uint64)(vproc) << 32) | \
++       (((tchan) & 1) << 16) | (((tcode) & 0x7f) << 21) | (((E4_uint64) 1) << 31) | OPEN_STEN_PKT_CMD)
++
++/*
++ * GUARD_CMD
++ *   [63:41]  unused
++ *   [40]     Reset Restart Fail Count        // only performed if the Guard executes the next command.
++ *   [39:32]  New Restart Fail Count value
++ *   [31]     Use Test
++ *   [30:28]  unused
++ *   [27:21]  Test Acceptable PAck code
++ *   [20:16]  Test Ack Channel Number
++ *   [15:9]   unused
++ *   [8:4]    Ack Channel Number
++ *   [3:0]    Command type
++ */
++/* GUARD_CHANNEL(chan)
++ */
++#define GUARD_ALL_CHANNELS    ((1 << 9) | GUARD_CMD)
++#define GUARD_CHANNEL(chan)   ((((chan) & 1) << 4) | GUARD_CMD)
++#define GUARD_TEST(chan,code) ((1ull << 31) | (((code) & 0x7f) << 21) | (((chan) & 1) << 16))
++#define GUARD_RESET(count)    ((1ull << 40) | ((((E4_uint64) count) & 0xff) << 32))
++
++#define GUARD_CHANNEL_TEST(chan,tchan,tcode) \
++      ((((chan) & 1) << 4) | (((tchan) & 1) << 16) | (((tcode) & 0x7f) << 21) | \
++       (((E4_uint64) 1) << 31) | GUARD_CMD)
++
++/*
++ * SEND_TRANS_CMD
++ * [63:32]    unused
++ * [31:16]    transaction type
++ * [15:4]     unused
++ * [3:0]      Command type
++ */
++#define SEND_TRANS(TransType) (((TransType) << 16) | SEND_TRANS_CMD)
++
++/*
++ * Command port trace debug levels
++ */
++#define TRACE_CMD_BUFFER      0x01
++#define TRACE_CMD_TYPE                0x02
++#define TRACE_CHANNEL_OPENS   0x04
++#define TRACE_GUARDED_ATOMICS 0x08
++#define TRACE_CMD_TIMEOUT     0x10
++
++/*
++ * Commands that should be preceeded by a GUARD_CMD.
++ */
++#define IS_ATOMIC_CMD(cmd)                                                            \
++   ((cmd) == RUN_THREAD_CMD || (cmd) == ADD_DWORD_CMD || (cmd) == INTERRUPT_CMD ||    \
++    (cmd) == RUN_DMA_CMD    || (cmd) == SET_EVENT_CMD || (cmd) == SET_EVENTN_CMD ||   \
++    (cmd) == WAIT_EVENT_CMD)
++
++#ifndef _ASM
++
++/*
++ * These structures are used to build event copy command streams. They are intended to be included
++ * in a larger structure to form a self documenting command sequence that can be easily coped and manipulated.
++ */
++
++typedef struct e4_runthreadcmd
++{
++   E4_Addr    PC;
++   E4_uint64  r[6];
++} E4_RunThreadCmd;
++
++typedef E4_uint64 E4_OpenCmd;
++
++typedef struct e4_writecmd
++{
++   E4_Addr    WriteAddr;
++   E4_uint64  WriteValue;
++} E4_WriteCmd;
++
++typedef struct e4_addcmd
++{
++   E4_Addr    AddAddr;
++   E4_uint64  AddValue;
++} E4_AddCmd;
++
++typedef struct e4_copycmd
++{
++   E4_Addr    SrcAddr;
++   E4_Addr    DstAddr;
++} E4_CopyCmd;
++
++typedef E4_uint64 E4_GaurdCmd;
++typedef E4_uint64 E4_SetEventCmd;
++
++/*
++ * The data to this command must be declared as a vector after the use of this.
++ */
++typedef struct e4_sendtranscmd
++{
++   E4_Addr    Type;
++   E4_Addr    Addr;
++} E4_SendTransCmd;
++
++typedef E4_uint64 E4_IntCmd;
++
++/* The normal Dma struc can be used here. */
++
++typedef struct e4_seteventncmd
++{
++   E4_Addr    Event;
++   E4_Addr    SetCount;
++} E4_SetEventNCmd;
++
++typedef E4_uint64 E4_NopCmd;
++typedef E4_uint64 E4_MakeExtCleanCmd;
++
++typedef struct e4_waitcmd
++{
++   E4_Addr    ev_Event;
++   E4_Addr    ev_CountType;
++   E4_Addr    ev_Params[2];
++} E4_WaitCmd;
++
++#endif /* _ASM */
++
++#endif /* __ELAN4_COMMANDS_H  */
++
+Index: linux-2.4.21/include/elan4/debug.h
+===================================================================
+--- linux-2.4.21.orig/include/elan4/debug.h    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan4/debug.h 2005-06-01 23:12:54.733418736 -0400
+@@ -0,0 +1,113 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef _ELAN4_ELANDEBUG_H
++#define _ELAN4_ELANDEBUG_H
++
++#ident "$Id: debug.h,v 1.19.6.1 2005/01/18 14:36:10 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/debug.h,v $ */
++
++/* values for "type" field - note a "ctxt" is permissible */
++/* and BUFFER/CONSOLE are for explict calls to elan4_debugf() */
++#define DBG_DEVICE    ((void *) 0)
++#define DBG_USER      ((void *) 1)
++
++#define DBG_BUFFER    ((void *) 62)
++#define DBG_CONSOLE   ((void *) 63)
++#define DBG_NTYPES    64
++
++/* values for "mode" field */
++#define DBG_CONFIG    0x00000001
++#define DBG_INTR      0x00000002
++#define DBG_MAININT   0x00000004
++#define DBG_SDRAM     0x00000008
++#define DBG_MMU               0x00000010
++#define DBG_REGISTER  0x00000020
++#define DBG_CQ                0x00000040
++#define DBG_NETWORK_CTX       0x00000080
++
++#define DBG_FLUSH     0x00000100
++#define DBG_FILE      0x00000200
++#define DBG_CONTROL   0x00000400
++#define DBG_MEM               0x00000800
++
++#define DBG_PERM      0x00001000
++#define DBG_FAULT     0x00002000
++#define DBG_SWAP      0x00004000
++#define DBG_TRAP      0x00008000
++#define DBG_DDCQ      0x00010000
++#define DBG_VP                0x00020000
++#define DBG_RESTART   0x00040000
++#define DBG_RESUME    0x00080000
++#define DBG_CPROC     0x00100000
++#define DBG_DPROC     0x00200000
++#define DBG_EPROC     0x00400000
++#define DBG_IPROC     0x00800000
++#define DBG_TPROC     0x01000000
++#define DBG_IOPROC    0x02000000
++#define DBG_ROUTE     0x04000000
++#define DBG_NETERR    0x08000000
++
++#define DBG_ALL               0x7FFFFFFF
++
++
++#ifdef DEBUG_PRINTF
++
++#  define PRINTF0(type,m,fmt)                 ((elan4_debug&(m) || (type) == DBG_CONSOLE) ? elan4_debugf(type,m,fmt)                  : (void)0)
++#  define PRINTF1(type,m,fmt,a)                       ((elan4_debug&(m) || (type) == DBG_CONSOLE) ? elan4_debugf(type,m,fmt,a)                : (void)0)
++#  define PRINTF2(type,m,fmt,a,b)             ((elan4_debug&(m) || (type) == DBG_CONSOLE) ? elan4_debugf(type,m,fmt,a,b)              : (void)0)
++#  define PRINTF3(type,m,fmt,a,b,c)           ((elan4_debug&(m) || (type) == DBG_CONSOLE) ? elan4_debugf(type,m,fmt,a,b,c)            : (void)0)
++#  define PRINTF4(type,m,fmt,a,b,c,d)         ((elan4_debug&(m) || (type) == DBG_CONSOLE) ? elan4_debugf(type,m,fmt,a,b,c,d)          : (void)0)
++#  define PRINTF5(type,m,fmt,a,b,c,d,e)               ((elan4_debug&(m) || (type) == DBG_CONSOLE) ? elan4_debugf(type,m,fmt,a,b,c,d,e)        : (void)0)
++#  define PRINTF6(type,m,fmt,a,b,c,d,e,f)     ((elan4_debug&(m) || (type) == DBG_CONSOLE) ? elan4_debugf(type,m,fmt,a,b,c,d,e,f)      : (void)0)
++#  define PRINTF7(type,m,fmt,a,b,c,d,e,f,g)   ((elan4_debug&(m) || (type) == DBG_CONSOLE) ? elan4_debugf(type,m,fmt,a,b,c,d,e,f,g)    : (void)0)
++#  define PRINTF8(type,m,fmt,a,b,c,d,e,f,g,h) ((elan4_debug&(m) || (type) == DBG_CONSOLE) ? elan4_debugf(type,m,fmt,a,b,c,d,e,f,g,h)  : (void)0)
++#  define PRINTF9(type,m,fmt,a,b,c,d,e,f,g,h,i)       ((elan4_debug&(m) || (type) == DBG_CONSOLE) ? elan4_debugf(type,m,fmt,a,b,c,d,e,f,g,h,i): (void)0)
++#ifdef __GNUC__
++#  define PRINTF(type,m,args...)              ((elan4_debug&(m) || (type) == DBG_CONSOLE) ? elan4_debugf(type,m, ##args)              : (void)0)
++#endif
++#  define DBGCMD(type,m,cmd)                  ((elan4_debug&(m) || (type) == DBG_CONSOLE)  ? (void) (cmd) : (void) 0)
++
++#else
++
++#  define PRINTF0(type,m,fmt)                 (0)
++#  define PRINTF1(type,m,fmt,a)                       (0)
++#  define PRINTF2(type,m,fmt,a,b)             (0)
++#  define PRINTF3(type,m,fmt,a,b,c)           (0)
++#  define PRINTF4(type,m,fmt,a,b,c,d)         (0)
++#  define PRINTF5(type,m,fmt,a,b,c,d,e)               (0)
++#  define PRINTF6(type,m,fmt,a,b,c,d,e,f)     (0)
++#  define PRINTF7(type,m,fmt,a,b,c,d,e,f,g)   (0)
++#  define PRINTF8(type,m,fmt,a,b,c,d,e,f,g,h) (0)
++#  define PRINTF9(type,m,fmt,a,b,c,d,e,f,g,h,i)       (0)
++#ifdef __GNUC__
++#  define PRINTF(type,m,args...)
++#endif
++#  define DBGCMD(type,m,cmd)                  ((void) 0)
++
++#endif /* DEBUG_PRINTF */
++
++extern unsigned   elan4_debug;
++extern unsigned   elan4_debug_toconsole;
++extern unsigned   elan4_debug_tobuffer;
++extern unsigned   elan4_debug_display_ctxt;
++extern unsigned   elan4_debug_ignore_ctxt;
++extern unsigned   elan4_debug_ignore_type;
++
++extern void     elan4_debug_init(void);
++extern void     elan4_debug_fini(void);
++extern void       elan4_debugf (void *type, int mode, char *fmt, ...);
++extern int        elan4_debug_snapshot (caddr_t ubuffer, int len);
++extern int      elan4_debug_display (void);
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++#endif /* _ELAN4_ELANDEBUG_H */
+Index: linux-2.4.21/include/elan4/device.h
+===================================================================
+--- linux-2.4.21.orig/include/elan4/device.h   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan4/device.h        2005-06-01 23:12:54.735418432 -0400
+@@ -0,0 +1,781 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN4_ELANDEV_H
++#define __ELAN4_ELANDEV_H
++
++#ident "$Id: device.h,v 1.68.2.1 2004/11/03 14:24:32 duncant Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/device.h,v $ */
++
++#include <elan/devinfo.h>
++#include <elan/capability.h>
++
++#include <elan4/pci.h>
++#include <elan4/sdram.h>
++#include <elan4/dma.h>
++#include <elan4/events.h>
++#include <elan4/registers.h>
++
++#include <elan4/mmu.h>
++#include <elan4/trap.h>
++#include <elan4/stats.h>
++#include <elan4/neterr.h>
++
++#ifdef CONFIG_MPSAS
++#include <elan4/mpsas.h>
++#endif
++
++#if defined(LINUX)
++#include <elan4/device_Linux.h>
++#elif defined(TRU64UNIX)
++#include <elan4/device_OSF1.h>
++#elif defined(SOLARIS)
++#include <elan4/device_SunOS.h>
++#endif
++
++/*
++ * Network context number allocation.
++ * [0]          neterr fixup system context
++ * [1]          kernel comms system context
++ * [2048-4095]        kernel comms data contexts
++ */
++#define ELAN4_NETERR_CONTEXT_NUM      0x00                    /* network error fixup context number */
++#define ELAN4_KCOMM_CONTEXT_NUM               0x01                    /* kernel comms context number */
++#define ELAN4_KCOMM_BASE_CONTEXT_NUM  0x800                   /* kernel comms data transfer contexts */
++#define ELAN4_KCOMM_TOP_CONTEXT_NUM   0xfff
++
++#define ELAN4_SYSTEM_CONTEXT(ctx)  ((ctx) >= ELAN4_KCOMM_BASE_CONTEXT_NUM)
++
++typedef void (ELAN4_HALTFN)(struct elan4_dev *dev, void *arg);
++
++typedef struct elan4_haltop
++{
++    struct list_head    op_link;                              /* chain on a list */
++    E4_uint32         op_mask;                                /* Interrupt mask to see before calling function */
++    
++    ELAN4_HALTFN       *op_function;                          /* function to call */
++    void             *op_arg;                                 /* arguement to pass to function */
++} ELAN4_HALTOP;
++
++typedef void (ELAN4_DMA_FLUSHFN)(struct elan4_dev *dev, void *arg, int qfull);
++
++typedef struct elan4_dma_flushop
++{
++    struct list_head    op_link;                              /* chain on a list */
++    ELAN4_DMA_FLUSHFN  *op_function;                          /* function to call */
++    void             *op_arg;                                 /* arguement to pass to function */
++} ELAN4_DMA_FLUSHOP;
++
++typedef void (ELAN4_INTFN)(struct elan4_dev *dev, void *arg);
++
++typedef struct elan4_intop
++{
++    struct list_head    op_link;                              /* chain on a list */
++    ELAN4_INTFN        *op_function;                          /* function to call */
++    void             *op_arg;                                 /* arguement to pass to function */
++    E4_uint64         op_cookie;                              /* and main interrupt cookie */
++} ELAN4_INTOP;
++
++#define SDRAM_MIN_BLOCK_SHIFT 10
++#define SDRAM_NUM_FREE_LISTS  19                                      /* allows max 256 Mb block */
++#define SDRAM_MIN_BLOCK_SIZE  (1 << SDRAM_MIN_BLOCK_SHIFT)
++#define SDRAM_MAX_BLOCK_SIZE  (SDRAM_MIN_BLOCK_SIZE << (SDRAM_NUM_FREE_LISTS-1))
++
++#if PAGE_SHIFT < 13
++#define SDRAM_PAGE_SIZE               8192
++#define SDRAM_PGOFF_OFFSET    1
++#define SDRAM_PGOFF_MASK      (~SDRAM_PGOFF_OFFSET)
++#else
++#define SDRAM_PAGE_SIZE               PAGE_SIZE
++#define SDRAM_PGOFF_OFFSET    0
++#define SDRAM_PGOFF_MASK      (~SDRAM_PGOFF_OFFSET)
++#endif
++
++typedef struct elan4_sdram
++{
++    sdramaddr_t       b_base;                                 /* offset in sdram bar */
++    unsigned          b_size;                                 /* size of bank */
++    ioaddr_t          b_ioaddr;                               /* ioaddr where mapped into the kernel */
++    ELAN4_MAP_HANDLE  b_handle;                               /*    and mapping handle */
++    bitmap_t         *b_bitmaps[SDRAM_NUM_FREE_LISTS];        /* buddy allocator bitmaps */
++} ELAN4_SDRAM_BANK;
++
++/* command queue */
++typedef struct elan4_cq 
++{
++    struct elan4_cqa    *cq_cqa;                                      /* command queue allocator this belongs to */
++    unsigned           cq_idx;                                        /*  and which command queue this is */
++
++    sdramaddr_t                cq_space;                                      /* sdram backing up command queue */
++    unsigned           cq_size;                                       /* size value */
++    unsigned           cq_perm;                                       /* permissions */
++    ioaddr_t           cq_mapping;                                    /* mapping of command queue page */
++    ELAN4_MAP_HANDLE   cq_handle;                                     /*    and mapping handle */
++} ELAN4_CQ;
++
++/* cqtype flags to elan4_alloccq() */
++#define CQ_Priority   (1 << 0)
++#define CQ_Reorder    (1 << 1)
++
++/* command queues are allocated in chunks,so that all the
++ * command ports are in a single system page */
++#define ELAN4_CQ_PER_CQA      MAX(1, (PAGESIZE/CQ_CommandMappingSize))
++
++/* maximum number of command queues per context */
++#define ELAN4_MAX_CQA         (256 / ELAN4_CQ_PER_CQA)
++
++typedef struct elan4_cqa
++{
++    struct list_head  cqa_link;                                       /* linked together */
++    bitmap_t          cqa_bitmap[BT_BITOUL(ELAN4_CQ_PER_CQA)];        /* bitmap of which are free */
++    unsigned int        cqa_type;                                     /* allocation type */
++    unsigned int      cqa_cqnum;                                      /* base cq number */
++    unsigned int      cqa_ref;                                        /* "mappings" to a queue */
++    unsigned int      cqa_idx;                                        /* index number */
++    ELAN4_CQ          cqa_cq[ELAN4_CQ_PER_CQA];                       /* command queue entries */
++} ELAN4_CQA;
++
++#define elan4_cq2num(cq)      ((cq)->cq_cqa->cqa_cqnum + (cq)->cq_idx)
++#define elan4_cq2idx(cq)      ((cq)->cq_cqa->cqa_idx * ELAN4_CQ_PER_CQA + (cq)->cq_idx)
++
++typedef struct elan4_ctxt
++{
++    struct elan4_dev      *ctxt_dev;                                  /* device we're associated with */
++    struct list_head       ctxt_link;                                 /* chained on device */
++    
++    struct elan4_trap_ops *ctxt_ops;                                  /* client specific operations */
++
++    signed               ctxt_num;                                    /* local context number */
++
++    struct list_head     ctxt_cqalist;                                /* link list of command queue allocators */
++    bitmap_t             ctxt_cqamap[BT_BITOUL(ELAN4_MAX_CQA)];       /*   bitmap for allocating cqa_idx */
++
++    ELAN4_HASH_ENTRY     **ctxt_mmuhash[2];                           /* software hash tables */
++    spinlock_t                   ctxt_mmulock;                                /*   and spinlock. */
++} ELAN4_CTXT;
++
++typedef struct elan4_trap_ops
++{
++    void       (*op_eproc_trap) (ELAN4_CTXT *ctxt, E4_uint64 status);
++    void       (*op_cproc_trap) (ELAN4_CTXT *ctxt, E4_uint64 status, unsigned cqnum);
++    void       (*op_dproc_trap) (ELAN4_CTXT *ctxt, E4_uint64 status, unsigned unit);
++    void       (*op_tproc_trap) (ELAN4_CTXT *ctxt, E4_uint64 status);
++    void       (*op_iproc_trap) (ELAN4_CTXT *ctxt, E4_uint64 status, unsigned unit);
++    void       (*op_interrupt)  (ELAN4_CTXT *ctxt, E4_uint64 cookie);
++    void       (*op_neterrmsg)  (ELAN4_CTXT *ctxt, ELAN4_NETERR_MSG *msg);
++} ELAN4_TRAP_OPS;
++
++typedef struct elan4_route_table
++{
++    spinlock_t  tbl_lock;
++    unsigned  tbl_size;
++    sdramaddr_t tbl_entries;
++} ELAN4_ROUTE_TABLE;
++
++#ifdef ELAN4_LARGE_PAGE_SUPPORT
++#define NUM_HASH_TABLES               2
++#else
++#define NUM_HASH_TABLES               1
++#endif
++
++#define DEV_STASH_ROUTE_COUNT 20
++
++typedef struct elan4_route_ringbuf {
++    int start;
++    int end;
++    E4_VirtualProcessEntry routes[DEV_STASH_ROUTE_COUNT]; 
++} ELAN4_ROUTE_RINGBUF;
++
++#define elan4_ringbuf_init(ringbuf) memset(&ringbuf, 0, sizeof(ELAN4_ROUTE_RINGBUF));
++
++typedef struct elan4_dev
++{
++    ELAN4_CTXT                 dev_ctxt;                                      /* context for device operations */
++
++    ELAN4_DEV_OSDEP    dev_osdep;                                     /* OS specific entries */
++
++    int                        dev_instance;                                  /* device number */
++    ELAN_DEVINFO       dev_devinfo;                                   /* device information (revision etc */
++    ELAN_POSITION      dev_position;                                  /* position connected to switch */
++    ELAN_DEV_IDX       dev_idx;                                       /* device idx registered with elanmod */
++
++    kmutex_t           dev_lock;                                      /* lock for device state/references */
++    unsigned           dev_state;                                     /* device state */
++    unsigned           dev_references;                                /*  # references */
++    unsigned           dev_features;                                  /* features supported */
++
++    ioaddr_t           dev_regs;                                      /* Mapping of device registers */
++    ELAN4_MAP_HANDLE   dev_regs_handle;
++    ioaddr_t           dev_rom;                                       /* Mapping of rom */
++    ELAN4_MAP_HANDLE   dev_rom_handle;
++    ioaddr_t           dev_i2c;                                       /* Mapping of I2C registers */
++    ELAN4_MAP_HANDLE   dev_i2c_handle;
++    
++    E4_uint64          dev_sdram_cfg;                                 /* SDRAM config value (from ROM) */
++    E4_uint64          dev_sdram_initial_ecc_val;                     /* power on ECC register value */
++    int                        dev_sdram_numbanks;                            /* # banks of sdram */
++    ELAN4_SDRAM_BANK   dev_sdram_banks[SDRAM_MAX_BANKS];              /* Mapping of sdram banks */
++    spinlock_t                 dev_sdram_lock;                                /* spinlock for buddy allocator */
++    sdramaddr_t                dev_sdram_freelists[SDRAM_NUM_FREE_LISTS];
++    unsigned           dev_sdram_freecounts[SDRAM_NUM_FREE_LISTS];
++
++    sdramaddr_t                dev_cacheflush_space;                          /* sdram reserved for cache flush operation */
++
++    sdramaddr_t                dev_faultarea;                                 /* fault areas for each unit */
++    sdramaddr_t                dev_inputtraparea;                             /* trap area for trapped transactions */
++    sdramaddr_t                dev_ctxtable;                                  /* context table (E4_ContextControlBlock) */
++    int                        dev_ctxtableshift;                             /* and size (in bits) */
++
++    E4_uint32          dev_syscontrol;                                /* copy of system control register */
++    spinlock_t                 dev_syscontrol_lock;                           /*   spinlock to sequentialise modifications */
++    unsigned           dev_direct_map_pci_writes;                     /*   # counts for CONT_DIRECT_MAP_PCI_WRITES */
++
++    volatile E4_uint32         dev_intmask;                                   /* copy of interrupt mask register */
++    spinlock_t                 dev_intmask_lock;                              /*   spinlock to sequentialise modifications */
++
++    /* i2c section */
++    spinlock_t                 dev_i2c_lock;                                  /* spinlock for i2c operations */
++    unsigned int         dev_i2c_led_disabled;                                /* count of reasons led auto update disabled */
++
++    /* mmu section */
++    unsigned           dev_pagesizeval[NUM_HASH_TABLES];              /* page size value */
++    unsigned           dev_pageshift[NUM_HASH_TABLES];                        /* pageshift in bits. */
++    unsigned           dev_hashsize[NUM_HASH_TABLES];                 /* # entries in mmu hash table */
++    sdramaddr_t                dev_hashtable[NUM_HASH_TABLES];                /* mmu hash table */
++    ELAN4_HASH_ENTRY  *dev_mmuhash[NUM_HASH_TABLES];                  /*   and software shadow */
++    ELAN4_HASH_ENTRY   **dev_mmufree[NUM_HASH_TABLES];                        /*   and partially free blocks */
++    ELAN4_HASH_ENTRY    *dev_mmufreelist;                             /*   and free blocks */
++    spinlock_t           dev_mmulock;
++    E4_uint16          dev_topaddr[4];                                /* top address values */
++    unsigned char      dev_topaddrvalid;
++    unsigned char      dev_topaddrmode;
++    unsigned char      dev_pteval;                                    /* allow setting of relaxed order/dont snoop attributes */
++
++    unsigned           dev_rsvd_hashmask[NUM_HASH_TABLES];
++    unsigned           dev_rsvd_hashval[NUM_HASH_TABLES];
++
++    /* run queues */
++    sdramaddr_t                dev_comqlowpri;                                /* CProc low & high pri run queues */
++    sdramaddr_t                dev_comqhighpri;
++
++    sdramaddr_t                dev_dmaqlowpri;                                /* DProc,TProc,Interrupt queues */
++    sdramaddr_t                dev_dmaqhighpri;
++    sdramaddr_t                dev_threadqlowpri;
++    sdramaddr_t                dev_threadqhighpri;
++    sdramaddr_t                dev_interruptq;
++
++    E4_uint32          dev_interruptq_nfptr;                          /* cache next main interrupt fptr */
++    struct list_head     dev_interruptq_list;                         /*   list of operations to call when space in interruptq*/
++
++    /* command queue section */
++    sdramaddr_t                dev_cqaddr;                                    /* SDRAM address of command queues */
++    unsigned           dev_cqoffset;                                  /* offset for command queue alignment constraints */
++    unsigned           dev_cqcount;                                   /* number of command queue descriptors */
++    bitmap_t          *dev_cqamap;                                    /* bitmap for allocation */
++    spinlock_t                 dev_cqlock;                                    /* spinlock to protect bitmap */
++#ifdef CONFIG_MTRR
++    unsigned           dev_cqreorder;                                 /* offset for first re-ordering queue on revb */
++#endif
++
++    /* halt operation section */
++    struct list_head     dev_haltop_list;                             /* list of operations to call when units halted */
++    E4_uint32          dev_haltop_mask;                               /* mask of which ones to halt */
++    E4_uint32          dev_haltop_active;                             /* mask of which haltops are executing */
++    spinlock_t                 dev_haltop_lock;                               /*    and their spinlock */
++
++    struct {
++      struct list_head list;                                          /* list of halt operations for DMAs */
++      ELAN4_CQ        *cq;                                            /*   and command queue's */
++      ELAN4_INTOP      intop;                                         /*   and main interrupt op */
++      E4_uint64        status;                                        /*   status register (when waiting for intop)*/
++    }                  dev_dma_flushop[2];
++
++    unsigned           dev_halt_all_count;                            /* count of reasons to halt all units */
++    unsigned           dev_halt_lowpri_count;                         /* count of reasons to halt lowpri queues */
++    unsigned           dev_halt_cproc_count;                          /* count of reasons to halt command processor */
++    unsigned           dev_halt_dproc_count;                          /* count of reasons to halt dma processor */
++    unsigned           dev_halt_tproc_count;                          /* count of reasons to halt thread processor */
++    unsigned           dev_discard_all_count;                         /* count of reasons to discard all packets */
++    unsigned           dev_discard_lowpri_count;                      /* count of reasons to discard non-system packets */
++    unsigned           dev_discard_highpri_count;                     /* count of reasons to discard system packets */
++
++    E4_uint32          dev_schedstatus;                               /* copy of schedule status register */
++
++    /* local context allocation section */
++    spinlock_t                 dev_ctxlock;                                   /* spinlock to protect bitmap */
++    bitmap_t          *dev_ctxmap;                                    /* bitmap for local context allocation */
++
++    spinlock_t                 dev_ctxt_lock;                                 /* spinlock to protect context list */
++    struct list_head     dev_ctxt_list;                                       /* linked list of contexts */
++
++    /* locks to sequentialise interrupt handling */
++    spinlock_t                 dev_trap_lock;                                 /* spinlock while handling a trap */
++    spinlock_t                 dev_requeue_lock;                              /* spinlock sequentialising cproc requeue */
++
++    /* error rate interrupt section */
++    long               dev_error_time;                                /* lbolt at start of sampling period */
++    unsigned           dev_errors_per_period;                         /* errors so far this sampling period */
++    timer_fn_t                 dev_error_timeoutid;                           /* timeout to re-enable error interrupts */
++    timer_fn_t                 dev_linkerr_timeoutid;                         /* timeout to clear link error led */
++
++    /* kernel threads */
++    unsigned           dev_stop_threads:1;                            /* kernel threads should exit */
++
++    /* main interrupt thread */
++    kcondvar_t                 dev_mainint_wait;                              /* place for mainevent interrupt thread to sleep */
++    spinlock_t                 dev_mainint_lock;                              /*   and it's spinlock */
++    unsigned           dev_mainint_started:1;
++    unsigned           dev_mainint_stopped:1;
++
++    /* device context - this is used to flush insert cache/instruction cache/dmas & threads */
++    ELAN4_CPROC_TRAP     dev_cproc_trap;                              /* space to extract cproc trap into */
++
++    struct list_head     dev_intop_list;                              /* list of main interrupt operations */
++    spinlock_t                 dev_intop_lock;                                /*   and spinlock */
++    E4_uint64          dev_intop_cookie;                              /*   and next cookie to use */
++
++    spinlock_t                 dev_flush_lock;                                /* spinlock for flushing */
++    kcondvar_t                 dev_flush_wait;                                /*  and place to sleep */
++
++    ELAN4_CQ          *dev_flush_cq[COMMAND_INSERTER_CACHE_ENTRIES];  /* command queues to flush the insert cache */
++    ELAN4_INTOP          dev_flush_op[COMMAND_INSERTER_CACHE_ENTRIES];        /* and a main interrupt operation for each one */
++    unsigned           dev_flush_finished;                            /* flush command finished */
++
++    ELAN4_HALTOP       dev_iflush_haltop;                             /* halt operation for icache flush */
++    unsigned           dev_iflush_queued:1;                           /* icache haltop queued */
++
++    ELAN4_ROUTE_TABLE   *dev_routetable;                              /* virtual process table (for dma queue flush)*/
++    sdramaddr_t          dev_sdrampages[2];                           /* pages of sdram to hold suspend code sequence */
++    E4_Addr            dev_tproc_suspend;                             /*  st8suspend instruction */
++    E4_Addr            dev_tproc_space;                               /*     and target memory */
++
++    sdramaddr_t                dev_neterr_inputq;                             /* network error input queue descriptor & event */
++    sdramaddr_t                dev_neterr_slots;                              /* network error message slots */
++    ELAN4_CQ          *dev_neterr_msgcq;                              /* command queue for sending messages */
++    ELAN4_CQ          *dev_neterr_intcq;                              /* command queue for message received interrupt */
++    ELAN4_INTOP                dev_neterr_intop;                              /*   and it's main interrupt operation */
++    E4_uint64          dev_neterr_queued;                             /* # message queued in msgcq */
++    spinlock_t           dev_neterr_lock;                             /*   and spinlock .... */
++
++    ELAN4_DEV_STATS    dev_stats;                                     /* device statistics */
++    E4_uint64          dev_sdramerrs[30];                             /* last few sdram errors for procfs */
++
++    spinlock_t                 dev_error_routes_lock;
++    unsigned int      *dev_ack_errors;                                /* Map of source of dproc ack errors */
++    ELAN4_ROUTE_RINGBUF  dev_ack_error_routes;
++    unsigned int        *dev_dproc_timeout;                             /* Ditto dproc timeout errors */
++    ELAN4_ROUTE_RINGBUF  dev_dproc_timeout_routes;
++    unsigned int        *dev_cproc_timeout;                             /* Ditto cproc timeout errors */
++    ELAN4_ROUTE_RINGBUF  dev_cproc_timeout_routes;
++
++    struct list_head     dev_hc_list;                                   /* list of the allocated hash_chunks */
++
++    ELAN4_IPROC_TRAP     dev_iproc_trap;                              /* space for iproc trap */
++} ELAN4_DEV;
++
++/* values for dev_state */
++#define ELAN4_STATE_STOPPED           (1 << 0)                        /* device initialised but not started */
++#define ELAN4_STATE_STARTING          (1 << 1)                        /* device in process of starting */
++#define ELAN4_STATE_STARTED           (1 << 2)                        /* device started */
++#define ELAN4_STATE_STOPPING          (1 << 3)                        /* device in process of stopping */
++
++/* values for dev_features */
++#define ELAN4_FEATURE_NO_WRITE_COMBINE        (1 << 0)                        /* don't allow write combinig at all */
++#define ELAN4_FEATURE_PCI_MAP         (1 << 1)                        /* must use pci mapping functions */
++#define ELAN4_FEATURE_NO_DWORD_READ   (1 << 2)                        /* must perform 64 bit PIO reads */
++
++extern __inline__ unsigned int
++__elan4_readb (ELAN4_DEV *dev, ioaddr_t addr)
++{
++    if (dev->dev_features & ELAN4_FEATURE_NO_DWORD_READ)
++    {
++      uint64_t val = readq ((void *) ((unsigned long) addr & ~7));
++      return ((val >> (((unsigned long) addr & 7) << 3)) & 0xff);
++    }
++    return readb (addr);
++}
++
++extern __inline__ unsigned int
++__elan4_readw (ELAN4_DEV *dev, ioaddr_t addr)
++{
++    if (dev->dev_features & ELAN4_FEATURE_NO_DWORD_READ)
++    {
++      uint64_t val = readq ((void *) ((unsigned long) addr & ~7));
++      return ((val >> (((unsigned long) addr & 7) << 3)) & 0xffff);
++    }
++    return readw (addr);
++}
++
++extern __inline__ unsigned int
++__elan4_readl (ELAN4_DEV *dev, ioaddr_t addr)
++{
++    if (dev->dev_features & ELAN4_FEATURE_NO_DWORD_READ)
++    {
++      uint64_t val = readq ((void *) ((unsigned long) addr & ~7));
++      return ((val >> (((unsigned long) addr & 7) << 3)) & 0xffffffff);
++    }
++    return readl (addr);
++}
++
++/* macros for accessing dev->dev_regs.Tags. */
++#define write_tag(dev,what,val)               writeq (val, dev->dev_regs + offsetof (E4_Registers, Tags.what))
++#define read_tag(dev,what)            readq (dev->dev_regs + offsetof (E4_Registers, Tags.what))
++
++/* macros for accessing dev->dev_regs.Regs. */
++#define write_reg64(dev,what,val)     writeq (val, dev->dev_regs + offsetof (E4_Registers, Regs.what))
++#define write_reg32(dev,what,val)     writel (val, dev->dev_regs + offsetof (E4_Registers, Regs.what))
++#define read_reg64(dev,what)          readq (dev->dev_regs + offsetof (E4_Registers, Regs.what))
++#define read_reg32(dev,what)          __elan4_readl (dev, dev->dev_regs + offsetof (E4_Registers, Regs.what))
++
++/* macros for accessing dev->dev_regs.uRegs. */
++#define write_ureg64(dev,what,val)    writeq (val, dev->dev_regs + offsetof (E4_Registers, uRegs.what))
++#define write_ureg32(dev,what,val)    writel (val, dev->dev_regs + offsetof (E4_Registers, uRegs.what))
++#define read_ureg64(dev,what)         readq (dev->dev_regs + offsetof (E4_Registers, uRegs.what))
++#define read_ureg32(dev,what)         __elan4_readl (dev, dev->dev_regs + offsetof (E4_Registers, uRegs.what))
++
++/* macros for accessing dev->dev_i2c */
++#define write_i2c(dev,what,val)               writeb (val, dev->dev_i2c + offsetof (E4_I2C, what))
++#define read_i2c(dev,what)            __elan4_readb (dev, dev->dev_i2c + offsetof (E4_I2C, what))
++
++/* macros for accessing dev->dev_rom */
++#define read_ebus_rom(dev,off)                __elan4_readb (dev, dev->dev_rom + off)
++
++/* PIO flush operations - ensure writes to registers/sdram are ordered */
++#ifdef CONFIG_IA64_SGI_SN2
++#define pioflush_reg(dev)             read_reg32(dev,InterruptReg)
++#define pioflush_sdram(dev)           elan4_sdram_readl(dev, 0)
++#else
++#define pioflush_reg(dev)             mb()
++#define pioflush_sdram(dev)           mb()
++#endif
++
++/* macros for manipulating the interrupt mask register */
++#define SET_INT_MASK(dev,value)       \
++do { \
++    write_reg32(dev, InterruptMask, (dev)->dev_intmask = (value)); \
++    pioflush_reg(dev);\
++} while (0)
++
++#define CHANGE_INT_MASK(dev, value) \
++do { \
++    if ((dev)->dev_intmask != (value)) \
++    {\
++      write_reg32 (dev, InterruptMask, (dev)->dev_intmask = (value));\
++      pioflush_reg(dev);\
++    }\
++} while (0)
++
++#define ENABLE_INT_MASK(dev,value) \
++do { \
++    unsigned long flags; \
++ \
++    spin_lock_irqsave (&(dev)->dev_intmask_lock, flags); \
++    write_reg32(dev, InterruptMask, (dev)->dev_intmask |= (value)); \
++    pioflush_reg(dev);\
++    spin_unlock_irqrestore (&(dev)->dev_intmask_lock, flags); \
++} while (0)
++
++#define DISABLE_INT_MASK(dev,value) \
++do { \
++    unsigned long flags; \
++ \
++    spin_lock_irqsave (&(dev)->dev_intmask_lock, flags); \
++    write_reg32(dev, InterruptMask, (dev)->dev_intmask &= ~(value)); \
++    pioflush_reg(dev);\
++    spin_unlock_irqrestore (&(dev)->dev_intmask_lock, flags); \
++} while (0)
++
++#define SET_SYSCONTROL(dev,what,value) \
++do { \
++    unsigned long flags; \
++\
++    spin_lock_irqsave (&(dev)->dev_syscontrol_lock, flags); \
++    if ((dev)->what++ == 0) \
++        write_reg64 (dev, SysControlReg, (dev)->dev_syscontrol |= (value)); \
++    pioflush_reg(dev);\
++    spin_unlock_irqrestore (&(dev)->dev_syscontrol_lock, flags); \
++} while (0)
++
++#define CLEAR_SYSCONTROL(dev,what,value) \
++do { \
++    unsigned long flags; \
++\
++    spin_lock_irqsave (&(dev)->dev_syscontrol_lock, flags); \
++    if (--(dev)->what == 0)\
++       write_reg64 (dev, SysControlReg, (dev)->dev_syscontrol &= ~(value)); \
++    pioflush_reg (dev); \
++    spin_unlock_irqrestore (&(dev)->dev_syscontrol_lock, flags); \
++} while (0)
++
++#define PULSE_SYSCONTROL(dev,value) \
++do { \
++    unsigned long flags; \
++\
++    spin_lock_irqsave (&(dev)->dev_syscontrol_lock, flags); \
++    write_reg64 (dev, SysControlReg, (dev)->dev_syscontrol | (value)); \
++    pioflush_reg (dev); \
++    spin_unlock_irqrestore (&(dev)->dev_syscontrol_lock, flags); \
++} while (0)
++
++#define CHANGE_SYSCONTROL(dev,add,sub) \
++do { \
++    unsigned long flags; \
++\
++    spin_lock_irqsave (&(dev)->dev_syscontrol_lock, flags); \
++    dev->dev_syscontrol |= (add);\
++    dev->dev_syscontrol &= ~(sub);\
++    write_reg64 (dev, SysControlReg, (dev)->dev_syscontrol);\
++    pioflush_reg (dev); \
++    spin_unlock_irqrestore (&(dev)->dev_syscontrol_lock, flags); \
++} while (0)
++
++#define SET_SCHED_STATUS(dev, value)\
++do {\
++    write_reg32 (dev, SchedStatus.Status, (dev)->dev_schedstatus = (value));\
++    pioflush_reg (dev);\
++} while (0)
++
++#define CHANGE_SCHED_STATUS(dev, value)\
++do {\
++    if ((dev)->dev_schedstatus != (value))\
++    {\
++      write_reg32 (dev, SchedStatus.Status, (dev)->dev_schedstatus = (value));\
++      pioflush_reg (dev);\
++    }\
++} while (0)
++
++#define PULSE_SCHED_RESTART(dev,value)\
++do {\
++    write_reg32 (dev, SchedStatus.Restart, value);\
++    pioflush_reg (dev);\
++} while (0)
++
++/* device context elan address space */
++#define DEVICE_TPROC_SUSPEND_ADDR             (0x1000000000000000ull)
++#define DEVICE_TPROC_SPACE_ADDR                       (0x1000000000000000ull + SDRAM_PAGE_SIZE)
++#if defined(__LITTLE_ENDIAN__)
++#  define DEVICE_TPROC_SUSPEND_INSTR          0xd3f040c0 /* st64suspend %r16, [%r1] */
++#else
++#  define DEVICE_TPROC_SUSPEND_INSTR          0xc040f0d3 /* st64suspend %r16, [%r1] */
++#endif
++
++#define DEVICE_NETERR_INPUTQ_ADDR             (0x2000000000000000ull)
++#define DEVICE_NETERR_INTCQ_ADDR              (0x2000000000000000ull + SDRAM_PAGE_SIZE)
++#define DEVICE_NETERR_SLOTS_ADDR              (0x2000000000000000ull + SDRAM_PAGE_SIZE*2)
++
++/*
++ * Interrupt operation cookie space
++ * [50:48]    type
++ * [47:0]     value
++ */
++#define INTOP_PERSISTENT                      (0x1000000000000ull)
++#define INTOP_ONESHOT                         (0x2000000000000ull)
++#define INTOP_TYPE_MASK                               (0x3000000000000ull)
++#define INTOP_VALUE_MASK                      (0x0ffffffffffffull)
++
++/* functions for accessing sdram - sdram.c */
++extern unsigned char      elan4_sdram_readb (ELAN4_DEV *dev, sdramaddr_t ptr);
++extern unsigned short     elan4_sdram_readw (ELAN4_DEV *dev, sdramaddr_t ptr);
++extern unsigned int       elan4_sdram_readl (ELAN4_DEV *dev, sdramaddr_t ptr);
++extern unsigned long long elan4_sdram_readq (ELAN4_DEV *dev, sdramaddr_t ptr);
++extern void               elan4_sdram_writeb (ELAN4_DEV *dev, sdramaddr_t ptr, unsigned char val);
++extern void               elan4_sdram_writew (ELAN4_DEV *dev, sdramaddr_t ptr, unsigned short val);
++extern void               elan4_sdram_writel (ELAN4_DEV *dev, sdramaddr_t ptr, unsigned int val);
++extern void               elan4_sdram_writeq (ELAN4_DEV *dev, sdramaddr_t ptr, unsigned long long val);
++
++extern void             elan4_sdram_zerob_sdram (ELAN4_DEV *dev, sdramaddr_t ptr, int nbytes);
++extern void             elan4_sdram_zerow_sdram (ELAN4_DEV *dev, sdramaddr_t ptr, int nbytes);
++extern void             elan4_sdram_zerol_sdram (ELAN4_DEV *dev, sdramaddr_t ptr, int nbytes);
++extern void             elan4_sdram_zeroq_sdram (ELAN4_DEV *dev, sdramaddr_t ptr, int nbytes);
++
++extern void               elan4_sdram_copyb_from_sdram (ELAN4_DEV *dev, sdramaddr_t from, void *to, int nbytes);
++extern void               elan4_sdram_copyw_from_sdram (ELAN4_DEV *dev, sdramaddr_t from, void *to, int nbytes);
++extern void               elan4_sdram_copyl_from_sdram (ELAN4_DEV *dev, sdramaddr_t from, void *to, int nbytes);
++extern void               elan4_sdram_copyq_from_sdram (ELAN4_DEV *dev, sdramaddr_t from, void *to, int nbytes);
++extern void               elan4_sdram_copyb_to_sdram (ELAN4_DEV *dev, void *from, sdramaddr_t to, int nbytes);
++extern void               elan4_sdram_copyw_to_sdram (ELAN4_DEV *dev, void *from, sdramaddr_t to, int nbytes);
++extern void               elan4_sdram_copyl_to_sdram (ELAN4_DEV *dev, void *from, sdramaddr_t to, int nbytes);
++extern void               elan4_sdram_copyq_to_sdram (ELAN4_DEV *dev, void *from, sdramaddr_t to, int nbytes);
++
++/* device.c - configuration */
++extern unsigned int elan4_hash_0_size_val;
++extern unsigned int elan4_hash_1_size_val;
++extern unsigned int elan4_ctxt_table_shift;
++extern unsigned int elan4_ln2_max_cqs;
++extern unsigned int elan4_dmaq_highpri_size;
++extern unsigned int elan4_threadq_highpri_size;
++extern unsigned int elan4_dmaq_lowpri_size;
++extern unsigned int elan4_threadq_lowpri_size;
++extern unsigned int elan4_interruptq_size;
++
++extern unsigned int elan4_mainint_punt_loops;
++extern unsigned int elan4_mainint_resched_ticks;
++
++
++/* device.c */
++extern void               elan4_set_schedstatus (ELAN4_DEV *dev, E4_uint32 intreg);
++extern void               elan4_queue_haltop (ELAN4_DEV *dev, ELAN4_HALTOP *op);
++extern void             elan4_queue_intop (ELAN4_DEV *dev, ELAN4_CQ *cq, ELAN4_INTOP *op);
++extern void             elan4_register_intop (ELAN4_DEV *dev, ELAN4_INTOP *op);
++extern void             elan4_deregister_intop (ELAN4_DEV *dev, ELAN4_INTOP *op);
++extern void             elan4_queue_dma_flushop (ELAN4_DEV *dev, ELAN4_DMA_FLUSHOP *op, int hipri);
++extern void             elan4_queue_mainintop (ELAN4_DEV *dev, ELAN4_INTOP *op);
++
++extern int                elan4_1msi0 (ELAN4_DEV *dev);
++
++extern int                elan4_insertctxt (ELAN4_DEV *dev, ELAN4_CTXT *ctxt, ELAN4_TRAP_OPS *ops);
++extern void               elan4_removectxt (ELAN4_DEV *dev, ELAN4_CTXT *ctxt);
++extern ELAN4_CTXT        *elan4_localctxt (ELAN4_DEV *dev, unsigned num);
++extern ELAN4_CTXT        *elan4_networkctxt (ELAN4_DEV *dev, unsigned num);
++
++extern int                elan4_attach_filter (ELAN4_CTXT *ctxt, unsigned int ctxnum);
++extern void               elan4_detach_filter (ELAN4_CTXT *ctxt, unsigned int ctxnum);
++extern void             elan4_set_filter (ELAN4_CTXT *ctxt, unsigned int ctxnum, E4_uint32 state);
++extern void             elan4_set_routetable (ELAN4_CTXT *ctxt, ELAN4_ROUTE_TABLE *tbl);
++
++extern ELAN4_CQA *        elan4_getcqa (ELAN4_CTXT *ctxt, unsigned int idx);
++extern void               elan4_putcqa (ELAN4_CTXT *ctxt, unsigned int idx);
++extern ELAN4_CQ          *elan4_alloccq (ELAN4_CTXT *ctxt, unsigned cqsize, unsigned cqperm, unsigned cqtype);
++extern void               elan4_freecq (ELAN4_CTXT *ctxt, ELAN4_CQ *cq);
++extern void               elan4_restartcq (ELAN4_DEV *dev, ELAN4_CQ *cq);
++extern void               elan4_flushcq (ELAN4_DEV *dev, ELAN4_CQ *cq);
++extern void               elan4_updatecq (ELAN4_DEV *dev, ELAN4_CQ *cq, unsigned perm, unsigned restart);
++
++extern void             elan4_flush_icache (ELAN4_CTXT *ctxt);
++extern void             elan4_flush_icache_halted (ELAN4_CTXT *ctxt);
++
++extern int                elan4_initialise_device (ELAN4_DEV *dev);
++extern void               elan4_finalise_device (ELAN4_DEV *dev);
++extern int                elan4_start_device (ELAN4_DEV *dev);
++extern void               elan4_stop_device (ELAN4_DEV *dev);
++
++extern int              elan4_compute_position (ELAN_POSITION *pos, unsigned nodeid, unsigned numnodes, unsigned aritiyval);
++extern int              elan4_get_position (ELAN4_DEV *dev, ELAN_POSITION *pos);
++extern int              elan4_set_position (ELAN4_DEV *dev, ELAN_POSITION *pos);
++extern void             elan4_get_params   (ELAN4_DEV *dev, ELAN_PARAMS *params, unsigned short *mask);
++extern void             elan4_set_params   (ELAN4_DEV *dev, ELAN_PARAMS *params, unsigned short mask);
++
++
++extern int                elan4_read_vpd(ELAN4_DEV *dev, unsigned char *tag, unsigned char *result) ;
++
++
++/* device_osdep.c */
++extern unsigned int     elan4_pll_cfg;
++extern int              elan4_pll_div;
++extern int              elan4_mod45disable;
++
++extern int                elan4_pciinit (ELAN4_DEV *dev);
++extern void               elan4_pcifini (ELAN4_DEV *dev);
++extern void               elan4_pcierror (ELAN4_DEV *dev);
++
++extern ELAN4_DEV       *elan4_reference_device (int instance, int state);
++extern void             elan4_dereference_device (ELAN4_DEV *dev);
++
++extern ioaddr_t           elan4_map_device (ELAN4_DEV *dev, unsigned bar, unsigned off, unsigned size, ELAN4_MAP_HANDLE *handlep);
++extern void               elan4_unmap_device (ELAN4_DEV *dev, ioaddr_t ptr, unsigned size, ELAN4_MAP_HANDLE *handlep);
++extern unsigned long      elan4_resource_len (ELAN4_DEV *dev, unsigned bar);
++
++extern void               elan4_configure_mtrr (ELAN4_DEV *dev);
++extern void             elan4_unconfigure_mtrr (ELAN4_DEV *dev);
++
++/* i2c.c */
++extern int              i2c_disable_auto_led_update (ELAN4_DEV *dev);
++extern void             i2c_enable_auto_led_update (ELAN4_DEV *dev);
++extern int              i2c_write (ELAN4_DEV *dev, unsigned int addr, unsigned int count, unsigned char *data);
++extern int              i2c_read (ELAN4_DEV *dev, unsigned int addr, unsigned int count, unsigned char *data);
++extern int              i2c_writereg (ELAN4_DEV *dev, unsigned int addr, unsigned int reg, unsigned int count, unsigned char *data);
++extern int              i2c_readreg (ELAN4_DEV *dev, unsigned int addr, unsigned int reg, unsigned int count, unsigned char *data);
++extern int              i2c_read_rom (ELAN4_DEV *dev, unsigned int addr, unsigned int count, unsigned char *data);
++
++#if defined(__linux__)
++/* procfs_Linux.c */
++extern void             elan4_procfs_device_init (ELAN4_DEV *dev);
++extern void             elan4_procfs_device_fini (ELAN4_DEV *dev);
++extern void             elan4_procfs_init(void);
++extern void             elan4_procfs_fini(void);
++
++extern struct proc_dir_entry *elan4_procfs_root;
++extern struct proc_dir_entry *elan4_config_root;
++#endif
++
++/* sdram.c */
++extern void             elan4_sdram_init (ELAN4_DEV *dev);
++extern void               elan4_sdram_fini (ELAN4_DEV *dev);
++extern void               elan4_sdram_setup_delay_lines (ELAN4_DEV *dev);
++extern int                elan4_sdram_init_bank (ELAN4_DEV *dev, ELAN4_SDRAM_BANK *bank);
++extern void               elan4_sdram_fini_bank (ELAN4_DEV *dev, ELAN4_SDRAM_BANK *bank);
++extern void             elan4_sdram_add_bank (ELAN4_DEV *dev, ELAN4_SDRAM_BANK *bank);
++extern sdramaddr_t        elan4_sdram_alloc (ELAN4_DEV *dev, int nbytes);
++extern void               elan4_sdram_free (ELAN4_DEV *dev, sdramaddr_t ptr, int nbytes);
++extern void               elan4_sdram_flushcache (ELAN4_DEV *dev, sdramaddr_t base, int nbytes);
++extern char              *elan4_sdramerr2str (ELAN4_DEV *dev, E4_uint64 status, char *str);
++
++/* traps.c */
++extern void               elan4_display_eproc_trap (void *type, int mode, char *str, ELAN4_EPROC_TRAP *trap);
++extern void               elan4_display_cproc_trap (void *type, int mode, char *str, ELAN4_CPROC_TRAP *trap);
++extern void               elan4_display_dproc_trap (void *type, int mode, char *str, ELAN4_DPROC_TRAP *trap);
++extern void               elan4_display_tproc_trap (void *type, int mode, char *str, ELAN4_TPROC_TRAP *trap);
++extern void               elan4_display_iproc_trap (void *type, int mode, char *str, ELAN4_IPROC_TRAP *trap);
++
++
++extern void               elan4_extract_eproc_trap (ELAN4_DEV *dev, E4_uint64 status, ELAN4_EPROC_TRAP *trap, int iswaitevent);
++extern void               elan4_extract_cproc_trap (ELAN4_DEV *dev, E4_uint64 status, ELAN4_CPROC_TRAP *trap, unsigned cqnum);
++extern void               elan4_extract_dproc_trap (ELAN4_DEV *dev, E4_uint64 status, ELAN4_DPROC_TRAP *trap, unsigned unit);
++extern void               elan4_extract_tproc_trap (ELAN4_DEV *dev, E4_uint64 status, ELAN4_TPROC_TRAP *trap);
++extern void               elan4_extract_iproc_trap (ELAN4_DEV *dev, E4_uint64 status, ELAN4_IPROC_TRAP *trap, unsigned unit);
++extern void elan4_ringbuf_store(ELAN4_ROUTE_RINGBUF *ringbuf, E4_VirtualProcessEntry *route, ELAN4_DEV *dev);
++extern int                cproc_open_extract_vp (ELAN4_DEV *dev, ELAN4_CQ *cq);
++
++extern void               elan4_inspect_iproc_trap (ELAN4_IPROC_TRAP *trap);
++extern E4_uint64          elan4_trapped_open_command (ELAN4_DEV *dev, ELAN4_CQ *cq);
++
++/* mmu.c */
++extern void               elan4mmu_flush_tlb (ELAN4_DEV *dev);
++extern ELAN4_HASH_ENTRY  *elan4mmu_ptealloc (ELAN4_CTXT *ctxt, int tbl, E4_Addr vaddr, unsigned int *tagidxp);
++extern int                elan4mmu_pteload (ELAN4_CTXT *ctxt, int tbl, E4_Addr vaddr, E4_uint64 pte);
++extern void               elan4mmu_unload_range (ELAN4_CTXT *ctxt, int tbl, E4_Addr start, unsigned long len);
++extern void               elan4mmu_invalidate_ctxt (ELAN4_CTXT *ctxt);
++
++extern ELAN4_HASH_CACHE  *elan4mmu_reserve (ELAN4_CTXT *ctxt, int tbl, E4_Addr start, unsigned int npages, int cansleep);
++extern void               elan4mmu_release (ELAN4_CTXT *ctxt, ELAN4_HASH_CACHE *hc);
++extern void               elan4mmu_set_pte (ELAN4_CTXT *ctxt, ELAN4_HASH_CACHE *hc, unsigned int idx, E4_uint64 newpte);
++extern E4_uint64          elan4mmu_get_pte (ELAN4_CTXT *ctxt, ELAN4_HASH_CACHE *hc, unsigned int idx);
++extern void               elan4mmu_clear_pte (ELAN4_CTXT *ctxt, ELAN4_HASH_CACHE *hc, unsigned int idx);
++
++/* mmu_osdep.c */
++extern int              elan4mmu_categorise_paddr (ELAN4_DEV *dev, physaddr_t *physp);
++extern int                elan4mmu_alloc_topaddr (ELAN4_DEV *dev, physaddr_t paddr, unsigned type);
++extern E4_uint64          elan4mmu_phys2pte (ELAN4_DEV *dev, physaddr_t paddr, unsigned perm);
++extern physaddr_t       elan4mmu_pte2phys (ELAN4_DEV *dev, E4_uint64 pte);
++
++/* neterr.c */
++extern int                elan4_neterr_init (ELAN4_DEV *dev);
++extern void               elan4_neterr_destroy (ELAN4_DEV *dev);
++extern int                elan4_neterr_sendmsg (ELAN4_DEV *dev, unsigned int nodeid, unsigned int retries, ELAN4_NETERR_MSG *msg);
++extern int                elan4_neterr_iproc_trap (ELAN4_DEV *dev, ELAN4_IPROC_TRAP *trap);
++
++/* routetable.c */
++extern ELAN4_ROUTE_TABLE *elan4_alloc_routetable (ELAN4_DEV *dev, unsigned size);
++extern void               elan4_free_routetable (ELAN4_DEV *dev, ELAN4_ROUTE_TABLE *tbl);
++extern void               elan4_write_route (ELAN4_DEV *dev, ELAN4_ROUTE_TABLE *tbl, unsigned vp, E4_VirtualProcessEntry *entry);
++extern void               elan4_read_route (ELAN4_DEV *dev, ELAN4_ROUTE_TABLE *tbl, unsigned vp, E4_VirtualProcessEntry *entry);
++extern void               elan4_invalidate_route (ELAN4_DEV *dev, ELAN4_ROUTE_TABLE *tbl, unsigned vp);
++extern int                elan4_generate_route (ELAN_POSITION *pos, E4_VirtualProcessEntry *route, unsigned ctxnum,
++                                              unsigned lowid, unsigned highid, unsigned options);
++extern int              elan4_check_route (ELAN_POSITION *pos, ELAN_LOCATION location, E4_VirtualProcessEntry *route, unsigned flags);
++
++/* user.c */
++extern int        __categorise_command (E4_uint64 command, int *cmdSize);
++extern int        __whole_command (sdramaddr_t *commandPtr, sdramaddr_t insertPtr, unsigned int cqSize, unsigned int cmdSize);
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++#endif /* __ELAN4_ELANDEV_H */
+Index: linux-2.4.21/include/elan4/device_Linux.h
+===================================================================
+--- linux-2.4.21.orig/include/elan4/device_Linux.h     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan4/device_Linux.h  2005-06-01 23:12:54.735418432 -0400
+@@ -0,0 +1,97 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN4_ELANDEV_LINUX_H
++#define __ELAN4_ELANDEV_LINUX_H
++
++#ident "$Id: device_Linux.h,v 1.19 2004/08/09 14:02:37 daniel Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/device_Linux.h,v $*/
++
++#include <linux/coproc.h>
++
++#if defined(MPSAS)
++#include <elan4/mpsas.h>
++#endif
++
++#if defined(CONFIG_DEVFS_FS)
++#include <linux/devfs_fs_kernel.h>
++#endif
++
++#define ELAN4_MAJOR              61
++#define ELAN4_NAME               "elan4"
++#define ELAN4_MAX_CONTROLLER     16          /* limited to 4 bits */
++ 
++/* OS dependant component of ELAN4_DEV struct */
++typedef struct elan4_dev_osdep
++{
++    struct pci_dev      *pdev;                        /* PCI config data */
++
++    struct proc_dir_entry *procdir;
++    struct proc_dir_entry *configdir;
++    struct proc_dir_entry *statsdir;
++
++#if defined(CONFIG_DEVFS_FS)
++    devfs_handle_t devfs_control;
++    devfs_handle_t devfs_sdram;
++    devfs_handle_t devfs_user;
++#endif
++
++#if defined(CONFIG_MTRR)
++    int                          sdram_mtrr;
++    int                          regs_mtrr;
++#endif
++} ELAN4_DEV_OSDEP;
++
++/* /dev/elan/rmsX */
++
++/* /dev/elan4/controlX */
++typedef struct control_private
++{
++    struct elan4_dev   *pr_dev;
++    unsigned          pr_boundary_scan;
++} CONTROL_PRIVATE;
++
++/* /dev/elan4/sdramX */
++typedef struct mem_page
++{
++    struct mem_page *pg_next;
++    sdramaddr_t      pg_addr;
++    unsigned long    pg_pgoff;
++    unsigned       pg_ref;
++} MEM_PAGE;
++
++#define MEM_HASH_SIZE 32
++#define MEM_HASH(pgoff)       ((pgoff) & (MEM_HASH_SIZE-1))
++
++typedef struct mem_private
++{
++    struct elan4_dev *pr_dev;
++    MEM_PAGE         *pr_pages[MEM_HASH_SIZE];
++    spinlock_t        pr_lock;
++} MEM_PRIVATE;
++
++/* /dev/elan4/userX */
++typedef struct user_private
++{
++    atomic_t        pr_ref;
++    struct user_ctxt *pr_uctx;
++    struct mm_struct *pr_mm;
++    coproc_ops_t      pr_coproc;
++} USER_PRIVATE;
++
++/* No mapping handles on linux */
++typedef void *ELAN4_MAP_HANDLE;
++
++#define ELAN4_TASK_HANDLE()   ((unsigned long) current->mm)
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++#endif /* __ELAN4_ELANDEV_LINUX_H */
+Index: linux-2.4.21/include/elan4/dma.h
+===================================================================
+--- linux-2.4.21.orig/include/elan4/dma.h      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan4/dma.h   2005-06-01 23:12:54.736418280 -0400
+@@ -0,0 +1,82 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN4_DMA_H
++#define __ELAN4_DMA_H
++
++#ident "$Id: dma.h,v 1.16 2003/09/04 12:39:17 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan4hdr/dma.h,v $*/
++
++#include <elan4/types.h>
++
++/* Alignment for a DMA descriptor */
++#define E4_DMA_ALIGN          (64)
++
++/* Maximum size of a single DMA ((1 << 31)-1) */
++#define E4_MAX_DMA_SIZE               (0x7fffffff)
++
++/* 
++ * dma_typeSize
++ *
++ * [63:32]    Size
++ * [31]               unused
++ * [30]               IsRemote
++ * [29]               QueueWrite
++ * [28]               ShmemWrite
++ * [27:26]    DataType
++ * [25]               Broadcast
++ * [24]               AlignPackets
++ * [23:16]    FailCount
++ * [15:14]    unused
++ * [13:0]     Context
++ */
++
++#define DMA_FailCount(val)    (((val) & 0xff) << 16)
++#define DMA_AlignPackets      (1 << 24)
++#define DMA_Broadcast         (1 << 25)
++#define DMA_ShMemWrite                (1 << 28)
++#define DMA_QueueWrite                (1 << 29)
++#define DMA_IsRemote          (1 << 30)
++#define DMA_Context(val)      ((unsigned) (val) & 0x3ff)
++#define DMA_ContextMask               0x3fffull
++#define Dma_TypeSizeMask      0xfffffffffff00000ull
++
++#define DMA_DataTypeByte      (E4_DATATYPE_BYTE  << 26)
++#define DMA_DataTypeShort     (E4_DATATYPE_SHORT << 26)
++#define DMA_DataTypeWord      (E4_DATATYPE_WORD  << 26)
++#define DMA_DataTypeLong      (E4_DATATYPE_DWORD << 26)
++
++#define E4_DMA_TYPE_SIZE(size, dataType, flags, failCount)    \
++    ((((E4_uint64)(size)) << 32) |  ((dataType) & DMA_DataTypeLong) | \
++     (flags) | DMA_FailCount(failCount))
++
++typedef volatile struct e4_dma
++{
++    E4_uint64         dma_typeSize;
++    E4_uint64         dma_cookie;
++    E4_uint64         dma_vproc;
++    E4_Addr           dma_srcAddr;
++    E4_Addr           dma_dstAddr;
++    E4_Addr           dma_srcEvent;
++    E4_Addr           dma_dstEvent;
++} E4_DMA;
++
++/* Same as above but padded to 64-bytes */
++typedef volatile struct e4_dma64
++{
++    E4_uint64         dma_typeSize;
++    E4_uint64         dma_cookie;
++    E4_uint64         dma_vproc;
++    E4_Addr           dma_srcAddr;
++    E4_Addr           dma_dstAddr;
++    E4_Addr           dma_srcEvent;
++    E4_Addr           dma_dstEvent;
++    E4_Addr           dma_pad;
++} E4_DMA64;
++
++#endif /* __ELAN4_DMA_H */
+Index: linux-2.4.21/include/elan4/events.h
+===================================================================
+--- linux-2.4.21.orig/include/elan4/events.h   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan4/events.h        2005-06-01 23:12:54.736418280 -0400
+@@ -0,0 +1,179 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN4_EVENTS_H
++#define __ELAN4_EVENTS_H
++
++#ident "$Id: events.h,v 1.22 2004/06/23 11:07:18 addy Exp $"
++/*      $Source: /cvs/master/quadrics/elan4hdr/events.h,v $*/
++
++#define E4_EVENT_ALIGN                32
++#define E4_EVENTBLOCK_SIZE    64
++
++#ifndef _ASM
++/*
++ * Event locations must be aligned to a 32 byte boundary. It is very much more efficent to place
++ * them in elan local memory but is not essential.
++ */
++typedef struct _E4_Event
++{
++    volatile E4_uint64        ev_CountAndType;
++    E4_uint64         ev_Params[2];
++} E4_Event;
++
++/* Same as above but padded to correct Event alignment */
++typedef struct _E4_Event32
++{
++    volatile E4_uint64        ev_CountAndType;
++    E4_uint64         ev_Params[2];
++    E4_uint64         ev_pad;
++} E4_Event32;
++
++/*
++ * An E4_EVENTBLOCK_SIZE aligned block of Main or Elan memory
++ */
++typedef union _E4_Event_Blk
++{
++    /* Padded to 64-bytes in case a cache-line write is more efficient */
++    volatile E4_uint8  eb_unit8[E4_EVENTBLOCK_SIZE];
++    volatile E4_uint32 eb_uint32[E4_EVENTBLOCK_SIZE/sizeof(E4_uint32)];
++    volatile E4_uint64 eb_uint64[E4_EVENTBLOCK_SIZE/sizeof(E4_uint64)];
++} E4_Event_Blk;
++#define eb_done       eb_uint32[14]
++#define eb_done_dword eb_uint64[7]
++
++#endif /* ! _ASM */
++
++/*
++ * ev_CountAndType
++ *  [63:31]   Count
++ *  [10]      CopyType
++ *  [9:8]     DataType
++ *  [7:0]     CopySize
++ */
++#define E4_EVENT_TYPE_MASK    0x00000000ffffffffull
++#define E4_EVENT_COUNT_MASK   0xffffffff00000000ull
++#define E4_EVENT_COUNT_SHIFT  32
++#define E4_EVENT_COPY_TYPE_MASK       (1 << 10)
++#define E4_EVENT_DATA_TYPE_MASK       (3 << 8)
++#define E4_EVENT_COPY_SIZE_MASK       (0xff)
++
++/* CopyType */
++#define E4_EVENT_COPY         (0 << 10)
++#define E4_EVENT_WRITE                (1 << 10)
++
++/* DataType */
++#define E4_EVENT_DTYPE_BYTE   (0 << 8)
++#define E4_EVENT_DTYPE_SHORT  (1 << 8)
++#define E4_EVENT_DTYPE_WORD   (2 << 8)
++#define E4_EVENT_DTYPE_LONG   (3 << 8)
++
++#define EVENT_COUNT(EventPtr) ((E4_int32)(elan4_load64 (&(EventPtr)->ev_CountAndType) >> E4_EVENT_COUNT_SHIFT))
++#define EVENT_TYPE(EventPtr)  ((E4_uint32)(elan4_load64 (&(EventPtr)->ev_CountAndType) & E4_EVENT_TYPE_MASK))
++
++#define E4_WAITEVENT_COUNT_TYPE_VALUE(Count, EventType, DataType, CopySize) \
++      (((E4_uint64)(Count) << E4_EVENT_COUNT_SHIFT) | (EventType) | (DataType) | (CopySize))
++
++#define E4_EVENT_TYPE_VALUE(EventType, DataType, CopySize)    \
++      ((EventType) | (DataType) | (CopySize))
++
++#define E4_EVENT_INIT_VALUE(InitialCount, EventType, DataType, CopySize)      \
++      (((E4_uint64)(InitialCount) << E4_EVENT_COUNT_SHIFT) | E4_EVENT_TYPE_VALUE(EventType, DataType, CopySize))
++
++#define ev_CopySource ev_Params[0]
++#define ev_CopyDest   ev_Params[1]
++#define ev_WritePtr   ev_Params[0]
++#define ev_WriteValue ev_Params[1]
++
++#define EVENT_BLK_READY(BLK) ((BLK)->eb_done != 0)
++#define EVENT_READY(EVENT)   ((E4_uint32)((((volatile E4_Event *) (EVENT))->ev_CountAndType) >> E4_EVENT_COUNT_SHIFT) >= 0)
++
++#define ELAN_WAIT_EVENT (0)
++#define ELAN_POLL_EVENT (-1)
++
++#define E4_BLK_PATTERN        ((E4_uint32)0xfeedface)
++
++#define E4_INIT_COPY_EVENT(EVENT, BLK_ELAN, BLK, SIZE)                                                        \
++      do {                                                                                            \
++         elan4_store64 (E4_EVENT_INIT_VALUE(0, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, SIZE), &(EVENT)->ev_CountAndType); \
++           elan4_store64 ((BLK_ELAN), &(EVENT)->ev_CopySource); \
++         elan4_store64 ((BLK), &(EVENT)->ev_CopyDest); \
++      } while (0)
++
++#define E4_INIT_WRITE_EVENT(EVENT, DWORD)                                                             \
++      do {                                                                                            \
++          elan4_store64 (E4_EVENT_INIT_VALUE(0, E4_EVENT_WRITE, E4_EVENT_DTYPE_LONG, 0), &(EVENT)->ev_CountAndType);  \
++          elan4_store64 ((DWORD), &(EVENT)->ev_WritePtr); \
++          elan4_store64 ((E4_Addr) (E4_BLK_PATTERN), &(EVENT)->ev_WriteValue); \
++      } while (0)
++
++#define E4_RESET_BLK_EVENT(BLK)                                       \
++      do {                                                            \
++              (BLK)->eb_done = (0);                                   \
++      } while (0)
++
++#define E4_PRIME_BLK_EVENT(EVENT, COUNT)                              \
++      do {                                                            \
++         elan4_store64 (E4_EVENT_INIT_VALUE(COUNT, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, 8), &(EVENT)->ev_CountAndType);\
++      } while (0)
++
++#define E4_PRIME_COPY_EVENT(EVENT, SIZE, COUNT)                               \
++      do {                                                            \
++         elan4_store64 (E4_EVENT_INIT_VALUE(COUNT, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, (SIZE >> 3)), &(EVENT)->ev_CountAndType);\
++      } while (0)
++
++#define E4_PRIME_WRITE_EVENT(EVENT, COUNT)                                    \
++      do {                                                                    \
++         elan4_store64 (E4_EVENT_INIT_VALUE(COUNT, E4_EVENT_WRITE, E4_EVENT_DTYPE_LONG, 0), &(EVENT)->ev_CountAndType);\
++      } while (0)
++
++#ifndef _ASM
++
++#define E4_INPUTQ_ALIGN                       32      /* Descriptor must be 32-byte aligned */
++
++typedef struct _E4_InputQueue
++{
++   volatile E4_Addr   q_bptr;         /* 64 bit aligned ptr to current back item */
++   E4_Addr            q_fptr;         /* 64 bit aligned ptr to current front item */
++   E4_uint64          q_control;      /* this defines the last item, item size, and offset back to the first item. */
++   E4_Addr            q_event;        /* queue event */
++} E4_InputQueue;
++
++#define E4_INPUTQ_LASTITEM_MASK       0x00000000ffffffffULL
++#define E4_INPUTQ_ITEMSIZE_MASK               0x000000ff00000000ULL
++#define E4_INPUTQ_LASTITEM_OFFSET_MASK        0xffffff0000000000ULL
++#define E4_INPUTQ_LASTITEM_SHIFT      0
++#define E4_INPUTQ_ITEMSIZE_SHIFT      32
++#define E4_INPUTQ_LASTITEM_OFFSET_SHIFT       40
++
++/*
++ * Macro to initialise the InputQueue control word given the FirstItem, LastItem & ItemSize
++ * FirstItem and LastItem are 64 bit double word aligned elan addresses.
++ */
++#define E4_InputQueueControl(FirstItem, LastItem, ItemSizeInBytes)\
++   (((((E4_uint64)(LastItem)))                                                      & E4_INPUTQ_LASTITEM_MASK) |\
++    ((((E4_uint64)(ItemSizeInBytes))        << (E4_INPUTQ_ITEMSIZE_SHIFT-3))        & E4_INPUTQ_ITEMSIZE_MASK)  |\
++    ((((E4_uint64)((FirstItem)-(LastItem))) << (E4_INPUTQ_LASTITEM_OFFSET_SHIFT-3)) & E4_INPUTQ_LASTITEM_OFFSET_MASK))    
++
++/* 
++ * LastItemOffset is a sign extended -ve quantity with LastItemOffset[26:3] == q_control[63:40]
++ * we sign extend this by setting LastItemOffset[63:27] to be #one.
++ */
++#define E4_InputQueueLastItemOffset(control)  ((((E4_int64) -1) << (64 - (E4_INPUTQ_LASTITEM_OFFSET_SHIFT-3))) | \
++                                             ((E4_int64) (((control) & E4_INPUTQ_LASTITEM_OFFSET_MASK) >> (E4_INPUTQ_LASTITEM_OFFSET_SHIFT-3))))
++#define E4_InputQueueItemSize(control)              (((control) & E4_INPUTQ_ITEMSIZE_MASK) >> (E4_INPUTQ_ITEMSIZE_SHIFT-3))
++
++/*
++ * Macro to increment the InputQ front pointer taking into account wrap 
++ */
++#define E4_InputQueueFptrIncrement(Q, FirstItem, LastItem, ItemSizeInBytes) \
++      ((Q)->q_fptr = ( ((Q)->q_fptr == (LastItem)) ? (FirstItem) : ((Q)->q_fptr + (ItemSizeInBytes))) )
++
++#endif /* _ASM */
++
++#endif /* __ELAN4_EVENTS_H */
+Index: linux-2.4.21/include/elan4/i2c.h
+===================================================================
+--- linux-2.4.21.orig/include/elan4/i2c.h      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan4/i2c.h   2005-06-01 23:12:54.736418280 -0400
+@@ -0,0 +1,47 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef _ELAN4_I2C_H
++#define _ELAN4_I2C_H
++
++#ident "@(#)$Id: i2c.h,v 1.10 2003/12/02 16:11:22 lee Exp $ $Name: QSNETMODULES-4-30_20050128 $"
++/*      $Source: /cvs/master/quadrics/elan4hdr/i2c.h,v $*/
++
++/* I2C address space - bits[7:1] */
++#define I2C_LED_I2C_ADDR                      0x20
++#define I2C_TEMP_ADDR                         0x48
++#define I2C_EEPROM_ADDR                               0x50
++
++#define I2C_WRITE_ADDR(addr)                  ((addr) << 1 | 0)
++#define I2C_READ_ADDR(addr)                   ((addr) << 1 | 1)
++
++/* I2C EEPROM appears as 8 I2C 256 byte devices */
++#define I2C_24LC16B_BLOCKSIZE                 (256)
++#define I2C_24LC16B_BLOCKADDR(addr)           ((addr) >> 8)
++#define I2C_24LC16B_BLOCKOFFSET(addr)         ((addr) & 0xff)
++
++#define I2C_ELAN_EEPROM_PCI_BASEADDR          0       /* PCI config starts at addr 0 in the EEPROM */
++#define I2C_ELAN_EEPROM_VPD_BASEADDR          256     /* VPD data start                            */
++#define I2C_ELAN_EEPROM_PCI_SIZE              256     /* PCI data max size                         */
++#define I2C_ELAN_EEPROM_VPD_SIZE              256     /* VPD data max size                         */
++
++#define I2C_ELAN_EEPROM_SIZE                  2048
++
++#define I2C_ELAN_EEPROM_DEVICE_ID             0xA0
++#define I2C_ELAN_EEPROM_FAIL_LIMIT              8
++
++#define I2C_ELAN_EEPROM_ADDR_BLOCKSIZE_SHIFT  0x8
++#define I2C_ELAN_EEPROM_ADDR_BLOCK_MASK               0x7
++#define I2C_ELAN_EEPROM_ADDR_BLOCK_SHIFT      0x1
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++#endif /* _ELAN4_I2C_H */
+Index: linux-2.4.21/include/elan4/intcookie.h
+===================================================================
+--- linux-2.4.21.orig/include/elan4/intcookie.h        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan4/intcookie.h     2005-06-01 23:12:54.737418128 -0400
+@@ -0,0 +1,62 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: intcookie.h,v 1.10 2004/08/09 14:02:37 daniel Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/intcookie.h,v $*/
++
++#ifndef __ELAN4_INTCOOKIE_H
++#define __ELAN4_INTCOOKIE_H
++
++typedef E4_uint64 ELAN4_INTCOOKIE;
++
++#ifdef __KERNEL__
++
++typedef struct intcookie_entry
++{
++    struct intcookie_entry    *ent_next;
++    struct intcookie_entry    *ent_prev;
++
++    spinlock_t                       ent_lock;
++    unsigned                 ent_ref;
++
++    ELAN4_INTCOOKIE          ent_cookie;
++    ELAN4_INTCOOKIE          ent_fired;
++    kcondvar_t                       ent_wait;
++} INTCOOKIE_ENTRY;
++
++typedef struct intcookie_table
++{
++    struct intcookie_table    *tbl_next;
++    struct intcookie_table    *tbl_prev;
++
++    ELAN_CAPABILITY           *tbl_cap;
++
++    spinlock_t                       tbl_lock;
++    unsigned                 tbl_ref;
++    INTCOOKIE_ENTRY           *tbl_entries;
++} INTCOOKIE_TABLE;
++
++extern void                intcookie_init(void);
++extern void                intcookie_fini(void);
++extern INTCOOKIE_TABLE    *intcookie_alloc_table (ELAN_CAPABILITY *cap);
++extern void                intcookie_free_table (INTCOOKIE_TABLE *tbl);
++extern int                 intcookie_alloc (INTCOOKIE_TABLE *tbl, ELAN4_INTCOOKIE cookie);
++extern int                 intcookie_free (INTCOOKIE_TABLE *tbl, ELAN4_INTCOOKIE cookie);
++extern int                 intcookie_fire (INTCOOKIE_TABLE *tbl, ELAN4_INTCOOKIE cookie);
++extern int                 intcookie_fire_cap (ELAN_CAPABILITY *cap, ELAN4_INTCOOKIE cookie);
++extern int                 intcookie_wait (INTCOOKIE_TABLE *tbl, ELAN4_INTCOOKIE cookie);
++extern int                 intcookie_arm (INTCOOKIE_TABLE *tbl, ELAN4_INTCOOKIE cookie);
++
++#endif /* __KERNEL */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++#endif /* __ELAN4_INTCOOKIE_H */
+Index: linux-2.4.21/include/elan4/ioctl.h
+===================================================================
+--- linux-2.4.21.orig/include/elan4/ioctl.h    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan4/ioctl.h 2005-06-01 23:12:54.738417976 -0400
+@@ -0,0 +1,320 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN4_IOCTL_H
++#define __ELAN4_IOCTL_H
++
++#ident "@(#)$Id: ioctl.h,v 1.27.6.2 2005/01/11 12:15:39 duncant Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/ioctl.h,v $*/
++
++#include <elan/devinfo.h>
++#include <elan/capability.h>
++
++#include <elan4/dma.h>
++#include <elan4/neterr.h>
++#include <elan4/registers.h>
++#include <elan4/intcookie.h>
++
++#define ELAN4IO_CONTROL_PATHNAME      "/dev/elan4/control%d"
++#define ELAN4IO_USER_PATHNAME         "/dev/elan4/user%d"
++#define ELAN4IO_SDRAM_PATHNAME        "/dev/elan4/sdram%d"
++#define ELAN4IO_MAX_PATHNAMELEN       32
++
++/*
++ * NOTE - ioctl values 0->0x1f are defined for 
++ *        generic/control usage.
++ */
++
++/* Macro to generate 'offset' to mmap "control" device */
++#define OFF_TO_BAR(off)               (((off) >> 28) & 0xF)
++#define OFF_TO_OFFSET(off)    ((off) & 0x0FFFFFFF)
++#define GEN_OFF(bar,off)      (((bar) << 28) | ((off) & 0x0FFFFFFF))
++
++/* Definiations for generic ioctls */
++#define ELAN4IO_GENERIC_BASE          0x00
++
++typedef struct elan4io_stats_struct
++{
++    int                      which;
++    unsigned long long ptr;                                   /* always pass pointer as 64 bit */
++} ELAN4IO_STATS_STRUCT;
++
++#define ELAN4IO_STATS                 _IOR ('e', ELAN4IO_GENERIC_BASE + 0, ELAN4IO_STATS_STRUCT)
++#define ELAN4IO_DEVINFO                       _IOR ('e', ELAN4IO_GENERIC_BASE + 1, ELAN_DEVINFO)
++#define ELAN4IO_POSITION              _IOR ('e', ELAN4IO_GENERIC_BASE + 2, ELAN_POSITION)
++
++
++/* 
++ * Definitions for /dev/elan4/controlX
++ */
++#define ELAN4IO_CONTROL_BASE          0x20
++
++#define ELAN4IO_GET_POSITION          _IOR ('e', ELAN4IO_CONTROL_BASE + 0, ELAN_POSITION)
++#define ELAN4IO_SET_POSITION          _IOW ('e', ELAN4IO_CONTROL_BASE + 1, ELAN_POSITION)
++#define ELAN4IO_DEBUG_SNAPSHOT                _IOW ('e', ELAN4IO_CONTROL_BASE + 2, )
++
++typedef struct elan4io_params_mask_struct
++{
++    unsigned short    p_mask;
++    ELAN_PARAMS               p_params;
++} ELAN4IO_PARAMS_STRUCT;
++#define ELAN4IO_GET_PARAMS            _IOR ('e', ELAN4IO_CONTROL_BASE + 3, ELAN4IO_PARAMS_STRUCT)
++#define ELAN4IO_SET_PARAMS            _IOW ('e', ELAN4IO_CONTROL_BASE + 4, ELAN4IO_PARAMS_STRUCT)
++
++/* old versions - implicit p_mask == 3 */
++#define ELAN4IO_OLD_GET_PARAMS                _IOR ('e', ELAN4IO_CONTROL_BASE + 3, ELAN_PARAMS)
++#define ELAN4IO_OLD_SET_PARAMS                _IOW ('e', ELAN4IO_CONTROL_BASE + 4, ELAN_PARAMS)
++
++/*
++ * Definitions for /dev/elan4/userX
++ */
++#define ELAN4IO_USER_BASE             0x40
++
++#define ELAN4IO_FREE                  _IO   ('e', ELAN4IO_USER_BASE + 0)
++#define ELAN4IO_ATTACH                        _IOWR ('e', ELAN4IO_USER_BASE + 1, ELAN_CAPABILITY)
++#define ELAN4IO_DETACH                        _IOWR ('e', ELAN4IO_USER_BASE + 2, ELAN_CAPABILITY)
++#define ELAN4IO_BLOCK_INPUTTER                _IO   ('e', ELAN4IO_USER_BASE + 3)
++
++typedef struct elan4io_add_p2pvp_struct 
++{
++    unsigned        vp_process;
++    ELAN_CAPABILITY vp_capability;
++} ELAN4IO_ADD_P2PVP_STRUCT;
++
++#define ELAN4IO_ADD_P2PVP             _IOW  ('e', ELAN4IO_USER_BASE + 4, ELAN4IO_ADD_P2PVP_STRUCT)
++
++typedef struct elan4io_add_bcastvp_struct
++{
++    unsigned int      vp_process;
++    unsigned int      vp_lowvp;
++    unsigned int      vp_highvp;
++} ELAN4IO_ADD_BCASTVP_STRUCT;
++
++#define ELAN4IO_ADD_BCASTVP           _IOW  ('e', ELAN4IO_USER_BASE + 5, ELAN4IO_ADD_BCASTVP_STRUCT)
++
++#define ELAN4IO_REMOVEVP              _IO   ('e', ELAN4IO_USER_BASE + 6)
++
++typedef struct elan4io_route_struct
++{
++    unsigned int         rt_process;
++    unsigned int         rt_error;
++    E4_VirtualProcessEntry rt_route;
++} ELAN4IO_ROUTE_STRUCT;
++
++#define ELAN4IO_SET_ROUTE             _IOW  ('e', ELAN4IO_USER_BASE + 7, ELAN4IO_ROUTE_STRUCT)
++#define ELAN4IO_RESET_ROUTE           _IOW  ('e', ELAN4IO_USER_BASE + 9, ELAN4IO_ROUTE_STRUCT)
++#define ELAN4IO_GET_ROUTE             _IOWR ('e', ELAN4IO_USER_BASE + 8, ELAN4IO_ROUTE_STRUCT)
++#define ELAN4IO_CHECK_ROUTE           _IOWR ('e', ELAN4IO_USER_BASE + 10, ELAN4IO_ROUTE_STRUCT)
++
++typedef struct elan4io_alloc_cq_struct
++{
++    unsigned int cq_size;                                     /* input: size of queue */
++    unsigned int cq_perm;                                     /* input: requested permissions */
++    unsigned int cq_type;                                     /* input: queue type */
++    unsigned int cq_indx;                                     /* output: queue number */
++} ELAN4IO_ALLOCCQ_STRUCT;
++
++#define ELAN4IO_ALLOCCQ                       _IOWR ('e', ELAN4IO_USER_BASE + 11, ELAN4IO_ALLOCCQ_STRUCT)
++#define ELAN4IO_FREECQ                _IOWR ('e', ELAN4IO_USER_BASE + 12, unsigned)
++
++#define ELAN4IO_CQ_TYPE_REORDER               1                       /* revb reordering command queue */
++
++typedef struct elan4io_perm_struct
++{
++    E4_Addr            ps_eaddr;
++    E4_uint64          ps_len;
++    unsigned long      ps_maddr;
++    unsigned int       ps_perm;
++} ELAN4IO_PERM_STRUCT;
++
++typedef struct elan4io_perm_struct32
++{
++    E4_Addr            ps_eaddr;
++    E4_uint64          ps_len;
++    unsigned int       ps_maddr;
++    unsigned int       ps_perm;
++} ELAN4IO_PERM_STRUCT32;
++
++#define ELAN4IO_SETPERM                       _IOWR ('e', ELAN4IO_USER_BASE + 13, ELAN4IO_PERM_STRUCT)
++#define ELAN4IO_SETPERM32             _IOWR ('e', ELAN4IO_USER_BASE + 13, ELAN4IO_PERM_STRUCT32)
++#define ELAN4IO_CLRPERM                       _IOWR ('e', ELAN4IO_USER_BASE + 14, ELAN4IO_PERM_STRUCT)
++#define ELAN4IO_CLRPERM32             _IOWR ('e', ELAN4IO_USER_BASE + 14, ELAN4IO_PERM_STRUCT32)
++
++typedef struct elan4io_trapsig_struct
++{
++    int               ts_signo;
++} ELAN4IO_TRAPSIG_STRUCT;
++#define ELAN4IO_TRAPSIG                       _IOW  ('e', ELAN4IO_USER_BASE + 15, ELAN4IO_TRAPSIG_STRUCT)
++
++typedef struct elan4io_traphandler_struct
++{
++    unsigned int       th_nticks;                             /* number of ticks to sleep for next trap */
++    unsigned int       th_proc;                                       /* elan processor involved */
++    unsigned long      th_trapp;                              /* space to store trap */
++} ELAN4IO_TRAPHANDLER_STRUCT;
++
++typedef struct elan4io_traphandler_struct32
++{
++    unsigned int       th_nticks;                             /* number of ticks to sleep for next trap */
++    unsigned int       th_proc;                                       /* elan processor involved */
++    unsigned int       th_trapp;                              /* space to store trap */
++} ELAN4IO_TRAPHANDLER_STRUCT32;
++
++#define ELAN4IO_TRAPHANDLER           _IOW  ('e', ELAN4IO_USER_BASE + 16, ELAN4IO_TRAPHANDLER_STRUCT)
++#define ELAN4IO_TRAPHANDLER32         _IOW  ('e', ELAN4IO_USER_BASE + 16, ELAN4IO_TRAPHANDLER_STRUCT32)
++
++typedef struct elan4io_required_mappings_struct
++{
++    E4_Addr    rm_upage_addr;                                 /* elan address of user page */
++    E4_Addr    rm_trestart_addr;                              /* elan address of tproc restart trampoline */
++} ELAN4IO_REQUIRED_MAPPINGS_STRUCT;
++#define ELAN4IO_REQUIRED_MAPPINGS     _IOW  ('e', ELAN4IO_USER_BASE + 17, ELAN4IO_REQUIRED_MAPPINGS_STRUCT)
++
++typedef struct elan4io_resume_eproc_trap_struct
++{
++    E4_Addr             rs_addr;
++} ELAN4IO_RESUME_EPROC_TRAP_STRUCT;
++#define ELAN4IO_RESUME_EPROC_TRAP     _IOW  ('e', ELAN4IO_USER_BASE + 18, ELAN4IO_RESUME_EPROC_TRAP_STRUCT)
++
++typedef struct elan4io_resume_cproc_trap_struct
++{
++    unsigned int      rs_indx;
++} ELAN4IO_RESUME_CPROC_TRAP_STRUCT;
++#define ELAN4IO_RESUME_CPROC_TRAP     _IOW  ('e', ELAN4IO_USER_BASE + 19, ELAN4IO_RESUME_CPROC_TRAP_STRUCT)
++
++typedef struct elan4io_resume_dproc_trap_struct
++{
++    E4_DMA            rs_desc;
++} ELAN4IO_RESUME_DPROC_TRAP_STRUCT;
++#define ELAN4IO_RESUME_DPROC_TRAP     _IOW  ('e', ELAN4IO_USER_BASE + 20, ELAN4IO_RESUME_DPROC_TRAP_STRUCT)
++
++typedef struct elan4io_resume_tproc_trap_struct
++{
++    E4_ThreadRegs     rs_regs;
++} ELAN4IO_RESUME_TPROC_TRAP_STRUCT;
++#define ELAN4IO_RESUME_TPROC_TRAP     _IOW  ('e', ELAN4IO_USER_BASE + 21, ELAN4IO_RESUME_TPROC_TRAP_STRUCT)
++
++typedef struct elan4io_resume_iproc_trap_struct
++{
++    unsigned int      rs_channel;
++    unsigned int      rs_trans;
++    E4_IprocTrapHeader  rs_header;
++    E4_IprocTrapData    rs_data;
++} ELAN4IO_RESUME_IPROC_TRAP_STRUCT;
++#define ELAN4IO_RESUME_IPROC_TRAP     _IOW  ('e', ELAN4IO_USER_BASE + 22, ELAN4IO_RESUME_IPROC_TRAP_STRUCT)
++
++#define ELAN4IO_FLUSH_ICACHE          _IO   ('e', ELAN4IO_USER_BASE + 23)
++#define ELAN4IO_STOP_CTXT             _IO   ('e', ELAN4IO_USER_BASE + 24)
++
++#define ELAN4IO_ALLOC_INTCOOKIE               _IOW  ('e', ELAN4IO_USER_BASE + 25, ELAN4_INTCOOKIE)
++#define ELAN4IO_FREE_INTCOOKIE                _IOW  ('e', ELAN4IO_USER_BASE + 26, ELAN4_INTCOOKIE)
++#define ELAN4IO_ARM_INTCOOKIE         _IOW  ('e', ELAN4IO_USER_BASE + 27, ELAN4_INTCOOKIE)
++#define ELAN4IO_WAIT_INTCOOKIE                _IOW  ('e', ELAN4IO_USER_BASE + 28, ELAN4_INTCOOKIE)
++
++typedef struct elan4io_alloc_trap_queues_struct
++{
++    unsigned int      tq_ndproc_traps;
++    unsigned int      tq_neproc_traps;
++    unsigned int      tq_ntproc_traps;
++    unsigned int      tq_nthreads;
++    unsigned int      tq_ndmas;
++} ELAN4IO_ALLOC_TRAP_QUEUES_STRUCT;
++#define ELAN4IO_ALLOC_TRAP_QUEUES     _IOW  ('e', ELAN4IO_USER_BASE + 29, ELAN4IO_ALLOC_TRAP_QUEUES_STRUCT)
++
++typedef struct elan4io_neterr_msg_struct
++{
++    unsigned int      nm_vp;
++    unsigned int      nm_nctx;
++    unsigned int      nm_retries;
++    unsigned int        nm_pad;
++    ELAN4_NETERR_MSG    nm_msg;
++} ELAN4IO_NETERR_MSG_STRUCT;
++#define ELAN4IO_NETERR_MSG            _IOW ('e', ELAN4IO_USER_BASE + 30, ELAN4IO_NETERR_MSG_STRUCT)
++
++typedef struct elan4io_neterr_timer_struct 
++{
++    unsigned int      nt_usecs;
++} ELAN4IO_NETERR_TIMER_STUCT;
++
++#define ELAN4IO_NETERR_TIMER          _IO  ('e', ELAN4IO_USER_BASE + 31)
++
++typedef struct elan4io_neterr_fixup_struct
++{
++    E4_uint64         nf_cookie;
++    unsigned int      nf_waitforeop;
++    unsigned int      nf_sten;
++    unsigned int      nf_vp;
++    unsigned int      nf_pad;
++} ELAN4IO_NETERR_FIXUP_STRUCT;
++
++#define ELAN4IO_NETERR_FIXUP          _IOW ('e', ELAN4IO_USER_BASE + 32, ELAN4IO_NETERR_FIXUP_STRUCT)
++
++typedef struct elan4io_firecap_struct 
++{
++    ELAN_CAPABILITY     fc_capability;
++    ELAN4_INTCOOKIE     fc_cookie;
++} ELAN4IO_FIRECAP_STRUCT;
++
++#define ELAN4IO_FIRE_INTCOOKIE                _IOW  ('e', ELAN4IO_USER_BASE + 33, ELAN4IO_FIRECAP_STRUCT)
++
++#define ELAN4IO_ALLOC_INTCOOKIE_TABLE _IOW  ('e', ELAN4IO_USER_BASE + 34, ELAN_CAPABILITY)
++#define ELAN4IO_FREE_INTCOOKIE_TABLE  _IO   ('e', ELAN4IO_USER_BASE + 35)
++
++typedef struct elan4io_translation
++{
++    E4_Addr           tr_addr;
++    unsigned long     tr_len;
++    unsigned int      tr_access;
++} ELAN4IO_TRANSLATION_STRUCT;
++
++#define ELAN4IO_LOAD_TRANSLATION      _IOW  ('e', ELAN4IO_USER_BASE + 36, ELAN4IO_TRANSLATION_STRUCT)
++#define ELAN4IO_UNLOAD_TRANSLATION    _IOW  ('e', ELAN4IO_USER_BASE + 37, ELAN4IO_TRANSLATION_STRUCT)
++
++typedef struct elan4io_dumpcq_struct32
++{
++    E4_uint64 cq_space;      /* output: sdram addr of q, used to decode ptrs */
++    E4_uint32 cq_size;       /* output: The real size of the command queue */
++    E4_uint32 bufsize;       /* input: The size of the buffer to dump to */
++    E4_uint32 cq_indx;       /* input: index of cq to dump */
++    unsigned int buffer;     /* input: user address of rgs->buffer to dump to */
++} ELAN4IO_DUMPCQ_STRUCT32;
++
++typedef struct elan4io_dumpcq_struct
++{
++    E4_uint64 cq_space;      /* output: sdram addr of q, used to decode ptrs */
++    E4_uint32 cq_size;       /* output: The real size of the command queue */
++    E4_uint32 bufsize;       /* input: The size of the buffer to dump to */
++    E4_uint32 cq_indx;       /* input: index of cq to dump */
++    unsigned long buffer;    /* input: user address of rgs->buffer to dump to */
++} ELAN4IO_DUMPCQ_STRUCT;
++
++#define ELAN4IO_DUMPCQ                        _IOWR ('e', ELAN4IO_USER_BASE + 38, ELAN4IO_DUMPCQ_STRUCT)
++#define ELAN4IO_DUMPCQ32                      _IOWR ('e', ELAN4IO_USER_BASE + 38, ELAN4IO_DUMPCQ_STRUCT32)
++
++/* mmap offsets - - we define the file offset space as follows:
++ *
++ * page 0 - 4095 - command queues
++ * page 4096   - device user registers
++ * page 4097   - flag page/user stats
++ * page 4098   - device stats
++ * page 4099     - tproc trampoline
++ */
++
++#define ELAN4_OFF_COMMAND_QUEUES      0
++#define ELAN4_OFF_USER_REGS           4096
++#define ELAN4_OFF_USER_PAGE           4097
++#define ELAN4_OFF_DEVICE_STATS                4098
++#define ELAN4_OFF_TPROC_TRAMPOLINE    4099
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++#endif /* __ELAN4_IOCTL_H */
+Index: linux-2.4.21/include/elan4/mmu.h
+===================================================================
+--- linux-2.4.21.orig/include/elan4/mmu.h      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan4/mmu.h   2005-06-01 23:12:54.738417976 -0400
+@@ -0,0 +1,94 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: mmu.h,v 1.11 2004/04/21 12:04:24 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/mmu.h,v $*/
++
++
++#ifndef __ELAN4_MMU_H
++#define __ELAN4_MMU_H
++
++typedef struct elan4_hash_entry
++{
++    struct elan4_hash_entry   *he_next;
++    struct elan4_hash_entry   *he_prev;
++
++    sdramaddr_t                        he_entry;
++    
++    struct elan4_hash_entry   *he_chain[2];
++    E4_uint64                  he_tag[2];
++    E4_uint32                  he_pte[2];
++} ELAN4_HASH_ENTRY;
++
++#define ELAN4_HENT_CHUNKS     16              /* SDRAM_MIN_BLOCK_SIZE/sizeof (E4_HashTableEntry) */
++
++typedef struct elan4_hash_chunk
++{
++    struct list_head            hc_link;
++    ELAN4_HASH_ENTRY          hc_hents[ELAN4_HENT_CHUNKS];
++} ELAN4_HASH_CHUNK;
++
++typedef struct elan4_hash_cache
++{
++    E4_Addr           hc_start;
++    E4_Addr           hc_end;
++    int                     hc_tbl;
++
++    ELAN4_HASH_ENTRY *hc_hes[1];
++} ELAN4_HASH_CACHE;
++
++/* 
++ * he_pte is really 4 bytes of pte "type" one for each pte
++ * entry - however we declare it as an "int" so we can
++ * easily determine that all 4 entries are invalid 
++ */
++#define HE_SET_PTE(he,tagidx,pteidx,val)      (((E4_uint8 *) &(he->he_pte[tagidx]))[pteidx] = (val))
++#define HE_GET_PTE(he,tagidx,pteidx)          (((E4_uint8 *) &(he->he_pte[tagidx]))[pteidx])
++
++/*
++ * he_tag has the following form :
++ *     [63:27]        tag
++ *     [20:17]  pte valid
++ *     [16]     locked
++ *     [15]     copy
++ *     [14]     valid
++ *     [13:0] context
++ */
++
++#define HE_TAG_VALID          (1 << 14)
++#define HE_TAG_COPY           (1 << 15)
++#define HE_TAG_LOCKED         (1 << 16)
++
++#define INVALID_CONTEXT               0
++
++extern u_char elan4_permtable[];
++#define ELAN4_INCOMPAT_ACCESS(perm,access) ((elan4_permtable[(perm)] & (1 << (access))) == 0)
++extern u_char elan4_permreadonly[];
++#define ELAN4_PERM_READONLY(perm)       (elan4_permreadonly[(perm)])
++
++/* return code from elan4mmu_categorise_paddr */
++#define ELAN4MMU_PADDR_SDRAM          0
++#define ELAN4MMU_PADDR_COMMAND                1
++#define ELAN4MMU_PADDR_LOCALPCI               2
++#define ELAN4MMU_PADDR_PAGE           3
++#define ELAN4MMU_PADDR_OTHER          4
++
++extern int elan4_debug_mmu;
++
++#ifdef DEBUG_PRINTF
++#  define MPRINTF(ctxt,lvl,args...)   (elan4_debug_mmu > (lvl) ? elan4_debugf(ctxt,DBG_MMU, ##args) : (void)0)
++#else
++#  define MPRINTF(ctxt,lvl,args...)   ((void) 0)
++#endif
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++#endif /* __ELAN4_MMU_H */
+Index: linux-2.4.21/include/elan4/neterr.h
+===================================================================
+--- linux-2.4.21.orig/include/elan4/neterr.h   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan4/neterr.h        2005-06-01 23:12:54.738417976 -0400
+@@ -0,0 +1,40 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2004 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN4_NETERR_H
++#define __ELAN4_NETERR_H
++
++#ident "@(#)$Id: neterr.h,v 1.1 2004/01/19 14:38:34 david Exp $ $Name: QSNETMODULES-4-30_20050128 $"
++/*      $Source: /cvs/master/quadrics/elan4mod/neterr.h,v $*/
++
++typedef struct elan4_neterr_msg
++{
++    E4_uint8          msg_type;
++    E4_uint8          msg_waitforeop;
++    E4_uint16         msg_context;                            /* network context # message sent to */
++    E4_int16          msg_found;                              /* # cookie found (response) */
++
++    ELAN_LOCATION     msg_sender;                             /* nodeid/context # message sent from */
++    E4_uint32         msg_pad;
++
++    E4_uint64         msg_cookies[6];                         /* 64 bit cookies from identify packets */
++} ELAN4_NETERR_MSG;
++
++#define ELAN4_NETERR_MSG_SIZE         sizeof (ELAN4_NETERR_MSG)
++#define ELAN4_NETERR_MSG_REQUEST      1
++#define ELAN4_NETERR_MSG_RESPONSE     2
++
++#define ELAN4_NETERR_MAX_COOKIES      (sizeof (((ELAN4_NETERR_MSG *) 0)->msg_cookies) / \
++                                       sizeof (((ELAN4_NETERR_MSG *) 0)->msg_cookies[0]))
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++#endif /* __ELAN4_NETERR_H */
+Index: linux-2.4.21/include/elan4/pci.h
+===================================================================
+--- linux-2.4.21.orig/include/elan4/pci.h      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan4/pci.h   2005-06-01 23:12:54.739417824 -0400
+@@ -0,0 +1,227 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN4_PCI_H 
++#define __ELAN4_PCI_H
++
++#ident "$Id: pci.h,v 1.32 2003/09/04 12:39:17 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan4hdr/pci.h,v $*/
++
++/* Elan has 2 64 bit bars */
++#define ELAN4_BAR_SDRAM                       0
++#define ELAN4_BAR_REGISTERS           2
++
++#define PCI_VENDOR_ID_QUADRICS                0x14fc
++#define PCI_DEVICE_ID_ELAN3           0x0000
++#define   PCI_REVISION_ID_ELAN3_REVA  0x0000
++#define   PCI_REVISION_ID_ELAN3_REVB  0x0001
++#define PCI_DEVICE_ID_ELAN4           0x0001
++#define   PCI_REVISION_ID_ELAN4_REVA  0x0000
++#define   PCI_REVISION_ID_ELAN4_REVB  0x0001
++
++/* support standard pseudo bars */
++#define ELAN4_PSEUDO_BAR_ROM          8
++
++/* Elan PCI control
++ configuration space register. ElanControlRegister */
++#define PCI_ELAN_PARITY_ADDR_LO               0x40
++#define PCI_ELAN_PARITY_ADDR_HI               0x44
++#define PCI_ELAN_PARITY_TYPE          0x48
++#define PCI_ELAN_CONTROL              0x4c
++#define PCI_ELAN_PLL_CONTROL          0x50
++#define PCI_ELAN_SPLIT_MESSAGE_ATTR   0x54
++#define PCI_ELAN_SPLIT_MESSAGE_VALUE  0x54
++#define PCI_ELAN_RAMBIST_FAILED               0x54
++#define PCI_ELAN_TOPPHYSADDR(i)               (0x58 + ((i)<<1))
++
++/*
++ * [31]          PciM66EN             This is set it the bus is running in PCI2.3 - 66MHz mode.
++ * [30:28] InitPattern                This gives the PCI-X startup mode. See "Pci intialisation patterns" below.
++ * [27]          notBusIs64Bits       If set the bus is running 32 bits wide. If Clear it is a 64 bit bus.
++ * [26:24] RamBistCntl                Used to control the Elan4 RAM BIST. Not acitive it zero.
++ * [23]          RamBistFinished      Only used when performing the RAM BIST test.
++ * [22]          SelectSplitMessAttr  See ECTRL_SELECT_SPLIT_MESS_ATTR below.
++ * [21]          ReceivedSplitCompError See ECTRL_REC_SPLIT_COMP_MESSAGE below
++ * [20:16] WriteHighPriTime   Used with ReadHighPriTime to control the ratio of PCI master write to PCI master
++ *                            read bandwidth under heavy load. The high the value of WriteHighPriTime the longer
++ *                            the PCI write bursts will be allowed without interruption from a read transfer.
++ * [15]    DisableCouplingTest        This is only used as part of the RAM BIST test. It effects the testing of the main
++ *                            cache tag RAMS.
++ * [14:13] Not used           Will read as zero.
++ * [12:8]  ReadHighPriTime    Used with WriteHighPriTime to control the ratio of PCI master write to PCI master
++ *                            read bandwidth under heavy load. The high the value of ReadHighPriTime the longer
++ *                            the PCI read bursts will be allowed without interruption from a write transfer.
++ * [7] EnableLatencyCountReset  This bit effect the behaviour of disconnects due to the removal of GNT# after the latency
++ *                            counter has expired. If set it will allow the latency counter to be reset each time the
++ *                            GNT# is reasserted. If asserted it should provided improved bandwidth on the PCI bus
++ *                            without increasing the maximum latency another device would have for access to the bus.
++ *                            It will increase the average latency of other devices.
++ * [6] ExtraMasterAddrBits    This bit used to control the physical PCI addresses generated by the MMU.
++ * [5] ReducedPciDecode               If set the PCI local memory BAR will decode 256Mbytes of PCI address space. If clear it
++ *                            will decode 2Gbyte of PCI address space.
++ * [4] ConfigInEBusRom                If set the constant values of the Elan4 PCI configuration space will be taken from the
++ *                            EEPROM. If clear the internal values will be used.
++ * [3] EnableRd2_2Bursts      This bit only effects the behaviour of burst reads when the PCI bus is operating in
++ *                            PCI-2.2 mode. It allows adjacent reads to be merged into longer bursts for higher
++ *                            performance.
++ * [2] SoftIntReset           If set this bit will cause the Elan4 to reset itself with the exception of the PCI
++ *                            configuation space. All internal state machines will be put into the reset state.
++ * [1] EnableWrBursts         This bit allows much longer PCI-X write bursts. If set it will stop the Elan4 from
++ *                            being completely PCI-X compliant as the Elan4 may request a long PCI-X write burst that
++ *                            it does not complete. However it should significantly increase the maximum PCI-X write
++ *                            bandwidth and is unlikely to cause problems with many PCI-X bridge chips.
++ * [0] InvertMSIPriority      This bit effect the way MSI interrupts are generated. It provides flexiblity to generate
++ *                            the MSI interrupts in a different way to allow for different implimentations of MSI
++ *                            logic and still give the correct priority of Elan4 interrupts.
++ *
++ *    {PciM66EN, InitPattern, notBusIs64Bits, RamBistCntl, RamBistFinished,
++ *     SelectSplitMessAttr, ReceivedSplitCompError, WriteHighPriTime,
++ *     DisableCouplingTest, 2'h0, ReadHighPriTime,
++ *     EnableLatencyCountReset, ExtraMasterAddrBits, ReducedPciDecode, ConfigInEBusRom,
++ *     EnableRd2_2Bursts, SoftIntReset, EnableWrBursts, InvertMSIPriority}
++ */
++
++#define ECTRL_INVERT_MSI_PRIO         (1 << 0)
++#define ECTRL_ENABLE_WRITEBURSTS      (1 << 1)
++#define ECTRL_SOFTWARE_INTERNAL_RESET (1 << 2)
++#define ECTRL_ENABLE_2_2READBURSTS    (1 << 3)
++#define ECTRL_CONFIG_IN_EBUS_ROM      (1 << 4)
++#define ECTRL_28_NOT_30_BIT_LOCAL_BAR (1 << 5)
++#define ECTRL_ExtraMasterAddrBits     (1 << 6)
++#define ECTRL_ENABLE_LATENCY_RESET      (1 << 7)
++#define ECTRL_DISABLE_COUPLING_TEST   (1 << 15)
++
++/*
++ * Ratio of the following two registers set the relative bandwidth given to intputer data
++ * versus other PCI pci traffic when scheduling new PCI master accesses.
++ */
++#define ECTRL_OTHER_HIGH_PRI_TIME_SHIFT       (8)     /* Sets top 4 bits of 8 bit counter */
++#define ECTRL_OTHER_HIGH_PRI_TIME_MASK        (0x1f)
++
++
++#define ECTRL_IPROC_HIGH_PRI_TIME_SHIFT       (16)    /* Sets top 4 bits of 8 bit counter */
++#define ECTRL_IPROC_HIGH_PRI_TIME_MASK        (0x1f)
++
++/*
++ * This is set if a split completion message is received.
++ * This will cause a PCI error interrupt.
++ * This error is cleared by writting a 1 to this bit.
++ */
++#define ECTRL_REC_SPLIT_COMP_MESSAGE  (1 << 21)
++/*
++ * This bit is used to select reading of either the Split message attribute value when
++ * set or the split completion message data value from 0x54 in the config space
++ * if the ECTRL_REC_SPLIT_COMP_MESSAGE bit is set. 0x54 returns the the BistFailed flags
++ * if any of the BIST control bits are set (bits 26 to 24)
++ */
++#define ECTRL_SELECT_SPLIT_MESS_ATTR  (1 << 22)
++
++// Internal RAM bist control bits.
++// Three bits of state control the RAM BIST (Built in self test).
++//
++// These bits must not be set unless the ECTRL_SOFTWARE_INTERNAL_RESET bit has also been set!
++//
++// For a normal fast ram test assert ECTRL_BIST_FAST_TEST. 
++// For a data retention test first write ECTRL_START_RETENTION_TEST then wait the retention period of
++// at least 1ms and preferably much longer then write ECTRL_CONTINUE_RETENTION_TEST then wait
++// again and finallly write ECTRL_FINISH_RETENTION_TEST.
++// 
++// The read only bit ECTRL_BIST_FINISHED_TEST can be polled to check that the test has compleated.
++#define ECTRL_BIST_CTRL_SHIFT         (24)
++#define ECTRL_BIST_CTRL_MASK          (7 << 24)
++
++#define ECTRL_BIST_FAST_TEST          ((7 << 24) | ECTRL_SOFTWARE_INTERNAL_RESET)     // old scheme
++#define ECTRL_START_RETENTION_TEST    ((1 << 24) | ECTRL_SOFTWARE_INTERNAL_RESET)
++#define ECTRL_CONTINUE_RETENTION_TEST ((3 << 24) | ECTRL_SOFTWARE_INTERNAL_RESET)
++#define ECTRL_FINISH_RETENTION_TEST   ((7 << 24) | ECTRL_SOFTWARE_INTERNAL_RESET)
++
++#define ECTRL_BIST_KICK_OFF           ((1 << 24) | ECTRL_SOFTWARE_INTERNAL_RESET)     // new scheme
++#define ECTRL_BIST_MOVE_ON_ODD                ((3 << 24) | ECTRL_SOFTWARE_INTERNAL_RESET)
++#define ECTRL_BIST_MOVE_ON_EVEN               ((5 << 24) | ECTRL_SOFTWARE_INTERNAL_RESET)
++#define ECTRL_BIST_SCREAM_THROUGH     ((7 << 24) | ECTRL_SOFTWARE_INTERNAL_RESET)
++
++#define ECTRL_CLEAR_BIST_TEST         (0 << 24)
++#define ECTRL_BIST_FINISHED_TEST      (1 << 23)
++
++// Read only current PCI bus type.
++#define ECTRL_RUNNING_32BIT_MODE      (1 << 27)
++#define ECTRL_INITIALISATION_MODE     (7 << 28)
++#define ECTRL_RUNNING_M66EN_MODE      (1 << 31)
++
++#define ECTRL_INIT_PATTERN_SHIFT      (28)
++#define ECTRL_INIT_PATTERN_MASK               (0x7)
++
++// Pci intialisation patterns
++#define Pci2_2                                (0 << 28)
++#define PciX50To66MHz                 (1 << 28)
++#define PciX66to100MHz                        (2 << 28)
++#define PciX100to133MHz                       (3 << 28)
++#define PciXReserved1                 (4 << 28)
++#define PciXReserved2                 (5 << 28)
++#define PciXReserved3                 (6 << 28)
++#define PciXReserved4                 (7 << 28)
++
++/* Elan PCI pll and pad control configuration space register. ElanPllControlReg */
++// This overrides the default PCI pll control settings.
++#define PciPll_FeedForwardISel0               (1 << 0)        // Lsi name Z0
++#define PciPll_FeedForwardISel1               (1 << 1)        // Lsi name Z1
++#define PciPll_ChargePumpISel0                (1 << 2)        // Lsi name P0
++#define PciPll_ChargePumpISel1                (1 << 3)        // Lsi name P1
++#define PciPll_EnableAutoReset                (1 << 4)        // Lsi name ENARST
++#define PciPll_RSEL200500             (1 << 5)        // Lsi name Range Select, 0: 100 - 250MHz, 1: 200 - 500MHz
++#define PciPll_DivideFeedback         (1 << 6)        // Just used for test - This divides the shortcut feedback to the PCI PLL so that it can lock to the tester clock.
++#define PciPll_CutFeedback            (1 << 7)        // Just used for test - This disables the shortcut feedback.
++
++// This overrides the default PCI BZ controler settings.
++#define PciBZ_UPDI                    (0xf << 8)
++#define PciBZ_WAIT_INT                        (0xf << 12)
++
++// This overrides the default Sys and SDRam pll control settings.
++#define SysPll_FeedForwardISel0               (1 << 16)       // Lsi name P0     
++#define SysPll_FeedForwardISel1               (1 << 17)       // Lsi name P1     
++#define SysPll_ChargePumpISel0                (1 << 18)       // Lsi name Z0    
++#define SysPll_ChargePumpISel1                (1 << 19)       // Lsi name Z1    
++#define SysPll_EnableAutoReset                (1 << 20)       // Lsi name ENARST
++#define SysPll_DivPhaseCompInBy2      (1 << 21)       // Lsi name NODIV (Should be DIV)
++#define SysPll_PllTestClkSel          (1 << 22)       // If asserted the master clock source is not taken from the pll.
++
++#define Pll_ForceEBusADTristate               (1 << 23)       // Required to enable the testing of EnableAutoReset. Enables use of EBusAD[7] (rev A)
++#define Pll_LinkErrDirectToSDA                (1 << 23)       // Access to link error flag for triggering (rev B)
++
++
++#define ECTRL_SYS_CLOCK_RATIO_SHIFT   (24)
++// Config: with 800MHz                Speeds are 266 200 160 133.
++//         0 = 133/133 (1:1)  6:6     1
++//       1 = 160/133 (6:5)    5:6     1.2
++//         2 = 200/133 (3:2)  4:6     1.5
++//       3 = 266/133 (2:1)    3:6     2
++//       4 = 200/200 (1:1)    4:4     1
++//       5 = 266/200 (4:3)    3:4     1.33
++
++// Config: with 600MHz                Speeds are 200 150 120 100
++//         0 = 100/100 (1:1)  6:6     1
++//       1 = 120/100 (6:5)    5:6     1.2
++//         2 = 150/100 (3:2)  4:6     1.5
++//       3 = 200/100 (2:1)    3:6     2
++//       4 = 150/150 (1:1)    4:4     1
++//       5 = 200/150 (4:3)    3:4     1.33
++
++#define ECTRL_SYS_CLOCK_RATIO_SHIFT   (24)
++#define ECTRL_SYS_CLOCK_RATIO_1_1Slow (0 << ECTRL_SYS_CLOCK_RATIO_SHIFT)
++#define ECTRL_SYS_CLOCK_RATIO_6_5     (1 << ECTRL_SYS_CLOCK_RATIO_SHIFT)
++#define ECTRL_SYS_CLOCK_RATIO_3_2     (2 << ECTRL_SYS_CLOCK_RATIO_SHIFT)
++#define ECTRL_SYS_CLOCK_RATIO_2_1     (3 << ECTRL_SYS_CLOCK_RATIO_SHIFT)
++#define ECTRL_SYS_CLOCK_RATIO_1_1Fast (4 << ECTRL_SYS_CLOCK_RATIO_SHIFT)
++#define ECTRL_SYS_CLOCK_RATIO_4_3     (5 << ECTRL_SYS_CLOCK_RATIO_SHIFT)
++#define ECTRL_SYS_CLOCK_MAX_NORMAL    (6)                                     /* used to generate a valid random value */
++#define GET_RANDOM_CLOCK_RATIO                (Random(ECTRL_SYS_CLOCK_MAX_NORMAL) << ECTRL_SYS_CLOCK_RATIO_SHIFT)
++#define ECTRL_SYS_CLOCK_RATIO_PLL_TEST        (6 << ECTRL_SYS_CLOCK_RATIO_SHIFT)
++#define ECTRL_SYS_CLOCK_RATIO_TEST    (7 << ECTRL_SYS_CLOCK_RATIO_SHIFT)
++#define ECTRL_SYS_CLOCK_RATIO_MASK    (7 << ECTRL_SYS_CLOCK_RATIO_SHIFT)
++
++#endif /* __ELAN4_PCI_H */
+Index: linux-2.4.21/include/elan4/registers.h
+===================================================================
+--- linux-2.4.21.orig/include/elan4/registers.h        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan4/registers.h     2005-06-01 23:12:54.742417368 -0400
+@@ -0,0 +1,1588 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef _ELAN4_REGISTERS_H
++#define _ELAN4_REGISTERS_H
++
++#ident "$Id: registers.h,v 1.117.2.1 2004/10/04 14:26:18 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan4hdr/registers.h,v $*/
++
++/*
++ * Header file for internal slave mapping of the ELAN4 registers
++ */
++
++#define E4_CACHELINE_SIZE     (64)
++#define E4_STACK_ALIGN                (64)
++
++#ifndef _ASM
++
++#include <elan4/types.h>
++#include <elan4/dma.h>
++#include <elan4/userregs.h>
++
++typedef volatile struct _E4_CacheSets
++{
++   E4_uint64  Set0[1024];     /* 8k bytes per set */
++   E4_uint64  Set1[1024];     /* 8k bytes per set */
++   E4_uint64  Set2[1024];     /* 8k bytes per set */
++   E4_uint64  Set3[1024];     /* 8k bytes per set */
++} E4_CacheSets;
++
++typedef union e4_cache_tag
++{
++   struct {
++       E4_uint32 pad0;                        /* Undefined value when read */
++#if (BYTE_ORDER == LITTLE_ENDIAN) || defined(__LITTLE_ENDIAN__)
++       E4_uint32 :10;                                         /* 0-9   - reserved */
++       E4_uint32 LineError:1;                                 /* 10    - line error */
++       E4_uint32 Modified:1;                                  /* 11    - modified */
++       E4_uint32 FillPending:1;                                       /* 12    - fill pending */
++       E4_uint32 AddrTag30to13:18;                            /* 30-13 - tag */
++       E4_uint32 :1;                                          /* 31    -  */
++#else
++       E4_uint32 :1;                                          /* 31    -  */
++       E4_uint32 AddrTag30to13:18;                            /* 30-13 - tag */
++       E4_uint32 FillPending:1;                                       /* 12    - fill pending */
++       E4_uint32 Modified:1;                                  /* 11    - modified */
++       E4_uint32 LineError:1;                                 /* 10    - line error */
++       E4_uint32 :10;                                         /* 0-9   - reserved */
++#endif
++   } s;
++   E4_uint64  Value;
++} E4_CacheTag;
++
++typedef volatile struct _E4_CacheTags
++{
++   E4_CacheTag Tags[4][128];  /* 8k bytes per set, 64 byte cache line */
++} E4_CacheTags;
++
++#define E4_NumCacheSets               4
++#define E4_NumCacheLines      128
++#define E4_CacheLineSize      64
++#define E4_CacheSize          (E4_NumCacheSets * E4_NumCacheLines * E4_CacheLineSize)
++#define E4_CacheSetSize       (E4_NumCacheLines * E4_CacheLineSize)
++
++/*
++ * Run Queue pointers 
++ *
++ * [62:35]    FrontPointer[30:3]
++ * [33:32]    Size Value
++ * [30:3]     BackPointer[30:3]
++ */
++#define E4_QueuePtrMask               (0x7ffffff8ULL)
++#define E4_QueueSizeMask      3
++#define E4_QueueEntrySize       sizeof (E4_uint64)
++
++#define E4_Queue8KBytes               0
++#define E4_Queue64KBytes      1
++#define E4_Queue512KBytes     2
++#define E4_Queue4MBytes               3
++
++#define E4_QueueFrontValue(val,size)  ((val) | (size))
++#define E4_QueueValue(queue,size)     (((E4_uint64) E4_QueueFrontValue(queue,size)) << 32 | ((E4_uint64) (queue)))
++
++#define E4_QueueFrontPointer(val)     /* extract queue front pointer from register */\
++      (((val) >> 32) & E4_QueuePtrMask)
++#define E4_QueueBackPointer(val)      /* extract queue back pointer from register */ \
++      ((val) & E4_QueuePtrMask)
++#define E4_QueueSizeValue(val)                /* extract queue size value from register */ \
++      (((val) >> 32) & E4_QueueSizeMask)
++#define E4_QueueSize(value)           /* queue size in bytes from size value */ \
++      (1 << (((value)*3) + 13))
++#define E4_QueueOffsetMask(fptr)\
++        ((8192 << (((fptr) & E4_QueueSizeMask) << 3)) - 1)
++#define E4_QueueOffset(fptr)\
++        ((fptr) & E4_QueueOffsetMask(fptr))
++#define E4_QueueFrontPointerInc(fptr)   \
++        ( ((fptr) & ~E4_QueueOffsetMask(fptr)) | ((E4_QueueOffset(fptr) + 8) & E4_QueueOffsetMask(fptr)) )
++
++typedef union _E4_QueuePtr
++{
++   E4_uint64  Value;
++   struct {
++       E4_uint32 Back;
++       E4_uint32 Front;
++   } s;
++} E4_QueuePtr;
++
++/*
++ * DMA processor status register.
++ *
++ * [48]               FirstSendTrans          Set for the first packet of a dma.
++ * [47:46]    TimeSliceCount          Time left to timeslice.
++ * [45]               DmaLastPacket           Set for the last packet of a dma.
++ * [44]               CurrPrefetchDma         Dma descriptor the prefetcher is valid for.
++ * [43:39]    PrefetcherState         Dma prefetcher's state machines value.
++ * [38:33]    PacketAssemblyState     Packet assembler's state machines value.
++ * [32:31]    PrefetcherWakeupFnt     Dma prefetcher's wakeup function.
++ * [30:28]    PacketAssWakeupFnt      Packet assembler's wakeup function.
++ * [27]               AckBufferValid          Packet ack is valid.
++ * [26]               PrefetchedDataProblem   Had either a data read fault or data error. Valid if AckBufferValid.
++ * [25]               PrefetcherHalting       Prefetch data about to stop for halt. Valid if AckBufferValid.
++ * [24]               PacketTimeout           Packet timeout. Sent an EopError. Valid if AckBufferValid set.
++ * [23:22]    PacketAckValue          Packet ack type. Valid if AckBufferValid set.
++ * [21:20]    FaultUnitNo             Set if the dma prefetcher has faulted.
++ * [19:17]    TrapType                Packet assembler's trap type.
++ * [16]               PrefetcherFault         Set if the dma prefetcher has faulted for this DMA unit.
++ * [15]               Remote                  The Dma had been issued remotly
++ * [14]               Priority                Running at high priority.
++ * [13:0]     Context                 procs current context.
++ */
++
++#define DPROC_FirstSendTrans(s)               ((unsigned)((s) >> 48) & 1)
++#define DPROC_TimeSliceCount(s)               ((unsigned)(((s) >> 46) & 3)
++#define DPROC_DmaLastPacket(s)                ((unsigned)((s) >> 45) & 1)
++#define DPROC_CurrPrefetchDma(s)      ((unsigned)((s) >> 44) & 1)
++#define DPROC_PrefetcerState(s)               ((unsigned)((s) >> 39) & 0x1f)
++#define DPROC_PacketAssemblerState(s) ((unsigned)((s) >> 33) & 0x1f)
++#define DPROC_PrefetcherWakeupFn(s)   ((unsigned)((s) >> 31) & 3)
++#define DPROC_PacketAssemblerWakeupFn(s)((unsigned)((s) >> 28) & 3)
++#define DPROC_AckBufferValid(s)               ((unsigned)((s) >> 27) & 1)
++#define DPROC_PrefetcherDataProblem(s)        ((unsigned)((s) >> 26) & 1)
++#define DPROC_PrefetcherHalting(s)    ((unsigned)((s) >> 25) & 1)
++#define DPROC_PacketTimeout(s)                ((unsigned)((s) >> 24) & 1)
++#define DPROC_PacketAckValue(s)               ((unsigned)((s) >> 22) & 3)
++#define DPROC_FaultUnitNo(s)          ((unsigned)((s) >> 20) & 3)
++#define DPROC_TrapType(s)             ((unsigned)((s) >> 17) & 7)
++#define DPROC_PrefetcherFault(s)      ((unsigned)((s) >> 16) & 1)
++#define DPROC_Remote(s)                       ((unsigned)((s) >> 15) & 1)
++#define DPROC_Priority(s)             ((unsigned)((s) >> 14) & 1)
++#define DPROC_Context(s)              ((unsigned)(s) & 0x3fff)
++
++/*
++ * Command processor status register.
++ *
++ * [26:21]    CPState         procs current state.
++ * [20]               WakeupFnt       procs wakeup function.
++ * [19:16]    TrapValue       procs trap value.
++ * [15]               Remote          Issued remotely.
++ * [14]               Priority        Running at high priority.
++ * [13:0]     Context         procs current context.
++ */
++
++#define CPROC_TrapType(s)             ((unsigned)((s) >> 16) & 0xf)
++#define CPROC_Remote(s)                       ((unsigned)((s) >> 15) & 0x1)
++#define CPROC_Priority(s)             ((unsigned)((s) >> 14) & 0x1)
++#define CPROC_Context(s)              ((unsigned)(s) & 0x3fff)
++
++/*
++ * Event processor status register.
++ *
++ * [34:30]    CPState         event procs current state.
++ * [29:28]    WakeupFnt       event procs wakeup function.
++ * [27:20]    EventCopySize   This is the number of DWords to still be copied on a copy dword event.
++ * [19]               EProcPort1Fault CUN_EventProc1 has taken a translation fault.
++ * [18]               EProcPort0Fault CUN_EventProc0 has taken a translation fault.
++ * [17:16]    TrapValue       event proc's trap value.
++ * [15]               Remote          Issued remotely.
++ * [14]               Priority        Running at high priority.
++ * [13:0]     Context         procs current context.
++ */
++
++#define EPROC_CPState(s)              ((unsigned)((s) >> 30) & 0x1f)
++#define EPROC_WakeupFunction(s)               ((unsigned)((s) >> 28) & 3)
++#define EPROC_CopySize(s)             ((unsigned)((s) >> 20) & 0xFF)
++#define EPROC_Port1Fault(s)           ((unsigned)((s) >> 19) & 1)
++#define EPROC_Port0Fault(s)           ((unsigned)((s) >> 18) & 1)
++#define EPROC_TrapType(s)             ((unsigned)((s) >> 16) & 3)
++#define EPROC_Remote(s)                       ((unsigned)((s) >> 15) & 1)
++#define EPROC_Priority(s)             ((unsigned)((s) >> 14) & 1)
++#define EPROC_Context(s)              ((unsigned)(s) & 0x3fff)
++
++/*
++ * Thread processor status register.
++ *
++ * [39:24]    MemPortBusy             16 bits of port busy flags for all FFU memory ports.
++ * [23:21]    Reads as zero
++ * [20:18]    TQState                 State vector for thread queuing proc.
++ * [17]               HighRunQueueFull        High priority run queue is full
++ * [16]               LowRunQueueFull         Low priority run queue is full
++ * [15]               ReadyHigh               More runable threads at high priority
++ * [14]               ReadyLow                More runable threads at low priority
++ * [13:0]     Context                 procs current context.
++ */
++#define TPROC_HighRunQueueFull(s)     ((unsigned)((s) >> 17) & 1)
++#define TPROC_LowRunQueueFull(s)      ((unsigned)((s) >> 16) & 1)
++#define TPROC_ReadyHigh(s)            ((unsigned)((s) >> 15) & 1)
++#define TPROC_ReadyLow(s)             ((unsigned)((s) >> 14) & 1)
++#define TPROC_Context(s)              ((unsigned)((s) & 0x3fff))
++
++/*
++ * Input processor status register
++ *
++ * [55]               Last Trans (~EOP)
++ * [54]               First Trans (~EOP)
++ * [53]               Channel (~EOP) 
++ * [52]               Bad Length (~EOP)
++ * [51:50]    Trans CRC Status (~EOP)
++ * [49:48]    EOP type
++ * [47]               EOP trap
++ * [46]               Trapping priority
++ * [45]               Trapping Channel
++ * [44:43]    Bad ack sent
++ * [42:41]    Good ack sent
++ * [40]               Queueing Packet (~EOP)
++ * [39:36]    Channel trapped bits
++ * [35:32]    IProc Trap Value
++ * [31:16]    Network Context (~EOP)
++ * [15:0]     Transaction Type (~EOP)
++ */
++#define IPROC_LastTrans(s)            ((unsigned)((s) >> 55) & 0x1)
++#define IPROC_FirstTrans(s)           ((unsigned)((s) >> 54) & 0x1)
++#define IPROC_Channel(s)              ((unsigned)((s) >> 53) & 0x1)
++#define IPROC_BadLength(s)            ((unsigned)((s) >> 52) & 0x1)
++#define IPROC_TransCRCStatus(s)               ((unsigned)((s) >> 50) & 0x3)
++#define IPROC_EOPType(s)              ((unsigned)((s) >> 48) & 0x3)
++#define IPROC_EOPTrap(s)              ((unsigned)((s) >> 47) & 0x1)
++#define IPROC_InputterPri(s)          ((unsigned)((s) >> 46) & 0x1)
++#define IPROC_InputterChan(s)         ((unsigned)((s) >> 45) & 0x1)
++#define IPROC_BadAckSent(s)           ((unsigned)((s) >> 43) & 0x3)
++#define IPROC_GoodAckSent(s)          ((unsigned)((s) >> 41) & 0x3)
++#define IPROC_QueueingPacket(s)               ((unsigned)((s) >> 40) & 0x1)
++#define IPROC_ChannelTrapped(s)               ((unsigned)((s) >> 36) & 0xF)
++#define IPROC_TrapValue(s)            ((unsigned)((s) >> 32) & 0xF)
++#define IPROC_NetworkContext(s)               ((unsigned)((s) >> 16) & 0xFFFF)
++#define IPROC_TransactionType(s)      ((unsigned)(s) & 0xFFFF)
++
++/* values for IPROC_TransCRCStatus */
++#define CRC_STATUS_GOOD    (0)
++#define CRC_STATUS_DISCARD (1)
++#define CRC_STATUS_ERROR   (2)
++#define CRC_STATUS_BAD     (3)
++
++/* values for IPROC_EOPType */
++#define EOP_GOOD         (1)
++#define EOP_BADACK       (2)
++#define EOP_ERROR_RESET          (3)
++
++/*
++ * Interrupt register bits
++ *
++ * There are up to four sources of interrupt for the MSI port.
++ * The Elan will request 4 ports but may only get either 2 or 1 port. The Interrupts are assigned
++ * as shown below:
++ * No Of MSI ints     Low Prioity                                                     High Prioity
++ *    4               Event Ints      OtherInts               Inputer Ints            Hard Error ints.
++ *               i.e.                 Dproc, Tproc, Sten.     HighPri and LowPri      Link errs, ECC errs,
++ *
++ *    2               Event Ints      All other interrupts.
++ *    1               All together.
++ * 
++ * It is not safe to change the number of sources of interrupt while there may be outstanding,
++ * unserviced interrupts pending.
++ * There two forms of encoding. This has been provided in case an MSI implimentation assumes either
++ * a high value to have a high priority or a low value to have a high priority. This is controled
++ * by a bit in the Elan Pci Control register.
++ */
++#define INT_LinkPortKeyFail           (1<<18)
++#define INT_PciMemErr                 (1<<17)
++#define INT_SDRamInt                  (1<<16)
++#define INT_LinkError                 (1<<15)
++#define INT_IProcCh1HighPri           (1<<14)
++#define INT_IProcCh0HighPri           (1<<13)
++#define INT_IProcCh1LowPri            (1<<12)
++#define INT_IProcCh0LowPri            (1<<11)
++#define INT_DiscardingHighPri         (1<<10)
++#define INT_DiscardingLowPri          (1<<9)
++#define INT_CProcHalted                       (1<<8)
++#define INT_TProcHalted                       (1<<7)
++#define INT_DProcHalted                       (1<<6)
++#define INT_EProc                     (1<<5)
++#define INT_TProc                     (1<<4)
++#define INT_CProc                     (1<<3)
++#define INT_Dma1Proc                  (1<<2)
++#define INT_Dma0Proc                  (1<<1)
++#define INT_MainInterrupt             (1<<0)
++
++#define INT_Units             (INT_EProc | INT_TProc | INT_CProc | INT_Dma1Proc | INT_Dma0Proc)
++#define INT_Inputters         (INT_IProcCh1HighPri | INT_IProcCh0HighPri | INT_IProcCh1LowPri | INT_IProcCh0LowPri)
++#define INT_Discarding        (INT_DiscardingHighPri | INT_DiscardingLowPri)
++#define INT_Halted            (INT_CProcHalted | INT_TProcHalted | INT_DProcHalted)
++#define INT_ErrorInterrupts   (INT_LinkPortKeyFail | INT_PciMemErr | INT_SDRamInt | INT_LinkError)
++
++#define INT_MSI0              INT_MainInterrupt
++#define INT_MSI1              (INT_Units | INT_Discarding | INT_Halted)
++#define INT_MSI2              (INT_Inputters)
++#define INT_MSI3              (INT_ErrorInterrupts)
++
++#define E4_INTERRUPT_REG_SHIFT        32
++#define E4_INTERRUPT_MASK_MASK        (0xffffffffULL)
++
++/*
++ * Trap type values - see trapvalues.v
++ */
++
++#define CommandProcInserterError              0x1
++#define CommandProcPermissionTrap             0x2
++#define CommandProcSendTransInvalid           0x3
++#define CommandProcSendTransExpected          0x4
++#define CommandProcDmaQueueOverflow           0x5
++#define CommandProcInterruptQueueOverflow     0x6
++#define CommandProcMemoryFault                        0x7
++#define CommandProcRouteFetchFault            0x8
++#define CommandProcFailCountZero              0x9
++#define CommandProcAddressAlignment           0xa
++#define CommandProcWaitTrap                   0xb
++#define CommandProcMultipleGuards             0xc
++#define CommandProcOpenOnGuardedChan          0xd
++#define CommandProcThreadQueueOverflow                0xe
++#define CommandProcBadData                      0xf
++
++#define DmaProcNoFault                                0x0
++#define DmaProcRouteFetchFault                        0x1
++#define DmaProcFailCountError                 0x2
++#define DmaProcPacketAckError                 0x3
++#define DmaProcRunQueueReadFault              0x4
++#define DmaProcQueueOverflow                  0x5
++
++#define EventProcNoFault                      0x0
++#define EventProcAddressAlignment             0x1
++#define EventProcMemoryFault                  0x2
++#define EventProcCountWrapError                       0x3
++
++#define InputNoFault                          0x0
++#define InputAddressAlignment                 0x1
++#define InputMemoryFault                      0x2
++#define InputInvalidTransType                 0x3
++#define InputDmaQueueOverflow                 0x4
++#define InputEventEngineTrapped                       0x5
++#define InputCrcErrorAfterPAckOk              0x6
++#define InputEopErrorOnWaitForEop             0x7
++#define InputEopErrorTrap                     0x8
++#define InputDiscardAfterAckOk                        0x9
++ 
++typedef struct _E4_Sched_Status
++{
++    E4_uint32 Status;
++    E4_uint32 Restart;
++} E4_Sched_Status;
++ 
++typedef struct _E4_Input_Ptrs
++{
++    E4_uint32 ContextFilterTable;
++    E4_uint32 TrapBasePtr;
++} E4_Input_Ptrs;
++
++#define SCH_StopLowPriQueues          (1 << 0)
++#define SCH_DProcHalt                 (1 << 1)
++#define SCH_TProcHalt                 (1 << 2)
++#define SCH_CProcHalt                 (1 << 3)
++
++#define SCH_CProcTimeout600ns         (1 << 4)
++#define SCH_CProcTimeout1p4us         (2 << 4)
++#define SCH_CProcTimeout3p0us         (3 << 4)
++#define SCH_CProcTimeout6p2us         (4 << 4)
++#define SCH_CProcTimeout12p6us                (5 << 4)
++#define SCH_CProcTimeout25p4us                (6 << 4)
++#define SCH_CProcTimeout51p0us                (7 << 4)
++#define SCH_DiscardLowPriInput                (1 << 7)
++#define SCH_DiscardHighPriInput               (1 << 8)
++
++#define SCH_DProcTimeslice64us                (0 << 9)
++#define SCH_DProcTimeslice128us               (1 << 9)
++#define SCH_DProcTimeslice256us               (2 << 9)
++#define SCH_DProcTimeslice512us               (3 << 9)
++
++#define SCH_Halt                      (SCH_StopLowPriQueues | SCH_DProcHalt | SCH_TProcHalt | SCH_CProcHalt)
++#define SCH_Discard                   (SCH_DiscardLowPriInput | SCH_DiscardHighPriInput)
++
++#define SCH_RestartCProc              (1 << 0)
++#define SCH_RestartTProc              (1 << 1)
++#define SCH_RestartEProc              (1 << 2)
++#define SCH_RestartDma0Proc           (1 << 3)
++#define SCH_RestartDma1Proc           (1 << 4)
++#define SCH_RestartDmaPrefetchProc    (1 << 5)
++#define SCH_RestartCh0LowPriInput     (1 << 6)
++#define SCH_RestartCh1LowPriInput     (1 << 7)
++#define SCH_RestartCh0HighPriInput    (1 << 8)
++#define SCH_RestartCh1HighPriInput    (1 << 9)
++#define SCH_ClearLinkErrorInt         (1 << 10)
++#define SCH_ContextFilterFlush                (1 << 11)
++
++/*
++ * Link state bits.
++ */
++#define LS_LinkNotReady               (1 << 0) /* Link is in reset or recovering from an error */
++#define LS_Locked             (1 << 1) /* Linkinput PLL is locked */
++#define LS_LockError          (1 << 2) /* Linkinput PLL was unable to lock onto the input clock. */
++#define LS_DeskewError                (1 << 3) /* Linkinput was unable to Deskew all the inputs. (Broken wire?) */
++#define LS_PhaseError         (1 << 4) /* Linkinput Phase alignment error. */
++#define LS_DataError          (1 << 5) /* Received value was neither good data or a token. */
++#define LS_FifoOvFlow0                (1 << 6) /* Channel 0 input fifo overflowed. */
++#define LS_FifoOvFlow1                (1 << 7) /* Channel 1 input fifo overflowed. */
++#define LS_Mod45Changed               (1 << 8) /* Mod45 bit has changed. Error setr to force reset. */
++#define LS_PAckNotSeenError   (1 << 9) /* PAck value not returned for this packet. */
++
++/*
++ * Link State Constant defines, used for writing to LinkSetValue
++ */
++
++#define LRS_DataDel0          0x0
++#define LRS_DataDel1          0x1
++#define LRS_DataDel2          0x2
++#define LRS_DataDel3          0x3
++#define LRS_DataDel4          0x4
++#define LRS_DataDel5          0x5
++#define LRS_DataDel6          0x6
++#define LRS_DataDel7          0x7
++#define LRS_DataDel8          0x8
++#define LRS_LinkInValue               0x9
++#define LRS_PllDelValue               0xA
++#define LRS_ClockEven         0xB
++#define LRS_ErrorVal8to0      0xC
++#define LRS_ErrorVal17to9     0xD
++#define LRS_ErrorVal26to18    0xE
++#define LRS_ErrorVal35to27    0xF
++#define LRS_NumLinkDels         0x10
++
++#define LRS_Pllfast             0x40
++
++typedef struct _E4_CommandControl
++{
++    volatile E4_uint32 CommandQueueDescsBase;
++    volatile E4_uint32 CommandRequeuePtr;
++} E4_CommandControl;
++
++#define E4_CommandRequeueBusy         0x80000000      /* Test against read value of CommandRequeuePtr */
++#define E4_CommandRequeueHighPri      0x1             /* Will requeue onto the high pri queue */
++#define E4_QueueDescPtrMask           0x7fffffe0
++
++typedef struct _E4_CommandQueueDesc
++{
++    E4_uint64 CQ_QueuePtrs;
++    E4_uint64 CQ_HoldingValue;                /* 32 bit value for 32 bit accesses or OutOfOrderMask*/
++    E4_uint64 CQ_AckBuffers;          /* Space for 32 4 bit ack buffer values. */
++    E4_uint64 CQ_Control;
++} E4_CommandQueueDesc;
++
++/*
++ * Rev A - CQ_QueuePtrs
++ * [63]               Unused          Should be set to zero.
++ * [62:51]    Unused          (reads as top of InsertPtr)
++ * [50:35]    CompletedPtr    Completed pointer. This is alligned to a byte address.
++ * [34]               Trapped         Will be set if the command has trapped.
++ * [33:32]    Size            Size of queue.
++ * [31]               Used            Will be set if the descriptor has been changed and written back by the elan.
++ * [30:3]     InsertPtr       Insert pointer. This is alligned to a byte address.
++ * [2]                TimedOut        Will be set if the queue timedout executing a command.
++ * [1]                Priority        When set the queue runs at high priority.
++ * [0]                Error           If this becomes set all new data written to the queue is * discarded.
++ *
++ * Rev B - CQ_QueuePtrs
++ * [63]               TimedOut        Will be set if the queue timedout executing a command.
++ * [62]               Priority        When set the queue runs at high priority.
++ * [61]               QueueType       1=will accept unordered 64 bit PCI writes. 0=will accept ordered 32 or 64 bit PCI writes.
++ * [60:51]    Unused          (reads as top of InsertPtr)
++ * [50:35]    CompletedPtr    Completed pointer. This is alligned to a byte address.
++ * [34]               Trapped         Will be set if the command has trapped.
++ * [33:32]    Size            Size of queue.
++ * [31]               Used            Will be set if the descriptor has been changed and written back by the elan.
++ * [30:3]     InsertPtr       Insert pointer. This is alligned to a byte address.
++ * [2]                OrderControl    Holds bit 8 of last PCI accesses. Used by a reordering queue.
++ * [1:0]      ErrorType       This field has the current error status of the queue.
++ */
++
++/* Common between revA and RevB */
++#define CQ_PtrMask            (0x7ffffff8)                    /* 31 bit sdram address */
++#define CQ_PtrOffsetMask      (0x7fff8)
++#define CQ_PtrBaseMask                (0x7ff80000)
++
++#define CQ_InsertPtrShift     (3 - 3)                         /* InsertPtr is 64 bit aligned */
++#define CQ_SizeShift          (32)
++#  define CQ_Size1K           0
++#  define CQ_Size8K           1
++#  define CQ_Size64K          2
++#  define CQ_Size512K         3
++#  define CQ_SizeMask         3
++
++#define CQ_CompletedPtrShift  (35 - 3)                        /* CompletedPtr is 64 but aligned */
++
++#define CQ_Used                       (1ull << 31)
++#define CQ_Trapped            (1ull << 34)
++
++#define CQ_QueuePtrsValue(Size,Inserter,Completer) \
++      (((E4_uint64) (Size) << CQ_SizeShift) | \
++       ((E4_uint64) (Inserter) << CQ_InsertPtrShift) | \
++       ((E4_uint64) (Completer) << CQ_CompletedPtrShift))
++
++#define CQ_InsertPtr(QueuePtrs) \
++      (((E4_uint64) QueuePtrs) & CQ_PtrMask)
++
++#define CQ_CompletedPtr(QueuePtrs) \
++      (((E4_uint32)((QueuePtrs) >> CQ_CompletedPtrShift) & CQ_PtrOffsetMask) | \
++       (CQ_InsertPtr(QueuePtrs) & CQ_PtrBaseMask))
++
++#define CQ_Size(SizeVal)              (1024 * (1 << ((SizeVal)*3)))
++
++/* Rev A specific */
++#define CQ_RevA_Error                 (1 << 0)
++#define CQ_RevA_Priority              (1 << 1)
++#define CQ_RevA_TimedOut              (1 << 2)
++
++/* Rev B specific */
++#define CQ_RevB_ErrorType(QueuePtr)   ((QueuePtr) & (3 << 0))
++#  define CQ_RevB_NoError             (0ull << 0)
++#  define CQ_RevB_Overflowed          (1ull << 0)
++#  define CQ_RevB_InvalidWriteSize    (2ull << 0)
++#  define CQ_RevB_InvalidWriteOrder   (3ull << 0)
++#define CQ_RevB_OrderControl          (1ull << 2)
++
++#define CQ_RevB_QueueType(QueuePtr)   ((QueuePtr) & (1ull << 61))
++#  define CQ_RevB_ReorderingQueue     (1ull << 61)
++#  define CQ_RevB_32bitWriteQueue     (0ull << 61)
++
++#define CQ_RevB_Priority              (1ull << 62)
++#define CQ_RevB_TimedOut              (1ull << 62)
++
++/* 
++ * CQ_AckBuffers - Packet Ack Values
++ */
++#define PackOk                        (0x0)
++#define PackTestFail          (0x1)
++#define PackDiscard           (0x2)
++#define PackError             (0x7)
++#define PackTimeout           (0x8)
++#define PackWaiting           (0xF)
++#define PackValue(val,chan)   (((val) >> ((chan) * 4)) & 0xf)
++
++/*
++ * CQ_Control
++ * [63:35]    ExtractPtr
++ * [34]               Unused
++ * [33:32]    ChannelNotCompleted
++ * [31:24]    Permissions
++ * [23:16]    RestartCount            Decremented after each restart. Will trap when zero
++ * [15:14]    Unused                  Should be set to zero
++ * [13:0]     Context
++ */
++#define CQ_Context(Control)           ((E4_uint32) ((Control) >>  0) & 0x3fff)
++#define CQ_RestartCount(Control)      ((E4_uint32) ((Control) >> 16) & 0x7f)
++#define CQ_ChannelNotCompleted(Control)       ((E4_uint32) ((Control) >> 32) & 3)
++#define CQ_ExtractPtr(Control)                ((E4_uint32) ((Control) >> 32) & 0xFFFFFFF8)
++
++#define CQ_RestartCountShift          16
++
++#define CQ_SetEventEnableBit  (1 << 24)
++#define CQ_WaitEventEnableBit (1 << 25)
++#define CQ_ModifyEnableBit    (1 << 26)
++#define CQ_WriteEnableBit     (1 << 27)
++#define CQ_ThreadStartEnableBit       (1 << 28)
++#define CQ_DmaStartEnableBit  (1 << 29)
++#define CQ_STENEnableBit      (1 << 30)
++#define CQ_InterruptEnableBit (1 << 31)
++#define CQ_EnableAllBits        (0xFF000000)
++#define CQ_PermissionMask     (0xFF000000)
++
++#define CQ_ControlValue(Cntx, RestartCount, Permissions) \
++      (((Cntx) & 0x3fff) | (((RestartCount) & 0xff) << 16) | ((Permissions) & CQ_PermissionMask))
++
++/*
++ * This file describes the slave address map of Elan4.
++ *
++ * Elan4 has two PCI 64 bit base address registers. One is setup for elan
++ * local memory and the other is for the command port, elan registers and ebus.
++ *
++ * This file describes the command port, elan registers and ebus BAR. This is a
++ * 26 bit base address register and is split up as follows:
++ * 1 The ebus requires 21 bits of address. 26'h3e00000 to 26'h3ffffff
++ * 2 The control regsiters requires 16 bits of address. 26'h3df0000 to 26'h3dfffff
++ * 3 The command port has the rest. This give just under 8k command ports or about 123 per
++ *   processor of a 64 node SMP.
++ */
++
++/* BAR1 contains the command queues followed by the registers and the Ebus - and is 26 bits */
++/* each command queue has an 8K page associated with it */
++#define CQ_CommandMappingSize         (1 << 13)
++#define CQ_NumCommandDescs            ((1 << (26 - 13)))
++#define CQ_CommandDescsAlignment      ((1 << (26 - 13)) * sizeof (E4_CommandQueueDesc))
++
++/* control reg bits i.e. E4_DataBusMap.SysControlReg */
++#define CONT_EN_ALL_SETS              (1ULL << 0) /* enable cache */
++#define CONT_MMU_ENABLE                       (1ULL << 1) /* bit 0 enables mmu */
++#define CONT_CACHE_HASH_TABLE         (1ULL << 2) /* cache up hash table entries */
++#define CONT_CACHE_CHAINS             (1ULL << 3) /* cache up chain entries */
++#define CONT_CACHE_ROOT_CNTX          (1ULL << 4) /* cache root context table for routes and filters. */
++#define CONT_CACHE_STEN_ROUTES                (1ULL << 5) /* cache up sten packet routes */
++#define CONT_CACHE_DMA_ROUTES         (1ULL << 6) /* cache up dma packet routes */
++
++#define CONT_CACHE_NONE               0ULL
++#define CONT_CACHE_ALL                (CONT_CACHE_HASH_TABLE | CONT_CACHE_CHAINS | CONT_CACHE_ROOT_CNTX | \
++                               CONT_CACHE_STEN_ROUTES | CONT_CACHE_DMA_ROUTES)
++
++/* This controls the format size and position of the MMU hash tables. */
++#define CONT_INHIBIT_MAX_CHAIN_ITEMS  (1ULL << 7)     /* Prevents the MaxChainItems value of 1024 from forcing a translation miss */
++#define CONT_TABLE0_MASK_SIZE_SHIFT   8               /* Defines the size of hash table 0 */
++#define CONT_TABLE0_PAGE_SIZE_SHIFT   13              /* Set the page size for hash table 0 */
++#define CONT_TABLE1_MASK_SIZE_SHIFT   16              /* Defines the size of hash table 1 */
++#define CONT_TABLE1_PAGE_SIZE_SHIFT   21              /* Set the page size for hash table 1 */
++#define CONT_TWO_HASH_TABLES          (1ULL << 24)    /* Sets the MMU to use two hash tables. If not set only 0 used. */
++#define CONT_2K_NOT_1K_DMA_PACKETS    (1ULL << 25)    /* Used to select the default DMA packet size. */
++#define CONT_ALIGN_ALL_DMA_PACKETS    (1ULL << 26)    /* Will force all dma packets to be aligned to a page.*/
++#define CONT_DIRECT_MAP_PCI_WRITES    (1ULL << 27)    /* Will force pci writes to write and flush the dcache.*/
++#define CONT_TLB_FLUSH                        (1ULL << 28)    /* Invalidates the TLB and indicates when flushed */
++#define CONT_CLEAR_WALK_WROTE_TABLES  (1ULL << 29)    /* Used to guarantee that the elan is using new PTE values. */
++#define CONT_ROUTE_FLUSH              (1ULL << 30)    /* Invalidates all route cache entries. */
++#define CONT_CLEAR_LINKPORT_INT               (1ULL << 31)    /* Clears the Linkport key fail interrupt. Reads as 0. */
++#define CONT_CLEAR_SDRAM_ERROR                (1ULL << 32)    /* Clears an EEC error interrupt. Reads as 0. */
++
++/*
++ * These are extra control bits used for testing the DLLs of the SDRAM interface. Most of the Sdram
++ * control bits are defined in xsdram.h
++ */
++#define SDRAM_FIXED_DLL_DELAY_SHIFT   47
++#define SDRAM_FIXED_DLL_DELAY_BITS    5
++#define SDRAM_FIXED_DLL_DELAY_MASK    ((1ULL << SDRAM_FIXED_DLL_DELAY_BITS) - 1ULL)
++#define SDRAM_FIXED_DLL_DELAY(Value)  ((SDRAM_FIXED_DLL_DELAY_MASK & (Value)) << SDRAM_FIXED_DLL_DELAY_SHIFT)
++#define SDRAM_FIXED_DELAY_ENABLE      (1ULL << 52)
++#define SDRAM_GET_DLL_DELAY(Value)    (((Value) >> SDRAM_FIXED_DLL_DELAY_SHIFT) & SDRAM_FIXED_DLL_DELAY_MASK)
++
++#define SDRAM_DLL_CORRECTION_FACTOR   3       /* This is to allow for SSO and ringing on the DQ lines */
++
++#define PAGE_SIZE_4K  0x0
++#define PAGE_SIZE_8K  0x1
++#define PAGE_SIZE_64K 0x2
++#define PAGE_SIZE_512K        0x3
++#define PAGE_SIZE_2M  0x4
++#define PAGE_SIZE_4M  0x5
++#define PAGE_SIZE_64M 0x6
++#define PAGE_SIZE_512M        0x7
++
++#define PAGE_SIZE_MASK        0x7
++#define PAGE_MASK_MASK        0x1f
++
++/* control reg bits i.e. E4_DataBusMap.LinkControlReg */
++#define LCONT_REVA_GREEN_LED          (1 << 0)
++#define LCONT_REVA_YELLOW_LED         (1 << 1)
++#define LCONT_REVA_RED_LED            (1 << 2)
++#define LCONT_REVA_ENABLE_LED_DRIVE   (1 << 3) /* Enable manual setting of the Leds to the bits set above. */
++
++#define LCONT_REVB_DISABLE_TLB_PREFETCH       (1 << 0)
++#define LCONT_REVB_DISABLE_CRC_ERROR_CHECKING (1 << 1)
++
++
++#define LCONT_EN_SYS_WRITES           (1 << 4) /* Enable linkport writes to sys registers. i.e. all of E4_DataBusMap. */
++#define LCONT_EN_SYS_READS            (1 << 5) /* Enable linkport reads from sys registers. i.e. all of E4_DataBusMap. */
++#define LCONT_EN_USER_WRITES          (1 << 6) /* Enable linkport writes to user registers. i.e. all of E4_User_Regs. */
++#define LCONT_EN_USER_READS           (1 << 7) /* Enable linkport reads from user registers. i.e. all of E4_User_Regs. */
++
++#define LCONT_TEST_VALUE_MASK         0x3ff    /* Value used for test writes and link boundary scan. */
++#define LCONT_TEST_VALUE_SHIFT                8
++#define LCONT_TEST_VALUE(Value)               ((LCONT_LINK_STATE_MASK & (Value)) << LCONT_TEST_VALUE_SHIFT)
++
++/*
++ * State read from LINK_STATE when TEST_VALUE is set to the following values.
++ * TEST_VALUE   LINK_STATE read       TEST_VALUE        LINK_STATE read
++ *    000     -   Data delay count 0     008       -  Data delay count 8
++ *    001     -   Data delay count 1     009       -  Link in value
++ *    002     -   Data delay count 2     00a       -  PLL delay
++ *    003     -   Data delay count 3     00b       -  Clock Delay
++ *    004     -   Data delay count 4     00c       ?  ErrorVal8to0
++ *    005     -   Data delay count 5     00d       ?  ErrorVal17to9
++ *    006     -   Data delay count 6     00e       ?  ErrorVal26to18
++ *    007     -   Data delay count 7     00f       ?  ErrorVal35to27
++ */
++
++#define LCONT_TEST_CONTROL_MASK               0x3     /* Selects and controls the action of the LINK_STATE value. */
++#define LCONT_TEST_CONTROL_SHIFT      18
++
++#define LCONT_READ_ERRORS             0       /* {Mod45RequestChanged, FifoOverflowError, DataError, PhaseError,
++                                               *      DeskewError, LockError, Locked, LinkNotReady} */
++#define LCONT_READ_STATE              1       /* Read valus addressed by TEST_CONTROL value */
++#define LCONT_FIX_LINK_DELAYS         2       /* Sets delays to TEST_CONTROL value */
++#define LCONT_BOUNDARY_SCAN           3       /* Puts link into boundary scan. Outputs TEST_CONTROL value to link,
++                                               * reads LINK_STATE from link. */ 
++
++#define LCONT_LINK_STATE_MASK         0x3ff   /* Read only */
++#define LCONT_LINK_STATE_SHIFT                20      /* Read only */
++#define LCONT_LINK_STATE(ControlRegValue)     (LCONT_LINK_STATE_MASK & ((ControlRegValue) >> LCONT_LINK_STATE_SHIFT))
++
++/* control reg bits i.e. E4_DataBusMap.LinkContSettings */
++#define LCONT_MOD45_DISABLE           (1 << 0) /* is set the link will try to run in TNB mode. */
++#define LCONT_CONFIG_PHASE_MASK               0x7     /* This set the delay through the phase alignment buffer. */
++#define LCONT_CONFIG_PHASE_SHIFT      1
++
++#define LCONT_PLL_REF_VAL_BITS_MASK   0x7f    /* This is the divide value on the LinkIn clock to form the comms PLL */
++#define LCONT_PLL_REF_VAL_BITS_SHIFT  4       /* reference clock. Div value is (n - 2). e.g. to Divide by 7 set to 5. */
++
++#define LCONT_FORCE_COMMSCLK_LOCAL    (1 << 11) /* This must be set at one end of a back to back Elan configuration. */
++#define LCONT_LVDS_VOLTAGE_BITS_MASK  0x3     /* This is used to set the voltage swing on the LVDS link output pads. */
++#define LCONT_LVDS_VOLTAGE_BITS_SHIFT 12      /* reference clock. Div value is (n - 2). e.g. to Divide by 7 set to 5. */
++
++#define LCONT_VOD_170                 0       /* Approximate differential voltage swing in mV of link outputs into */
++#define LCONT_VOD_360                 1       /* a 100 ohm diferential load. */
++#define LCONT_VOD_460                 2
++#define LCONT_VOD_550                 3
++
++#define LCONT_LVDS_TERMINATION_MASK   0x3     /* This set the resistor values of the internal single ended termation */
++#define LCONT_LVDS_TERMINATION_SHIFT  14      /* resistors of the link input and comms input clcok. */
++
++#define LCONT_TERM_55_OHM             0       /* Resistor values for internal termination of LVDS pads. */
++#define LCONT_TERM_50_OHM             1
++#define LCONT_TERM_AUTO_OHM           2       /* Should normally be set to auto. */
++#define LCONT_TERM_45_OHM             3
++
++#define LCONT_LVDS_EN_TERM_UPDATE     (1 << 47) /* This should be asserted and deasserted if LCONT_LVDS_TERMINATION is changed. */
++
++/* Macros used to access and construct MMU hash table and chain entries. */
++/*
++ * Each hash entry is made up of a 64 byte block. Each entry hash two tags where each
++ * tag has 4 PTE's. PTE's 0 to 2 use the bottom 48 bits of a 64 bit word and PTE 3
++ * uses the top 16 bits of 3 64 bit words.
++ *
++ * These macros can be used to build a single PTE. PTE3 needs to be built into a 48 bit
++ * object before they can be used.
++ */
++#define PTE_ENTRY_MASK                0x0000ffffffffffffULL
++#define PTE_TYPE_MASK         0x000000000000000fULL   
++#define PTE_PERM_MASK         0x00000000000000f0ULL
++#define PTE_PERM_TYPE_MASK    0x00000000000000ffULL
++#define PTE_REF_MASK          0x0000000000000100ULL
++#define PTE_PPN_MASK          0x00007ffffffffe00ULL
++#define PTE_MOD_MASK          0x0000800000000000ULL
++#define PTE_TOPADDR_MASK      0x0000600000000000ULL
++
++#define PTE_MOD_SHIFT         47
++#define PTE_PPN_SHIFT         9
++#define PTE_REF_SHIFT         8
++#define PTE_PERM_SHIFT                4
++#define PTE_TYPE_SHIFT                0
++
++#define PTE_PADDR_SHIFT               (12 - 9)                /* Physical addresses are shifted down 3 this to go into the PTE */
++
++
++/* Values required for tag 3 */
++#define PTE_REF_3                     0x0100000000000000ULL
++#define PTE_MOD_3                     0x8000000000000000ULL
++#define PTE_ENTRY_MASK_3              0xffff000000000000ULL
++#define PTE_PERM_TYPE_MASK_3          0x00ff000000000000ULL
++#define PTE_ENTRY_3_FOR_0(NewPte)     ((NewPte << (48)) & PTE_ENTRY_MASK_3)
++#define PTE_ENTRY_3_FOR_1(NewPte)     ((NewPte << (32)) & PTE_ENTRY_MASK_3)
++#define PTE_ENTRY_3_FOR_2(NewPte)     ((NewPte << (16)) & PTE_ENTRY_MASK_3)
++
++/* Values required for the tags */
++#define TAG_CONTEXT_MASK              0x0000000000003fffULL
++#define TAG_ADDRESS_MASK              0xfffffffff8000000ULL
++#define TAG_CHAINPTR_18TO6_MASK               0x0000000007ffc000ULL
++#define TAG_CHAINPTR_LOW_SHIFT                (14 - 6)
++#define TAG_CHAINPTR_30TO19_MASK      0x0000000003ffc000ULL
++#define TAG_CHAINPTR_HIGH_SHIFT               (19 - 14)
++#define TAG_COPY_BIT                  0x0000000004000000ULL
++
++/*
++ * This takes number loaded into the control register and returns the page size as a power of two.
++ */
++
++#define E4_PAGE_SIZE_TABLE            E4_uint32 const PageSizeTable[] = {12, 13, 16, 19, 21, 22, 26, 29}
++#define E4_PAGE_SIZE_TABLE_SIZE               (sizeof(PageSizeTable)/sizeof(PageSizeTable[0]))
++
++/*
++ * This macro generates a hash block index.
++ *
++ * Cntx                This is the 14 bit context. It should not be larger than 14 bits.
++ * VAddr       This is the 64 bit virtual address. It does not require any masking and can be a byte address.
++ * PageSize    This is the value loaded into the control register for this hash table.
++ * HashTableMask This should be set mask out upper bits past the end of the hash table.
++ */
++#define E4MMU_SHIFT_ADDR(VAddr, Shift) \
++    ((((E4_uint32)(VAddr)) >> (Shift)) | (((E4_uint32)((VAddr) >> 32)) << (32 - (Shift))))
++
++#define E4MMU_CONTEXT_SCRAMBLE(Cntx) \
++            ((((Cntx) << 8) | ((Cntx) >> 6)) ^ (((Cntx) << 15) | ((Cntx) << 1)))
++
++#define E4MMU_HASH_INDEX(Cntx, VAddr, PageShift, HashTableMask)               \
++          ((E4MMU_SHIFT_ADDR(VAddr, (PageShift) + 2) ^ E4MMU_CONTEXT_SCRAMBLE(Cntx)) & (HashTableMask))
++
++#define E4MMU_TAG(vaddr,ctx)  (((vaddr) & TAG_ADDRESS_MASK) | ((ctx) & TAG_CONTEXT_MASK))
++
++#define E4MMU_TAG2VADDR(tag,hashidx,PageShift,HashTableMask)  \
++              (((tag) & TAG_ADDRESS_MASK) | ((((hashidx) ^ E4MMU_CONTEXT_SCRAMBLE((tag) & TAG_CONTEXT_MASK)) & (HashTableMask)) << ((PageShift + 2))))
++
++/*
++ * Detailed bit descriptions for the tags and PTE's are better done with the macros
++ * defined above.
++ */
++typedef struct _E4_HashTableEntry
++{
++   E4_uint64  Tag[2];
++   E4_uint64  TagPTE[2][3];
++} E4_HashTableEntry;
++
++#define E4MMU_TAG_OFFSET(tag)         ((tag) << 3)
++#define E4MMU_PTE_LOW_OFFSET(tag,pte) ((((tag)*3 + (pte) + 2) << 3))
++#define E4MMU_PTE_HIGH_OFFSET(tag,pte)        ((((tag)*3 + (pte) + 2) << 3) + 4)
++#define E4MMU_PTE3_WORD0_OFFSET(tag)  ((((tag)*3 + 2) << 3) + 6)
++#define E4MMU_PTE3_WORD1_OFFSET(tag)  ((((tag)*3 + 3) << 3) + 6)
++#define E4MMU_PTE3_WORD2_OFFSET(tag)  ((((tag)*3 + 4) << 3) + 6)
++
++
++/*
++ * Hash0AddrBits is the size of the hash table in bytes as a power of 2.
++ * e.g. 11 would give 32 hash entries where each entry is 64 bytes.
++ */
++#define SETUP_HASH_TABLES(Hash0PageSize, Hash0AddrBits, Hash1PageSize, Hash1AddrBits) \
++                        (((Hash0PageSize) << CONT_TABLE0_PAGE_SIZE_SHIFT) |   \
++                         ((Hash0AddrBits) << CONT_TABLE0_MASK_SIZE_SHIFT) |   \
++                         ((Hash1PageSize) << CONT_TABLE1_PAGE_SIZE_SHIFT) |   \
++                         ((Hash1AddrBits) << CONT_TABLE1_MASK_SIZE_SHIFT))
++
++/* ECC status register */
++#define ECC_Addr(s)                   ((s) & 0x7ffffff8ULL)
++#define ECC_Syndrome(s)                       (((s) >> 32) & 0xffffULL)
++#define ECC_RisingDQSSyndrome(s)      (((s) >> 32) & 0xffULL)
++#define ECC_FallingDQSSyndrome(s)     (((s) >> 40) & 0xffULL)
++#define ECC_UncorrectableErr(s)       (((s) >> 48) & 1ULL)
++#define ECC_MultUncorrectErrs(s)      (((s) >> 49) & 1ULL)
++#define ECC_CorrectableErr(s)         (((s) >> 50) & 1ULL)
++#define ECC_MultCorrectErrs(s)                (((s) >> 51) & 1ULL)
++
++/* Permission type saved in a PTE. This is a four bit field */
++#define PERM_Disabled         0x0
++#define PERM_Unused           0x1
++#define PERM_LocDataRead      0x2
++#define PERM_LocDataWrite     0x3
++#define PERM_LocRead          0x4
++#define PERM_LocExecute               0x5
++#define PERM_ReadOnly         0x6
++#define PERM_LocWrite         0x7
++#define PERM_LocEventOnly     0x8
++#define PERM_LocEventWrite    0x9
++#define PERM_RemoteEvent      0xa
++#define PERM_RemoteAll                0xb
++#define PERM_RemoteReadOnly   0xc
++#define PERM_RemoteWriteLocRead       0xd
++#define PERM_DataReadWrite    0xe
++#define PERM_NoFault          0xf
++
++#define PERM_Mask             0xf
++
++/* Permission type hints to device driver */
++#define PERM_Preload          0x10
++
++#define PTE_SetPerm(Perm)     (((Perm) & PERM_Mask) << 4)
++
++/* Control info saved in the lookup field of the TLB */
++#define PTE_PciNotLocal               (1ULL << 0)             /* Directs the access to the PCI interface */
++#define PTE_BigEndian         (1ULL << 1)             /* Valid for PCI entries only */
++#define PTE_RelaxedOrder      (1ULL << 2)             /* Valid for PCI entries only */
++#define PTE_DontSnoop         (1ULL << 3)             /* Valid for PCI entries only */
++
++#define PTE_UseFixedSet               (1ULL << 1)             /* Value for non PCI entries only */
++#define PTE_CommandQueue      (1ULL << 2)             /* Value for non PCI entries only */
++#define PTE_SetFixedSetNo(Set)        ((((Set) & 3) << 2) | PTE_UseFixedSet)
++
++#define PTE_TypeBitsMask      (0xfULL)
++#define PTE_PermissionTypeMask        (0xfULL << 4)
++#define PTE_Referenced                (1ULL << 8)
++#define PTE_PhysicalPageNoMask        (0x7ffffffffe00ULL)
++#define PTE_Modified          (1ULL << 47)
++
++#define PTE_PhysicalAddrShiftIntoPTE  (12 - 9)
++
++/* define page table entry bit fields */
++#define TLB_PageSizeBits      (3 << 0)
++#define TLB_ACCBits           (7 << 2)
++#define TLB_LocalBit          (1 << 5)
++#define TLB_PCI64BitTargetBit (1 << 6)
++#define TLB_PCIBigEndianBit   (1 << 7)
++
++#define TLB_ModifiedBit               (1 << 55)
++#define TLB_ReferencedBit     (1 << 63)
++
++/* Used to read values from the tlb. */
++#define TLB_TlbReadCntBitsSh  56
++#define TLB_UseSelAddrSh      (1ULL << 60)
++#define TLB_WriteTlbLine      (1ULL << 61)
++
++#define TLB_SEL_LINE(LineNo) (TLB_UseSelAddrSh | \
++                            ((E4_uint64)((LineNo) & 0xf) << TLB_TlbReadCntBitsSh))
++
++#define TLB_NUM_ENTRIES               16
++/*
++ * The following macros are used with the test access port (TlbLineValue) for the TLBs.
++ */
++#define TLV_DoPciAccess                       (1ULL << 0)
++#define TLV_CommandAccess             (1ULL << 1)
++#define TLV_DoCacheAccess             (1ULL << 2)
++#define TLV_notStartTLBWalk           (1ULL << 3)
++#define TLV_UseFixedSet                       (1ULL << 4)
++#define TLV_BigEndian                 (1ULL << 4)
++#define TLV_RelaxedOrder              (1ULL << 5)
++#define TLV_DontSnoop                 (1ULL << 6)
++#define TLV_FixedSetNo_MASK           (3ULL << 5)
++#define TLV_PciTypeBits_MASK          (7ULL << 4)
++#define TLV_LookupBits_MASK           (0x7fULL)
++#define TLV_MissErr                   (1ULL << 7)
++#define TLV_TypeBits                  (0xffULL)
++
++#define TLV_PhysicalAddr_MASK         (0x3fffffffff000ULL)
++
++#define TLV_TlbTesting                        (1ULL << 51)
++#define TLV_SelectUnitsTlbRead                (1ULL << 52)
++#define TLV_SelectTProcTlbRead                (1ULL << 53)
++
++#define TLV_TlbLineSelect_MASK                (0xf)
++#define TLV_UnitsTlbLineSelect_SHIFT  (54)
++#define TLV_TProcTlbLineSelect_SHIFT  (59)
++#define TLV_EnableUnitsTlbRead                (1ULL << 58)
++#define TLV_EnableTProcTlbRead                (1ULL << 63)
++
++/*
++ * Use this macro to enable direct testing of the Units TLB.
++ * When Line is in the range 0 to 15 a TLB line is selected for reading or writing.
++ * When Line is set to -1 the tlb will be activated to perform a match.
++ */
++#define TLV_UnitsTlbLineSel(Line) (((Line) == -1) ? 0ULL : \
++    (TLV_EnableUnitsTlbRead | ((E4_uint64)((Line) & TLV_TlbLineSelect_MASK) << TLV_UnitsTlbLineSelect_SHIFT)))
++#define TLV_TProcTlbLineSel(Line) (((Line) == -1) ? 0ULL : \
++    (TLV_EnableTProcTlbRead | ((E4_uint64)((Line) & TLV_TlbLineSelect_MASK) << TLV_TProcTlbLineSelect_SHIFT)))
++ 
++/* 
++ * Thread_Trap_State
++ *  see f_RegFileControl.v TProcStatus
++ */
++#define TS_HaltThread               (1 << 0)
++#define TS_TrapForTooManyInstructions (1 << 1)
++#define TS_InstAccessException              (1 << 2)
++#define TS_Unimplemented            (1 << 3)
++#define TS_DataAccessException              (1 << 4)
++#define TS_DataAlignmentError       (1 << 5)
++#define TS_TrapForUsingBadData              (1 << 6)
++#define TS_TrapTypeMask                     (0x7f)
++#define TS_DataPortNo(ts)           (((ts) >> 7) & 7)
++#define TS_TrappedFlag                      (1 << 10)
++#define TS_MemLock                  (1 << 11)
++#define TS_XCCshift                 12
++#define TS_XCCmask                  0xff
++#define TS_ICC(ts)                  (((ts) >> 12) & 15)
++#define TS_XCC(ts)                  (((ts) >> 16) & 15)
++#define TS_InstValid_F                      (1 << 20)
++#define TS_InstValid_R                      (1 << 21)
++#define TS_InstValid_E                      (1 << 22)
++#define TS_InstValid_W                      (1 << 23)
++#define TS_HighPriority                     (1 << 24)
++#define TS_RemoteThread                     (1 << 25)
++#define TS_TProcTranslationInProgress (1 << 26)
++#define TS_MemLock_E                (1 << 27)
++
++/* Thread run queue entries */
++typedef struct E4_ThreadRegs
++{
++    E4_uint64 Registers[7];
++} E4_ThreadRegs;
++
++typedef struct E4_TProcQueueEntry
++{
++    E4_ThreadRegs     Regs;                   /* XXXX: jon check this */
++    E4_uint64         Context;                /* XXXX: jon check this */
++} E4_TProcQueueEntry;
++
++typedef struct E4_DProcQueueEntry
++{
++    E4_DMA            Desc;
++    E4_uint64         Pad;
++} E4_DProcQueueEntry;
++
++/*
++ * Packet acknowledge values.
++ */
++#define E4_PAckOk     0
++#define E4_PAckTestFail       1
++#define E4_PAckDiscard        2
++#define E4_PAckError  3
++
++/*
++ * return values from breaktest instruction.
++ */
++#define ICC_CARRY_BIT           (0x1ULL << 0)  /* Breaktest: Load pending         */
++#define ICC_ZERO_BIT            (0x1ULL << 1)  /* Breaktest: Time to break        */
++#define ICC_SIGNED_BIT          (0x1ULL << 2)  /* Breaktest: Another thread ready */
++#define ICC_TPROC_RDY_LOW_PRI   (0x1ULL << 3)
++#define ICC_TPROC_RDY_HIGH_PRI  (0x1ULL << 4)
++#define ICC_RUNNING_HIGH_PRI    (0x1ULL << 5)
++#define ICC_RUNNING_AS_REMOTE   (0x1ULL << 6)
++#define ICC_TIME_TO_BREAK       (0x1ULL << 7)
++#define ICC_RS1LOAD_PENDING     (0x1ULL << 8)
++#define ICC_TPROC_HALT          (0x1ULL << 9)
++
++/*
++ * Main Interrupt cookies
++ * [63:14]    user cookie
++ * [13:0]     context
++ */
++#define E4_MAIN_INT_SHIFT             14
++#define E4_MAIN_INT_COOKIE(cookie)    ((cookie) >> E4_MAIN_INT_SHIFT)
++#define E4_MAIN_INT_CTX(cookie)               ((cookie) & 0x3FFF)
++
++typedef E4_uint64 E4_MainIntEntry;
++
++#define E4_MainIntEntrySize   sizeof (E4_MainIntEntry)
++
++/*
++ * The internal databus is 64 bits wide.
++ * All writes to the internal registers MUST be made with 64 bit write operations.
++ * These can be made up of pairs 32 bit writes on the PCI bus. The writes will be
++ * treated as nops if they are performed with two separate 32 bit writes.
++ */
++typedef volatile struct _E4_DataBusMap
++{
++   E4_uint64          InputTrans[4][16];                                                                      /* 0x000 */
++
++   E4_uint64          Dma0TransAddr;                                                                          /* 0x200 */
++   E4_DMA             Dma0Desc;       /* Current Dma0 registers */                                            /* 0x208 */
++
++   E4_uint64          Dma1TransAddr;                                                                          /* 0x240 */
++   E4_DMA             Dma1Desc;       /* Current Dma1 registers */                                            /* 0x248 */
++  
++   E4_uint64          Dma0LastPacketSize;                                                                     /* 0x280 */
++   E4_uint64          Dma0ThisPacketSize;                                                                     /* 0x288 */
++   E4_uint64          Dma0DescSizeInProg;                                                                     /* 0x290 */
++   E4_uint64          Dma0BytesToPrefetch;                                                                    /* 0x298 */
++   E4_uint64          Dma0PrefetchAddr;                                                                       /* 0x2a0 */
++   E4_uint64          EventCountAndType;                                                                      /* 0x2a8 */
++   E4_uint64          EventParameters[2];                                                                     /* 0x2b0 */
++  
++   E4_uint64          Dma1LastPacketSize;                                                                     /* 0x2c0 */
++   E4_uint64          Dma1ThisPacketSize;                                                                     /* 0x2c8 */
++   E4_uint64          Dma1DescSizeInProg;                                                                     /* 0x2d0 */
++   E4_uint64          Dma1BytesToPrefetch;                                                                    /* 0x2d8 */
++   E4_uint64          Dma1PrefetchAddr;                                                                       /* 0x2e0 */
++   E4_Input_Ptrs      InputTrapAndFilter;                                                                     /* 0x2e8 */
++   E4_uint64          EventAddress;                                                                           /* 0x2f0 */
++   E4_QueuePtr                MainIntQueuePtrs;                                                                       /* 0x2f8 */
++   
++   E4_uint64          Event_Copy[16];                                                                         /* 0x300 */
++
++   E4_uint64          CommandCopy[7];                                                                         /* 0x380 */
++   E4_uint64          CommandHold;                                                                            /* 0x3b8 */
++
++   E4_uint64          InputQueueDesc[4];                                                                      /* 0x3c0 */
++
++   /* Run queue Pointers */
++   E4_uint64          DProcLowPriPtrs;                                                                        /* 0x3e0 */
++   E4_uint64          DProcHighPriPtrs;                                                                       /* 0x3e8 */
++   E4_uint64          TProcLowPriPtrs;                                                                        /* 0x3f0 */
++   E4_uint64          TProcHighPriPtrs;                                                                       /* 0x3f8 */
++
++   E4_uint64          CProcStatus;                                                                            /* 0x400 */
++   E4_uint64          TProcStatus;                                                                            /* 0x408 */
++   E4_uint64          IProcStatus;                                                                            /* 0x410 */
++   E4_uint64          EProcStatus;                                                                            /* 0x418 */
++   E4_uint64          DProc0Status;                                                                           /* 0x420 */
++   E4_uint64          DProc1Status;                                                                           /* 0x428 */
++   E4_Sched_Status    SchedStatus;                                                                            /* 0x430 */
++
++   E4_uint64          LoadIProcCntxFilter;    /* Will load one of 4 cntx filter regs. Write only */           /* 0x438 */
++
++   E4_CommandControl  CommandControl;                                                                         /* 0x440 */
++   E4_uint64          CommandCacheTestPort;                                                                   /* 0x448 */
++   E4_uint64          CommandLowPriRunPtrs;                                                                   /* 0x450 */
++   E4_uint64          CommandHighPriRunPtrs;                                                                  /* 0x458 */
++   E4_uint64          CommandSchedDataPort[4];                                                                /* 0x460 */
++
++   E4_uint64          DmaRouteBuffer[2][2];   /* Write only. Should not be written to. */                     /* 0x480 */
++   E4_uint64          StenRouteBuffer[2];     /* Write only. Should not be written to. */                     /* 0x4a0 */
++   E4_uint64          pad4[0x098 - 0x096];                                                                    /* 0x4b0 */
++
++   E4_uint64          DmaAlignmentPort[8];    /* Write only. Should only be written to clear the prev reg. */ /* 0x4c0 */
++
++   E4_uint64          MmuBlockEntry[8];       /* Used for hash table and chain fetches */                     /* 0x500 */
++   E4_uint64          WriteUnitsTlbLine[3];                                                                   /* 0x550 */
++   E4_uint64          pad5;                                                                                   /* 0x540 */
++   E4_uint64          WriteTProcTlbLine[3];                                                                   /* 0x568 */
++   E4_uint64          pad6;                                                                                   /* 0x540 */
++
++   E4_uint64          MmuTableBasePtrs;       /* Both tables packed into a single 64 bit value */             /* 0x580 */
++   E4_uint64          MmuFaultAndRootCntxPtr; /* Both packed into a single 64 bit value */                    /* 0x588 */
++   E4_uint64          UnitsVAddr;                                                                             /* 0x590 */
++   E4_uint64          TProcVAddr;                                                                             /* 0x598 */
++   E4_uint64          UnitsCntx;                                                                              /* 0x5a0 */
++   E4_uint64          TProcCntx;              /* Read only. Writes access VProcCacheWritePort */              /* 0x5a8 */
++   E4_uint64          FaultAddrReg;                                                                           /* 0x5b0 */
++   E4_uint64          FaultTypeAndContextReg;                                                                 /* 0x5b8 */
++
++   E4_uint32          SysControlReg;                                                                          /* 0x5c0 */
++   E4_uint32          CacheTagValue;                                                                          /* 0x5c4 */
++   E4_uint64          TlbLineValue;                                                                           /* 0x5c8 */
++   E4_uint64          SDRamConfigReg;                                                                         /* 0x5d0 */
++   E4_uint32          InterruptMask;                                                                          /* 0x5d8 */
++   E4_uint32          InterruptReg;                                                                           /* 0x5dc */
++   E4_uint64          SDRamECCStatus;                                                                         /* 0x5e0 */
++   E4_uint32          LinkControlReg;                                                                         /* 0x5e8 */
++   E4_uint32          LinkContSettings;                                                                       /* 0x5ec */
++   E4_uint64          LinkPortKey;                                                                            /* 0x5f0 */
++   E4_uint64          LinkPortLock;                                                                           /* 0x5f8 */
++
++   E4_uint64          SDRamWriteBuffer[4][8];                                                                 /* 0x600 */
++   E4_uint64          SDRamReadBuffer[4][8];                                                                  /* 0x700 */
++
++   E4_uint64          TProcRegs[64];                                                                          /* 0x800 */
++   E4_uint64          TProcStartUp[8];        /* Not to be used except by the elan itself */                  /* 0xa00 */
++
++   E4_uint64          LoadPending;                                                                            /* 0xa40 */
++   E4_uint64          StortPending;                                                                           /* 0xa48 */
++   E4_uint64          DirtyBits;                                                                              /* 0xa50 */
++   E4_uint64          BadBits;                                                                                /* 0xa58 */
++
++   E4_uint64          ICachePort_Cntl_Addr;                                                                   /* 0xa60 */
++   E4_uint64          Thread_Trap_State;                                                                      /* 0xa68 */
++
++/* Instruction buffer (4 * 32 bit words) */
++   E4_uint64          nPC_W;                                                                                  /* 0xa70 */
++   E4_uint64          PC_W;                                                                                   /* 0xa78 */
++
++   E4_uint64          ICacheFillData[8];                                                                      /* 0xa80 */
++   E4_uint64          ICachePort[8];                                                                          /* 0xac0 */
++
++   E4_uint64          PciDataBufs[4][8];                                                                      /* 0xb00 */
++
++   E4_uint64          CommandQueueBuffer[128];                                                                /* 0xc00 */
++} E4_DataBusMap;
++
++#define LINK_PORT_LOCK_VALUE  0x123456789abcdef0ULL
++
++/*
++ * These macros are used to setup the thread pcoessors ICache.
++ */
++#define E4_ICacheTagAddrShift         6
++#define E4_AccessICacheRams           1
++#define E4_InvalidTagValue            0xffffffffffffffffULL
++#define E4_ICacheSizeInBytes          (1024*16)
++#define E4_ICacheLineSizeInBytes      (64)
++#define E4_ICacheLines                        (E4_ICacheSizeInBytes/E4_ICacheLineSizeInBytes)
++#define E4_ICachePortSize             ( (sizeof((E4_DataBusMap *) 0)->ICachePort) /   \
++                                        (sizeof((E4_DataBusMap *) 0)->ICachePort[0]))
++
++#define E4_ICacheFixupInsn            0xc0b02f95ull           /* st1 [%r0 +  0xf95] */
++#define E4_ICacheFixupAddr            0xf95ull
++#define E4_ICacheFixupOffset          0xfc0
++
++/*
++ * Event interrupt
++ */
++typedef volatile union _E4_EventInt
++{
++   E4_uint64   ForceAlign;
++   struct {
++       E4_uint32 IntCookie;
++       E4_uint32 EventContext;        /* Bits 16 to 28 */
++    } s;
++} E4_EventInt;
++
++/*
++ * The following are used to interpret a fault status register.
++ */
++
++/*
++ * FSR[14:0] - AccessType
++ *
++ * T = Type bit
++ * S = size bit. Size is in units of 64 bits or 8 bytes.
++ * E = Byte end pointer. Used to define the last written byte of the last 64 bits written.
++ * D = Data type bit. Used for endian conversion in the PCI interface.
++ * C = Used by the cache to decide if this access should allocate a cache line.
++ * d = Set if dma read or write data data. This is used to guarantee order at the PCI interface.
++ * A = Access type used to check permissions by the MMU in a virtual access.
++ * P = Part Write. If set some byte enables may be used. Effects the action of a cache miss.
++ */
++
++/* FSR[7:0] */
++/* bit 7 => virtual write */
++#define AT_VirtualWriteAccBit         (1 << 7)                /* AAADDdC1EEESSSS = Virtual Write */
++#define AT_VirtualWriteSizeMask               0xf                     /* size of write access (0 => 128 bytes) */
++#define AT_VirtualWriteEndPtrShift    4                       /* end byte pointer for part write block */
++#define AT_VirtualWriteEndPtrMask     0x7
++
++/* else bit 6 => virtual read */
++#define AT_VirtualReadAccBit          (1 << 6)                /* AAADDdC01SSSSSS = Virtual Read */
++#define AT_VirtualReadSizeMask                0x3f                    /* size of read access (0 => 512 bytes) */
++
++/* else => special access */
++#define AT_SelBitsMask                        0xf                     /* Bits to select the type of acces from */
++#define AT_SelBitsShift                       0x4
++#define AT_SpecialRd                  (0x0 << 4)              /* AAADDdC0000TTTT = Special read Access */
++#define AT_SpecialWr                  (0x1 << 4)              /* AAADDdC0001TTTT = Special write Access */
++#define AT_PhysicalRd                 (0x2 << 4)              /* AAADDdC00100SSS = Physical Read */
++#define AT_PhysicalWr                 (0x3 << 4)              /* AAADDdC0011PSSS = Physical write */
++
++#define AT_OtherSizeMask              0xf                     /* Size bits used by all other accesses. 0=128 bytes */
++#define AT_SpecialBitsMask            0xf                     /* Bits used to define the special access types */
++#define AT_CacheSizeBitsMask          0x7                     /* Size bits used for local accesses. 0=64 */
++#define AT_CachePhysPartWriteBit      0x8                     /* This bit is set if the access is a part write to the cache */
++
++/* Special memory access operations */
++#define AT_RegAccess                  0x0
++#define AT_GetCntxFilter              0xe                     /* Only used by special reads */
++#define AT_RouteFetch                 0xf                     /* Only used by special reads */
++
++/* FSR[9:8] */
++#define AT_NonAlloc                   (1 << 8)                /* 1=Do not fill cache with this data */
++#define AT_DmaData                    (1 << 9)                /* This is a DMA read access. Required to guarantee dma read order. */
++
++/* FSR[11:10] - Data Type - defines data type for endian conversion in PCI interface*/
++#define AT_BlkDataTyMask              0x3
++#define AT_BlkDataTyShift             10
++
++#define AT_BlkDataType(FSR)           (((FSR) >> AT_BlkDataTyShift) & AT_BlkDataTyMask)
++#define AT_TypeByte                   0x0
++#define AT_TypeHWord                  0x1
++#define AT_TypeWord                   0x2
++#define AT_TypeDWord                  0x3
++
++/* FSR[14:12] - Access Permissions */
++#define AT_PermBitsMask                       0x7
++#define AT_PermBitsShift              12
++
++#define AT_Perm(FSR)                  (((FSR) >> AT_PermBitsShift) & AT_PermBitsMask)
++#define AT_PermLocalDataRead          0x0
++#define AT_PermLocalDataWrite         0x1
++#define AT_PermRemoteRead             0x2
++#define AT_PermRemoteWrite            0x3
++#define AT_PermExecute                        0x4
++#define AT_PermLocalEvent             0x5
++#define AT_PermRemoteEvent            0x7
++
++/* FSR[22:15] - reason for fault */
++
++#define FSR_WalkForThread             (1 << 15) /* The thread processor caused the fault */
++#define FSR_Walking                   (1 << 16) /* The fault was caused during a hash table access */
++#define FSR_NoTranslationsFound               (1 << 17) /* The hash table did not contain a matching tag */
++#define FSR_WalkingProtectionFault    (1 << 18) /* A protection fault was detected while walking */
++#define FSR_HashTable1                        (1 << 19) /* Was accessing hash table 1 not 0 */
++#define FSR_RouteVProcErr             (1 << 20) /* This is an invalid vproc for a route fetch */
++#define FSR_FaultForBadData           (1 << 21) /* Bad data (double bit ECC error) while performing a walk access */
++#define FSR_FaultForMaxChainCount     (1 << 22) /* The Elan4 has walked a chain of 1024 items. */
++
++typedef volatile struct _E4_FaultSave
++{
++    E4_uint64 FSRAndFaultContext;                 /* Bits 0-31 : FaultContext. Bits 32-63 : FaultStatus Register */
++    E4_uint64 FaultAddress;
++} E4_FaultSave;
++
++#define FaultSaveContext(FSRAndFaultContext)  ((E4_uint32) ((FSRAndFaultContext) & 0xFFFFFFFF))
++#define FaultSaveFSR(FSRAndFaultContext)      ((E4_uint32) ((FSRAndFaultContext) >> 32))
++
++typedef union E4_TrTypeCntx
++{
++   E4_uint32 TypeContext;
++   struct
++   {
++#if (BYTE_ORDER == LITTLE_ENDIAN) || defined(__LITTLE_ENDIAN__)
++      E4_uint32 Type:16;              /* Transaction type field */
++      E4_uint32 Context:13;           /* Transaction context */
++      E4_uint32 TypeCntxInvalid:1;    /* Bit  29 */
++      E4_uint32 StatusRegValid:1;     /* Bit  30 */
++      E4_uint32 LastTrappedTrans:1;   /* Bit  31 */
++#else
++      E4_uint32 LastTrappedTrans:1;   /* Bit  31 */
++      E4_uint32 StatusRegValid:1;     /* Bit  30 */
++      E4_uint32 TypeCntxInvalid:1;    /* Bit  29 */
++      E4_uint32 Context:13;           /* Transaction context */
++      E4_uint32 Type:16;              /* Transaction type field */
++#endif
++   } s;
++} E4_TrTypeCntx;
++
++#define MAX_TRAPPED_TRANS     28
++#define TRANS_DATA_DWORDS     16
++#define TRANS_DATA_BYTES      128
++#define NO_OF_INPUT_CHANNELS  4
++
++#define CH0_LOW_PRI_CHAN      0
++#define CH1_LOW_PRI_CHAN      1
++#define CH0_HIGH_PRI_CHAN     2
++#define CH1_HIGH_PRI_CHAN     3
++
++/* Words have been swapped for big endian access when fetched with dword access from elan.*/
++typedef struct _E4_IprocTrapHeader
++{
++   E4_uint64  TrAddr;
++   E4_uint64  IProcStatusCntxAndTrType;
++} E4_IprocTrapHeader;
++
++typedef struct _E4_IprocTrapData
++{
++   E4_uint64 Data[TRANS_DATA_DWORDS];
++} E4_IprocTrapData;
++
++/*
++ * This struct defines the trap state for the inputers. It requires a contiguous 16K byte block of local memory.
++ * The channel bits have been grouped to the low end of the address to force all Identify cookies to use the
++ * same cache line.
++ */
++typedef struct _E4_IprocTrapState
++{
++   E4_IprocTrapData   TrData[MAX_TRAPPED_TRANS][NO_OF_INPUT_CHANNELS];
++   E4_IprocTrapHeader TrHeader[MAX_TRAPPED_TRANS][NO_OF_INPUT_CHANNELS];
++   E4_uint64        pad[8*NO_OF_INPUT_CHANNELS];
++} E4_IprocTrapState;
++
++/*
++ * 64 kbytes of elan local memory. Must be aligned on a 64k boundary
++ */
++#define E4_LowPriQueueSize    0x400
++#define E4_HighPriQueueSize   0x100
++
++typedef struct _E4_FaultSaveArea
++{
++   E4_FaultSave               TProcData[8];
++   E4_FaultSave               TProcInst;
++   E4_FaultSave               Dummy[7];
++   E4_FaultSave               SchedProc;
++   E4_FaultSave               DProc;
++   E4_FaultSave               EventProc;
++   E4_FaultSave               IProc;
++   E4_FaultSave               DProcData[4];
++   E4_FaultSave               QReadData[8];
++} E4_FaultSaveArea;
++
++/* Macros to manipulate event queue pointers */
++/*     generate index in EventIntQueue */
++#define E4_EVENT_INTQ_INDEX(fptr)     (((fptr) & 0x1fff) >> 3)
++/*     generate next fptr */
++#define E4_EVENT_INTQ_NEXT(fptr)      ((((fptr) + 8) & ~0x4000) | 0x2000)
++
++typedef struct _E4_CommandPort
++{
++   volatile E4_uint64 Command[1024];  /* a whole 8k page */
++} E4_CommandPort;
++
++/*
++ * This is the allocation of unit numbers within the ELAN. It is used to extract the fault address
++ * and fault type after a unit has trapped on a memory fetch. Only units that can generate traps
++ * have been included.
++ */
++#define CUN_TProcData0                0x00
++#define CUN_TProcData1                0x01
++#define CUN_TProcData2                0x02
++#define CUN_TProcData3                0x03
++#define CUN_TProcData4                0x04
++#define CUN_TProcData5                0x05
++#define CUN_TProcData6                0x06
++#define CUN_TProcData7                0x07
++#define CUN_TProcInst         0x08
++
++/* memory current unit numbers
++ * TProc data bus */
++#define CUN_DProcPA0          0x10
++#define CUN_DProcPA1          0x11
++#define CUN_DProcPrefetch     0x12
++#define CUN_CommandProc               0x13
++#define CUN_DProcData0                0x14    /* Dma prefetch reads. */
++#define CUN_DProcData1                0x15    /* Dma prefetch reads. */
++#define CUN_DProcData2                0x16    /* Dma prefetch reads. */
++#define CUN_DProcData3                0x17    /* Dma prefetch reads. */
++
++#define CUN_IProcLowPri               0x18
++#define CUN_IProcHighPri      0x19
++#define CUN_Spare0            0x1A
++#define CUN_Spare1            0x1B
++#define CUN_Spare2            0x1C
++#define CUN_ThreadQueue               0x1D
++#define CUN_EventProc0                0x1e
++#define CUN_EventProc1                0x1f
++
++#define CUN_Entries           0x20
++
++typedef struct E4_Registers
++{
++   E4_CacheTags               Tags;                           /* 4k bytes  c000 -> cfff */
++   E4_DataBusMap      Regs;                           /* 4k bytes  d000 -> dfff */
++   E4_User_Regs               uRegs;                          /* 8k bytes  e000 -> ffff */
++} E4_Registers;
++
++#define I2cCntl_I2cPortWrite          (0 << 0)
++#define I2cCntl_I2cPortRead           (1 << 0)
++#define I2cCntl_I2cPortGenStopBit     (1 << 1)
++#define I2cCntl_I2cPortGenRestartBit  (1 << 2)
++#define I2cCntl_I2cPortAccFailed      (1 << 3)
++#define I2cCntl_I2cStopped            (1 << 4)
++#define I2cCntl_I2cWakeupFailed               (1 << 5)
++#define I2cCntl_I2cFastMode           (1 << 6)
++#define I2cCntl_I2cPortBusy           (1 << 7)
++
++#define I2cCntl_LedI2cRegBase_Mask    0x7f
++#define I2cCntl_I2cUpdatingLedReg     (1 << 7)
++
++#define I2cCntl_InvertLedValues               (1 << 0)                /* read/write */
++#define I2cCntl_LedRegWriteFailed     (1 << 1)                /* read only */
++#define I2cCntl_EEPromLoadFailed      (1 << 2)                /* read only */
++#define I2cCntl_InhibitI2CRom         (1 << 3)                /* read only */
++#define I2cCntl_BadRomCrc             (1 << 4)                /* read only */
++#define I2cCntl_MapInI2cConfigData    (1 << 5)                /* read/write */
++#define I2cCntl_SampleNewLedValues    (1 << 6)                /* read/write */
++#define I2cCntl_ClearLinkError                (1 << 7)                /* write only */
++
++typedef struct E4_I2C
++{
++   volatile E4_uint8    I2cWrData;
++   volatile E4_uint8    I2cRdData;
++   volatile E4_uint8    I2cPortControl;
++   volatile E4_uint8  I2cLedBase;
++   volatile E4_uint8    I2cStatus;
++   volatile E4_uint8    I2cLedsValue;
++   volatile E4_uint16 I2cPad;
++ 
++   E4_uint8           pad[256 - sizeof(E4_uint64)];
++
++   E4_uint8           UnchangedElan4ConfigRegs[256];
++   E4_uint8           I2cRomConfigShadowValues[256];
++   E4_uint8           ChangedElan4ConfigRegs[256];
++} E4_I2C;
++
++typedef struct _E4_ContextControlBlock 
++{
++    E4_uint32 Filter;                 /* Use a Network context to index for this value */
++    E4_uint32 VirtualProcessTable;    /* Use a local context to index for this value */
++} E4_ContextControlBlock;
++
++/*
++ * Filter
++ *   [13:0]   Context
++ *   [14]     DiscardAll
++ *   [15]     AckAll
++ *   [16]     HighPri
++ *   [17]     CountStats
++ *   [31:18]  Unused
++ */
++#define E4_FILTER_STATS               (1 << 17)
++#define E4_FILTER_HIGH_PRI    (1 << 16)
++#define E4_FILTER_ACKOK_ALL   (1 << 15)
++#define E4_FILTER_DISCARD_ALL (1 << 14)
++#define E4_FILTER_CONTEXT_MASK        (0x3FFF)
++
++/*
++ * VirtualProcessTable
++ *   [8:0]    Unused  
++ *   [12:9]   Size       num vp entries = 512 << Size
++ *   [30:13]  Pointer
++ *   [31]     Valid
++ */
++#define E4_VPT_MIN_ENTRIES      512
++#define E4_VPT_VALID          ((unsigned)1 << 31)
++#define E4_VPT_PTR_SHIFT      0
++#define E4_VPT_SIZE_SHIFT     9
++#define E4_VPT_SIZE_MASK        0xf
++#define E4_VPT_NUM_VP(vpt_val)  (E4_VPT_MIN_ENTRIES << (((vpt_val) >> E4_VPT_SIZE_SHIFT) & E4_VPT_SIZE_MASK))
++#define E4_VPT_VALUE(ptr,size)        (((ptr) << E4_VPT_PTR_SHIFT) | ((size) << E4_VPT_SIZE_SHIFT))
++
++
++/* Virtual Process Table */
++typedef struct _E4_VirtualProcessEntry
++{
++    E4_uint64 Values[2];
++} E4_VirtualProcessEntry;
++
++/*
++ * Entries have the following format - rtX is a packed route 
++ *
++ * |rt11|rt10|rt9 |rt8 |rt7 |rt6 |rt5 |rt4 |rt3 |rt2 |rt2 |rt0 |PAAADD       RRRRRR|
++ * |output context     |rt23|rt22|rt21|rt20|rt19|rt18|rt17|rt16|rt15|rt14|rt13|rt12|
++ */
++
++#define ROUTE_CTXT_SHIFT      48
++#define ROUTE_CTXT_MASK               (~((1ull << ROUTE_CTXT_SHIFT)-1))
++#define ROUTE_CTXT_VALUE(ctx) (((E4_uint64) ctx) << ROUTE_CTXT_SHIFT)
++
++#define ROUTE_PACKED_OFFSET   16
++#define ROUTE_NUM_PACKED      24
++
++/* defines for first flit of a route */
++#define FIRST_TIMEOUT(Val)    ((Val) << 14)                   /* [15:14]  */
++#define FIRST_SYSTEM_PACKET     (1 << 13)                       /* [13]     */
++#define FIRST_FLOOD_PACKET      (1 << 12)                       /* [12]     */
++#define FIRST_HIGH_PRI                (1 << 11)                       /* [11]    */
++#define FIRST_AGE(Val)                ((Val) << 7)                    /* [10:7] */
++#define FIRST_OPTIONS_MASK    (0xFF80)
++
++/* [6:0] unpacked 1st route value */
++#define FIRST_INVALID         (0)
++#define FIRST_ROUTE(Val)      (0x08 | (Val))
++#define FIRST_ADAPTIVE                (0x30)
++#define FIRST_BCAST_TREE      (0x20)
++#define FIRST_MYLINK          (0x10)
++#define FIRST_BCAST(Top, Bot) (0x40 | ((Top) << 3) | (Bot))
++
++/* defines for 3 bit packed entries for subsequent flits */
++#define PACKED_INVALID                (0)
++#define PACKED_ROUTE(Val)     (8 | (Val))
++#define PACKED_ADAPTIVE               (3)
++#define PACKED_BCAST_TREE     (2)
++#define PACKED_MYLINK         (1)
++#define PACKED_BCAST0(Top,Bot)        (4 | (Bot & 3))
++#define PACKED_BCAST1(Top,Bot)        ((Top << 1) | (Bot >> 2))
++
++#endif /* _ASM */
++/* The MMU root context pointer has a mask to bounds check 
++ * it - this is computed as follows.
++ */
++#define E4_CONTEXT_MASK(num)  (((num) >= 0x2000) ? 0x00 :     \
++                               ((num) >= 0x1000) ? 0x80 :     \
++                               ((num) >= 0x0800) ? 0xc0 :     \
++                               ((num) >= 0x0400) ? 0xe0 :     \
++                               ((num) >= 0x0200) ? 0xf0 :     \
++                               ((num) >= 0x0100) ? 0xf8 :     \
++                               ((num) >= 0x0080) ? 0xfc :     \
++                               ((num) >= 0x0040) ? 0xfe : 0xff)
++/*
++ * This generates the size field for a virtual process table.
++ * Size defined as 2^n no of 8K pages.
++ * Single cycle route fetches are possible if the minimum vproc table size is 8k.
++ */
++#define E4_GEN_VPT_SIZE(Size)         (((Size) & E4_VPT_SIZE_MASK) << E4_VPT_SIZE_SHIFT)
++
++#define COMMAND_RUN_QUEUE_BITS                (13 + 2) /* 8K entries of 4 bytes. This is fixed in hardware. */
++#define COMMAND_DESCS_SPACE_BITS      (13 + 5) /* 8K entries of 32 bytes. This is fixed in hardware. */
++#define COMMAND_INSERTER_CACHE_ENTRIES        16
++
++#define COM_TEST_PORT_ADDR_MASK               0xfULL
++#define COM_TEST_PORT_ADDR_SH         0
++
++/*
++ * The flush register is accessed through the CommandControl register.
++ * The address is naturally alligned. It also positions the command descriptors in memory.
++ * When no command queues need flushing it should be or with COM_FLUSH_INVALID. This sets
++ * it to the top command queue descriptor. This cannot be accessed from the PCI.
++ */
++#define COM_ENABLE_DEQUEUE            (1 << 4)
++#define COM_FLUSH_DESCRIPTOR_MASK     0x7fffffe0ULL
++#define COM_FLUSH_INVALID             0x0003ffe0ULL
++
++
++/*
++ * Elan4 BAR1 is split up as follows :
++ *
++ * RevA
++ *     0x3f00000 EBUS other
++ *     0x3e00000 EBUS ROM
++ *     0x3dfc000 registers
++ *     0x0000000 command ports
++ *
++ * RevB
++ *     0x3ffc000 registers
++ *     0x3ff8000 padding
++ *     0x3ff6000 i2c registers
++ *     0x0000000 command ports
++ */
++#define ELAN4_BAR1_SIZE                       (1 << 26)       /* 64M */
++#define ELAN4_REG_SIZE                        (1 << 14)       /* 16K */
++
++#define ELAN4_REVA_EBUS_SIZE          (1 << 21)       /* 2M */
++#define ELAN4_REVA_EBUS_OFFSET                (ELAN4_BAR1_SIZE - ELAN4_REVA_EBUS_SIZE)
++#define ELAN4_REVA_REG_OFFSET         (ELAN4_REVA_EBUS_OFFSET - ELAN4_REG_SIZE)
++#define ELAN4_REVA_NUM_COMMAND_QUEUES (ELAN4_REVA_REG_OFFSET >> 13)
++
++#define ELAN4_REVA_EBUS_ROM_SIZE      (1 << 20)       /* 1M */
++#define ELAN4_REVA_EBUS_ROM_OFFSET    0
++
++#define ELAN4_REVB_I2C_PADDING                (1 << 14)       /* 16K */
++#define ELAN4_REVB_I2C_SIZE           (1 << 13)       /* 8k */
++#define ELAN4_REVB_REG_OFFSET         (ELAN4_BAR1_SIZE - ELAN4_REG_SIZE)
++#define ELAN4_REVB_I2C_OFFSET         (ELAN4_REVB_REG_OFFSET - ELAN4_REVB_I2C_PADDING - ELAN4_REVB_I2C_SIZE)
++#define ELAN4_REVB_NUM_COMMAND_QUEUES (ELAN4_REVB_I2C_OFFSET >> 13)
++
++#endif /* notdef _ELAN4_REGISTERS_H */
+Index: linux-2.4.21/include/elan4/sdram.h
+===================================================================
+--- linux-2.4.21.orig/include/elan4/sdram.h    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan4/sdram.h 2005-06-01 23:12:54.743417216 -0400
+@@ -0,0 +1,41 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN4_SDRAM_H
++#define __ELAN4_SDRAM_H
++
++#ident "$Id: sdram.h,v 1.8 2003/09/24 13:55:55 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan4hdr/sdram.h,v $*/
++
++/* Include header file generated by sdram configuration program */
++#include <elan4/xsdram.h> 
++
++/* SDRAM bank shift definitions */
++#define SDRAM_0_CS_SHIFT      25
++#define SDRAM_1_CS_SHIFT      27
++#define SDRAM_2_CS_SHIFT      28
++#define SDRAM_3_CS_SHIFT      29
++
++#define SDRAM_BANK_SHIFT(cfg) \
++      (((cfg >> SDRAM_RamSize_SH) & 3) == 0 ? SDRAM_0_CS_SHIFT : \
++       ((cfg >> SDRAM_RamSize_SH) & 3) == 1 ? SDRAM_1_CS_SHIFT : \
++       ((cfg >> SDRAM_RamSize_SH) & 3) == 2 ? SDRAM_2_CS_SHIFT : SDRAM_3_CS_SHIFT)
++
++#define SDRAM_BANK_SIZE(cfg)          (1ULL << SDRAM_BANK_SHIFT(cfg))
++#define SDRAM_BANK_OFFSET(cfg,bank)   ((unsigned long long)(bank) << SDRAM_BANK_SHIFT(cfg))
++#define SDRAM_NUM_BANKS(cfg)          (4)
++#define SDRAM_MAX_BANKS                       4
++
++/* When the elan access sdram it passes eaddr[12] as sdramaddr[12] when
++ * running with a 4k page size, however PCI accesses pass paddr[12], so
++ * we must ensure that sdram pages are allocated such that eaddr[12] is the
++ * same as paddr[12] - the easiest way is to allocate sdram in 8k chunks and
++ * ensure that maddr[12] == eaddr[12] == pgoff[0] */
++#define SDRAM_MIN_PAGE_SIZE           (8192)
++
++#endif /* __ELAN4_SDRAM_H */
+Index: linux-2.4.21/include/elan4/stats.h
+===================================================================
+--- linux-2.4.21.orig/include/elan4/stats.h    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan4/stats.h 2005-06-01 23:12:54.743417216 -0400
+@@ -0,0 +1,83 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: stats.h,v 1.10.12.1 2004/10/06 11:09:12 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/stats.h,v $*/
++
++#ifndef __ELAN4_STATS_H
++#define __ELAN4_STATS_H
++
++#define ELAN4_DEV_STATS_BUCKETS               8
++
++
++typedef struct elan4_dev_stats
++{
++    unsigned long     s_interrupts;
++    
++    unsigned long       s_mainints[ELAN4_DEV_STATS_BUCKETS];
++    unsigned long     s_mainint_punts;
++    unsigned long     s_mainint_rescheds;
++
++    unsigned long       s_haltints;
++
++    unsigned long     s_cproc_traps;
++    unsigned long     s_dproc_traps;
++    unsigned long     s_eproc_traps;
++    unsigned long     s_iproc_traps;
++    unsigned long     s_tproc_traps;
++
++    unsigned long       s_cproc_trap_types[0x10];
++    unsigned long       s_dproc_trap_types[6];
++    unsigned long       s_eproc_trap_types[4];
++    unsigned long       s_iproc_trap_types[0xa];
++    unsigned long       s_tproc_trap_types[7];
++
++    unsigned long       s_correctable_errors;
++    unsigned long       s_multiple_errors;
++    
++    unsigned long       s_link_errors;
++    unsigned long       s_lock_errors;
++    unsigned long       s_deskew_errors;
++    unsigned long       s_phase_errors;
++    unsigned long     s_data_errors;
++    unsigned long     s_fifo_overflow0;
++    unsigned long     s_fifo_overflow1;
++    unsigned long       s_mod45changed;
++    unsigned long       s_pack_not_seen;
++    unsigned long       s_linkport_keyfail;
++
++    unsigned long     s_eop_reset;
++    unsigned long       s_bad_length;
++    unsigned long       s_crc_bad;
++    unsigned long       s_crc_error;
++
++    unsigned long     s_cproc_timeout;
++    unsigned long     s_dproc_timeout;
++
++    unsigned long     s_sdram_bytes_free;
++} ELAN4_DEV_STATS;
++
++#define MainIntBuckets                ((int[ELAN4_DEV_STATS_BUCKETS-1]) {1, 2, 3, 4, 8, 16, 32})
++
++#define BumpDevStat(dev,stat) ((dev)->dev_stats.stat++)
++#define BucketDevStat(dev,stat,n,bucket)      ((n) <= (bucket)[0] ? (dev)->dev_stats.stat[0]++ : \
++                                               (n) <= (bucket)[1] ? (dev)->dev_stats.stat[1]++ : \
++                                               (n) <= (bucket)[2] ? (dev)->dev_stats.stat[2]++ : \
++                                               (n) <= (bucket)[3] ? (dev)->dev_stats.stat[3]++ : \
++                                               (n) <= (bucket)[4] ? (dev)->dev_stats.stat[4]++ : \
++                                               (n) <= (bucket)[5] ? (dev)->dev_stats.stat[5]++ : \
++                                               (n) <= (bucket)[6] ? (dev)->dev_stats.stat[6]++ : \
++                                                                    (dev)->dev_stats.stat[7]++)
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++#endif /*__ELAN4_STATS_H */
+Index: linux-2.4.21/include/elan4/tprintf.h
+===================================================================
+--- linux-2.4.21.orig/include/elan4/tprintf.h  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan4/tprintf.h       2005-06-01 23:12:54.743417216 -0400
+@@ -0,0 +1,24 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN4_TPRINTF_H
++#define __ELAN4_TPRINTF_H
++
++#ident "$Id: tprintf.h,v 1.6 2003/09/04 12:39:17 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan4hdr/tprintf.h,v $*/
++
++
++#ifdef _ASM
++#define TPRINTF0(string)          add %r0, __LINE__, %r0
++#define TPRINTF1(string,reg)      add reg, __LINE__, %r0
++#else
++#define TPRINTF0(string)          asm volatile ("add %%r0, %0, %%r0" : : "i" (__LINE__))
++#define TPRINTF1(string, value)           asm volatile ("add %0,   %1, %%r0" : : "r" (value), "i" (__LINE__))
++#endif /* _ASM */
++
++#endif /* __ELAN4_TPRINTF_H */
+Index: linux-2.4.21/include/elan4/trap.h
+===================================================================
+--- linux-2.4.21.orig/include/elan4/trap.h     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan4/trap.h  2005-06-01 23:12:54.743417216 -0400
+@@ -0,0 +1,95 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: trap.h,v 1.10 2003/10/07 12:11:10 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/trap.h,v $*/
++
++#ifndef __ELAN4_TRAP_H
++#define __ELAN4_TRAP_H
++
++/*
++ * If the EProc Faults whilst performing an action (e.g. Read/Write on the data src or dest Addr)
++ *  the Eproc increments the Addr(s) by a block size (64 bytes):
++ *  1: Fault on Read: 
++ *                     Src EventAddr = Read Addr + block
++ *  2: Fault on Write:
++ *                     Src EventAddr = Read Addr + block
++ *                     Dst EventAddr = Read Addr + block
++ *                     Size          = Size - block ndwords
++ *  We must rewind the addr correctly to completely the transfer successfully
++ */
++#define EVENT_COPY_NDWORDS    0x8
++#define EVENT_COPY_BLOCK_SIZE 0x40
++
++typedef struct elan4_eproc_trap
++{
++    E4_uint64         tr_status;
++    E4_FaultSave      tr_faultarea;
++    E4_Event          tr_event;
++    E4_Addr           tr_eventaddr;
++} ELAN4_EPROC_TRAP;
++
++typedef struct elan4_cproc_trap
++{
++    E4_uint64         tr_status;                                      /* cproc status register */
++    E4_uint64         tr_command;                                     /* cproc command */
++    E4_CommandQueueDesc tr_qdesc;                                     /* copy of command queue descriptor */
++    E4_FaultSave      tr_faultarea;                                   /* fault area for mmu traps */
++    ELAN4_EPROC_TRAP  tr_eventtrap;                                   /* associated event trap (waitevent) */
++} ELAN4_CPROC_TRAP;
++
++typedef struct elan4_dproc_trap
++{
++    E4_DMA            tr_desc;
++    E4_FaultSave      tr_packAssemFault;
++    E4_FaultSave      tr_prefetchFault;
++    E4_uint64         tr_status;
++} ELAN4_DPROC_TRAP;
++
++typedef struct elan4_tproc_trap
++{
++    E4_uint64         tr_regs[64];
++    E4_FaultSave      tr_dataFault;
++    E4_FaultSave      tr_instFault;
++    E4_uint64         tr_status;
++    E4_uint64         tr_state;
++    E4_Addr           tr_pc;
++    E4_Addr           tr_npc;
++    E4_uint64         tr_dirty;
++    E4_uint64         tr_bad;
++} ELAN4_TPROC_TRAP;
++
++typedef struct elan4_iproc_trap
++{
++    E4_uint32           tr_numTransactions;
++    E4_uint32           tr_flags;
++    E4_uint32           tr_trappedTrans;
++    E4_uint32           tr_waitForEopTrans;
++    E4_uint32           tr_identifyTrans;
++    E4_uint32           tr_pad;
++
++    E4_FaultSave          tr_faultarea;
++    E4_IprocTrapHeader    tr_transactions[MAX_TRAPPED_TRANS];
++    E4_IprocTrapData      tr_dataBuffers[MAX_TRAPPED_TRANS];
++} ELAN4_IPROC_TRAP;
++
++#define TR_FLAG_ACK_SENT      (1 << 0)
++#define TR_FLAG_EOP_ERROR     (1 << 1)
++#define TR_FLAG_BAD_TRANS     (1 << 2)
++#define TR_FLAG_DMA_PACKET    (1 << 3)
++#define TR_FLAG_EOP_BAD               (1 << 4)
++#define TR_FLAG_TOOMANY_TRANS (1 << 5)
++
++#define TR_TRANS_INVALID      (0xffffffff)
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++#endif /* __ELAN4_TRAP_H */
+Index: linux-2.4.21/include/elan4/trtype.h
+===================================================================
+--- linux-2.4.21.orig/include/elan4/trtype.h   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan4/trtype.h        2005-06-01 23:12:54.744417064 -0400
+@@ -0,0 +1,112 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef _ELAN4_TRTYPE_H
++#define _ELAN4_TRTYPE_H
++
++#ident "$Id: trtype.h,v 1.20 2004/02/06 10:38:21 mike Exp $"
++/*      $Source: /cvs/master/quadrics/elan4hdr/trtype.h,v $*/
++
++/*<15:11> Size field is used to give the number of additional 64 bit data values.
++        A value from 0 to 16 inclusive is valid. */
++
++#include <elan4/types.h>
++
++#define TR_SIZE_SHIFT         (11)
++#define TR_SIZE_MASK          (0x1f << TR_SIZE_SHIFT)
++#define SET_TR_SIZE(Size)     (((Size) << TR_SIZE_SHIFT) & TR_SIZE_MASK)
++
++/* <10:9> Last Transaction and AckNow bits, marks the last transaction and
++          enables a PACK_OK to be sent. */
++#define TR_LAST_AND_SEND_ACK  (3 << 9)
++
++
++/* <8>  Only valid on the last transaction. Delays execution until an EOP_GOOD is received.
++ *      Any other EOP type will abort execution of this transaction. */
++#define TR_WAIT_FOR_EOP               (1 << 8)
++
++/*
++ * Data type. This is used by transactions of variable data type. It controls any endian
++ * converion required if the destiantion host processor has a big endian memory format.
++ */
++/*    WriteBlock      <8:7>   Data type
++                      <6:0>   Part write size */
++#define TR_DATATYPE_SHIFT     (6)
++#define TR_DATATYPE_MASK      ((1 << 2) - 1)
++
++#define TR_DATATYPE_BYTE      E4_DATATYPE_BYTE        
++#define TR_DATATYPE_SHORT     E4_DATATYPE_SHORT
++#define TR_DATATYPE_WORD      E4_DATATYPE_WORD        
++#define TR_DATATYPE_DWORD     E4_DATATYPE_DWORD
++
++/* <5:0> Transaction Type
++ *       For Writeblock <5:3> 000 => Write, 0001 => Read
++ *                      <2:0> End Byte Addr */
++#define TR_OPCODE_MASK                0x3F
++#define TR_BLOCK_OPCODE_MASK  0x38
++
++#define TR_WRITEBLOCK         0x0
++#define TR_ENDBYTE_MASK               0x7
++#define TR_WRITE(Size, EndByte, DataType)                                             \
++                      (0x0 | SET_TR_SIZE(Size) | ((EndByte) & TR_ENDBYTE_MASK) |      \
++                       (((DataType) & TR_DATATYPE_MASK) << TR_DATATYPE_SHIFT))
++
++#define TR_NOP_TRANS          (0x10 | SET_TR_SIZE(0))
++#define TR_SETEVENT           0x10
++#define TR_SETEVENT_NOIDENT   (TR_SETEVENT | SET_TR_SIZE(0) | TR_LAST_AND_SEND_ACK)
++#define TR_SETEVENT_IDENTIFY  (TR_SETEVENT | SET_TR_SIZE(1) | TR_LAST_AND_SEND_ACK)
++#define TR_REMOTEDMA          (0x11 | SET_TR_SIZE(7) | TR_LAST_AND_SEND_ACK)
++#define TR_SENDDISCARD                (0x12 | SET_TR_SIZE(0))
++
++/*
++ * Conditional transactions that might return PAckTestFail.
++ * All will allow further exection of the packet if ([Address] operator DataValue) is true.
++ * e.g. for TR_GTE further execution if ([Address] >= DataValue) is true.
++ * These should be used where a definite TRUE/FALSE answer is required.
++ */
++#define TR_GTE                        (0x14 | SET_TR_SIZE(1))
++#define TR_LT                 (0x15 | SET_TR_SIZE(1))
++#define TR_EQ                 (0x16 | SET_TR_SIZE(1))
++#define TR_NEQ                        (0x17 | SET_TR_SIZE(1))
++
++/*
++ * Conditional transactions that might return PAckDiscard.
++ * All will allow further exection of the packet if ([Address] operator DataValue) is true.
++ * e.g. for TR_GTE further execution if ([Address] >= DataValue) is true.
++ * These should be used where eventually a TRUE answer is expected but the node might not be ready yet.
++ * These can be mixed with the normal conditionals to allow a single packet to test for readyness and
++ * a TRUE/FALSE answer.
++ */
++#define TR_GTE_DISCARD                (0x34 | SET_TR_SIZE(1))
++#define TR_LT_DISCARD         (0x35 | SET_TR_SIZE(1))
++#define TR_EQ_DISCARD         (0x36 | SET_TR_SIZE(1))
++#define TR_NEQ_DISCARD                (0x37 | SET_TR_SIZE(1))
++
++#define TR_TRACEROUTE_TRANS   0x18
++#define TR_TRACEROUTE(Size)   (TR_TRACEROUTE_TRANS | (TR_DATATYPE_WORD << TR_DATATYPE_SHIFT) |SET_TR_SIZE(Size))
++#define TR_IDENTIFY           (0x19 | SET_TR_SIZE(0))
++
++#define TR_ADDWORD            (0x1c | SET_TR_SIZE(2) | TR_LAST_AND_SEND_ACK)
++#define TR_INPUT_Q_COMMIT     (0x1d | SET_TR_SIZE(1) | TR_LAST_AND_SEND_ACK)
++#define TR_TESTANDWRITE       (0x1e | SET_TR_SIZE(3) | TR_LAST_AND_SEND_ACK)
++#define TR_INPUT_Q_GETINDEX   (0x1f | SET_TR_SIZE(0))
++
++
++
++/* TraceRoute formate */
++#define TR_TRACEROUTE0_CHANID(val)            ((val) & 1)                     /* 0     Chan Id */
++#define TR_TRACEROUTE0_LINKID(val)            (((val) >> 1) & 7)              /* 1:3   Link Id */
++#define TR_TRACEROUTE0_REVID(val)             (((val) >> 4) & 7)              /* 4:6   Revision Id */
++#define TR_TRACEROUTE0_BCAST_PIN(val)         (((val) >> 7) & 1)              /* 7     Bcast Top Pin */
++#define TR_TRACEROUTE0_LNR(val)                       (((val) >> 8) & 0xFF)           /* 8:15  Global Link Not Ready */
++
++#define TR_TRACEROUTE1_ROUTES_SELECTED(val)   ((val & 0xFF))                  /* 0:7   Routes Selected */
++#define TR_TRACEROUTE1_BCAST_TOP(val)         (((val) >> 8) & 7)              /* 8:10  Broadcast Top */
++#define TR_TRACEROUTE1_BCAST_BOTTOM(val)      (((val) >> 12) & 7)             /* 12:14 Broadcast Bottom */
++
++#endif /* _ELAN4_TRANSACTIONTYPE_H */
+Index: linux-2.4.21/include/elan4/types.h
+===================================================================
+--- linux-2.4.21.orig/include/elan4/types.h    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan4/types.h 2005-06-01 23:12:54.744417064 -0400
+@@ -0,0 +1,69 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN4_TYPES_H
++#define __ELAN4_TYPES_H
++
++#ident "@(#)$Id: types.h,v 1.9 2003/09/04 12:39:17 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan4hdr/types.h,v $*/
++
++#include <qsnet/config.h>
++/*
++ * "flip" values for correctly indexing into
++ * block data which was copied from the Elan
++ * using 64 bit accesses.
++ */
++#if defined(__LITTLE_ENDIAN__)
++#  define ByteEndianFlip  0
++#  define ShortEndianFlip 0
++#  define WordEndianFlip  0
++#else
++#  define ByteEndianFlip  7
++#  define ShortEndianFlip 3
++#  define WordEndianFlip  1
++#endif
++
++
++#ifndef _ASM
++
++typedef signed int       E4_int;
++typedef unsigned int             E4_uint;
++
++typedef signed char      E4_int8;
++typedef unsigned char            E4_uint8;
++
++typedef signed short     E4_int16;
++typedef unsigned short           E4_uint16;
++
++typedef signed int       E4_int32;
++typedef unsigned int             E4_uint32;
++
++#ifdef _LP64
++typedef signed long        E4_int64;
++typedef unsigned long      E4_uint64;
++#else
++typedef signed long long   E4_int64;
++typedef unsigned long long E4_uint64;
++#endif
++
++/* 64-bit Elan4 */
++typedef E4_uint64        E4_Addr;
++typedef E4_uint32        E4_LocPhysAddr;      /* Really 31 bits */
++
++#define OneK  (1024)
++#define EightK        (8*OneK)
++
++#define E4_DATATYPE_BYTE      0
++#define E4_DATATYPE_SHORT     1
++#define E4_DATATYPE_WORD      2
++#define E4_DATATYPE_DWORD     3
++
++#endif /* _ASM */
++
++#endif /* __ELAN4_TYPES_H */
++
+Index: linux-2.4.21/include/elan4/user.h
+===================================================================
+--- linux-2.4.21.orig/include/elan4/user.h     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan4/user.h  2005-06-01 23:12:54.745416912 -0400
+@@ -0,0 +1,344 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: user.h,v 1.37.2.2 2004/11/18 17:54:17 duncant Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/user.h,v $*/
++
++#ifndef __ELAN4_USER_H
++#define __ELAN4_USER_H
++
++#include <elan/capability.h>
++#include <elan4/usertrap.h>
++#include <elan4/intcookie.h>
++
++typedef struct trap_queue
++{
++    unsigned  q_back;                 /* Next free space */
++    unsigned  q_front;                /* First object to remove */
++    unsigned  q_size;                 /* Size of queue */
++    unsigned  q_count;                /* Current number of entries */
++    unsigned  q_slop;                 /* FULL <=> (count+slop) == size */
++} RING_QUEUE;
++
++#define RING_QUEUE_INIT(q,num,slop)   ((q).q_size = (num), (q).q_slop = (slop), (q).q_front = (q).q_back = 0, (q).q_count = 0)
++#define RING_QUEUE_FULL(q)            ((q).q_count >= ((q).q_size - (q).q_slop))
++#define RING_QUEUE_REALLY_FULL(q)     ((q).q_count == (q).q_size)
++#define RING_QUEUE_EMPTY(q)           ((q).q_count == 0)
++#define RING_QUEUE_NEXT(q,indx)               ((indx) = (((indx)+1) % (q).q_size))
++#define RING_QUEUE_PREV(q,indx)               ((indx) = (((indx)+(q).q_size-1) % (q).q_size))
++#define RING_QUEUE_ADD(q)             (RING_QUEUE_NEXT(q ,(q).q_back),  (++(q).q_count) >= ((q).q_size - (q).q_slop))
++#define RING_QUEUE_REMOVE(q)          (RING_QUEUE_NEXT(q, (q).q_front), (--(q).q_count) == 0)
++#define RING_QUEUE_ADD_FRONT(q)               (RING_QUEUE_PREV(q, (q).q_front), (++(q).q_count) >= ((q).q_size - (q).q_slop))
++#define RING_QUEUE_ENTRY(qArea,indx)  (&(qArea)[(indx)])
++#define RING_QUEUE_FRONT(q,qArea)     RING_QUEUE_ENTRY(qArea, (q).q_front)
++#define RING_QUEUE_BACK(q,qArea)      RING_QUEUE_ENTRY(qArea, (q).q_back)
++#define RING_QUEUE_ITERATE(q,idx)     for (idx = (q).q_front; idx != (q).q_back; idx = (((idx) + 1) % (q).q_size))
++
++typedef struct user_rgn
++{
++    struct user_rgn *rgn_mnext;                                       /* Doubly linked list of regions */
++    struct user_rgn *rgn_mprev;                                       /*   sorted on main address */ 
++    virtaddr_t       rgn_mbase;                                       /* main address of base of region */
++
++    struct user_rgn *rgn_enext;                                       /* Doubly linked list of regions */
++    struct user_rgn *rgn_eprev;                                       /*   sorted on elan address */
++    E4_Addr        rgn_ebase;                                 /* elan address of base of region */
++
++    unsigned long    rgn_len;                                 /* length of region */
++    unsigned       rgn_perm;                                  /* elan access permission */
++} USER_RGN;
++
++typedef struct user_vpseg
++{ 
++    struct list_head  vps_link;
++
++    unsigned short    vps_process;                            /* virtual process number */
++    unsigned short    vps_entries;                            /*   and # virtual processes */
++
++    unsigned          vps_type;
++    union
++    {
++      struct {
++          ELAN_CAPABILITY        *cap;
++          E4_VirtualProcessEntry *routes;
++      } p2p;
++#define vps_p2p_cap   vps_u.p2p.cap
++#define vps_p2p_routes  vps_u.p2p.routes
++
++      struct {
++          unsigned short lowvp;
++          unsigned short highvp;
++      } bcast;
++#define vps_bcast_lowvp               vps_u.bcast.lowvp
++#define vps_bcast_highvp      vps_u.bcast.highvp
++    } vps_u;
++} USER_VPSEG;
++
++/* values for vps_type */
++#define USER_VPSEG_P2P                0
++#define USER_VPSEG_BCAST      1
++
++typedef struct user_cq
++{
++    struct list_head ucq_link;
++
++    ELAN4_CQ      *ucq_cq;                                    /* the real command queue */
++
++    unsigned char    ucq_state;                                       /* command queue state */
++    unsigned char    ucq_errored;                             /* command queue has errored */
++    unsigned char    ucq_flags;                                       /* flags */
++    ELAN4_CPROC_TRAP ucq_trap;                                        /* trap state */
++
++    atomic_t       ucq_ref;                                   /* # references to this cq (mmaps) */
++} USER_CQ;
++
++/* values for ucq_state */
++#define UCQ_RUNNING                    0                      /* command queue is running */
++#define UCQ_TRAPPED                    1                      /* command queue has trapped */
++#define UCQ_NEEDS_RESTART                2                    /* command queue has trapped, and needs restarting */
++#define UCQ_STOPPED                    3                      /* command queue has trapped, and delivered to user */
++
++/* values for ucq_flags */
++#define UCQ_SYSTEM            (1 << 0)
++#define UCQ_REORDER           (1 << 1)
++
++extern int num_fault_save;
++extern int min_fault_pages;
++extern int max_fault_pages;
++
++typedef struct fault_save
++{
++    struct fault_save           *next;
++    E4_Addr                      addr;
++    E4_uint32                    count;
++} FAULT_SAVE;
++
++typedef struct user_iproc_trap
++{
++    unsigned char     ut_state;
++    ELAN4_IPROC_TRAP  ut_trap;
++} USER_IPROC_TRAP;
++
++/* values for ut_state */
++#define UTS_IPROC_RUNNING                     0
++#define UTS_IPROC_TRAPPED                     1
++#define UTS_IPROC_RESOLVING                   2
++#define UTS_IPROC_EXECUTE_PACKET              3
++#define UTS_IPROC_EXECUTING                   4
++#define UTS_IPROC_NETWORK_ERROR                       5
++#define UTS_IPROC_STOPPED                     6
++
++typedef struct user_ctxt_entry
++{
++    struct list_head   cent_link;                                     /* entry chained on context */
++    ELAN_CAPABILITY   *cent_cap;                                      /* capability we attached with */
++} USER_CTXT_ENTRY;
++
++typedef struct user_ctxt
++{
++    ELAN4_CTXT         uctx_ctxt;                             /* is also an elan context */
++
++    spinlock_t               uctx_spinlock;                           /* spinlock for items used with interrupt handler */
++    kcondvar_t               uctx_wait;                               /* place to sleep (traphandler/swapout/swapin/neterr fixup) */
++
++    unsigned         uctx_status;                             /* status                               (uctx_spinlock) */
++
++    pid_t            uctx_trap_pid;                           /* pid to deliver signals to on trap */
++    int                      uctx_trap_signo;                         /* signal number to deliver */
++    unsigned         uctx_trap_state;                         /* state of trap handling code */
++    unsigned         uctx_trap_count;                         /* count of "thread" in user_trap_handler() */
++
++    unsigned         uctx_int_count;                          /* # interrupts since last zeroed */
++    unsigned long      uctx_int_start;                                /* tick when int_count last zeroed */
++    unsigned long      uctx_int_delay;                                /* # ticks to delay next wakeup */
++    struct timer_list  uctx_int_timer;                                /* and timer to use to delay signal */
++
++    struct timer_list  uctx_neterr_timer;                     /* network error timer */
++
++    struct list_head   uctx_vpseg_list;                               /* list of vp segments we've got */
++    kmutex_t           uctx_vpseg_lock;                               /*   and lock to protect it. */
++    ELAN4_ROUTE_TABLE *uctx_routetable;                               /* our virtual process table */
++    ELAN_POSITION      uctx_position;                         /* position in network */
++
++    struct list_head   uctx_cent_list;                                /* list of attached network contexts */
++
++    USER_CQ         *uctx_ddcq;                               /* command queue for re-issueing traps */
++    E4_uint64        uctx_ddcq_insertcnt;                     /* # dwords inserted into command queue */
++    E4_uint64          uctx_ddcq_completed;                   /* last "completed" write was here */
++    int                      uctx_ddcq_intr;                          /* count of outstanding ddcq interrupts */
++
++    ELAN4_HALTOP       uctx_haltop;                           /* halt operation for flushing */
++    ELAN4_DMA_FLUSHOP  uctx_dma_flushop;                      /* flush operation for flushing dma runqueue */
++
++    INTCOOKIE_TABLE   *uctx_intcookie_table;                  /* table of interrupt cookies (shared with other uctxs for this task) */
++
++    kmutex_t         uctx_cqlock;                             /* lock for create/destory cqs */
++    struct list_head   uctx_cqlist;                           /* list of command queues               (uctx_cqlock,uctx_spinlock) */
++
++    ELAN4_DPROC_TRAP  *uctx_dprocTraps;                               /* queue of dproc traps to resolve/reissue */
++    RING_QUEUE               uctx_dprocTrapQ;
++
++    ELAN4_TPROC_TRAP  *uctx_tprocTraps;                               /* queue of tproc traps to resolve/reissue */
++    RING_QUEUE         uctx_tprocTrapQ;
++
++    ELAN4_EPROC_TRAP  *uctx_eprocTraps;                               /* queue of eproc traps to resolve */
++    RING_QUEUE               uctx_eprocTrapQ;
++
++    USER_IPROC_TRAP    uctx_iprocTrap[2];                     /* input trap state, 1 per virtual channel */
++
++    E4_DMA          *uctx_dmas;                               /* queue of dmas to restart */
++    RING_QUEUE         uctx_dmaQ;
++    
++    E4_ThreadRegs     *uctx_threads;                          /* queue of threads to restart */
++    RING_QUEUE         uctx_threadQ;
++
++    ELAN4_NETERR_MSG  *uctx_msgs;                             /* queue of neterr messages */
++    RING_QUEUE               uctx_msgQ;
++    kmutex_t         uctx_rgnmutex;                           /* lock for create/destroy regions */
++    spinlock_t               uctx_rgnlock;                            /* spinlock to protect linked lists */
++    USER_RGN        *uctx_mrgns;                              /* Doubly linked list of memory regions (uctx_rgnlock) */
++    USER_RGN        *uctx_mtail;                              /* Last memory region on list           (uctx_rgnlock) */
++    USER_RGN        *uctx_mrgnlast;                           /* Last region 'hit'                    (uctx_rgnlock) */
++
++    USER_RGN        *uctx_ergns;                              /* Doubly linked list of memory regions (uctx_rgnlock) */
++    USER_RGN        *uctx_etail;                              /* Last memory region on list           (uctx_rgnlock) */
++    USER_RGN        *uctx_ergnlast;                           /* Last region 'hit'                    (uctx_rgnlock) */
++
++    ELAN4_USER_PAGE   *uctx_upage;                            /* kernel page shared with user */
++    sdramaddr_t              uctx_trampoline;                         /* sdram page for tproc trampoline */
++
++    E4_Addr          uctx_upage_addr;                         /*   elan addr page mapped into */
++    E4_Addr          uctx_trestart_addr;                      /* address of thread restart code */
++    FAULT_SAVE         *uctx_faults;
++    FAULT_SAVE         *uctx_fault_list;
++    int                 uctx_num_fault_save;
++    spinlock_t          uctx_fault_lock;
++} USER_CTXT;
++
++/* bit values for uctx_status */
++#define UCTX_EXITING                          (1 << 0)                /* context is exiting. */
++#define UCTX_USER_FILTERING                   (1 << 1)                /* user requested context filter */
++#define UCTX_USER_STOPPED                     (1 << 2)                /* user requested stop */
++
++#define UCTX_SWAPPING                         (1 << 3)                /* context is swapping out */
++#define UCTX_SWAPPED                          (1 << 4)                /* context is swapped out */
++
++#define UCTX_STOPPING                         (1 << 5)                /* stopping elan from running this context */
++#define UCTX_STOPPED                          (1 << 6)                /* elan no longer running this context */
++
++#define UCTX_EPROC_QUEUE_FULL                 (1 << 7)                /* reasons for stopping running */
++#define UCTX_DPROC_QUEUE_FULL                 (1 << 8)
++#define UCTX_TPROC_QUEUE_FULL                 (1 << 9)
++#define UCTX_IPROC_CH0_TRAPPED                        (1 << 10)
++#define UCTX_IPROC_CH1_TRAPPED                        (1 << 11)
++
++#define UCTX_NETERR_TIMER                     (1 << 12)
++#define UCTX_NETERR_FIXUP                     (1 << 13)
++
++#define UCTX_EPROC_QUEUE_OVERFLOW             (1 << 14)
++#define UCTX_DPROC_QUEUE_OVERFLOW             (1 << 15)
++#define UCTX_TPROC_QUEUE_OVERFLOW             (1 << 16)
++
++#define UCTX_EPROC_QUEUE_ERROR                        (1 << 17)
++#define UCTX_DPROC_QUEUE_ERROR                        (1 << 18)
++#define UCTX_TPROC_QUEUE_ERROR                        (1 << 19)
++
++#define UCTX_STOPPED_REASONS                  (UCTX_EPROC_QUEUE_FULL | UCTX_DPROC_QUEUE_FULL | UCTX_TPROC_QUEUE_FULL)
++#define UCTX_SWAPPED_REASONS                  (UCTX_EXITING | UCTX_USER_STOPPED | UCTX_NETERR_FIXUP)
++#define UCTX_NACKING_REASONS                  (UCTX_USER_FILTERING | UCTX_IPROC_CH0_TRAPPED | UCTX_IPROC_CH1_TRAPPED)
++
++#define UCTX_OVERFLOW_REASONS                 (UCTX_EPROC_QUEUE_OVERFLOW | UCTX_DPROC_QUEUE_OVERFLOW | UCTX_TPROC_QUEUE_OVERFLOW)
++#define UCTX_ERROR_REASONS                    (UCTX_EPROC_QUEUE_ERROR | UCTX_DPROC_QUEUE_ERROR | UCTX_TPROC_QUEUE_ERROR)
++
++#define UCTX_RUNNABLE(uctx)                   (((uctx)->uctx_status & (UCTX_SWAPPED_REASONS | UCTX_STOPPED_REASONS)) == 0)
++#define UCTX_NACKING(uctx)                    (((uctx)->uctx_status & (UCTX_SWAPPED_REASONS | UCTX_STOPPED_REASONS | UCTX_NACKING_REASONS)) != 0)
++
++/* values for uctx_trap_signalled */
++#define UCTX_TRAP_IDLE                                0
++#define UCTX_TRAP_SLEEPING                    1
++#define UCTX_TRAP_SIGNALLED                   2
++#define UCTX_TRAP_ACTIVE                      3
++
++extern int        user_p2p_route_options;
++extern int        user_bcast_route_options;
++extern int      user_dproc_retry_count;
++extern int      user_cproc_retry_count;
++
++extern USER_CTXT *user_alloc (ELAN4_DEV *dev);
++extern void       user_free (USER_CTXT *uctx);
++extern void       user_swapout (USER_CTXT *uctx, unsigned reason);
++extern void       user_swapin (USER_CTXT *uctx, unsigned reason);
++extern int        user_attach (USER_CTXT *uctx, ELAN_CAPABILITY *cap);
++extern void       user_detach (USER_CTXT *uctx, ELAN_CAPABILITY *cap);
++extern void       user_block_inputter (USER_CTXT *uctx, unsigned blocked);
++extern int        user_alloc_trap_queues (USER_CTXT *uctx, unsigned ndproc_traps, unsigned neproc_traps, 
++                                        unsigned ntproc_traps, unsigned nthreads, unsigned ndmas);
++
++extern int        user_add_p2pvp (USER_CTXT *uctx, unsigned process, ELAN_CAPABILITY *cap);
++extern int        user_add_bcastvp (USER_CTXT *uctx, unsigned process, unsigned lowvp, unsigned highvp);
++extern int        user_removevp (USER_CTXT *uctx, unsigned process);
++
++extern int        user_set_route (USER_CTXT *uctx, unsigned process, E4_VirtualProcessEntry *route);
++extern int        user_reset_route (USER_CTXT *uctx, unsigned process);
++extern int        user_get_route (USER_CTXT *uctx, unsigned process, E4_VirtualProcessEntry *route);
++extern int        user_check_route (USER_CTXT *uctx, unsigned process, E4_VirtualProcessEntry *route, unsigned *error);
++extern int      user_send_neterr_msg (USER_CTXT *uctx, unsigned int vp, unsigned int nctx, unsigned int retries, ELAN4_NETERR_MSG *msg);
++extern int        user_neterr_sten (USER_CTXT *uctx, unsigned int vp, E4_uint64 cookie, int waitforeop);
++extern int        user_neterr_dma (USER_CTXT *uctx, unsigned int vp, E4_uint64 cookie, int waitforeop);
++
++extern int        user_resume_eproc_trap (USER_CTXT *uctx, E4_Addr addr);
++extern int        user_resume_cproc_trap (USER_CTXT *uctx, unsigned indx);
++extern int        user_resume_dproc_trap (USER_CTXT *uctx, E4_DMA *dma);
++extern int        user_resume_tproc_trap (USER_CTXT *uctx, E4_ThreadRegs *regs);
++extern int        user_resume_iproc_trap (USER_CTXT *uctx, unsigned channel, unsigned trans,
++                                        E4_IprocTrapHeader *hdrp, E4_IprocTrapData *datap);
++
++extern int        user_trap_handler (USER_CTXT *uctx, ELAN4_USER_TRAP *utrapp, int nticks);
++extern USER_CQ   *user_findcq (USER_CTXT *uctx, unsigned num);
++extern USER_CQ   *user_alloccq (USER_CTXT *uctx, unsigned size, unsigned perm, unsigned flags);
++extern void       user_freecq (USER_CTXT *uctx, USER_CQ *cq);
++extern void       user_dropcq (USER_CTXT *uctx, USER_CQ *cq);
++
++/* user_osdep.c */
++extern int        user_load_range (USER_CTXT *uctx, E4_Addr addr, unsigned long nbytes, E4_uint32 fsr);
++extern void       user_update_main (USER_CTXT *uctx, struct mm_struct *mm, unsigned long start, unsigned long len);
++extern void       user_unload_main (USER_CTXT *uctx, unsigned long start, unsigned long len);
++
++
++/* regions.c */
++extern USER_RGN  *user_findrgn_elan (USER_CTXT *uctx, E4_Addr addr, int tail);
++extern USER_RGN  *user_findrgn_main (USER_CTXT *uctx, virtaddr_t addr, int tail);
++extern USER_RGN  *user_rgnat_elan (USER_CTXT *uctx, E4_Addr addr);
++extern USER_RGN  *user_rgnat_main (USER_CTXT *uctx, virtaddr_t addr);
++extern int        user_setperm (USER_CTXT *uctx, virtaddr_t maddr, E4_Addr eaddr, unsigned long len, unsigned perm);
++extern void       user_clrperm (USER_CTXT *uctx, E4_Addr addr, unsigned long len);
++extern int        user_checkperm (USER_CTXT *uctx, E4_Addr raddr, unsigned long rsize, unsigned access);
++extern virtaddr_t user_elan2main (USER_CTXT *uctx, E4_Addr addr);
++extern E4_Addr    user_main2elan (USER_CTXT *uctx, virtaddr_t addr);
++extern void       user_preload_main (USER_CTXT *uctx, virtaddr_t addr, unsigned long len);
++extern void       user_freergns (USER_CTXT *uctx);
++
++/* user_ddcq.c */
++extern int        user_ddcq_check (USER_CTXT *uctx, unsigned num);
++extern int        user_ddcq_flush (USER_CTXT *uctx);
++extern void       user_ddcq_intr (USER_CTXT *uctx);
++extern void       user_ddcq_write_dword (USER_CTXT *uctx, E4_Addr addr, E4_uint64 value);
++extern void       user_ddcq_interrupt (USER_CTXT *uctx, E4_uint64 cookie);
++extern void       user_ddcq_run_dma (USER_CTXT *uctx, E4_DMA *dma);
++extern void       user_ddcq_run_thread (USER_CTXT *uctx, E4_ThreadRegs *regs);
++extern void       user_ddcq_setevent (USER_CTXT *uctx, E4_Addr addr);
++extern void       user_ddcq_seteventn (USER_CTXT *uctx, E4_Addr addr, E4_uint32 count);
++extern void       user_ddcq_waitevent (USER_CTXT *uctx, E4_Addr addr, E4_uint64 CountAndType, E4_uint64 Param0, E4_uint64 Param1);
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++#endif /* __ELAN4_USER_H */
+Index: linux-2.4.21/include/elan4/userregs.h
+===================================================================
+--- linux-2.4.21.orig/include/elan4/userregs.h 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan4/userregs.h      2005-06-01 23:12:54.746416760 -0400
+@@ -0,0 +1,383 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN4_USERREGS_H
++#define __ELAN4_USERREGS_H
++
++#ident "$Id: userregs.h,v 1.14.2.1 2004/10/07 10:57:40 addy Exp $"
++/*      $Source: /cvs/master/quadrics/elan4hdr/userregs.h,v $*/
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++/*
++ * Statistic control reg values
++ * Each 4-bit nibble of the control word specifies what statistic
++ * is to be recorded in each of the 8 statistic counters
++ */
++#define COUNT_REG0_SHIFT   32ull
++#define COUNT_REG1_SHIFT   36ull
++#define COUNT_REG2_SHIFT   40ull
++#define COUNT_REG3_SHIFT   44ull
++#define COUNT_REG4_SHIFT   48ull
++#define COUNT_REG5_SHIFT   52ull
++#define COUNT_REG6_SHIFT   56ull
++#define COUNT_REG7_SHIFT   60ull
++
++
++/* Count reg 0 */
++#define STC_INPUT_NON_WRITE_BLOCKS    (0x0ull << COUNT_REG0_SHIFT)
++#define STP_DMA_EOP_WAIT_ACK          (0x1ull << COUNT_REG0_SHIFT)
++#define STP_TPROC_RUNNING             (0x2ull << COUNT_REG0_SHIFT)
++#define STC_STEN_PKTS_OPEN              (0x3ull << COUNT_REG0_SHIFT)
++#define STP_CPROC_HOLDS_FFU_DP        (0x4ull << COUNT_REG0_SHIFT)
++#define STC_TLB_TABLE_WALKS             (0x5ull << COUNT_REG0_SHIFT)
++#define STC_CACHE_HITS                  (0x6ull << COUNT_REG0_SHIFT)
++#define STC_PCI_SLAVE_READS             (0x7ull << COUNT_REG0_SHIFT)
++#define STP_PCI_WAITING_FOR_GNT         (0x8ull << COUNT_REG0_SHIFT)
++#define STP_SYS_CLOCK_RATE0           (0xfull << COUNT_REG0_SHIFT)
++
++#define STATS_REG0_NAMES {                     \
++          "STC_INPUT_NON_WRITE_BLOCKS",        \
++          "STP_DMA_EOP_WAIT_ACK",              \
++          "STP_TPROC_RUNNING",                 \
++          "STC_STEN_PKTS_OPEN",                \
++          "STP_CPROC_HOLDS_FFU_DP",            \
++          "STC_TLB_TABLE_WALKS",               \
++          "STC_CACHE_HITS",                    \
++          "STC_PCI_SLAVE_READS",               \
++          "STP_PCI_WAITING_FOR_GNT",           \
++          "STP_SYS_CLOCK_RATE0"                \
++}
++
++/* Count reg 1 */
++#define STC_INPUT_WRITE_BLOCKS        (0x0ull << COUNT_REG1_SHIFT)
++#define STP_DMA_DATA_TRANSMITTING     (0x1ull << COUNT_REG1_SHIFT)
++#define STC_CPROC_VALUES_EXE          (0x2ull << COUNT_REG1_SHIFT)
++#define STC_STEN_TRANS_SENT           (0x3ull << COUNT_REG1_SHIFT)
++#define STP_TPROC_DQ_HOLDS_FFU_DP     (0x4ull << COUNT_REG1_SHIFT)
++#define STC_TPROC_TLB_HITS            (0x5ull << COUNT_REG1_SHIFT)
++#define STC_CACHE_ALLOC_MISSES        (0x6ull << COUNT_REG1_SHIFT)
++#define STP_PCI_MASTER_READ_WAITING   (0x7ull << COUNT_REG1_SHIFT)
++#define STP_PCI_WAITING_FOR_DEVSEL      (0x8ull << COUNT_REG1_SHIFT)
++#define STP_SYS_CLOCK_RATE1           (0xfull << COUNT_REG1_SHIFT)
++
++#define STATS_REG1_NAMES {                   \
++          "STC_INPUT_WRITE_BLOCKS",            \
++          "STP_DMA_DATA_TRANSMITTING",         \
++          "STC_CPROC_VALUES_EXE",              \
++          "STC_STEN_TRANS_SENT",               \
++          "STP_TPROC_DQ_HOLDS_FFU_DP",         \
++          "STC_TPROC_TLB_HITS",                \
++          "STC_CACHE_ALLOC_MISSES",            \
++          "STP_PCI_MASTER_READ_WAITING",       \
++          "STP_PCI_WAITING_FOR_DEVSEL",        \
++          "STP_SYS_CLOCK_RATE1"                \
++}
++
++/* Count reg 2 */
++#define STC_INPUT_PKTS                (0x0ull << COUNT_REG2_SHIFT)
++#define STP_DMA_WAITING_MEM           (0x1ull << COUNT_REG2_SHIFT)
++#define STC_CPROC_TRANSFERS             (0x2ull << COUNT_REG2_SHIFT)
++#define STP_STEN_WAIT_NETWORK_BUSY    (0x3ull << COUNT_REG2_SHIFT)
++#define STP_IPROC_HOLDS_FFU_DP        (0x4ull << COUNT_REG2_SHIFT)
++#define STC_UNITS_TLB_HITS            (0x5ull << COUNT_REG2_SHIFT)
++#define STC_CACHE_NON_ALLOC_MISSES      (0x6ull << COUNT_REG2_SHIFT)
++#define STP_PCI_MASTER_WRITE_WAITING  (0x7ull << COUNT_REG2_SHIFT)
++#define STC_PCI_OUT_OF_ORDER_SPLIT_COMP (0x8ull << COUNT_REG2_SHIFT)
++#define STP_SYS_CLOCK_RATE2           (0xfull << COUNT_REG2_SHIFT)
++
++#define STATS_REG2_NAMES {                   \
++          "STC_INPUT_PKTS",                    \
++          "STP_DMA_WAITING_MEM",               \
++          "STC_CPROC_TRANSFERS",               \
++          "STP_STEN_WAIT_NETWORK_BUSY",        \
++          "STP_IPROC_HOLDS_FFU_DP",            \
++          "STC_UNITS_TLB_HITS",                \
++          "STC_CACHE_NON_ALLOC_MISSES",        \
++          "STP_PCI_MASTER_WRITE_WAITING",      \
++          "STC_PCI_OUT_OF_ORDER_SPLIT_COMP",   \
++          "STP_SYS_CLOCK_RATE2"                \
++}
++
++/* Count reg 3 */
++#define STC_INPUT_PKTS_REJECTED         (0x0ull << COUNT_REG3_SHIFT)
++#define STP_DMA_WAIT_NETWORK_BUSY       (0x1ull << COUNT_REG3_SHIFT)
++#define STC_CPROC_PREFETCH_SDRAM        (0x2ull << COUNT_REG3_SHIFT)
++#define STP_STEN_BLOCKED_ACKS_OR_VC     (0x3ull << COUNT_REG3_SHIFT)
++#define STP_EPROC_HOLDS_FFU_DP          (0x4ull << COUNT_REG3_SHIFT)
++#define STP_TPROC_BLOCKED_MEMSYS        (0x5ull << COUNT_REG3_SHIFT)
++#define STC_CACHE_WRITE_BACKS           (0x6ull << COUNT_REG3_SHIFT)
++#define STP_PCI_SLAVE_READ_WAITING      (0x7ull << COUNT_REG3_SHIFT)
++#define STP_PCI_IDLE_CYCLES           (0x8ull << COUNT_REG3_SHIFT)
++#define STP_SYS_CLOCK_RATE3           (0xfull << COUNT_REG3_SHIFT)
++
++#define STATS_REG3_NAMES {                   \
++          "STC_INPUT_PKTS_REJECTED",           \
++          "STP_DMA_WAIT_NETWORK_BUSY",         \
++          "STC_CPROC_PREFETCH_SDRAM",          \
++          "STP_STEN_BLOCKED_ACKS_OR_VC",       \
++          "STP_EPROC_HOLDS_FFU_DP",            \
++          "STP_TPROC_BLOCKED_MEMSYS",          \
++          "STC_CACHE_WRITE_BACKS",             \
++          "STP_PCI_SLAVE_READ_WAITING",        \
++          "STP_PCI_IDLE_CYCLES",               \
++          "STP_SYS_CLOCK_RATE3"                \
++}
++
++/* Count reg 4 */
++#define STP_INPUT_DATA_TRANSMITTING   (0x0ull << COUNT_REG4_SHIFT)
++#define STC_DMA_PKTS_ACCEPTED         (0x1ull << COUNT_REG4_SHIFT)
++#define STC_CPROC_FLUSH_REQ_SDRAM     (0x2ull << COUNT_REG4_SHIFT)
++#define STP_STEN_EOP_WAIT_ACK         (0x3ull << COUNT_REG4_SHIFT)
++#define STP_DMA_HOLDS_FFU_DP          (0x4ull << COUNT_REG4_SHIFT)
++#define STP_UNIT_BLOCKED_MEMSYS       (0x5ull << COUNT_REG4_SHIFT)
++#define STC_PCI_MASTER_READS          (0x6ull << COUNT_REG4_SHIFT)
++#define STP_PCI_SLAVE_WRITE_WAITING   (0x7ull << COUNT_REG4_SHIFT)
++#define STC_INPUT_PACKETS_DISCARDED   (0x8ull << COUNT_REG4_SHIFT)
++#define STP_SYS_CLOCK_RATE4           (0xfull << COUNT_REG4_SHIFT)
++
++#define STATS_REG4_NAMES {                   \
++          "STP_INPUT_DATA_TRANSMITTING",       \
++          "STC_DMA_PKTS_ACCEPTED",             \
++          "STC_CPROC_FLUSH_REQ_SDRAM",         \
++          "STP_STEN_EOP_WAIT_ACK",             \
++          "STP_DMA_HOLDS_FFU_DP",              \
++          "STP_UNIT_BLOCKED_MEMSYS",           \
++          "STC_PCI_MASTER_READS",              \
++          "STP_PCI_SLAVE_WRITE_WAITING",       \
++          "STC_INPUT_PACKETS_DISCARDED",       \
++          "STP_SYS_CLOCK_RATE4"                \
++}
++
++/* Count reg 5 */
++#define STP_INPUT_WAITING_NETWORK_DATA  (0x0ull << COUNT_REG5_SHIFT)
++#define STC_DMA_PKTS_REJECTED           (0x1ull << COUNT_REG5_SHIFT)
++#define STC_CPROC_INSERT_CACHE_MISSES   (0x2ull << COUNT_REG5_SHIFT)
++#define STP_STEN_TRANSMITTING_DATA      (0x3ull << COUNT_REG5_SHIFT)
++#define FFU_BLOCKED_DIFF_FFU_PROC       (0x4ull << COUNT_REG5_SHIFT)
++#define STP_TABLE_WALKS_BLOCKED_MEMSYS  (0x5ull << COUNT_REG5_SHIFT)
++#define STC_PCI_MASTER_WRITES           (0x6ull << COUNT_REG5_SHIFT)
++#define STP_PCI_MASTER_HOLDS_BUS        (0x7ull << COUNT_REG5_SHIFT)
++#define STC_PCI_NO_SPLIT_COMPS                (0x8ull << COUNT_REG5_SHIFT)
++#define STP_SYS_CLOCK_RATE5           (0xfull << COUNT_REG5_SHIFT)
++
++#define STATS_REG5_NAMES {                   \
++          "STP_INPUT_WAITING_NETWORK_DATA",    \
++          "STC_DMA_PKTS_REJECTED",             \
++          "STC_CPROC_INSERT_CACHE_MISSES",     \
++          "STP_STEN_TRANSMITTING_DATA",        \
++          "FFU_BLOCKED_DIFF_FFU_PROC",         \
++          "STP_TABLE_WALKS_BLOCKED_MEMSYS",    \
++          "STC_PCI_MASTER_WRITES",             \
++          "STP_PCI_MASTER_HOLDS_BUS",          \
++          "STC_PCI_NO_SPLIT_COMPS",            \
++          "STP_SYS_CLOCK_RATE5"                \
++}
++
++/* Count reg 6 */
++#define STP_INPUT_BLOCKED_WAITING_TRANS (0x0ull << COUNT_REG6_SHIFT)
++#define STP_TPROC_INST_STALL                  (0x1ull << COUNT_REG6_SHIFT)
++#define STP_CPROC_WAITING_DESCHED             (0x2ull << COUNT_REG6_SHIFT)
++#define STP_STEN_PKT_OPEN_WAITING_DATA        (0x3ull << COUNT_REG6_SHIFT)
++#define STP_TLB_HASH_TABLE_ACCESSES           (0x4ull << COUNT_REG6_SHIFT)
++#define STP_PCI_SLAVE_BLOCKED_MEMSYS          (0x5ull << COUNT_REG6_SHIFT)
++#define STP_PCI_TRANSFERRING_DATA       (0x6ull << COUNT_REG6_SHIFT)
++#define STP_PCI_MASTER_WAITING_BUS      (0x7ull << COUNT_REG6_SHIFT)
++#define STP_PCI_READ_LATENCY          (0x8ull << COUNT_REG6_SHIFT)
++#define STP_SYS_CLOCK_RATE6           (0xfull << COUNT_REG6_SHIFT)
++
++#define STATS_REG6_NAMES {                   \
++          "STP_INPUT_BLOCKED_WAITING_TRANS",   \
++          "STP_TPROC_INST_STALL",              \
++          "STP_CPROC_WAITING_DESCHED",         \
++          "STP_STEN_PKT_OPEN_WAITING_DATA",    \
++          "STP_TLB_HASH_TABLE_ACCESSES",       \
++          "STP_PCI_SLAVE_BLOCKED_MEMSYS",      \
++          "STP_PCI_TRANSFERRING_DATA",         \
++          "STP_PCI_MASTER_WAITING_BUS",        \
++          "STP_PCI_READ_LATENCY",              \
++          "STP_SYS_CLOCK_RATE6"                \
++}
++
++/* Count reg 7 */
++#define STC_INPUT_CTX_FILTER_FILL       (0x0ull << COUNT_REG7_SHIFT)  
++#define STP_TPROC_LOAD_STORE_STALL      (0x1ull << COUNT_REG7_SHIFT)
++#define STC_CPROC_TIMEOUTS              (0x2ull << COUNT_REG7_SHIFT)
++#define STP_STEN_BLOCKED_NETWORK        (0x3ull << COUNT_REG7_SHIFT)
++#define STP_TLB_CHAIN_ACCESSES          (0x4ull << COUNT_REG7_SHIFT)
++#define STP_CPROC_SCHED_BLOCKED_MEMSYS  (0x5ull << COUNT_REG7_SHIFT)
++#define STC_PCI_SLAVE_WRITES            (0x6ull << COUNT_REG7_SHIFT)
++#define STC_PCI_DISCONNECTS_RETRIES     (0x7ull << COUNT_REG7_SHIFT)
++#define STC_RING_OSCILLATOR           (0x8ull << COUNT_REG7_SHIFT)
++#define STP_SYS_CLOCK_RATE7           (0xfull << COUNT_REG7_SHIFT)
++
++#define STATS_REG7_NAMES {                   \
++          "STC_INPUT_CTX_FILTER_FILL",         \
++          "STP_TPROC_LOAD_STORE_STALL",        \
++          "STC_CPROC_TIMEOUTS",                \
++          "STP_STEN_BLOCKED_NETWORK",          \
++          "STP_TLB_CHAIN_ACCESSES",            \
++          "STP_CPROC_SCHED_BLOCKED_MEMSYS",    \
++          "STC_PCI_SLAVE_WRITES",              \
++          "STC_PCI_DISCONNECTS_RETRIES",       \
++          "STC_RING_OSCILLATOR",               \
++          "STP_SYS_CLOCK_RATE7"                \
++}
++
++#define STATS_REG_NAMES { \
++    STATS_REG0_NAMES, \
++    STATS_REG1_NAMES, \
++    STATS_REG2_NAMES, \
++    STATS_REG3_NAMES, \
++    STATS_REG4_NAMES, \
++    STATS_REG5_NAMES, \
++    STATS_REG6_NAMES, \
++    STATS_REG7_NAMES, \
++}
++
++
++#define INPUT_PERF_STATS        (STC_INPUT_NON_WRITE_BLOCKS | STC_INPUT_WRITE_BLOCKS |              \
++                               STC_INPUT_PKTS | STC_INPUT_PKTS_REJECTED |                         \
++                                 STC_INPUT_CTX_FILTER_FILL | STP_INPUT_DATA_TRANSMITTING |           \
++                               STP_INPUT_WAITING_NETWORK_DATA | STP_INPUT_BLOCKED_WAITING_TRANS | STC_INPUT_PACKETS_DISCARDED) 
++
++#define DMA_PERF_STATS          (STC_DMA_PKTS_ACCEPTED | STC_DMA_PKTS_REJECTED |                    \
++                                 STP_DMA_EOP_WAIT_ACK | STP_DMA_DATA_TRANSMITTING |                 \
++                               STP_DMA_WAITING_MEM | STP_DMA_WAIT_NETWORK_BUSY)                 
++
++
++#define TPROC_PERF_STATS        (STP_TPROC_RUNNING | STP_TPROC_INST_STALL |                         \
++                                 STP_TPROC_LOAD_STORE_STALL)
++
++#define CPROC_PERF_STATS        (STC_CPROC_VALUES_EXE | STC_CPROC_TRANSFERS |                       \
++                               STC_CPROC_PREFETCH_SDRAM | STC_CPROC_FLUSH_REQ_SDRAM |             \
++                               STC_CPROC_INSERT_CACHE_MISSES | STP_CPROC_WAITING_DESCHED |        \
++                               STC_CPROC_TIMEOUTS)
++
++#define STEN_PERF_STATS         (STC_STEN_PKTS_OPEN | STC_STEN_TRANS_SENT |                         \
++                               STP_STEN_WAIT_NETWORK_BUSY | STP_STEN_BLOCKED_ACKS_OR_VC |         \
++                               STP_STEN_EOP_WAIT_ACK | STP_STEN_TRANSMITTING_DATA |               \
++                               STP_STEN_PKT_OPEN_WAITING_DATA | STP_STEN_BLOCKED_NETWORK)
++
++#define FFU_PREF_STATS          (STP_CPROC_HOLDS_FFU_DP | STP_TPROC_DQ_HOLDS_FFU_DP |               \
++                               STP_IPROC_HOLDS_FFU_DP | STP_EPROC_HOLDS_FFU_DP |                  \
++                               STP_DMA_HOLDS_FFU_DP | FFU_BLOCKED_DIFF_FFU_PROC)
++
++#define TABLE_WALK_PERF_STATS   (STC_TPROC_TLB_HITS | STC_UNITS_TLB_HITS |                          \
++                               STP_TLB_HASH_TABLE_ACCESSES | STP_TLB_CHAIN_ACCESSES |             \
++                               STC_TLB_TABLE_WALKS)
++
++#define ADDRESS_ARB_PERF_STATS  (STP_UNIT_BLOCKED_MEMSYS | STP_TPROC_BLOCKED_MEMSYS |               \
++                               STP_TABLE_WALKS_BLOCKED_MEMSYS | STP_CPROC_SCHED_BLOCKED_MEMSYS |  \
++                               STP_PCI_SLAVE_BLOCKED_MEMSYS)
++
++#define CACHE_PERF_STATS        (STC_CACHE_HITS | STC_CACHE_ALLOC_MISSES |                          \
++                               STC_CACHE_NON_ALLOC_MISSES | STC_CACHE_WRITE_BACKS)
++
++
++#define PCI_PERF_STATS          (STC_PCI_SLAVE_READS | STP_PCI_MASTER_READ_WAITING |                \
++                                 STP_PCI_MASTER_WRITE_WAITING | STP_PCI_SLAVE_READ_WAITING |        \
++                                 STP_PCI_SLAVE_WRITE_WAITING | STC_PCI_MASTER_WRITES |              \
++                                 STP_PCI_TRANSFERRING_DATA | STC_PCI_SLAVE_WRITES)
++
++#define PCIBUS_PERF_STATS       (STP_PCI_WAITING_FOR_GNT | STP_PCI_WAITING_FOR_DEVSEL |                   \
++                               STC_PCI_OUT_OF_ORDER_SPLIT_COMP | STP_PCI_IDLE_CYCLES |            \
++                               STC_PCI_MASTER_READS | STP_PCI_MASTER_HOLDS_BUS |                  \
++                               STP_PCI_MASTER_WAITING_BUS | STC_PCI_DISCONNECTS_RETRIES)
++
++                               
++    extern const char *elan_stats_names[8][10];
++
++#define ELAN_STATS_NAME(COUNT, CONTROL) (elan_stats_names[(COUNT)][(CONTROL) & 7])
++
++    typedef volatile union e4_StatsControl
++    {
++      E4_uint64 StatsControl;
++      struct
++      {
++#if (BYTE_ORDER == LITTLE_ENDIAN) || defined(__LITTLE_ENDIAN__)
++          E4_uint32 StatCont0:4;
++          E4_uint32 StatCont1:4;
++          E4_uint32 StatCont2:4;
++          E4_uint32 StatCont3:4;
++          E4_uint32 StatCont4:4;
++          E4_uint32 StatCont5:4;
++          E4_uint32 StatCont6:4;
++          E4_uint32 StatCont7:4;
++#else
++          E4_uint32 StatCont7:4;
++          E4_uint32 StatCont6:4;
++          E4_uint32 StatCont5:4;
++
++          E4_uint32 StatCont4:4;
++          E4_uint32 StatCont3:4;
++          E4_uint32 StatCont2:4;
++          E4_uint32 StatCont1:4;
++          E4_uint32 StatCont0:4;
++#endif
++          E4_uint32 pad;
++      } s;
++    } E4_StatsControl;
++
++typedef volatile union e4_StatsCount
++{
++   E4_uint64    ClockStat; 
++   struct
++   {
++       E4_uint32 ClockLSW;    /* read only */
++       E4_uint32 StatsCount;
++   } s;
++} E4_StatsCount;
++
++typedef volatile union e4_clock
++{
++   E4_uint64 NanoSecClock;
++   struct
++   {
++      E4_uint32 ClockLSW;
++      E4_uint32 ClockMSW;
++   } s;
++} E4_Clock;
++#define E4_TIME( X ) ((X).NanoSecClock)
++
++#define ELAN4_COMMS_CLOCK_FREQUENCY   660             /* In Mhz. This is half the bit rate. */
++#define ELAN4_CLOCK_ADD_VALUE         200             /* For 200ns increment rate */
++#define ELAN4_CLOCK_COMMS_DIV_VALUE   (((ELAN4_COMMS_CLOCK_FREQUENCY * ELAN4_CLOCK_ADD_VALUE) / (1000 * 4)) - 1)
++#define ELAN4_CLOCK_TICK_RATE         ((ELAN4_CLOCK_ADD_VALUE << 8) + ELAN4_CLOCK_COMMS_DIV_VALUE)
++
++typedef volatile union e4_clocktickrate
++{
++   E4_uint64 NanoSecClock;
++   struct
++   {
++      E4_uint32 pad1;
++      E4_uint32 TickRates;
++   } s;
++} E4_ClockTickRate;
++
++/*
++ * This is made into an 8k byte object.
++ */
++typedef volatile struct _E4_User_Regs
++{
++   E4_StatsCount      StatCounts[8];
++   E4_StatsCount      InstCount;
++   E4_Clock           Clock;
++   E4_StatsControl    StatCont;
++   E4_ClockTickRate   ClockTickRate;
++   E4_uint8           pad1[EightK - ((sizeof(E4_StatsCount)*9)+sizeof(E4_StatsControl)+
++                                      sizeof(E4_Clock)+sizeof(E4_ClockTickRate))];
++} E4_User_Regs;
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* __ELAN4_USERREGS_H */
+Index: linux-2.4.21/include/elan4/usertrap.h
+===================================================================
+--- linux-2.4.21.orig/include/elan4/usertrap.h 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan4/usertrap.h      2005-06-01 23:12:54.746416760 -0400
+@@ -0,0 +1,114 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: usertrap.h,v 1.17 2004/05/05 09:08:35 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/usertrap.h,v $*/
++
++#ifndef __ELAN4_USERTRAP_H
++#define __ELAN4_USERTRAP_H
++
++#ifndef _ASM
++typedef struct elan4_user_page
++{
++    E4_uint64         upage_ddcq_completed;
++} ELAN4_USER_PAGE;
++
++typedef struct elan4_user_trap
++{
++    int                               ut_type;
++    unsigned                  ut_proc;
++    unsigned                  ut_args[4];
++
++    union {
++      ELAN4_EPROC_TRAP        eproc;
++      ELAN4_CPROC_TRAP        cproc;
++      ELAN4_DPROC_TRAP        dproc;
++      ELAN4_IPROC_TRAP        iproc;
++      ELAN4_TPROC_TRAP        tproc;
++      ELAN4_NETERR_MSG        msg;
++    }                         ut_trap;
++} ELAN4_USER_TRAP;
++
++#endif /* _ASM */
++
++
++/* value for ut_type */
++#define UTS_FINISHED          0                               /* all pending traps have been handled */
++#define UTS_RESCHEDULE                1                               /* must return to user mode and re-enter */
++#define UTS_UNIMP_INSTR               2                               /* unimplemented thread instruction */
++#define UTS_EXECUTE_PACKET    3                               /* iproc trap needs packet executing */
++#define UTS_NETWORK_ERROR_TRAP        4                               /* network error on this trap */
++#define UTS_NETWORK_ERROR_MSG 5                               /* network error message  */
++#define UTS_NETWORK_ERROR_TIMER       6                               /* network error timer expired */
++
++#define UTS_EFAULT            -1                              /* failed to copyout trap */
++#define UTS_INVALID_ADDR      -2                              /* all -ve codes mean trap could not be resolved. */
++#define UTS_INVALID_VPROC     -3
++#define UTS_INVALID_COMMAND   -4
++#define UTS_BAD_TRAP          -5
++#define UTS_ALIGNMENT_ERROR   -6
++#define UTS_QUEUE_OVERFLOW    -7
++#define UTS_QUEUE_ERROR               -8
++#define UTS_INVALID_TRANS     -9
++#define UTS_PERMISSION_DENIED -10
++#define UTS_CPROC_ERROR               -11
++#define UTS_INVALID_COOKIE    -12
++#define UTS_NETERR_ERROR      -13
++
++/* "special" values for registering handlers */
++#define UTS_ALL_TRAPS         -9999
++
++/* value for ut_proc */
++#define UTS_NOPROC            0
++#define UTS_EPROC             1
++#define UTS_CPROC             2
++#define UTS_DPROC             3
++#define UTS_TPROC             4
++#define UTS_IPROC             5
++#define UTS_NETERR_MSG                6
++
++/* unimplemented trap numbers for thread processor */
++#define ELAN4_T_TRAP_INSTR(t) (0x80202000 | ((t) & 0xFF))
++
++#define ELAN4_T_SYSCALL_TRAP  1
++#  define ELAN4_T_OPEN                0
++#  define ELAN4_T_WRITE               1
++#  define ELAN4_T_READ                2
++#  define ELAN4_T_IOCTL               3
++#  define ELAN4_T_LSEEK               4
++#  define ELAN4_T_POLL                5
++#  define ELAN4_T_CLOSE               6
++#  define ELAN4_T_KILL                7
++#  define ELAN4_T_MMAP                8
++#  define ELAN4_T_MUNMAP      9
++#  define ELAN4_T_ABORT               100
++#  define ELAN4_T_DEBUG               101
++#  define ELAN4_T_REGDUMP     102
++
++#define ELAN4_T_REGDUMP_TRAP  2
++
++#define ELAN4_T_LIBELAN_TRAP  3
++#  define ELAN4_T_TPORT_NEWBUF        0
++#  define ELAN4_T_TPORT_GC    1
++#  define ELAN4_T_TPORT_DEBUG 2
++
++#define ELAN4_T_ALLOC_TRAP    4
++#  define ELAN4_T_ALLOC_ELAN  0
++#  define ELAN4_T_ALLOC_MAIN  1
++#  define ELAN4_T_FREE_ELAN   2
++#  define ELAN4_T_FREE_MAIN   3
++
++/* reserved main interrupt cookies */
++#define ELAN4_INT_COOKIE_DDCQ 0
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++#endif /* __ELAN4_USERTRAP_H */
+Index: linux-2.4.21/include/elan4/xsdram.h
+===================================================================
+--- linux-2.4.21.orig/include/elan4/xsdram.h   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/elan4/xsdram.h        2005-06-01 23:12:54.747416608 -0400
+@@ -0,0 +1,59 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN4_XSDRAM_H
++#define __ELAN4_XSDRAM_H
++
++#ident "@(#)$Id: xsdram.h,v 1.13 2004/03/05 12:32:04 jon Exp $ $Name: QSNETMODULES-4-30_20050128 $"
++/*      $Source: /cvs/master/quadrics/elan4hdr/xsdram.h,v $*/
++
++/* SAMSUNG K4H281638D-TCB3 */
++
++#define SDRAM_tRCF_1_SH         0
++#define SDRAM_tRP_1_SH          4
++#define SDRAM_tRCD_SH           8
++#define SDRAM_tRRD_SH           12
++#define SDRAM_tEndWr_SH         16
++#define SDRAM_tEndRd_SH         20
++#define SDRAM_Burst_SH          24
++#define SDRAM_CL_SH             28
++#define SDRAM_DsblBypass       (1ULL << 31)
++#define SDRAM_RefreshRate_SH    32
++#define SDRAM_RamSize_SH        34
++#define SDRAM_ReadLtncy_1_SH    36
++#define SDRAM_RdOffset_SH       40
++#define SDRAM_FlightDelay_SH    42
++
++#define SDRAM_ENABLE_ECC       (1ULL << 44) // Enables error detecting on the ECC.
++#define SDRAM_SDRAM_TESTING    (1ULL << 45) // Switches to test mode for checking EEC data bits
++#define SDRAM_SETUP            (1ULL << 46) // Writes SDram control reg when set. Also starts
++
++#define SDRAM_CS_MODE0          0ULL         // 64Mbit, 128Mbit, 256Mbit, 512Mbit or 1Gbit (16-bit output)
++#define SDRAM_CS_MODE1          1ULL         // 64Mbit, 128Mbit, 256Mbit or 512Mbit (8-bit output)
++#define SDRAM_CS_MODE2          2ULL         // 2Gbit (16-bit output) or 1Gbit (8-bit output)
++#define SDRAM_CS_MODE3          3ULL         // 4Gbit (16-bit output) or 2Gbit (8-bit output)
++
++#if defined(LINUX) && !defined(CONFIG_MPSAS)
++#define SDRAM_STARTUP_VALUE   ((0xbULL << SDRAM_tRCF_1_SH)      | (0x2ULL << SDRAM_tRP_1_SH)       | \
++                               (0x3ULL << SDRAM_tRCD_SH)        | (0x2ULL << SDRAM_tRRD_SH)        | \
++                               (0xaULL << SDRAM_tEndWr_SH)      | (0x6ULL << SDRAM_tEndRd_SH)      | \
++                               (0x8ULL << SDRAM_Burst_SH)       | (0x6ULL << SDRAM_CL_SH)          | \
++                               (0x2ULL << SDRAM_RefreshRate_SH) | (0x3ULL << SDRAM_RamSize_SH)     | \
++                               (0x1ULL << SDRAM_RdOffset_SH)    | (0x1ULL << SDRAM_FlightDelay_SH) | \
++                               (0x4ULL << SDRAM_ReadLtncy_1_SH))
++#else
++#define SDRAM_STARTUP_VALUE   ((0xbULL << SDRAM_tRCF_1_SH)      | (0x2ULL << SDRAM_tRP_1_SH)       | \
++                               (0x3ULL << SDRAM_tRCD_SH)        | (0x2ULL << SDRAM_tRRD_SH)        | \
++                               (0xaULL << SDRAM_tEndWr_SH)      | (0x6ULL << SDRAM_tEndRd_SH)      | \
++                               (0x8ULL << SDRAM_Burst_SH)       | (0x6ULL << SDRAM_CL_SH)          | \
++                               (0x0ULL << SDRAM_RefreshRate_SH) | (0x0ULL << SDRAM_RamSize_SH)     | \
++                               (0x1ULL << SDRAM_RdOffset_SH)    | (0x1ULL << SDRAM_FlightDelay_SH) | \
++                               (0x4ULL << SDRAM_ReadLtncy_1_SH) | SDRAM_ENABLE_ECC | SDRAM_SETUP)
++#endif
++
++#endif /* __ELAN4_XSDRAM_H */
+Index: linux-2.4.21/include/jtag/jtagio.h
+===================================================================
+--- linux-2.4.21.orig/include/jtag/jtagio.h    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/jtag/jtagio.h 2005-06-01 23:12:54.747416608 -0400
+@@ -0,0 +1,106 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "$Id: jtagio.h,v 1.7.8.1 2005/01/27 15:21:47 lee Exp $"
++/*             $Source: /cvs/master/quadrics/jtagmod/jtagio.h,v $*/
++
++
++#ifndef __SYS_JTAGMOD_H
++#define __SYS_JTAGMOD_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#define JTAG_MAX_CHIPS                8
++#define JTAG_MAX_INSTR_LEN    8
++#define JTAG_MAX_BITS           (JTAG_MAX_CHIPS * JTAG_MAX_INSTR_LEN)
++#define JTAG_MAX_DATA_LEN     1024
++
++#define JTAG_BYPASS           0xFF
++
++#define I2C_ADDR_LEN          7                               /* 7 bits of address */
++#define I2C_DATA_LEN          8                               /* 8 bits of data */
++#define I2C_MAX_DATA_LEN      9                               /* and upto 9 bytes worth */
++
++#define BITS_PER_BYTE         8
++#define JTAG_NBYTES(nbits)    (((nbits)+BITS_PER_BYTE-1)/BITS_PER_BYTE)
++#define JTAG_BIT(v, num)      (((v)[(num) / BITS_PER_BYTE] >> ((num) % BITS_PER_BYTE)) & 1)
++#define JTAG_SET_BIT(v, num)  ((v)[(num) / BITS_PER_BYTE] |= (1 << ((num) % BITS_PER_BYTE)))
++#define JTAG_CLR_BIT(v, num)  ((v)[(num) / BITS_PER_BYTE] &= ~(1 << ((num) % BITS_PER_BYTE)))
++
++#define RING_CLOCK_CARD               (0x3D)
++#define RING_CLOCK_SHIFT      (0x3E)
++#define RING_JTAG_LOOPBACK    (0x3F)
++#define RING_MAX              (0x40)
++
++#define RING_QUAD_BIT         (0x40)
++#define RING_I2C_BIT          (0x80)
++
++#define VALID_JTAG_RING(ring) ((ring) < 0x20 || (ring) == RING_JTAG_LOOPBACK)
++#define VALID_I2C_RING(ring)  ((ring) < 0x20 || (ring) == RING_CLOCK_CARD)
++
++
++typedef struct jtag_value
++{
++    u_char    bytes[JTAG_NBYTES(JTAG_MAX_DATA_LEN)];
++} JTAG_VALUE;
++
++/* arguements to JTAG_SHIFT_IR/JTAG_SHIFT_DR */
++typedef struct jtag_reset_args
++{
++    u_int     ring;
++} JTAG_RESET_ARGS;
++
++typedef struct jtag_shift_args
++{
++    u_int     ring;
++    u_int     nbits;
++    u_char     *value;
++} JTAG_SHIFT_ARGS;
++
++typedef struct i2c_args
++{
++    u_int     ring;
++    u_int     device;
++    u_int     reg;
++    u_int     count;
++    u_int     ok;
++    u_char    data[I2C_MAX_DATA_LEN];
++} I2C_ARGS;
++
++/* values for 'ok' - the return value from i2c_xx functions */
++#define I2C_OP_SUCCESS                0
++#define I2C_OP_ERROR          1
++#define I2C_OP_NOT_IDLE               2
++#define I2C_OP_NO_DEVICE      3
++#define I2C_OP_WRITE_TO_BIG   4
++#define I2C_OP_BAD_RESOURCE   5
++
++typedef struct i2c_clock_shift_args
++{
++    u_int     t;
++    u_int     n;
++    u_int     m;
++} I2C_CLOCK_SHIFT_ARGS;
++
++#define JTAG_RESET            _IOWR('j', '0', JTAG_RESET_ARGS)
++#define JTAG_SHIFT_IR         _IOWR('j', '1', JTAG_SHIFT_ARGS)
++#define JTAG_SHIFT_DR         _IOWR('j', '2', JTAG_SHIFT_ARGS)
++
++#define I2C_CLOCK_SHIFT               _IOWR('j', '4', I2C_CLOCK_SHIFT_ARGS)
++#define I2C_WRITE             _IOWR('j', '5', I2C_ARGS)
++#define I2C_READ              _IOWR('j', '6', I2C_ARGS)
++#define I2C_WRITEREG          _IOWR('j', '7', I2C_ARGS)
++#define I2C_READREG           _IOWR('j', '8', I2C_ARGS)
++
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* __SYS_JTAGMOD_H */
+Index: linux-2.4.21/include/linux/coproc.h
+===================================================================
+--- linux-2.4.21.orig/include/linux/coproc.h   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/linux/coproc.h        2005-06-01 23:12:54.748416456 -0400
+@@ -0,0 +1,206 @@
++/*
++ *    Copyright (C) 2002, 2003 Quadrics Ltd.
++ *
++ *    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.
++ *
++ *    This program is distributed in the hope that it will be useful,
++ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *    GNU General Public License for more details.
++ *
++ *    You should have received a copy of the GNU General Public License
++ *    along with this program; if not, write to the Free Software
++ *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ *
++ *
++ */
++
++/*
++ * Callbacks for coprocessor page table updates.
++ */
++
++#ifndef __LINUX_COPROC_H__
++#define __LINUX_COPROC_H__
++
++#include <linux/sched.h>
++#include <linux/mm.h>
++#include <linux/list.h>
++#include <linux/slab.h> /* kmalloc */
++
++typedef struct coproc_ops_struct {
++      struct list_head list;
++      void *arg;
++
++      void (*release)(void *arg, struct mm_struct *mm);
++      void (*sync_range)(void *arg, struct mm_struct *mm, unsigned long start, unsigned long end);
++      void (*invalidate_range)(void *arg, struct mm_struct *mm, unsigned long start, unsigned long end);
++      void (*update_range)(void *arg, struct mm_struct *mm, unsigned long start, unsigned long end);
++
++      void (*change_protection)(void *arg, struct mm_struct *mm, unsigned long start, unsigned long end, pgprot_t newprot);
++
++      void (*sync_page)(void *arg, struct vm_area_struct *vma, unsigned long address);
++      void (*invalidate_page)(void *arg, struct vm_area_struct *vma, unsigned long address);
++      void (*update_page)(void *arg, struct vm_area_struct *vma, unsigned long address);
++
++} coproc_ops_t;
++
++extern __inline__ void 
++register_coproc_ops(struct mm_struct *mm, coproc_ops_t *cp)
++{
++      if (mm->coproc_ops == NULL) {
++              mm->coproc_ops = (struct list_head *)
++                  kmalloc(sizeof(struct list_head), GFP_KERNEL);
++              INIT_LIST_HEAD(mm->coproc_ops);
++      }
++      list_add(&cp->list, mm->coproc_ops);
++}
++
++extern __inline__ void 
++unregister_coproc_ops(struct mm_struct *mm, coproc_ops_t *cp)
++{
++      list_del(&cp->list);
++      if (list_empty(mm->coproc_ops)) {
++              kfree(mm->coproc_ops);
++              mm->coproc_ops = NULL;
++      }
++}
++
++extern __inline__ void 
++coproc_release(struct mm_struct *mm)
++{
++      struct list_head *head = mm->coproc_ops;
++        struct list_head *lp;
++        coproc_ops_t *cp;
++
++      if (head) {
++              while (! list_empty(head)) {
++                  lp = head->next;
++                  cp = list_entry(lp, coproc_ops_t, list);
++                  
++                  list_del (&cp->list);
++                  
++                  if (cp->release)
++                      cp->release(cp->arg, mm);
++              }
++              kfree(head);
++              mm->coproc_ops = NULL;
++      }
++}
++
++extern __inline__ void 
++coproc_sync_range(struct mm_struct *mm, unsigned long start, unsigned long end)
++{
++      struct list_head *head = mm->coproc_ops;
++        struct list_head *lp;
++        coproc_ops_t *cp;
++
++      if (head) {
++              for (lp = head->next; lp != head; lp = lp->next) {
++                      cp = list_entry(lp, coproc_ops_t, list);
++                      if (cp->sync_range)
++                              cp->sync_range(cp->arg, mm, start, end);
++              }               
++      }
++}
++
++extern __inline__ void 
++coproc_invalidate_range(struct mm_struct *mm, unsigned long start, unsigned long end)
++{
++      struct list_head *head = mm->coproc_ops;
++        struct list_head *lp;
++        coproc_ops_t *cp;
++
++      if (head) {
++              for (lp = head->next; lp != head; lp = lp->next) {
++                      cp = list_entry(lp, coproc_ops_t, list);
++                      if (cp->invalidate_range)
++                              cp->invalidate_range(cp->arg, mm, start, end);
++              }               
++      }
++}
++
++extern __inline__ void 
++coproc_update_range(struct mm_struct *mm, unsigned long start, unsigned long end)
++{
++      struct list_head *head = mm->coproc_ops;
++        struct list_head *lp;
++        coproc_ops_t *cp;
++
++      if (head) {
++              for (lp = head->next; lp != head; lp = lp->next) {
++                      cp = list_entry(lp, coproc_ops_t, list);
++                      if (cp->update_range)
++                              cp->update_range(cp->arg, mm, start, end);
++              }               
++      }
++}
++
++extern __inline__ void 
++coproc_change_protection (struct mm_struct *mm, unsigned long start, unsigned long end, pgprot_t newprot)
++{
++      struct list_head *head = mm->coproc_ops;
++        struct list_head *lp;
++        coproc_ops_t *cp;
++
++      if (head) {
++              for (lp = head->next; lp != head; lp = lp->next) {
++                      cp = list_entry(lp, coproc_ops_t, list);
++                      if (cp->change_protection)
++                              cp->change_protection(cp->arg, mm, start, end, newprot);
++              }               
++      }
++}
++
++extern __inline__ void 
++coproc_sync_page(struct vm_area_struct *vma, unsigned long addr)
++{
++      struct list_head *head = vma->vm_mm->coproc_ops;
++        struct list_head *lp;
++        coproc_ops_t *cp;
++
++      if (head) {
++              for (lp = head->next; lp != head; lp = lp->next) {
++                      cp = list_entry(lp, coproc_ops_t, list);
++                      if (cp->sync_page)
++                              cp->sync_page(cp->arg, vma, addr);
++              }               
++      }
++}
++
++extern __inline__ void 
++coproc_invalidate_page(struct vm_area_struct *vma, unsigned long addr)
++{
++      struct list_head *head = vma->vm_mm->coproc_ops;
++        struct list_head *lp;
++        coproc_ops_t *cp;
++
++      if (head) {
++              for (lp = head->next; lp != head; lp = lp->next) {
++                      cp = list_entry(lp, coproc_ops_t, list);
++                      if (cp->invalidate_page)
++                              cp->invalidate_page(cp->arg, vma, addr);
++              }               
++      }
++}
++
++extern __inline__ void 
++coproc_update_page(struct vm_area_struct *vma, unsigned long addr)
++{
++      struct list_head *head = vma->vm_mm->coproc_ops;
++        struct list_head *lp;
++        coproc_ops_t *cp;
++
++      if (head) {
++              for (lp = head->next; lp != head; lp = lp->next) {
++                      cp = list_entry(lp, coproc_ops_t, list);
++                      if (cp->update_page)
++                              cp->update_page(cp->arg, vma, addr);
++              }               
++      }
++}
++
++
++#endif /* __LINUX_COPROC_H__ */
+Index: linux-2.4.21/include/linux/ptrack.h
+===================================================================
+--- linux-2.4.21.orig/include/linux/ptrack.h   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/linux/ptrack.h        2005-06-01 23:12:54.748416456 -0400
+@@ -0,0 +1,53 @@
++/*
++ *    Copyright (C) 2000  Regents of the University of California
++ *
++ *    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.
++ *
++ *    This program is distributed in the hope that it will be useful,
++ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *    GNU General Public License for more details.
++ *
++ *    You should have received a copy of the GNU General Public License
++ *    along with this program; if not, write to the Free Software
++ *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ *
++ * Derived from exit_actn.c by
++ *    Copyright (C) 2003 Quadrics Ltd.
++ *
++ */
++#ifndef __LINUX_PTRACK_H
++#define __LINUX_PTRACK_H
++
++/* 
++ * Process tracking - this allows a module to keep track of processes
++ * in order that it can manage all tasks derived from a single process.
++ */
++
++#define PTRACK_PHASE_CLONE            1
++#define PTRACK_PHASE_CLONE_FAIL       2
++#define PTRACK_PHASE_EXEC             3
++#define PTRACK_PHASE_EXIT             4
++
++typedef int (*ptrack_callback_t)(void *arg, int phase, struct task_struct *child);
++
++#define PTRACK_FINISHED               0
++#define PTRACK_INNHERIT               1
++#define PTRACK_DENIED         2
++
++struct ptrack_desc {
++       struct list_head        link;
++       ptrack_callback_t       callback;
++       void                   *arg;
++};
++
++extern int     ptrack_register (ptrack_callback_t callback, void *arg);
++extern void    ptrack_deregister (ptrack_callback_t callback, void *arg);
++extern int     ptrack_registered (ptrack_callback_t callback, void *arg);
++
++extern int     ptrack_call_callbacks (int phase, struct task_struct *child);
++
++#endif /* __LINUX_PTRACK_H */
+Index: linux-2.4.21/include/linux/sched.h
+===================================================================
+--- linux-2.4.21.orig/include/linux/sched.h    2005-06-01 22:52:05.000000000 -0400
++++ linux-2.4.21/include/linux/sched.h 2005-06-01 23:12:54.749416304 -0400
+@@ -30,6 +30,8 @@
+ #include <linux/pid.h>
+ #include <linux/kernel_stat.h>
++#include <linux/list.h>
++
+ struct exec_domain;
+ extern int exec_shield;
+ extern int exec_shield_randomize;
+@@ -322,6 +324,9 @@
+ #endif
+       /* Architecture-specific MM context */
+       mm_context_t context;
++      
++      /* Support page table updates on adapter cards with on-board MMU */
++      struct list_head *coproc_ops;
+       /* coredumping support */
+       int core_waiters;
+@@ -342,6 +347,7 @@
+       mmap_sem:       __RWSEM_INITIALIZER(name.mmap_sem), \
+       page_table_lock: SPIN_LOCK_UNLOCKED,            \
+       mmlist:         LIST_HEAD_INIT(name.mmlist),    \
++      coproc_ops:     NULL,                           \
+       rlimit_rss:     RLIM_INFINITY,                  \
+ }
+@@ -572,6 +578,9 @@
+ /* context-switch lock */
+       spinlock_t switch_lock;
++/* process tracking callbacks */
++       struct list_head ptrack_list;
++
+ /* journalling filesystem info */
+       void *journal_info;
+@@ -740,6 +749,7 @@
+     blocked:          {{0}},                                          \
+     alloc_lock:               SPIN_LOCK_UNLOCKED,                             \
+     switch_lock:      SPIN_LOCK_UNLOCKED,                             \
++    ptrack_list:      LIST_HEAD_INIT(tsk.ptrack_list),                \
+     journal_info:     NULL,                                           \
+     real_stack:               &tsk,                                           \
+ }
+Index: linux-2.4.21/include/qsnet/autoconf.h
+===================================================================
+--- linux-2.4.21.orig/include/qsnet/autoconf.h 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/qsnet/autoconf.h      2005-06-01 23:12:54.750416152 -0400
+@@ -0,0 +1,38 @@
++/*
++ *    Copyright (c) 2004 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ * NOTE: This file has been automatically generated:
++ *       node   : milano
++ *       kernel : /src/linux/qsnet/linux-2.4.21
++ *       date   : Wed May  4 18:24:23 EDT 2005
++ *
++ */
++
++#include <linux/version.h>
++#undef        NO_RMAP
++#define       AC
++#undef        NO_O1_SCHED
++#undef        NO_NPTL
++#define       NO_ABI
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
++#define       PROCESS_ACCT
++#endif
++#undef        RSS_ATOMIC
++#undef        NO_COPROC
++#define       NO_IOPROC
++#undef        NO_PTRACK
++#define       NO_PANIC_NOTIFIER
++#undef        NO_SHM_CLEANUP
++#undef        NO_PDE
++
++
++#define       CONFIG_EIP
++#define       CONFIG_ELAN
++#define       CONFIG_ELAN3
++#define       CONFIG_ELAN4
++#define       CONFIG_EP
++#define       CONFIG_JTAG
++#define       CONFIG_QSNET
++#define       CONFIG_RMS
+Index: linux-2.4.21/include/qsnet/condvar.h
+===================================================================
+--- linux-2.4.21.orig/include/qsnet/condvar.h  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/qsnet/condvar.h       2005-06-01 23:12:54.750416152 -0400
+@@ -0,0 +1,140 @@
++/*
++ *    Copyright (C) 2000  Regents of the University of California
++ *
++ *    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.
++ *
++ *    This program is distributed in the hope that it will be useful,
++ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *    GNU General Public License for more details.
++ *
++ *    You should have received a copy of the GNU General Public License
++ *    along with this program; if not, write to the Free Software
++ *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ *
++ */
++
++#if   !defined(_LINUX_CONDVAR_H)
++#define       _LINUX_CONDVAR_H
++
++#if   defined(__KERNEL__)
++
++#include <linux/list.h>
++#include <qsnet/debug.h>
++
++#define CV_RET_SIGPENDING     0
++#define CV_RET_TIMEOUT                (-1)
++#define CV_RET_NORMAL         1
++
++struct kcondvar_task {
++      struct task_struct      *task;          /* need to wrap task in this */
++      struct list_head        list;           /*   to thread as a list */
++      int                     blocked;
++};
++
++typedef struct {
++      struct list_head        task_list;      /* list of kcondvar_task's */
++} kcondvar_t;
++
++#define kcondvar_wait(c,l,fl)                 debug_kcondvar_wait(c, l, fl, 0,  TASK_UNINTERRUPTIBLE)
++#define kcondvar_waitsig(c,l,fl)              debug_kcondvar_wait(c, l, fl, 0,  TASK_INTERRUPTIBLE)
++#define kcondvar_timedwait(c,l,fl,to)         debug_kcondvar_wait(c, l, fl, to, TASK_UNINTERRUPTIBLE)
++#define kcondvar_timedwaitsig(c,l,fl,to)      debug_kcondvar_wait(c, l, fl, to, TASK_INTERRUPTIBLE)
++#define kcondvar_wakeupone(c,l)                       kcondvar_wakeup(c, l, 0)
++#define kcondvar_wakeupall(c,l)                       kcondvar_wakeup(c, l, 1)
++ 
++extern __inline__ void
++kcondvar_init(kcondvar_t *c)
++{
++      INIT_LIST_HEAD(&c->task_list);
++}
++
++extern __inline__ void
++kcondvar_destroy(kcondvar_t *c)
++{
++      ASSERT(list_empty(&c->task_list));
++}
++
++/*
++ * We thread a struct kcondvar_task, allocated on the stack, onto the kcondvar_t's
++ * task_list, and take it off again when we wake up.
++ */
++extern __inline__ int
++debug_kcondvar_wait(kcondvar_t *c, spinlock_t *l, unsigned long *fl, long tmo, int state)
++{
++      struct kcondvar_task cvt;
++      int ret = CV_RET_NORMAL;
++
++      ASSERT(!in_interrupt());                /* we can block */
++      ASSERT(SPINLOCK_HELD(l));               /* enter holding lock */
++
++      cvt.task = current;
++      cvt.blocked = 1;
++      list_add(&cvt.list, &c->task_list);
++      do {
++             /* Note: we avoid using TASK_UNINTERRUPTIBLE here because avenrun()
++              * (linux/kernel/timer.c:calc_load())
++              * computation treats it like TASK_RUNNABLE hence creates false high
++              * load averages when we create kernel threads.
++              * The cvt.blocked flag distinguishes a signal wakeup from a kcondvar_wakeup.
++              *
++              * However, if we do take a signal we could end up busily spinning here, if
++              * we ignore it (state == TASK_UNINTERRUPTIBLE) so once we see a signal
++              * pending we do sleep TASK_UNINTERRUPTIBLE to stop a busy spin.
++              * I have now blocked all signals for kernel threads to prevent this
++              * happening but other users of kcondvar_wait may still hit this spin.
++              */
++              set_current_state (signal_pending(current) ? state : TASK_INTERRUPTIBLE);
++
++              if (fl)
++                  spin_unlock_irqrestore(l, *fl);
++              else
++                  spin_unlock(l);
++              if (tmo) {
++                      if (tmo <= jiffies || !schedule_timeout(tmo - jiffies))
++                              ret = CV_RET_TIMEOUT;
++              } else
++                      schedule();
++              if (fl)
++                  spin_lock_irqsave (l, *fl);
++              else
++                  spin_lock(l);
++              
++              /* signal_pending - Only exit the loop if the user was waiting TASK_INTERRUPTIBLE */
++              if ((state == TASK_INTERRUPTIBLE) && signal_pending(current))
++                      ret = CV_RET_SIGPENDING;
++
++      } while (cvt.blocked && ret == CV_RET_NORMAL);
++      list_del(&cvt.list);
++
++      /* Reset task state in case we didn't sleep above */
++      set_current_state (TASK_RUNNING);
++
++      return ret;                             /* return holding lock */
++}
++
++extern __inline__ void
++kcondvar_wakeup(kcondvar_t *c, spinlock_t *l, int wakeall)
++{
++      struct list_head *lp;
++      struct kcondvar_task *cvtp;
++
++      ASSERT(SPINLOCK_HELD(l));                       /* already holding lock */
++      for (lp = c->task_list.next; lp != &c->task_list; lp = lp->next) {
++              cvtp = list_entry(lp, struct kcondvar_task, list);
++              if (cvtp->blocked) {
++                      cvtp->blocked = 0;
++                      /* wake_up_process added to kernel/ksyms.c */
++                      wake_up_process(cvtp->task); 
++                      if (!wakeall)
++                              break;
++              }
++      }
++}                                             /* return still holding lock */
++
++
++#endif /* __KERNEL__ */
++#endif /* _LINUX_CONDVAR_H */
+Index: linux-2.4.21/include/qsnet/config.h
+===================================================================
+--- linux-2.4.21.orig/include/qsnet/config.h   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/qsnet/config.h        2005-06-01 23:12:54.751416000 -0400
+@@ -0,0 +1,195 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef _QSNET_CONFIG_H
++#define _QSNET_CONFIG_H
++
++#ident "$Id: config.h,v 1.23 2003/07/24 21:31:19 robin Exp $"
++/*      $Source: /cvs/master/quadrics/qsnet/config.h,v $*/
++
++
++/*
++ * QSNET standard defines :
++ *
++ *   Target operating system defines
++ *            SOLARIS
++ *            TRU64UNIX/DIGITAL_UNIX
++ *            LINUX
++ *
++ *   Target processor defines
++ *            SPARC
++ *            ALPHA
++ *            I386
++ *            IA64
++ *            X86_64
++ *
++ *   Byte order defines
++ *            __LITTLE_ENDIAN__
++ *            __BIG_ENDIAN__
++ *
++ *   Data size defines
++ *            _LP64                   - LP64 - long/pointer is 64 bits
++ *            _ILP32                  - LP32 - long/pointer is 32 bits
++ *
++ *   Elan defines for main processor
++ *            __MAIN_LITTLE_ENDIAN__  - main byte order (for thread code)
++ *            __MAIN_BIG_ENDIAN__
++ *            _MAIN_LP64              - main long size (for thread code)
++ *            _MAIN_ILP32
++ *
++ *   Compiling for kernel (defined in makefile)
++ *            _KERNEL
++ *
++ */
++
++#if defined(__LP64__) && !defined(_LP64)
++#  define _LP64
++#endif
++
++#if defined(__arch64__) && !defined(_LP64) && !defined(_ILP32)
++#  define _LP64
++#endif
++
++#if defined(__alpha__) && !defined(_LP64) && !defined(_ILP32)
++#  define _LP64
++#endif
++
++#if !defined(__arch64__) && !defined(_ILP32) && !defined(_LP64)
++#  define _ILP32
++#endif
++
++#if defined(__ELAN__) || defined(__ELAN3__)
++
++#define __LITTLE_ENDIAN__
++
++#if defined(__host_solaris) && defined(__host_sparc)
++#define SOLARIS
++#define SPARC
++#define SOLARIS_SPARC
++#define _MAIN_ILP32
++#define __MAIN_BIG_ENDIAN__
++
++#elif defined(__host_osf)
++#define TRU64UNIX
++#define DIGITAL_UNIX
++#define ALPHA
++#define _MAIN_LP64
++#define __MAIN_LITTLE_ENDIAN__
++
++#elif defined(__host_linux) && defined(__host_alpha)
++#define LINUX
++#define ALPHA
++#define LINUX_ALPHA
++#define _MAIN_LP64
++#define __MAIN_LITTLE_ENDIAN__
++
++#elif defined(__host_linux) && defined(__host_sparc)
++#define LINUX
++#define SPARC
++#define LINUX_SPARC
++#define __MAIN_BIG_ENDIAN__
++#ifdef __KERNEL__
++#  define _MAIN_LP64
++#else
++#  define _MAIN_ILP32
++#endif
++
++#elif defined(__host_linux) && defined(__host_i386)
++#define LINUX
++#define I386
++#define LINUX_I386
++#define _MAIN_ILP32
++#define __MAIN_LITTLE_ENDIAN__
++
++#elif defined(__host_linux) && defined(__host_ia64)
++#define LINUX
++#define IA64
++#define LINUX_IA64
++#define _MAIN_LP64
++#define __MAIN_LITTLE_ENDIAN__
++
++#elif defined(__host_linux) && defined(__host_x86_64)
++#define LINUX
++#define X86_64
++#define LINUX_X86_64
++#define _MAIN_LP64
++#define __MAIN_LITTLE_ENDIAN__
++
++#else
++#error Cannot determine operating system/processor architecture.
++#endif
++
++#else /* !defined(__ELAN3__) */
++
++#if (defined(sun) || defined(__sun)) && defined(sparc) && !defined(__sparcv9) /* Sun Solaris 5.6 */
++#define SOLARIS
++#define SPARC
++#define SOLARIS_SPARC
++#ifndef __BIG_ENDIAN__
++#define __BIG_ENDIAN__
++#endif 
++
++#elif (defined(sun) || defined(__sun)) && defined(sparc) && defined(__sparcv9)        /* Sun Solaris 5.7 */
++#define SOLARIS
++#define SPARC
++#define SOLARIS_SPARC
++#define __BIG_ENDIAN__
++
++#elif defined(__osf__) && defined(__alpha)                                    /* Digital Unix */
++#define TRU64UNIX
++#define DIGITAL_UNIX
++#define ALPHA
++#define __LITTLE_ENDIAN__
++
++#elif (defined(linux) || defined(__linux__)) && defined(__alpha)              /* Linux Alpha */
++
++#define LINUX
++#define ALPHA
++#define LINUX_ALPHA
++#define __LITTLE_ENDIAN__
++
++#elif (defined(linux) || defined(__linux__)) && defined(__sparc)              /* Linux Sparc */
++
++#define LINUX
++#define SPARC
++#define LINUX_SPARC
++#define __BIG_ENDIAN__
++
++#elif (defined(linux) || defined(__linux__)) && defined(__i386)                       /* Linux i386 */
++
++#define LINUX
++#define I386
++#define LINUX_I386
++#define __LITTLE_ENDIAN__
++
++#elif (defined(linux) || defined(__linux__)) && defined(__ia64)                       /* Linux ia64 */
++
++#define LINUX
++#define IA64
++#define LINUX_IA64
++#define __LITTLE_ENDIAN__
++
++#elif (defined(linux) || defined(__linux__)) && defined(__x86_64)                     /* Linux x86_64 */
++
++#define LINUX
++#define X86_64
++#define LINUX_X86_64
++#define __LITTLE_ENDIAN__
++
++#elif defined(__QNXNTO__)
++#define QNX
++#define I386
++#define __LITTLE_ENDIAN__
++#else
++#error Cannot determine operating system/processor architecture.
++#endif
++
++#endif
++
++#include <qsnet/workarounds.h>
++
++#endif /* _QSNET_CONFIG_H */
+Index: linux-2.4.21/include/qsnet/crwlock.h
+===================================================================
+--- linux-2.4.21.orig/include/qsnet/crwlock.h  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/qsnet/crwlock.h       2005-06-01 23:12:54.751416000 -0400
+@@ -0,0 +1,207 @@
++/* 
++ *    Copyright (C) 2000  Regents of the University of California
++ *
++ *    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.
++ *
++ *    This program is distributed in the hope that it will be useful,
++ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *    GNU General Public License for more details.
++ *
++ *    You should have received a copy of the GNU General Public License
++ *    along with this program; if not, write to the Free Software
++ *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ *
++ */
++
++/*
++ *    Complex - Reader/Writer locks
++ *    Ref: "UNIX Systems for Modern Architectures", by Curt Schimmel, 
++ *    sec 11.6.3. 
++ *
++ *    This implementation is based on semaphores and may not be called from 
++ *    interrupt handlers.
++ *
++ */
++
++#if   !defined(_LINUX_RWLOCK_H)
++#define       _LINUX_RWLOCK_H
++
++#if   defined(__KERNEL__)
++
++typedef enum { RD, WRT, ANY } crwlock_type_t;
++
++#define crwlock_write_held(l) debug_crwlock_held(l, WRT, __BASE_FILE__,__LINE__)
++#define crwlock_read_held(l) debug_crwlock_held(l, RD, __BASE_FILE__, __LINE__)
++#define crwlock_held(l)      debug_crwlock_held(l, ANY, __BASE_FILE__, __LINE__)
++
++#define crwlock_read(l)            debug_crwlock_read(l, __BASE_FILE__, __LINE__)
++#define crwlock_write(l)     debug_crwlock_write(l, __BASE_FILE__, __LINE__)
++#define crwlock_done(l)      debug_crwlock_done(l, __BASE_FILE__, __LINE__)
++
++#if     defined(DEBUG_RWLOCK) && defined(__alpha__) && !defined(DEBUG_SPINLOCK)
++#define DEBUG_SPINLOCK
++#endif
++
++#include <linux/spinlock.h>
++#include <asm/semaphore.h>
++#include <qsnet/debug.h>
++#include <qsnet/mutex.h>
++#include <linux/version.h>
++
++#if   !defined(DEBUG_SPINLOCK)
++#define debug_spin_lock(lock, file, line)       spin_lock(lock)
++#endif
++
++typedef struct {
++        spinlock_t            m_lock;         /* protects cnt fields below */
++        int                     m_rdcnt;        /* # of rdrs in crit section */
++        int                     m_wrcnt;        /* # of wrtrs in crit section */
++        int                     m_rdwcnt;       /* # of waiting readers */
++        int                     m_wrwcnt;       /* # of waiting writers */
++        struct semaphore        m_rdwait;       /* sema where readers wait */
++        struct semaphore        m_wrwait;       /* sema where writers wait */
++        pid_t                 m_wrholder;     /* task holding write lock */
++} crwlock_t;
++ 
++extern __inline__ void 
++crwlock_init(crwlock_t *l)
++{
++      l->m_lock = SPIN_LOCK_UNLOCKED;
++#if   LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
++      l->m_rdwait = MUTEX_LOCKED;
++      l->m_wrwait = MUTEX_LOCKED;
++#else
++      sema_init(&l->m_rdwait,0);
++      sema_init(&l->m_wrwait,0);
++#endif
++      l->m_rdcnt = l->m_wrcnt = l->m_rdwcnt = l->m_wrwcnt = 0;
++      l->m_wrholder = PID_NONE;
++}
++
++extern __inline__ void 
++crwlock_destroy(crwlock_t *l)
++{
++      ASSERT(l->m_rdcnt == 0 && l->m_wrcnt == 0);
++}
++
++/*
++ * If a writer has the lock presently or there are writers waiting,
++ * then we have to wait.
++ */
++extern __inline__ void 
++debug_crwlock_read(crwlock_t *l, char *file, int line)
++{
++      ASSERT(!in_interrupt());
++      spin_lock(&l->m_lock);
++      if (l->m_wrcnt || l->m_wrwcnt) {
++              l->m_rdwcnt++;
++              spin_unlock(&l->m_lock);
++              down(&l->m_rdwait); /* P */
++      } else {
++              l->m_rdcnt++;
++              spin_unlock(&l->m_lock);
++      }
++}
++
++/*
++ * If we're the last reader, and a writer is waiting,
++ * then let the writer go now.
++ */
++/* private */
++extern __inline__ void 
++debug_crwlock_read_done(crwlock_t *l, char *file, int line)
++{
++      spin_lock(&l->m_lock);
++      l->m_rdcnt--;
++      if (l->m_wrwcnt && l->m_rdcnt == 0) {
++              l->m_wrcnt = 1;
++              l->m_wrwcnt--;
++              spin_unlock(&l->m_lock);
++              up(&l->m_wrwait); /* V */       
++              return;
++      }
++      spin_unlock(&l->m_lock);
++}
++
++extern __inline__ void 
++debug_crwlock_write(crwlock_t *l, char *file, int line)
++{
++      ASSERT(!in_interrupt());
++      spin_lock(&l->m_lock);
++      if (l->m_wrcnt || l->m_rdcnt) {         /* block if lock is in use */
++              l->m_wrwcnt++;
++              spin_unlock(&l->m_lock);
++              down(&l->m_wrwait); /* P */
++      } else {                                /* lock is not in use */
++              l->m_wrcnt = 1;
++              spin_unlock(&l->m_lock);
++      }
++      l->m_wrholder = current->pid;
++}
++
++/* private */
++extern __inline__ void
++debug_crwlock_write_done(crwlock_t *l, char *file, int line)
++{
++      int rdrs;
++
++      spin_lock(&l->m_lock);
++      l->m_wrholder = PID_NONE;
++      if (l->m_rdwcnt) {                      /* let any readers go first */
++              l->m_wrcnt = 0;
++              rdrs = l->m_rdwcnt;
++              l->m_rdcnt = rdrs;
++              l->m_rdwcnt = 0;
++              spin_unlock(&l->m_lock);
++              while (rdrs--)
++                      up(&l->m_rdwait); /* V */
++      } else if (l->m_wrwcnt) {               /* or let any writer go */
++              l->m_wrwcnt--;
++              spin_unlock(&l->m_lock);
++              up(&l->m_wrwait); /* V */
++      } else {                                /* nobody waiting, unlock */
++              l->m_wrcnt = 0;
++              spin_unlock(&l->m_lock);
++      }
++}
++
++extern __inline__ void
++debug_crwlock_done(crwlock_t *l, char *file, int line)
++{
++      if (l->m_wrholder == current->pid)
++              debug_crwlock_write_done(l, file, line);
++      else
++              debug_crwlock_read_done(l, file, line);
++}
++
++/*
++ * Return nonzero if lock is held
++ */
++extern __inline__ int  
++debug_crwlock_held(crwlock_t *l, crwlock_type_t t, char *file, int line)
++{
++      int res;
++
++      spin_lock(&l->m_lock);
++      switch(t) {
++              case RD:
++                      res = l->m_rdcnt;
++                      break;
++              case WRT:
++                      res = l->m_wrcnt;
++                      break;
++              case ANY:
++                      res = l->m_wrcnt + l->m_rdcnt;
++                      break;
++      }
++      spin_unlock(&l->m_lock);
++
++      return res;
++}
++
++#endif /* __KERNEL__ */
++#endif /* _LINUX_RWLOCK_H */
+Index: linux-2.4.21/include/qsnet/ctrl_linux.h
+===================================================================
+--- linux-2.4.21.orig/include/qsnet/ctrl_linux.h       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/qsnet/ctrl_linux.h    2005-06-01 23:12:54.751416000 -0400
+@@ -0,0 +1,37 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __QSNET_CTRL_LINUX_H
++#define __QSNET_CTRL_LINUX_H
++
++#ident "$Id: ctrl_linux.h,v 1.3 2003/03/26 09:32:03 mike Exp $"
++/*      $Source: /cvs/master/quadrics/qsnet/ctrl_linux.h,v $*/
++
++#define QSNETIO_USER_BASE             0x40
++
++#define QSNETIO_DEBUG_DUMP            _IO   ('e', QSNETIO_USER_BASE + 0)
++
++typedef struct qsnetio_debug_buffer_struct
++{
++      caddr_t addr; 
++      size_t  len;
++} QSNETIO_DEBUG_BUFFER_STRUCT;
++#define QSNETIO_DEBUG_BUFFER          _IOWR ('e', QSNETIO_USER_BASE + 1, QSNETIO_DEBUG_BUFFER_STRUCT)
++
++typedef struct qsnetio_debug_kmem_struct
++{
++      void *handle;
++} QSNETIO_DEBUG_KMEM_STRUCT;
++#define QSNETIO_DEBUG_KMEM            _IOWR   ('e', QSNETIO_USER_BASE + 2, QSNETIO_DEBUG_KMEM_STRUCT)
++
++#endif /* __QSNET_CTRL_LINUX_H */
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.21/include/qsnet/debug.h
+===================================================================
+--- linux-2.4.21.orig/include/qsnet/debug.h    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/qsnet/debug.h 2005-06-01 23:12:54.752415848 -0400
+@@ -0,0 +1,68 @@
++/*
++ *    Copyright (C) 2000  Regents of the University of California
++ *
++ *    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.
++ *
++ *    This program is distributed in the hope that it will be useful,
++ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *    GNU General Public License for more details.
++ *
++ *    You should have received a copy of the GNU General Public License
++ *    along with this program; if not, write to the Free Software
++ *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ *
++ */
++#ifndef _QSNET_DEBUG_H
++#define _QSNET_DEBUG_H
++
++#if defined(DIGITAL_UNIX) 
++#include <kern/assert.h>
++#elif defined(LINUX)
++extern int qsnet_assfail (char *ex, const char *func, char *file, int line);
++
++#define ASSERT(EX)    do { \
++      if (!(EX) && qsnet_assfail (#EX, __FUNCTION__, __BASE_FILE__, __LINE__)) { \
++              BUG(); \
++      } \
++} while (0)
++#endif /* DIGITAL_UNIX */
++
++/* debug.c */
++extern void qsnet_debug_init(void);
++extern void qsnet_debug_fini(void);
++extern void qsnet_debug_disable(int);
++extern void qsnet_debug_alloc(void);
++
++#define QSNET_DEBUG_BUFFER  ((unsigned int)(0x01))
++#define QSNET_DEBUG_CONSOLE ((unsigned int)(0x02))
++#define QSNET_DEBUG_BUF_CON ( QSNET_DEBUG_BUFFER | QSNET_DEBUG_CONSOLE )
++
++#ifdef __GNUC__
++extern void qsnet_debugf      (unsigned int mode, char *fmt, ...)
++      __attribute__ ((format (printf,2,3)));
++extern void kqsnet_debugf      (char *fmt, ...)
++      __attribute__ ((format (printf,1,2)));
++#else
++extern void qsnet_debugf      (unsigned int mode, char *fmt, ...);
++extern void kqsnet_debugf     (char *fmt, ...);
++#endif
++extern void qsnet_vdebugf     (unsigned int mode, char * prefix, char *fmt,  va_list ap);
++extern int  qsnet_debug_buffer(caddr_t ubuffer, int len);
++extern int  qsnet_debug_dump  (void);
++extern int  qsnet_debug_kmem  (void *handle);
++
++extern void qsnet_debug_buffer_on(void);
++extern void qsnet_debug_buffer_clear(void);
++extern void qsnet_debug_buffer_mark(char *str);
++
++#endif /* _QSNET_DEBUG_H */
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.21/include/qsnet/fence.h
+===================================================================
+--- linux-2.4.21.orig/include/qsnet/fence.h    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/qsnet/fence.h 2005-06-01 23:12:54.752415848 -0400
+@@ -0,0 +1,178 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++/* $Id: fence.h,v 1.21.6.4 2004/11/23 14:34:45 addy Exp $ */
++/*             $Source: /cvs/master/quadrics/qsnet/fence.h,v $*/
++
++#ifndef _CONFIG_FENCE_H
++#define _CONFIG_FENCE_H
++
++#ident "$Id: fence.h,v 1.21.6.4 2004/11/23 14:34:45 addy Exp $"
++
++#ifdef        __cplusplus
++extern "C" {
++#endif
++
++#if defined(__ELAN__) || defined(__ELAN3__)
++
++/* no memory barriers required on elan3/elan4 */
++
++#elif defined QSNET_MEMBARS_ASSERT
++
++#include <assert.h>
++#define MEMBAR_MEMISSUE()       assert(0);
++#define MEMBAR_SYNC()           assert(0);
++#define MEMBAR_STORELOAD()      assert(0);
++#define MEMBAR_LOADSTORE()      assert(0);
++#define MEMBAR_STORESTORE()     assert(0);
++#define MEMBAR_LOADLOAD()       assert(0);
++#define MEMBAR_VISIBLE()        assert(0);
++#define MEMBAR_DRAIN()          assert(0);
++    
++#elif defined(__alpha)
++
++/* Memory barrier instructions */
++#if defined(__DECC) || defined(__DECXX)
++long   asm( const char *,...);
++#pragma intrinsic( asm )
++#define MEMBAR_MEMISSUE()     asm("mb")
++#define MEMBAR_SYNC()                 asm("mb")
++#define MEMBAR_STORELOAD()    asm("wmb")
++#define MEMBAR_LOADSTORE()    asm("mb")
++#define MEMBAR_STORESTORE()   asm("wmb")
++#define MEMBAR_LOADLOAD()     asm("mb")
++#define MEMBAR_VISIBLE()      asm("")
++#define MEMBAR_DRAIN()                asm("wmb")
++
++#else
++/* Assume gcc */
++#define MEMBAR_MEMISSUE()     asm volatile ("mb"::)
++#define MEMBAR_SYNC()                 asm volatile ("mb"::)
++#define MEMBAR_STORELOAD()    asm volatile ("wmb"::)
++#define MEMBAR_LOADSTORE()    asm volatile ("mb"::)
++#define MEMBAR_STORESTORE()   asm volatile ("wmb"::)
++#define MEMBAR_LOADLOAD()     asm volatile ("mb"::)
++#define MEMBAR_VISIBLE()      asm volatile (""   ::: "memory")
++#define MEMBAR_DRAIN()                asm volatile ("wmb"::: "memory")
++
++#endif /* __DECC */
++
++#elif defined(__sparc)
++
++/* UltraSPARC with WRITE MERGING enabled */
++#define MEMBAR_MEMISSUE()     asm volatile ("membar #MemIssue");
++#define MEMBAR_SYNC()         asm volatile ("membar #Sync");
++#define MEMBAR_STORELOAD()    asm volatile ("membar #StoreLoad");
++#define MEMBAR_LOADSTORE()    asm volatile ("membar #LoadStore");
++#define MEMBAR_STORESTORE()   asm volatile ("membar #StoreStore");
++#define MEMBAR_LOADLOAD()     asm volatile ("membar #LoadLoad");
++#define MEMBAR_VISIBLE()      asm volatile (""::: "memory")
++#define MEMBAR_DRAIN()                asm volatile (""::: "memory")
++
++#elif defined(__linux__)
++
++#if defined(__INTEL_COMPILER)
++
++/* NB: Intel compiler version 8.0 now also defines __GNUC__ unless you set the -no-gcc cmdline option
++ * I've moved the check for __INTEL_COMPILER to be first to get around this
++ */
++#ifdef __ECC
++
++#include <ia64intrin.h>
++
++#define MEMBAR_MEMISSUE()       __mf()
++#define MEMBAR_SYNC()           __mf()
++#define MEMBAR_STORELOAD()      __mf()
++#define MEMBAR_LOADSTORE()      __mf()
++#define MEMBAR_STORESTORE()     __mf()
++#define MEMBAR_LOADLOAD()       __mf()
++#define MEMBAR_VISIBLE()      __mf()
++#define MEMBAR_DRAIN()                __mf()
++
++#else
++
++#warning Membars not implemented with this compiler.
++#define MEMBAR_MEMISSUE()       ;
++#define MEMBAR_SYNC()           ;
++#define MEMBAR_STORELOAD()      ;
++#define MEMBAR_LOADSTORE()      ;
++#define MEMBAR_STORESTORE()     ;
++#define MEMBAR_LOADLOAD()       ;
++#define MEMBAR_VISIBLE()        ;
++#define MEMBAR_DRAIN()          ;
++
++#endif /* __ECC */
++
++#elif defined(__GNUC__)
++
++#ifndef __ia64
++
++/* These are needed by <asm/system.h> on AMD64 */
++#include <asm/types.h>
++#include <asm/bitops.h>
++
++#ifndef __cplusplus
++/* this header file has a parameter called "new" - great huh */
++#include <asm/system.h>
++#endif
++
++#else
++#  define mb()        __asm__ __volatile__ ("mf" ::: "memory")
++#  define rmb()       mb()
++#  define wmb()       mb()
++#endif /* !__ia64 */
++
++#if defined(__x86_64) || defined(__i386)
++/* For some reason the AMD64 definition (glibc-devel 2.3.X) of this 
++ * is not useful (compiler only directive) so we overload it here
++ */
++/* I don't trust the IA32 header files either as with mtrr enabled
++ * we really need a membar and not a compiler directive
++ * NB: sfence is only available with X86_FEATURE_XMM CPUs
++ */
++#undef wmb
++#define wmb()    asm volatile("sfence":::"memory");
++#endif /* __x86_64 */
++
++#define MEMBAR_MEMISSUE()     mb()
++#define MEMBAR_SYNC()                 mb()
++#define MEMBAR_STORELOAD()    wmb()
++#define MEMBAR_LOADSTORE()    mb()
++#define MEMBAR_STORESTORE()   wmb()
++#define MEMBAR_LOADLOAD()     mb()
++
++#ifdef __ia64
++#define MEMBAR_VISIBLE()      asm volatile ("mf.a;;mf;;"::: "memory")
++#define MEMBAR_DRAIN()                asm volatile ("mf;"::: "memory")
++#else
++#define MEMBAR_VISIBLE()      asm volatile (""::: "memory")
++#define MEMBAR_DRAIN()                wmb()
++#endif
++
++#else /* elif __GNUC__ */
++
++#error Membars not implemented for this architecture/compiler.
++
++#endif /* __INTEL_COMPILER */
++
++#else /* elif __linux__ */
++
++#error Membars not implemented for this architecture/compiler.
++
++#endif
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* _CONFIG_FENCE_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/include/qsnet/kernel.h
+===================================================================
+--- linux-2.4.21.orig/include/qsnet/kernel.h   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/qsnet/kernel.h        2005-06-01 23:12:54.752415848 -0400
+@@ -0,0 +1,38 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __QSNET_KERNEL_H
++#define __QSNET_KERNEL_H
++
++#ident "$Id: kernel.h,v 1.8 2003/03/14 10:18:22 mike Exp $"
++/*      $Source: /cvs/master/quadrics/qsnet/kernel.h,v $*/
++
++#include <qsnet/config.h>
++#include <qsnet/types.h>
++
++#if defined(SOLARIS)
++#include <qsnet/kernel_solaris.h>
++#endif
++
++#if defined(DIGITAL_UNIX)
++#include <qsnet/kernel_dunix.h>
++#endif
++
++#if defined(LINUX)
++#include <qsnet/kernel_linux.h>
++#endif
++
++#include <qsnet/debug.h>
++
++#endif /* __QSNET_KERNEL_H */
++
++
++
++
++
++
++
+Index: linux-2.4.21/include/qsnet/kernel_linux.h
+===================================================================
+--- linux-2.4.21.orig/include/qsnet/kernel_linux.h     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/qsnet/kernel_linux.h  2005-06-01 23:12:54.753415696 -0400
+@@ -0,0 +1,354 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __QSNET_KERNEL_LINUX_H
++#define __QSNET_KERNEL_LINUX_H
++
++#ident "$Id: kernel_linux.h,v 1.62.6.5 2005/01/18 14:37:22 david Exp $"
++/*      $Source: /cvs/master/quadrics/qsnet/kernel_linux.h,v $*/
++
++#if defined(MODVERSIONS)
++#include <linux/modversions.h>
++#endif
++
++#include <linux/autoconf.h>
++#include <linux/module.h>
++
++
++/* ASSERT(spin_is_locked(l)) would always fail on UP kernels */
++#if defined(CONFIG_SMP)
++#define SPINLOCK_HELD(l)      spin_is_locked(l)
++#else
++#define SPINLOCK_HELD(l)      (1) 
++#endif
++
++#include <asm/io.h>
++#include <asm/uaccess.h>
++
++#include <linux/types.h>
++#include <linux/time.h>
++
++#include <linux/delay.h>
++#include <linux/smp_lock.h>
++#include <linux/spinlock.h>
++#include <linux/module.h>
++
++#include <linux/coproc.h>     /* Quadrics added */
++
++#include <linux/highmem.h>
++
++#include <qsnet/mutex.h>
++#include <qsnet/condvar.h>
++#include <qsnet/crwlock.h>
++
++#if defined(LINUX_ALPHA)
++#  include <asm/core_tsunami.h>       /* for TSUNAMI_MEM */
++#endif
++
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)
++#     undef   MOD_INC_USE_COUNT
++#     undef   MOD_DEC_USE_COUNT
++#     define  MOD_INC_USE_COUNT
++#     define  MOD_DEC_USE_COUNT
++#endif
++
++#define MIN(a,b)      ((a) > (b) ? (b) : (a))
++#define MAX(a,b)      ((a) > (b) ? (a) : (b))
++
++/* stray types */
++typedef u64              u_longlong_t;
++typedef unsigned long    uintptr_t;
++typedef int              bool_t;
++
++typedef unsigned long    virtaddr_t;                          /* virtual address */
++typedef unsigned long      ioaddr_t;                          /* io address */
++typedef unsigned long      sdramaddr_t;                               /* elan sdram offset */
++
++/* 386 kernel can be compiled with PAE enabled to use a 44 bit physical address */
++#if defined(CONFIG_X86_PAE)
++typedef unsigned long long physaddr_t;
++#else
++typedef unsigned long    physaddr_t;
++#endif
++
++/* ticks since reboot, and tick freq */
++#define lbolt         jiffies 
++#define hz            HZ
++
++/* System page size and friends */
++#define PAGESIZE      PAGE_SIZE
++#define PAGESHIFT     PAGE_SHIFT
++#define PAGEOFFSET    (PAGE_SIZE - 1)
++#define PAGEMASK      PAGE_MASK
++
++#define PAGE_ALIGNED(a)       (((a) & PAGE_MASK) == a)
++
++/* convert between bytes and pages */
++#define btop(b)         ((unsigned long)(b) >> PAGE_SHIFT)    /* rnd down */ 
++#define btopr(b)        btop(PAGE_ALIGN((unsigned long) b))   /* rnd up */
++#define ptob(p)               ((unsigned long)(p) << PAGE_SHIFT)
++
++/* round up sz to the nearest multiple of blk */
++#define roundup(sz,blk) ((blk) * ((sz) / (blk) + ((sz) % (blk) ? 1 : 0)))     
++
++/* send a signal to a process */
++#define psignal(pr,sig)       send_sig(sig,pr,0)
++
++/* microsecond delay */
++#define DELAY(us)     udelay(us)
++
++/* macro macros */
++#define MACRO_BEGIN     do {
++#define MACRO_END       } while (0)
++
++/* D-Unix compatable errno values */
++#define ESUCCESS        0
++#define EFAIL           255
++
++/* ASSERT(NO_LOCKS_HELD) will be a no-op */
++#define NO_LOCKS_HELD 1
++
++/* misc */
++typedef int           label_t;
++#define on_fault(ljp) ((ljp) == NULL)
++#define _NOTE(X)
++#define no_fault()    ((void) 0)
++#define panicstr      0
++
++/* return from system call is -EXXX on linux */
++#define set_errno(e)  (-(e))
++
++/* 
++ * BSD-style byte ops 
++ */
++
++#define bcmp(src1,src2,len)           memcmp(src1,src2,len)
++#define bzero(dst,len)                        memset(dst,0,len)
++#define bcopy(src,dst,len)            memcpy(dst,src,len)
++
++#define preemptable_start             do { long must_yield_at = lbolt + (hz/10);
++#define preemptable_end                       } while (0)
++#define preemptable_check()           do {\
++                                            if ((lbolt - must_yield_at) > 0)\
++                                          {\
++                                              preemptable_yield() ; \
++                                              must_yield_at = lbolt + (hz/10);\
++                                          }\
++                                      } while (0)
++
++#define preemptable_yield()           schedule()
++
++#define CURPROC()                       current
++#define CURTHREAD()                     current
++#define SUSER()                               suser()
++
++/* 64 bit IO operations on 32 bit intel cpus using MMX */
++#if defined(LINUX_I386)
++extern u64        qsnet_readq (volatile u64 *ptr);
++extern void       qsnet_writeq (u64 value, volatile u64 *ptr);
++
++#define readq(ptr)            qsnet_readq((void *) ptr)
++#define writeq(val,ptr)               qsnet_writeq(val, (void *)ptr)
++#endif
++
++/*
++ * Memory barriers
++ */
++#ifndef mmiob
++#  define mmiob()                     mb()
++#endif
++
++/* 
++ * Exit handlers
++ */
++#define HANDLER_REGISTER(func,arg,flags)   xa_handler_register(func,arg,flags)
++#define HANDLER_UNREGISTER(func,arg,flags) xa_handler_unregister(func,arg,flags)
++
++/* 
++ * KMEM_GETPAGES and KMEM_ALLOC both call kmem_alloc, which 
++ * translates the call to kmalloc if < PAGE_SIZE, or vmalloc 
++ * if >= PAGE_SIZE.  vmalloc will always return a page-aligned 
++ * region rounded up to the nearest page, while kmalloc will 
++ * return bits and pieces of a page.
++ */
++
++#ifdef KMEM_DEBUG
++extern void          *qsnet_kmem_alloc_debug(int len, int sleep, int zerofill, char *file, int line);
++extern void           qsnet_kmem_free_debug(void *ptr, int len, char *file, int line);
++#define KMEM_ALLOC(ptr,type,len,sleep) \
++      { KMEM_ASSERT(sleep); (ptr)=(type)qsnet_kmem_alloc_debug(len,sleep,0,__FILE__,__LINE__); }
++#define KMEM_ZALLOC(ptr,type,len,sleep) \
++      { KMEM_ASSERT(sleep); (ptr)=(type)qsnet_kmem_alloc_debug(len,sleep,1,__FILE__,__LINE__); }
++
++#define KMEM_FREE(ptr,len)              qsnet_kmem_free_debug((void *)ptr,len,__FILE__,__LINE__)
++
++#else
++
++extern void          *qsnet_kmem_alloc(int len, int sleep, int zerofill);
++extern void           qsnet_kmem_free(void *ptr, int len);
++
++#define KMEM_ALLOC(ptr,type,len,sleep) \
++      { KMEM_ASSERT(sleep); (ptr)=(type)qsnet_kmem_alloc(len,sleep,0); }
++#define KMEM_ZALLOC(ptr,type,len,sleep) \
++      { KMEM_ASSERT(sleep); (ptr)=(type)qsnet_kmem_alloc(len,sleep,1); }
++
++#define KMEM_FREE(ptr,len)              qsnet_kmem_free((void *)ptr,len)
++
++#endif
++extern void       qsnet_kmem_display(void *handle);
++extern physaddr_t kmem_to_phys(void *ptr);
++
++#define KMEM_ASSERT(sleep)             ASSERT(!(in_interrupt() && sleep))
++
++
++#define KMEM_GETPAGES(ptr,type,pgs,sleep) KMEM_ZALLOC(ptr,type,ptob(pgs),sleep)
++#define KMEM_FREEPAGES(ptr,pgs)         KMEM_FREE(ptr,ptob(pgs));
++
++/*
++ * Copying from user space -> kernel space (perms checked)
++ */
++#define copyin(up,kp,size)            copy_from_user(kp,up,size)
++#define copyin_noerr(up,kp,size)      copy_from_user(kp,up,size)
++
++/* get_user() gets xfer width right */
++#define fulinux(ret, up)              (get_user(ret, (up)) == 0 ? ret : -1)
++#define fulinuxp(ret, up)             (get_user(ret, (up)) == 0 ? ret : NULL)
++
++extern __inline__ int fubyte    (u8  *up) { u8  ret;   return fulinux(ret, up);}
++extern __inline__ int fusword   (u16 *up) { u16 ret;   return fulinux(ret, up);}
++extern __inline__ int fuword    (u32 *up) { u32 ret;   return fulinux(ret, up);}
++#if BITS_PER_LONG > 32
++extern __inline__ u64 fulonglong(u64 *up) { u64 ret;   return fulinux(ret, up);}
++#else
++extern __inline__ u64 fulonglong(u64 *up) { return ((u64) fuword((u32 *)up) | (((u64) fuword(((u32 *)up)+1))<<32)); }
++#endif
++extern __inline__ void *fuptr (void **up) { void *ret; return fulinuxp(ret,up);}
++
++#define fubyte_noerr(up)              fubyte(up)
++#define fusword_noerr(up)             fusword(up)
++#define fuword_noerr(up)              fuword(up)
++#define fulonglong_noerr(up)          fulonglong(up)
++#define fuptr_noerr(up)                       fuptr(up)
++
++extern __inline__ int copyinstr(char *up, char *kp, int max, int *size)
++{ 
++      for (*size = 1; *size <= max; (*size)++) {
++              if (get_user(*kp, up++) != 0)
++                      return EFAULT;  /* bad user space addr */
++              if (*kp++ == '\0')
++                      return 0;       /* success */
++      }
++      *size = max;
++      return ENAMETOOLONG;            /* runaway string */
++}
++ 
++/*
++ * Copying from kernel space -> user space (perms checked)
++ */
++
++#define copyout(kp,up,size)           copy_to_user(up,kp,size)
++#define copyout_noerr(kp,up,size)     copy_to_user(up,kp,size)
++
++/* put_user() gets xfer width right */
++#define sulinux(val, up)              (put_user(val, (up)) == 0 ? 0 : -1)
++
++extern __inline__ int subyte    (u8  *up, u8  val) { return sulinux(val, up); }
++extern __inline__ int susword   (u16 *up, u16 val) { return sulinux(val, up); }
++extern __inline__ int suword    (u32 *up, u32 val) { return sulinux(val, up); }
++#if BITS_PER_LONG > 32
++extern __inline__ int sulonglong(u64 *up, u64 val) { return sulinux(val, up); }
++#else
++extern __inline__ int sulonglong(u64 *up, u64 val) { return (suword((u32 *) up, (u32) val) == 0 ? 
++                                                           suword(((u32 *) up)+1, (u32) (val >> 32)) : -1); }
++#endif
++extern __inline__ int suptr   (void **up,void *val){ return sulinux(val, up); }
++
++#define subyte_noerr(up,val)          subyte(up,val)  
++#define susword_noerr(up,val)         susword(up,val) 
++#define suword_noerr(up,val)          suword(up,val)  
++#define sulonglong_noerr(up,val)      sulonglong(up,val)      
++#define suptr_noerr(up,val)           suptr(up,val)   
++
++/*
++ * /proc/qsnet interface
++ */
++extern inline int
++str_append(char *buf, char *add, int size)
++{
++#define TRUNC_MSG      "[Output truncated]\n"
++      int full = 0;
++      int max = size - strlen(TRUNC_MSG) - strlen(add) - 1;
++
++      if (strlen(buf) > max) {
++              strcat(buf, TRUNC_MSG);
++              full = 1;
++      } else
++              strcat(buf, add);
++      return full;
++}
++
++/* Spinlocks */
++#define spin_lock_destroy(l)          ((void) 0)
++
++/* Complex - Reader/Writer locks - we added <linux/crwlock.h> */
++typedef crwlock_t                     krwlock_t;
++#define krwlock_init(l)                       crwlock_init(l)
++#define krwlock_destroy(l)            crwlock_destroy(l)
++#define krwlock_write(l)              crwlock_write(l)
++#define krwlock_read(l)                       crwlock_read(l)
++#define krwlock_done(l)                       crwlock_done(l)
++#define krwlock_is_locked(l)          crwlock_held(l)
++#define krwlock_is_write_locked(l)    crwlock_write_held(l)
++#define krwlock_is_read_locked(l)     crwlock_read_held(l)
++
++/*
++ * Timeouts - Solaris style.
++ */
++typedef struct timer_list timer_fn_t;
++
++extern inline void
++schedule_timer_fn(timer_fn_t *timer, void (*fun)(void *), void *arg, long hz_delay)
++{
++      init_timer(timer);
++
++      timer->function = (void (*)(unsigned long)) fun;
++      timer->data     = (unsigned long) arg;
++      timer->expires  = jiffies + hz_delay;
++
++      add_timer(timer);
++}
++
++/* returns 1 if timer_fn was cancelled */
++extern inline int
++cancel_timer_fn(timer_fn_t *timer)
++{
++    return (del_timer_sync(timer));
++}
++
++extern inline int
++timer_fn_queued(timer_fn_t *timer)
++{
++    return (timer_pending (timer));
++}
++/*
++ * Hold/release CPU's.
++ */
++
++extern void   cpu_hold_all(void);
++extern void   cpu_release_all(void);
++#define CAPTURE_CPUS()                cpu_hold_all()
++#define RELEASE_CPUS()                cpu_release_all()
++
++#define IASSERT ASSERT
++
++#endif /* __QSNET_KERNEL_LINUX_H */
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.21/include/qsnet/kpte.h
+===================================================================
+--- linux-2.4.21.orig/include/qsnet/kpte.h     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/qsnet/kpte.h  2005-06-01 23:12:54.753415696 -0400
+@@ -0,0 +1,107 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2004 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __QSNET_KPTE_H
++#define __QSNET_KPTE_H
++
++#ident "@(#)$Id: kpte.h,v 1.1.2.1 2004/11/02 10:45:29 david Exp $ $Name: QSNETMODULES-4-30_20050128 $"
++/*      $Source: /cvs/master/quadrics/qsnet/kpte.h,v $*/
++
++#include <qsnet/autoconf.h>
++
++#ifdef NO_RMAP
++#     define pte_offset_kernel pte_offset
++#     define pte_offset_map    pte_offset
++#       define pte_unmap(A)      do { ; } while (0)
++#endif
++
++/* 
++ * Pte stuff
++ */
++static __inline__ struct mm_struct *
++get_kern_mm(void)
++{
++        return &init_mm;
++}
++
++static __inline__ pte_t *
++find_pte_map(struct mm_struct *mm, unsigned long vaddr)
++{
++        pgd_t *pgd;
++        pmd_t *pmd;
++      pte_t *ptep;
++
++/* XXXX - handle hugh tlb code */
++      pgd = pgd_offset(mm, vaddr);
++      if (pgd_none(*pgd) || pgd_bad(*pgd))
++              goto out;
++      
++      pmd = pmd_offset(pgd, vaddr);
++      if (pmd_none(*pmd) || pmd_bad (*pmd))
++              goto out;
++
++      ptep = pte_offset_map (pmd, vaddr);
++      if (! ptep)
++              goto out;
++      
++      if (pte_present (*ptep))
++              return ptep;
++
++      pte_unmap (ptep);
++out:
++      return NULL;
++}
++
++static __inline__ pte_t *
++find_pte_kernel(unsigned long vaddr)
++{
++        pgd_t *pgd;
++        pmd_t *pmd;
++      pte_t *pte;
++
++      pgd = pgd_offset_k(vaddr);
++      if (pgd && !pgd_none(*pgd)) {
++          pmd = pmd_offset(pgd, vaddr);
++          if (pmd && pmd_present(*pmd)) {
++              pte = pte_offset_kernel(pmd, vaddr);
++              if (pte && pte_present(*pte))
++                  return (pte);
++          }
++      }
++      return (NULL);
++}
++
++static __inline__ physaddr_t
++pte_phys(pte_t pte)
++{
++#if defined(LINUX_ALPHA)
++      /* RedHat 7.1 2.4.3-12 
++       * They have now enabled Monster windows on Tsunami
++       * and so can use the Main's phys pte value 
++       */
++      return (pte_val(pte) >> (32-PAGE_SHIFT));
++#elif defined(LINUX_I386)
++      return (pte_val(pte) & ~((1 << PAGE_SHIFT)-1));
++#elif defined(LINUX_SPARC)
++      return (pte_val(pte) & _PAGE_PADDR);
++#elif defined(LINUX_IA64)
++      return (pte_val(pte) & _PFN_MASK);
++#elif defined(LINUX_X86_64)
++      return (pte_val(pte) & ~((1 << PAGE_SHIFT)-1) & ~_PAGE_NX);
++#else
++#error Unknown architecture
++#endif
++}
++
++#endif /* __QSNET_KPTE_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/include/qsnet/kthread.h
+===================================================================
+--- linux-2.4.21.orig/include/qsnet/kthread.h  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/qsnet/kthread.h       2005-06-01 23:12:54.754415544 -0400
+@@ -0,0 +1,71 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2004 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __QSNET_KTHREAD_H
++#define __QSNET_KTHREAD_H
++
++#ident "@(#)$Id: kthread.h,v 1.1 2004/10/28 11:50:29 david Exp $ $Name: QSNETMODULES-4-30_20050128 $"
++/*      $Source: /cvs/master/quadrics/qsnet/kthread.h,v $*/
++
++#include <qsnet/autoconf.h>
++
++/* 
++ * kernel threads 
++ */
++extern __inline__ void
++kernel_thread_init(char *comm)
++{
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
++#ifndef NO_NPTL
++#     define sigmask_lock                     sighand->siglock
++#endif
++      lock_kernel();
++      daemonize();
++        reparent_to_init();
++
++        /* avoid getting signals */
++        spin_lock_irq(&current->sigmask_lock);
++        flush_signals(current);
++        sigfillset(&current->blocked);
++      
++#ifdef NO_NPTL
++        recalc_sigpending(current);
++#else
++        recalc_sigpending();
++#endif
++
++        spin_unlock_irq(&current->sigmask_lock);
++
++      /* set our name for identification purposes */
++      strncpy(current->comm, comm, sizeof(current->comm));
++
++      unlock_kernel();
++#else
++      daemonize(comm);
++#endif
++}
++
++extern __inline__ void *
++kernel_thread_wrap(caddr_t stk, int stksize, void (*proc)(void *), void *arg)
++{
++        ASSERT(stk == NULL && stksize == 0);
++        kernel_thread((int (*)(void *))proc, arg, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
++        return (void *)1; /* non-null value */
++}
++
++#define kernel_thread_create(proc,arg)  kernel_thread_wrap(NULL,0,(void (*)(void *))proc,arg)
++#define kernel_thread_exit()          ((void) 0)
++#define kernel_thread_become_highpri()        ((void) 0)
++
++#endif /* __QSNET_KTHREAD_H */
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.21/include/qsnet/list.h
+===================================================================
+--- linux-2.4.21.orig/include/qsnet/list.h     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/qsnet/list.h  2005-06-01 23:12:54.754415544 -0400
+@@ -0,0 +1,80 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Limited.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: list.h,v 1.5 2003/10/27 13:55:33 david Exp $"
++/*      $Source: /cvs/master/quadrics/qsnet/list.h,v $*/
++
++#ifndef __QSNET_LIST_H
++#define __QSNET_LIST_H
++
++/* Implementation of doubly linked lists - compatible with linux */
++struct list_head 
++{
++    struct list_head *next;
++    struct list_head *prev;
++};
++
++#if !defined(LINUX)
++#if ! defined( offsetof ) 
++#define offsetof(T,F) ((int )&(((T *)0)->F))
++#endif
++
++#define LIST_HEAD_INIT(name) { &(name), &(name) }
++
++#define LIST_HEAD(name) \
++      struct list_head name = LIST_HEAD_INIT(name)
++#endif
++
++#define list_entry(ptr, type, off) \
++      ((type *) ((unsigned long)(ptr) - offsetof (type,off)))
++
++#define INIT_LIST_HEAD(list) \
++MACRO_BEGIN \
++      (list)->next = (list)->prev = (list); \
++MACRO_END
++
++#define list_add(new, list) \
++MACRO_BEGIN \
++      (list)->next->prev = (new); \
++      (new)->next = (list)->next; \
++      (new)->prev = (list); \
++      (list)->next = (new); \
++MACRO_END
++
++#define list_add_tail(new, list) \
++MACRO_BEGIN \
++      (list)->prev->next = new; \
++      (new)->prev = (list)->prev; \
++      (new)->next = (list); \
++      (list)->prev = (new); \
++MACRO_END
++
++#define list_del(entry) \
++MACRO_BEGIN \
++      (entry)->prev->next = (entry)->next; \
++      (entry)->next->prev = (entry)->prev; \
++MACRO_END
++
++#define list_del_init(entry) \
++MACRO_BEGIN \
++      (entry)->prev->next = (entry)->next; \
++      (entry)->next->prev = (entry)->prev; \
++      (entry)->next = (entry)->prev = (entry); \
++MACRO_END
++
++#define list_empty(list) \
++      ((list)->next == (list))
++
++#define list_for_each(pos,list) \
++      for (pos = (list)->next; pos != (list); \
++           pos = (pos)->next)
++
++#define list_for_each_safe(pos,n,list) \
++      for (pos = (list)->next, n = (pos)->next; pos != (list); \
++           pos = n, n = (pos)->next)
++
++#endif /* __QSNET_LIST_H */
+Index: linux-2.4.21/include/qsnet/mutex.h
+===================================================================
+--- linux-2.4.21.orig/include/qsnet/mutex.h    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/qsnet/mutex.h 2005-06-01 23:12:54.754415544 -0400
+@@ -0,0 +1,91 @@
++/*
++ *    Copyright (C) 2000  Regents of the University of California
++ *
++ *    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.
++ *
++ *    This program is distributed in the hope that it will be useful,
++ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *    GNU General Public License for more details.
++ *
++ *    You should have received a copy of the GNU General Public License
++ *    along with this program; if not, write to the Free Software
++ *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ *
++ */
++
++#if   !defined(_LINUX_MUTEX_H)
++#define       _LINUX_MUTEX_H
++#if   defined(__KERNEL__)
++
++#include <asm/smp.h>
++#include <linux/spinlock.h>
++#include <asm/semaphore.h>
++#include <qsnet/debug.h>
++#include <linux/interrupt.h>
++#include <linux/version.h>
++
++#define PID_NONE      0
++
++typedef struct
++{
++    struct semaphore sem;
++    pid_t          holder;
++} kmutex_t;
++
++extern __inline__ void
++kmutex_init (kmutex_t *l)
++{
++#if   LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
++    l->sem = MUTEX;
++#else
++    init_MUTEX(&l->sem);
++#endif
++    l->holder = PID_NONE;
++}
++
++extern __inline__ void
++kmutex_destroy (kmutex_t *l) 
++{
++    ASSERT (l->holder == PID_NONE); 
++}
++
++extern __inline__ void
++kmutex_lock (kmutex_t *l) 
++{ 
++    ASSERT(l->holder != current->pid);
++    down (&l->sem);
++    l->holder = current->pid; 
++}
++
++extern __inline__ void
++kmutex_unlock (kmutex_t *l) 
++{
++    ASSERT(l->holder == current->pid);
++
++    l->holder = PID_NONE;
++    up (&l->sem);
++}
++
++extern __inline__ int
++kmutex_trylock (kmutex_t *l) 
++{
++    if (down_trylock (&l->sem) == 0) 
++    {
++      l->holder = current->pid;
++      return (1);
++    }
++    return (0);
++}
++
++extern __inline__ int
++kmutex_is_locked (kmutex_t *l) 
++{
++    return (l->holder == current->pid);
++}
++
++#endif /* __KERNEL__ */
++#endif /* _LINUX_MUTEX_H */
+Index: linux-2.4.21/include/qsnet/procfs_linux.h
+===================================================================
+--- linux-2.4.21.orig/include/qsnet/procfs_linux.h     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/qsnet/procfs_linux.h  2005-06-01 23:12:54.755415392 -0400
+@@ -0,0 +1,234 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __PROCFS_LINUX_H
++#define __PROCFS_LINUX_H
++
++#ident "$Id: procfs_linux.h,v 1.6.2.6 2004/12/06 17:36:24 robin Exp $"
++/*      $Source: /cvs/master/quadrics/qsnet/procfs_linux.h,v $ */
++
++#if defined(__KERNEL__)
++
++#include <qsnet/kernel_linux.h>
++#include <qsnet/autoconf.h>
++#include <linux/proc_fs.h>
++
++extern gid_t qsnet_procfs_gid;
++
++/* borrowed from fs/proc/proc_misc - helper for proc_read_int */
++static inline int 
++qsnet_proc_calc_metrics(char *page, char **start, off_t off, int count, int *eof, int len)
++{
++      if (len <= off+count) *eof = 1;
++      *start = page + off;
++      len -= off;
++      if (len>count) len = count;
++      if (len<0) len = 0;
++      return len;
++}
++
++static inline int
++qsnet_proc_write_int(struct file *file, const char *buf, unsigned long count, void *data)
++{
++      char tmpbuf[16];
++      int  res = count;
++      
++      if (count > sizeof(tmpbuf) - 1)
++              return (-EINVAL);
++      
++      MOD_INC_USE_COUNT;
++      if (copy_from_user(tmpbuf, buf, count))
++              res = -EFAULT;
++      else
++      {
++              tmpbuf[count] = '\0'; 
++              *(int *)data = simple_strtoul(tmpbuf, NULL, 0);
++      }
++      MOD_DEC_USE_COUNT;
++      
++      return (res);
++}
++
++static inline int
++qsnet_proc_read_int(char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++      int len, res;
++      
++      MOD_INC_USE_COUNT;
++      
++      len = sprintf(page, "%d\n", *(int *)data);
++      res = qsnet_proc_calc_metrics(page, start, off, count, eof, len);
++      
++      MOD_DEC_USE_COUNT;
++      return (res);
++}
++
++static inline struct proc_dir_entry *
++qsnet_proc_register_int(struct proc_dir_entry *dir, char *path, int *var, int read_only)
++{
++      struct proc_dir_entry *p;
++      
++      p = create_proc_entry(path, read_only ? S_IRUGO : S_IRUGO|S_IWUSR|S_IWGRP, dir);
++      if (p) {
++              if (! read_only) 
++                      p->write_proc = qsnet_proc_write_int;
++              p->read_proc  = qsnet_proc_read_int;
++              p->data       = var;
++              p->owner      = THIS_MODULE;
++              p->gid        = qsnet_procfs_gid;
++      }
++      return p;
++}
++
++static inline int
++qsnet_proc_write_hex(struct file *file, const char *buf, unsigned long count, void *data)
++{
++      char tmpbuf[16];
++      int  res = count;
++      
++      if (count > sizeof(tmpbuf) - 1)
++              return (-EINVAL);
++      
++      MOD_INC_USE_COUNT;
++      if (copy_from_user(tmpbuf, buf, count))
++              res = -EFAULT;
++      else
++      {
++              tmpbuf[count] = '\0'; 
++              *(int *)data = simple_strtoul(tmpbuf, NULL, 0);
++      }
++      MOD_DEC_USE_COUNT;
++      
++      return (res);
++}
++
++static inline int
++qsnet_proc_read_hex(char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++      int len, res;
++      
++      MOD_INC_USE_COUNT;
++      
++      len = sprintf(page, "0x%x\n", *(int *)data);
++      res = qsnet_proc_calc_metrics(page, start, off, count, eof, len);
++      
++      MOD_DEC_USE_COUNT;
++      return (res);
++}
++
++static inline struct proc_dir_entry *
++qsnet_proc_register_hex(struct proc_dir_entry *dir, char *path, int *var, int read_only)
++{
++      struct proc_dir_entry *p;
++      
++      p = create_proc_entry(path, read_only ? S_IRUGO : S_IRUGO|S_IWUSR|S_IWGRP, dir);
++      if (p) {
++              if (! read_only) 
++                      p->write_proc = qsnet_proc_write_hex;
++              p->read_proc  = qsnet_proc_read_hex;
++              p->data       = var;
++              p->owner      = THIS_MODULE;
++              p->gid        = qsnet_procfs_gid;
++      }
++      return p;
++}
++
++#define QSNET_PROC_STR_LEN_MAX ((int)256)
++
++static inline int
++qsnet_proc_write_str(struct file *file, const char *buf, unsigned long count, void *data)
++{
++      int  res = count;
++      
++      if (count > (QSNET_PROC_STR_LEN_MAX - 1))
++              return (-EINVAL);
++      
++      MOD_INC_USE_COUNT;
++      if (copy_from_user((char *)data, buf, count))
++              res = -EFAULT;
++      else
++      {
++              ((char *)data)[count] = '\0'; 
++              /* remove linefeed */
++              if ( (count) && (((char *)data)[count -1] == '\n'))
++                      ((char *)data)[count -1] = '\0';
++      }
++      MOD_DEC_USE_COUNT;
++      
++      return (res);
++}
++
++static inline int
++qsnet_proc_read_str(char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++      int len, res;
++      
++      if ( strlen(data) > (count + 1))
++              return (-EINVAL);       
++
++      MOD_INC_USE_COUNT;
++      
++      /* cant output too much */
++      if ( strlen(data) > (count + 1))
++      {
++              MOD_DEC_USE_COUNT;
++              return (-EINVAL);       
++      }
++
++
++      len = sprintf(page, "%s\n", (char *)data);
++      if (len > count)
++      {
++              MOD_DEC_USE_COUNT;
++              return (-EINVAL);       
++      }
++
++      res = qsnet_proc_calc_metrics(page, start, off, count, eof, len);
++      
++      MOD_DEC_USE_COUNT;
++      return (res);
++}
++
++static inline struct proc_dir_entry *
++qsnet_proc_register_str(struct proc_dir_entry *dir, char *path, char *var, int read_only)
++{
++      struct proc_dir_entry *p;
++      
++      p = create_proc_entry(path, read_only ? S_IRUGO : S_IRUGO|S_IWUSR|S_IWGRP, dir);
++      if (p) {
++              if (! read_only) 
++                      p->write_proc = qsnet_proc_write_str;
++              p->read_proc  = qsnet_proc_read_str;
++              p->data       = var;
++              p->owner      = THIS_MODULE;
++              p->gid        = qsnet_procfs_gid;
++      }
++      return p;
++}
++
++extern struct proc_dir_entry *qsnet_procfs_root; 
++extern struct proc_dir_entry *qsnet_procfs_config;
++
++#ifdef NO_PDE
++static inline struct proc_dir_entry *PDE(const struct inode *inode)
++{
++    return inode->u.generic_ip;
++}
++#endif
++#endif /* __KERNEL__ */
++
++#define QSNET_PROCFS_IOCTL      "/proc/qsnet/ioctl"
++#define QSNET_PROCFS_KMEM_DEBUG "/proc/qsnet/kmem_debug"
++#define QSNET_PROCFS_VERSION    "/proc/qsnet/version"
++
++#endif /* __PROCFS_LINUX_H */
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.21/include/qsnet/pthread.h
+===================================================================
+--- linux-2.4.21.orig/include/qsnet/pthread.h  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/qsnet/pthread.h       2005-06-01 23:12:54.755415392 -0400
+@@ -0,0 +1,59 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++/* $Id: pthread.h,v 1.5 2004/06/07 10:47:06 addy Exp $ */
++/*             $Source: /cvs/master/quadrics/qsnet/pthread.h,v $*/
++
++#ifndef _CONFIG_PTHREAD_H
++#define _CONFIG_PTHREAD_H
++
++#ifdef        __cplusplus
++extern "C" {
++#endif
++
++#if defined(__ELAN__)
++
++/* No pthread support on Elan co-processor */
++
++#define MUTEX                   unsigned long long
++#define MUTEX_INIT(X)         ;
++#define       MUTEX_LOCK(X)           ;
++#define       MUTEX_UNLOCK(X)         ;
++
++#else
++#if defined(DIGITAL_UNIX)
++#include <tis.h>
++#define MUTEX                 pthread_mutex_t
++#define MUTEX_INIT(X)         tis_mutex_init(X)
++#define       MUTEX_LOCK(X)           tis_mutex_lock(X)
++#define       MUTEX_UNLOCK(X)         tis_mutex_unlock(X)
++#define       MUTEX_TRYLOCK(X)        (tis_mutex_trylock(X) == 0)
++
++#else /* Linux... */
++
++/* Use standard pthread calls */
++#include <pthread.h>
++#define MUTEX                 pthread_mutex_t
++#define MUTEX_INIT(X)         pthread_mutex_init(X, NULL)
++#define       MUTEX_LOCK(X)           pthread_mutex_lock(X)
++#define       MUTEX_UNLOCK(X)         pthread_mutex_unlock(X)
++#define       MUTEX_TRYLOCK(X)        (pthread_mutex_trylock(X) == 0)
++
++#endif /* DIGITAL_UNIX */
++#endif /* __ELAN__ */
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* _CONFIG_PTHREAD_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.4.21/include/qsnet/statsformat.h
+===================================================================
+--- linux-2.4.21.orig/include/qsnet/statsformat.h      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/qsnet/statsformat.h   2005-06-01 23:12:54.756415240 -0400
+@@ -0,0 +1,25 @@
++#ifndef _QSNET_STATSFORMAT_H
++#define _QSNET_STATSFORMAT_H
++
++#ident "$Id: statsformat.h,v 1.2 2003/05/22 19:37:14 addy Exp $"
++/*      $Source: /cvs/master/quadrics/qsnet/statsformat.h,v $*/
++
++#include <qsnet/config.h>
++
++/*
++ * format of an Elan stats record
++ *
++ * type    char(8), type of statistic, e.g. FPAGE, ELAN3, TPORT
++ * time    uint64, 10 digits, time in millisecs since counters initialised
++ * device  uint, 2 digits, Elan device id
++ * name    char(32), name of the statistic
++ * value   uint64, current value of statistic
++ */
++    
++#ifdef _ILP32
++#define ELAN_STATSFORMAT "%-8s %10llu %2d %-32s %llu\n"
++#else
++#define ELAN_STATSFORMAT "%-8s %10lu %2d %-32s %lu\n"
++#endif
++
++#endif
+Index: linux-2.4.21/include/qsnet/types.h
+===================================================================
+--- linux-2.4.21.orig/include/qsnet/types.h    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/qsnet/types.h 2005-06-01 23:12:54.756415240 -0400
+@@ -0,0 +1,90 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __QSNET_TYPES_H
++#define __QSNET_TYPES_H
++
++#ident "$Id: types.h,v 1.16 2003/08/01 16:21:38 addy Exp $"
++/*      $Source: /cvs/master/quadrics/qsnet/types.h,v $*/
++
++/*
++ * Include typedefs for ISO/IEC 9899:1990 standard types
++ *
++ *
++ *    The following integer typedefs are used:
++ *
++ *    int8_t, int16_t, int32_t, int64_t, intptr_t
++ *    uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t
++ *    uchar_t, ushort_t, uint_t, ulong_t
++ *
++ *    <sys/types.h> also defines the following:
++ *    u_char, u_short, u_int, u_long, caddr_t
++ */
++
++#include <qsnet/config.h>
++
++#if defined(SOLARIS) && defined(__KERNEL__)
++#  include <sys/inttypes.h>
++#endif
++
++#if defined(SOLARIS) && !defined(__KERNEL__)
++#  include <inttypes.h>
++#  include <sys/types.h>
++#endif
++
++#if defined(DIGITAL_UNIX) && defined(__KERNEL__)
++#  include <sys/bitypes.h>
++#endif
++
++#if defined(DIGITAL_UNIX) && !defined(__KERNEL__)
++#  include <inttypes.h>
++#  include <sys/types.h>
++#endif
++
++#if defined(LINUX) && defined(__KERNEL__)
++#  include <linux/types.h>
++#endif
++
++#if defined(LINUX) && !defined(__KERNEL__)
++#  include <stdint.h>
++#  include <inttypes.h>
++#  include <sys/types.h>
++
++typedef unsigned char  uchar_t;
++typedef unsigned short ushort_t;
++typedef unsigned int   uint_t;
++typedef unsigned long  ulong_t;
++#endif
++
++#if defined(QNX)
++#  include <inttypes.h>
++#  include <sys/types.h>
++#endif
++
++/* Define a type that will represent a Main CPU pointer
++ * on both the Main and the Elan
++ */
++#ifdef __ELAN__
++
++#if defined(_MAIN_LP64)
++#define QSNET_MAIN_PTR        uint64_t
++#else
++#define QSNET_MAIN_PTR        uint32_t
++#endif
++
++#else
++
++#ifdef _LP64
++#define QSNET_MAIN_PTR        uint64_t
++#else
++#define QSNET_MAIN_PTR        uint32_t
++#endif
++
++#endif
++
++
++#endif /* __QSNET_TYPES_H */
+Index: linux-2.4.21/include/qsnet/workarounds.h
+===================================================================
+--- linux-2.4.21.orig/include/qsnet/workarounds.h      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/qsnet/workarounds.h   2005-06-01 23:12:54.756415240 -0400
+@@ -0,0 +1,24 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef _QSNET_WORKAROUNDS_H
++#define _QSNET_WORKAROUNDS_H
++
++#ident "$Id: workarounds.h,v 1.11 2002/08/09 11:15:55 addy Exp $"
++/*      $Source: /cvs/master/quadrics/qsnet/workarounds.h,v $ */
++
++/* Elan workarounds */
++#undef  ELAN_REVA_SUPPORTED   /* rev a elans no longer supported. */
++#undef  ELITE_REVA_SUPPORTED  /* removed since RMS disables broadcast on rev A elites. */
++#define ELAN_REVB_BUG_1
++/* WORKAROUND for GNAT hw-elan3/3263 */
++#define ELAN_REVB_BUG_2
++
++/* WORKAROUND for GNATs ic-elan3/3637 & ic-elan3/3550 */
++#define ELAN_REVB_BUG_3
++
++#endif /* _QSNET_WORKAROUNDS_H */
+Index: linux-2.4.21/include/rms/rmscall.h
+===================================================================
+--- linux-2.4.21.orig/include/rms/rmscall.h    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/rms/rmscall.h 2005-06-01 23:12:54.757415088 -0400
+@@ -0,0 +1,144 @@
++/*
++ * Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ * For licensing information please see the supplied COPYING file
++ *
++ * rmscall.h:  user interface to rms kernel module
++ *
++ * $Id: rmscall.h,v 1.25 2004/05/14 08:55:57 duncan Exp $
++ * $Source: /cvs/master/quadrics/rmsmod/rmscall.h,v $
++ *
++ */
++
++#ifndef RMSCALL_H_INCLUDED
++#define RMSCALL_H_INCLUDED 1
++
++#ident        "$Id: rmscall.h,v 1.25 2004/05/14 08:55:57 duncan Exp $"
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++/*
++ * flags for rms_fork_register
++ *
++ * RMS_IOF is not in a public header file 
++ */
++#define RMS_IOF 1             /* inherit on fork */
++
++#ifndef __KERNEL__
++#include <sys/types.h>
++#endif
++
++#include <qsnet/types.h>
++#include <elan/capability.h>
++
++#define MAXCOREPATHLEN 32
++
++#if defined(SOLARIS)
++typedef long long rmstime_t;
++#else  /* DIGITAL_UNIX */
++typedef long rmstime_t;
++#endif
++
++typedef enum {
++    
++    PRG_RUNNING  = 0x01,      /* program is running                  */
++    PRG_ZOMBIE   = 0x02,      /* last process on a node has exited   */
++    PRG_NODE     = 0x04,      /* stats are complete for this node    */
++    PRG_KILLED   = 0x08,      /* program was killed                  */
++    PRG_SUSPEND  = 0x10               /* program is suspended                */
++
++} PRGSTATUS_FLAGS;
++
++/*
++ * program time statistics extended in version 5 of the kernel module
++ */
++typedef struct {
++    rmstime_t etime;          /* elapsed cpu time (milli-secs)       */
++    rmstime_t atime;          /* allocated cpu time (cpu milli-secs) */
++    rmstime_t utime;          /* user cpu time (cpu milli-secs)      */
++    rmstime_t stime;          /* system cpu time (cpu milli-secs)    */
++    int ncpus;                        /* number of cpus allocated            */
++    int flags;                        /* program status flags                */
++    int mem;                  /* max memory size in MBytes           */
++    int pageflts;             /* number of page faults               */
++    rmstime_t memint;         /* memory integral                     */
++} prgstats_old_t;
++
++typedef struct {
++    uint64_t etime;           /* elapsed cpu time (milli-secs)       */
++    uint64_t atime;           /* allocated cpu time (cpu milli-secs) */
++    uint64_t utime;           /* user cpu time (cpu milli-secs)      */
++    uint64_t stime;           /* system cpu time (cpu milli-secs)    */
++    uint64_t pageflts;                /* number of page faults               */
++    uint64_t memint;          /* memory integral                     */
++    uint64_t ebytes;          /* data transferred by the Elan(s)     */
++    uint64_t exfers;          /* number of Elan data transfers       */
++    uint64_t spare64[4];      /* expansion space                     */
++    int ncpus;                        /* number of cpus allocated            */
++    int flags;                        /* program status flags                */
++    int mem;                  /* max memory size in MBytes           */
++    int spare32[5];             /* expansion space                     */
++} prgstats_t;
++
++int  rmsmod_init(void);
++void rmsmod_fini(void);
++
++int rms_setcorepath(caddr_t path);
++int rms_getcorepath(pid_t pid, caddr_t path, int maxlen);
++int rms_prgcreate(int id, uid_t uid, int cpus);
++int rms_prgdestroy(int id);
++int rms_prgids(int maxids, int *prgids, int *nprgs);
++int rms_prginfo(int id, int maxpids, pid_t *pids, int *nprocs);
++int rms_prgaddcap(int id, int index, ELAN_CAPABILITY *cap);
++
++int rms_prgsuspend(int id);
++int rms_prgresume(int id);
++int rms_prgsignal(int id, int signo);
++
++int rms_getprgid(pid_t pid, int *id);
++int rms_ncaps(int *ncaps);
++int rms_getcap(int index, ELAN_CAPABILITY *cap);
++int rms_mycap(int *index);
++int rms_setcap(int index, int ctx);
++int rms_prefcap(int nprocess, int *index);
++
++int   rms_prggetstats(int id, prgstats_t *stats);
++void  rms_accumulatestats(prgstats_t *total, prgstats_t *stats);
++char *rms_statsreport(prgstats_t *stats, char *buf);
++
++int rms_elaninitdone(int vp);
++int rms_prgelanpids(int id, int maxpids, int *vps, pid_t *pids, int *npids);
++int rms_setelanstats(int id, uint64_t ebytes, uint64_t exfers);
++
++int rms_setpset(int psid);
++int rms_getpset(int id, int *psid);
++int rms_modversion();
++
++#ifdef __cplusplus
++}
++#endif
++
++
++#if defined(__KERNEL__)
++
++int rms_init(void);
++int rms_fini(void);
++int rms_reconfigure(void);
++
++extern int rms_debug;
++
++#if 1
++#define DBG(x) do if (rms_debug) x ; while (0)
++#else
++#define DBG(x)
++#endif
++
++#endif
++
++#endif /* RMSCALL_H_INCLUDED */
++
++
++
++
+Index: linux-2.4.21/include/rms/rmsio.h
+===================================================================
+--- linux-2.4.21.orig/include/rms/rmsio.h      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/include/rms/rmsio.h   2005-06-01 23:12:54.757415088 -0400
+@@ -0,0 +1,185 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: rmsio.h,v 1.6 2004/05/14 08:55:57 duncan Exp $"
++/*      $Source: /cvs/master/quadrics/rmsmod/rmsio.h,v $*/
++
++
++#ifndef __RMSMOD_RMSIO_H
++#define __RMSMOD_RMSIO_H
++
++/* arg is corepath string */
++#define RMSIO_SETCOREPATH     _IOW ('r', 1, char)
++
++typedef struct rmsio_getcorepath_struct
++{
++    pid_t             pid;
++    char              *corepath;
++    int                       maxlen;
++} RMSIO_GETCOREPATH_STRUCT;
++#define RMSIO_GETCOREPATH     _IOW ('r', 2, RMSIO_GETCOREPATH_STRUCT)
++
++typedef struct rmsio_prgcreate_struct
++{
++    int                       id;
++    uid_t             uid;
++    int                       cpus;
++} RMSIO_PRGCREATE_STRUCT;
++#define RMSIO_PRGCREATE               _IOW ('r', 3, RMSIO_PRGCREATE_STRUCT)
++
++typedef struct rmsio_prginfo_struct
++{
++    int                       id;
++    int                       maxpids;
++    pid_t             *pids;
++    int                       *nprocs;
++} RMSIO_PRGINFO_STRUCT;
++#define RMSIO_PRGINFO         _IOW ('r', 4, RMSIO_PRGINFO_STRUCT)
++
++typedef struct rmsio_prgsignal_struct
++{
++    int                       id;
++    int                       signo;
++} RMSIO_PRGSIGNAL_STRUCT;
++#define RMSIO_PRGSIGNAL               _IOW ('r', 5, RMSIO_PRGSIGNAL_STRUCT)
++
++typedef struct rmsio_prgaddcap_struct
++{
++    int                       id;
++    int                       index;
++    ELAN_CAPABILITY   *cap;
++} RMSIO_PRGADDCAP_STRUCT;
++#define RMSIO_PRGADDCAP               _IOW ('r', 6, RMSIO_PRGADDCAP_STRUCT)
++typedef struct rmsio_setcap_struct
++{
++    int                       index;
++    int                       ctx;
++} RMSIO_SETCAP_STRUCT;
++#define RMSIO_SETCAP          _IOW ('r', 7, RMSIO_SETCAP_STRUCT)
++
++typedef struct rmsio_getcap_struct
++{
++    int                       index;
++    ELAN_CAPABILITY     *cap;
++} RMSIO_GETCAP_STRUCT;
++#define RMSIO_GETCAP          _IOW ('r', 8, RMSIO_GETCAP_STRUCT)
++
++typedef struct rmsio_getcap_struct32
++{
++    int                       index;
++    unsigned int        capptr;
++} RMSIO_GETCAP_STRUCT32;
++#define RMSIO_GETCAP32                _IOW ('r', 8, RMSIO_GETCAP_STRUCT32)
++
++/* arg is pointer to ncaps */
++#define RMSIO_NCAPS           _IOW ('r', 9, int)
++
++typedef struct rmsio_prggetstats_struct
++{
++    int                       id;
++    prgstats_old_t    *stats;
++} RMSIO_PRGGETSTATS_STRUCT;
++#define RMSIO_PRGGETSTATS     _IOW ('r', 10, RMSIO_PRGGETSTATS_STRUCT)
++
++/* arg is program id */
++#define RMSIO_PRGSUSPEND      _IOW ('r', 11, int)
++#define RMSIO_PRGRESUME               _IOW ('r', 12, int)
++#define RMSIO_PRGDESTROY      _IOW ('r', 13, int)
++
++typedef struct rmsio_getprgid_struct
++{
++    pid_t             pid;
++    int                       *id;
++} RMSIO_GETPRGID_STRUCT;
++#define RMSIO_GETPRGID                _IOW ('r', 14, RMSIO_GETPRGID_STRUCT)
++
++typedef struct rmsio_getprgid_struct32
++{
++    pid_t             pid;
++    unsigned int      idptr;
++} RMSIO_GETPRGID_STRUCT32;
++#define RMSIO_GETPRGID32      _IOW ('r', 14, RMSIO_GETPRGID_STRUCT32)
++
++/* arg is pointer to index */
++#define RMSIO_GETMYCAP                _IOW ('r', 15, int)
++
++typedef struct rmsio_prgids_struct
++{
++    int                       maxids;
++    int                       *prgids;
++    int                       *nprgs;
++} RMSIO_PRGIDS_STRUCT;
++#define RMSIO_PRGIDS          _IOW ('r', 16, RMSIO_PRGIDS_STRUCT)
++
++/* arg is pointer to vp */
++#define RMSIO_ELANINITDONE    _IOW ('r', 17, int)
++
++typedef struct rmsio_prgelanpids_struct
++{
++    int    id;
++    int    maxpids;
++    int   *vps;
++    int   *pids;
++    int   *npids;
++} RMSIO_PRGELANPIDS_STRUCT;
++#define RMSIO_PRGELANPIDS     _IOW ('r', 18, RMSIO_PRGELANPIDS_STRUCT)
++
++typedef struct rmsio_setpset_struct
++{
++    int    id;
++    int    psid;
++} RMSIO_SETPSET_STRUCT;
++#define RMSIO_SETPSET         _IOW ('r', 19, RMSIO_SETPSET_STRUCT)
++
++typedef struct rmsio_getpset_struct
++{
++    int    id;
++    int   *psid;
++} RMSIO_GETPSET_STRUCT;
++#define RMSIO_GETPSET         _IOW ('r', 20, RMSIO_GETPSET_STRUCT)
++
++/*
++ * have to pass a pointer to the stats, the switch
++ * statement goes wrong in the module of the size
++ * is too large
++ */
++typedef struct {
++    uint64_t ebytes;
++    uint64_t exfers;
++} elanstats_t;
++
++typedef struct rmsio_setelanstats_struct
++{
++    int    id;
++    elanstats_t *estats;
++} RMSIO_SETELANSTATS_STRUCT;
++#define RMSIO_SETELANSTATS      _IOW ('r', 21, RMSIO_SETELANSTATS_STRUCT)
++
++typedef struct rmsio_prggetstats2_struct
++{
++    int                       id;
++    prgstats_t                *stats;
++} RMSIO_PRGGETSTATS2_STRUCT;
++#define RMSIO_PRGGETSTATS2    _IOW ('r', 22, RMSIO_PRGGETSTATS2_STRUCT)
++
++typedef struct rmsio_modversion_struct
++{
++    int *version;
++} RMSIO_MODVERSION_STRUCT;
++#define RMSIO_MODVERSION      _IOW ('r', 23, RMSIO_MODVERSION_STRUCT)
++
++
++#endif /* __RMSMOD_RMSIO_H */
++
++
++
++
++
++
++
++
++
+Index: linux-2.4.21/ipc/shm.c
+===================================================================
+--- linux-2.4.21.orig/ipc/shm.c        2005-06-01 22:51:50.000000000 -0400
++++ linux-2.4.21/ipc/shm.c     2005-06-01 23:12:54.758414936 -0400
+@@ -723,6 +723,44 @@
+       return retval;
+ }
++/*
++ * Mark all segments created by this process for destruction
++ */
++asmlinkage int shm_cleanup ()
++{
++      int i;
++
++      down(&shm_ids.sem);
++
++      for(i = 0; i <= shm_ids.max_id; i++) {
++              struct shmid_kernel* shp;
++
++              shp = shm_lock(i);
++              if(shp!=NULL) {
++
++                  /* Mark this segment for destruction if we created it */
++                  if (current->pid == shp->shm_cprid)
++                  {
++                      /* Copy of IPC_RMID code */
++                      if (shp->shm_nattch){
++                              shp->shm_flags |= SHM_DEST;
++                              /* Do not find it any more */
++                              shp->shm_perm.key = IPC_PRIVATE;
++                      } else {
++                              shm_destroy(shp);
++                              continue;
++                      }
++                  }
++
++                  shm_unlock(i);
++              }
++      }
++
++      up(&shm_ids.sem);
++
++      return 0;
++}
++
+ #ifdef CONFIG_PROC_FS
+ static int sysvipc_shm_read_proc(char *buffer, char **start, off_t offset, int length, int *eof, void *data)
+ {
+Index: linux-2.4.21/kernel/exit.c
+===================================================================
+--- linux-2.4.21.orig/kernel/exit.c    2005-06-01 22:58:09.055062312 -0400
++++ linux-2.4.21/kernel/exit.c 2005-06-01 23:12:54.759414784 -0400
+@@ -19,6 +19,7 @@
+ #include <linux/file.h>
+ #include <linux/binfmts.h>
+ #include <linux/ptrace.h>
++#include <linux/ptrack.h>
+ #include <linux/mount.h>
+ #include <linux/process_timing.h>
+ #include <asm/uaccess.h>
+@@ -705,6 +706,10 @@
+       if (current->tux_info)
+               current->tux_exit();
+       acct_process(code);
++
++      /* Notify any ptrack callbacks of the process exit */
++      ptrack_call_callbacks(PTRACK_PHASE_EXIT, NULL);
++
+       if (isaudit(tsk))
+               audit_exit(tsk, code);
+       __exit_mm(tsk);
+Index: linux-2.4.21/kernel/fork.c
+===================================================================
+--- linux-2.4.21.orig/kernel/fork.c    2005-06-01 22:58:09.055062312 -0400
++++ linux-2.4.21/kernel/fork.c 2005-06-01 23:12:54.760414632 -0400
+@@ -14,6 +14,7 @@
+ #include <linux/config.h>
+ #include <linux/slab.h>
+ #include <linux/init.h>
++#include <linux/ptrack.h>
+ #include <linux/unistd.h>
+ #include <linux/smp_lock.h>
+ #include <linux/module.h>
+@@ -308,6 +309,7 @@
+       /* unlimited stack is larger than TASK_SIZE */
+       mm->non_executable_cache = NON_EXECUTABLE_CACHE(current);
+       mm->pgd = pgd_alloc(mm);
++      mm->coproc_ops = NULL;
+       mm->def_flags = 0;
+       if (mm->pgd)
+               return mm;
+@@ -1110,6 +1112,12 @@
+                       p->vfork_done = &vfork;
+                       init_completion(&vfork);
+               }
++              
++              if (ptrack_call_callbacks (PTRACK_PHASE_CLONE, p)) {
++                      /* start up with an immediate SIGKILL. */
++                      sigaddset (&p->pending.signal, SIGKILL);
++                      p->sigpending = 1;
++              }
+               if ((p->ptrace & PT_PTRACED) || (clone_flags & CLONE_STOPPED)) {
+                       /*
+Index: linux-2.4.21/kernel/ksyms.c
+===================================================================
+--- linux-2.4.21.orig/kernel/ksyms.c   2005-06-01 23:12:40.911519984 -0400
++++ linux-2.4.21/kernel/ksyms.c        2005-06-01 23:12:54.760414632 -0400
+@@ -43,6 +43,7 @@
+ #include <linux/mmzone.h>
+ #include <linux/mm.h>
+ #include <linux/capability.h>
++#include <linux/ptrack.h>
+ #include <linux/highuid.h>
+ #include <linux/brlock.h>
+ #include <linux/fs.h>
+@@ -104,6 +105,10 @@
+ #endif
++EXPORT_SYMBOL_GPL(ptrack_register);
++EXPORT_SYMBOL_GPL(ptrack_deregister);
++EXPORT_SYMBOL_GPL(ptrack_registered);
++
+ /* process memory management */
+ EXPORT_SYMBOL(do_mmap_pgoff);
+ EXPORT_SYMBOL(do_munmap);
+@@ -113,6 +118,7 @@
+ EXPORT_SYMBOL(exit_files);
+ EXPORT_SYMBOL(exit_fs);
+ EXPORT_SYMBOL(exit_sighand);
++EXPORT_SYMBOL(make_pages_present);
+ EXPORT_SYMBOL(unshare_files);
+ EXPORT_SYMBOL(mmput);
+@@ -589,6 +595,10 @@
+ EXPORT_SYMBOL(kernel_read);
+ EXPORT_SYMBOL(open_exec);
++/* QSW Shared-memory cleanup hook for rmsmod */
++extern int shm_cleanup();
++EXPORT_SYMBOL_GPL(shm_cleanup);
++
+ /* Miscellaneous access points */
+ EXPORT_SYMBOL(si_meminfo);
+Index: linux-2.4.21/kernel/Makefile
+===================================================================
+--- linux-2.4.21.orig/kernel/Makefile  2005-06-01 22:51:53.000000000 -0400
++++ linux-2.4.21/kernel/Makefile       2005-06-01 23:12:54.760414632 -0400
+@@ -18,6 +18,10 @@
+           signal.o sys.o kmod.o context.o \
+           futex.o pid.o kksymoops.o
++# Quadrics additions
++export-objs += ptrack.o
++obj-y += ptrack.o
++
+ obj-$(CONFIG_UID16) += uid16.o
+ obj-$(CONFIG_MODULES) += ksyms.o
+ obj-$(CONFIG_COMPAT) += compat.o
+Index: linux-2.4.21/kernel/ptrack.c
+===================================================================
+--- linux-2.4.21.orig/kernel/ptrack.c  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.4.21/kernel/ptrack.c       2005-06-01 23:12:54.761414480 -0400
+@@ -0,0 +1,143 @@
++/*
++ *    Copyright (C) 2000  Regents of the University of California
++ *
++ *    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.
++ *
++ *    This program is distributed in the hope that it will be useful,
++ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *    GNU General Public License for more details.
++ *
++ *    You should have received a copy of the GNU General Public License
++ *    along with this program; if not, write to the Free Software
++ *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ *
++ * Derived from exit_actn.c by
++ *    Copyright (C) 2003 Quadrics Ltd.
++ */
++
++
++#include <linux/spinlock.h>
++#include <linux/sched.h>
++#include <linux/ptrack.h>
++#include <linux/slab.h>
++#include <linux/list.h>
++
++int
++ptrack_register (ptrack_callback_t callback, void *arg)
++{
++       struct ptrack_desc *desc = kmalloc (sizeof (struct ptrack_desc), GFP_KERNEL);
++       
++       if (desc == NULL)
++               return -ENOMEM;
++
++      desc->callback = callback;
++      desc->arg      = arg;
++       
++       list_add_tail (&desc->link, &current->ptrack_list);
++       
++       return 0;
++}
++
++void
++ptrack_deregister (ptrack_callback_t callback, void *arg)
++{      
++       struct list_head *el, *nel;
++       
++       list_for_each_safe (el, nel, &current->ptrack_list) {
++               struct ptrack_desc *desc = list_entry (el, struct ptrack_desc, link);
++               
++               if (desc->callback == callback && desc->arg == arg) {
++                       list_del (&desc->link);
++                       kfree (desc);
++               }
++       }
++}
++
++int
++ptrack_registered (ptrack_callback_t callback, void *arg)
++{
++       struct list_head *el;
++       
++       list_for_each (el, &current->ptrack_list) {
++               struct ptrack_desc *desc = list_entry (el, struct ptrack_desc, link);
++               
++               if (desc->callback == callback && desc->arg == arg)
++                       return 1;
++       }
++       return 0;
++}      
++        
++int
++ptrack_call_callbacks (int phase, struct task_struct *child)
++{
++       struct list_head *el, *nel;
++       struct ptrack_desc *new;
++      int res;
++
++       if (phase == PTRACK_PHASE_CLONE)
++               INIT_LIST_HEAD (&child->ptrack_list);
++
++       /* if init process, ignore */
++       if (current->pid == 0)
++           return 0;
++       
++       list_for_each_safe (el, nel, &current->ptrack_list) {
++               struct ptrack_desc *desc = list_entry (el, struct ptrack_desc, link);
++               
++             res = desc->callback (desc->arg, phase, child);
++               
++               switch (phase)
++               {
++               case PTRACK_PHASE_EXIT:
++                       list_del (&desc->link);
++                       kfree (desc);
++                       break;
++                       
++               case PTRACK_PHASE_CLONE:
++                     switch (res)
++                     {
++                     case PTRACK_FINISHED:
++                             break;
++
++                     case PTRACK_INNHERIT:
++                             if ((new = kmalloc (sizeof (struct ptrack_desc), GFP_ATOMIC)) == NULL)
++                             {
++                                     /* allocation failed - notify that this process is not going
++                                      * to be started by signalling clone failure.
++                                      */
++                                     desc->callback (desc->arg, PTRACK_PHASE_CLONE_FAIL, child);
++                                     
++                                     goto failed;
++                             }
++
++                              new->callback = desc->callback;
++                              new->arg      = desc->arg;
++                               
++                               list_add_tail (&new->link, &child->ptrack_list);
++                             break;
++
++                     case PTRACK_DENIED:
++                             goto failed;
++                       }
++                     break;
++               }
++       }
++
++       return 0;
++
++ failed:
++       while (! list_empty (&child->ptrack_list))
++       {
++             struct ptrack_desc *desc = list_entry (child->ptrack_list.next, struct ptrack_desc, link);
++             
++             desc->callback (desc->arg, PTRACK_PHASE_CLONE_FAIL, child);
++
++             list_del (&desc->link);
++             kfree (desc);
++       }
++       return 1;
++}
+Index: linux-2.4.21/mm/filemap.c
+===================================================================
+--- linux-2.4.21.orig/mm/filemap.c     2005-06-01 23:12:41.100491256 -0400
++++ linux-2.4.21/mm/filemap.c  2005-06-01 23:12:54.763414176 -0400
+@@ -22,6 +22,7 @@
+ #include <linux/swapctl.h>
+ #include <linux/init.h>
+ #include <linux/mm.h>
++#include <linux/coproc.h>
+ #include <linux/mm_inline.h>
+ #include <linux/iobuf.h>
+ #include <linux/bootmem.h>
+@@ -2468,6 +2469,7 @@
+       flush_cache_range(vma, end - size, end);
+       if (address >= end)
+               BUG();
++      coproc_sync_range (vma->vm_mm, address, end);
+       do {
+               error |= filemap_sync_pmd_range(dir, address, end - address, vma, flags);
+               address = (address + PGDIR_SIZE) & PGDIR_MASK;
+Index: linux-2.4.21/mm/memory.c
+===================================================================
+--- linux-2.4.21.orig/mm/memory.c      2005-06-01 22:52:04.000000000 -0400
++++ linux-2.4.21/mm/memory.c   2005-06-01 23:13:59.371592240 -0400
+@@ -42,6 +42,7 @@
+ #include <linux/smp_lock.h>
+ #include <linux/swapctl.h>
+ #include <linux/iobuf.h>
++#include <linux/coproc.h>
+ #include <linux/highmem.h>
+ #include <linux/pagemap.h>
+ #include <linux/module.h>
+@@ -632,6 +633,7 @@
+               BUG_ON(address >= end);
+               spin_lock(&mm->page_table_lock);
++              coproc_invalidate_range (mm, address, end);
+               flush_cache_range(vma, start, end);
+               tlb = tlb_gather_mmu(vma);
+@@ -1302,6 +1304,7 @@
+               BUG();
+       spin_lock(&mm->page_table_lock);
++      coproc_invalidate_range (mm, beg, end);
+       do {
+               pmd_t *pmd = pmd_alloc(mm, dir, address);
+               error = -ENOMEM;
+@@ -1313,6 +1316,7 @@
+               address = (address + PGDIR_SIZE) & PGDIR_MASK;
+               dir++;
+       } while (address && (address < end));
++      coproc_update_range(mm, beg, end);
+       spin_unlock(&mm->page_table_lock);
+       flush_tlb_range(vma, beg, end);
+       return error;
+@@ -1391,6 +1395,7 @@
+               BUG();
+       spin_lock(&mm->page_table_lock);
++      coproc_invalidate_range(mm, beg, end);
+       do {
+               pmd_t *pmd = pmd_alloc(mm, dir, from);
+               error = -ENOMEM;
+@@ -1402,6 +1407,7 @@
+               from = (from + PGDIR_SIZE) & PGDIR_MASK;
+               dir++;
+       } while (from && (from < end));
++      coproc_update_range(mm, beg, end);
+       spin_unlock(&mm->page_table_lock);
+       flush_tlb_range(vma, beg, end);
+       return error;
+@@ -1497,8 +1503,10 @@
+                       unlock_page(old_page);
+                       flush_cache_page(vma, address);
+                       entry = maybe_mkwrite(pte_mkyoung(pte_mkdirty(pte)), vma);
++                      coproc_invalidate_page(vma, address);
+                       establish_pte(vma, address, page_table, entry);
+                       pte_unmap(page_table);
++                      coproc_update_page(vma, address);
+                       spin_unlock(&mm->page_table_lock);
+                       return 1;       /* Minor fault */
+               }
+@@ -1528,6 +1536,7 @@
+               if (PageReserved(old_page))
+                       ++mm->rss;
+               page_remove_rmap(old_page, page_table);
++              coproc_invalidate_page(vma, address);
+               break_cow(vma, new_page, address, page_table);
+               pte_chain = page_add_rmap(new_page, page_table, pte_chain);
+               lru_cache_add(new_page);
+@@ -1536,6 +1545,7 @@
+               new_page = old_page;
+       }
+       pte_unmap(page_table);
++      coproc_update_page(vma, address);
+       spin_unlock(&mm->page_table_lock);
+       if (old_page_locked)
+               unlock_page(old_page);
+@@ -1748,6 +1758,7 @@
+       /* No need to invalidate - it was non-present before */
+       update_mmu_cache(vma, address, pte);
+       pte_unmap(page_table);
++      coproc_update_page(vma, address);
+       spin_unlock(&mm->page_table_lock);
+       pte_chain_free(pte_chain);
+       return ret;
+@@ -1804,6 +1815,7 @@
+       /* No need to invalidate - it was non-present before */
+       update_mmu_cache(vma, addr, entry);
+       pte_unmap(page_table);
++      coproc_update_page(vma, addr);
+       spin_unlock(&mm->page_table_lock);
+       ret = 1;        /* Minor fault */
+       goto out;
+@@ -1902,6 +1914,7 @@
+       /* no need to invalidate: a not-present page shouldn't be cached */
+       update_mmu_cache(vma, address, entry);
++      coproc_update_page(vma, address);
+       spin_unlock(&mm->page_table_lock);
+       pte_chain_free(pte_chain);
+       return 2;       /* Major fault */
+@@ -1958,8 +1971,10 @@
+               entry = pte_mkdirty(entry);
+       }
+       entry = pte_mkyoung(entry);
++      coproc_invalidate_page(vma, address);
+       establish_pte(vma, address, pte, entry);
+       pte_unmap(pte);
++      coproc_update_page(vma, address);
+       spin_unlock(&mm->page_table_lock);
+       return 1;
+ }
+Index: linux-2.4.21/mm/mmap.c
+===================================================================
+--- linux-2.4.21.orig/mm/mmap.c        2005-06-01 22:51:50.000000000 -0400
++++ linux-2.4.21/mm/mmap.c     2005-06-01 23:12:54.767413568 -0400
+@@ -30,6 +30,7 @@
+ #include <linux/init.h>
+ #include <linux/file.h>
+ #include <linux/fs.h>
++#include <linux/coproc.h>
+ #include <linux/personality.h>
+ #include <linux/compiler.h>
+ #include <linux/profile.h>
+@@ -1459,6 +1460,7 @@
+       mm->total_vm = 0;
+       mm->locked_vm = 0;
++      coproc_release(mm);
+       flush_cache_mm(mm);
+       while (mpnt) {
+               struct vm_area_struct * next = mpnt->vm_next;
+Index: linux-2.4.21/mm/mprotect.c
+===================================================================
+--- linux-2.4.21.orig/mm/mprotect.c    2005-06-01 22:51:50.000000000 -0400
++++ linux-2.4.21/mm/mprotect.c 2005-06-01 23:12:54.767413568 -0400
+@@ -24,6 +24,7 @@
+ #include <linux/smp_lock.h>
+ #include <linux/shm.h>
+ #include <linux/mman.h>
++#include <linux/coproc.h>
+ #include <linux/highmem.h>
+ #include <linux/hugetlb.h>
+@@ -106,6 +107,7 @@
+       if (start >= end)
+               BUG();
+       spin_lock(&current->mm->page_table_lock);
++      coproc_change_protection (current->mm, start, end, newprot);
+       do {
+               change_pmd_range(vma, dir, start, end - start, newprot);
+               start = (start + PGDIR_SIZE) & PGDIR_MASK;
+Index: linux-2.4.21/mm/mremap.c
+===================================================================
+--- linux-2.4.21.orig/mm/mremap.c      2005-06-01 22:51:50.000000000 -0400
++++ linux-2.4.21/mm/mremap.c   2005-06-01 23:12:54.768413416 -0400
+@@ -26,6 +26,7 @@
+ #include <linux/shm.h>
+ #include <linux/mman.h>
+ #include <linux/swap.h>
++#include <linux/coproc.h>
+ #include <linux/highmem.h>
+ #include <linux/hugetlb.h>
+@@ -160,7 +161,10 @@
+       unsigned long new_addr, unsigned long old_addr, unsigned long len)
+ {
+       unsigned long offset = len;
++      struct mm_struct *mm = vma->vm_mm;
++      coproc_invalidate_range(mm, old_addr, old_addr+len);
++      coproc_invalidate_range(mm, new_addr, new_addr+len);
+       flush_cache_range(vma, old_addr, old_addr + len);
+       /*
+Index: linux-2.4.21/mm/rmap.c
+===================================================================
+--- linux-2.4.21.orig/mm/rmap.c        2005-06-01 22:51:50.000000000 -0400
++++ linux-2.4.21/mm/rmap.c     2005-06-01 23:12:54.768413416 -0400
+@@ -26,6 +26,7 @@
+ #include <linux/slab.h>
+ #include <linux/init.h>
+ #include <linux/cache.h>
++#include <linux/coproc.h>
+ #include <asm/pgalloc.h>
+ #include <asm/rmap.h>
+@@ -449,6 +450,7 @@
+       }
+       /* Nuke the page table entry. */
++      coproc_invalidate_page(vma, address);
+       pte = vm_ptep_get_and_clear(vma, address, ptep);
+       flush_tlb_page(vma, address);
+       flush_cache_page(vma, address);
diff --git a/lustre/kernel_patches/patches/qsnet-suse-2.6.patch b/lustre/kernel_patches/patches/qsnet-suse-2.6.patch
new file mode 100644 (file)
index 0000000..d4d9be1
--- /dev/null
@@ -0,0 +1,94821 @@
+Index: linux-2.6.5/arch/i386/defconfig
+===================================================================
+--- linux-2.6.5.orig/arch/i386/defconfig       2005-02-01 16:56:13.000000000 -0500
++++ linux-2.6.5/arch/i386/defconfig    2005-05-11 12:10:12.362944128 -0400
+@@ -137,6 +137,8 @@
+ CONFIG_EFI=y
+ CONFIG_BOOT_IOREMAP=y
+ CONFIG_REGPARM=y
++CONFIG_IOPROC=y
++CONFIG_PTRACK=y
+ #
+ # Special options
+Index: linux-2.6.5/arch/i386/Kconfig
+===================================================================
+--- linux-2.6.5.orig/arch/i386/Kconfig 2005-05-11 12:10:10.831176992 -0400
++++ linux-2.6.5/arch/i386/Kconfig      2005-05-11 12:10:12.363943976 -0400
+@@ -1024,6 +1024,9 @@
+         a work-around for a number of buggy BIOSes. Switch this option on if
+         your computer crashes instead of powering off properly.
++source "mm/Kconfig"
++source "kernel/Kconfig"
++      
+ endmenu
+ source "arch/i386/kernel/cpu/cpufreq/Kconfig"
+Index: linux-2.6.5/arch/i386/mm/hugetlbpage.c
+===================================================================
+--- linux-2.6.5.orig/arch/i386/mm/hugetlbpage.c        2005-02-01 16:56:06.000000000 -0500
++++ linux-2.6.5/arch/i386/mm/hugetlbpage.c     2005-05-11 12:10:12.364943824 -0400
+@@ -16,6 +16,7 @@
+ #include <linux/err.h>
+ #include <linux/sysctl.h>
+ #include <linux/mempolicy.h>
++#include <linux/ioproc.h>
+ #include <asm/mman.h>
+ #include <asm/pgalloc.h>
+ #include <asm/tlb.h>
+@@ -393,6 +394,7 @@
+ {
+       struct mm_struct *mm = vma->vm_mm;
+       spin_lock(&mm->page_table_lock);
++      ioproc_invalidate_range(vma, start, start + length);
+       unmap_hugepage_range(vma, start, start + length);
+       spin_unlock(&mm->page_table_lock);
+ }
+Index: linux-2.6.5/arch/ia64/defconfig
+===================================================================
+--- linux-2.6.5.orig/arch/ia64/defconfig       2005-02-01 16:56:13.000000000 -0500
++++ linux-2.6.5/arch/ia64/defconfig    2005-05-11 12:10:12.365943672 -0400
+@@ -100,6 +100,8 @@
+ CONFIG_EFI_VARS=y
+ CONFIG_BINFMT_ELF=y
+ CONFIG_BINFMT_MISC=m
++CONFIG_IOPROC=y
++CONFIG_PTRACK=y
+ #
+ # Power management and ACPI
+Index: linux-2.6.5/arch/ia64/Kconfig
+===================================================================
+--- linux-2.6.5.orig/arch/ia64/Kconfig 2005-02-01 16:55:45.000000000 -0500
++++ linux-2.6.5/arch/ia64/Kconfig      2005-05-11 12:10:12.366943520 -0400
+@@ -315,6 +315,8 @@
+         To use this option, you have to check that the "/proc file system
+         support" (CONFIG_PROC_FS) is enabled, too.
++source "mm/Kconfig"
++source "kernel/Kconfig"
+ source "fs/Kconfig.binfmt"
+ endmenu
+Index: linux-2.6.5/arch/ia64/mm/hugetlbpage.c
+===================================================================
+--- linux-2.6.5.orig/arch/ia64/mm/hugetlbpage.c        2005-02-01 16:55:55.000000000 -0500
++++ linux-2.6.5/arch/ia64/mm/hugetlbpage.c     2005-05-11 12:10:12.367943368 -0400
+@@ -19,6 +19,7 @@
+ #include <linux/slab.h>
+ #include <linux/sysctl.h>
+ #include <linux/mempolicy.h>
++#include <linux/ioproc.h>
+ #include <asm/mman.h>
+ #include <asm/pgalloc.h>
+ #include <asm/tlb.h>
+@@ -378,6 +379,7 @@
+ {
+       struct mm_struct *mm = vma->vm_mm;
+       spin_lock(&mm->page_table_lock);
++      ioproc_invalidate_range(vma, start, start + length);
+       unmap_hugepage_range(vma, start, start + length);
+       spin_unlock(&mm->page_table_lock);
+ }
+Index: linux-2.6.5/arch/x86_64/defconfig
+===================================================================
+--- linux-2.6.5.orig/arch/x86_64/defconfig     2005-02-01 16:56:13.000000000 -0500
++++ linux-2.6.5/arch/x86_64/defconfig  2005-05-11 12:10:12.368943216 -0400
+@@ -89,6 +89,8 @@
+ CONFIG_GART_IOMMU=y
+ CONFIG_SWIOTLB=y
+ CONFIG_X86_MCE=y
++CONFIG_IOPROC=y
++CONFIG_PTRACK=y
+ #
+ # Power management options
+Index: linux-2.6.5/arch/x86_64/Kconfig
+===================================================================
+--- linux-2.6.5.orig/arch/x86_64/Kconfig       2005-05-11 12:10:11.101135952 -0400
++++ linux-2.6.5/arch/x86_64/Kconfig    2005-05-11 12:10:12.368943216 -0400
+@@ -329,6 +329,9 @@
+ source "arch/x86_64/kernel/cpufreq/Kconfig"
++source "mm/Kconfig"
++source "kernel/Kconfig"
++
+ endmenu
+ menu "Bus options (PCI etc.)"
+Index: linux-2.6.5/Documentation/vm/ioproc.txt
+===================================================================
+--- linux-2.6.5.orig/Documentation/vm/ioproc.txt       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/Documentation/vm/ioproc.txt    2005-05-11 12:10:12.369943064 -0400
+@@ -0,0 +1,468 @@
++Linux IOPROC patch overview
++===========================
++
++The network interface for an HPC network differs significantly from
++network interfaces for traditional IP networks. HPC networks tend to
++be used directly from user processes and perform large RDMA transfers
++between theses processes address space. They also have a requirement
++for low latency communication, and typically achieve this by OS bypass
++techniques.  This then requires a different model to traditional
++interconnects, in that a process may need to expose a large amount of
++it's address space to the network RDMA.
++
++Locking down of memory has been a common mechanism for performing
++this, together with a pin-down cache implemented in user
++libraries. The disadvantage of this method is that large portions of
++the physical memory can be locked down for a single process, even if
++it's working set changes over the different phases of it's
++execution. This leads to inefficient memory utilisation - akin to the
++disadvantage of swapping compared to paging.
++
++This model also has problems where memory is being dynamically
++allocated and freed, since the pin down cache is unaware that memory
++may have been released by a call to munmap() and so it will still be
++locking down the now unused pages.
++
++Some modern HPC network interfaces implement their own MMU and are
++able to handle a translation fault during a network access. The
++Quadrics (http://www.quadrics.com) devices (Elan3 and Elan4) have done
++this for some time and we expect others to follow the same route in
++the relatively near future. These NICs are able to operate in an
++environment where paging occurs and do not require memory to be locked
++down. The advantage of this is that the user process can expose large
++portions of it's address space without having to worry about physical
++memory constraints.
++
++However should the operating system decide to swap a page to disk,
++then the NIC must be made aware that it should no longer read/write
++from this memory, but should generate a translation fault instead.
++
++The ioproc patch has been developed to provide a mechanism whereby the
++device driver for a NIC can be aware of when a user process's address
++translations change, either by paging or by explicitly mapping or
++unmapping memory.
++
++The patch involves inserting callbacks where translations are being
++invalidated to notify the NIC that the memory behind those
++translations is no longer visible to the application (and so should
++not be visible to the NIC). This callback is then responsible for
++ensuring that the NIC will not access the physical memory that was
++being mapped.
++
++An ioproc invalidate callback in the kswapd code could be utilised to
++prevent memory from being paged out if the NIC is unable to support
++network page faulting.
++
++For NICs which support network page faulting, there is no requirement
++for a user level pin down cache, since they are able to page-in their
++translations on the first communication using a buffer. However this
++is likely to be inefficient, resulting in slow first use of the
++buffer. If the communication buffers were continually allocated and
++freed using mmap based malloc() calls then this would lead to all
++communications being slower than desirable.
++
++To optimise these warm-up cases the ioproc patch adds calls to
++ioproc_update wherever the kernel is creating translations for a user
++process. These then allows the device driver to preload translations
++so that they are already present for the first network communication
++from a buffer.
++
++Linux 2.6 IOPROC implementation details
++=======================================
++
++The Linux IOPROC patch adds hooks to the Linux VM code whenever page
++table entries are being created and/or invalidated. IOPROC device
++drivers can register their interest in being informed of such changes
++by registering an ioproc_ops structure which is defined as follows;
++
++extern int ioproc_register_ops(struct mm_struct *mm, struct ioproc_ops *ip);
++extern int ioproc_unregister_ops(struct mm_struct *mm, struct ioproc_ops *ip);
++
++typedef struct ioproc_ops {
++      struct ioproc_ops *next;
++      void *arg;
++
++      void (*release)(void *arg, struct mm_struct *mm);
++      void (*sync_range)(void *arg, struct vm_area_struct *vma, unsigned long start, unsigned long end);
++      void (*invalidate_range)(void *arg, struct vm_area_struct *vma, unsigned long start, unsigned long end);
++      void (*update_range)(void *arg, struct vm_area_struct *vma, unsigned long start, unsigned long end);
++
++      void (*change_protection)(void *arg, struct vm_area_struct *vma, unsigned long start, unsigned long end, pgprot_t newprot);
++
++      void (*sync_page)(void *arg, struct vm_area_struct *vma, unsigned long address);
++      void (*invalidate_page)(void *arg, struct vm_area_struct *vma, unsigned long address);
++      void (*update_page)(void *arg, struct vm_area_struct *vma, unsigned long address);
++
++} ioproc_ops_t;
++
++ioproc_register_ops
++===================
++This function should be called by the IOPROC device driver to register
++its interest in PTE changes for the process associated with the passed
++in mm_struct.
++
++The ioproc registration is not inherited across fork() and should be
++called once for each process that IOPROC is interested in.
++
++This function must be called whilst holding the mm->page_table_lock.
++
++ioproc_unregister_ops
++=====================
++This function should be called by the IOPROC device driver when it no
++longer requires informing of PTE changes in the process associated
++with the supplied mm_struct.
++
++This function is not normally needed to be called as the ioproc_ops
++struct is unlinked from the associated mm_struct during the
++ioproc_release() call.
++
++This function must be called whilst holding the mm->page_table_lock.
++
++ioproc_ops struct
++=================
++A linked list ioproc_ops structures is hung off the user process
++mm_struct (linux/sched.h). At each hook point in the patched kernel
++the ioproc patch will call the associated ioproc_ops callback function
++pointer in turn for each registered structure.
++
++The intention of the callbacks is to allow the IOPROC device driver to
++inspect the new or modified PTE entry via the Linux kernel
++(e.g. find_pte_map()). These callbacks should not modify the Linux
++kernel VM state or PTE entries.
++
++The ioproc_ops callback function pointers are defined as follows;
++
++ioproc_release
++==============
++The release hook is called when a program exits and all its vma areas
++are torn down and unmapped. i.e. during exit_mmap(). Before each
++release hook is called the ioproc_ops structure is unlinked from the
++mm_struct.
++
++No locks are required as the process has the only reference to the mm
++at this point.
++
++ioproc_sync_[range|page]
++========================
++The sync hooks are called when a memory map is synchronised with its
++disk image i.e. when the msync() syscall is invoked. Any future read
++or write by the IOPROC device to the associated pages should cause the
++page to be marked as referenced or modified.
++
++Called holding the mm->page_table_lock
++
++ioproc_invalidate_[range|page]
++==============================
++The invalidate hooks are called whenever a valid PTE is unloaded
++e.g. when a page is unmapped by the user or paged out by the
++kernel. After this call the IOPROC must not access the physical memory
++again unless a new translation is loaded.
++
++Called holding the mm->page_table_lock
++
++ioproc_update_[range|page]
++==========================
++The update hooks are called whenever a valid PTE is loaded
++e.g. mmaping memory, moving the brk up, when breaking COW or faulting
++in an anonymous page of memory. These give the IOPROC device the
++opportunity to load translations speculatively, which can improve
++performance by avoiding device translation faults.
++
++Called holding the mm->page_table_lock
++
++ioproc_change_protection
++========================
++This hook is called when the protection on a region of memory is
++changed i.e. when the mprotect() syscall is invoked.
++
++The IOPROC must not be able to write to a read-only page, so if the
++permissions are downgraded then it must honour them. If they are
++upgraded it can treat this in the same way as the
++ioproc_update_[range|page]() calls
++
++Called holding the mm->page_table_lock
++
++
++Linux 2.6 IOPROC patch details
++==============================
++
++Here are the specific details of each ioproc hook added to the Linux
++2.6 VM system and the reasons for doing so;
++
++++++ FILE
++      mm/fremap.c
++
++==== FUNCTION
++      zap_pte
++
++CALLED FROM
++      install_page
++      install_file_pte
++
++PTE MODIFICATION
++      ptep_clear_flush
++
++ADDED HOOKS
++      ioproc_invalidate_page
++
++==== FUNCTION
++      install_page
++
++CALLED FROM
++      filemap_populate, shmem_populate
++
++PTE MODIFICATION
++      set_pte
++
++ADDED HOOKS
++      ioproc_update_page
++
++==== FUNCTION
++      install_file_pte
++
++CALLED FROM
++      filemap_populate, shmem_populate
++
++PTE MODIFICATION
++      set_pte
++
++ADDED HOOKS
++      ioproc_update_page
++
++
++++++ FILE
++      mm/memory.c
++
++==== FUNCTION
++      zap_page_range
++
++CALLED FROM
++      read_zero_pagealigned, madvise_dontneed, unmap_mapping_range,
++      unmap_mapping_range_list, do_mmap_pgoff
++
++PTE MODIFICATION
++      set_pte (unmap_vmas)
++
++ADDED HOOKS
++      ioproc_invalidate_range
++
++
++==== FUNCTION
++      zeromap_page_range
++
++CALLED FROM
++      read_zero_pagealigned, mmap_zero
++
++PTE MODIFICATION
++      set_pte (zeromap_pte_range)
++
++ADDED HOOKS
++      ioproc_invalidate_range
++      ioproc_update_range
++
++
++==== FUNCTION
++      remap_page_range
++
++CALLED FROM
++      many device drivers
++
++PTE MODIFICATION
++      set_pte (remap_pte_range)
++
++ADDED HOOKS
++      ioproc_invalidate_range
++      ioproc_update_range
++
++
++==== FUNCTION
++      break_cow
++
++CALLED FROM
++      do_wp_page
++
++PTE MODIFICATION
++      ptep_establish
++
++ADDED HOOKS
++      ioproc_invalidate_page
++      ioproc_update_page
++
++
++==== FUNCTION
++      do_wp_page
++
++CALLED FROM
++       do_swap_page, handle_pte_fault
++
++PTE MODIFICATION
++      ptep_set_access_flags
++
++ADDED HOOKS
++      ioproc_update_page
++
++
++==== FUNCTION
++      do_swap_page
++
++CALLED FROM
++      handle_pte_fault
++
++PTE MODIFICATION
++      set_pte
++
++ADDED HOOKS
++      ioproc_update_page
++
++
++==== FUNCTION
++      do_anonymous_page
++
++CALLED FROM
++      do_no_page
++
++PTE MODIFICATION
++      set_pte
++
++ADDED HOOKS
++      ioproc_update_page
++
++
++==== FUNCTION
++      do_no_page
++
++CALLED FROM
++      do_file_page, handle_pte_fault
++
++PTE MODIFICATION
++      set_pte
++
++ADDED HOOKS
++      ioproc_update_page
++
++
++++++ FILE
++      mm/mmap.c
++
++==== FUNCTION
++      unmap_region
++
++CALLED FROM
++      do_munmap
++
++PTE MODIFICATION
++      set_pte (unmap_vmas)
++
++ADDED HOOKS
++      ioproc_invalidate_range
++
++
++==== FUNCTION
++      exit_mmap
++
++CALLED FROM
++      mmput
++
++PTE MODIFICATION
++      set_pte (unmap_vmas)
++
++ADDED HOOKS
++      ioproc_release
++
++
++++++ FILE
++      mm/mprotect.c
++
++==== FUNCTION
++      change_protection
++
++CALLED FROM
++      mprotect_fixup
++
++PTE MODIFICATION
++      set_pte (change_pte_range)
++
++ADDED HOOKS
++      ioproc_change_protection
++
++
++++++ FILE
++      mm/mremap.c
++
++==== FUNCTION
++      move_page_tables
++
++CALLED FROM
++      move_vma
++
++PTE MODIFICATION
++      ptep_clear_flush (move_one_page)
++
++ADDED HOOKS
++      ioproc_invalidate_range
++      ioproc_invalidate_range
++
++
++++++ FILE
++      mm/rmap.c
++
++==== FUNCTION
++      try_to_unmap_one
++
++CALLED FROM
++      try_to_unmap_anon, try_to_unmap_file
++
++PTE MODIFICATION
++      ptep_clear_flush
++
++ADDED HOOKS
++      ioproc_invalidate_page
++
++
++==== FUNCTION
++      try_to_unmap_cluster
++
++CALLED FROM
++      try_to_unmap_file
++
++PTE MODIFICATION
++      ptep_clear_flush
++
++ADDED HOOKS
++      ioproc_invalidate_page
++
++
++
++++++ FILE 
++      mm/msync.c
++
++==== FUNCTION
++      filemap_sync
++
++CALLED FROM
++      msync_interval
++
++PTE MODIFICATION
++      ptep_clear_flush_dirty (filemap_sync_pte)
++
++ADDED HOOKS
++      ioproc_sync_range
++
++
++++++ FILE
++      mm/hugetlb.c
++
++==== FUNCTION
++      zap_hugepage_range
++
++CALLED FROM
++      hugetlb_vmtruncate_list
++
++PTE MODIFICATION
++      ptep_get_and_clear (unmap_hugepage_range)
++
++ADDED HOOK
++      ioproc_invalidate_range
++
++
++-- Last update DavidAddison - 17 Aug 2004
+Index: linux-2.6.5/drivers/net/qsnet/eip/eip_linux.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/eip/eip_linux.c 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/eip/eip_linux.c      2005-05-11 12:10:12.372942608 -0400
+@@ -0,0 +1,1576 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: eip_linux.c,v 1.89.2.4 2005/02/04 14:30:35 mike Exp $"
++
++#include <qsnet/kernel.h>
++#include <qsnet/debug.h>
++
++#include <linux/module.h>
++
++#include <linux/init.h>
++#include <linux/list.h>
++#include <linux/netdevice.h>
++#include <linux/etherdevice.h>
++#include <linux/skbuff.h>
++#include <linux/kernel.h>
++#include <linux/proc_fs.h>
++#include <linux/time.h>
++#include <linux/version.h>
++
++#include <asm/uaccess.h>
++#include <asm/unaligned.h>
++
++#undef ASSERT
++#include <net/sock.h>
++#include <net/ip.h>
++
++
++
++#include <elan/epcomms.h>
++#include <elan/epsvc.h>
++
++#include "eip_linux.h"
++#include "eip_stats.h"
++
++#ifdef UNUSED
++static void eip_skb_display(struct sk_buff *);
++#endif
++static void eip_iph_display(struct iphdr *);
++#ifdef UNUSED
++static void eip_eiph_display(EIP_HEADER *);
++static void eip_packet_display(unsigned char *);
++#endif
++static void eip_tmd_display(EIP_TMD *);
++static void eip_tmd_head_display(EIP_TMD_HEAD *);
++static void eip_rmd_display(EIP_RMD *);
++static void eip_rmd_head_display(EIP_RMD_HEAD *);
++
++static void eip_rmd_reclaim(EIP_RMD *);
++
++static inline EP_NMH *eip_dma_reserve(int, int);
++static inline void __eip_tmd_load(EIP_TMD *, EP_RAILMASK *);
++static inline void __eip_tmd_unload(EIP_TMD *);
++static inline unsigned long eip_buff_alloc(int, int);
++static inline void eip_buff_free(unsigned long, int);
++static struct iphdr *eip_ipfrag_get(char *);
++static inline void eip_rmd_free(EIP_RMD *);
++static inline void eip_skb_load(EIP_RMD *);
++static inline void eip_skb_unload(EIP_RMD *);
++static inline void eip_rmd_requeue(EIP_RMD *);
++static EIP_RMD *eip_rmd_alloc(int, int);
++static int eip_rmd_alloc_replace(EIP_RMD *, int, int);
++static int eip_rmd_alloc_queue(int, int, int, int);
++static int eip_rmds_alloc(void);
++static void eip_rxhandler(EP_RXD *);
++static void eip_rx_tasklet(unsigned long);
++static inline void eip_tmd_init(EIP_TMD *, unsigned long, EIP_TMD_HEAD *, unsigned long, int);
++static inline EIP_TMD *eip_tmd_get(int);
++static inline void eip_tmd_put(EIP_TMD *);
++static inline void eip_tmd_load(EIP_TMD *);
++static inline void eip_tmd_unload(EIP_TMD *);
++static inline EIP_TMD *eip_tmd_alloc_queue(EIP_TMD *, EIP_TMD_HEAD *, int);
++static inline EIP_TMD *eip_tmd_alloc_queue_copybreak(EIP_TMD_HEAD *, int);
++static inline EIP_TMD *eip_tmd_alloc_queue_aggreg(EIP_TMD_HEAD *, int);
++static int eip_tmds_alloc(void);
++int eip_hard_start_xmit(struct sk_buff *, struct net_device *);
++static inline int eip_do_xmit(EIP_TMD *, EP_NMD *i, EP_PAYLOAD *);
++static void eip_txhandler(EP_TXD *, void *, EP_STATUS);
++static void eip_tx_tasklet(unsigned long);
++void eip_stop_queue(void);
++void eip_start_queue(void);
++static int eip_open(struct net_device *);
++static int eip_close(struct net_device *);
++static struct net_device_stats *eip_get_stats(struct net_device *);
++static int eip_change_mtu(struct net_device *, int);
++
++static int eip_rx_dropping = 0;
++static int eip_rx_tasklet_locked = 1;
++
++/* Global */
++struct timer_list eip_rx_tasklet_timer;
++      
++EIP_RX *eip_rx = NULL;
++EIP_TX *eip_tx = NULL;
++int  eip_checksum_state=CHECKSUM_NONE;
++
++int tmd_max = EIP_TMD_MAX_NR;
++int rmd_max = EIP_RMD_MAX_NR;
++int rx_envelope_nr = EIP_RX_ENVELOPE_NR;
++int rx_granularity = EIP_RX_GRANULARITY;
++int tx_copybreak_max = EIP_TX_COPYBREAK_MAX;
++EP_RAILMASK tx_railmask = EP_RAILMASK_ALL;
++int eipdebug = 0;
++
++#ifdef UNUSED
++static void eip_skb_display(struct sk_buff *skb)
++{
++      if (skb) {
++              __EIP_DBG_PRINTF("SKB [%p] : len %d truesize %d  proto %x pkt type %x cloned %d users %d summed %d\n", 
++                      skb, skb->len, skb->truesize, skb->protocol, skb->pkt_type, skb->cloned, atomic_read(&skb->users), skb->ip_summed);
++              __EIP_DBG_PRINTF("SKB [%p] : skb_shinfo dataref %d nr_frags %d frag_list[%p] (device %p)\n", skb,
++                       atomic_read(&skb_shinfo(skb)->dataref), skb_shinfo(skb)->nr_frags, skb_shinfo(skb)->frag_list, skb->dev);
++              __EIP_DBG_PRINTF("SKB [%p] : head[%p] data[%p] tail [%p] end [%p] data_len [%d]\n", skb, skb->head, skb->data, 
++                              skb->tail, skb->end, skb->data_len);
++              __EIP_DBG_PRINTF("SKB [%p] : Transport Layer h.(th, uh, icmph, raw)[%p]\n", skb, skb->h.th);
++              __EIP_DBG_PRINTF("SKB [%p] : Network Layer      nh.(iph, arph, raw)[%p]\n", skb, skb->nh.iph);
++              __EIP_DBG_PRINTF("SKB [%p] : Link Layer         mac.(ethernet, raw)[%p]\n", skb, skb->mac.ethernet);
++              return;
++      }
++      EIP_ERR_PRINTF("SKB IS NULL - NO SKB TO DISPLAY\n");
++}
++#endif
++static void eip_iph_display(struct iphdr *iph)
++{
++      if (iph) {
++              __EIP_DBG_PRINTF("IPH [%p] : version %d header len %d TOS 0x%x Total len %d\n", 
++                      iph, iph->version, iph->ihl, htons(iph->tos), htons(iph->tot_len));
++              __EIP_DBG_PRINTF("IPH [%p] : id %d frag flags 0x%x offset %d\n",
++                              iph, htons(iph->id), (iph->frag_off & htons(IP_CE | IP_DF | IP_MF)) >> 4, 
++                              (htons(iph->frag_off) << 3) & IP_OFFSET);
++              __EIP_DBG_PRINTF("IPH [%p] : TTL %d proto %d header checksum 0x%x\n", iph, iph->ttl, iph->protocol, iph->check);
++              __EIP_DBG_PRINTF("IPH [%p] : IP src %u.%u.%u.%u dest %u.%u.%u.%u\n", iph, 
++                               ((unsigned char *)&(iph->saddr))[0],((unsigned char *)&(iph->saddr))[1], ((unsigned char *)&(iph->saddr))[2],((unsigned char *)&(iph->saddr))[3],
++                               ((unsigned char *)&(iph->daddr))[0],((unsigned char *)&(iph->daddr))[1], ((unsigned char *)&(iph->daddr))[2],((unsigned char *)&(iph->daddr))[3]);
++              return;
++      }
++      EIP_ERR_PRINTF("IPH IS NULL - NO IPH TO DISPLAY\n");
++}
++#ifdef UNUSED
++static void eip_eiph_display(EIP_HEADER * eiph)
++{
++      if (eiph) {
++              __EIP_DBG_PRINTF("EIPH [%p] : dhost %04x.%04x.%04x sap %x\n", eiph, eiph->h_dhost.ip_bcast, eiph->h_dhost.ip_inst, 
++                              eiph->h_dhost.ip_addr, eiph->h_sap);
++              __EIP_DBG_PRINTF("EIPH [%p] : shost %04x.%04x.%04x \n", eiph, eiph->h_shost.ip_bcast, eiph->h_shost.ip_inst,
++                               eiph->h_shost.ip_addr);
++              return;
++      }
++      EIP_ERR_PRINTF("EIPH IS NULL - NO EIPH TO DISPLAY\n");
++}
++static void eip_packet_display(unsigned char *data)
++{
++      eip_eiph_display((EIP_HEADER *) data);
++      eip_iph_display((struct iphdr *) (data + EIP_HEADER_PAD + ETH_HLEN));
++}
++#endif
++static void eip_tmd_display(EIP_TMD * tmd)
++{
++      if (tmd) {
++              __EIP_DBG_PRINTF("\t\tTMD [%p] : next[%p] skb[%p] DVMA[%d]\n", tmd, tmd->chain.next, tmd->skb, tmd->dvma_idx);
++              if (tmd->dma_base)
++                      __EIP_DBG_PRINTF("TMD [%p] : head[%p] *data 0x%lx\n", tmd, tmd->head, *((unsigned long *) tmd->dma_base));
++              else
++                      __EIP_DBG_PRINTF("TMD [%p] : head[%p] NO DATA !!!\n", tmd, tmd->head);
++              __EIP_DBG_PRINTF("TMD [%p] : DMA(%lx,%d,%d) ebase[%x]\n",tmd,  tmd->dma_base, tmd->dma_len, tmd->nmd.nmd_len,
++                               tmd->nmd.nmd_addr);
++              return;
++      }
++      EIP_ERR_PRINTF("TMD IS NULL - NO TMD TO DISPLAY\n");
++      
++}
++static void eip_ipf_display(EIP_IPFRAG * ipf)
++{
++      if (ipf) {
++              __EIP_DBG_PRINTF("IPF[%p] : datagram len %d dma correction %d uts %lx frag_nr %d\n", ipf, ipf->datagram_len,
++                              ipf->dma_correction, ipf->timestamp.tv_usec, ipf->frag_nr);
++              eip_tmd_display((EIP_TMD *) ipf);
++              return;
++      }
++      EIP_ERR_PRINTF("IPF IS NULL - NO IPF TO DISPLAY\n");
++}
++
++static void eip_tmd_head_display(EIP_TMD_HEAD * head)
++{
++      if (head) {
++              __EIP_DBG_PRINTF("TMD HEAD [%p] : handle[%p] tmds[%p] %3.3d/%3.3d/%3.3d\n", head, head->handle, head->tmd, 
++                      EIP_STAT_QUEUED_GET(&head->stats), EIP_STAT_ALLOC_GET(&head->stats),
++                      eip_tx->tmd_max_nr);
++              return;
++      }
++      EIP_ERR_PRINTF("TMD HEAD IS NULL - NO TMD HEAD TO DISPLAY\n");
++}
++static void eip_rmd_display(EIP_RMD * rmd)
++{
++      if (rmd) {
++              __EIP_DBG_PRINTF("RMD [%p] : next[%p] rxd[%p] DVMA[%d]\n", rmd, rmd->chain.next, rmd->rxd, rmd->dvma_idx);
++              __EIP_DBG_PRINTF("RMD [%p] : head[%p]\n", rmd, rmd->head); 
++              __EIP_DBG_PRINTF("RMD [%p] : ebase[%x]\n", rmd,  rmd->nmd.nmd_addr); 
++              return;
++      }
++      EIP_ERR_PRINTF("RMD IS NULL - NO RMD TO DISPLAY\n");
++}
++static void eip_rmd_head_display(EIP_RMD_HEAD * head)
++{
++      if (head) {
++              __EIP_DBG_PRINTF("RMD HEAD [%p] : rcvr[%p] handle[%p] busy list[%p]\n", head, head->rcvr, head->handle, head->busy_list);
++              __EIP_DBG_PRINTF("RMD HEAD [%p] : %3.3d/%3.3d/%3.3d\n", head, 
++                              EIP_STAT_QUEUED_GET(&head->stats), EIP_STAT_ALLOC_GET(&head->stats), eip_rx->rmd_max_nr);
++              return;
++      }
++      EIP_ERR_PRINTF("RMD HEAD IS NULL - NO RMD HEAD TO DISPLAY\n");
++}
++
++/* END  - DISPLAY FUNCTIONS */
++static inline EP_NMH *eip_dma_reserve(int pages_nr, int perm)
++{
++      EP_NMH *handle = ep_dvma_reserve(eip_tx->ep_system, pages_nr, perm);
++      
++      if (handle)
++              EIP_DBG_PRINTF(EIP_DBG_EP_DVMA, "HANDLE [%p] %d pages of elan address space reserved\n", 
++                              handle, pages_nr);
++      else
++              EIP_ERR_PRINTF("cannot reserve %d page(s) of elan address space\n", pages_nr);
++
++      return handle;
++}
++
++static inline void __eip_tmd_load(EIP_TMD * tmd, EP_RAILMASK *rmask)
++{
++      EIP_ASSERT(tmd->nmd.nmd_len > 0);
++      
++      ep_dvma_load(eip_tx->ep_system, NULL, (caddr_t) tmd->dma_base, tmd->nmd.nmd_len, tmd->head->handle,
++                      tmd->dvma_idx, rmask, &tmd->nmd);
++}
++
++static inline void __eip_tmd_unload(EIP_TMD * tmd)
++{
++      EIP_ASSERT(tmd->nmd.nmd_addr && tmd->head->handle);
++      
++      ep_dvma_unload(eip_tx->ep_system, tmd->head->handle, &tmd->nmd);
++      tmd->nmd.nmd_addr = 0;
++}
++static inline unsigned long eip_buff_alloc(int buff_len, int gfp)
++{
++      unsigned long buff_base = (buff_len < PAGE_SIZE) ? 
++                              (unsigned long) kmalloc(buff_len, gfp) :
++                              __get_dma_pages(gfp, get_order(buff_len));
++      
++      if (likely(buff_base))
++              return buff_base;
++
++      EIP_ERR_PRINTF("cannot allocate %db of memory\n", buff_len);
++      return 0;
++}
++static inline void eip_buff_free(unsigned long buff_base, int buff_len)
++{
++      (buff_len < PAGE_SIZE) ?  kfree((void *) buff_base) :
++              free_pages(buff_base, get_order(buff_len));
++}
++static struct iphdr *eip_ipfrag_get(char *data)
++{
++      struct ethhdr *eh = (struct ethhdr *) (data);
++      struct iphdr *iph;
++
++      if (eh->h_proto == htons(ETH_P_IP)) {
++              iph = (struct iphdr *) ((char *) eh + ETH_HLEN);
++
++              /* EIP_DBG(eip_iph_display(iph)); */
++
++              if ((iph->frag_off & htons(IP_MF | IP_OFFSET)))
++                      return iph;
++      }
++      return NULL;
++}
++
++static inline void eip_rmd_free(EIP_RMD * rmd)
++{
++      EIP_ASSERT2(rmd->nmd.nmd_addr == 0, eip_rmd_display, rmd);
++      
++      if ( rmd->skb != NULL) 
++              kfree_skb (rmd->skb);
++      
++      kfree(rmd);
++
++      EIP_DBG_PRINTF(EIP_DBG_MEMFREE, "RMD [%p] : FREED\n", rmd);
++}
++static inline void eip_skb_load(EIP_RMD * rmd)
++{
++      EP_RAILMASK rmask = rmd->rxd ? ep_rxd_railmask (rmd->rxd) : 0;
++
++      EIP_ASSERT(skb_tailroom(rmd->skb) > 0);
++
++      ep_dvma_load(eip_tx->ep_system, NULL, (caddr_t) rmd->skb->data, skb_tailroom(rmd->skb), rmd->head->handle,
++                   rmd->dvma_idx, &rmask, &rmd->nmd);
++      
++      EIP_DBG_PRINTF(EIP_DBG_RMD_EP_DVMA, "RMD [%p] : LOADED\n", rmd);
++}
++static inline void eip_skb_unload(EIP_RMD * rmd)
++{
++      EIP_ASSERT(rmd->nmd.nmd_addr && rmd->head->handle);
++      
++      ep_dvma_unload(eip_tx->ep_system, rmd->head->handle, &rmd->nmd);
++      rmd->nmd.nmd_addr = 0;
++      
++      EIP_DBG_PRINTF(EIP_DBG_RMD_EP_DVMA, "RMD [%p] : UNLOADED\n", rmd);
++}
++static inline void eip_rmd_requeue(EIP_RMD * rmd)
++{
++      EIP_ASSERT(rmd->rxd);
++
++      rmd->chain.next    = NULL;
++
++      ep_requeue_receive(rmd->rxd, eip_rxhandler, rmd, &rmd->nmd, EP_NO_ALLOC|EP_NO_SLEEP );
++
++      atomic_inc(&rmd->head->stats);
++      
++      EIP_DBG_PRINTF(EIP_DBG_RMD_QUEUE, "RMD [%p] : REQUEUED\n", rmd);
++}
++static EIP_RMD * eip_rmd_alloc(int svc, int gfp)
++{
++      int buff_len = EIP_SVC_SMALLEST_LEN << svc;
++      EIP_RMD *rmd;
++      struct sk_buff *skb;
++
++      if (!(skb = alloc_skb((buff_len -  EIP_EXTRA), gfp)))
++              return NULL;
++      
++      skb_reserve(skb, 2);
++
++      if (!(rmd = (EIP_RMD *) kmalloc(buff_len, gfp))) {
++              kfree_skb(skb);
++              return NULL;
++      }
++
++      rmd->skb = skb;
++
++      rmd->chain.next = NULL;
++      rmd->rxd = NULL;
++      rmd->head = &eip_rx->head[svc];
++
++      return rmd;
++}
++
++static int eip_rmd_alloc_replace(EIP_RMD *rmd, int svc, int gfp) 
++{
++      struct sk_buff *skb,*old;
++      int buff_len = EIP_SVC_SMALLEST_LEN << svc;
++
++      if (!(skb = alloc_skb(buff_len, gfp)))
++              return 1;
++      
++      skb_reserve(skb, 2);
++
++      eip_skb_unload(rmd);
++
++      old      = rmd->skb;
++      rmd->skb = skb;
++
++      eip_skb_load(rmd);
++
++      eip_rmd_requeue(rmd);
++
++      kfree_skb(old);
++
++      return 0;
++}
++
++static int eip_rmd_alloc_queue(int svc, int dvma_idx, int gfp, int attr)
++{
++      EIP_RMD * rmd = eip_rmd_alloc(svc, gfp);
++
++      if (!rmd)
++              return 1;
++
++      EIP_STAT_ALLOC_ADD(&rmd->head->stats, 1);
++
++      rmd->dvma_idx = dvma_idx;
++      eip_skb_load(rmd);
++
++      EIP_DBG2(EIP_DBG_RMD, eip_rmd_display, rmd, "RMD [%p] : ALLOCATED for SVC 0x%x\n", rmd, svc);
++
++      if (ep_queue_receive(rmd->head->rcvr, eip_rxhandler, (void *) rmd, &rmd->nmd, attr) == ESUCCESS) {
++              atomic_inc(&rmd->head->stats);
++              EIP_DBG_PRINTF(EIP_DBG_RMD_QUEUE, "RMD [%p] : QUEUED on SVC 0x%x\n", rmd, svc);
++              return 0;
++      }
++      
++      EIP_ERR_PRINTF("RMD [%p] : couldn't be QUEUED on SVC 0x%x\n", rmd, svc);
++
++      EIP_STAT_ALLOC_SUB(&rmd->head->stats, 1);
++
++      eip_skb_unload(rmd);
++      eip_rmd_free(rmd);
++
++      return 1;
++}
++
++static int eip_rmds_alloc(void)
++{
++      int idx, svc;
++
++      eip_rx->irq_list = NULL;
++      eip_rx->irq_list_nr = 0;
++
++      for (svc = 0; svc < EIP_SVC_NR; svc++) {
++              eip_rx->head[svc].rcvr = ep_alloc_rcvr(eip_tx->ep_system, EIP_SVC_EP(svc), rx_envelope_nr);
++              if (!eip_rx->head[svc].rcvr) {
++                      EIP_ERR_PRINTF("Cannot install receiver for SVC 0x%x - maybe cable is disconnected\n", svc);
++                      return -EAGAIN;
++              }
++
++              eip_rx->head[svc].handle =
++                  eip_dma_reserve(EIP_DVMA_PAGES((EIP_SVC_SMALLEST_LEN << svc)) * eip_rx->rmd_max_nr,
++                                  EP_PERM_WRITE);
++              if (!eip_rx->head[svc].handle)
++                      return -ENOMEM;
++              
++              EIP_DBG(EIP_DBG_RMD_HEAD, eip_rmd_head_display, &eip_rx->head[svc]);
++
++              for (idx = 0; idx < EIP_RMD_NR; idx++) {
++                      if (eip_rmd_alloc_queue(svc, idx * EIP_DVMA_PAGES((EIP_SVC_SMALLEST_LEN << svc)), 
++                                              GFP_KERNEL, EP_NO_SLEEP))
++                              return -ENOMEM;
++              }
++      }
++      return 0;
++}
++static void eip_rmds_free(void)
++{
++      unsigned long flags;
++      EIP_RMD *rmd;
++      int svc; 
++      
++      spin_lock_irqsave(&eip_rx->lock, flags);
++      rmd = eip_rx->irq_list;
++      eip_rx->irq_list = NULL;
++      eip_rx->irq_list_nr = 0;
++      spin_unlock_irqrestore(&eip_rx->lock, flags);
++
++      eip_rmd_reclaim(rmd);
++      
++      for (svc = 0; svc < EIP_SVC_NR ; svc++) {
++              
++              while ((rmd = eip_rx->head[svc].busy_list)) {
++                      eip_rx->head[svc].busy_list = NULL;
++                      eip_rmd_reclaim(rmd);
++                      if (eip_rx->head[svc].busy_list) {
++                              EIP_DBG_PRINTF(EIP_DBG_RMD_QUEUE, "Still RMD [%p] on BUSY list SVC 0x%d - Scheduling\n", rmd, svc);     
++                              schedule();
++                      }
++              }
++
++              EIP_ASSERT(EIP_STAT_QUEUED_GET(&eip_rx->head[svc].stats) == EIP_STAT_ALLOC_GET(&eip_rx->head[svc].stats));
++              
++              EIP_DBG_PRINTF(EIP_DBG_GEN, "HEAD[%p] : FREEING RCVR [%p]\n", &eip_rx->head[svc],
++                              eip_rx->head[svc].rcvr);
++              
++              ep_free_rcvr(eip_rx->head[svc].rcvr);
++
++              EIP_DBG_PRINTF(EIP_DBG_EP_DVMA, "HEAD[%p] : RELEASING DVMA [%p]\n", &eip_rx->head[svc], 
++                              eip_rx->head[svc].handle);
++
++              ep_dvma_release(eip_tx->ep_system, eip_rx->head[svc].handle);
++      }
++
++}
++static int eip_rx_queues_low (void) {
++      int svc;
++      for (svc = 0; svc < EIP_SVC_NR; svc++) 
++              if (EIP_STAT_QUEUED_GET(&eip_rx->head[svc].stats)  < EIP_RMD_ALLOC_THRESH) 
++                      return (1);
++      return (0);
++}
++static void eip_rxhandler(EP_RXD * rxd)
++{
++      EIP_RMD *rmd            = (EIP_RMD *) ep_rxd_arg(rxd);
++      EP_STATUS ret           = ep_rxd_status(rxd);
++      EP_PAYLOAD * payload    = ep_rxd_payload(rxd);
++      unsigned long data      = (unsigned long) rmd->skb->data; 
++      int frag_nr             = 0;
++      int len;
++
++      struct sk_buff *skb;
++      static char count = 0;
++
++      atomic_dec(&rmd->head->stats);
++      rmd->rxd = rxd;
++
++      if (likely(ret == EP_SUCCESS)) {
++
++              rmd->head->dma++;
++
++              if ( eip_rx_dropping) {
++                  eip_rmd_requeue(rmd);
++                  return;
++              }
++
++              len = (payload) ? payload->Data[frag_nr++] : ep_rxd_len(rxd);
++
++              EIP_DBG(EIP_DBG_RMD, eip_rmd_display, rmd);
++
++again:
++              if ( (skb = skb_clone(rmd->skb, GFP_ATOMIC)) ) {
++                      unsigned int off = (data - (unsigned long) rmd->skb->data);
++
++                      /* have to set the length before calling
++                       * skb pull as it will not allow you to
++                       * pull past the end */
++
++                      skb_put (skb, off + len);
++                      skb_pull (skb, off);
++
++                      skb->protocol = eth_type_trans(skb, eip_rx->net_device);
++                      skb->ip_summed = eip_checksum_state;
++                      skb->dev = eip_rx->net_device;
++
++                      /* Fabien/David/Mike this is a hack/fix to allow aggrigation of packets to work.
++                       * The problem is ip_frag looks at the truesize to see if it is caching too much space.
++                       * As we are reusing a large skb (cloned) for a number of small fragments, they appear to take up alot of space.
++                       * so ip_frag dropped them after 4 frags (not good). So we lie and set the truesize to just bigger than the data. 
++                       */
++                      if (payload) 
++                              skb->truesize = SKB_DATA_ALIGN(skb->len + EIP_HEADER_PAD) +sizeof(struct sk_buff);
++
++              }
++              if ( (skb) && 
++                   (netif_rx(skb) != NET_RX_DROP)){
++
++                      eip_rx->bytes += len;
++                      
++                      if (payload && payload->Data[frag_nr] ) {
++                              data += EIP_IP_ALIGN(len);
++                              len   = payload->Data[frag_nr++];
++                              goto again;
++                      }
++                      eip_rx->packets += ++frag_nr;
++              } else if ( (eip_rx->dropped++ % 20) == 0)
++                              __EIP_DBG_PRINTK("Packet dropped by the TCP/IP stack - increase /proc/sys/net/core/netdev_max_backlog\n");
++      } else if (ret == EP_SHUTDOWN ) {
++              EIP_DBG2(EIP_DBG_RMD, eip_rmd_display, rmd, "ABORTING\n");
++                ep_complete_receive(rxd);
++                eip_skb_unload(rmd);
++              EIP_STAT_ALLOC_SUB(&rmd->head->stats, 1);
++                eip_rmd_free(rmd);
++              return;
++      } else {
++              EP_ENVELOPE *env = ep_rxd_envelope(rxd);
++              EP_NMD *nmd ;
++              
++              EIP_ERR_PRINTF("RMD[%p] : RECEIVE ret = %d\n", rmd, ret);
++
++              for (len = 0 ; len < env->nFrags ; len++) {
++                      nmd = &env->Frags[len];
++                      EIP_ERR_PRINTF("RMD[%p] : ep_frag #%d nmd_addr [%x] nmd_len %d\n", rmd, len, 
++                                      (unsigned int) nmd->nmd_addr, nmd->nmd_len);
++              }
++              eip_rx->errors++;
++              EIP_ASSERT2(atomic_read(&skb_shinfo(rmd->skb)->dataref) == 1, eip_rmd_display, rmd);
++      }
++
++      /* data is used to store the irq flags */
++      spin_lock_irqsave(&eip_rx->lock, data);
++      rmd->chain.next = eip_rx->irq_list;
++      eip_rx->irq_list = rmd;
++      eip_rx->irq_list_nr++;
++      spin_unlock_irqrestore(&eip_rx->lock, data);
++
++      if (((count++ % eip_rx->sysctl_granularity) == 0) /* and either we have passed up a number of them */
++          || eip_rx_queues_low())                       /* or we are low                                 */
++              tasklet_schedule(&eip_rx->tasklet);
++      else
++      {
++              if ( !timer_pending (&eip_rx_tasklet_timer)  )    /* the timer not already set  */
++                      mod_timer (&eip_rx_tasklet_timer, lbolt);
++      }
++}
++
++/* dest ; if the buffer still reference on it mocve the rmd to the dest list */
++static void eip_rmd_reclaim(EIP_RMD *rmd) 
++{
++      EIP_RMD *rmd_next = rmd;
++      int dataref;
++
++      while (rmd_next) {
++              rmd = rmd_next;
++              rmd_next = rmd_next->chain.next;
++
++              dataref = atomic_read(&skb_shinfo(rmd->skb)->dataref);
++              EIP_ASSERT(dataref > 0);
++              
++              if (dataref == 1) {
++                      eip_rmd_requeue(rmd);
++              } else {
++                      rmd->chain.next = rmd->head->busy_list;
++                      rmd->head->busy_list = rmd;
++              }
++      }
++}
++static void eip_rx_tasklet(unsigned long arg)
++{
++      EIP_RMD *rmd, *rmd_next;
++      unsigned long flags;
++      short svc, queued;
++      int   needs_reschedule;
++
++      if (eip_rx_tasklet_locked) /* we dont want the tasklet to do anything when we are finishing */
++          return;
++
++      for (svc = 0; svc < EIP_SVC_NR; svc++) {
++              rmd = eip_rx->head[svc].busy_list;
++              eip_rx->head[svc].busy_list = NULL;
++              eip_rmd_reclaim(rmd);
++      }
++
++      spin_lock_irqsave(&eip_rx->lock, flags);
++      rmd = eip_rx->irq_list;
++      eip_rx->irq_list = NULL;
++      eip_rx->irq_list_nr = 0;
++      spin_unlock_irqrestore(&eip_rx->lock, flags);
++      
++      eip_rmd_reclaim(rmd);
++
++      needs_reschedule = 0;
++
++      for (svc = 0; svc < EIP_SVC_NR; svc++) {
++              /* the plan is : allocate some more if possible or steall some dvma space from those on the EIP_BUSY_LIST */
++              queued = EIP_STAT_QUEUED_GET(&eip_rx->head[svc].stats);
++
++              EIP_ASSERT(queued >= 0 && queued <= EIP_RMD_MAX_NR);    
++              
++              if (queued < EIP_RMD_ALLOC_THRESH) {
++                      short allocated = EIP_STAT_ALLOC_GET(&eip_rx->head[svc].stats);
++                      short how_many; 
++
++                      EIP_ASSERT(allocated >= 0 && allocated <= EIP_RMD_MAX_NR);
++                      
++                      if (likely(allocated < eip_rx->rmd_max_nr)) {
++
++                              how_many = (((allocated / EIP_RMD_ALLOC_STEP) + 1) * EIP_RMD_ALLOC_STEP);
++                              if (how_many > eip_rx->rmd_max_nr)
++                                      how_many = eip_rx->rmd_max_nr;
++
++                              for (; allocated < how_many &&  
++                                                      (eip_rmd_alloc_queue(svc, allocated * EIP_DVMA_PAGES((EIP_SVC_SMALLEST_LEN << svc)), 
++                                                                            GFP_ATOMIC, EP_NO_ALLOC|EP_NO_SLEEP) == 0) ; allocated++);
++                              if ( allocated != how_many ) {
++                                      eip_rx->reschedule++;
++                                      needs_reschedule = 1;
++                              }
++                      } else {
++                              /* steal how_many rmds and put them on the aside list */
++                              how_many = EIP_RMD_ALLOC_THRESH - queued;
++
++                              EIP_ASSERT(how_many >= 0 && how_many <= EIP_RMD_ALLOC_THRESH);
++                              
++                              rmd_next = eip_rx->head[svc].busy_list;
++                              eip_rx->head[svc].busy_list = NULL;
++
++                              while (how_many-- && rmd_next) {
++                                      rmd = rmd_next;
++                                      rmd_next = rmd_next->chain.next;
++
++                                      if (eip_rmd_alloc_replace(rmd, svc, GFP_ATOMIC)) {
++                                              rmd_next = rmd;
++                                              break;
++                                      }
++                              }
++                              eip_rx->head[svc].busy_list = rmd_next;
++                              if ( how_many )
++                                      needs_reschedule = 1;
++                      }
++              }
++      }
++      
++      if (needs_reschedule) 
++      {
++              if ( !timer_pending (&eip_rx_tasklet_timer)) 
++                      mod_timer (&eip_rx_tasklet_timer, lbolt);
++      }
++}
++static void eip_rx_tasklet_resched(unsigned long arg)
++{
++      tasklet_schedule(&eip_rx->tasklet);     
++}
++
++static inline void eip_tmd_init(EIP_TMD * tmd, unsigned long buff_base, EIP_TMD_HEAD * head, unsigned long buff_len,
++                              int dvma_idx)
++{
++      tmd->dvma_idx = dvma_idx;
++      tmd->dma_base = buff_base;
++      tmd->dma_len = -1;
++      tmd->skb = NULL;
++      tmd->head = head;
++      tmd->chain.next = NULL;
++
++      if (tmd->head != &eip_tx->head[EIP_TMD_STD]) {
++              tmd->nmd.nmd_len = buff_len;
++              eip_tmd_load(tmd);
++      } else  {
++              tmd->nmd.nmd_len  = -1;
++              tmd->nmd.nmd_addr = 0;
++      }
++}
++
++static inline EIP_TMD *eip_tmd_get(int id)
++{
++      unsigned long flags;
++      EIP_TMD *tmd = NULL;
++      spin_lock_irqsave(&eip_tx->lock, flags);
++      while ((tmd = eip_tx->head[id].tmd) == NULL) {
++              spin_unlock_irqrestore(&eip_tx->lock, flags);
++              if (ep_enable_txcallbacks(eip_tx->xmtr) == 0) {
++
++                      spin_lock_irqsave (&eip_tx->lock, flags);
++                      if (eip_tx->head[id].tmd == NULL) {
++                              __EIP_DBG_PRINTF("Cannot get a TMD on head %d ... stopping queue\n", id);
++                              
++                              eip_stop_queue ();
++                              
++                              spin_unlock_irqrestore (&eip_tx->lock, flags);
++
++                              return NULL;
++                      }
++                      spin_unlock_irqrestore (&eip_tx->lock, flags);
++              }
++
++              ep_disable_txcallbacks(eip_tx->xmtr);
++              spin_lock_irqsave(&eip_tx->lock, flags);
++      }
++      eip_tx->head[id].tmd = tmd->chain.next;
++      spin_unlock_irqrestore(&eip_tx->lock, flags);
++      atomic_dec(&tmd->head->stats);
++      return tmd;
++}
++
++static inline void eip_tmd_put(EIP_TMD * tmd)
++{
++      unsigned long flags;
++
++      tmd->skb = NULL;
++
++      spin_lock_irqsave(&eip_tx->lock, flags);
++      tmd->chain.next = tmd->head->tmd;
++      tmd->head->tmd = tmd;
++      spin_unlock_irqrestore(&eip_tx->lock, flags);
++      atomic_inc(&tmd->head->stats);
++
++      eip_start_queue();
++
++      EIP_DBG_PRINTF(EIP_DBG_TMD_QUEUE, "TMD [%p] : REQUEUED\n", tmd);
++}
++static inline void eip_tmd_load(EIP_TMD * tmd)
++{
++      EP_RAILMASK rmask = tx_railmask;
++
++      __eip_tmd_load(tmd, &rmask);
++      
++      EIP_DBG_PRINTF(EIP_DBG_EP_DVMA, "TMD [%p] : LOADED\n", tmd);
++}
++static inline void eip_tmd_unload(EIP_TMD * tmd)
++{
++      __eip_tmd_unload(tmd);
++      
++      EIP_DBG_PRINTF(EIP_DBG_EP_DVMA, "TMD [%p] : UNLOADED\n", tmd);
++}
++static inline void eip_tmd_free(EIP_TMD * tmd)
++{
++      eip_buff_free(tmd->dma_base, tmd->nmd.nmd_len);
++      
++      EIP_DBG_PRINTF(EIP_DBG_MEMFREE, "TMD [%p] : FREED\n", tmd);
++      
++      EIP_STAT_ALLOC_SUB(&tmd->head->stats, 1);
++}
++
++/* tmd on a separate block */
++static inline EIP_TMD *eip_tmd_alloc_queue(EIP_TMD * tmd, EIP_TMD_HEAD * head, int dvma_idx)
++{
++      eip_tmd_init(tmd, 0, head, -1, dvma_idx);
++
++      eip_tmd_put(tmd);
++
++      EIP_STAT_ALLOC_ADD(&tmd->head->stats, 1);
++      EIP_DBG(EIP_DBG_TMD, eip_tmd_display, tmd);
++      return tmd;
++}
++/* tmd on the buffer */
++static inline EIP_TMD *eip_tmd_alloc_queue_copybreak(EIP_TMD_HEAD * head, int dvma_idx)
++{
++      EIP_TMD *tmd;
++      unsigned long buff_base;
++
++      if (!(buff_base = eip_buff_alloc(tx_copybreak_max + sizeof(EIP_TMD), GFP_KERNEL)))
++              return NULL;
++
++      tmd = (EIP_TMD *) (buff_base + tx_copybreak_max);
++      eip_tmd_init(tmd, buff_base, head, tx_copybreak_max, dvma_idx);
++
++      eip_tmd_put(tmd);
++      EIP_STAT_ALLOC_ADD(&tmd->head->stats, 1);
++      EIP_DBG(EIP_DBG_TMD, eip_tmd_display, tmd);
++      return tmd;
++}
++
++/* ipf are on the buffer */
++static inline EIP_TMD *eip_tmd_alloc_queue_aggreg(EIP_TMD_HEAD * head, int dvma_idx)
++{
++      EIP_TMD *tmd;
++      unsigned long buff_base;
++
++      if (!(buff_base = eip_buff_alloc(EIP_SVC_BIGGEST_LEN, GFP_KERNEL)))
++              return NULL;
++
++      tmd = (EIP_TMD *) (buff_base + EIP_SVC_BIGGEST_LEN - sizeof(EIP_IPFRAG));
++      eip_tmd_init(tmd, buff_base, head, EIP_SVC_BIGGEST_LEN - sizeof(EIP_IPFRAG), dvma_idx);
++
++      eip_tmd_put(tmd);
++      EIP_STAT_ALLOC_ADD(&tmd->head->stats, 1);
++      EIP_DBG(EIP_DBG_TMD, eip_tmd_display, tmd);
++      return tmd;
++}
++
++static int eip_tmds_alloc()
++{
++      int i;
++      int page_nr;
++      EIP_TMD *tmd;
++
++      page_nr = EIP_DVMA_PAGES(tx_copybreak_max);
++
++      eip_tx->head[EIP_TMD_COPYBREAK].handle = eip_dma_reserve(page_nr * eip_tx->tmd_max_nr, EP_PERM_READ);
++      
++      EIP_DBG(EIP_DBG_TMD_HEAD, eip_tmd_head_display, &eip_tx->head[EIP_TMD_COPYBREAK]);
++
++      for (i = 0; i < EIP_TMD_NR; i++) {
++              if (!eip_tmd_alloc_queue_copybreak(&eip_tx->head[EIP_TMD_COPYBREAK], i * page_nr))
++                      return -ENOMEM;
++      }
++
++      eip_tx->head[EIP_TMD_STD].handle =
++          eip_dma_reserve(EIP_DVMA_PAGES(EIP_SVC_BIGGEST_LEN) * eip_tx->tmd_max_nr, EP_PERM_READ);
++      
++      EIP_DBG(EIP_DBG_TMD_HEAD, eip_tmd_head_display, &eip_tx->head[EIP_TMD_STD]);
++
++      tmd = kmalloc(sizeof(EIP_TMD) * EIP_TMD_NR, GFP_KERNEL);
++      if (!tmd) {
++              EIP_ERR_PRINTF("Cannot ALLOCATE %d of tmds\n", (int) sizeof(EIP_TMD) * EIP_TMD_NR);
++              return -ENOMEM;
++      }
++      
++      page_nr = EIP_DVMA_PAGES(EIP_SVC_BIGGEST_LEN);
++      
++      for (i = 0; i < EIP_TMD_NR; i++, tmd++) {
++              if (!eip_tmd_alloc_queue(tmd, &eip_tx->head[EIP_TMD_STD], i * page_nr))
++                      return -ENOMEM;
++      }
++
++      page_nr = EIP_DVMA_PAGES(EIP_SVC_BIGGEST_LEN);
++
++      eip_tx->head[EIP_TMD_AGGREG].handle = eip_dma_reserve(page_nr * eip_tx->tmd_max_nr, EP_PERM_READ);
++      EIP_DBG(EIP_DBG_TMD_HEAD, eip_tmd_head_display, &eip_tx->head[EIP_TMD_AGGREG]);
++
++      for (i = 0; i < EIP_TMD_NR; i++) {
++              if (!eip_tmd_alloc_queue_aggreg(&eip_tx->head[EIP_TMD_AGGREG], i * page_nr))
++                      return -ENOMEM;
++      }
++      return 0;
++}
++
++static void eip_tmds_free(void) 
++{
++      EIP_TMD *tmd;
++      EIP_TMD *tmd_next;
++      int i;
++      
++      ep_poll_transmits(eip_tx->xmtr);
++
++      for (i = 0 ; i < 3 ; i++) {
++again:
++              if (EIP_STAT_QUEUED_GET(&eip_rx->head[i].stats) < EIP_STAT_ALLOC_GET(&eip_rx->head[i].stats)) {
++                      EIP_DBG_PRINTF(EIP_DBG_TMD, "Polling XMTR [%p]\n", eip_tx->xmtr);       
++                      ep_poll_transmits(eip_tx->xmtr);
++                      goto again;
++              }
++      }
++      /* everything should be queued */
++        if ((tmd = eip_tx->head[EIP_TMD_COPYBREAK].tmd)) {
++            do {
++                      tmd_next = tmd->chain.next;
++                        eip_tmd_unload(tmd);
++                      
++                      EIP_DBG(EIP_DBG_TMD, eip_tmd_display, tmd);
++                      
++                        eip_tmd_free(tmd);
++            } while (tmd_next && (tmd = tmd_next));
++        }
++      
++      EIP_DBG_PRINTF(EIP_DBG_TMD_EP_DVMA, "HEAD[EIP_TMD_COPYBREAK] release DVMA [%p]\n",
++                      eip_tx->head[EIP_TMD_COPYBREAK].handle);        
++      
++        ep_dvma_release(eip_tx->ep_system, eip_tx->head[EIP_TMD_COPYBREAK].handle);
++      
++      /* these ones have been allocated as a block */
++      if ((tmd = eip_tx->head[EIP_TMD_STD].tmd)) {
++              do {
++                      if (tmd->dvma_idx == 0 ) {
++                              kfree(tmd);
++                              /* eip_tmd_free(tmd); */
++                              EIP_STAT_ALLOC_SUB(&tmd->head->stats, EIP_TMD_NR);
++                              tmd_next = NULL;
++                              EIP_DBG_PRINTF(EIP_DBG_TMD_EP_DVMA, "TMD HEAD[%p] : [EIP_TMD_STD] BLOCK FREED\n", tmd); 
++                      } else 
++                              tmd_next = tmd->chain.next;
++              } while (tmd_next && (tmd = tmd_next));
++      }
++      EIP_DBG_PRINTF(EIP_DBG_TMD_EP_DVMA, "HEAD[EIP_TMD_STD] release DVMA [%p]\n", 
++                      eip_tx->head[EIP_TMD_STD].handle);      
++      
++        ep_dvma_release(eip_tx->ep_system, eip_tx->head[EIP_TMD_STD].handle);
++      
++      if ((tmd = eip_tx->head[EIP_TMD_AGGREG].tmd)) {
++              do {
++                      tmd_next = tmd->chain.next;
++
++                      EIP_DBG(EIP_DBG_TMD, eip_tmd_display, tmd);
++                      
++                      eip_tmd_unload(tmd);
++                      eip_tmd_free(tmd);
++              } while (tmd_next && (tmd = tmd_next));
++      }
++      EIP_DBG_PRINTF(EIP_DBG_TMD_EP_DVMA, "TMD HEAD[%p] : [EIP_TMD_AGGREG] release DVMA\n", 
++                      eip_tx->head[EIP_TMD_AGGREG].handle);   
++      
++        ep_dvma_release(eip_tx->ep_system, eip_tx->head[EIP_TMD_AGGREG].handle);
++
++      ep_free_xmtr(eip_tx->xmtr);
++      EIP_DBG_PRINTF(EIP_DBG_TMD, "XMTR[%p] : FREED\n", eip_tx->xmtr);
++}
++
++static inline void eip_ipf_skb_add(EIP_IPFRAG * ipf, struct sk_buff *skb)
++{
++      int align = EIP_IP_ALIGN(skb->len);
++      
++      
++      if (ipf->dma_len == -1) {       /* like a virgin; touched for the very first time */
++              do_gettimeofday(&ipf->timestamp);
++              /* FIXE ME put that in release tmd code */
++              ipf->frag_nr            = 0;
++              ipf->dma_len            = 0;
++              ipf->datagram_len       = -1;
++              ipf->dma_correction     = 0;
++      }
++      
++      memcpy((void *) (ipf->dma_base + ipf->dma_len), skb->data, skb->len);
++      
++      if (ipf->datagram_len == -1) {
++              struct iphdr * iph = skb->nh.iph;
++              int offset = ntohs(iph->frag_off);
++
++              /* last one ? ;  offset & ~IP_OFFSET = IP fragment flags */
++              if (((offset & ~IP_OFFSET) & IP_MF) == 0) {
++                      offset &= IP_OFFSET;
++                      offset <<= 3;    
++                      ipf->datagram_len = offset + htons(iph->tot_len) - sizeof(struct iphdr);
++              }
++      }
++
++      skb->next                       = ipf->skb;
++      ipf->skb                        = skb;
++      ipf->payload.Data[ipf->frag_nr] = skb->len;
++      ipf->dma_len                   += align;
++      ipf->dma_correction            += align - skb->len  + ETH_HLEN + sizeof(struct iphdr);
++      /* FIXME ; Count got wrong if ip header has options */
++
++      ipf->frag_nr++;
++
++      EIP_DBG2(EIP_DBG_TMD, eip_ipf_display, ipf, "ADDED skb[%p] len %db ALIGNED(%db)\n", skb, skb->len, EIP_IP_ALIGN(skb->len));
++}
++
++#define eip_ipf_hasroom(ipf, skb) ((ipf->dma_len + EIP_IP_ALIGN(skb->len) < eip_tx->sysctl_ipfrag_copybreak))
++int eip_hard_start_xmit(struct sk_buff *skb, struct net_device *devnet) 
++{
++
++      EIP_TMD *tmd;
++      EP_NMD nmd;
++      struct iphdr *iph;
++      int j;
++
++      if (skb->destructor){
++              atomic_inc(&eip_tx->destructor);
++              tasklet_schedule(&eip_tx->tasklet);
++      } 
++
++      if (!(iph = eip_ipfrag_get(skb->data)) || (eip_tx->sysctl_aggregation == 0)) { /* not ip fragment */
++no_aggreg:
++              j = (skb->len < eip_tx->sysctl_copybreak) ? EIP_TMD_COPYBREAK : EIP_TMD_STD; /* j = head id */
++              
++              if (!(tmd = eip_tmd_get(j))) {
++                      if (skb->destructor)
++                              atomic_dec(&eip_tx->destructor);
++                      return 1;
++              }
++              
++              tmd->dma_len    = skb->len;
++              tmd->skb        = skb;
++              tmd->skb->next  = NULL;
++              tmd->chain.next = NULL;
++              
++              if (j == EIP_TMD_COPYBREAK) {
++                      memcpy((void *) tmd->dma_base, skb->data, skb->len);
++                      
++                      ep_nmd_subset(&nmd, &tmd->nmd, 0, skb->len);
++#ifdef EIP_MORE_STATS
++                      eip_tx->sent_copybreak++;
++#endif
++                      return eip_do_xmit(tmd, &nmd, NULL);
++              }
++              tmd->dma_base           = (unsigned long) skb->data;
++              tmd->nmd.nmd_len        = skb->len;
++              eip_tmd_load(tmd);
++
++#ifdef EIP_MORE_STATS
++              eip_tx->sent_std++;
++#endif
++              return eip_do_xmit(tmd, &tmd->nmd, NULL);
++      } else if ( skb->len > EIP_SVC_BIGGEST_LEN/2 ) { 
++              /* don't aggregate when we have a full mtu of data */
++              /* or more than 32k ; in this case it is cheaper   */
++              /* to just map the buffer and send it              */
++              goto no_aggreg;
++      } else {
++              EIP_IPFRAG *ipf = NULL;
++              unsigned long flags;
++              struct list_head *l;
++              struct iphdr *iph2;
++              int i;
++              __u16 id = iph->id;
++              __u32 saddr = iph->saddr;
++              __u32 daddr = iph->daddr;
++              __u8 protocol = iph->protocol;
++
++                      EIP_DBG(EIP_DBG_IPH, eip_iph_display, iph);
++
++              j = 0;
++
++              /* here we can't have full mtu size aggregated packet */
++              EIP_ASSERT_RET(skb->len < eip_tx->sysctl_ipfrag_copybreak, 0);
++
++              spin_lock_irqsave(&eip_tx->ipfraglock, flags);
++              list_for_each(l, &eip_tx->ipfrag) {
++                      ipf = list_entry(l, EIP_IPFRAG, list);
++                      iph2 = eip_ipfrag_get((char *) ipf->dma_base);
++                      
++                        EIP_ASSERT(iph2);
++                      
++                      if ((iph2->id == id) && 
++                                      (get_unaligned(&iph2->saddr) == saddr) && 
++                                      (get_unaligned(&iph2->daddr) == daddr) && 
++                                      (iph2->protocol == protocol)) {
++                              /* || timeout */
++                              if (eip_ipf_hasroom(ipf, skb)) {
++                                      
++                                      eip_ipf_skb_add(ipf, skb);
++                                      
++                                      if ((ipf->datagram_len != -1) && 
++                                                      (ipf->dma_len == (ipf->datagram_len + ipf->dma_correction) || 
++                                                       ipf->frag_nr == (128 / sizeof(uint32_t)))) {
++send_aggreg:
++                                              ipf->payload.Data[ipf->frag_nr] = 0;
++                                              list_del(&ipf->list);
++                                              eip_tx->ipfrag_count--;
++                                              spin_unlock_irqrestore(&eip_tx->ipfraglock, flags);
++                                      
++                                              ep_nmd_subset(&nmd, &ipf->nmd, 0, ipf->dma_len);
++                                              
++#ifdef EIP_MORE_STATS
++                                              eip_tx->sent_aggreg++;
++#endif
++                                              if ((i = eip_do_xmit((EIP_TMD *) ipf, &nmd, &ipf->payload)) != EP_SUCCESS)
++                                                      return i;
++                                              if (j)
++                                                      goto new;
++                                              return 0;
++                                      }
++                                      
++                                      spin_unlock_irqrestore(&eip_tx->ipfraglock, flags);
++                                      tasklet_schedule(&eip_tx->tasklet);
++                                      return 0;
++                              } else {
++                                      EIP_DBG_PRINTF(EIP_DBG_TMD, "IPF[%p] : FULL %db full - sending it\n", ipf, ipf->dma_len);
++                                      j = 1;
++                                      goto send_aggreg;
++                              }
++                      }
++              }
++              spin_unlock_irqrestore(&eip_tx->ipfraglock, flags);
++new:
++              if (!(ipf = (EIP_IPFRAG *) eip_tmd_get(EIP_TMD_AGGREG)))
++                      goto no_aggreg;
++
++              eip_ipf_skb_add(ipf, skb);
++              
++              spin_lock_irqsave(&eip_tx->ipfraglock, flags);
++              list_add_tail(&ipf->list, &eip_tx->ipfrag);
++              eip_tx->ipfrag_count++;
++              spin_unlock_irqrestore(&eip_tx->ipfraglock, flags);
++              tasklet_schedule(&eip_tx->tasklet);
++      }
++      return 0;
++}
++static int eip_do_xmit(EIP_TMD * tmd, EP_NMD *nmd, EP_PAYLOAD *payload)
++{
++      EIP_HEADER *eiph = (EIP_HEADER *) tmd->dma_base;
++      int         attr = EP_SET_DATA((EP_NO_SLEEP | EP_NO_INTERRUPT | EP_NO_FAILOVER), EP_TYPE_SVC_INDICATOR, EP_SVC_EIP);
++      unsigned long flags;
++      int svc, rnum;
++
++      SIZE_TO_SVC(nmd->nmd_len, svc);
++
++      EIP_DBG(EIP_DBG_TMD, eip_tmd_display, tmd);
++      /* EIP_DBG(eip_eiph_display(eiph)); */
++      
++      if (unlikely (eiph->h_dhost.ip_bcast))
++              rnum = ep_pickRail (EP_NMD_RAILMASK (nmd) & tx_railmask & ep_xmtr_availrails(eip_tx->xmtr));
++      else
++              rnum = ep_pickRail (EP_NMD_RAILMASK (nmd) & tx_railmask & ep_xmtr_noderails(eip_tx->xmtr, ntohs(eiph->h_dhost.ip_addr)));
++
++      if (rnum >= 0)
++              attr = EP_SET_PREFRAIL(attr, rnum);
++
++      /* add to inuse list  */
++      spin_lock_irqsave (&eip_tx->lock, flags);
++      list_add_tail (&tmd->chain.link, &eip_tx->inuse);
++      spin_unlock_irqrestore (&eip_tx->lock, flags);
++
++      /* ENOMEM EINVAL ECONNREFUSED ESUCCESS */
++      svc = (unlikely(eiph->h_dhost.ip_bcast)) ? 
++              ep_multicast_message(eip_tx->xmtr, -1, -1, NULL, EIP_SVC_EP(svc), attr | EP_NOT_MYSELF, eip_txhandler, tmd, payload, nmd, 1) :
++
++              ep_transmit_message(eip_tx->xmtr, ntohs(eiph->h_dhost.ip_addr), EIP_SVC_EP(svc),  attr, eip_txhandler, tmd, payload, nmd, 1);
++              
++      if (likely(svc == EP_SUCCESS))
++              return 0;
++      else if (svc == ENOMEM) {
++              EIP_ERR_PRINTF("%s", "Memory allocation error ...\n");
++              eip_tx->errors++;
++      }
++      else
++      {
++              /* EP_EINVAL occurs when the svc has a bad value or the iovec has too many frag; */
++              /* we don't use the latter option here                                        */
++              __EIP_DBG_PRINTF("TMD [%p] : DROPPED skb[%p] status = %d from ep_?_message\n", tmd, tmd->skb, svc);
++
++              eip_tx->dropped++;
++      }
++
++      eip_txhandler(NULL, tmd, -99);
++
++      /* Quadrics GNAT sw-elan/4397 - since we will "never" be able to send this packet to the */
++      /* destination node, we drop it and feign success - this has the same behaviour as an    */
++      /* ethernet where it sticks the packet on the wire, but no-one receives it.              */
++      return 0;
++}
++
++static void eip_txhandler(EP_TXD * txd, void *arg, EP_STATUS status)
++{
++      EIP_TMD *tmd = (EIP_TMD *) arg;
++      struct sk_buff *skb_next;
++      unsigned long flags;
++      int svc = 0;
++      
++      if (likely(status == EP_SUCCESS)) {
++              SIZE_TO_SVC(tmd->dma_len, svc);
++              eip_tx->dma[svc]++;
++              eip_tx->bytes += tmd->dma_len;
++              
++              if (tmd->head == &eip_tx->head[EIP_TMD_AGGREG]) {
++                      EIP_IPFRAG *ipf = (EIP_IPFRAG *) tmd;
++                      eip_tx->packets += ipf->frag_nr;
++              } else
++                      eip_tx->packets++;
++      } else {
++              if (tmd->head == &eip_tx->head[EIP_TMD_AGGREG]) {
++                      EIP_IPFRAG *ipf = (EIP_IPFRAG *) tmd;
++                      eip_tx->dropped += ipf->frag_nr;
++                      EIP_DBG_PRINTF(EIP_DBG_TMD, "txhandler aggreg packet dropped status = %d\n", status);
++              } else  {
++                      eip_tx->dropped++;
++                      EIP_DBG_PRINTF(EIP_DBG_TMD, "txhandler packet dropped status = %d\n", status);
++              }
++      }
++
++      if (tmd->head == &eip_tx->head[EIP_TMD_STD]) {
++              eip_tmd_unload(tmd);
++              tmd->dma_base = 0;
++              tmd->nmd.nmd_len = -1;
++      }
++              
++      tmd->dma_len = -1;
++      
++      svc = 0;
++      while (tmd->skb) {
++              svc++;
++              
++              if (tmd->skb->destructor)
++                      atomic_dec(&eip_tx->destructor);
++
++              skb_next = tmd->skb->next;
++              dev_kfree_skb_any(tmd->skb);
++              tmd->skb = skb_next;
++      }
++      EIP_DBG_PRINTF(EIP_DBG_TMD, "IPF/TMD [%p] : %d skb RELEASE/FREED\n", tmd, svc);
++
++      /* remove from inuse list  */
++      spin_lock_irqsave (&eip_tx->lock, flags);
++      list_del (&tmd->chain.link);
++      spin_unlock_irqrestore (&eip_tx->lock, flags);
++
++      eip_tmd_put(tmd);
++}
++
++static void eip_tx_tasklet(unsigned long arg)
++{
++      struct timeval now;
++      unsigned long flags;
++      EIP_IPFRAG *ipf, *ipfq = NULL;
++      EP_NMD nmd;
++      struct list_head *list;
++      struct list_head *tmp;
++      char resched = 0;
++      char poll = 1;
++      
++      do_gettimeofday(&now);
++      
++      spin_lock_irqsave(&eip_tx->ipfraglock, flags);
++      if (eip_tx->ipfrag_count) {
++              list_for_each_safe(list, tmp, &eip_tx->ipfrag) {
++                      ipf = list_entry(list, EIP_IPFRAG, list);
++                      /* delta = (((now.tv_sec - ipf->timestamp.tv_sec) * 1000000UL) + now.tv_usec) - ipf->timestamp.tv_usec; */
++                      if (((((now.tv_sec - ipf->timestamp.tv_sec) * 1000000UL) + now.tv_usec) - 
++                                      ipf->timestamp.tv_usec) >= (1000UL * eip_tx->sysctl_ipfrag_to)) {
++                              list_del(&ipf->list);
++                              eip_tx->ipfrag_count--;
++                              ipf->chain.next = (EIP_TMD *) ipfq;
++                              ipfq = ipf;
++                      }
++              }
++      }
++      if (eip_tx->ipfrag_count)
++              resched = 1;
++      spin_unlock_irqrestore(&eip_tx->ipfraglock, flags);
++
++      while (ipfq) {
++              poll = 0;
++
++              ep_nmd_subset(&nmd, &ipfq->nmd, 0, ipfq->dma_len);
++              
++              ipfq->payload.Data[ipfq->frag_nr] = 0;
++              
++#ifdef EIP_MORE_STATS
++              eip_tx->sent_aggreg++;
++#endif
++              ipf = (EIP_IPFRAG *) ipfq->chain.next;
++              eip_do_xmit((EIP_TMD *) ipfq, &nmd, &ipfq->payload);
++              ipfq = ipf;
++       }
++       
++       if (poll)
++               ep_poll_transmits(eip_tx->xmtr);
++
++       if (atomic_read(&eip_tx->destructor) || resched )
++               tasklet_schedule(&eip_tx->tasklet);
++}
++void eip_start_queue()
++{
++      if (netif_queue_stopped(eip_tx->net_device)) {
++              EIP_DBG_PRINTK(EIP_DBG_GEN, "Waking up %s queue\n", eip_tx->net_device->name);
++              netif_wake_queue(eip_tx->net_device);
++      }
++}
++void eip_stop_queue()
++{
++      EIP_DBG_PRINTK(EIP_DBG_GEN, "Stopping %s queue\n", eip_tx->net_device->name);
++      netif_stop_queue(eip_tx->net_device);
++}
++
++static int eip_open(struct net_device *devnet)
++{
++      if (devnet->flags & IFF_PROMISC)
++              EIP_DBG_PRINTK(EIP_DBG_GEN, "%s entering in promiscuous mode\n", devnet->name);
++
++      netif_start_queue(devnet);
++      EIP_DBG_PRINTK(EIP_DBG_GEN, "iface %s MAC %02x:%02x:%02x:%02x:%02x:%02x up\n",
++                      devnet->name, (devnet->dev_addr[0]) & 0xff,
++                      (devnet->dev_addr[1]) & 0xff, (devnet->dev_addr[2]) & 0xff, (devnet->dev_addr[3]) & 0xff,
++                      (devnet->dev_addr[4]) & 0xff, (devnet->dev_addr[5]) & 0xff);
++      return 0;
++}
++
++static int eip_close(struct net_device *devnet)
++{
++      if (devnet->flags & IFF_PROMISC)
++              EIP_DBG_PRINTK(EIP_DBG_GEN, "%s leaving promiscuous mode\n", devnet->name);
++
++      netif_stop_queue(devnet);
++
++      eip_rx_tasklet(0);
++
++      EIP_DBG_PRINTK(EIP_DBG_GEN, "iface %s MAC %02x:%02x:%02x:%02x:%02x:%02x down\n", 
++              devnet->name, (devnet->dev_addr[0]) & 0xff,
++              (devnet->dev_addr[1]) & 0xff, (devnet->dev_addr[2]) & 0xff, (devnet->dev_addr[3]) & 0xff,
++              (devnet->dev_addr[4]) & 0xff, (devnet->dev_addr[5]) & 0xff);
++      return 0;
++}
++
++static struct net_device_stats *eip_get_stats(struct net_device *devnet)
++{
++      static struct net_device_stats stats;
++
++      stats.rx_packets = eip_rx->packets;
++      stats.rx_bytes = eip_rx->bytes;
++      stats.rx_errors = eip_rx->errors;
++      stats.rx_dropped = eip_rx->dropped;
++
++      stats.tx_packets = eip_tx->packets;
++      stats.tx_bytes = eip_tx->bytes;
++      stats.tx_errors = eip_tx->errors;
++      stats.tx_dropped = eip_tx->dropped;
++      return &stats;
++}
++
++static int eip_change_mtu(struct net_device *devnet, int mtu)
++{
++      if (mtu <= EIP_MTU_MAX) {
++              EIP_DBG_PRINTK(EIP_DBG_GEN, "MTU size changed from %d to %d\n", devnet->mtu, mtu);
++              devnet->mtu = mtu;
++      }
++      return 0;
++}
++
++#ifdef MODULE
++int eip_init(void)
++{
++      struct net_device *devnet;
++      int errno = 0;
++
++      eip_rx_dropping = 0; 
++      eip_rx_tasklet_locked = 1;
++
++      /* timer up but not started */
++      init_timer (&eip_rx_tasklet_timer);
++      eip_rx_tasklet_timer.function = eip_rx_tasklet_resched;
++      eip_rx_tasklet_timer.data     = (unsigned long) 0;
++      eip_rx_tasklet_timer.expires  = lbolt + hz;
++
++      devnet = alloc_etherdev(sizeof(EIP_RX) + sizeof(EIP_TX));
++      if (!devnet) {
++              EIP_ERR_PRINTF("Unable to ALLOCATE etherdev structure\n");
++              return -ENOMEM;
++      }
++      strcpy (devnet->name, "eip0");
++
++      EIP_DBG_PRINTK(EIP_DBG_GEN, "Enabling aggregation code\n");
++      devnet->change_mtu = eip_change_mtu;
++      devnet->mtu = EIP_MTU_MAX;
++      devnet->open = eip_open;
++      devnet->stop = eip_close;
++      devnet->hard_start_xmit = eip_hard_start_xmit;
++      devnet->get_stats = eip_get_stats;
++
++        /* devnet->features |= (NETIF_F_DYNALLOC); */
++        /* devnet->features = (NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA); */
++        /* devnet->features |= (NETIF_F_SG|NETIF_F_FRAGLIST|NETIF_F_HIGHDMA|NETIF_F_HW_CSUM); */
++
++      eip_rx = (EIP_RX *) devnet->priv;
++      eip_tx = (EIP_TX *) (eip_rx + 1);
++
++      /* instance 0 */
++      eip_tx->ep_system = ep_system();
++      if (eip_tx->ep_system == NULL) {
++              EIP_ERR_PRINTF("kernel comms for iface %s does not exist\n", devnet->name);
++              errno = -ENXIO;
++              goto out;
++      }
++      if (ep_waitfor_nodeid(eip_tx->ep_system) == ELAN_INVALID_NODE) {
++              EIP_ERR_PRINTF("network position not found\n");
++              errno = -EAGAIN;
++              goto out;
++      }
++      eip_tx->xmtr = ep_alloc_xmtr(eip_tx->ep_system);
++      if (!eip_tx->xmtr) {
++              EIP_ERR_PRINTF("Cannot create allocated transmitter - maybe cable is disconnected\n");
++              errno = -EAGAIN;
++              goto out;
++      }
++      /* assign MAC address */
++      *((int *) &devnet->dev_addr[4]) = htons(ep_nodeid(eip_tx->ep_system));
++      eip_rx->net_device = devnet;
++      eip_tx->net_device = devnet;
++
++      atomic_set(&eip_tx->destructor, 0);
++
++      if ((tmd_max >= EIP_TMD_MIN_NR) && (tmd_max <= EIP_TMD_MAX_NR)) {
++              EIP_DBG_PRINTF(EIP_DBG_GEN, "Setting tmd_max_nr to %d\n", tmd_max);
++              eip_tx->tmd_max_nr = tmd_max;
++      } else {
++              EIP_ERR_PRINTF("parameter error : %d <= tmd_max(%d) <= %d using default %d\n", 
++                              EIP_TMD_MIN_NR, tmd_max, EIP_TMD_MAX_NR, EIP_TMD_MAX_NR);
++              eip_tx->tmd_max_nr = EIP_TMD_MAX_NR;
++      }
++
++      if ((rmd_max >= EIP_RMD_MIN_NR) && (rmd_max <= EIP_RMD_MAX_NR)) {
++              EIP_DBG_PRINTF(EIP_DBG_GEN, "Setting rmd_max_nr to %d\n", rmd_max);
++              eip_rx->rmd_max_nr = rmd_max;
++      } else {
++              EIP_ERR_PRINTF("parameter error : %d <= rmd_max(%d) <= %d using default %d\n", EIP_RMD_MIN_NR,
++                         rmd_max, EIP_RMD_MAX_NR, EIP_RMD_MAX_NR);
++              eip_rx->rmd_max_nr = EIP_RMD_MAX_NR;
++      }
++
++      if ((rx_envelope_nr > 0) && (rx_envelope_nr <= 1024)) { /* > 1024 don't be silly */
++              EIP_DBG_PRINTK(EIP_DBG_GEN, "Setting rx_envelope_nr to %d\n", rx_envelope_nr);
++      } else {
++              EIP_ERR_PRINTF("parameter error : 0 < rx_envelope_nr(%d) <= 1024 using default %d\n",
++                         rx_envelope_nr, EIP_RX_ENVELOPE_NR);
++              rx_envelope_nr = EIP_RX_ENVELOPE_NR;
++      }
++
++      if (tx_copybreak_max <= EIP_TX_COPYBREAK_MAX) {
++              EIP_DBG_PRINTF(EIP_DBG_GEN, "Setting tx_copybreak_max to %d\n", tx_copybreak_max);
++      } else {
++              EIP_ERR_PRINTF("parameter error : tx_copybreak_max > %d using default %d\n",
++                         EIP_TX_COPYBREAK_MAX, EIP_TX_COPYBREAK_MAX);
++              tx_copybreak_max = EIP_TX_COPYBREAK_MAX;
++      }
++#ifdef EIP_MORE_STATS
++      eip_tx->sent_copybreak = 0;
++      eip_tx->sent_std = 0;
++      eip_tx->sent_aggreg = 0;
++#endif
++
++      eip_tx->ipfrag_count = 0;
++      eip_aggregation_set(1);
++      eip_rx_granularity_set(rx_granularity);
++      eip_tx_copybreak_set(EIP_TX_COPYBREAK);
++      eip_ipfrag_to_set(EIP_IPFRAG_TO);
++      eip_ipfrag_copybreak_set(EIP_IPFRAG_COPYBREAK);
++
++      spin_lock_init(&eip_tx->lock);
++      spin_lock_init(&eip_tx->ipfraglock);
++      spin_lock_init(&eip_rx->lock);
++      tasklet_init(&eip_rx->tasklet, eip_rx_tasklet, 0);
++      tasklet_init(&eip_tx->tasklet, eip_tx_tasklet, 0);
++      INIT_LIST_HEAD(&eip_tx->ipfrag);
++      INIT_LIST_HEAD(&eip_tx->inuse);
++
++      /* if we fail here cannot do much yet; waiting for rcvr remove code in ep. */
++      errno = eip_tmds_alloc();
++      if (errno)
++              goto out;
++
++      errno = eip_rmds_alloc();
++      if (errno)
++              goto out;
++
++      errno = eip_stats_init();
++      if (errno)
++              goto out;
++
++      if (ep_svc_indicator_set(eip_tx->ep_system, EP_SVC_EIP) != EP_SUCCESS) {
++              EIP_ERR_PRINTF("Cannot set the service indicator\n");
++              errno = -EINVAL;
++              goto out;
++      }
++
++      eip_rx_tasklet_locked = 0;
++      tasklet_schedule(&eip_rx->tasklet);
++
++      SET_MODULE_OWNER(eip_tx->net_device);
++
++      if (register_netdev(devnet)) {
++              printk("eip: failed to register netdev\n");
++              goto out;
++      }
++
++      EIP_DBG_PRINTK(EIP_DBG_GEN, "iface %s MAC %02x:%02x:%02x:%02x:%02x:%02x ready\n", 
++              devnet->name, (devnet->dev_addr[0]) & 0xff,
++              (devnet->dev_addr[1]) & 0xff, (devnet->dev_addr[2]) & 0xff, (devnet->dev_addr[3]) & 0xff,
++              (devnet->dev_addr[4]) & 0xff, (devnet->dev_addr[5]) & 0xff);
++
++      return 0;
++      out:
++      unregister_netdev(devnet);
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 25)
++      kfree(devnet);
++#else
++      free_netdev(devnet);
++#endif
++
++      return errno;
++}
++void eip_exit(void)
++{
++      int i;
++
++      eip_rx_dropping = 1;                /* means that new messages wont be sent to tcp stack */
++      eip_rx_tasklet_locked = 1;
++
++      netif_stop_queue(eip_tx->net_device);
++
++      if (ep_svc_indicator_clear(eip_tx->ep_system, EP_SVC_EIP) != EP_SUCCESS) {
++              EIP_ERR_PRINTF("Cannot unset the service indicator\n");
++      }
++
++      schedule_timeout(10);
++      
++      del_timer_sync (&eip_rx_tasklet_timer);
++
++      tasklet_disable(&eip_rx->tasklet);
++      tasklet_disable(&eip_tx->tasklet);
++
++      tasklet_kill(&eip_tx->tasklet);
++      tasklet_kill(&eip_rx->tasklet);
++
++        eip_rmds_free();
++        eip_tmds_free();
++
++      /* that things freed */
++      for (i = 0 ; i < EIP_SVC_NR ; i++) {
++              if ( EIP_STAT_ALLOC_GET(&eip_rx->head[i].stats) != 0 )
++                      EIP_ERR_PRINTF("%d RMDs not FREED on SVC[%d]\n", EIP_STAT_ALLOC_GET(&eip_rx->head[i].stats), i);
++      }
++      for (i = 0 ; i < 3 ; i++) {
++              if ( EIP_STAT_ALLOC_GET(&eip_tx->head[i].stats) != 0 )
++                      EIP_ERR_PRINTF("%d TMDs not freed on TX HEAD[%d]\n", EIP_STAT_ALLOC_GET(&eip_tx->head[i].stats), i);
++              
++      }
++      unregister_netdev(eip_tx->net_device);
++      kfree(eip_tx->net_device);
++      
++      eip_stats_cleanup();
++}
++
++module_init(eip_init);
++module_exit(eip_exit);
++
++MODULE_PARM(eipdebug, "i");
++MODULE_PARM_DESC(eipdebug, "Set debug flags");
++
++MODULE_PARM(rx_envelope_nr, "i");
++MODULE_PARM_DESC(rx_enveloppe_nr, "Number of allocated enveloppe on the rx side");
++
++MODULE_PARM(tx_copybreak_max, "i");
++MODULE_PARM_DESC(tx_copybreak_max, "Maximum size of the tx copybreak limit (default 512)");
++
++MODULE_PARM(tmd_max, "i");
++MODULE_PARM(rmd_max, "i");
++MODULE_PARM_DESC(tmd_max, "Maximun number of transmit buffers (default 64)");
++MODULE_PARM_DESC(rmd_max, "Maximun number of receive buffers (default 64)");
++
++MODULE_PARM(tx_railmask, "i");
++MODULE_PARM_DESC(tx_railmask, "Mask of which rails transmits can be queued on");
++
++MODULE_AUTHOR("Quadrics Ltd.");
++MODULE_DESCRIPTION("Elan IP driver");
++MODULE_LICENSE("GPL");
++#endif        /* MODULE */
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/eip/eip_linux.h
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/eip/eip_linux.h 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/eip/eip_linux.h      2005-05-11 12:10:12.373942456 -0400
+@@ -0,0 +1,399 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "$Id: eip_linux.h,v 1.46.2.1 2004/10/01 10:49:38 mike Exp $"
++
++#ifndef __EIP_LINUX_H
++#define __EIP_LINUX_H
++
++#define EIP_WATERMARK                 (0xfab1e)
++
++#define EIP_PAGES(s)                  (((s - 1) >> PAGE_SHIFT) + 1)
++#define EIP_DVMA_PAGES(s)             ((s < PAGE_SIZE) ? EIP_PAGES(s) + 1 : EIP_PAGES(s))
++
++#define EIP_SVC_SMALLEST_LEN          (1 << 9)        /* 512 */
++#define EIP_SVC_BIGGEST_LEN           (1 << 16)       /* 64k */
++
++#define EIP_SVC_SMALLEST              (0)
++#define EIP_SVC_BIGGEST                       (7)
++
++#define EIP_SVC_NR                    (8)
++#define EIP_SVC_EP(s)                 (s + EP_MSG_SVC_EIP512)
++
++#define EIP_STAT_ALLOC_SHIFT          (8)
++#define EIP_STAT_ALLOC_GET(atomicp)   ((int) atomic_read(atomicp) >> EIP_STAT_ALLOC_SHIFT)
++#define EIP_STAT_ALLOC_ADD(atomicp, v)        (atomic_add((v << EIP_STAT_ALLOC_SHIFT), atomicp))
++#define EIP_STAT_ALLOC_SUB(atomicp, v)        (atomic_sub((v << EIP_STAT_ALLOC_SHIFT), atomicp))
++
++#define EIP_STAT_QUEUED_MASK          (0xff)
++#define EIP_STAT_QUEUED_GET(atomicp)  ((int) atomic_read(atomicp) & EIP_STAT_QUEUED_MASK)
++
++#define EIP_RMD_NR                    (8)
++#define EIP_RMD_MIN_NR                        (8)
++#define EIP_RMD_MAX_NR                        (64)    /* should be < than (1 << EIP_STAT_ALLOC_SHIFT) */
++
++#define EIP_RMD_ALLOC_STEP            (8)
++#define EIP_RMD_ALLOC_THRESH          (16)
++
++#define EIP_RMD_ALLOC                 (1)
++#define EIP_RMD_REPLACE                       (0)
++
++#define EIP_TMD_NR                    (64)
++#define EIP_TMD_MIN_NR                        (16)
++#define EIP_TMD_MAX_NR                        (64)    /* should be < than (1 << EIP_STAT_ALLOC_SHIFT) */
++
++#define EIP_TMD_TYPE_NR                       (3)
++#define EIP_TMD_COPYBREAK             (0x0)
++#define EIP_TMD_STD                   (0x1)
++#define EIP_TMD_AGGREG                        (0x2)
++
++#define EIP_TX_COPYBREAK              (512)
++#define EIP_TX_COPYBREAK_MAX          (1024)
++
++#define EIP_IPFRAG_TO                 (50)    /* time out before a frag is sent in msec */
++#define EIP_IPFRAG_COPYBREAK          (EIP_SVC_BIGGEST_LEN - sizeof(EIP_IPFRAG) - EIP_HEADER_PAD)
++
++#define EIP_RX_ENVELOPE_NR            ((EIP_RMD_MAX_NR*EIP_SVC_NR)/2)
++#define EIP_RX_GRANULARITY            (1)
++
++#define EIP_IP_ALIGN(X)                       (((X) + (15)) & ~(15))
++#define EIP_EXTRA                     roundup (sizeof(EIP_RMD), 256)
++#define EIP_RCV_DMA_LEN(s)                    (s - EIP_EXTRA - EIP_HEADER_PAD)
++#define EIP_MTU_MAX                   (EIP_RCV_DMA_LEN(EIP_SVC_BIGGEST_LEN) - (ETH_HLEN))
++
++#define SIZE_TO_SVC(s, svc)                                                                   \
++      do {                                                                                    \
++                                      if (s <= EIP_RCV_DMA_LEN((1 << 9)))  {svc = 0;break;}   \
++                                      if (s <= EIP_RCV_DMA_LEN((1 << 10))) {svc = 1;break;}   \
++                                      if (s <= EIP_RCV_DMA_LEN((1 << 11))) {svc = 2;break;}   \
++                                      if (s <= EIP_RCV_DMA_LEN((1 << 12))) {svc = 3;break;}   \
++                                      if (s <= EIP_RCV_DMA_LEN((1 << 13))) {svc = 4;break;}   \
++                                      if (s <= EIP_RCV_DMA_LEN((1 << 14))) {svc = 5;break;}   \
++                                      if (s <= EIP_RCV_DMA_LEN((1 << 15))) {svc = 6;break;}   \
++                                      if (s <= EIP_RCV_DMA_LEN((1 << 16))) {svc = 7;break;}   \
++                                      svc = -666;                                             \
++                                      EIP_ASSERT(1 == 0);                                     \
++      } while (0)
++
++extern int eipdebug;
++#define EIP_ASSERT_ON 
++/* #define NO_DEBUG */
++
++
++/* ######################## */
++#ifdef NO_DEBUG
++#define __EIP_DBG_PRINTF(fmt, args...)
++#define EIP_DBG_PRINTF(flag, fmt, args...)
++#else
++
++#define EIP_DBG_RMD           0x1
++#define EIP_DBG_TMD           0x2
++#define EIP_DBG_RMD_HEAD      0x4
++#define EIP_DBG_TMD_HEAD      0x8
++#define EIP_DBG_EIPH          0x10
++#define EIP_DBG_IPH           0x20
++#define EIP_DBG_RMD_EP_DVMA   0x40
++#define EIP_DBG_TMD_EP_DVMA   0x80
++#define EIP_DBG_EP_DVMA               (EIP_DBG_RMD_EP_DVMA|EIP_DBG_TMD_EP_DVMA)
++#define EIP_DBG_MEMALLOC      0x100
++#define EIP_DBG_MEMFREE               0x200
++#define EIP_DBG_RMD_QUEUE     0x400
++#define EIP_DBG_TMD_QUEUE     0x800
++#define EIP_DBG_GEN           0x1000
++#define EIP_DBG_DEBUG         0x2000
++      
++#define __EIP_DBG_PRINTF(fmt, args...)        (qsnet_debugf (QSNET_DEBUG_BUFFER, " CPU #%d %s: " fmt, smp_processor_id(), __func__, ## args))
++#define EIP_DBG_PRINTF(flag, fmt, args...) (unlikely(eipdebug & flag) ? __EIP_DBG_PRINTF(fmt, ## args):(void)0)
++
++#define __EIP_DBG_PRINTK(fmt, args...)        (qsnet_debugf (QSNET_DEBUG_BUF_CON, " CPU #%d %s: " fmt, smp_processor_id(), __func__, ## args))
++#define EIP_DBG_PRINTK(flag, fmt, args...) (unlikely(eipdebug & flag) ? __EIP_DBG_PRINTF(fmt, ## args):(void)0)
++          
++#define EIP_ERR_PRINTF(fmt, args...)  __EIP_DBG_PRINTK("!!! ERROR !!! - " fmt, ## args)
++
++      
++#define EIP_DBG2(flag, fn, fn_arg, fmt, args...)                                                              \
++    if (unlikely(eipdebug & flag)) {                                                                          \
++          qsnet_debugf (QSNET_DEBUG_BUFFER, "+CPU #%d %s: " fmt, smp_processor_id(), __func__, ##args);       \
++            (void)(fn)(fn_arg);                                                                               \
++          qsnet_debugf (QSNET_DEBUG_BUFFER, "-CPU #%d %s: " fmt, smp_processor_id(), __func__, ##args);       \
++    }
++
++
++#define EIP_DBG(flag, fn, args...)                                                            \
++    if (unlikely(eipdebug & flag)) {                                                          \
++          qsnet_debugf (QSNET_DEBUG_BUFFER, "+CPU #%d %s\n", smp_processor_id(), __func__);   \
++            (void)(fn)(args);                                                                 \
++          qsnet_debugf (QSNET_DEBUG_BUFFER, "-CPU #%d %s :\n", smp_processor_id(), __func__); \
++    }
++#endif /* NO_DEBUG */
++
++
++#ifdef EIP_ASSERT_ON
++
++#define __EIP_ASSERT_PRINT(exp)                               \
++              eipdebug = 0xffff;                              \
++              EIP_ERR_PRINTF("ASSERT : %s, %s::%d\n",         \
++                     #exp, __BASE_FILE__, __LINE__);          
++
++#define EIP_ASSERT(exp)                                                       \
++              if (!(exp)) {                                           \
++                      __EIP_ASSERT_PRINT(exp);                        \
++                      netif_stop_queue(eip_tx->net_device);           \
++              }
++
++#define EIP_ASSERT2(exp, f, arg)                                      \
++      do {                                                            \
++              if (!(exp)) {                                           \
++                      __EIP_ASSERT_PRINT(exp);                        \
++                      f(arg);                                         \
++              }                                                       \
++      } while (0)
++
++#define EIP_ASSERT_BUG(exp)                                           \
++      do {                                                            \
++              if (!(exp)) {                                           \
++                      __EIP_ASSERT_PRINT(exp);                        \
++                      BUG();                                          \
++              }                                                       \
++      } while (0)
++
++#define EIP_ASSERT_GOTO(exp, label, f, arg)                           \
++      do {                                                            \
++              if (!(exp)) {                                           \
++                      __EIP_ASSERT_PRINT(exp);                        \
++                      f(arg);                                         \
++                      goto label;                                     \
++              }                                                       \
++      } while (0)
++
++#define EIP_ASSERT_RET(exp, ret)                                      \
++      do {                                                            \
++              if (!(exp)) {                                           \
++                      __EIP_ASSERT_PRINT(exp);                        \
++                      return ret;                                     \
++              }                                                       \
++      } while (0)
++
++#define EIP_ASSERT_RETURN(exp, f, arg)                                        \
++      do {                                                            \
++              if (!(exp)) {                                           \
++                      __EIP_ASSERT_PRINT(exp);                        \
++                      f(arg);                                         \
++                      return;                                         \
++              }                                                       \
++      } while (0)
++
++#define EIP_ASSERT_RETNULL(exp, f, arg)                                       \
++      do {                                                            \
++              if (!(exp)) {                                           \
++                      __EIP_ASSERT_PRINT(exp);                        \
++                      f(arg);                                         \
++                      return NULL;                                    \
++              }                                                       \
++      } while (0)
++
++#else
++
++#define EIP_ASSERT(exp)               do {} while(0)
++#define EIP_ASSERT_OUT(exp)           do {} while(0)
++#define EIP_ASSERT_RETURN(exp)                do {} while(0)
++#define EIP_ASSERT_RETNULL(exp)               do {} while(0)
++#define EIP_ASSERT_BUG(exp)           do {} while(0)
++
++#endif /* EIP_ASSERT */
++
++
++
++typedef struct {
++      u_short ip_bcast;
++      u_short ip_inst;
++      u_short ip_addr;
++} EIP_ADDRESS;
++
++typedef struct {
++      EIP_ADDRESS h_dhost;
++      EIP_ADDRESS h_shost;
++      u_short h_sap;
++} EIP_HEADER;
++#define EIP_HEADER_PAD                        (2)
++
++typedef struct eip_proc_fs {
++      const char *name;
++      struct proc_dir_entry **parent;
++      read_proc_t *read;
++      write_proc_t *write;
++      unsigned char allocated;
++      struct proc_dir_entry *entry;
++} EIP_PROC_FS;
++
++#define EIP_PROC_ROOT_DIR             "eip"
++
++#define EIP_PROC_DEBUG_DIR            "debug"
++#define EIP_PROC_DEBUG_RX_FLUSH               "rx_flush"
++#define EIP_PROC_DEBUG_TX_FLUSH               "tx_flush"
++
++#define EIP_PROC_AGGREG_DIR           "aggregation"
++#define EIP_PROC_AGGREG_ONOFF         "enable"
++#define EIP_PROC_AGGREG_TO            "timeout"
++#define EIP_PROC_AGGREG_COPYBREAK     "copybreak"
++
++#define EIP_PROC_TX_COPYBREAK         "tx_copybreak"
++#define EIP_PROC_STATS                        "stats"
++#define EIP_PROC_RX_GRAN              "rx_granularity"
++#define EIP_PROC_TX_RAILMASK          "tx_railmask"
++#define EIP_PROC_TMD_INUSE            "tmd_inuse"
++#define EIP_PROC_EIPDEBUG             "eipdebug"
++#define EIP_PROC_CHECKSUM               "checksum"
++
++/* RX */
++/* dma_len is used to keep the len of a received packet */
++/* nmd.nmd_len is the max dma that can be received      */
++/*                                                      */
++struct eip_rmd {
++      struct sk_buff *skb;
++
++      EP_NMD nmd;
++      u16 dvma_idx;
++
++      EP_RXD *rxd;
++      struct eip_rmd_head *head;
++      union {
++              struct list_head link;                          /* when on "busy" list */
++              struct eip_rmd  *next;                          /* all other lists */
++      } chain;
++};
++typedef struct eip_rmd EIP_RMD;
++struct eip_rmd_head {
++      EP_NMH *handle;
++
++      EP_RCVR *rcvr;
++      EIP_RMD *busy_list;
++
++      /* stats */
++      atomic_t stats;
++      unsigned long dma;
++};
++
++typedef struct eip_rmd_head EIP_RMD_HEAD;
++typedef struct eip_rx {
++      struct eip_rmd_head head[EIP_SVC_NR];
++
++      EIP_RMD *irq_list;
++      short    irq_list_nr;   
++
++      /* stats */
++      unsigned long packets;
++      unsigned long bytes;
++      unsigned long errors;
++      unsigned long dropped;
++      unsigned long reschedule;
++
++      spinlock_t lock;
++      struct tasklet_struct tasklet;
++      unsigned char rmd_max_nr;
++      unsigned char sysctl_granularity;
++      struct net_device *net_device;
++} EIP_RX;
++
++/* TX */
++/* dma_len_max is the maximum len for a given DMA                      */
++/* where mnd.nmd_len is the len of the packet to send ~> than skb->len */
++typedef struct eip_ipfrag_handle {
++      /* common with tmd */
++      unsigned long dma_base;
++      int dma_len;
++      EP_NMD nmd;
++      u16 dvma_idx;
++
++      struct sk_buff *skb;
++      struct eip_tmd_head *head;
++      union {
++              struct list_head link;                          /* when on "busy" list */
++              struct eip_tmd  *next;                          /* all other lists */
++      } chain;
++
++      /* private */
++      struct list_head list;
++      struct timeval timestamp;
++      unsigned int frag_nr;
++      int datagram_len; /* Ip data */
++      int dma_correction;
++      EP_PAYLOAD payload;
++} EIP_IPFRAG;
++
++struct eip_tmd {
++      unsigned long dma_base;
++      int dma_len;
++      EP_NMD nmd;
++      u16 dvma_idx;
++
++      struct sk_buff *skb;
++      struct eip_tmd_head *head;
++      union {
++              struct list_head link;                          /* when on "busy" list */
++              struct eip_tmd  *next;                          /* all other lists */
++      } chain;
++};
++
++struct eip_tmd_head {
++      EP_NMH *handle;
++
++      struct eip_tmd *tmd;
++      atomic_t stats;
++};
++
++typedef struct eip_tmd EIP_TMD;
++typedef struct eip_tmd_head EIP_TMD_HEAD;
++
++/* #define EIP_MORE_STATS */
++
++typedef struct eip_tx {
++      struct net_device *net_device;
++      EP_XMTR *xmtr;
++      EP_SYS *ep_system;
++
++      struct eip_tmd_head head[EIP_TMD_TYPE_NR];
++      struct list_head inuse;
++      atomic_t destructor;
++
++      /* stats */
++      unsigned long packets;
++      unsigned long bytes;
++      unsigned long errors;
++      unsigned long dropped;
++      unsigned long dma[EIP_SVC_NR];
++      
++#ifdef EIP_MORE_STATS
++      unsigned long sent_copybreak;
++      unsigned long sent_std;
++      unsigned long sent_aggreg;
++#endif
++
++      unsigned char tmd_max_nr;
++
++      unsigned short sysctl_copybreak;
++      unsigned short sysctl_ipfrag_to;
++      unsigned short sysctl_ipfrag_copybreak;
++      unsigned short sysctl_aggregation;
++
++      unsigned short ipfrag_count;
++      struct list_head ipfrag;
++      spinlock_t ipfraglock;
++
++      spinlock_t lock;
++      struct tasklet_struct tasklet;
++} EIP_TX;
++
++/* =============================================== */
++    /* unsigned long   multicast; */
++#endif                                /* __EIP_LINUX_H */
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/eip/eip_stats.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/eip/eip_stats.c 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/eip/eip_stats.c      2005-05-11 12:10:12.374942304 -0400
+@@ -0,0 +1,375 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++/*
++ * $Id: eip_stats.c,v 1.34.2.2 2005/03/20 12:01:22 david Exp $
++ * $Source: /cvs/master/quadrics/eipmod/eip_stats.c,v $
++ */
++
++#include <qsnet/kernel.h>
++#include <linux/module.h>
++
++#include <elan/epcomms.h>
++
++#include <linux/netdevice.h>
++
++#include <linux/kernel.h>
++#include <linux/proc_fs.h>
++
++#include <asm/atomic.h>
++
++#include <qsnet/procfs_linux.h>
++
++#include "eip_linux.h"
++#include "eip_stats.h"
++
++extern EIP_RX *eip_rx;
++extern EIP_TX *eip_tx;
++extern int tx_copybreak_max;
++extern EP_RAILMASK tx_railmask;
++extern int  eip_checksum_state;
++extern void eip_stop_queue(void);
++extern void eip_start_queue(void);
++
++static int eip_stats_read(char *buf, char **start, off_t off, int count, int *eof, void *data)
++{
++      int i, outlen = 0;
++
++      *buf = '\0';
++      strcat(buf, "\n");
++      strcat(buf, "--------------------------------------------+------------+-----------------+\n");
++      strcat(buf, "    SKB/DMA    |               | Rx         | Tx         |  TMD TYPE       |\n");
++      strcat(buf, "--------------------------------------------+------------|-----------------+\n");
++
++      i = 0;
++      sprintf(buf + strlen(buf), " [%5d/%5d] | [%3.3d/%3.3d/%3.3d] | %10ld | %10ld | #1[%3.3d/%3.3d/%3.3d] |\n",
++              EIP_SVC_SMALLEST_LEN << i, (int) EIP_RCV_DMA_LEN((EIP_SVC_SMALLEST_LEN << i)),
++              EIP_STAT_QUEUED_GET(&eip_rx->head[i].stats), EIP_STAT_ALLOC_GET(&eip_rx->head[i].stats),
++              eip_rx->rmd_max_nr, eip_rx->head[i].dma, eip_tx->dma[i],
++              EIP_STAT_QUEUED_GET(&eip_tx->head[i].stats), EIP_STAT_ALLOC_GET(&eip_tx->head[i].stats),
++               eip_tx->tmd_max_nr);
++
++      i++;
++      sprintf(buf + strlen(buf), " [%5d/%5d] | [%3.3d/%3.3d/%3.3d] | %10ld | %10ld | #2[%3.3d/%3.3d/%3.3d] |\n",
++              EIP_SVC_SMALLEST_LEN << i, (int) EIP_RCV_DMA_LEN((EIP_SVC_SMALLEST_LEN << i)),
++              EIP_STAT_QUEUED_GET(&eip_rx->head[i].stats), EIP_STAT_ALLOC_GET(&eip_rx->head[i].stats),
++              eip_rx->rmd_max_nr, eip_rx->head[i].dma, eip_tx->dma[i],
++              EIP_STAT_QUEUED_GET(&eip_tx->head[i].stats), EIP_STAT_ALLOC_GET(&eip_tx->head[i].stats),
++              eip_tx->tmd_max_nr);
++
++      i++;
++      sprintf(buf + strlen(buf), " [%5d/%5d] | [%3.3d/%3.3d/%3.3d] | %10ld | %10ld | #3[%3.3d/%3.3d/%3.3d] |\n",
++              EIP_SVC_SMALLEST_LEN << i, (int) EIP_RCV_DMA_LEN((EIP_SVC_SMALLEST_LEN << i)),
++              EIP_STAT_QUEUED_GET(&eip_rx->head[i].stats), EIP_STAT_ALLOC_GET(&eip_rx->head[i].stats),
++              eip_rx->rmd_max_nr, eip_rx->head[i].dma, eip_tx->dma[i],
++              EIP_STAT_QUEUED_GET(&eip_tx->head[i].stats), EIP_STAT_ALLOC_GET(&eip_tx->head[i].stats),
++              eip_tx->tmd_max_nr);
++
++      i++;
++      sprintf(buf + strlen(buf), " [%5d/%5d] | [%3.3d/%3.3d/%3.3d] | %10ld | %10ld +-----------------+\n",
++              EIP_SVC_SMALLEST_LEN << i, (int) EIP_RCV_DMA_LEN((EIP_SVC_SMALLEST_LEN << i)),
++              EIP_STAT_QUEUED_GET(&eip_rx->head[i].stats), EIP_STAT_ALLOC_GET(&eip_rx->head[i].stats),
++              eip_rx->rmd_max_nr, eip_rx->head[i].dma, eip_tx->dma[i]);
++
++      i++;
++      sprintf(buf + strlen(buf), " [%5d/%5d] | [%3.3d/%3.3d/%3.3d] | %10ld | %10ld |\n",
++              EIP_SVC_SMALLEST_LEN << i, (int) EIP_RCV_DMA_LEN((EIP_SVC_SMALLEST_LEN << i)),
++              EIP_STAT_QUEUED_GET(&eip_rx->head[i].stats), EIP_STAT_ALLOC_GET(&eip_rx->head[i].stats),
++              eip_rx->rmd_max_nr, eip_rx->head[i].dma, eip_tx->dma[i]);
++
++      i++;
++      sprintf(buf + strlen(buf), " [%5d/%5d] | [%3.3d/%3.3d/%3.3d] | %10ld | %10ld |\n",
++              EIP_SVC_SMALLEST_LEN << i, (int) EIP_RCV_DMA_LEN((EIP_SVC_SMALLEST_LEN << i)),
++              EIP_STAT_QUEUED_GET(&eip_rx->head[i].stats), EIP_STAT_ALLOC_GET(&eip_rx->head[i].stats),
++              eip_rx->rmd_max_nr, eip_rx->head[i].dma, eip_tx->dma[i]);
++
++      i++;
++      sprintf(buf + strlen(buf), " [%5d/%5d] | [%3.3d/%3.3d/%3.3d] | %10ld | %10ld |\n",
++              EIP_SVC_SMALLEST_LEN << i, (int) EIP_RCV_DMA_LEN((EIP_SVC_SMALLEST_LEN << i)),
++              EIP_STAT_QUEUED_GET(&eip_rx->head[i].stats), EIP_STAT_ALLOC_GET(&eip_rx->head[i].stats),
++              eip_rx->rmd_max_nr, eip_rx->head[i].dma, eip_tx->dma[i]);
++
++      i++;
++      sprintf(buf + strlen(buf), " [%5d/%5d] | [%3.3d/%3.3d/%3.3d] | %10ld | %10ld |\n",
++              EIP_SVC_SMALLEST_LEN << i, (int) EIP_RCV_DMA_LEN((EIP_SVC_SMALLEST_LEN << i)),
++              EIP_STAT_QUEUED_GET(&eip_rx->head[i].stats), EIP_STAT_ALLOC_GET(&eip_rx->head[i].stats),
++              eip_rx->rmd_max_nr, eip_rx->head[i].dma, eip_tx->dma[i]);
++
++      strcat(buf, "--------------------------------------------+------------+\n");
++      sprintf(buf + strlen(buf), " RMD IRQ %4.4d                    %10lu | %10lu |\n",
++              eip_rx->irq_list_nr, 
++              eip_rx->packets, eip_tx->packets);
++      strcat(buf, "--------------------------------------------+------------+\n");
++
++#ifdef EIP_MORE_STATS
++      strcat(buf, "\n");
++      sprintf(buf + strlen(buf), " Copybreak %10ld Std %10ld Aggreg %10ld\n",
++                      eip_tx->sent_copybreak, eip_tx->sent_std, eip_tx->sent_aggreg);
++#endif
++
++
++      strcat(buf, "\n");
++      sprintf(buf + strlen(buf), "Rx bytes: %lu (%lu Mb) errors: %lu dropped: %lu reschedule: %lu\n",
++              eip_rx->bytes, eip_rx->bytes / (1024 * 1024), eip_rx->errors, eip_rx->dropped, eip_rx->reschedule);
++      sprintf(buf + strlen(buf), "Tx bytes: %lu (%lu Mb) errors: %lu dropped: %lu\n",
++              eip_tx->bytes, eip_tx->bytes / (1024 * 1024), eip_tx->errors, eip_tx->dropped);
++      strcat(buf, "\n");
++
++      outlen = strlen(buf);
++      ASSERT(outlen < PAGE_SIZE);
++      *eof = 1;
++      return outlen;
++}
++
++void eip_stats_dump(void)
++{
++    int eof;
++
++    char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
++
++    if (buf == NULL)
++    {
++      printk("no memory to produce eip_stats\n");
++      return;
++    }
++
++    eip_stats_read(buf, NULL, 0, 0, &eof, NULL);
++
++    printk(buf);
++
++    kfree(buf);
++}
++
++static int eip_stats_write(struct file *file, const char *buf, unsigned long count, void *data)
++{
++      int i;
++      unsigned long flags;
++
++      spin_lock_irqsave(&eip_rx->lock, flags);
++      eip_rx->packets = 0;
++      eip_rx->bytes = 0;
++      eip_rx->errors = 0;
++      eip_rx->dropped = 0;
++      eip_rx->reschedule = 0;
++      for (i = 0; i < EIP_SVC_NR; eip_rx->head[i].dma = 0, i++);
++      spin_unlock_irqrestore(&eip_rx->lock, flags);
++
++      spin_lock_irqsave(&eip_tx->lock, flags);
++      eip_tx->packets = 0;
++      eip_tx->bytes = 0;
++      eip_tx->errors = 0;
++      eip_tx->dropped = 0;
++#ifdef EIP_MORE_STATS
++      eip_tx->sent_copybreak = 0;
++      eip_tx->sent_std = 0;
++      eip_tx->sent_aggreg = 0;
++#endif
++      for (i = 0; i < EIP_SVC_NR; eip_tx->dma[i] = 0, i++);
++      spin_unlock_irqrestore(&eip_tx->lock, flags);
++
++      return count;
++}
++
++#define               eip_stats_var_write(name)                                                                       \
++static int eip_stats_##name##_write(struct file *file, const char *buf, unsigned long count, void *data)      \
++{                                                                                                             \
++      char * b = (char *) buf;                                                                                \
++      *(b + count) = '\0';                                                                                    \
++      eip_##name##_set((int) simple_strtoul(b, NULL, 10));                                                    \
++      return count;                                                                                           \
++}
++
++#define       eip_stats_var_read(name, var)                                                                   \
++static int eip_stats_##name##_read(char *buf, char **start, off_t off, int count, int *eof, void *data)               \
++{                                                                                                             \
++      sprintf(buf, "%d\n", var);                                                                              \
++      *eof = 1;                                                                                               \
++      return strlen(buf);                                                                                     \
++}
++
++
++#define               eip_stats_var_set(name, min, max, default, var)                                                                 \
++void eip_##name##_set(int i)                                                                                                  \
++{                                                                                                                             \
++      if ( (i >= min) && (i <= max)) {                                                                                        \
++              EIP_DBG_PRINTK(EIP_DBG_GEN, "Setting " #name " to %d\n", i);                                                    \
++              var =(unsigned short) i;                                                                                        \
++      }                                                                                                                       \
++      else {                                                                                                                  \
++              EIP_ERR_PRINTF("parameter error : %d <= " #name "(%d) <= %d using default %d\n", min, i, (int) max, (int) default);     \
++      }                                                                                                                       \
++}
++
++eip_stats_var_set(tx_copybreak, 0, tx_copybreak_max, EIP_TX_COPYBREAK, eip_tx->sysctl_copybreak);
++eip_stats_var_set(rx_granularity, 1, EIP_RMD_MIN_NR, EIP_RX_GRANULARITY, eip_rx->sysctl_granularity);
++eip_stats_var_set(tx_railmask, 0, EP_RAILMASK_ALL, EP_RAILMASK_ALL, tx_railmask);
++eip_stats_var_set(ipfrag_to, 0, (1 << 16), EIP_IPFRAG_TO, eip_tx->sysctl_ipfrag_to);
++eip_stats_var_set(aggregation, 0, 1, 1, eip_tx->sysctl_aggregation);
++eip_stats_var_set(ipfrag_copybreak, 0, EIP_IPFRAG_COPYBREAK, EIP_IPFRAG_COPYBREAK, eip_tx->sysctl_ipfrag_copybreak);
++/* eip_stats_var_set(eipdebug, 0, , 0, eipdebug); */
++
++eip_stats_var_read(aggregation, eip_tx->sysctl_aggregation);
++eip_stats_var_read(ipfrag_count, eip_tx->ipfrag_count);
++eip_stats_var_read(ipfrag_to, eip_tx->sysctl_ipfrag_to);
++eip_stats_var_read(ipfrag_copybreak, eip_tx->sysctl_ipfrag_copybreak);
++eip_stats_var_read(tx_copybreak, eip_tx->sysctl_copybreak);
++eip_stats_var_read(rx_granularity, eip_rx->sysctl_granularity);
++eip_stats_var_read(tx_railmask, tx_railmask);
++
++eip_stats_var_write(aggregation);
++eip_stats_var_write(ipfrag_to);
++eip_stats_var_write(ipfrag_copybreak);
++eip_stats_var_write(tx_copybreak);
++eip_stats_var_write(rx_granularity);
++eip_stats_var_write(tx_railmask);
++
++
++static int eip_checksum_write(struct file *file, const char *buf, unsigned long count, void *data)
++{
++      char * b = (char *) buf;
++      int    value;
++
++      *(b + count) = '\0';
++
++      value = (int) simple_strtoul(b, NULL, 10);
++      if  ((value >= CHECKSUM_NONE) && (value <= CHECKSUM_UNNECESSARY)) 
++              eip_checksum_state = value;
++      else 
++              EIP_ERR_PRINTF("%d <= checksum(%d) <= %d using old value %d\n", CHECKSUM_NONE, value, CHECKSUM_UNNECESSARY, eip_checksum_state);
++
++      return count;
++}
++
++static int eip_checksum_read(char *buf, char **start, off_t off, int count, int *eof, void *data)
++{
++      switch ( eip_checksum_state ) 
++      {
++      case 0  : sprintf(buf, "0 CHECKSUM_NONE\n");                      break;
++      case 1  : sprintf(buf, "1 CHECKSUM_HW\n");                        break;
++      case 2  : sprintf(buf, "2 CHECKSUM_UNNECESSARY\n");               break;
++      default : sprintf(buf, "%d INVALID VALUE\n", eip_checksum_state); break;
++      }
++      *eof = 1;
++      return strlen(buf);
++}
++
++static int eip_stats_eipdebug_read(char *buf, char **start, off_t off, int count, int *eof, void *data)
++{
++      *buf = '\0';
++      sprintf(buf + strlen(buf), "0x%x\n", eipdebug);
++      *eof = 1;
++      return strlen(buf);
++}
++static int eip_stats_eipdebug_write(struct file *file, const char *buf, unsigned long count, void *data)
++{
++      char * p = (char *) buf;
++      *(p + count - 1) = '\0';
++      eipdebug = simple_strtoul(p, NULL, 0);
++      __EIP_DBG_PRINTK("Setting eipdebug to 0x%x\n", eipdebug);
++      return count;
++}
++
++static int eip_stats_tmd_inuse_read(char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++      struct list_head *lp;
++      unsigned long flags;
++      unsigned int len = 0;
++
++      spin_lock_irqsave(&eip_tx->lock, flags);
++      list_for_each (lp, &eip_tx->inuse) {
++              EIP_TMD *tmd = list_entry (lp, EIP_TMD, chain.link);
++              EIP_HEADER *eiph = (EIP_HEADER *) tmd->dma_base;
++              
++                len += sprintf(page+len, "tmd=%p id=%d len=%d\n",
++                             tmd, eiph ? ntohs(eiph->h_dhost.ip_addr) : -1,
++                             tmd->dma_len);
++
++                if (len + 40 >= count)
++                        break;
++        }
++        spin_unlock_irqrestore(&eip_tx->lock, flags);
++
++      return qsnet_proc_calc_metrics (page, start, off, count, eof, len);
++}
++
++static int eip_stats_debug_rx_flush(struct file *file, const char *buf, unsigned long count, void *data)
++{
++      EIP_DBG_PRINTF(EIP_DBG_GEN, "Flushing rx ...\n");
++      tasklet_schedule(&eip_rx->tasklet);
++      return count;
++}
++static int eip_stats_debug_tx_flush(struct file *file, const char *buf, unsigned long count, void *data)
++{
++      EIP_DBG_PRINTF(EIP_DBG_GEN, "Flushing tx ... %d tmds reclaimed\n", ep_enable_txcallbacks(eip_tx->xmtr));
++      ep_disable_txcallbacks(eip_tx->xmtr);
++      tasklet_schedule(&eip_tx->tasklet);
++      return count;
++}
++
++#define EIP_PROC_PARENT_NR    (3)
++/* NOTE : the parents should be declared b4 the children */
++static EIP_PROC_FS eip_procs[] = {
++      /* {name, parent, read fn, write fn, allocated, entry}, */
++      {EIP_PROC_ROOT_DIR, &qsnet_procfs_root, NULL, NULL, 0, NULL},
++      {EIP_PROC_DEBUG_DIR, &eip_procs[0].entry, NULL, NULL, 0, NULL},
++      {EIP_PROC_AGGREG_DIR, &eip_procs[0].entry, NULL, NULL, 0, NULL},        /* end of parents */
++      {EIP_PROC_STATS, &eip_procs[0].entry, eip_stats_read, eip_stats_write, 0, NULL},
++      {EIP_PROC_TX_COPYBREAK, &eip_procs[0].entry, eip_stats_tx_copybreak_read, eip_stats_tx_copybreak_write, 0, NULL},
++      {EIP_PROC_RX_GRAN, &eip_procs[0].entry, eip_stats_rx_granularity_read, eip_stats_rx_granularity_write, 0, NULL},
++      {EIP_PROC_TX_RAILMASK, &eip_procs[0].entry, eip_stats_tx_railmask_read, eip_stats_tx_railmask_write, 0, NULL},
++      {EIP_PROC_TMD_INUSE, &eip_procs[0].entry, eip_stats_tmd_inuse_read, NULL, 0, NULL},
++      {EIP_PROC_EIPDEBUG, &eip_procs[0].entry, eip_stats_eipdebug_read, eip_stats_eipdebug_write, 0, NULL},
++      {EIP_PROC_CHECKSUM, &eip_procs[0].entry, eip_checksum_read, eip_checksum_write, 0, NULL},
++      {EIP_PROC_DEBUG_RX_FLUSH, &eip_procs[1].entry, NULL, eip_stats_debug_rx_flush, 0, NULL},
++      {EIP_PROC_DEBUG_TX_FLUSH, &eip_procs[1].entry, NULL, eip_stats_debug_tx_flush, 0, NULL},
++      {"ipfrag_count", &eip_procs[2].entry, eip_stats_ipfrag_count_read, NULL, 0, NULL},
++      {EIP_PROC_AGGREG_TO, &eip_procs[2].entry, eip_stats_ipfrag_to_read, eip_stats_ipfrag_to_write, 0, NULL},
++      {EIP_PROC_AGGREG_ONOFF, &eip_procs[2].entry, eip_stats_aggregation_read, eip_stats_aggregation_write, 0, NULL},
++      {EIP_PROC_AGGREG_COPYBREAK, &eip_procs[2].entry, eip_stats_ipfrag_copybreak_read, eip_stats_ipfrag_copybreak_write, 0, NULL},
++      {NULL, NULL, NULL, NULL, 1, NULL},
++};
++
++int eip_stats_init(void)
++{
++      int p;
++
++      for (p = 0; !eip_procs[p].allocated; p++) {
++              if (p < EIP_PROC_PARENT_NR)
++                      eip_procs[p].entry = proc_mkdir(eip_procs[p].name, *eip_procs[p].parent);
++              else
++                      eip_procs[p].entry = create_proc_entry(eip_procs[p].name, 0, *eip_procs[p].parent);
++
++              if (!eip_procs[p].entry) {
++                      EIP_ERR_PRINTF("%s\n", "Cannot allocate proc entry");
++                      eip_stats_cleanup();
++                      return -ENOMEM;
++              }
++
++              eip_procs[p].entry->owner = THIS_MODULE;
++              eip_procs[p].entry->write_proc = eip_procs[p].write;
++              eip_procs[p].entry->read_proc = eip_procs[p].read;
++              eip_procs[p].allocated = 1;
++      }
++      eip_procs[p].allocated = 0;
++      return 0;
++}
++
++void eip_stats_cleanup(void)
++{
++      int p;
++      for (p = (sizeof (eip_procs)/sizeof (eip_procs[0]))-1; p >= 0; p--)
++              if (eip_procs[p].allocated) {
++                      EIP_DBG_PRINTF(EIP_DBG_GEN, "Removing %s from proc\n", eip_procs[p].name);
++                      remove_proc_entry(eip_procs[p].name, *eip_procs[p].parent);
++              }
++}
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/eip/eip_stats.h
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/eip/eip_stats.h 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/eip/eip_stats.h      2005-05-11 12:10:12.374942304 -0400
+@@ -0,0 +1,22 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "$Id: eip_stats.h,v 1.14 2004/05/10 14:47:47 daniel Exp $"
++
++#ifndef __EIP_STATS_H
++#define       __EIP_STATS_H
++
++int eip_stats_init(void);
++void eip_stats_cleanup(void);
++void eip_rx_granularity_set(int);
++void eip_tx_copybreak_set(int);
++void eip_ipfrag_to_set(int);
++void eip_aggregation_set(int);
++void eip_ipfrag_copybreak_set(int);
++void eip_stats_dump(void);
++
++#endif                                /* __EIP_STATS_H */
+Index: linux-2.6.5/drivers/net/qsnet/eip/Makefile
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/eip/Makefile    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/eip/Makefile 2005-05-11 12:10:12.374942304 -0400
+@@ -0,0 +1,15 @@
++#
++# Makefile for Quadrics QsNet
++#
++# Copyright (c) 2002-2004 Quadrics Ltd
++#
++# File: drivers/net/qsnet/eip/Makefile
++#
++
++
++#
++
++obj-$(CONFIG_EIP)     += eip.o
++eip-objs      := eip_linux.o eip_stats.o
++
++EXTRA_CFLAGS          +=  -DDEBUG -DDEBUG_PRINTF -DDEBUG_ASSERT
+Index: linux-2.6.5/drivers/net/qsnet/eip/Makefile.conf
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/eip/Makefile.conf       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/eip/Makefile.conf    2005-05-11 12:10:12.375942152 -0400
+@@ -0,0 +1,10 @@
++# Flags for generating QsNet Linux Kernel Makefiles
++MODNAME               =       eip.o
++MODULENAME    =       eip
++KOBJFILES     =       eip_linux.o eip_stats.o
++EXPORT_KOBJS  =       
++CONFIG_NAME   =       CONFIG_EIP
++SGALFC                =       
++# EXTRALINES START
++
++# EXTRALINES END
+Index: linux-2.6.5/drivers/net/qsnet/eip/quadrics_version.h
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/eip/quadrics_version.h  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/eip/quadrics_version.h       2005-05-11 12:10:12.375942152 -0400
+@@ -0,0 +1 @@
++#define QUADRICS_VERSION "4.31qsnet"
+Index: linux-2.6.5/drivers/net/qsnet/elan/bitmap.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan/bitmap.c   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan/bitmap.c        2005-05-11 12:10:12.375942152 -0400
+@@ -0,0 +1,287 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: bitmap.c,v 1.5 2004/01/20 17:32:17 david Exp $"
++/*      $Source: /cvs/master/quadrics/elanmod/shared/bitmap.c,v $*/
++
++#if defined(__KERNEL__)
++#include <qsnet/kernel.h>
++#endif
++#include <qsnet/config.h>
++#include <elan/bitmap.h>
++
++/*
++ * Return the index of the first available bit in the 
++ * bitmap , or -1 for failure
++ */
++int
++bt_freebit (bitmap_t *bitmap, int nbits)
++{
++    int last = (--nbits) >> BT_ULSHIFT;
++    int maxbit;
++    int       i, j;
++
++    /* look for a word with a bit off */
++    for (i = 0; i <= last; i++)
++      if (bitmap[i] != ~((bitmap_t) 0))
++          break;
++
++    if (i <= last)
++    {
++      /* found an word with a bit off,  now see which bit it is */
++      maxbit = (i == last) ? (nbits & BT_ULMASK) : (BT_NBIPUL-1);
++      for (j = 0; j <= maxbit; j++)
++          if ((bitmap[i] & (1 << j)) == 0)
++              return ((i << BT_ULSHIFT) | j);
++    }
++    return (-1);
++    
++}
++
++/*
++ * bt_lowbit:
++ *     Return the index of the lowest set bit in the
++ *     bitmap, or -1 for failure.
++ */
++int
++bt_lowbit (bitmap_t *bitmap, int nbits)
++{
++    int last = (--nbits) >> BT_ULSHIFT;
++    int maxbit;
++    int i, j;
++    
++    /* look for a word with a bit on */
++    for (i = 0; i <= last; i++)
++      if (bitmap[i] != 0)
++          break;
++    if (i <= last)
++    {
++      /* found a word bit a bit on, now see which bit it is */
++      maxbit = (i == last) ? (nbits & BT_ULMASK) : (BT_NBIPUL-1);
++      for (j = 0; j <= maxbit; j++)
++          if (bitmap[i] & (1 << j))
++              return ((i << BT_ULSHIFT) | j);
++    }
++
++    return (-1);
++}
++
++/*
++ * Return the index of the first available bit in the 
++ * bitmap , or -1 for failure
++ */
++int
++bt_nextbit (bitmap_t *bitmap, int nbits, int last, int isset)
++{
++    int first = ((last+1) + BT_NBIPUL-1) >> BT_ULSHIFT;
++    int end   = (--nbits) >> BT_ULSHIFT;
++    int maxbit;
++    int       i, j;
++
++    /* look for bits before the first whole word */
++    if (((last+1) & BT_ULMASK) != 0)
++    {
++      maxbit = ((first-1) == last) ? (nbits & BT_ULMASK) : (BT_NBIPUL-1);
++      for (j = ((last+1) & BT_ULMASK); j <= maxbit; j++)
++          if ((bitmap[first-1] & (1 << j)) == (isset << j))
++              return (((first-1) << BT_ULSHIFT) | j);
++    }
++
++    /* look for a word with a bit off */
++    for (i = first; i <= end; i++)
++      if (bitmap[i] != (isset ? 0 : ~((bitmap_t) 0)))
++          break;
++
++    if (i <= end)
++    {
++      /* found an word with a bit off,  now see which bit it is */
++      maxbit = (i == end) ? (nbits & BT_ULMASK) : (BT_NBIPUL-1);
++      for (j = 0; j <= maxbit; j++)
++          if ((bitmap[i] & (1 << j)) == (isset << j))
++              return ((i << BT_ULSHIFT) | j);
++    }
++    return (-1);
++}
++
++void
++bt_copy (bitmap_t *a, bitmap_t *b, int nbits)
++{
++    int i;
++
++    for (i = 0; i < (nbits>>BT_ULSHIFT); i++)
++      b[i] = a[i];
++
++    for (i <<= BT_ULSHIFT; i < nbits; i++)
++      if (BT_TEST(a, i))
++          BT_SET(b,i);
++      else
++          BT_CLEAR(b,i);
++}
++
++void
++bt_zero (bitmap_t *bitmap, int nbits)
++{
++    int i;
++
++    for (i = 0; i < (nbits>>BT_ULSHIFT); i++)
++      bitmap[i] = 0;
++
++    for (i <<= BT_ULSHIFT; i < nbits; i++)
++      BT_CLEAR(bitmap,i);
++}
++
++void
++bt_fill (bitmap_t *bitmap, int nbits)
++{
++    int i;
++
++    for (i = 0; i < (nbits>>BT_ULSHIFT); i++)
++      bitmap[i] = ~((bitmap_t) 0);
++
++    for (i <<= BT_ULSHIFT; i < nbits; i++)
++      BT_SET(bitmap,i);
++}
++
++int
++bt_cmp (bitmap_t *a, bitmap_t *b, int nbits)
++{
++    int i;
++
++    for (i = 0; i < (nbits>>BT_ULSHIFT); i++)
++      if (a[i] != b[i])
++          return (1);
++
++    for (i <<= BT_ULSHIFT; i < nbits; i++)
++      if (BT_TEST (a, i) != BT_TEST(b, i))
++          return (1);
++    return (0);
++}
++
++void
++bt_intersect (bitmap_t *a, bitmap_t *b, int nbits)
++{
++    int i;
++    
++    for (i = 0; i < (nbits>>BT_ULSHIFT); i++)
++      a[i] &= b[i];
++
++    for (i <<= BT_ULSHIFT; i < nbits; i++)
++      if (BT_TEST (a, i) && BT_TEST (b, i))
++          BT_SET (a, i);
++      else
++          BT_CLEAR (a, i);
++}
++
++void
++bt_remove (bitmap_t *a, bitmap_t *b, int nbits)
++{
++    int i;
++
++    for (i = 0; i < (nbits>>BT_ULSHIFT); i++)
++      a[i] &= ~b[i];
++
++    for (i <<= BT_ULSHIFT; i < nbits; i++)
++      if (BT_TEST (b, i))
++          BT_CLEAR (a, i);
++}
++
++void
++bt_add (bitmap_t *a, bitmap_t *b, int nbits)
++{
++    int i;
++
++    for (i = 0; i < (nbits>>BT_ULSHIFT); i++)
++      a[i] |= b[i];
++
++    for (i <<= BT_ULSHIFT; i < nbits; i++)
++      if (BT_TEST(b, i))
++          BT_SET (a, i);
++}
++
++/*
++ * bt_spans : partition a spans partition b
++ *    == all bits set in 'b' are set in 'a'
++ */
++int
++bt_spans (bitmap_t *a, bitmap_t *b, int nbits)
++{
++    int i;
++    
++    for (i = 0; i < nbits; i++)
++      if (BT_TEST (b, i) && !BT_TEST (a, i))
++          return (0);
++    return (1);
++}
++
++/*
++ * bt_subset: copy [base,base+nbits-1] from 'a' to 'b'
++ */
++void
++bt_subset (bitmap_t *a, bitmap_t *b, int base, int nbits)
++{
++    int i;
++
++    for (i = 0; i < nbits; i++)
++    {
++      if (BT_TEST (a, base+i))
++          BT_SET(b,i);
++      else
++          BT_CLEAR (b,i);
++    }
++}
++
++void 
++bt_up (bitmap_t *a, bitmap_t *b, bitmap_t *c, int nbits)
++{
++    int i;
++    
++    for (i = 0; i < nbits; i++)
++    {
++      if (!BT_TEST (a, i) && BT_TEST (b, i))
++      {
++          BT_SET (c, i);
++        }
++      else
++      {
++          BT_CLEAR (c, i);
++        }
++    }
++}
++
++void 
++bt_down (bitmap_t *a, bitmap_t *b, bitmap_t *c, int nbits)
++{
++    int i;
++    
++    for (i = 0; i < nbits; i++)
++    {
++      if (BT_TEST (a, i) && !BT_TEST (b, i))
++      {
++          BT_SET (c, i);
++        }
++      else
++      {
++          BT_CLEAR (c, i);
++        }
++    }
++}
++
++int
++bt_nbits (bitmap_t *a, int nbits)
++{
++    int i, c;
++    for (i = 0, c = 0; i < nbits; i++)
++      if (BT_TEST (a, i))
++          c++;
++    return (c);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan/capability.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan/capability.c       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan/capability.c    2005-05-11 12:10:12.376942000 -0400
+@@ -0,0 +1,628 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: capability.c,v 1.13 2004/07/20 10:15:33 david Exp $"
++/*      $Source: /cvs/master/quadrics/elanmod/modsrc/capability.c,v $ */
++
++
++#include <qsnet/kernel.h>
++#include <elan/elanmod.h>
++
++static LIST_HEAD(elan_cap_list); 
++
++typedef struct elan_vp_struct
++{
++      struct list_head list;
++      ELAN_CAPABILITY  vp;
++} ELAN_VP_NODE_STRUCT;
++
++
++typedef struct elan_attached_struct
++{
++      void               *cb_args;
++      ELAN_DESTROY_CB  cb_func;
++} ELAN_ATTACHED_STRUCT;
++
++typedef struct elan_cap_node_struct
++{
++      struct list_head list;
++      ELAN_CAP_STRUCT     node;
++      ELAN_ATTACHED_STRUCT *attached[ELAN_MAX_RAILS];
++      struct list_head vp_list;
++} ELAN_CAP_NODE_STRUCT;
++
++
++ELAN_CAP_NODE_STRUCT *
++find_cap_node(ELAN_CAPABILITY *cap)
++{
++      struct list_head        *tmp;
++      ELAN_CAP_NODE_STRUCT *ptr=NULL;
++
++      list_for_each(tmp, &elan_cap_list) {
++              ptr = list_entry(tmp, ELAN_CAP_NODE_STRUCT , list);
++              /* is it an exact match */
++              if ( ELAN_CAP_TYPE_MATCH(&ptr->node.cap,cap) 
++                   && ELAN_CAP_GEOM_MATCH(&ptr->node.cap,cap)) {
++                      return ptr;
++              }
++      }
++      return ptr;
++};
++
++ELAN_VP_NODE_STRUCT *
++find_vp_node( ELAN_CAP_NODE_STRUCT *cap_node,ELAN_CAPABILITY *map)
++{
++      struct list_head       * tmp;
++      ELAN_VP_NODE_STRUCT * ptr = NULL;
++
++      list_for_each(tmp, &cap_node->vp_list) {
++              ptr = list_entry(tmp, ELAN_VP_NODE_STRUCT , list);
++              /* is it an exact match */
++              if ( ELAN_CAP_TYPE_MATCH(&ptr->vp,map) 
++                   && ELAN_CAP_GEOM_MATCH(&ptr->vp,map)){
++                      return ptr;
++              }
++      }
++      return ptr;
++}
++
++int 
++elan_validate_cap(ELAN_CAPABILITY *cap)
++{
++      char                      space[127];
++
++      ELAN_DEBUG1 (ELAN_DBG_VP,"elan_validate_cap %s\n",elan_capability_string(cap,space));
++
++      /* check versions */
++      if (cap->cap_version != ELAN_CAP_VERSION_NUMBER)
++      {
++              ELAN_DEBUG2 (ELAN_DBG_VP,"elan_validate_cap: (cap->Version != ELAN_CAP_VERSION) %d %d\n", cap->cap_version, ELAN_CAP_VERSION_NUMBER);
++              return (EINVAL);
++      }
++
++      /* check its not HWTEST */
++      if ( cap->cap_type & ELAN_CAP_TYPE_HWTEST )
++      {
++              ELAN_DEBUG0 (ELAN_DBG_VP,"elan_validate_cap: failed type = ELAN_CAP_TYPE_HWTEST \n");   
++              return (EINVAL);
++      }
++      
++      /* check its type */
++      switch (cap->cap_type & ELAN_CAP_TYPE_MASK)
++      {
++      case ELAN_CAP_TYPE_KERNEL :     
++              ELAN_DEBUG0 (ELAN_DBG_VP,"elan_validate_cap: failed type = ELAN_CAP_TYPE_KERNEL \n");   
++              return (EINVAL);
++
++              /* check it has a valid type */
++      case ELAN_CAP_TYPE_BLOCK:
++      case ELAN_CAP_TYPE_CYCLIC:
++              break;
++
++              /* all others are failed as well */
++      default:
++              ELAN_DEBUG1 (ELAN_DBG_VP,"elan_validate_cap: failed unknown type = %x \n", (cap->cap_type & ELAN_CAP_TYPE_MASK));       
++              return (EINVAL);
++      }
++      
++      if ((cap->cap_lowcontext == ELAN_CAP_UNINITIALISED) || (cap->cap_highcontext == ELAN_CAP_UNINITIALISED)
++          || (cap->cap_lownode == ELAN_CAP_UNINITIALISED) || (cap->cap_highnode    == ELAN_CAP_UNINITIALISED))
++      {
++              
++              ELAN_DEBUG4 (ELAN_DBG_VP,"elan_validate_cap: ELAN_CAP_UNINITIALISED   LowNode %d   HighNode %d   LowContext %d   highContext %d\n",
++                           cap->cap_lownode , cap->cap_highnode,
++                           cap->cap_lowcontext , cap->cap_highcontext);
++              return (EINVAL);
++      }       
++
++      if (cap->cap_lowcontext > cap->cap_highcontext)
++      {
++              ELAN_DEBUG2 (ELAN_DBG_VP,"elan_validate_cap: (cap->cap_lowcontext > cap->cap_highcontext) %d %d\n",cap->cap_lowcontext , cap->cap_highcontext);
++              return (EINVAL);
++      }
++      
++      if (cap->cap_lownode > cap->cap_highnode)
++      {
++              ELAN_DEBUG2 (ELAN_DBG_VP,"elan_validate_cap: (cap->cap_lownode > cap->cap_highnode) %d %d\n",cap->cap_lownode, cap->cap_highnode);
++              return (EINVAL);
++      }
++
++      if (cap->cap_mycontext != ELAN_CAP_UNINITIALISED) 
++      {
++              ELAN_DEBUG1 (ELAN_DBG_VP,"elan_validate_cap: failed cap->cap_mycontext is set %d  \n", cap->cap_mycontext);
++              return (EINVAL);
++      }
++
++
++      if ((ELAN_CAP_NUM_NODES(cap) * ELAN_CAP_NUM_CONTEXTS(cap)) > ELAN_MAX_VPS)
++      {
++              ELAN_DEBUG6 (ELAN_DBG_VP,"elan_validate_cap: too many vps  LowNode %d   HighNode %d   LowContext %d   highContext %d,  %d >% d\n",
++                           cap->cap_lownode , cap->cap_highnode,
++                           cap->cap_lowcontext , cap->cap_highcontext,
++                           (ELAN_CAP_NUM_NODES(cap) * ELAN_CAP_NUM_CONTEXTS(cap)),
++                           ELAN_MAX_VPS);
++              
++              return (EINVAL);
++      }
++
++      return (ESUCCESS);
++}
++
++int
++elan_validate_map(ELAN_CAPABILITY *cap, ELAN_CAPABILITY *map)
++{
++      ELAN_CAP_NODE_STRUCT * ptr  = NULL;
++      ELAN_VP_NODE_STRUCT  * vptr = NULL;
++      char space[256];
++
++      kmutex_lock(&elan_mutex);
++
++      ELAN_DEBUG0 (ELAN_DBG_VP,"elan_validate_map \n");
++      ELAN_DEBUG1 (ELAN_DBG_VP,"elan_validate_map cap = %s \n",elan_capability_string(cap,space));
++      ELAN_DEBUG1 (ELAN_DBG_VP,"elan_validate_map map = %s \n",elan_capability_string(map,space));
++
++      /* does cap exist    */
++      ptr = find_cap_node(cap);
++      if ( ptr == NULL ) 
++      {
++              ELAN_DEBUG0 (ELAN_DBG_VP,"elan_validate_map: cap not found \n");
++              kmutex_unlock(&elan_mutex);
++              return EINVAL;
++      }
++      /* is it active */
++      if ( ! ptr->node.active ) 
++      {
++              ELAN_DEBUG0 (ELAN_DBG_VP,"elan_validate_map: cap not active \n");
++              kmutex_unlock(&elan_mutex);
++              return EINVAL;
++      }
++
++      /* are they the same */
++      if ( ELAN_CAP_TYPE_MATCH(cap,map) 
++           && ELAN_CAP_GEOM_MATCH(cap,map)) 
++      {
++              ELAN_DEBUG0 (ELAN_DBG_VP,"elan_validate_map: cap == map  passed\n");
++              kmutex_unlock(&elan_mutex);
++              return ESUCCESS;
++      }
++
++      /* is map in map list */
++      vptr = find_vp_node(ptr, map);
++      if ( vptr == NULL ) 
++      {
++              ELAN_DEBUG0 (ELAN_DBG_VP,"elan_validate_map:  map not found\n");
++              kmutex_unlock(&elan_mutex);
++              return EINVAL;
++      }
++      
++      ELAN_DEBUG0 (ELAN_DBG_VP,"elan_validate_map:  map passed\n");
++      kmutex_unlock(&elan_mutex);
++      return ESUCCESS;
++}
++
++int
++elan_create_cap(ELAN_CAP_OWNER owner, ELAN_CAPABILITY *cap)
++{
++      char                      space[127];
++      struct list_head        * tmp;
++      ELAN_CAP_NODE_STRUCT * ptr = NULL;
++      int                       i, rail;
++
++      kmutex_lock(&elan_mutex);
++
++      ELAN_DEBUG1 (ELAN_DBG_VP,"elan_create_cap %s\n",elan_capability_string(cap,space));     
++
++      /* need to check that the cap does not over lap another one 
++         or is an exact match with only the userkey changing */
++      list_for_each(tmp, &elan_cap_list) {
++              ptr = list_entry(tmp, ELAN_CAP_NODE_STRUCT , list);
++
++              /* is it an exact match */
++              if ( ELAN_CAP_TYPE_MATCH(&ptr->node.cap,cap) 
++                   && ELAN_CAP_GEOM_MATCH(&ptr->node.cap,cap)
++                   && (&ptr->node.owner == owner)) {
++                      if ( ptr->node.active ) {
++                              /* dont inc attached count as its like a create */
++                              ptr->node.cap.cap_userkey = cap->cap_userkey;
++                              kmutex_unlock(&elan_mutex);
++                              return ESUCCESS;
++                      }
++                      else
++                      {
++                              kmutex_unlock(&elan_mutex);
++                              return EINVAL;
++                      }
++              }
++              
++              /* does it overlap, even with ones being destroyed */
++              if (elan_cap_overlap(&ptr->node.cap,cap))
++              {
++                      kmutex_unlock(&elan_mutex);
++                      return  EACCES;
++              }
++      }
++
++      /* create it */
++      KMEM_ALLOC(ptr, ELAN_CAP_NODE_STRUCT *, sizeof(ELAN_CAP_NODE_STRUCT), 1);
++      if (ptr == NULL)
++      {
++              kmutex_unlock(&elan_mutex);
++              return  ENOMEM;
++      }
++
++      /* create space for the attached array */
++      for(rail=0;rail<ELAN_MAX_RAILS;rail++)
++      {
++              ptr->attached[rail]=NULL;
++              if ( ELAN_CAP_IS_RAIL_SET(cap,rail) ) 
++              {
++                      KMEM_ALLOC(ptr->attached[rail], ELAN_ATTACHED_STRUCT *, sizeof(ELAN_ATTACHED_STRUCT) *  ELAN_CAP_NUM_CONTEXTS(cap), 1);
++                      if (ptr->attached[rail] == NULL) 
++                      {
++                              for(;rail>=0;rail--)
++                                      if ( ptr->attached[rail] )
++                                              KMEM_FREE(ptr->attached[rail], sizeof(ELAN_ATTACHED_STRUCT) *  ELAN_CAP_NUM_CONTEXTS(cap));
++
++                              KMEM_FREE(ptr, sizeof(ELAN_CAP_NODE_STRUCT));
++                              kmutex_unlock(&elan_mutex);
++                              return  ENOMEM;
++                      }
++                      /* blank the attached array */
++                      for(i=0;i<ELAN_CAP_NUM_CONTEXTS(cap);i++)
++                              ptr->attached[rail][i].cb_func = NULL;
++              }
++      }       
++      
++      ptr->node.owner     = owner;
++      ptr->node.cap       = *cap;
++      ptr->node.attached  = 1;    /* creator counts as attached */
++      ptr->node.active    = 1;
++      ptr->vp_list.next   = &(ptr->vp_list);
++      ptr->vp_list.prev   = &(ptr->vp_list);
++
++      list_add_tail(&ptr->list, &elan_cap_list);      
++
++      kmutex_unlock(&elan_mutex);
++      return  ESUCCESS;
++}
++
++void
++elan_destroy_cap_test(ELAN_CAP_NODE_STRUCT *cap_ptr)
++{
++      /* called by someone holding the mutex   */
++      struct list_head       * vp_tmp;
++      ELAN_VP_NODE_STRUCT * vp_ptr = NULL;
++      int                      rail;
++
++      /* check to see if it can be deleted now */
++      if ( cap_ptr->node.attached == 0 ) {
++              
++              ELAN_DEBUG0(ELAN_DBG_CAP,"elan_destroy_cap_test: attached == 0\n");     
++              
++              /* delete the vp list */
++              list_for_each(vp_tmp, &(cap_ptr->vp_list)) {
++                      vp_ptr = list_entry(vp_tmp, ELAN_VP_NODE_STRUCT , list);
++                      list_del(&vp_ptr->list);
++                      KMEM_FREE( vp_ptr, sizeof(ELAN_VP_NODE_STRUCT));
++              }
++              
++              list_del(&cap_ptr->list);
++
++              /* delete space for the attached array */
++              for(rail=0;rail<ELAN_MAX_RAILS;rail++)
++                      if (cap_ptr->attached[rail]) 
++                              KMEM_FREE(cap_ptr->attached[rail], sizeof(ELAN_ATTACHED_STRUCT) * ELAN_CAP_NUM_CONTEXTS(&(cap_ptr->node.cap)));
++                      
++              KMEM_FREE(cap_ptr, sizeof(ELAN_CAP_NODE_STRUCT));               
++      }
++}
++
++int
++elan_destroy_cap(ELAN_CAP_OWNER owner, ELAN_CAPABILITY *cap)
++{
++      char                      space[127];
++      struct list_head        * el;
++      struct list_head        * nel;
++      ELAN_CAP_NODE_STRUCT * ptr = NULL;
++      int                       i, rail;
++      int                       found = 0;
++
++      kmutex_lock(&elan_mutex);
++
++      ELAN_DEBUG1 (ELAN_DBG_CAP,"elan_destroy_cap %s\n",elan_capability_string(cap,space));   
++
++      list_for_each_safe (el, nel, &elan_cap_list) {
++              ptr = list_entry(el, ELAN_CAP_NODE_STRUCT , list);
++              
++              /* is it an exact match */
++              if ( (ptr->node.owner == owner )
++                   && (  (cap == NULL) 
++                         || (ELAN_CAP_TYPE_MATCH(&ptr->node.cap,cap) && ELAN_CAP_GEOM_MATCH(&ptr->node.cap,cap)))) {
++
++                      if ( ptr->node.active ) {
++
++                              /* mark as in active and dec attached count */
++                              ptr->node.active = 0;
++                              ptr->node.attached--;
++                              ptr->node.owner  = 0; /* no one own's it now */
++                              
++                              /* need to tell any one who was attached that this has been destroy'd */
++                              for(rail=0;rail<ELAN_MAX_RAILS;rail++)
++                                      if (ELAN_CAP_IS_RAIL_SET( &(ptr->node.cap), rail)) {
++                                              for(i=0;i< ELAN_CAP_NUM_CONTEXTS(&(ptr->node.cap));i++)
++                                                      if ( ptr->attached[rail][i].cb_func != NULL) 
++                                                              ptr->attached[rail][i].cb_func(ptr->attached[rail][i].cb_args, cap, NULL);
++                                      }
++                              
++                              /* now try to destroy it */
++                              elan_destroy_cap_test(ptr);
++                              
++                              /* found it */
++                              found = 1;
++                      }
++              }
++      }
++      
++      if ( found )
++      {
++              kmutex_unlock(&elan_mutex);
++              return ESUCCESS;
++      }
++
++      /* failed */
++      ELAN_DEBUG0(ELAN_DBG_CAP,"elan_destroy_cap: didnt find it \n"); 
++
++      kmutex_unlock(&elan_mutex);
++      return EINVAL;
++}
++
++int 
++elan_get_caps(uint *number_of_results, uint array_size, ELAN_CAP_STRUCT *caps)
++{
++      uint                      results = 0;
++      struct list_head        * tmp;
++      ELAN_CAP_NODE_STRUCT * ptr = NULL;
++      
++
++      kmutex_lock(&elan_mutex);
++
++      ELAN_DEBUG0(ELAN_DBG_CAP,"elan_get_caps\n");    
++
++      list_for_each(tmp, &elan_cap_list) {
++              ptr = list_entry(tmp, ELAN_CAP_NODE_STRUCT , list);
++              
++              copyout(&ptr->node, &caps[results], sizeof (ELAN_CAP_STRUCT));
++              
++              results++;
++              
++              if ( results >= array_size )
++              {
++                      copyout(&results, number_of_results, sizeof(uint));     
++                      kmutex_unlock(&elan_mutex);
++                      return ESUCCESS;
++              }
++      }
++
++      copyout(&results, number_of_results, sizeof(uint));     
++
++      kmutex_unlock(&elan_mutex);
++      return ESUCCESS;
++}
++
++int
++elan_create_vp(ELAN_CAP_OWNER owner, ELAN_CAPABILITY *cap, ELAN_CAPABILITY *map)
++{
++      ELAN_CAP_NODE_STRUCT * cap_ptr = NULL;
++      ELAN_VP_NODE_STRUCT  * vp_ptr  = NULL;
++      
++      kmutex_lock(&elan_mutex);
++
++
++      ELAN_DEBUG0(ELAN_DBG_CAP,"elan_create_vp\n");
++
++      /* the railmasks must match */
++      if ( cap->cap_railmask != map->cap_railmask)
++      {
++              kmutex_unlock(&elan_mutex);
++              return  EINVAL;
++      }
++
++      /* does the cap exist */
++      cap_ptr = find_cap_node(cap);
++      if ((cap_ptr == NULL) || ( cap_ptr->node.owner != owner ) || (! cap_ptr->node.active) )
++      {
++              kmutex_unlock(&elan_mutex);
++              return  EINVAL;
++      }
++      
++      /* is there already a mapping */
++      vp_ptr = find_vp_node(cap_ptr,map);
++      if ( vp_ptr != NULL) 
++      {
++              kmutex_unlock(&elan_mutex);
++              return  EINVAL;
++      }
++
++      /* create space for mapping */
++      KMEM_ALLOC(vp_ptr, ELAN_VP_NODE_STRUCT *, sizeof(ELAN_VP_NODE_STRUCT), 1);
++      if (vp_ptr == NULL)
++      {
++              kmutex_unlock(&elan_mutex);
++              return  ENOMEM;
++      }
++                      
++      /* copy map */
++      vp_ptr->vp = *map;
++      list_add_tail(&vp_ptr->list, &(cap_ptr->vp_list));      
++      kmutex_unlock(&elan_mutex);
++      return  ESUCCESS;
++}
++
++int
++elan_destroy_vp(ELAN_CAP_OWNER owner, ELAN_CAPABILITY *cap, ELAN_CAPABILITY *map)
++{
++      ELAN_CAP_NODE_STRUCT * cap_ptr = NULL;
++      ELAN_VP_NODE_STRUCT  * vp_ptr  = NULL;
++      int                       i, rail;
++
++      kmutex_lock(&elan_mutex);
++
++      ELAN_DEBUG0(ELAN_DBG_CAP,"elan_destroy_vp\n");  
++
++      cap_ptr = find_cap_node(cap);
++      if ((cap_ptr!=NULL) && (cap_ptr->node.owner == owner) && ( cap_ptr->node.active))
++      {               
++              vp_ptr = find_vp_node( cap_ptr, map );
++              if ( vp_ptr != NULL ) 
++              {
++                      list_del(&vp_ptr->list);
++                      KMEM_FREE(vp_ptr, sizeof(ELAN_VP_NODE_STRUCT));
++            
++                      /* need to tell those who are attached that map is nolonger in use */
++                      for(rail=0;rail<ELAN_MAX_RAILS;rail++)
++                              if (ELAN_CAP_IS_RAIL_SET(cap, rail))
++                              {
++                                      for(i=0;i< ELAN_CAP_NUM_CONTEXTS(&(cap_ptr->node.cap));i++)
++                                              if ( cap_ptr->attached[rail][i].cb_func != NULL) 
++                                                      cap_ptr->attached[rail][i].cb_func( cap_ptr->attached[rail][i].cb_args, cap, map);
++                              }
++
++                      kmutex_unlock(&elan_mutex);
++                      return  ESUCCESS;
++              }
++      }       
++      
++      /* didnt find it */
++      kmutex_unlock(&elan_mutex);
++      return  EINVAL;
++}
++
++int 
++elan_attach_cap(ELAN_CAPABILITY *cap, unsigned int rail, void *args, ELAN_DESTROY_CB func)
++{
++      char                  space[127];
++      struct list_head     *el;
++
++      ELAN_DEBUG1 (ELAN_DBG_CAP,"elan_attach_cap %s\n",elan_capability_string(cap,space));
++
++      /* currently must provide a call back, as null mean something */
++      if ( func == NULL)
++              return (EINVAL);
++
++      /* mycontext must be set and correct */
++      if ( ! ELAN_CAP_VALID_MYCONTEXT(cap))
++              return (EINVAL);
++
++      /* rail must be one of the rails in railmask */
++      if (((1 << rail) & cap->cap_railmask) == 0)
++              return (EINVAL);
++      
++      kmutex_lock(&elan_mutex);
++
++      list_for_each(el, &elan_cap_list) {
++              ELAN_CAP_NODE_STRUCT *cap_ptr = list_entry(el, ELAN_CAP_NODE_STRUCT , list);
++              
++              /* is it an exact match */
++              if (ELAN_CAP_MATCH(&cap_ptr->node.cap,cap) && cap_ptr->node.active) {
++                      unsigned int attached_index = cap->cap_mycontext - cap->cap_lowcontext;
++                      
++                      if ( cap_ptr->attached[rail][attached_index].cb_func != NULL ) /* only one per ctx per rail */
++                      {
++                              kmutex_unlock(&elan_mutex);
++                              return   EINVAL;
++                      }
++
++                      /* keep track of who attached as we might need to tell them when */
++                      /* cap or maps get destroyed                                     */
++                      cap_ptr->attached[rail][ attached_index ].cb_func = func;
++                      cap_ptr->attached[rail][ attached_index ].cb_args = args;
++                      cap_ptr->node.attached++;
++
++                      ELAN_DEBUG0(ELAN_DBG_CAP,"elan_attach_cap: passed\n");
++                      kmutex_unlock(&elan_mutex);
++                      return ESUCCESS;
++              }
++      }
++      
++      ELAN_DEBUG0(ELAN_DBG_CAP,"elan_attach_cap: failed to find \n");
++
++      /* didnt find one */
++      kmutex_unlock(&elan_mutex);
++      return EINVAL;
++}
++
++int 
++elan_detach_cap(ELAN_CAPABILITY *cap, unsigned int rail)
++{
++      struct list_head *el, *nel;
++      char              space[256];
++
++      kmutex_lock(&elan_mutex);
++
++      ELAN_DEBUG1(ELAN_DBG_CAP,"elan_detach_cap %s\n",elan_capability_string(cap,space));
++      list_for_each_safe (el, nel, &elan_cap_list) {
++              ELAN_CAP_NODE_STRUCT *ptr = list_entry (el, ELAN_CAP_NODE_STRUCT, list);
++
++              /* is it an exact match */
++              if (ELAN_CAP_TYPE_MATCH(&ptr->node.cap,cap) &&
++                  ELAN_CAP_GEOM_MATCH(&ptr->node.cap,cap) &&
++                  (ptr->node.cap.cap_railmask & cap->cap_railmask) == cap->cap_railmask) {
++              
++                      unsigned int attached_index = cap->cap_mycontext - cap->cap_lowcontext;
++
++                      if ( ptr->attached[rail][ attached_index ].cb_func == NULL ) 
++                              ELAN_DEBUG0(ELAN_DBG_CAP,"elanmod_detach_cap already removed \n");
++
++                      ptr->attached[rail][ attached_index ].cb_func = NULL;
++                      ptr->attached[rail][ attached_index ].cb_args = (void *)0;
++
++                      ptr->node.attached--;
++
++                      ELAN_DEBUG1(ELAN_DBG_CAP,"elanmod_detach_cap new attach count%d \n", ptr->node.attached);
++
++                      elan_destroy_cap_test(ptr);
++
++                      ELAN_DEBUG0(ELAN_DBG_CAP,"elan_detach_cap: success\n"); 
++
++                      kmutex_unlock(&elan_mutex);
++                      return  ESUCCESS;
++              }
++      }
++
++      ELAN_DEBUG0(ELAN_DBG_CAP,"elan_detach_cap: failed to find\n");
++      kmutex_unlock(&elan_mutex);
++      return  EINVAL;
++}
++
++int
++elan_cap_dump()
++{
++      struct list_head        * tmp;
++      ELAN_CAP_NODE_STRUCT * ptr = NULL;
++      
++      kmutex_lock(&elan_mutex);       
++      
++      list_for_each(tmp, &elan_cap_list) {
++              ptr = list_entry(tmp, ELAN_CAP_NODE_STRUCT , list);
++
++              ELAN_DEBUG2 (ELAN_DBG_ALL, "cap dump: owner %p type %x\n", ptr->node.owner, ptr->node.cap.cap_type);
++                      
++              ELAN_DEBUG5 (ELAN_DBG_ALL, "cap dump: LowNode %d   HighNode %d   LowContext %d   mycontext %d   highContext %d\n",
++                           ptr->node.cap.cap_lownode , ptr->node.cap.cap_highnode,
++                           ptr->node.cap.cap_lowcontext , ptr->node.cap.cap_mycontext, ptr->node.cap.cap_highcontext);
++
++      }
++
++      kmutex_unlock(&elan_mutex);
++      return  ESUCCESS;
++}
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan/capability_general.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan/capability_general.c       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan/capability_general.c    2005-05-11 12:10:12.377941848 -0400
+@@ -0,0 +1,446 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: capability_general.c,v 1.10 2004/02/25 13:47:59 daniel Exp $"
++/*      $Source: /cvs/master/quadrics/elanmod/shared/capability_general.c,v $ */
++
++#if defined(__KERNEL__)
++
++#include <qsnet/kernel.h>
++
++#else
++
++#include <stdlib.h>
++#include <stdio.h>
++#include <sys/param.h>
++
++#endif
++
++#include <elan/elanmod.h>
++
++
++void
++elan_nullcap (ELAN_CAPABILITY *cap)
++{
++      register int i;
++
++      for (i = 0; i < sizeof (cap->cap_userkey)/sizeof(cap->cap_userkey.key_values[0]); i++)
++              cap->cap_userkey.key_values[i] = ELAN_CAP_UNINITIALISED;
++    
++      cap->cap_lowcontext  = ELAN_CAP_UNINITIALISED;
++      cap->cap_highcontext = ELAN_CAP_UNINITIALISED;
++      cap->cap_mycontext   = ELAN_CAP_UNINITIALISED;
++      cap->cap_lownode     = ELAN_CAP_UNINITIALISED;
++      cap->cap_highnode    = ELAN_CAP_UNINITIALISED;
++      cap->cap_railmask    = ELAN_CAP_UNINITIALISED;
++      cap->cap_type        = ELAN_CAP_UNINITIALISED;
++      cap->cap_spare       = 0;
++      cap->cap_version     = ELAN_CAP_VERSION_NUMBER;
++      
++      for (i = 0; i < sizeof (cap->cap_bitmap)/sizeof (cap->cap_bitmap[0]); i++)
++              cap->cap_bitmap[i] = 0;
++}
++
++char *
++elan_capability_string (ELAN_CAPABILITY *cap, char *str)
++{
++      if (cap == NULL) 
++              sprintf (str, "[-.-.-.-] cap = NULL\n");
++      else
++              sprintf (str, "[%x.%x.%x.%x] Version %x Type %x \n"
++                       "Context %x.%x.%x Node %x.%x\n",
++                       cap->cap_userkey.key_values[0], cap->cap_userkey.key_values[1],
++                       cap->cap_userkey.key_values[2], cap->cap_userkey.key_values[3],
++                       cap->cap_version, cap->cap_type, 
++                       cap->cap_lowcontext, cap->cap_mycontext, cap->cap_highcontext,
++                       cap->cap_lownode, cap->cap_highnode);
++      
++      return (str);
++}
++
++ELAN_LOCATION
++elan_vp2location (u_int process, ELAN_CAPABILITY *cap)
++{
++      ELAN_LOCATION location;
++      int i, vp, node, context, nnodes, nctxs;
++
++      vp = 0;
++
++      location.loc_node    = ELAN_INVALID_NODE;
++      location.loc_context = -1;
++       
++      nnodes = cap->cap_highnode - cap->cap_lownode + 1;
++      nctxs  = cap->cap_highcontext - cap->cap_lowcontext + 1;
++       
++      switch (cap->cap_type & ELAN_CAP_TYPE_MASK)
++      {
++      case ELAN_CAP_TYPE_BLOCK:
++              for (node = 0, i = 0; node < nnodes; node++)
++              {
++                      for (context = 0; context < nctxs; context++)
++                      {
++                              if ((cap->cap_type & ELAN_CAP_TYPE_NO_BITMAP) || BT_TEST (cap->cap_bitmap, context + (node * nctxs)))
++                              {
++                                      if (vp == process)
++                                      {
++                                              /* Return relative indices within the capability box */
++                                              location.loc_node    = node;
++                                              location.loc_context = context;
++
++                                              return (location);
++                                      }
++                     
++                                      vp++;
++                              }
++                      }
++              }
++              break;
++      
++      case ELAN_CAP_TYPE_CYCLIC:
++              for (context = 0, i = 0; context < nctxs; context++)
++              {
++                      for (node = 0; node < nnodes; node++)
++                      {
++                              if ((cap->cap_type & ELAN_CAP_TYPE_NO_BITMAP) || BT_TEST (cap->cap_bitmap, node + (context * nnodes)))
++                              {
++                                      if (vp == process)
++                                      {
++                                              location.loc_node    = node;
++                                              location.loc_context = context;
++
++                                              return (location);
++                                      }
++                  
++                                      vp++;
++                              }
++                      }
++              }
++              break;
++      }
++    
++      return( location );
++}
++
++int
++elan_location2vp (ELAN_LOCATION location, ELAN_CAPABILITY *cap)
++{
++    int  vp, node, context, nnodes, nctxs;
++
++    nnodes = cap->cap_highnode - cap->cap_lownode + 1;
++    nctxs  = cap->cap_highcontext - cap->cap_lowcontext + 1;
++
++    vp = 0;
++    
++    switch (cap->cap_type & ELAN_CAP_TYPE_MASK)
++    {
++    case ELAN_CAP_TYPE_BLOCK:
++      for (node = 0 ; node < nnodes ; node++)
++      {
++          for (context = 0; context < nctxs; context++)
++          {
++              if ((cap->cap_type & ELAN_CAP_TYPE_NO_BITMAP) || BT_TEST (cap->cap_bitmap, context + (node * nctxs)))
++              {
++                  if ((location.loc_node == node) && (location.loc_context == context))
++                  {
++                      /* Found it ! */
++                      return( vp );
++                  }
++                  
++                  vp++;
++              }
++          }
++      }
++      break;
++      
++    case ELAN_CAP_TYPE_CYCLIC:
++      for (context = 0; context < nctxs; context++)
++      {
++          for (node = 0; node < nnodes; node++)
++          {
++              if ((cap->cap_type & ELAN_CAP_TYPE_NO_BITMAP) || BT_TEST (cap->cap_bitmap, node + (context * nnodes)))
++              {
++                  if ((location.loc_node == node) && (location.loc_context == context))
++                  {
++                      /* Found it ! */
++                      return( vp );
++                  }
++                  
++                  vp++;
++              }
++          }
++      }
++      break;
++    }
++    
++    /* Failed to find it */
++    return( -1 );
++}
++
++/* Return the number of processes as described by a capability */
++int
++elan_nvps (ELAN_CAPABILITY *cap)
++{
++      int i, c, nbits = ELAN_CAP_BITMAPSIZE(cap);
++
++      if (cap->cap_type & ELAN_CAP_TYPE_NO_BITMAP)
++              return (nbits);
++
++      for (i = 0, c = 0; i < nbits; i++)
++              if (BT_TEST (cap->cap_bitmap, i))
++                      c++;
++
++      return (c);
++}
++
++/* Return the number of local processes on a given node as described by a capability */
++int
++elan_nlocal (int node, ELAN_CAPABILITY *cap)
++{
++      int vp;
++      ELAN_LOCATION loc;
++      int nLocal = 0;
++
++      for (vp = 0; vp < elan_nvps(cap); vp++)
++      {
++              loc = elan_vp2location(vp, cap);
++              if (loc.loc_node == node)
++                      nLocal++;
++      }
++
++      return (nLocal);
++}
++
++/* Return the maximum number of local processes on any node as described by a capability */
++int
++elan_maxlocal (ELAN_CAPABILITY *cap)
++{
++      return(cap->cap_highcontext - cap->cap_lowcontext + 1);
++}
++
++/* Return the vps of the local processes on a given node as described by a capability */
++int
++elan_localvps (int node, ELAN_CAPABILITY *cap, int *vps, int size)
++{
++      int context;
++      ELAN_LOCATION loc;
++      int nLocal = 0;
++    
++      loc.loc_node = node;
++
++      for (context = 0; context < MIN(size, elan_maxlocal(cap)); context++)
++      {
++              loc.loc_context = context;
++      
++              /* Should return -1 if none found */
++              if ( (vps[context] = elan_location2vp( loc, cap )) != -1)
++                      nLocal++;
++      }
++
++      return (nLocal);
++}
++
++/* Return the number of rails that this capability utilises */
++int
++elan_nrails (ELAN_CAPABILITY *cap)
++{
++      int nrails = 0;
++      unsigned int railmask;
++
++      /* Test for a multi-rail capability */
++      if (cap->cap_type & ELAN_CAP_TYPE_MULTI_RAIL)
++      {
++              /* Grab rail bitmask from capability */
++              railmask = cap->cap_railmask;
++      
++              while (railmask)
++              {
++                      if (railmask & 1)
++                              nrails++;
++          
++                      railmask >>= 1;
++              }
++      }
++      else 
++              /* Default to just one rail */
++              nrails = 1;
++      
++      return (nrails);
++}
++
++/* Fill out an array giving the physical rail numbers utilised by a capability */
++int
++elan_rails (ELAN_CAPABILITY *cap, int *rails)
++{
++      int nrails, rail;
++      unsigned int railmask;
++
++      /* Test for a multi-rail capability */
++      if (cap->cap_type & ELAN_CAP_TYPE_MULTI_RAIL)
++      {
++              /* Grab rail bitmask from capability */
++              railmask = cap->cap_railmask;
++      
++              nrails = rail = 0;
++              while (railmask)
++              {
++                      if (railmask & 1)
++                              rails[nrails++] = rail;
++          
++                      rail++;
++                      railmask >>= 1;
++              }
++      }
++      else
++      {
++              /* Default to just one rail */
++              rails[0] = 0;
++              nrails = 1;
++      }
++
++      return( nrails );
++}
++
++int 
++elan_cap_overlap(ELAN_CAPABILITY *cap1, ELAN_CAPABILITY *cap2)
++{
++      /* by context */
++      if ( cap1->cap_highcontext < cap2->cap_lowcontext ) return (0);
++      if ( cap1->cap_lowcontext  > cap2->cap_highcontext) return (0);
++      
++      /* by node */
++      if ( cap1->cap_highnode < cap2->cap_lownode ) return (0);
++      if ( cap1->cap_lownode  > cap2->cap_highnode) return (0);
++
++      /* by rail */
++      /* they overlap if they have a rail in common */
++      return (cap1->cap_railmask & cap2->cap_railmask);
++}
++
++#if !defined(__KERNEL__)
++
++/* Fill out an array that hints at the best use of the rails on a
++ * per process basis. The library user can then decide whether or not
++ * to take this into account (e.g. TPORTs)
++ * All processes calling this fn will be returned the same information.
++ */
++int
++elan_prefrails(ELAN_CAPABILITY *cap, int *pref, int nvp)
++{
++      int i;
++      int nrails = elan_nrails(cap);
++      int maxlocal = elan_maxlocal(cap);
++
++      /* Test for a multi-rail capability */
++      if (! (cap->cap_type & ELAN_CAP_TYPE_MULTI_RAIL))
++      {
++              /* Default to just one rail */
++              for (i = 0; i < nvp; i++)
++                      pref[i] = 0;
++
++              return( 0 );
++      }
++
++      /*
++       * We allocate rails on a per node basis sharing our the rails
++       * equally amongst the local processes. However, if there is only
++       * one process per node and multiple rails, then we use a different
++       * algorithm where rails are allocated across all the processes in 
++       * a round-robin fashion
++       */
++    
++      if (maxlocal == 1)
++      {
++              /* Allocate rails in a round-robin manner */
++              for (i = 0; i < nvp; i++)
++                      *pref++ = i % nrails;
++      }
++      else
++      {
++              int node;
++              int *vps;
++              int nnodes = cap->cap_highnode - cap->cap_lownode + 1;
++
++              vps = (int *) malloc(sizeof(int)*maxlocal);
++
++              /* Grab the local process info for each node and allocate
++               * rails to those vps on an equal basis
++               */
++              for (node = 0; node < nnodes; node++)
++              {
++                      int nlocal;
++                      int pprail;
++
++                      /* Grab an array of local vps */
++                      nlocal = elan_localvps(node, cap, vps, maxlocal);
++          
++                      /* Calculate the number processes per rail */
++                      if ((pprail = nlocal/nrails) == 0)
++                              pprail = 1;
++
++                      /* Allocate processes to rails */
++                      for (i = 0; i < nlocal; i++)
++                      {
++                              pref[vps[i]] = (i / pprail) % nrails;
++                      }
++              }
++      
++              free(vps);
++      }
++
++      return( 0 );
++}
++
++void 
++elan_get_random_key(ELAN_USERKEY *key)
++{
++    int i;
++    for (i = 0; i < sizeof(key->key_values) / sizeof(key->key_values[0]); i++)
++      key->key_values[i] = lrand48();
++}
++
++int elan_lowcontext(ELAN_CAPABILITY *cap)
++{
++    return(cap->cap_lowcontext);
++}
++
++int elan_mycontext(ELAN_CAPABILITY *cap)
++{
++    return(cap->cap_mycontext);
++}
++
++int elan_highcontext(ELAN_CAPABILITY *cap)
++{
++    return(cap->cap_highcontext);
++}
++
++int elan_lownode(ELAN_CAPABILITY *cap)
++{
++    return(cap->cap_lownode);
++}
++
++int elan_highnode(ELAN_CAPABILITY *cap)
++{
++    return(cap->cap_highnode);
++}
++
++int elan_captype(ELAN_CAPABILITY *cap)
++{
++    return(cap->cap_type);
++}
++
++int elan_railmask(ELAN_CAPABILITY *cap)
++{
++    return(cap->cap_railmask);
++}
++
++#endif
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan/device.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan/device.c   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan/device.c        2005-05-11 12:10:12.378941696 -0400
+@@ -0,0 +1,147 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: device.c,v 1.5 2003/09/24 13:55:37 david Exp $"
++/*      $Source: /cvs/master/quadrics/elanmod/modsrc/device.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <elan/elanmod.h>
++
++static LIST_HEAD(elan_dev_list);
++
++ELAN_DEV_STRUCT *
++elan_dev_find (ELAN_DEV_IDX devidx)
++{
++      struct list_head   *tmp;
++      ELAN_DEV_STRUCT *ptr=NULL;
++
++      list_for_each(tmp, &elan_dev_list) {
++              ptr = list_entry(tmp, ELAN_DEV_STRUCT , node);
++              if (ptr->devidx == devidx) 
++                      return ptr;
++              if (ptr->devidx > devidx)
++                      return ERR_PTR(-ENXIO);
++      }
++      
++      return ERR_PTR(-EINVAL);
++}
++
++ELAN_DEV_STRUCT *
++elan_dev_find_byrail (unsigned short deviceid, unsigned rail)
++{
++      struct list_head   *tmp;
++      ELAN_DEV_STRUCT *ptr=NULL;
++
++      list_for_each(tmp, &elan_dev_list) {
++              ptr = list_entry(tmp, ELAN_DEV_STRUCT , node);
++
++              ELAN_DEBUG5 (ELAN_DBG_ALL,"elan_dev_find_byrail devidx %d - %04x %04x,  %d %d \n", ptr->devidx, 
++                           ptr->devinfo->dev_device_id, deviceid, ptr->devinfo->dev_rail, rail);
++
++              if (ptr->devinfo->dev_device_id == deviceid && ptr->devinfo->dev_rail == rail)
++                      return ptr;
++      }
++      
++      return NULL;
++}
++
++ELAN_DEV_IDX
++elan_dev_register (ELAN_DEVINFO *devinfo, ELAN_DEV_OPS *ops, void * user_data)
++{
++      ELAN_DEV_STRUCT *ptr;
++      ELAN_DEV_IDX        devidx = 0;
++      struct list_head   *tmp;
++
++        kmutex_lock(&elan_mutex);
++
++      /* is it already registered */
++      if ((ptr = elan_dev_find_byrail(devinfo->dev_device_id, devinfo->dev_rail)) != NULL) 
++      {
++              kmutex_unlock(&elan_mutex);
++              return EINVAL;
++      }
++
++      /* find a free device idx */
++      list_for_each (tmp, &elan_dev_list) {
++              if (list_entry (tmp, ELAN_DEV_STRUCT, node)->devidx != devidx)
++                      break;
++              devidx++;
++      }
++
++      /* create it and add */
++      KMEM_ALLOC(ptr, ELAN_DEV_STRUCT *, sizeof(ELAN_DEV_STRUCT), 1);
++      if (ptr == NULL)
++      {
++              kmutex_unlock(&elan_mutex);
++              return ENOMEM;
++      }
++
++      ptr->devidx    = devidx;
++      ptr->ops       = ops;
++      ptr->devinfo   = devinfo;
++      ptr->user_data = user_data;
++
++      /* insert this entry *before* the last entry we've found */
++      list_add_tail(&ptr->node, tmp);
++
++      kmutex_unlock(&elan_mutex);
++      return  ESUCCESS;
++}
++
++int
++elan_dev_deregister (ELAN_DEVINFO *devinfo)
++{
++      ELAN_DEV_STRUCT *target;
++
++      kmutex_lock(&elan_mutex);
++
++      if ((target = elan_dev_find_byrail (devinfo->dev_device_id, devinfo->dev_rail)) == NULL)
++      {
++              kmutex_unlock(&elan_mutex);
++              return  EINVAL;
++      }
++
++      list_del(&target->node);
++
++      /* delete target entry */
++      KMEM_FREE(target, sizeof(ELAN_DEV_STRUCT));
++
++      kmutex_unlock(&elan_mutex);
++      return  ESUCCESS;
++}
++
++int
++elan_dev_dump ()
++{
++      struct list_head   *tmp;
++      ELAN_DEV_STRUCT *ptr=NULL;
++
++      kmutex_lock(&elan_mutex);       
++
++      list_for_each(tmp, &elan_dev_list) {
++              ptr = list_entry(tmp, ELAN_DEV_STRUCT , node);
++
++              ELAN_DEBUG3 (ELAN_DBG_ALL,"dev dump: index %u rail %u elan%c\n", 
++                           ptr->devidx, ptr->devinfo->dev_rail, '3' + ptr->devinfo->dev_device_id);
++              ELAN_DEBUG5 (ELAN_DBG_ALL,"dev dump: Vid %x   Did %x  Rid %x  DR %d  DVal %x\n",
++                           ptr->devinfo->dev_vendor_id,
++                           ptr->devinfo->dev_device_id,
++                           ptr->devinfo->dev_revision_id,
++                           ptr->devinfo->dev_driver_version,
++                           ptr->devinfo->dev_num_down_links_value);
++
++      }
++
++      kmutex_unlock(&elan_mutex);
++      return ESUCCESS;
++}
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan/devinfo.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan/devinfo.c  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan/devinfo.c       2005-05-11 12:10:12.378941696 -0400
+@@ -0,0 +1,78 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: devinfo.c,v 1.5 2003/09/24 13:55:37 david Exp $"
++/*      $Source: /cvs/master/quadrics/elanmod/modsrc/devinfo.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <elan/elanmod.h>
++
++int 
++elan_get_devinfo(ELAN_DEV_IDX devidx, ELAN_DEVINFO *devinfo)
++{
++      ELAN_DEV_STRUCT *target;
++      int                 res;
++
++      kmutex_lock(&elan_mutex);
++
++      target = elan_dev_find (devidx);
++
++      if (IS_ERR (target))
++              res = PTR_ERR(target);
++      else
++      {
++              copyout(target->devinfo, devinfo, sizeof(ELAN_DEVINFO));
++              res = ESUCCESS;
++      }
++      
++      kmutex_unlock(&elan_mutex);
++      return res;
++}
++
++int 
++elan_get_position(ELAN_DEV_IDX devidx, ELAN_POSITION *position)
++{
++      ELAN_DEV_STRUCT *target;
++      int                 res;
++
++      kmutex_lock(&elan_mutex);
++
++      target = elan_dev_find(devidx);
++
++      if (IS_ERR (target))
++              res = PTR_ERR(target);
++      else
++              res = target->ops->get_position(target->user_data, position);
++      
++      kmutex_unlock(&elan_mutex);
++      return res;
++}
++
++int 
++elan_set_position(ELAN_DEV_IDX devidx, unsigned short nodeId, unsigned short numNodes)
++{
++      ELAN_DEV_STRUCT *target;
++      int                 res;
++
++      kmutex_lock(&elan_mutex);
++
++      target = elan_dev_find(devidx);
++
++      if (IS_ERR (target))
++              res = PTR_ERR (target);
++      else
++              res = target->ops->set_position(target->user_data, nodeId, numNodes);
++      
++      kmutex_unlock(&elan_mutex);
++      return res;
++}
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan/elanmod.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan/elanmod.c  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan/elanmod.c       2005-05-11 12:10:12.378941696 -0400
+@@ -0,0 +1,149 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++#ident "@(#)$Id: elanmod.c,v 1.11 2004/06/18 09:28:16 mike Exp $"
++/*      $Source: /cvs/master/quadrics/elanmod/modsrc/elanmod.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <elan/elanmod.h>
++
++kmutex_t  elan_mutex;
++
++int 
++elan_init()
++{
++      kmutex_init(&elan_mutex);
++      return (ESUCCESS);
++}
++
++int 
++elan_fini()
++{
++      kmutex_destroy(&elan_mutex);
++      return (ESUCCESS);
++}
++
++int 
++elanmod_classify_cap (ELAN_POSITION *position, ELAN_CAPABILITY *cap, unsigned use)
++{
++      if (cap->cap_version != ELAN_CAP_VERSION_NUMBER)
++      {
++              ELAN_DEBUG2 (ELAN_DBG_VP, "elanmod_classify_cap: (cap->Version != ELAN_CAP_VERSION) %d %d\n", cap->cap_version, ELAN_CAP_VERSION_NUMBER);
++              return (-EINVAL);
++      }
++      
++      if (cap->cap_lowcontext == ELAN_CAP_UNINITIALISED || cap->cap_highcontext == ELAN_CAP_UNINITIALISED)
++      {
++              ELAN_DEBUG3 (ELAN_DBG_VP, "elanmod_classify_cap: LowContext %d    HighContext %d MyContext %d\n",
++                           cap->cap_lowcontext , cap->cap_highcontext, cap->cap_mycontext);
++              return (-EINVAL);
++      }
++      
++      if (cap->cap_lowcontext > cap->cap_highcontext)
++      {
++              ELAN_DEBUG2 (ELAN_DBG_VP, "elanmod_classify_cap: (cap->cap_lowcontext > cap->cap_highcontext) %d %d\n",cap->cap_lowcontext , cap->cap_highcontext);
++              return (-EINVAL);
++      }
++      
++      
++      switch (cap->cap_type & ELAN_CAP_TYPE_MASK)
++      {
++      case ELAN_CAP_TYPE_BLOCK:
++      case ELAN_CAP_TYPE_CYCLIC:
++              if (position->pos_mode == ELAN_POS_UNKNOWN)
++              {
++                      ELAN_DEBUG0 (ELAN_DBG_VP, "elanmod_classify_cap: Position Unknown \n");
++                      return (-EAGAIN);
++              }
++              
++              if ( ! ( ELAN_USER_CONTEXT(cap->cap_lowcontext) && ELAN_USER_CONTEXT(cap->cap_highcontext)))
++              {
++                      ELAN_DEBUG4 (ELAN_DBG_VP, "elanmod_classify_cap:  USER_BASE_CONTEXT %d %d %d %d \n" ,  ELAN_USER_BASE_CONTEXT_NUM,cap->cap_lowcontext, cap->cap_highcontext ,ELAN_USER_TOP_CONTEXT_NUM);
++                      return (-EINVAL);
++              }
++              if (cap->cap_lownode == ELAN_CAP_UNINITIALISED)
++                      cap->cap_lownode = position->pos_nodeid;
++              if (cap->cap_highnode == ELAN_CAP_UNINITIALISED)
++                      cap->cap_highnode = position->pos_nodeid;
++              
++              if (cap->cap_lownode < 0 || cap->cap_highnode >= position->pos_nodes || cap->cap_lownode > cap->cap_highnode)
++              {
++                      ELAN_DEBUG3 ( ELAN_DBG_VP,"elanmod_classify_cap: low %d high %d pos %d \n" , cap->cap_lownode  ,cap->cap_highnode, position->pos_nodes);
++                      
++                      return (-EINVAL);
++              }
++              
++              if ((cap->cap_highnode < position->pos_nodeid) || (cap->cap_lownode > position->pos_nodeid))
++              {
++                      ELAN_DEBUG3 (ELAN_DBG_VP, "elanmod_classify_cap: node not i range low %d high %d this %d\n",
++                                   cap->cap_lownode, cap->cap_highnode, position->pos_nodeid);
++                      return (-EINVAL);
++              }
++
++              break;
++      default:
++              ELAN_DEBUG1 (ELAN_DBG_VP, "elanmod_classify_cap: cant decode type %x \n", cap->cap_type & ELAN_CAP_TYPE_MASK);
++              return (-EINVAL);
++
++      }
++
++      switch (use)
++      {
++      case ELAN_USER_ATTACH:
++      case ELAN_USER_DETACH:
++              if (cap->cap_mycontext == ELAN_CAP_UNINITIALISED)
++              {
++                      ELAN_DEBUG0 (ELAN_DBG_VP, "elanmod_classify_cap: cap->cap_mycontext == ELAN_CAP_UNINITIALISED");
++                      return (-EINVAL);
++              }
++      
++              if ((cap->cap_mycontext != ELAN_CAP_UNINITIALISED) && 
++                  (cap->cap_mycontext < cap->cap_lowcontext || cap->cap_mycontext > cap->cap_highcontext))
++              {
++                      ELAN_DEBUG3 (ELAN_DBG_VP, "elanmod_classify_cap: cap->cap_mycontext out of range %d %d %d \n", cap->cap_lowcontext,cap->cap_mycontext,cap->cap_highcontext);
++                      return (-EINVAL);
++              }   
++              break;
++
++      case ELAN_USER_P2P:
++              break;
++
++      case ELAN_USER_BROADCAST:
++              if (! (cap->cap_type & ELAN_CAP_TYPE_BROADCASTABLE)) {
++                      ELAN_DEBUG0 (ELAN_DBG_VP, "elanmod_classify_cap: use ELAN_USER_BROADCAST but cap not ELAN_CAP_TYPE_BROADCASTABLE\n");
++                      return (-EINVAL);
++              }
++              break;
++
++      default:
++              ELAN_DEBUG1 (ELAN_DBG_VP, "elanmod_classify_cap: unknown use (%d)\n",use);
++              return (-EINVAL);
++      }
++
++
++
++      /* is any ctxt an rms one ?? */
++      if (ELAN_RMS_CONTEXT(cap->cap_lowcontext) || ELAN_RMS_CONTEXT(cap->cap_highcontext))
++      {
++              /* so both low and high must be */
++              if (!(ELAN_RMS_CONTEXT(cap->cap_lowcontext) && ELAN_RMS_CONTEXT(cap->cap_highcontext))) 
++              {
++                      ELAN_DEBUG2 (ELAN_DBG_VP, "elanmod_classify_cap: not rms ctxt %x %x\n",cap->cap_lowcontext,cap->cap_highcontext );
++                      return (-EINVAL);
++              }
++              ELAN_DEBUG0 (ELAN_DBG_VP, "elanmod_classify_cap: returning ELAN_CAP_RMS\n");
++              return (ELAN_CAP_RMS);
++      }
++
++      ELAN_DEBUG0 (ELAN_DBG_VP, "elanmod_classify_cap: returning ELAN_CAP_OK\n");
++      return (ELAN_CAP_OK);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan/elanmod_linux.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan/elanmod_linux.c    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan/elanmod_linux.c 2005-05-11 12:10:12.379941544 -0400
+@@ -0,0 +1,410 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: elanmod_linux.c,v 1.16 2004/06/14 15:45:37 mike Exp $"
++/*      $Source: /cvs/master/quadrics/elanmod/modsrc/elanmod_linux.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan/elanmod.h>
++#include <elan/elanmod_linux.h>
++
++#include <linux/module.h>
++
++#include <linux/sysctl.h>
++#include <linux/init.h>
++
++#include <qsnet/procfs_linux.h>
++
++MODULE_AUTHOR("Quadrics Ltd.");
++MODULE_DESCRIPTION("Elan support module");
++
++MODULE_LICENSE("GPL");
++
++/* elanmod.c */
++EXPORT_SYMBOL(elanmod_classify_cap);
++
++/* bitmap.c */
++#include <elan/bitmap.h>
++
++EXPORT_SYMBOL(bt_freebit);
++EXPORT_SYMBOL(bt_lowbit); 
++EXPORT_SYMBOL(bt_nextbit);
++EXPORT_SYMBOL(bt_copy);
++EXPORT_SYMBOL(bt_zero); 
++EXPORT_SYMBOL(bt_fill); 
++EXPORT_SYMBOL(bt_cmp); 
++EXPORT_SYMBOL(bt_intersect);
++EXPORT_SYMBOL(bt_remove); 
++EXPORT_SYMBOL(bt_add); 
++EXPORT_SYMBOL(bt_spans);
++EXPORT_SYMBOL(bt_subset);  
++EXPORT_SYMBOL(bt_up);
++EXPORT_SYMBOL(bt_down);
++EXPORT_SYMBOL(bt_nbits);
++
++/* capability.c */
++EXPORT_SYMBOL(elan_nullcap);
++EXPORT_SYMBOL(elan_detach_cap);
++EXPORT_SYMBOL(elan_attach_cap);
++EXPORT_SYMBOL(elan_validate_map);
++
++/* stats.c */
++EXPORT_SYMBOL(elan_stats_register);
++EXPORT_SYMBOL(elan_stats_deregister);
++
++/* device.c */
++EXPORT_SYMBOL(elan_dev_deregister);
++EXPORT_SYMBOL(elan_dev_register);
++
++/* debug */
++int  elan_debug_mode = QSNET_DEBUG_BUFFER; 
++int  elan_debug_mask;
++
++static struct proc_dir_entry *elan_procfs_root;
++
++extern void elan_procfs_init(void);
++extern void elan_procfs_fini(void);
++
++static int elan_open    (struct inode *ino, struct file *fp);
++static int elan_release (struct inode *ino, struct file *fp);
++static int elan_ioctl   (struct inode *ino, struct file *fp, unsigned int cmd, unsigned long arg);
++
++static struct file_operations elan_fops = 
++{
++      ioctl:   elan_ioctl,
++      open:    elan_open,
++      release: elan_release,
++};
++
++static int __init elan_start(void)
++{
++      int res;
++
++      elan_procfs_init(); 
++
++      if ((res = elan_init()) != ESUCCESS)
++      {
++              elan_procfs_fini();
++              return (-res);
++      }
++
++      return (0);
++}
++
++static void __exit elan_exit(void)
++{
++      elan_fini();
++      elan_procfs_fini();
++}
++
++
++/* Declare the module init and exit functions */
++void
++elan_procfs_init()
++{
++      struct proc_dir_entry  *p;
++      
++      elan_procfs_root = proc_mkdir("elan",   qsnet_procfs_root);
++      
++      qsnet_proc_register_hex(elan_procfs_root, "debug_mask", &elan_debug_mask, 0);
++      qsnet_proc_register_hex(elan_procfs_root, "debug_mode", &elan_debug_mode, 0);
++
++      if ((p = create_proc_entry ("ioctl", 0, elan_procfs_root)) != NULL)
++      {
++              p->proc_fops = &elan_fops;
++              p->data      = 0;
++              p->owner     = THIS_MODULE;
++      }   
++}
++
++void
++elan_procfs_fini()
++{
++      remove_proc_entry ("debug_mask", elan_procfs_root);
++      remove_proc_entry ("debug_mode", elan_procfs_root);
++      
++      remove_proc_entry ("ioctl",   elan_procfs_root); 
++      remove_proc_entry ("version", elan_procfs_root);  
++      
++      remove_proc_entry ("elan",   qsnet_procfs_root);
++}
++
++module_init(elan_start);
++module_exit(elan_exit);
++
++static int
++elan_open (struct inode *inode, struct file *fp)
++{
++      MOD_INC_USE_COUNT;
++      fp->private_data = NULL;
++      return (0);
++}
++
++static int
++elan_release (struct inode *inode, struct file *fp)
++{
++      /* mark all caps owned by fp to be destroyed */
++      elan_destroy_cap(fp,NULL);
++
++      MOD_DEC_USE_COUNT;
++      return (0);
++}
++
++static int 
++elan_ioctl(struct inode *inode, struct file *fp, unsigned int cmd, unsigned long arg)
++{
++      int rep = 0;
++
++      switch (cmd) 
++      {
++      case ELANCTRL_STATS_GET_NEXT :
++      {
++              ELANCTRL_STATS_GET_NEXT_STRUCT args;
++
++              if (copy_from_user (&args, (void *) arg, sizeof (ELANCTRL_STATS_GET_NEXT_STRUCT)))
++                      return (-EFAULT); 
++
++              /* uses copyin/copyout */
++              if (elan_stats_get_next_index(args.statidx, args.next_statidx) != 0 ) 
++                      return (-EINVAL);       
++
++              break;
++      }
++      case ELANCTRL_STATS_FIND_INDEX :
++      {
++              ELANCTRL_STATS_FIND_INDEX_STRUCT args;
++
++              if (copy_from_user (&args, (void *) arg, sizeof (ELANCTRL_STATS_FIND_INDEX_STRUCT)))
++                      return (-EFAULT); 
++
++              /* uses copyin/copyout */
++              if (elan_stats_find_index(args.block_name, args.statidx, args.num_entries) != 0 ) 
++                      return (-EINVAL);       
++
++              break;
++      }
++      case ELANCTRL_STATS_GET_BLOCK_INFO :
++      {
++              ELANCTRL_STATS_GET_BLOCK_INFO_STRUCT args;
++              
++              if (copy_from_user (&args, (void *) arg, sizeof (ELANCTRL_STATS_GET_BLOCK_INFO_STRUCT)))
++                      return (-EFAULT);
++
++              /* uses copyin/copyout */
++              if (elan_stats_get_block_info(args.statidx, args.block_name, args.num_entries) != 0 ) 
++                      return (-EINVAL);
++              break;          
++      }
++      case ELANCTRL_STATS_GET_INDEX_NAME :
++      {
++              ELANCTRL_STATS_GET_INDEX_NAME_STRUCT args;
++              
++              if (copy_from_user (&args, (void *) arg, sizeof (ELANCTRL_STATS_GET_INDEX_NAME_STRUCT)))
++                      return (-EFAULT);
++
++              /* uses copyin/copyout */
++              if (elan_stats_get_index_name(args.statidx, args.index, args.name) != 0 )
++                      return (-EINVAL);
++              break;
++      }
++      case ELANCTRL_STATS_CLEAR_BLOCK :
++      {
++              ELANCTRL_STATS_CLEAR_BLOCK_STRUCT args;
++              
++              if (copy_from_user (&args, (void *) arg, sizeof (ELANCTRL_STATS_CLEAR_BLOCK_STRUCT)))
++                      return (-EFAULT);
++
++              /* statidx is not a pointer */
++              if (elan_stats_clear_block(args.statidx) != 0 )
++                      return (-EINVAL);
++              break;
++      }
++      case ELANCTRL_STATS_GET_BLOCK :
++      {
++              ELANCTRL_STATS_GET_BLOCK_STRUCT args;
++              
++              if (copy_from_user (&args, (void *) arg, sizeof (ELANCTRL_STATS_GET_BLOCK_STRUCT)))
++                      return (-EFAULT);
++
++              /* uses copyin/copyout */
++              if (elan_stats_get_block(args.statidx, args.entries, args.values) != 0 )
++                      return (-EINVAL);
++              break;
++      }
++      case ELANCTRL_GET_DEVINFO :
++      {
++              ELANCTRL_GET_DEVINFO_STRUCT args;
++              
++              if (copy_from_user (&args, (void *) arg, sizeof (ELANCTRL_GET_DEVINFO_STRUCT)))
++                      return (-EFAULT);
++
++              /* uses copyin/copyout */
++              if (elan_get_devinfo(args.devidx, args.devinfo) != 0 )
++                      return (-EINVAL);
++              break;          
++      }
++      case ELANCTRL_GET_POSITION :
++      {
++              ELANCTRL_GET_POSITION_STRUCT args;
++              
++              if (copy_from_user (&args, (void *) arg, sizeof (ELANCTRL_GET_POSITION_STRUCT)))
++                      return (-EFAULT); 
++
++              /* uses copyin/copyout */
++              if (elan_get_position(args.devidx, args.position) != 0 )
++                      return (-EINVAL);
++              break;          
++      }
++      case ELANCTRL_SET_POSITION :
++      {
++              ELANCTRL_SET_POSITION_STRUCT args;
++              
++              if (copy_from_user (&args, (void *) arg, sizeof (ELANCTRL_SET_POSITION_STRUCT)))
++                      return (-EFAULT);
++
++              /* uses copyin/copyout */
++              if (elan_set_position(args.devidx, args.nodeId, args.numNodes) != 0 )
++                      return (-EINVAL);       
++              break;          
++      }
++      case ELANCTRL_CREATE_CAP  :
++      {
++              ELANCTRL_CREATE_CAP_STRUCT *args;
++
++              /* get space for args */
++              KMEM_ALLOC(args, ELANCTRL_CREATE_CAP_STRUCT *, sizeof(ELANCTRL_CREATE_CAP_STRUCT), 1);
++              if (args == NULL)
++                      return(-ENOMEM);        
++
++              /* copy them */
++              if (copy_from_user (args, (void *) arg, sizeof (ELANCTRL_CREATE_CAP_STRUCT)))
++                      return (-EFAULT);
++              else 
++              {
++                      if ((elan_validate_cap(&args->cap) != 0) || (elan_create_cap(fp,&args->cap) != 0 )) 
++                              rep = (-EINVAL);
++              }
++
++              /* free the space */
++              KMEM_FREE(args, sizeof(ELANCTRL_CREATE_CAP_STRUCT));
++
++              break;          
++      }
++      case ELANCTRL_DESTROY_CAP  :
++      {
++              ELANCTRL_DESTROY_CAP_STRUCT *args;
++
++              /* get space for args */
++              KMEM_ALLOC(args, ELANCTRL_DESTROY_CAP_STRUCT *, sizeof(ELANCTRL_DESTROY_CAP_STRUCT), 1);
++              if (args == NULL)
++                      return(-ENOMEM);        
++
++              /* copy them */
++              if (copy_from_user (args, (void *) arg, sizeof (ELANCTRL_DESTROY_CAP_STRUCT)))
++                      rep = (-EFAULT);
++              else 
++              {
++                      if (elan_destroy_cap(fp, &args->cap) != 0 )
++                              rep = (-EINVAL);
++              }
++
++              /* free the space */
++              KMEM_FREE(args, sizeof(ELANCTRL_DESTROY_CAP_STRUCT));
++
++              break;          
++      }
++      case ELANCTRL_CREATE_VP  :
++      {
++              ELANCTRL_CREATE_VP_STRUCT *args;
++
++              /* get space for args */
++              KMEM_ALLOC(args, ELANCTRL_CREATE_VP_STRUCT *, sizeof(ELANCTRL_CREATE_VP_STRUCT), 1);
++              if (args == NULL)
++                      return(-ENOMEM);        
++
++              /* copy them */
++              if (copy_from_user (args, (void *) arg, sizeof (ELANCTRL_CREATE_VP_STRUCT)))
++                      return (-EFAULT);
++              else
++              {
++                      if ((elan_validate_cap( &args->map) != 0) || (elan_create_vp(fp, &args->cap, &args->map) != 0 ))
++                              rep = (-EINVAL);        
++              }
++
++              KMEM_FREE(args, sizeof(ELANCTRL_CREATE_VP_STRUCT ));
++
++              break;          
++      }
++      case ELANCTRL_DESTROY_VP  :
++      {
++              ELANCTRL_DESTROY_VP_STRUCT *args;
++
++              /* get space for args */
++              KMEM_ALLOC(args, ELANCTRL_DESTROY_VP_STRUCT *, sizeof(ELANCTRL_DESTROY_VP_STRUCT), 1);
++              if (args == NULL)
++                      return(-ENOMEM);        
++              
++              /* copy them */
++              if (copy_from_user (args, (void *) arg, sizeof (ELANCTRL_DESTROY_VP_STRUCT)))
++                      rep = (-EFAULT);
++              else 
++              {
++                      if (elan_destroy_vp(fp, &args->cap, &args->map) != 0 )
++                              rep = (-EINVAL);        
++              }
++
++              KMEM_FREE(args, sizeof(ELANCTRL_DESTROY_VP_STRUCT ));
++
++              break;          
++      }
++
++      case ELANCTRL_GET_CAPS  :
++      {
++              ELANCTRL_GET_CAPS_STRUCT args;
++              if (copy_from_user (&args, (void *) arg, sizeof (ELANCTRL_GET_CAPS_STRUCT)))
++                      return (-EFAULT);
++
++              /* uses copyin/copyout */
++              if (elan_get_caps(args.number_of_results, args.array_size, args.caps) != 0 )
++                      return (-EINVAL);
++              break;          
++      }
++      case ELANCTRL_DEBUG_DUMP :
++      {
++              elan_cap_dump();
++              elan_dev_dump();
++
++              break;
++      }
++      case ELANCTRL_DEBUG_BUFFER :
++      {
++              ELANCTRL_DEBUG_BUFFER_STRUCT args;
++
++              if (copy_from_user (&args, (void *) arg, sizeof (ELANCTRL_DEBUG_BUFFER_STRUCT)))
++                      return (-EFAULT);
++
++              /* uses copyin/copyout */
++              if ((args.size = qsnet_debug_buffer (args.buffer, args.size)) != -1 &&
++                  copy_to_user ((void *) arg, &args, sizeof (ELANCTRL_DEBUG_BUFFER_STRUCT)))
++                      return (-EFAULT);
++              break;
++      }
++      default:
++              return (-EINVAL);
++              break;
++      }
++
++      return (rep);
++}
++
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan/Makefile
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan/Makefile   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan/Makefile        2005-05-11 12:10:12.379941544 -0400
+@@ -0,0 +1,15 @@
++#
++# Makefile for Quadrics QsNet
++#
++# Copyright (c) 2002-2004 Quadrics Ltd
++#
++# File: drivers/net/qsnet/elan/Makefile
++#
++
++
++#
++
++obj-$(CONFIG_QSNET)   += elan.o
++elan-objs     := elanmod.o device.o stats.o devinfo.o capability.o elanmod_linux.o capability_general.o bitmap.o
++
++EXTRA_CFLAGS          +=  -DDEBUG -DDEBUG_PRINTF -DDEBUG_ASSERT
+Index: linux-2.6.5/drivers/net/qsnet/elan/Makefile.conf
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan/Makefile.conf      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan/Makefile.conf   2005-05-11 12:10:12.380941392 -0400
+@@ -0,0 +1,10 @@
++# Flags for generating QsNet Linux Kernel Makefiles
++MODNAME               =       elan.o
++MODULENAME    =       elan
++KOBJFILES     =       elanmod.o device.o stats.o devinfo.o capability.o elanmod_linux.o capability_general.o bitmap.o
++EXPORT_KOBJS  =       elanmod_linux.o 
++CONFIG_NAME   =       CONFIG_QSNET
++SGALFC                =       
++# EXTRALINES START
++
++# EXTRALINES END
+Index: linux-2.6.5/drivers/net/qsnet/elan/quadrics_version.h
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan/quadrics_version.h 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan/quadrics_version.h      2005-05-11 12:10:12.380941392 -0400
+@@ -0,0 +1 @@
++#define QUADRICS_VERSION "4.31qsnet"
+Index: linux-2.6.5/drivers/net/qsnet/elan/stats.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan/stats.c    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan/stats.c 2005-05-11 12:10:12.380941392 -0400
+@@ -0,0 +1,277 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: stats.c,v 1.6 2003/09/24 13:55:37 david Exp $"
++/*      $Source: /cvs/master/quadrics/elanmod/modsrc/stats.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <elan/elanmod.h>
++
++static LIST_HEAD(elan_stats_list);
++static ELAN_STATS_IDX elan_next_statidx=0;
++
++ELAN_STATS_STRUCT *
++elan_stats_find(ELAN_STATS_IDX statidx)
++{
++      struct list_head     *tmp;
++      ELAN_STATS_STRUCT *ptr=NULL;
++
++      list_for_each(tmp, &elan_stats_list) {
++              ptr = list_entry(tmp, ELAN_STATS_STRUCT , node);
++              if ( ptr->statidx == statidx ) 
++                      return ptr;
++      }
++
++      ELAN_DEBUG1 (ELAN_DBG_CTRL, "elan_stats_find failed %d\n", statidx);    
++      return NULL;
++}
++
++ELAN_STATS_STRUCT *
++elan_stats_find_by_name(caddr_t block_name)
++{
++      struct list_head     *tmp;
++      ELAN_STATS_STRUCT *ptr=NULL;
++
++      list_for_each(tmp, &elan_stats_list)    {
++              ptr = list_entry(tmp, ELAN_STATS_STRUCT , node);
++              if (!strcmp(ptr->block_name, block_name)) 
++              {
++                      ELAN_DEBUG3 (ELAN_DBG_CTRL, "elan_stats_find_by_name found %s (%d,%d)\n", block_name, ptr->statidx, ptr->num_entries);  
++                      return ptr;
++              }
++      }
++
++      ELAN_DEBUG1 (ELAN_DBG_CTRL, "elan_stats_find_by_name failed %s\n", block_name);
++      return NULL;
++}
++
++ELAN_STATS_STRUCT *
++elan_stats_find_next(ELAN_STATS_IDX statidx)
++{
++      struct list_head     *tmp;
++      ELAN_STATS_STRUCT *ptr=NULL;
++
++      list_for_each(tmp, &elan_stats_list) {
++              ptr = list_entry(tmp, ELAN_STATS_STRUCT , node);
++        
++              if ( ptr->statidx > statidx ) 
++                      return ptr;       
++      }       
++
++      return NULL;
++}
++
++int 
++elan_stats_get_next_index (ELAN_STATS_IDX statidx, ELAN_STATS_IDX *next_block)
++{
++      ELAN_STATS_STRUCT *target;
++      ELAN_STATS_IDX        next = 0;
++
++      kmutex_lock(&elan_mutex);
++
++      if ((target = elan_stats_find_next(statidx)) != NULL)
++              next = target->statidx;
++
++      copyout(&next, next_block, sizeof(ELAN_STATS_IDX) );
++
++      kmutex_unlock(&elan_mutex);
++      return 0;
++}
++
++int 
++elan_stats_find_index  (caddr_t  block_name, ELAN_STATS_IDX *statidx,  uint *num_entries)
++
++{
++      ELAN_STATS_STRUCT *target;
++      ELAN_STATS_IDX        index   = 0;
++      uint                  entries = 0;
++
++      kmutex_lock(&elan_mutex);
++
++      ELAN_DEBUG1(ELAN_DBG_CTRL, "elan_stats_find_index %s \n", block_name);
++
++      if ((target = elan_stats_find_by_name(block_name)) != NULL)
++      {
++              index   = target->statidx;
++              entries = target->num_entries;
++      }
++
++      ELAN_DEBUG3(ELAN_DBG_CTRL, "elan_stats_find_index found %d %d (target=%p)\n", index, entries, target);
++
++      copyout(&index,   statidx,     sizeof(ELAN_STATS_IDX));
++      copyout(&entries, num_entries, sizeof(uint));
++
++      kmutex_unlock(&elan_mutex);
++      return  ESUCCESS;
++}
++
++int 
++elan_stats_get_block_info (ELAN_STATS_IDX statidx, caddr_t  block_name, uint *num_entries)
++{
++      ELAN_STATS_STRUCT *target;
++      int                   res=EINVAL;
++
++      kmutex_lock(&elan_mutex);
++
++      ELAN_DEBUG1(ELAN_DBG_CTRL, "elan_stats_get_block_info statidx %d\n",statidx);
++
++      if ((target = elan_stats_find(statidx)) != NULL)
++      {
++              ELAN_DEBUG2(ELAN_DBG_CTRL, "elan_stats_get_block_info name %s entries %d\n",block_name, *num_entries);
++              
++              copyout( target->block_name,  block_name,  ELAN_STATS_NAME_MAX_LEN);
++              copyout(&target->num_entries, num_entries, sizeof(uint));
++
++              res = ESUCCESS;
++      }
++
++      kmutex_unlock(&elan_mutex);
++      return res;
++}
++
++int 
++elan_stats_get_index_name (ELAN_STATS_IDX statidx, uint index, caddr_t name)
++{
++      ELAN_STATS_STRUCT *target;
++      int                   res=EINVAL;
++
++      kmutex_lock(&elan_mutex);
++
++      ELAN_DEBUG2(ELAN_DBG_CTRL, "elan_stats_get_index_name statidx %d index %d\n",statidx, index);
++
++      if ((target = elan_stats_find(statidx)) != NULL)
++      {
++              if ( target->ops->elan_stats_get_name== NULL) 
++              {
++                      ELAN_DEBUG0(ELAN_DBG_CTRL, "elan_stats_get_index_name no callback\n");  
++                      kmutex_unlock(&elan_mutex);
++                      return  res;
++              }
++
++              if ((res = target->ops->elan_stats_get_name(target->arg, index, name)) == 0)
++                      ELAN_DEBUG1(ELAN_DBG_CTRL, "elan_stats_get_index_name name %s\n",name); 
++
++      }
++      kmutex_unlock(&elan_mutex);
++      return  res;
++}
++
++int 
++elan_stats_get_block (ELAN_STATS_IDX statidx, uint entries, ulong *values)
++{
++      ELAN_STATS_STRUCT *target;
++      int                   res=EINVAL;
++
++      kmutex_lock(&elan_mutex);
++
++      
++      if ((target = elan_stats_find(statidx)) != NULL)
++      {
++              if ( target->ops->elan_stats_get_block == NULL) 
++              {
++                      kmutex_unlock(&elan_mutex);
++                      return  res;
++              }
++
++              res = target->ops->elan_stats_get_block(target->arg, entries, values);
++      }
++
++      kmutex_unlock(&elan_mutex);
++      return  res;
++}
++
++int 
++elan_stats_clear_block (ELAN_STATS_IDX statidx)
++{
++      ELAN_STATS_STRUCT *target;
++      int                   res=EINVAL;
++
++      kmutex_lock(&elan_mutex);
++
++      if ((target = elan_stats_find(statidx)) != NULL)
++      {
++              if ( target->ops->elan_stats_clear_block == NULL) 
++              {
++                      kmutex_unlock(&elan_mutex);
++                      return  res;
++              }
++      
++              res = target->ops->elan_stats_clear_block(target->arg);
++      }
++      kmutex_unlock(&elan_mutex);
++      return  res;
++}
++
++void
++elan_stats_next_statidx(void)
++{
++      /* XXXXX need to put not in use check here incase we loop MRH */
++      /* tho its a bigish loop :)                                   */
++      elan_next_statidx++;
++      if (!elan_next_statidx)
++              elan_next_statidx++;
++}
++
++int 
++elan_stats_register (ELAN_STATS_IDX    *statidx, 
++                      char              *block_name, 
++                      uint               num_entries,
++                      ELAN_STATS_OPS *ops,
++                      void              *arg)
++{
++      ELAN_STATS_STRUCT *target;
++
++      kmutex_lock(&elan_mutex);
++
++      /* create it and add */
++      KMEM_ALLOC(target, ELAN_STATS_STRUCT *, sizeof(ELAN_STATS_STRUCT), 1);
++      if (target == NULL)
++      {
++              kmutex_unlock(&elan_mutex);
++              return  ENOMEM;
++      }
++
++      elan_stats_next_statidx();
++
++      *statidx = elan_next_statidx;
++
++      target->statidx     = elan_next_statidx;
++      target->num_entries = num_entries;
++      target->ops         = ops;
++      target->arg         = arg;
++      strcpy(target->block_name, block_name);
++      
++      list_add_tail(&target->node, &elan_stats_list);
++
++      kmutex_unlock(&elan_mutex);
++      return  0;
++}
++
++int
++elan_stats_deregister (ELAN_STATS_IDX statidx)
++{
++      ELAN_STATS_STRUCT *target;
++
++      kmutex_lock(&elan_mutex);
++      if ((target = elan_stats_find(statidx)) != NULL)
++      {
++
++              list_del(&target->node);
++              
++              /* delete target entry */
++              KMEM_FREE(target, sizeof(ELAN_STATS_STRUCT));
++      }
++      kmutex_unlock(&elan_mutex);
++
++      return  target == NULL ? EINVAL : 0;
++}
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan3/context.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan3/context.c 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan3/context.c      2005-05-11 12:10:12.384940784 -0400
+@@ -0,0 +1,2101 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: context.c,v 1.116.2.1 2004/11/12 14:24:18 mike Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/os/context.c,v $ */
++
++#include <qsnet/kernel.h>
++#include <qsnet/autoconf.h>
++#include <elan/elanmod.h>
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elanvp.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanctxt.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elandebug.h>
++#include <elan3/urom_addrs.h>
++#include <elan3/thread.h>
++#include <elan3/vmseg.h>
++#include <elan3/elan3ops.h>
++#include <elan3/elansyscall.h>
++/*
++ * Global variables configurable from /etc/system file
++ *     (OR /etc/sysconfigtab on Digital UNIX)
++ */
++int ntrapped_threads   = 64;
++int ntrapped_dmas      = 64;
++int ntrapped_events    = E3_NonSysCntxQueueSize + 128;
++int ntrapped_commands  = 64;
++int noverflow_commands = 1024;
++int nswapped_threads   = 64;
++int nswapped_dmas      = 64;
++
++#define NUM_HALTOPS   8
++
++void *SwapListsLockInfo;
++void *CmdLockInfo;
++
++static void HaltSwapContext (ELAN3_DEV *dev, void *arg);
++
++static char *OthersStateStrings[]  = {"others_running", "others_halting", "others_swapping", 
++                                    "others_halting_more", "others_swapping_more", "others_swapped"};
++
++ELAN3_CTXT *
++elan3_alloc (ELAN3_DEV *dev, int  kernel)
++{
++    ELAN3_CTXT    *ctxt;
++    int           i;
++    unsigned long flags;
++
++    PRINTF1 (DBG_DEVICE, DBG_FN, "elan3_alloc: %s\n", kernel ? "kernel" : "user");
++
++    KMEM_ZALLOC (ctxt, ELAN3_CTXT *, sizeof (ELAN3_CTXT), TRUE);
++    
++    if (ctxt == NULL)
++      return (NULL);
++
++    elan_nullcap (&ctxt->Capability);
++
++    ctxt->Device      = dev;
++    ctxt->OthersState = CTXT_OTHERS_SWAPPED;
++    ctxt->RefCnt      = 1;
++    ctxt->Position    = dev->Position;
++
++    if (kernel)
++      ctxt->Status = CTXT_DETACHED | CTXT_SWAPPED_OUT | CTXT_KERNEL;
++    else
++      ctxt->Status = CTXT_DETACHED | CTXT_SWAPPED_OUT | CTXT_NO_LWPS;
++
++    ctxt->Elan3mmu = elan3mmu_alloc (ctxt);
++
++    kcondvar_init (&ctxt->Wait);
++    kcondvar_init (&ctxt->CommandPortWait);
++    kcondvar_init (&ctxt->LwpWait);
++    kcondvar_init (&ctxt->HaltWait);
++
++    spin_lock_init (&ctxt->InputFaultLock);
++
++    kmutex_init (&ctxt->SwapListsLock);
++    kmutex_init (&ctxt->CmdPortLock);
++    kmutex_init (&ctxt->NetworkErrorLock);
++    kmutex_init (&ctxt->CmdLock);
++
++    krwlock_init (&ctxt->VpLock);
++
++    KMEM_GETPAGES (ctxt->FlagPage, ELAN3_FLAGSTATS *, 1, TRUE);
++    if (!ctxt->FlagPage)
++      goto error;
++    bzero ((char *) ctxt->FlagPage, PAGESIZE);
++
++    KMEM_ZALLOC (ctxt->CommandTraps, COMMAND_TRAP *,    sizeof (COMMAND_TRAP)    * ntrapped_commands, TRUE);
++    if (!ctxt->CommandTraps)
++      goto error;
++
++    KMEM_ZALLOC (ctxt->ThreadTraps,  THREAD_TRAP *,     sizeof (THREAD_TRAP)     * ntrapped_threads,  TRUE);
++    if (!ctxt->ThreadTraps)
++      goto error;
++
++    KMEM_ZALLOC (ctxt->DmaTraps,     DMA_TRAP *,        sizeof (DMA_TRAP)        * ntrapped_dmas,     TRUE);
++    if (!ctxt->DmaTraps)
++      goto error;
++
++    KMEM_ZALLOC (ctxt->EventCookies, EVENT_COOKIE *,    sizeof (EVENT_COOKIE)    * ntrapped_events,   TRUE);
++    if (!ctxt->EventCookies)
++      goto error;
++
++    KMEM_ZALLOC (ctxt->Commands,     CProcTrapBuf_BE *, sizeof (CProcTrapBuf_BE) * noverflow_commands,TRUE);
++    if (!ctxt->Commands)
++      goto error;
++
++    KMEM_ZALLOC (ctxt->SwapThreads,  E3_Addr *,         sizeof (E3_Addr)         * nswapped_threads,  TRUE);
++    if (!ctxt->SwapThreads)
++      goto error;
++
++    KMEM_ZALLOC (ctxt->SwapDmas,     E3_DMA_BE *,       sizeof (E3_DMA_BE)       * nswapped_dmas,     TRUE);
++    if (!ctxt->SwapDmas)
++      goto error;
++
++    /*
++     * "slop" is defined as follows :
++     *     number of entries REQUIRED to be left spare to consume all other traps
++     *     up until the time that the context can be swapped out.
++     *  
++     * CommandTrapQ : 1 command issued by main + 1 issued by the thread processor per elan
++     * ThreadTrapQ  : 2 from command + 2 input
++     * DmaTrapQ     : 2 from command + 2 input
++     * EventTrapQ   : 2 from command + 1 thread + 1 dma + 2 input + E3_NonSysCntxQueueSize
++     */
++    spin_lock_irqsave (&dev->IntrLock, flags);
++    ELAN3_QUEUE_INIT (ctxt->CommandTrapQ, ntrapped_commands,  2);
++    ELAN3_QUEUE_INIT (ctxt->ThreadTrapQ,  ntrapped_threads,   4);
++    ELAN3_QUEUE_INIT (ctxt->DmaTrapQ,     ntrapped_dmas,      4);
++    ELAN3_QUEUE_INIT (ctxt->EventCookieQ, ntrapped_events,    MIN(E3_NonSysCntxQueueSize + 6, ntrapped_events - 6));
++    ELAN3_QUEUE_INIT (ctxt->CommandQ,     noverflow_commands, 0);
++    ELAN3_QUEUE_INIT (ctxt->SwapThreadQ,  nswapped_threads,   0);
++    ELAN3_QUEUE_INIT (ctxt->SwapDmaQ,     nswapped_dmas,      0);
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++
++#if defined(DIGITAL_UNIX)
++    /* Allocate the segelan for the command port */
++    if (! kernel && elan3_segelan3_create (ctxt) == NULL)
++    {
++      elan3_detach(ctxt);
++      elan3_free (ctxt);
++      return ((ELAN3_CTXT *) NULL);
++    }
++#endif
++
++    /*
++     * Initialise the Input Fault list 
++     */
++    spin_lock (&ctxt->InputFaultLock);
++    for (i = 0; i < NUM_INPUT_FAULT_SAVE; i++)
++      ctxt->InputFaults[i].Next = (i == (NUM_INPUT_FAULT_SAVE-1)) ? NULL : &ctxt->InputFaults[i+1];
++    ctxt->InputFaultList = &ctxt->InputFaults[0];
++    spin_unlock (&ctxt->InputFaultLock);
++
++    ReserveHaltOperations (dev, NUM_HALTOPS, TRUE);
++    
++    if ((ctxt->RouteTable = AllocateRouteTable (ctxt->Device, ELAN3_MAX_VPS)) == NULL)
++    {
++      PRINTF0 (DBG_DEVICE, DBG_FN, "elan3_alloc: cannot map route table\n");
++      elan3_detach(ctxt);
++      elan3_free (ctxt);
++      return ((ELAN3_CTXT *) NULL);
++    }  
++
++    return (ctxt);
++
++
++ error:
++
++    elan3_detach(ctxt);
++    elan3_free (ctxt);
++    if (ctxt->FlagPage)
++      KMEM_FREEPAGES ((void *) ctxt->FlagPage, 1);
++    if (ctxt->CommandTraps)
++      KMEM_FREE ((void *) ctxt->CommandTraps, sizeof (COMMAND_TRAP)    * ntrapped_commands);
++    if (ctxt->ThreadTraps)
++      KMEM_FREE ((void *) ctxt->ThreadTraps,  sizeof (THREAD_TRAP)     * ntrapped_threads);
++    if (ctxt->DmaTraps)
++      KMEM_FREE ((void *) ctxt->DmaTraps,     sizeof (DMA_TRAP)        * ntrapped_dmas);
++    if (ctxt->EventCookies)
++      KMEM_FREE ((void *) ctxt->EventCookies, sizeof (EVENT_COOKIE)    * ntrapped_events);
++    if (ctxt->Commands)
++      KMEM_FREE ((void *) ctxt->Commands,     sizeof (CProcTrapBuf_BE) * noverflow_commands);
++    if (ctxt->SwapThreads)
++      KMEM_FREE ((void *) ctxt->SwapThreads,  sizeof (E3_Addr)         * nswapped_threads);
++    if (ctxt->SwapDmas)
++      KMEM_FREE ((void *) ctxt->SwapDmas,     sizeof (E3_DMA_BE)       * nswapped_dmas);
++
++    kcondvar_destroy (&ctxt->Wait);
++    kcondvar_destroy (&ctxt->CommandPortWait);
++    kcondvar_destroy (&ctxt->LwpWait);
++    kcondvar_destroy (&ctxt->HaltWait);
++
++    kmutex_destroy (&ctxt->SwapListsLock);
++    kmutex_destroy (&ctxt->CmdLock);
++    kmutex_destroy (&ctxt->NetworkErrorLock);
++    spin_lock_destroy  (&ctxt->InputFaultLock);
++
++    krwlock_destroy (&ctxt->VpLock);
++
++    KMEM_FREE (ctxt, sizeof (ELAN3_CTXT));
++
++    return (NULL);
++}
++
++void
++elan3_free (ELAN3_CTXT *ctxt)
++{
++    ELAN3_DEV     *dev = ctxt->Device;
++    NETERR_FIXUP *nef;
++    
++    PRINTF1 (ctxt, DBG_FN, "elan3_free: %p \n", ctxt);
++   
++    elan3_removevp (ctxt, ELAN3_INVALID_PROCESS);                     /* Remove any virtual process mappings */
++
++#if defined(DIGITAL_UNIX)
++    WaitForContext (ctxt);                                    /* wait for all references to this context to go away */
++#endif
++
++    if (ctxt->RouteTable)
++      FreeRouteTable (dev, ctxt->RouteTable);
++    ctxt->RouteTable = NULL;
++
++    elan3mmu_free (ctxt->Elan3mmu);                           /* free of our Elan3mmu  */
++
++    if (ctxt->Private)                                                /* Call back to "user" to free off  */
++      ELAN3_OP_FREE_PRIVATE (ctxt);                           /* private data */
++
++#if defined(DIGITAL_UNIX)
++    if (! CTXT_IS_KERNEL(ctxt))
++      elan3_segelan3_destroy (ctxt);                          /* Unmap the command port from the users address space. */
++#endif
++   
++    ReleaseHaltOperations (dev, NUM_HALTOPS);
++
++    if (ctxt->Input0Resolver)
++      CancelNetworkErrorResolver (ctxt->Input0Resolver);
++
++    if (ctxt->Input1Resolver)
++      CancelNetworkErrorResolver (ctxt->Input1Resolver);
++
++    while ((nef = ctxt->NetworkErrorFixups) != NULL)
++    {
++      ctxt->NetworkErrorFixups = nef->Next;
++
++      CompleteNetworkErrorFixup (ctxt, nef, ESRCH);
++    }
++
++    KMEM_FREEPAGES ((void *) ctxt->FlagPage, 1);
++
++    KMEM_FREE ((void *) ctxt->CommandTraps, sizeof (COMMAND_TRAP)    * ntrapped_commands);
++    KMEM_FREE ((void *) ctxt->ThreadTraps,  sizeof (THREAD_TRAP)     * ntrapped_threads);
++    KMEM_FREE ((void *) ctxt->DmaTraps,     sizeof (DMA_TRAP)        * ntrapped_dmas);
++    KMEM_FREE ((void *) ctxt->EventCookies, sizeof (EVENT_COOKIE)    * ntrapped_events);
++    KMEM_FREE ((void *) ctxt->Commands,     sizeof (CProcTrapBuf_BE) * noverflow_commands);
++    KMEM_FREE ((void *) ctxt->SwapThreads,  sizeof (E3_Addr)         * nswapped_threads);
++    KMEM_FREE ((void *) ctxt->SwapDmas,     sizeof (E3_DMA_BE)       * nswapped_dmas);
++
++    kcondvar_destroy (&ctxt->Wait);
++    kcondvar_destroy (&ctxt->CommandPortWait);
++    kcondvar_destroy (&ctxt->LwpWait);
++    kcondvar_destroy (&ctxt->HaltWait);
++
++    kmutex_destroy (&ctxt->SwapListsLock);
++    kmutex_destroy (&ctxt->CmdLock);
++    kmutex_destroy (&ctxt->NetworkErrorLock);
++    spin_lock_destroy  (&ctxt->InputFaultLock);
++
++    krwlock_destroy (&ctxt->VpLock);
++
++    KMEM_FREE (ctxt, sizeof (ELAN3_CTXT));
++}
++
++int 
++elan3_doattach(ELAN3_CTXT *ctxt, ELAN_CAPABILITY *cap)
++{
++    unsigned long pgnum = ((cap->cap_mycontext & MAX_ROOT_CONTEXT_MASK) * sizeof (E3_CommandPort)) / PAGE_SIZE;
++    unsigned long pgoff = ((cap->cap_mycontext & MAX_ROOT_CONTEXT_MASK) * sizeof (E3_CommandPort)) & (PAGE_SIZE-1);
++    ELAN3_DEV     *dev   = ctxt->Device;
++    int           res   = ESUCCESS;
++    unsigned long flags;
++
++    /* Map in the command port for this context */
++    if (MapDeviceRegister (dev, ELAN3_BAR_COMMAND_PORT, &ctxt->CommandPage, pgnum * PAGE_SIZE, PAGE_SIZE, &ctxt->CommandPageHandle) != ESUCCESS)
++    {
++      PRINTF0 (ctxt, DBG_FN, "elan3_doattach: MapDeviceRegister failed");
++      return (EINVAL);
++    }
++
++    ctxt->CommandPort = ctxt->CommandPage + pgoff;
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++
++    res = 0;
++    if (ELAN3_DEV_CTX_TABLE(dev,cap->cap_mycontext) != NULL)
++      res = EBUSY;
++    else
++    {
++      if ((res = elan3mmu_attach (ctxt->Device, cap->cap_mycontext, ctxt->Elan3mmu, 
++                                  ctxt->RouteTable->Table, ctxt->RouteTable->Size-1)) == 0)
++      {
++          ELAN3_DEV_CTX_TABLE(dev,cap->cap_mycontext) = ctxt;
++          ctxt->Capability                            = *cap;
++      }
++    }
++
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++
++    if (res == ESUCCESS)
++      elan3_swapin (ctxt, CTXT_DETACHED);
++    else 
++    {
++      UnmapDeviceRegister (dev, &ctxt->CommandPageHandle);
++      ctxt->CommandPage = (ioaddr_t) 0; 
++      ctxt->CommandPort = (ioaddr_t) 0;
++    }
++
++    return (res);
++}
++
++void
++elan3_destroy_callback( void * args, ELAN_CAPABILITY *cap, ELAN_CAPABILITY *map)
++{
++    if (map == NULL) 
++    {
++      /* the cap is being destroyed */
++      PRINTF0 (NULL, DBG_VP, "elan3_destroy_callback: the cap is being destroyed \n");
++    }
++    else
++    {
++      /* the map is being destroyed */
++      PRINTF0 (NULL, DBG_VP, "elan3_destroy_callback: the map is being destroyed \n");
++    }
++}
++
++int
++elan3_attach (ELAN3_CTXT *ctxt, ELAN_CAPABILITY *cap)
++{
++    ELAN3_DEV *dev = ctxt->Device;
++    int type;
++    int res;
++
++    switch (type = elan3_validate_cap (dev, cap, ELAN_USER_ATTACH))
++    {
++    case ELAN_CAP_OK:
++      /* nothing */
++      break;
++
++    case ELAN_CAP_RMS:
++      if ((res = elan_attach_cap(cap, dev->Devinfo.dev_rail, ctxt, elan3_destroy_callback)) != 0)
++          return res;
++      break;
++
++    default:
++      return (EINVAL);
++    }
++
++    if (((res = elan3_doattach(ctxt,cap)) != ESUCCESS) && (type == ELAN_CAP_RMS))
++      elan_detach_cap(cap, dev->Devinfo.dev_rail);
++
++    return res;
++}
++
++void
++elan3_detach ( ELAN3_CTXT *ctxt )
++{
++    ELAN3_DEV   *dev                 = ctxt->Device;
++    int need_to_call_elanmod_detach = 0;
++    unsigned long flags;
++
++    PRINTF1 (ctxt, DBG_FN, "elan3_detach: %p \n", ctxt );
++    
++    if (ctxt->Capability.cap_mycontext == ELAN_CAP_UNINITIALISED)
++    {
++      PRINTF0 (ctxt, DBG_FN, "elan3_detach: context not attached \n");
++      return ;
++    }
++
++    /* must you be in the ctx_table ?? */
++    
++    switch (ctxt->Capability.cap_type & ELAN_CAP_TYPE_MASK)
++    {
++    case ELAN_CAP_TYPE_BLOCK:
++    case ELAN_CAP_TYPE_CYCLIC:
++    {
++      if (ELAN3_SYSTEM_CONTEXT (ctxt->Capability.cap_mycontext))
++          return ;
++
++      if (! (ctxt->Capability.cap_type & ELAN_CAP_TYPE_HWTEST))
++          need_to_call_elanmod_detach = 1;
++
++      break;
++    } 
++    default:
++      return ;
++    }
++
++    elan3_swapout (ctxt, CTXT_DETACHED);
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++
++    elan3mmu_detach (dev, ctxt->Capability.cap_mycontext);
++    ELAN3_DEV_CTX_TABLE(dev,ctxt->Capability.cap_mycontext) = NULL;
++
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++
++    if (ctxt->CommandPage)
++    {
++      UnmapDeviceRegister (dev, &ctxt->CommandPageHandle);
++      ctxt->CommandPage = (ioaddr_t) 0;
++    }
++    
++    if (need_to_call_elanmod_detach) 
++      elan_detach_cap(&ctxt->Capability, dev->Devinfo.dev_rail);
++
++    elan_nullcap (&ctxt->Capability);
++
++}
++
++void
++elan3_dodetach ( ELAN3_CTXT *ctxt )
++{
++    ELAN3_DEV     *dev = ctxt->Device;
++    unsigned long flags;
++
++    PRINTF1 (ctxt, DBG_FN, "elan3_dodetach: %p \n", ctxt );
++    
++    if (ctxt->Capability.cap_mycontext == ELAN_CAP_UNINITIALISED)
++    {
++      PRINTF0 (ctxt, DBG_FN, "elan3_dodetach: context not attached \n");
++      return ;
++    }
++
++    elan3_swapout (ctxt, CTXT_DETACHED);
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++
++    elan3mmu_detach (dev, ctxt->Capability.cap_mycontext);
++    ELAN3_DEV_CTX_TABLE(dev,ctxt->Capability.cap_mycontext) = NULL;
++
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++
++    if (ctxt->CommandPage)
++    {
++      UnmapDeviceRegister (dev, &ctxt->CommandPageHandle);
++      ctxt->CommandPage = (ioaddr_t) 0;
++    }
++    
++    elan_nullcap (&ctxt->Capability);
++}
++
++void
++elan3_swapin (ELAN3_CTXT *ctxt, int reason)
++{
++    ELAN3_DEV *dev = ctxt->Device;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++
++    ASSERT (ctxt->Status & CTXT_SWAPPED_REASONS);
++
++    PRINTF3 (ctxt, DBG_SWAP, "elan3_swapin: status %x State %s reason %x\n", 
++           ctxt->Status, OthersStateStrings[ctxt->OthersState], reason);
++
++    while (ctxt->Status & CTXT_SWAPPING_OUT)                  /* In transition */
++      kcondvar_wait (&ctxt->LwpWait, &dev->IntrLock, &flags);
++
++    if (reason == CTXT_NO_LWPS && ctxt->LwpCount++ != 0)      /* Added another LWP */
++    {
++      spin_unlock_irqrestore (&dev->IntrLock, flags);
++      return;
++    }
++
++    if ((ctxt->Status & ~reason) & CTXT_SWAPPED_REASONS)
++      ctxt->Status &= ~reason;
++    else
++    {
++      ASSERT (ctxt->Status & CTXT_SWAPPED_OUT);
++      ASSERT (ctxt->OthersState == CTXT_OTHERS_SWAPPED);
++      
++      /*
++       * Will not be swapped out anymore, so ask the "user" to perform 
++       * any swapping in he needs before letting the context run again.
++       */
++      
++      ctxt->Status &= ~(CTXT_SWAPPED_OUT | CTXT_QUEUES_EMPTY | reason);
++      ctxt->OthersState = CTXT_OTHERS_RUNNING;
++
++      if (ctxt->Input0Trap.State == CTXT_STATE_OK && ctxt->Input1Trap.State == CTXT_STATE_OK)
++          SetInputterStateForContext (ctxt, 0, NULL);
++      
++      kcondvar_wakeupall (&ctxt->Wait, &dev->IntrLock);
++    }
++
++    PRINTF2 (ctxt, DBG_SWAP, "elan3_swapin: all done - status %x state %s\n",
++           ctxt->Status, OthersStateStrings[ctxt->OthersState]);
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++}
++
++
++void
++elan3_swapout (ELAN3_CTXT *ctxt, int reason)
++{
++    ELAN3_DEV     *dev = ctxt->Device;
++    int           cansleep;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++
++    PRINTF3 (ctxt, DBG_SWAP, "elan3_swapout: status %x state %s reason %x\n", 
++           ctxt->Status, OthersStateStrings[ctxt->OthersState], reason);
++
++    if (reason == CTXT_NO_LWPS)
++    {
++      if (--ctxt->LwpCount != 0)                              /* Still other LWPs running */
++      {
++          spin_unlock_irqrestore (&dev->IntrLock, flags);
++          return;
++      }
++
++      kcondvar_wakeupall (&ctxt->LwpWait, &dev->IntrLock);            /* Wakeup anyone waiting on LwpCount */
++    }
++    
++    ctxt->Status |= reason;
++    
++    while (ctxt->Status & CTXT_SWAPPING_OUT)                  /* wait for someone else to finish swapping */
++      kcondvar_wait (&ctxt->LwpWait, &dev->IntrLock, &flags);         /* out */
++
++    if (ctxt->Status & CTXT_SWAPPED_OUT)
++    {
++      if (reason == CTXT_NO_LWPS)                             /* Wakeup other thread waiting on LWP exit */
++          kcondvar_wakeupall (&ctxt->LwpWait, &dev->IntrLock);
++      
++      spin_unlock_irqrestore (&dev->IntrLock, flags);
++      return;
++    }
++    
++    /*
++     * mark the context as swapping out.
++     */
++    ctxt->Status |= CTXT_SWAPPING_OUT;
++    
++    if (reason != CTXT_FIXUP_NETERR)
++    {
++      /*
++       * Stop all of the lwps.
++       */
++      while (ctxt->LwpCount)
++      {
++          kcondvar_wakeupall (&ctxt->Wait, &dev->IntrLock);           /* Wake up any lwps */
++          kcondvar_wait (&ctxt->LwpWait, &dev->IntrLock, &flags);             /* then wait for them to enter elan3_swapout */
++      }
++    }
++    
++    StartSwapoutContext (ctxt, 0, NULL);
++    for (;;)
++    {
++      PRINTF0 (ctxt, DBG_SWAP, "elan3_swapout: HandleExceptions\n");
++
++      cansleep = (HandleExceptions(ctxt, &flags) == ESUCCESS);
++
++      PRINTF2 (ctxt, DBG_SWAP, "elan3_swapout: OthersState=%d cansleep=%d\n", ctxt->OthersState, cansleep);
++
++      if (ctxt->OthersState == CTXT_OTHERS_SWAPPED)
++          break;
++
++      if (cansleep)
++          kcondvar_wait (&ctxt->Wait, &dev->IntrLock, &flags);
++    }
++    PRINTF0 (ctxt, DBG_SWAP, "elan3_swapout: swapped out\n");
++    
++    ASSERT (ELAN3_QUEUE_EMPTY (ctxt->DmaTrapQ));
++    ASSERT (ELAN3_QUEUE_EMPTY (ctxt->ThreadTrapQ));
++
++    ctxt->Status |=  CTXT_SWAPPED_OUT;
++    ctxt->Status &= ~CTXT_SWAPPING_OUT;
++
++    kcondvar_wakeupall (&ctxt->LwpWait, &dev->IntrLock);
++
++    PRINTF2 (ctxt, DBG_SWAP, "elan3_swapout: all done - status %x state %s\n",
++           ctxt->Status, OthersStateStrings[ctxt->OthersState]);
++
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++}
++
++int
++elan3_pagefault (ELAN3_CTXT *ctxt, E3_FaultSave_BE *FaultSave, int npages)
++{
++    E3_Addr     elanAddr = FaultSave->s.FaultAddress;
++    int               writeable;
++    int               res;
++
++    PRINTF3 (ctxt, DBG_FAULT, "elan3_pagefault: elanAddr %08x FSR %08x : %s\n", elanAddr, FaultSave->s.FSR.Status,
++           FaultSave->s.FSR.s.ProtFault ? "protection fault" : "pte invalid");
++    
++    /* Look at the FSR to determine the fault type etc */
++    
++    if (FaultSave->s.FSR.Status == 0)                         /* this is a target abort/parity error, so look */
++    {                                                         /* at the PCI config space registers to determine  */
++      ElanBusError (ctxt->Device);
++      return (EFAULT);                                        
++    }
++    
++    if (FaultSave->s.FSR.s.AlignmentErr)                      /* Alignment errors are always fatal. */
++    {
++      PRINTF0 (ctxt, DBG_FAULT, "elan3_pagefault: Alignment error\n");
++      return (EFAULT);
++    }
++
++    if (FaultSave->s.FSR.s.WalkBadData)                               /* Memory ECC error during a walk */
++    {
++      PRINTF0 (ctxt, DBG_FAULT, "elan3_pagefault: Memory ECC error during walk\n");
++      return (EFAULT);
++    }
++
++    if (!FaultSave->s.FSR.s.ProtFault &&                      /* DMA memory type changed */
++      !FaultSave->s.FSR.s.Walking)
++    {
++      PRINTF0 (ctxt, DBG_FAULT, "elan3_pagefault: DMA memory type changed\n");
++      return (EFAULT);
++    }
++
++    ASSERT (FaultSave->s.FSR.s.ProtFault ?                    /* protection errors, should always have a valid pte */
++          (!FaultSave->s.FSR.s.Walking || !(FaultSave->s.FSR.s.Level==3) ||  FaultSave->s.FSR.s.FaultPte == ELAN3_ET_PTE) : 
++          FaultSave->s.FSR.s.FaultPte == ELAN3_ET_INVALID);   /* otherwise it must be an invalid pte */
++
++    /*
++     * Determine whether to fault for a 'write' from the access permissions we need, and not
++     * from the access type (WrAcc).
++     */
++    writeable = (FaultSave->s.FSR.s.AccTypePerm & (1 << FSR_WritePermBit));
++
++    /* Check that we have the right permissions for this access type. */
++    if ((res = elan3mmu_checkperm (ctxt->Elan3mmu, (elanAddr&PAGEMASK), npages*PAGESIZE, FaultSave->s.FSR.s.AccTypePerm)) != 0)
++    {
++      PRINTF1 (ctxt, DBG_FAULT, "elan3_pagefault: %s\n", (res == ENOMEM) ? "no protection mapping" : "protection error");
++      
++      return (res);
++    }
++
++    res = LoadElanTranslation (ctxt, (elanAddr&PAGEMASK), npages*PAGESIZE, FaultSave->s.FSR.s.ProtFault, writeable);
++
++    if (res == ESUCCESS)
++    {
++      BumpStat (ctxt->Device, PageFaults);
++      BumpUserStat (ctxt, PageFaults);
++    }
++
++    PRINTF1 (ctxt, DBG_FAULT, "elan3_pagefault: -> %d\n", res);
++
++    return (res);
++}
++
++void
++elan3_block_inputter (ELAN3_CTXT *ctxt, int block)
++{
++    ELAN3_DEV *dev = ctxt->Device;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++    
++    if (block)
++      ctxt->Status |= CTXT_USER_FILTERING;
++    else
++      ctxt->Status &= ~CTXT_USER_FILTERING;
++
++    if (ctxt->Capability.cap_mycontext != ELAN_CAP_UNINITIALISED)
++      SetInputterStateForContext (ctxt, 0, NULL);
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++}
++
++int
++FixupNetworkErrors (ELAN3_CTXT *ctxt, unsigned long *flags)
++{
++    ELAN3_DEV          *dev = ctxt->Device;
++    NETERR_FIXUP *nef;
++
++    ASSERT (SPINLOCK_HELD (&dev->IntrLock));
++    
++    if (ctxt->NetworkErrorFixups == NULL)
++      return (ESUCCESS);
++
++    spin_unlock_irqrestore (&dev->IntrLock, *flags);
++    
++    kmutex_lock (&ctxt->NetworkErrorLock);                    /* single thread while fixing up errors */
++    elan3_swapout (ctxt, CTXT_FIXUP_NETERR);
++
++    spin_lock_irqsave (&dev->IntrLock, *flags);
++    while ((nef = ctxt->NetworkErrorFixups) != NULL)
++    {
++      ctxt->NetworkErrorFixups = nef->Next;
++      spin_unlock_irqrestore (&dev->IntrLock, *flags);
++
++      if (ELAN3_OP_FIXUP_NETWORK_ERROR (ctxt, nef) == OP_FAILED)
++          CompleteNetworkErrorFixup (ctxt, nef, EINVAL);
++
++      spin_lock_irqsave (&dev->IntrLock, *flags);
++    }
++    spin_unlock_irqrestore (&dev->IntrLock, *flags);
++
++    elan3_swapin (ctxt, CTXT_FIXUP_NETERR);
++
++    kmutex_unlock (&ctxt->NetworkErrorLock);
++    spin_lock_irqsave (&dev->IntrLock, *flags);
++    return (EAGAIN);
++}
++
++int
++CompleteNetworkErrorResolver (ELAN3_CTXT *ctxt, INPUT_TRAP *trap, NETERR_RESOLVER *rvp)
++{
++    int state;
++
++    switch (rvp->Status)
++    {
++    case ESUCCESS:
++      /*
++       * the item still existed at the source - if it's a wait for EOP transaction
++       * then the source will retry - otherwise the remote event will have been
++       * cleared and we should execute it
++       */
++      PRINTF1 (ctxt, DBG_NETERR, "CompleteNetworkErrorResolver: ESUCCESS zero WaitForEopTransaction %p\n", trap->WaitForEopTransaction);
++
++      state = trap->WaitForEopTransaction ? CTXT_STATE_OK : CTXT_STATE_NEEDS_RESTART;
++
++      break;
++
++    case ESRCH:       
++      /*
++       * the item was not found at the source - we should always execute the transaction
++       * since it will never be resent
++       */
++      PRINTF1 (ctxt, DBG_NETERR, "CompleteNetworkErrorResolver: ESRCH execute WaitForEopTransaction %p\n", trap->WaitForEopTransaction);
++      state = CTXT_STATE_NEEDS_RESTART;
++      break;
++
++    default:                                                  /* other errors */
++      PRINTF1 (ctxt, DBG_NETERR, "CompleteNetworkErrorResolver: %d\n", rvp->Status);
++      if (ElanException (ctxt, EXCEPTION_NETWORK_ERROR, INPUT_PROC, trap, &rvp) == OP_HANDLED)
++          state = CTXT_STATE_NEEDS_RESTART;
++      else
++          state = CTXT_STATE_OK;
++      break;
++    }
++
++    FreeNetworkErrorResolver (rvp);
++
++    return (state);
++}
++
++int
++HandleExceptions (ELAN3_CTXT *ctxt, unsigned long *flags)
++{
++    ELAN3_DEV        *dev    = ctxt->Device;
++    THREAD_TRAP      tproc;
++    DMA_TRAP         dproc;
++    NETERR_RESOLVER *rvp;
++    int                    state;
++
++    if (ctxt->Status & CTXT_COMMAND_OVERFLOW_ERROR)
++    {
++      ctxt->Status &= ~CTXT_COMMAND_OVERFLOW_ERROR;
++      spin_unlock_irqrestore (&dev->IntrLock, *flags);
++      ElanException (ctxt, EXCEPTION_COMMAND_OVERFLOW, COMMAND_PROC, NULL);
++      spin_lock_irqsave (&dev->IntrLock, *flags);
++      return (EAGAIN);
++    }
++    
++    if (! ELAN3_QUEUE_BACK_EMPTY (ctxt->CommandTrapQ))
++    {
++      /* XXXX: unmap translations to the command port */
++
++      spin_unlock_irqrestore (&dev->IntrLock, *flags);
++      ResolveCProcTrap (ctxt);
++      spin_lock_irqsave (&dev->IntrLock, *flags);
++      return (EAGAIN);
++    }
++    
++    if (ctxt->Input0Trap.State == CTXT_STATE_TRAPPED)
++    {
++      ctxt->Input0Trap.State = CTXT_STATE_RESOLVING;
++
++      spin_unlock_irqrestore (&dev->IntrLock, *flags);
++      ResolveIProcTrap (ctxt, &ctxt->Input0Trap, &ctxt->Input0Resolver);
++      spin_lock_irqsave (&dev->IntrLock, *flags);
++      return (EAGAIN);
++    }
++
++    if (ctxt->Input1Trap.State == CTXT_STATE_TRAPPED)
++    {
++      ctxt->Input1Trap.State = CTXT_STATE_RESOLVING;
++
++      spin_unlock_irqrestore (&dev->IntrLock, *flags);
++      ResolveIProcTrap (ctxt, &ctxt->Input1Trap, &ctxt->Input1Resolver);
++      spin_lock_irqsave (&dev->IntrLock, *flags);
++      return (EAGAIN);
++    }
++
++    if ((rvp = ctxt->Input0Resolver) != NULL && rvp->Completed)
++    {
++      ASSERT (ctxt->Input0Trap.State == CTXT_STATE_NETWORK_ERROR);
++
++      ctxt->Input0Resolver = NULL;
++      
++      spin_unlock_irqrestore (&dev->IntrLock, *flags);
++      state = CompleteNetworkErrorResolver (ctxt, &ctxt->Input0Trap, rvp);
++      spin_lock_irqsave (&dev->IntrLock, *flags);
++      ctxt->Input0Trap.State = state;
++      return (EAGAIN);
++    }
++
++    if ((rvp = ctxt->Input1Resolver) != NULL && rvp->Completed)
++    {
++      ASSERT (ctxt->Input1Trap.State == CTXT_STATE_NETWORK_ERROR);
++
++      ctxt->Input1Resolver = NULL;
++      
++      spin_unlock_irqrestore (&dev->IntrLock, *flags);
++      state = CompleteNetworkErrorResolver (ctxt,&ctxt->Input1Trap, rvp);
++      spin_lock_irqsave (&dev->IntrLock, *flags);
++      ctxt->Input1Trap.State = state;
++      return (EAGAIN);
++    }
++
++    if (NextTProcTrap (ctxt, &tproc))
++    {
++      spin_unlock_irqrestore (&dev->IntrLock, *flags);
++      ResolveTProcTrap (ctxt, &tproc);
++      spin_lock_irqsave (&dev->IntrLock, *flags);
++      return (EAGAIN);
++    }
++    ctxt->Status &= ~CTXT_THREAD_QUEUE_FULL;
++
++    if (NextDProcTrap (ctxt, &dproc))
++    {
++      spin_unlock_irqrestore (&dev->IntrLock, *flags);
++      ResolveDProcTrap (ctxt, &dproc);
++      spin_lock_irqsave (&dev->IntrLock, *flags);
++      return (EAGAIN);
++    }
++    ctxt->Status &= ~CTXT_DMA_QUEUE_FULL;
++
++    /* Handle all event interrupts. */
++    if (! ELAN3_QUEUE_EMPTY (ctxt->EventCookieQ))
++    {
++      while (! ELAN3_QUEUE_EMPTY (ctxt->EventCookieQ))
++      {
++          E3_uint32 cookie = *ELAN3_QUEUE_FRONT (ctxt->EventCookieQ, ctxt->EventCookies);
++
++          ELAN3_QUEUE_REMOVE (ctxt->EventCookieQ);
++
++          spin_unlock_irqrestore (&dev->IntrLock, *flags);
++          if (ELAN3_OP_EVENT (ctxt, cookie, OP_LWP) != OP_DEFER)
++              spin_lock_irqsave (&dev->IntrLock, *flags);
++          else
++          {
++              spin_lock_irqsave (&dev->IntrLock, *flags);     /* place the cookie back on the queue. */
++                                                              /* note we place it on the front to ensure  */
++              ELAN3_QUEUE_ADD_FRONT (ctxt->EventCookieQ);     /* event ordering. */
++              *ELAN3_QUEUE_FRONT (ctxt->EventCookieQ, ctxt->EventCookies) = cookie;
++          }
++      }
++      return (EAGAIN);
++    }
++    ctxt->Status &= ~CTXT_EVENT_QUEUE_FULL;
++
++    if (! ELAN3_QUEUE_EMPTY (ctxt->SwapDmaQ))
++    {
++      while (! ELAN3_QUEUE_EMPTY (ctxt->SwapDmaQ))
++      {
++          E3_DMA_BE DmaDesc = *ELAN3_QUEUE_FRONT (ctxt->SwapDmaQ, ctxt->SwapDmas);
++
++          ELAN3_QUEUE_REMOVE (ctxt->SwapDmaQ);
++
++          spin_unlock_irqrestore (&dev->IntrLock, *flags);
++          RestartDmaDesc (ctxt, &DmaDesc);
++          spin_lock_irqsave (&dev->IntrLock, *flags);
++      }
++      return (EAGAIN);
++    }
++    
++    if (! ELAN3_QUEUE_EMPTY (ctxt->SwapThreadQ))
++    {
++      while (! ELAN3_QUEUE_EMPTY (ctxt->SwapThreadQ))
++      {
++          E3_Addr StackPointer = *ELAN3_QUEUE_FRONT (ctxt->SwapThreadQ, ctxt->SwapThreads);
++
++          ELAN3_QUEUE_REMOVE (ctxt->SwapThreadQ);
++
++          spin_unlock_irqrestore (&dev->IntrLock, *flags);
++          ReissueStackPointer (ctxt, StackPointer);
++          spin_lock_irqsave (&dev->IntrLock, *flags);
++      }
++      return (EAGAIN);
++    }
++    
++    switch (ctxt->OthersState)
++    {
++    case CTXT_OTHERS_SWAPPING:
++      if (! (ctxt->Status & CTXT_OTHERS_REASONS))
++          ctxt->OthersState = CTXT_OTHERS_RUNNING;
++      else
++          ctxt->OthersState = CTXT_OTHERS_SWAPPED;
++
++      PRINTF1 (ctxt, DBG_LWP, "HandleExceptions: OthersState : swapping -> %s\n", OthersStateStrings[ctxt->OthersState]);
++          
++      break;
++
++    case CTXT_OTHERS_SWAPPING_MORE:
++      ctxt->OthersState = CTXT_OTHERS_HALTING_MORE;
++      QueueHaltOperation (dev, 0, NULL, INT_DProcHalted | INT_TProcHalted, HaltSwapContext, ctxt);
++
++      PRINTF1 (ctxt, DBG_LWP, "HandleExceptions: OthersState : swapping_more -> %s\n", OthersStateStrings[ctxt->OthersState]);
++      break;
++    }
++    return (ESUCCESS);
++}
++
++int
++RestartContext (ELAN3_CTXT *ctxt, unsigned long *flags)
++{
++    ELAN3_DEV *dev = ctxt->Device;
++    int       res;
++
++    ASSERT (SPINLOCK_HELD (&dev->IntrLock));
++
++    PRINTF1 (ctxt, DBG_LWP, "RestartContext: status %x\n", ctxt->Status);
++
++    if (! (ctxt->Status & CTXT_OTHERS_REASONS))
++    {
++      if (! ELAN3_QUEUE_FRONT_EMPTY (ctxt->CommandTrapQ) || ! ELAN3_QUEUE_EMPTY(ctxt->CommandQ))
++      {
++          spin_unlock_irqrestore (&dev->IntrLock, *flags);
++          RestartCProcTrap (ctxt);
++          spin_lock_irqsave (&dev->IntrLock, *flags);
++          return (EAGAIN);
++      }
++
++      if (ctxt->Input0Trap.State == CTXT_STATE_NEEDS_RESTART)
++      {
++          ctxt->Input0Trap.State = CTXT_STATE_EXECUTING;
++
++          spin_unlock_irqrestore (&dev->IntrLock, *flags);
++          res = RestartIProcTrap (ctxt, &ctxt->Input0Trap);
++          spin_lock_irqsave (&dev->IntrLock, *flags);
++          
++          if (res == ESUCCESS)
++              ctxt->Input0Trap.State = CTXT_STATE_OK;
++          else
++              ctxt->Input0Trap.State = CTXT_STATE_NEEDS_RESTART;
++          return (EAGAIN);
++      }
++
++      if (ctxt->Input1Trap.State == CTXT_STATE_NEEDS_RESTART)
++      {
++          ctxt->Input1Trap.State = CTXT_STATE_EXECUTING;
++
++          spin_unlock_irqrestore (&dev->IntrLock, *flags);
++          res = RestartIProcTrap (ctxt, &ctxt->Input1Trap);
++          spin_lock_irqsave (&dev->IntrLock, *flags);
++
++          if (res == ESUCCESS)
++              ctxt->Input1Trap.State = CTXT_STATE_OK;
++          else
++              ctxt->Input1Trap.State = CTXT_STATE_NEEDS_RESTART;
++          return (EAGAIN);
++      }
++
++      if (SetEventsNeedRestart (ctxt))
++      {
++          spin_unlock_irqrestore (&dev->IntrLock, *flags);
++          RestartSetEvents (ctxt);
++          spin_lock_irqsave (&dev->IntrLock, *flags);
++          return (EAGAIN);
++      }
++
++      SetInputterStateForContext (ctxt, 0, NULL);
++
++      if (TProcNeedsRestart (ctxt))
++      {
++          spin_unlock_irqrestore (&dev->IntrLock, *flags);
++
++          LoadCommandPortTranslation (ctxt);
++          RestartTProcItems (ctxt);
++          spin_lock_irqsave (&dev->IntrLock, *flags);
++          return (EAGAIN);
++      }
++
++      if (DProcNeedsRestart (ctxt))
++      {
++          spin_unlock_irqrestore (&dev->IntrLock, *flags);
++          RestartDProcItems (ctxt);
++          spin_lock_irqsave (&dev->IntrLock, *flags);
++          return (EAGAIN);
++      }
++
++      if (ELAN3_QUEUE_EMPTY (ctxt->CommandTrapQ))
++      {
++          PRINTF1 (ctxt, DBG_LWP, "RestartContext: setting Command Flag at %p to 0\n", &ctxt->FlagPage->CommandFlag);
++
++          ctxt->FlagPage->CommandFlag = 0;
++
++          if (ctxt->Status & CTXT_WAITING_COMMAND)
++          {
++              PRINTF0 (ctxt, DBG_LWP, "RestartContext: waking up threads waiting for commandport\n");
++              
++              ctxt->Status &= ~CTXT_WAITING_COMMAND;
++              
++              kcondvar_wakeupall (&ctxt->CommandPortWait, &dev->IntrLock);
++          }
++      }
++    }
++
++    return (ESUCCESS);
++}
++
++static void
++HaltSwapContext (ELAN3_DEV *dev, void *arg)
++{
++    ELAN3_CTXT        *ctxt    = (ELAN3_CTXT *) arg;
++    int                     SysCntx = (ctxt->Capability.cap_mycontext & SYS_CONTEXT_BIT);
++    E3_ThreadQueue_BE thread;
++    E3_DMA_BE         dma;
++    sdramaddr_t       FPtr, BPtr;
++    sdramaddr_t             Base, Top;
++    u_int          *runCount;
++    unsigned long     flags;
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++
++    ASSERT (ctxt->OthersState == CTXT_OTHERS_HALTING || ctxt->OthersState == CTXT_OTHERS_HALTING_MORE);
++
++    PRINTF2 (ctxt, DBG_SWAP, "HaltSwapContext: status %x state %s\n", ctxt->Status, OthersStateStrings[ctxt->OthersState]);
++
++    if (! (ctxt->Status & CTXT_OTHERS_REASONS))
++    {
++      if (ctxt->OthersState == CTXT_OTHERS_HALTING_MORE)
++      {
++          runCount = SysCntx ? &dev->HaltAllCount : &dev->HaltNonContext0Count;
++
++          if (--(*runCount) == 0)
++              SetSchedStatusRegister (dev, 0, NULL);
++      }
++      ctxt->OthersState = CTXT_OTHERS_RUNNING;
++      
++      PRINTF0 (ctxt, DBG_SWAP, "HaltSwapContext: no more reason to swap -> others_running\n");
++
++      kcondvar_wakeupall (&ctxt->Wait, &dev->IntrLock);
++      spin_unlock_irqrestore (&dev->IntrLock, flags);
++      return;
++    }
++
++    /*
++     * Capture all other processors since we're not being responsive to 
++     * the command processor interrupt.
++     */
++    CAPTURE_CPUS();
++
++    if (SysCntx)
++    {
++      FPtr = read_reg32 (dev, TProc_SysCntx_FPtr);
++      BPtr = read_reg32 (dev, TProc_SysCntx_BPtr);
++      Base = dev->TAndQBase + offsetof (E3_TrapAndQueue, SysCntxThreadQueue[0]);
++      Top  = dev->TAndQBase + offsetof (E3_TrapAndQueue, SysCntxThreadQueue[E3_SysCntxQueueSize-1]);
++    }
++    else
++    {
++      FPtr  = read_reg32 (dev, TProc_NonSysCntx_FPtr);
++      BPtr  = read_reg32 (dev, TProc_NonSysCntx_BPtr);
++      Base  = dev->TAndQBase + offsetof (E3_TrapAndQueue, NonSysCntxThreadQueue[0]);
++      Top   = dev->TAndQBase + offsetof (E3_TrapAndQueue, NonSysCntxThreadQueue[E3_NonSysCntxQueueSize-1]);
++    }
++
++    while (FPtr != BPtr)
++    {
++      elan3_sdram_copyq_from_sdram (dev, FPtr, (void *) &thread, sizeof (E3_ThreadQueue_BE));
++      
++      if (thread.s.Context == ctxt->Capability.cap_mycontext)
++      {
++          if (ELAN3_QUEUE_FULL (ctxt->SwapThreadQ))
++              break;
++          
++          *ELAN3_QUEUE_BACK(ctxt->SwapThreadQ, ctxt->SwapThreads) = thread.s.Thread;
++          ELAN3_QUEUE_ADD (ctxt->SwapThreadQ);
++          
++          /*
++           * Remove this entry from the queue by replacing it with 
++           * the "magic" thread value.
++           *
++           * NOTE: we must preserve the SYS_CONTEXT_BIT since the Elan uses this
++           * to mark the approriate run queue as empty.
++           */
++          thread.s.Context = SysCntx ? SYS_CONTEXT_BIT : 0;
++          thread.s.Thread  = VanishingStackPointer;
++
++          elan3_sdram_copyq_to_sdram (dev, (void *) &thread, FPtr, sizeof (E3_ThreadQueue_BE));
++      }
++      
++      FPtr = (FPtr == Top) ? Base : FPtr + sizeof (E3_ThreadQueue);
++    }
++
++    ASSERT (elan3_sdram_readl (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProc.s.FSR)) == 0);
++    ASSERT (elan3_sdram_readl (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProcData0.s.FSR.Status)) == 0);
++    ASSERT (elan3_sdram_readl (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProcData1.s.FSR.Status)) == 0);
++    ASSERT (elan3_sdram_readl (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProcData2.s.FSR.Status)) == 0);
++    ASSERT (elan3_sdram_readl (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProcData3.s.FSR.Status)) == 0);
++
++    if (SysCntx)
++    {
++      FPtr  = read_reg32 (dev, DProc_SysCntx_FPtr);
++      BPtr  = read_reg32 (dev, DProc_SysCntx_BPtr);
++      Base  = dev->TAndQBase + offsetof (E3_TrapAndQueue, SysCntxDmaQueue[0]);
++      Top   = dev->TAndQBase + offsetof (E3_TrapAndQueue, SysCntxDmaQueue[E3_SysCntxQueueSize-1]);
++    }
++    else
++    {
++      FPtr  = read_reg32 (dev, DProc_NonSysCntx_FPtr);
++      BPtr  = read_reg32 (dev, DProc_NonSysCntx_BPtr);
++      Base  = dev->TAndQBase + offsetof (E3_TrapAndQueue, NonSysCntxDmaQueue[0]);
++      Top   = dev->TAndQBase + offsetof (E3_TrapAndQueue, NonSysCntxDmaQueue[E3_NonSysCntxQueueSize-1]);
++    }
++
++    while (FPtr != BPtr)
++    {
++      elan3_sdram_copyq_from_sdram (dev, FPtr, &dma, sizeof (E3_DMA_BE));
++          
++      if (dma.s.dma_u.s.Context == ctxt->Capability.cap_mycontext)
++      {
++          if (ELAN3_QUEUE_FULL (ctxt->SwapDmaQ))
++              break;
++          
++          *ELAN3_QUEUE_BACK (ctxt->SwapDmaQ, ctxt->SwapDmas) = dma;
++          ELAN3_QUEUE_ADD (ctxt->SwapDmaQ);
++
++          /*
++           * Remove the DMA from the queue by replacing it with one with
++           * zero size and no events.
++           *
++           * NOTE: we must preserve the SYS_CONTEXT_BIT since the Elan uses this
++           * to mark the approriate run queue as empty.
++           */
++          dma.s.dma_type            = ((SysCntx ? SYS_CONTEXT_BIT : 0) << 16);
++          dma.s.dma_size            = 0;
++          dma.s.dma_source          = (E3_Addr) 0;
++          dma.s.dma_dest            = (E3_Addr) 0;
++          dma.s.dma_destCookieVProc = (E3_Addr) 0;
++          dma.s.dma_srcEvent        = (E3_Addr) 0;
++          dma.s.dma_srcCookieVProc  = (E3_Addr) 0;
++
++          elan3_sdram_copyq_to_sdram (dev, &dma, FPtr, sizeof (E3_DMA_BE));
++      }
++
++      FPtr = (FPtr == Top) ? Base : FPtr + sizeof (E3_DMA);
++    }
++
++    /*
++     * Release the other processors now before signalling the LWP.
++     */
++    RELEASE_CPUS();
++
++    if (! ELAN3_QUEUE_FULL (ctxt->SwapDmaQ) && !ELAN3_QUEUE_FULL (ctxt->SwapThreadQ))
++    {
++      /*
++       * We've compleletly emptied the elan queues of items in this
++       * context, so we now mark it as fully swapped out.
++       */
++      if (ctxt->OthersState == CTXT_OTHERS_HALTING_MORE)
++      {
++          runCount = SysCntx ? &dev->HaltAllCount : &dev->HaltNonContext0Count;
++          
++          if (--(*runCount) == 0)
++              SetSchedStatusRegister (dev, 0, NULL);
++          
++      }
++      PRINTF0 (ctxt, DBG_SWAP, "HaltSwapContext: queues emptied -> others_swapping\n");
++
++      ctxt->OthersState = CTXT_OTHERS_SWAPPING;
++      kcondvar_wakeupall (&ctxt->Wait, &dev->IntrLock);
++    }
++    else
++    {
++      if (ctxt->OthersState == CTXT_OTHERS_HALTING)
++      {
++          runCount = SysCntx ? &dev->HaltAllCount : &dev->HaltNonContext0Count;
++          
++          if ((*runCount)++ == 0)
++              SetSchedStatusRegister (dev, 0, NULL);
++      }
++      PRINTF0 (ctxt, DBG_SWAP, "HaltSwapContext: queues not emptied -> others_swapping_more\n");
++
++      ctxt->OthersState = CTXT_OTHERS_SWAPPING_MORE;
++      kcondvar_wakeupone (&ctxt->Wait, &dev->IntrLock);
++    }
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++}
++
++void
++UnloadCommandPageMapping (ELAN3_CTXT *ctxt)
++{
++    /*
++     * Unload the Elan translations,  and flag the main processor to stall after 
++     * issueing its next command.
++     */
++    if (ctxt->CommandPageMapping != NULL && (ctxt->Status & CTXT_COMMAND_MAPPED_ELAN))
++    {
++      ELAN3MMU_RGN *rgn = elan3mmu_rgnat_main (ctxt->Elan3mmu, ctxt->CommandPageMapping);
++      
++      if (rgn != NULL)
++      {
++          E3_Addr eaddr = rgn->rgn_ebase + (ctxt->CommandPageMapping - rgn->rgn_mbase);
++          
++          PRINTF1 (ctxt, DBG_INTR, "UnloadCommandPageMapping: unmapping command port at addr %08x\n", eaddr);
++          
++          elan3mmu_unload (ctxt->Elan3mmu, eaddr, PAGESIZE, PTE_UNLOAD);
++      }
++      
++      ctxt->Status &= ~CTXT_COMMAND_MAPPED_ELAN;
++    }
++}
++
++void
++StartSwapoutContext (ELAN3_CTXT *ctxt, E3_uint32 Pend, E3_uint32 *Maskp)
++{
++    ELAN3_DEV   *dev     = ctxt->Device;
++    int               SysCntx = (ctxt->Capability.cap_mycontext & SYS_CONTEXT_BIT);
++    u_int      *runCount;
++
++    ASSERT (SPINLOCK_HELD (&dev->IntrLock));
++
++    PRINTF2 (ctxt, DBG_SWAP, "StartSwapoutContext: Status %x OthersState %s\n",
++           ctxt->Status, OthersStateStrings [ctxt->OthersState]);
++    /*
++     * Disable the inputters,  we should already have a reason for it.
++     */
++    SetInputterStateForContext (ctxt, Pend, Maskp);
++
++    UnloadCommandPageMapping (ctxt);
++
++    /* 
++     * Flag main processor to stall after issueing next command
++     */
++    PRINTF1 (ctxt, DBG_SWAP, "StartSwapoutContext: setting Command Flag at %p to 1\n", &ctxt->FlagPage->CommandFlag);
++
++    ctxt->FlagPage->CommandFlag = 1;
++
++    PRINTF1 (ctxt, DBG_SWAP, "StartSwapoutContext: OthersState=%d\n", ctxt->OthersState);
++
++    /*
++     * And queue a haltop to stop the queues and clear it out.
++     */
++    switch (ctxt->OthersState)
++    {
++    case CTXT_OTHERS_RUNNING:
++      PRINTF0 (ctxt, DBG_SWAP, "StartSwapoutContext: -> others_halting\n");
++
++      ctxt->OthersState = CTXT_OTHERS_HALTING;
++
++      QueueHaltOperation (dev, Pend, Maskp, INT_DProcHalted | INT_TProcHalted, HaltSwapContext, ctxt);
++      break;
++      
++    case CTXT_OTHERS_SWAPPING:
++      PRINTF0 (ctxt, DBG_SWAP, "StartSwapoutContext: -> others_swapping_more\n");
++      ctxt->OthersState = CTXT_OTHERS_SWAPPING_MORE;
++
++      runCount = SysCntx ? &dev->HaltAllCount : &dev->HaltNonContext0Count;
++          
++      if ((*runCount)++ == 0)
++          SetSchedStatusRegister (dev, Pend, Maskp);
++      break;
++    default:
++      PRINTF1 (ctxt, DBG_SWAP, "StartSwapoutContext: OthersState=%d\n", ctxt->OthersState);
++      break;
++    }
++}
++
++#if defined(DIGITAL_UNIX)
++/* temporary tweaks to priority bump */
++int lwp_do_prio = 1;
++int lwp_do_nxm = 1;
++int lwp_prio = BASEPRI_USER-1;
++#elif defined(LINUX)
++/* This is the default nice level for the helper LWP */
++int LwpNice = -1;
++#endif
++
++int
++elan3_lwp (ELAN3_CTXT *ctxt)
++{
++    ELAN3_DEV     *dev = ctxt->Device;
++    int                 res;
++    unsigned long flags;
++
++    PRINTF1 (ctxt, DBG_LWP, "elan3_lwp: started, context 0x%x\n", ctxt->Capability.cap_mycontext);
++
++#if defined(DIGITAL_UNIX)
++    {
++        thread_t mythread = current_thread();
++        if (lwp_do_prio && (lwp_do_nxm || !IS_NXM_TASK(mythread->task)))
++        {
++            mythread->priority = mythread->sched_pri = lwp_prio;
++            mythread->max_priority = BASEPRI_HIGHEST;
++            (void) thread_priority(mythread, lwp_prio, 0, 1);
++        }
++    }
++#elif defined(LINUX)
++    {
++      /* Do the priority trick for the helper LWP so that it
++       * runs in preferance to the user threads which may be
++       * burning CPU waiting for a trap to be fixed up
++       */
++#ifdef NO_O1_SCHED
++      if (LwpNice >= -20 && LwpNice < 20)
++          current->nice = LwpNice;
++#else
++      set_user_nice(current, LwpNice);
++#endif
++    }
++#endif
++
++    elan3_swapin (ctxt, CTXT_NO_LWPS);
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++
++    /* If we're swapped out, and not detached (or exiting) then wait until we're swapped back in */
++    /* since otherwise we could "spin" forever continually calling elan3_lwp() */
++    if ((ctxt->Status & CTXT_SWAPPED_REASONS) && ! (ctxt->Status & (CTXT_DETACHED|CTXT_EXITING)))
++      kcondvar_waitsig (&ctxt->Wait, &dev->IntrLock, &flags);
++
++    for (;;)
++    {
++#if defined(DIGITAL_UNIX)
++        if (thread_should_halt(current_thread()) || 
++            CURSIG_CHECK(task_to_proc(current_thread()->task), u.np_uthread))
++      {
++          PRINTF1 (ctxt, DBG_LWP, "elan3_lwp: exiting on %s\n", 
++                   thread_should_halt(current_thread()) ? "halt" : "signal");
++            break;
++      }
++#endif
++
++      if (ctxt->Status & CTXT_SWAPPED_REASONS)
++      {
++          PRINTF0 (ctxt, DBG_LWP, "elan3_lwp: exiting on swapped reasons\n");
++          break;
++      }
++
++      if (! (ctxt->inhibit))
++      {
++          if (FixupNetworkErrors (ctxt, &flags) == ESUCCESS &&
++              HandleExceptions (ctxt, &flags) == ESUCCESS &&
++              RestartContext (ctxt, &flags) == ESUCCESS)
++              {
++                  if (kcondvar_waitsig (&ctxt->Wait, &dev->IntrLock, &flags) == 0)
++                  {
++                      PRINTF0 (ctxt, DBG_LWP, "elan3_lwp: exiting by kcondvar_wait_sig()\n");
++                      break;
++                  }
++              }
++      }
++      else
++      {
++          printk("elan3_lwp :: skipping as inhibited\n");
++          if (kcondvar_waitsig (&ctxt->Wait, &dev->IntrLock, &flags) == 0)
++          {
++              PRINTF0 (ctxt, DBG_LWP, "elan3_lwp: exiting by kcondvar_wait_sig()\n");
++              break;
++          }
++      }
++
++    }
++
++    /* Return EINVAL to elan3_syscall_lwp() when we want it to exit */
++    res = (ctxt->Status & (CTXT_DETACHED|CTXT_EXITING)) ? EINVAL : 0;
++
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++    
++    elan3_swapout (ctxt, CTXT_NO_LWPS);
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++    FixupNetworkErrors (ctxt, &flags);
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++
++    return (res);
++}
++
++void
++SetInputterStateForContext (ELAN3_CTXT *ctxt, E3_uint32 Pend, E3_uint32 *Maskp)
++{
++    ELAN3_DEV  *dev          = NULL;
++    int        new_disabled = 0;
++    int              ctxnum;
++
++    ASSERT (ctxt != NULL);
++    dev  = ctxt->Device;
++    ASSERT (SPINLOCK_HELD (&dev->IntrLock));
++
++    new_disabled = (ctxt->Input0Trap.State != CTXT_STATE_OK ||
++                  ctxt->Input1Trap.State != CTXT_STATE_OK ||
++                  (ctxt->Status & CTXT_INPUTTER_REASONS) != 0);
++    
++
++    ctxnum   = ctxt->Capability.cap_mycontext;
++
++#ifndef __lock_lint  
++    PRINTF2 (ctxt , DBG_IPROC, "SetInputterState: ctxnum %x %s attached\n", ctxnum, ctxt->Disabled ? "disabled " : "");
++#endif /* __lock_lint */
++        
++    if (ctxt->Disabled != new_disabled)
++    {
++      PRINTF2 (ctxt, DBG_IPROC, "SetInputterState: ctxnum %x change %s\n", ctxnum, new_disabled ? "enabled to disabled" : "disabled to enabled");
++      
++      ctxt->Disabled = new_disabled;
++
++      /* synchronize the context filter for this context */
++      elan3mmu_set_context_filter (dev, ctxnum, new_disabled, Pend, Maskp);
++    }
++}
++
++int
++CheckCommandQueueFlushed (ELAN3_CTXT *ctxt, E3_uint32 cflags, int how, unsigned long *flags)
++{
++    ELAN3_DEV *dev    = ctxt->Device;
++    int       delay  = 1;
++    int i, SeenComQueueEmpty;
++
++    ASSERT (SPINLOCK_HELD (&dev->IntrLock));
++    ASSERT (cflags != DmaComQueueNotEmpty || dev->HaltDmaDequeueCount != 0);
++
++    /*
++     * Flush the command processor queues and poll the queue to see it it empties.
++     */
++    if (dev->FlushCommandCount++ == 0)
++      SetSchedStatusRegister (dev, 0, NULL);
++
++    /* 
++     * Ensure previous writes have been flushed through the write buffers
++     */
++    wmb(); mmiob();
++
++    /*
++     * If the command processor traps,  or it's taking too long to observe
++     * the queue as emtpy,  then we need to force the interrupt handler to 
++     * run for us.  So queue a halt operation for the dma processor.
++     */
++    SeenComQueueEmpty = !(read_reg32 (dev, ComQueueStatus) & cflags);
++    for (i = 20; i > 0 || (how & ISSUE_COMMAND_CANT_WAIT); i--)
++    {
++      if (SeenComQueueEmpty || (read_reg32 (dev, Exts.InterruptReg) & (INT_CProc | INT_ComQueue)))
++          break;
++      
++      mb();
++      DELAY (delay);
++
++      if ((delay <<= 1) == 0) delay = 1;
++
++      SeenComQueueEmpty = !(read_reg32 (dev, ComQueueStatus) & cflags);
++    }
++
++    if (--dev->FlushCommandCount == 0)
++      SetSchedStatusRegister (dev, 0, NULL);
++
++    /*
++     * If we've seen the command queue that we're interested in with nothing in it
++     * and the command processor has not trapped then the commands we've
++     * issued have been successfully processed.
++     */
++    if (SeenComQueueEmpty && ! (read_reg32 (dev, Exts.InterruptReg) & (INT_CProc | INT_ComQueue)))
++    {
++      PRINTF0 (ctxt, DBG_CMD, "CheckCommandQueueFlushed: observed dma queue empty and command proc not trapped\n");
++
++      if (cflags == DmaComQueueNotEmpty && --dev->HaltDmaDequeueCount == 0)
++          SetSchedStatusRegister (dev, 0, NULL);
++
++      return (ISSUE_COMMAND_OK);
++    }
++
++    if ((how & ISSUE_COMMAND_CANT_WAIT) != 0)
++      return (ISSUE_COMMAND_WAIT);
++    
++    /*
++     * Halt the dma processor and wait for it to halt,  if the command we've issued has
++     * trapped then the interrupt handler will have moved it to the context structure.
++     */
++    PRINTF0 (ctxt, DBG_CMD, "CheckCommandQueueFlushed: waiting for dproc to halt\n");
++    QueueHaltOperation (dev, 0, NULL, INT_DProcHalted, WakeupLwp, ctxt);
++    while (! ctxt->Halted)
++    {
++      PRINTF1 (ctxt, DBG_CMD, "CheckCommandQueueFlushed: waiting for Halted - %d\n", ctxt->Halted);
++
++      kcondvar_wait (&ctxt->HaltWait, &dev->IntrLock, flags);
++
++      PRINTF1 (ctxt, DBG_CMD, "CheckCommandQueueFlushed: woken for Halted - %d\n", ctxt->Halted);
++    }
++    ctxt->Halted = 0;
++    
++    PRINTF0 (ctxt, DBG_CMD, "CheckCommandQueueFlushed: dproc halted, checking for trap\n");
++    
++    if (cflags == DmaComQueueNotEmpty && --dev->HaltDmaDequeueCount == 0)
++      SetSchedStatusRegister (dev, 0, NULL);
++
++    return (ELAN3_QUEUE_BACK_EMPTY (ctxt->CommandTrapQ) ? ISSUE_COMMAND_OK : ISSUE_COMMAND_TRAPPED);
++}
++
++int
++WaitForCommandPort (ELAN3_CTXT *ctxt)
++{
++    ELAN3_DEV     *dev = ctxt->Device;
++    int                 res;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++
++    if (ctxt->Status & CTXT_DETACHED)
++      res = EINVAL;
++    else 
++    {
++      if (! ELAN3_QUEUE_EMPTY (ctxt->CommandTrapQ) || (ctxt->Status & CTXT_OTHERS_REASONS))
++      {
++          ctxt->Status |= CTXT_WAITING_COMMAND;
++          if (CTXT_IS_KERNEL(ctxt))
++              kcondvar_wait (&ctxt->CommandPortWait, &dev->IntrLock, &flags);
++          else 
++              kcondvar_waitsig (&ctxt->CommandPortWait, &dev->IntrLock, &flags);
++      }
++      
++      res = (!ELAN3_QUEUE_EMPTY(ctxt->CommandTrapQ) || (ctxt->Status & CTXT_OTHERS_REASONS)) ? EAGAIN : 0;
++    }
++      
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++
++    return (res);
++}
++
++static char *
++CommandName (int offset)
++{
++    switch (offset)
++    {
++    case offsetof (E3_CommandPort, PutDma):   return ("PutDma");
++    case offsetof (E3_CommandPort, GetDma):   return ("GetDma");
++    case offsetof (E3_CommandPort, RunThread):        return ("RunThread");
++    case offsetof (E3_CommandPort, WaitEvent0):       return ("WaitEvent0");
++    case offsetof (E3_CommandPort, WaitEvent1):       return ("WaitEvent1");
++    case offsetof (E3_CommandPort, SetEvent): return ("SetEvent");
++    default:                                  return ("Bad Command");
++    }
++}
++
++int
++IssueCommand (ELAN3_CTXT *ctxt, unsigned cmdoff, E3_Addr value, int cflags)
++{
++    ELAN3_DEV     *dev = ctxt->Device;
++    int                 res;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++
++    if ((! (cflags & ISSUE_COMMAND_FOR_CPROC) && !ELAN3_QUEUE_EMPTY (ctxt->CommandTrapQ)) || (ctxt->Status & CTXT_OTHERS_REASONS))
++    {
++      /*
++       * Cannot issue commands for non-cproc traps if command port is trapped, 
++       * nor if the dma/thread trap queues are full, or we're swapping out
++       */
++      PRINTF2 (ctxt, DBG_CMD, "IssueCommand: %s %08x -> ISSUE_COMMAND_RETRY\n",
++               CommandName (cmdoff), value);
++
++      res = ISSUE_COMMAND_RETRY;
++    }
++    else
++    {
++      PRINTF2 (ctxt, DBG_CMD, "IssueCommand: %s %08x -> ISSUE_COMMAND_OK\n",
++               CommandName (cmdoff), value);
++
++      mb();                                                   /* ensure writes to main memory completed */
++      writel (value, ctxt->CommandPort + cmdoff);             /* issue command */
++      mmiob();                                                /* and flush through IO writes */
++
++      res = ISSUE_COMMAND_OK;
++    }
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++    
++    return (res);
++}
++
++int
++IssueDmaCommand (ELAN3_CTXT *ctxt, E3_Addr value, void *item, int how)
++{
++    ELAN3_DEV     *dev    = ctxt->Device;
++    int                 res;
++    unsigned long flags;
++
++    /*
++     * Since we may be issuing a command that could trap, and we're interested in
++     * the outcome, the command port trap resolving code must be locked out.
++     */
++    kmutex_lock (&ctxt->CmdLock);
++    spin_lock_irqsave (&dev->IntrLock, flags);
++
++    if ((! (how & ISSUE_COMMAND_FOR_CPROC) && !ELAN3_QUEUE_EMPTY (ctxt->CommandTrapQ)) || (ctxt->Status & CTXT_OTHERS_REASONS))
++    {
++      PRINTF2 (ctxt, DBG_CMD, "IssueDmaCommand: PutDma %08x [%p] -> ISSUE_COMMAND_RETRY\n", value, item);
++
++      /*
++       * Cannot issue commands for non-cproc traps if command port is trapped, 
++       * nor if the dma/thread trap queues are full, or we're swapping out
++       */
++      spin_unlock_irqrestore (&dev->IntrLock, flags);
++      kmutex_unlock (&ctxt->CmdLock);
++      return (ISSUE_COMMAND_RETRY);
++    }
++    
++    ASSERT (item == NULL || ctxt->CommandPortItem == NULL);
++
++    /*
++     * Stop the DMA processor from removing entries from the 
++     * command port, and force the command processor to do this.
++     * This means that if a trap occurs then it will be the command
++     * processor that traps.
++     */
++    if (dev->HaltDmaDequeueCount++ == 0)
++      SetSchedStatusRegister (dev, 0, NULL);
++
++    PRINTF2 (ctxt, DBG_CMD, "IssueDmaCommand: PutDma %08x [%p]\n", value, item);
++
++    /*
++     * Always issue the DMA to the 'write' command,  since we've asserted HaltDmaDequeue
++     * the command processor will read the descriptor and transfer it to the run queue. 
++     * The command processor looks at the dma_direction field to determine whether it is
++     * a read or a write and whether to alter the dma_souce of the descriptr on the run 
++     * queue
++     */
++    mb();                                                     /* ensure writes to main memory ccompleted */
++    writel (value, ctxt->CommandPort + offsetof (E3_CommandPort, PutDma));
++    mmiob();                                                  /* and flush through IO writes */
++    
++    res = CheckCommandQueueFlushed (ctxt, DmaComQueueNotEmpty, how, &flags);
++
++    if (res == ISSUE_COMMAND_TRAPPED)
++    {
++      PRINTF2 (ctxt, DBG_CMD, "IssueDmaCommand: PutDma %08x [%p] -> ISSUE_COMMAND_TRAPPED\n", value, item);
++      /*
++       * Remember the item we're issueing so that if the command port traps the item will not
++       * get freed off until the descriptor has been read after the command trap has been fixed
++       * up.
++       */
++      if (item != NULL)
++          ctxt->CommandPortItem = item;
++    }
++
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++    kmutex_unlock (&ctxt->CmdLock);
++
++    return (res);
++}
++
++int
++WaitForDmaCommand (ELAN3_CTXT *ctxt, void *item, int how)
++{
++    ELAN3_DEV     *dev = ctxt->Device;
++    int           res;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++
++    res = CheckCommandQueueFlushed (ctxt, DmaComQueueNotEmpty, how, &flags);
++
++    if (res == ISSUE_COMMAND_TRAPPED && item != NULL)
++      ctxt->CommandPortItem = item;
++
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++    
++    return (res);
++}
++
++void
++FixupEventTrap (ELAN3_CTXT *ctxt, int proc, void *trap, E3_uint32 TrapType, E3_FaultSave_BE *FaultSaveArea, int flags)
++{
++    ASSERT (! CTXT_IS_KERNEL (ctxt));
++
++    /*
++     * This code re-issues the part of the set event that trapped.
++     */
++    switch (TrapType)
++    {
++    case MI_ChainedEventError:
++      ElanException (ctxt, EXCEPTION_CHAINED_EVENT, proc, trap, FaultSaveArea->s.EventAddress);
++      break;
++      
++
++    case MI_SetEventReadWait:
++      /*
++       * Fault occured on the read for the event location. Just re-issue
++       * setevent using EventAddress in E3_FaultSave
++       */
++      PRINTF1 (ctxt, DBG_EVENT, "FixupEventTrap: MI_SetEventReadWait: re-issuing setevent %08x\n", 
++               FaultSaveArea->s.EventAddress);
++      
++      ReissueEvent (ctxt, (E3_Addr) FaultSaveArea->s.EventAddress, flags);
++      break;
++
++    case MI_DoSetEvent:
++    {
++      /*
++       * Fault occured because the block write of a block copy event trapped.
++       * Must grab the event type, source and dest then simulate the block copy and then
++       * perform the set. Once the block copy is started the event location cannot be read
++       * again.
++       */
++      E3_Event *EventPtr  = (E3_Event *) elan3mmu_mainaddr (ctxt->Elan3mmu, FaultSaveArea->s.EventAddress);
++      E3_uint32 EventType = fuword (&EventPtr->ev_Type);
++      
++      /*
++       * Check that the event has the block copy bit
++       * set in it,  since we couldn't trap here if it
++       * didn't
++       */
++      if ((EventType & EV_TYPE_BCOPY) != EV_TYPE_BCOPY)
++      {
++          PRINTF1 (ctxt, DBG_EVENT, "FixupEventTrap: MI_DoSetEvent: Unexpected type=%x\n", EventType);
++          ElanException (ctxt, EXCEPTION_BAD_EVENT, proc, trap, FaultSaveArea, TrapType);
++          break;
++      }
++      
++      PRINTF1 (ctxt, DBG_EVENT, "FixupEventTrap: MI_DoSetEvent: RunEventType %x\n", EventType);
++
++      if (RunEventType (ctxt, FaultSaveArea, EventType))
++          ElanException (ctxt, EXCEPTION_BAD_EVENT, proc, trap, FaultSaveArea, TrapType);
++
++      break;
++    }
++    
++    case MI_ThreadUpdateNonSysCntxBack:
++    case MI_ThreadUpdateSysCntxBack:
++    {
++      /*
++       * Fault occured because the block write of a block copy event trapped.
++       * Must grab the event type, source and dest then simulate the block copy and then
++       * run the thread. Once the block copy is started the event location cannot be read
++       * again.
++       */
++      E3_Event *EventPtr = (E3_Event *) elan3mmu_mainaddr (ctxt->Elan3mmu, FaultSaveArea->s.EventAddress);
++      E3_uint32 EventType = fuword (&EventPtr->ev_Type);
++
++      /*
++       * Check for the correct EventPtr type
++       */
++      if ((EventType & (EV_TYPE_MASK_THREAD|EV_TYPE_MASK_BCOPY)) != (EV_TYPE_BCOPY | EV_TYPE_THREAD))
++      {
++          PRINTF1 (ctxt, DBG_EVENT, "FixupEventTrap: MI_ThreadUpdateCntx0Back: Unexpected type=%x for setevent trap. Should be thread\n", EventType);
++          ElanException (ctxt, EXCEPTION_BAD_EVENT, proc, trap, FaultSaveArea, TrapType);
++          break;
++      }
++      
++      PRINTF1 (ctxt, DBG_EVENT, "FixupEventTrap: MI_ThreadUpdateCntx0Back: RunEventType %x\n", EventType);
++      if (RunEventType (ctxt, FaultSaveArea, EventType))
++          ElanException (ctxt, EXCEPTION_BAD_EVENT, proc, trap, FaultSaveArea, TrapType);
++      break;
++    }
++    
++    case MI_EventIntUpdateBPtr:
++    {
++      /*
++       * Fault occured because the block write of a block copy event trapped.
++       * Must grab the event type, source and dest then simulate the block copy and then
++       * run the dma. Once the block copy is started the event location cannot be read
++       * again.
++       */
++      E3_Event *EventPtr = (E3_Event *) elan3mmu_mainaddr (ctxt->Elan3mmu, FaultSaveArea->s.EventAddress);
++      E3_uint32 EventType = fuword (&EventPtr->ev_Type);
++
++      /*
++       * Check for the correct EventPtr type
++       */
++      if ((EventType & (EV_TYPE_MASK_EVIRQ|EV_TYPE_MASK_BCOPY)) != (EV_TYPE_BCOPY | EV_TYPE_EVIRQ))
++      {
++          PRINTF1 (ctxt, DBG_EVENT, "FixupEventTrap: MI_EventIntUpdateBPtr: Unexpected type=%x\n", EventType);
++          ElanException (ctxt, EXCEPTION_BAD_EVENT, proc, trap, FaultSaveArea, TrapType);
++          break;
++      }
++
++      PRINTF1 (ctxt, DBG_EVENT, "FixupEventTrap: MI_EventIntUpdateBPtr: RunEventType %x\n", EventType);
++      if (RunEventType(ctxt, FaultSaveArea, EventType))
++          ElanException (ctxt, EXCEPTION_BAD_EVENT, proc, trap, FaultSaveArea, TrapType);
++      break;
++    }
++    
++    case MI_RunDmaDesc:
++    {
++      /*
++       * Fault occured because the block write of a block copy event trapped.
++       * Must grab the event type, source and dest then simulate the block copy and then
++       * run the dma. Once the block copy is started the event location cannot be read
++       * again.
++       */
++      E3_Event *EventPtr = (E3_Event *) elan3mmu_mainaddr (ctxt->Elan3mmu, FaultSaveArea->s.EventAddress);
++      E3_uint32 EventType = fuword (&EventPtr->ev_Type);
++
++      /*
++       * Check for the correct EventPtr type
++       */
++      if ((EventType & (EV_TYPE_MASK_DMA|EV_TYPE_MASK_BCOPY)) != (EV_TYPE_BCOPY | EV_TYPE_DMA))
++      {
++          PRINTF1 (ctxt, DBG_EVENT, "FixupEventTrap: MI_RunDmaDesc: Unexpected type=%x\n", EventType);
++          ElanException (ctxt, EXCEPTION_BAD_EVENT, proc, trap, FaultSaveArea, TrapType);
++          break;
++      }
++
++      PRINTF1 (ctxt, DBG_EVENT, "FixupEventTrap: MI_RunDmaDesc: RunEventType %x\n", EventType);
++      if (RunEventType(ctxt, FaultSaveArea, EventType))
++          ElanException (ctxt, EXCEPTION_BAD_EVENT, proc, trap, FaultSaveArea, TrapType);
++      break;
++    }
++    
++    case MI_WaitForCntxDmaDescRead:
++    case MI_WaitForNonCntxDmaDescRead:
++      /*
++       * Fault occured on the read of the dma descriptor. Run dma using the
++       * Fault Address in FaultSave.
++       */
++      PRINTF1 (ctxt, DBG_EVENT, "FixupEventTrap: MI_WaitForCntxDmaDescRead: re-issue dma at %08x\n", FaultSaveArea->s.FaultAddress);
++      
++      RestartDmaPtr (ctxt, FaultSaveArea->s.FaultAddress);
++      break;
++    
++    case MI_FinishedSetEvent:
++      /*
++       * Fault occured because the block write of a block copy event trapped.
++       * Simulate the block copy.
++       */
++      if (SimulateBlockCopy (ctxt, FaultSaveArea->s.EventAddress))
++          ElanException (ctxt, EXCEPTION_BAD_EVENT, proc, trap, FaultSaveArea, TrapType);
++      break;
++      
++    case MI_BlockCopyEvent:
++    case MI_BlockCopyWaitForReadData:
++    {
++      /*
++       * Fault occured on the read or write of the data for a block copy
++       * event. Simulate the block copy using EventAddress in E3_FaultSave. Must also sample
++       * the event type and then perform a run.
++       */
++      E3_Event *EventPtr = (E3_Event *) elan3mmu_mainaddr (ctxt->Elan3mmu, FaultSaveArea->s.EventAddress);
++      E3_uint32 EventType = fuword (&EventPtr->ev_Type);
++
++      PRINTF0 (ctxt, DBG_EVENT, "FixupEventTrap: MI_BlockCopyWaitForReadData: BCopy read fault in BCopy event. Simulating BCopy.\n");
++      
++      if (RunEventType(ctxt, FaultSaveArea, EventType))
++          ElanException (ctxt, EXCEPTION_BAD_EVENT, proc, trap, FaultSaveArea, TrapType);
++      break;
++    }
++    
++    case MI_EventQueueOverflow:
++    case MI_ThreadQueueOverflow:
++    case MI_DmaQueueOverflow:
++      /* XXXX: should handle queue overflow */
++      PRINTF0 (ctxt, DBG_EVENT, "FixupEventTrap: Queue overflow\n");
++
++      ElanException (ctxt, EXCEPTION_QUEUE_OVERFLOW, proc, trap, FaultSaveArea, TrapType);
++      break;
++
++    default:
++      ElanException (ctxt, EXCEPTION_BUS_ERROR, proc, trap, FaultSaveArea, TrapType);
++      break;
++    }
++}
++
++int
++SimulateBlockCopy (ELAN3_CTXT *ctxt, E3_Addr EventAddress)
++{
++    E3_Addr  SourcePtrElan;
++    E3_Addr  DestPtrElan;
++    unsigned DataType;
++    int      i;
++
++    if (ELAN3_OP_START_FAULT_CHECK (ctxt))
++    {
++      ELAN3_OP_END_FAULT_CHECK (ctxt);
++
++      ElanException (ctxt, EXCEPTION_FAULTED, EVENT_PROC, NULL, EventAddress);
++      return (TRUE);
++    }
++
++    SourcePtrElan = ELAN3_OP_LOAD32 (ctxt, EventAddress + offsetof (E3_BlockCopyEvent, ev_Source));
++    DestPtrElan   = ELAN3_OP_LOAD32 (ctxt, EventAddress + offsetof (E3_BlockCopyEvent, ev_Dest));
++    DataType      = DestPtrElan & EV_BCOPY_DTYPE_MASK;
++    DestPtrElan  &= ~EV_BCOPY_DTYPE_MASK;
++
++
++    PRINTF3 (ctxt, DBG_EVENT, "SimulateBlockCopy: Event %08x SourcePtr %08x DestPtr %08x\n",
++           EventAddress, SourcePtrElan, DestPtrElan);
++
++    if (SourcePtrElan & EV_WCOPY)
++      ELAN3_OP_STORE32 (ctxt, DestPtrElan, SourcePtrElan);
++    else
++    {
++      /*
++       * NOTE: since the block copy could be to sdram, we issue the writes backwards,
++       *       except we MUST ensure that the last item in the block is written last.
++       */
++#if defined(__LITTLE_ENDIAN__)
++      /*
++       * For little endian cpu's we don't need to worry about the data type.
++       */
++      for (i = E3_BLK_SIZE-(2*sizeof (E3_uint64)); i >= 0; i -= sizeof (E3_uint64))
++          ELAN3_OP_STORE64 (ctxt, DestPtrElan + i, ELAN3_OP_LOAD64 (ctxt, SourcePtrElan + i));
++
++      i = E3_BLK_SIZE - sizeof (E3_uint64);
++      ELAN3_OP_STORE64 (ctxt, DestPtrElan + i, ELAN3_OP_LOAD64 (ctxt, SourcePtrElan + i));
++#else
++      switch (DataType)
++      {
++      case EV_TYPE_BCOPY_BYTE:
++          for (i = E3_BLK_SIZE-(2*sizeof (E3_uint8)); i >= 0; i -= sizeof (E3_uint8))
++              ELAN3_OP_STORE8 (ctxt, DestPtrElan + i, ELAN3_OP_LOAD8 (ctxt, SourcePtrElan + i));
++          
++          i = E3_BLK_SIZE - sizeof (E3_uint8);
++          ELAN3_OP_STORE8 (ctxt, DestPtrElan + i, ELAN3_OP_LOAD8 (ctxt, SourcePtrElan + i));
++          break;
++
++      case EV_TYPE_BCOPY_HWORD: 
++          for (i = E3_BLK_SIZE-(2*sizeof (E3_uint16)); i >= 0; i -= sizeof (E3_uint16))
++              ELAN3_OP_STORE16 (ctxt, DestPtrElan + i, ELAN3_OP_LOAD16 (ctxt, SourcePtrElan + i));
++          
++          i = E3_BLK_SIZE - sizeof (E3_uint16);
++          ELAN3_OP_STORE16 (ctxt, DestPtrElan + i, ELAN3_OP_LOAD16 (ctxt, SourcePtrElan + i));
++          break;
++          
++      case EV_TYPE_BCOPY_WORD:  
++          for (i = E3_BLK_SIZE-(2*sizeof (E3_uint32)); i >= 0; i -= sizeof (E3_uint32))
++              ELAN3_OP_STORE32 (ctxt, DestPtrElan + i, ELAN3_OP_LOAD32 (ctxt, SourcePtrElan + i));
++          
++          i = E3_BLK_SIZE - sizeof (E3_uint32);
++          ELAN3_OP_STORE32 (ctxt, DestPtrElan + i, ELAN3_OP_LOAD32 (ctxt, SourcePtrElan + i));
++          break;
++          
++      case EV_TYPE_BCOPY_DWORD: 
++          for (i = E3_BLK_SIZE-(2*sizeof (E3_uint64)); i >= 0; i -= sizeof (E3_uint64))
++              ELAN3_OP_STORE64 (ctxt, DestPtrElan + i, ELAN3_OP_LOAD64 (ctxt, SourcePtrElan + i));
++          
++          i = E3_BLK_SIZE - sizeof (E3_uint64);
++          ELAN3_OP_STORE64 (ctxt, DestPtrElan + i, ELAN3_OP_LOAD64 (ctxt, SourcePtrElan + i));
++          break;
++      }
++#endif
++    }
++    ELAN3_OP_END_FAULT_CHECK (ctxt);
++
++    return (FALSE);
++}
++
++void
++ReissueEvent (ELAN3_CTXT *ctxt, E3_Addr addr, int flags)
++{
++    PRINTF1 (ctxt, DBG_CMD, "ReissueEvent : Event=%08x\n", addr);
++
++    if (IssueCommand (ctxt, offsetof (E3_CommandPort, SetEvent), addr, flags) == ISSUE_COMMAND_RETRY)
++    {
++      PRINTF1 (ctxt, DBG_CMD, "ReissueEvent: queue event %08x\n", addr);
++
++      kmutex_lock (&ctxt->SwapListsLock);
++      ctxt->ItemCount[LIST_SETEVENT]++;
++      ELAN3_OP_PUT_WORD_ITEM (ctxt, LIST_SETEVENT, addr);
++      kmutex_unlock (&ctxt->SwapListsLock);
++    }
++}
++
++int
++SetEventsNeedRestart (ELAN3_CTXT *ctxt)
++{
++    return (ctxt->ItemCount[LIST_SETEVENT] != 0);
++}
++
++void
++RestartSetEvents (ELAN3_CTXT *ctxt)
++{
++    void     *item;
++    E3_uint32 EventPointer;
++
++    kmutex_lock (&ctxt->SwapListsLock);
++    
++    while (ctxt->ItemCount[LIST_SETEVENT])
++    {
++      if (! ELAN3_OP_GET_WORD_ITEM (ctxt, LIST_SETEVENT, &item, &EventPointer))
++          ctxt->ItemCount[LIST_SETEVENT] = 0;
++      else
++      {
++          if (IssueCommand (ctxt, offsetof (E3_CommandPort, SetEvent), EventPointer, FALSE) == ISSUE_COMMAND_RETRY)
++          {
++              ELAN3_OP_PUTBACK_ITEM (ctxt, LIST_SETEVENT, item);
++              kmutex_unlock (&ctxt->SwapListsLock);
++              return;
++          }
++          
++          ctxt->ItemCount[LIST_SETEVENT]--;
++          ELAN3_OP_FREE_WORD_ITEM (ctxt, item);
++      }
++    }
++    kmutex_unlock (&ctxt->SwapListsLock);
++}
++
++int
++RunEventType(ELAN3_CTXT *ctxt, E3_FaultSave_BE *FaultSaveArea, E3_uint32 EventType)
++{
++    int failed = FALSE;
++
++    if ((EventType & EV_TYPE_BCOPY) != 0)
++      failed = SimulateBlockCopy(ctxt, FaultSaveArea->s.EventAddress);
++    
++    if ((EventType & EV_TYPE_MASK) == EV_TYPE_THREAD)
++      ReissueStackPointer (ctxt, EventType & ~(EV_TYPE_MASK_THREAD|EV_TYPE_MASK_BCOPY));
++    else if ((EventType & EV_TYPE_MASK) == EV_TYPE_DMA)
++      RestartDmaPtr (ctxt, EventType & ~(EV_TYPE_MASK_DMA|EV_TYPE_MASK_BCOPY));
++    else if ((EventType & EV_TYPE_EVIRQ) != 0)
++      QueueEventInterrupt (ctxt, EventType & ~(EV_TYPE_MASK_EVIRQ|EV_TYPE_MASK_BCOPY));
++    else /* Chained event */
++    {
++      if ((EventType & ~EV_TYPE_BCOPY) != 0) /* not null setevent */
++          ReissueEvent (ctxt, EventType & ~(EV_TYPE_MASK_CHAIN|EV_TYPE_MASK_BCOPY), FALSE);
++    }
++
++    return (failed);
++}
++
++void
++WakeupLwp (ELAN3_DEV *dev, void *arg)
++{
++    ELAN3_CTXT    *ctxt = (ELAN3_CTXT *) arg;
++    unsigned long flags;
++
++    PRINTF1 (ctxt, DBG_INTR, "WakeupLwp: %d\n", SPINLOCK_HELD (&dev->IntrLock));
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++    ctxt->Halted = 1;
++    kcondvar_wakeupone (&ctxt->HaltWait, &dev->IntrLock);
++
++    PRINTF0 (ctxt, DBG_INTR, "WakeupLwp: woken up context\n");
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++}
++
++void
++QueueEventInterrupt (ELAN3_CTXT *ctxt, E3_uint32 cookie)
++{
++    ELAN3_DEV     *dev = ctxt->Device;
++    unsigned long flags;
++
++    PRINTF1 (ctxt, DBG_EVENT, "QueueEventInterrupt: cookie %08x\n", cookie);
++
++    if (ELAN3_OP_EVENT (ctxt, cookie, OP_INTR) == OP_DEFER)
++    {
++      spin_lock_irqsave (&ctxt->Device->IntrLock, flags);
++
++      if (ELAN3_QUEUE_REALLY_FULL (ctxt->EventCookieQ))
++      {
++          ctxt->Status |= CTXT_COMMAND_OVERFLOW_ERROR;
++          StartSwapoutContext (ctxt, 0, NULL);
++      }
++      else
++      {
++          *(ELAN3_QUEUE_BACK (ctxt->EventCookieQ, ctxt->EventCookies)) = cookie;
++          
++          ELAN3_QUEUE_ADD (ctxt->EventCookieQ);
++          kcondvar_wakeupone (&ctxt->Wait, &dev->IntrLock);
++          if (ELAN3_QUEUE_FULL (ctxt->EventCookieQ))
++          {
++              ctxt->Status |= CTXT_EVENT_QUEUE_FULL;
++              StartSwapoutContext (ctxt, 0, NULL);
++          }
++      }
++      spin_unlock_irqrestore (&ctxt->Device->IntrLock, flags);
++    }
++}
++
++int
++ElanException (ELAN3_CTXT *ctxt, int type, int proc, void *trap, ...)
++{
++    int     res;
++    va_list ap;
++
++    va_start (ap, trap);
++
++    PRINTF2 (ctxt, DBG_FN, "ElanException: proc %d type %d\n", proc, type);
++
++    res = ELAN3_OP_EXCEPTION (ctxt, type, proc, trap, ap);
++
++    va_end (ap);
++    
++    return (res);
++}
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan3/context_linux.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan3/context_linux.c   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan3/context_linux.c        2005-05-11 12:10:12.398938656 -0400
+@@ -0,0 +1,229 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Limited.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: context_linux.c,v 1.28.2.3 2005/03/02 13:45:27 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/os/context_linux.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <qsnet/kpte.h>
++
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elanvp.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanctxt.h>
++#include <elan3/elandebug.h>
++#include <elan3/urom_addrs.h>
++#include <elan3/thread.h>
++
++int
++LoadElanTranslation (ELAN3_CTXT *ctxt, E3_Addr addr, int len, int protFault, int writeable)
++{
++    ELAN3MMU          *elan3mmu = ctxt->Elan3mmu;
++    ELAN3MMU_RGN      *rgn;
++    caddr_t           mainAddr;
++    int                       perm;
++    unsigned int        off;
++    unsigned long       flags;
++
++    ASSERT (PAGE_ALIGNED (addr) && PAGE_ALIGNED (len));
++
++    PRINTF (ctxt, DBG_FAULT, "LoadElanTranslation: addr %08x len %08x%s%s\n", 
++        addr, len, protFault ? " prot fault" : "", writeable ? " writeable" : "");
++
++    /* Ensure there's enough elan mmu tables for us to use */
++    elan3mmu_expand (elan3mmu, addr, len, PTBL_LEVEL_3, 0);
++
++    while (len > 0) 
++    {
++      /*
++       * Retrieve permission region and calculate main address
++       */
++      spin_lock (&elan3mmu->elan3mmu_lock);
++
++      rgn = elan3mmu_rgnat_elan (elan3mmu, addr);
++      if (rgn == NULL) {
++          PRINTF (ctxt, DBG_FAULT, "LoadElanTranslation: no permission region at %lx %p\n", 
++              (u_long) addr, rgn);
++          spin_unlock (&elan3mmu->elan3mmu_lock);
++          return (EFAULT);
++      }
++      mainAddr = rgn->rgn_mbase + (addr - rgn->rgn_ebase);
++
++      ASSERT (PAGE_ALIGNED ((unsigned long)mainAddr));
++
++      spin_unlock (&elan3mmu->elan3mmu_lock);
++
++      /*
++       * If we're tying to load a translation to the elan command port, 
++       * then don't do it now, but mark the context to have it reloaded
++       * just before we restart any threads. We do this because we don't
++       * want to call into the segment driver since we could then block
++       * waiting for the command port to become available.
++       */
++      if (mainAddr == ctxt->CommandPageMapping)
++      {
++          PRINTF (ctxt, DBG_FAULT, "LoadElanTranslation: addr=%08x maps command port\n", addr);
++
++          spin_lock_irqsave (&ctxt->Device->IntrLock, flags);
++          UnloadCommandPageMapping (ctxt);
++          spin_unlock_irqrestore (&ctxt->Device->IntrLock, flags);
++      }
++      else 
++      {
++          struct vm_area_struct *area;
++          struct mm_struct *mm = current->mm;
++          pte_t *ptep_ptr;
++          pte_t  ptep_value;
++
++          down_read (&current->mm->mmap_sem);
++
++          if ((area = find_vma_intersection(mm, (unsigned long)mainAddr, (unsigned long)mainAddr + PAGESIZE)) == NULL)
++          {
++              PRINTF (ctxt, DBG_FAULT, "LoadElanTranslation: %p no vma\n", mainAddr);
++              up_read (&current->mm->mmap_sem);
++              return EFAULT;
++          }
++
++          if (writeable && !(area->vm_flags & VM_WRITE)) 
++          {
++              PRINTF (ctxt, DBG_FAULT, "LoadElanTranslation: %p not writeable\n", mainAddr);
++              up_read (&current->mm->mmap_sem);
++              return EFAULT;
++          }
++          
++          spin_lock (&mm->page_table_lock);
++
++          /* dont deference the pointer after the unmap */
++          ptep_ptr = find_pte_map (mm, (unsigned long)mainAddr);  
++          if (ptep_ptr) {
++              ptep_value = *ptep_ptr;
++              pte_unmap(ptep_ptr);
++          }
++
++          PRINTF (ctxt, DBG_FAULT, "LoadElanTranslation: %p %s %s\n", 
++                  mainAddr, writeable ? "writeable" : "readonly", 
++                  !ptep_ptr ? "invalid" : pte_none(ptep_value) ? "none " : !pte_present(ptep_value) ? "swapped " : 
++                  writeable && !pte_write(ptep_value) ? "COW" : "OK");
++          
++          if (!ptep_ptr || pte_none(ptep_value) || !pte_present(ptep_value) || (writeable && !pte_write(ptep_value))) 
++          {  
++              spin_unlock (&mm->page_table_lock);
++
++              get_user_pages (current, current->mm, (unsigned long) mainAddr, PAGE_SIZE, 
++                              (area->vm_flags & VM_WRITE), 0, NULL, NULL);
++
++              spin_lock (&mm->page_table_lock);
++
++              /* dont deference the pointer after the unmap */
++              ptep_ptr = find_pte_map (mm, (unsigned long)mainAddr);  
++              if (ptep_ptr) {
++                  ptep_value = *ptep_ptr;
++                  pte_unmap(ptep_ptr);
++              }
++
++              if (!ptep_ptr || pte_none(ptep_value) || !pte_present(ptep_value) || (writeable && !pte_write(ptep_value))) 
++              {
++                  spin_unlock (&mm->page_table_lock);
++                  up_read (&current->mm->mmap_sem);
++                  return EFAULT;
++              }
++          } 
++
++          /* don't allow user write access to kernel pages if not kernel */
++          if (!pte_read(ptep_value))
++          {
++              spin_unlock (&mm->page_table_lock);
++              up_read (&current->mm->mmap_sem);
++              return EFAULT;
++          }
++
++          if (writeable)
++              pte_mkdirty(ptep_value);
++          pte_mkyoung (ptep_value);
++
++          /* now load the elan pte */
++          if (writeable)
++              perm  = rgn->rgn_perm;
++          else
++              perm = ELAN3_PERM_READONLY(rgn->rgn_perm & ELAN3_PTE_PERM_MASK) | (rgn->rgn_perm & ~ELAN3_PTE_PERM_MASK);
++
++          for (off = 0; off < PAGE_SIZE; off += ELAN3_PAGE_SIZE)
++              elan3mmu_pteload (elan3mmu, PTBL_LEVEL_3, addr + off, pte_phys(ptep_value) + off, perm, PTE_LOAD | PTE_NO_SLEEP);
++
++          spin_unlock (&mm->page_table_lock);
++          up_read (&current->mm->mmap_sem);
++      }
++
++      len -= PAGESIZE;
++      addr += PAGESIZE;
++    }
++    return (ESUCCESS);
++}
++
++
++/*
++ * LoadCommandPortTranslation:
++ *    explicitly load an elan translation to the command port.
++ *    but only do it if the command port is accessible.
++ *
++ *    we call this function just after we have restarted
++ *    and trapped commands,  since when a command traps
++ *    the elan translation to the command port is unloaded.
++ */
++void
++LoadCommandPortTranslation (ELAN3_CTXT *ctxt)
++{
++    ELAN3MMU     *elan3mmu = ctxt->Elan3mmu;
++    ELAN3MMU_RGN *rgn;
++    E3_Addr       addr;
++    int                 perm;
++    physaddr_t    phys;
++    unsigned int  off;
++    unsigned long flags;
++
++    PRINTF (ctxt, DBG_FAULT, "LoadCommandPortTranslation: SegAddr=%p Status=%x\n", ctxt->CommandPageMapping, ctxt->Status);
++
++    if (ctxt->CommandPageMapping != NULL  && !(ctxt->Status & CTXT_COMMAND_MAPPED_ELAN))
++    {
++      spin_lock (&elan3mmu->elan3mmu_lock);
++      
++      rgn = elan3mmu_rgnat_main (elan3mmu, ctxt->CommandPageMapping);
++      if (rgn == (ELAN3MMU_RGN *) NULL) 
++      {
++          PRINTF(ctxt, DBG_FAULT, "LoadCommandPortTranslation: no permission for command port\n");
++          spin_unlock (&elan3mmu->elan3mmu_lock);
++          return;
++      }
++      
++      addr = rgn->rgn_ebase + (ctxt->CommandPageMapping - rgn->rgn_mbase);
++      perm = rgn->rgn_perm;
++      phys = kmem_to_phys((caddr_t) ctxt->CommandPage);
++
++      spin_lock_irqsave (&ctxt->Device->IntrLock, flags);
++      if (ELAN3_QUEUE_EMPTY(ctxt->CommandTrapQ) && !(ctxt->Status & CTXT_OTHERS_REASONS))
++      {
++          PRINTF(ctxt, DBG_FAULT, "LoadCommandPortTranslation: load xlation addr=%08x phys=%llx perm=%d\n", 
++                 addr, (unsigned long long)phys, perm);
++
++          ctxt->Status |= CTXT_COMMAND_MAPPED_ELAN;
++
++          for (off = 0; off < PAGESIZE; off += ELAN3_PAGE_SIZE)
++              elan3mmu_pteload (elan3mmu, PTBL_LEVEL_3, addr + off, phys + off, perm, PTE_LOAD | PTE_NO_SLEEP);
++      }
++      spin_unlock_irqrestore (&ctxt->Device->IntrLock, flags);
++      
++      spin_unlock (&elan3mmu->elan3mmu_lock);
++    }
++}
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan3/cproc.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan3/cproc.c   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan3/cproc.c        2005-05-11 12:10:12.399938504 -0400
+@@ -0,0 +1,539 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: cproc.c,v 1.46 2004/02/10 15:05:10 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/os/cproc.c,v $ */
++
++
++#include <qsnet/kernel.h>
++
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elanvp.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanctxt.h>
++#include <elan3/elandebug.h>
++#include <elan3/urom_addrs.h>
++#include <elan3/vmseg.h>
++
++void
++HandleCProcTrap (ELAN3_DEV *dev, E3_uint32 Pend, E3_uint32 *Maskp)
++{
++    E3_FaultSave_BE     FaultSave;
++    CProcTrapBuf_BE   TrapBuf;
++    COMMAND_TRAP       *trap;
++    ELAN3_CTXT               *ctxt;
++    sdramaddr_t         CurrTrap;
++    sdramaddr_t         LastTrapAddr;
++    int               NTrapEntries;
++    int                       NewPend;
++    unsigned long       flags;
++
++    /* 
++     * Temporarily mask out the command processor interrupt, since
++     * we may cause it be re-asserted when we re-issue the commands
++     * from the overflow queue area.
++     */
++    DISABLE_INT_MASK (dev, INT_CProc | INT_ComQueue);
++
++    NewPend = read_reg32 (dev, Exts.InterruptReg);
++
++    do {
++      if (NewPend & INT_ComQueue)
++      {
++          if ((read_reg32 (dev, ComQueueStatus) & ComQueueError) != 0)
++          {
++              printk ("elan%d: InterruptReg=%x ComQueueStatus=%x\n", dev->Instance,
++                      read_reg32 (dev, Exts.InterruptReg), read_reg32 (dev, ComQueueStatus));
++              panic ("elan: command queue has overflowed !!");
++              /* NOTREACHED */
++          }
++
++          BumpStat (dev, ComQueueHalfFull);
++
++          /*
++           * Capture the other cpus and stop the threads processor then
++           * allow the command processor to eagerly flush the command queue.
++           */
++          dev->FlushCommandCount++; dev->HaltThreadCount++;
++          SetSchedStatusRegister (dev, Pend, Maskp);
++
++          CAPTURE_CPUS();
++
++          while ((read_reg32 (dev, ComQueueStatus) & ComQueueNotEmpty) != 0)
++              mb();
++          
++          /*
++           * Let the threads processor run again, and release the cross call.
++           */
++          RELEASE_CPUS();
++
++          dev->FlushCommandCount--; dev->HaltThreadCount--;
++          SetSchedStatusRegister (dev, Pend, Maskp);
++
++          /*
++           * Re-sample the interrupt register to see if the command processor
++           * has trapped while flushing the queue.  Preserve the INT_ComQueue
++           * bit, so we can clear the ComQueueStatus register later.
++           */
++          NewPend = (read_reg32 (dev, Exts.InterruptReg) | INT_ComQueue);
++      }
++      
++      CurrTrap = dev->CommandPortTraps[dev->CurrentCommandPortTrap];
++      
++      if (NewPend & INT_CProc)
++      {
++          BumpStat (dev, CProcTraps);
++
++          /*
++           * Copy the MMU Fault Save area and zero it out for future traps.
++           */
++          elan3_sdram_copyq_from_sdram (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, CProc), &FaultSave, sizeof (E3_FaultSave));
++          elan3_sdram_zeroq_sdram      (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, CProc), sizeof (E3_FaultSave));
++
++          /*
++           * First entry in the cproc trap save area is the value of Areg and Breg for the
++           * uWord before the address fault.
++           */
++          TrapBuf.Align64 = elan3_sdram_readq (dev, CurrTrap); CurrTrap += sizeof (TrapBuf.Align64);
++
++          ctxt = ELAN3_DEV_CTX_TABLE(dev, (TrapBuf.r.Breg >> 16));
++          if (ctxt == NULL)
++          {
++              PRINTF2 (DBG_DEVICE, DBG_INTR, "HandleCProcTrap: context invalid [%08x.%08x]\n", TrapBuf.r.Areg, TrapBuf.r.Breg);
++              BumpStat (dev, InvalidContext);
++          }
++          else
++          {
++              if (ELAN3_QUEUE_REALLY_FULL (ctxt->CommandTrapQ))
++              {
++                  if ((ctxt->Status & CTXT_COMMAND_OVERFLOW_ERROR) == 0)
++                  {
++                      ctxt->Status |= CTXT_COMMAND_OVERFLOW_ERROR;
++                      StartSwapoutContext (ctxt, Pend, Maskp);
++                  }
++              }
++              else
++              {
++                  trap = ELAN3_QUEUE_BACK (ctxt->CommandTrapQ, ctxt->CommandTraps);
++                  
++                  trap->FaultSave     = FaultSave;
++                  trap->Status.Status = read_reg32 (dev, Exts.CProcStatus.Status);
++                  trap->TrapBuf       = TrapBuf;
++                  
++                  /*
++                   * The command processor does not stop after it has trapped. It will continue
++                   * to save commands for other contexts into the commands port save area.
++                   * The valid context for the trap is held in FaultSave. As some of this
++                   * trap code uses the context in the status register the local copy must be
++                   * updated with the trap context.
++                   */
++                  trap->Status.s.Context = (TrapBuf.r.Breg >> 16);
++                  
++                  PRINTF4 (ctxt, DBG_INTR, "HandleCProcTrap: WakeupFnt=%x Cntx=%x SuspAddr=%x TrapType=%s\n",
++                           trap->Status.s.WakeupFunction, trap->Status.s.Context,
++                           trap->Status.s.SuspendAddr, MiToName(trap->Status.s.TrapType));
++                  PRINTF2 (ctxt, DBG_INTR, "HandleCProcTrap: Areg=%08x Breg=%08x\n", 
++                           trap->TrapBuf.r.Areg, trap->TrapBuf.r.Breg);
++                  
++                  if (ELAN3_OP_CPROC_TRAP (ctxt, trap) == OP_DEFER)
++                  {
++                      ELAN3_QUEUE_ADD (ctxt->CommandTrapQ);
++                      
++                      PRINTF1 (ctxt, DBG_INTR, "HandleCProcTrap: setting Command Flag at %p to 1\n", &ctxt->FlagPage->CommandFlag);
++                      
++                      ctxt->FlagPage->CommandFlag = 1;
++                      
++                      kcondvar_wakeupone (&ctxt->Wait, &dev->IntrLock);
++                  }
++              }
++
++              UnloadCommandPageMapping (ctxt);
++          }
++      }
++      
++      /*
++       * Now change the CommandPortTrap queue.
++       * Must stop the command processor, wait for it to stop, find the final
++       * entry in the current cproc trap save area, reset the comm port
++       * trap save address to the other queue, clear the command port interrupt and
++       * set it running normally again, and then let it go again. This is not very
++       * time critical but it would be a good idea to prevent a higher priority
++       * interrupt from slowing down the process to prevent to fifos filling.
++       */
++      spin_lock_irqsave (&dev->CProcLock, flags);
++
++      SET_SCHED_STATUS (dev, CProcStop);
++
++      while ((read_reg32 (dev, Exts.SchCntReg) & CProcStopped) == 0)
++      {
++          PRINTF0 (DBG_DEVICE, DBG_INTR, "HandleCProcTrap: waiting for command processor to stop\n");
++          mb();
++      }
++      
++      /*
++       * Remember how many entries are in the saved command queue,  and 
++       * re-initialise it, before restarting the command processor.
++       */
++      NTrapEntries = (read_reg32 (dev, CProc_TrapSave_Addr) - dev->CommandPortTraps[dev->CurrentCommandPortTrap])/sizeof (E3_uint64);
++      LastTrapAddr = dev->CommandPortTraps[dev->CurrentCommandPortTrap] + NTrapEntries*sizeof (TrapBuf);
++
++      dev->CurrentCommandPortTrap ^= 1;
++      write_reg32 (dev, CProc_TrapSave_Addr, dev->CommandPortTraps[dev->CurrentCommandPortTrap]);
++
++      PRINTF1 (DBG_DEVICE, DBG_INTR, "HandleCProcTrap: command trap queue has %d entries\n", NTrapEntries);
++
++      if (NTrapEntries > ELAN3_COMMAND_TRAP_SIZE/sizeof (E3_uint64))
++          panic ("HandleCProcTrap: command trap queue has overflowed\n");
++      
++      if (NewPend & INT_CProc)
++      {
++          /*
++           * Clear the CProc interrupt and set it running normally again. Nothing should
++           * be running now that could issue commands apart from this trap handler.
++           */
++          PULSE_SCHED_STATUS (dev, RestartCProc);
++      }
++      
++      if (NewPend & INT_ComQueue)
++      {
++          /*
++           * Write any value here to clear out the half full and error bits of the command
++           * overflow queues. This will also remove the overflow interrupt.
++           */
++          write_reg32 (dev, ComQueueStatus, 0);
++      }
++      
++      /*
++       * And let the command processor start again
++       */
++      CLEAR_SCHED_STATUS (dev, CProcStop);
++      
++      /*
++       * Now re-issue all the commands that were issued after the command port trapped.
++       * Should halt the dma processor and force command sto be put onto the run queues
++       * to ensure that a remote re-issued command is handled correctly. NOTE it is
++       * not necessary to wait for the dma processor to stop and this will reduce the
++       * performance impact. As CProcHalt is asserted all commands will be flushed
++       * to the queues.
++       */
++      dev->HaltDmaDequeueCount++; dev->FlushCommandCount++;
++      SetSchedStatusRegister (dev, Pend, Maskp);
++      
++      /*
++       * XXXX: should we do a capture/release if the trap overflow
++       *       area has a "large" number of commands in it,  since
++       *       we will just stuff them all back in, together with 
++       *       all those issued by the other cpus/thread processors.
++       */
++      while (CurrTrap != LastTrapAddr)
++      {
++          /* Read the next saved (but not trapped) command */
++          TrapBuf.Align64 = elan3_sdram_readq (dev, CurrTrap); CurrTrap += sizeof (TrapBuf);
++          
++
++          ctxt = ELAN3_DEV_CTX_TABLE(dev, (TrapBuf.s.ContextType >> 16));
++          
++          if (ctxt == NULL)
++          {
++              PRINTF1 (DBG_DEVICE, DBG_INTR, "HandleCProcTrap: context %x invalid\n", TrapBuf.s.ContextType >> 16);
++              BumpStat (dev, InvalidContext);
++          }
++          else
++          {
++              if (!ELAN3_QUEUE_EMPTY (ctxt->CommandTrapQ) || (ctxt->Status & CTXT_OTHERS_REASONS))
++              {
++                  PRINTF3 (ctxt, DBG_INTR, "HandleCProcTrap: save command %x context %x - %08x\n",
++                           (TrapBuf.s.ContextType>>3) & 0x3ff, TrapBuf.s.ContextType >> 17, TrapBuf.s.Addr);
++                  
++                  if (ELAN3_QUEUE_REALLY_FULL (ctxt->CommandQ))
++                  {
++                      ctxt->Status |= CTXT_COMMAND_OVERFLOW_ERROR;
++                      StartSwapoutContext (ctxt, Pend, Maskp);
++                  }
++                  else
++                  {
++                      *ELAN3_QUEUE_BACK(ctxt->CommandQ, ctxt->Commands) = TrapBuf;
++
++                      ELAN3_QUEUE_ADD (ctxt->CommandQ);
++                  }
++                  continue;
++              }
++              
++              /* Reissue the command to the command port for this context */
++              PRINTF2 (ctxt, DBG_INTR, "HandleCProcTrap: re-issue command %x - %08x\n",
++                       (TrapBuf.s.ContextType>>5) & 0xff, TrapBuf.s.Addr);
++              
++              mb();
++              if (ELAN3_OP_CPROC_REISSUE(ctxt, &TrapBuf) != OP_HANDLED)
++                  ((E3_uint32 *) ctxt->CommandPort)[(TrapBuf.s.ContextType>>5) & 0xff] = TrapBuf.s.Addr;
++              mmiob();
++          }
++      }
++      
++      while ((read_reg32 (dev, ComQueueStatus) & ComQueueNotEmpty) != 0)
++      {
++          PRINTF0 (DBG_DEVICE, DBG_INTR, "HandleCProcTrap: waiting for queues to empty after reissueing commands\n");
++          mb();
++      }
++      
++      dev->HaltDmaDequeueCount--; dev->FlushCommandCount--;
++      SetSchedStatusRegister (dev, Pend, Maskp);
++      
++      spin_unlock_irqrestore (&dev->CProcLock, flags);
++
++      /*
++       * Re-read the interrupt register and see if we've got another command
++       * port interrupt
++       */
++      NewPend = read_reg32 (dev, Exts.InterruptReg);
++    } while ((NewPend & (INT_CProc | INT_ComQueue)) != 0);
++
++
++    /*
++     * Re-enable the command processor interrupt as we've finished 
++     * polling it.
++     */
++    ENABLE_INT_MASK (dev, INT_CProc | INT_ComQueue);
++}
++
++void
++ResolveCProcTrap (ELAN3_CTXT *ctxt)
++{
++    ELAN3_DEV     *dev = ctxt->Device;
++    COMMAND_TRAP *trap;
++    int                 res;
++    unsigned long flags;
++
++    kmutex_lock (&ctxt->CmdLock);
++    spin_lock_irqsave (&dev->IntrLock, flags);
++
++    while (! ELAN3_QUEUE_BACK_EMPTY (ctxt->CommandTrapQ))
++    {
++      trap = ELAN3_QUEUE_MIDDLE(ctxt->CommandTrapQ, ctxt->CommandTraps);
++      spin_unlock_irqrestore (&dev->IntrLock, flags);
++
++      switch (trap->Status.s.TrapType)
++      {
++      case MI_EventIntUpdateBPtr:
++      case MI_ChainedEventError:
++      case MI_EventQueueOverflow:
++      case MI_ThreadQueueOverflow:
++      case MI_DmaQueueOverflow:
++          PRINTF1 (ctxt, DBG_CPROC, "ResolveCProcTrap: %s\n", MiToName (trap->Status.s.TrapType));
++          break;
++          
++      default:
++          /* All other traps are MMU related, we should have a fault address and FSR */
++          if ((res = elan3_pagefault (ctxt, &trap->FaultSave, 1)) != ESUCCESS)
++          {
++              PRINTF1 (ctxt, DBG_CPROC, "ResolveCProcTrap: elan3_pagefault failed for address %08x\n", 
++                       trap->FaultSave.s.FaultAddress);
++              ElanException (ctxt, EXCEPTION_INVALID_ADDR, COMMAND_PROC, trap, &trap->FaultSave, res);
++              
++              /* Set the trap type to 0 so the command does not get re-issued */
++              trap->Status.s.TrapType = 0;
++          }
++          break;
++      }
++      
++      spin_lock_irqsave (&dev->IntrLock, flags);
++
++      ELAN3_QUEUE_CONSUME (ctxt->CommandTrapQ);
++    }
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++    kmutex_unlock (&ctxt->CmdLock);
++}
++
++int
++RestartCProcTrap (ELAN3_CTXT *ctxt)
++{
++    ELAN3_DEV     *dev      = ctxt->Device;
++    COMMAND_TRAP  trap;
++    void       *item;
++    int                 res;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++
++    while (! ELAN3_QUEUE_FRONT_EMPTY (ctxt->CommandTrapQ))
++    {
++      trap = (*ELAN3_QUEUE_FRONT (ctxt->CommandTrapQ, ctxt->CommandTraps));
++      ELAN3_QUEUE_REMOVE (ctxt->CommandTrapQ);
++      spin_unlock_irqrestore (&dev->IntrLock, flags);
++      
++      BumpUserStat (ctxt, CProcTraps);
++
++      switch (trap.Status.s.TrapType)
++      {
++      case 0:
++          res = ISSUE_COMMAND_OK;
++          break;
++          
++      case MI_WaitForWaitEventDesc:
++          /*
++           * Fault occured on the read of wait event descriptor for wait event type 0.
++           * Fault already fixed. Just re-issue the wait command. Wait event descriptor addr
++           * is in the Areg save value.
++           */
++          PRINTF1 (ctxt, DBG_CPROC, "RestartCProcTrap: WaitEvent type0 desc read fault %08x\n", 
++                   trap.TrapBuf.r.Areg);
++          
++          res = IssueCommand (ctxt, offsetof (E3_CommandPort, WaitEvent0), trap.TrapBuf.r.Areg, ISSUE_COMMAND_FOR_CPROC);
++          break;
++
++      case MI_WaitForEventReadTy0:
++          /*
++           * Fault occured on the read of event location for wait event type 0.
++           * Fault already fixed. Just re-issue the wait command. Wait event descriptor addr
++           * is in the Areg save value.
++           */
++          PRINTF1 (ctxt, DBG_CPROC, "RestartCProcTrap: WaitEvent type0 event loc fault %08x\n",
++                   trap.TrapBuf.r.Areg);
++          
++          res = IssueCommand (ctxt, offsetof (E3_CommandPort, WaitEvent0), trap.TrapBuf.r.Areg, ISSUE_COMMAND_FOR_CPROC);
++          break;
++          
++      case MI_WaitForEventReadTy1:
++          /*
++           * Fault occured on the read of the event location for wait event type 1.
++           * Areg has the original ptr and count.
++           * Fault already fixed. Just re-issue the wait command using Areg and context.
++           */
++          PRINTF1 (ctxt, DBG_CPROC, "RestartCProcTrap: WaitEvent type1 event location read fault %08x\n",
++                   trap.TrapBuf.r.Areg);
++          res = IssueCommand (ctxt, offsetof (E3_CommandPort, WaitEvent1), trap.TrapBuf.r.Areg, ISSUE_COMMAND_FOR_CPROC);
++          break;
++          
++      case MI_WaitForCntxDmaDescRead:
++      case MI_WaitForNonCntxDmaDescRead:
++          /*
++           * Fault occured on the read of the dma descriptor. Run dma using the
++           * Fault Address in FaultSave.
++           */
++          PRINTF1 (ctxt, DBG_CPROC, "RestartCProcTrap: MI_WaitForCntxDmaDescRead: re-issue dma at %08x\n", 
++                   trap.FaultSave.s.FaultAddress);
++          
++          res = IssueDmaCommand (ctxt, trap.FaultSave.s.FaultAddress, NULL, ISSUE_COMMAND_FOR_CPROC);
++          break;
++          
++      default:
++          /*
++           * Assume the fault will be fixed by FixupEventTrap.
++           */
++          FixupEventTrap (ctxt, COMMAND_PROC, &trap, trap.Status.s.TrapType, &trap.FaultSave, ISSUE_COMMAND_FOR_CPROC);
++
++          res = ISSUE_COMMAND_OK;
++          break;
++      }
++
++      switch (res)
++      {
++      case ISSUE_COMMAND_OK:                                  /* command re-issued ok*/
++          break;
++
++      case ISSUE_COMMAND_TRAPPED:                             /* command trapped,  it will have been copied */
++          return (EAGAIN);                                    /* to the back of the trap queue */
++
++      case ISSUE_COMMAND_RETRY:                               /* didn't issue command, so place back at front for */
++          spin_lock_irqsave (&dev->IntrLock, flags);          /* later (after resolving other traps */
++
++          if (ELAN3_QUEUE_REALLY_FULL (ctxt->CommandTrapQ))
++              ctxt->Status |= CTXT_COMMAND_OVERFLOW_ERROR;
++          else
++          {
++              ELAN3_QUEUE_ADD_FRONT(ctxt->CommandTrapQ);
++              (*ELAN3_QUEUE_FRONT (ctxt->CommandTrapQ, ctxt->CommandTraps)) = trap;
++          }
++          spin_unlock_irqrestore (&dev->IntrLock, flags);
++          return (EAGAIN);
++
++      default:
++          return (EINVAL);
++      }
++      spin_lock_irqsave (&dev->IntrLock, flags);
++    } 
++
++    /*
++     * GNAT 5409 - if CommandPortItem was not NULL, but other reasons were set,
++     *             then we'd not free the CommandPortItem even though we'd re-
++     *             issued all trapped and overflowed commands.  Hence only return
++     *             without clearing CommandPortItem if we will be called again as
++     *             either CommandTrapQ or CommandQ is not empty.
++     */
++
++    /* Now run the overflowed commands for this context */
++    if (! ELAN3_QUEUE_EMPTY (ctxt->CommandQ))
++    {
++      if (! ELAN3_QUEUE_EMPTY (ctxt->CommandTrapQ) || (ctxt->Status & CTXT_OTHERS_REASONS))
++      {
++          PRINTF0 (ctxt, DBG_CPROC, "RestartCProcTrap: cannot issue overflowed commands\n");
++          spin_unlock_irqrestore (&dev->IntrLock, flags);
++          return (EAGAIN);
++      }
++
++      /*
++       * Just re-issue the commands,  if one traps then the remainder will 
++       * just get placed in the overflow queue again and the interrupt handler
++       * will copy them back in here.
++       *
++       * Stop the dma processor from taking commands,  since one of the commands
++       * could be a re-issued remote dma, which must be processed by the command
++       * processor.
++       */
++      
++      if (dev->HaltDmaDequeueCount++ == 0)
++          SetSchedStatusRegister (dev, 0, NULL);
++      
++      while (! ELAN3_QUEUE_EMPTY (ctxt->CommandQ))
++      {
++          CProcTrapBuf_BE *TrapBuf = ELAN3_QUEUE_FRONT (ctxt->CommandQ, ctxt->Commands);
++          
++          PRINTF2 (ctxt, DBG_CPROC, "RestartCProcTrap: re-issue command %x - %08x\n",
++                   (TrapBuf->s.ContextType>>5) & 0xff, TrapBuf->s.Addr);
++          mb();                                                       /* ensure writes to main memory completed */
++          ((E3_uint32 *) ctxt->CommandPort)[(TrapBuf->s.ContextType>>5) & 0xff] = TrapBuf->s.Addr;
++          mmiob();                                            /* and flush through IO writes */
++          
++          ELAN3_QUEUE_REMOVE (ctxt->CommandQ);
++      }
++      
++      /* observe the command processor having halted */
++      res = CheckCommandQueueFlushed (ctxt, DmaComQueueNotEmpty, 0, &flags);
++      
++      if (res != ISSUE_COMMAND_OK)
++      {
++          PRINTF0 (ctxt, DBG_CPROC, "RestartCProcTrap: trapped after issueing overflowed commands\n");
++          spin_unlock_irqrestore (&dev->IntrLock, flags);
++          return (EAGAIN);
++      }
++    }
++
++    /* remove the command port item, while holding the lock */
++    item = ctxt->CommandPortItem;
++    ctxt->CommandPortItem = NULL;
++
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++      
++    if (item != NULL)                                         /* Free of any item that may have been stored */
++    {                                                         /* because of the commandport trap */
++      PRINTF1 (ctxt, DBG_CPROC, "RestartCProcTrap: commandPortItem %p\n", item);
++
++      kmutex_lock (&ctxt->SwapListsLock);
++      ELAN3_OP_FREE_BLOCK_ITEM (ctxt, item);
++      kmutex_unlock (&ctxt->SwapListsLock);
++    }
++
++    return (ESUCCESS);
++}
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan3/dproc.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan3/dproc.c   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan3/dproc.c        2005-05-11 12:10:12.400938352 -0400
+@@ -0,0 +1,553 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: dproc.c,v 1.52 2003/09/24 13:57:25 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/os/dproc.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elanvp.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanctxt.h>
++#include <elan3/elandebug.h>
++#include <elan3/urom_addrs.h>
++#include <elan3/intrinsics.h>
++#include <elan3/dma.h>
++#include <elan3/vmseg.h>
++
++#define DMA_RETRY_FAIL_COUNT  8
++
++static void PrintUserDma (ELAN3_CTXT *ctxt, E3_Addr addr);
++
++int
++HandleDProcTrap (ELAN3_DEV *dev, E3_uint32 *RestartBits)
++{
++    DMA_TRAP   *trap   = dev->DmaTrap;
++
++    ASSERT(SPINLOCK_HELD (&dev->IntrLock));
++
++    /* Scoop out the trap information, before restarting the Elan */
++    trap->Status.Status = read_reg32 (dev, Exts.DProcStatus.Status);
++    
++    ASSERT(trap->Status.s.WakeupFunction == WakeupNever);
++
++    /* copy the normal dma access fault type */
++    elan3_sdram_copyq_from_sdram (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProc), &trap->FaultSave, sizeof (E3_FaultSave_BE));
++    
++    /* copy all 4 of the dma data fault type */
++    elan3_sdram_copyq_from_sdram (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProcData0), &trap->Data0, 4*sizeof (E3_FaultSave_BE));
++    
++    /* Copy the DMA descriptor */
++    copy_dma_regs (dev, &trap->Desc);
++    
++    /* Copy the packet info */
++    trap->PacketInfo.Value = read_reg32 (dev, Exts.Dmas.DmaRds.DMA_PacketInfo.Value);
++
++    /* update device statistics */
++    BumpStat (dev, DProcTraps);
++    switch (trap->Status.s.TrapType)
++    {
++    case MI_DmaPacketTimedOutOrPacketError:
++      if (trap->PacketInfo.s.PacketTimeout)
++          BumpStat (dev, DmaOutputTimeouts);
++      else if (trap->PacketInfo.s.PacketAckValue == C_ACK_ERROR)
++          BumpStat (dev, DmaPacketAckErrors);
++      break;
++      
++    case MI_DmaFailCountError:
++      BumpStat (dev, DmaRetries);
++      break;
++    }
++
++    /* Must now zero all the FSRs so that a subsequent fault can be seen */
++    elan3_sdram_zeroq_sdram (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProc), sizeof (E3_FaultSave));
++    elan3_sdram_zeroq_sdram (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProcData0), 4*sizeof (E3_FaultSave));
++          
++    *RestartBits |= RestartDProc;
++    return (TRUE);
++}
++
++void
++DeliverDProcTrap (ELAN3_DEV *dev, DMA_TRAP *dmaTrap, E3_uint32 Pend)
++{
++    ELAN3_CTXT            *ctxt;
++    E3_FaultSave_BE *FaultArea;
++    DMA_TRAP      *trap;
++    register int     i;
++
++    ASSERT(SPINLOCK_HELD (&dev->IntrLock));
++
++    ctxt = ELAN3_DEV_CTX_TABLE(dev, dmaTrap->Status.s.Context);
++
++    if (ctxt == NULL)
++    {
++      PRINTF1 (DBG_DEVICE, DBG_INTR, "DeliverDProcTrap: context %x invalid\n", dmaTrap->Status.s.Context);
++      BumpStat (dev, InvalidContext);
++    }
++    else
++    {
++      if (ELAN3_OP_DPROC_TRAP (ctxt, dmaTrap) == OP_DEFER)
++      {
++          if (ELAN3_QUEUE_REALLY_FULL (ctxt->DmaTrapQ))
++          {
++              ctxt->Status |= CTXT_COMMAND_OVERFLOW_ERROR;
++              StartSwapoutContext (ctxt, Pend, NULL);
++          }
++          else
++          {
++              trap = ELAN3_QUEUE_BACK (ctxt->DmaTrapQ, ctxt->DmaTraps);
++              
++              bcopy (dmaTrap, trap, sizeof (DMA_TRAP));
++              
++              PRINTF5 (ctxt, DBG_INTR, "DeliverDProcTrap: WakeupFnt=%x Cntx=%x SuspAddr=%x PacketInfo=%x TrapType=%s\n",
++                       trap->Status.s.WakeupFunction, trap->Status.s.Context, 
++                       trap->Status.s.SuspendAddr, trap->PacketInfo.Value, MiToName (trap->Status.s.TrapType));
++              PRINTF3 (ctxt, DBG_INTR, "                    FaultAddr=%x EventAddr=%x FSR=%x\n",
++                       trap->FaultSave.s.FaultAddress, trap->FaultSave.s.EventAddress,
++                       trap->FaultSave.s.FSR.Status);
++              for (i = 0, FaultArea = &trap->Data0; i < 4; i++, FaultArea++)
++                  PRINTF4 (ctxt, DBG_INTR, "                  %d FaultAddr=%x EventAddr=%x FSR=%x\n", i,
++                           FaultArea->s.FaultAddress, FaultArea->s.EventAddress, FaultArea->s.FSR.Status);
++              
++              PRINTF4 (ctxt, DBG_INTR, "                 type %08x size %08x source %08x dest %08x\n",
++                       trap->Desc.s.dma_type, trap->Desc.s.dma_size, trap->Desc.s.dma_source, trap->Desc.s.dma_dest);
++              PRINTF2 (ctxt, DBG_INTR, "                 Dest event %08x cookie/proc %08x\n",
++                       trap->Desc.s.dma_destEvent, trap->Desc.s.dma_destCookieVProc);
++              PRINTF2 (ctxt, DBG_INTR, "                 Source event %08x cookie/proc %08x\n",
++                       trap->Desc.s.dma_srcEvent, trap->Desc.s.dma_srcCookieVProc);
++              ELAN3_QUEUE_ADD (ctxt->DmaTrapQ);
++              kcondvar_wakeupone (&ctxt->Wait, &dev->IntrLock);
++              
++              if (ELAN3_QUEUE_FULL (ctxt->DmaTrapQ))
++              {
++                  PRINTF0 (ctxt, DBG_INTR, "DeliverDProcTrap: dma queue full, must swap out\n");
++                  ctxt->Status |= CTXT_DMA_QUEUE_FULL;
++                  
++                  StartSwapoutContext (ctxt, Pend, NULL);
++              }
++          }
++      }
++    }
++}
++
++int
++NextDProcTrap (ELAN3_CTXT *ctxt, DMA_TRAP *trap)
++{
++    ELAN3_DEV *dev = ctxt->Device;
++
++    ASSERT (SPINLOCK_HELD (&dev->IntrLock));
++    
++    if (ELAN3_QUEUE_EMPTY (ctxt->DmaTrapQ))
++      return (0);
++
++    *trap = *ELAN3_QUEUE_FRONT (ctxt->DmaTrapQ, ctxt->DmaTraps);
++    ELAN3_QUEUE_REMOVE (ctxt->DmaTrapQ);
++    
++    return (1);
++}
++
++void
++ResolveDProcTrap (ELAN3_CTXT *ctxt, DMA_TRAP *trap)
++{
++    E3_FaultSave_BE *FaultArea;
++    int                    FaultHandled = 0;
++    int                    res;
++    register int     i;
++    
++    PRINTF4 (ctxt, DBG_DPROC, "ResolveDProcTrap: WakeupFnt=%x Cntx=%x SuspAddr=%x TrapType=%s\n",
++           trap->Status.s.WakeupFunction, trap->Status.s.Context, 
++           trap->Status.s.SuspendAddr, MiToName (trap->Status.s.TrapType));
++    PRINTF3 (ctxt, DBG_DPROC, "                    FaultAddr=%x EventAddr=%x FSR=%x\n",
++           trap->FaultSave.s.FaultAddress, trap->FaultSave.s.EventAddress,
++           trap->FaultSave.s.FSR.Status);
++    for (i = 0, FaultArea = &trap->Data0; i < 4; i++, FaultArea++)
++      PRINTF4 (ctxt, DBG_DPROC, "                  %d FaultAddr=%x EventAddr=%x FSR=%x\n", i,
++               FaultArea->s.FaultAddress, FaultArea->s.EventAddress, FaultArea->s.FSR.Status);
++
++    PRINTF4 (ctxt, DBG_DPROC, "                  type %08x size %08x source %08x dest %08x\n",
++           trap->Desc.s.dma_type, trap->Desc.s.dma_size, trap->Desc.s.dma_source, trap->Desc.s.dma_dest);
++    PRINTF2 (ctxt, DBG_DPROC, "                  Dest event %08x cookie/proc %08x\n",
++           trap->Desc.s.dma_destEvent, trap->Desc.s.dma_destCookieVProc);
++    PRINTF2 (ctxt, DBG_DPROC, "                  Source event %08x cookie/proc %08x\n",
++           trap->Desc.s.dma_srcEvent, trap->Desc.s.dma_srcCookieVProc);
++    
++    BumpUserStat (ctxt, DProcTraps);
++
++    switch (trap->Status.s.TrapType)
++    {
++    case MI_DmaPacketTimedOutOrPacketError:
++      /*
++       * Faulted due to packet timeout or a PAckError.
++       * Reset fail count and reissue the same desc.
++       */
++      PRINTF0 (ctxt, DBG_DPROC, "ResolveDProcTrap: got a PAckError or the output timed out. Rescheduling dma.\n");
++      if (ElanException (ctxt, EXCEPTION_PACKET_TIMEOUT, DMA_PROC, trap) == OP_IGNORE)
++      {
++          BumpUserStat (ctxt, DmaRetries);
++
++          trap->Desc.s.dma_failCount = DMA_RETRY_FAIL_COUNT;
++
++          RestartDmaTrap (ctxt, trap);
++      }
++      return;
++
++    case MI_DmaFailCountError:
++      /*
++       * Faulted due to dma fail count.
++       * Reset fail count and reissue the same desc.
++       */
++      PRINTF1 (ctxt, DBG_DPROC, "ResolveDProcTrap: Reset dma fail count to %d\n", DMA_RETRY_FAIL_COUNT);
++      
++      if (ElanException (ctxt, EXCEPTION_DMA_RETRY_FAIL, DMA_PROC, trap) == OP_IGNORE)
++      {
++          BumpUserStat (ctxt, DmaRetries);
++
++          trap->Desc.s.dma_failCount = DMA_RETRY_FAIL_COUNT;
++
++          RestartDmaTrap (ctxt, trap);
++      }
++      return;
++
++    case MI_TimesliceDmaQueueOverflow:
++      PRINTF0 (ctxt, DBG_DPROC, "ResolveDProcTrap: dma timeslice queue overflow\n");
++      RestartDmaTrap (ctxt, trap);
++      return;
++      
++    case MI_UnimplementedError:
++      PRINTF0 (ctxt, DBG_DPROC, "ResolveDProcTrap: unimplemented dma trap\n");
++      if (ElanException (ctxt, EXCEPTION_UNIMPLEMENTED, DMA_PROC, trap) == OP_IGNORE)
++          RestartDmaTrap (ctxt, trap);
++      return;
++
++    case MI_EventQueueOverflow:
++    case MI_ThreadQueueOverflow:
++    case MI_DmaQueueOverflow:
++      PRINTF0 (ctxt, DBG_DPROC, "ResolveDProcTrap: trapped on a write set event.\n");
++      FixupEventTrap (ctxt, DMA_PROC, trap, trap->Status.s.TrapType, &trap->FaultSave, 0);
++      return;
++
++    case MI_RemoteDmaCommand:
++    case MI_RunDmaCommand:
++    case MI_DequeueNonSysCntxDma:
++    case MI_DequeueSysCntxDma:
++      /*
++       * The DMA processor has trapped due to outstanding prefetches from the previous 
++       * dma.  The "current" dma has not been consumed, so we just ignore the trap
++       */
++      return;
++
++    case MI_WaitForRemoteDescRead2:
++    case MI_ExecuteDmaDescriptorForRun:
++      /*
++       * The DMA processor has trapped while fetching the dma descriptor, so
++       * zero it out to not confuse the user on an error
++       */
++      bzero (&trap->Desc, sizeof (trap->Desc));
++      break;
++    }
++
++    /*
++     * All other uWords will have updated one of the fault areas,  so fix
++     * any faults found in them.  If there were no faults found then it 
++     * must have been a bus error
++     */
++    for (i = 0, FaultArea = &trap->Data0; i < 4; i++, FaultArea++)
++    {
++      if (FaultArea->s.FSR.Status != 0)
++      {
++          FaultHandled++;
++
++          ASSERT ((FaultArea->s.FSR.Status & FSR_SizeMask) == FSR_Block64 ||
++                  (FaultArea->s.FSR.Status & FSR_SizeMask) == FSR_Block32);
++          
++          ASSERT (FaultArea->s.FaultContext == trap->Status.s.Context);
++          
++          if (((trap->Desc.s.dma_source & PAGEOFFSET) >= (PAGESIZE-E3_BLK_SIZE)) &&
++              ((trap->Desc.s.dma_source & PAGEMASK) != ((trap->Desc.s.dma_source + trap->Desc.s.dma_size-1) & PAGEMASK)))
++          {
++              /* XXXX: dma started within last 64 bytes of the page
++               *       terminate the process if it has pagefaulted */
++              if (FaultArea->s.FaultAddress == (trap->Desc.s.dma_source & ~(E3_BLK_SIZE-1)))
++              {
++                  printk ("elan%d: invalid dma - context=%x source=%x\n", ctxt->Device->Instance, 
++                          ctxt->Capability.cap_mycontext, trap->Desc.s.dma_source);
++                  
++                  if (ElanException (ctxt, EXCEPTION_BAD_DMA, DMA_PROC, trap, NULL, 0) != OP_IGNORE)
++                      return;
++              }
++          }
++
++          if (trap->Desc.s.dma_size != 0 && (res = elan3_pagefault (ctxt, FaultArea, 1)) != ESUCCESS)
++          {
++              /* XXXX: Rev B Elans can prefetch data passed the end of the dma descriptor */
++              /*       if the fault relates to this, then just ignore it */
++              if (FaultArea->s.FaultAddress < (trap->Desc.s.dma_source+trap->Desc.s.dma_size) ||
++                  FaultArea->s.FaultAddress > (trap->Desc.s.dma_source+trap->Desc.s.dma_size+E3_BLK_SIZE*2))
++              {
++                  PRINTF1 (ctxt, DBG_DPROC, "ResolveDProcTrap: elan3_pagefault failed for address %x\n",
++                           FaultArea->s.FaultAddress);
++                  
++                  if (ElanException (ctxt, EXCEPTION_INVALID_ADDR, DMA_PROC, trap, FaultArea, res) != OP_IGNORE)
++                      return;
++              }
++          }
++      }
++    }
++    
++    if (trap->FaultSave.s.FSR.Status != 0)
++    {
++      FaultHandled++;
++
++      ASSERT (trap->FaultSave.s.FaultContext == trap->Status.s.Context);
++
++      if ((trap->FaultSave.s.FSR.Status & FSR_SizeMask) == FSR_RouteFetch)
++      {
++          res = ResolveVirtualProcess (ctxt, trap->FaultSave.s.FaultAddress & 0xffff); /* mask out cookie */
++
++          switch (res)
++          {
++          default:
++              if (ElanException (ctxt, EXCEPTION_INVALID_PROCESS, DMA_PROC, trap, trap->FaultSave.s.FaultAddress, res) != OP_IGNORE)
++                  return;
++              
++          case EAGAIN:
++              /* XXXX; wait on trail blazing code */
++
++          case 0:
++              break;
++          }
++      }
++      else
++      {
++          if ((res = elan3_pagefault (ctxt, &trap->FaultSave, 1)) != ESUCCESS)
++          {
++              PRINTF1 (ctxt, DBG_DPROC, "ResolveDProcTrap: elan3_pagefault failed for address %x\n",
++                       trap->FaultSave.s.FaultAddress);
++
++              if (ElanException (ctxt, EXCEPTION_INVALID_ADDR, DMA_PROC, trap, &trap->FaultSave, res) != OP_IGNORE)
++                  return;
++          }
++      }
++    }
++
++    if (! FaultHandled)
++    {
++      ElanBusError (ctxt->Device);
++
++      if (ElanException (ctxt, EXCEPTION_INVALID_ADDR, DMA_PROC, trap, &trap->FaultSave, EFAULT) != OP_IGNORE)
++          return;
++    }
++
++    switch (trap->Status.s.TrapType)
++    {
++    case MI_WaitForRemoteDescRead2:
++      /*
++       * Faulted while trying to read the dma descriptor for a read dma.
++       * Fix fault and re-issue using FaultAddress.
++       */
++      PRINTF1 (ctxt, DBG_DPROC, "ResolveDProcTrap: trapped reading a remote dma descriptor at %x.\n",
++               trap->FaultSave.s.FaultAddress);
++      
++      RestartDmaPtr (ctxt, trap->FaultSave.s.FaultAddress);
++      break;
++      
++    case MI_ExecuteDmaDescriptorForRun:
++      /*
++       * Faulted while trying to read the dma descriptor for a write dma.
++       * Fix fault and re-issue using FaultAddress.
++       */
++      PRINTF1 (ctxt, DBG_DPROC, "ResolveDProcTrap: trapped reading a write dma descriptor at %x.\n", 
++               trap->FaultSave.s.FaultAddress);
++      
++      RestartDmaPtr (ctxt, trap->FaultSave.s.FaultAddress);
++      break;
++      
++    case MI_WaitForRemoteRoutes1:
++    case MI_WaitForRemoteRoutes2:
++    case MI_SendRemoteDmaDesc:
++    case MI_SendDmaIdentify:
++    case MI_SendRemoteDmaRoutes2:
++    case MI_WaitForDmaRoutes1:
++    case MI_DmaLoop:
++    case MI_ExitDmaLoop:
++    case MI_GetDestEventValue:
++    case MI_SendFinalUnlockTrans:
++    case MI_SendNullSetEvent:
++    case MI_SendFinalSetEvent:
++    case MI_SendDmaEOP:
++      /*
++       * Faulted either fetching routes or fetching dma data.
++       * Fix fault and re-issue using FaultAddress.
++       */
++
++    case MI_SendEOPforRemoteDma:
++    case MI_LookAtRemoteAck:
++    case MI_FailedAckIfCCis0:
++      /*
++       * Possible fault when reading the remote desc into the dma data buffers
++       */
++      PRINTF0 (ctxt, DBG_DPROC, "ResolveDProcTrap:  trapped reading a dma data or fetching a route\n");
++      RestartDmaTrap (ctxt, trap);
++      break;
++      
++    case MI_DequeueSysCntxDma:
++    case MI_DequeueNonSysCntxDma:
++    case MI_RemoteDmaCommand:
++    case MI_RunDmaCommand:
++      /*
++       * It is possible that a dma can get back onto the queue while outstanding dma
++       * have not finished trapping. In this case the trap can be ignored as the dma
++       * state has been saved. It might trap again the next time it comes to the front
++       * of the queue and be fixed then.
++       */
++      PRINTF0 (ctxt, DBG_DPROC, "ResolveDProcTrap: trap after dma has finished. ignored\n");
++      break;
++      
++    default:
++      PRINTF0 (ctxt, DBG_DPROC, "ResolveDProcTrap: trapped on a write set event.\n");
++      FixupEventTrap (ctxt, DMA_PROC, trap, trap->Status.s.TrapType, &trap->FaultSave, 0);
++      break;
++    }
++}
++
++int
++DProcNeedsRestart (ELAN3_CTXT *ctxt)
++{
++    return (ctxt->ItemCount[LIST_DMA_PTR] != 0 ||
++          ctxt->ItemCount[LIST_DMA_DESC] != 0);
++}
++
++void
++RestartDProcItems (ELAN3_CTXT *ctxt)
++{
++    void      *item;
++    E3_Addr    value;
++    int              res;
++    
++    kmutex_lock (&ctxt->SwapListsLock);
++    while (ctxt->ItemCount[LIST_DMA_PTR])
++    {
++      if (! ELAN3_OP_GET_WORD_ITEM (ctxt, LIST_DMA_PTR, &item, &value))
++          ctxt->ItemCount[LIST_DMA_PTR] = 0;
++      else
++      {
++          PRINTF1 (ctxt, DBG_DPROC, "RestartDProc: issue write dma at %x\n", value);
++          PrintUserDma (ctxt, value);
++
++          res = IssueDmaCommand (ctxt, value, NULL, 0);
++          
++          if (res == ISSUE_COMMAND_RETRY)
++          {
++              ELAN3_OP_PUTBACK_ITEM (ctxt, LIST_DMA_PTR, item);
++              kmutex_unlock (&ctxt->SwapListsLock);
++              return;
++          }
++          
++          ctxt->ItemCount[LIST_DMA_PTR]--;
++          ELAN3_OP_FREE_WORD_ITEM (ctxt, item);
++      }
++    }
++    
++    while (ctxt->ItemCount[LIST_DMA_DESC])
++    {
++      if (! ELAN3_OP_GET_BLOCK_ITEM (ctxt, LIST_DMA_DESC, &item, &value))
++          ctxt->ItemCount[LIST_DMA_DESC] = 0;
++      else
++      {
++          PRINTF1 (ctxt, DBG_DPROC, "RestartDProc: issue dma desc at %x\n", value);
++          PrintUserDma (ctxt, value);
++
++          res = IssueDmaCommand (ctxt, value, item, 0);
++
++          switch (res)
++          {
++          case ISSUE_COMMAND_OK:
++              ctxt->ItemCount[LIST_DMA_DESC]--;
++              ELAN3_OP_FREE_BLOCK_ITEM (ctxt, item);
++              break;
++              
++          case ISSUE_COMMAND_RETRY:
++              ELAN3_OP_PUTBACK_ITEM (ctxt, LIST_DMA_DESC, item);
++              kmutex_unlock (&ctxt->SwapListsLock);
++              return;
++              
++          case ISSUE_COMMAND_TRAPPED:
++              ctxt->ItemCount[LIST_DMA_DESC]--;
++              /* The item will be freed off when the command port trap */
++              /* fixed up and the command successfully re-issued */
++              break;
++          }
++      }
++    }
++
++    kmutex_unlock (&ctxt->SwapListsLock);
++}
++
++void
++RestartDmaDesc(ELAN3_CTXT *ctxt, E3_DMA_BE *desc)
++{
++    kmutex_lock (&ctxt->SwapListsLock);
++    if (desc->s.dma_direction != DMA_WRITE)
++      desc->s.dma_direction = (desc->s.dma_direction & ~DMA_READ) | DMA_READ_REQUEUE;
++
++    ELAN3_OP_PUT_BLOCK_ITEM (ctxt, LIST_DMA_DESC, (E3_uint32 *) desc);
++    ctxt->ItemCount[LIST_DMA_DESC]++;
++
++    kmutex_unlock (&ctxt->SwapListsLock);
++}
++
++void
++RestartDmaTrap(ELAN3_CTXT *ctxt, DMA_TRAP *trap)
++{
++    /* Negative length DMAs are illegal, since they hangup the dma processor,
++     * if they got generated then they will have been spotted by PollForDmahungup,
++     * and delivered to us with a Dequeue  suspend address,
++     *
++     * GNAT sw-elan3/3908: Moved this check into this new function to avoid
++     * it sampling old or invalid register state
++     */
++    if (trap->Desc.s.dma_size > E3_MAX_DMA_SIZE)
++      ElanException (ctxt, EXCEPTION_BAD_DMA, DMA_PROC, trap, NULL, 0);
++    else
++      RestartDmaDesc (ctxt, &trap->Desc);
++}
++
++void
++RestartDmaPtr (ELAN3_CTXT *ctxt, E3_Addr ptr)
++{
++    kmutex_lock (&ctxt->SwapListsLock);
++    ELAN3_OP_PUT_WORD_ITEM (ctxt, LIST_DMA_PTR, ptr);
++    ctxt->ItemCount[LIST_DMA_PTR]++;
++    kmutex_unlock (&ctxt->SwapListsLock);
++}
++
++static void
++PrintUserDma (ELAN3_CTXT *ctxt, E3_Addr addr)
++{
++    E3_DMA *dma;
++
++    /* Dont call a function which takes locks unless we need to */
++    if (!(elan3_debug & DBG_DPROC))
++        return;
++
++    dma = (E3_DMA *) elan3mmu_mainaddr (ctxt->Elan3mmu, addr);
++
++    PRINTF4 (ctxt, DBG_DPROC, "DMA: type %08x size %08x source %08x dest %08x\n",
++           fuword ((int *) &dma->dma_type), fuword ((int *) &dma->dma_size), 
++           fuword ((int *) &dma->dma_source), fuword ((int *) &dma->dma_dest));
++    PRINTF4 (ctxt, DBG_DPROC, "DMA: Dest %08x %08x  Local %08x %08x\n",
++           fuword ((int *) &dma->dma_destEvent), fuword ((int *) &dma->dma_destCookieProc), 
++           fuword ((int *) &dma->dma_srcEvent), fuword ((int *) &dma->dma_srcCookieProc));
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan3/elan3mmu_generic.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan3/elan3mmu_generic.c        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan3/elan3mmu_generic.c     2005-05-11 12:10:12.405937592 -0400
+@@ -0,0 +1,3255 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: elan3mmu_generic.c,v 1.75.2.1 2004/12/14 10:19:51 mike Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/vm/elan3mmu_generic.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elanvp.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanctxt.h>
++#include <elan3/elandebug.h>
++#include <elan3/urom_addrs.h>
++#include <elan3/thread.h>
++
++#ifdef CONFIG_MPSAS
++#  define zero_all_ptbls
++#endif
++
++/*
++ * Debugging
++ */
++int   elan3mmu_debug = 0;
++
++#define       N_L3PTBL_MTX    (0x20)
++#define       N_L2PTBL_MTX    (0x40)
++#define       N_L1PTBL_MTX    (0x20)
++
++#define       L3PTBL_MTX_HASH(p) \
++      ((((uintptr_t)(p) >> 12) ^ ((uintptr_t)(p) >> 2)) & (N_L3PTBL_MTX - 1))
++static        spinlock_t l3ptbl_lock[N_L3PTBL_MTX];
++
++#define       L2PTBL_MTX_HASH(p)   \
++      ((((uintptr_t)(p) >> 12) ^ ((uintptr_t)(p) >> 2)) & (N_L2PTBL_MTX - 1))
++static        spinlock_t l2ptbl_lock[N_L2PTBL_MTX];
++
++#define       L1PTBL_MTX_HASH(p)   \
++      ((((uintptr_t)(p) >> 12) ^ ((uintptr_t)(p) >> 2)) & (N_L1PTBL_MTX - 1))
++static        spinlock_t l1ptbl_lock[N_L1PTBL_MTX];
++
++
++#define       BASE2VA(p)      ((E3_Addr)((p)->ptbl_base << 16))
++#define       VA2BASE(v)      ((u_short)(((uintptr_t)(v)) >> 16))
++
++ELAN3MMU_GLOBAL_STATS elan3mmu_global_stats;
++
++static void          elan3mmu_flush_context_filter (ELAN3_DEV *dev, void *);
++static void          elan3mmu_unload_loop (ELAN3MMU *elan3mmu, ELAN3_PTBL *ptbl, int first_valid, int nptes, int flags);
++
++static ELAN3_PTBL    *elan3mmu_create_ptbls (ELAN3_DEV *dev, int level, int attr, int keep);
++static ELAN3_PTBL    *elan3mmu_ta_to_ptbl (ELAN3MMU *elan3mmu, ELAN3_PTP *ptp);
++
++static ELAN3_PTBL    *elan3mmu_alloc_pte    (ELAN3_DEV *dev, ELAN3MMU *elan3mmu, int *idx);
++void                 elan3mmu_free_lXptbl  (ELAN3_DEV *dev, ELAN3_PTBL *ptbl);
++
++void                 elan3mmu_free_pte  (ELAN3_DEV *dev,  ELAN3MMU *elan3mmu,  ELAN3_PTBL *ptbl_ptr, int idx);
++
++static ELAN3_PTBL    *elan3mmu_alloc_l1ptbl (ELAN3_DEV *dev, int attr, ELAN3MMU *elan3mmu);
++static ELAN3_PTBL    *elan3mmu_alloc_l2ptbl (ELAN3_DEV *dev, int attr, ELAN3_PTBL *parent, ELAN3MMU *elan3mmu,
++                                          E3_Addr base, spinlock_t **plock, unsigned long *flags);
++static ELAN3_PTBL    *elan3mmu_alloc_l3ptbl (ELAN3_DEV *dev, int attr, ELAN3_PTBL *parent, ELAN3MMU *elan3mmu,
++                                          E3_Addr base, spinlock_t **plock, unsigned long *flags);
++
++static int         elan3mmu_steal_this_ptbl (ELAN3_DEV *dev, ELAN3_PTBL *l3ptbl);
++static ELAN3_PTBL    *elan3mmu_steal_l3ptbl (ELAN3_DEV *dev, int attr);
++
++static spinlock_t   *elan3mmu_ptbl_to_lock (int level, ELAN3_PTBL *ptbl);
++
++/*
++ * Encoding of MMU permissions against access type,
++ * to allow quick permission checking against access 
++ * type.
++ */
++u_char elan3mmu_permissionTable[] =
++{
++    0xcc,     /* 11001100 ELAN3_PERM_NULL        */
++    0x01,     /* 00000001 ELAN3_PERM_LOCALREAD   */
++    0x05,     /* 00000101 ELAN3_PERM_READ        */
++    0x33,     /* 00110011 ELAN3_PERM_NOREMOTE    */
++    0x37,     /* 00110111 ELAN3_PERM_REMOTEREAD  */
++    0x3f,     /* 00111111 ELAN3_PERM_REMOTEWRITE */
++    0xf7,     /* 11110111 ELAN3_PERM_REMOTEEVENT */
++    0xff,     /* 11111111 ELAN3_PERM_REMOTEALL          */
++} ;
++
++void
++elan3mmu_init()
++{
++    register int i;
++
++    HAT_PRINTF0 (1, "elan3mmu_init: initialising elan mmu\n");
++
++    for (i = 0; i < N_L1PTBL_MTX; i++)
++      spin_lock_init (&l1ptbl_lock[i]);
++
++    for (i = 0; i < N_L2PTBL_MTX; i++)
++      spin_lock_init (&l2ptbl_lock[i]);
++
++    for (i = 0; i < N_L3PTBL_MTX; i++)
++      spin_lock_init (&l3ptbl_lock[i]);
++
++    elan3mmu_global_stats.version = ELAN3MMU_STATS_VERSION;
++
++    elan3mmu_init_osdep();
++}
++
++void
++elan3mmu_fini()
++{
++    register int i;
++
++    HAT_PRINTF0 (1, "elan3mmu_fini: finalising elan mmu\n");
++
++    for (i = 0; i < N_L1PTBL_MTX; i++)
++      spin_lock_destroy (&l1ptbl_lock[i]);
++
++    for (i = 0; i < N_L2PTBL_MTX; i++)
++      spin_lock_destroy (&l2ptbl_lock[i]);
++
++    for (i = 0; i < N_L3PTBL_MTX; i++)
++      spin_lock_destroy (&l3ptbl_lock[i]);
++
++    elan3mmu_fini_osdep();
++}
++
++ELAN3MMU *
++elan3mmu_alloc (ELAN3_CTXT *ctxt)
++{
++    ELAN3MMU  *elan3mmu;
++    ELAN3_PTBL *l1ptbl;
++
++    ALLOC_ELAN3MMU (elan3mmu, TRUE);
++    
++    spin_lock_init (&elan3mmu->elan3mmu_lock);
++
++    spin_lock (&elan3mmu->elan3mmu_lock);                     /* lock_lint */
++
++    elan3mmu->elan3mmu_ergns    = NULL;
++    elan3mmu->elan3mmu_etail    = NULL;
++    elan3mmu->elan3mmu_ergnlast = NULL;
++    elan3mmu->elan3mmu_mrgns    = NULL;
++    elan3mmu->elan3mmu_mtail    = NULL;
++    elan3mmu->elan3mmu_mrgnlast = NULL;
++    elan3mmu->elan3mmu_ctxt     = ctxt;
++
++    spin_lock_init (&elan3mmu->elan3mmu_lXptbl_lock);
++    elan3mmu->elan3mmu_lXptbl   = NULL;
++
++    spin_unlock (&elan3mmu->elan3mmu_lock);                   /* lock_lint */
++
++    l1ptbl = elan3mmu_alloc_l1ptbl(ctxt->Device, 0, elan3mmu);
++
++    elan3mmu->elan3mmu_ctp      = (sdramaddr_t) 0;
++    elan3mmu->elan3mmu_dev      = ctxt->Device;
++    elan3mmu->elan3mmu_l1ptbl   = l1ptbl;
++
++    /* Ensure that there are at least some level 3 page tables,  since if a level 2 and */
++    /* a level 3 table are allocated together, then the level 3 is allocated with the NO_ALLOC */
++    /* flag,  thus there MUST be at least one that can be stolen or on the free list */
++    if (elan3mmu->elan3mmu_dev->Level[PTBL_LEVEL_3].PtblFreeList == NULL)
++      elan3mmu_create_ptbls (elan3mmu->elan3mmu_dev, PTBL_LEVEL_3, 0, 0);
++
++    HAT_PRINTF1 (1, "elan3mmu_alloc: elan3mmu %p\n", elan3mmu);
++
++    elan3mmu_alloc_osdep (elan3mmu);
++
++    return (elan3mmu);
++}
++
++void 
++elan3mmu_free (ELAN3MMU *elan3mmu)
++{
++    ELAN3MMU_RGN   *rgn;
++    ELAN3_PTBL           *l1ptbl;
++    spinlock_t           *l1lock;
++    unsigned long   l1flags;
++    unsigned long   flags;
++
++    HAT_PRINTF1 (1, "elan3mmu_free : elan3mmu %p\n", elan3mmu);
++    
++    /*
++     * Invalidate the level1 page table,  since it's already removed
++     * from the context table, there is no need to flush the tlb.
++     */
++    l1ptbl = elan3mmu->elan3mmu_l1ptbl;
++    elan3mmu->elan3mmu_l1ptbl = NULL;
++    
++    if (elan3mmu_lock_ptbl (l1ptbl, LK_PTBL_FAILOK, elan3mmu, (E3_Addr) 0, PTBL_LEVEL_1, &l1lock, &l1flags) == LK_PTBL_OK)
++    {
++      elan3mmu_l1inval (elan3mmu, l1ptbl, PTE_UNLOAD_NOFLUSH);
++      elan3mmu_free_l1ptbl (elan3mmu->elan3mmu_dev, l1ptbl, l1lock, l1flags);
++    }
++
++    /*
++     * Free of any permission regions.
++     */
++    spin_lock (&elan3mmu->elan3mmu_lock);                                     /* lock_lint */
++    while ((rgn = elan3mmu->elan3mmu_mrgns) != NULL)
++    {
++      spin_lock_irqsave (&elan3mmu->elan3mmu_dev->IntrLock, flags);           /* lock_lint */
++      elan3mmu_removergn_elan (elan3mmu, rgn->rgn_ebase);
++      elan3mmu_removergn_main (elan3mmu, rgn->rgn_mbase);
++      spin_unlock_irqrestore (&elan3mmu->elan3mmu_dev->IntrLock, flags);      /* lock_lint */
++      
++      FREE_ELAN3MMU_RGN (rgn);
++    }
++    elan3mmu->elan3mmu_mrgnlast = NULL;
++    elan3mmu->elan3mmu_ergnlast = NULL;
++
++    /* 
++     * Free the lXptbl list
++     */
++    ASSERT (elan3mmu->elan3mmu_lXptbl == NULL); /* XXXX MRH need to add list removal */
++ 
++    elan3mmu->elan3mmu_lXptbl = NULL;
++    spin_lock_destroy (&elan3mmu->elan3mmu_lXptbl_lock);
++
++
++    spin_unlock (&elan3mmu->elan3mmu_lock);                                   /* lock_lint */
++
++    spin_lock_destroy (&elan3mmu->elan3mmu_lock);
++
++    FREE_ELAN3MMU (elan3mmu);
++}
++
++/*================================================================================*/
++/* Interface routines to device driver */
++static void
++elan3mmu_flush_context_filter (ELAN3_DEV *dev, void *arg)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++    ASSERT ((read_reg32 (dev, Exts.InterruptReg) & (INT_DiscardingSysCntx | INT_DiscardingNonSysCntx)) == 
++          (INT_DiscardingSysCntx | INT_DiscardingNonSysCntx));
++
++    dev->FilterHaltQueued = 0;
++
++    write_reg32 (dev, Input_Context_Fil_Flush, 0);
++
++    HAT_PRINTF0 (1, "elan3mmu_flush_context_filter completed\n");
++
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++}
++
++void
++elan3mmu_set_context_filter (ELAN3_DEV *dev, int ctx, int disabled, E3_uint32 Pend, E3_uint32 *Maskp)
++{
++    int         mctx = ctx & MAX_ROOT_CONTEXT_MASK;
++    sdramaddr_t ctp  = dev->ContextTable + mctx * sizeof (E3_ContextControlBlock);
++
++    ASSERT (SPINLOCK_HELD (&dev->IntrLock));
++
++    ASSERT ((mctx < 32 || mctx >= ELAN3_KCOMM_BASE_CONTEXT_NUM) ? (ctx & SYS_CONTEXT_BIT) : ! (ctx & SYS_CONTEXT_BIT));
++
++    elan3_sdram_writel (dev, ctp + offsetof (E3_ContextControlBlock, filter), 
++                ((ctx & SYS_CONTEXT_BIT) ? E3_CCB_CNTX0 : 0) | (disabled ? E3_CCB_DISCARD_ALL : 0));
++
++    HAT_PRINTF4 (1, "elan3mmu_set_context_filter: ctx %x [%lx] -> %s (%x)\n", ctx, ctp,
++               disabled ? "up" : "down", elan3_sdram_readl (dev, ctp + offsetof (E3_ContextControlBlock, filter)));
++
++    /* queue a halt operation to flush the context filter while the inputter is halted */
++    if (dev->FilterHaltQueued == 0)
++    {
++      dev->FilterHaltQueued = 1;
++      QueueHaltOperation (dev, Pend, Maskp, INT_DiscardingSysCntx | INT_DiscardingNonSysCntx, 
++                          elan3mmu_flush_context_filter, NULL);
++    }
++}
++
++int
++elan3mmu_attach (ELAN3_DEV *dev, int ctx, ELAN3MMU *elan3mmu, sdramaddr_t routeTable, E3_uint32 routeMask)
++{
++    sdramaddr_t ctp;
++    ELAN3_PTP    trootptp;
++
++    ASSERT (SPINLOCK_HELD (&dev->IntrLock));
++
++    ctx &= MAX_ROOT_CONTEXT_MASK;                                             /* Mask out all high bits in context */
++    
++    if (ctx < 0 || ctx >= dev->ContextTableSize)
++      return (EINVAL);
++
++    ctp = dev->ContextTable + ctx * sizeof (E3_ContextControlBlock);
++    
++    trootptp = elan3_readptp (dev, ctp + offsetof (E3_ContextControlBlock, rootPTP));
++    
++    if (ELAN3_PTP_TYPE(trootptp) != ELAN3_ET_INVALID)
++      return (EBUSY);
++
++    elan3mmu->elan3mmu_ctp = ctp;
++    
++    trootptp = PTBL_TO_PTADDR (elan3mmu->elan3mmu_l1ptbl) | ELAN3_ET_PTP;
++    
++    HAT_PRINTF4 (1, "elan3mmu_attach: ctp at %08lx : trootptp=%08x VPT_ptr=%08lx VPT_mask=%08x\n",
++               ctp, trootptp, routeTable, routeMask);
++    
++    elan3_writeptp (dev, ctp + offsetof (E3_ContextControlBlock, rootPTP), trootptp);
++    elan3_writeptp (dev, ctp + offsetof (E3_ContextControlBlock, VPT_ptr), routeTable);
++    elan3_writeptp (dev, ctp + offsetof (E3_ContextControlBlock, VPT_mask), routeMask);
++    
++    return (ESUCCESS);
++}
++
++void
++elan3mmu_detach (ELAN3_DEV *dev, int ctx)
++{
++    ELAN3_PTP    invalidptp = ELAN3_INVALID_PTP;
++    sdramaddr_t ctp;
++    
++    ctx &= MAX_ROOT_CONTEXT_MASK;                                             /* Mask out all high bits in context */
++    
++    if (ctx < 0 || ctx >= dev->ContextTableSize)
++      return;
++    
++    ctp = dev->ContextTable + ctx * sizeof (E3_ContextControlBlock);
++    
++    HAT_PRINTF1 (1, "elan3mmu_detach: clearing ptp at %lx\n", ctp);
++    
++    elan3_writeptp (dev, ctp + offsetof (E3_ContextControlBlock, rootPTP), invalidptp);
++    elan3_writeptp (dev, ctp + offsetof (E3_ContextControlBlock, VPT_mask), 0);
++    elan3_writeptp (dev, ctp + offsetof (E3_ContextControlBlock, VPT_ptr), 0);
++    
++    ElanFlushTlb (dev);
++}
++
++int
++elan3mmu_reference (ELAN3MMU *elan3mmu, int ctx)
++{
++    ELAN3_DEV              *dev = elan3mmu->elan3mmu_dev;
++    sdramaddr_t            ctp;
++    E3_ContextControlBlock ccb;
++    ELAN3_PTP               trootptp;
++
++    ctx &= MAX_ROOT_CONTEXT_MASK;                                             /* Mask out all high bits in context */
++    
++    if (ctx < 0 || ctx >= dev->ContextTableSize)
++      return (EINVAL);
++
++    ctp = dev->ContextTable + ctx * sizeof (E3_ContextControlBlock);
++
++    trootptp = elan3_readptp (dev, ctp + offsetof (E3_ContextControlBlock, rootPTP));
++    
++    if (ELAN3_PTP_TYPE(trootptp) != ELAN3_ET_INVALID)
++      return (EBUSY);
++    
++    elan3_sdram_copyl_from_sdram (dev, elan3mmu->elan3mmu_ctp, &ccb, sizeof (E3_ContextControlBlock));
++    elan3_sdram_copyl_to_sdram (dev, &ccb, ctp, sizeof (E3_ContextControlBlock));
++    
++    return (ESUCCESS);
++    
++}
++/*================================================================================*/
++/* Elan permission regions. */
++
++/* elan address region management */
++ELAN3MMU_RGN *
++elan3mmu_findrgn_elan (ELAN3MMU *elan3mmu,
++                     E3_Addr addr, int tail)
++{
++    ELAN3MMU_RGN *next = NULL;
++    ELAN3MMU_RGN *rgn;
++    ELAN3MMU_RGN *hirgn;
++    ELAN3MMU_RGN *lorgn;
++    E3_Addr       base;
++    E3_Addr       lastaddr;
++    int                 forward;
++
++    ASSERT (SPINLOCK_HELD (&elan3mmu->elan3mmu_dev->IntrLock) || SPINLOCK_HELD (&elan3mmu->elan3mmu_lock));
++
++    if (elan3mmu->elan3mmu_ergns == NULL)
++      return (NULL);
++
++    rgn = elan3mmu->elan3mmu_ergnlast;
++    if (rgn == NULL)
++      rgn = elan3mmu->elan3mmu_ergns;
++
++    forward = 0;
++    if ((u_long) (base = rgn->rgn_ebase) < (u_long)addr)
++    {
++      if ((u_long)addr <= ((u_long) base + rgn->rgn_len - 1))
++          return (rgn);                                       /* ergnlast contained addr */
++
++      hirgn = elan3mmu->elan3mmu_etail;
++
++      if ((u_long) (lastaddr = (hirgn->rgn_ebase + hirgn->rgn_len - 1)) < (u_long) addr)
++          return (tail ? hirgn : NULL);                       /* addr is out of range */
++      
++      if ((u_long) (addr - base) > (u_long) (lastaddr - addr))
++          rgn = hirgn;
++      else
++      {
++          rgn = rgn->rgn_enext;
++          forward++;
++      }
++    }
++    else
++    {
++      lorgn = elan3mmu->elan3mmu_ergns;
++
++      if ((u_long)lorgn->rgn_ebase > (u_long) addr)
++          return (lorgn);                                     /* lowest regions is higher than addr */
++      if ((u_long)(addr - lorgn->rgn_ebase) < (u_long) (base - addr))
++      {
++          rgn = lorgn;                                        /* search forward from head */
++          forward++;
++      }
++    }
++    if (forward)
++    {
++      while ((u_long)(rgn->rgn_ebase + rgn->rgn_len - 1) < (u_long)addr)
++          rgn = rgn->rgn_enext;
++
++      if ((u_long)rgn->rgn_ebase <= (u_long)addr)
++          elan3mmu->elan3mmu_ergnlast = rgn;
++      return (rgn);
++    }
++    else
++    {
++      while ((u_long)rgn->rgn_ebase > (u_long)addr)
++      {
++          next = rgn;
++          rgn = rgn->rgn_eprev;
++      }
++
++      if ((u_long) (rgn->rgn_ebase + rgn->rgn_len - 1) < (u_long)addr)
++          return (next);
++      else
++      {
++          elan3mmu->elan3mmu_ergnlast = rgn;
++          return (rgn);
++      }
++    }
++}
++
++int
++elan3mmu_addrgn_elan (ELAN3MMU *elan3mmu, ELAN3MMU_RGN *nrgn)
++{
++    ELAN3MMU_RGN *rgn   = elan3mmu_findrgn_elan (elan3mmu, nrgn->rgn_ebase, 1);
++    E3_Addr       nbase = nrgn->rgn_ebase;
++    E3_Addr     ntop  = nbase + nrgn->rgn_len - 1; /* avoid wrap */
++    E3_Addr     base;
++
++    ASSERT (SPINLOCK_HELD (&elan3mmu->elan3mmu_dev->IntrLock) && SPINLOCK_HELD (&elan3mmu->elan3mmu_lock));
++
++    if (rgn == NULL)
++    {
++      elan3mmu->elan3mmu_ergns = elan3mmu->elan3mmu_etail = nrgn;
++      nrgn->rgn_enext = nrgn->rgn_eprev = NULL;
++    }
++    else
++    {
++      base = rgn->rgn_ebase;
++
++      if ((u_long)(base + rgn->rgn_len - 1) < (u_long)nbase)  /* top of region below requested address */
++      {                                                       /* so insert after region (and hence at end */
++          nrgn->rgn_eprev = rgn;                              /* of list */
++          nrgn->rgn_enext = NULL;
++          rgn->rgn_enext = elan3mmu->elan3mmu_etail = nrgn;
++      }
++      else
++      {
++          if ((u_long)nbase >= (u_long)base || (u_long)ntop >= (u_long)base)
++              return (-1);                                    /* overlapping region */
++
++          nrgn->rgn_enext = rgn;                              /* insert before region */
++          nrgn->rgn_eprev = rgn->rgn_eprev;
++          rgn->rgn_eprev  = nrgn;
++          if (elan3mmu->elan3mmu_ergns == rgn)
++              elan3mmu->elan3mmu_ergns = nrgn;
++          else
++              nrgn->rgn_eprev->rgn_enext = nrgn;
++      }
++    }
++    elan3mmu->elan3mmu_ergnlast = nrgn;
++    
++    return (0);
++}
++
++ELAN3MMU_RGN *
++elan3mmu_removergn_elan (ELAN3MMU *elan3mmu, E3_Addr addr)
++{
++    ELAN3MMU_RGN *rgn = elan3mmu_findrgn_elan (elan3mmu, addr, 0);
++    
++    ASSERT (SPINLOCK_HELD (&elan3mmu->elan3mmu_dev->IntrLock) && SPINLOCK_HELD (&elan3mmu->elan3mmu_lock));
++
++    if (rgn == NULL || rgn->rgn_ebase != addr)
++      return (NULL);
++    
++    elan3mmu->elan3mmu_ergnlast = rgn->rgn_enext;
++    if (rgn == elan3mmu->elan3mmu_etail)
++      elan3mmu->elan3mmu_etail = rgn->rgn_eprev;
++    else
++      rgn->rgn_enext->rgn_eprev = rgn->rgn_eprev;
++    
++    if (rgn == elan3mmu->elan3mmu_ergns)
++      elan3mmu->elan3mmu_ergns = rgn->rgn_enext;
++    else
++      rgn->rgn_eprev->rgn_enext = rgn->rgn_enext;
++
++    return (rgn);
++}
++
++ELAN3MMU_RGN *
++elan3mmu_rgnat_elan (ELAN3MMU *elan3mmu, E3_Addr addr)
++{
++    ELAN3MMU_RGN *rgn = elan3mmu_findrgn_elan (elan3mmu, addr, 0);
++    E3_Addr       base;
++
++    if (rgn != NULL && (u_long)(base = rgn->rgn_ebase) <= (u_long)addr && (u_long)addr <= (u_long)(base + rgn->rgn_len - 1))
++      return (rgn);
++    return (NULL);
++}
++
++/* main address region management */
++ELAN3MMU_RGN *
++elan3mmu_findrgn_main (ELAN3MMU *elan3mmu,
++                     caddr_t addr, int tail)
++{
++    ELAN3MMU_RGN *next = NULL;
++    ELAN3MMU_RGN *rgn;
++    ELAN3MMU_RGN *hirgn;
++    ELAN3MMU_RGN *lorgn;
++    caddr_t       lastaddr;
++    caddr_t       base;
++    int                 forward;
++
++    ASSERT (SPINLOCK_HELD (&elan3mmu->elan3mmu_dev->IntrLock) || SPINLOCK_HELD (&elan3mmu->elan3mmu_lock));
++
++    if (elan3mmu->elan3mmu_mrgns == NULL)
++      return (NULL);
++
++    rgn = elan3mmu->elan3mmu_mrgnlast;
++    if (rgn == NULL)
++      rgn = elan3mmu->elan3mmu_mrgns;
++
++    forward = 0;
++    if ((base = rgn->rgn_mbase) < addr)
++    {
++      if (addr <= (base + rgn->rgn_len - 1))
++          return (rgn);                                       /* ergnlast contained addr */
++
++      hirgn = elan3mmu->elan3mmu_mtail;
++      if ((lastaddr = hirgn->rgn_mbase + hirgn->rgn_len - 1) < addr)
++          return (tail ? hirgn : NULL);                       /* addr is out of range */
++      
++      if ((addr - base) > (lastaddr - addr))
++          rgn = hirgn;
++      else
++      {
++          rgn = rgn->rgn_mnext;
++          forward++;
++      }
++    }
++    else
++    {
++      lorgn = elan3mmu->elan3mmu_mrgns;
++      if (lorgn->rgn_mbase > addr)
++          return (lorgn);                                     /* lowest regions is higher than addr */
++      if ((addr - lorgn->rgn_mbase) < (base - addr))
++      {
++          rgn = lorgn;                                        /* search forward from head */
++          forward++;
++      }
++    }
++    if (forward)
++    {
++      while ((rgn->rgn_mbase + rgn->rgn_len - 1) < addr)
++          rgn = rgn->rgn_mnext;
++
++      if (rgn->rgn_mbase <= addr)
++          elan3mmu->elan3mmu_mrgnlast = rgn;
++      return (rgn);
++    }
++    else
++    {
++      while (rgn->rgn_mbase > addr)
++      {
++          next = rgn;
++          rgn = rgn->rgn_mprev;
++      }
++      if ((rgn->rgn_mbase + rgn->rgn_len - 1) < addr)
++          return (next);
++      else
++      {
++          elan3mmu->elan3mmu_mrgnlast = rgn;
++          return (rgn);
++      }
++    }
++}
++
++int
++elan3mmu_addrgn_main (ELAN3MMU *elan3mmu, ELAN3MMU_RGN *nrgn)
++{
++    ELAN3MMU_RGN *rgn   = elan3mmu_findrgn_main (elan3mmu, nrgn->rgn_mbase, 1);
++    caddr_t       nbase = nrgn->rgn_mbase;
++    caddr_t     ntop  = nbase + nrgn->rgn_len - 1;
++    caddr_t     base;
++
++    ASSERT (SPINLOCK_HELD (&elan3mmu->elan3mmu_dev->IntrLock) && SPINLOCK_HELD (&elan3mmu->elan3mmu_lock));
++
++    if (rgn == NULL)
++    {
++      elan3mmu->elan3mmu_mrgns = elan3mmu->elan3mmu_mtail = nrgn;
++      nrgn->rgn_mnext = nrgn->rgn_mprev = NULL;
++    }
++    else
++    {
++      base = rgn->rgn_mbase;
++
++      if ((base + rgn->rgn_len - 1) < nbase)                  /* top of region below requested address */
++      {                                                       /* so insert after region (and hence at end */
++          nrgn->rgn_mprev = rgn;                              /* of list */
++          nrgn->rgn_mnext = NULL;
++          rgn->rgn_mnext = elan3mmu->elan3mmu_mtail = nrgn;
++      }
++      else
++      {
++          if (nbase >= base || ntop >= base)
++              return (-1);                                    /* overlapping region */
++
++          nrgn->rgn_mnext = rgn;                              /* insert before region */
++          nrgn->rgn_mprev = rgn->rgn_mprev;
++          rgn->rgn_mprev  = nrgn;
++          if (elan3mmu->elan3mmu_mrgns == rgn)
++              elan3mmu->elan3mmu_mrgns = nrgn;
++          else
++              nrgn->rgn_mprev->rgn_mnext = nrgn;
++      }
++    }
++    elan3mmu->elan3mmu_mrgnlast = nrgn;
++    
++    return (0);
++}
++
++ELAN3MMU_RGN *
++elan3mmu_removergn_main (ELAN3MMU *elan3mmu, caddr_t addr)
++{
++    ELAN3MMU_RGN *rgn = elan3mmu_findrgn_main (elan3mmu, addr, 0);
++    
++    ASSERT (SPINLOCK_HELD (&elan3mmu->elan3mmu_dev->IntrLock) && SPINLOCK_HELD (&elan3mmu->elan3mmu_lock));
++
++    if (rgn == NULL || rgn->rgn_mbase != addr)
++      return (NULL);
++    
++    elan3mmu->elan3mmu_mrgnlast = rgn->rgn_mnext;
++    if (rgn == elan3mmu->elan3mmu_mtail)
++      elan3mmu->elan3mmu_mtail = rgn->rgn_mprev;
++    else
++      rgn->rgn_mnext->rgn_mprev = rgn->rgn_mprev;
++    
++    if (rgn == elan3mmu->elan3mmu_mrgns)
++      elan3mmu->elan3mmu_mrgns = rgn->rgn_mnext;
++    else
++      rgn->rgn_mprev->rgn_mnext = rgn->rgn_mnext;
++
++    return (rgn);
++}
++
++ELAN3MMU_RGN *
++elan3mmu_rgnat_main (ELAN3MMU *elan3mmu, caddr_t addr)
++{
++    ELAN3MMU_RGN *rgn = elan3mmu_findrgn_main (elan3mmu, addr, 0);
++    caddr_t       base;
++
++    if (rgn != NULL && (base = rgn->rgn_mbase) <= addr && addr <= (base + rgn->rgn_len - 1))
++      return (rgn);
++    return (NULL);
++}
++
++int
++elan3mmu_setperm (ELAN3MMU *elan3mmu,
++                caddr_t   maddr,
++                E3_Addr   eaddr,
++                u_int     len,
++                u_int     perm)
++{
++    ELAN3_DEV     *dev = elan3mmu->elan3mmu_dev;
++    ELAN3MMU_RGN *nrgn;
++    unsigned long  flags;
++
++    HAT_PRINTF4 (1, "elan3mmu_setperm: user %p elan %08x len %x perm %x\n", maddr, eaddr, len, perm);
++
++    if ((((uintptr_t) maddr) & PAGEOFFSET) || (eaddr & PAGEOFFSET) || (len & PAGEOFFSET)) 
++    {
++        HAT_PRINTF0 (1, "elan3mmu_setperm:  alignment failure\n");
++      return (EINVAL);
++    }
++
++    if (((uintptr_t) maddr + len - 1) < (uintptr_t) maddr || ((u_long)eaddr + len - 1) < (u_long)eaddr) 
++    {
++      HAT_PRINTF0 (1, "elan3mmu_setperm:  range failure\n");
++      return (EINVAL);
++    }
++
++    ALLOC_ELAN3MMU_RGN(nrgn, TRUE);
++    
++    spin_lock (&elan3mmu->elan3mmu_lock);
++    nrgn->rgn_mbase = maddr;
++    nrgn->rgn_ebase = eaddr;
++    nrgn->rgn_len   = len;
++    nrgn->rgn_perm  = perm;
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++    if (elan3mmu_addrgn_elan (elan3mmu, nrgn) < 0)
++    {
++      HAT_PRINTF0 (1, "elan3mmu_setperm:  elan address exists\n");
++      spin_unlock_irqrestore (&dev->IntrLock, flags);
++      spin_unlock (&elan3mmu->elan3mmu_lock);
++
++      FREE_ELAN3MMU_RGN (nrgn);
++      return (EINVAL);
++    }
++    
++    if (elan3mmu_addrgn_main (elan3mmu, nrgn) < 0)
++    {
++      HAT_PRINTF0 (1, "elan3mmu_setperm:  main address exists\n");
++      elan3mmu_removergn_elan (elan3mmu, eaddr);
++
++      spin_unlock_irqrestore (&dev->IntrLock, flags);
++      spin_unlock (&elan3mmu->elan3mmu_lock);
++
++      FREE_ELAN3MMU_RGN (nrgn);
++      return (EINVAL);
++    }
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++    spin_unlock (&elan3mmu->elan3mmu_lock);
++
++    return (ESUCCESS);
++}
++
++void
++elan3mmu_clrperm (ELAN3MMU *elan3mmu,
++                E3_Addr   addr,
++                u_int     len)
++{
++    E3_Addr       raddr;
++    E3_Addr       rtop;
++    ELAN3MMU_RGN *nrgn;
++    ELAN3MMU_RGN *rgn;
++    ELAN3MMU_RGN *rgn_next;
++    u_int       ssize;
++    unsigned long flags;
++    int                 res;
++
++    HAT_PRINTF2 (1, "elan3mmu_clrperm: elan %08x len %x\n", addr, len);
++
++    raddr = (addr & PAGEMASK);
++    rtop = ((addr + len - 1) & PAGEMASK) + PAGEOFFSET;
++
++    ALLOC_ELAN3MMU_RGN (nrgn, TRUE);
++
++    spin_lock (&elan3mmu->elan3mmu_lock);
++    
++    for (rgn = elan3mmu_findrgn_elan (elan3mmu, addr, 0); rgn != NULL; rgn = rgn_next)
++    {
++      if (rtop < rgn->rgn_ebase)                              /* rtop was in a gap */
++          break;
++      
++      rgn_next = rgn->rgn_enext;                              /* Save next region pointer */
++      
++      if (raddr <= rgn->rgn_ebase && rtop >= (rgn->rgn_ebase + rgn->rgn_len - 1)) 
++      {
++          /* whole region is cleared */
++          elan3mmu_unload (elan3mmu, rgn->rgn_ebase, rgn->rgn_len, PTE_UNLOAD);
++          
++          spin_lock_irqsave (&elan3mmu->elan3mmu_dev->IntrLock, flags);
++          elan3mmu_removergn_elan (elan3mmu, rgn->rgn_ebase);
++          elan3mmu_removergn_main (elan3mmu, rgn->rgn_mbase);
++          spin_unlock_irqrestore (&elan3mmu->elan3mmu_dev->IntrLock, flags);
++
++          FREE_ELAN3MMU_RGN (rgn);
++      }
++      else if (raddr <= rgn->rgn_ebase)
++      {
++          /* clearing at beginning, so shrink size and increment base ptrs */
++          ssize = rtop - rgn->rgn_ebase + 1;
++
++          elan3mmu_unload (elan3mmu, rgn->rgn_ebase, ssize, PTE_UNLOAD);
++          
++          spin_lock_irqsave (&elan3mmu->elan3mmu_dev->IntrLock, flags);
++          rgn->rgn_mbase += ssize;
++          rgn->rgn_ebase += ssize;
++          rgn->rgn_len   -= ssize;
++          spin_unlock_irqrestore (&elan3mmu->elan3mmu_dev->IntrLock, flags);
++          
++      }
++      else if (rtop >= (rgn->rgn_ebase + rgn->rgn_len - 1))
++      {
++          /* clearing at end, so just shrink length of region */
++          ssize = ((rgn->rgn_ebase + rgn->rgn_len - 1) - raddr) + 1;
++
++          elan3mmu_unload (elan3mmu, raddr, ssize, PTE_UNLOAD);
++
++          spin_lock_irqsave (&elan3mmu->elan3mmu_dev->IntrLock, flags);
++          rgn->rgn_len -= ssize;
++          spin_unlock_irqrestore (&elan3mmu->elan3mmu_dev->IntrLock, flags);
++      }
++      else
++      {
++          /* the section to go is in the middle,  so need to  */
++          /* split it into two regions */
++          elan3mmu_unload (elan3mmu, raddr, rtop - raddr + 1, PTE_UNLOAD);
++
++          spin_lock_irqsave (&elan3mmu->elan3mmu_dev->IntrLock, flags);
++
++          ASSERT (nrgn != NULL);
++
++          nrgn->rgn_mbase = rgn->rgn_mbase + (rtop - rgn->rgn_ebase + 1);;
++          nrgn->rgn_ebase = rtop + 1;
++          nrgn->rgn_len   = ((rgn->rgn_ebase + rgn->rgn_len - 1) - rtop);
++          nrgn->rgn_perm  = rgn->rgn_perm;
++
++          rgn->rgn_len = (raddr - rgn->rgn_ebase);            /* shrink original region */
++
++          res = elan3mmu_addrgn_elan (elan3mmu, nrgn);        /* insert new region */
++          ASSERT (res == 0);                                  /* which cannot fail */
++
++          res = elan3mmu_addrgn_main (elan3mmu, nrgn);        
++          ASSERT (res == 0);
++          spin_unlock_irqrestore (&elan3mmu->elan3mmu_dev->IntrLock, flags);
++
++          nrgn = NULL;
++      }
++    }
++    spin_unlock (&elan3mmu->elan3mmu_lock);
++
++    if (nrgn != NULL)
++      FREE_ELAN3MMU_RGN (nrgn);
++}
++
++int
++elan3mmu_checkperm (ELAN3MMU *elan3mmu,
++                  E3_Addr   addr,
++                  u_int     len,
++                  u_int     access)
++{
++    E3_Addr    raddr = (((E3_Addr) addr) & PAGEMASK);
++    u_int        rtop = ((addr + len - 1) & PAGEMASK) + PAGEOFFSET;
++    u_int      rsize = rtop - raddr + 1;
++    ELAN3MMU_RGN *rgn;
++
++    HAT_PRINTF3 (1, "elan3mmu_checkperm: user %08x len %x access %x\n", addr, len, access);
++    
++    
++    if ((raddr + rsize - 1) < raddr)
++      return (ENOMEM);
++    
++    spin_lock (&elan3mmu->elan3mmu_lock);
++    if ((rgn = elan3mmu_rgnat_elan (elan3mmu, raddr)) == (ELAN3MMU_RGN *) NULL)
++    {
++      spin_unlock (&elan3mmu->elan3mmu_lock);
++      return (ENOMEM);
++    }
++    else
++    {
++      register int ssize;
++      
++      for (; rsize != 0; rsize -= ssize, raddr += ssize)
++      {
++          if (raddr > (rgn->rgn_ebase + rgn->rgn_len - 1))
++          {
++              rgn  = rgn->rgn_enext;
++              
++              if (rgn == NULL || raddr != rgn->rgn_ebase)
++              {
++                  spin_unlock (&elan3mmu->elan3mmu_lock);
++                  return (ENOMEM);
++              }
++          }
++          if ((raddr + rsize - 1) > (rgn->rgn_ebase + rgn->rgn_len - 1))
++              ssize = ((rgn->rgn_ebase + rgn->rgn_len - 1) - raddr) + 1;
++          else
++              ssize = rsize;
++          
++          HAT_PRINTF4 (1, "elan3mmu_checkperm : rgn %x -> %x perm %x access %x\n",
++                       rgn->rgn_ebase, rgn->rgn_ebase + rgn->rgn_len, rgn->rgn_perm, access);
++
++          if (ELAN3_INCOMPAT_ACCESS (rgn->rgn_perm, access))
++          {
++              spin_unlock (&elan3mmu->elan3mmu_lock);
++              return (EACCES);
++          }
++      }
++    }
++    
++    spin_unlock (&elan3mmu->elan3mmu_lock);
++    
++    return (ESUCCESS);
++}
++
++caddr_t
++elan3mmu_mainaddr (ELAN3MMU *elan3mmu, E3_Addr addr)
++{
++    ELAN3MMU_RGN *rgn;
++    caddr_t     raddr;
++    
++    spin_lock (&elan3mmu->elan3mmu_lock);
++    if ((rgn = elan3mmu_rgnat_elan (elan3mmu, addr)) == (ELAN3MMU_RGN *) NULL)
++      raddr = NULL;
++    else
++      raddr = rgn->rgn_mbase + (addr - rgn->rgn_ebase);
++    spin_unlock (&elan3mmu->elan3mmu_lock);
++
++    return (raddr);
++}
++
++E3_Addr
++elan3mmu_elanaddr (ELAN3MMU *elan3mmu, caddr_t addr)
++{
++    ELAN3MMU_RGN *rgn;
++    E3_Addr       raddr;
++
++    spin_lock (&elan3mmu->elan3mmu_lock);
++    if ((rgn = elan3mmu_rgnat_main (elan3mmu, addr)) == (ELAN3MMU_RGN *) NULL)
++      raddr = (E3_Addr) 0;
++    else
++      raddr = rgn->rgn_ebase + (addr - rgn->rgn_mbase);
++    spin_unlock (&elan3mmu->elan3mmu_lock);
++
++    return (raddr);
++}
++
++void
++elan3mmu_displayrgns(ELAN3MMU *elan3mmu)
++{
++    ELAN3MMU_RGN *rgn;
++
++    spin_lock (&elan3mmu->elan3mmu_lock);
++    HAT_PRINTF0 (1, "elan3mmu_displayrgns: main regions\n");
++    for (rgn = elan3mmu->elan3mmu_mrgns; rgn; rgn = (rgn->rgn_mnext == elan3mmu->elan3mmu_mrgns) ? NULL : rgn->rgn_mnext)
++      HAT_PRINTF5 (1, "    RGN %p ebase %08x mbase %p len %08x perm %08x\n", rgn, rgn->rgn_ebase, rgn->rgn_mbase, rgn->rgn_len, rgn->rgn_perm);
++    HAT_PRINTF0 (1, "elan3mmu_displayrgns: elan regions\n");
++    for (rgn = elan3mmu->elan3mmu_ergns; rgn; rgn = (rgn->rgn_enext == elan3mmu->elan3mmu_ergns) ? NULL : rgn->rgn_enext)
++      HAT_PRINTF5 (1, "    RGN %p ebase %08x mbase %p len %08x perm %08x\n", rgn, rgn->rgn_ebase, rgn->rgn_mbase, rgn->rgn_len, rgn->rgn_perm);
++
++    spin_unlock (&elan3mmu->elan3mmu_lock);
++}
++
++/*============================================================================*/
++/* Private functions */
++#define ELAN3_PTE_IS_VALID(ptbl, pte) \
++          ((ptbl->ptbl_flags & PTBL_KERNEL) ? \
++          (pte&(~ELAN3_PTE_REF)) != elan3mmu_kernel_invalid_pte(ptbl->ptbl_elan3mmu) : \
++          ELAN3_PTE_VALID(pte))
++
++void
++elan3mmu_expand (ELAN3MMU *elan3mmu, E3_Addr addr, int len, int level, int attr)
++{
++    ELAN3_PTBL          *ptbl;
++    sdramaddr_t               pte;
++    spinlock_t               *lock;
++    u_int             span;
++    unsigned long       flags;
++
++    HAT_PRINTF3 (1, "elan3mmu_expand: elan3mmu %p %08x to %08x\n", elan3mmu, 
++               addr, addr + len);
++
++    for ( ; len != 0; addr += span, len -= span)
++    {
++      /* as we asked for level 3 we know its a pte */
++      pte = elan3mmu_ptealloc (elan3mmu, addr, level, &ptbl, &lock, attr, &flags);
++
++      switch (level)
++      {
++      case PTBL_LEVEL_3:
++          span = MIN(len, ELAN3_L3_PTSIZE - ((E3_Addr) addr & ELAN3_L3_PTOFFSET));
++          break;
++      case PTBL_LEVEL_2:
++          span = MIN(len, ELAN3_L2_PTSIZE - ((E3_Addr) addr & ELAN3_L2_PTOFFSET));
++          break;
++      default:
++          span = len;
++          break;
++      }
++      
++      if (pte != (sdramaddr_t) 0)
++          elan3mmu_unlock_ptbl (ptbl, lock, flags);
++    }
++}
++
++void
++elan3mmu_reserve (ELAN3MMU *elan3mmu, E3_Addr addr, u_int npages, sdramaddr_t *ptes)
++{
++    ELAN3_PTBL          *ptbl;
++    sdramaddr_t               pte;
++    spinlock_t               *lock;
++    u_int             span;
++    int                       len;
++    int                       i;
++    unsigned long       flags;
++
++    HAT_PRINTF3 (1, "elan3mmu_reserve: elan3mmu %p %08x to %08x\n", elan3mmu, 
++               addr, addr + (npages << ELAN3_PAGE_SHIFT));
++
++    for (len = (npages << ELAN3_PAGE_SHIFT); len != 0; addr += span, len -= span)
++    {
++      /* as we asked for level 3 we know its a pte */
++      pte = elan3mmu_ptealloc (elan3mmu, addr, 3, &ptbl, &lock, 0, &flags);
++
++      span = MIN(len, ELAN3_L3_PTSIZE - ((E3_Addr) addr & ELAN3_L3_PTOFFSET));
++      
++      if (ptes != NULL)
++      {
++          for (i = 0; i < span; i += ELAN3_PAGE_SIZE, pte += ELAN3_PTE_SIZE)
++              *ptes++ = pte;
++          ptbl->ptbl_valid += (span >> ELAN3_PAGE_SHIFT);
++
++          HAT_PRINTF4 (2, "elan3mmu_reserve: inc valid for level %d ptbl %p to %d   (%d)\n", 
++                   PTBL_LEVEL(ptbl->ptbl_flags), ptbl, ptbl->ptbl_valid, (span >> ELAN3_PAGE_SHIFT));
++
++      }
++
++      elan3mmu_unlock_ptbl (ptbl, lock, flags);
++    }
++}
++
++void
++elan3mmu_release (ELAN3MMU *elan3mmu, E3_Addr addr, u_int npages, sdramaddr_t *ptes)
++{
++    ELAN3_DEV           *dev = elan3mmu->elan3mmu_dev;
++    ELAN3_PTBL          *ptbl;
++    sdramaddr_t               pte;
++    ELAN3_PTE         tpte;
++    spinlock_t               *lock;
++    u_int             span;
++    int                       len;
++    int                       i;
++    int                       level;
++    unsigned long       flags;
++    
++    HAT_PRINTF3 (1, "elan3mmu_release: elan3mmu %p %08x to %08x\n", elan3mmu, 
++               addr, addr + (npages << ELAN3_PAGE_SHIFT));
++
++    if (ptes == NULL)
++      return;
++
++    tpte = elan3mmu_kernel_invalid_pte (elan3mmu);
++
++    for (len = (npages << ELAN3_PAGE_SHIFT); len != 0; addr += span, len -= span)
++    {
++      /* as we asked for level 3 we know its a pte */
++      pte = elan3mmu_ptefind(elan3mmu, addr, &level, &ptbl, &lock, &flags);
++      ASSERT (level == PTBL_LEVEL_3);
++
++      span = MIN(len, ELAN3_L3_PTSIZE - ((E3_Addr) addr & ELAN3_L3_PTOFFSET));
++
++
++      for (i = 0 ; i < span; i += ELAN3_PAGE_SIZE, pte += ELAN3_PTE_SIZE)
++          elan3_writepte (dev, pte, tpte);
++      ptbl->ptbl_valid -= (span >> ELAN3_PAGE_SHIFT);
++
++      HAT_PRINTF3 (2, "elan3mmu_release: inc valid for level %d ptbl %p to %d\n", 
++                   PTBL_LEVEL(ptbl->ptbl_flags), ptbl, ptbl->ptbl_valid);
++
++      elan3mmu_unlock_ptbl (ptbl, lock, flags);
++    }
++    ElanFlushTlb (elan3mmu->elan3mmu_dev);
++}
++
++void
++elan3mmu_pteload (ELAN3MMU *elan3mmu, int level, E3_Addr addr, physaddr_t paddr, int perm, int attr)
++    
++{
++    ELAN3_DEV     *dev;
++    ELAN3_PTBL    *ptbl;
++    spinlock_t   *lock;
++    unsigned long flags;
++    ELAN3_PTE      newpte;
++    ELAN3_PTE      oldpte;
++    sdramaddr_t   pte;
++
++    ASSERT((level == PTBL_LEVEL_2) || (level == PTBL_LEVEL_3));
++
++    /* Generate the new pte which we're going to load */
++    dev = elan3mmu->elan3mmu_dev;
++
++    newpte = elan3mmu_phys_to_pte (dev, paddr, perm);
++    
++    if (attr & PTE_LOAD_BIG_ENDIAN)
++      newpte |= ELAN3_PTE_BIG_ENDIAN;
++
++    HAT_PRINTF4 (1, "elan3mmu_pteload: elan3mmu %p level %d addr %x pte %llx\n", elan3mmu, level, addr, (long long) newpte);
++    HAT_PRINTF5 (1, "elan3mmu_pteload:%s%s%s perm=%d phys=%llx\n",
++               (newpte & ELAN3_PTE_LOCAL)  ? " local" : "",
++               (newpte & ELAN3_PTE_64_BIT)     ? " 64 bit" : "",
++               (newpte & ELAN3_PTE_BIG_ENDIAN) ? " big-endian" : " little-endian",
++               (u_int) (newpte & ELAN3_PTE_PERM_MASK) >> ELAN3_PTE_PERM_SHIFT,
++               (unsigned long long) (newpte & ELAN3_PTE_PFN_MASK));
++                
++    if (level == PTBL_LEVEL_3)
++      pte = elan3mmu_ptealloc (elan3mmu, addr, level, &ptbl, &lock, attr, &flags);
++    else
++    {
++      sdramaddr_t ptp = elan3mmu_ptealloc (elan3mmu, addr, level, &ptbl, &lock, attr, &flags);
++
++      pte = elan3mmu_ptp2pte (elan3mmu, ptp, level);
++
++      HAT_PRINTF3 (2, "elan3mmu_pteload: level %d ptp at %lx => pte at %lx\n", level, ptp, pte);
++    }
++
++    if (pte == (sdramaddr_t) 0)
++    {
++      ASSERT (level == PTBL_LEVEL_3 && (attr & (PTE_NO_SLEEP | PTE_NO_STEAL)) == (PTE_NO_SLEEP | PTE_NO_STEAL));
++      return;
++    }
++
++    ASSERT (ptbl->ptbl_elan3mmu == elan3mmu);
++    ASSERT (PTBL_LEVEL(ptbl->ptbl_flags) == level);
++    ASSERT (PTBL_IS_LOCKED (ptbl->ptbl_flags));
++    
++    oldpte = elan3_readpte (dev, pte);
++
++    HAT_PRINTF3 (2, "elan3mmu_pteload: modify pte at %lx from %llx to %llx\n", pte, (long long) oldpte, (long long) newpte);
++
++    if (ELAN3_PTE_IS_VALID(ptbl, oldpte))
++    {
++      ELAN3MMU_STAT(ptereload);
++
++      ASSERT ((newpte & ~((E3_uint64)ELAN3_PTE_PERM_MASK | ELAN3_RM_MASK)) == (oldpte & ~((E3_uint64)ELAN3_PTE_PERM_MASK | ELAN3_RM_MASK)));
++      
++      if ((newpte & ~ELAN3_RM_MASK) != (oldpte & ~ELAN3_RM_MASK))
++      {
++          /* We're modifying a valid translation, it must be mapping the same page */
++          /* so we use elan3_modifypte to not affect the referenced and modified bits */
++          elan3_modifypte (dev, pte, newpte);
++
++
++          ElanFlushTlb (elan3mmu->elan3mmu_dev);
++      }
++    }
++    else
++    {
++      ELAN3MMU_STAT(pteload);
++
++      ptbl->ptbl_valid++;
++
++      HAT_PRINTF3 (2, "elan3mmu_pteload: inc valid for level %d ptbl %p to %d\n", 
++                   PTBL_LEVEL(ptbl->ptbl_flags), ptbl, ptbl->ptbl_valid);
++
++      HAT_PRINTF2 (2, "elan3mmu_pteload: write pte %lx to %llx\n", pte, (long long) newpte);
++
++      elan3_writepte (dev, pte, newpte);
++
++      if (ptbl->ptbl_flags & PTBL_KERNEL)
++          ElanFlushTlb (elan3mmu->elan3mmu_dev);
++
++    }
++
++    elan3mmu_unlock_ptbl (ptbl, lock, flags);
++}
++
++void
++elan3mmu_unload (ELAN3MMU *elan3mmu, E3_Addr addr, u_int len, int attr)
++{
++    ELAN3_PTBL          *ptbl;
++    sdramaddr_t         ptp;
++    spinlock_t               *lock;
++    int                       level;
++    u_int             span;
++    unsigned long     flags;
++
++    HAT_PRINTF3(1, "elan3mmu_unload (elan3mmu %p addr %x -> %x)\n", elan3mmu, addr, addr+len-1);
++
++    for (; len != 0; addr += span, len -= span)
++    {
++      ptp  = elan3mmu_ptefind(elan3mmu, addr, &level, &ptbl, &lock, &flags);
++
++      span = MIN(len, ELAN3_L3_PTSIZE - ((E3_Addr) addr & ELAN3_L3_PTOFFSET));
++
++      if (ptp != (sdramaddr_t) 0)
++      {
++          HAT_PRINTF2 (2, "elan3mmu_unload: unload [%x,%x]\n", addr, addr + span);
++          
++          if ( level ==  PTBL_LEVEL_3 ) 
++              elan3mmu_unload_loop (elan3mmu, ptbl, ptp - PTBL_TO_PTADDR(ptbl), span >> ELAN3_PAGE_SHIFT, attr);
++          else
++          {
++              ELAN3_PTP    invalidptp = ELAN3_INVALID_PTP;
++              ELAN3_DEV   *dev = elan3mmu->elan3mmu_dev;
++              ELAN3_PTBL  *lXptbl;
++              ELAN3_PTP    tptp;
++              int         idx;
++
++              tptp = elan3_readptp (elan3mmu->elan3mmu_dev, ptp);
++
++              ASSERT (ELAN3_PTP_TYPE(tptp) == ELAN3_ET_PTE);
++
++              lXptbl = elan3mmu_ta_to_ptbl (elan3mmu, &tptp);
++              idx    = (PTP_TO_PT_PADDR(tptp) - PTBL_TO_PTADDR(lXptbl))/ELAN3_PTE_SIZE;
++
++              if ( level == PTBL_LEVEL_1) 
++                  span = MIN(len, ELAN3_L2_PTSIZE - ((E3_Addr) addr & ELAN3_L2_PTOFFSET));
++              else
++                  span = MIN(len, ELAN3_L3_PTSIZE - ((E3_Addr) addr & ELAN3_L3_PTOFFSET));
++
++              /* invalidate the ptp. */
++              elan3_writeptp (dev, ptp, invalidptp);
++              if (! (attr & PTE_UNLOAD_NOFLUSH))
++                  ElanFlushTlb (dev);     
++    
++              elan3mmu_free_pte ( dev, elan3mmu, lXptbl, idx); 
++
++              ptbl->ptbl_valid--;
++
++              HAT_PRINTF3 (2, "elan3mmu_unload: dec valid for level %d ptbl %p to %d\n", 
++                           PTBL_LEVEL(ptbl->ptbl_flags), ptbl, ptbl->ptbl_valid);     
++
++          }
++          elan3mmu_unlock_ptbl (ptbl, lock, flags);
++      }
++    }
++}
++
++static void
++elan3mmu_unload_loop (ELAN3MMU *elan3mmu, ELAN3_PTBL *ptbl, int first_valid, int nptes, int flags)
++{
++    ELAN3_DEV   *dev = elan3mmu->elan3mmu_dev;
++    sdramaddr_t pte;
++    ELAN3_PTE    tpte;
++    int         last_valid = first_valid + nptes;
++    int               i;
++    
++    HAT_PRINTF3 (1, "elan3mmu_unloadloop: ptbl %p entries [%d->%d]\n", ptbl, first_valid, last_valid);
++
++    ASSERT (PTBL_IS_LOCKED (ptbl->ptbl_flags));
++    ASSERT (PTBL_LEVEL(ptbl->ptbl_flags) == PTBL_LEVEL_3);
++    
++    pte = PTBL_TO_PTADDR(ptbl) + first_valid;
++    
++    for (i = first_valid; i < last_valid; i++, pte += ELAN3_PTE_SIZE)
++    {
++      if (ptbl->ptbl_valid == 0)
++          break;
++
++      tpte = elan3_readpte (dev, pte);
++      if (! ELAN3_PTE_IS_VALID(ptbl, tpte))
++          continue;
++      
++      elan3mmu_pteunload (ptbl, pte, flags, NO_MLIST_LOCK);
++    }
++}
++
++void
++elan3mmu_pteunload (ELAN3_PTBL *ptbl, sdramaddr_t pte, int flags, int got_mlist_lock)
++{
++    ELAN3_DEV   *dev = ptbl->ptbl_elan3mmu->elan3mmu_dev;
++    ELAN3_PTE    tpte;
++
++    ASSERT (PTBL_LEVEL (ptbl->ptbl_flags) == PTBL_LEVEL_3);
++    ASSERT (PTBL_IS_LOCKED (ptbl->ptbl_flags));
++
++    HAT_PRINTF2 (1, "elan3mmu_pteunload: ptbl %p pte %lx\n", ptbl, pte);
++
++    ELAN3MMU_STAT (pteunload);
++
++    elan3_invalidatepte (dev, pte);
++
++    if (! (flags & PTE_UNLOAD_NOFLUSH))
++      ElanFlushTlb (dev);
++    
++    tpte = ELAN3_INVALID_PTE;
++    elan3_writepte (dev, pte, tpte);
++    
++    if (ptbl->ptbl_flags & PTBL_KERNEL)
++    {
++      tpte = elan3mmu_kernel_invalid_pte(ptbl->ptbl_elan3mmu);
++
++      elan3_writepte (dev, pte, tpte);
++    }
++
++    ptbl->ptbl_valid--;
++
++    HAT_PRINTF3 (2, "elan3mmu_pteunload: dec valid for level %d ptbl %p to %d\n", 
++               PTBL_LEVEL(ptbl->ptbl_flags), ptbl, ptbl->ptbl_valid);
++
++}
++
++void
++elan3mmu_ptesync (ELAN3_PTBL *ptbl, sdramaddr_t pte, int flags, int got_mlist_lock)
++{
++
++}
++
++/*
++ * Create more page tables at a given level for this Elan.
++ */
++static ELAN3_PTBL *
++elan3mmu_create_ptbls (ELAN3_DEV *dev, int level, int attr, int keep)
++{
++    sdramaddr_t         pts;
++    ELAN3_PTBL    *ptbl;
++    ELAN3_PTBL    *first;
++    ELAN3_PTBL    *last;
++    ELAN3_PTBL_GR *ptg;
++    register int  i;
++    register int  inc;
++    
++    HAT_PRINTF1 (2, "elan3mmu_create_ptbls: create level %d ptbls\n", level);
++
++    pts = elan3_sdram_alloc (dev, PTBL_GROUP_SIZE);
++    if (pts == (sdramaddr_t) 0)
++    {
++      HAT_PRINTF0 (2, "elan3mmu_create_ptbls: cannot map elan pages\n");
++
++      ELAN3MMU_STAT (create_ptbl_failed);
++      return (NULL);
++    }
++    
++    HAT_PRINTF1 (2, "elan3mmu_create_ptbls: pts at %lx\n", pts);
++    
++    ALLOC_PTBL_GR (ptg, !(attr & PTE_NO_SLEEP));              /* Allocate the group of page tables */
++    if (ptg == NULL)                                          /* for this page */
++    {
++      HAT_PRINTF0 (2, "elan3mmu_create_ptbls: cannot allocate page table group\n");
++
++      elan3_sdram_free (dev, pts, PTBL_GROUP_SIZE);
++
++      ELAN3MMU_STAT (create_ptbl_failed);
++      return (NULL);
++    }
++
++    HAT_PRINTF1 (2, "elan3mmu_create_ptbls: ptg is %p\n", ptg);
++    
++    ElanSetPtblGr (dev, pts, ptg);
++    
++    HAT_PRINTF4 (2, "elan3mmu_create_ptbls: zeroing %d bytes at %lx, %d bytes at %p\n",
++               PTBL_GROUP_SIZE, pts, (int) sizeof (ELAN3_PTBL_GR), ptg);
++
++#ifndef zero_all_ptbls
++    elan3_sdram_zeroq_sdram (dev, pts, PTBL_GROUP_SIZE);              /* Ensure that all PTEs/PTPs are invalid */
++#endif
++    bzero ((caddr_t) ptg, sizeof (ELAN3_PTBL_GR));
++    
++    ptg->pg_addr  = pts;
++    ptg->pg_level = level;
++
++    ptbl = ptg->pg_ptbls;                                     /* Initialise the index in all page tables */
++    for (i = 0; i < PTBLS_PER_GROUP_MAX; i++)
++    {
++      ptbl->ptbl_index = (u_char) i;
++      ptbl->ptbl_next  = (ELAN3_PTBL *) 0xdeaddead;
++      ptbl++;
++    }
++    
++    switch (level)                                            /* Determine the number of ptbls we can  */
++    {                                                         /* allocate from this page, by jumping  */
++    case PTBL_LEVEL_X: inc = PTBLS_PER_PTBL_LX; break;                /* multiples of the smallest. */
++    case PTBL_LEVEL_1: inc = PTBLS_PER_PTBL_L1; break;
++    case PTBL_LEVEL_2: inc = PTBLS_PER_PTBL_L2; break;
++    case PTBL_LEVEL_3: inc = PTBLS_PER_PTBL_L3; break;
++    default:           inc = PTBLS_PER_PTBL_L3; break;
++    }
++
++    ptbl = ptg->pg_ptbls;                                     /* Chain them together */
++    for (i = 0; i < PTBLS_PER_GROUP_MAX; i += inc, ptbl += inc)
++      ptbl->ptbl_next = ptbl + inc;
++
++    first = ptg->pg_ptbls;                                    /* Determine list of */
++    last  = first + PTBLS_PER_GROUP_MAX - inc;                        /* ptbls to add to free list */
++    if (! keep)
++      ptbl = NULL;
++    else
++    {
++      ptbl  = first;
++      first = first->ptbl_next;
++    }
++    
++    spin_lock (&dev->Level[level].PtblLock);
++    dev->Level[level].PtblTotal     += PTBLS_PER_GROUP_MAX/inc;               /* Increment the counts */
++    dev->Level[level].PtblFreeCount += PTBLS_PER_GROUP_MAX/inc;
++
++    ELAN3MMU_SET_STAT (num_ptbl_level[level], dev->Level[level].PtblTotal);
++
++    if (keep)
++      dev->Level[level].PtblFreeCount--;
++    
++    last->ptbl_next = dev->Level[level].PtblFreeList;                 /* And add to free list */
++    dev->Level[level].PtblFreeList = first;
++    spin_unlock (&dev->Level[level].PtblLock);
++    
++    spin_lock (&dev->PtblGroupLock);
++    ptg->pg_next = dev->Level[level].PtblGroupList;
++    dev->Level[level].PtblGroupList = ptg;
++    spin_unlock (&dev->PtblGroupLock);
++
++    HAT_PRINTF1 (2, "elan3mmu_create_ptbls: returning ptbl %p\n", ptbl);
++    
++    return (ptbl);
++}
++
++static ELAN3_PTBL *
++elan3mmu_ta_to_ptbl (ELAN3MMU *elan3mmu, ELAN3_PTP *ptp)
++{
++    E3_Addr     ptpa  = PTP_TO_PT_PADDR(*ptp);
++    ELAN3_PTBL_GR *pg    = ElanGetPtblGr (elan3mmu->elan3mmu_dev, (sdramaddr_t)ptpa & ~(PTBL_GROUP_SIZE-1));
++    
++    return (pg->pg_ptbls + ((ptpa - pg->pg_addr) >> ELAN3_PT_SHIFT));
++}
++
++static ELAN3_PTBL *
++elan3mmu_alloc_lXptbl (ELAN3_DEV *dev, int attr,  ELAN3MMU *elan3mmu)
++{
++    ELAN3_PTBL *ptbl = NULL;
++
++    spin_lock (&dev->Level[PTBL_LEVEL_X].PtblLock);
++    if (dev->Level[PTBL_LEVEL_X].PtblFreeList)
++    {
++      ptbl = dev->Level[PTBL_LEVEL_X].PtblFreeList;
++
++      HAT_PRINTF1 (2, "elan3mmu_alloc_lXptbl: found ptbl %p on free list\n", ptbl);
++
++      dev->Level[PTBL_LEVEL_X].PtblFreeList = ptbl->ptbl_next;
++      dev->Level[PTBL_LEVEL_X].PtblFreeCount--;
++    }
++    spin_unlock (&dev->Level[PTBL_LEVEL_X].PtblLock);
++    
++    if (ptbl == NULL) 
++    {
++      ptbl = elan3mmu_create_ptbls (dev, PTBL_LEVEL_X, attr, 1);
++
++      HAT_PRINTF1 (2, "elan3mmu_alloc_lXptbl: created level X ptbl %p\n", ptbl);
++    }
++
++    if (ptbl == NULL)
++    {
++      if ((attr & PTE_NO_STEAL))
++      {
++          HAT_PRINTF0 (2, "elan3mmu_alloc_lXptbl: not allowed to steal ptbl for use at level 2\n");
++          return NULL;
++      }
++
++      ELAN3MMU_STAT(lX_alloc_l3);
++
++      ptbl = elan3mmu_steal_l3ptbl (dev, attr);
++      
++      HAT_PRINTF1 (2, "elan3mmu_alloc_lXptbl: stolen level3 ptbl %p used as level 2\n", ptbl);
++    }
++
++    ptbl->ptbl_elan3mmu = elan3mmu;
++    ptbl->ptbl_base     = 0;
++    ptbl->ptbl_parent   = 0;
++    ptbl->ptbl_flags    = PTBL_LEVEL_X | PTBL_ALLOCED;
++    
++    HAT_PRINTF2 (2, "elan3mmu_alloc_lXptbl: ptbl %p dev %p\n", ptbl, dev);
++
++#ifdef zero_all_ptbls
++    elan3_sdram_zero_sdarm (dev, PTBL_TO_PTADDR(ptbl), ELAN3_LX_ENTRIES*ELAN3_PTE_SIZE);
++#endif
++
++    return (ptbl);
++}
++
++static ELAN3_PTBL *
++elan3mmu_alloc_pte (ELAN3_DEV *dev, ELAN3MMU *elan3mmu, int *idx)
++{
++    ELAN3_PTBL   * ptbl_ptr;
++    int           index;
++
++    /* lock whilst looking for space */
++    spin_lock (&elan3mmu->elan3mmu_lXptbl_lock);
++    
++    /* walk the lXptbl list */
++    ptbl_ptr = elan3mmu->elan3mmu_lXptbl;
++    while ( ptbl_ptr != NULL ) 
++    {
++      /* does this ptlb have any free ones */
++      if (  (index = ptbl_ptr->ptbl_valid) < ELAN3_LX_ENTRIES) 
++      {
++          /*  better to search  from valid count as its likly to be free */
++          index = ptbl_ptr->ptbl_valid; 
++          do {
++              if ((ptbl_ptr->ptbl_base & (1 << index)) == 0)
++                  goto found;
++
++              /* move index on and wrap back to start if needed */
++              if ((++index) == ELAN3_LX_ENTRIES) 
++                  index = 0;
++          } while (index != ptbl_ptr->ptbl_valid);
++
++          panic ("elan3mmu_alloc_pte: has ptbl valid < 32 when but no free pte's");
++      }
++      ptbl_ptr = ptbl_ptr->ptbl_parent;
++    }
++      
++    /* unlock so we can create space */
++    spin_unlock (&elan3mmu->elan3mmu_lXptbl_lock); 
++
++    /* if create some more */
++    ptbl_ptr = elan3mmu_alloc_lXptbl(dev, 0, elan3mmu);
++
++    /* get the lock again */
++    spin_lock (&elan3mmu->elan3mmu_lXptbl_lock);
++      
++    /* add to front of list as its obviously got free ones on it */
++    ptbl_ptr->ptbl_parent     = elan3mmu->elan3mmu_lXptbl;
++    elan3mmu->elan3mmu_lXptbl = ptbl_ptr;
++
++    /* grap the first one */
++    index = 0;
++    
++ found:
++    ptbl_ptr->ptbl_base |= (1 << index);
++    ptbl_ptr->ptbl_valid++;
++
++    HAT_PRINTF3 (2, "elan3mmu_alloc_pte: inc valid for level %d ptbl %p to %d\n", 
++               PTBL_LEVEL(ptbl_ptr->ptbl_flags), ptbl_ptr, ptbl_ptr->ptbl_valid);
++
++    /* release the loc and return it */
++    spin_unlock (&elan3mmu->elan3mmu_lXptbl_lock); 
++
++    *idx = index;
++    return (ptbl_ptr);
++}
++
++static ELAN3_PTBL *
++elan3mmu_alloc_l1ptbl (ELAN3_DEV *dev, int attr, ELAN3MMU *elan3mmu)
++{
++    ELAN3_PTBL *ptbl = NULL;
++    ELAN3_PTBL *p;
++    int i,j;
++    
++    spin_lock (&dev->Level[PTBL_LEVEL_1].PtblLock);
++    if (dev->Level[PTBL_LEVEL_1].PtblFreeList)
++    {
++      ptbl = dev->Level[PTBL_LEVEL_1].PtblFreeList;
++      dev->Level[PTBL_LEVEL_1].PtblFreeList = ptbl->ptbl_next;
++      dev->Level[PTBL_LEVEL_1].PtblFreeCount--;
++    }
++    spin_unlock (&dev->Level[PTBL_LEVEL_1].PtblLock);
++    
++    if (ptbl == NULL)
++      ptbl = elan3mmu_create_ptbls (dev, PTBL_LEVEL_1, attr, 1);
++    
++    if (ptbl == NULL)
++      panic ("elan3mmu_alloc_l1ptbl: cannot alloc ptbl");
++    
++    for (p = ptbl, j = i = 0; i < PTBLS_PER_PTBL_L1; i++, p++)
++    {
++      p->ptbl_elan3mmu = elan3mmu;
++      p->ptbl_base     = VA2BASE (j);
++      p->ptbl_flags    = PTBL_LEVEL_1 | PTBL_GROUPED;
++      p->ptbl_parent   = NULL;
++      
++      j += L1_VA_PER_PTBL;
++    }
++    
++    /* Now mark the real page table as allocated */
++    /* level 1 ptbls are returned unlocked */
++    ptbl->ptbl_flags = PTBL_LEVEL_1 | PTBL_ALLOCED;
++    
++    HAT_PRINTF2 (2, "elan3mmu_alloc_l1ptbl: ptbl %p dev %p\n", ptbl, dev);
++
++#ifdef zero_all_ptbls
++    elan3_sdram_zeroq_sdram (dev, PTBL_TO_PTADDR(ptbl), ELAN3_L1_ENTRIES*ELAN3_PTP_SIZE);
++#endif
++
++    return (ptbl);
++}
++
++static ELAN3_PTBL *
++elan3mmu_alloc_l2ptbl (ELAN3_DEV *dev, int attr, ELAN3_PTBL *parent, ELAN3MMU *elan3mmu, E3_Addr base, spinlock_t **plock, unsigned long *flags)
++{
++    ELAN3_PTBL *ptbl = NULL;
++    ELAN3_PTBL *p;
++    int        i;
++    int        j;
++    unsigned long ptbl_flags;
++
++    spin_lock_irqsave (&dev->Level[PTBL_LEVEL_2].PtblLock, ptbl_flags);
++    if (dev->Level[PTBL_LEVEL_2].PtblFreeList)
++    {
++      ptbl = dev->Level[PTBL_LEVEL_2].PtblFreeList;
++
++      HAT_PRINTF1 (2, "elan3mmu_alloc_l2ptbl: found ptbl %p on free list\n", ptbl);
++
++      dev->Level[PTBL_LEVEL_2].PtblFreeList = ptbl->ptbl_next;
++      dev->Level[PTBL_LEVEL_2].PtblFreeCount--;
++    }
++    spin_unlock_irqrestore (&dev->Level[PTBL_LEVEL_2].PtblLock, ptbl_flags);
++    
++    if (ptbl == NULL) 
++    {
++      ptbl = elan3mmu_create_ptbls (dev, PTBL_LEVEL_2, attr, 1);
++
++      HAT_PRINTF1 (2, "elan3mmu_alloc_l2ptbl: created level 2 ptbl %p\n", ptbl);
++    }
++
++    if (ptbl == NULL)
++    {
++      if ((attr & PTE_NO_STEAL))
++      {
++          HAT_PRINTF0 (2, "elan3mmu_alloc_l2ptbl: not allowted to steal ptbl for use at level 2\n");
++          return (NULL);
++      }
++
++      ELAN3MMU_STAT(l2_alloc_l3);
++
++      ptbl = elan3mmu_steal_l3ptbl (dev, attr);
++      
++      HAT_PRINTF1 (2, "elan3mmu_alloc_l2ptbl: stolen level3 ptbl %p used as level 2\n", ptbl);
++    }
++    
++    *plock = elan3mmu_ptbl_to_lock (PTBL_LEVEL_2, ptbl);
++    spin_lock_irqsave (*plock, *flags);
++    
++    for (p = ptbl, j = i = 0; i < PTBLS_PER_PTBL_L2; i++, p++)
++    {
++      p->ptbl_elan3mmu = elan3mmu;
++      p->ptbl_base     = VA2BASE (base + j);
++      p->ptbl_flags    = PTBL_LEVEL_2 | PTBL_GROUPED;
++      p->ptbl_parent   = parent;
++      
++      j += L2_VA_PER_PTBL;
++    }
++    
++    ptbl->ptbl_flags  = PTBL_LEVEL_2 | PTBL_ALLOCED | PTBL_LOCKED;
++    
++    HAT_PRINTF3 (2, "elan3mmu_alloc_l2ptbl: ptbl %p dev %p base %x\n", ptbl, dev, base);
++
++#ifdef zero_all_ptbls
++    elan3_sdram_zero_sdarm (dev, PTBL_TO_PTADDR(ptbl), ELAN3_L2_ENTRIES*ELAN3_PTP_SIZE);
++#endif
++
++    return (ptbl);
++}
++
++static ELAN3_PTBL *
++elan3mmu_alloc_l3ptbl (ELAN3_DEV *dev, int attr, ELAN3_PTBL *parent, ELAN3MMU *elan3mmu, E3_Addr base, spinlock_t **plock, unsigned long *flags)
++{
++    ELAN3_PTBL *ptbl = NULL;
++    ELAN3_PTBL *p;
++    int              i;
++    int              j;
++    unsigned long ptbl_flags;
++
++    spin_lock_irqsave (&dev->Level[PTBL_LEVEL_3].PtblLock, ptbl_flags);
++    if (dev->Level[PTBL_LEVEL_3].PtblFreeList)
++    {
++      HAT_PRINTF1 (2, "elan3mmu_alloc_l3ptbl: found ptbl %p on free list\n", ptbl);
++
++      ptbl = dev->Level[PTBL_LEVEL_3].PtblFreeList;
++      dev->Level[PTBL_LEVEL_3].PtblFreeList = ptbl->ptbl_next;
++      dev->Level[PTBL_LEVEL_3].PtblFreeCount--;
++    }
++    spin_unlock_irqrestore (&dev->Level[PTBL_LEVEL_3].PtblLock, ptbl_flags);
++    
++    if (ptbl == NULL)
++    {
++      ptbl = elan3mmu_create_ptbls (dev, PTBL_LEVEL_3, attr, 1);
++
++      HAT_PRINTF1 (2, "elan3mmu_alloc_l3ptbl: created level 3 ptbl %p\n", ptbl);
++    }
++
++    if (ptbl == NULL)
++    {
++      if ((attr & PTE_NO_STEAL))
++      {
++          HAT_PRINTF0 (2, "elan3mmu_alloc_l3ptbl: not allowed to steal ptbl for use at level 3\n");
++          return (NULL);
++      }
++
++      ptbl = elan3mmu_steal_l3ptbl (dev, attr);
++
++      HAT_PRINTF1 (2, "elan3mmu_alloc_l3ptbl: stolen level3 ptbl %p\n", ptbl);
++    }
++    
++    *plock = elan3mmu_ptbl_to_lock (PTBL_LEVEL_3, ptbl);
++    spin_lock_irqsave (*plock,*flags);
++    
++    for (p = ptbl, j = i = 0; i < PTBLS_PER_PTBL_L3; i++, p++)
++    {
++      p->ptbl_elan3mmu = elan3mmu;
++      p->ptbl_base     = VA2BASE (base + j);
++      p->ptbl_flags    = PTBL_LEVEL_3 | PTBL_GROUPED;
++      p->ptbl_parent   = parent;
++      
++      j += L3_VA_PER_PTBL;
++    }
++    
++    ptbl->ptbl_flags = PTBL_LEVEL_3 | PTBL_ALLOCED | PTBL_LOCKED;
++    
++    HAT_PRINTF3 (2, "elan3mmu_alloc_l3ptbl: ptbl %p dev %p base %x\n", ptbl, dev, base);
++
++#ifdef zero_all_ptbls
++    elan3_sdram_zeroq_sdram (dev, PTBL_TO_PTADDR(ptbl), ELAN3_L3_ENTRIES*ELAN3_PTE_SIZE);
++#endif
++
++    return (ptbl);
++}
++
++void 
++elan3mmu_free_pte  (ELAN3_DEV *dev,  ELAN3MMU *elan3mmu,  ELAN3_PTBL *ptbl_ptr, int idx)
++{  
++    sdramaddr_t pte  = PTBL_TO_PTADDR (ptbl_ptr) | (idx * sizeof (ELAN3_PTE));
++    ELAN3_PTE    tpte = ELAN3_INVALID_PTE;
++    ELAN3_PTBL *prev;
++
++    /* ensure that the pte is invalid when free */
++    elan3_writepte (dev, pte, tpte);
++
++    /* lock whilst removing */
++    spin_lock (&elan3mmu->elan3mmu_lXptbl_lock);
++
++    HAT_PRINTF4 (2, "elan3mmu_free_pte idx %d   ptbl_ptr %p ptbl_base  %x  ptbl_ptr->ptbl_valid %d \n", 
++               idx, ptbl_ptr, ptbl_ptr->ptbl_base, ptbl_ptr->ptbl_valid);
++    /* make sure it was set */
++    ASSERT ( ptbl_ptr->ptbl_base & (1 << idx) ); 
++    ASSERT ( ptbl_ptr->ptbl_valid > 0  );
++
++    ptbl_ptr->ptbl_base &= ~(1 << idx);
++    ptbl_ptr->ptbl_valid--;
++
++    HAT_PRINTF3 (2, "elan3mmu_free_pte: dec valid for level %d ptbl %p to %d\n", 
++               PTBL_LEVEL(ptbl_ptr->ptbl_flags), ptbl_ptr, ptbl_ptr->ptbl_valid); 
++ 
++    /* was that the last one on this page */
++    if ( ! ptbl_ptr->ptbl_valid ) 
++    {
++      /* so no bits should be set then */
++      ASSERT ( ptbl_ptr->ptbl_base == 0 );
++
++      /* is this the first page ?? */
++      if ( elan3mmu->elan3mmu_lXptbl == ptbl_ptr ) 
++      {
++          /* make the list start at the second element */
++           elan3mmu->elan3mmu_lXptbl = ptbl_ptr->ptbl_parent;
++
++           /* put ptbl back on free list */
++           elan3mmu_free_lXptbl(dev, ptbl_ptr);
++
++           /* unlock and return */
++           spin_unlock (&elan3mmu->elan3mmu_lXptbl_lock);
++           return ;
++      }
++
++      /* scan thro list looking for this page */
++      prev = elan3mmu->elan3mmu_lXptbl;
++      while ( prev->ptbl_parent != NULL ) 
++      {
++          if ( prev->ptbl_parent == ptbl_ptr ) /* its the next one */
++          {
++              /* remove element from chain */
++              prev->ptbl_parent =  ptbl_ptr->ptbl_parent;
++
++              /* put ptbl back on free list */
++              elan3mmu_free_lXptbl(dev, ptbl_ptr);
++
++              /* unlock and return */
++              spin_unlock (&elan3mmu->elan3mmu_lXptbl_lock);
++              return ;
++          }           
++          prev = prev->ptbl_parent;
++      }
++      
++              panic ("elan3mmu_free_pte: failed to find ptbl in chain");
++      /* NOTREACHED */
++    }
++    
++    spin_unlock (&elan3mmu->elan3mmu_lXptbl_lock);
++}
++
++void
++elan3mmu_free_lXptbl (ELAN3_DEV *dev, ELAN3_PTBL *ptbl)
++{
++    ELAN3_PTBL_GR *ptg;
++
++    HAT_PRINTF2 (2, "elan3mmu_free_lXptbl: dev %p ptbl %p\n", dev, ptbl);
++
++    ASSERT (ptbl->ptbl_flags & PTBL_ALLOCED);
++    ASSERT ((ptbl->ptbl_flags & PTBL_KEEP) == 0);
++    ASSERT (PTBL_LEVEL(ptbl->ptbl_flags) == PTBL_LEVEL_X);
++    ASSERT (ptbl->ptbl_valid == 0);
++   
++    ptbl->ptbl_flags = 0;
++
++    ptg = PTBL_TO_GR(ptbl);
++
++    if (ptg->pg_level == PTBL_LEVEL_3)
++    {
++      ELAN3MMU_STAT(lX_freed_l3);
++
++      HAT_PRINTF1 (2, "elan3mmu_free_lXptbl: freeing stolen level 3 ptbl %p\n", ptbl);
++
++      /* this was really a level 3 ptbl which we had to steal */
++      spin_lock (&dev->Level[PTBL_LEVEL_3].PtblLock);
++      ptbl->ptbl_next = dev->Level[PTBL_LEVEL_3].PtblFreeList;
++      dev->Level[PTBL_LEVEL_3].PtblFreeList = ptbl;
++      dev->Level[PTBL_LEVEL_3].PtblFreeCount++;
++      spin_unlock (&dev->Level[PTBL_LEVEL_3].PtblLock);
++    }
++    else
++    {
++      spin_lock (&dev->Level[PTBL_LEVEL_X].PtblLock);
++      ptbl->ptbl_next = dev->Level[PTBL_LEVEL_X].PtblFreeList;
++      dev->Level[PTBL_LEVEL_X].PtblFreeList = ptbl;
++      dev->Level[PTBL_LEVEL_X].PtblFreeCount++;
++      spin_unlock (&dev->Level[PTBL_LEVEL_X].PtblLock);
++    }
++}
++
++void
++elan3mmu_free_l1ptbl (ELAN3_DEV *dev, ELAN3_PTBL *ptbl, spinlock_t *lock, unsigned long flags)
++{
++    HAT_PRINTF3 (2, "elan3mmu_free_l1ptbl: dev %p ptbl %p ptbl->ptbl_valid %x \n", dev, ptbl, ptbl->ptbl_valid);
++
++    ASSERT (ptbl->ptbl_flags & PTBL_ALLOCED);
++    ASSERT ((ptbl->ptbl_flags & PTBL_KEEP) == 0);
++    ASSERT (PTBL_LEVEL(ptbl->ptbl_flags) == PTBL_LEVEL_1);
++    ASSERT (ptbl->ptbl_valid == 0);
++    
++    HAT_PRINTF2 (2, "elan3mmu_free_l1ptbl: dev %p ptbl %p\n", dev, ptbl);
++
++    ptbl->ptbl_flags = 0;
++    spin_unlock (lock);
++    
++    spin_lock (&dev->Level[PTBL_LEVEL_1].PtblLock);
++    ptbl->ptbl_next = dev->Level[PTBL_LEVEL_1].PtblFreeList;
++    dev->Level[PTBL_LEVEL_1].PtblFreeList = ptbl;
++    dev->Level[PTBL_LEVEL_1].PtblFreeCount++;
++    spin_unlock (&dev->Level[PTBL_LEVEL_1].PtblLock);
++
++    local_irq_restore (flags);
++}
++
++void
++elan3mmu_free_l2ptbl (ELAN3_DEV *dev, ELAN3_PTBL *ptbl, spinlock_t *lock, unsigned long flags)
++{
++    ELAN3_PTBL_GR *ptg;
++
++    HAT_PRINTF2 (2, "elan3mmu_free_l2ptbl: dev %p ptbl %p\n", dev, ptbl);
++
++    ASSERT (PTBL_IS_LOCKED(ptbl->ptbl_flags));
++    ASSERT (ptbl->ptbl_flags & PTBL_ALLOCED);
++    ASSERT ((ptbl->ptbl_flags & PTBL_KEEP) == 0);
++    ASSERT (PTBL_LEVEL(ptbl->ptbl_flags) == PTBL_LEVEL_2);
++    ASSERT (ptbl->ptbl_valid == 0);
++   
++    ptbl->ptbl_flags = 0;
++    spin_unlock (lock);
++
++    ptg = PTBL_TO_GR(ptbl);
++
++    if (ptg->pg_level == PTBL_LEVEL_3)
++    {
++      ELAN3MMU_STAT(l2_freed_l3);
++
++      HAT_PRINTF1 (2, "elan3mmu_free_l2ptbl: freeing stolen level 3 ptbl %p\n", ptbl);
++
++      /* this was really a level 3 ptbl which we had to steal */
++      spin_lock (&dev->Level[PTBL_LEVEL_3].PtblLock);
++      ptbl->ptbl_next = dev->Level[PTBL_LEVEL_3].PtblFreeList;
++      dev->Level[PTBL_LEVEL_3].PtblFreeList = ptbl;
++      dev->Level[PTBL_LEVEL_3].PtblFreeCount++;
++      spin_unlock (&dev->Level[PTBL_LEVEL_3].PtblLock);
++    }
++    else
++    {
++      spin_lock (&dev->Level[PTBL_LEVEL_2].PtblLock);
++      ptbl->ptbl_next = dev->Level[PTBL_LEVEL_2].PtblFreeList;
++      dev->Level[PTBL_LEVEL_2].PtblFreeList = ptbl;
++      dev->Level[PTBL_LEVEL_2].PtblFreeCount++;
++      spin_unlock (&dev->Level[PTBL_LEVEL_2].PtblLock);
++    }  
++    local_irq_restore (flags);
++}
++
++void
++elan3mmu_free_l3ptbl (ELAN3_DEV *dev, ELAN3_PTBL *ptbl, spinlock_t *lock, unsigned long flags)
++{
++    ASSERT (PTBL_IS_LOCKED(ptbl->ptbl_flags));
++    ASSERT (ptbl->ptbl_flags & PTBL_ALLOCED);
++    ASSERT ((ptbl->ptbl_flags & PTBL_KEEP) == 0);
++    ASSERT (PTBL_LEVEL(ptbl->ptbl_flags) == PTBL_LEVEL_3);
++    ASSERT (ptbl->ptbl_valid == 0);
++    
++    HAT_PRINTF2 (2, "elan3mmu_free_l3ptbl: dev %p ptbl %p\n", dev, ptbl);
++
++    if (ptbl->ptbl_flags & PTBL_KERNEL)                               /* if the ptbl has been used by the kernel */
++    {                                                         /* then zero all the pte's, since they will */
++      elan3_sdram_zeroq_sdram (dev, PTBL_TO_PTADDR(ptbl), ELAN3_L3_ENTRIES*ELAN3_PTE_SIZE);
++    }
++
++    ptbl->ptbl_flags = 0;
++    spin_unlock (lock);
++    
++    spin_lock (&dev->Level[PTBL_LEVEL_3].PtblLock);
++    ptbl->ptbl_next = dev->Level[PTBL_LEVEL_3].PtblFreeList;
++    dev->Level[PTBL_LEVEL_3].PtblFreeList = ptbl;
++    dev->Level[PTBL_LEVEL_3].PtblFreeCount++;
++    spin_unlock (&dev->Level[PTBL_LEVEL_3].PtblLock);
++
++    local_irq_restore (flags);
++}
++
++void
++elan3mmu_kernel_l3ptbl (ELAN3_PTBL *ptbl)
++{
++    ELAN3_DEV   *dev  = ptbl->ptbl_elan3mmu->elan3mmu_dev;
++    sdramaddr_t pte  = PTBL_TO_PTADDR(ptbl);
++    ELAN3_PTE    tpte = elan3mmu_kernel_invalid_pte(ptbl->ptbl_elan3mmu);
++    int               i;
++
++    ptbl->ptbl_flags |= PTBL_KERNEL;
++    for (i = 0; i < ELAN3_L3_ENTRIES; i++, pte += ELAN3_PTE_SIZE)
++    {
++      elan3_writepte (dev, pte, tpte);
++    }
++}
++      
++#define PTBL_CAN_STEAL(flag)  (((flag) & (PTBL_KERNEL|PTBL_KEEP)) == 0 && (((flag) & PTBL_ALLOCED) && PTBL_LEVEL(flag) == PTBL_LEVEL_3))
++#define PTBL_MAY_STEAL(flag)  (((flag) & (PTBL_KERNEL|PTBL_KEEP|PTBL_LOCKED)) == 0 && (((flag) & PTBL_ALLOCED) && PTBL_LEVEL(flag) == PTBL_LEVEL_3))
++
++static int
++elan3mmu_steal_this_ptbl (ELAN3_DEV *dev, ELAN3_PTBL *l3ptbl)
++{
++    ELAN3_PTBL  *l2ptbl     = l3ptbl->ptbl_parent;
++    E3_Addr     l2addr     = BASE2VA(l2ptbl);
++    E3_Addr     l3addr     = BASE2VA(l3ptbl);
++    ELAN3_PTP    invalidptp = ELAN3_INVALID_PTP;
++    sdramaddr_t l2ptp;
++    spinlock_t *l2lock;
++    unsigned long l2flags;
++
++    HAT_PRINTF5 (1, "elan3mmu_steal_this_ptbl: l3ptbl %p (%x) l2ptbl %p (%x) l2addr %x\n",
++               l3ptbl, l3ptbl->ptbl_flags, l2ptbl, l2ptbl->ptbl_flags, l2addr);
++
++    if (PTBL_CAN_STEAL (l3ptbl->ptbl_flags) &&
++      elan3mmu_lock_ptbl (l2ptbl, LK_PTBL_NOWAIT, l3ptbl->ptbl_elan3mmu, l2addr, PTBL_LEVEL_2, &l2lock, &l2flags) == LK_PTBL_OK)
++    {
++      ELAN3MMU_STAT(stolen_ptbls);
++
++      /* Locked both L3 and L2 page tables. */
++      l2ptp = PTBL_TO_PTADDR (l2ptbl) + ELAN3_L2_INDEX(l3addr)*ELAN3_PTP_SIZE;
++      
++      /* detach the level 3 page table */
++      elan3_writeptp (dev, l2ptp, invalidptp);
++      ElanFlushTlb (dev);
++
++      l2ptbl->ptbl_valid--;
++
++      HAT_PRINTF3 (2, "elan3mmu_steal_this_ptbl: dec valid for level %d ptbl %p to %d\n", PTBL_LEVEL(l2ptbl->ptbl_flags), l2ptbl, l2ptbl->ptbl_valid); 
++
++      elan3mmu_unlock_ptbl (l2ptbl, l2lock, l2flags);
++
++      elan3mmu_unload_loop (l3ptbl->ptbl_elan3mmu, l3ptbl, 0, ELAN3_L3_ENTRIES, PTE_UNLOAD_NOFLUSH);
++
++      ASSERT (l3ptbl->ptbl_valid == 0);
++
++      l3ptbl->ptbl_flags = 0;
++      return (1);
++    }
++    return (0);
++}
++
++static ELAN3_PTBL *
++elan3mmu_steal_l3ptbl (ELAN3_DEV *dev, int attr)
++{
++    ELAN3_PTBL_GR     *ptg;
++    ELAN3_PTBL                *ptbl;
++    spinlock_t                *lock;
++    unsigned long        group_flags;
++    unsigned long        ptbl_flags;
++    register int       i;
++
++    HAT_PRINTF1 (2, "elan3mmu_steal_l3ptbl: attr %x\n", attr);
++
++    spin_lock_irqsave (&dev->PtblGroupLock, group_flags);
++
++    ptg = dev->Level3PtblGroupHand;
++
++    if (ptg == NULL)
++      ptg = dev->Level[PTBL_LEVEL_3].PtblGroupList;
++    
++    for (;;)
++    {
++      while (ptg)
++      {
++          for (i = 0, ptbl = ptg->pg_ptbls; i < PTBLS_PER_GROUP_MAX; i++, ptbl++)
++          {
++              if (PTBL_MAY_STEAL (ptbl->ptbl_flags) &&
++                  elan3mmu_lock_this_ptbl (ptbl, LK_PTBL_NOWAIT, &lock, &ptbl_flags) == LK_PTBL_OK)
++              {
++                  if (elan3mmu_steal_this_ptbl (dev, ptbl ))
++                  {
++                      HAT_PRINTF1 (2, "elan3mmu_steal_l3ptbl: stolen ptbl %p\n", ptbl);
++
++                      elan3mmu_unlock_ptbl (ptbl, lock,ptbl_flags);
++
++                      dev->Level3PtblGroupHand = ptg->pg_next;
++
++                      spin_unlock_irqrestore (&dev->PtblGroupLock, group_flags);
++
++                      return (ptbl);
++                  }
++                  elan3mmu_unlock_ptbl (ptbl, lock, ptbl_flags);
++              }
++          }
++          ptg = ptg->pg_next;
++      }
++      
++      if (dev->Level[PTBL_LEVEL_3].PtblFreeList)
++      {
++          spin_lock (&dev->Level[PTBL_LEVEL_3].PtblLock);
++          ptbl = dev->Level[PTBL_LEVEL_3].PtblFreeList;
++          if (ptbl != NULL)
++          {
++              dev->Level[PTBL_LEVEL_3].PtblFreeList = ptbl->ptbl_next;
++              dev->Level[PTBL_LEVEL_3].PtblFreeCount--;
++          }
++          spin_unlock (&dev->Level[PTBL_LEVEL_3].PtblLock);
++
++          if (ptbl != NULL)
++          {
++              HAT_PRINTF1 (2, "elan3mmu_steal_l3ptbl: found ptbl %p on free list\n", ptbl);
++              break;
++          }
++      }
++
++      ptbl = elan3mmu_create_ptbls (dev, PTBL_LEVEL_3, attr, 1);
++
++      if (ptbl != NULL)
++      {
++          HAT_PRINTF1 (2, "elan3mmu_steal_l3ptbl: created new ptbl %p\n", ptbl);
++          break;
++      }
++      
++      HAT_PRINTF0 (1, "elan3mmu_steal_l3ptbl: cannot find a ptbl, retrying\n");
++      ptg = dev->Level[PTBL_LEVEL_3].PtblGroupList;
++    }
++
++    spin_unlock (&dev->PtblGroupLock);
++    return (ptbl);
++}
++
++sdramaddr_t
++elan3mmu_ptefind (ELAN3MMU *elan3mmu, E3_Addr addr, int *level, 
++                ELAN3_PTBL **pptbl, spinlock_t **plock, unsigned long *flags)
++{
++    ELAN3_DEV   *dev = elan3mmu->elan3mmu_dev;
++    ELAN3_PTBL  *l1ptbl;
++    sdramaddr_t l1ptp;
++    ELAN3_PTP    tl1ptp;
++    E3_Addr     l1base;
++    ELAN3_PTBL  *l2ptbl;
++    sdramaddr_t l2ptp;
++    ELAN3_PTP    tl2ptp;
++    E3_Addr     l2base;
++    ELAN3_PTBL  *l3ptbl;
++    sdramaddr_t l3pte;
++    spinlock_t *l1lock;
++    spinlock_t *l2lock;
++    spinlock_t *l3lock;
++    unsigned long l1flags;
++    unsigned long l2flags;
++    unsigned long l3flags;
++
++    HAT_PRINTF2 (2, "elan3mmu_ptefind: elan3mmu %p addr %x\n", elan3mmu, addr);
++
++    l1ptbl = elan3mmu->elan3mmu_l1ptbl;
++    *level = 0;
++
++    if (l1ptbl == NULL)
++      return ((sdramaddr_t) NULL);
++
++    l1ptp  = PTBL_TO_PTADDR(l1ptbl) + ELAN3_L1_INDEX(addr)*ELAN3_PTP_SIZE;
++    l1base = ELAN3_L1_BASE(addr);
++    
++retryl1:
++    tl1ptp = elan3_readptp (dev, l1ptp);
++    
++    HAT_PRINTF4 (2, "elan3mmu_ptefind: l1ptbl %p l1ptp %lx l1base %x : tl1ptp %x\n", l1ptbl, l1ptp, l1base, tl1ptp);
++
++    switch (ELAN3_PTP_TYPE(tl1ptp))
++    {
++    case ELAN3_ET_PTE:
++      elan3mmu_lock_ptbl (l1ptbl, LK_PTBL_FAILOK, elan3mmu, addr, PTBL_LEVEL_1, &l1lock, &l1flags);
++
++      tl1ptp = elan3_readptp (dev, l1ptp);
++      if (ELAN3_PTP_TYPE(tl1ptp) != ELAN3_ET_PTE)
++      {
++          elan3mmu_unlock_ptbl (l1ptbl, l1lock, l1flags);
++          goto retryl1;
++      }
++      
++      *level = 1;
++      *pptbl = l1ptbl;
++      *plock = l1lock;
++      *flags = l1flags;
++      
++      /* return with l1lock */
++      return (l1ptp);  
++
++    case ELAN3_ET_INVALID:
++      return ((sdramaddr_t) 0);
++      
++    case ELAN3_ET_PTP:
++      break;
++
++    default:
++      panic ("elan3mmu_ptefind: found bad entry in level 1 page table");
++      /* NOTREACHED */
++    }
++    
++    HAT_PRINTF1 (2, "elan3mmu_ptefind: chain to level 2 ptbl from ptp %x\n", tl1ptp);
++
++    l2ptbl = elan3mmu_ta_to_ptbl (elan3mmu, &tl1ptp);
++    l2ptp  = PTBL_TO_PTADDR(l2ptbl) + ELAN3_L2_INDEX(addr)*ELAN3_PTP_SIZE;
++    l2base = ELAN3_L2_BASE(addr);
++    
++    tl2ptp = elan3_readptp (dev, l2ptp);
++    
++    HAT_PRINTF4 (2, "elan3mmu_ptefind: l2ptbl %p l2ptp %lx l2base %x : tl2ptp %x\n", l2ptbl, l2ptp, l2base, tl2ptp);
++
++    switch (ELAN3_PTP_TYPE(tl2ptp))
++    {
++    case ELAN3_ET_PTE:
++      switch (elan3mmu_lock_ptbl (l2ptbl, LK_PTBL_FAILOK, elan3mmu, addr, PTBL_LEVEL_2, &l2lock, &l2flags))
++      {
++      case LK_PTBL_OK:
++          tl2ptp = elan3_readptp (dev, l2ptp);
++          if (ELAN3_PTP_TYPE(tl2ptp) != ELAN3_ET_PTE)
++          {
++              elan3mmu_unlock_ptbl (l2ptbl, l2lock, l2flags);
++              goto retryl1;
++          }
++          
++          *level = 2;
++          *pptbl = l2ptbl;
++          *plock = l2lock;
++          *flags = l2flags;
++          
++          /* return with l2lock */
++          return (l2ptp); 
++          
++      case LK_PTBL_MISMATCH:
++          HAT_PRINTF6 (2, "elan3mmu_ptefind: PTBL_MISMATCH : ptbl %p flags %x elan3mmu %p base %x (%p %x)\n",
++                       l2ptbl, l2ptbl->ptbl_flags, l2ptbl->ptbl_elan3mmu, l2ptbl->ptbl_base, elan3mmu, addr);
++          
++          /*
++           * We've trogged down to this ptbl,  but someone has just
++           * stolen it,  so try all over again.
++           */
++          goto retryl1;
++          
++      default:
++          panic ("elan3mmu_ptefind: elan3mmu_lock_ptbl returned bad value");
++          /* NOTREACHED */
++      }
++    case ELAN3_ET_INVALID:
++      return ((sdramaddr_t) 0);
++      
++    case ELAN3_ET_PTP:
++      break;
++    default:
++      panic ("elan3mmu_ptefind: found bad entry in level 2 page table");
++      /* NOTREACHED */
++    }
++    
++    HAT_PRINTF1 (2, "elan3mmu_ptefind: chain to level 3 page table from ptp %x\n", tl2ptp);
++
++    l3ptbl = elan3mmu_ta_to_ptbl (elan3mmu, &tl2ptp);
++    l3pte  = PTBL_TO_PTADDR(l3ptbl) + ELAN3_L3_INDEX(addr)*ELAN3_PTE_SIZE;
++    
++    HAT_PRINTF2 (2, "elan3mmu_ptefind: l3ptbl %p l3pte %lx\n", l3ptbl, l3pte);
++               
++    switch (elan3mmu_lock_ptbl (l3ptbl, LK_PTBL_FAILOK, elan3mmu, addr, PTBL_LEVEL_3, &l3lock, &l3flags))
++    {
++    case LK_PTBL_OK:
++      *level = 3;
++      *plock = l3lock;
++      *pptbl = l3ptbl;
++      *flags = l3flags;
++
++      return (l3pte);
++      
++    case LK_PTBL_FAILED:
++      panic ("elan3mmu_ptefind: l3 lock failed");
++      /* NOTREACHED */
++
++    case LK_PTBL_MISMATCH:
++      HAT_PRINTF6 (2, "elan3mmu_ptefind: PTBL_MISMATCH : ptbl %p flags %x elan3mmu %p base %x (%p %x)\n",
++                   l3ptbl, l3ptbl->ptbl_flags, l3ptbl->ptbl_elan3mmu, l3ptbl->ptbl_base, elan3mmu, addr);
++                   
++      /*
++       * We've trogged down to this ptbl,  but someone has just
++       * stolen it,  so try all over again.
++       */
++      goto retryl1;
++      
++    default:
++      panic ("elan3mmu_ptefind: elan3mmu_lock_ptbl returned bad value");
++      /* NOTREACHED */
++    }
++    /* NOTREACHED */
++    return ((sdramaddr_t) 0);
++}
++
++sdramaddr_t 
++elan3mmu_ptp2pte (ELAN3MMU *elan3mmu, sdramaddr_t ptp, int level)
++{
++    ELAN3_PTP tptp = elan3_readptp (elan3mmu->elan3mmu_dev, ptp);
++
++    ASSERT (level != 3 && ELAN3_PTP_TYPE(tptp) == ELAN3_ET_PTE);
++
++    return PTP_TO_PT_PADDR(tptp);
++}
++
++sdramaddr_t
++elan3mmu_ptealloc (ELAN3MMU *elan3mmu, E3_Addr addr, int level, 
++                 ELAN3_PTBL **pptbl, spinlock_t **plock, int attr, unsigned long *flags)
++{
++    ELAN3_DEV   *dev     = elan3mmu->elan3mmu_dev;
++    ELAN3_PTBL  *l1ptbl;
++    ELAN3_PTBL  *lXptbl;
++    int         idx;
++    sdramaddr_t l1ptp;
++    ELAN3_PTP    tl1ptp;
++    E3_Addr     l1base;
++    spinlock_t *l1lock;
++    ELAN3_PTBL  *l2ptbl;
++    sdramaddr_t l2ptp;
++    ELAN3_PTP    tl2ptp;
++    E3_Addr     l2base;
++    spinlock_t *l2lock;
++    ELAN3_PTBL  *l3ptbl;
++    sdramaddr_t l3pte;
++    E3_Addr     l3base;
++    spinlock_t *l3lock;
++
++    unsigned long l1flags;
++    unsigned long l2flags;
++    unsigned long l3flags;
++
++    HAT_PRINTF2 (2, "elan3mmu_ptealloc: elan3mmu %p addr %x\n", elan3mmu, addr);
++
++    l1ptbl = elan3mmu->elan3mmu_l1ptbl;
++    if (l1ptbl == NULL)
++      return ((sdramaddr_t) 0);
++
++    l1ptp  = PTBL_TO_PTADDR(l1ptbl) + ELAN3_L1_INDEX(addr)*ELAN3_PTP_SIZE;
++    l1base = ELAN3_L1_BASE(addr);
++              
++retryl1:
++    tl1ptp = elan3_readptp (dev, l1ptp);
++
++    HAT_PRINTF5 (2, "elan3mmu_ptealloc: l1ptbl %p 1ptp %lx l1base %x (%x) : tl1ptp %x\n",
++               l1ptbl, l1ptp, l1base, l1ptbl->ptbl_base, tl1ptp);
++
++    switch (ELAN3_PTP_TYPE(tl1ptp))
++    {
++    case ELAN3_ET_PTE:
++      if (level == PTBL_LEVEL_1)
++      {
++          elan3mmu_lock_ptbl (l1ptbl, 0, elan3mmu, addr, PTBL_LEVEL_1, &l1lock, &l1flags);
++
++          tl1ptp = elan3_readptp (dev, l1ptp);
++          if (ELAN3_PTP_TYPE(tl1ptp) != ELAN3_ET_PTE)
++          {
++              elan3mmu_unlock_ptbl (l1ptbl, l1lock, l1flags);
++              goto retryl1;
++          }
++          
++          *pptbl = l1ptbl;
++          *plock = l1lock;
++          *flags = l1flags;
++
++          /* return holding l1lock */
++          return (l1ptp);
++      }
++      panic ("elan3mmu_ptealloc: found pte in level 1 page table");
++      /* NOTREACHED */
++
++    case ELAN3_ET_PTP:
++      if (level == PTBL_LEVEL_1)
++          panic ("elan3mmu_ptealloc: found PTP when loading a level 1 PTE\n");
++      break;
++
++    case ELAN3_ET_INVALID:
++      if (level == PTBL_LEVEL_1)
++      {
++          if ((lXptbl = elan3mmu_alloc_pte (dev, elan3mmu,  &idx)) == NULL)
++              return ((sdramaddr_t) 0);
++
++          elan3mmu_lock_ptbl (l1ptbl, 0, elan3mmu, addr, PTBL_LEVEL_1, &l1lock, &l1flags);
++
++          tl1ptp = elan3_readptp (dev, l1ptp);
++          if (ELAN3_PTP_TYPE(tl1ptp) != ELAN3_ET_INVALID)
++          {
++              /* raced with someone else, whose got there first */
++              elan3mmu_free_pte (dev, elan3mmu, lXptbl, idx);
++
++              /* drop the l1lock and retry */
++              elan3mmu_unlock_ptbl (l1ptbl, l1lock, l1flags);
++              goto retryl1;
++          }
++          
++          tl1ptp = PTBL_TO_PTADDR(lXptbl) | (idx * ELAN3_PTE_SIZE) | ELAN3_ET_PTE;
++          
++          elan3_writeptp (dev, l1ptp, tl1ptp);
++
++          *pptbl = l1ptbl;
++          *plock = l1lock;
++          *flags = l1flags;
++
++          /* return holding l1lock */
++          return (l1ptp);
++      }
++
++      if (level == PTBL_LEVEL_2)
++      {
++          if ((lXptbl = elan3mmu_alloc_pte (dev, elan3mmu, &idx)) == NULL)
++              return ((sdramaddr_t) 0);
++
++          if ((l2ptbl = elan3mmu_alloc_l2ptbl (dev, attr, l1ptbl, elan3mmu, ELAN3_L2_BASE(addr), &l2lock, &l2flags)) == NULL)
++          {
++              elan3mmu_free_pte (dev, elan3mmu, lXptbl, idx); 
++              return ((sdramaddr_t) 0);
++          }
++
++          /* Connect l2ptbl to the new LX pte */
++          l2ptp  = PTBL_TO_PTADDR(l2ptbl) + ELAN3_L2_INDEX(addr) * ELAN3_PTP_SIZE;
++          tl2ptp = PTBL_TO_PTADDR(lXptbl) | (idx * ELAN3_PTE_SIZE) | ELAN3_ET_PTE;
++
++          elan3_writeptp (dev, l2ptp, tl2ptp);
++
++          /* Now need to lock the l1 ptbl */
++          elan3mmu_unlock_ptbl (l2ptbl, l2lock, l2flags);
++
++          elan3mmu_lock_ptbl (l1ptbl, 0, elan3mmu, addr, PTBL_LEVEL_1, &l1lock, &l1flags);
++          elan3mmu_lock_ptbl (l2ptbl, 0, elan3mmu, addr, PTBL_LEVEL_2, &l2lock, &l2flags);
++
++          tl1ptp = elan3_readptp (dev, l1ptp);
++          if (ELAN3_PTP_TYPE(tl1ptp) != ELAN3_ET_INVALID)
++          {
++              HAT_PRINTF0 (2, "elan3mmu_ptealloc: beaten to it,  free l2 ptbl/lx pte\n");
++              
++              tl2ptp = ELAN3_INVALID_PTP;
++              elan3_writeptp (dev, l2ptp, tl2ptp);
++              
++              HAT_PRINTF2 (2, "elan3mmu_ptealloc: write level 2 ptp %lx to %x\n", l2ptp, tl2ptp);
++              HAT_PRINTF2 (2, "elan3mmu_ptealloc: freeing l2 ptbl %p (%x)\n", l2ptbl, l2ptbl->ptbl_flags);
++              
++              elan3mmu_free_l2ptbl (dev, l2ptbl, l2lock, l2flags);
++              elan3mmu_free_pte (dev, elan3mmu, lXptbl, idx);
++
++              elan3mmu_unlock_ptbl (l1ptbl, l1lock, l1flags);
++
++              goto retryl1;
++          }
++          
++          /* Now have L1 locked,  so install the L2 ptbl */
++          l1ptp  = PTBL_TO_PTADDR(l1ptbl) + ELAN3_L1_INDEX(addr)*ELAN3_PTP_SIZE;
++          tl1ptp = PTBL_TO_PTADDR(l2ptbl) | ELAN3_ET_PTP;
++          l1ptbl->ptbl_valid++;
++
++          HAT_PRINTF3 (2, "elan3mmu_ptealloc: inc valid for level %d ptbl %p to %d\n", 
++                       PTBL_LEVEL(l1ptbl->ptbl_flags), l1ptbl, l1ptbl->ptbl_valid);
++          
++          elan3_writeptp (dev, l1ptp, tl1ptp);
++          
++          HAT_PRINTF2 (2, "elan3mmu_ptealloc: write l1ptp %lx to %x\n", l1ptp, tl1ptp);
++
++          /* unordered unlock - lock l1ptbl, lock l2ptbl, unlock l1ptbl */
++          elan3mmu_unlock_ptbl (l1ptbl, l1lock, l2flags); /* need to unlock with the l2flags to keep irq order correct */
++
++          *pptbl = l2ptbl;
++          *plock = l2lock;
++          *flags = l1flags; /* return the l1flags here as we have released the l2flags already to keep order */
++
++          /* return holding l2lock */
++          return (l2ptp);
++      }
++
++      HAT_PRINTF0 (2, "elan3mmu_ptealloc: allocating level 2 and level 3 page tables\n");
++
++      /* Allocate a level 2 and level 3 page table and link them together */
++      if ((l2ptbl = elan3mmu_alloc_l2ptbl (dev, attr, l1ptbl, elan3mmu, ELAN3_L2_BASE(addr), &l2lock, &l2flags)) == NULL)
++          return ((sdramaddr_t) 0);
++
++      if ((l3ptbl = elan3mmu_alloc_l3ptbl (dev, attr | PTE_NO_SLEEP, l2ptbl, elan3mmu, ELAN3_L3_BASE(addr), &l3lock, &l3flags)) == NULL)
++      {
++          elan3mmu_unlock_ptbl (l2ptbl, l2lock, l2flags);
++          return ((sdramaddr_t) 0);
++      }
++
++      ASSERT (PTBL_IS_LOCKED (l2ptbl->ptbl_flags));
++      ASSERT (PTBL_LEVEL (l2ptbl->ptbl_flags) == PTBL_LEVEL_2);
++      ASSERT (PTBL_IS_LOCKED (l3ptbl->ptbl_flags));
++      ASSERT (PTBL_LEVEL (l3ptbl->ptbl_flags) == PTBL_LEVEL_3);
++
++      HAT_PRINTF6 (2, "elan3mmu_ptealloc: l2ptbl %p (%x,%x) l3ptbl %p (%x,%x)\n",
++                   l2ptbl, l2ptbl->ptbl_flags, l2ptbl->ptbl_base,
++                   l3ptbl, l3ptbl->ptbl_flags, l3ptbl->ptbl_base);
++
++      if (CTXT_IS_KERNEL (elan3mmu->elan3mmu_ctxt))
++      {
++          l2ptbl->ptbl_flags |= PTBL_KERNEL;
++          elan3mmu_kernel_l3ptbl (l3ptbl);
++      }
++      
++      /*
++       * Connect L3 ptbl to the new L2 ptbl.
++       */
++      l2ptp  = PTBL_TO_PTADDR(l2ptbl) + ELAN3_L2_INDEX(addr) * ELAN3_PTP_SIZE;
++      tl2ptp = PTBL_TO_PTADDR(l3ptbl) | ELAN3_ET_PTP;
++
++      l2ptbl->ptbl_valid = 1;
++
++      HAT_PRINTF3 (2, "elan3mmu_ptealloc: set valid for level %d ptbl %p to %d\n", 
++                   PTBL_LEVEL(l2ptbl->ptbl_flags), l2ptbl, l2ptbl->ptbl_valid);
++
++      HAT_PRINTF2 (2, "elan3mmu_ptealloc: write level 2 ptp %lx to %x\n", l2ptp, tl2ptp);
++
++      elan3_writeptp (dev, l2ptp, tl2ptp);
++
++      /* 
++       * Now need to lock the l1 ptbl - to maintain lock ordering
++       * we set the PTBL_KEEP bit to stop the l3 ptbl from being 
++       * stolen and drop the locks in the order we aquired them
++       */
++      l3ptbl->ptbl_flags |= PTBL_KEEP;
++
++      elan3mmu_unlock_ptbl (l3ptbl, l3lock, l3flags);
++      elan3mmu_unlock_ptbl (l2ptbl, l2lock, l2flags);
++
++      elan3mmu_lock_ptbl (l1ptbl, 0, elan3mmu, addr, PTBL_LEVEL_1, &l1lock, &l1flags);
++      elan3mmu_lock_ptbl (l3ptbl, 0, elan3mmu, addr, PTBL_LEVEL_3, &l3lock, &l3flags);
++          
++      l3ptbl->ptbl_flags &= ~PTBL_KEEP;
++         
++      /* Now have l1 and l3 ptbls locked,  so install the new l2 ptbl into the l1. */
++      tl1ptp = elan3_readptp (dev, l1ptp);
++
++      HAT_PRINTF2 (2, "elan3mmu_ptealloc: l1ptp %lx is %x\n", l1ptp, tl1ptp);
++
++      if (ELAN3_PTP_TYPE(tl1ptp) != ELAN3_ET_INVALID)
++      {
++          HAT_PRINTF0 (2, "elan3mmu_ptealloc: beaten to it,  free l2/l3 ptbls\n");
++
++          /* free off the level 3 page table */
++          HAT_PRINTF2 (2, "elan3mmu_ptealloc: freeing l3 ptbl %p (%x)\n", l3ptbl, l3ptbl->ptbl_flags);
++
++          l3ptbl->ptbl_flags &= ~PTBL_KEEP;
++          elan3mmu_free_l3ptbl (dev, l3ptbl, l3lock, l3flags);
++
++          /* and unlock the level 1 ptbl */
++          elan3mmu_unlock_ptbl (l1ptbl, l1lock, l1flags);
++          
++          /* lock the level 2 page table, and clear out the PTP, then free it */
++          (void) elan3mmu_lock_ptbl (l2ptbl, 0, elan3mmu, addr, PTBL_LEVEL_2, &l2lock, &l2flags);
++
++          HAT_PRINTF2 (2, "elan3mmu_ptealloc: locked l2 ptbl %p (%x)\n", l2ptbl, l2ptbl->ptbl_flags);
++          
++          tl2ptp = ELAN3_INVALID_PTP;
++          elan3_writeptp (dev, l2ptp, tl2ptp);
++          l2ptbl->ptbl_valid = 0;
++
++          HAT_PRINTF3 (2, "elan3mmu_ptealloc: set to 0 valid for level %d ptbl %p to %d\n", PTBL_LEVEL(l2ptbl->ptbl_flags), l2ptbl, l2ptbl->ptbl_valid); 
++
++          HAT_PRINTF2 (2, "elan3mmu_ptealloc: write level 2 ptp %lx to %x\n", l2ptp, tl2ptp);
++          HAT_PRINTF2 (2, "elan3mmu_ptealloc: freeing l2 ptbl %p (%x)\n", l2ptbl, l2ptbl->ptbl_flags);
++
++          elan3mmu_free_l2ptbl (dev, l2ptbl, l2lock, l2flags);
++
++          goto retryl1;
++      }
++      
++      HAT_PRINTF4 (2, "elan3mmu_ptealloc: l1ptbl is %p (%x), l3ptbl is %p (%x)\n", 
++                   l1ptbl, l1ptbl->ptbl_flags, l3ptbl, l3ptbl->ptbl_flags);
++
++      /* Now have L1 and L3 locked,  so install the L2 ptbl */
++      l1ptp  = PTBL_TO_PTADDR(l1ptbl) + ELAN3_L1_INDEX(addr)*ELAN3_PTP_SIZE;
++      tl1ptp = PTBL_TO_PTADDR(l2ptbl) | ELAN3_ET_PTP;
++      l1ptbl->ptbl_valid++;
++
++      HAT_PRINTF3 (2, "elan3mmu_ptealloc: inc valid for level %d ptbl %p to %d\n", 
++                   PTBL_LEVEL(l1ptbl->ptbl_flags), l1ptbl, l1ptbl->ptbl_valid);
++
++      elan3_writeptp (dev, l1ptp, tl1ptp);
++
++      HAT_PRINTF2 (2, "elan3mmu_ptealloc: write l1ptp %lx to %x\n", l1ptp, tl1ptp);
++
++      /* unordered unlock - lock l1ptbl, lock l3ptbl, unlock l1ptbl */
++      elan3mmu_unlock_ptbl (l1ptbl, l1lock, l3flags); /* free using l3flags to keep irq ordering */
++
++      l3pte = PTBL_TO_PTADDR (l3ptbl) + ELAN3_L3_INDEX(addr)*ELAN3_PTE_SIZE;
++
++      /* Level 3 ptbl is already locked,  so just return the pte */
++      *pptbl = l3ptbl;
++      *plock = l3lock;
++      *flags = l1flags; /* return l1flags to keep irq ordering */
++
++      return (l3pte);
++
++    default:
++      panic ("elan3mmu_ptealloc: found bad entry in level 1 page table");
++      /* NOTREACHED */
++    }
++
++    HAT_PRINTF1 (2, "elan3mmu_ptealloc: chain to level 2 ptbl from ptp %x\n", tl1ptp);
++
++    l2ptbl = elan3mmu_ta_to_ptbl (elan3mmu, &tl1ptp);
++    l2ptp  = PTBL_TO_PTADDR(l2ptbl) + ELAN3_L2_INDEX(addr)*ELAN3_PTP_SIZE;
++    l2base = ELAN3_L2_BASE(addr);
++
++    tl2ptp = elan3_readptp (dev, l2ptp);
++
++    HAT_PRINTF5 (2, "elan3mmu_ptealloc: l2ptbl %p l2ptp %lx l2base %x (%x) : tl2ptp %x\n",
++               l2ptbl, l2ptp, l2base, l2ptbl->ptbl_base, tl2ptp);
++
++    switch (ELAN3_PTP_TYPE(tl2ptp))
++    {
++    case ELAN3_ET_PTE:
++      if (level == PTBL_LEVEL_2) {
++          /* this is a pointer to a pte,  we should just return it */
++
++          switch (elan3mmu_lock_ptbl (l2ptbl, 0, elan3mmu, addr, PTBL_LEVEL_2, &l2lock, &l2flags))
++          {
++          case LK_PTBL_OK:
++              break;
++      
++          case LK_PTBL_FAILED:
++              panic ("elan3mmu_ptealloc: l2 lock failed");
++              /* NOTREACHED */
++              
++          case LK_PTBL_MISMATCH:
++              HAT_PRINTF6 (2, "elan3mmu_ptealloc: PTBL_MISMATCH : ptbl %p flags %x elan3mmu %p base %x (%p %x)\n",
++                           l2ptbl, l2ptbl->ptbl_flags, l2ptbl->ptbl_elan3mmu, l2ptbl->ptbl_base, elan3mmu, addr);
++              
++              /*
++               * We've trogged down to this ptbl,  but someone has just
++               * stolen it,  so try all over again.
++               */
++              goto retryl1;
++              
++          default:
++              panic ("elan3mmu_ptealloc: elan3mmu_lock_ptbl returned bad value");
++              /* NOTREACHED */
++          }
++
++
++          tl2ptp = elan3_readptp (dev, l2ptp);
++          if (ELAN3_PTP_TYPE(tl2ptp) != ELAN3_ET_PTE)
++          {
++              elan3mmu_unlock_ptbl (l2ptbl, l2lock, l2flags);
++              goto retryl1;
++          }
++
++          *pptbl = l2ptbl;
++          *plock = l2lock;
++          *flags = l2flags;
++
++          /* return holdind l2lock */
++          return (l2ptp);
++      }
++      panic ("elan3mmu: found pte in level 2 page table");
++      /* NOTREACHED */
++
++    case ELAN3_ET_PTP:
++      break;
++
++    case ELAN3_ET_INVALID:
++      if (level == PTBL_LEVEL_2) 
++      {
++          if ((lXptbl = elan3mmu_alloc_pte (dev, elan3mmu, &idx)) == NULL)
++              return ((sdramaddr_t) 0);
++
++          switch (elan3mmu_lock_ptbl (l2ptbl, 0, elan3mmu, addr, PTBL_LEVEL_2, &l2lock, &l2flags))
++          {
++          case LK_PTBL_OK:
++              break;
++      
++          case LK_PTBL_FAILED:
++              panic ("elan3mmu_ptealloc: l2 lock failed");
++              /* NOTREACHED */
++              
++          case LK_PTBL_MISMATCH:
++              HAT_PRINTF6 (2, "elan3mmu_ptealloc: PTBL_MISMATCH : ptbl %p flags %x elan3mmu %p base %x (%p %x)\n",
++                           l2ptbl, l2ptbl->ptbl_flags, l2ptbl->ptbl_elan3mmu, l2ptbl->ptbl_base, elan3mmu, addr);
++              
++              /*
++               * We've trogged down to this ptbl,  but someone has just
++               * stolen it,  so try all over again.
++               */
++              goto retryl1;
++              
++          default:
++              panic ("elan3mmu_ptealloc: elan3mmu_lock_ptbl returned bad value");
++              /* NOTREACHED */
++          }
++
++          tl2ptp = elan3_readptp (dev, l2ptp);
++          if (ELAN3_PTP_TYPE(tl2ptp) != ELAN3_ET_INVALID)
++          {
++              HAT_PRINTF0 (2, "elan3mmu_ptealloc: beaten to it,  free lx pte\n");
++
++              elan3mmu_free_pte (dev, elan3mmu, lXptbl, idx);
++
++              elan3mmu_unlock_ptbl (l2ptbl, l2lock, l2flags);
++              goto retryl1;
++          }
++
++          /* Connect l2ptbl to the new LX pte */
++          tl2ptp = PTBL_TO_PTADDR(lXptbl) | (idx * ELAN3_PTE_SIZE) | ELAN3_ET_PTE;
++                 
++          HAT_PRINTF3 (2, "elan3mmu_ptealloc: inc valid for level %d ptbl %p to %d\n", 
++                       PTBL_LEVEL(l2ptbl->ptbl_flags), l2ptbl, l2ptbl->ptbl_valid);
++          
++          elan3_writeptp (dev, l2ptp, tl2ptp);
++          
++          HAT_PRINTF2 (2, "elan3mmu_ptealloc: write l2ptp %lx to %x\n", l2ptp, tl2ptp);
++
++          *pptbl = l2ptbl;
++          *plock = l2lock;
++          *flags = l2flags;
++
++          /* return holding l2lock */
++          return (l2ptp);
++      }
++      HAT_PRINTF0 (2, "elan3mmu_ptealloc: allocate level 3 page table\n");
++
++      if ((l3ptbl = elan3mmu_alloc_l3ptbl (dev, attr, l2ptbl, elan3mmu, ELAN3_L3_BASE(addr), &l3lock, &l3flags)) == NULL)
++          return ((sdramaddr_t) 0);
++
++      if (CTXT_IS_KERNEL (elan3mmu->elan3mmu_ctxt))
++          elan3mmu_kernel_l3ptbl (l3ptbl);
++
++      /* 
++       * Now need to lock the l2 ptbl - to maintain lock ordering
++       * we set the PTBL_KEEP bit to stop the l3 ptbl from being 
++       * stolen and drop the locks in the order we aquired them
++       */
++      l3ptbl->ptbl_flags |= PTBL_KEEP;
++
++      elan3mmu_unlock_ptbl (l3ptbl, l3lock, l3flags);
++
++      if (elan3mmu_lock_ptbl (l2ptbl, LK_PTBL_FAILOK, elan3mmu, addr, PTBL_LEVEL_2, &l2lock, &l2flags) == LK_PTBL_MISMATCH)
++      {
++          HAT_PRINTF0 (2, "elan3mmu_ptealloc: l2ptbl freed, free l3 ptbl and try again\n");
++            
++          elan3mmu_lock_ptbl (l3ptbl, 0, elan3mmu, addr, PTBL_LEVEL_3, &l3lock, &l3flags);
++
++          /* free off the level 3 page table, and try again */
++          l3ptbl->ptbl_flags &= ~PTBL_KEEP;
++          elan3mmu_free_l3ptbl (dev, l3ptbl, l3lock, l3flags);
++          
++          goto retryl1;
++      }
++
++      elan3mmu_lock_ptbl (l3ptbl, 0, elan3mmu, addr, PTBL_LEVEL_3, &l3lock, &l3flags);
++
++      l3ptbl->ptbl_flags &= ~PTBL_KEEP;
++
++      /* Now have L2 and L3 ptbls locked, see if someone has beaten us to it. */
++      tl2ptp = elan3_readptp (dev, l2ptp);
++
++      HAT_PRINTF2 (2, "elan3mmu_ptealloc: l2ptp at %lx is %x\n", l2ptp, tl2ptp);
++
++      if (ELAN3_PTP_TYPE(tl2ptp) != ELAN3_ET_INVALID)
++      {
++          HAT_PRINTF0 (2, "elan3mmu_ptealloc: beaten to it, free l3 ptbl and try again\n");
++
++          /* free off the level 3 page table, and try again */
++          l3ptbl->ptbl_flags &= ~PTBL_KEEP;
++          elan3mmu_free_l3ptbl (dev, l3ptbl, l3lock, l3flags);
++          
++          /* Someone has allocated the ptbl before us */
++          elan3mmu_unlock_ptbl (l2ptbl, l2lock, l2flags);
++          
++          goto retryl1;
++      }
++
++      ASSERT (PTBL_IS_LOCKED (l2ptbl->ptbl_flags));
++
++      /* Install the L3 ptbl into the L2 one */
++      l2ptp  = PTBL_TO_PTADDR(l2ptbl) + ELAN3_L2_INDEX(addr)*ELAN3_PTP_SIZE;
++      tl2ptp = PTBL_TO_PTADDR(l3ptbl) | ELAN3_ET_PTP;
++      l2ptbl->ptbl_valid++;
++
++      HAT_PRINTF3 (2, "elan3mmu_ptealloc: inc valid for level %d ptbl %p to %d\n",
++                   PTBL_LEVEL(l2ptbl->ptbl_flags), l2ptbl, l2ptbl->ptbl_valid);
++
++      elan3_writeptp (dev, l2ptp, tl2ptp);
++
++      HAT_PRINTF2 (2, "elan3mmu_ptealloc: write level 2 ptp %lx to %x\n", l2ptp, tl2ptp);
++
++      /* unordered unlock - lock l2ptbl, lock l3ptbl, unlock l2ptbl */
++      elan3mmu_unlock_ptbl (l2ptbl, l2lock, l3flags); /* free with the l3flags to keep irq ordering */
++
++      l3pte = PTBL_TO_PTADDR(l3ptbl) + ELAN3_L3_INDEX(addr)*ELAN3_PTE_SIZE;
++      
++      /* Level 3 ptbl is already locked, so just return the pte */
++      *pptbl = l3ptbl;
++      *plock = l3lock;
++      *flags = l2flags; /* return l2flags to keep irq ordering */
++
++      return (l3pte);
++
++    default:
++      panic ("elan3mmu_ptealloc: found bad entry in level 2 page table");
++      /* NOTREACHED */
++    }
++
++    HAT_PRINTF1 (2, "elan3mmu_ptealloc: chain to level 3 page table from ptp %x\n", tl2ptp);
++
++    l3ptbl = elan3mmu_ta_to_ptbl (elan3mmu, &tl2ptp);
++    l3pte  = PTBL_TO_PTADDR(l3ptbl) + ELAN3_L3_INDEX(addr)*ELAN3_PTE_SIZE;
++    l3base = ELAN3_L3_BASE(addr);
++
++    HAT_PRINTF4 (2, "elan3mmu_ptealloc: l3ptbl %p 3pte %lx l3base %x (%x)\n",
++               l3ptbl, l3pte, l3base, l3ptbl->ptbl_base);
++               
++    if (elan3mmu_lock_ptbl (l3ptbl, LK_PTBL_FAILOK, elan3mmu, addr, PTBL_LEVEL_3, &l3lock, &l3flags) == LK_PTBL_OK)
++    {
++      *pptbl = l3ptbl;
++      *plock = l3lock;
++      *flags = l3flags;
++
++      return (l3pte);
++    }
++
++    /* got all the way down here,  but its been nicked before we could lock it */
++    /* so try all over again */
++    goto retryl1;
++}
++
++void
++elan3mmu_l1inval (ELAN3MMU *elan3mmu, ELAN3_PTBL *l1ptbl, int attr)
++{
++    ELAN3_DEV     *dev = elan3mmu->elan3mmu_dev;
++    ELAN3_PTP      invalidptp = ELAN3_INVALID_PTP;
++    ELAN3_PTP      tl1ptp;
++    sdramaddr_t   l1ptp;
++    E3_Addr       addr;
++    spinlock_t   *l2lock;
++    ELAN3_PTBL    *l2ptbl;
++    ELAN3_PTBL    *lXptbl;
++    int           idx;
++    int                 i;
++    int                 ret;
++    unsigned long flags;
++
++    l1ptp = PTBL_TO_PTADDR(l1ptbl);
++
++    HAT_PRINTF2 (1, "elan3mmu_l1inval: l1ptbl %p l1ptp %lx\n", l1ptbl, l1ptp);
++
++    for (i = 0, addr = 0; i < ELAN3_L1_ENTRIES; i++, l1ptp += ELAN3_PTP_SIZE)
++    {
++      tl1ptp = elan3_readptp (dev, l1ptp);
++      switch (ELAN3_PTP_TYPE(tl1ptp))
++      {
++      case ELAN3_ET_PTE:
++          lXptbl = elan3mmu_ta_to_ptbl (elan3mmu, &tl1ptp);
++          idx    = (PTP_TO_PT_PADDR(tl1ptp) - PTBL_TO_PTADDR(lXptbl))/ELAN3_PTE_SIZE;  
++
++          HAT_PRINTF3 (2, "elan3mmu_l1inval: l1ptbl %p : lXptbl %p idx %d\n",
++                       l1ptbl, lXptbl, idx);
++
++          /* invalidate the L1 pte. */
++          elan3_writeptp (dev, l1ptp, invalidptp);
++          if (! (attr & PTE_UNLOAD_NOFLUSH))
++              ElanFlushTlb (dev);         
++
++          l1ptbl->ptbl_valid--;
++          elan3mmu_free_pte ( dev, elan3mmu,  lXptbl, idx); 
++
++          HAT_PRINTF3 (2, "elan3mmu_l1inval: dec valid for level %d ptbl %p to %d\n",
++                   PTBL_LEVEL(l1ptbl->ptbl_flags), l1ptbl, l1ptbl->ptbl_valid);
++          
++          break;
++
++      case ELAN3_ET_PTP:
++          HAT_PRINTF5 (2, "elan3mmu_l1inval: l1ptbl %p : ptp %lx (%x) addr %x (%d)\n",
++                       l1ptbl, l1ptp, tl1ptp, addr, i);
++
++          /* invalidate the L1 ptp. */
++          elan3_writeptp (dev, l1ptp, invalidptp);
++          if (! (attr & PTE_UNLOAD_NOFLUSH))
++              ElanFlushTlb (dev);
++
++          /* invalidate the level 2 page table */
++          l2ptbl = elan3mmu_ta_to_ptbl (elan3mmu, &tl1ptp);
++          ret    = elan3mmu_l2inval (elan3mmu, l2ptbl, attr | PTE_UNLOAD_NOFLUSH, addr, &l2lock, &flags);
++
++          ASSERT ((l2ptbl->ptbl_flags & PTBL_KEEP) == 0);
++          
++          if (ret == LK_PTBL_OK)
++          {
++              if (((l2ptbl->ptbl_flags & PTBL_KEEP) == 0) && l2ptbl->ptbl_valid == 0)
++              {
++                  HAT_PRINTF1 (2, "elan3mmu_l1inval: free l2ptbl %p\n", l2ptbl);
++                  
++                  l1ptbl->ptbl_valid--;
++                  elan3mmu_free_l2ptbl (elan3mmu->elan3mmu_dev, l2ptbl, l2lock, flags);
++
++                  HAT_PRINTF3 (2, "elan3mmu_l1inval: dec valid for level %d ptbl %p to %d\n", 
++                               PTBL_LEVEL(l1ptbl->ptbl_flags), l1ptbl, l1ptbl->ptbl_valid);
++              }
++              else
++              {
++                  /* need to keep this page table,  so even though its now empty, */
++                  /* chain it back in */
++                  HAT_PRINTF1 (2, "elan3mmu_l1inval: keep l2ptbl %p\n", l2ptbl);
++
++                  elan3_writeptp (dev, l1ptp, tl1ptp);
++                  elan3mmu_unlock_ptbl (l2ptbl, l2lock, flags);
++              }    
++          }
++          else
++          {
++              l1ptbl->ptbl_valid--;
++
++              HAT_PRINTF3 (2, "elan3mmu_l1inval: dec valid for level %d ptbl %p to %d\n", 
++                           PTBL_LEVEL(l1ptbl->ptbl_flags), l1ptbl, l1ptbl->ptbl_valid);
++          }
++          break;
++          
++      case ELAN3_ET_INVALID:
++          break;
++
++      default:
++          panic ("elan3mmu_l1inval: found invalid entry in level 1 page table");
++          /* NOTREACHED */
++      }
++
++      if (l1ptbl->ptbl_valid == 0)
++          break;
++
++      addr += ELAN3_L1_SIZE;
++    }
++}
++
++int
++elan3mmu_l2inval (ELAN3MMU *elan3mmu, ELAN3_PTBL *l2ptbl, int attr, E3_Addr addr, spinlock_t **pl2lock, unsigned long *flags)
++{
++    ELAN3_DEV   *dev = elan3mmu->elan3mmu_dev;
++    ELAN3_PTP    invalidptp = ELAN3_INVALID_PTP;
++    ELAN3_PTP    tl2ptp;
++    sdramaddr_t l2ptp;
++    spinlock_t *l3lock;
++    unsigned long l3flags;
++    ELAN3_PTBL  *l3ptbl;
++    ELAN3_PTBL  *lXptbl;
++    int         idx;
++    int               i;
++    int               ret;
++
++    HAT_PRINTF2 (1, "elan3mmu_l2inval: l2ptbl %p addr %x\n", l2ptbl, addr);
++
++    ASSERT (PTBL_LEVEL (l2ptbl->ptbl_flags) == PTBL_LEVEL_2);
++    ASSERT (PTBL_LEVEL (l2ptbl->ptbl_parent->ptbl_flags) == PTBL_LEVEL_1);
++
++    ret = elan3mmu_lock_ptbl (l2ptbl, LK_PTBL_FAILOK, elan3mmu, addr, PTBL_LEVEL_2, pl2lock, flags);
++
++    ASSERT (ret == LK_PTBL_OK);
++    ASSERT (l2ptbl->ptbl_elan3mmu == elan3mmu);
++    ASSERT (l2ptbl->ptbl_parent->ptbl_elan3mmu == elan3mmu);
++
++    l2ptp = PTBL_TO_PTADDR(l2ptbl);
++
++    for (i = 0; i < ELAN3_L2_ENTRIES; i++, l2ptp += ELAN3_PTP_SIZE)
++    {
++      tl2ptp = elan3_readptp (dev, l2ptp);
++      switch (ELAN3_PTP_TYPE(tl2ptp))
++      {
++      case ELAN3_ET_PTE:
++          lXptbl = elan3mmu_ta_to_ptbl (elan3mmu, &tl2ptp);
++          idx    = (PTP_TO_PT_PADDR(tl2ptp) - PTBL_TO_PTADDR(lXptbl))/ELAN3_PTE_SIZE;  
++
++          HAT_PRINTF3 (2, "elan3mmu_l2inval: l2ptbl %p : lXptbl %p idx %d\n",
++                       l2ptbl, lXptbl, idx);
++
++          /* invalidate the L2 pte. */
++          elan3_writeptp (dev, l2ptp, invalidptp);
++          if (! (attr & PTE_UNLOAD_NOFLUSH))
++              ElanFlushTlb (dev);
++
++          l2ptbl->ptbl_valid--;
++          elan3mmu_free_pte ( dev, elan3mmu, lXptbl, idx); 
++
++          HAT_PRINTF3 (2, "elan3mmu_l2inval: dec valid for level %d ptbl %p to %d\n", PTBL_LEVEL(l2ptbl->ptbl_flags), l2ptbl, l2ptbl->ptbl_valid); 
++
++          break;
++          
++      case ELAN3_ET_PTP:
++          HAT_PRINTF5 (2, "elan3mmu_l2inval: l2ptbl %p : ptp %lx (%x) addr %x (%d)\n",
++                       l2ptbl, l2ptp, tl2ptp, addr, i);
++
++          /* invalidate the L2 ptp. */
++          elan3_writeptp (dev, l2ptp, invalidptp);
++          if (! (attr & PTE_UNLOAD_NOFLUSH))
++              ElanFlushTlb (dev);
++          
++          /* unload the level 3 page table */
++          l3ptbl = elan3mmu_ta_to_ptbl (elan3mmu, &tl2ptp);
++          ret = elan3mmu_l3inval (elan3mmu, l3ptbl, attr | PTE_UNLOAD_NOFLUSH, addr, &l3lock, &l3flags);
++          
++          if (ret == LK_PTBL_OK)
++          {
++              if ((l3ptbl->ptbl_flags & PTBL_KEEP) == 0 && l3ptbl->ptbl_valid == 0)
++              {
++                  /* decrement the valid count of the level 2 page table, and */
++                  /* free off the level 3 page table */
++                  HAT_PRINTF1 (2, "elan3mmu_l2inval: free l3ptbl %p\n", l3ptbl);
++
++                  l2ptbl->ptbl_valid--;
++                  elan3mmu_free_l3ptbl (elan3mmu->elan3mmu_dev, l3ptbl, l3lock, l3flags);
++
++                  HAT_PRINTF3 (2, "elan3mmu_l2inval: dec valid for level %d ptbl %p to %d\n", 
++                               PTBL_LEVEL(l2ptbl->ptbl_flags), l2ptbl, l2ptbl->ptbl_valid);
++              }
++              else
++              {
++                  /* need to keep this page table,  so even though its now empty, */
++                  /* chain it back in */
++                  HAT_PRINTF1 (2, "elan3mmu_l2inval: keep l3ptbl %p\n", l3ptbl);
++
++                  elan3_writeptp (dev, l2ptp, tl2ptp);
++                  elan3mmu_unlock_ptbl (l3ptbl, l3lock, l3flags);
++              }
++          }
++          else
++          {
++              l2ptbl->ptbl_valid--;
++
++              HAT_PRINTF3 (2, "elan3mmu_l2inval: dec valid for level %d ptbl %p to %d\n", 
++                           PTBL_LEVEL(l2ptbl->ptbl_flags), l2ptbl, l2ptbl->ptbl_valid);
++          }
++          break;
++          
++      case ELAN3_ET_INVALID:
++          break;
++
++      default:
++          panic ("elan3mmu_l2inval: found pte in level 2 page table");
++          /* NOTREACHED */
++      }
++
++      if (l2ptbl->ptbl_valid == 0)
++          break;
++
++      addr += ELAN3_L2_SIZE;
++    }
++
++    ASSERT (PTBL_IS_LOCKED(l2ptbl->ptbl_flags));
++
++    return (ret);
++}
++
++int 
++elan3mmu_l3inval (ELAN3MMU *elan3mmu, ELAN3_PTBL *l3ptbl, int attr, E3_Addr addr, spinlock_t **pl3lock, unsigned long *flags)
++{
++    int ret;
++
++    HAT_PRINTF3 (2, "elan3mmu_l3inval: l3ptbl %p parent %p addr %x\n", l3ptbl, l3ptbl->ptbl_parent, addr);
++
++    ASSERT (PTBL_IS_LOCKED (l3ptbl->ptbl_parent->ptbl_flags));
++    ASSERT (PTBL_LEVEL (l3ptbl->ptbl_parent->ptbl_flags) == PTBL_LEVEL_2);
++    ASSERT (l3ptbl->ptbl_parent->ptbl_elan3mmu == elan3mmu);
++    ASSERT (l3ptbl->ptbl_parent->ptbl_base == VA2BASE (ELAN3_L2_BASE(addr)));
++    
++    ret = elan3mmu_lock_ptbl (l3ptbl, LK_PTBL_FAILOK, elan3mmu, addr, PTBL_LEVEL_3, pl3lock, flags);
++
++    ASSERT (ret == LK_PTBL_OK);
++    ASSERT (PTBL_LEVEL (l3ptbl->ptbl_flags) == PTBL_LEVEL_3);
++
++    elan3mmu_unload_loop (elan3mmu, l3ptbl, 0, ELAN3_L3_ENTRIES, attr);
++
++    ASSERT (PTBL_IS_LOCKED (l3ptbl->ptbl_flags));
++
++    return (ret);
++ }
++
++int
++elan3mmu_lock_this_ptbl (ELAN3_PTBL *ptbl, int flag, spinlock_t **plock, unsigned long *flags)
++{
++    int         level = PTBL_LEVEL (ptbl->ptbl_flags);
++    spinlock_t *lock  = elan3mmu_ptbl_to_lock (level, ptbl);
++
++    local_irq_save (*flags);
++
++    if ((flag & LK_PTBL_NOWAIT) == 0)
++      spin_lock (lock);
++    else if (! spin_trylock (lock)) {
++      local_irq_restore (*flags);
++      return (LK_PTBL_FAILED);
++    }
++    
++    if (level != PTBL_LEVEL (ptbl->ptbl_flags))
++    {
++      spin_unlock (lock);     
++      local_irq_restore (*flags);
++      return (LK_PTBL_MISMATCH);
++    }
++
++    ptbl->ptbl_flags |= PTBL_LOCKED;
++    *plock = lock;
++    return (LK_PTBL_OK);
++}
++
++int
++elan3mmu_lock_ptbl (ELAN3_PTBL *ptbl, u_int flag, ELAN3MMU *elan3mmu, E3_Addr va, int level, spinlock_t **plock, unsigned long *flags)
++{
++    spinlock_t *lock = elan3mmu_ptbl_to_lock (level, ptbl);
++    int         res  = LK_PTBL_MISMATCH;
++
++    local_irq_save (*flags);
++    
++    if ((flag & LK_PTBL_NOWAIT) == 0)
++      spin_lock (lock);
++    else if (spin_trylock (lock) == 0) {
++      local_irq_restore(*flags);
++      return (LK_PTBL_FAILED);
++    }
++    
++    if (PTBL_LEVEL (ptbl->ptbl_flags) != level)
++    {
++      res = LK_PTBL_MISMATCH;
++      goto mismatch;
++    }
++    
++    /* We have the right mutex,  so check that its the ptbl we want. */
++    switch (level)
++    {
++    case PTBL_LEVEL_1: va = ELAN3_L1_BASE(va); break;
++    case PTBL_LEVEL_2: va = ELAN3_L2_BASE(va); break;
++    case PTBL_LEVEL_3: va = ELAN3_L3_BASE(va); break;
++    }
++
++    if (ptbl->ptbl_elan3mmu != elan3mmu || ptbl->ptbl_base != VA2BASE(va))
++    {
++      res = LK_PTBL_MISMATCH;
++      goto mismatch;
++    }
++
++    ASSERT ((ptbl->ptbl_flags & PTBL_LOCKED) == 0);
++    ptbl->ptbl_flags |= PTBL_LOCKED;
++
++    *plock = lock;
++    return (LK_PTBL_OK);
++
++mismatch:
++    if (! (flag & LK_PTBL_FAILOK))
++      panic ("elan3mmu: failed to lock ptbl\n");
++      
++    spin_unlock (lock);
++    local_irq_restore(*flags);
++    return (res);
++}
++
++void
++elan3mmu_unlock_ptbl (ELAN3_PTBL *ptbl, spinlock_t *lock, unsigned long flags)
++{
++    ptbl->ptbl_flags &= ~PTBL_LOCKED;
++    spin_unlock_irqrestore (lock,flags);
++}
++
++static spinlock_t *
++elan3mmu_ptbl_to_lock (int level, ELAN3_PTBL *ptbl)
++{
++    switch (level)
++    {
++    case PTBL_LEVEL_3: return (&l3ptbl_lock[L3PTBL_MTX_HASH(ptbl)]);
++    case PTBL_LEVEL_2: return (&l2ptbl_lock[L2PTBL_MTX_HASH(ptbl)]);
++    case PTBL_LEVEL_1: return (&l1ptbl_lock[L1PTBL_MTX_HASH(ptbl)]);
++    case PTBL_LEVEL_X: 
++      panic ("elan3mmu: ptbl_to_lock, bad level X");
++    default:
++      panic ("elan3mmu: ptbl_to_lock, bad level");
++      /* NOTREACHED */
++    }
++    return (NULL);
++}
++
++void
++elan3mmu_display (ELAN3MMU *elan3mmu, E3_Addr addr)
++{
++    ELAN3_DEV   *dev = elan3mmu->elan3mmu_dev;
++    ELAN3_PTBL  *l1ptbl;
++    sdramaddr_t l1ptp;
++    spinlock_t *l1lock;
++    ELAN3_PTE    tl1pte;
++    ELAN3_PTP    tl1ptp;
++    E3_Addr     l1base;
++    ELAN3_PTBL  *l2ptbl;
++    sdramaddr_t l2ptp;
++    ELAN3_PTE    tl2pte;
++    spinlock_t *l2lock;
++    ELAN3_PTP    tl2ptp;
++    E3_Addr     l2base;
++    ELAN3_PTBL  *l3ptbl;
++    sdramaddr_t l3pte;
++    ELAN3_PTE    tl3pte;
++    spinlock_t *l3lock;
++    ELAN3_PTBL  *lXptbl;
++    int         idx;
++    unsigned long flags;
++
++    elan3_debugf (NULL, DBG_HAT, "elan3mmu_display: elan3mmu %p addr %x\n", elan3mmu, addr);
++
++    l1ptbl = elan3mmu->elan3mmu_l1ptbl;
++
++    if (l1ptbl == NULL)
++      return;
++
++    l1ptp  = PTBL_TO_PTADDR(l1ptbl) + ELAN3_L1_INDEX(addr)*ELAN3_PTP_SIZE;
++    l1base = ELAN3_L1_BASE(addr);
++    
++    tl1ptp = elan3_readptp (dev, l1ptp);
++    elan3_debugf (NULL, DBG_HAT, "elan3mmu_display: l1ptbl %p l1ptp %lx l1base %x : tl1ptp %x\n", l1ptbl, l1ptp, l1base, tl1ptp);
++    
++    switch (ELAN3_PTP_TYPE(tl1ptp))
++    {
++    case ELAN3_ET_PTE:
++      elan3_debugf (NULL, DBG_HAT, "elan3mmu_display: level 1 page table for pte %x\n", tl1ptp);
++    
++      lXptbl = elan3mmu_ta_to_ptbl (elan3mmu, &tl1ptp);
++      idx    = (PTP_TO_PT_PADDR(tl1ptp) - PTBL_TO_PTADDR(lXptbl))/ELAN3_PTE_SIZE;  
++      
++      elan3_debugf (NULL, DBG_HAT, "elan3mmu_display: lXptbl %p idx %d\n",lXptbl, idx);
++
++      tl1pte = elan3_readpte (dev,(PTBL_TO_PTADDR (lXptbl) + idx * ELAN3_PTE_SIZE));
++
++      switch (elan3mmu_lock_ptbl (l1ptbl, LK_PTBL_FAILOK, elan3mmu, addr, PTBL_LEVEL_1, &l1lock, &flags))
++      {
++      case LK_PTBL_OK:
++          elan3mmu_unlock_ptbl (l1ptbl, l1lock, flags);
++          elan3_debugf (NULL, DBG_HAT, "elan3mmu_display: lvl 1 l1pte matches value %llx\n", (long long) tl1pte);
++          break;
++          
++      case LK_PTBL_FAILED:
++          panic ("elan3mmu_display: l1 lock failed");
++          /* NOTREACHED */
++          
++      case LK_PTBL_MISMATCH:
++          elan3_debugf (NULL, DBG_HAT, "elan3mmu_display: PTBL_MISMATCH : lvl 1 ptbl %p flags %x elan3mmu %p base %x (%p %x) %llx\n",
++                        l1ptbl, l1ptbl->ptbl_flags, l1ptbl->ptbl_elan3mmu, l1ptbl->ptbl_base, elan3mmu, addr, (long long)tl1pte);
++          
++          break;
++      default:
++          panic ("elan3mmu_display: lvl 1 elan3mmu_lock_ptbl returned bad value");
++          /* NOTREACHED */
++      }
++      return;
++      
++    case ELAN3_ET_INVALID:
++      return;
++      
++    case ELAN3_ET_PTP:
++      break;
++      
++    default:
++      panic ("elan3mmu_display: found bad entry in level 1 page table");
++      /* NOTREACHED */
++    }
++    
++    elan3_debugf (NULL, DBG_HAT, "elan3mmu_display: chain to level 2 ptbl from ptp %x\n", tl1ptp);
++    
++    l2ptbl = elan3mmu_ta_to_ptbl (elan3mmu, &tl1ptp);
++    l2ptp  = PTBL_TO_PTADDR(l2ptbl) + ELAN3_L2_INDEX(addr)*ELAN3_PTP_SIZE;
++    l2base = ELAN3_L2_BASE(addr);
++    
++    tl2ptp = elan3_readptp (dev, l2ptp);
++    elan3_debugf (NULL, DBG_HAT, "elan3mmu_display: l2ptbl %p l2ptp %lx l2base %x : tl2ptp %x\n",
++               l2ptbl, l2ptp, l2base, tl2ptp);
++    
++    switch (ELAN3_PTP_TYPE(tl2ptp))
++    {
++    case ELAN3_ET_PTE:
++      elan3_debugf (NULL, DBG_HAT, "elan3mmu_display: level 2 page table for pte %x\n", tl2ptp);
++    
++      lXptbl = elan3mmu_ta_to_ptbl (elan3mmu, &tl2ptp);
++      idx    = (PTP_TO_PT_PADDR(tl2ptp) - PTBL_TO_PTADDR(lXptbl))/ELAN3_PTE_SIZE;  
++      
++      elan3_debugf (NULL, DBG_HAT, "elan3mmu_display: lXptbl %p idx %d\n",lXptbl, idx);
++
++      tl2pte = elan3_readpte (dev,(PTBL_TO_PTADDR (lXptbl) + idx * ELAN3_PTE_SIZE));
++
++      switch (elan3mmu_lock_ptbl (l2ptbl, LK_PTBL_FAILOK, elan3mmu, addr, PTBL_LEVEL_2, &l2lock, &flags))
++      {
++      case LK_PTBL_OK:
++          elan3mmu_unlock_ptbl (l2ptbl, l2lock, flags);
++          elan3_debugf (NULL, DBG_HAT, "elan3mmu_display: lvl 2 l1pte matches value %llx\n", (long long)tl2pte);
++          break;
++          
++      case LK_PTBL_FAILED:
++          panic ("elan3mmu_display: l2 lock failed");
++          /* NOTREACHED */
++          
++      case LK_PTBL_MISMATCH:
++          elan3_debugf (NULL, DBG_HAT, "elan3mmu_display: PTBL_MISMATCH : lvl 2 ptbl %p flags %x elan3mmu %p base %x (%p %x) %llx\n",
++                        l2ptbl, l2ptbl->ptbl_flags, l2ptbl->ptbl_elan3mmu, l2ptbl->ptbl_base, elan3mmu, addr, (long long) tl2pte);
++          
++          break;
++      default:
++          panic ("elan3mmu_display: lvl 2 elan3mmu_lock_ptbl returned bad value");
++          /* NOTREACHED */
++      }
++      return;
++      
++    case ELAN3_ET_INVALID:
++      return;
++      
++    case ELAN3_ET_PTP:
++      break;
++
++    default:
++      panic ("elan3mmu_display: found bad entry in level 2 page table");
++      /* NOTREACHED */
++    }
++    
++    elan3_debugf (NULL, DBG_HAT, "elan3mmu_display: chain to level 3 page table from ptp %x\n", tl2ptp);
++    
++    l3ptbl = elan3mmu_ta_to_ptbl (elan3mmu, &tl2ptp);
++    l3pte  = PTBL_TO_PTADDR(l3ptbl) + ELAN3_L3_INDEX(addr)*ELAN3_PTE_SIZE;
++    
++    elan3_debugf (NULL, DBG_HAT, "elan3mmu_display: l3ptbl %p l3pte %lx\n",l3ptbl, l3pte);
++    
++    tl3pte = elan3_readpte (dev, l3pte);
++    switch (elan3mmu_lock_ptbl (l3ptbl, LK_PTBL_FAILOK, elan3mmu, addr, PTBL_LEVEL_3, &l3lock, &flags))
++    {
++    case LK_PTBL_OK:
++      elan3mmu_unlock_ptbl (l3ptbl, l3lock, flags);
++      elan3_debugf (NULL, DBG_HAT, "elan3mmu_display: l3pte matches value %llx\n", (long long) tl3pte);
++      break;
++      
++    case LK_PTBL_FAILED:
++      panic ("elan3mmu_display: l3 lock failed");
++      /* NOTREACHED */
++      
++    case LK_PTBL_MISMATCH:
++      elan3_debugf (NULL, DBG_HAT, "elan3mmu_display: PTBL_MISMATCH : ptbl %p flags %x elan3mmu %p base %x (%p %x) %llx\n",
++                   l3ptbl, l3ptbl->ptbl_flags, l3ptbl->ptbl_elan3mmu, l3ptbl->ptbl_base, elan3mmu, addr, (long long) tl3pte);
++      
++      break;
++      
++    default:
++      panic ("elan3mmu_display: elan3mmu_lock_ptbl returned bad value");
++      /* NOTREACHED */
++    }
++}
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan3/elan3mmu_linux.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan3/elan3mmu_linux.c  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan3/elan3mmu_linux.c       2005-05-11 12:10:12.406937440 -0400
+@@ -0,0 +1,284 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: elan3mmu_linux.c,v 1.50.2.3 2004/12/14 10:19:51 mike Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/vm/elan3mmu_linux.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <qsnet/kpte.h>
++
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elanvp.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanctxt.h>
++#include <elan3/elandebug.h>
++#include <elan3/urom_addrs.h>
++#include <elan3/thread.h>
++
++/*
++ * Strategy for syncing main <-> elan pte's:
++ * 
++ *   Install callbacks for linux flush_tlb_page(), flush_tlb_range(),
++ *   flush_tlb_all(), and flush_tlb_mm() so when a main PTE changes,
++ *   the elan translations, if any, are invalidated.  They can then be
++ *   faulted in again with the correct physical page, perms, etc., on demand. 
++ * 
++ *   Callbacks are stacked on the mm_struct, one per context.  We also stack
++ *   a ctxt pointer so we don't have to do lookups on every call.
++ *
++ *   Sanity check -- we clearly want to flush the elan PTEs in these 
++ *   situations, all of which are covered by tlb_flush_{page,range}()
++ *
++ *     1) kernel/vmscan.c::try_to_swap_out() swaps out a page
++ *
++ *     2) kernel/mremap.c::copy_one_pte() moves a page as a result of the 
++ *     mremap system call
++ * 
++ *     3) kernel/mprotect.c::change_pte_range() changes the permissions of a 
++ *     page as the result of the mprotect system call
++ *
++ * Other Notes: 
++ * 
++ *   Dirty a page in the mains page tables when it is faulted into the elan.
++ *   This way it will not be thrown away by the swapper.
++ * 
++ *   Pages write protected for COW are copied by elan3mmu_main_pagefault()
++ *   when a writeable translation is loaded into the elan.
++ */
++
++caddr_t            elan3mmu_kernel_invalid_space;
++ELAN3_PTE     elan3mmu_kernel_invalid_pte_val;
++
++void
++elan3mmu_init_osdep (void)
++{
++    pte_t *pte;
++
++    KMEM_GETPAGES (elan3mmu_kernel_invalid_space, caddr_t, 1, TRUE);
++
++    ASSERT(elan3mmu_kernel_invalid_space != NULL);
++
++    pte = find_pte_kernel ((unsigned long) elan3mmu_kernel_invalid_space);
++
++    elan3mmu_kernel_invalid_pte_val = ELAN3_PTE_64_BIT | (pte_phys(*pte) & ELAN3_PTE_PFN_MASK) | ELAN3_PERM_REMOTEREAD | ELAN3_ET_PTE;
++
++#ifdef __alpha
++    /*
++     * NOTE: Elan sign-extends bit 48 of the physical address, so if we need to
++     *       set any of bits 63:48, then we will set them all by setting bit 48/
++     */
++    if (alpha_mv.pci_dac_offset & 0xFFFF000000000000ull)
++        elan3mmu_kernel_invalid_pte_val |= (1ull << 48);
++    else
++      elan3mmu_kernel_invalid_pte_val |= alpha_mv.pci_dac_offset;
++#endif
++
++    HAT_PRINTF(0x10, "elan3mmu_invalid_space at %p phys=%llx pte=%llx\n", elan3mmu_kernel_invalid_space, 
++             (unsigned long long) pte_phys(*pte), (unsigned long long) elan3mmu_kernel_invalid_pte_val);
++}
++
++void
++elan3mmu_fini_osdep()
++{
++    KMEM_FREEPAGES (elan3mmu_kernel_invalid_space, 1);
++}
++
++void
++elan3mmu_alloc_osdep (ELAN3MMU *elan3mmu)
++{
++    elan3mmu->elan3mmu_coproc_mm = current->mm;
++}
++
++/*
++ * Convert physical page frame number to elan pte.
++ */
++ELAN3_PTE
++elan3mmu_phys_to_pte (ELAN3_DEV *dev, physaddr_t paddr, int perm)
++{
++    ELAN3_PTE newpte;
++    
++    ASSERT (paddr != 0);
++    
++    if ((paddr & dev->SdramPhysMask) == dev->SdramPhysBase)           /* SDRAM, turn on PTE_LOCAL bit */
++    {
++      PRINTF(NULL, DBG_HAT, "elan3mmu_phys_to_pte: phys %llx SDRAM\n", (unsigned long long) paddr);
++      
++      newpte = ELAN3_PTE_LOCAL | (paddr & ELAN3_PTE_PFN_MASK & ~dev->SdramPhysMask) | perm | ELAN3_ET_PTE;
++    }
++#if defined(LINUX_ALPHA)
++    else if ((paddr & dev->PciPhysMask) == dev->PciPhysBase)
++    {
++      PRINTF(NULL, DBG_HAT, "elan3mmu_phys_to_pte: phys %llx PCI\n", (unsigned long long) paddr);
++      newpte = ELAN3_PTE_64_BIT | (paddr & ELAN3_PTE_PFN_MASK & ~dev->PciPhysMask) | perm | ELAN3_ET_PTE;
++    }
++#endif
++    else                                              /* main memory, must convert to PCI view */
++    {
++      PRINTF(NULL, DBG_HAT, "elan3mmu_phys_to_pte: phys %llx is main memory\n", (unsigned long long) paddr);
++
++      /* main memory, just set the architecture specific PTE_BYPASS bit */
++      /* This requires the Tsunami chipset being programmed to support
++       * the monster window option. This is in linux-2.4.5 and later kernels 
++       * and is also patched into the RH 7.1/2.4.3-12 Alpha kernel
++       */
++      newpte = ELAN3_PTE_64_BIT | (paddr & ELAN3_PTE_PFN_MASK) | perm | ELAN3_ET_PTE;
++
++#ifdef __alpha
++      /*
++       * NOTE: Elan sign-extends bit 48 of the physical address, so if we need to
++       *       set any of bits 63:48, then we will set them all by setting bit 48/
++       */
++      if (alpha_mv.pci_dac_offset & 0xFFFF000000000000ull)
++            newpte |= (1ull << 48);
++        else
++          newpte |= alpha_mv.pci_dac_offset;
++#endif
++    }
++
++    if ( ELAN3_PERM_WRITEABLE( perm )) 
++      newpte |= ( ELAN3_PTE_MOD | ELAN3_PTE_REF );
++    else
++      newpte |= ( ELAN3_PTE_REF ) ;
++
++    return (newpte);
++}
++
++ELAN3_PTE
++elan3mmu_kernel_invalid_pte (ELAN3MMU *elan3mmu)
++{
++    if (elan3mmu->elan3mmu_dev->Devinfo.dev_revision_id == PCI_REVISION_ID_ELAN3_REVB)
++      return (elan3mmu_kernel_invalid_pte_val);
++    return (ELAN3_INVALID_PTE);
++}
++
++/* 
++ * Invalidate a range of addresses for specified context.
++ */
++void
++elan3mmu_pte_range_unload (ELAN3MMU *elan3mmu, struct mm_struct *mm, caddr_t addr, unsigned long len)
++{
++    E3_Addr       eaddr;
++    ELAN3MMU_RGN *rgn;
++    unsigned long span;
++
++    spin_lock (&elan3mmu->elan3mmu_lock);
++
++    for (; len; len -= span, addr += span)
++    {
++      rgn = elan3mmu_findrgn_main (elan3mmu, addr, 0);
++
++      if (rgn == NULL || (rgn->rgn_mbase + rgn->rgn_len) < addr)
++          span = len;
++      else if (rgn->rgn_mbase > addr)
++          span = MIN(len, rgn->rgn_mbase - addr);
++      else
++      {
++          span  = MIN(len, (rgn->rgn_mbase + rgn->rgn_len) - addr);
++          eaddr = rgn->rgn_ebase + (addr - rgn->rgn_mbase);
++
++            HAT_PRINTF(0x10, "  unloading eaddr %x main %p (%ld pages)\n", 
++            eaddr, addr, btopr(span));
++          elan3mmu_unload (elan3mmu, eaddr, span, PTE_UNLOAD);
++      }                       /* takes care of elan tlb flush also */
++    }
++
++    spin_unlock (&elan3mmu->elan3mmu_lock);
++}
++
++/*
++ *
++ */
++void
++elan3mmu_update_range (ELAN3MMU *elan3mmu, struct mm_struct *mm, caddr_t vaddr, E3_Addr eaddr, u_int len, u_int perm)
++{
++    u_int roperm = ELAN3_PERM_READONLY(perm & ELAN3_PTE_PERM_MASK) | (perm & ~ELAN3_PTE_PERM_MASK);
++    u_int off;
++
++    HAT_PRINTF3(1, "elan3mmu_update_range (elan3mmu %p addr %p -> %p)\n", elan3mmu, vaddr, vaddr+len-1);
++
++    while (len > 0)
++    {
++      pte_t *pte_ptr;
++      pte_t  pte_value;
++
++      pte_ptr = find_pte_map(mm, (unsigned long)vaddr);
++      if (pte_ptr) {
++          pte_value = *pte_ptr;
++          pte_unmap(pte_ptr);
++      }
++
++      HAT_PRINTF(0x10, "  elan3mmu_update_range %x (%p) %s\n", eaddr, vaddr, 
++              !pte_ptr ? "invalid" : pte_none(pte_value) ? "none " : !pte_present(pte_value) ? "swapped " : 
++              !pte_write(pte_value) ? "RO/COW" : "OK");
++      
++      if (pte_ptr && !pte_none(pte_value) && pte_present(pte_value))
++          for (off = 0; off < PAGE_SIZE; off += ELAN3_PAGE_SIZE)
++              elan3mmu_pteload (elan3mmu, PTBL_LEVEL_3, eaddr + off, pte_phys(pte_value) + off, pte_write(pte_value) ? perm : roperm, PTE_LOAD|PTE_NO_SLEEP|PTE_NO_STEAL);
++      vaddr += PAGESIZE;
++      eaddr += PAGESIZE;
++      len   -= PAGESIZE;
++    }
++}
++
++/* 
++ * Update a range of addresses for specified context.
++ */
++void
++elan3mmu_pte_range_update (ELAN3MMU *elan3mmu, struct mm_struct *mm,caddr_t vaddr, unsigned long len)
++{
++    E3_Addr       eaddr;
++    ELAN3MMU_RGN *rgn;
++    unsigned long span;
++
++    spin_lock (&elan3mmu->elan3mmu_lock);
++
++    for (; len; len -= span, vaddr += span)
++    {
++      rgn = elan3mmu_findrgn_main (elan3mmu, vaddr, 0);
++
++      if (rgn == NULL || (rgn->rgn_mbase + rgn->rgn_len) < vaddr)
++          span = len;
++      else if (rgn->rgn_mbase > vaddr)
++          span = MIN(len, rgn->rgn_mbase - vaddr);
++      else
++      {
++          span  = MIN(len, (rgn->rgn_mbase + rgn->rgn_len) - vaddr);
++          eaddr = rgn->rgn_ebase + (vaddr - rgn->rgn_mbase);
++
++            HAT_PRINTF(0x10, "  updating eaddr %u main %p (%ld pages)\n", 
++            eaddr, vaddr, btopr(span));
++          
++          elan3mmu_update_range(elan3mmu, mm, vaddr, eaddr, span, rgn->rgn_perm);
++      }                       
++    }
++
++    spin_unlock (&elan3mmu->elan3mmu_lock);
++}
++
++/* 
++ * Invalidate all ptes for the given context.
++ */
++void
++elan3mmu_pte_ctxt_unload(ELAN3MMU *elan3mmu)
++{
++    ELAN3_PTBL  *l1ptbl   = (elan3mmu ? elan3mmu->elan3mmu_l1ptbl : NULL);
++    spinlock_t *l1mtx;
++    unsigned long flags;
++
++    if (l1ptbl && elan3mmu_lock_ptbl (l1ptbl, LK_PTBL_FAILOK, elan3mmu, (E3_Addr) 0, 1, &l1mtx, &flags) == LK_PTBL_OK)
++    {
++      elan3mmu_l1inval(elan3mmu, elan3mmu->elan3mmu_l1ptbl, 0);
++      elan3mmu_unlock_ptbl (l1ptbl, l1mtx, flags);
++    }
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan3/elan3ops.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan3/elan3ops.c        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan3/elan3ops.c     2005-05-11 12:10:12.407937288 -0400
+@@ -0,0 +1,170 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: elan3ops.c,v 1.4 2003/09/24 13:57:25 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/os/elan3ops.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <elan/elanmod.h>
++
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elan3ops.h>
++
++extern ELAN_STATS_OPS elan3_device_stats_ops;
++
++ELAN_DEV_OPS elan3_dev_ops = {
++
++      get_position,
++      set_position,   
++
++      ELAN_DEV_OPS_VERSION
++};
++
++ELAN_STATS_OPS elan3_device_stats_ops = {
++      ELAN_STATS_OPS_VERSION,
++
++      stats_get_index_name,
++      stats_get_block,
++      stats_clear_block
++};
++
++static char *elan3_device_stats_names[ELAN3_NUM_STATS] = 
++{
++              "version field",                 /* not cleared */
++              "elan interrupts",
++              "tlb flushes",
++              "traps with invalid context",
++              "interrupts com queue half full",
++              "cproc traps",
++              "dproc traps",
++              "tproc traps",
++              "iproc traps",
++              "event interrupts",
++              "elan page faults",
++              "EopBadAcks",
++              "EopResets",
++              "InputterBadLength",
++              "InputterCRCDiscards",
++              "InputterCRCErrors",
++              "InputterCRCBad",       
++              "errors in dma data",
++              "errors after dma identify",
++              "errors after thread identify",
++              "dma retries",
++              "dma output timeouts",
++              "dma packet ack errors",
++              "forced tproc traps",
++              "too many instruction traps",
++              "output timeouts",
++              "packet ack errors",
++              "LockError",
++              "DeskewError",
++              "PhaseError",
++              "DataError",
++              "FifoOvFlow0",
++              "FifoOvFlow1",
++              "link error value on data error",
++              "correctable ecc errors",
++              "uncorrectable ecc errors",
++              "multiple ecc errors",
++              "sdram bytes free",              /* not cleared */
++              "longest interrupt in ticks",
++              "punts of event int's to thread",
++              "reschedules of event int's thread"
++};
++
++int 
++stats_get_index_name (void *arg, uint  index, caddr_t name)
++{
++      copyout (elan3_device_stats_names[index], name, strlen (elan3_device_stats_names[index]) + 1  /* with \0 */);
++
++      return (0);
++}
++
++int
++stats_get_block (void *arg, uint entries, ulong *value)
++{
++      ELAN3_DEV *dev = (ELAN3_DEV *) arg;
++
++      if ( entries >  ELAN3_NUM_STATS ) /* if space too big only send valid portion */
++              entries = ELAN3_NUM_STATS;
++      
++      copyout(&dev->Stats, value, sizeof(ulong) * entries);
++
++      return (0);
++}
++
++int 
++stats_clear_block (void *arg)
++{
++      ELAN3_DEV *dev = (ELAN3_DEV *) arg;
++      u_long   *ptr = (u_long *) &dev->Stats;
++      int                n;
++      
++      for (n = 0; n < ELAN3_NUM_STATS; n++)
++      {
++              switch (n) 
++              {
++              case offsetof (ELAN3_STATS, Version)/sizeof(u_long):
++              case offsetof (ELAN3_STATS, SdramBytesFree)/sizeof(u_long):
++                      break;
++              default:
++                      ptr[n] = (ulong)0;
++              }
++      }
++      return (0);
++}
++
++int 
++get_position (void *user_data, ELAN_POSITION *position)
++{
++      ELAN3_DEV *dev = (ELAN3_DEV *)user_data;
++
++      copyout(&dev->Position, position, sizeof(ELAN_POSITION));
++
++      return (0);     
++}
++
++int 
++set_position (void *user_data, unsigned short nodeId, unsigned short numNodes)
++{
++      ELAN3_DEV *dev = (ELAN3_DEV *)user_data;
++
++      if (ComputePosition (&dev->Position, nodeId, numNodes, dev->Devinfo.dev_num_down_links_value) != 0)
++              return (EINVAL);
++      
++      return (0);     
++}
++
++int
++elan3_register_dev_stats(ELAN3_DEV * dev) 
++{
++      char name[ELAN_STATS_NAME_MAX_LEN+1];
++
++      sprintf (name, ELAN3_STATS_DEV_FMT, dev->Instance);
++
++      elan_stats_register(&dev->StatsIndex,
++                             name,
++                             sizeof (elan3_device_stats_names)/sizeof (elan3_device_stats_names[0]),
++                             &elan3_device_stats_ops,
++                             (void *)dev);
++
++      return (0);
++}
++
++void
++elan3_deregister_dev_stats(ELAN3_DEV * dev) 
++{
++      elan_stats_deregister(dev->StatsIndex);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan3/elandebug.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan3/elandebug.c       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan3/elandebug.c    2005-05-11 12:10:12.407937288 -0400
+@@ -0,0 +1,151 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: elandebug.c,v 1.25 2003/09/24 13:57:25 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/os/elandebug.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elanvp.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanctxt.h>
++#include <elan3/elandebug.h>
++
++
++void
++elan3_debugf (void *p, unsigned int mode, char *fmt,...)
++{
++    char prefix[128];
++
++#if defined (DIGITAL_UNIX)
++#define PREFIX_FMT    "[%lx.%08x]"
++#define PREFIX_VAL    (int)CURTHREAD()
++#else
++#define PREFIX_FMT    "[%lx.%04d]"
++#define PREFIX_VAL    (current->pid)
++#endif
++
++    if ((unsigned long) p > DBG_NTYPES)
++    {
++      ELAN3_CTXT *ctxt = (ELAN3_CTXT *) p;
++
++        if (elan3_debug_display_ctxt && (ctxt->Capability.cap_mycontext & MAX_ROOT_CONTEXT_MASK) != elan3_debug_display_ctxt)
++            return;
++        if (elan3_debug_ignore_ctxt  && (ctxt->Capability.cap_mycontext & MAX_ROOT_CONTEXT_MASK) == elan3_debug_ignore_ctxt)
++            return;
++ 
++      if (ctxt->Capability.cap_mycontext == ELAN_CAP_UNINITIALISED)
++          sprintf (prefix, PREFIX_FMT " (XXX) ", lbolt, PREFIX_VAL);
++      else
++          sprintf (prefix, PREFIX_FMT " (%03x) ", lbolt, PREFIX_VAL,
++                   ctxt->Capability.cap_mycontext & MAX_ROOT_CONTEXT_MASK);
++    }
++    else
++    {
++      char *what;
++
++      if (elan3_debug_ignore_dev & (1 << ((unsigned long) p)))
++          return;
++
++      switch ((unsigned long) p)
++      {
++      case (int) DBG_DEVICE: what = "dev"; break;
++      case (int) DBG_KCOMM:  what = "kcm"; break;
++      case (int) DBG_ICS:    what = "ics"; break;
++      case (int) DBG_USER:   what = "usr"; break;
++      default:               what = NULL; break;
++      }
++          
++      if (what)
++          sprintf (prefix, PREFIX_FMT " [%s] ", lbolt,  PREFIX_VAL, what);
++      else
++          sprintf (prefix, PREFIX_FMT " [%3d] ", lbolt,  PREFIX_VAL, (int)(long)what);
++    }
++
++    {
++      va_list       ap;
++
++      va_start (ap, fmt);
++      qsnet_vdebugf ((((mode & elan3_debug_buffer)?QSNET_DEBUG_BUFFER:0)|((mode & elan3_debug_console)?QSNET_DEBUG_CONSOLE:0)) , prefix, fmt, ap);
++      va_end (ap);
++    }
++}
++
++
++void
++elan3_alloc_panicstate (ELAN3_DEV *dev, int allocsdram)
++{
++    register int bank;
++
++    if (dev->PanicState.RegPtr == NULL)
++      KMEM_ZALLOC (dev->PanicState.RegPtr, E3_Regs *, sizeof (E3_Regs), 1);
++
++    if (allocsdram)
++      for (bank = 0; bank < ELAN3_SDRAM_NUM_BANKS; bank++)
++          if (dev->PanicState.Sdram[bank] == NULL && dev->SdramBanks[bank].Size)
++              KMEM_ZALLOC (dev->PanicState.Sdram[bank], char *, dev->SdramBanks[bank].Size, 1);
++}
++
++void
++elan3_free_panicstate (ELAN3_DEV *dev)
++{
++    register int bank;
++
++    if (dev->PanicState.RegPtr != NULL)
++      KMEM_FREE (dev->PanicState.RegPtr, sizeof (E3_Regs));
++
++    for (bank = 0; bank < ELAN3_SDRAM_NUM_BANKS; bank++)
++      if (dev->PanicState.Sdram[bank] != NULL && dev->SdramBanks[bank].Size)
++          KMEM_FREE (dev->PanicState.Sdram[bank], dev->SdramBanks[bank].Size);
++
++    bzero (&dev->PanicState, sizeof (dev->PanicState));
++}
++
++void
++elan3_save_panicstate (ELAN3_DEV *dev)
++{
++    register int bank;
++    
++    if (dev->PanicState.RegPtr)
++    {
++      printk ("elan%d: saving state on panic .....\n", dev->Devinfo.dev_instance);
++
++      bcopy ((void *) dev->RegPtr, (void *) dev->PanicState.RegPtr, sizeof (E3_Regs));
++      
++      for (bank = 0; bank < ELAN3_SDRAM_NUM_BANKS; bank++)
++          if (dev->SdramBanks[bank].Size && dev->PanicState.Sdram[bank])
++              elan3_sdram_copyq_from_sdram (dev, (bank << ELAN3_SDRAM_BANK_SHIFT), dev->PanicState.Sdram[bank], dev->SdramBanks[bank].Size);
++      
++    }
++}
++
++int
++elan3_assfail (ELAN3_DEV *dev, char *string, char *file, int line)
++{
++    if (panicstr)
++      return (0);
++
++    printk ("elan: assertion failed '%s' File '%s' Line %d\n", string, file, line);
++
++#if defined(LINUX)
++    elan3_save_panicstate (dev);
++
++    panic ("elan: assertion failed '%s' File '%s' Line %d\n", string, file, line);
++#else
++    cmn_err (CE_PANIC, "elan: assertion failed '%s' File '%s' Line %d\n", string, file, line);
++#endif
++    /*NOTREACHED*/
++    return (0);
++}
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan3/elandev_generic.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan3/elandev_generic.c 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan3/elandev_generic.c      2005-05-11 12:10:12.410936832 -0400
+@@ -0,0 +1,1862 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: elandev_generic.c,v 1.111.2.3 2004/11/15 11:12:36 mike Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/os/elandev_generic.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <qsnet/kthread.h>
++
++#include <elan3/dma.h>
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elanvp.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanctxt.h>
++#include <elan3/elandebug.h>
++#include <elan3/elansyscall.h>
++#include <elan3/urom_addrs.h>
++#include <elan3/elan3ops.h>
++
++/*
++ * Module globals, configurable from system file.
++ */
++u_int  elan3_debug                  = 0;
++u_int  elan3_debug_console      = 0;
++u_int  elan3_debug_buffer           = -1;
++u_int  elan3_debug_ignore_dev       = 0;
++u_int  elan3_debug_ignore_kcomm     = 0;
++u_int  elan3_debug_ignore_ctxt      = 0;
++u_int  elan3_debug_display_ctxt     = 0;
++
++int    eventint_punt_loops;
++int    eventint_punt_ticks;
++int    eventint_resched_ticks;
++
++static void InitialiseDmaBuffers (ELAN3_DEV *dev, ioaddr_t CmdPort);
++static int  ProbeSdram (ELAN3_DEV *dev);
++static void InitialiseSdram (ELAN3_DEV *dev);
++static void ReEnableErrorInterrupts (void *arg);
++void        PollForDmaHungup (void *arg);
++static void elan3_event_interrupt (ELAN3_DEV *dev);
++
++/*
++ * BaseAddr is ptr to the start of a table aligned on a power of two byte address.
++ * SizePower must be in the range of 6 to 12. It defines the number of valid contexts as
++ * shown below.
++ *
++ * SizePower  Valid Contexts  Table size in bytes.
++ *     6           64               1k
++ *     7          128               2k
++ *     8          256               4K
++ *     9          512               8k
++ *    10         1024              16k
++ *    11         2048              32k
++ *    12         4096              64k
++ */
++#define GEN_CONTEXT_PTR(BaseAddr, SizePower) (((E3_uint32) BaseAddr) |        \
++                                            (~((1 << ((SizePower) - 6)) - 1) & 0x3f))
++
++int
++InitialiseElan (ELAN3_DEV *dev, ioaddr_t CmdPort)
++{
++    E3_IprocTrapHeader_BE   TrapCleanup[4];
++    E3_ContextControlBlock  ContextControlBlock;
++    sdramaddr_t             ptr;
++    int                           res;
++    int                           i;
++
++    eventint_punt_loops    = 100;
++    eventint_punt_ticks    = (hz/100);
++    eventint_resched_ticks = (hz/4);
++    
++    dev->Stats.Version     = ELAN3_STATS_VERSION;
++    dev->Position.pos_mode = ELAN_POS_UNKNOWN;
++
++    /*
++     * The elan should have already been reset, so the interrupt mask
++     * should be 0 and the schedule status register should be set to
++     * its initial state
++     */
++    ASSERT (dev->InterruptMask == 0);
++    ASSERT ((read_reg32 (dev, Exts.SchCntReg) & HaltStopAndExtTestMask) == Sched_Initial_Value);
++
++    /*
++     * Write any value here to clear out the half full and error bits of the command
++     * overflow queues.
++     */
++    write_reg32 (dev, ComQueueStatus, 0);
++
++    /* Initialise the cache tags before touching the SDRAM */
++    /* we initialise them to "map" the bottom of SDRAM */
++    for (i = 0; i < E3_NumCacheLines; i++)
++    {
++      write_cache_tag (dev, Tags[i][0].Value, 0x0000000000000000ULL);
++      write_cache_tag (dev, Tags[i][1].Value, 0x0000080000000000ULL);
++      write_cache_tag (dev, Tags[i][2].Value, 0x0000100000000000ULL);
++      write_cache_tag (dev, Tags[i][3].Value, 0x0000180000000000ULL);
++    }
++
++#ifndef CONFIG_MPSAS
++    for (i = 0; i < E3_NumCacheLines*(E3_CACHELINE_SIZE/sizeof(E3_uint64)); i++)
++    {
++      write_cache_set (dev, Set0[i], 0xcac1ecac1ecac1e0ULL);
++      write_cache_set (dev, Set1[i], 0xcac1ecac1ecac1e1ULL);
++      write_cache_set (dev, Set2[i], 0xcac1ecac1ecac1e2ULL);
++      write_cache_set (dev, Set3[i], 0xcac1ecac1ecac1e3ULL);
++    }
++#endif
++
++    if ((res = ProbeSdram(dev)) != ESUCCESS)
++      return (res);
++
++    /* Enable all cache sets before initialising the sdram allocators */
++    write_reg32 (dev, Cache_Control_Reg.ContReg, (dev->Cache_Control_Reg |= CONT_EN_ALL_SETS));
++
++    InitialiseSdram (dev);
++
++    dev->TAndQBase              = elan3_sdram_alloc (dev, ELAN3_TANDQ_SIZE);
++    dev->ContextTable           = elan3_sdram_alloc (dev, ELAN3_CONTEXT_SIZE);
++    dev->ContextTableSize       = ELAN3_NUM_CONTEXTS;
++    dev->CommandPortTraps[0]    = elan3_sdram_alloc (dev, ELAN3_COMMAND_TRAP_SIZE);
++    dev->CommandPortTraps[1]    = elan3_sdram_alloc (dev, ELAN3_COMMAND_TRAP_SIZE);
++    dev->CurrentCommandPortTrap = 0;
++
++    PRINTF3 (DBG_DEVICE, DBG_CONFIG, "InitialiseElan: ContextTable %08lx TAndQ %08lx CommandPortTrap %08lx\n",
++           dev->ContextTable, dev->TAndQBase, dev->CommandPortTraps[0]);
++
++    /* Allocate the thread amd dma trap areas */
++    KMEM_ZALLOC (dev->ThreadTrap, THREAD_TRAP *, sizeof (THREAD_TRAP), TRUE);
++    KMEM_ZALLOC (dev->DmaTrap, DMA_TRAP *, sizeof (DMA_TRAP), TRUE);
++
++    /* Allocate the ctxt table */
++    KMEM_ZALLOC (dev->CtxtTable,  ELAN3_CTXT **, dev->ContextTableSize * sizeof ( ELAN3_CTXT *), TRUE);
++
++    /* Initialise halt queue list */
++    dev->HaltOperationsTailpp   = &dev->HaltOperations;
++
++    /* From elan3/code/harness/elanstuff.c */
++    /* Init the clock. */
++    write_ureg64 (dev, Clock.NanoSecClock, 0);
++    
++    /* Init the instruction count reg. */
++    write_ureg32 (dev, InstCount.s.StatsCount, 0);
++    
++    /* Init the stats control reg. Must be done before the count regs.*/
++    write_ureg32 (dev, StatCont.StatsControl, 0);
++    
++    /* Init the stats count regs. */
++    write_ureg32 (dev, StatCounts[0].s.StatsCount, 0);
++    write_ureg32 (dev, StatCounts[1].s.StatsCount, 0);
++    write_ureg32 (dev, StatCounts[2].s.StatsCount, 0);
++    write_ureg32 (dev, StatCounts[3].s.StatsCount, 0);
++    write_ureg32 (dev, StatCounts[4].s.StatsCount, 0);
++    write_ureg32 (dev, StatCounts[5].s.StatsCount, 0);
++    write_ureg32 (dev, StatCounts[6].s.StatsCount, 0);
++    write_ureg32 (dev, StatCounts[7].s.StatsCount, 0);
++    
++    /*
++     * Initialise the Context_Ptr and Fault_Base_Ptr
++     */
++    write_reg32 (dev, Fault_Base_Ptr, dev->TAndQBase + offsetof(E3_TrapAndQueue, IProcSysCntx));
++    write_reg32 (dev, Context_Ptr, GEN_CONTEXT_PTR (dev->ContextTable, ELAN3_LN2_NUM_CONTEXTS));
++
++    /* scrub the TProc Registers */
++    for (i = 0; i < 8; i++)
++      write_reg32 (dev, Globals[i], 0xdeadbabe);
++    for (i = 0; i < 8; i++)
++      write_reg32 (dev, Outs[i], 0xdeadbabe);
++    for (i = 0; i < 8; i++)
++      write_reg32 (dev, Locals[i], 0xdeadbabe);
++    for (i = 0; i < 8; i++)
++      write_reg32 (dev, Ins[i], 0xdeadbabe);
++
++    /*
++     * Initialise the Queue pointers.  Arrange them so that the starting positions are
++     * farthest apart in one set of the cache. Thus 512 bytes apart,  but with cntx0
++     * thread the same as the interrupt queue.
++     */
++    write_reg32 (dev, TProc_NonSysCntx_FPtr, dev->TAndQBase + offsetof (E3_TrapAndQueue, NonSysCntxThreadQueue[0xc0]));
++    write_reg32 (dev, TProc_NonSysCntx_BPtr, dev->TAndQBase + offsetof (E3_TrapAndQueue, NonSysCntxThreadQueue[0xc0]));
++    write_reg32 (dev, TProc_SysCntx_FPtr,    dev->TAndQBase + offsetof (E3_TrapAndQueue, SysCntxThreadQueue[0x80]));
++    write_reg32 (dev, TProc_SysCntx_BPtr,    dev->TAndQBase + offsetof (E3_TrapAndQueue, SysCntxThreadQueue[0x80]));
++    
++    write_reg32 (dev, DProc_NonSysCntx_FPtr, dev->TAndQBase + offsetof (E3_TrapAndQueue, NonSysCntxDmaQueue[0]));
++    write_reg32 (dev, DProc_NonSysCntx_BPtr, dev->TAndQBase + offsetof (E3_TrapAndQueue, NonSysCntxDmaQueue[0]));
++    write_reg32 (dev, DProc_SysCntx_FPtr,    dev->TAndQBase + offsetof (E3_TrapAndQueue, SysCntxDmaQueue[0x10]));
++    write_reg32 (dev, DProc_SysCntx_BPtr,    dev->TAndQBase + offsetof (E3_TrapAndQueue, SysCntxDmaQueue[0x10]));
++    
++    dev->Event_Int_Queue_FPtr = dev->TAndQBase + offsetof (E3_TrapAndQueue, EventIntQueue[0x80]);
++    write_reg32 (dev, Event_Int_Queue_FPtr, dev->Event_Int_Queue_FPtr);
++    write_reg32 (dev, Event_Int_Queue_BPtr, dev->TAndQBase + offsetof (E3_TrapAndQueue, EventIntQueue[0x80]));
++    
++    
++    /* Initialise Input_Trap_Base to last 8 Kbytes of trap area, uCode adds the right offset */
++    write_reg32 (dev, Input_Trap_Base, dev->TAndQBase + offsetof (E3_TrapAndQueue, SysCntxThreadQueue[0]));
++    
++    /* Ptr to word used to save the SP to when a thread deschedules */
++    write_reg32 (dev, Thread_SP_Save_Ptr, dev->TAndQBase + offsetof (E3_TrapAndQueue, Thread_SP_Save));
++    
++    /* Initialise the command trap base */
++    write_reg32 (dev, CProc_TrapSave_Addr, dev->CommandPortTraps[0]);
++    
++    /* Initialise the set event tracing registers */
++    write_reg32 (dev, Event_Trace_Ptr, 0);
++    write_reg32 (dev, Event_Trace_Mask, 0);
++    
++    /* Initialise Tlb_Line_Value to zero. The TLB cannot be read while either the */
++    /* uCode or thread proc might be running. Must be set to 0. */
++    write_reg64 (dev, Tlb_Line_Value, 0);
++
++    /* Control register. Cache everything, Enable MMU, RefreshRate=3, CasLatency=1, StartSDR */
++    dev->Cache_Control_Reg |= CONT_MMU_ENABLE | CONT_EN_ALL_SETS | CONT_CACHE_ALL | CONT_ENABLE_ECC;
++
++#if ELAN3_PAGE_SHIFT == 13
++    dev->Cache_Control_Reg |= CONT_ENABLE_8K_PAGES;
++#endif
++
++    write_reg32 (dev, Cache_Control_Reg.ContReg,  dev->Cache_Control_Reg);
++
++    /*
++     * Initialise the context table to be discard for all contexts
++     */
++    ContextControlBlock.rootPTP  = 0;
++    ContextControlBlock.filter   = E3_CCB_DISCARD_ALL;
++    ContextControlBlock.VPT_mask = 0;
++    ContextControlBlock.VPT_ptr  = 0;
++
++    for (i = 0, ptr = dev->ContextTable; i < ELAN3_NUM_CONTEXTS; i++, ptr += sizeof (E3_ContextControlBlock))
++      elan3_sdram_copyl_to_sdram (dev, &ContextControlBlock, ptr, sizeof (E3_ContextControlBlock));
++
++    /* From elan3/code/trap_handler/init.c */
++    /*
++     * Initialise the Trap And Queue area in Elan SDRAM.
++     */
++    TrapCleanup[0].s.TrTypeCntx.TypeContext = 0;
++    TrapCleanup[0].s.TrAddr               = 0;
++    TrapCleanup[0].s.IProcTrapStatus.Status = CRC_STATUS_GOOD;
++    TrapCleanup[0].s.TrData0              = 0;
++    TrapCleanup[1].s.TrTypeCntx.TypeContext = 0;
++    TrapCleanup[1].s.TrAddr               = 0;
++    TrapCleanup[1].s.IProcTrapStatus.Status = CRC_STATUS_GOOD;
++    TrapCleanup[1].s.TrData0              = 0;
++    TrapCleanup[2].s.TrTypeCntx.TypeContext = 0;
++    TrapCleanup[2].s.TrAddr               = 0;
++    TrapCleanup[2].s.IProcTrapStatus.Status = CRC_STATUS_GOOD;
++    TrapCleanup[2].s.TrData0              = 0;
++    TrapCleanup[3].s.TrTypeCntx.TypeContext = 0;
++    TrapCleanup[3].s.TrAddr               = 0;
++    TrapCleanup[3].s.IProcTrapStatus.Status = CRC_STATUS_GOOD;
++    TrapCleanup[3].s.TrData0              = 0;
++
++    elan3_sdram_writel (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, IProcSysCntx.s.FaultContext),  0);
++    elan3_sdram_writel (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, IProcSysCntx.s.FSR.Status), 0);
++    elan3_sdram_writel (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, IProcNonSysCntx.s.FaultContext), 0);
++    elan3_sdram_writel (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, IProcNonSysCntx.s.FSR.Status), 0);
++    
++    /* Must now zero all the FSRs so that a subsequent Fault can be seen */ 
++    elan3_sdram_zeroq_sdram (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, CProc), 16);
++
++    elan3_sdram_zeroq_sdram (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProc), 16);
++    elan3_sdram_zeroq_sdram (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProcData0), 64);
++    
++    elan3_sdram_zeroq_sdram (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, TProc), 16);
++    elan3_sdram_zeroq_sdram (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, ThreadProcData), 16);
++    elan3_sdram_zeroq_sdram (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, ThreadProcInst), 16);
++    elan3_sdram_zeroq_sdram (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, ThreadProcOpen), 16);
++
++    elan3_sdram_copyq_to_sdram (dev, TrapCleanup, dev->TAndQBase + offsetof (E3_TrapAndQueue, VCh0_C0_TrHead[0]), 64);
++    elan3_sdram_copyq_to_sdram (dev, TrapCleanup, dev->TAndQBase + offsetof (E3_TrapAndQueue, VCh1_C0_TrHead[0]), 64);
++
++    elan3_sdram_copyq_to_sdram (dev, TrapCleanup, dev->TAndQBase + offsetof (E3_TrapAndQueue, VCh0_NonC0_TrHead[0]), 64);
++    elan3_sdram_copyq_to_sdram (dev, TrapCleanup, dev->TAndQBase + offsetof (E3_TrapAndQueue, VCh1_NonC0_TrHead[0]), 64);
++
++    InitialiseDmaBuffers(dev, CmdPort);
++
++    /* reserve a halt operation for flushing the context filter */
++    ReserveHaltOperations (dev, 1, TRUE);
++
++    /* Allow the Thread/Dma to run */
++    CLEAR_SCHED_STATUS (dev, HaltThread | HaltDmas);
++
++    /* Enable All Interrrupts */
++    SET_INT_MASK (dev, (INT_PciMemErr | INT_SDRamInt | INT_EventInterrupt | INT_LinkError | INT_ComQueue |
++                      INT_TProc | INT_CProc | INT_DProc | INT_IProcCh1NonSysCntx | 
++                      INT_IProcCh1SysCntx | INT_IProcCh0NonSysCntx | INT_IProcCh0SysCntx));
++
++    /* Take the link out of boundary scan */
++    SET_SCHED_LINK_VALUE (dev, 0, 0);
++    
++    /* And clear any link errors */
++    PULSE_SCHED_STATUS (dev, ClearLinkErrorInt);
++
++    /* XXXX: clear discard context 0,  AFTER setting up the kernel comms */
++    CLEAR_SCHED_STATUS (dev, DiscardSysCntxIn | DiscardNonSysCntxIn);
++
++    /* Start a thread to handle excessive Event Interrrupts */
++    if (kernel_thread_create (elan3_event_interrupt, (caddr_t) dev) == NULL)
++    {
++      panic ("InitialiseElan: cannot start elan3_event_interrupt\n");
++      return (EFAIL);
++    }
++    dev->EventInterruptThreadStarted = 1;
++
++    ReserveHaltOperations (dev, 1, TRUE);
++
++    PollForDmaHungup (dev);
++
++    /* register the device and stats with elanmod for RMS */
++    dev->DeviceIdx = elan_dev_register(&dev->Devinfo, &elan3_dev_ops, (void *) dev);
++    
++    elan3_register_dev_stats(dev);
++
++    return (ESUCCESS);
++}
++
++static void
++InitialiseDmaBuffers(ELAN3_DEV *dev, ioaddr_t CmdPort)
++{
++   register int i;
++
++   /* GNAT sw-elan3/3908:
++    * Clear down the power on state of the Dma_Desc registers to make sure we don't
++    * try and interpret them when a trap happens.
++    */
++   write_reg32 (dev, Dma_Desc.dma_type,            0);
++   write_reg32 (dev, Dma_Desc.dma_size,            0);
++   write_reg32 (dev, Dma_Desc.dma_source,          0);
++   write_reg32 (dev, Dma_Desc.dma_dest,            0);
++   write_reg32 (dev, Dma_Desc.dma_destEvent,       0);
++   write_reg32 (dev, Dma_Desc.dma_destCookieVProc, 0);
++   write_reg32 (dev, Dma_Desc.dma_srcEvent,        0);
++   write_reg32 (dev, Dma_Desc.dma_srcCookieVProc,  0);
++   
++   /*
++    * The following is a sequence of writes to remove X's from the dma buffers and 
++    * registers. It is only safe to write these registers after reset and before any
++    * dma's have been issued. The chip will NOT function corectly if they are written at
++    * any other time or in a different order.
++    */
++   write_reg64 (dev, Exts.Dmas.DmaWrs.LdAlignment, 0);
++   write_reg64 (dev, Exts.Dmas.DmaWrs.LdDmaType, 0);
++   write_reg64 (dev, Exts.Dmas.DmaWrs.ResetAckNLdBytesToWr, ((u_longlong_t)0x1000) << 32);
++   write_reg64 (dev, Exts.Dmas.DmaWrs.LdBytesToRd, ((u_longlong_t)0x100) << 32);
++
++   for (i=0;i<(4*8);i++)
++       write_reg64 (dev, Dma_Alignment_Port[0], 0);
++
++   /*
++    * This is used to clear out X's from some of the trap registers. This is required to
++    * prevent the first traps from possibly writting X's into the SDram and upsetting the
++    * ECC value. It requires that the trap save area registers have been set up but does
++    * not require any translations to be ready.
++    */
++   writel (-1, CmdPort + offsetof (E3_CommandPort, SetEvent));
++   while ((read_reg32 (dev, Exts.InterruptReg) & INT_CProc) == 0)
++   {
++       mb();
++       DELAY (1);
++   }
++
++   write_reg32 (dev, CProc_TrapSave_Addr, dev->CommandPortTraps[dev->CurrentCommandPortTrap]);
++   
++   PULSE_SCHED_STATUS(dev, RestartCProc);
++}
++
++void
++FinaliseElan (ELAN3_DEV *dev)
++{
++    ELAN3_PTBL_GR *ptg;
++    ELAN3_HALTOP  *op;
++    ELAN3_HALTOP  *chain = NULL;
++    int           bank;
++    int                 indx;
++    int                 size;
++    unsigned long flags;
++    int           level;
++
++    elan_stats_deregister (dev->StatsIndex);
++    elan_dev_deregister(&dev->Devinfo);
++
++    /* Cancel the dma poller */
++    cancel_timer_fn (&dev->DmaPollTimeoutId);
++
++    /* release it's halt operation */
++    ReleaseHaltOperations (dev, 1);
++
++    /* stop all kernel threads */
++    dev->ThreadsShouldStop = 1;
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++    while (dev->EventInterruptThreadStarted && !dev->EventInterruptThreadStopped)
++    {
++      kcondvar_wakeupall (&dev->IntrWait, &dev->IntrLock);
++      kcondvar_wait (&dev->IntrWait, &dev->IntrLock, &flags);
++    }
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++
++    /* Set the interrupt mask to 0 and the schedule control register to run nothing */
++    SET_INT_MASK (dev, 0);
++    SET_SCHED_STATUS (dev, DiscardNonSysCntxIn | DiscardSysCntxIn | HaltThread | HaltDmas);
++
++    /* Cancel any link error timeout */
++    if (timer_fn_queued(&dev->ErrorTimeoutId))
++      cancel_timer_fn (&dev->ErrorTimeoutId);
++
++    /* Free of and page tables that have been allocated */
++    spin_lock (&dev->PtblGroupLock);
++    for(level=0; level<4; level++) 
++    {
++      while ((ptg = dev->Level[level].PtblGroupList) != NULL)
++      {
++          dev->Level[level].PtblGroupList = ptg->pg_next;
++
++          elan3_sdram_free (dev, ptg->pg_addr, PTBL_GROUP_SIZE);
++          FREE_PTBL_GR(ptg);
++      }
++    }
++ 
++    spin_unlock (&dev->PtblGroupLock);
++
++    /* Free of all halt operations */
++    spin_lock_irqsave (&dev->FreeHaltLock, flags);
++    while ((op = dev->FreeHaltOperations) != NULL)
++    {
++      dev->FreeHaltOperations = op->Next;
++
++      /* Keep a list of 'freed' ops for later KMEM_FREE call */
++      op->Next = chain;
++      chain = op;
++    }
++    spin_unlock_irqrestore (&dev->FreeHaltLock, flags);
++
++    /* Have now dropped the spinlock - can call KMEM_FREE */
++    while ((op = chain) != NULL)
++    {
++      chain = op->Next;
++
++      KMEM_FREE (op, sizeof (ELAN3_HALTOP));
++    }
++      
++    /* Free of the ctxt table */
++    KMEM_FREE (dev->CtxtTable,  dev->ContextTableSize * sizeof (ELAN3_CTXT *));
++
++    /* Free of the thread and dma atrap areas */
++    KMEM_FREE (dev->ThreadTrap, sizeof (THREAD_TRAP));
++    KMEM_FREE (dev->DmaTrap, sizeof (DMA_TRAP));
++
++    /* Free of the memsegs and pages */
++    for (bank = 0; bank < ELAN3_SDRAM_NUM_BANKS; bank++)
++    {
++      if (dev->SdramBanks[bank].Size)
++      {
++          UnmapDeviceRegister (dev, &dev->SdramBanks[bank].Handle);
++
++          KMEM_FREE (dev->SdramBanks[bank].PtblGroups, sizeof (ELAN3_PTBL_GR *) * (dev->SdramBanks[bank].Size / PTBL_GROUP_SIZE));
++
++          for (indx = 0, size = SDRAM_MIN_BLOCK_SIZE; size <= dev->SdramBanks[bank].Size; indx++, size <<= 1)
++              KMEM_FREE (dev->SdramBanks[bank].Bitmaps[indx], sizeof (bitmap_t)*BT_BITOUL(dev->SdramBanks[bank].Size/size));
++      }
++    }
++    elan3_sdram_fini (dev);
++}
++
++#define INIT_PATTERN(offset)  (0xBEEC000000000011ull | ((u_longlong_t)(offset)) << 16)
++#define FREE_PATTERN(offset)  (0xBEEC000000000022ull | ((u_longlong_t)(offset)) << 16)
++
++static int
++ProbeSdram (ELAN3_DEV *dev)
++{
++    int                         Instance;
++    u_int               Bank;
++    int                         MemSpaceSize;
++    int                         BankMaxSize;
++    int                         BankOffset;
++    int                         BankSize;
++    ioaddr_t            BankBase;
++    ioaddr_t            PageBase;
++    ioaddr_t            PageBase1;
++    ioaddr_t            PageBase2;
++    DeviceMappingHandle   BankHandle;
++    DeviceMappingHandle   PageHandle;
++    DeviceMappingHandle   PageHandle1;
++    DeviceMappingHandle   PageHandle2;
++    register int          i;
++    u_longlong_t        value;
++    extern int            sdram_bank_limit;
++
++    /* NOTE: The Cache control register is set to only enable cache set 0 */
++    /*       and has ECC disabled */
++    Instance = dev->Instance;
++    
++    /* Determine the size of the SDRAM from the BAR register */
++    if (DeviceRegisterSize (dev, ELAN3_BAR_SDRAM, &MemSpaceSize) != ESUCCESS)
++    {
++      printk ("elan%d: cannot determine SDRAM size\n", Instance);
++      return (EFAIL);
++    }
++
++    elan3_sdram_init (dev);
++
++    BankMaxSize = MemSpaceSize / ELAN3_SDRAM_NUM_BANKS;
++
++    for (Bank = 0; Bank < ELAN3_SDRAM_NUM_BANKS; Bank++)
++    {
++      BankOffset = Bank * BankMaxSize;
++      
++      PRINTF3 (DBG_DEVICE, DBG_CONFIG, "elan%d: Probing RAM Bank %d (max size %08x)\n", Instance, Bank, BankMaxSize);
++      
++      /* Probe the memory bank by mapping two pages that are the size of the cache apart */
++      /* this guarantees that when we store the second pattern we displace the first pattern */
++      /* from the cache, also store the second pattern again the size of the cache up again */
++      /* to ensure that the SDRAM wires don't stay floating at pattern1 */
++
++      if (MapDeviceRegister (dev, ELAN3_BAR_SDRAM, &BankBase, BankOffset, PAGESIZE, &BankHandle) != ESUCCESS)
++      {
++          printk ("elan%d: Cannot probe memory bank %d\n", Instance, Bank);
++          continue;
++      }
++      
++      if (MapDeviceRegister (dev, ELAN3_BAR_SDRAM, &PageBase1, BankOffset + ELAN3_MAX_CACHE_SIZE, PAGESIZE, &PageHandle1) != ESUCCESS)
++      {
++          printk ("elan%d: Cannot probe memory bank %d\n", Instance, Bank);
++          UnmapDeviceRegister (dev, &BankHandle);
++          continue;
++      }
++
++      if (MapDeviceRegister (dev, ELAN3_BAR_SDRAM, &PageBase2, BankOffset + 2*ELAN3_MAX_CACHE_SIZE, PAGESIZE, &PageHandle2) != ESUCCESS)
++      {
++          printk ("elan%d: Cannot probe memory bank %d\n", Instance, Bank);
++          UnmapDeviceRegister (dev, &BankHandle);
++          UnmapDeviceRegister (dev, &PageHandle1);
++          continue;
++      }
++
++#define PATTERN0      (0x5555555555555555L)
++#define PATTERN1      (0xAAAAAAAAAAAAAAAAL)
++      writeq (PATTERN0, (u_longlong_t *) BankBase);
++      writeq (PATTERN1, (u_longlong_t *) PageBase1);
++      writeq (PATTERN1, (u_longlong_t *) PageBase2);
++
++      mmiob();
++
++      value = readq ((u_longlong_t *) BankBase);
++
++      if (value != PATTERN0)
++      {
++          UnmapDeviceRegister (dev, &BankHandle);
++          UnmapDeviceRegister (dev, &PageHandle1);
++          UnmapDeviceRegister (dev, &PageHandle2);
++          continue;
++      }
++
++      writeq (PATTERN1, (u_longlong_t *) BankBase);
++      writeq (PATTERN0, (u_longlong_t *) PageBase1);
++      writeq (PATTERN0, (u_longlong_t *) PageBase2);
++
++      mmiob();
++      
++      value = readq ((u_longlong_t *) BankBase);
++      if (value != PATTERN1)
++      {
++          UnmapDeviceRegister (dev, &BankHandle);
++          UnmapDeviceRegister (dev, &PageHandle1);
++          UnmapDeviceRegister (dev, &PageHandle2);
++          continue;
++      }
++      UnmapDeviceRegister (dev, &PageHandle1);
++      UnmapDeviceRegister (dev, &PageHandle2);
++
++      /* Bank is present, so work out its size,  we store tha maximum size at the base */
++      /* and then store the address at each address  on every power of two address until */
++      /* we reach the minimum mappable size (PAGESIZE), we then read back the value at the */
++      /* base to determine the bank size */
++      writeq ((u_longlong_t) BankMaxSize, (u_longlong_t *) BankBase);
++
++      for (BankSize = (BankMaxSize>>1); BankSize > PAGESIZE; BankSize >>= 1)
++      {
++          if (MapDeviceRegister (dev, ELAN3_BAR_SDRAM, &PageBase, BankOffset + BankSize, PAGESIZE, &PageHandle) == ESUCCESS)
++          {
++              writeq (BankSize, (u_longlong_t *) PageBase);
++              UnmapDeviceRegister (dev, &PageHandle);
++          }
++      }
++      mmiob();
++
++      BankSize = (u_long) readq ((u_longlong_t *) BankBase);
++      
++      if (sdram_bank_limit == 0 || BankSize <= (sdram_bank_limit * 1024 * 1024))
++          printk ("elan%d: memory bank %d is %dK\n", Instance, Bank, BankSize / 1024);
++      else
++      {
++          BankSize = (sdram_bank_limit * 1024 * 1024);
++          printk ("elan%d: limit memory bank %d to %dK\n", Instance, Bank, BankSize / 1024);
++      }
++
++      UnmapDeviceRegister (dev, &BankHandle);
++      
++      /* Now map all of this bank into the kernel */
++      if (MapDeviceRegister (dev, ELAN3_BAR_SDRAM, &BankBase, BankOffset, BankSize, &BankHandle) != ESUCCESS)
++      {
++          printk ("elan%d: Cannot initialise memory bank %d\n", Instance, Bank);
++          continue;
++      }
++      
++      dev->SdramBanks[Bank].Size    = BankSize;
++      dev->SdramBanks[Bank].Mapping = BankBase;
++      dev->SdramBanks[Bank].Handle  = BankHandle;
++
++#ifndef CONFIG_MPSAS
++      /* Initialise it for ECC */
++      preemptable_start {
++          for (i = 0; i < BankSize; i += 8)
++          {
++              elan3_sdram_writeq (dev, (Bank << ELAN3_SDRAM_BANK_SHIFT) | i, INIT_PATTERN(BankOffset+i));
++
++              preemptable_check();
++          }
++      } preemptable_end;
++#endif
++    }
++    
++    return (ESUCCESS);
++}
++
++static void
++InitialiseSdram (ELAN3_DEV *dev)
++{
++    int indx, size, b;
++
++    for (b = 0; b < ELAN3_SDRAM_NUM_BANKS; b++)
++    {
++      ELAN3_SDRAM_BANK *bank = &dev->SdramBanks[b];
++
++      if (bank->Size == 0)
++          continue;
++
++      /* allocate a ptbl group pointer for each possible ptbl group in this bank */
++      KMEM_ZALLOC (bank->PtblGroups, ELAN3_PTBL_GR **, sizeof (ELAN3_PTBL_GR *) * bank->Size/PTBL_GROUP_SIZE, TRUE);
++          
++      /* allocate the buddy allocator bitmaps */
++      for (indx = 0, size = SDRAM_MIN_BLOCK_SIZE; size <= bank->Size; indx++, size <<= 1)
++          KMEM_ZALLOC (bank->Bitmaps[indx], bitmap_t *, sizeof (bitmap_t)*BT_BITOUL(bank->Size/size), TRUE);
++          
++      /* and add it to the sdram buddy allocator */
++      elan3_sdram_add (dev, (b << ELAN3_SDRAM_BANK_SHIFT), (b << ELAN3_SDRAM_BANK_SHIFT) + bank->Size);
++    }
++}
++
++#include <elan3/vpd.h>
++
++int
++ReadVitalProductData (ELAN3_DEV *dev, int *CasLatency)
++{
++    DeviceMappingHandle RomHandle;
++    unsigned char      *RomBase;
++    unsigned char      *PCIDataPtr;
++    unsigned char      *VPDPtr;
++    unsigned char      *lim;
++    int                       type;
++    int                       i, len, len2;
++    char              name[3] = "XX";
++    char              value[256];
++    int                       finished = 0;
++
++    
++    /* default valud for CAS latency is 3 */
++    (*CasLatency) = CAS_LATENCY_3;
++
++    if (MapDeviceRegister (dev, ELAN3_BAR_EBUS, (ioaddr_t *) &RomBase, ELAN3_EBUS_ROM_OFFSET, ELAN3_EBUS_ROM_SIZE, &RomHandle) != ESUCCESS)
++    {
++      printk ("elan%d: Cannot map ROM\n", dev->Instance);
++      return (EFAIL);
++    }
++    
++    /* Check the ROM signature */
++    if (RomBase[0] != 0x55 || RomBase[1] != 0xAA)
++    {
++      printk ("elan%d: Invalid ROM signature %02x %02x\n", dev->Instance, RomBase[0], RomBase[1]);
++      return (ESUCCESS);
++    }
++    
++    PCIDataPtr = RomBase + ((RomBase[0x19] << 8) | RomBase[0x18]);
++
++    /* check the pci data structure */
++    if (PCIDataPtr[0] != 'P' || PCIDataPtr[1] != 'C' || PCIDataPtr[2] != 'I' || PCIDataPtr[3] != 'R')
++    {
++      printk ("elan%d: Invalid PCI Data structure\n", dev->Instance);
++      return (ESUCCESS);
++    }
++    
++    /* Extract the VPD pointer */
++    VPDPtr = RomBase + ((PCIDataPtr[9] << 8) | PCIDataPtr[8]);
++
++    if (VPDPtr == RomBase)
++    {
++      printk ("elan%d: No Vital Product Data\n", dev->Instance);
++      return (ESUCCESS);
++    }
++
++    while (! finished)
++    {
++      type = *VPDPtr++;
++      
++      if (type & LARGE_RESOURCE_BIT)
++      {
++          len = *(VPDPtr++);
++          len += *(VPDPtr++) << 8;
++
++          switch (type & ~LARGE_RESOURCE_BIT)
++          {
++          case LARGE_RESOURCE_STRING:
++              printk ("elan%d: ", dev->Instance);
++              for (i = 0; i < len; i++)
++                  printk ("%c", *VPDPtr++);
++              printk ("\n");
++              break;
++              
++          case LARGE_RESOURCE_VENDOR_DEFINED:
++              VPDPtr += len;
++              break;
++              
++          case LARGE_RESOURCE_VITAL_PRODUCT_DATA:
++              for (lim = VPDPtr + len; VPDPtr < lim; )
++              {
++                  name[0] = *VPDPtr++;
++                  name[1] = *VPDPtr++;
++                  len2    = *VPDPtr++;
++
++                  for (i = 0; i < len2 && VPDPtr < lim; i++)
++                      value[i] = *VPDPtr++;
++                  value[i] = '\0';
++
++                  if (! strcmp (name, "SN"))
++                      printk ("elan%d: Serial Number - %s\n", dev->Instance, value);
++
++                  if (! strcmp (name, "Z0"))
++                      (*CasLatency) = (strcmp (value, "CAS_LATENCY_2") ? CAS_LATENCY_3 : CAS_LATENCY_2);
++              }
++              break;
++              
++          default:
++              printk ("elan%d: unknown large resource %x\n", dev->Instance, type);
++              finished = 1;
++              break;
++          }
++      }
++      else
++      {
++          len = type & 0x7;
++
++          switch (type >> 3)
++          {
++          case SMALL_RESOURCE_COMPATIBLE_DEVICE_ID:
++              VPDPtr += len;
++              break;
++
++          case SMALL_RESOURCE_VENDOR_DEFINED:
++              VPDPtr += len;
++              break;
++              
++          case SMALL_RESOURCE_END_TAG:
++              finished = 1;
++              break;
++              
++          default:
++              printk ("elan%d: unknown small resource %x\n", dev->Instance, type >> 3);
++              finished = 1;
++              break;
++          }
++      }
++    }
++    
++    UnmapDeviceRegister (dev, &RomHandle);
++    return (ESUCCESS);
++}
++
++void
++ElanSetPtblGr (ELAN3_DEV *dev, sdramaddr_t offset, ELAN3_PTBL_GR *ptg)
++{
++    int bank = offset >> ELAN3_SDRAM_BANK_SHIFT;
++    
++    dev->SdramBanks[bank].PtblGroups[(offset & (ELAN3_SDRAM_BANK_SIZE-1)) / PTBL_GROUP_SIZE] = ptg;
++}
++
++ELAN3_PTBL_GR *
++ElanGetPtblGr (ELAN3_DEV *dev, sdramaddr_t offset)
++{
++    int bank = offset >> ELAN3_SDRAM_BANK_SHIFT;
++    
++    return (dev->SdramBanks[bank].PtblGroups[(offset & (ELAN3_SDRAM_BANK_SIZE-1)) / PTBL_GROUP_SIZE]);
++}
++
++void
++ElanFlushTlb (ELAN3_DEV *dev)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->TlbLock, flags);
++    BumpStat (dev, TlbFlushes);
++
++    write_reg32 (dev, Cache_Control_Reg.ContReg, dev->Cache_Control_Reg | MMU_FLUSH);
++    mmiob();
++    spin_unlock_irqrestore (&dev->TlbLock, flags);
++
++    while (! (read_reg32 (dev, Cache_Control_Reg.ContReg) & MMU_FLUSHED))
++      mb();
++}
++
++void
++KillNegativeDma (ELAN3_DEV *dev, void *arg)
++{
++    DMA_TRAP     *trap    = dev->DmaTrap;
++    E3_Status_Reg status;
++    sdramaddr_t   FPtr, BPtr;
++    sdramaddr_t   Base, Top;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++
++    ASSERT (read_reg32 (dev, Exts.InterruptReg) & INT_DProcHalted);
++
++    /* Initialise the trap to deliver to the offending user process */
++    trap->Status.Status   = read_reg32 (dev, Exts.DProcStatus.Status);
++    trap->PacketInfo.Value = 0;
++    
++    bzero (&trap->FaultSave, sizeof (trap->FaultSave));
++    bzero (&trap->Data0, sizeof (trap->Data0));
++    bzero (&trap->Data1, sizeof (trap->Data1));
++    bzero (&trap->Data2, sizeof (trap->Data2));
++    bzero (&trap->Data3, sizeof (trap->Data3));
++
++    /* run down the kernel dma run queue and panic on a -ve length dma */
++    FPtr  = read_reg32 (dev, DProc_SysCntx_FPtr);
++    BPtr  = read_reg32 (dev, DProc_SysCntx_BPtr);
++    Base  = dev->TAndQBase + offsetof (E3_TrapAndQueue, SysCntxDmaQueue[0]);
++    Top   = dev->TAndQBase + offsetof (E3_TrapAndQueue, SysCntxDmaQueue[E3_SysCntxQueueSize-1]);
++    
++    while (FPtr != BPtr)
++    {
++      elan3_sdram_copyq_from_sdram (dev, FPtr, &trap->Desc, sizeof (E3_DMA_BE));
++      
++      if (trap->Desc.s.dma_size > E3_MAX_DMA_SIZE)
++          panic ("KillNegativeDma: -ve sized kernel dma\n");
++
++      FPtr = (FPtr == Top) ? Base : FPtr + sizeof (E3_DMA);
++    }
++
++    /* run down the user dma run queue and "remove" and -ve length dma's */
++    FPtr  = read_reg32 (dev, DProc_NonSysCntx_FPtr);
++    BPtr  = read_reg32 (dev, DProc_NonSysCntx_BPtr);
++    Base  = dev->TAndQBase + offsetof (E3_TrapAndQueue, NonSysCntxDmaQueue[0]);
++    Top   = dev->TAndQBase + offsetof (E3_TrapAndQueue, NonSysCntxDmaQueue[E3_NonSysCntxQueueSize-1]);
++    
++    while (FPtr != BPtr)
++    {
++      elan3_sdram_copyq_from_sdram (dev, FPtr, &trap->Desc, sizeof (E3_DMA_BE));
++      
++      if (trap->Desc.s.dma_size > E3_MAX_DMA_SIZE)
++      {
++          PRINTF3 (NULL, DBG_INTR, "KillNegativeDma: remove dma - context %d size %d SuspendAddr %x\n", 
++                   trap->Desc.s.dma_u.s.Context, trap->Desc.s.dma_size, trap->Status.s.SuspendAddr);
++
++          trap->Status.s.TrapType = trap->Status.s.SuspendAddr;
++          trap->Status.s.Context  = trap->Desc.s.dma_u.s.Context;
++
++          DeliverDProcTrap (dev, trap, 0);
++
++          /*
++           * Remove the DMA from the queue by replacing it with one with
++           * zero size and no events.
++           *
++           * NOTE: we must preserve the SYS_CONTEXT_BIT since the Elan uses this
++           * to mark the approriate run queue as empty.
++           */
++          trap->Desc.s.dma_type            = 0;
++          trap->Desc.s.dma_size            = 0;
++          trap->Desc.s.dma_source          = (E3_Addr) 0;
++          trap->Desc.s.dma_dest            = (E3_Addr) 0;
++          trap->Desc.s.dma_destCookieVProc = (E3_Addr) 0;
++          trap->Desc.s.dma_srcEvent        = (E3_Addr) 0;
++          trap->Desc.s.dma_srcCookieVProc  = (E3_Addr) 0;
++
++          elan3_sdram_copyq_to_sdram (dev, &trap->Desc, FPtr, sizeof (E3_DMA_BE));
++      }
++
++      FPtr = (FPtr == Top) ? Base : FPtr + sizeof (E3_DMA);
++    }
++
++    status.Status = read_reg32 (dev, Exts.DProcStatus.Status);
++
++    if (status.s.SuspendAddr == MI_DequeueNonSysCntxDma || 
++      status.s.SuspendAddr == MI_DequeueSysCntxDma ||
++      status.s.SuspendAddr == MI_DmaLoop)
++    {
++      PRINTF0 (NULL, DBG_INTR, "KillNegativeDma: unlock dma processor\n");
++      write_reg32 (dev, Exts.Dmas.DmaWrs.LdAlignment, 0);
++      write_reg32 (dev, Exts.Dmas.DmaWrs.LdDmaType,   0);
++      mmiob();
++      
++      DELAY (10);
++      
++      write_reg32 (dev, Exts.Dmas.DmaWrs.LdAlignment, 0);
++      write_reg32 (dev, Exts.Dmas.DmaWrs.LdDmaType,   0);
++      mmiob();
++    }
++
++    PRINTF0 (NULL, DBG_INTR, "KillNegativeDma: dma processor restarted\n");
++
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++
++    schedule_timer_fn (&dev->DmaPollTimeoutId, PollForDmaHungup, (void *) dev, 1);
++}
++
++void
++ForceTProcTrap (ELAN3_DEV *dev, void *arg)
++{
++    printk ("elan%d: forced tproc trap .....\n", dev->Instance);
++
++    schedule_timer_fn (&dev->DmaPollTimeoutId, PollForDmaHungup, (void *) dev, 1);
++}
++
++void
++PollForDmaHungup (void *arg)
++{
++    ELAN3_DEV     *dev   = (ELAN3_DEV *) arg;
++    unsigned long flags;
++    E3_Status_Reg status;
++    E3_uint32     insn1, insn3;
++    register int  i;
++
++    if (read_reg32 (dev, Dma_Desc.dma_size) > E3_MAX_DMA_SIZE)
++    {
++      status.Status = read_reg32 (dev, Exts.DProcStatus);
++
++      PRINTF2 (NULL, DBG_INTR, "PollForDmaHungup: size %x SuspendAddr %x\n", read_reg32 (dev, Dma_Desc.dma_size), status.s.SuspendAddr);
++
++      if (status.s.SuspendAddr == MI_DequeueNonSysCntxDma || 
++          status.s.SuspendAddr == MI_DequeueSysCntxDma ||
++          status.s.SuspendAddr == MI_DmaLoop)
++      {
++          printk ("elan%d: PollForDmaHungup: size %x context %d SuspendAddr %x\n", 
++                  dev->Instance, read_reg32 (dev, Dma_Desc.dma_size),
++                  status.s.Context, status.s.SuspendAddr);
++      
++          PRINTF2 (NULL, DBG_INTR, "PollForDmaHungup: dma_size %x status %x\n",
++                   read_reg32 (dev, Dma_Desc.dma_size), status.Status);
++          
++          spin_lock_irqsave (&dev->IntrLock, flags);
++          QueueHaltOperation (dev, 0, NULL, INT_DProcHalted, KillNegativeDma, NULL);
++          spin_unlock_irqrestore (&dev->IntrLock, flags);
++          
++          return;
++      }
++    }
++
++    status.Status = read_reg32 (dev, Exts.TProcStatus);
++    if (status.s.WakeupFunction == WakeupStopped)
++    {
++      E3_uint32 PC = read_reg32 (dev, ExecutePC);
++
++      /* See if it's likely that the thread is really "stuck" on a waitevent/break 
++       * instruction ......... */
++      for (i = 0; i < 10; i++)
++      {
++          status.Status = read_reg32 (dev, Exts.TProcStatus);
++          insn1         = read_reg32 (dev, IBufferReg[1]);
++          insn3         = read_reg32 (dev, IBufferReg[3]);
++          
++          if (! (status.s.WakeupFunction == WakeupStopped && read_reg32 (dev, ExecutePC) == PC &&     /* stopping and it could be a break/waitevent */
++                 (insn1 == 0x81a00000 || insn3 == 0x81a00000 ||                                       /* break instruction */
++                  insn1 == 0x81b00000 || insn3 == 0x81b00000)))                                       /* waitevent instruction  */
++              break;
++      }
++
++      if (i == 10)
++      {
++          printk ("elan%d: forcing tproc trap from %s instruction at pc %x\n", dev->Instance, 
++                  (insn1 == 0x81a00000 || insn3 == 0x81a00000) ? "break" : "waitevent", PC);
++
++          spin_lock_irqsave (&dev->IntrLock, flags);
++          QueueHaltOperation (dev, 0, NULL, INT_TProcHalted, ForceTProcTrap, NULL);
++          spin_unlock_irqrestore (&dev->IntrLock, flags);
++          return;
++      }
++    }
++
++    schedule_timer_fn (&dev->DmaPollTimeoutId, PollForDmaHungup, (void *) dev, 10);
++}
++
++/*=======================================================================================*/
++/*
++ * Interrupt handler.
++ */
++static void
++ReEnableErrorInterrupts (void *arg)
++{
++    ELAN3_DEV     *dev = (ELAN3_DEV *) arg;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++
++    if ((dev->SchCntReg & LinkBoundaryScan) == 0)
++      ENABLE_INT_MASK (dev, INT_ErrorInterrupts);
++
++    PRINTF1 (DBG_DEVICE, DBG_INTR, "ReEnableErrorInterrupts: IntMask=%x\n", read_reg32 (dev, Exts.InterruptMask));
++
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++}
++
++void
++CheckForExcessiveErrorRate (ELAN3_DEV *dev)
++{
++    if (dev->ErrorTime == (lbolt/hz))
++    {
++      if (dev->ErrorsPerTick++ > 100)
++      {
++          PRINTF0 (DBG_DEVICE, DBG_INTR, "CheckForExcessiveErrorRate: too many links errors, disabling interrupt\n");
++
++          DISABLE_INT_MASK (dev, INT_ErrorInterrupts);
++
++          schedule_timer_fn (&dev->ErrorTimeoutId, ReEnableErrorInterrupts, (void *) dev, hz);
++      }
++    }
++    else
++    {
++      dev->ErrorTime     = (lbolt/hz);
++      dev->ErrorsPerTick = 0;
++    }
++}
++/*=======================================================================================*/
++/*
++ * Interrupt handler.
++ */
++static void
++HandlePciMemErr (ELAN3_DEV *dev)
++{
++    PRINTF0 (DBG_DEVICE, DBG_INTR, "HandlePciMemErr : masking out interrupt\n");
++    
++    ElanBusError (dev);
++    panic ("elan pci memory error\n");
++}
++
++static void
++HandleSDRamInterrupt (ELAN3_DEV *dev)
++{
++    E3_uint32     EccStatus0 = read_reg32 (dev, ECC_STATUS0);
++    E3_uint32     EccStatus1 = read_reg32 (dev, ECC_STATUS1);
++    unsigned long flags;
++
++    PRINTF5 (DBG_DEVICE, DBG_INTR, "elan: ECC error - Addr=%x UE=%x CE=%x ME=%x Syn=%x\n",
++           EccStatus0 & ECC_ADDR_MASK, EccStatus0 & ECC_UE_MASK, 
++           EccStatus0 & ECC_CE_MASK, EccStatus0 & ECC_ME_MASK, 
++           EccStatus1 & ECC_SYN_MASK);
++
++    if (EccStatus0 & (ECC_UE_MASK|ECC_CE_MASK))
++    {
++      printk ("elan%d: ECC memory error (Address=%08x Syndrome=%02x %s%s%s)\n",
++              dev->Instance, 
++              (EccStatus0 & ECC_ADDR_MASK), (EccStatus1 & ECC_SYN_MASK), 
++              (EccStatus0 & ECC_UE_MASK) ? "Uncorrectable "   : "",
++              (EccStatus0 & ECC_CE_MASK) ? "Correctable "     : "",
++              (EccStatus0 & ECC_ME_MASK) ? "Multiple Errors " : "");
++    }
++
++    if (EccStatus0 & ECC_UE_MASK)
++      panic ("elan: Uncorrectable ECC memory error");
++    if (EccStatus0 & ECC_CE_MASK)
++      BumpStat (dev, CorrectableErrors);
++    if (EccStatus0 & ECC_ME_MASK)
++      BumpStat (dev, MultipleErrors);
++
++    /*
++     * Clear the interrupt and reset the error flags.
++     * Note. Might loose an UE or CE if it occurs between reading the status and
++     *       clearing the interrupt. I don't think this matters very much as the
++     *          status reg will only be used to identify a bad simm.
++     */
++
++    spin_lock_irqsave (&dev->TlbLock, flags);
++    write_reg32 (dev, Cache_Control_Reg.ContReg, dev->Cache_Control_Reg | CLEAR_SDRAM_ERROR);
++    mmiob();
++    spin_unlock_irqrestore (&dev->TlbLock, flags);
++
++    CheckForExcessiveErrorRate (dev);
++}
++
++static int
++HandleEventInterrupt (ELAN3_DEV *dev, int nticks, unsigned long *flags)
++{
++    E3_uint32 Fptr  = dev->Event_Int_Queue_FPtr;
++    E3_uint32 Bptr  = read_reg32 (dev, Event_Int_Queue_BPtr);                                                 /* PCI read */
++    long      tlim  = lbolt + nticks;
++    long      count = 0;
++    ELAN3_CTXT *ctxt;
++
++    ASSERT (SPINLOCK_HELD (&dev->IntrLock));
++    ASSERT ((dev->InterruptMask & INT_EventInterrupt) == 0);
++          
++    while (Fptr != Bptr)
++    {
++      while (Fptr != Bptr)
++      {
++          E3_EventInt_BE  EvInt;
++          E3_uint32       Context;
++
++          /* If we're running in the interrupt handler and have seen a high
++           * rate of event interrupts then punt to the thread  - however on 
++           * Linux the elan interrupt handler can block the timer interrupt,
++           * and so lbolt (jiffies) is not incremented, hence we punt after
++           a number of loops instead */
++#if defined(LINUX)
++          if (in_interrupt() && ++count > eventint_punt_loops)
++              return (EAGAIN);
++#endif
++
++          if (nticks && ((int) (lbolt - tlim)) > 0)
++          {
++              PRINTF2 (DBG_DEVICE, DBG_INTR, "HandleEventInterrupt: Fptr %x Bptr %x punting to thread\n", Fptr, Bptr);
++              return (EAGAIN);
++          }
++
++          elan3_sdram_copyq_from_sdram (dev, Fptr, (void *) &EvInt, 8);                               /* PCI read */
++          
++          /* The context number is held in the top 16 bits of the EventContext */
++          Context = (EvInt.s.EventContext >> 16) & MAX_ROOT_CONTEXT_MASK;
++          
++          PRINTF2 (DBG_DEVICE, DBG_INTR, "HandleEventInterrupt: Context %d : Cookie %x\n", Context, EvInt.s.IntCookie);
++          
++          ctxt = ELAN3_DEV_CTX_TABLE(dev, Context);
++
++          /* Work out new fptr, and store it in the device, since we'll be dropping the IntrLock */
++          Fptr = E3_EVENT_INTQ_NEXT(Fptr);
++          dev->Event_Int_Queue_FPtr = Fptr;
++
++          if (ctxt == NULL)
++          {
++              PRINTF3 (DBG_DEVICE, DBG_INTR, "HandleEventInterrupt: Fptr %x Bptr %x context %d invalid\n",
++                       Fptr, Bptr, Context);
++              BumpStat (dev, InvalidContext);
++          }
++          else
++          {
++              BumpStat (dev, EventInterrupts);
++              
++              spin_unlock_irqrestore (&dev->IntrLock, *flags);
++              QueueEventInterrupt (ctxt, EvInt.s.IntCookie);
++              spin_lock_irqsave (&dev->IntrLock, *flags);
++          }
++          
++          /* Re-read the FPtr, since we've dropped the IntrLock */
++          Fptr = dev->Event_Int_Queue_FPtr;
++          
++          /* Store the new FPtr to the elan, this also clears the interrupt. */
++          write_reg32 (dev, Event_Int_Queue_FPtr, Fptr);                                      /* PCI write */
++
++          mmiob();
++      }
++
++      mb();
++      Bptr = read_reg32 (dev, Event_Int_Queue_BPtr);                                          /* PCI read */
++    }
++
++    return (ESUCCESS);
++}
++
++int
++SetLinkBoundaryScan (ELAN3_DEV *dev)
++{
++    int           res = ESUCCESS;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++    if ((dev->SchCntReg & LinkBoundaryScan) != 0)
++      res = EAGAIN;
++    else
++    {
++      PRINTF0 (DBG_DEVICE, DBG_BSCAN, "SetLinkBoundaryScan: setting link into boundary scan mode\n");
++
++      /*
++       * We're going to set the link into boundary scan mode,  so firstly
++       * set the inputters to discard everything.
++       */
++      if (dev->DiscardAllCount++ == 0)
++          SetSchedStatusRegister (dev, read_reg32 (dev, Exts.InterruptReg), NULL);
++
++      /*
++       * Now disable the error interrupts
++       */
++      DISABLE_INT_MASK (dev, INT_ErrorInterrupts);
++      
++      /*
++       * And set the link into boundary scan mode, and drive
++       * a reset token onto the link.
++       */
++      SET_SCHED_LINK_VALUE (dev, 1, LinkResetToken);
++    }
++
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++
++    return (res);
++}
++
++void
++ClearLinkBoundaryScan (ELAN3_DEV *dev)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++    if ((dev->SchCntReg & LinkBoundaryScan) != 0)
++    {
++      PRINTF0 (DBG_DEVICE, DBG_BSCAN, "ClearLinkBoundaryScan: taking link out of boundary scan mode\n");
++
++      /*
++       * Take the link out of boundary scan 
++       */
++      SET_SCHED_LINK_VALUE (dev, 0, 0);
++
++      /*
++       * Clear any link errors.
++       */
++      PULSE_SCHED_STATUS (dev, ClearLinkErrorInt);
++
++      /*
++       * Re-enable the error interrupts.
++       */
++      if (! timer_fn_queued(&dev->ErrorTimeoutId))
++          ENABLE_INT_MASK (dev, INT_ErrorInterrupts);
++
++      /*
++       * And stop the inputter from discarding all packets.
++       */
++      if (--dev->DiscardAllCount == 0)
++          SetSchedStatusRegister (dev, 0, NULL);
++    }
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++}
++
++int
++WriteBoundaryScanValue (ELAN3_DEV *dev, int value)
++{
++    int           res = 0;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++    if ((dev->SchCntReg & LinkBoundaryScan) != 0)
++    {
++      PRINTF1 (DBG_DEVICE, DBG_BSCAN, "WriteBoundaryScanValue: driving value 0x%x onto link\n", value);
++      SET_SCHED_LINK_VALUE (dev, 1, value);
++
++      res = read_reg32 (dev, Exts.LinkState);
++
++      PRINTF1 (DBG_DEVICE, DBG_BSCAN, "WriteBoundaryScanValue: return 0x%x\n", res);
++    }
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++
++    return (res);
++}
++
++int
++ReadBoundaryScanValue(ELAN3_DEV *dev, int link)
++{
++    int           res;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++    if ((dev->SchCntReg & LinkBoundaryScan) == 0)
++    {
++      PRINTF1 (DBG_DEVICE, DBG_BSCAN, "ReadBoundaryScanValue: set linkval 0x%x\n",  link);
++      SET_SCHED_LINK_VALUE (dev, 0, link);
++    }
++    res = read_reg32 (dev, Exts.LinkState);
++    PRINTF1 (DBG_DEVICE, DBG_BSCAN, "ReadBoundaryScanValue: return 0x%x\n", res);
++
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++
++    return (res);
++}
++
++static int
++ReadLinkVal (ELAN3_DEV *dev, int link)
++{
++    if ((dev->SchCntReg & LinkBoundaryScan) == 0)
++      SET_SCHED_LINK_VALUE (dev, 0, link);
++    
++    return (read_reg32 (dev, Exts.LinkState));
++}
++
++static void
++HandleLinkError (ELAN3_DEV *dev)
++{
++    E3_uint32 value = read_reg32 (dev, Exts.LinkErrorTypes);
++
++    PRINTF1 (DBG_DEVICE, DBG_LINKERR, "HandleLinkError: LinkErrorTypes %08x - clearing\n", value);
++    
++    if (value & LS_LockError)   BumpStat (dev, LockError);
++    if (value & LS_DeskewError) BumpStat (dev, DeskewError);
++    if (value & LS_PhaseError)  BumpStat (dev, PhaseError);
++    if (value & LS_DataError)   BumpStat (dev, DataError);
++    if (value & LS_FifoOvFlow0) BumpStat (dev, FifoOvFlow0);
++    if (value & LS_FifoOvFlow1) BumpStat (dev, FifoOvFlow1);
++
++    if (value & LS_DataError)
++      dev->Stats.LinkErrorValue = ReadLinkVal (dev, 12) | (ReadLinkVal (dev, 13) << 9);
++
++    PULSE_SCHED_STATUS (dev, ClearLinkErrorInt);
++
++    CheckForExcessiveErrorRate (dev);
++}
++
++static void
++HandleErrorInterrupt (ELAN3_DEV *dev, E3_uint32 Pend)
++{
++    if (Pend & INT_PciMemErr)
++      HandlePciMemErr (dev);
++    
++    if (Pend & INT_SDRamInt)
++      HandleSDRamInterrupt (dev);
++    
++    if (Pend & INT_LinkError)
++      HandleLinkError (dev);
++}
++      
++static void
++HandleAnyIProcTraps (ELAN3_DEV *dev, E3_uint32 Pend)
++{
++    E3_uint32      RestartBits = 0;
++    
++    if (Pend & INT_IProcCh0SysCntx)
++    {
++      HandleIProcTrap (dev, 0, Pend,
++                       dev->TAndQBase + offsetof (E3_TrapAndQueue, IProcSysCntx),
++                       dev->TAndQBase + offsetof (E3_TrapAndQueue, VCh0_C0_TrHead[0]),
++                       dev->TAndQBase + offsetof (E3_TrapAndQueue, VCh0_C0_TrData[0]));
++                       
++      RestartBits |= RestartCh0SysCntx;
++    }
++    
++    if (Pend & INT_IProcCh1SysCntx)
++    {
++      HandleIProcTrap (dev, 1, Pend,
++                       dev->TAndQBase + offsetof (E3_TrapAndQueue, IProcSysCntx),
++                       dev->TAndQBase + offsetof (E3_TrapAndQueue, VCh1_C0_TrHead[0]),
++                       dev->TAndQBase + offsetof (E3_TrapAndQueue, VCh1_C0_TrData[0]));
++                       
++      RestartBits |= RestartCh1SysCntx;
++    }
++
++    if (Pend & INT_IProcCh0NonSysCntx)
++    {
++      HandleIProcTrap (dev, 0, Pend,
++                       dev->TAndQBase + offsetof (E3_TrapAndQueue, IProcNonSysCntx),
++                       dev->TAndQBase + offsetof (E3_TrapAndQueue, VCh0_NonC0_TrHead[0]),
++                       dev->TAndQBase + offsetof (E3_TrapAndQueue, VCh0_NonC0_TrData[0]));
++
++      RestartBits |= RestartCh0NonSysCntx;
++    }
++    
++
++    if (Pend & INT_IProcCh1NonSysCntx)
++    {
++      HandleIProcTrap (dev, 1, Pend,
++                       dev->TAndQBase + offsetof (E3_TrapAndQueue, IProcNonSysCntx),
++                       dev->TAndQBase + offsetof (E3_TrapAndQueue, VCh1_NonC0_TrHead[0]),
++                       dev->TAndQBase + offsetof (E3_TrapAndQueue, VCh1_NonC0_TrData[0]));
++      RestartBits |= RestartCh1NonSysCntx;
++    }
++
++    PULSE_SCHED_STATUS (dev, RestartBits);
++}
++
++static void
++elan3_event_interrupt (ELAN3_DEV *dev)
++{
++    unsigned long flags;
++
++    kernel_thread_init("elan3_event_int");
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++    for (;;)
++    {
++      /* Make sure we never sleep with the EventInterrupt disabled */
++      if (! (dev->InterruptMask & INT_EventInterrupt))
++      {
++          if (HandleEventInterrupt (dev, eventint_resched_ticks, &flags) != ESUCCESS)
++              BumpStat (dev, EventRescheds);
++          
++          ENABLE_INT_MASK (dev, INT_EventInterrupt);
++      }
++
++      if (dev->ThreadsShouldStop)
++          break;
++
++      kcondvar_wait (&dev->IntrWait, &dev->IntrLock, &flags);
++    }
++    
++    dev->EventInterruptThreadStopped = 1;
++    kcondvar_wakeupall (&dev->IntrWait, &dev->IntrLock);
++
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++
++    kernel_thread_exit ();
++}
++
++int
++InterruptHandler (ELAN3_DEV *dev)
++{
++    E3_uint32     Mask;
++    E3_uint32     Pend;
++    E3_uint32     RestartBits;
++    int           deliverDProcTrap;
++    int                 deliverTProcTrap;
++    static long   lboltsave;
++    int           loop_count = 0; 
++    unsigned long flags;
++    int  tproc_delivered;
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++
++    BumpStat (dev, Interrupts);
++
++    Mask = dev->InterruptMask;
++    Pend = read_reg32 (dev, Exts.InterruptReg);                                               /* PCI read */
++
++    /* Save the lbolt so we know how long in do loop or in event handling */
++    lboltsave = lbolt;
++
++    if ((Pend & Mask) == INT_EventInterrupt)
++    {
++      DISABLE_INT_MASK (dev, INT_EventInterrupt);
++
++      if (HandleEventInterrupt (dev, eventint_punt_ticks, &flags) == ESUCCESS)
++          ENABLE_INT_MASK (dev, INT_EventInterrupt);
++      else
++      {
++          BumpStat (dev, EventPunts);
++
++          kcondvar_wakeupone (&dev->IntrWait, &dev->IntrLock);
++      }
++
++        if ((lbolt - lboltsave) > dev->Stats.LongestInterrupt)
++            dev->Stats.LongestInterrupt = (lbolt - lboltsave);
++      spin_unlock_irqrestore (&dev->IntrLock, flags);
++      return (ESUCCESS);
++    }
++
++    if ((Pend & Mask) == 0)
++    {
++      PRINTF3 (DBG_DEVICE, DBG_INTR, "InterruptHandler: Spurious Pend %x Mask %x SchedStatus %x\n", 
++               Pend, Mask, read_reg32 (dev, Exts.SchCntReg));
++
++        if ((lbolt - lboltsave) > dev->Stats.LongestInterrupt)
++            dev->Stats.LongestInterrupt = (lbolt - lboltsave);
++      spin_unlock_irqrestore (&dev->IntrLock, flags);
++      return (EFAIL);
++    }
++
++    PRINTF3 (DBG_DEVICE, DBG_INTR, "InterruptHandler: Pend %x Mask %08x SchedStatus %x\n", 
++           Pend, Mask, read_reg32 (dev, Exts.SchCntReg));
++
++    do {
++      loop_count++;
++      RestartBits = 0;
++
++      if (Pend & Mask & (INT_CProc | INT_ComQueue))
++          HandleCProcTrap (dev, Pend, &Mask);
++
++      tproc_delivered = 0;
++
++      if (Pend & Mask & INT_TProc) {
++          ELAN_REG_REC(Pend);
++          tproc_delivered = 1;
++          deliverTProcTrap = HandleTProcTrap (dev, &RestartBits);
++      }
++      else
++          deliverTProcTrap = 0;
++
++      if (Pend & Mask & INT_DProc)
++          deliverDProcTrap = HandleDProcTrap (dev, &RestartBits);
++      else
++          deliverDProcTrap = 0;
++
++      ASSERT ((RestartBits & RestartDProc) == 0 || (read_reg32 (dev, Exts.DProcStatus.Status) >> 29) == 4);
++      ASSERT ((RestartBits & RestartDProc) == 0 || elan3_sdram_readl (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProc.s.FSR.Status))      == 0);
++      ASSERT ((RestartBits & RestartDProc) == 0 || elan3_sdram_readl (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProcData0.s.FSR.Status)) == 0);
++      ASSERT ((RestartBits & RestartDProc) == 0 || elan3_sdram_readl (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProcData1.s.FSR.Status)) == 0);
++      ASSERT ((RestartBits & RestartDProc) == 0 || elan3_sdram_readl (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProcData2.s.FSR.Status)) == 0);
++      ASSERT ((RestartBits & RestartDProc) == 0 || elan3_sdram_readl (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProcData3.s.FSR.Status)) == 0);
++
++      PULSE_SCHED_STATUS (dev, RestartBits);          /* Restart any processors which had trapped. */
++      SET_INT_MASK (dev, Mask);                       /* And install the new interrupt mask */
++
++      if ((Pend & Mask & INT_TProc) && deliverTProcTrap)
++          DeliverTProcTrap (dev, dev->ThreadTrap, Pend);
++
++      if ((Pend & Mask & INT_DProc) && deliverDProcTrap)
++          DeliverDProcTrap (dev, dev->DmaTrap, Pend);
++
++      if (Pend & Mask & INT_Inputters)
++          HandleAnyIProcTraps (dev, Pend);
++      
++      if (Pend & Mask & INT_EventInterrupt)
++      {
++          DISABLE_INT_MASK (dev, INT_EventInterrupt);
++          
++          if (loop_count == 1 && HandleEventInterrupt (dev, eventint_punt_ticks, &flags) == ESUCCESS) /* always punt to the thread if we've */
++              ENABLE_INT_MASK (dev, INT_EventInterrupt);                                              /* been round the loop once */
++          else
++          {
++              BumpStat (dev, EventPunts);
++
++              kcondvar_wakeupone (&dev->IntrWait, &dev->IntrLock);
++          }
++      }
++
++      if (Pend & (INT_Halted | INT_Discarding))
++          ProcessHaltOperations (dev, Pend);
++
++      if (Pend & Mask & INT_ErrorInterrupts)
++          HandleErrorInterrupt (dev, Pend);
++
++      Mask = dev->InterruptMask;
++      Pend = read_reg32 (dev, Exts.InterruptReg);     /* PCI read */
++      
++      if (tproc_delivered)
++          ELAN_REG_REC(Pend);
++
++      PRINTF3 (DBG_DEVICE, DBG_INTR, "InterruptHandler: Pend %x Mask %08x SchedStatus %x\n", 
++               Pend, Mask, read_reg32 (dev, Exts.SchCntReg));
++    }  while ((Pend & Mask) != 0);
++
++    if ((lbolt - lboltsave) > dev->Stats.LongestInterrupt)
++        dev->Stats.LongestInterrupt = (lbolt - lboltsave);
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++
++    PRINTF2 (DBG_DEVICE, DBG_INTR, "InterruptHandler: lbolt is %lx; start lbolt is %lx\n", 
++           lbolt, lboltsave);
++
++    return (ESUCCESS);
++}
++
++void
++SetSchedStatusRegister (ELAN3_DEV *dev, E3_uint32 Pend, volatile E3_uint32 *Maskp)
++{
++    E3_uint32 HaltMask  = dev->HaltOperationsMask;
++    E3_uint32 Mask      = Maskp ? *Maskp : dev->InterruptMask;
++    E3_uint32 ClearBits = 0;
++    E3_uint32 SetBits   = 0;
++
++    PRINTF5 (DBG_DEVICE, DBG_INTR, "SetSchedStatusRegister: HaltOperationsMask=%x HaltAll=%d HaltDmaDequeue=%d HaltThread=%d DiscardAll=%d\n",
++           HaltMask, dev->HaltAllCount, dev->HaltDmaDequeueCount, dev->HaltThreadCount, dev->DiscardAllCount);
++
++    if (dev->FlushCommandCount)
++      SetBits |= FlushCommandQueues;
++    
++    if ((HaltMask & INT_DProcHalted) || dev->HaltAllCount)
++    {
++      SetBits |= HaltDmas | HaltDmaDequeue;
++      if (Pend & INT_DProcHalted)
++          Mask &= ~INT_DProcHalted;
++      else
++          Mask |= INT_DProcHalted;
++    }
++
++    if (dev->HaltDmaDequeueCount)
++    {
++      SetBits |= HaltDmaDequeue;
++      if (Pend & INT_DProcHalted)
++          Mask &= ~INT_DProcHalted;
++      else
++          Mask |= INT_DProcHalted;
++    }
++
++    if ((HaltMask & INT_TProcHalted) || dev->HaltAllCount || dev->HaltThreadCount)
++    {
++      SetBits |= HaltThread;
++      if (Pend & INT_TProcHalted)
++          Mask &= ~INT_TProcHalted;
++      else
++          Mask |= INT_TProcHalted;
++    }
++
++    if ((HaltMask & INT_DiscardingSysCntx) || dev->DiscardAllCount)
++    {
++      SetBits |= DiscardSysCntxIn;
++      if (Pend & INT_DiscardingSysCntx)
++          Mask &= ~INT_DiscardingSysCntx;
++      else
++          Mask |= INT_DiscardingSysCntx;
++    }
++
++    if ((HaltMask & INT_DiscardingNonSysCntx) || dev->DiscardNonContext0Count || dev->DiscardAllCount)
++    {
++      SetBits |= DiscardNonSysCntxIn;
++      if (Pend & INT_DiscardingNonSysCntx)
++          Mask &= ~INT_DiscardingNonSysCntx;
++      else
++          Mask |= INT_DiscardingNonSysCntx;
++    }
++
++    if (dev->HaltNonContext0Count)
++      SetBits |= StopNonSysCntxs;
++
++    ClearBits = SetBits ^ (FlushCommandQueues | HaltDmas | HaltDmaDequeue | HaltThread |
++                         DiscardSysCntxIn | DiscardNonSysCntxIn | StopNonSysCntxs);
++
++    PRINTF4 (DBG_DEVICE, DBG_INTR, "SetSchedStatusRegister: SetBits=%x InterruptMask=%x InterruptReg=%x Mask=%x\n",
++           SetBits, dev->InterruptMask, read_reg32 (dev, Exts.InterruptReg), Mask);
++
++    MODIFY_SCHED_STATUS (dev, SetBits, ClearBits);
++
++    if (Maskp)
++      *Maskp = Mask;                                          /* copyback new interrupt mask */
++    else
++      SET_INT_MASK(dev, Mask);
++}
++
++void
++FreeHaltOperation (ELAN3_DEV *dev, ELAN3_HALTOP *op)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->FreeHaltLock, flags);
++    op->Next = dev->FreeHaltOperations;
++    dev->FreeHaltOperations = op;
++    spin_unlock_irqrestore (&dev->FreeHaltLock, flags);
++}
++
++int
++ReserveHaltOperations (ELAN3_DEV *dev, int count, int cansleep)
++{
++    ELAN3_HALTOP   *op;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->FreeHaltLock, flags);
++    while ((dev->NumHaltOperations - dev->ReservedHaltOperations) < count)
++    {
++      spin_unlock_irqrestore (&dev->FreeHaltLock, flags);
++
++      KMEM_ZALLOC (op, ELAN3_HALTOP *, sizeof (ELAN3_HALTOP), cansleep);
++
++      if (op == NULL)
++          return (FALSE);
++
++      spin_lock_irqsave (&dev->FreeHaltLock, flags);
++
++      dev->NumHaltOperations++;
++
++      op->Next = dev->FreeHaltOperations;
++      dev->FreeHaltOperations = op;
++    }
++                  
++    dev->ReservedHaltOperations += count;
++    
++    spin_unlock_irqrestore (&dev->FreeHaltLock, flags);
++
++    return (TRUE);
++}
++
++void
++ReleaseHaltOperations (ELAN3_DEV *dev, int count)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->FreeHaltLock, flags);
++    dev->ReservedHaltOperations -= count;
++    spin_unlock_irqrestore (&dev->FreeHaltLock, flags);
++}
++
++void
++QueueHaltOperation (ELAN3_DEV *dev, E3_uint32 Pend, volatile E3_uint32 *Maskp, 
++                  E3_uint32 ReqMask, void (*Function)(ELAN3_DEV *, void *), void *Arguement)
++{
++    ELAN3_HALTOP *op;
++
++    ASSERT (SPINLOCK_HELD (&dev->IntrLock));
++    
++    spin_lock (&dev->FreeHaltLock);
++    op = dev->FreeHaltOperations;
++
++    ASSERT (op != NULL);
++
++    dev->FreeHaltOperations = op->Next;
++    spin_unlock (&dev->FreeHaltLock);
++
++    op->Mask      = ReqMask;
++    op->Function  = (void (*)(void *, void *))Function;
++    op->Arguement = Arguement;
++
++    dev->HaltOperationsMask |= ReqMask;                               /* Add our bits to the global bits needed. */
++    SetSchedStatusRegister (dev, Pend, Maskp);                        /* Set the control register and the interrupt mask */
++
++    /*
++     * If the condition is already satisfied, then SetSchedStatusRegister will
++     * have masked out the interrupt, so re-enable it now to take it straight
++     * away
++     */
++    if (Maskp == NULL)
++    {
++      if ((read_reg32 (dev, Exts.InterruptReg) & ReqMask) == ReqMask)
++          ENABLE_INT_MASK (dev, ReqMask);
++    }
++    else
++    {
++      if ((Pend & ReqMask) == ReqMask)
++          *Maskp |= ReqMask;
++    }
++
++    *dev->HaltOperationsTailpp = op;                          /* Queue at end of list, since ProcessHaltOperations */
++    dev->HaltOperationsTailpp = &op->Next;                    /* drops the IntrLock while running down the list */
++    op->Next = NULL;
++}
++                  
++void
++ProcessHaltOperations (ELAN3_DEV *dev, E3_uint32 Pend)
++{
++    E3_uint32     Mask;
++    ELAN3_HALTOP  *op;
++    ELAN3_HALTOP **prevp;
++    E3_uint32     haltMask;
++    ELAN3_HALTOP  *next;
++
++    PRINTF1 (DBG_DEVICE, DBG_INTR, "ProcessHaltOperations: Pend %x\n", Pend);
++
++    for (;;)
++    {
++      ELAN3_HALTOP  *head = NULL;
++      ELAN3_HALTOP **tailp = &head;
++
++      /*
++       * Generate a list of halt operations which can be called now.
++       */
++      for (haltMask = 0, prevp = &dev->HaltOperations; (op = *prevp) != NULL; )
++      {
++          if ((Pend & op->Mask) != op->Mask)
++          {
++              haltMask |= op->Mask;
++              prevp = &op->Next;
++          }
++          else
++          {
++              *prevp = op->Next;                              /* remove from list */
++              if (op->Next == NULL)
++                  dev->HaltOperationsTailpp = prevp;
++              
++              *tailp = op;                                    /* add to local list */
++              op->Next = NULL;
++              tailp = &op->Next;
++          }
++      }
++
++      if (head == NULL)                                       /* nothing to do, so update */
++      {                                                       /* the schedule status register */
++          dev->HaltOperationsMask = haltMask;                 /* and the interrupt mask */
++          SetSchedStatusRegister (dev, Pend, NULL);
++          return;
++      }
++
++      /*
++       * flush the command queues, before calling any operations
++       */
++      Mask = dev->InterruptMask;
++      
++      if (dev->FlushCommandCount++ == 0)
++          SetSchedStatusRegister (dev, Pend, &Mask);
++      
++      if ((read_reg32 (dev, ComQueueStatus) & ComQueueNotEmpty) != 0)
++      {
++          if (dev->HaltThreadCount++ == 0)
++              SetSchedStatusRegister (dev, Pend, &Mask);
++
++          CAPTURE_CPUS();
++
++          while ((read_reg32 (dev, ComQueueStatus) & ComQueueNotEmpty) != 0)
++              mb();
++
++          RELEASE_CPUS();
++                  
++          if (--dev->HaltThreadCount == 0)
++              SetSchedStatusRegister (dev, Pend, &Mask);
++      }
++              
++      if (read_reg32 (dev, Exts.InterruptReg) & INT_CProc)
++      {
++          PRINTF0 (DBG_DEVICE, DBG_INTR, "ProcessHaltOperations: command processor has trapped\n");
++          HandleCProcTrap (dev, Pend, &Mask);
++      }
++      
++      if (--dev->FlushCommandCount == 0)
++          SetSchedStatusRegister (dev, Pend, &Mask);
++      
++      PRINTF2 (DBG_DEVICE, DBG_INTR, "ProcessHaltOperations: interrupt mask %08x -> %08x\n", 
++               dev->InterruptMask, Mask);
++      
++      SET_INT_MASK (dev, Mask);
++      spin_unlock (&dev->IntrLock);
++
++      /*
++       * now process the list of operations
++       * we have
++       */
++      for (op = head; op != NULL; op = next)
++      {
++          next = op->Next;
++
++          op->Function (dev, op->Arguement);
++          
++          FreeHaltOperation (dev, op);
++      }
++
++      spin_lock (&dev->IntrLock);
++    }
++}
++
++int
++ComputePosition (ELAN_POSITION *pos, unsigned nodeId, unsigned numNodes, unsigned numDownLinksVal)
++{
++    int i, lvl, n;
++    char numDownLinks[ELAN_MAX_LEVELS];
++
++    if (nodeId >= numNodes)
++      return (EINVAL);
++
++    for (i = 0; i < ELAN_MAX_LEVELS; i++, numDownLinksVal >>= 4)
++      numDownLinks[i] = numDownLinksVal & 7;
++    
++    for (lvl = 0, n = numNodes; n > ((lvl % 3) == 2 ? 8 : 4) && lvl < ELAN_MAX_LEVELS; lvl++)
++    {
++      if (numDownLinks[lvl] == 0)
++          numDownLinks[lvl] = 4;
++      
++      if ((n % numDownLinks[lvl]) != 0)
++          return (EINVAL);
++      
++      n /= numDownLinks[lvl];
++    }
++
++    if (numDownLinks[lvl] == 0)
++      numDownLinks[lvl] = n;
++
++    if (numDownLinks[lvl] != n)
++      return (EINVAL);
++
++    for (i = 0; i <= lvl; i++)
++      pos->pos_arity[i] = numDownLinks[lvl - i];
++
++    pos->pos_nodes  = numNodes;
++    pos->pos_levels = lvl + 1;
++    pos->pos_nodeid = nodeId;
++    pos->pos_mode   = ELAN_POS_MODE_SWITCHED;
++
++    return (0);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan3/elandev_linux.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan3/elandev_linux.c   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan3/elandev_linux.c        2005-05-11 12:10:12.414936224 -0400
+@@ -0,0 +1,2358 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "$Id: elandev_linux.c,v 1.102.2.5 2005/03/07 16:27:44 david Exp $"
++/*    $Source: /cvs/master/quadrics/elan3mod/elan3/os/elandev_linux.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <qsnet/kpte.h>
++
++#include <linux/config.h>
++#include <linux/mm.h>
++#include <linux/pci.h>
++#include <linux/reboot.h>
++#include <linux/notifier.h>
++
++#include <linux/init.h>
++#include <linux/module.h>
++
++#include <linux/pci.h>
++#include <linux/ptrack.h>
++
++#include <asm/uaccess.h>
++#include <asm/io.h>
++#include <asm/pgalloc.h>
++#include <asm/pgtable.h>
++
++#include <elan/devinfo.h>
++#include <elan/elanmod.h>
++
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elanvp.h>
++#include <elan3/elanio.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanctxt.h>
++#include <elan3/elandebug.h>
++#include <elan3/elansyscall.h>
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0)
++#error please use a 2.2 series kernel or newer
++#endif
++
++/* Minor numbers encoded as :
++ *   [5:0]    device number
++ *   [15:6]   function number
++ */
++#define ELAN3_DEVICE_MASK          0x3F
++
++#define ELAN3_MINOR_CONTROL      0
++#define ELAN3_MINOR_MEM          1
++#define ELAN3_MINOR_USER       2
++#define ELAN3_MINOR_SHIFT        6
++
++#define ELAN3_DEVICE(inode)   (MINOR(inode->i_rdev) & ELAN3_DEVICE_MASK)
++#define ELAN3_MINOR(inode)    (MINOR(inode->i_rdev) >> ELAN3_MINOR_SHIFT)
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
++#     define SetPageReserved(page)    set_bit(PG_reserved, &(page)->flags)
++#     define ClearPageReserved(page)  clear_bit(PG_reserved, &(page)->flags)
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,23)
++typedef void irqreturn_t;
++#endif
++#       define IRQ_NONE
++#       define IRQ_HANDLED
++#       define IRQ_RETVAL(x)
++#endif
++
++
++/*
++ * Function prototypes.
++ */
++static int    elanattach(int instance, struct pci_dev *pcidev);
++static int    elandetach(int instance);
++
++static int    elan3_open (struct inode *inode, struct file *file);
++static int    elan3_ioctl (struct inode *inode, struct file *file, 
++                           unsigned int cmd, unsigned long arg);
++static int    elan3_mmap (struct file *file, struct vm_area_struct *vm_area);
++static int    elan3_release (struct inode *inode, struct file *file);
++
++static int      elan3_reboot_event (struct notifier_block *self, unsigned long event, void *buffer);
++static int      elan3_panic_event (struct notifier_block *self, unsigned long event, void *buffer);
++
++static irqreturn_t InterruptHandlerWrapper(int irq, void *dev_id, struct pt_regs *regs);
++
++static int    ConfigurePci(ELAN3_DEV *dev);
++static int    ResetElan(ELAN3_DEV *dev, ioaddr_t intPalAddr);
++
++static void     elan3_shutdown_devices(int panicing);
++
++/*
++ * Globals. 
++ */
++static ELAN3_DEV      *elan3_devices[ELAN3_MAX_CONTROLLER];
++static int       NodeId = ELAN3_INVALID_NODE;
++static int       NumNodes;
++static int       DownLinks;
++static int       RandomRoutingDisabled;
++int              BackToBackMaster;
++int              BackToBackSlave;
++int              enable_sdram_writecombining;
++int            sdram_bank_limit;
++extern int       LwpNice;
++
++char *    elan_reg_rec_file [ELAN_REG_REC_MAX];
++int       elan_reg_rec_line [ELAN_REG_REC_MAX];
++long      elan_reg_rec_lbolt[ELAN_REG_REC_MAX];
++int       elan_reg_rec_cpu  [ELAN_REG_REC_MAX];
++E3_uint32 elan_reg_rec_reg  [ELAN_REG_REC_MAX];
++int       elan_reg_rec_index;
++
++MODULE_AUTHOR("Quadrics Ltd.");
++MODULE_DESCRIPTION("Elan3 Device Driver");
++
++MODULE_LICENSE("GPL");
++
++MODULE_PARM(NodeId,"i");
++MODULE_PARM(NumNodes,"i");
++MODULE_PARM(RandomRoutingDisabled,"i");
++MODULE_PARM(DownLinks,"i");
++MODULE_PARM(BackToBackMaster,"i");
++MODULE_PARM(BackToBackSlave,"i");
++MODULE_PARM(LwpNice, "i");
++MODULE_PARM(elan3_debug, "i");
++MODULE_PARM(elan3_debug_console, "i");
++MODULE_PARM(elan3_debug_buffer, "i");
++MODULE_PARM(elan3mmu_debug, "i");
++MODULE_PARM(sdram_bank_limit, "i");
++
++/* elan3/os/context.c */
++EXPORT_SYMBOL(elan3_alloc);
++EXPORT_SYMBOL(elan3_attach);
++EXPORT_SYMBOL(elan3_doattach);
++EXPORT_SYMBOL(elan3_free);
++EXPORT_SYMBOL(elan3_detach);
++EXPORT_SYMBOL(elan3_dodetach);
++EXPORT_SYMBOL(elan3_block_inputter);
++EXPORT_SYMBOL(CheckCommandQueueFlushed);
++
++/* elan3/os/sdram.c */
++EXPORT_SYMBOL(elan3_sdram_alloc);
++EXPORT_SYMBOL(elan3_sdram_free);
++EXPORT_SYMBOL(elan3_sdram_to_phys);
++EXPORT_SYMBOL(elan3_sdram_writeb);
++EXPORT_SYMBOL(elan3_sdram_writew);
++EXPORT_SYMBOL(elan3_sdram_writel);
++EXPORT_SYMBOL(elan3_sdram_writeq);
++EXPORT_SYMBOL(elan3_sdram_readb);
++EXPORT_SYMBOL(elan3_sdram_readw);
++EXPORT_SYMBOL(elan3_sdram_readl);
++EXPORT_SYMBOL(elan3_sdram_readq);
++EXPORT_SYMBOL(elan3_sdram_zerob_sdram);
++EXPORT_SYMBOL(elan3_sdram_zerow_sdram);
++EXPORT_SYMBOL(elan3_sdram_zerol_sdram);
++EXPORT_SYMBOL(elan3_sdram_zeroq_sdram);
++EXPORT_SYMBOL(elan3_sdram_copyb_to_sdram);
++EXPORT_SYMBOL(elan3_sdram_copyw_to_sdram);
++EXPORT_SYMBOL(elan3_sdram_copyl_to_sdram);
++EXPORT_SYMBOL(elan3_sdram_copyq_to_sdram);
++EXPORT_SYMBOL(elan3_sdram_copyb_from_sdram);
++EXPORT_SYMBOL(elan3_sdram_copyw_from_sdram);
++EXPORT_SYMBOL(elan3_sdram_copyl_from_sdram);
++EXPORT_SYMBOL(elan3_sdram_copyq_from_sdram);
++
++/* elan3/os/tproc.c */
++EXPORT_SYMBOL(DeliverTProcTrap);
++EXPORT_SYMBOL(HandleTProcTrap);
++EXPORT_SYMBOL(SaveThreadToStack);
++
++/* elan3/os/tprocinsts.c */
++EXPORT_SYMBOL(RollThreadToClose);
++
++/* elan3/os/iproc.c */
++EXPORT_SYMBOL(InspectIProcTrap);
++EXPORT_SYMBOL(IProcTrapString);
++EXPORT_SYMBOL(SimulateUnlockQueue);
++
++/* elan3/os/cproc.c */
++EXPORT_SYMBOL(HandleCProcTrap);
++
++/* elan3/os/route_table.c */
++EXPORT_SYMBOL(GenerateRoute);
++EXPORT_SYMBOL(LoadRoute);
++EXPORT_SYMBOL(InvalidateRoute);
++EXPORT_SYMBOL(ValidateRoute);
++EXPORT_SYMBOL(ClearRoute);
++EXPORT_SYMBOL(GenerateProbeRoute);
++EXPORT_SYMBOL(GenerateCheckRoute);
++
++/* elan3/os/elandev_generic.c */
++EXPORT_SYMBOL(elan3_debug);
++EXPORT_SYMBOL(QueueHaltOperation);
++EXPORT_SYMBOL(ReleaseHaltOperations);
++EXPORT_SYMBOL(ReserveHaltOperations);
++
++/* elan3/vm/elan3mmu_generic.c */
++EXPORT_SYMBOL(elan3mmu_pteload);
++EXPORT_SYMBOL(elan3mmu_unload);
++EXPORT_SYMBOL(elan3mmu_set_context_filter);
++EXPORT_SYMBOL(elan3mmu_reserve);
++EXPORT_SYMBOL(elan3mmu_attach);
++EXPORT_SYMBOL(elan3mmu_detach);
++EXPORT_SYMBOL(elan3mmu_release);
++/* elan3/vm/elan3mmu_linux.c */
++EXPORT_SYMBOL(elan3mmu_phys_to_pte);
++EXPORT_SYMBOL(elan3mmu_kernel_invalid_pte);
++
++/* elan3/os/elan3_debug.c */
++EXPORT_SYMBOL(elan3_debugf);
++
++/* elan3/os/minames.c */
++EXPORT_SYMBOL(MiToName);
++
++/* elan3/os/elandev_generic.c */
++EXPORT_SYMBOL(MapDeviceRegister);
++EXPORT_SYMBOL(UnmapDeviceRegister);
++
++EXPORT_SYMBOL(elan_reg_rec_lbolt);
++EXPORT_SYMBOL(elan_reg_rec_file);
++EXPORT_SYMBOL(elan_reg_rec_index);
++EXPORT_SYMBOL(elan_reg_rec_cpu);
++EXPORT_SYMBOL(elan_reg_rec_reg);
++EXPORT_SYMBOL(elan_reg_rec_line);
++
++/*
++ * Standard device entry points.
++ */
++#if defined(CONFIG_DUMP) || defined(CONFIG_DUMP_MODULE)
++
++#include <linux/dump.h>
++
++static int      elan3_dump_event (struct notifier_block *self, unsigned long event, void *buffer);
++
++static struct notifier_block elan3_dump_notifier = 
++{
++    notifier_call:    elan3_dump_event,
++    priority:         0,
++};
++
++static int
++elan3_dump_event (struct notifier_block *self, unsigned long event, void *buffer)
++{
++    if ( event == DUMP_BEGIN )
++      elan3_shutdown_devices (FALSE);
++
++    return (NOTIFY_DONE);
++}
++
++#endif
++
++static struct file_operations elan3_fops = {
++        ioctl:   elan3_ioctl,         /* ioctl */
++        mmap:    elan3_mmap,          /* mmap */
++        open:    elan3_open,          /* open */
++        release: elan3_release,               /* release */
++};
++
++static struct notifier_block elan3_reboot_notifier = 
++{
++    notifier_call:    elan3_reboot_event,
++    priority:         0,
++};
++
++static struct notifier_block elan3_panic_notifier = 
++{
++    notifier_call:    elan3_panic_event,
++    priority:         0,
++};
++
++ELAN3_DEV *
++elan3_device (int instance)
++{
++      if (instance < 0 || instance >= ELAN3_MAX_CONTROLLER)
++          return ((ELAN3_DEV *) NULL);
++      return elan3_devices[instance];
++}
++EXPORT_SYMBOL(elan3_device);
++
++/*
++ * Called at rmmod time.  elandetach() for each card + general cleanup.
++ */
++#ifdef MODULE
++static void __exit elan3_exit(void)
++{
++      int i;
++
++      printk("elan: preparing to remove module\n");
++
++#if defined(CONFIG_DUMP) || defined(CONFIG_DUMP_MODULE)
++      unregister_dump_notifier (&elan3_dump_notifier);
++#endif
++      unregister_reboot_notifier (&elan3_reboot_notifier);
++      notifier_chain_unregister (&panic_notifier_list, &elan3_panic_notifier);
++
++      /* call elandetach() for each device configured. */
++      for (i = 0; i < ELAN3_MAX_CONTROLLER; i++)
++              if (elan3_devices[i] != NULL)
++                      elandetach(i);
++
++      FinaliseNetworkErrorResolver();
++      elan3mmu_fini();
++
++      cookie_fini();
++      unregister_chrdev(ELAN3_MAJOR, ELAN3_NAME);
++
++      elan3_procfs_fini();
++
++      printk("elan: module removed\n");
++}
++
++/*
++ * Called at insmod time.  First we perform general driver initialization,
++ * then call elanattach() for each card.
++ */
++#ifdef MODULE
++static int __init elan3_init(void)
++#else
++__initfunc(int elan3_init(void))
++#endif
++{
++      int e;
++      int boards;
++      struct pci_dev *dev;
++      char revid;
++
++      elan_reg_rec_index=0;
++      {
++          int i;
++          for(i=0;i<ELAN_REG_REC_MAX;i++)
++              elan_reg_rec_file[i] = NULL;
++      }       
++
++      /* register major/minor num */
++      e = register_chrdev(ELAN3_MAJOR, ELAN3_NAME, &elan3_fops);
++      if (e < 0)
++              return e;
++
++      elan3_procfs_init ();
++
++      cookie_init();
++      elan3mmu_init();
++      InitialiseNetworkErrorResolver();
++
++      /* call elanattach() for each device found on PCI */
++      memset(elan3_devices, 0, sizeof(elan3_devices));
++      boards = 0;
++      for (dev = NULL; (dev = pci_find_device(PCI_VENDOR_ID_QUADRICS, PCI_DEVICE_ID_ELAN3, dev)) != NULL ;) 
++      {
++          pci_read_config_byte (dev, PCI_REVISION_ID, &revid);
++
++          if (revid == PCI_REVISION_ID_ELAN3_REVA)
++              printk ("elan at pci %s - RevA device not supported\n", dev->slot_name);
++          else
++          {
++              if (boards < ELAN3_MAX_CONTROLLER)
++                      /* Count successfully attached devices */ 
++                      boards += ((elanattach(boards, dev) == 0) ? 1 : 0);
++              else
++              {
++                  printk ("elan: max controllers = %d\n", ELAN3_MAX_CONTROLLER);
++                  break;
++              }
++          }
++      }
++#if defined(CONFIG_DUMP) || defined(CONFIG_DUMP_MODULE)
++      register_dump_notifier (&elan3_dump_notifier);
++#endif
++      register_reboot_notifier (&elan3_reboot_notifier);
++      notifier_chain_register (&panic_notifier_list, &elan3_panic_notifier);
++
++      return 0;
++}
++
++/* Declare the module init and exit functions */
++module_init(elan3_init);
++module_exit(elan3_exit);
++
++#endif
++
++static void
++elan3_shutdown_devices(int panicing)
++{
++    ELAN3_DEV *dev;
++    unsigned long flags;
++    register int i;
++
++    local_irq_save (flags);
++    for (i = 0; i < ELAN3_MAX_CONTROLLER; i++)
++    {
++      if ((dev = elan3_devices[i]) != NULL)
++      {
++          if (! panicing) spin_lock (&dev->IntrLock);
++          
++          printk(KERN_INFO "elan%d: forcing link into reset\n", dev->Instance);
++
++          /*
++           * We're going to set the link into boundary scan mode,  so firstly
++           * set the inputters to discard everything.
++           */
++          if (dev->DiscardAllCount++ == 0)
++              SetSchedStatusRegister (dev, read_reg32 (dev, Exts.InterruptReg), NULL);
++
++          dev->LinkShutdown = 1;
++          
++          /*
++           * Now disable the error interrupts
++           */
++          DISABLE_INT_MASK (dev, INT_ErrorInterrupts);
++          
++          /*
++           * And set the link into boundary scan mode, and drive
++           * a reset token onto the link.
++           */
++          SET_SCHED_LINK_VALUE (dev, 1, LinkResetToken);
++
++          if (! panicing) spin_unlock (&dev->IntrLock);
++      }
++    }
++    local_irq_restore (flags);
++}
++
++static int
++elan3_reboot_event (struct notifier_block *self, unsigned long event, void *buffer)
++{
++    if (! (event == SYS_RESTART || event == SYS_HALT || event == SYS_POWER_OFF))
++      return (NOTIFY_DONE);
++
++    elan3_shutdown_devices (FALSE);
++
++    return (NOTIFY_DONE);
++}
++
++static int
++elan3_panic_event (struct notifier_block *self, unsigned long event, void *buffer)
++{
++    elan3_shutdown_devices (TRUE);
++
++    return (NOTIFY_DONE);
++}
++
++#include <elan3/elan3ops.h>
++/*
++ * Called by init_module() for each card discovered on PCI.
++ */
++static int
++elanattach(int instance, struct pci_dev *pcidev)
++{
++      ELAN3_DEV *dev;
++      int ramSize;
++      int level;
++      ioaddr_t sdramAddr, cmdPortAddr, intPalAddr;
++      DeviceMappingHandle handle;
++
++      printk("elan%d: attach, irq=%d\n", instance, pcidev->irq);
++
++      /*
++       * Allocate the ELAN3_DEV structure.
++       */
++      KMEM_ZALLOC(dev, ELAN3_DEV *, sizeof(ELAN3_DEV), TRUE);
++      if (dev == NULL) {
++              printk ("elan%d: KMEM_ALLOC failed\n", instance);
++              return (-ENOMEM);
++      }
++      elan3_devices[instance] = dev;
++      dev->Osdep.pci = pcidev;
++
++      dev->Instance = instance;
++
++      /* Initialise the device information */
++      pci_read_config_word (pcidev, PCI_VENDOR_ID,   &dev->Devinfo.dev_vendor_id);
++      pci_read_config_word (pcidev, PCI_DEVICE_ID,   &dev->Devinfo.dev_device_id);
++      pci_read_config_byte (pcidev, PCI_REVISION_ID, &dev->Devinfo.dev_revision_id);
++
++      dev->Devinfo.dev_instance             = instance;
++      dev->Devinfo.dev_rail                 = instance;
++      dev->Devinfo.dev_driver_version       = 0;
++      dev->Devinfo.dev_num_down_links_value = DownLinks;
++
++      dev->Position.pos_mode                = ELAN_POS_UNKNOWN;
++      dev->Position.pos_random_disabled     = RandomRoutingDisabled;
++      
++      /*
++       * Set up PCI config regs.
++       */
++      if (ConfigurePci(dev) != ESUCCESS)
++          goto fail0;
++
++      /*
++       * Determine the PFnums of the SDRAM and command port
++       */
++      if (MapDeviceRegister(dev, ELAN3_BAR_SDRAM, &sdramAddr, 0, PAGESIZE, &handle) != ESUCCESS)
++          goto fail1;
++
++      DeviceRegisterSize(dev, ELAN3_BAR_SDRAM, &ramSize);
++      
++      dev->SdramPhysMask = ~((physaddr_t) ramSize - 1);
++      dev->SdramPhysBase = kmem_to_phys((void *) sdramAddr);
++
++      UnmapDeviceRegister (dev, &handle);
++
++#if defined(LINUX_ALPHA)
++      /*
++       * consider a physical address to be on the same pci bus
++       * as us if it's physical address is "close" to our sdram
++       * physical address.
++       * this is almost certainly incorrect for large memory (> 2Gb)
++       * i386 machines - and is only correct for alpha for 32 bit
++       * base address registers.
++       *
++       * Modified this to match the Tru64 driver value;
++       * i.e. PciPhysMask = 0xfffffffffffc0000
++       */
++#  define PCI_ADDR_MASK (0x7FFFFFFFl)
++
++      dev->PciPhysMask = ~PCI_ADDR_MASK;
++      dev->PciPhysBase = dev->SdramPhysBase & dev->PciPhysMask;
++#endif
++      /*
++       * Now reset the elan chip.
++       */
++      if (MapDeviceRegister(dev, ELAN3_BAR_REGISTERS, &dev->RegPtr, 0, 0, &dev->RegHandle) != ESUCCESS)
++          goto fail1;
++
++      if (MapDeviceRegister(dev, ELAN3_BAR_EBUS, &intPalAddr, ELAN3_EBUS_INTPAL_OFFSET, PAGESIZE,
++                            &handle) != ESUCCESS)
++          goto fail2;
++
++      ResetElan(dev, intPalAddr);     
++
++      UnmapDeviceRegister (dev, &handle);
++
++      /* 
++       * Initialise the device mutex's which must be accessible from the 
++       * interrupt handler.  
++       */
++      kcondvar_init (&dev->IntrWait);
++      spin_lock_init (&dev->IntrLock);
++      spin_lock_init (&dev->TlbLock);
++      spin_lock_init (&dev->CProcLock);
++      spin_lock_init (&dev->FreeHaltLock);
++      for(level=0; level<4; level++)
++          spin_lock_init (&dev->Level[level].PtblLock);
++      spin_lock_init (&dev->PtblGroupLock);
++
++      /*
++       * Add the interrupt handler,  
++       */
++      if (request_irq(dev->Osdep.pci->irq, InterruptHandlerWrapper, 
++          SA_SHIRQ, "elan3", dev) != 0) {
++              printk ("elan%d: request_irq failed\n", instance);
++              goto fail3;
++      }
++
++      if (MapDeviceRegister(dev, ELAN3_BAR_COMMAND_PORT, &cmdPortAddr, 0, PAGESIZE, &handle) != ESUCCESS)
++          goto fail4;
++      
++      if (InitialiseElan(dev, cmdPortAddr) == EFAIL) {
++              printk ("elan%d: InitialiseElan failed\n", instance);
++              UnmapDeviceRegister (dev, &handle);
++              goto fail4;
++      }
++      UnmapDeviceRegister (dev, &handle);
++
++      /* If our nodeid is defined, then set it now */
++      if (NodeId != ELAN3_INVALID_NODE && ComputePosition (&dev->Position, NodeId, NumNodes, DownLinks) == 0)
++      {
++          if (RandomRoutingDisabled & ((1 << (dev->Position.pos_levels-1))-1))
++              printk ("elan%d: NodeId=%d NodeLevel=%d NumNodes=%d (random routing disabled 0x%x)\n", 
++                      dev->Instance, dev->Position.pos_nodeid, dev->Position.pos_levels, dev->Position.pos_nodes, RandomRoutingDisabled);
++          else
++              printk ("elan%d: NodeId=%d NodeLevel=%d NumNodes=%d (random routing ok)\n",
++                      dev->Instance, dev->Position.pos_nodeid, dev->Position.pos_levels, dev->Position.pos_nodes);
++      }
++
++      if (BackToBackMaster || BackToBackSlave)
++      {
++          dev->Position.pos_mode     = ELAN_POS_MODE_BACKTOBACK;
++          dev->Position.pos_nodeid   = (BackToBackMaster == 0);
++          dev->Position.pos_nodes    = 2;
++          dev->Position.pos_levels   = 1;
++          dev->Position.pos_arity[0] = 2;
++
++          printk ("elan%d: back-to-back %s - elan node %d\n", dev->Instance,
++                  BackToBackMaster ? "master" : "slave", dev->Position.pos_nodeid);
++      }
++
++      elan3_procfs_device_init (dev);
++      
++      /* Success */
++      return (0);
++
++fail4:
++      free_irq(dev->Osdep.pci->irq, dev);
++
++fail3:
++      kcondvar_destroy (&dev->IntrWait);
++      spin_lock_destroy (&dev->IntrLock);
++      spin_lock_destroy (&dev->InfoLock);
++      spin_lock_destroy (&dev->TlbLock);
++      spin_lock_destroy (&dev->CProcLock);
++      spin_lock_destroy (&dev->FreeHaltLock);
++      spin_lock_destroy (&dev->Level1PtblLock);
++      spin_lock_destroy (&dev->Level2PtblLock);
++      spin_lock_destroy (&dev->Level3PtblLock);
++      spin_lock_destroy (&dev->PtblGroupLock);
++
++fail2:
++      UnmapDeviceRegister (dev, &dev->RegHandle);
++
++fail1:
++      pci_disable_device (dev->Osdep.pci);
++fail0:
++      KMEM_FREE(dev, sizeof(ELAN3_DEV));
++
++      elan3_devices[instance] = NULL;
++      
++      /* Failure */
++      return (-ENODEV);
++}
++
++/*
++ * Called by elan3_exit() for each board found on PCI.
++ */
++static int
++elandetach(int instance)
++{
++      ELAN3_DEV *dev = elan3_devices[instance];
++
++      printk("elan%d: detach\n", instance);
++
++      elan3_procfs_device_fini (dev);
++
++      FinaliseElan (dev);
++
++      UnmapDeviceRegister (dev, &dev->RegHandle);
++
++      free_irq(dev->Osdep.pci->irq, dev);
++
++      pci_disable_device(dev->Osdep.pci);
++
++      kcondvar_destroy (&dev->IntrWait);
++      spin_lock_destroy (&dev->IntrLock);
++      spin_lock_destroy (&dev->InfoLock);
++      spin_lock_destroy (&dev->TlbLock);
++      spin_lock_destroy (&dev->CProcLock);
++      spin_lock_destroy (&dev->FreeHaltLock);
++      spin_lock_destroy (&dev->Level1PtblLock);
++      spin_lock_destroy (&dev->Level2PtblLock);
++      spin_lock_destroy (&dev->Level3PtblLock);
++      spin_lock_destroy (&dev->PtblGroupLock);
++
++      KMEM_FREE(dev, sizeof(ELAN3_DEV));
++      elan3_devices[instance] = NULL; 
++
++      return 0;
++}
++
++/*
++ * generic ioctls - available on control and user devices.
++ */
++
++static int
++device_stats_ioctl (ELAN3_DEV *dev, unsigned long arg)
++{
++    ELAN3IO_STATS_STRUCT *args;
++
++    KMEM_ALLOC(args, ELAN3IO_STATS_STRUCT *, sizeof(ELAN3IO_STATS_STRUCT), TRUE);
++      
++    if (args == NULL)
++      return (-ENOMEM);
++
++    if (copy_from_user (args, (void *) arg, sizeof (ELAN3IO_STATS_STRUCT)))
++    {
++      KMEM_FREE(args, sizeof(ELAN3IO_STATS_STRUCT));
++      return (-EFAULT);
++    }
++
++    switch (args->which)
++    {
++    case ELAN3_SYS_STATS_DEVICE:
++      if (copy_to_user (args->ptr, &dev->Stats, sizeof (ELAN3_STATS)))
++      {
++          KMEM_FREE(args, sizeof(ELAN3IO_STATS_STRUCT));
++          return (-EFAULT);
++      }
++      KMEM_FREE(args, sizeof(ELAN3IO_STATS_STRUCT));
++      return (0);
++
++    case ELAN3_SYS_STATS_MMU:
++      if (copy_to_user (args->ptr, &elan3mmu_global_stats, sizeof (ELAN3MMU_GLOBAL_STATS)))
++      {
++          KMEM_FREE(args, sizeof(ELAN3IO_STATS_STRUCT));
++          return (-EFAULT);
++      }
++      KMEM_FREE(args, sizeof(ELAN3IO_STATS_STRUCT));
++      return (0);
++          
++    default:
++      KMEM_FREE(args, sizeof(ELAN3IO_STATS_STRUCT));
++      return (-EINVAL);
++    }
++}
++
++/*
++ * /dev/elan3/controlX - control device
++ *
++ */
++
++typedef struct control_private
++{
++    u_int             pr_boundary_scan;
++} CONTROL_PRIVATE;
++
++static int
++control_open (struct inode *inode, struct file *file)
++{
++    CONTROL_PRIVATE *pr;
++
++    KMEM_ALLOC(pr, CONTROL_PRIVATE *, sizeof (CONTROL_PRIVATE), TRUE);
++
++    if (pr == NULL)
++      return (-ENOMEM);
++
++    pr->pr_boundary_scan = 0;
++    
++    file->private_data = (void *) pr;
++
++    MOD_INC_USE_COUNT;
++
++    return (0);
++}
++
++static int
++control_release (struct inode *inode, struct file *file)
++{
++    ELAN3_DEV        *dev = elan3_devices[ELAN3_DEVICE(inode)];
++    CONTROL_PRIVATE *pr  = (CONTROL_PRIVATE *) file->private_data;
++
++    if (pr->pr_boundary_scan)
++      ClearLinkBoundaryScan(dev);
++
++    KMEM_FREE (pr, sizeof(CONTROL_PRIVATE));
++
++    MOD_DEC_USE_COUNT;
++    return (0);
++}
++
++static int
++control_ioctl (struct inode *inode, struct file *file, 
++             unsigned int cmd, unsigned long arg)
++{
++    ELAN3_DEV        *dev = elan3_devices[ELAN3_DEVICE(inode)];
++    CONTROL_PRIVATE *pr  = (CONTROL_PRIVATE *) file->private_data;
++    int                    res;
++
++    switch (cmd) 
++    {
++    case ELAN3IO_SET_BOUNDARY_SCAN:
++      if (SetLinkBoundaryScan (dev) == 0)
++          pr->pr_boundary_scan = 1;
++      return (0);
++
++    case ELAN3IO_CLEAR_BOUNDARY_SCAN:
++      if (pr->pr_boundary_scan == 0)
++          return (-EINVAL);
++
++      pr->pr_boundary_scan = 0;
++
++      ClearLinkBoundaryScan (dev);
++      return (0);
++
++    case ELAN3IO_READ_LINKVAL:
++    {
++      E3_uint32 val;
++
++      if (pr->pr_boundary_scan == 0)
++          return (-EINVAL);
++
++      if (copy_from_user(&val, (E3_uint32 *)arg, sizeof(E3_uint32)))
++          return (-EFAULT);
++
++      val = ReadBoundaryScanValue (dev, val);
++
++      if (copy_to_user((E3_uint32 *)arg, &val, sizeof(E3_uint32)))
++          return (-EFAULT);
++      return (0);
++    }
++      
++    case ELAN3IO_WRITE_LINKVAL:
++    {
++      E3_uint32 val;
++
++      if (pr->pr_boundary_scan == 0)
++          return (-EINVAL);
++
++      if (copy_from_user(&val, (E3_uint32 *)arg, sizeof(E3_uint32)))
++          return (-EFAULT);
++
++      val = WriteBoundaryScanValue (dev, val);
++
++      if (copy_to_user((E3_uint32 *)arg, &val, sizeof(E3_uint32)))
++          return (-EFAULT);
++      
++      return (0);
++    }
++
++    case ELAN3IO_SET_POSITION:
++    {
++      ELAN3IO_SET_POSITION_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN3IO_SET_POSITION_STRUCT)))
++          return (-EFAULT);
++      
++      if (ComputePosition (&dev->Position, args.nodeId, args.numNodes, dev->Devinfo.dev_num_down_links_value) != 0)
++          return (-EINVAL);
++
++      return (0);
++    }
++
++    case ELAN3IO_SET_DEBUG:
++    {
++      ELAN3IO_SET_DEBUG_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN3IO_SET_DEBUG_STRUCT)))
++          return (-EFAULT);
++
++      if (! strcmp (args.what, "elan3_debug"))
++          elan3_debug = args.value;
++      else if (! strcmp (args.what, "elan3_debug_console"))
++          elan3_debug_console = args.value;
++      else if (! strcmp (args.what, "elan3_debug_buffer"))
++          elan3_debug_buffer = args.value;
++      else if (! strcmp (args.what, "elan3_debug_ignore_dev"))
++          elan3_debug_ignore_dev = args.value;
++      else if (! strcmp (args.what, "elan3_debug_ignore_ctxt"))
++          elan3_debug_ignore_ctxt = args.value;
++      else if (! strcmp (args.what, "elan3mmu_debug"))
++          elan3mmu_debug = args.value;
++      
++      return (0);
++    }
++
++    case ELAN3IO_NETERR_SERVER:
++    {
++      ELAN3IO_NETERR_SERVER_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN3IO_NETERR_SERVER_STRUCT)))
++          return (-EFAULT);
++      
++      res = AddNeterrServerSyscall (args.elanid, args.addr, args.name, NULL);
++      return (set_errno (res));
++    }
++    
++    case ELAN3IO_NETERR_FIXUP:
++    {
++      NETERR_MSG *msg;
++
++      KMEM_ALLOC(msg, NETERR_MSG *, sizeof (NETERR_MSG), TRUE);
++
++      if (msg == NULL)
++          return (set_errno (ENOMEM));
++      
++      if (copy_from_user (msg, (void *) arg, sizeof (NETERR_MSG)))
++          res = EFAULT;
++      else
++          res = ExecuteNetworkErrorFixup (msg);
++
++      KMEM_FREE (msg, sizeof (NETERR_MSG));
++      return (set_errno (res));
++    }
++
++    case ELAN3IO_STATS:
++      return (device_stats_ioctl (dev, arg));
++
++    case ELAN3IO_GET_DEVINFO:
++    {
++      if (copy_to_user ((void *) arg, &dev->Devinfo, sizeof (ELAN_DEVINFO)))
++          return (-EFAULT);
++      return (0);
++    }
++
++    case ELAN3IO_GET_POSITION:
++    {
++      if (copy_to_user ((void *) arg, &dev->Position, sizeof (ELAN_POSITION)))
++          return (-EFAULT);
++      return (0);
++    }
++    default:
++      return (-EINVAL);
++    }
++}
++
++static int
++control_mmap (struct file *file, struct vm_area_struct *vma)
++{
++    ELAN3_DEV                *dev   = elan3_devices[ELAN3_DEVICE(file->f_dentry->d_inode)];
++    int               space = OFF_TO_SPACE(vma->vm_pgoff << PAGE_SHIFT);
++    int               off   = OFF_TO_OFFSET(vma->vm_pgoff << PAGE_SHIFT);
++    int               size;
++    ioaddr_t          addr;
++    DeviceMappingHandle handle;
++    physaddr_t                phys;
++    
++    if (space < ELAN3_BAR_SDRAM || space > ELAN3_BAR_EBUS)
++      return (-EINVAL);
++
++    if (off < 0 || DeviceRegisterSize (dev, space, &size) != ESUCCESS || off > size)
++      return (-EINVAL);
++
++    if (MapDeviceRegister(dev, space, &addr, off, PAGESIZE, &handle) != ESUCCESS)
++      return (-EINVAL);
++
++    phys = kmem_to_phys((caddr_t) addr);
++    UnmapDeviceRegister(dev, &handle);
++
++#ifdef NO_RMAP
++    if (remap_page_range(vma->vm_start, phys, vma->vm_end - vma->vm_start, vma->vm_page_prot))
++#else
++    if (remap_page_range(vma, vma->vm_start, phys, vma->vm_end - vma->vm_start, vma->vm_page_prot))
++#endif
++      return (-EAGAIN);
++
++    return (0);
++}
++
++/*
++ * /dev/elan3/sdramX - sdram access device
++ */
++typedef struct mem_page
++{
++    struct mem_page *pg_next;
++    sdramaddr_t      pg_addr;
++    u_long         pg_pgoff;
++    u_int          pg_ref;
++} MEM_PAGE;
++
++#define MEM_HASH_SIZE 32
++#define MEM_HASH(pgoff)       ((pgoff) & (MEM_HASH_SIZE-1))
++
++typedef struct mem_private
++{
++    ELAN3_DEV   *pr_dev;
++    MEM_PAGE   *pr_pages[MEM_HASH_SIZE];
++    spinlock_t  pr_lock;
++} MEM_PRIVATE;
++
++static void 
++mem_freepage (MEM_PRIVATE *pr, MEM_PAGE *pg)
++{
++    PRINTF (DBG_DEVICE, DBG_SEG, "mem_freepage: pr=%p pgoff=%lx pg=%p ref=%d\n", pr, pg->pg_pgoff, pg, pg->pg_ref);
++
++    elan3_sdram_free (pr->pr_dev, pg->pg_addr, PAGE_SIZE);
++    KMEM_FREE (pg, sizeof(MEM_PAGE));
++}
++
++static MEM_PAGE *
++mem_getpage (MEM_PRIVATE *pr, u_long pgoff, virtaddr_t addr)
++{
++    int       hashval = MEM_HASH (pgoff);
++    MEM_PAGE *npg = NULL;
++    MEM_PAGE *pg;
++
++    PRINTF (DBG_DEVICE, DBG_SEG, "mem_getpage: pr=%p pgoff=%lx addr=%lx\n", pr, pgoff, addr);
++    
++ again:
++    spin_lock (&pr->pr_lock);
++    for (pg = pr->pr_pages[hashval]; pg; pg = pg->pg_next)
++      if (pg->pg_pgoff == pgoff)
++          break;
++    
++    if (pg != NULL)
++    {
++      PRINTF (DBG_DEVICE, DBG_SEG, "mem_getpage: pr=%p pgoff=%lx addr=%lx -> found %p addr=%lx\n", pr, pgoff, addr, pg, pg->pg_addr);
++
++      pg->pg_ref++;
++      spin_unlock (&pr->pr_lock);
++
++      if (npg != NULL)                                        /* we'd raced and someone else had created */
++          mem_freepage (pr, npg);                             /* this page - so free of our new one*/
++      return (pg);
++    }
++    
++    if (npg != NULL)                                          /* didn't find the page, so inset the */
++    {                                                         /* new one we've just created */
++      npg->pg_next = pr->pr_pages[hashval];
++      pr->pr_pages[hashval] = npg;
++      
++      spin_unlock (&pr->pr_lock);
++      return (npg);
++    }
++    
++    spin_unlock (&pr->pr_lock);                                       /* drop spinlock before creating a new page */
++    
++    KMEM_ALLOC(npg, MEM_PAGE *, sizeof (MEM_PAGE), TRUE);
++
++    if (npg == NULL)
++      return (NULL);
++
++    if ((npg->pg_addr = elan3_sdram_alloc (pr->pr_dev, PAGE_SIZE)) == 0)
++    {
++      KMEM_FREE (npg, sizeof (MEM_PAGE));
++      return (NULL);
++    }
++
++    /* zero the page before returning it to the user */
++    elan3_sdram_zeroq_sdram (pr->pr_dev, npg->pg_addr, PAGE_SIZE);
++    
++    npg->pg_pgoff = pgoff;
++    npg->pg_ref   = 1;
++    
++    /* created a new page - so have to rescan before inserting it */
++    goto again;
++}
++
++static void
++mem_droppage (MEM_PRIVATE *pr, u_long pgoff, int dontfree)
++{
++    MEM_PAGE **ppg;
++    MEM_PAGE  *pg;
++
++    spin_lock (&pr->pr_lock);
++    for (ppg = &pr->pr_pages[MEM_HASH(pgoff)]; *ppg; ppg = &(*ppg)->pg_next)
++      if ((*ppg)->pg_pgoff == pgoff)
++          break;
++
++    pg = *ppg;
++
++    ASSERT (*ppg != NULL);
++    
++    PRINTF (DBG_DEVICE, DBG_SEG, "mem_droppage: pr=%p pgoff=%lx pg=%p ref=%d dontfree=%d\n", pr, pgoff, (*ppg), (*ppg)->pg_ref, dontfree);
++
++    if (--pg->pg_ref == 0 && !dontfree)
++    {
++      *ppg = pg->pg_next;
++
++      mem_freepage (pr, pg);
++    }
++
++    spin_unlock (&pr->pr_lock);
++}
++
++static int
++mem_open (struct inode *inode, struct file *file)
++{
++    ELAN3_DEV    *dev = elan3_devices[ELAN3_DEVICE(inode)];
++    MEM_PRIVATE *pr;
++    register int i;
++
++    KMEM_ALLOC(pr, MEM_PRIVATE *, sizeof (MEM_PRIVATE), TRUE);
++
++    if (pr == NULL)
++      return (-ENOMEM);
++
++    spin_lock_init (&pr->pr_lock);
++    pr->pr_dev = dev;
++    for (i = 0; i < MEM_HASH_SIZE; i++)
++      pr->pr_pages[i] = NULL;
++
++    file->private_data = (void *) pr;
++    
++    MOD_INC_USE_COUNT;
++    return (0);
++}
++
++static int
++mem_release (struct inode *node, struct file *file)
++{
++    MEM_PRIVATE *pr = (MEM_PRIVATE *) file->private_data;
++    MEM_PAGE    *pg, *next;
++    int          i;
++
++    /* free off any pages that we'd allocated */
++    spin_lock (&pr->pr_lock);
++    for (i = 0; i < MEM_HASH_SIZE; i++)
++    {
++      for (pg = pr->pr_pages[i]; pg; pg = next)
++      {
++          next = pg->pg_next;
++          mem_freepage (pr, pg);
++      }
++    }
++    spin_unlock (&pr->pr_lock);
++
++    KMEM_FREE (pr, sizeof (MEM_PRIVATE));
++
++    MOD_DEC_USE_COUNT;
++    return (0);
++}
++
++static int
++mem_ioctl (struct inode *inode, struct file *file, 
++                unsigned int cmd, unsigned long arg)
++{
++    return (-EINVAL);
++}
++
++static void mem_vma_open(struct vm_area_struct *vma)
++{
++    MEM_PRIVATE   *pr = (MEM_PRIVATE *) vma->vm_private_data;
++    unsigned long addr;
++    unsigned long pgoff;
++
++    PRINTF (DBG_DEVICE, DBG_SEG, "mem_vma_open: vm_mm=%p start=%lx end=%lx pgoff=%lx file=%p\n",
++          vma->vm_mm, vma->vm_start, vma->vm_end, vma->vm_pgoff, vma->vm_file);
++
++    preemptable_start {
++      for (addr = vma->vm_start, pgoff = vma->vm_pgoff; addr < vma->vm_end; addr += PAGE_SIZE, pgoff++) {
++          mem_getpage (pr, pgoff, addr);
++          preemptable_check();
++      }
++    } preemptable_end;
++}
++
++static void mem_vma_close(struct vm_area_struct *vma)
++{
++    MEM_PRIVATE  *pr  = (MEM_PRIVATE *) vma->vm_private_data;
++    unsigned long addr;
++    unsigned long pgoff;
++
++    PRINTF (DBG_DEVICE, DBG_SEG, "mem_vma_close: vm_mm=%p start=%lx end=%lx pgoff=%lx file=%p\n",
++          vma->vm_mm, vma->vm_start, vma->vm_end, vma->vm_pgoff, vma->vm_file);
++
++    /* NOTE: the call to close may not have the same vm_start/vm_end values as 
++     *       were passed into mmap()/open() - since if an partial unmap had occured
++     *       then the vma could have been shrunk or even split.
++     *
++     *       if a the vma is split then an vma_open() will be called for the top
++     *       portion - thus causing the reference counts to become incorrect.
++     *
++     * We drop the reference to any pages we're notified about - so they get freed
++     * earlier than when the device is finally released.
++     */
++    for (pgoff = vma->vm_pgoff, addr = vma->vm_start; addr < vma->vm_end; addr += PAGE_SIZE, pgoff++)
++      mem_droppage (pr, pgoff, 0);
++}
++
++static struct vm_operations_struct mem_vm_ops = {
++    open:             mem_vma_open,
++    close:            mem_vma_close,
++};
++
++static int
++mem_mmap (struct file *file, struct vm_area_struct *vma)
++{
++    MEM_PRIVATE  *pr = (MEM_PRIVATE *) file->private_data;
++    MEM_PAGE     *pg;
++    unsigned long addr;
++    unsigned long pgoff;
++
++    PRINTF (DBG_DEVICE, DBG_SEG, "mem_mmap: vm_mm=%p start=%lx end=%lx pgoff=%lx prot=%lx file=%p\n",
++          vma->vm_mm, vma->vm_start, vma->vm_end, vma->vm_pgoff, vma->vm_page_prot.pgprot , file);
++
++    preemptable_start {
++      for (addr = vma->vm_start, pgoff = vma->vm_pgoff; addr < vma->vm_end; addr += PAGE_SIZE, pgoff++)
++      {
++          if ((pg = mem_getpage (pr, pgoff, addr)) == NULL)
++              goto failed;
++          
++#ifdef LINUX_SPARC
++          pgprot_val(vma->vm_page_prot) &= ~(_PAGE_CACHE);
++          pgprot_val(vma->vm_page_prot) |= _PAGE_IE;
++#elif defined(pgprot_noncached)
++          vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
++#endif
++          
++#if defined(__ia64__)
++          if (enable_sdram_writecombining)
++              vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
++#endif
++          PRINTF (DBG_DEVICE, DBG_SEG, "mem_mmap: addr %lx -> pg=%p addr=%lx phys=%llx flags=%lx prot=%lx\n",
++                  addr, pg, pg->pg_addr, (long long) elan3_sdram_to_phys (pr->pr_dev, pg->pg_addr), vma->vm_flags, vma->vm_page_prot.pgprot);
++          
++#ifdef NO_RMAP
++          if (remap_page_range (addr, elan3_sdram_to_phys (pr->pr_dev, pg->pg_addr), PAGE_SIZE, vma->vm_page_prot))
++#else
++          if (remap_page_range (vma, addr, elan3_sdram_to_phys (pr->pr_dev, pg->pg_addr), PAGE_SIZE, vma->vm_page_prot))
++#endif
++          {
++              mem_droppage (pr, pgoff, 0);                    /* drop our reference to this page */
++              goto failed;
++          }
++
++          preemptable_check();
++      }
++    } preemptable_end;
++
++    /* Don't try to swap out Elan SDRAM pages.. */
++    vma->vm_flags |= VM_RESERVED;
++    
++    /*
++     * Don't dump SDRAM pages to a core file 
++     * (Pity I would really like to do this but it crashes in elf_core_dump() as
++     * it can only handle pages that are in the mem_map area (addy 11/01/2002))
++     */
++    vma->vm_flags |= VM_IO;
++
++    vma->vm_ops          = &mem_vm_ops;
++    vma->vm_file         = file;
++    vma->vm_private_data = (void *) pr;
++
++    return (0);
++
++ failed:
++    PRINTF (DBG_DEVICE, DBG_SEG, "mem_mmap: failed\n");
++
++    /* free of any pages we've already allocated/referenced */
++    while ((--pgoff) >= vma->vm_pgoff)
++      mem_droppage (pr, pgoff, 0);
++
++    return (-ENOMEM);
++}
++
++/*
++ * /dev/elan3/userX - control device
++ *
++ * "user_private" can be referenced from a number of places
++ *   1) the "file" structure.
++ *   2) the "mm" ioproc ops
++ *   3) the "mmap" of the command port.
++ *
++ */
++typedef struct user_private
++{
++    spinlock_t        pr_lock;
++    atomic_t        pr_mappings;
++    atomic_t          pr_ref;
++    ELAN3_CTXT        *pr_ctxt;
++    struct mm_struct *pr_mm;
++    struct ioproc_ops pr_ioproc;
++} USER_PRIVATE;
++
++static void
++user_free (USER_PRIVATE *pr)
++{
++    /* Have to unreserve the FlagPage or else we leak memory like a sieve! */
++    ClearPageReserved(pte_page(*find_pte_kernel((unsigned long) pr->pr_ctxt->FlagPage)));
++
++    elan3_detach(pr->pr_ctxt);
++    elan3_free (pr->pr_ctxt);
++
++    KMEM_FREE (pr, sizeof(USER_PRIVATE));
++
++    MOD_DEC_USE_COUNT;
++}
++
++static void
++user_ioproc_release (void *arg, struct mm_struct *mm)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) arg;
++
++    PRINTF3 (pr->pr_ctxt, DBG_SEG, "user_ioproc_release: ctxt=%p pr=%p ref=%d\n",
++           pr->pr_ctxt, pr, atomic_read (&pr->pr_ref));
++
++    elan3mmu_pte_ctxt_unload (pr->pr_ctxt->Elan3mmu);
++
++    pr->pr_mm = NULL;
++
++    if (atomic_dec_and_test (&pr->pr_ref))
++      user_free (pr);
++}
++
++/*
++ * On 2.4 kernels we get passed a mm_struct, whereas on 2.6 kernels
++ * we get the vma which is more usefull
++ */
++#if defined(IOPROC_MM_STRUCT_ARG)
++static void
++user_ioproc_sync_range (void *arg, struct mm_struct *mm, unsigned long start, unsigned long end)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) arg;
++
++    PRINTF2 (pr->pr_ctxt, DBG_SEG, "user_ioproc_sync_range: start=%lx end=%lx\n", start, end);
++
++    ASSERT(start <= end);
++
++    elan3mmu_pte_range_unload(pr->pr_ctxt->Elan3mmu, mm, (caddr_t) start, end-start);
++}
++
++static void
++user_ioproc_invalidate_range (void *arg, struct mm_struct *mm, unsigned long start, unsigned long end)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) arg;
++
++    PRINTF2 (pr->pr_ctxt, DBG_SEG, "user_ioproc_invalidate_range: start=%lx end=%lx\n", start, end);
++
++    ASSERT(start <= end);
++
++    elan3mmu_pte_range_unload(pr->pr_ctxt->Elan3mmu, mm, (caddr_t) start, end-start);
++}
++
++static void
++user_ioproc_update_range (void *arg, struct mm_struct *mm, unsigned long start, unsigned long end)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) arg;
++
++    ASSERT(start <= end && ((start & PAGEOFFSET) == 0) && ((end & PAGEOFFSET) == 0));
++
++    PRINTF2 (pr->pr_ctxt, DBG_SEG, "user_ioproc_update_range: start=%lx end=%lx\n", start, end);
++
++    elan3mmu_pte_range_update (pr->pr_ctxt->Elan3mmu, mm,(caddr_t) start, end-start);
++}
++
++static void
++user_ioproc_change_protection (void *arg, struct mm_struct *mm, unsigned long start, unsigned long end, pgprot_t newprot)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) arg;
++
++    PRINTF2 (pr->pr_ctxt, DBG_SEG, "user_ioproc_change_protection: start=%lx end=%lx\n", start, end);
++
++    ASSERT(start <= end);
++
++    elan3mmu_pte_range_unload(pr->pr_ctxt->Elan3mmu, mm, (caddr_t) start, end-start);
++}
++
++#else
++
++static void
++user_ioproc_sync_range (void *arg, struct vm_area_struct *vma, unsigned long start, unsigned long end)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) arg;
++
++    PRINTF2 (pr->pr_ctxt, DBG_SEG, "user_ioproc_sync_range: start=%lx end=%lx\n", start, end);
++
++    ASSERT(start <= end);
++
++    elan3mmu_pte_range_unload(pr->pr_ctxt->Elan3mmu, vma->vm_mm, (caddr_t) start, end-start);
++}
++
++static void
++user_ioproc_invalidate_range (void *arg, struct vm_area_struct *vma, unsigned long start, unsigned long end)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) arg;
++
++    PRINTF2 (pr->pr_ctxt, DBG_SEG, "user_ioproc_invalidate_range: start=%lx end=%lx\n", start, end);
++
++    ASSERT(start <= end);
++
++    elan3mmu_pte_range_unload(pr->pr_ctxt->Elan3mmu, vma->vm_mm, (caddr_t) start, end-start);
++}
++
++static void
++user_ioproc_update_range (void *arg, struct vm_area_struct *vma, unsigned long start, unsigned long end)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) arg;
++
++    ASSERT(start <= end && ((start & PAGEOFFSET) == 0) && ((end & PAGEOFFSET) == 0));
++
++    PRINTF2 (pr->pr_ctxt, DBG_SEG, "user_ioproc_update_range: start=%lx end=%lx\n", start, end);
++
++    elan3mmu_pte_range_update (pr->pr_ctxt->Elan3mmu, vma->vm_mm, (caddr_t) start, end-start);
++}
++
++static void
++user_ioproc_change_protection (void *arg, struct vm_area_struct *vma, unsigned long start, unsigned long end, pgprot_t newprot)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) arg;
++
++    PRINTF2 (pr->pr_ctxt, DBG_SEG, "user_ioproc_change_protection: start=%lx end=%lx\n", start, end);
++
++    ASSERT(start <= end);
++
++    elan3mmu_pte_range_unload(pr->pr_ctxt->Elan3mmu, vma->vm_mm, (caddr_t) start, end-start);
++}
++#endif /* defined(IOPROC_NO_VMA_RANGE) */
++
++static void
++user_ioproc_sync_page (void *arg, struct vm_area_struct *vma, unsigned long addr)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) arg;
++
++    PRINTF1 (pr->pr_ctxt, DBG_SEG, "user_ioproc_sync_page: addr=%lx\n", addr);
++
++    elan3mmu_pte_range_unload(pr->pr_ctxt->Elan3mmu, vma->vm_mm, (caddr_t) (addr & PAGE_MASK), PAGE_SIZE);
++}
++
++static void
++user_ioproc_invalidate_page (void *arg, struct vm_area_struct *vma, unsigned long addr)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) arg;
++    
++    PRINTF1 (pr->pr_ctxt, DBG_SEG, "user_ioproc_invalidate_page: addr=%lx\n", addr);
++
++    elan3mmu_pte_range_unload(pr->pr_ctxt->Elan3mmu, vma->vm_mm, (caddr_t) (addr & PAGE_MASK), PAGE_SIZE);
++}
++
++static void
++user_ioproc_update_page (void *arg, struct vm_area_struct *vma, unsigned long addr)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) arg;
++
++    PRINTF1 (pr->pr_ctxt, DBG_SEG, "user_ioproc_update_page: addr=%lx\n", addr);
++
++    elan3mmu_pte_range_update (pr->pr_ctxt->Elan3mmu,vma->vm_mm, (caddr_t) (addr & PAGE_MASK), PAGE_SIZE);
++}
++
++int
++user_ptrack_handler (void *arg, int phase, struct task_struct *child)
++{
++    USER_PRIVATE *pr   = (USER_PRIVATE *) arg;
++    ELAN3_CTXT    *ctxt = pr->pr_ctxt;
++
++    PRINTF5 (pr->pr_ctxt, DBG_FN, "user_ptrack_handler: ctxt=%p pr=%p ref=%d phase %d mm->ref %d\n", 
++           pr->pr_ctxt, pr, atomic_read (&pr->pr_ref), phase, atomic_read (&current->mm->mm_count));
++
++    if (phase == PTRACK_PHASE_EXIT)
++    {
++      /* this will force the helper thread to exit */
++      elan3_swapout (ctxt, CTXT_EXITING);
++      
++      if (atomic_dec_and_test (&pr->pr_ref))
++          user_free (pr);
++    }  
++    return PTRACK_FINISHED;
++}
++
++static int
++user_open (struct inode *inode, struct file *file)
++{
++    ELAN3_DEV     *dev = elan3_devices[ELAN3_DEVICE(inode)];
++    USER_PRIVATE *pr;
++    ELAN3_CTXT    *ctxt;
++
++    if (dev == NULL)
++      return (-ENXIO);
++
++    KMEM_ALLOC(pr, USER_PRIVATE *, sizeof (USER_PRIVATE), TRUE);
++
++    if (pr == NULL)
++      return (-ENOMEM);
++    
++    if ((ctxt = elan3_alloc (dev, 0)) == NULL)
++    {
++      KMEM_FREE (pr, sizeof (USER_PRIVATE));
++      return (-ENOMEM);
++    }
++
++    if (sys_init (ctxt) == NULL)
++    {
++      elan3_detach(ctxt);
++      elan3_free (ctxt);
++      KMEM_FREE (pr, sizeof (USER_PRIVATE));
++      return (-ENOMEM);
++    }
++
++    /* initialise refcnt to 3 - one for "file", one for XA handler, one for the ioproc ops */
++    atomic_set (&pr->pr_ref, 3);
++
++    atomic_set (&pr->pr_mappings, 0);
++    spin_lock_init (&pr->pr_lock);
++
++    pr->pr_ctxt = ctxt;
++    pr->pr_mm   = current->mm;
++
++    /* register an ptrack handler to force the helper thread to exit when we do */
++    if (ptrack_register (user_ptrack_handler, pr) < 0)
++    {
++      elan3_detach(ctxt);
++      elan3_free (ctxt);
++      KMEM_FREE (pr, sizeof (USER_PRIVATE));
++      return (-ENOMEM);
++    }
++
++    /* register a ioproc callback to notify us of translation changes */
++    
++    pr->pr_ioproc.arg               = (void *) pr;
++    pr->pr_ioproc.release           = user_ioproc_release;
++    pr->pr_ioproc.sync_range        = user_ioproc_sync_range;
++    pr->pr_ioproc.invalidate_range  = user_ioproc_invalidate_range;
++    pr->pr_ioproc.update_range      = user_ioproc_update_range;
++    pr->pr_ioproc.change_protection = user_ioproc_change_protection;
++    pr->pr_ioproc.sync_page         = user_ioproc_sync_page;
++    pr->pr_ioproc.invalidate_page   = user_ioproc_invalidate_page;
++    pr->pr_ioproc.update_page       = user_ioproc_update_page;
++    
++    spin_lock (&current->mm->page_table_lock);
++    ioproc_register_ops (current->mm, &pr->pr_ioproc);
++    spin_unlock (&current->mm->page_table_lock);
++
++    file->private_data = (void *) pr;
++
++    PRINTF2 (pr->pr_ctxt, DBG_FN, "user_open: done ctxt=%p pr=%p\n", ctxt, pr);
++
++    MOD_INC_USE_COUNT;
++    return (0);
++}
++
++static int
++user_release (struct inode *inode, struct file *file)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) file->private_data;
++    
++    PRINTF3 (pr->pr_ctxt, DBG_FN, "user_release: ctxt=%p pr=%p ref=%d\n", pr->pr_ctxt, pr,
++           atomic_read (&pr->pr_ref));
++
++    if (atomic_dec_and_test (&pr->pr_ref))
++      user_free (pr);
++
++    return (0);
++}
++
++static int
++user_ioctl (struct inode *inode, struct file *file, 
++          unsigned int cmd, unsigned long arg)
++{
++    USER_PRIVATE *pr   = (USER_PRIVATE *) file->private_data;
++    ELAN3_CTXT    *ctxt = pr->pr_ctxt;
++    SYS_CTXT     *sctx = (SYS_CTXT *) ctxt->Private;
++    int           res  = 0;
++
++    if (current->mm != pr->pr_mm)
++      return (-EINVAL);
++    
++    PRINTF4 (ctxt, DBG_FN, "user_ioctl: ctxt=%p cmd=%x(%d) arg=%lx\n", ctxt, cmd, _IOC_NR(cmd), arg);
++
++    switch (cmd)
++    {
++    case ELAN3IO_FREE:
++      if (atomic_read (&pr->pr_mappings) > 0)
++          return (-EINVAL);
++      
++      spin_lock (&current->mm->page_table_lock);
++      if (pr->pr_mm != current->mm)
++          spin_unlock (&current->mm->page_table_lock);
++      else
++      {
++          ioproc_unregister_ops (current->mm, &pr->pr_ioproc);
++          spin_unlock (&current->mm->page_table_lock);
++
++          user_ioproc_release (pr, current->mm);
++      }
++
++      if (ptrack_registered (user_ptrack_handler, pr))
++      {
++          ptrack_deregister (user_ptrack_handler, pr);
++          user_ptrack_handler (pr, PTRACK_PHASE_EXIT, NULL);
++      }
++      break;
++      
++    case ELAN3IO_ATTACH:
++    {
++      ELAN_CAPABILITY *cap;
++
++      KMEM_ALLOC(cap, ELAN_CAPABILITY *, sizeof (ELAN_CAPABILITY), TRUE);
++
++      if (cap == NULL)
++          return (set_errno (EFAULT));
++
++      if (copy_from_user (cap, (void *) arg, sizeof (ELAN_CAPABILITY)))
++          res = EFAULT;
++      else
++      {
++          if ((res = elan3_attach (ctxt, cap)) == 0)
++          {
++              if (copy_to_user ((void *) arg, cap, sizeof (ELAN_CAPABILITY)))
++              {
++                  elan3_detach (ctxt);
++                  res = EFAULT;
++              }
++          }
++      }
++      KMEM_FREE (cap, sizeof(ELAN_CAPABILITY));
++      break;
++    }
++    
++    case ELAN3IO_DETACH:
++      spin_lock (&pr->pr_lock);
++      if (atomic_read (&pr->pr_mappings) > 0)
++          res = EINVAL;
++      else
++          elan3_detach (ctxt);
++      spin_unlock (&pr->pr_lock);
++      break;
++
++    case ELAN3IO_ADDVP:
++    {
++      ELAN3IO_ADDVP_STRUCT *args;
++
++      KMEM_ALLOC(args, ELAN3IO_ADDVP_STRUCT *, sizeof (ELAN3IO_ADDVP_STRUCT), TRUE);
++
++      if (args == NULL)
++          return (set_errno (ENOMEM));
++      
++      if (copy_from_user (args, (void *) arg, sizeof (ELAN3IO_ADDVP_STRUCT)))
++          res = EFAULT;
++      else
++      {
++          if ( (res=elan3_addvp (ctxt, args->process, &args->capability)) != 0)
++              PRINTF0 (ctxt, DBG_FN, "ELAN3IO_ADDVP elan3_addvp failed \n");  
++      }
++
++      KMEM_FREE (args, sizeof (ELAN3IO_ADDVP_STRUCT));
++      break;
++    }
++
++    case ELAN3IO_REMOVEVP:
++      res = elan3_removevp (ctxt, arg);
++      break;
++      
++    case ELAN3IO_BCASTVP:
++    {
++      ELAN3IO_BCASTVP_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN3IO_BCASTVP_STRUCT)))
++          return (-EFAULT);
++      
++      res = elan3_addbcastvp (ctxt, args.process, args.lowvp, args.highvp);
++      break;
++    }
++
++    case ELAN3IO_LOAD_ROUTE:
++    {
++      ELAN3IO_LOAD_ROUTE_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN3IO_LOAD_ROUTE_STRUCT)))
++          return (-EFAULT);
++      
++      res = elan3_load_route (ctxt, args.process, args.flits);
++      break;
++    }
++
++    case ELAN3IO_CHECK_ROUTE:
++    {
++      ELAN3IO_CHECK_ROUTE_STRUCT args;
++
++      args.routeError = 0;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN3IO_LOAD_ROUTE_STRUCT)))
++          return (-EFAULT);
++      
++      if ((res = elan3_check_route (ctxt, args.process, args.flits, & args.routeError)) ==  ESUCCESS)
++      {
++          if (copy_to_user ( (void *) arg, &args,sizeof (ELAN3IO_LOAD_ROUTE_STRUCT)))
++              return (-EFAULT);
++      }
++      break;
++    }
++
++    case ELAN3IO_PROCESS_2_LOCATION:
++    {
++      ELAN3IO_PROCESS_2_LOCATION_STRUCT args;
++      ELAN_LOCATION                    loc;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN3IO_PROCESS_2_LOCATION_STRUCT)))
++          return (-EFAULT);
++
++      krwlock_write (&ctxt->VpLock);
++      loc = ProcessToLocation (ctxt, NULL, args.process , NULL);
++      krwlock_done (&ctxt->VpLock);
++
++      args.loc = loc;
++
++      if (copy_to_user ( (void *) arg, &args,sizeof (ELAN3IO_PROCESS_2_LOCATION_STRUCT)))
++          return (-EFAULT);
++
++      break;
++    }
++
++    case ELAN3IO_GET_ROUTE:
++    {
++      ELAN3IO_GET_ROUTE_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN3IO_GET_ROUTE_STRUCT)))
++          return (-EFAULT);
++      
++      if ((res = elan3_get_route (ctxt, args.process, args.flits)) ==  ESUCCESS)
++      {
++          if (copy_to_user ( (void *) arg, &args,sizeof (ELAN3IO_GET_ROUTE_STRUCT)))
++              return (-EFAULT);
++      }
++      break;
++    }
++
++    case ELAN3IO_RESET_ROUTE:
++    {
++      ELAN3IO_RESET_ROUTE_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN3IO_RESET_ROUTE_STRUCT)))
++          return (-EFAULT);
++      
++      res = elan3_reset_route (ctxt, args.process);
++      break;
++    }
++
++    case ELAN3IO_VP2NODEID:
++    {
++      ELAN3IO_VP2NODEID_STRUCT *vp2nodeId;
++      ELAN_LOCATION           location;
++
++      KMEM_ALLOC (vp2nodeId, ELAN3IO_VP2NODEID_STRUCT *, sizeof(ELAN3IO_VP2NODEID_STRUCT), TRUE);
++      if (vp2nodeId == NULL) 
++          return (set_errno (ENOMEM));
++
++      if (copy_from_user (vp2nodeId, (void *) arg, sizeof (ELAN3IO_VP2NODEID_STRUCT))) {
++          KMEM_FREE (vp2nodeId, sizeof(ELAN3IO_VP2NODEID_STRUCT));
++          return (-EFAULT);
++      }
++
++      krwlock_write (&ctxt->VpLock);
++      location = ProcessToLocation (ctxt, NULL, vp2nodeId->process , NULL);
++      krwlock_done (&ctxt->VpLock);
++
++      vp2nodeId->nodeId = location.loc_node;
++      if (copy_to_user ( (void *) arg, vp2nodeId, sizeof (ELAN3IO_VP2NODEID_STRUCT))) {
++          KMEM_FREE (vp2nodeId, sizeof(ELAN3IO_VP2NODEID_STRUCT));
++          return (-EFAULT);
++      }
++
++      KMEM_FREE (vp2nodeId, sizeof(ELAN3IO_VP2NODEID_STRUCT));
++
++      break;
++    }
++
++    case ELAN3IO_PROCESS:
++      return (elan3_process (ctxt));
++
++    case ELAN3IO_SETPERM:
++    {
++      ELAN3IO_SETPERM_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN3IO_SETPERM_STRUCT)))
++          return (-EFAULT);
++
++      res = elan3mmu_setperm (ctxt->Elan3mmu, args.maddr, args.eaddr, args.len, args.perm);
++      break;
++    }
++
++    case ELAN3IO_CLEARPERM:
++    {
++      ELAN3IO_CLEARPERM_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN3IO_CLEARPERM_STRUCT)))
++          return (-EFAULT);
++
++      elan3mmu_clrperm (ctxt->Elan3mmu, args.eaddr, args.len);
++      break;
++    }
++
++    case ELAN3IO_CHANGEPERM:
++    {
++      ELAN3IO_CHANGEPERM_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN3IO_CHANGEPERM_STRUCT)))
++          return (-EFAULT);
++
++      res = EINVAL;
++      break;
++    }
++
++    case ELAN3IO_HELPER_THREAD:
++      res = elan3_lwp (ctxt);
++      break;
++      
++    case ELAN3IO_WAITCOMMAND:
++      res = WaitForCommandPort (ctxt);
++      break;
++
++    case ELAN3IO_BLOCK_INPUTTER:
++      elan3_block_inputter (ctxt, arg);
++      break;
++
++    case ELAN3IO_SET_FLAGS:
++      sctx->Flags = arg;
++      break;
++
++    case ELAN3IO_SET_SIGNAL:
++      sctx->signal = arg;
++      break;
++
++    case ELAN3IO_WAITEVENT:
++      res = sys_waitevent (ctxt, (E3_Event *) arg);
++      break;
++
++    case ELAN3IO_ALLOC_EVENTCOOKIE:
++      res = cookie_alloc_cookie (sctx->Table, arg);
++      break;
++
++    case ELAN3IO_FREE_EVENTCOOKIE:
++      res = cookie_free_cookie (sctx->Table, arg);
++      break;
++
++    case ELAN3IO_ARM_EVENTCOOKIE:
++      res = cookie_arm_cookie (sctx->Table, arg);
++      break;
++
++    case ELAN3IO_WAIT_EVENTCOOKIE:
++      res = cookie_wait_cookie (sctx->Table, arg);
++      break;
++
++    case ELAN3IO_SWAPSPACE:
++      if (fuword (&((SYS_SWAP_SPACE *) arg)->Magic) != SYS_SWAP_MAGIC)
++          return (set_errno (EINVAL));
++      
++      ((SYS_CTXT *) ctxt->Private)->Swap = (SYS_SWAP_SPACE *) arg;
++      break;
++
++    case ELAN3IO_EXCEPTION_SPACE:
++      if (fuword (&((SYS_EXCEPTION_SPACE *) arg)->Magic) != SYS_EXCEPTION_MAGIC)
++          return (set_errno (EINVAL));
++
++      ((SYS_CTXT *) ctxt->Private)->Exceptions = (SYS_EXCEPTION_SPACE *) arg;
++      break;
++
++    case ELAN3IO_GET_EXCEPTION:
++    {
++      SYS_EXCEPTION *exception;
++
++      if (((SYS_CTXT *) ctxt->Private)->Exceptions == NULL)
++          return (set_errno (EINVAL));
++      
++      KMEM_ALLOC(exception, SYS_EXCEPTION *, sizeof (SYS_EXCEPTION), TRUE);
++
++      if (exception == NULL)
++          return (set_errno (ENOMEM));
++
++      if ((res = sys_getException (((SYS_CTXT *) ctxt->Private), exception)) == 0 &&
++          copy_to_user ((void *) arg, exception, sizeof (SYS_EXCEPTION)))
++          res = EFAULT;
++      
++      KMEM_FREE (exception, sizeof (SYS_EXCEPTION));
++      break;
++    }
++    
++    case ELAN3IO_UNLOAD:
++    {
++      ELAN3MMU             *elan3mmu = ctxt->Elan3mmu;
++      ELAN3IO_UNLOAD_STRUCT args;
++      int                   span;
++      unsigned long         flags;
++      E3_Addr               eaddr;
++      caddr_t               addr;
++      size_t                len;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN3IO_UNLOAD_STRUCT)))
++          return (-EFAULT);
++
++      addr = (caddr_t) args.addr;
++      len  = args.len;
++
++      if (((unsigned long) addr & PAGEMASK) || (len & PAGEMASK) || (len < 0))
++          return -EINVAL;
++
++      spin_lock_irqsave (&elan3mmu->elan3mmu_lock, flags);
++      for (; len; len -= span, addr += span)
++      {
++          ELAN3MMU_RGN *rgn = elan3mmu_findrgn_main (elan3mmu, addr, 0);
++          
++          if (rgn == NULL || (rgn->rgn_mbase + rgn->rgn_len) < addr)
++              span = len;
++          else if (rgn->rgn_mbase > addr)
++              span = MIN(len, rgn->rgn_mbase - addr);
++          else
++          {
++              span  = MIN(len, (rgn->rgn_mbase + rgn->rgn_len) - addr);
++              eaddr = rgn->rgn_ebase + (addr - rgn->rgn_mbase);
++              
++              elan3mmu_unload (elan3mmu, eaddr, span, PTE_UNLOAD);
++          }
++      }
++      spin_unlock_irqrestore (&elan3mmu->elan3mmu_lock, flags);
++      
++      return 0;
++    }
++
++    case ELAN3IO_GET_DEVINFO:
++    {
++      ELAN3IO_GET_DEVINFO_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN3IO_GET_DEVINFO_STRUCT)))
++          return (-EFAULT);
++      
++      if (copy_to_user ((void *) args.devinfo, &ctxt->Device->Devinfo, sizeof (ELAN_DEVINFO))) 
++          res = EFAULT;
++      break;
++    }
++
++    case ELAN3IO_GET_POSITION:
++    {
++      ELAN3IO_GET_POSITION_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN3IO_GET_POSITION_STRUCT)))
++          return (-EFAULT);   
++
++      if (copy_to_user ((void *) args.position, &ctxt->Device->Position, sizeof (ELAN_POSITION)))
++          res = EFAULT;
++      break;
++    }
++
++    default:
++      return (-EINVAL);
++    }
++
++    return (res ? set_errno (res) : 0);
++}
++
++static void user_vma_open(struct vm_area_struct *vma)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) vma->vm_private_data;
++
++    PRINTF (DBG_DEVICE, DBG_SEG, "user_vma_open: vm_mm=%p start=%lx end=%lx pgoff=%lx file=%p\n",
++          vma->vm_mm, vma->vm_start, vma->vm_end, vma->vm_pgoff, vma->vm_file);
++
++    if (vma->vm_pgoff == ELAN3IO_OFF_COMMAND_PAGE)
++      if (atomic_dec_and_test (&pr->pr_mappings))
++          pr->pr_ctxt->CommandPageMapping = NULL;
++}
++
++static void user_vma_close(struct vm_area_struct *vma)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) vma->vm_private_data;
++
++    PRINTF (DBG_DEVICE, DBG_SEG, "user_vma_close: vm_mm=%p start=%lx end=%lx pgoff=%lx file=%p\n",
++          vma->vm_mm, vma->vm_start, vma->vm_end, vma->vm_pgoff, vma->vm_file);
++
++    if (vma->vm_pgoff == ELAN3IO_OFF_COMMAND_PAGE)
++      atomic_inc (&pr->pr_mappings);
++}
++
++static struct vm_operations_struct user_vm_ops = {
++    open:             user_vma_open,
++    close:            user_vma_close,
++};
++
++static int
++user_mmap (struct file *file, struct vm_area_struct *vma)
++{
++    USER_PRIVATE  *pr   = (USER_PRIVATE *) file->private_data;
++    ELAN3_CTXT     *ctxt = pr->pr_ctxt; 
++    ioaddr_t       ioaddr;
++
++    /* 
++     * NOTE - since we need to maintain the reference count on
++     *        the user_private we only permit single page 
++     *        mmaps - this means that we will certainly see
++     *        the correct number of closes to maintain the
++     *        the reference count correctly.
++     */
++    
++    if ((vma->vm_end - vma->vm_start) != PAGE_SIZE)
++      return (-EINVAL);
++
++    PRINTF (DBG_DEVICE, DBG_SEG, "user_mmap: vm_mm=%p start=%lx end=%lx pgoff=%lx flags=%lx prot=%lx file=%p\n",
++          vma->vm_mm, vma->vm_start, vma->vm_end, vma->vm_pgoff, vma->vm_flags, vma->vm_page_prot.pgprot, vma->vm_file);
++
++    switch (vma->vm_pgoff)
++    {
++    default:
++      return (-EINVAL);
++      
++    case ELAN3IO_OFF_COMMAND_PAGE:
++      spin_lock (&pr->pr_lock);
++      if (ctxt->CommandPage == (ioaddr_t) 0 || atomic_read (&pr->pr_mappings) != 0)
++      {
++          PRINTF (DBG_DEVICE, DBG_SEG, "user_mmap: command port - %s\n", ctxt->CommandPort ? "already mapped" : "not attached");
++          spin_unlock (&pr->pr_lock);
++          return (-EINVAL);
++      }
++#ifdef LINUX_SPARC
++      pgprot_val(vma->vm_page_prot) &= ~(_PAGE_CACHE);
++      pgprot_val(vma->vm_page_prot) |= _PAGE_IE;
++#elif defined(pgprot_noncached)
++      vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
++#endif
++
++      PRINTF (DBG_DEVICE, DBG_SEG, "user_mmap: commandport at %lx phys %llx prot %lx\n", 
++              vma->vm_start, (unsigned long long) kmem_to_phys ((void *) ctxt->CommandPort), vma->vm_page_prot.pgprot);
++
++      /* Don't try to swap out physical pages.. */
++      vma->vm_flags |= VM_RESERVED;
++    
++      /*
++       * Don't dump addresses that are not real memory to a core file.
++       */
++      vma->vm_flags |= VM_IO;
++
++#ifdef NO_RMAP
++      if (remap_page_range (vma->vm_start, kmem_to_phys ((void *) ctxt->CommandPage), vma->vm_end - vma->vm_start, vma->vm_page_prot))
++#else 
++      if (remap_page_range (vma, vma->vm_start, kmem_to_phys ((void *) ctxt->CommandPage), vma->vm_end - vma->vm_start, vma->vm_page_prot))
++#endif
++      {
++          spin_unlock (&pr->pr_lock);
++          return (-EAGAIN);
++      }
++      ctxt->CommandPageMapping = (void *) vma->vm_start;
++      
++      atomic_inc (&pr->pr_mappings);
++      
++      spin_unlock (&pr->pr_lock);
++      break;
++
++    case ELAN3IO_OFF_UREG_PAGE:
++#ifdef LINUX_SPARC
++      pgprot_val(vma->vm_page_prot) &= ~(_PAGE_CACHE);
++      pgprot_val(vma->vm_page_prot) |= _PAGE_IE;
++#elif defined(pgprot_noncached)
++      vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
++#endif
++      ioaddr = ctxt->Device->RegPtr + (offsetof (E3_Regs, URegs) & PAGEMASK);
++
++      PRINTF (DBG_DEVICE, DBG_SEG, "user_mmap: user_regs at %lx phys %llx prot %lx\n", vma->vm_start, 
++              (unsigned long long) kmem_to_phys ((void *) ioaddr), vma->vm_page_prot.pgprot);
++
++      /* Don't try to swap out physical pages.. */
++      vma->vm_flags |= VM_RESERVED;
++    
++      /*
++       * Don't dump addresses that are not real memory to a core file.
++       */
++      vma->vm_flags |= VM_IO;
++
++#ifdef NO_RMAP
++      if (remap_page_range (vma->vm_start, kmem_to_phys ((void *) ioaddr),
++#else
++      if (remap_page_range (vma, vma->vm_start, kmem_to_phys ((void *) ioaddr),
++#endif
++                            vma->vm_end - vma->vm_start, vma->vm_page_prot))
++          return (-EAGAIN);
++      break;
++      
++    case ELAN3IO_OFF_FLAG_PAGE:
++      PRINTF (DBG_DEVICE, DBG_SEG, "user_mmap: flag page at %lx phys %llx\n", vma->vm_start, 
++              (unsigned long long) kmem_to_phys ((void *) ctxt->FlagPage));
++
++      /* we do not want to have this area swapped out, lock it */
++      vma->vm_flags |= VM_LOCKED;
++
++      /* Mark the page as reserved or else the remap_page_range() doesn't remap it */
++      SetPageReserved(pte_page(*find_pte_kernel((unsigned long) ctxt->FlagPage)));
++      
++#ifdef NO_RMAP
++      if (remap_page_range (vma->vm_start, kmem_to_phys ((void *) ctxt->FlagPage),
++#else
++      if (remap_page_range (vma, vma->vm_start, kmem_to_phys ((void *) ctxt->FlagPage),
++#endif
++                            vma->vm_end - vma->vm_start, vma->vm_page_prot))
++          return (-EAGAIN);
++      break;
++    }
++
++    ASSERT (vma->vm_ops == NULL);
++    
++    vma->vm_ops          = &user_vm_ops;
++    vma->vm_file         = file;
++    vma->vm_private_data = (void *) pr;
++    
++    return (0);
++}
++
++/* driver entry points */
++static int
++elan3_open (struct inode *inode, struct file *file)
++{
++    if (elan3_devices[ELAN3_DEVICE(inode)] == NULL)
++      return (-ENXIO);
++
++    PRINTF (DBG_DEVICE, DBG_FN, "elan3_open: device %d minor %d file=%p\n", ELAN3_DEVICE(inode), ELAN3_MINOR(inode), file);
++    
++    switch (ELAN3_MINOR (inode))
++    {
++    case ELAN3_MINOR_CONTROL:
++      return (control_open (inode, file));
++    case ELAN3_MINOR_MEM:
++      return (mem_open (inode, file));
++    case ELAN3_MINOR_USER:
++      return (user_open (inode, file));
++    default:
++      return (-ENXIO);
++    }
++}
++
++static int
++elan3_release (struct inode *inode, struct file *file)
++{
++    PRINTF (DBG_DEVICE, DBG_FN, "elan3_release: device %d minor %d file=%p\n", ELAN3_DEVICE(inode), ELAN3_MINOR(inode), file);
++    
++    switch (ELAN3_MINOR (inode))
++    {
++    case ELAN3_MINOR_CONTROL:
++      return (control_release (inode, file));
++    case ELAN3_MINOR_MEM:
++      return (mem_release (inode, file));
++    case ELAN3_MINOR_USER:
++      return (user_release (inode, file));
++    default:
++      return (-ENXIO);
++    }
++}
++
++static int
++elan3_ioctl (struct inode *inode, struct file *file, 
++           unsigned int cmd, unsigned long arg)
++{
++    switch (ELAN3_MINOR (inode))
++    {
++    case ELAN3_MINOR_CONTROL:
++      return (control_ioctl (inode, file, cmd, arg));
++    case ELAN3_MINOR_MEM:
++      return (mem_ioctl (inode, file, cmd, arg));
++    case ELAN3_MINOR_USER:
++      return (user_ioctl (inode, file, cmd, arg));
++    default:
++      return (-ENXIO);
++    }
++}
++
++
++static int
++elan3_mmap (struct file *file, struct vm_area_struct *vma)
++{
++    PRINTF (DBG_DEVICE, DBG_SEG, "elan3_mmap: instance %d minor %d start=%lx end=%lx pgoff=%lx flags=%lx prot=%lx\n", 
++          ELAN3_DEVICE (file->f_dentry->d_inode), ELAN3_MINOR (file->f_dentry->d_inode),
++          vma->vm_start, vma->vm_end, vma->vm_pgoff, vma->vm_flags, vma->vm_page_prot.pgprot);
++
++    switch (ELAN3_MINOR (file->f_dentry->d_inode))
++    {
++    case ELAN3_MINOR_CONTROL:
++      return (control_mmap (file, vma));
++    case ELAN3_MINOR_MEM:
++      return (mem_mmap (file, vma));
++    case ELAN3_MINOR_USER:
++      return (user_mmap (file, vma));
++    default:
++      return (-ENXIO);
++    }
++}
++
++static irqreturn_t
++InterruptHandlerWrapper(int irq, void *dev_id, struct pt_regs *regs)
++{
++    if (InterruptHandler ((ELAN3_DEV *)dev_id) == 0)
++      return IRQ_HANDLED;
++    else
++      return IRQ_NONE;
++}
++
++
++/* 
++ * Elan specific PCI configuration registers.
++ */
++
++#define PCI_CONF_PARITY_PHYS_LO         0x40
++#define PCI_CONF_PARITY_PHYS_HI         0x44
++#define PCI_CONF_PARITY_PHASE_ADDR      0x46
++#define PCI_CONF_PARITY_MASTER_TYPE     0x47
++#define PCI_CONF_ELAN3_CTRL              0x48
++ 
++#define ECTRL_EXTEND_LATENCY            (1 << 0)
++#define ECTRL_ENABLE_PREFETCH           (1 << 1)
++#define ECTRL_SOFTWARE_INTERNAL_RESET   (1 << 2)
++#define ECTRL_REDUCED_RETRY_RATE        (1 << 3)
++#define ECTRL_CLOCK_DIVIDE_RATE_SHIFT   4
++#define ECTRL_COMMS_DIVIDE_RATE_SHIFT   10
++#define ECTRL_FORCE_COMMSCLK_LOCAL      (1 << 14)
++
++/*
++ * Configure PCI.
++ */
++static int
++ConfigurePci(ELAN3_DEV *dev)
++{
++      struct pci_dev *pci = dev->Osdep.pci;
++      u32 rom_address;
++
++      if (pci_enable_device(pci))
++          return (ENXIO);
++
++      /* disable ROM */
++      pci_read_config_dword(pci, PCI_ROM_ADDRESS, &rom_address);
++      rom_address &= ~PCI_ROM_ADDRESS_ENABLE;
++      pci_write_config_dword(pci, PCI_ROM_ADDRESS, rom_address);
++      mb();
++
++      /* this is in 32-bit WORDS */
++      pci_write_config_byte(pci, PCI_CACHE_LINE_SIZE, (64 >> 2));
++      mb();
++
++      /* allow 40 ticks to respond, 16 data phases */
++      pci_write_config_byte(pci, PCI_LATENCY_TIMER, 255);
++      mb();
++
++      /* don't enable PCI_COMMAND_SERR--see note in elandev_dunix.c */
++      pci_write_config_word(pci, PCI_COMMAND, PCI_COMMAND_MEMORY 
++          | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE | PCI_COMMAND_PARITY);
++      mb();
++
++      return ESUCCESS;
++}
++
++/* 
++ * Reset chip to a known state.
++ */
++static int
++ResetElan(ELAN3_DEV *dev, ioaddr_t intPalAddr)
++{
++      struct pci_dev *pci = dev->Osdep.pci;
++      int instance = dev->Instance;
++      u32 val;
++      u8 revid;
++      int CasLatency;
++      int res;
++
++      /* determine rev of board */
++      pci_read_config_byte(pci, PCI_REVISION_ID, &revid);
++
++      /* GNAT 2328 - don't set ECTRL_ENABLE_PREFETCH on Elan rev A */
++      val = ECTRL_EXTEND_LATENCY | (39 << ECTRL_CLOCK_DIVIDE_RATE_SHIFT)
++          | (6 << ECTRL_COMMS_DIVIDE_RATE_SHIFT);
++      switch (revid) 
++      {
++              case PCI_REVISION_ID_ELAN3_REVA:
++                      printk("elan%d: is an elan3 (revision a) - not supported\n", instance);
++                      return (EFAIL);
++
++              case PCI_REVISION_ID_ELAN3_REVB:        
++                      val |= ECTRL_ENABLE_PREFETCH;
++                      if (BackToBackMaster)
++                              val |= ECTRL_FORCE_COMMSCLK_LOCAL;
++                      printk("elan%d: is an elan3 (revision b)\n", instance);
++                      break;
++              default:
++                      printk("elan%d: unsupported elan3 revision %d\n", 
++                          instance, revid);
++                      return EFAIL;
++      }
++      pci_write_config_dword(pci, PCI_CONF_ELAN3_CTRL, val);
++      mb();
++
++      /*
++       * GNAT: 2474
++       * Hit reset on the Elan, then we MUST initialise the schedule status
++       * register to drive reset on the link before the link can come out
++       * of reset (15 uS). We need to keep it like this until we've 
++       * initialised SDRAM
++       */
++      pci_read_config_dword(pci, PCI_CONF_ELAN3_CTRL, &val);
++      pci_write_config_dword(pci, PCI_CONF_ELAN3_CTRL, 
++          val | ECTRL_SOFTWARE_INTERNAL_RESET);
++      mb();
++
++      /* Read the Vital Product Data to determine the cas latency */
++      if ((res = ReadVitalProductData (dev, &CasLatency)) != ESUCCESS)
++          return (res);
++
++      /*
++       * Now clear the Software internal reset bit, and start the sdram
++       */
++      pci_write_config_dword(pci, PCI_CONF_ELAN3_CTRL, val);
++      mb();
++
++      /* 
++       * Enable SDRAM before sizing and initalising it for ECC.
++       * NOTE: don't enable all sets of the cache (yet), nor ECC 
++       */
++      dev->Cache_Control_Reg = (CasLatency | REFRESH_RATE_16US);
++
++      write_reg32 (dev, Cache_Control_Reg.ContReg, (dev->Cache_Control_Reg | SETUP_SDRAM));
++      mb();
++
++      INIT_SCHED_STATUS(dev, Sched_Initial_Value);
++
++      /*
++       * Set the interrupt mask to 0 and enable the interrupt PAL
++       * by writing any value to it.
++       */
++      SET_INT_MASK (dev, 0);
++      writeb (0, intPalAddr);
++ 
++      return ESUCCESS;
++}
++
++/*
++ * Determine the size of elan PCI address spaces.  EFAIL is returned if 
++ * unused or invalid BAR is specified, or if board reports I/O mapped space.
++ */
++int
++DeviceRegisterSize(ELAN3_DEV *dev, int rnumber, int *sizep)
++{
++      struct pci_dev *pdev = dev->Osdep.pci;
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
++      *sizep = pci_resource_size(pdev, rnumber);
++#else
++      *sizep = pci_resource_end(pdev, rnumber) - pci_resource_start(pdev, rnumber) + 1;
++#endif
++      return ESUCCESS;
++}
++
++/*
++ * Map PCI memory into kernel virtual address space.  On the alpha, 
++ * we just return appropriate kseg address, and Unmap is a no-op.
++ */
++int
++MapDeviceRegister(ELAN3_DEV *dev, int rnumber, ioaddr_t *addrp,
++                int off, int len, DeviceMappingHandle *handlep)
++{     
++      struct pci_dev *pdev = dev->Osdep.pci;
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
++      u64 base = pci_get_base_address(pdev, rnumber);
++      *addrp = (ioaddr_t) pci_base_to_kseg(base + off, pdev->bus->number);
++
++#else
++      if (len == 0)
++          len = pci_resource_end(pdev, rnumber) - pci_resource_start(pdev, rnumber) + 1;
++      
++      if (len == 0)
++          return (EINVAL);
++
++      *addrp = (ioaddr_t) ioremap_nocache (pci_resource_start(pdev, rnumber) + off, len);
++#endif
++
++      *handlep = (void *) *addrp;
++
++      return (*addrp ? ESUCCESS : ENOMEM);
++}
++void
++UnmapDeviceRegister(ELAN3_DEV *dev, DeviceMappingHandle *handlep)
++{
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
++    iounmap (*handlep);
++#endif
++}
++
++void
++ElanBusError (ELAN3_DEV *dev)
++{
++      struct pci_dev  *pci = dev->Osdep.pci;  
++      u8  phaseaddr, type;
++      u16 status, cmd, physhi;
++      u32 physlo;
++ 
++      printk("elan%d: bus error occured\n", dev->Instance);
++
++      pci_read_config_word (pci, PCI_STATUS,                  &status);
++      pci_read_config_word (pci, PCI_COMMAND,                 &cmd);
++      pci_read_config_dword(pci, PCI_CONF_PARITY_PHYS_LO,     &physlo);
++      pci_read_config_word (pci, PCI_CONF_PARITY_PHYS_HI,     &physhi);
++      pci_read_config_byte (pci, PCI_CONF_PARITY_PHASE_ADDR,  &phaseaddr); 
++      pci_read_config_byte (pci, PCI_CONF_PARITY_MASTER_TYPE, &type);
++
++#define PCI_CONF_STAT_FORMAT  "\20" \
++      "\6SIXTY_SIX_MHZ\7UDF\10FAST_BACK\11PARITY" \
++      "\14SIG_TARGET_ABORT\15REC_TARGET_ABORT\16REC_MASTER_ABORT" \
++      "\17SIG_SYSTEM_ERROR\20DETECTED_PARITY"
++
++      printk ("elan%d: status %x cmd %4x physaddr %04x%08x phase %x type %x\n",
++              dev->Instance, status, cmd, physhi, physlo, phaseaddr, type);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan3/elansyscall.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan3/elansyscall.c     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan3/elansyscall.c  2005-05-11 12:10:12.416935920 -0400
+@@ -0,0 +1,1230 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: elansyscall.c,v 1.99.2.1 2004/10/28 17:08:56 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/os/elansyscall.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <qsnet/autoconf.h>
++
++#include <elan/elanmod.h>
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elanvp.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanctxt.h>
++#include <elan3/elandebug.h>
++#include <elan3/elansyscall.h>
++#include <elan/devinfo.h>
++
++static int       sys_exception (ELAN3_CTXT *ctxt, int type, int proc, void *trap, va_list ap);
++static int       sys_getWordItem (ELAN3_CTXT *ctxt, int list, void **itemp, E3_uint32 *valuep);
++static int       sys_getBlockItem (ELAN3_CTXT *ctxt, int list, void **itemp, E3_Addr *valuep);
++static void      sys_putWordItem (ELAN3_CTXT *ctxt, int list, E3_uint32 value);
++static void      sys_putBlockItem (ELAN3_CTXT *ctxt, int list, E3_uint32 *ptr);
++static void      sys_putbackItem (ELAN3_CTXT *ctxt, int list, void *item);
++static void      sys_freeWordItem (ELAN3_CTXT *ctxt, void *item);
++static void      sys_freeBlockItem (ELAN3_CTXT *ctxt, void *item);
++static int       sys_countItems (ELAN3_CTXT *ctxt, int list);
++static int       sys_event (ELAN3_CTXT *ctxt, E3_uint32 cookie, int flag);
++static void      sys_swapin (ELAN3_CTXT *ctxt);
++static void      sys_swapout (ELAN3_CTXT *ctxt);
++static void      sys_freePrivate (ELAN3_CTXT *ctxt);
++static int       sys_fixupNetworkError (ELAN3_CTXT *ctxt, NETERR_FIXUP *nef);
++static int       sys_startFaultCheck (ELAN3_CTXT *ctxt);
++static void      sys_endFaultCheck (ELAN3_CTXT *ctxt);
++static E3_uint8  sys_load8 (ELAN3_CTXT *ctxt, E3_Addr addr);
++static void      sys_store8 (ELAN3_CTXT *ctxt, E3_Addr addr, E3_uint8 val);
++static E3_uint16 sys_load16 (ELAN3_CTXT *ctxt, E3_Addr addr);
++static void      sys_store16 (ELAN3_CTXT *ctxt, E3_Addr addr, E3_uint16 val);
++static E3_uint32 sys_load32 (ELAN3_CTXT *ctxt, E3_Addr addr);
++static void      sys_store32 (ELAN3_CTXT *ctxt, E3_Addr addr, E3_uint32 val);
++static E3_uint64 sys_load64 (ELAN3_CTXT *ctxt, E3_Addr addr);
++static void      sys_store64 (ELAN3_CTXT *ctxt, E3_Addr addr, E3_uint64 val);
++
++static ELAN3_OPS elan3_sys_ops = {
++    ELAN3_OPS_VERSION,                /* Version */
++
++    sys_exception,            /* Exception */
++    sys_getWordItem,          /* GetWordItem */
++    sys_getBlockItem,         /* GetBlockItem */
++    sys_putWordItem,          /* PutWordItem */
++    sys_putBlockItem,         /* PutBlockItem */
++    sys_putbackItem,          /* PutbackItem */
++    sys_freeWordItem,         /* FreeWordItem */
++    sys_freeBlockItem,                /* FreeBlockItem */
++    sys_countItems,           /* CountItems */
++    sys_event,                        /* Event */
++    sys_swapin,                       /* Swapin */
++    sys_swapout,              /* Swapout */
++    sys_freePrivate,          /* FreePrivate */
++    sys_fixupNetworkError,    /* FixupNetworkError */
++    NULL,                     /* DProcTrap */
++    NULL,                     /* TProcTrap */
++    NULL,                     /* IProcTrap */
++    NULL,                     /* CProcTrap */
++    NULL,                     /* CProcReissue */
++    sys_startFaultCheck,      /* StartFaultCheck */
++    sys_endFaultCheck,          /* EndFaultCheck */
++    sys_load8,                        /* Load8 */
++    sys_store8,                       /* Store8 */
++    sys_load16,                       /* Load16 */
++    sys_store16,              /* Store16 */
++    sys_load32,                       /* Load32 */
++    sys_store32,              /* Store32 */
++    sys_load64,                       /* Load64 */
++    sys_store64                       /* Store64 */
++};
++
++va_list null_valist;
++
++SYS_CTXT *
++sys_init (ELAN3_CTXT *ctxt)
++{
++    SYS_CTXT *sctx;
++
++    /* Allocate and initialise the context private data */
++    KMEM_ZALLOC (sctx, SYS_CTXT *, sizeof  (SYS_CTXT), TRUE);
++
++    if (sctx == NULL)
++      return ((SYS_CTXT *) NULL);
++
++    sctx->Swap    = NULL;
++    sctx->Armed   = 0;
++    sctx->Backoff = 1;
++    sctx->Table   = cookie_alloc_table ((unsigned long) ELAN3_MY_TASK_HANDLE(), 0);
++    sctx->signal  = SIGSEGV;
++
++    if (sctx->Table == NULL)
++    {
++      KMEM_FREE (sctx, sizeof (SYS_CTXT));
++      return ((SYS_CTXT *) NULL);
++    }
++
++    kmutex_init  (&sctx->Lock);
++    spin_lock_init (&sctx->WaitLock);
++    kcondvar_init (&sctx->NetworkErrorWait);
++    
++    /* Install my context operations and private data */
++    ctxt->Operations = &elan3_sys_ops;
++    ctxt->Private    = (void *) sctx;
++    
++    return (sctx);
++}
++
++/* returns -ve on error or ELAN_CAP_OK or ELAN_CAP_RMS */
++/* use = ELAN_USER_ATTACH, ELAN_USER_P2P, ELAN_USER_BROADCAST */
++int 
++elan3_validate_cap(ELAN3_DEV *dev, ELAN_CAPABILITY *cap ,int use)
++{
++     /* Don't allow a user process to attach to system context */
++    if (ELAN3_SYSTEM_CONTEXT (cap->cap_lowcontext) || ELAN3_SYSTEM_CONTEXT (cap->cap_highcontext)
++      || cap->cap_highcontext <= ELAN_USER_BASE_CONTEXT_NUM  || cap->cap_highcontext <= ELAN_USER_BASE_CONTEXT_NUM)
++    {
++      PRINTF2 (DBG_DEVICE, DBG_VP,"elan3_validate_cap: lctx %x hctx %x \n",cap->cap_lowcontext,  cap->cap_highcontext);
++      PRINTF3 (DBG_DEVICE, DBG_VP,"elan3_validate_cap: bit %x  low %x high %x\n", ((cap->cap_lowcontext) & SYS_CONTEXT_BIT),
++               E3_NUM_CONTEXT_0, ELAN3_KCOMM_BASE_CONTEXT_NUM);
++
++
++      PRINTF0 (DBG_DEVICE, DBG_VP,"elan3_validate_cap: user process cant attach to system cap\n");
++      return (-EINVAL);
++    }
++ 
++    if (cap->cap_type & ELAN_CAP_TYPE_HWTEST)
++    {
++      if (!(cap->cap_type & ELAN_CAP_TYPE_NO_BITMAP)) /* cant have a bit map */
++      {
++          PRINTF0 (DBG_DEVICE, DBG_VP, "elanmod_classify_cap: ELAN_CAP_TYPE_HWTEST must have ELAN_CAP_TYPE_NO_BITMAP\n");
++          return (-EINVAL);
++      }
++      
++      if (cap->cap_lowcontext != cap->cap_highcontext) 
++      {
++          PRINTF2 (DBG_DEVICE, DBG_VP, "elanmod_classify_cap: ELAN_CAP_TYPE_HWTEST (cap->cap_lowcontext != cap->cap_highcontext) %d %d\n",cap->cap_lowcontext , cap->cap_highcontext) ;
++          return (-EINVAL);
++      }
++      
++      if ( ! (ELAN3_HWTEST_CONTEXT(cap->cap_lowcontext) && ELAN3_HWTEST_CONTEXT(cap->cap_highcontext)))
++      {
++          PRINTF3 (DBG_DEVICE, DBG_VP, "elanmod_classify_cap: ELAN_CAP_TYPE_HWTEST HWTEST_BASE_CONTEXT %d %d %d \n" , ELAN3_HWTEST_BASE_CONTEXT_NUM,cap->cap_lowcontext ,ELAN3_HWTEST_TOP_CONTEXT_NUM);
++          return (-EINVAL);
++      }
++      
++      if (cap->cap_lownode != ELAN_CAP_UNINITIALISED || cap->cap_highnode != ELAN_CAP_UNINITIALISED)
++      {
++          PRINTF0 (DBG_DEVICE, DBG_VP, "elanmod_classify_cap: ELAN_CAP_TYPE_HWTEST nodes != ELAN_CAP_UNINITIALISED\n");
++          return (-EINVAL);
++      }
++
++      return ELAN_CAP_OK;
++    }
++
++    return elanmod_classify_cap(&dev->Position, cap, use);
++}
++
++int
++sys_waitevent (ELAN3_CTXT *ctxt, E3_Event *event)
++{
++    SYS_CTXT    *sctx = (SYS_CTXT *) ctxt->Private;
++    EVENT_COOKIE cookie;
++
++    if (ctxt->Device->Devinfo.dev_revision_id == PCI_REVISION_ID_ELAN3_REVA)
++      return (EINVAL);
++
++    cookie = fuword ((int *) &event->ev_Type) & ~(EV_TYPE_MASK_EVIRQ | EV_TYPE_MASK_BCOPY);
++
++    if (cookie_alloc_cookie (sctx->Table, cookie) != ESUCCESS)
++      return (EINVAL);
++
++    cookie_arm_cookie (sctx->Table, cookie);
++
++    if (fuword ((int *) &event->ev_Count) > 0)
++      cookie_wait_cookie (sctx->Table, cookie);
++    
++    cookie_free_cookie (sctx->Table, cookie);
++    
++    return (ESUCCESS);
++}
++
++static void *
++sys_getItem (SYS_SWAP_SPACE *sp, int list)
++{
++    void *itemp = (void *) fuptr_noerr ((void **) &sp->ItemListsHead[list]);
++    void *next;
++    
++    PRINTF4 (DBG_DEVICE, DBG_SYSCALL, "sys_getItem: sp=%p list=%d head=%p itemp=%p\n",
++           sp, list, &sp->ItemListsHead[list], itemp);
++    
++    if (itemp == NULL)
++      return (NULL);
++
++    next = (void *) fuptr_noerr ((void *) itemp);
++
++    suptr_noerr ((void *) &sp->ItemListsHead[list], (void *) next);
++    if (next == NULL)
++      suptr_noerr ((void *) &sp->ItemListsTailp[list], (void *)&sp->ItemListsHead[list]);
++    return (itemp);
++}
++
++static void
++sys_putItemBack (SYS_SWAP_SPACE *sp, int list, void *itemp)
++{
++    PRINTF4 (DBG_DEVICE, DBG_SYSCALL, "sys_putItemBack: sp=%p list=%d itemp=%p value=%08x\n",
++           sp, list, itemp, fuword_noerr ((int *) &((SYS_WORD_ITEM *) itemp)->Value));
++
++    suptr_noerr ((void **) itemp, NULL);                                                      /* item->Next = NULL */
++    suptr_noerr ((void **) fuptr_noerr ((void **) &sp->ItemListsTailp[list]), (void *)itemp);         /* *Tailp = item */
++    suptr_noerr ((void **) &sp->ItemListsTailp[list], (void *) itemp);                                /* Tailp = &item->Next */
++}
++
++static void
++sys_putItemFront (SYS_SWAP_SPACE *sp, int list, void *itemp)
++{
++    PRINTF4 (DBG_DEVICE, DBG_SYSCALL, "sys_putItemFront: sp=%p list=%d itemp=%p value=%08x\n",
++           sp, list, itemp, fuword_noerr ((int *) &((SYS_WORD_ITEM *) itemp)->Value));
++
++    suptr_noerr ((void **) itemp, fuptr_noerr ((void **) &sp->ItemListsHead[list]));          /* item->Next = Head */
++    suptr_noerr ((void **) &sp->ItemListsHead[list], (void *) itemp);                         /* Head = item */
++
++    if (fuptr_noerr ((void **) &sp->ItemListsTailp[list]) == (void *) &sp->ItemListsHead[list])       /* if (Tailp == &Head) */
++      suptr_noerr ((void **) &sp->ItemListsTailp[list], (void *) itemp);                      /*    Tailp = &Item->Next */
++}
++
++
++static int
++sys_getWordItem (ELAN3_CTXT *ctxt, int list, void **itemp, E3_uint32 *valuep)
++{
++    SYS_CTXT     *sctx = (SYS_CTXT *) ctxt->Private;
++    SYS_SWAP_SPACE *sp   = sctx->Swap;
++    SYS_WORD_ITEM  *item;
++    int                   res;
++    label_t       ljb;
++
++    kmutex_lock (&sctx->Lock);
++    
++    if (on_fault (&ljb))
++    {
++      no_fault();
++      kmutex_unlock (&sctx->Lock);
++      sys_exception (ctxt, EXCEPTION_SWAP_FAULT, list, (void *) NULL, null_valist);
++      return (0);
++    }
++
++    item = (SYS_WORD_ITEM *) sys_getItem (sp, list);
++
++    if (item == NULL)
++      res = 0;
++    else
++    {
++      if (list == LIST_DMA_PTR)
++          sctx->Armed = TRUE;
++
++      *itemp  = (void *) item;
++      *valuep = (E3_Addr) fuword_noerr ((E3_int32 *) &item->Value);
++
++      PRINTF3 (ctxt, DBG_SYSCALL, "sys_getWordItem: list=%d -> item=%p value=%08x\n", list, *itemp, *valuep);
++
++      res = 1;
++    }
++    
++    no_fault();
++    kmutex_unlock (&sctx->Lock);
++
++    return (res);
++}
++
++static int
++sys_getBlockItem (ELAN3_CTXT *ctxt, int list, void **itemp, E3_Addr *valuep)
++{
++    SYS_CTXT     *sctx = (SYS_CTXT *) ctxt->Private;
++    SYS_SWAP_SPACE *sp   = sctx->Swap;
++    SYS_BLOCK_ITEM *item;
++    int                   res;
++    label_t       ljb;
++
++    kmutex_lock (&sctx->Lock);
++    
++    if (on_fault (&ljb))
++    {
++      no_fault();
++      kmutex_unlock (&sctx->Lock);
++      sys_exception (ctxt, EXCEPTION_SWAP_FAULT, list, (void *) NULL, null_valist);
++      return (0);
++    }
++
++    item = sys_getItem (sp, list);
++
++    if (item == NULL)
++      res = 0;
++    else
++    {
++      E3_uint32 *dest = fuptr_noerr ((void **) &item->Pointer);
++
++      if (list == LIST_DMA_DESC)
++          sctx->Armed = TRUE;
++
++      *itemp  = (void *) item;
++      *valuep = elan3mmu_elanaddr (ctxt->Elan3mmu, (caddr_t) dest);
++
++      PRINTF3 (ctxt, DBG_SYSCALL, "sys_getBlockItem: list=%d -> item=%p addr=%08x\n", list, *itemp, *valuep);
++      PRINTF4 (ctxt, DBG_SYSCALL, "                  %08x %08x %08x %08x\n",
++               fuword_noerr ((int *) &dest[0]), fuword_noerr ((int *) &dest[1]), 
++               fuword_noerr ((int *) &dest[2]), fuword_noerr ((int *) &dest[3]));
++      PRINTF4 (ctxt, DBG_SYSCALL, "                  %08x %08x %08x %08x\n",
++               fuword_noerr ((int *) &dest[4]), fuword_noerr ((int *) &dest[5]),
++               fuword_noerr ((int *) &dest[6]), fuword_noerr ((int *) &dest[7]));
++
++      
++      res = 1;
++    }
++    
++    no_fault();
++    kmutex_unlock (&sctx->Lock);
++
++    return (res);
++}
++
++static void
++sys_putWordItem (ELAN3_CTXT *ctxt, int list, E3_Addr value)
++{
++    SYS_CTXT     *sctx = (SYS_CTXT *) ctxt->Private;
++    SYS_SWAP_SPACE *sp   = sctx->Swap;
++    SYS_WORD_ITEM  *item;
++    label_t       ljp;
++
++    kmutex_lock (&sctx->Lock);
++
++    PRINTF2 (ctxt,DBG_SYSCALL, "sys_putWordItem: list=%x value=%x\n", list, value);
++
++    if (on_fault (&ljp))
++    {
++      no_fault();
++      kmutex_unlock (&sctx->Lock);
++      
++      sys_exception (ctxt, EXCEPTION_SWAP_FAULT, list, (void *) NULL, null_valist);
++      return;
++    }
++
++    item = sys_getItem (sp, LIST_FREE_WORD);
++
++    PRINTF1 (ctxt, DBG_SYSCALL, "sys_putWordItem: item=%p\n", item);
++
++    if (item == NULL)
++    {
++      no_fault();
++      kmutex_unlock (&sctx->Lock);
++      
++      sys_exception (ctxt, EXCEPTION_SWAP_FAILED, list, (void *) NULL, null_valist);
++      return;
++    }
++    
++    PRINTF2 (ctxt, DBG_SYSCALL, "sys_putWordItem: storing value=%08x at %p\n", value, &item->Value);
++
++    PRINTF2 (ctxt, DBG_SYSCALL, "sys_putWordItem: item=%p value=%08x\n", item, value);
++
++    suword_noerr ((E3_int32 *) &item->Value, value);                                          /* write "value" into item */
++
++    sys_putItemBack (sp, list, item);
++
++    no_fault();
++    kmutex_unlock (&sctx->Lock);
++}
++
++static void
++sys_putBlockItem (ELAN3_CTXT *ctxt, int list, E3_uint32 *ptr)
++{
++    SYS_CTXT     *sctx = (SYS_CTXT *) ctxt->Private;
++    SYS_SWAP_SPACE *sp   = sctx->Swap;
++    SYS_BLOCK_ITEM *item;
++    label_t       ljp;
++    E3_uint32      *source;
++    E3_uint32      *dest;
++
++    PRINTF2 (ctxt, DBG_SYSCALL, "sys_putBlockItem: list=%x ptr=%p\n", list, ptr);
++
++    kmutex_lock (&sctx->Lock);
++    
++    if (on_fault (&ljp))
++    {
++      no_fault();
++      kmutex_unlock (&sctx->Lock);
++      
++      sys_exception (ctxt, EXCEPTION_SWAP_FAULT, list, (void *) NULL, null_valist);
++      return;
++    }
++
++    item = sys_getItem (sp, LIST_FREE_BLOCK);                 /* get an item from the freelist. */
++
++    if (item == NULL)
++    {
++      no_fault();
++      kmutex_unlock (&sctx->Lock);
++      
++      sys_exception (ctxt, EXCEPTION_SWAP_FAILED, list, (void *) NULL, null_valist);
++      return;
++    }
++
++    /*
++     * The block will have been read using 64 bit reads,  since we have
++     * to write it to user memory using 32 bit writes, we need to perform
++     * an endian swap on the Ultrasparc.
++     */
++    dest   = (E3_uint32 *) fuptr_noerr ((void **) &item->Pointer);
++    source = (E3_uint32 *) ptr;
++
++    PRINTF2 (ctxt, DBG_SYSCALL, "sys_putBlockItem: item=%p dest=%p\n",item, dest);
++    PRINTF4 (ctxt, DBG_SYSCALL, "                  %08x %08x %08x %08x\n",
++          source[0^WordEndianFlip], source[1^WordEndianFlip], source[2^WordEndianFlip], source[3^WordEndianFlip]);
++    PRINTF4 (ctxt, DBG_SYSCALL, "                  %08x %08x %08x %08x\n",
++           source[4^WordEndianFlip], source[5^WordEndianFlip], source[6^WordEndianFlip], source[7^WordEndianFlip]);
++
++    suword_noerr ((E3_int32 *) &dest[7], (E3_int32) source[7^WordEndianFlip]);
++    suword_noerr ((E3_int32 *) &dest[6], (E3_int32) source[6^WordEndianFlip]);
++    suword_noerr ((E3_int32 *) &dest[5], (E3_int32) source[5^WordEndianFlip]);
++    suword_noerr ((E3_int32 *) &dest[4], (E3_int32) source[4^WordEndianFlip]);
++    suword_noerr ((E3_int32 *) &dest[3], (E3_int32) source[3^WordEndianFlip]);
++    suword_noerr ((E3_int32 *) &dest[2], (E3_int32) source[2^WordEndianFlip]);
++    suword_noerr ((E3_int32 *) &dest[1], (E3_int32) source[1^WordEndianFlip]);
++    suword_noerr ((E3_int32 *) &dest[0], (E3_int32) source[0^WordEndianFlip]);
++
++    sys_putItemBack (sp, list, item);                         /* chain onto list of items. */
++
++    no_fault();
++    kmutex_unlock (&sctx->Lock);
++}
++
++static void
++sys_freeWordItem (ELAN3_CTXT *ctxt, void *itemp)
++{
++    SYS_CTXT     *sctx = (SYS_CTXT *) ctxt->Private;
++    SYS_SWAP_SPACE *sp   = sctx->Swap;
++    label_t       ljp;
++
++    kmutex_lock (&sctx->Lock);
++    
++    if (on_fault (&ljp))
++    {
++      no_fault();
++      kmutex_unlock (&sctx->Lock);
++      
++      sys_exception (ctxt, EXCEPTION_SWAP_FAULT, LIST_FREE_WORD, (void *) NULL, null_valist);
++      return;
++    }
++
++    sys_putItemBack (sp, LIST_FREE_WORD, itemp);
++
++    no_fault();
++    kmutex_unlock (&sctx->Lock);
++}
++
++static void
++sys_freeBlockItem (ELAN3_CTXT *ctxt, void *itemp)
++{
++    SYS_CTXT       *sctx = (SYS_CTXT *) ctxt->Private;
++    SYS_SWAP_SPACE *sp   = sctx->Swap;
++    SYS_BLOCK_ITEM *item = (SYS_BLOCK_ITEM *)itemp;
++    E3_uint32      *dest;
++    label_t       ljp;
++
++    kmutex_lock (&sctx->Lock);
++    
++    if (on_fault (&ljp))
++    {
++      no_fault();
++      kmutex_unlock (&sctx->Lock);
++      
++      sys_exception (ctxt, EXCEPTION_SWAP_FAULT, LIST_FREE_BLOCK, (void *) NULL, null_valist);
++      return;
++    }
++#ifdef DEBUG_PRINTF
++    dest = (E3_uint32 *) fuptr_noerr ((void **) &item->Pointer);
++
++    PRINTF2 (ctxt, DBG_SYSCALL, "sys_freeBlockItem: item=%p dest=%p\n", item, dest);
++    PRINTF4 (ctxt, DBG_SYSCALL, "                  %08x %08x %08x %08x\n",
++           fuword_noerr ((int *) &dest[0]), fuword_noerr ((int *) &dest[1]), 
++           fuword_noerr ((int *) &dest[2]), fuword_noerr ((int *) &dest[3]));
++    PRINTF4 (ctxt, DBG_SYSCALL, "                  %08x %08x %08x %08x\n",
++           fuword_noerr ((int *) &dest[4]), fuword_noerr ((int *) &dest[5]),
++           fuword_noerr ((int *) &dest[6]), fuword_noerr ((int *) &dest[7]));
++#endif
++
++    sys_putItemBack (sp, LIST_FREE_BLOCK, itemp);
++
++    no_fault();
++    kmutex_unlock (&sctx->Lock);
++}
++
++static void
++sys_putbackItem (ELAN3_CTXT *ctxt, int list, void *itemp)
++{
++    SYS_CTXT       *sctx = (SYS_CTXT *) ctxt->Private;
++    SYS_SWAP_SPACE *sp   = sctx->Swap;
++    label_t       ljp;
++
++    kmutex_lock (&sctx->Lock);
++    
++    if (on_fault (&ljp))
++    {
++      no_fault();
++      kmutex_unlock (&sctx->Lock);
++      
++      sys_exception (ctxt, EXCEPTION_SWAP_FAULT, list, (void *) NULL, null_valist);
++      return;
++    }
++
++    sys_putItemFront (sp, list, itemp);
++
++    no_fault();
++    kmutex_unlock (&sctx->Lock);
++}
++
++static int
++sys_countItems (ELAN3_CTXT *ctxt, int list)
++{
++    SYS_CTXT     *sctx  = (SYS_CTXT *) ctxt->Private;
++    SYS_SWAP_SPACE *sp    = sctx->Swap;
++    int                   count = 0;
++    void         *item;
++    label_t       ljb;
++
++    kmutex_lock (&sctx->Lock);
++    
++    if (on_fault (&ljb))
++    {
++      no_fault();
++      kmutex_unlock (&sctx->Lock);
++      sys_exception (ctxt, EXCEPTION_SWAP_FAULT, list, (void *) NULL, null_valist);
++      return (0);
++    }
++
++    for (item = (void *) fuptr_noerr ((void **) &sp->ItemListsHead[list]); 
++       item != NULL;
++       item = (void *) fuptr_noerr ((void **) item))
++    {
++      count++;
++    }
++
++    no_fault();
++    kmutex_unlock (&sctx->Lock);
++
++    return (count);
++}
++
++
++long sys_longTime;
++long sys_shortTime;
++int  sys_waitTicks;
++int  sys_maxBackoff;
++
++#define SYS_LONG_TIME         MAX((hz * 5) / 1000, 1)         /* 5 ms */
++#define SYS_SHORT_TIME                MAX((hz * 2) / 1000, 1)         /* 2 ms */
++#define SYS_WAIT_TICKS                MAX((hz * 1) / 1000, 1)         /* 1 ms  - backoff granularity */
++#define SYS_MAX_BACKOFF               MAX((hz * 5) / 1000, 1)         /* 5 ms  - max backoff for "nacked" packets*/
++#define SYS_TIMEOUT_BACKOFF   MAX((hz * 10) / 1000, 1)        /* 10 ms - backoff for output timeout (point to point) */
++#define SYS_BCAST_BACKOFF     MAX((hz * 50) / 1000, 1)        /* 50 ms - backoff for output timeout (broadcast) */
++#define SYS_NETERR_BACKOFF    MAX((hz * 10) / 1000, 1)        /* 10 ms - delay for network error in dma data */
++
++static void
++sys_backoffWait (ELAN3_CTXT *ctxt, int ticks)
++{
++    SYS_CTXT *sctx = (SYS_CTXT *) ctxt->Private;
++    long      t;
++
++    spin_lock (&sctx->WaitLock);
++
++    t = lbolt - sctx->Time;
++
++    if (sys_longTime   == 0) sys_longTime   = SYS_LONG_TIME;
++    if (sys_shortTime  == 0) sys_shortTime  = SYS_SHORT_TIME;
++    if (sys_waitTicks  == 0) sys_waitTicks  = SYS_WAIT_TICKS;
++    if (sys_maxBackoff == 0) sys_maxBackoff = SYS_MAX_BACKOFF;
++
++    if (t > sys_longTime)                                     /* It's a long time since the last trap */
++      sctx->Backoff = 0;                                      /* so set the backoff back down to 0 */
++
++    if (ticks)
++    {
++      PRINTF2 (ctxt, DBG_DPROC, "sys_backoffWait : Waiting - %d ticks [%lx]\n", ticks, t);
++      kcondvar_timedwait (&sctx->NetworkErrorWait, &sctx->WaitLock, NULL, lbolt + ticks);
++    }
++    else if (sctx->Armed)
++    {
++      if (t < sys_shortTime)                                  /* It's been a short time since the last */
++      {                                                       /* trap, so increase the backoff */
++          sctx->Backoff++;
++          
++          if (sctx->Backoff > sys_maxBackoff)
++              sctx->Backoff = sys_maxBackoff;
++      }
++
++      PRINTF2 (ctxt, DBG_DPROC, "sys_backoffWait : Waiting - %d [%lx]\n", sctx->Backoff, t);
++
++       if (sctx->Backoff)
++           kcondvar_timedwaitsig (&sctx->NetworkErrorWait, &sctx->WaitLock, NULL, lbolt + sctx->Backoff * sys_waitTicks);
++
++      sctx->Armed = 0;
++    }
++    else
++    {
++      PRINTF1 (ctxt, DBG_DPROC, "sys_backoffWait : Not Waiting - %d\n", sctx->Backoff);
++
++    }
++    sctx->Time = lbolt;
++
++    spin_unlock (&sctx->WaitLock);
++}
++
++static int
++trapSize (int proc)
++{
++    switch (proc)
++    {
++    case DMA_PROC:    return (sizeof (DMA_TRAP));
++    case THREAD_PROC: return (sizeof (THREAD_TRAP));
++    case COMMAND_PROC:        return (sizeof (COMMAND_TRAP));
++    case INPUT_PROC:  return (sizeof (INPUT_TRAP));
++    default:          return (0);
++    }
++}
++
++static int
++sys_exception (ELAN3_CTXT *ctxt, int type, int proc, void *trapp, va_list ap)
++{
++    SYS_CTXT *sctx  = (SYS_CTXT *) ctxt->Private;
++    int             res;
++
++    PRINTF2 (ctxt, DBG_SYSCALL, "sys_exception: type %d proc %d\n", type, proc);
++
++    switch (type)
++    {
++    case EXCEPTION_INVALID_ADDR:
++    {
++      E3_FaultSave_BE *faultSave = va_arg (ap, E3_FaultSave_BE *);
++      int              res       = va_arg (ap, int);
++      
++      sys_addException (sctx, type, proc, trapp, trapSize(proc), faultSave, res, 0);
++      break;
++    }
++    
++    case EXCEPTION_UNIMP_INSTR:
++    {
++      E3_uint32 instr = va_arg (ap, E3_uint32);
++      
++      sys_addException (sctx, type, proc, trapp, trapSize(proc), NULL, 0, instr);
++      break;
++    }
++    
++    case EXCEPTION_INVALID_PROCESS:
++    {
++      E3_uint32 vproc = va_arg (ap, E3_uint32);
++      int       res  = va_arg (ap, int);
++
++      switch (proc)
++      {
++      case DMA_PROC:
++          if (sctx->Flags & ELAN3_SYS_FLAG_DMA_BADVP)
++          {
++              DMA_TRAP *trap = (DMA_TRAP *) trapp;
++
++              if (trap->Desc.s.dma_direction != DMA_WRITE)
++                  trap->Desc.s.dma_srcEvent = trap->Desc.s.dma_destEvent;
++
++              trap->Desc.s.dma_direction       = DMA_WRITE;
++              trap->Desc.s.dma_size            = 0;
++              trap->Desc.s.dma_source          = (E3_Addr) 0;
++              trap->Desc.s.dma_dest            = (E3_Addr) 0;
++              trap->Desc.s.dma_destEvent       = (E3_Addr) 0;
++              trap->Desc.s.dma_destCookieVProc = 0;
++              trap->Desc.s.dma_srcCookieVProc  = 0;
++              
++              return (OP_IGNORE);
++          }
++          break;
++
++      case THREAD_PROC:
++          if (sctx->Flags & ELAN3_SYS_FLAG_THREAD_BADVP)
++          {
++              THREAD_TRAP *trap = (THREAD_TRAP *) trapp;
++
++              trap->TrapBits.s.PacketAckValue = E3_PAckError;
++              
++              return (OP_IGNORE);
++          }
++          break;
++      }
++          
++      sys_addException (sctx, type, proc, trapp, trapSize(proc), NULL, res, vproc);
++      break;
++    }
++    
++    case EXCEPTION_FAULTED:
++    {
++      E3_Addr addr = va_arg (ap, E3_Addr);
++
++      sys_addException (sctx, type, proc, trapp, trapSize(proc), NULL, 0, addr);
++      break;
++    }
++    
++    case EXCEPTION_QUEUE_OVERFLOW:
++    {
++      E3_FaultSave_BE *faultSave = va_arg (ap, E3_FaultSave_BE *);
++      int              trapType  = va_arg (ap, int);
++      
++      sys_addException (sctx, type, proc, trapp, trapSize(proc), faultSave, 0, trapType);
++      break;
++    }
++    
++    case EXCEPTION_COMMAND_OVERFLOW:
++    {
++      int count = va_arg (ap, int);
++      
++      sys_addException (sctx, type, proc, trapp, trapSize(proc), NULL, 0, count);
++      break;
++    }
++    
++    case EXCEPTION_CHAINED_EVENT:
++    {
++      E3_Addr addr = va_arg (ap, E3_Addr);
++      
++      sys_addException (sctx, type, proc, trapp, trapSize(proc), NULL, 0, addr);
++      break;
++    }
++    
++    case EXCEPTION_DMA_RETRY_FAIL:
++    case EXCEPTION_PACKET_TIMEOUT:
++      if (proc != DMA_PROC)
++          sys_backoffWait (ctxt, SYS_TIMEOUT_BACKOFF);
++      else
++      {
++          DMA_TRAP *trap = (DMA_TRAP *) trapp;
++          
++          if (sctx->Flags & ELAN3_SYS_FLAG_DMAFAIL)
++          {
++              E3_BlockCopyEvent *event;
++
++              if (trap->Desc.s.dma_direction != DMA_WRITE)
++                  trap->Desc.s.dma_srcEvent = trap->Desc.s.dma_destEvent;
++
++              /* change the source word to be E3_EVENT_FAILED */
++              if ((event = (E3_BlockCopyEvent *) elan3mmu_mainaddr (ctxt->Elan3mmu, trap->Desc.s.dma_srcEvent)) == NULL)
++              {
++                  sys_addException (sctx, type, proc, trapp, trapSize(proc), NULL, 0, 0);
++                  break;
++              }
++
++              suword (&event->ev_Source, E3_EVENT_FAILED);
++              wmb(); mmiob();
++              
++              trap->Desc.s.dma_direction       = DMA_WRITE;
++              trap->Desc.s.dma_size            = 0;
++              trap->Desc.s.dma_source          = (E3_Addr) 0;
++              trap->Desc.s.dma_dest            = (E3_Addr) 0;
++              trap->Desc.s.dma_destEvent       = (E3_Addr) 0;
++              trap->Desc.s.dma_destCookieVProc = 0;
++              trap->Desc.s.dma_srcCookieVProc  = 0;
++              
++              return (OP_IGNORE);
++          }
++
++          if (type == EXCEPTION_DMA_RETRY_FAIL)
++              sys_backoffWait (ctxt, 0);
++          else
++          {
++              ELAN_LOCATION location;
++
++              krwlock_read (&ctxt->VpLock);
++              location = ProcessToLocation (ctxt, NULL, trap->Desc.s.dma_direction == DMA_WRITE ? 
++                                            trap->Desc.s.dma_destVProc : trap->Desc.s.dma_srcVProc, NULL);
++              krwlock_done (&ctxt->VpLock);
++              
++              sys_backoffWait (ctxt, location.loc_node == ELAN3_INVALID_NODE ? SYS_BCAST_BACKOFF : SYS_TIMEOUT_BACKOFF);
++          }
++      }
++      return (OP_IGNORE);
++      
++    case EXCEPTION_NETWORK_ERROR:
++    {
++      INPUT_TRAP       *trap  = (INPUT_TRAP *) trapp;
++      NETERR_RESOLVER **rvpp  = va_arg (ap, NETERR_RESOLVER **);
++
++      ASSERT (trap->State == CTXT_STATE_NETWORK_ERROR);
++
++      if (! (sctx->Flags & ELAN3_SYS_FLAG_NETERR) && (trap->DmaIdentifyTransaction || trap->ThreadIdentifyTransaction))
++      {
++          if ((*rvpp) != (NETERR_RESOLVER *) NULL)
++              res = (*rvpp)->Status;
++          else if ((res = QueueNetworkErrorResolver (ctxt, trap, rvpp)) == ESUCCESS)
++          {
++              /* Successfully queued the network error resolver */
++              return (OP_HANDLED);
++          }
++
++          /* network error resolution has failed - either a bad cookie or */
++          /* an rpc error has occured */
++          sys_addException (sctx, type, proc, trapp, trapSize(proc), NULL, res, 0);
++      }
++      else
++      {
++          /* Must be an overlaped dma packet. Must wait long enough to
++           * ensure that the sending dma'er has tried to send the next
++           * packet and had it discarded. In the real world this should
++           * be greater than an output timeout. (About 8mSec) */
++          
++          sys_backoffWait (ctxt, SYS_NETERR_BACKOFF);
++          
++          /* set this inputter state to be ok, since we've been called 
++           * by the lwp it will lower the context filter for us, so 
++           * re-enabling the inputter,  note we don't need to execute
++           * any of the packet since the dma process will re-transmit
++           * it after receiving a nack for the next packet */
++          trap->State = CTXT_STATE_OK;
++          
++          return (OP_HANDLED);
++      }
++      break;
++    }
++    
++    default:
++      sys_addException (sctx, type, proc, trapp, trapSize(proc), NULL, 0, 0);
++      break;
++    }
++    
++    if (type != EXCEPTION_DEBUG)
++#ifdef LINUX
++#ifdef NO_NPTL
++      psignal (CURPROC()->p_opptr, sctx->signal);
++#else
++      psignal (CURPROC()->parent, sctx->signal);
++#endif
++#else
++      psignal (CURPROC(), sctx->signal);
++#endif
++    return (OP_HANDLED);
++}
++
++static int
++sys_event (ELAN3_CTXT *ctxt, E3_uint32 cookie, int flag)
++{
++    SYS_CTXT *sctx = (SYS_CTXT *) ctxt->Private;
++
++    cookie_fire_cookie (sctx->Table, cookie);
++
++    return (OP_HANDLED); 
++}
++
++static void
++sys_swapin (ELAN3_CTXT *ctxt)
++{
++    PRINTF0 (ctxt, DBG_SYSCALL, "sys_swapin\n");
++}
++
++static void
++sys_swapout (ELAN3_CTXT *ctxt)
++{
++    PRINTF0 (ctxt, DBG_SYSCALL, "sys_swapout\n");
++}
++
++static void
++sys_freePrivate (ELAN3_CTXT *ctxt)
++{
++    SYS_CTXT *sctx = (SYS_CTXT *) ctxt->Private;
++
++    cookie_free_table (sctx->Table);
++
++    kmutex_destroy (&sctx->Lock);
++    spin_lock_destroy (&sctx->WaitLock);
++    kcondvar_destroy (&sctx->NetworkErrorWait);
++
++    KMEM_FREE (sctx, sizeof (SYS_CTXT));
++    ctxt->Private = NULL;
++}
++
++static int
++sys_checkThisDma (ELAN3_CTXT *ctxt, NETERR_FIXUP *nef, E3_DMA *dma)
++{
++    E3_DmaType type;
++    E3_uint32  cookie;
++    E3_uint32  cvproc;
++    int              ignore;
++    int              match;
++
++    type.type = fuword_noerr ((int *) &dma->dma_type);
++
++    if (type.s.direction == DMA_WRITE)
++    {
++      cookie = fuword_noerr ((int *) &dma->dma_srcCookieVProc);
++      cvproc = fuword_noerr ((int *) &dma->dma_destCookieVProc);
++    }
++    else
++    {
++      cookie = fuword_noerr ((int *) &dma->dma_destCookieVProc);
++      cvproc = fuword_noerr ((int *) &dma->dma_srcCookieVProc);
++    }
++
++    PRINTF5 (ctxt, DBG_NETERR, "sys_checkThisDma: dir = %d cookie = %08x cvproc = %08x CookieVProc %08x DstProcess %04x\n",
++           type.s.direction, cookie, cvproc, nef->Message.CookieVProc, nef->Message.DstProcess);
++
++    /* A DMA matches a network errror fixup if it's going to the right place (or is a broadcast)
++     * and the approriate cookie matches, except that we ignore DMA's which don't have a destEvent
++     * since they don't have any atomic behaviour (though they still send the identify) */
++
++    ignore = (type.s.direction == DMA_WRITE && cookie == 0 &&
++            fuword_noerr ((int *) &dma->dma_destEvent) == 0);
++    match  = (nef->Message.CookieVProc == cookie &&
++            (nef->Message.DstProcess == (cvproc & DMA_PROCESS_MASK) || nef->Message.WaitForEop));
++
++    PRINTF2 (ctxt, DBG_NETERR, "  -> %s %s\n", ignore ? "ignore" : match ? "matched" : "not-matched", nef->Message.WaitForEop ? "wait for eop" : "");
++
++    if (match && !ignore && !nef->Message.WaitForEop)
++    {
++      PRINTF0 (ctxt, DBG_NETERR, "sys_checkThisDma: nuking the dma\n");
++
++      /* NOTE - we access the dma descriptor backwards since it could exist in sdram */
++      if (type.s.direction != DMA_WRITE)
++          suword_noerr ((int *) &dma->dma_srcEvent, 0);
++
++      suword_noerr ((int *) &dma->dma_destEvent, 0);
++      suword_noerr ((int *) &dma->dma_dest,      0);
++      suword_noerr ((int *) &dma->dma_source,    0);
++      suword_noerr ((int *) &dma->dma_size,      0);
++
++      if (type.s.direction != DMA_WRITE)
++          suword_noerr ((int *) &dma->dma_type, fuword_noerr ((int *) &dma->dma_type) & E3_DMA_CONTEXT_MASK);
++
++      wmb(); mmiob();
++    }
++
++    return (match && !ignore);
++}
++
++static int
++sys_fixupNetworkError (ELAN3_CTXT *ctxt, NETERR_FIXUP *nef)
++{
++    SYS_CTXT       *sctx    = (SYS_CTXT *) ctxt->Private;
++    SYS_SWAP_SPACE *sp      = sctx->Swap;
++    int                   matched = 0;
++    SYS_WORD_ITEM  *wordp;
++    SYS_BLOCK_ITEM *blockp;
++    label_t       ljb;
++    int                   res;
++
++    PRINTF3 (ctxt, DBG_NETERR, "sys_fixupnetworkError %08x %08x %08x\n", 
++           nef->Message.CookieAddr, nef->Message.CookieVProc, nef->Message.NextCookie);
++
++    if (nef->Message.CookieAddr == (E3_Addr) 0)                       /* It's a DMA which requires fixing up */
++    {
++      kmutex_lock (&sctx->Lock);
++
++      if (on_fault (&ljb))
++          res = EFAULT;
++      else
++      {
++          /* scan the dma ptr list */
++          for (wordp = (SYS_WORD_ITEM *) fuptr_noerr ((void **) &sp->ItemListsHead[LIST_DMA_PTR]);
++               wordp != NULL; 
++               wordp = (SYS_WORD_ITEM *) fuptr_noerr ((void **) &wordp->Next))
++          {
++              E3_uint32 value = fuword_noerr ((int *) &wordp->Value);
++              E3_DMA    *dma  = (E3_DMA *) elan3mmu_mainaddr (ctxt->Elan3mmu, value);
++
++              PRINTF3 (ctxt, DBG_NETERR, "sys_fixupnetworkError: check block item %p Value %08x dma %p\n", wordp, value, dma);
++
++              matched += sys_checkThisDma (ctxt, nef, dma);
++          }
++      
++          /* scan the dma desc list */
++          for (blockp = (SYS_BLOCK_ITEM *) fuptr_noerr ((void **) &sp->ItemListsHead[LIST_DMA_DESC]);
++               blockp != NULL; 
++               blockp = (SYS_BLOCK_ITEM *) fuptr_noerr ((void **) &blockp->Next))
++          {
++              E3_DMA *dma = (E3_DMA *) fuptr_noerr ((void *) &blockp->Pointer);
++              
++              PRINTF2 (ctxt, DBG_NETERR, "sys_fixupnetworkError: check block item %p Pointer %p\n", blockp, dma);
++
++              matched += sys_checkThisDma (ctxt, nef, dma);
++          }
++          
++          /* If we've still not found it, then check the command port item */
++          /* it MUST be present as a command waiting to be executed, as */
++          /* otherwise it could have already happened and we will claim to */
++          /* have found it, but not realy */
++          if (ctxt->CommandPortItem != NULL)
++          {
++              E3_DMA *dma = (E3_DMA *) fuptr_noerr ((void *) &((SYS_BLOCK_ITEM *) ctxt->CommandPortItem)->Pointer);
++
++              if (sys_checkThisDma (ctxt, nef, dma))
++              {
++                  printk ("!!! it's the command port item - need to ensure that the command exists\n");
++                  matched++;
++              }
++          }
++
++          res = matched ? ESUCCESS : ESRCH;
++      }
++      no_fault();
++      kmutex_unlock (&sctx->Lock);
++
++      if (matched > 1)
++          ElanException (ctxt, EXCEPTION_COOKIE_ERROR, DMA_PROC, NULL, NULL, nef->Message.CookieVProc);
++    }
++    else                                                      /* It's a thread which requires fixing up */
++    {
++      E3_int32  *cookiePtr = (E3_int32 *) elan3mmu_mainaddr (ctxt->Elan3mmu, nef->Message.CookieAddr);
++      E3_uint32  curval    = fuword_noerr (cookiePtr);
++
++      if (curval == nef->Message.CookieVProc)         /* thread doesn't think it's been done */
++      {
++          if (! nef->Message.WaitForEop)
++          {
++              suword_noerr (cookiePtr, nef->Message.NextCookie);
++              mb(); mmiob();
++          }
++          
++          res = ESUCCESS;
++      }
++      else                                                    /* thread thinks that it's been executed */
++      {
++          res = ESRCH;
++      }
++    }
++    
++    CompleteNetworkErrorFixup (ctxt, nef, res);
++
++    return (OP_HANDLED);
++}
++
++
++static int
++sys_startFaultCheck (ELAN3_CTXT *ctxt)
++{
++    return (0);
++}
++
++static void
++sys_endFaultCheck (ELAN3_CTXT *ctxt)
++{
++    wmb();
++}
++
++static E3_uint8
++sys_load8 (ELAN3_CTXT *ctxt, E3_Addr addr)
++{
++    E3_uint8 *maddr = (E3_uint8 *) elan3mmu_mainaddr (ctxt->Elan3mmu, addr);
++
++    return (fubyte_noerr (maddr));
++}
++
++static void
++sys_store8 (ELAN3_CTXT *ctxt, E3_Addr addr, E3_uint8 val)
++{
++    E3_uint8 *maddr = (E3_uint8 *) elan3mmu_mainaddr (ctxt->Elan3mmu, addr);
++
++    subyte_noerr (maddr, val);
++    wmb(); mmiob();
++}
++
++static E3_uint16
++sys_load16 (ELAN3_CTXT *ctxt, E3_Addr addr)
++{
++    E3_uint16 *maddr = (E3_uint16 *) elan3mmu_mainaddr (ctxt->Elan3mmu, addr);
++
++    return (fusword_noerr (maddr));
++}
++
++static void
++sys_store16 (ELAN3_CTXT *ctxt, E3_Addr addr, E3_uint16 val)
++{
++    E3_uint16 *maddr = (E3_uint16 *) elan3mmu_mainaddr (ctxt->Elan3mmu, addr);
++
++    susword_noerr (maddr, val);
++    wmb(); mmiob();
++}
++
++static E3_uint32
++sys_load32 (ELAN3_CTXT *ctxt, E3_Addr addr)
++{
++    E3_uint32 *maddr = (E3_uint32 *) elan3mmu_mainaddr (ctxt->Elan3mmu, addr);
++
++    return (fuword_noerr (maddr));
++}
++
++static void
++sys_store32 (ELAN3_CTXT *ctxt, E3_Addr addr, E3_uint32 val)
++{
++    E3_uint32 *maddr = (E3_uint32 *) elan3mmu_mainaddr (ctxt->Elan3mmu, addr);
++
++    suword_noerr (maddr, val);
++    wmb(); mmiob();
++}
++
++static E3_uint64
++sys_load64 (ELAN3_CTXT *ctxt, E3_Addr addr)
++{
++    E3_uint64 *maddr = (E3_uint64 *) elan3mmu_mainaddr (ctxt->Elan3mmu, addr);
++
++    return (fulonglong_noerr ((long long *) maddr));
++}
++
++static void
++sys_store64 (ELAN3_CTXT *ctxt, E3_Addr addr, E3_uint64 val)
++{
++    E3_uint64 *maddr = (E3_uint64 *) elan3mmu_mainaddr (ctxt->Elan3mmu, addr);
++
++    sulonglong_noerr ((long long *) maddr, val);
++    wmb(); mmiob();
++}
++
++
++void
++sys_addException (SYS_CTXT *sctx, int type, int proc, caddr_t trapp, int size,
++                E3_FaultSave_BE *faultSave, u_long res, u_long value)
++{
++    SYS_EXCEPTION      *ex_ptr;
++    int                       front;
++    int                       back;
++    int                       count;
++    label_t           ljp;
++
++    PRINTF4 (DBG_DEVICE, DBG_FN, "sys_addException: type %d proc %d res %ld value %ld\n",
++           type, proc, res, value);
++
++    KMEM_ZALLOC (ex_ptr, SYS_EXCEPTION *, sizeof  (SYS_EXCEPTION), TRUE);
++
++    if (ex_ptr != NULL)
++    {
++      bzero ((caddr_t) ex_ptr, sizeof (SYS_EXCEPTION));
++
++      ex_ptr->Type  = type;
++      ex_ptr->Proc  = proc;
++      ex_ptr->Res   = res;
++      ex_ptr->Value = value;
++      
++      if (trapp && size)
++          bcopy (trapp, (caddr_t) &ex_ptr->Union, size);
++      if (faultSave)
++          bcopy ((caddr_t) faultSave, (caddr_t) &ex_ptr->FaultArea, sizeof (E3_FaultSave_BE));
++    }
++
++    kmutex_lock (&sctx->Lock);
++    if (! on_fault (&ljp))
++    {
++      front = fuword_noerr (&sctx->Exceptions->Front);
++      back  = fuword_noerr (&sctx->Exceptions->Back);
++      count = fuword_noerr (&sctx->Exceptions->Count);
++
++      if (count <= 0 || front < 0 || back < 0 || front >= count || back >= count)
++          suword_noerr (&sctx->Exceptions->Overflow, fuword_noerr (&sctx->Exceptions->Overflow) + 1);
++      else if (((front+1) % count ) == back)
++          suword_noerr (&sctx->Exceptions->Overflow, fuword_noerr (&sctx->Exceptions->Overflow) + 1);
++      else
++      {
++          if (ex_ptr != NULL)
++              copyout_noerr ((caddr_t) ex_ptr, (caddr_t) &sctx->Exceptions->Exceptions[front], sizeof (SYS_EXCEPTION));
++          else
++          {
++              suword_noerr (&sctx->Exceptions->Exceptions[front].Type, EXCEPTION_ENOMEM);
++              suword_noerr (&sctx->Exceptions->Exceptions[front].Proc, 0);
++          }
++          suword_noerr (&sctx->Exceptions->Front, (front + 1) % count);
++      }
++
++      /* always reset the magic number in case it's been overwritten */
++      /* so that 'edb' can find the exception page in the core file */
++      suword_noerr (&sctx->Exceptions->Magic, SYS_EXCEPTION_MAGIC);
++    }
++    no_fault();
++    kmutex_unlock (&sctx->Lock);
++    
++    if (ex_ptr != NULL)
++      KMEM_FREE (ex_ptr, sizeof  (SYS_EXCEPTION));
++}
++
++int
++sys_getException (SYS_CTXT *sctx, SYS_EXCEPTION *ex)
++{
++    int     front;
++    int     back;
++    int     count;
++    int     res;
++    label_t ljp;
++
++    if (sctx->Exceptions == NULL)
++      return (EINVAL);
++
++    kmutex_lock (&sctx->Lock);
++    if (on_fault (&ljp))
++    {
++      no_fault();
++      kmutex_unlock (&sctx->Lock);
++      return (EFAULT);
++    }
++    
++    front = fuword_noerr (&sctx->Exceptions->Front);
++    back  = fuword_noerr (&sctx->Exceptions->Back);
++    count = fuword_noerr (&sctx->Exceptions->Count);
++
++    if (count <= 0 || front < 0 || back < 0 || front >= count || back >= count || back == front)
++      res = EINVAL;
++    else
++    {
++      copyin_noerr ((caddr_t) &sctx->Exceptions->Exceptions[back], (caddr_t) ex, sizeof (SYS_EXCEPTION));
++      suword_noerr (&sctx->Exceptions->Back, (back+1) % count);
++
++      res = ESUCCESS;
++    }
++    no_fault();
++    kmutex_unlock (&sctx->Lock);
++
++    return (res);
++}
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan3/eventcookie.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan3/eventcookie.c     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan3/eventcookie.c  2005-05-11 12:10:12.416935920 -0400
+@@ -0,0 +1,324 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: eventcookie.c,v 1.7 2003/08/13 10:03:03 fabien Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/os/eventcookie.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elanvp.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanctxt.h>
++#include <elan3/elandebug.h>
++#include <elan3/urom_addrs.h>
++#include <elan3/thread.h>
++#include <elan3/vmseg.h>
++
++static EVENT_COOKIE_TABLE *cookie_tables;
++static spinlock_t        cookie_table_lock;
++
++/*
++ * cookie_drop_entry:
++ *   drop the reference to a cookie held 
++ *   by the cookie table
++ */
++static void
++cookie_drop_entry (EVENT_COOKIE_ENTRY *ent)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&ent->ent_lock, flags);
++    if (--ent->ent_ref != 0)
++    {
++      ent->ent_fired = ent->ent_cookie;
++      kcondvar_wakeupall (&ent->ent_wait, &ent->ent_lock);
++
++      spin_unlock_irqrestore (&ent->ent_lock, flags);
++    }
++    else
++    {
++      spin_unlock_irqrestore (&ent->ent_lock, flags);
++
++      spin_lock_destroy (&ent->ent_lock);
++      kcondvar_destroy (&ent->ent_wait);
++
++      KMEM_FREE (ent, sizeof (EVENT_COOKIE_ENTRY));
++    }
++}
++
++void
++cookie_init()
++{
++    spin_lock_init (&cookie_table_lock);
++}
++
++void
++cookie_fini()
++{
++    spin_lock_destroy (&cookie_table_lock);
++}
++
++EVENT_COOKIE_TABLE *
++cookie_alloc_table (unsigned long task, unsigned long handle)
++{
++    EVENT_COOKIE_TABLE *tbl, *ntbl;
++
++    KMEM_ZALLOC (ntbl, EVENT_COOKIE_TABLE *, sizeof (EVENT_COOKIE_TABLE), TRUE);
++
++    if (ntbl == NULL)
++      return (NULL);
++
++    spin_lock (&cookie_table_lock);
++    
++    for (tbl = cookie_tables; tbl; tbl = tbl->tbl_next)
++      if (tbl->tbl_task == task && tbl->tbl_handle == handle)
++          break;
++    
++    if (tbl != NULL)
++      tbl->tbl_ref++;
++    else
++    {
++      spin_lock_init (&ntbl->tbl_lock);
++
++      ntbl->tbl_task    = task;
++      ntbl->tbl_handle  = handle;
++      ntbl->tbl_ref     = 1;
++      ntbl->tbl_entries = NULL;
++
++      if ((ntbl->tbl_next = cookie_tables) != NULL)
++          cookie_tables->tbl_prev = ntbl;
++      cookie_tables = ntbl;
++      ntbl->tbl_prev = NULL;
++    }
++    spin_unlock (&cookie_table_lock);
++
++    if (tbl == NULL)
++      return (ntbl);
++    else
++    {
++      KMEM_FREE (ntbl, sizeof (EVENT_COOKIE_TABLE));
++      return (tbl);
++    }    
++}
++
++void
++cookie_free_table (EVENT_COOKIE_TABLE *tbl)
++{
++    EVENT_COOKIE_ENTRY *ent;
++
++    spin_lock (&cookie_table_lock);
++    if (tbl->tbl_ref > 1)
++    {
++      tbl->tbl_ref--;
++      spin_unlock (&cookie_table_lock);
++      return;
++    }
++    
++    if (tbl->tbl_prev)
++      tbl->tbl_prev->tbl_next = tbl->tbl_next;
++    else
++      cookie_tables = tbl->tbl_next;
++    if (tbl->tbl_next)
++      tbl->tbl_next->tbl_prev = tbl->tbl_prev;
++    
++    spin_unlock (&cookie_table_lock);
++    
++    /* NOTE - table no longer visible to other threads
++     *        no need to aquire tbl_lock */
++    while ((ent = tbl->tbl_entries) != NULL)
++    {
++      if ((tbl->tbl_entries = ent->ent_next) != NULL)
++          ent->ent_next->ent_prev = NULL;
++      
++      cookie_drop_entry (ent);
++    }
++    spin_lock_destroy (&tbl->tbl_lock);
++
++    KMEM_FREE (tbl, sizeof (EVENT_COOKIE_TABLE));
++}
++
++int
++cookie_alloc_cookie (EVENT_COOKIE_TABLE *tbl, EVENT_COOKIE cookie)
++{
++    EVENT_COOKIE_ENTRY *ent, *nent;
++    unsigned long flags;
++
++    KMEM_ZALLOC (nent, EVENT_COOKIE_ENTRY *, sizeof (EVENT_COOKIE_ENTRY), TRUE);
++    
++    spin_lock_irqsave (&tbl->tbl_lock, flags);
++    for (ent = tbl->tbl_entries; ent; ent = ent->ent_next)
++      if (ent->ent_cookie == cookie)
++          break;
++
++    if (ent == NULL)
++    {
++      kcondvar_init (&nent->ent_wait);
++      spin_lock_init (&nent->ent_lock);
++
++      nent->ent_ref    = 1;
++      nent->ent_cookie = cookie;
++
++      if ((nent->ent_next = tbl->tbl_entries) != NULL)
++          tbl->tbl_entries->ent_prev = nent;
++      tbl->tbl_entries = nent;
++      nent->ent_prev = NULL;
++    }
++    spin_unlock_irqrestore (&tbl->tbl_lock, flags);
++
++    if (ent == NULL)
++      return (ESUCCESS);
++    else
++    {
++      KMEM_FREE (nent, sizeof (EVENT_COOKIE_ENTRY));
++      return (EINVAL);
++    }
++}
++
++int
++cookie_free_cookie (EVENT_COOKIE_TABLE *tbl, EVENT_COOKIE cookie)
++{
++    EVENT_COOKIE_ENTRY *ent;
++    unsigned long flags;
++
++    spin_lock_irqsave (&tbl->tbl_lock, flags);
++    for (ent = tbl->tbl_entries; ent; ent = ent->ent_next)
++      if (ent->ent_cookie == cookie)
++          break;
++    
++    if (ent == NULL)
++    {
++      spin_unlock_irqrestore (&tbl->tbl_lock, flags);
++      return (EINVAL);
++    }
++
++    if (ent->ent_prev == NULL)
++      tbl->tbl_entries = ent->ent_next;
++    else
++      ent->ent_prev->ent_next = ent->ent_next;
++
++    if (ent->ent_next != NULL)
++      ent->ent_next->ent_prev = ent->ent_prev;
++    
++    spin_unlock_irqrestore (&tbl->tbl_lock, flags);
++
++    cookie_drop_entry (ent);
++
++    return (ESUCCESS);
++}
++
++/*
++ * cookie_fire_cookie:
++ *    fire the cookie - this is called from the event interrupt.
++ */
++int
++cookie_fire_cookie (EVENT_COOKIE_TABLE *tbl, EVENT_COOKIE cookie)
++{
++    EVENT_COOKIE_ENTRY *ent;
++    unsigned long flags;
++
++    spin_lock_irqsave (&tbl->tbl_lock, flags);
++    for (ent = tbl->tbl_entries; ent; ent = ent->ent_next)
++      if (ent->ent_cookie == cookie)
++          break;
++    
++    if (ent == NULL)
++    {
++      spin_unlock_irqrestore (&tbl->tbl_lock, flags);
++      return (EINVAL);
++    }
++          
++    spin_lock (&ent->ent_lock);
++    ent->ent_fired = cookie;
++    kcondvar_wakeupall (&ent->ent_wait, &ent->ent_lock);
++    spin_unlock (&ent->ent_lock);
++
++    spin_unlock_irqrestore (&tbl->tbl_lock, flags);
++
++    return (ESUCCESS);
++}    
++
++/*
++ * cookie_wait_cookie:
++ *    deschedule on a cookie if it has not already fired.
++ *    note - if the cookie is removed from the table, then
++ *           we free it off when we're woken up.
++ */
++int
++cookie_wait_cookie (EVENT_COOKIE_TABLE *tbl, EVENT_COOKIE cookie)
++{
++    EVENT_COOKIE_ENTRY *ent;
++    unsigned long flags;
++    
++    spin_lock_irqsave (&tbl->tbl_lock, flags);
++    for (ent = tbl->tbl_entries; ent; ent = ent->ent_next)
++      if (ent->ent_cookie == cookie)
++          break;
++    
++    if (ent == NULL)
++    {
++      spin_unlock_irqrestore (&tbl->tbl_lock, flags);
++      return (EINVAL);
++    }
++
++    spin_lock (&ent->ent_lock);
++    spin_unlock (&tbl->tbl_lock);
++
++    if (ent->ent_fired != 0)
++    {
++      spin_unlock_irqrestore (&ent->ent_lock, flags);
++      return (ESUCCESS);
++    }
++
++    ent->ent_ref++;
++    kcondvar_waitsig (&ent->ent_wait, &ent->ent_lock, &flags);
++    
++    if (--ent->ent_ref > 0)
++      spin_unlock_irqrestore (&ent->ent_lock, flags);
++    else
++    {
++      spin_unlock_irqrestore (&ent->ent_lock, flags);
++      
++      spin_lock_destroy (&ent->ent_lock);
++      kcondvar_destroy (&ent->ent_wait);
++
++      KMEM_FREE (ent, sizeof (EVENT_COOKIE_ENTRY));
++    }
++    return (ESUCCESS);
++}
++
++int
++cookie_arm_cookie (EVENT_COOKIE_TABLE *tbl, EVENT_COOKIE cookie)
++{
++    EVENT_COOKIE_ENTRY *ent;
++    unsigned long flags;
++
++    spin_lock_irqsave (&tbl->tbl_lock, flags);
++    for (ent = tbl->tbl_entries; ent; ent = ent->ent_next)
++      if (ent->ent_cookie == cookie)
++          break;
++    
++    if (ent == NULL)
++    {
++      spin_unlock_irqrestore (&tbl->tbl_lock, flags);
++      return (EINVAL);
++    }
++          
++    spin_lock (&ent->ent_lock);
++    ent->ent_fired = 0;
++    spin_unlock (&ent->ent_lock);
++
++    spin_unlock_irqrestore (&tbl->tbl_lock, flags);
++
++    return (ESUCCESS);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan3/iproc.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan3/iproc.c   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan3/iproc.c        2005-05-11 12:10:12.418935616 -0400
+@@ -0,0 +1,925 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: iproc.c,v 1.47 2003/09/24 13:57:25 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/os/iproc.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elanvp.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanctxt.h>
++#include <elan3/elandebug.h>
++#include <elan3/urom_addrs.h>
++#include <elan3/trtype.h>
++#include <elan3/vmseg.h>
++
++
++static int TrSizeTable[] = {0, 8, 16, 32, 64};
++
++static void  ConvertTransactionToSetEvent (ELAN3_CTXT *ctxt, E3_IprocTrapHeader_BE *hdrp, E3_Addr Addr);
++static void  SimulateBlockWrite  (ELAN3_CTXT *ctxt, E3_IprocTrapHeader_BE *hdrp, E3_IprocTrapData_BE *datap);
++static void  SimulateWriteWord   (ELAN3_CTXT *ctxt, E3_IprocTrapHeader_BE *hdrp, E3_IprocTrapData_BE *datap);
++static void  SimulateWriteDWord  (ELAN3_CTXT *ctxt, E3_IprocTrapHeader_BE *hdrp, E3_IprocTrapData_BE *datap);
++static void  SimulateTraceRoute  (ELAN3_CTXT *ctxt, E3_IprocTrapHeader_BE *hdrp, E3_IprocTrapData_BE *datap);
++static void  BumpInputterStats (ELAN3_DEV *dev, E3_IprocTrapHeader_BE *hdrp);
++
++void
++HandleIProcTrap (ELAN3_DEV           *dev, 
++               int                 Channel,
++               E3_uint32           Pend,
++               sdramaddr_t         FaultSaveOff,
++               sdramaddr_t         TransactionsOff,
++               sdramaddr_t         DataOff)
++{
++    E3_IprocTrapHeader_BE Transaction0;
++    ELAN3_CTXT                 *ctxt;
++    INPUT_TRAP           *trap;
++    register int          i;
++
++    /*
++     * Read the 1st set of transactions, so we can determine the 
++     * context for the trap 
++     */
++    elan3_sdram_copyq_from_sdram (dev, TransactionsOff, (void *) &Transaction0, 16);
++    
++    BumpStat (dev, IProcTraps);
++    BumpInputterStats (dev, &Transaction0);
++
++    if (Transaction0.s.TrTypeCntx.s.TypeCntxInvalid)
++    {
++      /*
++       * The context is not valid. This will occur if the packet
++       * trapped for an EopError with no IdentTrans or an error corrupted the context
++       * giving a CRC error on the first transaction and the Ack had not been returned.
++       */
++      if (Transaction0.s.TrTypeCntx.s.LastTrappedTrans)
++      {
++          PRINTF0 (DBG_DEVICE, DBG_IPROC, "iproc: Error on EOP without a good context, ignoring trap\n");
++      }
++      else
++      {
++          /* Check that only crap has been received.  If not then die. */
++          if (! Transaction0.s.IProcTrapStatus.s.BadLength &&
++              (Transaction0.s.IProcTrapStatus.Status & CRC_MASK) == CRC_STATUS_GOOD)
++          {
++              printk ("iproc: Did not have a valid context for the trap area.\n");
++              printk ("iproc: TrTypeCntx=%x TrAddr=%x TrData0=%x IProcTrapStatus=%x\n",
++                       Transaction0.s.TrTypeCntx.TypeContext, Transaction0.s.TrAddr,
++                       Transaction0.s.TrData0, Transaction0.s.IProcTrapStatus.Status);
++              panic ("elan3: iproc did not have a valid context");
++              /* NOTREACHED */
++          }
++          PRINTF0 (DBG_DEVICE, DBG_IPROC, "iproc: First transaction is bad, ignoring trap\n");
++      }
++    }
++    else
++    {
++      ctxt = ELAN3_DEV_CTX_TABLE(dev, Transaction0.s.TrTypeCntx.s.Context);
++      
++      if (ctxt == NULL)
++      {
++          PRINTF1 (DBG_DEVICE, DBG_INTR, "HandleIProcTrap: context %x invalid\n", 
++                   Transaction0.s.TrTypeCntx.s.Context);
++
++          BumpStat (dev, InvalidContext);
++      }
++      else
++      {
++          trap = (Channel == 0) ? &ctxt->Input0Trap : &ctxt->Input1Trap;
++
++          ASSERT (trap->State == CTXT_STATE_OK);
++          
++          trap->Transactions[0] = Transaction0;
++
++          PRINTF1 (ctxt, DBG_INTR, "HandleIProcTrap: %s\n", IProcTrapString (&trap->Transactions[0], NULL));
++          /*
++           * Copy the rest of the transactions into the trap area.
++           */
++          for (i = 0; !(trap->Transactions[i].s.TrTypeCntx.s.LastTrappedTrans);)
++          {
++              if (++i >= MAX_TRAPPED_TRANS)
++              {
++                  trap->Overflow = 1;
++                  break;
++              }
++
++              elan3_sdram_copyq_from_sdram (dev, TransactionsOff + i*sizeof (E3_IprocTrapHeader), (void *) &trap->Transactions[i], 16);
++
++              PRINTF1 (ctxt, DBG_INTR, "                 %s\n", IProcTrapString (&trap->Transactions[i], NULL));
++
++              BumpInputterStats (dev, &trap->Transactions[i]);
++          }
++          
++          /*
++           * Remember the number of transactions we've copied.
++           */
++          trap->NumTransactions = i+1;
++
++          PRINTF1 (ctxt, DBG_INTR, "                 NumTransactions = %d\n", trap->NumTransactions);
++          
++          /*
++           * Copy all the data blocks in one go to let the Elan prefetcher work 
++           */
++          elan3_sdram_copyq_from_sdram (dev, DataOff, trap->DataBuffers, trap->NumTransactions*sizeof (E3_IprocTrapData));
++
++          /*
++           * Copy fault save area and clear out for next time round.
++           */
++          elan3_sdram_copyq_from_sdram (dev, FaultSaveOff, (void *) &trap->FaultSave, 16);
++          elan3_sdram_zeroq_sdram (dev, FaultSaveOff, 16);
++
++          if (ELAN3_OP_IPROC_TRAP (ctxt, trap, Channel) == OP_DEFER)
++          {
++              /*
++               * Mark the trap as valid and set the inputter state to 
++               * raise the context filter.
++               */
++              trap->State = CTXT_STATE_TRAPPED;
++              kcondvar_wakeupone (&ctxt->Wait, &dev->IntrLock);
++              
++              SetInputterStateForContext (ctxt, Pend, NULL);
++          }
++      }
++    }
++}
++
++void
++InspectIProcTrap (ELAN3_CTXT *ctxt, INPUT_TRAP *trap)
++{
++    int             i;
++    int             StatusValid;
++
++    trap->AckSent                 = 0;
++    trap->BadTransaction            = 0;
++    
++    trap->TrappedTransaction        = NULL;
++    trap->TrappedDataBuffer       = NULL;
++    trap->WaitForEopTransaction     = NULL;
++    trap->WaitForEopDataBuffer      = NULL;
++    trap->DmaIdentifyTransaction    = NULL;
++    trap->ThreadIdentifyTransaction = NULL;
++    trap->LockQueuePointer          = (E3_Addr) 0;
++    trap->UnlockQueuePointer        = (E3_Addr) 0;
++
++    /*
++     * Now scan all the transactions received 
++     */
++    for (i = 0; i < trap->NumTransactions ; i++)
++    {
++      E3_IprocTrapHeader_BE *hdrp = &trap->Transactions[i];
++      E3_IprocTrapData_BE   *datap = &trap->DataBuffers[i];
++
++      StatusValid = hdrp->s.TrTypeCntx.s.StatusRegValid != 0;
++      
++      if (StatusValid && hdrp->s.IProcTrapStatus.s.AckSent)   /* Remember if we've sent the ack back */
++          trap->AckSent = 1;
++      
++      if (hdrp->s.TrTypeCntx.s.LastTrappedTrans)              /* Check for EOP */
++      {
++          ASSERT (i == trap->NumTransactions - 1);
++
++          switch (hdrp->s.IProcTrapStatus.Status & E3_IPS_EopType)
++          {
++          case EOP_GOOD:
++              /* if we get an EOP_GOOD then the outputer should have received a PAckOk. */  
++              /* unless it was a flood, in which case someone must have sent an ack */
++              /* but not necessarily us */
++              break;
++
++          case EOP_BADACK:
++              BumpUserStat (ctxt, EopBadAcks);
++
++              /* if we get an EOP_BADACK then the outputer did not receive a PAckOk even if
++               * we sent a PAckOk. We can clear tinfo.AckSent. */
++              if (trap->AckSent == 1)
++              {
++                  PRINTF0 (ctxt, DBG_IPROC, "InspectIProcTrap: Network error destroyed PAckOk\n");
++                  trap->AckSent = 0;
++              }
++              break;
++
++          case EOP_ERROR_RESET:
++              BumpUserStat (ctxt, EopResets);
++
++              /* if we get an EOP_ERROR_RESET then the outputer may or may not have got a PAckOk. */
++              trap->BadTransaction = 1;
++              break;
++
++          default:
++              panic ("InspectIProcTrap: invalid EOP type in status register\n");
++              /* NOTREACHED */
++          }
++          continue;
++      }
++
++      PRINTF2 (ctxt, DBG_IPROC, "InspectIProcTrap: %2d: %s\n", i, IProcTrapString (hdrp, datap));
++      
++      if (! StatusValid)                                      /* We're looking at transactions stored before the trap */
++      {                                                       /* these should only be identifies and lock transactions */
++
++          if (hdrp->s.TrTypeCntx.s.Type & TR_WRITEBLOCK_BIT)
++              panic ("InspectIProcTrap: writeblock transaction found in input trap header before trap occured\n");
++
++          switch (hdrp->s.TrTypeCntx.s.Type & TR_OPCODE_TYPE_MASK)
++          {
++          case TR_LOCKQUEUE & TR_OPCODE_TYPE_MASK:
++              if (trap->LockQueuePointer)                             /* Already seen a LOCKQUEUE transaction in this packet, */
++              {                                               /* the user program should not have done this !! */
++                  ElanException (ctxt, EXCEPTION_BAD_PACKET, INPUT_PROC, trap);
++                  return;
++              }
++
++              trap->LockQueuePointer = (E3_Addr) hdrp->s.TrAddr;      /* Remember the queue pointer in case we need to unlock it */
++              break;
++
++          case TR_DMAIDENTIFY & TR_OPCODE_TYPE_MASK:
++              if (trap->DmaIdentifyTransaction ||             /* Already seen an identify transaction in this packet */
++                  trap->ThreadIdentifyTransaction)            /* the user program should not have done this */
++              {                                                       
++                  ElanException (ctxt, EXCEPTION_BAD_PACKET, INPUT_PROC, trap);
++                  return;
++              }
++              trap->DmaIdentifyTransaction = hdrp;
++              break;
++
++          case TR_THREADIDENTIFY & TR_OPCODE_TYPE_MASK:
++              if (trap->DmaIdentifyTransaction ||             /* Already seen an identify transaction in this packet */
++                  trap->ThreadIdentifyTransaction)            /* the user program should not have done this */
++              {                                                       
++                  ElanException (ctxt, EXCEPTION_BAD_PACKET, INPUT_PROC, trap);
++                  return;
++              }
++              trap->ThreadIdentifyTransaction = hdrp;
++              break;
++              
++          default:
++              panic ("InspectIProcTrap: invalid transaction found in input trap header before trap occured\n");
++              /* NOTREACHED */
++          }
++          continue;
++      }
++
++      if (StatusValid && trap->TrappedTransaction == NULL)    /* Remember the transaction which caused the */
++      {                                                       /* trap */
++          trap->TrappedTransaction = hdrp;
++          trap->TrappedDataBuffer  = datap;
++      }
++
++      if(hdrp->s.IProcTrapStatus.s.BadLength ||
++         ((hdrp->s.IProcTrapStatus.Status & CRC_MASK) == CRC_STATUS_ERROR) ||
++         ((hdrp->s.IProcTrapStatus.Status & CRC_MASK) == CRC_STATUS_BAD))
++      {
++          int j;
++          PRINTF0 (ctxt, DBG_IPROC, "InspectIProcTrap: transaction has a bad crc\n");
++          for (j=0; j<TRANS_DATA_WORDS; j+=4)
++             PRINTF5 (ctxt, DBG_IPROC, "InspectIProcTrap: Data %0d %8x %8x %8x %8x\n",
++                      j, datap->TrData[j], datap->TrData[j+1], datap->TrData[j+2], datap->TrData[j+3]);
++          trap->BadTransaction = 1;
++          continue;
++      }
++      
++      /* No more to do if it's a writeblock transaction */
++      if (hdrp->s.TrTypeCntx.s.Type & TR_WRITEBLOCK_BIT)
++          continue;
++
++      
++      if (GET_STATUS_TRAPTYPE(hdrp->s.IProcTrapStatus) == MI_InputDoTrap &&
++          (hdrp->s.TrTypeCntx.s.Type & TR_WAIT_FOR_EOP) != 0)
++      {
++          /*
++           * This is a wait for eop transaction that has trapped because the inputer
++           * then received a EopError. The next transaction saved should always be an
++           * EopError.
++           */
++          PRINTF0 (ctxt, DBG_IPROC, "InspectIProcTrap: got a trapped WaitForEop transaction due to EopError\n");
++          
++          trap->WaitForEopTransaction = hdrp;
++          trap->WaitForEopDataBuffer  = datap;
++          continue;
++      }
++
++      switch (hdrp->s.TrTypeCntx.s.Type & TR_OPCODE_TYPE_MASK)
++      {
++      case TR_UNLOCKQUEUE & TR_OPCODE_TYPE_MASK:
++          if (trap->UnlockQueuePointer)
++          {
++              ElanException (ctxt, EXCEPTION_BAD_PACKET, INPUT_PROC, trap);
++              return;
++          }
++          trap->UnlockQueuePointer = (E3_Addr) hdrp->s.TrAddr;
++          break;
++      }
++    }
++}
++
++void
++ResolveIProcTrap (ELAN3_CTXT *ctxt, INPUT_TRAP *trap, NETERR_RESOLVER **rvpp)
++{
++    ELAN3_DEV     *dev = ctxt->Device;
++    int           res;
++    unsigned long flags;
++
++    ASSERT (! CTXT_IS_KERNEL (ctxt));
++
++    BumpUserStat (ctxt, IProcTraps);
++
++    InspectIProcTrap (ctxt, trap);
++
++    /*
++     * fixup page fault if we've trapped because of one.
++     */
++    if (trap->FaultSave.s.FaultContext != 0)
++    {
++      /*
++       * If it's a WRITEBLOCK transaction, then see if we remember faulting
++       * before it, and try and prefault in a sensible amount past it.
++       */
++      int                fixedFault = FALSE;
++      INPUT_FAULT_SAVE  *entry;
++      INPUT_FAULT_SAVE **predp;
++      int                npages;
++
++      if ((trap->TrappedTransaction->s.TrTypeCntx.s.Type & TR_WRITEBLOCK_BIT) != 0 && /* a DMA packet */
++          trap->LockQueuePointer == (E3_Addr) 0 &&                                    /* but not a queueing DMA */
++          trap->TrappedTransaction->s.TrAddr != 0)                                    /* and not a DMA to 0 */
++      {
++          spin_lock (&ctxt->InputFaultLock);
++          
++          for (predp = &ctxt->InputFaultList; (entry = *predp)->Next != NULL ; predp = &entry->Next)
++          {
++              if (entry->Addr == trap->TrappedTransaction->s.TrAddr)
++                  break;
++          }
++          
++          *predp = entry->Next;
++          entry->Next = ctxt->InputFaultList;
++          ctxt->InputFaultList = entry;
++          
++          if (entry->Addr == trap->TrappedTransaction->s.TrAddr)
++          {
++              if ((entry->Count <<= 1) > MAX_INPUT_FAULT_PAGES)
++                  entry->Count = MAX_INPUT_FAULT_PAGES;
++          }
++          else
++          {
++              entry->Count = MIN_INPUT_FAULT_PAGES;
++          }
++          
++          entry->Addr = trap->TrappedTransaction->s.TrAddr + (entry->Count * PAGESIZE);
++          npages = entry->Count;
++          
++          spin_unlock (&ctxt->InputFaultLock);
++          
++          if (elan3_pagefault (ctxt, &trap->FaultSave, npages) != ESUCCESS)
++          {
++              PRINTF2 (ctxt, DBG_IPROC, "ResolveIProcTrap: pagefaulting %d pages at %08x - failed\n", 
++                       npages, trap->TrappedTransaction->s.TrAddr);
++          }
++          else
++          {
++              PRINTF2 (ctxt, DBG_IPROC, "ResolveIProcTrap: pagefaulting %d pages at %08x - succeeded\n", 
++                       npages, trap->TrappedTransaction->s.TrAddr);
++              
++              fixedFault = TRUE;
++          }
++      }
++
++      /* Workaround WRITEBLOCK transaction executed when LOCKQUEUE transaction missed */
++      /* the packet will have been nacked */
++      if ((trap->TrappedTransaction->s.TrTypeCntx.s.Type & TR_WRITEBLOCK_BIT) &&      /* a DMA packet */
++          trap->LockQueuePointer == 0 && trap->UnlockQueuePointer &&                  /* a queueing DMA */
++          trap->TrappedTransaction->s.TrAddr == trap->FaultSave.s.FaultAddress)       /* and missed lockqueue */
++      {
++          fixedFault = TRUE;
++      }
++
++      if (! fixedFault)
++      {
++          if ((res = elan3_pagefault (ctxt, &trap->FaultSave, 1)) != ESUCCESS)
++          {
++              PRINTF1 (ctxt, DBG_IPROC, "ResolveIProcTrap: elan3_pagefault failed at %x\n", 
++                       trap->FaultSave.s.FaultAddress);
++              ElanException (ctxt, EXCEPTION_INVALID_ADDR, INPUT_PROC, trap, &trap->FaultSave, res);
++              return;
++          }
++      }
++    }
++
++    if (! trap->AckSent && trap->LockQueuePointer)                    /* Queued DMA */
++    {                                                                 /* The ack was not sent, so the queue will be locked. */
++      SimulateUnlockQueue (ctxt, trap->LockQueuePointer, FALSE);      /* We must unlock it. */
++    }
++
++    if (trap->AckSent && trap->BadTransaction)
++    {
++      if (trap->DmaIdentifyTransaction)
++      {
++          PRINTF0 (ctxt, DBG_IPROC, "ResolveIProcTrap: Dma identify needs network resultion\n");
++
++          BumpStat (dev, DmaIdentifyNetworkErrors);
++          BumpUserStat (ctxt, DmaIdentifyNetworkErrors);
++
++          if (trap->WaitForEopTransaction)
++              PRINTF0 (ctxt, DBG_IPROC, "ResolveIProcTrap: have delayed wait for eop transaction\n");
++      }
++      else if (trap->ThreadIdentifyTransaction)
++      {
++          PRINTF0 (ctxt, DBG_IPROC, "ResolveIProcTrap: Thread identify needs network resolution\n");
++
++          BumpStat (dev, ThreadIdentifyNetworkErrors);
++          BumpUserStat (ctxt, ThreadIdentifyNetworkErrors);
++
++          if (trap->WaitForEopTransaction)
++              PRINTF0 (ctxt, DBG_IPROC, "ResolveIProcTrap: have delayed wait for eop transaction\n");
++      }
++      else
++      {
++          BumpStat (dev, DmaNetworkErrors);
++          BumpUserStat (ctxt, DmaNetworkErrors);
++      }
++    }
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++    
++    if (! trap->AckSent)
++    {
++      PRINTF0 (ctxt, DBG_IPROC, "ResolveIProcTrap: ack not sent, lowering context filter\n");
++
++      trap->State = CTXT_STATE_OK;
++    }
++    else
++    {
++      if (trap->BadTransaction)
++      {
++          PRINTF0 (ctxt, DBG_IPROC, "ResolveIProcTrap: ack sent, waiting on bad transaction\n");
++          trap->State = CTXT_STATE_NETWORK_ERROR;
++      }
++      else
++      {
++          PRINTF0 (ctxt, DBG_IPROC, "ResolveIProcTrap: ack sent, waiting on packet to be re-executed\n");
++          trap->State = CTXT_STATE_NEEDS_RESTART;
++      }
++    }
++
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++
++    if (trap->AckSent && trap->BadTransaction)
++      ElanException (ctxt, EXCEPTION_NETWORK_ERROR, INPUT_PROC, trap, rvpp);
++}
++
++int
++RestartIProcTrap (ELAN3_CTXT *ctxt, INPUT_TRAP *trap)
++{
++    PRINTF1 (ctxt, DBG_IPROC, "RestartIProc: %d transactions\n", trap->NumTransactions);
++
++    if (trap->TrappedTransaction == NULL)                     /* No transaction trapped - probably a network */
++      return (ESUCCESS);                                      /* error */
++
++    while (! trap->TrappedTransaction->s.TrTypeCntx.s.LastTrappedTrans)
++    {
++      E3_IprocTrapHeader_BE *hdrp = trap->TrappedTransaction;
++      E3_IprocTrapData_BE   *datap = trap->TrappedDataBuffer;
++      
++      ASSERT (hdrp->s.TrTypeCntx.s.StatusRegValid != 0);
++
++      PRINTF2 (ctxt, DBG_IPROC, "RestartIProc: TrType=0x%x Status=0x%x\n",
++               hdrp->s.TrTypeCntx.TypeContext, hdrp->s.IProcTrapStatus.Status);
++      
++      if ((hdrp->s.TrTypeCntx.s.Type & TR_WRITEBLOCK_BIT) != 0)
++      {
++          PRINTF1 (ctxt, DBG_IPROC, "RestartIProc: WRITEBLOCK : Addr %x\n", hdrp->s.TrAddr);
++          SimulateBlockWrite (ctxt, hdrp, datap);
++      }
++      else
++      {
++          switch (hdrp->s.TrTypeCntx.s.Type & TR_OPCODE_TYPE_MASK)
++          {
++          case TR_SETEVENT & TR_OPCODE_TYPE_MASK:
++              PRINTF1 (ctxt, DBG_IPROC, "RestartIProc: SETEVENT : %x\n", hdrp->s.TrAddr);
++
++              if (GET_STATUS_TRAPTYPE(hdrp->s.IProcTrapStatus) != MI_InputDoTrap)
++                  FixupEventTrap (ctxt, INPUT_PROC, trap, GET_STATUS_TRAPTYPE(hdrp->s.IProcTrapStatus), &trap->FaultSave, FALSE);
++              else if (hdrp->s.TrAddr)
++              {
++                  if (IssueCommand (ctxt, offsetof (E3_CommandPort, SetEvent), hdrp->s.TrAddr, FALSE) != ISSUE_COMMAND_OK)
++                      return (EAGAIN);
++              }
++              break;
++
++          case TR_WRITEWORD & TR_OPCODE_TYPE_MASK:
++              SimulateWriteWord (ctxt, hdrp, datap);
++              break;
++
++          case TR_WRITEDOUBLEWORD & TR_OPCODE_TYPE_MASK:
++              SimulateWriteDWord (ctxt, hdrp, datap);
++              break;
++              
++          case TR_UNLOCKQUEUE & TR_OPCODE_TYPE_MASK:
++              if (GET_STATUS_TRAPTYPE(hdrp->s.IProcTrapStatus) == MI_InputDoTrap)
++                  ElanException (ctxt, EXCEPTION_BAD_PACKET, INPUT_PROC, trap);
++              else
++              {
++                  switch (GET_STATUS_TRAPTYPE (hdrp->s.IProcTrapStatus))
++                  {
++                  case MI_WaitForUnLockDescRead:
++                      /*
++                       * Fault occured on the read of the queue descriptor - since the ack
++                       * has been sent we need to move the queue on one slot.
++                       */
++                      PRINTF0 (ctxt, DBG_IPROC, "RestartIProc: TR_UNLOCKQUEUE : desc read fault\n");
++
++                      SimulateUnlockQueue (ctxt, trap->LockQueuePointer, TRUE);
++                      
++                      if (IssueCommand (ctxt, offsetof (E3_CommandPort, SetEvent),
++                                        hdrp->s.TrAddr + E3_QUEUE_EVENT_OFFSET, FALSE) != ISSUE_COMMAND_OK)
++                      {
++                          /* Failed to issue setevent to complete queue unlock, since we've already unlocked */
++                          /* the queue, we should "convert" this transaction into a setevent transaction that */
++                          /* hasn't trapped */
++                          PRINTF0 (ctxt, DBG_IPROC, "RestartIProc: could not issue setevent for SimulateUnlockQueue\n");
++
++                          ConvertTransactionToSetEvent (ctxt, hdrp, hdrp->s.TrAddr + E3_QUEUE_EVENT_OFFSET);
++                          return (EAGAIN);
++                      }
++                      break;
++                      
++                  case MI_DoSetEvent:
++                      /*
++                       * Fault occured on either the write to unlock the queue or during 
++                       * processing of the event.  Test the fault address against the
++                       * queue address to find out which - in this case, since the ack
++                       * has been sent we need to move the queue on one slot.
++                       */
++                      if (trap->FaultSave.s.FaultAddress == trap->LockQueuePointer)
++                      {
++                          PRINTF0 (ctxt, DBG_IPROC, "RestartIProc: fixed unlock queue write to unlock fault\n");
++
++                          SimulateUnlockQueue (ctxt, trap->LockQueuePointer, TRUE);
++                          
++                          if (IssueCommand (ctxt, offsetof (E3_CommandPort, SetEvent),
++                                            hdrp->s.TrAddr + E3_QUEUE_EVENT_OFFSET, FALSE) != ISSUE_COMMAND_OK)
++                          {
++                              /* Failed to issue setevent to complete queue unlock, since we've already unlocked */
++                              /* the queue, we should "convert" this transaction into a setevent transaction that */
++                              /* hasn't trapped */
++                              PRINTF0 (ctxt, DBG_IPROC, "RestartIProc: could not issue setevent for SimulateUnlockQueue\n");
++                              
++                              ConvertTransactionToSetEvent (ctxt, hdrp, hdrp->s.TrAddr + E3_QUEUE_EVENT_OFFSET);
++                              return (EFAIL);
++                          }
++                          break;
++                      }
++                      /*DROPTHROUGH*/
++                      
++                  default:
++                      FixupEventTrap (ctxt, INPUT_PROC, trap, GET_STATUS_TRAPTYPE (hdrp->s.IProcTrapStatus),
++                                      &trap->FaultSave, FALSE);
++                      break;
++                  }
++                  trap->LockQueuePointer = trap->UnlockQueuePointer = 0;
++              }
++              break;
++
++          case TR_SENDDISCARD & TR_OPCODE_TYPE_MASK:
++              /* Just ignore send-discard transactions */
++              PRINTF0 (ctxt, DBG_IPROC, "RestartIProc: ignore SENDDISCARD\n");
++              break;
++
++          case TR_REMOTEDMA & TR_OPCODE_TYPE_MASK:
++              PRINTF0 (ctxt, DBG_IPROC, "RestartIProc: REMOTEDMA\n");         
++
++              /* modify the dma type since it will still be a "read" dma */
++              ((E3_DMA_BE *) datap)->s.dma_type &= ~(DMA_TYPE_READ | E3_DMA_CONTEXT_MASK);
++              ((E3_DMA_BE *) datap)->s.dma_type |= DMA_TYPE_ISREMOTE;
++
++              RestartDmaDesc (ctxt, (E3_DMA_BE *) datap);
++              break;
++
++          case TR_TRACEROUTE & TR_OPCODE_TYPE_MASK:
++              PRINTF0 (ctxt, DBG_IPROC, "RestartIProc: TRACEROUTE\n");
++              SimulateTraceRoute (ctxt, hdrp, datap);
++              break;
++
++          default:
++              ElanException (ctxt, EXCEPTION_BAD_PACKET, INPUT_PROC, trap);
++              break;
++          }
++      }
++
++      /*
++       * We've successfully processed this transaction, so move onto the 
++       * next one.
++       */
++      trap->TrappedTransaction++;
++      trap->TrappedDataBuffer++;
++    }
++    
++    return (ESUCCESS);
++}
++
++static void
++ConvertTransactionToSetEvent (ELAN3_CTXT *ctxt, E3_IprocTrapHeader_BE *hdrp, E3_Addr Addr)
++{
++    hdrp->s.TrTypeCntx.s.Type           = TR_SETEVENT;
++    hdrp->s.TrTypeCntx.s.StatusRegValid = 0;
++    hdrp->s.TrAddr                      = Addr;
++}
++
++void
++SimulateBlockWrite (ELAN3_CTXT *ctxt, E3_IprocTrapHeader_BE *hdrp, E3_IprocTrapData_BE *datap)
++{
++    void     *saddr  = (void *) ((unsigned long) datap + (hdrp->s.TrAddr & 0x3f));
++    unsigned  nbytes = (hdrp->s.TrTypeCntx.s.Type) & TR_PARTSIZE_MASK;
++    int       i;
++
++    if (nbytes == 0)
++      nbytes = sizeof (E3_IprocTrapData_BE);
++
++    if (ELAN3_OP_START_FAULT_CHECK (ctxt))
++    {
++      ELAN3_OP_END_FAULT_CHECK (ctxt);
++
++      PRINTF1 (ctxt, DBG_IPROC, "SimulateBlockWrite: faulted at %x\n", hdrp->s.TrAddr);
++      ElanException (ctxt, EXCEPTION_FAULTED, INPUT_PROC, NULL, hdrp->s.TrAddr);
++      return;
++    }
++
++    /*
++     * NOTE: since the block copy could be to sdram, we issue the writes backwards,
++     *       except we MUST ensure that the last item in the block is written last.
++     */
++    switch (((hdrp->s.TrTypeCntx.s.Type) >> TR_TYPE_SHIFT) & TR_TYPE_MASK)
++    {
++    case TR_TYPE_BYTE:                                                /* 8 bit */
++      for (i = nbytes - (2*sizeof (E3_uint8)); i >= 0; i -= sizeof (E3_uint8))
++          ELAN3_OP_STORE8 (ctxt, hdrp->s.TrAddr + i, ((E3_uint8 *) saddr)[i]);
++      i = nbytes - sizeof (E3_uint8);
++      ELAN3_OP_STORE8 (ctxt, hdrp->s.TrAddr + i, ((E3_uint8 *) saddr)[i]);
++      break;
++      
++    case TR_TYPE_SHORT:                                               /* 16 bit */
++      for (i = nbytes - (2*sizeof (E3_uint16)); i >= 0; i -= sizeof (E3_uint16))
++      ELAN3_OP_STORE16 (ctxt, hdrp->s.TrAddr + i, ((E3_uint16 *) saddr)[i]);
++      i = nbytes - sizeof (E3_uint16);
++      ELAN3_OP_STORE16 (ctxt, hdrp->s.TrAddr + i, ((E3_uint16 *) saddr)[i]);
++      break;
++      
++    case TR_TYPE_WORD:                                                /* 32 bit */
++      for (i = nbytes - (2*sizeof (E3_uint32)); i >= 0; i -= sizeof (E3_uint32))
++          ELAN3_OP_STORE32 (ctxt, hdrp->s.TrAddr + i, ((E3_uint32 *) saddr)[i]);
++      i = nbytes - sizeof (E3_uint32);
++      ELAN3_OP_STORE32 (ctxt, hdrp->s.TrAddr + i, ((E3_uint32 *) saddr)[i]);
++      break;
++      
++    case TR_TYPE_DWORD:                                               /* 64 bit  */
++      for (i = nbytes - (2*sizeof (E3_uint64)); i >= 0; i -= sizeof (E3_uint64))
++          ELAN3_OP_STORE64 (ctxt, hdrp->s.TrAddr + i, ((E3_uint64 *) saddr)[i]);
++      i = nbytes - sizeof (E3_uint64);
++      ELAN3_OP_STORE64 (ctxt, hdrp->s.TrAddr + i, ((E3_uint64 *) saddr)[i]);
++      break;
++    }
++    ELAN3_OP_END_FAULT_CHECK (ctxt);
++}
++
++void
++SimulateWriteWord (ELAN3_CTXT *ctxt, E3_IprocTrapHeader_BE *hdrp, E3_IprocTrapData_BE *datap)
++{
++    if (ELAN3_OP_START_FAULT_CHECK (ctxt))
++    {
++      ELAN3_OP_END_FAULT_CHECK (ctxt);
++
++      PRINTF1 (ctxt, DBG_IPROC, "SimulateWriteWord: faulted at %x\n", hdrp->s.TrAddr);
++      ElanException (ctxt, EXCEPTION_FAULTED, INPUT_PROC, NULL, hdrp->s.TrAddr);
++      return;
++    }
++
++    ELAN3_OP_STORE32 (ctxt, hdrp->s.TrAddr, ((E3_uint32 *) datap)[WordEndianFlip]);
++    ELAN3_OP_END_FAULT_CHECK (ctxt);
++}
++
++void
++SimulateWriteDWord (ELAN3_CTXT *ctxt, E3_IprocTrapHeader_BE *hdrp, E3_IprocTrapData_BE *datap)
++{
++    if (ELAN3_OP_START_FAULT_CHECK (ctxt))
++    {
++      ELAN3_OP_END_FAULT_CHECK (ctxt);
++
++      PRINTF1 (ctxt, DBG_IPROC, "SimulateWriteDWord: faulted at %x\n", hdrp->s.TrAddr);
++      ElanException (ctxt, EXCEPTION_FAULTED, INPUT_PROC, NULL, hdrp->s.TrAddr);
++      return;
++    }
++
++    ELAN3_OP_STORE64 (ctxt, hdrp->s.TrAddr, ((E3_uint64 *) datap)[0]);
++    ELAN3_OP_END_FAULT_CHECK (ctxt);
++}
++
++void
++SimulateTraceRoute (ELAN3_CTXT *ctxt, E3_IprocTrapHeader_BE *hdrp, E3_IprocTrapData_BE *datap)
++{
++    E3_uint32 *saddr  = (E3_uint32 *) ((unsigned long) datap + (hdrp->s.TrAddr & 0x3f));
++    unsigned   nwords = TrSizeTable[(hdrp->s.TrTypeCntx.s.Type >> TR_SIZE_SHIFT) & TR_SIZE_MASK] / sizeof (E3_uint32);
++    int        i;
++
++    if (ELAN3_OP_START_FAULT_CHECK (ctxt))
++    {
++      ELAN3_OP_END_FAULT_CHECK (ctxt);
++
++      PRINTF1 (ctxt, DBG_IPROC, "SimulateTraceRoute: faulted at %x\n", hdrp->s.TrAddr);
++      ElanException (ctxt, EXCEPTION_FAULTED, INPUT_PROC, NULL, hdrp->s.TrAddr);
++      return;
++    }
++    
++    for (i = nwords-2; i >= 0; i--)
++      ELAN3_OP_STORE32 (ctxt, hdrp->s.TrAddr + (i * sizeof (E3_uint32)), saddr[i ^ WordEndianFlip]);
++
++    i = nwords-1;
++    ELAN3_OP_STORE32 (ctxt, hdrp->s.TrAddr + (i * sizeof (E3_uint32)), saddr[i ^ WordEndianFlip]);
++
++    ELAN3_OP_END_FAULT_CHECK (ctxt);
++}
++
++void
++SimulateUnlockQueue (ELAN3_CTXT *ctxt, E3_Addr QueuePointer, int SentAck)
++{
++    E3_uint32 QueueLock;
++    E3_Addr   QueueBPTR;
++    E3_Addr   QueueFPTR;
++    E3_uint64 QueueStateAndBPTR;
++
++    if (ELAN3_OP_START_FAULT_CHECK (ctxt))
++    {
++      ELAN3_OP_END_FAULT_CHECK (ctxt);
++
++      PRINTF1 (ctxt, DBG_IPROC, "UnlockQueue: faulted with QueuePointer %x\n", QueuePointer);
++      ElanException (ctxt, EXCEPTION_FAULTED, INPUT_PROC, NULL, QueuePointer);
++      return;
++    }
++    
++    if (SentAck)
++    {
++      QueueBPTR = ELAN3_OP_LOAD32 (ctxt, QueuePointer + offsetof (E3_Queue, q_bptr));
++      QueueFPTR = ELAN3_OP_LOAD32 (ctxt, QueuePointer + offsetof (E3_Queue, q_fptr));
++
++      if (QueueBPTR == ELAN3_OP_LOAD32 (ctxt, QueuePointer + offsetof (E3_Queue, q_top)))     /* move on back pointer */
++          QueueBPTR = ELAN3_OP_LOAD32 (ctxt, QueuePointer + offsetof (E3_Queue, q_base));
++      else
++          QueueBPTR += ELAN3_OP_LOAD32 (ctxt, QueuePointer + offsetof (E3_Queue, q_size));
++      
++      QueueLock = ELAN3_OP_LOAD32 (ctxt, QueuePointer + offsetof (E3_Queue, q_state));
++
++      if (QueueBPTR == QueueFPTR)                             /* and set full bit if fptr == bptr */
++          QueueLock |= E3_QUEUE_FULL;
++      
++      QueueLock &= ~E3_QUEUE_LOCKED;
++      
++      QueueStateAndBPTR = (E3_uint64)QueueLock << 32 | QueueBPTR;
++
++      ELAN3_OP_STORE64 (ctxt, QueuePointer + offsetof (E3_Queue, q_state), QueueStateAndBPTR);
++    }
++    else
++    {
++      QueueLock = ELAN3_OP_LOAD32 (ctxt, QueuePointer + offsetof (E3_Queue, q_state));
++
++      QueueLock &= ~E3_QUEUE_LOCKED;
++      
++      ELAN3_OP_STORE32 (ctxt, QueuePointer + offsetof (E3_Queue, q_state), QueueLock);
++    }
++
++    no_fault();
++}
++
++static void
++BumpInputterStats (ELAN3_DEV *dev, E3_IprocTrapHeader_BE *hdrp)
++{
++    if (hdrp->s.TrTypeCntx.s.LastTrappedTrans)                        /* EOP */
++    {
++      switch (hdrp->s.IProcTrapStatus.Status & E3_IPS_EopType)
++      {
++      case EOP_BADACK:
++          BumpStat (dev, EopBadAcks);
++          break;
++      case EOP_ERROR_RESET:
++          BumpStat (dev, EopResets);
++          break;
++      }
++    }
++    else if (hdrp->s.TrTypeCntx.s.StatusRegValid)
++    {
++      /*
++       * Errors are tested in order of badness. i.e. badlength will prevent a BadCrc and so on...
++       */
++      if (hdrp->s.IProcTrapStatus.s.BadLength)
++          BumpStat (dev, InputterBadLength);
++      else if ((hdrp->s.IProcTrapStatus.Status & CRC_MASK) == CRC_STATUS_BAD)
++          BumpStat (dev, InputterCRCBad);
++      else if ((hdrp->s.IProcTrapStatus.Status & CRC_MASK) == CRC_STATUS_ERROR)
++          BumpStat (dev, InputterCRCErrors);
++      else if ((hdrp->s.IProcTrapStatus.Status & CRC_MASK) == CRC_STATUS_DISCARD)
++          BumpStat (dev, InputterCRCDiscards);
++    }
++}
++
++char *
++IProcTrapString (E3_IprocTrapHeader_BE *hdrp, E3_IprocTrapData_BE *datap)
++{
++    static char buffer[256];
++    static char typeString[256];
++    static char statusString[256];
++    char *ptr;
++    E3_Addr     Addr        = hdrp->s.TrAddr;
++    E3_uint32   Type        = hdrp->s.TrTypeCntx.s.Type;
++    E3_uint32   Context     = hdrp->s.TrTypeCntx.s.Context;
++    E3_uint32   StatusValid = hdrp->s.TrTypeCntx.s.StatusRegValid;
++    
++    if (hdrp->s.TrTypeCntx.s.LastTrappedTrans)
++    {
++      switch (hdrp->s.IProcTrapStatus.Status & E3_IPS_EopType)
++      {
++      case EOP_GOOD:          sprintf (typeString, "EOP GOOD"); break;
++      case EOP_BADACK:        sprintf (typeString, "EOP BADACK"); break;
++      case EOP_ERROR_RESET:   sprintf (typeString, "EOP ERROR RESET"); break;
++      default:                sprintf (typeString, "EOP - bad status"); break;
++      }
++      sprintf (buffer, "%15s Cntx=%08x", typeString, Context);
++    }
++    else
++    {
++      if (Type & TR_WRITEBLOCK_BIT)
++      {
++          switch ((Type >> TR_TYPE_SHIFT) & TR_TYPE_MASK)
++          {
++          case TR_TYPE_BYTE:  ptr = "Byte";    break;
++          case TR_TYPE_SHORT: ptr = "Short";   break;
++          case TR_TYPE_WORD:  ptr = "Word";    break;
++          case TR_TYPE_DWORD: ptr = "Double";  break;
++          default:            ptr = "Unknown"; break;
++          }
++          
++          sprintf (typeString, "WriteBlock Type=%s Size=%2d", ptr, Type & TR_PARTSIZE_MASK);
++      }
++      else
++      {
++          switch (Type & TR_OPCODE_TYPE_MASK)
++          {
++          case TR_SETEVENT & TR_OPCODE_TYPE_MASK:             sprintf (typeString, "Setevent"); break;
++          case TR_REMOTEDMA & TR_OPCODE_TYPE_MASK:            sprintf (typeString, "Remote DMA"); break;
++          case TR_LOCKQUEUE & TR_OPCODE_TYPE_MASK:            sprintf (typeString, "Lock Queue"); break;
++          case TR_UNLOCKQUEUE & TR_OPCODE_TYPE_MASK:          sprintf (typeString, "Unlock Queue"); break;
++          case TR_SENDDISCARD & TR_OPCODE_TYPE_MASK:          sprintf (typeString, "Send Discard"); break;
++          case TR_DMAIDENTIFY & TR_OPCODE_TYPE_MASK:          sprintf (typeString, "DMA Identify"); break;
++          case TR_THREADIDENTIFY & TR_OPCODE_TYPE_MASK:       sprintf (typeString, "Thread Identify"); break;
++          case TR_GTE & TR_OPCODE_TYPE_MASK:                  sprintf (typeString, "GTE"); break;
++          case TR_LT & TR_OPCODE_TYPE_MASK:                   sprintf (typeString, "LT"); break;
++          case TR_EQ & TR_OPCODE_TYPE_MASK:                   sprintf (typeString, "EQ"); break;
++          case TR_NEQ & TR_OPCODE_TYPE_MASK:                  sprintf (typeString, "NEQ"); break;
++          case TR_WRITEWORD & TR_OPCODE_TYPE_MASK:            sprintf (typeString, "Write Word"); break;
++          case TR_WRITEDOUBLEWORD & TR_OPCODE_TYPE_MASK:      sprintf (typeString, "Write Double"); break;
++          case TR_ATOMICADDWORD & TR_OPCODE_TYPE_MASK:        sprintf (typeString, "Atomic Add"); break;
++          case TR_TESTANDWRITE & TR_OPCODE_TYPE_MASK:         sprintf (typeString, "Test and Write"); break;
++          default:                                            sprintf (typeString, "Type=%d", Type & TR_OPCODE_TYPE_MASK); break;
++          }
++      }
++      sprintf (buffer, "%15s Addr=%08x Cntx=%08x", typeString, Addr, Context);
++      /*(Type & TR_SENDACK)      ? " Sendack" : "", */
++      /*(Type & TR_LAST_TRANS)   ? " LastTrans" : "", */
++      /*(Type & TR_WAIT_FOR_EOP) ? " WaitForEop" : ""); */
++    }
++    
++    if (StatusValid)
++    {
++      sprintf (statusString, " Type=%s %x", MiToName (hdrp->s.IProcTrapStatus.s.TrapType), hdrp->s.IProcTrapStatus.Status);
++      strcat (buffer, statusString);
++
++      if (hdrp->s.IProcTrapStatus.s.BadLength)
++          strcat (buffer, " BadLength");
++      switch (hdrp->s.IProcTrapStatus.Status & CRC_MASK)
++      {
++      case CRC_STATUS_DISCARD:
++          strcat (buffer, " CRC Discard");
++          break;
++      case CRC_STATUS_ERROR:
++          strcat (buffer, " CRC Error");
++          break;
++
++      case CRC_STATUS_BAD:
++          strcat (buffer, " CRC Bad");
++          break;
++      }
++    }
++
++    return (buffer);
++}
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan3/Makefile
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan3/Makefile  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan3/Makefile       2005-05-11 12:10:12.418935616 -0400
+@@ -0,0 +1,15 @@
++#
++# Makefile for Quadrics QsNet
++#
++# Copyright (c) 2002-2004 Quadrics Ltd
++#
++# File: drivers/net/qsnet/elan3/Makefile
++#
++
++
++#
++
++obj-$(CONFIG_ELAN3)   += elan3.o
++elan3-objs    := context.o cproc.o dproc.o elandebug.o elandev_generic.o elansyscall.o eventcookie.o iproc.o sdram.o minames.o network_error.o route_table.o tproc.o tprocinsts.o routecheck.o virtual_process.o elan3ops.o context_linux.o elandev_linux.o procfs_linux.o tproc_linux.o elan3mmu_generic.o elan3mmu_linux.o
++
++EXTRA_CFLAGS          +=  -DDEBUG -DDEBUG_PRINTF -DDEBUG_ASSERT
+Index: linux-2.6.5/drivers/net/qsnet/elan3/Makefile.conf
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan3/Makefile.conf     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan3/Makefile.conf  2005-05-11 12:10:12.419935464 -0400
+@@ -0,0 +1,10 @@
++# Flags for generating QsNet Linux Kernel Makefiles
++MODNAME               =       elan3.o
++MODULENAME    =       elan3
++KOBJFILES     =       context.o cproc.o dproc.o elandebug.o elandev_generic.o elansyscall.o eventcookie.o iproc.o sdram.o minames.o network_error.o route_table.o tproc.o tprocinsts.o routecheck.o virtual_process.o elan3ops.o context_linux.o elandev_linux.o procfs_linux.o tproc_linux.o elan3mmu_generic.o elan3mmu_linux.o
++EXPORT_KOBJS  =       elandev_linux.o procfs_linux.o
++CONFIG_NAME   =       CONFIG_ELAN3
++SGALFC                =       
++# EXTRALINES START
++
++# EXTRALINES END
+Index: linux-2.6.5/drivers/net/qsnet/elan3/minames.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan3/minames.c 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan3/minames.c      2005-05-11 12:10:12.419935464 -0400
+@@ -0,0 +1,38 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: minames.c,v 1.12 2003/06/07 15:57:49 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/os/minames.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <elan3/urom_addrs.h>
++
++caddr_t
++MiToName (int mi)
++{
++    static char space[32];
++    static struct {
++      int   mi;
++      char *name;
++    } info[] = {
++#include <elan3/minames.h>
++    };
++    register int i;
++
++
++    for (i = 0; i < sizeof(info)/sizeof(info[0]); i++)
++      if (info[i].mi == mi)
++          return (info[i].name);
++    sprintf (space, "MI %x", mi);
++    return (space);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan3/network_error.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan3/network_error.c   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan3/network_error.c        2005-05-11 12:10:12.420935312 -0400
+@@ -0,0 +1,777 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: network_error.c,v 1.32.2.1 2004/10/28 11:54:57 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/os/network_error.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <qsnet/kthread.h>
++
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elanvp.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanctxt.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elandebug.h>
++
++#ifdef DIGITAL_UNIX
++#include <sys/cred.h>
++#include <sys/mbuf.h>
++#include <sys/utsname.h>
++#include <net/if.h>
++#include <netinet/in.h>
++#include <netinet/in_var.h>
++
++#include <rpc/types.h>
++#include <rpc/auth.h>
++#include <rpc/xdr.h>
++#include <rpc/clnt.h>
++
++typedef xdrproc_t kxdrproc_t;
++#endif
++
++#ifdef LINUX
++#include <linux/sunrpc/types.h>
++#include <linux/sunrpc/auth.h>
++#include <linux/sunrpc/xdr.h>
++#include <linux/sunrpc/clnt.h>
++
++#include <linux/utsname.h>
++#define SYS_NMLN      __NEW_UTS_LEN
++#endif
++
++#include <elan3/neterr_rpc.h>
++
++spinlock_t      ResolveRequestLock;
++kcondvar_t      ResolveRequestWait;
++
++NETERR_RESOLVER  *ResolveRequestHead;
++NETERR_RESOLVER **ResolveRequestTailp = &ResolveRequestHead;
++int             ResolveRequestCount;
++int             ResolveRequestThreads;
++int             ResolveRequestMaxThreads = 4;
++int             ResolveRequestTimeout = 60;
++
++typedef struct neterr_server
++{
++    struct neterr_server *Next;
++    struct neterr_server *Prev;
++    unsigned            ElanId;
++
++    char               *Name;
++    int                         RefCount;
++    struct sockaddr_in    Addr;
++} NETERR_SERVER;
++
++#define NETERR_HASH_ENTRIES   64
++#define NETERR_HASH(elanid)   (((unsigned) elanid) % NETERR_HASH_ENTRIES)
++NETERR_SERVER *NeterrServerHash[NETERR_HASH_ENTRIES];
++kmutex_t       NeterrServerLock;
++
++static NETERR_SERVER *FindNeterrServer (int elanId);
++static void           DereferenceNeterrServer (NETERR_SERVER *server);
++static int            CallNeterrServer (NETERR_SERVER *server, NETERR_MSG *msg);
++
++void
++InitialiseNetworkErrorResolver ()
++{
++    spin_lock_init (&ResolveRequestLock);
++    kcondvar_init (&ResolveRequestWait);
++    
++    ResolveRequestHead  = NULL;
++    ResolveRequestTailp = &ResolveRequestHead;
++
++    kmutex_init (&NeterrServerLock);
++}
++
++void
++FinaliseNetworkErrorResolver ()
++{
++    spin_lock_destroy (&ResolveRequestLock);
++    kcondvar_destroy (&ResolveRequestWait);
++    
++    kmutex_destroy (&NeterrServerLock);
++}
++
++static NETERR_RESOLVER *
++AllocateNetworkErrorResolver (void)
++{
++    NETERR_RESOLVER *rvp;
++
++    KMEM_ZALLOC (rvp, NETERR_RESOLVER *, sizeof (NETERR_RESOLVER), TRUE);
++    spin_lock_init (&rvp->Lock);
++
++    return (rvp);
++}
++
++void
++FreeNetworkErrorResolver (NETERR_RESOLVER *rvp)
++{
++    spin_lock_destroy (&rvp->Lock);
++    KMEM_FREE (rvp, sizeof (NETERR_RESOLVER));
++}
++
++static void
++elan3_neterr_resolver (void)
++{
++    NETERR_RESOLVER *rvp;
++    NETERR_SERVER   *server;
++    int                    status;
++    unsigned long    flags;
++
++    kernel_thread_init("elan3_neterr_resolver");
++    spin_lock (&ResolveRequestLock);
++
++    while ((rvp = ResolveRequestHead) != NULL)
++    {
++      if ((ResolveRequestHead = rvp->Next) == NULL)
++          ResolveRequestTailp = &ResolveRequestHead;
++      
++      spin_unlock (&ResolveRequestLock);
++
++      PRINTF1 (DBG_DEVICE, DBG_NETERR, "elan3_neterr_resolver: rvp = %p\n", rvp);
++      PRINTF1 (DBG_DEVICE, DBG_NETERR, "                      Rail          %d\n", rvp->Message.Rail);
++      PRINTF1 (DBG_DEVICE, DBG_NETERR, "                      SrcCapability %s\n", CapabilityString (&rvp->Message.SrcCapability));
++      PRINTF1 (DBG_DEVICE, DBG_NETERR, "                      DstCapability %s\n", CapabilityString (&rvp->Message.DstCapability));
++      PRINTF1 (DBG_DEVICE, DBG_NETERR, "                      CookieAddr    %08x\n", rvp->Message.CookieAddr);
++      PRINTF1 (DBG_DEVICE, DBG_NETERR, "                      CookieVProc   %08x\n", rvp->Message.CookieVProc);
++      PRINTF1 (DBG_DEVICE, DBG_NETERR, "                      NextCookie    %08x\n", rvp->Message.NextCookie);
++      PRINTF1 (DBG_DEVICE, DBG_NETERR, "                      WaitForEop    %08x\n", rvp->Message.WaitForEop);
++      
++      if ((server = FindNeterrServer (rvp->Location.loc_node)) == NULL)
++          status = ECONNREFUSED;
++      else if (ResolveRequestTimeout && ((int)(lbolt - rvp->Timestamp)) > (ResolveRequestTimeout*HZ))
++      {
++          printk ("elan_neterr: rpc to '%s' timedout - context %d killed\n", server->Name, rvp->Message.SrcCapability.cap_mycontext);
++          status = ECONNABORTED;
++      }
++      else
++      {
++          status = CallNeterrServer (server, &rvp->Message);
++
++          DereferenceNeterrServer (server);
++      }
++      
++      if ((status == EINTR || status == ETIMEDOUT) && rvp->Ctxt != NULL)
++      {
++          PRINTF1 (DBG_DEVICE, DBG_NETERR, "elan3_neterr_resolver: retry rvp=%p\n", rvp);
++          spin_lock (&ResolveRequestLock);
++          rvp->Next = NULL;
++          *ResolveRequestTailp = rvp;
++          ResolveRequestTailp = &rvp->Next;
++      }
++      else
++      {
++          rvp->Status = status;
++          
++          spin_lock (&rvp->Lock);
++          
++          if (rvp->Ctxt != NULL)
++          {
++              PRINTF2 (rvp->Ctxt, DBG_NETERR, "elan3_neterr_resolver: completing rvp %p for ctxt %p\n", rvp, rvp->Ctxt);
++              spin_lock_irqsave (&rvp->Ctxt->Device->IntrLock, flags);
++              
++              rvp->Completed = TRUE;
++              
++              kcondvar_wakeupall (&rvp->Ctxt->Wait, &rvp->Ctxt->Device->IntrLock);
++              
++              /*
++               * drop the locks out of order since the rvp can get freeed
++               * as soon as we drop the IntrLock - so cannot reference the
++               * rvp after this.
++               */
++              
++              spin_unlock (&rvp->Lock);
++              spin_unlock_irqrestore (&rvp->Ctxt->Device->IntrLock, flags);
++          }
++          else
++          {
++              PRINTF2 (DBG_DEVICE, DBG_NETERR, "elan3_neterr_resolver: completing rvp %p for deceased ctxt %p\n", rvp, rvp->Ctxt);
++              spin_unlock (&rvp->Lock);
++              FreeNetworkErrorResolver (rvp);
++          }
++          
++          spin_lock (&ResolveRequestLock);
++          ResolveRequestCount--;
++      }
++    }
++
++    ResolveRequestThreads--;
++
++    spin_unlock (&ResolveRequestLock);
++    kernel_thread_exit();
++}
++
++int
++QueueNetworkErrorResolver (ELAN3_CTXT *ctxt, INPUT_TRAP *trap, NETERR_RESOLVER **rvpp)
++{
++    int                          isdma   = trap->DmaIdentifyTransaction != NULL;
++    E3_IprocTrapHeader_BE *hdrp    = isdma ? trap->DmaIdentifyTransaction : trap->ThreadIdentifyTransaction;
++    E3_uint32              process = isdma ? (hdrp->s.TrAddr & 0xFFFF) : (hdrp->s.TrData0 & 0xFFFF);
++    NETERR_RESOLVER       *rvp;
++
++    PRINTF2 (ctxt, DBG_NETERR, "QueueNetworkErrorResolver: process = %d %s\n", process, isdma ? "(dma)" : "(thread)");
++
++    if ((rvp = AllocateNetworkErrorResolver()) == NULL)
++    {
++      PRINTF0 (ctxt, DBG_NETERR, "QueueNetworkErrorResolver: cannot allocate resolver\n");
++      return (ENOMEM);
++    }
++
++    rvp->Message.Rail = ctxt->Device->Devinfo.dev_rail;
++
++    krwlock_read (&ctxt->VpLock);
++    rvp->Location = ProcessToLocation (ctxt, NULL, process, &rvp->Message.SrcCapability);
++    krwlock_done (&ctxt->VpLock);
++
++    if (rvp->Location.loc_node == ELAN3_INVALID_NODE)
++    {
++      PRINTF0 (ctxt, DBG_NETERR, "QueueNetworkErrorResolver: invalid elan id\n");
++
++      FreeNetworkErrorResolver (rvp);
++      return (EINVAL);
++    }
++
++    rvp->Message.DstCapability = ctxt->Capability;
++    rvp->Message.DstProcess    = elan3_process (ctxt);
++    rvp->Message.WaitForEop    = (trap->WaitForEopTransaction != NULL);
++
++    if (isdma)
++    {
++      rvp->Message.CookieAddr  = 0;
++      rvp->Message.CookieVProc = hdrp->s.TrAddr;
++      rvp->Message.NextCookie  = 0;
++    }
++    else
++    {
++      rvp->Message.CookieAddr  = hdrp->s.TrAddr;
++      rvp->Message.CookieVProc = hdrp->s.TrData0;
++      rvp->Message.NextCookie  = hdrp->s.TrData1;
++    }
++
++    rvp->Completed = FALSE;
++    rvp->Ctxt      = ctxt;
++    rvp->Timestamp = lbolt;
++
++    spin_lock (&ResolveRequestLock);
++
++    rvp->Next = NULL;
++    *ResolveRequestTailp = rvp;
++    ResolveRequestTailp = &rvp->Next;
++    ResolveRequestCount++;
++
++    kcondvar_wakeupone (&ResolveRequestWait, &ResolveRequestLock);
++
++    if (ResolveRequestCount < ResolveRequestThreads || ResolveRequestThreads >= ResolveRequestMaxThreads)
++      spin_unlock (&ResolveRequestLock);
++    else
++    {
++      ResolveRequestThreads++;
++
++      spin_unlock (&ResolveRequestLock);
++      if (kernel_thread_create (elan3_neterr_resolver, NULL) == NULL)
++      {
++          spin_lock (&ResolveRequestLock);
++          ResolveRequestThreads--;
++          spin_unlock (&ResolveRequestLock);
++          
++          if (ResolveRequestThreads == 0)
++          {
++              PRINTF0 (ctxt, DBG_NETERR, "QueueNetworkErrorResolver: cannot thread pool\n");
++
++              FreeNetworkErrorResolver (rvp);
++              return (ENOMEM);
++          }
++      }
++    }
++
++    *rvpp = rvp;
++    return (ESUCCESS);
++}
++
++void
++CancelNetworkErrorResolver (NETERR_RESOLVER *rvp)
++{
++    spin_lock (&rvp->Lock);
++
++    PRINTF2 (rvp->Ctxt, DBG_NETERR, "CancelNetworkErrorResolver: rvp=%p %s\n", rvp, rvp->Completed ? "Completed" : "Pending");
++
++    if (rvp->Completed)
++    {
++      spin_unlock (&rvp->Lock);
++      FreeNetworkErrorResolver (rvp);
++    }
++    else
++    {
++      rvp->Ctxt = NULL;
++      spin_unlock (&rvp->Lock);
++    }
++}
++
++static NETERR_FIXUP *
++AllocateNetworkErrorFixup (void)
++{
++    NETERR_FIXUP *nef;
++
++    KMEM_ZALLOC (nef, NETERR_FIXUP *, sizeof (NETERR_FIXUP), TRUE);
++
++    if (nef == (NETERR_FIXUP *) NULL)
++      return (NULL);
++
++    kcondvar_init (&nef->Wait);
++
++    return (nef);
++}
++
++static void
++FreeNetworkErrorFixup (NETERR_FIXUP *nef)
++{
++    kcondvar_destroy (&nef->Wait);
++    KMEM_FREE (nef, sizeof (NETERR_FIXUP));
++}
++
++int
++ExecuteNetworkErrorFixup (NETERR_MSG *msg)
++{
++    ELAN3_DEV      *dev;
++    ELAN3_CTXT          *ctxt;
++    NETERR_FIXUP  *nef;
++    NETERR_FIXUP **predp;
++    int                  rc;
++    unsigned long  flags;
++
++    PRINTF1 (DBG_DEVICE, DBG_NETERR, "ExecuteNetworkErrorFixup: msg = %p\n", msg);
++    PRINTF1 (DBG_DEVICE, DBG_NETERR, "                      Rail          %d\n", msg->Rail);
++    PRINTF1 (DBG_DEVICE, DBG_NETERR, "                      SrcCapability %s\n", CapabilityString (&msg->SrcCapability));
++    PRINTF1 (DBG_DEVICE, DBG_NETERR, "                      DstCapability %s\n", CapabilityString (&msg->DstCapability));
++    PRINTF1 (DBG_DEVICE, DBG_NETERR, "                      CookieAddr    %08x\n", msg->CookieAddr);
++    PRINTF1 (DBG_DEVICE, DBG_NETERR, "                      CookieVProc   %08x\n", msg->CookieVProc);
++    PRINTF1 (DBG_DEVICE, DBG_NETERR, "                      NextCookie    %08x\n", msg->NextCookie);
++    PRINTF1 (DBG_DEVICE, DBG_NETERR, "                      WaitForEop    %08x\n", msg->WaitForEop);
++      
++    if ((dev = elan3_device (msg->Rail)) == NULL)
++      return (ESRCH);
++
++    if ((nef = AllocateNetworkErrorFixup()) == NULL)
++      return (ENOMEM);
++
++    if (nef == (NETERR_FIXUP *) NULL)
++      return (ENOMEM);
++    
++    bcopy (msg, &nef->Message, sizeof (NETERR_MSG));
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++    
++    ctxt = ELAN3_DEV_CTX_TABLE(dev, msg->SrcCapability.cap_mycontext);
++
++    if (ctxt == NULL)
++      rc = ESRCH;
++    else if (!ELAN_CAP_MATCH (&msg->SrcCapability, &ctxt->Capability))
++      rc = EPERM;
++    else
++    { 
++      if (ctxt->Status & CTXT_NO_LWPS)
++          rc = EAGAIN;
++      else
++      {
++          for (predp = &ctxt->NetworkErrorFixups; *predp != NULL; predp = &(*predp)->Next)
++              ;
++          nef->Next = NULL;
++          *predp = nef;
++          
++          kcondvar_wakeupone (&ctxt->Wait, &dev->IntrLock);
++
++          while (! nef->Completed)
++              kcondvar_wait (&nef->Wait, &dev->IntrLock, &flags);
++
++          rc = nef->Status;
++      }
++    }
++    
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++
++    FreeNetworkErrorFixup (nef);
++
++    return (rc);
++}
++
++void
++CompleteNetworkErrorFixup (ELAN3_CTXT *ctxt, NETERR_FIXUP *nef, int status)
++{
++    ELAN3_DEV *dev = ctxt->Device;
++    unsigned long flags;
++
++    PRINTF2 (ctxt, DBG_NETERR, "CompleteNetworkErrorFixup: %p %d\n", nef, status);
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++
++    nef->Status = status;
++    nef->Completed = TRUE;
++    kcondvar_wakeupone (&nef->Wait, &dev->IntrLock);
++
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++}
++
++
++static NETERR_SERVER *
++NewNeterrServer (int elanId, struct sockaddr_in *addr, char *name)
++{
++    NETERR_SERVER *server;
++
++    KMEM_ZALLOC (server, NETERR_SERVER *, sizeof (NETERR_SERVER), TRUE);
++    KMEM_ALLOC  (server->Name, char *, strlen (name)+1, TRUE);
++
++    bcopy (addr, &server->Addr, sizeof (struct sockaddr_in));
++    bcopy (name, server->Name, strlen (name)+1);
++
++    server->ElanId   = elanId;
++    server->RefCount = 1;
++    
++    return (server);
++}
++
++static void
++DeleteNeterrServer (NETERR_SERVER *server)
++{
++    KMEM_FREE (server->Name, strlen(server->Name)+1);
++    KMEM_FREE (server, sizeof (NETERR_SERVER));
++}
++
++static NETERR_SERVER *
++FindNeterrServer (int elanId)
++{
++    NETERR_SERVER *server;
++    
++    kmutex_lock (&NeterrServerLock);
++    
++    for (server = NeterrServerHash[NETERR_HASH(elanId)]; server != NULL; server = server->Next)
++      if (server->ElanId == elanId)
++          break;
++
++    if (server != NULL)
++      server->RefCount++;
++    kmutex_unlock (&NeterrServerLock);
++
++    return (server);
++}
++
++static void
++DereferenceNeterrServer (NETERR_SERVER *server)
++{
++    kmutex_lock (&NeterrServerLock);
++    if ((--server->RefCount) == 0)
++      DeleteNeterrServer (server);
++    kmutex_unlock  (&NeterrServerLock);
++}
++
++int
++AddNeterrServer (int elanId, struct sockaddr_in *addr, char *name)
++{
++    NETERR_SERVER *server;
++    NETERR_SERVER *old;
++    int            hashval = NETERR_HASH(elanId);
++
++    server = NewNeterrServer (elanId, addr, name);
++    
++    if (server == NULL)
++      return (ENOMEM);
++    
++    kmutex_lock (&NeterrServerLock);
++    for (old = NeterrServerHash[hashval]; old != NULL; old = old->Next)
++      if (old->ElanId == elanId)
++          break;
++    
++    /* remove "old" server from hash table */
++    if (old != NULL)
++    {
++      if (old->Prev)
++          old->Prev->Next = old->Next;
++      else
++          NeterrServerHash[hashval] = old->Next;
++      if (old->Next)
++          old->Next->Prev = old->Prev;
++    }
++
++    /* insert "new" server into hash table */
++    if ((server->Next = NeterrServerHash[hashval]) != NULL)
++      server->Next->Prev = server;
++    server->Prev = NULL;
++    NeterrServerHash[hashval] = server;
++
++    kmutex_unlock (&NeterrServerLock);
++
++    if (old != NULL)
++      DereferenceNeterrServer (old);
++    
++    return (ESUCCESS);
++}
++
++int
++AddNeterrServerSyscall (int elanId, void *addrp, void *namep, char *unused)
++{
++    struct sockaddr_in addr;
++    char              *name;
++    int                error;
++    int                nob;
++
++    /* Sanity check the supplied elanId argument */
++    if (elanId < 0)
++      return ( set_errno(EINVAL) );
++
++    KMEM_ALLOC (name, caddr_t, SYS_NMLN, TRUE);
++    
++    if (copyin ((caddr_t) addrp, (caddr_t) &addr, sizeof (addr)) ||
++      copyinstr ((caddr_t) namep, name, SYS_NMLN, &nob))
++    {
++      error = EFAULT;
++    }
++    else
++    {
++      PRINTF2 (DBG_DEVICE, DBG_NETERR, "AddNeterrServer: '%s' at elanid %d\n", name, elanId);
++
++      error = AddNeterrServer (elanId, &addr, name);
++    }
++    KMEM_FREE (name, SYS_NMLN);
++
++    return (error ? set_errno(error) : ESUCCESS);
++}
++
++
++#if defined(DIGITAL_UNIX)
++static int
++CallNeterrServer (NETERR_SERVER *server, NETERR_MSG *msg)
++{
++    cred_t       *cr = crget();
++    struct rpc_err  rpcerr;
++    extern cred_t  *kcred;
++    struct timeval  wait;
++    enum clnt_stat  rc;
++    int                   status;
++    CLIENT         *clnt;
++    int             error;
++
++    PRINTF4 (DBG_DEVICE, DBG_NETRPC, "CallNeterrServer(%s) - family=%d port=%d addr=%08x\n", server->Name,
++           server->Addr.sin_family, server->Addr.sin_port, server->Addr.sin_addr.s_addr);
++
++    if ((clnt = clntkudp_create (&server->Addr, (struct sockaddr_in *)0, NETERR_PROGRAM, NETERR_VERSION, 1, cr)) == NULL)
++    {
++      PRINTF1 (DBG_DEVICE, DBG_NETRPC, "CallNeterrServer(%s): clntkudp_create error\n", server->Name);
++
++      return (ENOMEM);
++    }
++    
++    wait.tv_sec  = NETERR_RPC_TIMEOUT;
++    wait.tv_usec = 0;
++    
++    PRINTF2 (DBG_DEVICE, DBG_NETRPC, "CallNeterrServer(%s): CLNT_CALL timeout = %d\n", server->Name, NETERR_RPC_TIMEOUT);
++   
++    rc = CLNT_CALL(clnt, NETERR_FIXUP_RPC, xdr_neterr_msg, (void *)msg, xdr_int, (void *) &status, wait);
++
++    PRINTF3 (DBG_DEVICE, DBG_NETRPC, "CallNeterrServer(%s): CLNT_CALL -> %d (%s)\n", server->Name, rc, clnt_sperrno(rc));;
++
++    switch (rc)
++    {
++    case RPC_SUCCESS:
++      break;
++
++    case RPC_INTR:
++      status = EINTR;
++      break;
++
++    case RPC_TIMEDOUT:
++      status = ETIMEDOUT;
++      break;
++
++    default:
++      printf ("CallNeterrServer(%s): %s\n", server->Name, clnt_sperrno(status));
++      status = ENOENT;
++      break;
++    }
++
++    CLNT_DESTROY(clnt);
++
++    crfree(cr);
++    
++    ASSERT(rc == RPC_SUCCESS || status != 0);
++
++    PRINTF2 (DBG_DEVICE, DBG_NETRPC, "CallNeterrServer(%s): status=%d\n", server->Name, status);
++
++    return (status);
++}
++#endif
++
++#if defined(LINUX)
++
++#define xdrsize(type) ((sizeof(type) + 3) >> 2)
++
++static int
++xdr_error(struct rpc_rqst *req, u32 *p, void *dummy)
++{
++    return -EIO;
++}
++
++static int
++xdr_decode_int(struct rpc_rqst *req, u32 *p, int *res)
++{ 
++    *res = ntohl(*p++);
++    return 0;
++}
++
++#define XDR_capability_sz ((12 + BT_BITOUL(ELAN3_MAX_VPS)) * sizeof (u32))
++
++static int
++xdr_encode_capability(u32 *p, ELAN_CAPABILITY *cap)
++{
++    u32 *pp = p;
++
++    /* basic xdr unit is u32 - for opaque types we must round up to that */
++    memcpy(p, &cap->cap_userkey, sizeof(cap->cap_userkey));
++    p += xdrsize(cap->cap_userkey);
++
++    *p++ = htonl(cap->cap_version);
++    ((u16 *) (p++))[1] = htons(cap->cap_type);
++    *p++ = htonl(cap->cap_lowcontext);
++    *p++ = htonl(cap->cap_highcontext);
++    *p++ = htonl(cap->cap_mycontext);
++    *p++ = htonl(cap->cap_lownode);
++    *p++ = htonl(cap->cap_highnode);
++    *p++ = htonl(cap->cap_railmask);
++
++    memcpy(p, &cap->cap_bitmap[0], sizeof(cap->cap_bitmap));
++    p += xdrsize(cap->cap_bitmap);
++
++    ASSERT (((unsigned long) p - (unsigned long) pp) == XDR_capability_sz);
++
++    return (p - pp);
++}
++
++
++#define XDR_neterr_sz (((1 + 5) * sizeof (u32)) + (2*XDR_capability_sz))
++
++static int
++xdr_encode_neterr_msg(struct rpc_rqst *req, u32 *p, NETERR_MSG *msg)
++{
++    u32 *pp = p;
++
++    *p++ = htonl(msg->Rail);
++
++    p += xdr_encode_capability(p, &msg->SrcCapability);
++    p += xdr_encode_capability(p, &msg->DstCapability);
++
++    *p++ = htonl(msg->DstProcess);
++    *p++ = htonl(msg->CookieAddr);
++    *p++ = htonl(msg->CookieVProc);
++    *p++ = htonl(msg->NextCookie);
++    *p++ = htonl(msg->WaitForEop);
++
++    ASSERT (((unsigned long) p - (unsigned long) pp) == XDR_neterr_sz);
++
++    req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
++
++    return 0;
++}
++
++static struct rpc_procinfo neterr_procedures[2] = 
++{
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
++#     define RPC_ID_NULL      "neterr_null"
++#     define RPC_ID_FIXUP_RPC "neterr_fixup_rpc"
++#else
++#     define RPC_ID_NULL      NETERR_NULL_RPC
++#     define RPC_ID_FIXUP_RPC NETERR_FIXUP_RPC
++#endif
++    {         
++      RPC_ID_NULL,                    /* procedure name or number*/
++      (kxdrproc_t) xdr_error,         /* xdr encode fun */
++        (kxdrproc_t) xdr_error,       /* xdr decode fun */
++      0,                              /* req buffer size */
++      0,                              /* call count */
++    },
++    {         
++      RPC_ID_FIXUP_RPC,
++        (kxdrproc_t) xdr_encode_neterr_msg,
++        (kxdrproc_t) xdr_decode_int,
++      XDR_neterr_sz,
++      0,                      
++    },
++};
++
++static struct rpc_version neterr_version1 = 
++{
++    1,                        /* version */
++    2,                        /* number of procedures */
++    neterr_procedures /* procedures */
++};
++
++static struct rpc_version *neterr_version[] = 
++{
++    NULL,
++    &neterr_version1,
++};
++
++static struct rpc_stat neterr_stats;
++
++static struct rpc_program neterr_program = 
++{
++    NETERR_SERVICE,
++    NETERR_PROGRAM,
++    sizeof(neterr_version)/sizeof(neterr_version[0]),
++    neterr_version,
++    &neterr_stats,
++};
++
++static int
++CallNeterrServer (NETERR_SERVER *server, NETERR_MSG *msg)
++{
++    struct rpc_xprt   *xprt;
++    struct rpc_clnt   *clnt;
++    struct rpc_timeout to;
++    int                rc, status;
++    
++    PRINTF (DBG_DEVICE, DBG_NETRPC, "CallNeterrServer(%s)\n", server->Name);
++
++    xprt_set_timeout(&to, 1, NETERR_RPC_TIMEOUT * HZ);
++
++    if ((xprt = xprt_create_proto(IPPROTO_UDP, &server->Addr, &to)) == NULL)
++    {
++      PRINTF (DBG_DEVICE, DBG_NETRPC, "CallNeterrServer(%s) xprt_create_proto failed\n", server->Name);
++      return EFAIL;
++    }
++
++    if ((clnt = rpc_create_client(xprt, server->Name, &neterr_program, NETERR_VERSION, RPC_AUTH_NULL)) == NULL)
++    {
++      PRINTF (DBG_DEVICE, DBG_NETRPC, "CallNeterrServer(%s) rpc_create_client failed\n", server->Name);
++      xprt_destroy (xprt);
++      
++      return EFAIL;
++    }
++
++    clnt->cl_softrtry = 1;
++    clnt->cl_chatty   = 0;
++    clnt->cl_oneshot  = 1;
++    clnt->cl_intr     = 0;
++
++    if ((rc = rpc_call(clnt, NETERR_FIXUP_RPC, msg, &status, 0)) < 0)
++    {
++      /* RPC error has occured - determine whether we should retry */
++
++      status = ETIMEDOUT;
++    }
++
++    PRINTF (DBG_DEVICE, DBG_NETRPC, "CallNeterrServer(%s): -> %d\n", server->Name, status);
++
++    return (status);
++}
++
++#endif /* defined(LINUX) */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan3/procfs_linux.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan3/procfs_linux.c    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan3/procfs_linux.c 2005-05-11 12:10:12.421935160 -0400
+@@ -0,0 +1,195 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: procfs_linux.c,v 1.21 2003/09/24 13:57:25 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/os/procfs_linux.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elandebug.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanvp.h>
++
++#include <linux/module.h>
++#include <linux/ctype.h>
++
++#include <qsnet/procfs_linux.h>
++
++struct proc_dir_entry *elan3_procfs_root;
++struct proc_dir_entry *elan3_config_root;
++
++static int
++proc_read_position (char *page, char **start, off_t off,
++                  int count, int *eof, void *data)
++{
++    ELAN3_DEV *dev = (ELAN3_DEV *) data;
++    int       len;
++
++    if (dev->Position.pos_mode == ELAN_POS_UNKNOWN)
++      len = sprintf (page, "<unknown>\n");
++    else
++      len = sprintf (page, 
++                     "NodeId                 %d\n"
++                     "NumLevels              %d\n"
++                     "NumNodes               %d\n",
++                     dev->Position.pos_nodeid, dev->Position.pos_levels, dev->Position.pos_nodes);
++
++    return (qsnet_proc_calc_metrics (page, start, off, count, eof, len));
++}
++
++static int
++proc_write_position (struct file *file, const char *buf, unsigned long count, void *data)
++{
++    ELAN3_DEV *dev      = (ELAN3_DEV *) data;
++    unsigned  nodeid   = ELAN3_INVALID_NODE;
++    unsigned  numnodes = 0;
++    char     *page, *p;
++    int       res;
++
++    if (count == 0)
++      return (0);
++
++    if (count >= PAGE_SIZE)
++      return (-EINVAL);
++
++    if ((page = (char *) __get_free_page (GFP_KERNEL)) == NULL)
++      return (-ENOMEM);
++
++    MOD_INC_USE_COUNT;
++
++    if (copy_from_user (page, buf, count))
++      res = -EFAULT;
++    else
++    {
++      page[count] = '\0';
++      
++      if (page[count-1] == '\n')
++          page[count-1] = '\0';
++
++      if (! strcmp (page, "<unknown>"))
++      {
++          dev->Position.pos_mode      = ELAN_POS_UNKNOWN;
++          dev->Position.pos_nodeid    = ELAN3_INVALID_NODE;
++          dev->Position.pos_nodes     = 0;
++          dev->Position.pos_levels    = 0;
++      }
++      else
++      {
++          for (p = page; *p; )
++          {
++              while (isspace (*p))
++                  p++;
++              
++              if (! strncmp (p, "NodeId=", strlen("NodeId=")))
++                  nodeid   = simple_strtoul (p + strlen ("NodeId="), NULL, 0);
++              if (! strncmp (p, "NumNodes=", strlen ("NumNodes=")))
++                  numnodes = simple_strtoul (p + strlen ("NumNodes="), NULL, 0);
++              
++              while (*p && !isspace(*p))
++                  p++;
++          }
++
++          if (ComputePosition (&dev->Position, nodeid, numnodes, dev->Devinfo.dev_num_down_links_value) != 0)
++              printk ("elan%d: invalid values for NodeId=%d NumNodes=%d\n", dev->Instance, nodeid, numnodes);
++          else
++              printk ("elan%d: setting NodeId=%d NumNodes=%d NumLevels=%d\n", dev->Instance, dev->Position.pos_nodeid,
++                      dev->Position.pos_nodes, dev->Position.pos_levels);
++      }
++    }
++
++    MOD_DEC_USE_COUNT;
++    free_page ((unsigned long) page);
++
++    return (count);
++}
++
++
++void
++elan3_procfs_device_init (ELAN3_DEV *dev)
++{
++    struct proc_dir_entry *dir, *p;
++    char name[NAME_MAX];
++
++    sprintf (name, "device%d", dev->Instance);
++    dir = dev->Osdep.procdir = proc_mkdir (name, elan3_procfs_root);
++
++    if ((p = create_proc_entry ("position", 0, dir)) != NULL)
++    {
++      p->read_proc  = proc_read_position;
++      p->write_proc = proc_write_position;
++      p->data       = dev;
++      p->owner      = THIS_MODULE;
++    }
++
++}
++
++void
++elan3_procfs_device_fini (ELAN3_DEV *dev)
++{
++    struct proc_dir_entry *dir = dev->Osdep.procdir;
++    char name[NAME_MAX];
++
++    remove_proc_entry ("position", dir);
++
++    sprintf (name, "device%d", dev->Instance);
++    remove_proc_entry (name, elan3_procfs_root);
++}
++
++void
++elan3_procfs_init()
++{
++    extern int eventint_punt_loops;
++    extern int ResolveRequestTimeout;
++
++    elan3_procfs_root = proc_mkdir("elan3",  qsnet_procfs_root);
++
++    elan3_config_root = proc_mkdir("config", elan3_procfs_root);
++
++    qsnet_proc_register_hex (elan3_config_root, "elan3_debug",           &elan3_debug,           0);
++    qsnet_proc_register_hex (elan3_config_root, "elan3_debug_console",   &elan3_debug_console,   0);
++    qsnet_proc_register_hex (elan3_config_root, "elan3_debug_buffer",    &elan3_debug_buffer,    0);
++    qsnet_proc_register_hex (elan3_config_root, "elan3mmu_debug",      &elan3mmu_debug,      0);
++    qsnet_proc_register_int (elan3_config_root, "eventint_punt_loops", &eventint_punt_loops, 0);
++    qsnet_proc_register_int (elan3_config_root, "neterr_timeout",      &ResolveRequestTimeout, 0);
++
++#if defined(__ia64__)
++    {
++      extern int enable_sdram_writecombining;
++      qsnet_proc_register_int (elan3_config_root, "enable_sdram_writecombining", &enable_sdram_writecombining, 0);
++    }
++#endif
++}
++
++void
++elan3_procfs_fini()
++{
++#if defined(__ia64__)
++    remove_proc_entry ("enable_sdram_writecombining", elan3_config_root);
++#endif
++    remove_proc_entry ("neterr_timeout",      elan3_config_root);
++    remove_proc_entry ("eventint_punt_loops", elan3_config_root);
++    remove_proc_entry ("elan3mmu_debug",      elan3_config_root);
++    remove_proc_entry ("elan3_debug_buffer",    elan3_config_root);
++    remove_proc_entry ("elan3_debug_console",   elan3_config_root);
++    remove_proc_entry ("elan3_debug",           elan3_config_root);
++
++    remove_proc_entry ("config",  elan3_procfs_root);
++    remove_proc_entry ("version", elan3_procfs_root);
++ 
++    remove_proc_entry ("elan3",  qsnet_procfs_root);
++}
++
++EXPORT_SYMBOL(elan3_procfs_root);
++EXPORT_SYMBOL(elan3_config_root);
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan3/quadrics_version.h
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan3/quadrics_version.h        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan3/quadrics_version.h     2005-05-11 12:10:12.421935160 -0400
+@@ -0,0 +1 @@
++#define QUADRICS_VERSION "4.31qsnet"
+Index: linux-2.6.5/drivers/net/qsnet/elan3/routecheck.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan3/routecheck.c      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan3/routecheck.c   2005-05-11 12:10:12.422935008 -0400
+@@ -0,0 +1,313 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++/* ------------------------------------------------------------- */
++
++#include <qsnet/kernel.h>
++
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elanvp.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanctxt.h>
++#include <elan3/elandebug.h>
++#include <elan3/urom_addrs.h>
++#include <elan3/thread.h>
++#include <elan3/vmseg.h>
++
++/* ---------------------------------------------------------------------- */
++typedef struct elan3_net_location {
++    int netid;
++    int plane;
++    int level;
++} ELAN3_NET_LOCATION;
++/* ---------------------------------------------------------------------- */
++#define FLIT_LINK_ARRAY_MAX (ELAN3_MAX_LEVELS*2)
++/* ---------------------------------------------------------------------- */
++int 
++elan3_route_follow_link( ELAN3_CTXT *ctxt, ELAN3_NET_LOCATION *loc, int link)
++{
++    ELAN_POSITION *pos = &ctxt->Position;
++
++    if ((link<0) || (link>7)) 
++    {
++      PRINTF1 (ctxt, DBG_VP, "elan3_route_follow_link: link (%d) out of range \n",link);
++      return (ELAN3_ROUTE_INVALID);
++    }   
++
++    /* going up or down ? */
++    if ( link >= pos->pos_arity[loc->level] ) 
++    {
++      /* Up */
++      if (loc->level >= pos->pos_levels)
++          loc->plane = 0;
++      else
++      {
++          if ((loc->level == 1) && (pos->pos_arity[0]  == 8)) /* oddness in some machines ie 512 */
++              loc->plane = (16 * ( loc->plane / 8 )) + (4 * ( loc->plane % 4)) 
++                  +(link - pos->pos_arity[loc->level]);
++          else
++              loc->plane = (loc->plane * (8 - pos->pos_arity[loc->level]))
++                  +(link - pos->pos_arity[loc->level]);
++      }
++      loc->level--; 
++      if ( loc->level < 0 )
++      {
++          PRINTF0 (ctxt, DBG_VP, "elan3_route_follow_link: link goes off the top\n");
++          return (ELAN3_ROUTE_INVALID_LEVEL);
++      }
++      loc->netid = loc->netid / pos->pos_arity[loc->level];
++    }
++    else
++    {
++      /* going down */
++      if ((loc->level == 0) && (pos->pos_arity[0] == 8)) /* oddness in some machines ie 512 */
++          loc->netid = link % 2;
++      else
++          loc->netid =(loc->netid * pos->pos_arity[loc->level])+link;
++
++      loc->level++;
++      if (loc->level > pos->pos_levels)
++      {
++          PRINTF0 (ctxt, DBG_VP, "elan3_route_follow_link: link goes off the bottom\n");
++          return (ELAN3_ROUTE_INVALID_LEVEL); 
++      }
++
++      if ( loc->level >= (pos->pos_levels-1))
++          loc->plane = 0;
++      else
++          if ((loc->level == 1) && (pos->pos_arity[0] == 8)) /* oddness in some machines ie 512 */
++              loc->plane = (((loc->plane)>>2)*2) - ( ((loc->plane)>>2) & 3  ) + ((link<2)?0:4); /* ((p/4) % 4) */
++          else 
++              loc->plane = loc->plane/(8-pos->pos_arity[loc->level]);
++    }
++    return (ELAN3_ROUTE_SUCCESS);
++}
++/* ---------------------------------------------------------------------- */
++int /* assumes they are connected, really only used for finding the MyLink */
++elan3_route_get_mylink (ELAN_POSITION *pos, ELAN3_NET_LOCATION *locA, ELAN3_NET_LOCATION *locB)
++{
++    /* whats the My Link for locA to LocB */
++    if ( locA->level > locB->level ) 
++      return locB->plane - (locA->plane * (8 - pos->pos_arity[locA->level])) + pos->pos_arity[locA->level];
++    
++    return  locB->netid - (locA->netid * pos->pos_arity[locA->level]);
++}
++/* ---------------------------------------------------------------------- */
++#define FIRST_GET_HIGH_PRI(FLIT)    (FLIT & FIRST_HIGH_PRI)
++#define FIRST_GET_AGE(FLIT)         ((FLIT & FIRST_AGE(15))>>11)
++#define FIRST_GET_TIMEOUT(FLIT)     ((FLIT & FIRST_TIMEOUT(3))>>9)
++#define FIRST_GET_NEXT(FLIT)        ((FLIT & FIRST_PACKED(3))>>7)
++#define FIRST_GET_ROUTE(FLIT)       (FLIT & 0x7f)
++#define FIRST_GET_BCAST(FLIT)       (FLIT & 0x40)
++#define FIRST_GET_IS_INVALID(FLIT)  ((FLIT & 0x78) == 0x08)
++#define FIRST_GET_TYPE(FLIT)        ((FLIT & 0x30)>>4)
++#define PRF_GET_ROUTE(FLIT,N)       ((FLIT >> (N*4)) & 0x0F)
++#define PRF_GET_IS_MYLINK(ROUTE)    (ROUTE == PACKED_MYLINK)
++#define PRF_GET_IS_NORMAL(ROUTE)    (ROUTE & 0x8)
++#define PRF_GET_NORMAL_LINK(ROUTE)  (ROUTE & 0x7)
++#define PRF_MOVE_ON(INDEX,NEXT)     do { if (NEXT==3) {NEXT=0;INDEX++;} else {NEXT++; }} while (0);
++/* ---------------------------------------------------------------------- */
++int /* turn level needed or -1 if not possible */
++elan3_route_get_min_turn_level( ELAN_POSITION *pos, int nodeId)
++{
++    int l,range = 1;
++
++    for(l=pos->pos_levels-1;l>=0;l--)
++    {
++      range = range * pos->pos_arity[l];
++      
++      if ( ((pos->pos_nodeid - (pos->pos_nodeid % range)) <= nodeId ) 
++           && (nodeId <= (pos->pos_nodeid - (pos->pos_nodeid % range)+range -1))) 
++          return l;
++    }
++    return -1;
++}
++/* ---------------------------------------------------------------------- */
++int  
++elan3_route_check(ELAN3_CTXT *ctxt, E3_uint16 *flits, int destNodeId)
++{
++    ELAN3_NET_LOCATION lastLoc,currLoc;
++    int               err;
++    int               turnLevel;
++    int               goingDown;
++    int               lnk,index,next,val;
++    ELAN_POSITION    *pos = &ctxt->Position;
++   
++    /* is the dest possible */
++    if ( (destNodeId <0 ) || (destNodeId >= pos->pos_nodes))
++      return  (ELAN3_ROUTE_PROC_RANGE);
++
++    /* 
++     * walk the route, 
++     * - to see if we get there 
++     * - checking we dont turn around 
++     */
++    currLoc.netid = pos->pos_nodeid;         /* the elan */
++    currLoc.plane = 0;
++    currLoc.level = pos->pos_levels;
++
++    turnLevel = currLoc.level; /* track the how far the route goes in */
++    goingDown = 0;             /* once set we cant go up again ie only one change of direction */
++
++    /* move onto the network from the elan */
++    if ((err=elan3_route_follow_link(ctxt,&currLoc,4)) != ELAN3_ROUTE_SUCCESS) 
++    {
++      PRINTF0 (ctxt, DBG_VP, "elan3_route_check: initial elan3_route_follow_link failed\n");
++      return err;
++    }
++    /* do the first part of flit */
++    switch ( FIRST_GET_TYPE(flits[0]) ) 
++    {
++    case 0  /* sent */   : { lnk = (flits[0] & 0x7);                                break; }    
++    case PACKED_MYLINK         : { lnk = pos->pos_nodeid % pos->pos_arity[pos->pos_levels-1];    break; }
++    case PACKED_ADAPTIVE : { lnk = 7; /* all routes are the same just check one */    break; }
++    default : 
++      PRINTF1 (ctxt, DBG_VP, "elan3_route_check: unexpected first flit (%d)\n",flits[0]);
++      return (ELAN3_ROUTE_INVALID); 
++    }
++    
++    /* move along this link and check new location */
++    memcpy(&lastLoc,&currLoc,sizeof(ELAN3_NET_LOCATION)); /* keep track of last loc */
++    if ((err=elan3_route_follow_link(ctxt,&currLoc,lnk)) != ELAN3_ROUTE_SUCCESS ) 
++    {
++      PRINTF0 (ctxt, DBG_VP, "elan3_route_check: elan3_route_follow_link failed\n");
++      return err;
++    }
++    if ((currLoc.level > pos->pos_levels) || (currLoc.level < 0 )) 
++    { 
++      PRINTF0 (ctxt, DBG_VP, "elan3_route_check: route leaves machine\n");
++      return (ELAN3_ROUTE_INVALID_LEVEL);
++    }
++    if ( lastLoc.level < currLoc.level ) 
++    {
++      turnLevel = lastLoc.level;
++      goingDown = 1;
++    }
++    else 
++    {
++      if (turnLevel > currLoc.level)
++          turnLevel =  currLoc.level;
++      if  (goingDown) 
++      {
++          PRINTF0 (ctxt, DBG_VP, "elan3_route_check: route ocilated\n");
++          return (ELAN3_ROUTE_OCILATES);
++      }
++    }   
++
++    /* loop on doing the remaining flits */
++    index = 1;
++    next  = FIRST_GET_NEXT(flits[0]);
++    val   = PRF_GET_ROUTE(flits[index],next);
++    while(val)
++    {
++      if (PRF_GET_IS_NORMAL(val) ) 
++          lnk = PRF_GET_NORMAL_LINK(val);
++      else
++      {
++        switch ( val ) 
++        {
++        case  PACKED_MYLINK : 
++        {
++            lnk = elan3_route_get_mylink(pos, &currLoc,&lastLoc);
++            break;
++        }
++        default : 
++            PRINTF1 (ctxt, DBG_VP, "elan3_route_check: unexpected packed flit (%d)\n",val);
++            return (ELAN3_ROUTE_INVALID);
++        }
++      }
++
++      /* move along this link and check new location */
++      memcpy(&lastLoc,&currLoc,sizeof(ELAN3_NET_LOCATION)); /* keep track of last loc */
++      if ((err=elan3_route_follow_link(ctxt,&currLoc,lnk)) != ELAN3_ROUTE_SUCCESS) 
++          return err;
++      
++      if ((currLoc.level > pos->pos_levels ) || ( currLoc.level < 0 ))
++      { 
++          PRINTF0 (ctxt, DBG_VP, "elan3_route_check: route leaves machine\n");
++          return (ELAN3_ROUTE_INVALID_LEVEL);
++      }
++
++      if ( lastLoc.level < currLoc.level ) 
++          goingDown = 1;
++      else 
++      {
++          if (turnLevel > currLoc.level)
++              turnLevel =  currLoc.level;
++          if  (goingDown) 
++          {
++              PRINTF0 (ctxt, DBG_VP, "elan3_route_check: route ocilated\n");
++              return (ELAN3_ROUTE_OCILATES);
++          }
++      }   
++
++      /* move to next part of flit */
++      PRF_MOVE_ON(index,next);
++      if ( index >= MAX_FLITS)
++      {
++          PRINTF0 (ctxt, DBG_VP, "elan3_route_check: route too long\n");
++          return (ELAN3_ROUTE_TOO_LONG);
++      }
++      /* extract the new value */
++      val = PRF_GET_ROUTE(flits[index],next);
++    }
++
++    /* have we got to where we want ? */
++    if ((currLoc.level != pos->pos_levels) || (currLoc.netid != destNodeId))
++    {
++      PRINTF2 (ctxt, DBG_VP, "elan3_route_check: goes to %d instead of %d\n",currLoc.netid , destNodeId );
++      return (ELAN3_ROUTE_WRONG_DEST);
++    }
++
++    /*
++     * there is the case of src == dest 
++     * getTurnLevel returns pos->pos_levels, and turnLevel is (pos->pos_levels -1) 
++     * then we assume they really want to  go onto the network.
++     * otherwise we check that the turn at the appriate level
++     */
++    if ( (pos->pos_nodeid != destNodeId) || ( turnLevel != (pos->pos_levels -1)) )
++    {
++      int lev;
++      if ((lev = elan3_route_get_min_turn_level(pos,destNodeId)) == -1) 
++      {
++          PRINTF0 (ctxt, DBG_VP, "elan3_route_check: cant calculate turn level\n");
++          return (ELAN3_ROUTE_INVALID); /* not sure this can happen here as checks above should protect me */
++      }
++      if (turnLevel != lev) 
++      {
++          PRINTF2 (ctxt, DBG_VP, "elan3_route_check: turn level should be %d but is %d \n", lev, turnLevel);
++          return (ELAN3_ROUTE_TURN_LEVEL);
++      }
++    }
++    return (ELAN3_ROUTE_SUCCESS);
++}
++/* ---------------------------------------------------------------------- */
++int
++elan3_route_broadcast_check(ELAN3_CTXT *ctxt , E3_uint16 *flits, int lowNode, int highNode ) 
++{
++    E3_uint16 flitsTmp[MAX_FLITS];
++    int       nflits,i;
++    
++    nflits = GenerateRoute (&ctxt->Position, flitsTmp, lowNode, highNode, DEFAULT_ROUTE_TIMEOUT, DEFAULT_ROUTE_PRIORITY);
++    
++    for(i=0;i<nflits;i++)
++      if ( flitsTmp[i] != flits[i] ) 
++      {
++          PRINTF3 (ctxt, DBG_VP, "elan3_route_broadcast_check:  flit[%d] %d (should be %d)\n",i,flits[i],flitsTmp[i]);
++          return (ELAN3_ROUTE_INVALID);   
++      }
++    
++    return (ELAN3_ROUTE_SUCCESS);
++}
++/* ---------------------------------------------------------------------- */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan3/route_table.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan3/route_table.c     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan3/route_table.c  2005-05-11 12:10:12.423934856 -0400
+@@ -0,0 +1,560 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "$Id: route_table.c,v 1.23 2003/09/24 13:57:25 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/os/route_table.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elanvp.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanctxt.h>
++#include <elan3/elandebug.h>
++
++static sdramaddr_t
++AllocateLargeRoute (ELAN3_DEV *dev, ELAN3_ROUTE_TABLE *tbl, int ctxnum, E3_uint64 *smallRoute)
++{
++    int                 bit = -1;
++    ELAN3_ROUTES  *rent;
++    unsigned long flags;
++    
++    spin_lock_irqsave (&tbl->Lock, flags);
++    
++    for (rent = tbl->LargeRoutes; rent; rent = rent->Next)
++    {
++      if ((bit = bt_freebit (rent->Bitmap, NROUTES_PER_BLOCK)) != -1)
++          break;
++    }
++    
++    if (bit == -1)                                            /* No spare entries in large routes */
++    {                                                         /* so allocate a new page */
++      PRINTF0 (DBG_DEVICE, DBG_VP, "AllocateLargeRoute: allocate route entries\n");
++      
++      spin_unlock_irqrestore (&tbl->Lock, flags);
++
++      KMEM_ZALLOC(rent, ELAN3_ROUTES *, sizeof (ELAN3_ROUTES), TRUE);
++      
++      if (rent == (ELAN3_ROUTES *) NULL)
++          return ((sdramaddr_t) 0);
++      
++      rent->Routes = elan3_sdram_alloc (dev, PAGESIZE);
++      if (rent->Routes == (sdramaddr_t) 0)
++      {
++          KMEM_FREE (rent, sizeof (ELAN3_ROUTES));
++          return ((sdramaddr_t) 0);
++      }
++
++      spin_lock_irqsave (&tbl->Lock, flags);
++
++      /* Add to list of large routes */
++      rent->Next       = tbl->LargeRoutes;
++      tbl->LargeRoutes = rent;
++
++      /* and use entry 0 */
++      bit = 0;
++    }
++    
++    /* Set the bit in the bitmap to mark this route as allocated */
++    BT_SET (rent->Bitmap, bit);
++    
++    /* And generate the small route pointer and the pointer to the large routes */
++    (*smallRoute) = BIG_ROUTE_PTR(rent->Routes + (bit*NBYTES_PER_LARGE_ROUTE), ctxnum);
++
++    PRINTF4 (DBG_DEVICE, DBG_VP, "AllocateLargeRoute: rent %p using entry %d at %lx with route pointer %llx\n",
++           rent, bit, rent->Routes + (bit * NBYTES_PER_LARGE_ROUTE), (long long) (*smallRoute));
++
++    /* Invalidate the large route */
++    elan3_sdram_zeroq_sdram (dev, rent->Routes + (bit * NBYTES_PER_LARGE_ROUTE), NBYTES_PER_LARGE_ROUTE);
++
++    spin_unlock_irqrestore (&tbl->Lock, flags);
++
++    return (rent->Routes + (bit * NBYTES_PER_LARGE_ROUTE));
++}
++
++static void
++FreeLargeRoute (ELAN3_DEV *dev, ELAN3_ROUTE_TABLE *tbl, E3_uint64 smallRoute)
++{
++    E3_Addr      addr = (E3_Addr) (smallRoute & ((1ULL << ROUTE_CTXT_SHIFT)-1));
++    ELAN3_ROUTES *rent;
++
++    PRINTF1 (DBG_DEVICE, DBG_VP, "FreeLargeRoute: free route %llx\n", (long long) smallRoute);
++
++    ASSERT (SPINLOCK_HELD (&tbl->Lock));
++
++    for (rent = tbl->LargeRoutes; rent; rent = rent->Next)
++    {
++      if (rent->Routes <= addr && (rent->Routes + ROUTE_BLOCK_SIZE) > addr)
++      {
++          int indx = (addr - rent->Routes)/NBYTES_PER_LARGE_ROUTE;
++          
++          PRINTF2 (DBG_DEVICE, DBG_VP, "FreeLargeRoute: rent=%p indx=%d\n", rent, indx);
++          
++          BT_CLEAR(rent->Bitmap, indx);
++          return;
++      }
++    }
++
++    panic ("elan: FreeLargeRoute - route not found in large route tables");
++}
++
++static void
++FreeLargeRoutes (ELAN3_DEV *dev, ELAN3_ROUTE_TABLE *tbl)
++{
++    ELAN3_ROUTES *rent;
++
++    while ((rent = tbl->LargeRoutes) != NULL)
++    {
++      PRINTF1 (DBG_DEVICE, DBG_VP, "FreeLargeRoutes: free rent %p\n", rent);
++
++      tbl->LargeRoutes = rent->Next;
++
++      elan3_sdram_free (dev, rent->Routes, PAGESIZE);
++      
++      KMEM_FREE (rent, sizeof(ELAN3_ROUTES));
++    }
++}
++
++int
++GetRoute (ELAN3_DEV *dev, ELAN3_ROUTE_TABLE *tbl, int process, E3_uint16 *flits)
++{
++    E3_uint64 routeValue;
++    sdramaddr_t largeRouteOff;
++  
++    if (process < 0 || process >= tbl->Size)
++      return (EINVAL);
++
++    routeValue = elan3_sdram_readq (dev, tbl->Table + process * NBYTES_PER_SMALL_ROUTE);
++
++    if (routeValue & ROUTE_PTR)
++    {
++      largeRouteOff = (routeValue & ROUTE_PTR_MASK);
++      
++      routeValue = elan3_sdram_readq (dev, largeRouteOff + 0);
++      flits[0] = routeValue & 0xffff;
++      flits[1] = (routeValue >> 16)  & 0xffff;
++      flits[2] = (routeValue >> 32)  & 0xffff;
++      flits[3] = (routeValue >> 48)  & 0xffff;
++      
++      routeValue = elan3_sdram_readq (dev, largeRouteOff + 8);
++      flits[4] = routeValue & 0xffff;
++      flits[5] = (routeValue >> 16)  & 0xffff;
++      flits[6] = (routeValue >> 32)  & 0xffff;
++      flits[6] = (routeValue >> 48)  & 0xffff;
++    }
++    else
++    {
++      flits[0] = routeValue & 0xffff;
++      flits[1] = (routeValue >> 16)  & 0xffff;
++      flits[2] = (routeValue >> 32)  & 0xffff;
++    }
++
++    return (ESUCCESS);
++}
++
++ELAN3_ROUTE_TABLE *
++AllocateRouteTable (ELAN3_DEV *dev, int size)
++{
++    ELAN3_ROUTE_TABLE *tbl;
++
++    KMEM_ZALLOC (tbl, ELAN3_ROUTE_TABLE *, sizeof (ELAN3_ROUTE_TABLE), TRUE);
++
++    if (tbl == (ELAN3_ROUTE_TABLE *) NULL)
++      return (NULL);
++    
++    tbl->Size  = size;
++    tbl->Table = elan3_sdram_alloc (dev, size*NBYTES_PER_SMALL_ROUTE);
++
++    if (tbl->Table == 0)
++    {
++      KMEM_FREE (tbl, sizeof (ELAN3_ROUTE_TABLE));
++      return (NULL);
++    }
++    spin_lock_init (&tbl->Lock);
++
++    /* zero the route table */
++    elan3_sdram_zeroq_sdram (dev, tbl->Table, size*NBYTES_PER_SMALL_ROUTE);
++
++    return (tbl);
++}
++
++void
++FreeRouteTable (ELAN3_DEV *dev, ELAN3_ROUTE_TABLE *tbl)
++{
++    elan3_sdram_free (dev, tbl->Table, tbl->Size*NBYTES_PER_SMALL_ROUTE);
++
++    FreeLargeRoutes (dev, tbl);
++
++    spin_lock_destroy (&tbl->Lock);
++
++    KMEM_FREE (tbl, sizeof (ELAN3_ROUTE_TABLE));
++}
++
++int
++LoadRoute (ELAN3_DEV *dev, ELAN3_ROUTE_TABLE *tbl, int process, int ctxnum, int nflits, E3_uint16 *flits)
++{
++    E3_uint64   routeValue;
++    E3_uint64   largeRouteValue;
++    sdramaddr_t   largeRouteOff;
++    unsigned long flags;
++
++    if (process < 0 || process >= tbl->Size)
++      return (EINVAL);
++
++    PRINTF3 (DBG_DEVICE, DBG_VP, "LoadRoute: table %lx process %d ctxnum %x\n", tbl->Table ,process, ctxnum);
++
++    if (nflits < 4)
++    {
++      spin_lock_irqsave (&tbl->Lock, flags);
++
++      /* See if we're replacing a "large" route */
++      routeValue = elan3_sdram_readq (dev, tbl->Table + process * NBYTES_PER_SMALL_ROUTE);
++      if (routeValue & ROUTE_PTR)
++          FreeLargeRoute (dev, tbl, routeValue);
++
++      routeValue = SMALL_ROUTE(flits, ctxnum);
++
++      if ( routeValue &  ROUTE_PTR)
++          PRINTF0 (DBG_DEVICE, DBG_VP, "SHOULD BE  A SMALL ROUTE !!!!!!!\n");
++
++      PRINTF2 (DBG_DEVICE, DBG_VP, "LoadRoute: loading small route %d  %llx\n", process, (long long) routeValue);
++      elan3_sdram_writeq (dev, tbl->Table + process * NBYTES_PER_SMALL_ROUTE, routeValue);
++    }
++    else
++    {
++      E3_uint64 value0 = BIG_ROUTE0(flits);
++      E3_uint64 value1 = BIG_ROUTE1(flits);
++
++      if ((largeRouteOff = AllocateLargeRoute (dev, tbl, ctxnum, &largeRouteValue)) == (sdramaddr_t) 0)
++          return (ENOMEM);
++
++      spin_lock_irqsave (&tbl->Lock, flags);
++          
++      routeValue = elan3_sdram_readq (dev, tbl->Table + process * NBYTES_PER_SMALL_ROUTE);
++
++      if ((routeValue & ROUTE_PTR) == 0)
++          elan3_sdram_writeq (dev, tbl->Table + process * NBYTES_PER_SMALL_ROUTE, largeRouteValue);
++      else
++      {
++          FreeLargeRoute (dev, tbl, largeRouteValue);
++
++          largeRouteOff   = (routeValue & ROUTE_PTR_MASK);
++      }
++
++      PRINTF3 (DBG_DEVICE, DBG_VP, "LoadRoute: loading large route %d - %llx %llx\n", process, 
++               (long long) value0, (long long) value1);
++
++      elan3_sdram_writeq (dev, largeRouteOff + 0, value0);
++      elan3_sdram_writeq (dev, largeRouteOff + 8, value1);
++    }
++
++    spin_unlock_irqrestore (&tbl->Lock, flags);
++    return (ESUCCESS);
++}
++void
++InvalidateRoute (ELAN3_DEV *dev, ELAN3_ROUTE_TABLE *tbl, int process)
++{
++    E3_uint64 routeValue;
++    unsigned long flags;
++
++    if (process < 0 || process >= tbl->Size)
++      return;
++
++    spin_lock_irqsave (&tbl->Lock, flags);
++
++    /* unset ROUTE_VALID
++     * does not matter if its short or long, will check when we re-use it
++     */
++    routeValue = elan3_sdram_readq (dev, tbl->Table + process * NBYTES_PER_SMALL_ROUTE);
++    elan3_sdram_writeq (dev, tbl->Table + process * NBYTES_PER_SMALL_ROUTE, (routeValue & (~ROUTE_VALID)));
++
++    spin_unlock_irqrestore (&tbl->Lock, flags);
++}
++void
++ValidateRoute (ELAN3_DEV *dev, ELAN3_ROUTE_TABLE *tbl, int process)
++{
++    E3_uint64 routeValue;
++    unsigned long flags;
++
++    if (process < 0 || process >= tbl->Size)
++      return;
++
++    PRINTF2 (DBG_DEVICE, DBG_VP, "ValidateRoute: table %ld process %d  \n", tbl->Table ,process);
++
++    spin_lock_irqsave (&tbl->Lock, flags);
++
++    /* set ROUTE_VALID
++     */
++    routeValue = elan3_sdram_readq (dev, tbl->Table + process * NBYTES_PER_SMALL_ROUTE);
++    elan3_sdram_writeq (dev, tbl->Table + process * NBYTES_PER_SMALL_ROUTE, (routeValue | ROUTE_VALID));
++
++    spin_unlock_irqrestore (&tbl->Lock, flags);
++}
++void
++ClearRoute (ELAN3_DEV *dev, ELAN3_ROUTE_TABLE *tbl, int process)
++{
++    E3_uint64 routeValue;
++    unsigned long flags;
++
++    if (process < 0 || process >= tbl->Size)
++      return;
++
++    spin_lock_irqsave (&tbl->Lock, flags);
++
++    PRINTF2 (DBG_DEVICE, DBG_VP, "ClearRoute: table %ld process %d  \n", tbl->Table ,process);
++
++    routeValue = elan3_sdram_readq (dev, tbl->Table + process * NBYTES_PER_SMALL_ROUTE);
++
++    elan3_sdram_writeq (dev, tbl->Table + process * NBYTES_PER_SMALL_ROUTE, 0);
++
++    if (routeValue & ROUTE_PTR)
++      FreeLargeRoute (dev, tbl, routeValue);
++
++    spin_unlock_irqrestore (&tbl->Lock, flags);
++}
++
++static int
++ElanIdEqual (ELAN_POSITION *pos, int level, int ida, int idb)
++{
++    int l;
++
++    for (l = pos->pos_levels-1; l >= level; l--)
++    {
++      ida /= pos->pos_arity[l];
++      idb /= pos->pos_arity[l];
++    }
++      
++    return (ida == idb);
++}
++
++static int
++RouteDown (ELAN_POSITION *pos, int level, int elanid)
++{
++    int l;
++
++    for (l = (pos->pos_levels - 1); level < pos->pos_levels - 1; level++, l--)
++    { 
++      if (  pos->pos_arity[l] )
++          elanid /= pos->pos_arity[l];    
++    }
++    elanid %= pos->pos_arity[l];
++
++    return elanid;
++}
++
++static int
++InitPackedAndFlits (u_char *packed, E3_uint16 *flits)
++{
++    int rb = 0;
++
++    bzero ((caddr_t) packed, MAX_PACKED+4);
++    bzero ((caddr_t) flits, MAX_FLITS * sizeof (E3_uint16));
++
++    /* Initialise 4 bytes of packed, so that the "padding" */
++    /* NEVER terminates with 00, as this is recognised as */
++    /* as CRC flit */
++    packed[rb++] = 0xF;
++    packed[rb++] = 0xF;
++    packed[rb++] = 0xF;
++    packed[rb++] = 0xF;
++
++    return (rb);
++}
++
++static int
++PackThemRoutesUp (E3_uint16 *flits, u_char *packed, int rb, int timeout, int highPri)
++{
++    int i, nflits;
++
++    flits[0] |= FIRST_TIMEOUT(timeout);
++    if (highPri)
++      flits[0] |= FIRST_HIGH_PRI;
++
++    /* round up the number of route bytes to flits */
++    /* and subtract the 4 extra we've padded out with */
++    nflits = (rb-1)/4;
++    
++    for (i = nflits; i > 0; i--)
++    {
++      flits[i] = (packed[rb-1] << 12 |
++                  packed[rb-2] << 8  |
++                  packed[rb-3] << 4  |
++                  packed[rb-4] << 0);
++      rb -= 4;
++    }
++    
++    /* Now set the position of the first packed route  */
++    /* byte in the 2nd 16 bit flit, taking account of the */
++    /* 4 byte padding */
++    flits[0] |= FIRST_PACKED (4-rb);
++    
++    return (nflits+1);
++}
++
++int
++GenerateRoute (ELAN_POSITION *pos, E3_uint16 *flits, int lowid, int highid, int timeout, int highPri)
++{
++    int     broadcast  = (lowid != highid);
++    int     rb         = 0;
++    int           first      = 1;
++    int     noRandom   = 0;
++    int     level;
++    u_char  packed[MAX_PACKED+4];
++    int     numDownLinks;
++
++    rb = InitPackedAndFlits (packed, flits);
++
++    for (level = pos->pos_levels-1;                           /* Move up out of the elan */
++       level > 0 && ! (ElanIdEqual (pos, level, pos->pos_nodeid, lowid) &&
++                       ElanIdEqual (pos, level, pos->pos_nodeid, highid)); level--)
++    {
++      noRandom |= pos->pos_random_disabled & (1 << (pos->pos_levels-1-level));
++    }
++    
++    for (level = pos->pos_levels-1;                           /* Move up out of the elan */
++       level > 0 && ! (ElanIdEqual (pos, level, pos->pos_nodeid, lowid) &&
++                       ElanIdEqual (pos, level, pos->pos_nodeid, highid)); level--)
++    {
++      numDownLinks = pos->pos_arity [level];
++      if (first)
++      {
++          if (broadcast || noRandom)
++              flits[0] = FIRST_BCAST_TREE;
++          else
++          {
++              if (numDownLinks == 4) 
++                  flits[0] = FIRST_ADAPTIVE;
++              else
++                  flits[0] = FIRST_ROUTE( numDownLinks + ( lowid % (8-numDownLinks) ));
++          }
++          first = 0;
++      }
++      else
++      {
++          if (broadcast || noRandom)
++              packed[rb++] = PACKED_BCAST_TREE;
++          else 
++          {
++              if (numDownLinks == 4) 
++                  packed[rb++] = PACKED_ADAPTIVE;
++              else
++                  packed[rb++] = PACKED_ROUTE( numDownLinks + ( lowid % (8-numDownLinks) ));
++          }               
++      }
++    }
++    
++    while (level < pos->pos_levels)
++    {
++      int lowRoute  = RouteDown (pos, level, lowid);
++      int highRoute = RouteDown (pos, level, highid);
++
++      if (first)
++      {
++          if (broadcast)
++              flits[0] = FIRST_BCAST(highRoute, lowRoute);
++          else
++              flits[0] = FIRST_ROUTE(lowRoute);
++
++          first = 0;
++      }
++      else
++      {
++          if (broadcast)
++          {
++              packed[rb++] = PACKED_BCAST0(highRoute, lowRoute);
++              packed[rb++] = PACKED_BCAST1(highRoute, lowRoute);
++          }
++          else
++              packed[rb++] = PACKED_ROUTE(lowRoute);
++      }
++      
++      level++;
++    }
++
++#ifdef ELITE_REVA_SUPPORTED
++    if (broadcast && (pos->pos_levels == 3))
++    {
++      packed[rb++] = PACKED_BCAST0(0, 0);
++      packed[rb++] = PACKED_BCAST1(0, 0);
++    }
++#endif
++
++    return (PackThemRoutesUp (flits, packed, rb, timeout, highPri));
++}
++
++int
++GenerateCheckRoute (ELAN_POSITION *pos, E3_uint16 *flits, int level, int adaptive)
++{
++    int     notfirst = 0;
++    int     l, rb;
++    u_char  packed[MAX_PACKED+4];
++
++    rb = InitPackedAndFlits (packed, flits);
++
++    for (l = pos->pos_levels-1; l > level; l--)
++      if (! notfirst++)
++          flits[0] = adaptive ? FIRST_ADAPTIVE : FIRST_BCAST_TREE;
++      else
++          packed[rb++] = adaptive ? PACKED_ADAPTIVE : PACKED_BCAST_TREE;
++
++    if (! notfirst++ ) 
++      flits[0] = FIRST_MYLINK;
++    else
++      packed[rb++] = PACKED_MYLINK;
++
++    for (l++ /* consume mylink */; l < pos->pos_levels; l++)
++      if (! notfirst++)
++          flits[0] = FIRST_ROUTE (RouteDown (pos, l, pos->pos_nodeid));
++      else
++          packed[rb++] = PACKED_ROUTE (RouteDown (pos, l, pos->pos_nodeid));
++
++
++    return (PackThemRoutesUp (flits, packed, rb, DEFAULT_ROUTE_TIMEOUT, HIGH_ROUTE_PRIORITY));
++}
++
++
++/*
++ * In this case "level" is the number of levels counted from the bottom.
++ */
++int
++GenerateProbeRoute (E3_uint16 *flits, int nodeid, int level, int *linkup, int *linkdown, int adaptive )
++{
++    int           first = 1;
++    int     i, rb;
++    u_char  packed[MAX_PACKED+4];
++
++    rb = InitPackedAndFlits (packed, flits);
++
++    /* Generate "up" routes */
++    for (i = 0; i < level; i++)
++    {
++      if (first)
++          flits[0] = linkup ? FIRST_ROUTE(linkup[i]) : adaptive ? FIRST_ADAPTIVE : FIRST_BCAST_TREE;
++      else
++          packed[rb++] = linkup ? PACKED_ROUTE(linkup[i]) : adaptive ? PACKED_ADAPTIVE : PACKED_BCAST_TREE;
++      first = 0;
++    }
++
++    /* Generate a "to-me" route down */
++    if (first)
++      flits[0] = FIRST_MYLINK;
++    else
++      packed[rb++] = PACKED_MYLINK;
++
++    for (i = level-1; i >= 0; i--)
++      packed[rb++] =  PACKED_ROUTE(linkdown[i]);
++
++    return (PackThemRoutesUp (flits, packed, rb, DEFAULT_ROUTE_TIMEOUT, HIGH_ROUTE_PRIORITY));
++}
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan3/sdram.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan3/sdram.c   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan3/sdram.c        2005-05-11 12:10:12.436932880 -0400
+@@ -0,0 +1,807 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: sdram.c,v 1.17 2003/09/24 13:57:25 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/os/sdram.c,v $*/
++
++
++#include <qsnet/kernel.h>
++
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elandebug.h>
++
++/* sdram access functions */
++#define sdram_off_to_bank(dev,off)    (&dev->SdramBanks[(off) >> ELAN3_SDRAM_BANK_SHIFT])
++#define sdram_off_to_offset(dev,off)  ((off) & (ELAN3_SDRAM_BANK_SIZE-1))
++#define sdram_off_to_bit(dev,indx,off)        (sdram_off_to_offset(dev,off) >> (SDRAM_MIN_BLOCK_SHIFT+(indx)))
++
++#define sdram_off_to_mapping(dev,off) (sdram_off_to_bank(dev,off)->Mapping + sdram_off_to_offset(dev,off))
++    
++unsigned char
++elan3_sdram_readb (ELAN3_DEV *dev, sdramaddr_t off)
++{
++    return (readb ((unsigned char *) sdram_off_to_mapping(dev, off)));
++}
++
++unsigned short
++elan3_sdram_readw (ELAN3_DEV *dev, sdramaddr_t off)
++{
++    return (readw ((unsigned short *) sdram_off_to_mapping(dev, off)));
++}
++
++unsigned int
++elan3_sdram_readl (ELAN3_DEV *dev, sdramaddr_t off)
++{
++    return (readl ((unsigned int *) sdram_off_to_mapping(dev, off)));
++}
++
++unsigned long long
++elan3_sdram_readq (ELAN3_DEV *dev, sdramaddr_t off)
++{
++    return (readq ((unsigned long long *) sdram_off_to_mapping(dev, off)));
++}
++
++void
++elan3_sdram_writeb (ELAN3_DEV *dev, sdramaddr_t off, unsigned char val)
++{
++    writeb (val, (unsigned char *) sdram_off_to_mapping(dev, off));
++    wmb();
++}
++
++void
++elan3_sdram_writew (ELAN3_DEV *dev, sdramaddr_t off, unsigned short val)
++{
++    writew (val, (unsigned short *) sdram_off_to_mapping(dev, off));
++    wmb();
++}
++
++void
++elan3_sdram_writel (ELAN3_DEV *dev, sdramaddr_t off, unsigned int val)
++{
++    writel (val, (unsigned int *) sdram_off_to_mapping(dev, off));
++    wmb();
++}
++
++void
++elan3_sdram_writeq (ELAN3_DEV *dev, sdramaddr_t off, unsigned long long val)
++{
++    writeq (val, (unsigned long long *) sdram_off_to_mapping(dev, off));
++    wmb();
++}
++
++void
++elan3_sdram_copyb_from_sdram (ELAN3_DEV *dev, sdramaddr_t from, void *to, int nbytes)
++{
++    bcopy ((void *)sdram_off_to_mapping(dev, from), to, nbytes);
++}
++
++void
++elan3_sdram_copyw_from_sdram (ELAN3_DEV *dev, sdramaddr_t from, void *to, int nbytes)
++{
++#ifdef __LITTLE_ENDIAN__
++    bcopy ((void *)sdram_off_to_mapping(dev, from), to, nbytes);
++#else
++#error incorrect for big endian
++#endif
++}
++
++void
++elan3_sdram_copyl_from_sdram (ELAN3_DEV *dev, sdramaddr_t from, void *to, int nbytes)
++{
++#ifdef __LITTLE_ENDIAN__
++    bcopy ((void *)sdram_off_to_mapping(dev, from), to, nbytes);
++#else
++#error incorrect for big endian
++#endif
++}
++
++void
++elan3_sdram_copyq_from_sdram (ELAN3_DEV *dev, sdramaddr_t from, void *to, int nbytes)
++{
++#ifdef __LITTLE_ENDIAN__
++    bcopy ((void *)sdram_off_to_mapping(dev, from), to, nbytes);
++#else
++#error incorrect for big endian
++#endif
++}
++
++#define E3_WRITEBUFFER_SIZE           16
++#define E3_WRITEBUFFER_OFFSET(x)      (((unsigned long) x) & (E3_WRITEBUFFER_SIZE-1))
++#define E3_WRITEBUFFER_BASE(x)                (((unsigned long) x) & ~((unsigned long) (E3_WRITEBUFFER_SIZE-1)))
++
++void
++elan3_sdram_copyb_to_sdram (ELAN3_DEV *dev, void *from, sdramaddr_t to, int nbytes)
++{
++    virtaddr_t dbase = (virtaddr_t) sdram_off_to_mapping (dev, to);
++    virtaddr_t dlim  = (virtaddr_t) dbase + nbytes;
++    virtaddr_t slim  = (virtaddr_t) from + nbytes;
++    unsigned   nbase = E3_WRITEBUFFER_SIZE - E3_WRITEBUFFER_OFFSET (dbase);
++    unsigned   ntop  = E3_WRITEBUFFER_OFFSET (dlim - sizeof (uint8_t)) + sizeof (uint8_t);
++    int        i;
++
++    if (E3_WRITEBUFFER_BASE(dbase) == E3_WRITEBUFFER_BASE(dlim))
++    {
++      for (i = 0; i < nbytes/sizeof(uint8_t); i++)
++          writeb (((uint8_t *) from)[i], &((uint8_t *) dbase)[i]);
++      wmb();
++    }
++    else
++    {
++      if (ntop < E3_WRITEBUFFER_SIZE)
++      {
++          slim -= ntop;
++          dlim -= ntop;
++          
++          for (i = 0; i < ntop/sizeof(uint8_t); i++)
++              writeb (((uint8_t *) slim)[i], &((uint8_t *) dlim)[i]);
++          wmb();
++      }
++      
++      while (dlim >= (dbase + E3_WRITEBUFFER_SIZE))
++      {
++          dlim -= E3_WRITEBUFFER_SIZE;
++          slim -= E3_WRITEBUFFER_SIZE;
++
++          for (i = 0; i < E3_WRITEBUFFER_SIZE/sizeof (uint8_t); i++)
++              writeb (((uint8_t *) slim)[i], &((uint8_t *) dlim)[i]);
++          wmb();
++      }
++      
++      if (nbase < E3_WRITEBUFFER_SIZE)
++      {
++          for (i = 0; i < nbase/sizeof(uint8_t); i++)
++              writeb (((uint8_t *) from)[i], &((uint8_t *) dbase)[i]);
++          wmb();
++      }
++    }
++}
++
++void
++elan3_sdram_zerob_sdram (ELAN3_DEV *dev, sdramaddr_t to, int nbytes)
++{
++    virtaddr_t dbase = (virtaddr_t) sdram_off_to_mapping (dev, to);
++    virtaddr_t dlim  = (virtaddr_t) dbase + nbytes;
++    unsigned   nbase = E3_WRITEBUFFER_SIZE - E3_WRITEBUFFER_OFFSET (dbase);
++    unsigned   ntop  = E3_WRITEBUFFER_OFFSET (dlim - sizeof (uint8_t)) + sizeof (uint8_t);
++    int        i;
++
++    if (E3_WRITEBUFFER_BASE(dbase) == E3_WRITEBUFFER_BASE(dlim))
++    {
++      for (i = 0; i < nbytes/sizeof(uint8_t); i++)
++          writeb (0, &((uint8_t *) dbase)[i]);
++      wmb();
++    }
++    else
++    {
++      if (ntop < E3_WRITEBUFFER_SIZE)
++      {
++          dlim -= ntop;
++          
++          for (i = 0; i < ntop/sizeof(uint8_t); i++)
++              writeb (0, &((uint8_t *) dlim)[i]);
++          wmb();
++      }
++      
++      while (dlim >= (dbase + E3_WRITEBUFFER_SIZE))
++      {
++          dlim -= E3_WRITEBUFFER_SIZE;
++
++          writeq (0, &((uint64_t *) dlim)[0]);
++          writeq (0, &((uint64_t *) dlim)[1]);
++
++          wmb();
++      }
++      
++      if (nbase < E3_WRITEBUFFER_SIZE)
++      {
++          for (i = 0; i < nbase/sizeof(uint8_t); i++)
++              writeb (0, &((uint8_t *) dbase)[i]);
++          wmb();
++      }
++    }
++}
++
++void
++elan3_sdram_copyw_to_sdram (ELAN3_DEV *dev, void *from, sdramaddr_t to, int nbytes)
++{
++    virtaddr_t dbase = (virtaddr_t) sdram_off_to_mapping (dev, to);
++    virtaddr_t dlim  = (virtaddr_t) dbase + nbytes;
++    virtaddr_t slim  = (virtaddr_t) from + nbytes;
++    unsigned   nbase = E3_WRITEBUFFER_SIZE - E3_WRITEBUFFER_OFFSET (dbase);
++    unsigned   ntop  = E3_WRITEBUFFER_OFFSET (dlim - sizeof (uint16_t)) + sizeof (uint16_t);
++    int        i;
++
++    if (E3_WRITEBUFFER_BASE(dbase) == E3_WRITEBUFFER_BASE(dlim))
++    {
++      for (i = 0; i < nbytes/sizeof(uint16_t); i++)
++          writew (((uint16_t *) from)[i], &((uint16_t *) dbase)[i]);
++      wmb();
++    }
++    else
++    {
++      if (ntop < E3_WRITEBUFFER_SIZE)
++      {
++          slim -= ntop;
++          dlim -= ntop;
++
++          for (i = 0; i < ntop/sizeof(uint16_t); i++)
++              writew (((uint16_t *) slim)[i], &((uint16_t *) dlim)[i]);
++          wmb();
++      }
++      
++      while (dlim >= (dbase + E3_WRITEBUFFER_SIZE))
++      {
++          dlim -= E3_WRITEBUFFER_SIZE;
++          slim -= E3_WRITEBUFFER_SIZE;
++
++          writew (((uint16_t *) slim)[0], &((uint16_t *) dlim)[0]);
++          writew (((uint16_t *) slim)[1], &((uint16_t *) dlim)[1]);
++          writew (((uint16_t *) slim)[2], &((uint16_t *) dlim)[2]);
++          writew (((uint16_t *) slim)[3], &((uint16_t *) dlim)[3]);
++          writew (((uint16_t *) slim)[4], &((uint16_t *) dlim)[4]);
++          writew (((uint16_t *) slim)[5], &((uint16_t *) dlim)[5]);
++          writew (((uint16_t *) slim)[6], &((uint16_t *) dlim)[6]);
++          writew (((uint16_t *) slim)[7], &((uint16_t *) dlim)[7]);
++          wmb();
++      }
++      
++      if (nbase < E3_WRITEBUFFER_SIZE)
++      {
++          for (i = 0; i < nbase/sizeof(uint16_t); i++)
++              writew (((uint16_t *) from)[i], &((uint16_t *) dbase)[i]);
++          wmb();
++      }
++    }
++}
++
++void
++elan3_sdram_zerow_sdram (ELAN3_DEV *dev, sdramaddr_t to, int nbytes)
++{
++    virtaddr_t dbase = (virtaddr_t) sdram_off_to_mapping (dev, to);
++    virtaddr_t dlim  = (virtaddr_t) dbase + nbytes;
++    unsigned   nbase = E3_WRITEBUFFER_SIZE - E3_WRITEBUFFER_OFFSET (dbase);
++    unsigned   ntop  = E3_WRITEBUFFER_OFFSET (dlim - sizeof (uint16_t)) + sizeof (uint16_t);
++    int        i;
++
++    if (E3_WRITEBUFFER_BASE(dbase) == E3_WRITEBUFFER_BASE(dlim))
++    {
++      for (i = 0; i < nbytes/sizeof(uint16_t); i++)
++          writew (0, &((uint16_t *) dbase)[i]);
++      wmb();
++    }
++    else
++    {
++      if (ntop < E3_WRITEBUFFER_SIZE)
++      {
++          dlim -= ntop;
++          
++          for (i = 0; i < ntop/sizeof(uint16_t); i++)
++              writew (0, &((uint16_t *) dlim)[i]);
++          wmb();
++      }
++      
++      while (dlim >= (dbase + E3_WRITEBUFFER_SIZE))
++      {
++          dlim -= E3_WRITEBUFFER_SIZE;
++
++          writeq (0, &((uint64_t *) dlim)[0]);
++          writeq (0, &((uint64_t *) dlim)[1]);
++          wmb();
++      }
++      
++      if (nbase < E3_WRITEBUFFER_SIZE)
++      {
++          for (i = 0; i < nbase/sizeof(uint16_t); i++)
++              writew (0, &((uint16_t *) dbase)[i]);
++          wmb();
++      }
++    }
++}
++
++void
++elan3_sdram_copyl_to_sdram (ELAN3_DEV *dev, void *from, sdramaddr_t to, int nbytes)
++{
++    virtaddr_t dbase = (virtaddr_t) sdram_off_to_mapping (dev, to);
++    virtaddr_t dlim  = (virtaddr_t) dbase + nbytes;
++    virtaddr_t slim  = (virtaddr_t) from + nbytes;
++    unsigned   nbase = E3_WRITEBUFFER_SIZE - E3_WRITEBUFFER_OFFSET (dbase);
++    unsigned   ntop  = E3_WRITEBUFFER_OFFSET (dlim - sizeof (uint32_t)) + sizeof (uint32_t);
++    int        i;
++
++    if (E3_WRITEBUFFER_BASE(dbase) == E3_WRITEBUFFER_BASE(dlim))
++    {
++      for (i = 0; i < nbytes/sizeof(uint32_t); i++)
++          writel (((uint32_t *) from)[i], &((uint32_t *) dbase)[i]);
++      wmb();
++    }
++    else
++    {
++      if (ntop < E3_WRITEBUFFER_SIZE)
++      {
++          slim -= ntop;
++          dlim -= ntop;
++
++          for (i = 0; i < ntop/sizeof(uint32_t); i++)
++              writel (((uint32_t *) slim)[i], &((uint32_t *) dlim)[i]);
++          wmb();
++      }
++      
++      while (dlim >= (dbase + E3_WRITEBUFFER_SIZE))
++      {
++          dlim -= E3_WRITEBUFFER_SIZE;
++          slim -= E3_WRITEBUFFER_SIZE;
++
++          writel (((uint32_t *) slim)[0], &((uint32_t *) dlim)[0]);
++          writel (((uint32_t *) slim)[1], &((uint32_t *) dlim)[1]);
++          writel (((uint32_t *) slim)[2], &((uint32_t *) dlim)[2]);
++          writel (((uint32_t *) slim)[3], &((uint32_t *) dlim)[3]);
++          wmb();
++      }
++      
++      if (nbase < E3_WRITEBUFFER_SIZE)
++      {
++          for (i = 0; i < nbase/sizeof(uint32_t); i++)
++              writel (((uint32_t *) from)[i], &((uint32_t *) dbase)[i]);
++          wmb();
++      }
++    }
++}
++
++void
++elan3_sdram_zerol_sdram (ELAN3_DEV *dev, sdramaddr_t to, int nbytes)
++{
++    virtaddr_t dbase = (virtaddr_t) sdram_off_to_mapping (dev, to);
++    virtaddr_t dlim  = (virtaddr_t) dbase + nbytes;
++    unsigned   nbase = E3_WRITEBUFFER_SIZE - E3_WRITEBUFFER_OFFSET (dbase);
++    unsigned   ntop  = E3_WRITEBUFFER_OFFSET (dlim - sizeof (uint32_t)) + sizeof (uint32_t);
++    int        i;
++
++    if (E3_WRITEBUFFER_BASE(dbase) == E3_WRITEBUFFER_BASE(dlim))
++    {
++      for (i = 0; i < nbytes/sizeof(uint32_t); i++)
++          writel (0, &((uint32_t *) dbase)[i]);
++      wmb();
++    }
++    else
++    {
++      if (ntop < E3_WRITEBUFFER_SIZE)
++      {
++          dlim -= ntop;
++
++          for (i = 0; i < ntop/sizeof(uint32_t); i++)
++              writel (0, &((uint32_t *) dlim)[i]);
++          wmb();
++      }
++      
++      while (dlim >= (dbase + E3_WRITEBUFFER_SIZE))
++      {
++          dlim -= E3_WRITEBUFFER_SIZE;
++
++          writeq (0, &((uint64_t *) dlim)[0]);
++          writeq (0, &((uint64_t *) dlim)[1]);
++          wmb();
++      }
++      
++      if (nbase < E3_WRITEBUFFER_SIZE)
++      {
++          for (i = 0; i < nbase/sizeof(uint32_t); i++)
++              writel (0, &((uint32_t *) dbase)[i]);
++          wmb();
++      }
++    }
++}
++
++void
++elan3_sdram_copyq_to_sdram (ELAN3_DEV *dev, void *from, sdramaddr_t to, int nbytes)
++{
++    virtaddr_t dbase = (virtaddr_t) sdram_off_to_mapping (dev, to);
++    virtaddr_t dlim  = (virtaddr_t) dbase + nbytes;
++    virtaddr_t slim  = (virtaddr_t) from + nbytes;
++    unsigned   nbase = E3_WRITEBUFFER_SIZE - E3_WRITEBUFFER_OFFSET (dbase);
++    unsigned   ntop  = E3_WRITEBUFFER_OFFSET (dlim - sizeof (uint64_t)) + sizeof (uint64_t);
++
++    if (E3_WRITEBUFFER_BASE(dbase) == E3_WRITEBUFFER_BASE(dlim))
++    {
++      writeq (((uint64_t *) from)[0], &((uint64_t *) dbase)[0]);
++      wmb();
++    }
++    else
++    {
++      if (ntop < E3_WRITEBUFFER_SIZE)
++      {
++          slim -= ntop;
++          dlim -= ntop;
++
++          writeq (((uint64_t *) slim)[0], &((uint64_t *) dlim)[0]);
++          wmb();
++      }
++      
++      while (dlim >= (dbase + E3_WRITEBUFFER_SIZE))
++      {
++          dlim -= E3_WRITEBUFFER_SIZE;
++          slim -= E3_WRITEBUFFER_SIZE;
++
++          writeq (((uint64_t *) slim)[0], &((uint64_t *) dlim)[0]);
++          writeq (((uint64_t *) slim)[1], &((uint64_t *) dlim)[1]);
++          wmb();
++      }
++      
++      if (nbase < E3_WRITEBUFFER_SIZE)
++      {
++          writeq (((uint64_t *) from)[0], &((uint64_t *) dbase)[0]);
++          wmb();
++      }
++    }
++}
++
++void
++elan3_sdram_zeroq_sdram (ELAN3_DEV *dev, sdramaddr_t to, int nbytes)
++{
++    virtaddr_t dbase = (virtaddr_t) sdram_off_to_mapping (dev, to);
++    virtaddr_t dlim  = (virtaddr_t) dbase + nbytes;
++    unsigned   nbase = E3_WRITEBUFFER_SIZE - E3_WRITEBUFFER_OFFSET (dbase);
++    unsigned   ntop  = E3_WRITEBUFFER_OFFSET (dlim - sizeof (uint64_t)) + sizeof (uint64_t);
++
++    if (E3_WRITEBUFFER_BASE(dbase) == E3_WRITEBUFFER_BASE(dlim))
++    {
++      writeq (0, &((uint64_t *) dbase)[0]);
++      wmb();
++    }
++    else
++    {
++      if (ntop < E3_WRITEBUFFER_SIZE)
++      {
++          dlim -= ntop;
++
++          writeq (0, &((uint64_t *) dlim)[0]);
++          wmb();
++      }
++      
++      while (dlim >= (dbase + E3_WRITEBUFFER_SIZE))
++      {
++          dlim -= E3_WRITEBUFFER_SIZE;
++          
++          writeq (0, &((uint64_t *) dlim)[0]);
++          writeq (0, &((uint64_t *) dlim)[1]);
++          wmb();
++      }
++      
++      if (nbase < E3_WRITEBUFFER_SIZE)
++      {
++          writeq (0, &((uint64_t *) dbase)[0]);
++          wmb();
++      }
++    }
++}
++
++physaddr_t
++elan3_sdram_to_phys (ELAN3_DEV *dev, sdramaddr_t off)
++{
++#if defined(DIGITAL_UNIX)
++    return (KSEG_TO_PHYS (sdram_off_to_mapping (dev, off)));
++#elif defined(LINUX)
++    return (kmem_to_phys ((void *) sdram_off_to_mapping (dev, off)));
++#endif    
++}
++
++/* sdram buddy allocator */
++#define read_next(dev, block)         elan3_sdram_readl(dev, block + 0)
++#define read_prev(dev, block)         elan3_sdram_readl(dev, block + 4)
++#define write_next(dev, block, val)   (elan3_sdram_writel(dev, block + 0, val), val)
++#define write_prev(dev, block, val)   (elan3_sdram_writel(dev, block + 4, val), val)
++
++#define freelist_insert(dev,idx,block)\
++do {\
++    sdramaddr_t next = dev->SdramFreeLists[(idx)];\
++\
++    /*\
++     * block->prev = NULL;\
++     * block->next = next;\
++     * if (next != NULL)\
++     *    next->prev = block;\
++     * freelist = block;\
++     */\
++    write_prev (dev, block, (sdramaddr_t) 0);\
++    write_next (dev, block, next);\
++    if (next != (sdramaddr_t) 0)\
++      write_prev (dev, next, block);\
++    dev->SdramFreeLists[idx] = block;\
++\
++    dev->SdramFreeCounts[idx]++;\
++    dev->Stats.SdramBytesFree += (SDRAM_MIN_BLOCK_SIZE << idx);\
++} while (0)
++
++#define freelist_remove(dev,idx,block)\
++do {\
++    /*\
++     * if (block->prev)\
++     *     block->prev->next = block->next;\
++     * else\
++     *     dev->SdramFreeLists[idx] = block->next;\
++     * if (block->next)\
++     *     block->next->prev = block->prev;\
++     */\
++    sdramaddr_t blocknext = read_next (dev, block);\
++    sdramaddr_t blockprev = read_prev (dev, block);\
++\
++    if (blockprev)\
++      write_next (dev, blockprev, blocknext);\
++    else\
++      dev->SdramFreeLists[idx] = blocknext;\
++    if (blocknext)\
++      write_prev (dev, blocknext, blockprev);\
++\
++    dev->SdramFreeCounts[idx]--;\
++    dev->Stats.SdramBytesFree -= (SDRAM_MIN_BLOCK_SIZE << idx);\
++} while (0)
++
++#define freelist_removehead(dev,idx,block)\
++do {\
++    sdramaddr_t blocknext = read_next (dev, block);\
++\
++    if ((dev->SdramFreeLists[idx] = blocknext) != 0)\
++      write_prev (dev, blocknext, 0);\
++\
++    dev->SdramFreeCounts[idx]--;\
++    dev->Stats.SdramBytesFree -= (SDRAM_MIN_BLOCK_SIZE << idx);\
++} while (0)
++
++#if defined(DEBUG)
++static int
++display_blocks (ELAN3_DEV *dev, int indx, char *string)
++{
++    sdramaddr_t block;
++    int nbytes = 0;
++
++    printk ("%s - indx %d\n", string, indx);
++    for (block = dev->SdramFreeLists[indx]; block != (sdramaddr_t) 0; block = read_next (dev, block))
++    {
++      printk ("  %lx", block);
++      nbytes += (SDRAM_MIN_BLOCK_SIZE << indx);
++    }
++    printk ("\n");
++
++    return (nbytes);
++}
++
++
++void
++elan3_sdram_display (ELAN3_DEV *dev, char *string)
++{
++    int indx;
++    int nbytes = 0;
++
++    printk ("elan3_sdram_display: dev=%p\n", dev);
++    for (indx = 0; indx < SDRAM_NUM_FREE_LISTS; indx++)
++      if (dev->SdramFreeLists[indx] != (sdramaddr_t) 0)
++          nbytes += display_blocks (dev, indx, string);
++    printk ("\n%d bytes free\n", nbytes);
++}
++
++void
++elan3_sdram_verify (ELAN3_DEV *dev)
++{
++    int indx, size, nbits, i, b;
++    sdramaddr_t block;
++
++    for (indx = 0, size = SDRAM_MIN_BLOCK_SIZE; indx < SDRAM_NUM_FREE_LISTS; indx++, size <<= 1)
++    {
++      unsigned count = 0;
++
++      for (block = dev->SdramFreeLists[indx]; block; block = read_next (dev, block), count++)
++      {
++          ELAN3_SDRAM_BANK *bank = sdram_off_to_bank (dev, block);
++          unsigned         off  = sdram_off_to_offset (dev, block);
++          int              bit  = sdram_off_to_bit (dev, indx, block);
++
++          if ((block & (size-1)) != 0)
++              printk ("elan3_sdram_verify: block=%lx indx=%x - not aligned\n", block, indx);
++          
++          if (bank == NULL || off > bank->Size)
++              printk ("elan3_sdram_verify: block=%lx indx=%x - outside bank\n", block, indx);
++          else if (BT_TEST (bank->Bitmaps[indx], bit) == 0)
++              printk ("elan3_sdram_verify: block=%lx indx=%x - bit not set\n", block, indx);
++          else
++          {
++              for (i = indx-1, nbits = 2; i >= 0; i--, nbits <<= 1)
++              {
++                  bit = sdram_off_to_bit (dev, i, block);
++
++                  for (b = 0; b < nbits; b++)
++                      if (BT_TEST(bank->Bitmaps[i], bit + b))
++                          printk ("elan3_sdram_verify: block=%lx indx=%x - also free i=%d bit=%x\n", block, indx, i, bit+b);
++              }
++          }
++      }
++
++      if (dev->SdramFreeCounts[indx] != count)
++          printk ("elan3_sdram_verify: indx=%x expected %d got %d\n", indx, dev->SdramFreeCounts[indx], count);
++    }
++}
++
++#endif /* defined(DEBUG) */
++
++static void
++free_block (ELAN3_DEV *dev, sdramaddr_t block, int indx)
++{
++    ELAN3_SDRAM_BANK *bank = sdram_off_to_bank (dev, block);
++    unsigned       bit  = sdram_off_to_bit(dev, indx, block);
++    unsigned         size = SDRAM_MIN_BLOCK_SIZE << indx;
++
++    PRINTF3 (DBG_DEVICE, DBG_SDRAM, "free_block: block=%lx indx=%d bit=%x\n", block, indx, bit);
++
++    ASSERT ((block & (size-1)) == 0);
++    ASSERT (BT_TEST (bank->Bitmaps[indx], bit) == 0);
++    
++    while (BT_TEST (bank->Bitmaps[indx], bit ^ 1))
++    {
++      sdramaddr_t buddy = block ^ size;
++      
++      PRINTF3 (DBG_DEVICE, DBG_SDRAM, "free_block: merge block=%lx buddy=%lx indx=%d\n", block, buddy, indx);
++
++      BT_CLEAR (bank->Bitmaps[indx], bit ^ 1);
++
++      freelist_remove (dev, indx, buddy);
++      
++      block = (block < buddy) ? block : buddy;
++      indx++;
++      size <<= 1;
++      bit >>= 1;
++    }
++
++    PRINTF3 (DBG_DEVICE, DBG_SDRAM, "free_block: free block=%lx indx=%d bit=%x\n", block, indx, bit);
++
++    freelist_insert (dev, indx, block);
++
++    BT_SET (bank->Bitmaps[indx], bit);
++}
++
++void
++elan3_sdram_init (ELAN3_DEV *dev)
++{
++    int indx;
++
++    spin_lock_init (&dev->SdramLock);
++
++    for (indx = 0; indx < SDRAM_NUM_FREE_LISTS; indx++)
++    {
++      dev->SdramFreeLists[indx]  = (sdramaddr_t) 0;
++      dev->SdramFreeCounts[indx] = 0;
++    }
++}
++
++void
++elan3_sdram_fini (ELAN3_DEV *dev)
++{
++    spin_lock_destroy (&dev->SdramLock);
++}
++
++void
++elan3_sdram_add (ELAN3_DEV *dev, sdramaddr_t base, sdramaddr_t top)
++{
++    register int indx;
++    register unsigned long size;
++
++    /* align to the minimum block size */
++    base = (base + SDRAM_MIN_BLOCK_SIZE - 1) & ~((sdramaddr_t) SDRAM_MIN_BLOCK_SIZE-1);
++    top &= ~((sdramaddr_t) SDRAM_MIN_BLOCK_SIZE-1);
++
++    /* don't allow 0 as a valid "base" */
++    if (base == 0)
++      base = E3_CACHE_SIZE;
++
++    /* carve the bottom to the biggest boundary */
++    for (indx = 0, size = SDRAM_MIN_BLOCK_SIZE; indx < SDRAM_NUM_FREE_LISTS; indx++, size <<= 1)
++    {
++      if ((base & size) == 0)
++          continue;
++
++      if ((base + size) > top)
++          break;
++
++      free_block (dev, base, indx);
++      
++      base += size;
++    }
++
++    /* carve the top down to the biggest boundary */
++    for (indx = 0, size = SDRAM_MIN_BLOCK_SIZE; indx < SDRAM_NUM_FREE_LISTS; indx++, size <<= 1)
++    {
++      if ((top & size) == 0)
++          continue;
++
++      if ((top - size) < base)
++          break;
++
++      free_block (dev, (top - size), indx);
++      
++      top -= size;
++    }
++
++    /* now free of the space in between */
++    while (base < top)
++    {
++      free_block (dev, base, (SDRAM_NUM_FREE_LISTS-1));
++
++      base += SDRAM_MAX_BLOCK_SIZE;
++    }
++}
++
++sdramaddr_t
++elan3_sdram_alloc (ELAN3_DEV *dev, int nbytes)
++{
++    sdramaddr_t block;
++    register int i, indx;
++    unsigned long size;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->SdramLock, flags);
++
++    for (indx = 0, size = SDRAM_MIN_BLOCK_SIZE; size < nbytes; indx++, size <<= 1)
++      ;
++
++    PRINTF2 (DBG_DEVICE, DBG_SDRAM, "elan3_sdram_alloc: nbytes=%d indx=%d\n", nbytes, indx);
++
++    /* find the smallest block which is big enough for this allocation */
++    for (i = indx; i < SDRAM_NUM_FREE_LISTS; i++, size <<= 1)
++      if (dev->SdramFreeLists[i])
++          break;
++    
++    if (i == SDRAM_NUM_FREE_LISTS)
++    {
++      spin_unlock_irqrestore (&dev->SdramLock, flags);
++      return ((sdramaddr_t) 0);
++    }
++    
++    PRINTF2 (DBG_DEVICE, DBG_SDRAM, "elan3_sdram_alloc: use block=%lx indx=%d\n", dev->SdramFreeLists[i], i);
++
++    /* remove the block from the free list */
++    freelist_removehead (dev, i, (block = dev->SdramFreeLists[i]));
++
++    /* clear the approriate bit in the bitmap */
++    BT_CLEAR (sdram_off_to_bank (dev, block)->Bitmaps[i], sdram_off_to_bit (dev,i, block));
++
++    /* and split it up as required */
++    while (i-- > indx)
++      free_block (dev, block + (size >>= 1), i);
++
++    PRINTF1 (DBG_DEVICE, DBG_SDRAM, "elan3_sdram_alloc: return block=%lx\n", block);
++
++    spin_unlock_irqrestore (&dev->SdramLock, flags);
++
++    ASSERT ((block & ((SDRAM_MIN_BLOCK_SIZE << (indx))-1)) == 0);
++
++    return ((sdramaddr_t) block);
++}
++
++void
++elan3_sdram_free (ELAN3_DEV *dev, sdramaddr_t block, int nbytes)
++{
++    register int indx;
++    unsigned long size;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->SdramLock, flags);
++
++    for (indx = 0, size = SDRAM_MIN_BLOCK_SIZE; size < nbytes; indx++, size <<= 1)
++      ;
++
++    PRINTF2 (DBG_DEVICE, DBG_SDRAM, "elan3_sdram_free: indx=%d block=%lx\n", indx, block);
++    
++    free_block (dev, block, indx);
++
++    spin_unlock_irqrestore (&dev->SdramLock, flags);
++}
++
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan3/tproc.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan3/tproc.c   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan3/tproc.c        2005-05-11 12:10:12.438932576 -0400
+@@ -0,0 +1,778 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: tproc.c,v 1.51.2.1 2004/11/15 11:12:36 mike Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/os/tproc.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elanvp.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanctxt.h>
++#include <elan3/elandebug.h>
++#include <elan3/urom_addrs.h>
++#include <elan3/thread.h>
++#include <elan3/elansyscall.h>
++#include <elan3/threadsyscall.h>
++#include <elan3/intrinsics.h>
++#include <elan3/vmseg.h>
++
++int
++HandleTProcTrap (ELAN3_DEV *dev, E3_uint32 *RestartBits)
++{
++    THREAD_TRAP  *trap  = dev->ThreadTrap;
++    int           delay = 1;
++
++    ASSERT(SPINLOCK_HELD (&dev->IntrLock));
++
++    trap->Status.Status  = read_reg32 (dev, Exts.TProcStatus);
++    trap->sp             = read_reg32 (dev, Thread_Desc_SP);
++    trap->pc             = read_reg32 (dev, ExecutePC);
++    trap->npc            = read_reg32 (dev, ExecuteNPC);
++    trap->StartPC        = read_reg32 (dev, StartPC);
++    trap->mi             = GET_STATUS_TRAPTYPE(trap->Status);
++    trap->TrapBits.Bits  = read_reg32 (dev, TrapBits.Bits);
++    trap->DirtyBits.Bits = read_reg32 (dev, DirtyBits.Bits);
++
++    if ( ! (trap->Status.s.WakeupFunction == SleepOneTick) ) {
++      int p,i;
++      E3_uint32 reg = read_reg32 (dev, Exts.InterruptReg);    
++
++      ELAN_REG_REC(reg);
++      p = elan_reg_rec_index;
++      for(i=0;i<ELAN_REG_REC_MAX;i++) {
++          if (elan_reg_rec_file[i] != NULL ) 
++              printk("Elan Reg Record[%2d](%ld): cpu %d  reg %x [%d:%s]\n", p, elan_reg_rec_lbolt[p], elan_reg_rec_cpu[p], elan_reg_rec_reg[p],
++                     elan_reg_rec_line[p], elan_reg_rec_file[p]);
++          p = ( (p+1) % ELAN_REG_REC_MAX);
++      }
++    }
++    
++    ASSERT(trap->Status.s.WakeupFunction == SleepOneTick);
++
++    /* copy the four access fault areas */
++    elan3_sdram_copyq_from_sdram (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, TProc),          (void *) &trap->FaultSave, 16);
++    elan3_sdram_copyq_from_sdram (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, ThreadProcData), (void *) &trap->DataFaultSave, 16);
++    elan3_sdram_copyq_from_sdram (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, ThreadProcInst), (void *) &trap->InstFaultSave, 16);
++    elan3_sdram_copyq_from_sdram (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, ThreadProcOpen), (void *) &trap->OpenFaultSave, 16);
++    
++    /* copy the registers,  note the endian swap flips the odd registers into the even registers
++       and visa versa. */
++    copy_thread_regs (dev, trap->Registers);
++
++    /*
++     * If the output was open then the ack may not have returned yet. Must wait for the
++     * ack to become valid and update trap_dirty with the new value. Will simulate the
++     * instructions later.
++     */
++    if (trap->TrapBits.s.OutputWasOpen)
++    {
++      trap->TrapBits.Bits = read_reg32 (dev, TrapBits.Bits);
++      while (! trap->TrapBits.s.AckBufferValid)
++      {
++          PRINTF0 (DBG_DEVICE, DBG_INTR, "tproc: waiting for ack to become valid\n");
++          trap->TrapBits.Bits = read_reg32 (dev, TrapBits.Bits);
++          DELAY (delay);
++
++          if ((delay <<= 1) == 0) delay = 1;
++      }
++    }
++    
++    /* update device statistics */
++    BumpStat (dev, TProcTraps);
++    switch (trap->mi)
++    {
++    case MI_UnimplementedError:
++      if (trap->TrapBits.s.ForcedTProcTrap)
++          BumpStat (dev, ForcedTProcTraps);
++      if (trap->TrapBits.s.ThreadTimeout)
++      {
++          if (trap->TrapBits.s.PacketTimeout)
++              BumpStat (dev, ThreadOutputTimeouts);
++          else if (trap->TrapBits.s.PacketAckValue == E3_PAckError)
++              BumpStat (dev, ThreadPacketAckErrors);
++      }
++      if (trap->TrapBits.s.TrapForTooManyInsts)
++          BumpStat (dev, TrapForTooManyInsts);
++      break;
++    }
++
++    elan3_sdram_zeroq_sdram (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, TProc), 16);
++    elan3_sdram_zeroq_sdram (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, ThreadProcData), 16);
++    elan3_sdram_zeroq_sdram (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, ThreadProcInst), 16);
++    elan3_sdram_zeroq_sdram (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, ThreadProcOpen), 16);
++
++    *RestartBits |= RestartTProc;
++
++    return (TRUE);
++}
++
++void
++DeliverTProcTrap (ELAN3_DEV *dev, THREAD_TRAP *threadTrap, E3_uint32 Pend)
++{
++    ELAN3_CTXT             *ctxt;
++    THREAD_TRAP      *trap;
++
++    ASSERT(SPINLOCK_HELD (&dev->IntrLock));
++
++    ctxt = ELAN3_DEV_CTX_TABLE(dev, threadTrap->Status.s.Context);
++
++    if (ctxt == NULL)
++    {
++      PRINTF1 (DBG_DEVICE, DBG_INTR, "DeliverTProcTrap: context %x invalid\n", threadTrap->Status.s.Context);
++      BumpStat (dev, InvalidContext);
++    }
++    else
++    {
++      if (ELAN3_OP_TPROC_TRAP (ctxt, threadTrap) == OP_DEFER)
++      {
++          if (ELAN3_QUEUE_REALLY_FULL (ctxt->ThreadTrapQ))
++          {
++              ctxt->Status |= CTXT_COMMAND_OVERFLOW_ERROR;
++              StartSwapoutContext (ctxt, Pend, NULL);
++          }
++          else
++          {
++              trap = ELAN3_QUEUE_BACK (ctxt->ThreadTrapQ, ctxt->ThreadTraps);
++              
++              bcopy (threadTrap, trap, sizeof (THREAD_TRAP));
++              
++              PRINTF4 (ctxt, DBG_INTR, "DeliverTProcTrap: SP=%08x PC=%08x NPC=%08x StartPC %08x\n",
++                       trap->sp, trap->pc, trap->npc, trap->StartPC);
++              PRINTF3 (ctxt, DBG_INTR, "       mi=%s trap=%08x dirty=%08x\n",
++                       MiToName (trap->mi), trap->TrapBits.Bits, trap->DirtyBits.Bits);
++              PRINTF3 (ctxt, DBG_INTR, "       FaultSave : FaultAddress %08x EventAddress %08x FSR %08x\n",
++                       trap->FaultSave.s.FaultAddress, trap->FaultSave.s.EventAddress, trap->FaultSave.s.FSR.Status);
++              PRINTF3 (ctxt, DBG_INTR, "       DataFault : FaultAddress %08x EventAddress %08x FSR %08x\n",
++                       trap->DataFaultSave.s.FaultAddress, trap->DataFaultSave.s.EventAddress, trap->DataFaultSave.s.FSR.Status);
++              PRINTF3 (ctxt, DBG_INTR, "       InstFault : FaultAddress %08x EventAddress %08x FSR %08x\n",
++                       trap->InstFaultSave.s.FaultAddress, trap->InstFaultSave.s.EventAddress, trap->InstFaultSave.s.FSR.Status);
++              PRINTF3 (ctxt, DBG_INTR, "       OpenFault : FaultAddress %08x EventAddress %08x FSR %08x\n",
++                       trap->OpenFaultSave.s.FaultAddress, trap->OpenFaultSave.s.EventAddress, trap->OpenFaultSave.s.FSR.Status);
++              
++              PRINTF4 (ctxt, DBG_INTR, "       g0=%08x g1=%08x g2=%08x g3=%08x\n", 
++                       trap->Registers[REG_GLOBALS+(0^WordEndianFlip)], trap->Registers[REG_GLOBALS+(1^WordEndianFlip)], 
++                       trap->Registers[REG_GLOBALS+(2^WordEndianFlip)], trap->Registers[REG_GLOBALS+(3^WordEndianFlip)]);
++              PRINTF4 (ctxt, DBG_INTR, "       g4=%08x g5=%08x g6=%08x g7=%08x\n", 
++                       trap->Registers[REG_GLOBALS+(4^WordEndianFlip)], trap->Registers[REG_GLOBALS+(5^WordEndianFlip)], 
++                       trap->Registers[REG_GLOBALS+(6^WordEndianFlip)], trap->Registers[REG_GLOBALS+(7^WordEndianFlip)]);
++              PRINTF4 (ctxt, DBG_INTR, "       o0=%08x o1=%08x o2=%08x o3=%08x\n", 
++                       trap->Registers[REG_OUTS+(0^WordEndianFlip)], trap->Registers[REG_OUTS+(1^WordEndianFlip)], 
++                       trap->Registers[REG_OUTS+(2^WordEndianFlip)], trap->Registers[REG_OUTS+(3^WordEndianFlip)]);
++              PRINTF4 (ctxt, DBG_INTR, "       o4=%08x o5=%08x o6=%08x o7=%08x\n", 
++                       trap->Registers[REG_OUTS+(4^WordEndianFlip)], trap->Registers[REG_OUTS+(5^WordEndianFlip)], 
++                       trap->Registers[REG_OUTS+(6^WordEndianFlip)], trap->Registers[REG_OUTS+(7^WordEndianFlip)]);
++              PRINTF4 (ctxt, DBG_INTR, "       l0=%08x l1=%08x l2=%08x l3=%08x\n", 
++                       trap->Registers[REG_LOCALS+(0^WordEndianFlip)], trap->Registers[REG_LOCALS+(1^WordEndianFlip)],
++                       trap->Registers[REG_LOCALS+(2^WordEndianFlip)], trap->Registers[REG_LOCALS+(3^WordEndianFlip)]);
++              PRINTF4 (ctxt, DBG_INTR, "       l4=%08x l5=%08x l6=%08x l7=%08x\n", 
++                       trap->Registers[REG_LOCALS+(4^WordEndianFlip)], trap->Registers[REG_LOCALS+(5^WordEndianFlip)],
++                       trap->Registers[REG_LOCALS+(6^WordEndianFlip)], trap->Registers[REG_LOCALS+(7^WordEndianFlip)]);
++              PRINTF4 (ctxt, DBG_INTR, "       i0=%08x i1=%08x i2=%08x i3=%08x\n", 
++                       trap->Registers[REG_INS+(0^WordEndianFlip)], trap->Registers[REG_INS+(1^WordEndianFlip)],
++                       trap->Registers[REG_INS+(2^WordEndianFlip)], trap->Registers[REG_INS+(3^WordEndianFlip)]);
++              PRINTF4 (ctxt, DBG_INTR, "       i4=%08x i5=%08x i6=%08x i7=%08x\n", 
++                       trap->Registers[REG_INS+(4^WordEndianFlip)], trap->Registers[REG_INS+(5^WordEndianFlip)],
++                       trap->Registers[REG_INS+(6^WordEndianFlip)], trap->Registers[REG_INS+(7^WordEndianFlip)]);
++              
++              ELAN3_QUEUE_ADD (ctxt->ThreadTrapQ);
++              kcondvar_wakeupone (&ctxt->Wait, &dev->IntrLock);
++              
++              if (ELAN3_QUEUE_FULL (ctxt->ThreadTrapQ))
++              {
++                  PRINTF0 (ctxt, DBG_INTR, "DeliverTProcTrap: thread queue full,  must swap out\n");
++                  ctxt->Status |= CTXT_THREAD_QUEUE_FULL;
++                  
++                  StartSwapoutContext (ctxt, Pend, NULL);
++              }
++          }
++      }
++    }
++}
++
++int
++NextTProcTrap (ELAN3_CTXT *ctxt, THREAD_TRAP *trap)
++{
++    ELAN3_DEV *dev = ctxt->Device;
++
++    ASSERT (SPINLOCK_HELD (&dev->IntrLock));
++    
++    if (ELAN3_QUEUE_EMPTY (ctxt->ThreadTrapQ))
++      return (0);
++
++    *trap = *ELAN3_QUEUE_FRONT (ctxt->ThreadTrapQ, ctxt->ThreadTraps);
++    ELAN3_QUEUE_REMOVE (ctxt->ThreadTrapQ);
++    
++    return (1);
++}
++
++void
++ResolveTProcTrap (ELAN3_CTXT *ctxt, THREAD_TRAP *trap)
++{
++    int       i;
++    int       res;
++    E3_Addr   StackPointer;
++
++    PRINTF4 (ctxt, DBG_TPROC, "ResolveTProcTrap: SP=%08x PC=%08x NPC=%08x StartPC %08x\n",
++           trap->sp, trap->pc, trap->npc, trap->StartPC);
++    PRINTF3 (ctxt, DBG_TPROC, "       mi=%s trap=%08x dirty=%08x\n",
++           MiToName (trap->mi), trap->TrapBits.Bits, trap->DirtyBits.Bits);
++    PRINTF3 (ctxt, DBG_TPROC, "       FaultSave : FaultAddress %08x EventAddress %08x FSR %08x\n",
++           trap->FaultSave.s.FaultAddress, trap->FaultSave.s.EventAddress, trap->FaultSave.s.FSR.Status);
++    PRINTF3 (ctxt, DBG_TPROC, "       DataFault : FaultAddress %08x EventAddress %08x FSR %08x\n",
++           trap->DataFaultSave.s.FaultAddress, trap->DataFaultSave.s.EventAddress, trap->DataFaultSave.s.FSR.Status);
++    PRINTF3 (ctxt, DBG_TPROC, "       InstFault : FaultAddress %08x EventAddress %08x FSR %08x\n",
++           trap->InstFaultSave.s.FaultAddress, trap->InstFaultSave.s.EventAddress, trap->InstFaultSave.s.FSR.Status);
++    PRINTF3 (ctxt, DBG_TPROC, "       OpenFault : FaultAddress %08x EventAddress %08x FSR %08x\n",
++           trap->OpenFaultSave.s.FaultAddress, trap->OpenFaultSave.s.EventAddress, trap->OpenFaultSave.s.FSR.Status);
++    
++    PRINTF4 (ctxt, DBG_TPROC, "       g0=%08x g1=%08x g2=%08x g3=%08x\n", 
++           trap->Registers[REG_GLOBALS+(0^WordEndianFlip)], trap->Registers[REG_GLOBALS+(1^WordEndianFlip)], 
++           trap->Registers[REG_GLOBALS+(2^WordEndianFlip)], trap->Registers[REG_GLOBALS+(3^WordEndianFlip)]);
++    PRINTF4 (ctxt, DBG_TPROC, "       g4=%08x g5=%08x g6=%08x g7=%08x\n", 
++           trap->Registers[REG_GLOBALS+(4^WordEndianFlip)], trap->Registers[REG_GLOBALS+(5^WordEndianFlip)], 
++           trap->Registers[REG_GLOBALS+(6^WordEndianFlip)], trap->Registers[REG_GLOBALS+(7^WordEndianFlip)]);
++    PRINTF4 (ctxt, DBG_TPROC, "       o0=%08x o1=%08x o2=%08x o3=%08x\n", 
++           trap->Registers[REG_OUTS+(0^WordEndianFlip)], trap->Registers[REG_OUTS+(1^WordEndianFlip)], 
++           trap->Registers[REG_OUTS+(2^WordEndianFlip)], trap->Registers[REG_OUTS+(3^WordEndianFlip)]);
++    PRINTF4 (ctxt, DBG_TPROC, "       o4=%08x o5=%08x o6=%08x o7=%08x\n", 
++           trap->Registers[REG_OUTS+(4^WordEndianFlip)], trap->Registers[REG_OUTS+(5^WordEndianFlip)], 
++           trap->Registers[REG_OUTS+(6^WordEndianFlip)], trap->Registers[REG_OUTS+(7^WordEndianFlip)]);
++    PRINTF4 (ctxt, DBG_TPROC, "       l0=%08x l1=%08x l2=%08x l3=%08x\n", 
++           trap->Registers[REG_LOCALS+(0^WordEndianFlip)], trap->Registers[REG_LOCALS+(1^WordEndianFlip)],
++           trap->Registers[REG_LOCALS+(2^WordEndianFlip)], trap->Registers[REG_LOCALS+(3^WordEndianFlip)]);
++    PRINTF4 (ctxt, DBG_TPROC, "       l4=%08x l5=%08x l6=%08x l7=%08x\n", 
++           trap->Registers[REG_LOCALS+(4^WordEndianFlip)], trap->Registers[REG_LOCALS+(5^WordEndianFlip)],
++           trap->Registers[REG_LOCALS+(6^WordEndianFlip)], trap->Registers[REG_LOCALS+(7^WordEndianFlip)]);
++    PRINTF4 (ctxt, DBG_TPROC, "       i0=%08x i1=%08x i2=%08x i3=%08x\n", 
++           trap->Registers[REG_INS+(0^WordEndianFlip)], trap->Registers[REG_INS+(1^WordEndianFlip)],
++           trap->Registers[REG_INS+(2^WordEndianFlip)], trap->Registers[REG_INS+(3^WordEndianFlip)]);
++    PRINTF4 (ctxt, DBG_TPROC, "       i4=%08x i5=%08x i6=%08x i7=%08x\n", 
++           trap->Registers[REG_INS+(4^WordEndianFlip)], trap->Registers[REG_INS+(5^WordEndianFlip)],
++           trap->Registers[REG_INS+(6^WordEndianFlip)], trap->Registers[REG_INS+(7^WordEndianFlip)]);
++          
++
++    BumpUserStat (ctxt, TProcTraps);
++
++    switch (trap->mi)
++    {
++    case MI_UnimplementedError:
++    {
++      /*
++       * This occurs if the threads processor trapped. All other cases will be for the ucode
++       * thread trapping.
++       */
++      int restart = 1;
++      int skip    = 0;
++      
++      PRINTF1 (ctxt, DBG_TPROC, "TProc: Mi=Unimp. Using trap->TrapBits=%x\n", trap->TrapBits.Bits);
++      
++      /*
++       * Data Access Exception.
++       */
++      if (trap->TrapBits.s.DataAccessException)
++      {
++          ASSERT (CTXT_IS_KERNEL(ctxt) || trap->DataFaultSave.s.FSR.Status == 0 ||
++                  ctxt->Capability.cap_mycontext == trap->DataFaultSave.s.FaultContext);
++
++          PRINTF1 (ctxt, DBG_TPROC, "ResolveTProcTrap: DataAccessException %08x\n", trap->DataFaultSave.s.FaultAddress);
++
++          if ((res = elan3_pagefault (ctxt, &trap->DataFaultSave, 1)) != ESUCCESS)
++          {
++              PRINTF1 (ctxt, DBG_TPROC, "ResolveTProcTrap: elan3_pagefault failed for data %08x\n",
++                       trap->DataFaultSave.s.FaultAddress);
++
++              if (ElanException (ctxt, EXCEPTION_INVALID_ADDR, THREAD_PROC, trap, &trap->DataFaultSave, res) != OP_IGNORE)
++                  restart = 0;
++          }
++      }
++      
++      /* 
++       * Instruction Access Exception.
++       */
++      if (trap->TrapBits.s.InstAccessException)
++      {
++          ASSERT (CTXT_IS_KERNEL (ctxt) || trap->InstFaultSave.s.FSR.Status == 0 ||
++                  ctxt->Capability.cap_mycontext == trap->InstFaultSave.s.FaultContext);
++          
++          PRINTF1 (ctxt, DBG_TPROC, "ResolveTProcTrap: InstAccessException %08x\n", trap->InstFaultSave.s.FaultAddress);
++
++          if ((res = elan3_pagefault (ctxt, &trap->InstFaultSave, 1)) != ESUCCESS)
++          {
++              PRINTF1 (ctxt, DBG_TPROC, "ResolveTProcTrap: elan3_pagefault failed for inst %08x\n",
++                       trap->InstFaultSave.s.FaultAddress);
++
++              ElanException (ctxt, EXCEPTION_INVALID_ADDR, THREAD_PROC, trap, &trap->InstFaultSave, res);
++              restart = 0;
++          }
++      }
++      
++      /*
++       * Forced TProc trap/Unimplemented instruction
++       *
++       *  If there is a force tproc trap then don't look at 
++       *  the unimplemented instruction bit - since it can
++       *  be set in obscure circumstances.
++       */
++      if (trap->TrapBits.s.ForcedTProcTrap)
++          PRINTF0 (ctxt, DBG_TPROC, "ResolveTProcTrap: forced tproc trap, restarting\n");
++      else if (trap->TrapBits.s.Unimplemented)
++      {
++          E3_uint32 instr = ELAN3_OP_LOAD32 (ctxt, trap->pc & PC_MASK);
++
++          PRINTF1 (ctxt, DBG_TPROC, "ResolveTProcTrap: unimplemented instruction %08x\n", instr);
++
++          if ((instr & OPCODE_MASK) == OPCODE_Ticc &&
++              (instr & OPCODE_IMM)  == OPCODE_IMM &&
++              (Ticc_COND(instr)     == Ticc_TA))
++          {
++              switch (INSTR_IMM(instr))
++              {
++              case ELAN3_ELANCALL_TRAPNUM:
++                  /*
++                   * Since the thread cannot easily access the global variable which holds
++                   * the elan system call number, we provide a different trap for the elan
++                   * system call, and copy the system call number into %g1 before calling
++                   * ThreadSyscall().
++                   */
++                  BumpUserStat (ctxt, ThreadElanCalls);
++
++                  if (ThreadElancall (ctxt, trap, &skip) != ESUCCESS)
++                  {
++                      ElanException (ctxt, EXCEPTION_BAD_SYSCALL, THREAD_PROC, trap);
++                      restart = 0;
++                  }
++                  break;
++
++              case ELAN3_SYSCALL_TRAPNUM:
++                  BumpUserStat (ctxt, ThreadSystemCalls);
++
++                  if (ThreadSyscall (ctxt, trap, &skip) != ESUCCESS)
++                  {
++                      ElanException (ctxt, EXCEPTION_BAD_SYSCALL, THREAD_PROC, trap);
++                      restart = 0;
++                  }
++                  break;
++
++              case ELAN3_DEBUG_TRAPNUM:
++                  ElanException (ctxt, EXCEPTION_DEBUG, THREAD_PROC, trap);
++                  skip = 1;
++                  break;
++                  
++              case ELAN3_ABORT_TRAPNUM:
++              default:
++                  ElanException (ctxt, EXCEPTION_UNIMP_INSTR, THREAD_PROC, trap, instr);
++                  restart = 0;
++                  break;
++              }
++                  
++          }
++          else
++          {
++              ElanException (ctxt, EXCEPTION_UNIMP_INSTR, THREAD_PROC, trap, instr);
++              restart = 0;
++          }
++      }
++      
++      /*
++       * Faulted fetching routes.
++       */
++      if (trap->TrapBits.s.OpenRouteFetch)
++      {
++          PRINTF1 (ctxt, DBG_TPROC, "ResolveTProcTrap: OpenRouteFetch %08x\n", trap->OpenFaultSave.s.FaultAddress);
++          
++          if ((res = ResolveVirtualProcess (ctxt, trap->OpenFaultSave.s.FaultAddress)) != ESUCCESS &&
++              ElanException (ctxt, EXCEPTION_INVALID_PROCESS, THREAD_PROC, trap, trap->DataFaultSave.s.FaultAddress, res) != OP_IGNORE)
++          {
++              restart = 0;
++          }
++          else if (RollThreadToClose (ctxt, trap, E3_PAckDiscard) != ESUCCESS)        /* Force a discard */
++          {
++              restart = 0;
++          }
++      }
++      
++      /*
++       * Thread Timeout
++       */
++      if (trap->TrapBits.s.ThreadTimeout)
++      {
++          if (ElanException (ctxt, EXCEPTION_PACKET_TIMEOUT, THREAD_PROC, trap) != OP_IGNORE)
++              restart = 0;
++          else
++          {
++              PRINTF0 (ctxt, DBG_TPROC, "ResolveTProcTrap: timeout or PAckError!\n");
++              
++              /* Might deschedule the thread for a while or mark the link error here. */
++              if (! trap->TrapBits.s.OutputWasOpen && RollThreadToClose (ctxt, trap, trap->TrapBits.s.PacketAckValue) != ESUCCESS)
++              {
++                  restart = 0;
++              }
++          }
++      }
++      
++      /*
++       * Open exception
++       */
++      if (trap->TrapBits.s.OpenException)
++      {
++          PRINTF0 (ctxt, DBG_TPROC, "ResolveTProcTrap: open exception\n");
++          if (ElanException (ctxt, EXCEPTION_THREAD_KILLED, THREAD_PROC, trap) != OP_IGNORE)
++              restart = 0;
++      }
++      
++      /*
++       * Too many instructions.
++       */
++      if (trap->TrapBits.s.TrapForTooManyInsts)
++      {
++          PRINTF0 (ctxt, DBG_TPROC, "ResolveTProcTrap: too many instructions\n");
++          if (ElanException (ctxt, EXCEPTION_THREAD_KILLED, THREAD_PROC, trap) != OP_IGNORE)
++              restart = 0;
++      }
++      
++      if (restart)
++      {
++          /*
++           * If the output was open when the trap was taken then the trap code must move
++           * the PC on past the close instruction and simulate the effect of all the instructions
++           * that do not output onto the link. The value of the ack received is then used to
++           * simulate the close instruction.
++           */
++          if (trap->TrapBits.s.OutputWasOpen && RollThreadToClose(ctxt, trap, trap->TrapBits.s.PacketAckValue) != ESUCCESS)
++          {
++              /*
++               * Don't restart if we couldn't roll it forweards 
++               * to a close instruction.
++               */
++              break;
++          }
++
++          /*
++           * We must check back 3 instructions from the PC,  and if we see the
++           * c_close_cookie() sequence then we must execute the instructions to
++           * the end of it.
++           */
++          /* XXXX: code to be written */
++          
++          StackPointer = SaveThreadToStack (ctxt, trap, skip);
++          
++          ReissueStackPointer (ctxt, StackPointer);
++      }
++      
++      break;
++    }
++    
++    /*
++     * This case is different from the others as %o6 has been overwritten with
++     * the SP. The real PC can be read from StartPC and written back
++     * into %o6 on the stack.
++     */
++    case MI_TProcNext:                        /* Reading the outs block */
++    {
++      E3_Addr stack = (trap->sp & SP_MASK) - sizeof (E3_Stack);
++
++      if (ELAN3_OP_START_FAULT_CHECK (ctxt))
++      {
++          ELAN3_OP_END_FAULT_CHECK (ctxt);
++
++          PRINTF0 (ctxt, DBG_TPROC, "ResolveTProcTrap: faulted writing StartPc to o6\n");
++          ElanException (ctxt, EXCEPTION_CANNOT_SAVE_THREAD, THREAD_PROC, NULL);
++          break;
++      }
++      ELAN3_OP_STORE32 (ctxt, stack + offsetof (E3_Stack, Outs[6]), trap->StartPC & PC_MASK);
++      ELAN3_OP_END_FAULT_CHECK (ctxt);
++      /* DROPTHROUGH */
++    }
++    /*
++     * all of these will be generated when starting up a thread.
++     * Just re-issue the command after fixing the trap. The ucode keeps the startup
++     * from trap information in Thread_Desc_SP while it is still loading the regs.
++     */
++    case MI_WaitForGlobalsRead:               /* Reading the globals block (trap restart) */
++    case MI_WaitForNPCRead:           /* Reading the nPC, V and C (trap restart) */
++    case MI_WaitForPCload:            /* Reading the PC, N and Z (trap restart) */
++    case MI_WaitForInsRead:           /* Reading the ins block (trap restart) */
++    case MI_WaitForLocals:            /* Reading the ins block (trap restart) */
++    case MI_WaitForPCload2:           /* Reading the PC (normal thread start) */
++    case MI_WaitForSpStore:           /* Writing the SP to the outs block */
++      PRINTF2 (ctxt, DBG_TPROC, "ResolveTProcTrap: %s %08x\n", MiToName (trap->mi), trap->InstFaultSave.s.FaultAddress);
++
++      if ((res = elan3_pagefault (ctxt, &trap->FaultSave, 1)) != ESUCCESS)
++      {
++          PRINTF1 (ctxt, DBG_TPROC, "ResolveTProcTrap: elan3_pagefault failed at %08x\n",
++                   trap->FaultSave.s.FaultAddress);
++          if (ElanException (ctxt, EXCEPTION_INVALID_ADDR, THREAD_PROC, &trap->FaultSave, trap, res) != OP_IGNORE)
++              break;
++      }
++
++      ReissueStackPointer (ctxt, trap->sp);
++      break;
++      
++      /*
++       * These traps could occur after the threads proc has stopped (either for a wait,
++       * break, or suspend, but not a trap). Must simulate the uCode's job.
++       */
++    case MI_WaitForOutsWrite:         /* Writing the outs block */
++    case MI_WaitForNPCWrite:          /* Writing the nPC block */
++    {
++      E3_uint32 DeschedBits = (trap->TrapBits.Bits & E3_TProcDescheduleMask);
++      E3_Addr   stack       = (trap->sp & SP_MASK) - sizeof (E3_Stack);
++      
++      PRINTF1 (ctxt, DBG_TPROC, "ResolveTProcTrap: trapped on %s while stopping a thread\n", MiToName(trap->mi));
++      
++      /*
++       * Copy npc into o6.
++       */
++      trap->Registers[REG_OUTS+(6^WordEndianFlip)] = trap->npc;
++      
++      if (ELAN3_OP_START_FAULT_CHECK (ctxt))
++      {
++          ELAN3_OP_END_FAULT_CHECK (ctxt);
++
++          PRINTF0 (ctxt, DBG_TPROC, "ResolveTProcTrap: faulted writing outs to stack\n");
++          ElanException (ctxt, EXCEPTION_CANNOT_SAVE_THREAD, THREAD_PROC, NULL);
++          break;
++      }
++      
++      /*
++       * Now write the outs back to the stack. NOTE then endian flip is undone.
++       */
++      for (i = 0; i < 8; i++)
++          ELAN3_OP_STORE32 (ctxt, stack + offsetof (E3_Stack, Outs[i]), trap->Registers[REG_OUTS+(i^WordEndianFlip)]);
++      ELAN3_OP_END_FAULT_CHECK (ctxt);
++      
++      /*
++       * thread has been saved. Now find out why the thread proc stopped.
++       */
++      if (DeschedBits == E3_TProcDescheduleSuspend)
++      {
++          PRINTF0 (ctxt, DBG_TPROC, "ResolveTProcTrap: suspend instruction executed\n");
++          break;
++      }
++      
++      /*
++       * Break. Just reissue the command.
++       */
++      if (DeschedBits == E3_TProcDescheduleBreak)
++      {
++          PRINTF1 (ctxt, DBG_TPROC, "ResolveTProcTrap: break instruction, reissue sp %08x\n", trap->sp);
++          ReissueStackPointer (ctxt, trap->sp);
++          break;
++      }
++      
++      ASSERT (DeschedBits == E3_TProcDescheduleWait);
++       
++      /* DROPTHROUGH to fix up a wait event */
++    }
++    
++    /*
++     * Trapped here trying to execute a wait instruction. All the thread state has already
++     * been saved and the trap has been fixed so simplest thing to do is to start the
++     * thread up at the wait instruction again.
++     */
++    case MI_WaitForEventWaitAddr:     /* Reading back the %o0,%o1 pair for a
++                                         wait event instr. */
++    case MI_WaitForWaitEventAccess:   /* Locked dword read of the event location.
++                                                 Note that this read is done with write
++                                         permissions so we never get a trap on the write */
++    {
++      E3_Addr stack = (trap->sp & SP_MASK) - sizeof (E3_Stack);
++      
++      if ((res = elan3_pagefault (ctxt, &trap->FaultSave, 1)) != ESUCCESS)
++      {
++          PRINTF1 (ctxt, DBG_TPROC, "ResolveTProcTrap: elan3_pagefault failed at %08x\n", 
++                   trap->FaultSave.s.FaultAddress);
++          if (ElanException (ctxt, EXCEPTION_INVALID_ADDR, THREAD_PROC, trap, &trap->DataFaultSave, res) != OP_IGNORE)
++              break;
++      }
++
++      if (ELAN3_OP_START_FAULT_CHECK (ctxt))
++      {
++          ELAN3_OP_END_FAULT_CHECK (ctxt);
++
++          PRINTF0 (ctxt, DBG_TPROC, "ResolveTProcTrap: faulted writing pc to stack\n");
++          ElanException (ctxt, EXCEPTION_CANNOT_SAVE_THREAD, THREAD_PROC, NULL);
++          break;
++      }
++
++      ELAN3_OP_STORE32 (ctxt, stack + offsetof (E3_Stack, Outs[6]), trap->pc);
++      ELAN3_OP_END_FAULT_CHECK (ctxt);
++      
++      ReissueStackPointer (ctxt, trap->sp);
++      break;
++    }
++    
++    /*
++     * Assume the fault will be fixed by FixupEventTrap.
++     */
++    default:
++      FixupEventTrap (ctxt, THREAD_PROC, trap, trap->mi, &trap->FaultSave, 0);
++      break;
++    }
++}
++
++int
++TProcNeedsRestart (ELAN3_CTXT *ctxt)
++{
++    return (ctxt->ItemCount[LIST_THREAD] != 0);
++}
++
++void
++RestartTProcItems (ELAN3_CTXT *ctxt)
++{
++    void     *item;
++    E3_uint32 StackPointer;
++
++    kmutex_lock (&ctxt->SwapListsLock);
++    
++    while (ctxt->ItemCount[LIST_THREAD])
++    {
++      if (! ELAN3_OP_GET_WORD_ITEM (ctxt, LIST_THREAD, &item, &StackPointer))
++          ctxt->ItemCount[LIST_THREAD] = 0;
++      else
++      {
++          if (IssueCommand (ctxt, offsetof (E3_CommandPort, RunThread), StackPointer, 0) == ISSUE_COMMAND_RETRY)
++          {
++              ELAN3_OP_PUTBACK_ITEM (ctxt, LIST_THREAD, item);
++              kmutex_unlock (&ctxt->SwapListsLock);
++              return;
++          }
++          
++          ctxt->ItemCount[LIST_THREAD]--;
++          ELAN3_OP_FREE_WORD_ITEM (ctxt, item);
++      }
++    }
++    kmutex_unlock (&ctxt->SwapListsLock);
++}
++
++E3_Addr
++SaveThreadToStack (ELAN3_CTXT *ctxt, THREAD_TRAP *trap, int SkipInstruction)
++{
++    E3_Addr      stack = (trap->sp & SP_MASK) - sizeof (E3_Stack);
++    E3_Addr      orflag;
++    register int i;
++
++    /*
++     * When the thread deschedules normally, the N & Z flags are written 
++     * to the stack in o6, and the V & C flags are lost.
++     * Since the Elan will store the NPC into o6 (to skip the instruction), 
++     * the CC flags are visible to the trap handler in the trapped PC and NPC.   
++     * If the instruction needs to be re-executed then the CC flags need to be
++     * kept in the right place to be read in when the thread re-starts.
++     *
++     * PC  has N & Z from trapped NPC.
++     * NPC has V & C from trapped PC.
++     */
++    if (SkipInstruction)
++    {
++      trap->Registers[REG_OUTS+(6^WordEndianFlip)]    = trap->npc;
++      trap->Registers[REG_GLOBALS+(0^WordEndianFlip)] = ((trap->npc & PC_MASK) + 4) | (trap->pc & CC_MASK);
++    }
++    else
++    {
++      trap->Registers[REG_OUTS+(6^WordEndianFlip)]    = (trap->pc & PC_MASK) | (trap->npc & CC_MASK);
++      trap->Registers[REG_GLOBALS+(0^WordEndianFlip)] = (trap->npc & PC_MASK) | (trap->pc & CC_MASK);
++    }
++    
++    if (ELAN3_OP_START_FAULT_CHECK(ctxt))
++    {
++      PRINTF0 (ctxt, DBG_TPROC, "RestartThread: faulted writing out thread\n");
++      ELAN3_OP_END_FAULT_CHECK(ctxt);
++
++      ElanException (ctxt, EXCEPTION_CANNOT_SAVE_THREAD, THREAD_PROC, NULL);
++      return ((E3_Addr) 0);
++    }
++
++
++#ifdef DEBUG_PRINTF
++    PRINTF4 (ctxt, DBG_TPROC, "SaveThreadToStack: SP=%08x PC=%08x NPC=%08x DIRTY=%08x\n",
++           trap->sp, trap->pc, trap->npc, trap->DirtyBits.Bits);
++    if (trap->DirtyBits.s.GlobalsDirty)
++    {
++      PRINTF4 (ctxt, DBG_TPROC, "       g0=%08x g1=%08x g2=%08x g3=%08x\n", 
++               trap->Registers[REG_GLOBALS+(0^WordEndianFlip)], trap->Registers[REG_GLOBALS+(1^WordEndianFlip)], 
++               trap->Registers[REG_GLOBALS+(2^WordEndianFlip)], trap->Registers[REG_GLOBALS+(3^WordEndianFlip)]);
++      PRINTF4 (ctxt, DBG_TPROC, "       g4=%08x g5=%08x g6=%08x g7=%08x\n", 
++               trap->Registers[REG_GLOBALS+(4^WordEndianFlip)], trap->Registers[REG_GLOBALS+(5^WordEndianFlip)], 
++               trap->Registers[REG_GLOBALS+(6^WordEndianFlip)], trap->Registers[REG_GLOBALS+(7^WordEndianFlip)]);
++    }
++    if (trap->DirtyBits.s.OutsDirty)
++    {
++      PRINTF4 (ctxt, DBG_TPROC, "       o0=%08x o1=%08x o2=%08x o3=%08x\n", 
++               trap->Registers[REG_OUTS+(0^WordEndianFlip)], trap->Registers[REG_OUTS+(1^WordEndianFlip)], 
++               trap->Registers[REG_OUTS+(2^WordEndianFlip)], trap->Registers[REG_OUTS+(3^WordEndianFlip)]);
++      PRINTF4 (ctxt, DBG_TPROC, "       o4=%08x o5=%08x o6=%08x o7=%08x\n", 
++               trap->Registers[REG_OUTS+(4^WordEndianFlip)], trap->Registers[REG_OUTS+(5^WordEndianFlip)], 
++               trap->Registers[REG_OUTS+(6^WordEndianFlip)], trap->Registers[REG_OUTS+(7^WordEndianFlip)]);
++    }
++    if (trap->DirtyBits.s.LocalsDirty)
++    {
++      PRINTF4 (ctxt, DBG_TPROC, "       l0=%08x l1=%08x l2=%08x l3=%08x\n", 
++               trap->Registers[REG_LOCALS+(0^WordEndianFlip)], trap->Registers[REG_LOCALS+(1^WordEndianFlip)], 
++               trap->Registers[REG_LOCALS+(2^WordEndianFlip)], trap->Registers[REG_LOCALS+(3^WordEndianFlip)]);
++      PRINTF4 (ctxt, DBG_TPROC, "       l4=%08x l5=%08x l6=%08x l7=%08x\n", 
++               trap->Registers[REG_LOCALS+(4^WordEndianFlip)], trap->Registers[REG_LOCALS+(5^WordEndianFlip)], 
++               trap->Registers[REG_LOCALS+(6^WordEndianFlip)], trap->Registers[REG_LOCALS+(7^WordEndianFlip)]);
++    }
++    if (trap->DirtyBits.s.InsDirty)
++    {
++      PRINTF4 (ctxt, DBG_TPROC, "       i0=%08x i1=%08x i2=%08x i3=%08x\n", 
++               trap->Registers[REG_INS+(0^WordEndianFlip)], trap->Registers[REG_INS+(1^WordEndianFlip)], 
++               trap->Registers[REG_INS+(2^WordEndianFlip)], trap->Registers[REG_INS+(3^WordEndianFlip)]);
++      PRINTF4 (ctxt, DBG_TPROC, "       i4=%08x i5=%08x i6=%08x i7=%08x\n", 
++               trap->Registers[REG_INS+(4^WordEndianFlip)], trap->Registers[REG_INS+(5^WordEndianFlip)], 
++               trap->Registers[REG_INS+(6^WordEndianFlip)], trap->Registers[REG_INS+(7^WordEndianFlip)]);
++    }
++#endif 
++    
++    PRINTF1 (ctxt, DBG_TPROC, "flushing registers to stack %08x\n", stack);
++
++    /* 
++     * NOTE - store the register to the stack in reverse order, since the stack 
++     * will be allocated in sdram, and we cannot use the sdram accessing functions 
++     * here, as it is "mapped" in user-space.
++     */
++    for (i = 0; i < 8; i++)
++    {
++      if (trap->DirtyBits.s.GlobalsDirty & (1 << i))
++          ELAN3_OP_STORE32 (ctxt, stack + offsetof (E3_Stack, Globals[i]), trap->Registers[REG_GLOBALS+(i^WordEndianFlip)]);
++      if (trap->DirtyBits.s.OutsDirty & (1 << i))
++          ELAN3_OP_STORE32 (ctxt, stack + offsetof (E3_Stack, Outs[i]),    trap->Registers[REG_OUTS+(i^WordEndianFlip)]);
++      if (trap->DirtyBits.s.LocalsDirty & (1 << i))
++          ELAN3_OP_STORE32 (ctxt, stack + offsetof (E3_Stack, Locals[i]),  trap->Registers[REG_LOCALS+(i^WordEndianFlip)]);
++      if (trap->DirtyBits.s.InsDirty & (1 << i))
++          ELAN3_OP_STORE32 (ctxt, stack + offsetof (E3_Stack, Ins[i]),     trap->Registers[REG_INS+(i^WordEndianFlip)]);
++    }
++
++    /* always restore all registers */
++    orflag = ThreadRestartFromTrapBit | ThreadReloadAllRegs;
++    
++    ELAN3_OP_END_FAULT_CHECK (ctxt);
++
++    return (trap->sp | orflag);
++}
++
++void
++ReissueStackPointer (ELAN3_CTXT *ctxt, E3_Addr StackPointer)
++{
++    PRINTF1 (ctxt, DBG_TPROC, "ReissueStackPointer : Queue SP %08x\n", StackPointer);
++    
++    kmutex_lock (&ctxt->SwapListsLock);
++    ctxt->ItemCount[LIST_THREAD]++;
++    ELAN3_OP_PUT_WORD_ITEM (ctxt, LIST_THREAD, StackPointer);
++    kmutex_unlock (&ctxt->SwapListsLock);
++}
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan3/tprocinsts.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan3/tprocinsts.c      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan3/tprocinsts.c   2005-05-11 12:10:12.438932576 -0400
+@@ -0,0 +1,401 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: tprocinsts.c,v 1.20 2003/09/24 13:57:25 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/os/tprocinsts.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elanvp.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanctxt.h>
++#include <elan3/elandebug.h>
++#include <elan3/urom_addrs.h>
++#include <elan3/thread.h>
++#include <elan3/vmseg.h>
++#include <elan3/elan3mmu.h>
++
++#define MAXINSTR      256             /* # Instructions to look at while looking for close */
++
++static E3_uint32 ALU (ELAN3_CTXT *ctxt,
++                    E3_uint32 fcode, E3_uint32 X, E3_uint32 Y,
++                    E3_uint32 *Z, E3_uint32 *N, E3_uint32 *C, E3_uint32 *V);
++
++char *OpcodeNames[] =
++{
++   "ADD   ",
++   "AND   ",
++   "OR    ",
++   "XOR   ",
++   "SUB   ",
++   "ANDN  ",
++   "ORN   ",
++   "XNOR  ",
++   "ADDX  ",
++   "UNIP  ",
++   "UMUL  ",
++   "SMUL  ",
++   "SUBX  ",
++   "UNIP  ",
++   "UDIV  ",
++   "SDIV  ",
++   "ADDcc ",
++   "ANDcc ",
++   "ORcc  ",
++   "XORcc ",
++   "SUBcc ",
++   "ANDNcc",
++   "ORNcc ",
++   "XNORcc",
++   "ADDXcc",
++   "UNIPcc",
++   "UMULcc",
++   "SMULcc",
++   "SUBXcc",
++   "UNIPcc",
++   "UDIVcc",
++   "SDIVcc"
++};
++
++#define REGISTER_VALUE(trap, rN)              (((rN) == 0) ? 0 : (trap)->Registers[(rN)^WordEndianFlip])
++#define ASSIGN_REGISTER(trap, rN, value)      ((rN) != 0 ? trap->Registers[(rN)^WordEndianFlip] = (value) : 0)
++
++int
++RollThreadToClose (ELAN3_CTXT *ctxt, THREAD_TRAP *trap, E3_uint32 PAckVal)
++{
++    E3_Addr   pc      = (trap->pc & PC_MASK);
++    E3_Addr   npc     = (trap->npc & PC_MASK);
++    E3_uint32 Z       = (trap->npc & PSR_Z_BIT) ? 1 : 0;
++    E3_uint32 N       = (trap->npc & PSR_N_BIT) ? 1 : 0;
++    E3_uint32 C       = (trap->pc  & PSR_C_BIT) ? 1 : 0;
++    E3_uint32 V       = (trap->pc  & PSR_V_BIT) ? 1 : 0;
++    E3_uint32 instr;
++    E3_Addr   addr;
++
++    if (ELAN3_OP_START_FAULT_CHECK (ctxt))
++    {
++    failed:
++      ELAN3_OP_END_FAULT_CHECK (ctxt);
++
++      ElanException (ctxt, EXCEPTION_SIMULATION_FAILED, THREAD_PROC, trap);
++      return (EFAULT);
++    }
++
++    /*
++     * Thread trapped with output open, or while closing,
++     * so roll the PC forwards to the instruction after the
++     * next c_close, and execute that with the register
++     * specified in c_close set to the trap which occured.
++     * (This is not 1 which means an ACK)
++     */
++    PRINTF1 (ctxt, DBG_TPROC, "RollThreadToClose: roll pc %x to c_close\n", pc);
++    
++    for (;;)
++    {
++      instr = ELAN3_OP_LOAD32 (ctxt, pc);
++
++      PRINTF2 (ctxt, DBG_TPROC, "RollThreadToClose: PC=%x INSTR=%x\n", pc, instr);
++
++      switch (OPCODE_CLASS(instr))
++      {
++      case OPCODE_CLASS_0:
++          switch ((instr) & OPCODE_CLASS0_MASK)
++          {
++          case OPCODE_SETHI:
++              PRINTF3 (ctxt, DBG_TPROC, "PC %x : sethi r%d = %x\n", pc, INSTR_RD(instr), instr << 10);
++
++              ASSIGN_REGISTER (trap, INSTR_RD(instr), instr << 10);
++              break;
++
++          case OPCODE_SENDREG:
++              PRINTF1 (ctxt, DBG_TPROC, "PC %x : sendreg\n", pc);
++              break;
++              
++          case OPCODE_SENDMEM:
++              PRINTF1 (ctxt, DBG_TPROC, "PC %x : sendmem\n", pc);
++              break;
++              
++          case OPCODE_BICC:
++          {
++              int     DoBranch   = (instr >> 28) & 1;
++              int     CondBranch = 1;
++              E3_Addr OldnPC     = npc;
++
++              PRINTF5 (ctxt, DBG_TPROC, "PC %x : Bicc Z=%x N=%x C=%x V=%x ", pc, Z, N, C, V);
++              switch (instr & OPCODE_BICC_MASK)
++              {
++              case OPCODE_BICC_BN:    CondBranch = 0;                 break;
++              case OPCODE_BICC_BE:    DoBranch ^= Z;                  break;
++              case OPCODE_BICC_BLE:   DoBranch ^= Z | (N ^ V);        break;
++              case OPCODE_BICC_BL:    DoBranch ^= N ^ V;              break;
++              case OPCODE_BICC_BLEU:  DoBranch ^= C | Z;              break;
++              case OPCODE_BICC_BCS:   DoBranch ^= C;                  break;
++              case OPCODE_BICC_BNEG:  DoBranch ^= N;                  break;
++              case OPCODE_BICC_BVS:   DoBranch ^= V;                  break;
++              }
++
++              /* Do the branch */
++              if (DoBranch != 0)
++              {
++                  npc = pc + (((instr & 0x3fffff) << 2) |
++                              (((instr & 0x200000) != 0) ? 0xff000000 : 0));
++                  
++                  PRINTF2 (ctxt, DBG_TPROC, "PC %x : branch taken to %x\n", pc, npc);
++              }
++              else
++              {
++                  npc = npc + 4;
++                  PRINTF1 (ctxt, DBG_TPROC, "PC %x : branch not taken\n", pc);
++              }
++              pc = OldnPC;
++
++              /* Test if the next is annuled */
++              if (((instr & OPCODE_BICC_ANNUL) != 0) &
++                  ((DoBranch == 0) | (CondBranch == 0)))
++              {
++                  PRINTF1 (ctxt, DBG_TPROC, "PC %x : branch annulled\n", pc);
++
++                  pc = npc;
++                  npc += 4;
++              }
++
++              /*
++               * we've already consumed the instruction - so continue rather
++               * than break;
++               */
++              continue;
++          }
++          
++          default:
++              PRINTF2 (ctxt, DBG_TPROC, "PC %x : unknown class 0 instr %x\n", pc, instr);
++              goto failed;
++          }
++          break;
++
++      case OPCODE_CLASS_1:
++              PRINTF2 (ctxt, DBG_TPROC, "PC %x : unknown class 1 instr %x\n", pc, instr);
++              goto failed;
++              
++      case OPCODE_CLASS_2:
++      {
++          E3_uint32 X = REGISTER_VALUE (trap, INSTR_RS1(instr));
++          E3_uint32 Y = (instr & OPCODE_IMM) ? INSTR_IMM(instr) : REGISTER_VALUE (trap, INSTR_RS2(instr));
++          
++          if ((instr & OPCODE_NOT_ALUOP) == 0)
++          {
++              E3_uint32 fcode  = (instr >> OPCODE_FCODE_SHIFT) & OPCODE_FCODE_MASK;
++              E3_uint32 result = ALU (ctxt, fcode, X, Y, &Z, &N, &C, &V);
++
++              PRINTF5 (ctxt, DBG_TPROC, "PC %x : %s %x %x -> %x", pc, OpcodeNames[fcode], X, Y, result);
++              PRINTF4 (ctxt, DBG_TPROC, "        Z=%x N=%x C=%x V=%x\n", Z, N, C, V);
++              
++              ASSIGN_REGISTER (trap, INSTR_RD(instr), result);
++          }
++          else
++          {
++              switch (instr & OPCODE_MASK)
++              {
++              case OPCODE_OPEN:
++                  PRINTF1 (ctxt, DBG_TPROC, "PC %x : c_open\n", pc);
++                  break;
++
++              case OPCODE_CLOSE:
++                  PRINTF1 (ctxt, DBG_TPROC, "PC %x : c_close\n", pc);
++                  goto found_close;
++
++              case OPCODE_SLL:
++                  PRINTF1 (ctxt, DBG_TPROC, "PC %x : SLL\n", pc);
++
++                  ASSIGN_REGISTER (trap, INSTR_RD(instr), X << Y);
++                  break;
++                  
++              case OPCODE_SRL:
++                  PRINTF1 (ctxt, DBG_TPROC, "PC %x : SRL\n", pc);
++                  
++                  ASSIGN_REGISTER (trap, INSTR_RD(instr), X >> Y);
++                  break;
++                  
++              case OPCODE_SRA:
++                  PRINTF1 (ctxt, DBG_TPROC, "PC %x : SRA\n", pc);
++                  
++                  ASSIGN_REGISTER (trap, INSTR_RD(instr), X >> Y);
++                  break;
++                  
++              case OPCODE_BREAKTEST:
++                  PRINTF1 (ctxt, DBG_TPROC, "PC %x : BREAKTEST  not allowed while open\n", pc);
++                  goto failed;
++                  
++              case OPCODE_BREAK:
++                  PRINTF1 (ctxt, DBG_TPROC, "PC %x : BREAK not allowed while open\n", pc);
++                  goto failed;
++
++              case OPCODE_SUSPEND:
++                  PRINTF1 (ctxt, DBG_TPROC, "PC %x : SUSPEND not allowed while open\n", pc);
++                  goto failed;
++                  
++              case OPCODE_WAIT:
++                  PRINTF1 (ctxt, DBG_TPROC, "PC %x : WAIT not allowed while open\n", pc);
++                  goto failed;
++
++              default:
++                  PRINTF2 (ctxt, DBG_TPROC, "PC %x : unknown class 2 instr %x\n", pc, instr);
++                  goto failed;
++              }
++          }
++          break;
++      }
++      
++      case OPCODE_CLASS_3:
++      {
++          if ((instr & OPCODE_IMM) != 0)
++              addr = REGISTER_VALUE (trap, INSTR_RS1(instr)) + INSTR_IMM(instr);
++          else
++              addr = (REGISTER_VALUE (trap, INSTR_RS1(instr)) + 
++                      REGISTER_VALUE (trap, INSTR_RS2(instr)));
++
++          switch (instr & OPCODE_MASK)
++          {
++          case OPCODE_LD:
++              PRINTF3 (ctxt, DBG_TPROC, "PC %x : LD [%x], r%d\n", pc, addr, INSTR_RD(instr));
++              
++              ASSIGN_REGISTER (trap, INSTR_RD(instr), ELAN3_OP_LOAD32 (ctxt, addr));
++              break;
++              
++          case OPCODE_LDD:
++          case OPCODE_LDBLOCK16:
++          case OPCODE_LDBLOCK32:
++          case OPCODE_LDBLOCK64:
++              PRINTF2 (ctxt, DBG_TPROC, "PC %x : LDBLOCKx @ %x is not possible while output open\n", pc, addr);
++              goto failed;
++          
++          case OPCODE_ST:
++              PRINTF2 (ctxt, DBG_TPROC, "PC %x : ST @ %x\n", pc, addr);
++              
++              ELAN3_OP_STORE32 (ctxt, addr, REGISTER_VALUE (trap, INSTR_RD(instr)));
++              break;
++                            
++          case OPCODE_STD:
++          case OPCODE_STBLOCK16:
++          case OPCODE_STBLOCK32:
++          case OPCODE_STBLOCK64:
++              PRINTF2 (ctxt, DBG_TPROC, "PC %x : STD @ %x is not posisble while output open\n", pc, addr);
++              goto failed;
++
++          case OPCODE_SWAP:
++              PRINTF2 (ctxt, DBG_TPROC, "PC %x : SWAP @ %x is not posible while output open\n", pc, addr);
++              goto failed;
++              
++          default:
++              PRINTF2 (ctxt, DBG_TPROC, "PC %x : unknown class 3 instr %x\n", pc, instr);
++              goto failed;
++          }
++          break;
++      }}
++
++      pc = npc;
++      npc += 4;
++    }
++    
++found_close:
++    ELAN3_OP_END_FAULT_CHECK (ctxt);
++
++    PRINTF1 (ctxt, DBG_TPROC, "PC %x : c_close\n", pc);
++    
++    /*
++     * Found the new pc, and have the close instruction in *instr
++     */
++    ASSIGN_REGISTER (trap, INSTR_RD(instr), PAckVal);
++    
++    /*
++     * Move to instruction after close.
++    */
++    trap->pc = npc;
++    
++    /* Insert the value of Z and N from the close inst */
++    trap->npc = (npc + 4) | ((PAckVal == E3_PAckOk) ? 1 :
++                           (PAckVal == E3_PAckTestFail) ? 2 : 0);
++
++    return (ESUCCESS);
++}
++
++E3_uint32
++ALU (ELAN3_CTXT *ctxt,
++     E3_uint32 fcode, E3_uint32 X, E3_uint32 Y,
++     E3_uint32 *Z, E3_uint32 *N, E3_uint32 *C, E3_uint32 *V)
++{
++    E3_uint32 XMSB, YMSB, ZMSB, Cprime;
++    E3_uint32 Yprime;
++    E3_uint32 Result=0;
++    
++    Yprime = ((fcode >> 2) & 1) ? ~Y : Y;
++    Cprime = ((fcode >> 2) & 1) ^ (*C & ((fcode >> 3) & 1));
++    XMSB             = (X >> 31) & 1;
++    YMSB             = (Yprime >> 31) & 1;
++    /* mul or div */
++    if ((fcode & 0xa) == 0xa)
++    {
++      PRINTF0 (ctxt, DBG_TPROC, "ALU: tried a multiply or a divide\n");
++      return (0);
++    }
++
++    switch (fcode & 3)
++    {
++      /*ADD */
++    case 0:
++      Result = X + Yprime + Cprime ;
++      if ((fcode & 0x10) == 0)
++          return (Result);
++      
++      ZMSB   = Result >> 31;
++      *V = ((XMSB & YMSB & ~ZMSB) | (~XMSB &~YMSB &  ZMSB));
++      *C = ((fcode >> 2) & 1) ^ ( (XMSB & YMSB) | (~ZMSB & (XMSB | YMSB)));
++      break;
++      
++      /*AND */
++    case 1:
++      Result = X & Yprime ;
++      if ((fcode & 0x10) == 0)
++          return (Result);
++      
++      *V = 0;
++      *C = 0;
++      break;
++      
++      /*OR  */
++    case 2:
++      Result = X | Yprime ;
++      if ((fcode & 0x10) == 0)
++          return (Result);
++      
++      *V = 0;
++      *C = 0;
++      break;
++      
++      /*XOR */
++    case 3:
++      Result = X ^ Yprime ;
++      if ((fcode & 0x10) == 0)
++          return (Result);
++      
++      *V = 0;
++      *C = 0;
++      break;
++    }
++    
++    *Z = (Result == 0) ? 1 : 0;
++    *N = (Result >> 31) & 1;
++
++    return (Result);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan3/tproc_linux.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan3/tproc_linux.c     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan3/tproc_linux.c  2005-05-11 13:24:43.188275368 -0400
+@@ -0,0 +1,215 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "$Id: tproc_linux.c,v 1.19.2.1 2004/10/28 17:08:56 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/os/tproc_linux.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <qsnet/autoconf.h>
++
++#include <asm/mman.h>
++#include <linux/file.h>
++
++#ifdef NO_ABI
++#include <asm/poll.h>
++extern asmlinkage long sys_open(const char *, int, int);
++extern asmlinkage ssize_t sys_write(unsigned int, const char *, size_t);
++extern asmlinkage ssize_t sys_read(unsigned int, char *, size_t);
++extern asmlinkage off_t sys_lseek(unsigned int, off_t, unsigned int);
++extern asmlinkage long sys_poll(struct pollfd *, unsigned int, long);
++extern asmlinkage long sys_kill(int, int); 
++#else
++#     include <linux/syscalls.h>
++#endif
++
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elanvp.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanctxt.h>
++#include <elan3/elandebug.h>
++#include <elan3/urom_addrs.h>
++#include <elan3/thread.h>
++#include <elan3/elansyscall.h>
++#include <elan3/threadsyscall.h>
++
++/*
++ * NOTE: system calls from kernel on Linux are different on alpha and i386 
++ *       on alpha they return -errno on failure 
++ *       on i386  they return -1 on failure and set errno 
++ */
++
++static void
++ReturnSyscall (THREAD_TRAP *trap, unsigned long rc, int *skip)
++{
++    if (rc >= (unsigned long) (-130))
++    {
++      trap->pc |= PSR_C_BIT;  /* clear carry to indicate failure */
++
++      trap->Registers[REG_OUTS+(0^WordEndianFlip)] = -rc;
++    } 
++    else 
++    {
++      trap->pc &= ~PSR_C_BIT; /* set carry to indicate success */
++      trap->Registers[REG_OUTS+(0^WordEndianFlip)] = rc;
++    }
++    trap->Registers[REG_OUTS+(1^WordEndianFlip)] = 0;
++    *skip = 1;
++}
++
++static void 
++dump_regs(ELAN3_CTXT *ctxt, THREAD_TRAP *trap)
++{
++    PRINTF (ctxt, DBG_TPROC, "               OUTS %08x %08x %08x %08x\n",
++      trap->Registers[REG_OUTS+(0^WordEndianFlip)], 
++      trap->Registers[REG_OUTS+(1^WordEndianFlip)],
++      trap->Registers[REG_OUTS+(2^WordEndianFlip)], 
++      trap->Registers[REG_OUTS+(3^WordEndianFlip)]);
++    PRINTF (ctxt, DBG_TPROC, "                    %08x %08x %08x %08x\n",
++      trap->Registers[REG_OUTS+(4^WordEndianFlip)], 
++      trap->Registers[REG_OUTS+(5^WordEndianFlip)],
++      trap->Registers[REG_OUTS+(6^WordEndianFlip)], 
++      trap->Registers[REG_OUTS+(7^WordEndianFlip)]);
++}
++
++int
++ThreadSyscall (ELAN3_CTXT *ctxt, THREAD_TRAP *trap, int *skip)
++{
++    int                 code;
++    caddr_t       maddr;
++    struct file  *file;
++    unsigned long rc;
++    int           i;
++    uintptr_t     av[6];
++    uintptr_t     ptr;
++   
++    PRINTF (ctxt, DBG_TPROC, "ThreadSyscall: PC %08x G1 %08x\n", 
++      trap->pc, trap->Registers[REG_GLOBALS+(1^WordEndianFlip)]);
++    dump_regs(ctxt, trap);
++
++    code = trap->Registers[REG_GLOBALS+(1^WordEndianFlip)];
++
++    /* Copy the system call arguments from %o0-%o5 */
++    for (i = 0; i < 6; i++)
++      av[i] = trap->Registers[REG_OUTS+(i^WordEndianFlip)];
++    
++    rc = (unsigned long) -EINVAL;
++
++    switch (code) {
++      case ELAN3_SYS_open:
++          maddr = elan3mmu_mainaddr (ctxt->Elan3mmu, (E3_Addr) av[0]);
++          if (maddr != NULL)
++              rc = sys_open((const char *)maddr, av[1], av[2]);
++          break;
++
++      case ELAN3_SYS_close:
++          rc = sys_close(av[0]);
++          break;
++
++      case ELAN3_SYS_write:
++          maddr = elan3mmu_mainaddr (ctxt->Elan3mmu, (E3_Addr) av[1]);
++          if (maddr != NULL)
++              rc = sys_write(av[0], (const char *)maddr, av[2]);
++          break;
++
++      case ELAN3_SYS_read:
++          maddr = elan3mmu_mainaddr (ctxt->Elan3mmu, (E3_Addr) av[1]);
++          if (maddr != NULL)
++              rc = sys_read(av[0], (char *)maddr, av[2]);
++          break;
++
++      case ELAN3_SYS_poll:
++          maddr = elan3mmu_mainaddr (ctxt->Elan3mmu, (E3_Addr) av[0]);
++          if (maddr != NULL)
++              rc = sys_poll((struct pollfd *)maddr, av[1], av[2]);
++          break;
++      
++      case ELAN3_SYS_lseek:
++          rc = sys_lseek(av[0], av[1], av[2]);
++          break;
++          
++      case ELAN3_SYS_mmap:
++          if ((E3_Addr) av[0] == (E3_Addr) 0)
++              maddr = NULL;
++          else if ((maddr = elan3mmu_mainaddr (ctxt->Elan3mmu, (E3_Addr) av[0])) == NULL)
++              break;
++      
++          file = NULL;
++          /* GNAT 5515: If *not* anonymous memory need to do fget */
++          if ((av[3] & MAP_ANONYMOUS) == 0 && (file = fget (av[4])) == NULL)
++          {
++              rc = -EBADF;
++              break;
++          }
++
++          down_write (&current->mm->mmap_sem);
++          ptr = do_mmap_pgoff (file, (unsigned long) maddr, av[1], av[2], av[3], av[5] >>PAGE_SHIFT);
++          up_write (&current->mm->mmap_sem);
++
++          if (file)
++              fput (file);
++          
++          if (IS_ERR((void *) ptr))
++              rc = PTR_ERR((void *) ptr);
++          else
++              rc = elan3mmu_elanaddr (ctxt->Elan3mmu, (caddr_t)ptr);
++
++          break;
++      
++      case ELAN3_SYS_munmap:
++          maddr = elan3mmu_mainaddr (ctxt->Elan3mmu, (E3_Addr) av[0]);
++
++#ifdef AC
++          if (maddr != NULL)
++              rc = do_munmap(current->mm, (unsigned long) maddr, av[1], 1);
++#else
++          if (maddr != NULL)
++              rc = do_munmap(current->mm, (unsigned long) maddr, av[1]);
++#endif
++          break;
++
++      case ELAN3_SYS_kill:
++          rc = sys_kill(av[0], av[1]);
++          break;
++
++      case ELAN3_SYS_getpid:
++          rc = current->pid;
++          break;
++
++      default:
++          return EINVAL;
++    }
++    ReturnSyscall(trap, rc, skip);
++    return ESUCCESS;
++}
++
++
++int
++ThreadElancall (ELAN3_CTXT *ctxt, THREAD_TRAP *trap, int *skip)
++{
++      int ret = ESUCCESS;
++
++      PRINTF (ctxt, DBG_TPROC, "ThreadElancall: PC %08x\n", trap->pc);
++      dump_regs(ctxt, trap);
++
++      /*
++       * Elan system call 'type' is passed in o0
++       */
++      switch (trap->Registers[REG_OUTS+(0^WordEndianFlip)]) 
++      {
++      default:
++              ret = EINVAL;
++              break;
++      }
++      return ret;
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan3/virtual_process.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan3/virtual_process.c 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan3/virtual_process.c      2005-05-11 12:10:12.440932272 -0400
+@@ -0,0 +1,884 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: virtual_process.c,v 1.68 2004/06/07 13:50:10 mike Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/os/virtual_process.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan/elanmod.h>
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elanvp.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanctxt.h>
++#include <elan3/elandebug.h>
++#include <elan3/urom_addrs.h>
++#include <elan3/thread.h>
++#include <elan3/vmseg.h>
++#include <elan3/elansyscall.h>
++
++static ELAN3_VPSEG *
++InstallSegment (ELAN3_CTXT *ctxt, int process, int entries)
++{
++    ELAN3_VPSEG **prevSeg, *seg;
++    int lastTop = -1;
++    int       top     = process + entries-1;
++
++    ASSERT (krwlock_is_write_locked (&ctxt->VpLock));
++
++    for (prevSeg = &ctxt->VpSegs; (seg = (*prevSeg)) != NULL; prevSeg = &seg->Next)
++    {
++      int thisTop = seg->Process + seg->Entries - 1;
++
++        if (process < seg->Process && (process <= lastTop || top >= seg->Process))
++      {
++          /*
++           * Overlaps with last segment, or this one 
++           */
++          return (NULL);
++      }
++      if (seg->Process > process)
++          break;
++      
++      lastTop = thisTop;
++    }
++    
++    KMEM_ZALLOC (seg, ELAN3_VPSEG *, sizeof (ELAN3_VPSEG), TRUE);
++    
++    if (seg == (ELAN3_VPSEG *) NULL)
++      return (NULL);
++    
++    seg->Process = process;
++    seg->Entries = entries;
++
++
++    PRINTF2 (ctxt, DBG_VP, "InstallSegment: add seg %p before %p\n", seg, *prevSeg);
++
++    seg->Next = *prevSeg;
++    *prevSeg = seg;
++
++    return (seg);
++}
++
++static int 
++RemoveSegment (ELAN3_CTXT *ctxt, ELAN3_VPSEG *seg)
++{
++    ELAN3_VPSEG **prevSeg, *thisSeg;
++
++    ASSERT (krwlock_is_write_locked (&ctxt->VpLock));
++
++    for (prevSeg = &ctxt->VpSegs; (thisSeg = (*prevSeg)) != NULL; prevSeg = &thisSeg->Next)
++    {
++      if (thisSeg == seg)
++          break;
++    }
++
++    if (thisSeg == (ELAN3_VPSEG *) NULL)
++      return (EINVAL);
++
++
++    PRINTF2 (ctxt, DBG_VP, "RemoveSegment: remove seg %p next %p\n", thisSeg, thisSeg->Next);
++
++    *prevSeg = thisSeg->Next;
++    
++    KMEM_FREE ((caddr_t) seg, sizeof (ELAN3_VPSEG));
++
++    return (ESUCCESS);
++}
++
++static ELAN3_VPSEG *
++FindSegment (ELAN3_CTXT *ctxt, int low, int high)
++{
++    ELAN3_VPSEG *seg;
++
++    ASSERT(krwlock_is_locked (&ctxt->VpLock));
++    
++    for (seg = ctxt->VpSegs; seg; seg = seg->Next)
++    {
++      if (seg->Process <= low && (seg->Process + seg->Entries) > high)
++          return (seg);
++    }
++
++    return ((ELAN3_VPSEG *) NULL);
++}
++
++ELAN_LOCATION
++ProcessToLocation (ELAN3_CTXT *ctxt, ELAN3_VPSEG *seg, int process, ELAN_CAPABILITY *cap)
++{
++    ELAN_LOCATION location;
++    int           nnodes,nctxs;
++    int           node,ctx,i;
++
++    ASSERT(krwlock_is_locked (&ctxt->VpLock));
++
++    location.loc_node    = ELAN3_INVALID_NODE;
++    location.loc_context = -1;
++
++    PRINTF3 (ctxt, DBG_VP, "ProcessToLocation: process %d seg %p cap %p\n", process, seg, cap);
++
++    if (seg == NULL)
++      seg = FindSegment (ctxt, process, process);
++
++    if (!seg || (seg->Type != ELAN3_VPSEG_P2P))
++      return (location);
++
++    cap    = &seg->SegCapability;
++    nnodes = ELAN_CAP_NUM_NODES (cap);
++    nctxs  = ELAN_CAP_NUM_CONTEXTS (cap);
++
++    switch (seg->SegCapability.cap_type & ELAN_CAP_TYPE_MASK)
++    {
++    case ELAN_CAP_TYPE_BLOCK:
++    {
++      int entries = ELAN_CAP_ENTRIES(cap);
++
++      for (node = 0, i = 0; node < nnodes && i < entries; node++)
++      {
++          for (ctx = 0; ctx < nctxs && i < entries; ctx++)
++          {
++              if (( seg->SegCapability.cap_type & ELAN_CAP_TYPE_NO_BITMAP) || BT_TEST (seg->SegCapability.cap_bitmap, ctx + (node * nctxs)))
++              {
++                  if (i++ == (process - seg->Process))
++                  { 
++                      location.loc_node    = seg->SegCapability.cap_lownode    + node;
++                      location.loc_context = seg->SegCapability.cap_lowcontext + ctx;
++                      goto found;
++                  }
++              }
++          }
++      }
++      break;
++    }
++    case ELAN_CAP_TYPE_CYCLIC:
++    {
++      int entries = ELAN_CAP_ENTRIES(cap);
++
++      for (ctx = 0, i = 0; ctx < nctxs && i < entries; ctx++)
++      {
++          for (node = 0; node < nnodes && i < entries; node++)
++          {
++              if ((seg->SegCapability.cap_type & ELAN_CAP_TYPE_NO_BITMAP) || BT_TEST (seg->SegCapability.cap_bitmap, node + (ctx * nnodes)))
++              {                                   
++                  if (i++ ==  (process - seg->Process))
++                  { 
++                      location.loc_node    = seg->SegCapability.cap_lownode    + node;
++                      location.loc_context = seg->SegCapability.cap_lowcontext + ctx;
++                      goto found;
++                  }
++              }
++          }
++      }
++      break;  
++    }
++    default:
++      break;
++    }
++       
++ found:
++    
++    PRINTF3 (ctxt, DBG_VP, "ProcessToLocation: process %d -> Node %d Context %d\n", process, location.loc_node,  location.loc_context);
++
++    if (cap != NULL)
++    {
++      bcopy ((caddr_t) &seg->SegCapability, (caddr_t) cap, sizeof (ELAN_CAPABILITY));
++      cap->cap_mycontext = location.loc_context;
++    }
++
++    return (location);
++}
++
++int
++LocationToProcess (ELAN3_CTXT *ctxt, ELAN3_VPSEG *seg, ELAN_LOCATION loc, ELAN_CAPABILITY *cap)
++{
++    int nnodes,nctxs;
++    int node,ctx,i;
++
++    if (seg == NULL)
++      return ELAN3_INVALID_PROCESS;
++
++    if (!seg || (seg->Type != ELAN3_VPSEG_P2P))
++      return ELAN3_INVALID_PROCESS;
++
++    nnodes = cap->cap_highnode - cap->cap_lownode + 1;
++    nctxs  = cap->cap_highcontext - cap->cap_lowcontext + 1;
++
++    switch (cap->cap_type & ELAN_CAP_TYPE_MASK)
++    {
++    case ELAN_CAP_TYPE_BLOCK:
++    {
++      int entries = ELAN_CAP_ENTRIES(cap);
++
++      for (node = 0, i = 0; node < nnodes && i < entries; node++)
++      {
++          for (ctx = 0; ctx < nctxs && i < entries; ctx++)
++          {
++              if ((cap->cap_type & ELAN_CAP_TYPE_NO_BITMAP) || BT_TEST (cap->cap_bitmap, ctx + (node * nctxs)))
++              {
++                  if ((loc.loc_node    == (cap->cap_lownode + node) ) 
++                      && (loc.loc_context == (cap->cap_lowcontext + ctx) ))
++                  {
++                      return (i + seg->Process);
++                  }
++                  i++;
++              }
++          }
++      }
++      break;
++    } 
++    case ELAN_CAP_TYPE_CYCLIC:
++    {
++      int entries = ELAN_CAP_ENTRIES(cap);
++
++      for (ctx = 0, i = 0; ctx < nctxs && i < entries; ctx++)
++      {
++          for (node = 0; node < nnodes && i < entries; node++)
++          {
++              if ((cap->cap_type & ELAN_CAP_TYPE_NO_BITMAP) || BT_TEST (cap->cap_bitmap, node + (ctx * nnodes)))
++              {
++                  if ((loc.loc_node   == (cap->cap_lownode + node) ) 
++                      && (loc.loc_context == (cap->cap_lowcontext + ctx) ))
++                  {
++                      return (i + seg->Process);
++                  }
++                  i++;
++                  
++              }
++          }
++      }
++      break;
++    } 
++    default:
++      break;
++    }
++       
++    return ELAN3_INVALID_PROCESS;
++}
++
++int
++elan3_addvp (ELAN3_CTXT *ctxt, int process, ELAN_CAPABILITY *cap)
++{
++    ELAN3_DEV      *dev = ctxt->Device;
++    ELAN_POSITION    *pos = &ctxt->Position;
++    ELAN3_VPSEG       *seg;
++    int                     i;
++    int                     nodeOff;
++    int                     ctxOff;
++    int                     nnodes;
++    int                     nctxs;
++    E3_uint16         flits[MAX_FLITS];
++    int               nflits;
++    int               entries;
++
++    PRINTF2 (ctxt, DBG_VP, "elan3_addvp: %d -> %s\n", process, CapabilityString (cap));
++
++    entries = ELAN_CAP_ENTRIES(cap);
++    if (entries <= 0 || (process + entries) > ELAN3_MAX_VPS)
++      return (EINVAL);
++
++    /*
++     * Scan the virtual process segment list, to add this entry, and ensure that
++     * the ranges don't overlap.
++     */
++    krwlock_write (&ctxt->VpLock);
++
++    /* check cap. */
++    switch (elan3_validate_cap (ctxt->Device, cap, ELAN_USER_P2P))
++    {
++    case ELAN_CAP_OK:
++      /* nothing */
++      break;
++
++    case ELAN_CAP_RMS:
++      if ( elan_validate_map(cap, cap) != ESUCCESS)
++      {
++          krwlock_done (&ctxt->VpLock);
++          return (EINVAL);
++      }
++      break;
++
++    default:
++      krwlock_done (&ctxt->VpLock);
++      return (EINVAL);
++    }
++
++    if ((seg = InstallSegment (ctxt, process, entries)) == NULL)
++    {
++      PRINTF0 (ctxt, DBG_VP, "elan3_addvp: failed to find a seg\n");
++      krwlock_done (&ctxt->VpLock);
++      return (EINVAL);
++    }
++    
++    seg->Type                        = ELAN3_VPSEG_P2P;
++    seg->SegCapability               = *cap;
++    seg->SegCapability.cap_mycontext = ELAN_CAP_UNINITIALISED;
++
++    PRINTF3 (ctxt, DBG_VP, "elan3_addvp: segment type %x  %d %d\n",
++          seg->SegCapability.cap_type, seg->Process, entries);
++
++
++    nnodes = cap->cap_highnode - cap->cap_lownode + 1;
++    nctxs  = cap->cap_highcontext - cap->cap_lowcontext + 1;
++
++    /* position not determined, so cannot load any routes, the hwtest
++     * process must explicitly set it's own routes */
++    
++    if (!(cap->cap_type & ELAN_CAP_TYPE_HWTEST) && (pos->pos_mode != ELAN_POS_UNKNOWN))
++    {
++      switch (cap->cap_type & ELAN_CAP_TYPE_MASK)
++      {
++      case ELAN_CAP_TYPE_BLOCK:
++          for (nodeOff = 0, i = 0; nodeOff < nnodes && i < entries; nodeOff++)
++          {
++              for (ctxOff = 0; ctxOff < nctxs && i < entries; ctxOff++)
++              {
++                  if ((cap->cap_type & ELAN_CAP_TYPE_NO_BITMAP) || BT_TEST (cap->cap_bitmap, ctxOff + (nodeOff * nctxs)))
++                  {
++                      /* Don't load a route if there's no switch and trying to talk to myself */
++                      if (pos->pos_mode == ELAN_POS_MODE_SWITCHED ||
++                          (pos->pos_mode == ELAN_POS_MODE_LOOPBACK && cap->cap_lownode + nodeOff == pos->pos_nodeid) ||
++                          (pos->pos_mode == ELAN_POS_MODE_BACKTOBACK && cap->cap_lownode + nodeOff != pos->pos_nodeid))
++                      {
++                          PRINTF3 (ctxt, DBG_VP, "elan3_addvp: virtual process %d -> node %d context %d\n",
++                                   seg->Process + i, cap->cap_lownode +nodeOff, cap->cap_lowcontext +ctxOff);
++                          
++                          nflits = GenerateRoute (pos, flits, cap->cap_lownode + nodeOff, cap->cap_lownode + nodeOff,
++                                                  DEFAULT_ROUTE_TIMEOUT, DEFAULT_ROUTE_PRIORITY);
++                          
++
++
++                          LoadRoute (dev, ctxt->RouteTable, seg->Process+i, cap->cap_lowcontext + ctxOff, nflits, flits);  
++                      }
++                      
++                      i++;
++                  }
++              }
++          }
++          break;
++          
++      case ELAN_CAP_TYPE_CYCLIC:
++          for (ctxOff = 0, i = 0; ctxOff < nctxs && i < entries; ctxOff++)
++          {
++              for (nodeOff = 0; nodeOff < nnodes && i < entries; nodeOff++)
++              {
++                  if ((cap->cap_type & ELAN_CAP_TYPE_NO_BITMAP) || BT_TEST (cap->cap_bitmap, nodeOff + (ctxOff * nnodes)))
++                  {
++                      /* Don't load a route if there's no switch and trying to talk to myself */
++                      if (pos->pos_mode == ELAN_POS_MODE_SWITCHED ||
++                          (pos->pos_mode == ELAN_POS_MODE_LOOPBACK && cap->cap_lownode + nodeOff == pos->pos_nodeid) ||
++                          (pos->pos_mode == ELAN_POS_MODE_BACKTOBACK && cap->cap_lownode + nodeOff != pos->pos_nodeid))
++                      {
++                          PRINTF3 (ctxt, DBG_VP, "elan3_addvp: virtual process %d -> node %d context %d\n",
++                                   seg->Process + i, cap->cap_lownode + nodeOff, cap->cap_lowcontext +ctxOff);
++                      
++                          nflits = GenerateRoute (pos, flits, cap->cap_lownode + nodeOff, cap->cap_lownode + nodeOff,
++                                                  DEFAULT_ROUTE_TIMEOUT, DEFAULT_ROUTE_PRIORITY);
++                          
++
++                          LoadRoute (dev, ctxt->RouteTable, seg->Process+i, cap->cap_lowcontext +ctxOff, nflits, flits);  
++                      } 
++                      i++;                
++                  }
++              }
++          }
++          break;      
++      default:
++          break;
++      }
++    }
++  
++    krwlock_done (&ctxt->VpLock);
++
++    return (ESUCCESS);
++}
++
++int
++elan3_removevp (ELAN3_CTXT *ctxt, int process)
++{
++    ELAN3_VPSEG *seg;
++    ELAN3_VPSEG *next;
++    int               i;
++
++    krwlock_write (&ctxt->VpLock);
++
++    PRINTF1 (ctxt, DBG_VP, "elan3_removevp: remove process %d\n", process);
++
++    if (process == ELAN3_INVALID_PROCESS)
++      seg = ctxt->VpSegs;
++    else
++      seg = FindSegment (ctxt, process, process);
++
++    if (seg == (ELAN3_VPSEG *) NULL)
++    {
++      krwlock_done (&ctxt->VpLock);
++      return (EINVAL);
++    }
++    
++    do {
++      PRINTF3 (ctxt, DBG_VP, "elan3_removevp: segment is %p [%x,%x]\n",
++               seg, seg->Process, seg->Process+seg->Entries);
++
++      for (i = 0; i < seg->Entries; i++)
++          ClearRoute (ctxt->Device, ctxt->RouteTable, seg->Process+i);
++
++        /* get Next pointer value before structure is free'd */
++        next = seg->Next;     
++      RemoveSegment (ctxt, seg);
++
++    } while (process == ELAN3_INVALID_PROCESS && (seg = next) != NULL);
++    
++    krwlock_done (&ctxt->VpLock);
++
++    return (ESUCCESS);
++}
++
++int
++elan3_addbcastvp (ELAN3_CTXT *ctxt, int process, int lowProc, int highProc)
++{
++    ELAN_POSITION *pos = &ctxt->Position;
++    ELAN3_VPSEG    *seg;
++    ELAN3_VPSEG    *aseg;
++    int            virtualProcess;
++    E3_uint64    routeValue;
++
++    PRINTF3 (ctxt, DBG_VP, "elan3_addbcastvp: process %d [%d,%d]\n", process, lowProc, highProc);
++
++    if (lowProc > highProc || pos->pos_mode != ELAN_POS_MODE_SWITCHED)
++      return (EINVAL);
++    
++    krwlock_write (&ctxt->VpLock);
++
++    if ((aseg = FindSegment (ctxt, lowProc, highProc)) == NULL || (aseg->Type != ELAN3_VPSEG_P2P))
++    {
++      PRINTF2 (ctxt, DBG_VP, "elan3_addbcastvp: process [%d,%d] does not map to p2p segment\n", lowProc, highProc);
++      krwlock_done (&ctxt->VpLock);
++      return (EINVAL);
++    }
++
++    /* check aseg->SegCapability */    
++    switch (elan3_validate_cap (ctxt->Device, &aseg->SegCapability, ELAN_USER_BROADCAST))
++    {
++    case ELAN_CAP_OK:
++      /* nothing */
++      break;
++      
++    case ELAN_CAP_RMS:
++      if ( elan_validate_map(&ctxt->Capability, &aseg->SegCapability) != ESUCCESS )
++      {
++          krwlock_done (&ctxt->VpLock);
++          return (EINVAL);
++      }
++      break;
++
++    default:
++      krwlock_done (&ctxt->VpLock);
++      return (EINVAL);
++    }
++
++    if ( ProcessToLocation (ctxt, aseg, lowProc,  NULL).loc_context != 
++       ProcessToLocation (ctxt, aseg, highProc, NULL).loc_context)
++    {
++      PRINTF2 (ctxt, DBG_VP, "elan3_addbcastvp: process [%d,%d] does not map to single context\n", lowProc, highProc);
++      krwlock_done (&ctxt->VpLock);
++      return (EINVAL);
++    }
++    
++    if ((seg = InstallSegment (ctxt, process, 1)) == NULL)
++    {
++      krwlock_done (&ctxt->VpLock);
++      return (EINVAL);
++    }
++
++    seg->Type        = ELAN3_VPSEG_BROADCAST;
++    seg->SegLowProc  = lowProc;
++    seg->SegHighProc = highProc;
++
++    PRINTF4 (ctxt, DBG_VP, "elan3_addbcastvp: installed seg %p Type %d LowProc %d HighProc %d\n",
++          seg, seg->Type, seg->SegLowProc, seg->SegHighProc);
++
++    for (virtualProcess = lowProc; virtualProcess <= highProc; virtualProcess++)
++    {
++      if (virtualProcess < 0 || virtualProcess >= ctxt->RouteTable->Size)
++          routeValue = 0;
++      else
++          routeValue = elan3_sdram_readq ( ctxt->Device, ctxt->RouteTable->Table + virtualProcess * NBYTES_PER_SMALL_ROUTE);
++      
++      if (! (routeValue & ROUTE_VALID))
++      {
++          PRINTF2 (ctxt, DBG_VP, "loadvp[%x]: broadcast %x not valid\n", 
++                   ctxt->Capability.cap_mycontext, virtualProcess);
++          break;
++      }
++    }
++          
++    if (virtualProcess > highProc)                    /* All vps now present */
++    {                                         /* so load up broadcast route */
++      E3_uint16     flits[MAX_FLITS];
++      ELAN_LOCATION low    = ProcessToLocation (ctxt, aseg, lowProc,   NULL);
++      ELAN_LOCATION high   = ProcessToLocation (ctxt, aseg, highProc,  NULL);
++      int           nflits = GenerateRoute (pos, flits, low.loc_node, high.loc_node, DEFAULT_ROUTE_TIMEOUT, DEFAULT_ROUTE_PRIORITY);
++      
++      PRINTF6 (ctxt, DBG_VP, "loadvp[%x]: broadcast %d -> %x.%x [%x.%x]\n", ctxt->Capability.cap_mycontext,
++               seg->Process, low.loc_node, high.loc_node, 
++               low.loc_context, high.loc_context);
++      
++      LoadRoute ( ctxt->Device, ctxt->RouteTable, seg->Process, low.loc_context, nflits, flits);
++    }
++
++    krwlock_done (&ctxt->VpLock);
++
++    return (ESUCCESS);
++}
++
++int
++elan3_process (ELAN3_CTXT *ctxt)
++{
++    int           res = ELAN3_INVALID_PROCESS;
++    ELAN3_VPSEG   *seg;
++    ELAN_LOCATION loc;
++
++    krwlock_write (&ctxt->VpLock);
++
++    loc.loc_node    = ctxt->Position.pos_nodeid;
++    loc.loc_context = ctxt->Capability.cap_mycontext;
++
++    for (seg = ctxt->VpSegs ; seg; seg = seg->Next)
++    {
++      if (seg->Type == ELAN3_VPSEG_P2P &&
++          seg->SegCapability.cap_lowcontext  <= ctxt->Capability.cap_mycontext &&
++          seg->SegCapability.cap_highcontext >= ctxt->Capability.cap_mycontext &&
++          seg->SegCapability.cap_lownode     <= ctxt->Position.pos_nodeid &&
++          seg->SegCapability.cap_highnode    >= ctxt->Position.pos_nodeid)
++      {
++          if ((res=LocationToProcess (ctxt,seg,loc,&ctxt->Capability)) != ELAN3_INVALID_PROCESS)
++          {
++               krwlock_done (&ctxt->VpLock);
++               return res;
++          }
++      }
++    }
++
++    krwlock_done (&ctxt->VpLock);
++
++    return (res);
++}
++
++int
++elan3_check_route (ELAN3_CTXT *ctxt, int process, E3_uint16 *flits, E3_uint32 *routeError)
++{
++    PRINTF5 (ctxt, DBG_VP, "elan3_check_route: vp=%d flits=%04x %04x %04x %04x\n",
++           process, flits[0], flits[1], flits[2], flits[3]);
++    PRINTF4 (ctxt, DBG_VP, "                            %04x %04x %04x %04x\n",
++           flits[4], flits[5], flits[6], flits[7]);
++
++    krwlock_read (&ctxt->VpLock);
++    *routeError=elan3_route_check(ctxt,flits,ProcessToLocation (ctxt, NULL, process, NULL).loc_node);
++    krwlock_done (&ctxt->VpLock);
++
++    return (ESUCCESS); /* the call is a success tho the errorcode may be set */
++}
++
++int
++elan3_load_route (ELAN3_CTXT *ctxt, int process, E3_uint16 *flits)
++{
++    ELAN3_VPSEG *seg;
++    int               res = 0;
++    int               nflits;
++    int         err;
++
++    PRINTF5 (ctxt, DBG_VP, "elan3_load_route: vp=%d flits=%04x %04x %04x %04x\n",
++           process, flits[0], flits[1], flits[2], flits[3]);
++    PRINTF4 (ctxt, DBG_VP, "                            %04x %04x %04x %04x\n",
++           flits[4], flits[5], flits[6], flits[7]);
++
++    krwlock_write (&ctxt->VpLock);
++
++    /* check the route is valid */
++    if (!(ctxt->Capability.cap_type & ELAN_CAP_TYPE_HWTEST))
++    {
++      /* must have already attached to define my context number */
++      if (ctxt->Capability.cap_mycontext == ELAN_CAP_UNINITIALISED)
++      {
++          krwlock_done (&ctxt->VpLock);
++          return (EINVAL);
++      }
++
++      if ((err=elan3_route_check(ctxt,flits,ProcessToLocation (ctxt, NULL, process, NULL).loc_node)) != ELAN3_ROUTE_SUCCESS)
++      {
++          krwlock_done (&ctxt->VpLock);
++          return (EINVAL);
++      }
++    }
++
++    if ((seg = FindSegment (ctxt, process, process)) == NULL || seg->Type != ELAN3_VPSEG_P2P)
++    {
++      krwlock_done (&ctxt->VpLock);
++      return (EINVAL);
++    }
++
++    /* Calculate number of flits in this route */
++    for (nflits = 0; nflits < MAX_FLITS && flits[nflits]; nflits++)
++      ;
++    
++    res = LoadRoute (ctxt->Device, ctxt->RouteTable, process, ProcessToLocation (ctxt, seg, process, NULL).loc_context, nflits, flits);
++
++    krwlock_done (&ctxt->VpLock);
++
++    return (res);
++}
++
++int
++elan3_get_route (ELAN3_CTXT *ctxt, int process, E3_uint16 *flits)
++{
++    ELAN3_VPSEG *seg;
++    int               res = 0;
++
++    PRINTF1 (ctxt, DBG_VP, "elan3_get_route: vp=%d \n",  process);
++
++    krwlock_write (&ctxt->VpLock);
++
++    if (ctxt->RouteTable == NULL)  /* is there a route table */
++    {
++      krwlock_done (&ctxt->VpLock);
++      return (EINVAL);
++    }
++
++    if ((seg = FindSegment (ctxt, process, process)) != NULL && seg->Type != ELAN3_VPSEG_P2P)
++    {
++      krwlock_done (&ctxt->VpLock);
++      return (EINVAL);
++    }
++    
++    if (seg == NULL)
++    {
++      krwlock_done (&ctxt->VpLock);
++      return (EINVAL);
++    }
++    
++    res = GetRoute (ctxt->Device, ctxt->RouteTable, process, flits);
++    
++    krwlock_done (&ctxt->VpLock);
++
++    return (res);
++}
++
++int
++elan3_reset_route (ELAN3_CTXT *ctxt, int process)
++{
++    E3_uint16     flits[MAX_FLITS];
++
++    PRINTF1 (ctxt, DBG_VP, "elan3_reset_route: vp=%d \n",  process);
++ 
++    GenerateRoute (&ctxt->Position, flits, process, process, DEFAULT_ROUTE_TIMEOUT, DEFAULT_ROUTE_PRIORITY);
++    
++    return elan3_load_route(ctxt,process,flits);
++}
++
++int
++ResolveVirtualProcess (ELAN3_CTXT *ctxt, int process)
++{
++    E3_uint16   flits[MAX_FLITS];
++    ELAN3_DEV     *dev = ctxt->Device;
++    int                 res = ESUCCESS;
++    ELAN3_VPSEG   *seg;
++    ELAN3_VPSEG   *aseg;
++    E3_uint64   routeValue;
++
++    krwlock_read (&ctxt->VpLock);
++
++    PRINTF1 (ctxt, DBG_VP, "ResolveVirtualProcess: vp=%d \n",  process);
++
++    if (ctxt->RouteTable == NULL || process < 0 || process >= ctxt->RouteTable->Size)
++    {
++      krwlock_done (&ctxt->VpLock);
++      return (EINVAL);
++    }
++
++    if (! (seg = FindSegment (ctxt, process, process)))
++    {
++      PRINTF1 (ctxt, DBG_VP, "ResolveVirtualProcess: cannot find segment for virtual process %d\n", process);
++      krwlock_done (&ctxt->VpLock);
++      return (EINVAL);
++    }
++    
++    /* check cap. */
++    switch (elan3_validate_cap (ctxt->Device, &seg->SegCapability, ((seg->Type == ELAN3_VPSEG_P2P) ? ELAN_USER_P2P : ELAN_USER_BROADCAST)))
++    {
++    case ELAN_CAP_OK:
++      /* nothing */
++      break;
++
++    case ELAN_CAP_RMS:
++      if ( elan_validate_map(&ctxt->Capability, &seg->SegCapability) != ESUCCESS)
++      {
++          krwlock_done (&ctxt->VpLock);
++          return (EINVAL);
++      }
++      break;
++
++    default:
++      krwlock_done (&ctxt->VpLock);
++      return (EINVAL);
++    }
++
++    BumpUserStat (ctxt, LoadVirtualProcess);
++
++    routeValue = elan3_sdram_readq (dev, ctxt->RouteTable->Table + process * NBYTES_PER_SMALL_ROUTE);
++    if (routeValue & ROUTE_VALID)                             /* Virtual process already */
++    {                                                         /* loaded */
++      krwlock_done (&ctxt->VpLock);
++      return (ESUCCESS);                      
++    }
++    
++    switch (seg->Type)
++    {
++    case ELAN3_VPSEG_P2P:
++      switch (seg->SegCapability.cap_type & ELAN_CAP_TYPE_MASK)
++      {
++      case ELAN_CAP_TYPE_BLOCK:
++      case ELAN_CAP_TYPE_CYCLIC:
++          if ((res = elan_validate_map (&ctxt->Capability,&seg->SegCapability)) == ESUCCESS &&
++              (res = GetRoute(dev, ctxt->RouteTable ,process,  flits)) == ESUCCESS)
++          {
++              if (elan3_route_check(ctxt, flits, ProcessToLocation (ctxt, seg, process, NULL).loc_node))
++                  res = EINVAL;
++              else
++                  ValidateRoute(dev, ctxt->RouteTable, process);
++          }
++          break;
++      default:
++          res = EINVAL;
++          break;
++      }
++      break;
++
++    case ELAN3_VPSEG_BROADCAST:
++      /* Find the segment that this broadcast range spans. */
++      aseg = FindSegment (ctxt, seg->SegLowProc, seg->SegHighProc);
++      
++      if (aseg == NULL || (aseg->Type != ELAN3_VPSEG_P2P) || !(aseg->SegCapability.cap_type & ELAN_CAP_TYPE_BROADCASTABLE))
++      {
++          PRINTF2 (ctxt, DBG_VP, "resolveVirtualProcess: %d -> EINVAL (%s)\n", process, 
++                   (aseg == NULL ? "no segment" : ((seg->Type != ELAN3_VPSEG_P2P) ? "not point to point" :
++                                                   "not broadcastable")));
++          res = EINVAL;
++          break;
++      }
++      
++      switch (aseg->SegCapability.cap_type & ELAN_CAP_TYPE_MASK)
++      {
++      case ELAN_CAP_TYPE_BLOCK:
++      case ELAN_CAP_TYPE_CYCLIC:
++      {
++          ELAN_LOCATION lowNode  = ProcessToLocation (ctxt,aseg,seg->SegLowProc  , NULL);
++          ELAN_LOCATION highNode = ProcessToLocation (ctxt,aseg,seg->SegHighProc , NULL);
++
++
++          if ((res = elan_validate_map (&ctxt->Capability,&aseg->SegCapability)) == ESUCCESS &&
++              (res=GetRoute(dev, ctxt->RouteTable ,process,  flits)) == ESUCCESS)
++          {
++              if (elan3_route_broadcast_check(ctxt,flits, lowNode.loc_node , highNode.loc_node ) != ELAN3_ROUTE_SUCCESS )
++                  res = EINVAL;
++              else
++                  ValidateRoute(dev, ctxt->RouteTable, process);
++          }
++          break;
++      }
++
++      default:
++          res = EINVAL;
++          break;
++      }
++    default:
++      res  = EINVAL;
++      break;
++    }
++
++    krwlock_done (&ctxt->VpLock);
++    return (res);
++}       
++
++void
++UnloadVirtualProcess (ELAN3_CTXT *ctxt, ELAN_CAPABILITY *cap)
++{
++    ELAN3_DEV        *dev  = ctxt->Device;
++    ELAN3_VPSEG      *seg;
++    ELAN_CAPABILITY *scap;
++    int              i;
++
++    for (seg = ctxt->VpSegs; seg; seg = seg->Next)
++    {
++      switch (seg->Type)
++      {
++      case ELAN3_VPSEG_P2P:
++          scap = &seg->SegCapability;
++          
++          if (cap == NULL || ELAN_CAP_MATCH (scap, cap))
++          {
++              PRINTF2 (ctxt, DBG_VP, "unloadvp: segment [%x.%x]\n", 
++                       seg->Process, seg->Process + seg->Entries-1);
++              
++              for (i = 0; i < seg->Entries; i++)
++                  InvalidateRoute (dev, ctxt->RouteTable, seg->Process+i);
++          }
++          break;
++
++      case ELAN3_VPSEG_BROADCAST:
++          for (i = 0; i < seg->Entries; i++)
++          {
++              ELAN3_VPSEG *aseg = FindSegment (ctxt, seg->SegLowProc, seg->SegHighProc);
++              
++              if (aseg != NULL && ELAN_CAP_MATCH(&aseg->SegCapability, cap))
++              {
++                  PRINTF1 (ctxt, DBG_VP, "unloadvp: broadcast vp %d\n", seg->Process);
++              
++                  InvalidateRoute (dev, ctxt->RouteTable, seg->Process+i);
++              }
++          }
++      }
++    }
++}
++
++caddr_t
++CapabilityString (ELAN_CAPABILITY *cap)
++{
++#define CAPSTR_LEN    200
++#define NCAPSTRS      4
++    static char       space[CAPSTR_LEN*NCAPSTRS];
++    static int        bufnum;
++    static spinlock_t lock;
++    static int              lockinitialised;
++    int                     num;
++    unsigned long     flags;
++
++    if (! lockinitialised)
++    {
++      spin_lock_init (&lock);
++      lockinitialised = 1;
++    }
++
++    spin_lock_irqsave (&lock, flags);
++    
++    if ((num = ++bufnum) == NCAPSTRS)
++      num = bufnum = 0;
++    spin_unlock_irqrestore (&lock, flags);
++
++    sprintf (space + (num * CAPSTR_LEN), "%4x %4x %4x %4x %4x %4x %4x [%x.%x.%x.%x]", cap->cap_type,
++           cap->cap_lownode, cap->cap_highnode, 
++           cap->cap_lowcontext, cap->cap_mycontext, cap->cap_highcontext,  ELAN_CAP_ENTRIES(cap),
++           cap->cap_userkey.key_values[0],  cap->cap_userkey.key_values[1],
++           cap->cap_userkey.key_values[2],  cap->cap_userkey.key_values[3]);
++
++    return (space + (num * CAPSTR_LEN));
++}
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan4/debug.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan4/debug.c   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan4/debug.c        2005-05-11 12:10:12.441932120 -0400
+@@ -0,0 +1,94 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: debug.c,v 1.16 2004/07/07 11:22:33 addy Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/debug.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan4/debug.h>
++#include <elan4/device.h>
++
++unsigned       elan4_debug           = 0;
++unsigned       elan4_debug_toconsole = 0;
++unsigned       elan4_debug_tobuffer  = DBG_ALL;
++
++unsigned       elan4_debug_display_ctxt;
++unsigned       elan4_debug_ignore_ctxt;
++unsigned       elan4_debug_ignore_type;
++
++void
++elan4_debug_init()
++{
++    if ((elan4_debug & elan4_debug_tobuffer) != 0)
++      qsnet_debug_alloc();
++}
++
++void
++elan4_debug_fini()
++{
++}
++
++void
++elan4_debugf (void *type, int mode, char *fmt,...)
++{
++    char    prefix[128];
++    int     where = 0;
++    va_list ap;
++
++    if ((mode & elan4_debug_tobuffer) != 0 || type == DBG_BUFFER)
++      where |= QSNET_DEBUG_BUFFER;
++    if ((mode & elan4_debug_toconsole) != 0 || type == DBG_CONSOLE)
++      where |= QSNET_DEBUG_CONSOLE;
++
++    if (where == 0)
++      return;
++    
++    if ((unsigned long) type > DBG_NTYPES)
++    {
++      ELAN4_CTXT *ctxt = (ELAN4_CTXT *) type;
++
++        if (elan4_debug_display_ctxt && ctxt->ctxt_num != elan4_debug_display_ctxt)
++            return;
++        if (elan4_debug_ignore_ctxt  && ctxt->ctxt_num == elan4_debug_ignore_ctxt)
++            return;
++
++      sprintf (prefix, "[%08ld.%04d] elan4 (%03x) ", lbolt,  current->pid, ctxt->ctxt_num);
++    }
++    else if ((unsigned long) type == (int) DBG_CONSOLE)
++      prefix[0] = '\0';
++    else
++    {
++      char *what;
++
++      if (elan4_debug_ignore_type & (1 << ((unsigned long) type)))
++          return;
++
++      switch ((unsigned long) type)
++      {
++      case (int) DBG_DEVICE: what = "dev"; break;
++      case (int) DBG_USER:   what = "usr"; break;
++      default:               what = NULL; break;
++      }
++          
++      if (what)
++          sprintf (prefix, "[%08ld.%04d] elan4 [%s] ", lbolt, current->pid, what);
++      else
++          sprintf (prefix, "[%08ld.%04d] elan4 [%3d] ", lbolt, current->pid, (int)(long)type);
++    }
++
++    va_start(ap,fmt);
++    qsnet_vdebugf (where, prefix, fmt, ap);
++    va_end (ap);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan4/device.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan4/device.c  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan4/device.c       2005-05-11 12:10:12.446931360 -0400
+@@ -0,0 +1,2916 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: device.c,v 1.87.6.11 2005/03/18 13:48:53 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/device.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <qsnet/kthread.h>
++
++#include <elan4/sdram.h>
++#include <elan4/debug.h>
++#include <elan4/device.h>
++#include <elan4/commands.h>
++#include <elan4/trtype.h>
++#include <elan4/neterr.h>
++
++#include <elan4/i2c.h>
++#include <elan3/vpd.h>
++
++/* allow this code to compile against an Eagle elanmod */
++#ifdef __ELANMOD_DEVICE_H
++#define ELAN_DEV_OPS          ELANMOD_DEV_OPS
++#define ELAN_DEV_OPS_VERSION  ELANMOD_DEV_OPS_VERSION
++#define elan_dev_register     elanmod_dev_register
++#define elan_dev_deregister   elanmod_dev_deregister
++#endif
++
++/* XXXX configurational defines */
++
++#if defined (CONFIG_MPSAS)
++#define HASH_0_SIZE_VAL                       (12 + 6)
++#define HASH_1_SIZE_VAL                       (2 + 6)
++#define CTXT_TABLE_SHIFT              8
++#define LN2_MAX_CQS                   8               /* 256 */
++#else
++#define HASH_0_SIZE_VAL                       (13 + 6)
++#define HASH_1_SIZE_VAL                       (2 + 6)
++#define CTXT_TABLE_SHIFT              12
++#define LN2_MAX_CQS                   10              /* 1024 */
++#endif
++
++unsigned int elan4_hash_0_size_val       = HASH_0_SIZE_VAL;
++unsigned int elan4_hash_1_size_val       = HASH_1_SIZE_VAL;
++unsigned int elan4_ctxt_table_shift      = CTXT_TABLE_SHIFT;
++unsigned int elan4_ln2_max_cqs           = LN2_MAX_CQS;
++unsigned int elan4_dmaq_highpri_size     = 2;                 /* 8192 entries */
++unsigned int elan4_threadq_highpri_size  = 1;                 /* 1024 entries */
++unsigned int elan4_dmaq_lowpri_size      = 2;                 /* 8192 entries */
++unsigned int elan4_threadq_lowpri_size   = 1;                 /* 1024 entries */
++unsigned int elan4_interruptq_size       = 0;                 /* 1024 entries */
++unsigned int elan4_mainint_punt_loops    = 1;
++unsigned int elan4_mainint_resched_ticks = 0;
++unsigned int elan4_linkport_lock       = 0xbe0fcafe;          /* default link port lock */
++unsigned int elan4_eccerr_recheck        = 1;
++
++static int 
++elan4_op_get_position (void *arg, ELAN_POSITION *ptr)
++{
++    ELAN4_DEV     *dev = (ELAN4_DEV *)arg;
++    ELAN_POSITION  pos;
++
++    elan4_get_position (dev, &pos);
++
++    return copyout (&pos, ptr, sizeof (ELAN_POSITION));
++}
++
++static int 
++elan4_op_set_position (void *arg, unsigned short nodeid, unsigned short numnodes)
++{
++    /* XXXXX 
++
++       ELAN4_DEV *dev = (ELAN4_DEV *) arg;
++
++       compute_position (&pos, nodeid, numnode, num_down_links_value);
++
++       return elan4_set_position (dev, pos);
++    */
++    return EINVAL;
++}
++
++ELAN_DEV_OPS elan4_dev_ops = 
++{
++    elan4_op_get_position,
++    elan4_op_set_position,
++
++    ELAN_DEV_OPS_VERSION
++};
++
++static E4_uint32
++elan4_read_filter (ELAN4_DEV *dev, unsigned networkctx)
++{
++    return (elan4_sdram_readl (dev, dev->dev_ctxtable + (networkctx * sizeof (E4_ContextControlBlock)) + 
++                             offsetof (E4_ContextControlBlock, Filter)));
++}
++
++static void
++elan4_write_filter (ELAN4_DEV *dev, unsigned networkctx, E4_uint32 value)
++{
++    elan4_sdram_writel (dev, (dev->dev_ctxtable + (networkctx * sizeof (E4_ContextControlBlock)) +
++                      offsetof (E4_ContextControlBlock, Filter)), value);
++    pioflush_sdram(dev);
++}
++
++void
++elan4_set_schedstatus (ELAN4_DEV *dev, E4_uint32 intreg)
++{
++    E4_uint32 setbits  = 0;
++    E4_uint32 intmask  = 0;
++    E4_uint32 haltmask;
++    E4_uint32 next_sched;
++    E4_uint32 next_intmask;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->dev_intmask_lock, flags);
++
++    haltmask = (dev->dev_haltop_mask | dev->dev_haltop_active);
++
++    if ((haltmask & INT_DProcHalted) || dev->dev_halt_all_count || dev->dev_halt_dproc_count)
++      setbits |= SCH_DProcHalt;
++    
++    if ((haltmask & INT_TProcHalted) || dev->dev_halt_all_count || dev->dev_halt_tproc_count)
++      setbits |= SCH_TProcHalt;
++
++    if ((haltmask & INT_CProcHalted) || dev->dev_halt_all_count || dev->dev_halt_cproc_count)
++      setbits |= SCH_CProcHalt;
++
++    if ((haltmask & INT_DiscardingLowPri) || dev->dev_discard_all_count || dev->dev_discard_lowpri_count)
++      setbits |= SCH_DiscardLowPriInput;
++    
++    if ((haltmask & INT_DiscardingHighPri) || dev->dev_discard_all_count || dev->dev_discard_highpri_count)
++      setbits |= SCH_DiscardHighPriInput;
++    
++    if (dev->dev_halt_lowpri_count)
++      setbits |= SCH_StopLowPriQueues;
++    
++    if (haltmask & INT_DProcHalted) intmask |= INT_DProcHalted;
++    if (haltmask & INT_TProcHalted) intmask |= INT_TProcHalted;
++    if (haltmask & INT_CProcHalted) intmask |= INT_CProcHalted;
++    if (haltmask & INT_DiscardingLowPri) intmask |= INT_DiscardingLowPri;
++    if (haltmask & INT_DiscardingHighPri) intmask |= INT_DiscardingHighPri;
++
++    next_intmask = (dev->dev_intmask     & ~(INT_Halted | INT_Discarding)) | (intmask & ~intreg);
++    next_sched   = (dev->dev_schedstatus & ~(SCH_Halt | SCH_Discard))      | setbits;
++
++    PRINTF5 (DBG_DEVICE, DBG_REGISTER, "elan4_set_schedstatus: haltmask=%x setbits=%x intmask=%x next_sched=%x next_intmask=%x\n",
++           haltmask, setbits, intmask, next_sched, next_intmask);
++
++    CHANGE_INT_MASK (dev, next_intmask);
++    CHANGE_SCHED_STATUS (dev, next_sched);
++
++    spin_unlock_irqrestore (&dev->dev_intmask_lock, flags);
++}
++
++void
++elan4_queue_haltop (ELAN4_DEV *dev, ELAN4_HALTOP *op)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->dev_haltop_lock, flags);
++
++    /* add to the end of the halt operations list */
++    list_add_tail (&op->op_link, &dev->dev_haltop_list);
++
++    if ((dev->dev_haltop_mask & op->op_mask) != op->op_mask)
++    {
++      dev->dev_haltop_mask |= op->op_mask;
++      
++      elan4_set_schedstatus (dev, 0);
++    }
++    
++    spin_unlock_irqrestore (&dev->dev_haltop_lock, flags);
++}
++
++void
++elan4_queue_intop (ELAN4_DEV *dev, ELAN4_CQ *cq, ELAN4_INTOP *op)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->dev_intop_lock, flags);
++
++    op->op_cookie = INTOP_ONESHOT | ((dev->dev_intop_cookie++) & INTOP_VALUE_MASK);
++
++    list_add_tail (&op->op_link, &dev->dev_intop_list);
++
++    writeq ((op->op_cookie << E4_MAIN_INT_SHIFT) | INTERRUPT_CMD, cq->cq_mapping);
++
++    spin_unlock_irqrestore (&dev->dev_intop_lock, flags);
++}
++
++void
++elan4_register_intop (ELAN4_DEV *dev, ELAN4_INTOP *op)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->dev_intop_lock, flags);
++
++    op->op_cookie = INTOP_PERSISTENT | ((dev->dev_intop_cookie++) & INTOP_VALUE_MASK);
++
++    list_add_tail (&op->op_link, &dev->dev_intop_list);
++
++    spin_unlock_irqrestore (&dev->dev_intop_lock, flags);
++}
++
++void
++elan4_deregister_intop (ELAN4_DEV *dev, ELAN4_INTOP *op)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->dev_intop_lock, flags);
++    list_del (&op->op_link);
++    spin_unlock_irqrestore (&dev->dev_intop_lock, flags);
++}
++
++static __inline__ void
++__issue_dma_flushop_cmd (ELAN4_CQ *cq)
++{
++    writeq (DMA_ShMemWrite | RUN_DMA_CMD, cq->cq_mapping);
++    writeq (0 /* cookie */,               cq->cq_mapping);
++    writeq (0 /* vproc */,                cq->cq_mapping);
++    writeq (0 /* srcAddr */,              cq->cq_mapping);
++    writeq (0 /* dstAddr */,              cq->cq_mapping);
++    writeq (0 /* srcEvent */,             cq->cq_mapping);
++    writeq (0 /* dstEvent */,             cq->cq_mapping);
++    writeq (SET_EVENT_CMD,                cq->cq_mapping);
++}
++
++static void
++handle_dma_flushops_intop (ELAN4_DEV *dev, void *arg)
++{
++    unsigned int  hipri        = ((unsigned long) arg & 1);
++    E4_uint64     status       = dev->dev_dma_flushop[hipri].status;
++    ELAN4_CQ     *cq           = dev->dev_dma_flushop[hipri].cq;
++    sdramaddr_t   cqdesc       = dev->dev_cqaddr + (elan4_cq2num(cq) * sizeof (E4_CommandQueueDesc));
++    E4_uint64     queuePtrs    = elan4_sdram_readq (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_QueuePtrs));
++    E4_uint32     completedPtr = CQ_CompletedPtr(queuePtrs);
++    E4_uint32     size         = CQ_Size ((queuePtrs >> CQ_SizeShift) & CQ_SizeMask);
++    unsigned long flags;
++
++    /*
++     * Since we're called from a main interrupt which was issued through the approriate
++     * flushcq the command queue descriptor for dma flushing can no longer be in the 
++     * insert cache, nor can it be in the extractor (as it's trapped), hence it is
++     * safe to modify the completed pointer
++     */
++
++    spin_lock_irqsave (&dev->dev_haltop_lock, flags);
++
++    ASSERT (status != 0);
++
++    /* skip over either the DMA/SETEVENT or just the SETEVENT depending on the trap type */
++    if (CPROC_TrapType (status) == CommandProcDmaQueueOverflow)
++      completedPtr = (completedPtr & ~(size-1)) | ((completedPtr + 64) & (size - 1));
++    else
++      completedPtr = (completedPtr & ~(size-1)) | ((completedPtr + 8) & (size - 1));
++    
++    elan4_sdram_writel (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_QueuePtrs) + 4,
++                      ((queuePtrs >> 32) & ~CQ_PtrOffsetMask) | (completedPtr & CQ_PtrOffsetMask));
++    
++    elan4_restartcq (dev, dev->dev_dma_flushop[hipri].cq);
++
++    if (! list_empty (&dev->dev_dma_flushop[hipri].list))
++      __issue_dma_flushop_cmd (dev->dev_dma_flushop[hipri].cq);
++
++    dev->dev_dma_flushop[hipri].status = 0;
++    
++    spin_unlock_irqrestore (&dev->dev_haltop_lock, flags);
++
++}
++
++static void
++handle_dma_flushops (ELAN4_DEV *dev, E4_uint64 status, int cqnum)
++{
++    unsigned int       hipri  = (cqnum == elan4_cq2num(dev->dev_dma_flushop[1].cq) ? 1 : 0);
++    ELAN4_CQ          *cq     = dev->dev_dma_flushop[hipri].cq;
++    ELAN4_CQ          *flushq = dev->dev_flush_cq[elan4_cq2num(cq) & (COMMAND_INSERTER_CACHE_ENTRIES-1)];
++    struct list_head  *ops;
++    unsigned long      flags;
++    int                      qfull,count;
++    E4_uint64        queuePtrs;
++    LIST_HEAD(list);
++    
++    spin_lock_irqsave (&dev->dev_haltop_lock, flags);
++    
++    ASSERT (cqnum == elan4_cq2num (dev->dev_dma_flushop[hipri].cq));
++    ASSERT (! list_empty (&dev->dev_dma_flushop[hipri].list));
++    ASSERT (dev->dev_dma_flushop[hipri].status == 0);
++    
++    /* remove the whole list */
++    ops = dev->dev_dma_flushop[hipri].list.next;
++
++    list_del_init (&dev->dev_dma_flushop[hipri].list);
++    
++    /* and add it to our local list */
++    list_add_tail (&list, ops);
++    
++    /* now determine whether the queue was full - since it cannot be empty 
++     * then if the front and back pointers are the same then it is full */
++    queuePtrs = hipri ? read_reg64 (dev, DProcHighPriPtrs) : read_reg64 (dev, DProcLowPriPtrs);
++    qfull     = (E4_QueueFrontPointer (queuePtrs) == E4_QueueBackPointer (queuePtrs));
++    
++    if (CPROC_TrapType(status) == CommandProcDmaQueueOverflow && !qfull)
++      printk (" ******* queue overflow trap - but queue not full\n");
++
++    if (qfull && CPROC_TrapType(status) != CommandProcDmaQueueOverflow)
++      printk (" ****** queue full - but not overflow trap : %llx %llx %x\n", 
++              read_reg64 (dev, DProcLowPriPtrs), read_reg64 (dev, DProcHighPriPtrs), CPROC_TrapType(status));
++
++    /* Store the status register, this also indicates that the intop is pending */
++    dev->dev_dma_flushop[hipri].status = status;
++
++    spin_unlock_irqrestore (&dev->dev_haltop_lock, flags);
++
++    /* Issue a main interrupt command to the approriate flush command queue,
++     * which will then safely update the completed pointer to skip over the
++     * command which has trapped, also prevent any new commands to be issued
++     * to the command queue.
++     */
++    dev->dev_dma_flushop[hipri].intop.op_function = handle_dma_flushops_intop;
++    dev->dev_dma_flushop[hipri].intop.op_arg      = (void *) (unsigned long) hipri;
++
++    elan4_queue_intop (dev, flushq, &dev->dev_dma_flushop[hipri].intop);
++    
++    /* now execute all operations */
++    for (count = 0; ! list_empty (&list); count++)
++    {
++      ELAN4_DMA_FLUSHOP *op = list_entry (list.next, ELAN4_DMA_FLUSHOP, op_link);
++      
++      list_del (&op->op_link);
++      
++      (*op->op_function) (dev, op->op_arg, qfull);
++    }
++
++    /* finally release the "reasons" for halting */
++    spin_lock_irqsave (&dev->dev_haltop_lock, flags);
++    if ((dev->dev_halt_dproc_count -= count) == 0)
++      elan4_set_schedstatus (dev, 0);
++    spin_unlock_irqrestore (&dev->dev_haltop_lock, flags);
++      
++    return;
++}
++
++void
++elan4_queue_dma_flushop (ELAN4_DEV *dev, ELAN4_DMA_FLUSHOP *op, int hipri)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->dev_haltop_lock, flags);
++
++    if (dev->dev_halt_dproc_count++ == 0)                     /* ensure that the DMA processor cannot */
++      elan4_set_schedstatus (dev, 0);                         /* execute the DMA we issue. */
++
++    if (list_empty (&dev->dev_dma_flushop[hipri].list) && dev->dev_dma_flushop[hipri].status == 0)
++      __issue_dma_flushop_cmd (dev->dev_dma_flushop[hipri].cq);
++      
++    list_add_tail (&op->op_link, &dev->dev_dma_flushop[hipri].list);
++
++    spin_unlock_irqrestore (&dev->dev_haltop_lock, flags);
++}
++
++static void
++enable_elan_errors (void *arg)
++{
++    ELAN4_DEV *dev = (ELAN4_DEV *) arg;
++
++    ENABLE_INT_MASK (dev, INT_ErrorInterrupts);
++}
++
++#define ERROR_DISABLE_PERIOD  (hz/2)
++#define ERROR_SAMPLE_PERIOD   (hz/10)
++#define ERROR_LIMIT           (100)
++
++static __inline__ void
++check_error_rate (ELAN4_DEV *dev)
++{
++    if (dev->dev_error_time == (lbolt/ERROR_SAMPLE_PERIOD))
++    {
++        if (++dev->dev_errors_per_period >= ERROR_LIMIT && (dev->dev_intmask & INT_ErrorInterrupts))
++      {
++          DISABLE_INT_MASK (dev, INT_ErrorInterrupts);
++          
++          schedule_timer_fn (&dev->dev_error_timeoutid, enable_elan_errors, (void *) dev, ERROR_DISABLE_PERIOD);
++      }
++    }
++    else
++    {
++      dev->dev_error_time        = (lbolt/ERROR_SAMPLE_PERIOD);
++      dev->dev_errors_per_period = 0;
++    }
++}
++
++static __inline__ int
++handle_mainints (ELAN4_DEV *dev, int nticks, int nintr)
++{
++    E4_uint32 nfptr = dev->dev_interruptq_nfptr;
++    E4_uint32 bptr  = read_reg32 (dev, MainIntQueuePtrs.s.Back);
++    E4_uint32 qsize = E4_QueueSize(elan4_interruptq_size);
++    E4_uint32 qmask = qsize - 1;
++    long      tlim  = lbolt + nticks;
++    int       done = 0;
++    unsigned long flags;
++
++    do {
++      int todo  = ((bptr - nfptr) & qmask) / E4_MainIntEntrySize;
++
++      ASSERT (todo > 0);
++
++      PRINTF4 (DBG_DEVICE, DBG_MAININT, "handle_mainints: fptr %x nfptr %x bptr %x : %d todo\n", 
++               read_reg32 (dev, MainIntQueuePtrs.s.Front), nfptr, bptr, todo);
++
++      if (nintr >= 0 && (done + todo) > nintr)                /* punt because too may to do in interrupt */
++      {
++          PRINTF4 (DBG_DEVICE, DBG_MAININT, "handle_mainints: punting (done %d todo %d) (bptr %x fptr %x)\n",
++                   done, todo, bptr, read_reg32 (dev, MainIntQueuePtrs.s.Front));
++
++          return 1;
++      }
++
++      BucketDevStat (dev, s_mainints, todo, MainIntBuckets);
++
++      /* consume all the entries in the queue which we think are there */
++      do {
++          E4_uint64   value = elan4_sdram_readq (dev, nfptr);
++          ELAN4_CTXT *ctxt  = elan4_localctxt (dev, E4_MAIN_INT_CTX (value));
++          E4_uint32   fptr  = nfptr;
++
++          PRINTF2 (DBG_DEVICE, DBG_MAININT, "handle_mainints: process cookie %llx - write fptr=%x\n", value, nfptr);
++
++          if (ctxt == NULL)
++              PRINTF1 (DBG_DEVICE, DBG_INTR, "handle_mainints: context %d invalid\n", E4_MAIN_INT_CTX (value));
++          else
++              ctxt->ctxt_ops->op_interrupt (ctxt, E4_MAIN_INT_COOKIE(value));
++
++          /* compute the next queue front pointer, before updating the front pointer
++           * since we need to ensure that elan4_queue_mainintop doesn't see the queue
++           * as being empty if an extra interrupt is queued in between */
++          dev->dev_interruptq_nfptr = nfptr = (nfptr & ~qmask) | ((nfptr + sizeof (E4_uint64)) & qmask);
++    
++          /* update the queue front pointer, doing this will clear the
++           * interrupt for *all* interrupt cookies which have previously 
++           * been added to the queue */
++          write_reg32 (dev, MainIntQueuePtrs.s.Front, E4_QueueFrontValue (fptr, elan4_interruptq_size));
++          pioflush_reg (dev);
++      } while (bptr != nfptr);
++      
++      /* re-sample the back pointer and if it's different from the previous
++       * queue front pointer, then the queue has something on it again */
++      done += todo;
++      
++      if ((nticks > 0 && ((int) (lbolt - tlim)) > 0))         /* been executing for too long in thread */
++          return 1;
++
++      bptr = read_reg32 (dev, MainIntQueuePtrs.s.Back);
++
++      PRINTF3 (DBG_DEVICE, DBG_MAININT, "handle_mainints: resample : fptr %x nfptr %x bptr %x\n", 
++               read_reg32 (dev, MainIntQueuePtrs.s.Front), nfptr, bptr);
++
++      /* at this point we've made some space in the interrupt queue,
++       * so check to see if we've got anything to restart */
++      spin_lock_irqsave (&dev->dev_mainint_lock, flags);
++      while (! list_empty (&dev->dev_interruptq_list))
++      {
++          ELAN4_INTOP *op = list_entry (dev->dev_interruptq_list.next, ELAN4_INTOP, op_link);
++          
++          list_del (&op->op_link);
++
++          op->op_function (dev, op->op_arg);
++      }
++      spin_unlock_irqrestore (&dev->dev_mainint_lock, flags);
++
++    } while (bptr != nfptr);
++
++    return 0;
++}
++
++static void
++elan4_mainint_thread (ELAN4_DEV *dev)
++{
++    unsigned long flags;
++
++    kernel_thread_init ("elan4_mainint");
++    
++    spin_lock_irqsave (&dev->dev_mainint_lock, flags);
++    for (;;)
++    {
++      if (dev->dev_stop_threads)
++          break;
++      
++      if (! (dev->dev_intmask & INT_MainInterrupt))
++      {
++          spin_unlock_irqrestore (&dev->dev_mainint_lock, flags);
++          
++          if (handle_mainints (dev, elan4_mainint_resched_ticks, -1))
++              BumpDevStat (dev, s_mainint_rescheds);
++
++          spin_lock_irqsave (&dev->dev_mainint_lock, flags);
++          ENABLE_INT_MASK (dev, INT_MainInterrupt);
++      }
++      
++      kcondvar_wait (&dev->dev_mainint_wait, &dev->dev_mainint_lock, &flags);
++    }
++
++    dev->dev_mainint_stopped = 1;
++    kcondvar_wakeupall (&dev->dev_mainint_wait, &dev->dev_mainint_lock);
++
++    spin_unlock_irqrestore (&dev->dev_mainint_lock, flags);
++
++    kernel_thread_exit();
++}
++
++void
++elan4_queue_mainintop (ELAN4_DEV *dev, ELAN4_INTOP *op)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->dev_mainint_lock, flags);
++    if (dev->dev_interruptq_nfptr == read_reg32 (dev, MainIntQueuePtrs.s.Back))
++      op->op_function (dev, op->op_arg);
++    else
++      list_add_tail (&op->op_link, &dev->dev_interruptq_list);
++    spin_unlock_irqrestore (&dev->dev_mainint_lock, flags);
++}
++
++static __inline__ E4_uint32
++handle_cproc_trap (ELAN4_DEV *dev)
++{
++    E4_uint32   cqptr   = read_reg32 (dev, CommandControl.CommandQueueDescsBase) & E4_QueueDescPtrMask;
++    unsigned    cqnum   = ((cqptr - dev->dev_cqaddr) / sizeof (E4_CommandQueueDesc));
++    sdramaddr_t cqdesc  = dev->dev_cqaddr + (cqnum * sizeof (E4_CommandQueueDesc));
++    E4_uint64   control = elan4_sdram_readq (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_Control));
++    E4_uint64   status  = read_reg64 (dev, CProcStatus);
++    ELAN4_CTXT *ctxt    = elan4_localctxt (dev, CQ_Context (control));
++
++    PRINTF4 (DBG_DEVICE, DBG_INTR, "handle_cproc_trap: cqnum=%d status=%016llx control=%016llx TrapType\n", 
++           cqnum, status, control, CPROC_TrapType (status));
++    PRINTF4 (DBG_DEVICE, DBG_INTR, "                   %016llx %016llx %016llx %016llx\n",
++           elan4_sdram_readq (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_QueuePtrs)),
++           elan4_sdram_readq (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_HoldingValue)),
++           elan4_sdram_readq (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_AckBuffers)),
++           elan4_sdram_readq (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_Control)));
++
++    BumpDevStat (dev, s_cproc_traps);
++
++    ctxt->ctxt_ops->op_cproc_trap (ctxt, status, cqnum);
++
++    return (CPROC_TrapType (status) == CommandProcWaitTrap ? SCH_RestartCProc | SCH_RestartEProc : SCH_RestartCProc);
++}
++
++static __inline__ E4_uint32
++handle_dproc_trap (ELAN4_DEV *dev, int unit)
++{
++    E4_uint64   status  = (unit == 0) ? read_reg64 (dev, DProc0Status) : read_reg64 (dev, DProc1Status);
++    E4_uint32   restart = (unit == 0) ? SCH_RestartDma0Proc : SCH_RestartDma1Proc;
++    ELAN4_CTXT *ctxt    = elan4_localctxt (dev, DPROC_Context (status));
++    
++    PRINTF3 (DBG_DEVICE, DBG_INTR, "handle_dproc_trap: unit %d context %d%s\n", unit, DPROC_Context(status),
++           DPROC_PrefetcherFault(status) ? " (prefetcher)" : "");
++
++    if (DPROC_PrefetcherFault (status))
++      restart |= SCH_RestartDmaPrefetchProc;
++                    
++    BumpDevStat (dev, s_dproc_traps);
++
++    ctxt->ctxt_ops->op_dproc_trap (ctxt, status, unit);
++
++    return (restart);
++}
++
++static __inline__ E4_uint32
++handle_eproc_trap (ELAN4_DEV *dev)
++{
++    E4_uint64   status = read_reg64 (dev, EProcStatus);
++    ELAN4_CTXT *ctxt   = elan4_localctxt (dev, EPROC_Context (status));
++
++    BumpDevStat (dev, s_eproc_traps);
++
++    ctxt->ctxt_ops->op_eproc_trap (ctxt, status);
++
++    return (SCH_RestartEProc);
++}
++
++static __inline__ E4_uint32
++handle_tproc_trap (ELAN4_DEV *dev)
++{
++    E4_uint64   status = read_reg64 (dev, TProcStatus);
++    ELAN4_CTXT *ctxt   = elan4_localctxt (dev, TPROC_Context (status));
++
++    BumpDevStat (dev, s_tproc_traps);
++
++    ctxt->ctxt_ops->op_tproc_trap (ctxt, status);
++    
++    return (SCH_RestartTProc);
++}
++
++static __inline__ void
++handle_haltints (ELAN4_DEV *dev, E4_uint32 intreg)
++{
++    struct list_head  list   = LIST_HEAD_INIT(list);
++    E4_uint32         mask   = 0;
++    E4_uint32         active = 0;
++    struct list_head *entry;
++    struct list_head *next;
++    unsigned long     flags;
++
++    BumpDevStat (dev, s_haltints);
++
++    spin_lock_irqsave (&dev->dev_haltop_lock, flags);
++
++    list_for_each_safe (entry, next, &dev->dev_haltop_list) {
++      ELAN4_HALTOP *op = list_entry (entry, ELAN4_HALTOP, op_link);
++
++      PRINTF (DBG_DEVICE, DBG_INTR, "handle_haltints: op=%p op_mask=%x intreg=%x\n", op, op->op_mask, intreg);
++
++      if ((op->op_mask & intreg) != op->op_mask)
++          mask |= op->op_mask;
++      else
++      {
++          list_del (&op->op_link);                            /* remove from list */
++          list_add_tail (&op->op_link, &list);                /* add to local list */
++
++          active |= op->op_mask;
++      }
++    }
++
++    ASSERT (dev->dev_haltop_mask == (mask | active));
++
++    dev->dev_haltop_mask = mask;
++
++    if (list_empty (&list))
++      elan4_set_schedstatus (dev, intreg);
++    else
++    {
++      dev->dev_haltop_active = active;
++      spin_unlock_irqrestore (&dev->dev_haltop_lock, flags);
++
++      while (! list_empty (&list)) 
++      {
++          ELAN4_HALTOP *op = list_entry (list.next, ELAN4_HALTOP, op_link);
++          
++          list_del (&op->op_link);
++
++          (*op->op_function) (dev, op->op_arg);
++      }
++
++      spin_lock_irqsave (&dev->dev_haltop_lock, flags);
++      dev->dev_haltop_active = 0;
++
++      elan4_set_schedstatus (dev, 0);
++    }
++
++    spin_unlock_irqrestore (&dev->dev_haltop_lock, flags);
++}
++
++static __inline__ E4_uint32
++handle_iproc_trap (ELAN4_DEV *dev, unsigned unit)
++{
++    sdramaddr_t hdroff = dev->dev_inputtraparea + offsetof (E4_IprocTrapState, TrHeader[0][unit]);
++    E4_uint64   status = elan4_sdram_readq (dev, hdroff + offsetof (E4_IprocTrapHeader, IProcStatusCntxAndTrType));
++    E4_uint32   filter = elan4_read_filter (dev, IPROC_NetworkContext (status));
++    ELAN4_CTXT *ctxt   = elan4_localctxt (dev, filter & E4_FILTER_CONTEXT_MASK);
++
++    /*
++     * The context is not valid in the following case :
++     *     ack not been sent AND bad CRC/bad length.
++     *
++     *  NOTE TransCRCStatus and BadLength only valid if NOT an EopTrap.
++     */
++    ASSERT ((IPROC_GoodAckSent (status) & (1 << IPROC_InputterChan (status))) || IPROC_EOPTrap (status) ||
++          (IPROC_TransCRCStatus (status) == CRC_STATUS_GOOD && !IPROC_BadLength (status)));
++    
++    BumpDevStat (dev, s_iproc_traps);
++
++    ctxt->ctxt_ops->op_iproc_trap (ctxt, status, unit);
++
++    return (SCH_RestartCh0LowPriInput << unit);
++}
++
++void
++handle_pcimemerr (ELAN4_DEV *dev)
++{
++    elan4_pcierror (dev);
++
++    check_error_rate (dev);
++}
++
++void
++handle_sdramint (ELAN4_DEV *dev)
++{
++    E4_uint64 status    = read_reg64 (dev, SDRamECCStatus);
++    E4_uint64 ConfigRegValue = read_reg64 (dev, SDRamConfigReg);
++    char      errstr[200];
++    int             i;
++    int             Found = 0;
++
++    PRINTF0 (DBG_DEVICE, DBG_INTR, "handle_sdramint\n");
++
++    printk ("elan%d: ECC Error %s status=%llx\n",
++          dev->dev_instance, elan4_sdramerr2str (dev, status, ConfigRegValue, errstr), status);
++
++    if (!ECC_UncorrectableErr(status) && !ECC_MultUncorrectErrs(status))
++      printk ("elan%d: ECC error data=%016llx\n", dev->dev_instance, elan4_sdram_readq (dev, ECC_Addr(status)));
++
++    if (ECC_CorrectableErr (status))
++      BumpDevStat (dev, s_correctable_errors);
++    if (ECC_MultCorrectErrs (status))
++      BumpDevStat (dev, s_multiple_errors);
++
++    if (ECC_UncorrectableErr(status))
++      panic ("elan%d: uncorrectable ECC error\n", dev->dev_instance);
++    if (ECC_MultUncorrectErrs(status))
++      panic ("elan%d: muliple uncorrectable ECC error\n", dev->dev_instance);
++    
++    PULSE_SYSCONTROL (dev, CONT_CLEAR_SDRAM_ERROR);
++
++    /*
++     * Now try to test for a read/write error type.
++     * This can only be done if it was a correctable error as an uncorrectable error might lockup the node.
++     * It should not be attempted if the data is in the dcache because fetching again would not generate an
++     * error even if the problem was a read, and flushing the cache line would fix a write probelm.
++     * Reading the same location again should cause a new error if the problem was caused by a bad write.
++     */
++    if (elan4_eccerr_recheck &&
++      (dev->dev_devinfo.dev_revision_id != PCI_REVISION_ID_ELAN4_REVA) &&
++        ECC_CorrectableErr(status) && !ECC_UncorrectableErr(status))
++    {
++      E4_uint64 status2;
++      E4_uint64 Addr = ECC_Addr(status) & ~(E4_CACHELINE_SIZE-1);
++      E4_uint32 SetIndex = (Addr >> 6) & ~(E4_NumCacheLines-1);
++      int       InCache = 0;
++
++      /* check the cache tags to see if the data has been read into a cache line. */
++      for (i=0; i<E4_NumCacheSets; i++)
++         if (((E4_uint32)__elan4_readq (dev, dev->dev_regs + offsetof(E4_Registers, Tags.Tags[i][SetIndex].Value)) & 0x7fffe000) == (Addr & 0x7fffe000))
++         {
++             InCache = 1;
++             break;
++         }
++
++      if (InCache == 0)
++      {
++          printk ("elan%d: checking if ECC error was read or write\n", dev->dev_instance);
++
++          /* Now read and throw away the answer. A read of a word will schedule a block read of sdram */
++          elan4_sdram_readq (dev, Addr);
++          status2 = read_reg64 (dev, SDRamECCStatus);
++          if ((Addr == (ECC_Addr(status2) & ~(E4_CACHELINE_SIZE-1))) && ECC_CorrectableErr(status2))  // Write error.
++          {
++              status = (status & ~0x0030000000000000ULL) | 0x0010000000000000ULL;
++              PULSE_SYSCONTROL (dev, CONT_CLEAR_SDRAM_ERROR);
++          }
++          else
++              status = (status & ~0x0030000000000000ULL) | 0x0020000000000000ULL;
++      }
++      else
++          status = status | 0x0030000000000000ULL;
++    }
++    else
++      status &= ~0x0030000000000000ULL;
++
++    /* search for this error already being logged */
++    for (i = sizeof (dev->dev_sdramerrs)/sizeof (dev->dev_sdramerrs[0]) - 1; i >= 0; i--)
++        if ((dev->dev_sdramerrs[i].EccStatus == status) && (dev->dev_sdramerrs[i].ConfigReg == ConfigRegValue))
++      {
++            Found = 1;
++          dev->dev_sdramerrs[i].ErrorCount += 1; // Keep a count.
++          break;
++      }
++
++    /* stash the status for /proc */
++    if (!Found)
++    {
++      for (i = sizeof (dev->dev_sdramerrs)/sizeof (dev->dev_sdramerrs[0]) - 1; i > 0; i--)
++          dev->dev_sdramerrs[i] = dev->dev_sdramerrs[i-1];
++      dev->dev_sdramerrs[0].EccStatus = status;
++      dev->dev_sdramerrs[0].ConfigReg = ConfigRegValue;
++      dev->dev_sdramerrs[0].ErrorCount = 1; // First error
++    }
++    
++    check_error_rate (dev);
++}
++
++static void
++clear_linkerr_led (void *arg)
++{
++    ELAN4_DEV *dev = (ELAN4_DEV *) arg;
++
++    write_i2c (dev, I2cStatus, read_i2c (dev, I2cStatus) | I2cCntl_ClearLinkError);
++}
++
++void
++handle_linkerror (ELAN4_DEV *dev)
++{
++    E4_uint32 LinkState;
++    E4_uint32 CurrState = read_reg32 (dev, LinkControlReg);
++
++    /* Set for reading errors. */
++    write_reg32 (dev, LinkControlReg,
++                 (CurrState = CurrState & ~((LCONT_TEST_CONTROL_MASK << LCONT_TEST_CONTROL_SHIFT) |
++                                          (LCONT_TEST_VALUE_MASK << LCONT_TEST_VALUE_SHIFT))));
++    LinkState = LCONT_LINK_STATE(CurrState = read_reg32 (dev, LinkControlReg));
++
++#ifdef DEBUG
++    {
++      E4_uint8 ErrorMsg[256], DataErrorVal[64];
++
++      strcpy (ErrorMsg, "handle_linkerror:");
++      if (LinkState & LS_LockError)    strcat (ErrorMsg, " LockError");
++      if (LinkState & LS_DeskewError)  strcat (ErrorMsg, " DeskewError");
++      if (LinkState & LS_PhaseError)   strcat (ErrorMsg, " PhaseError");
++      if (LinkState & LS_DataError)
++      {
++          E4_uint32 error[4];
++          E4_uint32 i;
++          strcat (ErrorMsg, " DataError");
++          /* Errors */
++          for(i = LRS_ErrorVal8to0; i <= LRS_ErrorVal35to27; i++)
++          {
++              write_reg32 (dev, LinkControlReg,
++                           CurrState | LCONT_TEST_VALUE(i) | (LCONT_READ_STATE << LCONT_TEST_CONTROL_SHIFT));
++              error[i - LRS_ErrorVal8to0] = LCONT_LINK_STATE(read_reg32 (dev, LinkControlReg));
++          }
++          sprintf (DataErrorVal, " Link State Error Val: %09llx %03x %03x %03x %03x", 
++                   (unsigned long long) ((error[0] & 0x1ffUL) | ((error[1] & 0x1ffUL) << 9)  |
++                                ((error[2] & 0x1ffUL) << 18) | ((error[3] & 0x1ffUL) << 27)),
++                   error[3], error[2], error[1], error[0]);
++          strcat (ErrorMsg, DataErrorVal);
++      }
++      if (LinkState & LS_FifoOvFlow0)  strcat (ErrorMsg, " FifoOvFlow0");
++      if (LinkState & LS_FifoOvFlow1)  strcat (ErrorMsg, " FifoOvFlow1");
++      if (LinkState & LS_Mod45Changed)         strcat (ErrorMsg, " Mod45Changed");
++      if (LinkState & LS_PAckNotSeenError) strcat (ErrorMsg, " PAckNotSeenError");
++      strcat (ErrorMsg, "\n");
++      PRINTF0 (DBG_DEVICE, DBG_INTR, ErrorMsg);
++    }
++#endif
++
++    BumpDevStat (dev, s_link_errors);
++    
++    if (LinkState & LS_LockError)      BumpDevStat (dev, s_lock_errors);
++    if (LinkState & LS_DeskewError)    BumpDevStat (dev, s_deskew_errors);
++    if (LinkState & LS_PhaseError)     BumpDevStat (dev, s_phase_errors);
++    if (LinkState & LS_DataError)      BumpDevStat (dev, s_data_errors);
++    if (LinkState & LS_FifoOvFlow0)    BumpDevStat (dev, s_fifo_overflow0);
++    if (LinkState & LS_FifoOvFlow1)    BumpDevStat (dev, s_fifo_overflow1);
++    if (LinkState & LS_Mod45Changed)   BumpDevStat (dev, s_mod45changed);
++    if (LinkState & LS_PAckNotSeenError) BumpDevStat (dev, s_pack_not_seen);
++
++    PULSE_SCHED_RESTART (dev, SCH_ClearLinkErrorInt);
++    
++    /* schedule a timer to clear the link error LED, so that it stays on 
++     * for a second for every link error that occurs */
++    if (dev->dev_devinfo.dev_revision_id != PCI_REVISION_ID_ELAN4_REVA && !timer_fn_queued (&dev->dev_linkerr_timeoutid))
++      schedule_timer_fn (&dev->dev_linkerr_timeoutid, clear_linkerr_led, (void *) dev, HZ);
++
++    /*
++     * Signal the link error to the switch by
++     * enabling the INT_LinkPortKeyFail bit.
++     * Always clear the error bit as the switch
++     * might have produced a spurious "ack" ...
++     */
++    PULSE_SYSCONTROL (dev, CONT_CLEAR_LINKPORT_INT);
++
++    if (dev->dev_linkerr_signalled == 0)
++      dev->dev_linkerr_signalled = 1;
++    else
++      dev->dev_linkerr_signalled = 2;
++    
++    ENABLE_INT_MASK (dev, INT_LinkPortKeyFail);
++      
++    check_error_rate (dev);
++}
++
++void
++handle_linkportkeyfail (ELAN4_DEV *dev)
++{
++    PRINTF0 (DBG_DEVICE, DBG_INTR, "handle_linkportkeyfail\n");
++
++    PULSE_SYSCONTROL (dev, CONT_CLEAR_LINKPORT_INT);
++
++    if (! dev->dev_linkerr_signalled)
++    {
++      /* Hmmm - they're not playing ball */
++      BumpDevStat (dev, s_linkport_keyfail);
++
++      DISABLE_INT_MASK (dev, INT_LinkPortKeyFail);
++    }
++    else
++    {
++      /* If more link errors have occured since we 
++       * signalled the error, then leave it signalled. */
++      if (--dev->dev_linkerr_signalled == 0)
++          DISABLE_INT_MASK (dev, INT_LinkPortKeyFail);
++    }
++}
++
++
++static __inline__ void
++__elan4_4msi0 (ELAN4_DEV *dev, E4_uint32 intreg, E4_uint32 intmask)
++{
++    unsigned long flags;
++
++    if (intreg & intmask & INT_MainInterrupt)
++    {
++      DISABLE_INT_MASK (dev, INT_MainInterrupt);
++
++      if (handle_mainints (dev, -1, elan4_mainint_punt_loops) == 0)
++          ENABLE_INT_MASK (dev, INT_MainInterrupt);
++      else
++      {
++          BumpDevStat (dev, s_mainint_punts);
++          
++          spin_lock_irqsave (&dev->dev_mainint_lock, flags);
++          kcondvar_wakeupone (&dev->dev_mainint_wait, &dev->dev_mainint_lock);
++          spin_unlock_irqrestore (&dev->dev_mainint_lock, flags);
++      }
++    }
++}
++
++static __inline__ void
++__elan4_4msi1 (ELAN4_DEV *dev, E4_uint32 intreg, E4_uint32 intmask)
++{
++    E4_uint32 restart = 0;
++
++    PRINTF1 (DBG_DEVICE, DBG_INTR, "__elan4_4msi1: %x\n", intreg);
++    
++    spin_lock (&dev->dev_trap_lock);
++    
++    if (intreg & intmask & INT_CProc)
++      restart |= handle_cproc_trap (dev);
++    if (intreg & intmask & INT_EProc) 
++      restart |= handle_eproc_trap (dev);
++    if (intreg & intmask & INT_Dma0Proc) 
++      restart |= handle_dproc_trap (dev, 0);
++    if (intreg & intmask & INT_Dma1Proc) 
++      restart |= handle_dproc_trap (dev, 1);
++    if (intreg & intmask & INT_TProc)
++      restart |= handle_tproc_trap (dev);
++    
++    PULSE_SCHED_RESTART (dev, restart);
++    
++    spin_unlock (&dev->dev_trap_lock);
++    
++    if (intreg & (INT_Halted|INT_Discarding))
++      handle_haltints (dev, intreg);
++}
++
++static __inline__ void
++__elan4_4msi2 (ELAN4_DEV *dev, E4_uint32 intreg, E4_uint32 intmask)
++{
++    E4_uint32 restart = 0;
++
++    PRINTF1 (DBG_DEVICE, DBG_INTR, "__elan4_4msi2: %x\n", intreg);
++    
++    spin_lock (&dev->dev_trap_lock);
++    if (intreg & intmask & INT_IProcCh0LowPri)
++      restart |= handle_iproc_trap (dev, 0);
++    
++    if (intreg & intmask & INT_IProcCh1LowPri)
++      restart |= handle_iproc_trap (dev, 1);
++    
++    if (intreg & intmask & INT_IProcCh0HighPri)
++      restart |= handle_iproc_trap (dev, 2);
++    
++    if (intreg & intmask & INT_IProcCh1HighPri)
++      restart |= handle_iproc_trap (dev, 3);
++    
++    PULSE_SCHED_RESTART (dev, restart);
++    
++    spin_unlock (&dev->dev_trap_lock);
++}
++
++static __inline__ void
++__elan4_4msi3 (ELAN4_DEV *dev, E4_uint32 intreg, E4_uint32 intmask)
++{
++    PRINTF1 (DBG_DEVICE, DBG_INTR, "__elan4_4msi3: %x\n", intreg);
++    
++    if (intreg & intmask & INT_PciMemErr)
++      handle_pcimemerr (dev);
++    
++    if (intreg & intmask & INT_SDRamInt)
++      handle_sdramint (dev);
++    
++    if (intreg & intmask & INT_LinkError)
++      handle_linkerror (dev);
++    
++    if (intreg & intmask & INT_LinkPortKeyFail)
++      handle_linkportkeyfail (dev);
++}
++
++int
++elan4_1msi0 (ELAN4_DEV *dev)
++{
++    E4_uint32 intmask = dev->dev_intmask;
++    E4_uint32 intreg;
++
++    if (intmask == 0 || ((intreg = read_reg32 (dev, InterruptReg)) & intmask) == 0)
++      return (0);
++
++    BumpDevStat (dev, s_interrupts);
++    
++    do {
++      PRINTF1 (DBG_DEVICE, DBG_INTR, "elan4_1msi0: %x\n", intreg);
++
++      if (intreg & intmask & INT_MSI0)
++          __elan4_4msi0(dev, intreg, intmask);
++      if (intreg & intmask & INT_MSI1)
++          __elan4_4msi1(dev, intreg, intmask);
++      if (intreg & intmask & INT_MSI2)
++          __elan4_4msi2(dev, intreg, intmask); 
++      if (intreg & intmask & INT_MSI3)
++          __elan4_4msi3(dev, intreg, intmask);
++
++      if (intreg & INT_LinkPortKeyFail)
++          handle_linkportkeyfail (dev);
++
++      /* must ensure that the read of the interrupt mask
++       * completes before the read of the interrupt register
++       * since the main interrupt thread clears it's interrupt
++       * and then re-enables it in the interrupt mask. */
++      intmask = dev->dev_intmask;
++      mb();
++      intreg = read_reg32 (dev, InterruptReg);
++
++    } while ((intreg & intmask) != 0);
++
++    return (1);
++}
++
++/* local context management */
++int
++elan4_insertctxt (ELAN4_DEV *dev, ELAN4_CTXT *ctxt, ELAN4_TRAP_OPS *ops)
++{
++    unsigned long flags;
++    int tbl;
++
++    ctxt->ctxt_dev = dev;
++    ctxt->ctxt_ops = ops;
++
++    INIT_LIST_HEAD (&ctxt->ctxt_cqalist);
++    spin_lock_init (&ctxt->ctxt_mmulock);
++
++    for (tbl = 0; tbl < NUM_HASH_TABLES; tbl++)
++    {
++      KMEM_ZALLOC (ctxt->ctxt_mmuhash[tbl], ELAN4_HASH_ENTRY **,  dev->dev_hashsize[tbl] * sizeof (ELAN4_HASH_ENTRY *), 1);
++      
++      if (ctxt->ctxt_mmuhash[tbl] == NULL)
++      {
++          if (tbl != 0)
++              KMEM_FREE (ctxt->ctxt_mmuhash[0], dev->dev_hashsize[0] * sizeof (ELAN4_HASH_ENTRY *));
++          spin_lock_destroy (&ctxt->ctxt_mmulock);
++          return (-ENOMEM);
++      }
++    }
++
++    spin_lock_irqsave (&dev->dev_ctxt_lock, flags);
++
++    if ((ctxt->ctxt_num = bt_freebit (dev->dev_ctxmap, (1 << dev->dev_ctxtableshift))) >= 0)
++    {
++      /* chain onto the lists of all contexts */
++      list_add (&ctxt->ctxt_link, &dev->dev_ctxt_list);
++
++      BT_SET (dev->dev_ctxmap, ctxt->ctxt_num);
++    }
++    
++    spin_unlock_irqrestore (&dev->dev_ctxt_lock, flags);
++
++    return (ctxt->ctxt_num < 0 ? -ENOMEM : 0);
++}
++
++void
++elan4_removectxt (ELAN4_DEV *dev, ELAN4_CTXT *ctxt)
++{
++    unsigned long flags;
++    int tbl;
++
++    /* remove from list of contexts */
++    spin_lock_irqsave (&dev->dev_ctxt_lock, flags);
++
++    list_del (&ctxt->ctxt_link);
++
++    BT_CLEAR (dev->dev_ctxmap, ctxt->ctxt_num);
++
++    spin_unlock_irqrestore (&dev->dev_ctxt_lock, flags);
++
++    spin_lock_destroy (&ctxt->ctxt_info_lock);
++
++    for (tbl = 0; tbl < NUM_HASH_TABLES; tbl++)
++      KMEM_FREE (ctxt->ctxt_mmuhash[tbl],  dev->dev_hashsize[tbl] * sizeof (ELAN4_HASH_ENTRY *));
++
++    spin_lock_destroy (&ctxt->ctxt_mmulock);
++}
++
++ELAN4_CTXT *
++elan4_localctxt (ELAN4_DEV *dev, unsigned num)
++{
++    struct list_head *entry;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->dev_ctxt_lock, flags);
++
++    list_for_each (entry, &dev->dev_ctxt_list) {
++      ELAN4_CTXT *ctxt = list_entry (entry, ELAN4_CTXT, ctxt_link);
++      
++      if (ctxt->ctxt_num == num)
++      {
++          spin_unlock_irqrestore (&dev->dev_ctxt_lock, flags);
++          return (ctxt);
++      }
++    }
++    spin_unlock_irqrestore (&dev->dev_ctxt_lock, flags);
++
++    return ((ELAN4_CTXT *) NULL);
++}
++
++ELAN4_CTXT *
++elan4_networkctxt (ELAN4_DEV *dev, unsigned num)
++{
++    E4_uint32 filter = elan4_read_filter (dev, num);
++    
++    if ((filter & E4_FILTER_CONTEXT_MASK) == INVALID_CONTEXT)
++      return NULL;
++    else
++      return elan4_localctxt (dev, filter & E4_FILTER_CONTEXT_MASK);
++}
++
++/* network context management */
++int
++elan4_attach_filter (ELAN4_CTXT *ctxt, unsigned int ctxnum)
++{
++    ELAN4_DEV        *dev = ctxt->ctxt_dev;
++    int               res = 0;
++    E4_uint32         filter;
++    unsigned long     flags;
++    
++    spin_lock_irqsave (&dev->dev_ctxt_lock, flags);
++    
++    filter = elan4_read_filter (dev, ctxnum);
++    if ((filter & E4_FILTER_CONTEXT_MASK) != INVALID_CONTEXT)
++    {
++      PRINTF2 (ctxt, DBG_NETWORK_CTX, "elan4_attach_filter: ctx=%d filter=%x -> EBUSY\n", ctxnum, filter);
++      res = -EBUSY;
++    }
++    else
++    {
++      PRINTF1 (ctxt, DBG_NETWORK_CTX, "elan4_attach_filter: ctx=%d - SUCCESS\n", ctxnum);
++
++      elan4_write_filter (dev, ctxnum, ctxt->ctxt_num | E4_FILTER_DISCARD_ALL);
++      PULSE_SCHED_RESTART (dev, SCH_ContextFilterFlush);
++    }
++    spin_unlock_irqrestore (&dev->dev_ctxt_lock, flags);
++    
++    return (res);
++}
++
++void
++elan4_detach_filter (ELAN4_CTXT *ctxt, unsigned int ctxnum)
++{
++    ELAN4_DEV *dev = ctxt->ctxt_dev;
++
++    PRINTF1 (ctxt, DBG_NETWORK_CTX, "elan4_detach_filter: detach from network context %d\n", ctxnum);
++          
++    elan4_write_filter (dev, ctxnum, INVALID_CONTEXT | E4_FILTER_DISCARD_ALL);
++    PULSE_SCHED_RESTART (dev, SCH_ContextFilterFlush);
++}
++
++void
++elan4_set_filter (ELAN4_CTXT *ctxt, unsigned int ctxnum, E4_uint32 state)
++{
++    ELAN4_DEV *dev = ctxt->ctxt_dev;
++
++    PRINTF6 (ctxt, DBG_NETWORK_CTX, "elan4_set_filter: set filter state %x for network context %d <%s%s%s%s>\n", state, ctxnum,
++           (state & E4_FILTER_DISCARD_ALL) ? "discard,"  : "",
++           (state & E4_FILTER_ACKOK_ALL)   ? "ack-ok,"   : "",
++           (state & E4_FILTER_HIGH_PRI)    ? "high-pri," : "",
++           (state & E4_FILTER_STATS)       ? "stats,"    : "");
++          
++    elan4_write_filter (dev, ctxnum, ctxt->ctxt_num | state);
++    PULSE_SCHED_RESTART (dev, SCH_ContextFilterFlush);
++}
++
++void
++elan4_set_routetable (ELAN4_CTXT *ctxt, ELAN4_ROUTE_TABLE *tbl)
++{
++    ELAN4_DEV *dev   = ctxt->ctxt_dev;
++    E4_uint32  value = tbl ? (E4_VPT_VALID | E4_VPT_VALUE(tbl->tbl_entries, tbl->tbl_size)) : 0;
++    
++    /* and insert into the vp table */
++    elan4_sdram_writel (dev, (dev->dev_ctxtable + (ctxt->ctxt_num * sizeof (E4_ContextControlBlock)) +
++                      offsetof (E4_ContextControlBlock, VirtualProcessTable)), value);
++    pioflush_sdram(dev);
++
++    PULSE_SYSCONTROL (dev, CONT_ROUTE_FLUSH);
++}
++
++/* command queue management */
++ELAN4_CQA *
++elan4_getcqa (ELAN4_CTXT *ctxt, unsigned int idx)
++{
++    ELAN4_DEV *dev = ctxt->ctxt_dev;
++    struct list_head *el;
++
++    spin_lock (&dev->dev_cqlock);
++    list_for_each (el, &ctxt->ctxt_cqalist) {
++      ELAN4_CQA *cqa = list_entry (el, ELAN4_CQA, cqa_link);
++      
++      if (cqa->cqa_idx == idx)
++      {
++          cqa->cqa_ref++;
++
++          spin_unlock (&dev->dev_cqlock);
++          return cqa;
++      }
++    }
++    spin_unlock (&dev->dev_cqlock);
++    return NULL;
++}
++
++void
++elan4_putcqa (ELAN4_CTXT *ctxt, unsigned int idx)
++{
++    ELAN4_DEV *dev = ctxt->ctxt_dev;
++    struct list_head *el, *nel;
++
++    spin_lock (&dev->dev_cqlock);
++    list_for_each_safe (el, nel, &ctxt->ctxt_cqalist) {
++      ELAN4_CQA *cqa = list_entry (el, ELAN4_CQA, cqa_link);
++      
++      if (cqa->cqa_idx == idx)
++      {
++          if (--cqa->cqa_ref || bt_lowbit (cqa->cqa_bitmap, ELAN4_CQ_PER_CQA) != -1)
++              spin_unlock (&dev->dev_cqlock);
++          else
++          {
++              list_del (&cqa->cqa_link);
++              
++              BT_CLEAR (ctxt->ctxt_cqamap, cqa->cqa_idx);
++              BT_CLEAR (dev->dev_cqamap, cqa->cqa_cqnum/ELAN4_CQ_PER_CQA);
++              spin_unlock (&dev->dev_cqlock);
++              
++              KMEM_FREE (cqa, sizeof (ELAN4_CQA));
++          }
++          return;
++      }
++    }
++    spin_unlock (&dev->dev_cqlock);
++
++    printk ("elan4_putcqa: idx %d not found\n", idx);
++    BUG();
++}
++
++static ELAN4_CQ *
++elan4_getcq (ELAN4_CTXT *ctxt, unsigned int type)
++{
++    ELAN4_DEV        *dev = ctxt->ctxt_dev;
++    ELAN4_CQA        *cqa;
++    struct list_head *el;
++    int                     cidx, didx;
++
++    spin_lock (&dev->dev_cqlock);
++    list_for_each (el, &ctxt->ctxt_cqalist) {
++      cqa = list_entry (el, ELAN4_CQA, cqa_link);
++
++      if (cqa->cqa_type == type && (cidx = bt_freebit (cqa->cqa_bitmap, ELAN4_CQ_PER_CQA)) >=0)
++      {
++          BT_SET (cqa->cqa_bitmap, cidx);
++          
++          spin_unlock (&dev->dev_cqlock);
++          return &cqa->cqa_cq[cidx];
++      }
++    }
++    spin_unlock (&dev->dev_cqlock);
++
++    /* allocate a new cqa and it's chunk of command queue descriptors */
++    KMEM_ZALLOC (cqa, ELAN4_CQA *, sizeof (ELAN4_CQA), 1);
++    if (cqa == NULL)
++      return NULL;
++
++    spin_lock (&dev->dev_cqlock);
++    cidx = bt_freebit (ctxt->ctxt_cqamap, ELAN4_MAX_CQA);
++
++    /* On architectures which have MTRR registers for write-combinig
++     * the top command queues from dev->dev_cqreorder upwards are
++     * used for reordered queues.  Without MTRR registers any page
++     * sized group can use write combinig through the ptes. */
++    if (dev->dev_cqreorder == 0)
++      didx = bt_freebit (dev->dev_cqamap, dev->dev_cqcount/ELAN4_CQ_PER_CQA);
++    else
++    {
++      if ((type & CQ_Reorder) != 0)
++          didx = bt_nextbit (dev->dev_cqamap, dev->dev_cqcount/ELAN4_CQ_PER_CQA, (dev->dev_cqreorder/ELAN4_CQ_PER_CQA) - 1, 0);
++      else
++          didx = bt_freebit (dev->dev_cqamap, dev->dev_cqreorder/ELAN4_CQ_PER_CQA);
++    }
++
++    if (cidx < 0 || didx < 0)
++    {
++      spin_unlock (&dev->dev_cqlock);
++      KMEM_FREE (cqa, sizeof (ELAN4_CQA));
++      return NULL;
++    }
++
++    BT_SET (ctxt->ctxt_cqamap, cidx);
++    BT_SET (dev->dev_cqamap, didx);
++
++    cqa->cqa_idx   = cidx;
++    cqa->cqa_type  = type;
++    cqa->cqa_cqnum = (didx * ELAN4_CQ_PER_CQA);
++    
++    list_add_tail (&cqa->cqa_link, &ctxt->ctxt_cqalist);
++    
++    /* initialise the cqa struct */
++    for (cidx = 0; cidx < ELAN4_CQ_PER_CQA; cidx++)
++    {
++      cqa->cqa_cq[cidx].cq_idx = cidx;
++      cqa->cqa_cq[cidx].cq_cqa = cqa;
++    }
++
++    /* no mappings yet */
++    cqa->cqa_ref = 0;
++
++    /* we're going to return entry zero */
++    BT_SET (cqa->cqa_bitmap, 0);
++    spin_unlock (&dev->dev_cqlock);
++    
++    return &cqa->cqa_cq[0];
++}
++
++static void
++elan4_putcq (ELAN4_CTXT *ctxt, ELAN4_CQ *cq)
++{
++    ELAN4_DEV        *dev = ctxt->ctxt_dev;
++    ELAN4_CQA        *cqa = cq->cq_cqa;
++
++    spin_lock (&dev->dev_cqlock);
++
++    BT_CLEAR (cqa->cqa_bitmap, cq->cq_idx);
++
++    if (bt_lowbit (cqa->cqa_bitmap, ELAN4_CQ_PER_CQA) != -1 || cqa->cqa_ref)
++      spin_unlock (&dev->dev_cqlock);
++    else
++    {
++      list_del (&cqa->cqa_link);
++      
++      BT_CLEAR (ctxt->ctxt_cqamap, cqa->cqa_idx);
++      BT_CLEAR (dev->dev_cqamap, cqa->cqa_cqnum/ELAN4_CQ_PER_CQA);
++      spin_unlock (&dev->dev_cqlock);
++      
++      KMEM_FREE (cqa, sizeof (ELAN4_CQA));
++    }
++}
++
++ELAN4_CQ *
++elan4_alloccq (ELAN4_CTXT *ctxt, unsigned cqsize, unsigned perm, unsigned cqtype)
++{
++    ELAN4_DEV   *dev = ctxt->ctxt_dev;
++    ELAN4_CQ    *cq;
++    int         cqnum;
++    sdramaddr_t cqdesc;
++    unsigned    offset;
++    E4_uint64   value;
++
++    if ((cq = elan4_getcq (ctxt, cqtype)) == NULL)
++      return NULL;
++
++    cqnum = elan4_cq2num(cq);
++    
++    cq->cq_space = elan4_sdram_alloc (dev, CQ_Size(cqsize));
++    if (cq->cq_space == (virtaddr_t) 0)
++    {
++      elan4_putcq (ctxt, cq);
++      return (NULL);
++    }
++
++    cq->cq_size   = cqsize;
++    cq->cq_perm   = perm;
++    
++    /* and finally initialise the command queue descriptor */
++    cqdesc = dev->dev_cqaddr + (cqnum * sizeof (E4_CommandQueueDesc));
++
++    value  = CQ_QueuePtrsValue (cqsize, cq->cq_space, cq->cq_space);
++    if (dev->dev_devinfo.dev_revision_id == PCI_REVISION_ID_ELAN4_REVA)
++      value |= ((cqtype & CQ_Priority) ? CQ_RevA_Priority : 0);
++    else
++      value |= (((cqtype & CQ_Priority) ? CQ_RevB_Priority : 0) | 
++                ((cqtype & CQ_Reorder)  ? CQ_RevB_ReorderingQueue : CQ_RevB_32bitWriteQueue));
++
++    elan4_sdram_writeq (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_QueuePtrs), value);
++    elan4_sdram_writeq (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_HoldingValue), 0);
++    elan4_sdram_writeq (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_AckBuffers), 0);
++    elan4_sdram_writeq (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_Control), CQ_ControlValue (ctxt->ctxt_num, 2, perm));
++    pioflush_sdram (dev);
++
++    offset = (cqnum + dev->dev_cqoffset) * CQ_CommandMappingSize;
++
++    cq->cq_mapping = elan4_map_device (dev, ELAN4_BAR_REGISTERS, (offset & ~(PAGE_SIZE-1)), 
++                                     PAGE_SIZE, &cq->cq_handle) + (offset & (PAGE_SIZE-1));
++#ifdef CONFIG_MPSAS
++    if (ctxt == &dev->dev_ctxt)
++      return (cq);
++#endif
++
++    elan4_sdram_flushcache (dev, cq->cq_space, CQ_Size(cqsize));
++
++    return (cq);
++}
++    
++void
++elan4_freecq (ELAN4_CTXT *ctxt, ELAN4_CQ *cq)
++{
++    ELAN4_DEV *dev    = ctxt->ctxt_dev;
++    unsigned   offset = (elan4_cq2num(cq) + dev->dev_cqoffset) * CQ_CommandMappingSize;
++
++    elan4_flushcq (dev, cq);
++
++    elan4_unmap_device (dev, cq->cq_mapping - (offset & (PAGE_SIZE-1)), PAGE_SIZE, &cq->cq_handle);
++    elan4_sdram_free (dev, cq->cq_space, CQ_Size (cq->cq_size));
++
++    elan4_putcq (ctxt, cq);
++}
++
++void
++elan4_restartcq (ELAN4_DEV *dev, ELAN4_CQ *cq)
++{
++    sdramaddr_t   cqdesc = dev->dev_cqaddr + (elan4_cq2num(cq) * sizeof (E4_CommandQueueDesc));
++    int           hipri;
++    unsigned long flags;
++    
++    PRINTF1 (DBG_DEVICE, DBG_CPROC, "restartcq: restarting cq %p\n", cq);
++    
++    spin_lock_irqsave (&dev->dev_requeue_lock, flags);
++
++    while (read_reg32 (dev, CommandControl.CommandRequeuePtr) & E4_CommandRequeueBusy)
++      ;
++    
++    if (dev->dev_devinfo.dev_revision_id == PCI_REVISION_ID_ELAN4_REVA)
++      hipri = (elan4_sdram_readq (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_QueuePtrs)) & CQ_RevA_Priority) != 0;
++    else
++      hipri = (elan4_sdram_readq (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_QueuePtrs)) & CQ_RevB_Priority) != 0;
++    
++    if (hipri)
++    {
++      PRINTF1 (DBG_DEVICE, DBG_CPROC, "restartcq: restart cq %d as high pri\n", elan4_cq2num(cq));
++      write_reg32 (dev, CommandControl.CommandRequeuePtr, cqdesc | E4_CommandRequeueHighPri);
++    }
++    else
++    {
++      PRINTF1 (DBG_DEVICE, DBG_CPROC, "restartcq: restart cq %d as low pri\n", elan4_cq2num(cq));
++      write_reg32 (dev, CommandControl.CommandRequeuePtr, cqdesc);
++    }
++    pioflush_reg (dev);
++    
++    spin_unlock_irqrestore (&dev->dev_requeue_lock, flags);
++}
++
++static void
++flushcq_intop (ELAN4_DEV *dev, void *arg)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->dev_flush_lock, flags);
++    dev->dev_flush_finished |= (1 << (unsigned long) arg);
++    kcondvar_wakeupall (&dev->dev_flush_wait, &dev->dev_flush_lock);
++    spin_unlock_irqrestore (&dev->dev_flush_lock, flags);
++}
++void
++elan4_flushcq (ELAN4_DEV *dev, ELAN4_CQ *cq)
++{
++    int                 flushqnum = elan4_cq2num(cq) & (COMMAND_INSERTER_CACHE_ENTRIES-1);
++    ELAN4_CQ     *flushq    = dev->dev_flush_cq[flushqnum];
++    unsigned long flags;
++
++    PRINTF (DBG_DEVICE, DBG_FLUSH, "elan4_flushcq: cqnum=%d\n", elan4_cq2num(cq));
++
++    spin_lock_irqsave (&dev->dev_flush_lock, flags);
++
++    while (! (dev->dev_flush_finished & (1 << flushqnum)))
++      kcondvar_wait (&dev->dev_flush_wait, &dev->dev_flush_lock, &flags);
++    
++    dev->dev_flush_finished &= ~(1 << flushqnum);
++
++    dev->dev_flush_op[flushqnum].op_function = flushcq_intop;
++    dev->dev_flush_op[flushqnum].op_arg      = (void *) (unsigned long) flushqnum;
++    
++    elan4_queue_intop (dev, flushq, &dev->dev_flush_op[flushqnum]);
++
++    while (! (dev->dev_flush_finished & (1 << flushqnum)))
++      kcondvar_wait (&dev->dev_flush_wait, &dev->dev_flush_lock, &flags);
++    
++    spin_unlock_irqrestore (&dev->dev_flush_lock, flags);
++}
++
++void
++elan4_updatecq (ELAN4_DEV *dev, ELAN4_CQ *cq, unsigned perm, unsigned restart)
++{
++    sdramaddr_t cqdesc  = dev->dev_cqaddr + (elan4_cq2num(cq) * sizeof (E4_CommandQueueDesc));
++    E4_uint32   control = elan4_sdram_readl (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_Control));
++
++    /* Write the command queues control word, but ensure that the ChannelNotCompleted fields
++     * are not modified.   We use this to just alter the RestartCount/Permissions fields */
++
++    elan4_sdram_writel (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_Control), 
++                      CQ_ControlValue (CQ_Context (control), restart ? restart : CQ_RestartCount (control), perm));
++}
++
++/* instruction cache flush */
++static __inline__ void
++elan4_flush_icache_locked (ELAN4_DEV *dev)
++{
++    int i, j;
++
++    PRINTF0 (DBG_DEVICE, DBG_FLUSH, "elan4_flush_icache_locked: flushing icache\n");
++
++    for (i = 0; i < (E4_ICacheLines/E4_ICachePortSize); i++)
++    {
++        write_reg64 (dev, ICachePort_Cntl_Addr, i << E4_ICacheTagAddrShift);
++        for (j = 0; j < E4_ICachePortSize; j++)
++          write_reg64 (dev, ICachePort[j], E4_InvalidTagValue);
++    }
++
++    /*
++     * Initialise the top of the ICache Set0 with a instruction which will
++     * cause a know trap fingerprint so that the application can identify it
++     * and ignore the trap.
++     */
++    write_reg64 (dev, ICachePort_Cntl_Addr, E4_ICacheFixupOffset | E4_AccessICacheRams);
++
++    /* Errata 24: must ensure that the DCache is flushed after loading 
++     *            code for the thread processor. */
++    if (dev->dev_devinfo.dev_revision_id == PCI_REVISION_ID_ELAN4_REVA)
++      elan4_sdram_flushcache (dev, 0, E4_CacheSize);
++
++    pioflush_reg (dev);
++}
++
++static void
++device_iflush_haltop (ELAN4_DEV *dev, void *arg)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->dev_flush_lock, flags);
++    
++    elan4_flush_icache_locked (dev);
++
++    dev->dev_iflush_queued = 0;
++
++    kcondvar_wakeupall (&dev->dev_flush_wait, &dev->dev_flush_lock);
++    spin_unlock_irqrestore (&dev->dev_flush_lock, flags);
++}
++
++void
++elan4_flush_icache_halted (ELAN4_CTXT *ctxt)
++{
++    ELAN4_DEV *dev = ctxt->ctxt_dev;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->dev_flush_lock, flags);
++    
++    elan4_flush_icache_locked (dev);
++
++    spin_unlock_irqrestore (&dev->dev_flush_lock, flags);
++}
++
++void
++elan4_flush_icache (ELAN4_CTXT *ctxt)
++{
++    ELAN4_DEV *dev = ctxt->ctxt_dev;
++    unsigned long flags;
++    
++    spin_lock_irqsave (&dev->dev_flush_lock, flags);
++
++    PRINTF1 (DBG_DEVICE, DBG_FLUSH, "elan4_flush_icache: queued=%d\n", dev->dev_iflush_queued);
++
++    if (! dev->dev_iflush_queued)
++    {
++      dev->dev_iflush_queued = 1;
++      
++      elan4_queue_haltop (dev, &dev->dev_iflush_haltop);
++    }
++
++    while (dev->dev_iflush_queued)
++      kcondvar_wait (&dev->dev_flush_wait, &dev->dev_flush_lock, &flags);
++
++    spin_unlock_irqrestore (&dev->dev_flush_lock, flags);
++}
++
++/* device context operations */
++static void
++device_cproc_trap (ELAN4_CTXT *ctxt, E4_uint64 status, unsigned cqnum)
++{
++    ELAN4_DEV        *dev  = ctxt->ctxt_dev;
++    ELAN4_CPROC_TRAP *trap = &dev->dev_cproc_trap;
++
++    elan4_extract_cproc_trap (dev, status, trap, cqnum);
++
++    DBGCMD (DBG_DEVICE, DBG_FLUSH, elan4_display_cproc_trap (DBG_DEVICE, DBG_FLUSH, "device_cproc_trap", trap));
++
++    switch (CPROC_TrapType (trap->tr_status))
++    {
++    case CommandProcInterruptQueueOverflow:
++      PRINTF (ctxt, DBG_FLUSH, "device_cproc_trap: cqnum=%d\n", cqnum);
++
++      /* XXXX: we could either just hit restart (and hope) - or we could extract
++       *       the event interrupt cookie out and "complete" the command before
++       *       restarting it */
++      elan4_restartcq (dev, dev->dev_flush_cq[cqnum]);
++      return;
++
++    case CommandProcDmaQueueOverflow:
++    case CommandProcPermissionTrap:
++      handle_dma_flushops (dev, status, cqnum);
++      return;
++      
++    default:
++      printk ("device_cproc_trap: status=%llx control=%llx TrapType=%x cqnum=%d\n", (long long) trap->tr_status,
++              elan4_sdram_readq (dev, dev->dev_cqaddr + cqnum * sizeof (E4_CommandQueueDesc) +
++                                 offsetof (E4_CommandQueueDesc, CQ_Control)),
++              (int) CPROC_TrapType(trap->tr_status), cqnum);
++      panic ("device_cproc_trap");
++    }
++}
++
++static void
++device_tproc_trap (ELAN4_CTXT *ctxt, E4_uint64 status)
++{
++    ELAN4_TPROC_TRAP trap;
++
++    elan4_extract_tproc_trap (ctxt->ctxt_dev, status, &trap);
++
++    elan4_display_tproc_trap (DBG_CONSOLE, DBG_TRAP, "device_tproc_trap", &trap);
++    panic ("device_tproc_trap");
++}
++
++static void
++device_dproc_trap (ELAN4_CTXT *ctxt, E4_uint64 status, unsigned unit)
++{
++    ELAN4_DPROC_TRAP trap;
++
++    elan4_extract_dproc_trap (ctxt->ctxt_dev, status, &trap, unit);
++
++    elan4_display_dproc_trap (DBG_CONSOLE, DBG_TRAP, "device_dproc_trap", &trap);
++    panic ("device_dproc_trap");
++}
++
++static void
++device_interrupt (ELAN4_CTXT *ctxt, E4_uint64 cookie)
++{
++    ELAN4_DEV *dev = (ELAN4_DEV *) ctxt;
++    struct list_head *el,*nel;
++    unsigned long flags;
++
++    PRINTF (ctxt, DBG_FLUSH, "device_interrupt: cookie=%llx\n", cookie);
++
++    spin_lock_irqsave (&dev->dev_intop_lock, flags);
++    list_for_each_safe (el, nel, &dev->dev_intop_list) {
++      ELAN4_INTOP *op = list_entry (el, ELAN4_INTOP, op_link);
++
++      if (op->op_cookie == cookie)
++      {
++          if ((op->op_cookie & INTOP_TYPE_MASK) == INTOP_ONESHOT)
++              list_del (&op->op_link);
++
++          spin_unlock_irqrestore (&dev->dev_intop_lock, flags);
++          
++          (*op->op_function)(dev, op->op_arg);
++          return;
++      }
++    }
++    spin_unlock_irqrestore (&dev->dev_intop_lock, flags);
++
++    panic ("device_interrupt: interrupt cookie %llx not found\n", cookie);
++}
++
++static void
++device_iproc_trap (ELAN4_CTXT *ctxt, E4_uint64 status, unsigned unit)
++{
++    ELAN4_DEV *dev = ctxt->ctxt_dev;
++    ELAN4_IPROC_TRAP *trap = &dev->dev_iproc_trap;
++
++    elan4_extract_iproc_trap (dev, status, trap, unit);
++    elan4_inspect_iproc_trap (trap);
++
++    DBGCMD (ctxt, DBG_IPROC, elan4_display_iproc_trap (ctxt, DBG_IPROC, "device_iproc_trap", trap));
++
++    if (elan4_neterr_iproc_trap (dev, trap))
++      return;
++
++    elan4_display_iproc_trap (DBG_CONSOLE, DBG_TRAP, "device_iproc_trap", trap);
++    panic ("device_iproc_trap: unexpected trap\n");
++}
++
++ELAN4_TRAP_OPS device_trap_ops = 
++{
++    NULL,
++    device_cproc_trap,
++    device_dproc_trap,
++    device_tproc_trap,
++    device_iproc_trap,
++    device_interrupt,
++};
++
++/*
++ * elan4_initialise_device
++ *    initialise the ELAN4_DEV struct - spinlocks,cvs etc.
++ *    map the registers, sdram etc
++ */
++int
++elan4_initialise_device (ELAN4_DEV *dev)
++{
++    int i, bit;
++
++    if (elan4_mainint_resched_ticks == 0)
++      elan4_mainint_resched_ticks = (hz/4);
++
++    /* map the registers */
++    switch (dev->dev_devinfo.dev_revision_id)
++    {
++    case PCI_REVISION_ID_ELAN4_REVA:
++      dev->dev_regs = elan4_map_device (dev, ELAN4_BAR_REGISTERS, ELAN4_REVA_REG_OFFSET, ELAN4_REG_SIZE, &dev->dev_regs_handle);
++      
++      dev->dev_rom  = elan4_map_device (dev, ELAN4_BAR_REGISTERS, ELAN4_REVA_EBUS_OFFSET + ELAN4_REVA_EBUS_ROM_OFFSET, 
++                                        ELAN4_REVA_EBUS_ROM_SIZE, &dev->dev_rom_handle);
++      break;
++      
++    case PCI_REVISION_ID_ELAN4_REVB:
++      dev->dev_regs = elan4_map_device (dev, ELAN4_BAR_REGISTERS, ELAN4_REVB_REG_OFFSET, ELAN4_REG_SIZE, &dev->dev_regs_handle);
++      dev->dev_rom  = (ioaddr_t) 0;
++      dev->dev_i2c  = elan4_map_device (dev, ELAN4_BAR_REGISTERS, ELAN4_REVB_I2C_OFFSET, ELAN4_REVB_I2C_SIZE, &dev->dev_i2c_handle);
++      break;
++
++    default:
++      return -EINVAL;
++    }
++
++    /* XXXX: parse the ebus rom to determine the sdram configuration */
++    {
++      extern long long       sdram_cfg;
++
++      if (sdram_cfg == 0)
++          dev->dev_sdram_cfg = SDRAM_STARTUP_VALUE;
++      else
++          dev->dev_sdram_cfg = sdram_cfg;
++    }
++
++    for (bit = 0; ((1 << bit) & elan4_resource_len (dev, ELAN4_BAR_SDRAM)) == 0; bit++)
++      ;
++
++    switch ((dev->dev_sdram_cfg >> SDRAM_RamSize_SH) & 3)
++    {
++    case 0:                   /* 64Mbit, 128Mbit, 256Mbit, 512Mbit or 1Gbit (16-bit output) */
++      dev->dev_sdram_numbanks = 4; bit -= 2;
++      for (i = 0; i < dev->dev_sdram_numbanks; i++)
++      {
++          dev->dev_sdram_banks[i].b_base = (i << bit);
++          dev->dev_sdram_banks[i].b_size = (1 << bit);
++      }
++      break;
++
++    case 1:                   /*  64Mbit, 128Mbit, 256Mbit or 512Mbit (8-bit output) */
++      dev->dev_sdram_numbanks = 4; bit -= 2;
++      for (i = 0; i < dev->dev_sdram_numbanks; i++)
++      {
++          dev->dev_sdram_banks[i].b_base = ((i & 2) << (bit)) | ((i & 1) << (bit-1));
++          dev->dev_sdram_banks[i].b_size = (1 << bit);
++      }
++      break;
++      
++    case 2:                   /* 2Gbit (16-bit output) or 1Gbit (8-bit output) */
++      dev->dev_sdram_numbanks = 2; bit--;
++      for (i = 0; i < dev->dev_sdram_numbanks; i++)
++      {
++          dev->dev_sdram_banks[i].b_base = (i << bit);
++          dev->dev_sdram_banks[i].b_size = (1 << bit);
++      }
++      break;
++
++    case 3:                   /* 4Gbit (16-bit output) or 2Gbit (8-bit output) */
++      dev->dev_sdram_numbanks = 1;
++      dev->dev_sdram_banks[0].b_base = 0;
++      dev->dev_sdram_banks[0].b_size = (1 << bit);
++      break;
++    }
++
++    elan4_sdram_init (dev);
++
++    /* initialise locks for classes of interrupts */
++    spin_lock_init (&dev->dev_trap_lock);
++    spin_lock_init (&dev->dev_intop_lock);
++    spin_lock_init (&dev->dev_haltop_lock);
++    spin_lock_init (&dev->dev_mainint_lock);
++
++    /* initialise other locks */
++    spin_lock_init (&dev->dev_i2c_lock);
++
++    spin_lock_init (&dev->dev_mmulock);
++    spin_lock_init (&dev->dev_cqlock);
++    spin_lock_init (&dev->dev_ctxlock);
++
++    spin_lock_init (&dev->dev_intmask_lock);
++    spin_lock_init (&dev->dev_syscontrol_lock);
++
++    spin_lock_init (&dev->dev_ctxt_lock);
++    spin_lock_init (&dev->dev_flush_lock);
++    spin_lock_init (&dev->dev_requeue_lock);
++
++    kmutex_init (&dev->dev_lock);
++
++    kcondvar_init (&dev->dev_mainint_wait);
++    kcondvar_init (&dev->dev_flush_wait);
++
++    /* initialsie lists */
++    INIT_LIST_HEAD (&dev->dev_ctxt_list);
++    INIT_LIST_HEAD (&dev->dev_intop_list);
++    INIT_LIST_HEAD (&dev->dev_interruptq_list);
++    INIT_LIST_HEAD (&dev->dev_hc_list);
++    INIT_LIST_HEAD (&dev->dev_haltop_list);
++    INIT_LIST_HEAD (&dev->dev_dma_flushop[0].list);
++    INIT_LIST_HEAD (&dev->dev_dma_flushop[1].list);
++
++    dev->dev_state = ELAN4_STATE_STOPPED;
++
++    return (0);
++}
++
++void
++elan4_finalise_device (ELAN4_DEV *dev)
++{
++    kcondvar_destroy (&dev->dev_flush_wait);
++    kcondvar_destroy (&dev->dev_mainint_wait);
++
++    kmutex_destroy (&dev->dev_lock);
++
++    spin_lock_destroy (&dev->dev_requeue_lock);
++    spin_lock_destroy (&dev->dev_flush_lock);
++    spin_lock_destroy (&dev->dev_ctxt_lock);
++
++    spin_lock_destroy (&dev->dev_syscontrol_lock);
++    spin_lock_destroy (&dev->dev_intmask_lock);
++
++    spin_lock_destroy (&dev->dev_ctxlock);
++    spin_lock_destroy (&dev->dev_cqlock);
++    spin_lock_destroy (&dev->dev_mmulock);
++
++    spin_lock_destroy (&dev->dev_i2c_lock);
++
++    spin_lock_destroy (&dev->dev_mainint_lock);
++    spin_lock_destroy (&dev->dev_haltop_lock);
++    spin_lock_destroy (&dev->dev_intop_lock);
++    spin_lock_destroy (&dev->dev_trap_lock);
++
++    while (! list_empty (&dev->dev_hc_list))
++    {
++      ELAN4_HASH_CHUNK *hc = list_entry (dev->dev_hc_list.next, ELAN4_HASH_CHUNK, hc_link);
++      
++      list_del (&hc->hc_link);
++
++      KMEM_FREE(hc, sizeof (ELAN4_HASH_CHUNK));
++    }
++    
++    elan4_sdram_fini (dev);
++    
++    switch (dev->dev_devinfo.dev_revision_id)
++    {
++    case PCI_REVISION_ID_ELAN4_REVA:
++      elan4_unmap_device (dev, dev->dev_rom,  ELAN4_REVA_EBUS_ROM_SIZE, &dev->dev_rom_handle);
++      elan4_unmap_device (dev, dev->dev_regs, ELAN4_REG_SIZE, &dev->dev_regs_handle);
++      break;
++    case PCI_REVISION_ID_ELAN4_REVB:
++      elan4_unmap_device (dev, dev->dev_i2c,  ELAN4_REVB_I2C_SIZE, &dev->dev_i2c_handle);
++      elan4_unmap_device (dev, dev->dev_regs, ELAN4_REG_SIZE, &dev->dev_regs_handle);
++      break;
++    }
++}
++
++static int
++measure_sysclk (ELAN4_DEV *dev)       
++{
++    E4_uint64 val0, val1;
++    E4_uint32 ticks, ns;
++    
++    write_ureg64 (dev, StatCont, STP_SYS_CLOCK_RATE0);
++    
++    val0 = read_ureg64 (dev, StatCounts[0]);
++    udelay (1000);
++    val1 = read_ureg64 (dev, StatCounts[0]);
++    
++    
++    ticks = ((val1 >> 32) - (val0 >> 32));
++    ns    = ((val1 & 0xffffffff) - (val0 & 0xffffffff));
++    
++    return (ticks / (ns / 1000));
++}
++
++static void
++initialise_cache (ELAN4_DEV *dev)
++{
++    register int set, line;
++
++    /* Initialise the cache to "map" the bottom of sdram - we will use
++     * this space for cache flushing, so require the cache to be set
++     * up so that cachelines for this are in the correct set.
++     *
++     * XXXX: for MPSAS we set bit 28, to ensure that any access to 
++     *       sdram causes the line to be filled first to expunge any
++     *       Xs. */
++    for (set = 0; set < E4_NumCacheSets; set++)
++      for (line = 0; line < E4_NumCacheLines; line++)
++          write_tag (dev, Tags[set][line], (((E4_uint64) set) << 29) | (1 << 28) | (line << 16));
++}
++
++#ifndef CONFIG_MPSAS
++static void
++initialise_cache_tags (ELAN4_DEV *dev, unsigned addr)
++{
++    register int set, line;
++
++    /* Initialise the whole cache to hold sdram at "addr" as direct mapped */
++
++    for (set = 0; set < E4_NumCacheSets; set++)
++      for (line = 0; line < E4_NumCacheLines; line++)
++          write_tag (dev, Tags[set][line], addr | (set << 13) | (1 << 11));
++}
++
++static void
++initialise_ecc (ELAN4_DEV *dev, ELAN4_SDRAM_BANK *bank)
++{
++    register int i, addr;
++
++    if (dev->dev_devinfo.dev_revision_id == PCI_REVISION_ID_ELAN4_REVA)
++    {
++        initialise_cache_tags (dev, E4_CacheSize);
++        for (addr = 0; addr < bank->b_size; addr += E4_CacheSize)
++        {
++          for (i = 0; i < E4_CacheSize; i += sizeof (E4_uint64))
++              writeq (0xbeec000000000000ull | addr | i, bank->b_ioaddr + addr + i);
++          initialise_cache_tags (dev, addr);
++        }
++    }
++    else
++    {
++      /* Write the whole of this bank of sdram. */
++        for (addr = 0; addr < bank->b_size; addr += sizeof (E4_uint64))
++          writeq (0xbeec000000000000ull | addr, bank->b_ioaddr + addr);
++
++      /* Now flush out the top out of the cache */
++        for (addr = 0; addr < E4_CacheSize; addr += sizeof (E4_uint64))
++          writeq (0xbeec000000000000ull | addr, bank->b_ioaddr + addr);
++
++      /* Now read the top value of sdram to guarantee the write has occured before the ecc is enabled */
++      __elan4_readq (dev, bank->b_ioaddr + bank->b_size - sizeof (E4_uint64));
++    }
++}
++#endif
++
++#ifdef CONFIG_MPSAS
++static void
++do_initdma (ELAN4_DEV *dev)
++{
++#define VIRTUAL_ADDRESS       0x10000000ull
++    ELAN4_CQ  *cq  = dev->dev_flush_cq[0];
++    E4_uint64 value;
++    E4_uint32 intreg;
++    E4_uint64 status;
++
++    PRINTF (DBG_DEVICE, DBG_CONFIG, "elan: performing initialising dma\n");
++    
++    DISABLE_INT_MASK (dev, INT_Dma0Proc | INT_Dma1Proc);
++    
++    /* initialise the context filter */
++    elan4_attach_filter (&dev->dev_ctxt, 0);
++
++    /* now issue a DMA - we expect this to trap */
++    writeq (E4_DMA_TYPE_SIZE (128*4, DMA_DataTypeByte, 0, 0) | RUN_DMA_CMD, cq->cq_mapping + (0 << 3));
++    writeq (0,                                                              cq->cq_mapping + (1 << 3));
++    writeq (0,                                                              cq->cq_mapping + (2 << 3));
++    writeq (dev->dev_tproc_space,                                           cq->cq_mapping + (3 << 3));
++    writeq (dev->dev_tproc_space,                                           cq->cq_mapping + (4 << 3));
++    writeq (0,                                                              cq->cq_mapping + (5 << 3));
++    writeq (0,                                                              cq->cq_mapping + (6 << 3));
++    
++    /* spin waiting for it to trap - then restart the dma processor */
++    do {
++      value   = read_reg64 (dev, IntAndMaskReg);
++      intreg  = (value >> E4_INTERRUPT_REG_SHIFT);
++    } while ((intreg & (INT_Dma0Proc | INT_Dma1Proc)) == 0);
++    
++    /* check it trapped for the right reason */
++    status = (intreg & INT_Dma0Proc) ? read_reg64 (dev, DProc0Status) : read_reg64 (dev, DProc1Status);
++    
++    if (DPROC_PrefetcherFault (status) || (DPROC_TrapType(status) != DmaProcFailCountError && DPROC_TrapType(status) != DmaProcPacketAckError))
++    {
++      printk ("elan: bad dma trap, status = %lx\n", (long)status);
++      panic ("elan: bad dma trap\n");
++    }
++    
++    PULSE_SCHED_RESTART (dev, SCH_RestartDma0Proc | SCH_RestartDma1Proc | SCH_RestartDmaPrefetchProc);
++
++    elan4_detach _filter (&dev->dev_ctxt, 0);
++
++    ENABLE_INT_MASK (dev, INT_Dma0Proc | INT_Dma1Proc);
++#undef VIRTUAL_ADDRESS
++}
++#endif
++
++static int
++ebus_read_vpd (ELAN4_DEV *dev, unsigned char *data, unsigned int nob)
++{
++    unsigned int pci_data_ptr;
++    unsigned int vpd_ptr;
++    register int i;
++
++    if (read_ebus_rom (dev, 0) != 0x55 || read_ebus_rom (dev, 1) != 0xaa)
++    {
++      printk ("elan%d: invalid rom signature in ebus rom\n", dev->dev_instance);
++      return -EINVAL;
++    }
++
++    pci_data_ptr = (read_ebus_rom (dev, 0x19) << 8) | read_ebus_rom (dev, 0x18);
++
++    /* check the pci data structure */
++    if (read_ebus_rom (dev, pci_data_ptr + 0) != 'P' ||
++      read_ebus_rom (dev, pci_data_ptr + 1) != 'C' ||
++      read_ebus_rom (dev, pci_data_ptr + 2) != 'I' ||
++      read_ebus_rom (dev, pci_data_ptr + 3) != 'R')
++    {
++      printk ("elan%d: invalid pci data structure in ebus rom\n", dev->dev_instance);
++      return -EINVAL;
++    }
++    
++    /* extract the VPD pointer */
++    vpd_ptr = (read_ebus_rom (dev, pci_data_ptr + 9) << 8) | read_ebus_rom (dev, pci_data_ptr + 8);
++
++    if (vpd_ptr == 0)
++    {
++      printk ("elan%d: no vital product data in ebus rom\n", dev->dev_instance);
++      return -EINVAL;
++    }
++    
++    /* read the vpd data */
++    for (i = 0; i < nob; i++)
++      data[i] = read_ebus_rom (dev, vpd_ptr + i);
++
++    return 0;
++}
++
++int
++elan4_read_vpd (ELAN4_DEV *dev, unsigned char *tag, unsigned char *result) 
++{
++    unsigned char vpd[I2C_ELAN_EEPROM_VPD_SIZE];
++    unsigned char *ptr = vpd;
++    unsigned int   finished = 0;
++    unsigned char *lim;
++    unsigned char  name[3];
++    unsigned char  value[256];
++    unsigned char  type;
++    unsigned int   len, len2;
++    register int   i;
++
++    if (dev->dev_devinfo.dev_revision_id == PCI_REVISION_ID_ELAN4_REVA)
++    {
++      if (ebus_read_vpd (dev, vpd, I2C_ELAN_EEPROM_VPD_SIZE) < 0)
++      {
++          PRINTF1 (DBG_DEVICE, DBG_CONFIG, "elan%d: elan4_read_vpd, unable to read serial number from EBUS rom\n", dev->dev_instance);
++          return -EINVAL ;
++      }       
++    }
++    else
++    {
++      if (i2c_read_rom (dev, I2C_ELAN_EEPROM_VPD_BASEADDR, I2C_ELAN_EEPROM_VPD_SIZE, vpd) < 0)
++      {
++          PRINTF1 (DBG_DEVICE, DBG_CONFIG, "elan%d: elan4_read_vpd, unable to read serial number from I2C rom\n", dev->dev_instance);
++          return  -EINVAL;
++      }
++    }
++
++    result[0] = 0;
++    while (! finished)
++    {
++      type = *ptr++;
++      
++      if (type & LARGE_RESOURCE_BIT)
++      {
++          len = *(ptr++);
++          len += *(ptr++) << 8;
++          
++          switch (type & ~LARGE_RESOURCE_BIT)
++          {
++          case LARGE_RESOURCE_STRING:
++          case LARGE_RESOURCE_VENDOR_DEFINED:
++              ptr += len;
++              break;
++              
++          case LARGE_RESOURCE_VITAL_PRODUCT_DATA:
++              for (lim = ptr + len; ptr < lim; )
++              {
++                  name[0] = *ptr++;
++                  name[1] = *ptr++;
++                  name[2] = '\0';
++                  len2    = *ptr++;
++                  
++                  for (i = 0; i < len2 && ptr < lim; i++)
++                      value[i] = *ptr++;
++                  value[i] = '\0';
++                                  
++                  PRINTF3 (DBG_DEVICE, DBG_CONFIG, "elan%d: elan4_read_vpd, %s: $s\n", dev->dev_instance, name, value);
++
++                  if (tag != NULL) 
++                  { /* looking for just one tag */
++                      if (!strcmp (name, tag))
++                          strcpy(result, value);
++                  } 
++                  else 
++                  { /* get all tags */
++                      strcat(result,name);
++                      strcat(result,": ");
++                      strcat(result,value);
++                      strcat(result,"\n");
++                  }
++              }
++              break;
++              
++          default:
++              PRINTF2 (DBG_DEVICE, DBG_CONFIG, "elan%d: elan4_read_vpd, unknown large resource %x\n", dev->dev_instance, type);
++              finished = 1;
++              break;
++          }
++      }
++      else
++      {
++          len = type & 0x7;
++          
++          switch (type >> 3)
++          {
++          case SMALL_RESOURCE_COMPATIBLE_DEVICE_ID:
++              ptr += len;
++              break;
++              
++          case SMALL_RESOURCE_VENDOR_DEFINED:
++              ptr += len;
++              break;
++              
++          case SMALL_RESOURCE_END_TAG:
++              finished = 1;
++              break;
++              
++          default:
++              PRINTF2 (DBG_DEVICE, DBG_CONFIG, "elan%d: elan4_read_vpd, unknown small resource %x\n", dev->dev_instance, type >> 3);
++              finished = 1;
++              break;
++          }
++      }
++    }
++
++    if ( result[0] == 0 ) {
++      if ( tag != 0 ) 
++          PRINTF2 (DBG_DEVICE, DBG_CONFIG, "elan%d: elan4_read_vpd, failed to find tag %s\n", dev->dev_instance, tag);
++      else
++          PRINTF1 (DBG_DEVICE, DBG_CONFIG, "elan%d: elan4_read_vpd, failed to find any tags\n", dev->dev_instance);
++      return -EINVAL;
++    }
++
++    return (0);
++}
++
++int
++elan4_start_device (ELAN4_DEV *dev)
++{
++    E4_VirtualProcessEntry entry;
++    unsigned             pagesizeval[2];
++    unsigned             hashsizeval[2];
++    register int           i, j, tbl, res;
++    unsigned               attempts = 0;
++    E4_PAGE_SIZE_TABLE;
++    unsigned char          serial[256];
++    unsigned int           sdram_factor = SDRAM_166_DLL_CORRECTION_FACTOR;
++
++    PRINTF (DBG_DEVICE, DBG_ALL, "elan4_start_device: entered\n");
++
++    dev->dev_state = ELAN4_STATE_STARTING;
++
++ tryagain:
++    /* Initialise the pci config space */
++    if ((res = elan4_pciinit (dev)) < 0)
++      return (res);
++
++    /* Display the serial number */
++    if (elan4_read_vpd (dev, "SN", serial))
++      printk("elan%d: SN: failed to read\n", dev->dev_instance);
++    else
++      printk("elan%d: SN: %s\n", dev->dev_instance, serial);
++
++    /* initialise the interrupt mask to zero */
++    SET_INT_MASK (dev, 0);
++
++    /* Initialise the device registers */
++    write_reg64 (dev, TlbLineValue, 0);
++    write_reg64 (dev, SysControlReg, 0);
++
++    /* Initialise the SDRAM using the configuration value from the ROM */
++    write_reg64 (dev, SDRamConfigReg, dev->dev_sdram_cfg | SDRAM_SETUP);
++
++    /* Setup the linkport registers */
++    write_reg64 (dev, LinkPortLock, elan4_linkport_lock);
++
++    /* Setup the tick rates, start the clock, and init the stats registers */
++    write_ureg32 (dev, ClockTickRate.s.TickRates, ELAN4_CLOCK_TICK_RATE);
++    write_ureg64 (dev, Clock, 0);
++    write_ureg32 (dev, InstCount.s.StatsCount, 0);
++    for (i = 0; i < 8; i++)
++      write_ureg32 (dev, StatCounts[i].s.StatsCount, 0);
++
++    /* Initialise the Link Control register - disable the TLB prefetcher on RevB
++     * as it can cause very occasional data corruption. */
++    if (dev->dev_devinfo.dev_revision_id == PCI_REVISION_ID_ELAN4_REVB)
++      write_reg32 (dev, LinkControlReg, LCONT_EN_SYS_READS | LCONT_REVB_DISABLE_TLB_PREFETCH);
++    else
++      write_reg32 (dev, LinkControlReg, LCONT_EN_SYS_READS);
++
++    /* Initialise the Link Control Settings to set the PLL Reference Value */
++    write_reg32 (dev, LinkContSettings, 
++               (elan4_mod45disable ? LCONT_MOD45_DISABLE : 0) |
++               (3 << LCONT_CONFIG_PHASE_SHIFT) |
++               ((elan4_pll_div & LCONT_PLL_REF_VAL_BITS_MASK) << LCONT_PLL_REF_VAL_BITS_SHIFT) |
++               (LCONT_VOD_360 << LCONT_LVDS_VOLTAGE_BITS_SHIFT) |
++               (LCONT_TERM_AUTO_OHM << LCONT_LVDS_TERMINATION_SHIFT));
++
++    /* Clear the link error LED on RevB and above */
++    if (dev->dev_devinfo.dev_revision_id != PCI_REVISION_ID_ELAN4_REVA)
++      write_i2c (dev, I2cStatus, read_i2c (dev, I2cStatus) | I2cCntl_ClearLinkError);
++
++    /* Compute the SysClk frequency and update the PLL if necessary */
++    if (dev->dev_devinfo.dev_revision_id != PCI_REVISION_ID_ELAN4_REVA)
++    {
++      int mhz = measure_sysclk (dev);
++
++      if (elan4_pll_cfg != 0 || mhz > 190 || mhz < 170)
++          printk ("elan%d: SysClk running at %d Mhz\n", dev->dev_instance, measure_sysclk (dev));
++      else
++      {
++          sdram_factor = SDRAM_150_DLL_CORRECTION_FACTOR;
++
++          elan4_updatepll (dev, ECTRL_SYS_CLOCK_RATIO_4_3);
++          
++          printk ("elan%d: SysClk now running at %d Mhz\n", dev->dev_instance, measure_sysclk (dev));
++      }
++    }
++      
++    initialise_cache (dev);
++
++    /* Initialise the MMU hash table parameters */
++    /* Select the largest elan pagesize which is spanned by the
++     * system pagesize for mmu table 0*/
++    for (i = 0; i < E4_PAGE_SIZE_TABLE_SIZE; i++)
++      if (PageSizeTable[i] > PAGE_SHIFT)
++          break;
++
++    pagesizeval[0] = i - 1;
++    hashsizeval[0] = elan4_hash_0_size_val;
++      
++    /* Select a suitable elan pagesize to match any "large" page
++     * support that the OS provides. */
++    pagesizeval[1] = PAGE_SIZE_4M;
++    hashsizeval[1] = elan4_hash_1_size_val;
++
++    for (tbl = 0; tbl < NUM_HASH_TABLES; tbl++)
++    {
++      dev->dev_pagesizeval[tbl]   = pagesizeval[tbl];
++      dev->dev_pageshift[tbl]     = PageSizeTable[pagesizeval[tbl]];
++      dev->dev_hashsize[tbl]      = (1 << hashsizeval[tbl])/sizeof (E4_HashTableEntry);
++      dev->dev_rsvd_hashmask[tbl] = ((1 << (27 - dev->dev_pageshift[tbl]))-1) & ~((1 << hashsizeval[tbl])-1);
++      dev->dev_rsvd_hashval[tbl]  = 0xFFFFFFFF;
++    }
++
++    PRINTF2 (DBG_DEVICE, DBG_CONFIG, "elan4_start_device: pageshifts %d,%d\n", dev->dev_pageshift[0], 
++           NUM_HASH_TABLES == 2 ? dev->dev_pageshift[1] : 0);
++
++    /* Initialise the control register to the desired value */
++    dev->dev_syscontrol = (CONT_EN_ALL_SETS | CONT_MMU_ENABLE | CONT_CACHE_ALL | CONT_2K_NOT_1K_DMA_PACKETS |
++                         (pagesizeval[0] << CONT_TABLE0_PAGE_SIZE_SHIFT) | (hashsizeval[0] << CONT_TABLE0_MASK_SIZE_SHIFT));
++
++    if (NUM_HASH_TABLES == 2)
++      dev->dev_syscontrol |= CONT_TWO_HASH_TABLES | (pagesizeval[1] << CONT_TABLE1_PAGE_SIZE_SHIFT) | (hashsizeval[1] << CONT_TABLE1_MASK_SIZE_SHIFT);
++
++    write_reg64 (dev, SysControlReg, dev->dev_syscontrol);
++
++    /* use direct mapped pci writes during sdram initialisation, since for 
++     * cache flushing to work, we need to ensure that the cacheflush page
++     * never gets lines into the incorrect cache set. */
++    SET_SYSCONTROL (dev, dev_direct_map_pci_writes, CONT_DIRECT_MAP_PCI_WRITES);
++
++    if (dev->dev_devinfo.dev_revision_id == PCI_REVISION_ID_ELAN4_REVB)
++      elan4_sdram_setup_delay_lines(dev, sdram_factor);
++
++    for (i = res = 0; i < dev->dev_sdram_numbanks; i++)
++      if (dev->dev_sdram_banks[i].b_size)
++          res |= elan4_sdram_init_bank (dev, &dev->dev_sdram_banks[i]);
++
++    if (! res)
++    {
++      if (dev->dev_devinfo.dev_device_id == PCI_REVISION_ID_ELAN4_REVB && ++attempts < 5)
++      {
++          printk ("elan%d: sdram not working, resetting\n", dev->dev_instance);
++          goto tryagain;
++      }
++
++      printk ("elan%d: could not find any sdram banks\n", dev->dev_instance);
++      goto failed;
++    }
++
++#ifndef CONFIG_MPSAS
++    PRINTF0 (DBG_DEVICE, DBG_CONFIG, "elan4_start_device: initialising for ECC\n");
++
++    for (i = 0 ; i < dev->dev_sdram_numbanks; i++)
++      if (dev->dev_sdram_banks[i].b_ioaddr)
++          initialise_ecc (dev, &dev->dev_sdram_banks[i]);
++#endif
++
++    dev->dev_sdram_initial_ecc_val = read_reg64 (dev, SDRamECCStatus);
++
++    /* Now enable ECC after we've scrubbed the memory */
++    write_reg64 (dev, SDRamConfigReg, dev->dev_sdram_cfg | SDRAM_ENABLE_ECC);
++
++    /* clear any errors, and flush the tlb/route cache */
++    PULSE_SYSCONTROL (dev, CONT_TLB_FLUSH | CONT_ROUTE_FLUSH | CONT_CLEAR_LINKPORT_INT | CONT_CLEAR_SDRAM_ERROR);
++
++    write_ureg32 (dev, InstCount.s.StatsCount, 0);
++
++    /* Initialise the thread processor's register file */
++    for (i = 0; i < 64; i++)
++      write_reg64 (dev, TProcRegs[i], 0);
++
++    /* Initialise the thread processor's ICache tags */
++    for (i = 0; i < (E4_ICacheLines/E4_ICachePortSize); i++)
++    {
++        write_reg64 (dev, ICachePort_Cntl_Addr, i << E4_ICacheTagAddrShift);
++        for (j = 0; j < E4_ICachePortSize; j++)
++          write_reg64 (dev, ICachePort[j], E4_InvalidTagValue);
++    }
++
++    /*
++     * Initialise the ICache with a sethi %hi(addr << 7), %r0
++     * writing 8 64 bit values per loop of sethi %g0 values ending in 77 for something different??
++     */
++    for (i = 0; i < E4_ICacheSizeInBytes; i += (E4_ICachePortSize << 3))
++    {
++      write_reg64 (dev, ICachePort_Cntl_Addr, E4_AccessICacheRams | (i >> 3));
++
++      for (j = 0; j < E4_ICachePortSize; j++)
++          write_reg64 (dev, ICachePort[j], 
++                       (E4_uint64) (((E4_uint64)i << (4+7))    + ((E4_uint64)j << (1+7))    + (0x077)) |
++                       (E4_uint64) (((E4_uint64)i << (4+7+32)) + ((E4_uint64)j << (1+7+32)) + (0x0e7)) << 32);
++    }
++
++    /*
++     * Initialise the top of the ICache Set0 with a instruction which will
++     * cause a know trap fingerprint so that the application can identify it
++     * and ignore the trap.
++     */
++    write_reg64 (dev, ICachePort_Cntl_Addr, E4_ICacheFixupOffset | E4_AccessICacheRams);
++    for (i = 0; i < E4_ICachePortSize; i++)
++      write_reg64 (dev, ICachePort[i], E4_ICacheFixupInsn | (E4_ICacheFixupInsn << 32));
++
++    /* create the buddy allocator for SDRAM */
++    for (i = 0; i < dev->dev_sdram_numbanks; i++)
++      if (dev->dev_sdram_banks[i].b_ioaddr)
++          elan4_sdram_add_bank (dev, &dev->dev_sdram_banks[i]);
++
++    dev->dev_ctxtableshift        = elan4_ctxt_table_shift;
++    dev->dev_cqcount              = (1 << elan4_ln2_max_cqs);
++    dev->dev_cqreorder            = 0;
++
++    /* allocate the sdram for cache flushing whilst still in direct mapped mode */
++    dev->dev_cacheflush_space = elan4_sdram_alloc (dev, E4_CacheSize);
++
++    /* and longer need direct mapped pci writes */
++    CLEAR_SYSCONTROL (dev, dev_direct_map_pci_writes, CONT_DIRECT_MAP_PCI_WRITES);
++
++    /* allocate the hash tables, command queues, context tables etc */
++    PRINTF0 (DBG_DEVICE, DBG_CONFIG, "elan4_start_device: allocating hash tables, command queueus, context tables\n");
++
++    dev->dev_comqlowpri       = elan4_sdram_alloc (dev, (1 << COMMAND_RUN_QUEUE_BITS));
++    dev->dev_comqhighpri      = elan4_sdram_alloc (dev, (1 << COMMAND_RUN_QUEUE_BITS));
++    dev->dev_cqaddr           = elan4_sdram_alloc (dev, sizeof (E4_CommandQueueDesc) * dev->dev_cqcount);
++    dev->dev_dmaqhighpri      = elan4_sdram_alloc (dev, E4_QueueSize(elan4_dmaq_highpri_size));
++    dev->dev_dmaqlowpri       = elan4_sdram_alloc (dev, E4_QueueSize(elan4_dmaq_lowpri_size));
++    dev->dev_threadqhighpri   = elan4_sdram_alloc (dev, E4_QueueSize(elan4_threadq_highpri_size));
++    dev->dev_threadqlowpri    = elan4_sdram_alloc (dev, E4_QueueSize(elan4_threadq_lowpri_size));
++    dev->dev_interruptq       = elan4_sdram_alloc (dev, E4_QueueSize(elan4_interruptq_size));
++
++    dev->dev_ctxtable         = elan4_sdram_alloc (dev, (1 << dev->dev_ctxtableshift) * sizeof (E4_ContextControlBlock));
++    dev->dev_faultarea        = elan4_sdram_alloc (dev, CUN_Entries * sizeof (E4_FaultSave));
++    dev->dev_inputtraparea    = elan4_sdram_alloc (dev, sizeof (E4_IprocTrapState));
++
++    dev->dev_sdrampages[0]    = elan4_sdram_alloc (dev, SDRAM_PAGE_SIZE);
++    dev->dev_sdrampages[1]    = elan4_sdram_alloc (dev, SDRAM_PAGE_SIZE);
++
++    for (tbl = 0; tbl < NUM_HASH_TABLES; tbl++)
++    {
++      dev->dev_hashtable[tbl] = elan4_sdram_alloc (dev, dev->dev_hashsize[tbl] * sizeof (E4_HashTableEntry));
++#ifndef CONFIG_MPSAS
++      /* Initialise hash tables to invalid (zero) */
++      elan4_sdram_zeroq_sdram (dev, dev->dev_hashtable[tbl], dev->dev_hashsize[tbl] * sizeof (E4_HashTableEntry));
++#endif
++    }
++
++    /* Initialise all context filters to discard */
++#ifdef CONFIG_MPSAS
++    if (sas_memset_dev (dev->dev_osdep.pdev, ELAN4_BAR_SDRAM, dev->dev_ctxtable, 
++                      E4_FILTER_DISCARD_ALL, (1 << (dev->dev_ctxtableshift-1))) < 0)
++    {
++      for (i = 0; i < (1 << dev->dev_ctxtableshift); i++)
++          elan4_write_filter (dev, i, E4_FILTER_DISCARD_ALL);
++    }
++#else
++    for (i = 0; i < (1 << dev->dev_ctxtableshift); i++)
++      elan4_write_filter (dev, i, E4_FILTER_DISCARD_ALL);
++#endif
++
++    PRINTF4 (DBG_DEVICE, DBG_CONFIG, "elan4_start_device: hashtables %x,%x, %x,%x\n", dev->dev_hashtable[0], 
++          dev->dev_hashsize[0], dev->dev_hashtable[1], dev->dev_hashsize[1]);
++
++    /* install the hash table pointers */
++    PRINTF0 (DBG_DEVICE, DBG_CONFIG, "elan4_start_device: initialise registers with table addresses\n");
++    write_reg64 (dev, MmuTableBasePtrs, (((E4_uint64) dev->dev_hashtable[0]) | ((E4_uint64) dev->dev_hashtable[1]) << 32));
++    write_reg64 (dev, MmuFaultAndRootCntxPtr, (((E4_uint64) dev->dev_ctxtableshift) | 
++                                             ((E4_uint64) dev->dev_ctxtable) |
++                                             ((E4_uint64) dev->dev_faultarea) << 32));
++    write_reg64 (dev, InputTrapAndFilter, (((E4_uint64) dev->dev_ctxtableshift) | 
++                                         ((E4_uint64) dev->dev_ctxtable) |
++                                         ((E4_uint64) dev->dev_inputtraparea) << 32));
++    /*
++     * The run ptrs have this format: (Front << 32) | Back
++     * The base for both the front and back is uses the high bits of the back pointer.
++     * So writting just the base value is good enough.
++     */
++    write_reg64 (dev, CommandLowPriRunPtrs,  dev->dev_comqlowpri);
++    write_reg64 (dev, CommandHighPriRunPtrs, dev->dev_comqhighpri);
++
++    /* Initialise the run queues */
++    write_reg64 (dev, DProcHighPriPtrs,       E4_QueueValue (dev->dev_dmaqhighpri,    elan4_dmaq_highpri_size));
++    write_reg64 (dev, DProcLowPriPtrs,        E4_QueueValue (dev->dev_dmaqlowpri,     elan4_dmaq_lowpri_size));
++    write_reg64 (dev, TProcHighPriPtrs,       E4_QueueValue (dev->dev_threadqhighpri, elan4_threadq_highpri_size));
++    write_reg64 (dev, TProcLowPriPtrs,        E4_QueueValue (dev->dev_threadqlowpri,  elan4_threadq_lowpri_size));
++
++    /* Initialise the interrupt queue as "empty" - this is actually with one entry on it */
++    write_reg64 (dev, MainIntQueuePtrs.Value, (((E4_uint64) E4_QueueFrontValue (dev->dev_interruptq, elan4_interruptq_size) << 32) |
++                                             ((E4_uint64) E4_QueueBackPointer(dev->dev_interruptq + E4_MainIntEntrySize))));
++    
++    dev->dev_interruptq_nfptr = dev->dev_interruptq + E4_MainIntEntrySize;
++
++    /*
++     * Flush the context filter before dropping the Discard all bits in the schedule status register.
++     * Also hit the SCH_RestartTProc to clear out X's from the trap state and
++     * hit the SCH_RestartDmaPrefetchProc to clear out X's from the prev register.
++     */
++    PULSE_SCHED_RESTART (dev, SCH_ContextFilterFlush | SCH_RestartTProc | SCH_RestartDmaPrefetchProc);
++
++    /* setup the schedule status register. */
++    SET_SCHED_STATUS (dev, SCH_CProcTimeout6p2us | SCH_DProcTimeslice512us);
++
++    /*
++     * Now initialise the inserter cache.s
++     * Bit 31 of the first word of the descriptor is a valid bit. This must be cleared.
++     * Bit 31 becomes a used bit in the descriptors in memory.
++     */
++    for (i = 0; i < COMMAND_INSERTER_CACHE_ENTRIES; i++)
++    {
++      write_reg32 (dev, CommandControl.CommandQueueDescsBase, i);     /* select a cache line */
++      write_reg64 (dev, CommandCacheTestPort, 0);                     /* Mark it invalid */
++    }
++    
++    /* Setup the pointer to the command descriptors */
++    /*   the table must be aligned on a CQ_CommandDescsAlignement boundary */
++    /*   since we've allocated a small table - we work out the offset of the */
++    /*   first entry in our table for mapping in the command ports later */
++    dev->dev_cqoffset = (dev->dev_cqaddr & (CQ_CommandDescsAlignment-1)) / sizeof (E4_CommandQueueDesc);
++
++    write_reg32 (dev, CommandControl.CommandQueueDescsBase, (dev->dev_cqaddr & ~(CQ_CommandDescsAlignment-1)) | COM_ENABLE_DEQUEUE);
++
++    /* allocate the bitmaps for cq,ctxt allocation */
++    KMEM_ZALLOC (dev->dev_cqamap, bitmap_t *, BT_BITOUL(dev->dev_cqcount/ELAN4_CQ_PER_CQA) * sizeof (bitmap_t), 1);
++    KMEM_ZALLOC (dev->dev_ctxmap, bitmap_t *, BT_BITOUL(1 << dev->dev_ctxtableshift) * sizeof (bitmap_t), 1);
++
++    if (dev->dev_cqamap == NULL || dev->dev_ctxmap == NULL)
++      goto failed;
++
++    /* Make every fourth context be invalid for ICache fixup.
++     * context 0 is also invalid - since it is used to indicate 
++     * an invalid tag. */
++    for (i = 0; i < (1 << dev->dev_ctxtableshift); i += 4)
++      BT_SET (dev->dev_ctxmap, i);
++    
++    /* initialise the halt operations */
++    dev->dev_haltop_mask   = 0;
++    dev->dev_haltop_active = 0;
++
++    /* allocate the hash table shadow structures - and place all blocks on the free lists */
++    for (tbl = 0; tbl < NUM_HASH_TABLES; tbl++)
++    {
++      KMEM_ZALLOC (dev->dev_mmuhash[tbl], ELAN4_HASH_ENTRY *,  dev->dev_hashsize[tbl] * sizeof (ELAN4_HASH_ENTRY), 1);
++      KMEM_ZALLOC (dev->dev_mmufree[tbl], ELAN4_HASH_ENTRY **, dev->dev_hashsize[tbl] * sizeof (ELAN4_HASH_ENTRY *), 1);
++
++      if (dev->dev_mmuhash[tbl] == NULL || dev->dev_mmufree[tbl] == NULL)
++          goto failed;
++
++      for (i = 0; i < dev->dev_hashsize[tbl]; i++)
++      {
++          dev->dev_mmuhash[tbl][i].he_entry = dev->dev_hashtable[tbl] + (i * sizeof (E4_HashTableEntry));
++          dev->dev_mmufree[tbl][i]          = &dev->dev_mmuhash[tbl][i];
++      }
++    }
++
++    /* setup the interrupt mask register */
++    SET_INT_MASK (dev, (INT_MSI0 | INT_MSI1 | INT_MSI2 | INT_MSI3) & ~(INT_Discarding | INT_Halted | INT_LinkPortKeyFail));
++
++    /* start a thread to handle excessive main interrupts */
++    if (kernel_thread_create (elan4_mainint_thread, (caddr_t) dev) == NULL)
++      goto failed;
++    dev->dev_mainint_started = 1;
++    
++    /* install the device context - and allocate the first 16 command queues */
++    if (elan4_insertctxt (dev, &dev->dev_ctxt, &device_trap_ops) != 0)
++      goto failed;
++
++    /* Allocate command queues, one for each entry in the inserter cache, 
++     * we'll use these queues to flush the insert cache */
++    for (i = 0; i < COMMAND_INSERTER_CACHE_ENTRIES; i++)
++    {
++      if ((dev->dev_flush_cq[i] = elan4_alloccq (&dev->dev_ctxt, CQ_Size1K, CQ_DmaStartEnableBit | CQ_InterruptEnableBit, 
++                                                 CQ_Priority)) == NULL)
++          goto failed;
++      
++      ASSERT (elan4_cq2num(dev->dev_flush_cq[i]) == i);
++
++      dev->dev_flush_finished |= (1 << i);
++    }
++
++    /* Allocate command queues for dma halt operations */
++    if ((dev->dev_dma_flushop[0].cq = elan4_alloccq (&dev->dev_ctxt, CQ_Size1K, CQ_DmaStartEnableBit, 0)) == NULL ||
++      (dev->dev_dma_flushop[1].cq = elan4_alloccq (&dev->dev_ctxt, CQ_Size1K, CQ_DmaStartEnableBit, CQ_Priority)) == NULL)
++      goto failed;
++
++#ifdef CONFIG_MPSAS
++    elan4_sdram_flushcache (dev, 0, E4_CacheSize);
++#endif
++
++    /* initialise halt operation for flushing the icache */
++    dev->dev_iflush_haltop.op_function = device_iflush_haltop;
++    dev->dev_iflush_haltop.op_arg      = dev;
++    dev->dev_iflush_haltop.op_mask     = INT_TProcHalted;
++
++    /* Allocate a route table, and create a valid route for vp==0, this is used
++     * when a DMA is removed from the dma run queue */
++    if ((dev->dev_routetable = elan4_alloc_routetable (dev, 0)) == NULL)
++      goto failed;
++
++    elan4_set_routetable (&dev->dev_ctxt, dev->dev_routetable);
++
++    entry.Values[0] = FIRST_MYLINK;
++    entry.Values[1] = 0;
++
++    elan4_write_route (dev, dev->dev_routetable, 0, &entry);
++
++    /* map the sdram pages into the elan */
++    dev->dev_tproc_suspend = DEVICE_TPROC_SUSPEND_ADDR;
++    dev->dev_tproc_space   = DEVICE_TPROC_SPACE_ADDR;
++
++    elan4mmu_pteload (&dev->dev_ctxt, 0, dev->dev_tproc_suspend, (dev->dev_sdrampages[0] >> PTE_PADDR_SHIFT) | PTE_SetPerm(PERM_LocExecute));
++    elan4mmu_pteload (&dev->dev_ctxt, 0, dev->dev_tproc_space,   (dev->dev_sdrampages[1] >> PTE_PADDR_SHIFT) | PTE_SetPerm(PERM_LocDataWrite));
++
++    /* and store the thread suspend sequence in it for use when a thread is removed from the run queue */
++    elan4_sdram_writel (dev, dev->dev_sdrampages[0], DEVICE_TPROC_SUSPEND_INSTR);
++
++#ifdef CONFIG_MPSAS
++    do_initdma (dev);
++#endif
++    
++    if (!elan4_neterr_init (dev))
++      goto failed;
++
++    elan4_configure_writecombining (dev);
++
++    /* finally register the device with elanmod for rms */
++    dev->dev_idx = elan_dev_register (&dev->dev_devinfo, &elan4_dev_ops, (void *) dev);
++
++    dev->dev_state = ELAN4_STATE_STARTED;
++
++    return (0);
++
++ failed:
++    printk ("elan%d: failed to start elan4 device - stopping\n", dev->dev_instance);
++
++    elan4_stop_device (dev);
++    return (-ENOMEM);
++}
++
++void
++elan4_stop_device (ELAN4_DEV *dev)
++{
++    unsigned long flags;
++    int i, tbl;
++
++    dev->dev_state = ELAN4_STATE_STOPPING;
++
++    elan_dev_deregister (&dev->dev_devinfo);
++
++    elan4_unconfigure_writecombining (dev);
++
++    elan4_neterr_destroy (dev);
++
++    if (dev->dev_tproc_suspend)
++      elan4mmu_unload_range (&dev->dev_ctxt, 0, dev->dev_tproc_suspend, 1 << dev->dev_pageshift[0]);
++
++    if (dev->dev_tproc_space)
++      elan4mmu_unload_range (&dev->dev_ctxt, 0, dev->dev_tproc_space,   1 << dev->dev_pageshift[0]);
++
++    if (dev->dev_routetable)
++    {
++      elan4_set_routetable (&dev->dev_ctxt, NULL);
++      elan4_free_routetable (dev, dev->dev_routetable);
++    }
++
++    for (i = 0; i < 2; i++)
++      if (dev->dev_dma_flushop[i].cq)
++          elan4_freecq (&dev->dev_ctxt, dev->dev_dma_flushop[i].cq);
++
++    /* free of the device context - and insert cache flushing command queues */
++    for (i = 0; i < COMMAND_INSERTER_CACHE_ENTRIES; i++)
++      if (dev->dev_flush_cq[i])
++          elan4_freecq (&dev->dev_ctxt, dev->dev_flush_cq[i]);
++
++    if (dev->dev_ctxt.ctxt_dev)
++      elan4_removectxt (dev, &dev->dev_ctxt);
++
++    /* stop the mainint thread */
++    spin_lock_irqsave (&dev->dev_mainint_lock, flags);
++    dev->dev_stop_threads = 1;
++
++    while (dev->dev_mainint_started && !dev->dev_mainint_stopped)
++    {
++      kcondvar_wakeupall (&dev->dev_mainint_wait, &dev->dev_mainint_lock);
++      kcondvar_wait (&dev->dev_mainint_wait, &dev->dev_mainint_lock, &flags);
++    }
++    dev->dev_mainint_started = dev->dev_mainint_stopped = 0;
++    spin_unlock_irqrestore (&dev->dev_mainint_lock, flags);
++
++    /* cancel any error interrupt timeouts */
++    if (timer_fn_queued (&dev->dev_error_timeoutid))
++      cancel_timer_fn (&dev->dev_error_timeoutid);
++
++    if (dev->dev_devinfo.dev_revision_id != PCI_REVISION_ID_ELAN4_REVA && timer_fn_queued (&dev->dev_linkerr_timeoutid))
++      cancel_timer_fn (&dev->dev_linkerr_timeoutid);
++    
++    /* reset the interrupt mask register to zero */
++    if (dev->dev_regs)
++      SET_INT_MASK (dev, 0);
++
++    for (tbl = 0; tbl < NUM_HASH_TABLES; tbl++)
++    {
++      if (dev->dev_mmuhash[tbl])
++          KMEM_FREE (dev->dev_mmuhash[tbl], dev->dev_hashsize[tbl] * sizeof (ELAN4_HASH_ENTRY));
++      if (dev->dev_mmufree[tbl])
++          KMEM_FREE (dev->dev_mmufree[tbl], dev->dev_hashsize[tbl] * sizeof (ELAN4_HASH_ENTRY *));
++      if (dev->dev_hashtable[tbl])
++          elan4_sdram_free (dev, dev->dev_hashtable[tbl], dev->dev_hashsize[tbl] * sizeof (E4_HashTableEntry));
++    }
++
++    if (dev->dev_cqamap)
++      KMEM_FREE (dev->dev_cqamap, BT_BITOUL (dev->dev_cqcount/ELAN4_CQ_PER_CQA) * sizeof (bitmap_t));
++    if (dev->dev_ctxmap)
++      KMEM_FREE (dev->dev_ctxmap, BT_BITOUL(1 << dev->dev_ctxtableshift) * sizeof (bitmap_t));
++
++    if (dev->dev_comqlowpri)
++      elan4_sdram_free (dev, dev->dev_comqlowpri,     (1 << COMMAND_RUN_QUEUE_BITS));
++    if (dev->dev_comqhighpri)
++      elan4_sdram_free (dev, dev->dev_comqhighpri,    (1 << COMMAND_RUN_QUEUE_BITS));
++    if (dev->dev_cqaddr)
++      elan4_sdram_free (dev, dev->dev_cqaddr,         sizeof (E4_CommandQueueDesc) * dev->dev_cqcount);
++    if (dev->dev_dmaqhighpri)
++      elan4_sdram_free (dev, dev->dev_dmaqhighpri,    E4_QueueSize(elan4_dmaq_highpri_size));
++    if (dev->dev_dmaqlowpri)
++      elan4_sdram_free (dev, dev->dev_dmaqlowpri,     E4_QueueSize(elan4_dmaq_lowpri_size));
++    if (dev->dev_threadqhighpri)
++      elan4_sdram_free (dev, dev->dev_threadqhighpri, E4_QueueSize(elan4_threadq_highpri_size));
++    if (dev->dev_threadqlowpri)
++      elan4_sdram_free (dev, dev->dev_threadqlowpri,  E4_QueueSize(elan4_threadq_lowpri_size));
++    if (dev->dev_interruptq)
++      elan4_sdram_free (dev, dev->dev_interruptq,     E4_QueueSize(elan4_interruptq_size));
++    
++    if (dev->dev_ctxtable)
++      elan4_sdram_free (dev, dev->dev_ctxtable,       (1 << dev->dev_ctxtableshift) * sizeof (E4_ContextControlBlock));
++    if (dev->dev_faultarea)
++      elan4_sdram_free (dev, dev->dev_faultarea,      CUN_Entries * sizeof (E4_FaultSave));
++    if (dev->dev_inputtraparea)
++      elan4_sdram_free (dev, dev->dev_inputtraparea,  sizeof (E4_IprocTrapState));
++
++    if (dev->dev_sdrampages[0])
++      elan4_sdram_free (dev, dev->dev_sdrampages[0],  SDRAM_PAGE_SIZE);
++    if (dev->dev_sdrampages[1])
++      elan4_sdram_free (dev, dev->dev_sdrampages[1],  SDRAM_PAGE_SIZE);
++
++    for (i = 0; i < dev->dev_sdram_numbanks; i++)
++      if (dev->dev_sdram_banks[i].b_ioaddr)
++              elan4_sdram_fini_bank (dev, &dev->dev_sdram_banks[i]);
++
++    elan4_pcifini (dev);
++
++    dev->dev_state = ELAN4_STATE_STOPPED;
++
++    if (dev->dev_ack_errors)
++        kfree(dev->dev_ack_errors);
++    if (dev->dev_dproc_timeout)
++        kfree(dev->dev_dproc_timeout);
++    if (dev->dev_cproc_timeout)
++        kfree(dev->dev_cproc_timeout);
++}
++
++static __inline__ int
++compute_arity (int lvl, unsigned n, char *arity)
++{
++    if (arity[lvl] == 0)
++    {
++      if (n <= 8)
++          arity[lvl] = n;
++      else
++          arity[lvl] = 4;
++    }
++
++    return (arity[lvl]);
++}
++
++int
++elan4_compute_position (ELAN_POSITION *pos, unsigned nodeid, unsigned numnodes, unsigned arityval)
++{
++    int i, lvl, n;
++    char arity[ELAN_MAX_LEVELS];
++
++    if (nodeid >= numnodes)
++      return -EINVAL;
++
++    for (i = 0; i < ELAN_MAX_LEVELS; i++, arityval >>= 4)
++      arity[i] = arityval & 7;
++    
++    for (lvl = 0, n = numnodes; n > compute_arity(lvl, n, arity) && lvl < ELAN_MAX_LEVELS; lvl++)
++    {
++      if ((n % arity[lvl]) != 0)
++          return -EINVAL;
++      
++      n /= arity[lvl];
++    }
++
++    if (arity[lvl] != n)
++      return -EINVAL;
++
++    for (i = 0; i <= lvl; i++)
++      pos->pos_arity[i] = arity[lvl - i];
++
++    pos->pos_nodes  = numnodes;
++    pos->pos_levels = lvl + 1;
++    pos->pos_nodeid = nodeid;
++    pos->pos_mode   = ELAN_POS_MODE_SWITCHED;
++
++    return 0;
++}
++
++int
++elan4_get_position (ELAN4_DEV *dev, ELAN_POSITION *pos)
++{
++    kmutex_lock (&dev->dev_lock);
++    *pos = dev->dev_position;
++    kmutex_unlock (&dev->dev_lock);
++
++    return (pos->pos_mode);
++}
++
++int
++elan4_set_position (ELAN4_DEV *dev, ELAN_POSITION *pos)
++{
++    int forceLocal = 0;
++    int nnodes, i;
++    unsigned int *ack_errors;
++    unsigned int *dproc_timeout;
++    unsigned int *cproc_timeout;
++
++    switch (pos->pos_mode)
++    {
++    case ELAN_POS_UNKNOWN:
++      break;
++      
++    case ELAN_POS_MODE_SWITCHED:
++      if (pos->pos_levels > ELAN_MAX_LEVELS)
++          return (-EINVAL);
++      
++      for (i = 0, nnodes = 1; i < pos->pos_levels; i++)
++      {
++
++          if (pos->pos_arity[i] <= 0 || (i == 0 ? pos->pos_arity[i] > 8 : pos->pos_arity[i] >= 8))  /* allow an 8 way top-switch */
++              return (-EINVAL);
++          
++          nnodes *= pos->pos_arity[i];
++      }
++
++      if (pos->pos_nodes > nnodes || pos->pos_nodeid >= pos->pos_nodes)
++          return (-EINVAL);
++      break;
++      
++    case ELAN_POS_MODE_LOOPBACK:
++      if (pos->pos_levels != 1 || pos->pos_nodes != 1 || pos->pos_nodeid != 0 || pos->pos_arity[0] != 1)
++          return (-EINVAL);
++
++      forceLocal = 1;
++      break;
++
++    case ELAN_POS_MODE_BACKTOBACK:
++      if (pos->pos_levels != 1 || pos->pos_nodes != 2 || pos->pos_nodeid >= 2 || pos->pos_arity[0] != 2)
++          return (-EINVAL);
++
++      forceLocal = (pos->pos_nodeid == 0);
++      break;
++
++    default:
++      return (-EINVAL);
++    }
++
++    ack_errors = kmalloc(pos->pos_nodes * sizeof(unsigned int), GFP_KERNEL);
++    if (!ack_errors)
++      return (-EINVAL);
++    memset(ack_errors, 0, pos->pos_nodes * sizeof(unsigned int));
++    dproc_timeout = kmalloc(pos->pos_nodes * sizeof(unsigned int), GFP_KERNEL);
++    if (!dproc_timeout) 
++    {
++        kfree(ack_errors);
++        return (-EINVAL);
++    }
++    memset(dproc_timeout, 0, pos->pos_nodes * sizeof(unsigned int));
++    cproc_timeout = kmalloc(pos->pos_nodes * sizeof(unsigned int), GFP_KERNEL);
++    if (!cproc_timeout)
++    {
++        kfree(ack_errors);
++        kfree(dproc_timeout);
++        return (-EINVAL);
++    }
++    memset(cproc_timeout, 0, pos->pos_nodes * sizeof(unsigned int));
++      
++    kmutex_lock (&dev->dev_lock);
++    dev->dev_position = *pos;
++    dev->dev_ack_errors = ack_errors;
++    dev->dev_dproc_timeout = dproc_timeout;
++    dev->dev_cproc_timeout = cproc_timeout;
++
++    if (forceLocal)
++      write_reg32 (dev, LinkContSettings, read_reg32 (dev, LinkContSettings) | LCONT_FORCE_COMMSCLK_LOCAL);
++    else
++      write_reg32 (dev, LinkContSettings, read_reg32 (dev, LinkContSettings) & ~LCONT_FORCE_COMMSCLK_LOCAL);
++
++    pioflush_reg (dev);
++    kmutex_unlock (&dev->dev_lock);
++
++    return (0);
++}
++
++void
++elan4_get_params (ELAN4_DEV *dev, ELAN_PARAMS *params, unsigned short *mask)
++{
++    kmutex_lock (&dev->dev_lock);
++
++    *mask = dev->dev_devinfo.dev_params_mask;
++    memcpy (params, &dev->dev_devinfo.dev_params, sizeof (ELAN_PARAMS));
++    
++    kmutex_unlock (&dev->dev_lock);
++}
++
++void
++elan4_set_params (ELAN4_DEV *dev, ELAN_PARAMS *params, unsigned short mask)
++{     
++    int i;
++
++    kmutex_lock (&dev->dev_lock);
++    for (i = 0; i < ELAN4_PARAM_COUNT; i++)
++      if (mask & (1 << i))
++          dev->dev_devinfo.dev_params.values[i] = params->values[i];
++    
++    dev->dev_devinfo.dev_params_mask |= mask;
++    kmutex_unlock (&dev->dev_lock);
++}
++
++
++EXPORT_SYMBOL(elan4_get_position);
++EXPORT_SYMBOL(elan4_set_position);
++
++EXPORT_SYMBOL(elan4_queue_haltop);
++EXPORT_SYMBOL(elan4_queue_dma_flushop);
++EXPORT_SYMBOL(elan4_queue_mainintop);
++
++EXPORT_SYMBOL(elan4_insertctxt);
++EXPORT_SYMBOL(elan4_removectxt);
++
++EXPORT_SYMBOL(elan4_attach_filter);
++EXPORT_SYMBOL(elan4_detach_filter);
++EXPORT_SYMBOL(elan4_set_filter);
++EXPORT_SYMBOL(elan4_set_routetable);
++
++EXPORT_SYMBOL(elan4_alloccq);
++EXPORT_SYMBOL(elan4_freecq);
++EXPORT_SYMBOL(elan4_restartcq);
++
++EXPORT_SYMBOL(elan4_flush_icache);
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan4/device_Linux.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan4/device_Linux.c    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan4/device_Linux.c 2005-05-11 12:10:12.450930752 -0400
+@@ -0,0 +1,2760 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: device_Linux.c,v 1.74.6.20 2005/03/10 11:30:01 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/device_Linux.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <qsnet/kthread.h>
++#include <qsnet/kpte.h>
++
++#include <asm/io.h>
++#include <asm/irq.h>
++#ifdef CONFIG_MTRR
++#include <asm/mtrr.h>
++#endif
++
++#include <linux/init.h>
++#include <linux/pci.h>
++#include <linux/module.h>
++#include <linux/reboot.h>
++#include <linux/notifier.h>
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
++#include <linux/wrapper.h>
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,23)
++typedef void irqreturn_t;
++#endif
++#       define IRQ_NONE
++#       define IRQ_HANDLED
++#endif
++
++#include <elan4/debug.h>
++#include <elan4/device.h>
++#include <elan4/user.h>
++#include <elan4/ioctl.h>
++#include <elan4/intcookie.h>
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
++#error please use a 2.4.0 series kernel or newer
++#endif
++
++
++#if defined(LINUX_SPARC) || defined(LINUX_PPC64)
++#define __io_remap_page_range(from,offset,size,prot)  remap_page_range(from,offset,size,prot)
++#define __remap_page_range(from,offset,size,prot)     remap_page_range(from,offset,size,prot)
++#elif defined(NO_RMAP)
++#define __io_remap_page_range(from,offset,size,prot)  io_remap_page_range(from,offset,size,prot)
++#define __remap_page_range(from,offset,size,prot)     remap_page_range(from,offset,size,prot)
++#else
++#define __io_remap_page_range(from,offset,size,prot)  io_remap_page_range(vma,from,offset,size,prot)
++#define __remap_page_range(from,offset,size,prot)     remap_page_range(vma,from,offset,size,prot)
++#endif
++
++static unsigned int pat_pteval = -1;
++
++#ifndef pgprot_noncached
++static inline pgprot_t pgprot_noncached(pgprot_t _prot)
++{
++      unsigned long prot = pgprot_val(_prot);
++#if defined(__powerpc__)
++      prot |= _PAGE_NO_CACHE | _PAGE_GUARDED;
++#elif defined(__sparc__)
++      prot &= ~(_PAGE_CACHE);
++      prot |= _PAGE_IE;
++#endif
++
++      return __pgprot(prot);
++}
++#endif
++
++#ifndef pgprot_writecombine
++static inline pgprot_t pgprot_writecombine (pgprot_t _prot)
++{
++    unsigned long prot = pgprot_val(_prot);
++
++    if (pat_pteval != -1)
++      prot = (prot & ~(_PAGE_PCD | _PAGE_PWT | _PAGE_PSE)) | pat_pteval;
++
++    return __pgprot (prot);
++}
++#endif
++
++#define ELAN4_DRIVER_VERSION          0x103           /* 16 bit value */
++
++/*
++ * Function prototypes.
++ */
++static int       elan4_attach_device (int instance, struct pci_dev *pdev);
++static void      elan4_detach_device (ELAN4_DEV *dev);
++
++static int       elan4_open (struct inode *inode, struct file *file);
++static int       elan4_release(struct inode *inode, struct file *file);
++static int       elan4_ioctl (struct inode *inode, struct file *file, 
++                              unsigned int cmd, unsigned long arg);
++static int       elan4_mmap (struct file *file, struct vm_area_struct *vm_area);
++
++static irqreturn_t elan4_irq (int irq, void *arg, struct pt_regs *regs);
++
++static void        elan4_shutdown_devices(int panicing);
++
++static int      disabled;                                     /* bitmask of which devices not to start */
++unsigned int  elan4_pll_cfg      = 0;
++int           elan4_pll_div      = 31;                        /* RevC PCB */
++int           elan4_mod45disable = 0;
++static int      optimise_pci_bus   = 1;                               /* 0 => don't, 1 => if ok, 2 => always */
++static int      default_features   = 0;                               /* default values for dev_devinfo.dev_params.values[ELAN4_PARAM_DRIVER_FEATURES] */
++
++long long       sdram_cfg = SDRAM_STARTUP_VALUE;
++static int      sdram_cfg_lo;
++static int    sdram_cfg_hi;
++int           sdram_bank_limit;
++
++MODULE_AUTHOR("Quadrics Ltd.");
++MODULE_DESCRIPTION("Elan 4 Device Driver");
++MODULE_LICENSE("GPL");
++
++MODULE_PARM(elan4_debug, "i");
++MODULE_PARM(elan4_debug_toconsole, "i");
++MODULE_PARM(elan4_debug_tobuffer, "i");
++MODULE_PARM(elan4_debug_mmu, "i");
++MODULE_PARM(elan4_pll_cfg, "i");
++MODULE_PARM(elan4_pll_div, "i");
++MODULE_PARM(elan4_mod45disable, "i");
++MODULE_PARM(optimise_pci_bus, "i");
++MODULE_PARM(default_features, "i");
++
++MODULE_PARM(disabled, "i");
++MODULE_PARM(sdram_cfg_lo, "i");
++MODULE_PARM(sdram_cfg_hi, "i");
++MODULE_PARM(sdram_bank_limit, "i");
++
++MODULE_PARM(elan4_hash_0_size_val, "i");
++MODULE_PARM(elan4_hash_1_size_val, "i");
++MODULE_PARM(elan4_ctxt_table_shift, "i");
++MODULE_PARM(elan4_ln2_max_cqs, "i");
++MODULE_PARM(elan4_dmaq_highpri_size, "i");
++MODULE_PARM(elan4_threadq_highpri_size, "i");
++MODULE_PARM(elan4_dmaq_lowpri_size, "i");
++MODULE_PARM(elan4_threadq_lowpri_size, "i");
++MODULE_PARM(elan4_interruptq_size, "i");
++
++MODULE_PARM(elan4_mainint_punt_loops, "i");
++MODULE_PARM(elan4_mainint_resched_ticks, "i");
++MODULE_PARM(elan4_linkport_lock, "i");
++MODULE_PARM(elan4_eccerr_recheck, "i");
++
++MODULE_PARM(user_p2p_route_options, "i");
++MODULE_PARM(user_bcast_route_options, "i");
++MODULE_PARM(user_dproc_retry_count, "i");
++MODULE_PARM(user_cproc_retry_count, "i");
++
++/*
++ * Standard device entry points.
++ */
++static struct file_operations elan4_fops = {
++    ioctl:   elan4_ioctl,
++    mmap:    elan4_mmap,
++    open:    elan4_open,
++    release: elan4_release,
++};
++
++ELAN4_DEV *elan4_devices[ELAN4_MAX_CONTROLLER];
++
++#if defined(CONFIG_DEVFS_FS)
++static devfs_handle_t devfs_handle;
++#endif
++
++
++#if defined(CONFIG_PPC64) || defined(CONFIG_SPARC64) || defined(CONFIG_X86_64)
++static int
++elan4_ioctl32_cmds[] =
++{     /* /dev/elan/control */
++    ELAN4IO_DEVINFO,
++    ELAN4IO_GET_POSITION,
++    ELAN4IO_SET_POSITION,
++    ELAN4IO_GET_PARAMS,
++    ELAN4IO_SET_PARAMS,
++
++    /* /dev/elan4/user */
++    ELAN4IO_POSITION,
++    ELAN4IO_FREE,
++    ELAN4IO_ATTACH,
++    ELAN4IO_DETACH,
++    ELAN4IO_BLOCK_INPUTTER,
++
++    ELAN4IO_ADD_P2PVP,
++    ELAN4IO_ADD_BCASTVP,
++    ELAN4IO_REMOVEVP,
++    ELAN4IO_SET_ROUTE,
++    ELAN4IO_RESET_ROUTE,
++    ELAN4IO_GET_ROUTE,
++    ELAN4IO_CHECK_ROUTE,
++
++    ELAN4IO_ALLOCCQ,
++    ELAN4IO_FREECQ,
++    ELAN4IO_SETPERM32,
++    ELAN4IO_CLRPERM32,
++    ELAN4IO_TRAPSIG,
++    ELAN4IO_TRAPHANDLER32,
++    ELAN4IO_REQUIRED_MAPPINGS,
++      
++    ELAN4IO_RESUME_EPROC_TRAP,
++    ELAN4IO_RESUME_CPROC_TRAP,
++    ELAN4IO_RESUME_DPROC_TRAP,
++    ELAN4IO_RESUME_TPROC_TRAP,
++    ELAN4IO_RESUME_IPROC_TRAP,
++
++    ELAN4IO_FLUSH_ICACHE,
++
++    ELAN4IO_STOP_CTXT,
++
++    ELAN4IO_ALLOC_INTCOOKIE,
++    ELAN4IO_FREE_INTCOOKIE,
++    ELAN4IO_ARM_INTCOOKIE,
++    ELAN4IO_WAIT_INTCOOKIE,
++
++    ELAN4IO_ALLOC_TRAP_QUEUES,
++    ELAN4IO_NETERR_MSG,
++    ELAN4IO_NETERR_TIMER,
++    ELAN4IO_NETERR_FIXUP,
++
++    ELAN4IO_DUMPCQ32,
++};
++
++static int      elan4_ioctl32 (unsigned int fd, unsigned int cmd, 
++                             unsigned long arg, struct file *file);
++#endif
++
++/*
++ * Standard device entry points.
++ */
++#if defined(CONFIG_DUMP) || defined(CONFIG_DUMP_MODULE)
++
++#include <linux/dump.h>
++
++static int
++elan4_dump_event (struct notifier_block *self, unsigned long event, void *buffer)
++{
++    if (event == DUMP_BEGIN)
++      elan4_shutdown_devices (FALSE);
++
++    return (NOTIFY_DONE);
++}
++static struct notifier_block elan4_dump_notifier = 
++{
++    notifier_call:    elan4_dump_event,
++    priority:         0,
++};
++
++#endif
++
++static int
++elan4_reboot_event (struct notifier_block *self, unsigned long event, void *buffer)
++{
++    if ((event == SYS_RESTART || event == SYS_HALT || event == SYS_POWER_OFF))
++      elan4_shutdown_devices (0);
++
++    return (NOTIFY_DONE);
++}
++
++static struct notifier_block elan4_reboot_notifier = 
++{
++    notifier_call:    elan4_reboot_event,
++    priority:         0,
++};
++
++static int
++elan4_panic_event (struct notifier_block *self, unsigned long event, void *buffer)
++{
++    elan4_shutdown_devices (1);
++
++    return (NOTIFY_DONE);
++}
++
++static struct notifier_block elan4_panic_notifier = 
++{
++    notifier_call:    elan4_panic_event,
++    priority:         0,
++};
++
++static int __init
++elan4_init (void)
++{
++    int             err;
++    struct pci_dev *pdev;
++    int                   count;
++#if defined(__ia64)
++    int             seenRevA = 0;
++#endif
++    
++    if ((err = register_chrdev (ELAN4_MAJOR, ELAN4_NAME, &elan4_fops)) < 0)
++      return (err);
++
++#if defined(CONFIG_DEVFS_FS)
++    devfs_handle = devfs_mk_dir (NULL, "elan4", NULL);
++#endif
++
++    intcookie_init();
++    elan4_debug_init();
++    elan4_procfs_init();
++    
++#ifdef CONFIG_MPSAS
++    sas_init();
++#endif
++
++    if (sdram_cfg_lo != 0 && sdram_cfg_hi != 0)
++      sdram_cfg = (((unsigned long long) sdram_cfg_hi) << 32) | ((unsigned long long) sdram_cfg_lo);
++
++    for (count = 0, pdev = NULL; (pdev = pci_find_device(PCI_VENDOR_ID_QUADRICS, PCI_DEVICE_ID_ELAN4, pdev)) != NULL ; count++)
++    {
++#if defined(__ia64)
++      unsigned char revid;
++      
++      pci_read_config_byte (pdev, PCI_REVISION_ID, &revid);
++
++      if (revid == PCI_REVISION_ID_ELAN4_REVA && seenRevA++ != 0 && pci_find_device (PCI_VENDOR_ID_HP, 0x122e, NULL))
++      {
++          printk ("elan: only a single elan4a supported on rx2600\n");
++          continue;
++      }
++#endif
++
++      if (count < ELAN4_MAX_CONTROLLER)
++          elan4_attach_device (count, pdev);
++    }
++
++    if (count >= ELAN4_MAX_CONTROLLER)
++      printk ("elan: found %d elan4 devices - only support %d\n", count, ELAN4_MAX_CONTROLLER);
++
++#if defined(CONFIG_PPC64) || defined(CONFIG_SPARC64) || defined(CONFIG_X86_64)
++    lock_kernel();
++    {
++      extern int register_ioctl32_conversion(unsigned int cmd, int (*handler)(unsigned int, unsigned int, unsigned long, struct file *));
++      register int i;
++      for (i = 0; i < sizeof (elan4_ioctl32_cmds)/sizeof(elan4_ioctl32_cmds[0]); i++)
++          register_ioctl32_conversion (elan4_ioctl32_cmds[i], elan4_ioctl32);
++    }
++    unlock_kernel();
++#endif
++
++#if defined(CONFIG_DUMP) || defined(CONFIG_DUMP_MODULE)
++    register_dump_notifier (&elan4_dump_notifier);
++#endif
++    register_reboot_notifier (&elan4_reboot_notifier);
++
++#if !defined(NO_PANIC_NOTIFIER)
++    notifier_chain_register (&panic_notifier_list, &elan4_panic_notifier);
++#endif
++
++    return (0);
++}
++
++#ifdef MODULE
++static void __exit
++elan4_exit (void)
++{
++    int i;
++
++#if defined(CONFIG_PPC64) || defined(CONFIG_SPARC64) || defined(CONFIG_X86_64)
++    lock_kernel();
++    {
++      extern void unregister_ioctl32_conversion(unsigned int cmd);
++
++      for (i = 0; i < sizeof (elan4_ioctl32_cmds)/sizeof(elan4_ioctl32_cmds[0]); i++)
++          unregister_ioctl32_conversion (elan4_ioctl32_cmds[i]);
++    }
++    unlock_kernel();
++#endif
++
++#if defined(CONFIG_DUMP) || defined(CONFIG_DUMP_MODULE)
++    unregister_dump_notifier (&elan4_dump_notifier);
++#endif
++    unregister_reboot_notifier (&elan4_reboot_notifier);
++
++#if !defined(NO_PANIC_NOTIFIER)
++    notifier_chain_unregister (&panic_notifier_list, &elan4_panic_notifier);
++#endif
++
++    for (i = 0; i < ELAN4_MAX_CONTROLLER; i++)
++      if (elan4_devices[i] != NULL)
++          elan4_detach_device (elan4_devices[i]);
++    
++    elan4_procfs_fini();
++    elan4_debug_fini();
++    intcookie_fini();
++
++#if defined(CONFIG_DEVFS_FS)
++    devfs_unregister (devfs_handle);
++#endif
++
++    unregister_chrdev(ELAN4_MAJOR, ELAN4_NAME);
++}
++
++module_init (elan4_init);
++module_exit (elan4_exit);
++
++#else
++__initcall (elan4_init);
++#endif
++
++/*
++ * Minor numbers encoded as :
++ *   [5:0]    device number
++ *   [15:6]   function number
++ */
++#define ELAN4_DEVICE_MASK     0x3F
++#define ELAN4_DEVICE(inode)   (MINOR((inode)->i_rdev) & ELAN4_DEVICE_MASK)
++
++#define ELAN4_MINOR_CONTROL   0
++#define ELAN4_MINOR_MEM               1
++#define ELAN4_MINOR_USER      2
++
++#define ELAN4_MINOR_SHIFT     6
++#define ELAN4_MINOR(inode)    (MINOR((inode)->i_rdev) >> ELAN4_MINOR_SHIFT)
++
++/*
++ * Called by init_module() for each card discovered on PCI.
++ */
++static int
++elan4_attach_device (int instance, struct pci_dev *pdev)
++{
++    ELAN4_DEV *dev;
++    int res;
++
++    KMEM_ALLOC (dev, ELAN4_DEV *, sizeof (ELAN4_DEV), 1);
++    if ((dev == NULL))
++      return (-ENOMEM);
++    memset (dev, 0, sizeof (ELAN4_DEV));
++
++    /* setup os dependent section of ELAN4_DEV */
++    dev->dev_instance   = instance;
++    dev->dev_osdep.pdev = pdev;
++
++    /* initialise the devinfo */
++    pci_read_config_word (dev->dev_osdep.pdev, PCI_VENDOR_ID,   &dev->dev_devinfo.dev_vendor_id);
++    pci_read_config_word (dev->dev_osdep.pdev, PCI_DEVICE_ID,   &dev->dev_devinfo.dev_device_id);
++    pci_read_config_byte (dev->dev_osdep.pdev, PCI_REVISION_ID, &dev->dev_devinfo.dev_revision_id);
++
++    dev->dev_devinfo.dev_rail                                        = instance;
++    dev->dev_devinfo.dev_driver_version                              = ELAN4_DRIVER_VERSION;
++    dev->dev_devinfo.dev_num_down_links_value                        = 0;
++    dev->dev_devinfo.dev_params_mask                                 = (1 << ELAN4_PARAM_DRIVER_FEATURES);
++    dev->dev_devinfo.dev_params.values[ELAN4_PARAM_DRIVER_FEATURES]  = default_features;
++
++    dev->dev_position.pos_mode = ELAN_POS_UNKNOWN;
++
++    /* initialise the data structures and map the device */
++    if ((res = elan4_initialise_device (dev)) != 0)
++    {
++      kfree (dev);
++      return res;
++    }
++
++    /* add the interrupt handler */
++    if (request_irq (pdev->irq, elan4_irq, SA_SHIRQ, "elan4", dev) != 0)
++    {
++      elan4_finalise_device (dev);
++      KMEM_FREE (dev, sizeof(*dev));
++      return -ENXIO;
++    }
++
++    if (pci_request_regions(dev->dev_osdep.pdev, "elan4"))
++    {
++      free_irq (dev->dev_osdep.pdev->irq, dev);
++      KMEM_FREE (dev, sizeof(*dev));
++      return -ENODEV;
++    }
++
++#if defined(CONFIG_DEVFS_FS)
++    {
++      char name[16];
++      
++      sprintf (name, "control%d", dev->dev_instance);
++      dev->dev_osdep.devfs_control = devfs_register(devfs_handle, name, DEVFS_FL_NONE, ELAN4_MAJOR,
++                                                    dev->dev_instance | (ELAN4_MINOR_CONTROL << ELAN4_MINOR_SHIFT), S_IFCHR | S_IRUSR | S_IWUSR, 
++                                                    &elan4_fops, NULL);
++      sprintf (name, "sdram%d", dev->dev_instance);
++      dev->dev_osdep.devfs_sdram =  devfs_register(devfs_handle, name, DEVFS_FL_NONE, ELAN4_MAJOR,
++                                                   dev->dev_instance | (ELAN4_MINOR_MEM << ELAN4_MINOR_SHIFT), S_IFCHR | S_IRUSR|S_IWUSR | S_IRGRP|S_IWGRP | S_IROTH|S_IWOTH,
++                                                   &elan4_fops, NULL);
++      sprintf (name, "user%d", dev->dev_instance);
++      dev->dev_osdep.devfs_user =  devfs_register(devfs_handle, name, DEVFS_FL_NONE, ELAN4_MAJOR,
++                                                  dev->dev_instance | (ELAN4_MINOR_USER << ELAN4_MINOR_SHIFT), S_IFCHR | S_IRUSR|S_IWUSR | S_IRGRP|S_IWGRP | S_IROTH|S_IWOTH,
++                                                  &elan4_fops, NULL);
++    }
++#endif
++
++    /* add the procfs entry */
++    elan4_procfs_device_init (dev);
++
++    /* allow the device to be referenced now */
++    elan4_devices[instance] = dev;
++
++    if ((disabled & (1 << instance)) == 0)
++    {
++      if (elan4_start_device (dev) != 0)
++      {
++          printk ("elan%d: auto-start of device failed\n", dev->dev_instance);
++
++          elan4_detach_device (dev);
++          return (-ENXIO);
++      }
++      
++      dev->dev_state = ELAN4_STATE_STARTED;
++    }
++
++#if defined (__sparc)
++    printk ("elan%d: at pci %s (irq = %s)\n", instance, pdev->slot_name, __irq_itoa(pdev->irq));
++#else
++    printk ("elan%d: at pci %s (irq = %d)\n", instance, pdev->slot_name, pdev->irq);
++#endif
++
++    return (0);
++}
++
++/*
++ * Called by cleanup_module() for each board found on PCI.
++ */
++static void
++elan4_detach_device (ELAN4_DEV *dev)
++{
++    /* stop the chip and free of resources */
++    if (dev->dev_state == ELAN4_STATE_STARTED)
++      elan4_stop_device (dev);
++    
++    elan4_devices[dev->dev_instance] = NULL;
++
++#if defined(CONFIG_DEVFS_FS)
++    devfs_unregister (dev->dev_osdep.devfs_control);
++    devfs_unregister (dev->dev_osdep.devfs_sdram);
++    devfs_unregister (dev->dev_osdep.devfs_user);
++#endif
++
++    /* release the address space */
++    pci_release_regions (dev->dev_osdep.pdev);
++
++    /* release the interrupt */
++    free_irq (dev->dev_osdep.pdev->irq, dev);
++
++    /* remove the procfs entry */
++    elan4_procfs_device_fini (dev);
++
++    /* unmap the device and finalise the data structures */
++    elan4_finalise_device (dev);
++    
++    KMEM_FREE (dev, sizeof(*dev));
++}
++
++/*
++ * Maintain reference counts on the device
++ */
++ELAN4_DEV *
++elan4_reference_device (int instance, int state)
++{
++    ELAN4_DEV *dev = elan4_devices[instance];
++
++    if (dev == NULL)
++      return (NULL);
++
++    kmutex_lock (&dev->dev_lock);
++
++    if ((dev->dev_state & state) == 0)
++    {
++      kmutex_unlock (&dev->dev_lock);
++      return (NULL);
++    }
++
++    dev->dev_references++;
++    kmutex_unlock (&dev->dev_lock);
++
++#ifdef MODULE
++    MOD_INC_USE_COUNT;
++#endif
++
++#ifdef CONFIG_MPSAS
++    sas_set_position(dev);
++#endif
++
++    return (dev);
++}
++
++void
++elan4_dereference_device (ELAN4_DEV *dev)
++{
++    kmutex_lock (&dev->dev_lock);
++    dev->dev_references--;
++    kmutex_unlock (&dev->dev_lock);
++
++#ifdef MODULE
++    MOD_DEC_USE_COUNT;
++#endif
++}
++
++static void
++elan4_shutdown_devices(int panicing)
++{
++    ELAN4_DEV *dev;
++    unsigned long flags;
++    register int i;
++
++    local_irq_save (flags);
++    for (i = 0; i < ELAN4_MAX_CONTROLLER; i++)
++    {
++      if ((dev = elan4_devices[i]) != NULL)
++      {
++          printk(KERN_INFO "elan%d: forcing link into reset\n", dev->dev_instance);
++
++          /* set the inputters to discard everything */
++          if (! panicing) spin_lock (&dev->dev_haltop_lock);
++
++          if (dev->dev_discard_lowpri_count++ == 0)
++              elan4_set_schedstatus (dev, 0);
++          if (dev->dev_discard_highpri_count++ == 0)
++              elan4_set_schedstatus (dev, 0);
++
++          if (! panicing) spin_unlock (&dev->dev_haltop_lock);
++
++          /* ideally we'd like to halt all the outputters too,
++           * however this will prevent the kernel comms flushing
++           * to work correctly .....
++           */
++      }
++    }
++    local_irq_restore (flags);
++}
++
++/*
++ * /dev/elan4/controlX - control device
++ *
++ */
++static int
++control_open (struct inode *inode, struct file *file)
++{
++    ELAN4_DEV       *dev = elan4_reference_device (ELAN4_DEVICE(inode), ELAN4_STATE_STOPPED | ELAN4_STATE_STARTED);
++    CONTROL_PRIVATE *pr;
++    
++    if (dev == NULL)
++      return (-ENXIO);
++    
++    KMEM_ALLOC (pr, CONTROL_PRIVATE *, sizeof (CONTROL_PRIVATE), 1);
++    if ((pr == NULL))
++    {
++      elan4_dereference_device (dev);
++      
++      return (-ENOMEM);
++    }
++
++    PRINTF (DBG_USER, DBG_FILE, "control_open: dev=%p pr=%p\n", dev, pr);
++
++    pr->pr_dev           = dev;
++    pr->pr_boundary_scan = 0;
++
++    file->private_data = (void *) pr;
++
++    return (0);
++}
++
++static int
++control_release (struct inode *inode, struct file *file)
++{
++    CONTROL_PRIVATE *pr  = (CONTROL_PRIVATE *) file->private_data;
++    ELAN4_DEV       *dev = pr->pr_dev;
++
++    PRINTF (DBG_DEVICE, DBG_FILE, "control_release: pr=%p\n", pr);
++
++    //if (pr->pr_boundary_scan)
++    //    elan4_clear_boundary_scan (dev, pr);
++
++    elan4_dereference_device (dev);
++
++    KMEM_FREE (pr, sizeof(*pr));
++
++    return (0);
++}
++
++static int
++control_ioctl (struct inode *inode, struct file *file, 
++                   unsigned int cmd, unsigned long arg)
++{
++    CONTROL_PRIVATE *pr  = (CONTROL_PRIVATE *) file->private_data;
++
++    PRINTF (DBG_DEVICE, DBG_FILE, "control_ioctl: cmd=%x arg=%lx\n", cmd, arg);
++
++    switch (cmd)
++    {
++    case ELAN4IO_DEVINFO:
++      if (copy_to_user ((void *) arg, &pr->pr_dev->dev_devinfo, sizeof (ELAN_DEVINFO)))
++          return (-EFAULT);
++      return (0);
++
++    case ELAN4IO_GET_POSITION:
++    {
++      ELAN_POSITION pos;
++
++      elan4_get_position (pr->pr_dev, &pos);
++
++      if (copy_to_user ((void *) arg, &pos, sizeof (ELAN_POSITION)))
++          return (-EFAULT);
++
++      return (0);
++    }
++
++    case ELAN4IO_SET_POSITION:
++    {
++      ELAN_POSITION pos;
++
++      if (copy_from_user (&pos, (void *) arg, sizeof (ELAN_POSITION)))
++          return (-EFAULT);
++      
++      return (elan4_set_position (pr->pr_dev, &pos));
++    }
++
++    case ELAN4IO_OLD_GET_PARAMS:
++    {
++      ELAN_PARAMS params;
++      unsigned short mask;
++
++      elan4_get_params (pr->pr_dev, &params, &mask);
++
++      if (copy_to_user ((void *) arg, &params, sizeof (ELAN_PARAMS)))
++          return (-EFAULT);
++
++      return (0);
++    }
++
++    case ELAN4IO_OLD_SET_PARAMS:
++    {
++      ELAN_PARAMS params;
++
++      if (copy_from_user (&params, (void *) arg, sizeof (ELAN_PARAMS)))
++          return (-EFAULT);
++      
++      elan4_set_params (pr->pr_dev, &params, 3);
++      
++      return (0);
++    }
++
++    case ELAN4IO_SET_PARAMS:
++    {
++      ELAN4IO_PARAMS_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_PARAMS_STRUCT)))
++          return (-EFAULT);
++      
++      elan4_set_params (pr->pr_dev, &args.p_params, args.p_mask);
++      
++      return (0);
++    }
++
++    case ELAN4IO_GET_PARAMS:
++    {
++      ELAN4IO_PARAMS_STRUCT args;
++
++      elan4_get_params (pr->pr_dev, &args.p_params, &args.p_mask);
++
++      if (copy_to_user ((void *) arg, &args, sizeof (ELAN_PARAMS)))
++          return (-EFAULT);
++
++      return (0);
++    }
++    }
++
++    return (-EINVAL);
++}
++
++static int
++control_mmap (struct file *file, struct vm_area_struct *vma)
++{
++    CONTROL_PRIVATE *pr  = (CONTROL_PRIVATE *) file->private_data;
++    unsigned       bar = OFF_TO_BAR (vma->vm_pgoff << PAGE_SHIFT);
++    unsigned long    off = OFF_TO_OFFSET (vma->vm_pgoff << PAGE_SHIFT);
++    long           len = vma->vm_end - vma->vm_start;
++
++    PRINTF (DBG_USER, DBG_FILE, "control_mmap: pr=%p bar=%x off=%x\n", pr, bar, off);
++
++    /* check bar number and translate the standard psuedo bars */
++    switch (bar)
++    {
++    case ELAN4_BAR_SDRAM:
++    case ELAN4_BAR_REGISTERS:
++      break;
++
++    default:
++      return (-EINVAL);
++    }
++
++    if (off < 0 || (off + len) > pci_resource_len (pr->pr_dev->dev_osdep.pdev, bar))
++      return (-EINVAL);
++
++    vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
++
++    if (__io_remap_page_range (vma->vm_start, pci_resource_start (pr->pr_dev->dev_osdep.pdev, bar) + off, len, vma->vm_page_prot))
++      return (-EAGAIN);
++
++    return (0);
++}
++
++/*
++ * /dev/elan4/sdramX - sdram access device
++ */
++static void 
++mem_freepage (MEM_PRIVATE *pr, MEM_PAGE *pg)
++{
++    PRINTF (DBG_USER, DBG_MEM, "mem_freepage: pr=%p pgoff=%lx pg=%p ref=%d\n", pr, pg->pg_pgoff, pg, pg->pg_ref);
++
++    elan4_sdram_free (pr->pr_dev, pg->pg_addr, SDRAM_PAGE_SIZE);
++
++    KMEM_FREE(pg, sizeof(*pg));
++}
++
++static MEM_PAGE *
++mem_getpage (MEM_PRIVATE *pr, unsigned long pgoff)
++{
++    int       hashval = MEM_HASH (pgoff);
++    MEM_PAGE *npg = NULL;
++    MEM_PAGE *pg;
++
++    ASSERT ((pgoff & SDRAM_PGOFF_OFFSET) == 0);
++
++    PRINTF (DBG_USER, DBG_MEM, "mem_getpage: pr=%p pgoff=%lx\n", pr, pgoff);
++    
++ again:
++    spin_lock (&pr->pr_lock);
++    for (pg = pr->pr_pages[hashval]; pg; pg = pg->pg_next)
++      if (pg->pg_pgoff == pgoff)
++          break;
++    
++    if (pg != NULL)
++    {
++      PRINTF (DBG_USER, DBG_MEM, "mem_getpage: pr=%p pgoff=%lx -> found %p addr=%x\n", pr, pgoff, pg, pg->pg_addr);
++
++      pg->pg_ref++;
++      spin_unlock (&pr->pr_lock);
++
++      if (npg != NULL)                                        /* we'd raced and someone else had created */
++          mem_freepage (pr, npg);                             /* this page - so free of our new one*/
++      return (pg);
++    }
++    
++    if (npg != NULL)                                          /* didn't find the page, so inset the */
++    {                                                         /* new one we've just created */
++      npg->pg_next = pr->pr_pages[hashval];
++      pr->pr_pages[hashval] = npg;
++      
++      spin_unlock (&pr->pr_lock);
++      return (npg);
++    }
++    
++    spin_unlock (&pr->pr_lock);                                       /* drop spinlock before creating a new page */
++    
++    KMEM_ALLOC (npg, MEM_PAGE *, sizeof (MEM_PAGE), 1);
++    if ((npg == NULL))
++      return (NULL);
++    
++    if ((npg->pg_addr = elan4_sdram_alloc (pr->pr_dev, SDRAM_PAGE_SIZE)) == 0)
++    {
++      KMEM_FREE(npg, sizeof(*npg));
++      return (NULL);
++    }
++
++#ifndef CONFIG_MPSAS
++    /* zero the page before returning it to the user */
++    elan4_sdram_zeroq_sdram (pr->pr_dev, npg->pg_addr, SDRAM_PAGE_SIZE);
++#endif
++    
++    npg->pg_pgoff = pgoff;
++    npg->pg_ref   = 1;
++    
++    /* created a new page - so have to rescan before inserting it */
++    goto again;
++}
++
++static void
++mem_droppage (MEM_PRIVATE *pr, unsigned long pgoff, int dontfree)
++{
++    MEM_PAGE **ppg;
++    MEM_PAGE  *pg;
++
++    spin_lock (&pr->pr_lock);
++    for (ppg = &pr->pr_pages[MEM_HASH(pgoff)]; *ppg; ppg = &(*ppg)->pg_next)
++      if ((*ppg)->pg_pgoff == pgoff)
++          break;
++
++    pg = *ppg;
++
++    ASSERT (*ppg != NULL);
++
++    PRINTF (DBG_USER, DBG_MEM, "mem_droppage: pr=%p pgoff=%lx pg=%p ref=%d dontfree=%d\n", pr, pgoff, (*ppg), (*ppg)->pg_ref, dontfree);
++
++    if (--pg->pg_ref == 0 && !dontfree)
++    {
++      *ppg = pg->pg_next;
++
++      mem_freepage (pr, pg);
++    }
++
++    spin_unlock (&pr->pr_lock);
++}
++
++static int
++mem_open (struct inode *inode, struct file *file)
++{
++    ELAN4_DEV   *dev = elan4_reference_device (ELAN4_DEVICE(inode), ELAN4_STATE_STARTED);
++    MEM_PRIVATE *pr;
++    register int i;
++
++    if (dev == NULL)
++      return (-ENXIO);
++
++    KMEM_ALLOC (pr, MEM_PRIVATE *, sizeof (MEM_PRIVATE), 1);
++    if ((pr == NULL))
++    {
++      elan4_dereference_device (dev);
++      return (-ENOMEM);
++    }
++
++    spin_lock_init (&pr->pr_lock);
++    pr->pr_dev = dev;
++    for (i = 0; i < MEM_HASH_SIZE; i++)
++      pr->pr_pages[i] = NULL;
++
++    file->private_data = (void *) pr;
++    
++    return (0);
++}
++
++static int
++mem_release (struct inode *node, struct file *file)
++{
++    MEM_PRIVATE *pr = (MEM_PRIVATE *) file->private_data;
++    MEM_PAGE    *pg, *next;
++    int          i;
++
++    /* free off any pages that we'd allocated */
++    spin_lock (&pr->pr_lock);
++    for (i = 0; i < MEM_HASH_SIZE; i++)
++    {
++      for (pg = pr->pr_pages[i]; pg; pg = next)
++      {
++          next = pg->pg_next;
++          mem_freepage (pr, pg);
++      }
++    }
++    spin_unlock (&pr->pr_lock);
++
++    elan4_dereference_device (pr->pr_dev);
++    KMEM_FREE(pr, sizeof(*pr));
++
++    return (0);
++}
++
++static int
++mem_ioctl (struct inode *inode, struct file *file, 
++                unsigned int cmd, unsigned long arg)
++{
++    return (-EINVAL);
++}
++
++static void 
++mem_vma_open (struct vm_area_struct *vma)
++{
++    MEM_PRIVATE   *pr = (MEM_PRIVATE *) vma->vm_private_data;
++    unsigned long addr;
++    unsigned long pgoff;
++
++    PRINTF (DBG_USER, DBG_MEM, "mem_vma_open: vm_mm=%p start=%lx end=%lx pgoff=%lx file=%p\n",
++          vma->vm_mm, vma->vm_start, vma->vm_end, vma->vm_pgoff, vma->vm_file);
++    
++    for (addr = vma->vm_start, pgoff = vma->vm_pgoff; addr < vma->vm_end; addr += PAGE_SIZE, pgoff++)
++      mem_getpage (pr, pgoff & SDRAM_PGOFF_MASK);
++}
++
++static void
++mem_vma_close (struct vm_area_struct *vma)
++{
++    MEM_PRIVATE  *pr  = (MEM_PRIVATE *) vma->vm_private_data;
++    unsigned long addr;
++    unsigned long pgoff;
++
++    PRINTF (DBG_USER, DBG_MEM, "mem_vma_close: vm_mm=%p start=%lx end=%lx pgoff=%lx file=%p\n",
++          vma->vm_mm, vma->vm_start, vma->vm_end, vma->vm_pgoff, vma->vm_file);
++
++    /* NOTE: the call to close may not have the same vm_start/vm_end values as 
++     *       were passed into mmap()/open() - since if an partial unmap had occurred
++     *       then the vma could have been shrunk or even split.
++     *
++     *       if a the vma is split then an vma_open() will be called for the top
++     *       portion - thus causing the reference counts to become incorrect.
++     *
++     * We drop the reference to any pages we're notified about - so they get freed
++     * earlier than when the device is finally released.
++     */
++    for (pgoff = vma->vm_pgoff, addr = vma->vm_start; addr < vma->vm_end; addr += PAGE_SIZE, pgoff++)
++      mem_droppage (pr, pgoff & SDRAM_PGOFF_MASK, 0);
++}
++
++struct vm_operations_struct mem_vm_ops = {
++    open:             mem_vma_open,
++    close:            mem_vma_close,
++};
++
++static int
++mem_mmap (struct file *file, struct vm_area_struct *vma)
++{
++    MEM_PRIVATE  *pr = (MEM_PRIVATE *) file->private_data;
++    MEM_PAGE     *pg;
++    unsigned long addr;
++    unsigned long pgoff;
++
++    PRINTF (DBG_USER, DBG_MEM, "mem_mmap: vma=%p start=%lx end=%lx pgoff=%lx file=%p\n",
++          vma, vma->vm_start, vma->vm_end, vma->vm_pgoff, file);
++
++    /* Don't allow these pages to be swapped out of dumped */
++    vma->vm_flags |= (VM_RESERVED | VM_IO);
++
++    vma->vm_ops          = &mem_vm_ops;
++    vma->vm_file         = file;
++    vma->vm_private_data = (void *) pr;
++
++    for (addr = vma->vm_start, pgoff = vma->vm_pgoff; addr < vma->vm_end; addr += PAGE_SIZE, pgoff++)
++    {
++      if ((pg = mem_getpage (pr, pgoff & SDRAM_PGOFF_MASK)) == NULL)
++          goto failed;
++
++      PRINTF (DBG_USER, DBG_MEM, "mem_mmap: addr %lx -> pg=%p sdram=%x+%x bar=%lx\n",
++              addr, pg, pg->pg_addr, (pgoff & SDRAM_PGOFF_OFFSET) * PAGE_SIZE,
++              pci_resource_start (pr->pr_dev->dev_osdep.pdev, ELAN4_BAR_SDRAM));
++
++      vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
++
++      if (! (pr->pr_dev->dev_devinfo.dev_params.values[ELAN4_PARAM_DRIVER_FEATURES] & ELAN4_FEATURE_NO_WRITE_COMBINE))
++          vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
++
++      if (__io_remap_page_range (addr, pci_resource_start (pr->pr_dev->dev_osdep.pdev, ELAN4_BAR_SDRAM) +
++                               pg->pg_addr + (pgoff & SDRAM_PGOFF_OFFSET) * PAGE_SIZE, PAGE_SIZE, vma->vm_page_prot))
++      {
++          mem_droppage (pr, pgoff & SDRAM_PGOFF_MASK, 0); /* drop our reference to this page */
++          goto failed;
++      }
++
++#if defined(conditional_schedule)
++      conditional_schedule();
++#endif
++    }
++
++    return (0);
++
++ failed:
++    /* free of any pages we've already allocated/referenced */
++    while (pgoff-- > vma->vm_pgoff)
++      mem_droppage (pr, pgoff & SDRAM_PGOFF_MASK, 0);
++
++    return (-ENOMEM);
++}
++
++/*
++ * /dev/elan4/userX - control device
++ *
++ */
++static inline void
++user_private_free (USER_PRIVATE *pr)
++{
++    ELAN4_DEV *dev = pr->pr_uctx->uctx_ctxt.ctxt_dev;
++
++    ASSERT (atomic_read (&pr->pr_ref) == 0);
++
++    user_free (pr->pr_uctx);
++    KMEM_FREE(pr, sizeof(*pr));
++
++    elan4_dereference_device (dev);
++}
++
++#if defined(IOPROC_PATCH_APPLIED)
++static void
++user_ioproc_release (void *arg, struct mm_struct *mm)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) arg;
++
++    PRINTF (pr->pr_uctx, DBG_IOPROC, "user_ioproc_release: ref=%d\n", atomic_read (&pr->pr_ref));
++
++    elan4mmu_invalidate_ctxt (&pr->pr_uctx->uctx_ctxt);
++
++    pr->pr_mm = NULL;
++
++    if (atomic_dec_and_test (&pr->pr_ref))
++      user_private_free (pr);
++}
++
++/*
++ * On 2.4 kernels we get passed a mm_struct, whereas on 2.6 kernels
++ * we get the vma which is more usefull
++ */
++#if defined(IOPROC_MM_STRUCT_ARG)
++static void
++user_ioproc_sync_range (void *arg, struct mm_struct *mm, unsigned long start, unsigned long end)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) arg;
++
++    PRINTF (pr->pr_uctx, DBG_IOPROC, "user_ioproc_sync_range: start=%lx end=%lx\n", start, end);
++
++    /* XXXX: this is intended to sync the modified bit from our page tables,
++     *       into the main cpu's modified bits - however since we do not
++     *       syncronize our modified bit on a ioproc_invalidate_page() call,
++     *       then it could get lost if we modify the page after the last
++     *       modification and writepage has occurred. Hence we invalidate
++     *       all translations and allow it to refault.
++     */
++
++    user_unload_main (pr->pr_uctx, start, end - start);
++}
++
++static void
++user_ioproc_invalidate_range (void *arg, struct mm_struct *mm, unsigned long start, unsigned long end)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) arg;
++
++    PRINTF (pr->pr_uctx, DBG_IOPROC, "user_ioproc_invalidate_range: start=%lx end=%lx\n", start, end);
++
++    user_unload_main (pr->pr_uctx, start, end - start);
++}
++
++static void
++user_ioproc_update_range (void *arg, struct mm_struct *mm, unsigned long start, unsigned long end)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) arg;
++
++    PRINTF (pr->pr_uctx, DBG_IOPROC, "user_ioproc_update_range: mm=%p start=%lx end=%lx\n", mm, start, end);
++
++#if defined(CONFIG_MPSAS)
++    if (((end - start) >> PAGE_SHIFT) > 16)
++      return;
++#endif
++
++    user_update_main (pr->pr_uctx, mm, start, end - start);
++}
++
++static void
++user_ioproc_change_protection (void *arg, struct mm_struct *mm, unsigned long start, unsigned long end, pgprot_t newprot)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) arg;
++
++    PRINTF (pr->pr_uctx, DBG_IOPROC, "user_ioproc_change_protection: start=%lx end=%lx\n", start, end);
++
++    user_unload_main (pr->pr_uctx, start, end - start);
++}
++
++
++#else
++
++static void
++user_ioproc_sync_range (void *arg, struct vm_area_struct *vma, unsigned long start, unsigned long end)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) arg;
++
++    PRINTF (pr->pr_uctx, DBG_IOPROC, "user_ioproc_sync_range: start=%lx end=%lx\n", start, end);
++
++    /* XXXX: this is intended to sync the modified bit from our page tables,
++     *       into the main cpu's modified bits - however since we do not
++     *       syncronize our modified bit on a ioproc_invalidate_page() call,
++     *       then it could get lost if we modify the page after the last
++     *       modification and writepage has occurred. Hence we invalidate
++     *       all translations and allow it to refault.
++     */
++
++    user_unload_main (pr->pr_uctx, start, end - start);
++}
++
++static void
++user_ioproc_invalidate_range (void *arg, struct vm_area_struct *vma, unsigned long start, unsigned long end)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) arg;
++
++    PRINTF (pr->pr_uctx, DBG_IOPROC, "user_ioproc_invalidate_range: start=%lx end=%lx\n", start, end);
++
++    user_unload_main (pr->pr_uctx, start, end - start);
++}
++
++static void
++user_ioproc_update_range (void *arg, struct vm_area_struct *vma, unsigned long start, unsigned long end)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) arg;
++
++    PRINTF (pr->pr_uctx, DBG_IOPROC, "user_ioproc_update_range: vma=%p start=%lx end=%lx\n", vma, start, end);
++
++#if defined(CONFIG_MPSAS)
++    if (((end - start) >> PAGE_SHIFT) > 16)
++      return;
++#endif
++
++    user_update_main (pr->pr_uctx, vma->vm_mm, start, end - start);
++}
++
++static void
++user_ioproc_change_protection (void *arg, struct vm_area_struct *vma, unsigned long start, unsigned long end, pgprot_t newprot)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) arg;
++
++    PRINTF (pr->pr_uctx, DBG_IOPROC, "user_ioproc_change_protection: start=%lx end=%lx\n", start, end);
++
++    user_unload_main (pr->pr_uctx, start, end - start);
++}
++#endif /* defined(IOPROC_NO_VMA_RANGE) */
++
++static void
++user_ioproc_sync_page (void *arg, struct vm_area_struct *vma, unsigned long addr)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) arg;
++
++    PRINTF (pr->pr_uctx, DBG_IOPROC, "user_ioproc_sync_page: addr=%lx\n", addr);
++
++    user_unload_main (pr->pr_uctx, addr & PAGE_MASK, PAGE_SIZE);
++}
++
++static void
++user_ioproc_invalidate_page (void *arg, struct vm_area_struct *vma, unsigned long addr)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) arg;
++
++    PRINTF (pr->pr_uctx, DBG_IOPROC, "user_ioproc_invalidate_page: addr=%lx\n", addr);
++
++    user_unload_main (pr->pr_uctx, addr & PAGE_MASK, PAGE_SIZE);
++}
++
++static void
++user_ioproc_update_page (void *arg, struct vm_area_struct *vma, unsigned long addr)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) arg;
++
++    PRINTF (pr->pr_uctx, DBG_IOPROC, "user_ioproc_update_page: addr=%lx\n", addr);
++
++    user_update_main (pr->pr_uctx, vma->vm_mm, addr & PAGE_MASK, PAGE_SIZE);
++}
++#endif /* defined(IOPROC_PATCH_APPLIED) */
++
++static int
++user_open (struct inode *inode, struct file *file)
++{
++    ELAN4_DEV    *dev;
++    USER_PRIVATE *pr;
++    USER_CTXT    *uctx;
++    
++    PRINTF (DBG_USER, DBG_FILE, "user_open: mm=%p users=%d count=%d\n", current->mm,
++          atomic_read (&current->mm->mm_users), atomic_read (&current->mm->mm_count));
++
++    if ((dev = elan4_reference_device (ELAN4_DEVICE(inode), ELAN4_STATE_STARTED)) == NULL)
++      return (-ENXIO);
++    
++    KMEM_ALLOC (pr, USER_PRIVATE *, sizeof (USER_PRIVATE), 1);
++    if ((pr == NULL))
++    {
++      elan4_dereference_device (dev);
++      return (-ENOMEM);
++    }
++
++    uctx = user_alloc (dev);
++
++    if (IS_ERR(uctx))
++    {
++      elan4_dereference_device (dev);
++      KMEM_FREE(pr, sizeof(*pr));
++
++      return PTR_ERR(uctx);
++    }
++
++    /* initialise refcnt to 1 - one for "file" */
++    atomic_set (&pr->pr_ref, 1);
++
++    pr->pr_uctx = uctx;
++    pr->pr_mm   = current->mm;
++
++    {
++      /* register a ioproc callback to notify us of translation changes */
++      pr->pr_ioproc.arg               = (void *) pr;
++      pr->pr_ioproc.release           = user_ioproc_release;
++      pr->pr_ioproc.sync_range        = user_ioproc_sync_range;
++      pr->pr_ioproc.invalidate_range  = user_ioproc_invalidate_range;
++      pr->pr_ioproc.update_range      = user_ioproc_update_range;
++      pr->pr_ioproc.change_protection = user_ioproc_change_protection;
++      pr->pr_ioproc.sync_page         = user_ioproc_sync_page;
++      pr->pr_ioproc.invalidate_page   = user_ioproc_invalidate_page;
++      pr->pr_ioproc.update_page       = user_ioproc_update_page;
++      
++      /* add an extra reference for the ioproc ops */
++      atomic_inc (&pr->pr_ref);
++      
++      spin_lock (&current->mm->page_table_lock);
++      ioproc_register_ops (current->mm, &pr->pr_ioproc);
++      spin_unlock (&current->mm->page_table_lock);
++    }
++
++    file->private_data = (void *) pr;
++
++    return (0);
++}
++
++static int
++user_release (struct inode *inode, struct file *file)
++{
++    USER_PRIVATE *pr = (USER_PRIVATE *) file->private_data;
++
++    PRINTF (pr->pr_uctx, DBG_FILE, "user_release: ref=%d\n", atomic_read (&pr->pr_ref));
++
++    if (atomic_dec_and_test (&pr->pr_ref))
++      user_private_free (pr);
++
++    return (0);
++}
++
++static int
++user_ioctl (struct inode *inode, struct file *file, 
++          unsigned int cmd, unsigned long arg)
++{
++    USER_PRIVATE *pr   = (USER_PRIVATE *) file->private_data;
++    USER_CTXT    *uctx = pr->pr_uctx;
++    int           res  = 0;
++
++    PRINTF (uctx, DBG_FILE, "user_ioctl: cmd=%x arg=%lx\n", cmd, arg);
++
++    if (current->mm != pr->pr_mm)
++      return (-EINVAL);
++    
++    switch (cmd)
++    {
++    case ELAN4IO_DEVINFO:
++      if (copy_to_user ((void *) arg, &uctx->uctx_ctxt.ctxt_dev->dev_devinfo, sizeof (ELAN_DEVINFO)))
++          return (-EFAULT);
++      return (0);
++
++    case ELAN4IO_POSITION:
++    {
++      ELAN4_DEV *dev = uctx->uctx_ctxt.ctxt_dev;
++
++      if (copy_to_user ((void *) arg, &dev->dev_position, sizeof (ELAN_POSITION)))
++          return (-EFAULT);
++      return (0);
++    }
++
++    case ELAN4IO_FREE:
++        {
++          spin_lock (&current->mm->page_table_lock);
++          if (pr->pr_mm != current->mm)
++              spin_unlock (&current->mm->page_table_lock);
++          else
++          {
++              ioproc_unregister_ops (current->mm, &pr->pr_ioproc);
++              spin_unlock (&current->mm->page_table_lock);
++              
++              user_ioproc_release (pr, current->mm);
++          }
++      }
++      return (0);
++
++    case ELAN4IO_ATTACH:
++    {
++      ELAN_CAPABILITY *cap;
++
++      KMEM_ALLOC (cap, ELAN_CAPABILITY *, sizeof (ELAN_CAPABILITY), 1);
++      if ((cap == NULL))
++          return (-ENOMEM);
++
++      if (copy_from_user (cap, (void *) arg, sizeof (ELAN_CAPABILITY)))
++          res = -EFAULT;
++      else if ((res = user_attach (uctx, cap)) == 0 && 
++               copy_to_user ((void *) arg, cap, sizeof (ELAN_CAPABILITY)))
++      {
++          user_detach (uctx, cap);
++          res = -EFAULT;
++      }
++
++      KMEM_FREE(cap, sizeof(*cap));
++      return (res);
++    }
++
++    case ELAN4IO_DETACH:
++    {
++      ELAN_CAPABILITY *cap;
++
++      KMEM_ALLOC (cap, ELAN_CAPABILITY *, sizeof (ELAN_CAPABILITY), 1);
++      if ((cap == NULL))
++          return (-ENOMEM);
++
++      if (copy_from_user (cap, (void *) arg, sizeof (ELAN_CAPABILITY)))
++          res = -EFAULT;
++      else
++          user_detach (uctx, cap);
++
++      KMEM_FREE(cap, sizeof(*cap));
++      return (res);
++    }
++
++    case ELAN4IO_BLOCK_INPUTTER:
++      user_block_inputter (uctx, arg);
++      return (0);
++
++    case ELAN4IO_ADD_P2PVP:
++    {
++      ELAN4IO_ADD_P2PVP_STRUCT *args;
++      
++      KMEM_ALLOC (args, ELAN4IO_ADD_P2PVP_STRUCT *, sizeof (ELAN4IO_ADD_P2PVP_STRUCT), 1);
++      if ((args == NULL))
++          return (-ENOMEM);
++
++      if (copy_from_user (args, (void *) arg, sizeof (ELAN4IO_ADD_P2PVP_STRUCT)))
++          res = -EFAULT;
++      else 
++          res = user_add_p2pvp (uctx, args->vp_process, &args->vp_capability);
++      
++      KMEM_FREE(args, sizeof(*args));
++      return (res);
++    }
++
++    case ELAN4IO_ADD_BCASTVP:
++    {
++      ELAN4IO_ADD_BCASTVP_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_ADD_BCASTVP_STRUCT)))
++          return (-EFAULT);
++
++      return (user_add_bcastvp (uctx, args.vp_process, args.vp_lowvp, args.vp_highvp));
++    }
++
++    case ELAN4IO_REMOVEVP:
++      return (user_removevp (uctx, arg));
++
++    case ELAN4IO_SET_ROUTE:
++    {
++      ELAN4IO_ROUTE_STRUCT args;
++      
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_ROUTE_STRUCT)))
++          return (-EFAULT);
++
++      return (user_set_route (uctx, args.rt_process, &args.rt_route));
++    }
++
++    case ELAN4IO_RESET_ROUTE:
++    {
++      ELAN4IO_ROUTE_STRUCT args;
++      
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_ROUTE_STRUCT)))
++          return (-EFAULT);
++
++      return (user_reset_route (uctx, args.rt_process));
++    }
++
++    case ELAN4IO_GET_ROUTE:
++    {
++      ELAN4IO_ROUTE_STRUCT args;
++      
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_ROUTE_STRUCT)))
++          return (-EFAULT);
++
++      if ((res = user_get_route (uctx, args.rt_process, &args.rt_route)) == 0 &&
++          copy_to_user ((void *) arg, &args, sizeof (ELAN4IO_ROUTE_STRUCT)))
++          res = -EFAULT;
++
++      return (res);
++    }
++
++    case ELAN4IO_CHECK_ROUTE:
++    {
++      ELAN4IO_ROUTE_STRUCT args;
++      
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_ROUTE_STRUCT)))
++          return (-EFAULT);
++
++      if ((res = user_check_route (uctx, args.rt_process, &args.rt_route, &args.rt_error)) == 0 &&
++          copy_to_user ((void *) arg, &args, sizeof (ELAN4IO_ROUTE_STRUCT)))
++          res = -EFAULT;
++
++      return (res);
++    }
++      
++    case ELAN4IO_ALLOCCQ:
++    {
++      ELAN4IO_ALLOCCQ_STRUCT args;
++      USER_CQ              *ucq;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_ALLOCCQ_STRUCT)))
++          return (-EFAULT);
++      
++      ucq = user_alloccq (uctx, args.cq_size & CQ_SizeMask, args.cq_perm & CQ_PermissionMask,
++                          (args.cq_type & ELAN4IO_CQ_TYPE_REORDER) ? UCQ_REORDER : 0);
++      if (IS_ERR (ucq))
++          return PTR_ERR (ucq);
++      
++      args.cq_indx = elan4_cq2idx (ucq->ucq_cq);
++      
++      if (copy_to_user ((void *) arg, &args, sizeof (ELAN4IO_ALLOCCQ_STRUCT)))
++      {
++          user_dropcq (uctx, ucq);
++          return (-EFAULT);
++      }
++      
++      /* don't drop the reference on the cq until the context is freed,
++       * or the caller explicitly frees the cq */
++      return (0);
++    }
++      
++    case ELAN4IO_FREECQ:
++    {
++      USER_CQ *ucq;
++      unsigned indx;
++
++      if (copy_from_user (&indx, (void *) arg, sizeof (unsigned)))
++          return (-EFAULT);
++
++      if ((ucq = user_findcq (uctx, indx)) == NULL)           /* can't free unallocated cq */
++          return (-EINVAL);
++      
++      user_dropcq (uctx, ucq);                                /* drop the reference we've just taken */
++
++      if ((ucq->ucq_flags & UCQ_SYSTEM))                      /* can't free device driver cq */
++          return (-EINVAL);
++
++      user_dropcq (uctx, ucq);                                /* and the one held from the alloccq call */
++
++      return (0);
++    }
++
++    case ELAN4IO_DUMPCQ:
++    {
++      ELAN4IO_DUMPCQ_STRUCT args;
++      ELAN4_DEV *dev = uctx->uctx_ctxt.ctxt_dev;
++      USER_CQ *ucq;
++      void *buf;
++      int i;
++      
++      if (copy_from_user (&args, (void *) arg, sizeof(ELAN4IO_DUMPCQ_STRUCT)))
++          return (-EFAULT);
++
++      if ((ucq = user_findcq (uctx, args.cq_indx)) == NULL)
++          return (-EINVAL);
++
++      if (args.bufsize)
++      {
++          E4_uint32 usedBufSize = min(args.cq_size, args.bufsize);
++
++          KMEM_ALLOC (buf, void *, usedBufSize, 1);
++
++          if (buf == NULL)
++              return (-ENOMEM);
++
++          for (i=0; i<usedBufSize; i+=sizeof(int))
++              ((int *)buf)[i/sizeof(int)] = elan4_sdram_readl(dev, ucq->ucq_cq->cq_space + i);
++
++          if (copy_to_user((void *)args.buffer, buf, usedBufSize))
++          {
++              KMEM_FREE(buf, args.bufsize);
++              return (-EFAULT);
++          }
++          KMEM_FREE(buf, usedBufSize);
++          args.bufsize = usedBufSize;
++      }
++
++      args.cq_size = CQ_Size(ucq->ucq_cq->cq_size);
++      args.cq_space = ucq->ucq_cq->cq_space;
++
++
++      if (copy_to_user((void *)arg, &args, sizeof(ELAN4IO_DUMPCQ_STRUCT)))
++      {
++          return (-EFAULT);
++      }
++      
++      user_dropcq (uctx, ucq); /* drop the reference we've just taken */
++
++      return (0);
++    }
++
++    case ELAN4IO_SETPERM:
++    {
++      ELAN4IO_PERM_STRUCT args;
++      
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_PERM_STRUCT)))
++          return (-EFAULT);
++
++      return (user_setperm (uctx, args.ps_maddr, args.ps_eaddr, args.ps_len, args.ps_perm));
++    }
++
++    case ELAN4IO_CLRPERM:
++    {
++      ELAN4IO_PERM_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_PERM_STRUCT)))
++          return (-EFAULT);
++
++      user_clrperm (uctx, args.ps_eaddr, args.ps_len);
++      return (0);
++    }
++    
++    case ELAN4IO_TRAPSIG:
++    {
++      ELAN4IO_TRAPSIG_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_TRAPSIG_STRUCT)))
++          return (-EFAULT);
++
++      pr->pr_uctx->uctx_trap_pid   = current->pid;
++      pr->pr_uctx->uctx_trap_signo = args.ts_signo;
++      
++      return (0);
++    }
++    
++    case ELAN4IO_TRAPHANDLER:
++    {
++      ELAN4IO_TRAPHANDLER_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_TRAPHANDLER_STRUCT)))
++          return (-EFAULT);
++
++      return (user_trap_handler (pr->pr_uctx, (ELAN4_USER_TRAP *)args.th_trapp, args.th_nticks));
++    }
++
++    case ELAN4IO_REQUIRED_MAPPINGS:
++    {
++      ELAN4IO_REQUIRED_MAPPINGS_STRUCT args;
++      
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_REQUIRED_MAPPINGS_STRUCT)))
++          return (-EFAULT);
++
++      pr->pr_uctx->uctx_upage_addr    = args.rm_upage_addr;
++      pr->pr_uctx->uctx_trestart_addr = args.rm_trestart_addr;
++
++      return (0);
++    }
++
++    case ELAN4IO_ALLOC_TRAP_QUEUES:
++    {
++      ELAN4IO_ALLOC_TRAP_QUEUES_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_ALLOC_TRAP_QUEUES_STRUCT)))
++          return (-EFAULT);
++
++      return (user_alloc_trap_queues (uctx, args.tq_ndproc_traps, args.tq_neproc_traps, 
++                                      args.tq_ntproc_traps, args.tq_nthreads, args.tq_ndmas));
++    }
++
++    case ELAN4IO_RESUME_EPROC_TRAP:
++    {
++      ELAN4IO_RESUME_EPROC_TRAP_STRUCT args;
++      
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_RESUME_EPROC_TRAP_STRUCT)))
++          return (-EFAULT);
++
++      return (user_resume_eproc_trap (pr->pr_uctx, args.rs_addr));
++    }
++
++    case ELAN4IO_RESUME_CPROC_TRAP:
++    {
++      ELAN4IO_RESUME_CPROC_TRAP_STRUCT args;
++      
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_RESUME_CPROC_TRAP_STRUCT)))
++          return (-EFAULT);
++
++      return (user_resume_cproc_trap (pr->pr_uctx, args.rs_indx));
++    }
++
++    case ELAN4IO_RESUME_DPROC_TRAP:
++    {
++      ELAN4IO_RESUME_DPROC_TRAP_STRUCT args;
++      
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_RESUME_DPROC_TRAP_STRUCT)))
++          return (-EFAULT);
++
++      return (user_resume_dproc_trap (pr->pr_uctx, &args.rs_desc));
++    }
++
++    case ELAN4IO_RESUME_TPROC_TRAP:
++    {
++      ELAN4IO_RESUME_TPROC_TRAP_STRUCT args;
++      
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_RESUME_TPROC_TRAP_STRUCT)))
++          return (-EFAULT);
++
++      return (user_resume_tproc_trap (pr->pr_uctx, &args.rs_regs));
++    }
++
++    case ELAN4IO_RESUME_IPROC_TRAP:
++    {
++      ELAN4IO_RESUME_IPROC_TRAP_STRUCT args;
++      
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_RESUME_IPROC_TRAP_STRUCT)))
++          return (-EFAULT);
++
++      return (user_resume_iproc_trap (pr->pr_uctx, args.rs_channel, args.rs_trans, 
++                                      &args.rs_header, &args.rs_data));
++    }
++
++    case ELAN4IO_FLUSH_ICACHE:
++      elan4_flush_icache (&uctx->uctx_ctxt);
++      return (0);
++
++    case ELAN4IO_STOP_CTXT:
++      if (arg)
++          user_swapout (uctx, UCTX_USER_STOPPED);
++      else
++          user_swapin (uctx, UCTX_USER_STOPPED);
++      return (0);
++
++    case ELAN4IO_ALLOC_INTCOOKIE_TABLE:
++    {
++      ELAN_CAPABILITY *cap;
++      INTCOOKIE_TABLE *tbl;
++
++      KMEM_ALLOC (cap, ELAN_CAPABILITY *, sizeof (ELAN_CAPABILITY), 1);
++      if ((cap == NULL))
++          return (-ENOMEM);
++
++      if (copy_from_user (cap, (void *) arg, sizeof (ELAN_CAPABILITY)))
++          res = -EFAULT;
++      else
++      {
++          tbl = intcookie_alloc_table(cap);
++
++          if (tbl == NULL)
++              res = -ENOMEM;
++          else
++          {
++              /* Install the intcookie table we've just created */
++              spin_lock (&uctx->uctx_spinlock);
++              if (uctx->uctx_intcookie_table != NULL)
++                  res = -EBUSY;
++              else
++                  uctx->uctx_intcookie_table = tbl;
++              spin_unlock (&uctx->uctx_spinlock);
++              
++              /* drop the table we created if there already was one */
++              if (res != 0)
++                  intcookie_free_table (tbl);
++          }
++      }
++
++      KMEM_FREE(cap, sizeof(*cap));
++
++      return (res);
++    }
++
++    case ELAN4IO_FREE_INTCOOKIE_TABLE:
++    {
++      INTCOOKIE_TABLE *tbl;
++
++      spin_lock (&uctx->uctx_spinlock);
++      tbl = uctx->uctx_intcookie_table;
++      uctx->uctx_intcookie_table = NULL;
++      spin_unlock (&uctx->uctx_spinlock);
++
++      if (tbl != NULL)
++          intcookie_free_table (tbl);
++
++      return (tbl == NULL ? -EINVAL : 0);
++    }
++
++    case ELAN4IO_ALLOC_INTCOOKIE:
++    {
++      /* For backwards compatibility with the old libs (pre 1.8.0)
++       * we allocate an intcookie table on the first cookie
++       * alloc if one hasn't be created already
++       */
++      if (uctx->uctx_intcookie_table == NULL)
++      {
++          ELAN_CAPABILITY *cap;
++          INTCOOKIE_TABLE *tbl;
++          
++          KMEM_ALLOC (cap, ELAN_CAPABILITY *, sizeof (ELAN_CAPABILITY), 1);
++          if ((cap == NULL))
++              return (-ENOMEM);
++
++          /* Create a dummy capability */
++          elan_nullcap(cap);
++
++          /* Must be unique for each process on a node */
++          cap->cap_mycontext = (int) ELAN4_TASK_HANDLE();
++
++          /* Create a new intcookie table */
++          tbl = intcookie_alloc_table(cap);
++
++          /* Hang intcookie table off uctx */
++          spin_lock (&uctx->uctx_spinlock);
++          if (uctx->uctx_intcookie_table == NULL)
++          {
++              uctx->uctx_intcookie_table = tbl;
++              spin_unlock (&uctx->uctx_spinlock);
++          }
++          else
++          {
++              spin_unlock (&uctx->uctx_spinlock);
++              intcookie_free_table(tbl);
++          }
++
++          KMEM_FREE(cap, sizeof(*cap));
++      }
++      
++      return (intcookie_alloc (uctx->uctx_intcookie_table, arg));
++    }
++
++    case ELAN4IO_FREE_INTCOOKIE:
++      if (uctx->uctx_intcookie_table == NULL)
++          return -EINVAL;
++      else
++          return (intcookie_free (uctx->uctx_intcookie_table, arg));
++
++    case ELAN4IO_ARM_INTCOOKIE:
++      if (uctx->uctx_intcookie_table == NULL)
++          return -EINVAL;
++      else
++          return (intcookie_arm (uctx->uctx_intcookie_table, arg));
++
++    case ELAN4IO_WAIT_INTCOOKIE:
++      if (uctx->uctx_intcookie_table == NULL)
++          return -EINVAL;
++      else
++          return (intcookie_wait (uctx->uctx_intcookie_table, arg));
++
++    case ELAN4IO_FIRE_INTCOOKIE:
++    {
++      ELAN4IO_FIRECAP_STRUCT *args;
++
++      KMEM_ALLOC (args, ELAN4IO_FIRECAP_STRUCT *, sizeof (ELAN4IO_FIRECAP_STRUCT), 1);
++      if ((args == NULL))
++          return (-ENOMEM);
++
++      if (copy_from_user (args, (void *) arg, sizeof (ELAN4IO_FIRECAP_STRUCT)))
++          res = -EFAULT;
++      else
++          res = intcookie_fire_cap (&args->fc_capability, args->fc_cookie);
++      
++      KMEM_FREE(args, sizeof(*args));
++
++      return (res);
++    }
++
++    case ELAN4IO_NETERR_MSG:
++    {
++      ELAN4IO_NETERR_MSG_STRUCT args;
++      
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_NETERR_MSG_STRUCT)))
++          return (-EFAULT);
++
++      return (user_send_neterr_msg (uctx, args.nm_vp, args.nm_nctx, args.nm_retries, &args.nm_msg));
++    }
++
++    case ELAN4IO_NETERR_TIMER:
++    {
++      unsigned long ticks = ((unsigned long) arg * HZ) / 1000;
++
++      PRINTF (uctx, DBG_NETERR, "elan4_neterr_timer: arg %ld inc %ld\n", arg, ticks);
++
++      mod_timer (&uctx->uctx_neterr_timer, (jiffies + (ticks > 0 ? ticks : 1)));
++      return 0;
++    }
++              
++    case ELAN4IO_NETERR_FIXUP:
++    {
++      ELAN4IO_NETERR_FIXUP_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_NETERR_FIXUP_STRUCT)))
++          return (-EFAULT);
++
++      if (args.nf_sten)
++          return (user_neterr_sten (uctx, args.nf_vp, args.nf_cookie, args.nf_waitforeop));
++      else
++          return (user_neterr_dma  (uctx, args.nf_vp, args.nf_cookie, args.nf_waitforeop));
++    }
++    default:
++      PRINTF (uctx, DBG_FILE, "user_ioctl: invalid ioctl %x\n", cmd);
++      return (-EINVAL);
++    }
++}
++
++static void
++user_vma_open (struct vm_area_struct *vma)
++{
++    USER_PRIVATE *pr   = (USER_PRIVATE *) vma->vm_private_data;
++    USER_CTXT    *uctx = pr->pr_uctx;
++    unsigned long addr;
++    unsigned long pgoff;
++
++    PRINTF (uctx, DBG_FILE, "user_vma_open: vm_mm=%p start=%lx end=%lx pgoff=%lx file=%p\n",
++          vma->vm_mm, vma->vm_start, vma->vm_end, vma->vm_pgoff, vma->vm_file);
++
++    for (addr = vma->vm_start, pgoff = vma->vm_pgoff; addr < vma->vm_end; addr += PAGE_SIZE, pgoff++)
++      elan4_getcqa (&uctx->uctx_ctxt, pgoff);
++}
++
++static void 
++user_vma_close (struct vm_area_struct *vma)
++{
++    USER_PRIVATE *pr   = (USER_PRIVATE *) vma->vm_private_data;
++    USER_CTXT    *uctx = pr->pr_uctx;
++    unsigned long addr;
++    unsigned long pgoff;
++
++    PRINTF (uctx, DBG_FILE, "user_vma_close: vm_mm=%p start=%lx end=%lx pgoff=%lx file=%p\n",
++          vma->vm_mm, vma->vm_start, vma->vm_end, vma->vm_pgoff, vma->vm_file);
++
++    /* NOTE: the same comments apply as mem_vma_close */
++    for (addr = vma->vm_start, pgoff = vma->vm_pgoff; addr < vma->vm_end; addr += PAGE_SIZE, pgoff++)
++      if (elan4_getcqa (&uctx->uctx_ctxt, pgoff) != NULL)
++      {
++          elan4_putcqa (&uctx->uctx_ctxt, pgoff);                     /* drop the reference we've just taken */
++          elan4_putcqa (&uctx->uctx_ctxt, pgoff);                     /* and the one held by the mmap */
++      }
++}
++
++struct vm_operations_struct user_vm_ops = {
++    open:             user_vma_open,
++    close:            user_vma_close,
++};
++
++static int
++user_mmap (struct file *file, struct vm_area_struct *vma)
++{
++    USER_PRIVATE *pr    = (USER_PRIVATE *) file->private_data;
++    USER_CTXT    *uctx  = pr->pr_uctx;
++    ELAN4_DEV     *dev   = uctx->uctx_ctxt.ctxt_dev;
++    ELAN4_CQA     *cqa;
++    unsigned long addr;
++    unsigned long pgoff;
++    int           res;
++    ioaddr_t      ioaddr;
++    
++    /* Don't allow these pages to be swapped out of dumped */
++    vma->vm_flags |= (VM_RESERVED | VM_IO);
++
++    vma->vm_ops          = &user_vm_ops;
++    vma->vm_file         = file;
++    vma->vm_private_data = (void *) pr;
++    
++    for (addr = vma->vm_start, pgoff = vma->vm_pgoff; addr < vma->vm_end; addr += PAGE_SIZE, pgoff++)
++    {
++      switch (pgoff)
++      {
++      default:
++          PRINTF (uctx, DBG_FILE, "user_mmap: command queue %ld mapping at %lx\n",  pgoff, addr);
++          
++          if ((cqa = elan4_getcqa (&uctx->uctx_ctxt, pgoff)) == NULL)
++          {
++              res = -EINVAL;
++              goto failed;
++          }
++
++          PRINTF (uctx, DBG_FILE, "user_mmap: cqa=%p idx=%d num=%d ref=%d\n", cqa, cqa->cqa_idx, cqa->cqa_cqnum, cqa->cqa_ref);
++    
++          vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
++
++          if (! (dev->dev_devinfo.dev_params.values[ELAN4_PARAM_DRIVER_FEATURES] & ELAN4_FEATURE_NO_WRITE_COMBINE) && (cqa->cqa_type & CQ_Reorder) != 0)
++              vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
++
++          PRINTF (uctx, DBG_FILE, "user_mmap: remap_page_range (%lx, %lx, %lx, %lx)\n",
++                  addr, pci_resource_start (dev->dev_osdep.pdev, ELAN4_BAR_REGISTERS) + 
++                  (cqa->cqa_cqnum + dev->dev_cqoffset) * CQ_CommandMappingSize, PAGE_SIZE,
++                  vma->vm_page_prot);
++
++          if (__io_remap_page_range (addr, 
++                                     pci_resource_start (dev->dev_osdep.pdev, ELAN4_BAR_REGISTERS) + 
++                                     (cqa->cqa_cqnum + dev->dev_cqoffset) * CQ_CommandMappingSize,
++                                     PAGE_SIZE, vma->vm_page_prot))
++          {
++              PRINTF (uctx, DBG_FILE, "user_mmap: remap_page_range failed\n");
++
++              elan4_putcqa (&uctx->uctx_ctxt, pgoff);
++              res = -ENOMEM;
++              goto failed;
++          }
++          break;
++          
++      case ELAN4_OFF_USER_REGS:
++          vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
++
++          switch (dev->dev_devinfo.dev_revision_id)
++          {
++          case PCI_REVISION_ID_ELAN4_REVA:
++              ioaddr = pci_resource_start (dev->dev_osdep.pdev, ELAN4_BAR_REGISTERS) + ELAN4_REVA_REG_OFFSET + offsetof(E4_Registers, uRegs);
++              break;
++              
++          case PCI_REVISION_ID_ELAN4_REVB:
++              ioaddr = pci_resource_start (dev->dev_osdep.pdev, ELAN4_BAR_REGISTERS) + ELAN4_REVB_REG_OFFSET + offsetof(E4_Registers, uRegs);
++              break;
++
++          default:
++              res = -EINVAL;
++              goto failed;
++          }
++
++          PRINTF (uctx, DBG_FILE, "user_mmap: user_regs at %lx ioaddr %lx prot %lx\n",
++                  addr, ioaddr, vma->vm_page_prot.pgprot);
++
++          if (__io_remap_page_range (addr,  (ioaddr & PAGEMASK), PAGE_SIZE, vma->vm_page_prot))
++          {                     
++              res = -EAGAIN;
++              goto failed;
++          }
++
++          break;
++          
++      case ELAN4_OFF_USER_PAGE:
++          PRINTF (uctx, DBG_FILE, "user_mmap: shared user page - kaddr=%lx uaddr=%lx phys=%lx\n", 
++                  uctx->uctx_upage, addr, kmem_to_phys (uctx->uctx_upage));
++
++          /* we do not want to have this area swapped out, lock it */
++          vma->vm_flags |= VM_LOCKED;
++          
++          /* Mark the page as reserved or else the remap_page_range() doesn't remap it */
++          SetPageReserved(pte_page(*find_pte_kernel((unsigned long) uctx->uctx_upage)));
++      
++          if (__remap_page_range (addr, kmem_to_phys (uctx->uctx_upage), PAGE_SIZE, vma->vm_page_prot))
++          {
++              PRINTF (uctx, DBG_FILE, "user_mmap: remap_page_range (user_page) failed\n");
++              res = -ENOMEM;
++              goto failed;
++          }
++          break;
++          
++      case ELAN4_OFF_TPROC_TRAMPOLINE:
++          vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
++
++          PRINTF (uctx, DBG_FILE, "user_mmap: tproc trampoline - kaddr=%lx uaddr=%lx phys=%lx\n", uctx->uctx_trampoline, addr, 
++                  pci_resource_start (dev->dev_osdep.pdev, ELAN4_BAR_SDRAM) + uctx->uctx_trampoline + (addr & (SDRAM_PGOFF_OFFSET << PAGE_SHIFT)));
++
++          if (__io_remap_page_range (addr, pci_resource_start (dev->dev_osdep.pdev, ELAN4_BAR_SDRAM) + 
++                                     uctx->uctx_trampoline + (addr & (SDRAM_PGOFF_OFFSET << PAGE_SHIFT)),
++                                     PAGE_SIZE, vma->vm_page_prot))
++          {
++              PRINTF (uctx, DBG_FILE, "user_mmap: remap_page_range (tproc_trampoline) failed\n");
++              res = -ENOMEM;
++              goto failed;
++          }
++          break;
++          
++      case ELAN4_OFF_DEVICE_STATS:
++          printk ("user_mmap: device_stats\n");
++          break;
++      }
++      
++    }
++
++    return (0);
++
++ failed:
++    for (addr -= PAGE_SIZE, pgoff--; addr >= vma->vm_start; addr -= PAGE_SIZE, pgoff--)
++      elan4_putcqa (&uctx->uctx_ctxt, pgoff);         /* drop the reference we've just taken */
++    return (res);
++}
++
++/* driver entry points */
++static int
++elan4_open (struct inode *inode, struct file *file)
++{
++    PRINTF (DBG_USER, DBG_FILE, "elan4_open: device %d minor %d file=%p\n", ELAN4_DEVICE(inode), ELAN4_MINOR(inode), file);
++    
++    switch (ELAN4_MINOR (inode))
++    {
++    case ELAN4_MINOR_CONTROL:
++      return (control_open (inode, file));
++    case ELAN4_MINOR_MEM:
++      return (mem_open (inode, file));
++    case ELAN4_MINOR_USER:
++      return (user_open (inode, file));
++    default:
++      return (-ENXIO);
++    }
++}
++
++static int
++elan4_release (struct inode *inode, struct file *file)
++{
++    PRINTF (DBG_USER, DBG_FILE, "elan4_release: device %d minor %d file=%p\n", ELAN4_DEVICE(inode), ELAN4_MINOR(inode), file);
++    
++    switch (ELAN4_MINOR (inode))
++    {
++    case ELAN4_MINOR_CONTROL:
++      return (control_release (inode, file));
++    case ELAN4_MINOR_MEM:
++      return (mem_release (inode, file));
++    case ELAN4_MINOR_USER:
++      return (user_release (inode, file));
++    default:
++      return (-ENXIO);
++    }
++}
++
++static int
++elan4_ioctl (struct inode *inode, struct file *file, 
++           unsigned int cmd, unsigned long arg)
++{
++    PRINTF (DBG_USER, DBG_FILE, "elan4_ioctl: device %d minor %d cmd %x\n", ELAN4_DEVICE(inode), ELAN4_MINOR(inode), cmd);
++    
++    switch (ELAN4_MINOR (inode))
++    {
++    case ELAN4_MINOR_CONTROL:
++      return (control_ioctl (inode, file, cmd, arg));
++    case ELAN4_MINOR_MEM:
++      return (mem_ioctl (inode, file, cmd, arg));
++    case ELAN4_MINOR_USER:
++      return (user_ioctl (inode, file, cmd, arg));
++    default:
++      return (-ENXIO);
++    }
++}
++
++#if defined(CONFIG_PPC64) || defined(CONFIG_SPARC64) || defined(CONFIG_X86_64)
++static int
++elan4_ioctl32 (unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file)
++{
++    struct inode *inode = file->f_dentry->d_inode;
++    extern int sys_ioctl (unsigned int fd, unsigned int cmd, unsigned long arg);
++
++    PRINTF (DBG_USER, DBG_FILE, "elan4_ioctl32: device %d minor %d cmd %x\n", ELAN4_DEVICE(inode), ELAN4_MINOR(inode), cmd);
++    
++    if (ELAN4_MINOR (inode) == ELAN4_MINOR_USER)
++    {
++      USER_PRIVATE *pr    = (USER_PRIVATE *) file->private_data;
++      USER_CTXT    *uctx  = pr->pr_uctx;
++
++      if (current->mm != pr->pr_mm)
++          return -EINVAL;
++      
++      switch (cmd)
++      {
++      case ELAN4IO_SETPERM32:
++      {
++          ELAN4IO_PERM_STRUCT32 args;
++          
++          if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_PERM_STRUCT32)))
++              return (-EFAULT);
++          
++          PRINTF (DBG_USER, DBG_FILE, "user_ioctl32: setperm maddr=%x eaddr=%llx len=%llxx perm=%d\n",
++                  args.ps_maddr, args.ps_eaddr,args.ps_len, args.ps_perm);
++
++          return (user_setperm (uctx, args.ps_maddr, args.ps_eaddr, args.ps_len, args.ps_perm));
++      }
++      
++      case ELAN4IO_CLRPERM32:
++      {
++          ELAN4IO_PERM_STRUCT32 args;
++          
++          if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_PERM_STRUCT32)))
++              return (-EFAULT);
++          
++          PRINTF (DBG_USER, DBG_FILE, "user_ioctl32: clrperm eaddr=%llx len=%ll\n",
++                  args.ps_eaddr, args.ps_len);
++
++          user_clrperm (uctx, args.ps_eaddr, args.ps_len);
++          return (0);
++      }
++    
++      case ELAN4IO_TRAPHANDLER32:
++      {
++          ELAN4IO_TRAPHANDLER_STRUCT32 args;
++          
++          if (copy_from_user (&args, (void *) arg, sizeof (ELAN4IO_TRAPHANDLER_STRUCT32)))
++              return (-EFAULT);
++          
++          PRINTF (DBG_USER, DBG_FILE, "user_ioctl32: traphandler trapp=%x nticks=%d\n",
++                  args.th_trapp, args.th_nticks);
++
++          return (user_trap_handler (pr->pr_uctx, (ELAN4_USER_TRAP *)(unsigned long)args.th_trapp, args.th_nticks));
++      }
++      }
++    }
++
++    PRINTF (DBG_USER, DBG_FILE, "elan4_ioctl32: fd=%d cmd=%x arg=%lx file=%p\n", fd, cmd, arg, file);
++    return (sys_ioctl (fd, cmd, arg));
++}
++#endif
++
++
++
++static int
++elan4_mmap (struct file *file, struct vm_area_struct *vma)
++{
++    PRINTF (DBG_USER, DBG_FILE, "elan4_mmap: instance %d minor %d start=%lx end=%lx pgoff=%lx\n", 
++          ELAN4_DEVICE (file->f_dentry->d_inode), ELAN4_MINOR (file->f_dentry->d_inode),
++          vma->vm_start, vma->vm_end, vma->vm_pgoff);
++
++    switch (ELAN4_MINOR (file->f_dentry->d_inode))
++    {
++    case ELAN4_MINOR_CONTROL:
++      return (control_mmap (file, vma));
++    case ELAN4_MINOR_MEM:
++      return (mem_mmap (file, vma));
++    case ELAN4_MINOR_USER:
++      return (user_mmap (file, vma));
++    default:
++      return (-ENXIO);
++    }
++}
++
++void
++elan4_update_intel_p64h2 (ELAN4_DEV *dev, struct pci_dev *bridge)
++{
++    u16 cnf;
++    
++    pci_read_config_word (bridge, 0x40 /* CNF */, &cnf);
++    
++    /* We expect the CNF register to be configured as follows
++     *
++     * [8]   == 1     PMODE PCI Mode
++     * [7:6] == 2/3   PFREQ PCI Frequency (100/133)
++     * [5]   == 0     RSDIS Restreaming Disable
++     * [4:3] == 0x    PP    Prefetch Policy
++     * [2]   == 0       DTD   Delayed Transaction Depth
++     * [1:0] == 10      MDT   MaximumDelaedTransactions
++     */
++    
++    if ((cnf & (1 << 8)) == 0)
++      printk ("elan%d: strangeness - elan reports PCI-X but P64H2 reports PCI mode !\n", dev->dev_instance);
++    else if ((cnf & 0xb7) != 0x82 && (cnf & 0xb7) != 0x84 && optimise_pci_bus < 2)
++      printk ("elan%d: P64H2 CNF is not configured as expected : RSDIS=%d PP=%d DTD=%d MDT=%d\n",
++              dev->dev_instance, (cnf >> 5) & 1, (cnf >> 3) & 3, (cnf >> 2) & 1, cnf & 3);
++    else
++    {
++      switch ((cnf >> 6) & 3)
++      {
++      case 2:                                         /* PCI-X 100 */
++          pci_write_config_word (bridge, 0xfc /* PC100 */, 0x7777);
++          
++          printk ("elan%d: optimise P64H2 : setting MDT=0, DTD=1, PFC=777 for PCI-X 100\n", dev->dev_instance);
++          
++          break;
++          
++      case 3:                                         /* PCI-X 133 */
++          pci_write_config_word (bridge, 0xfe /* PC133 */, 0x7777);
++          
++          printk ("elan%d: optimise P64H2 : setting MDT=0, DTD=1, PFC=777 for PCI-X 133\n", dev->dev_instance);
++          break;
++      }
++      
++      pci_write_config_word (bridge, 0x40 /* CNF */, (cnf & 0xfff8) | 0x4);   /* DTD=1 MDT=0 */
++    }
++}
++
++int
++elan4_optimise_intel_p64h2 (ELAN4_DEV *dev, struct pci_dev *pdev)
++{
++    struct pci_bus   *bus      = pdev->bus;
++    struct pci_dev   *bridge   = bus->self;
++    unsigned int      devcount = 0;
++    u8                revision;
++    u32               ectrl;
++    struct list_head *el;
++    
++    pci_read_config_dword (pdev, PCI_ELAN_CONTROL, &ectrl);
++
++    /* We can only run in PCI-Xmode with a B1 stepping P64H2 because of P64H2 Errata 3 */
++    pci_read_config_byte (bridge, PCI_REVISION_ID, &revision);
++    if (revision < 0x04)
++    {
++      if ((ectrl & ECTRL_INITIALISATION_MODE) != Pci2_2)
++      {
++          static const char *p64h2_stepping[4] = {"UNKNOWN", "UNKNOWN", "UNKNOWN", "B0"};
++
++          printk ("elan%d: unable to use device because of P64H2 Errata 3 on\n"
++                  "       %s stepping part and running in a PCI-X slot\n", 
++                  dev->dev_instance, p64h2_stepping[revision]);
++          return -EINVAL;
++      }
++    }
++    
++    /* We can only alter the bus configuration registers if the Elan is the only device
++     * on the bus ... */
++    list_for_each (el, &bus->devices) {
++      struct pci_dev *pcip = list_entry (el, struct pci_dev, bus_list);
++
++      if (pcip == pdev || (pcip->vendor == PCI_VENDOR_ID_INTEL && pcip->device == 0x1462 /* P64H2 HOTPLUG */))
++          continue;
++          
++      devcount++;
++    }
++
++    if (devcount > 0 || !list_empty (&bus->children))
++    {
++      printk ("elan%d: unable to optimise P64H2 settings as %s%s\n", dev->dev_instance,
++              (devcount > 0) ? "more than one device on bus" :  "",
++              ! list_empty (&bus->children) ? "has child buses" : "");
++      return 0;
++    }
++
++#ifdef __ia64
++    if ((ectrl & ECTRL_INITIALISATION_MODE) == PciX100to133MHz)
++    {
++      struct pci_dev *pcip;
++      unsigned int sioh_good      = 0;
++      unsigned int sioh_downgrade = 0;
++      unsigned int snc_good       = 0;
++      unsigned int snc_downgrade  = 0;
++      
++      /* Search for the associated SIOH and SNC on ia64,
++       * if we have a C2 SIOH and a C0/C1 SNC, then we can
++       * reconfigure the P64H2 as follows:
++       *    CNF:MDT   = 0
++       *    CNF:DTD   = 1
++       *    CNF:PC133 = 7777
++       *
++       * if not, then issue a warning that down rev parts
++       * affect bandwidth.
++       */
++      for (pcip = NULL; (pcip = pci_find_device (PCI_VENDOR_ID_INTEL, 0x500, pcip)); )
++      {
++          pci_read_config_byte (pcip, PCI_REVISION_ID, &revision);
++          
++          if (revision >= 0x21)
++              snc_good++;
++          else
++          {
++              printk ("elan%d: SNC revision %x (%s)\n", dev->dev_instance, revision,
++                      revision == 0x00 ? "A0" : revision == 0x01 ? "A1" : 
++                      revision == 0x02 ? "A2" : revision == 0x03 ? "A3" :
++                      revision == 0x10 ? "B0" : revision == 0x20 ? "C0" : 
++                      revision == 0x21 ? "C1" : "UNKNOWN");
++          
++              snc_downgrade++;
++          }
++      }
++
++      for (pcip = NULL; (pcip = pci_find_device (PCI_VENDOR_ID_INTEL, 0x510, pcip)) != NULL; )
++      {
++          pci_read_config_byte (pcip, PCI_REVISION_ID, &revision);
++          
++          
++          if (revision >= 0x22)
++              sioh_good++;
++          else
++          {
++              printk ("elan%d: SIOH revsision %x (%s)\n", dev->dev_instance, revision,
++                      revision == 0x10 ? "C0" : revision == 0x20 ? "C0" : 
++                      revision == 0x21 ? "C1" : revision == 0x22 ? "C2" : "UNKNOWN");
++
++              sioh_downgrade++;
++          }
++      }
++
++      if (optimise_pci_bus < 2 && (sioh_downgrade || snc_downgrade))
++          printk ("elan%d: unable to optimise as SNC/SIOH below required C1/C2 steppings\n", dev->dev_instance);
++      else if (optimise_pci_bus < 2 && (sioh_good == 0 || snc_good == 0))
++          printk ("elan%d: unable to optimise as cannot determine SNC/SIOH revision\n", dev->dev_instance);
++      else
++          elan4_update_intel_p64h2 (dev, bridge);
++    }
++#endif
++    
++#ifdef __i386
++    if ((ectrl & ECTRL_INITIALISATION_MODE) == PciX100to133MHz)
++      elan4_update_intel_p64h2 (dev, bridge);
++#endif            
++    return 0;
++}
++
++int
++elan4_optimise_intel_pxh (ELAN4_DEV *dev, struct pci_dev *pdev)
++{
++    dev->dev_devinfo.dev_params.values[ELAN4_PARAM_DRIVER_FEATURES] |= ELAN4_FEATURE_64BIT_READ;
++
++    return 0;
++}
++
++void
++elan4_optimise_serverworks_ciobx2 (ELAN4_DEV *dev)
++{
++    struct pci_dev *pdev = dev->dev_osdep.pdev;
++    struct pci_dev *pcip;
++    unsigned char   bus;
++    unsigned int    dor;
++    
++    /* Find the CIOBX2 for our bus number */
++    for (pcip = NULL; (pcip = pci_find_device (PCI_VENDOR_ID_SERVERWORKS, 0x0101, pcip)) != NULL;)
++    {
++      pci_read_config_byte (pcip, 0x44 /* BUSNUM */, &bus);
++      
++      if (pdev->bus->number == bus)
++      {
++          printk ("elan%d: optimise CIOBX2 : setting DOR to disable read pipe lining\n", dev->dev_instance);
++
++          pci_read_config_dword (pcip, 0x78 /* DOR */, &dor);
++          pci_write_config_dword (pcip, 0x78 /* DOR */, dor | (1 << 16));
++
++          printk ("elan%d: disabling write-combining on ServerWorks chipset\n", dev->dev_instance);
++          dev->dev_devinfo.dev_params.values[ELAN4_PARAM_DRIVER_FEATURES] |= ELAN4_FEATURE_NO_WRITE_COMBINE;
++      }
++    }
++}
++
++int
++elan4_optimise_bus (ELAN4_DEV *dev)
++{
++    struct pci_dev *pdev = dev->dev_osdep.pdev;
++
++    if (pdev->bus && pdev->bus->self) 
++    {
++      struct pci_dev *bridge = pdev->bus->self;
++      
++      if (bridge->vendor == PCI_VENDOR_ID_INTEL && bridge->device == 0x1460 /* Intel P64H2 */)
++          return elan4_optimise_intel_p64h2 (dev, pdev);
++
++      if ((bridge->vendor == PCI_VENDOR_ID_INTEL && bridge->device == 0x0329) /* Intel 6700PXH Fn 0 */ ||
++          (bridge->vendor == PCI_VENDOR_ID_INTEL && bridge->device == 0x032a) /* Intel 6700PXH Fn 2 */ ||
++          (bridge->vendor == PCI_VENDOR_ID_INTEL && bridge->device == 0x032c) /* Intel 6702PXH */ ||
++          (bridge->vendor == PCI_VENDOR_ID_INTEL && bridge->device == 0x0320) /* Intel PXH-D */)
++          return elan4_optimise_intel_pxh (dev, pdev);
++    }
++
++    if (pci_find_device (PCI_VENDOR_ID_HP, 0x122e, NULL) != NULL)             /* on HP ZX1 set the relaxed ordering  */
++      dev->dev_pteval = PTE_RelaxedOrder;                                     /* bit to get better DMA bandwidth. */
++
++    if (pci_find_device (PCI_VENDOR_ID_SERVERWORKS, 0x0101, NULL) != NULL)    /* ServerWorks CIOBX2 */
++      elan4_optimise_serverworks_ciobx2 (dev);
++
++    return 0;
++}
++
++int
++elan4_pciinit (ELAN4_DEV *dev)
++{
++    int res;
++    u32 value;
++    u16 command;
++    u8 cacheline;
++    unsigned long flags;
++
++    if (optimise_pci_bus && (res = elan4_optimise_bus (dev)) <0)
++      return (res);
++
++    if ((res = pci_enable_device (dev->dev_osdep.pdev)) < 0)
++      return (res);
++
++    pci_read_config_dword (dev->dev_osdep.pdev, PCI_ELAN_CONTROL, &value);
++    if ((value & ECTRL_INITIALISATION_MODE) == Pci2_2)
++      printk ("elan%d: is an elan4%c (PCI-2.2)\n", dev->dev_instance, 'a' + dev->dev_devinfo.dev_revision_id);
++    else
++    {
++      switch (value & ECTRL_INITIALISATION_MODE)
++      {
++      case PciX50To66MHz:
++          printk ("elan%d: is an elan4%c (PCI-X 50-66)\n", dev->dev_instance, 'a' + dev->dev_devinfo.dev_revision_id);
++          break;
++          
++      case PciX66to100MHz:
++          printk ("elan%d: is an elan4%c (PCI-X 66-100)\n", dev->dev_instance, 'a' + dev->dev_devinfo.dev_revision_id);
++          break;
++          
++      case PciX100to133MHz:
++          printk ("elan%d: is an elan4%c (PCI-X 100-133)\n", dev->dev_instance, 'a' + dev->dev_devinfo.dev_revision_id);
++          break;
++          
++      default:
++          printk ("elan%d: Invalid PCI-X mode\n", dev->dev_instance);
++          return (-EINVAL);
++      }
++    }
++
++    /* initialise the elan pll control register */
++    pci_read_config_dword (dev->dev_osdep.pdev, PCI_ELAN_PLL_CONTROL, &value);
++
++    if (elan4_pll_cfg)
++    {
++      printk ("elan%d: setting pll control to %08x\n", dev->dev_instance, elan4_pll_cfg);
++
++      pci_write_config_dword (dev->dev_osdep.pdev, PCI_ELAN_PLL_CONTROL, elan4_pll_cfg);
++    }
++    else
++    {
++      if (dev->dev_devinfo.dev_revision_id == PCI_REVISION_ID_ELAN4_REVA)
++          pci_write_config_dword (dev->dev_osdep.pdev, PCI_ELAN_PLL_CONTROL, 
++                                  (value & ~ECTRL_SYS_CLOCK_RATIO_MASK) | ECTRL_SYS_CLOCK_RATIO_4_3);
++      else
++          pci_write_config_dword (dev->dev_osdep.pdev, PCI_ELAN_PLL_CONTROL, 
++                                  (value & ~ECTRL_SYS_CLOCK_RATIO_MASK) | ECTRL_SYS_CLOCK_RATIO_6_5 | SysPll_FeedForwardISel0 | SysPll_FeedForwardISel1);
++    } 
++
++    /* initialise the elan control register */
++    pci_read_config_dword (dev->dev_osdep.pdev, PCI_ELAN_CONTROL, &value);
++
++    value = ((15 << ECTRL_IPROC_HIGH_PRI_TIME_SHIFT) |
++           (15 << ECTRL_OTHER_HIGH_PRI_TIME_SHIFT) |
++           (value & ECTRL_28_NOT_30_BIT_LOCAL_BAR) |
++           (dev->dev_topaddrmode ? ECTRL_ExtraMasterAddrBits : 0) |
++           ECTRL_ENABLE_LATENCY_RESET | 
++           ECTRL_ENABLE_WRITEBURSTS | 
++           ECTRL_ENABLE_2_2READBURSTS);
++
++#ifdef LINUX_SPARC
++    value &= ~(ECTRL_ENABLE_LATENCY_RESET | ECTRL_ENABLE_WRITEBURSTS);
++#endif
++
++    pci_write_config_dword (dev->dev_osdep.pdev, PCI_ELAN_CONTROL, value | ECTRL_SOFTWARE_INTERNAL_RESET);
++
++    switch (dev->dev_devinfo.dev_revision_id)
++    {
++    case PCI_REVISION_ID_ELAN4_REVA:
++      /* Delay 10ms here if we've changed the sysclock ratio */
++      /* to allow the PLL to stabalise before proceeding */
++      udelay (10000);
++      break;
++      
++    case PCI_REVISION_ID_ELAN4_REVB:
++    {
++      unsigned char val = read_i2c (dev, I2cLedsValue);
++
++      /* On RevB we have to explicitly reset the PLLs */
++      pci_read_config_word (dev->dev_osdep.pdev, PCI_COMMAND, &command);
++
++      write_i2c (dev, I2cLedsValue, val | 0x80);
++      udelay (1000);
++
++      /* Issue the PLL counter reset and immediately inhibit all pci interaction 
++       * while the PLL is recovering. The write to the PCI_COMMAND register has 
++       * to occur within 50uS of the write to the i2c registers */
++      local_irq_save (flags);
++      write_i2c (dev, I2cLedsValue, val & ~0x80);
++      pci_write_config_word (dev->dev_osdep.pdev, PCI_COMMAND, (1 << 10) /* PCI_COMMAND_DISABLE_INT */);
++      local_irq_restore (flags);
++
++      /* Wait for the write to occur and for the PLL to regain lock */
++      udelay (20000); udelay (20000);
++
++      /* Re-enable pci interaction and clear any spurious errors deteced */
++      pci_write_config_word (dev->dev_osdep.pdev, PCI_STATUS, PCI_STATUS_DETECTED_PARITY | PCI_STATUS_SIG_SYSTEM_ERROR);
++      pci_write_config_word (dev->dev_osdep.pdev, PCI_COMMAND, command);
++      break;
++    }
++    }
++
++    pci_write_config_dword (dev->dev_osdep.pdev, PCI_ELAN_CONTROL, value);
++
++    /* Enable master accesses */
++    pci_set_master (dev->dev_osdep.pdev);
++
++    /* Verify that the memWrInvalidate bit is set */
++    pci_read_config_word (dev->dev_osdep.pdev, PCI_COMMAND, &command);
++    pci_read_config_byte (dev->dev_osdep.pdev, PCI_CACHE_LINE_SIZE, &cacheline);
++
++    if ((command & PCI_COMMAND_INVALIDATE) == 0)
++    {
++      printk ("elan%d: enable MemWrInvalidate (cacheline %d)\n",
++              dev->dev_instance, cacheline * 4);
++
++      pci_write_config_word (dev->dev_osdep.pdev, PCI_COMMAND, command | PCI_COMMAND_INVALIDATE);
++    }
++
++    return (0);
++}
++
++void
++elan4_updatepll (ELAN4_DEV *dev, unsigned int val)
++{
++    u32 value;
++
++    if (elan4_pll_cfg == 0)
++    {
++      pci_read_config_dword (dev->dev_osdep.pdev, PCI_ELAN_PLL_CONTROL, &value);
++
++      pci_write_config_dword (dev->dev_osdep.pdev, PCI_ELAN_PLL_CONTROL, 
++                              (value & ~ECTRL_SYS_CLOCK_RATIO_MASK) | val);
++
++      /* Delay 10ms here if we've changed the sysclock ratio */
++      /* to allow the PLL to stabalise before proceeding */
++      udelay (10000);
++    } 
++}
++
++void
++elan4_pcifini (ELAN4_DEV *dev)
++{
++    u32 value;
++
++    pci_read_config_dword (dev->dev_osdep.pdev, PCI_ELAN_CONTROL, &value);
++    pci_write_config_dword (dev->dev_osdep.pdev, PCI_ELAN_CONTROL, value | ECTRL_SOFTWARE_INTERNAL_RESET);
++    pci_write_config_dword (dev->dev_osdep.pdev, PCI_ELAN_CONTROL, value);
++
++    pci_disable_device (dev->dev_osdep.pdev);
++}
++
++void
++elan4_pcierror (ELAN4_DEV *dev)
++{
++    struct pci_dev *pci = dev->dev_osdep.pdev;
++    u8  type;
++    u16 status, cmd;
++    u32 physlo, physhi, control;
++    
++    printk("elan%d: pci error has occurred\n", dev->dev_instance);
++
++    pci_read_config_word  (pci, PCI_STATUS,            &status);
++    pci_read_config_word  (pci, PCI_COMMAND,             &cmd);
++    pci_read_config_dword (pci, PCI_ELAN_CONTROL,      &control);
++
++    if (control & ECTRL_REC_SPLIT_COMP_MESSAGE)
++    {
++      u32 message, attr;
++      
++      pci_write_config_dword (pci, PCI_ELAN_CONTROL, control & ~ECTRL_SELECT_SPLIT_MESS_ATTR);
++      pci_read_config_dword (pci, PCI_ELAN_SPLIT_MESSAGE_VALUE, &message);
++      pci_write_config_dword (pci, PCI_ELAN_CONTROL, control | ECTRL_SELECT_SPLIT_MESS_ATTR);
++      pci_read_config_dword (pci, PCI_ELAN_SPLIT_MESSAGE_VALUE, &attr);
++
++      printk ("elan%d: pcierror - received split completion message - attr=%08x, message=%08x\n", 
++              dev->dev_instance, attr, message);
++
++      pci_write_config_dword (pci, PCI_ELAN_CONTROL, control | ECTRL_REC_SPLIT_COMP_MESSAGE); /* clear the error */
++    }
++    else
++    {
++      pci_read_config_dword (pci, PCI_ELAN_PARITY_ADDR_LO, &physlo);
++      pci_read_config_dword (pci, PCI_ELAN_PARITY_ADDR_HI, &physhi);
++      pci_read_config_byte  (pci, PCI_ELAN_PARITY_TYPE,    &type);
++      
++      printk ("elan%d: pcierror - status %x cmd %4x physaddr %08x%08x type %x\n", 
++              dev->dev_instance, status, cmd, physhi, physlo, type);
++      
++      if (status & PCI_STATUS_PARITY)
++          printk ("elan%d: parity error signalled (PERR)\n", dev->dev_instance);
++      if (status & PCI_STATUS_DETECTED_PARITY)
++          printk ("elan%d: detected parity error\n", dev->dev_instance);
++      if (status & PCI_STATUS_REC_MASTER_ABORT)
++          printk ("elan%d: received master abort\n", dev->dev_instance);
++      if (status & PCI_STATUS_REC_TARGET_ABORT)
++          printk ("elan%d: received target abort\n", dev->dev_instance);
++      if (status & PCI_STATUS_SIG_SYSTEM_ERROR)
++          printk ("elan%d: signalled SERR\n", dev->dev_instance);
++      if (status & PCI_STATUS_SIG_TARGET_ABORT)
++          printk ("elan%d: signalled target abort\n", dev->dev_instance);
++
++      pci_write_config_word (pci, PCI_STATUS, status);        /* clear the errors */
++    }
++
++    DISABLE_INT_MASK (dev, INT_PciMemErr);
++
++#ifdef notdef
++    panic ("elan%d: pcierror\n", dev->dev_instance);          /* better panic ! */
++#endif
++}
++
++static irqreturn_t
++elan4_irq (int irq, void *arg, struct pt_regs *regs)
++{
++    if (elan4_1msi0 ((ELAN4_DEV *) arg))
++          return IRQ_HANDLED;
++    else
++          return IRQ_NONE;
++}
++
++ioaddr_t
++elan4_map_device (ELAN4_DEV *dev, unsigned bar, unsigned off, unsigned size, ELAN4_MAP_HANDLE *handle)
++{
++    return (ioaddr_t) ioremap_nocache (pci_resource_start (dev->dev_osdep.pdev, bar) + off, size);
++}
++
++void
++elan4_unmap_device (ELAN4_DEV *dev, ioaddr_t ptr, unsigned size, ELAN4_MAP_HANDLE *handle)
++{
++    iounmap ((void *) ptr);
++}
++
++unsigned long
++elan4_resource_len (ELAN4_DEV *dev, unsigned bar)
++{
++    return (pci_resource_len (dev->dev_osdep.pdev, bar));
++}
++
++void
++elan4_configure_writecombining (ELAN4_DEV *dev)
++{
++    if ((dev->dev_devinfo.dev_params.values[ELAN4_PARAM_DRIVER_FEATURES] & ELAN4_FEATURE_NO_WRITE_COMBINE))
++      return;
++
++#if (defined(__i386) || defined(__x86_64)) && defined (X86_FEATURE_PAT)
++
++#ifndef boot_cpu_has
++#  define boot_cpu_has(bit)      test_bit(bit, boot_cpu_data.x86_capability)
++#endif
++
++    /* Try to utilise PAT entries which already exist */
++    if (boot_cpu_has (X86_FEATURE_PAT))
++    {
++      unsigned int val0, val1, i;
++      int slot = -1;
++
++      /* Read the IA32CR_PAT MSR register and see if a slot is
++       * set for write-combinig.  Note we assume that all CPUs 
++       * are configured the same like they're supposed to. */
++      rdmsr (0x277, val0, val1);
++      
++      /* Check for PAT write combining entry (value 0x01) */
++      for (i = 0; i < 4; i++, val0 >>= 8)
++          if ((val0 & 0xff) == 0x01)
++              slot = i;
++      for (i = 4; i < 8; i++, val1 >>= 8)
++          if ((val1 & 0xff) == 0x01)
++              slot = i;
++
++      if (slot >= 0)
++      {
++          printk ("elan%d: using PAT for write combining (slot %d)\n", dev->dev_instance, slot);
++
++          pat_pteval = ((slot & 4) ? _PAGE_PSE : 0) | ((slot & 2) ? _PAGE_PCD : 0) | ((slot & 1) ? _PAGE_PWT : 0);
++          return;
++      }
++    }
++#endif
++
++#ifdef CONFIG_MTRR
++    /* try and initialise the MTRR registers to enable write-combining */
++    dev->dev_osdep.sdram_mtrr = mtrr_add (pci_resource_start (dev->dev_osdep.pdev, ELAN4_BAR_SDRAM), 
++                                        pci_resource_len   (dev->dev_osdep.pdev, ELAN4_BAR_SDRAM),
++                                        MTRR_TYPE_WRCOMB, 1);
++    if (dev->dev_osdep.sdram_mtrr < 0)
++      printk ("elan%d: cannot configure MTRR for sdram\n", dev->dev_instance);
++    
++    if (dev->dev_devinfo.dev_revision_id == PCI_REVISION_ID_ELAN4_REVB)
++    {
++      unsigned int cqreorder = dev->dev_cqcount >> 1;
++      unsigned int cqcount   = dev->dev_cqcount  - cqreorder;
++
++      dev->dev_osdep.regs_mtrr = mtrr_add (pci_resource_start (dev->dev_osdep.pdev, ELAN4_BAR_REGISTERS) + 
++                                           (dev->dev_cqoffset + cqreorder) * CQ_CommandMappingSize,
++                                           CQ_CommandMappingSize * cqcount,
++                                           MTRR_TYPE_WRCOMB, 1);
++      
++      if (dev->dev_osdep.regs_mtrr < 0)
++          printk ("elan%d: cannot configure MTRR for command ports\n", dev->dev_instance);
++      else
++          dev->dev_cqreorder = cqreorder;
++    }
++#endif
++}
++
++void
++elan4_unconfigure_writecombining (ELAN4_DEV *dev)
++{
++    if ((dev->dev_devinfo.dev_params.values[ELAN4_PARAM_DRIVER_FEATURES] & ELAN4_FEATURE_NO_WRITE_COMBINE))
++      return;
++
++#ifdef CONFIG_MTRR
++    if (pat_pteval == -1)
++    {
++      if (dev->dev_osdep.sdram_mtrr >=0 )
++          mtrr_del (dev->dev_osdep.sdram_mtrr, pci_resource_start (dev->dev_osdep.pdev, ELAN4_BAR_SDRAM), 
++                    pci_resource_len   (dev->dev_osdep.pdev, ELAN4_BAR_SDRAM));
++      
++      if (dev->dev_cqreorder && dev->dev_osdep.regs_mtrr >= 0)
++          mtrr_del (dev->dev_osdep.regs_mtrr, 
++                    pci_resource_start (dev->dev_osdep.pdev, ELAN4_BAR_REGISTERS) + 
++                    (dev->dev_cqoffset + dev->dev_cqreorder) * CQ_CommandMappingSize,
++                    CQ_CommandMappingSize * (dev->dev_cqcount >> 1));
++    }
++#endif
++}
++
++EXPORT_SYMBOL(elan4_reference_device);
++EXPORT_SYMBOL(elan4_dereference_device);
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan4/i2c.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan4/i2c.c     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan4/i2c.c  2005-05-11 12:10:12.450930752 -0400
+@@ -0,0 +1,248 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: i2c.c,v 1.4 2004/01/07 13:37:45 jon Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/i2c.c,v $*/
++#include <qsnet/kernel.h>
++
++#include <elan4/sdram.h>
++#include <elan4/debug.h>
++#include <elan4/device.h>
++#include <elan4/commands.h>
++
++#include <elan4/i2c.h>
++#include <elan4/pci.h>
++#include <elan4/ioctl.h>
++#include <elan4/registers.h>
++
++#define I2C_POLL_LIMIT                8
++
++static int
++i2c_poll_busy (ELAN4_DEV *dev)
++{
++    int t    = 100;
++    int loop = 0;
++    volatile unsigned char val;
++
++    /* wait for any led I2C operation to finish */
++    while (((val = read_i2c (dev, I2cPortControl)) & I2cCntl_I2cPortBusy) && loop++ < I2C_POLL_LIMIT)
++    {
++      DELAY (t);
++      
++      if (t < 500000)
++          t <<= 1;
++    }
++    if (loop >= I2C_POLL_LIMIT)
++    {
++      printk ("elan%d: I2c has timed out waiting for I2cPortBusy to clear!\n", dev->dev_instance);
++      printk ("elan%d: I2cPortControl=%x I2cLedBase=%x I2cStatus=%x\n",
++              dev->dev_instance, val, read_i2c (dev, I2cLedBase), read_i2c (dev, I2cStatus));
++    }
++
++    return val;
++}
++
++static int
++i2c_poll_stopped (ELAN4_DEV *dev)
++{
++    int t    = 100;
++    int loop = 0;
++    unsigned char val=0, newval;
++
++    /* wait for any led I2C operation to finish. Must see it stopped at least twice */
++    while (!(((newval = read_i2c (dev, I2cPortControl)) & I2cCntl_I2cStopped) &&
++            (val & I2cCntl_I2cStopped)) &&
++            (loop++ < I2C_POLL_LIMIT))
++    {
++      DELAY (t);
++      
++      if (t < 500000)
++          t <<= 1;
++      val = newval;
++    }
++
++    return val;
++}
++
++int
++i2c_disable_auto_led_update (ELAN4_DEV *dev)
++{
++    spin_lock (&dev->dev_i2c_lock);
++
++    if (dev->dev_i2c_led_disabled++ == 0)
++    {
++      write_i2c (dev, I2cLedBase, read_i2c (dev, I2cLedBase) & ~I2cCntl_I2cUpdatingLedReg);
++
++      if (! (i2c_poll_stopped (dev) & I2cCntl_I2cStopped))
++      {
++          write_i2c (dev, I2cLedBase, read_i2c (dev, I2cLedBase) | I2cCntl_I2cUpdatingLedReg);
++          
++          spin_unlock (&dev->dev_i2c_lock);
++          
++          return -EAGAIN;
++      }
++      
++      write_i2c (dev, I2cStatus, read_i2c (dev, I2cStatus) & ~I2cCntl_SampleNewLedValues);
++    }
++
++    spin_unlock (&dev->dev_i2c_lock);
++
++    return 0;
++}
++
++void
++i2c_enable_auto_led_update (ELAN4_DEV *dev)
++{
++    spin_lock (&dev->dev_i2c_lock);
++    if (--dev->dev_i2c_led_disabled == 0)
++    {
++      write_i2c (dev, I2cLedBase, read_i2c (dev, I2cLedBase) | I2cCntl_I2cUpdatingLedReg);
++      write_i2c (dev, I2cStatus, read_i2c (dev, I2cStatus) | I2cCntl_SampleNewLedValues);
++    }
++
++    spin_unlock (&dev->dev_i2c_lock);
++}
++
++int
++i2c_write (ELAN4_DEV *dev, unsigned int address, unsigned int count, unsigned char *data)
++{
++    int i;
++
++    if (! (i2c_poll_busy (dev) & I2cCntl_I2cStopped))
++      return -EAGAIN;
++    
++    write_i2c (dev, I2cWrData,      I2C_WRITE_ADDR(address));
++    write_i2c (dev, I2cPortControl, I2cCntl_I2cPortWrite);
++    
++    if (i2c_poll_busy (dev) & I2cCntl_I2cPortAccFailed)
++      return -ENXIO;
++
++    for (i = 0; i < count; i++)
++    {
++      write_i2c (dev, I2cWrData, data[i]);
++      write_i2c (dev, I2cPortControl, I2cCntl_I2cPortWrite | (i == (count-1) ? I2cCntl_I2cPortGenStopBit : 0));
++    }
++
++    return 0;
++}
++
++int
++i2c_read (ELAN4_DEV *dev, unsigned int address, unsigned int count, unsigned char *data)
++{
++    int i;
++
++    if (! (i2c_poll_busy (dev) & I2cCntl_I2cStopped))
++      return -EAGAIN; /* not idle */ 
++
++    write_i2c (dev, I2cWrData,      I2C_READ_ADDR(address));
++    write_i2c (dev, I2cPortControl, I2cCntl_I2cPortWrite);
++
++    if (i2c_poll_busy (dev) & I2cCntl_I2cPortAccFailed)
++      return -ENXIO;
++    
++    for (i = 0; i < count; i++)
++    {
++      write_i2c (dev, I2cWrData, 0xff);
++      write_i2c (dev, I2cPortControl, I2cCntl_I2cPortRead | ((i == count-1) ? I2cCntl_I2cPortGenStopBit : 0));
++
++      i2c_poll_busy (dev);
++
++      data[i] = read_i2c (dev, I2cRdData);
++    }
++
++    return 0;
++}
++
++int
++i2c_writereg (ELAN4_DEV *dev, unsigned int address, unsigned int reg, unsigned int count, unsigned char *data)
++{
++    int i;
++
++    if (! (i2c_poll_busy (dev) & I2cCntl_I2cStopped))
++      return -EAGAIN; /* not idle */ 
++
++    write_i2c (dev, I2cWrData,      I2C_WRITE_ADDR(address));
++    write_i2c (dev, I2cPortControl, I2cCntl_I2cPortWrite);
++
++    if (i2c_poll_busy (dev) & I2cCntl_I2cPortAccFailed)
++      return -ENXIO;
++    
++    write_i2c (dev, I2cWrData,      reg);
++    write_i2c (dev, I2cPortControl, I2cCntl_I2cPortWrite);
++
++    if (i2c_poll_busy (dev) & I2cCntl_I2cPortAccFailed)
++      return -ENXIO;
++    
++    for (i = 0; i < count; i++)
++    {
++      write_i2c (dev, I2cWrData, data[i]);
++      write_i2c (dev, I2cPortControl, I2cCntl_I2cPortWrite | ((i == count-1) ? I2cCntl_I2cPortGenStopBit : 0));
++
++      if (i2c_poll_busy (dev) & I2cCntl_I2cPortAccFailed)
++          printk (" i2c_writereg: off %d failed\n", i);
++    }
++
++    return 0;
++}
++
++int
++i2c_readreg (ELAN4_DEV *dev, unsigned int address, unsigned int reg, unsigned int count, unsigned char *data)
++{
++    if (! (i2c_poll_busy (dev) & I2cCntl_I2cStopped))
++      return -EAGAIN; /* not idle */ 
++
++    write_i2c (dev, I2cWrData,      I2C_WRITE_ADDR(address));
++    write_i2c (dev, I2cPortControl, I2cCntl_I2cPortWrite);
++
++    if (i2c_poll_busy (dev) & I2cCntl_I2cPortAccFailed)
++      return -ENXIO;
++    
++    write_i2c (dev, I2cWrData,      reg);
++    write_i2c (dev, I2cPortControl, I2cCntl_I2cPortWrite | I2cCntl_I2cPortGenStopBit);
++
++    if (i2c_poll_busy (dev) & I2cCntl_I2cPortAccFailed)
++      return -ENXIO;
++
++    return i2c_read (dev, address, count, data);
++}
++
++int
++i2c_read_rom (ELAN4_DEV *dev, unsigned int addr, unsigned int len, unsigned char *data)
++{
++    unsigned int top = addr + len;
++    int res;
++
++    if ((res = i2c_disable_auto_led_update (dev)) == 0)
++    {
++      /* read the rom in chunks that don't span the block boundary */
++      while (addr < top)
++      {
++          unsigned int thisnob  = top - addr;
++          unsigned int blocknob = I2C_24LC16B_BLOCKSIZE - I2C_24LC16B_BLOCKOFFSET(addr);
++          
++          if (thisnob > blocknob)
++              thisnob = blocknob;
++
++          if ((res = i2c_readreg (dev, I2C_EEPROM_ADDR + I2C_24LC16B_BLOCKADDR(addr),
++                                  I2C_24LC16B_BLOCKOFFSET(addr), thisnob, data)) < 0)
++              break;
++          
++          addr += thisnob;
++          data += thisnob;
++      }
++
++      i2c_enable_auto_led_update (dev);
++    }
++    return res;
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan4/intcookie.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan4/intcookie.c       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan4/intcookie.c    2005-05-11 12:10:12.451930600 -0400
+@@ -0,0 +1,371 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: intcookie.c,v 1.14.2.1 2005/03/01 12:01:57 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/intcookie.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan4/debug.h>
++#include <elan4/types.h>
++#include <elan/capability.h>
++#include <elan4/intcookie.h>
++
++static INTCOOKIE_TABLE *intcookie_tables;
++static spinlock_t     intcookie_table_lock;
++
++/*
++ * intcookie_drop_entry:
++ *   drop the reference to a cookie held 
++ *   by the cookie table
++ */
++static void
++intcookie_drop_entry (INTCOOKIE_ENTRY *ent)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&ent->ent_lock, flags);
++    if (--ent->ent_ref != 0)
++    {
++      ent->ent_fired = ent->ent_cookie;
++      kcondvar_wakeupall (&ent->ent_wait, &ent->ent_lock);
++
++      spin_unlock_irqrestore (&ent->ent_lock, flags);
++    }
++    else
++    {
++      spin_unlock_irqrestore (&ent->ent_lock, flags);
++
++      spin_lock_destroy (&ent->ent_lock);
++      kcondvar_destroy (&ent->ent_wait);
++
++      KMEM_FREE (ent, sizeof (INTCOOKIE_ENTRY));
++    }
++}
++
++void
++intcookie_init()
++{
++    spin_lock_init (&intcookie_table_lock);
++}
++
++void
++intcookie_fini()
++{
++    spin_lock_destroy (&intcookie_table_lock);
++}
++
++INTCOOKIE_TABLE *
++intcookie_alloc_table (ELAN_CAPABILITY *cap)
++{
++    INTCOOKIE_TABLE *tbl, *ntbl;
++    ELAN_CAPABILITY *ncap;
++    
++    KMEM_ZALLOC (ntbl, INTCOOKIE_TABLE *, sizeof (INTCOOKIE_TABLE), 1);
++
++    if (ntbl == NULL)
++      return (NULL);
++
++    KMEM_ALLOC (ncap, ELAN_CAPABILITY *, ELAN_CAP_SIZE(cap), 1);
++
++    if (ncap == NULL)
++    {
++      KMEM_FREE (ntbl, sizeof (INTCOOKIE_TABLE));
++      return (NULL);
++    }
++
++    spin_lock (&intcookie_table_lock);
++    
++    for (tbl = intcookie_tables; tbl; tbl = tbl->tbl_next)
++      if (ELAN_CAP_MATCH (tbl->tbl_cap, cap) && tbl->tbl_cap->cap_mycontext == cap->cap_mycontext)
++          break;
++    
++    if (tbl != NULL)
++      tbl->tbl_ref++;
++    else
++    {
++      spin_lock_init (&ntbl->tbl_lock);
++
++      ntbl->tbl_cap     = ncap;
++      ntbl->tbl_ref     = 1;
++      ntbl->tbl_entries = NULL;
++      
++      /* Save supplied cap */
++      memcpy (ncap, cap, ELAN_CAP_SIZE(cap));
++
++      if ((ntbl->tbl_next = intcookie_tables) != NULL)
++          intcookie_tables->tbl_prev = ntbl;
++      intcookie_tables = ntbl;
++      ntbl->tbl_prev = NULL;
++    }
++    spin_unlock (&intcookie_table_lock);
++
++    if (tbl == NULL)
++      return (ntbl);
++    else
++    {
++      KMEM_FREE (ntbl, sizeof (INTCOOKIE_TABLE));
++      KMEM_FREE (ncap, ELAN_CAP_SIZE(cap));
++      return (tbl);
++    }    
++}
++
++void
++intcookie_free_table (INTCOOKIE_TABLE *tbl)
++{
++    INTCOOKIE_ENTRY *ent;
++
++    spin_lock (&intcookie_table_lock);
++    if (tbl->tbl_ref > 1)
++    {
++      tbl->tbl_ref--;
++      spin_unlock (&intcookie_table_lock);
++      return;
++    }
++    
++    if (tbl->tbl_prev)
++      tbl->tbl_prev->tbl_next = tbl->tbl_next;
++    else
++      intcookie_tables = tbl->tbl_next;
++    if (tbl->tbl_next)
++      tbl->tbl_next->tbl_prev = tbl->tbl_prev;
++    
++    spin_unlock (&intcookie_table_lock);
++    
++    /* NOTE - table no longer visible to other threads
++     *        no need to aquire tbl_lock */
++    while ((ent = tbl->tbl_entries) != NULL)
++    {
++      if ((tbl->tbl_entries = ent->ent_next) != NULL)
++          ent->ent_next->ent_prev = NULL;
++      
++      intcookie_drop_entry (ent);
++    }
++    spin_lock_destroy (&tbl->tbl_lock);
++
++    KMEM_FREE (tbl->tbl_cap, ELAN_CAP_SIZE(tbl->tbl_cap));
++    KMEM_FREE (tbl, sizeof (INTCOOKIE_TABLE));
++}
++
++int
++intcookie_alloc (INTCOOKIE_TABLE *tbl, ELAN4_INTCOOKIE cookie)
++{
++    INTCOOKIE_ENTRY *ent, *nent;
++    unsigned long flags;
++
++    KMEM_ZALLOC (nent, INTCOOKIE_ENTRY *, sizeof (INTCOOKIE_ENTRY), 1);
++
++    if (nent == NULL)
++      return (-ENOMEM);
++    
++    spin_lock_irqsave (&tbl->tbl_lock, flags);
++    for (ent = tbl->tbl_entries; ent; ent = ent->ent_next)
++      if (ent->ent_cookie == cookie)
++          break;
++
++    if (ent == NULL)
++    {
++      kcondvar_init (&nent->ent_wait);
++      spin_lock_init (&nent->ent_lock);
++
++      nent->ent_ref    = 1;
++      nent->ent_cookie = cookie;
++
++      if ((nent->ent_next = tbl->tbl_entries) != NULL)
++          tbl->tbl_entries->ent_prev = nent;
++      tbl->tbl_entries = nent;
++      nent->ent_prev = NULL;
++    }
++    spin_unlock_irqrestore (&tbl->tbl_lock, flags);
++
++    if (ent == NULL)
++      return (0);
++    else
++    {
++      KMEM_FREE (nent, sizeof (INTCOOKIE_ENTRY));
++      return (-EINVAL);
++    }
++}
++
++int
++intcookie_free (INTCOOKIE_TABLE *tbl, ELAN4_INTCOOKIE cookie)
++{
++    INTCOOKIE_ENTRY *ent;
++    unsigned long flags;
++
++    spin_lock_irqsave (&tbl->tbl_lock, flags);
++    for (ent = tbl->tbl_entries; ent; ent = ent->ent_next)
++      if (ent->ent_cookie == cookie)
++          break;
++    
++    if (ent == NULL)
++    {
++      spin_unlock_irqrestore (&tbl->tbl_lock, flags);
++      return (-EINVAL);
++    }
++
++    if (ent->ent_prev == NULL)
++      tbl->tbl_entries = ent->ent_next;
++    else
++      ent->ent_prev->ent_next = ent->ent_next;
++
++    if (ent->ent_next != NULL)
++      ent->ent_next->ent_prev = ent->ent_prev;
++    
++    spin_unlock_irqrestore (&tbl->tbl_lock, flags);
++
++    intcookie_drop_entry (ent);
++
++    return (0);
++}
++
++/*
++ * intcookie_fire_cookie:
++ *    fire the cookie - this is called from the event interrupt.
++ */
++int
++intcookie_fire (INTCOOKIE_TABLE *tbl, ELAN4_INTCOOKIE cookie)
++{
++    INTCOOKIE_ENTRY *ent;
++    unsigned long flags;
++
++    spin_lock_irqsave (&tbl->tbl_lock, flags);
++    for (ent = tbl->tbl_entries; ent; ent = ent->ent_next)
++      if (ent->ent_cookie == cookie)
++          break;
++    
++    if (ent == NULL)
++    {
++      spin_unlock_irqrestore (&tbl->tbl_lock, flags);
++      return (-EINVAL);
++    }
++          
++    spin_lock (&ent->ent_lock);
++    ent->ent_fired = cookie;
++    kcondvar_wakeupall (&ent->ent_wait, &ent->ent_lock);
++    spin_unlock (&ent->ent_lock);
++
++    spin_unlock_irqrestore (&tbl->tbl_lock, flags);
++
++    return (0);
++}    
++
++int
++intcookie_fire_cap (ELAN_CAPABILITY *cap, ELAN4_INTCOOKIE cookie)
++{
++    int res;
++    INTCOOKIE_TABLE *tbl;
++
++    spin_lock (&intcookie_table_lock);
++    
++    for (tbl = intcookie_tables; tbl; tbl = tbl->tbl_next)
++      if (ELAN_CAP_MATCH (tbl->tbl_cap, cap) && tbl->tbl_cap->cap_mycontext == cap->cap_mycontext)
++          break;
++    
++    if (tbl != NULL)
++      tbl->tbl_ref++;
++
++    spin_unlock (&intcookie_table_lock);
++
++    /* No matching table found */
++    if (tbl == NULL)
++      return (-EINVAL);
++
++    /* Fire the correct cookie */
++    res = intcookie_fire (tbl, cookie);
++
++    /* Decrement reference count (and free if necessary) */
++    intcookie_free_table (tbl);
++
++    return (res);
++}
++
++/*
++ * intcookie_wait_cookie:
++ *    deschedule on a cookie if it has not already fired.
++ *    note - if the cookie is removed from the table, then
++ *           we free it off when we're woken up.
++ */
++int
++intcookie_wait (INTCOOKIE_TABLE *tbl, ELAN4_INTCOOKIE cookie)
++{
++    INTCOOKIE_ENTRY *ent;
++    unsigned long flags;
++    int res;
++    
++    spin_lock_irqsave (&tbl->tbl_lock, flags);
++    for (ent = tbl->tbl_entries; ent; ent = ent->ent_next)
++      if (ent->ent_cookie == cookie)
++          break;
++    
++    if (ent == NULL)
++    {
++      spin_unlock_irqrestore (&tbl->tbl_lock, flags);
++      return (-EINVAL);
++    }
++
++    spin_lock (&ent->ent_lock);
++    spin_unlock (&tbl->tbl_lock);
++
++    if (ent->ent_fired != 0)
++    {
++      spin_unlock_irqrestore (&ent->ent_lock, flags);
++      return (0);
++    }
++
++    ent->ent_ref++;
++    kcondvar_waitsig (&ent->ent_wait, &ent->ent_lock, &flags);
++    
++    res = ent->ent_fired ? 0 : -EINTR;
++
++    if (--ent->ent_ref > 0)
++      spin_unlock_irqrestore (&ent->ent_lock, flags);
++    else
++    {
++      spin_unlock_irqrestore (&ent->ent_lock, flags);
++      
++      spin_lock_destroy (&ent->ent_lock);
++      kcondvar_destroy (&ent->ent_wait);
++
++      KMEM_FREE (ent, sizeof (INTCOOKIE_ENTRY));
++    }
++
++    return (res);
++}
++
++int
++intcookie_arm (INTCOOKIE_TABLE *tbl, ELAN4_INTCOOKIE cookie)
++{
++    INTCOOKIE_ENTRY *ent;
++    unsigned long flags;
++
++    spin_lock_irqsave (&tbl->tbl_lock, flags);
++    for (ent = tbl->tbl_entries; ent; ent = ent->ent_next)
++      if (ent->ent_cookie == cookie)
++          break;
++    
++    if (ent == NULL)
++    {
++      spin_unlock_irqrestore (&tbl->tbl_lock, flags);
++      return (-EINVAL);
++    }
++          
++    spin_lock (&ent->ent_lock);
++    ent->ent_fired = 0;
++    spin_unlock (&ent->ent_lock);
++
++    spin_unlock_irqrestore (&tbl->tbl_lock, flags);
++
++    return (0);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan4/Makefile
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan4/Makefile  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan4/Makefile       2005-05-11 12:10:12.451930600 -0400
+@@ -0,0 +1,15 @@
++#
++# Makefile for Quadrics QsNet
++#
++# Copyright (c) 2002-2004 Quadrics Ltd
++#
++# File: drivers/net/qsnet/elan4/Makefile
++#
++
++
++#
++
++obj-$(CONFIG_ELAN4)   += elan4.o
++elan4-objs    := device.o i2c.o mmu.o sdram.o debug.o routetable.o trap.o user.o user_ddcq.o regions.o intcookie.o neterr.o device_Linux.o user_Linux.o procfs_Linux.o mmu_Linux.o
++
++EXTRA_CFLAGS          +=  -DDEBUG -DDEBUG_PRINTF -DDEBUG_ASSERT
+Index: linux-2.6.5/drivers/net/qsnet/elan4/Makefile.conf
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan4/Makefile.conf     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan4/Makefile.conf  2005-05-11 12:10:12.452930448 -0400
+@@ -0,0 +1,10 @@
++# Flags for generating QsNet Linux Kernel Makefiles
++MODNAME               =       elan4.o
++MODULENAME    =       elan4
++KOBJFILES     =       device.o i2c.o mmu.o sdram.o debug.o routetable.o trap.o user.o user_ddcq.o regions.o intcookie.o neterr.o device_Linux.o user_Linux.o procfs_Linux.o mmu_Linux.o
++EXPORT_KOBJS  =       device.o device_Linux.o mmu.o mmu_Linux.o procfs_Linux.o routetable.o sdram.o trap.o
++CONFIG_NAME   =       CONFIG_ELAN4
++SGALFC                =       
++# EXTRALINES START
++
++# EXTRALINES END
+Index: linux-2.6.5/drivers/net/qsnet/elan4/mmu.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan4/mmu.c     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan4/mmu.c  2005-05-11 12:10:12.453930296 -0400
+@@ -0,0 +1,862 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: mmu.c,v 1.29.6.3 2005/03/10 15:49:24 mike Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/mmu.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <qsnet/kpte.h>
++
++#include <elan4/debug.h>
++#include <elan4/device.h>
++
++int elan4_debug_mmu;
++
++/* Permission table - see ELAN4 MMU documentation */
++u_char elan4_permtable[] =
++{
++   0x00, /* 0x000000 - Disable */
++   0x00, /* 0x000000 - Unused  */
++   0x01, /* 0x000001 - Local Data Read */
++   0x03, /* 0x000011 - Local Data Write */
++   0x11, /* 0x010001 - Local Read */
++   0x10, /* 0x010000 - Local Execute */
++   0x05, /* 0x000101 - Read Only */
++   0x13, /* 0x010011 - Local Write */
++   0x20, /* 0x100000 - Local Event Access */
++   0x23, /* 0x100011 - Local Event Write Ac */
++   0xa3, /* 1x100011 - Remote Ev Loc Write */
++   0xaf, /* 1x101111 - Remote All */
++   0x07, /* 0x000111 - Remote Read Only */
++   0x0d, /* 0x001101 - Remote Write Only */
++   0x0f, /* 0x001111 - Remote Read/Write */
++   0xbf, /* 1x111111 - No Fault */
++};
++
++u_char elan4_permreadonly[] = 
++{
++    PERM_Disabled,            /* PERM_Disabled */
++    PERM_Disabled,            /* PERM_Unused */
++    PERM_LocDataRead,         /* PERM_LocDataRead */
++    PERM_LocDataRead,         /* PERM_LocDataWrite */
++    PERM_LocRead,             /* PERM_LocRead */
++    PERM_LocExecute,          /* PERM_LocExecute */
++    PERM_ReadOnly,            /* PERM_ReadOnly */
++    PERM_LocRead,             /* PERM_LocWrite */
++    PERM_LocEventOnly,                /* PERM_LocEventOnly */
++    PERM_LocDataRead,         /* PERM_LocEventWrite */
++    PERM_LocDataRead,         /* PERM_RemoteEvent */
++    PERM_ReadOnly,            /* PERM_RemoteAll */
++    PERM_RemoteReadOnly,      /* PERM_RemoteReadOnly */
++    PERM_ReadOnly,            /* PERM_RemoteWriteLocRead */
++    PERM_ReadOnly,            /* PERM_DataReadWrite */
++    PERM_ReadOnly,            /* PERM_NoFault */
++};
++
++static void
++elan4mmu_synctag (ELAN4_DEV *dev, ELAN4_HASH_ENTRY *he, int tagidx)
++{
++    E4_uint64 value = (he->he_tag[tagidx] & HE_TAG_VALID) ? he->he_tag[tagidx] & (TAG_ADDRESS_MASK | TAG_CONTEXT_MASK) : INVALID_CONTEXT;
++    
++    if (he->he_next)
++      value |= ((tagidx == 0) ? 
++                ((he->he_next->he_entry >> TAG_CHAINPTR_HIGH_SHIFT) & TAG_CHAINPTR_30TO19_MASK) :
++                ((he->he_next->he_entry << TAG_CHAINPTR_LOW_SHIFT) & TAG_CHAINPTR_18TO6_MASK));
++    else if (tagidx == 0)
++      value |= TAG_CHAINPTR_30TO19_MASK;
++    
++    MPRINTF (DBG_DEVICE, 4, "elan4mmu_synctag: he=%p tagidx=%d he->he_tag=%llx -> value=%llx\n", he, tagidx, he->he_tag[tagidx], value);
++
++    elan4_sdram_writeq (dev, he->he_entry + E4MMU_TAG_OFFSET(tagidx), value);
++}
++
++static void
++elan4mmu_chain_hents (ELAN4_DEV *dev, ELAN4_HASH_ENTRY *phe, ELAN4_HASH_ENTRY *he)
++{
++    ASSERT ((elan4_sdram_readq (dev, phe->he_entry + E4MMU_TAG_OFFSET(0)) & TAG_CHAINPTR_30TO19_MASK) == TAG_CHAINPTR_30TO19_MASK);
++
++    elan4_sdram_writeq (dev, phe->he_entry + E4MMU_TAG_OFFSET(1),
++                      ((phe->he_tag[1] & (TAG_ADDRESS_MASK | TAG_CONTEXT_MASK)) | ((he->he_entry << TAG_CHAINPTR_LOW_SHIFT) & TAG_CHAINPTR_18TO6_MASK)));
++    elan4_sdram_writeq (dev, phe->he_entry + E4MMU_TAG_OFFSET(0),
++                      ((phe->he_tag[0] & (TAG_ADDRESS_MASK | TAG_CONTEXT_MASK)) | ((he->he_entry >> TAG_CHAINPTR_HIGH_SHIFT) & TAG_CHAINPTR_30TO19_MASK)));
++}
++
++static void
++elan4mmu_writepte (ELAN4_DEV *dev, ELAN4_HASH_ENTRY *he, int tagidx, int pteidx, E4_uint64 value)
++{
++    /*
++     * NOTE - we can only change a valid PTE if we're upgrading it's permissions,
++     * any other changes should have invalidated it first. */
++
++    MPRINTF (DBG_DEVICE, 4, "elan4mmu_writepte: he=%p tagidx=%d pteidx=%x value=%llx\n", he, tagidx, pteidx, (unsigned long long) value);
++
++    if (pteidx == 3)
++    {
++      elan4_sdram_writew (dev, he->he_entry + E4MMU_PTE3_WORD1_OFFSET(tagidx), (value >> 16) & 0xFFFF);
++      elan4_sdram_writew (dev, he->he_entry + E4MMU_PTE3_WORD2_OFFSET(tagidx), (value >> 32) & 0xFFFF);
++      elan4_sdram_writew (dev, he->he_entry + E4MMU_PTE3_WORD0_OFFSET(tagidx), (value >> 0)  & 0xFFFF);
++    }
++    else
++    {
++      elan4_sdram_writew (dev, he->he_entry + E4MMU_PTE_HIGH_OFFSET(tagidx, pteidx), (value >> 32) & 0xFFFF);
++      elan4_sdram_writel (dev, he->he_entry + E4MMU_PTE_LOW_OFFSET(tagidx, pteidx), value & 0xFFFFFFFF);
++    }
++}
++
++static void
++elan4mmu_invalidatepte (ELAN4_DEV *dev, ELAN4_HASH_ENTRY *he, int tagidx, int pteidx)
++{
++    if (pteidx == 3)
++      elan4_sdram_writeb (dev, he->he_entry + E4MMU_PTE3_WORD0_OFFSET(tagidx), PTE_SetPerm (PERM_Disabled));
++    else
++      elan4_sdram_writeb (dev, he->he_entry + E4MMU_PTE_LOW_OFFSET(tagidx, pteidx), PTE_SetPerm (PERM_Disabled));
++}
++
++static E4_uint64
++elan4mmu_readpte (ELAN4_DEV *dev, ELAN4_HASH_ENTRY *he, int tagidx, int pteidx)
++{
++    if (pteidx == 3)
++      return (((E4_uint64) elan4_sdram_readw (dev, he->he_entry + E4MMU_PTE3_WORD0_OFFSET(tagidx)) << 0)  |
++              ((E4_uint64) elan4_sdram_readw (dev, he->he_entry + E4MMU_PTE3_WORD1_OFFSET(tagidx)) << 16) |
++              ((E4_uint64) elan4_sdram_readw (dev, he->he_entry + E4MMU_PTE3_WORD2_OFFSET(tagidx)) << 32));
++    else
++      return ((E4_uint64) elan4_sdram_readl (dev, he->he_entry + E4MMU_PTE_LOW_OFFSET(tagidx, pteidx)) |
++              ((E4_uint64) elan4_sdram_readw (dev, he->he_entry + E4MMU_PTE_HIGH_OFFSET(tagidx, pteidx)) << 32));
++}
++
++
++void
++elan4mmu_flush_tlb (ELAN4_DEV *dev)
++{
++    PULSE_SYSCONTROL (dev, CONT_TLB_FLUSH);
++
++    while (read_reg64 (dev, SysControlReg) & CONT_TLB_FLUSH)
++      DELAY (1);
++}
++
++/*
++ * elanmmu_flush_tlb_hash - this flushes the hash copy entries and the elan
++ * tlb.  However after the write to the hash copy entry if the elan was
++ * in the process of walking, then it could write the hash copy with a valid
++ * entry which we had just invalidated. However once we've seen the tlb flushed
++ * then if the walk engine had done a write - then we need to invaldate the
++ * hash copy entries again and reflush the tlb.
++ *
++ * If we're invalidating a lot of hash blocks, then the chances are that the
++ * walk engine will perform a write - so we flush the tlb first, then invalidate
++ * the hash copy entries, then flush the tlb again.
++ */
++static void
++elan4mmu_flush_tlb_hash (ELAN4_DEV *dev, int tbl, unsigned baseidx, unsigned topidx)
++{
++    int       notmany = (abs(topidx - baseidx) < 5) ? 1 : 0;
++    int       hashidx;
++    E4_uint32 reg;
++
++    if (notmany)
++      PULSE_SYSCONTROL (dev, CONT_CLEAR_WALK_WROTE_TABLES);
++    else
++      elan4mmu_flush_tlb(dev);
++
++    do {
++      for (hashidx = baseidx; hashidx <= topidx; hashidx++)
++          if (dev->dev_mmuhash[tbl][hashidx].he_tag[0] & HE_TAG_COPY)
++          {
++              ASSERT ((dev->dev_mmuhash[tbl][hashidx].he_tag[0] & HE_TAG_VALID) == 0);
++              ASSERT ((dev->dev_mmuhash[tbl][hashidx].he_tag[1] & HE_TAG_VALID) == 0);
++
++              elan4mmu_synctag (dev, &dev->dev_mmuhash[tbl][hashidx], 0);
++              elan4mmu_synctag (dev, &dev->dev_mmuhash[tbl][hashidx], 1);
++          }
++      
++      PULSE_SYSCONTROL (dev, CONT_TLB_FLUSH);
++      
++      while ((reg = read_reg64 (dev, SysControlReg)) & CONT_TLB_FLUSH)
++          DELAY (1);
++      
++    } while (notmany-- && (reg & CONT_CLEAR_WALK_WROTE_TABLES) != 0);
++}
++
++void
++elan4mmu_display_hent (ELAN4_DEV *dev, ELAN4_HASH_ENTRY *he, int hashidx)
++{
++    int tagidx;
++
++    elan4_debugf (DBG_DEVICE, DBG_MMU, "elan4mmu_display_hent: hashidx=%d he=%p entry at %lx\n", hashidx, he, he->he_entry);
++    elan4_debugf (DBG_DEVICE, DBG_MMU, "                       next=%p prev=%p chain=%p,%p\n", he->he_next, he->he_prev, he->he_chain[0], he->he_chain[1]);
++    for (tagidx = 0; tagidx < 2; tagidx++)
++    {
++      E4_uint64 tag  = elan4_sdram_readq (dev, he->he_entry + E4MMU_TAG_OFFSET(tagidx));
++      E4_uint64 pte0 = elan4_sdram_readq (dev, he->he_entry + E4MMU_PTE_LOW_OFFSET(tagidx, 0));
++      E4_uint64 pte1 = elan4_sdram_readq (dev, he->he_entry + E4MMU_PTE_LOW_OFFSET(tagidx, 1));
++      E4_uint64 pte2 = elan4_sdram_readq (dev, he->he_entry + E4MMU_PTE_LOW_OFFSET(tagidx, 2));
++      E4_uint64 pte3 = ((pte0 >> 48) | (pte1 >> 32) | (pte2 >> 16));
++      
++      elan4_debugf (DBG_DEVICE, DBG_MMU, "                       Tag %d (%llx,%08x) context=%04x vaddr=%llx\n", tagidx, he->he_tag[tagidx], he->he_pte[tagidx], (int) (tag & TAG_CONTEXT_MASK), (tag & TAG_ADDRESS_MASK));
++      elan4_debugf (DBG_DEVICE, DBG_MMU, "                       Pte 0 - PPN=%llx PERM=%x TYPE=%x%s%s\n", (pte0 & PTE_PPN_MASK) >> PTE_PPN_SHIFT, 
++                    (int) (pte0 & PTE_PERM_MASK) >> PTE_PERM_SHIFT, (int)(pte0 & PTE_TYPE_MASK), (pte0 & PTE_MOD_MASK) ? " mod" : "", (pte0 & PTE_REF_MASK) ? " ref" : "");
++      elan4_debugf (DBG_DEVICE, DBG_MMU, "                       Pte 1 - PPN=%llx PERM=%x TYPE=%x%s%s\n", (pte1 & PTE_PPN_MASK) >> PTE_PPN_SHIFT, 
++                    (int) (pte1 & PTE_PERM_MASK) >> PTE_PERM_SHIFT, (int)(pte1 & PTE_TYPE_MASK), (pte1 & PTE_MOD_MASK) ? " mod" : "", (pte1 & PTE_REF_MASK) ? " ref" : "");
++      elan4_debugf (DBG_DEVICE, DBG_MMU, "                       Pte 2 - PPN=%llx PERM=%x TYPE=%x%s%s\n", (pte2 & PTE_PPN_MASK) >> PTE_PPN_SHIFT, 
++                    (int) (pte2 & PTE_PERM_MASK) >> PTE_PERM_SHIFT, (int)(pte2 & PTE_TYPE_MASK), (pte2 & PTE_MOD_MASK) ? " mod" : "", (pte2 & PTE_REF_MASK) ? " ref" : "");
++      elan4_debugf (DBG_DEVICE, DBG_MMU, "                       Pte 3 - PPN=%llx PERM=%x TYPE=%x%s%s\n", (pte3 & PTE_PPN_MASK) >> PTE_PPN_SHIFT, 
++                    (int) (pte3 & PTE_PERM_MASK) >> PTE_PERM_SHIFT, (int)(pte3 & PTE_TYPE_MASK), (pte3 & PTE_MOD_MASK) ? " mod" : "", (pte3 & PTE_REF_MASK) ? " ref" : "");
++    }
++}
++
++static __inline__ ELAN4_HASH_ENTRY *
++he_ctxt_next (ELAN4_HASH_ENTRY *he, int ctxnum)
++{
++    return ((he->he_tag[0] & TAG_CONTEXT_MASK) == ctxnum) ? he->he_chain[0] : he->he_chain[1];
++}
++
++static __inline__ ELAN4_HASH_ENTRY *
++he_ctxt_unlink (ELAN4_CTXT *ctxt, int tbl, int hashidx, ELAN4_HASH_ENTRY *prevhe, ELAN4_HASH_ENTRY *he, ELAN4_HASH_ENTRY *next)
++{
++    /* Check whether either tag is in use by this context */
++    if ((he->he_tag[0] & TAG_CONTEXT_MASK) == ctxt->ctxt_num || (he->he_tag[1] & TAG_CONTEXT_MASK) == ctxt->ctxt_num)
++      return he;
++
++    if (prevhe == NULL)
++      ctxt->ctxt_mmuhash[tbl][hashidx] = next;
++    else
++    {
++      /* previous he, ensure that both chain pointers are changed is this ctxt is using both tags */
++      ASSERT ((prevhe->he_tag[0] & TAG_CONTEXT_MASK) == ctxt->ctxt_num || (prevhe->he_tag[1] & TAG_CONTEXT_MASK) == ctxt->ctxt_num);
++
++      if ((prevhe->he_tag[0] & TAG_CONTEXT_MASK) == ctxt->ctxt_num)
++          prevhe->he_chain[0] = next;
++      if ((prevhe->he_tag[1] & TAG_CONTEXT_MASK) == ctxt->ctxt_num)
++          prevhe->he_chain[1] = next;
++    }
++
++    return prevhe;
++}
++
++void
++elan4mmu_display (ELAN4_CTXT *ctxt, int tbl, const char *tag)
++{
++    ELAN4_DEV *dev = ctxt->ctxt_dev;
++    ELAN4_HASH_ENTRY *he;
++    int hashidx;
++
++    for (hashidx = 0; hashidx < dev->dev_hashsize[tbl]; hashidx++)
++      for (he = ctxt->ctxt_mmuhash[tbl][hashidx]; he != NULL; he = he_ctxt_next (he, ctxt->ctxt_num))
++      {
++          elan4_debugf (DBG_DEVICE, DBG_MMU, "%s: hashidx=%d he=%p tags <%llx,%llx>\n", tag, hashidx, he,
++                        (he->he_tag[0] & TAG_CONTEXT_MASK) == ctxt->ctxt_num ? E4MMU_TAG2VADDR (he->he_tag[0], hashidx, dev->dev_pageshift[tbl], dev->dev_hashsize[tbl]-1) : 0,
++                        (he->he_tag[1] & TAG_CONTEXT_MASK) == ctxt->ctxt_num ? E4MMU_TAG2VADDR (he->he_tag[1], hashidx, dev->dev_pageshift[tbl], dev->dev_hashsize[tbl]-1) : 0);
++          elan4mmu_display_hent (dev, he, hashidx);
++      }
++}
++
++static ELAN4_HASH_ENTRY *
++elan4mmu_alloc_hent (ELAN4_DEV *dev, int tbl, int hashidx, E4_uint64 newtag, int *tagidx)
++{
++    ELAN4_HASH_ENTRY *he, *phe;
++    unsigned long flags;
++    int i;
++
++    spin_lock_irqsave (&dev->dev_mmulock, flags);
++
++    /* 2nd see if there are any partial free blocks */
++    if ((he = dev->dev_mmufree[tbl][hashidx]) != NULL)
++    {
++      *tagidx = ((he->he_tag[0] & TAG_CONTEXT_MASK) == INVALID_CONTEXT) ? 0 : 1;
++      
++      MPRINTF (DBG_DEVICE, 3, "elan4mmu_alloc_hent: allocate he=%p idx=%d%s\n", he, *tagidx, (he == &dev->dev_mmuhash[tbl][hashidx]) ? " hash-block" : "");
++      
++      he->he_tag[*tagidx] = newtag | HE_TAG_VALID;
++
++      elan4mmu_synctag (dev, he, *tagidx);
++      
++      if ((he->he_tag[(*tagidx) ^ 1] & TAG_CONTEXT_MASK) != INVALID_CONTEXT)
++      {
++          MPRINTF (DBG_DEVICE, 3, "elan4mmu_alloc_hent: block full - remove from freelist\n");
++          dev->dev_mmufree[tbl][hashidx] = he->he_chain[*tagidx];
++      }
++      
++      spin_unlock_irqrestore (&dev->dev_mmulock, flags);
++      return (he);
++    }
++    
++    if ((he = dev->dev_mmufreelist) != NULL)
++      dev->dev_mmufreelist = he->he_next;
++    else
++    {
++      ELAN4_HASH_CHUNK *hc;
++      sdramaddr_t       entry;
++
++      KMEM_ALLOC (hc, ELAN4_HASH_CHUNK *, sizeof (ELAN4_HASH_CHUNK), 0);
++      
++      if (hc == NULL)
++      {
++          spin_unlock_irqrestore (&dev->dev_mmulock, flags);
++          return ((ELAN4_HASH_ENTRY *) NULL);
++      }
++      
++      if ((entry = elan4_sdram_alloc (dev, sizeof (E4_HashTableEntry) * ELAN4_HENT_CHUNKS)) == (sdramaddr_t) 0)
++      {
++          spin_unlock_irqrestore (&dev->dev_mmulock, flags);
++
++          KMEM_FREE (hc, sizeof (ELAN4_HASH_CHUNK));
++          return ((ELAN4_HASH_ENTRY *) NULL);
++      }
++
++      list_add_tail (&hc->hc_link, &dev->dev_hc_list);
++
++      elan4_sdram_zeroq_sdram (dev, entry, sizeof (E4_HashTableEntry) * ELAN4_HENT_CHUNKS);
++
++      /* no initialise all chunks and chain all but the first onto the freelist */
++      for (i = 0; i < ELAN4_HENT_CHUNKS; i++, entry += sizeof (E4_HashTableEntry))
++      {
++          hc->hc_hents[i].he_entry = entry;
++
++          if (i == 0)
++              he = &hc->hc_hents[0];
++          else
++          {
++              hc->hc_hents[i].he_next = dev->dev_mmufreelist;
++              dev->dev_mmufreelist = &hc->hc_hents[i];
++          }
++      }
++    }
++
++    /* Initialise hash entry, using slot 0 */
++    *tagidx = 0;
++
++    he->he_next     = NULL;
++    he->he_prev     = NULL;
++    he->he_chain[0] = NULL;
++    he->he_chain[1] = NULL;
++    he->he_tag[0]   = newtag | HE_TAG_VALID;
++    he->he_tag[1]   = E4MMU_TAG(0, INVALID_CONTEXT);
++    he->he_pte[0]   = 0;
++    he->he_pte[1]   = 0;
++    
++    elan4mmu_synctag (dev, he, 0);
++    
++    /* add slot 1 to freelist */
++    he->he_chain[1] = dev->dev_mmufree[tbl][hashidx];
++    dev->dev_mmufree[tbl][hashidx] = he;
++    
++    /* add to mmuhash lists */
++    for (phe = &dev->dev_mmuhash[tbl][hashidx]; phe->he_next; phe = phe->he_next)
++      ;
++    phe->he_next = he;
++    he->he_prev  = phe;
++    he->he_next  = NULL;
++    
++    /* finally chain the hash block into the hash tables */
++    elan4mmu_chain_hents (dev, phe, he);
++    
++    spin_unlock_irqrestore (&dev->dev_mmulock, flags);
++    return (he);
++}
++
++static void
++elan4mmu_free_hent (ELAN4_DEV *dev, int tbl, int hashidx, ELAN4_HASH_ENTRY *he, int tagidx)
++{
++    unsigned long flags;
++    int pteidx;
++
++    /* Invalidate the tag, and zero all ptes */
++    for (pteidx = 0; pteidx < 4; pteidx++)
++      if (HE_GET_PTE(he, tagidx, pteidx))
++          elan4mmu_writepte (dev, he, tagidx, pteidx, 0);
++
++    spin_lock_irqsave (&dev->dev_mmulock, flags);
++
++    he->he_tag[tagidx] = E4MMU_TAG(0, INVALID_CONTEXT);
++    he->he_pte[tagidx] = 0;
++
++    elan4mmu_synctag (dev, he, tagidx);
++
++    if ((he->he_tag[tagidx^1] & TAG_CONTEXT_MASK) == INVALID_CONTEXT) /* Both tags are now free */
++    {
++      if (he == &dev->dev_mmuhash[tbl][hashidx])              /* it's the hash block entry */
++      {                                                       /* so as it's already on the freelist */
++          he->he_chain[tagidx] = he->he_chain[tagidx^1];      /* just copy it's chain pointers */
++
++          MPRINTF (DBG_DEVICE, 3, "elan4mmu_free_hent: tbl=%d hashidx=%x tagidx=%d he=%p => all free but hashblk\n", tbl, hashidx, tagidx, he);
++      }
++      else
++      {
++          MPRINTF (DBG_DEVICE, 3, "elan4mmu_free_hent: tbl=%d hashidx=%x tagidx=%d he=%p => all free\n", tbl, hashidx, tagidx, he);
++          
++          /* XXXX - should remove it from the hash table, and 
++          *         place back on the anonymous freelist */
++          he->he_chain[tagidx] = he->he_chain[tagidx^1];
++      }
++    }
++    else
++    {
++      /* Other tag still in use */
++      he->he_chain[tagidx] = dev->dev_mmufree[tbl][hashidx];
++      dev->dev_mmufree[tbl][hashidx] = he;
++
++      MPRINTF (DBG_DEVICE, 3, "elan4mmu_free_hent: tbl=%d hashidx=%x tagidx=%d he=%p => other tag in use\n", tbl, hashidx, tagidx, he);
++    }
++    spin_unlock_irqrestore (&dev->dev_mmulock, flags);
++}
++
++ELAN4_HASH_ENTRY *
++elan4mmu_ptealloc (ELAN4_CTXT *ctxt, int tbl, E4_Addr vaddr, unsigned int *tagidxp)
++{
++    ELAN4_DEV        *dev     = ctxt->ctxt_dev;
++    unsigned        ctxnum  = ctxt->ctxt_num;
++    unsigned          hashidx = E4MMU_HASH_INDEX (ctxnum, vaddr, dev->dev_pageshift[tbl], dev->dev_hashsize[tbl]-1);
++    E4_uint64         newtag  = E4MMU_TAG(vaddr, ctxnum);
++    ELAN4_HASH_ENTRY *he      = &dev->dev_mmuhash[tbl][hashidx];
++    unsigned        tagidx;
++
++    MPRINTF (ctxt, 2, "elan4mmu_ptealloc: tbl=%d ctxnum=%d vaddr=%llx -> hashidx %d\n", tbl, ctxnum, vaddr, hashidx);
++
++    /* 1st) check whether we're reloading an existing entry */
++    for (he = ctxt->ctxt_mmuhash[tbl][hashidx]; he != NULL; he = he_ctxt_next (he, ctxnum))
++    {
++      ASSERT ((he->he_tag[0] & TAG_CONTEXT_MASK) == ctxnum || (he->he_tag[1] & TAG_CONTEXT_MASK) == ctxnum);
++
++      for (tagidx = 0; tagidx < 2; tagidx++)
++      {
++          if ((he->he_tag[tagidx] & (TAG_ADDRESS_MASK | TAG_CONTEXT_MASK | HE_TAG_VALID)) == (newtag | HE_TAG_VALID))
++          {
++              MPRINTF (ctxt, 2, "elan4mmu_ptealloc: return old he %p tagidx %d\n", he, tagidx);
++
++              *tagidxp = tagidx;
++              return he;
++          }
++      }
++    }
++
++    if ((he = elan4mmu_alloc_hent (dev, tbl, hashidx, newtag, &tagidx)) == NULL)
++      return NULL;
++
++    /* chain onto context hash */
++    if ((he->he_tag[tagidx ^ 1] & TAG_CONTEXT_MASK) == ctxnum)        /* already chained using other link */
++    {                                                         /* so ensure both slots are chained the same */
++      he->he_chain[tagidx] = he->he_chain[tagidx^1];
++    }
++    else
++    {
++      he->he_chain[tagidx] = ctxt->ctxt_mmuhash[tbl][hashidx];
++      ctxt->ctxt_mmuhash[tbl][hashidx] = he;
++    }
++
++    MPRINTF (ctxt, 2, "elan4mmu_ptealloc: return new he %p tagidx %d\n", he, tagidx);
++
++    *tagidxp = tagidx;
++
++    return he;
++}
++
++int
++elan4mmu_pteload (ELAN4_CTXT *ctxt, int tbl, E4_Addr vaddr, E4_uint64 newpte)
++{
++    ELAN4_DEV        *dev     = ctxt->ctxt_dev;
++    unsigned          pteidx  = E4MMU_SHIFT_ADDR(vaddr, dev->dev_pageshift[tbl]) & 3;
++    unsigned        tagidx;
++    ELAN4_HASH_ENTRY *he;
++
++    MPRINTF (ctxt, 0, "elan4mmu_pteload: ctx=%d tbl=%d pteidx=%d vaddr=%llx pte=%llx\n", 
++          ctxt->ctxt_num, tbl, pteidx, (unsigned long long)vaddr, newpte);
++
++    spin_lock (&ctxt->ctxt_mmulock);
++
++    if ((he = elan4mmu_ptealloc (ctxt, tbl, vaddr, &tagidx)) == NULL)
++    {
++      spin_unlock (&ctxt->ctxt_mmulock);
++      return -ENOMEM;
++    }
++
++    MPRINTF (ctxt, 1, "elan4mmu_pteload: %s he=%p tagidx=%d pteidx=%d\n", HE_GET_PTE(he,0,pteidx) ? "reloading" : "loading", he, tagidx, pteidx);
++    
++    ASSERT (HE_GET_PTE(he,tagidx,pteidx) == 0 ||                                                      /* invalid -> valid */
++          (elan4mmu_readpte (dev, he, tagidx, pteidx) & PTE_PPN_MASK) == (newpte & PTE_PPN_MASK));    /* or same phys address */
++    
++    elan4mmu_writepte (dev, he, tagidx, pteidx, newpte);
++    
++    HE_SET_PTE(he, tagidx, pteidx, (newpte & PTE_PERM_TYPE_MASK));
++
++    spin_unlock (&ctxt->ctxt_mmulock);
++    return 0;
++}
++
++void
++elan4mmu_unload_range (ELAN4_CTXT *ctxt, int tbl, E4_Addr start, unsigned long len)
++{
++    ELAN4_DEV        *dev       = ctxt->ctxt_dev;
++    unsigned          ctxnum    = ctxt->ctxt_num;
++    unsigned long     tagspan   = (1 << (dev->dev_pageshift[tbl] + 2));
++    E4_Addr           end       = start + len - 1;
++    int                     needflush = 0;
++    unsigned          baseidx, topidx;
++    unsigned          hashidx, tagidx, pteidx;
++    ELAN4_HASH_ENTRY *he, *prevhe, *next;
++    
++    MPRINTF (ctxt, 0, "elan4mmu_unload_range: tbl=%d start=%llx end=%llx len=%lx\n", tbl, start, end, len);
++
++    /* determine how much of the hash table we've got to scan */
++    
++    /* GNAT 6760: When we have a Main page size which maps onto multiple Elan pages
++     * we need to do something a bit more clever here or else it takes ms per page invalidate
++     * This change helps in the meantime
++     */
++    /* if (len <= (1 << dev->dev_pageshift[tbl])) */
++    if (len <= PAGE_SIZE)
++    {
++      baseidx = E4MMU_HASH_INDEX (ctxnum, start, dev->dev_pageshift[tbl], dev->dev_hashsize[tbl]-1);
++      topidx  = E4MMU_HASH_INDEX (ctxnum, end,   dev->dev_pageshift[tbl], dev->dev_hashsize[tbl]-1);
++
++      if (baseidx != topidx)
++      {
++          /* GNAT 6760: Need to search whole of the hash table (slow!) */
++          baseidx = 0;
++          topidx  = dev->dev_hashsize[tbl] - 1;
++      }
++    }
++    else
++    {
++      baseidx = 0;
++      topidx  = dev->dev_hashsize[tbl] - 1;
++    }
++
++    MPRINTF (ctxt, 1, "elan4mmu_unload_range: baseidx=%d topidx=%d\n", baseidx, topidx);
++
++    spin_lock (&ctxt->ctxt_mmulock);
++
++    /* 1st - invalidate the tag for all hash blocks which are completely invalidated,
++     *       and remember the first/last hash blocks */
++    for (hashidx = baseidx; hashidx <= topidx; hashidx++)
++      for (he = ctxt->ctxt_mmuhash[tbl][hashidx]; he != NULL; he = he_ctxt_next (he, ctxnum))
++          for (tagidx = 0; tagidx < 2; tagidx++)
++              if ((he->he_tag[tagidx] & TAG_CONTEXT_MASK) == ctxnum)
++              {
++                  E4_Addr base = E4MMU_TAG2VADDR (he->he_tag[tagidx], hashidx, dev->dev_pageshift[tbl], dev->dev_hashsize[tbl]-1);
++                  E4_Addr top  = base + (tagspan -1);
++                  
++                  if (start < top && end > base)
++                  {
++                      unsigned bidx = (start <= base) ? 0 : (start & (tagspan-1)) >> dev->dev_pageshift[tbl];
++                      unsigned tidx = (end   >= top)  ? 3 : (end   & (tagspan-1)) >> dev->dev_pageshift[tbl];
++                      
++                      MPRINTF (ctxt, 1, "elan4mmu_unload_range: he=%p base=%llx top=%llx hashidx=%d bidx=%d tidx=%d\n", he, base, top, hashidx, bidx, tidx);
++                      
++                      for (pteidx = bidx; pteidx <= tidx; pteidx++)
++                          if (HE_GET_PTE(he, tagidx, pteidx))
++                          {
++                              elan4mmu_invalidatepte (dev, he, tagidx, pteidx);
++                              needflush = 1;
++                          }
++                  }
++                  else if (base >= start && top <= end)               /* hash entry completely spanned */
++                  {                                                   /* so invalidate the tag */
++                      MPRINTF (ctxt, 1, "elan4mmu_unload_range: he=%p base=%llx top=%llx spanned\n", he, base, top);
++
++                      he->he_tag[tagidx] &= ~HE_TAG_VALID;
++                      
++                      elan4mmu_synctag (dev, he, tagidx);
++                      needflush = 1;
++                  }
++              }
++
++    if (needflush)
++    {
++      /* 2nd invalidate the first/last hash blocks if they are partially invalidated
++       * and flush the tlb/hash copy blocks */
++      elan4mmu_flush_tlb_hash (dev, tbl, baseidx, topidx);
++      
++      /* 3rd free off the hash entries which are completely invalidated */
++      for (hashidx = baseidx; hashidx <= topidx; hashidx++)
++          for (prevhe = NULL, he = ctxt->ctxt_mmuhash[tbl][hashidx]; he != NULL; he = next)
++          {
++              next = he_ctxt_next (he, ctxnum);
++              
++              for (tagidx = 0; tagidx < 2; tagidx++)
++                  if ((he->he_tag[tagidx] & TAG_CONTEXT_MASK) == ctxnum)
++                  {
++                      E4_Addr base = E4MMU_TAG2VADDR (he->he_tag[tagidx], hashidx, dev->dev_pageshift[tbl], dev->dev_hashsize[tbl]-1);
++                      E4_Addr top  = base + (tagspan -1);
++                      
++                      if (start < top && end > base)
++                      {
++                          unsigned bidx = (start <= base) ? 0 : (start & (tagspan-1)) >> dev->dev_pageshift[tbl];
++                          unsigned tidx = (end   >= top)  ? 3 : (end   & (tagspan-1)) >> dev->dev_pageshift[tbl];
++                          
++                          MPRINTF (ctxt, 1, "elan4mmu_unload_range: he=%p base=%llx top=%llx bidx=%d tidx=%d\n", he, base, top, bidx, tidx);
++                          
++                          for (pteidx = bidx; pteidx <= tidx; pteidx++)
++                              if (HE_GET_PTE(he, tagidx, pteidx))
++                              {
++                                  HE_SET_PTE(he, tagidx, pteidx, 0);
++                                  
++                                  elan4mmu_writepte (dev, he, tagidx, pteidx, 0);
++                              }
++                      }
++                      
++                      if ((base >= start && top <= end) || he->he_pte[tagidx] == 0)   /* hash entry completely spanned or all pte's cleared */
++                      {                                                                       /* so invalidate the pte's and free it */
++                          
++                          MPRINTF (ctxt, 1, "elan4mmu_unload_range: he=%p base=%llx top=%llx spanned or empty\n", he, base, top);
++                          
++                          elan4mmu_free_hent (dev, tbl, hashidx, he, tagidx);
++                      }
++                  }
++              
++              prevhe = he_ctxt_unlink (ctxt, tbl, hashidx, prevhe, he, next);
++          }
++    }
++    spin_unlock (&ctxt->ctxt_mmulock);
++}
++
++void
++elan4mmu_invalidate_ctxt (ELAN4_CTXT *ctxt)
++{
++    ELAN4_DEV *dev    = ctxt->ctxt_dev;
++    int        ctxnum = ctxt->ctxt_num;
++    ELAN4_HASH_ENTRY *he;
++    int tbl, hashidx, tagidx;
++
++    MPRINTF (ctxt, 0, "elan4mmu_invalidate_ctxt: invalidating ctxnum=%d\n", ctxnum);
++
++    spin_lock (&ctxt->ctxt_mmulock);
++
++    /* 1st invalidate all tags belonging to me */
++    for (tbl = 0; tbl < NUM_HASH_TABLES; tbl++)
++      for (hashidx = 0; hashidx < dev->dev_hashsize[tbl]; hashidx++)
++          for (he = ctxt->ctxt_mmuhash[tbl][hashidx]; he != NULL; he = he_ctxt_next (he, ctxnum))
++              for (tagidx = 0; tagidx < 2; tagidx++)
++                  if ((he->he_tag[tagidx] & TAG_CONTEXT_MASK) == ctxnum) /* own tag block */
++                  {
++                      MPRINTF (ctxt, 1, "elan4mmu_invalidate_ctxt: he=%p addr=%llx hashidx=%d tagidx=%d\n", 
++                               he, he->he_tag[tagidx] & TAG_ADDRESS_MASK, hashidx, tagidx);
++
++                      he->he_tag[tagidx] &= ~HE_TAG_VALID;
++                      
++                      elan4mmu_synctag (dev, he, tagidx);
++                  }
++
++    /* 2nd flush the tlb & cached hash block */
++    elan4mmu_flush_tlb (dev);
++    
++    /* 3rd invalidate all pte's and free off the hash entries */
++    for (tbl = 0; tbl < NUM_HASH_TABLES; tbl++)
++      for (hashidx = 0; hashidx < dev->dev_hashsize[tbl]; hashidx++)
++          while ((he = ctxt->ctxt_mmuhash[tbl][hashidx]) != NULL)
++          {
++              ctxt->ctxt_mmuhash[tbl][hashidx] = he_ctxt_next (he, ctxnum);
++
++              for (tagidx = 0; tagidx < 2; tagidx++)
++                  if ((he->he_tag[tagidx] & TAG_CONTEXT_MASK) == ctxnum)
++                      elan4mmu_free_hent (dev, tbl, hashidx, he, tagidx);
++          }
++    spin_unlock (&ctxt->ctxt_mmulock);
++}
++
++ELAN4_HASH_CACHE *
++elan4mmu_reserve (ELAN4_CTXT *ctxt, int tbl, E4_Addr start, unsigned int npages, int cansleep)
++{
++    ELAN4_DEV        *dev      = ctxt->ctxt_dev;
++    E4_Addr           end      = start + (npages << dev->dev_pageshift[tbl]) - 1;
++    unsigned long     tagshift = dev->dev_pageshift[tbl] + 2;
++    E4_Addr           tagspan  = 1 << tagshift;
++    E4_Addr           base     = (start & ~(tagspan-1));
++    E4_Addr           top      = (end   & ~(tagspan-1)) + (tagspan-1);
++    unsigned int      nhes     = (top - base + 1) >> tagshift;
++    ELAN4_HASH_CACHE *hc;
++    unsigned int      tagidx,  pteidx;
++    E4_Addr           addr;
++    int                     i;
++    
++    MPRINTF (ctxt, 0, "elan4mmu_reserve: start=%llx npages=%d\n", start, npages);
++    MPRINTF (ctxt, 0, "         pageshift=%d tagspan=%lx base=%llx top=%llx end=%llx nhes=%d\n",
++           dev->dev_pageshift[tbl], tagspan, base, top, end, nhes);
++
++    KMEM_ALLOC (hc, ELAN4_HASH_CACHE *, offsetof (ELAN4_HASH_CACHE, hc_hes[nhes]), cansleep);
++
++    if (hc == NULL)
++      return NULL;
++
++    hc->hc_start = start;
++    hc->hc_end   = end;
++    hc->hc_tbl   = tbl;
++
++    spin_lock (&ctxt->ctxt_mmulock);
++    for (addr = base, i = 0; i < nhes; addr += tagspan, i++)
++    {
++      unsigned bidx = (i == 0)        ? (start & (tagspan-1)) >> dev->dev_pageshift[tbl] : 0;
++      unsigned tidx = (i == (nhes-1)) ? (end   & (tagspan-1)) >> dev->dev_pageshift[tbl] : 3;
++
++      
++      if ((hc->hc_hes[i] = elan4mmu_ptealloc (ctxt, tbl, addr & ~(tagspan-1), &tagidx)) == NULL)
++          goto failed;
++
++
++      MPRINTF (ctxt, 2, "elan4mmu_reserve: tbl=%d addr=%llx -> hashidx=%d tagidx=%d\n", tbl, addr & ~(tagspan-1), 
++               E4MMU_HASH_INDEX (ctxt->ctxt_num, (addr & ~(tagspan-1)), dev->dev_pageshift[tbl], dev->dev_hashsize[tbl]-1), tagidx);
++                       
++      for (pteidx = bidx; pteidx <= tidx; pteidx++)
++      {
++          ASSERT (HE_GET_PTE (hc->hc_hes[i], tagidx, pteidx) == 0);
++
++          MPRINTF (ctxt, 2, "elan4mmu_reserve: i=%d addr=%llx he=%p (tagidx=%d pteidx=%d)\n",
++                   i, addr, hc->hc_hes[i], tagidx, pteidx);
++
++          HE_SET_PTE (hc->hc_hes[i], tagidx, pteidx, PTE_PERM_TYPE_MASK);
++      }
++    }
++    spin_unlock (&ctxt->ctxt_mmulock);
++
++    return hc;
++
++ failed:
++    for (i--, addr -= tagspan; i >= 0; i--, addr -= tagspan)
++    {
++      unsigned bidx    = (i == 0) ? (start & (tagspan-1)) >> dev->dev_pageshift[tbl] : 0;
++      unsigned tidx    = (i == (nhes-1)) ? (end   & (tagspan-1)) >> dev->dev_pageshift[tbl] : 3;
++      unsigned hashidx = E4MMU_HASH_INDEX (ctxt->ctxt_num, addr, dev->dev_pageshift[tbl], dev->dev_hashsize[tbl]-1);
++      unsigned tagidx  = (addr == E4MMU_TAG2VADDR (hc->hc_hes[i]->he_tag[0], hashidx, dev->dev_pageshift[tbl], dev->dev_hashsize[tbl]-1)) ? 0 : 1;
++          
++      for (pteidx = bidx; pteidx <= tidx; pteidx++)
++          HE_SET_PTE(hc->hc_hes[i], tagidx, pteidx, 0);
++
++      if (hc->hc_hes[i]->he_pte[tagidx] == 0)
++          elan4mmu_free_hent (dev, tbl, hashidx, hc->hc_hes[i], tagidx);
++    }
++    spin_unlock (&ctxt->ctxt_mmulock);
++
++    KMEM_FREE (hc, offsetof (ELAN4_HASH_CACHE, hc_hes[nhes]));
++    
++    return NULL;
++}
++
++void
++elan4mmu_release (ELAN4_CTXT *ctxt, ELAN4_HASH_CACHE *hc)
++{
++    ELAN4_DEV        *dev      = ctxt->ctxt_dev;
++    E4_Addr         start    = hc->hc_start;
++    E4_Addr           end      = hc->hc_end;
++    unsigned long     tagshift = dev->dev_pageshift[hc->hc_tbl] + 2;
++    E4_Addr           tagspan  = 1 << tagshift;
++    E4_Addr           base     = (start & ~(tagspan-1));
++    E4_Addr           top      = (end   & ~(tagspan-1)) + (tagspan-1);
++    unsigned int      nhes     = (top - base + 1) >> tagshift;
++    ELAN4_HASH_ENTRY *prevhe, *he, *next;
++    E4_Addr           addr;
++    unsigned int      pteidx;
++    int                     i;
++
++    spin_lock (&ctxt->ctxt_mmulock);
++
++    MPRINTF (ctxt, 0, "elan4mmu_release: base=%llx top=%llx\n", base, top);
++
++    for (addr = base, i = 0; i < nhes; addr += tagspan, i++)
++    {
++      unsigned bidx    = (i == 0)        ? (start & (tagspan-1)) >> dev->dev_pageshift[hc->hc_tbl] : 0;
++      unsigned tidx    = (i == (nhes-1)) ? (end   & (tagspan-1)) >> dev->dev_pageshift[hc->hc_tbl] : 3;
++      unsigned hashidx = E4MMU_HASH_INDEX (ctxt->ctxt_num, addr, dev->dev_pageshift[hc->hc_tbl], dev->dev_hashsize[hc->hc_tbl]-1);
++      unsigned tagidx  = (addr == E4MMU_TAG2VADDR (hc->hc_hes[i]->he_tag[0], hashidx, dev->dev_pageshift[hc->hc_tbl], dev->dev_hashsize[hc->hc_tbl]-1)) ? 0 : 1;
++          
++      for (pteidx = bidx; pteidx <= tidx; pteidx++)
++      {
++          elan4mmu_invalidatepte (dev, hc->hc_hes[i], tagidx, pteidx);
++
++          HE_SET_PTE(hc->hc_hes[i], tagidx, pteidx, 0);
++      }
++
++      MPRINTF (ctxt, 2, "elan4mmu_release: i=%d addr=%llx he=%p (hashidx=%d tagidx=%d pteidx=%d) pte=%x\n",
++               i, addr, hc->hc_hes[i], hashidx, tagidx, pteidx, hc->hc_hes[i]->he_pte[tagidx]);
++
++      /* remove from context hash */
++      /* need to move to the  hc->hc_hes[i] in the ctxt list and set prevhe, he, next */
++      prevhe = NULL;
++      he = ctxt->ctxt_mmuhash[hc->hc_tbl][hashidx];
++      next = he_ctxt_next (he, ctxt->ctxt_num);
++
++      while(he != hc->hc_hes[i]) {
++          prevhe = he;
++          he = next;
++          next = he_ctxt_next (he, ctxt->ctxt_num);
++      }
++
++      if (he->he_pte[tagidx] == 0) 
++          elan4mmu_free_hent (dev, hc->hc_tbl, hashidx, he, tagidx);
++
++      he_ctxt_unlink (ctxt, hc->hc_tbl, hashidx, prevhe, he, next);
++    }
++    spin_unlock (&ctxt->ctxt_mmulock);
++}
++
++void
++elan4mmu_set_pte (ELAN4_CTXT *ctxt, ELAN4_HASH_CACHE *hc, unsigned int idx, E4_uint64 newpte)
++{
++    ELAN4_DEV        *dev      = ctxt->ctxt_dev;
++    unsigned int      tbl      = hc->hc_tbl;
++    unsigned int      tagshift = dev->dev_pageshift[tbl] + 2;
++    E4_Addr           tagspan  = 1 << tagshift;
++    E4_Addr           addr     = hc->hc_start + (idx << dev->dev_pageshift[tbl]);
++    ELAN4_HASH_ENTRY *he       = hc->hc_hes[(addr - (hc->hc_start & ~(tagspan-1))) >> tagshift];
++    unsigned          pteidx   = E4MMU_SHIFT_ADDR(addr, dev->dev_pageshift[tbl]) & 3;
++    unsigned          tagidx   = he->he_tag[0] == (E4MMU_TAG (addr, ctxt->ctxt_num) | HE_TAG_VALID) ? 0 : 1;
++
++    MPRINTF (ctxt, 2, "elan4mmu_set_pte: idx=%d addr=%llx he=%p (tagidx=%d pteidx=%d) newpte=%llx\n", idx, addr, he, tagidx, pteidx, newpte);
++
++    ASSERT (he->he_tag[tagidx] == (E4MMU_TAG (addr, ctxt->ctxt_num) | HE_TAG_VALID));
++
++    elan4mmu_writepte (dev, he, tagidx, pteidx, newpte);
++}
++
++E4_uint64
++elan4mmu_get_pte (ELAN4_CTXT *ctxt, ELAN4_HASH_CACHE *hc, unsigned int idx)
++{
++    ELAN4_DEV        *dev      = ctxt->ctxt_dev;
++    unsigned int      tbl      = hc->hc_tbl;
++    unsigned int      tagshift = dev->dev_pageshift[tbl] + 2;
++    E4_Addr           tagspan  = 1 << tagshift;
++    E4_Addr           addr     = hc->hc_start + (idx << dev->dev_pageshift[tbl]);
++    ELAN4_HASH_ENTRY *he       = hc->hc_hes[(addr - (hc->hc_start & ~(tagspan-1))) >> tagshift];
++    unsigned          pteidx   = E4MMU_SHIFT_ADDR(addr, dev->dev_pageshift[tbl]) & 3;
++    unsigned          tagidx   = he->he_tag[0] == (E4MMU_TAG (addr, ctxt->ctxt_num) | HE_TAG_VALID) ? 0 : 1;
++
++    ASSERT (he->he_tag[tagidx] == (E4MMU_TAG (addr, ctxt->ctxt_num) | HE_TAG_VALID));
++
++    return elan4mmu_readpte (dev, he, tagidx, pteidx);
++}
++
++void
++elan4mmu_clear_pte (ELAN4_CTXT *ctxt, ELAN4_HASH_CACHE *hc, unsigned int idx)
++{
++    ELAN4_DEV        *dev      = ctxt->ctxt_dev;
++    unsigned int      tbl      = hc->hc_tbl;
++    unsigned int      tagshift = dev->dev_pageshift[tbl] + 2;
++    E4_Addr           tagspan  = 1 << tagshift;
++    E4_Addr           addr     = hc->hc_start + (idx << dev->dev_pageshift[tbl]);
++    ELAN4_HASH_ENTRY *he       = hc->hc_hes[(addr - (hc->hc_start & ~(tagspan-1))) >> tagshift];
++    unsigned          pteidx   = E4MMU_SHIFT_ADDR(addr, dev->dev_pageshift[tbl]) & 3;
++    unsigned          tagidx   = he->he_tag[0] == (E4MMU_TAG (addr, ctxt->ctxt_num) | HE_TAG_VALID) ? 0 : 1;
++
++    MPRINTF (ctxt, 2, "elan4mmu_clear_pte: idx=%d addr=%llx he=%p (tagidx=%d pteidx=%d)\n", idx, addr, he, tagidx, pteidx);
++
++    ASSERT (he->he_tag[tagidx] == (E4MMU_TAG (addr, ctxt->ctxt_num) | HE_TAG_VALID));
++
++    elan4mmu_invalidatepte (dev, he, tagidx, pteidx);
++}
++
++EXPORT_SYMBOL(elan4mmu_flush_tlb);
++EXPORT_SYMBOL(elan4mmu_pteload);
++EXPORT_SYMBOL(elan4mmu_unload_range);
++EXPORT_SYMBOL(elan4mmu_reserve);
++EXPORT_SYMBOL(elan4mmu_release);
++EXPORT_SYMBOL(elan4mmu_set_pte);
++EXPORT_SYMBOL(elan4mmu_get_pte);
++EXPORT_SYMBOL(elan4mmu_clear_pte);
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan4/mmu_Linux.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan4/mmu_Linux.c       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan4/mmu_Linux.c    2005-05-11 12:10:12.454930144 -0400
+@@ -0,0 +1,265 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: mmu_Linux.c,v 1.8 2004/05/10 14:10:46 daniel Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/mmu_Linux.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan4/debug.h>
++#include <elan4/device.h>
++
++#include <linux/pci.h>
++#include <linux/version.h>
++
++/*
++ * Convert a physical address into an pte.  This should generate a "local" pte for 
++ * physical addresses which are elan4 sdram or elan4 command queues.  For elan4
++ * registers and other addresses on the same bus, this should be the local pci 
++ * bus address.  All other addresses should access the physical address via the
++ * PCI bridge.
++ */
++
++#ifdef __alpha
++#define ioaddr2paddr(ioaddr)  virt_to_phys((void *) __ioremap(ioaddr, PAGE_SIZE))
++#elif defined(__ia64)
++#define ioaddr2paddr(ioaddr)  ((ioaddr) & ~__IA64_UNCACHED_OFFSET)
++#else
++#define ioaddr2paddr(ioaddr)  (ioaddr)
++#endif
++
++int
++elan4mmu_categorise_paddr (ELAN4_DEV *dev, physaddr_t *physp)
++{
++    physaddr_t sdram_base = ioaddr2paddr (pci_resource_start (dev->dev_osdep.pdev, ELAN4_BAR_SDRAM));
++    physaddr_t sdram_top  = ioaddr2paddr (pci_resource_end (dev->dev_osdep.pdev, ELAN4_BAR_SDRAM));
++    physaddr_t regs_base  = ioaddr2paddr (pci_resource_start (dev->dev_osdep.pdev, ELAN4_BAR_REGISTERS));
++    physaddr_t regs_top   = ioaddr2paddr (pci_resource_end (dev->dev_osdep.pdev, ELAN4_BAR_REGISTERS));
++    physaddr_t phys       = *physp;
++    int        iscommand;
++
++    if (phys >= sdram_base && phys <= sdram_top)
++    {
++      (*physp) = (phys ^ sdram_base);
++      return ELAN4MMU_PADDR_SDRAM;
++    }
++    
++    if (phys >= regs_base && phys < regs_top)
++    {
++      if (dev->dev_devinfo.dev_revision_id == PCI_REVISION_ID_ELAN4_REVA)
++          iscommand = (phys < (regs_base + ELAN4_REVA_REG_OFFSET));
++      else
++          iscommand = (phys < (regs_base + ELAN4_REVB_I2C_OFFSET));
++      
++      if (iscommand)
++      {
++          (*physp) = phys ^ regs_base;
++
++          return ELAN4MMU_PADDR_COMMAND;
++      }
++      else
++      {
++          // XXXX (*physp) = phys2bus (phys);
++
++          return ELAN4MMU_PADDR_LOCALPCI;
++      }
++    }
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
++    if (VALID_PAGE (virt_to_page (phys_to_virt (phys))))
++#else
++    if (virt_addr_valid (phys_to_virt (phys)))
++#endif
++      return ELAN4MMU_PADDR_PAGE;
++    
++    return ELAN4MMU_PADDR_OTHER;
++}
++
++int
++elan4mmu_sdram_aliascheck (ELAN4_CTXT *ctxt, E4_Addr addr, physaddr_t phys)
++{
++    ELAN4_DEV *dev = ctxt->ctxt_dev;
++
++    /*
++     * On MPSAS we don't allocate a large enough context table, so 
++     * if we see an address/context pair which would "alias" because
++     * they differ in unchecked hash bits to a previous pteload, 
++     * then we kill the application.
++     */
++    unsigned hashval = (E4MMU_SHIFT_ADDR(addr, (dev->dev_pageshift[0]) + 2) ^ E4MMU_CONTEXT_SCRAMBLE(ctxt->ctxt_num));
++    
++    if (dev->dev_rsvd_hashval[0] == 0xFFFFFFFF)
++      dev->dev_rsvd_hashval[0] = hashval & dev->dev_rsvd_hashmask[0];
++    
++    if ((hashval & dev->dev_rsvd_hashmask[0]) != dev->dev_rsvd_hashval[0])
++    {
++      printk ("elan4mmu_sdram_aliascheck: vaddr=%016llx ctxnum=%x -> [%x] overlaps %x - %x [hashidx=%x]\n", (unsigned long long) addr, 
++              ctxt->ctxt_num, hashval, hashval & dev->dev_rsvd_hashmask[0], dev->dev_rsvd_hashval[0],
++              E4MMU_HASH_INDEX (ctxt->ctxt_num, addr, dev->dev_pageshift[0], dev->dev_hashsize[0]-1));
++      
++      return 0;
++    }
++
++    if (((addr & (SDRAM_PGOFF_OFFSET << PAGE_SHIFT)) != (phys & (SDRAM_PGOFF_OFFSET << PAGE_SHIFT))))
++    {
++      printk ("elan4mmu_sdram_aliascheck: vaddr=%016llx incorrectly alias sdram at %lx\n", (unsigned long long) addr, 
++              phys ^ pci_resource_start (dev->dev_osdep.pdev, ELAN4_BAR_SDRAM));
++      return 0;
++    }
++
++    return 1;
++}
++
++int
++elan4mmu_alloc_topaddr (ELAN4_DEV *dev, physaddr_t paddr, unsigned type)
++{
++#if defined(__i386) && !defined(CONFIG_X86_PAE)
++    if (dev->dev_topaddrvalid == 0)
++    {
++      dev->dev_topaddrvalid = 1;
++
++      pci_write_config_word (dev->dev_osdep.pdev, PCI_ELAN_TOPPHYSADDR(0), 0);
++      pci_write_config_word (dev->dev_osdep.pdev, PCI_ELAN_TOPPHYSADDR(1), 0);
++      pci_write_config_word (dev->dev_osdep.pdev, PCI_ELAN_TOPPHYSADDR(2), 0);
++      pci_write_config_word (dev->dev_osdep.pdev, PCI_ELAN_TOPPHYSADDR(3), 0);
++    }
++    return (0);
++#else
++    register int i;
++    E4_uint16 match;
++
++    if (dev->dev_topaddrmode)                                 /* ExtraMasterAddrBits=1 => match {paddr[63:50],type[3:2]} */
++      match = ((paddr >> 48) & ~3) | ((type >> 2) & 3);
++    else                                                      /* ExtraMasterAddrBits=0 => match {paddr[63:48]} */
++      match = (paddr >> 48);
++    
++    MPRINTF (DBG_DEVICE, 2, "elan4mmu_alloc_topaddr: mode=%d paddr=%lx type=%x match=%x [%x %x.%x.%x.%x]\n",
++           dev->dev_topaddrmode, paddr, type, match, dev->dev_topaddrvalid,
++           dev->dev_topaddr[0], dev->dev_topaddr[1], dev->dev_topaddr[2], dev->dev_topaddr[3]);
++    
++    for (i = 0; i < 4; i++)
++      if ((dev->dev_topaddrvalid & (1 << i)) && dev->dev_topaddr[i] == match)
++          return (i);
++    
++    for (i = 0; i < 4; i++)
++    {
++      if ((dev->dev_topaddrvalid & (1 << i)) == 0)
++      {
++          MPRINTF (DBG_DEVICE, 2, "elan4mmu_alloc_topaddr: allocate slot %d for %x\n", i, match);
++
++          dev->dev_topaddrvalid |= (1 << i);
++          dev->dev_topaddr[i] = match;
++
++          pci_write_config_word (dev->dev_osdep.pdev, PCI_ELAN_TOPPHYSADDR(i), match);
++          return (i);
++      }
++    }
++
++    panic ("elan4mmu_alloc_topaddr: all topaddrs in use\n");
++    return (0);
++#endif
++}
++
++E4_uint64
++elan4mmu_phys2pte (ELAN4_DEV *dev, physaddr_t phys, unsigned perm)
++{
++    physaddr_t sdram_base = ioaddr2paddr (pci_resource_start (dev->dev_osdep.pdev, ELAN4_BAR_SDRAM));
++    physaddr_t sdram_top  = ioaddr2paddr (pci_resource_end (dev->dev_osdep.pdev, ELAN4_BAR_SDRAM));
++    physaddr_t regs_base  = ioaddr2paddr (pci_resource_start (dev->dev_osdep.pdev, ELAN4_BAR_REGISTERS));
++    physaddr_t regs_top   = ioaddr2paddr (pci_resource_end (dev->dev_osdep.pdev, ELAN4_BAR_REGISTERS));
++    int        iscommand;
++    E4_uint64  pte;
++    unsigned   type;
++    
++    if (phys >= sdram_base && phys <= sdram_top)
++    {
++      phys ^= sdram_base;
++      type  = PTE_SetPerm (perm);
++    }
++    else if (phys >= regs_base && phys < regs_top)
++    {
++      if (dev->dev_devinfo.dev_revision_id == PCI_REVISION_ID_ELAN4_REVA)
++          iscommand = (phys < (regs_base + ELAN4_REVA_REG_OFFSET));
++      else
++          iscommand = (phys < (regs_base + ELAN4_REVB_I2C_OFFSET));
++      
++      if (iscommand)
++      {
++          phys ^= regs_base;
++          type  = PTE_SetPerm (perm) | PTE_CommandQueue;
++      }
++      else
++      {
++          type = PTE_SetPerm (perm) | PTE_PciNotLocal;
++          // phys = phys2bus (phys);
++      }
++    }
++    else
++    {
++      type = PTE_SetPerm (perm) | PTE_PciNotLocal | dev->dev_pteval;
++
++#ifdef LINUX_SPARC
++      /* XXXX if not local pci bus, then or in the bypass bit */
++      phys |= 0xfffe000000000000;
++      type |= PTE_BigEndian;
++#endif
++
++
++#if defined(__alpha)
++      phys |= alpha_mv.pci_dac_offset;
++#endif
++    }
++
++    if ((type & PTE_PciNotLocal) == 0)
++      pte = (phys >> PTE_PADDR_SHIFT) | type;
++    else
++    {
++      unsigned topaddr = elan4mmu_alloc_topaddr (dev, phys, type);
++      
++      if (dev->dev_topaddrmode)
++          pte = (phys >> PTE_PADDR_SHIFT) | (type & ~0xc) | (topaddr << 2);
++      else
++          pte = ((phys >> PTE_PADDR_SHIFT) & ~PTE_TOPADDR_MASK) | (((E4_uint64) topaddr) << 45) | type;
++    }
++
++    return pte;
++}
++
++physaddr_t
++elan4mmu_pte2phys (ELAN4_DEV *dev, E4_uint64 pte)
++{
++    physaddr_t sdram_base = ioaddr2paddr (pci_resource_start (dev->dev_osdep.pdev, ELAN4_BAR_SDRAM));
++    physaddr_t regs_base  = ioaddr2paddr (pci_resource_start (dev->dev_osdep.pdev, ELAN4_BAR_REGISTERS));
++    physaddr_t phys;
++    
++    if (pte & PTE_PciNotLocal)
++    {
++      if (dev->dev_topaddrmode)
++          phys = ((physaddr_t)(dev->dev_topaddr[(pte >> 2) & 3] & 0xfffc) << 48) | ((pte & PTE_PPN_MASK) << PTE_PADDR_SHIFT);
++      else
++          phys = ((physaddr_t)(dev->dev_topaddr[(pte >> 45) & 3] & 0xffff) << 48)| ((pte & PTE_PPN_MASK & ~PTE_TOPADDR_MASK) << PTE_PADDR_SHIFT);
++
++#ifdef LINUX_SPARC    /* XXXX if not local pci bus, then or in the bypass bit */
++      phys ^= 0xfffe000000000000;
++#endif
++
++#if defined(__alpha)
++      phys ^= alpha_mv.pci_dac_offset;
++#endif
++      return phys;
++    }
++    
++    if (pte & PTE_CommandQueue)
++      return (regs_base | ((pte & PTE_PPN_MASK) << PTE_PADDR_SHIFT));
++    
++    /* sdram */
++    return (sdram_base | ((pte & PTE_PPN_MASK) << PTE_PADDR_SHIFT));
++}
++
++EXPORT_SYMBOL(elan4mmu_phys2pte);
++EXPORT_SYMBOL(elan4mmu_pte2phys);
+Index: linux-2.6.5/drivers/net/qsnet/elan4/neterr.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan4/neterr.c  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan4/neterr.c       2005-05-11 12:10:12.455929992 -0400
+@@ -0,0 +1,270 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: neterr.c,v 1.4.6.3 2004/11/05 13:11:17 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/neterr.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan4/sdram.h>
++#include <elan4/debug.h>
++#include <elan4/device.h>
++#include <elan4/commands.h>
++#include <elan4/trtype.h>
++#include <elan4/neterr.h>
++
++typedef struct neterr_inputq
++{
++    E4_InputQueue     inputq;                                 /* input queue */
++    E4_Event32                qevent;                                 /* input queue event */
++    E4_uint64         sent;                                   /* # messages sent (cq flow control)*/
++} NETERR_INPUTQ;
++
++#define NETERR_NSLOTS 64                                      /* single page of queue space (4Kb) */
++
++#define NETERR_RETRIES        16
++#define NETERR_CQ_SIZE        CQ_Size8K
++#define NETERR_CQ_MSGS        (CQ_Size(NETERR_CQ_SIZE) / (21*8))
++#define NETERR_VP_COUNT       64                                      /* this *must* be > NETERR_CQ_MSGS */
++#define NETERR_VP_BASE        1                                       /* use vp 1 upwards */
++
++void
++elan4_neterr_interrupt (ELAN4_DEV *dev, void *arg)
++{
++    E4_Addr          qfptr  = elan4_sdram_readq (dev, dev->dev_neterr_inputq + offsetof (NETERR_INPUTQ, inputq.q_fptr));
++    E4_Addr          qbptr  = elan4_sdram_readq (dev, dev->dev_neterr_inputq + offsetof (NETERR_INPUTQ, inputq.q_bptr));
++    E4_Addr          qfirst = DEVICE_NETERR_SLOTS_ADDR;
++    E4_Addr          qlast  = qfirst + (NETERR_NSLOTS-1) * ELAN4_NETERR_MSG_SIZE;
++    ELAN4_CQ        *cq     = dev->dev_neterr_intcq;
++    int              count  = 0;
++    ELAN4_CTXT      *ctxt;
++    ELAN4_NETERR_MSG msg;
++
++    while (qfptr != qbptr)
++    {
++      elan4_sdram_copyq_from_sdram (dev, dev->dev_neterr_slots + (qfptr - qfirst), &msg, ELAN4_NETERR_MSG_SIZE);
++
++      ctxt = elan4_networkctxt (dev, msg.msg_context);
++
++      if (ctxt != NULL && ctxt->ctxt_ops->op_neterrmsg)
++          ctxt->ctxt_ops->op_neterrmsg (ctxt, &msg);
++      else
++          PRINTF (DBG_DEVICE, DBG_NETERR, "elan4_neterr_interrupt: no process - sender %d.%d\n", msg.msg_sender.loc_node, msg.msg_sender.loc_context);
++
++      count++;
++
++      /* move on the from pointer */
++      qfptr = (qfptr == qlast) ? qfirst : qfptr + ELAN4_NETERR_MSG_SIZE;
++
++      elan4_sdram_writeq (dev, dev->dev_neterr_inputq + offsetof (NETERR_INPUTQ, inputq.q_fptr), qfptr);
++    }
++
++    if (count == 0)
++    {
++      printk ("elan4_neterr_interrupt: spurious\n");
++      return;
++    }
++
++    /* Issue the waitevent to the interrupt queue */
++    writeq (WAIT_EVENT_CMD | (DEVICE_NETERR_INPUTQ_ADDR + offsetof (NETERR_INPUTQ, qevent)),  cq->cq_mapping);
++    writeq (  E4_EVENT_INIT_VALUE (-32 * count, E4_EVENT_WRITE, E4_EVENT_DTYPE_LONG, 0),      cq->cq_mapping);
++    writeq (  DEVICE_NETERR_INTCQ_ADDR,                                                               cq->cq_mapping);
++    writeq (INTERRUPT_CMD | (dev->dev_neterr_intop.op_cookie << E4_MAIN_INT_SHIFT),           cq->cq_mapping);
++
++    pioflush_reg (dev);
++}
++
++int
++elan4_neterr_init (ELAN4_DEV *dev)
++{
++    unsigned int intqaddr;
++    E4_Addr    qfirst, qlast;
++    
++    if ((dev->dev_neterr_inputq = elan4_sdram_alloc (dev, SDRAM_PAGE_SIZE)) == 0)
++      return 0;
++
++    if ((dev->dev_neterr_slots = elan4_sdram_alloc (dev, roundup (NETERR_NSLOTS * ELAN4_NETERR_MSG_SIZE, SDRAM_PAGE_SIZE))) == 0)
++      return 0;
++
++    if ((dev->dev_neterr_msgcq = elan4_alloccq (&dev->dev_ctxt, NETERR_CQ_SIZE, CQ_STENEnableBit | CQ_WriteEnableBit, CQ_Priority)) == NULL)
++      return 0;
++
++    if ((dev->dev_neterr_intcq = elan4_alloccq (&dev->dev_ctxt, CQ_Size1K, CQ_WaitEventEnableBit | CQ_InterruptEnableBit, CQ_Priority)) == NULL)
++      return 0;
++
++    intqaddr = (dev->dev_cqoffset + elan4_cq2num (dev->dev_neterr_intcq)) * CQ_CommandMappingSize;
++    qfirst   = DEVICE_NETERR_SLOTS_ADDR;
++    qlast    = qfirst + (NETERR_NSLOTS-1) * ELAN4_NETERR_MSG_SIZE;
++
++    spin_lock_init (&dev->dev_neterr_lock);
++
++    /* Register an interrupt operation */
++    dev->dev_neterr_intop.op_function = elan4_neterr_interrupt;
++    dev->dev_neterr_intop.op_arg      = NULL;
++
++    elan4_register_intop (dev, &dev->dev_neterr_intop);
++
++    /* Initialise the inputq descriptor and event */
++    elan4_sdram_writeq (dev, dev->dev_neterr_inputq + offsetof (NETERR_INPUTQ, inputq.q_fptr), qfirst);
++    elan4_sdram_writeq (dev, dev->dev_neterr_inputq + offsetof (NETERR_INPUTQ, inputq.q_bptr), qfirst);
++    elan4_sdram_writeq (dev, dev->dev_neterr_inputq + offsetof (NETERR_INPUTQ, inputq.q_control), E4_InputQueueControl (qfirst, qlast, ELAN4_NETERR_MSG_SIZE));
++    elan4_sdram_writeq (dev, dev->dev_neterr_inputq + offsetof (NETERR_INPUTQ, inputq.q_event), DEVICE_NETERR_INPUTQ_ADDR + offsetof (NETERR_INPUTQ, qevent));
++    
++    elan4_sdram_writeq (dev, dev->dev_neterr_inputq + offsetof (NETERR_INPUTQ, qevent.ev_CountAndType), E4_EVENT_INIT_VALUE (-32, E4_EVENT_WRITE, E4_EVENT_DTYPE_LONG, 0));
++    elan4_sdram_writeq (dev, dev->dev_neterr_inputq + offsetof (NETERR_INPUTQ, qevent.ev_WritePtr), DEVICE_NETERR_INTCQ_ADDR);
++    elan4_sdram_writeq (dev, dev->dev_neterr_inputq + offsetof (NETERR_INPUTQ, qevent.ev_WriteValue), (dev->dev_neterr_intop.op_cookie << E4_MAIN_INT_SHIFT) | INTERRUPT_CMD);
++
++    elan4_sdram_writeq (dev, dev->dev_neterr_inputq + offsetof (NETERR_INPUTQ, sent), 0);
++
++    /* Map them all into the device context */
++    elan4mmu_pteload (&dev->dev_ctxt, 0, DEVICE_NETERR_INPUTQ_ADDR, (dev->dev_neterr_inputq >> PTE_PADDR_SHIFT) | PTE_SetPerm(PERM_RemoteAll));
++    elan4mmu_pteload (&dev->dev_ctxt, 0, DEVICE_NETERR_INTCQ_ADDR,  (intqaddr >> PTE_PADDR_SHIFT) | PTE_SetPerm(PERM_LocDataWrite) | PTE_CommandQueue);
++    elan4mmu_pteload (&dev->dev_ctxt, 0, DEVICE_NETERR_SLOTS_ADDR, (dev->dev_neterr_slots >> PTE_PADDR_SHIFT) | PTE_SetPerm(PERM_DataReadWrite));
++
++    /* finally attach to the neterr context */
++    if (elan4_attach_filter (&dev->dev_ctxt, ELAN4_NETERR_CONTEXT_NUM) != 0)
++      panic ("elan4_neterr_init: failed to attach to neterr context\n");
++
++    /* and drop the context filter */
++    elan4_set_filter (&dev->dev_ctxt, ELAN4_NETERR_CONTEXT_NUM, E4_FILTER_HIGH_PRI);
++
++    return 1;
++}
++
++void
++elan4_neterr_destroy (ELAN4_DEV *dev)
++{
++    if (dev->dev_neterr_intcq)
++    {
++      elan4_detach_filter (&dev->dev_ctxt, ELAN4_NETERR_CONTEXT_NUM);
++      
++      elan4mmu_unload_range (&dev->dev_ctxt, 0, DEVICE_NETERR_SLOTS_ADDR,  1 << dev->dev_pageshift[0]);
++      elan4mmu_unload_range (&dev->dev_ctxt, 0, DEVICE_NETERR_INTCQ_ADDR,  1 << dev->dev_pageshift[0]);
++      elan4mmu_unload_range (&dev->dev_ctxt, 0, DEVICE_NETERR_INPUTQ_ADDR, 1 << dev->dev_pageshift[0]);
++
++      spin_lock_destroy (&dev->dev_neterr_lock);
++    }
++
++    if (dev->dev_neterr_intcq)
++      elan4_freecq (&dev->dev_ctxt, dev->dev_neterr_intcq);
++    dev->dev_neterr_intcq = NULL;
++
++    if (dev->dev_neterr_msgcq)
++      elan4_freecq (&dev->dev_ctxt, dev->dev_neterr_msgcq);
++    dev->dev_neterr_msgcq = NULL;
++
++    if (dev->dev_neterr_slots)
++      elan4_sdram_free (dev, dev->dev_neterr_slots, roundup (NETERR_NSLOTS * ELAN4_NETERR_MSG_SIZE, SDRAM_PAGE_SIZE));
++    dev->dev_neterr_slots = 0;
++    
++    if (dev->dev_neterr_inputq)
++      elan4_sdram_free (dev, dev->dev_neterr_inputq, SDRAM_PAGE_SIZE);
++    dev->dev_neterr_inputq = 0;
++}
++
++int
++elan4_neterr_sendmsg (ELAN4_DEV *dev, unsigned int nodeid, unsigned int retries, ELAN4_NETERR_MSG *msg)
++{
++    ELAN4_CQ  *cq = dev->dev_neterr_msgcq;
++    E4_uint64  sent;
++    E4_VirtualProcessEntry route;
++    unsigned int vp;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->dev_neterr_lock, flags);
++
++    sent = elan4_sdram_readq (dev, dev->dev_neterr_inputq + offsetof (NETERR_INPUTQ, sent));
++
++    PRINTF (DBG_DEVICE, DBG_NETERR, "elan4_neterr_sendmsg: nodeid=%d retries=%d cookie=%llx sender=%d,%d%s\n", 
++          nodeid, retries, msg->msg_cookies[0], msg->msg_sender.loc_node, msg->msg_sender.loc_context,
++          (dev->dev_neterr_queued - sent) >= NETERR_CQ_MSGS ? " - no cq space" : "");
++
++    if ((dev->dev_neterr_queued - sent) >= NETERR_CQ_MSGS)
++    {
++      spin_unlock_irqrestore (&dev->dev_neterr_lock, flags);
++      return 0;
++    }
++
++    vp = NETERR_VP_BASE + (dev->dev_neterr_queued % NETERR_VP_COUNT);
++
++    if (elan4_generate_route (&dev->dev_position, &route, ELAN4_NETERR_CONTEXT_NUM, nodeid, nodeid, FIRST_SYSTEM_PACKET | FIRST_HIGH_PRI) < 0)
++    {
++      spin_unlock_irqrestore (&dev->dev_neterr_lock, flags);
++      return 0;
++    }
++
++    elan4_write_route (dev, dev->dev_routetable, vp, &route);
++
++    writeq ((GUARD_CMD | GUARD_CHANNEL(0) | GUARD_RESET(retries)),                            cq->cq_mapping);
++    writeq (NOP_CMD,                                                                          cq->cq_mapping);
++    
++    writeq (OPEN_STEN_PKT_CMD | OPEN_PACKET (0, PACK_OK | RESTART_COUNT_ZERO, vp),            cq->cq_mapping);
++    writeq (SEND_TRANS_CMD | (TR_INPUT_Q_GETINDEX << 16),                                     cq->cq_mapping);
++    writeq (  DEVICE_NETERR_INPUTQ_ADDR + offsetof (NETERR_INPUTQ, inputq),                   cq->cq_mapping);
++
++    writeq (SEND_TRANS_CMD | (TR_WRITE (64 >> 3, 0, TR_DATATYPE_DWORD) << 16),                        cq->cq_mapping);
++    writeq (  0 /* address */,                                                                        cq->cq_mapping);
++    writeq (  ((E4_uint64 *) msg)[0],                                                         cq->cq_mapping);
++    writeq (  ((E4_uint64 *) msg)[1],                                                         cq->cq_mapping);
++    writeq (  ((E4_uint64 *) msg)[2],                                                         cq->cq_mapping);
++    writeq (  ((E4_uint64 *) msg)[3],                                                         cq->cq_mapping);
++    writeq (  ((E4_uint64 *) msg)[4],                                                         cq->cq_mapping);
++    writeq (  ((E4_uint64 *) msg)[5],                                                         cq->cq_mapping);
++    writeq (  ((E4_uint64 *) msg)[6],                                                         cq->cq_mapping);
++    writeq (  ((E4_uint64 *) msg)[7],                                                         cq->cq_mapping);
++
++    writeq (SEND_TRANS_CMD | (TR_INPUT_Q_COMMIT << 16),                                               cq->cq_mapping);
++    writeq (  DEVICE_NETERR_INPUTQ_ADDR + offsetof (NETERR_INPUTQ, inputq),                   cq->cq_mapping);
++    writeq (  0 /* cookie */,                                                                 cq->cq_mapping);
++    
++    writeq (GUARD_CMD | GUARD_CHANNEL(0) | GUARD_RESET(NETERR_RETRIES),                               cq->cq_mapping);
++    writeq (WRITE_DWORD_CMD | (DEVICE_NETERR_INPUTQ_ADDR + offsetof (NETERR_INPUTQ, sent)),   cq->cq_mapping);
++    writeq (  ++dev->dev_neterr_queued,                                                               cq->cq_mapping);
++
++    pioflush_reg (dev);
++
++    spin_unlock_irqrestore (&dev->dev_neterr_lock, flags);
++
++    return 1;
++}
++
++int
++elan4_neterr_iproc_trap (ELAN4_DEV *dev, ELAN4_IPROC_TRAP *trap)
++{
++    E4_IprocTrapHeader *hdrp = &trap->tr_transactions[trap->tr_trappedTrans];
++    unsigned long flags;
++
++    switch (IPROC_TrapValue (hdrp->IProcStatusCntxAndTrType))
++    {
++    case InputEopErrorOnWaitForEop:
++    case InputEopErrorTrap:
++    case InputCrcErrorAfterPAckOk:
++      return 1;
++
++    case InputEventEngineTrapped:
++      printk ("elan%d: device_iproc_trap: InputEventEngineTrapped - Trans=%x TrAddr=%llx\n", 
++              dev->dev_instance, (int)IPROC_TransactionType (hdrp->IProcStatusCntxAndTrType), (long long) hdrp->TrAddr);
++
++      if ((IPROC_TransactionType (hdrp->IProcStatusCntxAndTrType) & TR_OPCODE_MASK) == (TR_INPUT_Q_COMMIT & TR_OPCODE_MASK) &&
++          hdrp->TrAddr == DEVICE_NETERR_INPUTQ_ADDR + offsetof (NETERR_INPUTQ, inputq))
++      {
++          spin_lock_irqsave (&dev->dev_neterr_lock, flags);
++          writeq ((DEVICE_NETERR_INPUTQ_ADDR + offsetof (NETERR_INPUTQ, qevent)) | SET_EVENT_CMD, dev->dev_neterr_msgcq->cq_mapping);
++          spin_unlock_irqrestore (&dev->dev_neterr_lock, flags);
++          return 1;
++      }
++      
++    default:
++      return 0;
++    }
++}
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan4/procfs_Linux.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan4/procfs_Linux.c    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan4/procfs_Linux.c 2005-05-11 12:10:12.457929688 -0400
+@@ -0,0 +1,1074 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: procfs_Linux.c,v 1.27.2.9 2005/03/09 12:00:08 addy Exp $ $Name: QSNETMODULES-4-31_20050321 $"
++/*      $Source: /cvs/master/quadrics/elan4mod/procfs_Linux.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <linux/module.h>
++#include <linux/proc_fs.h>
++#include <linux/ctype.h>
++
++#include <qsnet/procfs_linux.h>
++
++#include <elan4/i2c.h>
++#include <elan4/debug.h>
++#include <elan4/device.h>
++#include <elan4/user.h>
++
++/*
++ *
++ * procfs format for elan4:
++ *
++ * /proc/qsnet/elan4/config
++ *    elan4_debug
++ *    elan4_debug_toconsole
++ *    elan4_debug_tobuffer
++ *    elan4_debug_display_ctxt
++ *    elan4_debug_ignore_ctxt
++ *    elan4_debug_ignore_type
++ *    elan4_debug_mmu
++ *    elan4_mainint_punt_loops
++ *    user_p2p_route_options
++ *    user_bcast_route_options
++ *
++ * /proc/qsnet/elan4/deviceN
++ *    stats
++ *    position
++ *    vpd
++ */
++
++struct proc_dir_entry *elan4_procfs_root;
++struct proc_dir_entry *elan4_config_root;
++
++/* borrowed from fs/proc/proc_misc - helper for proc_read_int */
++static int 
++proc_calc_metrics(char *page, char **start, off_t off, int count, int *eof, int len)
++{
++    if (len <= off+count) *eof = 1;
++    *start = page + off;
++    len -= off;
++    if (len>count) len = count;
++    if (len<0) len = 0;
++    return len;
++}
++
++static int
++proc_read_devinfo (char *page, char **start, off_t off,
++                  int count, int *eof, void *data)
++{
++    ELAN4_DEV *dev = (ELAN4_DEV *) data;
++    int        len = 0;
++
++    if (! dev)
++      len = sprintf (page, "<unknown>\n");
++    else
++    {
++      len += sprintf (page + len, "dev_vendor_id            0x%x\n", dev->dev_devinfo.dev_vendor_id);
++      len += sprintf (page + len, "dev_device_id            0x%x\n", dev->dev_devinfo.dev_vendor_id);
++      len += sprintf (page + len, "dev_revision_id          0x%x\n", dev->dev_devinfo.dev_revision_id);
++      len += sprintf (page + len, "dev_instance             0x%x\n", dev->dev_devinfo.dev_instance);
++      len += sprintf (page + len, "dev_rail                 0x%x\n", dev->dev_devinfo.dev_rail);
++      len += sprintf (page + len, "dev_driver_version       0x%x\n", dev->dev_devinfo.dev_driver_version);
++      len += sprintf (page + len, "dev_params_mask          0x%x\n", dev->dev_devinfo.dev_params_mask);
++      len += sprintf (page + len, "dev_params:                  \n");
++      len += sprintf (page + len, " 0  - PciCmdQPadFlag     0x%x\n", dev->dev_devinfo.dev_params.values[0]);
++      len += sprintf (page + len, " 1  - EventCopyWinPt     0x%x\n", dev->dev_devinfo.dev_params.values[1]);
++      len += sprintf (page + len, " 2  - PciWriteCombining  0x%x\n", dev->dev_devinfo.dev_params.values[2]);
++      len += sprintf (page + len, " 3  -                    0x%x\n", dev->dev_devinfo.dev_params.values[3]);
++      len += sprintf (page + len, " 4  -                    0x%x\n", dev->dev_devinfo.dev_params.values[4]);
++      len += sprintf (page + len, " 5  -                    0x%x\n", dev->dev_devinfo.dev_params.values[5]);
++      len += sprintf (page + len, " 6  -                    0x%x\n", dev->dev_devinfo.dev_params.values[6]);
++      len += sprintf (page + len, " 7  -                    0x%x\n", dev->dev_devinfo.dev_params.values[7]);
++      len += sprintf (page + len, " 8  -                    0x%x\n", dev->dev_devinfo.dev_params.values[8]);
++      len += sprintf (page + len, " 9  -                    0x%x\n", dev->dev_devinfo.dev_params.values[9]);
++      len += sprintf (page + len, " 10 -                    0x%x\n", dev->dev_devinfo.dev_params.values[10]);
++      len += sprintf (page + len, " 11 - features           0x%x\n", dev->dev_devinfo.dev_params.values[11]);
++      len += sprintf (page + len, "dev_num_down_links_value 0x%x\n", dev->dev_devinfo.dev_num_down_links_value);
++    }
++
++    return (qsnet_proc_calc_metrics (page, start, off, count, eof, len));
++}
++
++static int
++proc_read_position (char *page, char **start, off_t off,
++                  int count, int *eof, void *data)
++{
++    ELAN4_DEV *dev = (ELAN4_DEV *) data;
++    int        len;
++
++    if (dev->dev_position.pos_mode == ELAN_POS_UNKNOWN)
++      len = sprintf (page, "<unknown>\n");
++    else
++      len = sprintf (page, 
++                     "NodeId                 %d\n"
++                     "NumLevels              %d\n"
++                     "NumNodes               %d\n",
++                     dev->dev_position.pos_nodeid, 
++                     dev->dev_position.pos_levels, 
++                     dev->dev_position.pos_nodes);
++
++    return (qsnet_proc_calc_metrics (page, start, off, count, eof, len));
++}
++
++static int
++proc_write_position (struct file *file, const char *buf, unsigned long count, void *data)
++{
++    ELAN4_DEV *dev     = (ELAN4_DEV *) data;
++    unsigned  nodeid   = ELAN_INVALID_NODE;
++    unsigned  numnodes = 0;
++    char     *page, *p;
++    int       res;
++    ELAN_POSITION pos;
++
++    if (count == 0)
++      return (0);
++
++    if (count >= PAGE_SIZE)
++      return (-EINVAL);
++
++    if ((page = (char *) __get_free_page (GFP_KERNEL)) == NULL)
++      return (-ENOMEM);
++
++    MOD_INC_USE_COUNT;
++
++    if (copy_from_user (page, buf, count))
++      res = -EFAULT;
++    else
++    {
++      page[count] = '\0';
++      
++      if (page[count-1] == '\n')
++          page[count-1] = '\0';
++
++      if (! strcmp (page, "<unknown>"))
++      {
++          pos.pos_mode      = ELAN_POS_UNKNOWN;
++          pos.pos_nodeid    = ELAN_INVALID_NODE;
++          pos.pos_nodes     = 0;
++          pos.pos_levels    = 0;
++      }
++      else
++      {
++          for (p = page; *p; )
++          {
++              while (isspace (*p))
++                  p++;
++              
++              if (! strncmp (p, "NodeId=", strlen("NodeId=")))
++                  nodeid   = simple_strtoul (p + strlen ("NodeId="), NULL, 0);
++              if (! strncmp (p, "NumNodes=", strlen ("NumNodes=")))
++                  numnodes = simple_strtoul (p + strlen ("NumNodes="), NULL, 0);
++              
++              while (*p && !isspace(*p))
++                  p++;
++          }
++
++          if (elan4_compute_position (&pos, nodeid, numnodes, dev->dev_devinfo.dev_num_down_links_value) != 0)
++              printk ("elan%d: invalid values for NodeId=%d NumNodes=%d\n", dev->dev_instance, nodeid, numnodes);
++          else
++          {
++              printk ("elan%d: setting NodeId=%d NumNodes=%d NumLevels=%d\n", dev->dev_instance, pos.pos_nodeid,
++                      pos.pos_nodes, pos.pos_levels);
++
++              if (elan4_set_position (dev, &pos) < 0)
++                  printk ("elan%d: failed to set device position\n", dev->dev_instance);
++          }
++      }
++    }
++
++    MOD_DEC_USE_COUNT;
++    free_page ((unsigned long) page);
++
++    return (count);
++}
++
++static int
++proc_read_temp (char *page, char **start, off_t off,
++              int count, int *eof, void *data)
++{
++    ELAN4_DEV    *dev = (ELAN4_DEV *) data;
++    unsigned char values[2];
++    int           len;
++
++    if (i2c_disable_auto_led_update (dev) < 0)
++      len = sprintf (page, "<unknown>");
++    else
++    {
++      if (i2c_read (dev, I2C_TEMP_ADDR, 2, values) < 0)
++          len = sprintf (page, "<not-present>");
++      else
++          len = sprintf (page, "%s%d%s\n", (values[0] & 0x80) ? "-" : "",
++                         (values[0] & 0x80) ? -((signed char)values[0]) - 1 : values[0],
++                         (values[1] & 0x80) ? ".5" : ".0");
++
++      i2c_enable_auto_led_update (dev);
++    }
++
++    return (qsnet_proc_calc_metrics (page, start, off, count, eof, len));
++}
++
++static int
++proc_read_eccerr (char *page, char **start, off_t off,
++                int count, int *eof, void *data)
++{
++    ELAN4_DEV    *dev = (ELAN4_DEV *) data;
++    char          errstr[200];
++    register int  i, len = 0;
++
++    *page = '\0';
++
++    for (i = 0; i < sizeof (dev->dev_sdramerrs)/sizeof(dev->dev_sdramerrs[0]); i++)
++      if (dev->dev_sdramerrs[i].ErrorCount != 0)
++          len += sprintf (page + len, "%s occured %0d times\n",
++                          elan4_sdramerr2str (dev, dev->dev_sdramerrs[i].EccStatus, dev->dev_sdramerrs[i].ConfigReg, errstr),
++                          dev->dev_sdramerrs[i].ErrorCount);
++
++    return (qsnet_proc_calc_metrics (page, start, off, count, eof, len));
++}
++
++static int
++proc_read_vpd (char *page, char **start, off_t off,
++             int count, int *eof, void *data)
++{
++    ELAN4_DEV *dev = (ELAN4_DEV *) data;
++    int        len;
++
++    if ( elan4_read_vpd (dev, NULL, page) )
++      len = sprintf (page, "no vpd tags found\n");
++    else
++      len = strlen(page)+1;
++
++    return (qsnet_proc_calc_metrics (page, start, off, count, eof, len));
++}
++
++static int
++proc_read_linkportkey (char *page, char **start, off_t off,
++                     int count, int *eof, void *data)
++{
++    ELAN4_DEV *dev = (ELAN4_DEV *) data;
++    int        len;
++
++    len = sprintf (page, "%llx\n", read_reg64 (dev, LinkPortLock));
++
++    return (qsnet_proc_calc_metrics (page, start, off, count, eof, len));
++}
++
++static int
++proc_write_linkportkey (struct file *file, const char *buf, unsigned long count, void *data)
++{
++    ELAN4_DEV *dev = (ELAN4_DEV *) data;
++    int              res = 0;
++    char       tmpbuf[30];
++
++    if (count > sizeof (tmpbuf) - 1)
++      return -EINVAL;
++
++    MOD_INC_USE_COUNT;
++
++    if (copy_from_user (tmpbuf, buf, count))
++      res = -EFAULT;
++    else
++    {
++      tmpbuf[count] = '\0';
++
++      write_reg64 (dev, LinkPortLock, simple_strtoull (tmpbuf, NULL, 16));
++    }
++
++    MOD_DEC_USE_COUNT;
++
++    return (count);
++}
++
++static struct device_info 
++{
++    char *name;
++    int (*read_func) (char *page, char **start, off_t off, int count, int *eof, void *data);
++    int (*write_func) (struct file *file, const char *buf, unsigned long count, void *data);
++    unsigned minrev;
++} device_info[] = {
++    {"devinfo",     proc_read_devinfo,     NULL,                   0},
++    {"position",    proc_read_position,    proc_write_position,    0},
++    {"temp",        proc_read_temp,        NULL,                   1},
++    {"eccerr",      proc_read_eccerr,      NULL,                   0},
++    {"vpd",         proc_read_vpd,         NULL,                   0},
++    {"linkportkey", proc_read_linkportkey, proc_write_linkportkey, 0},
++};
++
++static int
++proc_read_link_stats (char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++    ELAN4_DEV *dev = (ELAN4_DEV *) data;
++    char      *p   = page;
++
++    p += sprintf (p, "%20s %ld\n", "link_errors", dev->dev_stats.s_link_errors);
++    p += sprintf (p, "%20s %ld\n", "lock_errors", dev->dev_stats.s_lock_errors);
++    p += sprintf (p, "%20s %ld\n", "deskew_errors", dev->dev_stats.s_deskew_errors);
++    p += sprintf (p, "%20s %ld\n", "phase_errors", dev->dev_stats.s_phase_errors);
++
++    p += sprintf (p, "%20s %ld\n", "data_errors", dev->dev_stats.s_data_errors);
++    p += sprintf (p, "%20s %ld\n", "fifo_overflow0", dev->dev_stats.s_fifo_overflow0);
++    p += sprintf (p, "%20s %ld\n", "fifo_overflow1", dev->dev_stats.s_fifo_overflow1);
++    p += sprintf (p, "%20s %ld\n", "mod45changed", dev->dev_stats.s_mod45changed);
++    p += sprintf (p, "%20s %ld\n", "pack_not_seen", dev->dev_stats.s_pack_not_seen);
++
++    p += sprintf (p, "%20s %ld\n", "linkport_keyfail", dev->dev_stats.s_linkport_keyfail);
++    p += sprintf (p, "%20s %ld\n", "eop_reset", dev->dev_stats.s_eop_reset);
++    p += sprintf (p, "%20s %ld\n", "bad_length", dev->dev_stats.s_bad_length);
++    p += sprintf (p, "%20s %ld\n", "crc_error", dev->dev_stats.s_crc_error);
++    p += sprintf (p, "%20s %ld\n", "crc_bad", dev->dev_stats.s_crc_bad);
++
++    p += sprintf (p, "%20s %ld\n", "cproc_timeout", dev->dev_stats.s_cproc_timeout);
++    p += sprintf (p, "%20s %ld\n", "dproc_timeout", dev->dev_stats.s_dproc_timeout);
++
++    return (proc_calc_metrics (page, start, off, count, eof, p - page));
++}
++
++static char *
++proc_sprintf_bucket_stat (char *p, char *name, unsigned long *stats, int *buckets)
++{
++    int i;
++
++    p += sprintf (p, "%20s ", name);
++
++    for (i = 0; i < ELAN4_DEV_STATS_BUCKETS-1; i++)
++      p += sprintf (p, "%ld(<=%d) ", stats[i], buckets[i]);
++    p += sprintf (p, "%ld(>%d)\n", stats[i], buckets[i-1]);
++
++    return p;
++}
++
++static int
++proc_read_intr_stats (char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++    ELAN4_DEV *dev = (ELAN4_DEV *) data;
++    char      *p   = page;
++
++    p += sprintf (p, "%20s %ld\n", "interrupts",       dev->dev_stats.s_interrupts);
++    p += sprintf (p, "%20s %ld\n", "haltints",         dev->dev_stats.s_haltints);
++
++    p += sprintf (p, "%20s %ld\n", "mainint_punts",    dev->dev_stats.s_mainint_punts);
++    p += sprintf (p, "%20s %ld\n", "mainint_rescheds", dev->dev_stats.s_mainint_rescheds);
++
++    p  = proc_sprintf_bucket_stat (p, "mainints", dev->dev_stats.s_mainints, MainIntBuckets);
++
++    return (proc_calc_metrics (page, start, off, count, eof, p - page));
++}
++
++static int
++proc_read_trap_stats (char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++    ELAN4_DEV *dev = (ELAN4_DEV *) data;
++    char      *p   = page;
++
++    p += sprintf (p, "%20s %ld\n", "cproc_traps", dev->dev_stats.s_cproc_traps);
++    p += sprintf (p, "%20s %ld\n", "dproc_traps", dev->dev_stats.s_dproc_traps);
++    p += sprintf (p, "%20s %ld\n", "eproc_traps", dev->dev_stats.s_eproc_traps);
++    p += sprintf (p, "%20s %ld\n", "iproc_traps", dev->dev_stats.s_iproc_traps);
++    p += sprintf (p, "%20s %ld\n", "tproc_traps", dev->dev_stats.s_tproc_traps);
++
++    return (proc_calc_metrics (page, start, off, count, eof, p - page));
++}
++
++static int
++proc_read_cproc_trap_stats (char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++    ELAN4_DEV *dev = (ELAN4_DEV *) data;
++    char      *p   = page;
++    int        i;
++    extern char *const CProcTrapNames[];
++
++    for (i = 0; i < sizeof (dev->dev_stats.s_cproc_trap_types)/sizeof(dev->dev_stats.s_cproc_trap_types[0]); i++)
++      p += sprintf (p, "%-40s %ld\n", CProcTrapNames[i], dev->dev_stats.s_cproc_trap_types[i]);
++
++    return (proc_calc_metrics (page, start, off, count, eof, p - page));
++}
++
++static int
++proc_read_dproc_trap_stats (char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++    ELAN4_DEV *dev = (ELAN4_DEV *) data;
++    char      *p   = page;
++    int        i;
++    extern char *const DProcTrapNames[];
++
++    for (i = 0; i < sizeof (dev->dev_stats.s_dproc_trap_types)/sizeof(dev->dev_stats.s_dproc_trap_types[0]); i++)
++      p += sprintf (p, "%-40s %ld\n", DProcTrapNames[i], dev->dev_stats.s_dproc_trap_types[i]);
++
++    return (proc_calc_metrics (page, start, off, count, eof, p - page));
++}
++
++static int
++proc_read_eproc_trap_stats (char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++    ELAN4_DEV *dev = (ELAN4_DEV *) data;
++    char      *p   = page;
++    int        i;
++    extern char *const EProcTrapNames[];
++
++    for (i = 0; i < sizeof (dev->dev_stats.s_eproc_trap_types)/sizeof(dev->dev_stats.s_eproc_trap_types[0]); i++)
++      p += sprintf (p, "%-40s %ld\n", EProcTrapNames[i], dev->dev_stats.s_eproc_trap_types[i]);
++
++    return (proc_calc_metrics (page, start, off, count, eof, p - page));
++}
++
++static int
++proc_read_iproc_trap_stats (char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++    ELAN4_DEV *dev = (ELAN4_DEV *) data;
++    char      *p   = page;
++    int        i;
++    extern char *const IProcTrapNames[];
++
++    for (i = 0; i < sizeof (dev->dev_stats.s_iproc_trap_types)/sizeof(dev->dev_stats.s_iproc_trap_types[0]); i++)
++      p += sprintf (p, "%-40s %ld\n", IProcTrapNames[i], dev->dev_stats.s_iproc_trap_types[i]);
++
++    return (proc_calc_metrics (page, start, off, count, eof, p - page));
++}
++
++static int
++proc_read_tproc_trap_stats (char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++    ELAN4_DEV *dev = (ELAN4_DEV *) data;
++    char      *p   = page;
++    int        i;
++    extern char *const TProcTrapNames[];
++
++    for (i = 0; i < sizeof (dev->dev_stats.s_tproc_trap_types)/sizeof(dev->dev_stats.s_tproc_trap_types[0]); i++)
++      p += sprintf (p, "%-40s %ld\n", TProcTrapNames[i], dev->dev_stats.s_tproc_trap_types[i]);
++
++    return (proc_calc_metrics (page, start, off, count, eof, p - page));
++}
++
++static int
++proc_read_sdram_stats (char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++    ELAN4_DEV *dev = (ELAN4_DEV *) data;
++    char      *p   = page;
++
++    p += sprintf (p, "%20s %ld\n",  "correctable_errors", dev->dev_stats.s_correctable_errors);
++    p += sprintf (p, "%20s %ld\n",  "multiple_errors",    dev->dev_stats.s_multiple_errors);
++    p += sprintf (p, "%20s %ldK\n", "sdram_bytes_free",   dev->dev_stats.s_sdram_bytes_free/1024);
++
++    return (proc_calc_metrics (page, start, off, count, eof, p - page));
++}
++
++void
++elan4_ringbuf_store (ELAN4_ROUTE_RINGBUF *ringbuf, E4_VirtualProcessEntry *route, ELAN4_DEV *dev)
++{
++    int newend;
++    
++    ASSERT (kmutex_is_locked (&dev->dev_lock));
++
++    memcpy(&ringbuf->routes[ringbuf->end], route, sizeof(E4_VirtualProcessEntry));
++    newend = ringbuf->end + 1;
++    if (newend >= DEV_STASH_ROUTE_COUNT)
++        newend -= DEV_STASH_ROUTE_COUNT;
++    if (newend == ringbuf->start)
++        ringbuf->start += 1;
++    if (ringbuf->start >= DEV_STASH_ROUTE_COUNT)
++        ringbuf->start -= DEV_STASH_ROUTE_COUNT;
++    ringbuf->end = newend;
++}
++      
++static int
++proc_read_dproc_timeout_stats (char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++      ELAN4_DEV *dev = (ELAN4_DEV *) data;
++      char      *p   = page;
++      unsigned int *dproc_timeout;
++
++      dproc_timeout = dev->dev_dproc_timeout;
++
++      if (!dproc_timeout) 
++              p += sprintf (p, "No stats available\n");
++      else
++      {
++              int i;
++
++              for (i=0; i<dev->dev_position.pos_nodes; i++) 
++                      if (dproc_timeout[i] != 0) 
++                              p += sprintf (p, "Node %d: %u errors\n", i, dproc_timeout[i]);
++      }
++
++      return (proc_calc_metrics (page, start, off, count, eof, p - page));
++}
++
++static int
++elan4_route2str (E4_VirtualProcessEntry *route, char *routeStr)
++{
++    int        part = 0;
++    int        shift;
++    int        broadcast;
++    E4_uint64  value;
++    char      *ptr = routeStr;
++    int        b;
++
++    /* unpack first */
++    value = route->Values[part] & 0x7f;
++    if ( (value & 0x78) == 0) {
++        /* empty route */
++        strcpy(routeStr,"Invalid lead route");
++        return (-EINVAL);
++    }
++
++    if ( value & 0x40 ) {
++        /* broad cast */
++      strcpy(routeStr,"Broadcast");
++      return (-EINVAL);
++    } else {
++        switch ((value  & 0x30) >> 4) {
++        case 0: { *ptr++ = '0' + (value & 0x7); break; }
++        case 1: { *ptr++ = 'M';                 break; }
++        case 2: { *ptr++ = 'U';                 break; }
++        case 3: { *ptr++ = 'A';                 break; }
++        }
++    }
++
++    shift = 16;
++    broadcast = 0;
++    while ( 1 ) {
++        b =  (route->Values[part] >> shift) & 0xf;
++
++        if ( broadcast ) {
++            /* about to pick up the second byte of a broadcast pair */
++            broadcast = 0;
++        } else {
++            if ( b & 0x8) {
++                /*  output link */
++                 *ptr++ = '0' + (b & 0x7);
++            } else {
++                if ( b & 0x4) {
++                    /* broad cast */
++                    broadcast = 1;
++                } else {
++                    switch ( b & 0x3 ) {
++                    case 0: { *ptr++ =  0 ; return (0);     break; }
++                    case 1: { *ptr++ = 'M';                 break; }
++                    case 2: { *ptr++ = 'U';                 break; }
++                    case 3: { *ptr++ = 'A';                 break; }
++                    }
++                }
++            }
++        }
++
++        shift += 4; 
++        if ( part != 0 ) {
++            if ( shift > 36) {
++                /* too far, now in the crc value */
++                strcpy(routeStr,"Invalid route length");
++                return (-EINVAL);
++            }
++        } else { 
++            if ( shift >= 64) { 
++                /* move to the next 64 bits */
++                part = 1;
++                shift = 2;
++            }
++        }
++    }
++
++    /* never reached */
++    return (-EINVAL);
++}
++
++
++static int
++proc_read_dproc_timeout_routes (char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++      ELAN4_DEV *dev = (ELAN4_DEV *) data;
++      char      *p   = page;
++      ELAN4_ROUTE_RINGBUF *ringbuf;
++      char      routestr[33];
++
++      ringbuf = &dev->dev_dproc_timeout_routes;
++
++      if (!ringbuf) 
++              p += sprintf (p, "No stats available\n");
++      else
++      {
++              int start;
++              int end;
++              int i;
++
++              memset(&routestr, 0, 33);
++              
++              kmutex_lock(&dev->dev_lock);
++              
++              start = ringbuf->start;
++              end = ringbuf->end;
++
++              if (end < start)
++                      end = DEV_STASH_ROUTE_COUNT;
++
++              for (i=start; i<end; i++) 
++              {
++                      elan4_route2str (&ringbuf->routes[i], routestr);
++                      p += sprintf (p, "Route %llx %llx->%s\n", ringbuf->routes[i].Values[0], ringbuf->routes[i].Values[1], routestr);
++              }
++
++              if (ringbuf->end < start)
++              {
++                      start = 0;
++                      end = ringbuf->end;
++                      for (i=start; i<end; i++)
++                      {
++                              elan4_route2str (&ringbuf->routes[i], routestr);
++                              p += sprintf (p, "Route %llx %llx->%s\n", ringbuf->routes[i].Values[0], ringbuf->routes[i].Values[1], routestr);
++                      }
++              }
++
++              kmutex_unlock(&dev->dev_lock);
++      }
++
++      return (proc_calc_metrics (page, start, off, count, eof, p - page));
++}
++
++
++static int
++proc_read_cproc_timeout_stats (char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++      ELAN4_DEV *dev = (ELAN4_DEV *) data;
++      char      *p   = page;
++      unsigned int *cproc_timeout;
++
++      cproc_timeout = dev->dev_cproc_timeout;
++
++      if (!cproc_timeout) 
++              p += sprintf (p, "No stats available\n");
++      else
++      {
++              int i;
++
++              for (i=0; i<dev->dev_position.pos_nodes; i++) 
++                      if (cproc_timeout[i] != 0) 
++                              p += sprintf (p, "Node %d: %u errors\n", i, cproc_timeout[i]);
++      }
++
++      return (proc_calc_metrics (page, start, off, count, eof, p - page));
++}
++
++static int
++proc_read_cproc_timeout_routes (char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++      ELAN4_DEV *dev = (ELAN4_DEV *) data;
++      char      *p   = page;
++      ELAN4_ROUTE_RINGBUF *ringbuf;
++      char      routestr[33];
++
++      ringbuf = &dev->dev_cproc_timeout_routes;
++
++      if (!ringbuf) 
++              p += sprintf (p, "No stats available\n");
++      else
++      {
++              int start;
++              int end;
++              int i;
++
++              memset(&routestr, 0, 33);
++
++              kmutex_lock(&dev->dev_lock);
++              
++              start = ringbuf->start;
++              end = ringbuf->end;
++
++              if (end < start)
++                      end = DEV_STASH_ROUTE_COUNT;
++
++              for (i=start; i<end; i++) 
++              {
++                      elan4_route2str (&ringbuf->routes[i], routestr);
++                      p += sprintf (p, "Route %llx %llx->%s\n", ringbuf->routes[i].Values[0], ringbuf->routes[i].Values[1], routestr);
++              }
++
++              if (ringbuf->end < start)
++              {
++                      start = 0;
++                      end = ringbuf->end;
++                      for (i=start; i<end; i++)
++                      {
++                              elan4_route2str (&ringbuf->routes[i], routestr);
++                              p += sprintf (p, "Route %llx %llx->%s\n", ringbuf->routes[i].Values[0], ringbuf->routes[i].Values[1], routestr);
++                      }
++              }
++
++              kmutex_unlock(&dev->dev_lock);
++      }
++
++      return (proc_calc_metrics (page, start, off, count, eof, p - page));
++}
++
++static int
++proc_read_traperr_stats (char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++      ELAN4_DEV *dev = (ELAN4_DEV *) data;
++      char      *p   = page;
++      unsigned int *ack_errors;
++
++      ack_errors = dev->dev_ack_errors;
++
++      if (!ack_errors) 
++              p += sprintf (p, "No stats available\n");
++      else
++      {
++              int i;
++
++              for (i=0; i<dev->dev_position.pos_nodes; i++) 
++                      if (ack_errors[i] != 0) 
++                              p += sprintf (p, "Node %d: %u errors\n", i, ack_errors[i]);
++      }
++
++      return (proc_calc_metrics (page, start, off, count, eof, p - page));
++}
++
++static int
++proc_read_ackerror_routes (char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++      ELAN4_DEV *dev = (ELAN4_DEV *) data;
++      char      *p   = page;
++      ELAN4_ROUTE_RINGBUF *ringbuf;
++      char      routestr[33];
++
++      ringbuf = &dev->dev_ack_error_routes;
++
++      if (!ringbuf) 
++              p += sprintf (p, "No stats available\n");
++      else
++      {
++              int start;
++              int end;
++              int i;
++
++              memset(&routestr, 0, 33);
++
++              kmutex_lock(&dev->dev_lock);
++              
++              start = ringbuf->start;
++              end = ringbuf->end;
++
++              if (end < start)
++                      end = DEV_STASH_ROUTE_COUNT;
++
++              for (i=start; i<end; i++) 
++              {
++                      elan4_route2str (&ringbuf->routes[i], routestr);
++                      p += sprintf (p, "Route %llx %llx->%s\n", ringbuf->routes[i].Values[0], ringbuf->routes[i].Values[1], routestr);
++              }
++
++              if (ringbuf->end < start)
++              {
++                      start = 0;
++                      end = ringbuf->end;
++                      for (i=start; i<end; i++)
++                      {
++                              elan4_route2str (&ringbuf->routes[i], routestr);
++                              p += sprintf (p, "Route %llx %llx->%s\n", ringbuf->routes[i].Values[0], ringbuf->routes[i].Values[1], routestr);
++                      }
++              }
++
++              kmutex_unlock(&dev->dev_lock);
++      }
++
++      return (proc_calc_metrics (page, start, off, count, eof, p - page));
++}
++
++static struct stats_info 
++{
++    char *name;
++    int (*read_func) (char *page, char **start, off_t off, int count, int *eof, void *data);
++    int (*write_func) (struct file *file, const char *buf, unsigned long count, void *data); 
++} stats_info[] = {
++    {"link",     proc_read_link_stats, NULL},
++    {"intr",     proc_read_intr_stats, NULL},
++    {"trap",     proc_read_trap_stats, NULL},
++    {"cproc",    proc_read_cproc_trap_stats, NULL},
++    {"dproc",    proc_read_dproc_trap_stats, NULL},
++    {"eproc",    proc_read_eproc_trap_stats, NULL},
++    {"iproc",    proc_read_iproc_trap_stats, NULL},
++    {"tproc",    proc_read_tproc_trap_stats, NULL},
++    {"sdram",    proc_read_sdram_stats, NULL},
++    {"trapdmaerr", proc_read_traperr_stats, NULL},
++    {"dproctimeout", proc_read_dproc_timeout_stats, NULL},
++    {"cproctimeout", proc_read_cproc_timeout_stats, NULL},
++    {"dproctimeoutroutes", proc_read_dproc_timeout_routes, NULL},
++    {"cproctimeoutroutes", proc_read_cproc_timeout_routes, NULL},
++    {"ackerrroutes", proc_read_ackerror_routes, NULL},
++};
++
++static int
++proc_read_sysconfig (char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++    ELAN4_DEV *dev        = (ELAN4_DEV *) data;
++    E4_uint32  syscontrol = dev->dev_syscontrol;
++    int              len       = 0;
++
++   *eof = 1;
++   if (off != 0)
++      return (0);
++
++    if (syscontrol & CONT_EN_ALL_SETS)
++      len += sprintf (page + len, "%sEN_ALL_SETS", len == 0 ? "" : " ");
++    if (syscontrol & CONT_MMU_ENABLE)
++      len += sprintf (page + len, "%sMMU_ENABLE", len == 0 ? "" : " ");
++    if (syscontrol & CONT_CACHE_HASH_TABLE)
++      len += sprintf (page + len, "%sCACHE_HASH_TABLE", len == 0 ? "" : " ");
++    if (syscontrol & CONT_CACHE_CHAINS)
++      len += sprintf (page + len, "%sCACHE_CHAINS", len == 0 ? "" : " ");
++    if (syscontrol & CONT_CACHE_ROOT_CNTX)
++      len += sprintf (page + len, "%sCACHE_ROOT_CNTX", len == 0 ? "" : " ");
++    if (syscontrol & CONT_CACHE_STEN_ROUTES)
++      len += sprintf (page + len, "%sCACHE_STEN_ROUTES", len == 0 ? "" : " ");
++    if (syscontrol & CONT_CACHE_DMA_ROUTES)
++      len += sprintf (page + len, "%sCACHE_DMA_ROUTES", len == 0 ? "" : " ");
++    if (syscontrol & CONT_INHIBIT_MAX_CHAIN_ITEMS)
++      len += sprintf (page + len, "%sINHIBIT_MAX_CHAIN_ITEMS", len == 0 ? "" : " ");
++
++    len += sprintf (page + len, "%sTABLE0_MASK_SIZE=%d", len == 0 ? "" : " ", (syscontrol >> CONT_TABLE0_MASK_SIZE_SHIFT) & PAGE_MASK_MASK);
++    len += sprintf (page + len, "%sTABLE0_PAGE_SIZE=%d", len == 0 ? "" : " ", (syscontrol >> CONT_TABLE0_PAGE_SIZE_SHIFT) & PAGE_SIZE_MASK);
++    len += sprintf (page + len, "%sTABLE1_MASK_SIZE=%d", len == 0 ? "" : " ", (syscontrol >> CONT_TABLE1_MASK_SIZE_SHIFT) & PAGE_MASK_MASK);
++    len += sprintf (page + len, "%sTABLE1_PAGE_SIZE=%d", len == 0 ? "" : " ", (syscontrol >> CONT_TABLE1_PAGE_SIZE_SHIFT) & PAGE_SIZE_MASK);
++
++    if (syscontrol & CONT_2K_NOT_1K_DMA_PACKETS)
++      len += sprintf (page + len, "%s2K_NOT_1K_DMA_PACKETS", len == 0 ? "" : " ");
++    if (syscontrol & CONT_ALIGN_ALL_DMA_PACKETS)
++      len += sprintf (page + len, "%sALIGN_ALL_DMA_PACKETS", len == 0 ? "" : " ");
++    if (syscontrol & CONT_DIRECT_MAP_PCI_WRITES)
++      len += sprintf (page + len, "%sDIRECT_MAP_PCI_WRITES", len == 0 ? "" : " ");
++
++    len += sprintf (page + len, "\n");
++
++   *start = page;
++   return (len);
++}
++
++static int
++proc_write_sysconfig (struct file *file, const char *ubuffer, unsigned long count, void *data)
++{
++    ELAN4_DEV *dev       = (ELAN4_DEV *) data;
++    unsigned long page   = __get_free_page (GFP_KERNEL);
++    char         *buffer = (char *)page;
++    int            add   = 0;
++    int            sub   = 0;
++    
++    count = MIN (count, PAGE_SIZE - 1);
++    if (copy_from_user (buffer, ubuffer, count))
++    {
++      free_page (page);
++      return (-EFAULT);
++    }
++   
++    buffer[count] = 0;                                /* terminate string */
++
++    while (*buffer != 0)
++    {
++      char *ptr;
++      char *end;
++      int   ch;
++      int   val;
++      int   op;
++
++      ch = *buffer;
++      if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n')
++      {
++          buffer++;
++          continue;
++      }
++      
++      op = *buffer;
++      if (op == '+' || op == '-')
++          buffer++;
++
++      for (end = buffer; *end != 0; end++)
++          if (*end == ' ' || *end == '\t' ||
++              *end == '\r' || *end == '\n')
++              break;
++      
++      if (end == buffer)
++          break;
++      
++      ch = *end;
++      *end = 0;
++
++      for (ptr = buffer; *ptr != 0; ptr++)
++          if ('a' <= *ptr && *ptr <= 'z')
++              *ptr = *ptr + 'A' - 'a';
++      
++      if (!strcmp (buffer, "EN_ALL_SETS"))
++          val = CONT_EN_ALL_SETS;
++      if (!strcmp (buffer, "CACHE_HASH_TABLE"))
++          val = CONT_CACHE_HASH_TABLE;
++      else if (!strcmp (buffer, "CACHE_CHAINS"))
++          val = CONT_CACHE_CHAINS;
++      else if (!strcmp (buffer, "CACHE_ROOT_CNTX"))
++          val = CONT_CACHE_ROOT_CNTX;
++      else if (!strcmp (buffer, "CACHE_STEN_ROUTES"))
++          val = CONT_CACHE_STEN_ROUTES;
++      else if (!strcmp (buffer, "CACHE_DMA_ROUTES"))
++          val = CONT_CACHE_DMA_ROUTES;
++      else if (!strcmp (buffer, "2K_NOT_1K_DMA_PACKETS"))
++          val = CONT_2K_NOT_1K_DMA_PACKETS;
++      else if (!strcmp (buffer, "ALIGN_ALL_DMA_PACKETS"))
++          val = CONT_ALIGN_ALL_DMA_PACKETS;
++      else
++          val = 0;
++
++      if (op == '+')
++          add |= val;
++      else if (op == '-')
++          sub |= val;
++
++      *end = ch;
++      buffer = end;
++    }
++
++    if ((add | sub) & CONT_EN_ALL_SETS)
++      elan4_sdram_flushcache (dev, 0, E4_CacheSize);
++
++    CHANGE_SYSCONTROL (dev, add, sub);
++   
++    if ((add | sub) & CONT_EN_ALL_SETS)
++      elan4_sdram_flushcache (dev, 0, E4_CacheSize);
++
++    free_page (page);
++    return (count);
++}
++
++static struct config_info 
++{
++    char *name;
++    int (*read_func) (char *page, char **start, off_t off, int count, int *eof, void *data);
++    int (*write_func) (struct file *file, const char *buf, unsigned long count, void *data); 
++} config_info[] = {
++    {"sysconfig",  proc_read_sysconfig, proc_write_sysconfig},
++};
++
++void
++elan4_procfs_device_init (ELAN4_DEV *dev)
++{
++    struct proc_dir_entry *p;
++    char name[NAME_MAX];
++    int i;
++
++    sprintf (name, "device%d", dev->dev_instance);
++    dev->dev_osdep.procdir  = proc_mkdir (name, elan4_procfs_root);
++
++    for (i = 0; i < sizeof (device_info)/sizeof (device_info[0]); i++)
++    {
++      if (dev->dev_devinfo.dev_revision_id < device_info[i].minrev)
++          continue;
++
++      if ((p = create_proc_entry (device_info[i].name, 0, dev->dev_osdep.procdir)) != NULL)
++      {
++          p->read_proc  = device_info[i].read_func;
++          p->write_proc = device_info[i].write_func;
++          p->data       = dev;
++          p->owner      = THIS_MODULE;
++      }
++    }
++
++    dev->dev_osdep.configdir = proc_mkdir ("config", dev->dev_osdep.procdir);
++    for (i = 0; i < sizeof (config_info)/sizeof (config_info[0]); i++)
++    {
++      if ((p = create_proc_entry (config_info[i].name, 0, dev->dev_osdep.configdir)) != NULL)
++      {
++          p->read_proc  = config_info[i].read_func;
++          p->write_proc = config_info[i].write_func;
++          p->data       = dev;
++          p->owner      = THIS_MODULE;
++      }
++    }
++
++    dev->dev_osdep.statsdir = proc_mkdir ("stats", dev->dev_osdep.procdir);
++    for (i = 0; i < sizeof (stats_info)/sizeof (stats_info[0]); i++)
++    {
++      if ((p = create_proc_entry (stats_info[i].name, 0, dev->dev_osdep.statsdir)) != NULL)
++      {
++          p->read_proc  = stats_info[i].read_func;
++          p->write_proc = stats_info[i].write_func;
++          p->data       = dev;
++          p->owner      = THIS_MODULE;
++      }
++    }
++}
++
++void
++elan4_procfs_device_fini (ELAN4_DEV *dev)
++{
++    char name[NAME_MAX];
++    int i;
++
++    for (i = 0; i < sizeof (stats_info)/sizeof (stats_info[0]); i++)
++      remove_proc_entry (stats_info[i].name, dev->dev_osdep.statsdir);
++    remove_proc_entry ("stats", dev->dev_osdep.procdir);
++
++    for (i = 0; i < sizeof (config_info)/sizeof (config_info[0]); i++)
++      remove_proc_entry (config_info[i].name, dev->dev_osdep.configdir);
++    remove_proc_entry ("config", dev->dev_osdep.procdir);
++
++    for (i = 0; i < sizeof (device_info)/sizeof (device_info[0]); i++)
++    {
++      if (dev->dev_devinfo.dev_revision_id < device_info[i].minrev)
++          continue;
++      
++      remove_proc_entry (device_info[i].name, dev->dev_osdep.procdir);
++    }
++
++    sprintf (name, "device%d", dev->dev_instance);
++    remove_proc_entry (name, elan4_procfs_root);
++}
++
++void
++elan4_procfs_init(void)
++{
++    elan4_procfs_root = proc_mkdir("elan4", qsnet_procfs_root);
++    elan4_config_root = proc_mkdir("config", elan4_procfs_root);
++
++    qsnet_proc_register_hex (elan4_config_root, "elan4_debug",              &elan4_debug,              0);
++    qsnet_proc_register_hex (elan4_config_root, "elan4_debug_toconsole",    &elan4_debug_toconsole,    0);
++    qsnet_proc_register_hex (elan4_config_root, "elan4_debug_tobuffer",     &elan4_debug_tobuffer,     0);
++    qsnet_proc_register_int (elan4_config_root, "elan4_debug_mmu",          &elan4_debug_mmu,          0);
++    qsnet_proc_register_int (elan4_config_root, "elan4_mainint_punt_loops", &elan4_mainint_punt_loops, 0);
++    qsnet_proc_register_hex (elan4_config_root, "user_p2p_route_options",   &user_p2p_route_options,   0);
++    qsnet_proc_register_hex (elan4_config_root, "user_bcast_route_options", &user_bcast_route_options, 0);
++    qsnet_proc_register_int (elan4_config_root, "user_dproc_retry_count",   &user_dproc_retry_count,    0);
++    qsnet_proc_register_int (elan4_config_root, "user_cproc_retry_count",   &user_cproc_retry_count,    0);
++    qsnet_proc_register_int (elan4_config_root, "num_fault_save",   &num_fault_save,    0);
++    qsnet_proc_register_int (elan4_config_root, "min_fault_pages",   &min_fault_pages,    0);
++    qsnet_proc_register_int (elan4_config_root, "max_fault_pages",   &max_fault_pages,    0);
++}
++
++void
++elan4_procfs_fini(void)
++{
++    remove_proc_entry ("max_fault_pages",          elan4_config_root);
++    remove_proc_entry ("min_fault_pages",          elan4_config_root);
++    remove_proc_entry ("num_fault_save",           elan4_config_root);
++    remove_proc_entry ("user_cproc_retry_count",   elan4_config_root);
++    remove_proc_entry ("user_dproc_retry_count",   elan4_config_root);
++    remove_proc_entry ("user_bcast_route_options", elan4_config_root);
++    remove_proc_entry ("user_p2p_route_options",   elan4_config_root);
++    remove_proc_entry ("elan4_mainint_punt_loops", elan4_config_root);
++    remove_proc_entry ("elan4_debug_mmu",          elan4_config_root);
++    remove_proc_entry ("elan4_debug_tobuffer",     elan4_config_root);
++    remove_proc_entry ("elan4_debug_toconsole",    elan4_config_root);
++    remove_proc_entry ("elan4_debug",              elan4_config_root);
++
++    remove_proc_entry ("config", elan4_procfs_root);
++    remove_proc_entry ("elan4", qsnet_procfs_root);
++}
++
++EXPORT_SYMBOL(elan4_procfs_root);
++EXPORT_SYMBOL(elan4_config_root);
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan4/quadrics_version.h
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan4/quadrics_version.h        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan4/quadrics_version.h     2005-05-11 12:10:12.457929688 -0400
+@@ -0,0 +1 @@
++#define QUADRICS_VERSION "4.31qsnet"
+Index: linux-2.6.5/drivers/net/qsnet/elan4/regions.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan4/regions.c 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan4/regions.c      2005-05-11 12:10:12.462928928 -0400
+@@ -0,0 +1,609 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: regions.c,v 1.18.2.1 2004/11/18 11:31:08 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/regions.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan4/debug.h>
++#include <elan4/device.h>
++#include <elan4/user.h>
++
++/*================================================================================*/
++/* elan address region management */
++USER_RGN *
++user_findrgn_elan (USER_CTXT *uctx, E4_Addr addr, int tail)
++{
++    USER_RGN *rgn;
++    USER_RGN *hirgn;
++    USER_RGN *lorgn;
++    E4_Addr   base;
++    E4_Addr   lastaddr;
++    int             forward;
++    
++    ASSERT (SPINLOCK_HELD (&uctx->uctx_rgnlock) || kmutex_is_locked (&uctx->uctx_rgnmutex));
++
++    if (uctx->uctx_ergns == NULL)
++      return (NULL);
++
++    rgn = uctx->uctx_ergnlast;
++    if (rgn == NULL)
++      rgn = uctx->uctx_ergns;
++
++    forward = 0;
++    if ((base = rgn->rgn_ebase) < addr)
++    {
++      if (addr <= (base + rgn->rgn_len - 1))
++          return (rgn);                                       /* ergnlast contained addr */
++      
++      hirgn = uctx->uctx_etail;
++      
++      if ((lastaddr = (hirgn->rgn_ebase + hirgn->rgn_len - 1)) < addr)
++          return (tail ? hirgn : NULL);                       /* addr is out of range */
++      
++      if ((addr - base) > (lastaddr - addr))
++          rgn = hirgn;
++      else
++      {
++          rgn = rgn->rgn_enext;
++          forward++;
++      }
++    }
++    else
++    {
++      lorgn = uctx->uctx_ergns;
++
++      if (lorgn->rgn_ebase > addr)
++          return (lorgn);                                     /* lowest regions is higher than addr */
++      if ((addr - lorgn->rgn_ebase) < (base - addr))
++      {
++          rgn = lorgn;                                        /* search forward from head */
++          forward++;
++      }
++    }
++    if (forward)
++    {
++      while ((rgn->rgn_ebase + rgn->rgn_len - 1) < addr)
++          rgn = rgn->rgn_enext;
++
++      if (rgn->rgn_ebase <= addr)
++          uctx->uctx_ergnlast = rgn;
++      return (rgn);
++    }
++    else
++    {
++      while (rgn->rgn_ebase > addr)
++          rgn = rgn->rgn_eprev;
++
++      if ((rgn->rgn_ebase + rgn->rgn_len - 1) < addr)
++          return (rgn->rgn_enext);
++      else
++      {
++          uctx->uctx_ergnlast = rgn;
++          return (rgn);
++      }
++    }
++}
++
++static int
++user_addrgn_elan (USER_CTXT *uctx, USER_RGN  *nrgn)
++{
++    USER_RGN *rgn   = user_findrgn_elan (uctx, nrgn->rgn_ebase, 1);
++    E4_Addr   nbase = nrgn->rgn_ebase;
++    E4_Addr   ntop  = nbase + nrgn->rgn_len - 1;
++    E4_Addr   base;
++
++    ASSERT (SPINLOCK_HELD (&uctx->uctx_rgnlock) && kmutex_is_locked (&uctx->uctx_rgnmutex));
++
++    if (rgn == NULL)
++    {
++      uctx->uctx_ergns = uctx->uctx_etail = nrgn;
++      nrgn->rgn_enext = nrgn->rgn_eprev = NULL;
++    }
++    else
++    {
++      base = rgn->rgn_ebase;
++      
++      if ((base + rgn->rgn_len - 1) < nbase)                  /* top of region below requested address */
++      {                                                       /* so insert after region (and hence at end */
++          nrgn->rgn_eprev = rgn;                              /* of list */
++          nrgn->rgn_enext = NULL;
++          rgn->rgn_enext = uctx->uctx_etail = nrgn;
++      }
++      else
++      {
++          if (nbase >= base || ntop >= base)                  /* overlapping region */
++              return (-1);
++          
++          nrgn->rgn_enext = rgn;                              /* insert before region */
++          nrgn->rgn_eprev = rgn->rgn_eprev;
++          rgn->rgn_eprev  = nrgn;
++          if (uctx->uctx_ergns == rgn)
++              uctx->uctx_ergns = nrgn;
++          else
++              nrgn->rgn_eprev->rgn_enext = nrgn;
++      }
++    }
++    uctx->uctx_ergnlast = nrgn;
++    
++    return (0);
++}
++
++static USER_RGN *
++user_removergn_elan (USER_CTXT *uctx, USER_RGN  *rgn)
++{
++    ASSERT (SPINLOCK_HELD (&uctx->uctx_rgnlock) && kmutex_is_locked (&uctx->uctx_rgnmutex));
++
++    uctx->uctx_ergnlast = rgn->rgn_enext;
++    if (rgn == uctx->uctx_etail)
++      uctx->uctx_etail = rgn->rgn_eprev;
++    else
++      rgn->rgn_enext->rgn_eprev = rgn->rgn_eprev;
++    
++    if (rgn == uctx->uctx_ergns)
++      uctx->uctx_ergns = rgn->rgn_enext;
++    else
++      rgn->rgn_eprev->rgn_enext = rgn->rgn_enext;
++
++    return (rgn);
++}
++
++USER_RGN *
++user_rgnat_elan (USER_CTXT *uctx, E4_Addr addr)
++{
++    USER_RGN *rgn = user_findrgn_elan (uctx, addr, 0);
++
++    if (rgn != NULL && rgn->rgn_ebase <= addr && addr <= (rgn->rgn_ebase + rgn->rgn_len - 1))
++      return (rgn);
++
++    return (NULL);
++}
++
++/* main address region management */
++USER_RGN *
++user_findrgn_main (USER_CTXT *uctx, virtaddr_t addr, int tail)
++{
++    USER_RGN  *rgn;
++    USER_RGN  *hirgn;
++    USER_RGN  *lorgn;
++    virtaddr_t lastaddr;
++    virtaddr_t base;
++    int              forward;
++    
++    ASSERT (SPINLOCK_HELD (&uctx->uctx_rgnlock) || kmutex_is_locked (&uctx->uctx_rgnmutex));
++
++    if (uctx->uctx_mrgns == NULL)
++      return (NULL);
++    
++    rgn = uctx->uctx_mrgnlast;
++    if (rgn == NULL)
++      rgn = uctx->uctx_mrgns;
++    
++    forward = 0;
++    if ((base = rgn->rgn_mbase) < addr)
++    {
++      if (addr <= (base + rgn->rgn_len - 1))
++          return (rgn);                                       /* ergnlast contained addr */
++      
++      hirgn = uctx->uctx_mtail;
++      if ((lastaddr = hirgn->rgn_mbase + hirgn->rgn_len - 1) < addr)
++          return (tail ? hirgn : NULL);                       /* addr is out of range */
++      
++      if ((addr - base) > (lastaddr - addr))
++          rgn = hirgn;
++      else
++      {
++          rgn = rgn->rgn_mnext;
++          forward++;
++      }
++    }
++    else
++    {
++      lorgn = uctx->uctx_mrgns;
++      if (lorgn->rgn_mbase > addr)
++          return (lorgn);                                     /* lowest regions is higher than addr */
++      if ((addr - lorgn->rgn_mbase) < (base - addr))
++      {
++          rgn = lorgn;                                        /* search forward from head */
++          forward++;
++      }
++    }
++    if (forward)
++    {
++      while ((rgn->rgn_mbase + rgn->rgn_len - 1) < addr)
++          rgn = rgn->rgn_mnext;
++
++      if (rgn->rgn_mbase <= addr)
++          uctx->uctx_mrgnlast = rgn;
++      return (rgn);
++    }
++    else
++    {
++      while (rgn->rgn_mbase > addr)
++          rgn = rgn->rgn_mprev;
++
++      if ((rgn->rgn_mbase + rgn->rgn_len - 1) < addr)
++          return (rgn->rgn_mnext);
++      else
++      {
++          uctx->uctx_mrgnlast = rgn;
++          return (rgn);
++      }
++    }
++}
++
++static int
++user_addrgn_main (USER_CTXT *uctx, USER_RGN *nrgn)
++{
++    USER_RGN  *rgn   = user_findrgn_main (uctx, nrgn->rgn_mbase, 1);
++    virtaddr_t nbase = nrgn->rgn_mbase;
++    virtaddr_t ntop  = nbase + nrgn->rgn_len - 1;
++    virtaddr_t base;
++
++    ASSERT (SPINLOCK_HELD (&uctx->uctx_rgnlock) && kmutex_is_locked (&uctx->uctx_rgnmutex));
++
++    if (rgn == NULL)
++    {
++      uctx->uctx_mrgns = uctx->uctx_mtail = nrgn;
++      nrgn->rgn_mnext = nrgn->rgn_mprev = NULL;
++    }
++    else
++    {
++      base = rgn->rgn_mbase;
++
++      if ((base + rgn->rgn_len - 1) < nbase)                  /* top of region below requested address */
++      {                                                       /* so insert after region (and hence at end */
++          nrgn->rgn_mprev = rgn;                              /* of list */
++          nrgn->rgn_mnext = NULL;
++          rgn->rgn_mnext = uctx->uctx_mtail = nrgn;
++      }
++      else
++      {
++          if (nbase >= base || ntop >= base)                  /* overlapping region */
++              return (-1);
++
++          nrgn->rgn_mnext = rgn;                              /* insert before region */
++          nrgn->rgn_mprev = rgn->rgn_mprev;
++          rgn->rgn_mprev  = nrgn;
++          if (uctx->uctx_mrgns == rgn)
++              uctx->uctx_mrgns = nrgn;
++          else
++              nrgn->rgn_mprev->rgn_mnext = nrgn;
++      }
++    }
++    uctx->uctx_mrgnlast = nrgn;
++    
++    return (0);
++}
++
++static USER_RGN *
++user_removergn_main (USER_CTXT *uctx, USER_RGN *rgn)
++{
++    ASSERT (SPINLOCK_HELD (&uctx->uctx_rgnlock) && kmutex_is_locked (&uctx->uctx_rgnmutex));
++
++    uctx->uctx_mrgnlast = rgn->rgn_mnext;
++    if (rgn == uctx->uctx_mtail)
++      uctx->uctx_mtail = rgn->rgn_mprev;
++    else
++      rgn->rgn_mnext->rgn_mprev = rgn->rgn_mprev;
++    
++    if (rgn == uctx->uctx_mrgns)
++      uctx->uctx_mrgns = rgn->rgn_mnext;
++    else
++      rgn->rgn_mprev->rgn_mnext = rgn->rgn_mnext;
++
++    return (rgn);
++}
++
++/* Remove whole region from both lists */
++static void
++user_removergn (USER_CTXT *uctx, USER_RGN *rgn)
++{
++    spin_lock (&uctx->uctx_rgnlock);
++
++    elan4mmu_unload_range (&uctx->uctx_ctxt, 0 /* XXXX tbl */, rgn->rgn_ebase, rgn->rgn_len);
++          
++    user_removergn_elan (uctx, rgn);
++    user_removergn_main (uctx, rgn);
++    
++    spin_unlock (&uctx->uctx_rgnlock);
++    
++    KMEM_FREE (rgn, sizeof (USER_RGN));
++}
++
++/* Remove all allocated regions */
++void
++user_freergns (USER_CTXT *uctx)
++{
++    kmutex_lock (&uctx->uctx_rgnmutex);
++
++    while (uctx->uctx_mrgns)
++      user_removergn(uctx, uctx->uctx_mrgns);
++
++    kmutex_unlock (&uctx->uctx_rgnmutex);
++
++    ASSERT (uctx->uctx_ergns == NULL);
++}
++
++USER_RGN *
++user_rgnat_main (USER_CTXT *uctx, virtaddr_t addr)
++{
++    USER_RGN *rgn = user_findrgn_main (uctx, addr, 0);
++    
++    if (rgn != NULL && rgn->rgn_mbase <= addr && addr <= (rgn->rgn_mbase + rgn->rgn_len - 1))
++      return (rgn);
++    return (NULL);
++}
++
++int
++user_setperm (USER_CTXT *uctx, virtaddr_t maddr, E4_Addr eaddr, unsigned long len, unsigned perm)
++{
++    USER_RGN *nrgn;
++
++    PRINTF4 (uctx, DBG_PERM, "user_setperm: user %lx elan %llx len %lx perm %x\n", maddr, (long long) eaddr, len, perm);
++
++    if ((maddr & PAGEOFFSET) || (eaddr & PAGEOFFSET) || (len & PAGEOFFSET))
++    {
++        PRINTF0 (uctx, DBG_PERM, "user_setperm:  alignment failure\n");
++      return (-EINVAL);
++    }
++    
++    if ((maddr + len - 1) <= maddr || (eaddr + len - 1) <= eaddr) 
++    {
++      PRINTF0 (uctx, DBG_PERM, "user_setperm:  range failure\n");
++      return (-EINVAL);
++    }
++
++    KMEM_ALLOC (nrgn, USER_RGN *, sizeof (USER_RGN), 1);
++    
++    if (nrgn == NULL)
++      return (-ENOMEM);
++
++    nrgn->rgn_mbase = maddr;
++    nrgn->rgn_ebase = eaddr;
++    nrgn->rgn_len   = len;
++    nrgn->rgn_perm  = perm;
++
++    kmutex_lock (&uctx->uctx_rgnmutex);
++    spin_lock (&uctx->uctx_rgnlock);
++
++    if (user_addrgn_elan (uctx, nrgn) < 0)
++    {
++      PRINTF0 (uctx, DBG_PERM, "user_setperm:  elan address exists\n");
++      spin_unlock (&uctx->uctx_rgnlock);
++      kmutex_unlock (&uctx->uctx_rgnmutex);
++
++      KMEM_FREE (nrgn, sizeof (USER_RGN));
++      return (-EINVAL);
++    }
++    
++    if (user_addrgn_main (uctx, nrgn) < 0)
++    {
++      PRINTF0 (uctx, DBG_PERM, "user_setperm:  main address exists\n");
++      user_removergn_elan (uctx, nrgn);
++      
++      spin_unlock (&uctx->uctx_rgnlock);
++      kmutex_unlock (&uctx->uctx_rgnmutex);
++
++      KMEM_FREE (nrgn, sizeof (USER_RGN));
++      return (-EINVAL);
++    }
++    spin_unlock (&uctx->uctx_rgnlock);
++
++    if ((perm & PERM_Preload))
++      user_preload_main (uctx, maddr, len);
++
++    kmutex_unlock (&uctx->uctx_rgnmutex);
++
++    return (0);
++}
++
++void
++user_clrperm (USER_CTXT *uctx, E4_Addr addr, unsigned long len)
++{
++    E4_Addr       raddr;
++    E4_Addr       rtop;
++    USER_RGN     *nrgn;
++    USER_RGN     *rgn;
++    USER_RGN     *rgn_next;
++    unsigned long ssize;
++    int                 res;
++
++    PRINTF2 (uctx, DBG_PERM, "user_clrperm: elan %llx len %lx\n", addr, len);
++
++    raddr = (addr & PAGEMASK);
++    rtop = ((addr + len - 1) & PAGEMASK) + (PAGESIZE-1);
++
++    kmutex_lock (&uctx->uctx_rgnmutex);
++    
++    for (rgn = user_findrgn_elan (uctx, addr, 0); rgn != NULL; rgn = rgn_next)
++    {
++      if (rtop < rgn->rgn_ebase)                              /* rtop was in a gap */
++          break;
++      
++      rgn_next = rgn->rgn_enext;                              /* Save next region pointer */
++      
++      PRINTF (uctx, DBG_PERM, "              elan %llx->%llx main %p->%p\n", 
++              rgn->rgn_ebase, rgn->rgn_ebase + rgn->rgn_len-1,
++              rgn->rgn_mbase, rgn->rgn_mbase + rgn->rgn_len-1);
++
++      if (raddr <= rgn->rgn_ebase && rtop >= (rgn->rgn_ebase + rgn->rgn_len - 1))
++      {
++          /* whole region is cleared */
++
++          PRINTF (uctx, DBG_PERM, "              whole region\n");
++          PRINTF (uctx, DBG_PERM, "              unload elan %llx->%llx\n", rgn->rgn_ebase, rgn->rgn_ebase + rgn->rgn_len-1);
++          user_removergn (uctx, rgn);
++      }
++      else if (raddr <= rgn->rgn_ebase)
++      {
++          /* clearing at beginning, so shrink size and increment base ptrs */
++          ssize = rtop - rgn->rgn_ebase + 1;
++          
++          PRINTF (uctx, DBG_PERM, "              clear at beginning %x\n", ssize);
++
++          spin_lock (&uctx->uctx_rgnlock);
++
++          PRINTF (uctx, DBG_PERM, "              unload elan %llx->%llx\n", rgn->rgn_ebase, rgn->rgn_ebase + ssize-1);
++          elan4mmu_unload_range (&uctx->uctx_ctxt, 0 /* XXXX tbl */, rgn->rgn_ebase, ssize);
++
++          rgn->rgn_mbase += ssize;
++          rgn->rgn_ebase += ssize;
++          rgn->rgn_len   -= ssize;
++          
++          spin_unlock(&uctx->uctx_rgnlock);
++      }
++      else if (rtop >= (rgn->rgn_ebase + rgn->rgn_len - 1))
++      {
++          /* clearing at end, so just shrink length of region */
++          ssize = (rgn->rgn_ebase + rgn->rgn_len - 1) - raddr + 1;
++
++          PRINTF (uctx, DBG_PERM, "              clear at end %x\n", ssize);
++
++          spin_lock (&uctx->uctx_rgnlock);
++
++          PRINTF (uctx, DBG_PERM, "              unload elan %llx->%llx\n", raddr, raddr+ssize-1);
++          elan4mmu_unload_range (&uctx->uctx_ctxt, 0 /* XXXX tbl */, raddr, ssize);
++
++          rgn->rgn_len -= ssize;
++
++          spin_unlock(&uctx->uctx_rgnlock);
++      }
++      else
++      {
++          /* the section to go is in the middle,  so need to  */
++          /* split it into two regions */
++          KMEM_ALLOC (nrgn, USER_RGN *, sizeof (USER_RGN), 1);
++
++          spin_lock (&uctx->uctx_rgnlock);
++
++          PRINTF (uctx, DBG_PERM, "              unload elan %llx->%llx\n", raddr, rtop);
++          elan4mmu_unload_range (&uctx->uctx_ctxt, 0 /* XXXX tbl */, raddr, rtop - raddr + 1);
++
++          nrgn->rgn_mbase = rgn->rgn_mbase + (rtop - rgn->rgn_ebase + 1);
++          nrgn->rgn_ebase = rtop + 1;
++          nrgn->rgn_len   = (rgn->rgn_ebase + rgn->rgn_len - 1) - rtop;
++          nrgn->rgn_perm  = rgn->rgn_perm;
++
++          PRINTF (uctx, DBG_PERM, "              new elan %llx->%llx main %p->%p\n", 
++                  nrgn->rgn_ebase, nrgn->rgn_ebase + nrgn->rgn_len-1,
++                  nrgn->rgn_mbase, nrgn->rgn_mbase + nrgn->rgn_len-1);
++
++          rgn->rgn_len = (raddr - rgn->rgn_ebase);            /* shrink original region */
++
++          PRINTF (uctx, DBG_PERM, "              old elan %llx->%llx main %p->%p\n", 
++                  rgn->rgn_ebase, rgn->rgn_ebase + rgn->rgn_len-1,
++                  rgn->rgn_mbase, rgn->rgn_mbase + rgn->rgn_len-1);
++
++          res = user_addrgn_elan (uctx, nrgn);                /* insert new region */
++          ASSERT (res == 0);                                  /* which cannot fail */
++
++          res = user_addrgn_main (uctx, nrgn);        
++          ASSERT (res == 0);
++
++          spin_unlock(&uctx->uctx_rgnlock);
++      }
++    }
++    kmutex_unlock (&uctx->uctx_rgnmutex);
++}
++
++int
++user_checkperm (USER_CTXT *uctx, E4_Addr raddr, unsigned long rsize, unsigned access)
++{
++    USER_RGN *rgn;
++
++    PRINTF3 (uctx, DBG_PERM, "user_checkperm: elan %lx len %lx access %x\n", raddr, rsize, access);
++    
++    if ((raddr + rsize - 1) < raddr)
++      return (-ENOMEM);
++    
++    kmutex_lock (&uctx->uctx_rgnmutex);
++    if ((rgn = user_rgnat_elan (uctx, raddr)) == (USER_RGN *) NULL)
++    {
++      kmutex_unlock (&uctx->uctx_rgnmutex);
++      return (-ENOMEM);
++    }
++    else
++    {
++      register int ssize;
++      
++      for (; rsize != 0; rsize -= ssize, raddr += ssize)
++      {
++          if (raddr > (rgn->rgn_ebase + rgn->rgn_len - 1))
++          {
++              rgn  = rgn->rgn_enext;
++              
++              if (rgn == NULL || raddr != rgn->rgn_ebase)
++              {
++                  kmutex_unlock (&uctx->uctx_rgnmutex);
++                  return (-ENOMEM);
++              }
++          }
++          if ((raddr + rsize - 1) > (rgn->rgn_ebase + rgn->rgn_len - 1))
++              ssize = ((rgn->rgn_ebase + rgn->rgn_len - 1) - raddr) + 1;
++          else
++              ssize = rsize;
++          
++          PRINTF4 (uctx, DBG_PERM, "user_checkperm : rgn %lx -> %lx perm %x access %x\n",
++                   rgn->rgn_ebase, rgn->rgn_ebase + (E4_Addr)rgn->rgn_len, rgn->rgn_perm, access);
++
++          if (ELAN4_INCOMPAT_ACCESS (rgn->rgn_perm, access))
++          {
++              kmutex_unlock (&uctx->uctx_rgnmutex);
++              return (-EACCES);
++          }
++      }
++    }
++    
++    kmutex_unlock (&uctx->uctx_rgnmutex);
++    
++    return (0);
++}
++
++virtaddr_t
++user_elan2main (USER_CTXT *uctx, E4_Addr addr)
++{
++    USER_RGN  *rgn;
++    virtaddr_t raddr;
++    
++    spin_lock (&uctx->uctx_rgnlock);
++    
++    if ((rgn = user_rgnat_elan (uctx, addr)) == (USER_RGN *) NULL)
++      raddr = (virtaddr_t) 0;
++    else
++      raddr = rgn->rgn_mbase + (addr - rgn->rgn_ebase);
++
++    spin_unlock (&uctx->uctx_rgnlock);
++
++    return (raddr);
++}
++
++E4_Addr
++user_main2elan (USER_CTXT *uctx, virtaddr_t addr)
++{
++    USER_RGN *rgn;
++    E4_Addr   raddr;
++
++    spin_lock (&uctx->uctx_rgnlock);
++    
++    if ((rgn = user_rgnat_main (uctx, addr)) == (USER_RGN *) NULL)
++      raddr = (virtaddr_t) 0;
++    else
++      raddr = rgn->rgn_ebase + (addr - rgn->rgn_mbase);
++    
++    spin_unlock (&uctx->uctx_rgnlock);
++
++    return (raddr);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan4/routetable.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan4/routetable.c      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan4/routetable.c   2005-05-11 12:10:12.463928776 -0400
+@@ -0,0 +1,249 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: routetable.c,v 1.15 2004/07/20 09:29:40 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/routetable.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan4/sdram.h>
++#include <elan4/debug.h>
++#include <elan4/device.h>
++
++ELAN4_ROUTE_TABLE *
++elan4_alloc_routetable (ELAN4_DEV *dev, unsigned size)
++{
++    ELAN4_ROUTE_TABLE *tbl;
++
++    KMEM_ZALLOC (tbl, ELAN4_ROUTE_TABLE *, sizeof (ELAN4_ROUTE_TABLE), 1);
++
++    if (tbl == (ELAN4_ROUTE_TABLE *) NULL)
++      return (NULL);
++    
++    tbl->tbl_size    = (size & E4_VPT_SIZE_MASK);
++    tbl->tbl_entries = elan4_sdram_alloc (dev, (E4_VPT_MIN_ENTRIES << tbl->tbl_size) * sizeof (E4_VirtualProcessEntry));
++
++    if (tbl->tbl_entries == 0)
++    {
++      KMEM_FREE (tbl, sizeof (ELAN4_ROUTE_TABLE));
++      return ((ELAN4_ROUTE_TABLE *) NULL);
++    }
++
++    spin_lock_init (&tbl->tbl_lock);
++
++    /* zero the route table */
++    elan4_sdram_zeroq_sdram (dev, tbl->tbl_entries, (E4_VPT_MIN_ENTRIES << tbl->tbl_size) * sizeof (E4_VirtualProcessEntry));
++
++    return (tbl);
++}
++
++void
++elan4_free_routetable (ELAN4_DEV *dev, ELAN4_ROUTE_TABLE *tbl)
++{
++    elan4_sdram_free (dev, tbl->tbl_entries, (E4_VPT_MIN_ENTRIES << tbl->tbl_size) * sizeof (E4_VirtualProcessEntry));
++    
++    spin_lock_destroy (&tbl->tbl_lock);
++
++    KMEM_FREE (tbl, sizeof (ELAN4_ROUTE_TABLE));
++}
++
++void
++elan4_write_route (ELAN4_DEV *dev, ELAN4_ROUTE_TABLE *tbl, unsigned vp, E4_VirtualProcessEntry *entry)
++{
++    ASSERT (vp < (E4_VPT_MIN_ENTRIES << tbl->tbl_size));
++    
++    elan4_sdram_writeq (dev, tbl->tbl_entries + (vp * sizeof (E4_VirtualProcessEntry)) + offsetof (E4_VirtualProcessEntry, Values[1]), entry->Values[1]);
++    elan4_sdram_writeq (dev, tbl->tbl_entries + (vp * sizeof (E4_VirtualProcessEntry)) + offsetof (E4_VirtualProcessEntry, Values[0]), entry->Values[0]);
++    pioflush_sdram (dev);
++}
++
++void
++elan4_read_route (ELAN4_DEV *dev, ELAN4_ROUTE_TABLE *tbl, unsigned vp, E4_VirtualProcessEntry *entry)
++{
++    ASSERT (vp < (E4_VPT_MIN_ENTRIES << tbl->tbl_size));
++    
++    entry->Values[0] = elan4_sdram_readq (dev, tbl->tbl_entries + (vp * sizeof (E4_VirtualProcessEntry)) + offsetof (E4_VirtualProcessEntry, Values[0]));
++    entry->Values[1] = elan4_sdram_readq (dev, tbl->tbl_entries + (vp * sizeof (E4_VirtualProcessEntry)) + offsetof (E4_VirtualProcessEntry, Values[1]));
++}
++
++void
++elan4_invalidate_route (ELAN4_DEV *dev, ELAN4_ROUTE_TABLE *tbl, unsigned vp)
++{
++    ASSERT (vp < (E4_VPT_MIN_ENTRIES << tbl->tbl_size));
++
++    elan4_sdram_writeq (dev, tbl->tbl_entries + (vp * sizeof (E4_VirtualProcessEntry)) + offsetof (E4_VirtualProcessEntry, Values[0]), 0);
++    elan4_sdram_writeq (dev, tbl->tbl_entries + (vp * sizeof (E4_VirtualProcessEntry)) + offsetof (E4_VirtualProcessEntry, Values[1]), 0);
++    pioflush_sdram (dev);
++}
++
++static void
++pack_them_routes (E4_VirtualProcessEntry *entry, E4_uint16 first, E4_uint8 *packed, unsigned ctx)
++{
++    E4_uint64 value0 = first;
++    E4_uint64 value1 = ROUTE_CTXT_VALUE(ctx);
++    E4_uint32 ThirdRouteBCastVal;
++    register int i;
++
++    for (i = 0; i < (ROUTE_NUM_PACKED >> 1); i++)
++    {
++      value0 |= ((E4_uint64) packed[i]) << ((i << 2) + ROUTE_PACKED_OFFSET);
++      value1 |= ((E4_uint64) packed[i+(ROUTE_NUM_PACKED >> 1)]) << ((i << 2));
++    }
++
++    /* DMA fix for large broadcast route values that fall into the double issue of route value 3 bug. */
++    /* NOTE - this is only required when the link is running in Mod45 mode, it could be automatically
++     *        disabled when Mod44 is detected */
++    
++    /* First seach for the alignment type. The bug is only sensitive to an odd bcast aligment on the 3rd word. */
++    for (i=4;i<16;i++)
++      if (((value0 >> (i*4)) & 0xc) == 4)
++          i++;
++    
++    if (i == 17)
++    {
++      ThirdRouteBCastVal = value1 & 0xcccccccc;
++      if      (((value1 & 0xfffff0000000ULL) == 0ULL) && (ThirdRouteBCastVal == 0x04444444))
++          value1 |= 0x140000000ULL;
++      else if (((value1 & 0xfffffff00000ULL) == 0ULL) && (ThirdRouteBCastVal == 0x00044444))
++          value1 |= 0x1400000ULL;
++      else if (((value1 & 0xfffffffff000ULL) == 0ULL) && (ThirdRouteBCastVal == 0x00000444))
++          value1 |= 0x14000ULL;
++      else if (((value1 & 0xfffffffffff0ULL) == 0ULL) && (ThirdRouteBCastVal == 0x00000004))
++          value1 |= 0x140ULL;
++    }
++    
++    entry->Values[0] = value0;
++    entry->Values[1] = value1;
++}
++
++int
++elan4_generate_route (ELAN_POSITION *pos, E4_VirtualProcessEntry *route, unsigned ctx, unsigned lowid, unsigned highid, unsigned options)
++{
++    unsigned int broadcast    = (lowid != highid);
++    unsigned int noadaptive   = 0;
++    int          padbcast     = 0;
++    E4_uint16    first;
++    int                rb;
++    E4_uint8     packed[ROUTE_NUM_PACKED];
++    int                level, llink, hlink;
++
++ regenerate_routes:
++    first = 0;
++    rb    = 0;
++
++    switch (pos->pos_mode)
++    {
++    case ELAN_POS_MODE_LOOPBACK:
++      if (lowid != highid || lowid != pos->pos_nodeid)
++          return (-EINVAL);
++      
++      route->Values[0] = FIRST_MYLINK;
++      route->Values[1] = ROUTE_CTXT_VALUE (ctx);
++      return (0);
++
++    case ELAN_POS_MODE_BACKTOBACK:
++      if (lowid != highid || lowid == pos->pos_nodeid)
++          return (-EINVAL);
++      
++      route->Values[0] = FIRST_MYLINK;
++      route->Values[1] = ROUTE_CTXT_VALUE (ctx);
++      return (0);
++
++    case ELAN_POS_MODE_SWITCHED:
++    {
++      unsigned char *arityp  = &pos->pos_arity[pos->pos_levels - 1];
++      unsigned int   spanned = *arityp;
++      unsigned int   broadcasting = 0;
++      
++      bzero (packed, sizeof (packed));
++
++      /* XXXX compute noadaptive ? */
++
++      for (level = 0; 
++           level < pos->pos_levels && ! ((pos->pos_nodeid / spanned) == (lowid / spanned) &&
++                                         (pos->pos_nodeid / spanned) ==  (highid / spanned)); 
++           level++, spanned *= *(--arityp))
++      {
++          if (first == 0)
++              first = (broadcast || noadaptive) ? FIRST_BCAST_TREE : FIRST_ADAPTIVE;
++          else if (broadcast && padbcast)
++          {
++              padbcast = 0;
++              packed[rb++] = PACKED_BCAST0(4, 4);
++              packed[rb++] = PACKED_BCAST1(4, 4);
++          }
++          else
++              packed[rb++] = (broadcast || noadaptive) ? PACKED_BCAST_TREE : PACKED_ADAPTIVE;    
++      }
++
++      while (level >= 0)
++      {
++          spanned /= *arityp;
++          
++          llink = (lowid  / spanned) % *arityp;
++          hlink = (highid / spanned) % *arityp;
++          
++          if (llink != hlink || broadcasting)
++          {
++              broadcasting = 1;
++              
++              if (first == 0)
++                  first = FIRST_BCAST (hlink, llink);
++              else
++              {
++                  packed[rb++] = PACKED_BCAST0(hlink, llink);
++                  
++                  if ((rb % 4) == 0 && PACKED_BCAST1(hlink, llink) == 0)
++                  {
++                      padbcast = 1;
++                      goto regenerate_routes;
++                  }
++                  
++                  packed[rb++] = PACKED_BCAST1(hlink, llink);
++              }
++          }
++          else
++          {
++              if (first == 0)
++                  first = FIRST_ROUTE(llink);
++              else
++                  packed[rb++] = PACKED_ROUTE(llink);
++          }
++          
++          level--;
++          arityp++;
++      }
++
++      pack_them_routes (route, first | (options & FIRST_OPTIONS_MASK), packed, ctx);
++      return (0);
++    }
++    }
++
++    return (-EINVAL);
++}
++
++int
++elan4_check_route (ELAN_POSITION *postiion, ELAN_LOCATION location, E4_VirtualProcessEntry *route, unsigned flags)
++{
++    /* XXXX - TBD */
++    return (0);
++}
++
++EXPORT_SYMBOL(elan4_alloc_routetable);
++EXPORT_SYMBOL(elan4_free_routetable);
++EXPORT_SYMBOL(elan4_write_route);
++EXPORT_SYMBOL(elan4_read_route);
++EXPORT_SYMBOL(elan4_invalidate_route);
++EXPORT_SYMBOL(elan4_generate_route);
++EXPORT_SYMBOL(elan4_check_route);
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan4/sdram.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan4/sdram.c   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan4/sdram.c        2005-05-11 12:10:12.464928624 -0400
+@@ -0,0 +1,1039 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: sdram.c,v 1.29.6.4 2005/03/03 16:30:45 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/sdram.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan4/debug.h>
++#include <elan4/device.h>
++
++EXPORT_SYMBOL_GPL(elan4_sdram_readb);
++EXPORT_SYMBOL_GPL(elan4_sdram_readw);
++EXPORT_SYMBOL_GPL(elan4_sdram_readl);
++EXPORT_SYMBOL_GPL(elan4_sdram_readq);
++EXPORT_SYMBOL_GPL(elan4_sdram_writeb);
++EXPORT_SYMBOL_GPL(elan4_sdram_writew);
++EXPORT_SYMBOL_GPL(elan4_sdram_writel);
++EXPORT_SYMBOL_GPL(elan4_sdram_writeq);
++EXPORT_SYMBOL_GPL(elan4_sdram_zerob_sdram);
++EXPORT_SYMBOL_GPL(elan4_sdram_zerow_sdram);
++EXPORT_SYMBOL_GPL(elan4_sdram_zerol_sdram);
++EXPORT_SYMBOL_GPL(elan4_sdram_zeroq_sdram);
++EXPORT_SYMBOL_GPL(elan4_sdram_copyb_from_sdram);
++EXPORT_SYMBOL_GPL(elan4_sdram_copyw_from_sdram);
++EXPORT_SYMBOL_GPL(elan4_sdram_copyl_from_sdram);
++EXPORT_SYMBOL_GPL(elan4_sdram_copyq_from_sdram);
++EXPORT_SYMBOL_GPL(elan4_sdram_copyb_to_sdram);
++EXPORT_SYMBOL_GPL(elan4_sdram_copyw_to_sdram);
++EXPORT_SYMBOL_GPL(elan4_sdram_copyl_to_sdram);
++EXPORT_SYMBOL_GPL(elan4_sdram_copyq_to_sdram);
++EXPORT_SYMBOL_GPL(elan4_sdram_alloc);
++EXPORT_SYMBOL_GPL(elan4_sdram_free);
++EXPORT_SYMBOL_GPL(elan4_sdram_flushcache);
++
++#define SDRAM_MIN_BANK_SIZE           ((1 << 15) * 8)         /* 256 Kbytes */
++
++static inline ELAN4_SDRAM_BANK *
++sdramaddr_to_bank (ELAN4_DEV *dev, sdramaddr_t saddr)
++{
++    register int i;
++    
++    for (i = 0; i < dev->dev_sdram_numbanks; i++)
++    {
++      ELAN4_SDRAM_BANK *bank = &dev->dev_sdram_banks[i];
++
++      if (saddr >= bank->b_base && saddr < (bank->b_base + bank->b_size))
++          return (bank);
++    }
++    printk ("sdramaddr_to_bank: sdram address %lx not in a sdram bank\n", saddr);
++    BUG();
++
++    return (NULL);    /* NOTREACHED */
++}
++
++static inline int
++sdramaddr_to_bankoffset (ELAN4_DEV *dev, sdramaddr_t saddr)
++{
++    return (saddr & (sdramaddr_to_bank (dev, saddr)->b_size-1));
++}
++
++static inline int
++sdramaddr_to_bit(ELAN4_DEV *dev, int indx, sdramaddr_t saddr)
++{
++    return (sdramaddr_to_bankoffset(dev, saddr) >> (SDRAM_MIN_BLOCK_SHIFT+(indx)));
++}
++
++static inline ioaddr_t
++sdramaddr_to_ioaddr (ELAN4_DEV *dev, sdramaddr_t saddr)
++{
++    ELAN4_SDRAM_BANK *bank = sdramaddr_to_bank (dev, saddr);
++
++    return (bank->b_ioaddr + (saddr - bank->b_base));
++}
++
++unsigned char
++elan4_sdram_readb (ELAN4_DEV *dev, sdramaddr_t off)
++{
++    return (__elan4_readb (dev, sdramaddr_to_ioaddr(dev, off)));
++}
++
++unsigned short
++elan4_sdram_readw (ELAN4_DEV *dev, sdramaddr_t off)
++{
++    return (__elan4_readw (dev, sdramaddr_to_ioaddr(dev, off)));
++}
++
++unsigned int
++elan4_sdram_readl (ELAN4_DEV *dev, sdramaddr_t off)
++{
++    return (__elan4_readl (dev, sdramaddr_to_ioaddr(dev, off)));
++}
++
++unsigned long long
++elan4_sdram_readq (ELAN4_DEV *dev, sdramaddr_t off)
++{
++    return (__elan4_readq (dev, sdramaddr_to_ioaddr(dev, off)));
++}
++
++void
++elan4_sdram_writeb (ELAN4_DEV *dev, sdramaddr_t off, unsigned char val)
++{
++    writeb (val, (void *) sdramaddr_to_ioaddr(dev, off));
++
++    mb();
++}
++
++void
++elan4_sdram_writew (ELAN4_DEV *dev, sdramaddr_t off, unsigned short val)
++{
++    writew (val, (void *) sdramaddr_to_ioaddr(dev, off));
++
++    mb();
++}
++
++void
++elan4_sdram_writel (ELAN4_DEV *dev, sdramaddr_t off, unsigned int val)
++{
++    writel (val, (void *) (sdramaddr_to_ioaddr(dev, off)));
++
++    mb();
++}
++
++void
++elan4_sdram_writeq (ELAN4_DEV *dev, sdramaddr_t off, unsigned long long val)
++{
++    writeq (val, (void *) (sdramaddr_to_ioaddr(dev, off)));
++
++    mb();
++}
++
++void
++elan4_sdram_zerob_sdram (ELAN4_DEV *dev, sdramaddr_t to, int nbytes)
++{
++    ioaddr_t dest = sdramaddr_to_ioaddr (dev, to);
++    ioaddr_t lim  = dest + nbytes;
++
++    for (; dest < lim; dest += sizeof (u8))
++      writeb (0, (void *) dest);
++}
++
++void
++elan4_sdram_zerow_sdram (ELAN4_DEV *dev, sdramaddr_t to, int nbytes)
++{
++    ioaddr_t dest = sdramaddr_to_ioaddr (dev, to);
++    ioaddr_t lim  = dest + nbytes;
++
++    for (; dest < lim; dest += sizeof (u8))
++      writeb (0, (void *) dest);
++}
++
++void
++elan4_sdram_zerol_sdram (ELAN4_DEV *dev, sdramaddr_t to, int nbytes)
++{
++    ioaddr_t dest = sdramaddr_to_ioaddr (dev, to);
++    ioaddr_t lim  = dest + nbytes;
++
++    for (; dest < lim; dest += sizeof (u32))
++      writel (0, (void *) dest);
++}
++
++void
++elan4_sdram_zeroq_sdram (ELAN4_DEV *dev, sdramaddr_t to, int nbytes)
++{
++    ioaddr_t dest = sdramaddr_to_ioaddr (dev, to);
++    ioaddr_t lim  = dest + nbytes;
++
++#ifdef CONFIG_MPSAS
++    if (sas_memset_dev (dev->dev_osdep.pdev, ELAN4_BAR_SDRAM, to, 0, nbytes) == 0)
++      return;
++#endif
++
++    for (; dest < lim; dest += sizeof (u64))
++      writeq (0, (void *) dest);
++}
++
++void
++elan4_sdram_copyb_from_sdram (ELAN4_DEV *dev, sdramaddr_t from, void *to, int nbytes)
++{
++    ioaddr_t src  = sdramaddr_to_ioaddr (dev, from);
++    u8      *dest = (u8 *) to;
++    ioaddr_t lim  = src + nbytes;
++
++    for (; src < lim; src += sizeof (u8))
++      *dest++ = __elan4_readb (dev, src);
++}
++
++void
++elan4_sdram_copyw_from_sdram (ELAN4_DEV *dev, sdramaddr_t from, void *to, int nbytes)
++{
++    ioaddr_t src  = sdramaddr_to_ioaddr (dev, from);
++    u16     *dest = (u16 *) to;
++    ioaddr_t lim  = src + nbytes;
++
++    for (; src < lim; src += sizeof (u16))
++      *dest++ = __elan4_readw (dev, src);
++}
++
++void
++elan4_sdram_copyl_from_sdram (ELAN4_DEV *dev, sdramaddr_t from, void *to, int nbytes)
++{
++    ioaddr_t src  = sdramaddr_to_ioaddr (dev, from);
++    u32     *dest = (u32 *) to;
++    ioaddr_t lim  = src + nbytes;
++
++    for (; src < lim; src += sizeof (u32))
++      *dest++ = __elan4_readl (dev, src);
++}
++
++void
++elan4_sdram_copyq_from_sdram (ELAN4_DEV *dev, sdramaddr_t from, void *to, int nbytes)
++{
++    ioaddr_t src  = sdramaddr_to_ioaddr (dev, from);
++    u64     *dest = (u64 *) to;
++    ioaddr_t lim  = src + nbytes;
++
++#ifdef CONFIG_MPSAS
++    if (sas_copyfrom_dev (dev->dev_osdep.pdev, ELAN4_BAR_SDRAM, from, (unsigned long) to, nbytes) == 0)
++      return;
++#endif
++
++    for (; src < lim; src += sizeof (u64))
++      *dest++ = __elan4_readq (dev, src);
++}
++
++void
++elan4_sdram_copyb_to_sdram (ELAN4_DEV *dev, void *from, sdramaddr_t to, int nbytes)
++{
++    ioaddr_t dest = sdramaddr_to_ioaddr (dev, to);
++    u8      *src  = (u8 *) from;
++    ioaddr_t lim  = dest + nbytes;
++
++    for (; dest < lim; dest += sizeof (u8))
++      writeb (*src++, (void *) (dest));
++
++    mb();
++}
++
++void
++elan4_sdram_copyw_to_sdram (ELAN4_DEV *dev, void *from, sdramaddr_t to, int nbytes)
++{
++    ioaddr_t dest = sdramaddr_to_ioaddr (dev, to);
++    u16     *src  = (u16 *) from;
++    ioaddr_t lim  = dest + nbytes;
++
++    for (; dest < lim; dest += sizeof (u16))
++      writew (*src++, (void *) (dest));
++
++    mb();
++}
++
++void
++elan4_sdram_copyl_to_sdram (ELAN4_DEV *dev, void *from, sdramaddr_t to, int nbytes)
++{
++    ioaddr_t dest = sdramaddr_to_ioaddr (dev, to);
++    u32     *src  = (u32 *) from;
++    ioaddr_t lim  = dest + nbytes;
++
++    for (; dest < lim; dest += sizeof (u16))
++      writew (*src++, (void *) (dest));
++
++    mb();
++}
++
++void
++elan4_sdram_copyq_to_sdram (ELAN4_DEV *dev, void *from, sdramaddr_t to, int nbytes)
++{
++    ioaddr_t dest = sdramaddr_to_ioaddr (dev, to);
++    u64     *src  = (u64 *) from;
++    ioaddr_t lim  = dest + nbytes;
++
++#ifdef CONFIG_MPSAS
++    if (sas_copyto_dev (dev->dev_osdep.pdev, ELAN4_BAR_SDRAM, to, (unsigned long) from, nbytes) == 0)
++      return;
++#endif
++
++    for (; dest < lim; dest += sizeof (u64))
++      writeq (*src++, (void *) (dest));
++
++    mb();
++}
++
++/* sdram buddy allocator */
++typedef struct sdramblock
++{
++    sdramaddr_t       next;
++    sdramaddr_t prev;
++} sdramblock_t;
++
++static inline sdramaddr_t
++read_next (ELAN4_DEV *dev, sdramaddr_t block)
++{
++    return __elan4_readl (dev, sdramaddr_to_ioaddr (dev, block + offsetof (sdramblock_t, next)));
++}
++
++static inline sdramaddr_t
++read_prev (ELAN4_DEV *dev, sdramaddr_t block)
++{
++    return __elan4_readl (dev, sdramaddr_to_ioaddr (dev, block + offsetof (sdramblock_t, prev)));
++}
++
++static inline void
++write_next (ELAN4_DEV *dev, sdramaddr_t block, sdramaddr_t val)
++{
++    writel (val, (void *) (sdramaddr_to_ioaddr (dev, block + offsetof (sdramblock_t, next))));
++}
++
++static inline void
++write_prev (ELAN4_DEV *dev, sdramaddr_t block, sdramaddr_t val)
++{
++    writel (val, (void *) (sdramaddr_to_ioaddr (dev, block + offsetof (sdramblock_t, prev))));
++}
++
++static inline void
++freelist_insert (ELAN4_DEV *dev, int idx, sdramaddr_t block)
++{
++    sdramaddr_t next = dev->dev_sdram_freelists[(idx)];
++
++    /*
++     * block->prev = NULL;
++     * block->next = next;
++     * if (next != NULL)
++     *    next->prev = block;
++     * freelist = block;
++     */
++    write_prev (dev, block, (sdramaddr_t) 0);
++    write_next (dev, block, next);
++    if (next != (sdramaddr_t) 0)
++      write_prev (dev, next, block);
++    dev->dev_sdram_freelists[idx] = block;
++
++    dev->dev_sdram_freecounts[idx]++;
++    dev->dev_stats.s_sdram_bytes_free += (SDRAM_MIN_BLOCK_SIZE << idx);
++
++    mb();
++}
++
++static inline void
++freelist_remove (ELAN4_DEV *dev,int idx, sdramaddr_t block)
++{
++    /*
++     * if (block->prev)
++     *     block->prev->next = block->next;
++     * else
++     *     dev->dev_sdram_freelists[idx] = block->next;
++     * if (block->next)
++     *     block->next->prev = block->prev;
++     */
++    sdramaddr_t blocknext = read_next (dev, block);
++    sdramaddr_t blockprev = read_prev (dev, block);
++
++    if (blockprev)
++      write_next (dev, blockprev, blocknext);
++    else
++      dev->dev_sdram_freelists[idx] = blocknext;
++    if (blocknext)
++      write_prev (dev, blocknext, blockprev);
++
++    dev->dev_sdram_freecounts[idx]--;
++    dev->dev_stats.s_sdram_bytes_free -= (SDRAM_MIN_BLOCK_SIZE << idx);
++
++    mb();
++}
++
++static inline void
++freelist_removehead(ELAN4_DEV *dev, int idx, sdramaddr_t block)
++{
++    sdramaddr_t blocknext = read_next (dev, block);
++
++    if ((dev->dev_sdram_freelists[idx] = blocknext) != 0)
++      write_prev (dev, blocknext, 0);
++
++    dev->dev_sdram_freecounts[idx]--;
++    dev->dev_stats.s_sdram_bytes_free -= (SDRAM_MIN_BLOCK_SIZE << idx);
++
++    mb();
++}
++
++#ifdef DEBUG
++static int
++display_blocks (ELAN4_DEV *dev, int indx, char *string)
++{
++    sdramaddr_t block;
++    int nbytes = 0;
++
++    PRINTF (DBG_DEVICE, DBG_SDRAM, "%s - indx %d\n", string, indx);
++    for (block = dev->dev_sdram_freelists[indx]; block != (sdramaddr_t) 0; block = read_next (dev, block))
++    {
++      PRINTF (DBG_DEVICE, DBG_SDRAM, "  %x\n", block);
++      nbytes += (SDRAM_MIN_BLOCK_SIZE << indx);
++    }
++
++    return (nbytes);
++}
++
++void
++elan4_sdram_display (ELAN4_DEV *dev, char *string)
++{
++    int indx;
++    int nbytes = 0;
++    
++    PRINTF (DBG_DEVICE, DBG_SDRAM, "elan4_sdram_display: dev=%p\n", dev);
++    for (indx = 0; indx < SDRAM_NUM_FREE_LISTS; indx++)
++      if (dev->dev_sdram_freelists[indx] != (sdramaddr_t) 0)
++          nbytes += display_blocks (dev, indx, string);
++    PRINTF (DBG_DEVICE, DBG_SDRAM, "\n%d bytes free - %d pages free\n", nbytes, nbytes/SDRAM_PAGE_SIZE);
++}
++
++void
++elan4_sdram_verify (ELAN4_DEV *dev)
++{
++    int indx, size, nbits, i, b;
++    sdramaddr_t block;
++
++    for (indx = 0, size = SDRAM_MIN_BLOCK_SIZE; indx < SDRAM_NUM_FREE_LISTS; indx++, size <<= 1)
++    {
++      unsigned count = 0;
++
++      for (block = dev->dev_sdram_freelists[indx]; block; block = read_next (dev, block), count++)
++      {
++          ELAN4_SDRAM_BANK *bank = sdramaddr_to_bank (dev, block);
++          unsigned         off  = sdramaddr_to_bankoffset (dev, block);
++          int              bit  = sdramaddr_to_bit (dev, indx, block);
++
++          if ((block & (size-1)) != 0)
++              printk ("elan4_sdram_verify: block=%lx indx=%x - not aligned\n", block, indx);
++          
++          if (bank == NULL || off > bank->b_size)
++              printk ("elan4_sdram_verify: block=%lx indx=%x - outside bank\n", block, indx);
++          else if (BT_TEST (bank->b_bitmaps[indx], bit) == 0)
++              printk ("elan4_sdram_verify: block=%lx indx=%x - bit not set\n", block, indx);
++          else
++          {
++              for (i = indx-1, nbits = 2; i >= 0; i--, nbits <<= 1)
++              {
++                  bit = sdramaddr_to_bit (dev, i, block);
++
++                  for (b = 0; b < nbits; b++)
++                      if (BT_TEST(bank->b_bitmaps[i], bit + b))
++                          printk ("elan4_sdram_verify: block=%lx indx=%x - also free i=%d bit=%x\n", block, indx, i, bit+b);
++              }
++          }
++      }
++
++      if (dev->dev_sdram_freecounts[indx] != count)
++          printk ("elan4_sdram_verify: indx=%x expected %d got %d\n", indx, dev->dev_sdram_freecounts[indx], count);
++    }
++}
++
++#endif
++
++static void
++free_block (ELAN4_DEV *dev, sdramaddr_t block, int indx)
++{
++    ELAN4_SDRAM_BANK *bank = sdramaddr_to_bank (dev, block);
++    unsigned         bit  = sdramaddr_to_bit (dev, indx, block);
++    unsigned         size = SDRAM_MIN_BLOCK_SIZE << indx;
++
++    PRINTF3 (DBG_DEVICE, DBG_SDRAM, "free_block: block=%x indx=%d bit=%x\n", block, indx, bit);
++
++    ASSERT ((block & (size-1)) == 0);
++    ASSERT (BT_TEST (bank->b_bitmaps[indx], bit) == 0);
++
++    while (BT_TEST (bank->b_bitmaps[indx], bit ^ 1))
++    {
++      sdramaddr_t buddy = block ^ size;
++      
++      PRINTF3 (DBG_DEVICE, DBG_SDRAM, "free_block: merge block=%x buddy=%x indx=%d\n", block, buddy, indx);
++      
++      BT_CLEAR (bank->b_bitmaps[indx], bit ^ 1);
++      
++      freelist_remove (dev, indx, buddy);
++      
++      block = (block < buddy) ? block : buddy;
++      indx++;
++      size <<= 1;
++      bit >>= 1;
++    }
++    
++    PRINTF3 (DBG_DEVICE, DBG_SDRAM, "free_block: free block=%x indx=%d bit=%x\n", block, indx, bit);
++    
++    freelist_insert (dev, indx, block);
++    
++    BT_SET (bank->b_bitmaps[indx], bit);
++}
++
++void
++elan4_sdram_init (ELAN4_DEV *dev)
++{
++    int indx;
++
++    spin_lock_init (&dev->dev_sdram_lock);
++
++    for (indx = 0; indx < SDRAM_NUM_FREE_LISTS; indx++)
++    {
++      dev->dev_sdram_freelists[indx]  = (sdramaddr_t) 0;
++      dev->dev_sdram_freecounts[indx] = 0;
++    }
++}
++
++void
++elan4_sdram_fini (ELAN4_DEV *dev)
++{
++    spin_lock_destroy (&dev->dev_sdram_lock);
++}
++
++#ifdef CONFIG_MPSAS
++/* size of Elan SDRAM in simulation */
++#define SDRAM_used_addr_bits          (16)
++#define SDRAM_SIMULATION_BANK_SIZE    ((1 << SDRAM_used_addr_bits) * 8)       /* 128 kbytes */
++
++static int
++elan4_sdram_probe_bank (ELAN4_DEV *dev, ELAN4_SDRAM_BANK *bank)
++{
++    printk ("elan%d: memory bank %d is %d Kb\n", dev->dev_instance, (int) (bank - dev->dev_sdram_banks), (int) (SDRAM_SIMULATION_BANK_SIZE / 1024));
++
++    bank->b_size = SDRAM_SIMULATION_BANK_SIZE;
++
++    return 1;
++}
++
++#else
++
++static void
++initialise_cache_tags (ELAN4_DEV *dev, unsigned addr)
++{
++    register int set, line;
++
++    mb();
++
++    /* Initialise the whole cache to hold sdram at "addr" as direct mapped */
++
++    for (set = 0; set < E4_NumCacheSets; set++)
++      for (line = 0; line < E4_NumCacheLines; line++)
++          write_tag (dev, Tags[set][line], addr | (set << 13) | (1 << 11));
++
++    read_tag (dev, Tags[set][line]);  /* read it back to guarantee the memory system is quite again */
++    mb();
++}
++
++static __inline__ int
++sdram_GreyToBinary(int GreyVal, int NoOfBits)
++{
++    int Bit;
++    int BinaryVal=0;
++    for (Bit=(1 << (NoOfBits-1)); Bit != 0; Bit >>= 1)
++      BinaryVal ^= (GreyVal & Bit) ^ ((BinaryVal >> 1) & Bit);
++    return (BinaryVal);
++}
++
++static __inline__ int
++sdram_BinaryToGrey(int BinaryVal)
++{
++    return (BinaryVal ^ (BinaryVal >> 1));
++}
++
++void
++elan4_sdram_setup_delay_lines (ELAN4_DEV *dev, int factor)
++{
++    /* This is used to fix the SDRAM delay line values */
++    int i, AutoGenDelayValue=0;
++    int NewDelayValue;
++
++    if (dev->dev_sdram_cfg & SDRAM_FIXED_DELAY_ENABLE)          /* already setup. */
++      return;
++
++    /* now get an average of 10 dll values */
++    for (i=0;i<10;i++)
++       AutoGenDelayValue += sdram_GreyToBinary(SDRAM_GET_DLL_DELAY(read_reg64 (dev, SDRamConfigReg)),
++                                             SDRAM_FIXED_DLL_DELAY_BITS);
++
++    NewDelayValue = factor + (AutoGenDelayValue / 10); /* Mean of 10 values */
++
++    dev->dev_sdram_cfg = (dev->dev_sdram_cfg & ~(SDRAM_FIXED_DLL_DELAY_MASK << SDRAM_FIXED_DLL_DELAY_SHIFT)) |
++                        SDRAM_FIXED_DELAY_ENABLE | SDRAM_FIXED_DLL_DELAY(sdram_BinaryToGrey(NewDelayValue));
++
++    write_reg64 (dev, SDRamConfigReg, dev->dev_sdram_cfg);    /* Put back the new value */
++
++    pioflush_reg (dev);
++}
++
++static int
++elan4_sdram_probe_bank (ELAN4_DEV *dev, ELAN4_SDRAM_BANK *bank)
++{
++    unsigned long      mappedsize = bank->b_size;
++    ioaddr_t           ioaddr;
++    unsigned long long value, size;
++    register int       i;
++    extern int         sdram_bank_limit;
++
++    if (mappedsize > SDRAM_MAX_BLOCK_SIZE)
++      mappedsize = SDRAM_MAX_BLOCK_SIZE;
++
++    while ((ioaddr = elan4_map_device (dev, ELAN4_BAR_SDRAM, bank->b_base, mappedsize, &bank->b_handle)) == 0)
++    {
++      if (mappedsize <= (64*1024*1024))                       /* boards normally populated with 64mb, so winge if we can't see this much */
++          printk ("elan%d: could not map bank %d size %dMb\n", dev->dev_instance, (int)(bank - dev->dev_sdram_banks), (int)mappedsize/(1024*1024));
++
++      if ((mappedsize >>= 1) < (1024*1024))
++          return 0;
++    }
++
++    /* first probe to see if the memory bank is present */
++    if (dev->dev_devinfo.dev_revision_id == PCI_REVISION_ID_ELAN4_REVA)
++       initialise_cache_tags (dev, E4_CacheSize);
++
++    for (i = 0; i < 64; i++)
++    {
++      unsigned long long pattern = (1ull << i);
++
++      writeq (pattern, ioaddr);                                       /* write pattern at base  */
++
++        if (dev->dev_devinfo.dev_revision_id == PCI_REVISION_ID_ELAN4_REVA)
++          initialise_cache_tags (dev, 0);
++
++      writeq (~pattern, ioaddr + E4_CacheSize);                       /* write ~pattern at cachesize */
++
++        if (dev->dev_devinfo.dev_revision_id == PCI_REVISION_ID_ELAN4_REVA)
++         initialise_cache_tags (dev, E4_CacheSize);
++      
++      writeq (~pattern, ioaddr + 2*E4_CacheSize);                     /* write ~pattern at 2*cachesize */
++        if (dev->dev_devinfo.dev_revision_id == PCI_REVISION_ID_ELAN4_REVA)
++          initialise_cache_tags (dev, 2*E4_CacheSize);
++      
++      value = __elan4_readq (dev, ioaddr);                            /* read pattern back at 0 */
++      
++      if (value != pattern)
++      {
++          printk ("elan%d: sdram bank %d not present\n", dev->dev_instance, (int) (bank - dev->dev_sdram_banks));
++          elan4_unmap_device (dev, ioaddr, mappedsize, &bank->b_handle);
++          return 0;
++      }
++    }
++    
++    /* sdram bank is present, so work out it's size.  We store the maximum size at the base
++     * and then store the address at each address on every power of two address until
++     * we reach the minimum mappable size (PAGESIZE), we then read back the value at the
++     * base to determine the bank size */
++    writeq (mappedsize, ioaddr);
++    if (dev->dev_devinfo.dev_revision_id == PCI_REVISION_ID_ELAN4_REVA)
++        initialise_cache_tags (dev, 0);
++
++    for (size = mappedsize >> 1; size > PAGE_SIZE; size >>= 1)
++    {
++      writeq (size, ioaddr + size);
++        if (dev->dev_devinfo.dev_revision_id == PCI_REVISION_ID_ELAN4_REVA)
++          initialise_cache_tags (dev, size);
++    }
++
++    if ((size = __elan4_readq (dev, ioaddr)) < SDRAM_MIN_BANK_SIZE)
++    {
++      printk ("elan%d: memory bank %d dubious\n", dev->dev_instance, (int) (bank - dev->dev_sdram_banks));
++      elan4_unmap_device (dev, ioaddr, mappedsize, &bank->b_handle);
++      return 0;
++    }
++
++    if (sdram_bank_limit == 0 || size <= (sdram_bank_limit * 1024 * 1024))
++      printk ("elan%d: memory bank %d is %d Mb\n", dev->dev_instance, (int) (bank - dev->dev_sdram_banks), (int) (size / (1024*1024)));
++    else
++    {
++      size = (sdram_bank_limit * 1024 * 1024);
++      printk ("elan%d: limit bank %d to %d Mb\n", dev->dev_instance, (int) (bank - dev->dev_sdram_banks), (int) (size / (1024*1024)));
++    }
++
++    bank->b_size = size;
++
++    elan4_unmap_device (dev, ioaddr, mappedsize, &bank->b_handle);
++    return 1;
++}
++#endif
++
++int
++elan4_sdram_init_bank (ELAN4_DEV *dev, ELAN4_SDRAM_BANK *bank)
++{
++    int indx, size;
++
++    bank->b_ioaddr = 0;
++
++    if (! elan4_sdram_probe_bank (dev, bank))
++      return 0;
++
++    if ((bank->b_ioaddr = elan4_map_device (dev, ELAN4_BAR_SDRAM, bank->b_base, bank->b_size, &bank->b_handle)) == (ioaddr_t) 0)
++    {
++      printk ("elan%d: could not map sdrambank %d\n", dev->dev_instance, (int) (bank - dev->dev_sdram_banks));
++      return 0;
++    }
++
++    for (indx = 0, size = SDRAM_MIN_BLOCK_SIZE; size <= bank->b_size; indx++, size <<= 1) /* allocate the buddy allocator bitmaps */
++      KMEM_ZALLOC (bank->b_bitmaps[indx], bitmap_t *, sizeof (bitmap_t) * BT_BITOUL(bank->b_size/size), 1);
++    
++    return 1;
++}
++
++void
++elan4_sdram_fini_bank (ELAN4_DEV *dev, ELAN4_SDRAM_BANK *bank)
++{
++    int indx, size;
++
++    for (indx = 0, size = SDRAM_MIN_BLOCK_SIZE; size <= bank->b_size; indx++, size <<= 1)
++      KMEM_FREE (bank->b_bitmaps[indx], sizeof (bitmap_t) * BT_BITOUL(bank->b_size/size));
++    
++    elan4_unmap_device (dev, bank->b_ioaddr, bank->b_size, &bank->b_handle);
++}
++
++void
++elan4_sdram_add_bank (ELAN4_DEV *dev, ELAN4_SDRAM_BANK *bank)
++{
++    sdramaddr_t base = bank->b_base;
++    sdramaddr_t top  = bank->b_base + bank->b_size;
++    register int indx;
++    register unsigned long size;
++
++    /* align to the minimum block size */
++    base = (base + SDRAM_MIN_BLOCK_SIZE - 1) & ~((sdramaddr_t) SDRAM_MIN_BLOCK_SIZE-1);
++    top &= ~((sdramaddr_t) SDRAM_MIN_BLOCK_SIZE-1);
++
++    /* don't allow 0 as a valid "base" */
++    if (base == 0)
++      base = SDRAM_MIN_BLOCK_SIZE;
++
++    /* carve the bottom to the biggest boundary */
++    for (indx = 0, size = SDRAM_MIN_BLOCK_SIZE; indx < SDRAM_NUM_FREE_LISTS; indx++, size <<= 1)
++    {
++      if ((base & size) == 0)
++          continue;
++
++      if ((base + size) > top)
++          break;
++
++      free_block (dev, base, indx);
++      
++      base += size;
++    }
++
++    /* carve the top down to the biggest boundary */
++    for (indx = 0, size = SDRAM_MIN_BLOCK_SIZE; indx < SDRAM_NUM_FREE_LISTS; indx++, size <<= 1)
++    {
++      if ((top & size) == 0)
++          continue;
++
++      if ((top - size) < base)
++          break;
++
++      free_block (dev, (top - size), indx);
++      
++      top -= size;
++    }
++
++    /* now free of the space in between */
++    while (base < top)
++    {
++      free_block (dev, base, (SDRAM_NUM_FREE_LISTS-1));
++
++      base += SDRAM_MAX_BLOCK_SIZE;
++    }
++}
++
++sdramaddr_t
++elan4_sdram_alloc (ELAN4_DEV *dev, int nbytes)
++{
++    sdramaddr_t block;
++    register int i, indx;
++    unsigned long size;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->dev_sdram_lock, flags);
++
++    for (indx = 0, size = SDRAM_MIN_BLOCK_SIZE; size < nbytes; indx++, size <<= 1)
++      ;
++
++    PRINTF2 (DBG_DEVICE, DBG_SDRAM, "elan4_sdram_alloc: nbytes=%d indx=%d\n", nbytes, indx);
++
++    /* need to split a bigger block up */
++    for (i = indx; i < SDRAM_NUM_FREE_LISTS; i++, size <<= 1)
++      if (dev->dev_sdram_freelists[i])
++          break;
++    
++    if (i == SDRAM_NUM_FREE_LISTS)
++    {
++      spin_unlock_irqrestore (&dev->dev_sdram_lock, flags);
++      printk ("elan4_sdram_alloc: %d bytes failed\n", nbytes);
++      return ((sdramaddr_t) 0);
++    }
++    
++    PRINTF2 (DBG_DEVICE, DBG_SDRAM, "elan4_sdram_alloc: use block=%x indx=%d\n", dev->dev_sdram_freelists[i], i);
++
++    /* remove the block from the free list */
++    freelist_removehead (dev, i, (block = dev->dev_sdram_freelists[i]));
++
++    /* clear the approriate bit in the bitmap */
++    BT_CLEAR (sdramaddr_to_bank (dev, block)->b_bitmaps[i], sdramaddr_to_bit (dev,i, block));
++
++    /* and split it up as required */
++    while (i-- > indx)
++      free_block (dev, block + (size >>= 1), i);
++
++    spin_unlock_irqrestore (&dev->dev_sdram_lock, flags);
++
++    ASSERT ((block & ((SDRAM_MIN_BLOCK_SIZE << (indx))-1)) == 0);
++
++#ifdef CONFIG_MPSAS
++    elan4_sdram_zeroq_sdram (dev, block, sizeof (sdramblock_t));
++#endif
++
++    return ((sdramaddr_t) block);
++}
++
++void
++elan4_sdram_free (ELAN4_DEV *dev, sdramaddr_t block, int nbytes)
++{
++    register int indx;
++    unsigned long size;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->dev_sdram_lock, flags);
++
++    for (indx = 0, size = SDRAM_MIN_BLOCK_SIZE; size < nbytes; indx++, size <<= 1)
++      ;
++
++    PRINTF2 (DBG_DEVICE, DBG_SDRAM, "elan4_sdram_free: indx=%d block=%x\n", indx, block);
++
++    free_block (dev, block, indx);
++
++    spin_unlock_irqrestore (&dev->dev_sdram_lock, flags);
++}
++
++void
++elan4_sdram_flushcache (ELAN4_DEV *dev, sdramaddr_t addr, int len)
++{
++    int set, off;
++
++    SET_SYSCONTROL (dev, dev_direct_map_pci_writes, CONT_DIRECT_MAP_PCI_WRITES);
++
++    /*
++     * if flushing more than a single set (8K), then you have to flush the whole cache.
++     *   NOTE - in the real world we will probably want to generate a burst across
++     *          the pci bus.
++     */
++    if (len >= E4_CacheSetSize)
++    {
++      PRINTF3 (DBG_DEVICE, DBG_SDRAM, "elan4_sdram_flushcache: addr=%x len=%x (%x) => whole cache\n", addr, len, addr + len);
++
++#ifdef CONFIG_MPSAS
++      elan4_sdram_zeroq_sdram (dev, dev->dev_cacheflush_space, E4_CacheSize);
++#else
++      for (set = 0; set < E4_NumCacheSets; set++)
++          for (off = 0; off < E4_CacheSetSize; off += E4_CacheLineSize)
++              elan4_sdram_writeq (dev, dev->dev_cacheflush_space + (set * E4_CacheSetSize) + off, 0);
++#endif
++    }
++    else
++    {
++      unsigned base    = addr & ~(E4_CACHELINE_SIZE-1);
++      unsigned top     = (addr + len + (E4_CACHELINE_SIZE-1)) & ~(E4_CACHELINE_SIZE-1);
++      unsigned baseoff = base & (E4_CacheSetSize-1);
++      unsigned topoff  = top  & (E4_CacheSetSize-1);
++
++      if ((base ^ top) & E4_CacheSetSize)                     /* wraps */
++      {
++          PRINTF7 (DBG_DEVICE, DBG_SDRAM, "elan4_sdram_flushcache: addr=%x len=%x (%x) => split cache (%x,%x %x,%x)\n", 
++                   addr, len, addr + len, 0, topoff, baseoff, E4_CacheSetSize);
++
++#ifdef CONFIG_MPSAS
++          for (set = 0; set < E4_NumCacheSets; set++)
++          {
++              elan4_sdram_zeroq_sdram (dev, dev->dev_cacheflush_space + (set * E4_CacheSetSize), topoff);
++              elan4_sdram_zeroq_sdram (dev, dev->dev_cacheflush_space + (set * E4_CacheSetSize) + baseoff, E4_CacheSetSize - baseoff);
++          }
++#else
++          for (set = 0; set < E4_NumCacheSets; set++)
++          {
++              for (off = 0; off < (top & (E4_CacheSetSize-1)); off += E4_CACHELINE_SIZE)
++                  elan4_sdram_writeq (dev, dev->dev_cacheflush_space + (set * E4_CacheSetSize) + off, 0);
++              
++              for (off = (base & (E4_CacheSetSize-1)); off < E4_CacheSetSize; off += E4_CACHELINE_SIZE)
++                  elan4_sdram_writeq (dev, dev->dev_cacheflush_space + (set * E4_CacheSetSize) + off, 0);
++          }
++#endif
++      }
++      else
++      {
++          PRINTF5 (DBG_DEVICE, DBG_SDRAM, "elan4_sdram_flushcache: addr=%x len=%x (%x) => part cache (%x,%x)\n", 
++                   addr, len, addr + len, baseoff, topoff);
++
++#ifdef CONFIG_MPSAS
++          for (set = 0; set < E4_NumCacheSets; set++)
++              elan4_sdram_zeroq_sdram (dev, dev->dev_cacheflush_space + (set * E4_CacheSetSize) + baseoff, topoff - baseoff);
++#else
++          for (set = 0; set < E4_NumCacheSets; set++)
++              for (off = (base & (E4_CacheSetSize-1)); off < (top & (E4_CacheSetSize-1)); off += E4_CACHELINE_SIZE)
++                  elan4_sdram_writeq (dev, dev->dev_cacheflush_space + (set * E4_CacheSetSize) + off, 0);
++#endif
++      }
++    }
++    pioflush_sdram (dev);
++    
++    CLEAR_SYSCONTROL (dev, dev_direct_map_pci_writes, CONT_DIRECT_MAP_PCI_WRITES);
++}
++
++static char *
++get_correctableErr_bitpos(uint SyndromeBits)
++{
++    switch (SyndromeBits)
++    {
++    case 0x00: return ("NoErr");
++    case 0x31: return ("00"); 
++    case 0x32: return ("01"); 
++    case 0xc4: return ("02"); 
++    case 0xc8: return ("03"); 
++    case 0x26: return ("04"); 
++    case 0x91: return ("05"); 
++    case 0x89: return ("06"); 
++    case 0x64: return ("07"); 
++    case 0xc1: return ("08"); 
++    case 0xf2: return ("09"); 
++    case 0x34: return ("10"); 
++    case 0xf8: return ("11"); 
++    case 0xf1: return ("12"); 
++    case 0xc2: return ("13"); 
++    case 0xf4: return ("14"); 
++    case 0x38: return ("15"); 
++    case 0xd6: return ("16"); 
++    case 0xa1: return ("17"); 
++    case 0x79: return ("18"); 
++    case 0xa4: return ("19"); 
++    case 0xd9: return ("20"); 
++    case 0xa2: return ("21"); 
++    case 0x76: return ("22"); 
++    case 0xa8: return ("23"); 
++    case 0xe6: return ("24"); 
++    case 0x51: return ("25"); 
++    case 0xb9: return ("26"); 
++    case 0x54: return ("27"); 
++    case 0xe9: return ("28"); 
++    case 0x52: return ("29"); 
++    case 0xb6: return ("30"); 
++    case 0x58: return ("31"); 
++    case 0x13: return ("32"); 
++    case 0x23: return ("33"); 
++    case 0x4c: return ("34"); 
++    case 0x8c: return ("35"); 
++    case 0x62: return ("36"); 
++    case 0x19: return ("37"); 
++    case 0x98: return ("38"); 
++    case 0x46: return ("39"); 
++    case 0x1c: return ("40"); 
++    case 0x2f: return ("41"); 
++    case 0x43: return ("42"); 
++    case 0x8f: return ("43"); 
++    case 0x1f: return ("44"); 
++    case 0x2c: return ("45"); 
++    case 0x4f: return ("46"); 
++    case 0x83: return ("47"); 
++    case 0x6d: return ("48"); 
++    case 0x1a: return ("49"); 
++    case 0x97: return ("50"); 
++    case 0x4a: return ("51"); 
++    case 0x9d: return ("52"); 
++    case 0x2a: return ("53"); 
++    case 0x67: return ("54"); 
++    case 0x8a: return ("55"); 
++    case 0x6e: return ("56"); 
++    case 0x15: return ("57"); 
++    case 0x9b: return ("58"); 
++    case 0x45: return ("59"); 
++    case 0x9e: return ("60"); 
++    case 0x25: return ("61"); 
++    case 0x6b: return ("62"); 
++    case 0x85: return ("63"); 
++    case 0x01: return ("C0"); 
++    case 0x02: return ("C1"); 
++    case 0x04: return ("C2"); 
++    case 0x08: return ("C3"); 
++    case 0x10: return ("C4"); 
++    case 0x20: return ("C5"); 
++    case 0x40: return ("C6"); 
++    case 0x80: return ("C7"); 
++
++    case 0x07: case 0x0b: case 0x0d: case 0x0e: case 0x3d: case 0x3e: case 0x70: case 0x7c: // T  
++    case 0xb0: case 0xbc: case 0xc7: case 0xcb: case 0xd0: case 0xd3: case 0xe0: case 0xe3: // T  
++       return ("triple");
++
++    case 0x0f: case 0x55: case 0x5a: case 0xa5: case 0xaa: case 0xf0: case 0xff: // Q  
++       return ("quadruple");
++
++    case 0x16: case 0x29: case 0x37: case 0x3b: case 0x49: case 0x57: case 0x5b: case 0x5d: case 0x5e: case 0x61: // M  
++    case 0x68: case 0x73: case 0x75: case 0x7a: case 0x7f: case 0x86: case 0x92: case 0x94: case 0xa7: case 0xab: // M  
++    case 0xad: case 0xae: case 0xb3: case 0xb5: case 0xba: case 0xbf: case 0xcd: case 0xce: case 0xd5: case 0xda: // M  
++    case 0xdc: case 0xdf: case 0xe5: case 0xea: case 0xec: case 0xef: case 0xf7: case 0xfb: case 0xfd: case 0xfe: // M  
++       return ("multiple");
++
++    default:  // all other cases
++       return ("double");
++    }
++}
++
++char *
++elan4_sdramerr2str (ELAN4_DEV *dev, E4_uint64 status, E4_uint64 ConfigReg, char *str)
++{
++    E4_uint64 StartupSyndrome    = dev->dev_sdram_initial_ecc_val;
++    int       RisingDQSsyndrome  = ((ECC_RisingDQSSyndrome(status) == ECC_RisingDQSSyndrome(StartupSyndrome)) ?
++                                  0 : ECC_RisingDQSSyndrome(status));
++    int             FallingDQSsyndrome = ((ECC_FallingDQSSyndrome(status) == ECC_FallingDQSSyndrome(StartupSyndrome)) ?
++                                  0 : ECC_FallingDQSSyndrome(status));
++    E4_uint64 Addr = ECC_Addr(status);
++    int       Bank = (Addr >> 6) & 3;
++    int       Cas  = ((Addr >> 3) & 7) | ((Addr >> (8 - 3)) & 0xf8) | ((Addr >> (25 - 8)) & 0x100) |
++                   ((Addr >> (27 - 9)) & 0x200) | ((Addr >> (29 - 10)) & 0xc00);
++    int       Ras  = ((Addr >> 13) & 0xfff) | ((Addr >> (26 - 12)) & 0x1000) | ((Addr >> (28 - 13)) & 0x2000) |
++                   ((Addr >> (30 - 14)) & 0x4000);
++
++    sprintf (str, "Addr=%07llx Bank=%x Ras=%x Cas=%x Falling DQS=%s Rising DQS=%s Syndrome=%x%s%s%s%s Type=%s SDRamDelay=%s,%0d",             /* 41 + 16 + 8 + 15 + 24 + 13 + 22 + 10 + 10 == 151 */
++           (long long)Addr, Bank, Ras, Cas,
++           get_correctableErr_bitpos(FallingDQSsyndrome),
++           get_correctableErr_bitpos(RisingDQSsyndrome),
++           (int)ECC_Syndrome(status),
++           ECC_UncorrectableErr(status)   ? " Uncorrectable" : "",
++           ECC_MultUncorrectErrs(status)  ? " Multiple-Uncorrectable" : "",
++           ECC_CorrectableErr(status)     ? " Correctable" : "",
++           ECC_MultCorrectErrs(status)    ? " Multiple-Correctable" : "",
++           (status & 0x0010000000000000ull)  ? "W" :
++           (status & 0x0020000000000000ull)  ? "R" :
++           (status & 0x0030000000000000ull)  ? "C" : "-",
++           (ConfigReg & SDRAM_FIXED_DELAY_ENABLE)  ? "F" : "A",
++           sdram_GreyToBinary(SDRAM_GET_DLL_DELAY(ConfigReg), SDRAM_FIXED_DLL_DELAY_BITS));
++
++    return str;
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan4/trap.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan4/trap.c    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan4/trap.c 2005-05-11 12:10:12.466928320 -0400
+@@ -0,0 +1,777 @@
++/*
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: trap.c,v 1.19.10.3 2005/03/09 12:00:08 addy Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/trap.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan4/debug.h>
++#include <elan4/device.h>
++
++#include <elan4/trtype.h>
++#include <elan4/commands.h>
++
++char * const PermTypes[16] = 
++{
++    "Disabled",       "Unused",          "LocalDataRead", "LocalDataWrite",
++    "LocalRead",      "LocalExecute",    "ReadOnly",      "LocalWrite",
++    "LocalEventOnly", "LocalEventWrite", "RemoteEvent",   "RemoteAll",
++    "RemoteReadOnly", "RemoteWriteOnly", "DataReadWrite", "NoFault",
++};
++
++char * const AccTypes[] =
++{
++    "LocalDataRead ", "LocalDataWrite", "RemoteRead    ", "RemoteWrite   ",
++    "Execute       ", "LocalEvent    ", "Unused        ", "RemoteEvent   "
++};
++char * const DataTypes[] = {"Byte ", "HWord", "Word ", "DWord"};
++char * const PhysTypes[] = {"Special Read", "Special Write", "Physical Read", "Physical Write"};
++    
++char * const EProcTrapNames[] = {
++    "EventProcNoFault",
++    "EventProcAddressAlignment",
++    "EventProcMemoryFault",
++    "EventProcCountWrapError",
++};
++
++char * const CProcTrapNames[] = {
++    "CommandProcNoFault",
++    "CommandProcInserterError",
++    "CommandProcPermissionTrap",
++    "CommandProcSendTransInvalid",
++    "CommandProcSendTransExpected",
++    "CommandProcDmaQueueOverflow",
++    "CommandProcInterruptQueueOverflow",
++    "CommandProcMemoryFault",
++    "CommandProcRouteFetchFault",
++    "CommandProcFailCountZero",
++    "CommandProcAddressAlignment",
++    "CommandProcWaitTrap",
++    "CommandProcMultipleGuards",
++    "CommandProcOpenOnGuardedChan",
++    "CommandProcThreadQueueOverflow",
++    "CommandProcBadData",
++};
++
++char *const CProcInsertError[] = {
++    "No Error",
++    "Overflowed",
++    "Invalid Write Size",
++    "Invalid Write Order",
++};
++
++char * const DProcTrapNames[] = {
++    "DmaProcNoFault",
++    "DmaProcRouteFetchFault",
++    "DmaProcFailCountError",
++    "DmaProcPacketAckError",
++    "DmaProcRunQueueReadFault",
++    "DmaProcQueueOverFlow",
++};
++
++char *const IProcTrapNames[] = {
++    "InputNoFault",
++    "InputAddressAlignment",
++    "InputMemoryFault",
++    "InputInvalidTransType",
++    "InputDmaQueueOverflow",
++    "InputEventEngineTrapped",
++    "InputCrcErrorAfterPAckOk",
++    "InputEopErrorOnWaitForEop",
++    "InputEopErrorTrap",
++    "InputDiscardAfterAckOk",
++};
++
++char *const TProcTrapNames[] = {
++    "HaltThread",
++    "TrapForTooManyInstructions",
++    "InstAccessException",
++    "Unimplemented",
++    "DataAccessException",
++    "DataAlignmentError",
++    "TrapForUsingBadData",
++};
++
++#define declare_spaces(space, str)            char space[64]; do { int i; for (i = 0; i < strlen(str); i++) spaces[i] = ' '; space[i] = '\0'; } while (0)
++#define declare_prefix(space, spaces, str)    char space[64]; do { strcpy (space, spaces); strcat (space, str); } while (0)
++
++void
++elan4_display_farea (void *type, int mode, char *str, E4_FaultSave *farea)
++{
++    E4_uint32 FSR = FaultSaveFSR(farea->FSRAndFaultContext);
++
++    declare_spaces(spaces, str);
++    
++    elan4_debugf (type, mode, "%s Fault occurred at %016llx for context %4x\n", str,
++                farea->FaultAddress, FaultSaveContext(farea->FSRAndFaultContext));
++    
++    if (FSR & AT_VirtualWriteAccBit)                          /* Virtual write access */
++      elan4_debugf (type, mode, "%s FSR=%x: Virtual Write. DWSize=0x%x EndP=0x%x Access=%s DT=%s\n",
++                    spaces, FSR, FSR & AT_VirtualWriteSizeMask,
++                    (FSR >> AT_VirtualWriteEndPtrShift) & AT_VirtualWriteEndPtrMask,
++                    AccTypes[(FSR >> AT_PermBitsShift) & AT_PermBitsMask],
++                    DataTypes[(FSR >> AT_BlkDataTyShift) & AT_BlkDataTyMask]);
++    else if (FSR & AT_VirtualReadAccBit)                      /* Virtual read access */
++      elan4_debugf (type, mode, "%s FSR=%x: Virtual Read. DWSize=0x%x Access=%s DT=%s\n",
++                    spaces, FSR, FSR & AT_VirtualReadSizeMask,
++                    AccTypes[(FSR >> AT_PermBitsShift) & AT_PermBitsMask],
++                    DataTypes[(FSR >> AT_BlkDataTyShift) & AT_BlkDataTyMask]);
++    else
++      elan4_debugf (type, mode, "%s FSR=%x: %s. Size=0x%x\n", spaces,
++                    FSR, PhysTypes[(FSR >> AT_SelBitsShift) & AT_SelBitsMask],
++                    FSR & AT_OtherSizeMask);
++    elan4_debugf (type, mode, "%s FSR: %s %s%s %sWalking\n", spaces,
++                (FSR & AT_NonAlloc) ? "NonAlloc" : "Alloc",
++                (FSR & AT_DmaData) ? "Dma " : "",
++                (FSR & FSR_WalkForThread) ? "ThreadAcc" : "UnitsAcc",
++                (FSR & FSR_Walking) ? "" : "Not");
++    PRINTF (type, mode, "%s FSR: %s%sHashTable=%s\n", spaces,
++          (FSR & FSR_NoTranslationsFound) ? "NoTranslationsFound " : "",
++          (FSR & FSR_WalkingProtectionFault) ? "WalkingProtectionFault " : "",
++          (FSR & FSR_HashTable1) ? "1" : "0");
++    if (FSR & (FSR_RouteVProcErr | FSR_FaultForBadData))
++      elan4_debugf (type, mode, "%s FSR: %s%s\n", spaces,
++                    (FSR & FSR_RouteVProcErr) ? "RouteVProcErr " : "",
++                    (FSR & FSR_FaultForBadData) ? "FaultForBadData " : "");
++}
++
++void
++elan4_display_eproc_trap (void *type, int mode, char *str, ELAN4_EPROC_TRAP *trap)
++{
++    declare_spaces (spaces, str);
++
++    elan4_debugf (type, mode, "%s Status=%016llx %s EventAddr=%016llx CountAndType=%016llx\n", str,
++                trap->tr_status, EProcTrapNames[EPROC_TrapType(trap->tr_status)],
++                trap->tr_eventaddr, trap->tr_event.ev_CountAndType);
++    elan4_debugf (type, mode, "%s Param=%016llx.%016llx\n", spaces,
++                trap->tr_event.ev_Params[0], trap->tr_event.ev_Params[1]);
++
++    elan4_display_farea (type, mode, strcat (spaces, EPROC_Port0Fault(trap->tr_status) ? " EPROC0" : " EPROC1"), &trap->tr_faultarea);
++}
++
++void
++elan4_display_cproc_trap (void *type, int mode, char *str, ELAN4_CPROC_TRAP *trap)
++{
++    declare_spaces(spaces, str);
++
++    elan4_debugf (type, mode, "%s Status=%llx %s Command=%llx\n", str, trap->tr_status, 
++                CProcTrapNames[CPROC_TrapType(trap->tr_status)], trap->tr_command);
++    elan4_debugf (type, mode, "%s Desc=%016llx %016llx %016llx %016llx\n", str,
++                trap->tr_qdesc.CQ_QueuePtrs, trap->tr_qdesc.CQ_HoldingValue,
++                trap->tr_qdesc.CQ_AckBuffers, trap->tr_qdesc.CQ_Control);
++
++    switch (CPROC_TrapType (trap->tr_status))
++    {
++    case CommandProcInserterError:
++      elan4_debugf (type, mode, "%s   %s\n", str, CProcInsertError[CQ_RevB_ErrorType(trap->tr_qdesc.CQ_QueuePtrs)]);
++      break;
++
++    case CommandProcWaitTrap:
++      elan4_display_eproc_trap (type, mode, spaces, &trap->tr_eventtrap);
++      break;
++
++    default:
++      elan4_display_farea (type, mode, spaces, &trap->tr_faultarea);
++      break;
++    }
++}
++
++void
++elan4_display_dproc_trap (void *type, int mode, char *str, ELAN4_DPROC_TRAP *trap)
++{
++    declare_spaces (spaces, str);
++
++    elan4_debugf (type, mode, "%s status %llx - %s\n", str,
++                trap->tr_status, DProcTrapNames[DPROC_TrapType(trap->tr_status)]);
++
++    elan4_debugf (type, mode, "%s DESC %016llx %016llx %016llx %016llx\n", spaces, trap->tr_desc.dma_typeSize, 
++                trap->tr_desc.dma_cookie, trap->tr_desc.dma_vproc, trap->tr_desc.dma_srcAddr);
++    elan4_debugf (type, mode, "%s      %016llx %016llx %016llx\n", spaces, trap->tr_desc.dma_dstAddr, 
++                trap->tr_desc.dma_srcEvent, trap->tr_desc.dma_dstEvent);
++
++    if (DPROC_PrefetcherFault (trap->tr_status))
++      elan4_display_farea (type, mode, spaces, &trap->tr_prefetchFault);
++}
++
++void
++elan4_display_tproc_trap (void *type, int mode, char *str, ELAN4_TPROC_TRAP *trap)
++{
++    register int i;
++    declare_spaces (spaces, str);
++
++    elan4_debugf (type, mode, "%s PC=%016llx nPC=%016llx State=%016llx Status=%016llx -%s%s%s%s\n", str,
++                trap->tr_pc, trap->tr_npc, trap->tr_state, trap->tr_status, 
++                (trap->tr_state & TS_TrapForTooManyInstructions) ? " TrapForTooManyInstructions" : "",
++                (trap->tr_state & TS_Unimplemented)              ? " Unimplemented"              : "",
++                (trap->tr_state & TS_DataAlignmentError)         ? " DataAlignmentError"         : "",
++                (trap->tr_state & TS_InstAccessException)        ? " InstAccessException"        : "",
++                (trap->tr_state & TS_DataAccessException)        ? " DataAlignmentError"         : "");
++    
++    for (i = 0; i < 64; i += 4)
++      elan4_debugf (type, mode, "%s r%d - %016llx %016llx %016llx %016llx\n", spaces, i,
++                    trap->tr_regs[i], trap->tr_regs[i+1], trap->tr_regs[i+2], trap->tr_regs[i+3]);
++    
++    if (trap->tr_state & TS_InstAccessException)
++    {
++      declare_prefix (prefix, spaces, "Inst");
++
++      elan4_display_farea (type, mode, prefix, &trap->tr_instFault);
++    }
++
++    if (trap->tr_state & TS_DataAccessException)
++    {
++      declare_prefix (prefix, spaces, "Data");
++      elan4_display_farea (type, mode, prefix, &trap->tr_dataFault);
++    }
++}
++
++void
++elan4_display_iproc_trap (void *type, int mode, char *str, ELAN4_IPROC_TRAP *trap)
++{
++    register int i;
++    declare_spaces (spaces, str);
++
++    for (i = 0; i < trap->tr_numTransactions; i++)
++    {
++      E4_IprocTrapHeader *hdrp    = &trap->tr_transactions[i];
++      E4_uint64           status  = hdrp->IProcStatusCntxAndTrType;
++      E4_Addr             addr    = hdrp->TrAddr;
++      char               *typeString;
++      char                buffer[256];
++      char               *ptr = buffer;
++      
++      if (IPROC_EOPTrap(status))
++      {
++          switch (IPROC_EOPType(status))
++          {
++          case EOP_GOOD:        typeString = "EopGood";   break;
++          case EOP_BADACK:      typeString = "EopBadAck"; break;
++          case EOP_ERROR_RESET: typeString = "EopReset";  break;
++          default:              typeString = "EopBad";    break;
++          }
++          
++          ptr += sprintf (ptr, "%15s Cntx=%-6d", typeString, IPROC_NetworkContext(status));
++      }
++      else
++      {
++          if (IPROC_BadLength(status))
++              typeString = "BadLength";
++          else if (IPROC_TransCRCStatus(status) == CRC_STATUS_DISCARD)
++              typeString = "DiscardCrc";
++          else if (IPROC_TransCRCStatus(status) == CRC_STATUS_ERROR)
++              typeString = "ErrorCrc Remote Network error";
++          else if (IPROC_TransCRCStatus(status) == CRC_STATUS_BAD)
++              typeString = "BadCrc Cable error into this node.";
++          else
++          {
++              if ((IPROC_TransactionType(status) & TR_BLOCK_OPCODE_MASK) == TR_WRITEBLOCK)
++                  typeString = "WriteBlock";
++              else
++              {
++                  switch (IPROC_TransactionType(status) & TR_OPCODE_MASK)
++                  {
++                  case TR_SETEVENT_IDENTIFY & TR_OPCODE_MASK: typeString = "SetEvent";        break;
++                  case TR_REMOTEDMA & TR_OPCODE_MASK:         typeString = "RemoteDma";       break;
++                  case TR_SENDDISCARD & TR_OPCODE_MASK:       typeString = "SendDiscard";     break;
++                  case TR_GTE & TR_OPCODE_MASK:               typeString = "GTE";             break;
++                  case TR_LT & TR_OPCODE_MASK:                typeString = "LT";              break;
++                  case TR_EQ & TR_OPCODE_MASK:                typeString = "EQ";              break;
++                  case TR_NEQ & TR_OPCODE_MASK:               typeString = "NEQ";             break;
++                  case TR_IDENTIFY & TR_OPCODE_MASK:          typeString = "Idenfity";        break;
++                  case TR_ADDWORD & TR_OPCODE_MASK:           typeString = "AddWord";         break;
++                  case TR_INPUT_Q_COMMIT & TR_OPCODE_MASK:    typeString = "InputQCommit";    break;
++                  case TR_TESTANDWRITE & TR_OPCODE_MASK:      typeString = "TestAndWrite";    break;
++                  case TR_INPUT_Q_GETINDEX & TR_OPCODE_MASK:  typeString = "InputQGetIndex";  break;
++                  case TR_TRACEROUTE_TRANS & TR_OPCODE_MASK:  typeString = "TraceRoute";      break;
++                  default:                                    typeString = "Unknown";         break;
++                  }
++              }
++          }
++
++          ptr += sprintf (ptr, "%15s Cntx=%-6d Addr=%016llx", typeString, IPROC_NetworkContext(status), (unsigned long long) addr);
++      }
++      
++      
++      if (IPROC_TrapValue(status) != InputNoFault)
++      {
++          ptr += sprintf (ptr, " TrType=%2d ChanTrapped=%x GoodAck=%x BadAck=%x InputterChan=%d", IPROC_TrapValue(status),
++                          IPROC_ChannelTrapped(status), IPROC_GoodAckSent(status), IPROC_BadAckSent(status),
++                          IPROC_InputterChan(status));
++          if (IPROC_EOPTrap(status))
++              ptr += sprintf (ptr, " EOPType=%d", IPROC_EOPType(status));
++          else
++              ptr += sprintf (ptr, " %s%s%s%s", 
++                              IPROC_FirstTrans(status) ? " FirstTrans" : "",
++                              IPROC_LastTrans(status) ? " LastTrans" : "",
++                              (IPROC_TransactionType(status) & TR_WAIT_FOR_EOP) ? " WaitForEop" : "",
++                              (IPROC_GoodAckSent(status) &  (1 << IPROC_Channel(status))) ? " AckSent" : "");
++      }
++      
++      elan4_debugf (type, mode, "%s %s\n", str, buffer);
++
++      str = spaces;
++    }
++
++    elan4_display_farea (type, mode, spaces, &trap->tr_faultarea);
++}
++
++#define elan4_sdram_copy_faultarea(dev, unit, farea) \
++    elan4_sdram_copyq_from_sdram ((dev), (dev)->dev_faultarea + (unit) * sizeof (E4_FaultSave), (E4_uint64 *) farea, sizeof (E4_FaultSave));
++
++void
++elan4_extract_eproc_trap (ELAN4_DEV *dev, E4_uint64 status, ELAN4_EPROC_TRAP *trap, int iswaitevent)
++{
++    /* only one of the memory ports can fault at a time */
++    ASSERT (EPROC_TrapType(status) != EventProcMemoryFault || (EPROC_Port0Fault(status) ^ EPROC_Port1Fault(status)) == 1);
++
++    trap->tr_status = status;
++    
++    if (EPROC_Port0Fault(status))
++      elan4_sdram_copy_faultarea (dev, CUN_EventProc0, &trap->tr_faultarea);
++    if (EPROC_Port1Fault(status))
++      elan4_sdram_copy_faultarea (dev, CUN_EventProc1, &trap->tr_faultarea);
++
++    if (iswaitevent)
++    {
++      /*
++       * for waitevents the Event address is always taken from the command processor
++       * 
++       * if we trapped during the copy then we take the "Event" from the event processor
++       * since we need to complete the copy.  Otherwise we'll be reissuing the original
++       * command again
++       */
++      E4_uint32 fsr = FaultSaveFSR(trap->tr_faultarea.FSRAndFaultContext);
++
++      trap->tr_eventaddr = read_reg64 (dev, CommandHold) ^ WAIT_EVENT_CMD;
++
++      if (EPROC_TrapType(trap->tr_status) == EventProcMemoryFault && 
++          (AT_Perm(fsr) == AT_PermLocalDataRead || AT_Perm(fsr) == AT_PermLocalDataWrite))
++      {
++          trap->tr_event.ev_CountAndType = read_reg64 (dev, EventCountAndType);
++          trap->tr_event.ev_Params[0]    = read_reg64 (dev, EventParameters[0]);
++          trap->tr_event.ev_Params[1]    = read_reg64 (dev, EventParameters[1]);
++      }
++      else
++      {
++          trap->tr_event.ev_Params[0]    = read_reg64 (dev, CommandCopy[5]);
++          trap->tr_event.ev_CountAndType = read_reg64 (dev, CommandCopy[4]);
++          trap->tr_event.ev_Params[1]    = read_reg64 (dev, CommandCopy[6]);
++
++      }
++    }
++    else
++    {
++      trap->tr_eventaddr             = read_reg64 (dev, EventAddress);
++      trap->tr_event.ev_CountAndType = read_reg64 (dev, EventCountAndType);
++      trap->tr_event.ev_Params[0]    = read_reg64 (dev, EventParameters[0]);
++      trap->tr_event.ev_Params[1]    = read_reg64 (dev, EventParameters[1]);
++    }
++
++    BumpDevStat (dev, s_eproc_trap_types[EPROC_TrapType(status)]);
++}
++
++int 
++cproc_open_extract_vp (ELAN4_DEV *dev, ELAN4_CQ *cq, int chan)
++{
++      /* cq = ucq->ucq_cq */
++      if ((cq->cq_perm & CQ_STENEnableBit) != 0)
++      {
++            sdramaddr_t   cqdesc       = dev->dev_cqaddr + (elan4_cq2num(cq) * sizeof (E4_CommandQueueDesc));
++          E4_uint64     queuePtrs    = elan4_sdram_readq (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_QueuePtrs));
++          sdramaddr_t   insertPtr    = (queuePtrs & CQ_PtrMask);
++          sdramaddr_t   commandPtr   = CQ_CompletedPtr (queuePtrs);
++          unsigned int  cqSize       = CQ_Size ((queuePtrs >> CQ_SizeShift) & CQ_SizeMask);
++
++          if (dev->dev_devinfo.dev_revision_id != PCI_REVISION_ID_ELAN4_REVA && (queuePtrs & CQ_RevB_ReorderingQueue))
++          {
++              E4_uint32 oooMask = elan4_sdram_readl (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_HoldingValue));
++
++              for (; (oooMask & 1) != 0; oooMask >>= 1)
++                  insertPtr = (insertPtr & ~(cqSize-1)) | ((insertPtr + sizeof (E4_uint64)) & (cqSize-1));
++          }
++
++          while (commandPtr != insertPtr)
++          {
++              E4_uint64    command = elan4_sdram_readq (dev, commandPtr);
++              unsigned int cmdSize;
++
++                switch (__categorise_command (command, &cmdSize))
++              {
++              case 0:
++                  (void) __whole_command (&commandPtr, insertPtr, cqSize, cmdSize);
++                  break;
++
++              case 1: /* open */
++                  if (((chan << 4) == (command & (1<<4))))
++                      /* Matches supplied channel */
++                      return (command >> 32);
++                  else
++                      (void) __whole_command (&commandPtr, insertPtr, cqSize, cmdSize);
++                  break;
++
++              case 2:
++                  (void) __whole_command (&commandPtr, insertPtr, cqSize, cmdSize);
++              case 3:
++                  printk ("cproc_open_extract_vp: invalid command %llx\n", command);
++                  return -1;
++              }
++          } /* while */
++      }
++
++      return -1;
++}
++
++void
++elan4_extract_cproc_trap (ELAN4_DEV *dev, E4_uint64 status, ELAN4_CPROC_TRAP *trap, unsigned cqnum)
++{
++    /* extract the state from the device */
++    elan4_sdram_copy_faultarea (dev, CUN_CommandProc, &trap->tr_faultarea);
++
++    trap->tr_status  = status;
++    trap->tr_command = read_reg64 (dev, CommandHold);
++    
++    elan4_sdram_copyq_from_sdram (dev, dev->dev_cqaddr + (cqnum * sizeof (E4_CommandQueueDesc)), &trap->tr_qdesc, sizeof (E4_CommandQueueDesc));
++
++    if (CPROC_TrapType (status) == CommandProcWaitTrap)
++      elan4_extract_eproc_trap (dev, read_reg64 (dev, EProcStatus), &trap->tr_eventtrap, 1);
++
++    BumpDevStat (dev, s_cproc_trap_types[CPROC_TrapType(status)]);
++
++    if (PackValue(trap->tr_qdesc.CQ_AckBuffers, 0) == PackTimeout || PackValue(trap->tr_qdesc.CQ_AckBuffers, 1) == PackTimeout)
++      BumpDevStat (dev, s_cproc_timeout);
++}
++
++void
++elan4_extract_dproc_trap (ELAN4_DEV *dev, E4_uint64 status, ELAN4_DPROC_TRAP *trap, unsigned unit)
++{
++    trap->tr_status = status;
++    
++    if (unit == 0)
++    {
++      trap->tr_desc.dma_typeSize   = read_reg64 (dev, Dma0Desc.dma_typeSize);
++      trap->tr_desc.dma_cookie     = read_reg64 (dev, Dma0Desc.dma_cookie);
++      trap->tr_desc.dma_vproc      = read_reg64 (dev, Dma0Desc.dma_vproc);
++      trap->tr_desc.dma_srcAddr    = read_reg64 (dev, Dma0Desc.dma_srcAddr);
++      trap->tr_desc.dma_dstAddr    = read_reg64 (dev, Dma0Desc.dma_dstAddr);
++      trap->tr_desc.dma_srcEvent   = read_reg64 (dev, Dma0Desc.dma_srcEvent);
++      trap->tr_desc.dma_dstEvent   = read_reg64 (dev, Dma0Desc.dma_dstEvent);
++      
++      elan4_sdram_copy_faultarea (dev, CUN_DProcPA0, &trap->tr_packAssemFault);
++    }
++    else
++    {
++      trap->tr_desc.dma_typeSize   = read_reg64 (dev, Dma1Desc.dma_typeSize);
++      trap->tr_desc.dma_cookie     = read_reg64 (dev, Dma1Desc.dma_cookie);
++      trap->tr_desc.dma_vproc      = read_reg64 (dev, Dma1Desc.dma_vproc);
++      trap->tr_desc.dma_srcAddr    = read_reg64 (dev, Dma1Desc.dma_srcAddr);
++      trap->tr_desc.dma_dstAddr    = read_reg64 (dev, Dma1Desc.dma_dstAddr);
++      trap->tr_desc.dma_srcEvent   = read_reg64 (dev, Dma1Desc.dma_srcEvent);
++      trap->tr_desc.dma_dstEvent   = read_reg64 (dev, Dma1Desc.dma_dstEvent);
++      
++      elan4_sdram_copy_faultarea (dev, CUN_DProcPA1, &trap->tr_packAssemFault);
++    }
++    
++    if (DPROC_PrefetcherFault (trap->tr_status))
++      elan4_sdram_copy_faultarea (dev, (CUN_DProcData0 | DPROC_FaultUnitNo(trap->tr_status)), &trap->tr_prefetchFault);
++
++    if (DPROC_PacketTimeout (trap->tr_status))
++      BumpDevStat (dev, s_dproc_timeout);
++
++    BumpDevStat (dev, s_dproc_trap_types[DPROC_TrapType(status)]);
++}    
++
++void
++elan4_extract_tproc_trap (ELAN4_DEV *dev, E4_uint64 status, ELAN4_TPROC_TRAP *trap)
++{
++    int i;
++
++    trap->tr_status = status;
++    trap->tr_state  = read_reg64 (dev, Thread_Trap_State);
++    trap->tr_pc     = read_reg64 (dev, PC_W);
++    trap->tr_npc    = read_reg64 (dev, nPC_W);
++    trap->tr_dirty  = read_reg64 (dev, DirtyBits);
++    trap->tr_bad    = read_reg64 (dev, BadBits);
++
++#ifdef CONFIG_MPSAS
++    if (sas_copyfrom_dev (dev->dev_osdep.pdev, ELAN4_BAR_REGISTERS, 
++                        ((dev->dev_devinfo.dev_revision_id == PCI_REVISION_ID_ELAN4_REVA) ? ELAN4_REVA_REG_OFFSET : ELAN4_REVB_REG_OFFSET) +
++                        offsetof (E4_Registers, Regs.TProcRegs), (unsigned long) &trap->tr_regs, 64*sizeof (E4_uint64)) < 0)
++    {
++      for (i = 0; i < 64; i++)
++          if (trap->tr_dirty & ((E4_uint64) 1 << i))
++              trap->tr_regs[i] = read_reg64 (dev, TProcRegs[i]);
++    }
++
++    for (i = 0; i < 64; i++)
++      if (! (trap->tr_dirty & ((E4_uint64) 1 << i)))
++          trap->tr_regs[i] = 0xdeadbabedeadbabeULL;
++#else
++    for (i = 0; i < 64; i++)
++    {
++      if (trap->tr_dirty & ((E4_uint64) 1 << i))
++          trap->tr_regs[i] = read_reg64 (dev, TProcRegs[i]);
++      else
++          trap->tr_regs[i] = 0xdeadbabedeadbabeULL;
++    }
++#endif
++    
++    if (trap->tr_state & TS_DataAccessException)
++      elan4_sdram_copy_faultarea (dev, CUN_TProcData0 | TS_DataPortNo (trap->tr_state), &trap->tr_dataFault);
++
++    if (trap->tr_state & TS_InstAccessException)
++      elan4_sdram_copy_faultarea (dev, CUN_TProcInst, &trap->tr_instFault);
++
++    for (i = 0; i < 7; i++)
++      if (trap->tr_state & (1 << i))
++          BumpDevStat (dev, s_tproc_trap_types[i]);
++}
++
++void
++elan4_extract_iproc_trap (ELAN4_DEV *dev, E4_uint64 status, ELAN4_IPROC_TRAP *trap, unsigned unit)
++{
++    sdramaddr_t hdroff  = dev->dev_inputtraparea + offsetof (E4_IprocTrapState, TrHeader[0][unit]);
++    sdramaddr_t dataoff = dev->dev_inputtraparea + offsetof (E4_IprocTrapState, TrData[0][unit]);
++    register int i, j;
++    int                 CurrUnitNo    = (unit >= 2) ? CUN_IProcHighPri : CUN_IProcLowPri;
++    sdramaddr_t CurrFaultArea = dev->dev_faultarea + (CurrUnitNo * sizeof (E4_FaultSave));
++
++    /* Finally copy the fault area */
++    elan4_sdram_copy_faultarea (dev, CurrUnitNo, &trap->tr_faultarea);
++
++    /*
++     * Clear out the fault save area after reading to allow a fault on the write of the back pointer of
++     * an InputQCommit to be obsurved if a simultaneous event proc trap occurs.
++     */
++    elan4_sdram_writeq (dev, CurrFaultArea + offsetof(E4_FaultSave, FSRAndFaultContext), 0x0ULL);
++    elan4_sdram_writeq (dev, CurrFaultArea + offsetof(E4_FaultSave, FaultAddress), 0x0ULL);
++
++    /* copy the transaction headers */
++    trap->tr_transactions[0].IProcStatusCntxAndTrType = status;
++    trap->tr_transactions[0].TrAddr                   = elan4_sdram_readq (dev, hdroff + offsetof (E4_IprocTrapHeader, TrAddr));
++    
++    for (i = 0; !IPROC_EOPTrap(trap->tr_transactions[i].IProcStatusCntxAndTrType);)
++    {
++      if (IPROC_BadLength (trap->tr_transactions[i].IProcStatusCntxAndTrType))
++          BumpDevStat (dev, s_bad_length);
++      else if (IPROC_TransCRCStatus (trap->tr_transactions[i].IProcStatusCntxAndTrType) == CRC_STATUS_BAD)
++          BumpDevStat (dev, s_crc_bad);
++      else if (IPROC_TransCRCStatus (trap->tr_transactions[i].IProcStatusCntxAndTrType) == CRC_STATUS_ERROR)
++          BumpDevStat (dev, s_crc_error);
++
++      BumpDevStat (dev, s_iproc_trap_types[IPROC_TrapValue (trap->tr_transactions[i].IProcStatusCntxAndTrType)]);
++
++      hdroff += NO_OF_INPUT_CHANNELS*sizeof (E4_IprocTrapHeader);
++
++      if (++i == MAX_TRAPPED_TRANS)
++          break;
++
++      elan4_sdram_copyq_from_sdram (dev, hdroff, &trap->tr_transactions[i], sizeof (E4_IprocTrapHeader));
++    }
++    
++    if (IPROC_EOPType (trap->tr_transactions[i].IProcStatusCntxAndTrType) == EOP_ERROR_RESET)
++      BumpDevStat (dev, s_eop_reset);
++
++    /* Remember the number of transactions we've copied */
++    trap->tr_numTransactions = i + 1;
++    
++    /* Copy all the data blocks in one go */
++    for (i = 0; i < MIN (trap->tr_numTransactions, MAX_TRAPPED_TRANS); i++, dataoff += NO_OF_INPUT_CHANNELS*sizeof (E4_IprocTrapData))
++    {
++      if (IPROC_BadLength(status) || IPROC_TransCRCStatus (status) != CRC_STATUS_GOOD)
++          elan4_sdram_copyq_from_sdram (dev, dataoff, trap->tr_dataBuffers[i].Data, TRANS_DATA_DWORDS*sizeof(E4_uint64));
++      else
++      {
++          int trtype  = IPROC_TransactionType(trap->tr_transactions[i].IProcStatusCntxAndTrType);
++          int ndwords = (trtype & TR_SIZE_MASK) >> TR_SIZE_SHIFT;
++
++          elan4_sdram_copyq_from_sdram (dev, dataoff, trap->tr_dataBuffers[i].Data, ndwords*sizeof(E4_uint64));
++
++          for (j = ndwords; j < TRANS_DATA_DWORDS; j++)
++              trap->tr_dataBuffers[i].Data[j] = 0xbeec0f212345678ull;
++      }
++    }
++    
++}
++
++void
++elan4_inspect_iproc_trap (ELAN4_IPROC_TRAP *trap)
++{
++    int i;
++
++    trap->tr_flags         = 0;
++    trap->tr_trappedTrans    = TR_TRANS_INVALID;
++    trap->tr_waitForEopTrans = TR_TRANS_INVALID;
++    trap->tr_identifyTrans   = TR_TRANS_INVALID;
++
++    if (trap->tr_numTransactions > MAX_TRAPPED_TRANS)
++      trap->tr_flags = TR_FLAG_TOOMANY_TRANS;
++
++    /*
++     * Now scan all the transactions received 
++     */
++    for (i = 0; i < MIN(trap->tr_numTransactions, MAX_TRAPPED_TRANS) ; i++)
++    {
++      E4_IprocTrapHeader *hdrp   = &trap->tr_transactions[i];
++      E4_uint64           status = hdrp->IProcStatusCntxAndTrType;
++
++      if (trap->tr_identifyTrans == TR_TRANS_INVALID)
++      {
++          switch (IPROC_TransactionType (status) & (TR_OPCODE_MASK | TR_SIZE_MASK))
++          {
++          case TR_IDENTIFY          & (TR_OPCODE_MASK | TR_SIZE_MASK):
++          case TR_REMOTEDMA         & (TR_OPCODE_MASK | TR_SIZE_MASK):
++          case TR_SETEVENT_IDENTIFY & (TR_OPCODE_MASK | TR_SIZE_MASK):
++          case TR_INPUT_Q_COMMIT    & (TR_OPCODE_MASK | TR_SIZE_MASK):
++          case TR_ADDWORD           & (TR_OPCODE_MASK | TR_SIZE_MASK):
++          case TR_TESTANDWRITE      & (TR_OPCODE_MASK | TR_SIZE_MASK):
++              trap->tr_identifyTrans = i;
++              break;
++          }
++      }
++
++      if (IPROC_TrapValue(status) == InputNoFault)            /* We're looking at transactions stored before the trap */
++          continue;                                           /* these should only be identifies */
++      
++      if (trap->tr_trappedTrans == TR_TRANS_INVALID)          /* Remember the transaction which caused the */
++          trap->tr_trappedTrans = i;                          /* trap */
++
++      if (IPROC_GoodAckSent (status) & (1 << IPROC_InputterChan (status)))
++          trap->tr_flags |= TR_FLAG_ACK_SENT;
++          
++      if (IPROC_EOPTrap(status))                              /* Check for EOP */
++      {
++          ASSERT (i == trap->tr_numTransactions - 1);
++
++          switch (IPROC_EOPType(status))
++          {
++          case EOP_GOOD:
++              /* if we get an EOP_GOOD then the outputer should have received a PAckOk. */  
++              /* unless it was a flood, in which case someone must have sent an ack */
++              /* but not necessarily us */
++              break;
++
++          case EOP_BADACK:
++              /* if we get an EOP_BADACK then the outputer did not receive a PAckOk even if
++               * we sent a PAckOk. WFlag this to ignore the AckSent. */
++              trap->tr_flags |= TR_FLAG_EOP_BAD;
++              break;
++
++          case EOP_ERROR_RESET:
++              /* if we get an EOP_ERROR_RESET then the outputer may or may not have got a PAckOk. */
++              trap->tr_flags |= TR_FLAG_EOP_ERROR;
++              break;
++
++          default:
++              printk ("elan4_inspect_iproc_trap: unknown eop type %d", IPROC_EOPType(status));
++              BUG();
++              /* NOTREACHED */
++          }
++          continue;
++      }
++      else
++      {
++          if (IPROC_BadLength(status) || (IPROC_TransCRCStatus (status) == CRC_STATUS_ERROR ||
++                                          IPROC_TransCRCStatus (status) == CRC_STATUS_BAD))
++          {
++              {
++                  register int j;
++                  if (IPROC_BadLength(status))
++                      PRINTF2 (DBG_DEVICE, DBG_INTR, "LinkError: Trapped on bad length data. status=%016llx Address=%016llx\n",
++                               status, hdrp->TrAddr);
++                  else
++                      PRINTF2 (DBG_DEVICE, DBG_INTR, "LinkError: Trapped with bad CRC. status=%016llx Address=%016llx\n",
++                               status, hdrp->TrAddr);
++                  for (j = 0; j < TRANS_DATA_DWORDS; j++)
++                      PRINTF2 (DBG_DEVICE, DBG_INTR, "LinkError: DataBuffers[%d] : %016llx\n", j, trap->tr_dataBuffers[i].Data[j]);
++              }
++
++              trap->tr_flags |= TR_FLAG_BAD_TRANS;
++              continue;
++          }
++          
++          if (IPROC_TransCRCStatus (status) == CRC_STATUS_DISCARD)
++              continue;
++
++          if ((((IPROC_TransactionType(status) & TR_BLOCK_OPCODE_MASK) == TR_WRITEBLOCK) ||
++               (IPROC_TransactionType(status) == TR_TRACEROUTE_TRANS)) &&
++              (trap->tr_flags & TR_FLAG_ACK_SENT) && trap->tr_identifyTrans == TR_TRANS_INVALID)
++          {
++              /* 
++               * Writeblock after the ack is sent without an identify transaction - this is 
++               * considered to be a DMA packet and requires the next packet to be nacked - since 
++               * the DMA processor will send this in a deterministic time and there's an upper 
++               * limit on the network latency (the output timeout) we just need to hold the context 
++               * filter up for a while.
++               */
++              trap->tr_flags |= TR_FLAG_DMA_PACKET;
++          }
++          
++          if (IPROC_LastTrans(status) && (IPROC_TransactionType(status) & TR_WAIT_FOR_EOP))
++          {
++              /*
++               * WaitForEop transactions - if we have to do network error fixup
++               * then we may need to execute/ignore this transaction dependant
++               * on whether the source will be resending it.
++               */
++              trap->tr_waitForEopTrans = i;
++          }
++
++          /*
++           * This is a special case caused by a minor input processor bug.
++           * If simultaneous InputMemoryFault and InputEventEngineTrapped occur then the chip will probably return
++           * InputEventEngineTrapped even though the write of the back pointer has not occured and must be done by
++           * the trap handler.
++           * In this case the fault address will equal q->q_bptr. If there has been only EventEngineTrap then the
++           * the fault address should be zero as the trap handler now always zeros this after every input trap.
++           */
++          if ((IPROC_TransactionType (status) & TR_OPCODE_MASK) == (TR_INPUT_Q_COMMIT & TR_OPCODE_MASK) &&
++              trap->tr_faultarea.FaultAddress == hdrp->TrAddr + offsetof(E4_InputQueue, q_bptr) &&
++              IPROC_TrapValue(status) == InputEventEngineTrapped)
++          {
++              hdrp->IProcStatusCntxAndTrType = (status & 0xFFFFFFF0FFFFFFFFull) | ((E4_uint64) InputMemoryFault << 32);
++          }
++      }
++
++      PRINTF (DBG_DEVICE, DBG_INTR, "inspect[%d] status=%llx TrapValue=%d -> flags %x\n", i, status, IPROC_TrapValue(status), trap->tr_flags);
++    }
++}
++
++E4_uint64
++elan4_trapped_open_command (ELAN4_DEV *dev, ELAN4_CQ *cq)
++{
++    sdramaddr_t cqdesc     = dev->dev_cqaddr + elan4_cq2num(cq) * sizeof (E4_CommandQueueDesc);
++    E4_uint64   cqcontrol  = elan4_sdram_readq (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_Control));
++    E4_uint32   extractOff = CQ_ExtractPtr (cqcontrol) & (CQ_Size(cq->cq_size)-1);
++    
++    if (extractOff == 0)
++      extractOff = CQ_Size(cq->cq_size) - sizeof (E4_uint64);
++    else
++      extractOff -= sizeof (E4_uint64);
++
++    return (elan4_sdram_readq (dev, cq->cq_space + extractOff));
++}
++
++EXPORT_SYMBOL(elan4_extract_eproc_trap);
++EXPORT_SYMBOL(elan4_display_eproc_trap);
++EXPORT_SYMBOL(elan4_extract_cproc_trap);
++EXPORT_SYMBOL(elan4_display_cproc_trap);
++EXPORT_SYMBOL(elan4_extract_dproc_trap);
++EXPORT_SYMBOL(elan4_display_dproc_trap);
++EXPORT_SYMBOL(elan4_extract_tproc_trap);
++EXPORT_SYMBOL(elan4_display_tproc_trap);
++EXPORT_SYMBOL(elan4_extract_iproc_trap);
++EXPORT_SYMBOL(elan4_inspect_iproc_trap);
++EXPORT_SYMBOL(elan4_display_iproc_trap);
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan4/user.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan4/user.c    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan4/user.c 2005-05-11 12:10:12.471927560 -0400
+@@ -0,0 +1,3362 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: user.c,v 1.68.2.11 2005/03/09 12:00:09 addy Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/user.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <qsnet/kpte.h>
++
++#include <elan/elanmod.h>
++#include <elan4/debug.h>
++#include <elan4/device.h>
++#include <elan4/user.h>
++
++#include <elan4/trtype.h>
++#include <elan4/commands.h>
++
++#include <stdarg.h>
++
++/* allow this code to compile against an Eagle elanmod */
++#ifdef __ELANMOD_DEVICE_H
++#define elan_attach_cap(cap,rnum,args,func)   elanmod_attach_cap(cap,args,func)
++#define elan_detach_cap(cap,rnum)             elanmod_detach_cap(cap)
++#endif
++
++#define NETERR_MSGS   16
++
++int user_p2p_route_options   = FIRST_TIMEOUT(3);
++int user_bcast_route_options = FIRST_TIMEOUT(3);
++int user_dproc_retry_count   = 15;
++int user_cproc_retry_count   = 2;
++
++int num_fault_save           = 30;
++int min_fault_pages          = 1;
++int max_fault_pages          = 128;
++
++static int
++user_validate_cap (USER_CTXT *uctx, ELAN_CAPABILITY *cap, unsigned use)
++{
++    /* Don't allow a user process to attach to system context */
++    if (ELAN4_SYSTEM_CONTEXT (cap->cap_lowcontext) || ELAN4_SYSTEM_CONTEXT (cap->cap_highcontext))
++    {
++      PRINTF3 (DBG_DEVICE, DBG_VP,"user_validate_cap: lctx %x hctx %x high %x\n", cap->cap_lowcontext, cap->cap_highcontext, ELAN4_KCOMM_BASE_CONTEXT_NUM);
++      PRINTF0 (DBG_DEVICE, DBG_VP,"user_validate_cap: user process cant attach to system cap\n");
++      return (EINVAL);
++    }
++    
++    return elanmod_classify_cap(&uctx->uctx_position, cap, use);
++}
++
++static __inline__ void
++__user_signal_trap (USER_CTXT *uctx)
++{
++    switch (uctx->uctx_trap_state)
++    {
++    case UCTX_TRAP_IDLE:
++      PRINTF (uctx, DBG_TRAP, "user_signal_trap: deliver signal %d to pid %d\n", uctx->uctx_trap_signo, uctx->uctx_trap_pid);
++
++      if (uctx->uctx_trap_signo)
++          kill_proc (uctx->uctx_trap_pid, uctx->uctx_trap_signo, 1);
++      break;
++
++    case UCTX_TRAP_SLEEPING:
++      PRINTF (uctx, DBG_TRAP, "user_signal_trap: wakeup sleeping trap handler\n");
++
++      kcondvar_wakeupone (&uctx->uctx_wait, &uctx->uctx_spinlock);
++      break;
++    }
++    uctx->uctx_trap_state = UCTX_TRAP_SIGNALLED;
++}
++
++static void
++user_signal_timer (unsigned long arg)
++{
++    USER_CTXT    *uctx = (USER_CTXT *) arg;
++    unsigned long flags;
++
++    PRINTF (uctx, DBG_TRAP, "user_signal_timer: state=%d pid=%d signal=%d (now %d start %d)\n",
++          uctx->uctx_trap_state, uctx->uctx_trap_pid, uctx->uctx_trap_signo, jiffies,
++          uctx->uctx_int_start);
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    __user_signal_trap (uctx);
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++}
++
++#define MAX_INTS_PER_TICK     50
++#define MIN_INTS_PER_TICK     20
++
++static void
++user_signal_trap (USER_CTXT *uctx)
++{
++    ASSERT (SPINLOCK_HELD (&uctx->uctx_spinlock));
++
++    PRINTF (uctx, DBG_TRAP, "user_signal_trap: state=%d pid=%d signal=%d%s\n", uctx->uctx_trap_state,
++          uctx->uctx_trap_pid, uctx->uctx_trap_signo, timer_pending(&uctx->uctx_int_timer) ? " (timer-pending)" : "");
++
++    uctx->uctx_int_count++;
++
++    if (timer_pending (&uctx->uctx_int_timer))
++      return;
++
++    if (uctx->uctx_int_count > ((int)(jiffies - uctx->uctx_int_start) * MAX_INTS_PER_TICK))
++    {
++      PRINTF (uctx, DBG_TRAP, "user_signal_trap: deferring signal for %d ticks (count %d ticks %d -> %d)\n", 
++              uctx->uctx_int_delay + 1, uctx->uctx_int_count, (int) (jiffies - uctx->uctx_int_start),
++              ((int)(jiffies - uctx->uctx_int_start) * MAX_INTS_PER_TICK));
++
++      /* We're interrupting too fast, so defer this signal */
++      uctx->uctx_int_timer.expires = jiffies + (++uctx->uctx_int_delay);
++
++      add_timer (&uctx->uctx_int_timer);
++    }
++    else
++    {
++      __user_signal_trap (uctx);
++
++      PRINTF (uctx, DBG_TRAP, "user_signal_trap: check signal for %d ticks (count %d ticks %d -> %d)\n", 
++              uctx->uctx_int_delay + 1, uctx->uctx_int_count, (int) (jiffies - uctx->uctx_int_start),
++              (int)(jiffies - uctx->uctx_int_start) * MIN_INTS_PER_TICK);
++          
++      if (uctx->uctx_int_count < ((int) (jiffies - uctx->uctx_int_start)) * MIN_INTS_PER_TICK)
++      {
++          PRINTF (uctx, DBG_TRAP, "user_signal_trap: reset interrupt throttle (count %d ticks %d)\n", 
++                  uctx->uctx_int_count, (int) (jiffies - uctx->uctx_int_start));
++
++          uctx->uctx_int_start = jiffies;
++          uctx->uctx_int_count = 0;
++          uctx->uctx_int_delay = 0;
++      }
++    }
++}
++
++static void
++user_neterr_timer (unsigned long arg)
++{
++    USER_CTXT *uctx = (USER_CTXT *) arg;
++    unsigned long flags;
++    
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++
++    uctx->uctx_status |= UCTX_NETERR_TIMER;
++    
++    user_signal_trap (uctx);
++
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++}
++
++static void
++user_flush_dma_runqueue (ELAN4_DEV *dev, USER_CTXT *uctx, int qfull)
++{
++    E4_uint64          qptrs = read_reg64 (dev, DProcLowPriPtrs);
++    E4_uint32          qsize = E4_QueueSize (E4_QueueSizeValue (qptrs));
++    E4_uint32          qfptr = E4_QueueFrontPointer (qptrs);
++    E4_uint32          qbptr = E4_QueueBackPointer (qptrs);
++    E4_DProcQueueEntry qentry;
++
++    while ((qfptr != qbptr) || qfull)
++    {
++      E4_uint64 typeSize = elan4_sdram_readq (dev, qfptr + offsetof (E4_DProcQueueEntry, Desc.dma_typeSize));
++
++      if (DMA_Context (typeSize) == uctx->uctx_ctxt.ctxt_num)
++      {
++          elan4_sdram_copyq_from_sdram (dev, qfptr, &qentry, sizeof (E4_DProcQueueEntry));
++
++          PRINTF4 (uctx, DBG_SWAP, "user_flush_dma_runqueue: %016llx %016llx %016llx %016llx\n", qentry.Desc.dma_typeSize, 
++                   qentry.Desc.dma_cookie, qentry.Desc.dma_vproc, qentry.Desc.dma_srcAddr);
++          PRINTF3 (uctx, DBG_SWAP, "                         %016llx %016llx %016llx\n", qentry.Desc.dma_dstAddr, 
++                   qentry.Desc.dma_srcEvent, qentry.Desc.dma_dstEvent);
++
++          if (RING_QUEUE_REALLY_FULL (uctx->uctx_dmaQ))
++              uctx->uctx_status |= UCTX_DPROC_QUEUE_OVERFLOW;
++          else
++          {
++              *RING_QUEUE_BACK (uctx->uctx_dmaQ, uctx->uctx_dmas) = qentry.Desc;
++              (void) RING_QUEUE_ADD (uctx->uctx_dmaQ);
++          }
++          
++          qentry.Desc.dma_typeSize = DMA_ShMemWrite | dev->dev_ctxt.ctxt_num;
++          qentry.Desc.dma_cookie   = 0;
++          qentry.Desc.dma_vproc    = 0;
++          qentry.Desc.dma_srcAddr  = 0;
++          qentry.Desc.dma_dstAddr  = 0;
++          qentry.Desc.dma_srcEvent = 0;
++          qentry.Desc.dma_dstEvent = 0;
++
++          elan4_sdram_copyq_to_sdram (dev, &qentry, qfptr, sizeof (E4_DProcQueueEntry));
++      }
++
++      qfptr = (qfptr & ~(qsize-1)) | ((qfptr + sizeof (E4_DProcQueueEntry)) & (qsize-1));
++      qfull = 0;
++    }
++}
++
++static void
++user_flush_thread_runqueue (ELAN4_DEV *dev, USER_CTXT *uctx, int qfull)
++{
++    E4_uint64          qptrs = read_reg64 (dev, TProcLowPriPtrs);
++    E4_uint32          qsize = E4_QueueSize (E4_QueueSizeValue (qptrs));
++    E4_uint32          qfptr = E4_QueueFrontPointer (qptrs);
++    E4_uint32          qbptr = E4_QueueBackPointer (qptrs);
++    E4_TProcQueueEntry qentry;
++
++    while ((qfptr != qbptr) || qfull)
++    {
++      E4_uint64 context = elan4_sdram_readq (dev, qfptr + offsetof (E4_TProcQueueEntry, Context));
++
++      if (TPROC_Context (context) == uctx->uctx_ctxt.ctxt_num)
++      {
++          elan4_sdram_copyq_from_sdram (dev, qfptr, &qentry, sizeof (E4_TProcQueueEntry));
++
++          PRINTF (uctx, DBG_SWAP, "user_flush_thread_runqueue: %016llx %016llx %016llx %016llx\n", qentry.Regs.Registers[0],
++                  qentry.Regs.Registers[1], qentry.Regs.Registers[2], qentry.Regs.Registers[3]);
++          PRINTF (uctx, DBG_SWAP, "                            %016llx %016llx %016llx\n", 
++                  qentry.Regs.Registers[4], qentry.Regs.Registers[5], qentry.Regs.Registers[6]);
++
++          if (RING_QUEUE_REALLY_FULL (uctx->uctx_threadQ))
++              uctx->uctx_status |= UCTX_TPROC_QUEUE_OVERFLOW;
++          else
++          {
++              *RING_QUEUE_BACK (uctx->uctx_threadQ, uctx->uctx_threads) = qentry.Regs;
++              (void) RING_QUEUE_ADD (uctx->uctx_threadQ);
++          }
++          
++          /* change the thread to execute the suspend sequence */
++          qentry.Regs.Registers[0] = dev->dev_tproc_suspend;
++          qentry.Regs.Registers[1] = dev->dev_tproc_space;
++          qentry.Context           = dev->dev_ctxt.ctxt_num;
++
++          elan4_sdram_copyq_to_sdram (dev, &qentry, qfptr, sizeof (E4_TProcQueueEntry));
++      }
++      
++      qfptr = (qfptr & ~(qsize-1)) | ((qfptr + sizeof (E4_TProcQueueEntry)) & (qsize-1));
++      qfull = 0;
++    }
++}
++
++static void
++user_flush_dmas (ELAN4_DEV *dev, void *arg, int qfull)
++{
++    USER_CTXT        *uctx = (USER_CTXT *) arg;
++    unsigned long     flags;
++    
++    ASSERT ((read_reg32 (dev, InterruptReg) & INT_DProcHalted) != 0);
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++
++    if ((uctx->uctx_status & (UCTX_SWAPPED_REASONS|UCTX_STOPPED_REASONS)) == 0)
++    {
++      PRINTF1 (uctx, DBG_SWAP, "user_flush_dmas: status %x - no more reasons\n", uctx->uctx_status);
++
++      uctx->uctx_status &= ~UCTX_STOPPING;
++
++      user_signal_trap (uctx);
++    }
++    else
++    {
++      user_flush_dma_runqueue (dev, uctx, qfull);
++
++      uctx->uctx_status = (uctx->uctx_status | UCTX_STOPPED) & ~UCTX_STOPPING;
++    
++      PRINTF1 (uctx, DBG_SWAP, "user_flush_dmas: statux %x - stopped\n", uctx->uctx_status);
++
++      kcondvar_wakeupall (&uctx->uctx_wait, &uctx->uctx_spinlock);
++    }
++
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++}
++
++static void
++user_flush (ELAN4_DEV *dev, void *arg)
++{
++    USER_CTXT        *uctx = (USER_CTXT *) arg;
++    struct list_head *entry;
++    unsigned long     flags;
++
++    ASSERT ((read_reg32 (dev, InterruptReg) & (INT_Halted|INT_Discarding)) == (INT_Halted|INT_Discarding));
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++
++    if ((uctx->uctx_status & (UCTX_SWAPPED_REASONS|UCTX_STOPPED_REASONS)) == 0)
++    {
++      PRINTF1 (uctx, DBG_SWAP, "user_flush: status %x - no more reasons\n", uctx->uctx_status);
++
++      uctx->uctx_status &= ~UCTX_STOPPING;
++
++      user_signal_trap (uctx);
++    }
++    else
++    {
++      PRINTF1 (uctx, DBG_SWAP, "user_flush: status %x - flushing context\n", uctx->uctx_status);
++
++      list_for_each (entry, &uctx->uctx_cqlist) {
++          USER_CQ *ucq = list_entry (entry, USER_CQ, ucq_link);
++
++          if (ucq->ucq_state == UCQ_RUNNING)
++          {
++              /* NOTE: since the inserter can still be running we modify the permissions
++               *       to zero then when the extractor starts up again it will trap */
++              PRINTF1 (uctx, DBG_SWAP, "user_flush: stopping cq indx=%d\n", elan4_cq2idx(ucq->ucq_cq));
++
++              elan4_updatecq (dev, ucq->ucq_cq, 0, 0);
++          }
++      }
++      
++      user_flush_thread_runqueue (dev, uctx, TPROC_LowRunQueueFull(read_reg64 (dev, TProcStatus)));
++
++      /* since we can't determine whether the dma run queue is full or empty, we use a dma
++       * halt operation to do the flushing - as the reason for halting the dma processor 
++       * will be released when we return, we keep it halted until the flush has completed */
++      elan4_queue_dma_flushop (dev, &uctx->uctx_dma_flushop, 0);
++
++      if (uctx->uctx_status & UCTX_EXITING)
++          elan4_flush_icache_halted (&uctx->uctx_ctxt);
++    }
++
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++}
++
++static void
++user_set_filter (USER_CTXT *uctx, E4_uint32 state)
++{
++    struct list_head *entry;
++
++    ASSERT (SPINLOCK_HELD (&uctx->uctx_spinlock));
++
++    list_for_each (entry, &uctx->uctx_cent_list) {
++      USER_CTXT_ENTRY *cent = list_entry (entry, USER_CTXT_ENTRY, cent_link);
++
++      elan4_set_filter (&uctx->uctx_ctxt, cent->cent_cap->cap_mycontext, state);
++    }
++}
++
++static void
++user_start_nacking (USER_CTXT *uctx, unsigned reason)
++{
++    PRINTF2 (uctx, DBG_SWAP, "user_start_nacking: status %x reason %x\n", uctx->uctx_status, reason);
++
++    ASSERT (SPINLOCK_HELD (&uctx->uctx_spinlock));
++
++    if (UCTX_NACKING(uctx))
++      uctx->uctx_status |= reason;
++    else
++    {
++      uctx->uctx_status |= reason;
++
++      user_set_filter (uctx, E4_FILTER_STATS | E4_FILTER_DISCARD_ALL);
++    }
++}
++
++static void
++user_stop_nacking (USER_CTXT *uctx, unsigned reason)
++{
++    PRINTF2 (uctx, DBG_SWAP, "user_stop_nacking: status %x reason %x\n", uctx->uctx_status, reason);
++    
++    ASSERT (SPINLOCK_HELD (&uctx->uctx_spinlock));
++    
++    uctx->uctx_status &= ~reason;
++    
++    if (! UCTX_NACKING (uctx))
++      user_set_filter (uctx, E4_FILTER_STATS);
++}
++
++static void
++user_start_stopping (USER_CTXT *uctx, unsigned reason)
++{
++    ELAN4_DEV *dev =uctx->uctx_ctxt.ctxt_dev;
++
++    PRINTF2 (uctx, DBG_SWAP, "user_start_stopping: status %x reason %x\n", uctx->uctx_status, reason);
++
++    ASSERT (! (uctx->uctx_status & UCTX_STOPPED));
++
++    user_start_nacking (uctx, reason);
++    
++    if ((uctx->uctx_status & UCTX_STOPPING) != 0)
++      return;
++    
++    uctx->uctx_status |= UCTX_STOPPING;
++
++    /* queue the halt operation to  remove all threads/dmas/cqs from the run queues */
++    /*    and also flush through the context filter change */
++    elan4_queue_haltop (dev, &uctx->uctx_haltop);
++}
++
++static void
++user_stop_stopping (USER_CTXT *uctx, unsigned reason)
++{
++    PRINTF2 (uctx, DBG_SWAP, "user_stop_stopping: status %x reason %x\n", uctx->uctx_status, reason);
++    
++    user_stop_nacking (uctx, reason);
++
++    if (UCTX_RUNNABLE (uctx))
++    {
++      uctx->uctx_status &= ~UCTX_STOPPED;
++
++      PRINTF1 (uctx, DBG_SWAP, "user_stop_stopping: no more reasons => %x\n", uctx->uctx_status);
++
++      user_signal_trap (uctx);
++    }
++}
++
++void
++user_swapout (USER_CTXT *uctx, unsigned reason)
++{
++    ELAN4_DEV *dev = uctx->uctx_ctxt.ctxt_dev;
++    unsigned long flags;
++    
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    
++    PRINTF2 (uctx, DBG_SWAP, "user_swapout: status %x reason %x\n", uctx->uctx_status, reason);
++    
++    user_start_nacking (uctx, reason);
++    
++    while (uctx->uctx_status & (UCTX_SWAPPING|UCTX_STOPPING) &&               /* wait for someone else to finish */
++         uctx->uctx_trap_count > 0)                                           /* and for trap handlers to notice */
++    {                                                                         /* and exit */
++      PRINTF1 (uctx, DBG_SWAP, "user_swapout: waiting for %d trap handlers to exit/previous swapout\n", uctx->uctx_trap_count);
++
++      kcondvar_wakeupall (&uctx->uctx_wait, &uctx->uctx_spinlock);
++      kcondvar_wait (&uctx->uctx_wait, &uctx->uctx_spinlock, &flags);
++    }
++
++    if (uctx->uctx_status & UCTX_SWAPPED)                                     /* already swapped out */
++    {
++      spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++      return;
++    }
++    
++    uctx->uctx_status |= (UCTX_SWAPPING|UCTX_STOPPING);                               /* mark the context as swapping & stopping */
++    
++    /* queue the halt operation to  remove all threads/dmas/cqs from the run queues */
++    /*    and also flush through the context filter change */
++    elan4_queue_haltop (dev, &uctx->uctx_haltop);
++    
++    while (! (uctx->uctx_status & UCTX_STOPPED))
++      kcondvar_wait (&uctx->uctx_wait, &uctx->uctx_spinlock, &flags);
++
++    /* all state has been removed from the elan - we can now "tidy" it up */
++
++    PRINTF0 (uctx, DBG_SWAP, "user_swapout: swapped out\n");
++    
++    uctx->uctx_status = (uctx->uctx_status & ~UCTX_SWAPPING) | UCTX_SWAPPED;
++    
++    kcondvar_wakeupall (&uctx->uctx_wait, &uctx->uctx_spinlock);
++
++    PRINTF1 (uctx, DBG_SWAP, "user_swapout: all done - status %x\n", uctx->uctx_status);
++
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++}
++
++void
++user_swapin (USER_CTXT *uctx, unsigned reason)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++
++    ASSERT (uctx->uctx_status & UCTX_SWAPPED_REASONS);
++
++    PRINTF2 (uctx, DBG_SWAP, "user_swapin: status %x reason %x\n", uctx->uctx_status, reason);
++
++    while (uctx->uctx_status & (UCTX_SWAPPING|UCTX_STOPPING))                 /* wait until other threads have */
++      kcondvar_wait (&uctx->uctx_wait, &uctx->uctx_spinlock, &flags);         /* completed their swap operation */
++
++    ASSERT (uctx->uctx_status & (UCTX_SWAPPED | UCTX_STOPPED));
++
++    user_stop_nacking (uctx, reason);
++
++    if (! (uctx->uctx_status & UCTX_SWAPPED_REASONS))
++    {
++      uctx->uctx_status &= ~UCTX_SWAPPED;
++
++      /* no longer swapped out - wakeup anyone sleeping waiting for swapin */
++      kcondvar_wakeupall (&uctx->uctx_wait, &uctx->uctx_spinlock);
++
++      if (! (uctx->uctx_status & UCTX_STOPPED_REASONS))
++      {
++          uctx->uctx_status &= ~UCTX_STOPPED;
++          user_signal_trap (uctx);
++      }
++    }
++
++    PRINTF1 (uctx, DBG_SWAP, "user_swapin: all done - status %x\n", uctx->uctx_status);
++
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++}
++
++void
++user_destroy_callback (void *arg, ELAN_CAPABILITY *cap, ELAN_CAPABILITY *map)
++{
++    USER_CTXT *uctx = (USER_CTXT *) arg;
++
++    PRINTF (uctx, DBG_VP, "user_destroy_callback: %s\n", map == NULL ? "cap destoyed" : "map destroyed");
++}
++
++int
++user_attach (USER_CTXT *uctx, ELAN_CAPABILITY *cap)
++{
++    ELAN4_DEV       *dev = uctx->uctx_ctxt.ctxt_dev;
++    USER_CTXT_ENTRY *cent;
++    unsigned long flags;
++    int ctype, res;
++    
++    if ((ctype = user_validate_cap (uctx, cap, ELAN_USER_ATTACH)) < 0)
++      return ctype;
++
++    if ((ctype == ELAN_CAP_RMS) && (res = elan_attach_cap (cap, dev->dev_devinfo.dev_rail, uctx, user_destroy_callback)) != 0)
++    {
++      /* NOTE: elan_attach_cap returns +ve errnos */
++      return -res;
++    }
++
++    KMEM_ALLOC (cent, USER_CTXT_ENTRY *, sizeof (USER_CTXT_ENTRY), 1);
++    if (cent == NULL)
++    {
++      if (ctype == ELAN_CAP_RMS)
++          elan_detach_cap (cap, dev->dev_devinfo.dev_rail);
++
++      return -ENOMEM;
++    }
++
++    KMEM_ALLOC (cent->cent_cap, ELAN_CAPABILITY *, ELAN_CAP_SIZE(cap), 1);
++    if (cent->cent_cap == NULL)
++    {
++      if (ctype == ELAN_CAP_RMS)
++          elan_detach_cap (cap, dev->dev_devinfo.dev_rail);
++
++      KMEM_FREE (cent, sizeof (USER_CTXT_ENTRY));
++      return -ENOMEM;
++    }
++
++    memcpy (cent->cent_cap, cap, ELAN_CAP_SIZE(cap));
++
++    if ((res = elan4_attach_filter (&uctx->uctx_ctxt, cap->cap_mycontext)) != 0)
++    {
++      if (ctype == ELAN_CAP_RMS)
++          elan_detach_cap (cap, dev->dev_devinfo.dev_rail);
++      
++      KMEM_FREE (cent->cent_cap, ELAN_CAP_SIZE (cap));
++      KMEM_FREE (cent, sizeof (USER_CTXT_ENTRY));
++
++      return res;
++    }
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++
++    list_add_tail (&cent->cent_link, &uctx->uctx_cent_list);
++
++    if (! UCTX_NACKING (uctx))
++      user_set_filter (uctx, E4_FILTER_STATS);
++
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++
++    return (0);
++    
++}
++
++void
++user_detach (USER_CTXT *uctx, ELAN_CAPABILITY *cap)
++{
++    ELAN4_DEV         *dev = uctx->uctx_ctxt.ctxt_dev;
++    struct list_head  *entry;
++    struct list_head  *next;
++    struct list_head   list;
++    unsigned long      flags;
++
++    INIT_LIST_HEAD (&list);
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    
++    PRINTF (uctx, DBG_NETWORK_CTX, cap ? "user_detach: network context %d\n" : "user_detach: all network contexts\n", cap ? cap->cap_mycontext : 0);
++
++    list_for_each_safe (entry, next, &uctx->uctx_cent_list) {
++      USER_CTXT_ENTRY *cent = list_entry (entry, USER_CTXT_ENTRY, cent_link);
++
++      if (cap == NULL || ELAN_CAP_MATCH (cap, cent->cent_cap))
++      {
++          PRINTF1 (uctx, DBG_NETWORK_CTX, "user_detach: detach from network context %d\n", cent->cent_cap->cap_mycontext);
++          
++          elan4_detach_filter (&uctx->uctx_ctxt, cent->cent_cap->cap_mycontext);
++
++          list_del (&cent->cent_link);
++          list_add_tail (&cent->cent_link, &list);
++      }
++    }
++
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++
++    while (! list_empty (&list))
++    {
++      USER_CTXT_ENTRY *cent = list_entry (list.next, USER_CTXT_ENTRY, cent_link);
++
++      list_del (&cent->cent_link);
++
++      if (user_validate_cap (uctx, cent->cent_cap, ELAN_USER_DETACH) == ELAN_CAP_RMS)
++          elan_detach_cap (cent->cent_cap, dev->dev_devinfo.dev_rail); 
++      
++      KMEM_FREE (cent->cent_cap, ELAN_CAP_SIZE (cent->cent_cap));
++      KMEM_FREE (cent, sizeof (USER_CTXT_ENTRY));
++    }
++}
++
++void
++user_block_inputter (USER_CTXT *uctx, unsigned blocked)
++{
++    unsigned long flags;
++    int isblocked;
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    
++    isblocked = (uctx->uctx_status & UCTX_USER_FILTERING);
++
++    if (blocked && !isblocked)
++      user_start_nacking (uctx, UCTX_USER_FILTERING);
++
++    if (!blocked && isblocked)
++      user_stop_nacking (uctx, UCTX_USER_FILTERING);
++
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++}
++
++static USER_VPSEG *
++user_install_vpseg (USER_CTXT *uctx, unsigned process, unsigned entries)
++{
++    struct list_head *entry;
++    USER_VPSEG       *seg;
++
++    ASSERT (kmutex_is_locked (&uctx->uctx_vpseg_lock));
++
++    list_for_each (entry, &uctx->uctx_vpseg_list) {
++      seg = list_entry (entry, USER_VPSEG, vps_link);
++
++      if (process <= (seg->vps_process + seg->vps_entries-1) && 
++          (process + entries - 1) >= seg->vps_process)
++          return ((USER_VPSEG *) NULL);
++    }
++
++    KMEM_ZALLOC (seg, USER_VPSEG *, sizeof (USER_VPSEG), 1);
++    
++    if (seg == (USER_VPSEG *) NULL)
++      return ((USER_VPSEG *) NULL);
++
++    seg->vps_process = process;
++    seg->vps_entries = entries;
++
++    list_add_tail (&seg->vps_link, &uctx->uctx_vpseg_list);
++
++    return (seg);
++}
++
++static void
++user_remove_vpseg (USER_CTXT *uctx, USER_VPSEG *seg)
++{
++    ASSERT (kmutex_is_locked (&uctx->uctx_vpseg_lock));
++
++    list_del (&seg->vps_link);
++    
++    switch (seg->vps_type)
++    {
++    case USER_VPSEG_P2P:
++      /* These pointers (union) are only valid for P2P segs */
++      if (seg->vps_p2p_routes)
++          KMEM_FREE (seg->vps_p2p_routes, sizeof (E4_VirtualProcessEntry) * seg->vps_entries);
++      
++      if (seg->vps_p2p_cap)
++          KMEM_FREE (seg->vps_p2p_cap, ELAN_CAP_SIZE(seg->vps_p2p_cap));
++
++      break;
++      
++    case USER_VPSEG_BCAST:
++      ;
++    }
++
++    KMEM_FREE (seg, sizeof (USER_VPSEG));
++}
++
++static USER_VPSEG *
++user_find_vpseg (USER_CTXT *uctx, unsigned low, unsigned high)
++{
++    struct list_head *entry;
++
++    ASSERT (kmutex_is_locked (&uctx->uctx_vpseg_lock));
++
++    list_for_each (entry, &uctx->uctx_vpseg_list) {
++      USER_VPSEG *seg = list_entry (entry, USER_VPSEG, vps_link);
++
++      if (seg->vps_process <= low && (seg->vps_process + seg->vps_entries) > high)
++          return (seg);
++    }
++
++    return ((USER_VPSEG *) NULL);
++}
++
++static ELAN_LOCATION 
++user_process2location (USER_CTXT *uctx, USER_VPSEG *seg, unsigned process)
++{
++    ELAN_LOCATION location;
++    int           nnodes, nctxs;
++    int           nodeOff, ctxOff, vpOff;
++
++    location.loc_node    = ELAN_INVALID_NODE;
++    location.loc_context = -1;
++
++    if (seg == NULL)
++      seg = user_find_vpseg (uctx, process, process);
++
++    if (seg == NULL || (seg->vps_type != USER_VPSEG_P2P))
++      return (location);
++
++    nnodes = ELAN_CAP_NUM_NODES (seg->vps_p2p_cap);
++    nctxs  = ELAN_CAP_NUM_CONTEXTS (seg->vps_p2p_cap);
++
++    switch (seg->vps_p2p_cap->cap_type & ELAN_CAP_TYPE_MASK)
++    {
++    case ELAN_CAP_TYPE_BLOCK:
++      for (nodeOff = 0, vpOff = 0; nodeOff < nnodes; nodeOff++)
++      {
++          for (ctxOff = 0; ctxOff < nctxs; ctxOff++)
++          {
++              if ((seg->vps_p2p_cap->cap_type & ELAN_CAP_TYPE_NO_BITMAP) || BT_TEST (seg->vps_p2p_cap->cap_bitmap, ctxOff + (nodeOff * nctxs)))
++              {
++                  if (vpOff++ == (process - seg->vps_process))
++                  { 
++                      location.loc_node    = seg->vps_p2p_cap->cap_lownode + nodeOff;
++                      location.loc_context = seg->vps_p2p_cap->cap_lowcontext + ctxOff;
++                      goto found;
++                  }
++              }
++          }
++      }
++      break;
++      
++    case ELAN_CAP_TYPE_CYCLIC:
++      for (ctxOff = 0, vpOff = 0; ctxOff < nctxs; ctxOff++)
++      {
++          for (nodeOff = 0; nodeOff < nnodes; nodeOff++)
++          {
++              if ((seg->vps_p2p_cap->cap_type & ELAN_CAP_TYPE_NO_BITMAP) || BT_TEST (seg->vps_p2p_cap->cap_bitmap, nodeOff + (ctxOff * nnodes)))
++              {                                   
++                  if (vpOff++ ==  (process - seg->vps_process))
++                  { 
++                      location.loc_node    = seg->vps_p2p_cap->cap_lownode + nodeOff;
++                      location.loc_context = seg->vps_p2p_cap->cap_lowcontext + ctxOff;
++                      goto found;
++                  }
++              }
++          }
++      }
++      break;  
++    }
++       
++ found:
++    return (location);
++}
++
++static unsigned 
++user_location2process (USER_CTXT *uctx, ELAN_LOCATION location)
++{
++    unsigned int      process = ELAN_INVALID_PROCESS;
++    struct list_head *entry;
++    int               nnodes, nctxs;
++    int               nodeOff, ctxOff, vpOff;
++
++    kmutex_lock (&uctx->uctx_vpseg_lock);
++    list_for_each (entry, &uctx->uctx_vpseg_list) {
++      USER_VPSEG *seg = list_entry (entry, USER_VPSEG, vps_link);
++
++      if (seg->vps_type != USER_VPSEG_P2P)
++          continue;
++
++      if (location.loc_node >= seg->vps_p2p_cap->cap_lownode && location.loc_node <= seg->vps_p2p_cap->cap_highnode &&
++          location.loc_context >= seg->vps_p2p_cap->cap_lowcontext && location.loc_context <= seg->vps_p2p_cap->cap_highcontext)
++      {
++          nnodes = ELAN_CAP_NUM_NODES (seg->vps_p2p_cap);
++          nctxs  = ELAN_CAP_NUM_CONTEXTS (seg->vps_p2p_cap);
++
++          switch (seg->vps_p2p_cap->cap_type & ELAN_CAP_TYPE_MASK)
++          {
++          case ELAN_CAP_TYPE_BLOCK:
++              for (nodeOff = 0, vpOff = 0; nodeOff < nnodes; nodeOff++)
++              {
++                  for (ctxOff = 0; ctxOff < nctxs; ctxOff++)
++                  {
++                      if ((seg->vps_p2p_cap->cap_type & ELAN_CAP_TYPE_NO_BITMAP) || BT_TEST (seg->vps_p2p_cap->cap_bitmap, ctxOff + (nodeOff * nctxs)))
++                      {
++                          if (location.loc_node == seg->vps_p2p_cap->cap_lownode + nodeOff &&
++                              location.loc_context == seg->vps_p2p_cap->cap_lowcontext + ctxOff)
++                          {
++                              process = seg->vps_process + vpOff;
++                              goto found;
++                          }
++                          vpOff++;
++                      }
++                  }
++              }
++              break;
++      
++          case ELAN_CAP_TYPE_CYCLIC:
++              for (ctxOff = 0, vpOff = 0; ctxOff < nctxs; ctxOff++)
++              {
++                  for (nodeOff = 0; nodeOff < nnodes; nodeOff++)
++                  {
++                      if ((seg->vps_p2p_cap->cap_type & ELAN_CAP_TYPE_NO_BITMAP) || BT_TEST (seg->vps_p2p_cap->cap_bitmap, nodeOff + (ctxOff * nnodes)))
++                      {
++                          if (location.loc_node == seg->vps_p2p_cap->cap_lownode + nodeOff &&
++                              location.loc_context == seg->vps_p2p_cap->cap_lowcontext + ctxOff)
++                          {
++                              process = seg->vps_process + vpOff;
++                              goto found;
++                          }
++                          vpOff++;
++                      }
++                  }
++              }
++              break;
++          }
++      }
++    }
++ found:
++    kmutex_unlock (&uctx->uctx_vpseg_lock);
++
++    return (process);
++}
++
++static void
++user_loadroute_vpseg (USER_CTXT *uctx, USER_VPSEG *seg, ELAN_POSITION *pos)
++{
++    ELAN4_DEV             *dev    = uctx->uctx_ctxt.ctxt_dev;
++    ELAN_CAPABILITY       *cap    = seg->vps_p2p_cap;
++    unsigned               nnodes = ELAN_CAP_NUM_NODES (cap);
++    unsigned               nctxs  = ELAN_CAP_NUM_CONTEXTS (cap);
++    E4_VirtualProcessEntry route;
++    unsigned             nodeOff;
++    unsigned             ctxOff;
++    unsigned             vpOff;
++
++    switch (cap->cap_type & ELAN_CAP_TYPE_MASK)
++    {
++    case ELAN_CAP_TYPE_BLOCK:
++      for (nodeOff = 0, vpOff = 0; nodeOff < nnodes; nodeOff++)
++      {
++          for (ctxOff = 0; ctxOff < nctxs; ctxOff++)
++          {
++              if ((cap->cap_type & ELAN_CAP_TYPE_NO_BITMAP) || BT_TEST (cap->cap_bitmap, ctxOff + (nodeOff * nctxs)))
++              {
++                  if (seg->vps_p2p_routes != NULL)
++                      route = seg->vps_p2p_routes[vpOff];
++                  else if (elan4_generate_route (&uctx->uctx_position, &route, cap->cap_lowcontext + ctxOff,
++                                                 cap->cap_lownode + nodeOff, cap->cap_lownode + nodeOff, user_p2p_route_options) < 0)
++                  {
++                      vpOff++;
++                      continue;
++                  }
++
++                  PRINTF5 (uctx, DBG_VP, "user_loadroute_vpseg: virtual process %d -> node %d context %d [%016llx.%016llx]\n",
++                           seg->vps_process + vpOff, cap->cap_lownode + nodeOff, cap->cap_lowcontext + ctxOff,
++                           route.Values[0], route.Values[1]);
++                  
++                  elan4_write_route (dev, uctx->uctx_routetable, seg->vps_process + vpOff, &route);
++                                            
++                  vpOff++;
++              }
++          }
++      }
++      break;
++
++    case ELAN_CAP_TYPE_CYCLIC:
++      for (ctxOff = 0, vpOff = 0; ctxOff < nctxs; ctxOff++)
++      {
++          for (nodeOff = 0; nodeOff < nnodes; nodeOff++)
++          {
++              if ((cap->cap_type & ELAN_CAP_TYPE_NO_BITMAP) || BT_TEST (cap->cap_bitmap, nodeOff + (ctxOff * nnodes)))
++              {
++                  if (seg->vps_p2p_routes != NULL)
++                      route = seg->vps_p2p_routes[vpOff];
++                  else if (elan4_generate_route (&uctx->uctx_position, &route, cap->cap_lowcontext + ctxOff,
++                                                 cap->cap_lownode + nodeOff, cap->cap_lownode + nodeOff, user_p2p_route_options) < 0)
++                  {
++                      vpOff++;
++                      continue;
++                  }
++
++                  PRINTF5 (uctx, DBG_VP, "user_loadroute_vpseg: virtual process %d -> node %d context %d [%016llx.%016llx]\n",
++                           seg->vps_process + vpOff, cap->cap_lownode + nodeOff, cap->cap_lowcontext + ctxOff,
++                           route.Values[0], route.Values[1]);
++                  
++                  elan4_write_route (dev, uctx->uctx_routetable, seg->vps_process + vpOff, &route);
++                                            
++                  vpOff++;
++              }
++          }
++      }
++      break;
++    }
++}
++
++static int
++user_loadroute_bcast (USER_CTXT *uctx, USER_VPSEG *seg)
++{
++    ELAN4_DEV             *dev = uctx->uctx_ctxt.ctxt_dev;
++    ELAN_POSITION         *pos = &uctx->uctx_position;
++    E4_VirtualProcessEntry route;
++    USER_VPSEG            *aseg;
++    int                    res;
++    ELAN_LOCATION          low;
++    ELAN_LOCATION          high;
++
++    if ((aseg = user_find_vpseg (uctx, seg->vps_bcast_lowvp, seg->vps_bcast_highvp)) == NULL || aseg->vps_type != USER_VPSEG_P2P)
++      return (-EINVAL);
++    
++#ifdef use_elanmod
++    if ((res = user_validate_cap (dev, aseg->vps_p2p_cap, ELAN_USER_BROADCAST)) < 0)
++      return (res);
++#endif
++    
++    low  = user_process2location (uctx, aseg, seg->vps_bcast_lowvp);
++    high = user_process2location (uctx, aseg, seg->vps_bcast_highvp);
++
++    if (low.loc_context != high.loc_context)
++      return (-EINVAL);
++
++    /* NOTE: if loopback can only broadcast to ourself - 
++     *       if back-to-back can only broadcast to other node */
++    if ((pos->pos_mode == ELAN_POS_MODE_LOOPBACK   && low.loc_node != high.loc_node && low.loc_node != pos->pos_nodeid) ||
++      (pos->pos_mode == ELAN_POS_MODE_BACKTOBACK && low.loc_node != high.loc_node && low.loc_node == pos->pos_nodeid))
++    {
++      return (-EINVAL);
++    }
++    
++    if ((res = elan4_generate_route (pos, &route, low.loc_context, low.loc_node, high.loc_node, user_bcast_route_options)) < 0)
++      return (res);
++
++    PRINTF (uctx, DBG_VP, "user_loadroute_bcast: virtual process %d -> nodes %d.%d context %d [%016llx.%016llx]\n",
++          seg->vps_process, low.loc_node, high.loc_node, low.loc_context, route.Values[0], route.Values[1]);
++    
++    elan4_write_route (dev, uctx->uctx_routetable, seg->vps_process, &route);
++    return (0);
++}
++
++int
++user_add_p2pvp (USER_CTXT *uctx, unsigned process, ELAN_CAPABILITY *cap)
++{
++    USER_VPSEG      *seg;
++    ELAN_CAPABILITY *ncap;
++    unsigned         entries;
++
++    if ((cap->cap_type & ELAN_CAP_TYPE_NO_BITMAP) == 0)
++      entries = bt_nbits (cap->cap_bitmap , ELAN_CAP_BITMAPSIZE(cap));
++    else
++      entries = ELAN_CAP_BITMAPSIZE(cap);
++    
++    if ((process + entries) > (E4_VPT_MIN_ENTRIES << uctx->uctx_routetable->tbl_size))
++      return (-EINVAL);
++
++    KMEM_ALLOC (ncap, ELAN_CAPABILITY *, ELAN_CAP_SIZE (cap), 1);
++
++    if (ncap == NULL)
++      return (-ENOMEM);
++    
++    memcpy (ncap, cap, ELAN_CAP_SIZE (cap));
++
++    kmutex_lock (&uctx->uctx_vpseg_lock);
++
++    if ((seg = user_install_vpseg (uctx, process, entries)) == NULL)
++    {
++      kmutex_unlock (&uctx->uctx_vpseg_lock);
++      return (-EINVAL);
++    }
++    
++    seg->vps_type       = USER_VPSEG_P2P;
++    seg->vps_p2p_cap    = ncap;
++    seg->vps_p2p_routes = NULL;
++
++    user_loadroute_vpseg (uctx, seg, &uctx->uctx_position);
++    
++    kmutex_unlock (&uctx->uctx_vpseg_lock);
++
++    return (0);
++}
++
++int
++user_add_bcastvp (USER_CTXT *uctx, unsigned process, unsigned lowvp, unsigned highvp)
++{
++    USER_VPSEG *seg;
++    int         res;
++
++    if (lowvp > highvp || process >= (E4_VPT_MIN_ENTRIES << uctx->uctx_routetable->tbl_size))
++      return (-EINVAL);
++
++    kmutex_lock (&uctx->uctx_vpseg_lock);
++
++    if ((seg = user_install_vpseg (uctx, process, 1)) == NULL)
++    {
++      kmutex_unlock (&uctx->uctx_vpseg_lock);
++      return (-EINVAL);
++    }
++
++    seg->vps_type         = USER_VPSEG_BCAST;
++    seg->vps_bcast_lowvp  = lowvp;
++    seg->vps_bcast_highvp = highvp;
++
++    if ((res = user_loadroute_bcast (uctx, seg)) < 0)
++      user_remove_vpseg (uctx, seg);
++
++    kmutex_unlock (&uctx->uctx_vpseg_lock);
++    return (res);
++}
++
++int
++user_removevp (USER_CTXT *uctx, unsigned process)
++{
++    USER_VPSEG *seg;
++
++    kmutex_lock (&uctx->uctx_vpseg_lock);
++    
++    if (process == ELAN_INVALID_PROCESS)
++      seg = list_entry (uctx->uctx_vpseg_list.next, USER_VPSEG, vps_link);
++    else
++      seg = user_find_vpseg (uctx, process, process);
++
++    if (seg == NULL)
++    {
++      kmutex_unlock (&uctx->uctx_vpseg_lock);
++      return (-EINVAL);
++    }
++
++    do {
++      ELAN4_DEV    *dev = uctx->uctx_ctxt.ctxt_dev;
++      int i;
++
++      for (i = 0; i < seg->vps_entries; i++)
++          elan4_invalidate_route (dev, uctx->uctx_routetable, seg->vps_process + i);
++
++      user_remove_vpseg (uctx, seg);
++
++    } while (process == ELAN_INVALID_PROCESS && (seg = list_entry (uctx->uctx_vpseg_list.next, USER_VPSEG, vps_link)) != NULL);
++
++    kmutex_unlock (&uctx->uctx_vpseg_lock);
++
++    return (0);
++}
++
++int
++user_set_route (USER_CTXT *uctx, unsigned process, E4_VirtualProcessEntry *route)
++{
++    ELAN4_DEV    *dev = uctx->uctx_ctxt.ctxt_dev;
++    USER_VPSEG   *seg;
++    ELAN_LOCATION location;
++
++    kmutex_lock (&uctx->uctx_vpseg_lock);
++
++    if ((seg = user_find_vpseg (uctx, process, process)) == NULL || seg->vps_type != USER_VPSEG_P2P)
++    {
++      kmutex_unlock (&uctx->uctx_vpseg_lock);
++      return (-EINVAL);
++    }
++
++    /* check that the route supplied is valid and goes to the correct place */
++    location = user_process2location (uctx, seg, process);
++
++    if (elan4_check_route (&uctx->uctx_position, location, route, 0) != 0)
++    {
++      kmutex_unlock (&uctx->uctx_vpseg_lock);
++      return (-EINVAL);
++    }
++
++    if (seg->vps_p2p_routes == NULL)
++      KMEM_ZALLOC (seg->vps_p2p_routes, E4_VirtualProcessEntry *, sizeof (E4_VirtualProcessEntry) * seg->vps_entries, 1);
++    
++    if (seg->vps_p2p_routes == NULL)
++    {
++      kmutex_unlock (&uctx->uctx_vpseg_lock);
++      return (-ENOMEM);
++    }
++    
++    seg->vps_p2p_routes[process - seg->vps_process].Values[0] = route->Values[0];
++    seg->vps_p2p_routes[process - seg->vps_process].Values[1] = ROUTE_CTXT_VALUE(location.loc_context) | (route->Values[1] & ~ROUTE_CTXT_MASK);
++    
++    PRINTF (uctx, DBG_ROUTE, "user_set_route: vp=%d -> %016llx%016llx\n", process, 
++          seg->vps_p2p_routes[process - seg->vps_process].Values[1], seg->vps_p2p_routes[process - seg->vps_process].Values[0]);
++
++    elan4_write_route (dev, uctx->uctx_routetable, process, &seg->vps_p2p_routes[process - seg->vps_process]);
++
++    kmutex_unlock (&uctx->uctx_vpseg_lock);
++
++    return (0);
++}
++
++int
++user_reset_route (USER_CTXT *uctx, unsigned process)
++{
++    ELAN4_DEV             *dev = uctx->uctx_ctxt.ctxt_dev;
++    E4_VirtualProcessEntry route;
++    ELAN_LOCATION          location;
++    USER_VPSEG            *seg;
++
++    kmutex_lock (&uctx->uctx_vpseg_lock);
++
++    if ((seg = user_find_vpseg (uctx, process, process)) == NULL || seg->vps_type != USER_VPSEG_P2P)
++    {
++      kmutex_unlock (&uctx->uctx_vpseg_lock);
++      return (-EINVAL);
++    }
++
++    if (seg->vps_p2p_routes != NULL)
++    {
++      seg->vps_p2p_routes[process - seg->vps_process].Values[0] = 0;
++      seg->vps_p2p_routes[process - seg->vps_process].Values[1] = 0;
++    }
++    
++    /* generate the default route to this location */
++    location = user_process2location (uctx, seg, process);
++
++    PRINTF (uctx, DBG_ROUTE, "user_reset_route: vp=%d\n", process);
++
++    if (elan4_generate_route (&uctx->uctx_position, &route, location.loc_context, location.loc_node, location.loc_node, 0) < 0)
++      elan4_invalidate_route (dev, uctx->uctx_routetable, process);
++    else
++      elan4_write_route (dev, uctx->uctx_routetable, process, &route);
++
++    kmutex_unlock (&uctx->uctx_vpseg_lock);
++
++    return (0);
++}
++
++int
++user_get_route (USER_CTXT *uctx, unsigned process, E4_VirtualProcessEntry *route)
++{
++    ELAN4_DEV  *dev = uctx->uctx_ctxt.ctxt_dev;
++    USER_VPSEG   *seg;
++    
++    kmutex_lock (&uctx->uctx_vpseg_lock);
++
++    if ((seg = user_find_vpseg (uctx, process, process)) == NULL || seg->vps_type != USER_VPSEG_P2P)
++    {
++      kmutex_unlock (&uctx->uctx_vpseg_lock);
++      return (-EINVAL);
++    }
++
++    elan4_read_route (dev, uctx->uctx_routetable, process, route);
++
++    kmutex_unlock (&uctx->uctx_vpseg_lock);
++    return (0);
++}
++
++int
++user_check_route (USER_CTXT *uctx, unsigned process, E4_VirtualProcessEntry *route, unsigned *error)
++{
++    ELAN4_DEV  *dev = uctx->uctx_ctxt.ctxt_dev;
++    USER_VPSEG *seg;
++    
++    kmutex_lock (&uctx->uctx_vpseg_lock);
++
++    if ((seg = user_find_vpseg (uctx, process, process)) == NULL || seg->vps_type != USER_VPSEG_P2P)
++    {
++      kmutex_unlock (&uctx->uctx_vpseg_lock);
++      return (-EINVAL);
++    }
++
++    elan4_read_route (dev, uctx->uctx_routetable, process, route);
++
++    *error = elan4_check_route (&uctx->uctx_position, user_process2location (uctx, seg, process), route, 0);
++
++    kmutex_unlock (&uctx->uctx_vpseg_lock);
++    return (0);
++}
++
++int
++user_send_neterr_msg (USER_CTXT *uctx, unsigned int vp, unsigned int nctx, unsigned int retries, ELAN4_NETERR_MSG *msg)
++{
++    USER_VPSEG   *seg;
++    ELAN_LOCATION location;
++    unsigned long flags;
++    int                 res, found = 0;
++    struct list_head *el;
++
++    kmutex_lock (&uctx->uctx_vpseg_lock);
++    /* determine the location of the virtual process */
++    if ((seg = user_find_vpseg (uctx, vp, vp)) == NULL)
++    {
++      PRINTF (uctx, DBG_NETERR, "user_send_neterr_msg: vp=%d has no vpseg\n", vp);
++
++      kmutex_unlock (&uctx->uctx_vpseg_lock);
++      return -EINVAL;
++    }
++
++    switch (seg->vps_type)
++    {
++    case USER_VPSEG_P2P:
++      location = user_process2location (uctx, seg, vp);
++      break;
++
++    case USER_VPSEG_BCAST:
++      PRINTF (uctx, DBG_NETERR, "user_send_neterr_msg: vp=%d is a bcast vp\n", vp);
++      kmutex_unlock (&uctx->uctx_vpseg_lock);
++      return -EINVAL;
++    }
++
++    /*  check that we're attached to the network context */
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    list_for_each (el , &uctx->uctx_cent_list) {
++      USER_CTXT_ENTRY *cent = list_entry (el, USER_CTXT_ENTRY, cent_link);
++      
++      if (cent->cent_cap->cap_mycontext == nctx)
++          found++;
++    }
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++    
++    if (! found)
++    {
++      PRINTF (uctx, DBG_NETERR, "user_send_neterr_msg: nctx=%d not attached\n", nctx);
++      kmutex_unlock (&uctx->uctx_vpseg_lock);
++
++      return -EINVAL;
++    }
++
++    /* Update the fields which the user might have "faked" */
++    msg->msg_context            = location.loc_context;
++    msg->msg_sender.loc_node    = uctx->uctx_position.pos_nodeid;
++    msg->msg_sender.loc_context = nctx;
++
++    res = elan4_neterr_sendmsg (uctx->uctx_ctxt.ctxt_dev, location.loc_node, retries, msg);
++
++    kmutex_unlock (&uctx->uctx_vpseg_lock);
++
++    return (res);
++}
++
++
++static int
++user_resolvevp (USER_CTXT *uctx, unsigned process)
++{
++    int                    res = 0;
++    USER_VPSEG            *seg;
++    ELAN_LOCATION          location;
++    E4_VirtualProcessEntry route;
++
++    PRINTF1 (uctx, DBG_VP, "user_resolvevp: process=%d\n", process);
++
++    kmutex_lock (&uctx->uctx_vpseg_lock);
++
++    if ((seg = user_find_vpseg (uctx, process, process)) == NULL)
++    {
++      kmutex_unlock (&uctx->uctx_vpseg_lock);
++      return (-EINVAL);
++    }
++
++    switch (seg->vps_type)
++    {
++    case USER_VPSEG_P2P:
++#ifdef use_elanmod
++      if ((res = user_validate_cap (uctx, seg->vps_p2p_cap, ELAN_USER_P2P)) != 0)
++          break;
++#endif
++
++      location = user_process2location (uctx, seg, process);
++
++      PRINTF (uctx, DBG_VP, "user_resolvevp: vp=%d -> node=%d ctx=%d\n", process, location.loc_node, location.loc_context);
++      
++      if (seg->vps_p2p_routes != NULL && seg->vps_p2p_routes[process - seg->vps_process].Values[0] != 0)
++          route = seg->vps_p2p_routes[process - seg->vps_process];
++      else if ((res = elan4_generate_route (&uctx->uctx_position, &route, location.loc_context, location.loc_node, location.loc_node, user_p2p_route_options)) < 0)
++          break;;
++      
++      elan4_write_route (uctx->uctx_ctxt.ctxt_dev, uctx->uctx_routetable, process, &route);
++      break;
++
++    case USER_VPSEG_BCAST:
++      res = user_loadroute_bcast (uctx, seg);
++      break;
++      
++    default:
++      res = -EINVAL;
++      break;
++    }
++
++    kmutex_unlock (&uctx->uctx_vpseg_lock);
++    return (res);
++}
++
++static void
++user_eproc_trap (ELAN4_CTXT *ctxt, E4_uint64 status)
++{
++    USER_CTXT    *uctx = (USER_CTXT *) ctxt;
++    unsigned long flags;
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++
++    if (RING_QUEUE_REALLY_FULL (uctx->uctx_eprocTrapQ) || (uctx->uctx_status & UCTX_STOPPED))
++    {
++      PRINTF (uctx, DBG_EPROC, "user_eproc_trap: %s\n", (uctx->uctx_status & UCTX_STOPPED) ? "context stopped" : "trap queue overflow");
++
++      uctx->uctx_status |= UCTX_EPROC_QUEUE_ERROR;
++    }
++    else
++    {
++      elan4_extract_eproc_trap (ctxt->ctxt_dev, status, RING_QUEUE_BACK (uctx->uctx_eprocTrapQ, uctx->uctx_eprocTraps), 0);
++      
++      DBGCMD (ctxt, DBG_EPROC, elan4_display_eproc_trap (ctxt, DBG_EPROC, "user_eproc_trap", RING_QUEUE_BACK(uctx->uctx_eprocTrapQ, uctx->uctx_eprocTraps)));
++      
++      if (RING_QUEUE_ADD (uctx->uctx_eprocTrapQ))
++          user_start_stopping (uctx, UCTX_EPROC_QUEUE_FULL);
++    }
++
++    user_signal_trap (uctx);
++
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++}
++
++static void
++user_cproc_trap (ELAN4_CTXT *ctxt, E4_uint64 status, unsigned cqnum)
++{
++    USER_CTXT        *uctx = (USER_CTXT *) ctxt;
++    USER_CQ          *ucq  = NULL;
++    struct list_head *entry;
++    unsigned long     flags;
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    
++    list_for_each (entry, &uctx->uctx_cqlist) {
++      ucq = list_entry (entry, USER_CQ, ucq_link);
++
++      if (elan4_cq2num(ucq->ucq_cq) == cqnum)
++          break;
++    }
++
++    ASSERT (ucq != NULL);
++
++    if (ucq->ucq_state != UCQ_RUNNING && CPROC_TrapType (status) == CommandProcInserterError)
++    {
++      PRINTF (ctxt, DBG_TRAP, "user_cproc_trap CommandProcInserterError\n");
++      ucq->ucq_errored++;
++    }
++    else
++    {
++      ASSERT (ucq->ucq_state == UCQ_RUNNING);
++
++      elan4_extract_cproc_trap (ctxt->ctxt_dev, status, &ucq->ucq_trap, cqnum);
++
++      DBGCMD (ctxt, DBG_CPROC, elan4_display_cproc_trap (ctxt, DBG_CPROC, "user_cproc_trap", &ucq->ucq_trap));
++
++      ucq->ucq_state = UCQ_TRAPPED;
++      
++    }
++
++    user_signal_trap (uctx);
++      
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++}
++
++static void
++user_dproc_trap (ELAN4_CTXT *ctxt, E4_uint64 status, unsigned unit)
++{
++    USER_CTXT *uctx = (USER_CTXT *) ctxt;
++    unsigned long flags;
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++
++    if (RING_QUEUE_REALLY_FULL (uctx->uctx_dprocTrapQ) || (uctx->uctx_status & UCTX_STOPPED))
++    {
++      PRINTF (uctx, DBG_DPROC, "user_dproc_trap: %s\n", (uctx->uctx_status & UCTX_STOPPED) ? "context stopped" : "trap queue overflow");
++
++      uctx->uctx_status |= UCTX_DPROC_QUEUE_ERROR;
++    }
++    else
++    {
++      ELAN4_DPROC_TRAP *trap = RING_QUEUE_BACK (uctx->uctx_dprocTrapQ, uctx->uctx_dprocTraps);
++      
++      elan4_extract_dproc_trap (ctxt->ctxt_dev, status, trap, unit);
++
++      DBGCMD (ctxt, DBG_DPROC, elan4_display_dproc_trap (ctxt, DBG_DPROC, "user_dproc_trap", trap));
++
++      if (!DPROC_PrefetcherFault (status) && DPROC_TrapType(status) == DmaProcFailCountError && !RING_QUEUE_FULL (uctx->uctx_dmaQ))
++      {
++          trap->tr_desc.dma_typeSize |= DMA_FailCount (user_dproc_retry_count);
++
++          *RING_QUEUE_BACK (uctx->uctx_dmaQ, uctx->uctx_dmas) = trap->tr_desc;
++    
++          (void) RING_QUEUE_ADD (uctx->uctx_dmaQ);
++      }
++      else
++      {
++          if (RING_QUEUE_ADD (uctx->uctx_dprocTrapQ))
++              user_start_stopping (uctx, UCTX_DPROC_QUEUE_FULL);
++      }
++    }
++
++    user_signal_trap (uctx);
++
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++}
++
++static void
++user_tproc_trap (ELAN4_CTXT *ctxt, E4_uint64 status)
++{
++    USER_CTXT *uctx = (USER_CTXT *) ctxt;
++    unsigned long flags;
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++
++    if (RING_QUEUE_REALLY_FULL (uctx->uctx_tprocTrapQ) || (uctx->uctx_status & UCTX_STOPPED))
++    {
++      PRINTF (uctx, DBG_TPROC, "user_tproc_trap: %s\n", (uctx->uctx_status & UCTX_STOPPED) ? "context stopped" : "trap queue overflow");
++
++      uctx->uctx_status |= UCTX_TPROC_QUEUE_ERROR;
++    }
++    else
++    {
++      elan4_extract_tproc_trap (ctxt->ctxt_dev, status, RING_QUEUE_BACK (uctx->uctx_tprocTrapQ, uctx->uctx_tprocTraps));
++      
++      DBGCMD (ctxt, DBG_TPROC, elan4_display_tproc_trap (ctxt, DBG_TPROC, "user_tproc_trap", RING_QUEUE_BACK (uctx->uctx_tprocTrapQ, uctx->uctx_tprocTraps)));
++      
++      if (RING_QUEUE_ADD (uctx->uctx_tprocTrapQ))
++          user_start_stopping (uctx, UCTX_TPROC_QUEUE_FULL);
++    }
++    user_signal_trap (uctx);
++
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++}
++
++static void
++user_iproc_trap (ELAN4_CTXT *ctxt, E4_uint64 status, unsigned unit)
++{
++    USER_CTXT       *uctx  = (USER_CTXT *) ctxt;
++    USER_IPROC_TRAP *utrap = &uctx->uctx_iprocTrap[unit & 1];
++    unsigned long    flags;
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++
++    ASSERT (utrap->ut_state == UTS_IPROC_RUNNING);
++
++    elan4_extract_iproc_trap (ctxt->ctxt_dev, status, &utrap->ut_trap, unit);
++    DBGCMD (ctxt, DBG_IPROC, elan4_display_iproc_trap (ctxt, DBG_IPROC, "user_iproc_trap", &utrap->ut_trap));
++
++    utrap->ut_state = UTS_IPROC_TRAPPED;
++
++    user_start_nacking (uctx, unit ? UCTX_IPROC_CH0_TRAPPED : UCTX_IPROC_CH1_TRAPPED);
++
++    user_signal_trap (uctx);
++
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++}
++
++static void
++user_interrupt (ELAN4_CTXT *ctxt, E4_uint64 cookie)
++{
++    USER_CTXT *uctx = (USER_CTXT *) ctxt;
++    unsigned long flags;
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++
++    PRINTF1 (uctx, DBG_TRAP, "user_interrupt: cookie=%llx\n", cookie);
++
++    switch (cookie)
++    {
++    case ELAN4_INT_COOKIE_DDCQ:
++      uctx->uctx_ddcq_intr--;
++
++      user_signal_trap (uctx);
++      break;
++
++    default:
++      if (uctx->uctx_intcookie_table == NULL || intcookie_fire (uctx->uctx_intcookie_table, cookie) != 0)
++      {
++          PRINTF2 (uctx, DBG_TRAP, "user_interrupt: cookie=%llx %s\n", cookie, uctx->uctx_intcookie_table ? "not found" : "no table");
++          uctx->uctx_status |= UCTX_EPROC_QUEUE_ERROR;
++          user_signal_trap (uctx);
++      }
++      break;
++    }
++
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++}
++
++static void
++user_neterrmsg (ELAN4_CTXT *ctxt, ELAN4_NETERR_MSG *msg)
++{
++    USER_CTXT *uctx = (USER_CTXT *) ctxt;
++    unsigned long flags;
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    
++    if (! RING_QUEUE_FULL (uctx->uctx_msgQ))
++    {
++      memcpy (RING_QUEUE_BACK (uctx->uctx_msgQ, uctx->uctx_msgs), msg, sizeof (ELAN4_NETERR_MSG));
++
++      (void) RING_QUEUE_ADD (uctx->uctx_msgQ);
++    
++      user_signal_trap (uctx);
++    }
++    
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++}
++
++ELAN4_TRAP_OPS user_trap_ops = 
++{
++    user_eproc_trap,
++    user_cproc_trap,
++    user_dproc_trap,
++    user_tproc_trap,
++    user_iproc_trap,
++    user_interrupt,
++    user_neterrmsg,
++};
++
++static int
++deliver_trap (ELAN4_USER_TRAP *utrapp, int type, unsigned proc, void *trap, ...)
++{
++    register int i, len;
++    va_list ap;
++
++    PRINTF (NULL, DBG_TRAP, "deliver_trap: type=%d proc=%d\n", type, proc);
++
++    switch (proc)
++    {
++    case UTS_CPROC:      len = sizeof (ELAN4_CPROC_TRAP); break;
++    case UTS_DPROC:      len = sizeof (ELAN4_DPROC_TRAP); break;
++    case UTS_EPROC:      len = sizeof (ELAN4_EPROC_TRAP); break;
++    case UTS_IPROC:      len = sizeof (ELAN4_IPROC_TRAP); break;
++    case UTS_TPROC:      len = sizeof (ELAN4_TPROC_TRAP); break;
++    case UTS_NETERR_MSG: len = sizeof (ELAN4_NETERR_MSG); break;
++    default:             len = 0; break;
++    }
++
++    if (put_user (type, &utrapp->ut_type) || put_user (proc, &utrapp->ut_proc) || copy_to_user (&utrapp->ut_trap, trap, len))
++      return (UTS_EFAULT);
++
++    va_start (ap, trap);
++    for (i = 0; i < sizeof (utrapp->ut_args)/sizeof (utrapp->ut_args[0]); i++)
++      if (put_user (va_arg (ap, unsigned long), &utrapp->ut_args[i]))
++          return (UTS_EFAULT);
++    va_end (ap);
++
++    return (type);
++}
++
++static int
++user_pagefault (USER_CTXT *uctx, E4_FaultSave *farea)
++{
++    E4_Addr      addr = farea->FaultAddress;
++    E4_uint32    fsr  = FaultSaveFSR(farea->FSRAndFaultContext);
++    FAULT_SAVE  *entry;
++    FAULT_SAVE **predp;
++    int count;
++
++    PRINTF2 (uctx, DBG_FAULT, "user_pagefault: addr=%llx fsr %x\n", (unsigned long long) addr, fsr);
++    
++    if ((fsr & FSR_FaultForBadData) != 0)                     /* Memory ECC error during walk */
++    {
++      PRINTF0 (uctx, DBG_FAULT, "user_pagefault: ECC error during walk\n");
++      return (-EFAULT);
++    }
++    
++    if ((fsr & FSR_FaultForMaxChainCount) != 0)                       /* Have walked a chain of 1024 items */
++    {
++      PRINTF0 (uctx, DBG_FAULT, "user_pagefault: pte chain too long\n");
++      return (-EFAULT);
++    }
++    
++    if (uctx->uctx_num_fault_save)
++    {
++        spin_lock (&uctx->uctx_fault_lock);
++        for( predp = &uctx->uctx_fault_list; (entry = *predp)->next != NULL; predp = &entry->next)
++        {
++          if (entry->addr == (addr & ~((E4_Addr) PAGE_SIZE-1)))
++              break;
++        }
++
++        *predp = entry->next;
++        entry->next = uctx->uctx_fault_list;
++        uctx->uctx_fault_list = entry;
++
++        if (entry->addr == (addr & ~((E4_Addr) PAGE_SIZE-1)))
++        {
++          if ((entry->count <<= 1) > max_fault_pages)
++              entry->count = max_fault_pages;
++        }
++        else
++          entry->count = min_fault_pages;
++
++        entry->addr = (addr & ~((E4_Addr) PAGE_SIZE-1))+(entry->count * PAGE_SIZE);
++        count = entry->count;
++        spin_unlock (&uctx->uctx_fault_lock);
++
++        if (user_load_range (uctx, addr & ~((E4_Addr) PAGE_SIZE-1), count * PAGESIZE, fsr) == 0)
++      return 0;
++
++      /* else pre-faulting has failed, try just this page */
++    }
++
++    return (user_load_range (uctx, addr & ~((E4_Addr) PAGE_SIZE-1), PAGE_SIZE, fsr));
++
++}
++
++static int
++queue_dma_for_retry (USER_CTXT *uctx, ELAN4_USER_TRAP *utrapp, E4_DMA *dma)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++
++    if (RING_QUEUE_FULL (uctx->uctx_dmaQ))
++    {
++      spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++      
++      return (deliver_trap (utrapp, UTS_QUEUE_OVERFLOW, UTS_NOPROC, NULL, UCTX_DPROC_QUEUE_OVERFLOW));
++    }
++
++    *RING_QUEUE_BACK (uctx->uctx_dmaQ, uctx->uctx_dmas) = *dma;
++    
++    (void) RING_QUEUE_ADD (uctx->uctx_dmaQ);
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++
++    return (UTS_FINISHED);
++}
++
++static int
++queue_thread_for_retry (USER_CTXT *uctx, ELAN4_USER_TRAP *utrapp, E4_ThreadRegs *regs)
++{
++    unsigned long flags;
++    
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++
++    if (RING_QUEUE_FULL (uctx->uctx_threadQ))
++    {
++      spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++
++      return (deliver_trap (utrapp, UTS_QUEUE_OVERFLOW, UTS_NOPROC, NULL, UCTX_TPROC_QUEUE_OVERFLOW));
++    }
++
++    *RING_QUEUE_BACK (uctx->uctx_threadQ, uctx->uctx_threads) = *regs;
++    (void) RING_QUEUE_ADD (uctx->uctx_threadQ);
++    
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++
++    return (UTS_FINISHED);
++}
++
++static int
++fixup_eproc_trap (USER_CTXT *uctx, ELAN4_EPROC_TRAP *trap, int waitevent)
++{
++    E4_FaultSave *farea = &trap->tr_faultarea;
++    E4_uint32     fsr   = FaultSaveFSR(farea->FSRAndFaultContext);
++    E4_uint64     CountAndType;
++    E4_uint64     CopySource;
++    E4_uint64     CopyDest;
++
++    /*
++     * Event processor can trap as follows :
++     *   1) Event location read               (faddr == event location & Event Permission)
++     *   2) Event location write      (faddr == event location & Event Permission)
++     *   3) Copy Source read          Read Access
++     *   4) Copy/Write dest write     other
++     *
++     *  NOTE - it is possible to see both 3) and 4) together - but only with physical errors.
++     */
++    if (AT_Perm(fsr) == AT_PermLocalDataRead || AT_Perm(fsr) == AT_PermLocalDataWrite)
++    {
++      /* 
++       * We complete the copy/write by issuing a waitevent 0 of the approriate type.
++       *   - NB mask off bottom bits of EventAddr in case of partial setevent
++       */
++      E4_uint64 EventAddr = trap->tr_eventaddr & ~((E4_uint64) E4_EVENT_ALIGN-1);
++
++      if (! user_ddcq_check (uctx, 4))
++          return (0);
++      
++      if ((trap->tr_event.ev_CountAndType & E4_EVENT_COPY_TYPE_MASK) == E4_EVENT_WRITE)
++      {
++          /* case 4) faulted on write word to destination */
++
++          CountAndType = trap->tr_event.ev_CountAndType & E4_EVENT_TYPE_MASK;
++          
++          PRINTF (uctx, DBG_TRAP, "fixup_eproc_trap: write Event=%llx CountAndType=%llx\n", EventAddr, CountAndType);
++          PRINTF (uctx, DBG_TRAP, "                  WritePtr=%llx WriteValue=%llx\n", 
++                  trap->tr_event.ev_WritePtr, trap->tr_event.ev_WriteValue);
++
++          user_ddcq_waitevent (uctx, EventAddr, CountAndType, trap->tr_event.ev_WritePtr, trap->tr_event.ev_WriteValue);
++      }
++      else
++      {
++          /* case 3) or case 4) faulted on read/write of copy */
++          if (AT_Perm (fsr) == AT_PermLocalDataRead)
++          {
++              CountAndType = (trap->tr_event.ev_CountAndType & E4_EVENT_DATA_TYPE_MASK) | EPROC_CopySize(trap->tr_status);
++              CopySource   = trap->tr_event.ev_CopySource - EVENT_COPY_BLOCK_SIZE;
++              CopyDest     = trap->tr_event.ev_CopyDest;
++          }
++          else
++          {
++              CountAndType = ((trap->tr_event.ev_CountAndType & E4_EVENT_DATA_TYPE_MASK) | 
++                              ((EPROC_CopySize(trap->tr_status) + EVENT_COPY_NDWORDS) & E4_EVENT_COPY_SIZE_MASK));
++              CopySource   = trap->tr_event.ev_CopySource - EVENT_COPY_BLOCK_SIZE;
++              CopyDest     = trap->tr_event.ev_CopyDest - EVENT_COPY_BLOCK_SIZE;
++          }
++          
++          PRINTF (uctx, DBG_TRAP, "fixup_eproc_trap: copy Event=%llx CountAndType=%llx\n", EventAddr, CountAndType);
++          PRINTF (uctx, DBG_TRAP, "                  CopySource=%llx CopyDest=%llx\n", CopySource, CopyDest);
++
++          user_ddcq_waitevent (uctx, EventAddr, CountAndType, CopySource, CopyDest);
++      }
++    }
++    else
++    {
++      E4_uint64 EventAddr  = trap->tr_eventaddr & ~((E4_uint64) E4_EVENT_ALIGN-1);
++      E4_uint32 EventCount = trap->tr_eventaddr & (E4_EVENT_ALIGN-1);
++
++      /* case 1) or 2) - just reissue the event */
++      if (! waitevent)
++          PRINTF (uctx, DBG_TRAP, "fixup_eproc_trap: setevent EventAddr=%llx EventCount=%x\n", EventAddr, EventCount);
++      else
++      {
++          PRINTF (uctx, DBG_TRAP, "fixup_eproc_trap: waitevent Event=%llx CountAndType=%llx\n", EventAddr, trap->tr_event.ev_CountAndType);
++          PRINTF (uctx, DBG_TRAP, "                  Param[0]=%llx Param[1]=%llx\n",
++                   trap->tr_event.ev_Params[0], trap->tr_event.ev_Params[1]);
++      }
++
++      if (! user_ddcq_check (uctx, waitevent ? 4 : 2))
++          return (0);
++      
++      if (waitevent)
++          user_ddcq_waitevent (uctx, EventAddr, trap->tr_event.ev_CountAndType, 
++                                trap->tr_event.ev_Params[0], trap->tr_event.ev_Params[1]);
++      else
++          user_ddcq_seteventn (uctx, EventAddr, EventCount);
++    }
++
++    return (1);
++}
++
++
++static int
++resolve_eproc_trap (USER_CTXT *uctx, ELAN4_USER_TRAP *utrapp, ELAN4_EPROC_TRAP *trap)
++{
++    switch (EPROC_TrapType (trap->tr_status))
++    {
++    case EventProcNoFault:
++      PRINTF (uctx, DBG_TRAP, "resolve_eproc_trap: EventProcNoFault\n");
++
++      return (UTS_FINISHED);
++      
++    case EventProcAddressAlignment:
++      return (deliver_trap (utrapp, UTS_ALIGNMENT_ERROR, UTS_EPROC, trap));
++
++    case EventProcMemoryFault:
++      PRINTF (uctx, DBG_TRAP, "resolve_eproc_trap: EventProcMemoryFault @ %llx\n", trap->tr_faultarea.FaultAddress);
++
++      if (user_pagefault (uctx, &trap->tr_faultarea) != 0)
++          return (deliver_trap (utrapp, UTS_INVALID_ADDR, UTS_EPROC, trap));
++
++      return (UTS_FINISHED);
++      
++    case EventProcCountWrapError:
++      return (deliver_trap (utrapp, UTS_BAD_TRAP, UTS_EPROC, trap));
++
++    default:
++      printk ("resolve_eproc_trap: bad trap type %d\n", EPROC_TrapType (trap->tr_status));
++      BUG();
++    }
++
++    return (UTS_FINISHED);
++}
++
++static int
++resolve_cproc_trap (USER_CTXT *uctx, ELAN4_USER_TRAP *utrapp, USER_CQ *ucq)
++{
++    ELAN4_DEV        *dev    = uctx->uctx_ctxt.ctxt_dev;
++    ELAN4_CPROC_TRAP *trap   = &ucq->ucq_trap;
++    E4_uint64         command;
++    int               res;
++    int               chan;
++
++    ELAN_LOCATION location;
++    int vp, node;
++
++    PRINTF2 (uctx, DBG_CPROC, "resolve_cproc_trap: cq %p is trapped - Status %lx\n", ucq, trap->tr_status);
++    
++    switch (CPROC_TrapType (trap->tr_status))
++    {
++    case CommandProcDmaQueueOverflow:
++      PRINTF (uctx, DBG_CPROC, "resolve_cproc_trap: CommandProcDmaQueueOverflow\n");
++      /*
++       * XXXX: should wait for the queue to become emptier if we're 
++       *       responsible for it being very full
++       */
++      ucq->ucq_state = UCQ_NEEDS_RESTART;
++      break;
++
++    case CommandProcInterruptQueueOverflow:
++      PRINTF (uctx, DBG_CPROC, "resolve_cproc_trap: CommandProcInterruptQueueOverflow\n");
++      /*
++       * XXXX: should wait for the queue to become emptier if we're
++       *       responsible for it being very full
++       */
++      ucq->ucq_state = UCQ_NEEDS_RESTART;
++      break;
++      
++    case CommandProcWaitTrap:
++      PRINTF0 (uctx, DBG_CPROC, "resolve_cproc_trap: CommandProcWaitTrap\n");
++      
++      if ((res = resolve_eproc_trap (uctx, utrapp, &trap->tr_eventtrap)) != UTS_FINISHED)
++      {
++          ucq->ucq_state = UCQ_STOPPED;
++
++          return (res);
++      }
++      
++      if (fixup_eproc_trap (uctx, &trap->tr_eventtrap, 1) == 0)
++          return UTS_RESCHEDULE;
++
++      ucq->ucq_state = UCQ_NEEDS_RESTART;
++      break;
++      
++    case CommandProcMemoryFault:
++      PRINTF1 (uctx, DBG_CPROC, "resolve_cproc_trap: CommandProcMemoryFault at %llx\n", trap->tr_faultarea.FaultAddress);
++      if (user_pagefault (uctx, &trap->tr_faultarea) != 0)
++      {
++          ucq->ucq_state = UCQ_STOPPED;
++
++          return (deliver_trap (utrapp, UTS_INVALID_ADDR, UTS_CPROC, trap, elan4_cq2idx(ucq->ucq_cq)));
++      }
++      
++      ucq->ucq_state = UCQ_NEEDS_RESTART;
++      break;
++      
++    case CommandProcRouteFetchFault:
++      command = elan4_trapped_open_command (dev, ucq->ucq_cq);
++      
++      PRINTF1 (uctx, DBG_CPROC, "resolve_cproc_trap: CommandProcRouteFetchFault to vp %d\n", (int) (command >> 32));
++      
++      if (user_resolvevp (uctx, (unsigned) (command >> 32)) != 0)
++      {
++          ucq->ucq_state = UCQ_STOPPED;
++
++          return (deliver_trap (utrapp, UTS_INVALID_VPROC, UTS_CPROC, trap, elan4_cq2idx(ucq->ucq_cq), (long) (command >> 32)));
++      }
++
++      ucq->ucq_state = UCQ_NEEDS_RESTART;
++      break;
++      
++    case CommandProcFailCountZero:
++      PRINTF0 (uctx, DBG_CPROC, "resolve_cproc_trap: CommandProcFailCountZero - reset failcount\n");
++      
++      /* Update CPROC timeout route statistics */
++      for (chan = 0; chan <= 1; chan++)
++      {
++          /* Was there a timeout on this channel ? */
++          if (PackValue(trap->tr_qdesc.CQ_AckBuffers, chan) == PackTimeout)
++          {
++              /* Find the last open command for that channel to extract the relevant vp */
++              if ((vp = cproc_open_extract_vp(uctx->uctx_ctxt.ctxt_dev, ucq->ucq_cq, chan)) != -1)
++              {
++                  E4_VirtualProcessEntry route;
++                  
++                  kmutex_lock (&uctx->uctx_vpseg_lock);
++                  location = user_process2location(uctx, NULL, vp);
++                  elan4_read_route (uctx->uctx_ctxt.ctxt_dev, uctx->uctx_routetable, vp, &route);
++                  kmutex_unlock (&uctx->uctx_vpseg_lock);
++                  node = location.loc_node;
++                  
++                  kmutex_lock(&uctx->uctx_ctxt.ctxt_dev->dev_lock);
++                  
++                  if ((node >= 0) && (node < uctx->uctx_ctxt.ctxt_dev->dev_position.pos_nodes))
++                  {
++                      uctx->uctx_ctxt.ctxt_dev->dev_cproc_timeout[node]++;
++                      
++                      elan4_ringbuf_store(&uctx->uctx_ctxt.ctxt_dev->dev_cproc_timeout_routes,
++                                          &route, uctx->uctx_ctxt.ctxt_dev);
++                  }
++                  
++                  kmutex_unlock(&uctx->uctx_ctxt.ctxt_dev->dev_lock);
++              }
++          }
++      }
++          
++      /* NOTE - we must not modify the ChannelNotCompleted bits - so modify */
++      /*        the restart count with a part-word store */
++      elan4_updatecq (dev, ucq->ucq_cq, ucq->ucq_cq->cq_perm, user_cproc_retry_count);
++
++      ucq->ucq_state = UCQ_NEEDS_RESTART;
++      break;
++
++    case CommandProcAddressAlignment:
++      ucq->ucq_state = UCQ_STOPPED;
++
++      return (deliver_trap (utrapp, UTS_ALIGNMENT_ERROR, UTS_CPROC, trap, elan4_cq2idx(ucq->ucq_cq)));
++
++    case CommandProcPermissionTrap:
++    {
++      sdramaddr_t cqdesc = dev->dev_cqaddr + (elan4_cq2num(ucq->ucq_cq) * sizeof (E4_CommandQueueDesc));
++      E4_uint64   control = elan4_sdram_readq (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_Control));
++
++      PRINTF (uctx, DBG_CPROC, "resolve_cproc_trap: CommandProcPermissionTrap - %s\n",
++              (control & CQ_PermissionMask) != ucq->ucq_cq->cq_perm ? "resume from stop" : "permission denied");
++      
++      if ((control & CQ_PermissionMask) == ucq->ucq_cq->cq_perm)
++          return (deliver_trap (utrapp, UTS_PERMISSION_DENIED, UTS_CPROC, trap, elan4_cq2idx(ucq->ucq_cq)));
++
++      elan4_updatecq (dev, ucq->ucq_cq, ucq->ucq_cq->cq_perm, 0);
++
++      ucq->ucq_state = UCQ_NEEDS_RESTART;
++      break;
++    }
++    
++    case CommandProcBadData:
++      ucq->ucq_state = UCQ_STOPPED;
++
++      return (deliver_trap (utrapp, UTS_INVALID_COMMAND, UTS_CPROC, trap, elan4_cq2idx(ucq->ucq_cq)));
++
++    default:
++      ucq->ucq_state = UCQ_STOPPED;
++
++      return (deliver_trap (utrapp, UTS_BAD_TRAP, UTS_CPROC, trap, elan4_cq2idx(ucq->ucq_cq)));
++    }
++
++    return (UTS_FINISHED);
++}
++
++static int
++resolve_dproc_trap (USER_CTXT *uctx, ELAN4_USER_TRAP *utrapp, ELAN4_DPROC_TRAP *trap)
++{
++    ELAN_LOCATION location;
++    int node;
++    E4_VirtualProcessEntry route;
++
++    if (DPROC_PrefetcherFault (trap->tr_status))
++    {
++      PRINTF (uctx, DBG_DPROC, "resolve_dproc_trap: PrefetcherFault at %llx\n", trap->tr_prefetchFault.FaultAddress);
++
++      if (user_pagefault (uctx, &trap->tr_prefetchFault) != 0)
++          return (deliver_trap (utrapp, UTS_INVALID_ADDR, UTS_DPROC, trap));
++      
++      return (queue_dma_for_retry (uctx, utrapp, &trap->tr_desc));
++    }
++    
++    switch (DPROC_TrapType (trap->tr_status))
++    {
++    case DmaProcRouteFetchFault:
++      PRINTF (uctx, DBG_DPROC, "resolve_dproc_trap: DmaProcRouteFetchFault vp %d\n", trap->tr_desc.dma_vproc);
++
++      if (user_resolvevp (uctx, trap->tr_desc.dma_vproc) != 0)
++          return (deliver_trap (utrapp, UTS_INVALID_VPROC, UTS_DPROC, trap, trap->tr_desc.dma_vproc));
++      
++      return (queue_dma_for_retry (uctx, utrapp, &trap->tr_desc /* immediate */));
++      
++    case DmaProcFailCountError:
++      PRINTF (uctx, DBG_DPROC, "resolve_dproc_trap: DmaProcFailCountError - vp %d cookie %llx\n",
++              trap->tr_desc.dma_vproc, trap->tr_desc.dma_cookie);
++
++      trap->tr_desc.dma_typeSize |= DMA_FailCount (user_dproc_retry_count);
++
++      return (queue_dma_for_retry (uctx, utrapp, &trap->tr_desc /* XXXX - backoff for some time later */));
++
++    case DmaProcPacketAckError:
++      PRINTF (uctx, DBG_DPROC, "resolve_dproc_trap: DmaProcPacketAckError - %d%s\n", DPROC_PacketAckValue (trap->tr_status), 
++              DPROC_PacketTimeout (trap->tr_status) ? " timeout" : "");
++
++      kmutex_lock (&uctx->uctx_vpseg_lock);
++      location = user_process2location(uctx, NULL, trap->tr_desc.dma_vproc);
++      elan4_read_route(uctx->uctx_ctxt.ctxt_dev, uctx->uctx_routetable, trap->tr_desc.dma_vproc, &route);
++      kmutex_unlock (&uctx->uctx_vpseg_lock);
++      node = location.loc_node;
++
++      /* Update dproc route timeout statistics */
++      if ((node >= 0) && (node < uctx->uctx_ctxt.ctxt_dev->dev_position.pos_nodes))
++      {
++          kmutex_lock(&uctx->uctx_ctxt.ctxt_dev->dev_lock);
++          
++          if ((route.Values[0] != 0) || (route.Values[1] != 0))
++          {
++              if (DPROC_PacketTimeout (trap->tr_status))
++              {
++                  uctx->uctx_ctxt.ctxt_dev->dev_dproc_timeout[node]++;
++                  elan4_ringbuf_store(&uctx->uctx_ctxt.ctxt_dev->dev_dproc_timeout_routes,
++                                      &route, uctx->uctx_ctxt.ctxt_dev);
++              }
++              else
++              {
++                  uctx->uctx_ctxt.ctxt_dev->dev_ack_errors[node]++;
++                  elan4_ringbuf_store(&uctx->uctx_ctxt.ctxt_dev->dev_ack_error_routes,
++                                      &route, uctx->uctx_ctxt.ctxt_dev);
++              }
++          }
++          
++          kmutex_unlock(&uctx->uctx_ctxt.ctxt_dev->dev_lock);
++      }
++
++      return (queue_dma_for_retry (uctx, utrapp, &trap->tr_desc /* XXXX - backoff for some time later */));
++
++    case DmaProcQueueOverflow:
++      PRINTF (uctx, DBG_DPROC, "resolve_dproc_trap: DmaProcQueueOverflow\n");
++      return (queue_dma_for_retry (uctx, utrapp, &trap->tr_desc /* XXXX - backoff for some time later */));
++      
++    case DmaProcRunQueueReadFault:
++      return (deliver_trap (utrapp, UTS_BAD_TRAP, UTS_DPROC, trap));
++      
++    default:
++      printk ("resolve_dproc_trap: unknown trap type : %d\n", DPROC_TrapType(trap->tr_status));
++      BUG();
++    }
++    return UTS_FINISHED;
++}
++
++int
++resolve_tproc_trap (USER_CTXT *uctx, ELAN4_USER_TRAP *utrapp, ELAN4_TPROC_TRAP *trap)
++{
++    PRINTF (uctx, DBG_TPROC, "resolve_tproc_trap: trap state = %lx\n", trap->tr_state);
++
++    if (trap->tr_state & TS_TrapForTooManyInstructions)
++      return (deliver_trap (utrapp, UTS_BAD_TRAP, UTS_TPROC, trap));
++    
++    if (trap->tr_state & TS_Unimplemented)
++      return (deliver_trap (utrapp, UTS_UNIMP_INSTR, UTS_TPROC, trap));
++    
++    if (trap->tr_state & TS_DataAlignmentError)
++      return (deliver_trap (utrapp, UTS_ALIGNMENT_ERROR, UTS_TPROC, trap));
++    
++    if ((trap->tr_state & TS_InstAccessException) && user_pagefault (uctx, &trap->tr_instFault) != 0)
++      return (deliver_trap (utrapp, UTS_INVALID_ADDR, UTS_TPROC, trap));
++    
++    if ((trap->tr_state & TS_DataAccessException) && user_pagefault (uctx, &trap->tr_dataFault) != 0)
++      return (deliver_trap (utrapp, UTS_INVALID_ADDR, UTS_TPROC, trap));
++    
++    /* If we're restarting from trap - then just need to re-issue it */
++    if (trap->tr_pc == uctx->uctx_trestart_addr || (trap->tr_state & TS_TrappedFlag))
++    {
++      PRINTF (uctx, DBG_TPROC, "resolve_tproc_trap: trapped in trap code PC=%llx SP=%llx\n", trap->tr_pc, trap->tr_regs[1]);
++
++      trap->tr_regs[0] = uctx->uctx_trestart_addr;
++    }
++    else
++    {
++      E4_uint64 *sp = (E4_uint64 *) user_elan2main (uctx, trap->tr_regs[1]);
++      int        i, reload;
++
++      /* need to store the register on the stack see */
++      /*  lib_tproc_trampoline_elan4_thread.S for stack layout */
++#define TS_STACK_OFF(REG)     ((((REG)&7)) - (((REG)>>3)*8) - 8)
++      for (reload = 0, i = 0; i < 64; i++)
++      {
++          if (trap->tr_dirty & ((E4_uint64) 1 << i))
++          {
++              PRINTF (uctx, DBG_TPROC, "resolve_tproc_trap: %%r%d  [%016llx] -> %p\n", i, trap->tr_regs[i], &sp[TS_STACK_OFF(i)]);
++
++              sulonglong ((u64 *) &sp[TS_STACK_OFF(i)], trap->tr_regs[i]);
++              
++              reload |= (1 << (i >> 3));
++          }
++      }
++#undef TS_STACK_OFF
++
++      PRINTF (uctx, DBG_TPROC, "resolve_tproc_trap: pc %llx npc %llx\n", trap->tr_pc, trap->tr_npc);
++      PRINTF (uctx, DBG_TPROC, "resolve_tproc_trap: CC %x reload %x\n", (int) (trap->tr_state >> TS_XCCshift), reload);
++
++      trap->tr_regs[0] = uctx->uctx_trestart_addr;
++      trap->tr_regs[2] = trap->tr_pc;
++      trap->tr_regs[3] = trap->tr_npc;
++      trap->tr_regs[4] = (trap->tr_state >> TS_XCCshift) & TS_XCCmask;
++      trap->tr_regs[5] = reload;
++    }
++
++    return (queue_thread_for_retry (uctx, utrapp, (E4_ThreadRegs *) trap->tr_regs));
++}
++
++static int
++resolve_iproc_trap (USER_CTXT *uctx, ELAN4_USER_TRAP *utrapp, int channel)
++{
++    USER_IPROC_TRAP  *utrap = &uctx->uctx_iprocTrap[channel];
++    ELAN4_IPROC_TRAP *trap  = &utrap->ut_trap;
++    unsigned long     flags;
++
++    elan4_inspect_iproc_trap (trap);
++
++    if (trap->tr_flags & TR_FLAG_TOOMANY_TRANS)
++      return (deliver_trap (utrapp, UTS_INVALID_TRANS, UTS_IPROC, trap, channel));
++
++    ASSERT (trap->tr_trappedTrans >= 0 && trap->tr_trappedTrans < trap->tr_numTransactions);
++
++    switch (IPROC_TrapValue (trap->tr_transactions[trap->tr_trappedTrans].IProcStatusCntxAndTrType))
++    {
++    case InputMemoryFault:
++      if (user_pagefault (uctx, &trap->tr_faultarea) != 0)
++      {
++          utrap->ut_state = UTS_IPROC_STOPPED;
++          
++          return (deliver_trap (utrapp, UTS_INVALID_ADDR, UTS_IPROC, trap, channel));
++      }
++      break;
++
++    case InputDmaQueueOverflow:
++    case InputEventEngineTrapped:
++      /* nothing to do for these 2 - restarting will simulate the transactions */
++      break;
++
++    case InputEopErrorOnWaitForEop:
++    case InputEopErrorTrap:
++      break;
++
++    case InputCrcErrorAfterPAckOk:
++      PRINTF (DBG_DEVICE, DBG_IPROC, "InputCrcErrorAfterPAckOk: flags %x\n", trap->tr_flags);
++
++      ASSERT ((trap->tr_flags & TR_FLAG_ACK_SENT) && ((trap->tr_flags & (TR_FLAG_DMA_PACKET|TR_FLAG_BAD_TRANS)) ||
++                                                      ((trap->tr_flags & TR_FLAG_EOP_ERROR) && trap->tr_identifyTrans == TR_TRANS_INVALID)));
++      break;
++
++    case InputDiscardAfterAckOk:
++      return (deliver_trap (utrapp, UTS_INVALID_TRANS, UTS_IPROC, trap, channel));
++
++    case InputAddressAlignment:
++      return (deliver_trap (utrapp, UTS_ALIGNMENT_ERROR, UTS_IPROC, trap, channel));
++
++    case InputInvalidTransType:
++      return (deliver_trap (utrapp, UTS_INVALID_TRANS, UTS_IPROC, trap, channel));
++
++    default:
++      printk ("resolve_iproc_trap: unknown trap type %d\n", IPROC_TrapValue (trap->tr_transactions[trap->tr_trappedTrans].IProcStatusCntxAndTrType));
++      BUG();
++      /* NOTREACHED */
++    }
++
++    if (! (trap->tr_flags & TR_FLAG_ACK_SENT) || (trap->tr_flags & TR_FLAG_EOP_BAD))
++    {
++      spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++
++      utrap->ut_state = UTS_IPROC_RUNNING;
++
++      user_stop_nacking (uctx, channel ? UCTX_IPROC_CH0_TRAPPED : UCTX_IPROC_CH1_TRAPPED);
++      
++      spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++    }
++    else if ((trap->tr_flags & (TR_FLAG_DMA_PACKET | TR_FLAG_BAD_TRANS)) || ((trap->tr_flags & TR_FLAG_EOP_ERROR) && (trap->tr_identifyTrans == TR_TRANS_INVALID)))
++    {
++      /* 
++       * TR_FLAG_DMA_PACKET   means a DMA packet has faulted.
++       *
++       * TR_FLAG_BAD_TRANS    means we have a transaction with a bad CRC after the transaction
++       *                      which sent the ack - this implies it's an overlapped ack DMA packet
++       *
++       * TR_FLAG_EOP_ERROR    means we've received an EOP reset - if we hadn't seen an identify
++       *                      transaction then it's a DMA packet.
++       *
++       * To ensure that the DMA processor works correctly the next packet must be NACKed to 
++       * cause it to resend this one.
++       */
++      PRINTF (uctx, DBG_IPROC, "resolve_iproc_trap: %s during DMA packet\n",
++              (trap->tr_flags & TR_FLAG_BAD_TRANS) ? "BadTransaction" : (trap->tr_flags & TR_FLAG_EOP_ERROR) ? "EopError" : "trap");
++
++      spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++
++      if (trap->tr_flags & TR_FLAG_DMA_PACKET)
++      {
++          if (! (trap->tr_flags & TR_FLAG_BAD_TRANS))
++              utrap->ut_state = UTS_IPROC_EXECUTE_PACKET;
++          else
++          {
++              kcondvar_t waithere;
++
++              /* We must ensure that the next packet is always nacked, so
++               * we wait here for an output timeout before dropping the 
++               * context filter - we just pause here for 4 mS */
++              kcondvar_init (&waithere);
++              kcondvar_timedwait (&waithere, &uctx->uctx_spinlock, &flags, lbolt + (HZ/250) + 1);;
++              kcondvar_destroy (&waithere);
++
++              utrap->ut_state = UTS_IPROC_RUNNING;
++              
++              user_stop_nacking (uctx, channel ? UCTX_IPROC_CH0_TRAPPED : UCTX_IPROC_CH1_TRAPPED);
++          }
++      }
++      else
++      {
++          utrap->ut_state = UTS_IPROC_RUNNING;
++
++          user_stop_nacking (uctx, channel ? UCTX_IPROC_CH0_TRAPPED : UCTX_IPROC_CH1_TRAPPED);
++      }
++
++      spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++    }
++    else if (trap->tr_flags & TR_FLAG_EOP_ERROR)
++    {
++      PRINTF (uctx, DBG_IPROC, "resolve_iproc_trap: EopError with identify\n");
++
++      utrap->ut_state = UTS_IPROC_NETWORK_ERROR;
++    }
++    else
++    {
++      PRINTF (uctx, DBG_IPROC, "resolve_iproc_trap: execute packet\n");
++
++      utrap->ut_state = UTS_IPROC_EXECUTE_PACKET;
++    }
++
++    return UTS_FINISHED;
++}
++
++
++static int
++resolve_cproc_traps (USER_CTXT *uctx, ELAN4_USER_TRAP *utrapp)
++{
++    struct list_head *entry;
++    int res = UTS_FINISHED;
++
++    kmutex_lock (&uctx->uctx_cqlock);
++    list_for_each (entry, &uctx->uctx_cqlist) {
++      USER_CQ *ucq = list_entry (entry, USER_CQ, ucq_link);
++
++      if (ucq->ucq_state == UCQ_TRAPPED)
++      {
++          res = resolve_cproc_trap (uctx, utrapp, ucq);
++
++          if (res != UTS_FINISHED)
++              break;
++      }
++
++      if (ucq->ucq_errored)
++      {
++          ucq->ucq_errored = 0;
++          res = deliver_trap (utrapp, UTS_CPROC_ERROR, UTS_CPROC, &ucq->ucq_trap, elan4_cq2idx(ucq->ucq_cq));
++          break;
++      }
++    }
++    kmutex_unlock (&uctx->uctx_cqlock);
++
++    return (res);
++}
++
++static int
++resolve_eproc_traps (USER_CTXT *uctx, ELAN4_USER_TRAP *utrapp)
++{
++    unsigned long flags;
++    int res;
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    while (! RING_QUEUE_EMPTY (uctx->uctx_eprocTrapQ))
++    {
++      ELAN4_EPROC_TRAP trap = *RING_QUEUE_FRONT (uctx->uctx_eprocTrapQ, uctx->uctx_eprocTraps);
++
++      (void) RING_QUEUE_REMOVE (uctx->uctx_eprocTrapQ);
++
++      spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++
++      if ((res = resolve_eproc_trap (uctx, utrapp, &trap)) != UTS_FINISHED)
++          return (res);
++
++      if (fixup_eproc_trap (uctx, &trap, 0) == 0)
++      {
++          PRINTF (uctx, DBG_EPROC, "resolve_eproc_trap: could not fixup eproc trap - requeue it\n");
++
++          spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++          if (RING_QUEUE_REALLY_FULL(uctx->uctx_eprocTrapQ))
++              uctx->uctx_status |= UCTX_EPROC_QUEUE_OVERFLOW;
++          else
++          {
++              *RING_QUEUE_FRONT(uctx->uctx_eprocTrapQ, uctx->uctx_eprocTraps) = trap;
++          
++              (void) RING_QUEUE_ADD_FRONT(uctx->uctx_eprocTrapQ);
++          }
++          spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++
++          return UTS_RESCHEDULE;
++      }
++      
++      spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    }
++
++    if (uctx->uctx_status & UCTX_EPROC_QUEUE_FULL)
++      user_stop_stopping (uctx, UCTX_EPROC_QUEUE_FULL);
++
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++    return (UTS_FINISHED);
++}
++          
++static int
++resolve_dproc_traps (USER_CTXT *uctx, ELAN4_USER_TRAP *utrapp)
++{
++    unsigned long flags;
++    int res;
++    
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    while (! RING_QUEUE_EMPTY (uctx->uctx_dprocTrapQ))
++    {
++      ELAN4_DPROC_TRAP trap = *RING_QUEUE_FRONT(uctx->uctx_dprocTrapQ, uctx->uctx_dprocTraps);
++      
++      (void) RING_QUEUE_REMOVE (uctx->uctx_dprocTrapQ);
++
++      spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++
++      if ((res = resolve_dproc_trap (uctx, utrapp, &trap)) != UTS_FINISHED)
++          return (res);
++      
++      spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    }
++
++    if (uctx->uctx_status & UCTX_DPROC_QUEUE_FULL)
++      user_stop_stopping (uctx, UCTX_DPROC_QUEUE_FULL);
++    
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++    return (UTS_FINISHED);
++}
++
++static int
++resolve_tproc_traps (USER_CTXT *uctx, ELAN4_USER_TRAP *utrapp)
++{
++    unsigned long flags;
++    int res;
++    
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    while (! RING_QUEUE_EMPTY (uctx->uctx_tprocTrapQ))
++    {
++      ELAN4_TPROC_TRAP trap = *RING_QUEUE_FRONT(uctx->uctx_tprocTrapQ, uctx->uctx_tprocTraps);
++      
++      (void) RING_QUEUE_REMOVE (uctx->uctx_tprocTrapQ);
++
++      spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++
++      if ((res = resolve_tproc_trap (uctx, utrapp, &trap)) != UTS_FINISHED)
++          return (res);
++      
++      spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    }
++
++    if (uctx->uctx_status & UCTX_TPROC_QUEUE_FULL)
++      user_stop_stopping (uctx, UCTX_TPROC_QUEUE_FULL);
++
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++    return (UTS_FINISHED);
++}
++
++static int
++resolve_iproc_traps (USER_CTXT *uctx, ELAN4_USER_TRAP *utrapp)
++{
++    unsigned long flags;
++    int i, res;
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    for (i = 0; i < 2; i++)
++      if (uctx->uctx_iprocTrap[i].ut_state == UTS_IPROC_TRAPPED)
++      {
++          uctx->uctx_iprocTrap[i].ut_state = UTS_IPROC_RESOLVING;
++          spin_unlock_irqrestore(&uctx->uctx_spinlock, flags);
++          
++          if ((res = resolve_iproc_trap (uctx, utrapp, i)) != UTS_FINISHED)
++              return (res);
++          
++          spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++      }
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++    
++    return (UTS_FINISHED);
++}
++
++static int
++resolve_all_traps (USER_CTXT *uctx, ELAN4_USER_TRAP *utrapp)
++{
++    int res;
++
++    if ((res = resolve_iproc_traps (uctx, utrapp)) != UTS_FINISHED ||
++      (res = resolve_cproc_traps (uctx, utrapp)) != UTS_FINISHED ||
++      (res = resolve_eproc_traps (uctx, utrapp)) != UTS_FINISHED ||
++      (res = resolve_dproc_traps (uctx, utrapp)) != UTS_FINISHED ||
++      (res = resolve_tproc_traps (uctx, utrapp)) != UTS_FINISHED)
++      return (res);
++
++    if (uctx->uctx_status & UCTX_OVERFLOW_REASONS)
++      return (deliver_trap (utrapp, UTS_QUEUE_OVERFLOW, UTS_NOPROC, NULL, uctx->uctx_status));
++
++    if (uctx->uctx_status & UCTX_ERROR_REASONS)
++      return (deliver_trap (utrapp, UTS_QUEUE_ERROR, UTS_NOPROC, NULL, uctx->uctx_status));
++
++    return (UTS_FINISHED);
++}
++
++static int
++execute_iproc_traps (USER_CTXT *uctx, ELAN4_USER_TRAP *utrapp)
++{
++    unsigned long flags;
++    int i;
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    for (i = 0; i < 2; i++)
++      switch (uctx->uctx_iprocTrap[i].ut_state)
++      {
++      case UTS_IPROC_EXECUTE_PACKET:
++          uctx->uctx_iprocTrap[i].ut_state = UTS_IPROC_EXECUTING;
++          spin_unlock_irqrestore(&uctx->uctx_spinlock, flags);
++          
++          return (deliver_trap (utrapp, UTS_EXECUTE_PACKET, UTS_IPROC, &uctx->uctx_iprocTrap[i].ut_trap, i));
++
++      case UTS_IPROC_NETWORK_ERROR:
++          spin_unlock_irqrestore(&uctx->uctx_spinlock, flags);
++          
++          return (deliver_trap (utrapp, UTS_NETWORK_ERROR_TRAP, UTS_IPROC, &uctx->uctx_iprocTrap[i].ut_trap, i));
++      }
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++    
++    return (UTS_FINISHED);
++}
++
++static int
++progress_neterr (USER_CTXT *uctx, ELAN4_USER_TRAP *utrapp)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    if (! RING_QUEUE_EMPTY (uctx->uctx_msgQ))
++    {
++      ELAN4_NETERR_MSG msg = *RING_QUEUE_FRONT (uctx->uctx_msgQ, uctx->uctx_msgs);
++      
++      (void) RING_QUEUE_REMOVE (uctx->uctx_msgQ);
++      spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++      
++      return deliver_trap (utrapp, UTS_NETWORK_ERROR_MSG, UTS_NETERR_MSG, &msg, user_location2process (uctx, msg.msg_sender));
++    }
++    
++    if (uctx->uctx_status & UCTX_NETERR_TIMER)
++    {
++      uctx->uctx_status &= ~UCTX_NETERR_TIMER;
++
++      spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++      
++      return deliver_trap (utrapp, UTS_NETWORK_ERROR_TIMER, UTS_NOPROC, NULL);
++    }
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++    
++    return (UTS_FINISHED);
++}
++
++static void
++restart_command_queues (USER_CTXT *uctx)
++{
++    struct list_head *entry;
++
++    ASSERT (SPINLOCK_HELD (&uctx->uctx_spinlock));
++    
++    list_for_each (entry, &uctx->uctx_cqlist) {
++      USER_CQ *ucq = list_entry (entry, USER_CQ, ucq_link);
++      
++      if (ucq->ucq_state == UCQ_NEEDS_RESTART)
++      {
++          ucq->ucq_state = UCQ_RUNNING;
++          
++          elan4_restartcq (uctx->uctx_ctxt.ctxt_dev, ucq->ucq_cq);
++      }
++    }
++}
++
++static int
++restart_dmas (USER_CTXT *uctx)
++{
++    PRINTF (uctx, DBG_TRAP, "restart_dmas: back=%d front=%d\n", uctx->uctx_dmaQ.q_back, uctx->uctx_dmaQ.q_front);
++
++    while (! RING_QUEUE_EMPTY (uctx->uctx_dmaQ))
++    {
++      if (! user_ddcq_check (uctx, 7))
++          return (0);
++
++      user_ddcq_run_dma (uctx, RING_QUEUE_FRONT(uctx->uctx_dmaQ, uctx->uctx_dmas));
++      
++      (void) RING_QUEUE_REMOVE (uctx->uctx_dmaQ);
++    }
++
++    return (1);
++}
++
++static int
++restart_threads (USER_CTXT *uctx)
++{
++    PRINTF (uctx, DBG_TRAP, "restart_threads: back=%d front=%d\n", uctx->uctx_threadQ.q_back, uctx->uctx_threadQ.q_front);
++
++    while (! RING_QUEUE_EMPTY (uctx->uctx_threadQ))
++    {
++      if (! user_ddcq_check (uctx, 7))
++          return (0);
++
++      user_ddcq_run_thread (uctx, RING_QUEUE_FRONT(uctx->uctx_threadQ, uctx->uctx_threads));
++      
++      (void) RING_QUEUE_REMOVE (uctx->uctx_threadQ);
++    }
++
++    return (1);
++}
++
++int
++user_resume_eproc_trap (USER_CTXT *uctx, E4_Addr addr)
++{
++    PRINTF2 (uctx, DBG_RESUME, "user_resume_eproc_trap: addr=%llx -> %s\n", addr, user_ddcq_check(uctx, 2) ? "success" : "EAGAIN");
++
++    if (! user_ddcq_check (uctx, 2))
++      return (-EAGAIN);
++
++    user_ddcq_setevent (uctx, addr);
++
++    return (0);
++}
++
++int
++user_resume_cproc_trap (USER_CTXT *uctx, unsigned indx)
++{
++    struct list_head *entry;
++    unsigned long flags;
++
++    PRINTF1 (uctx, DBG_RESUME, "user_resume_cproc_trap: indx=%d\n", indx);
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++
++    list_for_each (entry, &uctx->uctx_cqlist) {
++      USER_CQ *ucq = list_entry (entry, USER_CQ, ucq_link);
++      
++      if (elan4_cq2idx(ucq->ucq_cq) == indx && ucq->ucq_state == UCQ_STOPPED && !(ucq->ucq_flags & UCQ_SYSTEM))
++      {
++          ucq->ucq_state = UCQ_NEEDS_RESTART;
++          
++          user_signal_trap (uctx);
++
++          spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++          return (0);
++      }
++    }
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++
++    return (-EINVAL);
++}
++
++int
++user_resume_dproc_trap (USER_CTXT *uctx, E4_DMA *dma)
++{
++    unsigned long flags;
++    int res = 0;
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    if (RING_QUEUE_FULL (uctx->uctx_dmaQ))
++      res = -ENOMEM;
++    else
++    {
++      *RING_QUEUE_BACK (uctx->uctx_dmaQ, uctx->uctx_dmas) = *dma;
++      (void) RING_QUEUE_ADD (uctx->uctx_dmaQ);
++
++      user_signal_trap (uctx);
++    }
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++    return (res);
++}
++
++int
++user_resume_tproc_trap (USER_CTXT *uctx, E4_ThreadRegs *regs)
++{
++    unsigned long flags;
++    int res = 0;
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    if (RING_QUEUE_FULL (uctx->uctx_threadQ))
++      res = -ENOMEM;
++    else
++    {
++      *RING_QUEUE_BACK (uctx->uctx_threadQ, uctx->uctx_threads) = *regs;
++      (void) RING_QUEUE_ADD (uctx->uctx_threadQ);
++
++      user_signal_trap (uctx);
++    }
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++    return (res);
++}
++
++int
++user_resume_iproc_trap (USER_CTXT *uctx, unsigned channel, unsigned trans,
++                      E4_IprocTrapHeader *hdrp, E4_IprocTrapData *datap)
++{
++    unsigned long flags;
++    int res = 0;
++
++    if (channel >= 2)
++      return (-EINVAL);
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    if (uctx->uctx_iprocTrap[channel].ut_state != UTS_IPROC_STOPPED &&
++      uctx->uctx_iprocTrap[channel].ut_state != UTS_IPROC_EXECUTING &&
++      uctx->uctx_iprocTrap[channel].ut_state != UTS_IPROC_NETWORK_ERROR)
++      res = -EINVAL;
++    else
++    {
++      ELAN4_IPROC_TRAP *trap = &uctx->uctx_iprocTrap[channel].ut_trap;
++
++      if (trans < trap->tr_numTransactions)
++      {
++          PRINTF1 (uctx, DBG_RESUME, "user_resume_iproc_trap: trans=%d -> execute\n", trans);
++
++          uctx->uctx_iprocTrap[channel].ut_state = UTS_IPROC_EXECUTE_PACKET;
++          trap->tr_trappedTrans                  = trans;
++          trap->tr_transactions[trans]           = *hdrp;
++          trap->tr_dataBuffers[trans]            = *datap;
++      }
++      else
++      {
++          PRINTF1 (uctx, DBG_RESUME, "user_resume_iproc_trap: trans=%d -> running\n", trans);
++
++          uctx->uctx_iprocTrap[channel].ut_state = UTS_IPROC_RUNNING;
++      
++          user_stop_nacking (uctx, channel ? UCTX_IPROC_CH0_TRAPPED : UCTX_IPROC_CH1_TRAPPED);
++      }
++    }
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++    
++    return (res);
++}
++
++int
++__categorise_command (E4_uint64 command, int *cmdSize)
++{
++    switch (command & 0x3)
++    {
++    case RUN_THREAD_CMD: *cmdSize = 7; break;
++      
++    default:
++      switch (command & 0x7)
++      {
++      case WRITE_DWORD_CMD: *cmdSize = 2; break;
++      case ADD_DWORD_CMD:   *cmdSize = 2; break;
++          
++      default:
++          switch (command & 0xF)
++          {
++          case OPEN_STEN_PKT_CMD:
++              *cmdSize = 1;
++              return 1;
++              
++          case COPY64_CMD:    *cmdSize = 2; break;
++          case GUARD_CMD:     *cmdSize = 1; break;
++          case INTERRUPT_CMD: *cmdSize = 1; break;
++          case RUN_DMA_CMD:   *cmdSize = 7; break;
++              
++          default:
++              switch (command & 0x1f)
++              {
++              case SEND_TRANS_CMD:
++                  *cmdSize = 2 + (((command >> 16) & TR_SIZE_MASK) >> TR_SIZE_SHIFT);
++                  return 2;
++                  
++              case SET_EVENT_CMD:    *cmdSize = 1; break;
++              case SET_EVENTN_CMD:   *cmdSize = 2; break;
++              case WAIT_EVENT_CMD:   *cmdSize = 4; break;
++
++              default:
++                  switch (command & 0x3f)
++                  {
++                  case NOP_CMD:            *cmdSize = 1; break;
++                  case MAKE_EXT_CLEAN_CMD: *cmdSize = 1; break;
++                  default:
++                      return 3;
++                  }
++                  break;
++              }
++          }
++      }
++    }
++
++    return 0;
++}
++
++int
++__whole_command (sdramaddr_t *commandPtr, sdramaddr_t insertPtr, unsigned int cqSize, unsigned int cmdSize)
++{
++    /* Move onto next command */
++    while (cmdSize-- && (*commandPtr) != insertPtr)
++      *commandPtr = ((*commandPtr) & ~(cqSize-1)) | (((*commandPtr) + sizeof (E4_uint64)) & (cqSize-1));
++
++    return cmdSize == -1;
++}
++
++int
++user_neterr_sten (USER_CTXT *uctx, unsigned int vp, E4_uint64 cookie, int waitforeop)
++{
++    ELAN4_DEV        *dev   = uctx->uctx_ctxt.ctxt_dev;
++    int                     found = 0;
++    struct list_head *el;
++
++    user_swapout (uctx, UCTX_NETERR_FIXUP);
++
++    kmutex_lock (&uctx->uctx_cqlock);
++    list_for_each (el, &uctx->uctx_cqlist) {
++      USER_CQ *ucq = list_entry (el, USER_CQ, ucq_link);
++      
++      if ((ucq->ucq_cq->cq_perm & CQ_STENEnableBit) != 0)
++      {
++          sdramaddr_t   cqdesc       = dev->dev_cqaddr + (elan4_cq2num(ucq->ucq_cq) * sizeof (E4_CommandQueueDesc));
++          E4_uint64     queuePtrs    = elan4_sdram_readq (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_QueuePtrs));
++          sdramaddr_t   insertPtr    = (queuePtrs & CQ_PtrMask);
++          sdramaddr_t   commandPtr   = CQ_CompletedPtr (queuePtrs);
++          unsigned int  cqSize       = CQ_Size ((queuePtrs >> CQ_SizeShift) & CQ_SizeMask);
++          E4_uint64     openCommand  = 0;
++
++          if (dev->dev_devinfo.dev_revision_id != PCI_REVISION_ID_ELAN4_REVA && (queuePtrs & CQ_RevB_ReorderingQueue))
++          {
++              E4_uint32 oooMask = elan4_sdram_readl (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_HoldingValue));
++              
++              for (; (oooMask & 1) != 0; oooMask >>= 1)
++                  insertPtr = (insertPtr & ~(cqSize-1)) | ((insertPtr + sizeof (E4_uint64)) & (cqSize-1));
++          }
++
++          while (commandPtr != insertPtr)
++          {
++              E4_uint64    command = elan4_sdram_readq (dev, commandPtr);
++              sdramaddr_t  identifyPtr;
++              unsigned int cmdSize;
++              
++              switch (__categorise_command (command, &cmdSize))
++              {
++              case 0:
++                  (void) __whole_command (&commandPtr, insertPtr, cqSize, cmdSize);
++                  break;
++                  
++              case 1:
++                  PRINTF (uctx, DBG_NETERR, "user_neterr_sten: cq=%d OPEN %llx\n", elan4_cq2num (ucq->ucq_cq), command);
++                  
++                  if ((command >> 32) == vp)
++                      openCommand = command;
++
++                  (void) __whole_command (&commandPtr, insertPtr, cqSize, cmdSize);
++                  break;
++                  
++              case 2:
++                  PRINTF (uctx, DBG_NETERR, "user_neterr_sten: cq=%d SENDTRANS %llx\n", elan4_cq2num (ucq->ucq_cq), command);
++                  
++                  if (openCommand == 0)
++                      (void) __whole_command (&commandPtr, insertPtr, cqSize, cmdSize);
++                  else
++                  {
++                      switch ((command >> 16) & (TR_OPCODE_MASK | TR_SIZE_MASK))
++                      {
++                      case TR_IDENTIFY  & (TR_OPCODE_MASK | TR_SIZE_MASK):
++                      case TR_REMOTEDMA & (TR_OPCODE_MASK | TR_SIZE_MASK):
++                          PRINTF (uctx, DBG_NETERR, "user_neterr_sten: TR_IDENTIFY/TR_REMOTEDMA\n");
++                          identifyPtr = (commandPtr & ~(cqSize-1)) | ((commandPtr + sizeof (E4_uint64)) & (cqSize-1));
++                          break;
++                          
++                      case TR_SETEVENT_IDENTIFY & (TR_OPCODE_MASK | TR_SIZE_MASK):
++                      case TR_INPUT_Q_COMMIT    & (TR_OPCODE_MASK | TR_SIZE_MASK):
++                          PRINTF (uctx, DBG_NETERR, "user_neterr_sten: TR_SETEVENT_IDENTIFY/TR_INPUT_Q_COMMIT\n");
++                          identifyPtr = (commandPtr & ~(cqSize-1)) | ((commandPtr + 2*sizeof (E4_uint64)) & (cqSize-1));
++                          break;
++                          
++                      case TR_ADDWORD & (TR_OPCODE_MASK | TR_SIZE_MASK):
++                          PRINTF (uctx, DBG_NETERR, "user_neterr_sten: TR_ADDWORD\n");
++                          identifyPtr = (commandPtr & ~(cqSize-1)) | ((commandPtr + 3*sizeof (E4_uint64)) & (cqSize-1));
++                          break;
++                          
++                      case TR_TESTANDWRITE & (TR_OPCODE_MASK | TR_SIZE_MASK):
++                          PRINTF (uctx, DBG_NETERR, "user_neterr_sten: TR_TESTANDWRITE\n");
++                          identifyPtr = (commandPtr & ~(cqSize-1)) | ((commandPtr + 4*sizeof (E4_uint64)) & (cqSize-1));
++                          break;
++                          
++                      default:
++                          identifyPtr = 0;
++                      }
++                      
++                      if (! __whole_command (&commandPtr, insertPtr, cqSize, cmdSize))
++                      {
++                          PRINTF (uctx, DBG_NETERR, "user_neterr_sten: not whole command\n");
++                          openCommand = 0;
++                      }
++
++                      else if (identifyPtr)
++                      {
++                          E4_uint64 tcookie = elan4_sdram_readq (dev, identifyPtr);
++                          
++                          PRINTF (uctx, DBG_NETERR, "user_neterr_sten: cookie=%llx [%llx]\n", tcookie, cookie);
++                          
++                          if (tcookie == cookie)
++                          {
++                              unsigned int vchan = (openCommand >> 4) & 0x1f;
++                              
++                              PRINTF (uctx, DBG_NETERR, "user_neterr_sten: cookie matches - vchan=%d\n", vchan);
++                              
++                              if (! waitforeop)
++                              {
++                                  /* Alter the CQ_AckBuffer for this channel to indicate an
++                                   * ack was received */
++                                  E4_uint64 value  = elan4_sdram_readq (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_AckBuffers));
++                                  E4_uint64 nvalue = ((value & ~((E4_uint64)0xf << ((vchan & 0xf) << 2))) |
++                                                      ((E4_uint64) PackOk << ((vchan & 0xf) << 2)));
++                                  
++                                  PRINTF (uctx, DBG_NETERR, "user_neterr_sten: CQ_AckBuffers %llx -> %llx\n", value, nvalue);
++                                  
++                                  elan4_sdram_writeq (dev, cqdesc + offsetof (E4_CommandQueueDesc, CQ_AckBuffers), nvalue);
++                                  pioflush_sdram (dev);
++                              }
++                              
++                              found++;
++                          }
++                          openCommand = 0;
++                      }
++                      
++                      if ((command >> 16) & TR_LAST_AND_SEND_ACK)
++                          openCommand = 0;
++                  }
++                  break;
++                  
++              case 3:
++                  PRINTF (uctx, DBG_NETERR, "user_neterr_sten: invalid command %llx\n", command);
++                  kmutex_unlock (&uctx->uctx_cqlock);
++                  return -EINVAL;
++              }
++              
++          }
++      }
++    }
++    kmutex_unlock (&uctx->uctx_cqlock);
++
++    user_swapin (uctx, UCTX_NETERR_FIXUP);
++
++    return found;
++}
++
++int
++user_neterr_dma (USER_CTXT *uctx, unsigned int vp, E4_uint64 cookie, int waitforeop)
++{
++    unsigned long flags;
++    int found = 0;
++    int idx;
++
++    user_swapout (uctx, UCTX_NETERR_FIXUP);
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    RING_QUEUE_ITERATE (uctx->uctx_dmaQ, idx) {
++      E4_DMA *dma = &uctx->uctx_dmas[idx];
++
++      if (dma->dma_vproc == vp && dma->dma_cookie == cookie)
++      {
++          PRINTF (uctx, DBG_NETERR, "user_neterr_dma: dmaQ matches %s\n", waitforeop ? "waitforeop" : "remove remoteness");
++
++          if (! waitforeop) 
++          {
++              dma->dma_dstEvent = 0;
++              dma->dma_typeSize = DMA_ShMemWrite | DMA_Context (dma->dma_typeSize);
++          }
++          found++;
++      }
++    }
++
++    RING_QUEUE_ITERATE (uctx->uctx_dprocTrapQ, idx) {
++      ELAN4_DPROC_TRAP *trap = &uctx->uctx_dprocTraps[idx];
++
++      if (trap->tr_desc.dma_vproc == vp && trap->tr_desc.dma_cookie == cookie)
++      {
++          PRINTF (uctx, DBG_NETERR, "user_neterr_dma: dmaTrapQ matches %s\n", waitforeop ? "waitforeop" : "remove remoteness");
++
++          if (! waitforeop) 
++          {
++              trap->tr_desc.dma_dstEvent = 0;
++              trap->tr_desc.dma_typeSize = DMA_ShMemWrite | DMA_Context (trap->tr_desc.dma_typeSize);
++          }
++          found++;
++      }
++    }
++
++    /* The device driver command queue should be empty at this point ! */
++    if (user_ddcq_flush (uctx) == 0)
++      found = -EAGAIN;
++
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++
++    /* The device driver command queue should be empty at this point ! */
++    if (user_ddcq_flush (uctx) == 0)
++      found = -EAGAIN;
++    
++    user_swapin (uctx, UCTX_NETERR_FIXUP);
++
++    return found;
++}
++
++int
++user_trap_handler (USER_CTXT *uctx, ELAN4_USER_TRAP *utrapp, int nticks)
++{
++    unsigned long entered = jiffies;
++    unsigned int  need_reenter = 0;
++    unsigned long flags;
++    int           res;
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++
++    PRINTF1 (uctx, DBG_TRAP, "user_trap_handler: entered state=%d\n", uctx->uctx_trap_state);
++
++    uctx->uctx_trap_count++;
++    
++    for (;;)
++    {
++      if (uctx->uctx_status & UCTX_SWAPPED_REASONS)
++      {
++          PRINTF0 (uctx, DBG_TRAP, "user_trap_handler: exiting on swapped reasons\n");
++          
++          res = UTS_FINISHED;
++          goto no_more_to_do;
++      }
++
++      if ((long) (jiffies - entered) > HZ)
++      {
++          PRINTF0 (uctx, DBG_TRAP, "user_trap_handler: exiting for reschedule\n");
++          res = UTS_RESCHEDULE;
++          goto no_more_to_do;
++      }
++      
++      switch (uctx->uctx_trap_state)
++      {
++      case UCTX_TRAP_ACTIVE:
++          uctx->uctx_trap_state = UCTX_TRAP_SLEEPING;
++          
++          if (nticks == 0 || need_reenter || kcondvar_timedwaitsig (&uctx->uctx_wait, &uctx->uctx_spinlock, &flags, lbolt + nticks) != CV_RET_NORMAL)
++          {
++              PRINTF0 (uctx, DBG_TRAP, "user_trap_handler: exiting by kcondvar_timedwaitsig\n");
++
++              res = UTS_FINISHED;
++              goto no_more_to_do;
++          }
++
++          /* Have slept above, so resample entered */
++          entered = jiffies;
++          
++          uctx->uctx_trap_state = UCTX_TRAP_SIGNALLED;
++          continue;
++
++      case UCTX_TRAP_IDLE:
++      case UCTX_TRAP_SIGNALLED:
++          uctx->uctx_trap_state = UCTX_TRAP_ACTIVE;
++          break;
++      }
++      spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++
++      PRINTF2 (uctx, DBG_TRAP, "user_trap_handler: resolve traps - state=%d status=%x\n", uctx->uctx_trap_state, uctx->uctx_status);
++
++      switch ((res = resolve_all_traps (uctx, utrapp)))
++      {
++      case UTS_FINISHED:
++          break;
++          
++      case UTS_RESCHEDULE:
++          need_reenter++;
++          break;
++
++      default:
++          spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++          goto no_more_to_do;
++      }
++
++      spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++      if (! user_ddcq_flush (uctx))
++      {
++          PRINTF0 (uctx, DBG_TRAP, "user_trap_handler: ddcq not flushed - re-enter\n");
++          need_reenter++;
++          continue;
++      }
++      spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++
++      if ((res = progress_neterr (uctx, utrapp)) != UTS_FINISHED)
++      {
++          spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++          goto no_more_to_do;
++      }
++
++      if ((res = execute_iproc_traps (uctx, utrapp)) != UTS_FINISHED)
++      {
++          spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++          goto no_more_to_do;
++      }
++
++      PRINTF2 (uctx, DBG_TRAP, "user_trap_handler: restart items - state=%d status=%x\n", uctx->uctx_trap_state, uctx->uctx_status);
++
++      spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++      if (UCTX_RUNNABLE (uctx))
++      {
++          restart_command_queues (uctx);
++
++          if (! restart_threads (uctx) || ! restart_dmas (uctx))
++          {
++              PRINTF0 (uctx, DBG_TRAP, "user_trap_handler: ddcq full - re-enter\n");
++              need_reenter++;
++          }
++      }
++    }
++ no_more_to_do:
++    uctx->uctx_trap_state = UCTX_TRAP_IDLE;
++
++    /*
++     * Always ensure that the command queue is flushed with a flow control
++     * write, so that on the next trap we (hopefully) find it empty and so
++     * can immediately restart the context.   Also if we need to be re-enter
++     * the trap handler and don't have an interrupt outstanding, then issue
++     * one now.
++     */
++    user_ddcq_flush (uctx);
++    if (need_reenter && uctx->uctx_ddcq_intr == 0)
++    {
++      uctx->uctx_ddcq_intr++;
++      user_ddcq_intr (uctx);
++    }
++
++    if (--uctx->uctx_trap_count == 0 && (uctx->uctx_status & UCTX_SWAPPING))
++      kcondvar_wakeupall (&uctx->uctx_wait, &uctx->uctx_spinlock);
++
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++
++    PRINTF2 (uctx, DBG_TRAP, "user_trap_handler: finished state=%d res=%d\n", uctx->uctx_trap_state, res);
++
++    return (res == UTS_EFAULT ? -EFAULT : 0);
++}
++
++USER_CQ *
++user_alloccq (USER_CTXT *uctx, unsigned cqsize, unsigned perm, unsigned uflags)
++{
++    USER_CQ      *ucq;
++    unsigned long flags;
++
++    KMEM_ZALLOC (ucq, USER_CQ *, sizeof (USER_CQ), 1);
++
++    if (ucq == (USER_CQ *) NULL)
++      return ERR_PTR(-ENOMEM);
++    
++    /* NOTE - do not allow the user to create high-priority queues as we only flush through the low-priority run queues */
++    if ((ucq->ucq_cq = elan4_alloccq (&uctx->uctx_ctxt, cqsize, perm, (uflags & UCQ_REORDER) ? CQ_Reorder : 0)) == NULL)
++    {
++      KMEM_FREE (ucq, sizeof (USER_CQ));
++      
++      PRINTF2 (uctx, DBG_CQ, "user_alloccq: failed elan4_allocq cqsize %d uflags %x\n", cqsize, uflags);
++
++      return ERR_PTR(-ENOMEM);
++    }
++    
++    atomic_set (&ucq->ucq_ref, 1);
++
++    ucq->ucq_state = UCQ_RUNNING;
++    ucq->ucq_flags = uflags;
++    
++    PRINTF3 (uctx, DBG_CQ, "user_alloccq: ucq=%p idx=%d cqnum=%d\n", ucq, elan4_cq2idx (ucq->ucq_cq), elan4_cq2num(ucq->ucq_cq));
++
++    /* chain it onto the context */
++    kmutex_lock (&uctx->uctx_cqlock);
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    list_add (&ucq->ucq_link, &uctx->uctx_cqlist);
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++    kmutex_unlock (&uctx->uctx_cqlock);
++
++    return (ucq);
++}
++
++USER_CQ *
++user_findcq (USER_CTXT *uctx, unsigned idx)
++{
++    struct list_head *entry;
++    unsigned long flags;
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    list_for_each (entry, &uctx->uctx_cqlist) {
++      USER_CQ *ucq = list_entry (entry, USER_CQ, ucq_link);
++
++      if (elan4_cq2idx(ucq->ucq_cq) == idx)
++      {
++          atomic_inc (&ucq->ucq_ref);
++          spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++          return (ucq);
++      }
++    }
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++
++    return (NULL);
++}
++
++void
++user_dropcq (USER_CTXT *uctx, USER_CQ *ucq)
++{
++    unsigned long flags;
++
++    PRINTF2 (uctx, DBG_CQ, "user_dropcq: ucq=%p ref=%d\n", ucq, atomic_read (&ucq->ucq_ref));
++
++    kmutex_lock (&uctx->uctx_cqlock);
++    if (! atomic_dec_and_test (&ucq->ucq_ref))
++    {
++      kmutex_unlock (&uctx->uctx_cqlock);
++      return;
++    }
++
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++    list_del (&ucq->ucq_link);
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++
++    kmutex_unlock (&uctx->uctx_cqlock);
++
++    elan4_freecq (&uctx->uctx_ctxt, ucq->ucq_cq);
++
++    KMEM_FREE (ucq, sizeof (USER_CQ));
++}
++
++int
++user_alloc_trap_queues (USER_CTXT *uctx, unsigned ndproc_traps, unsigned neproc_traps, 
++                      unsigned ntproc_traps, unsigned nthreads, unsigned ndmas)
++{
++    ELAN4_DPROC_TRAP *dprocs;
++    ELAN4_EPROC_TRAP *eprocs;
++    ELAN4_TPROC_TRAP *tprocs;
++    E4_DMA           *dmas;
++    E4_ThreadRegs    *threads;
++    ELAN4_NETERR_MSG *msgs;
++    unsigned long     flags;
++
++    int nmsgs = NETERR_MSGS;
++
++    /* bounds check the values that have been passed in */
++    if (ndproc_traps < 2 || ndproc_traps > 10000 ||
++      ntproc_traps < 1 || ntproc_traps > 100   ||
++      neproc_traps < 6 || neproc_traps > 10000 ||
++      nthreads     < 2 || nthreads     > 10000 ||
++      ndmas        < 2 || ndmas        > 10000)
++      return -EINVAL;
++
++    if (uctx->uctx_dmas != NULL)
++      return -EBUSY;
++
++    KMEM_ZALLOC (dprocs, ELAN4_DPROC_TRAP *, ndproc_traps * sizeof (ELAN4_DPROC_TRAP), 1);
++    KMEM_ZALLOC (eprocs, ELAN4_EPROC_TRAP *, neproc_traps * sizeof (ELAN4_EPROC_TRAP), 1);
++    KMEM_ZALLOC (tprocs, ELAN4_TPROC_TRAP *, ntproc_traps * sizeof (ELAN4_TPROC_TRAP), 1);
++    KMEM_ZALLOC (threads, E4_ThreadRegs *, nthreads * sizeof (E4_ThreadRegs), 1);
++    KMEM_ZALLOC (dmas, E4_DMA *, ndmas * sizeof (E4_DMA), 1);
++    KMEM_ZALLOC (msgs, ELAN4_NETERR_MSG *, nmsgs * sizeof (ELAN4_NETERR_MSG), 1);
++
++    if (dprocs == NULL || eprocs == NULL || tprocs == NULL || dmas == NULL || threads == NULL || msgs == NULL)
++    {
++      if (dprocs != NULL) KMEM_FREE (dprocs, ndproc_traps * sizeof (ELAN4_DPROC_TRAP));
++      if (eprocs != NULL) KMEM_FREE (eprocs, neproc_traps * sizeof (ELAN4_EPROC_TRAP));
++      if (tprocs != NULL) KMEM_FREE (tprocs, ntproc_traps * sizeof (ELAN4_TPROC_TRAP));
++      if (threads != NULL) KMEM_FREE (threads, nthreads * sizeof (E4_ThreadRegs));
++      if (dmas != NULL) KMEM_FREE (dmas, ndmas * sizeof (E4_DMA));
++      if (msgs != NULL) KMEM_FREE (msgs, nmsgs * sizeof (ELAN4_NETERR_MSG));
++      
++      return -ENOMEM;
++    }
++    
++    spin_lock_irqsave (&uctx->uctx_spinlock, flags);
++
++    uctx->uctx_dprocTraps = dprocs;
++    uctx->uctx_eprocTraps = eprocs;
++    uctx->uctx_tprocTraps = tprocs;
++    uctx->uctx_threads    = threads;
++    uctx->uctx_dmas       = dmas;
++    uctx->uctx_msgs       = msgs;
++
++    RING_QUEUE_INIT (uctx->uctx_dprocTrapQ, ndproc_traps, 1 /* 1 for 2nd dma */);
++    RING_QUEUE_INIT (uctx->uctx_tprocTrapQ, ntproc_traps, 0);
++    RING_QUEUE_INIT (uctx->uctx_eprocTrapQ, neproc_traps, 5 /* 1 for command, 2 for dma, 2 for inputter */);
++    RING_QUEUE_INIT (uctx->uctx_threadQ,    nthreads,     1);
++    RING_QUEUE_INIT (uctx->uctx_dmaQ,       ndmas,        1);
++    RING_QUEUE_INIT (uctx->uctx_msgQ,       nmsgs,        0);
++
++    spin_unlock_irqrestore (&uctx->uctx_spinlock, flags);
++    
++    return 0;
++}
++
++USER_CTXT *
++user_alloc (ELAN4_DEV *dev)
++{
++    USER_CTXT *uctx;
++    int res;
++    int i;
++
++    /* Allocate and initialise the context private data */
++    KMEM_ZALLOC (uctx, USER_CTXT *, sizeof  (USER_CTXT), 1);
++
++    if (uctx == NULL)
++      return ERR_PTR(-ENOMEM);
++
++    if (elan4_get_position (dev, &uctx->uctx_position) == ELAN_POS_UNKNOWN)
++    {
++      KMEM_FREE (uctx, sizeof (USER_CTXT));
++      return ERR_PTR(-EAGAIN);
++    }
++    
++    if ((res = elan4_insertctxt (dev, &uctx->uctx_ctxt, &user_trap_ops)) != 0)
++    {
++      KMEM_FREE (uctx, sizeof (USER_CTXT));
++      return ERR_PTR(res);
++    }
++
++    KMEM_GETPAGES (uctx->uctx_upage, ELAN4_USER_PAGE *, btopr (sizeof (ELAN4_USER_PAGE)), 1);
++    if (uctx->uctx_upage == NULL)
++    {
++      elan4_removectxt (dev, &uctx->uctx_ctxt);
++      KMEM_FREE (uctx, sizeof (USER_CTXT));
++      return ERR_PTR(-ENOMEM);
++    }
++    
++    if ((uctx->uctx_trampoline = elan4_sdram_alloc (dev, SDRAM_PAGE_SIZE)) == 0)
++    {
++      KMEM_FREEPAGES (uctx->uctx_upage, btopr (sizeof (ELAN4_USER_PAGE)));
++      elan4_removectxt (dev, &uctx->uctx_ctxt);
++
++      KMEM_FREE (uctx, sizeof (USER_CTXT));
++      return ERR_PTR(-ENOMEM);
++    }
++    
++    if ((uctx->uctx_routetable = elan4_alloc_routetable (dev, 4 /* 512 << 4 == 8192 entries */)) == NULL)
++    {
++      elan4_sdram_free (dev, uctx->uctx_trampoline, SDRAM_PAGE_SIZE);
++      KMEM_FREEPAGES (uctx->uctx_upage, btopr (sizeof (ELAN4_USER_PAGE)));
++      elan4_removectxt (dev, &uctx->uctx_ctxt);
++
++      KMEM_FREE (uctx, sizeof (USER_CTXT));
++      return ERR_PTR(-ENOMEM);
++    }
++
++    elan4_set_routetable (&uctx->uctx_ctxt, uctx->uctx_routetable);
++
++    /* initialise the trap and swap queues to be really full */
++    RING_QUEUE_INIT (uctx->uctx_dprocTrapQ, 0, 1);
++    RING_QUEUE_INIT (uctx->uctx_tprocTrapQ, 0, 1);
++    RING_QUEUE_INIT (uctx->uctx_eprocTrapQ, 0, 1);
++    RING_QUEUE_INIT (uctx->uctx_threadQ, 0, 1);
++    RING_QUEUE_INIT (uctx->uctx_dmaQ, 0, 1);
++
++    INIT_LIST_HEAD (&uctx->uctx_cent_list);
++    INIT_LIST_HEAD (&uctx->uctx_vpseg_list);
++    INIT_LIST_HEAD (&uctx->uctx_cqlist);
++
++    uctx->uctx_haltop.op_function = user_flush;
++    uctx->uctx_haltop.op_arg      = uctx;
++    uctx->uctx_haltop.op_mask     = INT_Halted|INT_Discarding;
++
++    uctx->uctx_dma_flushop.op_function = user_flush_dmas;
++    uctx->uctx_dma_flushop.op_arg      = uctx;
++
++    kmutex_init (&uctx->uctx_vpseg_lock);
++    kmutex_init (&uctx->uctx_cqlock);
++    kmutex_init (&uctx->uctx_rgnmutex);
++
++    spin_lock_init (&uctx->uctx_spinlock);
++    spin_lock_init (&uctx->uctx_rgnlock);
++    spin_lock_init (&uctx->uctx_fault_lock);
++
++    kcondvar_init (&uctx->uctx_wait);
++
++    if ((uctx->uctx_ddcq = user_alloccq (uctx, CQ_Size1K, CQ_EnableAllBits, UCQ_SYSTEM)) == NULL)
++    {
++      user_free (uctx);
++      return ERR_PTR(-ENOMEM);
++    }
++
++    uctx->uctx_trap_count = 0;
++    uctx->uctx_trap_state = UCTX_TRAP_IDLE;
++    uctx->uctx_status     = 0 /* UCTX_DETACHED | UCTX_SWAPPED | UCTX_STOPPED */;
++
++    init_timer (&uctx->uctx_int_timer);
++
++    uctx->uctx_int_timer.function = user_signal_timer;
++    uctx->uctx_int_timer.data     = (unsigned long) uctx;
++    uctx->uctx_int_start          = jiffies;
++    uctx->uctx_int_count          = 0;
++    uctx->uctx_int_delay          = 0;
++
++    init_timer (&uctx->uctx_neterr_timer);
++    uctx->uctx_neterr_timer.function = user_neterr_timer;
++    uctx->uctx_neterr_timer.data     = (unsigned long) uctx;
++
++    uctx->uctx_upage->upage_ddcq_completed = 0;
++    uctx->uctx_ddcq_completed              = 0;
++    uctx->uctx_ddcq_insertcnt              = 0;
++
++    uctx->uctx_num_fault_save = num_fault_save;
++    if (uctx->uctx_num_fault_save) 
++    {  
++      KMEM_ZALLOC (uctx->uctx_faults, FAULT_SAVE *, (sizeof(FAULT_SAVE) * uctx->uctx_num_fault_save), 1);
++        if ( uctx->uctx_faults == NULL) 
++      {
++          user_free (uctx);
++          return ERR_PTR(-ENOMEM);
++        }
++    
++        for (i = 0; i < uctx->uctx_num_fault_save; i++)
++          uctx->uctx_faults[i].next = (i == (uctx->uctx_num_fault_save-1) ? NULL : &uctx->uctx_faults[i+1]);
++
++    }
++    uctx->uctx_fault_list = uctx->uctx_faults;
++
++    return (uctx);
++}
++
++void
++user_free (USER_CTXT *uctx)
++{
++    ELAN4_DEV *dev = uctx->uctx_ctxt.ctxt_dev;
++
++    user_swapout (uctx, UCTX_EXITING);
++
++    /* Detach from all input contexts */
++    user_detach (uctx, NULL);
++
++    /* since we're single threaded here - (called from close()) */
++    /* we don't need to hold the lock to drop the command queues */
++    /* since they cannot be mapped into user space */
++    while (! list_empty (&uctx->uctx_cqlist))
++      user_dropcq (uctx, list_entry (uctx->uctx_cqlist.next, USER_CQ, ucq_link));
++
++    /* Free off all of vpseg_list */
++    kmutex_lock (&uctx->uctx_vpseg_lock);
++    while (! list_empty (&uctx->uctx_vpseg_list))
++      user_remove_vpseg(uctx, list_entry (uctx->uctx_vpseg_list.next, USER_VPSEG, vps_link));
++    kmutex_unlock (&uctx->uctx_vpseg_lock);
++    
++    if (timer_pending (&uctx->uctx_int_timer))
++      del_timer_sync (&uctx->uctx_int_timer);
++    
++    if (timer_pending (&uctx->uctx_neterr_timer))
++      del_timer_sync (&uctx->uctx_neterr_timer);
++
++    if (uctx->uctx_dprocTraps)
++      KMEM_FREE (uctx->uctx_dprocTraps, uctx->uctx_dprocTrapQ.q_size * sizeof (ELAN4_DPROC_TRAP));
++    if (uctx->uctx_tprocTraps)
++      KMEM_FREE (uctx->uctx_tprocTraps, uctx->uctx_tprocTrapQ.q_size * sizeof (ELAN4_TPROC_TRAP));
++    if (uctx->uctx_eprocTraps)
++      KMEM_FREE (uctx->uctx_eprocTraps, uctx->uctx_eprocTrapQ.q_size * sizeof (ELAN4_EPROC_TRAP));
++    if (uctx->uctx_dmas)
++      KMEM_FREE (uctx->uctx_dmas, uctx->uctx_dmaQ.q_size * sizeof (E4_DMA));
++    if (uctx->uctx_msgs)
++      KMEM_FREE (uctx->uctx_msgs, NETERR_MSGS * sizeof (ELAN4_NETERR_MSG));
++    if (uctx->uctx_threads)
++      KMEM_FREE (uctx->uctx_threads, uctx->uctx_threadQ.q_size * sizeof (E4_ThreadRegs));
++    if (uctx->uctx_faults)
++      KMEM_FREE (uctx->uctx_faults, (sizeof(FAULT_SAVE) * uctx->uctx_num_fault_save));
++
++    if (uctx->uctx_intcookie_table)
++      intcookie_free_table (uctx->uctx_intcookie_table);
++
++    elan4_set_routetable (&uctx->uctx_ctxt, NULL);
++    elan4_free_routetable (dev, uctx->uctx_routetable);
++
++    /* Free off all USER_RGNs */
++    user_freergns(uctx);
++
++    elan4_sdram_free (dev, uctx->uctx_trampoline, SDRAM_PAGE_SIZE);
++
++    /* Clear the PG_Reserved bit before free to avoid a memory leak */
++    ClearPageReserved(pte_page(*find_pte_kernel((unsigned long) uctx->uctx_upage)));
++    KMEM_FREEPAGES (uctx->uctx_upage, btopr (sizeof (ELAN4_USER_PAGE)));
++
++    elan4_removectxt (dev, &uctx->uctx_ctxt);
++
++    kcondvar_destroy (&uctx->uctx_wait);
++
++    spin_lock_destroy (&uctx->uctx_rgnlock);
++    spin_lock_destroy (&uctx->uctx_spinlock);
++
++    kmutex_destroy (&uctx->uctx_rgnmutex);
++    kmutex_destroy (&uctx->uctx_cqlock);
++    kmutex_destroy (&uctx->uctx_vpseg_lock);
++
++    KMEM_FREE (uctx, sizeof (USER_CTXT));
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan4/user_ddcq.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan4/user_ddcq.c       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan4/user_ddcq.c    2005-05-11 12:10:12.472927408 -0400
+@@ -0,0 +1,226 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: user_ddcq.c,v 1.15 2004/06/23 11:06:05 addy Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/user_ddcq.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan4/debug.h>
++#include <elan4/device.h>
++#include <elan4/user.h>
++#include <elan4/commands.h>
++
++#if PAGE_SIZE < CQ_CommandMappingSize
++#  define ELAN4_COMMAND_QUEUE_MAPPING PAGE_SIZE
++#else
++#  define ELAN4_COMMAND_QUEUE_MAPPING CQ_CommandMappingSize
++#endif
++
++/* The user device driver command queue is used for re-issuing 
++ * trapped items.  It is allocated as a 1K command queue, and
++ * we insert command flow writes event 256 words.
++ */
++#define USER_CTRLFLOW_COUNT   256
++
++/* Flow control of the device driver command queue is handled by periodically 
++ * inserting dword writes into the command stream.   When you need to know 
++ * that the queue has been flushed, then you insert an extra contorl flow
++ * write into the command queue.   Should the queue not be flushed, but the
++ * trap handler be returning to user space, then it will also insert and 
++ * extra interrupt command to ensure that it is re-entered after the queue
++ * has been flushed.
++ *
++ * Note - we account the space for the interrupt command on each control 
++ * flow write so that we do not overflow the queue even if we end up 
++ * inserting an interrupt for every command flow write.  In general only
++ * a single interrupt should get inserted....
++ */
++
++#define user_ddcq_command_write(value,off) do { \
++    PRINTF(uctx, DBG_DDCQ, "user_ddcq_command_write: cmdptr=%x off=%d value=%llx\n", cmdptr, off, value);\
++    writeq(value, cmdptr + (off << 3)); \
++} while (0)
++
++#define user_ddcq_command_space(uctx) \
++    ((CQ_Size (uctx->uctx_ddcq->ucq_cq->cq_size)>>3) - ((uctx)->uctx_ddcq_insertcnt - (uctx)->uctx_upage->upage_ddcq_completed))
++
++#define user_ddcq_command_flow_write(uctx) do { \
++   E4_uint64 iptr   = (uctx)->uctx_ddcq_insertcnt; \
++   ioaddr_t  cmdptr = (uctx)->uctx_ddcq->ucq_cq->cq_mapping + ((iptr<<3) & ((ELAN4_COMMAND_QUEUE_MAPPING >> 1)-1));\
++\
++    (uctx)->uctx_ddcq_completed = ((uctx)->uctx_ddcq_insertcnt += 3);\
++\
++    PRINTF (uctx, DBG_DDCQ, "user_ddcq_command_flow_write: completed=%llx [%llx] addr=%llx\n", (uctx)->uctx_ddcq_completed, \
++          (uctx)->uctx_upage->upage_ddcq_completed, (uctx)->uctx_upage_addr); \
++    user_ddcq_command_write (GUARD_CMD       | GUARD_ALL_CHANNELS,      0);\
++    user_ddcq_command_write (WRITE_DWORD_CMD | (uctx)->uctx_upage_addr, 1);\
++    user_ddcq_command_write ((uctx)->uctx_ddcq_completed,               2);\
++} while (0)
++
++#define user_ddcq_command_flow_intr(uctx) do { \
++   E4_uint64 iptr   = (uctx)->uctx_ddcq_insertcnt; \
++   ioaddr_t  cmdptr = (uctx)->uctx_ddcq->ucq_cq->cq_mapping + ((iptr<<3) & ((ELAN4_COMMAND_QUEUE_MAPPING >> 1)-1));\
++\
++    PRINTF (uctx, DBG_DDCQ, "user_ddcq_command_flow_intr: completed=%llx [%llx] addr=%llx\n", (uctx)->uctx_ddcq_completed, \
++          (uctx)->uctx_upage->upage_ddcq_completed, (uctx)->uctx_upage_addr); \
++    user_ddcq_command_write (INTERRUPT_CMD   | ELAN4_INT_COOKIE_DDCQ,   3);\
++} while (0)
++
++#define user_ddcq_command_prologue(uctx, count) do { \
++   E4_uint64 iptr   = (uctx)->uctx_ddcq_insertcnt; \
++   ioaddr_t  cmdptr = (uctx)->uctx_ddcq->ucq_cq->cq_mapping + ((iptr<<3) & ((ELAN4_COMMAND_QUEUE_MAPPING >> 1)-1));\
++   PRINTF(uctx, DBG_DDCQ, "user_ddcq_command_prologue: iptr=%llx cmdptr=%x\n", iptr, cmdptr);
++
++#define user_ddcq_command_epilogue(uctx, count, extra) \
++   (uctx)->uctx_ddcq_insertcnt = iptr + (count);\
++\
++   PRINTF(uctx, DBG_DDCQ, "user_ddcq_command_epilogue: iptr=%llx + %x + %x - completed %llx\n", iptr, count, extra, (uctx)->uctx_ddcq_completed);\
++   if (((iptr) + (count) + (extra)) > ((uctx)->uctx_ddcq_completed + USER_CTRLFLOW_COUNT))\
++       user_ddcq_command_flow_write(uctx); \
++} while (0)
++
++int
++user_ddcq_check (USER_CTXT *uctx, unsigned num)
++{
++    PRINTF (uctx, DBG_DDCQ, "user_check_ddcq: insert=%llx completed=%llx num=%d\n", 
++          uctx->uctx_ddcq_insertcnt, uctx->uctx_upage->upage_ddcq_completed, num);
++
++    /* Ensure that there is enough space for the command we want to issue,
++     * PLUS the guard/writeword for the control flow flush.
++     * PLUS the interrupt command for rescheduling */
++    if (user_ddcq_command_space (uctx) > (num + 4))
++    {
++      PRINTF (uctx, DBG_DDCQ, "user_ddcq_check: loads of space\n");
++
++      return (1);
++    }
++    
++    PRINTF (uctx, DBG_DDCQ, "user_ddcq_check: not enough space - reschedule\n");
++
++    uctx->uctx_trap_state = UCTX_TRAP_SIGNALLED;
++    return (0);
++}
++
++int
++user_ddcq_flush (USER_CTXT *uctx)
++{
++    ELAN4_DEV *dev = uctx->uctx_ctxt.ctxt_dev;
++    USER_CQ   *ucq = uctx->uctx_ddcq;
++
++    switch (ucq->ucq_state)
++    {
++    case UCQ_TRAPPED:
++      PRINTF (uctx, DBG_DDCQ, "user_ddcq_flush: command queue is trapped\n");
++      return (0);
++      
++    case UCQ_NEEDS_RESTART:
++      PRINTF (uctx, DBG_DDCQ, "user_ddcq_flush: restarting command queue\n");
++
++      if (UCTX_RUNNABLE (uctx))
++      {
++          ucq->ucq_state = UCQ_RUNNING;
++          elan4_restartcq (dev, ucq->ucq_cq);
++      }
++      break;
++    }
++
++    PRINTF (uctx, DBG_DDCQ, "user_ddcq_flush: insertcnt=%llx completed=%llx [%llx]\n", 
++          uctx->uctx_ddcq_insertcnt, uctx->uctx_ddcq_completed, uctx->uctx_upage->upage_ddcq_completed);
++
++    if (uctx->uctx_ddcq_completed != uctx->uctx_ddcq_insertcnt)
++      user_ddcq_command_flow_write (uctx);
++
++    return (uctx->uctx_ddcq_completed == uctx->uctx_upage->upage_ddcq_completed);
++}
++
++void
++user_ddcq_intr (USER_CTXT *uctx)
++{
++    user_ddcq_command_flow_intr (uctx);
++}
++
++void
++user_ddcq_run_dma (USER_CTXT *uctx, E4_DMA *dma)
++{
++    PRINTF (uctx, DBG_DDCQ, "user_ddcq_run_dma: cookie=%llx vproc=%llx\n",  dma->dma_cookie, dma->dma_vproc);
++
++    user_ddcq_command_prologue(uctx, 7) {
++
++      user_ddcq_command_write ((dma->dma_typeSize & ~DMA_ContextMask) | RUN_DMA_CMD, 0);
++      user_ddcq_command_write (dma->dma_cookie,                                      1);
++      user_ddcq_command_write (dma->dma_vproc,                                       2);
++      user_ddcq_command_write (dma->dma_srcAddr,                                     3);
++      user_ddcq_command_write (dma->dma_dstAddr,                                     4);
++      user_ddcq_command_write (dma->dma_srcEvent,                                    5);
++      user_ddcq_command_write (dma->dma_dstEvent,                                    6);
++
++    } user_ddcq_command_epilogue (uctx, 7, 0);
++}
++
++void
++user_ddcq_run_thread (USER_CTXT *uctx, E4_ThreadRegs *regs)
++{
++    PRINTF (uctx, DBG_DDCQ, "user_ddcq_run_thread: PC=%llx SP=%llx\n", regs->Registers[0], regs->Registers[1]);
++
++    user_ddcq_command_prologue(uctx, 7) {
++
++      user_ddcq_command_write (regs->Registers[0] | RUN_THREAD_CMD, 0);
++      user_ddcq_command_write (regs->Registers[1],                  1);
++      user_ddcq_command_write (regs->Registers[2],                  2);
++      user_ddcq_command_write (regs->Registers[3],                  3);
++      user_ddcq_command_write (regs->Registers[4],                  4);
++      user_ddcq_command_write (regs->Registers[5],                  5);
++      user_ddcq_command_write (regs->Registers[6],                  6);
++      
++    } user_ddcq_command_epilogue (uctx, 7, 0);
++}
++
++void
++user_ddcq_setevent (USER_CTXT *uctx, E4_Addr addr)
++{
++    user_ddcq_command_prologue (uctx, 1) {
++
++      user_ddcq_command_write (SET_EVENT_CMD | addr, 0);
++    
++    } user_ddcq_command_epilogue (uctx, 1, 0);
++}
++
++void
++user_ddcq_seteventn (USER_CTXT *uctx, E4_Addr addr, E4_uint32 count)
++{
++    PRINTF (uctx, DBG_DDCQ, "user_ddcq_seteventn: addr=%llx count=%lx\n", addr, count);
++
++    user_ddcq_command_prologue (uctx, 2) {
++
++      user_ddcq_command_write (SET_EVENTN_CMD, 0);
++      user_ddcq_command_write (addr | count,   1);
++
++    } user_ddcq_command_epilogue (uctx, 2, 0);
++}
++
++void
++user_ddcq_waitevent (USER_CTXT *uctx, E4_Addr addr, E4_uint64 CountAndType, E4_uint64 Param0, E4_uint64 Param1)
++{
++    PRINTF (uctx, DBG_DDCQ, "user_ddcq_waitevent: addr=%llx CountAndType=%llx Param=%llx,%llx\n", addr, CountAndType, Param0, Param1);
++
++    user_ddcq_command_prologue (uctx, 4) {
++
++      user_ddcq_command_write (WAIT_EVENT_CMD | addr, 0);
++      user_ddcq_command_write (CountAndType,          1);
++      user_ddcq_command_write (Param0,                2);
++      user_ddcq_command_write (Param1,                3);
++
++    } user_ddcq_command_epilogue (uctx, 4, 0);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/elan4/user_Linux.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/elan4/user_Linux.c      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/elan4/user_Linux.c   2005-05-11 12:10:12.473927256 -0400
+@@ -0,0 +1,377 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: user_Linux.c,v 1.25.2.4 2005/01/18 14:36:10 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/user_Linux.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <qsnet/kpte.h>
++
++#include <linux/pci.h>
++
++#include <elan4/debug.h>
++#include <elan4/device.h>
++#include <elan4/user.h>
++
++static int
++user_pteload (USER_CTXT *uctx, E4_Addr addr, physaddr_t phys, int perm)
++{
++    ELAN4_DEV *dev = uctx->uctx_ctxt.ctxt_dev;
++    E4_uint64  newpte = elan4mmu_phys2pte (dev, phys, perm);
++    
++    /*
++     * On MPSAS we don't allocate a large enough context table, so 
++     * if we see an address/context pair which would "alias" because
++     * they differ in unchecked hash bits to a previous pteload, 
++     * then we kill the application.
++     */
++    {
++      unsigned hashval = (E4MMU_SHIFT_ADDR(addr, (dev->dev_pageshift[0]) + 2) ^ E4MMU_CONTEXT_SCRAMBLE(uctx->uctx_ctxt.ctxt_num));
++      
++      if (dev->dev_rsvd_hashval[0] == 0xFFFFFFFF)
++          dev->dev_rsvd_hashval[0] = hashval & dev->dev_rsvd_hashmask[0];
++      
++      if ((hashval & dev->dev_rsvd_hashmask[0]) != dev->dev_rsvd_hashval[0])
++      {
++          printk ("user_pteload: vaddr=%016llx ctxnum=%x -> [%x] overlaps %x - %x [hashidx=%x]\n", (unsigned long long) addr, 
++                  uctx->uctx_ctxt.ctxt_num, hashval, hashval & dev->dev_rsvd_hashmask[0], dev->dev_rsvd_hashval[0],
++                  E4MMU_HASH_INDEX (uctx->uctx_ctxt.ctxt_num, addr, dev->dev_pageshift[0], dev->dev_hashsize[0]-1));
++          
++          return -EFAULT;
++      }
++    }
++
++    if ((newpte & (PTE_PciNotLocal | PTE_CommandQueue)) == 0 && 
++      ((addr & (SDRAM_PGOFF_OFFSET << PAGE_SHIFT)) != (phys & (SDRAM_PGOFF_OFFSET << PAGE_SHIFT))))
++    {
++      printk ("user_pteload: vaddr=%016llx incorrectly alias sdram at %lx\n", (unsigned long long) addr, 
++              phys ^ pci_resource_start (dev->dev_osdep.pdev, ELAN4_BAR_SDRAM));
++      return -EFAULT;
++    }
++
++    if (newpte & PTE_PciNotLocal)
++      PRINTF (uctx, DBG_FAULT, "user_pteload: addr=%llx -> pte=%llx (pci)\n", addr, newpte);
++    else if (newpte & PTE_CommandQueue)
++      PRINTF (uctx, DBG_FAULT, "user_pteload: addr=%llx -> pte=%llx (command)\n", addr, newpte);
++    else
++      PRINTF (uctx, DBG_FAULT, "user_pteload: addr=%llx -> pte=%llx (sdram)\n", addr, newpte);
++
++    elan4mmu_pteload (&uctx->uctx_ctxt, 0, addr, newpte);
++
++    return (0);
++}
++
++int
++user_load_range (USER_CTXT *uctx, E4_Addr eaddr, unsigned long nbytes, E4_uint32 fsr)
++{
++    ELAN4_DEV             *dev       = uctx->uctx_ctxt.ctxt_dev;
++    struct mm_struct      *mm        = current->mm;
++    int                    writeable = (AT_Perm(fsr) == AT_PermLocalDataWrite ||
++                                      AT_Perm(fsr) == AT_PermRemoteWrite    ||
++                                      AT_Perm(fsr) == AT_PermLocalEvent     ||
++                                      AT_Perm(fsr) == AT_PermRemoteEvent);
++    struct vm_area_struct *vma;
++    int                    i, perm;
++    unsigned long          len;
++    unsigned long          maddr;
++    physaddr_t                   phys;
++
++    kmutex_lock (&uctx->uctx_rgnmutex);
++
++    while (nbytes > 0)
++    {
++      USER_RGN *rgn = user_rgnat_elan (uctx, eaddr);
++
++      if (rgn == NULL || ELAN4_INCOMPAT_ACCESS (rgn->rgn_perm, AT_Perm (fsr)))
++      {
++          PRINTF (uctx, DBG_FAULT, "user_load_range: eaddr=%llx -> %s\n", eaddr, rgn == NULL ? "no mapping" : "no permission");
++
++          kmutex_unlock (&uctx->uctx_rgnmutex);
++          return (rgn == NULL ? -EFAULT : -EPERM);
++      }
++
++      if (writeable)
++          perm = rgn->rgn_perm;
++/* This is the correct code but it breaks the Eagle libraries (1.6.X) - backed out (addy 24.08.04)
++      else if (AT_Perm(fsr) == AT_PermExecute && (rgn->rgn_perm & PERM_Mask) != PERM_LocExecute)
++*/
++      else if (AT_Perm(fsr) == AT_PermExecute)
++          perm = PERM_LocRead | (rgn->rgn_perm & ~PERM_Mask);
++      else
++          perm = ELAN4_PERM_READONLY (rgn->rgn_perm & PERM_Mask) | (rgn->rgn_perm & ~PERM_Mask);
++
++      PRINTF (uctx, DBG_FAULT, "user_load_range: rgn=%p [%llx.%lx.%x]\n", rgn, rgn->rgn_ebase, rgn->rgn_mbase, rgn->rgn_len);
++
++      len = ((rgn->rgn_ebase + rgn->rgn_len) - eaddr);
++      if (len > nbytes)
++          len = nbytes;
++      nbytes -= len;
++      
++      maddr = rgn->rgn_mbase + (eaddr - rgn->rgn_ebase);
++
++      PRINTF (uctx, DBG_FAULT, "user_load_range: eaddr=%llx->%llx -> %lx->%lx len=%x perm=%x\n", eaddr, 
++              eaddr + len, maddr, maddr + len, len, perm);
++
++      down_read (&mm->mmap_sem);
++      while (len > 0)
++      {
++          if ((vma = find_vma_intersection (mm, maddr, maddr + PAGE_SIZE)) == NULL ||
++              (writeable && !(vma->vm_flags & VM_WRITE)))
++          {
++              PRINTF (DBG_USER, DBG_FAULT, "ctxt_pagefault: %s %lx\n", vma ? "no writeble at" : "no vma for", maddr);
++              up_read (&mm->mmap_sem);
++              kmutex_unlock (&uctx->uctx_rgnmutex);
++              return (-EFAULT);
++          }
++
++          spin_lock (&mm->page_table_lock);
++          {
++              pte_t *ptep_ptr;
++              pte_t  ptep_value;
++
++              ptep_ptr = find_pte_map (mm, maddr);
++              if (ptep_ptr) {
++                  ptep_value = *ptep_ptr;
++                  pte_unmap(ptep_ptr);
++              }
++
++              PRINTF (uctx, DBG_FAULT, "user_load_range: %lx %s %s\n", maddr, writeable ? "writeable" : "readonly", 
++                      !ptep_ptr ? "invalid" : pte_none(ptep_value) ? "none " : !pte_present(ptep_value) ? "swapped " : 
++                      writeable && !pte_write(ptep_value) ? "COW" : "OK");
++              
++              if (ptep_ptr == NULL || pte_none(ptep_value) || !pte_present(ptep_value) || (writeable && !pte_write(ptep_value)) || !pte_read (ptep_value))
++              {
++                  spin_unlock (&mm->page_table_lock);
++                  
++                  make_pages_present(maddr, maddr + PAGE_SIZE);
++                  
++                  spin_lock (&mm->page_table_lock);
++
++                  ptep_ptr = find_pte_map (mm, maddr);
++                  if (ptep_ptr) {
++                      ptep_value = *ptep_ptr;
++                      pte_unmap(ptep_ptr);
++                  }
++                  
++                  if (ptep_ptr == NULL || pte_none(ptep_value) || !pte_present(ptep_value) || (writeable && !pte_write(ptep_value)) || !pte_read (ptep_value))
++                  {   
++                      spin_unlock (&mm->page_table_lock);
++                      up_read (&mm->mmap_sem);
++                      kmutex_unlock (&uctx->uctx_rgnmutex);
++                      return (-EFAULT);
++                  }
++              } 
++              
++              if (writeable)
++                  pte_mkdirty(ptep_value);
++              pte_mkyoung (ptep_value);
++
++              phys = pte_phys (ptep_value);
++
++              for (i = 0; i < PAGE_SIZE; i += (1 << dev->dev_pageshift[0]))
++              {
++                  if (user_pteload (uctx, eaddr, phys, perm) < 0)
++                  {
++                      spin_unlock (&mm->page_table_lock);
++                      up_read (&mm->mmap_sem);
++                      kmutex_unlock (&uctx->uctx_rgnmutex);
++                      return (-EFAULT);
++                  }
++                  
++                  eaddr += (1 << dev->dev_pageshift[0]);
++                  phys  += (1 << dev->dev_pageshift[0]);
++              }
++          }
++          spin_unlock (&mm->page_table_lock);
++              
++          maddr += PAGE_SIZE;
++          len   -= PAGE_SIZE;
++      }
++      up_read (&mm->mmap_sem);
++    }
++    kmutex_unlock (&uctx->uctx_rgnmutex);
++
++    PRINTF (uctx, DBG_FAULT, "user_load_range: alldone\n");
++
++    return (0);
++}
++
++void
++user_preload_main (USER_CTXT *uctx, virtaddr_t addr, unsigned long len)
++{
++    virtaddr_t             lim = addr + len - 1;
++    struct vm_area_struct *vma;
++
++    down_read (&current->mm->mmap_sem);
++
++    if ((vma = find_vma (current->mm, addr)) != NULL)
++    {
++      do {
++          unsigned long start = vma->vm_start;
++          unsigned long end   = vma->vm_end;
++
++          if ((start-1) >= lim)
++              break;
++
++          if (start < addr) start = addr;
++          if ((end-1) > lim) end = lim+1;
++              
++          if (vma->vm_flags & VM_IO)
++              continue;
++
++          user_unload_main (uctx, start, end - start);
++
++          make_pages_present (start, end);
++
++          user_update_main (uctx, current->mm, start, end - start);
++
++      } while ((vma = find_vma (current->mm, vma->vm_end)) != NULL);
++    }
++    up_read (&current->mm->mmap_sem);
++}
++
++static void
++user_update_range (USER_CTXT *uctx, int tbl, struct mm_struct *mm, virtaddr_t maddr, E4_Addr eaddr, unsigned long len, int perm)
++{
++    ELAN4_DEV *dev    = uctx->uctx_ctxt.ctxt_dev;
++    int        roperm = ELAN4_PERM_READONLY(perm & PERM_Mask) | (perm & ~PERM_Mask);
++    int        nbytes;
++
++    while (len > 0)
++    {
++      pte_t *ptep_ptr;
++      pte_t  ptep_value;
++      
++      ptep_ptr = find_pte_map (mm, maddr);
++      if (ptep_ptr) {
++          ptep_value = *ptep_ptr;
++          pte_unmap(ptep_ptr);
++      }
++
++      PRINTF (uctx, DBG_IOPROC, "user_update_range: %llx (%lx) %s\n", eaddr, maddr, 
++              !ptep_ptr ? "invalid" : pte_none(ptep_value) ? "none " : !pte_present(ptep_value) ? "swapped " : 
++              !pte_write(ptep_value) ? "RO/COW" : "OK");
++      
++      if (ptep_ptr && !pte_none(ptep_value) && pte_present(ptep_value) && pte_read (ptep_value)) {
++          physaddr_t phys_value = pte_phys(ptep_value);
++          for (nbytes = 0; nbytes < PAGE_SIZE; nbytes += (1 << dev->dev_pageshift[0]))
++          {
++              user_pteload (uctx, eaddr, phys_value, pte_write (ptep_value) ? perm : roperm);
++
++              eaddr       += (1 << dev->dev_pageshift[0]);
++              phys_value  += (1 << dev->dev_pageshift[0]);
++          }
++      }
++
++      maddr += PAGE_SIZE;
++      len   -= PAGE_SIZE;
++    }
++}
++
++void
++user_update_main (USER_CTXT *uctx, struct mm_struct *mm, virtaddr_t start, unsigned long len)
++{
++    USER_RGN     *rgn;
++    unsigned long ssize;
++    virtaddr_t    end = start + len - 1;
++
++    spin_lock (&uctx->uctx_rgnlock);
++
++    PRINTF (uctx, DBG_IOPROC, "user_update_main: start=%lx end=%lx\n", start, end);
++
++    for (rgn = user_findrgn_main (uctx, start, 0); rgn != NULL; rgn = rgn->rgn_mnext)
++    {
++      if (end < rgn->rgn_mbase)
++          break;
++      
++      if (start <= rgn->rgn_mbase && end >= (rgn->rgn_mbase + rgn->rgn_len - 1)) 
++      {
++          PRINTF (uctx, DBG_IOPROC, "user_update_main: whole %lx -> %lx\n", rgn->rgn_mbase, rgn->rgn_mbase + rgn->rgn_len - 1);
++
++          user_update_range (uctx, 0 /* tbl */, mm, rgn->rgn_mbase, rgn->rgn_ebase, rgn->rgn_len, rgn->rgn_perm);
++      }
++      else if (start <= rgn->rgn_mbase)
++      {
++          ssize = end - rgn->rgn_mbase + 1;
++
++          PRINTF (uctx, DBG_IOPROC, "user_update_main: start %lx -> %lx\n", rgn->rgn_mbase, rgn->rgn_mbase + ssize);
++
++          user_update_range (uctx, 0 /* tbl */, mm, rgn->rgn_mbase, rgn->rgn_ebase, ssize, rgn->rgn_perm);
++      }
++      else if (end >= (rgn->rgn_mbase + rgn->rgn_len - 1))
++      {
++          ssize = (rgn->rgn_mbase + rgn->rgn_len) - start;
++
++          PRINTF (uctx, DBG_IOPROC, "user_update_main: end   %lx -> %lx\n", start, start + ssize);
++
++          user_update_range (uctx, 0 /* tbl */, mm, start, rgn->rgn_ebase + (start - rgn->rgn_mbase), ssize, rgn->rgn_perm);
++      }
++      else
++      {
++          PRINTF (uctx, DBG_IOPROC, "user_update_main: middle %lx -> %lx\n", start, end);
++
++          user_update_range (uctx, 0 /* tbl */, mm, start, rgn->rgn_ebase + (start - rgn->rgn_mbase), len, rgn->rgn_perm);
++      }
++    }
++    spin_unlock (&uctx->uctx_rgnlock);
++}
++
++void
++user_unload_main (USER_CTXT *uctx, virtaddr_t start, unsigned long len)
++{
++    USER_RGN     *rgn;
++    unsigned long ssize;
++    virtaddr_t    end = start + len - 1;
++
++    spin_lock (&uctx->uctx_rgnlock);
++
++    PRINTF (uctx, DBG_IOPROC, "user_unload_main: start=%lx end=%lx\n", start, end);
++
++    for (rgn = user_findrgn_main (uctx, start, 0); rgn != NULL; rgn = rgn->rgn_mnext)
++    {
++      if (end < rgn->rgn_mbase)
++          break;
++      
++      if (start <= rgn->rgn_mbase && end >= (rgn->rgn_mbase + rgn->rgn_len - 1))
++      {
++          PRINTF (uctx, DBG_IOPROC, "user_unload_main: whole %lx -> %lx\n", rgn->rgn_mbase, rgn->rgn_mbase + rgn->rgn_len - 1);
++
++          elan4mmu_unload_range (&uctx->uctx_ctxt, 0 /* tbl */, rgn->rgn_ebase, rgn->rgn_len);
++      }
++      else if (start <= rgn->rgn_mbase)
++      {
++          ssize = end - rgn->rgn_mbase + 1;
++
++          PRINTF (uctx, DBG_IOPROC, "user_unload_main: start %lx -> %lx\n", rgn->rgn_mbase, rgn->rgn_mbase + ssize);
++
++          elan4mmu_unload_range (&uctx->uctx_ctxt, 0 /* tbl */, rgn->rgn_ebase, ssize);
++      }
++      else if (end >= (rgn->rgn_mbase + rgn->rgn_len - 1))
++      {
++          ssize = (rgn->rgn_mbase + rgn->rgn_len) - start;
++          
++          PRINTF (uctx, DBG_IOPROC, "user_unload_main: end   %lx -> %lx\n", start, start + ssize);
++          
++          elan4mmu_unload_range (&uctx->uctx_ctxt, 0 /* tbl */, rgn->rgn_ebase + (start - rgn->rgn_mbase), ssize);
++      }
++      else
++      {
++
++          PRINTF (uctx, DBG_IOPROC, "user_unload_main: middle %lx -> %lx\n", start, end);
++
++          elan4mmu_unload_range (&uctx->uctx_ctxt, 0 /* tbl */, rgn->rgn_ebase + (start - rgn->rgn_mbase), len);
++      }
++    }
++    spin_unlock (&uctx->uctx_rgnlock);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/ep/asm_elan4_thread.S
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/asm_elan4_thread.S   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/asm_elan4_thread.S        2005-05-11 12:10:12.473927256 -0400
+@@ -0,0 +1,78 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: asm_elan4_thread.S,v 1.1 2003/09/23 13:55:11 david Exp $ $Name: QSNETMODULES-4-31_20050321 $"
++/*      $Source: /cvs/master/quadrics/epmod/asm_elan4_thread.S,v $*/
++
++#include <elan4/events.h>
++#include <elan4/commands.h>
++
++/*
++ * c_reschedule (E4_uint64 *commandport)
++ */           
++      .global c_reschedule
++c_reschedule:
++      add             %sp, -128, %sp
++      st64            %r16, [%sp]                     // preserve call preserved registers
++      st64            %r24, [%sp + 64]                // - see CALL_USED_REGISTERS.
++      mov             %r16,%r16                       // BUG FIX: E4 RevA
++      mov             %r24,%r24                       // BUG FIX: E4 RevA
++      nop                                             // BUG FIX: E4 RevA
++      nop                                             // BUG FIX: E4 RevA
++      
++      mov             %r7, %r18                       // (%r2) return pc
++1:    call            2f
++       mov            %sp, %r17                       // (%r1) SP
++2:    add             %r7, (3f-1b), %r16              // (%r0) PC
++      mov             NOP_CMD, %r23                   // "nop" command
++      st64suspend     %r16, [%r8]
++3:    ld64            [%sp], %r16
++      ld64            [%sp + 64], %r24                // restore call preserved register
++      jmpl            %r2+8, %r0                      // and return
++       add            %sp, 128, %sp
++      
++
++/*
++ * c_waitevent (E4_uint64 *commandport, E4_Event *event, E4_uint64 count)
++ */
++      .global c_waitevent
++c_waitevent:
++      add             %sp, -192, %sp
++      st64            %r16, [%sp + 64]                // preserve call preserved registers
++      st64            %r24, [%sp + 128]               // - see CALL_USED_REGISTERS.
++      mov             %r16,%r16                       // BUG FIX: E4 RevA
++      mov             %r24,%r24                       // BUG FIX: E4 RevA
++      nop                                             // BUG FIX: E4 RevA
++      nop                                             // BUG FIX: E4 RevA
++
++      mov             %r7, %r18                       // (%r2) return pc
++1:    call            2f
++       mov            %sp, %r17                       // (%r1) SP
++2:    add             %r7, (3f-1b), %r16              // (%r0) PC
++      st32            %r16, [%sp]                     // event source block
++      mov             MAKE_EXT_CLEAN_CMD, %r23        // "flush command queue desc" command
++      st8             %r23, [%sp+56]                  // event source block
++      mov             %r16,%r16                       // BUG FIX: E4 RevA
++      mov             %r23,%r23                       // BUG FIX: E4 RevA
++      nop                                             // BUG FIX: E4 RevA
++      nop                                             // BUG FIX: E4 RevA
++      
++
++      or              %r9, WAIT_EVENT_CMD, %r16
++      sll8            %r10, 32, %r17
++      or              %r17, E4_EVENT_TYPE_VALUE(E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, 8), %r17
++      mov             %sp, %r18
++      mov             %r8, %r19
++      
++      st32suspend     %r16, [%r8]
++      
++3:    ld64            [%sp + 64], %r16                // restore call preserved register
++      ld64            [%sp + 128], %r24
++      jmpl            %r2+8, %r0                      // and return
++       add            %sp, 192, %sp
++
+Index: linux-2.6.5/drivers/net/qsnet/ep/assym_elan4.h
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/assym_elan4.h        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/assym_elan4.h     2005-05-11 12:10:12.474927104 -0400
+@@ -0,0 +1,20 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: genassym_elan4.c,v 1.3 2004/04/25 11:26:07 david Exp $ $Name: QSNETMODULES-4-31_20050321 $"
++/*      $Source: /cvs/master/quadrics/epmod/genassym_elan4.c,v $*/
++
++/* Generated by genassym_elan4 - do not modify */
++
++#define EP4_RCVR_THREAD_STALL 0
++#define EP4_RCVR_PENDING_TAILP        128
++#define EP4_RCVR_PENDING_HEAD 136
++#define EP4_RCVR_DEBUG                176
++#define EP4_RXD_NEXT          664
++#define EP4_RXD_QUEUED                728
++#define EP4_RXD_DEBUG         944
+Index: linux-2.6.5/drivers/net/qsnet/ep/cm.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/cm.c 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/cm.c      2005-05-11 12:10:12.479926344 -0400
+@@ -0,0 +1,3000 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: cm.c,v 1.83.2.6 2005/01/13 12:37:57 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/cm.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++
++#include "kcomm_vp.h"
++#include "debug.h"
++#include "cm.h"
++#include <elan/epsvc.h>
++
++#include <qsnet/procfs_linux.h>
++
++#if defined(LINUX)
++#include "conf_linux.h"
++#endif
++
++int BranchingRatios[CM_MAX_LEVELS];
++
++int MachineId      = -1;
++int BrokenLevel    = -1;                      /* Simulates Broken Network */
++int RejoinCheck    = 1;
++int RejoinPanic    = 0;
++
++static int
++SegmentNo (CM_RAIL *cmRail, u_int nodeid, u_int lvl)
++{
++    int i;
++
++    ASSERT (lvl < cmRail->NumLevels);
++    
++    for (i = 0; i < lvl; i++)
++      nodeid /= cmRail->Levels[i].NumSegs;
++    
++    return (nodeid % cmRail->Levels[lvl].NumSegs);
++}
++
++static int
++ClusterIds (CM_RAIL *cmRail, int clvl, int *clmin, int *clmax)
++{
++    int clid  = cmRail->Rail->Position.pos_nodeid - cmRail->Levels[clvl].MinNodeId;
++
++    if (clvl == 0)
++      *clmin = *clmax = clid;
++    else
++    {
++      *clmin = cmRail->Levels[clvl - 1].MinNodeId - cmRail->Levels[clvl].MinNodeId;
++      *clmax = *clmin + cmRail->Levels[clvl - 1].NumNodes - 1;
++    }
++    return (clid);
++}
++
++#if defined(PER_CPU_TIMEOUT)
++static void
++__Schedule_Discovery (CM_RAIL *cmRail)                /* we urgently need to schedule discovery */
++{
++    cmRail->NextDiscoverTime = lbolt;
++
++    if (cmRail->NextRunTime == 0 || AFTER (cmRail->NextRunTime, cmRail->NextDiscoverTime))
++      cmRail->NextRunTime = cmRail->NextDiscoverTime;
++}
++
++static void
++__Schedule_Heartbeat (CM_RAIL *cmRail)
++{
++    cmRail->NextHeartbeatTime = lbolt;
++
++    if (cmRail->NextRunTime == 0 || AFTER (cmRail->NextRunTime, cmRail->NextHeartbeatTime))
++      cmRail->NextRunTime = cmRail->NextHeartbeatTime;
++}
++#else
++
++static void
++__Schedule_Timer (CM_RAIL *cmRail, long tick)
++{
++    if (! timer_pending (&cmRail->HeartbeatTimer) || AFTER (cmRail->NextRunTime, tick))
++    {
++      cmRail->NextRunTime = tick;
++
++      mod_timer (&cmRail->HeartbeatTimer, tick);
++    }
++}
++
++static void
++__Schedule_Discovery (CM_RAIL *cmRail)                /* we urgently need to schedule discovery */
++{
++    __Schedule_Timer (cmRail, cmRail->NextDiscoverTime = lbolt);
++}
++
++static void
++__Schedule_Heartbeat (CM_RAIL *cmRail)
++{
++    __Schedule_Timer (cmRail, cmRail->NextHeartbeatTime = lbolt);
++}
++#endif
++
++static int
++MsgBusy (CM_RAIL *cmRail, int msgNumber)
++{
++    switch (ep_outputq_state (cmRail->Rail, cmRail->MsgQueue, msgNumber))
++    {
++    case EP_OUTPUTQ_BUSY:                     /* still busy */
++      return 1;
++      
++    case EP_OUTPUTQ_FAILED:                   /* NACKed */
++    {
++#if defined(DEBUG_PRINTF)
++      CM_MSG  *msg  = ep_outputq_msg (cmRail->Rail, cmRail->MsgQueue, msgNumber);
++      uint8_t  type  = msg->Hdr.Type;
++      uint16_t nmaps = msg->Hdr.NumMaps;
++      int16_t  off   = msg->Payload.Statemaps[CM_MSG_MAP(0)].offset;
++      
++      CPRINTF4 (((type == CM_MSG_TYPE_DISCOVER_LEADER) || (type == CM_MSG_TYPE_DISCOVER_SUBORDINATE))  ? 6 : 3, /* we expect broadcasts to be NACKed */
++                "%s: msg %d type %d  failed%s\n", cmRail->Rail->Name, msgNumber, type, 
++                (type != CM_MSG_TYPE_HEARTBEAT) ? "" : nmaps == 0 ? ": null heartbeat" :
++                off == STATEMAP_RESET ? ": heartbeat with R statemaps" : ": heartbeat with statemaps");
++#endif
++      return 0;
++    }
++    
++    case EP_OUTPUTQ_FINISHED:
++      return 0;
++
++    default:
++      panic ("MsgBusy - bad return code from ep_outputq_state\n");
++      /* NOTREACHED */
++    }
++    return 0;
++}
++
++static void
++LaunchMessage (CM_RAIL *cmRail, int msgNumber, int vp, int qnum, int retries, int type, int lvl, int nmaps)
++{
++    CM_MSG *msg = ep_outputq_msg (cmRail->Rail, cmRail->MsgQueue, msgNumber);
++    CM_HDR *hdr = &msg->Hdr;
++
++    ASSERT (nmaps >= 0 && nmaps <= CM_MSG_MAXMAPS);
++    ASSERT (SPINLOCK_HELD (&cmRail->Lock));
++
++    hdr->Version   = CM_MSG_VERSION;
++    hdr->ParamHash = cmRail->ParamHash;
++    hdr->Timestamp = cmRail->Timestamp;
++    hdr->Checksum  = 0;
++    hdr->NodeId    = cmRail->Rail->Position.pos_nodeid;
++    hdr->MachineId = MachineId;
++    hdr->NumMaps   = nmaps;
++    hdr->Level     = lvl;
++    hdr->Type      = type;
++    hdr->Checksum  = CheckSum ((char *)msg + CM_MSG_BASE(nmaps), CM_MSG_SIZE(nmaps));
++
++    if (BrokenLevel != -1 && (lvl >= ((BrokenLevel >> (cmRail->Rail->Number*4)) & 0xf)))                      /* Simulate broken network? */
++       return;
++
++    if (ep_outputq_send (cmRail->Rail, cmRail->MsgQueue, msgNumber, 
++                       CM_MSG_SIZE(nmaps), vp, qnum, retries));
++      IncrStat (cmRail, LaunchMessageFail);
++}
++
++static int
++SendMessage (CM_RAIL *cmRail, int nodeId, int lvl, int type)
++{
++    int       msgNumber = CM_NUM_NODE_MSG_BUFFERS + cmRail->NextSpareMsg;
++    int n         = CM_NUM_SPARE_MSG_BUFFERS;
++    int retries;
++
++    ASSERT (type == CM_MSG_TYPE_IMCOMING ||   /* other types must use SendToSgmt */
++          type == CM_MSG_TYPE_REJOIN);
++   
++    while (n-- > 0 && MsgBusy (cmRail, msgNumber))    /* search for idle "spare" buffer */
++    {
++      if (++(cmRail->NextSpareMsg) == CM_NUM_SPARE_MSG_BUFFERS)
++          cmRail->NextSpareMsg = 0;
++      
++      msgNumber = CM_NUM_NODE_MSG_BUFFERS + cmRail->NextSpareMsg;
++    }
++
++    if (n == 0)                                       /* all "spare" message buffers busy */
++    {
++      CPRINTF3 (3, "%s: all spare message buffers busy: trying to send type %d to %d\n",
++                cmRail->Rail->Name, type, nodeId);
++      return (0);
++    }
++
++    /* NB IMCOMING may be echoed by MANY nodes, so we don't (and musn't) have any retries */
++    retries = (type == CM_MSG_TYPE_IMCOMING) ? 0 : CM_P2P_DMA_RETRIES;
++   
++    LaunchMessage (cmRail, msgNumber, EP_VP_NODE (nodeId), EP_SYSTEMQ_INTR, /* eager receive */
++                 retries, type, lvl, 0);
++   
++    if (++(cmRail->NextSpareMsg) == CM_NUM_SPARE_MSG_BUFFERS) /* check this one last next time */
++      cmRail->NextSpareMsg = 0;
++
++    return (1);
++}
++
++static int
++SendToSgmt (CM_RAIL *cmRail, CM_SGMT *sgmt, int type)
++{    
++    bitmap_t         seg;
++    int              offset;
++    int              nmaps;
++    int              sidx;
++    int              clvl;
++    
++    ASSERT (sgmt->Level <= cmRail->TopLevel);
++
++    if (MsgBusy (cmRail, sgmt->MsgNumber))            /* previous message still busy */
++    {
++      CPRINTF3 (3, "%s: node message buffer busy: trying to send type %d to %d\n",
++                cmRail->Rail->Name, type, sgmt->NodeId);
++      
++      return (0);
++    }
++
++    switch (type)
++    {
++    case CM_MSG_TYPE_RESOLVE_LEADER:
++    case CM_MSG_TYPE_DISCOVER_LEADER:
++      ASSERT (sgmt->State == CM_SGMT_ABSENT);
++      ASSERT (sgmt->Level == ((cmRail->Role == CM_ROLE_LEADER_CANDIDATE) ? cmRail->TopLevel : cmRail->TopLevel - 1));
++      ASSERT (sgmt->Level < cmRail->NumLevels);
++      ASSERT (sgmt->Sgmt == cmRail->Levels[sgmt->Level].MySgmt);
++      
++      /* broadcast to me and all my peers at this level (== my segment in the level above) */
++      sidx = (sgmt->Level == cmRail->NumLevels - 1) ? 0 : cmRail->Levels[sgmt->Level + 1].MySgmt;
++
++      LaunchMessage (cmRail, sgmt->MsgNumber, EP_VP_BCAST (sgmt->Level + 1, sidx), 
++                     EP_SYSTEMQ_INTR, 0,              /* eager rx; no retries */
++                     type, sgmt->Level, 0);
++      return (1);
++      
++    case CM_MSG_TYPE_DISCOVER_SUBORDINATE:
++      ASSERT (sgmt->Sgmt != cmRail->Levels[sgmt->Level].MySgmt);
++      ASSERT (sgmt->State == CM_SGMT_WAITING);
++      ASSERT (sgmt->Level > 0);                       /* broadcasting just to subtree */
++      
++      LaunchMessage (cmRail, sgmt->MsgNumber, EP_VP_BCAST (sgmt->Level, sgmt->Sgmt), 
++                     EP_SYSTEMQ_INTR, 0,              /* eager rx; no retries */
++                     CM_MSG_TYPE_DISCOVER_SUBORDINATE, sgmt->Level, 0);
++      return (1);
++      
++    case CM_MSG_TYPE_NOTIFY:
++      ASSERT (sgmt->State == CM_SGMT_PRESENT);
++      
++      LaunchMessage (cmRail, sgmt->MsgNumber, EP_VP_NODE (sgmt->NodeId),
++                     EP_SYSTEMQ_INTR, CM_P2P_DMA_RETRIES, /* eager rx; lots of retries */
++                     CM_MSG_TYPE_NOTIFY, sgmt->Level, 0);
++      return (1);
++      
++    case CM_MSG_TYPE_HEARTBEAT:
++    {
++      CM_MSG *msg = ep_outputq_msg (cmRail->Rail, cmRail->MsgQueue, sgmt->MsgNumber);
++      CM_HDR *hdr = &msg->Hdr;
++
++      ASSERT (sgmt->State == CM_SGMT_PRESENT);
++      
++      hdr->AckSeq = sgmt->AckSeq;
++   
++      if (!sgmt->MsgAcked)                    /* Current message not acknowledged */
++      {
++          /* must have been something significant to require an ack */
++          ASSERT (sgmt->SendMaps);
++          ASSERT (sgmt->NumMaps > 0);
++          
++          CPRINTF3 (3, "%s: retrying heartbeat to %d (%d entries)\n", cmRail->Rail->Name, sgmt->NodeId, sgmt->NumMaps);
++
++          IncrStat (cmRail, RetryHeartbeat);
++
++          nmaps = sgmt->NumMaps;
++      }
++      else
++      {
++          nmaps = 0;
++      
++          if (sgmt->SendMaps)                 /* can send maps */
++          {
++              for (clvl = sgmt->Level; clvl < cmRail->NumLevels; clvl++)
++              {
++                  if (!sgmt->Maps[clvl].OutputMapValid)
++                      continue;
++                  
++                  while ((offset = statemap_findchange (sgmt->Maps[clvl].OutputMap, &seg, 1)) >= 0)
++                  {
++                      CM_STATEMAP_ENTRY *map = &msg->Payload.Statemaps[CM_MSG_MAP(nmaps)];
++
++                      sgmt->Maps[clvl].SentChanges = 1;
++                      
++                      map->level  = clvl;
++                      map->offset = offset;
++                      map->seg[0] = seg & 0xffff;
++                      map->seg[1] = (seg >> 16) & 0xffff;
++#if (BT_ULSHIFT == 6)
++                      map->seg[2] = (seg >> 32) & 0xffff;
++                      map->seg[3] = (seg >> 48) & 0xffff;
++#elif (BT_ULSHIFT != 5)
++#error "Bad value for BT_ULSHIFT"
++#endif
++                      if (++nmaps == CM_MSG_MAXMAPS)
++                          goto msg_full;
++                  }
++
++                  if (sgmt->Maps[clvl].SentChanges)
++                  {
++                      CM_STATEMAP_ENTRY *map = &msg->Payload.Statemaps[CM_MSG_MAP(nmaps)];
++
++                      sgmt->Maps[clvl].SentChanges = 0;
++
++                      map->level  = clvl;
++                      map->offset = STATEMAP_NOMORECHANGES;
++                      
++                      if (++nmaps == CM_MSG_MAXMAPS)
++                          goto msg_full;
++                  }
++              }
++          }
++          
++          ASSERT (nmaps < CM_MSG_MAXMAPS);
++
++      msg_full:
++          sgmt->NumMaps = nmaps;              /* remember how many incase we retry */
++
++          if (nmaps == 0)                     /* no changes to send */
++              hdr->Seq = sgmt->MsgSeq;        /* this one can be dropped */
++          else
++          {
++              hdr->Seq = ++(sgmt->MsgSeq);    /* on to next message number */
++              sgmt->MsgAcked = 0;             /* need this one to be acked before I can send another */
++
++              IncrStat (cmRail, MapChangesSent);
++          }
++      }
++
++      LaunchMessage (cmRail, sgmt->MsgNumber, EP_VP_NODE (sgmt->NodeId), 
++                     EP_SYSTEMQ_POLLED,  CM_P2P_DMA_RETRIES, /* polled receive, lots of retries */
++                     CM_MSG_TYPE_HEARTBEAT, sgmt->Level, nmaps);
++
++      IncrStat (cmRail, HeartbeatsSent);
++
++      return (1);
++    }
++
++    default:                                  /* other types must use SendMessage */
++      printk ("SendToSgmt: invalid type %d\n", type);
++      ASSERT (0);
++
++      return (1);
++    }
++}
++
++static char *
++GlobalStatusString (statemap_t *map, int idx)
++{
++    char *strings[] = {"....", "S...", "C...", "R...", 
++                     ".s..", "Ss..", "Cs..", "Rs..", 
++                     "..r.", "S.r.", "C.r.", "R.r.", 
++                     ".sr.", "Ssr.", "Csr.", "Rsr.", 
++                     "...R", "S..R", "C..R", "R..R", 
++                     ".s.R", "Ss.R", "Cs.R", "Rs.R", 
++                     "..rR", "S.rR", "C.rR", "R.rR", 
++                     ".srR", "SsrR", "CsrR", "RsrR"};
++    
++    return (strings[statemap_getbits (map, idx * CM_GSTATUS_BITS, CM_GSTATUS_BITS)]);
++}
++
++static char *
++MapString (char *name, statemap_t *map, int nnodes, char *trailer)
++{
++    static char *space;
++    int          i;
++
++    if (space == NULL)
++      KMEM_ALLOC (space, char *, EP_MAX_NODES*(CM_GSTATUS_BITS+1), 0);
++
++    if (space == NULL)
++      return ("<cannot allocate memory>");
++    else
++    {
++      char *ptr = space;
++
++      sprintf (space, "%s ", name); ptr += strlen (ptr);
++      for (i = 0; i < nnodes; i++, ptr += strlen (ptr))
++          sprintf (ptr, "%s%s", i == 0 ? "" : ",", GlobalStatusString (map, i));
++      sprintf (ptr, " %s", trailer);
++      return (space);
++    }
++}
++
++void
++DisplayMap (DisplayInfo *di, CM_RAIL *cmRail, char *name, statemap_t *map, int nnodes, char *trailer)
++{
++    char  linebuf[256];
++    char *ptr = linebuf;
++    int   i;
++
++#define NODES_PER_LINE        32
++    for (i = 0; i < nnodes; i++)
++    {
++      if (ptr == linebuf)
++      {
++          sprintf (ptr, "%4d", i);
++          ptr += strlen (ptr);
++      }
++      
++      sprintf (ptr, ",%s", GlobalStatusString (map, i));
++      ptr += strlen (ptr);
++
++      if ((i % NODES_PER_LINE) == (NODES_PER_LINE-1) || (i == (nnodes-1)))
++      {
++          (di->func)(di->arg, "%s: %s %s %s\n", cmRail->Rail->Name, name, linebuf, trailer);
++          ptr = linebuf;
++      }
++    }
++#undef NODES_PER_LINE
++}
++
++void
++DisplayNodeMaps (DisplayInfo *di, CM_RAIL *cmRail)
++{
++    int   lvl;
++    int   clvl;
++    char  mapname[128];
++    
++    (di->func)(di->arg, "%s: Node %d maps...\n", cmRail->Rail->Name, cmRail->Rail->Position.pos_nodeid);
++
++    for (clvl = 0; clvl < cmRail->NumLevels; clvl++)
++    {
++      int nnodes = cmRail->Levels[clvl].NumNodes;
++
++      (di->func)(di->arg, "%s: Cluster level %d: Connected %ld - %s%s\n", 
++                 cmRail->Rail->Name, clvl, cmRail->Levels[clvl].Connected,
++                 cmRail->Levels[clvl].Online     ? "Online" : "Offline",
++                 cmRail->Levels[clvl].Restarting ? ", Restarting" : "");
++
++      for (lvl = 0; lvl < cmRail->TopLevel && lvl <= clvl; lvl++)
++      {
++          CM_LEVEL *level = &cmRail->Levels[lvl];
++
++          sprintf (mapname, "%10s%2d", "Level", lvl);
++          DisplayMap (di, cmRail, mapname, level->SubordinateMap[clvl], nnodes,
++                      level->SubordinateMapValid[clvl] ? "" : "(invalid)");
++      }
++
++      sprintf (mapname, "%12s", "Local");
++      DisplayMap (di, cmRail, mapname, cmRail->Levels[clvl].LocalMap, nnodes, "");
++
++      sprintf (mapname, "%12s", "Subtree");
++      DisplayMap (di, cmRail, mapname, cmRail->Levels[clvl].SubTreeMap, nnodes, 
++                  cmRail->Levels[clvl].SubTreeMapValid ? "" : "(invalid)");
++
++      sprintf (mapname, "%12s", "Global");
++      DisplayMap (di, cmRail, mapname, cmRail->Levels[clvl].GlobalMap, nnodes, 
++                  cmRail->Levels[clvl].GlobalMapValid ? "" : "(invalid)");
++
++      sprintf (mapname, "%12s", "LastGlobal");
++      DisplayMap (di, cmRail, mapname, cmRail->Levels[clvl].LastGlobalMap, nnodes, "");
++    }
++}
++
++void
++DisplayNodeSgmts (DisplayInfo *di, CM_RAIL *cmRail)
++{
++    int   lvl;
++    int   sidx;
++    
++    (di->func)(di->arg, "%s: Node %d segments...\n", cmRail->Rail->Name, cmRail->NodeId);
++    
++    for (lvl = 0; lvl <= cmRail->TopLevel && lvl < cmRail->NumLevels; lvl++)
++    {
++      (di->func)(di->arg, "   level %d: ", lvl);
++      
++      for (sidx = 0; sidx < ((lvl == cmRail->TopLevel) ? 1 : cmRail->Levels[lvl].NumSegs); sidx++)
++      {
++          CM_SGMT *sgmt = &cmRail->Levels[lvl].Sgmts[sidx];
++              
++          if (sgmt->State == CM_SGMT_PRESENT)
++              (di->func)(di->arg, "[%d, in: %d out: %d %s%s]", 
++                      sgmt->NodeId,
++                      sgmt->AckSeq,
++                      sgmt->MsgSeq,
++                      sgmt->MsgAcked ? "A" : "-",
++                      sgmt->SendMaps ? "!" : "-");
++          else
++              (di->func)(di->arg, "[%s]", (sgmt->State == CM_SGMT_ABSENT ? "absent" :
++                               sgmt->State == CM_SGMT_WAITING ? "waiting" :
++                               sgmt->State == CM_SGMT_COMING ? "coming" : "UNKNOWN"));
++      }
++      (di->func)(di->arg, "\n");
++    }
++}
++
++
++static void
++StartConnecting (CM_RAIL *cmRail, CM_SGMT *sgmt, int NodeId, int Timestamp)
++{
++    int clvl;
++
++    CPRINTF4 (2, "%s: lvl %d subtree %d node %d -> connecting\n", cmRail->Rail->Name, sgmt->Level, sgmt->Sgmt, NodeId);
++
++    /* Only reconnect the same guy if he was reborn */
++    ASSERT (sgmt->State != CM_SGMT_PRESENT ||
++          (sgmt->NodeId == NodeId && sgmt->Timestamp != Timestamp));
++   
++    /* After we've connected to a new peer, we wait to receive
++     * STATEMAP_RESET before we accumulate changes and we wait for a
++     * complete map to be received before we propagate changes to other
++     * nodes.
++     *
++     * If I'm the subordinate, I can start sending maps right away, since
++     * the leader is ready for them already.  If I'm the leader, I hold off
++     * sending maps until I've seen the subordinate's first heartbeat,
++     * because the subordinate might miss my NOTIFY message, still think
++     * she's a leader candidate and ignore my heartbeats.
++     */
++    sgmt->SendMaps = (sgmt->Level == cmRail->TopLevel); /* I can send maps to my leader (she NOTIFIED me) */
++
++    for (clvl = sgmt->Level; clvl < cmRail->NumLevels; clvl++)
++    {
++      statemap_reset (sgmt->Maps[clvl].CurrentInputMap);
++      statemap_reset (sgmt->Maps[clvl].InputMap);
++      statemap_reset (sgmt->Maps[clvl].OutputMap);
++      
++      sgmt->Maps[clvl].InputMapValid = 0;
++      sgmt->Maps[clvl].OutputMapValid = 0;
++      sgmt->Maps[clvl].SentChanges = 0;
++
++      if (sgmt->Level == cmRail->TopLevel)    /* connection to leader */
++      {
++          ASSERT (sgmt->Sgmt == 0);
++          ASSERT (cmRail->Role == CM_ROLE_SUBORDINATE);
++
++          if (cmRail->Levels[clvl].SubTreeMapValid) /* already got a subtree map to send up */
++          {
++              statemap_setmap (sgmt->Maps[clvl].OutputMap, cmRail->Levels[clvl].SubTreeMap);
++              sgmt->Maps[clvl].OutputMapValid = 1;
++
++              statemap_clearchanges (cmRail->Levels[clvl].SubTreeMap);
++          }
++      }
++      else                                    /* connection to subordinate */
++      {
++          ASSERT (sgmt->Sgmt != cmRail->Levels[sgmt->Level].MySgmt);
++
++          if (cmRail->Levels[clvl].GlobalMapValid) /* already got a global map to broadcast */
++          {
++              statemap_setmap (sgmt->Maps[clvl].OutputMap, cmRail->Levels[clvl].GlobalMap);
++              sgmt->Maps[clvl].OutputMapValid = 1;
++          }
++      }
++    }
++    
++    /* Initialise sequence counters */
++    sgmt->MsgSeq = sgmt->AckSeq = 0;
++    sgmt->MsgAcked = 1;                       /* ready to send a new sequenced message */
++   
++    sgmt->State      = CM_SGMT_PRESENT;
++    sgmt->NodeId     = NodeId;
++    sgmt->UpdateTick = lbolt;
++    sgmt->Timestamp  = Timestamp;
++}
++
++static void
++StartSubTreeDiscovery (CM_RAIL *cmRail, CM_SGMT *sgmt)
++{
++    sgmt->State = CM_SGMT_WAITING;
++    sgmt->UpdateTick = lbolt;
++    sgmt->WaitingTick = lbolt;
++
++    if (sgmt->Level > 0)
++      __Schedule_Discovery (cmRail);
++}
++
++void
++StartSubordinateDiscovery (CM_RAIL *cmRail)
++{
++    int       i;
++    int       lvl = cmRail->TopLevel - 1;
++    CM_LEVEL *level = &cmRail->Levels[lvl];
++
++    ASSERT (lvl >= 0 && lvl < cmRail->NumLevels);
++
++    for (i = 0; i < level->NumSegs; i++)
++    {
++        CM_SGMT *sgmt = &level->Sgmts[i];
++      
++      if (i != level->MySgmt)         /* No-one should connect here */
++          StartSubTreeDiscovery (cmRail, sgmt);
++    }
++}
++
++void
++StartLeaderDiscovery (CM_RAIL *cmRail)
++{
++    int       i;
++    int       clvl;
++    CM_LEVEL *level = &cmRail->Levels[cmRail->TopLevel];
++
++    ASSERT (cmRail->TopLevel < cmRail->NumLevels);
++
++    for (clvl = cmRail->TopLevel; clvl < cmRail->NumLevels; clvl++)
++    {
++        cmRail->Levels[clvl].GlobalMapValid = 0;
++      cmRail->Levels[clvl].SubTreeMapValid = 0;
++        level->SubordinateMapValid[clvl] = 0;
++    }
++
++    for (i = 0; i < level->NumSegs; i++)
++    {
++        CM_SGMT *sgmt = &level->Sgmts[i];
++      
++      sgmt->State = CM_SGMT_ABSENT;
++    }
++
++    cmRail->DiscoverStartTick = lbolt;
++    cmRail->Role = CM_ROLE_LEADER_CANDIDATE;
++   
++    __Schedule_Discovery (cmRail);
++}
++
++static void
++RaiseTopLevel (CM_RAIL *cmRail)
++{
++    ASSERT (cmRail->NumLevels != 0);
++    ASSERT (cmRail->TopLevel < cmRail->NumLevels);
++
++    CPRINTF2 (2, "%s: RaiseTopLevel %d\n", cmRail->Rail->Name, cmRail->TopLevel + 1);
++
++    if (++cmRail->TopLevel == cmRail->NumLevels)      /* whole machine leader? */
++       cmRail->Role = CM_ROLE_LEADER;
++    else
++       StartLeaderDiscovery (cmRail);         /* look for my leader */
++
++    StartSubordinateDiscovery (cmRail);               /* and any direct subordinates */
++}
++
++static void
++LowerTopLevel (CM_RAIL *cmRail, int lvl)
++{
++    ASSERT (cmRail->NumLevels != 0);
++    ASSERT (lvl < cmRail->NumLevels);
++
++    CPRINTF2 (2, "%s: LowerTopLevel %d\n", cmRail->Rail->Name, lvl);
++
++    if (lvl == 0)
++      cmRail->Timestamp = lbolt;
++
++    cmRail->TopLevel = lvl;
++
++    StartLeaderDiscovery (cmRail);            /* look for my leader */
++}
++
++static int
++IShouldLead (CM_RAIL *cmRail, CM_MSG *msg)
++{
++    /* NB, this function MUST be consistently calculated on any nodes, just
++     * from the info supplied in the message.  Otherwise leadership
++     * arbitration during concurrent discovery will fail.
++     */
++    return (cmRail->NodeId < msg->Hdr.NodeId);
++}
++
++static int
++SumCheck (CM_MSG *msg)
++{
++    CM_HDR   *hdr   = &msg->Hdr;
++    uint16_t  sum   = hdr->Checksum;
++    uint16_t  nmaps = hdr->NumMaps;
++
++    if (nmaps > CM_MSG_MAXMAPS) {
++      printk ("SumCheck: nmaps %d > CM_MSG_MAXMAPS\n", nmaps);
++      return 0;
++    }
++    
++    if ((hdr->Type != CM_MSG_TYPE_HEARTBEAT) && nmaps != 0) {
++      printk ("SumCheck: type(%d) not HEARTBEAT and nmaps(%d) != 0\n", hdr->Type, nmaps);
++      return 0;
++    }
++
++    hdr->Checksum = 0;
++    
++    if (CheckSum ((char *)msg + CM_MSG_BASE(nmaps), CM_MSG_SIZE(nmaps)) != sum) {
++      printk ("SumCheck: checksum failed %x %x\n", CheckSum ((char *)msg + CM_MSG_BASE(nmaps), CM_MSG_SIZE(nmaps)), sum);
++
++      return 0;
++    }
++      
++    return 1;
++}
++
++static void
++ProcessMessage (EP_RAIL *rail, void *arg, void *msgbuf)
++{
++    CM_RAIL      *cmRail = (CM_RAIL *) arg;
++    CM_MSG         *msg    = (CM_MSG *) msgbuf;
++    CM_HDR         *hdr    = &msg->Hdr;
++    int             lvl;
++    int             sidx;
++    CM_LEVEL       *level;
++    CM_SGMT        *sgmt;
++    bitmap_t        seg;
++    int             i;
++    int            delay;
++    static long    tlast;
++    static int     count;
++
++    /* Poll the message Version field until the message has completely
++     * arrived in main memory. */
++    for (delay = 1; hdr->Version == EP_SYSTEMQ_UNRECEIVED && delay < EP_SYSTEMQ_UNRECEIVED_TLIMIT; delay <<= 1)
++      DELAY (delay);
++
++    /* Display a message every 60 seconds if we see an "old" format message */
++    if (hdr->Version == EP_SYSTEMQ_UNRECEIVED && (((lbolt - tlast) > 60*HZ) ? (count = 0) : ++count) < 1)
++    {
++      printk ("%s: received old protocol message (type %d from node %d)\n", cmRail->Rail->Name, 
++              ((uint8_t *) msg)[20], ((uint16_t *) msg)[4]);
++
++      tlast = lbolt;
++      goto finished;
++    }
++
++    if (hdr->Version != CM_MSG_VERSION || hdr->ParamHash != cmRail->ParamHash || hdr->MachineId != MachineId)
++    {
++      CPRINTF8 (1, "%s: invalid message : Version %08x (%08x) ParamHash %08x (%08x) MachineId %04x (%04x) Nodeid %d\n", cmRail->Rail->Name,
++                hdr->Version, CM_MSG_VERSION, hdr->ParamHash, cmRail->ParamHash, hdr->MachineId, MachineId, hdr->NodeId);
++      goto finished;
++    }
++
++    if (!SumCheck (msg))
++    {
++      printk ("%s: checksum failed on msg from %d?\n", cmRail->Rail->Name, hdr->NodeId);
++      goto finished;
++    }
++    
++    if (hdr->NodeId == cmRail->NodeId)                /* ignore my own broadcast */       
++    {
++      CPRINTF3 (6, "%s: node %d type %d: ignored (MESSAGE FROM ME)\n", 
++                cmRail->Rail->Name, hdr->NodeId, hdr->Type);
++
++      if (hdr->Type != CM_MSG_TYPE_DISCOVER_LEADER && hdr->Type != CM_MSG_TYPE_RESOLVE_LEADER)
++          printk ("%s: node %d type %d: ignored (MESSAGE FROM ME)\n", 
++                  cmRail->Rail->Name, hdr->NodeId, hdr->Type);
++      goto finished;
++    }
++
++    lvl = hdr->Level;
++    level = &cmRail->Levels[lvl];
++
++    if (BrokenLevel != -1 && (lvl >= ((BrokenLevel >> (cmRail->Rail->Number*4)) & 0xf)))                      /* Simulate broken network? */
++       goto finished;
++    
++    if (lvl >= cmRail->NumLevels ||           /* from outer space  */
++      hdr->NodeId < level->MinNodeId ||       /* from outside this level's subtree */
++      hdr->NodeId >= level->MinNodeId + level->NumNodes)
++    {
++      printk ("%s: lvl %d node %d type %d: ignored (%s)\n", 
++              cmRail->Rail->Name, lvl, hdr->NodeId, hdr->Type, 
++              lvl >= cmRail->NumLevels ? "level too big for machine" : "outside subtree");
++      goto finished;
++    }
++
++    sidx = SegmentNo (cmRail, hdr->NodeId, lvl);
++    sgmt = &level->Sgmts[sidx];
++    
++    switch (hdr->Type)
++    {
++    case CM_MSG_TYPE_RESOLVE_LEADER:
++      if (lvl >= cmRail->TopLevel)
++      {
++          CPRINTF4 (6, "%s: lvl %d sidx %d node %d RESOLVE_LEADER: ignored (above my level)\n", 
++                    cmRail->Rail->Name, lvl, sidx, hdr->NodeId);
++          break;
++      }
++
++      /* someone else thinks they lead at the same level as me */
++      CPRINTF4 (1, "%s: lvl %d sidx %d node %d RESOLVE_LEADER: !REJOIN (putsch)\n", 
++                cmRail->Rail->Name, lvl, sidx, hdr->NodeId);
++      
++      printk ("%s: lvl %d sidx %d node %d RESOLVE_LEADER: !REJOIN (putsch)\n", 
++              cmRail->Rail->Name, lvl, sidx, hdr->NodeId);
++      
++
++      SendMessage (cmRail, hdr->NodeId, lvl, CM_MSG_TYPE_REJOIN);
++      break;
++      
++    case CM_MSG_TYPE_DISCOVER_LEADER:
++      if (lvl > cmRail->TopLevel)
++      {
++          CPRINTF4 (6, "%s: lvl %d sidx %d node %d DISCOVER_LEADER: ignored (above my level)\n", 
++                    cmRail->Rail->Name, lvl, sidx, hdr->NodeId);
++          break;
++      }
++
++      if (sidx == level->MySgmt)              /* someone I led thinks they lead some of my subtrees */
++      {
++          CPRINTF4 (1, "%s: lvl %d sidx %d node %d DISCOVER_LEADER: !REJOIN (putsch)\n", 
++                    cmRail->Rail->Name, lvl, sidx, hdr->NodeId);
++
++          printk ("%s: lvl %d sidx %d node %d DISCOVER_LEADER: !REJOIN (putsch)\n", 
++                  cmRail->Rail->Name, lvl, sidx, hdr->NodeId);
++
++          SendMessage (cmRail, hdr->NodeId, hdr->Level, CM_MSG_TYPE_REJOIN);
++          break;
++      }       
++
++      if (lvl < cmRail->TopLevel)                     /* I'm the leader of this level */
++      {
++          if (sgmt->State == CM_SGMT_PRESENT &&       /* someone thinks someone I lead is dead */
++              sgmt->NodeId != hdr->NodeId)
++          {
++              /* My subordinate's death could be noticed by one of her peers
++               * before I do.  If she _is_ dead, I'll notice before long and
++               * NOTIFY this discover.  If this discover completes before I
++               * detect my subordinate's death, the discovering node will
++               * try to take over from me, and then I'll RESET her.
++               */
++              CPRINTF4 (6, "%s: lvl %d sidx %d node %d DISCOVER_LEADER: ignored (got established subordinate)\n", 
++                        cmRail->Rail->Name, lvl, sidx, hdr->NodeId);
++              return;
++          }
++
++          if (sgmt->State != CM_SGMT_PRESENT || /* New connection */
++              sgmt->Timestamp != hdr->Timestamp) /* new incarnation */
++              StartConnecting (cmRail, sgmt, hdr->NodeId, hdr->Timestamp);
++        
++          CPRINTF4 (2, "%s: lvl %d sidx %d node %d DISCOVER_LEADER: !NOTIFY)\n", 
++                    cmRail->Rail->Name, lvl, sidx, hdr->NodeId);
++        
++          SendToSgmt (cmRail, sgmt, CM_MSG_TYPE_NOTIFY);
++          break;
++      }
++
++      ASSERT (lvl == cmRail->TopLevel);
++
++      if (cmRail->Role == CM_ROLE_SUBORDINATE)
++      {
++          /* I think my leader is alive, in which case she'll NOTIFY this
++           * DISCOVER.  If she's dead, I'll start to become a leader
++           * candidate and handle this appropriately.
++           */
++          CPRINTF3 (6, "%s: lvl %d node %d DISCOVER: ignored (I'm a subordinate)\n", 
++                    cmRail->Rail->Name, lvl, hdr->NodeId);
++          break;
++      }
++       
++      ASSERT (cmRail->Role == CM_ROLE_LEADER_CANDIDATE);
++
++      /* A peer at this level is bidding for leadership along with me */
++      if (IShouldLead (cmRail, msg))
++      {
++          CPRINTF3 (6, "%s: lvl %d node %d DISCOVER: but I should lead\n", 
++                    cmRail->Rail->Name, lvl, hdr->NodeId);
++
++          /* So there _is_ someone there; She'll be seeing my DISCOVER
++           * messages and extending her discovery period, so that when I
++           * become leader, I'll NOTIFY her.  In the meantime I'll flag her
++           * activity, so she remains WAITING.
++           */
++          sgmt->UpdateTick = lbolt;
++          break;
++      }
++       
++      /* Defer to sender... */
++      CPRINTF3 (6, "%s: lvl %d node %d DISCOVER: delaying me becoming leader\n", 
++                cmRail->Rail->Name, lvl, hdr->NodeId);
++       
++      StartLeaderDiscovery (cmRail);
++      break;
++
++    case CM_MSG_TYPE_DISCOVER_SUBORDINATE:
++      if (lvl <= cmRail->TopLevel)
++      {
++          CPRINTF3 (6, "%s: lvl %d node %d DISCOVER_SUBORDINATE: ignored (from my subtree)\n", 
++                    cmRail->Rail->Name, lvl, hdr->NodeId);
++          break;
++      }
++       
++      if (cmRail->Role != CM_ROLE_LEADER_CANDIDATE)
++      {
++          CPRINTF3 (6, "%s: lvl %d node %d DISCOVER_SUBORDINATE: ignored (I'm not looking for a leader)\n", 
++                    cmRail->Rail->Name, lvl, hdr->NodeId);
++          break;
++      }
++       
++      if (hdr->Level > cmRail->BroadcastLevel && AFTER (lbolt, cmRail->BroadcastLevelTick + EP_WITHDRAW_TIMEOUT))
++      {
++          CPRINTF3 (6, "%s: lvl %d node %d DISCOVER_SUBORDINATE: ignored (broadcast level too low)\n",
++                    cmRail->Rail->Name, lvl, hdr->NodeId);
++          break;
++      }
++
++      CPRINTF3 (2, "%s: lvl %d node %d DISCOVER_SUBORDINATE: !IMCOMING\n", 
++                cmRail->Rail->Name, lvl, hdr->NodeId);
++       
++      SendMessage (cmRail, hdr->NodeId, hdr->Level, CM_MSG_TYPE_IMCOMING);
++      break;
++
++    case CM_MSG_TYPE_IMCOMING:
++      if (lvl > cmRail->TopLevel ||           /* from peer or node above me */
++          sgmt->State == CM_SGMT_PRESENT ||   /* already got a subtree */
++          sgmt->State == CM_SGMT_ABSENT)      /* already written off this subtree */
++      {
++          CPRINTF4 (2, "%s: lvl %d sidx %d node %d IMCOMING: ignored\n", cmRail->Rail->Name, lvl, sidx, hdr->NodeId);
++          break;
++      }
++
++      CPRINTF4 (2, "%s: lvl %d sidx %d node %d IMCOMING: waiting...\n", cmRail->Rail->Name, lvl, sidx, hdr->NodeId);
++
++      sgmt->State = CM_SGMT_COMING;
++      sgmt->UpdateTick = lbolt;
++      break;
++       
++    case CM_MSG_TYPE_NOTIFY:
++      if (cmRail->Role != CM_ROLE_LEADER_CANDIDATE || /* I'm not looking for a leader */
++          lvl != cmRail->TopLevel)            /* at this level */
++      {
++          /* If this person really should be my leader, my existing leader
++           * will time out, and I'll discover this one. */
++          CPRINTF4 (2, "%s: lvl %d node %d NOTIFY: ignored (%s)\n", 
++                    cmRail->Rail->Name, lvl, hdr->NodeId,
++                    lvl < cmRail->TopLevel ? "already leader" : 
++                    lvl > cmRail->TopLevel ? "lvl too high" : "already subordinate");
++          break;
++      }
++
++      CPRINTF3 (2, "%s: lvl %d node %d NOTIFY: becoming subordinate\n", 
++                cmRail->Rail->Name, lvl, hdr->NodeId);
++       
++      cmRail->Role = CM_ROLE_SUBORDINATE;             /* Now I've found my level */
++      StartConnecting (cmRail, &level->Sgmts[0], hdr->NodeId, hdr->Timestamp);
++      break;
++
++    case CM_MSG_TYPE_HEARTBEAT:
++      if (lvl > cmRail->TopLevel)
++      {
++          CPRINTF3 (2, "%s: lvl %d node %d H/BEAT: ignored (lvl too high)\n", 
++                    cmRail->Rail->Name, lvl, hdr->NodeId);
++          break;
++      }
++
++      if (lvl == cmRail->TopLevel)                    /* heartbeat from my leader */
++      {
++          if (cmRail->Role == CM_ROLE_LEADER_CANDIDATE) /* but I've not got one */
++          {
++              /* I'm probably a new incarnation of myself; I'll keep doing
++               * discovery until my previous existence's leader NOTIFY's me.
++               * If I was this node's leader, she'll time me out (I'm not
++               * sending heartbeats to her) and we'll fight it out for
++               * leadership. */
++              CPRINTF3 (2, "%s: lvl %d node %d H/BEAT ignored (no leader)\n", 
++                        cmRail->Rail->Name, lvl, hdr->NodeId);
++              break;
++          }
++          sidx = 0;
++          sgmt = &level->Sgmts[0];
++      }
++      
++      if (sgmt->State != CM_SGMT_PRESENT ||   /* not fully connected with this guy */
++          sgmt->NodeId != hdr->NodeId ||      /* someone else impersonating my peer */
++          sgmt->Timestamp != hdr->Timestamp)  /* new incarnation of my peer */
++      {
++          CPRINTF4 (1, "%s: lvl %d sidx %d node %d H/BEAT: !REJOIN\n", 
++                    cmRail->Rail->Name, lvl, sidx, hdr->NodeId);
++        
++          printk ("%s: lvl %d sidx %d node %d H/BEAT: !REJOIN %s\n",
++                  cmRail->Rail->Name, lvl, sidx, hdr->NodeId,
++                  sgmt->State != CM_SGMT_PRESENT ? "not present" :
++                  sgmt->NodeId != hdr->NodeId ? "someone else" : "new incarnation");
++          
++          SendMessage (cmRail, hdr->NodeId, hdr->Level, CM_MSG_TYPE_REJOIN);
++          break;
++      }
++
++      if (!((hdr->Seq == sgmt->AckSeq) ||     /* NOT duplicate message or */
++            (hdr->Seq == (CM_SEQ)(sgmt->AckSeq + 1))) || /* expected message */
++          !((hdr->AckSeq == sgmt->MsgSeq) ||  /* NOT expected ack or */
++            (hdr->AckSeq == (CM_SEQ)(sgmt->MsgSeq - 1)))) /* duplicate ack */
++      {
++          CPRINTF9 (1, "%s: lvl %d sidx %d node %d type %d: H/BEAT !REJOIN (out-of-seq) M(%d,a%d) S%d,A%d\n", 
++                    cmRail->Rail->Name, lvl, sidx, hdr->NodeId, hdr->Type, 
++                    (int)hdr->Seq, (int)hdr->AckSeq, (int)sgmt->MsgSeq, (int)sgmt->AckSeq);
++       
++          printk ("%s: lvl %d sidx %d node %d type %d: H/BEAT !REJOIN (out-of-seq) M(%d,a%d) S%d,A%d\n", 
++                  cmRail->Rail->Name, lvl, sidx, hdr->NodeId, hdr->Type, 
++                  (int)hdr->Seq, (int)hdr->AckSeq, (int)sgmt->MsgSeq, (int)sgmt->AckSeq);
++       
++          SendMessage (cmRail, hdr->NodeId, hdr->Level, CM_MSG_TYPE_REJOIN);
++          break;
++      }
++
++      IncrStat (cmRail, HeartbeatsRcvd);
++
++      sgmt->UpdateTick = lbolt;
++      sgmt->SendMaps = 1;
++
++      if (sgmt->MsgSeq == hdr->AckSeq)                /* acking current message */
++          sgmt->MsgAcked = 1;                 /* can send the next one */
++
++      if (hdr->Seq == sgmt->AckSeq)           /* discard duplicate (or NULL heartbeat) */
++      {
++          CPRINTF6 (6, "%s: lvl %d sidx %d node %d type %d: %s H/BEAT\n", 
++                    cmRail->Rail->Name, lvl, sidx, hdr->NodeId, hdr->Type,
++                    hdr->NumMaps == 0 ? "null" : "duplicate");
++          break;
++      }
++
++      CPRINTF7 (6, "%s: lvl %d sidx %d node %d type %d: seq %d maps %d H/BEAT\n", 
++                cmRail->Rail->Name, lvl, sidx, hdr->NodeId, hdr->Type, hdr->Seq, hdr->NumMaps);
++
++      sgmt->AckSeq = hdr->Seq;                        /* ready to receive next one */
++       
++      for (i = 0; i < hdr->NumMaps; i++)
++      {
++          CM_STATEMAP_ENTRY *map  = &msg->Payload.Statemaps[CM_MSG_MAP(i)];
++          int                clvl = map->level;
++          
++          if (clvl < 0)                       /* end of message */
++              break;
++
++          if (clvl < sgmt->Level)             /* bad level */
++          {
++              CPRINTF6 (1, "%s: lvl %d sidx %d node %d type %d: H/BEAT !REJOIN (bad clevel %d)\n", 
++                        cmRail->Rail->Name, lvl, sidx, hdr->NodeId, hdr->Type, clvl);
++
++              SendMessage (cmRail, hdr->NodeId, hdr->Level, CM_MSG_TYPE_REJOIN);
++              goto finished;
++          }
++
++          if (map->offset == STATEMAP_NOMORECHANGES) /* end of atomic changes */
++          {
++              if (!sgmt->Maps[clvl].InputMapValid || /* not set InputMap yet */
++                  statemap_changed (sgmt->Maps[clvl].CurrentInputMap)) /* previously applied changes */
++              {
++                  CPRINTF3 (4, "%s: received new clvl %d map from %d\n", cmRail->Rail->Name, clvl, sgmt->NodeId);
++
++                  statemap_setmap (sgmt->Maps[clvl].InputMap, sgmt->Maps[clvl].CurrentInputMap);
++                  sgmt->Maps[clvl].InputMapValid = 1;
++
++                  statemap_clearchanges (sgmt->Maps[clvl].CurrentInputMap);
++              }
++              continue;
++          }
++          
++          seg = ((bitmap_t)map->seg[0])
++              | (((bitmap_t)map->seg[1]) << 16)
++#if (BT_ULSHIFT == 6)
++              | (((bitmap_t)map->seg[2]) << 32)
++              | (((bitmap_t)map->seg[3]) << 48)
++#elif (BT_ULSHIFT != 5)
++#error "Bad value for BT_ULSHIFT"
++#endif
++              ;
++          statemap_setseg (sgmt->Maps[clvl].CurrentInputMap, map->offset, seg);
++      }
++      break;
++
++    case CM_MSG_TYPE_REJOIN:
++      CPRINTF5 (1, "%s: lvl %d sidx %d node %d type %d: REJOIN\n",
++                cmRail->Rail->Name, lvl, sidx, hdr->NodeId, hdr->Type);
++      printk ("%s: lvl %d sidx %d node %d type %d: REJOIN\n", 
++              cmRail->Rail->Name, lvl, sidx, hdr->NodeId, hdr->Type);
++
++      LowerTopLevel (cmRail, 0);
++
++      IncrStat (cmRail, RejoinRequest);
++      break;
++
++    default:
++      printk ("%s: lvl=%d unknown message type %d\n", cmRail->Rail->Name, lvl, hdr->Type);
++      break;
++    }
++ finished:
++    hdr->Version = EP_SYSTEMQ_UNRECEIVED;
++}
++
++static void
++PollInputQueues (CM_RAIL *cmRail)
++{
++    ep_poll_inputq (cmRail->Rail, cmRail->IntrQueue, 0, ProcessMessage, cmRail);
++    ep_poll_inputq (cmRail->Rail, cmRail->PolledQueue, 0, ProcessMessage, cmRail);
++}
++
++static void
++IntrQueueCallback (EP_RAIL *rail, void *arg)
++{
++    CM_RAIL *cmRail = (CM_RAIL *) arg;
++    unsigned long flags;
++
++    /* If the lock is held, then don't bother spinning for it, 
++     * since the messages will be received at this, or the
++     * next heartbeat */
++    local_irq_save (flags);
++    if (spin_trylock (&cmRail->Lock))
++    {
++      if (AFTER (lbolt, cmRail->NextRunTime + MSEC2TICKS(CM_TIMER_SCHEDULE_TIMEOUT)))
++          printk ("%s: heartbeat timer stuck - scheduled\n", cmRail->Rail->Name);
++      else
++          ep_poll_inputq (rail, cmRail->IntrQueue, 0, ProcessMessage, cmRail);
++      spin_unlock (&cmRail->Lock);
++    }
++    local_irq_restore (flags);
++}
++
++char *
++sprintClPeers (char *str, CM_RAIL *cmRail, int clvl)
++{
++   int clLo     = cmRail->Levels[clvl].MinNodeId;
++   int clHi     = clLo + cmRail->Levels[clvl].NumNodes - 1;
++   int subClLo  = (clvl == 0) ? cmRail->NodeId : cmRail->Levels[clvl - 1].MinNodeId;
++   int subClHi  = subClLo + ((clvl == 0) ? 0 : cmRail->Levels[clvl - 1].NumNodes - 1);
++   
++   if (subClHi == clHi)
++      sprintf (str, "[%d-%d]", clLo, subClLo - 1);
++   else if (subClLo == clLo)
++      sprintf (str, "[%d-%d]", subClHi + 1, clHi);
++   else
++      sprintf (str, "[%d-%d][%d-%d]", clLo, subClLo - 1, subClHi + 1, clHi);
++
++   return (str);
++}
++
++static void
++RestartComms (CM_RAIL *cmRail, int clvl)
++{
++    int             base;
++    int             nodeId;
++    int             lstat;
++    int             numClNodes;
++    int             subClMin;
++    int             subClMax;
++    int             myClId;
++    int             thisClId;
++    
++    myClId     = ClusterIds (cmRail, clvl, &subClMin, &subClMax);
++    base       = myClId * CM_GSTATUS_BITS;
++    numClNodes = cmRail->Levels[clvl].NumNodes;
++
++    statemap_setbits (cmRail->Levels[clvl].LocalMap, base, 
++                    CM_GSTATUS_CLOSING | CM_GSTATUS_MAY_START | CM_GSTATUS_RESTART, CM_GSTATUS_BITS);
++    cmRail->Levels[clvl].Restarting = 1;
++
++    if (cmRail->Levels[clvl].Online)
++    {
++      cmRail->Levels[clvl].Online = 0;
++      
++      for (thisClId = 0; thisClId < numClNodes; thisClId++)
++      {
++          if (thisClId == subClMin)   /* skip sub-cluster; it's just someone in this cluster */
++          {                           /* that wants me to restart */
++              thisClId = subClMax;
++              continue;
++          }
++          
++          nodeId = cmRail->Levels[clvl].MinNodeId + thisClId;
++          base   = thisClId * CM_GSTATUS_BITS;
++          lstat  = statemap_getbits (cmRail->Levels[clvl].LocalMap, base, CM_GSTATUS_BITS);
++          
++          if ((lstat & CM_GSTATUS_ACK_MASK) == CM_GSTATUS_MAY_RUN)
++          {
++              switch (ep_disconnect_node (cmRail->Rail, nodeId))
++              {
++              case EP_NODE_CONNECTING:
++                  /* gstat must == RUNNING */
++                  cmRail->Levels[clvl].Connected--;
++                  break;
++              case EP_NODE_DISCONNECTED:
++                  /* CLOSING || STARTING || (lstat & RESTART) */
++                  break;
++              }
++          }
++      }
++    }
++}
++
++static void
++UpdateGlobalStatus (CM_RAIL *cmRail)
++{
++    char            clNodeStr[32];                            /* [%d-%d][%d-%d] */
++    int             nodeId;
++    int             offset;
++    int             base;
++    bitmap_t        gstat;
++    bitmap_t        lgstat;
++    bitmap_t        lstat;
++    int             clvl;
++    int             numClNodes;
++    int             subClMin;
++    int             subClMax;
++    int             myClId;
++    int             thisClId;
++    int             lastClId;
++
++    for (clvl = 0; clvl < cmRail->NumLevels; clvl++)
++    {
++      if (!cmRail->Levels[clvl].GlobalMapValid || /* not got the global map yet */
++          !statemap_changed (cmRail->Levels[clvl].GlobalMap)) /* no changes to respond to */
++      {
++          CPRINTF2 (6, "%s: Got invalid or unchanged clvl %d global map\n", cmRail->Rail->Name, clvl);
++          continue;
++      }
++      
++      CPRINTF2 (5, "%s: Got valid changed clvl %d global map\n", cmRail->Rail->Name, clvl);
++      
++      lastClId = -1;
++      myClId = ClusterIds (cmRail, clvl, &subClMin, &subClMax);
++      numClNodes = cmRail->Levels[clvl].NumNodes;
++      
++      while ((offset = statemap_findchange (cmRail->Levels[clvl].GlobalMap, &gstat, 1)) >= 0)
++      {
++          /*
++           * Check every node that this segment covers - however
++           * if the last node we checked in the previous segmemt
++           * is also the first node in this segment, then skip
++           * it.
++           */
++          if ((thisClId = (offset/CM_GSTATUS_BITS)) == lastClId)
++              thisClId++;
++          lastClId = (offset + BT_NBIPUL - 1)/CM_GSTATUS_BITS;
++          
++          /* check each node that might have changed */
++          for ( ; thisClId <= lastClId && thisClId < numClNodes; thisClId++)
++          {
++              base = thisClId * CM_GSTATUS_BITS;
++              nodeId = cmRail->Levels[clvl].MinNodeId + thisClId;
++
++              if (thisClId >= subClMin && thisClId <= subClMax) /* skip sub-cluster */
++                  continue;
++
++              /* This isn't me; I need to sense what this node is driving
++               * (just the starting and running bits) and respond
++               * appropriately...
++               */
++              lgstat = statemap_getbits (cmRail->Levels[clvl].LastGlobalMap, base, CM_GSTATUS_BITS) & CM_GSTATUS_STATUS_MASK;
++              gstat  = statemap_getbits (cmRail->Levels[clvl].GlobalMap,     base, CM_GSTATUS_BITS) & CM_GSTATUS_STATUS_MASK;
++
++              if (lgstat == gstat)            /* no change in peer state */
++                  continue;
++
++              CPRINTF5 (3, "%s: Node %d: lgstat %s, gstat %s, lstat %s\n", cmRail->Rail->Name, nodeId,
++                        GlobalStatusString (cmRail->Levels[clvl].LastGlobalMap, thisClId),
++                        GlobalStatusString (cmRail->Levels[clvl].GlobalMap, thisClId),
++                        GlobalStatusString (cmRail->Levels[clvl].LocalMap, thisClId));
++
++              /* What I'm currently driving as my acknowledgement */
++              lstat = statemap_getbits (cmRail->Levels[clvl].LocalMap, base, CM_GSTATUS_BITS);
++
++              switch (gstat)
++              {
++              case CM_GSTATUS_STARTING:
++                  if ((lgstat == CM_GSTATUS_ABSENT || lgstat == CM_GSTATUS_CLOSING) && lstat == CM_GSTATUS_MAY_START)
++                  {
++                      CPRINTF2 (1, "%s: ===================node %d STARTING\n", cmRail->Rail->Name, nodeId);
++                      
++                      ASSERT (cmRail->Rail->Nodes[nodeId].State == EP_NODE_DISCONNECTED);
++
++                      statemap_setbits (cmRail->Levels[clvl].LocalMap, base, CM_GSTATUS_MAY_RUN, CM_GSTATUS_BITS);
++                      continue;
++                  }
++                  break;
++                  
++              case CM_GSTATUS_RUNNING:
++                  if ((lgstat == CM_GSTATUS_ABSENT   && lstat == CM_GSTATUS_MAY_START) ||
++                      (lgstat == CM_GSTATUS_STARTING && lstat == CM_GSTATUS_MAY_RUN))
++                  {
++                      CPRINTF3 (1, "%s: ===================node %d%s RUNNING\n", cmRail->Rail->Name, nodeId,
++                                lgstat == CM_GSTATUS_ABSENT ? " Already" : "");
++
++                      ASSERT (cmRail->Rail->Nodes[nodeId].State == EP_NODE_DISCONNECTED);
++
++                      if (cmRail->Levels[clvl].Online)
++                      {
++                          ep_connect_node (cmRail->Rail, nodeId);
++
++                          cmRail->Levels[clvl].Connected++;
++                      }
++
++                      statemap_setbits (cmRail->Levels[clvl].LocalMap, base, CM_GSTATUS_MAY_RUN, CM_GSTATUS_BITS);
++                      continue;
++                  }
++                  break;
++                  
++              case CM_GSTATUS_CLOSING:
++                  CPRINTF4 (1, "%s: ===================node %d CLOSING%s%s\n", cmRail->Rail->Name, nodeId,
++                            (lstat & CM_GSTATUS_RESTART) ? " for Restart" : "",
++                            cmRail->Levels[clvl].Online ? "" : " (offline)");
++
++                  if ((lstat & CM_GSTATUS_ACK_MASK) == CM_GSTATUS_MAY_RUN)
++                  {
++                      switch (ep_disconnect_node (cmRail->Rail, nodeId))
++                      {
++                      case EP_NODE_CONNECTING:
++                          cmRail->Levels[clvl].Connected--;
++                          /* DROPTHROUGH */
++                      case EP_NODE_DISCONNECTED:
++                          lstat = CM_GSTATUS_MAY_START;
++                          break;
++                      }
++                  }
++
++                  if ((lstat & CM_GSTATUS_ACK_MASK) == CM_GSTATUS_MAY_START) /* clear restart if we've disconnected */
++                      statemap_setbits (cmRail->Levels[clvl].LocalMap, base, CM_GSTATUS_MAY_START, CM_GSTATUS_BITS);
++                  continue;
++                  
++              default:
++                  break;
++              }
++
++              /* "unexpected" state change forces me to ask her to restart */
++              if (! (lstat & CM_GSTATUS_RESTART))             /* not requesting restart already */
++              {
++                  CPRINTF5 (1, "%s: ===================node %d %s, old %s new %s\n", cmRail->Rail->Name, nodeId,
++                            (gstat == CM_GSTATUS_ABSENT)  ? "ABSENT" : "REQUEST RESTART", 
++                            GlobalStatusString (cmRail->Levels[clvl].LastGlobalMap, thisClId),
++                            GlobalStatusString (cmRail->Levels[clvl].GlobalMap, thisClId));
++
++                  /* request restart */
++                  if (cmRail->Levels[clvl].Online && lstat == CM_GSTATUS_MAY_RUN)
++                  {
++                      switch (ep_disconnect_node (cmRail->Rail, nodeId))
++                      {
++                      case EP_NODE_CONNECTING:
++                          cmRail->Levels[clvl].Connected--;
++                          /* DROPTHROUGH */
++                      case EP_NODE_DISCONNECTED:
++                          lstat = CM_GSTATUS_MAY_START;
++                          break;
++                      }
++                  }
++
++                  statemap_setbits (cmRail->Levels[clvl].LocalMap, base, lstat | CM_GSTATUS_RESTART, CM_GSTATUS_BITS);
++                  continue;
++              }
++
++              continue;
++          }
++      }
++    
++      /* Now check myself - see what everyone else thinks I'm doing */
++      base   = myClId * CM_GSTATUS_BITS;
++      lstat  = statemap_getbits (cmRail->Levels[clvl].LocalMap,  base, CM_GSTATUS_BITS);
++      gstat  = statemap_getbits (cmRail->Levels[clvl].GlobalMap, base, CM_GSTATUS_BITS);
++      lgstat = statemap_getbits (cmRail->Levels[clvl].LastGlobalMap, base, CM_GSTATUS_BITS);
++
++      if (lgstat == gstat)                    /* my state in this cluster hasn't changed */
++      {
++          CPRINTF3 (6, "%s: my clvl %d global status unchanged from %s\n", cmRail->Rail->Name,
++                    clvl, GlobalStatusString (cmRail->Levels[clvl].GlobalMap, myClId));
++          goto all_done;
++      }
++
++      if ((gstat & CM_GSTATUS_RESTART) != 0)  /* someone wants me to restart */
++      {
++          if ((lstat & CM_GSTATUS_STATUS_MASK) == CM_GSTATUS_CLOSING) /* I'm already restarting */
++              goto all_done;
++          
++          CPRINTF2 (1, "%s: ===================RESTART REQUEST from %s\n", cmRail->Rail->Name,
++                    sprintClPeers (clNodeStr, cmRail, clvl));
++          
++          printk ("%s: Restart Request from %s\n", cmRail->Rail->Name,
++                  sprintClPeers (clNodeStr, cmRail, clvl));
++          
++          RestartComms (cmRail, clvl);
++          goto all_done;
++      }
++      
++      CPRINTF6 (5, "%s: clvl %d: lgstat %s gstat %s, lstat %s%s\n", cmRail->Rail->Name, clvl,
++                GlobalStatusString (cmRail->Levels[clvl].LastGlobalMap, myClId),
++                GlobalStatusString (cmRail->Levels[clvl].GlobalMap, myClId),
++                GlobalStatusString (cmRail->Levels[clvl].LocalMap, myClId),
++                (gstat != lstat) ? " (IGNORED)" : "");
++                      
++      if (gstat != lstat)                     /* not everyone agrees with me */
++          goto all_done;
++
++      switch (lstat)
++      {
++      default:
++          ASSERT (0);                         /* I never drive this */
++          
++      case CM_GSTATUS_CLOSING | CM_GSTATUS_MAY_START: /* I can restart now (have seen restart go away) */
++          ASSERT (!cmRail->Levels[clvl].Online);
++          
++          CPRINTF2 (1,"%s: ===================NODES %s AGREE I MAY START\n", cmRail->Rail->Name,
++                    sprintClPeers (clNodeStr, cmRail, clvl));
++          printk ("%s: ===================NODES %s AGREE I MAY START\n", cmRail->Rail->Name,
++                  sprintClPeers (clNodeStr, cmRail, clvl));
++          
++          statemap_setbits (cmRail->Levels[clvl].LocalMap, base, 
++                            CM_GSTATUS_STARTING | CM_GSTATUS_MAY_RUN, CM_GSTATUS_BITS);
++          goto all_done;
++          
++      case CM_GSTATUS_STARTING | CM_GSTATUS_MAY_RUN:
++          ASSERT (!cmRail->Levels[clvl].Online);
++          
++          CPRINTF2 (1, "%s: ===================NODES %s AGREE I MAY RUN\n", cmRail->Rail->Name,
++                    sprintClPeers (clNodeStr, cmRail, clvl));
++          printk ("%s: ===================NODES %s AGREE I MAY RUN\n", cmRail->Rail->Name,
++                  sprintClPeers (clNodeStr, cmRail, clvl));
++          
++          statemap_setbits (cmRail->Levels[clvl].LocalMap, base, 
++                            CM_GSTATUS_RUNNING | CM_GSTATUS_MAY_RUN, CM_GSTATUS_BITS);
++          goto all_done;
++          
++      case CM_GSTATUS_RUNNING | CM_GSTATUS_MAY_RUN:
++          if (! cmRail->Levels[clvl].Online)
++          {
++              CPRINTF2 (1, "%s: ===================NODES %s AGREE I'M RUNNING\n", cmRail->Rail->Name,
++                        sprintClPeers (clNodeStr, cmRail, clvl));
++              printk ("%s: ===================NODES %s AGREE I'M RUNNING\n", cmRail->Rail->Name,
++                      sprintClPeers (clNodeStr, cmRail, clvl));
++              
++              cmRail->Levels[clvl].Online = 1;
++              
++              for (thisClId = 0; thisClId < numClNodes; thisClId++)
++              {
++                  if (thisClId == subClMin)   /* skip sub-cluster */
++                  {
++                      thisClId = subClMax;
++                      continue;
++                  }
++                  
++                  nodeId = cmRail->Levels[clvl].MinNodeId + thisClId;
++                  
++                  base  = thisClId * CM_GSTATUS_BITS;
++                  lstat = statemap_getbits (cmRail->Levels[clvl].LocalMap, base, CM_GSTATUS_BITS);
++                  gstat = statemap_getbits (cmRail->Levels[clvl].GlobalMap, base, CM_GSTATUS_BITS) & CM_GSTATUS_STATUS_MASK;
++                  
++                  /* Only connect to her if I see her as running and I'm not requesting her 
++                   * to restart - this means that I was offline when I saw her transition
++                   * to running and haven't seen her in a "bad" state since. */
++                  if (gstat == CM_GSTATUS_RUNNING && ! (lstat & CM_GSTATUS_RESTART))
++                  {
++                      CPRINTF5 (1, "%s: node %d lgstat %s gstat %s, lstat %s -> CONNECT\n", cmRail->Rail->Name, nodeId,
++                                GlobalStatusString (cmRail->Levels[clvl].LastGlobalMap, thisClId),
++                                GlobalStatusString (cmRail->Levels[clvl].GlobalMap, thisClId),
++                                GlobalStatusString (cmRail->Levels[clvl].LocalMap, thisClId));
++                      
++                      if (lstat == CM_GSTATUS_MAY_START)
++                          statemap_setbits (cmRail->Levels[clvl].LocalMap, base, CM_GSTATUS_MAY_RUN, CM_GSTATUS_BITS);
++
++                      ep_connect_node (cmRail->Rail, nodeId);
++
++                      cmRail->Levels[clvl].Connected++;
++                  }
++              }
++          }
++          goto all_done;
++      }
++
++    all_done:
++      statemap_setmap (cmRail->Levels[clvl].LastGlobalMap, cmRail->Levels[clvl].GlobalMap);
++    }
++}
++
++static void
++ReduceGlobalMap (CM_RAIL *cmRail, int clvl)
++{
++    int       lvl;
++    int       sidx;
++    int       recompute;
++    CM_LEVEL *level;
++    int       cTopLevel;
++    int       cRole;
++
++    if (clvl < cmRail->TopLevel)
++    {
++      cTopLevel = clvl + 1;
++      cRole = CM_ROLE_LEADER;
++    }
++    else
++    {
++      cTopLevel = cmRail->TopLevel;
++      cRole = cmRail->Role;
++    }
++    
++    /* Update cmRail->Levels[*].SubordinateMap[clvl] for all subordinate levels */
++    for (lvl = 0; lvl < cTopLevel; lvl++)
++    {
++      level = &cmRail->Levels[lvl];
++
++      /* We need to recompute this level's statemap if...
++       * . Previous level's statemap has changes to propagate OR
++       * . This level's statemap has not been computed yet OR
++       * . A subordinate at this level has sent me a change.
++       * Note that we can only do this if all subordinates from this
++       * level down are present with valid statemaps, or absent (i.e. not
++       * timing out).
++       */
++
++      ASSERT (lvl == 0 || cmRail->Levels[lvl - 1].SubordinateMapValid[clvl]);
++
++      recompute = !level->SubordinateMapValid[clvl] ||
++                  (lvl > 0 && statemap_changed (cmRail->Levels[lvl - 1].SubordinateMap[clvl]));
++        
++      for (sidx = 0; sidx < level->NumSegs; sidx++)
++      {
++          CM_SGMT *sgmt = &level->Sgmts[sidx];
++
++          if (!(sgmt->State == CM_SGMT_ABSENT || /* absent nodes contribute zeros */
++                (sgmt->State == CM_SGMT_PRESENT && /* present nodes MUST have received a map to contribute */
++                 sgmt->Maps[clvl].InputMapValid)))
++          {
++              CPRINTF5 (5, "%s: waiting for clvl %d lvl %d seg %d node %d\n", cmRail->Rail->Name,
++                        clvl, lvl, sidx, sgmt->NodeId);
++
++              /* Gotta wait for this guy, so we can't compute this level,
++               * or any higher levels. */
++              return;
++          }
++
++          if (statemap_changed (sgmt->Maps[clvl].InputMap))
++          {
++              ASSERT (sgmt->Maps[clvl].InputMapValid);
++
++              recompute = 1;
++
++              CPRINTF7 (5, "%s: %s clvl %d map from @ %d %d (%d) - %s\n",
++                        cmRail->Rail->Name, sgmt->State == CM_SGMT_ABSENT ? "newly absent" : "got new",
++                        clvl, lvl, sidx, sgmt->NodeId, 
++                        MapString ("Input", sgmt->Maps[clvl].InputMap, cmRail->Levels[clvl].NumNodes, ""));
++          }
++      }
++
++      if (recompute)
++      {
++          if (lvl == 0)
++              statemap_reset (cmRail->Levels[clvl].TmpMap);
++          else
++          {
++              ASSERT (cmRail->Levels[lvl - 1].SubordinateMapValid[clvl]);
++              
++              statemap_copy (cmRail->Levels[clvl].TmpMap, cmRail->Levels[lvl - 1].SubordinateMap[clvl]);
++              statemap_clearchanges (cmRail->Levels[lvl - 1].SubordinateMap[clvl]);
++          }
++       
++          for (sidx = 0; sidx < level->NumSegs; sidx++)
++          {
++              CM_SGMT *sgmt = &level->Sgmts[sidx];
++              
++              if (sgmt->State != CM_SGMT_ABSENT)      /* absent nodes contribute zeroes */
++              {
++                  ASSERT (sgmt->State == CM_SGMT_PRESENT);
++                  ASSERT (sgmt->Maps[clvl].InputMapValid);
++                  statemap_ormap (cmRail->Levels[clvl].TmpMap, sgmt->Maps[clvl].InputMap);
++              }
++              statemap_clearchanges (sgmt->Maps[clvl].InputMap);
++          }
++
++          statemap_setmap (level->SubordinateMap[clvl], cmRail->Levels[clvl].TmpMap);
++          level->SubordinateMapValid[clvl] = 1;
++
++          CPRINTF4 (5, "%s: recompute clvl %d level %d statemap - %s\n", cmRail->Rail->Name, clvl, lvl,
++                    MapString ("level", level->SubordinateMap[clvl], cmRail->Levels[clvl].NumNodes, ""));
++      }
++    }
++
++    if (cRole == CM_ROLE_LEADER_CANDIDATE)    /* don't know this cluster's leader yet */
++      return;
++
++    ASSERT (cTopLevel == 0 || cmRail->Levels[cTopLevel - 1].SubordinateMapValid[clvl]);
++
++    /* Update SubTreeMap */
++    
++    if (!cmRail->Levels[clvl].SubTreeMapValid || 
++      statemap_changed (cmRail->Levels[clvl].LocalMap) ||
++      (cTopLevel > 0 && statemap_changed (cmRail->Levels[cTopLevel - 1].SubordinateMap[clvl])))
++    {
++      statemap_copy (cmRail->Levels[clvl].TmpMap, cmRail->Levels[clvl].LocalMap);
++      statemap_clearchanges (cmRail->Levels[clvl].LocalMap);
++
++      if (cTopLevel > 0)
++      {
++          statemap_ormap (cmRail->Levels[clvl].TmpMap, cmRail->Levels[cTopLevel - 1].SubordinateMap[clvl]);
++          statemap_clearchanges (cmRail->Levels[cTopLevel - 1].SubordinateMap[clvl]);
++      }
++
++      statemap_setmap (cmRail->Levels[clvl].SubTreeMap, cmRail->Levels[clvl].TmpMap);
++      cmRail->Levels[clvl].SubTreeMapValid = 1;
++
++      CPRINTF3 (5, "%s: recompute clvl %d subtree map - %s\n", cmRail->Rail->Name, clvl,
++                MapString ("subtree", cmRail->Levels[clvl].SubTreeMap, cmRail->Levels[clvl].NumNodes, ""));
++    }
++
++    if (cRole == CM_ROLE_SUBORDINATE)         /* got a leader (Not me) */
++    {                                         /* => send SubTreeMap to her */
++      CM_SGMT *leader = &cmRail->Levels[cmRail->TopLevel].Sgmts[0];
++
++      ASSERT (leader->State == CM_SGMT_PRESENT);
++      ASSERT (cmRail->Levels[clvl].SubTreeMapValid);
++
++      if (!leader->Maps[clvl].OutputMapValid ||
++          statemap_changed (cmRail->Levels[clvl].SubTreeMap))
++      {
++          statemap_setmap (leader->Maps[clvl].OutputMap, cmRail->Levels[clvl].SubTreeMap);
++          leader->Maps[clvl].OutputMapValid = 1;
++
++          statemap_clearchanges (cmRail->Levels[clvl].SubTreeMap);
++
++          CPRINTF3 (5, "%s: sending clvl %d subtree map to leader (%d)\n", cmRail->Rail->Name, clvl, leader->NodeId);
++      }
++    }
++}
++
++void
++BroadcastGlobalMap (CM_RAIL *cmRail, int clvl)
++{
++    int       lvl;
++    int       sidx;
++    CM_LEVEL *level;
++    CM_SGMT  *leader;
++    int       cTopLevel;
++    int       cRole;
++
++    if (clvl < cmRail->TopLevel)
++    {
++      cTopLevel = clvl + 1;
++      cRole = CM_ROLE_LEADER;
++    }
++    else
++    {
++      cTopLevel = cmRail->TopLevel;
++      cRole = cmRail->Role;
++    }
++
++    switch (cRole)
++    {
++    default:
++      ASSERT (0);
++      
++    case CM_ROLE_LEADER_CANDIDATE:            /* don't know this cluster's leader yet */
++      return;
++
++    case CM_ROLE_LEADER:                      /* cluster leader: */
++      ASSERT (clvl < cmRail->TopLevel);               /* set GlobalMap from SubTreeMap */
++      
++      if (!cmRail->Levels[clvl].SubTreeMapValid)      /* can't set global map */
++          return;
++
++      if (cmRail->Levels[clvl].GlobalMapValid &&      /* already set global map */
++          !statemap_changed (cmRail->Levels[clvl].SubTreeMap)) /* no changes to propagate */
++          return;
++      
++      statemap_setmap (cmRail->Levels[clvl].GlobalMap, cmRail->Levels[clvl].SubTreeMap);
++      cmRail->Levels[clvl].GlobalMapValid = 1;
++      statemap_clearchanges (cmRail->Levels[clvl].SubTreeMap);
++
++      CPRINTF2 (5, "%s: whole cluster %d leader setting global map\n", cmRail->Rail->Name, clvl);
++
++      UpdateGlobalStatus (cmRail);
++      break;
++      
++    case CM_ROLE_SUBORDINATE:                 /* cluster subordinate: */
++      ASSERT (clvl >= cmRail->TopLevel);              /* receive GlobalMap from leader */
++      ASSERT (cmRail->TopLevel < cmRail->NumLevels);
++      
++      leader = &cmRail->Levels[cmRail->TopLevel].Sgmts[0];
++      ASSERT (leader->State == CM_SGMT_PRESENT);
++
++      if (!leader->Maps[clvl].InputMapValid)  /* can't set global map */
++          return;
++      
++      if (cmRail->Levels[clvl].GlobalMapValid &&      /* already set global map */
++          !statemap_changed (leader->Maps[clvl].InputMap)) /* no changes to propagate */
++          return;
++
++      statemap_setmap (cmRail->Levels[clvl].GlobalMap, leader->Maps[clvl].InputMap);
++      cmRail->Levels[clvl].GlobalMapValid = 1;
++      statemap_clearchanges (leader->Maps[clvl].InputMap);
++
++      CPRINTF3 (5, "%s: getting clvl %d global map from leader (%d)\n", cmRail->Rail->Name, clvl, leader->NodeId);
++
++      UpdateGlobalStatus (cmRail);
++      break;
++    }
++
++    CPRINTF3 (5, "%s: clvl %d %s\n", cmRail->Rail->Name, clvl,
++            MapString ("global", cmRail->Levels[clvl].GlobalMap, cmRail->Levels[clvl].NumNodes, ""));
++    
++    /* Broadcast global map to all subordinates */
++    for (lvl = 0; lvl < cTopLevel; lvl++)
++    {
++      level = &cmRail->Levels[lvl];
++      
++      for (sidx = 0; sidx < level->NumSegs; sidx++)
++      {
++          CM_SGMT *sgmt = &level->Sgmts[sidx];
++          
++          if (sgmt->State == CM_SGMT_PRESENT)
++          {
++              statemap_setmap (sgmt->Maps[clvl].OutputMap, cmRail->Levels[clvl].GlobalMap);
++              sgmt->Maps[clvl].OutputMapValid = 1;
++              
++              CPRINTF5 (5, "%s: sending clvl %d global map to subordinate %d %d (%d)\n", 
++                        cmRail->Rail->Name, clvl, lvl, sidx, sgmt->NodeId);
++          }
++      }
++    }
++}
++
++static void
++CheckPeerPulse (CM_RAIL *cmRail, CM_SGMT *sgmt)
++{
++    int clvl, sendRejoin;
++    
++    switch (sgmt->State)
++    {
++    case CM_SGMT_ABSENT:
++      break;
++
++    case CM_SGMT_WAITING:                     /* waiting for a subtree */
++      if (!AFTER (lbolt, sgmt->UpdateTick + MSEC2TICKS(CM_DISCOVER_TIMEOUT)))
++          break;
++      
++      CPRINTF3 (2, "%s: lvl %d subtree %d contains no live nodes\n", cmRail->Rail->Name, 
++                sgmt->Level, (int) (sgmt - &cmRail->Levels[sgmt->Level].Sgmts[0]));
++
++      sgmt->State = CM_SGMT_ABSENT;
++      for (clvl = sgmt->Level; clvl < cmRail->NumLevels; clvl++)
++      {
++          statemap_zero (sgmt->Maps[clvl].InputMap);          /* need to start propagating zeros (flags change) */
++          sgmt->Maps[clvl].InputMapValid = 1;         /* and must indicate that the map is now valid */
++      }
++      break;
++
++    case CM_SGMT_COMING:                              /* lost/waiting subtree sent me IMCOMING */
++      ASSERT (sgmt->Level > 0);                       /* we only do subtree discovery below our own level */
++
++      if (AFTER (lbolt, sgmt->WaitingTick + MSEC2TICKS(CM_WAITING_TIMEOUT)))
++      {
++          CPRINTF3 (1, "%s: lvl %d subtree %d waiting too long\n", cmRail->Rail->Name,
++                    sgmt->Level, (int) (sgmt - &cmRail->Levels[sgmt->Level].Sgmts[0]));
++          printk ("%s: lvl %d subtree %d waiting too long\n", cmRail->Rail->Name,
++                  sgmt->Level, (int) (sgmt - &cmRail->Levels[sgmt->Level].Sgmts[0]));
++
++          sgmt->State = CM_SGMT_ABSENT;
++          for (clvl = sgmt->Level; clvl < cmRail->NumLevels; clvl++)
++          {
++              statemap_zero (sgmt->Maps[clvl].InputMap);              /* need to start propagating zeros (flags change) */
++              sgmt->Maps[clvl].InputMapValid = 1;             /* and must indicate that the map is now valid */
++          }
++          break;
++      }
++
++      if (!AFTER (lbolt, sgmt->UpdateTick + MSEC2TICKS(CM_DISCOVER_TIMEOUT)))
++          break;
++
++      CPRINTF3 (2, "%s: lvl %d subtree %d hasn't connected yet\n", cmRail->Rail->Name,
++                sgmt->Level, (int) (sgmt - &cmRail->Levels[sgmt->Level].Sgmts[0]));
++
++      sgmt->State = CM_SGMT_WAITING;
++      sgmt->UpdateTick = lbolt;
++
++      if (sgmt->Level > 0)
++          __Schedule_Discovery (cmRail);
++      break;
++      
++    case CM_SGMT_PRESENT:
++      if (!AFTER (lbolt, sgmt->UpdateTick + MSEC2TICKS(CM_HEARTBEAT_TIMEOUT)))
++          break;
++
++      if (sgmt->Level == cmRail->TopLevel)            /* leader died */
++      {
++          sendRejoin = (sgmt->State == CM_SGMT_PRESENT && sgmt->AckSeq == 0);
++
++          CPRINTF4 (1, "%s: leader (%d) node %d JUST DIED%s\n", 
++                    cmRail->Rail->Name, sgmt->Level, sgmt->NodeId,
++                    sendRejoin ? ": !REJOIN" : "");
++          
++          printk ("%s: lvl %d leader (%d) JUST DIED%s\n", 
++                  cmRail->Rail->Name, sgmt->Level, sgmt->NodeId,
++                  sendRejoin ? ": !REJOIN" : "");
++      
++          if (sendRejoin)
++          {
++              /* she's not sent us any heartbeats even though she responded to a discover
++               * so tell her to rejoin the tree at the bottom, this will mean that she 
++               * has to run the heartbeat timer before being able to rejoin the tree. */
++              SendMessage (cmRail, sgmt->NodeId, sgmt->Level, CM_MSG_TYPE_REJOIN);
++          }
++
++          StartLeaderDiscovery (cmRail);
++          break;
++      }
++
++      sendRejoin = (sgmt->State == CM_SGMT_PRESENT && sgmt->AckSeq == 0);
++
++      CPRINTF5 (2, "%s: lvl %d subordinate %d (%d) JUST DIED%s\n", cmRail->Rail->Name, 
++                sgmt->Level, (int) (sgmt - &cmRail->Levels[sgmt->Level].Sgmts[0]), sgmt->NodeId,
++                sendRejoin ? ": !REJOIN" : "");
++      printk ("%s: lvl %d subordinate %d (%d) JUST DIED%s\n", cmRail->Rail->Name, 
++              sgmt->Level, (int) (sgmt - &cmRail->Levels[sgmt->Level].Sgmts[0]), sgmt->NodeId,
++              sendRejoin ? ": !REJOIN" : "");
++
++      if (sendRejoin)
++      {
++          /* she's not sent us any heartbeats even though she responded to a discover
++           * so tell her to rejoin the tree at the bottom, this will mean that she 
++           * has to run the heartbeat timer before being able to rejoin the tree. */
++          SendMessage (cmRail, sgmt->NodeId, sgmt->Level, CM_MSG_TYPE_REJOIN);
++      }
++
++      StartSubTreeDiscovery (cmRail, sgmt);
++      break;
++       
++    default:
++      ASSERT (0);
++    }
++}
++
++static void
++CheckPeerPulses (CM_RAIL *cmRail)
++{
++    int lvl;
++    int sidx;
++   
++    /* check children are alive */
++    for (lvl = 0; lvl < cmRail->TopLevel; lvl++)
++      for (sidx = 0; sidx < cmRail->Levels[lvl].NumSegs; sidx++)
++          CheckPeerPulse (cmRail, &cmRail->Levels[lvl].Sgmts[sidx]);
++
++    /* check leader is alive */
++    if (cmRail->Role == CM_ROLE_SUBORDINATE)
++    {
++      ASSERT (cmRail->TopLevel < cmRail->NumLevels);
++      ASSERT (cmRail->Levels[cmRail->TopLevel].Sgmts[0].State == CM_SGMT_PRESENT);
++      
++      CheckPeerPulse (cmRail, &cmRail->Levels[cmRail->TopLevel].Sgmts[0]);
++    }
++}
++
++static void
++SendHeartbeats (CM_RAIL *cmRail)
++{
++    int lvl;
++
++    /* Send heartbeats to my children */
++    for (lvl = 0; lvl < cmRail->TopLevel; lvl++)
++    {
++      CM_LEVEL *level = &cmRail->Levels[lvl];
++      int       sidx;
++       
++      for (sidx = 0; sidx < level->NumSegs; sidx++)
++      {
++          CM_SGMT *sgmt = &cmRail->Levels[lvl].Sgmts[sidx];
++
++          if (sgmt->State == CM_SGMT_PRESENT)
++              SendToSgmt (cmRail, sgmt, CM_MSG_TYPE_HEARTBEAT);
++      }
++    }
++
++    /* Send heartbeat to my leader */
++    if (cmRail->Role == CM_ROLE_SUBORDINATE)
++    {
++      ASSERT (cmRail->TopLevel < cmRail->NumLevels);
++      SendToSgmt (cmRail, &cmRail->Levels[cmRail->TopLevel].Sgmts[0], CM_MSG_TYPE_HEARTBEAT);
++    }
++}
++
++static int
++BroadcastDiscover (CM_RAIL *cmRail)
++{
++    int       sidx;
++    int             lvl;
++    int       msgType;
++    CM_LEVEL *level;
++    int       urgent;
++
++    ASSERT (cmRail->TopLevel <= cmRail->NumLevels);
++    ASSERT ((cmRail->Role == CM_ROLE_LEADER) ? (cmRail->TopLevel == cmRail->NumLevels) :
++          (cmRail->Role == CM_ROLE_SUBORDINATE) ? (cmRail->Levels[cmRail->TopLevel].Sgmts[0].State == CM_SGMT_PRESENT) :
++          (cmRail->Role == CM_ROLE_LEADER_CANDIDATE));
++
++    if (cmRail->Role != CM_ROLE_LEADER_CANDIDATE)     /* got a leader/lead whole machine */
++    {
++      urgent = 0;                             /* non-urgent leader discovery */
++      lvl = cmRail->TopLevel - 1;             /* on nodes I lead (resolves leader conflicts) */
++      msgType = CM_MSG_TYPE_RESOLVE_LEADER;
++    }
++    else
++    {
++      urgent = 1;                             /* urgent leader discovery */
++      lvl = cmRail->TopLevel;                 /* on nodes I'd like to lead */
++      msgType = CM_MSG_TYPE_DISCOVER_LEADER;
++    }
++
++    if (lvl >= 0)
++    {
++      if (lvl > cmRail->BroadcastLevel)
++      {
++          /* Unable to broadcast at this level in the spanning tree, so we 
++           * just continue doing discovery until we are able to broadcast */
++          CPRINTF4 (6, "%s: broadcast level %d too low to discover %d at level %d\n",
++                    cmRail->Rail->Name, cmRail->BroadcastLevel, msgType, lvl);
++
++          cmRail->DiscoverStartTick = lbolt;
++      }
++      else
++      {
++          level = &cmRail->Levels[lvl];
++          SendToSgmt (cmRail, &level->Sgmts[level->MySgmt], msgType);
++      }
++    }
++    
++    while (lvl > 0)
++    {
++      level = &cmRail->Levels[lvl];
++      
++      for (sidx = 0; sidx < level->NumSegs; sidx++)
++      {
++          CM_SGMT *sgmt = &level->Sgmts[sidx];
++       
++          if (sgmt->State == CM_SGMT_WAITING)
++          {
++              ASSERT (sidx != level->MySgmt);
++              /* Do subordinate discovery.  Existing subordinates will
++               * ignore it, but leader candidates will send IMCOMING.
++               * This is always urgent since we'll assume a subtree is
++               * absent if I don't get IMCOMING within the timeout.
++               */
++              SendToSgmt (cmRail, sgmt, CM_MSG_TYPE_DISCOVER_SUBORDINATE);
++              urgent = 1;
++          }
++      }
++      lvl--;
++    }
++   
++    return (urgent);
++}
++
++static void
++CheckBroadcast (CM_RAIL *cmRail)
++{
++    int  clvl;
++
++    for (clvl = cmRail->NumLevels-1; clvl >= 0 && cmRail->Rail->SwitchBroadcastLevel < cmRail->Levels[clvl].SwitchLevel; clvl--)
++      ;
++
++    if (cmRail->OfflineReasons || cmRail->Rail->System->Shutdown)
++      clvl = -1;
++
++    /* if the level at which we can broadcast drops, then we must rejoin the
++     * spanning tree at the highest level for which broadcast is good. */
++    if (cmRail->BroadcastLevel > clvl && clvl < (int)(cmRail->Role == CM_ROLE_LEADER ? cmRail->TopLevel - 1 : cmRail->TopLevel))
++    {
++      printk ("%s: REJOINING at level %d because %s\n", cmRail->Rail->Name, clvl+1, 
++              (cmRail->OfflineReasons & CM_OFFLINE_MANAGER) ? "of manager thread" :
++              (cmRail->OfflineReasons & CM_OFFLINE_PROCFS)  ? "force offline"  : 
++              cmRail->Rail->System->Shutdown ? "system shutdown" : "broadcast level changed");
++      LowerTopLevel (cmRail, clvl+1);
++    }
++    
++    if (cmRail->BroadcastLevel != clvl)
++    {
++      cmRail->BroadcastLevel     = clvl;
++      cmRail->BroadcastLevelTick = lbolt;
++    }
++
++    /* schedule the update thread, to withdraw from comms with 
++     * nodes "outside" of the valid broadcastable range. */
++    for (clvl = 0; clvl < cmRail->NumLevels; clvl++)
++    {
++      if (cmRail->BroadcastLevel < clvl)
++      {
++          if (AFTER (lbolt, cmRail->BroadcastLevelTick + EP_WITHDRAW_TIMEOUT) && 
++              !(cmRail->Levels[clvl].OfflineReasons & CM_OFFLINE_BROADCAST))
++          {
++              printk ("%s: Withdraw at Level %d\n", cmRail->Rail->Name, clvl);
++              cmRail->Levels[clvl].OfflineReasons |= CM_OFFLINE_BROADCAST;
++          }
++      }
++      else
++      {
++          if (cmRail->Levels[clvl].OfflineReasons & CM_OFFLINE_BROADCAST)
++          {
++              printk ("%s: Rejoin at Level %d\n", cmRail->Rail->Name, clvl);
++              cmRail->Levels[clvl].OfflineReasons &= ~CM_OFFLINE_BROADCAST;
++          }
++      }
++    }
++      
++}
++
++static void
++CheckManager (CM_RAIL *cmRail)
++{
++    long time,  state = ep_kthread_state (&cmRail->Rail->System->ManagerThread, &time);
++
++    if (state == KT_STATE_RUNNING && BEFORE (lbolt, time + MSEC2TICKS(CM_THREAD_RUNNING_TIMEOUT)))
++      state = KT_STATE_SLEEPING;
++    if (state != KT_STATE_SLEEPING && BEFORE (lbolt, time + MSEC2TICKS(CM_THREAD_SCHEDULE_TIMEOUT)))
++      state = KT_STATE_SLEEPING;
++
++    if ((cmRail->OfflineReasons & CM_OFFLINE_MANAGER) && state == KT_STATE_SLEEPING)
++    {
++      printk ("%s: manager thread unstuck\n", cmRail->Rail->Name);
++
++      cmRail->OfflineReasons &= ~CM_OFFLINE_MANAGER;
++    }
++
++    if (!(cmRail->OfflineReasons & CM_OFFLINE_MANAGER) && state != KT_STATE_SLEEPING)
++    {
++      printk ("%s: manager thread stuck - %s\n", cmRail->Rail->Name,
++              state == KT_STATE_SCHEDULED ? "scheduled" : 
++              state == KT_STATE_RUNNING ? "running" : 
++              state == KT_STATE_STALLED ? "stalled" : "unknown");
++
++      cmRail->OfflineReasons |= CM_OFFLINE_MANAGER;
++    }
++}
++
++static void
++CheckOfflineReasons (CM_RAIL *cmRail, int clvl)
++{
++    int subClMin, subClMax, myClId;
++    char clNodeStr[32];                               /* [%d-%d][%d-%d] */
++
++    if (cmRail->Levels[clvl].OfflineReasons)
++    {
++      if (cmRail->Levels[clvl].Online)
++      {
++          printk ("%s: Withdraw from %s\n", cmRail->Rail->Name, sprintClPeers (clNodeStr, cmRail, clvl));
++          
++          RestartComms (cmRail, clvl);
++      }
++    }
++    else
++    {
++      if (cmRail->Levels[clvl].Restarting && cmRail->Levels[clvl].Connected == 0)
++      {
++          printk ("%s: Rejoin with %s\n", cmRail->Rail->Name, sprintClPeers (clNodeStr, cmRail, clvl));
++
++          myClId = ClusterIds (cmRail, clvl, &subClMin, &subClMax);
++          
++          ASSERT (statemap_getbits (cmRail->Levels[clvl].LocalMap, myClId * CM_GSTATUS_BITS, CM_GSTATUS_BITS) == 
++                  (CM_GSTATUS_CLOSING | CM_GSTATUS_MAY_START | CM_GSTATUS_RESTART));
++    
++          statemap_setbits (cmRail->Levels[clvl].LocalMap, myClId * CM_GSTATUS_BITS,
++                            CM_GSTATUS_CLOSING | CM_GSTATUS_MAY_START, CM_GSTATUS_BITS);
++
++          cmRail->Levels[clvl].Restarting = 0;
++      }
++    }
++}
++
++void
++DoHeartbeatWork (CM_RAIL *cmRail)
++{
++    long now = lbolt;
++    int  clvl;
++
++    if ((RejoinCheck || RejoinPanic) &&
++      AFTER (now, cmRail->NextRunTime + MSEC2TICKS (CM_TIMER_SCHEDULE_TIMEOUT))) /* If I've been unresponsive for too long */
++    {
++      /* I'd better reconnect to the network because I've not been playing the game */
++      CPRINTF4 (1, "%s: REJOINING because I was too slow (heartbeat) [%ld,%ld,(%ld)]\n", cmRail->Rail->Name, now,  cmRail->NextRunTime, (long int)MSEC2TICKS (CM_TIMER_SCHEDULE_TIMEOUT));
++      printk ("%s: REJOINING because I was too slow (heartbeat) [%ld,%ld,(%ld)]\n", cmRail->Rail->Name, now,  cmRail->NextRunTime, (long int)MSEC2TICKS (CM_TIMER_SCHEDULE_TIMEOUT));
++      
++      LowerTopLevel (cmRail, 0);
++      
++      IncrStat (cmRail, RejoinTooSlow);
++      
++      if (RejoinPanic)
++          panic ("ep: REJOINING because I was too slow (heartbeat)\n");
++    }
++    
++    PollInputQueues (cmRail);
++    
++    if (cmRail->NextDiscoverTime && ! BEFORE (now, cmRail->NextDiscoverTime))
++    {
++      if (BroadcastDiscover (cmRail))         /* urgent discovery required? */
++          cmRail->NextDiscoverTime = now + MSEC2TICKS (CM_URGENT_DISCOVER_INTERVAL);
++      else
++          cmRail->NextDiscoverTime = now + MSEC2TICKS (CM_PERIODIC_DISCOVER_INTERVAL);
++      
++      if (cmRail->Role == CM_ROLE_LEADER_CANDIDATE && AFTER (now, cmRail->DiscoverStartTick + MSEC2TICKS (CM_DISCOVER_TIMEOUT)))
++          RaiseTopLevel (cmRail);
++    }
++    
++    if (cmRail->NextHeartbeatTime && ! BEFORE (now, cmRail->NextHeartbeatTime))
++    {
++      CheckPosition (cmRail->Rail);
++      CheckPeerPulses (cmRail);
++      CheckBroadcast (cmRail);
++      CheckManager (cmRail);
++      
++      for (clvl = 0; clvl < cmRail->NumLevels; clvl++)
++      {
++          CheckOfflineReasons (cmRail, clvl);
++          ReduceGlobalMap (cmRail, clvl);
++          BroadcastGlobalMap (cmRail, clvl);
++      }
++      
++      SendHeartbeats (cmRail);
++      
++      /* Compute the next heartbeat time, but "drift" it towards the last
++       * periodic discovery time we saw from the whole machine leader */
++      cmRail->NextHeartbeatTime = now + MSEC2TICKS (CM_HEARTBEAT_INTERVAL);
++    }
++
++    if (cmRail->NextDiscoverTime && AFTER (cmRail->NextHeartbeatTime, cmRail->NextDiscoverTime))
++      cmRail->NextRunTime = cmRail->NextDiscoverTime;
++    else 
++      cmRail->NextRunTime = cmRail->NextHeartbeatTime;
++}
++
++#define CM_SVC_INDICATOR_OFFSET(CMRAIL,CLVL,IND,NODEID)     ( ( CMRAIL->Levels[CLVL].NumNodes * CM_GSTATUS_BITS ) \
++                                                              + ( CMRAIL->Levels[CLVL].NumNodes * IND ) \
++                                                              + ( NODEID - CMRAIL->Levels[CLVL].MinNodeId ) )
++int
++cm_svc_indicator_set (EP_RAIL *rail, int svc_indicator)
++{
++    CM_RAIL *cmRail = rail->ClusterRail;
++    unsigned long flags;
++    int           clvl;
++
++    EPRINTF2 (DBG_SVC,"cm_svc_indicator_set: rail %p ind %d\n", rail, svc_indicator);
++
++    if (svc_indicator < 0 || svc_indicator >= EP_SVC_NUM_INDICATORS)
++    {
++      EPRINTF1 (DBG_SVC,"cm_svc_indicator_set: service indicator %d not registered\n", svc_indicator);
++      return (-1);
++    }
++
++    if (rail->State == EP_RAIL_STATE_UNINITIALISED) 
++      return (-2);
++    
++    spin_lock_irqsave (&cmRail->Lock, flags);
++    for (clvl = 0; clvl < cmRail->NumLevels; clvl++)  {
++      statemap_setbits (cmRail->Levels[clvl].LocalMap, CM_SVC_INDICATOR_OFFSET (cmRail, clvl, svc_indicator, cmRail->NodeId), 1, 1); 
++      EPRINTF3 (DBG_SVC,"cm_svc_indicator_set: clvl %d nodeId %d offset %d\n", clvl, cmRail->NodeId, CM_SVC_INDICATOR_OFFSET (cmRail, clvl, svc_indicator, cmRail->NodeId));
++    }
++    spin_unlock_irqrestore (&cmRail->Lock, flags);
++
++    return (0);
++}
++
++int
++cm_svc_indicator_clear (EP_RAIL *rail, int svc_indicator)
++{
++    CM_RAIL *cmRail = rail->ClusterRail;
++    unsigned long flags;
++    int           clvl;
++
++    EPRINTF2 (DBG_SVC, "cm_svc_indicator_clear: rail %p ind %d\n", rail, svc_indicator);
++
++    if (svc_indicator < 0 || svc_indicator >= EP_SVC_NUM_INDICATORS)
++    {
++      EPRINTF1 (DBG_SVC, "cm_svc_indicator_clear: service indicator %d not registered\n", svc_indicator);
++      return (-1);
++    }
++
++    if (rail->State == EP_RAIL_STATE_UNINITIALISED) 
++      return (-2);
++
++    spin_lock_irqsave (&cmRail->Lock, flags);
++    for (clvl = 0; clvl < cmRail->NumLevels; clvl++)  {
++      statemap_setbits (cmRail->Levels[clvl].LocalMap, CM_SVC_INDICATOR_OFFSET (cmRail, clvl, svc_indicator, cmRail->NodeId), 0, 1); 
++      EPRINTF3 (DBG_SVC, "cm_svc_indicator_clear: clvl %d nodeId %d offset %d\n", clvl, cmRail->NodeId, CM_SVC_INDICATOR_OFFSET (cmRail, clvl, svc_indicator, cmRail->NodeId));
++    }
++    spin_unlock_irqrestore (&cmRail->Lock, flags);
++
++    return (0);
++}
++
++int
++cm_svc_indicator_is_set (EP_RAIL *rail, int svc_indicator, int nodeId)
++{
++    CM_RAIL *cmRail = rail->ClusterRail;
++    unsigned long flags;
++    int           clvl;
++    bitmap_t      bits;
++
++    EPRINTF4 (DBG_SVC, "cm_svc_indicator_is_set: rail %p ind %d nodeId %d (me=%d)\n", rail, svc_indicator, nodeId, cmRail->NodeId);
++
++    if (svc_indicator < 0 || svc_indicator > EP_SVC_NUM_INDICATORS)
++    {
++      EPRINTF1 (DBG_SVC, "cm_svc_indicator_is_set: service indicator %d not registered\n", svc_indicator);
++      return (0);
++    }
++
++    if (rail->State == EP_RAIL_STATE_UNINITIALISED) 
++      return (0);
++    
++    spin_lock_irqsave (&cmRail->Lock, flags);
++    for (clvl = 0; clvl < cmRail->NumLevels; clvl++)
++      if (nodeId >= cmRail->Levels[clvl].MinNodeId && nodeId < (cmRail->Levels[clvl].MinNodeId + cmRail->Levels[clvl].NumNodes))
++          break;
++
++    if ( clvl == cmRail->NumLevels) { 
++      EPRINTF1 (DBG_SVC, "cm_svc_indicator_is_set: node out of range %d \n", nodeId); 
++      spin_unlock_irqrestore (&cmRail->Lock, flags);
++      return (0);
++    }
++
++    if ( cmRail->NodeId == nodeId ) 
++      bits = statemap_getbits (cmRail->Levels[clvl].LocalMap, CM_SVC_INDICATOR_OFFSET (cmRail, clvl, svc_indicator, nodeId), 1);
++    else
++      bits = statemap_getbits (cmRail->Levels[clvl].GlobalMap, CM_SVC_INDICATOR_OFFSET (cmRail, clvl, svc_indicator, nodeId), 1);
++
++    EPRINTF4 (DBG_SVC, "cm_svc_indicator_is_set: clvl %d nodeId %d offset %d %x\n", clvl, nodeId, CM_SVC_INDICATOR_OFFSET (cmRail, clvl, svc_indicator, nodeId), bits);
++
++    spin_unlock_irqrestore (&cmRail->Lock, flags);
++
++    return  ( (bits == 0) ? (0) : (1) );
++}
++
++int
++cm_svc_indicator_bitmap (EP_RAIL *rail, int svc_indicator, bitmap_t * bitmap, int low, int nnodes)
++{
++    /* or in the bit map */  
++    CM_RAIL      *cmRail = rail->ClusterRail;
++    int           nodeId, clvl;
++    bitmap_t      bits;
++    unsigned long flags;
++    int           clip_out_low, clip_out_high;
++    int           curr_low, curr_high;
++    int           check_low, check_high;
++
++    EPRINTF4 (DBG_SVC, "cm_svc_indicator_bitmap: rail %p ind %d low %d high %d\n", rail, svc_indicator, low, (low + nnodes));
++
++    if (svc_indicator < 0 || svc_indicator >= EP_SVC_NUM_INDICATORS)
++    {
++      EPRINTF1 (DBG_SVC, "cm_svc_indicator_bitmap: service indicator %d not registered\n", svc_indicator);
++      return (-1);
++    }
++
++    if (rail->State != EP_RAIL_STATE_RUNNING) 
++      return (-2);
++
++    spin_lock_irqsave (&cmRail->Lock, flags);
++    
++    clip_out_low = clip_out_high = -1; /* all in */
++    for (clvl = 0; clvl < cmRail->NumLevels; clvl++) {
++
++      /* curr_high/low is the range of the current lvl */
++      curr_low  = cmRail->Levels[clvl].MinNodeId;
++      curr_high = cmRail->Levels[clvl].MinNodeId + cmRail->Levels[clvl].NumNodes;
++
++      /* find out how much of low high is in this range and only check that part */
++      check_low  = ( low  < curr_low)  ? curr_low  : low; 
++      check_high = ( (low + nnodes) > curr_high) ? curr_high : (low + nnodes);
++
++      EPRINTF6 (DBG_SVC, "cm_svc_indicator_bitmap: curr(%d,%d) check(%d,%d) clip(%d,%d)\n", curr_low, curr_high, check_low, check_high, clip_out_low, clip_out_high);
++
++      for(nodeId = check_low; nodeId < check_high; nodeId++) {
++
++          if (  (clip_out_low <= nodeId) && (nodeId <= clip_out_high))
++              nodeId = clip_out_high; /* step over the cliped out section */
++          else {
++
++              if ( cmRail->NodeId == nodeId ) 
++                  bits = statemap_getbits (cmRail->Levels[clvl].LocalMap, CM_SVC_INDICATOR_OFFSET (cmRail, clvl, svc_indicator, nodeId), 1);
++              else
++                  bits = statemap_getbits (cmRail->Levels[clvl].GlobalMap, CM_SVC_INDICATOR_OFFSET (cmRail, clvl, svc_indicator, nodeId), 1);
++              
++              if ( bits ) {
++                  EPRINTF2 (DBG_SVC, "cm_svc_indicator_bitmap: its set nodeId %d (clvl %d)\n", nodeId, clvl);
++                  BT_SET ( bitmap , nodeId - low );
++              }
++          }
++      }
++
++      /* widen the clip out range */
++      clip_out_low  = curr_low;
++      clip_out_high = curr_high -1; 
++    }
++
++    spin_unlock_irqrestore (&cmRail->Lock, flags);      
++
++    return (0);
++}
++
++#if defined(PER_CPU_TIMEOUT)
++static void
++cm_percpu_timeout (void *arg)
++{
++    CM_RAIL          *cmRail = (CM_RAIL *) arg;
++    CM_TIMEOUT_DATA *hbd     = &cmRail->HeartbeatTimeoutsData[current_cpu()];
++    long             now     = lbolt;
++    unsigned         delay   = now - hbd->ScheduledAt;
++    unsigned long    flags;
++
++    if (delay > hbd->WorstDelay)
++      hbd->WorstDelay = delay;
++    if (hbd->BestDelay == 0 || delay < hbd->BestDelay)
++      hbd->BestDelay = delay;
++
++    if (cmRail->HeartbeatTimeoutsShouldStop)
++    {
++      spin_lock_irqsave (&cmRail->Lock, flags);
++      cmRail->HeartbeatTimeoutsStopped |= (1 << current_cpu());
++      kcondvar_wakeupall (&cmRail->HeartbeatTimeoutsWait, &cmRail->Lock);
++      spin_unlock_irqrestore (&cmRail->Lock, flags);
++      return;
++    }
++
++    if (cmRail->NextRunTime == 0 || AFTER (cmRail->NextRunTime, lbolt))
++      hbd->EarlyCount++;
++    else if (cmRail->HeartbeatTimeoutRunning)
++      hbd->MissedCount++;
++    else
++    {
++      local_irq_save (flags);
++      
++      if (! spin_trylock (&cmRail->HeartbeatTimeoutsLock))
++          hbd->WastedCount++;
++      else
++      {
++          cmRail->HeartbeatTimeoutRunning = 1;
++          hbd->WorkCount++;
++
++          spin_lock (&cmRail->Lock);
++
++          if ((delay = (lbolt - cmRail->NextRunTime)) > hbd->WorstHearbeatDelay)
++              hbd->WorstHearbeatDelay = delay;
++          if ((delay = (lbolt - now) > hbd->WorstLockDelay))
++              hbd->WorstLockDelay = delay;
++
++          DoHeartbeatWork (cmRail);
++
++          spin_unlock (&cmRail->Lock);
++          spin_unlock (&cmRail->HeartbeatTimeoutsLock);
++
++          cmRail->HeartbeatTimeoutRunning = 0;
++      }
++      local_irq_restore (flags);
++    }
++
++    hbd->ScheduledAt = lbolt + MSEC2TICKS (CM_PERCPU_TIMEOUT_INTERVAL);
++    timeout_cpu (cm_percpu_timeout, cmRail, MSECS2TICKS (CM_PERCPU_TIMEOUT_INTERVAL), CALLOUT_TYPE|CALLOUT_NOMALLOC);
++}
++
++static void
++StartPerCpuTimeouts (CM_RAIL *cmRail)
++{
++    register int c;
++
++    spin_lock_init (&cmRail->HeartbeatTimeoutsLock);
++
++    KMEM_ZALLOC (cmRail->HeartbeatTimeoutsData, CM_TIMEOUT_DATA *, ncpus * sizeof (CM_TIMEOUT_DATA), 1);
++
++    for (c = 0; c < cpus_in_box; c++)
++    {
++      if (cpu_to_processor (c))
++      {       
++          if (current_cpu() != c)
++          {
++              thread_bind (current_thread(), cpu_to_processor(c));
++              mpsleep (current_thread(), 0, "StartPerCpuTimeouts", 1, NULL, 0);
++
++              if (current_cpu() != c)
++                  panic ("ep: StartPerCpuTimeouts - failed to switch cpu\n");
++          }
++          
++          cmRail->HeartbeatTimeoutsStarted |= (1 << c);
++          cmRail->HeartbeatTimeoutsData[c].ScheduledAt = lbolt + c;
++
++          timeout_cpu (cm_percpu_timeout, cmRail, c, CALLOUT_TYPE|CALLOUT_NOMALLOC);
++      }
++    }
++
++    thread_bind(current_thread(), NULL);
++}
++
++static void
++StopPerCpuTimeouts (CM_RAIL *cmRail)
++{
++    register int c;
++    unsigned long flags;
++
++    cmRail->HeartbeatTimeoutsShouldStop = 1;
++
++    for (c = 0; c < cpus_in_box; c++)
++    {
++      if (cmRail->HeartbeatTimeoutsStarted & (1 << c))
++      {
++          printk ("%s: stopping cpu_timeout on cpu %d\n", cmRail->Rail->Name, c);
++
++          if (untimeout_cpu (cm_percpu_timeout, cmRail, c, CALLOUT_TYPE|CALLOUT_NOMALLOC, NULL))
++              cmRail->HeartbeatTimeoutsStopped |= (1 << c);
++      }
++    }
++    thread_bind(current_thread(), NULL);
++
++    spin_lock_irqsave (&cmRail->Lock, flags);
++    while (cmRail->HeartbeatTimeoutsStopped != cmRail->HeartbeatTimeoutsStarted)
++      kcondvar_wait (&cmRail->HeartbeatTimeoutsWait, &cmRail->Lock, &flags);
++    spin_unlock_irqrestore (&cmRail->Lock, flags);
++
++    cmRail->HeartbeatTimeoutsStarted    = 0;
++    cmRail->HeartbeatTimeoutsStopped    = 0;
++    cmRail->HeartbeatTimeoutsShouldStop = 0;
++
++    KMEM_FREE (cmRail->HeartbeatTimeoutsData, ncpus * sizeof (CM_TIMEOUT_DATA));
++
++    spin_lock_destroy (&cmRail->HeartbeatTimeoutsLock);
++}
++
++#else
++
++static void
++cm_heartbeat_timer (unsigned long arg)
++{
++    CM_RAIL *cmRail = (CM_RAIL *) arg;
++    unsigned long flags;
++
++    spin_lock_irqsave (&cmRail->Lock, flags);
++
++    ASSERT (cmRail->Rail->State == EP_RAIL_STATE_RUNNING);
++
++    DoHeartbeatWork (cmRail);
++    
++    __Schedule_Timer (cmRail, cmRail->NextRunTime);
++
++    spin_unlock_irqrestore (&cmRail->Lock, flags);
++}
++
++#endif /* defined(PER_CPU_TIMEOUT) */
++
++
++
++void
++DisplayRailDo (DisplayInfo *di, EP_RAIL *rail)
++{
++    CM_RAIL *cmRail = rail->ClusterRail;
++    unsigned long flags;
++    int  i, j;
++
++    if (rail->State != EP_RAIL_STATE_RUNNING)
++      return;
++
++    spin_lock_irqsave (&cmRail->Lock, flags);
++
++    (di->func)(di->arg, "NodeId=%d NodeLevel=%d NumLevels=%d NumNodes=%d\n", 
++          cmRail->NodeId, cmRail->TopLevel, cmRail->NumLevels, cmRail->Rail->Position.pos_nodes);
++    
++    (di->func)(di->arg, "[");
++
++    for (i = 0; i < cmRail->NumLevels; i++)
++    {
++      if (i > 0)
++          (di->func)(di->arg, ",");
++       
++      if (i < cmRail->TopLevel)
++      {
++          (di->func)(di->arg, "L ");
++        
++          for (j = 0; j < cmRail->Levels[i].NumSegs; j++)
++              switch (cmRail->Levels[i].Sgmts[j].State)
++              {
++              case CM_SGMT_PRESENT: (di->func)(di->arg, "p%-4d", cmRail->Levels[i].Sgmts[j].NodeId); break;
++              case CM_SGMT_WAITING: (di->func)(di->arg, "w%4s", ""); break;
++              case CM_SGMT_COMING:  (di->func)(di->arg, "c%4s", ""); break;
++              case CM_SGMT_ABSENT:  (di->func)(di->arg, ".%4s", ""); break;
++              default:              (di->func)(di->arg, "?%4s", ""); break;
++              }
++      }
++      else
++          switch (cmRail->Role)
++          {
++          case CM_ROLE_LEADER_CANDIDATE:      
++              (di->func)(di->arg,"l "); 
++              for (j = 0; j < cmRail->Levels[i].NumSegs; j++)
++                  (di->func)(di->arg,"     ");
++              break;
++        
++          case CM_ROLE_SUBORDINATE:       
++              switch (cmRail->Levels[i].Sgmts[0].State)
++              {
++              case CM_SGMT_PRESENT: (di->func)(di->arg, "p%-4d", cmRail->Levels[i].Sgmts[0].NodeId); break;
++              case CM_SGMT_WAITING: (di->func)(di->arg, "w%4s", ""); break;
++              case CM_SGMT_COMING:  (di->func)(di->arg, "c%4s", ""); break;
++              case CM_SGMT_ABSENT:  (di->func)(di->arg, ".%4s", ""); break;
++              default:              (di->func)(di->arg, "?%4s", ""); break;
++              }
++              for (j = 1; j < cmRail->Levels[i].NumSegs; j++)
++                  (di->func)(di->arg, "     ");
++              break;
++        
++          default:
++              (di->func)(di->arg, "####");
++              break;
++          }
++    }
++    (di->func)(di->arg, "]\n");
++
++    spin_unlock_irqrestore (&cmRail->Lock, flags);
++}
++
++void
++DisplayRail (EP_RAIL *rail) 
++{
++    if (rail->State == EP_RAIL_STATE_RUNNING)
++      DisplayRailDo (&di_ep_debug, rail);
++}
++
++void
++DisplayStatus (EP_RAIL *rail)
++{
++    if (rail->State == EP_RAIL_STATE_RUNNING)
++    {
++      CM_RAIL *cmRail = rail->ClusterRail;
++      unsigned long flags;
++      
++      spin_lock_irqsave (&cmRail->Lock, flags);
++      
++      DisplayNodeMaps (&di_ep_debug, cmRail);
++      
++      spin_unlock_irqrestore (&cmRail->Lock, flags);
++    }
++}
++
++void
++DisplaySegs (EP_RAIL *rail)
++{
++    if (rail->State == EP_RAIL_STATE_RUNNING)
++    {
++      CM_RAIL *cmRail = rail->ClusterRail;
++      unsigned long flags;
++      
++      spin_lock_irqsave (&cmRail->Lock, flags);
++      
++      DisplayNodeSgmts (&di_ep_debug, cmRail);
++      
++      spin_unlock_irqrestore (&cmRail->Lock, flags);
++    }
++}
++
++static void
++LoadBroadcastRoute (CM_RAIL *cmRail, int lvl, int sidx)
++{
++    EP_RAIL *rail  = cmRail->Rail;
++    int      nsegs = cmRail->Levels[0].NumSegs;
++    int      vp    = EP_VP_BCAST(lvl, sidx);
++    int      nodes = 1;
++    int      baseNode;
++    int      i;
++
++    ASSERT (lvl > 0 && lvl <= cmRail->NumLevels);
++    ASSERT (sidx == 0 || lvl < cmRail->NumLevels);
++
++    ASSERT (vp >= EP_VP_BCAST_BASE && vp < EP_VP_BCAST_BASE + EP_VP_BCAST_COUNT);
++
++    for (i = 1; i <= lvl; i++)
++    {
++      nodes *= nsegs;
++      nsegs = (i == cmRail->NumLevels) ? 1 : cmRail->Levels[i].NumSegs;
++    }
++
++    baseNode = ((cmRail->NodeId / (nodes * nsegs)) * nsegs + sidx) * nodes;
++
++    CPRINTF5 (2, "%s: broadcast vp lvl %d sidx %d [%d,%d]\n", 
++            cmRail->Rail->Name, lvl, sidx, baseNode, baseNode + nodes - 1);
++    
++    rail->Operations.LoadSystemRoute (rail, vp, baseNode, baseNode + nodes - 1);
++}
++
++static void
++LoadRouteTable (CM_RAIL *cmRail)
++{
++    EP_RAIL *rail = cmRail->Rail;
++    int      i, j;
++   
++   if (cmRail->NumNodes > EP_MAX_NODES)
++   {
++       printk ("More nodes (%d) than point-to-point virtual process table entries (%d)\n", cmRail->NumNodes, EP_MAX_NODES);
++       panic ("LoadRouteTable\n");
++   }
++
++   for (i = 0; i < cmRail->NumNodes; i++)
++       rail->Operations.LoadSystemRoute (rail, EP_VP_NODE(i), i, i);
++
++   /* Generate broadcast routes for subtrees */
++   for (i = 1; i < cmRail->NumLevels; i++)
++      for (j = 0; j < cmRail->Levels[i].NumSegs; j++)
++        LoadBroadcastRoute (cmRail, i, j);
++
++   /* Generate broadcast route for whole machine */
++   LoadBroadcastRoute (cmRail, cmRail->NumLevels, 0);
++
++   /* Finally invalidate all the data routes */
++   for (i = 0; i < cmRail->NumNodes; i++)
++       rail->Operations.UnloadNodeRoute (cmRail->Rail, i);
++}
++
++void
++cm_node_disconnected (EP_RAIL *rail, unsigned nodeId)
++{
++    CM_RAIL *cmRail = rail->ClusterRail;
++    int      base, lstat, lgstat;
++    int            clvl, subClMin, subClMax;
++    int      thisClId, myClId;
++    unsigned long flags;
++
++    ASSERT (nodeId != cmRail->NodeId);
++
++    spin_lock_irqsave (&cmRail->Lock, flags);
++    for (clvl = 0; clvl < cmRail->NumLevels; clvl++)
++      if (nodeId >= cmRail->Levels[clvl].MinNodeId && nodeId < (cmRail->Levels[clvl].MinNodeId + cmRail->Levels[clvl].NumNodes))
++          break;
++
++    myClId   = ClusterIds (cmRail, clvl, &subClMin, &subClMax);
++    thisClId = nodeId - cmRail->Levels[clvl].MinNodeId;
++    base     = thisClId * CM_GSTATUS_BITS;
++    lstat    = statemap_getbits (cmRail->Levels[clvl].LocalMap,  base, CM_GSTATUS_BITS);
++    lgstat   = statemap_getbits (cmRail->Levels[clvl].LastGlobalMap, base, CM_GSTATUS_BITS) & CM_GSTATUS_STATUS_MASK;
++
++    ASSERT ((lstat & CM_GSTATUS_ACK_MASK) == CM_GSTATUS_MAY_RUN);
++
++    CPRINTF7 (2, "%s: cm_node_disconnected: Node %d: clvl %d, lgstat %s, gstat %s, lstat %s -> %sMAY_START\n",
++            cmRail->Rail->Name, nodeId, clvl,
++            GlobalStatusString (cmRail->Levels[clvl].LastGlobalMap, thisClId),
++            GlobalStatusString (cmRail->Levels[clvl].GlobalMap, thisClId),
++            GlobalStatusString (cmRail->Levels[clvl].LocalMap, thisClId),
++            ((lgstat != CM_GSTATUS_CLOSING) && (lstat & CM_GSTATUS_RESTART)) ? "RESTART|" : "");
++    
++    switch (lgstat)
++    {
++    case CM_GSTATUS_CLOSING:
++      /* delayed ack of closing - set MAY_START and clear RESTART */
++      statemap_setbits (cmRail->Levels[clvl].LocalMap, base, CM_GSTATUS_MAY_START, CM_GSTATUS_BITS);
++      break;
++    case CM_GSTATUS_STARTING:
++    case CM_GSTATUS_RUNNING:
++      IASSERT (! cmRail->Levels[clvl].Online || lstat & CM_GSTATUS_RESTART);
++      break;
++    case CM_GSTATUS_ABSENT:
++      IASSERT (lstat & CM_GSTATUS_RESTART);
++    }
++
++    cmRail->Levels[clvl].Connected--;
++
++    spin_unlock_irqrestore (&cmRail->Lock, flags);
++}
++
++void
++cm_restart_node (EP_RAIL *rail, unsigned nodeId)
++{
++    CM_RAIL *cmRail = rail->ClusterRail;
++    int      base, lstat, lgstat;
++    int            clvl, subClMin, subClMax;
++    int      thisClId, myClId;
++    unsigned long flags;
++
++    spin_lock_irqsave (&cmRail->Lock, flags);
++    if (nodeId == rail->Position.pos_nodeid)
++    {
++      for (clvl = 0; clvl < cmRail->NumLevels; clvl++)
++          RestartComms (cmRail, clvl);
++    }
++    else
++    {
++      for (clvl = 0; clvl < cmRail->NumLevels; clvl++)
++          if (nodeId >= cmRail->Levels[clvl].MinNodeId && nodeId < (cmRail->Levels[clvl].MinNodeId + cmRail->Levels[clvl].NumNodes))
++              break;
++      
++      myClId   = ClusterIds (cmRail, clvl, &subClMin, &subClMax);
++      thisClId = nodeId - cmRail->Levels[clvl].MinNodeId;
++      base     = thisClId * CM_GSTATUS_BITS;
++      lstat    = statemap_getbits (cmRail->Levels[clvl].LocalMap,  base, CM_GSTATUS_BITS);
++      lgstat   = statemap_getbits (cmRail->Levels[clvl].LastGlobalMap,  base, CM_GSTATUS_BITS) & CM_GSTATUS_STATUS_MASK;
++
++      CPRINTF6 (2, "%s: cm_restart_node: Node %d: clvl %d, lgstat %s, gstat %s, lstat %s\n",
++                cmRail->Rail->Name, nodeId, clvl,
++                GlobalStatusString (cmRail->Levels[clvl].LastGlobalMap, thisClId),
++                GlobalStatusString (cmRail->Levels[clvl].GlobalMap, thisClId),
++                GlobalStatusString (cmRail->Levels[clvl].LocalMap, thisClId));
++      
++      if (lgstat != CM_GSTATUS_CLOSING)
++          statemap_setbits (cmRail->Levels[clvl].LocalMap, base, lstat | CM_GSTATUS_RESTART, CM_GSTATUS_BITS);
++    }
++    spin_unlock_irqrestore (&cmRail->Lock, flags);
++}
++
++void
++cm_force_offline (EP_RAIL *rail, int offline, unsigned int reason)
++{
++    CM_RAIL *cmRail = rail->ClusterRail;
++    unsigned long flags;
++
++    spin_lock_irqsave (&cmRail->Lock, flags);
++    if (offline)
++      cmRail->OfflineReasons |= reason;
++    else
++      cmRail->OfflineReasons &= ~reason;
++    spin_unlock_irqrestore (&cmRail->Lock, flags);
++}
++
++static void
++cm_remove_rail (EP_SUBSYS *subsys, EP_SYS *epsys, EP_RAIL *rail)
++{
++    CM_SUBSYS  *sys    = (CM_SUBSYS *) subsys;
++    CM_RAIL    *cmRail = sys->Rails[rail->Number];
++    int i, lvl, clvl;
++
++    cm_procfs_rail_fini (cmRail);
++
++    sys->Rails[rail->Number] = NULL;
++    rail->ClusterRail        = NULL;
++
++#if defined(PER_CPU_TIMEOUT)
++    StopPerCpuTimeouts (cmRail);
++#else
++    del_timer_sync (&cmRail->HeartbeatTimer);
++#endif
++    cmRail->NextRunTime      = 0;
++    cmRail->NextDiscoverTime = 0;
++    cmRail->NextHeartbeatTime = 0;
++    
++    for (clvl = 0; clvl < cmRail->NumLevels; clvl++)
++    {
++      for (lvl = 0; lvl <= clvl; lvl++)
++      {
++          CM_LEVEL *level = &cmRail->Levels[lvl];
++          
++          statemap_destroy (level->SubordinateMap[clvl]);
++          
++          for (i = 0; i < level->NumSegs; i++)
++          {
++              statemap_destroy (level->Sgmts[i].Maps[clvl].CurrentInputMap);
++              statemap_destroy (level->Sgmts[i].Maps[clvl].InputMap);
++              statemap_destroy (level->Sgmts[i].Maps[clvl].OutputMap);
++          }
++      }
++      
++      cmRail->Levels[clvl].Online = 0;
++      
++      statemap_destroy (cmRail->Levels[clvl].TmpMap);
++      statemap_destroy (cmRail->Levels[clvl].GlobalMap);
++      statemap_destroy (cmRail->Levels[clvl].LastGlobalMap);
++      statemap_destroy (cmRail->Levels[clvl].SubTreeMap);
++      statemap_destroy (cmRail->Levels[clvl].LocalMap);
++    }
++    
++    spin_lock_destroy (&cmRail->Lock);
++
++    ep_free_inputq (cmRail->Rail, cmRail->PolledQueue);
++    ep_free_inputq (cmRail->Rail, cmRail->IntrQueue);
++    ep_free_outputq (cmRail->Rail, cmRail->MsgQueue);
++
++    KMEM_FREE (cmRail, sizeof (CM_RAIL));
++}
++
++static int
++cm_add_rail (EP_SUBSYS *subsys, EP_SYS *epsys, EP_RAIL *rail)
++{
++    CM_SUBSYS     *sys = (CM_SUBSYS *) subsys;
++    ELAN_POSITION *pos = &rail->Position;
++    CM_RAIL       *cmRail;
++    int lvl, n, nn, clvl, span, i;
++    unsigned long flags;
++
++    KMEM_ZALLOC (cmRail, CM_RAIL *, sizeof (CM_RAIL), 1);
++
++    if (cmRail == NULL)
++      return (ENOMEM);
++    
++    cmRail->Rail     = rail;
++    cmRail->NodeId   = pos->pos_nodeid;
++    cmRail->NumNodes = pos->pos_nodes;
++
++    spin_lock_init (&cmRail->Lock);
++
++    if ((cmRail->IntrQueue   = ep_alloc_inputq (rail, EP_SYSTEMQ_INTR,   sizeof (CM_MSG), CM_INPUTQ_ENTRIES, IntrQueueCallback, cmRail)) == NULL ||
++      (cmRail->PolledQueue = ep_alloc_inputq (rail, EP_SYSTEMQ_POLLED, sizeof (CM_MSG), CM_INPUTQ_ENTRIES, NULL, 0)) == NULL ||
++      (cmRail->MsgQueue    = ep_alloc_outputq (rail, sizeof (CM_MSG), CM_NUM_MSG_BUFFERS)) == NULL)
++    {
++      goto failed;
++    }
++
++    /* point to first "spare" message buffer */
++    cmRail->NextSpareMsg = 0;
++
++    /* Compute the branching ratios from the switcy arity */
++    for (lvl = 0; lvl < CM_MAX_LEVELS; lvl++)
++      BranchingRatios[lvl] = (lvl < pos->pos_levels) ? pos->pos_arity[pos->pos_levels - lvl - 1] : 4;
++    
++    /* now determine the number of levels of hierachy we have */
++    /* and how many nodes per level there are */
++    for (lvl = 0, nn = 1, n = pos->pos_nodes; 
++       n > 1; 
++       nn *= BranchingRatios[lvl], n = n / BranchingRatios[lvl], lvl++)
++    {
++      int       nSegs = (n > BranchingRatios[lvl]) ? BranchingRatios[lvl] : n;
++      int       nNodes = nn * nSegs;
++      CM_LEVEL *level = &cmRail->Levels[lvl];
++
++      for (clvl = 0, span = pos->pos_arity[pos->pos_levels - clvl - 1]; 
++           span < nNodes && clvl < pos->pos_levels - 1;
++           clvl++, span *= pos->pos_arity[pos->pos_levels - clvl - 1])
++          ;
++      
++      level->SwitchLevel = clvl;
++      level->MinNodeId = (pos->pos_nodeid / nNodes) * nNodes;
++      level->NumNodes = nNodes;
++      level->NumSegs = nSegs;
++    }
++    
++    cmRail->NumLevels      = lvl;
++    cmRail->BroadcastLevel = lvl-1;
++
++    CPRINTF4 (2, "%s: NodeId=%d NumNodes=%d NumLevels=%d\n", 
++            rail->Name, pos->pos_nodeid, pos->pos_nodes, cmRail->NumLevels);
++
++    LoadRouteTable (cmRail);
++    
++    /* Init SGMT constants */
++    for (lvl = 0; lvl < cmRail->NumLevels; lvl++)
++    {
++      CM_LEVEL *level = &cmRail->Levels[lvl];
++
++      level->MySgmt = SegmentNo (cmRail, cmRail->NodeId, lvl);
++       
++      for (i = 0; i < CM_SGMTS_PER_LEVEL; i++)
++      {
++          CM_SGMT *sgmt = &level->Sgmts[i];
++        
++          sgmt->MsgNumber = lvl * CM_SGMTS_PER_LEVEL + i;
++          sgmt->Level = lvl;
++          sgmt->Sgmt = i;
++      }
++    }
++
++    /* Init maps for each cluster level */
++    for (clvl = 0; clvl < cmRail->NumLevels; clvl++)
++    {
++      int nNodes = cmRail->Levels[clvl].NumNodes;
++      int mapBits = (nNodes * CM_GSTATUS_BITS) + (nNodes * EP_SVC_NUM_INDICATORS);
++      int clmin;
++      int clmax;
++      int clid = ClusterIds (cmRail, clvl, &clmin, &clmax);
++
++      for (lvl = 0; lvl <= clvl; lvl++)
++      {
++          CM_LEVEL *level = &cmRail->Levels[lvl];
++
++          level->SubordinateMap[clvl] = statemap_create (mapBits);
++
++          for (i = 0; i < level->NumSegs; i++)
++          {
++              level->Sgmts[i].Maps[clvl].CurrentInputMap = statemap_create (mapBits);
++              level->Sgmts[i].Maps[clvl].InputMap        = statemap_create (mapBits);
++              level->Sgmts[i].Maps[clvl].OutputMap       = statemap_create (mapBits);
++          }
++      }
++      
++      cmRail->Levels[clvl].Online = 0;
++
++      cmRail->Levels[clvl].TmpMap        = statemap_create (mapBits);
++      cmRail->Levels[clvl].GlobalMap     = statemap_create (mapBits);
++      cmRail->Levels[clvl].LastGlobalMap = statemap_create (mapBits);
++      cmRail->Levels[clvl].SubTreeMap    = statemap_create (mapBits);
++      cmRail->Levels[clvl].LocalMap      = statemap_create (mapBits);
++
++      /* Flag everyone outside my next lower cluster as sensed offline... */
++      for (i = 0; i < clmin; i++)
++          statemap_setbits (cmRail->Levels[clvl].LocalMap, i * CM_GSTATUS_BITS, CM_GSTATUS_MAY_START, CM_GSTATUS_BITS);
++      
++      for (i = clmax + 1; i < nNodes; i++)
++          statemap_setbits (cmRail->Levels[clvl].LocalMap, i * CM_GSTATUS_BITS, CM_GSTATUS_MAY_START, CM_GSTATUS_BITS);
++      
++      /* ...and set my own state */
++      statemap_setbits (cmRail->Levels[clvl].LocalMap, clid * CM_GSTATUS_BITS,
++                        CM_GSTATUS_CLOSING | CM_GSTATUS_MAY_START, CM_GSTATUS_BITS);
++    }
++    
++    /* compute parameter hash to add to messages */
++    cmRail->ParamHash = EP_PROTOCOL_VERSION;
++    cmRail->ParamHash = cmRail->ParamHash * 127 + CM_PERIODIC_DISCOVER_INTERVAL;
++    cmRail->ParamHash = cmRail->ParamHash * 127 + CM_URGENT_DISCOVER_INTERVAL;
++    cmRail->ParamHash = cmRail->ParamHash * 127 + CM_HEARTBEAT_INTERVAL;
++    cmRail->ParamHash = cmRail->ParamHash * 127 + CM_P2P_DMA_RETRIES;
++    cmRail->ParamHash = cmRail->ParamHash * 127 + CM_P2P_MSG_RETRIES;
++    cmRail->ParamHash = cmRail->ParamHash * 127 + CM_BCAST_MSG_RETRIES;
++    cmRail->ParamHash = cmRail->ParamHash * 127 + CM_TIMER_SCHEDULE_TIMEOUT;
++    cmRail->ParamHash = cmRail->ParamHash * 127 + CM_HEARTBEAT_TIMEOUT;
++    cmRail->ParamHash = cmRail->ParamHash * 127 + CM_DISCOVER_TIMEOUT;
++    cmRail->ParamHash = cmRail->ParamHash * 127 + BT_NBIPUL;
++    cmRail->ParamHash = cmRail->ParamHash * 127 + CM_GSTATUS_BITS;
++    cmRail->ParamHash = cmRail->ParamHash * 127 + EP_SVC_NUM_INDICATORS;
++    cmRail->ParamHash = cmRail->ParamHash * 127 + cmRail->NumLevels;
++    cmRail->ParamHash = cmRail->ParamHash * 127 + cmRail->NumNodes;
++    for (i = 0; i < cmRail->NumLevels; i++)
++      cmRail->ParamHash = cmRail->ParamHash * 127 + BranchingRatios[i];
++    
++#if defined(PER_CPU_TIMEOUT)
++    StartPerCpuTimeouts (cmRail);
++#endif
++
++    spin_lock_irqsave (&cmRail->Lock, flags);
++
++#if !defined(PER_CPU_TIMEOUT)
++    /* Initialise the timer, but don't add it yet, since
++     * __Schedule_Heartbeat() will do this. */
++
++    init_timer (&cmRail->HeartbeatTimer);
++    
++    cmRail->HeartbeatTimer.function = cm_heartbeat_timer;
++    cmRail->HeartbeatTimer.data     = (unsigned long) cmRail;
++    cmRail->HeartbeatTimer.expires  = lbolt + hz;
++#endif
++
++    /* start sending heartbeats */
++    __Schedule_Heartbeat (cmRail);
++
++    /* start discovering who else is out there */
++    LowerTopLevel (cmRail, 0);
++
++    /* connect to myself straight away - I know I'm here */
++    ep_connect_node (rail, cmRail->NodeId);
++    
++    /* add to all rails */
++    sys->Rails[rail->Number] = cmRail;
++    rail->ClusterRail = (void *) cmRail;
++
++    spin_unlock_irqrestore (&cmRail->Lock, flags);
++
++    /* Enable the input queues */
++    ep_enable_inputq (rail, cmRail->PolledQueue);
++    ep_enable_inputq (rail, cmRail->IntrQueue);
++
++    /* Create the procfs entries */
++    cm_procfs_rail_init (cmRail);
++
++    return 0;
++
++ failed:
++    cm_remove_rail (subsys, epsys, rail);
++    return -ENOMEM;
++}
++
++static void
++cm_fini (EP_SUBSYS *subsys, EP_SYS *epsys)
++{
++    CM_SUBSYS *sys = (CM_SUBSYS *) subsys;
++
++    cm_procfs_fini(sys);
++    
++    KMEM_FREE (sys, sizeof (CM_SUBSYS));
++}
++
++int
++cm_init (EP_SYS *sys)
++{
++    CM_SUBSYS *subsys;
++
++    KMEM_ZALLOC (subsys, CM_SUBSYS *, sizeof (CM_SUBSYS), 1);
++
++    if (subsys == NULL)
++      return (ENOMEM);
++
++    subsys->Subsys.Sys        = sys;
++    subsys->Subsys.Name             = "cm";
++    subsys->Subsys.Destroy    = cm_fini;
++    subsys->Subsys.AddRail    = cm_add_rail;
++    subsys->Subsys.RemoveRail = cm_remove_rail;
++
++    ep_subsys_add (sys, &subsys->Subsys);
++
++    cm_procfs_init (subsys);
++
++    /*
++     * Initialise the machineid if it wasn't specified by
++     * the modules.conf file - otherwise truncate it to 
++     * 16 bits.
++     */
++    if (MachineId != -1)
++      MachineId = (uint16_t) MachineId;
++    else
++    {
++#if defined(LINUX_ALPHA)
++      MachineId = (uint16_t)((5 << 12) | HZ);
++#elif defined(LINUX_SPARC)
++      MachineId = (uint16_t)((4 << 12) | HZ);
++#elif defined(LINUX_I386)
++      MachineId = (uint16_t)((3 << 12) | HZ);
++#elif defined( LINUX_IA64)
++      MachineId = (uint16_t)((2 << 12) | HZ);
++#elif defined(LINUX_X86_64)
++      MachineId = (uint16_t)((1 << 12) | HZ);
++#else
++      MachineId = (uint16_t)((0 << 12) | HZ);
++#endif
++    }
++
++    return (0);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/ep/cm.h
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/cm.h 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/cm.h      2005-05-11 12:10:12.480926192 -0400
+@@ -0,0 +1,412 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN_CM_H
++#define __ELAN_CM_H
++
++#ident "@(#)$Id: cm.h,v 1.14.2.1 2004/11/12 10:54:50 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/cm.h,v $*/
++
++#include <elan/statemap.h>
++
++#if defined(DIGITAL_UNIX)
++/*
++ * On Tru64 - SMP doesn't mean Symmetric - cpu 0 is a master cpu and is responsible
++ * for handling all PCI interrupts and "funneled" operations.  When a kernel thread
++ * is made runnable, the scheduler will choose which cpu it will run on at that time,
++ * and will only execute a higher priority thread from another cpu's run queue when 
++ * it becomes totally idle (apparently also including user processes).  Also the 
++ * assert_wait_mesg_timo function uses a per-cpu timeout - these can only get executed
++ * at "preemptable" places - so again have no guarantee on when they will execute if
++ * they happen to be queued on a "hogged" cpu. The combination of these mean that the Tru64
++ * is incapable of scheduling a high priority kernel  thread within a deterministic time
++ * of when it should have become runnable - wonderfull.
++ *
++ * Hence the solution Compaq have proposed it to schedule a timeout onto all of the
++ * cpu's timeouts lists at the maximum frequency that we could want to execute code,
++ * then to handle the scheduling of work between these ourselves.  With a bit of luck
++ * ..... at least one cpu will be sufficiently unloaded to allow us to get a chance
++ * to do our important work.
++ *
++ * However ..... this still is not reliable, since timeouts under Tru64 are still 
++ * only run when the currently running kernel thread "co-operates" by calling one
++ * of a number of functions which is permitted to run the "lwc"s AND is not holding
++ * any spinlocks AND is running ai IPL 0.   However Compaq are unable to provide
++ * any upper limit on the time between the "lwc"'s being run and so it is possible
++ * for all 4 cpus to not run them for an unbounded time.
++ *
++ * The solution proposed is to use the RM_TEMP_BACKDOOR hook which was added to 
++ * hardclock() to "solve" this problem for Memory Channel.  However, since it
++ * is called within the clock interrupt it is not permissible to aquire any
++ * spinlocks, nor to run for "too long".  This means that it is not possible to
++ * call the heartbeat algorithm from this hook.  
++ *
++ * Our solution to these limitations is to use the hook to cause an elan interrupt 
++ * to be delivered, by issueing a mis-aligned SetEvent command - this causes the device 
++ * to trap and ep_cprocTrap() can then run the heartbeat code.  However there is a lock 
++ * order violation between the elan_dev::IntrLock and ep_dev::Lock, so we have to 
++ * use a trylock and if we fail, then hope that when the interrupt is delievered again
++ * some time later we will succeed.
++ *
++ * However this only works if the kernel is able to respond to the Elan interrupt,
++ * so we panic inside the RM_TEMP_BACKDOOR hook if the SetEvent's interrupt has
++ * not been taken for more than an CM_TIMER_SCHEDULE_TIMEOUT interval.
++ *
++ * In fact this is exactly the mechanism that other operating systems use to
++ * execute timeouts, since the hardclock interrupt posts a low priority 
++ * "soft interrupt" which "pre-eempts" the currently running thread and then
++ * executes the timeouts.To block timeouts you use splsoftclock() the same as 
++ * in Tru64.
++ */
++#define PER_CPU_TIMEOUT                       TRUE
++#endif
++
++
++#define CM_SGMTS_PER_LEVEL            8                       /* maximum nodes in each segment */
++#define CM_MAX_LEVELS                 6                       /* maximum depth of tree */
++
++/* message buffers/dmas/events etc */
++#define CM_NUM_NODE_MSG_BUFFERS               (CM_MAX_LEVELS * CM_SGMTS_PER_LEVEL) /* subordinates and leader */
++#define CM_NUM_SPARE_MSG_BUFFERS      8                       /* spare msg buffers for non-connected nodes */
++#define CM_NUM_MSG_BUFFERS            (CM_NUM_NODE_MSG_BUFFERS + CM_NUM_SPARE_MSG_BUFFERS)
++
++#define CM_INPUTQ_ENTRIES             128                     /* # entries in input queue */
++
++#define CM_PERIODIC_DISCOVER_INTERVAL (5000)          /* 5s (infrequent resolution of established leader conflicts) */
++#define CM_URGENT_DISCOVER_INTERVAL   (50)            /* 0.05s (more frequently than heartbeats 'cause they don't retry) */
++#define CM_HEARTBEAT_INTERVAL         (125)           /* 0.125s */
++#define CM_TIMER_SCHEDULE_TIMEOUT     (4000)          /* 4s     Maximum time before a timer that's secheduled to run gets to run (eg blocked in interrupt handlers etc) */
++#define CM_THREAD_SCHEDULE_TIMEOUT    (30000)         /* 30s    Maximum time before a thread that's scheduled to run gets to run */
++#define CM_THREAD_RUNNING_TIMEOUT     (30000)         /* 30s    Don't expect the manager thread to be running longer than this */
++
++#ifdef PER_CPU_TIMEOUT
++#define CM_PERCPU_TIMEOUT_INTERVAL    (50)            /* 0.05s (must be less than all above intervals) */
++#define CM_PACEMAKER_INTERVAL         (500)           /* 0.05s */
++
++#define CM_HEARTBEAT_OVERDUE          (250)           /* 0.25s Maximum time a timeout can be overdue before taking extreme action */
++#endif
++
++#define CM_P2P_DMA_RETRIES            31
++
++/* We expect at least 1 point-to-point message in CM_P2P_MSG_RETRIES
++ * attempts to send one to be successfully received */
++#define CM_P2P_MSG_RETRIES            8
++
++/* We expect at least 1 broadcast message in CM_BCAST_MSG_RETRIES attempts
++ * to send one to be successfully received. */
++#define CM_BCAST_MSG_RETRIES          40
++
++/* Heartbeat timeout allows for a node stalling and still getting its
++ * heartbeat. The 2 is to allow for unsynchronised polling times. */
++#define CM_HEARTBEAT_TIMEOUT          (CM_TIMER_SCHEDULE_TIMEOUT + (2 + CM_P2P_MSG_RETRIES) * CM_HEARTBEAT_INTERVAL)
++
++/* Discover timeout must be > CM_HEARTBEAT_TIMEOUT to guarantee that people
++ * who don't see discovery are considered dead by their leader.  This
++ * ensures that by the time a node "discovers" it is a leader of a segment,
++ * the previous leader of that segment will have been deemed to be dead by
++ * its the parent segment's leader */
++#define CM_DISCOVER_TIMEOUT           (CM_TIMER_SCHEDULE_TIMEOUT + (2 + CM_BCAST_MSG_RETRIES) * CM_URGENT_DISCOVER_INTERVAL)
++
++#define CM_WAITING_TIMEOUT            (CM_DISCOVER_TIMEOUT * 100)
++
++/*
++ * Convert all timeouts specified in mS into "ticks"
++ */
++#define MSEC2TICKS(MSEC)              (((MSEC)*HZ)/1000)
++
++
++/* statemap entry */
++typedef struct cm_state_entry
++{
++    int16_t           level;                  /* cluster level to apply to */
++    int16_t         offset;                   /* from statemap_findchange() */
++    uint16_t          seg[BT_NBIPUL/16];      /* ditto */
++} CM_STATEMAP_ENTRY;
++
++/* offset is >= 0 for a change to apply and */
++#define STATEMAP_NOMORECHANGES        (-1)            /* end of a set of updates */
++#define STATEMAP_RESET                (-2)            /* reset the target map */
++#define STATEMAP_NOOP         (-3)            /* null token */
++
++/* CM message format */
++typedef int8_t        CM_SEQ;                         /* heartbeat sequence numbers; at least 2 bits, signed */
++
++/*
++ * The message header is received into the last 64 byte block of 
++ * the input queue and the Version *MUST* be the last word of the 
++ * block to ensure that we can see that the whole of the message
++ * has reached main memory after we've seen the input queue pointer
++ * have been updated.
++ */
++typedef struct ep_cm_hdr
++{
++    uint32_t         Pad0;
++    uint32_t         Pad1;
++
++    uint8_t          Type;
++    uint8_t          Level;
++    CM_SEQ           Seq;                     /* precision at least 2 bits each*/
++    CM_SEQ           AckSeq;
++    
++    uint16_t         NumMaps;
++    uint16_t         MachineId;
++
++    uint16_t         NodeId;
++    uint16_t         Checksum;
++
++    uint32_t           Timestamp;
++    uint32_t           ParamHash;
++    uint32_t         Version;
++} CM_HDR;
++
++#define CM_HDR_SIZE       sizeof (CM_HDR)
++
++typedef struct cm_msg
++{
++    union {
++      CM_STATEMAP_ENTRY   Statemaps[1];               /* piggy-backed statemap updates start here */
++      uint8_t             Space[EP_SYSTEMQ_MSG_MAX - CM_HDR_SIZE];
++    } Payload;
++    
++    CM_HDR                Hdr;
++} CM_MSG;
++
++/* The maximum number of statemap entries that can fit within an EP_CM_MSG_BUFFER */
++#define CM_MSG_MAXMAPS                (offsetof (CM_MSG, Hdr) / sizeof (CM_STATEMAP_ENTRY))
++#define CM_MSG_MAP(mapno)     (CM_MSG_MAXMAPS - (mapno) - 1)
++
++/* The actual special message base & size, including 'nmaps' piggy-backed statemap entries */
++#define CM_MSG_BASE(nmaps)    (nmaps == 0 ? offsetof (CM_MSG, Hdr) : offsetof (CM_MSG, Payload.Statemaps[CM_MSG_MAXMAPS - nmaps]))
++#define CM_MSG_SIZE(nmaps)    (sizeof (CM_MSG) - CM_MSG_BASE(nmaps))
++
++#define CM_MSG_VERSION                                0xcad00005
++#define CM_MSG_TYPE_RESOLVE_LEADER            0
++#define CM_MSG_TYPE_DISCOVER_LEADER           1
++#define CM_MSG_TYPE_NOTIFY                    2
++#define CM_MSG_TYPE_DISCOVER_SUBORDINATE      3
++#define CM_MSG_TYPE_IMCOMING                  4
++#define CM_MSG_TYPE_HEARTBEAT                 5
++#define CM_MSG_TYPE_REJOIN                    6
++
++/* CM machine segment */
++typedef struct cm_sgmtMaps
++{
++    u_char       InputMapValid;                       /* Input map has been set */
++    u_char       OutputMapValid;              /* Output map has been set */
++    u_char       SentChanges;                 /* got an outstanding STATEMAP_NOMORECHANGES to send */
++    statemap_t  *OutputMap;                   /* state to send */
++    statemap_t  *InputMap;                    /* state received */
++    statemap_t  *CurrentInputMap;             /* state being received */
++} CM_SGMTMAPS;
++
++typedef struct cm_sgmt
++{
++   u_char       State;
++   u_char       SendMaps;
++   u_char       MsgAcked;
++   CM_SEQ     MsgSeq;
++   CM_SEQ     AckSeq;
++   u_int      NodeId;
++   long               UpdateTick;
++   long               WaitingTick;
++   uint32_t   Timestamp;
++   CM_SGMTMAPS  Maps[CM_MAX_LEVELS];          /* Maps[i] == state for cluster level i */
++   u_short      MsgNumber;                    /* msg buffer to use */
++   u_short    NumMaps;                        /* # maps in message buffer */
++   u_short      Level;
++   u_short      Sgmt;
++} CM_SGMT;
++
++#define CM_SGMT_ABSENT                0               /* no one there at all */
++#define CM_SGMT_WAITING               1               /* waiting for subtree to connect */
++#define CM_SGMT_COMING                2               /* expecting a subtree to reconnect */
++#define CM_SGMT_PRESENT               3               /* connected */
++
++typedef struct cm_level
++{
++    int              SwitchLevel;
++    u_int            MinNodeId;
++    u_int              NumNodes;
++    u_int              NumSegs;
++    u_int              MySgmt;
++   
++    /* SubordinateMap[i] == OR of all subordinate maps on this level and down for cluster level i */
++    u_char             SubordinateMapValid[CM_MAX_LEVELS];
++    statemap_t        *SubordinateMap[CM_MAX_LEVELS];
++
++    /* maps/flags for this cluster level */
++    u_int              Online:1;                              /* I've gone online (seen myself running) */
++    u_int            Restarting:1;                            /* driving my owm restart bit */
++    u_char           OfflineReasons;                          /* forced offline by broadcast */
++
++    u_char             GlobalMapValid;
++    u_char             SubTreeMapValid;
++    u_long           Connected;
++
++    statemap_t        *LocalMap;              /* state bits I drive */
++    statemap_t        *SubTreeMap;            /* OR of my and my subtree states */
++    statemap_t        *GlobalMap;             /* OR of all node states */
++    statemap_t        *LastGlobalMap;         /* last map I saw */
++    statemap_t        *TmpMap;                        /* scratchpad */
++
++    CM_SGMT          Sgmts[CM_SGMTS_PER_LEVEL];
++} CM_LEVEL;
++
++#define CM_ROLE_LEADER_CANDIDATE      0
++#define CM_ROLE_LEADER                        1
++#define CM_ROLE_SUBORDINATE           2
++
++/* global status bits */
++#define CM_GSTATUS_STATUS_MASK                0x03    /* bits nodes drive to broadcast their status */
++#define CM_GSTATUS_ABSENT             0x00    /* Off the network */
++#define CM_GSTATUS_STARTING           0x01    /* I'm waiting for everyone to see me online */
++#define CM_GSTATUS_RUNNING              0x03  /* up and running */
++#define CM_GSTATUS_CLOSING            0x02    /* I'm waiting for everyone to see me offline */
++
++#define CM_GSTATUS_ACK_MASK           0x0c    /* bits node drive to ack other status */
++#define CM_GSTATUS_MAY_START          0x04    /* Everyone thinks I may not start */
++#define CM_GSTATUS_MAY_RUN            0x08    /* Everyone thinks I may not run */
++
++#define CM_GSTATUS_RESTART            0x10    /* Someone thinks I should restart */
++#define CM_GSTATUS_BITS                       5
++
++#define CM_GSTATUS_BASE(node)         ((node) * CM_GSTATUS_BITS)
++
++#if defined(PER_CPU_TIMEOUT)
++typedef struct cm_timeout_data
++{
++    long              ScheduledAt;                            /* lbolt timeout was scheduled to run at */
++
++    unsigned long       EarlyCount;                           /* # times run early than NextRun */
++    unsigned long     MissedCount;                            /* # times run on time - but someone else was running it */
++    unsigned long       WastedCount;                          /* # times we failed to get the spinlock */
++    unsigned long     WorkCount;                              /* # times we're the one running */
++
++    unsigned long     WorstDelay;                             /* worst scheduling delay */
++    unsigned long     BestDelay;                              /* best scheduling delay */
++
++    unsigned long     WorstLockDelay;                         /* worst delay before getting rail->Lock */
++
++    unsigned long     WorstHearbeatDelay;                     /* worst delay before calling DoHeartbeatWork */
++} CM_TIMEOUT_DATA;
++#endif
++
++typedef struct cm_rail
++{
++    EP_RAIL         *Rail;                                    /* rail we're associated with */
++    struct list_head   Link;                                  /*   and linked on the CM_SUBSYS */
++
++    uint32_t         ParamHash;                               /* hash of critical parameters */
++    uint32_t           Timestamp;
++    long             DiscoverStartTick;                       /* when discovery start */
++
++    unsigned int       NodeId;                                        /* my node id */
++    unsigned int       NumNodes;                              /*   and number of nodes */
++    unsigned int       NumLevels;                             /* number of levels computed from machine size */
++    int                      BroadcastLevel;
++    long             BroadcastLevelTick;
++    unsigned int       TopLevel;                              /* level at which I'm not a leader */
++    unsigned char      Role;                                  /* state at TopLevel */
++
++    EP_INPUTQ       *PolledQueue;                             /* polled input queue */
++    EP_INPUTQ       *IntrQueue;                               /* intr input queue */
++    EP_OUTPUTQ              *MsgQueue;                                /* message  */
++    unsigned int       NextSpareMsg;                          /* next "spare" message buffer to use */
++
++    EP_CM_RAIL_STATS   Stats;                                 /* statistics */
++
++    kmutex_t         Mutex;
++    spinlock_t               Lock;
++    
++    long             NextHeartbeatTime;                       /* next time to check/send heartbeats */
++    long             NextDiscoverTime;                        /* next time to progress discovery  */
++    long             NextRunTime;                             /* the earlier of the above two or intr requires inputq poll*/
++
++    unsigned int       OfflineReasons;                                /* forced offline by procfs/manager thread stuck */
++
++#if defined(PER_CPU_TIMEOUT)
++    spinlock_t               HeartbeatTimeoutsLock;                   /* spinlock to sequentialise per-cpu timeouts */
++    long             HeartbeatTimeoutsStarted;                /* bitmap of which timeouts have started */
++    long             HeartbeatTimeoutsStopped;                /* bitmap of which timeouts have stopped */
++    long             HeartbeatTimeoutsShouldStop;             /* flag to indicate timeouts should stop */
++    kcondvar_t               HeartbeatTimeoutsWait;                   /* place to sleep waiting for timeouts to stop */
++    long             HeartbeatTimeoutRunning;                 /* someone is running the timeout - don't try for the lock */
++
++    long             HeartbeatTimeoutOverdue;                 /* heartbeat seen as overdue - interrupt requested */
++
++    CM_TIMEOUT_DATA   *HeartbeatTimeoutsData;                 /* per timeout data */
++#else
++    struct timer_list  HeartbeatTimer;                                /* timer for heartbeat/discovery */
++#endif
++
++    CM_LEVEL           Levels[CM_MAX_LEVELS];
++} CM_RAIL;
++
++/* OfflineReasons (both per-rail and  */
++#define CM_OFFLINE_BROADCAST          (1 << 0)
++#define CM_OFFLINE_PROCFS             (1 << 1)
++#define CM_OFFLINE_MANAGER            (1 << 2)
++
++typedef struct cm_subsys
++{
++    EP_SUBSYS         Subsys;
++    CM_RAIL            *Rails[EP_MAX_RAILS];
++} CM_SUBSYS;
++
++extern int  MachineId;
++
++extern void cm_node_disconnected (EP_RAIL *rail, unsigned nodeId);
++extern void cm_restart_node (EP_RAIL *rail, unsigned nodeId);
++extern void cm_restart_comms (CM_RAIL *cmRail);
++extern int  cm_init (EP_SYS *sys);
++
++extern void DisplayRail(EP_RAIL *rail);
++extern void DisplaySegs (EP_RAIL *rail);
++extern void DisplayStatus (EP_RAIL *rail);
++
++typedef struct proc_private
++{
++    struct nodeset_private *pr_next;
++    EP_RAIL                *pr_rail;
++    char                 *pr_data;
++    int                     pr_data_len;
++    unsigned              pr_off;
++    unsigned              pr_len;
++    DisplayInfo             pr_di;
++} PROC_PRIVATE;
++
++extern void    proc_character_fill (long mode, char *fmt, ...);
++extern int     proc_release (struct inode *inode, struct file *file);
++extern ssize_t proc_read (struct file *file, char *buf, size_t count, loff_t *ppos);
++
++
++extern void DisplayNodeMaps  (DisplayInfo *di, CM_RAIL *cmRail);
++extern void DisplayNodeSgmts (DisplayInfo *di, CM_RAIL *cmRail);
++extern void DisplayRailDo    (DisplayInfo *di, EP_RAIL *rail);
++
++extern int    cm_read_cluster(EP_RAIL *rail,char *page);
++extern void   cm_force_offline (EP_RAIL *rail, int offline, unsigned int reason);
++
++extern int    cm_svc_indicator_set      (EP_RAIL *rail, int svc_indicator);
++extern int    cm_svc_indicator_clear    (EP_RAIL *rail, int svc_indicator);
++extern int    cm_svc_indicator_is_set   (EP_RAIL *rail, int svc_indicator, int nodeId);
++extern int    cm_svc_indicator_bitmap   (EP_RAIL *rail, int svc_indicator, bitmap_t * bitmap, int low, int nnodes);
++
++/* cm_procfs.c */
++extern void   cm_procfs_init (CM_SUBSYS *subsys);
++extern void   cm_procfs_fini (CM_SUBSYS *subsys);
++extern void   cm_procfs_rail_init (CM_RAIL *rail);
++extern void   cm_procfs_rail_fini (CM_RAIL *rail);
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++#endif /* __ELAN_CM_H */
++
+Index: linux-2.6.5/drivers/net/qsnet/ep/cm_procfs.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/cm_procfs.c  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/cm_procfs.c       2005-05-11 12:10:12.480926192 -0400
+@@ -0,0 +1,254 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2005 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: cm_procfs.c,v 1.5 2004/05/14 09:23:13 daniel Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/cm_procfs.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++
++#include "kcomm_vp.h"
++#include "debug.h"
++#include "cm.h"
++#include <elan/epsvc.h>
++
++#include <qsnet/procfs_linux.h>
++
++extern char *sprintClPeers (char *str, CM_RAIL *cmRail, int clvl);
++
++static int
++proc_read_cluster(char *page, char **start, off_t off,
++              int count, int *eof, void *data)
++{
++    CM_RAIL *cmRail = (CM_RAIL *) data;
++    char    *p      = page;
++
++    page[0] = 0;
++
++    if (cmRail->Rail->State != EP_RAIL_STATE_RUNNING)
++      p += sprintf(p, "<not running>\n");
++    else
++    {
++      CM_LEVEL *cmLevel;
++      unsigned long flags;
++      int  i, j;
++      char clNodeStr[32]; /* [%d-%d][%d-%d] */
++      char seperate_with;
++
++      struct { int val; char *name; } bitvals[] = {
++          {CM_OFFLINE_BROADCAST, "Broadcast"},
++          {CM_OFFLINE_PROCFS,    "Offline"},
++          {CM_OFFLINE_MANAGER,   "Manager"}};
++      
++      spin_lock_irqsave (&cmRail->Lock, flags);
++      
++      for (i = 0; i < cmRail->NumLevels; i++)
++      {
++          cmLevel = &cmRail->Levels[i];
++          
++          p += sprintf(p, "%23s %7s ", sprintClPeers (clNodeStr, cmRail, i), cmLevel->Online?"Online":"Offline");
++          
++          if ((cmLevel->Online ) | ( cmLevel->Connected > 0))
++              p += sprintf(p, "Connected=%lu ", cmLevel->Connected);
++          
++          seperate_with = '<';
++          
++          if ( cmLevel->Restarting ) {
++              p += sprintf(p, "%cRestarting", seperate_with);
++              seperate_with = ',';
++          }
++          
++          if ( ! (cmLevel->GlobalMapValid & cmLevel->SubTreeMapValid )) {
++              p += sprintf(p, "%cMap Not Valid", seperate_with);
++              seperate_with = ',';
++          }
++          
++          if ( cmLevel->OfflineReasons ) {
++              for (j = 0; j < sizeof (bitvals)/sizeof(bitvals[0]); j++)
++                  if (cmLevel->OfflineReasons & bitvals[j].val) {
++                      p += sprintf(p, "%c%s", seperate_with, bitvals[j].name);
++                      seperate_with = ',';
++                  }
++          }
++          if ( cmRail->OfflineReasons ) {
++              for (j = 0; j < sizeof (bitvals)/sizeof(bitvals[0]); j++)
++                  if (cmRail->OfflineReasons & bitvals[j].val) {
++                      p += sprintf(p, "%c%s", seperate_with, bitvals[j].name);
++                      seperate_with = ',';
++                  }
++          }
++          
++          if ( seperate_with != '<' ) 
++              p += sprintf(p,">\n");
++          else
++              p += sprintf(p,"\n");
++      }
++      
++      spin_unlock_irqrestore (&cmRail->Lock, flags);
++    }
++
++    return qsnet_proc_calc_metrics (page, start, off, count, eof, p - page);
++}
++
++static struct rail_info
++{
++    char *name;
++    int (*read_func) (char *page, char **start, off_t off, int count, int *eof, void *data);
++    int (*write_func) (struct file *file, const char *buf, unsigned long count, void *data);
++} rail_info[] = {
++    {"cluster", proc_read_cluster, NULL},
++};
++
++struct proc_dir_entry *svc_indicators_root;
++
++typedef struct svc_indicator_data
++{
++    int       svc_indicator;
++    EP_RAIL  *rail;
++} SVC_INDICATOR_DATA;
++
++static SVC_INDICATOR_DATA svc_indicator_data[EP_SVC_NUM_INDICATORS][EP_MAX_RAILS];
++static char              *svc_indicator_names[EP_SVC_NUM_INDICATORS] = EP_SVC_NAMES;
++
++static int
++proc_read_svc_indicator_rail_bitmap (char *page, char **start, off_t off,
++                                   int count, int *eof, void *data)
++{
++    SVC_INDICATOR_DATA  *svc_data = (SVC_INDICATOR_DATA  *)data;
++    unsigned int       nnodes   = ep_numnodes (ep_system());
++    bitmap_t          *bitmap;
++
++    KMEM_ZALLOC (bitmap, bitmap_t *, (BT_BITOUL(EP_MAX_NODES) * sizeof (bitmap_t)), 1);
++
++    cm_svc_indicator_bitmap (svc_data->rail, svc_data->svc_indicator, bitmap, 0, nnodes);
++
++    ep_sprintf_bitmap (page, PAGESIZE, bitmap, 0, 0, nnodes);
++    
++    KMEM_FREE (bitmap, (BT_BITOUL(EP_MAX_NODES) * sizeof (bitmap_t)));
++    
++    strcat (page, "\n");
++
++    return (qsnet_proc_calc_metrics (page, start, off, count, eof, strlen(page)));
++}
++
++static int
++proc_read_svc_indicator_bitmap(char *page, char **start, off_t off,
++                             int count, int *eof, void *data)
++{
++    unsigned int         num      = (unsigned long) data;
++    EP_SYS              *sys      = ep_system();
++    unsigned int         nnodes   = ep_numnodes (sys);
++    bitmap_t            *bitmap;
++
++    KMEM_ALLOC(bitmap, bitmap_t *, (BT_BITOUL(EP_MAX_NODES) * sizeof (bitmap_t)), 1);
++     
++    ep_svc_indicator_bitmap (sys, num, bitmap, 0, nnodes);
++
++    ep_sprintf_bitmap (page, PAGESIZE, bitmap, 0, 0, nnodes);
++    
++    KMEM_FREE (bitmap, (BT_BITOUL(EP_MAX_NODES) * sizeof (bitmap_t)));
++    
++    strcat (page, "\n");
++
++    return (qsnet_proc_calc_metrics (page, start, off, count, eof, strlen(page)));
++}
++
++void
++cm_procfs_rail_init (CM_RAIL *cmRail)
++{
++    EP_RAIL *rail = cmRail->Rail;
++    struct proc_dir_entry *p;
++    int i;
++    
++    for (i = 0; i < sizeof (rail_info)/sizeof (rail_info[0]); i++)
++    {
++      if ((p = create_proc_entry (rail_info[i].name, 0, cmRail->Rail->ProcDir)) != NULL)
++      {
++          p->read_proc  = rail_info[i].read_func;
++          p->write_proc = rail_info[i].write_func;
++          p->data       = cmRail;
++          p->owner      = THIS_MODULE;
++      }
++    }
++
++    if ((rail->SvcIndicatorDir = proc_mkdir ("svc_indicators", cmRail->Rail->ProcDir)) != NULL)
++    {
++      for (i = 0; i < EP_SVC_NUM_INDICATORS; i++)
++      {
++          if ((p = create_proc_entry (svc_indicator_names[i], 0, rail->SvcIndicatorDir)) != NULL)
++          {
++              svc_indicator_data[i][rail->Number].svc_indicator = i;
++              svc_indicator_data[i][rail->Number].rail          = rail; 
++              
++              p->write_proc = NULL;
++              p->read_proc  = proc_read_svc_indicator_rail_bitmap;
++              p->data       = (void *)&svc_indicator_data[i][rail->Number];
++              p->owner      = THIS_MODULE;
++          }
++      }
++    }
++}
++
++void
++cm_procfs_rail_fini (CM_RAIL *cmRail)
++{
++    EP_RAIL *rail = cmRail->Rail;
++    int i;
++
++    if (rail->SvcIndicatorDir)
++    {
++      for (i = 0; i < EP_SVC_NUM_INDICATORS; i++)
++          remove_proc_entry (svc_indicator_names[i], rail->SvcIndicatorDir);
++
++      remove_proc_entry ("svc_indicators", cmRail->Rail->ProcDir);
++    }
++
++    for (i = 0; i < sizeof (rail_info)/sizeof (rail_info[0]); i++)
++      remove_proc_entry (rail_info[i].name, cmRail->Rail->ProcDir);
++}
++
++void
++cm_procfs_init (CM_SUBSYS *subsys)
++{
++    struct proc_dir_entry *p;
++    int i;
++
++    qsnet_proc_register_hex (ep_config_root, "machine_id",      &MachineId,      0);
++
++    if ((svc_indicators_root = proc_mkdir("svc_indicators", ep_procfs_root)) != NULL)
++    {
++      for (i = 0; i < EP_SVC_NUM_INDICATORS; i++)
++      {
++          if ((p = create_proc_entry (svc_indicator_names[i], 0, svc_indicators_root)) != NULL)
++          {
++              p->write_proc = NULL;
++              p->read_proc  = proc_read_svc_indicator_bitmap;
++              p->data       = (void *)(long) i;
++              p->owner      = THIS_MODULE;
++          }
++      }
++      
++    }
++}
++
++void
++cm_procfs_fini (CM_SUBSYS *subsys)
++{
++    int i;
++
++    if (svc_indicators_root)
++    {
++      for (i = 0; i < EP_SVC_NUM_INDICATORS; i++)
++          remove_proc_entry (svc_indicator_names[i], svc_indicators_root);
++      
++      remove_proc_entry ("svc_indicators",   ep_procfs_root);
++    }
++
++    remove_proc_entry ("machine_id",      ep_config_root);
++}
+Index: linux-2.6.5/drivers/net/qsnet/ep/commands_elan4.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/commands_elan4.c     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/commands_elan4.c  2005-05-11 12:10:12.481926040 -0400
+@@ -0,0 +1,173 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: commands_elan4.c,v 1.2 2003/10/23 15:07:53 david Exp $ $Name: QSNETMODULES-4-31_20050321 $"
++/*      $Source: /cvs/master/quadrics/epmod/commands_elan4.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++
++#include "kcomm_vp.h"
++#include "kcomm_elan4.h"
++#include "debug.h"
++
++#include <elan4/trtype.h>
++
++static __inline__ void
++elan4_command_write (ELAN4_CQ *cq, E4_uint64 val, unsigned off)
++{
++    writeq (val, cq->cq_mapping + offsetof (E4_CommandPort, Command[off]));
++}
++
++void
++elan4_nop_cmd (ELAN4_CQ *cq, E4_uint64 tag)
++{
++    elan4_command_write (cq, tag | NOP_CMD, 0);
++}
++
++void
++elan4_write_dword_cmd (ELAN4_CQ *cq, E4_Addr addr, E4_uint64 data)
++{
++    elan4_command_write (cq, addr | WRITE_DWORD_CMD, 0);
++    elan4_command_write (cq, data, 1);
++}
++
++void
++elan4_add_dword_cmd (ELAN4_CQ *cq, E4_Addr addr, E4_uint64 data)
++{
++    elan4_command_write (cq, addr | ADD_DWORD_CMD, 0);
++    elan4_command_write (cq, data,                 1);
++}
++
++void
++elan4_copy64_cmd (ELAN4_CQ *cq, E4_Addr from, E4_Addr to, E4_uint32 datatype)
++{
++    elan4_command_write (cq, from | (datatype << COPY64_DATA_TYPE_SHIFT) | COPY64_CMD, 0);
++    elan4_command_write (cq, to   | (datatype << COPY64_DATA_TYPE_SHIFT),              1);
++}
++
++void
++elan4_interrupt_cmd (ELAN4_CQ *cq, E4_uint64 cookie)
++{
++    elan4_command_write (cq, (cookie << E4_MAIN_INT_SHIFT) | INTERRUPT_CMD, 0);
++}
++
++
++void 
++elan4_run_thread_cmd (ELAN4_CQ *cq, E4_ThreadRegs *regs)
++{
++    elan4_command_write (cq, regs->Registers[0] | RUN_THREAD_CMD, 0);
++    elan4_command_write (cq, regs->Registers[1],                  1);
++    elan4_command_write (cq, regs->Registers[2],                  2);
++    elan4_command_write (cq, regs->Registers[3],                  3);
++    elan4_command_write (cq, regs->Registers[4],                  4);
++    elan4_command_write (cq, regs->Registers[5],                  5);
++    elan4_command_write (cq, regs->Registers[6],                  6);
++}
++
++void
++elan4_run_dma_cmd (ELAN4_CQ *cq, E4_DMA *dma)
++{
++    E4_uint64 *dmaptr = (E4_uint64 *) dma;
++
++    elan4_command_write (cq, dmaptr[0] | RUN_DMA_CMD, 0);
++    elan4_command_write (cq, dmaptr[1],               1);
++    elan4_command_write (cq, dmaptr[2],               2);
++    elan4_command_write (cq, dmaptr[3],               3);
++    elan4_command_write (cq, dmaptr[4],               4);
++    elan4_command_write (cq, dmaptr[5],               5);
++    elan4_command_write (cq, dmaptr[6],               6);
++}
++
++void
++elan4_set_event_cmd (ELAN4_CQ *cq, E4_Addr event)
++{
++    elan4_command_write (cq, event | SET_EVENT_CMD, 0);
++}
++
++void
++elan4_set_eventn_cmd (ELAN4_CQ *cq, E4_Addr event, E4_uint32 count)
++{
++    elan4_command_write (cq, SET_EVENTN_CMD,0);
++    elan4_command_write (cq, event | count, 1);
++}
++    
++void
++elan4_wait_event_cmd (ELAN4_CQ *cq, E4_Addr event, E4_uint64 candt, E4_uint64 param0, E4_uint64 param1)
++{
++    elan4_command_write (cq, event | WAIT_EVENT_CMD, 0);
++    elan4_command_write (cq, candt,                  1);
++    elan4_command_write (cq, param0,                 2);
++    elan4_command_write (cq, param1,                 3);
++}
++
++void
++elan4_open_packet (ELAN4_CQ *cq, E4_uint64 command)
++{
++    elan4_command_write (cq, command | OPEN_STEN_PKT_CMD, 0);
++}
++
++void
++elan4_guard (ELAN4_CQ *cq, E4_uint64 command)
++{
++    elan4_command_write (cq, command | GUARD_CMD, 0);
++}
++
++void
++elan4_sendtrans0 (ELAN4_CQ *cq, E4_uint16 trtype, E4_uint64 addr)
++{
++    elan4_command_write (cq, (trtype << 16) | SEND_TRANS_CMD, 0);
++    elan4_command_write (cq, addr,                              1);
++}
++
++void
++elan4_sendtrans1 (ELAN4_CQ *cq, E4_uint16 trtype, E4_uint64 addr, E4_uint64 p0)
++{
++    elan4_command_write (cq, (trtype << 16) | SEND_TRANS_CMD, 0);
++    elan4_command_write (cq, addr,                              1);
++    elan4_command_write (cq, p0,                                2);
++}
++
++void
++elan4_sendtrans2 (ELAN4_CQ *cq, E4_uint16 trtype, E4_uint64 addr, E4_uint64 p0, E4_uint64 p1)
++{
++    elan4_command_write (cq, (trtype << 16) | SEND_TRANS_CMD, 0);
++    elan4_command_write (cq, addr,                              1);
++    elan4_command_write (cq, p0,                                2);
++    elan4_command_write (cq, p1,                                3);
++}
++
++void
++elan4_sendtransn (ELAN4_CQ *cq, E4_uint16 trtype, E4_uint64 addr, ...)
++{
++    E4_uint32    ndword = ((trtype & TR_SIZE_MASK) >> TR_SIZE_SHIFT);
++    va_list      ap;
++    register int i;
++
++    elan4_command_write (cq, (trtype << 16) | SEND_TRANS_CMD, 0);
++    elan4_command_write (cq, addr,                              1);
++    
++    va_start (ap, addr);
++    for (i = 2; i < ndword+2; i++) 
++      elan4_command_write (cq, va_arg (ap, E4_uint64), i);
++    va_end (ap);
++}
++
++void
++elan4_sendtransp (ELAN4_CQ *cq, E4_uint16 trtype, E4_uint64 addr, E4_uint64 *ptr)
++{
++    E4_uint32    ndword = ((trtype &TR_SIZE_MASK) >> TR_SIZE_SHIFT);
++    register int i;
++
++    elan4_command_write (cq, (trtype << 16) | SEND_TRANS_CMD, 0);
++    elan4_command_write (cq, addr,                            1);
++    for (i = 2; i < ndword+2; i++)
++      elan4_command_write (cq, *ptr++, i);
++}
++
+Index: linux-2.6.5/drivers/net/qsnet/ep/conf_linux.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/conf_linux.c 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/conf_linux.c      2005-05-11 12:10:12.481926040 -0400
+@@ -0,0 +1,309 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: conf_linux.c,v 1.37.2.3 2005/01/18 14:47:35 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/conf_linux.c,v $ */
++
++#include <qsnet/kernel.h>
++#include <qsnet/autoconf.h>
++
++#include <elan/kcomm.h>
++#include <elan/epsvc.h>
++#include <elan/epcomms.h>
++
++#include "cm.h"
++
++#include "conf_linux.h"
++
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/reboot.h>
++#include <linux/notifier.h>
++
++/* Module parameters */
++unsigned int epdebug       = 0;
++unsigned int epdebug_console = 0;
++unsigned int epdebug_cmlevel = 0;
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++unsigned int epdebug_check_sum = 0;
++#endif
++int        disabled        = 0;
++int          sdram_assert    = 0;
++int          assfail_mode    = 0;
++int        txd_stabilise   = 7;
++int        portals_envelopes = 0;
++
++/* External module parameters */
++extern int    MaxSwitchLevels;
++extern int      RejoinCheck;
++extern int      RejoinPanic;
++extern int      PositionCheck;
++extern int      MachineId;
++
++/* Module globals */
++EP_SYS          epsys;
++
++#ifdef MODULE
++MODULE_AUTHOR("Quadrics Ltd");
++MODULE_DESCRIPTION("Elan Kernel Comms");
++
++MODULE_LICENSE("GPL");
++
++MODULE_PARM(epdebug,         "i");
++MODULE_PARM(epdebug_console, "i");
++MODULE_PARM(epdebug_cmlevel, "i");
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++MODULE_PARM(epdebug_check_sum, "i");
++#endif
++MODULE_PARM(disabled,        "i");
++
++MODULE_PARM(MachineId,       "i");
++MODULE_PARM(RejoinPanic,     "i");
++MODULE_PARM(RejoinCheck,     "i");
++MODULE_PARM(PositionCheck,   "i");
++MODULE_PARM(MaxSwitchLevels, "i");
++
++MODULE_PARM(sdram_assert,    "i");
++MODULE_PARM(assfail_mode,    "i");
++MODULE_PARM(txd_stabilise,   "i");
++MODULE_PARM(portals_envelopes,"i");
++
++/* epcomms.c large message service functions */
++EXPORT_SYMBOL(ep_alloc_xmtr);
++EXPORT_SYMBOL(ep_free_xmtr);
++EXPORT_SYMBOL(ep_transmit_message);
++EXPORT_SYMBOL(ep_multicast_message);
++EXPORT_SYMBOL(ep_transmit_rpc);
++
++EXPORT_SYMBOL(ep_alloc_rcvr);
++EXPORT_SYMBOL(ep_free_rcvr);
++EXPORT_SYMBOL(ep_queue_receive);
++EXPORT_SYMBOL(ep_requeue_receive);
++EXPORT_SYMBOL(ep_rpc_put);
++EXPORT_SYMBOL(ep_rpc_get);
++EXPORT_SYMBOL(ep_complete_rpc);
++EXPORT_SYMBOL(ep_complete_receive);
++
++EXPORT_SYMBOL(ep_poll_transmits);
++EXPORT_SYMBOL(ep_enable_txcallbacks);
++EXPORT_SYMBOL(ep_disable_txcallbacks);
++
++/* epcomms.c functions for accessing fields of rxds/txds */
++EXPORT_SYMBOL(ep_rxd_arg);
++EXPORT_SYMBOL(ep_rxd_len);
++EXPORT_SYMBOL(ep_rxd_isrpc);
++EXPORT_SYMBOL(ep_rxd_envelope);
++EXPORT_SYMBOL(ep_rxd_payload);
++EXPORT_SYMBOL(ep_rxd_node);
++EXPORT_SYMBOL(ep_rxd_status);
++EXPORT_SYMBOL(ep_rxd_statusblk);
++EXPORT_SYMBOL(ep_txd_node);
++EXPORT_SYMBOL(ep_txd_statusblk);
++
++/* kmap.c, nmh.c - handling mapping of pages into network memory */
++EXPORT_SYMBOL(ep_dvma_reserve);
++EXPORT_SYMBOL(ep_dvma_release);
++EXPORT_SYMBOL(ep_dvma_load);
++EXPORT_SYMBOL(ep_dvma_unload);
++EXPORT_SYMBOL(ep_nmd_subset);
++EXPORT_SYMBOL(ep_nmd_merge);
++
++EXPORT_SYMBOL(ep_system);
++
++/* kcomm.c */
++EXPORT_SYMBOL(ep_nodeid);
++EXPORT_SYMBOL(ep_numnodes);
++EXPORT_SYMBOL(ep_waitfor_nodeid);
++
++/* railhints.c */
++EXPORT_SYMBOL(ep_pickRail);
++EXPORT_SYMBOL(ep_xmtr_bcastrail);
++EXPORT_SYMBOL(ep_xmtr_prefrail);
++EXPORT_SYMBOL(ep_xmtr_availrails);
++EXPORT_SYMBOL(ep_xmtr_noderails);
++EXPORT_SYMBOL(ep_rcvr_prefrail);
++EXPORT_SYMBOL(ep_rcvr_availrails);
++EXPORT_SYMBOL(ep_rxd_railmask);
++
++EXPORT_SYMBOL(ep_svc_indicator_bitmap);
++EXPORT_SYMBOL(ep_svc_indicator_is_set);
++EXPORT_SYMBOL(ep_svc_indicator_clear);
++EXPORT_SYMBOL(ep_svc_indicator_set);
++
++/* cm.c */
++EXPORT_SYMBOL(cm_svc_indicator_clear);
++EXPORT_SYMBOL(cm_svc_indicator_set);
++EXPORT_SYMBOL(cm_svc_indicator_is_set);
++EXPORT_SYMBOL(cm_svc_indicator_bitmap);
++
++#endif
++
++EP_SYS *
++ep_system()
++{
++    return (&epsys);
++}
++
++void
++ep_mod_inc_usecount()
++{
++    MOD_INC_USE_COUNT;
++} 
++
++void
++ep_mod_dec_usecount()
++{
++    MOD_DEC_USE_COUNT;
++}
++
++#if defined(CONFIG_DUMP) || defined(CONFIG_DUMP_MODULE)
++
++#include <linux/dump.h>
++
++static int
++ep_dump_event (struct notifier_block *self, unsigned long event, void *buffer)
++{
++    if (event == DUMP_BEGIN)
++      ep_shutdown (&epsys);
++
++    return (NOTIFY_DONE);
++}
++static struct notifier_block ep_dump_notifier = 
++{
++    notifier_call:    ep_dump_event,
++    priority:         0,
++};
++
++#endif
++
++static int
++ep_reboot_event (struct notifier_block *self, unsigned long event, void *buffer)
++{
++    if ((event == SYS_RESTART || event == SYS_HALT || event == SYS_POWER_OFF))
++      ep_shutdown (&epsys);
++
++    return (NOTIFY_DONE);
++}
++
++static struct notifier_block ep_reboot_notifier = 
++{
++    notifier_call:    ep_reboot_event,
++    priority:         0,
++};
++
++static int
++ep_panic_event (struct notifier_block *self, unsigned long event, void *buffer)
++{
++    ep_shutdown (&epsys);
++
++    return (NOTIFY_DONE);
++}
++
++static struct notifier_block ep_panic_notifier = 
++{
++    notifier_call:    ep_panic_event,
++    priority:         0,
++};
++
++/*
++ * Module configuration. 
++ */
++#ifdef MODULE
++static int __init ep_init(void)
++#else
++__initfunc(int ep_init(void))
++#endif
++{
++    register int rmask = 0;
++
++    ep_procfs_init ();
++
++    ep_sys_init (&epsys);
++
++#if defined(CONFIG_ELAN4) || defined(CONFIG_ELAN4_MODULE)
++    rmask = ep4_create_rails (&epsys, disabled);
++#endif
++    
++    /* If we've brought up an elan4 rail, then disable all elan3 rails. */
++    if ((rmask & ~disabled) != 0)
++      disabled = ~rmask;
++
++#if defined(CONFIG_ELAN3) || defined(CONFIG_ELAN3_MODULE)
++    rmask = ep3_create_rails (&epsys, disabled);
++#endif
++
++#if defined(CONFIG_DUMP) || defined(CONFIG_DUMP_MODULE)
++    register_dump_notifier (&ep_dump_notifier);
++#endif
++    register_reboot_notifier (&ep_reboot_notifier);
++
++#if !defined(NO_PANIC_NOTIFIER)
++    notifier_chain_register (&panic_notifier_list, &ep_panic_notifier);
++#endif
++
++    return (0);
++}
++
++/*
++ * Module removal.
++ */
++#ifdef MODULE
++static void
++__exit ep_exit(void)
++{
++    register int i;
++
++#if defined(CONFIG_DUMP) || defined(CONFIG_DUMP_MODULE)
++    unregister_dump_notifier (&ep_dump_notifier);
++#endif
++    unregister_reboot_notifier (&ep_reboot_notifier);
++
++#if !defined(NO_PANIC_NOTIFIER)
++    notifier_chain_unregister (&panic_notifier_list, &ep_panic_notifier);
++#endif
++
++    for (i = 0; i < EP_MAX_RAILS; i++)
++    {
++      if (epsys.Rails[i])
++      {
++          switch (epsys.Rails[i]->State)
++          {
++          case EP_RAIL_STATE_UNINITIALISED:
++              break;
++
++          case EP_RAIL_STATE_STARTED:
++          case EP_RAIL_STATE_RUNNING:
++          case EP_RAIL_STATE_INCOMPATIBLE:
++              /* remove per-rail CM proc entries */
++              ep_stop_rail (epsys.Rails[i]);
++              break;
++          }
++
++          /* remove EP proc rail entries after per-rail CM entries */
++          ep_procfs_rail_fini (epsys.Rails[i]);
++          ep_destroy_rail (epsys.Rails[i]);
++      }
++    }
++
++    ep_sys_fini (&epsys);
++
++    ep_procfs_fini ();
++}
++
++/* Declare the module init and exit functions */
++module_init(ep_init);
++module_exit(ep_exit);
++
++#endif
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/ep/conf_linux.h
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/conf_linux.h 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/conf_linux.h      2005-05-11 12:10:12.482925888 -0400
+@@ -0,0 +1,29 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: conf_linux.h,v 1.6 2003/10/02 14:16:07 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/conf_linux.h,v $*/
++
++#ifndef __ELAN_CONF_LINUX_H
++#define __ELAN_CONF_LINUX_H
++
++extern void ep_procfs_init(void);
++extern void ep_procfs_fini(void);
++extern void ep_procfs_rail_init(EP_RAIL *rail);
++extern void ep_procfs_rail_fini(EP_RAIL *rail);
++
++extern void ep_procfs_svc_indicator_create(int svc_indicator, char *name);
++extern void ep_procfs_svc_indicator_remove(int svc_indicator, char *name);
++
++#endif /* __ELAN_CONF_LINUX_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/ep/debug.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/debug.c      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/debug.c   2005-05-11 12:10:12.482925888 -0400
+@@ -0,0 +1,145 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: debug.c,v 1.28.2.1 2004/11/12 10:54:50 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/debug.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++
++#include "debug.h"
++
++DisplayInfo di_ep_debug = {ep_debugf, DBG_DEBUG};
++
++/*
++ * Generate a partial bitmap string, for the bitmap from offset "off" for "count" bits,
++ * to allow for displaying of subsets, treat entry 0 of the bitmap as having value "base".
++ */
++int
++ep_sprintf_bitmap (char *str, unsigned nbytes, bitmap_t *bitmap, int base, int off, int nbits)
++{
++    char entry[12];                                           /* space for N-N */
++    register int i, j, len;
++    register int notstart = off;
++    register int notfirst = 0;
++    char *p = str;
++    
++    for (i = off; i < nbits; i++)
++    {
++      if (BT_TEST (bitmap, i))
++      {
++          for (j = i+1; j < nbits; j++)
++              if (! BT_TEST (bitmap, j))
++                  break;
++
++          if (j == (i+1))
++              len = (int)sprintf (entry, "%d", base + i);
++          else
++              len = (int)sprintf (entry, "%d-%d", base + i, base + j-1);
++          
++          /* NOTE the 2 is for: one for comma, one for (possible) closing bracket */
++          if ((p - str) <= (nbytes - (len+3)))
++              p += (int)sprintf (p, "%c%s", notfirst++ ? ',' : notstart ? ' ' : '[', entry);
++          else
++          {
++              /* no more space on this line, so move onto next */
++              sprintf (p, "%c", notfirst++ ? ',' : '[');
++
++              return (i);
++          }
++
++          i = j;
++      }
++    }
++    
++    if (!notfirst)
++      sprintf (str, "<empty>");
++    else
++      strcpy (p, "]");
++
++    return (-1);
++}
++
++void
++ep_display_bitmap (char *prefix, char *tag, bitmap_t *bitmap, unsigned base, unsigned nbits)
++{
++    /* Tru64 kernel printf() truncates lines at 128 bytes - the man pages for printf (9)
++     * do not mention this restriction, nor that it does not terminate the line with a
++     * carriage return, this  is pretty naff. 
++     * Linux has a similar limit though is much more generous at 1024 - and you can just 
++     * look at the code to see why this has been done.
++     *
++     * Our nodeset information could well be longer than 128 characters,  so we're going to 
++     * have to split it into a number of lines. */
++
++#define LINEBUF_SIZE          128
++    char *p, linebuf[LINEBUF_SIZE+1];                         /* +1 for null termination */
++    int i, noff, off = 0;
++
++    do {
++      if (off == 0)
++          p = linebuf + (int)sprintf (linebuf, "%s: %s ", prefix, tag);
++      else
++      {
++          p = linebuf + (int)sprintf (linebuf, "%s:  ", prefix);
++          for (i = 0; tag[i] != '\0'; i++)
++              *p++ = ' ';
++      }
++
++      noff = ep_sprintf_bitmap (p, &linebuf[LINEBUF_SIZE-1]-p, bitmap, base, off, nbits);
++
++      printk ("%s\n", linebuf);
++
++    } while ((off = noff) != -1);
++
++#undef LINEBUF_SIZE
++}
++
++void
++ep_debugf (long mode, char *fmt, ...)
++{
++   va_list ap;
++   char prefix[32];
++   
++   va_start (ap, fmt);
++#if defined(LINUX)
++   sprintf (prefix, "[%08d.%04d] ", (int) lbolt, current->pid);
++#else
++   sprintf (prefix, "[%08d.----] ", (int) lbolt);
++#endif
++   qsnet_vdebugf ((mode & epdebug_console ? QSNET_DEBUG_CONSOLE: 0) | QSNET_DEBUG_BUFFER, prefix, fmt, ap);
++   va_end (ap);
++}
++
++int
++ep_assfail (EP_RAIL *rail, const char *ex, const char *func, const char *file, const int line)
++{
++    qsnet_debugf (QSNET_DEBUG_BUFFER, "ep: assertion failure: %s, function: %s, file %s, line: %d\n", ex, func, file, line);
++    
++    printk (KERN_EMERG "ep: assertion failure: %s, function: %s, file %s, line: %d\n", ex, func, file, line);
++    
++    if (panicstr)
++      return (0);
++    
++    if (assfail_mode & 1)                             /* return to BUG() */
++      return 1;
++    
++    if (assfail_mode & 2)
++      panic ("ep: assertion failure: %s, function: %s, file %s, line: %d\n", ex, func, file, line);
++    if (assfail_mode & 4)
++      epdebug = 0;
++    
++    return 0;
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/ep/debug_elan4.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/debug_elan4.c        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/debug_elan4.c     2005-05-11 12:10:12.482925888 -0400
+@@ -0,0 +1,59 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: debug_elan4.c,v 1.1 2004/05/19 10:21:04 david Exp $ $Name: QSNETMODULES-4-31_20050321 $"
++/*      $Source: /cvs/master/quadrics/epmod/debug_elan4.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++
++#include "kcomm_vp.h"
++#include "kcomm_elan4.h"
++#include "conf_linux.h"
++#include "debug.h"
++
++static void
++ep4_display_ecqs (EP4_RAIL *rail)
++{
++    struct list_head *el;
++    unsigned long flags;
++    int i;
++
++    spin_lock_irqsave (&rail->r_ecq_lock, flags);
++    for (i = 0; i <EP4_NUM_ECQ; i++)
++    {
++      list_for_each (el, &rail->r_ecq_list[i]) {
++          EP4_ECQ *ecq = list_entry (el, EP4_ECQ, ecq_link);
++
++          ep_debugf (DBG_DEBUG, "ECQ: type %d: avail %d cqnum %d\n", i, ecq->ecq_avail, elan4_cq2num (ecq->ecq_cq));
++      }
++    }
++    spin_unlock_irqrestore (&rail->r_ecq_lock, flags);
++}
++
++void
++ep4_debug_rail (EP_RAIL *r)
++{
++    EP4_RAIL *rail = (EP4_RAIL *) r;
++    EP_SYS   *sys  = rail->r_generic.System;
++
++    ep_debugf (DBG_DEBUG, "ep%d: is elan4 %d rev %c\n", rail->r_generic.Number,
++             rail->r_generic.Devinfo.dev_instance, 'a' + rail->r_generic.Devinfo.dev_revision_id);
++
++    ep4_display_ecqs (rail);
++
++    ep_display_alloc (&sys->Allocator);
++    ep_display_rmap (sys->Allocator.ResourceMap);
++
++    ep_display_alloc (&rail->r_generic.ElanAllocator);
++    ep_display_alloc (&rail->r_generic.MainAllocator);
++
++    ep_display_rmap (rail->r_generic.ElanAllocator.ResourceMap);
++}
++
+Index: linux-2.6.5/drivers/net/qsnet/ep/debug.h
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/debug.h      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/debug.h   2005-05-11 12:10:12.483925736 -0400
+@@ -0,0 +1,109 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef _ELAN3_EPDEBUG_H
++#define _ELAN3_EPDEBUG_H
++
++#ident "$Id: debug.h,v 1.18.2.1 2004/11/12 10:54:50 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/debug.h,v $ */
++
++extern unsigned int epdebug;
++extern unsigned int epdebug_console;
++extern unsigned int epdebug_cmlevel;
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++extern unsigned int epdebug_check_sum;
++#endif
++#define DBG_CONFIG            0x00000001                      /* Module configuration */
++#define DBG_PROBE             0x00000002
++#define DBG_ROUTETABLE                0x00000004
++#define DBG_STATEMAP          0x00000008
++
++#define DBG_CM                        0x00000020
++#define DBG_XMTR              0x00000040
++#define DBG_RCVR              0x00000080
++#define DBG_FORWARD           0x00000100
++#define DBG_DISCON            0x00000200
++#define DBG_EPTRAP            0x00000400
++#define DBG_COMMAND           0x00000800
++#define DBG_RETRY             0x00001000
++#define DBG_DEBUG             0x00002000
++#define DBG_NETWORK_ERROR     0x00004000
++#define DBG_MSGSYS            0x00008000
++#define DBG_MANAGER           0x00010000
++#define DBG_KMAP              0x00020000
++#define DBG_FAILOVER          0x00040000
++#define DBG_MAPNMD            0x00080000
++#define DBG_KMSG              0x00100000
++#define DBG_SVC                 0x00200000
++#define DBG_STABILISE         0x00400000
++
++#if defined(DEBUG_PRINTF)
++
++#  define EPRINTF0(m,fmt)                     ((epdebug&(m)) ? ep_debugf(m,fmt)                     : (void)0)
++#  define EPRINTF1(m,fmt,a)                   ((epdebug&(m)) ? ep_debugf(m,fmt,a)                   : (void)0)
++#  define EPRINTF2(m,fmt,a,b)                 ((epdebug&(m)) ? ep_debugf(m,fmt,a,b)                 : (void)0)
++#  define EPRINTF3(m,fmt,a,b,c)                       ((epdebug&(m)) ? ep_debugf(m,fmt,a,b,c)               : (void)0)
++#  define EPRINTF4(m,fmt,a,b,c,d)             ((epdebug&(m)) ? ep_debugf(m,fmt,a,b,c,d)             : (void)0)
++#  define EPRINTF5(m,fmt,a,b,c,d,e)           ((epdebug&(m)) ? ep_debugf(m,fmt,a,b,c,d,e)           : (void)0)
++#  define EPRINTF6(m,fmt,a,b,c,d,e,f)         ((epdebug&(m)) ? ep_debugf(m,fmt,a,b,c,d,e,f)         : (void)0)
++#  define EPRINTF7(m,fmt,a,b,c,d,e,f,g)               ((epdebug&(m)) ? ep_debugf(m,fmt,a,b,c,d,e,f,g)       : (void)0)
++#  define EPRINTF8(m,fmt,a,b,c,d,e,f,g,h)     ((epdebug&(m)) ? ep_debugf(m,fmt,a,b,c,d,e,f,g,h)     : (void)0)
++#  define EPRINTF9(m,fmt,a,b,c,d,e,f,g,h,i)   ((epdebug&(m)) ? ep_debugf(m,fmt,a,b,c,d,e,f,g,h,i)   : (void)0)
++#  define EPRINTF10(m,fmt,a,b,c,d,e,f,g,h,i,j)        ((epdebug&(m)) ? ep_debugf(m,fmt,a,b,c,d,e,f,g,h,i,j) : (void)0)
++
++#  define CPRINTF0(lvl,fmt)                   (((lvl) <= epdebug_cmlevel) ? EPRINTF0(DBG_CM,fmt)                   : (void)0)
++#  define CPRINTF1(lvl,fmt,a)                 (((lvl) <= epdebug_cmlevel) ? EPRINTF1(DBG_CM,fmt,a)                 : (void)0)
++#  define CPRINTF2(lvl,fmt,a,b)                       (((lvl) <= epdebug_cmlevel) ? EPRINTF2(DBG_CM,fmt,a,b)               : (void)0)
++#  define CPRINTF3(lvl,fmt,a,b,c)             (((lvl) <= epdebug_cmlevel) ? EPRINTF3(DBG_CM,fmt,a,b,c)             : (void)0)
++#  define CPRINTF4(lvl,fmt,a,b,c,d)           (((lvl) <= epdebug_cmlevel) ? EPRINTF4(DBG_CM,fmt,a,b,c,d)           : (void)0)
++#  define CPRINTF5(lvl,fmt,a,b,c,d,e)         (((lvl) <= epdebug_cmlevel) ? EPRINTF5(DBG_CM,fmt,a,b,c,d,e)         : (void)0)
++#  define CPRINTF6(lvl,fmt,a,b,c,d,e,f)               (((lvl) <= epdebug_cmlevel) ? EPRINTF6(DBG_CM,fmt,a,b,c,d,e,f)       : (void)0)
++#  define CPRINTF7(lvl,fmt,a,b,c,d,e,f,g)     (((lvl) <= epdebug_cmlevel) ? EPRINTF7(DBG_CM,fmt,a,b,c,d,e,f,g)     : (void)0)
++#  define CPRINTF8(lvl,fmt,a,b,c,d,e,f,g,h)   (((lvl) <= epdebug_cmlevel) ? EPRINTF8(DBG_CM,fmt,a,b,c,d,e,f,g,h)   : (void)0)
++#  define CPRINTF9(lvl,fmt,a,b,c,d,e,f,g,h,i) (((lvl) <= epdebug_cmlevel) ? EPRINTF9(DBG_CM,fmt,a,b,c,d,e,f,g,h,i) : (void)0)
++
++#if defined __GNUC__
++extern void ep_debugf (long mode, char *fmt, ...) __attribute__ ((format (printf,2,3)));
++#else
++extern void ep_debugf (long mode, char *fmt, ...);
++#endif
++
++#else
++
++#  define EPRINTF0(m,fmt)                     (0)
++#  define EPRINTF1(m,fmt,a)                   (0)
++#  define EPRINTF2(m,fmt,a,b)                 (0)
++#  define EPRINTF3(m,fmt,a,b,c)                       (0)
++#  define EPRINTF4(m,fmt,a,b,c,d)             (0)
++#  define EPRINTF5(m,fmt,a,b,c,d,e)           (0)
++#  define EPRINTF6(m,fmt,a,b,c,d,e,f)         (0)
++#  define EPRINTF7(m,fmt,a,b,c,d,e,f,g)               (0)
++#  define EPRINTF8(m,fmt,a,b,c,d,e,f,g,h)     (0)
++#  define EPRINTF9(m,fmt,a,b,c,d,e,f,g,h,i)   (0)
++#  define EPRINTF9(m,fmt,a,b,c,d,e,f,g,h,i,j) (0)
++
++#  define CPRINTF0(lvl,fmt)                   (0)
++#  define CPRINTF1(lvl,fmt,a)                 (0)
++#  define CPRINTF2(lvl,fmt,a,b)                       (0)
++#  define CPRINTF3(lvl,fmt,a,b,c)             (0)
++#  define CPRINTF4(lvl,fmt,a,b,c,d)           (0)
++#  define CPRINTF5(lvl,fmt,a,b,c,d,e)         (0)
++#  define CPRINTF6(lvl,fmt,a,b,c,d,e,f)               (0)
++#  define CPRINTF7(lvl,fmt,a,b,c,d,e,f,g)     (0)
++#  define CPRINTF8(lvl,fmt,a,b,c,d,e,f,g,h)   (0)
++#  define CPRINTF9(lvl,fmt,a,b,c,d,e,f,g,h,i) (0)
++
++#endif /* DEBUG */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++#endif /* _ELAN3_EPDEBUG_H */
++
+Index: linux-2.6.5/drivers/net/qsnet/ep/epcomms_asm_elan4_thread.S
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/epcomms_asm_elan4_thread.S   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/epcomms_asm_elan4_thread.S        2005-05-11 12:10:12.483925736 -0400
+@@ -0,0 +1,133 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: epcomms_asm_elan4_thread.S,v 1.5 2004/04/25 11:25:43 david Exp $ $Name: QSNETMODULES-4-31_20050321 $"
++/*      $Source: /cvs/master/quadrics/epmod/epcomms_asm_elan4_thread.S,v $*/
++
++#include <elan4/events.h>
++#include <elan4/commands.h>
++
++#include "assym_elan4.h"
++
++/* XXXXX - registers.h */
++#define E4_MAIN_INT_SHIFT             14
++
++/*
++ * c_waitevent_interrupt (E4_uint64 *commandport, E4_Event *event, E4_uint64 count, E4_uint64 intcookie)
++ */
++      .global c_waitevent_interrupt
++c_waitevent_interrupt:
++      add             %sp, -192, %sp
++      st64            %r16, [%sp + 64]                // preserve call preserved registers
++      st64            %r24, [%sp + 128]               // - see CALL_USED_REGISTERS.
++      mov             %r16,%r16                       // BUG FIX: E4 RevA
++      mov             %r24,%r24                       // BUG FIX: E4 RevA
++      nop                                             // BUG FIX: E4 RevA
++      nop                                             // BUG FIX: E4 RevA
++
++      mov             %r7, %r18                       // (%r2) return pc
++1:    call            2f
++       mov            %sp, %r17                       // (%r1) SP
++2:    add             %r7, (3f-1b), %r16              // (%r0) PC
++      st32            %r16, [%sp]                     // event source block
++      mov             MAKE_EXT_CLEAN_CMD, %r23
++      st8             %r23, [%sp+56]                  // event source block
++      mov             %r16,%r16                       // BUG FIX: E4 RevA
++      mov             %r23,%r23                       // BUG FIX: E4 RevA
++      nop                                             // BUG FIX: E4 RevA
++      nop                                             // BUG FIX: E4 RevA
++      
++      or              %r9, WAIT_EVENT_CMD, %r16                                               ! WAIT_EVENT_CMD | event
++      sll8            %r10, 32, %r17
++      or              %r17, E4_EVENT_TYPE_VALUE(E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, 8), %r17  !   ev_CountAndType
++      mov             %sp, %r18                                                               !   ev_Source
++      mov             %r8, %r19                                                               !   ev_Dest
++      sll8            %r11, E4_MAIN_INT_SHIFT, %r20
++      or              %r20, INTERRUPT_CMD, %r20                                               ! INTERRUPT_CMD | (cookie << E4_MAIN_INT_SHIFT)
++      mov             NOP_CMD, %r21
++      mov             NOP_CMD, %r22
++      mov             NOP_CMD, %r23
++
++      st64suspend     %r16, [%r8]
++      
++3:    ld64            [%sp + 64], %r16                // restore call preserved register
++      ld64            [%sp + 128], %r24
++      jmpl            %r2+8, %r0                      // and return
++       add            %sp, 192, %sp
++
++
++#define EP4_RCVR_PENDING_STALLED              1               /* indicates thread has stalled for no descriptor (rcvr_pending_head) */
++
++#define RXD_DEBUG(VAL,RXD,TMP) \
++      mov     VAL, TMP; \
++      st8     TMP, [RXD + EP4_RXD_DEBUG]
++
++      
++      /*
++       * %r2  - rcvr elan
++       * %r3  - rxd elan
++       */
++      .global c_queue_rxd
++c_queue_rxd:
++      RXD_DEBUG(1, %r3, %r23)
++      
++      ld16    [%r2 + EP4_RCVR_PENDING_TAILP], %r18    /* r18 == tailp, r19 = head */
++      add     %r3, EP4_RXD_NEXT, %r4
++      
++      st8     %r0, [%r3 + EP4_RXD_NEXT]               /* rxd->rxd_next = NULL */
++      st8     %r4, [%r2 + EP4_RCVR_PENDING_TAILP]     /* tailp = &rxd->rxd_next */
++      st8     %r3, [%r18]                             /* *tailp = rxd */
++
++      cmp     %r19, EP4_RCVR_PENDING_STALLED          /* thread stalled ? */
++      beq     1f
++       mov    %r18, %r16                              /* must have used %r16, %r19, %r23 */
++      mov     %r3, %r23
++
++      RXD_DEBUG(2, %r3, %r23)
++      
++      st8suspend %r16, [%r3 + EP4_RXD_QUEUED]         /* no - mark as queued - all done */
++
++1:    st8     %r16, [%r3 + EP4_RXD_QUEUED]            /* mark as queued */
++
++      RXD_DEBUG(3, %r3, %r23)
++
++      mov     %r3, %r8                                /* return rxd from c_stall_thread */
++      ba      .epcomms_resume_thread                  /* resume the thread */
++       ld64   [%r2 + EP4_RCVR_THREAD_STALL], %r0
++
++      /*
++       *  c_stall_thread (EP4_RCVR_ELAN *rcvrElan)
++       */
++      .global c_stall_thread
++c_stall_thread:
++      add             %sp, -192, %sp
++      st64            %r16, [%sp + 64]                // preserve call preserved registers
++      st64            %r24, [%sp + 128]               // - see CALL_USED_REGISTERS.
++      mov             %r16,%r16                       // BUG FIX: E4 RevA
++      mov             %r24,%r24                       // BUG FIX: E4 RevA
++      nop                                             // BUG FIX: E4 RevA
++      nop                                             // BUG FIX: E4 RevA
++
++      mov             EP4_RCVR_PENDING_STALLED, %r9   // Mark rcvr as stalled
++      st8             %r9, [%r8 + EP4_RCVR_PENDING_HEAD]
++
++      // XXXX _ TBD should generate interrupt
++
++      mov             %r1, %r17                       // SP 
++      mov             %r7, %r23                       // return pc
++
++      st64suspend     %r16, [%r8 + EP4_RCVR_THREAD_STALL]
++      
++.epcomms_resume_thread:
++      /* %r8 == rxdElan */
++      
++      ld64            [%sp + 64], %r16                // restore call preserved register
++      ld64            [%sp + 128], %r24
++      jmpl            %r7+8, %r0                      // and return
++       add            %sp, 192, %sp
++
+Index: linux-2.6.5/drivers/net/qsnet/ep/epcomms.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/epcomms.c    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/epcomms.c 2005-05-11 12:10:12.484925584 -0400
+@@ -0,0 +1,484 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: epcomms.c,v 1.71.2.6 2004/11/30 12:02:16 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/epcomms.c,v $ */
++
++#include <qsnet/kernel.h>
++#include <qsnet/kthread.h>
++#include <qsnet/autoconf.h>
++
++#include <elan/kcomm.h>
++#include <elan/epsvc.h>
++#include <elan/epcomms.h>
++#include "cm.h"
++#include "debug.h"
++
++static void
++ep_comms_thread (void *arg)
++{
++    EP_COMMS_SUBSYS  *subsys = (EP_COMMS_SUBSYS *) arg;
++    struct list_head *el;
++
++    kernel_thread_init ("ep_comms");
++
++    /* since ep_alloc_xmtr() has incremented the module use count,
++     * we would be preventing the module from being unloaded, so
++     * we decrement the use count since this thread must terminate
++     * during unload of the module.
++     */
++    ep_mod_dec_usecount();
++
++    for (;;)
++    {
++      long nextRunTime = 0;
++
++      /* NOTE - subsys->Lock serializes us against flush/relocations
++       *        caused by rail nodeset transitions.
++       */
++      kmutex_lock (&subsys->Lock);
++      list_for_each (el, &subsys->Transmitters) {
++          nextRunTime = ep_check_xmtr (list_entry (el, EP_XMTR, Link), nextRunTime);
++      }
++
++      list_for_each (el, &subsys->Receivers) {
++          nextRunTime = ep_check_rcvr (list_entry (el, EP_RCVR, Link), nextRunTime);
++      }
++      kmutex_unlock (&subsys->Lock);
++
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++      ep_csum_rxds (subsys);  
++#endif
++      nextRunTime = ep_forward_rxds (subsys, nextRunTime);
++
++      if (ep_kthread_sleep (&subsys->Thread, nextRunTime) < 0)
++          break;
++    }
++
++    ep_mod_inc_usecount();
++
++    ep_kthread_stopped (&subsys->Thread);
++    kernel_thread_exit();
++}
++
++int
++ep_comms_add_rail (EP_SUBSYS *s, EP_SYS *sys, EP_RAIL *rail)
++{
++    EP_COMMS_SUBSYS  *subsys = (EP_COMMS_SUBSYS *) s;
++    EP_COMMS_RAIL    *commsRail;
++    struct list_head *el;
++
++    printk ("%s: vendorid=%x deviceid=%x\n", rail->Name, rail->Devinfo.dev_vendor_id, rail->Devinfo.dev_device_id);
++
++    switch (rail->Devinfo.dev_device_id)
++    {
++#if defined(CONFIG_ELAN3) || defined(CONFIG_ELAN3_MODULE)
++    case PCI_DEVICE_ID_ELAN3:
++      commsRail = ep3comms_add_rail (s, sys, rail);
++      break;
++#endif
++#if defined(CONFIG_ELAN4) || defined(CONFIG_ELAN4_MODULE)
++    case PCI_DEVICE_ID_ELAN4:
++      commsRail = ep4comms_add_rail (s, sys, rail);
++      break;
++#endif
++    default:
++      return 0;
++    }
++
++    if (commsRail == NULL)
++      return 1;
++
++    commsRail->Rail   = rail;
++    commsRail->Subsys = subsys;
++
++    kmutex_lock (&subsys->Lock);
++    list_add_tail (&commsRail->Link, &subsys->Rails);
++    
++    list_for_each (el, &subsys->Receivers) {
++      EP_RCVR *rcvr = list_entry (el, EP_RCVR, Link);
++
++      EP_RAIL_OP (commsRail, Rcvr.AddRail) (rcvr, commsRail);
++    }
++      
++    list_for_each (el, &subsys->Transmitters) {
++      EP_XMTR *xmtr = list_entry (el, EP_XMTR, Link);
++
++      EP_RAIL_OP (commsRail, Xmtr.AddRail) (xmtr, commsRail);
++    }
++
++    kmutex_unlock (&subsys->Lock);
++
++    return 0;
++}
++
++void
++ep_comms_del_rail (EP_SUBSYS *s, EP_SYS *sys, EP_RAIL *rail)
++{
++    EP_COMMS_SUBSYS  *subsys    = (EP_COMMS_SUBSYS *) s;
++    EP_COMMS_RAIL    *commsRail = NULL;
++    struct list_head *el;
++
++    kmutex_lock (&subsys->Lock);
++    /* find out rail entry and remove from system list */
++    list_for_each (el, &subsys->Rails) {
++      if ((commsRail = list_entry (el, EP_COMMS_RAIL, Link))->Rail == rail)
++          break;
++    }
++
++    list_del (&commsRail->Link);
++    
++    list_for_each (el, &subsys->Receivers) {
++      EP_RCVR *rcvr = list_entry (el, EP_RCVR, Link);
++
++      EP_RAIL_OP(commsRail, Rcvr.DelRail) (rcvr, commsRail);
++    }
++      
++    list_for_each (el, &subsys->Transmitters) {
++      EP_XMTR *xmtr = list_entry (el, EP_XMTR, Link);
++
++      EP_RAIL_OP(commsRail,Xmtr.DelRail) (xmtr, commsRail);
++    }
++
++    kmutex_unlock (&subsys->Lock);
++
++    EP_RAIL_OP (commsRail, DelRail) (commsRail);
++}
++
++void
++ep_comms_fini (EP_SUBSYS *s, EP_SYS *sys)
++{
++    EP_COMMS_SUBSYS *subsys = (EP_COMMS_SUBSYS *) s;
++
++    ep_kthread_stop (&subsys->Thread);
++    ep_kthread_destroy (&subsys->Thread);
++
++    if (subsys->ForwardXmtr)
++      ep_free_xmtr (subsys->ForwardXmtr);
++
++    spin_lock_destroy (&subsys->ForwardDescLock);
++
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++    spin_lock_destroy (&subsys->CheckSumDescLock);
++#endif
++
++    kmutex_destroy (&subsys->Lock);
++
++    KMEM_FREE (subsys, sizeof (EP_COMMS_SUBSYS));
++}
++
++int
++ep_comms_init (EP_SYS *sys)
++{
++    EP_COMMS_SUBSYS *subsys;
++
++    KMEM_ZALLOC (subsys, EP_COMMS_SUBSYS *, sizeof (EP_COMMS_SUBSYS), 1);
++
++    if (subsys == NULL)
++      return (ENOMEM);
++
++    INIT_LIST_HEAD (&subsys->Rails);
++    INIT_LIST_HEAD (&subsys->Receivers);
++    INIT_LIST_HEAD (&subsys->Transmitters);
++    INIT_LIST_HEAD (&subsys->ForwardDescList);
++
++    kmutex_init (&subsys->Lock);
++    spin_lock_init (&subsys->ForwardDescLock);
++
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++    INIT_LIST_HEAD (&subsys->CheckSumDescList);
++    spin_lock_init (&subsys->CheckSumDescLock);
++#endif
++
++    subsys->Subsys.Sys        = sys;
++    subsys->Subsys.Name             = "epcomms";
++    subsys->Subsys.Destroy    = ep_comms_fini;
++    subsys->Subsys.AddRail    = ep_comms_add_rail;
++    subsys->Subsys.RemoveRail = ep_comms_del_rail;
++
++    ep_subsys_add (sys, &subsys->Subsys);
++    ep_kthread_init (&subsys->Thread);
++
++    if ((subsys->ForwardXmtr = ep_alloc_xmtr (subsys->Subsys.Sys)) == NULL)
++      goto failed;
++
++    if (kernel_thread_create (ep_comms_thread, subsys) == NULL)
++      goto failed;
++    ep_kthread_started (&subsys->Thread);
++
++    return (0);
++
++ failed:
++    ep_subsys_del (sys, &subsys->Subsys);
++    ep_comms_fini (&subsys->Subsys, sys);
++
++    return (ENOMEM);
++}
++
++void
++ep_comms_display (EP_SYS *sys, char *how)
++{
++    EP_COMMS_SUBSYS  *subsys = (EP_COMMS_SUBSYS *) ep_subsys_find (sys, EPCOMMS_SUBSYS_NAME);
++    struct list_head *el;
++
++    if (how == NULL || !strncmp (how, "rail", 4))
++    {
++      kmutex_lock (&subsys->Lock);
++      list_for_each (el, &subsys->Rails) {
++          EP_COMMS_RAIL *commsRail = list_entry (el, EP_COMMS_RAIL, Link);
++
++          EP_RAIL_OP(commsRail, DisplayRail) (commsRail);
++      }
++      kmutex_unlock (&subsys->Lock);
++    }
++          
++    if (how == NULL || !strncmp (how, "xmtr", 4))
++      list_for_each (el, &subsys->Transmitters)
++          ep_display_xmtr (&di_ep_debug, list_entry (el, EP_XMTR, Link));
++
++    if (how == NULL || !strncmp (how, "rcvr", 4)) 
++      list_for_each (el, &subsys->Receivers)
++          ep_display_rcvr (&di_ep_debug, list_entry (el, EP_RCVR, Link), (how && how[4] == ',') ? 1 : 0);
++}
++
++int
++ep_svc_indicator_set (EP_SYS *epsys, int svc_indicator) 
++{
++    EP_COMMS_SUBSYS  *subsys;
++    struct list_head *el;
++
++    EPRINTF1 (DBG_SVC,"ep_svc_indicator_set: %d \n",svc_indicator);
++
++    if (svc_indicator < 0 || svc_indicator > EP_SVC_NUM_INDICATORS)
++      return (EP_EINVAL);
++
++    if ((subsys = (EP_COMMS_SUBSYS *) ep_subsys_find (epsys, "epcomms")) == NULL) {
++      EPRINTF0 (DBG_SVC,"ep_svc_indicator_set: ep_subsys_find failed\n");
++      return (EP_EINVAL);
++    }
++
++
++    kmutex_lock (&subsys->Lock); /* walking rails list and setting info on Rail */
++    list_for_each (el, &subsys->Rails) { 
++      EP_COMMS_RAIL *commsRail = list_entry (el, EP_COMMS_RAIL, Link);
++
++      cm_svc_indicator_set(commsRail->Rail, svc_indicator);
++    }
++    kmutex_unlock (&subsys->Lock);
++
++    EPRINTF1 (DBG_SVC,"ep_svc_indicator_set: %d success\n",svc_indicator);
++    return (EP_SUCCESS);
++}
++
++int
++ep_svc_indicator_clear (EP_SYS *epsys, int svc_indicator) 
++{
++    EP_COMMS_SUBSYS  *subsys;
++    struct list_head *el;
++
++    EPRINTF1 (DBG_SVC,"ep_svc_indicator_clear: %d \n",svc_indicator);
++
++    if (svc_indicator < 0 || svc_indicator >= EP_SVC_NUM_INDICATORS)
++      return (EP_EINVAL);
++
++    if ((subsys = (EP_COMMS_SUBSYS *) ep_subsys_find (epsys, "epcomms")) == NULL) {
++      EPRINTF0 (DBG_SVC,"ep_svc_indicator_clear: ep_subsys_find failed\n");
++      return (EP_EINVAL);
++    }
++
++    kmutex_lock (&subsys->Lock); /* walking rails list and setting info on Rail */
++    list_for_each (el, &subsys->Rails) { 
++      EP_COMMS_RAIL *commsRail = list_entry (el, EP_COMMS_RAIL, Link);
++
++      cm_svc_indicator_clear(commsRail->Rail, svc_indicator);
++    }
++    kmutex_unlock (&subsys->Lock);
++
++    EPRINTF1 (DBG_SVC,"ep_svc_indicator_clear: %d success\n",svc_indicator);
++    return (EP_SUCCESS);
++}
++
++int 
++ep_svc_indicator_is_set (EP_SYS *epsys, int svc_indicator, int nodeId) 
++{
++    EP_COMMS_SUBSYS  *subsys;
++    struct list_head *el;
++    int               set = 0;
++
++    EPRINTF2 (DBG_SVC,"ep_svc_indicator_is_set: svc %d node %d \n", svc_indicator, nodeId);
++
++    if ((subsys = (EP_COMMS_SUBSYS *) ep_subsys_find (epsys, "epcomms")) == NULL) {
++      EPRINTF0 (DBG_SVC,"ep_svc_indicator_is_set: ep_subsys_find failed\n");
++      return (0);
++    }
++
++    kmutex_lock (&subsys->Lock); /* walking rails list and setting info on Rail */
++    list_for_each (el, &subsys->Rails) { 
++      EP_COMMS_RAIL *commsRail = list_entry (el, EP_COMMS_RAIL, Link);
++
++      set |= cm_svc_indicator_is_set(commsRail->Rail, svc_indicator, nodeId);
++    }
++    kmutex_unlock (&subsys->Lock);
++
++    EPRINTF3 (DBG_SVC,"ep_svc_indicator_is_set: svc %d node %d returning %d\n", svc_indicator, nodeId, set);
++    return set;
++}
++
++int
++ep_svc_indicator_bitmap (EP_SYS *epsys, int svc_indicator, bitmap_t * bitmap, int low, int nnodes) 
++{
++    EP_COMMS_SUBSYS  *subsys;
++    struct list_head *el;
++
++    EPRINTF1 (DBG_SVC,"ep_svc_indicator_bitmap: svc %d\n", svc_indicator);
++
++    if (svc_indicator < 0 || svc_indicator >= EP_SVC_NUM_INDICATORS)
++      return (-1);
++
++    if ((subsys = (EP_COMMS_SUBSYS *) ep_subsys_find (epsys, "epcomms")) == NULL) {
++      EPRINTF0 (DBG_SVC,"ep_svc_indicator_bitmap: ep_subsys_find failed\n");
++      return (-2);
++    }
++
++    /* clear bitmap */
++    bt_zero (bitmap, nnodes);
++
++    kmutex_lock (&subsys->Lock); /* walking rails list and setting info on Rail */
++    list_for_each (el, &subsys->Rails) { 
++      EP_COMMS_RAIL *commsRail = list_entry (el, EP_COMMS_RAIL, Link);
++
++      /* this will or in each bit map */
++      cm_svc_indicator_bitmap (commsRail->Rail, svc_indicator, bitmap, low, nnodes);
++    }
++    kmutex_unlock (&subsys->Lock);
++
++    return (0);
++}
++
++int
++ep_xmtr_svc_indicator_bitmap (EP_XMTR *xmtr, int svc_indicator, bitmap_t * bitmap, int low, int nnodes) 
++{
++    int i;
++
++    EPRINTF1 (DBG_SVC,"ep_xmtr_svc_indicator_bitmap: svc %d\n", svc_indicator);
++
++    if (svc_indicator < 0 || svc_indicator >= EP_SVC_NUM_INDICATORS)
++      return (-1);
++
++    /* clear bitmap */
++    bt_zero (bitmap, nnodes);
++
++    for (i = 0; i < EP_MAX_RAILS; i++)
++    {
++      if (xmtr->RailMask & (1 << i) )
++      {
++          /* this will or in each bit map */
++          cm_svc_indicator_bitmap (xmtr->Rails[i]->CommsRail->Rail, svc_indicator, bitmap, low, nnodes);
++      }
++    }
++
++    return (0);
++}
++
++EP_RAILMASK
++ep_svc_indicator_railmask (EP_SYS *epsys, int svc_indicator, int nodeId)
++{
++    EP_COMMS_SUBSYS  *subsys;
++    struct list_head *el;
++    EP_RAILMASK       rmask=0;
++
++    if ((subsys = (EP_COMMS_SUBSYS *) ep_subsys_find (epsys, "epcomms")) == NULL)
++      return (rmask);
++
++    kmutex_lock (&subsys->Lock); /* walking rails list and reading info from Rail */
++    list_for_each (el, &subsys->Rails) { 
++      EP_COMMS_RAIL *commsRail = list_entry (el, EP_COMMS_RAIL, Link);
++
++      if ( cm_svc_indicator_is_set(commsRail->Rail, svc_indicator,nodeId))
++           rmask |= EP_RAIL2RAILMASK(commsRail->Rail->Number);
++    }
++    kmutex_unlock (&subsys->Lock);
++
++    return (rmask);
++}
++
++EP_RAILMASK
++ep_xmtr_svc_indicator_railmask (EP_XMTR *xmtr, int svc_indicator, int nodeId)
++{
++    EP_RAILMASK    rmask=0;
++    EP_COMMS_RAIL *commsRail;
++    int            i;
++
++    for (i = 0; i < EP_MAX_RAILS; i++)
++    {
++      if (xmtr->RailMask & (1 << i) )
++      {
++          commsRail = xmtr->Rails[i]->CommsRail;
++
++          if ( cm_svc_indicator_is_set(commsRail->Rail, svc_indicator,nodeId))
++              rmask |= EP_RAIL2RAILMASK(commsRail->Rail->Number);
++      }
++    }   
++    
++    EPRINTF3 (DBG_SVC, "ep_xmtr_svc_indicator_railmask: svc %d node %d mask 0x%x\n",  svc_indicator, nodeId, rmask);
++
++    return (rmask);
++}
++
++EP_RAILMASK
++ep_rcvr_railmask (EP_SYS *epsys, EP_SERVICE service)
++{
++    EP_COMMS_SUBSYS  *subsys;
++    EP_RAILMASK       rmask=0;
++    struct list_head *el;
++    
++    if ((subsys = (EP_COMMS_SUBSYS *) ep_subsys_find (epsys, "epcomms")) == NULL)
++      return (rmask);
++    
++    kmutex_lock (&subsys->Lock);
++    list_for_each (el, &subsys->Receivers) {
++      EP_RCVR *rcvr = list_entry (el, EP_RCVR, Link);
++ 
++      if (rcvr->Service == service)
++          rmask |= rcvr->RailMask; 
++    }
++    kmutex_unlock(&subsys->Lock);
++
++    return (rmask);
++}
++
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++uint32_t
++ep_calc_check_sum (EP_SYS *sys, EP_ENVELOPE *env, EP_NMD *nmd, int nFrags)
++{
++    EP_NMH   *nmh;
++    int       i;
++    uint16_t  check_data = 0;
++    uint16_t  check_env  = 0;
++
++    for (i = 0; i < nFrags; i++) {
++      /* find the nmh for this frag */
++      nmh = ep_nmh_find (&sys->MappingTable, &nmd[i]);
++
++      ASSERT( nmh != NULL);
++
++      /* add the next frag to the check sum */
++      check_data = nmh->nmh_ops->op_calc_check_sum (sys, nmh, &nmd[i], check_data);
++    }
++
++    check_env = rolling_check_sum ((char *) env, offsetof(EP_ENVELOPE, CheckSum), 0);
++
++    return (EP_ENVELOPE_CHECK_SUM | ( (check_env & 0x7FFF) << 16) | (check_data & 0xFFFF));
++}
++#endif
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/ep/epcomms_elan3.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/epcomms_elan3.c      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/epcomms_elan3.c   2005-05-11 12:10:12.485925432 -0400
+@@ -0,0 +1,191 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: epcomms_elan3.c,v 1.60 2004/08/03 11:34:34 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/epcomms_elan3.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++#include <elan/epsvc.h>
++#include <elan/epcomms.h>
++
++#include "kcomm_elan3.h"
++#include "epcomms_elan3.h"
++
++void
++ep3comms_flush_callback (void *arg, statemap_t *map)
++{
++    EP_COMMS_RAIL    *commsRail = (EP_COMMS_RAIL *) arg;
++    EP_COMMS_SUBSYS  *subsys    = commsRail->Subsys;
++    struct list_head *el;
++
++    kmutex_lock (&subsys->Lock);
++    list_for_each (el, &subsys->Transmitters) {
++      EP_XMTR *xmtr = list_entry (el, EP_XMTR, Link);
++      
++      if (xmtr->Rails[commsRail->Rail->Number])
++          ep3xmtr_flush_callback (xmtr, (EP3_XMTR_RAIL *) xmtr->Rails[commsRail->Rail->Number]);
++    }
++
++    list_for_each (el, &subsys->Receivers) {
++      EP_RCVR *rcvr = list_entry (el, EP_RCVR, Link);
++      
++      if (rcvr->Rails[commsRail->Rail->Number])
++          ep3rcvr_flush_callback (rcvr, (EP3_RCVR_RAIL *) rcvr->Rails[commsRail->Rail->Number]);
++    }
++    kmutex_unlock (&subsys->Lock);
++}
++
++void
++ep3comms_failover_callback (void *arg, statemap_t *map)
++{
++    EP_COMMS_RAIL    *commsRail = (EP_COMMS_RAIL *) arg;
++    EP_COMMS_SUBSYS  *subsys    = commsRail->Subsys;
++    struct list_head *el;
++
++    kmutex_lock (&subsys->Lock);
++    list_for_each (el, &subsys->Transmitters) {
++      EP_XMTR *xmtr = list_entry (el, EP_XMTR, Link);
++      
++      if (xmtr->Rails[commsRail->Rail->Number])
++          ep3xmtr_failover_callback (xmtr, (EP3_XMTR_RAIL *) xmtr->Rails[commsRail->Rail->Number]);
++    }
++
++    list_for_each (el, &subsys->Receivers) {
++      EP_RCVR *rcvr = list_entry (el, EP_RCVR, Link);
++      
++      if (rcvr->Rails[commsRail->Rail->Number])
++          ep3rcvr_failover_callback (rcvr, (EP3_RCVR_RAIL *) rcvr->Rails[commsRail->Rail->Number]);
++    }
++    kmutex_unlock (&subsys->Lock);
++}
++
++void
++ep3comms_disconnect_callback (void *arg, statemap_t *map)
++{
++    EP_COMMS_RAIL    *commsRail = (EP_COMMS_RAIL *) arg;
++    EP_COMMS_SUBSYS  *subsys    = commsRail->Subsys;
++    struct list_head *el;
++
++    kmutex_lock (&subsys->Lock);
++    list_for_each (el, &subsys->Transmitters) {
++      EP_XMTR *xmtr = list_entry (el, EP_XMTR, Link);
++      
++      if (xmtr->Rails[commsRail->Rail->Number])
++          ep3xmtr_disconnect_callback (xmtr, (EP3_XMTR_RAIL *) xmtr->Rails[commsRail->Rail->Number]);
++    }
++
++    list_for_each (el, &subsys->Receivers) {
++      EP_RCVR *rcvr = list_entry (el, EP_RCVR, Link);
++      
++      if (rcvr->Rails[commsRail->Rail->Number])
++          ep3rcvr_disconnect_callback (rcvr, (EP3_RCVR_RAIL *) rcvr->Rails[commsRail->Rail->Number]);
++    }
++    kmutex_unlock (&subsys->Lock);
++}
++
++EP_COMMS_RAIL *
++ep3comms_add_rail (EP_SUBSYS *s, EP_SYS *sys, EP_RAIL *r)
++{
++    EP3_RAIL         *rail   = (EP3_RAIL *) r;
++    ELAN3_DEV        *dev    = rail->Device;
++    EP3_COMMS_RAIL   *commsRail;
++    EP3_InputQueue    qdesc;
++    int i;
++
++    KMEM_ZALLOC (commsRail, EP3_COMMS_RAIL *, sizeof (EP3_COMMS_RAIL), TRUE);
++
++    if (commsRail == NULL)
++      return NULL;
++    
++    commsRail->Generic.Ops.DelRail          = ep3comms_del_rail;
++    commsRail->Generic.Ops.DisplayRail      = ep3comms_display_rail;
++    commsRail->Generic.Ops.Rcvr.AddRail     = ep3rcvr_add_rail;
++    commsRail->Generic.Ops.Rcvr.DelRail     = ep3rcvr_del_rail;
++    commsRail->Generic.Ops.Rcvr.Check       = ep3rcvr_check;
++    commsRail->Generic.Ops.Rcvr.QueueRxd    = ep3rcvr_queue_rxd;
++    commsRail->Generic.Ops.Rcvr.RpcPut      = ep3rcvr_rpc_put;
++    commsRail->Generic.Ops.Rcvr.RpcGet      = ep3rcvr_rpc_get;
++    commsRail->Generic.Ops.Rcvr.RpcComplete = ep3rcvr_rpc_complete;
++
++    commsRail->Generic.Ops.Rcvr.StealRxd    = ep3rcvr_steal_rxd;
++
++    commsRail->Generic.Ops.Rcvr.FillOutRailStats = ep3rcvr_fillout_rail_stats;
++
++    commsRail->Generic.Ops.Rcvr.DisplayRcvr = ep3rcvr_display_rcvr;
++    commsRail->Generic.Ops.Rcvr.DisplayRxd  = ep3rcvr_display_rxd;
++
++    commsRail->Generic.Ops.Xmtr.AddRail     = ep3xmtr_add_rail;
++    commsRail->Generic.Ops.Xmtr.DelRail     = ep3xmtr_del_rail;
++    commsRail->Generic.Ops.Xmtr.Check       = ep3xmtr_check;
++    commsRail->Generic.Ops.Xmtr.BindTxd     = ep3xmtr_bind_txd;
++    commsRail->Generic.Ops.Xmtr.UnbindTxd   = ep3xmtr_unbind_txd;
++    commsRail->Generic.Ops.Xmtr.PollTxd     = ep3xmtr_poll_txd;
++    commsRail->Generic.Ops.Xmtr.CheckTxdState = ep3xmtr_check_txd_state;
++
++    commsRail->Generic.Ops.Xmtr.DisplayXmtr = ep3xmtr_display_xmtr;
++    commsRail->Generic.Ops.Xmtr.DisplayTxd  = ep3xmtr_display_txd;
++
++    commsRail->Generic.Ops.Xmtr.FillOutRailStats = ep3xmtr_fillout_rail_stats;
++
++    /* Allocate the input queues at their fixed elan address */
++    if (! (commsRail->QueueDescs = ep_alloc_memory_elan (r, EP_EPCOMMS_QUEUE_BASE, roundup (EP_MSG_NSVC * sizeof (EP3_InputQueue), PAGESIZE), EP_PERM_ALL, 0)))
++    {
++      KMEM_FREE (commsRail, sizeof (EP3_COMMS_RAIL));
++      return NULL;
++    }
++
++    qdesc.q_state          = E3_QUEUE_FULL;
++    qdesc.q_base           = 0;
++    qdesc.q_top            = 0;
++    qdesc.q_fptr           = 0;
++    qdesc.q_bptr           = 0;
++    qdesc.q_size           = 0;
++    qdesc.q_event.ev_Count = 0;
++    qdesc.q_event.ev_Type  = 0;
++
++    /* Initialise all queue entries to be full */
++    for (i = 0; i < EP_MSG_NSVC; i++)
++      elan3_sdram_copyl_to_sdram (dev, &qdesc, commsRail->QueueDescs + (i * sizeof (EP3_InputQueue)), sizeof (EP3_InputQueue));
++
++    ep_register_callback (r, EP_CB_FLUSH_FILTERING, ep3comms_flush_callback,      commsRail);
++    ep_register_callback (r, EP_CB_FLUSH_FLUSHING,  ep3comms_flush_callback,      commsRail);
++    ep_register_callback (r, EP_CB_FAILOVER,        ep3comms_failover_callback,   commsRail);
++    ep_register_callback (r, EP_CB_DISCONNECTING,   ep3comms_disconnect_callback, commsRail);
++
++    return (EP_COMMS_RAIL *) commsRail;
++}
++
++void
++ep3comms_del_rail (EP_COMMS_RAIL *r)
++{
++    EP3_COMMS_RAIL *commsRail = (EP3_COMMS_RAIL *) r;
++    EP_RAIL        *rail      = commsRail->Generic.Rail;
++
++    ep_remove_callback (rail, EP_CB_FLUSH_FILTERING, ep3comms_flush_callback,      commsRail);
++    ep_remove_callback (rail, EP_CB_FLUSH_FLUSHING,  ep3comms_flush_callback,      commsRail);
++    ep_remove_callback (rail, EP_CB_FAILOVER,        ep3comms_failover_callback,   commsRail);
++    ep_remove_callback (rail, EP_CB_DISCONNECTING,   ep3comms_disconnect_callback, commsRail);
++
++    ep_free_memory_elan (rail, EP_EPCOMMS_QUEUE_BASE);
++
++    KMEM_FREE (commsRail, sizeof (EP3_COMMS_RAIL));
++}
++
++void
++ep3comms_display_rail (EP_COMMS_RAIL *r)
++{
++    
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/ep/epcomms_elan3.h
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/epcomms_elan3.h      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/epcomms_elan3.h   2005-05-11 12:10:12.485925432 -0400
+@@ -0,0 +1,330 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __EPCOMMS_ELAN3_H
++#define __EPCOMMS_ELAN3_H
++
++#ident "@(#)$Id: epcomms_elan3.h,v 1.27.2.1 2004/11/12 10:54:51 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/epcomms_elan3.h,v $ */
++
++#define EP3_DMAFAILCOUNT              3
++
++
++/* Main/Elan spinlock */
++typedef struct ep3_spinlock_elan 
++{
++    volatile E3_uint32        sl_lock;                /* main wants a lock */
++    volatile E3_uint32        sl_seq;                 /* thread owns this word */
++    /* NOTE: The lock/seq words must be within the same 32-byte Elan cache-line */
++    E3_uint64         sl_pad[14];             /* pad to 64-bytes */
++} EP3_SPINLOCK_ELAN;
++
++/* Declare this as a main memory cache block for efficiency */
++typedef struct ep3_spinlock_main {
++    volatile E3_uint32        sl_seq;                 /* copy of seq number updated by Elan */
++    volatile E3_uint32        sl_pad[15];             /* pad to 64-bytes */
++} EP3_SPINLOCK_MAIN;
++
++#if defined (__ELAN3__)
++
++extern void ep3_spinblock (EP3_SPINLOCK_ELAN *, EP3_SPINLOCK_MAIN *);
++
++#define EP3_SPINENTER(SLE,SL) \
++do {\
++      (SLE)->sl_seq++; \
++      if ((SLE)->sl_lock) \
++              ep3_spinblock(SLE, SL);\
++} while (0)
++
++#define EP3_SPINEXIT(SLE,SL) \
++do {\
++      (SL)->sl_seq = (SLE)->sl_seq;\
++} while (0)
++
++#else
++
++#define EP3_SPINENTER(DEV,SLE,SL) do { \
++    E3_uint32 seq; \
++\
++    mb();\
++    elan3_sdram_writel (DEV, (SLE) + offsetof (EP3_SPINLOCK_ELAN, sl_lock), 1);\
++    mb();\
++    seq = elan3_sdram_readl (DEV, (SLE) + offsetof (EP3_SPINLOCK_ELAN, sl_seq));\
++    while (seq != (SL)->sl_seq)\
++    {\
++      while ((SL)->sl_seq == (seq - 1))\
++      {\
++          mb();\
++\
++          DELAY (1); \
++      }\
++      seq = elan3_sdram_readl (DEV, (SLE) + offsetof (EP3_SPINLOCK_ELAN, sl_seq));\
++    }\
++} while (0)
++
++#define EP3_SPINEXIT(DEV,SLE,SL) do { \
++      wmb(); \
++      elan3_sdram_writel (DEV, (SLE) + offsetof (EP3_SPINLOCK_ELAN, sl_lock), 0);\
++      mmiob(); \
++} while (0)
++
++#endif /* ! __ELAN3__ */
++
++/* per-rail elan memory portion receive descriptor */
++typedef struct ep3_rxd_rail_elan
++{
++    E3_DMA            Dmas[EP_MAXFRAG+1];                     /* Dma's for fetching data/putting data & status blk */
++    E3_Event          ChainEvent[EP_MAXFRAG];                 /* Events to chain dmas */
++    E3_BlockCopyEvent DataEvent;                              /* message received block event */
++    E3_BlockCopyEvent DoneEvent;                              /* RPC status block event */
++    
++    EP_NMD            Data;                                   /* Network mapping handle for receive data */
++
++    E3_Addr           RxdMain;                                /* pointer to main memory portion */
++
++    E3_Addr           Next;                                   /* linked list when on pending list (elan address) */
++
++    E3_uint64         MainAddr;                               /* kernel address of ep_rxd_main */
++} EP3_RXD_RAIL_ELAN;
++
++#define EP3_RXD_RAIL_ELAN_SIZE        roundup (sizeof (EP3_RXD_RAIL_ELAN), E3_DMA_ALIGN)
++
++/* per-rail main memory portion of receive descriptor */
++typedef struct ep3_rxd_rail_main
++{
++    E3_uint32         DataEvent;                              /* dest for done event */
++    E3_uint32         DoneEvent;                              /* dest for done event */
++} EP3_RXD_RAIL_MAIN;
++
++#define EP3_RXD_RAIL_MAIN_SIZE        roundup (sizeof(EP3_RXD_RAIL_MAIN), sizeof (E3_uint32))
++
++#if !defined(__ELAN3__)
++/* Kernel memory portion of per-rail receive descriptor */
++typedef struct ep3_rxd_rail
++{
++    EP_RXD_RAIL               Generic;                                /* generic rxd rail */
++
++    EP3_COOKIE                DataCookie;                             /* Event cookie */
++    EP3_COOKIE                DoneCookie;                             /* Event cookie */
++    EP3_COOKIE                ChainCookie[EP_MAXFRAG];                /* Event cookie */
++
++    sdramaddr_t               RxdElan;                                /* per-rail elan receive descriptor */
++    E3_Addr           RxdElanAddr;                            /*   and elan address */
++
++    EP3_RXD_RAIL_MAIN  *RxdMain;                              /* per-rail main receive descriptor */
++    E3_Addr           RxdMainAddr;                            /*   and elan address */
++
++    EP_BACKOFF                Backoff;                                /* dma backoff */
++} EP3_RXD_RAIL;
++
++#define EP3_NUM_RXD_PER_BLOCK 16
++
++typedef struct ep3_rxd_rail_block
++{
++    struct list_head  Link;
++    
++    EP3_RXD_RAIL        Rxd[EP3_NUM_RXD_PER_BLOCK];
++} EP3_RXD_RAIL_BLOCK;
++
++#endif /* ! __ELAN3__ */
++
++typedef struct ep3_rcvr_rail_elan                             /* Elan memory service structure */
++{
++    EP3_SPINLOCK_ELAN  ThreadLock;                            /* elan memory portion of spin lock */
++    EP3_SPINLOCK_ELAN  PendingLock;                           /* spin lock for pending rx list */
++
++    E3_Addr          PendingDescs;                            /* list of pending receive descriptors */
++    E3_uint32          ThreadShouldHalt;                        /* marks that the thread should halt */
++
++    E3_uint64        MainAddr;                                /* kernel address of ep_rcvr (for StallThreadForNoDescs)*/
++} EP3_RCVR_RAIL_ELAN;
++
++typedef struct ep3_rcvr_rail_main                             /* Main memory service strucure */
++{
++    EP3_SPINLOCK_MAIN ThreadLock;                             /* main memory portion of spin lock */
++    EP3_SPINLOCK_MAIN PendingLock;                            /* spinlock for pending rx list */
++
++    volatile unsigned   PendingDescsTailp;                    /* next pointer of last receive descriptor on pending list */
++} EP3_RCVR_RAIL_MAIN;
++
++#if !defined(__ELAN3__)
++
++typedef struct ep3_rcvr_rail_stats
++{
++    unsigned long some_stat;
++} EP3_RCVR_RAIL_STATS;
++
++typedef struct ep3_rcvr_rail
++{
++    EP_RCVR_RAIL      Generic;                                /* generic portion */
++    
++    EP3_RCVR_RAIL_MAIN *RcvrMain;
++    E3_Addr           RcvrMainAddr;
++    sdramaddr_t         RcvrElan;
++    E3_Addr             RcvrElanAddr;
++
++    sdramaddr_t               InputQueueBase;                         /* base of receive queue */
++    E3_Addr           InputQueueAddr;                         /* elan address of receive queue */
++
++    E3_Addr           ThreadStack;                            /* Thread processor stack */
++    E3_Addr           ThreadWaiting;                          /* Elan thread is waiting as no receive descriptors pending (sp stored here ) */
++    E3_Addr           ThreadHalted;                           /* Elan thread is waiting as it was requested to halt */
++
++    struct list_head  FreeDescList;                           /* freelist of per-rail receive descriptors */
++    unsigned int      FreeDescCount;                          /*   and number on free list */
++    unsigned int        TotalDescCount;                               /*   total number created */
++    spinlock_t                FreeDescLock;                           /*   and lock for free list */
++    struct list_head    DescBlockList;                                /* list of receive descriptor blocks */
++
++    unsigned int        FreeDescWaiting;                      /* waiting for descriptors to be freed */
++    kcondvar_t                FreeDescSleep;                          /*   and sleep here */
++
++    unsigned int      CleanupWaiting;                         /* waiting for cleanup */
++    kcondvar_t                CleanupSleep;                           /*   and sleep here */
++
++    EP3_RCVR_RAIL_STATS stats;                                  /* elan3 specific rcvr_rail stats */
++} EP3_RCVR_RAIL;
++
++#endif /* ! __ELAN3__ */
++
++/* per-rail portion of transmit descriptor */
++typedef struct ep3_txd_rail_elan
++{
++    EP_ENVELOPE              Envelope;                                /* message envelope */
++    EP_PAYLOAD               Payload;                                 /* message payload */
++
++    E3_BlockCopyEvent EnveEvent;                              /* envelope event */
++    E3_BlockCopyEvent DataEvent;                              /* data transfer event */
++    E3_BlockCopyEvent DoneEvent;                              /* rpc done event */
++} EP3_TXD_RAIL_ELAN;
++
++#define EP3_TXD_RAIL_ELAN_SIZE        roundup (sizeof (EP3_TXD_RAIL_ELAN), E3_BLK_ALIGN)
++
++typedef struct ep3_txd_rail_main
++{
++    E3_uint32         EnveEvent;                              /* dest for envelope event */
++    E3_uint32         DataEvent;                              /* dest for data transfer event */
++    E3_uint32       DoneEvent;                                /* dest for rpc done event */
++} EP3_TXD_RAIL_MAIN;
++
++#define EP3_TXD_RAIL_MAIN_SIZE        roundup (sizeof(EP3_TXD_RAIL_MAIN), E3_BLK_ALIGN)
++
++#if !defined(__ELAN3__)
++
++typedef struct ep3_txd_rail
++{
++    EP_TXD_RAIL              Generic;                                 /* generic txd rail */
++
++    EP3_COOKIE               EnveCookie;                              /* Event cookies */
++    EP3_COOKIE               DataCookie;
++    EP3_COOKIE               DoneCookie;
++
++    sdramaddr_t              TxdElan;                                 /* Elan TX descriptor */
++    E3_Addr          TxdElanAddr;                             /*  and elan address */
++
++    EP3_TXD_RAIL_MAIN *TxdMain;                                       /* Elan Main memory tx descriptor */
++    E3_Addr          TxdMainAddr;                             /*  and elan address */
++
++    EP_BACKOFF               Backoff;                                 /* dma backoff */
++} EP3_TXD_RAIL;
++
++
++#define EP3_NUM_TXD_PER_BLOCK 16
++
++typedef struct ep3_txd_rail_block
++{
++    struct list_head  Link;
++    
++    EP3_TXD_RAIL      Txd[EP3_NUM_TXD_PER_BLOCK];
++} EP3_TXD_RAIL_BLOCK;
++
++typedef struct ep3_xmtr_rail_stats
++{
++    unsigned long some_stat;
++} EP3_XMTR_RAIL_STATS;
++
++typedef struct ep3_xmtr_rail
++{
++    EP_XMTR_RAIL      Generic;                                /* generic portion */
++
++    struct list_head  FreeDescList;                           /* freelist of per-rail receive descriptors */
++    unsigned int      FreeDescCount;                          /*   and number on free list */
++    unsigned int        TotalDescCount;
++    spinlock_t                FreeDescLock;                           /*   and lock for free list */
++    struct list_head    DescBlockList;                                /* list of receive descriptor blocks */
++
++    unsigned int        FreeDescWaiting;                      /* waiting for descriptors to be freed */
++    kcondvar_t          FreeDescSleep;                                /*   and sleep here */
++
++    EP3_XMTR_RAIL_STATS stats;                                  /* elan3 specific xmtr rail stats */
++} EP3_XMTR_RAIL;
++
++typedef struct ep3_comms_rail
++{
++    EP_COMMS_RAIL     Generic;                                /* generic comms rail */
++    sdramaddr_t               QueueDescs;                             /* input queue descriptors */
++} EP3_COMMS_RAIL;
++
++/* epcommxTx_elan3.c */
++extern void           ep3xmtr_flush_callback (EP_XMTR *xmtr, EP3_XMTR_RAIL *xmtrRail);
++extern void           ep3xmtr_failover_callback (EP_XMTR *xmtr, EP3_XMTR_RAIL *xmtrRail);
++extern void           ep3xmtr_disconnect_callback (EP_XMTR *xmtr, EP3_XMTR_RAIL *xmtrRail);
++
++/* epcommsRx_elan3.c */
++extern void         CompleteEnvelope (EP3_RAIL *rail, E3_Addr rxdMainAddr, E3_uint32 PAckVal);
++extern void           StallThreadForNoDescs (EP3_RAIL *rail, E3_Addr rcvrElanAddr, E3_Addr sp);
++extern void           StallThreadForHalted  (EP3_RAIL *rail, E3_Addr rcvrElanAddr, E3_Addr sp);
++
++extern void           ep3rcvr_flush_callback (EP_RCVR *rcvr, EP3_RCVR_RAIL *rcvrRail);
++extern void           ep3rcvr_failover_callback (EP_RCVR *rcvr, EP3_RCVR_RAIL *rcvrRail);
++extern void           ep3rcvr_disconnect_callback (EP_RCVR *rcvr, EP3_RCVR_RAIL *rcvrRail);
++
++/* epcomms_elan3.c */
++extern EP_COMMS_RAIL *ep3comms_add_rail (EP_SUBSYS *s, EP_SYS *sys, EP_RAIL *r);
++extern void           ep3comms_del_rail (EP_COMMS_RAIL *r);
++extern void           ep3comms_display_rail (EP_COMMS_RAIL *r);
++
++/* epcommsTx_elan3.c */
++extern int            ep3xmtr_bind_txd (EP_TXD *txd, EP_XMTR_RAIL *xmtrRail, unsigned int phase);
++extern void           ep3xmtr_unbind_txd (EP_TXD *txd, unsigned int phase);
++extern int            ep3xmtr_poll_txd (EP_XMTR_RAIL *xmtrRail, EP_TXD_RAIL *txdRail, int how);
++extern long           ep3xmtr_check (EP_XMTR_RAIL *xmtrRail, long nextRunTime);
++extern void           ep3xmtr_add_rail (EP_XMTR *xmtr, EP_COMMS_RAIL *commsRail);
++extern void           ep3xmtr_del_rail (EP_XMTR *xmtr, EP_COMMS_RAIL *commsRail);
++extern int            ep3xmtr_check_txd_state(EP_TXD *txd);
++
++extern void           ep3xmtr_display_xmtr (DisplayInfo *di, EP_XMTR_RAIL *xmtrRail);
++extern void           ep3xmtr_display_txd  (DisplayInfo *di, EP_TXD_RAIL *txdRail);
++
++extern void           ep3xmtr_fillout_rail_stats (EP_XMTR_RAIL *xmtr_rail, char *str);
++
++/* epcommsRx_elan3.c */
++extern int          ep3rcvr_queue_rxd (EP_RXD *rxd, EP_RCVR_RAIL *rcvrRail);
++extern void         ep3rcvr_rpc_put (EP_RXD *rxd, EP_NMD *local, EP_NMD *remote, unsigned nFrags);
++extern void         ep3rcvr_rpc_get (EP_RXD *rxd, EP_NMD *local, EP_NMD *remote, unsigned nFrags);
++extern void         ep3rcvr_rpc_complete (EP_RXD *rxd, EP_NMD *local, EP_NMD *remote, unsigned nFrags);
++
++extern EP_RXD      *ep3rcvr_steal_rxd (EP_RCVR_RAIL *rcvrRail);
++
++extern long         ep3rcvr_check (EP_RCVR_RAIL *rcvrRail, long nextRunTime);
++extern void           ep3rcvr_add_rail (EP_RCVR *rcvr, EP_COMMS_RAIL *rail);
++extern void           ep3rcvr_del_rail (EP_RCVR *rcvr, EP_COMMS_RAIL *rail);
++
++extern void           ep3rcvr_display_rcvr (DisplayInfo *di, EP_RCVR_RAIL *rcvrRail);
++extern void           ep3rcvr_display_rxd  (DisplayInfo *di, EP_RXD_RAIL *rxdRail);
++
++extern void           ep3rcvr_fillout_rail_stats (EP_RCVR_RAIL *rcvr_rail, char *str);
++
++#endif /* !defined(__ELAN3__) */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++#endif /* __EPCOMMS_ELAN3_H */
+Index: linux-2.6.5/drivers/net/qsnet/ep/epcomms_elan3_thread.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/epcomms_elan3_thread.c       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/epcomms_elan3_thread.c    2005-05-11 12:10:12.486925280 -0400
+@@ -0,0 +1,296 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: epcomms_elan3_thread.c,v 1.4 2004/01/20 11:03:15 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/epcomms_elan3_thread.c,v $ */
++
++//#include <qsnet/types.h>
++
++typedef char               int8_t;
++typedef unsigned char      uint8_t;
++typedef short              int16_t;
++typedef unsigned short     uint16_t;
++typedef int                int32_t;
++typedef unsigned int       uint32_t;
++typedef long long          int64_t;
++typedef unsigned long long uint64_t;
++
++#include <elan3/e3types.h>
++#include <elan3/events.h>
++#include <elan3/elanregs.h>
++#include <elan3/intrinsics.h>
++
++#include <elan/nmh.h>
++#include <elan/kcomm.h>
++#include <elan/epcomms.h>
++
++#include "kcomm_vp.h"
++#include "kcomm_elan3.h"
++#include "epcomms_elan3.h"
++
++#ifndef offsetof
++#define offsetof(s, m)                        (unsigned long)(&(((s *)0)->m))
++#endif
++
++EP3_RAIL_ELAN *rail;
++EP3_RCVR_RAIL_ELAN *r;
++EP3_RCVR_RAIL_MAIN *rm;
++
++void
++ep3comms_rcvr (EP3_RAIL_ELAN *rail, EP3_RCVR_RAIL_ELAN *rcvrElan, EP3_RCVR_RAIL_MAIN *rcvrMain, 
++            EP3_InputQueue *q, unsigned int *cookies)
++{
++    int           count = 1;
++    E3_Addr       nfptr = q->q_fptr + q->q_size;
++    E3_uint32     tmp;
++    int           i;
++    E3_Addr       buffer;
++    int                 len;
++    E3_DMA       *dma;
++    E3_Event     *event;
++
++    /* clear the queue state to allow envelopes to arrive */
++    q->q_state = 0;
++
++    for (;;)
++    {
++      if (! rcvrElan->ThreadShouldHalt)
++          c_waitevent ((E3_Event *) &q->q_event, count);                                              /* HALT POINT */
++
++      if (rcvrElan->ThreadShouldHalt && nfptr == q->q_bptr)
++      {
++          asm volatile ("mov %0, %%g1" : /* no outputs */ : "r" (rcvrElan));
++          asm volatile ("ta %0"        : /* no outputs */ : "i" (EP3_UNIMP_THREAD_HALTED));           /* HALT POINT */
++          continue;
++      }
++
++      count = 0;
++      do {
++          /* Process the message at nfptr */
++          EP_ENVELOPE      *env = (EP_ENVELOPE *) nfptr;
++          EP3_RXD_RAIL_ELAN *rxd;
++          int ack;
++          
++          EP3_SPINENTER(&rcvrElan->ThreadLock, &rcvrMain->ThreadLock);                                        /* HALT POINT */
++          
++          while ((rxd = (EP3_RXD_RAIL_ELAN *)rcvrElan->PendingDescs) == 0)
++          {
++              /* no receive descriptors, so trap to the kernel to wait
++               * for receive descriptor to be queued, we pass the rcvr
++               * in %g1, so that the trap handler can restart us. */
++              EP3_SPINEXIT(&rcvrElan->ThreadLock, &rcvrMain->ThreadLock);
++              asm volatile ("mov %0, %%g1" : /* no outputs */ : "r" (rcvrElan));
++              asm volatile ("ta %0"        : /* no outputs */ : "i" (EP3_UNIMP_TRAP_NO_DESCS));       /* HALT POINT */
++              EP3_SPINENTER(&rcvrElan->ThreadLock, &rcvrMain->ThreadLock);                            /* HALT POINT */
++          }
++
++          if (env->Version != EP_ENVELOPE_VERSION)
++          {
++              /* This envelope has been cancelled - so just consume it */
++              EP3_SPINEXIT(&rcvrElan->ThreadLock, &rcvrMain->ThreadLock);
++              goto consume_envelope;
++          }
++
++          dma   = rxd->Dmas;
++          event = rxd->ChainEvent;
++
++          if (EP_IS_MULTICAST(env->Attr))
++          {
++              dma->dma_type            = E3_DMA_TYPE (DMA_BYTE, DMA_READ, DMA_NORMAL, EP3_DMAFAILCOUNT);
++              dma->dma_size            = BT_BITOUL(EP_MAX_NODES) * sizeof (bitmap_t);
++              dma->dma_source          = env->TxdMain.nmd_addr + offsetof (EP_TXD_MAIN, Bitmap);
++              dma->dma_dest            = (E3_Addr) &((EP_RXD_MAIN *) rxd->RxdMain)->Bitmap;
++              dma->dma_destEvent       = (E3_Addr) event;
++              dma->dma_destCookieVProc = DMA_COOKIE_THREAD | DMA_COOKIE (cookies[env->NodeId], EP_VP_DATA (rail->NodeId));
++              dma->dma_srcEvent        = env->TxdRail + offsetof (EP3_TXD_RAIL_ELAN, DataEvent);
++              dma->dma_srcCookieVProc  = DMA_COOKIE_THREAD | DMA_REMOTE_COOKIE (cookies[env->NodeId], EP_VP_DATA (env->NodeId));
++              
++              event->ev_Count = 1;
++
++              dma++; event++;
++          }
++
++          if (env->nFrags == 0)
++          {
++              /* Generate a "get" DMA to accept the envelope and fire the rx handler */
++              dma->dma_type            = E3_DMA_TYPE(DMA_BYTE, DMA_READ, DMA_NORMAL, EP3_DMAFAILCOUNT);
++              dma->dma_size            = 0;
++              dma->dma_destEvent       = (E3_Addr) &rxd->DataEvent;
++              dma->dma_destCookieVProc = DMA_COOKIE_THREAD | DMA_COOKIE (cookies[env->NodeId], EP_VP_DATA (rail->NodeId));
++              dma->dma_srcEvent        = env->TxdRail + offsetof (EP3_TXD_RAIL_ELAN, DataEvent);
++              dma->dma_srcCookieVProc  = DMA_COOKIE_THREAD | DMA_REMOTE_COOKIE (cookies[env->NodeId], EP_VP_DATA (env->NodeId));
++              len = 0;
++          }
++          else
++          {
++              /* Generate the DMA chain to fetch the data */
++              for (i = 0, buffer = rxd->Data.nmd_addr, len = 0; i < env->nFrags; i++, dma++, event++)
++              {
++                  dma->dma_type            = E3_DMA_TYPE(DMA_BYTE, DMA_READ, DMA_NORMAL, EP3_DMAFAILCOUNT);
++                  dma->dma_size            = env->Frags[i].nmd_len;
++                  dma->dma_source          = env->Frags[i].nmd_addr;
++                  dma->dma_dest            = buffer;
++                  dma->dma_destEvent       = (E3_Addr) event;
++                  dma->dma_destCookieVProc = DMA_COOKIE_THREAD | DMA_COOKIE (cookies[env->NodeId], EP_VP_DATA (rail->NodeId));
++                  dma->dma_srcEvent        = env->TxdRail + offsetof (EP3_TXD_RAIL_ELAN, DataEvent);
++                  dma->dma_srcCookieVProc  = DMA_COOKIE_THREAD | DMA_REMOTE_COOKIE (cookies[env->NodeId], EP_VP_DATA (env->NodeId));
++                  
++                  event->ev_Count = 1;
++                  
++                  buffer += dma->dma_size;
++                  len    += dma->dma_size;
++              }
++              
++              /* Point the last dma at the done event */
++              (--dma)->dma_destEvent = (E3_Addr) &rxd->DataEvent;
++              
++              if (rxd->Data.nmd_len < len)
++              {
++                  /* The receive descriptor was too small for the message */
++                  /* complete the message anyway,  but don't transfer any */
++                  /* data,  we set the length to EP_MSG_TOO_BIG */
++                  for (i = 0, dma = rxd->Dmas; i < env->nFrags; i++, dma++)
++                      dma->dma_size = 0;
++                  
++                  len = EP_MSG_TOO_BIG;
++              }
++          }
++          
++          /* Store the received message length in the rxdElan for CompleteEnvelope */
++          rxd->Data.nmd_len = len;
++
++          /* Initialise %g1 with the  "rxd" so the trap handler can
++           * complete the envelope processing if we trap while sending the
++           * packet */
++          asm volatile ("mov %0, %%g1" : /* no outputs */ : "r" (rxd));
++
++          /* Generate a packet to start the data transfer */
++          c_open (EP_VP_DATA (env->NodeId));
++          c_sendtrans2 (TR_THREADIDENTIFY, rxd->Dmas->dma_destCookieVProc, 0, 0);
++          c_sendmem (TR_SENDACK | TR_REMOTEDMA, 0, rxd->Dmas); 
++          ack = c_close();
++          
++          /*
++           * If we trapped for an output timeout, then the trap handler will have
++           * completed processing this envelope and cleared the spinlock, so we just
++           * need to update the queue descriptor.
++           */
++          if (ack == EP3_PAckStolen)
++              goto consume_envelope;
++          
++          if (ack != E3_PAckOk)
++          {
++              /* our packet got nacked, so trap into the kernel so that
++               * it can complete processing of this envelope.
++               */
++              asm volatile ("ta %0" : /* no outputs */ : "i" (EP3_UNIMP_TRAP_PACKET_NACKED));         /* HALT POINT */
++              goto consume_envelope;
++          }
++
++          /* remove the RXD from the pending list */
++          EP3_SPINENTER (&rcvrElan->PendingLock, &rcvrMain->PendingLock);
++          if ((rcvrElan->PendingDescs = rxd->Next) == 0)
++              rcvrMain->PendingDescsTailp = 0;
++          EP3_SPINEXIT (&rcvrElan->PendingLock, &rcvrMain->PendingLock);
++
++          /* Copy the envelope information - as 5 64 byte chunks.
++           * We force the parameters in g5, g6 so that they aren't
++           * trashed by the loadblk32 into the locals/ins
++           */
++          if (EP_HAS_PAYLOAD(env->Attr))
++          { 
++              register void *src asm ("g5") = (void *) env;
++              register void *dst asm ("g6") = (void *)  &((EP_RXD_MAIN *) rxd->RxdMain)->Envelope;
++
++              asm volatile (
++                  "and     %%sp,63,%%g7               ! Calculate stack alignment\n"
++                  "add     %%g7,64,%%g7               ! Space to save the registers\n"
++                  "sub     %%sp,%%g7,%%sp             ! align stack\n" 
++                  "stblock64 %%l0,[%%sp]              ! save the locals and ins\n"
++
++                  "ldblock64 [%0 + 0],%%l0            ! load 64-byte block into locals/ins\n"         /* copy envelope */
++                  "stblock64 %%l0,[%1 + 0]            ! store 64-byte block from local/ins\n"
++                  "ldblock64 [%0 + 64],%%l0           ! load 64-byte block into locals/ins\n"
++                  "stblock64 %%l0,[%1 + 64]           ! store 64-byte block from local/ins\n"
++
++                  "ldblock64 [%0 + 128],%%l0          ! load 64-byte block into locals/ins\n"         /* copy payload */
++                  "stblock64 %%l0,[%1 + 128]          ! store 64-byte block from local/ins\n"
++                  "ldblock64 [%0 + 192],%%l0          ! load 64-byte block into locals/ins\n"
++                  "stblock64 %%l0,[%1 + 192]          ! store 64-byte block from local/ins\n"
++
++                  "ldblock64 [%%sp],%%l0              ! restore locals and ins\n"
++                  "add     %%sp,%%g7,%%sp             ! restore stack pointer\n"
++                  : /* outputs */
++                  : /* inputs */ "r" (src), "r" (dst)
++                  : /* clobbered */ "g5", "g6", "g7" );
++          }
++          else
++          { 
++              register void *src asm ("g5") = (void *) env;
++              register void *dst asm ("g6") = (void *)  &((EP_RXD_MAIN *) rxd->RxdMain)->Envelope;
++
++              asm volatile (
++                  "and     %%sp,63,%%g7               ! Calculate stack alignment\n"
++                  "add     %%g7,64,%%g7               ! Space to save the registers\n"
++                  "sub     %%sp,%%g7,%%sp             ! align stack\n" 
++                  "stblock64 %%l0,[%%sp]              ! save the locals and ins\n"
++
++                  "ldblock64 [%0 + 0],%%l0            ! load 64-byte block into locals/ins\n"
++                  "stblock64 %%l0,[%1 + 0]            ! store 64-byte block from local/ins\n"
++                  "ldblock64 [%0 + 64],%%l0           ! load 64-byte block into locals/ins\n"
++                  "stblock64 %%l0,[%1 + 64]           ! store 64-byte block from local/ins\n"
++
++                  "ldblock64 [%%sp],%%l0              ! restore locals and ins\n"
++                  "add     %%sp,%%g7,%%sp             ! restore stack pointer\n"
++                  : /* outputs */
++                  : /* inputs */ "r" (src), "r" (dst)
++                  : /* clobbered */ "g5", "g6", "g7" );
++          }
++
++          /* Store the message length to indicate that I've finished */
++          ((EP_RXD_MAIN *) rxd->RxdMain)->Len = rxd->Data.nmd_len;                                    /* PCI write  */
++          
++          EP3_SPINEXIT(&rcvrElan->ThreadLock, &rcvrMain->ThreadLock);
++
++      consume_envelope:
++          /* Sample the queue full bit *BEFORE* moving the fptr.
++           * Then only clear it if it was full before, otherwise,
++           * as soon as the fptr is moved on the queue could fill 
++           * up, and so clearing it could mark a full queue as 
++           * empty.
++           *
++           * While the full bit is set, the queue is in a 'steady
++           * state', so it is safe to set the q_state
++           * 
++           */
++          if (((tmp = q->q_state) & E3_QUEUE_FULL) == 0)
++              q->q_fptr = nfptr;                              /* update queue */
++          else
++          {
++              q->q_fptr = nfptr;                              /* update queue */
++              q->q_state = tmp &~E3_QUEUE_FULL;               /* and clear full flag */
++          }
++
++          count++;                                            /* bump message count */
++          if (nfptr == q->q_top)                              /* queue wrap */
++              nfptr = q->q_base;
++          else
++              nfptr += q->q_size;
++
++          c_break_busywait();                                 /* be nice              HALT POINT */
++
++      } while (nfptr != q->q_bptr);                           /* loop until Fptr == Bptr */
++    }
++}
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/ep/epcomms_elan4.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/epcomms_elan4.c      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/epcomms_elan4.c   2005-05-11 12:10:12.487925128 -0400
+@@ -0,0 +1,392 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: epcomms_elan4.c,v 1.11.2.1 2004/10/28 11:53:28 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/epcomms_elan4.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++#include <elan/epsvc.h>
++#include <elan/epcomms.h>
++
++#include "debug.h"
++#include "kcomm_elan4.h"
++#include "epcomms_elan4.h"
++
++static void
++ep4comms_flush_interrupt (EP4_RAIL *rail, void *arg)
++{
++    EP4_COMMS_RAIL *commsRail = (EP4_COMMS_RAIL *) arg;
++    unsigned long  flags;
++
++    spin_lock_irqsave (&commsRail->r_flush_lock, flags);
++    commsRail->r_flush_count = 0;
++    kcondvar_wakeupall (&commsRail->r_flush_sleep, &commsRail->r_flush_lock);
++    spin_unlock_irqrestore  (&commsRail->r_flush_lock, flags);
++}
++
++void
++ep4comms_flush_start (EP4_COMMS_RAIL *commsRail)
++{
++    kmutex_lock (&commsRail->r_flush_mutex);
++}
++
++void
++ep4comms_flush_wait (EP4_COMMS_RAIL *commsRail)
++{
++    unsigned long flags;
++
++    ep4_wait_event_cmd (commsRail->r_flush_mcq, 
++                      commsRail->r_elan_addr + offsetof (EP4_COMMS_RAIL_ELAN, r_flush_event),
++                      E4_EVENT_INIT_VALUE (-32 * commsRail->r_flush_count, E4_EVENT_WRITE, E4_EVENT_DTYPE_LONG, 0),
++                      commsRail->r_flush_ecq->ecq_addr, 
++                      INTERRUPT_CMD | (commsRail->r_flush_intcookie.int_val << E4_MAIN_INT_SHIFT));
++
++    spin_lock_irqsave (&commsRail->r_flush_lock, flags);
++    while (commsRail->r_flush_count != 0)
++      kcondvar_wait (&commsRail->r_flush_sleep, &commsRail->r_flush_lock, &flags);
++    spin_unlock_irqrestore (&commsRail->r_flush_lock, flags);
++    
++    kmutex_unlock (&commsRail->r_flush_mutex);
++}
++
++void
++ep4comms_flush_setevent (EP4_COMMS_RAIL *commsRail, ELAN4_CQ *cq)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&commsRail->r_flush_lock, flags);
++
++    elan4_set_event_cmd (cq, commsRail->r_elan_addr + offsetof (EP4_COMMS_RAIL_ELAN, r_flush_event));
++    
++    commsRail->r_flush_count++;
++    
++    spin_unlock_irqrestore (&commsRail->r_flush_lock, flags);
++}
++
++void
++ep4comms_flush_callback (void *arg, statemap_t *map)
++{
++    EP4_COMMS_RAIL   *commsRail = (EP4_COMMS_RAIL *) arg;
++    EP_COMMS_SUBSYS  *subsys    = commsRail->r_generic.Subsys;
++    EP4_RAIL       *rail      = (EP4_RAIL *) commsRail->r_generic.Rail;
++    unsigned int     rnum       = rail->r_generic.Number;
++    struct list_head *el;
++
++    /*
++     * We stall the retry thread from CB_FLUSH_FILTERING until
++     * we've finished CB_FLUSH_FLUSHING to ensure that sten 
++     * packets can not be being retried while we flush them
++     * through.
++     */
++    switch (rail->r_generic.CallbackStep)
++    {
++    case EP_CB_FLUSH_FILTERING:
++      ep_kthread_stall (&rail->r_retry_thread);
++
++      ep4comms_flush_start (commsRail);
++      break;
++
++    case EP_CB_FLUSH_FLUSHING:
++      break;
++    }
++
++    kmutex_lock (&subsys->Lock);
++    list_for_each (el, &subsys->Transmitters) {
++      EP_XMTR *xmtr = list_entry (el, EP_XMTR, Link);
++      
++      if (xmtr->Rails[rnum])
++          ep4xmtr_flush_callback (xmtr, (EP4_XMTR_RAIL *) xmtr->Rails[rnum]);
++    }
++
++    list_for_each (el, &subsys->Receivers) {
++      EP_RCVR *rcvr = list_entry (el, EP_RCVR, Link);
++      
++      if (rcvr->Rails[rnum])
++          ep4rcvr_flush_callback (rcvr, (EP4_RCVR_RAIL *) rcvr->Rails[rnum]);
++    }
++    kmutex_unlock (&subsys->Lock);
++
++    switch (rail->r_generic.CallbackStep)
++    {
++    case EP_CB_FLUSH_FILTERING:
++      ep4comms_flush_wait (commsRail);
++      break;
++
++    case EP_CB_FLUSH_FLUSHING:
++      ep_kthread_resume (&rail->r_retry_thread);
++      break;
++    }
++}
++
++void
++ep4comms_failover_callback (void *arg, statemap_t *map)
++{
++    EP_COMMS_RAIL    *commsRail = (EP_COMMS_RAIL *) arg;
++    EP_COMMS_SUBSYS  *subsys    = commsRail->Subsys;
++    unsigned int     rnum       = commsRail->Rail->Number;
++    struct list_head *el;
++
++    kmutex_lock (&subsys->Lock);
++    list_for_each (el, &subsys->Transmitters) {
++      EP_XMTR *xmtr = list_entry (el, EP_XMTR, Link);
++      
++      if (xmtr->Rails[rnum])
++          ep4xmtr_failover_callback (xmtr, (EP4_XMTR_RAIL *) xmtr->Rails[rnum]);
++    }
++
++    list_for_each (el, &subsys->Receivers) {
++      EP_RCVR *rcvr = list_entry (el, EP_RCVR, Link);
++      
++      if (rcvr->Rails[rnum])
++          ep4rcvr_failover_callback (rcvr, (EP4_RCVR_RAIL *) rcvr->Rails[rnum]);
++    }
++    kmutex_unlock (&subsys->Lock);
++}
++
++void
++ep4comms_disconnect_callback (void *arg, statemap_t *map)
++{
++    EP_COMMS_RAIL    *commsRail = (EP_COMMS_RAIL *) arg;
++    EP_COMMS_SUBSYS  *subsys    = commsRail->Subsys;
++    unsigned int     rnum       = commsRail->Rail->Number;
++    struct list_head *el;
++
++    kmutex_lock (&subsys->Lock);
++    list_for_each (el, &subsys->Transmitters) {
++      EP_XMTR *xmtr = list_entry (el, EP_XMTR, Link);
++      
++      if (xmtr->Rails[rnum])
++          ep4xmtr_disconnect_callback (xmtr, (EP4_XMTR_RAIL *) xmtr->Rails[rnum]);
++    }
++
++    list_for_each (el, &subsys->Receivers) {
++      EP_RCVR *rcvr = list_entry (el, EP_RCVR, Link);
++      
++      if (rcvr->Rails[rnum])
++          ep4rcvr_disconnect_callback (rcvr, (EP4_RCVR_RAIL *) rcvr->Rails[rnum]);
++    }
++    kmutex_unlock (&subsys->Lock);
++}
++
++void
++ep4comms_neterr_callback (EP4_RAIL *rail, void *arg, unsigned int nodeId, EP_NETERR_COOKIE *cookies)
++{
++    EP_COMMS_RAIL    *commsRail = (EP_COMMS_RAIL *) arg;
++    EP_COMMS_SUBSYS  *subsys    = commsRail->Subsys;
++    unsigned int     rnum       = commsRail->Rail->Number;
++    struct list_head *el;
++    
++    /* First - stall the retry thread, so that it will no longer restart 
++     *         any sten packets from the retry lists */
++    ep_kthread_stall (&rail->r_retry_thread);
++
++    ep4comms_flush_start ((EP4_COMMS_RAIL *) commsRail);
++
++    /* Second - flush through all command queues for xmtrs and rcvrs */
++    kmutex_lock (&subsys->Lock);
++    list_for_each (el, &subsys->Transmitters) {
++      EP_XMTR *xmtr = list_entry (el, EP_XMTR, Link);
++      
++      if (xmtr->Rails[rnum])
++          ep4xmtr_neterr_flush (xmtr, (EP4_XMTR_RAIL *) xmtr->Rails[rnum], nodeId, cookies);
++    }
++    
++    list_for_each (el, &subsys->Receivers) {
++      EP_RCVR *rcvr = list_entry (el, EP_RCVR, Link);
++      
++      if (rcvr->Rails[rnum])
++          ep4rcvr_neterr_flush (rcvr, (EP4_RCVR_RAIL *) rcvr->Rails[rnum], nodeId, cookies);
++    }
++    kmutex_unlock (&subsys->Lock);
++
++    /* Third - wait for flush to complete */
++    ep4comms_flush_wait ((EP4_COMMS_RAIL *) commsRail);
++    
++    /* Fourth - flush through all command queues */
++    ep4_flush_ecqs (rail);
++    
++    /* Fifth - search all the retry lists for the network error cookies */
++    kmutex_lock (&subsys->Lock);
++    list_for_each (el, &subsys->Transmitters) {
++      EP_XMTR *xmtr = list_entry (el, EP_XMTR, Link);
++      
++      if (xmtr->Rails[rnum])
++          ep4xmtr_neterr_check (xmtr, (EP4_XMTR_RAIL *) xmtr->Rails[rnum], nodeId, cookies);
++    }
++
++    list_for_each (el, &subsys->Receivers) {
++      EP_RCVR *rcvr = list_entry (el, EP_RCVR, Link);
++      
++      if (rcvr->Rails[rnum])
++          ep4rcvr_neterr_check (rcvr, (EP4_RCVR_RAIL *) rcvr->Rails[rnum], nodeId, cookies);
++    }
++    kmutex_unlock (&subsys->Lock);
++
++    ep_kthread_resume (&rail->r_retry_thread);
++}
++
++
++EP_COMMS_RAIL *
++ep4comms_add_rail (EP_SUBSYS *s, EP_SYS *sys, EP_RAIL *r)
++{
++    EP4_RAIL       *rail = (EP4_RAIL *)r;
++    ELAN4_DEV      *dev  = rail->r_ctxt.ctxt_dev;
++    EP4_COMMS_RAIL *commsRail;
++    E4_InputQueue   qdesc;
++    int i;
++
++    KMEM_ZALLOC (commsRail, EP4_COMMS_RAIL *,sizeof (EP4_COMMS_RAIL), 1);
++
++    if (commsRail == NULL)
++      return NULL;
++    
++    commsRail->r_generic.Ops.DelRail          = ep4comms_del_rail;
++    commsRail->r_generic.Ops.DisplayRail      = ep4comms_display_rail;
++    commsRail->r_generic.Ops.Rcvr.AddRail     = ep4rcvr_add_rail;
++    commsRail->r_generic.Ops.Rcvr.DelRail     = ep4rcvr_del_rail;
++    commsRail->r_generic.Ops.Rcvr.Check       = ep4rcvr_check;
++    commsRail->r_generic.Ops.Rcvr.QueueRxd    = ep4rcvr_queue_rxd;
++    commsRail->r_generic.Ops.Rcvr.RpcPut      = ep4rcvr_rpc_put;
++    commsRail->r_generic.Ops.Rcvr.RpcGet      = ep4rcvr_rpc_get;
++    commsRail->r_generic.Ops.Rcvr.RpcComplete = ep4rcvr_rpc_complete;
++
++    commsRail->r_generic.Ops.Rcvr.StealRxd    = ep4rcvr_steal_rxd;
++
++    commsRail->r_generic.Ops.Rcvr.DisplayRcvr = ep4rcvr_display_rcvr;
++    commsRail->r_generic.Ops.Rcvr.DisplayRxd  = ep4rcvr_display_rxd;
++
++    commsRail->r_generic.Ops.Rcvr.FillOutRailStats = ep4rcvr_fillout_rail_stats;
++
++    commsRail->r_generic.Ops.Xmtr.AddRail     = ep4xmtr_add_rail;
++    commsRail->r_generic.Ops.Xmtr.DelRail     = ep4xmtr_del_rail;
++    commsRail->r_generic.Ops.Xmtr.Check       = ep4xmtr_check;
++    commsRail->r_generic.Ops.Xmtr.BindTxd     = ep4xmtr_bind_txd;
++    commsRail->r_generic.Ops.Xmtr.UnbindTxd   = ep4xmtr_unbind_txd;
++    commsRail->r_generic.Ops.Xmtr.PollTxd     = ep4xmtr_poll_txd;
++    commsRail->r_generic.Ops.Xmtr.CheckTxdState = ep4xmtr_check_txd_state;
++
++    commsRail->r_generic.Ops.Xmtr.DisplayXmtr = ep4xmtr_display_xmtr;
++    commsRail->r_generic.Ops.Xmtr.DisplayTxd  = ep4xmtr_display_txd;
++
++    commsRail->r_generic.Ops.Xmtr.FillOutRailStats = ep4xmtr_fillout_rail_stats;
++
++    /* Allocate command queue space for flushing (1 dword for interrupt + 4 dwords for waitevent) */
++    if ((commsRail->r_flush_ecq = ep4_get_ecq (rail, EP4_ECQ_EVENT, 1)) == NULL)
++    {
++      KMEM_FREE (commsRail, sizeof (EP4_COMMS_RAIL));
++      return NULL;
++    }
++
++    if ((commsRail->r_flush_mcq = ep4_get_ecq (rail, EP4_ECQ_MAIN, 4)) == NULL)
++    {
++      ep4_put_ecq (rail, commsRail->r_flush_ecq, 1);
++      KMEM_FREE (commsRail, sizeof (EP4_COMMS_RAIL));
++      return NULL;
++    }
++
++    /* Allocate and initialise the elan memory part */
++    if ((commsRail->r_elan = ep_alloc_elan (r, EP4_COMMS_RAIL_ELAN_SIZE, 0, &commsRail->r_elan_addr)) == (sdramaddr_t) 0)
++    {
++      ep4_put_ecq (rail, commsRail->r_flush_mcq, 4);
++      ep4_put_ecq (rail, commsRail->r_flush_ecq, 1);
++      KMEM_FREE (commsRail, sizeof (EP4_COMMS_RAIL));
++      return NULL;
++    }
++
++    ep4_register_intcookie (rail, &commsRail->r_flush_intcookie, commsRail->r_elan_addr + offsetof (EP4_COMMS_RAIL_ELAN, r_flush_event),
++                          ep4comms_flush_interrupt, commsRail);
++
++    elan4_sdram_writeq (dev, commsRail->r_elan + offsetof (EP4_COMMS_RAIL_ELAN, r_flush_event.ev_CountAndType),
++                      E4_EVENT_INIT_VALUE (0, E4_EVENT_WRITE, E4_EVENT_DTYPE_LONG, 0));
++
++
++    /* Allocate and initialise all the queue desriptors as "full" with no event */
++    if ((commsRail->r_descs = ep_alloc_memory_elan (r, EP_EPCOMMS_QUEUE_BASE, roundup (EP_MSG_NSVC * EP_QUEUE_DESC_SIZE, SDRAM_PAGE_SIZE), EP_PERM_ALL, 0)) == (sdramaddr_t) 0)
++    {
++      ep_free_elan (r, commsRail->r_elan_addr, EP4_COMMS_RAIL_ELAN_SIZE);
++      ep4_put_ecq (rail, commsRail->r_flush_mcq, 4);
++      ep4_put_ecq (rail, commsRail->r_flush_ecq, 1);
++      KMEM_FREE (commsRail, sizeof (EP4_COMMS_RAIL));
++      return NULL;
++    }
++
++    qdesc.q_bptr    = 0;
++    qdesc.q_fptr    = 8;
++    qdesc.q_control = E4_InputQueueControl (qdesc.q_bptr,qdesc.q_fptr, 8);
++    qdesc.q_event   = 0;
++
++    for (i = 0; i < EP_MSG_NSVC; i++)
++      elan4_sdram_copyq_to_sdram (rail->r_ctxt.ctxt_dev, &qdesc, commsRail->r_descs + (i * EP_QUEUE_DESC_SIZE),
++                                  sizeof (E4_InputQueue));
++
++    kmutex_init (&commsRail->r_flush_mutex);
++    spin_lock_init (&commsRail->r_flush_lock);
++    kcondvar_init (&commsRail->r_flush_sleep);
++
++    ep_register_callback (r, EP_CB_FLUSH_FILTERING, ep4comms_flush_callback,      commsRail);
++    ep_register_callback (r, EP_CB_FLUSH_FLUSHING,  ep4comms_flush_callback,      commsRail);
++    ep_register_callback (r, EP_CB_FAILOVER,        ep4comms_failover_callback,   commsRail);
++    ep_register_callback (r, EP_CB_DISCONNECTING,   ep4comms_disconnect_callback, commsRail);
++
++    commsRail->r_neterr_ops.op_func = ep4comms_neterr_callback;
++    commsRail->r_neterr_ops.op_arg  = commsRail;
++    
++    ep4_add_neterr_ops (rail, &commsRail->r_neterr_ops);
++
++    return (EP_COMMS_RAIL *) commsRail;
++}
++
++void
++ep4comms_del_rail (EP_COMMS_RAIL *r)
++{
++    EP4_COMMS_RAIL *commsRail = (EP4_COMMS_RAIL *) r;
++    EP4_RAIL       *rail      = (EP4_RAIL *) commsRail->r_generic.Rail;
++
++    ep_remove_callback (&rail->r_generic, EP_CB_FLUSH_FILTERING, ep4comms_flush_callback,      commsRail);
++    ep_remove_callback (&rail->r_generic, EP_CB_FLUSH_FLUSHING,  ep4comms_flush_callback,      commsRail);
++    ep_remove_callback (&rail->r_generic, EP_CB_FAILOVER,        ep4comms_failover_callback,   commsRail);
++    ep_remove_callback (&rail->r_generic, EP_CB_DISCONNECTING,   ep4comms_disconnect_callback, commsRail);
++
++    kcondvar_destroy (&commsRail->r_flush_sleep);
++    spin_lock_destroy (&commsRail->r_flush_lock);
++    kmutex_destroy (&commsRail->r_flush_mutex);
++
++    ep_free_memory_elan (&rail->r_generic, EP_EPCOMMS_QUEUE_BASE);
++    ep_free_elan (&rail->r_generic, commsRail->r_elan_addr, EP4_COMMS_RAIL_ELAN_SIZE);
++
++    ep4_deregister_intcookie (rail, &commsRail->r_flush_intcookie);
++
++    ep4_put_ecq (rail, commsRail->r_flush_mcq, 4);
++    ep4_put_ecq (rail, commsRail->r_flush_ecq, 1);
++
++    KMEM_FREE (commsRail, sizeof (EP4_COMMS_RAIL));
++}
++
++void
++ep4comms_display_rail (EP_COMMS_RAIL *r)
++{
++    EP4_COMMS_RAIL *commsRail = (EP4_COMMS_RAIL *) r;
++    EP4_RAIL       *rail      = (EP4_RAIL *) commsRail->r_generic.Rail;
++    ELAN4_DEV      *dev       = rail->r_ctxt.ctxt_dev;
++    
++    ep4_display_rail (rail);
++
++    ep_debugf (DBG_DEBUG, "   flush count=%d mcq=%p ecq=%p event %llx.%llx.%llx\n", 
++             commsRail->r_flush_count, commsRail->r_flush_mcq, commsRail->r_flush_ecq,
++             elan4_sdram_readq (dev, commsRail->r_elan + offsetof (EP4_COMMS_RAIL_ELAN, r_flush_event.ev_CountAndType)),
++             elan4_sdram_readq (dev, commsRail->r_elan + offsetof (EP4_COMMS_RAIL_ELAN, r_flush_event.ev_WritePtr)),
++             elan4_sdram_readq (dev, commsRail->r_elan + offsetof (EP4_COMMS_RAIL_ELAN, r_flush_event.ev_WriteValue)));
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/ep/epcomms_elan4.h
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/epcomms_elan4.h      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/epcomms_elan4.h   2005-05-11 12:10:12.488924976 -0400
+@@ -0,0 +1,470 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __EPCOMMS_ELAN4_H
++#define __EPCOMMS_ELAN4_H
++
++#ident "@(#)$Id: epcomms_elan4.h,v 1.13.2.1 2004/11/12 10:54:51 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/epcomms_elan4.h,v $ */
++
++
++#include <elan4/types.h>
++
++/*
++ * Elan4 spinlocks are a pair of 64 bit words, one in elan sdram and one in main memory
++ * the sdram word holds the thread sequence number in the bottom 32 bits and the main
++ * lock in the top 32 bits.  The main memory word holds the sequence number only in
++ * it's bottom 32 bits */
++
++typedef volatile E4_uint64 EP4_SPINLOCK_MAIN;
++typedef volatile E4_uint64 EP4_SPINLOCK_ELAN;
++
++#define EP4_SPINLOCK_SEQ      0
++#define EP4_SPINLOCK_MLOCK    4
++
++#if defined(__elan4__)
++
++#define EP4_SPINENTER(CPORT,SLE,SLM) \
++do { \
++    register long tmp; \
++\
++    asm volatile ("ld4                [%1], %0\n" \
++                "inc          %0\n" \
++                "st4          %0, [%1]\n" \
++                "ld4          [%1 + 4], %0\n" \
++                "srl8,byte    %0, 4, %0\n" \
++                : /* outputs */ "=r" (tmp)  \
++                : /* inputs */ "r" (SLE), "r" (SLM)); \
++\
++    if (tmp) \
++      ep4_spinblock (CPORT,SLE, SLM); \
++} while (0)
++
++extern void ep4_spinblock(E4_uint64 *cport, EP4_SPINLOCK_ELAN *sle, EP4_SPINLOCK_MAIN *slm);
++
++#define EP4_SPINEXIT(CPORT,SLE,SLM) \
++do { \
++    register long tmp; \
++\
++    asm volatile ("ld4                [%1], %0\n" \
++                "st4          %0, [%2]\n" \
++                : /* outputs */ "=r" (tmp) \
++                : /* inputs */ "r" (SLE), "r" (SLM)); \
++} while (0)
++
++#else
++
++#define EP4_SPINENTER(DEV,SLE,SLM) \
++do { \
++    uint32_t seq; \
++\
++    mb(); \
++    elan4_sdram_writel (DEV, (SLE) + EP4_SPINLOCK_MLOCK, 1); \
++    mb(); \
++    while ((seq = elan4_sdram_readl (DEV, (SLE) + EP4_SPINLOCK_SEQ)) != *((uint32_t *) (SLM))) \
++    { \
++      while (*((uint32_t *) (SLM)) == (seq - 1)) \
++      { \
++          mb(); \
++          DELAY(1); \
++      } \
++    } \
++} while (0)
++
++#define EP4_SPINEXIT(DEV,SLE,SLM) \
++do { \
++    wmb(); \
++    elan4_sdram_writel (DEV, (SLE) + EP4_SPINLOCK_MLOCK, 0); \
++} while (0)
++
++#endif /* !defined(__elan4__) */
++
++#define EP4_STEN_RETRYCOUNT   16
++#define EP4_DMA_RETRYCOUNT    16
++
++typedef struct ep4_intr_cmd
++{
++    E4_uint64         c_write_cmd;
++    E4_uint64         c_write_value;
++    E4_uint64         c_intr_cmd;
++} EP4_INTR_CMD;
++
++#define       EP4_INTR_CMD_NDWORDS    (sizeof (EP4_INTR_CMD) / 8)
++
++typedef struct ep4_rxd_sten_cmd
++{
++    E4_uint64         c_open;
++
++    E4_uint64         c_trans;
++    E4_uint64         c_cookie;
++    E4_uint64         c_dma_typeSize;
++    E4_uint64         c_dma_cookie;
++    E4_uint64         c_dma_vproc;
++    E4_uint64         c_dma_srcAddr;
++    E4_uint64         c_dma_dstAddr;
++    E4_uint64         c_dma_srcEvent;
++    E4_uint64         c_dma_dstEvent;
++
++    E4_uint64         c_ok_guard;
++    E4_uint64         c_ok_write_cmd;
++    E4_uint64         c_ok_write_value;
++    
++    E4_uint64         c_fail_guard;
++    E4_uint64         c_fail_setevent;
++
++    E4_uint64         c_nop_cmd;
++} EP4_RXD_STEN_CMD;
++
++#define EP4_RXD_STEN_CMD_NDWORDS      (sizeof (EP4_RXD_STEN_CMD) / 8)
++
++typedef struct ep4_rxd_dma_cmd
++{
++    E4_uint64         c_dma_typeSize;
++    E4_uint64         c_dma_cookie;
++    E4_uint64         c_dma_vproc;
++    E4_uint64         c_dma_srcAddr;
++    E4_uint64         c_dma_dstAddr;
++    E4_uint64         c_dma_srcEvent;
++    E4_uint64         c_dma_dstEvent;
++    E4_uint64         c_nop_cmd;
++} EP4_RXD_DMA_CMD;
++
++#define EP4_RXD_DMA_CMD_NDWORDS               (sizeof (EP4_RXD_DMA_CMD) / 8)
++#define EP4_RXD_START_CMD_NDWORDS     (sizeof (E4_ThreadRegs) / 8)
++
++typedef struct ep4_rxd_rail_elan
++{
++    EP4_RXD_STEN_CMD    rxd_sten[EP_MAXFRAG+1];
++
++    EP4_INTR_CMD      rxd_done_cmd;                           /* command stream issued by done event (aligned to 64 bytes) */
++    E4_Addr           rxd_next;                               /* linked list when on pending list (pad to 32 bytes)*/
++    E4_Event32                rxd_failed;                             /* event set when sten packet fails */
++
++    EP4_INTR_CMD        rxd_failed_cmd;                               /* command stream issued by fail event (aligned to 64 bytes) */
++    E4_uint64         rxd_queued;                             /* rxd queuing thread has executed (pad to 32 bytes)*/
++
++    E4_Event32                rxd_start;                              /* event to set to fire off and event chain (used as chain[0]) */
++    E4_Event32                rxd_chain[EP_MAXFRAG];                  /* chained events (aligned to 32 bytes) */
++    E4_Event32                rxd_done;                               /* event to fire done command stream causing interrupt (used as chain[EP_MAXFRAG]) */
++
++    E4_Addr           rxd_rxd;                                /* elan address of EP4_RXD_MAIN */
++    E4_Addr           rxd_main;                               /* elan address of EP4_RXD_RAIL_MAIN */
++    E4_uint64         rxd_debug;                              /* thread debug value */
++
++    EP_NMD            rxd_buffer;                             /* Network mapping descriptor for receive data */
++} EP4_RXD_RAIL_ELAN;
++
++#define EP4_RXD_RAIL_ELAN_SIZE        roundup(sizeof (EP4_RXD_RAIL_ELAN), 64)
++
++typedef struct ep4_rxd_rail_main
++{
++    E4_uint64         rxd_sent[EP_MAXFRAG+1];                 /* sten packet sent */
++    E4_uint64         rxd_failed;                             /* sten packet failed */
++    E4_uint64         rxd_done;                               /* operation complete */
++
++    E4_Addr           rxd_scq;                                /* command port for scq */
++} EP4_RXD_RAIL_MAIN;
++
++#define EP4_RXD_RAIL_MAIN_SIZE        roundup(sizeof (EP4_RXD_RAIL_MAIN), 8)
++
++#if !defined(__elan4__)
++typedef struct ep4_rxd_rail
++{
++    EP_RXD_RAIL               rxd_generic;
++
++    struct list_head    rxd_retry_link;
++    unsigned long       rxd_retry_time;
++
++    EP4_INTCOOKIE     rxd_intcookie;
++
++    sdramaddr_t               rxd_elan;
++    EP_ADDR           rxd_elan_addr;
++    
++    EP4_RXD_RAIL_MAIN  *rxd_main;
++    EP_ADDR           rxd_main_addr;
++
++    EP4_ECQ          *rxd_ecq;                                /* cq with 128 bytes targetted by event */
++    EP4_ECQ          *rxd_scq;                                /* cq with 8 bytes targetted by main/thread store */
++} EP4_RXD_RAIL;
++
++#define EP4_NUM_RXD_PER_BLOCK 16
++
++typedef struct ep4_rxd_rail_block
++{
++    struct list_head  blk_link;
++    EP4_RXD_RAIL      blk_rxds[EP4_NUM_RXD_PER_BLOCK];
++} EP4_RXD_RAIL_BLOCK;
++
++#endif /* !defined(__elan4__) */
++
++typedef struct ep4_rcvr_rail_elan
++{
++    E4_uint64         rcvr_thread_stall[8];                   /* place for thread to stall */
++    E4_Event32                rcvr_qevent;                            /* Input queue event */
++    E4_Event32                rcvr_thread_halt;                       /* place for thread to halt */
++
++    volatile E4_Addr    rcvr_pending_tailp;                   /* list of pending rxd's (elan addr) */
++    volatile E4_Addr  rcvr_pending_head;                      /*   -- this pair aligned to 16 bytes */
++
++    EP4_SPINLOCK_ELAN rcvr_thread_lock;                       /* spinlock for thread processing loop */
++
++    E4_uint64         rcvr_stall_intcookie;                   /* interrupt cookie to use when requseted to halt */
++
++    E4_uint64         rcvr_qbase;                             /* base of input queue */
++    E4_uint64         rcvr_qlast;                             /* last item in input queue */
++
++    E4_uint64         rcvr_debug;                             /* thread debug value */
++} EP4_RCVR_RAIL_ELAN;
++
++typedef struct ep4_rcvr_rail_main
++{
++    EP4_SPINLOCK_MAIN   rcvr_thread_lock;                     /* spinlock for thread processing loop */
++} EP4_RCVR_RAIL_MAIN;
++
++#if !defined(__elan4__)
++
++typedef struct ep4_rcvr_rail_stats
++{
++    unsigned long some_stat;
++} EP4_RCVR_RAIL_STATS;
++
++typedef struct ep4_rcvr_rail
++{
++    EP_RCVR_RAIL      rcvr_generic;                           /* generic portion */
++    
++    sdramaddr_t               rcvr_elan;
++    EP_ADDR           rcvr_elan_addr;
++
++    EP4_RCVR_RAIL_MAIN *rcvr_main;
++    EP_ADDR           rcvr_main_addr;
++
++    sdramaddr_t               rcvr_slots;                             /* input queue slots */
++    EP_ADDR           rcvr_slots_addr;                        /*   and elan address */
++
++    EP_ADDR           rcvr_stack;                             /* stack for thread */
++
++    EP4_ECQ          *rcvr_ecq;                               /* command queue space for thread STEN packets */
++    EP4_ECQ          *rcvr_resched;                           /* command queue space to reschedule the thread */
++
++    struct list_head    rcvr_freelist;                                /* freelist of per-rail receive descriptors */
++    unsigned int        rcvr_freecount;                               /*   and number on free list */
++    unsigned int        rcvr_totalcount;                              /*   total number created */
++    spinlock_t          rcvr_freelock;                                /*   and lock for free list */
++    struct list_head    rcvr_blocklist;                               /* list of receive descriptor blocks */
++
++    unsigned int        rcvr_freewaiting;                     /* waiting for descriptors to be freed */
++    kcondvar_t                rcvr_freesleep;                         /*   and sleep here */
++
++    EP4_INTCOOKIE     rcvr_stall_intcookie;                   /* interrupt cookie for thread halt */
++    unsigned char     rcvr_thread_halted;                     /* thread has been halted */
++    unsigned char       rcvr_cleanup_waiting;                 /* waiting for cleanup */
++    kcondvar_t          rcvr_cleanup_sleep;                   /*   and sleep here */
++
++    EP4_RETRY_OPS     rcvr_retryops;
++
++    struct list_head    rcvr_retrylist;                               /* list of txd's to retry envelopes for */
++    struct list_head    rcvr_polllist;                                /* list of txd's to poll for completion */
++    spinlock_t          rcvr_retrylock;
++    
++    EP4_RCVR_RAIL_STATS rcvr_stats;                             /* elan4 specific rcvr_rail stats */
++
++} EP4_RCVR_RAIL;
++
++#endif /* !defined(__elan4__) */
++
++typedef struct ep4_txd_rail_elan
++{
++    EP4_INTR_CMD        txd_env_cmd;                          /* command stream for envelope event (64 byte aligned) */
++    E4_uint64         txd_pad0;                               /*  pad to 32 bytes */
++    E4_Event32                txd_env;                                /* event set when STEN packet fails */
++
++    EP4_INTR_CMD      txd_done_cmd;                           /* command stream for done event (64 byte aligned) */
++    E4_uint64         txd_pad1;                               /*  pad to 32 bytes */
++    E4_Event32                txd_done;                               /* event set when transmit complete */
++
++    E4_Event32                txd_data;                               /* event set when xmit completes (=> phase becomes passive) */
++} EP4_TXD_RAIL_ELAN;
++
++#define EP4_TXD_RAIL_ELAN_SIZE                roundup(sizeof(EP4_TXD_RAIL_ELAN), 64)
++
++typedef struct ep4_txd_rail_main
++{
++    E4_uint64         txd_env;
++    E4_uint64         txd_data;
++    E4_uint64         txd_done;
++} EP4_TXD_RAIL_MAIN;
++
++#define EP4_TXD_RAIL_MAIN_SIZE                roundup(sizeof(EP4_TXD_RAIL_MAIN), 8)
++
++#if !defined (__elan4__)
++typedef struct ep4_txd_rail
++{
++    EP_TXD_RAIL               txd_generic;
++
++    struct list_head    txd_retry_link;
++    unsigned long     txd_retry_time;
++
++    EP4_INTCOOKIE     txd_intcookie;
++
++    sdramaddr_t               txd_elan;
++    EP_ADDR           txd_elan_addr;
++    
++    EP4_TXD_RAIL_MAIN  *txd_main;
++    EP_ADDR           txd_main_addr;
++
++    EP4_ECQ          *txd_ecq;
++
++    E4_uint64         txd_cookie;
++} EP4_TXD_RAIL;
++
++#define EP4_NUM_TXD_PER_BLOCK 21
++
++typedef struct ep4_txd_rail_block
++{
++    struct list_head  blk_link;
++    EP4_TXD_RAIL      blk_txds[EP4_NUM_TXD_PER_BLOCK];
++} EP4_TXD_RAIL_BLOCK;
++
++typedef struct ep4_xmtr_rail_main
++{
++    E4_int64          xmtr_flowcnt;
++} EP4_XMTR_RAIL_MAIN;
++
++typedef struct ep4_xmtr_rail_stats
++{
++    unsigned long some_stat;
++} EP4_XMTR_RAIL_STATS;
++
++#define EP4_TXD_LIST_POLL     0
++#define EP4_TXD_LIST_STALLED  1
++#define EP4_TXD_LIST_RETRY    2
++#define EP4_TXD_NUM_LISTS     3
++typedef struct ep4_xmtr_rail
++{
++    EP_XMTR_RAIL      xmtr_generic;
++
++    EP4_XMTR_RAIL_MAIN *xmtr_main;
++    EP_ADDR           xmtr_main_addr;
++
++    struct list_head    xmtr_freelist;
++    unsigned int        xmtr_freecount;
++    unsigned int        xmtr_totalcount;
++    spinlock_t          xmtr_freelock;
++    struct list_head    xmtr_blocklist;
++    unsigned int        xmtr_freewaiting;
++    kcondvar_t                xmtr_freesleep;
++
++    EP4_INTCOOKIE     xmtr_intcookie;                         /* interrupt cookie for "polled" descriptors */
++
++    ELAN4_CQ           *xmtr_cq;
++    E4_int64          xmtr_flowcnt;
++
++    EP4_RETRY_OPS     xmtr_retryops;
++
++    struct list_head    xmtr_retrylist[EP4_TXD_NUM_LISTS];    /* list of txd's to retry envelopes for */
++    struct list_head    xmtr_polllist;                                /* list of txd's to poll for completion */
++    spinlock_t          xmtr_retrylock;
++
++    EP4_XMTR_RAIL_STATS stats;                                  /* elan4 specific xmtr rail stats */
++} EP4_XMTR_RAIL;
++
++#define EP4_XMTR_CQSIZE               CQ_Size64K                              /* size of command queue for xmtr */
++#define EP4_XMTR_FLOWCNT      (CQ_Size(EP4_XMTR_CQSIZE) / 512)        /* # of STEN packets which can fit in */
++
++typedef struct ep4_comms_rail_elan
++{
++    E4_Event32                r_flush_event;
++} EP4_COMMS_RAIL_ELAN;
++
++#define EP4_COMMS_RAIL_ELAN_SIZE      roundup(sizeof (EP4_COMMS_RAIL_ELAN), 32)
++
++typedef struct ep4_comms_rail
++{
++    EP_COMMS_RAIL     r_generic;                              /* generic comms rail */
++    sdramaddr_t               r_descs;                                /* input queue descriptors */
++
++    sdramaddr_t               r_elan;                                 /* elan portion */
++    EP_ADDR           r_elan_addr;
++
++    kmutex_t          r_flush_mutex;                          /* sequentialise flush usage */
++    EP4_INTCOOKIE       r_flush_intcookie;                    /* interrupt cookie to generate */
++
++    kcondvar_t                r_flush_sleep;                          /* place to sleep waiting */
++    spinlock_t                r_flush_lock;                           /*   and spinlock to use */
++
++    unsigned int      r_flush_count;                          /* # setevents issued */
++    EP4_ECQ          *r_flush_ecq;                            /* command queue for interrupt */
++    EP4_ECQ          *r_flush_mcq;                            /* command queeu to issue waitevent */
++
++    EP4_NETERR_OPS      r_neterr_ops;                         /* network error fixup ops */
++} EP4_COMMS_RAIL;
++
++/* epcommsTx_elan4.c */
++extern void           ep4xmtr_flush_callback (EP_XMTR *xmtr, EP4_XMTR_RAIL *xmtrRail);
++extern void           ep4xmtr_failover_callback (EP_XMTR *xmtr, EP4_XMTR_RAIL *xmtrRail);
++extern void           ep4xmtr_disconnect_callback (EP_XMTR *xmtr, EP4_XMTR_RAIL *xmtrRail);
++
++extern void         ep4xmtr_neterr_flush (EP_XMTR *xmtr, EP4_XMTR_RAIL *xmtrRail, unsigned int nodeId, EP_NETERR_COOKIE *cookies);
++extern void         ep4xmtr_neterr_check (EP_XMTR *xmtr, EP4_XMTR_RAIL *xmtrRail, unsigned int nodeId, EP_NETERR_COOKIE *cookies);
++
++/* epcommsRx_elan4.c */
++extern void           ep4rcvr_flush_callback (EP_RCVR *rcvr, EP4_RCVR_RAIL *rcvrRail);
++extern void           ep4rcvr_failover_callback (EP_RCVR *rcvr, EP4_RCVR_RAIL *rcvrRail);
++extern void           ep4rcvr_disconnect_callback (EP_RCVR *rcvr, EP4_RCVR_RAIL *rcvrRail);
++
++extern void         ep4rcvr_neterr_flush (EP_RCVR *rcvr, EP4_RCVR_RAIL *rcvrRail, unsigned int nodeId, EP_NETERR_COOKIE *cookies);
++extern void         ep4rcvr_neterr_check (EP_RCVR *rcvr, EP4_RCVR_RAIL *rcvrRail, unsigned int nodeId, EP_NETERR_COOKIE *cookies);
++
++/* epcomms_elan4.c */
++extern void           ep4comms_flush_start (EP4_COMMS_RAIL *commsRail);
++extern void           ep4comms_flush_wait (EP4_COMMS_RAIL *commsRail);
++extern void           ep4comms_flush_setevent (EP4_COMMS_RAIL *commsRail, ELAN4_CQ *cq);
++
++extern EP_COMMS_RAIL *ep4comms_add_rail (EP_SUBSYS *s, EP_SYS *sys, EP_RAIL *r);
++extern void           ep4comms_del_rail (EP_COMMS_RAIL *r);
++extern void         ep4comms_display_rail (EP_COMMS_RAIL *r);
++
++/* epcommsTx_elan4.c */
++extern int            ep4xmtr_bind_txd (EP_TXD *txd, EP_XMTR_RAIL *xmtrRail, unsigned int phase);
++extern void           ep4xmtr_unbind_txd (EP_TXD *txd, unsigned int phase);
++extern int            ep4xmtr_poll_txd (EP_XMTR_RAIL *xmtrRail, EP_TXD_RAIL *txdRail, int how);
++extern long           ep4xmtr_check (EP_XMTR_RAIL *xmtrRail, long nextRunTime);
++extern void           ep4xmtr_add_rail (EP_XMTR *xmtr, EP_COMMS_RAIL *commsRail);
++extern void           ep4xmtr_del_rail (EP_XMTR *xmtr, EP_COMMS_RAIL *commsRail);
++extern int            ep4xmtr_check_txd_state(EP_TXD *txd);
++
++extern void           ep4xmtr_display_xmtr (DisplayInfo *di, EP_XMTR_RAIL *xmtrRail);
++extern void           ep4xmtr_display_txd  (DisplayInfo *di, EP_TXD_RAIL *txdRail);
++
++extern void           ep4xmtr_fillout_rail_stats (EP_XMTR_RAIL *xmtr_rail, char *str);
++
++/* epcommsRx_elan4.c */
++extern int          ep4rcvr_queue_rxd (EP_RXD *rxd, EP_RCVR_RAIL *rcvrRail);
++extern void         ep4rcvr_rpc_put (EP_RXD *rxd, EP_NMD *local, EP_NMD *remote, unsigned nFrags);
++extern void         ep4rcvr_rpc_get (EP_RXD *rxd, EP_NMD *local, EP_NMD *remote, unsigned nFrags);
++extern void         ep4rcvr_rpc_complete (EP_RXD *rxd, EP_NMD *local, EP_NMD *remote, unsigned nFrags);
++
++extern EP_RXD      *ep4rcvr_steal_rxd (EP_RCVR_RAIL *rcvrRail);
++
++extern long         ep4rcvr_check (EP_RCVR_RAIL *rcvrRail, long nextRunTime);
++extern void           ep4rcvr_add_rail (EP_RCVR *rcvr, EP_COMMS_RAIL *rail);
++extern void           ep4rcvr_del_rail (EP_RCVR *rcvr, EP_COMMS_RAIL *rail);
++
++extern void           ep4rcvr_display_rcvr (DisplayInfo *di, EP_RCVR_RAIL *rcvrRail);
++extern void           ep4rcvr_display_rxd  (DisplayInfo *di, EP_RXD_RAIL *rxdRail);
++
++extern void           ep4rcvr_fillout_rail_stats (EP_RCVR_RAIL *rcvr_rail, char *str);
++
++#endif /* !defined(__elan4__) */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++#endif /* __EPCOMMS_ELAN4_H */
+Index: linux-2.6.5/drivers/net/qsnet/ep/epcomms_elan4_thread.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/epcomms_elan4_thread.c       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/epcomms_elan4_thread.c    2005-05-11 12:10:12.489924824 -0400
+@@ -0,0 +1,346 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: epcomms_elan4_thread.c,v 1.10.8.2 2004/09/28 10:36:51 david Exp $ $Name: QSNETMODULES-4-31_20050321 $"
++/*      $Source: /cvs/master/quadrics/epmod/epcomms_elan4_thread.c,v $*/
++
++//#include <qsnet/types.h>
++
++typedef char           int8_t;
++typedef unsigned char  uint8_t;
++typedef short          int16_t;
++typedef unsigned short uint16_t;
++typedef int            int32_t;
++typedef unsigned int   uint32_t;
++typedef long           int64_t;
++typedef unsigned long  uint64_t;
++
++#include <elan/nmh.h>
++#include <elan/kcomm.h>
++#include <elan/epcomms.h>
++
++#include <elan4/registers.h>
++
++#include "kcomm_vp.h"
++#include "kcomm_elan4.h"
++#include "epcomms_elan4.h"
++
++#include <elan4/trtype.h>
++
++/* assembler in epcomms_asm_elan4_thread.S */
++extern void               c_waitevent_interrupt (E4_uint64 *cport, E4_Event32 *event, E4_uint64 count, E4_uint64 intcookie);
++extern EP4_RXD_RAIL_ELAN *c_stall_thread (EP4_RCVR_RAIL_ELAN *rcvrRail);
++
++#define R32_to_R47            "%r32", "%r33", "%r34", "%r35", "%r36", "%r37", "%r38", "%r39", \
++                              "%r40", "%r41", "%r42", "%r43", "%r44", "%r45", "%r46", "%r47"
++#define R48_to_R63            "%r48", "%r49", "%r50", "%r51", "%r52", "%r53", "%r54", "%r55", \
++                              "%r56", "%r57", "%r58", "%r59", "%r60", "%r61", "%r62", "%r63"
++
++/* proto types for code in asm_elan4_thread.S */
++extern void c_waitevent (E4_uint64 *commandport, E4_Addr event, E4_uint64 count);
++extern void c_reschedule(E4_uint64 *commandport);
++
++static inline unsigned long
++c_load_u16(unsigned short *ptr)
++{
++    unsigned long value;
++
++    asm volatile ("ld2                [%1], %%r2\n"
++                "srl8,byte    %%r2, %1, %0\n"
++                "sll8         %0, 48, %0\n"
++                "srl8         %0, 48, %0\n"
++                : /* outputs */ "=r" (value) 
++                : /* inputs */ "r" (ptr)
++                : /* clobbered */ "%r2");
++    return value;
++}
++
++static inline unsigned long
++c_load_u32(unsigned int *ptr)
++{
++    unsigned long value;
++
++    asm volatile ("ld4                [%1], %%r2\n"
++                "srl8,byte    %%r2, %1, %0\n"
++                "sll8         %0, 32, %0\n"
++                "srl8         %0, 32, %0\n"
++                : /* outputs */ "=r" (value) 
++                : /* inputs */ "r" (ptr)
++                : /* clobbered */ "%r2");
++    return value;
++}
++
++static inline void
++c_store_u32(unsigned int *ptr, unsigned long value)
++{
++    asm volatile ("sll8,byte  %0, %1, %%r2\n"
++                "st4          %%r2, [%1]\n"
++                : /* no outputs */ 
++                : /* inputs */ "r" (value), "r" (ptr)
++                : /* clobbered */ "%r2");
++}
++
++/* Reschedule the current Elan thread to the back of the run queue 
++ * if there is another one ready to run */
++static inline void
++c_yield (E4_uint64 *commandport)
++{
++    unsigned long rval;
++
++    asm volatile ("breaktest %0" : /* outputs */ "=r" (rval) : /* inputs */);
++
++    if (rval  & ICC_SIGNED_BIT)
++      c_reschedule(commandport);
++}
++
++/* Reschedule the current thread if we're in danger of exceeding the 
++ * thread instruction count */
++static inline void
++c_insn_check(E4_uint64 *commandport)
++{
++    unsigned long rval;
++
++    asm volatile ("breaktest %0" : /* outputs */ "=r" (rval) : /* inputs */);
++
++    if (rval & ICC_ZERO_BIT)
++      c_reschedule(commandport);
++}
++
++void
++ep4_spinblock (E4_uint64 *cport, EP4_SPINLOCK_ELAN *sle, EP4_SPINLOCK_MAIN *slm)
++{
++    do {
++      unsigned long val = *sle & 0xfffffffff;
++
++      *slm = val;                                     /* Release my lock */
++      
++      while (*sle >> 32)                              /* Wait until the main */
++          c_yield(cport);                             /* releases the lock */
++      
++      c_store_u32 ((unsigned int *) sle, val + 1);    /* and try and relock */
++    } while (*sle >> 32);
++}
++
++#define RESCHED_AFTER_PKTS    ((CQ_Size(CQ_Size64K) / 128) - 1)
++
++void
++ep4comms_rcvr (EP4_RAIL_ELAN *rail, EP4_RCVR_RAIL_ELAN *rcvrElan, EP4_RCVR_RAIL_MAIN *rcvrMain,
++             E4_InputQueue *inputq, E4_uint64 *cport, E4_uint64 *resched)
++{
++    long count = 1;
++    long fptr  = inputq->q_fptr;
++
++    for (;;)
++    {
++      c_waitevent (cport, inputq->q_event, -count << 5);
++
++      count = 0;
++
++      while (fptr != inputq->q_bptr)
++      {
++          EP_ENVELOPE        *env      = (EP_ENVELOPE *) fptr;
++          unsigned long       nodeid   = c_load_u32 (&env->NodeId);
++          unsigned long       opencmd  = OPEN_STEN_PKT_CMD | OPEN_PACKET(0, PACK_OK | RESTART_COUNT_ZERO, EP_VP_DATA(nodeid));
++          unsigned long       vproc    = EP_VP_DATA(rail->r_nodeid);
++          EP_ATTRIBUTE        attr     = c_load_u32 (&env->Attr);
++          unsigned long       txdRail  = c_load_u32 (&env->TxdRail);
++          unsigned long       nFrags   = c_load_u32 (&env->nFrags);
++          E4_uint64           cookie   = rail->r_cookies[nodeid];
++          unsigned long       srcevent = (EP_IS_RPC(attr) ? txdRail + offsetof (EP4_TXD_RAIL_ELAN, txd_data) :
++                                          txdRail + offsetof (EP4_TXD_RAIL_ELAN, txd_done));
++          EP4_RXD_RAIL_ELAN  *rxdElan;
++          EP4_RXD_RAIL_MAIN  *rxdMain;
++          EP_RXD_MAIN        *rxd;
++          EP4_RXD_STEN_CMD   *sten;
++          E4_Event32         *event;
++          unsigned long       first;
++          unsigned long       buffer;
++          unsigned long       len;
++          unsigned long       i;
++
++          EP4_SPINENTER(resched, &rcvrElan->rcvr_thread_lock, &rcvrMain->rcvr_thread_lock);
++
++          if ((rxdElan = (EP4_RXD_RAIL_ELAN *) rcvrElan->rcvr_pending_head) == 0)
++          {
++              EP4_SPINEXIT (resched, &rcvrElan->rcvr_thread_lock, &rcvrMain->rcvr_thread_lock);
++
++              rxdElan = c_stall_thread (rcvrElan);
++
++              EP4_SPINENTER(resched, &rcvrElan->rcvr_thread_lock, &rcvrMain->rcvr_thread_lock);
++          }
++          
++          if (c_load_u32 (&env->Version) != EP_ENVELOPE_VERSION)              /* envelope has been cancelled */
++          {
++              EP4_SPINEXIT (resched, &rcvrElan->rcvr_thread_lock, &rcvrMain->rcvr_thread_lock);
++              goto consume_envelope;
++          }
++
++          rxd     = (EP_RXD_MAIN *) rxdElan->rxd_rxd;
++          rxdMain = (EP4_RXD_RAIL_MAIN *) rxdElan->rxd_main;
++          first   = (EP_MAXFRAG+1) - (( EP_IS_MULTICAST(attr) ? 1 : 0) + (nFrags == 0 ? 1 : nFrags));
++          sten    = &rxdElan->rxd_sten[first];
++          event   = &rxdElan->rxd_chain[first];
++
++          if (EP_IS_MULTICAST(attr))                          /* need to fetch broadcast bitmap */
++          {
++              sten->c_open          = opencmd;
++              sten->c_trans         = SEND_TRANS_CMD | ((TR_REMOTEDMA | TR_WAIT_FOR_EOP) << 16);
++              sten->c_cookie        = cookie | EP4_COOKIE_THREAD | EP4_COOKIE_STEN;
++              sten->c_dma_typeSize  = E4_DMA_TYPE_SIZE(BT_BITOUL(EP_MAX_NODES) * sizeof (bitmap_t), DMA_DataTypeWord, 0, EP4_DMA_RETRYCOUNT);
++              sten->c_dma_cookie    = cookie | EP4_COOKIE_THREAD | EP4_COOKIE_REMOTE | EP4_COOKIE_DMA | EP4_COOKIE_INC;
++              sten->c_dma_vproc     = vproc;
++              sten->c_dma_srcAddr   = c_load_u32 (&env->TxdMain.nmd_addr) + offsetof(EP_TXD_MAIN, Bitmap);
++              sten->c_dma_dstAddr   = (E4_Addr) &rxd->Bitmap;
++              sten->c_dma_srcEvent  = srcevent;
++              sten->c_dma_dstEvent  = (E4_Addr) event;
++
++              event->ev_CountAndType = E4_EVENT_INIT_VALUE(-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_RXD_STEN_CMD_NDWORDS);
++
++              cookie += (EP4_COOKIE_INC << 1);
++
++              sten++; event++;
++          }
++
++          if (nFrags == 0)
++          {
++              /* Generate an empty "get" DMA to accept the envelope and fire the rx handler */
++              sten->c_open          = opencmd;
++              sten->c_trans         = SEND_TRANS_CMD | ((TR_REMOTEDMA | TR_WAIT_FOR_EOP) << 16);
++              sten->c_cookie        = cookie | EP4_COOKIE_THREAD | EP4_COOKIE_STEN;
++              sten->c_dma_typeSize  = E4_DMA_TYPE_SIZE(0, DMA_DataTypeByte, 0, EP4_DMA_RETRYCOUNT);
++              sten->c_dma_cookie    = cookie | EP4_COOKIE_THREAD | EP4_COOKIE_REMOTE | EP4_COOKIE_DMA | EP4_COOKIE_INC;
++              sten->c_dma_vproc     = vproc;
++              sten->c_dma_srcEvent  = srcevent;
++              sten->c_dma_dstEvent  = (E4_Addr) event;
++
++              event->ev_CountAndType = E4_EVENT_INIT_VALUE(-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS);
++
++              len = 0;
++
++              cookie += (EP4_COOKIE_INC << 1);
++          }
++          else
++          {
++              /* Generate the DMA chain to fetch the data */
++              for (i = 0, buffer = c_load_u32 (&rxdElan->rxd_buffer.nmd_addr), len = 0; i < nFrags; i++)
++              {
++                  unsigned long fragLen = c_load_u32 (&env->Frags[i].nmd_len);
++
++                  sten->c_open          = opencmd;
++                  sten->c_trans         = SEND_TRANS_CMD | ((TR_REMOTEDMA | TR_WAIT_FOR_EOP) << 16);
++                  sten->c_cookie        = cookie | EP4_COOKIE_THREAD | EP4_COOKIE_STEN;
++                  sten->c_dma_typeSize  = E4_DMA_TYPE_SIZE(fragLen, DMA_DataTypeByte, 0, EP4_DMA_RETRYCOUNT);
++                  sten->c_dma_cookie    = cookie | EP4_COOKIE_THREAD | EP4_COOKIE_REMOTE | EP4_COOKIE_DMA | EP4_COOKIE_INC;
++                  sten->c_dma_vproc     = vproc;
++                  sten->c_dma_srcAddr   = c_load_u32 (&env->Frags[i].nmd_addr);
++                  sten->c_dma_dstAddr   = buffer;
++                  sten->c_dma_srcEvent  = srcevent;
++                  sten->c_dma_dstEvent  = (E4_Addr) event;
++                  
++                  event->ev_CountAndType = E4_EVENT_INIT_VALUE(-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_RXD_STEN_CMD_NDWORDS);
++                  
++                  buffer += fragLen;
++                  len    += fragLen;
++
++                  cookie += (EP4_COOKIE_INC << 1);
++
++                  sten++; event++;
++              }
++              
++              (--event)->ev_CountAndType = E4_EVENT_INIT_VALUE(-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS);
++
++              if (c_load_u32 (&rxdElan->rxd_buffer.nmd_len) < len)
++              {
++                  /* The receive descriptor was too small for the message */
++                  /* complete the message anyway,  but don't transfer any */
++                  /* data,  we set the length to EP_MSG_TOO_BIG */
++                  for (i = first, sten = &rxdElan->rxd_sten[first]; i <= EP_MAXFRAG; i++, sten++)
++                      sten->c_dma_typeSize = E4_DMA_TYPE_SIZE(0, DMA_DataTypeByte, 0, EP4_DMA_RETRYCOUNT);
++                  
++                  len = EP_MSG_TOO_BIG;
++              }
++          }
++          
++          /* Stuff the first STEN packet into the command queue, there's always enough space, 
++           * since we will insert a waitevent at least once for the queue size */
++          asm volatile ("ld64         [%0], %%r32\n"
++                        "ld64         [%0 + 64], %%r48\n"
++                        "st64         %%r32, [%1]\n"
++                        "st64         %%r48, [%1]\n"
++                        : /* no outputs */
++                        : /* inputs */ "r" (&rxdElan->rxd_sten[first]), "r" (cport)
++                        : /* clobbered */ R32_to_R47, R48_to_R63);
++
++          /* remove the RXD from the pending list */
++          if ((rcvrElan->rcvr_pending_head = rxdElan->rxd_next) == 0)
++              rcvrElan->rcvr_pending_tailp = (E4_Addr)&rcvrElan->rcvr_pending_head;
++
++          /* mark as not queued */
++          rxdElan->rxd_queued = 0;
++
++          /* copy down the envelope */
++          if (EP_HAS_PAYLOAD(attr))
++              asm volatile ("ld64     [%0],    %%r32\n"
++                            "ld64     [%0+64], %%r48\n"
++                            "st64     %%r32, [%1]\n"
++                            "ld64     [%0+128], %%r32\n"
++                            "st64     %%r48, [%1+64]\n"
++                            "ld64     [%0+192], %%r48\n"
++                            "st64     %%r32, [%1 + 128]\n"
++                            "st64     %%r48, [%1 + 192]\n"
++                            : /* no outputs */
++                            : /* inputs */    "r" (env), "r" (&rxd->Envelope)
++                            : /* clobbered */ R32_to_R47, R48_to_R63);
++ 
++          else
++              asm volatile ("ld64     [%0],    %%r32\n"
++                            "ld64     [%0+64], %%r48\n"
++                            "st64     %%r32, [%1]\n"
++                            "st64     %%r48, [%1+64]\n"
++                            : /* no outputs */
++                            : /* inputs */    "r" (env), "r" (&rxd->Envelope)
++                            : /* clobbered */ R32_to_R47, R48_to_R63);
++
++          /* Store the message length to indicate that I've finished */
++          c_store_u32 (&rxd->Len, len);
++          
++          /* Finally update the network error cookie */
++          rail->r_cookies[nodeid] = cookie;
++
++          EP4_SPINEXIT (resched, &rcvrElan->rcvr_thread_lock, &rcvrMain->rcvr_thread_lock);
++
++      consume_envelope:
++          if (fptr != rcvrElan->rcvr_qlast)
++              fptr += EP_INPUTQ_SIZE;
++          else
++              fptr = rcvrElan->rcvr_qbase;
++
++          if (! rcvrElan->rcvr_stall_intcookie)
++              inputq->q_fptr = fptr;
++
++          if (++count >= RESCHED_AFTER_PKTS)
++              break;
++
++          c_insn_check (cport);
++      }
++      
++      if (rcvrElan->rcvr_stall_intcookie)
++      {
++          c_waitevent_interrupt (cport, &rcvrElan->rcvr_thread_halt, -(1 << 5), rcvrElan->rcvr_stall_intcookie);
++          inputq->q_fptr = fptr;
++
++          count++;                                            /* one extra as we were given an extra set to wake us up */
++      }
++    }
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/ep/epcommsFwd.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/epcommsFwd.c 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/epcommsFwd.c      2005-05-11 12:10:12.490924672 -0400
+@@ -0,0 +1,310 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: epcommsFwd.c,v 1.12 2004/08/16 12:21:15 david Exp $ $Name: QSNETMODULES-4-31_20050321 $"
++/*      $Source: /cvs/master/quadrics/epmod/epcommsFwd.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++#include <elan/epsvc.h>
++#include <elan/epcomms.h>
++
++#include "debug.h"
++
++unsigned int epcomms_forward_limit = 8;
++
++static void
++GenerateTree (unsigned nodeId, unsigned lowId, unsigned  highId, bitmap_t *bitmap, 
++            unsigned *parentp, unsigned *childrenp, int *nchildrenp)
++{
++    int i;
++    int count;
++    int branch;
++    int nSub;
++    int branchIndex;
++    int parent;
++    int nBranch;
++    int rem;
++    int self;
++    int branchRatio;
++    int node;
++    int x, y, z;
++
++
++#ifdef DEBUG_PRINTF
++    {
++#define OVERFLOW "...]"
++#define LINESZ         128
++      char space[LINESZ+1];
++
++      if (ep_sprintf_bitmap (space, LINESZ-strlen(OVERFLOW), bitmap, 0, 0, (highId - lowId)+1) != -1)
++          strcat (space, OVERFLOW);
++
++      EPRINTF3 (DBG_FORWARD, "GenerateTree; elan node low=%d node high=%d bitmap=%s\n", lowId, highId, space);
++#undef OVERFLOW
++#undef LINESZ
++    }
++#endif
++
++    /* Count the number of nodes in the partition */
++    /* and work out which one I am */
++    for (count = 0, self = ELAN_INVALID_NODE, i = lowId; i <= highId; i++)
++    {
++      if (BT_TEST (bitmap, i-lowId))
++      {
++          if (i == nodeId)
++              self = count;
++          count++;
++      }
++    }
++
++    EPRINTF2 (DBG_FORWARD, "GenerateTree: count=%d self=%d\n", count, self);
++
++    if (count == 0 || self == ELAN_INVALID_NODE)
++    {
++      *parentp    = ELAN_INVALID_NODE;
++      *nchildrenp = 0;
++      return;
++    }
++
++    /* search for position in tree */
++    branchRatio = EP_TREE_ARITY;              /* branching ratio */
++    branch      = 0;                          /* start with process 0 */
++    nSub        = count;                      /* and whole tree */
++    branchIndex = -1;                         /* my branch # in parent */
++    parent      = -1;                         /* my parent's group index # */
++    
++    while (branch != self)                    /* descend process tree */
++    {                                         /* until I find myself */
++      parent = branch;
++      branch++;                               /* parent + 1 = first born */
++      nSub--;                                 /* set # descendents */
++      
++      rem  = nSub % branchRatio;
++      nSub = nSub / branchRatio + 1;
++      x = rem * nSub;
++      y = self - branch;
++      
++      if (y < x)                              /* my first 'rem' branches have */
++      {                                       /* 1 more descendent... */
++          branchIndex = y / nSub;
++          branch += branchIndex * nSub;
++      }
++      else                                    /* than the rest of my branches */
++      {
++          nSub--;
++          z = (y - x) / nSub;
++          branchIndex = rem + z;
++          branch += x + z * nSub;
++      }
++    }
++
++    branch++;                                 /* my first born */
++    nSub--;                                   /* total # of my descendents */
++    /* leaves + their parents may have # children < branchRatio */
++    nBranch = (nSub < branchRatio) ? nSub : branchRatio;      
++
++    EPRINTF2 (DBG_FORWARD, "GenerateTree: parent=%d nBranch=%d\n", parent, nBranch);
++
++    /* Now calculate the real elan id's of the parent and my children */
++    if (parent == -1)
++      *parentp = ELAN_INVALID_NODE;
++    else
++    {
++      for (i = lowId, node = 0; i <= highId; i++)
++      {
++          if (BT_TEST(bitmap, i-lowId))
++              if (node++ == parent)
++                  break;
++      }
++      *parentp = i;
++    }
++
++    for (i = lowId, branchIndex = 0, node = 0; branchIndex < nBranch && i <= highId; i++)
++    {
++      if (BT_TEST(bitmap, i-lowId))
++      {
++          if (node == branch)
++          {
++              branch = branch + nSub / branchRatio + ((branchIndex < (nSub % branchRatio)) ? 1 : 0);
++
++              childrenp[branchIndex++] = i;
++          }
++          node++;
++      }
++    }
++
++    *nchildrenp = branchIndex;
++}
++
++static void
++ForwardTxDone (EP_TXD *txd, void *arg, EP_STATUS status)
++{
++    EP_FWD_DESC     *desc   = (EP_FWD_DESC *) arg;
++    EP_RXD          *rxd    = desc->Rxd;
++    EP_COMMS_SUBSYS *subsys = rxd->Rcvr->Subsys;
++    unsigned long    flags;
++
++    /* XXXX: if transmit fails, could step to next node in this subtree ? */
++
++    spin_lock_irqsave (&subsys->ForwardDescLock, flags);
++
++    if (--desc->NumChildren > 0)
++      spin_unlock_irqrestore (&subsys->ForwardDescLock, flags);
++    else
++    {
++      rxd->Rcvr->ForwardRxdCount--;
++
++      spin_unlock_irqrestore (&subsys->ForwardDescLock, flags);
++
++      KMEM_FREE (desc, sizeof (EP_FWD_DESC));
++
++      rxd->Handler (rxd);
++    }
++}
++
++long
++ep_forward_rxds (EP_COMMS_SUBSYS *subsys, long nextRunTime)
++{
++    unsigned long flags;
++    int i, res;
++
++    spin_lock_irqsave (&subsys->ForwardDescLock, flags);
++    while (! list_empty (&subsys->ForwardDescList)) 
++    {
++      EP_RXD      *rxd     = (EP_RXD *) list_entry (subsys->ForwardDescList.next, EP_RXD, Link);
++      EP_RXD_MAIN *rxdMain = rxd->RxdMain;
++      EP_ENVELOPE *env     = &rxdMain->Envelope;
++      EP_FWD_DESC *desc;
++
++      EPRINTF2 (DBG_FORWARD, "ep: forwarding rxd %p to range %x\n", rxd, env->Range);
++
++      list_del (&rxd->Link);
++
++      rxd->Rcvr->ForwardRxdCount++;
++
++      spin_unlock_irqrestore (&subsys->ForwardDescLock, flags);
++
++      KMEM_ALLOC (desc, EP_FWD_DESC *, sizeof (EP_FWD_DESC), 1);
++
++      if (desc == NULL)
++      {
++          spin_lock_irqsave (&subsys->ForwardDescLock, flags);
++          rxd->Rcvr->ForwardRxdCount--;
++          spin_unlock_irqrestore (&subsys->ForwardDescLock, flags);
++
++          rxd->Handler (rxd);
++      }
++      else
++      {
++          /* compute the spanning tree for this message */
++          unsigned int destLo = EP_RANGE_LOW (env->Range);
++          unsigned int destHi = EP_RANGE_HIGH (env->Range);
++          unsigned int parent;
++
++          GenerateTree (subsys->Subsys.Sys->Position.pos_nodeid, destLo, destHi, rxdMain->Bitmap, &parent, desc->Children, &desc->NumChildren);
++          
++          if (desc->NumChildren == 0 || (epcomms_forward_limit && (rxd->Rcvr->ForwardRxdCount >= epcomms_forward_limit)))
++          {
++              EPRINTF5 (DBG_FORWARD, "ep; don't forward rxd %p to /%d (%d children/ %d forwarding (%d))\n",
++                        rxd, rxd->Rcvr->Service, desc->NumChildren, rxd->Rcvr->ForwardRxdCount, epcomms_forward_limit);
++
++              spin_lock_irqsave (&subsys->ForwardDescLock, flags);
++              rxd->Rcvr->ForwardRxdCount--;
++              spin_unlock_irqrestore (&subsys->ForwardDescLock, flags);
++
++              KMEM_FREE (desc, sizeof (EP_FWD_DESC));
++              
++              rxd->Handler (rxd);
++          }
++          else
++          {
++              ep_nmd_subset (&desc->Data, &rxd->Data, 0, ep_rxd_len (rxd));
++              desc->Rxd = rxd;
++
++              /* NOTE - cannot access 'desc' after last call to multicast, since it could complete
++               *        and free the desc before we access it again.  Hence the reverse loop. */
++              for (i = desc->NumChildren-1; i >= 0; i--)
++              {
++                  ASSERT (desc->Children[i] < subsys->Subsys.Sys->Position.pos_nodes);
++
++                  EPRINTF3 (DBG_FORWARD, "ep: forwarding rxd %p to node %d/%d\n", rxd, desc->Children[i], rxd->Rcvr->Service);
++
++                  if ((res = ep_multicast_forward (subsys->ForwardXmtr, desc->Children[i], rxd->Rcvr->Service, 0, 
++                                                   ForwardTxDone, desc, env, EP_HAS_PAYLOAD(env->Attr) ? &rxdMain->Payload : NULL,  
++                                                   rxdMain->Bitmap, &desc->Data, 1)) != EP_SUCCESS)
++                  {
++                      ep_debugf (DBG_FORWARD, "ep: ep_multicast_forward failed\n");
++                      ForwardTxDone (NULL, desc, res);
++                  }
++              }
++              
++          }
++      }
++
++      spin_lock_irqsave (&subsys->ForwardDescLock, flags);
++    }
++    spin_unlock_irqrestore (&subsys->ForwardDescLock, flags);
++
++    return (nextRunTime);
++}
++
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++void
++ep_csum_rxds (EP_COMMS_SUBSYS *subsys)
++{
++    unsigned long flags;
++ 
++    spin_lock_irqsave (&subsys->CheckSumDescLock, flags);
++    while (! list_empty (&subsys->CheckSumDescList)) 
++    {
++      EP_RXD      *rxd = (EP_RXD *) list_entry (subsys->CheckSumDescList.next, EP_RXD, CheckSumLink);
++      EP_ENVELOPE *env = &rxd->RxdMain->Envelope;
++
++      list_del_init (&rxd->CheckSumLink);
++      spin_unlock_irqrestore (&subsys->CheckSumDescLock, flags);
++
++      if (env->CheckSum) {
++          EP_NMD nmd;
++          uint32_t csum; 
++
++          ep_nmd_subset ( &nmd, &rxd->Data, 0, ep_rxd_len (rxd));
++
++          csum = ep_calc_check_sum(subsys->Subsys.Sys, env, &nmd, 1);
++          if ( env->CheckSum  != csum ) {
++              int f;
++      
++
++              printk("Check Sum Error: env(0x%x,0x%x) data(0x%x,0x%x)\n", ((csum >> 16) & 0x7FFF), ((env->CheckSum >> 16) & 0x7FFF), 
++                     (csum & 0xFFFF),  (env->CheckSum & 0xFFFF));
++              printk("Check Sum Error: Sent : NodeId %u Range 0x%x Service %u Version 0x%x Attr 0x%x\n", env->NodeId, env->Range, rxd->Rcvr->Service, env->Version, env->Attr);
++              printk("Check Sum Error: Sent : Xid Generation 0x%x Handle 0x%x Unique 0x%llx\n", env->Xid.Generation, env->Xid.Handle, env->Xid.Unique);
++              printk("Check Sum Error: Sent : TxdRail 0x%x TxdMain nmd_addr 0x%x  nmd_len %u  nmd_attr 0x%x\n",  env->TxdRail, env->TxdMain.nmd_addr, env->TxdMain.nmd_len, env->TxdMain.nmd_attr ); 
++              printk("Check Sum Error: Sent : nFrags %d \n", env->nFrags);
++              for(f=0;f<env->nFrags;f++)
++                  printk("Check Sum Error: Sent (%d): nmd_addr 0x%x   nmd_len %u   nmd_attr 0x%x\n", f,
++                         env->Frags[f].nmd_addr, env->Frags[f].nmd_len, env->Frags[f].nmd_attr);
++              printk("Check Sum Error: Recv : nmd_addr 0x%x   nmd_len %u   nmd_attr 0x%x\n",
++                     nmd.nmd_addr, nmd.nmd_len, nmd.nmd_attr);
++
++          }
++      }
++      ep_rxd_received_now(rxd);
++
++      spin_lock_irqsave (&subsys->CheckSumDescLock, flags);
++    }
++    spin_unlock_irqrestore (&subsys->CheckSumDescLock, flags);
++}
++#endif
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/ep/epcommsRx.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/epcommsRx.c  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/epcommsRx.c       2005-05-11 12:10:12.492924368 -0400
+@@ -0,0 +1,1205 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: epcommsRx.c,v 1.27.2.5 2004/11/30 12:02:16 mike Exp $ $Name: QSNETMODULES-4-31_20050321 $"
++/*      $Source: /cvs/master/quadrics/epmod/epcommsRx.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <elan/kcomm.h>
++#include <elan/epsvc.h>
++#include <elan/epcomms.h>
++
++#include "debug.h"
++
++unsigned int ep_rxd_lowat = 5;
++
++static int
++AllocateRxdBlock (EP_RCVR *rcvr, EP_ATTRIBUTE attr, EP_RXD **rxdp)
++{
++    EP_RXD_BLOCK *blk;
++    EP_RXD       *rxd;
++    EP_RXD_MAIN  *pRxdMain;
++    int                 i;
++    unsigned long flags;
++
++    KMEM_ZALLOC (blk, EP_RXD_BLOCK *, sizeof (EP_RXD_BLOCK), ! (attr & EP_NO_SLEEP));
++
++    if (blk == NULL)
++      return (ENOMEM);
++
++    if ((pRxdMain = ep_shared_alloc_main (rcvr->Subsys->Subsys.Sys, EP_RXD_MAIN_SIZE * EP_NUM_RXD_PER_BLOCK, attr, &blk->NmdMain)) == (sdramaddr_t) 0)
++    {
++      KMEM_FREE (blk, sizeof (EP_RXD_BLOCK));
++      return (ENOMEM);
++    }
++    
++    for (rxd = &blk->Rxd[0], i = 0; i < EP_NUM_RXD_PER_BLOCK; i++, rxd++)
++    {
++      rxd->Rcvr        = rcvr;
++      rxd->RxdMain     = pRxdMain;
++
++      ep_nmd_subset (&rxd->NmdMain, &blk->NmdMain, (i * EP_RXD_MAIN_SIZE), EP_RXD_MAIN_SIZE);
++
++      /* move onto next descriptor */
++      pRxdMain = (EP_RXD_MAIN *) ((unsigned long) pRxdMain + EP_RXD_MAIN_SIZE);
++    }
++
++    spin_lock_irqsave (&rcvr->FreeDescLock, flags);
++
++    list_add  (&blk->Link, &rcvr->DescBlockList);
++
++    rcvr->TotalDescCount += EP_NUM_RXD_PER_BLOCK;
++
++    for (i = rxdp ? 1 : 0; i < EP_NUM_RXD_PER_BLOCK; i++)
++    {
++      
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++      INIT_LIST_HEAD (&blk->Rxd[i].CheckSumLink);
++#endif
++
++      list_add (&blk->Rxd[i].Link, &rcvr->FreeDescList);
++      
++      rcvr->FreeDescCount++;
++
++      if (rcvr->FreeDescWanted)
++      {
++          rcvr->FreeDescWanted--;
++          kcondvar_wakeupone (&rcvr->FreeDescSleep, &rcvr->FreeDescLock);
++      }
++    }
++    spin_unlock_irqrestore (&rcvr->FreeDescLock, flags);
++    
++    if (rxdp)
++    {
++
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++      INIT_LIST_HEAD (&blk->Rxd[0].CheckSumLink);
++#endif
++             
++      *rxdp = &blk->Rxd[0];
++    }
++    return (ESUCCESS);
++}
++
++static void
++FreeRxdBlock (EP_RCVR *rcvr, EP_RXD_BLOCK *blk)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&rcvr->FreeDescLock, flags);
++
++    list_del (&blk->Link);
++
++    rcvr->TotalDescCount -= EP_NUM_RXD_PER_BLOCK;
++    rcvr->FreeDescCount -= EP_NUM_RXD_PER_BLOCK;
++
++    spin_unlock_irqrestore (&rcvr->FreeDescLock, flags);
++
++    ep_shared_free_main (rcvr->Subsys->Subsys.Sys, &blk->NmdMain);
++    KMEM_FREE (blk, sizeof (EP_RXD_BLOCK));
++}
++
++static EP_RXD *
++GetRxd (EP_RCVR *rcvr, EP_ATTRIBUTE attr)
++{
++    EP_RXD *rxd;
++    unsigned long flags;
++    int low_on_rxds;
++
++    spin_lock_irqsave (&rcvr->FreeDescLock, flags);
++
++    while (list_empty (&rcvr->FreeDescList))
++    {
++      if (! (attr & EP_NO_ALLOC))
++      {
++          spin_unlock_irqrestore (&rcvr->FreeDescLock, flags);
++
++          if (AllocateRxdBlock (rcvr, attr, &rxd) == ESUCCESS)
++              return (rxd);
++
++          spin_lock_irqsave (&rcvr->FreeDescLock, flags);
++      }
++
++      if (attr & EP_NO_SLEEP)
++      {
++          IncrStat (rcvr->Subsys, NoFreeRxds);
++          spin_unlock_irqrestore (&rcvr->FreeDescLock, flags);
++
++          ep_kthread_schedule (&rcvr->Subsys->Thread, lbolt);
++          return (NULL);
++      }
++
++      rcvr->FreeDescWanted++;
++      kcondvar_wait (&rcvr->FreeDescSleep, &rcvr->FreeDescLock, &flags);
++    }
++
++    rxd = list_entry (rcvr->FreeDescList.next, EP_RXD, Link);
++
++    list_del (&rxd->Link);
++
++    /* Wakeup the descriptor primer thread if there's not many left */
++    low_on_rxds = (--rcvr->FreeDescCount < ep_rxd_lowat);
++
++    spin_unlock_irqrestore (&rcvr->FreeDescLock, flags);
++
++    if (low_on_rxds)
++      ep_kthread_schedule (&rcvr->Subsys->Thread, lbolt);
++
++    return (rxd);
++}
++
++static void
++FreeRxd (EP_RCVR *rcvr, EP_RXD *rxd)
++{
++    unsigned long flags;
++
++    ASSERT (EP_XID_INVALID(rxd->MsgXid));
++
++    spin_lock_irqsave (&rcvr->FreeDescLock, flags);
++
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++    ASSERT(list_empty(&rxd->CheckSumLink));
++#endif
++   
++    list_add (&rxd->Link, &rcvr->FreeDescList);
++
++    rcvr->FreeDescCount++;
++
++    if (rcvr->FreeDescWanted)                                 /* someone waiting for a receive */
++    {                                                         /* descriptor, so wake them up */
++      rcvr->FreeDescWanted--;
++      kcondvar_wakeupone (&rcvr->FreeDescSleep, &rcvr->FreeDescLock);
++    }
++    
++    spin_unlock_irqrestore (&rcvr->FreeDescLock, flags);
++}
++
++int
++ep_queue_receive (EP_RCVR *rcvr, EP_RXH *handler, void *arg, EP_NMD *nmd, EP_ATTRIBUTE attr)
++{
++    EP_RCVR_RAIL *rcvrRail;
++    EP_RXD       *rxd;
++    int           rnum;
++    unsigned long flags;
++
++    if ((rxd = GetRxd (rcvr, attr)) == NULL)
++      return (ENOMEM);
++
++    rxd->Handler      = handler;
++    rxd->Arg          = arg;
++    rxd->Data         = *nmd;
++    rxd->RxdMain->Len = EP_RXD_PENDING;
++    
++    spin_lock_irqsave (&rcvr->Lock, flags);
++
++    list_add_tail (&rxd->Link, &rcvr->ActiveDescList);
++    
++    if (EP_IS_PREFRAIL_SET(attr))
++      rnum = EP_ATTR2PREFRAIL(attr);
++    else 
++      rnum = ep_rcvr_prefrail (rcvr, EP_NMD_RAILMASK(nmd));
++
++    if (rnum < 0 || !(EP_NMD_RAILMASK(nmd) & EP_RAIL2RAILMASK(rnum) & rcvr->RailMask))
++      rcvrRail = NULL;
++    else
++      rcvrRail = rcvr->Rails[rnum];
++
++    EPRINTF7 (DBG_RCVR,"ep_queue_receive: rxd=%p svc %d nmd=%08x,%d,%x rnum=%d rcvrRail=%p\n",
++            rxd, rcvr->Service, nmd->nmd_addr, nmd->nmd_len, nmd->nmd_attr, rnum, rcvrRail);
++
++    rxd->State = EP_RXD_RECEIVE_ACTIVE;
++
++    if (rcvrRail == NULL || !EP_RCVR_OP (rcvrRail, QueueRxd) (rxd, rcvrRail))
++    {
++      rxd->State = EP_RXD_RECEIVE_UNBOUND;
++
++      ep_kthread_schedule (&rcvr->Subsys->Thread, lbolt);
++    }
++
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++
++    return (ESUCCESS);
++}
++
++void
++ep_requeue_receive (EP_RXD *rxd, EP_RXH *handler, void *arg, EP_NMD *nmd, EP_ATTRIBUTE attr)
++{
++    EP_RCVR      *rcvr = rxd->Rcvr;
++    EP_SYS       *sys  = rcvr->Subsys->Subsys.Sys;
++    int           rnum = ep_pickRail(EP_NMD_RAILMASK(&rxd->Data));
++    EP_RCVR_RAIL *rcvrRail;
++    unsigned long flags;
++
++    ASSERT (rxd->RxdRail == NULL);
++
++    EPRINTF5 (DBG_RCVR,"ep_requeue_receive: rxd=%p svc %d nmd=%08x,%d,%x\n", 
++            rxd, rcvr->Service, nmd->nmd_addr, nmd->nmd_len, nmd->nmd_attr);
++
++    rxd->Handler      = handler;
++    rxd->Arg          = arg;
++    rxd->Data         = *nmd;
++    rxd->RxdMain->Len = EP_RXD_PENDING;
++    
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    
++    list_add_tail (&rxd->Link, &rcvr->ActiveDescList);
++
++    /*
++     * Rail selection: if they've asked for a particular rail, then use it, otherwise if
++     *                 the rail it was last received on is mapped for the nmd and is available
++     *                 then use that one, otherwise pick one that is mapped by the nmd.
++     */
++    if (EP_IS_PREFRAIL_SET(attr))
++      rnum = EP_ATTR2PREFRAIL(attr);
++    
++    if (rnum < 0 || ! (EP_RAIL2RAILMASK (rnum) & EP_NMD_RAILMASK(nmd) & ep_rcvr_availrails (rcvr)))
++      rnum = ep_rcvr_prefrail (rcvr, EP_NMD_RAILMASK(nmd));
++
++    if (rnum < 0)
++      rcvrRail = NULL;
++    else
++    {
++      rcvrRail = rcvr->Rails[rnum];
++
++      if (! (EP_NMD_RAILMASK(&rxd->Data) & EP_RAIL2RAILMASK(rnum)) && ep_nmd_map_rails (sys, &rxd->Data, EP_RAIL2RAILMASK(rnum)) < 0)
++          rcvrRail = NULL;
++    }
++
++    rxd->State = EP_RXD_RECEIVE_ACTIVE;
++
++    if (rcvrRail == NULL || !EP_RCVR_OP(rcvrRail, QueueRxd) (rxd, rcvrRail))
++    {
++      EPRINTF1 (DBG_RCVR, "ep_requeue_receive: rcvrRail=%p - setting unbound\n", rcvrRail);
++
++      rxd->State = EP_RXD_RECEIVE_UNBOUND;
++
++      ep_kthread_schedule (&rcvr->Subsys->Thread, lbolt);
++    }
++
++    if (rcvr->CleanupWaiting)
++      kcondvar_wakeupall (&rcvr->CleanupSleep, &rcvr->Lock);
++    rcvr->CleanupWaiting = 0;
++
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++}
++
++void
++
++ep_complete_receive (EP_RXD *rxd)
++{
++    EP_RCVR *rcvr = rxd->Rcvr;
++    unsigned long flags;
++
++    ASSERT (rxd->RxdRail == NULL && rxd->State == EP_RXD_COMPLETED);
++
++    FreeRxd (rcvr, rxd);
++
++    /* if we're waiting for cleanup, then wake them up */
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    if (rcvr->CleanupWaiting)
++      kcondvar_wakeupall (&rcvr->CleanupSleep, &rcvr->Lock);
++    rcvr->CleanupWaiting = 0;
++    spin_unlock_irqrestore (&rcvr->Lock, flags);   
++}
++
++int
++ep_rpc_put (EP_RXD *rxd, EP_RXH *handler, void *arg, EP_NMD *local, EP_NMD *remote, int nFrags)
++{
++    EP_RCVR      *rcvr = rxd->Rcvr;
++    EP_SYS       *sys  = rcvr->Subsys->Subsys.Sys;
++    EP_ENVELOPE  *env  = &rxd->RxdMain->Envelope;
++    unsigned long flags;
++
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    
++    if (rxd->State == EP_RXD_BEEN_ABORTED)
++    {
++      EPRINTF2 (DBG_RCVR, "ep_rpc_put: rcvr %p rxd %p completed because no rails available\n", rcvr, rxd);
++      
++      /* rxd no longer on active list - just free it */
++      /* off and return an error */
++      spin_unlock_irqrestore (&rcvr->Lock, flags);
++      
++      return EP_CONN_RESET;
++    }
++    else
++    {
++      EP_RXD_RAIL      *rxdRail   = rxd->RxdRail;
++      EP_RCVR_RAIL     *rcvrRail  = rxdRail->RcvrRail;
++      EP_COMMS_RAIL    *commsRail = rcvrRail->CommsRail;
++      EP_RAIL          *rail      = commsRail->Rail;
++      EP_NODE_RAIL     *nodeRail  = &rail->Nodes[env->NodeId];
++      int               i;
++      
++      /* Attempt to ensure that the local nmds are mapped */
++      for (i = 0; i < nFrags; i++)
++          if (! (EP_NMD_RAILMASK(&local[i]) & EP_RAIL2RAILMASK(rail->Number)))
++              ep_nmd_map_rails (sys, &local[i], EP_RAIL2RAILMASK(rail->Number));
++    
++      if (nodeRail->State == EP_NODE_CONNECTED &&                                                                     /* rail is connected */
++          (ep_nmd2railmask (local, nFrags) & ep_nmd2railmask (remote, nFrags) & EP_RAIL2RAILMASK (rail->Number)))     /* and NMDs valid for it */
++      {
++          rxd->State = EP_RXD_PUT_ACTIVE;
++
++          EP_RCVR_OP(rcvrRail, RpcPut) (rxd, local, remote, nFrags);
++      }
++      else
++      {
++          /* RPC completion cannot progress - either node is no longer connected on this 
++           * rail or some of the source/destination NMDs are not mapped on this rail.
++           * Save the NMDs into the RXD and schedule the thread to request mappings */
++          EPRINTF4 (DBG_RCVR, "%s: ep_rpc_put: rcvr %p rxd %p %s\n", rail->Name, rcvr, rxd,
++                    (nodeRail->State == EP_NODE_CONNECTED) ? "NMDs not valid on this rail" : "no longer connected on this rail");
++
++          rxd->State = EP_RXD_PUT_STALLED;
++
++          if (nodeRail->State == EP_NODE_CONNECTED)
++              ep_kthread_schedule (&rcvr->Subsys->Thread, lbolt);
++      }
++
++      /* install the handler */
++      rxd->Handler = handler;
++      rxd->Arg     = arg;
++      
++      /* store the arguements */
++      rxd->nFrags = nFrags;
++      for (i = 0; i < nFrags; i++)
++      {
++          rxd->Local[i]  = local[i];
++          rxd->Remote[i] = remote[i];
++      }
++    }
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++
++    return EP_SUCCESS;
++}
++
++int
++ep_rpc_get (EP_RXD *rxd, EP_RXH *handler, void *arg, EP_NMD *remote, EP_NMD *local, int nFrags)
++{
++    EP_RCVR      *rcvr = rxd->Rcvr;
++    EP_SYS       *sys  = rcvr->Subsys->Subsys.Sys;
++    EP_ENVELOPE  *env  = &rxd->RxdMain->Envelope;
++    unsigned long flags;
++
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    
++    if (rxd->State == EP_RXD_BEEN_ABORTED)
++    {
++      EPRINTF2 (DBG_RCVR, "ep_rpc_get: rcvr %p rxd %p completed because no rails available\n", rcvr, rxd);
++      
++      spin_unlock_irqrestore (&rcvr->Lock, flags);
++
++      return EP_CONN_RESET;
++    }
++    else
++    {
++      EP_RXD_RAIL      *rxdRail   = rxd->RxdRail;
++      EP_RCVR_RAIL     *rcvrRail  = rxdRail->RcvrRail;
++      EP_COMMS_RAIL    *commsRail = rcvrRail->CommsRail;
++      EP_RAIL          *rail      = commsRail->Rail;
++      EP_NODE_RAIL     *nodeRail  = &rail->Nodes[env->NodeId];
++      int               i;
++      
++      /* Attempt to ensure that the local nmds are mapped */
++      for (i = 0; i < nFrags; i++)
++          if (! (EP_NMD_RAILMASK(&local[i]) & EP_RAIL2RAILMASK(rail->Number)))
++              ep_nmd_map_rails (sys, &local[i], EP_RAIL2RAILMASK(rail->Number));
++
++      if (nodeRail->State == EP_NODE_CONNECTED &&                                                                     /* rail is connected */
++          (ep_nmd2railmask (local, nFrags) & ep_nmd2railmask (remote, nFrags) & EP_RAIL2RAILMASK (rail->Number)))     /* and NMDs valid for it */
++      {
++          rxd->State = EP_RXD_GET_ACTIVE;
++
++          EP_RCVR_OP (rcvrRail, RpcGet) (rxd, local, remote, nFrags);
++      }
++      else
++      {
++          /* RPC completion cannot progress - either node is no longer connected on this 
++           * node or some of the source/destination NMDs are not mapped on this rail.
++           * Save the NMDs into the RXD and schedule the thread to request mappings */
++          EPRINTF4 (DBG_RCVR, "%s: ep_rpc_get: rcvr %p rxd %p %s\n", rail->Name, rcvr, rxd, 
++                    (nodeRail->State == EP_NODE_CONNECTED) ? "NMDs not valid on this rail" : "no longer connected on this rail");
++          
++          rxd->State = EP_RXD_GET_STALLED;
++
++          if (nodeRail->State == EP_NODE_CONNECTED)
++              ep_kthread_schedule (&rcvr->Subsys->Thread, lbolt);
++      }
++
++      /* install the handler */
++      rxd->Handler = handler;
++      rxd->Arg     = arg;
++      
++      /* store the arguements */
++      rxd->nFrags = nFrags;
++      for (i = 0; i < nFrags; i++)
++      {
++          rxd->Local[i]  = local[i];
++          rxd->Remote[i] = remote[i];
++      }
++    }
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++    
++    return EP_SUCCESS;
++}
++
++int
++ep_complete_rpc (EP_RXD *rxd, EP_RXH *handler, void *arg, EP_STATUSBLK *blk, EP_NMD *local, EP_NMD *remote, int nFrags)
++{
++    EP_RCVR      *rcvr = rxd->Rcvr;
++    EP_SYS       *sys  = rcvr->Subsys->Subsys.Sys;
++    EP_ENVELOPE  *env  = &rxd->RxdMain->Envelope;
++    unsigned long flags;
++
++    spin_lock_irqsave (&rcvr->Lock, flags);
++
++    if (rxd->State == EP_RXD_BEEN_ABORTED)
++    {
++      EPRINTF2 (DBG_RCVR, "ep_complete_rpc: rcvr %p rxd %p completed because no rails available\n", rcvr, rxd);
++      
++      spin_unlock_irqrestore (&rcvr->Lock, flags);
++      return EP_CONN_RESET;
++    }
++    else
++    {
++      EP_RXD_RAIL      *rxdRail   = rxd->RxdRail;
++      EP_RCVR_RAIL     *rcvrRail  = rxdRail->RcvrRail;
++      EP_COMMS_RAIL    *commsRail = rcvrRail->CommsRail;
++      EP_RAIL          *rail      = commsRail->Rail;
++      EP_NODE_RAIL     *nodeRail  = &rail->Nodes[env->NodeId];
++      int               i;
++
++      if (blk == NULL)
++          bzero (&rxd->RxdMain->StatusBlk, sizeof (EP_STATUSBLK));
++      else
++          bcopy (blk, &rxd->RxdMain->StatusBlk, sizeof (EP_STATUSBLK));
++
++      /* Attempt to ensure that the local nmds are mapped */
++      for (i = 0; i < nFrags; i++)
++          if (! (EP_NMD_RAILMASK(&local[i]) & EP_RAIL2RAILMASK(rail->Number)))
++              ep_nmd_map_rails (sys, &local[i], EP_RAIL2RAILMASK(rail->Number));
++
++      if (nodeRail->State == EP_NODE_CONNECTED &&                                                                     /* rail is connected */
++          (ep_nmd2railmask (local, nFrags) & ep_nmd2railmask (remote, nFrags) & EP_RAIL2RAILMASK (rail->Number)))     /* and NMDs valid for it */
++      {
++          rxd->State = EP_RXD_COMPLETE_ACTIVE;
++
++          EP_RCVR_OP (rcvrRail, RpcComplete) (rxd, local, remote, nFrags);
++      }
++      else
++      {
++          /* RPC completion cannot progress - either node is no longer connected on this 
++           * node or some of the source/destination NMDs are not mapped on this rail.
++           * Save the NMDs into the RXD and schedule the thread to request mappings */
++          EPRINTF4 (DBG_RCVR, "%s: ep_complete_rpc: rcvr %p rxd %p %s\n", rail->Name, rcvr, rxd, 
++                    (nodeRail->State == EP_NODE_CONNECTED) ? "NMDs not valid on this rail" : "no longer connected on this rail");
++
++          rxd->State = EP_RXD_COMPLETE_STALLED;
++
++          if (nodeRail->State == EP_NODE_CONNECTED)
++              ep_kthread_schedule (&rcvr->Subsys->Thread, lbolt);
++      }
++
++      /* install the handler */
++      rxd->Handler = handler;
++      rxd->Arg     = arg;
++      
++      /* store the arguements */
++      rxd->nFrags = nFrags;
++      for (i = 0; i < nFrags; i++)
++      {
++          rxd->Local[i]  = local[i];
++          rxd->Remote[i] = remote[i];
++      }
++    }
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++      
++    return (ESUCCESS);
++}
++
++/* functions for accessing fields of rxds */
++void            *ep_rxd_arg(EP_RXD *rxd)              { return (rxd->Arg); }
++int              ep_rxd_len(EP_RXD *rxd)              { return (rxd->RxdMain->Len); }
++EP_STATUS      ep_rxd_status(EP_RXD *rxd)             { return (rxd->RxdMain->Len < 0 ? rxd->RxdMain->Len : EP_SUCCESS); }
++int              ep_rxd_isrpc(EP_RXD *rxd)            { return (EP_IS_RPC(rxd->RxdMain->Envelope.Attr) != 0); }
++EP_ENVELOPE     *ep_rxd_envelope(EP_RXD *rxd)                 { return (&rxd->RxdMain->Envelope); }
++EP_PAYLOAD      *ep_rxd_payload(EP_RXD *rxd)          { return (EP_HAS_PAYLOAD(rxd->RxdMain->Envelope.Attr) ? &rxd->RxdMain->Payload : NULL); }
++int              ep_rxd_node(EP_RXD *rxd)             { return (rxd->RxdMain->Envelope.NodeId); }
++EP_STATUSBLK    *ep_rxd_statusblk(EP_RXD *rxd)                { return (&rxd->RxdMain->StatusBlk); }
++EP_RAILMASK      ep_rxd_railmask(EP_RXD *rxd)         { return (rxd->Data.nmd_attr); }
++
++static void
++ProcessNmdMapResponse (EP_RCVR *rcvr, EP_RXD *rxd, EP_MANAGER_MSG *msg)
++{
++    EP_RXD_RAIL  *rxdRail  = rxd->RxdRail;
++    EP_RCVR_RAIL *rcvrRail = rxdRail->RcvrRail;
++    EP_RAIL      *rail     = rcvrRail->CommsRail->Rail;
++    EP_NODE_RAIL *nodeRail = &rail->Nodes[rxd->RxdMain->Envelope.NodeId];
++    int           i;
++
++    ASSERT (msg->Body.MapNmd.nFrags == rxd->nFrags);
++    
++    for (i = 0; i < rxd->nFrags; i++)
++      rxd->Remote[i] = msg->Body.MapNmd.Nmd[i];
++    
++    if (nodeRail->State == EP_NODE_CONNECTED &&       /* node is still connected on this rail */
++      (ep_nmd2railmask (rxd->Local, rxd->nFrags) & ep_nmd2railmask (rxd->Remote, rxd->nFrags) & EP_RAIL2RAILMASK (rail->Number)))     /* NMDs are now valid for this rail */
++    {
++      switch (rxd->State)
++      {
++      case EP_RXD_PUT_STALLED:
++          rxd->State = EP_RXD_PUT_ACTIVE;
++
++          EP_RCVR_OP(rcvrRail, RpcPut) (rxd, rxd->Local, rxd->Remote, rxd->nFrags);
++          break;
++
++      case EP_RXD_GET_STALLED:
++          rxd->State = EP_RXD_GET_ACTIVE;
++
++          EP_RCVR_OP(rcvrRail, RpcGet) (rxd, rxd->Local, rxd->Remote, rxd->nFrags);
++          break;
++          
++      case EP_RXD_COMPLETE_STALLED:
++          rxd->State = EP_RXD_COMPLETE_ACTIVE;
++
++          EP_RCVR_OP(rcvrRail, RpcComplete) (rxd, rxd->Local, rxd->Remote, rxd->nFrags);
++          break;
++
++      default:
++          panic ("ProcessNmdMapResponse: XID match but rxd in invalid state\n");
++          break;
++      }
++
++      rxd->NextRunTime = 0;
++    }
++    else
++      ep_debugf (DBG_MANAGER, "%s: ep_rcvr_xid_msg_handler: rcvr=%p rxd=%p - still cannot proceed\n", rail->Name, rcvr, rxd);
++}
++
++static void
++ProcessFailoverResponse (EP_RCVR *rcvr, EP_RXD *rxd, EP_MANAGER_MSG *msg)
++{
++    /* XXXX - TBD */
++#ifdef NOTYET
++    EP_COMMS_SUBSYS *subsys   = rcvr->Subsys;
++    EP_RXD_RAIL     *rxdRail  = rxd->RxdRail;
++    EP_RCVR_RAIL    *rcvrRail = rxdRail->RcvrRail;
++    EP_RAIL         *rail     = rcvrRail->CommsRail->Rail;
++    EP_RCVR_RAIL    *nRcvrRail;
++    EP_RXD_RAIL     *nRxdRail;
++
++    ASSERT (rxd->RxdMain->Envelope.Attr & EP_RPC);
++
++    EPRINTF6 (DBG_RCVR, "ep_rcvr_xid_msg_handler: rcvr=%p rxd=%p Xid=%016llx state %x.%x - txd on rail %d\n", rcvr, rxd, 
++            rxd->MsgXid.Unique, rxdRail->RxdMain->DataEvent, rxdRail->RxdMain->DoneEvent, msg->Body.FailoverTxd.Rail);
++
++    if ((nRcvrRail = rcvr->Rails[msg->Body.FailoverTxd.Rail]) == NULL ||
++      (nRcvrRail->Rcvr->RailMask & EP_RAIL2RAILMASK (rail->Number)) == NULL)
++    {
++      ep_debugf (DBG_MANAGER, "%s: ep_rcvr_xid_msg_handler: rcvr=%p rxd=%p - still cannot proceed\n", rail->Name, rcvr,rxd);
++      return;
++    }
++
++
++    nRxdRail = EP_RCVR_OP (nrcvrRail, GetRxd) (rcvr, nRcvrRail);
++
++
++    /* If the RPC was in progress, then rollback and mark it as flagged, 
++     * this will then get treated as though the NMDs were not mapped
++     * for the rail when the user initiated the operation.
++     */
++    switch (rxdRail->RxdMain->DataEvent)
++    {
++    case EP_EVENT_ACTIVE|EP_RXD_PHASE_PUT:
++    case EP_EVENT_FLAGGED|EP_RXD_PHASE_PUT:
++      ASSERT (rxdRail->RxdMain->DoneEvent == EP_EVENT_PRIVATE ||
++              rxdRail->RxdMain->DoneEvent == EP_EVENT_PENDING);
++      
++      nRxdRail->RxdMain->DataEvent = EP_EVENT_FLAGGED|EP_RXD_PHASE_PUT;
++      nRxdRail->RxdMain->DoneEvent = EP_EVENT_PENDING;
++      break;
++
++    case EP_EVENT_ACTIVE|EP_RXD_PHASE_GET:
++    case EP_EVENT_FLAGGED|EP_RXD_PHASE_GET:
++      ASSERT (rxdRail->RxdMain->DoneEvent == EP_EVENT_PRIVATE ||
++              rxdRail->RxdMain->DoneEvent == EP_EVENT_PENDING);
++      
++      nRxdRail->RxdMain->DataEvent = EP_EVENT_FLAGGED|EP_RXD_PHASE_GET;
++      nRxdRail->RxdMain->DoneEvent = EP_EVENT_PENDING;
++      break;
++
++    case EP_EVENT_PRIVATE:
++      switch (rxdRail->RxdMain->DoneEvent)
++      {
++      case EP_EVENT_ACTIVE|EP_RXD_PHASE_COMPLETE:
++      case EP_EVENT_FLAGGED|EP_RXD_PHASE_COMPLETE:
++          nRxdRail->RxdMain->DataEvent = EP_EVENT_PRIVATE;
++          nRxdRail->RxdMain->DoneEvent = EP_EVENT_FLAGGED|EP_RXD_PHASE_COMPLETE;
++          break;
++
++      case EP_EVENT_PENDING:
++          break;
++
++      default:
++          panic ("ep_rcvr_xid_msg_handler: rxd in invalid state\n");
++      }
++      break;
++
++    default:
++      panic ("ep_rcvr_xid_msg_handler: rxd in invalid staten");
++    }
++    
++    UnbindRxdFromRail (rxd, rxdRail);
++
++    /* Mark rxdRail as no longer active */
++    rxdRail->RxdMain->DataEvent = EP_EVENT_PRIVATE;
++    rxdRail->RxdMain->DoneEvent = EP_EVENT_PRIVATE;
++
++    sdram_writel (rail->Device, rxdRail->RxdElan + offsetof (EP_RXD_RAIL_ELAN, DataEvent.ev_Count), 0);
++    sdram_writel (rail->Device, rxdRail->RxdElan + offsetof (EP_RXD_RAIL_ELAN, DoneEvent.ev_Count), 0);
++    
++    FreeRxdRail (rcvrRail, rxdRail);
++
++    BindRxdToRail (rxd, nRxdRail);
++
++    ep_kthread_schedule (&subsys->Thread, lbolt);
++#endif
++}
++
++void
++ep_rcvr_xid_msg_handler (void *arg, EP_MANAGER_MSG *msg)
++{
++    EP_RCVR          *rcvr = (EP_RCVR *) arg;
++    struct list_head *el;
++    unsigned long     flags;
++
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    list_for_each (el, &rcvr->ActiveDescList) {
++      EP_RXD *rxd = list_entry (el,EP_RXD, Link);
++
++      if (EP_XIDS_MATCH (msg->Hdr.Xid, rxd->MsgXid))
++      {
++          EP_INVALIDATE_XID (rxd->MsgXid);
++
++          switch (msg->Hdr.Type)
++          {
++          case EP_MANAGER_MSG_TYPE_MAP_NMD_RESPONSE:
++              ProcessNmdMapResponse (rcvr, rxd, msg);
++              break;
++
++          case EP_MANAGER_MSG_TYPE_FAILOVER_RESPONSE:
++              ProcessFailoverResponse (rcvr, rxd, msg);
++              break;
++
++          default:
++              panic ("ep_rcvr_xid_msg_handler: XID match but invalid message type\n");
++          }
++      }
++    }
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++}
++
++
++EP_RCVR *
++ep_alloc_rcvr (EP_SYS *sys, EP_SERVICE svc, unsigned int nenvs)
++{
++    EP_COMMS_SUBSYS  *subsys;
++    EP_RCVR          *rcvr;
++    struct list_head *el;
++    extern int portals_envelopes;
++
++    if (portals_envelopes && (svc == EP_MSG_SVC_PORTALS_SMALL || svc == EP_MSG_SVC_PORTALS_LARGE))
++    {
++      printk ("ep: use %d envelopes rather than %d for portals %s message service\n", sys->Position.pos_nodes * 16, nenvs,
++              svc == EP_MSG_SVC_PORTALS_SMALL ? "small" : "large");
++
++      nenvs = portals_envelopes;
++    }
++
++    if ((subsys = (EP_COMMS_SUBSYS *) ep_subsys_find (sys, EPCOMMS_SUBSYS_NAME)) == NULL)
++      return (NULL);
++
++    KMEM_ZALLOC (rcvr, EP_RCVR *, sizeof (EP_RCVR), 1);
++
++    if (rcvr == NULL)
++      return (NULL);
++
++    rcvr->Subsys            = subsys;
++    rcvr->Service           = svc;
++    rcvr->InputQueueEntries = nenvs;
++    rcvr->FreeDescCount     = 0;
++    rcvr->TotalDescCount    = 0;
++    rcvr->ForwardRxdCount   = 0;
++
++    spin_lock_init (&rcvr->Lock);
++    INIT_LIST_HEAD (&rcvr->ActiveDescList);
++
++    kcondvar_init (&rcvr->CleanupSleep);
++    kcondvar_init (&rcvr->FreeDescSleep);
++    spin_lock_init (&rcvr->FreeDescLock);
++    INIT_LIST_HEAD (&rcvr->FreeDescList);
++    INIT_LIST_HEAD (&rcvr->DescBlockList);
++
++    ep_xid_cache_init (sys, &rcvr->XidCache);
++
++    rcvr->XidCache.MessageHandler = ep_rcvr_xid_msg_handler;
++    rcvr->XidCache.Arg            = rcvr;
++
++    kmutex_lock (&subsys->Lock);
++    /* See if this service is already in use */
++    list_for_each (el, &subsys->Receivers) {
++      EP_RCVR *rcvr = list_entry (el, EP_RCVR, Link);
++
++      if (rcvr->Service == svc)
++      {
++          KMEM_FREE (rcvr, sizeof (EP_RCVR));
++          kmutex_unlock (&subsys->Lock);   
++          return NULL;
++      }
++    }
++    
++    
++    list_add_tail (&rcvr->Link, &subsys->Receivers);
++
++    ep_procfs_rcvr_add(rcvr);
++
++    /* Now add all rails which are already started */
++    list_for_each (el, &subsys->Rails) { 
++      EP_COMMS_RAIL *commsRail = list_entry (el, EP_COMMS_RAIL, Link);
++
++      EP_RAIL_OP (commsRail, Rcvr.AddRail) (rcvr, commsRail);
++    }
++    kmutex_unlock (&subsys->Lock);   
++
++    ep_mod_inc_usecount();
++
++    return (rcvr);
++}
++
++void
++ep_free_rcvr (EP_RCVR *rcvr)
++{
++    EP_COMMS_SUBSYS  *subsys = rcvr->Subsys;
++    EP_SYS           *sys    = subsys->Subsys.Sys;
++    struct list_head  list;
++    struct list_head *el,*nel;
++    unsigned long flags;
++    
++    kmutex_lock (&subsys->Lock);
++    list_for_each (el, &subsys->Rails) { 
++      EP_COMMS_RAIL *commsRail = list_entry (el, EP_COMMS_RAIL, Link);
++      
++      EP_RAIL_OP (commsRail, Rcvr.DelRail) (rcvr, commsRail);
++    }
++
++    ep_procfs_rcvr_del(rcvr);
++
++    list_del (&rcvr->Link);
++    kmutex_unlock (&subsys->Lock);
++
++    INIT_LIST_HEAD (&list);
++
++    /* abort all rxds - should not be bound to a rail */
++    spin_lock_irqsave (&rcvr->Lock, flags);   
++    for (;;)
++    {
++      if (! list_empty (&rcvr->ActiveDescList))
++      {
++          list_for_each_safe (el, nel, &rcvr->ActiveDescList) {
++              EP_RXD *rxd = list_entry (el, EP_RXD, Link);
++              
++              ASSERT (rxd->RxdRail == NULL);
++              ASSERT (rxd->RxdMain->Len == EP_RXD_PENDING);
++              
++              rxd->State = EP_RXD_COMPLETED;
++              rxd->RxdMain->Len = EP_SHUTDOWN;
++              
++              list_del (&rxd->Link);
++              list_add_tail (&rxd->Link, &list);
++          }
++          spin_unlock_irqrestore (&rcvr->Lock, flags);
++          
++          while (! list_empty (&list))
++          {
++              EP_RXD *rxd = list_entry (list.next, EP_RXD, Link);
++              
++              list_del (&rxd->Link);
++              
++              if (rxd->Handler) 
++                  rxd->Handler (rxd);
++          }
++          spin_lock_irqsave (&rcvr->Lock, flags);   
++          continue;
++      }
++
++      if (rcvr->FreeDescCount == rcvr->TotalDescCount)
++          break;
++
++      rcvr->CleanupWaiting++;
++      kcondvar_wait (&rcvr->CleanupSleep, &rcvr->Lock, &flags);
++    }
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++
++    /* must all be in free list */
++    ASSERT( rcvr->FreeDescCount ==  rcvr->TotalDescCount);
++
++    while (! list_empty(& rcvr->DescBlockList) )
++      FreeRxdBlock (rcvr, list_entry (rcvr->DescBlockList.next, EP_RXD_BLOCK, Link));
++
++    /* had better be all gone now */
++    ASSERT((rcvr->FreeDescCount == 0) && (rcvr->TotalDescCount == 0));
++
++    ep_xid_cache_destroy (sys, &rcvr->XidCache);
++ 
++    spin_lock_destroy (&rcvr->Lock);
++    KMEM_FREE (rcvr, sizeof (EP_RCVR));
++
++    ep_mod_dec_usecount();
++}
++
++EP_RXD *
++StealRxdFromOtherRail (EP_RCVR *rcvr)
++{
++    EP_RXD         *rxd;
++    int               i;
++      
++    /* looking at the the rcvr railmask to find a rail to try to steal rxd from */
++    for (i = 0; i < EP_MAX_RAILS; i++) 
++      if (rcvr->RailMask & (1 << i) ) 
++          if ((rxd = EP_RCVR_OP (rcvr->Rails[i], StealRxd) (rcvr->Rails[i])) != NULL)
++              return rxd;
++
++    return NULL;
++}
++
++long
++CheckUnboundRxd (EP_RCVR *rcvr, EP_RXD *rxd, long nextRunTime)
++{
++    EP_SYS       *sys = rcvr->Subsys->Subsys.Sys;
++    EP_RCVR_RAIL *rcvrRail;
++    int           rnum;
++    
++    if ((rnum = ep_rcvr_prefrail (rcvr, EP_NMD_RAILMASK(&rxd->Data))) < 0)
++      rnum = ep_rcvr_prefrail (rcvr, ep_rcvr_availrails (rcvr));
++    
++    if ( rnum < 0 )   {
++      if (nextRunTime == 0 || AFTER (nextRunTime, lbolt + RESOURCE_RETRY_TIME))
++          nextRunTime = lbolt + RESOURCE_RETRY_TIME;
++      
++      return (nextRunTime);
++    }
++
++    ASSERT ( rnum >= 0 );
++
++    rcvrRail = rcvr->Rails[rnum];
++
++    ASSERT ( rcvrRail != NULL);
++
++    rxd->State = EP_RXD_RECEIVE_ACTIVE;
++
++    if ((!(EP_NMD_RAILMASK (&rxd->Data) & EP_RAIL2RAILMASK(rnum)) &&                  /* not mapped already and */
++       ep_nmd_map_rails (sys, &rxd->Data, EP_RAIL2RAILMASK(rnum)) == 0) ||            /* failed mapping, or */
++      !EP_RCVR_OP (rcvrRail, QueueRxd) (rxd, rcvrRail))                               /* failed to queue */
++    {
++      ASSERT (rxd->RxdRail == NULL);
++
++      EPRINTF4 (DBG_RCVR,"CheckUnboundRxd: rcvr=%p rxd=%p -> rnum=%d rcvrRail=%p (failed)\n", rcvr, rxd, rnum, rcvrRail);
++
++      rxd->State = EP_RXD_RECEIVE_UNBOUND;
++      
++      if (nextRunTime == 0 || AFTER (nextRunTime, lbolt + RESOURCE_RETRY_TIME))
++          nextRunTime = lbolt + RESOURCE_RETRY_TIME;
++    }
++
++    return (nextRunTime);
++}
++
++int
++CheckRxdNmdsMapped (EP_RCVR *rcvr, EP_RXD *rxd)
++{
++    EP_RXD_RAIL *rxdRail = rxd->RxdRail;
++    EP_RXD_MAIN *rxdMain = rxd->RxdMain;
++    EP_ENVELOPE *env     = &rxdMain->Envelope;
++    EP_SYS      *sys     = rcvr->Subsys->Subsys.Sys;
++    EP_RAIL     *rail    = rxdRail->RcvrRail->CommsRail->Rail;
++    int                i;
++
++    /* Try and map the local NMDs before checking to see if we can proceed */
++    if (! (ep_nmd2railmask (rxd->Local, rxd->nFrags) & EP_RAIL2RAILMASK (rail->Number)))
++    {
++      EPRINTF3 (DBG_MAPNMD, "%s: rcvr=%p rxd=%p RPC Local NMDs not mapped\n", rail->Name, rcvr, rxd);
++      
++      for (i = 0; i < rxd->nFrags; i++)
++          if (! (EP_NMD_RAILMASK(&rxd->Local[i]) & EP_RAIL2RAILMASK(rail->Number)))
++              if (ep_nmd_map_rails (sys, &rxd->Local[i], EP_RAIL2RAILMASK(rail->Number)))
++                  rxd->NextRunTime = lbolt + RESOURCE_RETRY_TIME;
++    }
++    
++    /* Try and map remote NMDs if they are not valid for this rail */
++    if (! (ep_nmd2railmask (rxd->Remote, rxd->nFrags) & EP_RAIL2RAILMASK (rail->Number)))
++    {
++      EP_MANAGER_MSG_BODY msgBody;
++
++      EPRINTF3 (DBG_MAPNMD, "%s: rcvr=%p rxd=%p RPC Remote NMDs not mapped\n", rail->Name, rcvr, rxd);
++
++      if (EP_XID_INVALID(rxd->MsgXid))
++          rxd->MsgXid = ep_xid_cache_alloc (sys, &rcvr->XidCache);
++
++      msgBody.MapNmd.nFrags   = rxd->nFrags;
++      msgBody.MapNmd.Railmask = EP_RAIL2RAILMASK (rail->Number);
++      for (i = 0; i < rxd->nFrags; i++)
++          msgBody.MapNmd.Nmd[i] = rxd->Remote[i];
++
++      if (ep_send_message (rail, env->NodeId, EP_MANAGER_MSG_TYPE_MAP_NMD_REQUEST, rxd->MsgXid, &msgBody) == 0)
++          rxd->NextRunTime = lbolt + MESSAGE_RETRY_TIME;
++      else
++          rxd->NextRunTime = lbolt + MSGBUSY_RETRY_TIME;
++
++      return 0;
++    }
++
++    if ((ep_nmd2railmask (rxd->Local, rxd->nFrags) & ep_nmd2railmask (rxd->Remote, rxd->nFrags) & EP_RAIL2RAILMASK (rail->Number)) != 0)
++    {
++      rxd->NextRunTime = 0;
++      return 1;
++    }
++
++    return 0;
++}
++
++long
++ep_check_rcvr (EP_RCVR *rcvr, long nextRunTime)
++{
++    struct list_head *el, *nel;
++    unsigned long     flags;
++    int               i;
++
++    /* Check to see if we're low on rxds */
++    if (rcvr->FreeDescCount < ep_rxd_lowat)
++      AllocateRxdBlock (rcvr, 0, NULL);
++
++    for (i = 0; i < EP_MAX_RAILS; i++) 
++      if (rcvr->RailMask & (1 << i) )
++          nextRunTime = EP_RCVR_OP (rcvr->Rails[i], Check) (rcvr->Rails[i], nextRunTime);
++
++    /* See if we have any rxd's which need to be handled */
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    list_for_each_safe (el, nel, &rcvr->ActiveDescList) {
++      EP_RXD      *rxd     = list_entry (el, EP_RXD, Link);
++      EP_RXD_MAIN *rxdMain = rxd->RxdMain;
++      EP_ENVELOPE *env     = &rxdMain->Envelope;
++      EP_RXD_RAIL *rxdRail = rxd->RxdRail;
++
++      if (rxdRail == NULL)
++          nextRunTime = CheckUnboundRxd (rcvr, rxd, nextRunTime);
++      else
++      {
++          EP_RCVR_RAIL *rcvrRail = rxdRail->RcvrRail;
++          EP_RAIL      *rail     = rcvrRail->CommsRail->Rail;
++
++          if (rxd->RxdMain->Len == EP_RXD_PENDING ||                          /* envelope not received yet */
++              rail->Nodes[env->NodeId].State != EP_NODE_CONNECTED)            /* will be failing over */
++              continue;
++
++          switch (rxd->State)
++          {
++          case EP_RXD_PUT_STALLED:
++              if (CheckRxdNmdsMapped (rcvr, rxd))
++              {
++                  rxd->State = EP_RXD_PUT_ACTIVE;
++
++                  EP_RCVR_OP (rcvrRail, RpcPut) (rxd, rxd->Local, rxd->Remote, rxd->nFrags);
++              }
++              break;
++
++          case EP_RXD_GET_STALLED:
++              if (CheckRxdNmdsMapped (rcvr, rxd))
++              {
++                  rxd->State = EP_RXD_GET_ACTIVE;
++
++                  EP_RCVR_OP (rcvrRail, RpcGet) (rxd, rxd->Local, rxd->Remote, rxd->nFrags);
++              }
++              break;
++          
++          case EP_RXD_COMPLETE_STALLED:
++              if (CheckRxdNmdsMapped (rcvr, rxd))
++              {
++                  rxd->State = EP_RXD_COMPLETE_ACTIVE;
++
++                  EP_RCVR_OP (rcvrRail, RpcComplete)(rxd, rxd->Local, rxd->Remote, rxd->nFrags);
++              }
++              break;
++          }
++              
++          if (rxd->NextRunTime && (nextRunTime == 0 || AFTER (nextRunTime, rxd->NextRunTime)))
++              nextRunTime = rxd->NextRunTime;
++      }
++    }
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++    
++    return (nextRunTime);
++}
++
++void
++ep_display_rxd (DisplayInfo *di, EP_RXD *rxd)
++{
++    EP_RXD_MAIN *rxdMain  = rxd->RxdMain;
++    EP_ENVELOPE *env      = &rxdMain->Envelope;
++    EP_RXD_RAIL *rxdRail  = rxd->RxdRail;
++    
++    (di->func)(di->arg, "  RXD: %p State=%x RxdMain=%p(%x.%x.%x) Data=%x.%x.%x %s\n", rxd,
++             rxd->State, rxd->RxdMain, rxd->NmdMain.nmd_addr, rxd->NmdMain.nmd_len,
++             rxd->NmdMain.nmd_attr, rxd->Data.nmd_addr, rxd->Data.nmd_len, rxd->Data.nmd_attr,
++             rxd->RxdMain->Len == EP_RXD_PENDING ? "Pending" : "Active");
++    (di->func)(di->arg, "      NodeId=%d Range=%d.%d TxdRail=%x TxdMain=%x.%x.%x nFrags=%d XID=%08x.%08x.%016llx\n",
++             env->NodeId,  EP_RANGE_LOW(env->Range), EP_RANGE_HIGH(env->Range), env->TxdRail, env->TxdMain.nmd_addr,
++             env->TxdMain.nmd_len, env->TxdMain.nmd_attr, env->nFrags, env->Xid.Generation, env->Xid.Handle, env->Xid.Unique);;
++    (di->func)(di->arg, "      Frag[0] %08x.%08x.%08x\n", env->Frags[0].nmd_addr, env->Frags[0].nmd_len, env->Frags[0].nmd_attr);
++    (di->func)(di->arg, "      Frag[1] %08x.%08x.%08x\n", env->Frags[1].nmd_addr, env->Frags[1].nmd_len, env->Frags[1].nmd_attr);
++    (di->func)(di->arg, "      Frag[2] %08x.%08x.%08x\n", env->Frags[2].nmd_addr, env->Frags[2].nmd_len, env->Frags[2].nmd_attr);
++    (di->func)(di->arg, "      Frag[3] %08x.%08x.%08x\n", env->Frags[3].nmd_addr, env->Frags[3].nmd_len, env->Frags[3].nmd_attr);
++
++    if (rxdRail) EP_RCVR_OP (rxdRail->RcvrRail, DisplayRxd) (di, rxdRail);
++}
++
++void
++ep_display_rcvr (DisplayInfo *di, EP_RCVR *rcvr, int full)
++{
++    int               freeCount    = 0;
++    int                     activeCount  = 0;
++    int                     pendingCount = 0;
++    int                     railCounts[EP_MAX_RAILS];
++    struct list_head *el;
++    int               i;
++    unsigned long     flags;
++
++    for (i = 0; i <EP_MAX_RAILS; i++)
++      railCounts[i] = 0;
++
++    spin_lock_irqsave (&rcvr->FreeDescLock, flags);
++    list_for_each (el, &rcvr->FreeDescList)
++      freeCount++;
++    spin_unlock_irqrestore (&rcvr->FreeDescLock, flags);
++
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    list_for_each (el, &rcvr->ActiveDescList) {
++      EP_RXD      *rxd     = list_entry (el, EP_RXD, Link);
++      EP_RXD_RAIL *rxdRail = rxd->RxdRail;
++
++      if (rxd->RxdMain->Len == EP_RXD_PENDING)
++          pendingCount++;
++      else
++          activeCount++;
++
++      if (rxdRail)
++          railCounts[rxdRail->RcvrRail->CommsRail->Rail->Number]++;
++    }
++
++    (di->func)(di->arg, "RCVR: rcvr=%p number=%d\n", rcvr, rcvr->Service);
++    (di->func)(di->arg, "      RXDS Free=%d (%d) Pending=%d Active=%d Rails=%d.%d.%d.%d\n",
++             freeCount, rcvr->FreeDescCount, pendingCount, activeCount, railCounts[0], railCounts[1],
++             railCounts[2], railCounts[3]);
++
++    for (i = 0; i < EP_MAX_RAILS; i++)
++      if (rcvr->Rails[i] != NULL)
++          EP_RCVR_OP (rcvr->Rails[i], DisplayRcvr) (di, rcvr->Rails[i]);
++
++    list_for_each (el, &rcvr->ActiveDescList) {
++      EP_RXD *rxd = list_entry (el, EP_RXD, Link);
++
++      if (rxd->RxdMain->Len != EP_RXD_PENDING || full)
++          ep_display_rxd (di, rxd);
++    }
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++}
++
++void
++ep_rxd_received_now(EP_RXD *rxd)
++{
++    EP_ENVELOPE   *env  = &rxd->RxdMain->Envelope;
++    EP_RCVR       *rcvr = rxd->Rcvr;
++    unsigned long  flags;
++
++    INC_STAT(rcvr->stats,rx);
++    ADD_STAT(rcvr->stats,rx_len, rxd->RxdMain->Len);
++
++    if (rxd->RxdMain->Len < 0 || !EP_IS_MULTICAST(env->Attr))
++    {
++      rxd->Handler (rxd);
++    }
++    else
++    {
++      EPRINTF5 (DBG_RCVR, "ep_rxd_received: forward rxd=%p Data=%08x.%08x.%08x len=%d\n", rxd, 
++                rxd->Data.nmd_addr, rxd->Data.nmd_len, rxd->Data.nmd_attr, ep_rxd_len(rxd));
++
++      spin_lock_irqsave (&rcvr->Subsys->ForwardDescLock, flags);
++      list_add_tail (&rxd->Link, &rcvr->Subsys->ForwardDescList);
++      spin_unlock_irqrestore (&rcvr->Subsys->ForwardDescLock, flags);
++      
++      ep_kthread_schedule (&rcvr->Subsys->Thread, lbolt);
++    } 
++}
++
++#if defined(CONFIG_EP_NO_CHECK_SUM)
++void
++ep_rxd_received(EP_RXD *rxd) 
++{
++   ep_rxd_received_now(rxd);
++}
++
++#else
++
++void
++ep_rxd_received(EP_RXD *rxd) 
++{
++  EP_ENVELOPE   *env  = &rxd->RxdMain->Envelope;
++
++  if (env->CheckSum) 
++      ep_rxd_queue_csum(rxd);
++  else 
++      ep_rxd_received_now(rxd);
++}
++
++void
++ep_rxd_queue_csum(EP_RXD *rxd)
++{
++    EP_RCVR       *rcvr = rxd->Rcvr;
++    unsigned long flags;
++
++    EPRINTF5 (DBG_RCVR, "ep_rxd_queue_csum: rxd=%p Data=%08x.%08x.%08x len=%d\n", rxd, 
++            rxd->Data.nmd_addr, rxd->Data.nmd_len, rxd->Data.nmd_attr, ep_rxd_len(rxd));
++    
++    spin_lock_irqsave (&rcvr->Subsys->CheckSumDescLock, flags);
++    list_add_tail (&rxd->CheckSumLink, &rcvr->Subsys->CheckSumDescList);
++    spin_unlock_irqrestore (&rcvr->Subsys->CheckSumDescLock, flags);
++    
++    ep_kthread_schedule (&rcvr->Subsys->Thread, lbolt);
++}
++#endif
++
++void
++ep_rcvr_fillout_stats(EP_RCVR *rcvr, char *str)
++{
++    sprintf(str+strlen(str),"Rx     %lu  %lu /sec\n",       GET_STAT_TOTAL(rcvr->stats,rx),      GET_STAT_PER_SEC(rcvr->stats,rx) );
++    sprintf(str+strlen(str),"MBytes %lu  %lu Mbytes/sec\n", GET_STAT_TOTAL(rcvr->stats,rx_len) / (1024*1024),  GET_STAT_PER_SEC(rcvr->stats,rx_len) / (1024*1024));
++}
++
++void
++ep_rcvr_rail_fillout_stats(EP_RCVR_RAIL *rcvr_rail, char *str)
++{
++    sprintf(str+strlen(str),"Rx     %lu  %lu /sec\n",       GET_STAT_TOTAL(rcvr_rail->stats,rx),      GET_STAT_PER_SEC(rcvr_rail->stats,rx) );
++    sprintf(str+strlen(str),"MBytes %lu  %lu Mbytes/sec\n", GET_STAT_TOTAL(rcvr_rail->stats,rx_len) / (1024*1024),  GET_STAT_PER_SEC(rcvr_rail->stats,rx_len) / (1024*1024));
++}
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/ep/epcommsRx_elan3.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/epcommsRx_elan3.c    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/epcommsRx_elan3.c 2005-05-11 12:10:12.495923912 -0400
+@@ -0,0 +1,1776 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: epcommsRx_elan3.c,v 1.19.2.4 2005/03/10 15:24:08 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/epcommsRx_elan3.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++#include <elan/epsvc.h>
++#include <elan/epcomms.h>
++
++#include "kcomm_vp.h"
++#include "kcomm_elan3.h"
++#include "epcomms_elan3.h"
++#include "debug.h"
++
++#define RCVR_TO_RAIL(rcvrRail)                ((EP3_RAIL *) ((EP_RCVR_RAIL *) rcvrRail)->CommsRail->Rail)
++#define RCVR_TO_DEV(rcvrRail)         (RCVR_TO_RAIL(rcvrRail)->Device)
++#define RCVR_TO_SUBSYS(rcvrRail)      (((EP_RCVR_RAIL *) rcvrRail)->Rcvr->Subsys)
++
++static void RxDataEvent (EP3_RAIL *rail, void *arg);
++static void RxDataRetry (EP3_RAIL *rail, void *arg, E3_DMA_BE *dma, int status);
++static void RxDataVerify (EP3_RAIL *rail, void *arg, E3_DMA_BE *dma);
++
++static EP3_COOKIE_OPS RxDataCookieOps = 
++{
++    RxDataEvent,
++    RxDataRetry,
++    NULL, /* DmaCancelled */
++    RxDataVerify,
++};
++
++static void RxDoneEvent (EP3_RAIL *rail, void *arg);
++static void RxDoneRetry (EP3_RAIL *rail, void *arg, E3_DMA_BE *dma, int status);
++static void RxDoneVerify (EP3_RAIL *rail, void *arg, E3_DMA_BE *dma);
++
++static EP3_COOKIE_OPS RxDoneCookieOps = 
++{
++    RxDoneEvent,
++    RxDoneRetry,
++    NULL, /* DmaCancelled */
++    RxDoneVerify,
++};
++
++static int
++AllocateRxdRailBlock (EP3_RCVR_RAIL *rcvrRail)
++{
++    EP3_RAIL        *rail = RCVR_TO_RAIL(rcvrRail);
++    ELAN3_DEV         *dev  = rail->Device;
++    EP3_RXD_RAIL_BLOCK *blk;
++    EP3_RXD_RAIL       *rxdRail;
++    sdramaddr_t        pRxdElan;
++    EP3_RXD_RAIL_MAIN  *pRxdMain;
++    E3_Addr          pRxdElanAddr;
++    E3_Addr          pRxdMainAddr;
++    E3_BlockCopyEvent  event;
++    int                      i, j;
++    unsigned long      flags;
++
++    KMEM_ZALLOC (blk, EP3_RXD_RAIL_BLOCK *, sizeof (EP3_RXD_RAIL_BLOCK), 1);
++    if (blk == NULL)
++      return 0;
++
++    if ((pRxdElan = ep_alloc_elan (&rail->Generic, EP3_RXD_RAIL_ELAN_SIZE * EP3_NUM_RXD_PER_BLOCK, 0, &pRxdElanAddr)) == (sdramaddr_t) 0)
++    {
++      KMEM_FREE (blk, sizeof (EP3_RXD_RAIL_BLOCK));
++      return 0;
++    }
++
++    if ((pRxdMain = ep_alloc_main (&rail->Generic, EP3_RXD_RAIL_MAIN_SIZE * EP3_NUM_RXD_PER_BLOCK, 0, &pRxdMainAddr)) == (sdramaddr_t) 0)
++    {
++      ep_free_elan (&rail->Generic, pRxdElanAddr, EP3_RXD_RAIL_ELAN_SIZE * EP3_NUM_RXD_PER_BLOCK);
++      KMEM_FREE (blk, sizeof (EP3_RXD_RAIL_BLOCK));
++      return 0;
++    }
++    
++    if (ReserveDmaRetries (rail, EP3_NUM_RXD_PER_BLOCK, 0) != ESUCCESS)
++    {
++      ep_free_main (&rail->Generic, pRxdMainAddr, EP3_RXD_RAIL_MAIN_SIZE * EP3_NUM_RXD_PER_BLOCK);
++      ep_free_elan (&rail->Generic, pRxdElanAddr, EP3_RXD_RAIL_ELAN_SIZE * EP3_NUM_RXD_PER_BLOCK);
++      KMEM_FREE (blk, sizeof (EP3_RXD_RAIL_BLOCK));
++      return 0;
++    }
++
++    for (rxdRail = &blk->Rxd[0], i = 0; i < EP3_NUM_RXD_PER_BLOCK; i++, rxdRail++)
++    {
++      rxdRail->Generic.RcvrRail = (EP_RCVR_RAIL *) rcvrRail;
++      rxdRail->RxdElan          = pRxdElan;
++      rxdRail->RxdElanAddr      = pRxdElanAddr;
++      rxdRail->RxdMain          = pRxdMain;
++      rxdRail->RxdMainAddr      = pRxdMainAddr;
++
++      elan3_sdram_writel (dev, pRxdElan + offsetof (EP3_RXD_RAIL_ELAN, RxdMain),  0);
++      elan3_sdram_writel (dev, pRxdElan + offsetof (EP3_RXD_RAIL_ELAN, Next),     0);
++      elan3_sdram_writeq (dev, pRxdElan + offsetof (EP3_RXD_RAIL_ELAN, MainAddr), (long) rxdRail);
++
++      for (j = 0; j < EP_MAXFRAG; j++)
++      {
++          RegisterCookie (&rail->CookieTable, &rxdRail->ChainCookie[j], pRxdElanAddr + offsetof (EP3_RXD_RAIL_ELAN, ChainEvent[j]), &RxDataCookieOps, (void *) rxdRail);
++
++          event.ev_Type  = EV_TYPE_DMA | (pRxdElanAddr + offsetof (EP3_RXD_RAIL_ELAN, Dmas[j+1]));
++          event.ev_Count = 0;
++
++          elan3_sdram_copyl_to_sdram (dev, &event, pRxdElan + offsetof (EP3_RXD_RAIL_ELAN, ChainEvent[j]), sizeof (E3_BlockCopyEvent));
++      }
++      
++      RegisterCookie (&rail->CookieTable, &rxdRail->DataCookie, pRxdElanAddr + offsetof (EP3_RXD_RAIL_ELAN, DataEvent), &RxDataCookieOps, (void *) rxdRail);
++      RegisterCookie (&rail->CookieTable, &rxdRail->DoneCookie, pRxdElanAddr + offsetof (EP3_RXD_RAIL_ELAN, DoneEvent), &RxDoneCookieOps, (void *) rxdRail);
++
++      EP3_INIT_COPY_EVENT (event, rxdRail->DataCookie, pRxdMainAddr + offsetof (EP3_RXD_RAIL_MAIN, DataEvent), 1);
++      elan3_sdram_copyl_to_sdram (dev, &event, pRxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent), sizeof (E3_BlockCopyEvent));
++
++      EP3_INIT_COPY_EVENT (event, rxdRail->DoneCookie, pRxdMainAddr + offsetof (EP3_RXD_RAIL_MAIN, DoneEvent), 1);
++      elan3_sdram_copyl_to_sdram (dev, &event, pRxdElan + offsetof (EP3_RXD_RAIL_ELAN, DoneEvent), sizeof (E3_BlockCopyEvent));
++      
++      pRxdMain->DataEvent = EP3_EVENT_FREE;
++      pRxdMain->DoneEvent = EP3_EVENT_FREE;
++
++      /* move onto next descriptor */
++      pRxdElan     += EP3_RXD_RAIL_ELAN_SIZE;
++      pRxdElanAddr += EP3_RXD_RAIL_ELAN_SIZE;
++      pRxdMain      = (EP3_RXD_RAIL_MAIN *) ((unsigned long) pRxdMain + EP3_RXD_RAIL_MAIN_SIZE);
++      pRxdMainAddr += EP3_RXD_RAIL_MAIN_SIZE;
++    }
++
++    spin_lock_irqsave (&rcvrRail->FreeDescLock, flags);
++
++    list_add  (&blk->Link, &rcvrRail->DescBlockList);
++    rcvrRail->TotalDescCount += EP3_NUM_RXD_PER_BLOCK;
++    rcvrRail->FreeDescCount  += EP3_NUM_RXD_PER_BLOCK;
++
++    for (i = 0; i < EP3_NUM_RXD_PER_BLOCK; i++)
++      list_add (&blk->Rxd[i].Generic.Link, &rcvrRail->FreeDescList);
++
++    spin_unlock_irqrestore (&rcvrRail->FreeDescLock, flags);
++    
++    return 1;
++}
++
++static void
++FreeRxdRailBlock (EP3_RCVR_RAIL *rcvrRail, EP3_RXD_RAIL_BLOCK *blk)
++{
++    EP3_RAIL     *rail = RCVR_TO_RAIL(rcvrRail);
++    EP3_RXD_RAIL *rxdRail;
++    unsigned long flags;
++    int i, j;
++
++    spin_lock_irqsave (&rcvrRail->FreeDescLock, flags);
++
++    list_del (&blk->Link);
++
++    rcvrRail->TotalDescCount -= EP3_NUM_RXD_PER_BLOCK;
++
++    for (rxdRail = &blk->Rxd[0], i = 0; i < EP3_NUM_RXD_PER_BLOCK; i++, rxdRail++)
++    {
++
++      rcvrRail->FreeDescCount--;
++
++      list_del (&rxdRail->Generic.Link);
++      
++      for (j = 0; j < EP_MAXFRAG; j++)
++          DeregisterCookie (&rail->CookieTable, &rxdRail->ChainCookie[j]);
++      
++      DeregisterCookie (&rail->CookieTable, &rxdRail->DataCookie);
++      DeregisterCookie (&rail->CookieTable, &rxdRail->DoneCookie);
++    }
++
++    spin_unlock_irqrestore (&rcvrRail->FreeDescLock, flags);
++
++    ReleaseDmaRetries (rail, EP3_NUM_RXD_PER_BLOCK);
++
++    ep_free_main (&rail->Generic, blk->Rxd[0].RxdMainAddr, EP3_RXD_RAIL_MAIN_SIZE * EP3_NUM_RXD_PER_BLOCK);
++    ep_free_elan (&rail->Generic, blk->Rxd[0].RxdElanAddr, EP3_RXD_RAIL_ELAN_SIZE * EP3_NUM_RXD_PER_BLOCK);
++
++    KMEM_FREE (blk, sizeof (EP3_RXD_RAIL_BLOCK));
++}
++
++static EP3_RXD_RAIL *
++GetRxdRail (EP3_RCVR_RAIL *rcvrRail)
++{
++    EP3_RXD_RAIL *rxdRail;
++    unsigned long flags;
++    int low_on_rxds;
++
++    spin_lock_irqsave (&rcvrRail->FreeDescLock, flags);
++
++    if (list_empty (&rcvrRail->FreeDescList))
++      rxdRail = NULL;
++    else
++    {
++      rxdRail = list_entry (rcvrRail->FreeDescList.next, EP3_RXD_RAIL, Generic.Link);
++
++      list_del (&rxdRail->Generic.Link);
++
++      rcvrRail->FreeDescCount--;
++    }
++
++    /* Wakeup the descriptor primer thread if there's not many left */
++    low_on_rxds = (rcvrRail->FreeDescCount < ep_rxd_lowat);
++
++    spin_unlock_irqrestore (&rcvrRail->FreeDescLock, flags);
++
++    if (low_on_rxds)
++      ep_kthread_schedule (&RCVR_TO_SUBSYS(rcvrRail)->Thread, lbolt);
++
++    return (rxdRail);
++}
++
++static void
++FreeRxdRail (EP3_RCVR_RAIL *rcvrRail, EP3_RXD_RAIL *rxdRail)
++{
++    unsigned long flags;
++
++#if defined(DEBUG_ASSERT)
++    {
++      EP_RAIL  *rail = (EP_RAIL *) RCVR_TO_RAIL(rcvrRail);
++      ELAN3_DEV *dev = RCVR_TO_DEV (rcvrRail);
++
++      EP_ASSERT (rail, rxdRail->Generic.RcvrRail == &rcvrRail->Generic);
++      
++      EP_ASSERT (rail, rxdRail->RxdMain->DataEvent == EP3_EVENT_PRIVATE);
++      EP_ASSERT (rail, rxdRail->RxdMain->DoneEvent == EP3_EVENT_PRIVATE);
++      EP_ASSERT (rail, SDRAM_ASSERT (elan3_sdram_readl (dev, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Count)) == 0));
++      EP_ASSERT (rail, SDRAM_ASSERT (elan3_sdram_readl (dev, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DoneEvent.ev_Count)) == 0));
++
++      rxdRail->RxdMain->DataEvent = EP3_EVENT_FREE;
++      rxdRail->RxdMain->DoneEvent = EP3_EVENT_FREE;
++    }
++#endif
++
++    spin_lock_irqsave (&rcvrRail->FreeDescLock, flags);
++    
++    list_add (&rxdRail->Generic.Link, &rcvrRail->FreeDescList);
++
++    rcvrRail->FreeDescCount++;
++
++    if (rcvrRail->FreeDescWaiting)
++    {
++      rcvrRail->FreeDescWaiting--;
++      kcondvar_wakeupall (&rcvrRail->FreeDescSleep, &rcvrRail->FreeDescLock);
++    }
++
++    spin_unlock_irqrestore (&rcvrRail->FreeDescLock, flags);
++}
++
++static void
++BindRxdToRail (EP_RXD *rxd, EP3_RXD_RAIL *rxdRail)
++{
++    EP3_RAIL *rail = RCVR_TO_RAIL (rxdRail->Generic.RcvrRail);
++
++    ASSERT (SPINLOCK_HELD (&rxd->Rcvr->Lock));
++
++    EPRINTF3 (DBG_RCVR, "%s: BindRxdToRail: rxd=%p rxdRail=%p\n",  rail->Generic.Name, rxd, rxdRail);
++
++    elan3_sdram_writel (rail->Device, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, RxdMain), rxd->NmdMain.nmd_addr);                       /* PCI write */
++
++    rxd->RxdRail         = &rxdRail->Generic;
++    rxdRail->Generic.Rxd = rxd;
++}
++
++static void
++UnbindRxdFromRail (EP_RXD *rxd, EP3_RXD_RAIL *rxdRail)
++{
++    EP3_RCVR_RAIL *rcvrRail = (EP3_RCVR_RAIL *) rxdRail->Generic.RcvrRail;
++
++    ASSERT (SPINLOCK_HELD (&rxd->Rcvr->Lock));
++    ASSERT (rxd->RxdRail == &rxdRail->Generic && rxdRail->Generic.Rxd == rxd);
++    
++    EPRINTF3 (DBG_RCVR, "%s: UnbindRxdFromRail: rxd=%p rxdRail=%p\n",  RCVR_TO_RAIL(rxdRail->Generic.RcvrRail)->Generic.Name, rxd, rxdRail);
++
++    rxd->RxdRail         = NULL;
++    rxdRail->Generic.Rxd = NULL;
++
++    if (rcvrRail->CleanupWaiting)
++      kcondvar_wakeupall (&rcvrRail->CleanupSleep, &rxd->Rcvr->Lock);
++    rcvrRail->CleanupWaiting = 0;
++}
++
++static void
++LockRcvrThread (EP3_RCVR_RAIL *rcvrRail)
++{
++    EP_COMMS_RAIL     *commsRail   = rcvrRail->Generic.CommsRail;
++    EP3_RAIL          *rail        = RCVR_TO_RAIL(rcvrRail);
++    ELAN3_DEV       *dev         = rail->Device;
++    sdramaddr_t        sle         = rcvrRail->RcvrElan + offsetof (EP3_RCVR_RAIL_ELAN, ThreadLock);
++    EP3_SPINLOCK_MAIN *sl          = &rcvrRail->RcvrMain->ThreadLock;
++    E3_uint32          RestartBits = 0;
++    int                delay       = 1;
++    E3_uint32          seq;
++    E3_uint32          reg;
++
++    ASSERT (SPINLOCK_HELD (&rcvrRail->Generic.Rcvr->Lock));
++
++    mb();
++    elan3_sdram_writel (dev, sle + offsetof (EP3_SPINLOCK_ELAN, sl_lock), 1);
++    mb();
++    seq = elan3_sdram_readl (dev, sle + offsetof (EP3_SPINLOCK_ELAN, sl_seq));
++    while (seq != sl->sl_seq)
++    {
++      while (sl->sl_seq == (seq - 1))
++      {
++          mb();
++
++          if ((read_reg32 (dev, Exts.InterruptReg) & (INT_TProc | INT_TProcHalted)) != 0 && spin_trylock (&dev->IntrLock))
++          {
++              reg=read_reg32 (dev, Exts.InterruptReg);
++              ELAN_REG_REC(reg);
++
++              if ((reg & (INT_TProc | INT_TProcHalted)) != 0&& 
++                  elan3_sdram_readl (dev, sle + offsetof (EP3_SPINLOCK_ELAN, sl_seq)) != sl->sl_seq)
++              {
++                  EPRINTF1 (DBG_RCVR, "%s: LockRcvrThread - thread trapped\n", rail->Generic.Name);
++
++                  /* The thread processor has *really* trapped, and the spinlock is still held.
++                   * thus is must have trapped due to a network error - we need to complete the
++                   * actions required for this envelope, since we may be spin-locking the receiver
++                   * to search the dma retry lists for a particular dma.  So must ensure that
++                   * if the thread had trapped then the dma has been queued onto the retry list
++                   * *before* we inspect them.
++                   */
++                  IncrStat (commsRail, LockRcvrTrapped);
++
++                  /* We're going to generate a spurious interrupt here - since we will
++                   * handle the thread processor trap directly */
++                  ELAN_REG_REC(reg);
++                  if (HandleTProcTrap (dev, &RestartBits))
++                  {
++                      /* NOTE - this is not an assert, since the "store" to unlock the lock could
++                       *        be held up on the PCI interface, whilst the thread processor has
++                       *        gone on and switched to a new thread, which has then trapped, and
++                       *        our read of the InterruptReg can overtake the unlock write.
++                       *
++                       * ASSERT (dev->ThreadTrap->Registers[REG_GLOBALS + (1^WordEndianFlip)] == 
++                       *         elan3_sdram_readl (dev, rcvr->RcvrElan + offsetof (EP_RCVR_ELAN, PendingRxDescsElan)));
++                       */
++
++                      PULSE_SCHED_STATUS (dev, RestartBits);
++
++                      DeliverTProcTrap (dev, dev->ThreadTrap, INT_TProc);
++                  }
++              }
++              spin_unlock (&dev->IntrLock);
++          }
++          
++          DELAY (delay); delay++;
++      }
++      seq = elan3_sdram_readl (dev, sle + offsetof (EP3_SPINLOCK_ELAN, sl_seq));
++    }
++}
++
++static void
++UnlockRcvrThread (EP3_RCVR_RAIL *rcvrRail)
++{
++    EP3_RAIL   *rail = RCVR_TO_RAIL(rcvrRail);
++    sdramaddr_t sle  = rcvrRail->RcvrElan + offsetof (EP3_RCVR_RAIL_ELAN, ThreadLock);
++
++    mb();
++    elan3_sdram_writel (rail->Device, sle + offsetof (EP3_SPINLOCK_ELAN, sl_lock), 0);
++    mmiob(); 
++}
++
++void
++CompleteEnvelope (EP3_RAIL *rail, E3_Addr rxdElanAddr, E3_uint32 PAckVal)
++{
++    ELAN3_DEV         *dev       = rail->Device;
++    sdramaddr_t        rxdElan   = ep_elan2sdram (&rail->Generic, rxdElanAddr);
++    EP3_RXD_RAIL      *rxdRail   = (EP3_RXD_RAIL *) (unsigned long) elan3_sdram_readq (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, MainAddr));
++    EP_RXD_MAIN       *rxdMain   = rxdRail->Generic.Rxd->RxdMain;
++    EP_ENVELOPE       *env       = &rxdMain->Envelope;
++    EP3_RCVR_RAIL     *rcvrRail  = (EP3_RCVR_RAIL *) rxdRail->Generic.RcvrRail;
++    EP_COMMS_RAIL     *commsRail = rcvrRail->Generic.CommsRail;
++    EP_RCVR           *rcvr      = rcvrRail->Generic.Rcvr;
++    sdramaddr_t        queue     = ((EP3_COMMS_RAIL *) commsRail)->QueueDescs + rcvr->Service * sizeof (EP3_InputQueue);
++    sdramaddr_t        sle       = rcvrRail->RcvrElan + offsetof (EP3_RCVR_RAIL_ELAN, ThreadLock);
++    EP3_SPINLOCK_MAIN *sl        = &rcvrRail->RcvrMain->ThreadLock;
++    int               nodeId;
++    EP_NODE_RAIL     *nodeRail;
++    E3_DMA_BE         dma;
++    E3_Addr           nfptr;
++    E3_Addr         next;
++
++    ASSERT (commsRail->Rail == &rail->Generic);
++    ASSERT (rxdElanAddr == elan3_sdram_readl (dev, rcvrRail->RcvrElan + offsetof (EP3_RCVR_RAIL_ELAN, PendingDescs)));
++
++    IncrStat (commsRail, CompleteEnvelope);
++
++    /* We don't need to aquire the NodeLock here (however we might be holding it),
++     * since this can only get called while the node is connected, or disconnecting.
++     * If the node is disconnecting, then we can get called from FlushDisconnecting()
++     * while holding the NodeLock - after we cannot get called again until the node 
++     * has reconnected from scratch.
++     */
++    /* Copy the envelope information */
++    nfptr = elan3_sdram_readl (dev, queue + offsetof (EP3_InputQueue, q_fptr));
++
++    if (nfptr == elan3_sdram_readl (dev, queue + offsetof (EP3_InputQueue, q_top)))
++      nfptr = elan3_sdram_readl (dev, queue + offsetof (EP3_InputQueue, q_base));
++    else
++      nfptr += elan3_sdram_readl (dev, queue + offsetof (EP3_InputQueue, q_size));
++
++    /* Copy the envelope and payload (unconditionally) */
++    elan3_sdram_copyl_from_sdram (dev, rcvrRail->InputQueueBase + (nfptr - rcvrRail->InputQueueAddr), env, EP_ENVELOPE_SIZE + EP_PAYLOAD_SIZE);
++
++    ASSERT (env->Version == EP_ENVELOPE_VERSION);
++
++    /* Copy the received message length */
++    rxdMain->Len = elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, Data.nmd_len));
++      
++    /* Remove the RXD from the pending desc list */
++    if ((next = elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, Next))) == 0)
++      rcvrRail->RcvrMain->PendingDescsTailp = 0;
++    elan3_sdram_writel (dev, rcvrRail->RcvrElan + offsetof (EP3_RCVR_RAIL_ELAN, PendingDescs), next);
++
++    /* Copy the DMA descriptor to queue on the approriate retry list */
++    elan3_sdram_copyq_from_sdram (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, Dmas[0]), &dma, sizeof (E3_DMA));       /* PCI read block */
++    
++    EP_ASSERT (&rail->Generic, dma.s.dma_direction == DMA_READ);;
++
++#if defined(DEBUG_ASSERT) && defined(DEBUG_SDRAM_ASSERT)
++    /* NOTE: not an assertion, since the thread packet could have successfully
++     *       transferred the "put" dma to the far side - which could then have
++     *       completed - but the far side will see a network error which will
++     *       cause the virtual circuit to be dropped by the far side and this 
++     *       DMA will be removed */
++    if (rxdRail->RxdMain->DataEvent != EP3_EVENT_ACTIVE ||
++      elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Count)) != 1)
++    {
++      printk ("CompleteEnvelope: suspicious dma : Node=%d DataBlock=%d Event=%d\n", 
++              env->NodeId, rxdRail->RxdMain->DataEvent, 
++              elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Count)));
++    }
++#endif
++    
++    EPRINTF6 (DBG_RCVR, "%s: CompleteEnvelope: rxd=%p NodeId=%d Xid=%llx Cookies=%08x,%08x\n", commsRail->Rail->Name,
++            rxdRail, env->NodeId, (long long) env->Xid.Unique, dma.s.dma_srcCookieVProc, dma.s.dma_destCookieVProc);
++
++    /* we MUST convert this into a DMA_READ_REQUEUE dma as if we don't the DMA descriptor will
++     * be read from the EP_RETRY_DMA rather than the original DMA - this can then get reused 
++     * and an incorrect DMA descriptor sent */
++    dma.s.dma_source    = rxdRail->RxdElanAddr + offsetof (EP3_RXD_RAIL_ELAN, Dmas[0]);
++    dma.s.dma_direction = (dma.s.dma_direction & ~DMA_READ) | DMA_READ_REQUEUE;
++    
++    nodeId   = EP_VP_TO_NODE(dma.s.dma_srcVProc);
++    nodeRail = &rail->Generic.Nodes[nodeId];
++
++    ASSERT (nodeRail->State >= EP_NODE_CONNECTED && nodeRail->State <= EP_NODE_LOCAL_PASSIVATE);
++
++    if (PAckVal != E3_PAckOk)
++    {
++      if (nodeRail->State == EP_NODE_CONNECTED)
++          QueueDmaForRetry (rail, &dma, EP_RETRY_LOW_PRI_RETRY);
++      else
++          QueueDmaOnStalledList (rail, &dma);
++    }
++
++    /* Finaly forcefully drop the spinlock for the thread */
++    sl->sl_seq = elan3_sdram_readl (dev, sle + offsetof (EP3_SPINLOCK_ELAN, sl_seq));
++
++    wmb();
++}
++
++void
++StallThreadForNoDescs (EP3_RAIL *rail, E3_Addr rcvrElanAddr, E3_Addr sp)
++{
++    ELAN3_DEV      *dev       = rail->Device;
++    sdramaddr_t    rcvrElan   = ep_elan2sdram (&rail->Generic, rcvrElanAddr);
++    EP3_RCVR_RAIL  *rcvrRail  = (EP3_RCVR_RAIL *) (unsigned long) elan3_sdram_readq (dev, rcvrElan + offsetof (EP3_RCVR_RAIL_ELAN, MainAddr));
++    EP_RCVR        *rcvr      = rcvrRail->Generic.Rcvr;
++    EP_COMMS_RAIL  *commsRail = rcvrRail->Generic.CommsRail;
++
++    EPRINTF3 (DBG_RCVR, "%s: StallThreadForNoDescs - rcvrRail=%p sp=%x\n", commsRail->Rail->Name, rcvrRail, sp);
++    
++    IncrStat (commsRail, StallThread);
++
++    /* NOTE: spin lock not required as thread is trapped */
++    
++    if (rcvrRail->RcvrMain->PendingDescsTailp != 0)
++    {
++      EPRINTF1 (DBG_RCVR, "%s: StallThreadForNoDescs - pending descriptors, wakeup thread\n", commsRail->Rail->Name);
++      
++      /*
++       * A receive buffer was queued after the thread had decided to go to 
++       * sleep, but before the event interrupt occured.  Just restart the
++       * thread to consume the envelope.
++       */
++      IssueRunThread (rail, sp);
++    }
++    else
++    {
++      EPRINTF1 (DBG_RCVR, "%s: StallThreadForNoDescs - set ThreadWaiting\n", commsRail->Rail->Name);
++      
++      IncrStat (commsRail, ThrdWaiting);
++
++      /* Mark the rcvr as waiting for a rxd, and schedule a call of ep_check_rcvr
++       * to attempt to "steal" a descriptor from a different rail */
++      rcvrRail->ThreadWaiting = sp;
++
++      ep_kthread_schedule (&rcvr->Subsys->Thread, lbolt);
++    }
++}
++
++void
++StallThreadForHalted (EP3_RAIL *rail, E3_Addr rcvrElanAddr, E3_Addr sp)
++{
++    ELAN3_DEV     *dev       = rail->Device;
++    sdramaddr_t    rcvrElan  = ep_elan2sdram (&rail->Generic, rcvrElanAddr);
++    EP3_RCVR_RAIL *rcvrRail  = (EP3_RCVR_RAIL *) (unsigned long) elan3_sdram_readq (dev, rcvrElan + offsetof (EP3_RCVR_RAIL_ELAN, MainAddr));
++    EP_RCVR       *rcvr      = rcvrRail->Generic.Rcvr;
++    unsigned long  flags     = 0;
++
++    spin_lock_irqsave (&rcvr->Lock, flags);
++
++    rcvrRail->ThreadHalted = sp;
++
++    EPRINTF2 (DBG_EPTRAP, "%s: StallThreadForHalted: sp=%08x\n", rail->Generic.Name, sp);
++
++    if (rcvrRail->CleanupWaiting)
++      kcondvar_wakeupone (&rcvrRail->CleanupSleep, &rcvr->Lock);
++    rcvrRail->CleanupWaiting = 0;
++
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++}
++/*
++ * RxDataEvent: arg == EP3_RXD_RAIL
++ *   Called on completion of receiving data.
++ */
++static void
++RxDataEvent (EP3_RAIL *rail, void *arg)
++{
++    EP3_RXD_RAIL  *rxdRail  = (EP3_RXD_RAIL *) arg;
++    EP3_RCVR_RAIL *rcvrRail = (EP3_RCVR_RAIL *) rxdRail->Generic.RcvrRail;
++    EP_RXD        *rxd      = rxdRail->Generic.Rxd;
++    EP_ENVELOPE   *env      = &rxd->RxdMain->Envelope;
++    EP_RCVR       *rcvr     = rxd->Rcvr;
++    ELAN3_DEV   *dev      = rail->Device;
++    unsigned long flags;
++    int delay = 1;
++
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    for (;;)
++    {
++      if (EP3_EVENT_FIRED (rxdRail->DataCookie, rxdRail->RxdMain->DataEvent))
++          break;
++
++      if (EP3_EVENT_FIRING (dev, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent), rxdRail->DataCookie, rxdRail->RxdMain->DataEvent))
++      {
++          if (delay > EP3_EVENT_FIRING_TLIMIT)
++              panic ("RxDataEvent: events set but block copy not completed\n");
++          DELAY(delay);
++          delay <<= 1;
++      }
++      else
++      {
++          printk ("%s: RxDataEvent: rxd %p not complete [%x,%x,%x]\n", rail->Generic.Name, rxd, rxdRail->RxdMain->DataEvent,
++                  elan3_sdram_readl (dev, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Count)),
++                  elan3_sdram_readl (dev, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Type)));
++          
++          spin_unlock_irqrestore (&rcvr->Lock, flags);
++          return;
++      }
++      mb();
++    }
++
++    /*
++     * Note, since the thread will have sent the "get" dma before copying the
++     * envelope, we must check that it has completed doing this,  if not then
++     * it might be that the thread trapped due to a network error, so we must
++     * spinlock against the thread 
++     */
++    if (rxd->RxdMain->Len == EP_RXD_PENDING)
++    {
++      LockRcvrThread (rcvrRail);
++      UnlockRcvrThread (rcvrRail);
++
++      ASSERT (env->Version == EP_ENVELOPE_VERSION && rxd->RxdMain->Len != EP_RXD_PENDING);
++    }
++
++    EPRINTF7 (DBG_RCVR, "%s: RxDataEvent: rxd=%p rxdRail=%p completed from elan node %d [XID=%llx] Length %d State %x\n", 
++            rail->Generic.Name, rxd, rxdRail, env->NodeId, (long long) env->Xid.Unique, rxd->RxdMain->Len, rxd->State);
++
++    EP_ASSERT (&rail->Generic, rxd->State == EP_RXD_RECEIVE_ACTIVE || rxd->State == EP_RXD_PUT_ACTIVE || rxd->State == EP_RXD_GET_ACTIVE);
++    EP_ASSERT (&rail->Generic, SDRAM_ASSERT (elan3_sdram_readl (dev, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Count)) == 0));     /* PCI read */
++    EP_ASSERT (&rail->Generic, rxdRail->RxdMain->DoneEvent == EP3_EVENT_PRIVATE);
++
++    rxdRail->RxdMain->DataEvent = EP3_EVENT_PRIVATE;
++    rxd->Data.nmd_attr          = EP_RAIL2RAILMASK (rail->Generic.Number);
++
++    if (rxd->RxdMain->Len >= 0 && EP_IS_RPC(env->Attr))
++      rxd->State = EP_RXD_RPC_IN_PROGRESS;
++    else
++    {
++      rxd->State = EP_RXD_COMPLETED;
++
++      /* remove from active list */
++      list_del (&rxd->Link);
++
++      UnbindRxdFromRail (rxd, rxdRail);
++      FreeRxdRail (rcvrRail, rxdRail);
++    }
++
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++    ep_rxd_received (rxd);
++
++}
++
++/* 
++ * RxDataRetry: arg == EP3_RXD_RAIL
++ *   Called on retry of "get" dma of large transmit data
++ *   and rpc_get/rpc_put and "put" of datavec of rpc completion.
++ */
++static void
++RxDataRetry (EP3_RAIL *rail, void *arg, E3_DMA_BE *dma, int status)
++{
++    EP3_RXD_RAIL  *rxdRail  = (EP3_RXD_RAIL *) arg;
++    EP_COMMS_RAIL *commsRail = rxdRail->Generic.RcvrRail->CommsRail;
++    EP_RXD        *rxd       = rxdRail->Generic.Rxd;
++
++#if defined(DEBUG_ASSERT)
++    RxDataVerify (rail, arg, dma);
++#endif
++
++    IncrStat (commsRail, RxDataRetry);
++
++    EPRINTF4 (DBG_RCVR, "%s: RxDataRetry: rcvr %p rxd %p [XID=%llx]\n", rail->Generic.Name, rxd->Rcvr, rxd, (long long) rxd->RxdMain->Envelope.Xid.Unique);
++
++    QueueDmaForRetry (rail, dma, EP_RETRY_LOW_PRI_RETRY + ep_backoff (&rxdRail->Backoff, EP_BACKOFF_DATA));
++}
++
++static void
++RxDataVerify (EP3_RAIL *rail, void *arg, E3_DMA_BE *dma)
++{
++#if defined(DEBUG_ASSERT)
++    EP3_RXD_RAIL   *rxdRail  = (EP3_RXD_RAIL *) arg;
++    EP_RXD        *rxd       = rxdRail->Generic.Rxd;
++
++    if (dma->s.dma_direction == DMA_WRITE)
++    {
++      EP_ASSERT (&rail->Generic, 
++                 (rxd->State == EP_RXD_RECEIVE_ACTIVE  && rxdRail->RxdMain->DataEvent == EP3_EVENT_ACTIVE && rxdRail->RxdMain->DoneEvent == EP3_EVENT_PRIVATE) ||
++                 (rxd->State == EP_RXD_PUT_ACTIVE      && rxdRail->RxdMain->DataEvent == EP3_EVENT_ACTIVE && rxdRail->RxdMain->DoneEvent == EP3_EVENT_PRIVATE) ||
++                 (rxd->State == EP_RXD_COMPLETE_ACTIVE && rxdRail->RxdMain->DataEvent == EP3_EVENT_PRIVATE && rxdRail->RxdMain->DoneEvent == EP3_EVENT_ACTIVE));
++      EP_ASSERT (&rail->Generic, SDRAM_ASSERT (rxd->State == EP_RXD_COMPLETE_ACTIVE ?
++                                               elan3_sdram_readl (rail->Device, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DoneEvent.ev_Count)) == 1:            /* PCI read */
++                                               elan3_sdram_readl (rail->Device, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Count)) == 1));          /* PCI read */
++    }
++    else
++    {
++      EP_ASSERT (&rail->Generic, dma->s.dma_direction == DMA_READ_REQUEUE);
++      
++#if defined(DEBUG_SDRAM_ASSERT)
++      /* NOTE: not an assertion, since the "get" DMA can still be running if
++       *       it's packet got a network error - and then the "put" from the
++       *       far side has completed - however the virtual circuit should
++       *       then be dropped by the far side and this DMA will be removed */
++      if (EP_VP_TO_NODE(dma->s.dma_srcVProc) != ep_rxd_node(rxd) || 
++          (rxd->State != EP_RXD_RECEIVE_ACTIVE && rxd->State != EP_RXD_GET_ACTIVE) ||
++          rxdRail->RxdMain->DataEvent != EP3_EVENT_ACTIVE ||
++          elan3_sdram_readl (rail->Device, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Count)) != 1)
++      {
++          EPRINTF6 (DBG_RCVR, "%s: RxDataRetry: suspicious dma : VProc=%d NodeId=%d State=%d DataBlock=%x Event=%d\n",  
++                    rail->Generic.Name, EP_VP_TO_NODE(dma->s.dma_srcVProc), ep_rxd_node(rxd), rxd->State, rxdRail->RxdMain->DataEvent, 
++                    elan3_sdram_readl (rail->Device, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Count)));
++      }
++#endif /* defined(DEBUG_SDRAM_ASSERT) */
++    }
++#endif /* DEBUG_ASSERT */
++}
++
++/*
++ * RxDoneEvent: arg == EP_RXD
++ *   Called on completion of large receive.
++ */
++static void
++RxDoneEvent (EP3_RAIL *rail, void *arg)
++{
++    EP3_RXD_RAIL  *rxdRail   = (EP3_RXD_RAIL *) arg;
++    EP3_RCVR_RAIL *rcvrRail  = (EP3_RCVR_RAIL *) rxdRail->Generic.RcvrRail;
++    EP_COMMS_RAIL *commsRail = rcvrRail->Generic.CommsRail;
++    EP_RXD        *rxd       = rxdRail->Generic.Rxd;
++    EP_RCVR       *rcvr      = rxd->Rcvr;
++    ELAN3_DEV   *dev       = rail->Device;
++    int            delay     = 1;
++    unsigned long  flags;
++
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    for (;;)
++    {
++      if (EP3_EVENT_FIRED (rxdRail->DoneCookie, rxdRail->RxdMain->DoneEvent))
++          break;
++      
++      if (EP3_EVENT_FIRING (dev, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DoneEvent), rxdRail->DoneCookie, rxdRail->RxdMain->DoneEvent))
++      {
++          if (delay > EP3_EVENT_FIRING_TLIMIT)
++              panic ("RxDoneEvent: events set but block copy not completed\n");
++          DELAY(delay);
++          delay <<= 1;
++      }
++      else
++      {
++          printk ("RxDoneEvent: rxd %p not complete [%x,%x.%x]\n", rxd, rxdRail->RxdMain->DoneEvent,
++                  elan3_sdram_readl (dev, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DoneEvent.ev_Count)),
++                  elan3_sdram_readl (dev, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DoneEvent.ev_Type)));
++          
++          spin_unlock_irqrestore (&rcvr->Lock, flags);
++          return;
++      }
++      mb();
++    }
++
++    EPRINTF4 (DBG_RCVR, "%s: RxDoneEvent: rxd %p completed from elan node %d [XID=%llx]\n", 
++            commsRail->Rail->Name, rxd, rxd->RxdMain->Envelope.NodeId, (long long) rxd->RxdMain->Envelope.Xid.Unique);
++    
++    IncrStat (commsRail, RxDoneEvent);
++
++    EP_ASSERT (&rail->Generic, rxdRail->RxdMain->DataEvent  == EP3_EVENT_PRIVATE);
++    EP_ASSERT (&rail->Generic, EP3_EVENT_FIRED (rxdRail->DoneCookie, rxdRail->RxdMain->DoneEvent));
++    EP_ASSERT (&rail->Generic, SDRAM_ASSERT (elan3_sdram_readl (dev, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Count)) == 0));     /* PCI read */
++    EP_ASSERT (&rail->Generic, SDRAM_ASSERT (elan3_sdram_readl (dev, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DoneEvent.ev_Count)) == 0));     /* PCI read */
++
++    /* mark rxd as private  */
++    rxdRail->RxdMain->DoneEvent = EP3_EVENT_PRIVATE;
++
++    /* remove from active list */
++    list_del (&rxd->Link);
++
++    UnbindRxdFromRail (rxd, rxdRail);
++    FreeRxdRail (rcvrRail, rxdRail);
++      
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++
++    rxd->Handler (rxd);
++}
++
++/* 
++ * RxDoneRetry: arg == EP_RXD
++ *   Called on retry of "put" of RPC completion status block
++ */
++static void
++RxDoneRetry (EP3_RAIL *rail, void *arg, E3_DMA_BE *dma, int status)
++{
++    EP3_RXD_RAIL  *rxdRail   = (EP3_RXD_RAIL *) arg;
++    EP_COMMS_RAIL *commsRail = rxdRail->Generic.RcvrRail->CommsRail;
++    EP_RXD        *rxd       = rxdRail->Generic.Rxd;
++
++#if defined(DEBUG_ASSERT)
++    RxDoneVerify (rail, arg, dma);
++#endif
++
++    IncrStat (commsRail, RxDoneRetry);
++
++    EPRINTF4 (DBG_RCVR, "%s: RxDoneRetry: rcvr %p rxd %p [XID=%llx]\n", commsRail->Rail->Name, rxd->Rcvr, rxd, (long long) rxd->RxdMain->Envelope.Xid.Unique);
++
++    QueueDmaForRetry (rail, dma, EP_RETRY_LOW_PRI_RETRY + ep_backoff (&rxdRail->Backoff, EP_BACKOFF_DONE));
++}
++
++static void
++RxDoneVerify (EP3_RAIL *rail, void *arg, E3_DMA_BE *dma)
++{
++#if defined(DEBUG_ASSERT)
++    EP3_RXD_RAIL *rxdRail = (EP3_RXD_RAIL *) arg;
++    EP_RXD       *rxd     = rxdRail->Generic.Rxd;
++
++    EP_ASSERT (&rail->Generic, dma->s.dma_direction == DMA_WRITE && EP_VP_TO_NODE(dma->s.dma_destVProc) == ep_rxd_node(rxd));
++    EP_ASSERT (&rail->Generic, rxd->State == EP_RXD_COMPLETE_ACTIVE && rxdRail->RxdMain->DoneEvent  == EP3_EVENT_ACTIVE);
++    EP_ASSERT (&rail->Generic, SDRAM_ASSERT (elan3_sdram_readl (rail->Device, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DoneEvent.ev_Count)) == 1));    /* PCI read */
++#endif /* defined(DEBUG_ASSERT) */
++}
++
++int
++ep3rcvr_queue_rxd (EP_RXD *rxd, EP_RCVR_RAIL *r)
++{
++    EP3_RCVR_RAIL *rcvrRail = (EP3_RCVR_RAIL *) r;
++    EP3_RAIL      *rail     = RCVR_TO_RAIL(rcvrRail);
++    ELAN3_DEV     *dev      = rail->Device;
++    EP3_RXD_RAIL  *rxdRail;
++
++    ASSERT ( SPINLOCK_HELD(&rxd->Rcvr->Lock));
++
++    if ((rxdRail = GetRxdRail (rcvrRail)) == NULL)
++      return 0;
++
++    /* Flush the Elan TLB if mappings have changed */
++    ep_perrail_dvma_sync (&rail->Generic);
++
++    elan3_sdram_writel (dev, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, Data.nmd_addr), rxd->Data.nmd_addr);             /* PCI write */
++    elan3_sdram_writel (dev, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, Data.nmd_len),  rxd->Data.nmd_len);              /* PCI write */
++    elan3_sdram_writel (dev, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, Data.nmd_attr), rxd->Data.nmd_attr);             /* PCI write */
++
++    /* Bind the rxdRail and rxd together */
++    BindRxdToRail (rxd, rxdRail);
++    
++    /* Mark as active */
++    elan3_sdram_writel (dev, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Count), 1);
++
++    rxdRail->RxdMain->DataEvent  = EP3_EVENT_ACTIVE;
++    rxdRail->RxdMain->DoneEvent  = EP3_EVENT_PRIVATE;
++
++    /* Interlock with StallThreadForNoDescs */
++    spin_lock (&dev->IntrLock);
++
++    EPRINTF4 (DBG_RCVR, "%s: ep3rcvr_queue_rxd: rcvr %p rxd %p rxdRail %p\n", rail->Generic.Name, rxd->Rcvr, rxd, rxdRail);
++
++    EP3_SPINENTER (dev, rcvrRail->RcvrElan + offsetof (EP3_RCVR_RAIL_ELAN, PendingLock), &rcvrRail->RcvrMain->PendingLock);
++
++    elan3_sdram_writel (dev, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, Next), 0);                                       /* PCI write */
++    if (rcvrRail->RcvrMain->PendingDescsTailp == 0)
++      elan3_sdram_writel (dev, rcvrRail->RcvrElan + offsetof (EP3_RCVR_RAIL_ELAN, PendingDescs), rxdRail->RxdElanAddr);       /* PCI write */
++    else
++      elan3_sdram_writel (dev, rcvrRail->RcvrMain->PendingDescsTailp, rxdRail->RxdElanAddr);                          /* PCI write */
++    rcvrRail->RcvrMain->PendingDescsTailp = rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, Next);
++    
++    EP3_SPINEXIT (dev, rcvrRail->RcvrElan + offsetof (EP3_RCVR_RAIL_ELAN, PendingLock), &rcvrRail->RcvrMain->PendingLock);
++
++    /* If the thread has paused because it was woken up with no receive buffer */
++    /* ready, then wake it up to process the one we've just added */
++    if (rcvrRail->ThreadWaiting)
++    {
++      EPRINTF1 (DBG_RCVR, "%s: DoReceive: ThreadWaiting - restart thread\n", rail->Generic.Name);
++
++      IssueRunThread (rail, rcvrRail->ThreadWaiting);
++
++      rcvrRail->ThreadWaiting = (E3_Addr) 0;
++    }
++
++    spin_unlock (&dev->IntrLock);
++
++    return 1;
++}
++
++void
++ep3rcvr_rpc_put (EP_RXD *rxd, EP_NMD *local, EP_NMD *remote, unsigned nFrags)
++{
++    EP3_RXD_RAIL      *rxdRail   = (EP3_RXD_RAIL *) rxd->RxdRail;
++    EP3_RCVR_RAIL     *rcvrRail  = (EP3_RCVR_RAIL *) rxdRail->Generic.RcvrRail;
++    EP3_RAIL          *rail      = RCVR_TO_RAIL (rcvrRail);
++    ELAN3_DEV         *dev       = rail->Device;
++
++    EP3_RXD_RAIL_MAIN *rxdMain   = rxdRail->RxdMain;
++    sdramaddr_t        rxdElan   = rxdRail->RxdElan;
++    EP_ENVELOPE       *env       = &rxd->RxdMain->Envelope;
++    E3_DMA_BE        dmabe;
++    int                      i, len;
++
++    EP_ASSERT (&rail->Generic, rxd->State == EP_RXD_PUT_ACTIVE);
++    EP_ASSERT (&rail->Generic, rxdMain->DataEvent == EP3_EVENT_PRIVATE && rxdMain->DoneEvent == EP3_EVENT_PRIVATE);
++    EP_ASSERT (&rail->Generic, SDRAM_ASSERT (elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Count)) == 0));      /* PCI read */
++    EP_ASSERT (&rail->Generic, SDRAM_ASSERT (elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, DoneEvent.ev_Count)) == 0));      /* PCI read */
++
++    /* Flush the Elan TLB if mappings have changed */
++    ep_perrail_dvma_sync (&rail->Generic);
++    
++    /* Generate the DMA chain to put the data in two loops to burst
++     * the data across the PCI bus */
++    for (len = 0, i = (nFrags-1), local += (nFrags-1), remote += (nFrags-1); i >= 0;   len += local->nmd_len, i--, local--, remote--)
++    {
++      dmabe.s.dma_type            = E3_DMA_TYPE(DMA_BYTE, DMA_WRITE, DMA_NORMAL, EP3_DMAFAILCOUNT);
++      dmabe.s.dma_size            = local->nmd_len;
++      dmabe.s.dma_source          = local->nmd_addr;
++      dmabe.s.dma_dest            = remote->nmd_addr;
++      dmabe.s.dma_destEvent       = (E3_Addr) 0;
++      dmabe.s.dma_destCookieVProc = EP_VP_DATA (env->NodeId);
++      if (i == (nFrags-1))
++          dmabe.s.dma_srcEvent    = rxdRail->RxdElanAddr + offsetof (EP3_RXD_RAIL_ELAN, DataEvent);
++      else
++          dmabe.s.dma_srcEvent    = rxdRail->RxdElanAddr + offsetof (EP3_RXD_RAIL_ELAN, ChainEvent[i]);
++      dmabe.s.dma_srcCookieVProc  = LocalCookie (rail, env->NodeId);
++      
++      EPRINTF9 (DBG_RCVR, "%s: ep3rcvr_rpc_put: rxd %p [XID=%llx] idx=%d Source=%08x Dest=%08x Len=%x Cookies=%x.%x\n", rail->Generic.Name, rxd,
++                (long long) env->Xid.Unique, i, local->nmd_addr, remote->nmd_addr, local->nmd_len, dmabe.s.dma_destCookieVProc, dmabe.s.dma_srcCookieVProc);
++      
++      if (i != 0)
++          elan3_sdram_copyq_to_sdram (dev, &dmabe, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, Dmas[i]), sizeof (E3_DMA)); /* PCI write block */
++    }
++    
++    for (i = 0; i < nFrags; i++)
++      elan3_sdram_writel (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, ChainEvent[i].ev_Count), 1);                            /* PCI write */
++    
++    /* Initialise the data event */
++    elan3_sdram_writel (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Count), 1);                  /* PCI write */
++    rxdMain->DataEvent = EP3_EVENT_ACTIVE;
++   
++    ASSERT (rail->Generic.Nodes[env->NodeId].State >= EP_NODE_CONNECTED && rail->Generic.Nodes[env->NodeId].State <= EP_NODE_LOCAL_PASSIVATE);
++
++    if (IssueDma (rail, &dmabe, EP_RETRY_LOW_PRI, FALSE) != ISSUE_COMMAND_OK)
++    {
++      /* Failed to issue the dma command, so copy the dma descriptor and queue it for retry */
++      EPRINTF2 (DBG_RCVR, "%s: ep3rcvr_rpc_put: queue rxd %p on retry thread\n", rail->Generic.Name, rxd);
++      
++      QueueDmaForRetry (rail, &dmabe, EP_RETRY_LOW_PRI);
++    }
++    
++    BucketStat (rxd->Rcvr->Subsys, RPCPut, len);
++}
++
++void
++ep3rcvr_rpc_get (EP_RXD *rxd, EP_NMD *local, EP_NMD *remote, unsigned nFrags)
++{
++    EP3_RXD_RAIL      *rxdRail   = (EP3_RXD_RAIL *) rxd->RxdRail;
++    EP3_RCVR_RAIL     *rcvrRail  = (EP3_RCVR_RAIL *) rxdRail->Generic.RcvrRail;
++    EP3_RAIL          *rail      = RCVR_TO_RAIL (rcvrRail);
++    ELAN3_DEV         *dev       = rail->Device;
++
++    EP3_RXD_RAIL_MAIN *rxdMain   = rxdRail->RxdMain;
++    sdramaddr_t        rxdElan   = rxdRail->RxdElan;
++    EP_ENVELOPE       *env       = &rxd->RxdMain->Envelope;
++    E3_DMA_BE        dmabe;
++    int                      i, len;
++
++    EP_ASSERT (&rail->Generic, rxd->State == EP_RXD_GET_ACTIVE);
++    EP_ASSERT (&rail->Generic, rxdMain->DataEvent == EP3_EVENT_PRIVATE && rxdMain->DoneEvent == EP3_EVENT_PRIVATE);
++    EP_ASSERT (&rail->Generic, SDRAM_ASSERT (elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Count)) == 0));      /* PCI read */
++    EP_ASSERT (&rail->Generic, SDRAM_ASSERT (elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, DoneEvent.ev_Count)) == 0));      /* PCI read */
++      
++    /* Flush the Elan TLB if mappings have changed */
++    ep_perrail_dvma_sync (&rail->Generic);
++    
++    /* Generate the DMA chain to get the data in two loops to burst
++     * the data across the PCI bus */
++    for (len = 0, i = (nFrags-1), remote += (nFrags-1), local += (nFrags-1); i >= 0;   len += remote->nmd_len, i--, remote--, local--)
++    {
++      dmabe.s.dma_type            = E3_DMA_TYPE(DMA_BYTE, DMA_READ, DMA_NORMAL, EP3_DMAFAILCOUNT);
++      dmabe.s.dma_size            = remote->nmd_len;
++      dmabe.s.dma_source          = remote->nmd_addr;
++      dmabe.s.dma_dest            = local->nmd_addr;
++      if (i == (nFrags-1))
++          dmabe.s.dma_destEvent   = rxdRail->RxdElanAddr + offsetof (EP3_RXD_RAIL_ELAN, DataEvent);
++      else
++          dmabe.s.dma_destEvent   = rxdRail->RxdElanAddr + offsetof (EP3_RXD_RAIL_ELAN, ChainEvent[i]);
++      dmabe.s.dma_destCookieVProc = LocalCookie (rail, env->NodeId);
++      dmabe.s.dma_srcEvent        = (E3_Addr) 0;
++      dmabe.s.dma_srcCookieVProc  = RemoteCookie (rail, env->NodeId);
++      
++      EPRINTF9 (DBG_RCVR, "%s: ep3rcvr_rpc_get rxd %p [XID=%llx] idx=%d Source=%08x Dest=%08x Len=%x Cookies=%x.%x\n", rail->Generic.Name, rxd,
++                (long long) env->Xid.Unique, i, remote->nmd_addr, local->nmd_addr, remote->nmd_len, dmabe.s.dma_destCookieVProc, 
++                dmabe.s.dma_srcCookieVProc);
++      
++      /* 
++       * Always copy down the dma descriptor, since we issue it as a READ_REQUEUE
++       * dma, and the elan will fetch the descriptor to send out of the link from
++       * the rxdElan->Dmas[i] location,  before issueing the DMA chain we modify
++       * the dma_source.
++       */
++      elan3_sdram_copyq_to_sdram (dev, &dmabe, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, Dmas[i]), sizeof (E3_DMA)); /* PCI write block */
++    }
++    
++    for (i = 0; i < nFrags; i++)
++      elan3_sdram_writel (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, ChainEvent[i].ev_Count), 1);                            /* PCI write */
++    
++    /* Initialise the data event */
++    elan3_sdram_writel (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Count), 1);                  /* PCI write */
++    rxdMain->DataEvent  = EP3_EVENT_ACTIVE;
++    
++    ASSERT (rail->Generic.Nodes[env->NodeId].State >= EP_NODE_CONNECTED && rail->Generic.Nodes[env->NodeId].State <= EP_NODE_LOCAL_PASSIVATE);
++
++    /* we MUST convert this into a DMA_READ_REQUEUE dma as if we don't the DMA descriptor will
++     * be read from the EP_RETRY_DMA rather than the orignal DMA - this can then get reused 
++     * and an incorrect DMA descriptor sent */
++    dmabe.s.dma_source    = rxdRail->RxdElanAddr + offsetof (EP3_RXD_RAIL_ELAN, Dmas[0]);
++    dmabe.s.dma_direction = (dmabe.s.dma_direction & ~DMA_READ) | DMA_READ_REQUEUE;
++    
++    if (IssueDma (rail, &dmabe, EP_RETRY_LOW_PRI, FALSE) != ISSUE_COMMAND_OK)
++    {
++      /* Failed to issue the dma command, so copy the dma descriptor and queue it for retry */
++      EPRINTF2 (DBG_RCVR, "%s: ep3rcvr_rpc_get: queue rxd %p on retry thread\n", rail->Generic.Name, rxd);
++      
++      QueueDmaForRetry (rail, &dmabe, EP_RETRY_LOW_PRI);
++    }
++
++    BucketStat (rxd->Rcvr->Subsys, RPCGet, len);
++}
++      
++void
++ep3rcvr_rpc_complete (EP_RXD *rxd, EP_NMD *local, EP_NMD *remote, unsigned nFrags)
++{
++    EP3_RXD_RAIL      *rxdRail   = (EP3_RXD_RAIL *) rxd->RxdRail;
++    EP3_RCVR_RAIL     *rcvrRail  = (EP3_RCVR_RAIL *) rxdRail->Generic.RcvrRail;
++    EP3_RAIL          *rail      = RCVR_TO_RAIL (rcvrRail);
++    ELAN3_DEV         *dev       = rail->Device;
++
++    EP3_RXD_RAIL_MAIN *rxdMain   = rxdRail->RxdMain;
++    sdramaddr_t        rxdElan   = rxdRail->RxdElan;
++    EP_ENVELOPE       *env       = &rxd->RxdMain->Envelope;
++    E3_DMA_BE        dmabe;
++    int                      i, len;
++    
++    EP_ASSERT (&rail->Generic, rxd->State == EP_RXD_COMPLETE_ACTIVE);
++    EP_ASSERT (&rail->Generic, rxdMain->DataEvent == EP3_EVENT_PRIVATE && rxdMain->DoneEvent == EP3_EVENT_PRIVATE);
++    EP_ASSERT (&rail->Generic, SDRAM_ASSERT (elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Count)) == 0));      /* PCI read */
++    EP_ASSERT (&rail->Generic, SDRAM_ASSERT (elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, DoneEvent.ev_Count)) == 0));      /* PCI read */
++
++    /* Flush the Elan TLB if mappings have changed */
++    ep_perrail_dvma_sync (&rail->Generic);
++    
++    /* Initialise the status block dma */
++    dmabe.s.dma_type            = E3_DMA_TYPE(DMA_BYTE, DMA_WRITE, DMA_NORMAL, EP3_DMAFAILCOUNT);
++    dmabe.s.dma_size            = sizeof (EP_STATUSBLK);
++    dmabe.s.dma_source          = rxd->NmdMain.nmd_addr + offsetof (EP_RXD_MAIN, StatusBlk);
++    dmabe.s.dma_dest            = env->TxdMain.nmd_addr + offsetof (EP_TXD_MAIN, StatusBlk);
++    dmabe.s.dma_destEvent       = env->TxdRail + offsetof (EP3_TXD_RAIL_ELAN, DoneEvent);
++    dmabe.s.dma_destCookieVProc = EP_VP_DATA(env->NodeId);
++    dmabe.s.dma_srcEvent        = rxdRail->RxdElanAddr + offsetof (EP3_RXD_RAIL_ELAN, DoneEvent);
++    dmabe.s.dma_srcCookieVProc  = LocalCookie (rail, env->NodeId);
++    
++    EPRINTF8 (DBG_RCVR, "%s: ep3rcvr_rpc_complete: rxd %p [XID=%llx] statusblk source=%08x dest=%08x len=%x Cookies=%x.%x\n", rail->Generic.Name, rxd,
++            (long long) env->Xid.Unique, dmabe.s.dma_source, dmabe.s.dma_dest, dmabe.s.dma_size, dmabe.s.dma_destCookieVProc, 
++            dmabe.s.dma_srcCookieVProc);
++
++    for (len = 0, i = EP_MAXFRAG, remote += (nFrags-1), local += (nFrags-1); i > EP_MAXFRAG-nFrags; len += local->nmd_len, i--, local--, remote--)
++    {
++      /* copy down previous dma */
++      elan3_sdram_copyq_to_sdram (dev, &dmabe, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, Dmas[i]),  sizeof (E3_DMA));    /* PCI write block */
++      
++      dmabe.s.dma_type            = E3_DMA_TYPE(DMA_BYTE, DMA_WRITE, DMA_NORMAL, EP3_DMAFAILCOUNT);
++      dmabe.s.dma_size            = local->nmd_len;
++      dmabe.s.dma_source          = local->nmd_addr;
++      dmabe.s.dma_dest            = remote->nmd_addr;
++      dmabe.s.dma_destEvent       = (E3_Addr) 0;
++      dmabe.s.dma_destCookieVProc = EP_VP_DATA (env->NodeId);
++      dmabe.s.dma_srcEvent        = rxdRail->RxdElanAddr + offsetof (EP3_RXD_RAIL_ELAN, ChainEvent[i-1]);
++      dmabe.s.dma_srcCookieVProc  = LocalCookie (rail, env->NodeId);
++      
++      EPRINTF9 (DBG_RCVR, "%s: ep3rcvr_rpc_complete: rxd %p [XID=%llx] idx=%d Source=%08x Dest=%08x Len=%x Cookies=%x.%x\n", rail->Generic.Name, rxd,
++                (long long) env->Xid.Unique, i, local->nmd_addr, remote->nmd_addr, local->nmd_len, dmabe.s.dma_destCookieVProc, 
++                dmabe.s.dma_srcCookieVProc);
++    }
++    
++    for (i = EP_MAXFRAG-nFrags; i < EP_MAXFRAG; i++)
++      elan3_sdram_writel (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, ChainEvent[i].ev_Count), 1);                            /* PCI write */
++    
++    /* Initialise the done event */
++    elan3_sdram_writel (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, DoneEvent.ev_Count), 1);                                  /* PCI write */
++    rxdMain->DoneEvent  = EP3_EVENT_ACTIVE;
++
++    ASSERT (rail->Generic.Nodes[env->NodeId].State >= EP_NODE_CONNECTED && rail->Generic.Nodes[env->NodeId].State <= EP_NODE_LOCAL_PASSIVATE);
++
++    if (IssueDma (rail, &dmabe, EP_RETRY_LOW_PRI, FALSE) != ISSUE_COMMAND_OK)
++    {
++      /* Failed to issue the dma command, so copy the dma descriptor and queue it for retry */
++      EPRINTF2 (DBG_RCVR, "%s: ep3rcvr_rpc_complete: queue rxd %p on retry thread\n", rail->Generic.Name, rxd);
++      
++      QueueDmaForRetry (rail, &dmabe, EP_RETRY_LOW_PRI);
++    }
++
++    BucketStat (rxd->Rcvr->Subsys, CompleteRPC, len);
++}
++      
++void
++ep3rcvr_add_rail (EP_RCVR *rcvr, EP_COMMS_RAIL *commsRail)
++{
++    EP3_RAIL          *rail   = (EP3_RAIL *) commsRail->Rail;
++    sdramaddr_t        qdescs = ((EP3_COMMS_RAIL *) commsRail)->QueueDescs;
++    EP3_RCVR_RAIL     *rcvrRail;
++    EP3_InputQueue     qdesc;
++    sdramaddr_t        stack;
++    unsigned long      flags;
++
++    KMEM_ZALLOC (rcvrRail, EP3_RCVR_RAIL *, sizeof (EP3_RCVR_RAIL), TRUE);
++
++    kcondvar_init (&rcvrRail->CleanupSleep);
++    spin_lock_init (&rcvrRail->FreeDescLock);
++    INIT_LIST_HEAD (&rcvrRail->FreeDescList);
++    INIT_LIST_HEAD (&rcvrRail->DescBlockList);
++
++    rcvrRail->Generic.CommsRail = commsRail;
++    rcvrRail->Generic.Rcvr      = rcvr;
++
++    rcvrRail->RcvrMain       = ep_alloc_main (&rail->Generic, sizeof (EP3_RCVR_RAIL_MAIN), 0, &rcvrRail->RcvrMainAddr);
++    rcvrRail->RcvrElan       = ep_alloc_elan (&rail->Generic, sizeof (EP3_RCVR_RAIL_ELAN), 0, &rcvrRail->RcvrElanAddr);
++    rcvrRail->InputQueueBase = ep_alloc_elan (&rail->Generic, EP_INPUTQ_SIZE * rcvr->InputQueueEntries, 0, &rcvrRail->InputQueueAddr);
++    stack                    = ep_alloc_elan (&rail->Generic, EP3_STACK_SIZE, 0, &rcvrRail->ThreadStack);
++
++    rcvrRail->TotalDescCount = 0;
++    rcvrRail->FreeDescCount  = 0;
++
++    /* Initialise the main/elan spin lock */
++    elan3_sdram_writel (rail->Device, rcvrRail->RcvrElan + offsetof (EP3_RCVR_RAIL_ELAN, ThreadLock.sl_lock), 0);
++    elan3_sdram_writel (rail->Device, rcvrRail->RcvrElan + offsetof (EP3_RCVR_RAIL_ELAN, ThreadLock.sl_seq),  0);
++
++    elan3_sdram_writel (rail->Device, rcvrRail->RcvrElan + offsetof (EP3_RCVR_RAIL_ELAN, PendingLock.sl_lock), 0);
++    elan3_sdram_writel (rail->Device, rcvrRail->RcvrElan + offsetof (EP3_RCVR_RAIL_ELAN, PendingLock.sl_seq), 0);
++    
++    /* Initialise the receive lists */
++    elan3_sdram_writel (rail->Device, rcvrRail->RcvrElan + offsetof (EP3_RCVR_RAIL_ELAN, PendingDescs), 0);
++    
++    /* Initialise the ThreadShould Halt */
++    elan3_sdram_writel (rail->Device, rcvrRail->RcvrElan + offsetof (EP3_RCVR_RAIL_ELAN, ThreadShouldHalt), 0);
++
++    /* Initialise pointer to the ep_rcvr_rail */
++    elan3_sdram_writeq (rail->Device, rcvrRail->RcvrElan + offsetof (EP3_RCVR_RAIL_ELAN, MainAddr), (unsigned long) rcvrRail);
++
++    /* Initialise elan visible main memory */
++    rcvrRail->RcvrMain->ThreadLock.sl_seq  = 0;
++    rcvrRail->RcvrMain->PendingLock.sl_seq = 0;
++    rcvrRail->RcvrMain->PendingDescsTailp  = 0;
++
++    /* initialise and copy down the input queue descriptor */
++    qdesc.q_state          = E3_QUEUE_FULL;
++    qdesc.q_base           = rcvrRail->InputQueueAddr;
++    qdesc.q_top            = rcvrRail->InputQueueAddr + (rcvr->InputQueueEntries-1) * EP_INPUTQ_SIZE;
++    qdesc.q_fptr           = rcvrRail->InputQueueAddr;
++    qdesc.q_bptr           = rcvrRail->InputQueueAddr + EP_INPUTQ_SIZE;
++    qdesc.q_size           = EP_INPUTQ_SIZE;
++    qdesc.q_event.ev_Count = 0;
++    qdesc.q_event.ev_Type  = 0;
++
++    elan3_sdram_copyl_to_sdram (rail->Device, &qdesc, qdescs + rcvr->Service * sizeof (EP3_InputQueue), sizeof (EP3_InputQueue));
++
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    rcvr->Rails[rail->Generic.Number] = &rcvrRail->Generic;
++    rcvr->RailMask |= EP_RAIL2RAILMASK (rail->Generic.Number);
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++
++    /* initialise and run the Elan thread to process the queue */
++    IssueRunThread (rail, ep3_init_thread (rail->Device, ep_symbol (&rail->ThreadCode, "ep3comms_rcvr"),
++                                         rcvrRail->ThreadStack, stack, EP3_STACK_SIZE, 5,
++                                         rail->RailElanAddr, rcvrRail->RcvrElanAddr, rcvrRail->RcvrMainAddr,
++                                         EP_MSGQ_ADDR(rcvr->Service),
++                                         rail->ElanCookies));
++}
++
++void
++ep3rcvr_del_rail (EP_RCVR *rcvr, EP_COMMS_RAIL *commsRail)
++{
++    EP3_RAIL         *rail     = (EP3_RAIL *) commsRail->Rail;
++    EP3_RCVR_RAIL    *rcvrRail = (EP3_RCVR_RAIL *) rcvr->Rails[rail->Generic.Number];  
++    unsigned long     flags;
++    struct list_head *el, *nel;
++
++    EPRINTF1 (DBG_RCVR, "%s: ep3rcvr_del_rail: removing rail\n", rail->Generic.Name);
++
++    /* flag the rail as no longer available */
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    rcvr->RailMask &= ~EP_RAIL2RAILMASK (rail->Generic.Number);
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++    
++    /* mark the input queue descriptor as full */
++    SetQueueLocked(rail, ((EP3_COMMS_RAIL *)commsRail)->QueueDescs + rcvr->Service * sizeof (EP3_InputQueue));
++
++    /* need to halt the thread first         */
++    /*   set ThreadShouldHalt in elan memory */
++    /*   then trigger the event              */
++    /*   and wait on haltWait                */
++    elan3_sdram_writel  (rail->Device, rcvrRail->RcvrElan + offsetof (EP3_RCVR_RAIL_ELAN, ThreadShouldHalt), TRUE);
++
++    IssueSetevent (rail,  EP_MSGQ_ADDR(rcvr->Service) + offsetof(EP3_InputQueue, q_event));
++
++    spin_lock_irqsave (&rcvr->Lock, flags);
++
++    while (rcvrRail->ThreadHalted == 0)
++    {
++      rcvrRail->CleanupWaiting++;
++      kcondvar_wait (&rcvrRail->CleanupSleep, &rcvr->Lock, &flags);
++    }
++
++    /* at this point the thread is halted and it has no envelopes */
++ 
++    /* we need to wait until all the rxd's in the list that are 
++     * bound to the rail we are removing are not pending 
++     */
++    for (;;)
++    {
++      int mustWait = 0;
++      
++      list_for_each (el, &rcvr->ActiveDescList) {
++          EP_RXD       *rxd     = list_entry (el,EP_RXD, Link);
++          EP3_RXD_RAIL *rxdRail = (EP3_RXD_RAIL *) rxd->RxdRail;
++
++          if (rxdRail && RXD_BOUND2RAIL (rxdRail, rcvrRail) && rxd->RxdMain->Len != EP_RXD_PENDING)
++          {
++              mustWait++;
++              break;
++          }
++      }
++      
++      if (! mustWait)
++          break;
++
++      EPRINTF1 (DBG_RCVR, "%s: ep3rcvr_del_rail: waiting for active rxd's to be returned\n", rail->Generic.Name);
++
++      rcvrRail->CleanupWaiting++;
++      kcondvar_wait (&rcvrRail->CleanupSleep, &rcvr->Lock, &flags);
++    }
++
++    /* at this point all rxd's in the list that are bound to the deleting rail are not pending */
++    list_for_each_safe (el, nel, &rcvr->ActiveDescList) {
++      EP_RXD       *rxd     = list_entry (el, EP_RXD, Link);
++      EP3_RXD_RAIL *rxdRail = (EP3_RXD_RAIL *) rxd->RxdRail;
++      
++      if (rxdRail && RXD_BOUND2RAIL (rxdRail, rcvrRail))
++      {
++          /* here we need to unbind the remaining rxd's */
++          rxdRail->RxdMain->DataEvent = EP3_EVENT_PRIVATE;
++          rxdRail->RxdMain->DoneEvent = EP3_EVENT_PRIVATE;
++ 
++          elan3_sdram_writel (rail->Device, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Count), 0);  /* PCI write */
++          elan3_sdram_writel (rail->Device, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DoneEvent.ev_Count), 0);  /* PCI write */
++
++          UnbindRxdFromRail (rxd, rxdRail);
++          FreeRxdRail(rcvrRail,  rxdRail );
++      }
++    }
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++    
++    /* wait for all rxd's for this rail to become free */
++    spin_lock_irqsave (&rcvrRail->FreeDescLock, flags);
++    while (rcvrRail->FreeDescCount != rcvrRail->TotalDescCount)
++    {
++      rcvrRail->FreeDescWaiting++;
++      kcondvar_wait (&rcvrRail->FreeDescSleep, &rcvrRail->FreeDescLock, &flags);
++    }
++    spin_unlock_irqrestore (&rcvrRail->FreeDescLock, flags);
++
++    /* can now remove the rail as it can no longer be used */
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    rcvr->Rails[rail->Generic.Number] = NULL;
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++
++    /* all the rxd's accociated with DescBlocks must be in the FreeDescList */
++    ASSERT (rcvrRail->TotalDescCount == rcvrRail->FreeDescCount);
++
++    /* run through the DescBlockList deleting them */
++    while (!list_empty (&rcvrRail->DescBlockList))
++      FreeRxdRailBlock (rcvrRail, list_entry(rcvrRail->DescBlockList.next, EP3_RXD_RAIL_BLOCK , Link));
++
++    /* it had better be empty after that */
++    ASSERT ((rcvrRail->TotalDescCount == 0) && (rcvrRail->TotalDescCount == rcvrRail->FreeDescCount));
++    
++    ep_free_elan (&rail->Generic, rcvrRail->ThreadStack, EP3_STACK_SIZE);
++    ep_free_elan (&rail->Generic, rcvrRail->InputQueueAddr, EP_INPUTQ_SIZE * rcvr->InputQueueEntries);
++    ep_free_elan (&rail->Generic, rcvrRail->RcvrElanAddr, sizeof (EP3_RCVR_RAIL_ELAN));
++    ep_free_main (&rail->Generic, rcvrRail->RcvrMainAddr, sizeof (EP3_RCVR_RAIL_MAIN));
++
++    KMEM_FREE (rcvrRail, sizeof (EP3_RCVR_RAIL));
++}
++
++EP_RXD *
++ep3rcvr_steal_rxd (EP_RCVR_RAIL *r)
++{
++    EP3_RCVR_RAIL *rcvrRail = (EP3_RCVR_RAIL *) r;
++    EP3_RAIL      *rail     = RCVR_TO_RAIL (rcvrRail);
++    EP_RCVR       *rcvr     = rcvrRail->Generic.Rcvr;
++    E3_Addr        rxdElanAddr;
++    unsigned long flags;
++
++    spin_lock_irqsave (&rcvr->Lock, flags);
++
++    LockRcvrThread (rcvrRail);
++    if ((rxdElanAddr = elan3_sdram_readl (rail->Device, rcvrRail->RcvrElan + offsetof (EP3_RCVR_RAIL_ELAN, PendingDescs))) != 0)
++    {
++      sdramaddr_t  rxdElan  = ep_elan2sdram (&rail->Generic, rxdElanAddr);
++      EP3_RXD_RAIL *rxdRail = (EP3_RXD_RAIL *) (unsigned long) elan3_sdram_readq (rail->Device, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, MainAddr));
++      EP_RXD      *rxd      = rxdRail->Generic.Rxd;
++      sdramaddr_t  next;
++      
++      EPRINTF2 (DBG_RCVR, "%s: StealRxdFromOtherRail stealing rxd %p\n", rail->Generic.Name, rail);
++      
++      /* Remove the RXD from the pending desc list */
++      if ((next = elan3_sdram_readl (rail->Device, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, Next))) == 0)
++          rcvrRail->RcvrMain->PendingDescsTailp = 0;
++      elan3_sdram_writel (rail->Device, rcvrRail->RcvrElan + offsetof (EP3_RCVR_RAIL_ELAN, PendingDescs), next);
++      UnlockRcvrThread (rcvrRail);
++      
++      UnbindRxdFromRail (rxd, rxdRail);
++      
++      spin_unlock_irqrestore (&rcvr->Lock, flags);
++      
++      /* Mark rxdRail as no longer active */
++      rxdRail->RxdMain->DataEvent = EP3_EVENT_PRIVATE;
++      rxdRail->RxdMain->DoneEvent = EP3_EVENT_PRIVATE;
++      elan3_sdram_writel (rail->Device, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Count), 0);
++      elan3_sdram_writel (rail->Device, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DoneEvent.ev_Count), 0);
++      
++      FreeRxdRail (rcvrRail, rxdRail);
++
++      return rxd;
++    }
++
++    UnlockRcvrThread (rcvrRail);
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++
++    return NULL;
++}
++
++long
++ep3rcvr_check (EP_RCVR_RAIL *r, long nextRunTime)
++{
++    EP3_RCVR_RAIL    *rcvrRail = (EP3_RCVR_RAIL *) r;
++    EP3_RAIL         *rail     = RCVR_TO_RAIL (rcvrRail);
++    EP_RCVR          *rcvr     = rcvrRail->Generic.Rcvr;
++    EP_COMMS_SUBSYS *subsys    = rcvr->Subsys;
++    EP_SYS           *sys       = subsys->Subsys.Sys;
++    EP_RXD           *rxd;
++    unsigned long     flags;
++
++    if (rcvrRail->FreeDescCount < ep_rxd_lowat && !AllocateRxdRailBlock (rcvrRail))
++    {
++      EPRINTF1 (DBG_RCVR,"%s: failed to grow rxd rail pool\n", rail->Generic.Name);
++              
++      if (nextRunTime == 0 || AFTER (nextRunTime, lbolt + RESOURCE_RETRY_TIME))
++          nextRunTime = lbolt + RESOURCE_RETRY_TIME;
++    }
++    
++    if (rcvrRail->ThreadWaiting && (rxd = StealRxdFromOtherRail (rcvr)) != NULL)
++    {
++      /* Map the receive buffer into this rail as well */
++      EPRINTF4 (DBG_RCVR, "%s: mapping rxd->Data (%08x.%08x.%08x) into this rails\n",
++                rail->Generic.Name, rxd->Data.nmd_addr,rxd->Data.nmd_len, rxd->Data.nmd_attr);
++
++      spin_lock_irqsave (&rcvr->Lock, flags);
++      if ((!(EP_NMD_RAILMASK (&rxd->Data) & EP_RAIL2RAILMASK(rail->Generic.Number)) &&                /* not already mapped and */
++           ep_nmd_map_rails (sys, &rxd->Data, EP_RAIL2RAILMASK(rail->Generic.Number)) == 0) ||        /* failed to map it */
++          ep3rcvr_queue_rxd (rxd, &rcvrRail->Generic))                                                /* or failed to queue it */
++      {
++          EPRINTF5 (DBG_RCVR,"%s: stolen rcvr=%p rxd=%p -> rnum=%d rcvrRail=%p (failed)\n", 
++                    rail->Generic.Name, rcvr, rxd, rail->Generic.Number, rcvrRail);
++              
++          if (nextRunTime == 0 || AFTER (nextRunTime, lbolt + RESOURCE_RETRY_TIME))
++              nextRunTime = lbolt + RESOURCE_RETRY_TIME;
++      }
++      spin_unlock_irqrestore (&rcvr->Lock, flags);
++    }
++    
++    return nextRunTime;
++}
++
++static void
++ep3rcvr_flush_filtering (EP_RCVR *rcvr, EP3_RCVR_RAIL *rcvrRail)
++{
++    EP3_COMMS_RAIL *commsRail = (EP3_COMMS_RAIL *) rcvrRail->Generic.CommsRail;
++    EP3_RAIL       *rail      = (EP3_RAIL *) commsRail->Generic.Rail;
++    ELAN3_DEV      *dev       = rail->Device;
++    sdramaddr_t    qdesc      = commsRail->QueueDescs + rcvr->Service*sizeof (EP3_InputQueue);
++    E3_Addr        qTop       = elan3_sdram_readl (dev, qdesc + offsetof (EP3_InputQueue, q_top));
++    E3_Addr        qBase      = elan3_sdram_readl (dev, qdesc + offsetof (EP3_InputQueue, q_base));
++    E3_Addr        qSize      = elan3_sdram_readl (dev,qdesc + offsetof (EP3_InputQueue, q_size));
++    E3_uint32      nfptr, qbptr;
++    unsigned long  flags;
++    
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    LockRcvrThread (rcvrRail);                                                                                /* PCI lock */
++
++    nfptr = elan3_sdram_readl (dev, qdesc + offsetof (EP3_InputQueue, q_fptr));
++    qbptr = elan3_sdram_readl (dev, qdesc + offsetof (EP3_InputQueue, q_bptr));
++    
++    if (nfptr == qTop)
++      nfptr = qBase;
++    else
++      nfptr += qSize;
++    
++    while (nfptr != qbptr)
++    {
++      unsigned nodeId = elan3_sdram_readl (dev, rcvrRail->InputQueueBase + (nfptr - rcvrRail->InputQueueAddr) + 
++                                     offsetof (EP_ENVELOPE, NodeId));
++      
++      EPRINTF3 (DBG_DISCON, "%s: ep3rcvr_flush_filtering: nodeId=%d State=%d\n", rail->Generic.Name, nodeId, rail->Generic.Nodes[nodeId].State);
++      
++      if (rail->Generic.Nodes[nodeId].State == EP_NODE_LOCAL_PASSIVATE)
++          elan3_sdram_writel (dev, rcvrRail->InputQueueBase + (nfptr - rcvrRail->InputQueueAddr) + 
++                        offsetof (EP_ENVELOPE, Version), 0);
++      
++      if (nfptr == qTop)
++          nfptr = qBase;
++      else
++          nfptr += qSize;
++    }
++    
++    UnlockRcvrThread (rcvrRail);                                                                              /* PCI unlock */
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++}
++
++static void
++ep3rcvr_flush_flushing (EP_RCVR *rcvr, EP3_RCVR_RAIL *rcvrRail)
++{
++    EP3_RAIL         *rail = RCVR_TO_RAIL (rcvrRail);
++    struct list_head *el, *nel;
++    unsigned long     flags;
++
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    LockRcvrThread (rcvrRail);                                                                                /* PCI lock */
++    
++    list_for_each_safe (el, nel, &rcvr->ActiveDescList) {
++      EP_RXD       *rxd      = list_entry (el, EP_RXD, Link);
++      EP3_RXD_RAIL *rxdRail  = (EP3_RXD_RAIL *) rxd->RxdRail;
++      EP_ENVELOPE  *env      = &rxd->RxdMain->Envelope;
++      EP_NODE_RAIL *nodeRail = &rail->Generic.Nodes[env->NodeId];
++
++      if (rxd->RxdMain->Len == EP_RXD_PENDING || !RXD_BOUND2RAIL(rxdRail,rcvrRail) || nodeRail->State != EP_NODE_LOCAL_PASSIVATE)
++          continue;
++      
++      EPRINTF6 (DBG_DISCON, "%s: ep3rcvr_flush_flushing: rcvr %p rxd %p state %x.%x elan node %d\n", rail->Generic.Name,
++                rcvr, rxd, rxdRail->RxdMain->DataEvent, rxdRail->RxdMain->DoneEvent, env->NodeId);
++      
++      switch (rxd->State)
++      {
++      case EP_RXD_FREE:
++          printk ("ep3rcvr_flush_flushing: rxd state is free but bound to a fail\n");
++          break;
++
++      case EP_RXD_RECEIVE_ACTIVE:
++          if (rxdRail->RxdMain->DataEvent == EP3_EVENT_ACTIVE)                /* incomplete message receive */
++          {
++              EPRINTF4 (DBG_RCVR, "%s: ep3rcvr_flush_flushing: rcvr %p rxd %p nodeId %d - passive\n", 
++                        rail->Generic.Name, rcvr, rxd, env->NodeId);
++              
++              nodeRail->MessageState |= EP_NODE_PASSIVE_MESSAGES;
++              continue;
++          }
++          break;
++          
++      default:
++          EP_ASSERT (&rail->Generic, EP_IS_RPC(env->Attr));
++
++          if (!EP3_EVENT_FIRED (rxdRail->DoneCookie, rxdRail->RxdMain->DoneEvent))    /* incomplete RPC */
++          {
++              EPRINTF4 (DBG_RCVR, "%s: ep3rcvr_flush_flushing: rcvr %p rxd %p nodeId %d - active\n", 
++                        rail->Generic.Name, rcvr, rxd, env->NodeId);
++              
++              EP_INVALIDATE_XID (rxd->MsgXid);                        /* Ignore any previous NMD map responses */
++              
++              nodeRail->MessageState |= EP_NODE_ACTIVE_MESSAGES;
++              continue;
++          }
++          break;
++
++      case EP_RXD_BEEN_ABORTED:
++          printk ("ep3rcvr_flush_flushing: rxd state is aborted but bound to a fail\n");
++          break;
++      }
++
++      EPRINTF4 (DBG_RCVR, "%s: ep3rcvr_flush_flushing: rcvr %p rxd %p nodeId %d - finished\n", 
++                rail->Generic.Name, rcvr, rxd, env->NodeId);
++    }    
++
++    UnlockRcvrThread (rcvrRail);                                                                      /* PCI unlock */
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++}
++
++void
++ep3rcvr_flush_callback (EP_RCVR *rcvr, EP3_RCVR_RAIL *rcvrRail)
++{
++    EP3_RAIL *rail = RCVR_TO_RAIL(rcvrRail);
++
++    switch (rail->Generic.CallbackStep)
++    {
++    case EP_CB_FLUSH_FILTERING:
++      ep3rcvr_flush_filtering (rcvr, rcvrRail);
++      break;
++
++    case EP_CB_FLUSH_FLUSHING:
++      ep3rcvr_flush_flushing (rcvr, rcvrRail);
++      break;
++    }
++}
++
++void
++ep3rcvr_failover_callback (EP_RCVR *rcvr, EP3_RCVR_RAIL *rcvrRail)
++{
++    EP_COMMS_SUBSYS  *subsys = rcvr->Subsys;
++    EP3_RAIL         *rail   = RCVR_TO_RAIL (rcvrRail);
++    ELAN3_DEV        *dev    = rail->Device;
++    struct list_head *el, *nel;
++    unsigned long     flags;
++#ifdef SUPPORT_RAIL_FAILOVER
++    EP_SYS           *sys    = subsys->Subsys.Sys;
++#endif
++   
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    LockRcvrThread (rcvrRail);                                                                                /* PCI lock */
++    
++    list_for_each_safe (el, nel, &rcvr->ActiveDescList) {
++      EP_RXD             *rxd      = list_entry (el, EP_RXD, Link);
++      EP3_RXD_RAIL       *rxdRail  = (EP3_RXD_RAIL *) rxd->RxdRail;
++      EP_ENVELOPE        *env      = &rxd->RxdMain->Envelope;
++      EP_NODE_RAIL       *nodeRail = &rail->Generic.Nodes[env->NodeId];
++#ifdef SUPPORT_RAIL_FAILOVER
++      EP_MANAGER_MSG_BODY msgBody;
++      EP_NODE            *node     = &sys->Nodes[env->NodeId];
++#endif
++      
++      if (rxd->RxdMain->Len == EP_RXD_PENDING || !RXD_BOUND2RAIL(rxdRail,rcvrRail) || nodeRail->State != EP_NODE_PASSIVATED)
++          continue;
++
++      EPRINTF6 (DBG_FAILOVER, "%s: ep3rcvr_failover_callback: rcvr %p rxd %p elan node %d state %x.%x\n", rail->Generic.Name, rcvr, rxd, env->NodeId,
++                rxdRail->RxdMain->DataEvent, rxdRail->RxdMain->DoneEvent);
++
++      switch (rxd->State)
++      {
++      case EP_RXD_FREE:
++          printk ("ep4rcvr_failover_callback: rxd state is free but bound to a fail\n");
++          break;
++
++      case EP_RXD_RECEIVE_ACTIVE:
++          if (rxdRail->RxdMain->DataEvent == EP3_EVENT_ACTIVE)                /* incomplete message receive */
++          {
++              EPRINTF4 (DBG_FAILOVER, "%s: ep3rcvr_failover_callback: rcvr %p rxd %p nodeId %d - unbind\n", rail->Generic.Name, rcvr, rxd, env->NodeId);
++              
++              UnbindRxdFromRail (rxd, rxdRail);
++              
++              /* clear the done flags - so that it will be ignored if an event interrupt is generated */
++              rxdRail->RxdMain->DataEvent = EP3_EVENT_PRIVATE;
++              rxdRail->RxdMain->DoneEvent = EP3_EVENT_PRIVATE;
++              
++              /* clear the data event - the done event should already be zero */
++              elan3_sdram_writel (dev, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Count), 0);       /* PCI write */
++              
++              FreeRxdRail (rcvrRail, rxdRail);
++              
++              /* epcomms thread will requeue on different rail */
++              ep_kthread_schedule (&subsys->Thread, lbolt);
++              continue;
++          }
++          break;
++
++      default:
++          EP_ASSERT (&rail->Generic, EP_IS_RPC(env->Attr));
++
++#ifdef SUPPORT_RAIL_FAILOVER
++          if (!EP3_EVENT_FIRED (rxdRail->DoneCookie, rxdRail->RxdMain->DoneEvent) && !(EP_IS_NO_FAILOVER(env->Attr)))         /* incomplete RPC, which can be failed over  */
++          {
++              EPRINTF7 (DBG_FAILOVER, "%s: ep3rcvr_failover_callback: rxd %p State %x.%x Xid %llxx MsgXid %llxx nodeId %d - failover\n", 
++                        rail->Generic.Name, rxd, rxdRail->RxdMain->DataEvent, rxdRail->RxdMain->DoneEvent, 
++                        (long long) env->Xid.Unique, (long long) rxd->MsgXid.Unique, env->NodeId);
++              
++              if (EP_XID_INVALID(rxd->MsgXid))
++                  rxd->MsgXid = ep_xid_cache_alloc (sys, &rcvr->XidCache);
++              
++              /* XXXX maybe only send the message if the node failover retry is now ? */
++              msgBody.Failover.Xid      = env->Xid;
++              msgBody.Failover.Railmask = node->ConnectedRails;
++              
++              ep_send_message (&rail->Generic, env->NodeId, EP_MANAGER_MSG_TYPE_FAILOVER_REQUEST, rxd->MsgXid, &msgBody);
++              
++              nodeRail->MessageState |= EP_NODE_ACTIVE_MESSAGES;
++              continue;
++          }
++#endif
++          break;
++
++      case EP_RXD_BEEN_ABORTED:
++          printk ("ep3rcvr_failover_callback: rxd state is aborted but bound to a rail\n");
++          break;
++      }
++
++      EPRINTF3 (DBG_FAILOVER, "%s: ep3rcvr_failover_callback: rxd %p nodeId %d - finished\n", rail->Generic.Name, rxd, env->NodeId);
++    }
++    
++    UnlockRcvrThread (rcvrRail);                                                                      /* PCI unlock */
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++}
++
++void
++ep3rcvr_disconnect_callback (EP_RCVR *rcvr, EP3_RCVR_RAIL *rcvrRail)
++{
++    EP3_RAIL         *rail = RCVR_TO_RAIL (rcvrRail);
++    ELAN3_DEV        *dev = rail->Device;
++    struct list_head *el, *nel;
++    struct list_head  rxdList;
++    unsigned long     flags;
++
++    INIT_LIST_HEAD (&rxdList);
++    
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    LockRcvrThread (rcvrRail);                                                                                /* PCI lock */
++    
++    list_for_each_safe (el, nel, &rcvr->ActiveDescList) {
++      EP_RXD       *rxd      = list_entry (el, EP_RXD, Link);
++      EP3_RXD_RAIL *rxdRail  = (EP3_RXD_RAIL *) rxd->RxdRail;
++      EP_ENVELOPE  *env      = &rxd->RxdMain->Envelope;
++      EP_NODE_RAIL *nodeRail = &rail->Generic.Nodes[env->NodeId];
++      
++      if (rxd->RxdMain->Len == EP_RXD_PENDING || !RXD_BOUND2RAIL(rxdRail,rcvrRail) || nodeRail->State != EP_NODE_DISCONNECTING)
++          continue;
++
++      EPRINTF4 (DBG_DISCON, "%s: ep3rcvr_disconnect_callback: rcvr %p rxd %p elan node %d\n", rail->Generic.Name, rcvr, rxd, env->NodeId);
++
++      switch (rxd->State)
++      {
++      case EP_RXD_FREE:
++          printk ("ep3rcvr_disconnect_callback: rxd state is free but bound to a fail\n");
++          break;
++
++      case EP_RXD_RECEIVE_ACTIVE:
++          if (rxdRail->RxdMain->DataEvent == EP3_EVENT_ACTIVE)                        /* incomplete message receive */
++          {
++              EPRINTF4 (DBG_RCVR, "%s: ep3rcvr_disconnect_callback: rcvr %p rxd %p nodeId %d - unbind\n", rail->Generic.Name, rcvr, rxd, env->NodeId);
++              
++              UnbindRxdFromRail (rxd, rxdRail);
++              
++              /* clear the done flags - so that it will be ignored if an event interrupt is generated */
++              rxdRail->RxdMain->DataEvent = EP3_EVENT_PRIVATE;
++              rxdRail->RxdMain->DoneEvent = EP3_EVENT_PRIVATE;
++              
++              /* clear the data event - the done event should already be zero */
++              elan3_sdram_writel (dev, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Count), 0);       /* PCI write */
++              
++              FreeRxdRail (rcvrRail, rxdRail);
++
++              /* remark it as pending if it was partially received */
++              rxd->RxdMain->Len = EP_RXD_PENDING;
++              
++              /* epcomms thread will requeue on different rail */
++              ep_kthread_schedule (&rcvr->Subsys->Thread, lbolt);
++              continue;
++          }
++          break;
++
++      default:
++          EP_ASSERT (&rail->Generic, EP_IS_RPC(env->Attr));
++
++          if (!EP3_EVENT_FIRED (rxdRail->DoneCookie, rxdRail->RxdMain->DoneEvent))    /* incomplete RPC */
++          {
++              EPRINTF4 (DBG_RCVR, "%s: ep3rcvr_disconnect_callback: rcvr %p rxd %p nodeId %d - not able to failover\n",
++                        rail->Generic.Name, rcvr, rxd, env->NodeId);
++          
++              /* Mark as no longer active */
++              rxdRail->RxdMain->DataEvent = EP3_EVENT_PRIVATE;
++              rxdRail->RxdMain->DoneEvent = EP3_EVENT_PRIVATE;
++              
++              elan3_sdram_writel (dev, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Count), 0);       /* PCI write */
++              elan3_sdram_writel (dev, rxdRail->RxdElan + offsetof (EP3_RXD_RAIL_ELAN, DoneEvent.ev_Count), 0);       /* PCI write */
++              
++              UnbindRxdFromRail (rxd, rxdRail);
++              FreeRxdRail (rcvrRail, rxdRail);
++
++              /* Ignore any previous NMD/failover responses */
++              EP_INVALIDATE_XID (rxd->MsgXid);
++              
++              /* Remove from active list */
++              list_del (&rxd->Link);
++              
++              if (rxd->State == EP_RXD_RPC_IN_PROGRESS)                               /* ownder by user .... */
++                  rxd->State = EP_RXD_BEEN_ABORTED;
++              else                                                                    /* queue for completion */
++              {
++                  rxd->RxdMain->Len = EP_CONN_RESET;                                  /* ensure ep_rxd_status() fails */
++                  list_add_tail (&rxd->Link, &rxdList);
++              }
++              continue;
++          }
++          break;
++
++      case EP_RXD_BEEN_ABORTED:
++          printk ("ep4rcvr_failover_callback: rxd state is aborted but bound to a fail\n");
++          break;
++      }
++          
++      EPRINTF4 (DBG_RCVR, "%s: ep3rcvr_disconnect_callback: rcvr %p rxd %p nodeId %d - finished\n", 
++                rail->Generic.Name, rcvr, rxd, env->NodeId);
++    }
++    
++    UnlockRcvrThread (rcvrRail);                                                                      /* PCI unlock */
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++
++    while (! list_empty (&rxdList)) 
++    {
++      EP_RXD *rxd = list_entry (rxdList.next, EP_RXD, Link);
++
++      list_del (&rxd->Link);
++
++      rxd->Handler (rxd);
++    }
++}
++
++void
++ep3rcvr_display_rxd (DisplayInfo *di, EP_RXD_RAIL *r)
++{
++    EP3_RXD_RAIL *rxdRail = (EP3_RXD_RAIL *) r;
++    sdramaddr_t   rxdElan = rxdRail->RxdElan;
++    EP3_RAIL     *rail    = RCVR_TO_RAIL (rxdRail->Generic.RcvrRail);
++    ELAN3_DEV    *dev     = rail->Device;
++
++    (di->func)(di->arg, "      ChainEvent=%x.%x %x.%x\n",
++             elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, ChainEvent[0].ev_Count)),
++             elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, ChainEvent[0].ev_Type)),
++             elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, ChainEvent[1].ev_Count)),
++             elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, ChainEvent[1].ev_Type)));
++    (di->func)(di->arg, "      ChainEvent=%x.%x %x.%x\n",
++             elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, ChainEvent[2].ev_Count)),
++             elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, ChainEvent[2].ev_Type)),
++             elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, ChainEvent[3].ev_Count)),
++             elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, ChainEvent[3].ev_Type)));
++    (di->func)(di->arg, "      DataEvent=%x.%x DoneEvent=%x.%x\n",
++             elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Count)),
++             elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, DataEvent.ev_Type)),
++             elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, DoneEvent.ev_Count)),
++             elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, DoneEvent.ev_Type)));
++    (di->func)(di->arg, "      Data=%x Len=%x\n",
++             elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, Data.nmd_addr)),
++             elan3_sdram_readl (dev, rxdElan + offsetof (EP3_RXD_RAIL_ELAN, Data.nmd_len)));
++}
++
++void
++ep3rcvr_display_rcvr (DisplayInfo *di, EP_RCVR_RAIL *r)
++{
++    EP3_RCVR_RAIL  *rcvrRail  = (EP3_RCVR_RAIL *) r;
++    EP3_COMMS_RAIL *commsRail = (EP3_COMMS_RAIL *) rcvrRail->Generic.CommsRail;
++    EP3_RAIL       *rail      = RCVR_TO_RAIL (rcvrRail);
++    ELAN3_DEV      *dev       = rail->Device;
++    sdramaddr_t     queue     = commsRail->QueueDescs + rcvrRail->Generic.Rcvr->Service * sizeof (EP3_InputQueue);
++    E3_Addr         qbase      = elan3_sdram_readl (dev, queue + offsetof (EP3_InputQueue, q_base));
++    E3_Addr         qtop       = elan3_sdram_readl (dev, queue + offsetof (EP3_InputQueue, q_top));
++    E3_uint32       qsize      = elan3_sdram_readl (dev, queue + offsetof (EP3_InputQueue, q_size));
++    int             freeCount  = 0;
++    int             blockCount = 0;
++    unsigned long   flags;
++    struct list_head *el;
++
++    spin_lock_irqsave (&rcvrRail->FreeDescLock, flags);
++    list_for_each (el, &rcvrRail->FreeDescList)
++      freeCount++;
++    list_for_each (el, &rcvrRail->DescBlockList)
++      blockCount++;
++    spin_unlock_irqrestore (&rcvrRail->FreeDescLock, flags);
++
++    (di->func)(di->arg, "                 Rail %d FreeDesc %d (%d) Total %d Blocks %d %s\n",
++             rail->Generic.Number, rcvrRail->FreeDescCount, freeCount, rcvrRail->TotalDescCount, blockCount, 
++             rcvrRail->ThreadWaiting ? "ThreadWaiting" : "");
++    
++    (di->func)(di->arg, "                 InputQueue state=%x bptr=%x size=%x top=%x base=%x fptr=%x\n",
++             elan3_sdram_readl (dev, queue + offsetof (EP3_InputQueue, q_state)),
++             elan3_sdram_readl (dev, queue + offsetof (EP3_InputQueue, q_bptr)),
++             elan3_sdram_readl (dev, queue + offsetof (EP3_InputQueue, q_size)),
++             elan3_sdram_readl (dev, queue + offsetof (EP3_InputQueue, q_top)),
++             elan3_sdram_readl (dev, queue + offsetof (EP3_InputQueue, q_base)),
++             elan3_sdram_readl (dev, queue + offsetof (EP3_InputQueue, q_fptr)));
++    (di->func)(di->arg, "                            event=%x.%x [%x.%x] wevent=%x.%x\n",
++             elan3_sdram_readl (dev, queue + offsetof (EP3_InputQueue, q_event.ev_Type)),
++             elan3_sdram_readl (dev, queue + offsetof (EP3_InputQueue, q_event.ev_Count)),
++             elan3_sdram_readl (dev, queue + offsetof (EP3_InputQueue, q_event.ev_Source)),
++             elan3_sdram_readl (dev, queue + offsetof (EP3_InputQueue, q_event.ev_Dest)),
++             elan3_sdram_readl (dev, queue + offsetof (EP3_InputQueue, q_wevent)),
++             elan3_sdram_readl (dev, queue + offsetof (EP3_InputQueue, q_wcount)));
++    
++    LockRcvrThread (rcvrRail);
++    {
++      E3_Addr     nfptr = elan3_sdram_readl (dev, queue + offsetof (EP3_InputQueue, q_fptr));
++      EP_ENVELOPE env;
++      
++      if (nfptr == qtop)
++          nfptr = qbase;
++      else
++          nfptr += qsize;
++
++      while (nfptr != elan3_sdram_readl (dev, queue + offsetof (E3_Queue, q_bptr)))
++      {
++          elan3_sdram_copyl_from_sdram (dev, rcvrRail->InputQueueBase + (nfptr - rcvrRail->InputQueueAddr),
++                                        &env, sizeof (EP_ENVELOPE));
++          
++          (di->func)(di->arg, "                 ENVELOPE Version=%x Attr=%x Xid=%08x.%08x.%016llx\n",
++                     env.Version, env.Attr, env.Xid.Generation, env.Xid.Handle, (long long) env.Xid.Unique);
++          (di->func)(di->arg, "                          NodeId=%x Range=%x TxdRail=%x TxdMain=%x.%x.%x\n",
++                     env.NodeId, env.Range, env.TxdRail, env.TxdMain.nmd_addr,
++                     env.TxdMain.nmd_len, env.TxdMain.nmd_attr);
++          
++          
++          if (nfptr == qtop)
++              nfptr = qbase;
++          else
++              nfptr += qsize;
++      }
++    }
++    UnlockRcvrThread (rcvrRail);
++}
++
++void
++ep3rcvr_fillout_rail_stats(EP_RCVR_RAIL *rcvr_rail, char *str) {
++    /* no stats here yet */
++    /* EP3_RCVR_RAIL * ep4rcvr_rail = (EP3_RCVR_RAIL *) rcvr_rail; */
++}
++
+Index: linux-2.6.5/drivers/net/qsnet/ep/epcommsRx_elan4.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/epcommsRx_elan4.c    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/epcommsRx_elan4.c 2005-05-11 12:10:12.499923304 -0400
+@@ -0,0 +1,1758 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: epcommsRx_elan4.c,v 1.30.2.3 2005/03/10 15:24:09 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/epcommsRx_elan4.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++#include <elan/epsvc.h>
++#include <elan/epcomms.h>
++
++#include "debug.h"
++#include "kcomm_vp.h"
++#include "kcomm_elan4.h"
++#include "epcomms_elan4.h"
++
++#include <elan4/trtype.h>
++
++#define RCVR_TO_COMMS(rcvrRail)               ((EP4_COMMS_RAIL *) ((EP_RCVR_RAIL *) rcvrRail)->CommsRail)
++#define RCVR_TO_RAIL(rcvrRail)                ((EP4_RAIL *) ((EP_RCVR_RAIL *) rcvrRail)->CommsRail->Rail)
++#define RCVR_TO_DEV(rcvrRail)         (RCVR_TO_RAIL(rcvrRail)->r_ctxt.ctxt_dev)
++#define RCVR_TO_SUBSYS(rcvrRail)      (((EP_RCVR_RAIL *) rcvrRail)->Rcvr->Subsys)
++
++#define RXD_TO_RCVR(txdRail)          ((EP4_RCVR_RAIL *) rxdRail->rxd_generic.RcvrRail)
++#define RXD_TO_RAIL(txdRail)          RCVR_TO_RAIL(RXD_TO_RCVR(rxdRail))
++
++static void rxd_interrupt (EP4_RAIL *rail, void *arg);
++
++static __inline__ void 
++__ep4_rxd_assert_free (EP4_RXD_RAIL *rxdRail, const char *file, const int line)
++{
++    EP4_RCVR_RAIL *rcvrRail = RXD_TO_RCVR(rxdRail);
++    ELAN4_DEV     *dev      = RCVR_TO_DEV(rcvrRail);
++    register int i, failed = 0;
++    
++    for (i = 0; i <= EP_MAXFRAG; i++)
++      if (((rxdRail)->rxd_main->rxd_sent[i] != EP4_STATE_FREE)) 
++          failed |= (1 << i);
++    
++    if (((rxdRail)->rxd_main->rxd_failed != EP4_STATE_FREE))
++      failed |= (1 << 5);
++    if (((rxdRail)->rxd_main->rxd_done   != EP4_STATE_FREE)) 
++      failed |= (1 << 6);
++    
++    if (sdram_assert)
++    {
++      if (((elan4_sdram_readq (RXD_TO_RAIL(rxdRail)->r_ctxt.ctxt_dev, (rxdRail)->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_start.ev_CountAndType)) >> 32) != 0)) 
++          failed |= (1 << 7);
++      for (i = 0; i < EP_MAXFRAG; i++)
++          if (((elan4_sdram_readq (dev, (rxdRail)->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[i].ev_CountAndType)) >> 32) != 0)) 
++              failed |= (1 << (8 + i));
++      if (((elan4_sdram_readq (dev, (rxdRail)->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done.ev_CountAndType)) >> 32) != 0)) 
++          failed |= (1 << 12);
++      if (((int)(elan4_sdram_readq (dev, (rxdRail)->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_failed.ev_CountAndType)) >> 32) != -32)) 
++          failed |= (1 << 13);
++    }
++
++    if (failed)
++    {
++      printk ("__ep4_rxd_assert_free: failed=%x rxdRail=%p %s - %d\n", failed, rxdRail, file, line);
++
++      ep_debugf (DBG_DEBUG, "__ep4_rxd_assert_free: failed=%x rxdRail=%p %s - %d\n", failed, rxdRail, file, line);
++      ep4rcvr_display_rxd (&di_ep_debug, &rxdRail->rxd_generic);
++
++      for (i = 0; i <= EP_MAXFRAG; i++)
++          (rxdRail)->rxd_main->rxd_sent[i] = EP4_STATE_FREE;
++
++      (rxdRail)->rxd_main->rxd_failed = EP4_STATE_FREE;
++      (rxdRail)->rxd_main->rxd_done   = EP4_STATE_FREE;
++
++      if (sdram_assert)
++      {
++          elan4_sdram_writew (RXD_TO_RAIL(rxdRail)->r_ctxt.ctxt_dev,
++                              (rxdRail)->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_start.ev_CountAndType) + 4, 0);
++
++          for (i = 0; i < EP_MAXFRAG; i++)
++              elan4_sdram_writew (dev, (rxdRail)->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[i].ev_CountAndType) + 4, 0);
++          elan4_sdram_writew (dev, (rxdRail)->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done.ev_CountAndType) + 4, 0);
++          elan4_sdram_writew (dev, (rxdRail)->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_failed.ev_CountAndType) + 4, -32);
++      }
++      EP_ASSFAIL (RCVR_TO_RAIL(rcvrRail), "__ep4_rxd_assert_free");
++    }
++}
++
++static __inline__ void
++__ep4_rxd_assert_pending(EP4_RXD_RAIL *rxdRail, const char *file, const int line)
++{ 
++    EP4_RCVR_RAIL *rcvrRail = RXD_TO_RCVR(rcvrRail);
++    register int failed = 0;
++
++    failed |= ((rxdRail)->rxd_main->rxd_done != EP4_STATE_ACTIVE);
++
++    if (failed)
++    {
++      printk ("__ep4_rxd_assert_pending: %s - %d\n", file, line);
++
++      ep_debugf (DBG_DEBUG, "__ep4_rxd_assert_pending: %s - %d\n", file, line);
++      ep4rcvr_display_rxd (&di_ep_debug, &rxdRail->rxd_generic);
++
++      (rxdRail)->rxd_main->rxd_done = EP4_STATE_ACTIVE;
++
++      EP_ASSFAIL (RCVR_TO_RAIL(rcvrRail), "__ep4_rxd_assert_pending");
++    }
++}
++
++static __inline__ void
++__ep4_rxd_assert_private(EP4_RXD_RAIL *rxdRail, const char *file, const int line)
++{
++    EP4_RCVR_RAIL *rcvrRail = RXD_TO_RCVR(rxdRail);
++    ELAN4_DEV     *dev      = RCVR_TO_DEV(rcvrRail);
++    register int failed = 0;
++
++    if (((rxdRail)->rxd_main->rxd_failed != EP4_STATE_ACTIVE)) failed |= (1 << 0);
++    if (((rxdRail)->rxd_main->rxd_done != EP4_STATE_PRIVATE))  failed |= (1 << 1);
++    
++    if (sdram_assert)
++    {
++      if (((elan4_sdram_readq (dev, (rxdRail)->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done.ev_CountAndType)) >> 32) != 0))           failed |= (1 << 2);
++      if (((int) (elan4_sdram_readq (dev, (rxdRail)->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_failed.ev_CountAndType)) >> 32) != -32)) failed |= (1 << 3);
++    }
++
++    if (failed)
++    {
++      printk ("__ep4_rxd_assert_private: %s - %d\n", file, line);
++
++      ep_debugf (DBG_DEBUG, "__ep4_rxd_assert_private: %s - %d\n", file, line);
++      ep4rcvr_display_rxd (&di_ep_debug, &rxdRail->rxd_generic);
++
++      (rxdRail)->rxd_main->rxd_failed = EP4_STATE_ACTIVE;
++      (rxdRail)->rxd_main->rxd_done   = EP4_STATE_PRIVATE;
++
++      if (sdram_assert)
++      {
++          elan4_sdram_writew (dev, (rxdRail)->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done.ev_CountAndType) + 4, 0);
++          elan4_sdram_writew (dev, (rxdRail)->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_failed.ev_CountAndType) + 4, -32);
++      }
++
++      EP_ASSFAIL (RCVR_TO_RAIL(rcvrRail), "__ep4_rxd_assert_private");
++    }
++}
++
++static __inline__ void
++__ep4_rxd_private_to_free (EP4_RXD_RAIL *rxdRail)
++{
++    register int i;
++
++    for (i = 0; i <= EP_MAXFRAG; i++)
++      rxdRail->rxd_main->rxd_sent[i] = EP4_STATE_FREE;
++ 
++    rxdRail->rxd_main->rxd_failed = EP4_STATE_FREE;
++    rxdRail->rxd_main->rxd_done   = EP4_STATE_FREE;
++}
++
++static __inline__ void
++__ep4_rxd_force_private (EP4_RXD_RAIL *rxdRail)
++{
++    EP4_RAIL  *rail = RXD_TO_RAIL(rxdRail);
++    ELAN4_DEV *dev  = rail->r_ctxt.ctxt_dev;
++
++    (rxdRail)->rxd_main->rxd_failed = EP4_STATE_ACTIVE;
++    (rxdRail)->rxd_main->rxd_done = EP4_STATE_PRIVATE;
++
++    if (sdram_assert) 
++      elan4_sdram_writeq (dev, (rxdRail)->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done.ev_CountAndType),
++                          E4_EVENT_INIT_VALUE(0, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++}
++
++#define EP4_RXD_ASSERT_FREE(rxdRail)          __ep4_rxd_assert_free(rxdRail, __FILE__, __LINE__)
++#define EP4_RXD_ASSERT_PENDING(rxdRail)               __ep4_rxd_assert_pending(rxdRail, __FILE__, __LINE__)
++#define EP4_RXD_ASSERT_PRIVATE(rxdRail)               __ep4_rxd_assert_private(rxdRail, __FILE__, __LINE__)
++#define EP4_RXD_PRIVATE_TO_FREE(rxdRail)      __ep4_rxd_private_to_free(rxdRail)
++#define EP4_RXD_FORCE_PRIVATE(rxdRail)                __ep4_rxd_force_private(rxdRail)
++
++static int
++alloc_rxd_block (EP4_RCVR_RAIL *rcvrRail)
++{
++    EP4_RAIL           *rail = RCVR_TO_RAIL (rcvrRail);
++    ELAN4_DEV          *dev  = rail->r_ctxt.ctxt_dev;
++    EP4_RXD_RAIL_BLOCK *blk;
++    EP4_RXD_RAIL_MAIN  *rxdMain;
++    EP_ADDR           rxdMainAddr;
++    sdramaddr_t               rxdElan;
++    EP_ADDR           rxdElanAddr;
++    EP4_RXD_RAIL       *rxdRail;
++    unsigned long       flags;
++    int                 i, j;
++
++    KMEM_ZALLOC (blk, EP4_RXD_RAIL_BLOCK *, sizeof (EP4_RXD_RAIL_BLOCK), 1);
++
++    if (blk == NULL)
++      return 0;
++
++    if ((rxdElan = ep_alloc_elan (&rail->r_generic, EP4_RXD_RAIL_ELAN_SIZE * EP4_NUM_RXD_PER_BLOCK, 0, &rxdElanAddr)) == (sdramaddr_t) 0)
++    {
++      KMEM_FREE (blk, sizeof (EP4_RXD_RAIL_BLOCK));
++      return 0;
++    }
++
++    if ((rxdMain = ep_alloc_main (&rail->r_generic, EP4_RXD_RAIL_MAIN_SIZE * EP4_NUM_RXD_PER_BLOCK, 0, &rxdMainAddr)) == (EP4_RXD_RAIL_MAIN *) NULL)
++    {
++      ep_free_elan (&rail->r_generic, rxdElanAddr, EP4_RXD_RAIL_ELAN_SIZE * EP4_NUM_RXD_PER_BLOCK);
++      KMEM_FREE (blk, sizeof (EP4_RXD_RAIL_BLOCK));
++      return 0;
++    }
++
++    if (ep4_reserve_dma_retries (rail, EP4_NUM_RXD_PER_BLOCK, 0) != 0)
++    {
++      ep_free_main (&rail->r_generic, blk->blk_rxds[0].rxd_main_addr, EP4_RXD_RAIL_MAIN_SIZE * EP4_NUM_RXD_PER_BLOCK);
++      ep_free_elan (&rail->r_generic, rxdElanAddr, EP4_RXD_RAIL_ELAN_SIZE * EP4_NUM_RXD_PER_BLOCK);
++      KMEM_FREE (blk, sizeof (EP4_RXD_RAIL_BLOCK));
++
++      return 0;
++    }
++
++    for (rxdRail = &blk->blk_rxds[0], i = 0; i < EP4_NUM_RXD_PER_BLOCK; i++, rxdRail++)
++    {
++      rxdRail->rxd_generic.RcvrRail = &rcvrRail->rcvr_generic;
++      rxdRail->rxd_elan             = rxdElan;
++      rxdRail->rxd_elan_addr        = rxdElanAddr;
++      rxdRail->rxd_main             = rxdMain;
++      rxdRail->rxd_main_addr        = rxdMainAddr;
++
++      /* reserve 128 bytes of "event" cq space for the chained STEN packets */
++      if ((rxdRail->rxd_ecq = ep4_get_ecq (rail, EP4_ECQ_EVENT, EP4_RXD_STEN_CMD_NDWORDS)) == NULL)
++          goto failed;
++
++      /* allocate a single word of "setevent" command space */
++      if ((rxdRail->rxd_scq = ep4_get_ecq (rail, EP4_ECQ_SINGLE, 1)) == NULL)
++      {
++          ep4_put_ecq (rail, rxdRail->rxd_ecq, EP4_RXD_STEN_CMD_NDWORDS);
++          goto failed;
++      }
++
++      /* initialise the completion events */
++      for (j = 0; j <= EP_MAXFRAG; j++)
++          rxdMain->rxd_sent[i] = EP4_STATE_FREE;
++
++      rxdMain->rxd_done   = EP4_STATE_FREE;
++      rxdMain->rxd_failed = EP4_STATE_FREE;
++
++      /* initialise the scq for the thread */
++      rxdMain->rxd_scq = rxdRail->rxd_scq->ecq_addr;
++
++      /* initialise the "start" event to copy the first STEN packet into the command queue */
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_start.ev_CountAndType),
++                          E4_EVENT_INIT_VALUE(0, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_RXD_START_CMD_NDWORDS));
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_start.ev_CopySource),
++                          rxdElanAddr + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[0]));
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_start.ev_CopyDest),
++                          rxdRail->rxd_ecq->ecq_addr);
++
++      /* initialise the "chain" events to copy the next STEN packet into the command queue */
++      for (j = 0; j < EP_MAXFRAG; j++)
++      {
++          elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[j].ev_CountAndType),
++                              E4_EVENT_INIT_VALUE(0, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_RXD_STEN_CMD_NDWORDS));
++          elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[j].ev_CopySource),
++                              rxdElanAddr + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[j+1]));
++          elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[j].ev_CopyDest),
++                              rxdRail->rxd_ecq->ecq_addr);
++      }
++
++      /* initialise the portions of the sten packets which don't change */
++      for (j = 0; j < EP_MAXFRAG+1; j++)
++      {
++          if (j < EP_MAXFRAG)
++              elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[j].c_dma_dstEvent),
++                                  rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[j]));
++          else
++              elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[j].c_dma_dstEvent),
++                                  rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done));
++
++          elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[j].c_ok_guard),
++                              GUARD_CMD | GUARD_CHANNEL (1) | GUARD_TEST(0, PACK_OK) | GUARD_RESET (EP4_STEN_RETRYCOUNT));
++          elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[j].c_ok_write_cmd),
++                              WRITE_DWORD_CMD | (rxdMainAddr + offsetof (EP4_RXD_RAIL_MAIN, rxd_sent[j])));
++          elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[j].c_ok_write_value),
++                              EP4_STATE_FINISHED);
++          elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[j].c_fail_guard),
++                              GUARD_CMD | GUARD_CHANNEL (1) | GUARD_TEST(0, RESTART_COUNT_ZERO) | GUARD_RESET (EP4_STEN_RETRYCOUNT));
++          elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[j].c_fail_setevent),
++                              SET_EVENT_CMD | (rxdElanAddr + offsetof (EP4_RXD_RAIL_ELAN, rxd_failed)));
++          elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[j].c_nop_cmd),
++                              NOP_CMD);
++      }
++
++      /* register a main interrupt cookie */
++      ep4_register_intcookie (rail, &rxdRail->rxd_intcookie, rxdElanAddr + offsetof (EP4_RXD_RAIL_ELAN, rxd_done),
++                              rxd_interrupt, rxdRail);
++
++      /* initialise the command stream for the done event */
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done_cmd.c_write_cmd),
++                          WRITE_DWORD_CMD | (rxdMainAddr + offsetof (EP4_RXD_RAIL_MAIN, rxd_done)));
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done_cmd.c_write_value),
++                          EP4_STATE_FINISHED);
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done_cmd.c_intr_cmd),
++                          INTERRUPT_CMD | (rxdRail->rxd_intcookie.int_val << E4_MAIN_INT_SHIFT));
++
++      /* initialise the command stream for the fail event */
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_failed_cmd.c_write_cmd),
++                          WRITE_DWORD_CMD | (rxdMainAddr + offsetof (EP4_RXD_RAIL_MAIN, rxd_failed)));
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_failed_cmd.c_write_value),
++                          EP4_STATE_FAILED);
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_failed_cmd.c_intr_cmd),
++                          INTERRUPT_CMD | (rxdRail->rxd_intcookie.int_val << E4_MAIN_INT_SHIFT));
++
++      /* initialise the done and fail events */
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done.ev_CountAndType),
++                          E4_EVENT_INIT_VALUE(0, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done.ev_CopySource),
++                          rxdElanAddr + offsetof (EP4_RXD_RAIL_ELAN, rxd_done_cmd));
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done.ev_CopyDest),
++                          rxdRail->rxd_ecq->ecq_addr);
++
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_failed.ev_CountAndType),
++                          E4_EVENT_INIT_VALUE(-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_failed.ev_CopySource),
++                          rxdElanAddr + offsetof (EP4_RXD_RAIL_ELAN, rxd_failed_cmd));
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_failed.ev_CopyDest),
++                          rxdRail->rxd_ecq->ecq_addr);
++      
++      /* initialise the pointer to the main memory portion */
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_main), 
++                          rxdMainAddr);
++
++      /* move onto next descriptor */
++      rxdElan     += EP4_RXD_RAIL_ELAN_SIZE;
++      rxdElanAddr += EP4_RXD_RAIL_ELAN_SIZE;
++      rxdMain      = (EP4_RXD_RAIL_MAIN *) ((unsigned long) rxdMain + EP4_RXD_RAIL_MAIN_SIZE);
++      rxdMainAddr += EP4_RXD_RAIL_MAIN_SIZE;
++    }
++
++    spin_lock_irqsave (&rcvrRail->rcvr_freelock, flags);
++
++    list_add  (&blk->blk_link, &rcvrRail->rcvr_blocklist);
++
++    rcvrRail->rcvr_totalcount += EP4_NUM_RXD_PER_BLOCK;
++    rcvrRail->rcvr_freecount  += EP4_NUM_RXD_PER_BLOCK;
++
++    for (i = 0; i < EP4_NUM_RXD_PER_BLOCK; i++)
++      list_add (&blk->blk_rxds[i].rxd_generic.Link, &rcvrRail->rcvr_freelist);
++
++    spin_unlock_irqrestore (&rcvrRail->rcvr_freelock, flags);
++
++    return 1;
++
++ failed:
++    while (--i >= 0)
++    {
++      rxdRail--;
++
++      ep4_put_ecq (rail, rxdRail->rxd_ecq, EP4_RXD_STEN_CMD_NDWORDS);
++      ep4_put_ecq (rail, rxdRail->rxd_scq, 1);
++
++      ep4_deregister_intcookie (rail, &rxdRail->rxd_intcookie);
++    }
++
++    ep4_release_dma_retries (rail, EP4_NUM_RXD_PER_BLOCK);
++    
++    ep_free_main (&rail->r_generic, blk->blk_rxds[0].rxd_main_addr, EP4_RXD_RAIL_MAIN_SIZE * EP4_NUM_RXD_PER_BLOCK);
++    ep_free_elan (&rail->r_generic, rxdElanAddr, EP4_RXD_RAIL_ELAN_SIZE * EP4_NUM_RXD_PER_BLOCK);
++    KMEM_FREE (blk, sizeof (EP4_RXD_RAIL_BLOCK));
++
++    return 0;
++}
++
++
++static void
++free_rxd_block (EP4_RCVR_RAIL *rcvrRail, EP4_RXD_RAIL_BLOCK *blk)
++{
++    EP4_RAIL     *rail = RCVR_TO_RAIL (rcvrRail);
++    EP4_RXD_RAIL *rxdRail;
++    unsigned long flags;
++    int           i;
++
++    spin_lock_irqsave (&rcvrRail->rcvr_freelock, flags);
++
++    list_del (&blk->blk_link);
++
++    rcvrRail->rcvr_totalcount -= EP4_NUM_RXD_PER_BLOCK;
++
++    for (rxdRail = &blk->blk_rxds[0], i = 0; i < EP4_NUM_RXD_PER_BLOCK; i++, rxdRail++)
++    {
++      rcvrRail->rcvr_freecount--;
++
++      ep4_put_ecq (rail, rxdRail->rxd_ecq, EP4_RXD_STEN_CMD_NDWORDS);
++      ep4_put_ecq (rail, rxdRail->rxd_scq, 1);
++
++      ep4_deregister_intcookie (rail, &rxdRail->rxd_intcookie);
++
++      list_del (&rxdRail->rxd_generic.Link);
++    }
++    spin_unlock_irqrestore (&rcvrRail->rcvr_freelock, flags);
++
++    ep4_release_dma_retries (rail, EP4_NUM_RXD_PER_BLOCK);
++
++    ep_free_main (&rail->r_generic, blk->blk_rxds[0].rxd_main_addr, EP4_RXD_RAIL_MAIN_SIZE * EP4_NUM_RXD_PER_BLOCK);
++    ep_free_elan (&rail->r_generic, blk->blk_rxds[0].rxd_elan_addr, EP4_RXD_RAIL_ELAN_SIZE * EP4_NUM_RXD_PER_BLOCK);
++
++    KMEM_FREE (blk, sizeof (EP4_RXD_RAIL_BLOCK));
++}
++
++static EP4_RXD_RAIL *
++get_rxd_rail (EP4_RCVR_RAIL *rcvrRail)
++{
++    EP_COMMS_SUBSYS  *subsys = RCVR_TO_SUBSYS(rcvrRail);
++    EP4_RXD_RAIL     *rxdRail;
++    unsigned long flags;
++    int low_on_rxds;
++
++    spin_lock_irqsave (&rcvrRail->rcvr_freelock, flags);
++
++    if (list_empty (&rcvrRail->rcvr_freelist))
++      rxdRail = NULL;
++    else
++    {
++      rxdRail = list_entry (rcvrRail->rcvr_freelist.next, EP4_RXD_RAIL, rxd_generic.Link);
++
++      EP4_RXD_ASSERT_FREE(rxdRail);
++
++      list_del (&rxdRail->rxd_generic.Link);
++
++      rcvrRail->rcvr_freecount--;
++    }
++    /* Wakeup the descriptor primer thread if there's not many left */
++    low_on_rxds = (rcvrRail->rcvr_freecount < ep_rxd_lowat);
++
++    spin_unlock_irqrestore (&rcvrRail->rcvr_freelock, flags);
++
++    if (low_on_rxds)
++      ep_kthread_schedule (&subsys->Thread, lbolt);
++
++    return (rxdRail);
++}
++
++static void
++free_rxd_rail (EP4_RCVR_RAIL *rcvrRail, EP4_RXD_RAIL *rxdRail)
++{
++    unsigned long flags;
++
++    EP4_RXD_ASSERT_FREE(rxdRail);
++
++    spin_lock_irqsave (&rcvrRail->rcvr_freelock, flags);
++    
++    list_add (&rxdRail->rxd_generic.Link, &rcvrRail->rcvr_freelist);
++
++    rcvrRail->rcvr_freecount++;
++
++    if (rcvrRail->rcvr_freewaiting)
++    {
++      rcvrRail->rcvr_freewaiting--;
++      kcondvar_wakeupall (&rcvrRail->rcvr_freesleep, &rcvrRail->rcvr_freelock);
++    }
++
++    spin_unlock_irqrestore (&rcvrRail->rcvr_freelock, flags);
++}
++
++static void
++bind_rxd_rail (EP_RXD *rxd, EP4_RXD_RAIL *rxdRail)
++{
++    EP4_RAIL *rail = RCVR_TO_RAIL (rxdRail->rxd_generic.RcvrRail);
++
++    ASSERT (SPINLOCK_HELD (&rxd->Rcvr->Lock));
++
++    EPRINTF3 (DBG_RCVR, "%s: bind_rxd_rail: rxd=%p rxdRail=%p\n",  rail->r_generic.Name, rxd, rxdRail);
++
++    elan4_sdram_writeq (rail->r_ctxt.ctxt_dev, rxdRail->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_rxd), rxd->NmdMain.nmd_addr);                     /* PCI write */
++
++    rxd->RxdRail             = &rxdRail->rxd_generic;
++    rxdRail->rxd_generic.Rxd = rxd;
++}
++
++static void
++unbind_rxd_rail (EP_RXD *rxd, EP4_RXD_RAIL *rxdRail)
++{
++    EP4_RCVR_RAIL *rcvrRail = (EP4_RCVR_RAIL *) rxdRail->rxd_generic.RcvrRail;
++    
++    ASSERT (SPINLOCK_HELD (&rxd->Rcvr->Lock));
++    ASSERT (rxd->RxdRail == &rxdRail->rxd_generic && rxdRail->rxd_generic.Rxd == rxd);
++
++    EP4_RXD_ASSERT_PRIVATE (rxdRail);
++
++    EPRINTF3 (DBG_RCVR, "%s: unbind_rxd_rail: rxd=%p rxdRail=%p\n",  RCVR_TO_RAIL(rcvrRail)->r_generic.Name, rxd, rxdRail);
++
++    rxd->RxdRail             = NULL;
++    rxdRail->rxd_generic.Rxd = NULL;
++
++    if (rcvrRail->rcvr_cleanup_waiting)
++      kcondvar_wakeupall (&rcvrRail->rcvr_cleanup_sleep, &rxd->Rcvr->Lock);
++    rcvrRail->rcvr_cleanup_waiting = 0;
++
++    EP4_RXD_PRIVATE_TO_FREE (rxdRail);
++}
++
++
++static void
++rcvr_stall_interrupt (EP4_RAIL *rail, void *arg)
++{
++    EP4_RCVR_RAIL *rcvrRail = (EP4_RCVR_RAIL *) arg;
++    EP_RCVR       *rcvr     = rcvrRail->rcvr_generic.Rcvr;
++    unsigned long  flags;
++
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    
++    EPRINTF1 (DBG_RCVR, "rcvr_stall_interrupt: rcvrRail %p thread halted\n", rcvrRail);
++
++    rcvrRail->rcvr_thread_halted = 1;
++
++    kcondvar_wakeupall (&rcvrRail->rcvr_cleanup_sleep, &rcvr->Lock);
++
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++}
++
++static void
++rcvr_stall_haltop (ELAN4_DEV *dev, void *arg)
++{
++    EP4_RCVR_RAIL  *rcvrRail  = (EP4_RCVR_RAIL *) arg;
++    EP4_COMMS_RAIL *commsRail = RCVR_TO_COMMS(rcvrRail);
++    EP_RCVR        *rcvr      = rcvrRail->rcvr_generic.Rcvr;
++    sdramaddr_t     qdesc     = ((EP4_COMMS_RAIL *) commsRail)->r_descs + (rcvr->Service * EP_QUEUE_DESC_SIZE);
++    E4_uint64       qbptr     = elan4_sdram_readq (dev, qdesc + offsetof (E4_InputQueue, q_bptr));
++
++    /* Mark the queue as full by writing the fptr */
++    if (qbptr == (rcvrRail->rcvr_slots_addr + EP_INPUTQ_SIZE * (rcvr->InputQueueEntries-1)))
++      elan4_sdram_writeq (dev, qdesc + offsetof (E4_InputQueue, q_fptr), rcvrRail->rcvr_slots_addr);
++    else
++      elan4_sdram_writeq (dev, qdesc + offsetof (E4_InputQueue, q_fptr), qbptr + EP_INPUTQ_SIZE);
++
++    /* Notify the thread that it should stall after processing any outstanding envelopes */
++    elan4_sdram_writeq (dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_stall_intcookie),
++                      rcvrRail->rcvr_stall_intcookie.int_val);
++
++    /* Issue a swtevent to the queue event to wake the thread up */
++    ep4_set_event_cmd (rcvrRail->rcvr_resched, rcvrRail->rcvr_elan_addr + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_qevent));
++}
++
++static void
++rxd_interrupt (EP4_RAIL *rail, void *arg)
++{
++    EP4_RXD_RAIL      *rxdRail  = (EP4_RXD_RAIL *) arg;
++    EP4_RCVR_RAIL     *rcvrRail = (EP4_RCVR_RAIL *) rxdRail->rxd_generic.RcvrRail;
++    EP_RCVR           *rcvr     = rcvrRail->rcvr_generic.Rcvr;
++    EP4_RXD_RAIL_MAIN *rxdMain  = rxdRail->rxd_main;
++    unsigned long      delay    = 1;
++    EP_RXD            *rxd;
++    EP_ENVELOPE       *env;
++    unsigned long      flags;
++
++    spin_lock_irqsave (&rcvr->Lock, flags);
++
++    for (;;)
++    {
++      if (rxdMain->rxd_done == EP4_STATE_FINISHED || rxdMain->rxd_failed == EP4_STATE_FAILED)
++          break;
++
++      /* The write to rxd_done could be held up in the PCI bridge even though
++       * we've seen the interrupt cookie.  Unlike elan3, there is no possibility
++       * of spurious interrupts since we flush the command queues on node 
++       * disconnection and the txcallback mechanism */
++      mb();
++
++      if (delay > EP4_EVENT_FIRING_TLIMIT)
++      {
++          spin_unlock_irqrestore (&rcvr->Lock, flags);
++
++          EP_ASSFAIL (RCVR_TO_RAIL(rcvrRail), "rxd_interrupt - not finished\n");
++          return;
++      }
++      DELAY(delay);
++      delay <<= 1;
++    }
++
++    if (rxdMain->rxd_done != EP4_STATE_FINISHED)
++    {
++      EPRINTF8 (DBG_RETRY, "%s: rxd_interrupt: rxdRail %p retry: done=%d failed=%d NodeId=%d XID=%08x.%08x.%016llx\n",
++                rail->r_generic.Name, rxdRail, (int)rxdMain->rxd_done, (int)rxdMain->rxd_failed, rxdRail->rxd_generic.Rxd->RxdMain->Envelope.NodeId,
++                rxdRail->rxd_generic.Rxd->RxdMain->Envelope.Xid.Generation, rxdRail->rxd_generic.Rxd->RxdMain->Envelope.Xid.Handle, 
++                rxdRail->rxd_generic.Rxd->RxdMain->Envelope.Xid.Unique);
++    
++      spin_lock (&rcvrRail->rcvr_retrylock);
++
++      rxdRail->rxd_retry_time = lbolt + EP_RETRY_LOW_PRI_TIME;                        /* XXXX backoff ? */
++
++      list_add_tail (&rxdRail->rxd_retry_link, &rcvrRail->rcvr_retrylist);
++
++      ep_kthread_schedule (&rail->r_retry_thread, rxdRail->rxd_retry_time);
++      spin_unlock (&rcvrRail->rcvr_retrylock);
++
++      spin_unlock_irqrestore (&rcvr->Lock, flags);
++      return;
++    }
++    
++    rxd = rxdRail->rxd_generic.Rxd;
++    env = &rxd->RxdMain->Envelope;
++
++    /*
++     * Note, since the thread will have sent the remote dma packet before copying 
++     * the envelope, we must check that it has completed doing this,  we do this
++     * by acquiring the spinlock against the thread which it only drops once it's
++     * completed.
++     */
++    if (rxd->RxdMain->Len == EP_RXD_PENDING)
++    {
++      EP4_SPINENTER (rail->r_ctxt.ctxt_dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_thread_lock),
++                     &rcvrRail->rcvr_main->rcvr_thread_lock);
++      
++      EP4_SPINEXIT (rail->r_ctxt.ctxt_dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_thread_lock),
++                    &rcvrRail->rcvr_main->rcvr_thread_lock);
++      
++      ASSERT (env->Version == EP_ENVELOPE_VERSION && rxd->RxdMain->Len != EP_RXD_PENDING);
++    }
++
++    EPRINTF8 (DBG_RCVR, "%s: rxd_interrupt: rxd %p finished from %d XID %08x.%08x.%016llx len %d attr %x\n", rail->r_generic.Name, 
++            rxd, rxd->RxdMain->Envelope.NodeId, rxd->RxdMain->Envelope.Xid.Generation, rxd->RxdMain->Envelope.Xid.Handle, 
++            rxd->RxdMain->Envelope.Xid.Unique,  rxd->RxdMain->Len, rxd->RxdMain->Envelope.Attr);
++
++    rxdMain->rxd_done  = EP4_STATE_PRIVATE;
++    rxd->Data.nmd_attr = EP_RAIL2RAILMASK (rail->r_generic.Number);
++
++    switch (rxd->State)
++    {
++    case EP_RXD_RECEIVE_ACTIVE:
++      if (rxd->RxdMain->Len >= 0 && EP_IS_RPC(env->Attr))
++          rxd->State = EP_RXD_RPC_IN_PROGRESS;
++      else
++      {
++          rxd->State = EP_RXD_COMPLETED;
++
++          /* remove from active list */
++          list_del (&rxd->Link);
++
++          unbind_rxd_rail (rxd, rxdRail);
++          free_rxd_rail (rcvrRail, rxdRail);
++      }
++
++      if (rxd->RxdMain->Len >= 0) {
++          INC_STAT(rcvrRail->rcvr_generic.stats,rx);
++          ADD_STAT(rcvrRail->rcvr_generic.stats,rx_len,rxd->RxdMain->Len);
++          INC_STAT(rail->r_generic.Stats,rx);
++          ADD_STAT(rail->r_generic.Stats,rx_len,rxd->RxdMain->Len);
++      }
++      spin_unlock_irqrestore (&rcvr->Lock, flags);
++      ep_rxd_received (rxd);
++
++      break;
++
++    case EP_RXD_PUT_ACTIVE:
++    case EP_RXD_GET_ACTIVE:
++      rxd->State = EP_RXD_RPC_IN_PROGRESS;
++      spin_unlock_irqrestore (&rcvr->Lock, flags);
++      
++      rxd->Handler (rxd);
++      break;
++
++    case EP_RXD_COMPLETE_ACTIVE:
++      rxd->State = EP_RXD_COMPLETED;
++
++      /* remove from active list */
++      list_del (&rxd->Link);
++
++      unbind_rxd_rail (rxd, rxdRail);
++      free_rxd_rail (rcvrRail, rxdRail);
++
++      spin_unlock_irqrestore (&rcvr->Lock, flags);
++
++      rxd->Handler(rxd);
++      break;
++
++    default:
++      spin_unlock_irqrestore (&rcvr->Lock, flags);
++
++      printk ("%s: rxd_interrupt: rxd %p in invalid state %d\n", rail->r_generic.Name, rxd, rxd->State);
++      /* NOTREACHED */
++    }
++}
++
++static void
++ep4rcvr_flush_filtering (EP_RCVR *rcvr, EP4_RCVR_RAIL *rcvrRail)
++{
++    EP4_COMMS_RAIL *commsRail = RCVR_TO_COMMS(rcvrRail);
++    EP4_RAIL       *rail      = RCVR_TO_RAIL(rcvrRail);
++    ELAN4_DEV      *dev       = rail->r_ctxt.ctxt_dev;
++    sdramaddr_t    qdesc      = commsRail->r_descs + (rcvr->Service * EP_QUEUE_DESC_SIZE);
++    E4_Addr        qbase      = rcvrRail->rcvr_slots_addr;
++    E4_Addr        qlast      = qbase + EP_INPUTQ_SIZE * (rcvr->InputQueueEntries-1);
++    E4_uint64      qfptr, qbptr;
++    unsigned long  flags;
++    
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    EP4_SPINENTER (dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_thread_lock), &rcvrRail->rcvr_main->rcvr_thread_lock);
++    
++    /* zip down the input queue and invalidate any envelope we find to a node which is locally passivated */
++    qfptr = elan4_sdram_readq (dev, qdesc + offsetof (E4_InputQueue, q_fptr));
++    qbptr = elan4_sdram_readq (dev, qdesc + offsetof (E4_InputQueue, q_bptr));
++
++    while (qfptr != qbptr)
++    {
++      unsigned int nodeId = elan4_sdram_readl (dev, rcvrRail->rcvr_slots + (qfptr - qbase) + offsetof (EP_ENVELOPE, NodeId));
++
++      EPRINTF3 (DBG_DISCON, "%s: ep4rcvr_flush_filtering: nodeId=%d State=%d\n", rail->r_generic.Name, nodeId, rail->r_generic.Nodes[nodeId].State);
++      
++      if (rail->r_generic.Nodes[nodeId].State == EP_NODE_LOCAL_PASSIVATE)
++          elan4_sdram_writel (dev,  rcvrRail->rcvr_slots + (qfptr - qbase) + offsetof (EP_ENVELOPE, Version), 0);
++      
++      if (qfptr != qlast)
++          qfptr += EP_INPUTQ_SIZE;
++      else
++          qfptr = qbase;
++    }
++
++    /* Insert an setevent command into the thread's command queue
++     * to ensure that all sten packets have completed */
++    elan4_guard (rcvrRail->rcvr_ecq->ecq_cq, GUARD_ALL_CHANNELS);
++    ep4comms_flush_setevent (commsRail, rcvrRail->rcvr_ecq->ecq_cq);
++    
++    EP4_SPINEXIT (dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_thread_lock), &rcvrRail->rcvr_main->rcvr_thread_lock);
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++}
++
++static void
++ep4rcvr_flush_flushing (EP_RCVR *rcvr, EP4_RCVR_RAIL *rcvrRail)
++{
++    EP4_RAIL         *rail = RCVR_TO_RAIL (rcvrRail);
++    ELAN4_DEV      *dev  = rail->r_ctxt.ctxt_dev;
++    struct list_head *el, *nel;
++    struct list_head  rxdList;
++    unsigned long     flags;
++
++    INIT_LIST_HEAD (&rxdList);
++    
++    /* remove any sten packates which are retrying to nodes which are being passivated */
++    spin_lock_irqsave (&rcvrRail->rcvr_retrylock, flags);
++    list_for_each_safe (el, nel, &rcvrRail->rcvr_retrylist) {
++      EP4_RXD_RAIL *rxdRail  = list_entry (el, EP4_RXD_RAIL, rxd_retry_link);
++      EP_ENVELOPE  *env      = &rxdRail->rxd_generic.Rxd->RxdMain->Envelope;
++      EP_NODE_RAIL *nodeRail = &rail->r_generic.Nodes[env->NodeId];
++
++      if (nodeRail->State == EP_NODE_LOCAL_PASSIVATE)
++      {
++          EPRINTF2 (DBG_XMTR, "%s; ep4rcvr_flush_flushing: removing rxdRail %p from retry list\n", rail->r_generic.Name, rxdRail);
++          
++          list_del (&rxdRail->rxd_retry_link);
++      }
++    }
++    spin_unlock_irqrestore (&rcvrRail->rcvr_retrylock, flags);
++
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    EP4_SPINENTER (dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_thread_lock), &rcvrRail->rcvr_main->rcvr_thread_lock);
++    
++    list_for_each_safe (el, nel, &rcvr->ActiveDescList) {
++      EP_RXD       *rxd      = list_entry (el, EP_RXD, Link);
++      EP4_RXD_RAIL *rxdRail  = (EP4_RXD_RAIL *) rxd->RxdRail;
++      EP_ENVELOPE  *env      = &rxd->RxdMain->Envelope;
++      EP_NODE_RAIL *nodeRail = &rail->r_generic.Nodes[env->NodeId];
++
++      if (rxd->RxdMain->Len == EP_RXD_PENDING || !RXD_BOUND2RAIL (rxdRail, rcvrRail) || nodeRail->State != EP_NODE_LOCAL_PASSIVATE)
++          continue;
++      
++      EPRINTF5 (DBG_DISCON, "%s: ep4rcvr_flush_flushing: rcvr %p rxd %p state %d elan node %d\n", 
++                rail->r_generic.Name, rcvr, rxd, (int)rxdRail->rxd_main->rxd_done, env->NodeId);
++      
++      switch (rxd->State)
++      {
++      case EP_RXD_FREE:
++          printk ("ep4rcvr_flush_flushing: rxd state is free but bound to a fail\n");
++          break;
++
++      case EP_RXD_RECEIVE_ACTIVE:
++          if (rxdRail->rxd_main->rxd_done == EP4_STATE_ACTIVE)                /* incomplete message receive */
++          {
++              EPRINTF4 (DBG_RCVR, "%s: ep4rcvr_flush_flushing: rcvr %p rxd %p nodeId %d - passive\n", 
++                        rail->r_generic.Name, rcvr, rxd, env->NodeId);
++              
++              nodeRail->MessageState |= EP_NODE_PASSIVE_MESSAGES;
++              continue;
++          }
++          break;
++          
++      default:
++          EP4_ASSERT (rail, EP_IS_RPC(env->Attr));
++
++          if (rxdRail->rxd_main->rxd_done == EP4_STATE_ACTIVE)                /* incomplete RPC */
++          {
++              EPRINTF4 (DBG_RCVR, "%s: ep4rcvr_flush_flushing: rcvr %p rxd %p nodeId %d - active\n", 
++                        rail->r_generic.Name, rcvr, rxd, env->NodeId);
++              
++              EP_INVALIDATE_XID (rxd->MsgXid);                        /* Ignore any previous NMD map responses */
++              
++              nodeRail->MessageState |= EP_NODE_ACTIVE_MESSAGES;
++              continue;
++          }
++          break;
++
++      case EP_RXD_BEEN_ABORTED:
++          printk ("ep4rcvr_flush_flushing: rxd state is aborted but bound to a fail\n");
++          break;
++      }
++
++      EPRINTF4 (DBG_RCVR, "%s: ep4rcvr_flush_flushing: rcvr %p rxd %p nodeId %d - finished\n", 
++                rail->r_generic.Name, rcvr, rxd, env->NodeId);
++    }    
++
++    EP4_SPINEXIT (dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_thread_lock), &rcvrRail->rcvr_main->rcvr_thread_lock);
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++}
++
++void
++ep4rcvr_flush_callback (EP_RCVR *rcvr, EP4_RCVR_RAIL *rcvrRail)
++{
++    EP4_RAIL *rail = RCVR_TO_RAIL(rcvrRail);
++
++    switch (rail->r_generic.CallbackStep)
++    {
++    case EP_CB_FLUSH_FILTERING:
++      ep4rcvr_flush_filtering (rcvr, rcvrRail);
++      break;
++
++    case EP_CB_FLUSH_FLUSHING:
++      ep4rcvr_flush_flushing (rcvr, rcvrRail);
++      break;
++    }
++}
++
++void
++ep4rcvr_failover_callback (EP_RCVR *rcvr, EP4_RCVR_RAIL *rcvrRail)
++{
++    EP_COMMS_SUBSYS  *subsys = rcvr->Subsys;
++    EP4_RAIL         *rail   = RCVR_TO_RAIL (rcvrRail);
++    ELAN4_DEV      *dev    = rail->r_ctxt.ctxt_dev;
++    struct list_head *el, *nel;
++    unsigned long     flags;
++#if SUPPORT_RAIL_FAILOVER
++    EP_SYS           *sys    = subsys->Subsys.Sys;
++#endif
++    
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    EP4_SPINENTER (dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_thread_lock), &rcvrRail->rcvr_main->rcvr_thread_lock);
++    
++    list_for_each_safe (el, nel, &rcvr->ActiveDescList) {
++      EP_RXD             *rxd      = list_entry (el, EP_RXD, Link);
++      EP4_RXD_RAIL       *rxdRail  = (EP4_RXD_RAIL *) rxd->RxdRail;
++      EP_ENVELOPE        *env      = &rxd->RxdMain->Envelope;
++      EP_NODE_RAIL       *nodeRail = &rail->r_generic.Nodes[env->NodeId];
++#if SUPPORT_RAIL_FAILOVER
++      EP_NODE            *node     = &sys->Nodes[env->NodeId];
++      EP_MANAGER_MSG_BODY msgBody;
++#endif
++      
++      if (rxd->RxdMain->Len == EP_RXD_PENDING || !RXD_BOUND2RAIL(rxdRail,rcvrRail) || nodeRail->State != EP_NODE_PASSIVATED)
++          continue;
++
++      EPRINTF5 (DBG_FAILOVER, "%s: ep4rcvr_failover_callback: rcvr %p rxd %p elan node %d state %d\n", 
++                rail->r_generic.Name, rcvr, rxd, env->NodeId, (int)rxdRail->rxd_main->rxd_done);
++
++      switch (rxd->State)
++      {
++      case EP_RXD_FREE:
++          printk ("ep4rcvr_failover_callback: rxd state is free but bound to a rail\n");
++          break;
++
++      case EP_RXD_RECEIVE_ACTIVE:
++          if (rxdRail->rxd_main->rxd_done == EP4_STATE_ACTIVE)                        /* incomplete message receive */
++          {
++              EPRINTF4 (DBG_FAILOVER, "%s: ep4rcvr_failover_callback: rcvr %p rxd %p nodeId %d - unbind\n", rail->r_generic.Name, rcvr, rxd, env->NodeId);
++
++              EP4_RXD_FORCE_PRIVATE(rxdRail);
++              
++              unbind_rxd_rail (rxd, rxdRail);
++
++              free_rxd_rail (rcvrRail, rxdRail);
++          
++              /* epcomms thread will requeue on different rail */
++              ep_kthread_schedule (&subsys->Thread, lbolt);
++              continue;
++          }
++          break;
++
++      default:
++          EP4_ASSERT (rail, EP_IS_RPC(env->Attr));
++
++#if SUPPORT_RAIL_FAILOVER
++          /* XXXX - no rail failover for now .... */
++          if (rxdRail->rxd_main->rxd_done == EP4_STATE_ACTIVE && !EP_IS_NO_FAILOVER(env->Attr))       /* incomplete RPC, which can be failed over */
++          {
++              EPRINTF6 (DBG_FAILOVER, "%s: ep4rcvr_failover_callback: rxd %p State %d Xid %llxx MsgXid %llxx nodeId %d - failover\n", 
++                        rail->r_generic.Name, rxd, rxd->State, env->Xid.Unique, rxd->MsgXid.Unique, env->NodeId);
++              
++              if (EP_XID_INVALID(rxd->MsgXid))
++                  rxd->MsgXid = ep_xid_cache_alloc (sys, &rcvr->XidCache);
++              
++              /* XXXX maybe only send the message if the node failover retry is now ? */
++              msgBody.Failover.Xid      = env->Xid;
++              msgBody.Failover.Railmask = node->ConnectedRails;
++              
++              ep_send_message (&rail->r_generic, env->NodeId, EP_MANAGER_MSG_TYPE_FAILOVER_REQUEST, rxd->MsgXid, &msgBody);
++              
++              nodeRail->MessageState |= EP_NODE_ACTIVE_MESSAGES;
++              continue;
++          }
++#endif
++          break;
++
++      case EP_RXD_BEEN_ABORTED:
++          printk ("ep4rcvr_failover_callback: rxd state is aborted but bound to a fail\n");
++          break;
++      }
++      EPRINTF3 (DBG_FAILOVER, "%s: ep4rcvr_failover_callback: rxd %p nodeId %d - finished\n", rail->r_generic.Name, rxd, env->NodeId);
++    }
++    
++    EP4_SPINEXIT (dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_thread_lock), &rcvrRail->rcvr_main->rcvr_thread_lock);
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++}
++
++void
++ep4rcvr_disconnect_callback (EP_RCVR *rcvr, EP4_RCVR_RAIL *rcvrRail)
++{
++    EP4_RAIL         *rail = RCVR_TO_RAIL (rcvrRail);
++    ELAN4_DEV        *dev = rail->r_ctxt.ctxt_dev;
++    struct list_head *el, *nel;
++    struct list_head  rxdList;
++    unsigned long     flags;
++
++    INIT_LIST_HEAD (&rxdList);
++    
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    EP4_SPINENTER (dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_thread_lock), &rcvrRail->rcvr_main->rcvr_thread_lock);
++    
++    list_for_each_safe (el, nel, &rcvr->ActiveDescList) {
++      EP_RXD       *rxd      = list_entry (el, EP_RXD, Link);
++      EP4_RXD_RAIL *rxdRail  = (EP4_RXD_RAIL *) rxd->RxdRail;
++      EP_ENVELOPE  *env      = &rxd->RxdMain->Envelope;
++      EP_NODE_RAIL *nodeRail = &rail->r_generic.Nodes[env->NodeId];
++      
++      if (rxd->RxdMain->Len == EP_RXD_PENDING || !RXD_BOUND2RAIL(rxdRail,rcvrRail) || nodeRail->State != EP_NODE_DISCONNECTING)
++          continue;
++
++      EPRINTF5 (DBG_DISCON, "%s: ep4rcvr_disconnect_callback: rcvr %p rxd %p elan node %d state %x\n", rail->r_generic.Name, rcvr, rxd, env->NodeId, rxd->State);
++
++      switch (rxd->State)
++      {
++      case EP_RXD_FREE:
++          printk ("ep4rcvr_disconnect_callback: rxd state is free but bound to a rail\n");
++          break;
++
++      case EP_RXD_RECEIVE_ACTIVE:
++          if (rxdRail->rxd_main->rxd_done == EP4_STATE_ACTIVE)                /* incomplete message receive */
++          {
++              EPRINTF4 (DBG_RCVR, "%s: ep4rcvr_disconnect_callback: rcvr %p rxd %p nodeId %d - unbind\n", rail->r_generic.Name, rcvr, rxd, env->NodeId);
++
++              EP4_RXD_FORCE_PRIVATE (rxdRail);
++              
++              unbind_rxd_rail (rxd, rxdRail);
++              free_rxd_rail (rcvrRail, rxdRail);
++              
++              /* remark it as pending if it was partially received */
++              rxd->RxdMain->Len = EP_RXD_PENDING;
++              
++              /* epcomms thread will requeue on different rail */
++              ep_kthread_schedule (&rcvr->Subsys->Thread, lbolt);
++              continue;
++          }
++          break;
++
++      default:
++          if (rxdRail->rxd_main->rxd_done == EP4_STATE_ACTIVE || rxdRail->rxd_main->rxd_done == EP4_STATE_PRIVATE)            /* incomplete RPC */
++          {
++              EPRINTF5 (DBG_RCVR, "%s: ep4rcvr_disconnect_callback: rcvr %p rxd %p nodeId %d state %x - not able to failover\n",
++                        rail->r_generic.Name, rcvr, rxd, env->NodeId, rxd->State);
++          
++              EP4_RXD_FORCE_PRIVATE (rxdRail);
++
++              unbind_rxd_rail (rxd, rxdRail);
++              free_rxd_rail (rcvrRail, rxdRail);
++
++              /* Ignore any previous NMD/failover responses */
++              EP_INVALIDATE_XID (rxd->MsgXid);
++              
++              /* Remove from active list */
++              list_del (&rxd->Link);
++              
++              if (rxd->State == EP_RXD_RPC_IN_PROGRESS)                               /* ownder by user .... */
++                  rxd->State = EP_RXD_BEEN_ABORTED;
++              else                                                                    /* queue for completion */
++              {
++                  rxd->RxdMain->Len = EP_CONN_RESET;                                  /* ensure ep_rxd_status() fails */
++                  list_add_tail (&rxd->Link, &rxdList);
++              }
++              continue;
++          }
++          break;
++
++      case EP_RXD_BEEN_ABORTED:
++          printk ("ep4rcvr_disconnect_callback: rxd state is aborted but bound to a rail\n");
++          break;
++      }
++
++      printk ("%s: ep4rcvr_disconnect_callback: rcvr %p rxd %p nodeId %d - finished\n", 
++                rail->r_generic.Name, rcvr, rxd, env->NodeId);
++      EPRINTF4 (DBG_RCVR, "%s: ep4rcvr_disconnect_callback: rcvr %p rxd %p nodeId %d - finished\n", 
++                rail->r_generic.Name, rcvr, rxd, env->NodeId);
++      ep4rcvr_display_rxd (&di_ep_debug, &rxdRail->rxd_generic);
++    }
++    
++    EP4_SPINEXIT (dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_thread_lock), &rcvrRail->rcvr_main->rcvr_thread_lock);
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++
++    while (! list_empty (&rxdList)) 
++    {
++      EP_RXD *rxd = list_entry (rxdList.next, EP_RXD, Link);
++
++      list_del (&rxd->Link);
++
++      rxd->Handler (rxd);
++    }
++}
++
++void
++ep4rcvr_neterr_flush (EP_RCVR *rcvr, EP4_RCVR_RAIL *rcvrRail, unsigned int nodeId, EP_NETERR_COOKIE *cookies)
++{
++    EP4_COMMS_RAIL *commsRail = RCVR_TO_COMMS(rcvrRail);
++    EP4_RAIL       *rail      = RCVR_TO_RAIL (rcvrRail);
++    ELAN4_DEV      *dev       = rail->r_ctxt.ctxt_dev;
++    unsigned long   flags;
++
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    EP4_SPINENTER (dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_thread_lock), &rcvrRail->rcvr_main->rcvr_thread_lock);
++
++    /* Insert an setevent command into the thread's command queue
++     * to ensure that all sten packets have completed */
++    elan4_guard (rcvrRail->rcvr_ecq->ecq_cq, GUARD_ALL_CHANNELS);
++    ep4comms_flush_setevent (commsRail, rcvrRail->rcvr_ecq->ecq_cq);
++    
++    EP4_SPINEXIT (dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_thread_lock), &rcvrRail->rcvr_main->rcvr_thread_lock);
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++}
++
++void
++ep4rcvr_neterr_check (EP_RCVR *rcvr, EP4_RCVR_RAIL *rcvrRail, unsigned int nodeId, EP_NETERR_COOKIE *cookies)
++{
++    EP4_RAIL         *rail = RCVR_TO_RAIL (rcvrRail);
++    ELAN4_DEV        *dev = rail->r_ctxt.ctxt_dev;
++    struct list_head *el;
++    unsigned long     flags;
++
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    EP4_SPINENTER (dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_thread_lock), &rcvrRail->rcvr_main->rcvr_thread_lock);
++    
++    list_for_each (el, &rcvr->ActiveDescList) {
++      EP_RXD       *rxd      = list_entry (el, EP_RXD, Link);
++      EP4_RXD_RAIL *rxdRail  = (EP4_RXD_RAIL *) rxd->RxdRail;
++      EP_ENVELOPE  *env      = &rxd->RxdMain->Envelope;
++
++      if (rxd->RxdMain->Len == EP_RXD_PENDING || !RXD_BOUND2RAIL(rxdRail,rcvrRail) || env->NodeId != nodeId)
++          continue;
++
++      if (rxd->State == EP_RXD_RECEIVE_ACTIVE || rxd->State == EP_RXD_GET_ACTIVE)
++      {
++          EP_NETERR_COOKIE cookie;
++          unsigned int     first, this;
++
++          if (rxd->State == EP_RXD_RECEIVE_ACTIVE)
++              first = (EP_MAXFRAG+1) - (( EP_IS_MULTICAST(env->Attr) ? 1 : 0) + (env->nFrags == 0 ? 1 : env->nFrags));
++          else
++              first = (EP_MAXFRAG+1) - rxd->nFrags;
++
++          for (this = first; this < (EP_MAXFRAG+1); this++)
++              if (rxdRail->rxd_main->rxd_sent[this] == EP4_STATE_ACTIVE)
++                  break;
++          
++          if (this > first)
++          {
++              /* Look at the last completed STEN packet and if it's neterr cookie matches, then change
++               * the rxd to look the same as if the sten packet had failed and then schedule it for retry */
++              cookie = elan4_sdram_readq (dev, rxdRail->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[--this].c_cookie));
++              
++              if (cookie == cookies[0] || cookie == cookies[1])
++              {
++                  EPRINTF5 (DBG_NETWORK_ERROR, "%s: ep4rcvr_neterr_check: cookie <%lld%s%s%s%s> matches rxd %p rxdRail %p this %d\n",
++                            rail->r_generic.Name, EP4_COOKIE_STRING(cookie), rxd, rxdRail, this);
++                  
++                  printk ("%s: ep4rcvr_neterr_check: cookie <%lld%s%s%s%s> matches rxd %p rxdRail %p this %d : time %ld\n",
++                          rail->r_generic.Name, EP4_COOKIE_STRING(cookie), rxd, rxdRail, this, rxdRail->rxd_retry_time);
++                  
++                  rxdRail->rxd_main->rxd_sent[this] = EP4_STATE_ACTIVE;
++                  rxdRail->rxd_main->rxd_failed     = EP4_STATE_FAILED;
++                  
++                  spin_lock (&rcvrRail->rcvr_retrylock);
++                  
++                  ASSERT (rxdRail->rxd_retry_time == 0);
++
++                  rxdRail->rxd_retry_time = lbolt + EP_RETRY_LOW_PRI_TIME;
++                      
++                  list_add_tail (&rxdRail->rxd_retry_link, &rcvrRail->rcvr_retrylist);
++                      
++                  ep_kthread_schedule (&rail->r_retry_thread, rxdRail->rxd_retry_time);
++                  
++                  spin_unlock (&rcvrRail->rcvr_retrylock);
++              }
++          }
++      }
++    }
++    EP4_SPINEXIT (dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_thread_lock), &rcvrRail->rcvr_main->rcvr_thread_lock);
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++}
++
++int
++ep4rcvr_queue_rxd (EP_RXD *rxd, EP_RCVR_RAIL *r)
++{
++    EP4_RCVR_RAIL *rcvrRail = (EP4_RCVR_RAIL *) r;
++    EP4_RAIL      *rail     = RCVR_TO_RAIL (rcvrRail);
++    ELAN4_DEV     *dev      = rail->r_ctxt.ctxt_dev;
++    EP4_RXD_RAIL  *rxdRail;
++    register int   i;
++
++    ASSERT (SPINLOCK_HELD(&rxd->Rcvr->Lock));
++
++    if ((rxdRail = get_rxd_rail (rcvrRail)) == NULL)
++      return 0;
++    
++    /* Flush the Elan TLB if mappings have changed */
++    ep_perrail_dvma_sync (&rail->r_generic);
++
++    EPRINTF6 (DBG_RCVR, "%s: ep4rcvr_queue_rxd: rcvr %p rxd %p rxdRail %p buffer %x len %x\n", 
++            rail->r_generic.Name, rxd->Rcvr, rxd, rxdRail, rxd->Data.nmd_addr, rxd->Data.nmd_len);
++
++    /* bind the rxdRail and rxd together */
++    bind_rxd_rail (rxd, rxdRail);
++
++    elan4_sdram_writel (dev, rxdRail->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_buffer.nmd_addr), rxd->Data.nmd_addr);      /* PCI write */
++    elan4_sdram_writel (dev, rxdRail->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_buffer.nmd_len),  rxd->Data.nmd_len);               /* PCI write */
++    elan4_sdram_writel (dev, rxdRail->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_buffer.nmd_attr), rxd->Data.nmd_attr);      /* PCI write */
++
++    /* Mark as active */
++    elan4_sdram_writeq (dev, rxdRail->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done.ev_CountAndType), 
++                      E4_EVENT_INIT_VALUE(-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++    
++    for (i = 0; i <= EP_MAXFRAG; i++)
++      rxdRail->rxd_main->rxd_sent[i] = EP4_STATE_ACTIVE;
++
++    rxdRail->rxd_main->rxd_failed = EP4_STATE_ACTIVE;
++    rxdRail->rxd_main->rxd_done = EP4_STATE_ACTIVE;
++
++    elan4_sdram_writeq (dev, rxdRail->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[0]) + 0x00, /* %r0 */
++                      ep_symbol (&rail->r_threadcode, "c_queue_rxd"));
++    elan4_sdram_writeq (dev, rxdRail->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[0]) + 0x10, /* %r2 */
++                      rcvrRail->rcvr_elan_addr);
++    elan4_sdram_writeq (dev, rxdRail->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[0]) + 0x18, /* %r3 */
++                      rxdRail->rxd_elan_addr);
++
++    elan4_sdram_writeq (dev, rxdRail->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_start.ev_CountAndType),
++                      E4_EVENT_INIT_VALUE(-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_RXD_START_CMD_NDWORDS));
++
++    ep4_set_event_cmd (rxdRail->rxd_scq, rxdRail->rxd_elan_addr + offsetof (EP4_RXD_RAIL_ELAN, rxd_start));
++
++    return 1;
++}
++
++void
++ep4rcvr_rpc_put (EP_RXD *rxd, EP_NMD *local, EP_NMD *remote, unsigned nFrags)
++{
++    EP4_RXD_RAIL    *rxdRail  = (EP4_RXD_RAIL *) rxd->RxdRail;
++    EP4_RCVR_RAIL   *rcvrRail = (EP4_RCVR_RAIL *) rxdRail->rxd_generic.RcvrRail;
++    EP4_RAIL        *rail     = RCVR_TO_RAIL (rcvrRail);
++    ELAN4_DEV     *dev      = RCVR_TO_DEV (rcvrRail);
++    sdramaddr_t     rxdElan   = rxdRail->rxd_elan;
++    EP_ENVELOPE    *env       = &rxd->RxdMain->Envelope;
++    unsigned long   first     = (EP_MAXFRAG+1) - nFrags;
++    EP4_RXD_DMA_CMD cmd;
++    register int    i, len;
++
++    EP4_ASSERT (rail, rxd->State == EP_RXD_PUT_ACTIVE);
++    EP4_ASSERT (rail, rxdRail->rxd_main->rxd_done == EP4_STATE_PRIVATE);
++    EP4_SDRAM_ASSERT (rail, rxdRail->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done.ev_CountAndType),
++                    E4_EVENT_INIT_VALUE (0, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++
++    /* Flush the Elan TLB if mappings have changed */
++    ep_perrail_dvma_sync (&rail->r_generic);
++
++    /* Generate the DMA chain to put the data */
++    for (i = 0, len = 0; i < nFrags; i++, len += local->nmd_len, local++, remote++)
++    {
++      cmd.c_dma_typeSize     = RUN_DMA_CMD | E4_DMA_TYPE_SIZE(local->nmd_len, DMA_DataTypeByte, 0, EP4_DMA_RETRYCOUNT);
++      cmd.c_dma_cookie       = ep4_neterr_cookie (rail, env->NodeId) | EP4_COOKIE_DMA;
++      cmd.c_dma_vproc        = EP_VP_DATA(env->NodeId);
++      cmd.c_dma_srcAddr      = local->nmd_addr;
++      cmd.c_dma_dstAddr      = remote->nmd_addr;
++      if (i == (nFrags-1))
++          cmd.c_dma_srcEvent = rxdRail->rxd_elan_addr + offsetof (EP4_RXD_RAIL_ELAN, rxd_done);
++      else
++          cmd.c_dma_srcEvent = rxdRail->rxd_elan_addr + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[first + i]);
++      cmd.c_dma_dstEvent     = 0;
++      cmd.c_nop_cmd          = NOP_CMD;
++
++      EPRINTF7 (DBG_RCVR, "%s: ep4rcvr_rpc_put: rxd %p [XID=%llx] idx=%d Source=%08x Dest=%08x Len=%x\n", 
++                rail->r_generic.Name, rxd, env->Xid.Unique, i, local->nmd_addr, remote->nmd_addr, local->nmd_len);
++      
++      elan4_sdram_copyq_to_sdram (dev, &cmd, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[first + i]), sizeof (EP4_RXD_DMA_CMD));
++    }
++
++    /* Initialise the event chain */
++    for (i = 0; i < nFrags-1; i++)
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[first + i]), 
++                          E4_EVENT_INIT_VALUE(-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_RXD_DMA_CMD_NDWORDS));
++
++    elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done),
++                      E4_EVENT_INIT_VALUE(-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++
++    for (i = 0; i <= EP_MAXFRAG; i++)
++      rxdRail->rxd_main->rxd_sent[i] = EP4_STATE_ACTIVE;
++
++    rxdRail->rxd_main->rxd_failed = EP4_STATE_ACTIVE;
++    rxdRail->rxd_main->rxd_done = EP4_STATE_ACTIVE;
++
++    /* Initialise the previous event to start the whole chain off */
++    elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[first - 1]),
++                      E4_EVENT_INIT_VALUE(-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_RXD_DMA_CMD_NDWORDS));
++
++    ASSERT (rail->r_generic.Nodes[env->NodeId].State >= EP_NODE_CONNECTED && rail->r_generic.Nodes[env->NodeId].State <= EP_NODE_LOCAL_PASSIVATE);
++
++    /* finally issue the setevent to start the whole chain */
++    ep4_set_event_cmd (rxdRail->rxd_scq, rxdRail->rxd_elan_addr + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[first - 1]));
++
++    BucketStat (rxd->Rcvr->Subsys, RPCPut, len);
++}    
++
++void
++ep4rcvr_rpc_get (EP_RXD *rxd, EP_NMD *local, EP_NMD *remote, unsigned nFrags)
++{
++    EP4_RXD_RAIL    *rxdRail  = (EP4_RXD_RAIL *) rxd->RxdRail;
++    EP4_RCVR_RAIL   *rcvrRail = (EP4_RCVR_RAIL *) rxdRail->rxd_generic.RcvrRail;
++    EP4_RAIL        *rail     = RCVR_TO_RAIL (rcvrRail);
++    ELAN4_DEV     *dev      = RCVR_TO_DEV (rcvrRail);
++    sdramaddr_t      rxdElan  = rxdRail->rxd_elan;
++    EP_ENVELOPE     *env      = &rxd->RxdMain->Envelope;
++    unsigned long    first    = (EP_MAXFRAG+1) - nFrags;
++    register int    i, len;
++
++    EP4_ASSERT (rail, rxd->State == EP_RXD_GET_ACTIVE);
++    EP4_ASSERT (rail, rxdRail->rxd_main->rxd_done == EP4_STATE_PRIVATE);
++    EP4_SDRAM_ASSERT (rail, rxdRail->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done.ev_CountAndType),
++                    E4_EVENT_INIT_VALUE (0, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++
++    /* Flush the Elan TLB if mappings have changed */
++    ep_perrail_dvma_sync (&rail->r_generic);
++
++    /* Generate the DMA chain to put the data */
++    for (i = 0, len = 0; i < nFrags; i++, len += local->nmd_len, local++, remote++)
++    {
++      EPRINTF7 (DBG_RCVR, "%s: ep4rcvr_rpc_get rxd %p [XID=%llx] idx=%d Source=%08x Dest=%08x Len=%x\n", 
++                rail->r_generic.Name, rxd, env->Xid.Unique, i, remote->nmd_addr, local->nmd_addr, remote->nmd_len);
++      
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[first + i].c_open),
++                          OPEN_STEN_PKT_CMD | OPEN_PACKET(0, PACK_OK | RESTART_COUNT_ZERO, EP_VP_DATA(env->NodeId)));
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[first + i].c_trans),
++                          SEND_TRANS_CMD | ((TR_REMOTEDMA | TR_WAIT_FOR_EOP) << 16));
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[first + i].c_cookie),
++                          ep4_neterr_cookie (rail, env->NodeId) | EP4_COOKIE_STEN);
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[first + i].c_dma_typeSize),
++                          E4_DMA_TYPE_SIZE (local->nmd_len, DMA_DataTypeByte, 0, EP4_DMA_RETRYCOUNT));
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[first + i].c_dma_cookie),
++                          ep4_neterr_cookie (rail, env->NodeId) | EP4_COOKIE_DMA);
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[first + i].c_dma_vproc),
++                          EP_VP_DATA (rail->r_generic.Position.pos_nodeid));
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[first + i].c_dma_srcAddr),
++                          remote->nmd_addr);
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[first + i].c_dma_dstAddr),
++                          local->nmd_addr);
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[first + i].c_dma_srcEvent),
++                          0);
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[first + i].c_dma_dstEvent),
++                          i == (nFrags-1) ? rxdRail->rxd_elan_addr + offsetof (EP4_RXD_RAIL_ELAN, rxd_done) : 
++                                            rxdRail->rxd_elan_addr + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[first + i]));
++    }
++
++    /* Initialise the event chain */
++    for (i = 0; i < nFrags-1; i++)
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[first + i]), 
++                          E4_EVENT_INIT_VALUE(-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_RXD_STEN_CMD_NDWORDS));
++
++    elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done),
++                      E4_EVENT_INIT_VALUE(-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++
++    for (i = 0; i <= EP_MAXFRAG; i++)
++      rxdRail->rxd_main->rxd_sent[i] = EP4_STATE_ACTIVE;
++
++    rxdRail->rxd_main->rxd_failed = EP4_STATE_ACTIVE;
++    rxdRail->rxd_main->rxd_done = EP4_STATE_ACTIVE;
++
++    /* Initialise the previous event to start the whole chain off */
++    elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[first - 1]),
++                      E4_EVENT_INIT_VALUE(-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_RXD_STEN_CMD_NDWORDS));
++
++    ASSERT (rail->r_generic.Nodes[env->NodeId].State >= EP_NODE_CONNECTED && rail->r_generic.Nodes[env->NodeId].State <= EP_NODE_LOCAL_PASSIVATE);
++
++    /* finally issue the setevent to start the whole chain */
++    ep4_set_event_cmd (rxdRail->rxd_scq, rxdRail->rxd_elan_addr + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[first - 1]));
++
++    BucketStat (rxd->Rcvr->Subsys, RPCPut, len);
++}
++
++void
++ep4rcvr_rpc_complete (EP_RXD *rxd, EP_NMD *local, EP_NMD *remote, unsigned nFrags)
++{
++    EP4_RXD_RAIL    *rxdRail  = (EP4_RXD_RAIL *) rxd->RxdRail;
++    EP4_RCVR_RAIL   *rcvrRail = (EP4_RCVR_RAIL *) rxdRail->rxd_generic.RcvrRail;
++    EP4_RAIL        *rail     = RCVR_TO_RAIL (rcvrRail);
++    ELAN4_DEV     *dev      = RCVR_TO_DEV (rcvrRail);
++    sdramaddr_t     rxdElan   = rxdRail->rxd_elan;
++    EP_ENVELOPE    *env       = &rxd->RxdMain->Envelope;
++    unsigned long   first     = (EP_MAXFRAG+1) - nFrags - 1;
++    EP4_RXD_DMA_CMD cmd;
++    register int    i, len;
++
++    EP4_ASSERT (rail, rxd->State == EP_RXD_COMPLETE_ACTIVE);
++    EP4_ASSERT (rail, rxdRail->rxd_main->rxd_done == EP4_STATE_PRIVATE);
++    EP4_SDRAM_ASSERT (rail, rxdRail->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done.ev_CountAndType),
++                    E4_EVENT_INIT_VALUE (0, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++
++    /* Flush the Elan TLB if mappings have changed */
++    ep_perrail_dvma_sync (&rail->r_generic);
++
++    /* Generate the DMA chain to put the data */
++    for (i = 0, len = 0; i < nFrags; i++, len += local->nmd_len, local++, remote++)
++    {
++      cmd.c_dma_typeSize = RUN_DMA_CMD | E4_DMA_TYPE_SIZE(local->nmd_len, DMA_DataTypeByte, 0, EP4_DMA_RETRYCOUNT);
++      cmd.c_dma_cookie   = ep4_neterr_cookie (rail, env->NodeId) | EP4_COOKIE_DMA;
++      cmd.c_dma_vproc    = EP_VP_DATA(env->NodeId);
++      cmd.c_dma_srcAddr  = local->nmd_addr;
++      cmd.c_dma_dstAddr  = remote->nmd_addr;
++      cmd.c_dma_srcEvent = rxdRail->rxd_elan_addr + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[first + i]);
++      cmd.c_dma_dstEvent = 0;
++      cmd.c_nop_cmd      = NOP_CMD;
++
++      EPRINTF7 (DBG_RCVR, "%s: ep4rcvr_rpc_complete: rxd %p [XID=%llx] idx=%d Source=%08x Dest=%08x Len=%x\n", 
++                rail->r_generic.Name, rxd, env->Xid.Unique, i, local->nmd_addr, remote->nmd_addr, local->nmd_len);
++
++      elan4_sdram_copyq_to_sdram (dev, &cmd, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[first + i]), sizeof (EP4_RXD_DMA_CMD));
++    }
++    
++    /* Initialise the status block dma */
++    cmd.c_dma_typeSize = RUN_DMA_CMD | E4_DMA_TYPE_SIZE(EP_STATUSBLK_SIZE, DMA_DataTypeByte, 0, EP4_DMA_RETRYCOUNT);
++    cmd.c_dma_cookie   = ep4_neterr_cookie (rail, env->NodeId) | EP4_COOKIE_DMA;
++    cmd.c_dma_vproc    = EP_VP_DATA(env->NodeId);
++    cmd.c_dma_srcAddr  = rxd->NmdMain.nmd_addr + offsetof (EP_RXD_MAIN, StatusBlk);
++    cmd.c_dma_dstAddr  = env->TxdMain.nmd_addr + offsetof (EP_TXD_MAIN, StatusBlk);
++    cmd.c_dma_srcEvent = rxdRail->rxd_elan_addr + offsetof (EP4_RXD_RAIL_ELAN, rxd_done);
++    cmd.c_dma_dstEvent = env->TxdRail + offsetof (EP4_TXD_RAIL_ELAN, txd_done);;
++    cmd.c_nop_cmd      = NOP_CMD;
++
++    EPRINTF6 (DBG_RCVR, "%s: ep4rcvr_rpc_complete: rxd %p [XID=%llx] statusblk source=%08x dest=%08x len=%x\n", 
++            rail->r_generic.Name, rxd, env->Xid.Unique, (int) cmd.c_dma_srcAddr, (int) cmd.c_dma_dstAddr, EP_STATUSBLK_SIZE);
++
++    elan4_sdram_copyq_to_sdram (dev, &cmd, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[EP_MAXFRAG]), sizeof (EP4_RXD_DMA_CMD));
++
++    /* Initialise the event chain */
++    for (i = 0; i < nFrags; i++)
++      elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[first + i]), 
++                          E4_EVENT_INIT_VALUE(-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_RXD_DMA_CMD_NDWORDS));
++
++    elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done),
++                      E4_EVENT_INIT_VALUE(-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++
++    for (i = 0; i <= EP_MAXFRAG; i++)
++      rxdRail->rxd_main->rxd_sent[i] = EP4_STATE_ACTIVE;
++
++    rxdRail->rxd_main->rxd_failed = EP4_STATE_ACTIVE;
++    rxdRail->rxd_main->rxd_done = EP4_STATE_ACTIVE;
++
++    /* Initialise the previous event to start the whole chain off */
++    elan4_sdram_writeq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[first - 1]),
++                      E4_EVENT_INIT_VALUE(-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_RXD_DMA_CMD_NDWORDS));
++
++    ASSERT (rail->r_generic.Nodes[env->NodeId].State >= EP_NODE_CONNECTED && rail->r_generic.Nodes[env->NodeId].State <= EP_NODE_LOCAL_PASSIVATE);
++
++    /* finally issue the setevent to start the whole chain */
++    ep4_set_event_cmd (rxdRail->rxd_scq, rxdRail->rxd_elan_addr + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[first - 1]));
++
++    BucketStat (rxd->Rcvr->Subsys, CompleteRPC, len);
++}
++
++EP_RXD *
++ep4rcvr_steal_rxd (EP_RCVR_RAIL *r)
++{
++    /* XXXX - TBD */
++    return NULL;
++}
++
++long
++ep4rcvr_check (EP_RCVR_RAIL *r, long nextRunTime)
++{
++    EP4_RCVR_RAIL *rcvrRail = (EP4_RCVR_RAIL *) r;
++    EP4_RAIL      *rail     = RCVR_TO_RAIL (rcvrRail);
++
++    if (rcvrRail->rcvr_freecount < ep_rxd_lowat && !alloc_rxd_block (rcvrRail))
++    {
++      EPRINTF1 (DBG_RCVR,"%s: failed to grow rxd rail pool\n", rail->r_generic.Name);
++              
++      if (nextRunTime == 0 || AFTER (nextRunTime, lbolt + RESOURCE_RETRY_TIME))
++          nextRunTime = lbolt + RESOURCE_RETRY_TIME;
++    }
++    
++    return nextRunTime;
++}
++
++unsigned long
++ep4rcvr_retry (EP4_RAIL *rail, void *arg, unsigned long nextRunTime)
++{
++    EP4_RCVR_RAIL *rcvrRail = (EP4_RCVR_RAIL *) arg;
++    ELAN4_DEV     *dev      = RCVR_TO_DEV(rcvrRail);
++    unsigned long  flags;
++
++    spin_lock_irqsave (&rcvrRail->rcvr_retrylock, flags);
++    while (! list_empty (&rcvrRail->rcvr_retrylist))
++    {
++      EP4_RXD_RAIL *rxdRail = list_entry (rcvrRail->rcvr_retrylist.next, EP4_RXD_RAIL, rxd_retry_link);
++      EP_ENVELOPE  *env     = &rxdRail->rxd_generic.Rxd->RxdMain->Envelope;
++      unsigned int  first   = (EP_MAXFRAG+1) - ((env->Attr & EP_MULTICAST ? 1 : 0) + (env->nFrags == 0 ? 1 : env->nFrags));
++      
++      if (BEFORE (lbolt, rxdRail->rxd_retry_time))
++      {
++          if (nextRunTime == 0 || AFTER (nextRunTime, rxdRail->rxd_retry_time))
++              nextRunTime = rxdRail->rxd_retry_time;
++
++          break;
++      }
++
++      list_del (&rxdRail->rxd_retry_link);
++      rxdRail->rxd_retry_time = 0;
++
++      /* determine which sten packet to resubmit */
++      for (; first < (EP_MAXFRAG+1); first++)
++          if (rxdRail->rxd_main->rxd_sent[first] == EP4_STATE_ACTIVE)
++              break;
++
++      EPRINTF3 (DBG_RETRY, "%s: ep4rcvr_retry: rxdRail %p, reissuing sten[%d]\n", rail->r_generic.Name, rxdRail, first);
++
++      /* re-initialise the fail event */
++      elan4_sdram_writeq (dev, rxdRail->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_failed.ev_CountAndType),
++                          E4_EVENT_INIT_VALUE (-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++
++      rxdRail->rxd_main->rxd_failed = EP4_STATE_ACTIVE;
++
++      /* re-initialise the chain event to resubmit this sten packet */
++      elan4_sdram_writeq (dev, rxdRail->rxd_elan + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[first-1].ev_CountAndType),
++                          E4_EVENT_INIT_VALUE(-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_RXD_STEN_CMD_NDWORDS));
++      
++      /* finally issue the setevent to start the chain again */
++      ep4_set_event_cmd (rxdRail->rxd_scq, rxdRail->rxd_elan_addr + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[first - 1]));
++    }
++    spin_unlock_irqrestore (&rcvrRail->rcvr_retrylock, flags);
++    
++    return nextRunTime;
++}
++
++void
++ep4rcvr_add_rail (EP_RCVR *rcvr, EP_COMMS_RAIL *commsRail)
++{
++    EP4_RAIL          *rail   = (EP4_RAIL *) commsRail->Rail;
++    ELAN4_DEV         *dev    = rail->r_ctxt.ctxt_dev;
++    sdramaddr_t        qdescs = ((EP4_COMMS_RAIL *) commsRail)->r_descs;
++    EP4_RCVR_RAIL     *rcvrRail;
++    E4_InputQueue      qdesc;
++    E4_ThreadRegs      tregs;
++    sdramaddr_t        stack;
++    unsigned long      flags;
++
++    KMEM_ZALLOC (rcvrRail, EP4_RCVR_RAIL *, sizeof (EP4_RCVR_RAIL), 1);
++
++    spin_lock_init (&rcvrRail->rcvr_freelock);
++    INIT_LIST_HEAD (&rcvrRail->rcvr_freelist);
++    INIT_LIST_HEAD (&rcvrRail->rcvr_blocklist);
++
++    kcondvar_init (&rcvrRail->rcvr_cleanup_sleep);
++    kcondvar_init (&rcvrRail->rcvr_freesleep);
++
++    INIT_LIST_HEAD (&rcvrRail->rcvr_retrylist);
++    spin_lock_init (&rcvrRail->rcvr_retrylock);
++
++    rcvrRail->rcvr_generic.CommsRail = commsRail;
++    rcvrRail->rcvr_generic.Rcvr      = rcvr;
++
++    rcvrRail->rcvr_main  = ep_alloc_main (&rail->r_generic, sizeof (EP4_RCVR_RAIL_MAIN), 0, &rcvrRail->rcvr_main_addr);
++    rcvrRail->rcvr_elan  = ep_alloc_elan (&rail->r_generic, sizeof (EP4_RCVR_RAIL_ELAN), 0, &rcvrRail->rcvr_elan_addr);
++    rcvrRail->rcvr_slots = ep_alloc_elan (&rail->r_generic, EP_INPUTQ_SIZE * rcvr->InputQueueEntries, 0, &rcvrRail->rcvr_slots_addr);
++    stack                = ep_alloc_elan (&rail->r_generic, EP4_STACK_SIZE, 0, &rcvrRail->rcvr_stack);
++
++    /* allocate a command queue for the thread to use, plus space for it to wait/reschedule */
++    rcvrRail->rcvr_ecq     = ep4_alloc_ecq (rail, CQ_Size64K);
++    rcvrRail->rcvr_resched = ep4_get_ecq (rail, EP4_ECQ_ATOMIC, 8);
++
++    ep4_register_intcookie (rail, &rcvrRail->rcvr_stall_intcookie, rcvrRail->rcvr_elan_addr + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_stall_intcookie),
++                          rcvr_stall_interrupt, rcvrRail);
++
++    /* Initialise the elan portion */
++    elan4_sdram_writeq (dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_qevent.ev_CountAndType), 0);
++    elan4_sdram_writeq (dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_thread_halt.ev_CountAndType), 0);
++    elan4_sdram_writeq (dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_thread_lock), 0);
++    elan4_sdram_writeq (dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_pending_tailp),
++                      rcvrRail->rcvr_elan_addr + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_pending_head));
++    elan4_sdram_writeq (dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_pending_head), 0);
++    elan4_sdram_writeq (dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_stall_intcookie), 0);
++    elan4_sdram_writeq (dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_qbase), rcvrRail->rcvr_slots_addr);
++    elan4_sdram_writeq (dev, rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_qlast), 
++                      rcvrRail->rcvr_slots_addr + EP_INPUTQ_SIZE * (rcvr->InputQueueEntries-1));
++
++    /* Initialise the main memory portion */
++    rcvrRail->rcvr_main->rcvr_thread_lock = 0;
++
++    /* Install our retry handler */
++    rcvrRail->rcvr_retryops.op_func = ep4rcvr_retry;
++    rcvrRail->rcvr_retryops.op_arg  = rcvrRail;
++
++    ep4_add_retry_ops (rail, &rcvrRail->rcvr_retryops);
++
++    /* Update the queue desriptor */
++    qdesc.q_bptr    = rcvrRail->rcvr_slots_addr;
++    qdesc.q_fptr    = rcvrRail->rcvr_slots_addr;
++    qdesc.q_control = E4_InputQueueControl (rcvrRail->rcvr_slots_addr, rcvrRail->rcvr_slots_addr + (EP_INPUTQ_SIZE * (rcvr->InputQueueEntries-1)), EP_INPUTQ_SIZE);
++    qdesc.q_event   = rcvrRail->rcvr_elan_addr + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_qevent);
++
++    ep4_write_qdesc (rail, qdescs + (rcvr->Service * EP_QUEUE_DESC_SIZE), &qdesc);
++
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    rcvr->Rails[rail->r_generic.Number] = &rcvrRail->rcvr_generic;
++    rcvr->RailMask |= EP_RAIL2RAILMASK (rail->r_generic.Number);
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++
++    {
++      sdramaddr_t stackTop     = stack + EP4_STACK_SIZE;
++      E4_Addr     stackTopAddr = rcvrRail->rcvr_stack + EP4_STACK_SIZE;
++
++      ep4_init_thread (rail, &tregs, stackTop, stackTopAddr, ep_symbol (&rail->r_threadcode, "ep4comms_rcvr"), 6, 
++                       (E4_uint64) rail->r_elan_addr, (E4_uint64) rcvrRail->rcvr_elan_addr, (E4_uint64) rcvrRail->rcvr_main_addr,
++                       (E4_uint64) EP_MSGQ_ADDR(rcvr->Service), (E4_uint64) rcvrRail->rcvr_ecq->ecq_addr, (E4_uint64) rcvrRail->rcvr_resched->ecq_addr);
++    }
++    
++    /* Issue the command to the threads private command queue */
++    elan4_run_thread_cmd (rcvrRail->rcvr_ecq->ecq_cq, &tregs);
++
++    ep_procfs_rcvr_add_rail(&(rcvrRail->rcvr_generic));
++}
++
++void
++ep4rcvr_del_rail (EP_RCVR *rcvr, EP_COMMS_RAIL *commsRail)
++{
++    EP4_RAIL         *rail     = (EP4_RAIL *) commsRail->Rail;
++    EP4_RCVR_RAIL    *rcvrRail = (EP4_RCVR_RAIL *) rcvr->Rails[rail->r_generic.Number];  
++    ELAN4_HALTOP      haltop;
++    struct list_head *el, *nel;
++    unsigned long     flags;
++
++    ep_procfs_rcvr_del_rail(&(rcvrRail->rcvr_generic));
++
++    /* Run a halt operation to mark the input queue as full and
++     * request the thread to halt */
++    haltop.op_mask     = INT_DiscardingHighPri | INT_TProcHalted;
++    haltop.op_function = rcvr_stall_haltop;
++    haltop.op_arg      = rcvrRail;
++
++    elan4_queue_haltop (rail->r_ctxt.ctxt_dev, &haltop);
++
++    /* Wait for the thread to tell us it's processed the input queue */
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    while (! rcvrRail->rcvr_thread_halted)
++      kcondvar_wait (&rcvrRail->rcvr_cleanup_sleep, &rcvr->Lock, &flags);
++    rcvrRail->rcvr_thread_halted = 0;
++
++    /* flag the rail as no longer available */
++    rcvr->RailMask &= ~EP_RAIL2RAILMASK (rail->r_generic.Number);
++
++    /* wait for all active communications to terminate */
++    for (;;)
++    {
++      int mustWait = 0;
++
++      list_for_each (el, &rcvr->ActiveDescList) {
++          EP_RXD       *rxd     = list_entry (el, EP_RXD, Link);
++          EP4_RXD_RAIL *rxdRail = (EP4_RXD_RAIL *) rxd->RxdRail;
++          
++          if (rxdRail && RXD_BOUND2RAIL (rxdRail, rcvrRail) && rxd->RxdMain->Len != EP_RXD_PENDING)
++          {
++              mustWait++;
++              break;
++          }
++      }
++
++      if (! mustWait)
++          break;
++
++      rcvrRail->rcvr_cleanup_waiting++;
++      kcondvar_wait (&rcvrRail->rcvr_cleanup_sleep, &rcvr->Lock, &flags);
++    }
++
++    /* at this point all rxd's in the list that are bound to the deleting rail are pending */
++    list_for_each_safe (el, nel, &rcvr->ActiveDescList) {
++      EP_RXD       *rxd     = list_entry (el, EP_RXD, Link);
++      EP4_RXD_RAIL *rxdRail = (EP4_RXD_RAIL *) rxd->RxdRail;
++
++      if (rxdRail && RXD_BOUND2RAIL (rxdRail, rcvrRail))
++      {
++          EP4_RXD_ASSERT_PENDING (rxdRail);
++          EP4_RXD_FORCE_PRIVATE (rxdRail);
++
++          unbind_rxd_rail (rxd, rxdRail);
++          free_rxd_rail (rcvrRail, rxdRail);
++      }
++    }
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++
++    /* wait for all rxd's for this rail to become free */
++    spin_lock_irqsave (&rcvrRail->rcvr_freelock, flags);
++    while (rcvrRail->rcvr_freecount != rcvrRail->rcvr_totalcount)
++    {
++      rcvrRail->rcvr_freewaiting++;
++      kcondvar_wait (&rcvrRail->rcvr_freesleep, &rcvrRail->rcvr_freelock, &flags);
++    }
++    spin_unlock_irqrestore (&rcvrRail->rcvr_freelock, flags);
++
++    /* can now remove the rail as it can no longer be used */
++    spin_lock_irqsave (&rcvr->Lock, flags);
++    rcvr->Rails[rail->r_generic.Number] = NULL;
++    spin_unlock_irqrestore (&rcvr->Lock, flags);
++
++    /* all the rxd's accociated with DescBlocks must be in the FreeDescList */
++    ASSERT (rcvrRail->rcvr_totalcount == rcvrRail->rcvr_freecount);
++
++    /* run through the DescBlockList deleting them */
++    while (!list_empty (&rcvrRail->rcvr_blocklist))
++      free_rxd_block (rcvrRail, list_entry(rcvrRail->rcvr_blocklist.next, EP4_RXD_RAIL_BLOCK , blk_link));
++
++    /* it had better be empty after that */
++    ASSERT ((rcvrRail->rcvr_totalcount == 0) && (rcvrRail->rcvr_totalcount == rcvrRail->rcvr_freecount));
++
++    ep4_remove_retry_ops (rail, &rcvrRail->rcvr_retryops);
++
++    ep4_deregister_intcookie (rail, &rcvrRail->rcvr_stall_intcookie);
++
++    ep4_put_ecq (rail, rcvrRail->rcvr_resched, 8);
++    ep4_free_ecq (rail, rcvrRail->rcvr_ecq);
++
++    ep_free_elan (&rail->r_generic, rcvrRail->rcvr_stack, EP4_STACK_SIZE);
++    ep_free_elan (&rail->r_generic, rcvrRail->rcvr_slots_addr, EP_INPUTQ_SIZE * rcvr->InputQueueEntries);
++    ep_free_elan (&rail->r_generic, rcvrRail->rcvr_elan_addr, sizeof (EP4_RCVR_RAIL_ELAN));
++    ep_free_main (&rail->r_generic, rcvrRail->rcvr_main_addr, sizeof (EP4_RCVR_RAIL_MAIN));
++
++    KMEM_FREE (rcvrRail, sizeof (EP4_RCVR_RAIL));
++}
++
++void
++ep4rcvr_display_rxd (DisplayInfo *di, EP_RXD_RAIL *r)
++{
++    EP4_RXD_RAIL *rxdRail = (EP4_RXD_RAIL *) r;
++    sdramaddr_t   rxdElan = rxdRail->rxd_elan;
++    EP4_RAIL     *rail    = RCVR_TO_RAIL (rxdRail->rxd_generic.RcvrRail);
++    ELAN4_DEV    *dev     = rail->r_ctxt.ctxt_dev;
++    int i;
++
++    (di->func)(di->arg, "    Rail %d rxd %p elan %lx(%x) main %p(%x) ecq %d scq %d debug %llx\n", rail->r_generic.Number,
++             rxdRail, rxdRail->rxd_elan, rxdRail->rxd_elan_addr, rxdRail->rxd_main, rxdRail->rxd_main_addr,
++             elan4_cq2num(rxdRail->rxd_ecq->ecq_cq), elan4_cq2num(rxdRail->rxd_scq->ecq_cq),
++             elan4_sdram_readq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_debug)));
++    (di->func)(di->arg, "          start    %016llx %016llx %016llx [%016llx %016llx]\n",
++             elan4_sdram_readq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_start.ev_CountAndType)),
++             elan4_sdram_readq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_start.ev_Params[0])),
++             elan4_sdram_readq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_start.ev_Params[1])),
++             elan4_sdram_readq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[0].c_cookie)),
++             elan4_sdram_readq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[0].c_dma_cookie)));
++             
++    for (i = 0; i < EP_MAXFRAG; i++)
++      (di->func)(di->arg, "          chain[%d] %016llx %016llx %016llx [%016llx %016llx]\n", i,
++                 elan4_sdram_readq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[i].ev_CountAndType)),
++                 elan4_sdram_readq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[i].ev_Params[0])),
++                 elan4_sdram_readq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_chain[i].ev_Params[1])),
++                 elan4_sdram_readq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[i+1].c_cookie)),
++                 elan4_sdram_readq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_sten[i+1].c_dma_cookie)));
++    (di->func)(di->arg, "          done    %016llx %016llx %016llx -> %016llx\n",
++             elan4_sdram_readq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done.ev_CountAndType)),
++             elan4_sdram_readq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done.ev_Params[0])),
++             elan4_sdram_readq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_done.ev_Params[1])),
++             rxdRail->rxd_main->rxd_done);
++    (di->func)(di->arg, "          fail    %016llx %016llx %016llx -> %016llx\n",
++             elan4_sdram_readq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_failed.ev_CountAndType)),
++             elan4_sdram_readq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_failed.ev_Params[0])),
++             elan4_sdram_readq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_failed.ev_Params[1])),
++             rxdRail->rxd_main->rxd_failed);
++    (di->func)(di->arg, "          next %016llx queued %016llx main %016llx\n",
++             elan4_sdram_readq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_next)),
++             elan4_sdram_readq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_queued)),
++             elan4_sdram_readq (dev, rxdElan + offsetof (EP4_RXD_RAIL_ELAN, rxd_main)));
++    (di->func)(di->arg, "          sent %016llx %016llx %016llx %016llx %016llx\n",
++             rxdRail->rxd_main->rxd_sent[0], rxdRail->rxd_main->rxd_sent[1], rxdRail->rxd_main->rxd_sent[2],
++             rxdRail->rxd_main->rxd_sent[3], rxdRail->rxd_main->rxd_sent[4]);
++}
++
++void
++ep4rcvr_display_rcvr (DisplayInfo *di, EP_RCVR_RAIL *r)
++{
++    EP_RCVR          *rcvr       = r->Rcvr;
++    EP4_RCVR_RAIL    *rcvrRail   = (EP4_RCVR_RAIL *) r;
++    EP4_COMMS_RAIL   *commsRail  = RCVR_TO_COMMS(rcvrRail);
++    EP4_RAIL         *rail       = RCVR_TO_RAIL (rcvrRail);
++    ELAN4_DEV        *dev        = rail->r_ctxt.ctxt_dev;
++    sdramaddr_t       rcvrElan   = rcvrRail->rcvr_elan;
++    sdramaddr_t       qdesc      = commsRail->r_descs + (rcvr->Service * EP_QUEUE_DESC_SIZE);
++    sdramaddr_t       event      = rcvrRail->rcvr_elan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_qevent);
++    unsigned int      freeCount  = 0;
++    unsigned int      blockCount = 0;
++    struct list_head *el;
++    unsigned long     flags;
++    
++    spin_lock_irqsave (&rcvrRail->rcvr_freelock, flags);
++    list_for_each (el, &rcvrRail->rcvr_freelist)
++      freeCount++;
++    list_for_each (el, &rcvrRail->rcvr_blocklist)
++      blockCount++;
++    spin_unlock_irqrestore(&rcvrRail->rcvr_freelock, flags);
++
++    (di->func)(di->arg, "      Rail %d elan %lx(%x) main %p(%x) ecq %d resched %d debug %llx\n",
++             rail->r_generic.Number, rcvrRail->rcvr_elan, rcvrRail->rcvr_elan_addr,
++             rcvrRail->rcvr_main, rcvrRail->rcvr_main_addr, elan4_cq2num(rcvrRail->rcvr_ecq->ecq_cq),
++             elan4_cq2num (rcvrRail->rcvr_resched->ecq_cq),
++             elan4_sdram_readq (dev, rcvrElan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_debug)));
++    (di->func)(di->arg, "        free %d (%d) total %d blocks %d\n",
++             rcvrRail->rcvr_freecount, freeCount, rcvrRail->rcvr_totalcount, blockCount);
++    (di->func)(di->arg, "        spinlock %016llx %016llx\n", rcvrRail->rcvr_main->rcvr_thread_lock,
++             elan4_sdram_readq (dev, rcvrElan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_thread_lock)));
++    (di->func)(di->arg, "        queue: bptr %016llx fptr %016llx control %016llx (base %lx %x)\n",
++             elan4_sdram_readq (dev, qdesc + offsetof (E4_InputQueue, q_bptr)),
++             elan4_sdram_readq (dev, qdesc + offsetof (E4_InputQueue, q_fptr)),
++             elan4_sdram_readq (dev, qdesc + offsetof (E4_InputQueue, q_control)),
++             rcvrRail->rcvr_slots, rcvrRail->rcvr_slots_addr);
++    (di->func)(di->arg, "        event %016llx %016llx %016llx\n",
++             elan4_sdram_readq (dev, event + offsetof (E4_Event32, ev_CountAndType)),
++             elan4_sdram_readq (dev, event + offsetof (E4_Event32, ev_Params[0])),
++             elan4_sdram_readq (dev, event + offsetof (E4_Event32, ev_Params[1])));
++    (di->func)(di->arg, "        pending_tailp %016llx pending_head %016llx\n", 
++             elan4_sdram_readq (dev, rcvrElan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_pending_tailp)),
++             elan4_sdram_readq (dev, rcvrElan + offsetof (EP4_RCVR_RAIL_ELAN, rcvr_pending_head)));
++}
++
++void
++ep4rcvr_fillout_rail_stats(EP_RCVR_RAIL *rcvr_rail, char *str) {
++    /* no stats here yet */
++    /* EP4_RCVR_RAIL * ep4rcvr_rail = (EP4_RCVR_RAIL *) rcvr_rail; */
++}
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/ep/epcommsTx.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/epcommsTx.c  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/epcommsTx.c       2005-05-11 12:10:12.501923000 -0400
+@@ -0,0 +1,919 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: epcommsTx.c,v 1.25.2.5 2004/12/09 10:02:42 david Exp $ $Name: QSNETMODULES-4-31_20050321 $"
++/*      $Source: /cvs/master/quadrics/epmod/epcommsTx.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++#include <elan/epsvc.h>
++#include <elan/epcomms.h>
++
++#include "cm.h"
++#include "debug.h"
++
++unsigned int ep_txd_lowat = 5;
++
++static int
++AllocateTxdBlock (EP_XMTR *xmtr, EP_ATTRIBUTE attr, EP_TXD **txdp)
++{
++    EP_TXD_BLOCK *blk;
++    EP_TXD       *txd;
++    EP_TXD_MAIN  *pTxdMain;
++    int                 i;
++    unsigned long flags;
++
++    EPRINTF1 (DBG_XMTR, "AllocateTxdBlock: xmtr=%p\n", xmtr);
++
++    KMEM_ZALLOC (blk, EP_TXD_BLOCK *, sizeof (EP_TXD_BLOCK), ! (attr & EP_NO_SLEEP));
++
++    if (blk == NULL)
++      return -ENOMEM;
++
++    if ((pTxdMain = ep_shared_alloc_main (xmtr->Subsys->Subsys.Sys, EP_TXD_MAIN_SIZE * EP_NUM_TXD_PER_BLOCK, attr, &blk->NmdMain)) == (sdramaddr_t) 0)
++    {
++      KMEM_FREE (blk, sizeof (EP_TXD_BLOCK));
++      return -ENOMEM;
++    }
++
++    for (txd = &blk->Txd[0], i = 0; i < EP_NUM_TXD_PER_BLOCK; i++, txd++)
++    {
++      txd->Xmtr     = xmtr;
++      txd->TxdMain = pTxdMain;
++
++      ep_nmd_subset (&txd->NmdMain, &blk->NmdMain, (i * EP_TXD_MAIN_SIZE), EP_TXD_MAIN_SIZE);
++
++      /* move onto next descriptor */
++      pTxdMain = (EP_TXD_MAIN *) ((unsigned long) pTxdMain + EP_TXD_MAIN_SIZE);
++    }
++
++    spin_lock_irqsave (&xmtr->FreeDescLock, flags);
++
++    list_add  (&blk->Link, &xmtr->DescBlockList);
++    xmtr->TotalDescCount += EP_NUM_TXD_PER_BLOCK;
++
++    for (i = txdp ? 1 : 0; i < EP_NUM_TXD_PER_BLOCK; i++)
++    {
++      list_add (&blk->Txd[i].Link, &xmtr->FreeDescList);
++
++      xmtr->FreeDescCount++;
++
++      if (xmtr->FreeDescWanted)
++      {
++          xmtr->FreeDescWanted--;
++          kcondvar_wakeupone (&xmtr->FreeDescSleep, &xmtr->FreeDescLock);
++      }
++    }
++    spin_unlock_irqrestore (&xmtr->FreeDescLock, flags);
++    
++    if (txdp)
++      *txdp = &blk->Txd[0];
++
++    return 0;
++}
++
++static void
++FreeTxdBlock (EP_XMTR *xmtr, EP_TXD_BLOCK *blk)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&xmtr->FreeDescLock, flags);
++    list_del (&blk->Link);
++
++    xmtr->TotalDescCount -= EP_NUM_RXD_PER_BLOCK;
++    xmtr->FreeDescCount -= EP_NUM_RXD_PER_BLOCK;
++    spin_unlock_irqrestore (&xmtr->FreeDescLock, flags);
++
++    ep_shared_free_main (xmtr->Subsys->Subsys.Sys, &blk->NmdMain);
++    KMEM_FREE (blk, sizeof (EP_TXD_BLOCK));
++}
++
++static EP_TXD *
++GetTxd (EP_XMTR *xmtr, EP_ATTRIBUTE attr)
++{
++    EP_COMMS_SUBSYS *subsys = xmtr->Subsys;
++    EP_TXD          *txd;
++    int low_on_txds;
++    unsigned long flags;
++
++    spin_lock_irqsave (&xmtr->FreeDescLock, flags);
++
++    while (list_empty (&xmtr->FreeDescList))
++    {
++      if (! (attr & EP_NO_ALLOC))
++      {
++          spin_unlock_irqrestore (&xmtr->FreeDescLock, flags);
++
++          if (AllocateTxdBlock (xmtr, attr, &txd) == ESUCCESS)
++              return (txd);
++
++          spin_lock_irqsave (&xmtr->FreeDescLock, flags);
++      }
++
++      if (attr & EP_NO_SLEEP)
++      {
++          spin_unlock_irqrestore (&xmtr->FreeDescLock, flags);
++
++          return (NULL);
++      }
++
++      xmtr->FreeDescWanted++;
++      kcondvar_wait (&xmtr->FreeDescSleep, &xmtr->FreeDescLock, &flags);
++    }
++
++    txd = list_entry (xmtr->FreeDescList.next, EP_TXD, Link);
++
++    list_del (&txd->Link);
++
++    /* Wakeup the descriptor primer thread if there's not many left */
++    low_on_txds = (--xmtr->FreeDescCount < ep_txd_lowat);
++
++    spin_unlock_irqrestore (&xmtr->FreeDescLock, flags);
++
++    if (low_on_txds)
++      ep_kthread_schedule (&subsys->Thread, lbolt);
++
++    return (txd);
++}
++
++void
++FreeTxd (EP_XMTR *xmtr, EP_TXD *txd)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&xmtr->FreeDescLock, flags);
++    
++    list_add (&txd->Link, &xmtr->FreeDescList);
++
++    xmtr->FreeDescCount++;
++
++    if (xmtr->FreeDescWanted)                                 /* someone waiting for a receive */
++    {                                                         /* descriptor, so wake them up */
++      xmtr->FreeDescWanted--;
++      kcondvar_wakeupone (&xmtr->FreeDescSleep, &xmtr->FreeDescLock);
++    }
++    
++    spin_unlock_irqrestore (&xmtr->FreeDescLock, flags);
++}
++
++int
++TxdShouldStabalise (EP_TXD_RAIL *txdRail, EP_RAIL *rail)
++{
++    EP_TXD      *txd  = txdRail->Txd;
++    EP_XMTR     *xmtr = txd->Xmtr;
++    EP_ATTRIBUTE attr = txd->Envelope.Attr;
++    int                stabilise;
++    extern int   txd_stabilise;
++
++    switch (EP_ATTR2TYPE (attr)) 
++    {
++    case EP_TYPE_SVC_INDICATOR:                               /* is the rail in the current service indicator rail mask */
++      if ((txd_stabilise & 4) == 0)
++          return 0;
++
++      stabilise = (ep_xmtr_svc_indicator_railmask (xmtr, EP_ATTR2DATA (attr), txd->NodeId) & EP_RAIL2RAILMASK (rail->Number)) == 0;
++      break;
++
++    case EP_TYPE_TIMEOUT:
++      if ((txd_stabilise & 2) == 0)
++          return 0;
++
++      stabilise = AFTER(lbolt, txdRail->Txd->TimeStamp + EP_ATTR2DATA(attr));
++      break;
++
++    default:
++      if ((txd_stabilise & 1) == 0)
++          return 0;
++
++      stabilise = AFTER(lbolt, txdRail->Txd->TimeStamp + EP_DEFAULT_TIMEOUT);
++      break;
++    }
++
++    if (stabilise)
++    {
++      txd->Envelope.Attr = EP_SET_TXD_STABALISING(txd->Envelope.Attr);
++      txd->RetryTime     = lbolt;
++
++      ep_kthread_schedule (&xmtr->Subsys->Thread, lbolt);    
++    }
++
++    return stabilise;
++}
++
++void ep_xmtr_txd_stat(EP_XMTR *xmtr, EP_TXD *txd) 
++{
++    int f;
++    unsigned long size;
++    EP_TXD_RAIL *txdRail = txd->TxdRail;
++
++    size = 0;
++    for (f=0; f < txd->Envelope.nFrags; f++)
++      size += txd->Envelope.Frags[f].nmd_len;
++
++    INC_STAT(xmtr->stats,tx);
++    ADD_STAT(xmtr->stats,tx_len, size);  
++    
++    if ((txdRail != NULL) && (txdRail->XmtrRail != NULL)){
++      INC_STAT(txdRail->XmtrRail->stats,tx);
++      ADD_STAT(txdRail->XmtrRail->stats,tx_len, size); 
++      
++      if ((txdRail->XmtrRail->CommsRail != NULL) && ( txdRail->XmtrRail->CommsRail->Rail != NULL)) {
++          INC_STAT(txdRail->XmtrRail->CommsRail->Rail->Stats,tx);
++          ADD_STAT(txdRail->XmtrRail->CommsRail->Rail->Stats,tx_len, size);
++      }
++    }
++}
++
++static int
++PollActiveTransmitList (EP_XMTR *xmtr, int flag)
++{
++    struct list_head *el, *nel;
++    struct list_head list;
++    unsigned long flags;
++    int count;
++
++    INIT_LIST_HEAD (&list);
++
++    spin_lock_irqsave (&xmtr->Lock, flags);
++    list_for_each_safe (el, nel, &xmtr->ActiveDescList) {
++      EP_TXD      *txd     = list_entry (el, EP_TXD, Link);
++      EP_TXD_RAIL *txdRail = txd->TxdRail;
++      
++      if (txdRail == NULL)
++          continue;
++
++      ASSERT (txdRail->Txd == txd);
++      
++      if (EP_XMTR_OP (txdRail->XmtrRail,PollTxd) (txdRail->XmtrRail, txdRail, flags))
++      {
++          list_del (&txd->Link);                              /* remove from active transmit list */
++          list_add_tail (&txd->Link, &list);                  /* and add to list to call handlers */
++      }
++    }
++    
++    spin_unlock_irqrestore (&xmtr->Lock, flags);
++
++    for (count = 0; !list_empty (&list); count++)
++    {
++      EP_TXD *txd = list_entry (list.next, EP_TXD, Link);
++
++      list_del (&txd->Link);
++
++      txd->Handler (txd, txd->Arg, EP_SUCCESS);
++
++      FreeTxd (xmtr, txd);
++    }
++    return (count);
++}
++
++static inline void
++DoTransmit (EP_XMTR *xmtr, EP_TXD *txd)
++{
++    EP_RAILMASK   nmdRailMask = ep_nmd2railmask (txd->Envelope.Frags, txd->Envelope.nFrags);
++    EP_XMTR_RAIL *xmtrRail;
++    unsigned long flags;
++    int rnum;
++
++    spin_lock_irqsave (&xmtr->Lock, flags);
++
++    if (EP_IS_SVC_INDICATOR(txd->Envelope.Attr))
++      nmdRailMask = nmdRailMask & ep_xmtr_svc_indicator_railmask(xmtr, EP_ATTR2DATA(txd->Envelope.Attr), txd->NodeId);
++
++    if (EP_IS_PREFRAIL_SET(txd->Envelope.Attr))
++      rnum = EP_ATTR2PREFRAIL(txd->Envelope.Attr);
++    else 
++      rnum = ep_xmtr_prefrail (xmtr, nmdRailMask, txd->NodeId);
++    
++    if (rnum < 0 || !(nmdRailMask & EP_RAIL2RAILMASK(rnum)))
++      xmtrRail = NULL;
++    else
++      xmtrRail = xmtr->Rails[rnum];
++    
++    /* Allocate the XID while holding the xmtr->Lock from our XID cache */
++    txd->Envelope.Xid = ep_xid_cache_alloc (xmtr->Subsys->Subsys.Sys, &xmtr->XidCache);
++    
++    EPRINTF7 (DBG_XMTR, "ep: transmit txd %p to %d/%d: Xid %llx nFrags %d [%08x.%d]\n",
++            txd, txd->NodeId, txd->Service, (long long) txd->Envelope.Xid.Unique, 
++            txd->Envelope.nFrags, txd->Envelope.Frags[0].nmd_addr, txd->Envelope.Frags[0].nmd_len);
++
++    /* Store time transmit started to timeout if not received */
++    txd->TimeStamp = lbolt;
++    
++    /* Initialise the retry backoff */
++    txd->Backoff.type = EP_BACKOFF_FREE;
++
++    list_add_tail (&txd->Link, &xmtr->ActiveDescList);
++
++    if (xmtrRail == NULL || !EP_XMTR_OP(xmtrRail,BindTxd) (txd, xmtrRail, EP_TXD_PHASE_ACTIVE))
++      ep_kthread_schedule (&xmtr->Subsys->Thread, lbolt);
++    
++    spin_unlock_irqrestore (&xmtr->Lock, flags);
++
++    if (EP_IS_NO_INTERRUPT(txd->Envelope.Attr))
++      PollActiveTransmitList (xmtr, POLL_TX_LIST);
++}
++
++EP_STATUS
++ep_transmit_message (EP_XMTR *xmtr, unsigned int dest, EP_SERVICE service, EP_ATTRIBUTE attr, 
++                   EP_TXH *handler, void *arg, EP_PAYLOAD *payload, EP_NMD *nmd, int nFrags)
++{
++    EP_TXD       *txd;
++    int           i, len;
++
++    if (nFrags > EP_MAXFRAG || service > EP_MSG_NSVC)
++      return (EP_EINVAL);
++
++    if ((txd = GetTxd (xmtr, attr)) == NULL)
++      return (EP_ENOMEM);
++
++    txd->Handler = handler;
++    txd->Arg     = arg;
++    txd->Service = service;
++    txd->NodeId  = (unsigned short) dest;
++
++    /* Initialise the envelope */
++    txd->Envelope.Version   = EP_ENVELOPE_VERSION;
++    txd->Envelope.Attr      = EP_CLEAR_LOCAL_ATTR(attr);
++    txd->Envelope.Range     = EP_RANGE (dest, dest);
++    txd->Envelope.TxdMain   = txd->NmdMain;
++    txd->Envelope.nFrags    = nFrags;
++
++    for (i = len = 0; i < nFrags; len += nmd[i].nmd_len, i++)
++      txd->Envelope.Frags[i] = nmd[i];
++
++    if (payload)
++    {
++      txd->Envelope.Attr = EP_SET_HAS_PAYLOAD(txd->Envelope.Attr);
++
++      bcopy (payload, &txd->Payload, sizeof (EP_PAYLOAD));
++    }
++
++    DoTransmit (xmtr, txd);
++
++    BucketStat (xmtr->Subsys, DataXmit, len);
++
++    return (EP_SUCCESS);
++}
++
++EP_STATUS
++ep_multicast_message (EP_XMTR *xmtr, unsigned int destLo, unsigned int destHi, bitmap_t *bitmap, EP_SERVICE service, 
++                   EP_ATTRIBUTE attr, EP_TXH *handler, void *arg, EP_PAYLOAD *payload, EP_NMD *nmd, int nFrags)
++{
++    EP_SYS       *sys = xmtr->Subsys->Subsys.Sys;
++    EP_TXD       *txd;
++    int           nnodes;
++    int           i, len;
++    unsigned long flags;    
++
++    if (nFrags > EP_MAXFRAG || service > EP_MSG_NSVC)
++      return (EP_EINVAL);
++
++    if (destLo == -1) 
++      destLo = sys->Position.pos_nodeid & ~(EP_MAX_NODES-1);
++
++    if (destHi == -1 && (destHi = ((sys->Position.pos_nodeid + EP_MAX_NODES) & ~(EP_MAX_NODES-1)) - 1) >= sys->Position.pos_nodes)
++      destHi = sys->Position.pos_nodes-1;
++
++    nnodes = (destHi-destLo+1);
++
++    if ((txd = GetTxd (xmtr, attr)) == NULL)
++      return (EP_ENOMEM);
++
++    txd->Handler = handler;
++    txd->Arg     = arg;
++    txd->Service = service;
++
++    /* Initialise the envelope */
++    txd->Envelope.Version   = EP_ENVELOPE_VERSION;
++    txd->Envelope.Attr      = EP_SET_MULTICAST(EP_CLEAR_LOCAL_ATTR(attr));
++    txd->Envelope.Range     = EP_RANGE (destLo, destHi);
++    txd->Envelope.TxdMain   = txd->NmdMain;
++    txd->Envelope.nFrags    = nFrags;
++
++    for (i = len = 0; i < nFrags; len += nmd[i].nmd_len, i++)
++      txd->Envelope.Frags[i] = nmd[i];
++
++    if (payload)
++    {
++      txd->Envelope.Attr = EP_SET_HAS_PAYLOAD(txd->Envelope.Attr);
++
++      bcopy (payload, &txd->Payload, sizeof (EP_PAYLOAD));
++    }
++
++    spin_lock_irqsave (&sys->NodeLock, flags);
++    if (EP_IS_SVC_INDICATOR(attr)) 
++      ep_xmtr_svc_indicator_bitmap(xmtr, EP_ATTR2DATA(attr), txd->TxdMain->Bitmap, destLo, nnodes);
++    else
++      bt_subset (statemap_tobitmap(sys->NodeSet), txd->TxdMain->Bitmap, destLo, nnodes);
++    spin_unlock_irqrestore (&sys->NodeLock, flags);
++
++    if (bitmap != NULL)                                                                       /* bitmap supplied, so intersect it with */
++      bt_intersect (txd->TxdMain->Bitmap, bitmap, nnodes);                            /* the current node set map */
++    
++    if ((attr & EP_NOT_MYSELF) && destLo <= sys->Position.pos_nodeid && sys->Position.pos_nodeid <= destHi)
++      BT_CLEAR (txd->TxdMain->Bitmap, (sys->Position.pos_nodeid-destLo));                     /* clear myself if not wanted */
++
++    if ((i = bt_lowbit (txd->TxdMain->Bitmap, nnodes)) < 0)
++    {
++      FreeTxd (xmtr, txd);
++      return (EP_NODE_DOWN);
++    }
++
++    txd->NodeId = (unsigned short) i;
++
++    DoTransmit (xmtr, txd);
++
++    BucketStat (xmtr->Subsys, McastXmit, len);
++
++    return (EP_SUCCESS);
++}
++
++EP_STATUS
++ep_transmit_rpc (EP_XMTR *xmtr, unsigned int dest, EP_SERVICE service, EP_ATTRIBUTE attr,
++               EP_TXH *handler, void *arg, EP_PAYLOAD *payload, EP_NMD *nmd, int nFrags)
++{
++    EP_TXD       *txd;
++    int           i, len;
++
++    if (nFrags > EP_MAXFRAG || service > EP_MSG_NSVC)
++      return (EP_EINVAL);
++
++    if ((txd = GetTxd (xmtr, attr)) == NULL)
++      return (EP_ENOMEM);
++
++    txd->Handler = handler;
++    txd->Arg     = arg;
++    txd->Service = service;
++    txd->NodeId  = dest;
++
++    /* Initialise the envelope */
++    txd->Envelope.Version   = EP_ENVELOPE_VERSION;
++    txd->Envelope.Attr      = EP_SET_RPC(EP_CLEAR_LOCAL_ATTR(attr));    
++    txd->Envelope.Range     = EP_RANGE (dest, dest);
++    txd->Envelope.TxdMain   = txd->NmdMain;
++    txd->Envelope.nFrags    = nFrags;
++     
++    for (i = len = 0; i < nFrags; len += nmd[i].nmd_len, i++)
++      txd->Envelope.Frags[i] = nmd[i];
++
++    if (payload)
++    {
++      txd->Envelope.Attr = EP_SET_HAS_PAYLOAD(txd->Envelope.Attr);
++
++      bcopy (payload, &txd->Payload, sizeof (EP_PAYLOAD));
++    }
++
++    DoTransmit (xmtr, txd);
++
++    BucketStat (xmtr->Subsys, RPCXmit, len);
++
++    return (EP_SUCCESS);
++}
++
++EP_STATUS
++ep_multicast_forward (EP_XMTR *xmtr, unsigned int dest, EP_SERVICE service, EP_ATTRIBUTE attr, EP_TXH *handler, void *arg,
++                    EP_ENVELOPE *env,  EP_PAYLOAD *payload, bitmap_t *bitmap, EP_NMD *nmd, int nFrags)
++{
++    EP_TXD       *txd;
++    int           i, len;
++
++    if (nFrags > EP_MAXFRAG || service > EP_MSG_NSVC)
++      return (EP_EINVAL);
++
++    if ((txd = GetTxd (xmtr, attr)) == NULL)
++      return (EP_ENOMEM);
++
++    txd->Handler = handler;
++    txd->Arg     = arg;
++    txd->Service = service;
++    txd->NodeId  = (unsigned short) dest;
++
++    /* Initialise the envelope */
++    txd->Envelope.Version   = EP_ENVELOPE_VERSION;
++    txd->Envelope.Attr      = EP_SET_MULTICAST(EP_CLEAR_LOCAL_ATTR(attr));
++    txd->Envelope.Range     = env->Range;
++    txd->Envelope.TxdMain   = txd->NmdMain;
++    txd->Envelope.nFrags    = nFrags;
++
++    for (i = len = 0; i < nFrags; len += nmd[i].nmd_len, i++)
++      txd->Envelope.Frags[i] = nmd[i];
++
++    bt_copy (bitmap, txd->TxdMain->Bitmap, EP_RANGE_HIGH(env->Range) - EP_RANGE_LOW(env->Range) + 1);
++
++    if (payload)
++    {
++      txd->Envelope.Attr = EP_SET_HAS_PAYLOAD(txd->Envelope.Attr);
++
++      bcopy (payload, &txd->Payload, sizeof (EP_PAYLOAD));
++    }
++
++    DoTransmit (xmtr, txd);
++
++    BucketStat (xmtr->Subsys, McastXmit, len);
++
++    return (EP_SUCCESS);
++}
++
++int
++ep_poll_transmits (EP_XMTR *xmtr)
++{
++    return (PollActiveTransmitList (xmtr, POLL_TX_LIST));
++}
++
++int
++ep_enable_txcallbacks (EP_XMTR *xmtr)
++{
++    return (PollActiveTransmitList (xmtr, ENABLE_TX_CALLBACK));
++}
++
++int
++ep_disable_txcallbacks (EP_XMTR *xmtr)
++{
++    return (PollActiveTransmitList (xmtr, DISABLE_TX_CALLBACK));
++}
++
++/* functions for accessing fields of txds */
++int              ep_txd_node(EP_TXD *txd)             { return (txd->NodeId); }
++EP_STATUSBLK    *ep_txd_statusblk(EP_TXD *txd)                { return (&txd->TxdMain->StatusBlk); }
++
++void
++ep_xmtr_xid_msg_handler (void *arg, EP_MANAGER_MSG *msg)
++{
++    EP_XMTR          *xmtr = (EP_XMTR *) arg;
++    EP_SYS           *sys  = xmtr->Subsys->Subsys.Sys;
++    struct list_head *el,*nel;
++    unsigned long     flags;
++
++    switch (msg->Hdr.Type)
++    {
++    case EP_MANAGER_MSG_TYPE_FAILOVER_REQUEST:
++      spin_lock_irqsave (&xmtr->Lock, flags);
++      list_for_each (el, &xmtr->ActiveDescList) {
++          EP_TXD      *txd     = list_entry (el, EP_TXD, Link);
++          EP_TXD_RAIL *txdRail = txd->TxdRail;
++
++          if (txdRail != NULL && EP_XIDS_MATCH (msg->Body.Failover.Xid, txd->Envelope.Xid))
++          {
++              EP_XMTR_RAIL       *xmtrRail = txdRail->XmtrRail;
++              EP_RAIL            *rail     = xmtrRail->CommsRail->Rail;
++              EP_MANAGER_MSG_BODY msgBody;
++              int                 rnum;
++
++              if (! (msg->Body.Failover.Railmask & EP_RAIL2RAILMASK (rail->Number)))
++              {
++                  /* Need to failover this txd to a different rail, select a rail from
++                   * the set that she has asked us to use and which is connected to her
++                   * on this transmitter.   If there are no such rails, then in all probability
++                   * we're offline on all common rails and eventually she will see we have no
++                   * rails in common and abort the receive. */
++                  if ((rnum = ep_xmtr_prefrail (xmtr, msg->Body.Failover.Railmask, txd->NodeId)) < 0)
++                      ep_debugf (DBG_XMTR, "%s: ep_xmtr_xid_msg_handler: FAILOVER_REQUEST but can't determine rail (%04x,%04x,%d,%04x)\n",
++                                 rail->Name, msg->Body.Failover.Railmask, xmtr->RailMask, txd->NodeId, sys->Nodes[txd->NodeId].ConnectedRails);
++                  else
++                  {
++                      EP_XMTR_RAIL *nXmtrRail = xmtr->Rails[rnum];
++
++                      EPRINTF4 (DBG_XMTR, "%s: ep_xmtr_xid_msg_handler: FAILOVER_REQUEST txd=%p XID=%llx-> rail %d\n", rail->Name, txd, (long long) txd->Envelope.Xid.Unique, rnum);
++
++                      /* Bind the txd rail onto the new rail - it doesn't matter if we fail
++                       * as it will remain bound to the original rail */
++                      (void) EP_XMTR_OP (nXmtrRail, BindTxd) (txd, nXmtrRail, EP_TXD_PHASE_PASSIVE);
++                  }
++              }
++
++              /* Send a failover response including an envelope update */
++              msgBody.FailoverTxd.Rail     = rail->Number;
++              msgBody.FailoverTxd.Xid      = txd->Envelope.Xid;
++              msgBody.FailoverTxd.TxdRail  = txd->Envelope.TxdRail;
++
++              ep_send_message (rail, msg->Hdr.NodeId, EP_MANAGER_MSG_TYPE_FAILOVER_RESPONSE, msg->Hdr.Xid, &msgBody);
++          }
++      }
++      spin_unlock_irqrestore (&xmtr->Lock, flags);
++      break;
++
++    case EP_MANAGER_MSG_TYPE_GET_NODE_STATE_RESPONSE: {
++      int         txd_has_not_sent_envelope = 0;
++      EP_TXD      *txd            = NULL;
++      EP_TXD_RAIL *txdRail        = NULL;
++
++      if (msg->Body.NodeState.NetworkErrorState != 0)
++          ep_kthread_schedule (&xmtr->Subsys->Thread, lbolt + MESSAGE_RETRY_TIME);
++      else
++      {
++          spin_lock_irqsave (&xmtr->Lock, flags);
++          list_for_each_safe (el, nel, &xmtr->ActiveDescList) {
++              
++              txd     = list_entry (el, EP_TXD, Link);
++              txdRail = txd->TxdRail;
++              
++              if (txdRail != NULL && EP_XIDS_MATCH (msg->Hdr.Xid, txd->Envelope.Xid)) {
++                  txd_has_not_sent_envelope = EP_XMTR_OP(txdRail->XmtrRail,CheckTxdState)(txd);
++                  break;
++              }
++          }
++          
++          if (txd_has_not_sent_envelope) {
++              EPRINTF2 (DBG_STABILISE, "ep_xmtr_xid_msg_handler: GET_NODE_STATE_RESPONSE txd=%p XID=%llx not sent envelope\n",
++                        txd, (long long) txd->Envelope.Xid.Unique);
++
++              /* at this point it has finished stabalising */
++              txd->Envelope.Attr = EP_CLEAR_TXD_STABALISING(txd->Envelope.Attr);
++
++              /* store railmask into txd if not a service indicator or timeout */
++              if (EP_IS_NO_TYPE(txd->Envelope.Attr))
++                  txd->Envelope.Attr = EP_SET_DATA(txd->Envelope.Attr, EP_TYPE_RAILMASK, msg->Body.NodeState.Railmask);
++
++              spin_unlock_irqrestore (&xmtr->Lock, flags);
++              
++              /* TXD is now no longer bound to a rail , so let ep_check_xmtr() handle it */
++              ep_kthread_schedule (&xmtr->Subsys->Thread, lbolt);
++          }
++          else
++              spin_unlock_irqrestore (&xmtr->Lock, flags);    
++      }
++      break;
++    }
++    default:
++      panic ("ep_xmtr_xid_msg_handler: XID match but invalid message type\n");
++    }
++}
++
++EP_XMTR *
++ep_alloc_xmtr (EP_SYS *sys)
++{
++    EP_COMMS_SUBSYS   *subsys;
++    EP_XMTR          *xmtr;
++    struct list_head *el;
++
++    if ((subsys = (EP_COMMS_SUBSYS *) ep_subsys_find (sys, EPCOMMS_SUBSYS_NAME)) == NULL)
++      return (NULL);
++
++    KMEM_ZALLOC (xmtr, EP_XMTR *, sizeof (EP_XMTR), 1);
++
++    if (xmtr == NULL)
++      return (NULL);
++    
++    xmtr->Subsys = subsys;
++
++    spin_lock_init (&xmtr->Lock);
++    INIT_LIST_HEAD (&xmtr->ActiveDescList);
++    
++    kcondvar_init (&xmtr->FreeDescSleep);
++    spin_lock_init (&xmtr->FreeDescLock);
++    INIT_LIST_HEAD (&xmtr->FreeDescList);
++    INIT_LIST_HEAD (&xmtr->DescBlockList);
++
++    ep_xid_cache_init (sys, &xmtr->XidCache);
++
++    xmtr->XidCache.MessageHandler = ep_xmtr_xid_msg_handler;
++    xmtr->XidCache.Arg            = xmtr;
++
++    kmutex_lock (&subsys->Lock);
++    list_add_tail (&xmtr->Link, &subsys->Transmitters);
++
++    ep_procfs_xmtr_add(xmtr);
++
++    /* Now add all rails which are already started */
++    list_for_each (el, &subsys->Rails) { 
++      EP_COMMS_RAIL *commsRail = list_entry (el, EP_COMMS_RAIL, Link);
++
++      EP_RAIL_OP(commsRail, Xmtr.AddRail) (xmtr, commsRail);
++    }
++    kmutex_unlock (&subsys->Lock);
++
++    ep_mod_inc_usecount();
++
++    return (xmtr);
++}
++
++void
++ep_free_xmtr (EP_XMTR *xmtr)
++{
++    EP_COMMS_SUBSYS  *subsys = xmtr->Subsys;
++    EP_SYS           *sys    = subsys->Subsys.Sys;
++    struct list_head *el;
++    
++    kmutex_lock (&subsys->Lock);
++    list_for_each (el, &subsys->Rails) { 
++      EP_COMMS_RAIL *rail = list_entry (el, EP_COMMS_RAIL, Link);
++
++      EP_RAIL_OP(rail,Xmtr.DelRail) (xmtr, rail);
++    }
++
++    list_del (&xmtr->Link);
++    kmutex_unlock (&subsys->Lock);
++
++    /* all the desc's must be free */
++    ASSERT(xmtr->FreeDescCount == xmtr->TotalDescCount);
++
++    /* delete the descs */
++    while (!list_empty (&xmtr->DescBlockList))
++      FreeTxdBlock( xmtr, list_entry(xmtr->DescBlockList.next, EP_TXD_BLOCK , Link));
++
++    /* they had better all be gone now */
++    ASSERT((xmtr->FreeDescCount == 0) && (xmtr->TotalDescCount == 0));
++
++    ep_procfs_xmtr_del(xmtr);
++
++    ep_xid_cache_destroy (sys, &xmtr->XidCache);
++
++    spin_lock_destroy (&xmtr->Lock);
++    KMEM_FREE (xmtr, sizeof (EP_XMTR));
++
++    ep_mod_dec_usecount();
++}
++
++long
++ep_check_xmtr (EP_XMTR *xmtr, long nextRunTime)
++{
++    EP_COMMS_SUBSYS  *subsys = xmtr->Subsys;
++    EP_SYS           *sys    = subsys->Subsys.Sys;
++    struct list_head *el, *nel;
++    struct list_head  txdList;
++    unsigned long       flags;
++    int                 timed_out=0;
++    int                 i;
++    EP_MANAGER_MSG_BODY body;
++
++    INIT_LIST_HEAD (&txdList);
++
++    /* See if we have any txd's which need to be bound to a rail */
++    spin_lock_irqsave (&xmtr->Lock, flags);
++    list_for_each_safe (el, nel, &xmtr->ActiveDescList) {
++      EP_TXD      *txd      = list_entry (el, EP_TXD, Link);
++      EP_NODE     *node     = &sys->Nodes[txd->NodeId];
++      EP_RAILMASK nodeRails = node->ConnectedRails & xmtr->RailMask;
++      EP_ENVELOPE *env      = &txd->Envelope;
++
++      if (EP_IS_TXD_STABALISING(txd->Envelope.Attr)) 
++      {
++          ASSERT(txd->TxdRail != NULL);
++
++          if (AFTER (lbolt, txd->RetryTime))
++          {
++              EPRINTF6 (DBG_STABILISE, "ep_check_xmtr txd=%p txdRail=%p send get node state to %d Xid=%08x.%08x.%016llx\n",
++                        txd, txd->TxdRail, txd->NodeId, env->Xid.Generation, env->Xid.Handle, env->Xid.Unique);
++              
++              body.Service = txd->Service;
++              if (ep_send_message ( txd->TxdRail->XmtrRail->CommsRail->Rail, txd->NodeId, EP_MANAGER_MSG_TYPE_GET_NODE_STATE, env->Xid, &body) == 0)
++                  txd->RetryTime = lbolt + (MESSAGE_RETRY_TIME << ep_backoff (&txd->Backoff, EP_BACKOFF_STABILISE));
++              else
++                  txd->RetryTime = lbolt + MSGBUSY_RETRY_TIME;
++          }
++
++          ep_kthread_schedule (&subsys->Thread, txd->RetryTime);
++          continue;
++      }
++
++      if (txd->TxdRail != NULL)
++          continue;
++
++      switch (EP_ATTR2TYPE(txd->Envelope.Attr)) 
++      {
++      case EP_TYPE_SVC_INDICATOR: 
++      {
++          EP_RAILMASK       rmask=0;
++          struct list_head *tmp;
++
++          list_for_each (tmp, &subsys->Rails) { 
++              EP_COMMS_RAIL *commsRail = list_entry (tmp, EP_COMMS_RAIL, Link);
++              if ( cm_svc_indicator_is_set(commsRail->Rail, EP_ATTR2DATA(txd->Envelope.Attr), txd->NodeId))
++                  rmask |= EP_RAIL2RAILMASK(commsRail->Rail->Number);
++          } 
++          nodeRails &= rmask;
++          break;
++      }
++      case EP_TYPE_TIMEOUT:
++          timed_out = AFTER(lbolt, txd->TimeStamp + EP_ATTR2DATA(txd->Envelope.Attr)) ? (1) : (0);
++          break;
++      case EP_TYPE_RAILMASK:
++          nodeRails &= EP_ATTR2DATA(txd->Envelope.Attr);
++          break;
++      default:
++          timed_out = AFTER(lbolt, txd->TimeStamp +  EP_DEFAULT_TIMEOUT) ? (1) : (0);
++          break;
++      }
++
++      if (nodeRails == 0 || timed_out || (EP_IS_NO_FAILOVER(env->Attr) && EP_IS_PREFRAIL_SET(env->Attr) && 
++                                          (nodeRails & EP_RAIL2RAILMASK(EP_ATTR2PREFRAIL(env->Attr))) == 0))
++      {
++          EPRINTF5 (timed_out ? DBG_STABILISE : DBG_XMTR, "ep_check_xmtr: txd=%p XID=%llx to %d no rails connected or cannot failover (nodeRails=0x%x,timed_out=%d\n", 
++                    txd, (long long) env->Xid.Unique, txd->NodeId, nodeRails, timed_out);
++
++          list_del  (&txd->Link);
++          list_add_tail (&txd->Link, &txdList);
++      }
++      else
++      {
++          EP_XMTR_RAIL *xmtrRail;
++          int i, len, rnum;
++
++          if (EP_IS_PREFRAIL_SET(env->Attr) && (nodeRails & EP_RAIL2RAILMASK(EP_ATTR2PREFRAIL(env->Attr))))
++              rnum = EP_ATTR2PREFRAIL(env->Attr);
++          else
++              rnum = ep_pickRail (nodeRails);
++
++          EPRINTF3 (DBG_XMTR, "ep_check_xmtr: txd=%p XID=%llx mapping NMDs onto rail %d \n", txd, (long long) env->Xid.Unique, rnum);
++
++          for (i = len = 0; i < env->nFrags; i++, len += env->Frags[i].nmd_len)
++              ep_nmd_map_rails (sys, &env->Frags[i], nodeRails);
++
++          if ((xmtrRail = xmtr->Rails[rnum]) == NULL || 
++              !EP_XMTR_OP(xmtrRail,BindTxd) (txd, xmtrRail, EP_TXD_PHASE_ACTIVE))
++              ep_kthread_schedule (&subsys->Thread, lbolt + RESOURCE_RETRY_TIME);
++      }
++    }
++    spin_unlock_irqrestore (&xmtr->Lock, flags);
++
++    while (! list_empty (&txdList))
++    {
++      EP_TXD *txd = list_entry (txdList.next, EP_TXD, Link);
++      list_del (&txd->Link);
++
++      txd->Handler (txd, txd->Arg, EP_NODE_DOWN);
++      FreeTxd (xmtr, txd);
++    }
++
++    /* Check to see if we're low on txds */
++    if (xmtr->FreeDescCount < ep_txd_lowat)
++      AllocateTxdBlock (xmtr, 0, NULL);
++    
++    /* Then check each rail */
++    for (i = 0; i < EP_MAX_RAILS; i++) 
++      if (xmtr->RailMask & (1 << i) ) 
++          nextRunTime = EP_XMTR_OP (xmtr->Rails[i],Check) (xmtr->Rails[i], nextRunTime);
++    return (nextRunTime);
++}
++
++void
++ep_display_txd (DisplayInfo *di, EP_TXD *txd)
++{
++    EP_ENVELOPE *env     = &txd->Envelope;
++    EP_TXD_RAIL *txdRail = txd->TxdRail;
++
++    (di->func)(di->arg, "TXD: %p Version=%x Attr=%x Xid=%08x.%08x.%016llx\n", txd, 
++             env->Version, env->Attr, env->Xid.Generation, env->Xid.Handle, (long long) env->Xid.Unique);
++    (di->func)(di->arg,  "     NodeId=%d Range=%d.%d TxdRail=%x TxdMain=%x.%x.%x nFrags=%d\n",
++             env->NodeId, EP_RANGE_LOW(env->Range), EP_RANGE_HIGH(env->Range), env->TxdRail,
++             env->TxdMain.nmd_addr, env->TxdMain.nmd_len, env->TxdMain.nmd_attr, env->nFrags);
++    (di->func)(di->arg,  "       Frag[0] %08x.%08x.%08x\n", env->Frags[0].nmd_addr, env->Frags[0].nmd_len, env->Frags[0].nmd_attr);
++    (di->func)(di->arg,  "       Frag[1] %08x.%08x.%08x\n", env->Frags[1].nmd_addr, env->Frags[1].nmd_len, env->Frags[1].nmd_attr);
++    (di->func)(di->arg,  "       Frag[2] %08x.%08x.%08x\n", env->Frags[2].nmd_addr, env->Frags[2].nmd_len, env->Frags[2].nmd_attr);
++    (di->func)(di->arg,  "       Frag[3] %08x.%08x.%08x\n", env->Frags[3].nmd_addr, env->Frags[3].nmd_len, env->Frags[3].nmd_attr);
++
++    if (txdRail != NULL) EP_XMTR_OP (txdRail->XmtrRail, DisplayTxd) (di, txdRail);
++}
++
++void
++ep_display_xmtr (DisplayInfo *di, EP_XMTR *xmtr)
++{
++    int               freeCount   = 0;
++    int               activeCount = 0;
++    struct list_head *el;
++    int               i;
++    unsigned long     flags;
++
++    spin_lock_irqsave (&xmtr->FreeDescLock, flags);
++    list_for_each (el, &xmtr->FreeDescList)
++      freeCount++;
++    spin_unlock_irqrestore (&xmtr->FreeDescLock, flags);
++
++    spin_lock_irqsave (&xmtr->Lock, flags);
++    list_for_each (el, &xmtr->ActiveDescList)
++      activeCount++;
++    
++    (di->func)(di->arg, "ep_display_xmtr: xmtr=%p Free=%d Active=%d\n", xmtr, freeCount, activeCount);
++    for (i = 0; i < EP_MAX_RAILS; i++)
++      if (xmtr->Rails[i]) EP_XMTR_OP (xmtr->Rails[i], DisplayXmtr) (di, xmtr->Rails[i]);
++
++    list_for_each (el,&xmtr->ActiveDescList)
++      ep_display_txd (di, list_entry (el, EP_TXD, Link));
++    spin_unlock_irqrestore (&xmtr->Lock, flags);
++}
++
++void
++ep_xmtr_fillout_stats(EP_XMTR *xmtr, char *str)
++{
++    sprintf(str+strlen(str),"Tx     %lu  %lu /sec\n",       GET_STAT_TOTAL(xmtr->stats,tx),      GET_STAT_PER_SEC(xmtr->stats,tx) );
++    sprintf(str+strlen(str),"MBytes %lu  %lu Mbytes/sec\n", GET_STAT_TOTAL(xmtr->stats,tx_len) / (1024*1024),  GET_STAT_PER_SEC(xmtr->stats,tx_len) / (1024*1024));
++}
++
++void
++ep_xmtr_rail_fillout_stats(EP_XMTR_RAIL *xmtr_rail, char *str)
++{
++    sprintf(str+strlen(str),"Tx     %lu  %lu /sec\n",       GET_STAT_TOTAL(xmtr_rail->stats,tx),      GET_STAT_PER_SEC(xmtr_rail->stats,tx) );
++    sprintf(str+strlen(str),"MBytes %lu  %lu Mbytes/sec\n", GET_STAT_TOTAL(xmtr_rail->stats,tx_len) / (1024*1024),  GET_STAT_PER_SEC(xmtr_rail->stats,tx_len) / (1024*1024));
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/ep/epcommsTx_elan3.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/epcommsTx_elan3.c    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/epcommsTx_elan3.c 2005-05-11 12:10:12.503922696 -0400
+@@ -0,0 +1,1173 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: epcommsTx_elan3.c,v 1.17.2.2 2004/11/12 10:54:51 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/epcommsTx_elan3.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++#include <elan/epsvc.h>
++#include <elan/epcomms.h>
++
++#include "kcomm_vp.h"
++#include "kcomm_elan3.h"
++#include "epcomms_elan3.h"
++#include "debug.h"
++
++#define XMTR_TO_RAIL(xmtrRail)                ((EP3_RAIL *) ((EP_XMTR_RAIL *) xmtrRail)->CommsRail->Rail)
++#define XMTR_TO_DEV(xmtrRail)         (XMTR_TO_RAIL(xmtrRail)->Device)
++#define XMTR_TO_SUBSYS(xmtrRail)      (((EP_XMTR_RAIL *) xmtrRail)->Xmtr->Subsys)
++
++static void TxEnveEvent (EP3_RAIL *rail, void *arg);
++static void TxEnveRetry (EP3_RAIL *rail, void *arg, E3_DMA_BE *dma, int status);
++static void TxEnveVerify (EP3_RAIL *rail, void *arg, E3_DMA_BE *dma);
++
++static EP3_COOKIE_OPS EnveCookieOps =
++{
++    TxEnveEvent,
++    TxEnveRetry,
++    NULL, /* DmaCancelled */
++    TxEnveVerify
++};
++
++static void TxDataEvent (EP3_RAIL *rail, void *arg);
++static void TxDataRetry (EP3_RAIL *rail, void *arg, E3_DMA_BE *dma, int status);
++static void TxDataVerify (EP3_RAIL *rail, void *arg, E3_DMA_BE *dma);
++
++static EP3_COOKIE_OPS DataCookieOps =
++{
++    TxDataEvent,
++    TxDataRetry,
++    NULL, /* DmaCancelled */
++    TxDataVerify
++};
++
++static void TxDoneEvent (EP3_RAIL *dev, void *arg);
++static void TxDoneRetry (EP3_RAIL *dev, void *arg, E3_DMA_BE *dma, int status);
++static void TxDoneVerify (EP3_RAIL  *dev, void *arg, E3_DMA_BE *dma);
++
++static EP3_COOKIE_OPS DoneCookieOps = 
++{
++    TxDoneEvent,
++    TxDoneRetry,
++    NULL, /* DmaCancelled */
++    TxDoneVerify,
++} ;
++
++static int
++AllocateTxdRailBlock (EP3_XMTR_RAIL *xmtrRail)
++{
++    EP3_RAIL          *rail = XMTR_TO_RAIL (xmtrRail);
++    ELAN3_DEV         *dev = rail->Device;
++    EP3_TXD_RAIL_BLOCK *blk;
++    EP3_TXD_RAIL       *txdRail;
++    sdramaddr_t        pTxdElan;
++    EP3_TXD_RAIL_MAIN  *pTxdMain;
++    E3_Addr          pTxdElanAddr;
++    E3_Addr          pTxdMainAddr;
++    E3_BlockCopyEvent  event;
++    int                      i;
++    unsigned long      flags;
++
++    KMEM_ZALLOC (blk, EP3_TXD_RAIL_BLOCK *, sizeof (EP3_TXD_RAIL_BLOCK), 1);
++
++    if (blk == NULL)
++      return 0;
++
++    if ((pTxdElan = ep_alloc_elan (&rail->Generic, EP3_TXD_RAIL_ELAN_SIZE * EP3_NUM_TXD_PER_BLOCK, 0, &pTxdElanAddr)) == (sdramaddr_t) 0)
++    {
++      KMEM_FREE (blk, sizeof (EP3_TXD_RAIL_BLOCK));
++      return 0;
++    }
++
++    if ((pTxdMain = ep_alloc_main (&rail->Generic, EP3_TXD_RAIL_MAIN_SIZE * EP3_NUM_TXD_PER_BLOCK, 0, &pTxdMainAddr)) == (EP3_TXD_RAIL_MAIN *) NULL)
++    {
++      ep_free_elan (&rail->Generic, pTxdElanAddr, EP3_TXD_RAIL_ELAN_SIZE * EP3_NUM_TXD_PER_BLOCK);
++      KMEM_FREE (blk, sizeof (EP3_TXD_RAIL_BLOCK));
++      return 0;
++    }
++    
++    if (ReserveDmaRetries (rail, EP3_NUM_TXD_PER_BLOCK, 0) != ESUCCESS)
++    {
++      ep_free_main (&rail->Generic, pTxdMainAddr, EP3_TXD_RAIL_MAIN_SIZE * EP3_NUM_TXD_PER_BLOCK);
++      ep_free_elan (&rail->Generic, pTxdElanAddr, EP3_TXD_RAIL_ELAN_SIZE * EP3_NUM_TXD_PER_BLOCK);
++      KMEM_FREE (blk, sizeof (EP3_TXD_RAIL_BLOCK));
++      return 0;
++    }
++
++    for (txdRail = &blk->Txd[0], i = 0; i < EP3_NUM_TXD_PER_BLOCK; i++, txdRail++)
++    {
++      txdRail->Generic.XmtrRail = &xmtrRail->Generic;
++      txdRail->TxdElan          = pTxdElan;
++      txdRail->TxdElanAddr      = pTxdElanAddr;
++      txdRail->TxdMain          = pTxdMain;
++      txdRail->TxdMainAddr      = pTxdMainAddr;
++
++      RegisterCookie (&rail->CookieTable, &txdRail->EnveCookie, pTxdElanAddr + offsetof (EP3_TXD_RAIL_ELAN, EnveEvent), &EnveCookieOps, (void *) txdRail);
++      RegisterCookie (&rail->CookieTable, &txdRail->DataCookie, pTxdElanAddr + offsetof (EP3_TXD_RAIL_ELAN, DataEvent), &DataCookieOps, (void *) txdRail);
++      RegisterCookie (&rail->CookieTable, &txdRail->DoneCookie, pTxdElanAddr + offsetof (EP3_TXD_RAIL_ELAN, DoneEvent), &DoneCookieOps, (void *) txdRail);
++
++      EP3_INIT_COPY_EVENT (event, txdRail->EnveCookie, pTxdMainAddr + offsetof (EP3_TXD_RAIL_MAIN, EnveEvent), 0);
++      elan3_sdram_copyl_to_sdram (dev, &event, pTxdElan + offsetof (EP3_TXD_RAIL_ELAN, EnveEvent), sizeof (E3_BlockCopyEvent));
++
++      EP3_INIT_COPY_EVENT (event, txdRail->DataCookie, pTxdMainAddr + offsetof (EP3_TXD_RAIL_MAIN, DataEvent), 0);
++      elan3_sdram_copyl_to_sdram (dev, &event, pTxdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent), sizeof (E3_BlockCopyEvent));
++
++      EP3_INIT_COPY_EVENT (event, txdRail->DoneCookie, pTxdMainAddr + offsetof (EP3_TXD_RAIL_MAIN, DoneEvent), 0);
++      elan3_sdram_copyl_to_sdram (dev, &event, pTxdElan + offsetof (EP3_TXD_RAIL_ELAN, DoneEvent), sizeof (E3_BlockCopyEvent));
++      
++      pTxdMain->EnveEvent = EP3_EVENT_FREE;
++      pTxdMain->DataEvent = EP3_EVENT_FREE;
++      pTxdMain->DoneEvent = EP3_EVENT_FREE;
++
++      /* move onto next descriptor */
++      pTxdElan     += EP3_TXD_RAIL_ELAN_SIZE;
++      pTxdElanAddr += EP3_TXD_RAIL_ELAN_SIZE;
++      pTxdMain      = (EP3_TXD_RAIL_MAIN *) ((unsigned long) pTxdMain + EP3_TXD_RAIL_MAIN_SIZE);
++      pTxdMainAddr += EP3_TXD_RAIL_MAIN_SIZE;
++    }
++
++    spin_lock_irqsave (&xmtrRail->FreeDescLock, flags);
++
++    list_add  (&blk->Link, &xmtrRail->DescBlockList);
++    xmtrRail->TotalDescCount += EP3_NUM_TXD_PER_BLOCK;
++    xmtrRail->FreeDescCount  += EP3_NUM_TXD_PER_BLOCK;
++
++    for (i = 0; i < EP3_NUM_TXD_PER_BLOCK; i++)
++      list_add (&blk->Txd[i].Generic.Link, &xmtrRail->FreeDescList);
++
++    spin_unlock_irqrestore (&xmtrRail->FreeDescLock, flags);
++    
++    return 1;
++}
++
++static void
++FreeTxdRailBlock (EP3_XMTR_RAIL *xmtrRail, EP3_TXD_RAIL_BLOCK *blk)
++{
++    EP3_RAIL     *rail = XMTR_TO_RAIL(xmtrRail);
++    EP3_TXD_RAIL *txdRail;
++    unsigned long flags;
++    int i;
++
++    spin_lock_irqsave (&xmtrRail->FreeDescLock, flags);
++
++    list_del (&blk->Link);
++    
++    xmtrRail->TotalDescCount -= EP3_NUM_TXD_PER_BLOCK;
++    
++    for (txdRail = &blk->Txd[0], i = 0; i < EP3_NUM_TXD_PER_BLOCK; i++, txdRail++)
++    {
++      xmtrRail->FreeDescCount--;
++      
++      list_del (&txdRail->Generic.Link);
++      
++      DeregisterCookie (&rail->CookieTable, &txdRail->EnveCookie);
++      DeregisterCookie (&rail->CookieTable, &txdRail->DataCookie);
++      DeregisterCookie (&rail->CookieTable, &txdRail->DoneCookie);
++    }
++
++    spin_unlock_irqrestore (&xmtrRail->FreeDescLock, flags);
++
++    ReleaseDmaRetries (rail, EP3_NUM_TXD_PER_BLOCK);
++
++    ep_free_main (&rail->Generic, blk->Txd[0].TxdMainAddr, EP3_TXD_RAIL_MAIN_SIZE * EP3_NUM_TXD_PER_BLOCK);
++    ep_free_elan (&rail->Generic, blk->Txd[0].TxdElanAddr, EP3_TXD_RAIL_ELAN_SIZE * EP3_NUM_TXD_PER_BLOCK);
++    KMEM_FREE (blk, sizeof (EP3_TXD_RAIL_BLOCK));
++}
++
++static EP3_TXD_RAIL *
++GetTxdRail (EP3_XMTR_RAIL *xmtrRail)
++{
++    EP_COMMS_SUBSYS  *subsys = xmtrRail->Generic.Xmtr->Subsys;
++    EP3_TXD_RAIL     *txdRail;
++    int low_on_txds;
++    unsigned long flags;
++
++    spin_lock_irqsave (&xmtrRail->FreeDescLock, flags);
++
++    if (list_empty (&xmtrRail->FreeDescList))
++      txdRail = NULL;
++    else
++    {
++      txdRail = list_entry (xmtrRail->FreeDescList.next, EP3_TXD_RAIL, Generic.Link);
++
++#if defined(DEBUG)
++      {
++          EP_RAIL   *rail = xmtrRail->Generic.CommsRail->Rail;
++          ELAN3_DEV *dev  = ((EP3_RAIL *) rail)->Device;
++          
++          EP_ASSERT (rail, txdRail->TxdMain->EnveEvent == EP3_EVENT_FREE);
++          EP_ASSERT (rail, txdRail->TxdMain->DataEvent == EP3_EVENT_FREE);
++          EP_ASSERT (rail, txdRail->TxdMain->DoneEvent == EP3_EVENT_FREE);
++          EP_ASSERT (rail, SDRAM_ASSERT(elan3_sdram_readl (dev, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, EnveEvent.ev_Count)) == 0));
++          EP_ASSERT (rail, SDRAM_ASSERT(elan3_sdram_readl (dev, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent.ev_Count)) == 0));
++          EP_ASSERT (rail, SDRAM_ASSERT(elan3_sdram_readl (dev, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DoneEvent.ev_Count)) == 0));
++      }
++#endif
++      
++      list_del (&txdRail->Generic.Link);
++
++      xmtrRail->FreeDescCount--;
++    }
++    /* Wakeup the descriptor primer thread if there's not many left */
++    low_on_txds = (xmtrRail->FreeDescCount < ep_txd_lowat);
++
++    spin_unlock_irqrestore (&xmtrRail->FreeDescLock, flags);
++
++    if (low_on_txds)
++      ep_kthread_schedule (&subsys->Thread, lbolt);
++
++    return (txdRail);
++}
++
++static void
++FreeTxdRail (EP3_XMTR_RAIL *xmtrRail, EP3_TXD_RAIL *txdRail)
++{
++    unsigned long flags;
++
++#if defined(DEBUG_ASSERT)
++    {
++      EP_RAIL   *rail = xmtrRail->Generic.CommsRail->Rail;
++      ELAN3_DEV *dev  = ((EP3_RAIL *) rail)->Device;
++
++      EP_ASSERT (rail, txdRail->Generic.XmtrRail == &xmtrRail->Generic);
++      
++      EP_ASSERT (rail, txdRail->TxdMain->EnveEvent == EP3_EVENT_PRIVATE);
++      EP_ASSERT (rail, txdRail->TxdMain->DataEvent == EP3_EVENT_PRIVATE);
++      EP_ASSERT (rail, txdRail->TxdMain->DoneEvent == EP3_EVENT_PRIVATE);
++      EP_ASSERT (rail, SDRAM_ASSERT (elan3_sdram_readl (dev, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, EnveEvent.ev_Count)) == 0));
++      EP_ASSERT (rail, SDRAM_ASSERT (elan3_sdram_readl (dev, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent.ev_Count)) == 0));
++      EP_ASSERT (rail, SDRAM_ASSERT (elan3_sdram_readl (dev, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DoneEvent.ev_Count)) == 0));
++      
++      txdRail->TxdMain->EnveEvent = EP3_EVENT_FREE;
++      txdRail->TxdMain->DataEvent = EP3_EVENT_FREE;
++      txdRail->TxdMain->DoneEvent = EP3_EVENT_FREE;
++    }
++#endif
++
++    spin_lock_irqsave (&xmtrRail->FreeDescLock, flags);
++    
++    list_add (&txdRail->Generic.Link, &xmtrRail->FreeDescList);
++
++    xmtrRail->FreeDescCount++;
++
++    if (xmtrRail->FreeDescWaiting)
++    {
++      xmtrRail->FreeDescWaiting--;
++      kcondvar_wakeupall (&xmtrRail->FreeDescSleep, &xmtrRail->FreeDescLock);
++    }
++
++    spin_unlock_irqrestore (&xmtrRail->FreeDescLock, flags);
++}
++
++static void
++BindTxdToRail (EP_TXD *txd, EP3_TXD_RAIL *txdRail)
++{
++    ASSERT (SPINLOCK_HELD (&txd->Xmtr->Lock));
++
++    EPRINTF6 (DBG_XMTR, "%s: BindTxdToRail: txd=%p txdRail=%p XID=%08x.%08x.%016llx\n", 
++            XMTR_TO_RAIL(txdRail->Generic.XmtrRail)->Generic.Name, txd, txdRail, 
++            txd->Envelope.Xid.Generation, txd->Envelope.Xid.Handle, (long long) txd->Envelope.Xid.Unique);
++
++    txd->TxdRail = &txdRail->Generic;
++    txdRail->Generic.Txd = txd;
++}
++
++static void
++UnbindTxdFromRail (EP_TXD *txd, EP3_TXD_RAIL *txdRail)
++{
++    ASSERT (SPINLOCK_HELD (&txd->Xmtr->Lock));
++    ASSERT (txd->TxdRail == &txdRail->Generic && txdRail->Generic.Txd == txd);
++
++    EPRINTF6 (DBG_XMTR, "%s: UnbindTxdToRail: txd=%p txdRail=%p XID=%08x.%08x.%016llx\n", 
++            XMTR_TO_RAIL(txdRail->Generic.XmtrRail)->Generic.Name, txd, txdRail, 
++            txd->Envelope.Xid.Generation, txd->Envelope.Xid.Handle, (long long) txd->Envelope.Xid.Unique);
++    txd->TxdRail = NULL;
++    txdRail->Generic.Txd = NULL; 
++}
++
++/*
++ * TxEnveEvent: arg == EP_TXD
++ *    Called when envelope delivered
++ */
++static void
++TxEnveEvent (EP3_RAIL *rail, void *arg)
++{
++    panic ("TxEnveEvent");
++}
++
++/*
++ * TxEnveRetry: arg == EP3_TXD_RAIL
++ *    Called on retry of dma of large message envelope.
++ */
++static void
++TxEnveRetry (EP3_RAIL *rail, void *arg, E3_DMA_BE *dma, int status)
++{
++    EP3_TXD_RAIL  *txdRail  = (EP3_TXD_RAIL *) arg;
++    EP3_XMTR_RAIL *xmtrRail = (EP3_XMTR_RAIL *) txdRail->Generic.XmtrRail;
++    
++    EPRINTF3 (DBG_XMTR, "%s: TxEnveRetry: xmtr %p txd %p\n",  rail->Generic.Name, xmtrRail, txdRail);
++    
++    EP_ASSERT (&rail->Generic, txdRail->TxdMain->EnveEvent == EP3_EVENT_ACTIVE);
++    EP_ASSERT (&rail->Generic, SDRAM_ASSERT (elan3_sdram_readl (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, EnveEvent.ev_Count)) == 1)); /* PCI read */
++    EP_ASSERT (&rail->Generic, dma->s.dma_direction == DMA_WRITE && EP_VP_TO_NODE(dma->s.dma_destVProc) == txdRail->Generic.Txd->NodeId);
++
++    if (! TxdShouldStabalise (&txdRail->Generic, &rail->Generic))
++      QueueDmaForRetry (rail, dma, EP_RETRY_LOW_PRI_RETRY + ep_backoff (&txdRail->Backoff, EP_BACKOFF_ENVELOPE));
++    else
++      QueueDmaForRetry (rail, dma, EP_RETRY_STABALISING);     /* place dma on stabilising list for neterr fixup */
++}
++
++static void
++TxEnveVerify (EP3_RAIL *rail, void *arg, E3_DMA_BE *dma)
++{
++    EP3_TXD_RAIL *txdRail = (EP3_TXD_RAIL *) arg;
++    
++    EP_ASSERT (&rail->Generic, txdRail->TxdMain->EnveEvent == EP3_EVENT_ACTIVE);
++    EP_ASSERT (&rail->Generic, SDRAM_ASSERT (elan3_sdram_readl (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, EnveEvent.ev_Count)) == 1)); /* PCI read */
++    EP_ASSERT (&rail->Generic, dma->s.dma_direction == DMA_WRITE && EP_VP_TO_NODE(dma->s.dma_destVProc) == txdRail->Generic.Txd->NodeId);
++}
++
++/*
++ * TxDataEvent: arg == EP3_TXD
++ *    Called on completion of a large transmit.
++ */
++static void
++TxDataEvent (EP3_RAIL *rail, void *arg)
++{
++    EP3_TXD_RAIL      *txdRail  = (EP3_TXD_RAIL *) arg;
++    EP3_XMTR_RAIL     *xmtrRail = (EP3_XMTR_RAIL *) txdRail->Generic.XmtrRail;
++    EP_XMTR         *xmtr     = xmtrRail->Generic.Xmtr;
++    EP3_TXD_RAIL_MAIN *txdMain  = txdRail->TxdMain;
++    sdramaddr_t        txdElan  = txdRail->TxdElan;
++    int                delay    = 1;
++    EP_TXD            *txd;
++    unsigned long      flags;
++
++    spin_lock_irqsave (&xmtr->Lock, flags);
++    for (;;)
++    {
++      if (EP3_EVENT_FIRED (txdRail->DataCookie, txdMain->DataEvent))
++          break;
++
++      if (EP3_EVENT_FIRING (rail->Device, txdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent), txdRail->DataCookie, txdMain->DataEvent))                /* PCI read */
++      {
++          if (delay > EP3_EVENT_FIRING_TLIMIT)
++              panic ("TxDataEvent: events set but block copy not completed\n");
++          DELAY(delay);
++          delay <<= 1;
++      }
++      else
++      {
++          EPRINTF3 (DBG_XMTR, "%s: TxDataEvent: xmtr %p txd %p previously collecting by polling\n", 
++                    rail->Generic.Name, xmtrRail, txdRail);
++          spin_unlock_irqrestore (&xmtr->Lock, flags);
++          return;
++      }
++      mb();
++    }
++
++    if ((txd = txdRail->Generic.Txd) == NULL ||                       /* If there is no txd, or if the descriptor is marked */
++      !(EP_IS_INTERRUPT_ENABLED(txd->Envelope.Attr)) ||       /* as no interrupt, or been reused as an RPC, */
++      (EP_IS_RPC(txd->Envelope.Attr)))                        /* then we were either called as a result of a previous */
++    {                                                         /* tx which was completed by polling or as a result */
++      spin_unlock_irqrestore (&xmtr->Lock, flags);            /* of a EnableTxCallBack/DisableTxCallback */
++
++      EPRINTF4 (DBG_XMTR, "%s: TxDataEvent: xmtr %p txd %p recyled (%x)\n", 
++                rail->Generic.Name, xmtr, txd, txd ? txd->Envelope.Attr : 0);
++      return;
++    }
++
++    ASSERT (EP3_EVENT_FIRED (txdRail->EnveCookie, txdMain->EnveEvent));
++
++    EPRINTF5 (DBG_XMTR, "%s: TxDataEvent : xmtrRail=%p txdRail=%p tx=%p XID=%llx\n", 
++            rail->Generic.Name, xmtrRail, txdRail, txd, (long long) txd->Envelope.Xid.Unique);
++    
++    ep_xmtr_txd_stat(xmtr,txd);
++    
++    /* remove from active transmit lists */
++    list_del (&txd->Link);
++
++    UnbindTxdFromRail (txd, txdRail);
++    
++    /* clear the done flags for next time round */
++    txdMain->EnveEvent = EP3_EVENT_PRIVATE;
++    txdMain->DataEvent = EP3_EVENT_PRIVATE;
++    txdMain->DoneEvent = EP3_EVENT_PRIVATE;
++    
++    FreeTxdRail (xmtrRail, txdRail);
++
++    spin_unlock_irqrestore (&xmtr->Lock, flags);
++    
++    txd->Handler (txd, txd->Arg, EP_SUCCESS);
++    
++    FreeTxd (xmtr, txd);
++}
++
++/*
++ * TxDataRetry: arg == EP3_TXD
++ *    Called on retry of remote "put" dma of large transmit data.
++ */
++static void
++TxDataRetry (EP3_RAIL *rail, void *arg, E3_DMA_BE *dma, int status)
++{
++    EP3_TXD_RAIL  *txdRail  = (EP3_TXD_RAIL *) arg;
++    EP3_XMTR_RAIL *xmtrRail = (EP3_XMTR_RAIL *) txdRail->Generic.XmtrRail;
++    EP_TXD        *txd      = txdRail->Generic.Txd;
++
++    EP_ASSERT (&rail->Generic, ((txdRail->TxdMain->DataEvent == EP3_EVENT_ACTIVE && 
++                               SDRAM_ASSERT (elan3_sdram_readl (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent.ev_Count)) >= 1)) ||  /* PCI read */
++                              (EP3_EVENT_FIRED (txdRail->DataCookie, txdRail->TxdMain->DataEvent) &&
++                               SDRAM_ASSERT (elan3_sdram_readl (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent.ev_Count)) == 0))));  /* PCI read */
++    EP_ASSERT (&rail->Generic, dma->s.dma_direction == DMA_WRITE && EP_VP_TO_NODE(dma->s.dma_destVProc) == txd->NodeId);
++
++    EPRINTF5 (DBG_XMTR, "%s: TxDataRetry: xmtrRail=%p txdRail=%p txd=%p XID=%llx\n", 
++            rail->Generic.Name, xmtrRail, txdRail, txd, (long long) txd->Envelope.Xid.Unique);
++    
++    QueueDmaForRetry (rail, dma, EP_RETRY_LOW_PRI_RETRY + ep_backoff (&txdRail->Backoff, EP_BACKOFF_DATA));
++}
++
++static void
++TxDataVerify (EP3_RAIL *rail, void *arg, E3_DMA_BE *dma)
++{
++    EP3_TXD_RAIL *txdRail = (EP3_TXD_RAIL *) arg;
++    EP_TXD       *txd     = txdRail->Generic.Txd;
++
++    EP_ASSERT (&rail->Generic, ((txdRail->TxdMain->DataEvent == EP3_EVENT_ACTIVE && 
++                               SDRAM_ASSERT (elan3_sdram_readl (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent.ev_Count)) >= 1)) ||  /* PCI read */
++                              (EP3_EVENT_FIRED (txdRail->DataCookie, txdRail->TxdMain->DataEvent) &&
++                               SDRAM_ASSERT (elan3_sdram_readl (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent.ev_Count)) == 0))));  /* PCI read */
++    EP_ASSERT (&rail->Generic, dma->s.dma_direction == DMA_WRITE && EP_VP_TO_NODE(dma->s.dma_destVProc) == txd->NodeId);
++}
++
++/*
++ * TxDoneEvent: arg == EP3_TXD
++ *    Called on completion of a RPC.
++ */
++static void
++TxDoneEvent (EP3_RAIL *rail, void *arg)
++{
++    EP3_TXD_RAIL      *txdRail  = (EP3_TXD_RAIL *) arg;
++    EP3_XMTR_RAIL     *xmtrRail = (EP3_XMTR_RAIL *) txdRail->Generic.XmtrRail;
++    EP_XMTR         *xmtr     = xmtrRail->Generic.Xmtr;
++    int                delay   = 1;
++    EP_TXD          *txd;
++    unsigned long      flags;
++
++    spin_lock_irqsave (&xmtr->Lock, flags);
++
++    for (;;)
++    {
++      if (EP3_EVENT_FIRED (txdRail->DoneCookie, txdRail->TxdMain->DoneEvent) && 
++          EP3_EVENT_FIRED (txdRail->DataCookie, txdRail->TxdMain->DataEvent))
++          break;
++      
++      if (EP3_EVENT_FIRING (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DoneEvent), txdRail->DoneCookie, txdRail->TxdMain->DoneEvent) && 
++          EP3_EVENT_FIRING (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent), txdRail->DataCookie, txdRail->TxdMain->DataEvent))
++      {
++          if (delay > EP3_EVENT_FIRING_TLIMIT)
++              panic ("TxDoneEvent: events set but block copy not completed\n");
++          DELAY(delay);
++          delay <<= 1;
++      }
++      else
++      {
++          EPRINTF3 (DBG_XMTR, "%s: TxDoneEvent: xmtr %p txdRail %p previously collecting by polling\n", 
++                    rail->Generic.Name, xmtr, txdRail);
++          spin_unlock_irqrestore (&xmtr->Lock, flags);
++          return;
++      }
++      mb();
++    }
++
++    if ((txd = txdRail->Generic.Txd) == NULL ||                                                /* If there is no txd, or if the descriptor is marked */
++      !(EP_IS_INTERRUPT_ENABLED(txd->Envelope.Attr) || EP_IS_RPC(txd->Envelope.Attr))) /* marked as no interrupt, or been reused as an transmit, */
++    {                                                                                  /* then we were either called as a result of a previous */
++      spin_unlock_irqrestore (&xmtr->Lock, flags);                                     /* tx which was completed by polling or as a result */
++                                                                                       /* of a EnableTxCallBack/DisableTxCallback */
++
++      EPRINTF4 (DBG_XMTR, "%s: TxDoneEvent: xmtr %p txd %p recyled (%x)\n", 
++                rail->Generic.Name, xmtr, txd, txd ? txd->Envelope.Attr : 0);
++      return; 
++    }
++
++    EPRINTF5 (DBG_XMTR, "%s: TxDoneEvent: xmtrRail=%p txdRail=%p txd=%p XID=%llx\n", 
++            rail->Generic.Name, xmtrRail, txdRail, txd, (long long) txd->Envelope.Xid.Unique);
++
++    ep_xmtr_txd_stat(xmtr,txd);
++
++    /* remove from active transmit list */
++    list_del (&txd->Link);
++    
++    UnbindTxdFromRail (txd, txdRail);
++    
++    /* clear the done flags for next time round */
++    txdRail->TxdMain->EnveEvent = EP3_EVENT_PRIVATE;
++    txdRail->TxdMain->DataEvent = EP3_EVENT_PRIVATE;
++    txdRail->TxdMain->DoneEvent = EP3_EVENT_PRIVATE;
++    
++    FreeTxdRail (xmtrRail, txdRail);
++
++    spin_unlock_irqrestore (&xmtr->Lock, flags);
++          
++    if (txd->Handler)
++      txd->Handler (txd, txd->Arg, EP_SUCCESS);
++      
++    FreeTxd (xmtr, txd);
++}
++
++/*
++ * TxDoneRetry: arg == EP3_TXD
++ */
++static void
++TxDoneRetry (EP3_RAIL *rail, void *arg, E3_DMA_BE *dma, int status)
++{
++    panic ("TxDoneRetry");
++}
++
++static void
++TxDoneVerify (EP3_RAIL *rail, void *arg, E3_DMA_BE *dma)
++{
++    panic ("TxDoneVerify");
++}
++
++static void
++EnableTransmitCallback (EP_TXD *txd, EP3_TXD_RAIL *txdRail)
++{
++    ELAN3_DEV *dev = XMTR_TO_RAIL(txdRail->Generic.XmtrRail)->Device;
++
++    EPRINTF3 (DBG_XMTR, "%s: EnableTransmitCallback: txd %p txdRail %p\n", XMTR_TO_RAIL (txdRail->Generic.XmtrRail)->Generic.Name, txd, txdRail);
++
++    txd->Envelope.Attr = EP_SET_INTERRUPT_ENABLED(txd->Envelope.Attr);
++              
++    elan3_sdram_writel (dev, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, EnveEvent.ev_Type), EV_TYPE_BCOPY);
++      
++    if (EP_IS_RPC(txd->Envelope.Attr))
++    {
++      elan3_sdram_writel (dev, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent.ev_Type), EV_TYPE_BCOPY);
++      elan3_sdram_writel (dev, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DoneEvent.ev_Type),  EV_TYPE_BCOPY | EV_TYPE_EVIRQ | txdRail->DoneCookie.Cookie);
++    }
++    else
++    {
++      elan3_sdram_writel (dev, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent.ev_Type), EV_TYPE_BCOPY | EV_TYPE_EVIRQ | txdRail->DataCookie.Cookie);
++      elan3_sdram_writel (dev, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DoneEvent.ev_Type), EV_TYPE_BCOPY);
++    }
++}
++
++static void
++DisableTransmitCallback (EP_TXD *txd, EP3_TXD_RAIL *txdRail)
++{
++    ELAN3_DEV *dev = XMTR_TO_RAIL(txdRail->Generic.XmtrRail)->Device;
++
++    EPRINTF3 (DBG_XMTR, "%s: DisableTransmitCallback: txd %p txdRail %p\n", XMTR_TO_RAIL (txdRail->Generic.XmtrRail)->Generic.Name, txd, txdRail);
++
++    txd->Envelope.Attr = EP_CLEAR_INTERRUPT_ENABLED(txd->Envelope.Attr);
++
++    elan3_sdram_writel (dev, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, EnveEvent.ev_Type), EV_TYPE_BCOPY);
++    elan3_sdram_writel (dev, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent.ev_Type), EV_TYPE_BCOPY);
++    elan3_sdram_writel (dev, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DoneEvent.ev_Type), EV_TYPE_BCOPY);
++}
++
++static void
++InitialiseTxdRail (EP_TXD *txd, EP3_TXD_RAIL *txdRail, int phase)
++{
++    EP3_XMTR_RAIL *xmtrRail = (EP3_XMTR_RAIL *) txdRail->Generic.XmtrRail;
++    EP3_RAIL      *rail     = XMTR_TO_RAIL (xmtrRail);
++
++    /* Flush the Elan TLB if mappings have changed */
++    ep_perrail_dvma_sync (&rail->Generic);
++
++    /* Initialise the per-rail fields in the envelope */
++    txd->Envelope.TxdRail = txdRail->TxdElanAddr;
++    txd->Envelope.NodeId  = rail->Generic.Position.pos_nodeid;
++
++    /* Initialise the dma backoff */
++    txdRail->Backoff.type = EP_BACKOFF_FREE;
++
++    /* Initialise the per-rail events */
++    switch (phase)
++    {
++    case EP_TXD_PHASE_ACTIVE:
++      elan3_sdram_writel (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, EnveEvent.ev_Count), 1);
++      elan3_sdram_writel (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent.ev_Count), 
++                          (txd->Envelope.nFrags ? txd->Envelope.nFrags : 1) + (EP_IS_MULTICAST(txd->Envelope.Attr) ? 1 : 0));
++      
++      txdRail->TxdMain->EnveEvent = EP3_EVENT_ACTIVE;
++      txdRail->TxdMain->DataEvent = EP3_EVENT_ACTIVE;
++      break;
++      
++    case EP_TXD_PHASE_PASSIVE:
++      ASSERT (EP_IS_RPC(txd->Envelope.Attr));
++
++      elan3_sdram_writel (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, EnveEvent.ev_Count), 0);
++      elan3_sdram_writel (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent.ev_Count), 0);
++
++      txdRail->TxdMain->EnveEvent = txdRail->EnveCookie.Cookie;
++      txdRail->TxdMain->DataEvent = txdRail->DataCookie.Cookie;
++      break;
++    }
++
++    if (! EP_IS_RPC(txd->Envelope.Attr))
++      txdRail->TxdMain->DoneEvent = txdRail->DoneCookie.Cookie;
++    else
++    {
++      elan3_sdram_writel (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DoneEvent.ev_Count), 1);
++      txdRail->TxdMain->DoneEvent = EP3_EVENT_ACTIVE;
++    }
++
++    if (EP_IS_NO_INTERRUPT(txd->Envelope.Attr))
++      DisableTransmitCallback (txd, txdRail);
++    else
++      EnableTransmitCallback (txd, txdRail);
++
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++    if ( epdebug_check_sum ) 
++      txd->Envelope.CheckSum = ep_calc_check_sum( txd->Xmtr->Subsys->Subsys.Sys, &txd->Envelope, txd->Envelope.Frags, txd->Envelope.nFrags);
++    else
++#endif
++      txd->Envelope.CheckSum = 0;  
++
++    /* copy the envelope and payload if present down to sdram */
++    elan3_sdram_copyl_to_sdram (rail->Device, &txd->Envelope, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, Envelope), EP_ENVELOPE_SIZE);
++    
++    if (EP_HAS_PAYLOAD(txd->Envelope.Attr))
++      elan3_sdram_copyl_to_sdram (rail->Device, &txd->Payload, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, Payload), EP_PAYLOAD_SIZE);
++}
++
++void
++ep3xmtr_flush_callback (EP_XMTR *xmtr, EP3_XMTR_RAIL *xmtrRail)
++{
++    EP3_RAIL *rail = XMTR_TO_RAIL (xmtrRail);
++    struct list_head *el;
++    unsigned long flags;
++
++    switch (rail->Generic.CallbackStep)
++    {
++    case EP_CB_FLUSH_FILTERING:
++      /* only need to acquire/release the Lock to ensure that
++       * the node state transition has been noticed. */
++      spin_lock_irqsave (&xmtr->Lock, flags);
++      spin_unlock_irqrestore (&xmtr->Lock, flags);
++      break;
++
++    case EP_CB_FLUSH_FLUSHING:
++      spin_lock_irqsave (&xmtr->Lock, flags);
++      
++      list_for_each (el, &xmtr->ActiveDescList) {
++          EP_TXD       *txd      = list_entry (el, EP_TXD, Link);
++          EP3_TXD_RAIL *txdRail  = (EP3_TXD_RAIL *) txd->TxdRail;
++          EP_NODE_RAIL *nodeRail = &rail->Generic.Nodes[txd->NodeId];
++          
++          if (!TXD_BOUND2RAIL(txdRail, xmtrRail) || nodeRail->State != EP_NODE_LOCAL_PASSIVATE)
++              continue;
++          
++          if (EP_IS_RPC(txd->Envelope.Attr))
++          {
++              if (! EP3_EVENT_FIRED (txdRail->DataCookie, txdRail->TxdMain->DataEvent))
++                  nodeRail->MessageState |= EP_NODE_ACTIVE_MESSAGES;
++              else if (! EP3_EVENT_FIRED (txdRail->DoneCookie, txdRail->TxdMain->DoneEvent))
++                  nodeRail->MessageState |= EP_NODE_PASSIVE_MESSAGES;
++          }
++          else
++          {
++              if (! EP3_EVENT_FIRED (txdRail->DataCookie, txdRail->TxdMain->DataEvent))
++                  nodeRail->MessageState |= EP_NODE_ACTIVE_MESSAGES;
++          }
++      }
++      spin_unlock_irqrestore (&xmtr->Lock, flags);
++      break;
++
++    default:
++      panic ("ep3xmtr_flush_callback: invalid callback step\n");
++      break;
++    }
++}
++
++void
++ep3xmtr_failover_callback (EP_XMTR *xmtr, EP3_XMTR_RAIL *xmtrRail)
++{
++    EP3_RAIL         *rail   = XMTR_TO_RAIL (xmtrRail);
++    struct list_head  txdList;
++    struct list_head *el, *nel;
++    unsigned long flags;
++#ifdef SUPPORT_RAIL_FAILOVER
++    EP_COMMS_SUBSYS  *subsys = xmtr->Subsys;
++#endif
++
++    INIT_LIST_HEAD (&txdList);
++
++    spin_lock_irqsave (&xmtr->Lock, flags);
++    list_for_each_safe (el, nel, &xmtr->ActiveDescList) {
++      EP_TXD       *txd       = list_entry (el, EP_TXD, Link);
++      EP3_TXD_RAIL *txdRail   = (EP3_TXD_RAIL *) txd->TxdRail;
++      EP_NODE_RAIL *nodeRail = &rail->Generic.Nodes[txd->NodeId];
++          
++      /* Only progress relocation of txd's bound to this rail */
++      if (!TXD_BOUND2RAIL(txdRail, xmtrRail) || nodeRail->State != EP_NODE_PASSIVATED)
++          continue;
++      
++#ifdef SUPPORT_RAIL_FAILOVER
++      /* Transmit data not been sent, so just restart on different rail */
++      if (! EP3_EVENT_FIRED (txdRail->DataCookie, txdRail->TxdMain->DataEvent))
++      {
++          EPRINTF4 (DBG_XMTR, "%s: ep3xmtr_failover_callback - xmtr %p txd %p node %d unbind an retry\n", rail->Generic.Name, xmtr, txd, txd->NodeId);
++          
++          UnbindTxdFromRail (txd, txdRail);
++          
++          /* clear the done flags - so that it will be ignored if an event interrupt is generated */
++          txdRail->TxdMain->EnveEvent = EP3_EVENT_PRIVATE;
++          txdRail->TxdMain->DataEvent = EP3_EVENT_PRIVATE;
++          txdRail->TxdMain->DoneEvent = EP3_EVENT_PRIVATE;
++
++          /* reset all events, since non of them could have been set */
++          elan3_sdram_writel (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, EnveEvent.ev_Count), 0);                          /* PCI write */
++          elan3_sdram_writel (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent.ev_Count), 0);                          /* PCI write */
++          elan3_sdram_writel (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DoneEvent.ev_Count), 0);                          /* PCI write */
++          
++          FreeTxdRail (xmtrRail, txdRail);
++          
++          /* epcomms thread will restart on different rail */
++          ep_kthread_schedule (&subsys->Thread, lbolt);
++          continue;
++      }
++
++      if (EP_IS_RPC(txd->Envelope.Attr) && !EP3_EVENT_FIRED (txdRail->DoneCookie, txdRail->TxdMain->DoneEvent))
++      {
++          if (EP_IS_NO_FAILOVER(txd->Envelope.Attr))
++          {
++              EPRINTF4 (DBG_XMTR, "%s: ep3xmtr_failover_callback - xmtr %p txd %p node %d - not able to failover\n",
++                        rail->Generic.Name, xmtr, txd, txd->NodeId);
++
++              list_del (&txd->Link);
++              UnbindTxdFromRail (txd, txdRail);
++              
++              /* clear the done flags - so that it will be ignored if an event interrupt is generated */
++              txdRail->TxdMain->EnveEvent = EP3_EVENT_PRIVATE;
++              txdRail->TxdMain->DataEvent = EP3_EVENT_PRIVATE;
++              txdRail->TxdMain->DoneEvent = EP3_EVENT_PRIVATE;
++              
++              /* envelope and data events must have been set, so only clear the done event */
++              EP_ASSERT (&rail->Generic, SDRAM_ASSERT(elan3_sdram_readl (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, EnveEvent.ev_Count)) == 0));
++              EP_ASSERT (&rail->Generic, SDRAM_ASSERT(elan3_sdram_readl (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent.ev_Count)) == 0));
++
++              elan3_sdram_writel (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DoneEvent.ev_Count), 0);                              /* PCI write */
++              
++              FreeTxdRail (xmtrRail, txdRail);
++          
++              list_add_tail (&txd->Link, &txdList);
++              continue;
++          }
++          EPRINTF4 (DBG_XMTR, "%s: ep3xmtr_failover_callback - xmtr %p txd %p node %d passive\n", rail->Generic.Name, xmtr, txd, txd->NodeId);
++          
++          nodeRail->MessageState |= EP_NODE_PASSIVE_MESSAGES;
++          continue;
++      }
++
++      EPRINTF4 (DBG_XMTR, "%s: ep3xmtr_failover_callback - xmtr %p txd %p node %d completed\n", rail->Generic.Name, xmtr, txd, txd->NodeId);
++#endif
++
++    }
++    spin_unlock_irqrestore (&xmtr->Lock, flags);
++
++    while (! list_empty (&txdList)) 
++    {
++      EP_TXD *txd = list_entry (txdList.next, EP_TXD, Link);
++
++      list_del (&txd->Link);
++
++      txd->Handler (txd, txd->Arg, EP_CONN_RESET);
++      
++      FreeTxd (xmtr, txd);
++    }
++}
++
++
++void
++ep3xmtr_disconnect_callback (EP_XMTR *xmtr, EP3_XMTR_RAIL *xmtrRail)
++{
++    EP3_RAIL         *rail = XMTR_TO_RAIL (xmtrRail);
++    struct list_head *el, *nel;
++    struct list_head  txdList;
++    unsigned long flags;
++    
++    INIT_LIST_HEAD (&txdList);
++
++    spin_lock_irqsave (&xmtr->Lock, flags);
++
++    list_for_each_safe (el, nel, &xmtr->ActiveDescList) {
++      EP_TXD       *txd       = list_entry (el, EP_TXD, Link);
++      EP3_TXD_RAIL *txdRail   = (EP3_TXD_RAIL *) txd->TxdRail;
++      EP_NODE_RAIL *nodeRail = &rail->Generic.Nodes[txd->NodeId];
++          
++      if (!TXD_BOUND2RAIL(txdRail, xmtrRail) || nodeRail->State != EP_NODE_DISCONNECTING)
++          continue;
++      
++      if (EP3_EVENT_FIRED (txdRail->EnveCookie, txdRail->TxdMain->EnveEvent) &&
++          EP3_EVENT_FIRED (txdRail->DataCookie, txdRail->TxdMain->DataEvent) &&
++          EP3_EVENT_FIRED (txdRail->DoneCookie, txdRail->TxdMain->DoneEvent))
++      {
++          EPRINTF4 (DBG_XMTR, "%s: ep3xmtr_disconnect_callback - xmtr %p txd %p completed to node %d\n", rail->Generic.Name, xmtr, txd, txd->NodeId);
++          continue;
++      }
++
++      /* Remove from active list */
++      list_del (&txd->Link);
++      
++      UnbindTxdFromRail (txd, txdRail);
++      
++      /* clear the done flags - so that it will be ignored if an event interrupt is generated */
++      txdRail->TxdMain->EnveEvent = EP3_EVENT_PRIVATE;
++      txdRail->TxdMain->DataEvent = EP3_EVENT_PRIVATE;
++      txdRail->TxdMain->DoneEvent = EP3_EVENT_PRIVATE;
++      
++      /* reset the envelope and data events, since only they could have been set */
++      elan3_sdram_writel (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, EnveEvent.ev_Count), 0);                              /* PCI write */
++      elan3_sdram_writel (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent.ev_Count), 0);                              /* PCI write */
++      elan3_sdram_writel (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DoneEvent.ev_Count), 0);                              /* PCI write */
++      
++      FreeTxdRail (xmtrRail, txdRail);
++          
++      EPRINTF4 (DBG_XMTR, "%s: ep3xmtr_disconnect_callback - xmtr %p txd %p node %d not conected\n", rail->Generic.Name, xmtr, txd, txd->NodeId);
++
++      /* add to the list of txd's which are to be completed */
++      list_add_tail (&txd->Link, &txdList);
++    }
++    spin_unlock_irqrestore (&xmtr->Lock, flags);
++    
++    while (! list_empty (&txdList)) 
++    {
++      EP_TXD *txd = list_entry (txdList.next, EP_TXD, Link);
++
++      list_del (&txd->Link);
++
++      txd->Handler (txd, txd->Arg, EP_CONN_RESET);
++      
++      FreeTxd (xmtr, txd);
++    }
++}
++
++int
++ep3xmtr_poll_txd (EP_XMTR_RAIL *x, EP_TXD_RAIL *t, int how)
++{
++    EP3_XMTR_RAIL *xmtrRail = (EP3_XMTR_RAIL *) x;
++    EP3_TXD_RAIL  *txdRail  = (EP3_TXD_RAIL *) t;
++    EP_TXD        *txd      = txdRail->Generic.Txd;
++
++    switch (how)
++    {
++    case ENABLE_TX_CALLBACK:
++      if (EP_IS_NO_INTERRUPT(txd->Envelope.Attr))
++          EnableTransmitCallback (txd, txdRail);
++      break;
++
++    case DISABLE_TX_CALLBACK:
++      if (EP_IS_NO_INTERRUPT(txd->Envelope.Attr))
++          DisableTransmitCallback (txd, txdRail);
++      break;
++    }
++
++    if (EP3_EVENT_FIRED (txdRail->EnveCookie, txdRail->TxdMain->EnveEvent) &&
++      EP3_EVENT_FIRED (txdRail->DataCookie, txdRail->TxdMain->DataEvent) &&
++      EP3_EVENT_FIRED (txdRail->DoneCookie, txdRail->TxdMain->DoneEvent))
++    {
++      EPRINTF3 (DBG_XMTR, "%s: ep3xmtr_poll_txd: txd=%p XID=%llx completed\n", 
++                XMTR_TO_RAIL (xmtrRail)->Generic.Name, txd, (long long) txd->Envelope.Xid.Unique);
++
++      ep_xmtr_txd_stat(xmtrRail->Generic.Xmtr,txd);
++
++      UnbindTxdFromRail (txd, txdRail);
++      
++      /* clear the done flags - so that it will be ignored if an event interrupt is generated */
++      txdRail->TxdMain->EnveEvent = EP3_EVENT_PRIVATE;
++      txdRail->TxdMain->DataEvent = EP3_EVENT_PRIVATE;
++      txdRail->TxdMain->DoneEvent = EP3_EVENT_PRIVATE;
++      
++      FreeTxdRail (xmtrRail, txdRail);
++
++      return 1;
++    }
++
++    return 0;
++}
++
++int
++ep3xmtr_bind_txd (EP_TXD *txd, EP_XMTR_RAIL *x, unsigned int phase)
++{
++    EP3_XMTR_RAIL *xmtrRail = (EP3_XMTR_RAIL *) x;
++    EP3_RAIL      *rail     = XMTR_TO_RAIL (xmtrRail);
++    EP3_TXD_RAIL  *txdRail;
++    E3_DMA_BE      dmabe;
++
++    if ((txdRail = GetTxdRail (xmtrRail)) == NULL)
++      return 0;
++
++    switch (phase)
++    {
++    case EP_TXD_PHASE_ACTIVE:
++      if (rail->Generic.Nodes[txd->NodeId].State != EP_NODE_CONNECTED)
++      {
++          EPRINTF2 (DBG_XMTR, "%s: TransmitTxdOnRail: node %u not connected on this rail\n", rail->Generic.Name, txd->NodeId);
++
++          /* clear the done flags - so that it will be ignored if an event interrupt is generated */
++          txdRail->TxdMain->EnveEvent = EP3_EVENT_PRIVATE;
++          txdRail->TxdMain->DataEvent = EP3_EVENT_PRIVATE;
++          txdRail->TxdMain->DoneEvent = EP3_EVENT_PRIVATE;
++
++          /* reset all events, since non of them could have been set */
++          elan3_sdram_writel (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, EnveEvent.ev_Count), 0);                          /* PCI write */
++          elan3_sdram_writel (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent.ev_Count), 0);                          /* PCI write */
++          elan3_sdram_writel (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DoneEvent.ev_Count), 0);                          /* PCI write */
++
++          FreeTxdRail (xmtrRail, txdRail);
++          return 0;
++      }
++
++      InitialiseTxdRail (txd, txdRail, phase);
++
++      /* Initialise the dma descriptor */
++      dmabe.s.dma_type            = E3_DMA_TYPE (DMA_BYTE, DMA_WRITE, DMA_QUEUED, EP3_DMAFAILCOUNT);
++      dmabe.s.dma_size            = (EP_HAS_PAYLOAD(txd->Envelope.Attr) ? EP_INPUTQ_SIZE : EP_ENVELOPE_SIZE);
++      dmabe.s.dma_source          = txdRail->TxdElanAddr + offsetof (EP3_TXD_RAIL_ELAN, Envelope);
++      dmabe.s.dma_dest            = (E3_Addr) 0;
++      dmabe.s.dma_destEvent       = EP_MSGQ_ADDR(txd->Service);
++      dmabe.s.dma_destCookieVProc = EP_VP_DATA (txd->NodeId);
++      dmabe.s.dma_srcEvent        = txdRail->TxdElanAddr + offsetof (EP3_TXD_RAIL_ELAN, EnveEvent);
++      dmabe.s.dma_srcCookieVProc  = LocalCookie (rail, txd->NodeId);
++
++      EPRINTF8 (DBG_XMTR, "%s: TransmitTxdOnRail: txd=%p txdRail=%p @ %x XID=%llx dest=%u srcEvent=%x srcCookie=%x\n", rail->Generic.Name, 
++                txd, txdRail, txdRail->TxdElanAddr, (long long) txd->Envelope.Xid.Unique, txd->NodeId, dmabe.s.dma_srcEvent, dmabe.s.dma_srcCookieVProc);
++      
++      BindTxdToRail (txd, txdRail);
++      
++      if (IssueDma (rail, &dmabe, EP_RETRY_LOW_PRI, FALSE) != ISSUE_COMMAND_OK)
++          QueueDmaForRetry (rail, &dmabe, EP_RETRY_LOW_PRI);
++      break;
++
++    case EP_TXD_PHASE_PASSIVE:
++      InitialiseTxdRail (txd, txdRail, EP_TXD_PHASE_PASSIVE);                         /* initialise as passive (updated envelope) */
++      
++      EP_XMTR_OP (txd->TxdRail->XmtrRail, UnbindTxd) (txd, EP_TXD_PHASE_PASSIVE);     /* unbind from existing rail */
++
++      BindTxdToRail (txd, txdRail);                                                   /* and bind it to our new rail */
++      break;
++    }
++
++    return 1;
++}
++
++void
++ep3xmtr_unbind_txd (EP_TXD *txd, unsigned int phase)
++{
++    EP3_TXD_RAIL  *txdRail  = (EP3_TXD_RAIL *) txd->TxdRail;
++    EP3_XMTR_RAIL *xmtrRail = (EP3_XMTR_RAIL *) txdRail->Generic.XmtrRail;
++    EP3_RAIL      *rail     = XMTR_TO_RAIL (xmtrRail);
++
++    /* XXXX - TBD assertions on phase */
++
++    UnbindTxdFromRail (txd, txdRail);
++    
++    /* clear the done flags - so that it will be ignored if an event interrupt is generated */
++    txdRail->TxdMain->EnveEvent = EP3_EVENT_PRIVATE;
++    txdRail->TxdMain->DataEvent = EP3_EVENT_PRIVATE;
++    txdRail->TxdMain->DoneEvent = EP3_EVENT_PRIVATE;
++    
++    /* reset the envelope and data events, since only they could have been set */
++    elan3_sdram_writel (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, EnveEvent.ev_Count), 0);                                /* PCI write */
++    elan3_sdram_writel (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent.ev_Count), 0);                                /* PCI write */
++    elan3_sdram_writel (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DoneEvent.ev_Count), 0);                                /* PCI write */         
++    
++    FreeTxdRail (xmtrRail, txdRail);
++}
++
++long
++ep3xmtr_check (EP_XMTR_RAIL *x, long nextRunTime)
++{
++    EP3_XMTR_RAIL *xmtrRail = (EP3_XMTR_RAIL *) x;
++
++    if (xmtrRail->FreeDescCount < ep_txd_lowat && !AllocateTxdRailBlock(xmtrRail))
++    {
++      EPRINTF1 (DBG_RCVR,"%s: failed to grow txd rail pool\n", XMTR_TO_RAIL(xmtrRail)->Generic.Name);
++              
++      if (nextRunTime == 0 || AFTER (nextRunTime, lbolt + RESOURCE_RETRY_TIME))
++          nextRunTime = lbolt + RESOURCE_RETRY_TIME;
++    }
++    
++    return nextRunTime;
++}
++
++void
++ep3xmtr_add_rail (EP_XMTR *xmtr, EP_COMMS_RAIL *commsRail)
++{
++    EP3_XMTR_RAIL *xmtrRail;
++    unsigned long  flags;
++
++    KMEM_ZALLOC (xmtrRail, EP3_XMTR_RAIL *, sizeof (EP3_XMTR_RAIL), 1);
++
++    spin_lock_init (&xmtrRail->FreeDescLock);
++    kcondvar_init  (&xmtrRail->FreeDescSleep);
++    INIT_LIST_HEAD (&xmtrRail->FreeDescList);
++    INIT_LIST_HEAD (&xmtrRail->DescBlockList);
++
++    xmtrRail->Generic.CommsRail = commsRail;
++    xmtrRail->Generic.Xmtr      = xmtr;
++
++    spin_lock_irqsave (&xmtr->Lock, flags);
++
++    xmtr->Rails[commsRail->Rail->Number] = &xmtrRail->Generic;
++    xmtr->RailMask |= EP_RAIL2RAILMASK(commsRail->Rail->Number);
++
++    spin_unlock_irqrestore (&xmtr->Lock, flags);
++}
++
++void
++ep3xmtr_del_rail (EP_XMTR *xmtr, EP_COMMS_RAIL *commsRail)
++{
++    EP3_RAIL         *rail     = (EP3_RAIL *) commsRail->Rail;
++    EP3_XMTR_RAIL    *xmtrRail = (EP3_XMTR_RAIL *) xmtr->Rails[commsRail->Rail->Number];
++    unsigned long     flags;
++
++    /* rail mask set as not usable */
++    spin_lock_irqsave (&xmtr->Lock, flags);
++    xmtr->RailMask &= ~EP_RAIL2RAILMASK (rail->Generic.Number);
++    spin_unlock_irqrestore (&xmtr->Lock, flags);
++
++    /* wait for all txd's for this rail to become free */
++    spin_lock_irqsave (&xmtrRail->FreeDescLock, flags);
++    while (xmtrRail->FreeDescCount != xmtrRail->TotalDescCount)
++    {
++      xmtrRail->FreeDescWaiting++;
++      kcondvar_wait (&xmtrRail->FreeDescSleep, &xmtrRail->FreeDescLock, &flags);
++    }
++    spin_unlock_irqrestore (&xmtrRail->FreeDescLock, flags);
++
++    spin_lock_irqsave (&xmtr->Lock, flags);
++    xmtr->Rails[commsRail->Rail->Number] = NULL;
++    spin_unlock_irqrestore (&xmtr->Lock, flags);
++
++    /* need to free up the txd's and blocks */
++    /* all the txd's accociated with DescBlocks must be in the FreeDescList */
++    ASSERT (xmtrRail->TotalDescCount == xmtrRail->FreeDescCount);
++
++    /* run through the DescBlockList deleting them */
++    while (!list_empty (&xmtrRail->DescBlockList))
++      FreeTxdRailBlock (xmtrRail, list_entry(xmtrRail->DescBlockList.next, EP3_TXD_RAIL_BLOCK , Link));
++    
++    /* it had better be empty after that */
++    ASSERT ((xmtrRail->FreeDescCount == 0) && (xmtrRail->TotalDescCount == 0));
++
++    spin_lock_destroy (&xmtrRail->FreeDescLock);
++    kcondvar_destroy (&xmtrRail->FreeDescSleep);
++
++    KMEM_FREE (xmtrRail, sizeof (EP3_XMTR_RAIL));
++}
++
++void
++ep3xmtr_display_xmtr (DisplayInfo *di, EP_XMTR_RAIL *x)
++{
++    EP3_XMTR_RAIL *xmtrRail = (EP3_XMTR_RAIL *) x;
++    EP3_RAIL      *rail     = XMTR_TO_RAIL (xmtrRail);
++    struct list_head *el;
++    unsigned long flags;
++    int freeCount = 0;
++
++    spin_lock_irqsave (&xmtrRail->FreeDescLock, flags);
++    list_for_each (el, &xmtrRail->FreeDescList)
++      freeCount++;
++    spin_unlock_irqrestore (&xmtrRail->FreeDescLock, flags);
++
++    (di->func)(di->arg, "                 Rail=%d Free=%d Total=%d (%d)\n",
++              rail->Generic.Number, xmtrRail->FreeDescCount, xmtrRail->TotalDescCount, freeCount);
++}
++
++void
++ep3xmtr_display_txd (DisplayInfo *di, EP_TXD_RAIL *t)
++{
++    EP3_TXD_RAIL      *txdRail   = (EP3_TXD_RAIL *) t;
++    EP3_XMTR_RAIL     *xmtrRail  = (EP3_XMTR_RAIL *) txdRail->Generic.XmtrRail;
++    EP3_TXD_RAIL_MAIN *txdMain   = txdRail->TxdMain;
++    sdramaddr_t        txdElan   = txdRail->TxdElan;
++    EP3_RAIL          *rail      = (EP3_RAIL *) xmtrRail->Generic.CommsRail->Rail;
++    ELAN3_DEV         *dev       = rail->Device;
++    
++    (di->func)(di->arg, "     EnveEvent=%x DataEvent=%x DoneEvent=%x Rail=%s\n", 
++             txdMain->EnveEvent, txdMain->DataEvent, txdMain->DoneEvent, rail->Generic.Name);
++    (di->func)(di->arg, "     EnveEvent=%x.%x DataEvent=%x.%x DoneEvent=%x.%x\n",
++             elan3_sdram_readl (dev, txdElan + offsetof (EP3_TXD_RAIL_ELAN, EnveEvent.ev_Count)),
++             elan3_sdram_readl (dev, txdElan + offsetof (EP3_TXD_RAIL_ELAN, EnveEvent.ev_Type)),
++             elan3_sdram_readl (dev, txdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent.ev_Count)),
++             elan3_sdram_readl (dev, txdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent.ev_Type)),
++             elan3_sdram_readl (dev, txdElan + offsetof (EP3_TXD_RAIL_ELAN, DoneEvent.ev_Count)),
++             elan3_sdram_readl (dev, txdElan + offsetof (EP3_TXD_RAIL_ELAN, DoneEvent.ev_Type)));
++}
++
++int
++ep3xmtr_check_txd_state (EP_TXD *txd)
++{
++    EP3_TXD_RAIL  *txdRail   = (EP3_TXD_RAIL *) txd->TxdRail;
++    EP3_XMTR_RAIL *xmtrRail  = (EP3_XMTR_RAIL *) txdRail->Generic.XmtrRail;
++    EP3_RAIL      *rail      = XMTR_TO_RAIL (xmtrRail);
++    E3_Addr        enveEvent = txdRail->TxdElanAddr + offsetof (EP3_TXD_RAIL_ELAN, EnveEvent);
++    EP3_RETRY_DMA *retry = NULL;
++
++    struct list_head *el;
++    struct list_head *nel;
++    unsigned long     flags;
++
++    /*  is enevelope event is really not set */
++    if (EP3_EVENT_FIRED (txdRail->EnveCookie, txdRail->TxdMain->EnveEvent )) 
++      return (0);
++    
++    /* remove matching dma from stalled list */           
++    spin_lock_irqsave (&rail->DmaRetryLock, flags);
++    
++    list_for_each_safe(el, nel,  &rail->DmaRetries[EP_RETRY_STABALISING]) {
++      retry = list_entry (el, EP3_RETRY_DMA, Link);
++      
++      if ( retry->Dma.s.dma_srcEvent == enveEvent ) {
++          /* remove from retry list */
++          list_del (&retry->Link);
++          break; /* there can only be one */
++      } 
++    }
++    ASSERT ( retry != NULL); /* must find one in list */
++    ASSERT ( retry->Dma.s.dma_srcEvent == enveEvent ); /* better still be the right type then */    
++
++    /* add to free list */
++    list_add (&retry->Link, &rail->DmaRetryFreeList);
++
++    spin_unlock_irqrestore (&rail->DmaRetryLock, flags);    
++                      
++    UnbindTxdFromRail (txd, txdRail);
++      
++    /* clear the done flags - so that it will be ignored if an event interrupt is generated */
++    txdRail->TxdMain->EnveEvent = EP3_EVENT_PRIVATE;
++    txdRail->TxdMain->DataEvent = EP3_EVENT_PRIVATE;
++    txdRail->TxdMain->DoneEvent = EP3_EVENT_PRIVATE;
++    
++    /* reset the envelope and data events, since only they could have been set */
++    elan3_sdram_writel (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, EnveEvent.ev_Count), 0);                                /* PCI write */
++    elan3_sdram_writel (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DataEvent.ev_Count), 0);                                /* PCI write */
++    elan3_sdram_writel (rail->Device, txdRail->TxdElan + offsetof (EP3_TXD_RAIL_ELAN, DoneEvent.ev_Count), 0);                                /* PCI write */         
++    
++    FreeTxdRail (xmtrRail, txdRail);
++
++    return (1);
++}
++
++void
++ep3xmtr_fillout_rail_stats(EP_XMTR_RAIL *xmtr_rail, char *str) {
++    /* no stats here yet */
++    /* EP3_XMTR_RAIL * ep3xmtr_rail = (EP3_XMTR_RAIL *) xmtr_rail; */
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/ep/epcommsTx_elan4.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/epcommsTx_elan4.c    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/epcommsTx_elan4.c 2005-05-11 12:10:12.506922240 -0400
+@@ -0,0 +1,1389 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: epcommsTx_elan4.c,v 1.26.2.4 2004/11/12 10:54:51 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/epcommsTx_elan4.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++#include <elan/epsvc.h>
++#include <elan/epcomms.h>
++
++#include "debug.h"
++#include "kcomm_vp.h"
++#include "kcomm_elan4.h"
++#include "epcomms_elan4.h"
++
++#include <elan4/trtype.h>
++
++#define XMTR_TO_COMMS(xmtrRail)               ((EP4_COMMS_RAIL *) ((EP_XMTR_RAIL *) xmtrRail)->CommsRail)
++#define XMTR_TO_RAIL(xmtrRail)                ((EP4_RAIL *) ((EP_XMTR_RAIL *) xmtrRail)->CommsRail->Rail)
++#define XMTR_TO_DEV(xmtrRail)         (XMTR_TO_RAIL(xmtrRail)->r_ctxt.ctxt_dev)
++#define XMTR_TO_SUBSYS(xmtrRail)      (((EP_XMTR_RAIL *) xmtrRail)->Xmtr->Subsys)
++
++#define TXD_TO_XMTR(txdRail)          ((EP4_XMTR_RAIL *) txdRail->txd_generic.XmtrRail)
++#define TXD_TO_RAIL(txdRail)          XMTR_TO_RAIL(TXD_TO_XMTR(txdRail))
++
++static void txd_interrupt (EP4_RAIL *rail, void *arg);
++static void poll_interrupt (EP4_RAIL *rail, void *arg);
++
++static __inline__ int
++on_list (struct list_head *ent, struct list_head *list)
++{
++    struct list_head *el;
++    unsigned int count = 0;
++    list_for_each (el, list) {
++      if (el == ent)
++          count++;
++    }
++    return count;
++}
++
++static __inline__ void
++__ep4_txd_assert_free (EP4_TXD_RAIL *txdRail, const char *file, const int line)
++{
++    EP4_XMTR_RAIL *xmtrRail = TXD_TO_XMTR (txdRail);
++    ELAN4_DEV     *dev      = XMTR_TO_DEV (xmtrRail);
++    register int   failed   = 0;
++    
++    if ((txdRail)->txd_retry_time     != 0)              failed |= (1 << 0);
++    if ((txdRail)->txd_main->txd_env  != EP4_STATE_FREE) failed |= (1 << 1);
++    if ((txdRail)->txd_main->txd_data != EP4_STATE_FREE) failed |= (1 << 2);
++    if ((txdRail)->txd_main->txd_done != EP4_STATE_FREE) failed |= (1 << 3);
++
++    if (sdram_assert)
++    {
++      if ((int)(elan4_sdram_readq (dev, (txdRail)->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_env.ev_CountAndType)) >> 32)  != -32) failed |= (1 << 4);
++      if ((int)(elan4_sdram_readq (dev, (txdRail)->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_data.ev_CountAndType)) >> 32) != 0)   failed |= (1 << 5);
++      if ((int)(elan4_sdram_readq (dev, (txdRail)->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_done.ev_CountAndType)) >> 32) != 0)   failed |= (1 << 6);
++    }
++
++    if (failed)
++    {
++      printk ("__ep4_txd_assert_free: failed=%x txdRail=%p at %s:%d\n", failed, txdRail, file, line);
++
++      ep_debugf (DBG_DEBUG, "__ep4_txd_assert_free: failed=%x txdRail=%p at %s:%d\n", failed, txdRail, file, line);
++      ep4xmtr_display_txd (&di_ep_debug, &txdRail->txd_generic);
++
++      (txdRail)->txd_retry_time     = 0;
++      (txdRail)->txd_main->txd_env  = EP4_STATE_FREE;
++      (txdRail)->txd_main->txd_data = EP4_STATE_FREE;
++      (txdRail)->txd_main->txd_done = EP4_STATE_FREE;
++
++      if (sdram_assert)
++      {
++          elan4_sdram_writel (dev, (txdRail)->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_env.ev_CountAndType)  + 4, -32);
++          elan4_sdram_writel (dev, (txdRail)->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_data.ev_CountAndType) + 4, 0);
++          elan4_sdram_writel (dev, (txdRail)->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_done.ev_CountAndType) + 4, 0);
++      }
++      EP_ASSFAIL (XMTR_TO_RAIL(xmtrRail), "__ep4_txd_assert_free");
++    }
++}
++
++static __inline__ void
++__ep4_txd_assert_finished (EP4_TXD_RAIL *txdRail, const char *file, const int line)
++{
++    EP4_XMTR_RAIL *xmtrRail = TXD_TO_XMTR (txdRail);
++    ELAN4_DEV     *dev      = XMTR_TO_DEV (xmtrRail);
++    register int   failed   = 0;
++    
++    if ((txdRail)->txd_retry_time     != 0)                  failed |= (1 << 0);
++    if ((txdRail)->txd_main->txd_env  != EP4_STATE_FINISHED) failed |= (1 << 1);
++    if ((txdRail)->txd_main->txd_data != EP4_STATE_FINISHED) failed |= (1 << 2);
++    if ((txdRail)->txd_main->txd_done != EP4_STATE_FINISHED) failed |= (1 << 3);
++    
++    if (sdram_assert)
++    {
++      if ((int)(elan4_sdram_readq (dev, (txdRail)->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_env.ev_CountAndType)) >> 32)  != -32) failed |= (1 << 4);
++      if ((int)(elan4_sdram_readq (dev, (txdRail)->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_data.ev_CountAndType)) >> 32) != 0)   failed |= (1 << 5);
++      if ((int)(elan4_sdram_readq (dev, (txdRail)->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_done.ev_CountAndType)) >> 32) != 0)   failed |= (1 << 6);
++    }
++
++    if (failed)
++    {
++      printk ("__ep4_txd_assert_finished: failed=%x txdRail=%p at %s:%d\n", failed, txdRail, file, line);
++
++      ep_debugf (DBG_DEBUG, "__ep4_txd_assert_finished: failed=%x txdRail=%p at %s:%d\n", failed, txdRail, file, line);
++      ep4xmtr_display_txd (&di_ep_debug, &txdRail->txd_generic);
++
++      (txdRail)->txd_retry_time     = 0;
++      (txdRail)->txd_main->txd_env  = EP4_STATE_FINISHED;
++      (txdRail)->txd_main->txd_data = EP4_STATE_FINISHED;
++      (txdRail)->txd_main->txd_done = EP4_STATE_FINISHED;
++
++      if (sdram_assert)
++      {
++          elan4_sdram_writel (dev, (txdRail)->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_env.ev_CountAndType)  + 4, -32);
++          elan4_sdram_writel (dev, (txdRail)->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_data.ev_CountAndType) + 4, 0);
++          elan4_sdram_writel (dev, (txdRail)->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_done.ev_CountAndType) + 4, 0);
++      }
++      EP_ASSFAIL (XMTR_TO_RAIL(xmtrRail), "__ep4_txd_assert_finished");
++    }
++}
++
++static __inline__ int
++__ep4_txd_assfail (EP4_TXD_RAIL *txdRail, const char *expr, const char *file, const int line)
++{
++    EP4_XMTR_RAIL *xmtrRail = TXD_TO_XMTR (txdRail);
++
++    printk ("__ep4_txd_assfail: %s:%d '%s'\n", file, line, expr);
++
++    ep_debugf (DBG_DEBUG, "__ep4_txd_assfail: %s:%d '%s'\n", file, line, expr);
++    ep4xmtr_display_txd (&di_ep_debug, &txdRail->txd_generic);
++
++    EP_ASSFAIL (XMTR_TO_RAIL (xmtrRail), "__ep4_txd_assfail");
++
++    return 0;
++}
++
++#define EP4_TXD_ASSERT(txdRail, EX)           ((void) ((EX) || (__ep4_txd_assfail(txdRail, #EX, __FILE__, __LINE__))))
++#define EP4_TXD_ASSERT_FREE(txdRail)          __ep4_txd_assert_free(txdRail, __FILE__, __LINE__)
++#define EP4_TXD_ASSERT_FINISHED(txdRail)      __ep4_txd_assert_finished(txdRail, __FILE__, __LINE__)
++
++static int
++alloc_txd_block (EP4_XMTR_RAIL *xmtrRail)
++{
++    EP4_RAIL           *rail = XMTR_TO_RAIL(xmtrRail);
++    ELAN4_DEV          *dev  = XMTR_TO_DEV(xmtrRail);
++    EP4_TXD_RAIL_BLOCK *blk;
++    EP4_TXD_RAIL_MAIN  *txdMain;
++    EP_ADDR           txdMainAddr;
++    sdramaddr_t               txdElan;
++    EP_ADDR           txdElanAddr;
++    EP4_TXD_RAIL       *txdRail;
++    unsigned long       flags;
++    int                 i;
++
++    KMEM_ZALLOC (blk, EP4_TXD_RAIL_BLOCK *, sizeof (EP4_TXD_RAIL_BLOCK), 1);
++
++    if (blk == NULL)
++      return 0;
++
++    if ((txdElan = ep_alloc_elan (&rail->r_generic, EP4_TXD_RAIL_ELAN_SIZE * EP4_NUM_TXD_PER_BLOCK, 0, &txdElanAddr)) == (sdramaddr_t) 0)
++    {
++      KMEM_FREE (blk, sizeof (EP4_TXD_RAIL_BLOCK));
++      return 0;
++    }
++
++    if ((txdMain = ep_alloc_main (&rail->r_generic, EP4_TXD_RAIL_MAIN_SIZE * EP4_NUM_TXD_PER_BLOCK, 0, &txdMainAddr)) == (EP4_TXD_RAIL_MAIN *) NULL)
++    {
++      ep_free_elan (&rail->r_generic, txdElanAddr, EP4_TXD_RAIL_ELAN_SIZE * EP4_NUM_TXD_PER_BLOCK);
++      KMEM_FREE (blk, sizeof (EP4_TXD_RAIL_BLOCK));
++      return 0;
++    }
++
++    if (ep4_reserve_dma_retries (rail, EP4_NUM_TXD_PER_BLOCK, 0) != 0)
++    {
++      ep_free_main (&rail->r_generic, blk->blk_txds[0].txd_main_addr, EP4_TXD_RAIL_MAIN_SIZE * EP4_NUM_TXD_PER_BLOCK);
++      ep_free_elan (&rail->r_generic, txdElanAddr, EP4_TXD_RAIL_ELAN_SIZE * EP4_NUM_TXD_PER_BLOCK);
++      KMEM_FREE (blk, sizeof (EP4_TXD_RAIL_BLOCK));
++      return 0;
++    }
++
++    for (txdRail = &blk->blk_txds[0], i = 0; i < EP4_NUM_TXD_PER_BLOCK; i++, txdRail++)
++    {
++      txdRail->txd_generic.XmtrRail = &xmtrRail->xmtr_generic;
++      txdRail->txd_elan             = txdElan;
++      txdRail->txd_elan_addr        = txdElanAddr;
++      txdRail->txd_main             = txdMain;
++      txdRail->txd_main_addr        = txdMainAddr;
++
++      /* We only need to reserve space for one command stream, since the sten packet
++       * can only be retrying *before* the dma source event is set.
++       * reserve bytes of "event" cq space for the completion write + interrupt */
++      if ((txdRail->txd_ecq = ep4_get_ecq (rail, EP4_ECQ_EVENT, EP4_INTR_CMD_NDWORDS)) == NULL)
++          goto failed;
++
++      /* register the main interrupt cookies */
++      ep4_register_intcookie (rail, &txdRail->txd_intcookie, txdElanAddr + offsetof (EP4_TXD_RAIL_ELAN, txd_done), txd_interrupt, txdRail);
++
++      /* initialise the events */
++      elan4_sdram_writeq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_env.ev_CountAndType),
++                          E4_EVENT_INIT_VALUE (-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++      elan4_sdram_writeq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_env.ev_CopySource),
++                          txdElanAddr + offsetof (EP4_TXD_RAIL_ELAN, txd_env_cmd));
++      elan4_sdram_writeq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_env.ev_CopyDest),
++                          txdRail->txd_ecq->ecq_addr);
++
++      elan4_sdram_writeq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_data.ev_CountAndType),
++                          E4_EVENT_INIT_VALUE (0, E4_EVENT_WRITE, E4_EVENT_DTYPE_LONG, 0));
++      elan4_sdram_writeq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_data.ev_WritePtr),
++                          txdMainAddr + offsetof (EP4_TXD_RAIL_MAIN, txd_data));
++      elan4_sdram_writeq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_data.ev_WriteValue),
++                          EP4_STATE_FINISHED);
++
++      elan4_sdram_writeq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_done.ev_CountAndType),
++                          E4_EVENT_INIT_VALUE (0, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++      elan4_sdram_writeq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_done.ev_CopySource),
++                          txdElanAddr + offsetof (EP4_TXD_RAIL_ELAN, txd_done_cmd));
++      elan4_sdram_writeq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_done.ev_CopyDest),
++                          txdRail->txd_ecq->ecq_addr);
++
++      /* Initialise the command streams */
++      elan4_sdram_writeq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_env_cmd.c_write_cmd),
++                          WRITE_DWORD_CMD | (txdMainAddr + offsetof (EP4_TXD_RAIL_MAIN, txd_env)));
++      elan4_sdram_writeq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_env_cmd.c_write_value),
++                          EP4_STATE_FAILED);
++      elan4_sdram_writeq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_env_cmd.c_intr_cmd),
++                          INTERRUPT_CMD | (txdRail->txd_intcookie.int_val << E4_MAIN_INT_SHIFT));
++
++      elan4_sdram_writeq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_done_cmd.c_write_cmd),
++                          WRITE_DWORD_CMD | (txdMainAddr + offsetof (EP4_TXD_RAIL_MAIN, txd_done)));
++      elan4_sdram_writeq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_done_cmd.c_write_value),
++                          EP4_STATE_FINISHED);
++      elan4_sdram_writeq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_done_cmd.c_intr_cmd),
++                          INTERRUPT_CMD | (txdRail->txd_intcookie.int_val << E4_MAIN_INT_SHIFT));
++
++      txdMain->txd_env  = EP4_STATE_FREE;
++      txdMain->txd_data = EP4_STATE_FREE;
++      txdMain->txd_done = EP4_STATE_FREE;
++
++      /* move onto next descriptor */
++      txdElan     += EP4_TXD_RAIL_ELAN_SIZE;
++      txdElanAddr += EP4_TXD_RAIL_ELAN_SIZE;
++      txdMain      = (EP4_TXD_RAIL_MAIN *) ((unsigned long) txdMain + EP4_TXD_RAIL_MAIN_SIZE);
++      txdMainAddr += EP4_TXD_RAIL_MAIN_SIZE;
++    }
++
++    spin_lock_irqsave (&xmtrRail->xmtr_freelock, flags);
++
++    list_add  (&blk->blk_link, &xmtrRail->xmtr_blocklist);
++
++    xmtrRail->xmtr_totalcount += EP4_NUM_TXD_PER_BLOCK;
++    xmtrRail->xmtr_freecount  += EP4_NUM_TXD_PER_BLOCK;
++
++    for (i = 0; i < EP4_NUM_TXD_PER_BLOCK; i++)
++      list_add (&blk->blk_txds[i].txd_generic.Link, &xmtrRail->xmtr_freelist);
++
++    spin_unlock_irqrestore (&xmtrRail->xmtr_freelock, flags);
++
++    return 1;
++
++ failed:
++    while (--i >= 0)
++    {
++      ep4_put_ecq (rail, txdRail->txd_ecq, EP4_INTR_CMD_NDWORDS);
++      ep4_deregister_intcookie (rail, &txdRail->txd_intcookie);
++    }
++    ep4_release_dma_retries (rail, EP4_NUM_TXD_PER_BLOCK);
++
++    ep_free_main (&rail->r_generic, blk->blk_txds[0].txd_main_addr, EP4_TXD_RAIL_MAIN_SIZE * EP4_NUM_TXD_PER_BLOCK);
++    ep_free_elan (&rail->r_generic, blk->blk_txds[0].txd_elan_addr, EP4_TXD_RAIL_ELAN_SIZE * EP4_NUM_TXD_PER_BLOCK);
++
++    KMEM_FREE (blk, sizeof (EP4_TXD_RAIL_BLOCK));
++
++    return 0;
++}
++
++static void
++free_txd_block (EP4_XMTR_RAIL *xmtrRail, EP4_TXD_RAIL_BLOCK *blk)
++{
++    EP4_RAIL     *rail = XMTR_TO_RAIL (xmtrRail);
++    EP4_TXD_RAIL *txdRail;
++    unsigned long flags;
++    int           i;
++
++    spin_lock_irqsave (&xmtrRail->xmtr_freelock, flags);
++
++    list_del (&blk->blk_link);
++
++    xmtrRail->xmtr_totalcount -= EP4_NUM_TXD_PER_BLOCK;
++
++    for (txdRail = &blk->blk_txds[0], i = 0; i < EP4_NUM_TXD_PER_BLOCK; i++, txdRail++)
++    {
++      xmtrRail->xmtr_freecount--;
++
++      ep4_put_ecq (rail, txdRail->txd_ecq, EP4_INTR_CMD_NDWORDS);
++
++      ep4_deregister_intcookie (rail, &txdRail->txd_intcookie);
++
++      list_del (&txdRail->txd_generic.Link);
++    }
++    spin_unlock_irqrestore (&xmtrRail->xmtr_freelock, flags);
++
++    ep4_release_dma_retries (rail, EP4_NUM_TXD_PER_BLOCK);
++
++    ep_free_main (&rail->r_generic, blk->blk_txds[0].txd_main_addr, EP4_TXD_RAIL_MAIN_SIZE * EP4_NUM_TXD_PER_BLOCK);
++    ep_free_elan (&rail->r_generic, blk->blk_txds[0].txd_elan_addr, EP4_TXD_RAIL_ELAN_SIZE * EP4_NUM_TXD_PER_BLOCK);
++
++    KMEM_FREE (blk, sizeof (EP4_TXD_RAIL_BLOCK));
++}
++
++static EP4_TXD_RAIL *
++get_txd_rail (EP4_XMTR_RAIL *xmtrRail)
++{
++    EP_COMMS_SUBSYS  *subsys = XMTR_TO_SUBSYS(xmtrRail);
++    EP4_TXD_RAIL     *txdRail;
++    unsigned long flags;
++    int low_on_txds;
++
++    spin_lock_irqsave (&xmtrRail->xmtr_freelock, flags);
++
++    if (list_empty (&xmtrRail->xmtr_freelist))
++      txdRail = NULL;
++    else
++    {
++      txdRail = list_entry (xmtrRail->xmtr_freelist.next, EP4_TXD_RAIL, txd_generic.Link);
++
++      EP4_TXD_ASSERT_FREE(txdRail);
++
++      list_del (&txdRail->txd_generic.Link);
++
++      xmtrRail->xmtr_freecount--;
++    }
++    /* Wakeup the descriptor primer thread if there's not many left */
++    low_on_txds = (xmtrRail->xmtr_freecount < ep_txd_lowat);
++
++    spin_unlock_irqrestore (&xmtrRail->xmtr_freelock, flags);
++
++    if (low_on_txds)
++      ep_kthread_schedule (&subsys->Thread, lbolt);
++
++
++    return (txdRail);
++}
++
++static void
++free_txd_rail (EP4_XMTR_RAIL *xmtrRail, EP4_TXD_RAIL *txdRail)
++{
++    unsigned long flags;
++
++    EP4_TXD_ASSERT_FREE(txdRail);
++
++    spin_lock_irqsave (&xmtrRail->xmtr_freelock, flags);
++    
++    list_add (&txdRail->txd_generic.Link, &xmtrRail->xmtr_freelist);
++
++    xmtrRail->xmtr_freecount++;
++
++    if (xmtrRail->xmtr_freewaiting)
++    {
++      xmtrRail->xmtr_freewaiting--;
++      kcondvar_wakeupall (&xmtrRail->xmtr_freesleep, &xmtrRail->xmtr_freelock);
++    }
++
++    spin_unlock_irqrestore (&xmtrRail->xmtr_freelock, flags);
++}
++
++static void
++bind_txd_rail (EP_TXD *txd, EP4_TXD_RAIL *txdRail)
++{
++    EPRINTF6 (DBG_XMTR, "%s: bind_txd_rail: txd=%p txdRail=%p XID=%08x.%08x.%016llx\n", 
++            XMTR_TO_RAIL(txdRail->txd_generic.XmtrRail)->r_generic.Name, txd, txdRail, 
++            txd->Envelope.Xid.Generation, txd->Envelope.Xid.Handle, txd->Envelope.Xid.Unique);
++
++    txd->TxdRail = &txdRail->txd_generic;
++    txdRail->txd_generic.Txd = txd;
++}
++
++static void
++unbind_txd_rail (EP_TXD *txd, EP4_TXD_RAIL *txdRail)
++{
++    EP4_TXD_ASSERT (txdRail, txd->TxdRail == &txdRail->txd_generic && txdRail->txd_generic.Txd == txd);
++
++    EPRINTF6 (DBG_XMTR, "%s: unbind_txd_rail: txd=%p txdRail=%p XID=%08x.%08x.%016llx\n", 
++            XMTR_TO_RAIL(txdRail->txd_generic.XmtrRail)->r_generic.Name, txd, txdRail, 
++            txd->Envelope.Xid.Generation, txd->Envelope.Xid.Handle, txd->Envelope.Xid.Unique);
++
++
++    txdRail->txd_generic.Txd = NULL; 
++    txd->TxdRail = NULL;
++}
++
++static void
++initialise_txd (EP_TXD *txd, EP4_TXD_RAIL *txdRail, unsigned int phase)
++{
++    EP4_XMTR_RAIL *xmtrRail = (EP4_XMTR_RAIL *) txdRail->txd_generic.XmtrRail;
++    EP4_RAIL      *rail     = XMTR_TO_RAIL (xmtrRail);
++    ELAN4_DEV     *dev      = rail->r_ctxt.ctxt_dev;
++    
++    /* Flush the Elan TLB if mappings have changed */
++    ep_perrail_dvma_sync (&rail->r_generic);
++    
++    /* Initialise the per-rail fields in the envelope */
++    txd->Envelope.TxdRail = txdRail->txd_elan_addr;
++    txd->Envelope.NodeId  = rail->r_generic.Position.pos_nodeid;
++
++    /* Allocate a network error fixup cookie */
++    txdRail->txd_cookie = ep4_neterr_cookie (rail, txd->NodeId) | EP4_COOKIE_STEN;
++
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++    if ( epdebug_check_sum ) 
++      txd->Envelope.CheckSum = ep_calc_check_sum( txd->Xmtr->Subsys->Subsys.Sys, &txd->Envelope, txd->Envelope.Frags, txd->Envelope.nFrags);
++    else
++#endif
++      txd->Envelope.CheckSum = 0;  
++
++    /* Initialise the per-rail events */
++    switch (phase)
++    {
++    case EP_TXD_PHASE_ACTIVE:
++    {
++      unsigned int nsets = (txd->Envelope.nFrags ? txd->Envelope.nFrags : 1) + ( EP_IS_MULTICAST(txd->Envelope.Attr) ? 1 : 0);
++
++      if (! EP_IS_RPC(txd->Envelope.Attr))
++      {
++          elan4_sdram_writeq (dev, txdRail->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_done.ev_CountAndType),
++                              E4_EVENT_INIT_VALUE (-32 * nsets, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++
++          txdRail->txd_main->txd_data = EP4_STATE_FINISHED;
++      }
++      else
++      {
++          elan4_sdram_writeq (dev, txdRail->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_data.ev_CountAndType),
++                              E4_EVENT_INIT_VALUE(-32 * nsets , E4_EVENT_WRITE, E4_EVENT_DTYPE_LONG, 0));
++          elan4_sdram_writeq (dev, txdRail->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_done.ev_CountAndType),
++                              E4_EVENT_INIT_VALUE (-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++
++          txdRail->txd_main->txd_data = EP4_STATE_ACTIVE;
++      }
++                 
++      txdRail->txd_main->txd_env  = EP4_STATE_ACTIVE;
++      txdRail->txd_main->txd_done = EP4_STATE_ACTIVE;
++      break;
++    }
++
++    case EP_TXD_PHASE_PASSIVE:
++      EP4_TXD_ASSERT (txdRail, EP_IS_RPC(txd->Envelope.Attr));
++      
++      txdRail->txd_main->txd_env  = EP4_STATE_FINISHED;
++      txdRail->txd_main->txd_data = EP4_STATE_FINISHED;
++      txdRail->txd_main->txd_done = EP4_STATE_ACTIVE;
++
++      elan4_sdram_writeq (dev, txdRail->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_done.ev_CountAndType),
++                          E4_EVENT_INIT_VALUE (-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++      break;
++    }
++
++   if (EP_IS_NO_INTERRUPT(txd->Envelope.Attr))
++      elan4_sdram_writeq (dev, txdRail->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_done_cmd.c_intr_cmd), NOP_CMD);
++}
++
++static void
++terminate_txd_rail (EP4_XMTR_RAIL *xmtrRail, EP4_TXD_RAIL *txdRail)
++{
++    EP4_SDRAM_ASSERT (TXD_TO_RAIL(txdRail),\
++                    (txdRail)->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_env.ev_CountAndType),\
++                    E4_EVENT_INIT_VALUE (-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));\
++
++    /* clear the done flags - so that it will be ignored if an event interrupt is generated */
++    txdRail->txd_main->txd_env  = EP4_STATE_FREE;
++    txdRail->txd_main->txd_data = EP4_STATE_FREE;
++    txdRail->txd_main->txd_done = EP4_STATE_FREE;
++
++#if defined(DEBUG_ASSERT)
++    if (sdram_assert)
++    {
++      ELAN4_DEV *dev = XMTR_TO_RAIL (xmtrRail)->r_ctxt.ctxt_dev;
++
++      elan4_sdram_writeq (dev, txdRail->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_data.ev_CountAndType),
++                          E4_EVENT_INIT_VALUE (0, E4_EVENT_WRITE, E4_EVENT_DTYPE_LONG, 0));
++      elan4_sdram_writeq (dev, txdRail->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_done.ev_CountAndType),
++                          E4_EVENT_INIT_VALUE (0, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++    }
++#endif 
++}
++
++static void
++defer_txd_rail (EP4_TXD_RAIL *txdRail)
++{
++    EP4_XMTR_RAIL   *xmtrRail = TXD_TO_XMTR(txdRail);
++    EP4_RAIL        *rail     = XMTR_TO_RAIL(xmtrRail);
++    ELAN4_DEV       *dev      = rail->r_ctxt.ctxt_dev;
++    EP_COMMS_SUBSYS *subsys   = XMTR_TO_SUBSYS(xmtrRail);
++
++    EPRINTF5 (DBG_XMTR, "%s: defer_txd_rail: xmtrRail=%p txdRail=%p env/data (%d,%d) not finished\n",
++            rail->r_generic.Name, xmtrRail, txdRail, (int)txdRail->txd_main->txd_env, (int)txdRail->txd_main->txd_data);
++                  
++    /* transmit has completed, but the data dma has not completed
++     * (because of network error fixup), we queue the txdRail onto a list
++     * to be polled for completion later.
++     */
++    if (txdRail->txd_retry_time)
++    {
++      EP4_TXD_ASSERT (txdRail, (on_list (&txdRail->txd_retry_link, &xmtrRail->xmtr_retrylist[EP4_TXD_LIST_RETRY]) == 1 ||
++                                on_list (&txdRail->txd_retry_link, &xmtrRail->xmtr_retrylist[EP4_TXD_LIST_STALLED]) == 1));
++
++      list_del (&txdRail->txd_retry_link);
++
++      txdRail->txd_main->txd_env = EP4_STATE_FINISHED;
++
++      /* re-initialise the envelope event */
++      elan4_sdram_writeq (dev, txdRail->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_env.ev_CountAndType),
++                          E4_EVENT_INIT_VALUE (-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++    }
++    
++    txdRail->txd_retry_time = lbolt;
++      
++    list_add_tail (&txdRail->txd_retry_link, &xmtrRail->xmtr_retrylist[EP4_TXD_LIST_POLL]);
++      
++    ep_kthread_schedule (&subsys->Thread, lbolt);
++}
++
++static void
++finalise_txd (EP_TXD *txd, EP4_TXD_RAIL *txdRail)
++{
++    EP4_XMTR_RAIL   *xmtrRail = TXD_TO_XMTR(txdRail);
++
++    EP4_TXD_ASSERT_FINISHED (txdRail);
++
++    unbind_txd_rail (txd, txdRail);
++    
++    terminate_txd_rail (xmtrRail, txdRail);
++    free_txd_rail (xmtrRail, txdRail);
++}
++
++static void
++txd_interrupt (EP4_RAIL *rail, void *arg)
++{
++    EP4_TXD_RAIL    *txdRail  = (EP4_TXD_RAIL *) arg;
++    EP4_XMTR_RAIL   *xmtrRail = TXD_TO_XMTR(txdRail);
++    EP_XMTR         *xmtr     = xmtrRail->xmtr_generic.Xmtr;
++    int              delay    = 1;
++    EP_TXD          *txd;
++    unsigned long    flags;
++
++    spin_lock_irqsave (&xmtr->Lock, flags);
++    for (;;)
++    {
++      if (txdRail->txd_main->txd_done == EP4_STATE_FINISHED || txdRail->txd_main->txd_env == EP4_STATE_FAILED)
++          break;
++      
++      /* The write to txd_done could be held up in the PCI bridge even though
++       * we've seen the interrupt cookie.  Unlike elan3, there is no possibility
++       * of spurious interrupts since we flush the command queues on node 
++       * disconnection and the txcallback mechanism */
++      mb();
++
++      if (delay > EP4_EVENT_FIRING_TLIMIT)
++      {
++          spin_unlock_irqrestore (&xmtr->Lock, flags);
++
++          EP_ASSFAIL (XMTR_TO_RAIL(xmtrRail), "txd_interrupt - not finished\n");
++          return;
++      }
++      DELAY (delay);
++      delay <<= 1;
++    }
++
++    txd = txdRail->txd_generic.Txd;
++
++    if (txdRail->txd_main->txd_env == EP4_STATE_FAILED)
++    {
++      spin_lock (&xmtrRail->xmtr_retrylock);
++
++      EP4_TXD_ASSERT (txdRail, txdRail->txd_retry_time == 0);                         /* cannot be on retry/poll list */
++      EP4_TXD_ASSERT (txdRail, txdRail->txd_main->txd_done != EP4_STATE_FINISHED);    /* data xfer cannot have finished */
++
++      if (TxdShouldStabalise (&txdRail->txd_generic, &rail->r_generic))
++      {
++          EPRINTF6 (DBG_STABILISE, "%s: txd_interrupt: stablise xmtrRail=%p txdRail=%p txd=%p XID=%llx dest=%u\n", rail->r_generic.Name,
++                    xmtrRail, txdRail, txd, txd->Envelope.Xid.Unique, txd->NodeId);
++
++          txdRail->txd_retry_time = lbolt;                    /* indicate on retry list */
++          
++          list_add_tail (&txdRail->txd_retry_link, &xmtrRail->xmtr_retrylist[EP4_TXD_LIST_STALLED]);
++      }
++      else
++      {
++          EPRINTF6 (DBG_RETRY, "%s: txd_interrupt: retry xmtrRail=%p txdRail=%p txd=%p XID=%llx dest=%u\n", rail->r_generic.Name,
++                    xmtrRail, txdRail, txd, txd->Envelope.Xid.Unique, txd->NodeId);
++
++          txdRail->txd_retry_time = lbolt + EP_RETRY_LOW_PRI_TIME;            /* XXXX: backoff ? */
++          
++          list_add_tail (&txdRail->txd_retry_link, &xmtrRail->xmtr_retrylist[EP4_TXD_LIST_RETRY]);
++          
++          ep_kthread_schedule (&rail->r_retry_thread, txdRail->txd_retry_time);
++      }
++      spin_unlock (&xmtrRail->xmtr_retrylock);
++
++      spin_unlock_irqrestore (&xmtr->Lock, flags);
++      return;
++    }
++
++    EP4_TXD_ASSERT (txdRail, txd != NULL && !(EP_IS_NO_INTERRUPT(txd->Envelope.Attr)));
++
++    EPRINTF6 (DBG_XMTR, "%s: txd_interrupt: xmtrRail=%p txdRail=%p txd=%p XID=%llx dest=%u\n", rail->r_generic.Name,
++            xmtrRail, txdRail, txd, txd->Envelope.Xid.Unique, txd->NodeId);
++           
++    if (txdRail->txd_main->txd_env != EP4_STATE_FINISHED || txdRail->txd_main->txd_data != EP4_STATE_FINISHED)
++    {
++      defer_txd_rail (txdRail);
++
++      spin_unlock_irqrestore (&xmtr->Lock, flags);
++    }
++    else
++    {
++      /* remove from active transmit list */
++      list_del (&txd->Link);
++
++      ep_xmtr_txd_stat(xmtr,txd);
++
++      finalise_txd (txd, txdRail);
++      
++      spin_unlock_irqrestore (&xmtr->Lock, flags);
++      
++      txd->Handler (txd, txd->Arg, EP_SUCCESS);
++      
++      FreeTxd (xmtr, txd);
++    }
++}
++
++static void
++poll_interrupt (EP4_RAIL *rail, void *arg)
++{
++    EP4_XMTR_RAIL   *xmtrRail = (EP4_XMTR_RAIL *) arg;
++
++    ep_poll_transmits (xmtrRail->xmtr_generic.Xmtr);
++}
++
++void
++issue_envelope_packet (EP4_XMTR_RAIL *xmtrRail, EP4_TXD_RAIL *txdRail)
++{
++    EP_TXD    *txd    = txdRail->txd_generic.Txd;
++    ELAN4_CQ  *cq     = xmtrRail->xmtr_cq;
++    E4_uint64 *blk0   = (E4_uint64 *) &txd->Envelope;
++    E4_uint64 *blk1   = EP_HAS_PAYLOAD(txd->Envelope.Attr) ? (E4_uint64 *) &txd->Payload : NULL;
++    E4_Addr    qaddr  = EP_MSGQ_ADDR(txd->Service);
++
++    EP4_SDRAM_ASSERT (TXD_TO_RAIL(txdRail),\
++                    (txdRail)->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_env.ev_CountAndType),\
++                    E4_EVENT_INIT_VALUE (-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));\
++
++    elan4_open_packet (cq, OPEN_PACKET (0, PACK_OK | RESTART_COUNT_ZERO, EP_VP_DATA(txd->NodeId)));
++    elan4_sendtrans0 (cq, TR_INPUT_Q_GETINDEX, EP_MSGQ_ADDR(txd->Service));
++          
++    /* send the payload if present */
++    if (blk0) elan4_sendtransp (cq, TR_WRITE(128 >> 3, 0, TR_DATATYPE_BYTE), 0,   blk0);
++    if (blk1) elan4_sendtransp (cq, TR_WRITE(128 >> 3, 0, TR_DATATYPE_BYTE), 128, blk1);
++
++    elan4_sendtrans1 (cq, TR_INPUT_Q_COMMIT, qaddr, txdRail->txd_cookie);
++
++    elan4_guard (cq, GUARD_CHANNEL (1) | GUARD_TEST(0, PACK_OK) | GUARD_RESET (EP4_STEN_RETRYCOUNT));
++    elan4_write_dword_cmd (cq, txdRail->txd_main_addr + offsetof (EP4_TXD_RAIL_MAIN, txd_env), EP4_STATE_FINISHED);
++          
++    elan4_guard (cq, GUARD_CHANNEL (1) | GUARD_TEST(0, RESTART_COUNT_ZERO) | GUARD_RESET (EP4_STEN_RETRYCOUNT));
++    elan4_set_event_cmd (cq, txdRail->txd_elan_addr + offsetof (EP4_TXD_RAIL_ELAN, txd_env));
++    
++    elan4_write_dword_cmd (cq, xmtrRail->xmtr_main_addr + offsetof (EP4_XMTR_RAIL_MAIN, xmtr_flowcnt), ++xmtrRail->xmtr_flowcnt);
++}
++
++void
++ep4xmtr_flush_callback (EP_XMTR *xmtr, EP4_XMTR_RAIL *xmtrRail)
++{
++    EP4_RAIL       *rail      = XMTR_TO_RAIL (xmtrRail);
++    EP4_COMMS_RAIL *commsRail = XMTR_TO_COMMS (xmtrRail);
++    struct list_head *el, *nel;
++    unsigned long flags;
++
++    switch (rail->r_generic.CallbackStep)
++    {
++    case EP_CB_FLUSH_FILTERING:
++      /* need to acquire/release the Lock to ensure that the node state
++       * transition has been noticed and no new envelopes are queued to 
++       * nodes which are passivating. */
++      spin_lock_irqsave (&xmtr->Lock, flags);
++
++      /* Then we insert a "setevent" into the command queue to flush
++       * through the envelopes which have already been submitted */
++      ep4comms_flush_setevent (commsRail, xmtrRail->xmtr_cq);
++
++      spin_unlock_irqrestore (&xmtr->Lock, flags);
++
++      break;
++
++    case EP_CB_FLUSH_FLUSHING:
++      /* remove any envelopes which are retrying to nodes which are going down */
++      spin_lock_irqsave (&xmtrRail->xmtr_retrylock, flags);
++      list_for_each_safe (el, nel, &xmtrRail->xmtr_retrylist[EP4_TXD_LIST_RETRY]) {
++          EP4_TXD_RAIL *txdRail  = list_entry (el, EP4_TXD_RAIL, txd_retry_link);
++          EP_TXD       *txd      = txdRail->txd_generic.Txd;
++          EP_NODE_RAIL *nodeRail = &rail->r_generic.Nodes[txd->NodeId];
++          
++          EP4_TXD_ASSERT (txdRail, txdRail->txd_main->txd_env == EP4_STATE_FAILED);
++          
++          if (nodeRail->State == EP_NODE_LOCAL_PASSIVATE)
++          {
++              EPRINTF2 (DBG_XMTR, "%s; ep4xmtr_flush_callback: removing txdRail %p from retry list\n", rail->r_generic.Name, txdRail);
++              
++              EP4_TXD_ASSERT (txdRail, txdRail->txd_retry_time != 0);
++
++              list_del (&txdRail->txd_retry_link);
++              list_add_tail (&txdRail->txd_retry_link, &xmtrRail->xmtr_retrylist[EP4_TXD_LIST_STALLED]);
++          }
++      }
++      spin_unlock_irqrestore (&xmtrRail->xmtr_retrylock, flags);
++
++      /* Determine whether we have active or passive messages to 
++       * any node which is passivating */
++      spin_lock_irqsave (&xmtr->Lock, flags);
++      list_for_each (el, &xmtr->ActiveDescList) {
++          EP_TXD       *txd      = list_entry (el, EP_TXD, Link);
++          EP4_TXD_RAIL *txdRail  = (EP4_TXD_RAIL *) txd->TxdRail;
++          EP_NODE_RAIL *nodeRail = &rail->r_generic.Nodes[txd->NodeId];
++          
++          if (txdRail == NULL || txdRail->txd_generic.XmtrRail != &xmtrRail->xmtr_generic || nodeRail->State != EP_NODE_LOCAL_PASSIVATE)
++              continue;
++          
++          EPRINTF5 (DBG_XMTR, "%s: flush txd=%p txdRail=%p data=%llx done=%llx\n", rail->r_generic.Name,
++                    txd, txdRail, txdRail->txd_main->txd_data, txdRail->txd_main->txd_done);
++
++          if (EP_IS_RPC(txd->Envelope.Attr))
++          {
++              if (txdRail->txd_main->txd_data == EP4_STATE_ACTIVE)
++                  nodeRail->MessageState |= EP_NODE_ACTIVE_MESSAGES;
++              else if (txdRail->txd_main->txd_data == EP4_STATE_ACTIVE)
++                  nodeRail->MessageState |= EP_NODE_PASSIVE_MESSAGES;
++          }
++          else
++          {
++              if (txdRail->txd_main->txd_data == EP4_STATE_ACTIVE)
++                  nodeRail->MessageState |= EP_NODE_ACTIVE_MESSAGES;
++          }
++      }
++      spin_unlock_irqrestore (&xmtr->Lock, flags);
++      break;
++
++    default:
++      panic ("ep4xmtr_flush_callback: invalid callback step\n");
++      break;
++    }
++}
++
++void
++ep4xmtr_failover_callback (EP_XMTR *xmtr, EP4_XMTR_RAIL *xmtrRail)
++{
++    EP4_RAIL         *rail   = XMTR_TO_RAIL (xmtrRail);
++    struct list_head  txdList;
++    struct list_head *el, *nel;
++    unsigned long flags;
++
++    INIT_LIST_HEAD (&txdList);
++
++    spin_lock_irqsave (&xmtr->Lock, flags);
++    list_for_each_safe (el, nel, &xmtr->ActiveDescList) {
++      EP_TXD       *txd       = list_entry (el, EP_TXD, Link);
++      EP4_TXD_RAIL *txdRail   = (EP4_TXD_RAIL *) txd->TxdRail;
++      EP_NODE_RAIL *nodeRail = &rail->r_generic.Nodes[txd->NodeId];
++          
++      /* Only progress relocation of txd's bound to this rail */
++      if (! TXD_BOUND2RAIL (txdRail, xmtrRail) || nodeRail->State != EP_NODE_PASSIVATED)
++          continue;
++      
++      /* XXXX - no rail failover for now ....*/
++
++      EPRINTF4 (DBG_XMTR, "%s: ep4xmtr_failover_callback - xmtr %p txd %p node %d completed\n", rail->r_generic.Name, xmtr, txd, txd->NodeId);
++    }
++    spin_unlock_irqrestore (&xmtr->Lock, flags);
++
++    while (! list_empty (&txdList)) 
++    {
++      EP_TXD *txd = list_entry (txdList.next, EP_TXD, Link);
++
++      list_del (&txd->Link);
++
++      txd->Handler (txd, txd->Arg, EP_CONN_RESET);
++      
++      FreeTxd (xmtr, txd);
++    }
++}
++
++
++void
++ep4xmtr_disconnect_callback (EP_XMTR *xmtr, EP4_XMTR_RAIL *xmtrRail)
++{
++    EP4_RAIL         *rail = XMTR_TO_RAIL (xmtrRail);
++    ELAN4_DEV        *dev  = rail->r_ctxt.ctxt_dev;
++    struct list_head *el, *nel;
++    struct list_head  txdList;
++    unsigned long flags;
++    
++    INIT_LIST_HEAD (&txdList);
++
++    spin_lock_irqsave (&xmtr->Lock, flags);
++
++    list_for_each_safe (el, nel, &xmtr->ActiveDescList) {
++      EP_TXD       *txd       = list_entry (el, EP_TXD, Link);
++      EP4_TXD_RAIL *txdRail   = (EP4_TXD_RAIL *) txd->TxdRail;
++      EP_NODE_RAIL *nodeRail = &rail->r_generic.Nodes[txd->NodeId];
++          
++      if ( ! TXD_BOUND2RAIL (txdRail, xmtrRail) || nodeRail->State != EP_NODE_DISCONNECTING)
++          continue;
++      
++      if (txdRail->txd_main->txd_done == EP4_STATE_ACTIVE)
++      {
++
++          EPRINTF8 (DBG_DISCON, "ep4xmtr_disconnect_callback: txdRail=%p : events %llx,%llx,%llx done %llx,%llx,%llx retry %lx\n",txdRail,
++                    elan4_sdram_readq (dev, txdRail->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_env.ev_CountAndType)),
++                    elan4_sdram_readq (dev, txdRail->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_data.ev_CountAndType)),
++                    elan4_sdram_readq (dev, txdRail->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_done.ev_CountAndType)),
++                    txdRail->txd_main->txd_env, txdRail->txd_main->txd_data, txdRail->txd_main->txd_done,
++                    txdRail->txd_retry_time);
++                     
++          if (txdRail->txd_retry_time)
++          {
++              /* re-initialise the envelope event */
++              elan4_sdram_writeq (dev, txdRail->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_env.ev_CountAndType),
++                                  E4_EVENT_INIT_VALUE (-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++              
++              EP4_TXD_ASSERT (txdRail, on_list (&txdRail->txd_retry_link, &xmtrRail->xmtr_retrylist[EP4_TXD_LIST_STALLED]) == 1);
++
++              txdRail->txd_retry_time  = 0;
++
++              list_del (&txdRail->txd_retry_link);
++          }
++
++          /* Remove from active list */
++          list_del (&txd->Link);
++      
++          unbind_txd_rail (txd, txdRail);
++
++          terminate_txd_rail (xmtrRail, txdRail);
++          free_txd_rail (xmtrRail, txdRail);
++          
++          EPRINTF4 (DBG_XMTR, "%s: ep4xmtr_disconnect_callback - xmtr %p txd %p node %d not conected\n", rail->r_generic.Name, xmtr, txd, txd->NodeId);
++
++          /* add to the list of txd's which are to be completed */
++          list_add_tail (&txd->Link, &txdList);
++      }
++    }
++    spin_unlock_irqrestore (&xmtr->Lock, flags);
++    
++    while (! list_empty (&txdList)) 
++    {
++      EP_TXD *txd = list_entry (txdList.next, EP_TXD, Link);
++
++      list_del (&txd->Link);
++
++      txd->Handler (txd, txd->Arg, EP_CONN_RESET);
++      
++      FreeTxd (xmtr, txd);
++    }
++}
++
++void
++ep4xmtr_neterr_flush (EP_XMTR *xmtr, EP4_XMTR_RAIL *xmtrRail, unsigned int nodeId, EP_NETERR_COOKIE *cookies)
++{
++    EP4_COMMS_RAIL   *commsRail = XMTR_TO_COMMS (xmtrRail);
++    unsigned long flags;
++    
++    spin_lock_irqsave (&xmtr->Lock, flags);
++
++    /* insert a "setevent" into the command queue to flush
++     * through the envelopes which have already been submitted */
++    ep4comms_flush_setevent (commsRail, xmtrRail->xmtr_cq);
++
++    spin_unlock_irqrestore (&xmtr->Lock, flags);
++}
++
++void
++ep4xmtr_neterr_check (EP_XMTR *xmtr, EP4_XMTR_RAIL *xmtrRail, unsigned int nodeId, EP_NETERR_COOKIE *cookies)
++{
++    EP4_RAIL *rail = XMTR_TO_RAIL (xmtrRail);
++    ELAN4_DEV *dev = rail->r_ctxt.ctxt_dev;
++    struct list_head *el;
++    unsigned long flags;
++    
++    spin_lock_irqsave (&xmtr->Lock, flags);
++    list_for_each (el, &xmtr->ActiveDescList) {
++      EP_TXD       *txd     = list_entry (el, EP_TXD, Link);
++      EP4_TXD_RAIL *txdRail = (EP4_TXD_RAIL *) txd->TxdRail;
++          
++      if ( ! TXD_BOUND2RAIL (txdRail, xmtrRail) || txd->NodeId != nodeId)
++          continue;
++      
++      /* The only non-dma associated with a txd is the initial sten packet, if it has been acked 
++       * and the neterr cookie matches, then change it to look like it's been acked since the
++       * INPUT_Q_COMMIT transaction has already been executed */
++      if (txdRail->txd_main->txd_env == EP4_STATE_FAILED && (txdRail->txd_cookie == cookies[0] || txdRail->txd_cookie == cookies[1]))
++      {
++          EPRINTF4 (DBG_NETWORK_ERROR, "%s: ep4xmtr_neterr_callback: cookie <%lld%s%s%s%s> matches txd %p txdRail %p\n", 
++                   rail->r_generic.Name, EP4_COOKIE_STRING(txdRail->txd_cookie), txd, txdRail);
++
++          EP4_TXD_ASSERT (txdRail, txdRail->txd_retry_time != 0);
++
++          txdRail->txd_main->txd_env = EP4_STATE_FINISHED;
++
++          /* re-initialise the envelope event */
++          elan4_sdram_writeq (dev, txdRail->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_env.ev_CountAndType),
++                              E4_EVENT_INIT_VALUE (-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++          
++          spin_lock (&xmtrRail->xmtr_retrylock);
++
++          EP4_TXD_ASSERT (txdRail, (on_list (&txdRail->txd_retry_link, &xmtrRail->xmtr_retrylist[EP4_TXD_LIST_RETRY]) == 1 ||
++                                    on_list (&txdRail->txd_retry_link, &xmtrRail->xmtr_retrylist[EP4_TXD_LIST_STALLED]) == 1));
++
++          txdRail->txd_retry_time = 0;
++
++          list_del (&txdRail->txd_retry_link);
++
++          spin_unlock (&xmtrRail->xmtr_retrylock);
++      }
++    }
++    spin_unlock_irqrestore (&xmtr->Lock, flags);
++}
++
++int
++ep4xmtr_poll_txd (EP_XMTR_RAIL *x, EP_TXD_RAIL *t, int how)
++{
++    EP4_XMTR_RAIL *xmtrRail = (EP4_XMTR_RAIL *) x;
++    ELAN4_DEV     *dev      = XMTR_TO_DEV (xmtrRail);
++    EP4_TXD_RAIL  *txdRail  = (EP4_TXD_RAIL *) t;
++    EP_TXD        *txd      = txdRail->txd_generic.Txd;
++
++    if (! EP_IS_NO_INTERRUPT(txd->Envelope.Attr))
++      return 0;
++
++    switch (how)
++    {
++    case ENABLE_TX_CALLBACK:
++      if (!EP_IS_INTERRUPT_ENABLED(txd->Envelope.Attr))
++      {
++          elan4_sdram_writeq (dev, txdRail->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_done_cmd.c_intr_cmd),
++                              INTERRUPT_CMD | (xmtrRail->xmtr_intcookie.int_val << E4_MAIN_INT_SHIFT));
++
++          txd->Envelope.Attr |= EP_INTERRUPT_ENABLED;
++      }
++      break;
++
++    case DISABLE_TX_CALLBACK:
++      if (EP_IS_INTERRUPT_ENABLED(txd->Envelope.Attr & EP_INTERRUPT_ENABLED))
++      {
++          elan4_sdram_writeq (dev, txdRail->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_done_cmd.c_intr_cmd), NOP_CMD);
++
++          txd->Envelope.Attr &= ~EP_INTERRUPT_ENABLED;
++      }
++    }
++    
++    if (txdRail->txd_main->txd_env == EP4_STATE_FINISHED && txdRail->txd_main->txd_data == EP4_STATE_FINISHED && txdRail->txd_main->txd_done == EP4_STATE_FINISHED)
++    {
++      EPRINTF3 (DBG_XMTR, "%s: ep4xmtr_poll_txd: txd=%p XID=%llx completed\n",
++                XMTR_TO_RAIL (xmtrRail)->r_generic.Name, txd, txd->Envelope.Xid.Unique);
++      
++      elan4_sdram_writeq (dev, txdRail->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_done_cmd.c_intr_cmd),
++                          INTERRUPT_CMD | (txdRail->txd_intcookie.int_val << E4_MAIN_INT_SHIFT));
++
++
++      ep_xmtr_txd_stat(xmtrRail->xmtr_generic.Xmtr,txd);
++
++      finalise_txd (txd, txdRail);
++
++      return 1;
++    }
++
++    return 0;
++}
++
++int
++ep4xmtr_bind_txd (EP_TXD *txd, EP_XMTR_RAIL *x, unsigned int phase)
++{
++    EP4_XMTR_RAIL *xmtrRail = (EP4_XMTR_RAIL *) x;
++    EP4_RAIL      *rail     = XMTR_TO_RAIL (xmtrRail);
++    EP4_TXD_RAIL  *txdRail;
++    unsigned long  flags;
++
++    if ((txdRail = get_txd_rail (xmtrRail)) == NULL)
++      return 0;
++    
++    switch (phase)
++    {
++    case EP_TXD_PHASE_ACTIVE:
++      if (rail->r_generic.Nodes[txd->NodeId].State != EP_NODE_CONNECTED)
++      {
++          EPRINTF2 (DBG_XMTR, "%s: ep4xmtr_bind_txd: node %u not connected on this rail\n", rail->r_generic.Name, txd->NodeId);
++
++          free_txd_rail (xmtrRail, txdRail);
++          return 0;
++      }
++
++      initialise_txd (txd, txdRail, EP_TXD_PHASE_ACTIVE);
++
++      bind_txd_rail (txd, txdRail);
++      
++      /* generate the STEN packet to transfer the envelope */
++      spin_lock_irqsave (&xmtrRail->xmtr_retrylock, flags);
++      if (((int) (xmtrRail->xmtr_flowcnt - xmtrRail->xmtr_main->xmtr_flowcnt)) < EP4_XMTR_FLOWCNT)
++          issue_envelope_packet (xmtrRail, txdRail);
++      else
++      {
++          txdRail->txd_retry_time = lbolt;
++
++          list_add_tail (&txdRail->txd_retry_link, &xmtrRail->xmtr_retrylist[EP4_TXD_LIST_RETRY]);
++
++          ep_kthread_schedule (&rail->r_retry_thread, txdRail->txd_retry_time);
++      }
++      spin_unlock_irqrestore (&xmtrRail->xmtr_retrylock, flags);
++      break;
++
++    case EP_TXD_PHASE_PASSIVE:
++      initialise_txd (txd, txdRail, EP_TXD_PHASE_PASSIVE);
++      
++      EP_XMTR_OP (txd->TxdRail->XmtrRail, UnbindTxd) (txd, EP_TXD_PHASE_PASSIVE);     /* unbind from existing rail */
++
++      bind_txd_rail (txd, txdRail);                                                   /* and bind it to our new rail */
++      break;
++    }
++
++    return 1;
++}
++
++void
++ep4xmtr_unbind_txd (EP_TXD *txd, unsigned int phase)
++{
++    /* XXXX - TBD */
++}
++
++long
++ep4xmtr_check (EP_XMTR_RAIL *x, long nextRunTime)
++{
++    EP4_XMTR_RAIL *xmtrRail = (EP4_XMTR_RAIL *) x;
++    EP_XMTR       *xmtr     = xmtrRail->xmtr_generic.Xmtr;
++    struct list_head  txdList;
++    struct list_head *el, *nel;
++    unsigned long flags;
++
++    INIT_LIST_HEAD (&txdList);
++
++    if (xmtrRail->xmtr_freecount < ep_txd_lowat && !alloc_txd_block (xmtrRail))
++    {
++      EPRINTF1 (DBG_RCVR,"%s: failed to grow txd rail pool\n", XMTR_TO_RAIL(xmtrRail)->r_generic.Name);
++              
++      if (nextRunTime == 0 || AFTER (nextRunTime, lbolt + RESOURCE_RETRY_TIME))
++          nextRunTime = lbolt + RESOURCE_RETRY_TIME;
++    }
++
++    spin_lock_irqsave (&xmtr->Lock, flags);
++    list_for_each_safe (el, nel, &xmtrRail->xmtr_retrylist[EP4_TXD_LIST_POLL]) {
++      EP4_TXD_RAIL *txdRail = list_entry (el, EP4_TXD_RAIL, txd_retry_link);
++
++      if (txdRail->txd_main->txd_env != EP4_STATE_FINISHED || txdRail->txd_main->txd_data != EP4_STATE_FINISHED)
++      {
++          ep_debugf (DBG_XMTR, "%s: ep4xmtr_check: xmtrRail=%p txdRail=%p env/data (%d,%d) not finished\n",
++                     XMTR_TO_RAIL(xmtrRail)->r_generic.Name, xmtrRail, txdRail, (int)txdRail->txd_main->txd_env, (int)txdRail->txd_main->txd_data);
++                  
++          nextRunTime = lbolt + HZ;
++      }
++      else
++      {
++          EP_TXD *txd = txdRail->txd_generic.Txd;
++
++          ep_debugf (DBG_XMTR, "%s: ep4xmtr_check: xmtrRail=%p txdRail=%p env/data (%d,%d) finished\n",
++                     XMTR_TO_RAIL(xmtrRail)->r_generic.Name, xmtrRail, txdRail, (int)txdRail->txd_main->txd_env, (int)txdRail->txd_main->txd_data);
++
++          EPRINTF5 (DBG_XMTR, "%s: ep4xmtr_check: xmtrRail=%p txdRail=%p env/data (%d,%d) finished\n",
++                    XMTR_TO_RAIL(xmtrRail)->r_generic.Name, xmtrRail, txdRail, (int)txdRail->txd_main->txd_env, (int)txdRail->txd_main->txd_data);
++          EPRINTF3  (DBG_XMTR, "%s:    done %x data %x\n", XMTR_TO_RAIL(xmtrRail)->r_generic.Name,
++                     txdRail->txd_elan_addr + offsetof (EP4_TXD_RAIL_ELAN, txd_done),
++                     txdRail->txd_elan_addr + offsetof (EP4_TXD_RAIL_ELAN, txd_data));
++
++          EP4_TXD_ASSERT (txdRail, txdRail->txd_retry_time != 0);
++
++          /* remove txd from active list and add to list to call handlers */
++          list_del (&txd->Link);
++          list_add_tail (&txd->Link, &txdList);
++
++          /* remove and free of txdRail */
++          txdRail->txd_retry_time = 0;
++          list_del (&txdRail->txd_retry_link);
++
++          finalise_txd (txd, txdRail);
++
++      }
++    }
++    spin_unlock_irqrestore (&xmtr->Lock, flags);
++
++    while (! list_empty (&txdList))
++    {
++      EP_TXD *txd = list_entry (txdList.next, EP_TXD, Link);
++
++      list_del (&txd->Link);
++
++      ep_xmtr_txd_stat (xmtr,txd);
++
++      txd->Handler (txd, txd->Arg, EP_SUCCESS);
++
++      FreeTxd (xmtr, txd);
++    }
++
++    return nextRunTime;
++}
++
++unsigned long
++ep4xmtr_retry (EP4_RAIL *rail, void *arg, unsigned long nextRunTime)
++{
++    EP4_XMTR_RAIL *xmtrRail = (EP4_XMTR_RAIL *) arg;
++    ELAN4_DEV     *dev      = XMTR_TO_DEV(xmtrRail);
++    unsigned long  flags;
++
++    spin_lock_irqsave (&xmtrRail->xmtr_retrylock, flags);
++    while (! list_empty (&xmtrRail->xmtr_retrylist[EP4_TXD_LIST_RETRY]))
++    {
++      EP4_TXD_RAIL *txdRail = list_entry (xmtrRail->xmtr_retrylist[EP4_TXD_LIST_RETRY].next, EP4_TXD_RAIL, txd_retry_link);
++
++      if (BEFORE (lbolt, txdRail->txd_retry_time))
++      {
++          if (nextRunTime == 0 || AFTER (nextRunTime, txdRail->txd_retry_time))
++              nextRunTime = txdRail->txd_retry_time;
++
++          break;
++      }
++
++      if (((int) (xmtrRail->xmtr_flowcnt - xmtrRail->xmtr_main->xmtr_flowcnt)) < EP4_XMTR_FLOWCNT)
++      {
++          txdRail->txd_retry_time = 0;
++
++          list_del (&txdRail->txd_retry_link);
++          
++          /* re-initialise the envelope event */
++          elan4_sdram_writeq (dev, txdRail->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_env.ev_CountAndType),
++                              E4_EVENT_INIT_VALUE (-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++          
++          EPRINTF3 (DBG_RETRY, "%s: ep4xmtr_retry: re-issue envelope packet to %d for txdRail=%p\n", 
++                    rail->r_generic.Name, txdRail->txd_generic.Txd->Envelope.NodeId, txdRail);
++          
++          txdRail->txd_main->txd_env = EP4_STATE_ACTIVE;
++          
++          issue_envelope_packet (xmtrRail, txdRail);
++      }
++      else
++      {
++          EPRINTF2 (DBG_RETRY, "%s: ep4xmtr_retry: cannot re-issue envelope packet to %d\n", rail->r_generic.Name, txdRail->txd_generic.Txd->Envelope.NodeId);
++
++          if (nextRunTime == 0 || AFTER (nextRunTime, txdRail->txd_retry_time))
++              nextRunTime = txdRail->txd_retry_time;
++
++          break;
++      }
++    }
++    spin_unlock_irqrestore (&xmtrRail->xmtr_retrylock, flags);
++    
++    return nextRunTime;
++}
++
++void
++ep4xmtr_add_rail (EP_XMTR *xmtr, EP_COMMS_RAIL *commsRail)
++{
++    EP4_RAIL         *rail   = (EP4_RAIL *) commsRail->Rail;
++    EP_COMMS_SUBSYS  *subsys = xmtr->Subsys;
++    EP4_XMTR_RAIL    *xmtrRail;
++    unsigned long     flags;
++    int                     i;
++
++    KMEM_ZALLOC (xmtrRail, EP4_XMTR_RAIL *, sizeof (EP4_XMTR_RAIL), 1);
++
++    spin_lock_init (&xmtrRail->xmtr_freelock);
++    kcondvar_init  (&xmtrRail->xmtr_freesleep);
++    INIT_LIST_HEAD (&xmtrRail->xmtr_freelist);
++    INIT_LIST_HEAD (&xmtrRail->xmtr_blocklist);
++
++    for (i = 0; i < EP4_TXD_NUM_LISTS; i++)
++      INIT_LIST_HEAD (&xmtrRail->xmtr_retrylist[i]);
++    spin_lock_init (&xmtrRail->xmtr_retrylock);
++
++    xmtrRail->xmtr_generic.CommsRail = commsRail;
++    xmtrRail->xmtr_generic.Xmtr      = xmtr;
++
++    xmtrRail->xmtr_main = ep_alloc_main (&rail->r_generic, sizeof (EP4_XMTR_RAIL_MAIN), 0, &xmtrRail->xmtr_main_addr);
++    xmtrRail->xmtr_cq   = elan4_alloccq (&rail->r_ctxt, EP4_XMTR_CQSIZE, CQ_EnableAllBits, CQ_Priority);
++
++    xmtrRail->xmtr_retryops.op_func = ep4xmtr_retry;
++    xmtrRail->xmtr_retryops.op_arg  = xmtrRail;
++
++    ep4_add_retry_ops (rail, &xmtrRail->xmtr_retryops);
++
++    ep4_register_intcookie (rail, &xmtrRail->xmtr_intcookie, xmtrRail->xmtr_main_addr,
++                          poll_interrupt, xmtrRail);
++
++    spin_lock_irqsave (&xmtr->Lock, flags);
++
++    xmtr->Rails[commsRail->Rail->Number] = &xmtrRail->xmtr_generic;
++    xmtr->RailMask |= EP_RAIL2RAILMASK(commsRail->Rail->Number);
++
++    spin_unlock_irqrestore (&xmtr->Lock, flags);
++
++    ep_kthread_schedule (&subsys->Thread, lbolt);
++
++    ep_procfs_xmtr_add_rail(&(xmtrRail->xmtr_generic));
++}
++
++void
++ep4xmtr_del_rail (EP_XMTR *xmtr, EP_COMMS_RAIL *commsRail)
++{
++    EP4_RAIL         *rail     = (EP4_RAIL *) commsRail->Rail;
++    EP4_XMTR_RAIL    *xmtrRail = (EP4_XMTR_RAIL *) xmtr->Rails[commsRail->Rail->Number];
++    unsigned long     flags;
++
++    /* rail mask set as not usable */
++    spin_lock_irqsave (&xmtr->Lock, flags);
++    xmtr->RailMask &= ~EP_RAIL2RAILMASK (rail->r_generic.Number);
++    spin_unlock_irqrestore (&xmtr->Lock, flags);
++
++    ep_procfs_xmtr_del_rail(&(xmtrRail->xmtr_generic));
++
++    /* wait for all txd's for this rail to become free */
++    spin_lock_irqsave (&xmtrRail->xmtr_freelock, flags);
++    while (xmtrRail->xmtr_freecount != xmtrRail->xmtr_totalcount)
++    {
++      xmtrRail->xmtr_freewaiting++;
++      kcondvar_wait (&xmtrRail->xmtr_freesleep, &xmtrRail->xmtr_freelock, &flags);
++    }
++    spin_unlock_irqrestore (&xmtrRail->xmtr_freelock, flags);
++
++    spin_lock_irqsave (&xmtr->Lock, flags);
++    xmtr->Rails[commsRail->Rail->Number] = NULL;
++    spin_unlock_irqrestore (&xmtr->Lock, flags);
++
++    /* all the txd's accociated with DescBlocks must be in the freelist */
++    ASSERT (xmtrRail->xmtr_totalcount == xmtrRail->xmtr_freecount);
++
++    /* run through the DescBlockList deleting them */
++    while (!list_empty (&xmtrRail->xmtr_blocklist))
++      free_txd_block (xmtrRail, list_entry(xmtrRail->xmtr_blocklist.next, EP4_TXD_RAIL_BLOCK , blk_link));
++    
++    /* it had better be empty after that */
++    ASSERT ((xmtrRail->xmtr_freecount == 0) && (xmtrRail->xmtr_totalcount == 0));
++
++    ep4_deregister_intcookie (rail, &xmtrRail->xmtr_intcookie);
++
++    ep4_remove_retry_ops (rail, &xmtrRail->xmtr_retryops);
++
++    elan4_freecq (&rail->r_ctxt, xmtrRail->xmtr_cq);
++    ep_free_main (&rail->r_generic, xmtrRail->xmtr_main_addr, sizeof (EP4_XMTR_RAIL_MAIN));
++
++    spin_lock_destroy (&xmtrRail->xmtr_retrylock);
++
++    spin_lock_destroy (&xmtrRail->xmtr_freelock);
++    kcondvar_destroy (&xmtrRail->xmtr_freesleep);
++
++    KMEM_FREE (xmtrRail, sizeof (EP4_XMTR_RAIL));
++}
++
++void
++ep4xmtr_display_xmtr (DisplayInfo *di, EP_XMTR_RAIL *x)
++{
++    EP4_XMTR_RAIL    *xmtrRail     = (EP4_XMTR_RAIL *) x;
++    EP4_RAIL         *rail         = XMTR_TO_RAIL (xmtrRail);
++    unsigned int      freeCount    = 0;
++    unsigned int      pollCount    = 0;
++    unsigned int      stalledCount = 0;
++    unsigned int      retryCount   = 0;
++    struct list_head *el;
++    unsigned long     flags;
++
++    spin_lock_irqsave (&xmtrRail->xmtr_freelock, flags);
++    list_for_each (el, &xmtrRail->xmtr_freelist)
++      freeCount++;
++    spin_unlock_irqrestore (&xmtrRail->xmtr_freelock, flags);
++
++    spin_lock_irqsave (&xmtrRail->xmtr_retrylock, flags);
++    list_for_each (el, &xmtrRail->xmtr_retrylist[EP4_TXD_LIST_POLL])
++      pollCount++;
++    list_for_each (el, &xmtrRail->xmtr_retrylist[EP4_TXD_LIST_STALLED])
++      stalledCount++;
++    list_for_each (el, &xmtrRail->xmtr_retrylist[EP4_TXD_LIST_RETRY])
++      retryCount++;
++    spin_unlock_irqrestore (&xmtrRail->xmtr_retrylock, flags);
++
++    (di->func)(di->arg, "        rail=%d free=%d total=%d (%d) (retry %d,%d,%d)\n",
++             rail->r_generic.Number, xmtrRail->xmtr_freecount, xmtrRail->xmtr_totalcount, 
++             freeCount, pollCount, stalledCount, retryCount);
++    (di->func)(di->arg, "        cq %d flowcnt %lld,%lld\n", elan4_cq2num (xmtrRail->xmtr_cq), xmtrRail->xmtr_flowcnt, xmtrRail->xmtr_main->xmtr_flowcnt);
++}
++
++void
++ep4xmtr_display_txd (DisplayInfo *di, EP_TXD_RAIL *t)
++{
++    EP4_TXD_RAIL      *txdRail  = (EP4_TXD_RAIL *) t;
++    EP4_XMTR_RAIL     *xmtrRail = TXD_TO_XMTR(txdRail);
++    EP4_TXD_RAIL_MAIN *txdMain  = txdRail->txd_main;
++    sdramaddr_t        txdElan  = txdRail->txd_elan;
++    EP4_RAIL          *rail     = XMTR_TO_RAIL (xmtrRail);
++    ELAN4_DEV         *dev      = XMTR_TO_DEV (xmtrRail);
++    char            *list     = "";
++    unsigned long      flags;
++
++    spin_lock_irqsave (&xmtrRail->xmtr_retrylock, flags);
++    if (txdRail->txd_retry_time)
++    {
++      if (on_list (&txdRail->txd_retry_link, &xmtrRail->xmtr_retrylist[EP4_TXD_LIST_POLL]))
++          list = " poll";
++      else if (on_list (&txdRail->txd_retry_link, &xmtrRail->xmtr_retrylist[EP4_TXD_LIST_STALLED]))
++          list = " stalled";
++      else if (on_list (&txdRail->txd_retry_link, &xmtrRail->xmtr_retrylist[EP4_TXD_LIST_RETRY]))
++          list = " retry";
++      else
++          list = " ERROR";
++    }
++    spin_unlock_irqrestore (&xmtrRail->xmtr_retrylock, flags);
++
++    (di->func)(di->arg, "      Rail %d txd %p elan %lx (%x) main %p (%x) cookie <%lld%s%s%s%s> ecq %d %s\n", rail->r_generic.Number,
++             txdRail, txdRail->txd_elan, txdRail->txd_elan_addr, txdRail->txd_main, txdRail->txd_main_addr, 
++             EP4_COOKIE_STRING(txdRail->txd_cookie), elan4_cq2num (txdRail->txd_ecq->ecq_cq), list);
++    
++    (di->func)(di->arg, "        env  %016llx %016llx %016llx -> %016llx\n",
++             elan4_sdram_readq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_env.ev_CountAndType)),
++             elan4_sdram_readq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_env.ev_Params[0])),
++             elan4_sdram_readq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_env.ev_Params[1])),
++             txdMain->txd_env);
++    (di->func)(di->arg, "        data %016llx %016llx %016llx -> %016llx\n",
++             elan4_sdram_readq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_data.ev_CountAndType)),
++             elan4_sdram_readq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_data.ev_Params[0])),
++             elan4_sdram_readq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_data.ev_Params[1])),
++             txdMain->txd_data);
++    (di->func)(di->arg, "        done %016llx %016llx %016llx -> %016llx\n",
++             elan4_sdram_readq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_done.ev_CountAndType)),
++             elan4_sdram_readq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_done.ev_Params[0])),
++             elan4_sdram_readq (dev, txdElan + offsetof (EP4_TXD_RAIL_ELAN, txd_done.ev_Params[1])),
++             txdMain->txd_done);
++}
++
++int
++ep4xmtr_check_txd_state (EP_TXD *txd) 
++{
++    EP4_TXD_RAIL  *txdRail  = (EP4_TXD_RAIL *) txd->TxdRail;
++    EP4_XMTR_RAIL *xmtrRail = (EP4_XMTR_RAIL *) txdRail->txd_generic.XmtrRail;
++    ELAN4_DEV     *dev      = XMTR_TO_DEV (xmtrRail);
++    unsigned long  flags;
++
++    if (txdRail->txd_main->txd_env == EP4_STATE_FINISHED)
++      return 0;
++
++    EP4_TXD_ASSERT (txdRail, txdRail->txd_retry_time != 0);
++
++    spin_lock_irqsave (&xmtrRail->xmtr_retrylock, flags);
++    EP4_TXD_ASSERT (txdRail, on_list (&txdRail->txd_retry_link, &xmtrRail->xmtr_retrylist[EP4_TXD_LIST_STALLED]) == 1);
++
++    list_del (&txdRail->txd_retry_link);
++    txdRail->txd_retry_time  = 0;
++    spin_unlock_irqrestore (&xmtrRail->xmtr_retrylock, flags);
++    
++    /* re-initialise the envelope event */
++    elan4_sdram_writeq (dev, txdRail->txd_elan + offsetof (EP4_TXD_RAIL_ELAN, txd_env.ev_CountAndType),
++                      E4_EVENT_INIT_VALUE (-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_INTR_CMD_NDWORDS));
++          
++    unbind_txd_rail (txd, txdRail);
++
++    terminate_txd_rail (xmtrRail, txdRail);
++    free_txd_rail (xmtrRail, txdRail);
++
++    return 1;
++}
++
++void
++ep4xmtr_fillout_rail_stats(EP_XMTR_RAIL *xmtr_rail, char *str) {
++    /* no stats here yet */
++    /* EP4_XMTR_RAIL * ep4xmtr_rail = (EP4_XMTR_RAIL *) xmtr_rail; */
++}
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/ep/ep_procfs.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/ep_procfs.c  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/ep_procfs.c       2005-05-11 12:10:12.506922240 -0400
+@@ -0,0 +1,331 @@
++
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: ep_procfs.c,v 1.5.6.4 2005/03/20 11:23:33 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/ep_procfs.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++#include <elan/epsvc.h>
++#include <elan/epcomms.h>
++
++#include "cm.h"
++#include "debug.h"
++#include "conf_linux.h"
++
++#include "kcomm_vp.h"
++#include "kcomm_elan4.h"
++#include "epcomms_elan4.h"
++
++#include <qsnet/procfs_linux.h>
++
++struct proc_dir_entry *ep_procfs_xmtr_root;
++struct proc_dir_entry *ep_procfs_rcvr_root;
++
++static int
++ep_proc_open (struct inode *inode, struct file *file)
++{
++    PROC_PRIVATE *pr;
++    int           pages = 4;
++
++    if ((pr = kmalloc (sizeof (PROC_PRIVATE), GFP_KERNEL)) == NULL)
++      return (-ENOMEM);
++    
++    do {      
++      pr->pr_data_len = PAGESIZE * pages;
++
++      KMEM_ZALLOC (pr->pr_data, char *, pr->pr_data_len, 1);
++      if (pr->pr_data == NULL) 
++      { 
++          pr->pr_len  = sprintf (pr->pr_data, "Out of Memory\n");
++          break;
++      } 
++      
++      pr->pr_off     = 0;
++      pr->pr_len     = 0;
++      pr->pr_data[0] = 0;
++      
++      pr->pr_di.func  = proc_character_fill;
++      pr->pr_di.arg   = (long)pr;
++      
++      if (!strcmp("debug_xmtr", file->f_dentry->d_iname)) 
++      {   
++          EP_XMTR *xmtr = (EP_XMTR *)(PDE(inode)->data);
++          ep_display_xmtr (&pr->pr_di, xmtr);
++      }
++      
++      if (!strcmp("debug_rcvr", file->f_dentry->d_iname)) 
++      {
++          EP_RCVR *rcvr = (EP_RCVR *)(PDE(inode)->data);
++          ep_display_rcvr (&pr->pr_di, rcvr, 0);
++      }
++      
++      if (!strcmp("debug_full", file->f_dentry->d_iname)) 
++      {
++          EP_RCVR *rcvr = (EP_RCVR *)(PDE(inode)->data);
++          ep_display_rcvr (&pr->pr_di, rcvr, 1);
++      }
++
++      if ( pr->pr_len < pr->pr_data_len) 
++          break; /* we managed to get all the output into the buffer */
++
++      pages++;
++      KMEM_FREE ( pr->pr_data,  pr->pr_data_len);
++    } while (1);
++      
++
++    file->private_data = (void *) pr;
++
++    MOD_INC_USE_COUNT;
++    return (0);
++}
++
++struct file_operations ep_proc_operations = 
++{
++    read:     proc_read,
++    open:     ep_proc_open,
++    release:  proc_release,
++};
++
++static int
++proc_read_rcvr_stats(char *page, char **start, off_t off,
++                   int count, int *eof, void *data)
++{
++    EP_RCVR *rcvr = (EP_RCVR *)data;
++    
++    if (rcvr == NULL) 
++      sprintf(page,"proc_read_rcvr_stats rcvr=NULL\n");
++    else {
++      page[0] = 0;
++      ep_rcvr_fillout_stats(rcvr,page);
++    }
++    return (qsnet_proc_calc_metrics (page, start, off, count, eof, strlen(page)));
++}
++
++static int
++proc_read_rcvr_rail_stats(char *page, char **start, off_t off,
++                   int count, int *eof, void *data)
++{
++    EP_RCVR_RAIL *rcvr_rail = (EP_RCVR_RAIL *)data;
++
++    if (rcvr_rail == NULL) {
++      strcpy(page,"proc_read_rcvr_rail_stats rcvr_rail=NULL");
++    } else {
++      page[0] = 0;
++      ep_rcvr_rail_fillout_stats(rcvr_rail, page);
++      EP_RCVR_OP(rcvr_rail,FillOutRailStats)(rcvr_rail,page);
++    }
++    return (qsnet_proc_calc_metrics (page, start, off, count, eof, strlen(page)));
++}
++
++void
++ep_procfs_rcvr_add(EP_RCVR *rcvr)
++{ 
++    /* ep/rcvr/service_number/stats       */
++    /* ep/rcvr/service_number/debug_rcvr  */
++    /* ep/rcvr/service_number/debug_full  */
++    struct proc_dir_entry *p;
++    char str[32];
++
++    sprintf(str,"%d", rcvr->Service);
++
++    rcvr->procfs_root = proc_mkdir (str, ep_procfs_rcvr_root);
++
++    if ((p = create_proc_entry ("stats", 0,  rcvr->procfs_root)) != NULL)
++    {
++      p->write_proc = NULL;
++      p->read_proc  = proc_read_rcvr_stats;
++      p->data       = rcvr;
++      p->owner      = THIS_MODULE;
++    }
++
++    if ((p = create_proc_entry ("debug_rcvr", 0, rcvr->procfs_root)) != NULL)
++    {
++      p->proc_fops = &ep_proc_operations;
++      p->owner     = THIS_MODULE;
++      p->data      = rcvr;
++    }
++
++    if ((p = create_proc_entry ("debug_full", 0, rcvr->procfs_root)) != NULL)
++    {
++      p->proc_fops = &ep_proc_operations;
++      p->owner     = THIS_MODULE;
++      p->data      = rcvr;
++    }
++}
++
++void
++ep_procfs_rcvr_del(EP_RCVR *rcvr)
++{  
++    char str[32];
++    sprintf(str,"%d", rcvr->Service);
++
++    remove_proc_entry ("debug_full", rcvr->procfs_root);
++    remove_proc_entry ("debug_rcvr", rcvr->procfs_root);
++    remove_proc_entry ("stats",      rcvr->procfs_root);
++
++    remove_proc_entry (str, ep_procfs_rcvr_root);
++}
++
++void 
++ep_procfs_rcvr_add_rail(EP_RCVR_RAIL *rcvrRail)
++{
++    /* ep/rcvr/service_number/railN/stats */
++
++    struct proc_dir_entry *p;
++    char str[32];
++    sprintf(str,"rail%d",rcvrRail->CommsRail->Rail->Number);
++
++    rcvrRail->procfs_root = proc_mkdir (str, rcvrRail->Rcvr->procfs_root);
++    
++    if ((p = create_proc_entry ("stats", 0,  rcvrRail->procfs_root)) != NULL)
++    {
++      p->write_proc = NULL;
++      p->read_proc  = proc_read_rcvr_rail_stats;
++      p->data       = rcvrRail;
++      p->owner      = THIS_MODULE;
++    } 
++}
++
++void 
++ep_procfs_rcvr_del_rail(EP_RCVR_RAIL *rcvrRail)
++{
++    char str[32];
++    sprintf(str,"rail%d",rcvrRail->CommsRail->Rail->Number);
++
++    remove_proc_entry ("stats", rcvrRail->procfs_root);
++
++    remove_proc_entry (str, rcvrRail->Rcvr->procfs_root);
++}
++
++
++
++
++static int
++proc_read_xmtr_stats(char *page, char **start, off_t off,
++                   int count, int *eof, void *data)
++{
++    EP_XMTR *xmtr = (EP_XMTR *)data;
++
++    if (xmtr == NULL) 
++      strcpy(page,"proc_read_xmtr_stats xmtr=NULL\n");
++    else {
++      page[0] = 0;
++      ep_xmtr_fillout_stats(xmtr, page);
++    }
++    return (qsnet_proc_calc_metrics (page, start, off, count, eof, strlen(page)));
++}
++
++static int
++proc_read_xmtr_rail_stats(char *page, char **start, off_t off,
++                   int count, int *eof, void *data)
++{
++    EP_XMTR_RAIL *xmtr_rail = (EP_XMTR_RAIL *)data;
++
++    if (xmtr_rail == NULL) 
++      strcpy(page,"proc_read_xmtr_rail_stats xmtr_rail=NULL\n");
++    else {
++      page[0] = 0;
++      ep_xmtr_rail_fillout_stats(xmtr_rail, page);
++      EP_XMTR_OP(xmtr_rail,FillOutRailStats)(xmtr_rail,page);
++    }
++    return (qsnet_proc_calc_metrics (page, start, off, count, eof, strlen(page)));
++}
++
++void
++ep_procfs_xmtr_add(EP_XMTR *xmtr)
++{ 
++    /* ep/xmtr/service_number/stats       */
++    /* ep/xmtr/service_number/debug_xmtr  */
++    struct proc_dir_entry *p;
++    char str[32];
++
++    sprintf(str,"%llx", (unsigned long long) (unsigned long)xmtr);
++
++    xmtr->procfs_root = proc_mkdir (str, ep_procfs_xmtr_root);
++
++    if ((p = create_proc_entry ("stats", 0,  xmtr->procfs_root)) != NULL)
++    {
++      p->write_proc = NULL;
++      p->read_proc  = proc_read_xmtr_stats;
++      p->data       = xmtr;
++      p->owner      = THIS_MODULE;
++    } 
++
++    if ((p = create_proc_entry ("debug_xmtr", 0, xmtr->procfs_root)) != NULL)
++    {
++      p->proc_fops = &ep_proc_operations;
++      p->owner     = THIS_MODULE;
++      p->data      = xmtr;
++    }
++}
++
++void
++ep_procfs_xmtr_del(EP_XMTR *xmtr)
++{  
++    char str[32];
++    sprintf(str,"%llx", (unsigned long long) (unsigned long)xmtr);
++
++    remove_proc_entry ("stats",      xmtr->procfs_root);
++    remove_proc_entry ("debug_xmtr", xmtr->procfs_root);
++
++    remove_proc_entry (str, ep_procfs_xmtr_root);
++}
++
++void 
++ep_procfs_xmtr_add_rail(EP_XMTR_RAIL *xmtrRail)
++{
++    /* ep/xmtr/service_number/railN/stats */
++    
++    struct proc_dir_entry *p;
++    char str[32];
++    sprintf(str,"rail%d",xmtrRail->CommsRail->Rail->Number);
++
++    xmtrRail->procfs_root = proc_mkdir (str, xmtrRail->Xmtr->procfs_root);
++
++    if ((p = create_proc_entry ("stats", 0,  xmtrRail->procfs_root)) != NULL)
++    {
++      p->write_proc = NULL;
++      p->read_proc  = proc_read_xmtr_rail_stats;
++      p->data       = xmtrRail;
++      p->owner      = THIS_MODULE;
++    } 
++}
++
++void 
++ep_procfs_xmtr_del_rail(EP_XMTR_RAIL *xmtrRail)
++{
++    char str[32];
++    sprintf(str,"rail%d",xmtrRail->CommsRail->Rail->Number);
++
++    remove_proc_entry ("stats", xmtrRail->procfs_root);
++
++    remove_proc_entry (str, xmtrRail->Xmtr->procfs_root);
++}
++
++void
++ep_procfs_rcvr_xmtr_init(void)
++{
++    ep_procfs_rcvr_root = proc_mkdir ("rcvr", ep_procfs_root);
++    ep_procfs_xmtr_root = proc_mkdir ("xmtr", ep_procfs_root); 
++}
++
++void
++ep_procfs_rcvr_xmtr_fini(void)
++{
++    remove_proc_entry ("rcvr", ep_procfs_root);
++    remove_proc_entry ("xmtr", ep_procfs_root);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/ep/kalloc.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/kalloc.c     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/kalloc.c  2005-05-11 12:10:12.507922088 -0400
+@@ -0,0 +1,677 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: kalloc.c,v 1.17.8.2 2004/12/14 10:19:14 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/kalloc.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++
++#include "debug.h"
++
++static void
++HashInPool (EP_ALLOC *alloc, EP_POOL *pool)
++{
++    int idx0 = HASH (pool->Handle.nmh_nmd.nmd_addr);
++    int idx1 = HASH (pool->Handle.nmh_nmd.nmd_addr + pool->Handle.nmh_nmd.nmd_len);
++
++    list_add (&pool->HashBase, &alloc->HashBase[idx0]);
++    list_add (&pool->HashTop, &alloc->HashTop[idx1]);
++}
++
++static void
++HashOutPool (EP_ALLOC *alloc, EP_POOL *pool)
++{
++    list_del (&pool->HashBase);
++    list_del (&pool->HashTop);
++}
++
++static EP_POOL *
++LookupPool (EP_ALLOC *alloc, EP_ADDR addr)
++{
++    struct list_head *el;
++    
++    list_for_each (el, &alloc->HashBase[HASH(addr)]) {
++      EP_POOL *pool = list_entry (el, EP_POOL, HashBase);
++      
++      if (pool->Handle.nmh_nmd.nmd_addr <= addr && addr < (pool->Handle.nmh_nmd.nmd_addr + pool->Handle.nmh_nmd.nmd_len))
++          return (pool);
++    }
++    
++    list_for_each (el, &alloc->HashTop[HASH(addr)]) {
++      EP_POOL *pool = list_entry (el, EP_POOL, HashTop);
++      
++      if (pool->Handle.nmh_nmd.nmd_addr <= addr && addr < (pool->Handle.nmh_nmd.nmd_addr + pool->Handle.nmh_nmd.nmd_len))
++          return (pool);
++    }
++    
++    return (NULL);
++}
++
++static EP_POOL *
++AllocatePool (EP_ALLOC *alloc, EP_ADDR addr, unsigned size, unsigned int perm, EP_ATTRIBUTE attr)
++{
++    EP_ADDR base = 0;
++    EP_POOL *pool;
++    EP_RAIL *rail;
++    int i, railmask = 0;
++    struct list_head *el;
++
++    KMEM_ZALLOC (pool, EP_POOL *, sizeof (EP_POOL), !(attr & EP_NO_SLEEP));
++    
++    if (pool == NULL)
++      return (NULL);
++    
++    if (addr != 0)
++      base = addr;
++    else
++    {
++      for (i = LN2_MIN_SIZE; i <= LN2_MAX_SIZE; i ++)
++      {
++          KMEM_ZALLOC (pool->Bitmaps[i - LN2_MIN_SIZE], bitmap_t *, BT_BITOUL(1 << (LN2_MAX_SIZE-i)) * sizeof (bitmap_t), !(attr & EP_NO_SLEEP));
++          if (pool->Bitmaps[i - LN2_MIN_SIZE] == NULL)
++              goto failed;
++      }
++    
++      if ((base = ep_rmalloc (alloc->ResourceMap, size, !(attr & EP_NO_SLEEP))) == 0)
++          goto failed;
++    }
++
++    switch (alloc->Type)
++    {
++    case EP_ALLOC_TYPE_PRIVATE_SDRAM:
++      rail = alloc->Data.Private.Rail;
++
++      if ((pool->Buffer.Sdram = rail->Operations.SdramAlloc (rail, base, size)) == 0)
++          goto failed;
++
++      ep_perrail_sdram_map (rail, base, pool->Buffer.Sdram, size, perm, attr);
++
++      pool->Handle.nmh_nmd.nmd_addr = base;
++      pool->Handle.nmh_nmd.nmd_len  = size;
++      break;
++      
++    case EP_ALLOC_TYPE_PRIVATE_MAIN:
++        KMEM_GETPAGES(pool->Buffer.Ptr, unsigned long, btop (size), !(attr & EP_NO_SLEEP));
++      if (pool->Buffer.Ptr == 0)
++          goto failed;
++
++      ep_perrail_kaddr_map (alloc->Data.Private.Rail, base, pool->Buffer.Ptr, size, perm, attr);
++
++      pool->Handle.nmh_nmd.nmd_addr = base;
++      pool->Handle.nmh_nmd.nmd_len  = size;
++      break;
++
++    case EP_ALLOC_TYPE_SHARED_MAIN:
++        KMEM_GETPAGES(pool->Buffer.Ptr, unsigned long, btop (size), !(attr & EP_NO_SLEEP));
++      if (pool->Buffer.Ptr == 0)
++          goto failed;
++
++      list_for_each (el, &alloc->Data.Shared.Rails) {
++          EP_RAIL *rail = list_entry (el, EP_RAIL_ENTRY, Link)->Rail;
++
++          ep_perrail_kaddr_map (rail, base, pool->Buffer.Ptr, size, perm, attr);
++
++          railmask |= (1 << rail->Number);
++      }
++      pool->Handle.nmh_nmd.nmd_addr = base;
++      pool->Handle.nmh_nmd.nmd_len  = size;
++      pool->Handle.nmh_nmd.nmd_attr = EP_NMD_ATTR (alloc->Data.Shared.System->Position.pos_nodeid, railmask);
++
++      ep_nmh_insert (&alloc->Data.Shared.System->MappingTable, &pool->Handle);
++      break;
++
++    default:
++      goto failed;
++    }
++
++    return (pool);
++    
++ failed:
++    if (addr == 0 && base)
++      ep_rmfree (alloc->ResourceMap, size, base);
++
++    for (i = LN2_MIN_SIZE; i <= LN2_MAX_SIZE; i ++)
++      if (pool->Bitmaps[i - LN2_MIN_SIZE] != NULL)
++          KMEM_FREE (pool->Bitmaps[i - LN2_MIN_SIZE], BT_BITOUL(1 << (LN2_MAX_SIZE - i)) * sizeof (bitmap_t));
++    
++    KMEM_FREE (pool, sizeof (EP_POOL));
++    return (NULL);
++}
++
++static void
++FreePool (EP_ALLOC *alloc, EP_POOL *pool)
++{
++    struct list_head *el;
++    int i;
++
++    switch (alloc->Type)
++    {
++    case EP_ALLOC_TYPE_PRIVATE_SDRAM:
++      ep_perrail_unmap (alloc->Data.Private.Rail, pool->Handle.nmh_nmd.nmd_addr, pool->Handle.nmh_nmd.nmd_len);
++
++      alloc->Data.Private.Rail->Operations.SdramFree (alloc->Data.Private.Rail, pool->Buffer.Sdram, pool->Handle.nmh_nmd.nmd_len);
++      break;
++      
++    case EP_ALLOC_TYPE_PRIVATE_MAIN:
++      ep_perrail_unmap (alloc->Data.Private.Rail, pool->Handle.nmh_nmd.nmd_addr, pool->Handle.nmh_nmd.nmd_len);
++
++      KMEM_FREEPAGES (pool->Buffer.Ptr, btop (pool->Handle.nmh_nmd.nmd_len));
++      break;
++
++    case EP_ALLOC_TYPE_SHARED_MAIN:
++      ep_nmh_remove (&alloc->Data.Shared.System->MappingTable, &pool->Handle);
++
++      list_for_each (el, &alloc->Data.Shared.Rails) {
++          EP_RAIL *rail = list_entry (el, EP_RAIL_ENTRY, Link)->Rail;
++
++          ep_perrail_unmap (rail, pool->Handle.nmh_nmd.nmd_addr, pool->Handle.nmh_nmd.nmd_len);
++      }
++
++      KMEM_FREEPAGES (pool->Buffer.Ptr, btop (pool->Handle.nmh_nmd.nmd_len));
++      break;
++    }
++    
++    if (pool->Bitmaps[0])
++    {
++      ep_rmfree (alloc->ResourceMap, pool->Handle.nmh_nmd.nmd_len, pool->Handle.nmh_nmd.nmd_addr);
++    
++      for (i = LN2_MIN_SIZE; i <= LN2_MAX_SIZE; i ++)
++          KMEM_FREE (pool->Bitmaps[i - LN2_MIN_SIZE], BT_BITOUL(1 << (LN2_MAX_SIZE - i)) * sizeof (bitmap_t));
++    }
++    
++    KMEM_FREE (pool, sizeof (EP_POOL));
++}
++
++static int
++AddRail (EP_ALLOC *alloc, EP_RAIL *rail)
++{
++    struct list_head *el;
++    EP_RAIL_ENTRY *l;
++    unsigned long flags;
++    int i;
++
++    ASSERT (alloc->Type == EP_ALLOC_TYPE_SHARED_MAIN);
++
++    KMEM_ZALLOC (l, EP_RAIL_ENTRY *, sizeof (EP_RAIL_ENTRY), 1);
++
++    if (l == NULL)
++      return (ENOMEM);
++
++    l->Rail = rail;
++
++    spin_lock_irqsave (&alloc->Lock, flags);
++    for (i = 0; i < NHASH; i++)
++    {
++      list_for_each (el, &alloc->HashBase[i]) {
++          EP_POOL *pool = list_entry (el, EP_POOL, HashBase);
++
++          ep_perrail_kaddr_map (rail, pool->Handle.nmh_nmd.nmd_addr, pool->Buffer.Ptr, 
++                                pool->Handle.nmh_nmd.nmd_len, EP_PERM_WRITE, EP_NO_SLEEP);
++
++          pool->Handle.nmh_nmd.nmd_attr |= EP_NMD_ATTR (0, 1 << rail->Number);
++      }
++    }
++
++    list_add (&l->Link, &alloc->Data.Shared.Rails);
++
++    spin_unlock_irqrestore (&alloc->Lock, flags); 
++    return (0);
++}
++
++static void
++RemoveRail (EP_ALLOC *alloc, EP_RAIL *rail)
++{
++    struct list_head *el;
++    unsigned long flags;
++    int i;
++
++    spin_lock_irqsave (&alloc->Lock, flags);
++    for (i = 0; i < NHASH; i++)
++    {
++      list_for_each (el, &alloc->HashBase[i]) {
++          EP_POOL *pool = list_entry (el, EP_POOL, HashBase);
++
++          ep_perrail_unmap (rail, pool->Handle.nmh_nmd.nmd_addr, pool->Handle.nmh_nmd.nmd_len);
++
++          pool->Handle.nmh_nmd.nmd_attr &= ~EP_NMD_ATTR (0, 1 << rail->Number);
++      }
++    }
++
++    list_for_each (el, &alloc->Data.Shared.Rails) {
++      EP_RAIL_ENTRY *tmp = list_entry (el, EP_RAIL_ENTRY, Link);
++      if (tmp->Rail == rail)
++      {
++          list_del (el);
++          KMEM_FREE(tmp, sizeof (EP_RAIL_ENTRY));
++          break;
++      }
++    }
++
++    spin_unlock_irqrestore (&alloc->Lock, flags);
++}
++
++static EP_POOL *
++AllocateBlock (EP_ALLOC *alloc, unsigned size, EP_ATTRIBUTE attr, int *offset)
++{
++    int block, j, k;
++    unsigned long flags;
++    EP_POOL *pool;
++
++
++    if (size > MAX_SIZE)
++    {
++      if ((attr & EP_NO_ALLOC) || (pool  = AllocatePool (alloc, 0, size, alloc->Perm, attr)) == NULL)
++          return (NULL);
++
++      spin_lock_irqsave (&alloc->Lock, flags);
++      HashInPool (alloc, pool);
++      spin_unlock_irqrestore (&alloc->Lock, flags);
++
++      *offset = 0;
++
++      return pool;
++    }
++
++    spin_lock_irqsave (&alloc->Lock, flags);
++
++    /* Round up size to next power of 2 */
++    for (k = LN2_MIN_SIZE; (1 << k) < size; k++)
++      ;
++    
++    /* k now has ln2 of the size to allocate. */
++    /* find the free list with the smallest block we can use*/
++    for (j = k; j <= LN2_MAX_SIZE && list_empty (&alloc->Freelists[j - LN2_MIN_SIZE]); j++)
++      ;
++    
++    /* j has ln2 of the smallest size block we can use */
++    if (j < LN2_MAX_SIZE)
++    {
++      int nbits = 1 << (LN2_MAX_SIZE-j);
++      
++      pool  = list_entry (alloc->Freelists[j - LN2_MIN_SIZE].next, EP_POOL, Link[j - LN2_MIN_SIZE]);
++      block = (bt_lowbit (pool->Bitmaps[j - LN2_MIN_SIZE], nbits) << j);
++      
++      BT_CLEAR (pool->Bitmaps[j - LN2_MIN_SIZE], block >> j);
++      
++      if (bt_lowbit (pool->Bitmaps[j - LN2_MIN_SIZE], nbits) == -1)
++          list_del (&pool->Link[j - LN2_MIN_SIZE]);
++    }
++    else
++    {
++      spin_unlock_irqrestore (&alloc->Lock, flags);
++      
++      if ((attr & EP_NO_ALLOC) || (pool  = AllocatePool (alloc, 0, MAX_SIZE, alloc->Perm, attr)) == NULL)
++          return (NULL);
++
++      block = 0;
++      j = LN2_MAX_SIZE;
++      
++      spin_lock_irqsave (&alloc->Lock, flags);
++      
++      HashInPool (alloc, pool);
++    }
++    
++    /* Split it until the buddies are the correct size, putting one
++     * buddy back on the free list and continuing to split the other */
++    while (--j >= k)
++    {
++      list_add (&pool->Link[j - LN2_MIN_SIZE], &alloc->Freelists[j - LN2_MIN_SIZE]);
++      
++      BT_SET (pool->Bitmaps[j - LN2_MIN_SIZE], block >> j);
++      
++      block += (1 << j);
++    }
++    spin_unlock_irqrestore (&alloc->Lock, flags);
++
++    *offset = block;
++
++    return (pool);
++}
++
++static void
++FreeBlock (EP_ALLOC *alloc, EP_ADDR addr, unsigned size)
++{
++    EP_POOL *pool;
++    int  k, block = 0;
++    unsigned long flags;
++    
++    spin_lock_irqsave (&alloc->Lock, flags);
++    /* Round up size to next power of 2 */
++    for (k = LN2_MIN_SIZE; (1 << k) < size; k++)
++      ;
++
++    /* Find the pool containing this block */
++    pool = LookupPool (alloc, addr);
++
++    /* It must exist */
++    ASSERT (pool != NULL);
++
++    /* If we're freeing a subset of it, then update the bitmaps */
++    if (size <= MAX_SIZE)
++    {
++      ASSERT (BT_TEST (pool->Bitmaps[k - LN2_MIN_SIZE], (addr - pool->Handle.nmh_nmd.nmd_addr) >> k) == 0);
++      
++      block = addr - pool->Handle.nmh_nmd.nmd_addr;
++      
++      while (k < LN2_MAX_SIZE && BT_TEST (pool->Bitmaps[k - LN2_MIN_SIZE], (block >> k) ^ 1))
++      {
++          BT_CLEAR (pool->Bitmaps[k - LN2_MIN_SIZE], (block >> k) ^ 1);
++          
++          if (bt_lowbit (pool->Bitmaps[k - LN2_MIN_SIZE], (1 << (LN2_MAX_SIZE - k))) == -1)
++              list_del (&pool->Link[k - LN2_MIN_SIZE]);
++          
++          k++;
++      }
++    }
++
++    if (k >= LN2_MAX_SIZE)
++    {
++      HashOutPool (alloc, pool);
++      spin_unlock_irqrestore (&alloc->Lock, flags);
++
++      FreePool (alloc, pool);
++    }
++    else
++    {
++      if (bt_lowbit (pool->Bitmaps[k - LN2_MIN_SIZE], (1 << (LN2_MAX_SIZE - k))) == -1)
++          list_add (&pool->Link[k - LN2_MIN_SIZE], &alloc->Freelists[k - LN2_MIN_SIZE]);
++
++      BT_SET (pool->Bitmaps[k - LN2_MIN_SIZE], block >> k);
++
++      spin_unlock_irqrestore (&alloc->Lock, flags);
++    }
++}
++
++static void
++InitialiseAllocator (EP_ALLOC *alloc, EP_ALLOC_TYPE type, unsigned int perm, EP_RMAP *rmap)
++{
++    int i;
++
++    spin_lock_init (&alloc->Lock);
++
++    alloc->Type        = type;
++    alloc->ResourceMap = rmap;
++    alloc->Perm        = perm;
++
++    for (i = 0; i < NHASH; i++)
++    {
++      (&alloc->HashBase[i])->next = &alloc->HashBase[i];
++
++      INIT_LIST_HEAD (&alloc->HashBase[i]);
++      INIT_LIST_HEAD (&alloc->HashTop[i]);
++    }
++    
++    for (i = 0; i < NUM_FREELISTS; i++)
++      INIT_LIST_HEAD (&alloc->Freelists[i]);
++}
++
++static void
++DestroyAllocator (EP_ALLOC *alloc)
++{
++    struct list_head *el, *next;
++    int i;
++
++    for (i = 0; i < NHASH; i++)
++    {
++      list_for_each_safe (el, next, &alloc->HashBase[i]) { 
++          EP_POOL *pool = list_entry (el, EP_POOL, HashBase);
++
++          printk ("!!DestroyAllocator: pool=%p type=%d addr=%x len=%x\n", pool, alloc->Type,
++                  pool->Handle.nmh_nmd.nmd_addr, pool->Handle.nmh_nmd.nmd_len);
++
++          list_del (&pool->HashBase);
++          list_del (&pool->HashTop);
++
++          // XXXX: FreePool (alloc, pool);
++      }
++    }
++
++    spin_lock_destroy (&alloc->Lock);
++}
++
++void
++ep_display_alloc (EP_ALLOC *alloc)
++{
++    struct list_head *el;
++    int i;
++    int npools = 0;
++    int nbytes = 0;
++    int nfree = 0;
++    unsigned long flags;
++
++    spin_lock_irqsave (&alloc->Lock, flags);
++
++    ep_debugf (DBG_DEBUG, "Kernel comms memory allocator %p type %d\n", alloc, alloc->Type);
++    for (i = 0; i < NHASH; i++)
++    {
++      list_for_each (el, &alloc->HashBase[i]) {
++          EP_POOL *pool = list_entry (el, EP_POOL, HashBase);
++
++          ep_debugf (DBG_DEBUG, "  POOL %4x: %p -> %x.%x\n", i, pool, pool->Handle.nmh_nmd.nmd_addr,
++                     pool->Handle.nmh_nmd.nmd_addr + pool->Handle.nmh_nmd.nmd_len);
++
++          npools++;
++          nbytes += pool->Handle.nmh_nmd.nmd_len;
++      }
++    }
++    
++    for (i = LN2_MIN_SIZE; i <= LN2_MAX_SIZE; i++)
++    {
++      int n = 0;
++
++      list_for_each (el, &alloc->Freelists[i - LN2_MIN_SIZE]) {
++          EP_POOL *pool  = list_entry (el, EP_POOL, Link[i - LN2_MIN_SIZE]);
++          int      nbits = bt_nbits (pool->Bitmaps[i - LN2_MIN_SIZE], 1 << (LN2_MAX_SIZE - i));
++
++          n += nbits;
++          nfree += (nbits << i);
++      }
++      
++      if (n != 0)
++          ep_debugf (DBG_DEBUG, "  SIZE %5d : num %d\n", (1 << i), n);
++    }
++    ep_debugf (DBG_DEBUG, "%d pools with %d bytes and %d bytes free\n", npools, nbytes, nfree);
++
++    spin_unlock_irqrestore (&alloc->Lock, flags);
++}
++
++/* per-rail allocators */
++void
++ep_alloc_init (EP_RAIL *rail)
++{
++    EP_RMAP *rmap = ep_rmallocmap (EP_PRIVATE_RMAP_SIZE, "PrivateMap", 1);
++
++    ep_rmfree (rmap, EP_PRIVATE_TOP-EP_PRIVATE_BASE, EP_PRIVATE_BASE);
++
++    InitialiseAllocator (&rail->ElanAllocator, EP_ALLOC_TYPE_PRIVATE_SDRAM, EP_PERM_ALL, rmap);
++    InitialiseAllocator (&rail->MainAllocator, EP_ALLOC_TYPE_PRIVATE_MAIN, EP_PERM_WRITE, rmap);
++
++    rail->ElanAllocator.Data.Private.Rail = rail;
++    rail->MainAllocator.Data.Private.Rail = rail;
++}
++
++void
++ep_alloc_fini (EP_RAIL *rail)
++{
++    EP_RMAP *rmap = rail->ElanAllocator.ResourceMap;
++
++    DestroyAllocator (&rail->ElanAllocator);
++    DestroyAllocator (&rail->MainAllocator);
++    
++    ep_rmfreemap (rmap);
++}
++
++sdramaddr_t
++ep_alloc_memory_elan (EP_RAIL *rail, EP_ADDR addr, unsigned size, unsigned int perm, EP_ATTRIBUTE attr)
++{
++    EP_POOL *pool = AllocatePool (&rail->ElanAllocator, addr, size, perm, attr);
++    unsigned long flags;
++
++    if (pool == NULL)
++      return (0);
++
++    spin_lock_irqsave (&rail->ElanAllocator.Lock, flags);
++    HashInPool (&rail->ElanAllocator, pool);
++    spin_unlock_irqrestore (&rail->ElanAllocator.Lock, flags);
++
++    return (pool->Buffer.Sdram);
++}
++
++void
++ep_free_memory_elan (EP_RAIL *rail, EP_ADDR addr)
++{
++    EP_POOL *pool;
++    unsigned long flags;
++
++    spin_lock_irqsave (&rail->ElanAllocator.Lock, flags);
++    pool = LookupPool (&rail->ElanAllocator, addr);
++    
++    HashOutPool (&rail->ElanAllocator, pool);
++    spin_unlock_irqrestore (&rail->ElanAllocator.Lock, flags);
++    
++    FreePool (&rail->ElanAllocator, pool);
++}
++
++sdramaddr_t
++ep_alloc_elan (EP_RAIL *rail, unsigned size, EP_ATTRIBUTE attr, EP_ADDR *addrp)
++{
++    int            offset;
++    EP_POOL *pool;
++
++    if ((pool = AllocateBlock (&rail->ElanAllocator, size, attr, &offset)) == NULL)
++      return (0);
++    
++    *addrp  = pool->Handle.nmh_nmd.nmd_addr + offset;
++
++    return (pool->Buffer.Sdram + offset);
++}
++
++void
++ep_free_elan (EP_RAIL *rail, EP_ADDR addr, unsigned size)
++{
++    FreeBlock (&rail->ElanAllocator, addr, size);
++}
++
++void *
++ep_alloc_main (EP_RAIL *rail, unsigned size, EP_ATTRIBUTE attr, EP_ADDR *addrp)
++{
++    int            offset;
++    EP_POOL *pool;
++
++    if ((pool = AllocateBlock (&rail->MainAllocator, size, attr, &offset)) == NULL)
++      return (NULL);
++    
++    *addrp  = pool->Handle.nmh_nmd.nmd_addr + offset;
++
++    return ((void *) ((unsigned long) pool->Buffer.Ptr + offset));
++}
++
++void
++ep_free_main (EP_RAIL *rail, EP_ADDR addr, unsigned size)
++{
++    FreeBlock (&rail->MainAllocator, addr, size);
++}
++
++sdramaddr_t
++ep_elan2sdram (EP_RAIL *rail, EP_ADDR addr)
++{
++    EP_POOL    *pool;
++    sdramaddr_t res;
++    unsigned long flags;
++
++    spin_lock_irqsave (&rail->ElanAllocator.Lock, flags);
++    if ((pool = LookupPool (&rail->ElanAllocator, addr)) == NULL)
++      res = 0;
++    else
++      res = pool->Buffer.Sdram + (addr - pool->Handle.nmh_nmd.nmd_addr);
++    spin_unlock_irqrestore (&rail->ElanAllocator.Lock, flags);
++
++    return (res);
++}
++
++void *
++ep_elan2main (EP_RAIL *rail, EP_ADDR addr)
++{
++    EP_POOL *pool;
++    void *res;
++    unsigned long flags;
++
++    spin_lock_irqsave (&rail->MainAllocator.Lock, flags);
++    if ((pool = LookupPool (&rail->MainAllocator, addr)) == NULL)
++      res = NULL;
++    else
++      res = (void *) ((unsigned long) pool->Buffer.Ptr + (addr - pool->Handle.nmh_nmd.nmd_addr));
++    spin_unlock_irqrestore (&rail->MainAllocator.Lock, flags);
++
++    return (res);
++}
++
++/* shared allocators */
++int
++ep_shared_alloc_add_rail (EP_SYS *sys, EP_RAIL *rail)
++{
++    return (AddRail (&sys->Allocator, rail));
++}
++
++void
++ep_shared_alloc_remove_rail (EP_SYS *sys, EP_RAIL *rail)
++{
++    RemoveRail (&sys->Allocator, rail);
++}
++
++void
++ep_shared_alloc_init (EP_SYS *sys)
++{
++    EP_RMAP *rmap = ep_rmallocmap (EP_SHARED_RMAP_SIZE, "shared_alloc_map", 1);
++
++    ep_rmfree (rmap, EP_SHARED_TOP - EP_SHARED_BASE, EP_SHARED_BASE);
++
++    InitialiseAllocator (&sys->Allocator, EP_ALLOC_TYPE_SHARED_MAIN, EP_PERM_WRITE, rmap);
++
++    INIT_LIST_HEAD (&sys->Allocator.Data.Shared.Rails);
++
++    sys->Allocator.Data.Shared.System = sys;
++}
++
++void
++ep_shared_alloc_fini (EP_SYS *sys)
++{
++    EP_RMAP *rmap = sys->Allocator.ResourceMap;
++
++    DestroyAllocator (&sys->Allocator);
++
++    ep_rmfreemap (rmap);
++}
++
++void *
++ep_shared_alloc_main (EP_SYS *sys, unsigned size, EP_ATTRIBUTE attr, EP_NMD *nmd)
++{
++    int offset;
++    EP_POOL *pool;
++
++    if ((pool = AllocateBlock (&sys->Allocator, size, attr, &offset)) == NULL)
++      return (NULL);
++
++    ep_nmd_subset (nmd, &pool->Handle.nmh_nmd, offset, size);
++
++    return ((void *) ((unsigned long) pool->Buffer.Ptr + offset));
++}
++
++void
++ep_shared_free_main (EP_SYS *sys, EP_NMD *nmd)
++{
++    FreeBlock (&sys->Allocator, nmd->nmd_addr, nmd->nmd_len);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/ep/kcomm.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/kcomm.c      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/kcomm.c   2005-05-11 12:10:12.510921632 -0400
+@@ -0,0 +1,1448 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: kcomm.c,v 1.50.2.9 2004/12/09 10:02:42 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/kcomm.c,v $ */
++
++#include <qsnet/kernel.h>
++#include <qsnet/kthread.h>
++
++#include <elan/kcomm.h>
++#include <elan/epsvc.h>
++#include <elan/epcomms.h>
++
++#include "cm.h"
++#include "debug.h"
++
++int MaxSwitchLevels = 5;                              /* Max 1024 sized machine */
++
++static char *NodeStateNames[EP_NODE_NUM_STATES] = 
++{
++    "Disconnected",
++    "Connecting",
++    "Connnected",
++    "LeavingConnected",
++    "LocalPassivate",
++    "RemotePassivate",
++    "Passivated",
++    "Disconnecting",
++};
++
++static void
++ep_xid_cache_fill (EP_SYS *sys, EP_XID_CACHE *cache)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&sys->XidLock, flags);
++
++    cache->Current = sys->XidNext;
++    cache->Last    = cache->Current + EP_XID_CACHE_CHUNKS-1;
++
++    sys->XidNext += EP_XID_CACHE_CHUNKS;
++
++    spin_unlock_irqrestore (&sys->XidLock, flags);
++}
++
++EP_XID
++ep_xid_cache_alloc (EP_SYS *sys, EP_XID_CACHE *cache)
++{
++    EP_XID xid;
++    
++    if (cache->Current == cache->Last)
++      ep_xid_cache_fill (sys, cache);
++
++    xid.Generation = sys->XidGeneration;
++    xid.Handle     = cache->Handle;
++    xid.Unique     = cache->Current++;
++
++    return (xid);
++}
++
++void
++ep_xid_cache_init (EP_SYS *sys, EP_XID_CACHE *cache)
++{
++    /* Stall manager thread - it doesn't lock the XidCacheList */
++    ep_kthread_stall (&sys->ManagerThread);
++
++    cache->Handle = ++sys->XidHandle;
++
++    list_add_tail (&cache->Link, &sys->XidCacheList);
++
++    ep_kthread_resume (&sys->ManagerThread);
++}
++
++void
++ep_xid_cache_destroy (EP_SYS *sys, EP_XID_CACHE *cache)
++{
++    /* Stall manager thread - it doesn't lock the XidCacheList */
++    ep_kthread_stall (&sys->ManagerThread);
++
++    list_del (&cache->Link);
++
++    ep_kthread_resume (&sys->ManagerThread);
++}
++
++EP_XID_CACHE *
++ep_xid_cache_find (EP_SYS *sys, EP_XID xid)
++{
++    struct list_head *el;
++
++    list_for_each (el, &sys->XidCacheList) {
++      EP_XID_CACHE *cache = list_entry (el, EP_XID_CACHE, Link);
++
++      if (sys->XidGeneration == xid.Generation && cache->Handle == xid.Handle)
++          return (cache);
++    }
++
++    return (NULL);
++}
++
++static int
++MsgBusy (EP_RAIL *rail, EP_OUTPUTQ *outputq, int slotNum)
++{
++    switch (rail->Operations.OutputQState (rail, outputq, slotNum))
++    {
++    case EP_OUTPUTQ_BUSY:                     /* still busy */
++      return 1;
++      
++    case EP_OUTPUTQ_FAILED:                   /* NACKed */
++    {
++#if defined(DEBUG_PRINTF)
++      EP_MANAGER_MSG *msg = rail->Operations.OutputQMsg (rail, outputq, slotNum);
++
++      EPRINTF4 (DBG_MANAGER, "%s: kcomm msg %d type %d to %d failed\n", rail->Name, slotNum, msg->Hdr.Type, msg->Hdr.DestId);
++#endif
++      break;
++    }
++    
++    case EP_OUTPUTQ_FINISHED:                 /* anything else is finished */
++      break;
++    }
++
++    return 0;
++}
++
++int
++ep_send_message (EP_RAIL *rail, int nodeId, int type, EP_XID xid, EP_MANAGER_MSG_BODY *body)
++{
++    EP_SYS         *sys  = rail->System;
++    EP_NODE        *node = &sys->Nodes[nodeId];
++    int             n    = EP_MANAGER_OUTPUTQ_SLOTS;
++    int             slotNum;
++    int             rnum;
++    EP_RAIL        *msgRail;
++    EP_MANAGER_MSG *msg;
++    unsigned long   flags;
++
++    ASSERT (! EP_XID_INVALID (xid));
++
++    if ((rnum = ep_pickRail (node->ConnectedRails)) >= 0)
++      msgRail = sys->Rails[rnum];
++    else
++    {
++      if (EP_MANAGER_MSG_TYPE_CONNECTED(type))
++      {
++          ep_debugf (DBG_MANAGER, "%s: no rails available, trying to send type %d to %d\n", rail->Name, type, nodeId);
++          return -EHOSTDOWN;
++      }
++
++      ep_debugf (DBG_MANAGER, "%s: no rails connected to %d - using receiving rail\n", rail->Name, nodeId);
++
++      msgRail = rail;
++    }
++    
++
++    spin_lock_irqsave (&msgRail->ManagerOutputQLock, flags);
++
++    slotNum = msgRail->ManagerOutputQNextSlot;
++
++    while (n-- > 0 && MsgBusy (msgRail, msgRail->ManagerOutputQ, slotNum))            /* search for idle message buffer */
++    {
++      if (++(msgRail->ManagerOutputQNextSlot) == EP_MANAGER_OUTPUTQ_SLOTS)
++          msgRail->ManagerOutputQNextSlot = 0;
++      
++      slotNum = msgRail->ManagerOutputQNextSlot;
++    }
++
++    if (n == 0)                                                       /* all message buffers busy */
++    {
++      spin_unlock_irqrestore (&msgRail->ManagerOutputQLock, flags);
++
++      ep_debugf (DBG_MANAGER, "%s: all message buffers busy: trying to send type %d to %d\n", msgRail->Name, type, nodeId);
++      return -EBUSY;
++    }
++
++    msg = msgRail->Operations.OutputQMsg (msgRail, msgRail->ManagerOutputQ, slotNum);
++    
++    EPRINTF7 (DBG_MANAGER, "%s: ep_send_message: type=%d nodeId=%d rail=%d xid=%08x.%08x.%016llx\n", 
++            msgRail->Name, type, nodeId, rail->Number, xid.Generation, xid.Handle, (long long) xid.Unique);
++
++    msg->Hdr.Version    = EP_MANAGER_MSG_VERSION;
++    msg->Hdr.Type       = type;
++    msg->Hdr.Rail       = rail->Number;
++    msg->Hdr.NodeId     = msgRail->Position.pos_nodeid;
++    msg->Hdr.DestId     = nodeId;
++    msg->Hdr.Xid        = xid;
++    msg->Hdr.Checksum   = 0;
++
++    if (body) bcopy (body, &msg->Body, sizeof (EP_MANAGER_MSG_BODY));
++
++    msg->Hdr.Checksum = CheckSum ((char *) msg, EP_MANAGER_MSG_SIZE);
++
++    if (rail->Operations.OutputQSend (msgRail, msgRail->ManagerOutputQ, slotNum, EP_MANAGER_MSG_SIZE,
++                                    nodeId, EP_SYSTEMQ_MANAGER, EP_MANAGER_OUTPUTQ_RETRIES) < 0)
++      IncrStat (msgRail, SendMessageFailed);
++    
++    if (++(msgRail->ManagerOutputQNextSlot) == EP_MANAGER_OUTPUTQ_SLOTS) /* check this one last next time */
++      msgRail->ManagerOutputQNextSlot = 0;
++
++    spin_unlock_irqrestore (&msgRail->ManagerOutputQLock, flags);
++
++    return 0;
++}
++
++void
++ep_panic_node (EP_SYS *sys, int nodeId, unsigned char *reason)
++{
++    EP_NODE            *node = &sys->Nodes[nodeId];
++    EP_MANAGER_MSG_BODY body;
++    EP_XID              xid;
++    kcondvar_t          sleep;
++    int                 rnum;
++    unsigned long       flags;
++
++    if (nodeId > sys->Position.pos_nodes)
++      return;
++
++    strncpy (body.PanicReason, reason, sizeof (body.PanicReason));
++
++    kcondvar_init (&sleep);
++    spin_lock_irqsave (&sys->NodeLock, flags);
++    for (;;)
++    {
++      if (node->ConnectedRails == 0)
++          break;
++
++      for (rnum = 0; rnum < EP_MAX_RAILS; rnum++)
++          if (node->ConnectedRails & (1 << rnum))
++              break;
++
++      xid = ep_xid_cache_alloc(sys, &sys->Rails[rnum]->XidCache);
++      
++      if (ep_send_message (sys->Rails[rnum], nodeId, EP_MANAGER_MSG_TYPE_REMOTE_PANIC, xid, &body) == 0)
++          break;
++
++      if (kcondvar_timedwaitsig (&sleep, &sys->NodeLock, &flags, lbolt + hz) == CV_RET_SIGPENDING)
++          break;
++    }
++    spin_unlock_irqrestore (&sys->NodeLock, flags);
++    kcondvar_destroy (&sleep);
++}
++
++static void
++ProcessNeterrRequest (EP_RAIL *msgRail, EP_RAIL *rail, EP_MANAGER_MSG *msg)
++{
++    EPRINTF4 (DBG_NETWORK_ERROR, "%s: process neterr request - node %d cookies %llx %llx\n", rail->Name, msg->Hdr.NodeId, msg->Body.Cookies[0], msg->Body.Cookies[1]);
++
++    rail->Operations.NeterrFixup (rail, msg->Hdr.NodeId, msg->Body.Cookies);
++    
++    ep_send_message (rail, msg->Hdr.NodeId, EP_MANAGER_MSG_TYPE_NETERR_RESPONSE, msg->Hdr.Xid, &msg->Body);
++}
++
++
++static void
++ProcessNeterrResponse (EP_RAIL *msgRail, EP_RAIL *rail, EP_MANAGER_MSG *msg)
++{
++    EP_SYS       *sys      = rail->System;
++    EP_NODE_RAIL *nodeRail = &rail->Nodes[msg->Hdr.NodeId];
++    unsigned long flags;
++
++    EPRINTF4 (DBG_NETWORK_ERROR, "%s: process neterr response - node %d cookies %llx %llx\n", rail->Name, msg->Hdr.NodeId, msg->Body.Cookies[0], msg->Body.Cookies[1]);
++
++    spin_lock_irqsave (&sys->NodeLock, flags);
++    if (EP_XIDS_MATCH (nodeRail->MsgXid, msg->Hdr.Xid))
++    {
++      EP_INVALIDATE_XID (nodeRail->MsgXid);
++
++      if (nodeRail->NetworkErrorCookies[0] != 0 && nodeRail->NetworkErrorCookies[0] == msg->Body.Cookies[0])
++          nodeRail->NetworkErrorCookies[0] = 0;
++
++      if (nodeRail->NetworkErrorCookies[1] != 0 && nodeRail->NetworkErrorCookies[1] == msg->Body.Cookies[1])
++          nodeRail->NetworkErrorCookies[1] = 0;
++      
++      if (nodeRail->NetworkErrorCookies[0] == 0 && nodeRail->NetworkErrorCookies[1] == 0)
++          nodeRail->NetworkErrorState &= ~EP_NODE_NETERR_ATOMIC_PACKET;
++    }
++    spin_unlock_irqrestore (&sys->NodeLock, flags);
++}
++
++
++static void
++ProcessGetNodeState (EP_RAIL *msgRail, EP_RAIL *rail, EP_MANAGER_MSG *msg)
++{
++    EP_NODE_RAIL *nodeRail = &rail->Nodes[msg->Hdr.NodeId];
++    unsigned int service = msg->Body.Service;
++
++    EPRINTF5 (DBG_MANAGER, "%s: ProcessGetNodeState: %s - %d %s%s\n",  msgRail->Name, rail->Name, msg->Hdr.NodeId,
++            NodeStateNames[nodeRail->State], nodeRail->NetworkErrorState ? " (NetworkError)" : "");
++
++    msg->Body.NodeState.State             = nodeRail->State;
++    msg->Body.NodeState.NetworkErrorState = nodeRail->NetworkErrorState;
++    msg->Body.NodeState.Railmask          = ep_rcvr_railmask (rail->System, service);
++
++    if (ep_send_message (rail, msg->Hdr.NodeId, EP_MANAGER_MSG_TYPE_GET_NODE_STATE_RESPONSE, msg->Hdr.Xid, &msg->Body) < 0)
++      printk ("%s: get node state for %s[%d] - failed to send response\n", msgRail->Name, rail->Name, msg->Hdr.NodeId);
++}
++
++static void
++ProcessFlushRequest (EP_RAIL *msgRail, EP_RAIL *rail, EP_MANAGER_MSG *msg)
++{
++    EP_NODE_RAIL *nodeRail = &rail->Nodes[msg->Hdr.NodeId];
++
++    EPRINTF5 (DBG_MANAGER, "%s: ProcessFlushRequest: %s - %d %s%s\n",  msgRail->Name, rail->Name, msg->Hdr.NodeId,
++            NodeStateNames[nodeRail->State], nodeRail->NetworkErrorState ? " (NetworkError)" : "");
++
++    switch (nodeRail->State)
++    {
++    case EP_NODE_REMOTE_PASSIVATE:
++      nodeRail->NextRunTime = lbolt + MSGBUSY_RETRY_TIME;     /* retransmit our flush request quickly */
++      EPRINTF3 (DBG_MANAGER, "%s: ProcessFlushRequest: NextRunTime -> %lx (%lx)\n", rail->Name, nodeRail->NextRunTime, lbolt);
++      /* DROPTHROUGH */
++
++    case EP_NODE_PASSIVATED:
++    case EP_NODE_DISCONNECTED:
++      if (nodeRail->NetworkErrorState != 0)
++          break;
++
++      if (ep_send_message (rail, msg->Hdr.NodeId, EP_MANAGER_MSG_TYPE_FLUSH_RESPONSE, msg->Hdr.Xid, NULL) < 0)
++          printk ("%s: flush request for %s[%d] - failed to send response\n", msgRail->Name, rail->Name, msg->Hdr.NodeId);
++      break;
++      
++    default:
++      EPRINTF4 (DBG_MANAGER, "%s: flush request for %s[%d] - node not in approriate state - %s\n", msgRail->Name, rail->Name, msg->Hdr.NodeId, NodeStateNames[nodeRail->State]);
++      break;
++    }
++}
++
++static void
++ProcessFlushResponse (EP_RAIL *msgRail, EP_RAIL *rail, EP_MANAGER_MSG *msg)
++{
++    EP_NODE_RAIL *nodeRail= &rail->Nodes[msg->Hdr.NodeId];
++
++    EPRINTF5 (DBG_MANAGER, "%s: ProcessFlushResponse: %s - %d %s%s\n",  msgRail->Name, rail->Name, msg->Hdr.NodeId,
++            NodeStateNames[nodeRail->State], EP_XIDS_MATCH (nodeRail->MsgXid, msg->Hdr.Xid) ? " (XIDS match)" : "");
++
++    if (nodeRail->State == EP_NODE_REMOTE_PASSIVATE && EP_XIDS_MATCH(nodeRail->MsgXid, msg->Hdr.Xid))
++    {
++      EP_INVALIDATE_XID (nodeRail->MsgXid);
++
++      printk ("%s: flush response from %d - move to passivated list\n", rail->Name, msg->Hdr.NodeId);
++      list_del (&nodeRail->Link);
++
++      /* Node is now passivated - attempt to failover  messages */
++      list_add_tail (&nodeRail->Link, &rail->PassivatedList);
++      nodeRail->State = EP_NODE_PASSIVATED;
++    }
++    else
++    {
++      printk ("%s: flush response from %d - not passivating (%s) or XIDs mismatch (%llx %llx)\n", rail->Name, 
++              msg->Hdr.NodeId, NodeStateNames[nodeRail->State], (long long) nodeRail->MsgXid.Unique, (long long) msg->Hdr.Xid.Unique);
++    }
++}
++
++static void
++ProcessMapNmdRequest (EP_RAIL *msgRail, EP_RAIL *rail, EP_MANAGER_MSG *msg)
++{
++    EP_SYS          *sys     = rail->System;
++    EP_MAP_NMD_BODY *msgBody = &msg->Body.MapNmd;
++    int              i;
++
++    EPRINTF4 (DBG_MANAGER, "%s: Map NMD request from %d for %d NMDs to railmask %x\n", rail->Name, msg->Hdr.NodeId, msgBody->nFrags, msgBody->Railmask);
++    
++    for (i = 0; i < msgBody->nFrags; i++)
++      ep_nmd_map_rails (sys, &msgBody->Nmd[i], msgBody->Railmask);
++    
++    /* Must flush TLBs before responding */
++    for (i = 0; i < EP_MAX_RAILS; i++)
++      if (sys->Rails[i] && sys->Rails[i]->TlbFlushRequired)
++          ep_perrail_dvma_sync (sys->Rails[i]);
++
++    if (ep_send_message (rail, msg->Hdr.NodeId, EP_MANAGER_MSG_TYPE_MAP_NMD_RESPONSE, msg->Hdr.Xid, &msg->Body) < 0)
++      printk ("%s: map nmd request for %s[%d] - failed to send response\n", msgRail->Name, rail->Name, msg->Hdr.NodeId);
++}
++
++static void
++ProcessXidMessage (EP_RAIL *msgRail, EP_MANAGER_MSG *msg, EP_XID xid)
++{
++    EP_XID_CACHE *xidCache = ep_xid_cache_find (msgRail->System, xid);
++
++    EPRINTF6 (DBG_MANAGER, "%s: ProcessXidMessage: XID=%08x.%0x8.%016llx -> %p(%p)\n",
++            msgRail->Name, xid.Generation, xid.Handle, (long long) xid.Unique,
++            xidCache  ? xidCache->MessageHandler : 0, xidCache  ? xidCache->Arg : 0);
++    
++    if (xidCache != NULL)
++      xidCache->MessageHandler (xidCache->Arg, msg);
++}
++
++static void
++ProcessMessage (EP_RAIL *msgRail, void *arg, void *msgbuf)
++{
++    EP_SYS         *sys  = msgRail->System;
++    EP_MANAGER_MSG *msg  = (EP_MANAGER_MSG *) msgbuf;
++    uint16_t        csum = msg->Hdr.Checksum;
++    EP_RAIL        *rail;
++
++    if (msg->Hdr.Version != EP_MANAGER_MSG_VERSION)
++      return;
++
++    msg->Hdr.Checksum= 0;
++    if (CheckSum ((char *) msg, EP_MANAGER_MSG_SIZE) != csum)
++    {
++      printk ("%s: checksum failed on msg from %d (%d) (%x != %x) ?\n", msgRail->Name, msg->Hdr.NodeId, msg->Hdr.Type, csum, CheckSum ((char *) msg, EP_MANAGER_MSG_SIZE));
++      return;
++    }
++
++    if ((rail = sys->Rails[msg->Hdr.Rail]) == NULL)
++    {
++      printk ("%s: rail no longer exists for msg from %d?\n", msgRail->Name, msg->Hdr.NodeId);
++      return;
++    }
++
++    EPRINTF7 (DBG_MANAGER, "%s: ProcessMessage (%s) type=%d node=%d XID=%08x.%0x8.%016llx\n", 
++            msgRail->Name, rail->Name, msg->Hdr.Type, msg->Hdr.NodeId,
++            msg->Hdr.Xid.Generation, msg->Hdr.Xid.Handle, msg->Hdr.Xid.Unique);
++
++    switch (msg->Hdr.Type)
++    {
++    case EP_MANAGER_MSG_TYPE_REMOTE_PANIC:
++      msg->Body.PanicReason[EP_PANIC_STRLEN] = '\0';          /* ensure string terminated */
++
++      printk ("%s: remote panic call from elan node %d - %s\n", msgRail->Name, msg->Hdr.NodeId, msg->Body.PanicReason);
++      panic ("ep: remote panic request\n");
++      break;
++
++    case EP_MANAGER_MSG_TYPE_NETERR_REQUEST:
++      ProcessNeterrRequest (msgRail, rail, msg);
++      break;
++
++    case EP_MANAGER_MSG_TYPE_NETERR_RESPONSE:
++      ProcessNeterrResponse (msgRail, rail, msg);
++      break;
++
++    case EP_MANAGER_MSG_TYPE_FLUSH_REQUEST:
++      ProcessFlushRequest (msgRail, rail, msg);
++      break;
++
++    case EP_MANAGER_MSG_TYPE_FLUSH_RESPONSE:
++      ProcessFlushResponse (msgRail, rail, msg);
++      break;
++
++    case EP_MANAGER_MSG_TYPE_MAP_NMD_REQUEST:
++      ProcessMapNmdRequest (msgRail, rail, msg);
++      break;
++
++    case EP_MANAGER_MSG_TYPE_MAP_NMD_RESPONSE:
++      ProcessXidMessage (msgRail, msg, msg->Hdr.Xid);
++      break;
++
++    case EP_MANAGER_MSG_TYPE_FAILOVER_REQUEST:
++      ProcessXidMessage (msgRail, msg, msg->Body.Failover.Xid);
++      break;
++
++    case EP_MANAGER_MSG_TYPE_FAILOVER_RESPONSE:
++      ProcessXidMessage (msgRail, msg, msg->Hdr.Xid);
++      break;
++       
++    case EP_MANAGER_MSG_TYPE_GET_NODE_STATE:
++      ProcessGetNodeState (msgRail, rail, msg);
++      break;
++
++    case EP_MANAGER_MSG_TYPE_GET_NODE_STATE_RESPONSE: 
++      ProcessXidMessage (msgRail, msg, msg->Hdr.Xid);
++      break;
++
++    default:
++      printk ("%s: Unknown message type %d from %d\n", msgRail->Name, msg->Hdr.Type, msg->Hdr.NodeId);
++      break;
++    }
++}
++
++
++static void
++ManagerQueueEvent (EP_RAIL *rail, void *arg)
++{
++    ep_kthread_schedule ((EP_KTHREAD *) arg, lbolt);
++}
++
++void
++UpdateConnectionState (EP_RAIL *rail, statemap_t *map)
++{
++    EP_SYS *sys = rail->System;
++    bitmap_t seg;
++    int offset, nodeId;
++    unsigned long flags;
++    
++    while ((offset = statemap_findchange (map, &seg, 1)) >= 0)
++    {
++      for (nodeId = offset; nodeId < (offset + BT_NBIPUL) && nodeId < rail->Position.pos_nodes; nodeId++)
++      {
++          EP_NODE      *node     = &sys->Nodes[nodeId];
++          EP_NODE_RAIL *nodeRail = &rail->Nodes[nodeId];
++
++          if (statemap_getbits (map, nodeId, 1))
++          {
++              spin_lock_irqsave (&sys->NodeLock, flags);
++
++              switch (nodeRail->State)
++              {
++              case EP_NODE_DISCONNECTED:
++                  EPRINTF2 (DBG_MANAGER, "%s: Node %d -> Disconnected \n", rail->Name, nodeId);
++                  break;
++
++              case EP_NODE_CONNECTING:
++                  EPRINTF2 (DBG_MANAGER, "%s: Node %d -> Connect\n", rail->Name, nodeId);
++                  
++                  /* load the route table entry *before*  setting the state
++                   * to connected, since DMA's can be initiated as soon as
++                   * the node is marked as connected */
++                  rail->Operations.LoadNodeRoute (rail, nodeId);
++                  
++                  nodeRail->State = EP_NODE_CONNECTED;
++                  
++                  statemap_setbits (rail->NodeSet, nodeId, 1, 1);
++                  if (statemap_getbits (sys->NodeSet, nodeId, 1) == 0)
++                      statemap_setbits (sys->NodeSet, nodeId, 1, 1);
++
++                  /* Add to rails connected to this node */
++                  node->ConnectedRails |= (1 << rail->Number);
++
++                  /* Finally lower the per-node context filter */
++                  rail->Operations.LowerFilter (rail, nodeId);
++                  break;
++                  
++              case EP_NODE_LEAVING_CONNECTED:
++                  EPRINTF2 (DBG_MANAGER, "%s: Node %d -> Local Passivate\n", rail->Name, nodeId);
++                  
++                  /* Raise the per-node context filter */
++                  rail->Operations.RaiseFilter (rail, nodeId);
++
++                  /* If it's resolving network errors it will be on the NodeNeterrList,
++                   * remove if from this list before placing it on the LocalPassivateList
++                   * as we'll resolve the network error later in RemotePassivate */
++                  if (nodeRail->NetworkErrorState)
++                      list_del (&nodeRail->Link);
++
++                  list_add_tail (&nodeRail->Link, &rail->LocalPassivateList);
++                  nodeRail->State = EP_NODE_LOCAL_PASSIVATE;
++
++                  /* Remove from rails connected to this node */
++                  node->ConnectedRails &= ~(1 << rail->Number);
++                  break;
++
++              default:
++                  printk ("%s: Node %d - in NodeChangeMap with state %d\n", rail->Name, nodeId, nodeRail->State);
++                  panic ("Node in NodeChangeMap with invalid state\n");
++                  break;
++              }
++              spin_unlock_irqrestore (&sys->NodeLock, flags);
++          }
++      }
++    }
++}
++
++void
++ProgressNetworkError (EP_RAIL *rail, EP_NODE_RAIL *nodeRail)
++{
++    EP_SYS             *sys    = rail->System;
++    int                 nodeId = nodeRail - rail->Nodes;
++    EP_MANAGER_MSG_BODY msg;
++
++    ASSERT (nodeRail->State >= EP_NODE_CONNECTED && nodeRail->State <= EP_NODE_REMOTE_PASSIVATE);
++
++    if (BEFORE (lbolt, nodeRail->NextRunTime))
++      return;
++
++    if (nodeRail->NetworkErrorState & EP_NODE_NETERR_DMA_PACKET)
++      nodeRail->NetworkErrorState &= ~EP_NODE_NETERR_DMA_PACKET;
++    
++    if (nodeRail->NetworkErrorState & EP_NODE_NETERR_ATOMIC_PACKET)
++    {
++      if (EP_XID_INVALID (nodeRail->MsgXid))
++          nodeRail->MsgXid = ep_xid_cache_alloc (sys, &rail->XidCache);
++
++      msg.Cookies[0] = nodeRail->NetworkErrorCookies[0];
++      msg.Cookies[1] = nodeRail->NetworkErrorCookies[1];
++      
++      EPRINTF4 (DBG_NETWORK_ERROR, "%s: progress neterr - node %d cookies %llx %llx\n", rail->Name, nodeId, msg.Cookies[0], msg.Cookies[1]);
++
++      if (ep_send_message (rail, nodeId, EP_MANAGER_MSG_TYPE_NETERR_REQUEST, nodeRail->MsgXid, &msg) == 0)
++          nodeRail->NextRunTime = lbolt + MESSAGE_RETRY_TIME;
++      else
++          nodeRail->NextRunTime = lbolt + MSGBUSY_RETRY_TIME;
++    }
++}
++
++long
++ProgressNodeLists (EP_RAIL *rail, long nextRunTime)
++{
++    EP_SYS           *sys = rail->System;
++    struct list_head *el, *nel;
++    unsigned long flags;
++
++    spin_lock_irqsave (&sys->NodeLock, flags);
++    list_for_each_safe (el, nel, &rail->NetworkErrorList) {
++      EP_NODE_RAIL *nodeRail = list_entry (el, EP_NODE_RAIL, Link);
++      int           nodeId   = nodeRail - rail->Nodes;
++
++      ProgressNetworkError (rail, nodeRail);
++
++      if (nodeRail->NetworkErrorState == 0)
++      {
++          EPRINTF2 (DBG_NETWORK_ERROR, "%s: lower context filter for node %d due to network error\n", rail->Name, nodeId);
++          printk ("%s: lower context filter for node %d due to network error\n", rail->Name, nodeId);
++
++          rail->Operations.LowerFilter (rail, nodeId);
++
++          list_del (&nodeRail->Link);
++          continue;
++      }
++      
++      if (nextRunTime == 0 || AFTER (nextRunTime, nodeRail->NextRunTime))
++          nextRunTime = nodeRail->NextRunTime;
++    }
++    spin_unlock_irqrestore (&sys->NodeLock, flags);
++
++    if (! list_empty (&rail->LocalPassivateList))
++    {
++      EPRINTF1 (DBG_MANAGER, "%s: Locally Passivating Nodes\n", rail->Name);
++      
++      /* We have disconnected from some nodes or have left ourselves
++       * flush through all communications and determine whether we
++       * need to perform rail failover */
++      rail->Operations.FlushFilters (rail);
++      
++      ep_call_callbacks (rail, EP_CB_FLUSH_FILTERING, rail->NodeSet);
++
++      rail->Operations.FlushQueues (rail);
++
++      ep_call_callbacks (rail, EP_CB_FLUSH_FLUSHING, rail->NodeSet);
++
++      while (! list_empty (&rail->LocalPassivateList))
++      {
++          EP_NODE_RAIL *nodeRail = list_entry (rail->LocalPassivateList.next, EP_NODE_RAIL, Link);
++          int           nodeId   = nodeRail - rail->Nodes;
++
++          list_del (&nodeRail->Link);
++
++          rail->Operations.UnloadNodeRoute (rail, nodeId);
++          
++          if (nodeRail->NetworkErrorState == 0 && nodeRail->MessageState == 0)
++          {
++              EPRINTF2 (DBG_MANAGER, "%s: Node %d -> Disconnecting\n", rail->Name, nodeId);
++
++              list_add_tail (&nodeRail->Link, &rail->DisconnectingList);
++              nodeRail->State = EP_NODE_DISCONNECTING;
++          }
++          else
++          {
++              EPRINTF2 (DBG_MANAGER, "%s: Node %d -> Remote Passivate\n", rail->Name, nodeId);
++
++              list_add_tail (&nodeRail->Link, &rail->RemotePassivateList);
++              nodeRail->State = EP_NODE_REMOTE_PASSIVATE;
++
++              if (nodeRail->NetworkErrorState == 0)
++                  nodeRail->NextRunTime = lbolt;
++          }
++      }
++
++      ep_call_callbacks (rail, EP_CB_PASSIVATED, rail->NodeSet);
++    }
++
++    list_for_each_safe (el, nel, &rail->RemotePassivateList) {
++      EP_NODE_RAIL *nodeRail = list_entry (el, EP_NODE_RAIL, Link);
++      int           nodeId   = nodeRail - rail->Nodes;
++      EP_NODE      *node     = &sys->Nodes[nodeId];
++
++      if (node->ConnectedRails == 0)                          /* no rails connected to this node (anymore) */
++      {
++          /*  Remove from this list */
++          list_del (&nodeRail->Link);
++
++          EPRINTF2 (DBG_MANAGER, "%s: Node %d, no rails, Remote Passivate -> Disconnecting\n", rail->Name, nodeId);
++
++          /* transition towards disconnected */
++          list_add_tail (&nodeRail->Link, &rail->DisconnectingList);
++          nodeRail->State = EP_NODE_DISCONNECTING;
++          continue;
++      }
++
++      EPRINTF6 (DBG_MANAGER, "%s: Node %d - %s NetworkErrorState=%x NextRunTime=%lx (%lx)\n",
++                rail->Name, nodeId, NodeStateNames[nodeRail->State], nodeRail->NetworkErrorState,
++                nodeRail->NextRunTime, nextRunTime);
++
++      if (nodeRail->NetworkErrorState)
++      {
++          ProgressNetworkError (rail, nodeRail);
++      }
++      else if (! BEFORE (lbolt, nodeRail->NextRunTime))
++      {
++          if (EP_XID_INVALID (nodeRail->MsgXid))
++              nodeRail->MsgXid = ep_xid_cache_alloc (sys, &rail->XidCache);
++
++          if (ep_send_message (rail, nodeId, EP_MANAGER_MSG_TYPE_FLUSH_REQUEST, nodeRail->MsgXid, NULL) == 0)
++              nodeRail->NextRunTime = lbolt + MESSAGE_RETRY_TIME;
++          else
++              nodeRail->NextRunTime = lbolt + MSGBUSY_RETRY_TIME;
++      }
++
++      if (nextRunTime == 0 || AFTER (nextRunTime, nodeRail->NextRunTime))
++          nextRunTime = nodeRail->NextRunTime;
++    }
++    
++    if (! list_empty (&rail->PassivatedList)) 
++    {
++      ep_call_callbacks (rail, EP_CB_FAILOVER, rail->NodeSet);
++
++      list_for_each_safe (el, nel, &rail->PassivatedList) {
++          EP_NODE_RAIL *nodeRail = list_entry (rail->PassivatedList.next, EP_NODE_RAIL, Link);
++          int           nodeId   = nodeRail - rail->Nodes;
++          EP_NODE      *node     = &sys->Nodes[nodeId];
++
++          ASSERT (nodeRail->NetworkErrorState == 0);
++
++          if (node->ConnectedRails == 0)
++          {
++              /*  Remove from this list */
++              list_del (&nodeRail->Link);
++
++              EPRINTF2 (DBG_MANAGER, "%s: Node %d, no rails, Passivated -> Disconnecting\n", rail->Name, nodeId);
++
++              /* transition towards disconnected */
++              list_add_tail (&nodeRail->Link, &rail->DisconnectingList);
++              nodeRail->State = EP_NODE_DISCONNECTING;
++              continue;
++          }
++          
++          EPRINTF6 (DBG_MANAGER, "%s: Node %d - %s NetworkErrorState=%x NextRunTime=%lx (%lx)\n",
++                    rail->Name, nodeId, NodeStateNames[nodeRail->State], nodeRail->NetworkErrorState,
++                    nodeRail->NextRunTime, nextRunTime);
++
++          if (nodeRail->MessageState == 0)
++          {
++              EPRINTF2 (DBG_MANAGER, "%s: Node %d, no messages, Passivated -> Disconnecting\n", rail->Name,nodeId);
++
++              list_del (&nodeRail->Link);
++              list_add_tail (&nodeRail->Link, &rail->DisconnectingList);
++              nodeRail->State = EP_NODE_DISCONNECTING;
++              continue;
++          }
++
++          nodeRail->MessageState = 0;
++          nodeRail->NextRunTime  = lbolt + FAILOVER_RETRY_TIME;
++
++          if (nextRunTime == 0 || AFTER (nextRunTime, nodeRail->NextRunTime))
++              nextRunTime = nodeRail->NextRunTime;
++      }
++    }
++
++    if (! list_empty (&rail->DisconnectingList))
++    {
++      ep_call_callbacks (rail, EP_CB_DISCONNECTING, rail->NodeSet);
++
++      while (! list_empty (&rail->DisconnectingList))
++      {
++          EP_NODE_RAIL *nodeRail = list_entry (rail->DisconnectingList.next, EP_NODE_RAIL, Link);
++          int           nodeId   = nodeRail - rail->Nodes;
++          EP_NODE      *node     = &sys->Nodes[nodeId];
++
++          EPRINTF2 (DBG_MANAGER, "%s: Node %d, Disconnecting -> Disconnected\n", rail->Name, nodeId);
++
++          list_del (&nodeRail->Link);
++
++          rail->Operations.NodeDisconnected (rail, nodeId);
++
++          /* Clear the network error state */
++          nodeRail->NextRunTime            = 0;
++          nodeRail->NetworkErrorState      = 0;
++          nodeRail->NetworkErrorCookies[0] = 0;
++          nodeRail->NetworkErrorCookies[1] = 0;
++
++          /* Clear the message state */
++          nodeRail->MessageState = 0;
++
++          cm_node_disconnected (rail, nodeId);
++
++          nodeRail->State = EP_NODE_DISCONNECTED;
++          
++          statemap_setbits (rail->NodeSet, nodeId, 0, 1);
++
++          if (node->ConnectedRails == 0)
++              statemap_setbits (sys->NodeSet, nodeId, 0, 1);
++      }
++
++      ep_call_callbacks (rail, EP_CB_DISCONNECTED, rail->NodeSet);
++    }
++
++    return (nextRunTime);
++}
++
++void
++DisplayNodes (EP_RAIL *rail)
++{
++    EP_SYS *sys = rail->System;
++    int i, state, count;
++    unsigned long flags;
++
++    spin_lock_irqsave (&sys->NodeLock, flags);
++
++    for (state = 0; state < EP_NODE_NUM_STATES; state++)
++    {
++      for (count = i = 0; i < rail->Position.pos_nodes; i++)
++      {
++          ASSERT (rail->Nodes[i].State < EP_NODE_NUM_STATES);
++
++          if (rail->Nodes[i].State == state)
++              if (state != EP_NODE_DISCONNECTED)
++                  printk ("%s %d", !count++ ? NodeStateNames[state] : "", i);
++      }
++      if (count)
++          printk ("%s (%d total)\n", state == EP_NODE_DISCONNECTED ? NodeStateNames[state] : "", count);
++    }
++    spin_unlock_irqrestore (&sys->NodeLock, flags);
++}
++
++static void
++PositionFound (EP_RAIL *rail, ELAN_POSITION *pos)
++{
++    EP_SYS           *sys = rail->System;
++    struct list_head *el;
++    int i;
++
++    /* only called from the ep_managage whilst rail->State == EP_RAIL_STATE_STARTED */
++    ASSERT ( rail->State == EP_RAIL_STATE_STARTED );
++
++#if defined(PER_CPU_TIMEOUT)
++    /*
++     * On Tru64 - if we're running in a "funnelled" thread, then we will be 
++     * unable to start the per-cpu timeouts, so if we return then eventually
++     * the ep_manager() thread will find the network position and we're
++     * in control of our own destiny.
++     */
++    if (THREAD_IS_FUNNELED(current_thread()))
++    {
++      ep_kthread_schedule (&sys->ManagerThread, lbolt);
++      return;
++    }
++#endif
++
++    sprintf (rail->Name, "ep%d[%d]", rail->Number, pos->pos_nodeid);
++
++    if (pos->pos_levels > MaxSwitchLevels)
++    {
++      for (i = 0; i < (pos->pos_levels - MaxSwitchLevels); i++)
++          pos->pos_nodes /= pos->pos_arity[i];
++
++      for (i = 0; i < MaxSwitchLevels; i++)
++          pos->pos_arity[i] = pos->pos_arity[i + (pos->pos_levels - MaxSwitchLevels)];
++
++      pos->pos_levels = MaxSwitchLevels;
++      pos->pos_nodeid = pos->pos_nodeid % pos->pos_nodes;
++                              
++      printk ("%s: limiting switch levels to %d\n", rail->Name, MaxSwitchLevels);
++      printk ("%s: nodeid=%d level=%d numnodes=%d\n", rail->Name, pos->pos_nodeid, pos->pos_levels, pos->pos_nodes);
++
++      sprintf (rail->Name, "ep%d[%d]", rail->Number, pos->pos_nodeid);
++    }
++
++    if (rail->Position.pos_mode != ELAN_POS_UNKNOWN && rail->Position.pos_nodeid != pos->pos_nodeid)
++    {
++      printk ("%s: NodeId has changed from %d to %d\n", rail->Name, rail->Position.pos_nodeid, pos->pos_nodeid);
++      panic ("ep: PositionFound: NodeId has changed\n");
++    }
++
++    if (sys->Position.pos_mode != ELAN_POS_UNKNOWN && (sys->Position.pos_nodeid != pos->pos_nodeid || sys->Position.pos_nodes != pos->pos_nodes))
++    {
++      printk ("%s: position incompatible - disabling rail\n", rail->Name);
++      rail->State = EP_RAIL_STATE_INCOMPATIBLE;
++      return;
++    }
++
++    if (sys->Position.pos_mode == ELAN_POS_UNKNOWN)
++    {
++      sys->Position = *pos;
++      sys->NodeSet  = statemap_create (pos->pos_nodes);
++      KMEM_ZALLOC (sys->Nodes, EP_NODE *, pos->pos_nodes * sizeof (EP_NODE), 1);
++    }
++
++    rail->Position             = *pos;
++    rail->SwitchBroadcastLevel = pos->pos_levels - 1;
++    rail->State                = EP_RAIL_STATE_RUNNING;
++
++    for (i = 0; i < pos->pos_levels; i++)
++    {
++      rail->SwitchProbeTick[i]   = lbolt;
++      rail->SwitchLast[i].uplink = 4;
++    }
++
++    rail->Operations.PositionFound (rail, pos);
++
++    INIT_LIST_HEAD (&rail->NetworkErrorList);
++    INIT_LIST_HEAD (&rail->LocalPassivateList);
++    INIT_LIST_HEAD (&rail->RemotePassivateList);
++    INIT_LIST_HEAD (&rail->PassivatedList);
++    INIT_LIST_HEAD (&rail->DisconnectingList);
++
++    rail->NodeSet       = statemap_create (rail->Position.pos_nodes);
++    rail->NodeChangeMap = statemap_create (rail->Position.pos_nodes);
++    rail->NodeChangeTmp = statemap_create (rail->Position.pos_nodes);
++
++    KMEM_ZALLOC (rail->Nodes, EP_NODE_RAIL *, rail->Position.pos_nodes * sizeof (EP_NODE_RAIL), 1);
++
++    for (i = 0; i < rail->Position.pos_nodes; i++)
++    {
++      spin_lock_init (&rail->Nodes[i].CookieLock);
++
++      INIT_LIST_HEAD (&rail->Nodes[i].StalledDmas);
++
++      rail->Nodes[i].State = EP_NODE_DISCONNECTED;
++    }
++
++    /* Notify all subsystems that a new rail has been enabled */
++    kmutex_lock (&sys->SubsysLock);
++    list_for_each (el, &sys->Subsystems) { 
++      EP_SUBSYS *subsys = list_entry (el, EP_SUBSYS, Link);
++
++      if (subsys->AddRail)
++          subsys->AddRail (subsys, sys, rail);
++
++      /* XXXX: what to do if the subsystem refused to add the rail ? */
++    }
++    kmutex_unlock (&sys->SubsysLock);
++
++    /* Now enable the manager input queue */
++    ep_enable_inputq (rail, rail->ManagerInputQ);
++}
++
++static void
++ep_manager (void *arg)
++{
++    EP_SYS            *sys = (EP_SYS *) arg;
++    struct list_head *el;
++    ELAN_POSITION     pos;
++    unsigned long     flags;
++
++    kernel_thread_init ("ep_manager");
++    kernel_thread_become_highpri();
++
++    for (;;)
++    {
++      long nextRunTime = lbolt + MSEC2TICKS(CM_THREAD_SCHEDULE_TIMEOUT);
++
++      list_for_each (el, &sys->ManagedRails) {
++          EP_RAIL *rail = list_entry (el, EP_RAIL, ManagerLink);
++
++          switch (rail->State)
++          {
++          case EP_RAIL_STATE_STARTED:
++              if (ProbeNetwork (rail, &pos) == 0)
++              {
++                  PositionFound (rail, &pos);
++                  break;
++              }
++
++              if (nextRunTime == 0 || AFTER (nextRunTime, lbolt + HZ))
++                  nextRunTime = lbolt + HZ;
++              break;
++
++          case EP_RAIL_STATE_RUNNING:
++              if (ep_poll_inputq (rail, rail->ManagerInputQ, 100, ProcessMessage, rail) >= 100)
++                  nextRunTime = lbolt;
++              
++              /* Handle any nodes which the cluster membership subsystem
++               * has indicated are to begin connecting or disconnecting */
++              spin_lock_irqsave (&sys->NodeLock, flags);
++              if (! statemap_changed (rail->NodeChangeMap))
++                  spin_unlock_irqrestore (&sys->NodeLock, flags);
++              else
++              {
++                  /*
++                   * Take a copy of the statemap, and zero all entries so
++                   * we only see new requests next time
++                   */
++                  statemap_copy (rail->NodeChangeTmp, rail->NodeChangeMap);
++                  statemap_zero (rail->NodeChangeMap);
++                  spin_unlock_irqrestore (&sys->NodeLock, flags);
++                  
++                  UpdateConnectionState (rail, rail->NodeChangeTmp);
++              }
++
++              nextRunTime = ProgressNodeLists (rail, nextRunTime);
++
++              if (statemap_changed (rail->NodeSet))
++              {
++                  ep_call_callbacks (rail, EP_CB_NODESET, rail->NodeSet);
++
++                  statemap_clearchanges (rail->NodeSet);
++              }
++              break;
++
++          case EP_RAIL_STATE_INCOMPATIBLE:
++              break;
++          }
++      }
++
++
++      EPRINTF5 (DBG_MANAGER, "ep_manager: sleep now=%lx nextRunTime=%lx (%ld) [%lx (%ld)]\n",
++                lbolt, nextRunTime, nextRunTime ? nextRunTime - lbolt : 0, sys->ManagerThread.next_run,
++                sys->ManagerThread.next_run ? sys->ManagerThread.next_run - lbolt : 0);
++
++      if (ep_kthread_sleep (&sys->ManagerThread, nextRunTime) < 0)
++          break;
++    }
++
++    ep_kthread_stopped (&sys->ManagerThread);
++    kernel_thread_exit();
++}
++
++void
++ep_connect_node (EP_RAIL *rail, int nodeId)
++{
++    EP_SYS       *sys  = rail->System;
++    EP_NODE_RAIL *node = &rail->Nodes[nodeId];
++    unsigned long flags;
++  
++    spin_lock_irqsave (&sys->NodeLock, flags);
++
++    EPRINTF2 (DBG_MANAGER, "%s: ep_connect_node: nodeId %d\n", rail->Name, nodeId);
++
++    ASSERT (node->State == EP_NODE_DISCONNECTED && statemap_getbits (rail->NodeChangeMap, nodeId, 1) == 0);
++    
++    node->State = EP_NODE_CONNECTING;
++
++    statemap_setbits (rail->NodeChangeMap, nodeId, 1, 1);
++
++    spin_unlock_irqrestore (&sys->NodeLock, flags);
++
++    ep_kthread_schedule (&sys->ManagerThread, lbolt);
++}
++
++int
++ep_disconnect_node (EP_RAIL *rail, int nodeId)
++{
++    EP_SYS       *sys  = rail->System;
++    EP_NODE_RAIL *node = &rail->Nodes[nodeId];
++    int                 state;
++    unsigned long flags;
++  
++    spin_lock_irqsave (&sys->NodeLock, flags);
++    
++    EPRINTF3 (DBG_MANAGER, "%s: ep_disconnect_node: nodeId %d - %s\n", rail->Name, nodeId, NodeStateNames[node->State]);
++
++    switch (state = node->State)
++    {
++    case EP_NODE_CONNECTING:
++      statemap_setbits (rail->NodeChangeMap, nodeId, 0, 1);
++
++      node->State = EP_NODE_DISCONNECTED;
++      break;
++      
++    case EP_NODE_CONNECTED:
++      statemap_setbits (rail->NodeChangeMap, nodeId, 1, 1);
++
++      node->State = EP_NODE_LEAVING_CONNECTED;
++      break;
++
++    case EP_NODE_LEAVING_CONNECTED:
++      /* no assert on NodeChangeMap as the map could have been taken but not acted on */
++      break;
++      
++    default:
++      ASSERT (statemap_getbits (rail->NodeChangeMap, nodeId, 1) == 0);
++      break;
++    }
++    spin_unlock_irqrestore (&sys->NodeLock, flags);
++
++    if (state == EP_NODE_CONNECTED)
++      ep_kthread_schedule (&sys->ManagerThread, lbolt);
++
++    return state;
++}
++
++int
++ep_manager_add_rail (EP_SYS *sys, EP_RAIL *rail)
++{
++    if ((rail->ManagerOutputQ = ep_alloc_outputq (rail, EP_MANAGER_MSG_SIZE, EP_MANAGER_OUTPUTQ_SLOTS)) == NULL)
++      return -ENOMEM;
++
++    if ((rail->ManagerInputQ = ep_alloc_inputq (rail, EP_SYSTEMQ_MANAGER, EP_MANAGER_MSG_SIZE, EP_MANAGER_INPUTQ_SLOTS,
++                                               ManagerQueueEvent, &sys->ManagerThread)) == NULL)
++    {
++      ep_free_outputq (rail, rail->ManagerOutputQ);
++      return -ENOMEM;
++    }
++
++    spin_lock_init (&rail->ManagerOutputQLock);
++
++    ep_xid_cache_init (sys, &rail->XidCache);
++
++    ep_kthread_stall (&sys->ManagerThread);
++    list_add_tail (&rail->ManagerLink, &sys->ManagedRails);
++    ep_kthread_resume (&sys->ManagerThread);
++
++    return (0);
++}
++
++void
++ep_manager_remove_rail (EP_SYS *sys, EP_RAIL *rail)
++{
++    if (rail->ManagerInputQ != NULL)
++    {
++      ep_kthread_stall (&sys->ManagerThread);
++      list_del (&rail->ManagerLink);
++      ep_kthread_resume (&sys->ManagerThread);
++
++      ep_xid_cache_destroy (sys, &rail->XidCache);
++
++      spin_lock_destroy (&rail->ManagerOutputQLock);
++
++      ep_disable_inputq (rail, rail->ManagerInputQ);
++      ep_free_inputq (rail, rail->ManagerInputQ);
++      ep_free_outputq (rail, rail->ManagerOutputQ);
++    }
++}
++
++int
++ep_manager_init (EP_SYS *sys)
++{
++    INIT_LIST_HEAD (&sys->ManagedRails);
++
++    ep_kthread_init (&sys->ManagerThread);
++
++    if (kernel_thread_create (ep_manager, (void *) sys) == 0)
++      return (ENOMEM);
++    
++    ep_kthread_started (&sys->ManagerThread);
++    
++    return (0);
++}
++
++void
++ep_manager_fini (EP_SYS *sys)
++{
++    ep_kthread_stop (&sys->ManagerThread);
++    ep_kthread_destroy (&sys->ManagerThread);
++}
++
++int
++ep_sys_init (EP_SYS *sys)
++{
++    kmutex_init (&sys->SubsysLock);   
++    kmutex_init (&sys->StartStopLock);
++    spin_lock_init (&sys->NodeLock);
++
++    INIT_LIST_HEAD (&sys->Subsystems);
++
++    /* initialise the xid allocators */
++    spin_lock_init (&sys->XidLock);
++    INIT_LIST_HEAD (&sys->XidCacheList);
++
++    /* initially don't know where we are in the network */
++    sys->Position.pos_mode = ELAN_POS_UNKNOWN;
++
++    /* initialise the network mapping descriptor hash tables */
++    ep_nmh_init (&sys->MappingTable);
++
++    /* intialise the shared allocators */
++    ep_shared_alloc_init (sys);
++
++    /* initialise the dvma space */
++    ep_dvma_init (sys);
++
++    /* intiialise the rail manager */
++    ep_manager_init (sys);
++
++    /* initialise all subsystems */
++    cm_init (sys);
++    ep_comms_init (sys);
++    //ep_msgsys_init (sys);
++
++    return (0);
++}
++
++void
++ep_sys_fini (EP_SYS *sys)
++{
++    /* Destroy the subsystems in the reverse order to their creation */
++    while (! list_empty (&sys->Subsystems))
++    {
++      EP_SUBSYS *subsys = list_entry (sys->Subsystems.prev, EP_SUBSYS, Link);
++
++      list_del (&subsys->Link);
++      
++      subsys->Destroy (subsys, sys);
++    }
++
++    ep_manager_fini(sys);
++    ep_dvma_fini (sys);
++    ep_shared_alloc_fini (sys);
++
++    ep_nmh_fini (&sys->MappingTable);
++
++    if (sys->Position.pos_mode != ELAN_POS_UNKNOWN) {
++      statemap_destroy (sys->NodeSet);
++      KMEM_FREE(sys->Nodes, sys->Position.pos_nodes * sizeof (EP_NODE));
++    }
++
++    spin_lock_destroy (&sys->XidLock);
++
++    spin_lock_destroy (&sys->NodeLock);
++    kmutex_destroy (&sys->SubsysLock);
++    kmutex_destroy (&sys->StartStopLock);
++}
++
++void
++ep_shutdown (EP_SYS *sys)
++{
++    sys->Shutdown = 1;
++}
++
++int
++ep_init_rail (EP_SYS *sys, EP_RAIL *rail)
++{
++    static int rnum;
++
++    rail->System              = sys;
++    rail->State               = EP_RAIL_STATE_UNINITIALISED;
++    rail->Number              = rnum++;
++    rail->Position.pos_mode   = ELAN_POS_UNKNOWN;
++    rail->Position.pos_nodeid = ELAN_INVALID_NODE;
++
++    rail->CallbackRegistered  = 0;
++
++    sprintf (rail->Name, "ep%d", rail->Number);
++
++    /* Initialise externally visible locks */
++    kmutex_init (&rail->CallbackLock);
++
++    ep_alloc_init (rail);
++
++    sys->Rails[rail->Number] = rail;
++
++    return 0;
++}
++
++void
++ep_destroy_rail (EP_RAIL *rail)
++{
++    ASSERT (rail->State == EP_RAIL_STATE_UNINITIALISED);
++
++    ep_alloc_fini (rail);
++
++    kmutex_destroy (&rail->CallbackLock);
++
++    rail->System->Rails[rail->Number] = NULL;
++
++    rail->Operations.DestroyRail (rail);
++}
++
++/* We need to traverse the Subsystems lists backwards
++ * but it's not defined in <linux/list.h> */
++#define list_for_each_backwards(pos,list) \
++      for (pos = (list)->prev; pos != (list); \
++           pos = (pos)->prev)
++
++void
++__ep_stop_rail (EP_RAIL *rail)
++{
++    /* called holding the sys->Lock */
++    EP_SYS           *sys = rail->System;
++    struct list_head *el;
++
++    rail->Operations.StallRail (rail);
++
++    /* Notify all subsystems that this rail is being stopped */
++    if (rail->State == EP_RAIL_STATE_RUNNING)
++    {
++      kmutex_lock (&sys->SubsysLock);
++      list_for_each_backwards (el, &sys->Subsystems) { 
++          EP_SUBSYS *subsys = list_entry (el, EP_SUBSYS, Link);
++          
++          if (subsys->RemoveRail)
++              subsys->RemoveRail (subsys, sys, rail);
++      }
++      kmutex_unlock (&sys->SubsysLock);
++
++      ep_manager_remove_rail (sys, rail);
++
++      KMEM_FREE (rail->Nodes, rail->Position.pos_nodes * sizeof (EP_NODE_RAIL));
++
++      statemap_destroy (rail->NodeChangeTmp);
++      statemap_destroy (rail->NodeChangeMap);
++      statemap_destroy (rail->NodeSet);
++    }
++
++    ep_dvma_remove_rail (sys, rail);
++    ep_shared_alloc_remove_rail (sys, rail);
++
++    rail->Operations.StopRail (rail);
++
++    rail->State = EP_RAIL_STATE_UNINITIALISED;
++}
++
++void
++ep_stop_rail (EP_RAIL *rail)
++{
++    EP_SYS *sys = rail->System;
++
++    /* stall ep_manager                      */
++    /* and remove the rail from the manaager */
++
++    ep_kthread_stall (&sys->ManagerThread);
++    if ( rail->State == EP_RAIL_STATE_STARTED ) 
++        ep_manager_remove_rail (sys, rail);
++    ep_kthread_resume (&sys->ManagerThread);
++
++    __ep_stop_rail (rail);
++}
++
++int
++ep_start_rail (EP_RAIL *rail)
++{
++    EP_SYS *sys = rail->System;
++
++    ASSERT (rail->State == EP_RAIL_STATE_UNINITIALISED);
++
++    if (rail->Operations.StartRail (rail) < 0)
++      return -ENXIO;
++    
++    kmutex_lock (&sys->StartStopLock);
++    /* Add this rail to the shared allocator */
++    if (ep_shared_alloc_add_rail (rail->System, rail))
++      goto failed;
++
++    /* Add this rail to dvma kmap */
++    if (ep_dvma_add_rail (rail->System, rail))
++      goto failed;
++
++    /* rail is now started */
++    rail->State = EP_RAIL_STATE_STARTED;
++
++    /* notify the rail manager of the new rail */
++    if (ep_manager_add_rail (rail->System, rail))
++      goto failed;
++
++    kmutex_unlock (&sys->StartStopLock);
++    return (ESUCCESS);
++
++ failed:
++    printk ("%s: start failed\n", rail->Name);
++    kmutex_unlock (&sys->StartStopLock);
++    __ep_stop_rail (rail);
++
++    return (ENOMEM);   
++}
++
++void
++ep_subsys_add (EP_SYS *sys, EP_SUBSYS *subsys)
++{
++    kmutex_lock (&sys->SubsysLock);
++    list_add_tail (&subsys->Link, &sys->Subsystems);
++    kmutex_unlock (&sys->SubsysLock);
++}
++
++void
++ep_subsys_del (EP_SYS *sys, EP_SUBSYS *subsys)
++{
++    kmutex_lock (&sys->SubsysLock);
++    list_del (&subsys->Link);
++    kmutex_unlock (&sys->SubsysLock);
++}
++
++EP_SUBSYS *
++ep_subsys_find (EP_SYS *sys, char *name)
++{
++    struct list_head *el;
++
++    ASSERT ( !in_interrupt());
++
++    kmutex_lock (&sys->SubsysLock); 
++    list_for_each (el, &sys->Subsystems) {
++      EP_SUBSYS *subsys = list_entry (el, EP_SUBSYS, Link);
++
++      if (! strcmp (subsys->Name, name))
++      {
++          kmutex_unlock (&sys->SubsysLock);
++          return (subsys);
++      }
++    }
++
++    kmutex_unlock (&sys->SubsysLock);
++    return (NULL);
++}
++
++int
++ep_waitfor_nodeid (EP_SYS *sys)
++{
++    int i, printed = 0;
++    kcondvar_t Sleep;
++    spinlock_t Lock;
++
++    kcondvar_init (&Sleep);
++    spin_lock_init (&Lock);
++
++#define TICKS_TO_WAIT (10*hz)
++#define TICKS_PER_LOOP        (hz/10)
++    for (i = 0; sys->Position.pos_mode == ELAN_POS_UNKNOWN && i < TICKS_TO_WAIT; i += TICKS_PER_LOOP)
++    {
++      if (! printed++)
++          printk ("ep: waiting for network position to be found\n");
++
++      spin_lock (&Lock);
++      kcondvar_timedwait (&Sleep, &Lock, NULL, lbolt + TICKS_PER_LOOP);
++      spin_unlock (&Lock);
++    }
++
++    if (sys->Position.pos_mode == ELAN_POS_UNKNOWN)
++      printk ("ep: network position not found after waiting\n");
++    else if (printed)
++      printk ("ep: network position found at nodeid %d\n", sys->Position.pos_nodeid);
++
++    spin_lock_destroy (&Lock);
++    kcondvar_destroy (&Sleep);
++
++    return (sys->Position.pos_mode == ELAN_POS_UNKNOWN ? ELAN_INVALID_NODE : sys->Position.pos_nodeid);
++}
++
++int
++ep_nodeid (EP_SYS *sys)
++{
++    return (sys->Position.pos_mode == ELAN_POS_UNKNOWN ? ELAN_INVALID_NODE : sys->Position.pos_nodeid);
++}
++
++int
++ep_numnodes (EP_SYS *sys)
++{
++    return (sys->Position.pos_nodes);
++}
++
++void
++ep_fillout_stats(EP_RAIL *r, char *str) 
++{
++    sprintf(str+strlen(str),"SendMessageFailed %lu NeterrAtomicPacket %lu NeterrDmaPacket %lu \n", r->Stats.SendMessageFailed, r->Stats.NeterrAtomicPacket, r->Stats.NeterrDmaPacket);
++    sprintf(str+strlen(str),"Rx %lu  %lu /sec\n",   GET_STAT_TOTAL(r->Stats,rx), GET_STAT_PER_SEC(r->Stats,rx) ); 
++    sprintf(str+strlen(str),"MBytes %lu  %lu MB/sec\n", GET_STAT_TOTAL(r->Stats,rx_len)/ (1024*1024), GET_STAT_PER_SEC(r->Stats,rx_len) / (1024*1024)); 
++    sprintf(str+strlen(str),"Tx %lu  %lu /sec\n",   GET_STAT_TOTAL(r->Stats,tx), GET_STAT_PER_SEC(r->Stats,tx) ); 
++    sprintf(str+strlen(str),"MBytes %lu  %lu MB/sec\n", GET_STAT_TOTAL(r->Stats,tx_len)/ (1024*1024), GET_STAT_PER_SEC(r->Stats,tx_len) / (1024*1024)); 
++}
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/ep/kcomm_elan3.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/kcomm_elan3.c        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/kcomm_elan3.c     2005-05-11 12:10:12.522919808 -0400
+@@ -0,0 +1,504 @@
++
++/*
++ *    Copyright (c) 2003 by Quadrics Ltd.
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: kcomm_elan3.c,v 1.31.8.3 2004/11/30 12:02:17 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/kcomm_elan3.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++
++#include "kcomm_vp.h"
++#include "kcomm_elan3.h"
++#include "conf_linux.h"
++
++extern EP_CODE threadcode_elan3;
++
++unsigned int
++ep3_create_rails (EP_SYS *sys, unsigned int disabled)
++{
++    unsigned int rmask = 0;
++    ELAN3_DEV   *dev;
++    EP_RAIL     *rail;
++    int          i;
++
++    for (i = 0; i < EP_MAX_RAILS; i++)
++    {
++      if ((dev = elan3_device (i)) != NULL)
++      {
++          if ((rail = ep3_create_rail (sys, dev)) != NULL)
++          {
++              if (disabled & (1 << rail->Number))
++                  printk ("%s: auto-start of device disabled by configuration\n", rail->Name);
++              else
++                  ep_start_rail (rail);
++              
++              ep_procfs_rail_init(rail);
++
++              rmask |= (1 << rail->Number);
++          }
++      }
++    }
++
++    return rmask;
++}
++
++EP_RAIL *
++ep3_create_rail (EP_SYS *sys, ELAN3_DEV *dev)
++{
++    EP3_RAIL *rail;
++    int       res;
++
++    KMEM_ZALLOC (rail, EP3_RAIL *, sizeof (EP3_RAIL), TRUE);
++
++    if (rail == NULL)
++      return (EP_RAIL *) NULL;
++
++    if ((res = ep_init_rail (sys, &rail->Generic)) != 0)
++    {
++      KMEM_FREE (rail, sizeof (EP3_RAIL));
++      return (EP_RAIL *) NULL;
++    }
++
++    rail->Device = dev;
++
++    /* Install our rail operations */
++    rail->Generic.Operations.DestroyRail      = ep3_destroy_rail;
++    rail->Generic.Operations.StartRail        = ep3_start_rail;
++    rail->Generic.Operations.StallRail        = ep3_stall_rail;
++    rail->Generic.Operations.StopRail         = ep3_stop_rail;
++
++    rail->Generic.Operations.SdramAlloc       = ep3_sdram_alloc;
++    rail->Generic.Operations.SdramFree        = ep3_sdram_free;
++    rail->Generic.Operations.SdramWriteb      = ep3_sdram_writeb;
++
++    rail->Generic.Operations.KaddrMap         = ep3_kaddr_map;
++    rail->Generic.Operations.SdramMap         = ep3_sdram_map;
++    rail->Generic.Operations.Unmap            = ep3_unmap;
++
++    rail->Generic.Operations.DvmaReserve      = ep3_dvma_reserve;
++    rail->Generic.Operations.DvmaRelease      = ep3_dvma_release;
++    rail->Generic.Operations.DvmaSetPte       = ep3_dvma_set_pte;
++    rail->Generic.Operations.DvmaReadPte      = ep3_dvma_read_pte;
++    rail->Generic.Operations.DvmaUnload       = ep3_dvma_unload;
++    rail->Generic.Operations.FlushTlb         = ep3_flush_tlb;
++
++    rail->Generic.Operations.ProbeRoute       = ep3_probe_route;
++    rail->Generic.Operations.PositionFound    = ep3_position_found;
++    rail->Generic.Operations.CheckPosition    = ep3_check_position;
++    rail->Generic.Operations.NeterrFixup      = ep3_neterr_fixup;
++
++    rail->Generic.Operations.LoadSystemRoute  = ep3_load_system_route;
++
++    rail->Generic.Operations.LoadNodeRoute    = ep3_load_node_route;
++    rail->Generic.Operations.UnloadNodeRoute  = ep3_unload_node_route;
++    rail->Generic.Operations.LowerFilter      = ep3_lower_filter;
++    rail->Generic.Operations.RaiseFilter      = ep3_raise_filter;
++    rail->Generic.Operations.NodeDisconnected = ep3_node_disconnected;
++
++    rail->Generic.Operations.FlushFilters     = ep3_flush_filters;
++    rail->Generic.Operations.FlushQueues      = ep3_flush_queues;
++
++    rail->Generic.Operations.AllocInputQ      = ep3_alloc_inputq;
++    rail->Generic.Operations.FreeInputQ       = ep3_free_inputq;
++    rail->Generic.Operations.EnableInputQ     = ep3_enable_inputq;
++    rail->Generic.Operations.DisableInputQ    = ep3_disable_inputq;
++    rail->Generic.Operations.PollInputQ       = ep3_poll_inputq;
++
++    rail->Generic.Operations.AllocOutputQ     = ep3_alloc_outputq;
++    rail->Generic.Operations.FreeOutputQ      = ep3_free_outputq;
++    rail->Generic.Operations.OutputQMsg       = ep3_outputq_msg;
++    rail->Generic.Operations.OutputQState     = ep3_outputq_state;
++    rail->Generic.Operations.OutputQSend      = ep3_outputq_send;
++
++    rail->Generic.Operations.FillOutStats     = ep3_fillout_stats;
++
++    rail->Generic.Devinfo = dev->Devinfo;
++
++    printk ("%s: connected via elan3 rev%c device %d\n", rail->Generic.Name,
++          'a' + dev->Devinfo.dev_revision_id, dev->Instance);
++
++    return (EP_RAIL *) rail;
++}
++
++void
++ep3_destroy_rail (EP_RAIL *r)
++{
++    EP3_RAIL *rail = (EP3_RAIL *) r;
++    
++    KMEM_FREE (rail, sizeof (EP3_RAIL));
++}
++
++static int
++ep3_attach_rail (EP3_RAIL *rail)
++{
++    ELAN3_DEV        *dev = rail->Device;
++    ELAN3_CTXT       *ctxt;
++    ELAN_CAPABILITY  *cap;
++    int               ctx;
++    unsigned long     flags;
++
++    if ((ctxt = elan3_alloc (dev, TRUE)) == (ELAN3_CTXT *) NULL)
++    {
++      printk ("%s: cannot allocate elan context\n", rail->Generic.Name);
++      return -ENXIO;
++    }
++    
++    ctxt->Operations = &ep3_elan3_ops;
++    ctxt->Private    = (void *) rail;
++    
++    /* Initialise a capability and attach to the elan*/
++    KMEM_ALLOC (cap, ELAN_CAPABILITY *, sizeof (ELAN_CAPABILITY), TRUE);
++    
++    elan_nullcap (cap);
++    
++    cap->cap_type        = ELAN_CAP_TYPE_KERNEL;
++    cap->cap_version     = ELAN_CAP_VERSION_NUMBER;
++    cap->cap_mycontext   = ELAN3_MRF_CONTEXT_NUM | SYS_CONTEXT_BIT;
++    cap->cap_lowcontext  = ELAN3_MRF_CONTEXT_NUM | SYS_CONTEXT_BIT;
++    cap->cap_highcontext = ELAN3_MRF_CONTEXT_NUM | SYS_CONTEXT_BIT;
++    cap->cap_railmask    = 1 << dev->Devinfo.dev_rail;
++    
++    /* Ensure the context filter is raised while we initialise */
++    elan3_block_inputter (ctxt, TRUE);
++
++    if (elan3_doattach (ctxt, cap) != 0)
++    {
++      printk ("%s: cannot attach to kernel context\n", rail->Generic.Name);
++
++      KMEM_FREE (cap, sizeof (ELAN_CAPABILITY));
++      elan3_free (ctxt);
++      return -ENXIO;
++    }
++    KMEM_FREE (cap, sizeof (ELAN_CAPABILITY));
++
++    /* now attach to all the kernel comms input/dmaring/data contexts */
++    spin_lock_irqsave (&dev->IntrLock, flags);
++
++    for (ctx = ELAN3_DMARING_BASE_CONTEXT_NUM; ctx <= ELAN3_DMARING_TOP_CONTEXT_NUM; ctx++)
++    {
++      /* place it in the info table.  NOTE: don't call elan3mmu_set_info, as this */
++      /* will queue the info again on the devices info list */
++      dev->CtxtTable[ctx] = ctxt;
++      
++      elan3mmu_set_context_filter (dev, ctx|SYS_CONTEXT_BIT, TRUE, 0, NULL);
++      elan3mmu_attach (dev, ctx, ctxt->Elan3mmu, ctxt->RouteTable->Table, ctxt->RouteTable->Size-1);
++    }
++
++    for (ctx = ELAN3_KCOMM_BASE_CONTEXT_NUM; ctx <= ELAN3_KCOMM_TOP_CONTEXT_NUM; ctx++)
++    {
++      /* place it in the info table.  NOTE: don't call elan3mmu_set_info, as this */
++      /* will queue the info again on the devices info list */
++      dev->CtxtTable[ctx] = ctxt;
++      
++      elan3mmu_set_context_filter (dev, ctx|SYS_CONTEXT_BIT, TRUE, 0, NULL);
++      elan3mmu_attach (dev, ctx, ctxt->Elan3mmu, ctxt->RouteTable->Table, ctxt->RouteTable->Size-1);
++    }
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++
++    /* Stash the ctxt,commandport, mmu and route table */
++    rail->Ctxt        = ctxt;
++    rail->CommandPort = ctxt->CommandPort;
++    rail->Elan3mmu    = ctxt->Elan3mmu;
++    rail->RouteTable  = ctxt->RouteTable;
++
++    return 0;
++}
++
++static void
++ep3_detach_rail (EP3_RAIL *rail)
++{
++    ELAN3_DEV *dev = rail->Device;
++    unsigned long flags;
++    int ctx;
++
++    /* detach from the elan */
++    spin_lock_irqsave (&dev->IntrLock, flags);
++
++    for (ctx = ELAN3_KCOMM_BASE_CONTEXT_NUM; ctx <= ELAN3_KCOMM_TOP_CONTEXT_NUM; ctx++)
++    {
++      dev->CtxtTable[ctx] = NULL;
++      elan3mmu_detach (dev, ctx);
++    }
++
++    for (ctx = ELAN3_DMARING_BASE_CONTEXT_NUM; ctx <= ELAN3_DMARING_TOP_CONTEXT_NUM; ctx++)
++    {
++      dev->CtxtTable[ctx] = NULL;
++      elan3mmu_detach (dev, ctx);
++    }
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++
++    elan3_dodetach(rail->Ctxt);
++    elan3_free (rail->Ctxt);
++
++    rail->Ctxt        = NULL;
++    rail->CommandPort = 0;
++    rail->Elan3mmu    = NULL;
++    rail->RouteTable  = NULL;
++}
++
++int
++ep3_start_rail (EP_RAIL *r)
++{
++    EP3_RAIL     *rail = (EP3_RAIL *) r;
++    int           i, res;
++    unsigned long flags;
++
++    if ((res = ep3_attach_rail (rail)) != 0)
++      return res;
++
++    spin_lock_init (&rail->CookieLock);
++    kmutex_init (&rail->HaltOpMutex);
++    kcondvar_init (&rail->HaltOpSleep);
++
++    /* Initialise event interrupt cookie table */
++    InitialiseCookieTable (&rail->CookieTable);
++
++    /* Load and map the thread code */
++    rail->ThreadCode = threadcode_elan3;
++    if (ep_loadcode (&rail->Generic, &rail->ThreadCode) != ESUCCESS)
++      goto failed;
++
++    /* Map the command port to be visible to the Elan */
++    ep3_ioaddr_map (&rail->Generic, EP3_COMMANDPORT_ADDR, rail->Ctxt->CommandPage, PAGESIZE, EP_PERM_WRITE);
++    rail->CommandPortAddr = EP3_COMMANDPORT_ADDR + (rail->Ctxt->CommandPort - rail->Ctxt->CommandPage);
++
++    /* Allocate the elan visible sdram/main memory */
++    if ((rail->RailElan = ep_alloc_elan (&rail->Generic, sizeof (EP3_RAIL_ELAN), 0, &rail->RailElanAddr)) == 0 ||
++      (rail->RailMain = ep_alloc_main (&rail->Generic, sizeof (EP3_RAIL_MAIN), 0, &rail->RailMainAddr)) == 0)
++    {
++      goto failed;
++    }
++
++    /* Allocate the system input queues at their fixed elan address */
++    if (! (rail->QueueDescs = ep_alloc_memory_elan (&rail->Generic, EP_SYSTEM_QUEUE_BASE, PAGESIZE, EP_PERM_ALL, 0)))
++      goto failed;
++
++    /* Initialise all queue entries to be full */
++    for (i = 0; i < EP_NUM_SYSTEMQ; i++)
++      elan3_sdram_writel (rail->Device, EP_SYSTEMQ_DESC(rail->QueueDescs, i) + offsetof (EP3_InputQueue, q_state), E3_QUEUE_FULL);
++
++    /* initialise the dma rings */
++    if (DmaRingsCreate (rail))
++      goto failed;
++    
++    if (InitialiseDmaRetries (rail))
++      goto failed;
++
++    if (ep3_init_probenetwork (rail))
++      goto failed;
++
++    /* can now drop the context filter for the system context */
++    spin_lock_irqsave (&rail->Device->IntrLock, flags);
++    elan3mmu_set_context_filter (rail->Device, ELAN3_MRF_CONTEXT_NUM|SYS_CONTEXT_BIT, FALSE, 0, NULL);
++    spin_unlock_irqrestore (&rail->Device->IntrLock, flags);
++
++    return 0;
++
++ failed:
++    printk ("ep3_start_rail: failed for rail %d\n", rail->Generic.Number);
++    ep3_stop_rail (&rail->Generic);
++
++    return -ENOMEM;
++}
++
++void
++ep3_stall_rail (EP_RAIL *r)
++{
++    EP3_RAIL     *rail = (EP3_RAIL *) r;
++    int           ctx;
++    unsigned long flags;
++
++    /* raise all the context filters */
++    spin_lock_irqsave (&rail->Device->IntrLock, flags);
++
++    for (ctx = ELAN3_KCOMM_BASE_CONTEXT_NUM; ctx <= ELAN3_KCOMM_TOP_CONTEXT_NUM; ctx++)
++      elan3mmu_set_context_filter (rail->Device, ctx|SYS_CONTEXT_BIT, TRUE, 0, NULL);
++
++    for (ctx = ELAN3_DMARING_BASE_CONTEXT_NUM; ctx <= ELAN3_DMARING_TOP_CONTEXT_NUM; ctx++)
++      elan3mmu_set_context_filter (rail->Device, ctx|SYS_CONTEXT_BIT, TRUE, 0, NULL);
++
++    elan3mmu_set_context_filter (rail->Device, ELAN3_MRF_CONTEXT_NUM|SYS_CONTEXT_BIT, TRUE, 0, NULL);
++
++    spin_unlock_irqrestore (&rail->Device->IntrLock, flags);
++}
++
++void
++ep3_stop_rail (EP_RAIL *r)
++{
++    EP3_RAIL *rail = (EP3_RAIL *) r;
++
++    ep3_destroy_probenetwork (rail);
++
++    if (rail->DmaRetryInitialised)
++      DestroyDmaRetries (rail);
++
++    DmaRingsRelease(rail);
++
++    if (rail->Generic.State == EP_RAIL_STATE_RUNNING)
++    {
++      KMEM_FREE (rail->MainCookies, rail->Generic.Position.pos_nodes * sizeof (E3_uint32));
++
++      ep_free_elan (&rail->Generic, rail->ElanCookies, rail->Generic.Position.pos_nodes * sizeof (E3_uint32));
++    }
++
++    if (rail->QueueDescs)
++      ep_free_memory_elan (&rail->Generic, EP_SYSTEM_QUEUE_BASE);
++    rail->QueueDescs = 0;
++
++    if (rail->RailMain)
++      ep_free_main (&rail->Generic, rail->RailMainAddr, sizeof (EP3_RAIL_MAIN));
++    rail->RailMain = 0;
++
++    if (rail->RailElan)
++      ep_free_elan (&rail->Generic, rail->RailElanAddr, sizeof (EP3_RAIL_ELAN));
++    rail->RailElan = 0;
++
++    ep_unloadcode (&rail->Generic, &rail->ThreadCode);
++
++    DestroyCookieTable (&rail->CookieTable);
++
++    ep_perrail_unmap (&rail->Generic, rail->Ctxt->CommandPage, PAGESIZE);
++
++    kcondvar_destroy (&rail->HaltOpSleep);
++    kmutex_destroy (&rail->HaltOpMutex);
++    spin_lock_destroy (&rail->CookieLock);
++
++    ep3_detach_rail (rail);
++}
++
++void
++ep3_position_found (EP_RAIL *r, ELAN_POSITION *pos)
++{
++    EP3_RAIL   *rail = (EP3_RAIL *) r;
++    sdramaddr_t addr;
++
++    rail->SwitchBroadcastLevelTick = lbolt;
++
++    elan3_sdram_writel (rail->Device, rail->RailElan + offsetof (EP3_RAIL_ELAN, NodeId), pos->pos_nodeid);
++
++    /* Allocate Network Identify cookie state */
++    KMEM_ZALLOC (rail->MainCookies, E3_uint32 *, pos->pos_nodes * sizeof (E3_uint32), 1);
++    
++    if (! (addr = ep_alloc_elan (&rail->Generic, pos->pos_nodes * sizeof (E3_uint32), 0, &rail->ElanCookies)))
++      panic ("ep: PositionFound: cannot allocate elan cookies array\n");
++
++    elan3_sdram_zeroq_sdram (rail->Device, addr, pos->pos_nodes * sizeof (E3_uint32));
++
++    ep3_probe_position_found (rail, pos);
++}
++
++sdramaddr_t
++ep3_sdram_alloc (EP_RAIL *r, EP_ADDR addr, unsigned size)
++{
++    return elan3_sdram_alloc (((EP3_RAIL *) r)->Device, size);
++}
++
++void
++ep3_sdram_free (EP_RAIL *r, sdramaddr_t addr, unsigned size)
++{
++    elan3_sdram_free (((EP3_RAIL *) r)->Device, addr, size);
++}
++
++void
++ep3_sdram_writeb (EP_RAIL *r, sdramaddr_t addr, unsigned char val)
++{
++    elan3_sdram_writeb (((EP3_RAIL *) r)->Device, addr, val);
++}
++
++void
++ep3_flush_tlb (EP_RAIL *r)
++{
++    EP3_RAIL *rail = (EP3_RAIL *) r;
++    ELAN3_DEV *dev = rail->Device;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->TlbLock, flags);
++    
++    IncrStat (dev, TlbFlushes);
++    
++    write_reg32 (dev, Cache_Control_Reg.ContReg, dev->Cache_Control_Reg | MMU_FLUSH);
++    mmiob ();
++    spin_unlock_irqrestore (&dev->TlbLock, flags);
++
++    while (! (read_reg32 (dev, Cache_Control_Reg.ContReg) & MMU_FLUSHED))
++      mb();
++}
++
++void
++ep3_load_system_route (EP_RAIL *r, unsigned vp, unsigned lowNode, unsigned highNode)
++{
++    EP3_RAIL  *rail = (EP3_RAIL *) r;
++    E3_uint16  flits[MAX_FLITS];
++    int        nflits;
++    
++    nflits = GenerateRoute (&rail->Generic.Position, flits, lowNode, highNode, DEFAULT_ROUTE_TIMEOUT, HIGH_ROUTE_PRIORITY);
++      
++    if (LoadRoute (rail->Device, rail->RouteTable, vp, ELAN3_MRF_CONTEXT_NUM|SYS_CONTEXT_BIT, nflits, flits) != 0)
++    {
++      /* XXXX: whilst LoadRoute() can fail - it is not likely. */
++      panic ("ep3_load_system_route: cannot load p2p route entry\n");
++    }
++}
++
++void
++ep3_load_node_route (EP_RAIL *r, unsigned nodeId)
++{
++    EP3_RAIL     *rail = (EP3_RAIL *) r;
++    E3_uint16     flits[MAX_FLITS];
++    int           nflits;
++
++    nflits = GenerateRoute (&rail->Generic.Position, flits, nodeId, nodeId, DEFAULT_ROUTE_TIMEOUT, DEFAULT_ROUTE_PRIORITY);
++
++    if (LoadRoute (rail->Device, rail->RouteTable, EP_VP_DATA(nodeId), EP3_CONTEXT_NUM(rail->Generic.Position.pos_nodeid), nflits, flits) != 0)
++      panic ("ep3_load_node_route: cannot load p2p data route entry\n");
++}
++
++void
++ep3_unload_node_route (EP_RAIL *r, unsigned nodeId)
++{
++    EP3_RAIL *rail = (EP3_RAIL *) r;
++
++    ClearRoute (rail->Device, rail->RouteTable, EP_VP_DATA(nodeId));
++}
++
++void
++ep3_lower_filter (EP_RAIL *r, unsigned nodeId)
++{
++    EP3_RAIL *rail = (EP3_RAIL *) r;
++    unsigned long flags;
++
++    spin_lock_irqsave (&rail->Device->IntrLock, flags);
++    elan3mmu_set_context_filter (rail->Device, EP3_CONTEXT_NUM(nodeId), 0, 0, NULL);
++    spin_unlock_irqrestore (&rail->Device->IntrLock, flags);
++}
++
++void
++ep3_raise_filter (EP_RAIL *r, unsigned nodeId)
++{
++    EP3_RAIL *rail = (EP3_RAIL *) r;
++    unsigned long flags;
++
++    spin_lock_irqsave (&rail->Device->IntrLock, flags);
++    elan3mmu_set_context_filter (rail->Device, EP3_CONTEXT_NUM(nodeId), 1, 0, NULL);
++    spin_unlock_irqrestore (&rail->Device->IntrLock, flags);
++}
++
++void
++ep3_node_disconnected (EP_RAIL *r, unsigned nodeId)
++{
++    FreeStalledDmas ((EP3_RAIL *) r, nodeId);
++}
++
++void
++ep3_fillout_stats(EP_RAIL *r, char *str) 
++{
++    /* no stats here yet */
++    /* EP3_RAIL *ep3rail = (EP3_RAIL *)r; */
++}
+Index: linux-2.6.5/drivers/net/qsnet/ep/kcomm_elan3.h
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/kcomm_elan3.h        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/kcomm_elan3.h     2005-05-11 12:10:12.523919656 -0400
+@@ -0,0 +1,431 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __EP_KCOMM_ELAN3_H
++#define __EP_KCOMM_ELAN3_H
++
++#ident "@(#)$Id: kcomm_elan3.h,v 1.50.8.3 2004/12/14 10:19:14 mike Exp $ $Name: QSNETMODULES-4-31_20050321 $"
++/*      $Source: /cvs/master/quadrics/epmod/kcomm_elan3.h,v $*/
++
++#if !defined(__ELAN3__)
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elanvp.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanctxt.h>
++#include <elan3/elandebug.h>
++#endif /* !defined(__ELAN3__) */
++
++#include <elan3/trtype.h>
++
++/* private address allocation */
++#define EP3_TEXT_BASE                 0xFF000000              /* base address for thread code (defined in makerules.elan3) */
++#define EP3_COMMANDPORT_ADDR          0xFFF00000              /* mapping address for elan command port */
++
++#define EP3_STACK_SIZE                        1024                    /* default thread code stack size */
++
++#define EP3_PACEMAKER_EVENTADDR               0xfeedbeef              /* mis-aligned address used by heartbeat pacemaker */
++
++/* context number allocation */
++#define EP3_CONTEXT_NUM(nodeId)               ((ELAN3_KCOMM_BASE_CONTEXT_NUM + (nodeId)) | SYS_CONTEXT_BIT)
++#define EP3_CONTEXT_ISDATA(ctx)               (((ctx) & MAX_ROOT_CONTEXT_MASK) >= ELAN3_KCOMM_BASE_CONTEXT_NUM && \
++                                       ((ctx) & MAX_ROOT_CONTEXT_MASK) <= ELAN3_KCOMM_TOP_CONTEXT_NUM)
++#define EP3_CONTEXT_TO_NODE(ctx)      (((ctx) & MAX_ROOT_CONTEXT_MASK) - ELAN3_KCOMM_BASE_CONTEXT_NUM)
++
++/* DMA issueing rings */
++#define EP3_RING_CRITICAL             0
++#define EP3_RING_CRITICAL_LEN         128
++#define EP3_RING_HIGH_PRI             1
++#define EP3_RING_HIGH_PRI_LEN         64
++#define EP3_RING_LOW_PRI              2
++#define EP3_RING_LOW_PRI_LEN          32
++#define EP3_NUM_RINGS                 3
++
++/* Value to "return" from c_close() when envelope handled  by the trap handler */
++#define EP3_PAckStolen                        4
++
++/* unimplemented instruction trap types for thread code */
++#define EP3_UNIMP_TRAP_NO_DESCS               0
++#define EP3_UNIMP_TRAP_PACKET_NACKED  1
++#define EP3_UNIMP_THREAD_HALTED               2
++#define EP3_NUM_UNIMP_TRAPS           3
++
++/* forward declarations */
++typedef struct ep3_rail       EP3_RAIL;
++
++/* block copy elan3 inputter queue - with waitvent0 */
++typedef struct ep3_inputqueue
++{
++    volatile E3_uint32        q_state;        /* queue is full=bit0, queue is locked=bit8 */
++    volatile E3_Addr  q_bptr;         /* block aligned ptr to current back item */
++    E3_uint32         q_size;         /* size of queue item; 0x1 <= size <= (0x40 * 5) */
++    E3_Addr           q_top;          /* block aligned ptr to last queue item */
++    E3_Addr           q_base;         /* block aligned ptr to first queue item */
++    volatile E3_Addr  q_fptr;         /* block aligned ptr to current front item */
++    E3_BlockCopyEvent q_event;        /* queue block copy event */
++    E3_uint32         q_pad[4];       /* pad to 64 bytes */
++    E3_Addr           q_wevent;       /* WaitEvent0 struct */
++    E3_int32          q_wcount;
++} EP3_InputQueue;
++
++
++#if !defined(__ELAN3__)
++
++/* dma retries types and retry times */
++typedef struct ep3_retry_dma
++{
++    struct list_head    Link;                                 /* chained on free/retry list */
++    long              RetryTime;                              /* "lbolt" to retry at */
++    E3_DMA_BE         Dma;                                    /* DMA (in main memory) */
++} EP3_RETRY_DMA;
++
++typedef struct ep3_dma_ring
++{
++    sdramaddr_t               pEvent;  
++    E3_Addr           epEvent;
++    
++    sdramaddr_t               pDma;     
++    E3_Addr           epDma; 
++    
++    E3_uint32        *pDoneBlk; 
++    E3_Addr           epDoneBlk; 
++    
++    int                       Entries;                                /* number of slots in array  */
++    int                       Position;                               /* current position in array */
++
++    ioaddr_t            CommandPort;
++    ioaddr_t          CommandPage;
++    DeviceMappingHandle CommandPageHandle;
++} EP3_DMA_RING;
++
++#define DMA_RING_EVENT(ring,n)                ((ring)->pEvent + (n)*sizeof (E3_BlockCopyEvent))
++#define DMA_RING_EVENT_ELAN(ring,n)   ((ring)->epEvent + (n)*sizeof (E3_BlockCopyEvent))
++
++#define DMA_RING_DMA(ring,n)          ((ring)->pDma   + (n)*sizeof (E3_DMA))
++#define DMA_RING_DMA_ELAN(ring,n)     ((ring)->epDma   + (n)*sizeof (E3_DMA))
++
++#define DMA_RING_DONE_ELAN(ring,n)    ((ring)->epDoneBlk + (n)*sizeof (E3_uint32))
++
++/* Event interrupt cookie operations and lookup table */
++typedef struct ep3_cookie_ops
++{
++    void      (*Event)       (EP3_RAIL *rail, void *arg);                             /* called from the interrupt handler when an event is "set" */
++    void      (*DmaRetry)    (EP3_RAIL *rail, void *arg, E3_DMA_BE *dma, int error);  /* called from the interrupt handler when a DMA is "nacked" */
++    void      (*DmaCancelled)(EP3_RAIL *rail, void *arg, E3_DMA_BE *dma);             /* called from the interrupt handler/flush disconnecting when cancelled. */
++    void      (*DmaVerify)   (EP3_RAIL *rail, void *arg, E3_DMA_BE *dma);             /* called from multiple places, to check dma is consistent with state. */
++} EP3_COOKIE_OPS;
++
++typedef struct ep3_cookie
++{
++    struct ep3_cookie  *Next;                                 /* Cookies are chained in hash table. */
++    E3_uint32         Cookie;                                 /* Cooke store in ev_Type */
++    EP3_COOKIE_OPS     *Operations;                           /* Cookie operations */
++    void             *Arg;                                    /* Users arguement. */
++} EP3_COOKIE;
++
++#define EP3_COOKIE_HASH_SIZE          (256)
++#define EP3_HASH_COOKIE(a)            ((((a) >> 3) ^ ((a) >> 7) ^ ((a) >> 11)) & (EP3_COOKIE_HASH_SIZE-1))
++
++typedef struct ep3_cookie_table
++{
++    spinlock_t                Lock;
++    EP3_COOKIE               *Entries[EP3_COOKIE_HASH_SIZE];
++} EP3_COOKIE_TABLE;
++
++#endif /* !defined(__ELAN3__) */
++
++#define EP3_EVENT_FREE                        ((1 << 4) | EV_WCOPY)
++#define EP3_EVENT_ACTIVE              ((2 << 4) | EV_WCOPY)
++/* DONE == Cookie */
++#define EP3_EVENT_FAILED              ((3 << 4) | EV_WCOPY)
++#define EP3_EVENT_PRIVATE             ((4 << 4) | EV_WCOPY)
++
++/* The event cookie can get posted (and seen) before the write has */
++/* hit main memory - in this case the event count is <= 0 and the block */
++/* will be marked as ACTIVE - but could transition to DONE at any time */
++/* Also for a word copy event, the value written into the "done" word */
++/* can be the event interrupt cookie rather than the "source" value */
++/* this happens since the uCode does not wait for the write to have */
++/* occured before overwriting TMP_0 with the cookie */
++#define EP3_EVENT_FIRING(edev, event, cookie, done) \
++      (((((done) & ~(EV_TYPE_BCOPY | EV_TYPE_MASK_EVIRQ)) == (cookie).Cookie) || (done) == EP3_EVENT_ACTIVE) && \
++       (int) elan3_sdram_readl (edev, (event) + offsetof (E3_BlockCopyEvent, ev_Count)) <= 0)
++#define EP3_EVENT_FIRED(cookie, done) \
++      (((done) & ~(EV_TYPE_BCOPY | EV_TYPE_MASK_EVIRQ)) == (cookie).Cookie)
++
++
++/* Time limit to wait while event is firing and block write has not occured */
++#define EP3_EVENT_FIRING_TLIMIT               16384                   /* 1023 uS */
++
++#define EP3_INIT_COPY_EVENT(event, cookie, dest, intr)                                                        \
++{                                                                                                     \
++      (event).ev_Count  = 0;                                                                          \
++      (event).ev_Type   = (intr) ? EV_TYPE_BCOPY | EV_TYPE_EVIRQ | (cookie).Cookie : EV_TYPE_BCOPY;   \
++      (event).ev_Source = (cookie).Cookie | EV_WCOPY;                                                 \
++      (event).ev_Dest   = (dest) | EV_TYPE_BCOPY_WORD;                                                \
++}
++
++#if !defined(__ELAN3__)
++
++/* Generic input queues which can be polled */
++typedef struct ep3_inputq
++{
++    EP3_COOKIE                q_cookie;
++    unsigned int      q_slotSize;
++    unsigned int      q_slotCount;
++
++    void             *q_slots;
++    EP_ADDR           q_slotsAddr;
++
++    EP_INPUTQ_CALLBACK *q_callback;
++    void             *q_arg;
++
++    sdramaddr_t               q_desc;
++    E3_Addr           q_descAddr;
++
++    E3_Addr           q_base;
++    E3_Addr           q_top;
++    E3_Addr           q_fptr;
++
++    E3_uint32         q_waitCount;
++} EP3_INPUTQ;
++
++typedef struct ep3_outputq
++{
++    EP3_COOKIE                q_cookie;
++
++    unsigned int      q_slotCount;                            /* # slots allocated */
++    unsigned int      q_slotSize;                             /* size of each slot (rounded up) */
++
++    sdramaddr_t               q_elan;
++    E3_Addr           q_elanAddr;
++
++    void             *q_main;
++    E3_Addr           q_mainAddr;
++} EP3_OUTPUTQ;
++
++#endif /* !defined(__ELAN3__) */
++
++/* per-rail elan memory portion of device */
++typedef struct ep3_rail_elan
++{
++    E3_uint16          ProbeSource0[TR_TRACEROUTE_ENTRIES];   /* 32 byte aligned */
++    E3_uint16          ProbeSource1[TR_TRACEROUTE_ENTRIES];
++
++    E3_BlockCopyEvent  ProbeDone;                             /* 16 byte aligned */
++    E3_Event           ProbeStart;                            /* 8 byte aligned */
++    
++    E3_uint32          ProbeType;                             /* 4 byte aligned */
++    E3_uint32          ProbeLevel;
++
++    E3_uint32          NodeId;
++} EP3_RAIL_ELAN;
++
++/* values for ProbeType */
++#define PROBE_SINGLE                  0
++#define PROBE_MULTIPLE                        1
++/* number of attempts for each type */
++#define PROBE_SINGLE_ATTEMPTS         10
++#define PROBE_SINGLE_TIMEOUTS         5
++#define PROBE_MULTIPLE_ATTEMPTS               20
++#define PROBE_MULTIPLE_TIMEOUTS               10
++
++/* per-rail elan memory portsion of device */
++typedef struct ep3_rail_main
++{
++    E3_uint16         ProbeDest0[TR_TRACEROUTE_ENTRIES];      /* 32 byte aligned */
++    E3_uint16         ProbeDest1[TR_TRACEROUTE_ENTRIES];
++    
++    E3_uint32         ProbeDone;                              /* 4 byte aligned */
++    E3_uint32         ProbeResult;
++    E3_uint32         ProbeLevel;
++} EP3_RAIL_MAIN;
++
++#if !defined(__ELAN3__)
++
++struct ep3_rail
++{
++    EP_RAIL           Generic;                                /* Generic rail */
++
++    ELAN3_DEV          *Device;                                       /* Elan device we're using */
++    ELAN3_CTXT               *Ctxt;                                   /* Elan context struct */
++    ioaddr_t            CommandPort;                          /* commandport from context */
++    E3_Addr           CommandPortAddr;                        /*  and address mapped into elan */
++
++    ELAN3_ROUTE_TABLE  *RouteTable;                           /* routetable from context */
++    ELAN3MMU         *Elan3mmu;                               /* elanmmu from context */
++
++    EP3_COOKIE_TABLE    CookieTable;                          /* Event cookie table */
++
++    EP_CODE           ThreadCode;                             /* copy of thread code */
++    unsigned int        CommandPortEventTrap;                 /* flag to indicate command port eventint queue overflow trap */
++
++    sdramaddr_t         RailElan;                             /* Elan visible main/sdram portions of */
++    E3_Addr             RailElanAddr;                         /* device structure */
++    EP3_RAIL_MAIN      *RailMain;
++    E3_Addr           RailMainAddr;
++
++    /* small system message queues */
++    sdramaddr_t               QueueDescs;                             /* Input Queue descriptors */
++
++    /* Network position prober */
++    E3_Addr           ProbeStack;                             /* Network position thread command structure */
++    EP3_COOKIE                ProbeCookie;                            /*   event cookie for Done event */
++    kcondvar_t                ProbeWait;                              /*   place to wait on probe thread */
++    spinlock_t                ProbeLock;                              /*     and lock */
++    volatile int        ProbeDone;                            /*     and flag to indicate it's done */
++
++    E3_uint16         ProbeDest0[TR_TRACEROUTE_ENTRIES];      /* last result of CheckNetworkPosition */
++    E3_uint16         ProbeDest1[TR_TRACEROUTE_ENTRIES];
++    E3_uint32         ProbeResult;
++
++    long              ProbeLevelTick[ELAN_MAX_LEVELS];
++    long              SwitchBroadcastLevelTick;
++
++    /* rings for issueing dmas */
++    EP3_DMA_RING        DmaRings[EP3_NUM_RINGS];
++
++    /* retry lists for dmas */
++    struct list_head    DmaRetries[EP_NUM_RETRIES];           /* Dma retry lists */
++    struct list_head    DmaRetryFreeList;                     /*   and free list */
++    u_int             DmaRetryCount;                          /*   and total retry count */
++    u_int             DmaRetryReserved;                       /*   and number reserved */
++    u_int             DmaRetryThreadShouldStall;              /*   count of reasons to stall retries */
++    u_int             DmaRetryThreadStarted:1;                /*   dma retry thread running */
++    u_int             DmaRetryThreadShouldStop:1;             /*     but should stop */
++    u_int             DmaRetryThreadStopped:1;                /*     and now it's stopped */
++    u_int             DmaRetryInitialised:1;                  /* have initialise dma retries */
++
++    spinlock_t                DmaRetryLock;                           /*   spinlock protecting lists */
++    kcondvar_t                DmaRetryWait;                           /*   place retry thread sleeps */
++    long              DmaRetryTime;                           /*   and when it will next wakeup */
++    unsigned int        DmaRetrySleeping;                     /*   and it's sleeping there */
++
++    /* Network Identify Cookies */
++    E3_uint32        *MainCookies;                            /* One cookie allocator per-node for main*/
++    E3_Addr           ElanCookies;                            /*   and one for elan */
++    spinlock_t                CookieLock;                             /* spinlock to protect main cookies */
++
++    /* Halt operation flags for flushing. */
++    kmutex_t            HaltOpMutex;                          /* serialize access to halt operations */
++    unsigned int      HaltOpCompleted;                        /* flag to indicate halt operation completed */
++    kcondvar_t                HaltOpSleep;                            /*   place to wait for it to complete */
++
++    /* Network error state */
++    kcondvar_t                NetworkErrorSleep;                      /* place to sleep for network error halt operation */
++    u_int             NetworkErrorFlushed;                    /*   and flag to indicate flushed */
++
++
++    EP3_RAIL_STATS    Stats;                                  /* statistics */
++};
++
++/* support.c */
++
++extern ELAN3_OPS  ep3_elan3_ops;
++
++extern E3_uint32    LocalCookie (EP3_RAIL *rail, unsigned int remoteNode);
++extern E3_uint32    RemoteCookie (EP3_RAIL *rail, unsigned int remoteNode);
++
++extern void         InitialiseCookieTable (EP3_COOKIE_TABLE *table);
++extern void         DestroyCookieTable (EP3_COOKIE_TABLE *table);
++extern void         RegisterCookie (EP3_COOKIE_TABLE *table, EP3_COOKIE *cookie, 
++                                  E3_Addr event, EP3_COOKIE_OPS *ops, void *arg);
++extern void         DeregisterCookie (EP3_COOKIE_TABLE *table, EP3_COOKIE *cookie);
++extern EP3_COOKIE   *LookupCookie (EP3_COOKIE_TABLE *table, uint32_t cookie);
++extern EP3_COOKIE   *LookupEventCookie (EP3_RAIL *rail, EP3_COOKIE_TABLE *table, E3_Addr);
++
++extern int          DmaRingsCreate (EP3_RAIL *rail);
++extern void         DmaRingsRelease (EP3_RAIL *rail);
++extern int          IssueDma (EP3_RAIL *rail, E3_DMA_BE *dma, int type, int retryThread);
++
++extern int          IssueWaitevent (EP3_RAIL *rail, E3_Addr value);
++extern void         IssueSetevent (EP3_RAIL *rail, E3_Addr value);
++extern void         IssueRunThread (EP3_RAIL *rail, E3_Addr value);
++extern long         DmaRetryTime (int type);
++extern int          InitialiseDmaRetries (EP3_RAIL *rail);
++extern void         DestroyDmaRetries (EP3_RAIL *rail);
++extern int          ReserveDmaRetries (EP3_RAIL *rail, int count, EP_ATTRIBUTE attr);
++extern void         ReleaseDmaRetries (EP3_RAIL *rail, int count);
++extern void         StallDmaRetryThread (EP3_RAIL *rail);
++extern void         ResumeDmaRetryThread (EP3_RAIL *rail);
++extern void         QueueDmaForRetry (EP3_RAIL *rail, E3_DMA_BE *dma, int interval);
++extern void         QueueDmaOnStalledList (EP3_RAIL *rail, E3_DMA_BE *dma);
++extern void         FreeStalledDmas (EP3_RAIL *rail, unsigned int nodeId);
++
++extern void         SetQueueLocked(EP3_RAIL *rail, sdramaddr_t qaddr);
++
++/* threadcode_elan3.c */
++extern E3_Addr    ep3_init_thread (ELAN3_DEV *dev, E3_Addr fn, E3_Addr addr, sdramaddr_t stack,
++                                 int stackSize, int nargs, ...);
++
++/* probenetwork.c */
++extern int        ep3_init_probenetwork (EP3_RAIL *rail);
++extern void       ep3_destroy_probenetwork (EP3_RAIL *rail);
++extern void       ep3_probe_position_found (EP3_RAIL *rail, ELAN_POSITION *pos);
++extern int        ep3_probe_route (EP_RAIL *r, int level, int sw, int nodeid, int *linkup, int *linkdown, int attempts, EP_SWITCH *lsw);
++extern int        ep3_check_position (EP_RAIL *rail);
++
++/* neterr_elan3.c */
++extern void       ep3_neterr_fixup (EP_RAIL *r, unsigned int nodeId, EP_NETERR_COOKIE *cookies);
++
++/* kcomm_elan3.c */
++extern EP_RAIL    *ep3_create_rail (EP_SYS *sys, ELAN3_DEV *dev);
++extern void        ep3_destroy_rail (EP_RAIL *rail);
++
++extern int         ep3_start_rail (EP_RAIL *rail);
++extern void        ep3_stall_rail (EP_RAIL *rail);
++extern void        ep3_stop_rail (EP_RAIL *rail);
++
++extern void      ep3_position_found (EP_RAIL *rail, ELAN_POSITION *pos);
++
++extern sdramaddr_t ep3_sdram_alloc (EP_RAIL *rail, EP_ADDR addr, unsigned int size);
++extern void        ep3_sdram_free (EP_RAIL *rail, sdramaddr_t addr, unsigned int size);
++extern void        ep3_sdram_writeb (EP_RAIL *rail, sdramaddr_t addr, unsigned char val);
++
++extern void        ep3_flush_tlb (EP_RAIL *r);
++extern void        ep3_load_system_route (EP_RAIL *r, unsigned int vp, unsigned int lowNode, unsigned int highNode);
++extern void        ep3_load_node_route (EP_RAIL *r, unsigned int nodeId);
++extern void        ep3_unload_node_route (EP_RAIL *r, unsigned int nodeId);
++extern void        ep3_lower_filter (EP_RAIL *r, unsigned int nodeId);
++extern void        ep3_raise_filter (EP_RAIL *rail, unsigned int nodeId);
++extern void        ep3_node_disconnected (EP_RAIL *r, unsigned int nodeId);
++
++extern void        ep3_fillout_stats(EP_RAIL *rail, char *str);
++
++/* kmap_elan3.c */
++extern void        ep3_kaddr_map (EP_RAIL *r, EP_ADDR eaddr, virtaddr_t kaddr, unsigned int len, unsigned int perm, int ep_attr);
++extern void        ep3_sdram_map (EP_RAIL *r, EP_ADDR eaddr, sdramaddr_t saddr, unsigned int len, unsigned int perm, int ep_attr);
++extern void        ep3_ioaddr_map (EP_RAIL *r, EP_ADDR eaddr, ioaddr_t ioaddr, unsigned int len, unsigned int perm);
++extern void        ep3_unmap (EP_RAIL *r, EP_ADDR eaddr, unsigned int len);
++extern void       *ep3_dvma_reserve (EP_RAIL *r, EP_ADDR eaddr, unsigned int npages);
++extern void        ep3_dvma_release (EP_RAIL *r, EP_ADDR eaddr, unsigned int npages, void *private);
++extern void        ep3_dvma_set_pte (EP_RAIL *r, void *private, unsigned int index, physaddr_t paddr, unsigned int perm);
++extern physaddr_t  ep3_dvma_read_pte (EP_RAIL *r, void *private, unsigned int index);
++extern void        ep3_dvma_unload (EP_RAIL *r, void *private, unsigned int index, unsigned int npages);
++
++/* kmsg_elan3.c */
++extern EP_INPUTQ  *ep3_alloc_inputq (EP_RAIL *r, unsigned int qnum, unsigned int slotSize, unsigned int slotCount,
++                                   EP_INPUTQ_CALLBACK *callback, void *arg);
++extern void        ep3_free_inputq (EP_RAIL *r, EP_INPUTQ *q);
++extern void        ep3_enable_inputq (EP_RAIL *r, EP_INPUTQ *q);
++extern void        ep3_disable_inputq (EP_RAIL *r, EP_INPUTQ *q);
++extern int         ep3_poll_inputq (EP_RAIL *r, EP_INPUTQ *q, int maxCount, EP_INPUTQ_HANDLER *handler, void *arg);
++extern EP_OUTPUTQ *ep3_alloc_outputq (EP_RAIL *r, unsigned int slotSize, unsigned int slotCount);
++extern void        ep3_free_outputq (EP_RAIL *r, EP_OUTPUTQ *q);
++extern void       *ep3_outputq_msg (EP_RAIL *r, EP_OUTPUTQ *q, unsigned int slotNum);
++extern int         ep3_outputq_state (EP_RAIL *r, EP_OUTPUTQ *q, unsigned int slotNum);
++extern int         ep3_outputq_send (EP_RAIL *r, EP_OUTPUTQ *q, unsigned int slotNum, unsigned int size,
++                                   unsigned int nodeId, unsigned int qnum, unsigned int retries);
++
++/* support_elan3.c */
++extern void        ep3_flush_filters (EP_RAIL *r);
++extern void        ep3_flush_queues (EP_RAIL *r);
++
++#endif /* !defined(__ELAN3__) */
++
++#endif /* __EP_KCOMM_ELAN3_H */
+Index: linux-2.6.5/drivers/net/qsnet/ep/kcomm_elan4.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/kcomm_elan4.c        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/kcomm_elan4.c     2005-05-11 12:10:12.524919504 -0400
+@@ -0,0 +1,526 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: kcomm_elan4.c,v 1.16.2.3 2004/11/30 12:02:17 mike Exp $ $Name: QSNETMODULES-4-31_20050321 $"
++/*      $Source: /cvs/master/quadrics/epmod/kcomm_elan4.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <qsnet/kthread.h>
++
++#include <elan/kcomm.h>
++
++#include "kcomm_vp.h"
++#include "kcomm_elan4.h"
++#include "conf_linux.h"
++
++extern EP_CODE threadcode_elan4;
++
++unsigned int
++ep4_create_rails (EP_SYS *sys, unsigned int disabled)
++{
++    unsigned int rmask = 0;
++    ELAN4_DEV   *dev;
++    EP_RAIL     *rail;
++    int          i;
++
++    for (i = 0; i < EP_MAX_RAILS; i++)
++    {
++      if ((dev = elan4_reference_device (i, ELAN4_STATE_STARTED)) != NULL)
++      {
++          if ((rail = ep4_create_rail (sys, dev)) == NULL)
++              elan4_dereference_device (dev);
++          else
++          {
++              if (disabled & (1 << rail->Number))
++                  printk ("%s: auto-start of device disabled by configuration\n", rail->Name);
++              else
++                  ep_start_rail (rail);
++              
++              ep_procfs_rail_init(rail);
++
++              rmask |= (1 << rail->Number);
++          }
++      }
++    }
++
++    if (rmask)
++      qsnet_debug_alloc();
++
++    return rmask;
++}
++
++EP_RAIL *
++ep4_create_rail (EP_SYS *sys, ELAN4_DEV *dev)
++{
++    EP4_RAIL *rail;
++    int res;
++
++    KMEM_ZALLOC (rail, EP4_RAIL *, sizeof (EP4_RAIL), 1);
++
++    if (rail == NULL)
++      return (EP_RAIL *) NULL;
++    
++    if ((res = ep_init_rail (sys, &rail->r_generic)) != 0)
++    {
++      KMEM_FREE (rail, sizeof (EP4_RAIL));
++      return (EP_RAIL *) NULL;
++    }
++
++    rail->r_ctxt.ctxt_dev = dev;
++
++    /* install our rail operations */
++    rail->r_generic.Operations.DestroyRail      = ep4_destroy_rail;
++    rail->r_generic.Operations.StartRail        = ep4_start_rail;
++    rail->r_generic.Operations.StallRail        = ep4_stall_rail;
++    rail->r_generic.Operations.StopRail         = ep4_stop_rail;    
++
++    rail->r_generic.Operations.SdramAlloc       = ep4_sdram_alloc;
++    rail->r_generic.Operations.SdramFree        = ep4_sdram_free;
++    rail->r_generic.Operations.SdramWriteb      = ep4_sdram_writeb;
++
++    rail->r_generic.Operations.KaddrMap         = ep4_kaddr_map;
++    rail->r_generic.Operations.SdramMap         = ep4_sdram_map;
++    rail->r_generic.Operations.Unmap            = ep4_unmap;
++
++    rail->r_generic.Operations.DvmaReserve    = ep4_dvma_reserve;
++    rail->r_generic.Operations.DvmaRelease    = ep4_dvma_release;
++    rail->r_generic.Operations.DvmaSetPte     = ep4_dvma_set_pte;
++    rail->r_generic.Operations.DvmaReadPte    = ep4_dvma_read_pte;
++    rail->r_generic.Operations.DvmaUnload     = ep4_dvma_unload;
++    rail->r_generic.Operations.FlushTlb               = ep4_flush_tlb;
++
++    rail->r_generic.Operations.ProbeRoute       = ep4_probe_route;
++
++    rail->r_generic.Operations.PositionFound    = ep4_position_found;
++    rail->r_generic.Operations.CheckPosition    = ep4_check_position;
++    rail->r_generic.Operations.NeterrFixup      = ep4_neterr_fixup;
++
++    rail->r_generic.Operations.LoadSystemRoute  = ep4_load_system_route;
++
++    rail->r_generic.Operations.LoadNodeRoute    = ep4_load_node_route;
++    rail->r_generic.Operations.UnloadNodeRoute  = ep4_unload_node_route;
++    rail->r_generic.Operations.LowerFilter    = ep4_lower_filter;
++    rail->r_generic.Operations.RaiseFilter    = ep4_raise_filter;
++    rail->r_generic.Operations.NodeDisconnected = ep4_node_disconnected;
++
++    rail->r_generic.Operations.FlushFilters     = ep4_flush_filters;
++    rail->r_generic.Operations.FlushQueues    = ep4_flush_queues;
++
++    rail->r_generic.Operations.AllocInputQ    = ep4_alloc_inputq;
++    rail->r_generic.Operations.FreeInputQ     = ep4_free_inputq;
++    rail->r_generic.Operations.EnableInputQ     = ep4_enable_inputq;
++    rail->r_generic.Operations.DisableInputQ    = ep4_disable_inputq;
++    rail->r_generic.Operations.PollInputQ     = ep4_poll_inputq;
++
++    rail->r_generic.Operations.AllocOutputQ     = ep4_alloc_outputq;
++    rail->r_generic.Operations.FreeOutputQ    = ep4_free_outputq;
++    rail->r_generic.Operations.OutputQMsg     = ep4_outputq_msg;
++    rail->r_generic.Operations.OutputQState     = ep4_outputq_state;
++    rail->r_generic.Operations.OutputQSend    = ep4_outputq_send;
++
++    rail->r_generic.Operations.FillOutStats     = ep4_fillout_stats;
++    rail->r_generic.Operations.Debug          = ep4_debug_rail;
++
++    rail->r_generic.Devinfo = dev->dev_devinfo;
++
++    printk ("%s: connected via elan4 rev%c device %d\n", rail->r_generic.Name,
++          'a' + dev->dev_devinfo.dev_revision_id, dev->dev_instance);
++
++    return (EP_RAIL *) rail;
++}
++
++void
++ep4_destroy_rail (EP_RAIL *r)
++{
++    EP4_RAIL *rail = (EP4_RAIL *) r;
++
++    elan4_dereference_device (rail->r_ctxt.ctxt_dev);
++
++    KMEM_FREE (rail, sizeof (EP4_RAIL));
++}
++
++static int
++ep4_attach_rail (EP4_RAIL *r)
++{
++    EP4_RAIL  *rail = (EP4_RAIL *) r;
++    ELAN4_DEV *dev  = rail->r_ctxt.ctxt_dev;
++    unsigned   ctx;
++
++    if (elan4_insertctxt (dev, &rail->r_ctxt, &ep4_trap_ops) != 0)
++      return -ENOMEM;
++    
++    if ((rail->r_routetable = elan4_alloc_routetable (dev, 4)) == NULL)       /* 512 << 4 == 8192 entries */
++    {
++      elan4_removectxt (dev, &rail->r_ctxt);
++      return -ENOMEM;
++    }
++    elan4_set_routetable (&rail->r_ctxt, rail->r_routetable);
++
++    /* Attach to the kernel comms nextwork context */
++    if (elan4_attach_filter (&rail->r_ctxt, ELAN4_KCOMM_CONTEXT_NUM) < 0)
++    {
++      elan4_free_routetable (dev, rail->r_routetable);
++      elan4_removectxt (dev, &rail->r_ctxt);
++
++      return -EBUSY;
++    }
++
++    for (ctx = ELAN4_KCOMM_BASE_CONTEXT_NUM; ctx <= ELAN4_KCOMM_TOP_CONTEXT_NUM; ctx++)
++      elan4_attach_filter (&rail->r_ctxt, ctx);
++
++    return 0;
++}
++
++static void
++ep4_detach_rail (EP4_RAIL *rail)
++{
++    ELAN4_DEV *dev = rail->r_ctxt.ctxt_dev;
++    unsigned   ctx;
++
++    elan4_detach_filter (&rail->r_ctxt, ELAN4_KCOMM_CONTEXT_NUM);
++
++    for (ctx = ELAN4_KCOMM_BASE_CONTEXT_NUM; ctx <= ELAN4_KCOMM_TOP_CONTEXT_NUM; ctx++)
++      elan4_detach_filter (&rail->r_ctxt, ctx);
++
++    if (rail->r_routetable)
++    {
++      elan4_set_routetable (&rail->r_ctxt, NULL);
++      elan4_free_routetable (dev, rail->r_routetable);
++    }
++
++    elan4_removectxt (dev, &rail->r_ctxt);
++}
++
++int
++ep4_start_rail (EP_RAIL *r)
++{
++    EP4_RAIL     *rail = (EP4_RAIL *) r;
++    ELAN4_DEV    *dev  = rail->r_ctxt.ctxt_dev;
++    E4_InputQueue qdesc;
++    int           i, res;
++
++    if ((res = ep4_attach_rail (rail)) < 0)
++      return res;
++
++    /* Initialise main interrupt cookie table */
++    spin_lock_init (&rail->r_intcookie_lock);
++    for (i = 0; i < EP4_INTCOOKIE_HASH_SIZE; i++)
++      INIT_LIST_HEAD (&rail->r_intcookie_hash[i]);
++
++    kmutex_init (&rail->r_haltop_mutex);
++    kcondvar_init (&rail->r_haltop_sleep);
++    spin_lock_init (&rail->r_haltop_lock);
++
++    spin_lock_init (&rail->r_cookie_lock);
++
++    INIT_LIST_HEAD (&rail->r_ecq_list[EP4_ECQ_EVENT]);
++    INIT_LIST_HEAD (&rail->r_ecq_list[EP4_ECQ_ATOMIC]);
++    INIT_LIST_HEAD (&rail->r_ecq_list[EP4_ECQ_SINGLE]);
++    INIT_LIST_HEAD (&rail->r_ecq_list[EP4_ECQ_MAIN]);
++    spin_lock_init (&rail->r_ecq_lock);
++
++    ep_kthread_init (&rail->r_retry_thread);
++    INIT_LIST_HEAD (&rail->r_retry_ops);
++
++    INIT_LIST_HEAD (&rail->r_neterr_ops);
++
++    kmutex_init (&rail->r_flush_mutex);
++    kcondvar_init (&rail->r_flush_sleep);
++
++    /* Allocate the elan visible sdram/main memory */
++    if ((rail->r_elan = ep_alloc_elan (&rail->r_generic, sizeof (EP4_RAIL_ELAN), 0, &rail->r_elan_addr)) == 0 ||
++      (rail->r_main = ep_alloc_main (&rail->r_generic, sizeof (EP4_RAIL_MAIN), 0, &rail->r_main_addr)) == 0)
++    {
++      goto failed;
++    }
++
++    for (i = 0; i < EP_NUM_SYSTEMQ; i++)
++      elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_qevents[i].ev_CountAndType), 0);
++
++    elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_flush_event.ev_CountAndType), E4_EVENT_INIT_VALUE (0, E4_EVENT_WRITE, E4_EVENT_DTYPE_LONG, 0));
++
++    /* Allocate the system input queues at their fixed elan address */
++    /*   avoid sdram address aliasing by allocating the min sdram pagesize */
++    if (! (rail->r_queuedescs= ep_alloc_memory_elan (&rail->r_generic, EP_SYSTEM_QUEUE_BASE, SDRAM_PAGE_SIZE, EP_PERM_ALL, 0)))
++      goto failed;
++
++    /* Initialise the input queue descriptor as "full" with no event */
++    qdesc.q_bptr    = 0;
++    qdesc.q_fptr    = 8;
++    qdesc.q_control = E4_InputQueueControl(qdesc.q_bptr, qdesc.q_fptr, 8);
++    qdesc.q_event   = 0;
++
++    for (i = 0; i < EP_NUM_SYSTEMQ; i++)
++      elan4_sdram_copyq_to_sdram (dev, &qdesc, EP_SYSTEMQ_DESC (rail->r_queuedescs, i), sizeof (E4_InputQueue));
++
++    /* Allocate the resource map for command queue mappings */
++    if ((rail->r_ecq_rmap = ep_rmallocmap (EP4_ECQ_RMAPSIZE, "r_ecq_rmap", 1)) == NULL)
++      goto failed;
++    
++    ep_rmfree (rail->r_ecq_rmap, EP4_ECQ_TOP - EP4_ECQ_BASE, EP4_ECQ_BASE);
++
++    /* register an interrupt cookie & allocate command queues for command queue flushing */
++    rail->r_flush_mcq = ep4_get_ecq (rail, EP4_ECQ_MAIN, 4);
++    rail->r_flush_ecq = ep4_get_ecq (rail, EP4_ECQ_EVENT, 1);
++
++    if (rail->r_flush_mcq == NULL || rail->r_flush_ecq == NULL)
++      goto failed;
++
++    ep4_register_intcookie (rail, &rail->r_flush_intcookie, rail->r_elan_addr + offsetof (EP4_RAIL_ELAN, r_flush_event), ep4_flush_interrupt, rail);
++
++    /* startup the retry thread */
++    if (kernel_thread_create (ep4_retry_thread, (void *) rail) == 0)
++      goto failed;
++    ep_kthread_started (&rail->r_retry_thread);
++    
++    ep4_initialise_dma_retries (rail);
++
++    if ((rail->r_event_ecq = ep4_alloc_ecq (rail, CQ_Size1K)) == NULL)
++      goto failed;
++    
++    rail->r_threadcode = threadcode_elan4;
++    if (ep_loadcode (&rail->r_generic, &rail->r_threadcode))
++      goto failed;
++
++    elan4_flush_icache (&rail->r_ctxt);
++
++    if (ep4_probe_init (rail))
++      goto failed;
++
++    /* can now drop the context filter for the system context */
++    elan4_set_filter (&rail->r_ctxt, ELAN4_KCOMM_CONTEXT_NUM, E4_FILTER_HIGH_PRI);
++
++    return 0;
++
++ failed:
++    printk ("ep4_start_rail: failed for rail '%s'\n", rail->r_generic.Name);
++    ep4_stop_rail (&rail->r_generic);
++
++    return -ENOMEM;
++}
++
++void
++ep4_stall_rail (EP_RAIL *r)
++{
++    EP4_RAIL *rail = (EP4_RAIL *) r;
++    unsigned  ctx;
++
++    /* Raise all the context filters */
++    elan4_set_filter (&rail->r_ctxt, ELAN4_KCOMM_CONTEXT_NUM, E4_FILTER_DISCARD_ALL);
++
++    for (ctx = ELAN4_KCOMM_BASE_CONTEXT_NUM; ctx <= ELAN4_KCOMM_TOP_CONTEXT_NUM; ctx++)
++      elan4_set_filter (&rail->r_ctxt, ctx, E4_FILTER_DISCARD_ALL);
++}
++
++void
++ep4_stop_rail (EP_RAIL *r)
++{
++    EP4_RAIL *rail = (EP4_RAIL *) r;
++
++    if (rail->r_generic.State == EP_RAIL_STATE_RUNNING) /* undo ep4_position_found() */
++    {
++      ELAN_POSITION *pos  = &rail->r_generic.Position;
++      EP_ADDR        addr = elan4_sdram_readq (rail->r_ctxt.ctxt_dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_cookies));
++
++      ep_free_elan (&rail->r_generic, addr, pos->pos_nodes * sizeof (E4_uint64));
++
++      KMEM_FREE (rail->r_cookies, pos->pos_nodes * sizeof (E4_uint64));
++    }
++
++    ep4_probe_destroy (rail);
++
++    ep_unloadcode (&rail->r_generic, &rail->r_threadcode);
++
++    if (rail->r_event_ecq)
++      ep4_free_ecq (rail, rail->r_event_ecq);
++    rail->r_event_ecq = NULL;
++
++    ep4_finalise_dma_retries (rail);
++
++    ep_kthread_stop (&rail->r_retry_thread);
++    ep_kthread_destroy (&rail->r_retry_thread);
++
++    if (rail->r_flush_intcookie.int_arg)
++      ep4_deregister_intcookie (rail, &rail->r_flush_intcookie);
++    rail->r_flush_intcookie.int_arg = NULL;
++
++    if (rail->r_flush_mcq)
++      ep4_put_ecq (rail, rail->r_flush_mcq, 4);
++    rail->r_flush_mcq = NULL;
++
++    if (rail->r_flush_ecq)
++      ep4_put_ecq (rail, rail->r_flush_ecq, 1);
++    rail->r_flush_ecq = NULL;
++
++    if (rail->r_ecq_rmap)
++      ep_rmfreemap (rail->r_ecq_rmap);
++    
++    if (rail->r_queuedescs)
++      ep_free_memory_elan (&rail->r_generic, EP_SYSTEM_QUEUE_BASE);
++    rail->r_queuedescs = 0;
++
++    if (rail->r_elan)
++      ep_free_elan (&rail->r_generic, rail->r_elan_addr, sizeof (EP4_RAIL_ELAN));
++    rail->r_elan = 0;
++
++    if (rail->r_main)
++      ep_free_main (&rail->r_generic, rail->r_main_addr, sizeof (EP4_RAIL_MAIN));
++    rail->r_main = NULL;
++
++    kcondvar_destroy (&rail->r_flush_sleep);
++    kmutex_destroy (&rail->r_flush_mutex);
++
++    spin_lock_destroy (&rail->r_ecq_lock);
++    spin_lock_destroy (&rail->r_cookie_lock);
++
++    spin_lock_destroy (&rail->r_haltop_lock);
++    kcondvar_destroy(&rail->r_haltop_sleep);
++    kmutex_destroy (&rail->r_haltop_mutex);
++    spin_lock_destroy (&rail->r_intcookie_lock);
++
++    ep4_detach_rail (rail);
++}
++
++void
++ep4_position_found (EP_RAIL *r, ELAN_POSITION *pos)
++{
++    EP4_RAIL   *rail = (EP4_RAIL *) r;
++    sdramaddr_t cookies;
++    EP_ADDR     addr;
++    int         i;
++
++    KMEM_ZALLOC (rail->r_cookies, E4_uint64 *, pos->pos_nodes * sizeof (E4_uint64), 1);
++
++    if (! (cookies = ep_alloc_elan (&rail->r_generic, pos->pos_nodes * sizeof (E4_uint64), 0, &addr)))
++      panic ("ep4_position_found: cannot allocate elan cookies array\n");
++
++    for (i = 0; i < pos->pos_nodes; i++)
++      elan4_sdram_writeq (rail->r_ctxt.ctxt_dev, cookies + (i * sizeof (E4_uint64)), 0);
++    
++    for (i = 0; i < pos->pos_nodes; i++)
++      rail->r_cookies[i] = 0;
++
++    elan4_sdram_writeq (rail->r_ctxt.ctxt_dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_nodeid), pos->pos_nodeid);
++    elan4_sdram_writeq (rail->r_ctxt.ctxt_dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_cookies), addr);
++
++    ep4_probe_position_found (rail, pos);
++}
++
++sdramaddr_t
++ep4_sdram_alloc (EP_RAIL *r, EP_ADDR addr, unsigned size)
++{
++    ELAN4_DEV *dev = ((EP4_RAIL *) r)->r_ctxt.ctxt_dev;
++
++    if (size >= SDRAM_PAGE_SIZE)
++      return elan4_sdram_alloc (dev, size);
++    else
++    {
++      sdramaddr_t block = elan4_sdram_alloc (dev, SDRAM_PAGE_SIZE);
++      sdramaddr_t sdram = block + (addr & (SDRAM_PAGE_SIZE-1));
++
++      /* free of the portion before sdram */
++      if (sdram > block)
++          elan4_sdram_free (dev, block, sdram - block);
++
++      /* free of the portion after sdram */
++      if ((block + SDRAM_PAGE_SIZE) > (sdram + size))
++          elan4_sdram_free (dev, sdram + size, block + SDRAM_PAGE_SIZE - (sdram + size));
++
++      return sdram;
++    }
++}
++
++void
++ep4_sdram_free (EP_RAIL *r, sdramaddr_t addr, unsigned size)
++{
++    elan4_sdram_free (((EP4_RAIL *) r)->r_ctxt.ctxt_dev, addr, size);
++}
++
++void
++ep4_sdram_writeb (EP_RAIL *r, sdramaddr_t addr, unsigned char val)
++{
++    elan4_sdram_writeb (((EP4_RAIL *) r)->r_ctxt.ctxt_dev, addr, val);
++}
++
++void
++ep4_flush_tlb (EP_RAIL *r)
++{
++    elan4mmu_flush_tlb (((EP4_RAIL *) r)->r_ctxt.ctxt_dev);
++}
++
++void
++ep4_load_system_route (EP_RAIL *r, unsigned vp, unsigned lowNode, unsigned highNode)
++{
++    EP4_RAIL  *rail = (EP4_RAIL *) r;
++    ELAN4_DEV *dev  = rail->r_ctxt.ctxt_dev;
++    E4_VirtualProcessEntry route;
++
++    if (elan4_generate_route (&rail->r_generic.Position, &route, ELAN4_KCOMM_CONTEXT_NUM, 
++                            lowNode, highNode, FIRST_SYSTEM_PACKET | FIRST_HIGH_PRI | FIRST_TIMEOUT(3)) < 0)
++    {
++      panic ("ep4_load_system_route: generate route failed\n");
++      /* NOTREACHED */
++    }
++
++    elan4_write_route (dev, rail->r_routetable, vp, &route);
++}
++
++void
++ep4_load_node_route (EP_RAIL *r, unsigned nodeId)
++{
++    EP4_RAIL  *rail = (EP4_RAIL *) r;
++    ELAN4_DEV *dev  = rail->r_ctxt.ctxt_dev;
++    E4_VirtualProcessEntry route;
++
++    if (elan4_generate_route (&rail->r_generic.Position, &route, EP4_CONTEXT_NUM(rail->r_generic.Position.pos_nodeid),
++                            nodeId, nodeId, FIRST_SYSTEM_PACKET | FIRST_TIMEOUT(3)) < 0)
++    {
++      panic ("ep4_load_node_route: generate route failed\n");
++      /* NOTREACHED */
++    }
++
++    elan4_write_route (dev, rail->r_routetable, EP_VP_DATA(nodeId), &route);
++}
++
++void
++ep4_unload_node_route (EP_RAIL *r, unsigned nodeId)
++{
++    EP4_RAIL  *rail = (EP4_RAIL *) r;
++    ELAN4_DEV *dev  = rail->r_ctxt.ctxt_dev;
++    
++    elan4_invalidate_route (dev, rail->r_routetable, EP_VP_DATA(nodeId));
++}
++
++void
++ep4_lower_filter (EP_RAIL *r, unsigned nodeId)
++{
++    EP4_RAIL *rail = (EP4_RAIL *) r;
++
++    elan4_set_filter (&rail->r_ctxt, EP4_CONTEXT_NUM(nodeId), E4_FILTER_HIGH_PRI);
++}
++
++void
++ep4_raise_filter (EP_RAIL *r, unsigned nodeId)
++{
++    EP4_RAIL *rail = (EP4_RAIL *) r;
++
++    elan4_set_filter (&rail->r_ctxt, EP4_CONTEXT_NUM(nodeId), E4_FILTER_DISCARD_ALL);
++}
++
++void
++ep4_node_disconnected (EP_RAIL *r, unsigned nodeId)
++{
++    ep4_free_stalled_dmas ((EP4_RAIL *) r, nodeId);
++}
++
++void
++ep4_fillout_stats(EP_RAIL *r, char *str) 
++{
++    /* no stats here yet */
++    /* EP4_RAIL *ep4rail = (EP4_RAIL *)r; */
++}
+Index: linux-2.6.5/drivers/net/qsnet/ep/kcomm_elan4.h
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/kcomm_elan4.h        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/kcomm_elan4.h     2005-05-11 12:10:12.525919352 -0400
+@@ -0,0 +1,443 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __EP_KCOMM_ELAN4_H
++#define __EP_KCOMM_ELAN4_H
++
++#ident "@(#)$Id: kcomm_elan4.h,v 1.16.2.2 2004/12/14 10:19:14 mike Exp $ $Name: QSNETMODULES-4-31_20050321 $"
++/*      $Source: /cvs/master/quadrics/epmod/kcomm_elan4.h,v $*/
++
++#include <elan4/types.h>
++
++#include <elan4/dma.h>
++#include <elan4/events.h>
++#include <elan4/commands.h>
++
++#if !defined(__elan4__)
++#include <elan4/device.h>
++#endif /* ! defined(__elan4__) */
++
++/* private address allocation */
++#define EP4_TEXT_BASE                 0xF8000000              /* base address for thread code (defined in makerules.elan4) */
++#define EP4_ECQ_BASE                  0xFF000000              /* address space for mapping command queues */
++#define EP4_ECQ_TOP                   0xFF800000
++
++#define EP4_ECQ_RMAPSIZE              128
++#define EP4_STACK_SIZE                        1024                    /* default thread code stack size */
++#define EP4_MAX_LEVELS                        8                       /* same as ELAN_MAX_LEVELS */
++
++/* context number allocation */
++#define EP4_CONTEXT_NUM(nodeId)               (ELAN4_KCOMM_BASE_CONTEXT_NUM + (nodeId))
++#define EP4_CONTEXT_ISDATA(ctx)               ((ctx) >= ELAN4_KCOMM_BASE_CONTEXT_NUM && \
++                                       (ctx) <= ELAN4_KCOMM_TOP_CONTEXT_NUM)
++#define EP4_CONTEXT_TO_NODE(ctx)      ((ctx) - ELAN4_KCOMM_BASE_CONTEXT_NUM)
++
++/*
++ * network error cookie format:
++ *  -------------------------------------------------
++ *  | unique cookie value | Remote | DMA | Location |
++ *  -------------------------------------------------
++ * [63:4] Cookie   - unique cookie number
++ * [3]    Thread   - cookie generated by thread code
++ * [2]          Remote   - cookie generated by remote end
++ * [1]    STEN     - cookie is for a STEN packet
++ * [0]    DMA      - cookie is for a DMA
++ */
++#define EP4_COOKIE_DMA                (1    << 0)
++#define EP4_COOKIE_STEN               (1    << 1)
++#define EP4_COOKIE_REMOTE     (1    << 2)
++#define EP4_COOKIE_THREAD     (1    << 3)
++#define EP4_COOKIE_INC                (1ull << 4)
++
++#define EP4_COOKIE_STRING(val)        ((val) & ~(EP4_COOKIE_INC-1)) >> 4, \
++                              ((val) & EP4_COOKIE_DMA)    ? ",dma" : "", \
++                              ((val) & EP4_COOKIE_REMOTE) ? ",remote" : "", \
++                              ((val) & EP4_COOKIE_THREAD) ? ",thread" : "", \
++                              ((val) & EP4_COOKIE_STEN)   ? ",sten" : ""
++/*
++ * Done "word" values 
++ */
++#define EP4_STATE_FREE                0
++#define EP4_STATE_ACTIVE      1
++#define EP4_STATE_FINISHED    2
++#define EP4_STATE_FAILED      3
++#define EP4_STATE_PRIVATE     4
++
++#define EP4_EVENT_FIRING_TLIMIT       16384                   /* 1023 uS */
++
++/* forward declarations */
++typedef struct ep4_rail       EP4_RAIL;
++
++#if !defined(__elan4__)
++
++typedef struct ep4_intcookie
++{
++    struct list_head           int_link;
++    E4_uint64                  int_val;
++    void                     (*int_callback)(EP4_RAIL *rail, void *arg);
++    void                      *int_arg;
++} EP4_INTCOOKIE;
++
++#define EP4_INTCOOKIE_HASH_SIZE       256
++#define EP4_INTCOOKIE_HASH(a)         ((((a) >> 3) ^ ((a) >> 7) ^ ((a) >> 11)) & (EP4_INTCOOKIE_HASH_SIZE-1))
++
++typedef struct ep4_ecq
++{
++    struct list_head  ecq_link;                               /* linked on r_ecq_list */
++    ELAN4_INTOP               ecq_intop;                              /* main interrupt op space */
++    ELAN4_CQ         *ecq_cq;                                 /* command queue */
++    E4_Addr           ecq_addr;                               /* address mapped into elan */
++    unsigned int      ecq_avail;                              /* # dwords still available */
++
++    spinlock_t                ecq_lock;                               /* spinlock for main accesses */
++    sdramaddr_t               ecq_event;                              /* event for flushing "event" queues */
++    EP_ADDR           ecq_event_addr;
++    struct ep4_ecq     *ecq_flushcq;                          /*  and command port to issue setevent to */
++} EP4_ECQ;
++
++#define EP4_ECQ_EVENT         0                               /* command queues targetted by multi-blocks events */
++#define EP4_ECQ_ATOMIC                1                               /* command queues targetted by atomic store operations */
++#define EP4_ECQ_SINGLE                2                               /* command queues targetted by single word commands from main */
++#define EP4_ECQ_MAIN          3                               /* command queues targetted by multi word commands from main */
++#define EP4_NUM_ECQ           4
++
++#define EP4_ECQ_Size(which)           ((which) == EP4_ECQ_EVENT  ? CQ_Size64K : \
++                                       (which) == EP4_ECQ_ATOMIC ? CQ_Size8K  : \
++                                       (which) == EP4_ECQ_SINGLE ? CQ_Size1K  : \
++                                       (which) == EP4_ECQ_MAIN   ? CQ_Size8K  : \
++                                       CQ_Size1K)
++
++typedef struct ep4_dma_retry
++{
++    struct list_head    retry_link;                           /* chained on free/retry list */
++    unsigned long     retry_time;                             /* "lbolt" to retry at */
++    E4_DMA            retry_dma;                              /* DMA (in main memory) */
++} EP4_DMA_RETRY;
++
++#define EP4_DMA_RETRY_CQSIZE          CQ_Size8K                               /* size of command queue for dma retry */
++#define EP4_DMA_RETRY_FLOWCNT         (CQ_Size(EP4_DMA_RETRY_CQSIZE)/72)      /* # of reissued DMA's which can fit in */
++
++typedef struct ep4_inputq
++{
++    EP4_INTCOOKIE     q_intcookie;
++    unsigned int      q_slotSize;
++    unsigned int      q_slotCount;
++
++    void             *q_slots;
++    EP_ADDR           q_slotsAddr;
++    
++    EP_INPUTQ_CALLBACK *q_callback;
++    void             *q_arg;
++
++    sdramaddr_t               q_desc;
++    EP_ADDR           q_descAddr;
++    EP_ADDR           q_eventAddr;
++    EP4_ECQ          *q_wcq;                                  /* command queue to issue waitevent to */
++    EP4_ECQ          *q_ecq;                                  /* command queue targetted by event to generate interrupt */
++
++    EP_ADDR           q_fptr;                                 /* cached current front pointer */
++    EP_ADDR           q_last;                                 /* elan addr for last queue slot  */
++
++    atomic_t          q_fired;                                /* atomic flag that interrupt received */
++    unsigned int      q_count;                                /* count of slots consumed */
++} EP4_INPUTQ;
++
++typedef struct ep4_outputq
++{
++    spinlock_t                q_lock;
++    unsigned int      q_slotCount;
++    unsigned int      q_slotSize;
++    unsigned int        q_dwords;
++    ELAN4_CQ         *q_cq;
++    void             *q_main;
++    EP_ADDR           q_mainAddr;
++    unsigned int      q_retries;
++} EP4_OUTPUTQ;
++
++#endif /* ! defined(__elan4__) */
++
++typedef struct ep4_check_sten
++{
++    E4_uint64         c_reset_event_cmd;                      /* WRITEDWORD to reset start event */
++    E4_uint64                 c_reset_event_value;
++
++    E4_uint64         c_open;                                 /* OPEN VP_PROBE(lvl) */
++    E4_uint64         c_trans_traceroute0;                    /* SENDTRANS TR_TRACEROUTE 0s */
++    E4_uint64         c_addr_traceroute0;
++    E4_uint64         c_data_traceroute0[8];
++    E4_uint64         c_trans_traceroute1;                    /* SENDTRANS TR_TRACEROUTE 1s */
++    E4_uint64         c_addr_traceroute1;
++    E4_uint64         c_data_traceroute1[8];
++    E4_uint64         c_trans_sendack;                        /* SENDTRANS SENDACK */
++    E4_uint64         c_addr_sendack;
++    
++    E4_uint64         c_guard_ok;                             /* GUARD OK - write level */
++    E4_uint64         c_writedword_ok;
++    E4_uint64         c_value_ok;
++    
++    E4_uint64         c_guard_fail;                           /* GUARD FAIL - chain setevent/write fail */
++    E4_uint64         c_setevent_fail;
++    E4_uint64         c_setevent_nop;
++    E4_uint64         c_nop_pad;
++} EP4_CHECK_STEN;
++
++#define EP4_CHECK_STEN_NDWORDS        (sizeof (EP4_CHECK_STEN) >> 3)
++
++typedef struct ep4_rail_elan
++{
++    EP4_CHECK_STEN    r_check_sten[EP4_MAX_LEVELS];
++    E4_Event32                r_check_fail;                                   /* Check failed (== r_check_start[-1]) */
++    E4_Event32          r_check_start[EP4_MAX_LEVELS];
++
++    E4_Event32                r_qevents[EP_NUM_SYSTEMQ];
++    E4_Event32                r_flush_event;
++
++    E4_uint64         r_nodeid;
++#ifdef __elan4__
++    E4_uint64        *r_cookies;
++#else
++    E4_Addr           r_cookies;
++#endif
++} EP4_RAIL_ELAN;
++
++#define TRACEROUTE_ENTRIES    16                      /* 2 * ELAN_MAX_LEVELS */
++#define TRACEROUTE_NDWORDS    (TRACEROUTE_ENTRIES/2)
++
++typedef struct ep4_rail_main
++{
++    E4_uint32         r_probe_dest0[TRACEROUTE_ENTRIES];
++    E4_uint32         r_probe_dest1[TRACEROUTE_ENTRIES];
++    E4_uint64         r_probe_result;
++    E4_uint64         r_probe_level;
++
++    E4_uint64           r_dma_flowcnt;                                /*  count of dma's queued */
++} EP4_RAIL_MAIN;
++
++#define EP4_PROBE_ACTIVE      (0xffff)
++#define EP4_PROBE_FAILED      (0xfffe)
++
++#if !defined(__elan4__)
++
++typedef struct ep4_retry_ops
++{
++    struct list_head  op_link;
++    unsigned long     (*op_func)(EP4_RAIL *rail, void *arg, unsigned long nextRunTime);
++    void             *op_arg;
++} EP4_RETRY_OPS;
++
++typedef struct ep4_neterr_ops
++{
++    struct list_head  op_link;
++    void            (*op_func) (EP4_RAIL *rail, void *arg, unsigned int nodeId, EP_NETERR_COOKIE *cookies);
++    void             *op_arg;
++} EP4_NETERR_OPS;
++
++struct ep4_rail
++{
++    EP_RAIL           r_generic;
++    ELAN4_CTXT                r_ctxt;
++    ELAN4_ROUTE_TABLE  *r_routetable;
++    
++    spinlock_t                r_intcookie_lock;
++    struct list_head    r_intcookie_hash[EP4_INTCOOKIE_HASH_SIZE];
++
++    sdramaddr_t               r_elan;
++    EP_ADDR           r_elan_addr;
++    EP4_RAIL_MAIN      *r_main;
++    EP_ADDR           r_main_addr;
++    
++    EP_CODE           r_threadcode;                           /* copy of thread code */
++
++    sdramaddr_t               r_queuedescs;                           /* systemq queue descriptors */
++
++    E4_uint64        *r_cookies;                              /* network error cookies */
++    spinlock_t          r_cookie_lock;                                /*    and spin lock */
++
++    kcondvar_t                r_probe_wait;                           /* network position probing */
++    spinlock_t                r_probe_lock;
++    volatile int      r_probe_done;
++    EP4_INTCOOKIE     r_probe_intcookie;
++    EP4_ECQ           *r_probe_cq;
++    E4_uint32         r_probe_source0[TRACEROUTE_ENTRIES];
++    E4_uint32         r_probe_source1[TRACEROUTE_ENTRIES];
++
++    kmutex_t          r_haltop_mutex;                         /* halt/flush operations */
++    ELAN4_HALTOP      r_haltop;
++    ELAN4_DMA_FLUSHOP   r_flushop;
++    kcondvar_t                r_haltop_sleep;
++    spinlock_t                r_haltop_lock;
++
++    struct list_head    r_ecq_list[EP4_NUM_ECQ];              /* list of statically allocated command queues */
++    EP_RMAP          *r_ecq_rmap;                             /* resource map for command queue mappings */
++    spinlock_t          r_ecq_lock;                           /* spinlock for list/space management */
++
++    kmutex_t          r_flush_mutex;                          /* serialize command queue flushing */
++    unsigned long     r_flush_count;                          /* # setevents issued for flushing */
++    EP4_ECQ          *r_flush_mcq;                            /*   and command queue for waitevent */
++    EP4_ECQ            *r_flush_ecq;                          /*   and command queue for interrupt */
++    EP4_INTCOOKIE       r_flush_intcookie;                    /*   and interrupt cookie */
++    kcondvar_t          r_flush_sleep;                                /*   and place to sleep ... */
++
++    EP_KTHREAD                r_retry_thread;                         /* retry thread */
++    struct list_head    r_retry_ops;                          /*  list of retry operations */
++
++    EP4_RETRY_OPS       r_dma_ops;                            /* dma retry operations */
++    EP4_ECQ          *r_dma_ecq;                              /*   command queue to reissue DMAs */
++    E4_uint64           r_dma_flowcnt;                                /*   count of dma's reissued */
++    struct list_head    r_dma_retrylist[EP_NUM_RETRIES];      /*   retry lists  */
++    struct list_head    r_dma_freelist;                               /*   and free list */
++    spinlock_t          r_dma_lock;                           /*   and spinlock to protect lists */
++    unsigned long       r_dma_allocated;                      /*   # retries allocated*/
++    unsigned long       r_dma_reserved;                               /*   # retries reserved */
++
++    EP4_ECQ          *r_event_ecq;                            /* command queue for occasional setevents */
++
++    struct list_head    r_neterr_ops;                         /* list of neterr fixup operations */
++
++    ELAN4_IPROC_TRAP    r_iproc_trap;
++    ELAN4_TPROC_TRAP    r_tproc_trap;
++} ;
++
++#define EP4_CTXT_TO_RAIL(ctxt)        ((EP4_RAIL *) (((unsigned long) (ctxt)) - offsetof (EP4_RAIL, r_ctxt)))
++
++#if defined(DEBUG_ASSERT)
++#define EP4_ASSERT(rail,EXPR)                 EP_ASSERT(&((rail)->r_generic), EXPR)
++#define EP4_SDRAM_ASSERT(rail,off,value)      EP4_ASSERT(rail, (sdram_assert ? elan4_sdram_readq ((rail)->r_ctxt.ctxt_dev, (off)) == (value) : 1))
++#else
++#define EP4_ASSERT(rail,EXPR)
++#define EP4_SDRAM_ASSERT(rail,off,value)
++#endif
++
++/* kcomm_elan4.c */
++extern EP_RAIL    *ep4_create_rail (EP_SYS *sys, ELAN4_DEV *dev);
++extern void        ep4_destroy_rail (EP_RAIL *rail);
++
++extern int         ep4_start_rail (EP_RAIL *rail);
++extern void        ep4_stall_rail (EP_RAIL *rail);
++extern void        ep4_stop_rail (EP_RAIL *rail);
++
++extern void      ep4_debug_rail (EP_RAIL *rail);
++
++extern void        ep4_position_found (EP_RAIL *rail, ELAN_POSITION *pos);
++
++extern sdramaddr_t ep4_sdram_alloc (EP_RAIL *rail, EP_ADDR addr, unsigned int size);
++extern void        ep4_sdram_free (EP_RAIL *rail, sdramaddr_t addr, unsigned int size);
++extern void        ep4_sdram_writeb (EP_RAIL *rail, sdramaddr_t addr, unsigned char val);
++
++extern void        ep4_flush_tlb (EP_RAIL *r);
++extern void        ep4_load_system_route (EP_RAIL *r, unsigned int vp, unsigned int lowNode, unsigned int highNode);
++extern void        ep4_load_node_route (EP_RAIL *r, unsigned int nodeId);
++extern void        ep4_unload_node_route (EP_RAIL *r, unsigned int nodeId);
++extern void        ep4_lower_filter (EP_RAIL *r, unsigned int nodeId);
++extern void        ep4_raise_filter (EP_RAIL *rail, unsigned int nodeId);
++extern void        ep4_node_disconnected (EP_RAIL *r, unsigned int nodeId);
++
++/* kmap_elan4.c */
++extern void        ep4_kaddr_map (EP_RAIL *r, EP_ADDR eaddr, virtaddr_t kaddr, unsigned int len, unsigned int perm, int ep_attr);
++extern void        ep4_sdram_map (EP_RAIL *r, EP_ADDR eaddr, sdramaddr_t saddr, unsigned int len, unsigned int perm, int ep_attr);
++extern void        ep4_ioaddr_map (EP_RAIL *r, EP_ADDR eaddr, ioaddr_t ioaddr, unsigned int len, unsigned int perm);
++extern void        ep4_unmap (EP_RAIL *r, EP_ADDR eaddr, unsigned int len);
++extern void       *ep4_dvma_reserve (EP_RAIL *r, EP_ADDR eaddr, unsigned int npages);
++extern void        ep4_dvma_release (EP_RAIL *r, EP_ADDR eaddr, unsigned int npages, void *private);
++extern void        ep4_dvma_set_pte (EP_RAIL *r, void *private, unsigned int index, physaddr_t paddr, unsigned int perm);
++extern physaddr_t  ep4_dvma_read_pte (EP_RAIL *r, void *private, unsigned int index);
++extern void        ep4_dvma_unload (EP_RAIL *r, void *private, unsigned int index, unsigned int npages);
++
++/* kmsg_elan4.c */
++extern EP_INPUTQ  *ep4_alloc_inputq (EP_RAIL *r, unsigned int qnum, unsigned int slotSize, unsigned int slotCount,
++                                   EP_INPUTQ_CALLBACK *callback, void *arg);
++extern void        ep4_free_inputq (EP_RAIL *r, EP_INPUTQ *q);
++extern void        ep4_enable_inputq (EP_RAIL *r, EP_INPUTQ *q);
++extern void        ep4_disable_inputq (EP_RAIL *r, EP_INPUTQ *q);
++extern int         ep4_poll_inputq (EP_RAIL *r, EP_INPUTQ *q, int maxCount, EP_INPUTQ_HANDLER *handler, void *arg);
++extern EP_OUTPUTQ *ep4_alloc_outputq (EP_RAIL *r, unsigned int slotSize, unsigned int slotCount);
++extern void        ep4_free_outputq (EP_RAIL *r, EP_OUTPUTQ *q);
++extern void       *ep4_outputq_msg (EP_RAIL *r, EP_OUTPUTQ *q, unsigned int slotNum);
++extern int         ep4_outputq_state (EP_RAIL *r, EP_OUTPUTQ *q, unsigned int slotNum);
++extern int         ep4_outputq_send (EP_RAIL *r, EP_OUTPUTQ *q, unsigned int slotNum, unsigned int size,
++                                   unsigned int nodeId, unsigned int qnum, unsigned int retries);
++
++/* probenetwork_elan4.c */
++extern int         ep4_probe_init (EP4_RAIL *r);
++extern void        ep4_probe_destroy (EP4_RAIL *r);
++extern void        ep4_probe_position_found (EP4_RAIL *rail, ELAN_POSITION *pos);
++extern int         ep4_probe_route (EP_RAIL *r, int level, int sw, int nodeid, int *linkup, int *linkdown, int attempts, EP_SWITCH *lsw);
++extern int         ep4_check_position (EP_RAIL *rail);
++
++/* support_elan4.c */
++extern ELAN4_TRAP_OPS ep4_trap_ops;
++extern void           ep4_register_intcookie (EP4_RAIL *rail, EP4_INTCOOKIE *cp, E4_uint64 cookie, void (*callback)(EP4_RAIL *r, void *arg), void *arg);
++extern void           ep4_deregister_intcookie (EP4_RAIL *rail, EP4_INTCOOKIE *cp);
++extern EP4_INTCOOKIE *ep4_lookup_intcookie (EP4_RAIL *rail, E4_uint64 cookie);
++extern E4_uint64      ep4_neterr_cookie (EP4_RAIL *rail, unsigned int node);
++
++extern void           ep4_flush_filters (EP_RAIL *r);
++extern void           ep4_flush_queues (EP_RAIL *r);
++extern void         ep4_write_qdesc (EP4_RAIL *rail, sdramaddr_t qaddr, E4_InputQueue *qdesc);
++
++extern EP4_ECQ       *ep4_alloc_ecq (EP4_RAIL *rail, unsigned int cqsize);
++extern void           ep4_free_ecq (EP4_RAIL *rail, EP4_ECQ *ecq);
++extern EP4_ECQ             *ep4_get_ecq (EP4_RAIL *rail, unsigned int which, unsigned int ndwords);
++extern void           ep4_put_ecq (EP4_RAIL *rail, EP4_ECQ *ecq, unsigned int ndwords);
++
++extern void           ep4_nop_cmd (EP4_ECQ *ecq, E4_uint64 tag);
++extern void           ep4_set_event_cmd (EP4_ECQ *ecq, E4_Addr event);
++extern void           ep4_wait_event_cmd (EP4_ECQ *ecq, E4_Addr event, E4_uint64 candt, E4_uint64 param0, E4_uint64 param1);
++
++extern void           ep4_flush_interrupt (EP4_RAIL *rail, void *arg);
++extern void           ep4_flush_ecqs (EP4_RAIL *rail);
++
++extern void           ep4_init_thread (EP4_RAIL *rail, E4_ThreadRegs *regs, sdramaddr_t stackTop, 
++                                     EP_ADDR stackAddr, E4_Addr startpc, int nargs,...);
++
++extern void           ep4_initialise_dma_retries (EP4_RAIL *rail);
++extern void           ep4_finalise_dma_retries (EP4_RAIL *rail);
++extern int            ep4_reserve_dma_retries (EP4_RAIL *rail, unsigned int count, unsigned int attr);
++extern void         ep4_release_dma_retries(EP4_RAIL *rail, unsigned int count);
++extern void           ep4_queue_dma_retry (EP4_RAIL *rail, E4_DMA *dma, int interval);
++extern void           ep4_queue_dma_stalled (EP4_RAIL *rail, E4_DMA *dma);
++extern void           ep4_free_stalled_dmas (EP4_RAIL *rail, unsigned int nodeId);
++extern void           ep4_display_rail (EP4_RAIL *rail);
++
++extern void           ep4_add_retry_ops (EP4_RAIL *rail, EP4_RETRY_OPS *ops);
++extern void           ep4_remove_retry_ops (EP4_RAIL *rail, EP4_RETRY_OPS *ops);
++extern void           ep4_retry_thread (EP4_RAIL *rail);
++
++/* neterr_elan4.c */
++extern void           ep4_add_neterr_ops (EP4_RAIL *rail, EP4_NETERR_OPS *ops);
++extern void           ep4_remove_neterr_ops (EP4_RAIL *rail, EP4_NETERR_OPS *ops);
++extern void           ep4_neterr_fixup (EP_RAIL *r, unsigned int nodeId, EP_NETERR_COOKIE *cookies);
++
++/* commands_elan4.c */
++extern void           elan4_nop_cmd (ELAN4_CQ *cq, E4_uint64 tag);
++extern void           elan4_write_dword_cmd (ELAN4_CQ *cq, E4_Addr addr, E4_uint64 data);
++extern void           elan4_add_dword_cmd (ELAN4_CQ *cq, E4_Addr addr, E4_uint64 data);
++extern void           elan4_copy64_cmd (ELAN4_CQ *cq, E4_Addr from, E4_Addr to, E4_uint32 datatype);
++extern void           elan4_interrupt_cmd (ELAN4_CQ *cq, E4_uint64 cookie);
++extern void           elan4_run_thread_cmd (ELAN4_CQ *cq, E4_ThreadRegs *regs);
++extern void           elan4_run_dma_cmd (ELAN4_CQ *cq, E4_DMA *dma);
++extern void           elan4_set_event_cmd (ELAN4_CQ *cq, E4_Addr event);
++extern void           elan4_set_eventn_cmd (ELAN4_CQ *cq, E4_Addr event, E4_uint32 count);
++extern void           elan4_wait_event_cmd (ELAN4_CQ *cq, E4_Addr event, E4_uint64 candt, E4_uint64 param0, E4_uint64 param1);
++extern void           elan4_open_packet (ELAN4_CQ *cq, E4_uint64 command);
++extern void           elan4_guard (ELAN4_CQ *cq, E4_uint64 command);
++extern void           elan4_sendtrans0 (ELAN4_CQ *cq, E4_uint16 trtype, E4_uint64 addr);
++extern void           elan4_sendtrans1 (ELAN4_CQ *cq, E4_uint16 trtype, E4_uint64 addr, E4_uint64 p0);
++extern void           elan4_sendtrans2 (ELAN4_CQ *cq, E4_uint16 trtype, E4_uint64 addr, E4_uint64 p0, E4_uint64 p1);
++extern void           elan4_sendtransn (ELAN4_CQ *cq, E4_uint16 trtype, E4_uint64 addr, ...);
++extern void           elan4_sendtransp (ELAN4_CQ *cq, E4_uint16 trtype, E4_uint64 addr, E4_uint64 *ptr);
++
++extern void           ep4_add_retry_ops (EP4_RAIL *rail, EP4_RETRY_OPS *ops);
++extern void         ep4_remove_retry_ops (EP4_RAIL *rail, EP4_RETRY_OPS *ops);
++extern void           ep4_retry_thread (EP4_RAIL *rail);
++
++extern void           ep4_fillout_stats(EP_RAIL *rail, char *str);
++
++#endif /* ! defined(__elan4__) */
++
++#endif /* __EP_KCOMM_ELAN4_H */
+Index: linux-2.6.5/drivers/net/qsnet/ep/kcomm_vp.h
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/kcomm_vp.h   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/kcomm_vp.h        2005-05-11 12:10:12.525919352 -0400
+@@ -0,0 +1,36 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __EP_KCOMM_VP_H
++#define __EP_KCOMM_VP_H
++
++#ident "@(#)$Id: kcomm_vp.h,v 1.2 2004/03/24 11:32:56 david Exp $ $Name: QSNETMODULES-4-31_20050321 $"
++/*      $Source: /cvs/master/quadrics/epmod/kcomm_vp.h,v $*/
++
++#define EP_MAX_NODES                  2048                    /* Max nodes we support */
++
++/* virtual process allocation */
++#define EP_VP_NODE_BASE                       (0)
++#define EP_VP_DATA_BASE                       (EP_VP_NODE_BASE + EP_MAX_NODES)
++#define EP_VP_PROBE_BASE              (EP_VP_DATA_BASE + EP_MAX_NODES)
++#define EP_VP_PROBE_COUNT             ELAN_MAX_LEVELS
++
++#define EP_VP_BCAST_BASE              (EP_VP_PROBE_BASE + EP_VP_PROBE_COUNT)
++#define EP_VP_BCAST_COUNT             (CM_SGMTS_PER_LEVEL * (CM_MAX_LEVELS - 1) + 1)
++
++#define EP_VP_NODE(nodeId)            (EP_VP_NODE_BASE + (nodeId))
++#define EP_VP_DATA(nodeId)            (EP_VP_DATA_BASE + (nodeId))
++#define EP_VP_PROBE(lvl)              (EP_VP_PROBE_BASE + (lvl))
++#define EP_VP_BCAST(lvl,sgmt)         (EP_VP_BCAST_BASE + ((lvl) - 1)*CM_SGMTS_PER_LEVEL + (sgmt))
++
++#define EP_VP_TO_NODE(vp)             ((vp) & (EP_MAX_NODES-1))
++#define EP_VP_ISDATA(vp)              ((vp) >= EP_VP_DATA_BASE && (vp) < (EP_VP_DATA_BASE + EP_MAX_NODES))
++
++#endif /* __EP_KCOMM_VP_H */
++
++
+Index: linux-2.6.5/drivers/net/qsnet/ep/kmap.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/kmap.c       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/kmap.c    2005-05-11 12:10:12.526919200 -0400
+@@ -0,0 +1,561 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: kmap.c,v 1.10.6.2 2004/12/14 10:19:14 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/kmap.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <qsnet/kpte.h>
++
++#include <elan/kcomm.h>
++
++#include "debug.h"
++
++#if defined(DIGITAL_UNIX)
++#  define kernel_map                  (first_task->map)
++#  define vaddr_to_phys(map, addr)    (pmap_extract (vm_map_pmap ((vm_map_t) map), (unsigned long) addr))
++#elif defined(LINUX)
++#  define kernel_map                  get_kern_mm()
++#  define vaddr_to_phys(map, addr)    (kmem_to_phys(addr))
++#elif defined(SOLARIS)
++#  define kernel_map                  &kas
++#  define vaddr_to_phys(map,addr)     ptob(hat_getpfnum (((struct as *) map)->a_hat, (caddr_t) addr))
++#endif
++
++void
++ep_perrail_kaddr_map (EP_RAIL *rail, EP_ADDR eaddr, virtaddr_t kaddr, unsigned long len,  unsigned int perm, int ep_attr)
++{
++    rail->Operations.KaddrMap (rail, eaddr, kaddr, len, perm, ep_attr);
++}
++
++void
++ep_perrail_sdram_map (EP_RAIL *rail, EP_ADDR eaddr, sdramaddr_t saddr, unsigned long len, unsigned int perm, int ep_attr)
++{
++    rail->Operations.SdramMap (rail, eaddr, saddr, len, perm, ep_attr);
++}
++
++void
++ep_perrail_unmap (EP_RAIL *rail, EP_ADDR eaddr, unsigned long len)
++{
++    rail->Operations.Unmap (rail, eaddr, len);
++}
++
++void
++ep_perrail_dvma_sync (EP_RAIL *rail)
++{
++    if (rail->TlbFlushRequired)
++    {
++      rail->TlbFlushRequired = 0;
++
++      rail->Operations.FlushTlb (rail);
++    }
++}
++
++
++static int ep_dvma_map_rails (EP_SYS *sys, EP_NMH *nmh, EP_NMD *nmd, EP_RAILMASK mask);
++
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++static uint16_t ep_dvma_calc_check_sum (EP_SYS *sys, EP_NMH *nmh, EP_NMD *nmd, uint16_t check_sum);
++#endif
++
++EP_NMH_OPS ep_dvma_nmh_ops = 
++{
++    ep_dvma_map_rails,
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++    ep_dvma_calc_check_sum
++#endif
++};
++
++extern void
++ep_dvma_init (EP_SYS *sys)
++{
++    EP_DVMA_STATE *d = &sys->DvmaState;
++
++    kmutex_init (&d->dvma_lock);
++    
++    INIT_LIST_HEAD (&d->dvma_handles);
++    INIT_LIST_HEAD (&d->dvma_rails);
++
++    d->dvma_rmap = ep_rmallocmap (EP_DVMA_RMAP_SIZE, "dvma_rmap", 1);
++
++    ep_rmfree (d->dvma_rmap, EP_DVMA_TOP - EP_DVMA_BASE, EP_DVMA_BASE);
++}
++
++extern void
++ep_dvma_fini (EP_SYS *sys)
++{
++    EP_DVMA_STATE *d = &sys->DvmaState;
++
++    ep_rmfreemap (d->dvma_rmap);
++
++    kmutex_destroy (&d->dvma_lock);
++}
++    
++extern int
++ep_dvma_add_rail (EP_SYS *sys, EP_RAIL *rail)
++{
++    EP_DVMA_STATE    *d = &sys->DvmaState;
++    EP_RAIL_ENTRY    *l;
++    struct list_head *el;
++
++    KMEM_ZALLOC (l, EP_RAIL_ENTRY *, sizeof (EP_RAIL_ENTRY), 1);
++
++    if (l == NULL)
++      return (ENOMEM);
++
++    kmutex_lock (&d->dvma_lock);
++
++    l->Rail = rail;
++
++    list_add_tail (&l->Link, &d->dvma_rails);
++
++    list_for_each (el, &d->dvma_handles) {
++      EP_DVMA_NMH *desc   = list_entry (el, EP_DVMA_NMH, dvma_link);
++      int          npages = desc->dvma_nmh.nmh_nmd.nmd_len >> PAGESHIFT;
++
++      desc->dvma_rails[rail->Number] = rail;
++      desc->dvma_railmask |= ( 1 << rail->Number);
++
++      desc->dvma_private[rail->Number] = rail->Operations.DvmaReserve (rail, desc->dvma_nmh.nmh_nmd.nmd_addr, npages);
++    }
++
++    kmutex_unlock (&d->dvma_lock);
++    return (0);
++}
++
++extern void
++ep_dvma_remove_rail (EP_SYS *sys, EP_RAIL *rail)
++{
++    EP_DVMA_STATE    *d = &sys->DvmaState;
++    struct list_head *el;
++
++    kmutex_lock (&d->dvma_lock);
++    
++    list_for_each (el, &d->dvma_handles) {
++      EP_DVMA_NMH *desc   = list_entry (el, EP_DVMA_NMH, dvma_link);
++      int          npages = desc->dvma_nmh.nmh_nmd.nmd_len >> PAGESHIFT;
++
++      desc->dvma_rails[rail->Number] = NULL;
++      desc->dvma_railmask &= ~(1 << rail->Number);
++
++      rail->Operations.DvmaRelease (rail, desc->dvma_nmh.nmh_nmd.nmd_addr, npages, desc->dvma_private[rail->Number]);
++    }
++
++    list_for_each (el, &d->dvma_rails) {
++      EP_RAIL_ENTRY *tmp = list_entry (el, EP_RAIL_ENTRY, Link);
++
++      if (tmp->Rail == rail)
++      {
++          list_del (el);
++
++          KMEM_FREE (tmp, sizeof (EP_RAIL_ENTRY));
++          break;
++      }
++    }
++    kmutex_unlock (&d->dvma_lock);
++}
++
++EP_NMH *
++ep_dvma_reserve (EP_SYS *sys, unsigned npages, unsigned perm)
++{
++    EP_DVMA_STATE    *d = &sys->DvmaState;
++    EP_DVMA_NMH      *desc;
++    EP_ADDR         addr;
++    struct list_head *el;
++    int               i;
++
++    KMEM_ZALLOC (desc, EP_DVMA_NMH *, offsetof (EP_DVMA_NMH, dvma_attrs[npages]), 1);
++    
++    if (desc == NULL)
++      return (NULL);
++
++    if ((addr = ep_rmalloc (d->dvma_rmap, npages << PAGESHIFT, 0)) == 0)
++    {
++
++      KMEM_FREE (desc, sizeof (EP_DVMA_NMH));
++      return (NULL);
++    }
++
++    spin_lock_init (&desc->dvma_lock);
++
++    desc->dvma_perm = perm;
++
++    kmutex_lock (&d->dvma_lock);
++    /* reserve the mapping resource */
++    list_for_each (el, &d->dvma_rails) {
++      EP_RAIL *rail = list_entry (el, EP_RAIL_ENTRY, Link)->Rail;
++
++      EPRINTF4 (DBG_KMAP, "%s: ep_dvma_reserve desc=%p npages=%d rail=%p\n", rail->Name, desc, npages, rail);
++
++      if ((desc->dvma_private[rail->Number] = rail->Operations.DvmaReserve (rail, addr, npages)) == NULL)
++      {
++          printk ("%s: !!ep_dvma_reserve - rail->DvmaReserve failed\n", rail->Name);
++          goto failed;
++      }
++
++      desc->dvma_rails[rail->Number] = rail;
++      desc->dvma_railmask |= (1 << rail->Number);
++    }
++
++    /* insert into the network mapping handle table */
++    desc->dvma_nmh.nmh_nmd.nmd_addr = addr;
++    desc->dvma_nmh.nmh_nmd.nmd_len  = npages << PAGESHIFT;
++    desc->dvma_nmh.nmh_nmd.nmd_attr = EP_NMD_ATTR (sys->Position.pos_nodeid, 0);
++    desc->dvma_nmh.nmh_ops          = &ep_dvma_nmh_ops;
++
++    ep_nmh_insert (&sys->MappingTable, &desc->dvma_nmh);
++
++    list_add (&desc->dvma_link, &d->dvma_handles);
++
++    kmutex_unlock (&d->dvma_lock);
++
++    return (&desc->dvma_nmh);
++
++ failed:
++
++    kmutex_unlock (&d->dvma_lock);
++
++    for (i = 0; i < EP_MAX_RAILS; i++)
++      if (desc->dvma_rails[i] != NULL)
++          desc->dvma_rails[i]->Operations.DvmaRelease (desc->dvma_rails[i], addr, npages, desc->dvma_private[i]);
++
++    ep_rmfree (d->dvma_rmap, npages << PAGESHIFT, addr);
++
++    KMEM_FREE (desc, sizeof (EP_DVMA_NMH));
++    return (NULL);
++}
++
++void
++ep_dvma_release (EP_SYS *sys, EP_NMH *nmh)
++{
++    EP_DVMA_STATE *d      = &sys->DvmaState;
++    EP_DVMA_NMH   *desc   = (EP_DVMA_NMH *) nmh;
++    EP_ADDR        addr   = nmh->nmh_nmd.nmd_addr;
++    int            npages = nmh->nmh_nmd.nmd_len >> PAGESHIFT;
++    EP_RAIL       *rail;
++    int            i;
++
++    kmutex_lock (&d->dvma_lock);
++
++    list_del (&desc->dvma_link);
++    
++    ep_nmh_remove (&sys->MappingTable, nmh);
++
++    for (i = 0; i < EP_MAX_RAILS; i++)
++      if ((rail = desc->dvma_rails[i]) != NULL)
++          rail->Operations.DvmaRelease (rail, addr, npages, desc->dvma_private[i]);
++
++    ep_rmfree (d->dvma_rmap, npages << PAGESHIFT, addr);
++
++    KMEM_FREE (desc, offsetof (EP_DVMA_NMH, dvma_attrs[npages]));
++
++    kmutex_unlock (&d->dvma_lock);
++}
++
++void
++ep_dvma_load (EP_SYS *sys, void *map, caddr_t vaddr, unsigned len, EP_NMH *nmh, unsigned index, EP_RAILMASK *hints, EP_NMD *subset)
++{
++    EP_DVMA_NMH *desc = (EP_DVMA_NMH *) nmh;
++    unsigned     offset = (unsigned long) vaddr & PAGEOFFSET;
++    unsigned     npages = btopr (len + offset);
++    EP_ADDR      addr   = nmh->nmh_nmd.nmd_addr + (index << PAGESHIFT);
++    int                rmask  = *hints;
++    EP_RAIL     *rail;
++    register int i, rnum;
++    unsigned long flags;
++
++    EPRINTF7 (DBG_KMAP, "ep_dvma_load: map=%p vaddr=%p len=%x nmh=%p(%x,%x) index=%d\n",
++            map, vaddr, len, nmh, nmh->nmh_nmd.nmd_addr, nmh->nmh_nmd.nmd_len, index);
++
++    /* If no rail specified, then map into all rails */
++    if (rmask == 0)
++      rmask = desc->dvma_railmask;
++
++    ASSERT ((index + npages) <= (nmh->nmh_nmd.nmd_len >> PAGESHIFT));
++
++    /* If not map specified then use the kernel map */
++    if (map == NULL)
++      map = kernel_map;
++
++    spin_lock_irqsave (&desc->dvma_lock, flags);
++    /* Now map each of the specified pages (backwards) */
++
++    vaddr = (vaddr - offset) + (npages-1)*PAGESIZE;
++    for (i = npages-1; i >= 0; i--, vaddr -= PAGESIZE)
++    {
++      physaddr_t paddr = vaddr_to_phys (map, vaddr);
++      
++      for (rnum = 0; rnum < EP_MAX_RAILS; rnum++)
++      {
++          if (! (rmask & (1 << rnum)) || (rail = desc->dvma_rails[rnum]) == NULL)
++              rmask &= ~(1 << rnum);
++          else
++          {
++              rail->Operations.DvmaSetPte (rail, desc->dvma_private[rnum], index + i, paddr, desc->dvma_perm);
++
++              desc->dvma_attrs[index + i] |= (1 << rnum);
++          }
++      }
++    }
++
++    for (rnum = 0; rnum < EP_MAX_RAILS; rnum++)
++      if ((rmask & (1 << rnum)) && (rail = desc->dvma_rails[rnum]) != NULL)
++          rail->TlbFlushRequired = 1;
++
++    spin_unlock_irqrestore (&desc->dvma_lock, flags);
++
++    /* Construct the network mapping handle to be returned. */
++    subset->nmd_addr = addr + offset;
++    subset->nmd_len  = len;
++    subset->nmd_attr = EP_NMD_ATTR(sys->Position.pos_nodeid, rmask);
++}
++
++void
++ep_dvma_unload (EP_SYS *sys, EP_NMH *nmh, EP_NMD *nmd)
++{
++    EP_DVMA_NMH *desc = (EP_DVMA_NMH *) nmh;
++    unsigned     offset = nmd->nmd_addr & PAGEOFFSET;
++    unsigned     npages = btopr (nmd->nmd_len + offset);
++    unsigned     index  = (nmd->nmd_addr - nmh->nmh_nmd.nmd_addr) >> PAGESHIFT;
++    EP_RAIL     *rail;
++    int          rnum;
++    int          rmask;
++    register int i;
++    unsigned long flags;
++    
++    spin_lock_irqsave (&desc->dvma_lock, flags);
++
++    /* compute which rails we need to unload on */
++    for (rmask = 0, i = 0; i < npages; i++)
++    {
++      rmask |= desc->dvma_attrs[index + i];
++      
++      desc->dvma_attrs[index + i] = 0;
++    }
++    
++    for (rnum = 0; rnum < EP_MAX_RAILS; rnum++)
++      if ((rmask & (1 << rnum)) && (rail = desc->dvma_rails[rnum]) != NULL)
++          rail->Operations.DvmaUnload (rail, desc->dvma_private[rnum], index, npages);
++
++    spin_unlock_irqrestore (&desc->dvma_lock, flags);
++}
++
++int
++ep_dvma_map_rails (EP_SYS *sys, EP_NMH *nmh, EP_NMD *nmd, EP_RAILMASK mask)
++{
++    EP_DVMA_NMH *desc = (EP_DVMA_NMH *) nmh;
++    unsigned     offset = nmd->nmd_addr & PAGEOFFSET;
++    unsigned     npages = btopr (nmd->nmd_len + offset);
++    unsigned     index  = (nmd->nmd_addr - nmh->nmh_nmd.nmd_addr) >> PAGESHIFT;
++    int          r, rnum;
++    register int i;
++    unsigned long flags;
++
++    spin_lock_irqsave (&desc->dvma_lock, flags);
++
++    EPRINTF4 (DBG_KMAP, "ep_dvma_map_rails: nmd=%08x.%08x.%08x mask=%04x\n", nmd->nmd_addr, nmd->nmd_len, nmd->nmd_attr, mask);
++
++    if ((mask &= desc->dvma_railmask) == 0)
++    {
++      printk ("ep_dvma_map_rails: no intersecting rails %04x.%04x\n", mask, desc->dvma_railmask);
++      spin_unlock_irqrestore (&desc->dvma_lock, flags);
++      return (-1);
++    }
++
++    for (i = npages-1; i >= 0; i--)
++    {
++      int pgidx = (index + i);
++
++      for (rnum = 0; rnum < EP_MAX_RAILS; rnum++)
++          if (desc->dvma_attrs[pgidx] & (1 << rnum))
++              break;
++      
++      if (rnum == EP_MAX_RAILS)
++      {
++          EPRINTF3 (DBG_KMAP, "ep_dvma_map_rails: nmh=%p idx=%x [%08x] not ptes valid\n", nmh, pgidx, 
++                    nmh->nmh_nmd.nmd_addr + ((pgidx) << PAGESHIFT));
++          mask = 0;
++      }
++      else
++      {
++          EP_RAIL   *rail  = desc->dvma_rails[rnum];
++          physaddr_t paddr = rail->Operations.DvmaReadPte (rail, desc->dvma_private[rnum], pgidx);
++          
++          EPRINTF5 (DBG_KMAP, "%s: ep_dvma_map_rails: nmh=%p idx=%x [%08x] paddr %llx\n", rail->Name, nmh, pgidx,
++                    nmh->nmh_nmd.nmd_addr + (pgidx << PAGESHIFT), (long long) paddr);
++          
++          for (r = 0; r < EP_MAX_RAILS; r++)
++          {
++              if ((mask & (1 << r)) == 0)
++                  continue;
++              
++              if ((desc->dvma_attrs[pgidx] & (1 << r)) == 0)
++              {
++                  EPRINTF5 (DBG_KMAP, "%s: ep_dvma_map_rails: nmh=%p idx=%x [%08x] paddr=%llx\n",
++                            desc->dvma_rails[rnum]->Name, nmh, pgidx, nmh->nmh_nmd.nmd_addr + (pgidx << PAGESHIFT), 
++                            (long long) paddr);
++                  
++                  rail->Operations.DvmaSetPte (rail, desc->dvma_private[rnum], pgidx, paddr, desc->dvma_perm);
++                  
++                  desc->dvma_attrs[pgidx] |= (1 << r);
++              }
++          }
++      }
++    }
++
++    for (rnum = 0; rnum < EP_MAX_RAILS; rnum++)
++      if ((mask & (1 << rnum)) != 0)
++          desc->dvma_rails[rnum]->TlbFlushRequired = 1;
++
++    EPRINTF4 (DBG_KMAP, "ep_dvma_map_rails: nmd=%08x.%08x.%08x|%04x\n", nmd->nmd_addr, nmd->nmd_len, nmd->nmd_attr, mask);
++
++    /* Finally update the network memory descriptor */
++    nmd->nmd_attr |= mask;
++
++    spin_unlock_irqrestore (&desc->dvma_lock, flags);
++
++    return (0);
++}
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++#include <linux/highmem.h>
++
++/* Generic rolling checksum algorithm */
++uint16_t
++rolling_check_sum (char *msg, int nob, uint16_t sum)
++{
++    while (nob-- > 0)
++      sum = sum * 13 + *msg++;
++
++    return (sum);
++}
++
++#if ! defined(NO_RMAP)
++void  
++unmap_phys_address(unsigned long phys_addr)
++{
++    unsigned long pfn = (phys_addr >> PAGE_SHIFT);
++    
++    if (pfn_valid(pfn)) 
++      kunmap(pfn_to_page(pfn));
++}
++
++void * 
++map_phys_address(unsigned long phys_addr)
++{
++    unsigned long pfn = (phys_addr >> PAGE_SHIFT);
++    
++    if (pfn_valid(pfn)) 
++      return  kmap(pfn_to_page(pfn));
++    
++    return NULL;
++}
++#else
++void  
++unmap_phys_address(unsigned long phys_addr)
++{
++    struct page *p = virt_to_page(__va(phys_addr));
++    
++    if (VALID_PAGE(p)) 
++      kunmap(p);
++}
++
++void * 
++map_phys_address(unsigned long phys_addr)
++{
++    struct page *p = virt_to_page(__va(phys_addr));
++                              
++    if (VALID_PAGE(p)) 
++      return  kmap(p);
++    
++    return NULL;
++}
++#endif
++
++uint16_t
++ep_dvma_calc_check_sum (EP_SYS *sys, EP_NMH *nmh, EP_NMD *nmd, uint16_t check_sum)
++{
++    /* cant be called from an interupt */
++
++    EP_DVMA_NMH *desc = (EP_DVMA_NMH *) nmh;
++    unsigned     offset = nmd->nmd_addr & PAGEOFFSET;
++    unsigned     npages = btopr (nmd->nmd_len + offset);
++    unsigned     index  = (nmd->nmd_addr - nmh->nmh_nmd.nmd_addr) >> PAGESHIFT;
++    unsigned     start, len;
++    int          rnum;
++    register int i;
++    unsigned long flags;
++    EP_RAIL      *rail;
++
++
++    spin_lock_irqsave (&desc->dvma_lock, flags);
++
++    EPRINTF3 (DBG_KMAP, "ep_dvma_calc_check_sum: nmd=%08x.%08x.%08x \n", nmd->nmd_addr, nmd->nmd_len, nmd->nmd_attr);
++ 
++    /* find a rail */
++    for (rnum = 0; rnum < EP_MAX_RAILS; rnum++)
++      if (desc->dvma_attrs[index] & (1 << rnum))
++          break;
++      
++    ASSERT (rnum != EP_MAX_RAILS);
++ 
++    rail = desc->dvma_rails[rnum];
++
++    for (i = 0; i <= (npages-1); i++)
++    {
++      int        pgidx = (index + i);
++      physaddr_t paddr = rail->Operations.DvmaReadPte (rail, desc->dvma_private[rnum], pgidx);
++      void *     virt;
++
++      spin_unlock_irqrestore (&desc->dvma_lock, flags); /* unlock for check sum calc */
++
++      virt = map_phys_address(paddr);
++
++      if (!virt)
++          printk("ep_dvma_calc_check_sum: virt = NULL ! \n");
++      else {
++          if ( i == 0 ) {
++              /* last bit of the first page */
++              start =  (nmd->nmd_addr & (PAGESIZE - 1)) ;
++              len   =  PAGESIZE - start;
++              if ( len > nmd->nmd_len) /* less than the remaining page */ 
++                  len =  nmd->nmd_len;
++          } else {
++              if ( i != (npages-1)) {
++                  /* all of the middle pages    */
++                  start = 0;
++                  len   = PAGESIZE;
++              } else {
++                  /* first bit of the last page */
++                  start = 0;
++                  len   = ((nmd->nmd_addr + nmd->nmd_len -1) & (PAGESIZE -1)) +1;
++              }
++          }
++
++          check_sum = rolling_check_sum (((char *)virt)+start, len, check_sum);
++          unmap_phys_address(paddr);
++   
++          /* re aquire the lock */
++          spin_lock_irqsave (&desc->dvma_lock, flags);
++      }
++      
++      EPRINTF5 (DBG_KMAP, "%s: ep_dvma_calc_check_sum: nmh=%p idx=%x [%08x] paddr %llx\n", rail->Name, nmh, pgidx,
++                nmh->nmh_nmd.nmd_addr + (pgidx << PAGESHIFT), (long long) paddr);     
++    }
++
++    EPRINTF4 (DBG_KMAP, "ep_dvma_calc_check_sum: nmd=%08x.%08x.%08x = %d\n", nmd->nmd_addr, nmd->nmd_len, nmd->nmd_attr, check_sum);
++
++    spin_unlock_irqrestore (&desc->dvma_lock, flags);
++
++    return (check_sum);
++}
++#endif
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/ep/kmap_elan3.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/kmap_elan3.c 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/kmap_elan3.c      2005-05-11 12:10:12.527919048 -0400
+@@ -0,0 +1,209 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: kmap_elan3.c,v 1.3.8.1 2004/12/14 10:19:14 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/kmap_elan3.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan3/elanregs.h>
++#include <elan3/elandev.h>
++#include <elan3/elanvp.h>
++#include <elan3/elan3mmu.h>
++#include <elan3/elanctxt.h>
++#include <elan3/elandebug.h>
++
++#include <elan/kcomm.h>
++
++#include "kcomm_elan3.h"
++
++#if defined(DIGITAL_UNIX)
++#  define kernel_map                  (first_task->map)
++#  define vaddr_to_phys(map, addr)    (pmap_extract (vm_map_pmap ((vm_map_t) map), (unsigned long) addr))
++#elif defined(LINUX)
++#  define kernel_map                  get_kern_mm()
++#  define vaddr_to_phys(map, addr)    (kmem_to_phys(addr))
++#elif defined(SOLARIS)
++#  define kernel_map                  &kas
++#  define vaddr_to_phys(map,addr)     ptob(hat_getpfnum (((struct as *) map)->a_hat, (caddr_t) addr))
++#endif
++
++#define ELAN3_PTES_PER_PAGE           (PAGESIZE/ELAN3_PAGE_SIZE)
++
++#if defined(__LITTLE_ENDIAN__)
++#define PERM_ENDIAN   0
++#else
++#define PERM_ENDIAN   ELAN3_PTE_BIG_ENDIAN
++#endif
++
++static unsigned int main_permtable[] = 
++{
++    ELAN3_PERM_REMOTEALL,             /* EP_PERM_EXECUTE */
++    ELAN3_PERM_REMOTEREAD,            /* EP_PERM_READ */
++    ELAN3_PERM_REMOTEWRITE,           /* EP_PERM_WRITE */
++    ELAN3_PERM_REMOTEWRITE,           /* EP_PERM_ALL */
++};
++
++static unsigned int sdram_permtable[] = 
++{
++    ELAN3_PERM_REMOTEREAD,            /* EP_PERM_EXECUTE */
++    ELAN3_PERM_REMOTEREAD,            /* EP_PERM_READ */
++    ELAN3_PERM_REMOTEWRITE,           /* EP_PERM_WRITE */
++    ELAN3_PERM_REMOTEALL,             /* EP_PERM_ALL */
++};
++
++static unsigned int io_permtable[] = 
++{
++    ELAN3_PERM_LOCAL_READ,            /* EP_PERM_EXECUTE */
++    ELAN3_PERM_REMOTEREAD,            /* EP_PERM_READ */
++    ELAN3_PERM_REMOTEWRITE,           /* EP_PERM_WRITE */
++    ELAN3_PERM_REMOTEWRITE,           /* EP_PERM_ALL */
++};
++
++void
++ep3_kaddr_map (EP_RAIL *r, EP_ADDR eaddr, virtaddr_t kaddr, unsigned len, unsigned int perm, int ep_attr)
++{
++    EP3_RAIL    *rail   = (EP3_RAIL *) r;
++    unsigned     npages = len >> PAGESHIFT;
++    int          i;
++    unsigned int off;
++
++    ASSERT ((eaddr & PAGEOFFSET) == 0 && (kaddr & PAGEOFFSET) == 0 && (len & PAGEOFFSET) == 0);
++
++    for (i = 0; i < npages; i++)
++    {
++      physaddr_t paddr = vaddr_to_phys (kernel_map, (void *) kaddr);
++
++      for (off = 0; off < PAGESIZE; off += ELAN3_PAGE_SIZE)
++          elan3mmu_pteload (rail->Elan3mmu, PTBL_LEVEL_3, eaddr + off, paddr + off, 
++                            main_permtable[perm], PTE_LOAD_LOCK | PTE_LOAD_NOSYNC | ((ep_attr & EP_NO_SLEEP) ? PTE_NO_SLEEP : 0));
++
++      eaddr += PAGESIZE;
++      kaddr += PAGESIZE;
++    }
++}
++
++void
++ep3_sdram_map (EP_RAIL *r, EP_ADDR eaddr, sdramaddr_t saddr, unsigned len, unsigned int perm, int ep_attr)
++{
++    EP3_RAIL    *rail   = (EP3_RAIL *) r;
++    unsigned     npages = len >> PAGESHIFT;
++    int          i;
++    unsigned int off;
++
++    ASSERT ((eaddr & PAGEOFFSET) == 0 && (saddr & PAGEOFFSET) == 0 && (len & PAGEOFFSET) == 0);
++
++    for (i = 0; i < npages; i++)
++    {
++      physaddr_t paddr = elan3_sdram_to_phys (rail->Device, saddr);
++
++      for (off = 0; off < PAGESIZE; off += ELAN3_PAGE_SIZE)
++          elan3mmu_pteload (rail->Elan3mmu, PTBL_LEVEL_3, eaddr+off, paddr+off, 
++                            sdram_permtable[perm], PTE_LOAD_LOCK | PTE_LOAD_NOSYNC | ((ep_attr & EP_NO_SLEEP) ? PTE_NO_SLEEP : 0) );
++
++      eaddr += PAGESIZE;
++      saddr += PAGESIZE;
++    }
++}
++
++void
++ep3_ioaddr_map (EP_RAIL *r, EP_ADDR eaddr, ioaddr_t ioaddr, unsigned len, unsigned int perm)
++{
++    EP3_RAIL    *rail   = (EP3_RAIL *) r;
++    unsigned     npages = len >> PAGESHIFT;
++    int          i;
++    unsigned int off;
++
++    ASSERT ((eaddr & PAGEOFFSET) == 0 && (ioaddr & PAGEOFFSET) == 0 && (len & PAGEOFFSET) == 0);
++
++    for (i = 0; i < npages; i++)
++    {
++      physaddr_t paddr = vaddr_to_phys (kernel_map, (void *) ioaddr);
++
++      for (off = 0; off < PAGESIZE; off += ELAN3_PAGE_SIZE)
++          elan3mmu_pteload (rail->Elan3mmu, PTBL_LEVEL_3, eaddr + off, paddr + off, 
++                            io_permtable[perm], PTE_LOAD_LOCK | PTE_LOAD_NOSYNC);
++
++      eaddr += PAGESIZE;
++      ioaddr += PAGESIZE;
++    }
++}
++void
++ep3_unmap (EP_RAIL *r, EP_ADDR eaddr, unsigned len)
++{
++    EP3_RAIL *rail = (EP3_RAIL *) r;
++
++    ASSERT ((eaddr & PAGEOFFSET) == 0 && (len & PAGEOFFSET) == 0);
++
++    elan3mmu_unload (rail->Elan3mmu, eaddr, len, PTE_UNLOAD_UNLOCK | PTE_UNLOAD_NOSYNC);
++}
++
++void *
++ep3_dvma_reserve (EP_RAIL *r, EP_ADDR eaddr, unsigned npages)
++{
++    EP3_RAIL *rail = (EP3_RAIL *) r;
++    void     *private;
++
++    KMEM_ALLOC (private, void *, npages * ELAN3_PTES_PER_PAGE * sizeof (sdramaddr_t), 1);
++    
++    if (private == NULL)
++      return NULL;
++    
++    elan3mmu_reserve (rail->Elan3mmu, eaddr, npages * ELAN3_PTES_PER_PAGE, (sdramaddr_t *) private);
++
++    return private;
++}
++
++void
++ep3_dvma_release (EP_RAIL *r, EP_ADDR eaddr, unsigned npages, void *private)
++{
++    EP3_RAIL *rail = (EP3_RAIL *) r;
++
++    elan3mmu_release (rail->Elan3mmu, eaddr, npages * ELAN3_PTES_PER_PAGE, (sdramaddr_t *) private);
++
++    KMEM_FREE (private, npages * ELAN3_PTES_PER_PAGE * sizeof (sdramaddr_t));
++}
++
++void
++ep3_dvma_set_pte (EP_RAIL *r, void *private, unsigned index, physaddr_t paddr, unsigned int perm)
++{
++    ELAN3_DEV    *dev  = ((EP3_RAIL *) r)->Device;
++    sdramaddr_t *ptep = &((sdramaddr_t *) private)[index * ELAN3_PTES_PER_PAGE];
++    int          off;
++
++    for (off =0 ; off < PAGESIZE; off += ELAN3_PAGE_SIZE)
++    { 
++      ELAN3_PTE newpte = elan3mmu_phys_to_pte (dev, paddr + off, main_permtable[perm]) | ELAN3_PTE_REF | ELAN3_PTE_MOD;
++
++      elan3_writepte (dev, *ptep, newpte);
++
++      ptep++;
++    }
++}
++
++physaddr_t
++ep3_dvma_read_pte (EP_RAIL *r, void *private, unsigned index)
++{
++    EP3_RAIL    *rail = (EP3_RAIL *) r;
++    sdramaddr_t *ptep = &((sdramaddr_t *) private)[index * ELAN3_PTES_PER_PAGE];
++    ELAN3_PTE     pte  = elan3_readpte (rail->Device, *ptep);
++
++    return pte & ELAN3_PTE_PFN_MASK;
++}
++
++void
++ep3_dvma_unload (EP_RAIL *r, void *private, unsigned index, unsigned npages)
++{
++    EP3_RAIL    *rail = (EP3_RAIL *) r;
++    sdramaddr_t *ptep = &((sdramaddr_t *) private)[index * ELAN3_PTES_PER_PAGE];
++    ELAN3_PTE     tpte = elan3mmu_kernel_invalid_pte (rail->Elan3mmu);
++    int i;
++
++    for (i = (npages * ELAN3_PTES_PER_PAGE) - 1; i >= 0; i--)
++      elan3_writepte (rail->Device, ptep[i], tpte);
++}
+Index: linux-2.6.5/drivers/net/qsnet/ep/kmap_elan4.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/kmap_elan4.c 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/kmap_elan4.c      2005-05-11 12:10:12.528918896 -0400
+@@ -0,0 +1,226 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: kmap_elan4.c,v 1.7.8.3 2005/03/18 13:54:01 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/kmap_elan4.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++
++#include "debug.h"
++#include "kcomm_elan4.h"
++
++#if defined(DIGITAL_UNIX)
++#  define kernel_map                  (first_task->map)
++#  define vaddr_to_phys(map, addr)    (pmap_extract (vm_map_pmap ((vm_map_t) map), (unsigned long) addr))
++#elif defined(LINUX)
++#  define kernel_map                  get_kern_mm()
++#  define vaddr_to_phys(map, addr)    (kmem_to_phys(addr))
++#elif defined(SOLARIS)
++#  define kernel_map                  &kas
++#  define vaddr_to_phys(map,addr)     ptob(hat_getpfnum (((struct as *) map)->a_hat, (caddr_t) addr))
++#endif
++
++static unsigned int main_permtable[] = 
++{
++    PERM_Unused,                      /* EP_PERM_EXECUTE */
++    PERM_RemoteReadOnly,              /* EP_PERM_READ */
++    PERM_DataReadWrite,                       /* EP_PERM_WRITE */
++    PERM_DataReadWrite,                       /* EP_PERM_ALL */
++};
++
++static unsigned int sdram_permtable[] = 
++{
++    PERM_LocExecute,                  /* EP_PERM_EXECUTE */
++    PERM_RemoteReadOnly,              /* EP_PERM_READ */
++    PERM_DataReadWrite,                       /* EP_PERM_WRITE */
++    PERM_RemoteAll,                   /* EP_PERM_ALL */
++};
++
++static unsigned int io_permtable[] = 
++{
++    PERM_Unused,                      /* EP_PERM_EXECUTE */
++    PERM_RemoteReadOnly,              /* EP_PERM_READ */
++    PERM_DataReadWrite,                       /* EP_PERM_WRITE */
++    PERM_Unused,                      /* EP_PERM_ALL */
++};
++
++void
++ep4_kaddr_map (EP_RAIL *r, EP_ADDR eaddr, virtaddr_t kaddr, unsigned int len, unsigned int perm, int ep_attr)
++{
++    EP4_RAIL    *rail   = (EP4_RAIL *) r;
++    ELAN4_DEV   *dev    = rail->r_ctxt.ctxt_dev;
++    unsigned int npages = len >> PAGESHIFT;
++    int          i;
++    unsigned int off;
++
++    ASSERT ((eaddr & PAGEOFFSET) == 0 && (kaddr & PAGEOFFSET) == 0 && (len & PAGEOFFSET) == 0);
++
++    for (i = 0; i < npages; i++)
++    {
++      physaddr_t paddr = vaddr_to_phys (kernel_map, (void *) kaddr);
++
++      for (off = 0; off < PAGESIZE; off += (1 << dev->dev_pageshift[0]))
++      {
++          E4_uint64 newpte = elan4mmu_phys2pte (dev, paddr + off, main_permtable[perm]);
++
++          elan4mmu_pteload (&rail->r_ctxt, 0, eaddr + off, newpte);
++      }
++
++      eaddr += PAGESIZE;
++      kaddr += PAGESIZE;
++    }
++}
++
++void
++ep4_sdram_map (EP_RAIL *r, EP_ADDR eaddr, sdramaddr_t saddr, unsigned int len, unsigned int perm, int ep_attr)
++{
++    EP4_RAIL    *rail   = (EP4_RAIL *) r;
++    ELAN4_DEV   *dev    = rail->r_ctxt.ctxt_dev;
++    unsigned int npages = len >> PAGESHIFT;
++    int          i;
++    unsigned int off;
++
++    ASSERT ((eaddr & PAGEOFFSET) == 0 && (saddr & PAGEOFFSET) == 0 && (len & PAGEOFFSET) == 0);
++
++    if ((eaddr & (SDRAM_PGOFF_OFFSET << PAGE_SHIFT)) != (saddr & (SDRAM_PGOFF_OFFSET << PAGE_SHIFT)))
++      printk ("ep4_sdram_map: eaddr=%x saddr=%lx - incorrectly alised\n", eaddr, saddr);
++
++    for (i = 0; i < npages; i++)
++    {
++      for (off = 0; off < PAGESIZE; off += (1 << dev->dev_pageshift[0]))
++      {
++          E4_uint64 newpte = ((saddr + off) >> PTE_PADDR_SHIFT) | PTE_SetPerm (sdram_permtable[perm]);
++
++          elan4mmu_pteload (&rail->r_ctxt, 0, eaddr + off, newpte);
++      }
++
++      eaddr += PAGESIZE;
++      saddr += PAGESIZE;
++    }
++}
++
++void
++ep4_ioaddr_map (EP_RAIL *r, EP_ADDR eaddr, ioaddr_t ioaddr, unsigned int len, unsigned int perm)
++{
++    EP4_RAIL    *rail   = (EP4_RAIL *) r;
++    ELAN4_DEV   *dev    = rail->r_ctxt.ctxt_dev;
++    unsigned int npages = len >> PAGESHIFT;
++    int          i;
++    unsigned int off;
++
++    ASSERT ((eaddr & PAGEOFFSET) == 0 && (ioaddr & PAGEOFFSET) == 0 && (len & PAGEOFFSET) == 0);
++
++    for (i = 0; i < npages; i++)
++    {
++      physaddr_t paddr = vaddr_to_phys (kernel_map, (void *) ioaddr);
++
++      for (off = 0; off < PAGESIZE; off += (1 << dev->dev_pageshift[0]))
++      {
++          E4_uint64 newpte = elan4mmu_phys2pte (dev, paddr + off, io_permtable[perm]);
++
++          elan4mmu_pteload (&rail->r_ctxt, 0, eaddr + off, newpte);
++      }
++
++      eaddr += PAGESIZE;
++      ioaddr += PAGESIZE;
++    }
++}
++void
++ep4_unmap (EP_RAIL *r, EP_ADDR eaddr, unsigned int len)
++{
++    EP4_RAIL *rail = (EP4_RAIL *) r;
++
++    ASSERT ((eaddr & PAGEOFFSET) == 0 && (len & PAGEOFFSET) == 0);
++
++    elan4mmu_unload_range (&rail->r_ctxt, 0, eaddr, len);
++}
++
++void *
++ep4_dvma_reserve (EP_RAIL *r, EP_ADDR eaddr, unsigned int npages)
++{
++    EP4_RAIL  *rail = (EP4_RAIL *) r;
++    ELAN4_DEV *dev  = rail->r_ctxt.ctxt_dev;
++
++    EPRINTF3 (DBG_KMAP, "ep4_dvma_reserve: eaddr=%x npages=%d (=> %d)\n", eaddr, npages, (npages << (PAGE_SHIFT - dev->dev_pageshift[0])));
++
++    return elan4mmu_reserve (&rail->r_ctxt, 0, (E4_Addr) eaddr, (npages << (PAGE_SHIFT - dev->dev_pageshift[0])), 1);
++}
++
++void
++ep4_dvma_release (EP_RAIL *r, EP_ADDR eaddr, unsigned int npages, void *private)
++{
++    EP4_RAIL *rail = (EP4_RAIL *) r;
++
++    EPRINTF3 (DBG_KMAP, "ep4_dvma_release: eaddr=%x npages=%d private=%p\n", eaddr, npages, private);
++
++    elan4mmu_release (&rail->r_ctxt, (ELAN4_HASH_CACHE *) private);
++}
++
++void
++ep4_dvma_set_pte (EP_RAIL *r, void *private, unsigned int index, physaddr_t paddr, unsigned int perm)
++{
++    EP4_RAIL     *rail  = (EP4_RAIL *) r;
++    ELAN4_DEV    *dev   = rail->r_ctxt.ctxt_dev;
++    unsigned int  off;
++    unsigned long flags;
++
++    EPRINTF3 (DBG_KMAP, "ep4_dvma_set_pte: index %x -> eaddr %llx paddr %llx\n", 
++            index, ((ELAN4_HASH_CACHE *) private)->hc_start + (index * PAGE_SIZE), (long long) paddr);
++
++    local_irq_save (flags);
++    for (off = 0; off < PAGESIZE; off += (1 << dev->dev_pageshift[0]))
++    {
++      E4_uint64 newpte = elan4mmu_phys2pte (dev, paddr + off, main_permtable[perm]);
++
++      elan4mmu_set_pte (&rail->r_ctxt, (ELAN4_HASH_CACHE *) private, (index << (PAGE_SHIFT - dev->dev_pageshift[0])) +
++                        (off >> dev->dev_pageshift[0]), newpte);
++    }
++    local_irq_restore (flags);
++}
++
++physaddr_t
++ep4_dvma_read_pte (EP_RAIL *r, void *private, unsigned int index)
++{
++    EP4_RAIL     *rail  = (EP4_RAIL *) r;
++    ELAN4_DEV    *dev   = rail->r_ctxt.ctxt_dev;
++    E4_uint64     pte;
++    unsigned long flags;
++
++    local_irq_save (flags);
++    pte = elan4mmu_get_pte (&rail->r_ctxt, (ELAN4_HASH_CACHE *) private, index << (PAGE_SHIFT - dev->dev_pageshift[0]));
++    local_irq_restore (flags);
++
++    return elan4mmu_pte2phys (dev, pte);
++}
++
++void
++ep4_dvma_unload (EP_RAIL *r, void *private, unsigned int index, unsigned int npages)
++{
++    EP4_RAIL  *rail  = (EP4_RAIL *) r;
++    ELAN4_DEV *dev   = rail->r_ctxt.ctxt_dev;
++    EP_ADDR    eaddr = ((ELAN4_HASH_CACHE *) private)->hc_start + (index * PAGE_SIZE);
++    unsigned long idx = (index << (PAGE_SHIFT - dev->dev_pageshift[0]));
++    unsigned long lim = idx + (npages << (PAGE_SHIFT - dev->dev_pageshift[0]));
++    unsigned long flags;
++
++    EPRINTF5 (DBG_KMAP, "ep4_dvma_unload: eaddr %x -> %lx : index=%d idx=%ld lim=%ld\n", 
++            eaddr, (unsigned long)(eaddr + (npages * PAGE_SIZE)), index, idx, lim);
++
++    local_irq_save (flags);
++    for (; idx < lim; idx++)
++      elan4mmu_clear_pte (&rail->r_ctxt, (ELAN4_HASH_CACHE *) private, idx);
++    local_irq_restore (flags);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/ep/kmsg_elan3.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/kmsg_elan3.c 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/kmsg_elan3.c      2005-05-11 12:10:12.529918744 -0400
+@@ -0,0 +1,345 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: kmsg_elan3.c,v 1.3.8.1 2004/09/30 09:52:37 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/kmsg_elan3.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++
++#include "kcomm_vp.h"
++#include "kcomm_elan3.h"
++#include "debug.h"
++
++static void
++ep3_inputq_event (EP3_RAIL *rail, void *arg)
++{
++    EP3_INPUTQ *inputq = (EP3_INPUTQ *) arg;
++    
++    (*inputq->q_callback)((EP_RAIL *)rail, inputq->q_arg);
++}
++
++static EP3_COOKIE_OPS ep3_inputq_cookie_ops = 
++{
++    ep3_inputq_event,
++};
++
++EP_INPUTQ *
++ep3_alloc_inputq (EP_RAIL *r, unsigned qnum, unsigned slotSize, unsigned slotCount,
++                EP_INPUTQ_CALLBACK *callback, void *arg)
++{
++    EP3_RAIL      *rail = (EP3_RAIL *) r;
++    EP3_INPUTQ    *inputq;
++    EP3_InputQueue qdesc;
++    void          *slots;
++    int            i;
++
++    ASSERT ((slotSize & (EP_SYSTEMQ_MSG_ALIGN-1)) == 0);
++
++    KMEM_ALLOC (inputq, EP3_INPUTQ *, sizeof (EP3_INPUTQ), TRUE);
++
++    if (inputq == NULL)
++      return (EP_INPUTQ *) NULL;
++    
++    if ((slots = ep_alloc_main (&rail->Generic, slotSize * slotCount, 0, &inputq->q_slotsAddr)) == NULL)
++    {
++      KMEM_FREE (inputq, sizeof (EP3_INPUTQ));
++      return (EP_INPUTQ *) NULL;
++    }
++
++    inputq->q_slotSize  = slotSize;
++    inputq->q_slotCount = slotCount;
++    inputq->q_callback  = callback;
++    inputq->q_arg       = arg;
++    inputq->q_slots     = slots;
++
++    /* Initialise all the slots to be "unreceived" */
++    for (i = 0; i < slotCount; i++)
++      ((uint32_t *) ((unsigned long) slots + (i+1) * slotSize))[-1] = EP_SYSTEMQ_UNRECEIVED;
++    
++    inputq->q_base     = inputq->q_slotsAddr;
++    inputq->q_top      = inputq->q_base + (slotCount-1) * slotSize;
++    inputq->q_fptr     = inputq->q_base;
++    inputq->q_desc     = EP_SYSTEMQ_DESC(rail->QueueDescs, qnum);
++    inputq->q_descAddr = EP_SYSTEMQ_ADDR (qnum);
++
++    if (callback)
++      RegisterCookie (&rail->CookieTable, &inputq->q_cookie, inputq->q_descAddr, &ep3_inputq_cookie_ops, inputq);
++
++    /* Initialise the input queue descriptor */
++    qdesc.q_state          = E3_QUEUE_FULL;
++    qdesc.q_bptr           = inputq->q_base + slotSize;
++    qdesc.q_fptr           = inputq->q_fptr;
++    qdesc.q_base           = inputq->q_base;
++    qdesc.q_top            = inputq->q_top;
++    qdesc.q_size           = slotSize;
++    qdesc.q_event.ev_Count = 1;
++    qdesc.q_event.ev_Type  = callback ? EV_TYPE_EVIRQ | inputq->q_cookie.Cookie : 0;
++    qdesc.q_wevent         = inputq->q_descAddr + offsetof (EP3_InputQueue, q_event);
++    qdesc.q_wcount         = 0;
++
++    /* copy the queue descriptor down to sdram */
++    elan3_sdram_copyl_to_sdram (rail->Device, &qdesc, inputq->q_desc, sizeof (EP3_InputQueue));
++
++    return (EP_INPUTQ *) inputq;
++}
++
++void
++ep3_free_inputq (EP_RAIL *r, EP_INPUTQ *q)
++{
++    EP3_RAIL   *rail   = (EP3_RAIL *) r;
++    EP3_INPUTQ *inputq = (EP3_INPUTQ *) q;
++
++    ep_free_main (&rail->Generic, inputq->q_slotsAddr, inputq->q_slotSize * inputq->q_slotCount);
++
++    if (inputq->q_callback)
++      DeregisterCookie (&rail->CookieTable, &inputq->q_cookie);
++
++    KMEM_FREE (inputq, sizeof (EP3_INPUTQ));
++}
++
++void
++ep3_enable_inputq (EP_RAIL *r, EP_INPUTQ *q)
++{
++    EP3_RAIL   *rail   = (EP3_RAIL *) r;
++    EP3_INPUTQ *inputq = (EP3_INPUTQ *) q;
++
++    elan3_sdram_writel (rail->Device, inputq->q_desc + offsetof (EP3_InputQueue, q_state), 0);
++}
++
++void
++ep3_disable_inputq (EP_RAIL *r, EP_INPUTQ *q)
++{
++    EP3_RAIL      *rail   = (EP3_RAIL *) r;
++    EP3_INPUTQ    *inputq = (EP3_INPUTQ *) q;
++    EP3_InputQueue qdesc;
++
++    /* mark the queue as locked */
++    SetQueueLocked (rail, inputq->q_desc);
++
++    /* re-initialise the queue as empty */
++    qdesc.q_state          = E3_QUEUE_FULL;
++    qdesc.q_bptr           = (E3_Addr) inputq->q_base + inputq->q_slotSize;
++    qdesc.q_fptr           = inputq->q_fptr;
++    qdesc.q_base           = inputq->q_base;
++    qdesc.q_top            = inputq->q_top;
++    qdesc.q_size           = inputq->q_slotSize;
++    qdesc.q_event.ev_Count = 1;
++    qdesc.q_event.ev_Type  = inputq->q_callback ? EV_TYPE_EVIRQ | inputq->q_cookie.Cookie : 0;
++    qdesc.q_wevent         = inputq->q_descAddr + offsetof (EP3_InputQueue, q_event);
++    qdesc.q_wcount         = 0;
++
++    /* copy the queue descriptor down to sdram */
++    elan3_sdram_copyl_to_sdram (rail->Device, &qdesc, inputq->q_desc, sizeof (EP3_InputQueue));
++}
++
++int
++ep3_poll_inputq (EP_RAIL *r, EP_INPUTQ *q, int maxCount, EP_INPUTQ_HANDLER *handler, void *arg)
++{
++    EP3_RAIL          *rail   = (EP3_RAIL *) r;
++    EP3_INPUTQ        *inputq = (EP3_INPUTQ *) q;
++    sdramaddr_t        qdesc  = inputq->q_desc;
++    E3_Addr            nfptr;
++    int                count = 0;
++    E3_uint32          state;
++    int                      delay;
++
++ run_again_because_of_eventqueue_overflow:
++    nfptr = inputq->q_fptr + inputq->q_slotSize;
++    if (nfptr > inputq->q_top)                                        
++      nfptr = inputq->q_base;
++
++    while (nfptr != elan3_sdram_readl (rail->Device, qdesc + offsetof (EP3_InputQueue, q_bptr)))                      /* PCI read */
++    {
++      unsigned long slot = (unsigned long) inputq->q_slots + (nfptr - inputq->q_base);
++
++      /* Poll the final word of the message until the message has completely
++       * arrived in main memory. */
++      for (delay = 1; ((uint32_t *) (slot + inputq->q_slotSize))[-1] == EP_SYSTEMQ_UNRECEIVED && delay < EP_SYSTEMQ_UNRECEIVED_TLIMIT; delay <<= 1)
++          DELAY (delay);
++
++      /* Call the message handler */
++      (*handler) (r, arg, (void *) slot);
++      
++      state = elan3_sdram_readl (rail->Device, qdesc + offsetof (EP3_InputQueue, q_state));                           /* PCI read */
++      if ((state & E3_QUEUE_FULL) == 0)
++          elan3_sdram_writel (rail->Device, qdesc + offsetof (EP3_InputQueue, q_fptr), nfptr);                        /* PCI write */
++      else
++      {
++          elan3_sdram_writel (rail->Device, qdesc + offsetof (EP3_InputQueue, q_fptr), nfptr);                        /* PCI write */
++          elan3_sdram_writel (rail->Device, qdesc + offsetof (EP3_InputQueue, q_state), (state & ~E3_QUEUE_FULL));    /* PCI write */
++      }
++      inputq->q_fptr = nfptr;
++      
++      nfptr += roundup (inputq->q_slotSize, E3_BLK_ALIGN);
++      if (nfptr > inputq->q_top)
++          nfptr = inputq->q_base;
++
++      if (++count >= maxCount && maxCount)
++          break;
++    }
++    
++    if (inputq->q_callback && count != 0)
++    {
++      if (count != inputq->q_waitCount)
++          elan3_sdram_writel (rail->Device, qdesc + offsetof (EP3_InputQueue, q_wcount), inputq->q_waitCount = count);
++
++      if (IssueWaitevent (rail, inputq->q_descAddr + offsetof (EP3_InputQueue, q_wevent)) == ISSUE_COMMAND_TRAPPED)
++          goto run_again_because_of_eventqueue_overflow;
++    }
++
++    return count;
++}
++
++#define Q_EVENT(q,slotNum)            ((q)->q_elan      + (slotNum) * sizeof (E3_BlockCopyEvent))
++#define Q_EVENT_ADDR(q,slotNum)               ((q)->q_elanAddr  + (slotNum) * sizeof (E3_BlockCopyEvent))
++#define Q_MSG(q,slotNum)      (void *)((q)->q_main      + (slotNum) * (q)->q_slotSize)
++#define Q_MSG_ADDR(q,slotNum)         ((q)->q_mainAddr  + (slotNum) * (q)->q_slotSize)
++#define Q_DONE(q,slotNum)     (*((int *)((q)->q_main      + (q)->q_slotCount * (q)->q_slotSize + (slotNum) * sizeof (E3_uint32))))
++#define Q_DONE_ADDR(q,slotNum)                ((q)->q_mainAddr  + (q)->q_slotCount * (q)->q_slotSize + (slotNum) * sizeof (E3_uint32))
++
++#define Q_ELAN_SIZE(q)                        ((q)->q_slotCount * sizeof (E3_BlockCopyEvent))
++#define Q_MAIN_SIZE(q)                        ((q)->q_slotCount * ((q)->q_slotSize + sizeof (E3_uint32)))
++
++static void
++ep3_outputq_retry (EP3_RAIL *rail, void *arg, E3_DMA_BE *dma, int error)
++{
++    E3_DMA_BE    *dmabe = (E3_DMA_BE *) dma;
++    sdramaddr_t   event = ep_elan2sdram (&rail->Generic, dmabe->s.dma_srcEvent);
++    E3_Addr       done  = elan3_sdram_readl (rail->Device, event + offsetof (E3_BlockCopyEvent, ev_Dest));
++    E3_uint32    *donep = ep_elan2main (&rail->Generic, done & ~EV_BCOPY_DTYPE_MASK);
++
++    EPRINTF1 (DBG_KMSG, "ep3_ouputq_retry: donep at %p -> FAILED\n", donep);
++    
++    *donep = EP3_EVENT_FAILED;
++}
++
++static EP3_COOKIE_OPS ep3_outputq_cookie_ops =
++{
++    NULL, /* Event */
++    ep3_outputq_retry,
++    NULL, /* DmaCancelled */
++    NULL, /* DmaVerify */
++};
++
++EP_OUTPUTQ *
++ep3_alloc_outputq (EP_RAIL *r, unsigned slotSize, unsigned slotCount)
++{
++    EP3_RAIL         *rail = (EP3_RAIL *) r;
++    EP3_OUTPUTQ      *outputq;
++    int               i;
++    E3_BlockCopyEvent event;
++
++    ASSERT ((slotSize & (EP_SYSTEMQ_MSG_ALIGN-1)) == 0);
++
++    KMEM_ALLOC (outputq, EP3_OUTPUTQ *, sizeof (EP3_OUTPUTQ), 1);
++
++    if (outputq == NULL)
++      return NULL;
++
++    outputq->q_slotCount = slotCount;
++    outputq->q_slotSize  = slotSize;
++
++    outputq->q_elan = ep_alloc_elan (r, Q_ELAN_SIZE(outputq), 0, &outputq->q_elanAddr);
++
++    if (outputq->q_elan == (sdramaddr_t) 0)
++    {
++      KMEM_FREE (outputq, sizeof (EP3_OUTPUTQ));
++      return NULL;
++    }
++
++    outputq->q_main = ep_alloc_main (r, Q_MAIN_SIZE(outputq), 0, &outputq->q_mainAddr);
++
++    if (outputq->q_main == (void *) NULL)
++    {
++      ep_free_elan (r, outputq->q_elanAddr, Q_ELAN_SIZE(outputq));
++      KMEM_FREE (outputq, sizeof (EP3_OUTPUTQ));
++      return NULL;
++    }
++
++    RegisterCookie (&rail->CookieTable, &outputq->q_cookie, outputq->q_elanAddr, &ep3_outputq_cookie_ops, outputq);
++
++    for (i = 0; i < slotCount; i++)
++    {
++      EP3_INIT_COPY_EVENT (event, outputq->q_cookie, Q_DONE_ADDR(outputq, i), 0);
++
++      Q_DONE(outputq, i) = outputq->q_cookie.Cookie;
++      
++      elan3_sdram_copyl_to_sdram (rail->Device, &event, Q_EVENT(outputq, i), sizeof (E3_BlockCopyEvent));
++    }
++
++    return (EP_OUTPUTQ *) outputq;
++}
++
++void
++ep3_free_outputq (EP_RAIL *r, EP_OUTPUTQ *q)
++{
++    EP3_RAIL    *rail    = (EP3_RAIL *) r;
++    EP3_OUTPUTQ *outputq = (EP3_OUTPUTQ *) q;
++
++    DeregisterCookie (&rail->CookieTable, &outputq->q_cookie);
++    
++    ep_free_main (r, outputq->q_mainAddr, Q_MAIN_SIZE(outputq));
++    ep_free_elan (r, outputq->q_elanAddr, Q_ELAN_SIZE(outputq));
++ 
++    KMEM_FREE (outputq, sizeof (EP3_OUTPUTQ));
++}
++
++void *
++ep3_outputq_msg (EP_RAIL *r, EP_OUTPUTQ *q, unsigned slotNum)
++{
++    return Q_MSG ((EP3_OUTPUTQ *) q, slotNum);
++}
++
++int
++ep3_outputq_state (EP_RAIL *r, EP_OUTPUTQ *q, unsigned slotNum)
++{
++    switch (Q_DONE((EP3_OUTPUTQ *) q, slotNum))
++    {
++    case EP3_EVENT_ACTIVE:
++      return EP_OUTPUTQ_BUSY;
++      
++    case EP3_EVENT_FAILED:
++      return EP_OUTPUTQ_FAILED;
++      
++    default:
++      return EP_OUTPUTQ_FINISHED;
++    }
++}
++
++int
++ep3_outputq_send (EP_RAIL *r, EP_OUTPUTQ *q, unsigned slotNum, unsigned size, 
++                unsigned vp, unsigned qnum, unsigned retries)
++{
++    EP3_RAIL    *rail    = (EP3_RAIL *) r;
++    EP3_OUTPUTQ *outputq = (EP3_OUTPUTQ *) q;
++    unsigned     base    = outputq->q_slotSize - roundup (size, E3_BLK_ALIGN);
++    E3_DMA_BE    dmabe;
++
++    dmabe.s.dma_type          = E3_DMA_TYPE(DMA_BYTE, DMA_WRITE, DMA_QUEUED, retries);
++    dmabe.s.dma_size            = roundup (size, E3_BLK_ALIGN);
++    dmabe.s.dma_source          = Q_MSG_ADDR(outputq, slotNum) + base;
++    dmabe.s.dma_dest            = base;
++    dmabe.s.dma_destEvent       = EP_SYSTEMQ_ADDR(qnum);
++    dmabe.s.dma_destCookieVProc = vp;
++    dmabe.s.dma_srcEvent        = Q_EVENT_ADDR(outputq, slotNum);
++    dmabe.s.dma_srcCookieVProc  = 0;
++
++    Q_DONE(outputq, slotNum) = EP3_EVENT_ACTIVE;
++    
++    elan3_sdram_writel (rail->Device, Q_EVENT(outputq, slotNum), 1);
++
++    if (IssueDma (rail, &dmabe, EP_RETRY_CRITICAL, FALSE) != ISSUE_COMMAND_OK)
++    {
++      Q_DONE(outputq, slotNum) = EP3_EVENT_FAILED;
++      return FALSE;
++    }
++
++    return TRUE;
++}
+Index: linux-2.6.5/drivers/net/qsnet/ep/kmsg_elan4.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/kmsg_elan4.c 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/kmsg_elan4.c      2005-05-11 12:10:12.530918592 -0400
+@@ -0,0 +1,418 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: kmsg_elan4.c,v 1.8.6.2 2005/02/28 14:06:56 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/kmsg_elan4.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++
++#include "debug.h"
++#include "kcomm_vp.h"
++#include "kcomm_elan4.h"
++
++#include <elan4/trtype.h>
++
++static void
++ep4_inputq_interrupt (EP4_RAIL *rail, void *arg)
++{
++    EP4_INPUTQ *inputq = (EP4_INPUTQ *) arg;
++
++    /* mark the queue as "fired" to cause a single waitevent
++     * to be issued next time the queue is polled */
++    atomic_inc (&inputq->q_fired);
++    
++    (*inputq->q_callback)(&rail->r_generic, inputq->q_arg);
++}
++
++EP_INPUTQ *
++ep4_alloc_inputq (EP_RAIL *r, unsigned qnum, unsigned slotSize, unsigned slotCount,
++                EP_INPUTQ_CALLBACK *callback, void *arg)
++{
++    EP4_RAIL     *rail = (EP4_RAIL *) r;
++    EP4_INPUTQ   *inputq;
++    E4_Event32    qevent;
++    void         *slots;
++    int           i;
++
++    ASSERT ((slotSize & (EP_SYSTEMQ_MSG_ALIGN-1)) == 0);
++
++    KMEM_ALLOC (inputq, EP4_INPUTQ *, sizeof (EP4_INPUTQ), 1);
++
++    if (inputq == NULL)
++      return (EP_INPUTQ *) NULL;
++    
++    if ((slots = ep_alloc_main (&rail->r_generic, slotSize * slotCount, 0, &inputq->q_slotsAddr)) == NULL)
++    {
++      KMEM_FREE (inputq, sizeof (EP4_INPUTQ));
++      return (EP_INPUTQ *) NULL;
++    }
++
++    inputq->q_slotSize  = slotSize;
++    inputq->q_slotCount = slotCount;
++    inputq->q_callback  = callback;
++    inputq->q_arg       = arg;
++    inputq->q_slots     = slots;
++
++    /* Initialise all the slots to be "unreceived" */
++    for (i = 0; i < slotCount; i++)
++      ((uint32_t *) ((unsigned long) slots + (i+1) * slotSize))[-1] = EP_SYSTEMQ_UNRECEIVED;
++    
++    inputq->q_last      = inputq->q_slotsAddr + (slotCount-1) * slotSize;
++    inputq->q_fptr      = inputq->q_slotsAddr;
++    inputq->q_desc      = EP_SYSTEMQ_DESC (rail->r_queuedescs, qnum);
++    inputq->q_descAddr  = EP_SYSTEMQ_ADDR (qnum);
++    inputq->q_eventAddr = rail->r_elan_addr + offsetof (EP4_RAIL_ELAN, r_qevents[qnum]);
++
++    if (callback)
++    {
++      if ((inputq->q_ecq = ep4_get_ecq (rail, EP4_ECQ_EVENT, 1)) == 0)
++      {
++          ep_free_main (&rail->r_generic, inputq->q_slotsAddr, inputq->q_slotSize * inputq->q_slotCount);
++
++          KMEM_FREE (inputq, sizeof (EP4_INPUTQ));
++          return (EP_INPUTQ *) NULL;
++      }
++
++      if ((inputq->q_wcq = ep4_get_ecq (rail, EP4_ECQ_MAIN, 4)) == 0)
++      {
++          ep4_put_ecq (rail, inputq->q_ecq, 1);
++          ep_free_main (&rail->r_generic, inputq->q_slotsAddr, inputq->q_slotSize * inputq->q_slotCount);
++
++          KMEM_FREE (inputq, sizeof (EP4_INPUTQ));
++          return (EP_INPUTQ *) NULL;
++      }
++
++      ep4_register_intcookie (rail, &inputq->q_intcookie, inputq->q_descAddr, ep4_inputq_interrupt, inputq);
++
++      inputq->q_count = 0;
++
++      atomic_set (&inputq->q_fired, 0);
++
++      /* Initialise the queue event */
++      qevent.ev_CountAndType = E4_EVENT_INIT_VALUE (callback ? -32 : 0, E4_EVENT_WRITE, E4_EVENT_DTYPE_LONG, 0);
++      qevent.ev_WritePtr     = inputq->q_ecq->ecq_addr;
++      qevent.ev_WriteValue   = (inputq->q_intcookie.int_val << E4_MAIN_INT_SHIFT) | INTERRUPT_CMD;
++    }
++
++    /* copy the event down to sdram */
++    elan4_sdram_copyq_to_sdram (rail->r_ctxt.ctxt_dev, &qevent, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_qevents[qnum]), sizeof (E4_Event32));
++
++    return (EP_INPUTQ *) inputq;
++}
++
++void
++ep4_free_inputq (EP_RAIL *r, EP_INPUTQ *q)
++{
++    EP4_RAIL   *rail   = (EP4_RAIL *) r;
++    EP4_INPUTQ *inputq = (EP4_INPUTQ *) q;
++
++    ep_free_main (&rail->r_generic, inputq->q_slotsAddr, inputq->q_slotSize * inputq->q_slotCount);
++
++    if (inputq->q_callback)
++    {
++      ep4_deregister_intcookie (rail, &inputq->q_intcookie);
++      ep4_put_ecq (rail, inputq->q_ecq, 1);
++      ep4_put_ecq (rail, inputq->q_wcq, 4);
++    }
++
++    KMEM_FREE (inputq, sizeof (EP4_INPUTQ));
++}
++
++void
++ep4_enable_inputq (EP_RAIL *r, EP_INPUTQ *q)
++{
++    EP4_RAIL     *rail     = (EP4_RAIL *) r;
++    EP4_INPUTQ   *inputq   = (EP4_INPUTQ *) q;
++    EP_ADDR       lastSlot = inputq->q_slotsAddr + (inputq->q_slotCount-1) * inputq->q_slotSize;
++    E4_InputQueue qdesc;
++
++    qdesc.q_bptr    = inputq->q_slotsAddr;
++    qdesc.q_fptr    = inputq->q_slotsAddr;
++    qdesc.q_control = E4_InputQueueControl (inputq->q_slotsAddr, lastSlot, inputq->q_slotSize);
++    qdesc.q_event   = inputq->q_callback ? inputq->q_eventAddr : 0;
++
++    /* copy the queue descriptor down to sdram */
++    ep4_write_qdesc (rail, inputq->q_desc, &qdesc);
++
++    EPRINTF5 (DBG_KMSG,  "ep_enable_inputq: %x - %016llx %016llx %016llx %016llx\n", (int) inputq->q_descAddr,
++          elan4_sdram_readq (rail->r_ctxt.ctxt_dev, inputq->q_desc + 0),
++          elan4_sdram_readq (rail->r_ctxt.ctxt_dev, inputq->q_desc + 8),
++          elan4_sdram_readq (rail->r_ctxt.ctxt_dev, inputq->q_desc + 16),
++          elan4_sdram_readq (rail->r_ctxt.ctxt_dev, inputq->q_desc + 24));
++}
++
++void
++ep4_disable_inputq (EP_RAIL *r, EP_INPUTQ *q)
++{
++    EP4_RAIL     *rail   = (EP4_RAIL *) r;
++    EP4_INPUTQ   *inputq = (EP4_INPUTQ *) q;
++    E4_InputQueue qdesc;
++
++    /* Initialise the input queue descriptor as "full" with no event */
++    qdesc.q_bptr    = 0;
++    qdesc.q_fptr    = 8;
++    qdesc.q_control = E4_InputQueueControl(qdesc.q_bptr, qdesc.q_fptr, 8);
++    qdesc.q_event   = 0;
++
++    /* copy the queue descriptor down to sdram */
++    ep4_write_qdesc (rail, inputq->q_desc, &qdesc);
++}
++
++int
++ep4_poll_inputq (EP_RAIL *r, EP_INPUTQ *q, int maxCount, EP_INPUTQ_HANDLER *handler, void *arg)
++{
++    EP4_RAIL   *rail   = (EP4_RAIL *) r;
++    ELAN4_DEV  *dev    = rail->r_ctxt.ctxt_dev; 
++    EP4_INPUTQ *inputq = (EP4_INPUTQ *) q;
++    sdramaddr_t qdesc = inputq->q_desc;
++    E4_Addr     fptr  = inputq->q_fptr;
++    E4_Addr     bptr  = elan4_sdram_readl (dev, qdesc + offsetof (E4_InputQueue, q_bptr));
++    int               count = 0;
++    int         delay;
++
++    while (bptr != 0 && fptr != bptr)
++    {
++      while (fptr != bptr)
++      {
++          unsigned long slot = (unsigned long) inputq->q_slots + (fptr - inputq->q_slotsAddr);
++          
++          /* Poll the final word of the message until the message has completely
++           * arrived in main memory. */
++          for (delay = 1; ((uint32_t *) (slot + inputq->q_slotSize))[-1] == EP_SYSTEMQ_UNRECEIVED && delay < EP_SYSTEMQ_UNRECEIVED_TLIMIT; delay <<= 1)
++              DELAY (delay);
++          
++          EPRINTF4(DBG_KMSG, "ep4_poll_inputq: %x slot %d of %d [%08x]\n", (int)inputq->q_descAddr,
++                   ((int)(fptr - inputq->q_slotsAddr))/inputq->q_slotSize, 
++                   inputq->q_slotCount, ((uint32_t *) (slot + inputq->q_slotSize))[-1]);
++          
++          /* Call the message handler */
++          (*handler) (r, arg, (void *) slot);
++          
++          /* reset the last word of the slot to "unreceived" */
++          ((uint32_t *) (slot + inputq->q_slotSize))[-1] = EP_SYSTEMQ_UNRECEIVED;
++          
++          /* move on the front pointer */
++          fptr = (fptr == inputq->q_last) ? inputq->q_slotsAddr : fptr + inputq->q_slotSize;
++          
++          elan4_sdram_writel (dev, qdesc + offsetof (E4_InputQueue, q_fptr), fptr);
++          
++          inputq->q_count++;
++          
++          if (++count >= maxCount && maxCount)
++          {
++              inputq->q_fptr = fptr;
++
++              return count;
++          }
++      }
++
++      bptr = elan4_sdram_readl (dev, qdesc + offsetof (E4_InputQueue, q_bptr));
++    }
++
++    inputq->q_fptr = fptr;
++
++    /* Only insert a single wait event command if the callback has
++     * occured, otherwise just acrue the count as we've just periodically
++     * polled it.
++     */
++    if (inputq->q_callback && atomic_read (&inputq->q_fired))
++    {
++      atomic_dec (&inputq->q_fired);
++
++      ep4_wait_event_cmd (inputq->q_wcq, inputq->q_eventAddr,
++                          E4_EVENT_INIT_VALUE (-inputq->q_count << 5, E4_EVENT_WRITE, E4_EVENT_DTYPE_LONG, 0),
++                          inputq->q_ecq->ecq_addr,
++                          (inputq->q_intcookie.int_val << E4_MAIN_INT_SHIFT) | INTERRUPT_CMD);
++
++      inputq->q_count = 0;
++    }
++
++    return count;
++}
++
++#define Q_MSG(q,slotNum)         (unsigned long)((q)->q_main      + (slotNum) * (q)->q_slotSize)
++#define Q_MSG_ADDR(q,slotNum)                 ((q)->q_mainAddr  + (slotNum) * (q)->q_slotSize)
++#define Q_DONE(q,slotNum)        *((E4_uint64 *)((q)->q_main      + (q)->q_slotCount * (q)->q_slotSize + (slotNum) * sizeof (E4_uint64)))
++#define Q_DONE_ADDR(q,slotNum)                        ((q)->q_mainAddr  + (q)->q_slotCount * (q)->q_slotSize + (slotNum) * sizeof (E4_uint64))
++
++#define Q_MAIN_SIZE(q)                        ((q)->q_slotCount * ((q)->q_slotSize + sizeof (E4_uint64)))
++
++#define Q_DONE_VAL(val,cnt)           ((cnt) << 16 | (val))
++#define Q_DONE_RET(done)              ((int) ((done) & 0xffff))
++#define Q_DONE_CNT(done)              ((int) ((done) >> 16))
++
++EP_OUTPUTQ *
++ep4_alloc_outputq (EP_RAIL *r, unsigned slotSize, unsigned slotCount)
++{
++    EP4_RAIL    *rail = (EP4_RAIL *) r;
++    EP4_OUTPUTQ *outputq;
++    int          i;
++
++    ASSERT ((slotSize & (EP_SYSTEMQ_MSG_ALIGN-1)) == 0);
++
++    KMEM_ALLOC (outputq, EP4_OUTPUTQ *, sizeof (EP4_OUTPUTQ), 1);
++
++    if (outputq == NULL)
++      return NULL;
++
++    spin_lock_init (&outputq->q_lock);
++
++    outputq->q_slotCount = slotCount;
++    outputq->q_slotSize  = slotSize;
++    outputq->q_main      = ep_alloc_main (r, Q_MAIN_SIZE(outputq), 0, &outputq->q_mainAddr);
++
++    if (outputq->q_main == (E4_uint64 *) NULL)
++    {
++      KMEM_FREE (outputq, sizeof (EP_OUTPUTQ));
++      return NULL;
++    }
++
++    outputq->q_cq = elan4_alloccq (&rail->r_ctxt, CQ_Size64K, CQ_STENEnableBit | CQ_WriteEnableBit, CQ_Priority);
++
++    if (outputq->q_cq == (ELAN4_CQ *) NULL)
++    {
++      ep_free_main (&rail->r_generic, outputq->q_mainAddr, Q_MAIN_SIZE(outputq));
++
++      KMEM_FREE (outputq, sizeof (EP_OUTPUTQ));
++    }
++
++    outputq->q_dwords = CQ_Size (outputq->q_cq->cq_size) >> 3;
++
++    /* mark all the queue slots as finished */
++    for (i = 0; i < slotCount; i++)
++      Q_DONE(outputq, i) = Q_DONE_VAL (EP_OUTPUTQ_FINISHED, 0);
++
++    return (EP_OUTPUTQ *) outputq;
++}
++
++void
++ep4_free_outputq (EP_RAIL *r, EP_OUTPUTQ *q)
++{
++    EP4_RAIL    *rail    = (EP4_RAIL *) r;
++    EP4_OUTPUTQ *outputq = (EP4_OUTPUTQ *) q;
++
++    elan4_freecq (&rail->r_ctxt, outputq->q_cq);
++
++    ep_free_main (&rail->r_generic, outputq->q_mainAddr, Q_MAIN_SIZE(outputq));
++
++    spin_lock_destroy (&outputq->q_lock);
++
++    KMEM_FREE (outputq, sizeof (EP4_OUTPUTQ));
++}
++
++void *
++ep4_outputq_msg (EP_RAIL *r, EP_OUTPUTQ *q, unsigned slotNum)
++{
++    return (void *) Q_MSG ((EP4_OUTPUTQ *) q, slotNum);
++}
++
++int
++ep4_outputq_state (EP_RAIL *r, EP_OUTPUTQ *q, unsigned slotNum)
++{
++    EPRINTF2 (DBG_KMSG, "ep4_outputq_state: slotNum %d state %x\n", slotNum, (int)Q_DONE((EP4_OUTPUTQ *) q, slotNum));
++
++    return Q_DONE_RET(Q_DONE((EP4_OUTPUTQ *)q, slotNum));
++}
++
++int
++ep4_outputq_send (EP_RAIL *r, EP_OUTPUTQ *q, unsigned slotNum, unsigned size, 
++                unsigned vp, unsigned qnum, unsigned retries)
++{
++    EP4_OUTPUTQ *outputq = (EP4_OUTPUTQ *) q;
++    unsigned int nbytes  = roundup (size, 32);
++    unsigned int base    = outputq->q_slotSize - nbytes;
++    unsigned int i, dwords;
++    unsigned long flags;
++    E4_uint64 val;
++
++    spin_lock_irqsave (&outputq->q_lock, flags);
++
++    EPRINTF4 (DBG_KMSG, "ep4_outputq_send: slotNum=%d size=%d vp=%d qnum=%d\n", slotNum, size, vp, qnum);
++
++    /* compute command queue size as follows - each slot uses
++     *     overhead:  14 dwords +
++     *           data > 128 ? 36 dwords
++     *     data > 64  ? 18 dwords
++     *     data > 32  ? 10 dwords
++     *     else         6  dwords
++     */
++    dwords = 14 + (size > 128 ? 36 :
++                 size > 64  ? 18 :
++                 size  ? 10 : 6);
++
++    outputq->q_dwords += Q_DONE_CNT (Q_DONE(outputq, slotNum));
++
++    if (dwords > outputq->q_dwords)
++    {
++      /* attempt to reclaim command queue space from other slots */
++      i = slotNum;
++      do {
++          if (++i == outputq->q_slotCount)
++              i = 0;
++          
++          val = Q_DONE(outputq, i);
++
++          if ((Q_DONE_RET (val) == EP_OUTPUTQ_FINISHED || Q_DONE_RET (val) == EP_OUTPUTQ_FAILED) && Q_DONE_CNT(val) > 0)
++          {
++              outputq->q_dwords += Q_DONE_CNT (val);
++
++              Q_DONE(outputq, i) = Q_DONE_VAL(Q_DONE_RET(val), 0);
++          }
++      } while (i != slotNum && dwords > outputq->q_dwords);
++    }
++
++    if (dwords > outputq->q_dwords)
++    {
++      spin_unlock_irqrestore (&outputq->q_lock, flags);
++      
++      EPRINTF0 (DBG_KMSG, "ep4_outputq_state: no command queue space\n");
++      return 0;
++    }
++
++    outputq->q_dwords -= dwords;
++
++    Q_DONE(outputq, slotNum) = Q_DONE_VAL (EP_OUTPUTQ_BUSY, dwords);
++
++    if (outputq->q_retries != retries)
++    {
++      outputq->q_retries = retries;
++
++      elan4_guard (outputq->q_cq, GUARD_CHANNEL(1) | GUARD_RESET(retries));
++      elan4_nop_cmd (outputq->q_cq, 0);
++    }
++
++    /* transfer the top "size" bytes from message buffer to top of input queue */
++    elan4_open_packet (outputq->q_cq, OPEN_PACKET (0, PACK_OK | RESTART_COUNT_ZERO, vp));
++    elan4_sendtrans0 (outputq->q_cq, TR_INPUT_Q_GETINDEX, EP_SYSTEMQ_ADDR(qnum));
++
++    /* send upto EP_SYSTEMQ_MSG_MAX (256) bytes of message to the top of the slot */
++    if (size > 128)
++    {
++      elan4_sendtransp (outputq->q_cq, TR_WRITE (128 >> 3, 0, TR_DATATYPE_DWORD), base + 0,   (void *) (Q_MSG(outputq, slotNum) + base + 0));
++      elan4_sendtransp (outputq->q_cq, TR_WRITE (128 >> 3, 0, TR_DATATYPE_DWORD), base + 128, (void *) (Q_MSG(outputq, slotNum) + base + 128));
++    }
++    else if (size > 64)
++      elan4_sendtransp (outputq->q_cq, TR_WRITE (128 >> 3, 0, TR_DATATYPE_DWORD), base, (void *) (Q_MSG(outputq, slotNum) + base));
++    else if (size > 32)
++      elan4_sendtransp (outputq->q_cq, TR_WRITE (64 >> 3, 0, TR_DATATYPE_DWORD),  base, (void *) (Q_MSG(outputq, slotNum) + base));
++    else
++      elan4_sendtransp (outputq->q_cq, TR_WRITE (32 >> 3, 0, TR_DATATYPE_DWORD),  base, (void *) (Q_MSG(outputq, slotNum) + base));
++    elan4_sendtrans1 (outputq->q_cq, TR_INPUT_Q_COMMIT, EP_SYSTEMQ_ADDR(qnum), 0 /* no cookie */);
++
++    elan4_guard (outputq->q_cq, GUARD_CHANNEL (1) | GUARD_TEST(0, PACK_OK) | GUARD_RESET (outputq->q_retries));
++    elan4_write_dword_cmd (outputq->q_cq, Q_DONE_ADDR(outputq, slotNum), Q_DONE_VAL (EP_OUTPUTQ_FINISHED, dwords));
++
++    elan4_guard (outputq->q_cq, GUARD_CHANNEL (1) | GUARD_TEST(0, RESTART_COUNT_ZERO) | GUARD_RESET (outputq->q_retries));
++    elan4_write_dword_cmd (outputq->q_cq, Q_DONE_ADDR(outputq, slotNum), Q_DONE_VAL (EP_OUTPUTQ_FAILED, dwords));
++
++    spin_unlock_irqrestore (&outputq->q_lock, flags);
++
++    return 1;
++}
+Index: linux-2.6.5/drivers/net/qsnet/ep/kthread.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/kthread.c    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/kthread.c 2005-05-11 12:10:12.530918592 -0400
+@@ -0,0 +1,186 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: kthread.c,v 1.5 2004/05/19 08:54:57 david Exp $ $Name: QSNETMODULES-4-31_20050321 $"
++/*      $Source: /cvs/master/quadrics/epmod/kthread.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan/kthread.h>
++
++void
++ep_kthread_init (EP_KTHREAD *kt)
++{
++      spin_lock_init (&kt->lock);
++      kcondvar_init (&kt->wait);
++      
++      kt->next_run     = 0;
++      kt->should_stall = 0;
++      kt->started      = 0;
++      kt->should_stop  = 0;
++      kt->stopped      = 0;
++      kt->state        = KT_STATE_RUNNING;
++}
++
++void
++ep_kthread_destroy (EP_KTHREAD *kt)
++{
++      spin_lock_destroy (&kt->lock);
++      kcondvar_destroy (&kt->wait);
++}
++
++void
++ep_kthread_started (EP_KTHREAD *kt)
++{
++      unsigned long flags;
++      
++      spin_lock_irqsave (&kt->lock, flags);
++      kt->started = 1;
++      spin_unlock_irqrestore(&kt->lock, flags);
++}
++
++void
++ep_kthread_stopped (EP_KTHREAD *kt)
++{
++      unsigned long flags;
++      
++      spin_lock_irqsave (&kt->lock, flags);
++      kt->stopped = 1;
++      kcondvar_wakeupall (&kt->wait, &kt->lock);
++      spin_unlock_irqrestore(&kt->lock, flags);
++}
++
++int
++ep_kthread_should_stall (EP_KTHREAD *kth)
++{
++      return (kth->should_stall);
++}
++
++int
++ep_kthread_sleep (EP_KTHREAD *kt, long next_run)
++{
++      unsigned long flags;
++
++      spin_lock_irqsave (&kt->lock, flags);
++      if (next_run && (kt->next_run == 0 || BEFORE (next_run, kt->next_run)))
++              kt->next_run = next_run;
++
++      if (kt->should_stop)
++      {
++              spin_unlock_irqrestore (&kt->lock, flags);
++              return (-1);
++      }
++      
++      do {
++              if (kt->should_stall)
++                      kcondvar_wakeupall (&kt->wait, &kt->lock);
++
++              kt->state = KT_STATE_SLEEPING;
++              kt->running = 0;
++              if (kt->should_stall || kt->next_run == 0)
++                      kcondvar_wait (&kt->wait, &kt->lock, &flags);
++              else
++                      kcondvar_timedwait (&kt->wait,&kt->lock, &flags, kt->next_run);
++              kt->state = KT_STATE_RUNNING;
++              kt->running = lbolt;
++      } while (kt->should_stall);
++      kt->next_run = 0;
++      spin_unlock_irqrestore (&kt->lock, flags);
++      
++      return (0);
++}
++
++void
++ep_kthread_schedule (EP_KTHREAD *kt, long tick)
++{
++      unsigned long flags;
++      
++      spin_lock_irqsave (&kt->lock, flags);
++      if (kt->next_run == 0 || BEFORE (tick, kt->next_run))
++      {
++              kt->next_run = tick;
++              if (!kt->should_stall && kt->state == KT_STATE_SLEEPING)
++              {
++                      kt->state = KT_STATE_SCHEDULED;
++                      kcondvar_wakeupone (&kt->wait, &kt->lock);
++              }
++      }
++      spin_unlock_irqrestore (&kt->lock, flags);
++}
++
++void
++ep_kthread_stall (EP_KTHREAD *kt)
++{
++      unsigned long flags;
++      
++      spin_lock_irqsave (&kt->lock, flags);
++      if (kt->should_stall++ == 0)
++              kcondvar_wakeupall (&kt->wait, &kt->lock);
++
++      while (kt->state != KT_STATE_SLEEPING)
++              kcondvar_wait (&kt->wait, &kt->lock, &flags);
++      spin_unlock_irqrestore (&kt->lock, flags);
++}
++
++void
++ep_kthread_resume (EP_KTHREAD *kt)
++{
++      unsigned long flags;
++
++      spin_lock_irqsave (&kt->lock, flags);
++      if (--kt->should_stall == 0)
++      {
++              kt->state = KT_STATE_SCHEDULED;
++              kcondvar_wakeupone (&kt->wait, &kt->lock);
++      }
++      spin_unlock_irqrestore (&kt->lock, flags);
++}
++
++void
++ep_kthread_stop (EP_KTHREAD *kt)
++{
++      unsigned long flags;
++      
++      spin_lock_irqsave (&kt->lock, flags);
++      kt->should_stop = 1;
++      while (kt->started && !kt->stopped)
++      {
++              kcondvar_wakeupall (&kt->wait, &kt->lock);
++              kcondvar_wait (&kt->wait, &kt->lock, &flags);
++      }
++      spin_unlock_irqrestore (&kt->lock, flags);
++}
++
++int
++ep_kthread_state (EP_KTHREAD *kt, long *time)
++{
++      unsigned long flags;
++      int res = KT_STATE_SLEEPING;
++
++      spin_lock_irqsave (&kt->lock, flags);
++
++      if (kt->next_run) {
++              *time = kt->next_run;
++              res   = kt->should_stall ? KT_STATE_STALLED : KT_STATE_SCHEDULED;
++      }
++
++      if (kt->running) {
++              *time = kt->running;
++              res   = KT_STATE_RUNNING;
++      }
++
++      spin_unlock_irqrestore (&kt->lock, flags);
++      
++      return res;
++}
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/ep/kthread.h
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/kthread.h    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/kthread.h 2005-05-11 12:10:12.530918592 -0400
+@@ -0,0 +1,53 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN3_KTHREAD_H
++#define __ELAN3_KTHREAD_H
++
++#ident "@(#)$Id: kthread.h,v 1.4 2004/05/06 14:24:08 david Exp $ $Name: QSNETMODULES-4-31_20050321 $"
++/*      $Source: /cvs/master/quadrics/epmod/kthread.h,v $*/
++
++typedef struct ep_kthread
++{
++      kcondvar_t      wait;                                   /* place to sleep */
++      spinlock_t      lock;                                   /* and lock */
++      long            next_run;                               /* tick when thread should next run */
++      long            running;                                /* tick when thread started to run */
++      unsigned short  should_stall;
++      unsigned char   state;
++      unsigned int    started:1;
++      unsigned int    should_stop:1;
++      unsigned int    stopped:1;
++} EP_KTHREAD;
++
++#define KT_STATE_SLEEPING             0
++#define KT_STATE_SCHEDULED            1
++#define KT_STATE_RUNNING              2
++#define KT_STATE_STALLED              3
++
++#define AFTER(a, b)                   ((((long)(a)) - ((long)(b))) > 0)
++#define BEFORE(a,b)                   ((((long)(a)) - ((long)(b))) < 0)
++
++extern void ep_kthread_init (EP_KTHREAD *kt);
++extern void ep_kthread_destroy (EP_KTHREAD *kt);
++extern void ep_kthread_started (EP_KTHREAD *kt);
++extern void ep_kthread_stopped (EP_KTHREAD *kt);
++extern int  ep_kthread_should_stall (EP_KTHREAD *kth);
++extern int  ep_kthread_sleep (EP_KTHREAD *kth, long next_run);
++extern void ep_kthread_schedule (EP_KTHREAD *kt, long when);
++extern void ep_kthread_stall (EP_KTHREAD *kth);
++extern void ep_kthread_resume (EP_KTHREAD *kt);
++extern void ep_kthread_stop (EP_KTHREAD *kt);
++extern int  ep_kthread_state (EP_KTHREAD *kt, long *time);
++#endif /* __ELAN3_KTHREAD_H */
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/ep/Makefile
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/Makefile     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/Makefile  2005-05-11 12:10:12.531918440 -0400
+@@ -0,0 +1,17 @@
++#
++# Makefile for Quadrics QsNet
++#
++# Copyright (c) 2002-2004 Quadrics Ltd
++#
++# File: drivers/net/qsnet/ep/Makefile
++#
++
++
++ep3-$(CONFIG_ELAN3)   := kcomm_elan3.o kmsg_elan3.o kmap_elan3.o neterr_elan3.o probenetwork_elan3.o support_elan3.o threadcode_elan3.o threadcode_elan3_Linux.o epcomms_elan3.o epcommsTx_elan3.o epcommsRx_elan3.o
++ep4-$(CONFIG_ELAN4)   := kcomm_elan4.o kmsg_elan4.o kmap_elan4.o neterr_elan4.o probenetwork_elan4.o commands_elan4.o debug_elan4.o support_elan4.o threadcode_elan4_Linux.o epcomms_elan4.o epcommsTx_elan4.o epcommsRx_elan4.o
++#
++
++obj-$(CONFIG_EP)      += ep.o
++ep-objs       := cm.o debug.o kalloc.o kcomm.o kmap.o kthread.o neterr.o nmh.o probenetwork.o railhints.o rmap.o statemap.o support.o threadcode.o epcomms.o epcommsRx.o epcommsTx.o epcommsFwd.o conf_linux.o procfs_linux.o ep_procfs.o cm_procfs.o $(ep3-$(CONFIG_EP)) $(ep4-$(CONFIG_EP))
++
++EXTRA_CFLAGS          +=  -DDEBUG -DDEBUG_PRINTF -DDEBUG_ASSERT
+Index: linux-2.6.5/drivers/net/qsnet/ep/Makefile.conf
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/Makefile.conf        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/Makefile.conf     2005-05-11 12:10:12.531918440 -0400
+@@ -0,0 +1,12 @@
++# Flags for generating QsNet Linux Kernel Makefiles
++MODNAME               =       ep.o
++MODULENAME    =       ep
++KOBJFILES     =       cm.o debug.o kalloc.o kcomm.o kmap.o kthread.o neterr.o nmh.o probenetwork.o railhints.o rmap.o statemap.o support.o threadcode.o epcomms.o epcommsRx.o epcommsTx.o epcommsFwd.o conf_linux.o procfs_linux.o ep_procfs.o cm_procfs.o \$\(ep3-\$\(CONFIG_EP\)\) \$\(ep4-\$\(CONFIG_EP\)\)
++EXPORT_KOBJS  =       conf_linux.o
++CONFIG_NAME   =       CONFIG_EP
++SGALFC                =       
++# EXTRALINES START
++
++ep3-$(CONFIG_ELAN3)   := kcomm_elan3.o kmsg_elan3.o kmap_elan3.o neterr_elan3.o probenetwork_elan3.o support_elan3.o threadcode_elan3.o threadcode_elan3_Linux.o epcomms_elan3.o epcommsTx_elan3.o epcommsRx_elan3.o
++ep4-$(CONFIG_ELAN4)   := kcomm_elan4.o kmsg_elan4.o kmap_elan4.o neterr_elan4.o probenetwork_elan4.o commands_elan4.o debug_elan4.o support_elan4.o threadcode_elan4_Linux.o epcomms_elan4.o epcommsTx_elan4.o epcommsRx_elan4.o
++# EXTRALINES END
+Index: linux-2.6.5/drivers/net/qsnet/ep/neterr.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/neterr.c     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/neterr.c  2005-05-11 12:10:12.531918440 -0400
+@@ -0,0 +1,82 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: neterr.c,v 1.25.8.1 2004/11/12 10:54:51 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/neterr.c,v $ */
++
++#include <qsnet/kernel.h>
++#include <elan/kcomm.h>
++
++#include "debug.h"
++
++void
++ep_queue_network_error (EP_RAIL *rail, int nodeId, int what, int channel, EP_NETERR_COOKIE cookie)
++{
++    EP_SYS       *sys      = rail->System;
++    EP_NODE_RAIL *nodeRail = &rail->Nodes[nodeId];
++    unsigned long flags;
++
++    spin_lock_irqsave (&sys->NodeLock, flags);
++
++    ASSERT (nodeRail->State >= EP_NODE_CONNECTED && nodeRail->State <= EP_NODE_LOCAL_PASSIVATE);
++    
++    if (nodeRail->NetworkErrorState == 0)
++    {
++      EPRINTF2 (DBG_NETWORK_ERROR, "%s: raise context filter for node %d due to network error\n", rail->Name, nodeId);
++      printk ("%s: raise context filter for node %d due to network error\n", rail->Name, nodeId);
++      
++      rail->Operations.RaiseFilter (rail, nodeId);
++      
++      if (nodeRail->State == EP_NODE_LOCAL_PASSIVATE)
++          printk ("%s: node %d is flushing - deferring network error fixup\n", rail->Name, nodeId);
++      else
++          list_add_tail (&nodeRail->Link, &rail->NetworkErrorList);
++    }
++    
++    switch (what)
++    {
++    case EP_NODE_NETERR_ATOMIC_PACKET:
++      ASSERT (nodeRail->NetworkErrorCookies[channel] == 0);
++      
++      /* Need to raise the approriate context filter for this node,
++       * and periodically send a neterr fixup message to it until 
++       * we receive an ack from it
++       */
++      IncrStat (rail, NeterrAtomicPacket);
++      
++      nodeRail->NetworkErrorCookies[channel] = cookie;
++      
++      nodeRail->NetworkErrorState |= EP_NODE_NETERR_ATOMIC_PACKET;
++      nodeRail->MsgXid             = ep_xid_cache_alloc (sys, &rail->XidCache);
++      
++      EPRINTF3 (DBG_NETWORK_ERROR, "%s: atomic packet destroyed - node %d cookie %llx\n", rail->Name, nodeId, cookie);
++
++      printk ("%s: atomic packet destroyed - node %d cookie %llx\n", rail->Name, nodeId, cookie);
++      break;
++
++    case EP_NODE_NETERR_DMA_PACKET:
++      /* Must be an overlapped dma packet, raise the context filter,
++       * and hold it up for a NETWORK_ERROR_TIMEOUT */
++      IncrStat (rail, NeterrDmaPacket);
++      
++      nodeRail->NetworkErrorState |= EP_NODE_NETERR_DMA_PACKET;
++      break;
++    }
++
++    nodeRail->NextRunTime = lbolt + NETWORK_ERROR_TIMEOUT;
++    
++    spin_unlock_irqrestore (&sys->NodeLock, flags);
++
++    ep_kthread_schedule (&sys->ManagerThread, nodeRail->NextRunTime);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++
+Index: linux-2.6.5/drivers/net/qsnet/ep/neterr_elan3.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/neterr_elan3.c       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/neterr_elan3.c    2005-05-11 12:10:12.532918288 -0400
+@@ -0,0 +1,326 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: neterr_elan3.c,v 1.24 2003/11/17 13:26:45 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/neterr_elan3.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++
++#include "kcomm_vp.h"
++#include "kcomm_elan3.h"
++#include "debug.h"
++
++typedef struct neterr_halt_args
++{
++    EP3_RAIL       *Rail;
++    unsigned int      NodeId;
++    EP_NETERR_COOKIE *Cookies;
++} NETERR_HALT_ARGS;
++
++static int
++DmaMatchesCookie (EP3_RAIL *rail, E3_DMA_BE *dma, int nodeId, EP_NETERR_COOKIE *cookies, char *where)
++{
++    E3_uint32     cvproc;
++    E3_uint32     cookie;
++    
++    if (dma->s.dma_direction == DMA_WRITE)
++    {
++      cvproc = dma->s.dma_destCookieVProc;
++      cookie = dma->s.dma_srcCookieVProc;
++    }
++    else
++    {
++      cvproc = dma->s.dma_srcCookieVProc;
++      cookie = dma->s.dma_destCookieVProc;
++    }
++    
++    EPRINTF6 (DBG_NETWORK_ERROR, "%s: Neterr - %s: DMA %08x %08x %08x %08x\n", rail->Generic.Name, where,
++            dma->s.dma_type, dma->s.dma_size, dma->s.dma_source, dma->s.dma_dest);
++    EPRINTF5 (DBG_NETWORK_ERROR, "%s:                     %08x %08x %08x %08x\n", rail->Generic.Name,
++            dma->s.dma_destEvent, dma->s.dma_destCookieVProc, dma->s.dma_srcEvent, dma->s.dma_srcCookieVProc);
++
++    if (EP_VP_ISDATA((cvproc & DMA_PROCESS_MASK)) && EP_VP_TO_NODE(cvproc & DMA_PROCESS_MASK) == nodeId)
++    {
++      /*
++       * This is a DMA going to the node which has a network fixup
++       * request pending, so check if the cookie matches.
++       */
++      if ((cookie == cookies[0] || cookie == cookies[1]) /* && !WaitForEop */)
++      {
++          EPRINTF3 (DBG_NETWORK_ERROR, "%s: match cookie %08x on %s\n", rail->Generic.Name, cookie, where);
++          
++          return (TRUE);
++      }
++    }
++
++    return (FALSE);
++}
++
++
++static void
++NetworkErrorHaltOperation (ELAN3_DEV *dev, void *arg)
++{
++    NETERR_HALT_ARGS *args = (NETERR_HALT_ARGS *) arg;
++    EP3_RAIL         *rail = args->Rail;
++    EP_SYS           *sys  = rail->Generic.System;
++    sdramaddr_t       FPtr, BPtr;
++    sdramaddr_t       Base, Top;
++    E3_DMA_BE         dma;
++    unsigned long     flags;
++
++    spin_lock_irqsave (&sys->NodeLock, flags);
++
++    ASSERT (elan3_sdram_readl (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProc.s.FSR)) == 0);
++    ASSERT (elan3_sdram_readl (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProcData0.s.FSR.Status)) == 0);
++    ASSERT (elan3_sdram_readl (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProcData1.s.FSR.Status)) == 0);
++    ASSERT (elan3_sdram_readl (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProcData2.s.FSR.Status)) == 0);
++    ASSERT (elan3_sdram_readl (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProcData3.s.FSR.Status)) == 0);
++    
++    FPtr  = read_reg32 (dev, DProc_SysCntx_FPtr);
++    BPtr =  read_reg32 (dev, DProc_SysCntx_BPtr);
++    Base  = dev->TAndQBase + offsetof (E3_TrapAndQueue, SysCntxDmaQueue[0]);
++    Top   = dev->TAndQBase + offsetof (E3_TrapAndQueue, SysCntxDmaQueue[E3_SysCntxQueueSize-1]);
++    
++    while (FPtr != BPtr)
++    {
++      elan3_sdram_copyq_from_sdram (dev, FPtr, &dma, sizeof (E3_DMA_BE));
++      
++      if (DmaMatchesCookie (rail, &dma, args->NodeId, args->Cookies, "runq "))
++      {
++          /*
++           * Transfer the DMA to the node, it's source event will 
++           * get executed later.
++           */
++          QueueDmaOnStalledList (rail, &dma);
++          
++          /*
++           * Remove the DMA from the queue by replacing it with one with
++           * zero size and no events.
++           *
++           * NOTE: we must preserve the SYS_CONTEXT_BIT since the Elan uses this
++           * to mark the approriate run queue as empty.
++           */
++          dma.s.dma_type            = (SYS_CONTEXT_BIT << 16);
++          dma.s.dma_size            = 0;
++          dma.s.dma_source          = (E3_Addr) 0;
++          dma.s.dma_dest            = (E3_Addr) 0;
++          dma.s.dma_destEvent       = (E3_Addr) 0;
++          dma.s.dma_destCookieVProc = 0;
++          dma.s.dma_srcEvent        = (E3_Addr) 0;
++          dma.s.dma_srcCookieVProc  = 0;
++          
++          elan3_sdram_copyq_to_sdram (dev, &dma, FPtr, sizeof (E3_DMA_BE));
++      }
++
++      FPtr = (FPtr == Top) ? Base : FPtr + sizeof (E3_DMA);
++    }
++
++    rail->NetworkErrorFlushed = TRUE;
++    kcondvar_wakeupall (&rail->NetworkErrorSleep, &sys->NodeLock);
++
++    spin_unlock_irqrestore (&sys->NodeLock, flags);
++}
++
++void
++ep3_neterr_fixup (EP_RAIL *r, unsigned int nodeId, EP_NETERR_COOKIE *cookies)
++{
++    EP3_RAIL        *rail        = (EP3_RAIL *) r;
++    EP_SYS          *sys         = rail->Generic.System;
++    ELAN3_DEV       *dev         = rail->Device;
++    EP_NODE_RAIL    *nodeRail    = &rail->Generic.Nodes[nodeId];
++    E3_DMA_BE        dmabe;
++    EP3_COOKIE      *cp;
++    E3_uint32        vp;
++    NETERR_HALT_ARGS args;
++    struct list_head *el, *nel, matchedList;
++    int              i;
++    unsigned long    flags;
++
++    INIT_LIST_HEAD (&matchedList);
++
++    StallDmaRetryThread (rail);
++
++    args.Rail       = rail;
++    args.NodeId     = nodeId;
++    args.Cookies    = cookies;
++
++    spin_lock_irqsave (&rail->Device->IntrLock, flags);
++    QueueHaltOperation (rail->Device, 0, NULL, INT_TProcHalted | INT_DProcHalted, NetworkErrorHaltOperation, &args);
++    spin_unlock_irqrestore (&rail->Device->IntrLock, flags);
++    
++    spin_lock_irqsave (&sys->NodeLock, flags);
++    while (! rail->NetworkErrorFlushed)
++      kcondvar_wait (&rail->NetworkErrorSleep, &sys->NodeLock, &flags);
++    rail->NetworkErrorFlushed = FALSE;
++    
++    spin_lock (&rail->DmaRetryLock);
++    for (i = EP_RETRY_BASE; i < EP_NUM_RETRIES; i++)
++    {
++      list_for_each_safe (el, nel, &rail->DmaRetries[i]) {
++          EP3_RETRY_DMA *retry = list_entry (el, EP3_RETRY_DMA, Link);
++
++          if (DmaMatchesCookie (rail, &retry->Dma, nodeId, cookies, "retry"))
++          {
++              /* remove from retry list */
++              list_del (&retry->Link);
++
++              /* add to list of dmas which matched */
++              list_add_tail (&retry->Link, &matchedList);
++          }
++      }
++    }
++    
++    list_for_each_safe (el, nel, &nodeRail->StalledDmas) {
++      EP3_RETRY_DMA *retry = list_entry (el, EP3_RETRY_DMA, Link);
++      
++      if (DmaMatchesCookie (rail, &retry->Dma, nodeId, cookies, "stalled"))
++      {
++          /* remove from retry list */
++          list_del (&retry->Link);
++          
++          /* add to list of dmas which matched */
++          list_add_tail (&retry->Link, &matchedList);
++      }
++    }
++    
++    spin_unlock (&rail->DmaRetryLock);
++    spin_unlock_irqrestore (&sys->NodeLock, flags);
++    
++    ResumeDmaRetryThread (rail);
++
++    /* Now "set" the source event of any write DMA's */
++    while (! list_empty (&matchedList))
++    {
++      EP3_RETRY_DMA *retry = list_entry (matchedList.next, EP3_RETRY_DMA, Link);
++      
++      list_del (&retry->Link);
++
++      if (retry->Dma.s.dma_direction == DMA_WRITE && retry->Dma.s.dma_srcEvent)
++      {
++          sdramaddr_t event = ep_elan2sdram (&rail->Generic, retry->Dma.s.dma_srcEvent);
++
++          /* Block local interrupts, since we need to atomically
++           * decrement the event count and perform the word write
++           */
++          local_irq_save (flags);
++          {
++              E3_uint32 type  = elan3_sdram_readl (dev, event + offsetof (E3_Event, ev_Type));
++              E3_uint32 count = elan3_sdram_readl (dev, event + offsetof (E3_Event, ev_Count));
++
++              elan3_sdram_writel (dev, event + offsetof (E3_Event, ev_Count), count - 1);
++
++              if (count == 1)
++              {
++                  if (type & EV_TYPE_MASK_BCOPY)
++                  {
++                      E3_Addr srcVal  = elan3_sdram_readl (dev, event + offsetof (E3_BlockCopyEvent, ev_Source));
++                      E3_Addr dstAddr = elan3_sdram_readl (dev, event + offsetof (E3_BlockCopyEvent, ev_Dest)) & ~EV_BCOPY_DTYPE_MASK;
++
++                      ASSERT ((srcVal & EV_WCOPY) != 0);
++                      
++                      EPRINTF3 (DBG_NETWORK_ERROR, "%s: neterr perform event word write at %08x with %08x\n", rail->Generic.Name, dstAddr, srcVal);
++
++                      ELAN3_OP_STORE32 (rail->Ctxt, dstAddr, srcVal);
++                  }
++
++                  if ((type & ~EV_TYPE_MASK_BCOPY) != 0)
++                  {
++                      if ((type & EV_TYPE_MASK_CHAIN) == EV_TYPE_CHAIN)
++                      {
++                          printk ("%s: event at %08x - chained event %x is invalid\n", rail->Generic.Name, retry->Dma.s.dma_srcEvent, type);
++                          panic ("ep: neterr invalid event type\n");
++                      }
++                      else if ((type & EV_TYPE_MASK_EVIRQ) == EV_TYPE_EVIRQ)
++                      {
++                          EPRINTF2 (DBG_NETWORK_ERROR, "%s: neterr event interrupt - cookie %08x\n", rail->Generic.Name, (type & ~(EV_TYPE_MASK_EVIRQ|EV_TYPE_MASK_BCOPY)));
++                          
++                          cp = LookupCookie (&rail->CookieTable, (type & ~(EV_TYPE_MASK_EVIRQ|EV_TYPE_MASK_BCOPY)));
++                          
++                          if (cp->Operations->Event)
++                              cp->Operations->Event(rail, cp->Arg);
++                      }
++                      else if ((type & EV_TYPE_MASK_DMA) == EV_TYPE_DMA)
++                      {
++                          sdramaddr_t dma = ep_elan2sdram (&rail->Generic, (type & ~EV_TYPE_MASK2));
++                          
++                          EPRINTF2 (DBG_NETWORK_ERROR, "%s: neterr chained dma - %08x\n", rail->Generic.Name, (type & ~EV_TYPE_MASK2));
++                          
++                          elan3_sdram_copyq_from_sdram (dev, dma, &dmabe, sizeof (E3_DMA));
++                          
++                          if (dmabe.s.dma_direction == DMA_WRITE)
++                          {
++                              vp = dmabe.s.dma_destVProc;
++                              cp = LookupEventCookie (rail, &rail->CookieTable, dmabe.s.dma_srcEvent);
++                          }
++                          else
++                          {
++                              vp = dmabe.s.dma_srcVProc;
++                              cp = LookupEventCookie (rail, &rail->CookieTable, dmabe.s.dma_destEvent);
++                              
++                              /* we MUST convert this into a DMA_READ_REQUEUE dma as if we don't the 
++                               * DMA descriptor will be read from the EP_RETRY_DMA rather than the 
++                               * original DMA - this can then get reused and an incorrect DMA 
++                               * descriptor sent 
++                               * eventp->ev_Type contains the dma address with type in the lower bits 
++                               */ 
++                          
++                              dmabe.s.dma_source    = (type & ~EV_TYPE_MASK2);
++                              dmabe.s.dma_direction = (dmabe.s.dma_direction & ~DMA_READ) | DMA_READ_REQUEUE;
++                          }
++                      
++                          ASSERT (EP_VP_ISDATA(vp));
++                      
++                          nodeRail = &rail->Generic.Nodes[EP_VP_TO_NODE(vp)];
++
++                          switch (nodeRail->State)
++                          {
++                          case EP_NODE_CONNECTED:
++                          case EP_NODE_LEAVING_CONNECTED:
++                              if (cp != NULL)
++                                  cp->Operations->DmaRetry (rail, cp->Arg, &dmabe, EAGAIN);
++                              else
++                              {
++                                  ASSERT (dmabe.s.dma_direction == DMA_WRITE && dmabe.s.dma_srcEvent == 0 && dmabe.s.dma_isRemote);
++                              
++                                  QueueDmaForRetry (rail, &dmabe, EP_RETRY_ANONYMOUS);
++                              }
++                              break;
++
++                          case EP_NODE_LOCAL_PASSIVATE:
++                              QueueDmaOnStalledList (rail, &dmabe);
++                              break;
++
++                          default:
++                              panic ("ep: neterr incorrect state for node\n");
++                          }
++                      }
++                      else if ((type & EV_TYPE_MASK_THREAD) == EV_TYPE_THREAD)
++                      {
++                          printk ("%s: event at %08x - thread waiting %x is invalid\n", rail->Generic.Name, retry->Dma.s.dma_srcEvent, type);
++                          panic ("ep: neterr invalid event type\n");
++                      }
++                  }
++              }
++          }
++          local_irq_restore(flags);
++      }
++      
++      /* add to free list */
++      spin_lock_irqsave (&rail->DmaRetryLock, flags);
++      list_add (&retry->Link, &rail->DmaRetryFreeList);
++      spin_unlock_irqrestore (&rail->DmaRetryLock, flags);
++    }
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++
+Index: linux-2.6.5/drivers/net/qsnet/ep/neterr_elan4.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/neterr_elan4.c       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/neterr_elan4.c    2005-05-11 12:10:12.533918136 -0400
+@@ -0,0 +1,251 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: neterr_elan4.c,v 1.2 2003/11/24 17:57:24 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/neterr_elan4.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++
++#include "kcomm_vp.h"
++#include "kcomm_elan4.h"
++#include "debug.h"
++
++struct neterr_desc
++{
++    EP4_RAIL         *rail;
++    unsigned int      nodeid;
++    EP_NETERR_COOKIE *cookies;
++    int                     done;
++} ;
++
++static int
++dma_matches_cookie (EP4_RAIL *rail, E4_uint64 vproc, E4_uint64 cookie, unsigned int nodeId, EP_NETERR_COOKIE *cookies, const char *where)
++{
++    if ((EP_VP_ISDATA (vproc) && EP_VP_TO_NODE (vproc) == nodeId) && (cookie == cookies[0] || cookie == cookies[1]))
++    {
++      EPRINTF3 (DBG_NETWORK_ERROR, "%s: match cookie %016llx on %s\n", rail->r_generic.Name, cookie, where);
++
++      return 1;
++    }
++    return 0;
++}
++
++static void
++ep4_neterr_dma_flushop (ELAN4_DEV *dev, void *arg, int qfull)
++{
++    struct neterr_desc *desc  = (struct neterr_desc *) arg;
++    EP4_RAIL           *rail  = desc->rail;
++    E4_uint64           qptrs = read_reg64 (dev, DProcHighPriPtrs);
++    E4_uint32           qsize = E4_QueueSize (E4_QueueSizeValue (qptrs));
++    E4_uint32           qfptr = E4_QueueFrontPointer (qptrs);
++    E4_uint32           qbptr = E4_QueueBackPointer (qptrs);
++    E4_DProcQueueEntry  qentry;
++    unsigned long       flags;
++
++    while ((qfptr != qbptr) || qfull)
++    {
++      E4_uint64 cookie = elan4_sdram_readq (dev, qfptr + offsetof (E4_DProcQueueEntry, Desc.dma_cookie));
++      E4_uint64 vproc  = elan4_sdram_readq (dev, qfptr + offsetof (E4_DProcQueueEntry, Desc.dma_vproc));
++
++      if (dma_matches_cookie (rail, vproc, cookie, desc->nodeid, desc->cookies, "runq "))
++      {
++          elan4_sdram_copyq_from_sdram (dev, qfptr, &qentry, sizeof (E4_DProcQueueEntry));
++
++          ep4_queue_dma_stalled (rail, &qentry.Desc);
++
++          /* Replace the dma with one which will "disappear" */
++          qentry.Desc.dma_typeSize = DMA_ShMemWrite | dev->dev_ctxt.ctxt_num;
++          qentry.Desc.dma_cookie   = 0;
++          qentry.Desc.dma_vproc    = 0;
++          qentry.Desc.dma_srcAddr  = 0;
++          qentry.Desc.dma_dstAddr  = 0;
++          qentry.Desc.dma_srcEvent = 0;
++          qentry.Desc.dma_dstEvent = 0;
++
++          elan4_sdram_copyq_to_sdram (dev, &qentry, qfptr, sizeof (E4_DProcQueueEntry));
++      }
++      
++      qfptr = (qfptr & ~(qsize-1)) | ((qfptr + sizeof (E4_DProcQueueEntry)) & (qsize-1));
++      qfull = 0;
++    }
++
++    spin_lock_irqsave (&rail->r_haltop_lock, flags);
++    desc->done = 1;
++    kcondvar_wakeupall (&rail->r_haltop_sleep, &rail->r_haltop_lock);
++    spin_unlock_irqrestore (&rail->r_haltop_lock, flags);
++}
++
++static void
++ep4_neterr_dma_haltop (ELAN4_DEV *dev, void *arg)
++{
++    struct neterr_desc *desc = (struct neterr_desc *) arg;
++
++    elan4_queue_dma_flushop (dev, &desc->rail->r_flushop, 1);
++}
++
++void
++ep4_neterr_fixup_dmas (EP4_RAIL *rail, unsigned int nodeId, EP_NETERR_COOKIE *cookies)
++{
++    EP_NODE_RAIL      *nodeRail = &rail->r_generic.Nodes[nodeId];
++    struct neterr_desc desc;
++    struct list_head   matchedList;
++    struct list_head  *el, *nel;
++    unsigned long      flags;
++    register int       i;
++
++    desc.rail    = rail;
++    desc.nodeid  = nodeId;
++    desc.cookies = cookies;
++    desc.done    = 0;
++
++    INIT_LIST_HEAD (&matchedList);
++
++    /* First -  stall the retry thread, so that it will no longer restart
++     *          any dma's from the retry list */
++    ep_kthread_stall (&rail->r_retry_thread);
++    
++    /* Second - flush through all command queues targetted by events, thread etc */
++    ep4_flush_ecqs (rail);
++    
++    /* Third - queue a halt operation to flush through all DMA's which are executing
++     *         or on the run queues */
++    kmutex_lock (&rail->r_haltop_mutex);
++    
++    rail->r_haltop.op_mask      = INT_DProcHalted;
++    rail->r_haltop.op_function  = ep4_neterr_dma_haltop;
++    rail->r_haltop.op_arg       = &desc;
++
++    rail->r_flushop.op_function = ep4_neterr_dma_flushop;
++    rail->r_flushop.op_arg      = &desc;
++    
++    elan4_queue_haltop (rail->r_ctxt.ctxt_dev, &rail->r_haltop);
++
++    spin_lock_irqsave (&rail->r_haltop_lock, flags);
++    while (! desc.done)
++      kcondvar_wait (&rail->r_haltop_sleep, &rail->r_haltop_lock, &flags);
++    spin_unlock_irqrestore (&rail->r_haltop_lock, flags);
++    kmutex_unlock (&rail->r_haltop_mutex);
++
++    /* Fourth - run down the dma retry lists and move all entries to the cancelled
++     *          list.  Any dma's which were on the run queues have already been
++     *          moved there */
++    spin_lock_irqsave (&rail->r_dma_lock, flags);
++    for (i = EP_RETRY_BASE; i < EP_NUM_RETRIES; i++)
++    {
++      list_for_each_safe (el,nel, &rail->r_dma_retrylist[i]) {
++          EP4_DMA_RETRY *retry    = list_entry (el, EP4_DMA_RETRY, retry_link);
++          
++          if (dma_matches_cookie (rail, retry->retry_dma.dma_vproc, retry->retry_dma.dma_cookie, nodeId, cookies, "retry"))
++          {
++              /* remove from retry list */
++              list_del (&retry->retry_link);
++              
++              /* add to list of dmas which matched */
++              list_add_tail (&retry->retry_link, &matchedList);
++          }
++      }
++    }
++    
++    list_for_each_safe (el, nel, &nodeRail->StalledDmas) {
++      EP4_DMA_RETRY *retry = list_entry (el, EP4_DMA_RETRY, retry_link);
++      
++      if (dma_matches_cookie (rail, retry->retry_dma.dma_vproc, retry->retry_dma.dma_cookie, nodeId, cookies, "stalled"))
++      {
++          /* remove from retry list */
++          list_del (&retry->retry_link);
++          
++          /* add to list of dmas which matched */
++          list_add_tail (&retry->retry_link, &matchedList);
++      }
++    }
++    spin_unlock_irqrestore (&rail->r_dma_lock, flags);
++    
++    /* Now "set" the source event of any put DMA#'s we can use the dma 
++     * retry command queue as the retry thread is stalled */
++    while (! list_empty (&matchedList))
++    {
++      EP4_DMA_RETRY *retry = list_entry (matchedList.next, EP4_DMA_RETRY, retry_link);
++      
++      list_del (&retry->retry_link);
++
++      elan4_set_event_cmd (rail->r_dma_ecq->ecq_cq, retry->retry_dma.dma_srcEvent);
++
++      spin_lock_irqsave (&rail->r_dma_lock, flags);
++      list_add (&retry->retry_link, &rail->r_dma_freelist);
++      spin_unlock_irqrestore (&rail->r_dma_lock, flags);
++    }
++
++    /* Flush through the command queues to ensure that all the setevents have executed */
++    ep4_flush_ecqs (rail);
++
++    /* Finally - allow the retry thread to run again */
++    ep_kthread_resume (&rail->r_retry_thread);
++}
++
++void
++ep4_add_neterr_ops (EP4_RAIL *rail, EP4_NETERR_OPS *ops)
++{
++    /* we're called from the ManagerThread, so no need to stall it */
++    list_add_tail (&ops->op_link, &rail->r_neterr_ops);
++}
++void
++ep4_remove_neterr_ops (EP4_RAIL *rail, EP4_NETERR_OPS *ops)
++{
++    EP_SYS *sys = rail->r_generic.System;
++
++    ep_kthread_stall (&sys->ManagerThread);
++    list_del (&ops->op_link);
++    ep_kthread_resume (&sys->ManagerThread);
++}
++
++void
++ep4_neterr_fixup_sten (EP4_RAIL *rail, unsigned int nodeId, EP_NETERR_COOKIE *cookies)
++{
++    struct list_head *el;
++
++    list_for_each (el, &rail->r_neterr_ops) {
++      EP4_NETERR_OPS *op = list_entry (el, EP4_NETERR_OPS, op_link);
++
++      (op->op_func) (rail, op->op_arg, nodeId, cookies);
++    }
++}
++
++void
++ep4_neterr_fixup (EP_RAIL *r, unsigned int nodeId, EP_NETERR_COOKIE *cookies)
++{
++    EP4_RAIL *rail = (EP4_RAIL *) r;
++
++    /* network error cookies can come from the following :
++     *
++     *   DMA  engine
++     *     if a DMA matches a network error cookie, then we just need to 
++     *     execute the local setevent *before* returning.
++     *
++     *   STEN packet
++     *     if the STEN packet was generated with as a WAIT_FOR_EOP
++     *     and it's not present on the retry lists, then re-create
++     *     it.
++     *
++     */
++    EPRINTF4 (DBG_NETWORK_ERROR, "%s: ep4_neterr_fixup: node %d cookies <%lld%s%s%s%s> <%lld%s%s%s%s>\n",
++            rail->r_generic.Name, nodeId, EP4_COOKIE_STRING(cookies[0]), EP4_COOKIE_STRING(cookies[1]));
++
++    if ((cookies[0] & EP4_COOKIE_DMA) || (cookies[1] & EP4_COOKIE_DMA))
++      ep4_neterr_fixup_dmas (rail, nodeId, cookies);
++
++    if ((cookies[0] & EP4_COOKIE_STEN) || (cookies[1] & EP4_COOKIE_STEN))
++      ep4_neterr_fixup_sten (rail, nodeId, cookies);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++
+Index: linux-2.6.5/drivers/net/qsnet/ep/nmh.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/nmh.c        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/nmh.c     2005-05-11 12:10:12.533918136 -0400
+@@ -0,0 +1,181 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++#ident "@(#)$Id: nmh.c,v 1.6 2004/01/05 13:48:08 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/nmh.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++
++#define EP_NMD_SPANS(nmd, base, top)  ((nmd)->nmd_addr <= (base) &&  \
++                                       ((nmd)->nmd_addr + (nmd)->nmd_len - 1) >= (top))
++
++#define EP_NMD_OVERLAPS(nmd, addr, len)       ((nmd)->nmd_addr <= ((addr) + (len)) && \
++                                       ((nmd)->nmd_addr + (nmd)->nmd_len - 1) >= (addr))
++
++#define EP_NMH_HASH(tbl,idx,addr)     ((addr) % (tbl)->tbl_size[idx])
++
++int
++ep_nmh_init (EP_NMH_TABLE *tbl)
++{
++    int i, idx, hsize = 1;
++
++    for (idx = EP_NMH_NUMHASH-1; idx >= 0; idx--, hsize <<= 1)
++    {
++      tbl->tbl_size[idx] = (hsize < EP_NMH_HASHSIZE) ? hsize : EP_NMH_HASHSIZE;
++
++      KMEM_ZALLOC (tbl->tbl_hash[idx], struct list_head *, sizeof (struct list_head) * tbl->tbl_size[idx], 1);
++      
++      if (tbl->tbl_hash == NULL)
++      {
++          while (++idx < EP_NMH_NUMHASH)
++              KMEM_FREE (tbl->tbl_hash[idx], sizeof (struct list_head) * tbl->tbl_size[idx]);
++          return (ENOMEM);
++      }
++
++      for (i = 0; i < tbl->tbl_size[idx]; i++)
++          INIT_LIST_HEAD (&tbl->tbl_hash[idx][i]);
++    }
++
++    return (0);
++}
++
++void
++ep_nmh_fini (EP_NMH_TABLE *tbl)
++{
++    int idx;
++
++    for (idx = 0; idx < EP_NMH_NUMHASH; idx++)
++      if (tbl->tbl_hash[idx])
++          KMEM_FREE (tbl->tbl_hash[idx], sizeof (struct list_head) * tbl->tbl_size[idx]);
++    
++    bzero (tbl, sizeof (EP_NMH_TABLE));
++}
++
++void
++ep_nmh_insert (EP_NMH_TABLE *tbl, EP_NMH *nmh)
++{
++    EP_ADDR base = nmh->nmh_nmd.nmd_addr;
++    EP_ADDR top  = base + nmh->nmh_nmd.nmd_len - 1;
++    int     idx;
++
++    for (idx = 0, base >>= 12, top >>= 12; base != top && idx < EP_NMH_NUMHASH; idx++, base >>= 1, top >>= 1)
++      ;
++
++    list_add_tail (&nmh->nmh_link, &tbl->tbl_hash[idx][EP_NMH_HASH(tbl, idx, base)]);
++}
++
++void
++ep_nmh_remove (EP_NMH_TABLE *tbl, EP_NMH *nmh)
++{
++    list_del (&nmh->nmh_link);
++}
++
++EP_NMH *
++ep_nmh_find (EP_NMH_TABLE *tbl, EP_NMD *nmd)
++{
++    EP_ADDR           base = nmd->nmd_addr;
++    EP_ADDR           top  = base + nmd->nmd_len - 1;
++    int               idx;
++    struct list_head *le;
++    
++    for (idx = 0, base >>= 12, top >>= 12; base != top && idx < EP_NMH_NUMHASH; idx++, base >>= 1, top >>= 1)
++      ;
++    
++    for (; idx < EP_NMH_NUMHASH; idx++, base >>= 1, top >>= 1) {
++
++      list_for_each (le, &tbl->tbl_hash[idx][EP_NMH_HASH(tbl, idx, base)]) {
++          EP_NMH *nmh = list_entry (le, EP_NMH, nmh_link);
++
++          if (EP_NMD_SPANS (&nmh->nmh_nmd, nmd->nmd_addr, nmd->nmd_addr + nmd->nmd_len - 1))
++              return (nmh);
++      }
++    }
++
++    return (0);
++}
++
++void
++ep_nmd_subset (EP_NMD *subset, EP_NMD *nmd, unsigned off, unsigned len)
++{
++    ASSERT ((off + len - 1) <= nmd->nmd_len);
++
++    subset->nmd_addr = nmd->nmd_addr + off;
++    subset->nmd_len  = len;
++    subset->nmd_attr = nmd->nmd_attr;
++}
++
++int
++ep_nmd_merge (EP_NMD *merged, EP_NMD *a, EP_NMD *b)
++{
++    if (EP_NMD_NODEID (a) != EP_NMD_NODEID (b))                       /* not generated on the same node */
++      return 0;
++    
++    if ((EP_NMD_RAILMASK (a) & EP_NMD_RAILMASK (b)) == 0)     /* no common rails */
++      return 0;
++    
++    if (b->nmd_addr == (a->nmd_addr + a->nmd_len))
++    {
++      if (merged != NULL)
++      {
++          merged->nmd_addr = a->nmd_addr;
++          merged->nmd_len  = a->nmd_len + b->nmd_len;
++          merged->nmd_attr = EP_NMD_ATTR(EP_NMD_NODEID(a), EP_NMD_RAILMASK(a) & EP_NMD_RAILMASK(b));
++      }
++      return 1;
++    }
++    
++    if (a->nmd_addr == (b->nmd_addr + b->nmd_len))
++    {
++      if (merged != NULL)
++      {
++          merged->nmd_addr = b->nmd_addr;
++          merged->nmd_len  = b->nmd_len + a->nmd_len;
++          merged->nmd_attr = EP_NMD_ATTR(EP_NMD_NODEID(b), EP_NMD_RAILMASK(a) & EP_NMD_RAILMASK(b));
++      }
++      
++      return 1;
++    }
++
++    return 0;
++}
++
++int
++ep_nmd_map_rails (EP_SYS *sys, EP_NMD *nmd, unsigned railmask)
++{
++    EP_NMH *nmh = ep_nmh_find (&sys->MappingTable, nmd);
++
++    if (nmh == NULL)
++    {
++      printk ("ep_nmd_map_rails: nmd=%08x.%08x.%08x cannot be found\n",
++              nmd->nmd_addr, nmd->nmd_len, nmd->nmd_attr);
++      return (-1);
++    }
++
++    return (nmh->nmh_ops->op_map_rails (sys, nmh, nmd, railmask));
++}
++
++EP_RAILMASK
++ep_nmd2railmask (EP_NMD *frags, int nFrags)
++{
++    EP_RAILMASK mask;
++
++    if (nFrags == 0)
++      return ((EP_RAILMASK)-1);
++    
++    for (mask = EP_NMD_RAILMASK(frags); --nFrags; )
++      mask &= EP_NMD_RAILMASK(++frags);
++
++    return (mask);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/ep/probenetwork.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/probenetwork.c       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/probenetwork.c    2005-05-11 12:10:12.534917984 -0400
+@@ -0,0 +1,446 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: probenetwork.c,v 1.43 2004/04/19 15:43:15 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/probenetwork.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++#include "debug.h"
++
++int PositionCheck = 1;
++
++#define NUM_DOWN_FROM_VAL(NumDownLinksVal, level)     (((NumDownLinksVal) >> ((level) << 2)) & 0xF)
++
++int
++ProbeNetwork (EP_RAIL *rail, ELAN_POSITION *pos)
++{
++    int               lvl, i;
++    int               level;
++    int               nodeid;
++    int               numnodes;
++    int                     randomRoutingDisabled;
++    int               sw;
++    int               nacks;
++    int               nowayup;
++    int                     nalias;
++    int                     upmask;
++    int                     partial;
++    int                     link;
++    int                     invalid;
++    int                     linkdown[ELAN_MAX_LEVELS];
++    int                     linkup[ELAN_MAX_LEVELS];
++    EP_SWITCH        *switches[ELAN_MAX_LEVELS];
++    int               switchCount[ELAN_MAX_LEVELS+1];
++    int               lowestBcast;
++    int               numUpLinks[ELAN_MAX_LEVELS];
++    int               routedown [ELAN_MAX_LEVELS];
++
++    EPRINTF1 (DBG_PROBE, "%s: ProbeNetwork started\n", rail->Name);
++
++    switchCount[0] = 1;
++    numUpLinks [0] = 4;
++
++    for (level = 0; level < ELAN_MAX_LEVELS; level++)
++    {
++      int ndown  = NUM_DOWN_FROM_VAL (rail->Devinfo.dev_num_down_links_value, level);
++
++      KMEM_ZALLOC (switches[level], EP_SWITCH *, sizeof (EP_SWITCH) * switchCount[level], 1);
++
++      for (sw = 0, nacks = 0, nowayup = 0, lowestBcast=7; sw < switchCount[level]; sw++)
++      {
++          EP_SWITCH *lsw  = &switches[level][sw];
++          int        good = 1;
++          int        tsw;
++
++          for (nodeid = 0,tsw = sw, lvl = level-1 ; lvl >= 0 ; lvl--)
++          {
++              EP_SWITCH *lsw;
++              int        link = (8-numUpLinks[lvl]) + (tsw % numUpLinks[lvl]);
++
++              tsw  = tsw / numUpLinks[lvl];
++              lsw  = &switches[lvl][tsw];
++
++              if (lsw->present == 0 || (lsw->lnr & (1 << link)))
++              {
++                  EPRINTF4 (DBG_PROBE, "lvl %d sw %d present=%d lnr=%x\n", lvl, sw, lsw->present, lsw->lnr);
++                  good = 0;
++              }
++              
++              linkup[lvl]   = link;
++              linkdown[lvl] = lsw->link;
++
++              if ( lvl ) nodeid = ((nodeid + linkdown[lvl]) * (8-numUpLinks[lvl-1]));
++              else       nodeid += linkdown[0];
++
++          }
++          
++          /* 
++           * don't bother probing routes which we we've already seen are unreachable 
++           * because a link upwards was in reset or the switch previously nacked us.
++           */
++          if (! good)
++          {
++              lsw->present = 0;
++
++              nacks++;
++              nowayup++;
++
++              continue;
++          }
++
++          lsw->present = rail->Operations.ProbeRoute (rail, level, sw, nodeid, linkup, linkdown, 5, lsw);
++
++          if (! lsw->present)
++          {
++              EPRINTF3 (DBG_PROBE, "%s: level %d switch %d - unexpected nack\n", rail->Name, level, sw);
++
++              nacks++;
++              nowayup++;
++          }
++          else
++          {
++              EPRINTF5 (DBG_PROBE, "%s: level %d switch %d - link %d bcast %d\n", rail->Name, level, sw, lsw->link, lsw->bcast);
++
++              if (level == 2 && rail->Devinfo.dev_device_id == PCI_DEVICE_ID_ELAN3)
++              {
++                  /* If we see broadcast top as 7, and we came in on a low link, then we can't
++                   * determine whether we're in a 128 way or a un-configured 64u64d switch, so
++                   * we treat it as a 64u64d and detect the 128 way case by "going over the top" 
++                   * below. Unless we've been told what it really is by NumDownLinksVal.
++                   */
++                  if (lsw->bcast == 7 && lsw->link < 4)
++                      lsw->bcast = ndown ? (ndown - 1) : 3;
++              }
++
++              if ( lowestBcast > lsw->bcast ) 
++                  lowestBcast = lsw->bcast;
++
++              if (lsw->link > (ndown ? (ndown-1) : (lowestBcast == 7 ? 3 : lowestBcast)))
++              {
++                  /* We've arrived on a "up-link" - this could be either
++                   * we're in the top half of a x8 top-switch - or we're
++                   * in the bottom half and have gone "over the top". We
++                   * differentiate these cases since the switches below
++                   * a x8 top-switch will have broadcast top set to 3, 
++                   * and the x8 topswitch have broadcast top set to 7.
++                   */
++                  if (lsw->bcast == 7)
++                      nowayup++;
++                  else
++                  {
++                      EPRINTF2 (DBG_PROBE, "%s: level %d - gone over the top\n",
++                                rail->Name, level);
++
++                      if (level > 0)
++                      {
++                          KMEM_FREE (switches[level], sizeof (EP_SWITCH) * switchCount[level] );
++                          level--;
++                      }
++                      
++                      numUpLinks[level] = 0;
++                      goto finished;
++                  }
++              }
++
++          }
++      }
++
++      numUpLinks[level]    = ndown ? (8 - ndown) : (7 - lowestBcast);
++      switchCount[level+1] = switchCount[level] *  numUpLinks[level];
++      
++      /* Now we know which links are uplinks, we can see whether there is
++       * any possible ways up */
++      upmask = (ndown ? (0xFF << ndown) & 0xFF : (0xFF << (8 - numUpLinks[level])) & 0xFF);
++
++      for (sw = 0; sw < switchCount[level]; sw++)
++      {
++          EP_SWITCH *lsw  = &switches[level][sw];
++
++          if (lsw->present && lsw->link <= (ndown ? (ndown-1) : (lowestBcast == 7 ? 3 : lowestBcast)) && (switches[level][sw].lnr & upmask) == upmask)
++              nowayup++;
++      }
++
++      EPRINTF7 (DBG_PROBE, "%s: level %d - sw=%d nacks=%d nowayup=%d bcast=%d numup=%d\n", 
++                rail->Name, level, sw, nacks, nowayup, lowestBcast, numUpLinks[level]);
++
++      if (nacks == sw)
++      {
++          static bitmap_t printed[BT_BITOUL(EP_MAX_RAILS)];
++
++          if (! BT_TEST (printed, rail->Number))
++              printk ("%s: cannot determine network position\n", rail->Name);
++          BT_SET (printed, rail->Number);
++          goto failed;
++      }
++
++      if (nowayup == sw)
++          goto finished;
++    }
++    
++    printk ("%s: exceeded number of levels\n", rail->Name);
++    level = ELAN_MAX_LEVELS - 1;
++
++ failed:
++    
++    for (lvl = 0; lvl <= level; lvl++)
++      KMEM_FREE (switches[lvl], sizeof (EP_SWITCH) * switchCount[lvl] );
++
++    return -EAGAIN;
++
++ finished:
++    /* we've successfully probed the network - now calculate our node 
++     * positon and what level of random routing is possible */
++    nalias = 1;
++    for (lvl = 0, invalid = 0, partial = 0, randomRoutingDisabled = 0; lvl <= level; lvl++)
++    {
++      int ndown  = NUM_DOWN_FROM_VAL (rail->Devinfo.dev_num_down_links_value, lvl);
++      int upmask = ndown ? (0xFF << ndown) & 0xFF : 0xF0;
++
++      for (sw = 0, nalias = 0; sw < switchCount[lvl]; sw++)
++      {
++          EP_SWITCH *lsw = &switches[lvl][sw];
++          
++          /* You can only use adaptive routing if links 4-7 are uplinks, and at least one of them is
++           * not in reset.   Otherwise you can randomly select an "uplink" if all the uplinks are not
++           * in reset. */
++          if (lsw->present && ((upmask == 0xF0) ? (lsw->lnr & upmask) == upmask : (lsw->lnr & upmask) != 0))
++              randomRoutingDisabled |= (1 << lvl);
++          
++          if (!lsw->present)
++              partial++;
++          else
++          {
++              if (lsw->invalid)
++              {
++                  printk ("%s: invalid switch detected (level %d switch %d)\n", rail->Name, lvl, sw);
++                  invalid++;
++              }
++              
++              for (i = 0; i < nalias; i++)
++                  if (linkdown[i] == lsw->link)
++                      break;
++              if (i == nalias)
++                  linkdown[nalias++] = lsw->link;
++          }
++      }
++      
++      link = linkdown[0];
++      for (i = 1; i < nalias; i++)
++          if (linkdown[i] < link)
++              link = linkdown[i];
++
++      if (nalias > 1 && lvl != level)
++      {
++          printk ("%s: switch aliased below top level (level %d)\n", rail->Name, lvl);
++          invalid++;
++      }
++      
++      routedown[lvl] = link;
++   }
++
++    for (lvl = 0; lvl <= level; lvl++) 
++      KMEM_FREE (switches[lvl], sizeof (EP_SWITCH) * switchCount[lvl] );
++
++    if (invalid)
++    {
++      printk ("%s: invalid switch configuration\n", rail->Name);
++      return (EINVAL);
++    }
++
++    /* Handle the aliasing case where a 16 way is used as multiple smaller switches */
++    if (nalias == 1)
++      level++;
++    else if (nalias == 2)                                     /* a 16 way as 2x8 ways */
++      numUpLinks[level++] = 6;                                /*   only 2 down links */
++    else if (nalias > 4)                                      /* a 16 way as 8x2 ways */
++      numUpLinks[level-1] = 6;
++    
++    /* 
++     * Compute my nodeid and number of nodes in the machine
++     * from the routedown and the number of downlinks at each level.
++     */
++    for(nodeid=0, lvl = level - 1; lvl >= 0; lvl--)
++    {
++      if (lvl) nodeid = ((nodeid + routedown[lvl]) * (8-numUpLinks[lvl-1]));  
++      else     nodeid += routedown[0];
++    }
++
++    for (numnodes = 1, lvl = 0; lvl < level; lvl++)
++      numnodes *= (8 - numUpLinks[lvl]);
++
++    sprintf (rail->Name, "ep%d[%d]", rail->Number, nodeid);
++
++    if (randomRoutingDisabled & ((1 << (level-1))-1))
++      printk ("%s: nodeid=%d level=%d numnodes=%d (random routing disabled 0x%x)\n", 
++              rail->Name, nodeid, level, numnodes, randomRoutingDisabled);
++    else if (partial)
++      printk ("%s: nodeid=%d level=%d numnodes=%d (random routing ok)\n",
++              rail->Name, nodeid, level, numnodes);
++    else
++      printk ("%s: nodeid=%d level=%d numnodes=%d\n",
++              rail->Name, nodeid, level, numnodes);
++
++    pos->pos_mode              = ELAN_POS_MODE_SWITCHED;
++    pos->pos_nodeid              = nodeid;
++    pos->pos_levels              = level;
++    pos->pos_nodes               = numnodes;
++    pos->pos_random_disabled     = randomRoutingDisabled;
++
++    for(lvl = 0; lvl < level; lvl++)
++      pos->pos_arity[level -lvl - 1] = (8-numUpLinks[lvl]);
++    pos->pos_arity[level] = 1;                                /* XXXX why does this need to be 1 ? */
++    
++    return 0;
++}
++
++/*
++ * broadcast top is invalid if it is not set to the number of downlinks-1,
++ * or at the topmost level it is less than ndown-1.
++ */
++#define BCAST_TOP_INVALID(lvl, bcast, ndown)  ((lvl) == 0 ? (bcast) < ((ndown)-1) : (bcast) != ((ndown) - 1))
++
++void
++CheckPosition (EP_RAIL *rail)
++{
++    ELAN_POSITION *pos     = &rail->Position;
++    unsigned int   nodeid  = pos->pos_nodeid;
++    unsigned int   invalid = 0;
++    unsigned int   changed = 0;
++    int lvl, slvl;
++
++    if (! PositionCheck)
++      return;
++
++    if (rail->Operations.CheckPosition(rail))         /* is update ready for this rail */
++    {
++      EPRINTF2 (DBG_ROUTETABLE, "%s: check position: SwitchProbeLevel=%d\n", rail->Name, rail->SwitchProbeLevel);
++
++      for (lvl = 0, slvl = pos->pos_levels-1; lvl <= rail->SwitchProbeLevel; lvl++, slvl--)
++      {
++          EP_SWITCHSTATE *state  = &rail->SwitchState[lvl];
++          EP_SWITCHSTATE *lstate = &rail->SwitchLast[lvl];
++          unsigned int    ndown  = pos->pos_arity[slvl];
++          unsigned int    upmask = (0xFF << ndown) & 0xFF;
++          unsigned int    mylink = nodeid % ndown;
++          unsigned int    error  = 0;
++          unsigned int    binval = 0;
++
++          nodeid /= ndown;
++
++          /*
++           * broadcast top is invalid if it is not set to the number of downlinks-1,
++           * or at the topmost level it is less than ndown-1.
++           */
++          if (BCAST_TOP_INVALID(lvl, state->bcast, ndown) || (state->LNR & upmask) == upmask)
++          {
++              /* no way up from here - we'd better be at the top */
++              if (lvl != (pos->pos_levels-1))
++              {
++                  if (state->bcast != (ndown-1))
++                      printk ("%s: invalid broadcast top %d at level %d\n", rail->Name, state->bcast, lvl);
++                  else if ((state->LNR & upmask) == upmask && (lstate->LNR & upmask) == upmask)
++                      printk ("%s: no way up to switch at level %d (turned off ?)\n", rail->Name, lvl+1);
++              }
++              else
++              {
++                  if (state->linkid != mylink)
++                      printk ("%s: moved at top level was connected to link %d now connected to %d\n", rail->Name, mylink, state->linkid);
++              }
++
++              if (state->linkid != mylink)
++                  error++;
++              
++              if (BCAST_TOP_INVALID (lvl, state->bcast, ndown))
++                  binval++;
++          }
++          else
++          {
++              if (state->linkid != mylink)
++              {
++                  if (state->linkid != rail->SwitchLast[lvl].linkid)
++                      printk ("%s: moved at lvl %d was connected to link %d now connected to %d\n", rail->Name, lvl, mylink, state->linkid);
++                      
++                  error++;
++              }
++          }
++
++          if (error == 0 && invalid == 0)
++              rail->SwitchProbeTick[lvl] = lbolt;
++          
++          EPRINTF10 (DBG_ROUTETABLE, "%s:   lvl=%d (slvl=%d) linkid=%d bcast=%d lnr=%02x uplink=%d : error=%d binval=%d invalid=%d\n", 
++                     rail->Name, lvl, slvl, state->linkid, state->bcast, state->LNR, state->uplink, error, binval, invalid);
++
++          invalid |= (error | binval);
++      }
++      
++      for (lvl = 0; lvl < rail->SwitchProbeLevel; lvl++)
++          if (rail->SwitchState[lvl].uplink != rail->SwitchLast[lvl].uplink)
++              changed++;
++
++      if (changed)
++      {
++          printk ("%s: broadcast tree has changed from", rail->Name);
++          for (lvl = 0; lvl < rail->SwitchProbeLevel; lvl++)
++              printk ("%c%d", lvl == 0 ? ' ' : ',', rail->SwitchLast[lvl].uplink);
++
++          for (lvl = 0; lvl < rail->SwitchProbeLevel; lvl++)
++              printk ("%s%d", lvl == 0 ? " to " : ",", rail->SwitchState[lvl].uplink);
++          printk ("\n");
++      }
++
++      if (rail->SwitchProbeLevel > 0)
++          bcopy (rail->SwitchState, rail->SwitchLast, rail->SwitchProbeLevel * sizeof (EP_SWITCHSTATE));
++    }
++
++    for (lvl = 0; lvl < pos->pos_levels; lvl++)
++    {
++      EPRINTF4 (DBG_ROUTETABLE, "%s: level %d lbolt=%lx ProbeLevelTick=%lx\n",
++                rail->Name, lvl, lbolt, rail->SwitchProbeTick[lvl]);
++      
++      if (AFTER (lbolt, rail->SwitchProbeTick[lvl] + EP_POSITION_TIMEOUT))
++      {
++          if (lvl < rail->SwitchBroadcastLevel+1)
++          {
++              if (lvl == 0)
++                  printk ("%s: cable disconnected\n", rail->Name);
++              else
++                  printk ("%s: broadcast level has dropped to %d (should be %d)\n",
++                          rail->Name, lvl, rail->Position.pos_levels);
++          }
++          break;
++      }
++    }
++    
++    if (lvl > rail->SwitchBroadcastLevel+1)
++    {
++      if (rail->SwitchBroadcastLevel < 0)
++          printk ("%s: cable reconnected\n", rail->Name);
++      if (lvl == rail->Position.pos_levels)
++          printk ("%s: broadcast level has recovered\n", rail->Name);
++      else
++          printk ("%s: broadcast level has recovered to %d (should be %d)\n", 
++                  rail->Name, lvl, rail->Position.pos_levels);
++    }
++    
++    if (rail->SwitchBroadcastLevel != (lvl - 1))
++    {
++      EPRINTF2 (DBG_ROUTETABLE, "%s: setting SwitchBroadcastLevel to %d\n", rail->Name, lvl-1);
++      
++      rail->SwitchBroadcastLevel     = lvl - 1;
++      rail->SwitchBroadcastLevelTick = lbolt;
++    }
++}
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/ep/probenetwork_elan3.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/probenetwork_elan3.c 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/probenetwork_elan3.c      2005-05-11 12:10:12.535917832 -0400
+@@ -0,0 +1,298 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: probenetwork_elan3.c,v 1.40 2004/04/15 12:30:08 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/probenetwork_elan3.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++
++#include "kcomm_vp.h"
++#include "kcomm_elan3.h"
++#include "debug.h"
++
++#include <elan3/intrinsics.h>
++
++static void ep3_probe_event (EP3_RAIL *rail, void *arg);
++static EP3_COOKIE_OPS ep3_probe_ops = 
++{
++    ep3_probe_event
++} ;
++
++int
++ep3_init_probenetwork (EP3_RAIL *rail)
++{
++    sdramaddr_t             stack;
++    E3_Addr           sp;
++    E3_BlockCopyEvent event;
++    int               i;
++
++    if (! (stack = ep_alloc_elan (&rail->Generic, EP3_STACK_SIZE, 0, &rail->ProbeStack)))
++      return -ENOMEM;
++
++    spin_lock_init (&rail->ProbeLock);
++    kcondvar_init (&rail->ProbeWait);
++
++    /* Initialise the probe command structure */
++    for (i = 0; i < TR_TRACEROUTE_ENTRIES; i++)
++      elan3_sdram_writew (rail->Device, rail->RailElan + offsetof (EP3_RAIL_ELAN, ProbeSource0[i]), 0);
++    for (i = 0; i < TR_TRACEROUTE_ENTRIES; i++)
++      elan3_sdram_writew (rail->Device, rail->RailElan + offsetof (EP3_RAIL_ELAN, ProbeSource1[i]), 1);
++    
++    RegisterCookie (&rail->CookieTable, &rail->ProbeCookie, rail->RailElanAddr + offsetof (EP3_RAIL_ELAN, ProbeDone), &ep3_probe_ops, rail);
++    
++    elan3_sdram_writel (rail->Device, rail->RailElan + offsetof (EP3_RAIL_ELAN, ProbeStart.ev_Type), 0);
++    elan3_sdram_writel (rail->Device, rail->RailElan + offsetof (EP3_RAIL_ELAN, ProbeStart.ev_Count), 0);
++
++    EP3_INIT_COPY_EVENT (event, rail->ProbeCookie, rail->RailMainAddr + offsetof (EP3_RAIL_MAIN, ProbeDone), 1);
++    elan3_sdram_copyl_to_sdram (rail->Device, &event, rail->RailElan + offsetof (EP3_RAIL_ELAN, ProbeDone), sizeof (E3_BlockCopyEvent));
++
++    rail->RailMain->ProbeDone = EP3_EVENT_FREE;
++
++    sp = ep3_init_thread (rail->Device, ep_symbol (&rail->ThreadCode, "kcomm_probe"),
++                        rail->ProbeStack, stack, EP3_STACK_SIZE,
++                        3, rail->CommandPortAddr, rail->RailElanAddr, rail->RailMainAddr);
++    
++    IssueRunThread (rail, sp);
++
++    return 0;
++}
++
++void
++ep3_destroy_probenetwork (EP3_RAIL *rail)
++{
++    if (rail->ProbeStack == (sdramaddr_t) 0)
++      return;
++
++    /* XXXX: ensure that the network probe thread is stopped */
++
++    DeregisterCookie (&rail->CookieTable, &rail->ProbeCookie);
++
++    kcondvar_destroy (&rail->ProbeWait);
++    spin_lock_destroy (&rail->ProbeLock);
++    
++    ep_free_elan (&rail->Generic, rail->ProbeStack, EP3_STACK_SIZE);
++}
++
++static void
++ep3_probe_event (EP3_RAIL *rail, void *arg)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&rail->ProbeLock, flags);
++    rail->ProbeDone = 1;
++    kcondvar_wakeupone (&rail->ProbeWait, &rail->ProbeLock);
++    spin_unlock_irqrestore (&rail->ProbeLock, flags);
++}
++
++int
++ep3_probe_route (EP_RAIL *r, int level, int sw, int nodeid, int *linkup, int *linkdown, int attempts, EP_SWITCH *lsw)
++{
++    EP3_RAIL      *rail     = (EP3_RAIL *) r;
++    EP3_RAIL_MAIN *railMain = rail->RailMain;
++    sdramaddr_t    railElan = rail->RailElan;
++    E3_uint16      flits[MAX_FLITS];
++    E3_uint32      result;
++    int                  nflits;
++    unsigned long  flags;
++
++    spin_lock_irqsave (&rail->ProbeLock, flags);
++
++    nflits = GenerateProbeRoute ( flits, nodeid, level, linkup, linkdown, 0);
++          
++    if (LoadRoute (rail->Device, rail->RouteTable, EP_VP_PROBE(level), ELAN3_MRF_CONTEXT_NUM|SYS_CONTEXT_BIT, nflits, flits) != 0)
++    {
++      EPRINTF0 (DBG_ROUTETABLE, "ProbeRoute: cannot load route entry\n");
++      spin_unlock_irqrestore (&rail->ProbeLock, flags);
++      return (EINVAL);
++    }
++
++    do {
++      /* Initialise the probe source to include our partially computed nodeid */
++      elan3_sdram_writew (rail->Device, railElan + offsetof (EP3_RAIL_ELAN, ProbeSource0[TR_TRACEROUTE_ENTRIES-1]), nodeid);
++      elan3_sdram_writew (rail->Device, railElan + offsetof (EP3_RAIL_ELAN, ProbeSource1[TR_TRACEROUTE_ENTRIES-1]), nodeid);
++
++      /* Initialise the count result etc */
++      elan3_sdram_writel (rail->Device, railElan + offsetof (EP3_RAIL_ELAN, ProbeType), PROBE_SINGLE);
++      elan3_sdram_writel (rail->Device, railElan + offsetof (EP3_RAIL_ELAN, ProbeLevel), level);
++
++      railMain->ProbeResult  = -1;
++          
++      /* Clear the receive area */
++      bzero (railMain->ProbeDest0, sizeof (railMain->ProbeDest0));
++      bzero (railMain->ProbeDest1, sizeof (railMain->ProbeDest1));
++    
++      /* Re-arm the completion event */
++      elan3_sdram_writel (rail->Device, railElan + offsetof (EP3_RAIL_ELAN, ProbeDone.ev_Count), 1);
++      railMain->ProbeDone = EP3_EVENT_ACTIVE;
++      rail->ProbeDone = 0;
++
++      /* And wakeup the thread to do the probe */
++      IssueSetevent (rail, rail->RailElanAddr + offsetof (EP3_RAIL_ELAN, ProbeStart));
++
++      /* Now wait for it to complete */
++      while (! rail->ProbeDone)
++          kcondvar_wait (&rail->ProbeWait, &rail->ProbeLock, &flags);
++
++      /* wait for block copy event to flush write buffers */
++      while (! EP3_EVENT_FIRED (rail->ProbeCookie, railMain->ProbeDone))
++          if (! EP3_EVENT_FIRING(rail->Device, railElan + offsetof (EP3_RAIL_ELAN, ProbeDone), rail->ProbeCookie, railMain->ProbeDone))
++              panic ("ProbeRoute: network probe event failure\n");
++
++      result = railMain->ProbeResult;
++
++      if (result == C_ACK_ERROR)
++          kcondvar_timedwait (&rail->ProbeWait, &rail->ProbeLock, &flags, lbolt + (hz/8));
++      
++      railMain->ProbeDone = EP3_EVENT_FREE;
++
++    } while (result != C_ACK_OK && --attempts);
++
++    if (result == C_ACK_OK)
++    {
++      if (railMain->ProbeDest0[TR_TRACEROUTE_ENTRIES - ((2*level)+1) - 1] != nodeid ||
++          railMain->ProbeDest1[TR_TRACEROUTE_ENTRIES - ((2*level)+1) - 1] != nodeid)
++      {
++          printk ("%s: lost nodeid at level %d switch %d - %d != %d\n", rail->Generic.Name, level, sw,
++                  railMain->ProbeDest0[TR_TRACEROUTE_ENTRIES - ((2*level)+1) - 1], nodeid);
++
++          result = C_ACK_ERROR;
++      }
++      else
++      {
++          E3_uint16 val0 = railMain->ProbeDest0[TR_TRACEROUTE_ENTRIES - level - 1];
++          E3_uint16 val1 = railMain->ProbeDest1[TR_TRACEROUTE_ENTRIES - level - 1];
++              
++          EPRINTF7 (DBG_PROBE, "%s: level %d switch %d - linkid=%d bcast=%d LNR=%02x%s\n", 
++                    rail->Generic.Name, level, sw, TR_TRACEROUTE0_LINKID(val0),
++                    TR_TRACEROUTE1_BCAST_TOP(val1), TR_TRACEROUTE0_LNR(val0),
++                    TR_TRACEROUTE0_REVID(val0) ? "" : " RevA Part");
++          
++          lsw->lnr     = TR_TRACEROUTE0_LNR(val0);
++          lsw->link    = TR_TRACEROUTE0_LINKID(val0);
++          lsw->bcast   = TR_TRACEROUTE1_BCAST_TOP(val1);
++          lsw->invalid = (TR_TRACEROUTE0_REVID(val0) == 0);
++      }
++    }
++    spin_unlock_irqrestore (&rail->ProbeLock, flags);
++    
++    return (result == C_ACK_OK);
++}
++
++void
++ep3_probe_position_found (EP3_RAIL *rail, ELAN_POSITION *pos)
++{
++    E3_uint16  flits[MAX_FLITS];
++    int        lvl, nflits;
++    
++    for (lvl = 0; lvl < pos->pos_levels; lvl++)
++    {
++      nflits = GenerateCheckRoute (pos, flits, pos->pos_levels - lvl - 1, 0);
++
++      if (LoadRoute (rail->Device, rail->Ctxt->RouteTable, EP_VP_PROBE(lvl), ELAN3_MRF_CONTEXT_NUM|SYS_CONTEXT_BIT, nflits, flits) != 0)
++          panic ("ep3_probe_position_found: cannot load probe route entry\n");
++    }
++    
++    /* Initialise the traceroute source data with our nodeid */
++    elan3_sdram_writew (rail->Device, rail->RailElan + offsetof (EP3_RAIL_ELAN, ProbeSource0[TR_TRACEROUTE_ENTRIES-1]), pos->pos_nodeid);
++    elan3_sdram_writew (rail->Device, rail->RailElan + offsetof (EP3_RAIL_ELAN, ProbeSource1[TR_TRACEROUTE_ENTRIES-1]), pos->pos_nodeid);
++}
++
++int
++ep3_check_position (EP_RAIL *r)
++{
++    EP3_RAIL      *rail     = (EP3_RAIL *) r;
++    EP3_RAIL_MAIN *railMain = rail->RailMain;
++    sdramaddr_t    railElan = rail->RailElan;
++    ELAN_POSITION *pos      = &rail->Generic.Position;
++    unsigned int   level    = rail->RailMain->ProbeLevel;
++    unsigned int   updated  = EP3_EVENT_FIRED (rail->ProbeCookie, railMain->ProbeDone);
++    unsigned int   lvl;
++
++    if (updated)
++    {
++      if (railMain->ProbeResult != C_ACK_OK)
++      {
++          EPRINTF2 (DBG_PROBE, "%s: CheckNetworkPosition: packet nacked result=%d\n", rail->Generic.Name, railMain->ProbeResult); 
++          
++          rail->Generic.SwitchProbeLevel = -1;
++      }
++      else
++      {
++          E3_uint16 val0 = railMain->ProbeDest0[TR_TRACEROUTE_ENTRIES - 2*(level+1)];
++          E3_uint16 val1 = railMain->ProbeDest1[TR_TRACEROUTE_ENTRIES - 2*(level+1)];
++
++          if (val0 != pos->pos_nodeid || val1 != pos->pos_nodeid)
++          {
++              static unsigned long printed = 0;
++
++              /* We've received a packet from another node - this probably means
++               * that we've moved */
++              if ((lbolt - printed) > (HZ*10))
++              {
++                  printk ("%s: ep3_check_position - level %d lost nodeid\n", rail->Generic.Name, level);
++                  printed = lbolt;
++              }
++
++              rail->Generic.SwitchProbeLevel = -1;
++          }
++          else
++          {
++              for (lvl = 0; lvl <= level; lvl++)
++              {
++                  E3_uint16 val0 = railMain->ProbeDest0[TR_TRACEROUTE_ENTRIES - ((2*level) - lvl + 1)];
++                  E3_uint16 val1 = railMain->ProbeDest1[TR_TRACEROUTE_ENTRIES - ((2*level) - lvl + 1)];
++
++                  rail->Generic.SwitchState[lvl].linkid = TR_TRACEROUTE0_LINKID(val0);
++                  rail->Generic.SwitchState[lvl].LNR    = TR_TRACEROUTE0_LNR(val0);
++                  rail->Generic.SwitchState[lvl].bcast  = TR_TRACEROUTE1_BCAST_TOP(val1);
++                  rail->Generic.SwitchState[lvl].uplink = 4;
++
++                  EPRINTF5 (DBG_PROBE, " --- lvl %d: linkid=%d LNR=%x bcast=%d uplink=%d\n", lvl, rail->Generic.SwitchState[lvl].linkid,
++                            rail->Generic.SwitchState[lvl].LNR, rail->Generic.SwitchState[lvl].bcast ,rail->Generic.SwitchState[lvl].uplink);
++              }
++              rail->Generic.SwitchProbeLevel = level;
++          }
++      }
++
++      railMain->ProbeDone = EP3_EVENT_FREE;
++    }
++
++    if (railMain->ProbeDone == EP3_EVENT_FREE)
++    {
++      if (rail->Generic.SwitchBroadcastLevel == rail->Generic.Position.pos_levels-1)
++          level = rail->Generic.Position.pos_levels - 1;
++      else
++          level = rail->Generic.SwitchBroadcastLevel + 1;
++
++      EPRINTF2 (DBG_PROBE, "%s: ep3_check_postiion: level %d\n", rail->Generic.Name, level);
++
++      /* Initialise the count result etc */
++      elan3_sdram_writel (rail->Device, railElan + offsetof (EP3_RAIL_ELAN, ProbeType), PROBE_MULTIPLE);
++      elan3_sdram_writel (rail->Device, railElan + offsetof (EP3_RAIL_ELAN, ProbeLevel), level);
++
++      railMain->ProbeResult = -1;
++      railMain->ProbeLevel  = -1;
++      
++      /* Clear the receive area */
++      bzero (railMain->ProbeDest0, sizeof (railMain->ProbeDest0));
++      bzero (railMain->ProbeDest1, sizeof (railMain->ProbeDest1));
++      
++      /* Re-arm the completion event */
++      elan3_sdram_writel (rail->Device, railElan + offsetof (EP3_RAIL_ELAN, ProbeDone.ev_Type), EV_TYPE_BCOPY);
++      elan3_sdram_writel (rail->Device, railElan + offsetof (EP3_RAIL_ELAN, ProbeDone.ev_Count), 1);
++
++      railMain->ProbeDone = EP3_EVENT_ACTIVE;
++      
++      IssueSetevent (rail, rail->RailElanAddr + offsetof (EP3_RAIL_ELAN, ProbeStart));
++    }
++
++    return updated;
++}
++
+Index: linux-2.6.5/drivers/net/qsnet/ep/probenetwork_elan3_thread.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/probenetwork_elan3_thread.c  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/probenetwork_elan3_thread.c       2005-05-11 12:10:12.535917832 -0400
+@@ -0,0 +1,98 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: probenetwork_elan3_thread.c,v 1.19 2004/03/24 11:32:56 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/probenetwork_elan3_thread.c,v $*/
++
++#include <elan3/e3types.h>
++#include <elan3/events.h>
++#include <elan3/elanregs.h>
++#include <elan3/intrinsics.h>
++
++#include "kcomm_vp.h"
++#include "kcomm_elan3.h"
++
++static int
++kcomm_probe_vp (EP3_RAIL_ELAN *railElan, EP3_RAIL_MAIN *railMain, int vp, int attempts, int timeouts)
++{
++    int rc;
++
++    /* Since we use %g1 to hold the "rxd" so the trap handler can
++     * complete the envelope processing - we pass zero to indicate we're
++     * not a receiver thread */
++    asm volatile ("mov %g0, %g1");
++
++    while (attempts && timeouts)
++    {
++      c_open (vp);
++      c_sendmem (TR_TRACEROUTE, &railMain->ProbeDest0, &railElan->ProbeSource0);
++      c_sendmem (TR_TRACEROUTE, &railMain->ProbeDest1, &railElan->ProbeSource1);
++      c_sendtrans0 (TR_SENDACK | TR_SETEVENT, (E3_Addr) 0);
++      
++      switch (rc = c_close())
++      {
++      case C_ACK_OK:
++          return (C_ACK_OK);
++          
++      case C_ACK_DISCARD:
++          attempts--;
++          break;
++
++      default:                                        /* output timeout */
++          timeouts--;
++      }
++
++      c_break_busywait();
++    }
++
++    return (timeouts == 0 ? C_ACK_ERROR : C_ACK_DISCARD);
++}
++
++void
++kcomm_probe (E3_CommandPort *cport, EP3_RAIL_ELAN *railElan, EP3_RAIL_MAIN *railMain)
++{
++    int level;
++
++    for (;;)
++    {
++      c_waitevent (&railElan->ProbeStart, 1);
++
++      switch (railElan->ProbeType)
++      {
++      case PROBE_SINGLE:
++          railMain->ProbeResult = kcomm_probe_vp (railElan, railMain, EP_VP_PROBE(railElan->ProbeLevel),
++                                                  PROBE_SINGLE_ATTEMPTS, PROBE_SINGLE_TIMEOUTS);
++
++          cport->SetEvent = (E3_Addr) &railElan->ProbeDone;
++          break;
++
++      case PROBE_MULTIPLE:
++          for (level = railElan->ProbeLevel; level >= 0; level--)
++          {
++              if (kcomm_probe_vp (railElan, railMain, EP_VP_PROBE(level),
++                                  PROBE_MULTIPLE_ATTEMPTS, PROBE_MULTIPLE_TIMEOUTS) == C_ACK_OK)
++              {
++                  railMain->ProbeLevel  = level;
++                  railMain->ProbeResult = C_ACK_OK;
++                  break;
++              }
++
++              c_break_busywait();
++          }
++          cport->SetEvent = (E3_Addr) &railElan->ProbeDone;
++          break;
++      }
++
++    }
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/ep/probenetwork_elan4.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/probenetwork_elan4.c 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/probenetwork_elan4.c      2005-05-11 12:10:12.536917680 -0400
+@@ -0,0 +1,396 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: probenetwork_elan4.c,v 1.9 2004/08/19 11:05:03 david Exp $ $Name: QSNETMODULES-4-31_20050321 $"
++/*      $Source: /cvs/master/quadrics/epmod/probenetwork_elan4.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++
++#include "kcomm_vp.h"
++#include "kcomm_elan4.h"
++#include "debug.h"
++
++#include <elan4/trtype.h>
++#include <elan4/commands.h>
++
++static void
++probe_interrupt (EP4_RAIL *rail, void *arg)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&rail->r_probe_lock, flags);
++    rail->r_probe_done = 1;
++    kcondvar_wakeupone (&rail->r_probe_wait, &rail->r_probe_lock);
++    spin_unlock_irqrestore (&rail->r_probe_lock, flags);
++}
++
++int
++ep4_probe_init (EP4_RAIL *rail)
++{
++    spin_lock_init (&rail->r_probe_lock);
++    kcondvar_init (&rail->r_probe_wait);
++
++    rail->r_probe_cq = ep4_alloc_ecq (rail, CQ_Size1K);
++
++    if (rail->r_probe_cq == NULL)
++      return -ENOMEM;
++
++    ep4_register_intcookie (rail, &rail->r_probe_intcookie, rail->r_elan_addr, probe_interrupt, rail);
++
++    return 0;
++}
++
++void
++ep4_probe_destroy (EP4_RAIL *rail)
++{
++    if (rail->r_probe_cq)
++      ep4_free_ecq (rail, rail->r_probe_cq);
++
++    if (rail->r_probe_intcookie.int_arg == NULL)
++      return;
++    ep4_deregister_intcookie (rail, &rail->r_probe_intcookie);
++
++    kcondvar_destroy (&rail->r_probe_wait);
++    spin_lock_destroy (&rail->r_probe_lock);
++}
++
++#define LINKDOWN(nodeid, level)       ((nodeid >> (level << 1)) & 3)
++#define PROBE_PATTERN0(nodeid)        (0xaddebabe ^ nodeid)
++#define PROBE_PATTERN1(nodeid)  (0xfeedbeef ^ nodeid)
++
++#define EP4_PROBE_RETRIES     4
++
++int
++ep4_probe_route (EP_RAIL *r, int level, int sw, int nodeid, int *linkup, int *linkdown, int attempts, EP_SWITCH *lsw)
++{
++    EP4_RAIL      *rail  = (EP4_RAIL *) r;
++    EP4_RAIL_MAIN *rmain = rail->r_main;
++    E4_uint16      first = 0;
++    int                  rb    = 0;
++
++    E4_uint8  packed[ROUTE_NUM_PACKED];
++    E4_VirtualProcessEntry route;
++    unsigned long flags;
++    int i;
++
++    for (i = 0; i < ROUTE_NUM_PACKED; i++)
++      packed[i] = 0;
++
++    /* Generate "up" routes */
++    for (i = 0; i < level; i++)
++      if (first == 0)
++          first = linkup ? FIRST_ROUTE(linkup[i]) : FIRST_ADAPTIVE;
++      else
++          packed[rb++] = linkup ? PACKED_ROUTE(linkup[i]) : PACKED_ADAPTIVE;
++    
++    /* Generate a "to-me" route down */
++    if (first == 0)
++      first = FIRST_MYLINK;
++    else
++      packed[rb++] = PACKED_MYLINK;
++    
++    /* Generate the "down" routes */
++    for (i = level-1; i >= 0; i--)
++      packed[rb++] = linkdown ? PACKED_ROUTE(linkdown[i]) : PACKED_ROUTE(LINKDOWN(nodeid, i));
++    
++    /* Pack up the routes into the virtual process entry */
++    route.Values[0] = first | FIRST_HIGH_PRI | FIRST_SYSTEM_PACKET | FIRST_TIMEOUT(3);
++    route.Values[1] = ROUTE_CTXT_VALUE(ELAN4_KCOMM_CONTEXT_NUM);
++
++    for (i = 0; i < (ROUTE_NUM_PACKED >> 1); i++)
++    {
++      route.Values[0] |= ((E4_uint64) packed[i]) << ((i << 2) + ROUTE_PACKED_OFFSET);
++      route.Values[1] |= ((E4_uint64) packed[i+(ROUTE_NUM_PACKED >> 1)]) << ((i << 2));
++    }
++
++    elan4_write_route (rail->r_ctxt.ctxt_dev, rail->r_routetable, EP_VP_PROBE(level), &route);
++    
++    while (attempts--)
++    {
++      rail->r_probe_done = 0;
++
++      /* generate the STEN packet - note we use a datatype of dword as we're copying to elan in dwords
++       *   NB - no flow control is required, since the max packet size is less than the command queue
++       *        size and it's dedicated for network probing.
++       */
++      
++      elan4_guard   (rail->r_probe_cq->ecq_cq, GUARD_CHANNEL(1) | GUARD_RESET(EP4_PROBE_RETRIES));
++      elan4_nop_cmd (rail->r_probe_cq->ecq_cq, 0);
++      
++      elan4_open_packet (rail->r_probe_cq->ecq_cq, OPEN_STEN_PKT_CMD | OPEN_PACKET(0, PACK_OK | RESTART_COUNT_ZERO, EP_VP_PROBE(level)));
++      elan4_sendtransn  (rail->r_probe_cq->ecq_cq, TR_TRACEROUTE(TRACEROUTE_NDWORDS),
++                         rail->r_main_addr + offsetof (EP4_RAIL_MAIN, r_probe_dest0),
++                         0x0000000000000000ull, 0x0000000000000000ull, 0x0000000000000000ull, 0x0000000000000000ull, 
++                         0x0000000000000000ull, 0x0000000000000000ull, 0x0000000000000000ull, 0x0000000000000000ull | ((E4_uint64)PROBE_PATTERN0(nodeid) << 32));
++      elan4_sendtransn  (rail->r_probe_cq->ecq_cq, TR_TRACEROUTE(TRACEROUTE_NDWORDS),
++                         rail->r_main_addr + offsetof (EP4_RAIL_MAIN, r_probe_dest1),
++                         0x0000000100000001ull, 0x0000000100000001ull, 0x0000000100000001ull, 0x0000000100000001ull, 
++                         0x0000000100000001ull, 0x0000000100000001ull, 0x0000000100000001ull, 0x0000000000000001ull | ((E4_uint64)PROBE_PATTERN1(nodeid) << 32));
++      elan4_sendtrans0  (rail->r_probe_cq->ecq_cq, TR_NOP_TRANS | TR_LAST_AND_SEND_ACK, 0);
++
++      elan4_guard           (rail->r_probe_cq->ecq_cq, GUARD_CHANNEL(1) | GUARD_TEST(0, PACK_OK) | GUARD_RESET(EP4_PROBE_RETRIES));
++      elan4_write_dword_cmd (rail->r_probe_cq->ecq_cq, rail->r_main_addr + offsetof (EP4_RAIL_MAIN, r_probe_result), EP4_STATE_FINISHED);
++
++      elan4_guard            (rail->r_probe_cq->ecq_cq, GUARD_CHANNEL(1) | GUARD_TEST(0, RESTART_COUNT_ZERO) | GUARD_RESET(EP4_PROBE_RETRIES));
++      elan4_write_dword_cmd  (rail->r_probe_cq->ecq_cq, rail->r_main_addr + offsetof (EP4_RAIL_MAIN, r_probe_result), EP4_STATE_FAILED);
++
++      elan4_interrupt_cmd   (rail->r_probe_cq->ecq_cq,  rail->r_probe_intcookie.int_val);
++
++      spin_lock_irqsave (&rail->r_probe_lock, flags);
++      while (! rail->r_probe_done)
++          kcondvar_wait (&rail->r_probe_wait, &rail->r_probe_lock, &flags);
++      spin_unlock_irqrestore (&rail->r_probe_lock, flags);
++
++      if (rmain->r_probe_result == EP4_STATE_FINISHED)
++      {
++          if (rmain->r_probe_dest0[TRACEROUTE_ENTRIES - ((2*level)+1) - 1] != PROBE_PATTERN0(nodeid) ||
++              rmain->r_probe_dest1[TRACEROUTE_ENTRIES - ((2*level)+1) - 1] != PROBE_PATTERN1(nodeid))
++          {
++              printk ("%s: lost nodeid at level %d switch %d - %d != %d\n", rail->r_generic.Name, level, sw,
++                      rmain->r_probe_dest0[TRACEROUTE_ENTRIES - ((2*level)+1) - 1], PROBE_PATTERN0(nodeid));
++          }
++          else
++          {
++              E4_uint32 val0 = rmain->r_probe_dest0[TRACEROUTE_ENTRIES - level - 1];
++              E4_uint32 val1 = rmain->r_probe_dest1[TRACEROUTE_ENTRIES - level - 1];
++              
++              lsw->lnr     = TR_TRACEROUTE0_LNR(val0);
++              lsw->link    = TR_TRACEROUTE0_LINKID(val0);
++              lsw->bcast   = TR_TRACEROUTE1_BCAST_TOP(val1);
++              lsw->invalid = 0;
++
++              return 1;
++          }
++      }
++
++      rmain->r_probe_result = EP4_STATE_FREE;
++    }
++
++    return 0;
++}
++
++
++void
++ep4_probe_position_found (EP4_RAIL *rail, ELAN_POSITION *pos)
++{
++    ELAN4_DEV  *dev  = rail->r_ctxt.ctxt_dev;
++    int         lvl;
++
++    for (lvl = 0; lvl < pos->pos_levels; lvl++)
++    {
++      /* Initialise the "probe" route to use the broadcast tree */
++      ELAN_POSITION *pos     = &rail->r_generic.Position;
++      unsigned char *arityp  = &pos->pos_arity[pos->pos_levels - 1];
++      unsigned int   spanned = *arityp;
++      E4_uint16      first   = 0;
++      int            rb      = 0;
++      
++      E4_uint8  packed[ROUTE_NUM_PACKED];
++      E4_VirtualProcessEntry route;
++      int i;
++      
++      for (i = 0; i < ROUTE_NUM_PACKED; i++)
++          packed[i] = 0;
++
++      /* Generate "up" routes */
++      for (i = 0; i < lvl; i++, spanned *= *(--arityp))
++      {
++          if (first == 0)
++              first = FIRST_BCAST_TREE;
++          else
++              packed[rb++] = PACKED_BCAST_TREE;
++      }
++
++      /* Generate a "to-me" route down */
++      if (first == 0)
++          first = FIRST_MYLINK;
++      else
++          packed[rb++] = PACKED_MYLINK;
++
++      spanned /= *arityp++;
++
++      /* Generate the "down" routes */
++      for (i = lvl-1; i >= 0; i--)
++      {
++          spanned /= *arityp;
++          packed[rb++] = PACKED_ROUTE((pos->pos_nodeid / spanned) % *arityp);
++          arityp++;
++      }
++
++    
++      /* Pack up the routes into the virtual process entry */
++      route.Values[0] = first | FIRST_HIGH_PRI | FIRST_SYSTEM_PACKET | FIRST_TIMEOUT(3);
++      route.Values[1] = ROUTE_CTXT_VALUE(ELAN4_KCOMM_CONTEXT_NUM);
++      
++      for (i = 0; i < (ROUTE_NUM_PACKED >> 1); i++)
++      {
++          route.Values[0] |= ((E4_uint64) packed[i]) << ((i << 2) + ROUTE_PACKED_OFFSET);
++          route.Values[1] |= ((E4_uint64) packed[i+(ROUTE_NUM_PACKED >> 1)]) << ((i << 2));
++      }
++      
++      elan4_write_route (rail->r_ctxt.ctxt_dev, rail->r_routetable, EP_VP_PROBE(lvl), &route);
++      
++      /* Initialise "start" event for this level */
++      elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_start[lvl].ev_CountAndType),
++                          E4_EVENT_INIT_VALUE (-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_CHECK_STEN_NDWORDS));
++      elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_start[lvl].ev_CopySource),
++                          rail->r_elan_addr + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl]));
++      elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_start[lvl].ev_CopyDest),
++                          rail->r_probe_cq->ecq_addr);
++
++      /* Initiailise command stream - reset the start event */
++      elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_reset_event_cmd),
++                          WRITE_DWORD_CMD | (rail->r_elan_addr + offsetof (EP4_RAIL_ELAN, r_check_start[lvl])));
++      elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_reset_event_value),
++                          E4_EVENT_INIT_VALUE (-32, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, EP4_CHECK_STEN_NDWORDS));
++
++      /* Initiailise command stream - sten traceroute packet */
++      elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_open),
++                          OPEN_STEN_PKT_CMD | OPEN_PACKET (0, PACK_OK | RESTART_COUNT_ZERO, EP_VP_PROBE(lvl)));
++
++      /* Initiailise command stream - traceroute 0 */
++      elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_trans_traceroute0),
++                          SEND_TRANS_CMD | (TR_TRACEROUTE(TRACEROUTE_NDWORDS) << 16));
++      elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_addr_traceroute0),
++                          rail->r_main_addr + offsetof (EP4_RAIL_MAIN, r_probe_dest0));
++      for (i = 0; i < (TRACEROUTE_NDWORDS-1); i++)
++          elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_data_traceroute0[i]),
++                              0x0000000000000000ull);
++      elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_data_traceroute0[i]),
++                          0x0000000000000000ull | ((E4_uint64) PROBE_PATTERN0(pos->pos_nodeid) << 32));
++
++      /* Initiailise command stream - traceroute 1 */
++      elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_trans_traceroute1),
++                          SEND_TRANS_CMD | (TR_TRACEROUTE(TRACEROUTE_NDWORDS) << 16));
++      elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_addr_traceroute1),
++                          rail->r_main_addr + offsetof (EP4_RAIL_MAIN, r_probe_dest1));
++      for (i = 0; i < (TRACEROUTE_NDWORDS-1); i++)
++          elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_data_traceroute1[i]),
++                              0x0000000100000001ull);
++      elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_data_traceroute1[i]),
++                          0x0000000000000001ull | ((E4_uint64) PROBE_PATTERN1(pos->pos_nodeid) << 32));
++
++      /* Initiailise command stream - null sendack */
++      elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_trans_sendack),
++                          SEND_TRANS_CMD | ((TR_NOP_TRANS | TR_LAST_AND_SEND_ACK) << 16));
++      elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_addr_sendack),
++                          0);
++      
++      /* Initiailise command stream - guard ok, write done */
++      elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_guard_ok),
++                          GUARD_CMD | GUARD_CHANNEL(1) | GUARD_TEST(0, PACK_OK) | GUARD_RESET(EP4_PROBE_RETRIES));
++      elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_writedword_ok),
++                          WRITE_DWORD_CMD | (rail->r_main_addr + offsetof (EP4_RAIL_MAIN, r_probe_level)));
++      elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_value_ok),
++                          lvl);
++
++      /* Initiailise command stream - guard fail, chain to next or write done */
++      elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_guard_fail),
++                          GUARD_CMD | GUARD_CHANNEL(1) | GUARD_TEST(0, RESTART_COUNT_ZERO) | GUARD_RESET(EP4_PROBE_RETRIES));
++
++      if (lvl > 0)
++      {
++          elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_setevent_fail),
++                              SET_EVENT_CMD | (rail->r_elan_addr + offsetof (EP4_RAIL_ELAN, r_check_start[lvl-1])));
++          elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_setevent_nop),
++                              NOP_CMD);
++      }
++      else
++      {
++          elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_setevent_fail),
++                              WRITE_DWORD_CMD | (rail->r_main_addr + offsetof (EP4_RAIL_MAIN, r_probe_level)));
++          elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_setevent_nop),
++                              EP4_PROBE_FAILED);
++      }
++      elan4_sdram_writeq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_check_sten[lvl].c_nop_pad),
++                          NOP_CMD);
++    }
++
++    
++    rail->r_main->r_probe_level = EP4_PROBE_ACTIVE;
++
++    mb();
++    ep4_set_event_cmd (rail->r_probe_cq, rail->r_elan_addr + offsetof (EP4_RAIL_ELAN, r_check_start[pos->pos_levels-1]));
++}
++
++int
++ep4_check_position (EP_RAIL *r)
++{
++    EP4_RAIL      *rail = (EP4_RAIL *) r;
++    ELAN_POSITION *pos  = &rail->r_generic.Position;
++    unsigned int level  = rail->r_main->r_probe_level;
++    unsigned int lvl;
++
++    EPRINTF2 (DBG_PROBE, "%s: ep4_check_position: level=%lld\n", rail->r_generic.Name, rail->r_main->r_probe_level);
++
++    if (rail->r_main->r_probe_level != EP4_PROBE_ACTIVE)
++    {
++      if (rail->r_main->r_probe_level == EP4_PROBE_FAILED)
++      {
++          EPRINTF1 (DBG_PROBE, "%s: ep4_check_position: packets all nacked\n", rail->r_generic.Name);
++
++          rail->r_generic.SwitchProbeLevel = -1;
++      }
++      else
++      {
++          E4_uint32 val0  = rail->r_main->r_probe_dest0[TRACEROUTE_ENTRIES - 2*(level+1)];
++          E4_uint32 val1  = rail->r_main->r_probe_dest1[TRACEROUTE_ENTRIES - 2*(level+1)];
++
++          if (val0 != PROBE_PATTERN0 (pos->pos_nodeid) || val1 != PROBE_PATTERN1 (pos->pos_nodeid))
++          {
++              static unsigned long printed = 0;
++
++              /* We've received a packet from another node - this probably means
++               * that we've moved */
++              if ((lbolt - printed) > (HZ*10))
++              {
++                  printk ("%s: ep4_check_position - level %d lost nodeid\n", rail->r_generic.Name, level);
++                  printed = lbolt;
++              }
++
++              rail->r_generic.SwitchProbeLevel = -1;
++          }
++          else
++          {
++              for (lvl = 0 ; lvl <= level; lvl++)
++              {
++                  E4_uint32 uval0  = rail->r_main->r_probe_dest0[TRACEROUTE_ENTRIES - lvl - 1];
++                  E4_uint32 dval0  = rail->r_main->r_probe_dest0[TRACEROUTE_ENTRIES - ((2*level) - lvl + 1)];
++                  E4_uint32 dval1  = rail->r_main->r_probe_dest1[TRACEROUTE_ENTRIES - ((2*level) - lvl + 1)];
++
++                  rail->r_generic.SwitchState[lvl].linkid = TR_TRACEROUTE0_LINKID (dval0);
++                  rail->r_generic.SwitchState[lvl].LNR    = TR_TRACEROUTE0_LNR(dval0);
++                  rail->r_generic.SwitchState[lvl].bcast  = TR_TRACEROUTE1_BCAST_TOP (dval1);
++                  rail->r_generic.SwitchState[lvl].uplink = TR_TRACEROUTE0_LINKID (uval0);
++
++                  EPRINTF5 (DBG_PROBE, " --- lvl %d: linkid=%d LNR=%x bcast=%d uplink=%d\n", lvl, rail->r_generic.SwitchState[lvl].linkid,
++                            rail->r_generic.SwitchState[lvl].LNR, rail->r_generic.SwitchState[lvl].bcast ,rail->r_generic.SwitchState[lvl].uplink);
++
++              }
++
++              rail->r_generic.SwitchProbeLevel = level;
++          }
++      }
++
++      rail->r_main->r_probe_level = EP4_PROBE_ACTIVE;
++      mb();
++
++      if (rail->r_generic.SwitchBroadcastLevel == rail->r_generic.Position.pos_levels-1)
++          level = rail->r_generic.Position.pos_levels - 1;
++      else
++          level = rail->r_generic.SwitchBroadcastLevel + 1;
++
++      ep4_set_event_cmd (rail->r_probe_cq, rail->r_elan_addr + offsetof (EP4_RAIL_ELAN, r_check_start[level]));
++
++      return 1;
++    }
++
++    return 0;
++}
+Index: linux-2.6.5/drivers/net/qsnet/ep/procfs_linux.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/procfs_linux.c       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/procfs_linux.c    2005-05-11 12:10:12.537917528 -0400
+@@ -0,0 +1,693 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: procfs_linux.c,v 1.53.2.4 2005/01/18 14:18:42 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/procfs_linux.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++#include <elan/epsvc.h>
++#include <elan/epcomms.h>
++
++#include "cm.h"
++#include "debug.h"
++#include "conf_linux.h"
++#include <linux/module.h>
++#include <linux/wait.h>
++#include <linux/poll.h>
++
++#include <qsnet/procfs_linux.h>
++
++struct proc_dir_entry *ep_procfs_root;
++struct proc_dir_entry *ep_config_root;
++
++/*
++ * We provide a slightly "special" interface for /proc/elan/device%d/nodeset,
++ * so that it can be included in a "poll" system call.  On each "read" on the
++ * file, we generate a new nodeset if a) the previous one has been completely
++ * read and b) if it has changed since it was generated.
++ *
++ * Unfortunately ... this doesn't allow "tail -f" to work, since this uses
++ * fstat() on the fd, as we only hold the last nodeset string, we could not
++ * handle the case where two processes were reading a different rates.
++ * We could maybe have implemented this as a "sliding window", so that we 
++ * add a new nodeset string, when it has changed and someone reads past 
++ * end of the last one.   Then if someone read from before out "window"
++ * we would produce "padding" data.  The problem with this, is that a 
++ * simple "cat" on /proc/elan/device%d/nodeset will read the whole "file"
++ * which will be mostly padding !
++ *
++ * Just to not that the purpose of this interface is:
++ *    1) to allow cat /proc/elan/device%d/nodeset to show the current
++ *       nodeset.
++ *    2) to allow rms (or similar) to poll() on the file, and when the
++ *       nodeset changes read a new one.
++ *
++ * so ... we don't bother solving the troublesome "tail -f" problem.
++ */
++
++typedef struct nodeset_private
++{
++    struct nodeset_private *pr_next;
++    EP_RAIL                *pr_rail;
++    unsigned              pr_changed;
++    char                 *pr_page;
++    unsigned              pr_off;
++    unsigned              pr_len;
++} NODESET_PRIVATE;
++
++NODESET_PRIVATE   *ep_nodeset_list;
++wait_queue_head_t  ep_nodeset_wait;
++spinlock_t         ep_nodeset_lock;
++
++static int
++proc_write_state(struct file *file, const char *buffer,
++               unsigned long count, void *data)
++{
++    EP_RAIL *rail = (EP_RAIL *) data;
++    char    tmpbuf[128];
++    int     res;
++
++    if (count > sizeof (tmpbuf)-1)
++      return (-EINVAL);
++    
++    MOD_INC_USE_COUNT;
++    
++    if (copy_from_user (tmpbuf, buffer, count))
++      res = -EFAULT;
++    else 
++    {
++      tmpbuf[count] = '\0';   
++
++      if (tmpbuf[count-1] == '\n')
++          tmpbuf[count-1] = '\0';
++
++      if (! strcmp (tmpbuf, "start") && rail->State == EP_RAIL_STATE_UNINITIALISED)
++          ep_start_rail (rail);
++      
++      if (! strcmp (tmpbuf, "stop") && rail->State > EP_RAIL_STATE_UNINITIALISED)
++          ep_stop_rail (rail);
++      
++      if (! strcmp (tmpbuf, "offline") && rail->State > EP_RAIL_STATE_UNINITIALISED)
++          cm_force_offline (rail, 1, CM_OFFLINE_PROCFS);
++
++      if (! strcmp (tmpbuf, "online") && rail->State > EP_RAIL_STATE_UNINITIALISED)
++          cm_force_offline (rail, 0, CM_OFFLINE_PROCFS);
++
++      if (! strncmp (tmpbuf, "restart=", 8) && rail->State == EP_RAIL_STATE_RUNNING)
++          cm_restart_node (rail, simple_strtol (tmpbuf + 8, NULL, 0));
++
++      if (! strncmp (tmpbuf, "panic=", 6))
++          ep_panic_node (rail->System, simple_strtol(tmpbuf + 6, NULL, 0),
++                         strchr (tmpbuf, ',') ? strchr(tmpbuf, ',') + 1 : "remote panic request");
++
++      if (! strncmp (tmpbuf, "raise=", 6) && rail->State > EP_RAIL_STATE_UNINITIALISED)
++          rail->Operations.RaiseFilter (rail, simple_strtol (tmpbuf + 6, NULL, 0));
++
++      if (! strncmp (tmpbuf, "lower=", 6) && rail->State > EP_RAIL_STATE_UNINITIALISED)
++          rail->Operations.LowerFilter (rail, simple_strtol (tmpbuf + 6, NULL, 0));
++      
++      res = count;
++    }
++
++    MOD_DEC_USE_COUNT;
++
++    return (res);
++}
++
++static int
++proc_read_state(char *page, char **start, off_t off,
++              int count, int *eof, void *data)
++{
++    EP_RAIL *rail = (EP_RAIL *) data;
++    int     len;
++
++    switch (rail->State)
++    {
++    case EP_RAIL_STATE_UNINITIALISED:
++      len = sprintf (page, "uninitialised\n");
++      break;
++    case EP_RAIL_STATE_STARTED:
++      len = sprintf (page, "started\n");
++      break;
++    case EP_RAIL_STATE_RUNNING:
++      len = sprintf (page, "running NodeId=%d NumNodes=%d\n", rail->Position.pos_nodeid, rail->Position.pos_nodes);
++      break;
++    case EP_RAIL_STATE_INCOMPATIBLE:
++      len = sprintf (page, "incompatible NodeId=%d NumNodes=%d\n", rail->Position.pos_nodeid, rail->Position.pos_nodes);
++      break;
++    default:
++      len = sprintf (page, "<unknown>\n");
++      break;
++    }
++
++    return (qsnet_proc_calc_metrics (page, start, off, count, eof, len));
++}
++
++static int
++proc_write_display(struct file *file, const char *buffer,
++                 unsigned long count, void *data)
++{
++    EP_RAIL *rail = (EP_RAIL *) data;
++    char    tmpbuf[128];
++    int     res;
++
++    if (count > sizeof (tmpbuf)-1)
++      return (-EINVAL);
++    
++    MOD_INC_USE_COUNT;
++    
++    if (copy_from_user (tmpbuf, buffer, count))
++      res = -EFAULT;
++    else 
++    {
++      tmpbuf[count] = '\0';   
++
++      if (tmpbuf[count-1] == '\n')
++          tmpbuf[count-1] = '\0';
++
++      if (! strcmp (tmpbuf, "rail"))
++          DisplayRail (rail);
++      if (! strcmp (tmpbuf, "segs"))
++          DisplaySegs (rail);
++      if (! strcmp (tmpbuf, "nodes"))
++          DisplayNodes (rail);
++      if (! strcmp (tmpbuf, "status"))
++          DisplayStatus (rail);
++      if (! strcmp (tmpbuf, "debug") && rail->Operations.Debug)
++          rail->Operations.Debug (rail);
++      if (! strncmp (tmpbuf, "epcomms", 7))
++          ep_comms_display (rail->System, tmpbuf[7] == '=' ? tmpbuf + 8 : NULL);
++      res = count;
++    }
++
++    MOD_DEC_USE_COUNT;
++
++    return (res);
++}
++
++static int
++proc_read_display(char *page, char **start, off_t off,
++                int count, int *eof, void *data)
++{
++    int len = sprintf (page, "<unreadable>\n");
++    
++    return (qsnet_proc_calc_metrics (page, start, off, count, eof, len));
++}
++
++
++static int
++proc_read_stats(char *page, char **start, off_t off,
++              int count, int *eof, void *data)
++{
++    EP_RAIL *rail = (EP_RAIL *) data;
++
++    if ( rail == NULL ) {
++      strcpy(page,"proc_read_stats rail=NULL\n");
++    } else {
++      page[0] = 0;
++      ep_fillout_stats(rail, page);
++      rail->Operations.FillOutStats (rail, page);
++    }
++    return (qsnet_proc_calc_metrics (page, start, off, count, eof, strlen(page)));
++}
++
++static int
++proc_read_devinfo(char *page, char **start, off_t off,
++                int count, int *eof, void *data)
++{
++    EP_RAIL       *rail    = (EP_RAIL *) data;
++    ELAN_DEVINFO  *devinfo = &rail->Devinfo;
++    ELAN_POSITION *pos     = &rail->Position;
++    char          *p       = page;
++    
++    switch (devinfo->dev_device_id)
++    {
++    case PCI_DEVICE_ID_ELAN3:
++      p += sprintf (p, "ep%d is elan3 %d rev %c\n", rail->Number, 
++                    devinfo->dev_instance, 'a' + devinfo->dev_revision_id);
++      break;
++      
++    case PCI_DEVICE_ID_ELAN4:
++      p += sprintf (p, "ep%d is elan4 %d rev %c\n", rail->Number, 
++                    devinfo->dev_instance, 'a' + devinfo->dev_revision_id);
++      break;
++    default:
++      p += sprintf (p, "ep%d is unkown %x/%x\n", rail->Number, devinfo->dev_vendor_id, devinfo->dev_device_id);
++      break;
++    }
++
++    if (rail->State == EP_RAIL_STATE_RUNNING)
++      p += sprintf (p, "ep%d nodeid %d numnodes %d\n", rail->Number, pos->pos_nodeid, pos->pos_nodes);
++
++    return (qsnet_proc_calc_metrics (page, start, off, count, eof, p - page));
++}
++
++static struct rail_info
++{
++    char *name;
++    int (*read_func) (char *page, char **start, off_t off, int count, int *eof, void *data);
++    int (*write_func) (struct file *file, const char *buf, unsigned long count, void *data);
++} rail_info[] = {
++    {"state",   proc_read_state,   proc_write_state},
++    {"display", proc_read_display, proc_write_display},
++    {"stats",   proc_read_stats,   NULL},
++    {"devinfo", proc_read_devinfo, NULL},
++};
++
++static int
++nodeset_open (struct inode *inode, struct file *file)
++{
++    NODESET_PRIVATE *pr;
++
++    if ((pr = kmalloc (sizeof (NODESET_PRIVATE), GFP_KERNEL)) == NULL)
++      return (-ENOMEM);
++    
++    pr->pr_changed = 1;
++    pr->pr_off     = 0;
++    pr->pr_len     = 0;
++    pr->pr_page    = NULL;
++    pr->pr_rail    = (EP_RAIL *)( PDE(inode)->data );
++
++    spin_lock (&ep_nodeset_lock);
++    pr->pr_next = ep_nodeset_list;
++    ep_nodeset_list = pr;
++    spin_unlock (&ep_nodeset_lock);
++
++    file->private_data = (void *) pr;
++
++    MOD_INC_USE_COUNT;
++    return (0);
++}
++
++static int
++nodeset_release (struct inode *inode, struct file *file)
++{
++    NODESET_PRIVATE *pr = (NODESET_PRIVATE *) file->private_data;
++    NODESET_PRIVATE **ppr;
++
++    spin_lock (&ep_nodeset_lock);
++    for (ppr = &ep_nodeset_list; (*ppr) != pr; ppr = &(*ppr)->pr_next)
++      ;
++    (*ppr) = pr->pr_next;
++    spin_unlock (&ep_nodeset_lock);
++
++    if (pr->pr_page)
++      free_page ((unsigned long) pr->pr_page);
++    kfree (pr);
++    
++    MOD_DEC_USE_COUNT;
++    return (0);
++}
++
++static ssize_t
++nodeset_read (struct file *file, char *buf, size_t count, loff_t *ppos)
++{
++    NODESET_PRIVATE *pr  = (NODESET_PRIVATE *) file->private_data;
++    EP_RAIL          *rail = pr->pr_rail;
++    int              error;
++    unsigned long    flags;
++
++    if (!pr->pr_changed && pr->pr_off >= pr->pr_len)
++      return (0);
++
++    if ((error = verify_area (VERIFY_WRITE, buf, count)) != 0)
++      return (error);
++
++    if (pr->pr_page == NULL && (pr->pr_page = (char *) __get_free_page (GFP_KERNEL)) == NULL)
++      return (-ENOMEM);
++
++    if (pr->pr_off >= pr->pr_len)
++    {
++      kmutex_lock (&rail->CallbackLock);
++      if (rail->State == EP_RAIL_STATE_RUNNING)
++      {
++          spin_lock_irqsave (&rail->System->NodeLock, flags);
++          ep_sprintf_bitmap (pr->pr_page, PAGESIZE, statemap_tobitmap(rail->NodeSet), 0, 0, rail->Position.pos_nodes);
++          spin_unlock_irqrestore (&rail->System->NodeLock, flags);
++
++          if (rail->SwitchBroadcastLevel == -1)
++              strcat (pr->pr_page, "<disconnected>");
++          else if (rail->SwitchBroadcastLevel < (rail->Position.pos_levels-1))
++              sprintf (pr->pr_page + strlen (pr->pr_page), "<%d>", rail->SwitchBroadcastLevel);
++          strcat (pr->pr_page, "\n");
++      }
++      else
++          strcpy (pr->pr_page, "<not running>\n");
++      kmutex_unlock (&rail->CallbackLock);
++
++      pr->pr_len     = strlen (pr->pr_page);
++      pr->pr_off     = 0;
++      pr->pr_changed = 0;
++    }
++
++    if (count >= (pr->pr_len - pr->pr_off))
++      count = pr->pr_len - pr->pr_off;
++
++    copy_to_user (buf, pr->pr_page + pr->pr_off, count);
++
++    pr->pr_off += count;
++    *ppos      += count;
++
++    if (pr->pr_off >= pr->pr_len)
++    {
++      free_page ((unsigned long) pr->pr_page);
++      pr->pr_page = NULL;
++    }
++
++    return (count);
++}
++
++static unsigned int
++nodeset_poll (struct file *file, poll_table *wait)
++{
++    NODESET_PRIVATE *pr = (NODESET_PRIVATE *) file->private_data;
++
++    poll_wait (file, &ep_nodeset_wait, wait);
++    if (pr->pr_changed || pr->pr_off < pr->pr_len)
++      return (POLLIN | POLLRDNORM);
++    return (0);
++}
++
++static void 
++nodeset_callback (void *arg, statemap_t *map)
++{
++    EP_RAIL         *rail = (EP_RAIL *) arg;
++    NODESET_PRIVATE *pr;
++
++    ep_display_bitmap (rail->Name, "Nodeset", statemap_tobitmap(map), 0, ep_numnodes(rail->System));
++
++    spin_lock (&ep_nodeset_lock);
++    for (pr = ep_nodeset_list; pr; pr = pr->pr_next)
++      if (pr->pr_rail == rail)
++          pr->pr_changed = 1;
++    spin_unlock (&ep_nodeset_lock);
++
++    wake_up_interruptible (&ep_nodeset_wait);
++}
++
++void
++proc_character_fill (long mode, char *fmt, ...)
++{
++    int len;
++    va_list ap;
++    PROC_PRIVATE *private = (PROC_PRIVATE *)mode;
++    
++    /* is the buffer already full */
++    if (private->pr_len >= private->pr_data_len) 
++      return;
++    
++    /* attempt to fill up to the remaining space */
++    va_start (ap, fmt);
++    len = vsnprintf ( & private->pr_data[private->pr_len], (private->pr_data_len - private->pr_len), fmt, ap);
++    va_end (ap);
++    
++    if (len < 0 ) 
++    {
++      /* we have reached the end of buffer and need to fail all future writes
++       * the caller can check (pr_len >= pr_data_len) and recall with more space 
++       */
++      private->pr_len = private->pr_data_len;
++      return;
++    }
++    
++    /* move the length along */
++    private->pr_len += len;   
++}
++
++int
++proc_release (struct inode *inode, struct file *file)
++{
++    PROC_PRIVATE *pr = (PROC_PRIVATE *) file->private_data;
++    
++    if (pr->pr_data)
++      KMEM_FREE (pr->pr_data, pr->pr_data_len);
++    kfree (pr);
++    
++    MOD_DEC_USE_COUNT;
++    return (0);
++}
++
++ssize_t
++proc_read (struct file *file, char *buf, size_t count, loff_t *ppos)
++{
++    PROC_PRIVATE *pr  = (PROC_PRIVATE *) file->private_data;
++    int           error;
++
++    if (pr->pr_off >= pr->pr_len)
++      return (0);
++
++    if ((error = verify_area (VERIFY_WRITE, buf, count)) != 0)
++      return (error);
++
++    if (count >= (pr->pr_len - pr->pr_off))
++      count = pr->pr_len - pr->pr_off;
++
++    copy_to_user (buf, pr->pr_data + pr->pr_off, count);
++
++    pr->pr_off += count;
++    *ppos      += count;
++
++    return (count);
++}
++
++static int
++proc_open (struct inode *inode, struct file *file)
++{
++    PROC_PRIVATE *pr;
++    CM_RAIL      *cmRail;
++    int           pages = 4;
++    unsigned long flags;
++
++    if ((pr = kmalloc (sizeof (PROC_PRIVATE), GFP_KERNEL)) == NULL)
++      return (-ENOMEM);
++    
++    pr->pr_rail = (EP_RAIL *)(PDE(inode)->data);
++      
++    do {      
++      pr->pr_data_len = PAGESIZE * pages;
++
++      KMEM_ZALLOC (pr->pr_data, char *, pr->pr_data_len, 1);
++      if (pr->pr_data == NULL) 
++      { 
++          pr->pr_len  = sprintf (pr->pr_data, "Out of Memory\n");
++          break;
++      } 
++      
++      pr->pr_off     = 0;
++      pr->pr_len     = 0;
++      pr->pr_data[0] = 0;
++      
++      if (pr->pr_rail->State != EP_RAIL_STATE_RUNNING) 
++      { 
++          pr->pr_len  = sprintf (pr->pr_data, "Rail not Running\n");
++          break;
++      } 
++      else 
++      {
++          pr->pr_di.func  = proc_character_fill;
++          pr->pr_di.arg   = (long)pr;
++
++          if (!strcmp("maps", file->f_dentry->d_iname)) 
++          {
++              cmRail = pr->pr_rail->ClusterRail;
++
++              spin_lock_irqsave (&cmRail->Lock, flags);
++              DisplayNodeMaps (&pr->pr_di, cmRail);   
++              spin_unlock_irqrestore (&cmRail->Lock, flags);  
++          }
++
++          if (!strcmp("segs", file->f_dentry->d_iname)) 
++          {
++              cmRail = pr->pr_rail->ClusterRail;
++              
++              spin_lock_irqsave (&cmRail->Lock, flags);       
++              DisplayNodeSgmts (&pr->pr_di, cmRail);
++              spin_unlock_irqrestore (&cmRail->Lock, flags);
++          }
++
++          if (!strcmp("tree", file->f_dentry->d_iname)) 
++              DisplayRailDo (&pr->pr_di, pr->pr_rail);
++      }
++
++      if ( pr->pr_len < pr->pr_data_len) 
++          break; /* we managed to get all the output into the buffer */
++
++      pages++;
++      KMEM_FREE ( pr->pr_data,  pr->pr_data_len);
++    } while (1);
++      
++
++    file->private_data = (void *) pr;
++
++    MOD_INC_USE_COUNT;
++    return (0);
++}
++
++struct file_operations proc_nodeset_operations = 
++{
++    read:     nodeset_read,
++    poll:     nodeset_poll,
++    open:     nodeset_open,
++    release:  nodeset_release,
++};
++
++struct file_operations proc_operations = 
++{
++    read:     proc_read,
++    open:     proc_open,
++    release:  proc_release,
++};
++
++void
++ep_procfs_rail_init (EP_RAIL *rail)
++{
++    struct proc_dir_entry *dir;
++    struct proc_dir_entry *p;
++    char                   name[10];
++    int                    i;
++
++    sprintf (name, "rail%d", rail->Number);
++
++    if ((dir = rail->ProcDir = proc_mkdir (name, ep_procfs_root)) == NULL)
++      return;
++
++    for (i = 0; i < sizeof (rail_info)/sizeof (rail_info[0]); i++)
++    {
++      if ((p = create_proc_entry (rail_info[i].name, 0, dir)) != NULL)
++      {
++          p->read_proc  = rail_info[i].read_func;
++          p->write_proc = rail_info[i].write_func;
++          p->data       = rail;
++          p->owner      = THIS_MODULE;
++      }
++    }
++
++    if ((p = create_proc_entry ("nodeset", 0, dir)) != NULL)
++    {
++      p->proc_fops = &proc_nodeset_operations;
++      p->owner     = THIS_MODULE;
++      p->data      = rail;
++
++      rail->CallbackRegistered = 1;
++      ep_register_callback (rail, EP_CB_NODESET, nodeset_callback, rail);
++    }
++     
++    if ((p = create_proc_entry ("maps", 0, dir)) != NULL)
++    {
++      p->proc_fops = &proc_operations;
++      p->owner     = THIS_MODULE;
++      p->data      = rail;    
++    }
++    
++    if ((p = create_proc_entry ("segs", 0, dir)) != NULL)
++    {
++      p->proc_fops = &proc_operations;
++      p->owner     = THIS_MODULE;
++      p->data      = rail;
++    }
++    
++    if ((p = create_proc_entry ("tree", 0, dir)) != NULL)
++    {
++      p->proc_fops = &proc_operations;
++      p->owner     = THIS_MODULE;
++      p->data      = rail;
++    }
++
++}
++
++void
++ep_procfs_rail_fini (EP_RAIL *rail)
++{
++    struct proc_dir_entry *dir = rail->ProcDir;
++    char name[10];
++    int  i;
++
++    if (dir == NULL)
++      return;
++
++    if (rail->CallbackRegistered)
++    {
++      ep_remove_callback (rail, EP_CB_NODESET, nodeset_callback, rail);
++
++      remove_proc_entry ("nodeset", dir);
++    }
++
++    remove_proc_entry ("maps",    dir);
++    remove_proc_entry ("segs",    dir);
++    remove_proc_entry ("tree",    dir);
++
++    for (i = 0; i < sizeof (rail_info)/sizeof (rail_info[0]); i++)
++      remove_proc_entry (rail_info[i].name, dir);
++
++    sprintf (name, "rail%d", rail->Number);
++    remove_proc_entry (name, ep_procfs_root);
++}
++
++#include "quadrics_version.h"
++static char     quadrics_version[] = QUADRICS_VERSION;
++
++void
++ep_procfs_init()
++{
++    extern int txd_stabilise;
++    extern int MaxSwitchLevels;
++
++    spin_lock_init (&ep_nodeset_lock);
++    init_waitqueue_head (&ep_nodeset_wait);
++
++    ep_procfs_root = proc_mkdir ("ep", qsnet_procfs_root);
++    ep_config_root = proc_mkdir ("config", ep_procfs_root);
++
++    qsnet_proc_register_str (ep_procfs_root, "version", quadrics_version, 1);
++
++    qsnet_proc_register_hex (ep_config_root, "epdebug",               &epdebug,               0);
++    qsnet_proc_register_hex (ep_config_root, "epdebug_console",       &epdebug_console,       0);
++    qsnet_proc_register_hex (ep_config_root, "epdebug_cmlevel",       &epdebug_cmlevel,       0);
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++    qsnet_proc_register_hex (ep_config_root, "epdebug_check_sum",     &epdebug_check_sum,     0);
++#endif
++    qsnet_proc_register_hex (ep_config_root, "epcomms_forward_limit", &epcomms_forward_limit, 0);
++    qsnet_proc_register_int (ep_config_root, "txd_stabilise",         &txd_stabilise,         0);
++    qsnet_proc_register_int (ep_config_root, "assfail_mode",          &assfail_mode,          0);
++    qsnet_proc_register_int (ep_config_root, "max_switch_levels",     &MaxSwitchLevels,       1);
++
++    ep_procfs_rcvr_xmtr_init();
++}
++
++void
++ep_procfs_fini(void)
++{
++    ep_procfs_rcvr_xmtr_fini();
++
++    remove_proc_entry ("max_switch_levels",     ep_config_root);
++    remove_proc_entry ("assfail_mode",          ep_config_root);
++    remove_proc_entry ("txd_stabilise",         ep_config_root);
++    remove_proc_entry ("epcomms_forward_limit", ep_config_root);
++
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++    remove_proc_entry ("epdebug_check_sum",     ep_config_root);
++#endif
++    remove_proc_entry ("epdebug_cmlevel",       ep_config_root);
++    remove_proc_entry ("epdebug_console",       ep_config_root);
++    remove_proc_entry ("epdebug",               ep_config_root);
++
++    remove_proc_entry ("version", ep_procfs_root);
++    
++    remove_proc_entry ("config", ep_procfs_root);
++    remove_proc_entry ("ep", qsnet_procfs_root);
++
++    spin_lock_destroy (&ep_nodeset_lock);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/ep/quadrics_version.h
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/quadrics_version.h   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/quadrics_version.h        2005-05-11 12:10:12.538917376 -0400
+@@ -0,0 +1 @@
++#define QUADRICS_VERSION "4.31qsnet"
+Index: linux-2.6.5/drivers/net/qsnet/ep/railhints.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/railhints.c  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/railhints.c       2005-05-11 12:10:12.538917376 -0400
+@@ -0,0 +1,103 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: railhints.c,v 1.5 2004/02/06 22:37:06 david Exp $ $Name: QSNETMODULES-4-31_20050321 $"
++/*      $Source: /cvs/master/quadrics/epmod/railhints.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++#include <elan/epsvc.h>
++#include <elan/epcomms.h>
++
++#include "debug.h"
++
++int
++ep_pickRail(EP_RAILMASK railmask)
++{
++    static volatile int lastGlobal;
++    int i, rnum, last = lastGlobal;
++
++    /* Pick a single rail out of the railmask */
++    for (i = 0; i < EP_MAX_RAILS; i++)
++      if (railmask & (1 << ((last + i) % EP_MAX_RAILS)))
++          break;
++
++    if (i == EP_MAX_RAILS)
++      return (-1);
++
++    rnum = (last + i) % EP_MAX_RAILS;
++
++    lastGlobal = (rnum + 1) % EP_MAX_RAILS;
++
++    ASSERT (railmask & (1 << rnum));
++
++    return (rnum);
++}
++
++int
++ep_xmtr_bcastrail (EP_XMTR *xmtr, EP_RAILMASK allowedRails)
++{
++    /* Retrun a single rail out of allowed mask with the best connectivity for broadcast. */
++    return (ep_pickRail (allowedRails & xmtr->RailMask));
++}
++
++int
++ep_xmtr_prefrail (EP_XMTR *xmtr, EP_RAILMASK allowedRails, unsigned nodeId)
++{
++    EP_NODE *node = &xmtr->Subsys->Subsys.Sys->Nodes[nodeId];
++
++    EPRINTF5 (DBG_XMTR, "ep_xmtr_prefrail: xmtr=%p allowedRails=%x nodeId=%d xmtr->RailMaks=%x Connected=%x\n", 
++            xmtr, allowedRails, nodeId, xmtr->RailMask, node->ConnectedRails);
++
++    /* Return a single rail which is currently connected to nodeId (limited to rails
++     * in allowedmask) - if more than one rail is possible, then round-robin between 
++     * them */
++    return (ep_pickRail (allowedRails & xmtr->RailMask & node->ConnectedRails));
++}
++
++EP_RAILMASK
++ep_xmtr_availrails (EP_XMTR *xmtr)
++{
++    /* Return which rails can be used to transmit one. */
++
++    return (xmtr->RailMask);
++}
++
++EP_RAILMASK
++ep_xmtr_noderails (EP_XMTR *xmtr, unsigned nodeId)
++{
++    EP_NODE *node = &xmtr->Subsys->Subsys.Sys->Nodes[nodeId];
++
++    /* Return which rails can be used to transmit to this node. */
++
++    return (xmtr->RailMask & node->ConnectedRails);
++}
++
++int
++ep_rcvr_prefrail (EP_RCVR *rcvr, EP_RAILMASK allowedRails)
++{
++    /* Return the "best" rail for queueing a receive buffer out on - this will be a
++     * rail with ThreadWaiting set or the rail with the least descriptors queued
++     * on it. */
++    
++    return (ep_pickRail (allowedRails & rcvr->RailMask));
++}
++
++EP_RAILMASK
++ep_rcvr_availrails (EP_RCVR *rcvr)
++{
++    /* Return which rails can be used to queue receive buffers. */
++    return (rcvr->RailMask);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/ep/rmap.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/rmap.c       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/rmap.c    2005-05-11 12:10:12.539917224 -0400
+@@ -0,0 +1,365 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: rmap.c,v 1.15 2004/05/19 10:24:38 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/rmap.c,v $ */
++
++#include <qsnet/kernel.h>
++#include <elan/rmap.h>
++
++#include "debug.h"
++
++void
++ep_display_rmap (EP_RMAP *mp)
++{
++    EP_RMAP_ENTRY *bp;
++    unsigned long flags;
++    
++    spin_lock_irqsave (&mp->m_lock, flags);
++    ep_debugf (DBG_DEBUG, "map: %s size %d free %d\n", mp->m_name, mp->m_size, mp->m_free);
++    for (bp = &mp->m_map[0]; bp->m_size; bp++)
++      ep_debugf (DBG_DEBUG, "   [%lx - %lx]\n", bp->m_addr, bp->m_addr+bp->m_size-1);
++    spin_unlock_irqrestore (&mp->m_lock, flags);
++}
++
++void
++ep_mapinit (EP_RMAP *mp, char *name, u_int mapsize)
++{
++    spin_lock_init (&mp->m_lock);
++    kcondvar_init (&mp->m_wait);
++    
++    /* The final segment in the array has size 0 and acts as a delimiter
++     * we insure that we never use segments past the end of the array by
++     * maintaining a free segment count in m_free.  When excess segments
++     * occur we discard some resources */
++    
++    mp->m_size = mapsize;
++    mp->m_free = mapsize;
++    mp->m_name = name;
++    
++    bzero (mp->m_map, sizeof (EP_RMAP_ENTRY) * (mapsize+1));
++}
++
++EP_RMAP *
++ep_rmallocmap (size_t mapsize, char *name, int cansleep)
++{
++    EP_RMAP *mp;
++
++    KMEM_ZALLOC (mp, EP_RMAP *, sizeof (EP_RMAP) + mapsize*sizeof (EP_RMAP_ENTRY), cansleep);
++
++    if (mp != NULL)
++      ep_mapinit (mp, name, mapsize);
++
++    return (mp);
++}
++
++void
++ep_rmfreemap (EP_RMAP *mp)
++{
++    spin_lock_destroy (&mp->m_lock);
++    kcondvar_destroy (&mp->m_wait);
++    
++    KMEM_FREE (mp, sizeof (EP_RMAP) + mp->m_size * sizeof (EP_RMAP_ENTRY));
++}
++
++static u_long
++ep_rmalloc_locked (EP_RMAP *mp, size_t size)
++{
++    EP_RMAP_ENTRY *bp;
++    u_long            addr;
++    
++    ASSERT (size > 0);
++    ASSERT (SPINLOCK_HELD (&mp->m_lock));
++
++    for (bp = &mp->m_map[0]; bp->m_size; bp++)
++    {
++      if (bp->m_size >= size)
++      {
++          addr = bp->m_addr;
++          bp->m_addr += size;
++          
++          if ((bp->m_size -= size) == 0)
++          {
++              /* taken all of this slot - so shift the map down */
++              do {
++                  bp++;
++                  (bp-1)->m_addr = bp->m_addr;
++              } while (((bp-1)->m_size = bp->m_size) != 0);
++
++              mp->m_free++;
++          }
++          return (addr);
++      }
++    }
++
++    return (0);
++}
++
++u_long
++ep_rmalloc (EP_RMAP *mp, size_t size, int cansleep)
++{
++    unsigned long addr;
++    unsigned long flags;
++
++    spin_lock_irqsave (&mp->m_lock, flags);
++    while ((addr = ep_rmalloc_locked (mp, size)) == 0 && cansleep)
++    {
++      mp->m_want = 1;
++      kcondvar_wait (&mp->m_wait, &mp->m_lock, &flags);
++    }
++
++    spin_unlock_irqrestore (&mp->m_lock, flags);
++
++    return (addr);
++}
++
++
++
++u_long
++ep_rmalloc_constrained (EP_RMAP *mp, size_t size, u_long alo, u_long ahi, u_long align, int cansleep)
++{
++    EP_RMAP_ENTRY *bp, *bp2, *lbp;
++    unsigned long addr=0;
++    size_t        delta;
++    int           ok;
++    unsigned long flags;
++
++    spin_lock_irqsave (&mp->m_lock, flags);
++ again:
++    for (bp = &mp->m_map[0]; bp->m_size; bp++)
++    {
++      delta = 0;
++      
++      if (alo < bp->m_addr)
++      {
++          addr = bp->m_addr;
++          
++          if (addr & (align-1))
++              addr = (addr + (align-1)) & ~(align-1);
++          
++          delta = addr - bp->m_addr;
++          
++          if (ahi >= bp->m_addr + bp->m_size)
++              ok = (bp->m_size >= (size + delta));
++          else
++              ok = ((bp->m_addr + size + delta) <= ahi);
++      }
++      else
++      {
++          addr = alo;
++          if (addr & (align-1))
++              addr = (addr + (align-1)) & ~(align-1);
++          delta = addr - bp->m_addr;
++          
++          if (ahi >= bp->m_addr + bp->m_size)
++              ok = ((alo + size + delta) <= (bp->m_addr + bp->m_size));
++          else
++              ok = ((alo + size + delta) <= ahi);
++      }
++
++      if (ok)
++          break;
++    } 
++    
++    if (bp->m_size == 0)
++    {
++      if (cansleep)
++      {
++          mp->m_want = 1;
++          kcondvar_wait (&mp->m_wait, &mp->m_lock, &flags);
++          goto again;
++      }
++      spin_unlock_irqrestore (&mp->m_lock, flags);
++      return (0);
++    }
++
++    /* found an approriate map entry - so take the bit out which we want */
++    if (bp->m_addr == addr) 
++    {
++      if (bp->m_size == size) 
++      {
++          /* allocate entire segment and compress map */
++          bp2 = bp;
++          while (bp2->m_size) 
++          {
++              bp2++;
++              (bp2-1)->m_addr = bp2->m_addr;
++              (bp2-1)->m_size = bp2->m_size;
++          }
++          mp->m_free++;
++      }
++      else 
++      {
++          /* take from start of segment */
++          bp->m_addr += size;
++          bp->m_size -= size;
++      }
++    }
++    else 
++    {
++      if (bp->m_addr + bp->m_size == addr + size) 
++      {
++          /* take from end of segment */
++          bp->m_size -= size;
++      }
++      else 
++      {
++          /* split the segment loosing the last entry if there's no space */
++          if (mp->m_free == 0) 
++          {
++              /* find last map entry */
++              for (lbp = bp; lbp->m_size != 0; lbp++)
++                  ;
++              lbp--;
++              
++              if (lbp->m_size > (lbp-1)->m_size)
++                  lbp--;
++              
++              printk ("%s: lost resource map entry [%lx, %lx]\n",
++                      mp->m_name, lbp->m_addr, lbp->m_addr + lbp->m_size);
++              
++              *lbp = *(lbp+1);
++              (lbp+1)->m_size = 0;
++              
++              mp->m_free++;
++          }
++          
++          for (bp2 = bp; bp2->m_size != 0; bp2++)
++              continue;
++          
++          for (bp2--; bp2 > bp; bp2--)
++          {
++              (bp2+1)->m_addr = bp2->m_addr;
++              (bp2+1)->m_size = bp2->m_size;
++          }
++
++          mp->m_free--;
++          
++          (bp+1)->m_addr = addr + size;
++          (bp+1)->m_size = bp->m_addr + bp->m_size - (addr + size);
++          bp->m_size = addr - bp->m_addr;
++      }
++    }
++
++    spin_unlock_irqrestore (&mp->m_lock, flags);
++    return (addr);
++}
++
++void
++ep_rmfree (EP_RMAP *mp, size_t size, u_long addr)
++{
++    EP_RMAP_ENTRY *bp;
++    unsigned long t;
++    unsigned long flags;
++
++    spin_lock_irqsave (&mp->m_lock, flags);
++
++    ASSERT (addr != 0 && size > 0);
++      
++again:
++    /* find the piece of the map which starts after the returned space
++     * or the end of the map */
++    for (bp = &mp->m_map[0]; bp->m_addr <= addr && bp->m_size != 0; bp++)
++      ;
++
++    /* bp points to the piece to the right of where we want to go */
++    
++    if (bp > &mp->m_map[0] && (bp-1)->m_addr + (bp-1)->m_size >= addr) 
++    {
++      /* merge with piece on the left */
++      
++      ASSERT ((bp-1)->m_addr + (bp-1)->m_size <= addr);
++      
++      (bp-1)->m_size += size;
++      
++      ASSERT (bp->m_size == 0 || addr+size <= bp->m_addr);
++      
++      if (bp->m_size && (addr + size) == bp->m_addr)
++      {
++          /* merge witht he piece on the right by 
++           * growing the piece on the left and shifting
++           * the map down */
++          
++          ASSERT ((addr + size) <= bp->m_addr);
++          
++          (bp-1)->m_size += bp->m_size;
++          while (bp->m_size) 
++          {
++              bp++;
++              (bp-1)->m_addr = bp->m_addr;
++              (bp-1)->m_size = bp->m_size;
++          }
++          
++          mp->m_free++;
++      }
++    }
++    else if (addr + size >= bp->m_addr && bp->m_size)
++    {
++      /* merge with piece to the right */
++      
++      ASSERT ((addr + size) <= bp->m_addr);
++      
++      bp->m_addr -= size;
++      bp->m_size += size;
++    }
++    else
++    {
++      /* doesn't join with left or right - check for map
++         overflow and discard the smallest of the last or
++         next to last entries */
++
++      if (mp->m_free == 0)
++      {
++          EP_RMAP_ENTRY *lbp;
++          
++          /* find last map entry */
++          for (lbp = bp; lbp->m_size != 0; lbp++)
++              ;
++          lbp--;
++          
++          if (lbp->m_size > (lbp-1)->m_size)
++              lbp--;
++          
++          printk ("%s: lost resource map entry [%lx, %lx]\n", 
++                  mp->m_name, lbp->m_addr, lbp->m_addr + lbp->m_size);
++          
++          *lbp = *(lbp+1);
++          (lbp+1)->m_size = 0;
++
++          mp->m_free++;
++          goto again;
++      }
++
++      /* make a new entry and push the remaining ones up */
++      do {
++          t = bp->m_addr;
++          bp->m_addr = addr;
++          addr = t;
++          t = bp->m_size;
++          bp->m_size = size;
++          bp++;
++      } while ((size = t) != 0);
++
++      mp->m_free--;
++    }
++    
++    /* if anyone blocked on rmalloc failure, wake 'em up */
++    if (mp->m_want)
++    {
++      mp->m_want = 0;
++      kcondvar_wakeupall (&mp->m_wait, &mp->m_lock);
++    }
++
++    spin_unlock_irqrestore (&mp->m_lock, flags);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/ep/spinlock_elan3_thread.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/spinlock_elan3_thread.c      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/spinlock_elan3_thread.c   2005-05-11 12:10:12.539917224 -0400
+@@ -0,0 +1,44 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: spinlock_elan3_thread.c,v 1.9 2003/10/07 13:22:38 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/spinlock_elan3_thread.c,v $ */
++
++#include <qsnet/types.h>
++
++#include <elan3/e3types.h>
++#include <elan3/events.h>
++#include <elan3/elanregs.h>
++#include <elan3/intrinsics.h>
++
++#include <elan/nmh.h>
++#include <elan/kcomm.h>
++#include <elan/epcomms.h>
++
++#include "kcomm_elan3.h"
++#include "epcomms_elan3.h"
++
++void
++ep3_spinblock (EP3_SPINLOCK_ELAN *sle, EP3_SPINLOCK_MAIN *sl)
++{
++    do {
++      sl->sl_seq = sle->sl_seq;                       /* Release my lock */
++      
++      while (sle->sl_lock)                            /* Wait until the main */
++          c_break();                                  /* releases the lock */
++      
++      sle->sl_seq++;                                  /* and try and relock */
++    } while (sle->sl_lock);
++}
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/ep/statemap.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/statemap.c   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/statemap.c        2005-05-11 12:10:12.540917072 -0400
+@@ -0,0 +1,385 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: statemap.c,v 1.11.8.1 2004/11/18 12:05:00 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/statemap.c,v $ */
++
++#include <qsnet/kernel.h>
++#include <elan/statemap.h>
++
++/******************************** global state bitmap stuff **********************************/
++static int
++statemap_setmapbit (bitmap_t *map, int offset, int bit)
++{
++   bitmap_t *e    = &map[offset >> BT_ULSHIFT];
++   bitmap_t  mask = ((bitmap_t)1) << (offset & BT_ULMASK);
++   int       rc = ((*e) & mask) != 0;
++   
++   if (bit)
++   {
++      *e |= mask;
++      return (!rc);
++   }
++
++   *e &= ~mask;
++   return (rc);
++}
++
++static int
++statemap_firstsegbit (bitmap_t seg)
++{
++   int            bit = 0;
++   
++   if (seg == 0)
++      return (-1);
++
++#if (BT_ULSHIFT == 6)
++   if ((seg & 0xffffffffL) == 0)
++   {
++      seg >>= 32;
++      bit += 32;
++   }
++#elif (BT_ULSHIFT != 5)
++# error "Unexpected value of BT_ULSHIFT"
++#endif
++
++   if ((seg & 0xffff) == 0)
++   {
++      seg >>= 16;
++      bit += 16;
++   }
++      
++   if ((seg & 0xff) == 0)
++   {
++      seg >>= 8;
++      bit += 8;
++   }
++      
++   if ((seg & 0xf) == 0)
++   {
++      seg >>= 4;
++      bit += 4;
++   }
++      
++   if ((seg & 0x3) == 0)
++   {
++      seg >>= 2;
++      bit += 2;
++   }
++
++   return (((seg & 0x1) == 0) ? bit + 1 : bit);
++}
++
++bitmap_t
++statemap_getseg (statemap_t *map, unsigned int offset)
++{
++   ASSERT (offset < map->size);
++   ASSERT ((offset & BT_ULMASK) == 0);
++
++   return (map->bitmap[offset >> BT_ULSHIFT]);
++}
++
++void
++statemap_setseg (statemap_t *map, unsigned int offset, bitmap_t seg)
++{
++   ASSERT (offset < map->size);
++   ASSERT ((offset & BT_ULMASK) == 0);
++
++   offset >>= BT_ULSHIFT;
++   if (map->bitmap[offset] == seg)
++      return;
++
++   map->bitmap[offset] = seg;
++
++   if (statemap_setmapbit (map->changemap2, offset,       1) &&
++       statemap_setmapbit (map->changemap1, offset >>= BT_ULSHIFT, 1))
++      statemap_setmapbit (map->changemap0, offset >>= BT_ULSHIFT, 1);
++}
++
++bitmap_t
++statemap_getbits (statemap_t *map, unsigned int offset, int nbits)
++{
++   int      index = offset >> BT_ULSHIFT;
++   bitmap_t mask  = (nbits == BT_NBIPUL) ? (bitmap_t) -1 : (((bitmap_t)1) << nbits) - 1;
++   
++   ASSERT (nbits <= BT_NBIPUL);
++   ASSERT (offset + nbits <= map->size);
++
++   offset &= BT_ULMASK;
++   if (offset + nbits <= BT_NBIPUL)
++      return ((map->bitmap[index] >> offset) & mask);
++   
++   return (((map->bitmap[index] >> offset) |
++          (map->bitmap[index + 1] << (BT_NBIPUL - offset))) & mask);
++}
++
++void
++statemap_setbits (statemap_t *map, unsigned int offset, bitmap_t bits, int nbits)
++{
++   int      index = offset >> BT_ULSHIFT;
++   bitmap_t mask;
++   bitmap_t seg;
++   bitmap_t newseg;
++
++   ASSERT (nbits <= BT_NBIPUL);
++   ASSERT (offset + nbits <= map->size);
++
++   offset &= BT_ULMASK;
++   if (offset + nbits <= BT_NBIPUL)
++   {
++      mask = ((nbits == BT_NBIPUL) ? -1 : ((((bitmap_t)1) << nbits) - 1)) << offset;
++      seg = map->bitmap[index];
++      newseg = ((bits << offset) & mask) | (seg & ~mask);
++      
++      if (seg == newseg)
++       return;
++   
++      map->bitmap[index] = newseg;
++      
++      if (statemap_setmapbit (map->changemap2, index,       1) &&
++        statemap_setmapbit (map->changemap1, index >>= BT_ULSHIFT, 1))
++       statemap_setmapbit (map->changemap0, index >>= BT_ULSHIFT, 1);
++      return;
++   }
++   
++   mask = ((bitmap_t)-1) << offset;
++   seg = map->bitmap[index];
++   newseg = ((bits << offset) & mask) | (seg & ~mask);
++
++   if (seg != newseg)
++   {
++      map->bitmap[index] = newseg;
++      
++      if (statemap_setmapbit (map->changemap2, index,       1) &&
++        statemap_setmapbit (map->changemap1, index >> BT_ULSHIFT, 1))
++       statemap_setmapbit (map->changemap0, index >> (2 * BT_ULSHIFT), 1);
++   }
++   
++   index++;
++   offset = BT_NBIPUL - offset;
++   mask = (((bitmap_t)1) << (nbits - offset)) - 1;
++   seg = map->bitmap[index];
++   newseg = ((bits >> offset) & mask) | (seg & ~mask);
++   
++   if (seg == newseg)
++      return;
++   
++   map->bitmap[index] = newseg;
++   
++   if (statemap_setmapbit (map->changemap2, index,       1) &&
++       statemap_setmapbit (map->changemap1, index >>= BT_ULSHIFT, 1))
++      statemap_setmapbit (map->changemap0, index >>= BT_ULSHIFT, 1);
++}
++
++void
++statemap_zero (statemap_t *dst)
++{
++   int       size       = dst->size;
++   int       offset     = 0;
++   bitmap_t *changemap0 = dst->changemap0;
++   bitmap_t *changemap1 = dst->changemap1;
++   bitmap_t *changemap2 = dst->changemap2;
++   bitmap_t *dstmap     = dst->bitmap;
++   bitmap_t  bit0;
++   bitmap_t  bit1;
++   bitmap_t  bit2;
++
++   for (bit0 = 1; offset < size; bit0 <<= 1, changemap1++)
++   {
++      for (bit1 = 1; bit1 != 0 && offset < size; bit1 <<= 1, changemap2++)
++      {
++       for (bit2 = 1; bit2 != 0 && offset < size; bit2 <<= 1, dstmap++, offset += BT_NBIPUL)
++       {
++           *dstmap = 0;
++           *changemap2 |= bit2;
++       }
++       *changemap1 |= bit1;
++      }
++      *changemap0 |= bit0;
++   }
++}
++   
++void
++statemap_setmap (statemap_t *dst, statemap_t *src)
++{
++   int       size       = dst->size;
++   int       offset     = 0;
++   bitmap_t *changemap0 = dst->changemap0;
++   bitmap_t *changemap1 = dst->changemap1;
++   bitmap_t *changemap2 = dst->changemap2;
++   bitmap_t *dstmap     = dst->bitmap;
++   bitmap_t *srcmap     = src->bitmap;
++   bitmap_t  bit0;
++   bitmap_t  bit1;
++   bitmap_t  bit2;
++
++   ASSERT (src->size == size);
++   
++   for (bit0 = 1; offset < size; bit0 <<= 1, changemap1++)
++   {
++      for (bit1 = 1; bit1 != 0 && offset < size; bit1 <<= 1, changemap2++)
++      {
++       for (bit2 = 1; bit2 != 0 && offset < size; bit2 <<= 1, dstmap++, srcmap++, offset += BT_NBIPUL)
++          if (*dstmap != *srcmap)
++          {
++             *dstmap = *srcmap;
++             *changemap2 |= bit2;
++          }
++       if (*changemap2 != 0)
++          *changemap1 |= bit1;
++      }
++      if (*changemap1 != 0)
++       *changemap0 |= bit0;
++   }
++}
++
++void
++statemap_ormap (statemap_t *dst, statemap_t *src)
++{
++   int       size       = dst->size;
++   int       offset     = 0;
++   bitmap_t *changemap0 = dst->changemap0;
++   bitmap_t *changemap1 = dst->changemap1;
++   bitmap_t *changemap2 = dst->changemap2;
++   bitmap_t *dstmap     = dst->bitmap;
++   bitmap_t *srcmap     = src->bitmap;
++   bitmap_t  bit0;
++   bitmap_t  bit1;
++   bitmap_t  bit2;
++   bitmap_t  seg;
++
++   ASSERT (src->size == size);
++   
++   for (bit0 = 1; offset < size; bit0 <<= 1, changemap1++)
++   {
++      for (bit1 = 1; bit1 != 0 && offset < size; bit1 <<= 1, changemap2++)
++      {
++       for (bit2 = 1; bit2 != 0 && offset < size; bit2 <<= 1, dstmap++, srcmap++, offset += BT_NBIPUL)
++       {
++          seg = *dstmap | *srcmap;
++          if (*dstmap != seg)
++          {
++             *dstmap = seg;
++             *changemap2 |= bit2;
++          }
++       }
++       if (*changemap2 != 0)
++          *changemap1 |= bit1;
++      }
++      if (*changemap1 != 0)
++       *changemap0 |= bit0;
++   }
++}
++
++int
++statemap_findchange (statemap_t *map, bitmap_t *newseg, int clearchange)
++{
++   int          bit0;
++   bitmap_t    *cm1;
++   int          bit1;
++   bitmap_t    *cm2;
++   int          bit2;
++   unsigned int offset;
++
++   bit0 = statemap_firstsegbit (*(map->changemap0));
++   if (bit0 < 0)
++      return (-1);
++
++   offset = bit0;
++   cm1 = map->changemap1 + offset;
++   bit1 = statemap_firstsegbit (*cm1);
++   ASSERT (bit1 >= 0);
++
++   offset = (offset << BT_ULSHIFT) + bit1;
++   cm2 = map->changemap2 + offset;
++   bit2 = statemap_firstsegbit (*cm2);
++   ASSERT (bit2 >= 0);
++   
++   offset = (offset << BT_ULSHIFT) + bit2;
++   *newseg = map->bitmap[offset];
++
++   if (clearchange &&
++       (*cm2 &= ~(((bitmap_t)1) << bit2)) == 0 &&
++       (*cm1 &= ~(((bitmap_t)1) << bit1)) == 0)
++      map->changemap0[0] &= ~(((bitmap_t)1) << bit0);
++
++   return (offset << BT_ULSHIFT);
++}
++
++int
++statemap_changed (statemap_t *map)
++{
++   return ((*(map->changemap0) != 0));
++}
++
++void
++statemap_reset (statemap_t *map)
++{
++   bzero (map->changemap0, map->changemap_nob + map->bitmap_nob);
++}
++
++void
++statemap_copy (statemap_t *dst, statemap_t *src)
++{
++   ASSERT (dst->size == src->size);
++   bcopy (src->changemap0, dst->changemap0, src->changemap_nob + src->bitmap_nob);
++}
++
++void
++statemap_clearchanges (statemap_t *map)
++{
++   if (statemap_changed (map))
++      bzero (map->changemap0, map->changemap_nob);
++}
++
++bitmap_t *
++statemap_tobitmap (statemap_t *map)
++{
++    return (map->bitmap);
++}
++
++statemap_t *
++statemap_create (int size)
++{
++   int   struct_entries     = (sizeof (statemap_t) * 8 + (BT_NBIPUL-1)) >> BT_ULSHIFT;
++   int   bitmap_entries     = (size + (BT_NBIPUL-1)) >> BT_ULSHIFT;
++   int   changemap2_entries = (bitmap_entries + (BT_NBIPUL-1)) >> BT_ULSHIFT;
++   int   changemap1_entries = (changemap2_entries + (BT_NBIPUL-1)) >> BT_ULSHIFT;
++   int   changemap0_entries = (changemap1_entries + (BT_NBIPUL-1)) >> BT_ULSHIFT;
++   int   changemap_entries  = changemap0_entries + changemap1_entries + changemap2_entries;
++   int   nob                = (struct_entries + bitmap_entries + changemap_entries) * sizeof (bitmap_t);
++   statemap_t *map;
++
++   ASSERT ((1 << BT_ULSHIFT) == BT_NBIPUL);
++   ASSERT (changemap0_entries == 1);
++
++   KMEM_ZALLOC (map, statemap_t *, nob, 1);
++
++   map->size = size;
++   map->nob  = nob;
++   map->changemap_nob = changemap_entries * sizeof (bitmap_t);
++   map->bitmap_nob = bitmap_entries * sizeof (bitmap_t);
++   map->changemap0 = ((bitmap_t *)map) + struct_entries;
++   map->changemap1 = map->changemap0 + changemap0_entries;
++   map->changemap2 = map->changemap1 + changemap1_entries;
++   map->bitmap     = map->changemap2 + changemap2_entries;
++
++   return (map);
++}
++
++void
++statemap_destroy (statemap_t *map)
++{
++   KMEM_FREE (map, map->nob);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/ep/statusmon.h
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/statusmon.h  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/statusmon.h       2005-05-11 12:10:12.540917072 -0400
+@@ -0,0 +1,44 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: statusmon.h,v 1.6 2003/10/07 13:22:38 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/statusmon.h,v $*/
++
++#ifndef __ELAN3_STATUSMON_H
++#define __ELAN3_STATUSMON_H
++
++typedef struct statusmon_node
++{
++    u_int     NodeId;
++    u_int     State;
++} STATUSMON_SGMT;
++
++typedef struct statusmon_level
++{
++    unsigned     Width;
++    STATUSMON_SGMT Nodes[CM_SGMTS_PER_LEVEL];
++} STATUSMON_LEVEL;
++
++typedef struct statusmon_msg
++{
++    unsigned      Type;
++    unsigned      NodeId;
++    unsigned      NumLevels;
++    unsigned      TopLevel;
++    unsigned        Role;
++    STATUSMON_LEVEL Levels[CM_MAX_LEVELS];
++} STATUSMON_MSG;
++
++
++#endif /* __ELAN3_STATUSMON_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/ep/support.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/support.c    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/support.c 2005-05-11 12:10:12.540917072 -0400
+@@ -0,0 +1,109 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: support.c,v 1.37.8.1 2004/09/30 15:01:53 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/support.c,v $ */
++
++#include <qsnet/kernel.h>
++#include <elan/kcomm.h>
++
++/****************************************************************************************/
++/*
++ * Nodeset/flush callbacks.
++ */
++int
++ep_register_callback (EP_RAIL *rail, unsigned idx, void (*routine)(void *, statemap_t *), void *arg)
++{
++    EP_CALLBACK *cb;
++    
++    KMEM_ALLOC (cb, EP_CALLBACK *, sizeof (EP_CALLBACK), 1);
++    
++    cb->Routine = routine;
++    cb->Arg     = arg;
++
++    kmutex_lock (&rail->CallbackLock);
++    cb->Next = rail->CallbackList[idx];
++    rail->CallbackList[idx] = cb;
++    kmutex_unlock (&rail->CallbackLock);
++    
++    return (ESUCCESS);
++}
++
++void
++ep_remove_callback (EP_RAIL *rail, unsigned idx, void (*routine)(void *, statemap_t *), void *arg)
++{
++    EP_CALLBACK  *cb;
++    EP_CALLBACK **predp;
++
++    kmutex_lock (&rail->CallbackLock);
++    for (predp = &rail->CallbackList[idx]; (cb = *predp); predp = &cb->Next)
++      if (cb->Routine == routine && cb->Arg == arg)
++          break;
++
++    if (cb == NULL)
++      panic ("ep_remove_member_callback");
++    
++    *predp = cb->Next;
++    kmutex_unlock (&rail->CallbackLock);
++    
++    KMEM_FREE (cb, sizeof (EP_CALLBACK));
++}
++
++void
++ep_call_callbacks (EP_RAIL *rail, unsigned idx, statemap_t *map)
++{
++    EP_CALLBACK *cb;
++
++    kmutex_lock (&rail->CallbackLock);
++
++    rail->CallbackStep = idx;
++
++    for (cb = rail->CallbackList[idx]; cb; cb = cb->Next) {
++      (cb->Routine) (cb->Arg, map);
++    }
++    kmutex_unlock (&rail->CallbackLock);
++}
++
++unsigned int
++ep_backoff (EP_BACKOFF *backoff, int type)
++{
++    static int bcount[EP_NUM_BACKOFF] = {1, 16, 32, 64, 128, 256, 512, 1024};
++    
++    if (backoff->type != type)
++    {
++      backoff->type  = type;
++      backoff->indx  = 0;
++      backoff->count = 0;
++    }
++
++    if (++backoff->count > bcount[backoff->indx] && backoff->indx < (EP_NUM_BACKOFF-1))
++    {
++      backoff->indx++;
++      backoff->count = 0;
++    }
++
++    return (backoff->indx);
++}
++
++/* Generic checksum algorithm */
++uint16_t
++CheckSum (char *msg, int nob)
++{
++    uint16_t sum = 0;
++   
++    while (nob-- > 0)
++      sum = sum * 13 + *msg++;
++
++    return (sum);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/ep/support_elan3.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/support_elan3.c      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/support_elan3.c   2005-05-11 12:10:12.544916464 -0400
+@@ -0,0 +1,2111 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: support_elan3.c,v 1.42.8.3 2004/11/12 10:54:51 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/support_elan3.c,v $ */
++
++#include <qsnet/kernel.h>
++#include <qsnet/kthread.h>
++
++#include <elan/kcomm.h>
++#include <elan/epsvc.h>
++#include <elan/epcomms.h>
++
++#include "kcomm_vp.h"
++#include "kcomm_elan3.h"
++#include "epcomms_elan3.h"
++#include "debug.h"
++
++#include <elan3/thread.h>
++#include <elan3/urom_addrs.h>
++
++/****************************************************************************************/
++#define DMA_RING_NEXT_POS(ring)      ((ring)->Position+1 == ring->Entries ? 0 : ((ring)->Position+1))
++#define DMA_RING_PREV_POS(ring,pos)  ((pos) == 0 ? (ring)->Entries-1 : (pos) - 1)
++
++static int 
++DmaRingCreate (EP3_RAIL *rail, EP3_DMA_RING *ring, int ctxnum, int entries)
++{
++    unsigned long pgnum = (ctxnum * sizeof (E3_CommandPort)) / PAGE_SIZE;
++    unsigned long pgoff = (ctxnum * sizeof (E3_CommandPort)) & (PAGE_SIZE-1);
++    int           s;    
++        
++    /* set up the initial position */
++    ring->Entries  = entries;
++    ring->Position = 0;
++    
++    if (! (ring->pEvent = ep_alloc_elan (&rail->Generic, entries * sizeof (E3_BlockCopyEvent), 0, &ring->epEvent)))
++    {
++      ring->CommandPort = (ioaddr_t) NULL;
++      return (ENOMEM);
++    }
++    
++    if (! (ring->pDma = ep_alloc_elan (&rail->Generic, entries * sizeof (E3_DMA), 0, &ring->epDma)))
++    {
++      ep_free_elan (&rail->Generic, ring->epEvent, entries * sizeof (E3_BlockCopyEvent));
++
++      ring->CommandPort = (ioaddr_t) NULL;
++      return (ENOMEM);
++    }
++    
++    if (! (ring->pDoneBlk = ep_alloc_main (&rail->Generic, entries * sizeof (E3_uint32), 0, &ring->epDoneBlk)))
++    {
++      ep_free_elan (&rail->Generic, ring->epEvent, entries * sizeof (E3_BlockCopyEvent));
++      ep_free_elan (&rail->Generic, ring->epDma,   entries * sizeof (E3_DMA));
++
++      ring->CommandPort = (ioaddr_t) NULL;
++      return (ENOMEM);
++    }
++    
++    if (MapDeviceRegister (rail->Device, ELAN3_BAR_COMMAND_PORT, &ring->CommandPage, pgnum * PAGE_SIZE, PAGE_SIZE, &ring->CommandPageHandle) != ESUCCESS)
++    {
++      ep_free_elan (&rail->Generic, ring->epEvent,   entries * sizeof (E3_BlockCopyEvent));
++      ep_free_elan (&rail->Generic, ring->epDma,     entries * sizeof (E3_DMA));
++      ep_free_main (&rail->Generic, ring->epDoneBlk, entries * sizeof (E3_uint32));
++
++      ring->CommandPort = (ioaddr_t) NULL;
++      return (ENOMEM);
++    }
++    ring->CommandPort = ring->CommandPage + pgoff;
++      
++    for (s = 0; s < entries; s++)
++    {
++      /* setup the event */
++      elan3_sdram_writel(rail->Device, DMA_RING_EVENT(ring,s) + offsetof(E3_BlockCopyEvent,ev_Type),   
++                         EV_TYPE_BCOPY | EV_TYPE_DMA | DMA_RING_DMA_ELAN(ring, s));
++      elan3_sdram_writel(rail->Device, DMA_RING_EVENT(ring,s) + offsetof(E3_BlockCopyEvent,ev_Source), DMA_RING_DMA_ELAN(ring,s)  | EV_WCOPY);
++      elan3_sdram_writel(rail->Device, DMA_RING_EVENT(ring,s) + offsetof(E3_BlockCopyEvent,ev_Dest),   DMA_RING_DONE_ELAN(ring,s) | EV_TYPE_BCOPY_WORD );         
++
++      /* need to set all the doneBlks to appear that they have completed */
++      ring->pDoneBlk[s] = DMA_RING_DMA_ELAN(ring,s)  | EV_WCOPY;
++    }
++
++    return 0; /* success */
++}
++
++static void
++DmaRingRelease(EP3_RAIL *rail, EP3_DMA_RING *ring)
++{
++    if (ring->CommandPage != (ioaddr_t) 0)
++    {
++      UnmapDeviceRegister(rail->Device, &ring->CommandPageHandle);
++
++      ep_free_elan (&rail->Generic, ring->epEvent,   ring->Entries * sizeof (E3_BlockCopyEvent));
++      ep_free_elan (&rail->Generic, ring->epDma,     ring->Entries * sizeof (E3_DMA));
++      ep_free_main (&rail->Generic, ring->epDoneBlk, ring->Entries * sizeof (E3_uint32));
++    }
++    ring->CommandPage = (ioaddr_t) 0;
++}
++
++void 
++DmaRingsRelease (EP3_RAIL *rail)
++{
++    DmaRingRelease (rail, &rail->DmaRings[EP3_RING_CRITICAL]);
++    DmaRingRelease (rail, &rail->DmaRings[EP3_RING_HIGH_PRI]);
++    DmaRingRelease (rail, &rail->DmaRings[EP3_RING_LOW_PRI]);
++}
++
++int 
++DmaRingsCreate (EP3_RAIL *rail)
++{
++    if (DmaRingCreate (rail, &rail->DmaRings[EP3_RING_CRITICAL], ELAN3_DMARING_BASE_CONTEXT_NUM + EP3_RING_CRITICAL, EP3_RING_CRITICAL_LEN) ||
++      DmaRingCreate (rail, &rail->DmaRings[EP3_RING_HIGH_PRI], ELAN3_DMARING_BASE_CONTEXT_NUM + EP3_RING_HIGH_PRI, EP3_RING_HIGH_PRI_LEN) ||
++      DmaRingCreate (rail, &rail->DmaRings[EP3_RING_LOW_PRI],  ELAN3_DMARING_BASE_CONTEXT_NUM + EP3_RING_LOW_PRI,  EP3_RING_LOW_PRI_LEN))
++    {
++      DmaRingsRelease (rail);
++      return (ENOMEM);
++    }
++  
++    return 0;
++}
++
++static int 
++DmaRingNextSlot (EP3_DMA_RING *ring)
++{
++    int pos  = ring->Position;
++    int npos = DMA_RING_NEXT_POS(ring);
++
++    if (ring->pDoneBlk[npos] == EP3_EVENT_ACTIVE)
++      return (-1);
++    
++    ring->pDoneBlk[pos] = EP3_EVENT_ACTIVE;
++
++    ring->Position = npos; /* move on one */
++
++    return (pos);
++}
++
++
++/****************************************************************************************/
++/*
++ * Dma/event command issueing - these handle cproc queue overflow traps.
++ */
++static int
++DmaRunQueueSizeCheck (EP3_RAIL *rail, E3_uint32 len)
++{
++    E3_uint64  FandBPtr = read_reg64 (rail->Device, DProc_SysCntx_FPtr);
++    E3_uint32  FPtr, BPtr;
++    E3_uint32  qlen;
++
++#if (BYTE_ORDER == LITTLE_ENDIAN) || defined(__LITTLE_ENDIAN__)
++    FPtr = (FandBPtr & 0xFFFFFFFFull);
++    BPtr = (FandBPtr >> 32);
++#else
++    FPtr = (FandBPtr >> 32);
++    BPtr = (FandBPtr & 0xFFFFFFFFull);
++#endif
++    
++    qlen = (((BPtr - FPtr)/sizeof (E3_DMA)) & (E3_SysCntxQueueSize-1));
++    
++    if      (qlen < 4)   IncrStat (rail, DmaQueueLength[0]);
++    else if (qlen < 8)   IncrStat (rail, DmaQueueLength[1]);
++    else if (qlen < 16)  IncrStat (rail, DmaQueueLength[2]);
++    else if (qlen < 32)  IncrStat (rail, DmaQueueLength[3]);
++    else if (qlen < 64)  IncrStat (rail, DmaQueueLength[4]);
++    else if (qlen < 128) IncrStat (rail, DmaQueueLength[5]);
++    else if (qlen < 240) IncrStat (rail, DmaQueueLength[6]);
++    else                 IncrStat (rail, DmaQueueLength[7]);
++      
++    return (qlen < len);
++}
++
++int
++IssueDma (EP3_RAIL *rail, E3_DMA_BE * dmabe, int type, int retryThread)
++{
++    ELAN3_DEV     *dev = rail->Device;
++    EP3_RETRY_DMA *retry;
++    EP3_DMA_RING  *ring;
++    int           slot;
++    int           i, res;
++    unsigned long flags;
++
++    ASSERT (dmabe->s.dma_direction == DMA_WRITE || dmabe->s.dma_direction == DMA_READ_REQUEUE);
++
++    ASSERT (! EP_VP_ISDATA(dmabe->s.dma_destVProc) ||
++          (dmabe->s.dma_direction == DMA_WRITE ? 
++           EP_VP_TO_NODE(dmabe->s.dma_srcVProc) == rail->Generic.Position.pos_nodeid :
++           EP_VP_TO_NODE(dmabe->s.dma_destVProc) == rail->Generic.Position.pos_nodeid));
++    
++    /*
++     * If we're not the retry thread - then don't issue this DMA
++     * if there are any already queued on the retry lists with
++     * higher or equal priority than this one that are ready to
++     * retry.
++     */
++    if (! retryThread)
++    {
++      for (i = EP_RETRY_BASE; i < type; i++)
++      {
++          if (list_empty (&rail->DmaRetries[i]))
++              continue;
++
++          retry = list_entry (rail->DmaRetries[i].next, EP3_RETRY_DMA, Link);
++              
++          if (AFTER (lbolt, retry->RetryTime))
++          {
++              IncrStat (rail, IssueDmaFail[type]);
++              return (ISSUE_COMMAND_RETRY);
++          }
++      }
++    }
++
++    /*
++     * Depending on the type of DMA we're issuing - throttle back
++     * issueing of it if the DMA run queue is too full.  This then
++     * prioritises the "special" messages and completing data 
++     * transfers which have matched a receive buffer.
++     */
++
++    if (type >= EP_RETRY_LOW_PRI_RETRY)
++    {
++      if (! DmaRunQueueSizeCheck (rail, E3_SysCntxQueueSize / 2))
++      {
++          IncrStat (rail, IssueDmaFail[type]);
++          return (ISSUE_COMMAND_RETRY);
++      }
++      ring = &rail->DmaRings[EP3_RING_LOW_PRI];
++    } 
++    else if (type == EP_RETRY_LOW_PRI)
++    {
++      if (! DmaRunQueueSizeCheck (rail, E3_SysCntxQueueSize / 3))
++      {
++          IncrStat (rail, IssueDmaFail[type]);
++          return (ISSUE_COMMAND_RETRY);
++      }
++      ring = &rail->DmaRings[EP3_RING_LOW_PRI];
++    }
++    else if (type >= EP_RETRY_HIGH_PRI)
++      ring = &rail->DmaRings[EP3_RING_HIGH_PRI];
++    else
++      ring = &rail->DmaRings[EP3_RING_CRITICAL];
++
++    local_irq_save (flags);
++    if (! spin_trylock (&dev->CProcLock))
++    {
++      IncrStat (rail, IssueDmaFail[type]);
++
++      res = ISSUE_COMMAND_RETRY;
++    }
++    else
++    {
++      if ((slot = DmaRingNextSlot (ring)) == -1)
++      {
++          IncrStat (rail, IssueDmaFail[type]);
++          
++          res = ISSUE_COMMAND_RETRY;
++      }
++      else
++      {
++          EPRINTF4 (DBG_COMMAND, "IssueDma: type %08x size %08x Elan source %08x Elan dest %08x\n",
++                    dmabe->s.dma_type, dmabe->s.dma_size, dmabe->s.dma_source, dmabe->s.dma_dest);
++          EPRINTF2 (DBG_COMMAND, "          dst event %08x cookie/proc %08x\n",
++                    dmabe->s.dma_destEvent, dmabe->s.dma_destCookieVProc);
++          EPRINTF2 (DBG_COMMAND, "          src event %08x cookie/proc %08x\n",
++                    dmabe->s.dma_srcEvent, dmabe->s.dma_srcCookieVProc);
++
++          elan3_sdram_copyq_to_sdram (dev,  dmabe,  DMA_RING_DMA(ring, slot), sizeof (E3_DMA));                       /* PCI write block */
++          elan3_sdram_writel (dev, DMA_RING_EVENT(ring, slot) + offsetof (E3_BlockCopyEvent, ev_Count), 1);   /* PCI write */
++          
++          mb();                                                               /* ensure writes to main memory completed */
++          writel (DMA_RING_EVENT_ELAN(ring,slot), ring->CommandPort + offsetof (E3_CommandPort, SetEvent));
++          mmiob();                                                            /* and flush through IO writes */
++          
++          res = ISSUE_COMMAND_OK;
++      }
++      spin_unlock (&dev->CProcLock);
++    }
++    local_irq_restore (flags);
++
++    return (res);
++}
++
++int
++IssueWaitevent (EP3_RAIL *rail, E3_Addr value)
++{
++    ELAN3_DEV     *dev   = rail->Device;
++    int           res;
++    unsigned long flags;
++    
++    spin_lock_irqsave (&dev->IntrLock, flags);
++
++    ASSERT (rail->CommandPortEventTrap == FALSE);
++
++    /*
++     * Disable the command processor interrupts, so that we don't see
++     * spurious interrupts appearing.
++     */
++    DISABLE_INT_MASK (dev, INT_CProc | INT_ComQueue);
++
++    EPRINTF1 (DBG_COMMAND, "IssueWaitevent: %08x\n", value);
++
++    mb();                                                             /* ensure writes to main memory completed */
++    writel (value, rail->CommandPort + offsetof (E3_CommandPort, WaitEvent0));
++    mmiob();                                                          /* and flush through IO writes */
++    
++    do {
++      res = CheckCommandQueueFlushed (rail->Ctxt, EventComQueueNotEmpty, ISSUE_COMMAND_CANT_WAIT, &flags);
++
++      EPRINTF1 (DBG_COMMAND, "IssueWaitevent: CheckCommandQueueFlushed -> %d\n", res);
++
++      if (res == ISSUE_COMMAND_WAIT)
++          HandleCProcTrap (dev, 0, NULL);
++    } while (res != ISSUE_COMMAND_OK);
++
++    if (! rail->CommandPortEventTrap)
++      res = ISSUE_COMMAND_OK;
++    else
++    {
++      rail->CommandPortEventTrap = FALSE;
++      res = ISSUE_COMMAND_TRAPPED;
++    }
++
++    EPRINTF1 (DBG_COMMAND, "IssueWaitevent: -> %d\n", res);
++
++    /*
++     * Re-enable the command processor interrupt as we've finished 
++     * polling it.
++     */
++    ENABLE_INT_MASK (dev, INT_CProc | INT_ComQueue);
++
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++
++    return (res);
++}
++
++void
++IssueSetevent (EP3_RAIL *rail, E3_Addr value)
++{
++    EPRINTF1 (DBG_COMMAND, "IssueSetevent: %08x\n", value);
++
++    mb();                                                             /* ensure writes to main memory completed */
++    writel (value, rail->CommandPort + offsetof (E3_CommandPort, SetEvent));
++    mmiob();                                                          /* and flush through IO writes */
++}
++
++void
++IssueRunThread (EP3_RAIL *rail, E3_Addr value)
++{
++    EPRINTF1 (DBG_COMMAND, "IssueRunThread: %08x\n", value);
++
++    mb();                                                             /* ensure writes to main memory completed */
++    writel (value, rail->CommandPort + offsetof (E3_CommandPort, RunThread));
++    mmiob();                                                          /* and flush through IO writes */
++}
++
++/****************************************************************************************/
++/*
++ * DMA retry list management
++ */
++static unsigned DmaRetryTimes[EP_NUM_RETRIES]; 
++
++static void
++ep3_dma_retry (EP3_RAIL *rail)
++{
++    EP3_COOKIE    *cp;
++    int            res;
++    int                  vp;
++    unsigned long  flags;
++    int            i;
++
++    kernel_thread_init("ep3_dma_retry");
++
++    spin_lock_irqsave (&rail->DmaRetryLock, flags);
++
++    for (;;)
++    {
++      long yieldAt   = lbolt + (hz/10);
++      long retryTime = 0;
++
++      if (rail->DmaRetryThreadShouldStop)
++          break;
++      
++      for (i = EP_RETRY_BASE; i < EP_NUM_RETRIES; i++)
++      {
++          while (! list_empty (&rail->DmaRetries[i]))
++          {
++              EP3_RETRY_DMA *retry = list_entry (rail->DmaRetries[i].next, EP3_RETRY_DMA, Link);
++
++              if (! AFTER (lbolt, retry->RetryTime))
++                  break;
++              
++              if (rail->DmaRetryThreadShouldStall || AFTER (lbolt, yieldAt))
++                  goto cant_do_more;
++
++              EPRINTF2 (DBG_RETRY, "%s: DmaRetryThread: retry %p\n", rail->Generic.Name, retry);
++              EPRINTF5 (DBG_RETRY, "%s:                 %08x %08x %08x %08x\n",
++                        rail->Generic.Name, retry->Dma.s.dma_type, retry->Dma.s.dma_size, retry->Dma.s.dma_source, retry->Dma.s.dma_dest);
++              EPRINTF5 (DBG_RETRY, "%s:                 %08x %08x %08x %08x\n",
++                        rail->Generic.Name, retry->Dma.s.dma_destEvent, retry->Dma.s.dma_destCookieVProc,
++                        retry->Dma.s.dma_srcEvent, retry->Dma.s.dma_srcCookieVProc);
++#if defined(DEBUG)
++              if (retry->Dma.s.dma_direction == DMA_WRITE)
++                  cp = LookupEventCookie (rail, &rail->CookieTable, retry->Dma.s.dma_srcEvent);
++              else
++                  cp = LookupEventCookie (rail, &rail->CookieTable, retry->Dma.s.dma_destEvent);
++
++              ASSERT (cp != NULL || (retry->Dma.s.dma_srcEvent == 0 && retry->Dma.s.dma_direction == DMA_WRITE && retry->Dma.s.dma_isRemote));
++              
++              if (cp && cp->Operations->DmaVerify)
++                  cp->Operations->DmaVerify (rail, cp->Arg, &retry->Dma);
++#endif
++
++#if defined(DEBUG_ASSERT)
++              if (retry->Dma.s.dma_direction == DMA_WRITE)
++                  vp = retry->Dma.s.dma_destVProc;
++              else
++                  vp = retry->Dma.s.dma_srcVProc;
++
++              ASSERT (!EP_VP_ISDATA(vp) || 
++                      (rail->Generic.Nodes[EP_VP_TO_NODE(vp)].State >= EP_NODE_CONNECTED &&
++                       rail->Generic.Nodes[EP_VP_TO_NODE(vp)].State <= EP_NODE_LOCAL_PASSIVATE));
++#endif
++              spin_unlock_irqrestore (&rail->DmaRetryLock, flags);
++              res = IssueDma (rail, &(retry->Dma), i, TRUE);
++              spin_lock_irqsave (&rail->DmaRetryLock, flags);
++              
++              if (res != ISSUE_COMMAND_OK)
++                  goto cant_do_more;
++              
++              /* Command issued, so remove from list, and add to free list */
++              list_del (&retry->Link);
++              list_add (&retry->Link, &rail->DmaRetryFreeList);
++          }
++      }
++    cant_do_more:
++      
++      for (i = EP_RETRY_BASE; i < EP_NUM_RETRIES; i++)
++      {
++          if (!list_empty (&rail->DmaRetries[i]))
++          {
++              EP3_RETRY_DMA *retry = list_entry (rail->DmaRetries[i].next, EP3_RETRY_DMA, Link);
++
++              retryTime = retryTime ? MIN(retryTime, retry->RetryTime) : retry->RetryTime;
++          }
++      }
++
++      if (retryTime && !AFTER (retryTime, lbolt))
++          retryTime = lbolt + 1;
++
++      do {
++          EPRINTF3 (DBG_RETRY, "%s: ep_cm_retry: %s %lx\n", rail->Generic.Name, rail->DmaRetryThreadShouldStall ? "stalled" : "sleeping", retryTime);
++          
++          if (rail->DmaRetryTime == 0 || (retryTime != 0 && retryTime < rail->DmaRetryTime))
++              rail->DmaRetryTime = retryTime;
++          
++          rail->DmaRetrySleeping = TRUE;
++          
++          if (rail->DmaRetryThreadShouldStall)                                        /* wakeup threads waiting in StallDmaRetryThread */
++              kcondvar_wakeupall (&rail->DmaRetryWait, &rail->DmaRetryLock);  /* for us to really go to sleep for good. */
++
++          if (rail->DmaRetryTime == 0 || rail->DmaRetryThreadShouldStall)
++              kcondvar_wait (&rail->DmaRetryWait, &rail->DmaRetryLock, &flags);
++          else
++              kcondvar_timedwait (&rail->DmaRetryWait, &rail->DmaRetryLock, &flags, rail->DmaRetryTime);
++
++          rail->DmaRetrySleeping = FALSE;
++
++      } while (rail->DmaRetryThreadShouldStall);
++
++      rail->DmaRetryTime = 0;
++    }
++
++    rail->DmaRetryThreadStopped = 1;
++    kcondvar_wakeupall (&rail->DmaRetryWait, &rail->DmaRetryLock);
++    spin_unlock_irqrestore (&rail->DmaRetryLock, flags);
++
++    kernel_thread_exit();
++}
++
++void
++StallDmaRetryThread (EP3_RAIL *rail)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&rail->DmaRetryLock, flags);
++    rail->DmaRetryThreadShouldStall++;
++
++    while (! rail->DmaRetrySleeping)
++      kcondvar_wait (&rail->DmaRetryWait, &rail->DmaRetryLock, &flags);
++    spin_unlock_irqrestore (&rail->DmaRetryLock, flags);
++}
++
++void 
++ResumeDmaRetryThread (EP3_RAIL *rail)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&rail->DmaRetryLock, flags);
++
++    ASSERT (rail->DmaRetrySleeping);
++
++    if (--rail->DmaRetryThreadShouldStall == 0)
++    {
++      rail->DmaRetrySleeping = 0;
++      kcondvar_wakeupone (&rail->DmaRetryWait, &rail->DmaRetryLock);
++    }
++    spin_unlock_irqrestore (&rail->DmaRetryLock, flags);
++}
++
++int
++InitialiseDmaRetries (EP3_RAIL *rail)
++{
++    int i;
++
++    spin_lock_init (&rail->DmaRetryLock);
++    kcondvar_init (&rail->DmaRetryWait);
++
++    for (i = 0; i < EP_NUM_RETRIES; i++)
++      INIT_LIST_HEAD (&rail->DmaRetries[i]);
++
++    INIT_LIST_HEAD (&rail->DmaRetryFreeList);
++
++    DmaRetryTimes[EP_RETRY_HIGH_PRI]  = EP_RETRY_HIGH_PRI_TIME;
++
++    for (i =0 ; i < EP_NUM_BACKOFF; i++)
++      DmaRetryTimes[EP_RETRY_HIGH_PRI_RETRY+i] = EP_RETRY_HIGH_PRI_TIME << i;
++    
++    DmaRetryTimes[EP_RETRY_LOW_PRI] = EP_RETRY_LOW_PRI_TIME;
++
++    for (i =0 ; i < EP_NUM_BACKOFF; i++)
++      DmaRetryTimes[EP_RETRY_LOW_PRI_RETRY+i] = EP_RETRY_LOW_PRI_TIME << i;
++    
++    DmaRetryTimes[EP_RETRY_ANONYMOUS] = EP_RETRY_ANONYMOUS_TIME;
++    DmaRetryTimes[EP_RETRY_NETERR]    = EP_RETRY_NETERR_TIME;
++
++    rail->DmaRetryInitialised = 1;
++
++    if (kernel_thread_create (ep3_dma_retry, (void *) rail) == 0)
++    {
++      spin_lock_destroy (&rail->DmaRetryLock);
++      return (ENOMEM);
++    }
++
++    rail->DmaRetryThreadStarted = 1;
++
++    return (ESUCCESS);
++}
++
++void
++DestroyDmaRetries (EP3_RAIL *rail)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&rail->DmaRetryLock, flags);
++    rail->DmaRetryThreadShouldStop = 1;
++    while (rail->DmaRetryThreadStarted && !rail->DmaRetryThreadStopped)
++    {
++      kcondvar_wakeupall (&rail->DmaRetryWait, &rail->DmaRetryLock);
++      kcondvar_wait (&rail->DmaRetryWait, &rail->DmaRetryLock, &flags);
++    }
++    rail->DmaRetryThreadStarted = 0;
++    rail->DmaRetryThreadStopped = 0;
++    rail->DmaRetryThreadShouldStop = 0;
++    rail->DmaRetryInitialised = 0;
++
++    spin_unlock_irqrestore (&rail->DmaRetryLock, flags);
++
++    /* Everyone should have given back their retry dma's by now */
++    ASSERT (rail->DmaRetryReserved == 0);
++
++    while (! list_empty (&rail->DmaRetryFreeList))
++    {
++      EP3_RETRY_DMA *retry = list_entry (rail->DmaRetryFreeList.next, EP3_RETRY_DMA, Link);
++      
++      list_del (&retry->Link);
++
++      KMEM_FREE (retry, sizeof (EP3_RETRY_DMA));
++    }
++
++    kcondvar_destroy (&rail->DmaRetryWait);
++    spin_lock_destroy (&rail->DmaRetryLock);
++}
++
++int
++ReserveDmaRetries (EP3_RAIL *rail, int count, EP_ATTRIBUTE attr)
++{
++    EP3_RETRY_DMA *retry;
++    int                 remaining = count;
++    unsigned long flags;
++
++    spin_lock_irqsave (&rail->DmaRetryLock, flags);
++    
++    if (remaining <= (rail->DmaRetryCount - rail->DmaRetryReserved))
++    {
++      rail->DmaRetryReserved += remaining;
++
++      spin_unlock_irqrestore (&rail->DmaRetryLock, flags);
++      return (ESUCCESS);
++    }
++
++    remaining -= (rail->DmaRetryCount - rail->DmaRetryReserved);
++
++    rail->DmaRetryReserved = rail->DmaRetryCount;
++
++    spin_unlock_irqrestore (&rail->DmaRetryLock, flags);
++
++    while (remaining)
++    {
++      KMEM_ALLOC (retry, EP3_RETRY_DMA *, sizeof (EP3_RETRY_DMA), !(attr & EP_NO_SLEEP));
++      
++      if (retry == NULL)
++          goto failed;
++
++      /* clear E3_DMA */
++      bzero((char *)(&(retry->Dma.s)), sizeof(E3_DMA));
++
++      remaining--; 
++
++      spin_lock_irqsave (&rail->DmaRetryLock, flags);
++
++      list_add (&retry->Link, &rail->DmaRetryFreeList);
++
++      rail->DmaRetryCount++;
++      rail->DmaRetryReserved++;
++
++      spin_unlock_irqrestore (&rail->DmaRetryLock, flags);
++    }
++    return (ESUCCESS);
++
++ failed:
++    spin_lock_irqsave (&rail->DmaRetryLock, flags);
++    rail->DmaRetryReserved -= (count - remaining);
++    spin_unlock_irqrestore (&rail->DmaRetryLock, flags);
++    return (ENOMEM);
++}
++
++void
++ReleaseDmaRetries (EP3_RAIL *rail, int count)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&rail->DmaRetryLock, flags);
++    rail->DmaRetryReserved -= count;
++    spin_unlock_irqrestore (&rail->DmaRetryLock, flags);
++}
++
++void
++QueueDmaForRetry (EP3_RAIL *rail, E3_DMA_BE *dma, int interval)
++{
++    EP3_RETRY_DMA *retry;
++    unsigned long flags;
++
++    /*
++     * When requeueing DMAs they must never be "READ" dma's since
++     * these would fetch the DMA descriptor from the retryn descriptor
++     */
++    ASSERT (dma->s.dma_direction == DMA_WRITE || dma->s.dma_direction == DMA_READ_REQUEUE);
++    ASSERT (dma->s.dma_direction == DMA_WRITE ? 
++          EP_VP_TO_NODE(dma->s.dma_srcVProc) == rail->Generic.Position.pos_nodeid :
++          EP_VP_TO_NODE(dma->s.dma_destVProc) == rail->Generic.Position.pos_nodeid);
++
++    spin_lock_irqsave (&rail->DmaRetryLock, flags);
++    
++    EP_ASSERT (&rail->Generic, !list_empty (&rail->DmaRetryFreeList));
++
++    /* take an item of the free list */
++    retry = list_entry (rail->DmaRetryFreeList.next, EP3_RETRY_DMA, Link);
++
++    list_del (&retry->Link);
++    
++    EPRINTF5 (DBG_RETRY, "%s: QueueDmaForRetry: %08x %08x %08x %08x\n", rail->Generic.Name,
++            dma->s.dma_type, dma->s.dma_size, dma->s.dma_source, dma->s.dma_dest);
++    EPRINTF5 (DBG_RETRY, "%s:                   %08x %08x %08x %08x\n",rail->Generic.Name,
++           dma->s.dma_destEvent, dma->s.dma_destCookieVProc,
++           dma->s.dma_srcEvent, dma->s.dma_srcCookieVProc);
++
++    /* copy the DMA into the retry descriptor */
++    retry->Dma.s.dma_type            = dma->s.dma_type;
++    retry->Dma.s.dma_size            = dma->s.dma_size;
++    retry->Dma.s.dma_source          = dma->s.dma_source;
++    retry->Dma.s.dma_dest            = dma->s.dma_dest;
++    retry->Dma.s.dma_destEvent       = dma->s.dma_destEvent;
++    retry->Dma.s.dma_destCookieVProc = dma->s.dma_destCookieVProc;
++    retry->Dma.s.dma_srcEvent        = dma->s.dma_srcEvent;
++    retry->Dma.s.dma_srcCookieVProc  = dma->s.dma_srcCookieVProc;
++
++    retry->RetryTime = lbolt + DmaRetryTimes[interval];
++
++    /* chain onto the end of the approriate retry list */
++    list_add_tail (&retry->Link, &rail->DmaRetries[interval]);
++
++    /* now wakeup the retry thread */
++    if (rail->DmaRetryTime == 0 || retry->RetryTime < rail->DmaRetryTime)
++      rail->DmaRetryTime = retry->RetryTime;
++    
++    if (rail->DmaRetrySleeping && !rail->DmaRetryThreadShouldStall)
++    {
++      rail->DmaRetrySleeping = 0;
++      kcondvar_wakeupone (&rail->DmaRetryWait, &rail->DmaRetryLock);
++    }
++
++    spin_unlock_irqrestore (&rail->DmaRetryLock, flags);
++}
++
++void
++QueueDmaOnStalledList (EP3_RAIL *rail, E3_DMA_BE *dma)
++{
++    EP_NODE_RAIL *nodeRail = &rail->Generic.Nodes[dma->s.dma_direction == DMA_WRITE ? 
++                                                EP_VP_TO_NODE(dma->s.dma_srcVProc) :
++                                                EP_VP_TO_NODE(dma->s.dma_destVProc)];
++    EP3_RETRY_DMA *retry;
++    unsigned long flags;
++
++    /*
++     * When requeueing DMAs they must never be "READ" dma's since
++     * these would fetch the DMA descriptor from the retryn descriptor
++     */
++    ASSERT (dma->s.dma_direction == DMA_WRITE || dma->s.dma_direction == DMA_READ_REQUEUE);
++    ASSERT (dma->s.dma_direction == DMA_WRITE ? 
++          EP_VP_TO_NODE(dma->s.dma_srcVProc) == rail->Generic.Position.pos_nodeid :
++          EP_VP_TO_NODE(dma->s.dma_destVProc) == rail->Generic.Position.pos_nodeid);
++
++    spin_lock_irqsave (&rail->DmaRetryLock, flags);
++    
++    EP_ASSERT (&rail->Generic, !list_empty (&rail->DmaRetryFreeList));
++
++    /* take an item of the free list */
++    retry = list_entry (rail->DmaRetryFreeList.next, EP3_RETRY_DMA, Link);
++
++    list_del (&retry->Link);
++    
++    EPRINTF5 (DBG_RETRY, "%s: QueueDmaOnStalledList: %08x %08x %08x %08x\n", rail->Generic.Name,
++            dma->s.dma_type, dma->s.dma_size, dma->s.dma_source, dma->s.dma_dest);
++    EPRINTF5 (DBG_RETRY, "%s:                        %08x %08x %08x %08x\n", rail->Generic.Name,
++            dma->s.dma_destEvent, dma->s.dma_destCookieVProc,
++            dma->s.dma_srcEvent, dma->s.dma_srcCookieVProc);
++
++    /* copy the DMA into the retry descriptor */
++    retry->Dma.s.dma_type            = dma->s.dma_type;
++    retry->Dma.s.dma_size            = dma->s.dma_size;
++    retry->Dma.s.dma_source          = dma->s.dma_source;
++    retry->Dma.s.dma_dest            = dma->s.dma_dest;
++    retry->Dma.s.dma_destEvent       = dma->s.dma_destEvent;
++    retry->Dma.s.dma_destCookieVProc = dma->s.dma_destCookieVProc;
++    retry->Dma.s.dma_srcEvent        = dma->s.dma_srcEvent;
++    retry->Dma.s.dma_srcCookieVProc  = dma->s.dma_srcCookieVProc;
++
++    /* chain onto the node cancelled dma list */
++    list_add_tail (&retry->Link, &nodeRail->StalledDmas);
++
++    spin_unlock_irqrestore (&rail->DmaRetryLock, flags);
++}
++
++void
++FreeStalledDmas (EP3_RAIL *rail, unsigned int nodeId)
++{
++    EP_NODE_RAIL *nodeRail = &rail->Generic.Nodes[nodeId];
++    struct list_head *el, *nel;
++    unsigned long flags;
++
++    spin_lock_irqsave (&rail->DmaRetryLock, flags);
++    list_for_each_safe (el, nel, &nodeRail->StalledDmas) {
++      list_del (el);
++      list_add (el, &rail->DmaRetryFreeList);
++    }
++    spin_unlock_irqrestore (&rail->DmaRetryLock, flags);
++}
++
++/****************************************************************************************/
++/*
++ * Connection management.
++ */
++static void
++DiscardingHaltOperation (ELAN3_DEV *dev, void *arg)
++{
++    EP3_RAIL *rail = (EP3_RAIL *) arg;
++    unsigned long flags;
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++    rail->HaltOpCompleted = 1;
++    kcondvar_wakeupall (&rail->HaltOpSleep, &dev->IntrLock);
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++}
++ 
++typedef struct {
++     EP3_RAIL  *rail;
++    sdramaddr_t qaddr;
++} SetQueueFullData;
++ 
++static void
++SetQueueLockedOperation (ELAN3_DEV *dev, void *arg)
++{
++    SetQueueFullData *data =  (SetQueueFullData *) arg;
++    unsigned long     flags;     
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++
++    elan3_sdram_writel  (dev, data->qaddr, E3_QUEUE_LOCKED | elan3_sdram_readl(dev, data->qaddr));
++   
++    data->rail->HaltOpCompleted = 1;
++    kcondvar_wakeupall (&data->rail->HaltOpSleep, &dev->IntrLock);
++
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++}
++
++static void
++FlushDmaQueuesHaltOperation (ELAN3_DEV *dev, void *arg)
++{
++    EP3_RAIL      *rail    = (EP3_RAIL *) arg;
++    sdramaddr_t    FPtr, BPtr;
++    sdramaddr_t          Base, Top;
++    E3_DMA_BE      dma;
++    EP_NODE_RAIL  *node;
++    int            vp;
++    unsigned long  flags;
++
++    ASSERT (elan3_sdram_readl (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProc.s.FSR)) == 0);
++    ASSERT (elan3_sdram_readl (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProcData0.s.FSR.Status)) == 0);
++    ASSERT (elan3_sdram_readl (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProcData1.s.FSR.Status)) == 0);
++    ASSERT (elan3_sdram_readl (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProcData2.s.FSR.Status)) == 0);
++    ASSERT (elan3_sdram_readl (dev, dev->TAndQBase + offsetof (E3_TrapAndQueue, DProcData3.s.FSR.Status)) == 0);
++    
++    FPtr  = read_reg32 (dev, DProc_SysCntx_FPtr);
++    BPtr =  read_reg32 (dev, DProc_SysCntx_BPtr);
++    Base  = dev->TAndQBase + offsetof (E3_TrapAndQueue, SysCntxDmaQueue[0]);
++    Top   = dev->TAndQBase + offsetof (E3_TrapAndQueue, SysCntxDmaQueue[E3_SysCntxQueueSize-1]);
++    
++    while (FPtr != BPtr)
++    {
++      elan3_sdram_copyq_from_sdram (dev, FPtr, &dma, sizeof (E3_DMA_BE));
++      
++      EPRINTF5 (DBG_DISCON, "%s: FlushDmaQueuesHaltOperation: %08x %08x %08x %08x\n", rail->Generic.Name,
++                dma.s.dma_type, dma.s.dma_size, dma.s.dma_source, dma.s.dma_dest);
++      EPRINTF5 (DBG_DISCON, "%s:                              %08x %08x %08x %08x\n", rail->Generic.Name,
++                dma.s.dma_destEvent, dma.s.dma_destCookieVProc,
++               dma.s.dma_srcEvent, dma.s.dma_srcCookieVProc);
++      
++      ASSERT ((dma.s.dma_u.s.Context & SYS_CONTEXT_BIT) != 0);
++
++      if (dma.s.dma_direction == DMA_WRITE)
++          vp = dma.s.dma_destVProc;
++      else
++          vp = dma.s.dma_srcVProc;
++      
++      node = &rail->Generic.Nodes[EP_VP_TO_NODE(vp)];
++
++      ASSERT (!EP_VP_ISDATA(vp) || (node->State >= EP_NODE_CONNECTED && node->State <= EP_NODE_LOCAL_PASSIVATE));
++
++      if (EP_VP_ISDATA(vp) && node->State == EP_NODE_LOCAL_PASSIVATE)
++      {
++          /*
++           * This is a DMA going to the node which is being removed, 
++           * so move it onto the node dma list where it will get
++           * handled later.
++           */
++          EPRINTF1 (DBG_DISCON, "%s: FlushDmaQueuesHaltOperation: move dma to cancelled list\n", rail->Generic.Name);
++         
++          if (dma.s.dma_direction != DMA_WRITE)
++          {
++              /* for read dma's set the DMA_READ_REQUEUE bits as the dma_source has been 
++               * modified by the elan to point at the dma in the rxd where it was issued
++               * from */
++              dma.s.dma_direction = (dma.s.dma_direction & ~DMA_READ) | DMA_READ_REQUEUE;
++          }
++          
++          QueueDmaOnStalledList (rail, &dma);
++          
++          /*
++           * Remove the DMA from the queue by replacing it with one with
++           * zero size and no events.
++           *
++           * NOTE: we must preserve the SYS_CONTEXT_BIT since the Elan uses this
++           * to mark the approriate run queue as empty.
++           */
++          dma.s.dma_type            = (SYS_CONTEXT_BIT << 16);
++          dma.s.dma_size            = 0;
++          dma.s.dma_source          = (E3_Addr) 0;
++          dma.s.dma_dest            = (E3_Addr) 0;
++          dma.s.dma_destEvent       = (E3_Addr) 0;
++          dma.s.dma_destCookieVProc = 0;
++          dma.s.dma_srcEvent        = (E3_Addr) 0;
++          dma.s.dma_srcCookieVProc  = 0;
++          
++          elan3_sdram_copyq_to_sdram (dev, &dma, FPtr, sizeof (E3_DMA_BE));
++      }
++
++      FPtr = (FPtr == Top) ? Base : FPtr + sizeof (E3_DMA);
++    }
++
++    spin_lock_irqsave (&dev->IntrLock, flags);
++    rail->HaltOpCompleted = 1;
++    kcondvar_wakeupall (&rail->HaltOpSleep, &dev->IntrLock);
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++}
++
++void
++SetQueueLocked (EP3_RAIL *rail, sdramaddr_t qaddr)
++{
++    ELAN3_DEV        *dev = rail->Device;
++    SetQueueFullData  data;
++    unsigned long     flags;
++    
++    /* Ensure that the context filter changes have been seen by halting
++     * then restarting the inputters - this also ensures that any setevent
++     * commands used to issue dma's have completed and any trap has been
++     * handled. */
++    data.rail  = rail;
++    data.qaddr = qaddr;
++
++    kmutex_lock (&rail->HaltOpMutex);
++    spin_lock_irqsave (&dev->IntrLock, flags);
++    QueueHaltOperation (dev, 0, NULL, INT_DiscardingSysCntx | INT_TProcHalted, SetQueueLockedOperation, &data);
++
++    while (! rail->HaltOpCompleted)
++      kcondvar_wait (&rail->HaltOpSleep, &dev->IntrLock, &flags);
++    rail->HaltOpCompleted = 0;
++
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++    kmutex_unlock (&rail->HaltOpMutex);
++}
++
++void
++ep3_flush_filters (EP_RAIL *r)
++{
++    EP3_RAIL *rail = (EP3_RAIL *) r;
++    ELAN3_DEV *dev  = rail->Device;
++    unsigned long flags;
++
++    /* Ensure that the context filter changes have been seen by halting
++     * then restarting the inputters - this also ensures that any setevent
++     * commands used to issue dma's have completed and any trap has been
++     * handled. */
++    kmutex_lock (&rail->HaltOpMutex);
++    spin_lock_irqsave (&dev->IntrLock, flags);
++    QueueHaltOperation (dev, 0, NULL, INT_DiscardingSysCntx, DiscardingHaltOperation, rail);
++    
++    while (! rail->HaltOpCompleted)
++      kcondvar_wait (&rail->HaltOpSleep, &dev->IntrLock, &flags);
++    rail->HaltOpCompleted = 0;
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++    kmutex_unlock (&rail->HaltOpMutex);
++}
++
++void
++ep3_flush_queues (EP_RAIL *r)
++{
++    EP3_RAIL         *rail = (EP3_RAIL *) r;
++    ELAN3_DEV         *dev  = rail->Device;
++    struct list_head *el;
++    struct list_head *nel;
++    EP_NODE_RAIL     *node;
++    unsigned long flags;
++    int vp, i;
++
++    ASSERT (NO_LOCKS_HELD);
++    
++    /* First - stall the dma retry thread, so that it will no longer
++     *         restart any dma's from the rety lists. */
++    StallDmaRetryThread (rail);
++
++    /* Second - queue a halt operation to flush through all DMA's which are executing
++     *          or on the run queue. */
++    kmutex_lock (&rail->HaltOpMutex);
++    spin_lock_irqsave (&dev->IntrLock, flags);
++    QueueHaltOperation (dev, 0, NULL, INT_DProcHalted | INT_TProcHalted, FlushDmaQueuesHaltOperation, rail);
++    while (! rail->HaltOpCompleted)
++      kcondvar_wait (&rail->HaltOpSleep, &dev->IntrLock, &flags);
++    rail->HaltOpCompleted = 0;
++    spin_unlock_irqrestore (&dev->IntrLock, flags);
++    kmutex_unlock (&rail->HaltOpMutex);
++
++    /* Third - run down the dma retry lists and move all entries to the cancelled
++     *         list.  Any dma's which were on the run queues have already been
++     *         moved there */
++    spin_lock_irqsave (&rail->DmaRetryLock, flags);
++    for (i = EP_RETRY_BASE; i < EP_NUM_RETRIES; i++)
++    {
++      list_for_each_safe (el, nel, &rail->DmaRetries[i]) {
++          EP3_RETRY_DMA *retry = list_entry (el, EP3_RETRY_DMA, Link);
++
++          if (retry->Dma.s.dma_direction == DMA_WRITE)
++              vp = retry->Dma.s.dma_destVProc;
++          else
++              vp = retry->Dma.s.dma_srcVProc;
++          
++          node = &rail->Generic.Nodes[EP_VP_TO_NODE(vp)];
++          
++          ASSERT (!EP_VP_ISDATA(vp) || (node->State >= EP_NODE_CONNECTED && node->State <= EP_NODE_LOCAL_PASSIVATE));
++
++          if (EP_VP_ISDATA(vp) && node->State == EP_NODE_LOCAL_PASSIVATE)
++          {
++              EPRINTF5 (DBG_DISCON, "%s: FlushDmaQueues: %08x %08x %08x %08x\n",rail->Generic.Name,
++                        retry->Dma.s.dma_type, retry->Dma.s.dma_size, retry->Dma.s.dma_source, retry->Dma.s.dma_dest);
++              EPRINTF5 (DBG_DISCON, "%s:                 %08x %08x %08x %08x\n", rail->Generic.Name,
++                        retry->Dma.s.dma_destEvent, retry->Dma.s.dma_destCookieVProc,
++                        retry->Dma.s.dma_srcEvent, retry->Dma.s.dma_srcCookieVProc);
++
++              list_del (&retry->Link);
++
++              list_add_tail (&retry->Link, &node->StalledDmas);
++          }
++      }
++    }
++    spin_unlock_irqrestore (&rail->DmaRetryLock, flags);
++
++    /* Finally - allow the dma retry thread to run again */
++    ResumeDmaRetryThread (rail);
++}
++
++/****************************************************************************************/
++/* NOTE - we require that all cookies are non-zero, which is 
++ *        achieved because EP_VP_DATA() is non-zero for all
++ *        nodes */
++E3_uint32
++LocalCookie (EP3_RAIL *rail, unsigned remoteNode)
++{
++    E3_uint32     cookie;
++    unsigned long flags;
++
++    spin_lock_irqsave (&rail->CookieLock, flags);
++    cookie = DMA_COOKIE (rail->MainCookies[remoteNode], EP_VP_DATA(rail->Generic.Position.pos_nodeid));
++    spin_unlock_irqrestore (&rail->CookieLock, flags);
++
++    /* Main processor cookie for srcCookie - this is what is sent
++     * to the remote node along with the setevent from the put
++     * or the dma descriptor for a get */
++    return (cookie);
++}
++
++E3_uint32
++RemoteCookie (EP3_RAIL *rail, u_int remoteNode)
++{
++    uint32_t      cookie;
++    unsigned long flags;
++
++    spin_lock_irqsave (&rail->CookieLock, flags);
++    cookie = DMA_REMOTE_COOKIE (rail->MainCookies[remoteNode], EP_VP_DATA(remoteNode));
++    spin_unlock_irqrestore (&rail->CookieLock, flags);
++
++    /* Main processor cookie for dstCookie - this is the cookie
++     * that the "remote put" dma uses for it's setevent packets for
++     * a get dma */
++    
++    return (cookie);
++}
++
++/****************************************************************************************/
++/*
++ * Event Cookie management.
++ *
++ *   We find the ep_cookie in one of two ways:
++ *     1) for block copy events
++ *          the cookie value is stored in the ev_Source - for EVIRQ events
++ *          it is also stored in the ev_Type
++ *     2) for normal events
++ *          we just use the event address.
++ */
++void 
++InitialiseCookieTable (EP3_COOKIE_TABLE *table)
++{
++    register int i;
++    
++    spin_lock_init (&table->Lock);
++    
++    for (i = 0; i < EP3_COOKIE_HASH_SIZE; i++)
++      table->Entries[i] = NULL;
++}
++
++void
++DestroyCookieTable (EP3_COOKIE_TABLE *table)
++{
++    register int i;
++
++    for (i = 0; i < EP3_COOKIE_HASH_SIZE; i++)
++      if (table->Entries[i])
++          printk ("DestroyCookieTable: entry %d not empty\n", i);
++
++    spin_lock_destroy (&table->Lock);
++}
++
++void
++RegisterCookie (EP3_COOKIE_TABLE *table, EP3_COOKIE *cp, E3_uint32 cookie, EP3_COOKIE_OPS *ops, void *arg)
++{
++    EP3_COOKIE *tcp;
++    int hashval = EP3_HASH_COOKIE(cookie);
++    unsigned long flags;
++
++    spin_lock_irqsave (&table->Lock, flags);
++    
++    cp->Operations = ops;
++    cp->Arg        = arg;
++    cp->Cookie     = cookie;
++    
++#if defined(DEBUG)
++    /* Check that the cookie is unique */
++    for (tcp = table->Entries[hashval]; tcp; tcp = tcp->Next)
++      if (tcp->Cookie == cookie)
++          panic ("RegisterEventCookie: non unique cookie\n");
++#endif
++    cp->Next = table->Entries[hashval];
++    
++    table->Entries[hashval] = cp;
++    
++    spin_unlock_irqrestore (&table->Lock, flags);
++}
++
++void
++DeregisterCookie (EP3_COOKIE_TABLE *table, EP3_COOKIE *cp)
++{
++    EP3_COOKIE **predCookiep;
++    unsigned long flags;
++
++    spin_lock_irqsave (&table->Lock, flags);
++    
++    for (predCookiep = &table->Entries[EP3_HASH_COOKIE (cp->Cookie)]; *predCookiep; predCookiep = &(*predCookiep)->Next)
++    {
++      if (*predCookiep == cp)
++      {
++          *predCookiep = cp->Next;
++          break;
++      }
++    }
++
++    spin_unlock_irqrestore (&table->Lock, flags);
++
++    cp->Operations = NULL;
++    cp->Arg        = NULL;
++    cp->Cookie     = 0;
++    cp->Next       = NULL;
++}
++
++EP3_COOKIE *
++LookupCookie (EP3_COOKIE_TABLE *table, E3_Addr cookie)
++{
++    EP3_COOKIE *cp;
++    unsigned long flags;
++
++    spin_lock_irqsave (&table->Lock, flags);
++    
++    for (cp = table->Entries[EP3_HASH_COOKIE(cookie)]; cp; cp = cp->Next)
++      if (cp->Cookie == cookie)
++          break;
++    
++    spin_unlock_irqrestore (&table->Lock, flags);
++    return (cp);
++}
++
++EP3_COOKIE *
++LookupEventCookie (EP3_RAIL *rail, EP3_COOKIE_TABLE *table, E3_Addr eaddr)
++{
++    sdramaddr_t event;
++    E3_uint32 type;
++
++    if ((event = ep_elan2sdram (&rail->Generic, eaddr)) != (sdramaddr_t) 0)
++    {
++      type = elan3_sdram_readl (rail->Device, event + offsetof (E3_BlockCopyEvent, ev_Type));
++
++      if (type & EV_TYPE_BCOPY)
++          return (LookupCookie (table, elan3_sdram_readl (rail->Device, event + offsetof (E3_BlockCopyEvent, ev_Source)) & ~EV_WCOPY));
++      else
++          return (LookupCookie (table, eaddr));
++    }
++
++    return (NULL);
++}
++
++/****************************************************************************************/
++/*
++ * Elan context operations - note only support interrupt ops.
++ */
++static int        ep3_event     (ELAN3_CTXT *ctxt, E3_uint32 cookie, int flag);
++static int        ep3_dprocTrap (ELAN3_CTXT *ctxt, DMA_TRAP *trap);
++static int        ep3_tprocTrap (ELAN3_CTXT *ctxt, THREAD_TRAP *trap);
++static int        ep3_iprocTrap (ELAN3_CTXT *ctxt, INPUT_TRAP *trap, int chan);
++static int        ep3_cprocTrap (ELAN3_CTXT *ctxt, COMMAND_TRAP *trap);
++static int        ep3_cprocReissue (ELAN3_CTXT *ctxt, CProcTrapBuf_BE *tbuf);
++
++static E3_uint8   ep3_load8 (ELAN3_CTXT *ctxt, E3_Addr addr);
++static void       ep3_store8 (ELAN3_CTXT *ctxt, E3_Addr addr, E3_uint8 val);
++static E3_uint16  ep3_load16 (ELAN3_CTXT *ctxt, E3_Addr addr);
++static void       ep3_store16 (ELAN3_CTXT *ctxt, E3_Addr addr, E3_uint16 val);
++static E3_uint32  ep3_load32 (ELAN3_CTXT *ctxt, E3_Addr addr);
++static void       ep3_store32 (ELAN3_CTXT *ctxt, E3_Addr addr, E3_uint32 val);
++static E3_uint64  ep3_load64 (ELAN3_CTXT *ctxt, E3_Addr addr);
++static void       ep3_store64 (ELAN3_CTXT *ctxt, E3_Addr addr, E3_uint64 val);
++
++ELAN3_OPS ep3_elan3_ops = 
++{
++    ELAN3_OPS_VERSION,                /* Version */
++    
++    NULL,                     /* Exception */
++    NULL,                     /* GetWordItem */
++    NULL,                     /* GetBlockItem */
++    NULL,                     /* PutWordItem */
++    NULL,                     /* PutBlockItem */
++    NULL,                     /* PutbackItem */
++    NULL,                     /* FreeWordItem */
++    NULL,                     /* FreeBlockItem */
++    NULL,                     /* CountItems */
++    ep3_event,                        /* Event */
++    NULL,                     /* SwapIn */
++    NULL,                     /* SwapOut */
++    NULL,                     /* FreePrivate */
++    NULL,                     /* FixupNetworkError */
++    ep3_dprocTrap,            /* DProcTrap */
++    ep3_tprocTrap,            /* TProcTrap */
++    ep3_iprocTrap,            /* IProcTrap */
++    ep3_cprocTrap,            /* CProcTrap */
++    ep3_cprocReissue,         /* CProcReissue */
++    NULL,                     /* StartFaultCheck */
++    NULL,                     /* EndFaulCheck */
++    ep3_load8,                        /* Load8 */
++    ep3_store8,                       /* Store8 */
++    ep3_load16,                       /* Load16 */
++    ep3_store16,              /* Store16 */
++    ep3_load32,                       /* Load32 */
++    ep3_store32,              /* Store32 */
++    ep3_load64,                       /* Load64 */
++    ep3_store64,              /* Store64 */
++};
++
++static int
++ep3_event (ELAN3_CTXT *ctxt, E3_uint32 cookie, int flag)
++{
++    EP3_RAIL  *rail = (EP3_RAIL *) ctxt->Private;
++    EP3_COOKIE *cp   = LookupCookie (&rail->CookieTable, cookie);
++    
++    if (cp == NULL)
++    {
++      printk ("ep3_event: cannot find event cookie for %x\n", cookie);
++      return (OP_HANDLED);
++    }
++    
++    if (cp->Operations->Event)
++      cp->Operations->Event(rail, cp->Arg);
++    
++    return (OP_HANDLED);
++}
++
++/* Trap interface */
++int
++ep3_dprocTrap (ELAN3_CTXT *ctxt, DMA_TRAP *trap)
++{
++    EP3_RAIL        *rail = (EP3_RAIL *) ctxt->Private;
++    ELAN3_DEV        *dev = rail->Device;
++    EP3_COOKIE       *cp;
++    E3_FaultSave_BE *FaultArea;
++    E3_uint16        vp;
++    int                    validTrap;
++    int                    numFaults;
++    int                    i;
++    sdramaddr_t      event;
++    E3_uint32        type;
++    sdramaddr_t      dma;
++    E3_DMA_BE        dmabe;
++    int              status = EAGAIN;
++
++    EPRINTF4 (DBG_EPTRAP, "ep3_dprocTrap: WakeupFnt=%x Cntx=%x SuspAddr=%x TrapType=%s\n",
++            trap->Status.s.WakeupFunction, trap->Status.s.Context, 
++            trap->Status.s.SuspendAddr, MiToName (trap->Status.s.TrapType));
++    EPRINTF4 (DBG_EPTRAP, "              type %08x size %08x source %08x dest %08x\n",
++            trap->Desc.s.dma_type, trap->Desc.s.dma_size, trap->Desc.s.dma_source, trap->Desc.s.dma_dest);
++    EPRINTF2 (DBG_EPTRAP, "              Dest event %08x cookie/proc %08x\n",
++            trap->Desc.s.dma_destEvent, trap->Desc.s.dma_destCookieVProc);
++    EPRINTF2 (DBG_EPTRAP, "              Source event %08x cookie/proc %08x\n",
++            trap->Desc.s.dma_srcEvent, trap->Desc.s.dma_srcCookieVProc);
++
++    ASSERT (trap->Status.s.Context & SYS_CONTEXT_BIT);
++
++    switch (trap->Status.s.TrapType)
++    {
++    case MI_DmaPacketTimedOutOrPacketError:
++      if (trap->Desc.s.dma_direction == DMA_WRITE)
++          vp = trap->Desc.s.dma_destVProc;
++      else
++          vp = trap->Desc.s.dma_srcVProc;
++      
++      if (! trap->PacketInfo.s.PacketTimeout)
++          status = ETIMEDOUT;
++      else
++      {
++          status = EHOSTDOWN;
++
++          /* XXXX: dma timedout - might want to "restart" tree ? */
++      }
++      goto retry_dma;
++
++    case MI_DmaFailCountError:
++      goto retry_dma;
++
++    case MI_TimesliceDmaQueueOverflow:
++      IncrStat (rail, DprocDmaQueueOverflow);
++
++      goto retry_dma;
++
++    case MI_RemoteDmaCommand:
++    case MI_RunDmaCommand:
++    case MI_DequeueNonSysCntxDma:
++    case MI_DequeueSysCntxDma:
++      /*
++       * The DMA processor has trapped due to outstanding prefetches from the previous 
++       * dma.  The "current" dma has not been consumed, so we just ignore the trap
++       */
++      return (OP_HANDLED);
++      
++    case MI_EventQueueOverflow:
++      IncrStat (rail, DprocEventQueueOverflow);
++
++      if ((event = ep_elan2sdram (&rail->Generic, trap->Desc.s.dma_srcEvent)) != (sdramaddr_t) 0 &&
++          ((type  = elan3_sdram_readl (dev, event + offsetof(E3_Event,ev_Type))) & EV_TYPE_MASK_EVIRQ) == EV_TYPE_EVIRQ)
++      {
++          spin_unlock (&ctxt->Device->IntrLock);
++          ep3_event (ctxt, (type & ~(EV_TYPE_MASK_EVIRQ | EV_TYPE_MASK_BCOPY)), OP_LWP);
++          spin_lock (&ctxt->Device->IntrLock);
++      }
++      return (OP_HANDLED);
++      
++    case MI_DmaQueueOverflow:
++      IncrStat (rail, DprocDmaQueueOverflow);
++
++      if ((event = ep_elan2sdram (&rail->Generic, trap->Desc.s.dma_srcEvent)) != (sdramaddr_t) 0 &&
++          ((type = elan3_sdram_readl (dev, event + offsetof (E3_Event, ev_Type))) & EV_TYPE_MASK_DMA) == EV_TYPE_DMA &&
++          (dma  = ep_elan2sdram (&rail->Generic, (type & ~EV_TYPE_MASK2))) != (sdramaddr_t) 0)
++      {
++          elan3_sdram_copyq_from_sdram (dev, dma, &dmabe, sizeof (E3_DMA));
++          
++          /* We only chain together DMA's of the same direction, so since
++           * we took a DmaQueueOverflow trap - this means that DMA which
++           * trapped was a WRITE dma - hence the one we chain to must also
++           * be a WRITE dma.
++           */
++          ASSERT (dmabe.s.dma_direction == DMA_WRITE);
++          
++          cp = LookupEventCookie (rail, &rail->CookieTable, dmabe.s.dma_srcEvent);
++
++#ifdef DEBUG_ASSERT
++          {
++              E3_uint16     vp       = dmabe.s.dma_destVProc;
++              EP_NODE_RAIL *nodeRail = &rail->Generic.Nodes[EP_VP_TO_NODE(vp)];
++
++              ASSERT (cp != NULL && (!EP_VP_ISDATA(vp) || (nodeRail->State >= EP_NODE_CONNECTED && nodeRail->State <= EP_NODE_LOCAL_PASSIVATE)));
++          }
++#endif
++          cp->Operations->DmaRetry (rail, cp->Arg, &dmabe, EAGAIN);
++          
++          return (OP_HANDLED);
++      }
++
++      panic ("ep3_dprocTrap\n");
++      return (OP_HANDLED);
++
++    default:
++      break;
++    }
++
++    /* If it's a dma which traps past the end of the source, then */
++    /* just re-issue it */
++    numFaults = validTrap = (trap->FaultSave.s.FSR.Status != 0);
++    for (i = 0, FaultArea = &trap->Data0; i < 4; i++, FaultArea++)
++    {
++      if (FaultArea->s.FSR.Status != 0)
++      {
++          numFaults++;
++
++          /* XXXX: Rev B Elans can prefetch data past the end of the dma descriptor */
++          /*       if the fault relates to this, then just ignore it */
++          if (FaultArea->s.FaultAddress >= (trap->Desc.s.dma_source+trap->Desc.s.dma_size))
++          {
++              static int i;
++              if (i < 10 && i++ < 10)
++                  printk ("ep3_dprocTrap: Rev B prefetch trap error %08x %08x\n",
++                           FaultArea->s.FaultAddress, (trap->Desc.s.dma_source+trap->Desc.s.dma_size));
++              continue;
++          }
++
++          validTrap++;
++      }
++    }
++
++    /*
++     * NOTE: for physical errors (uncorrectable ECC/PCI parity errors) the FSR will
++     *       be zero - hence we will not see any faults - and none will be valid, 
++     *       so only ignore a Rev B prefetch trap if we've seen some faults. Otherwise
++     *       we can reissue a DMA which has already sent it's remote event !
++     */
++    if (numFaults != 0 && validTrap == 0)
++    {
++    retry_dma:
++      if (trap->Desc.s.dma_direction == DMA_WRITE)
++      {
++          vp = trap->Desc.s.dma_destVProc;
++          cp = LookupEventCookie (rail, &rail->CookieTable, trap->Desc.s.dma_srcEvent);
++      }
++      else
++      {
++          ASSERT (EP3_CONTEXT_ISDATA(trap->Desc.s.dma_queueContext) || trap->Desc.s.dma_direction == DMA_READ_REQUEUE);
++
++          vp = trap->Desc.s.dma_srcVProc;
++          cp = LookupEventCookie (rail, &rail->CookieTable, trap->Desc.s.dma_destEvent);
++
++          /* for read dma's set the DMA_READ_REQUEUE bits as the dma_source has been 
++           * modified by the elan to point at the dma in the rxd where it was issued
++           * from */
++          trap->Desc.s.dma_direction = (trap->Desc.s.dma_direction & ~DMA_READ) | DMA_READ_REQUEUE;
++      }
++
++#ifdef DEBUG_ASSERT
++      {
++          EP_NODE_RAIL *nodeRail = &rail->Generic.Nodes[EP_VP_TO_NODE(vp)];
++
++          ASSERT (!EP_VP_ISDATA(vp) || (nodeRail->State >= EP_NODE_CONNECTED && nodeRail->State <= EP_NODE_LOCAL_PASSIVATE));
++      }
++#endif
++      
++      if (cp != NULL)
++          cp->Operations->DmaRetry (rail, cp->Arg, &trap->Desc, status);
++      else
++      {
++          ASSERT (trap->Desc.s.dma_direction == DMA_WRITE && trap->Desc.s.dma_srcEvent == 0 && trap->Desc.s.dma_isRemote);
++
++          QueueDmaForRetry (rail, &trap->Desc, EP_RETRY_ANONYMOUS);
++      }
++
++      return (OP_HANDLED);
++    }
++    
++    printk ("ep3_dprocTrap: WakeupFnt=%x Cntx=%x SuspAddr=%x TrapType=%s\n",
++           trap->Status.s.WakeupFunction, trap->Status.s.Context, 
++           trap->Status.s.SuspendAddr, MiToName (trap->Status.s.TrapType));
++    printk ("                    FaultAddr=%x EventAddr=%x FSR=%x\n",
++           trap->FaultSave.s.FaultAddress, trap->FaultSave.s.EventAddress,
++           trap->FaultSave.s.FSR.Status);
++    for (i = 0, FaultArea = &trap->Data0; i < 4; i++, FaultArea++)
++      printk ("                  %d FaultAddr=%x EventAddr=%x FSR=%x\n", i,
++               FaultArea->s.FaultAddress, FaultArea->s.EventAddress, FaultArea->s.FSR.Status);
++    
++    printk ("                  type %08x size %08x source %08x dest %08x\n",
++           trap->Desc.s.dma_type, trap->Desc.s.dma_size, trap->Desc.s.dma_source, trap->Desc.s.dma_dest);
++    printk ("                  Dest event %08x cookie/proc %08x\n",
++           trap->Desc.s.dma_destEvent, trap->Desc.s.dma_destCookieVProc);
++    printk ("                  Source event %08x cookie/proc %08x\n",
++           trap->Desc.s.dma_srcEvent, trap->Desc.s.dma_srcCookieVProc);
++
++//    panic ("ep3_dprocTrap");
++
++    return (OP_HANDLED);
++}
++
++int
++ep3_tprocTrap (ELAN3_CTXT *ctxt, THREAD_TRAP *trap)
++{
++    EP3_RAIL *rail = (EP3_RAIL *) ctxt->Private;
++
++    EPRINTF6 (DBG_EPTRAP, "ep3_tprocTrap: SP=%08x PC=%08x NPC=%08x DIRTY=%08x TRAP=%08x MI=%s\n",
++            trap->sp, trap->pc, trap->npc, trap->DirtyBits.Bits, trap->TrapBits.Bits, MiToName (trap->mi));
++    EPRINTF4 (DBG_EPTRAP, "              g0=%08x g1=%08x g2=%08x g3=%08x\n", 
++            trap->Registers[REG_GLOBALS+(0^WordEndianFlip)], trap->Registers[REG_GLOBALS+(1^WordEndianFlip)], 
++            trap->Registers[REG_GLOBALS+(2^WordEndianFlip)], trap->Registers[REG_GLOBALS+(3^WordEndianFlip)]);
++    EPRINTF4 (DBG_EPTRAP, "              g4=%08x g5=%08x g6=%08x g7=%08x\n", 
++            trap->Registers[REG_GLOBALS+(4^WordEndianFlip)], trap->Registers[REG_GLOBALS+(5^WordEndianFlip)], 
++            trap->Registers[REG_GLOBALS+(6^WordEndianFlip)], trap->Registers[REG_GLOBALS+(7^WordEndianFlip)]);
++    EPRINTF4 (DBG_EPTRAP, "              o0=%08x o1=%08x o2=%08x o3=%08x\n", 
++            trap->Registers[REG_OUTS+(0^WordEndianFlip)], trap->Registers[REG_OUTS+(1^WordEndianFlip)], 
++            trap->Registers[REG_OUTS+(2^WordEndianFlip)], trap->Registers[REG_OUTS+(3^WordEndianFlip)]);
++    EPRINTF4 (DBG_EPTRAP, "              o4=%08x o5=%08x o6=%08x o7=%08x\n", 
++            trap->Registers[REG_OUTS+(4^WordEndianFlip)], trap->Registers[REG_OUTS+(5^WordEndianFlip)], 
++            trap->Registers[REG_OUTS+(6^WordEndianFlip)], trap->Registers[REG_OUTS+(7^WordEndianFlip)]);
++    EPRINTF4 (DBG_EPTRAP, "              l0=%08x l1=%08x l2=%08x l3=%08x\n", 
++            trap->Registers[REG_LOCALS+(0^WordEndianFlip)], trap->Registers[REG_LOCALS+(1^WordEndianFlip)], 
++            trap->Registers[REG_LOCALS+(2^WordEndianFlip)], trap->Registers[REG_LOCALS+(3^WordEndianFlip)]);
++    EPRINTF4 (DBG_EPTRAP, "              l4=%08x l5=%08x l6=%08x l7=%08x\n", 
++            trap->Registers[REG_LOCALS+(4^WordEndianFlip)], trap->Registers[REG_LOCALS+(5^WordEndianFlip)], 
++            trap->Registers[REG_LOCALS+(6^WordEndianFlip)], trap->Registers[REG_LOCALS+(7^WordEndianFlip)]);
++    EPRINTF4 (DBG_EPTRAP, "              i0=%08x i1=%08x i2=%08x i3=%08x\n", 
++            trap->Registers[REG_INS+(0^WordEndianFlip)], trap->Registers[REG_INS+(1^WordEndianFlip)], 
++            trap->Registers[REG_INS+(2^WordEndianFlip)], trap->Registers[REG_INS+(3^WordEndianFlip)]);
++    EPRINTF4 (DBG_EPTRAP, "              i4=%08x i5=%08x i6=%08x i7=%08x\n", 
++            trap->Registers[REG_INS+(4^WordEndianFlip)], trap->Registers[REG_INS+(5^WordEndianFlip)], 
++            trap->Registers[REG_INS+(6^WordEndianFlip)], trap->Registers[REG_INS+(7^WordEndianFlip)]);
++    
++    ASSERT (trap->Status.s.Context & SYS_CONTEXT_BIT);
++
++    switch (trap->mi)
++    {
++    case MI_UnimplementedError:
++      if (trap->TrapBits.s.ForcedTProcTrap)
++      {
++          ASSERT (trap->TrapBits.s.OutputWasOpen == 0);
++          
++          EPRINTF0 (DBG_EPTRAP, "ep3_tprocTrap: ForcedTProcTrap\n");
++
++          IssueRunThread (rail, SaveThreadToStack (ctxt, trap, FALSE));
++          return (OP_HANDLED);
++      }
++
++      if (trap->TrapBits.s.ThreadTimeout)
++      {
++          EPRINTF0 (DBG_EPTRAP, "ep3_tprocTrap: ThreadTimeout\n");
++
++          if (trap->Registers[REG_GLOBALS + (1^WordEndianFlip)] == 0)
++              RollThreadToClose (ctxt, trap, trap->TrapBits.s.PacketAckValue);
++          else
++          {
++              CompleteEnvelope (rail, trap->Registers[REG_GLOBALS + (1^WordEndianFlip)], trap->TrapBits.s.PacketAckValue);
++
++              RollThreadToClose (ctxt, trap, EP3_PAckStolen);
++          }
++              
++          IssueRunThread (rail, SaveThreadToStack (ctxt, trap, FALSE));
++          return (OP_HANDLED);
++      }
++
++      if (trap->TrapBits.s.Unimplemented)
++      {
++          E3_uint32 instr = ELAN3_OP_LOAD32 (ctxt, trap->pc & PC_MASK);
++
++          PRINTF1 (ctxt, DBG_EPTRAP, "ep3_tprocTrap: unimplemented instruction %08x\n", instr);
++
++          if ((instr & OPCODE_MASK) == OPCODE_Ticc &&
++              (instr & OPCODE_IMM)  == OPCODE_IMM &&
++              (Ticc_COND(instr)     == Ticc_TA))
++          {
++              switch (INSTR_IMM(instr))
++              {
++              case EP3_UNIMP_TRAP_NO_DESCS:
++                  StallThreadForNoDescs (rail, trap->Registers[REG_GLOBALS + (1^WordEndianFlip)], 
++                                         SaveThreadToStack (ctxt, trap, TRUE));
++                  return (OP_HANDLED);
++
++              case EP3_UNIMP_TRAP_PACKET_NACKED:
++                  CompleteEnvelope (rail, trap->Registers[REG_GLOBALS + (1^WordEndianFlip)], E3_PAckDiscard);
++
++                  IssueRunThread (rail, SaveThreadToStack (ctxt, trap, TRUE));
++                  return (OP_HANDLED);
++
++              case EP3_UNIMP_THREAD_HALTED: 
++                  StallThreadForHalted (rail, trap->Registers[REG_GLOBALS + (1^WordEndianFlip)], 
++                                        SaveThreadToStack (ctxt, trap, TRUE));
++                  return (OP_HANDLED);
++
++              default:
++                  break;
++                  
++              }
++          }
++      }
++      break;
++
++    default:
++      break;
++    }
++
++    /* All other traps should not happen for kernel comms */
++    printk ("ep3_tprocTrap: SP=%08x PC=%08x NPC=%08x DIRTY=%08x TRAP=%08x MI=%s\n",
++           trap->sp, trap->pc, trap->npc, trap->DirtyBits.Bits,
++           trap->TrapBits.Bits, MiToName (trap->mi));
++    printk ("              FaultSave : FaultAddress %08x EventAddress %08x FSR %08x\n",
++           trap->FaultSave.s.FaultAddress, trap->FaultSave.s.EventAddress, trap->FaultSave.s.FSR.Status);
++    printk ("              DataFault : FaultAddress %08x EventAddress %08x FSR %08x\n",
++           trap->DataFaultSave.s.FaultAddress, trap->DataFaultSave.s.EventAddress, trap->DataFaultSave.s.FSR.Status);
++    printk ("              InstFault : FaultAddress %08x EventAddress %08x FSR %08x\n",
++           trap->InstFaultSave.s.FaultAddress, trap->InstFaultSave.s.EventAddress, trap->InstFaultSave.s.FSR.Status);
++    printk ("              OpenFault : FaultAddress %08x EventAddress %08x FSR %08x\n",
++           trap->OpenFaultSave.s.FaultAddress, trap->OpenFaultSave.s.EventAddress, trap->OpenFaultSave.s.FSR.Status);
++
++    if (trap->DirtyBits.s.GlobalsDirty)
++    {
++      printk ("              g0=%08x g1=%08x g2=%08x g3=%08x\n", 
++               trap->Registers[REG_GLOBALS+(0^WordEndianFlip)], trap->Registers[REG_GLOBALS+(1^WordEndianFlip)], 
++               trap->Registers[REG_GLOBALS+(2^WordEndianFlip)], trap->Registers[REG_GLOBALS+(3^WordEndianFlip)]);
++      printk ("              g4=%08x g5=%08x g6=%08x g7=%08x\n", 
++               trap->Registers[REG_GLOBALS+(4^WordEndianFlip)], trap->Registers[REG_GLOBALS+(5^WordEndianFlip)], 
++               trap->Registers[REG_GLOBALS+(6^WordEndianFlip)], trap->Registers[REG_GLOBALS+(7^WordEndianFlip)]);
++    }
++    if (trap->DirtyBits.s.OutsDirty)
++    {
++      printk ("              o0=%08x o1=%08x o2=%08x o3=%08x\n", 
++               trap->Registers[REG_OUTS+(0^WordEndianFlip)], trap->Registers[REG_OUTS+(1^WordEndianFlip)], 
++               trap->Registers[REG_OUTS+(2^WordEndianFlip)], trap->Registers[REG_OUTS+(3^WordEndianFlip)]);
++      printk ("              o4=%08x o5=%08x o6=%08x o7=%08x\n", 
++               trap->Registers[REG_OUTS+(4^WordEndianFlip)], trap->Registers[REG_OUTS+(5^WordEndianFlip)], 
++               trap->Registers[REG_OUTS+(6^WordEndianFlip)], trap->Registers[REG_OUTS+(7^WordEndianFlip)]);
++    }
++    if (trap->DirtyBits.s.LocalsDirty)
++    {
++      printk ("              l0=%08x l1=%08x l2=%08x l3=%08x\n", 
++               trap->Registers[REG_LOCALS+(0^WordEndianFlip)], trap->Registers[REG_LOCALS+(1^WordEndianFlip)], 
++               trap->Registers[REG_LOCALS+(2^WordEndianFlip)], trap->Registers[REG_LOCALS+(3^WordEndianFlip)]);
++      printk ("              l4=%08x l5=%08x l6=%08x l7=%08x\n", 
++               trap->Registers[REG_LOCALS+(4^WordEndianFlip)], trap->Registers[REG_LOCALS+(5^WordEndianFlip)], 
++               trap->Registers[REG_LOCALS+(6^WordEndianFlip)], trap->Registers[REG_LOCALS+(7^WordEndianFlip)]);
++    }
++    if (trap->DirtyBits.s.InsDirty)
++    {
++      printk ("              i0=%08x i1=%08x i2=%08x i3=%08x\n", 
++               trap->Registers[REG_INS+(0^WordEndianFlip)], trap->Registers[REG_INS+(1^WordEndianFlip)], 
++               trap->Registers[REG_INS+(2^WordEndianFlip)], trap->Registers[REG_INS+(3^WordEndianFlip)]);
++      printk ("              i4=%08x i5=%08x i6=%08x i7=%08x\n", 
++               trap->Registers[REG_INS+(4^WordEndianFlip)], trap->Registers[REG_INS+(5^WordEndianFlip)], 
++               trap->Registers[REG_INS+(6^WordEndianFlip)], trap->Registers[REG_INS+(7^WordEndianFlip)]);
++    }
++    
++//    panic ("ep3_tprocTrap");
++
++    return (OP_HANDLED);
++}
++
++int
++ep3_iprocTrap (ELAN3_CTXT *ctxt, INPUT_TRAP *trap, int channel)
++{
++    EP3_RAIL      *rail = (EP3_RAIL *) ctxt->Private;
++    ELAN3_DEV      *dev = ctxt->Device;
++    EP3_COOKIE    *cp;
++    sdramaddr_t    event;
++    E3_uint32      type;
++    sdramaddr_t    dma;
++    E3_DMA_BE      dmabe;
++
++    ASSERT (trap->Transactions[0].s.TrTypeCntx.s.Context & SYS_CONTEXT_BIT);
++
++    /*
++     * first process the trap to determine the cause
++     */
++    InspectIProcTrap (ctxt, trap);
++
++    if (! trap->AckSent && trap->LockQueuePointer)            /* Must be a network error in a queueing DMA */
++    {                                                         /* packet - unlock the queue */
++      IncrStat (rail, QueueingPacketTrap);
++
++      SimulateUnlockQueue (ctxt, trap->LockQueuePointer, FALSE);
++      return (OP_HANDLED);
++    }
++
++    if (trap->AckSent && trap->BadTransaction)
++    {
++      spin_unlock (&dev->IntrLock);
++
++      /* NOTE - no network error fixup is necessary for system context
++       *        messages since they are idempotent and are single packet 
++       *        dmas
++       */
++      if (EP3_CONTEXT_ISDATA (trap->Transactions[0].s.TrTypeCntx.s.Context))
++      {
++          int nodeId = EP3_CONTEXT_TO_NODE(trap->Transactions[0].s.TrTypeCntx.s.Context);
++          
++          if (trap->DmaIdentifyTransaction)
++              ep_queue_network_error (&rail->Generic, nodeId, EP_NODE_NETERR_ATOMIC_PACKET, channel, trap->DmaIdentifyTransaction->s.TrAddr);
++          else if (trap->ThreadIdentifyTransaction)
++              ep_queue_network_error (&rail->Generic, nodeId, EP_NODE_NETERR_ATOMIC_PACKET, channel, trap->ThreadIdentifyTransaction->s.TrAddr);
++          else
++              ep_queue_network_error (&rail->Generic, nodeId, EP_NODE_NETERR_DMA_PACKET, channel, 0);
++      }
++
++      spin_lock (&dev->IntrLock);
++      return (OP_HANDLED);
++    }
++    
++    if (trap->AckSent)
++    {
++      if (trap->TrappedTransaction == NULL)
++          return (OP_HANDLED);
++      
++      while (! trap->TrappedTransaction->s.TrTypeCntx.s.LastTrappedTrans)
++      {
++          E3_IprocTrapHeader_BE *hdrp  = trap->TrappedTransaction;
++          E3_IprocTrapData_BE   *datap = trap->TrappedDataBuffer;
++          
++          ASSERT (hdrp->s.TrTypeCntx.s.StatusRegValid != 0);
++          
++          if ((hdrp->s.TrTypeCntx.s.Type & TR_WRITEBLOCK_BIT) != 0)
++          {
++              printk ("ep3_iprocTrap: WRITEBLOCK : Addr %x\n", hdrp->s.TrAddr);
++//            panic ("ep3_iprocTrap\n");
++          }
++          else
++          {
++              switch (hdrp->s.TrTypeCntx.s.Type & TR_OPCODE_TYPE_MASK)
++              {
++              case TR_SETEVENT & TR_OPCODE_TYPE_MASK:
++                  switch (GET_STATUS_TRAPTYPE (hdrp->s.IProcTrapStatus))
++                  {
++                  case MI_DmaQueueOverflow:
++                      IncrStat (rail, IprocDmaQueueOverflow);
++
++                      if ((event = ep_elan2sdram (&rail->Generic, hdrp->s.TrAddr)) != (sdramaddr_t) 0 &&
++                          ((type = elan3_sdram_readl (dev, event + offsetof (E3_Event, ev_Type))) & EV_TYPE_MASK_DMA) == EV_TYPE_DMA &&
++                          (dma  = ep_elan2sdram (&rail->Generic, (type & ~EV_TYPE_MASK2))) != (sdramaddr_t) 0)
++                      {
++                          elan3_sdram_copyq_from_sdram (dev, dma, &dmabe, sizeof (E3_DMA));
++                          
++                          if (dmabe.s.dma_direction == DMA_WRITE)
++                              cp = LookupEventCookie (rail, &rail->CookieTable, dmabe.s.dma_srcEvent);
++                          else
++                          {
++                              cp = LookupEventCookie (rail, &rail->CookieTable, dmabe.s.dma_destEvent);
++                              
++                              /* we MUST convert this into a DMA_READ_REQUEUE dma as if we don't the 
++                               * DMA descriptor will be read from the EP3_RETRY_DMA rather than the 
++                               * original DMA - this can then get reused and an incorrect DMA 
++                               * descriptor sent 
++                               * eventp->ev_Type contains the dma address with type in the lower bits 
++                               */ 
++                              
++                              dmabe.s.dma_source    = (type & ~EV_TYPE_MASK2);
++                              dmabe.s.dma_direction = (dmabe.s.dma_direction & ~DMA_READ) | DMA_READ_REQUEUE;
++                          }
++
++#ifdef DEBUG_ASSERT
++                          {
++                              E3_uint16     vp       = (dmabe.s.dma_direction == DMA_WRITE ? dmabe.s.dma_destVProc : dmabe.s.dma_srcVProc);
++                              EP_NODE_RAIL *nodeRail = &rail->Generic.Nodes[EP_VP_TO_NODE(vp)];
++
++                              ASSERT (!EP_VP_ISDATA(vp) || (nodeRail->State >= EP_NODE_CONNECTED && nodeRail->State <= EP_NODE_LOCAL_PASSIVATE));
++                          }
++#endif
++
++                          if (cp != NULL)
++                              cp->Operations->DmaRetry (rail, cp->Arg, &dmabe, EAGAIN);
++                          else
++                          {
++                              ASSERT (dmabe.s.dma_direction == DMA_WRITE && dmabe.s.dma_srcEvent == 0 && dmabe.s.dma_isRemote);
++                              
++                              QueueDmaForRetry (rail, &dmabe, EP_RETRY_ANONYMOUS);
++                          }
++                          break;
++                      }
++
++                      printk ("ep3_iprocTrap: SETEVENT : %x - cannot find dma to restart\n", hdrp->s.TrAddr);
++//                    panic ("ep3_iprocTrap\n");
++                      break;
++
++                  case MI_EventQueueOverflow:
++                  {
++                      sdramaddr_t event;
++                      E3_uint32   type;
++
++                      IncrStat (rail, IprocEventQueueOverflow);
++
++                      if ((event = ep_elan2sdram (&rail->Generic, hdrp->s.TrAddr)) != (sdramaddr_t) 0 &&
++                          ((type = elan3_sdram_readl (dev, event + offsetof (E3_Event, ev_Type))) & EV_TYPE_MASK_EVIRQ) == EV_TYPE_EVIRQ)
++                      {
++                          spin_unlock (&dev->IntrLock);
++                          ep3_event (ctxt, (type & ~(EV_TYPE_MASK_EVIRQ|EV_TYPE_MASK_BCOPY)), OP_LWP);
++                          spin_lock (&dev->IntrLock);
++
++                          break;
++                      }
++
++                      printk ("ep3_iprocTrap: SETEVENT : %x - cannot find event\n", hdrp->s.TrAddr);
++//                    panic ("ep3_iprocTrap\n");
++                      break;
++                  }
++
++                  default:
++                      printk ("ep3_iprocTrap: SETEVENT : %x MI=%x\n", hdrp->s.TrAddr, GET_STATUS_TRAPTYPE(hdrp->s.IProcTrapStatus));
++//                    panic ("ep3_iprocTrap\n");
++                      break;
++                  }
++                  break;
++                  
++              case TR_SENDDISCARD & TR_OPCODE_TYPE_MASK:
++                  /* Just ignore send-discard transactions */
++                  break;
++                  
++              case TR_REMOTEDMA & TR_OPCODE_TYPE_MASK:
++              {
++                  E3_DMA_BE *dmap = (E3_DMA_BE *) datap;
++
++                  if (GET_STATUS_TRAPTYPE(hdrp->s.IProcTrapStatus) != MI_DmaQueueOverflow)
++                  {
++                      printk ("ep3_iprocTrap: MI=%x\n", GET_STATUS_TRAPTYPE(hdrp->s.IProcTrapStatus));
++                      break;
++                  }
++
++                  IncrStat (rail, IprocDmaQueueOverflow);
++
++                  cp = LookupEventCookie (rail, &rail->CookieTable, dmap->s.dma_srcEvent);
++
++                  /* modify the dma type since it will still be a "read" dma */
++                  dmap->s.dma_type = (dmap->s.dma_type & ~DMA_TYPE_READ) | DMA_TYPE_ISREMOTE;
++
++#ifdef DEBUG_ASSERT
++                  {
++                      E3_uint16     vp       = dmap->s.dma_destVProc;
++                      EP_NODE_RAIL *nodeRail = &rail->Generic.Nodes[EP_VP_TO_NODE(vp)];
++                      
++                      ASSERT (!EP_VP_ISDATA(vp) || (nodeRail->State >= EP_NODE_CONNECTED && nodeRail->State <= EP_NODE_LOCAL_PASSIVATE));
++                  }
++#endif
++                  if (cp != NULL)
++                      cp->Operations->DmaRetry (rail, cp->Arg, dmap, EAGAIN);
++                  else
++                  {
++                      ASSERT (dmap->s.dma_direction == DMA_WRITE && dmap->s.dma_srcEvent == 0 && dmap->s.dma_isRemote);
++                      
++                      QueueDmaForRetry (rail, dmap, EP_RETRY_ANONYMOUS);
++                  }
++                  break;
++              }   
++              default:
++                  printk ("ep3_iprocTrap: %s\n", IProcTrapString (hdrp, datap));
++                  break;
++              }
++          }
++          
++          /*
++           * We've successfully processed this transaction, so move onto the 
++           * next one.
++           */
++          trap->TrappedTransaction++;
++          trap->TrappedDataBuffer++;
++      }
++
++      return (OP_HANDLED);
++    }
++    
++    /* Workaround WRITEBLOCK transaction executed when LOCKQUEUE transaction missed */
++    if ((trap->TrappedTransaction->s.TrTypeCntx.s.Type & TR_WRITEBLOCK_BIT) &&        /* a DMA packet */
++      trap->LockQueuePointer == 0 && trap->UnlockQueuePointer &&              /* a queueing DMA */
++      trap->TrappedTransaction->s.TrAddr == trap->FaultSave.s.FaultAddress)   /* and missed lockqueue */
++    {
++      printk ("ep3_iprocTrap: missed lockqueue transaction for queue %x\n", trap->UnlockQueuePointer);
++      return (OP_HANDLED);
++    }
++
++    if (trap->FaultSave.s.FaultContext != 0)
++      printk ("ep3_iprocTrap: pagefault at %08x in context %x\n", 
++              trap->FaultSave.s.FaultAddress, trap->FaultSave.s.FaultContext);
++
++//    panic ("ep3_iprocTrap: unexpected inputter trap\n");
++    
++    return (OP_HANDLED);
++}
++
++/*
++ * Command processor trap
++ *   kernel comms should only be able to generate
++ *   queue overflow traps
++ */
++int
++ep3_cprocTrap (ELAN3_CTXT *ctxt, COMMAND_TRAP *trap)
++{
++    EP3_RAIL     *rail   = (EP3_RAIL *) ctxt->Private;
++    int           ctxnum = (trap->TrapBuf.r.Breg >> 16) & MAX_ROOT_CONTEXT_MASK;
++    ELAN3_DEV     *dev    = rail->Device;
++    EP3_DMA_RING  *ring;
++    EP3_COOKIE   *cp;
++    E3_DMA_BE     dmabe;
++    int           vp, slot;
++    unsigned long flags;
++
++    switch (trap->Status.s.TrapType)
++    {
++    case MI_DmaQueueOverflow:
++      IncrStat (rail, CprocDmaQueueOverflow);
++
++      /* Use the context number that the setevent was issued in,
++       * to find the appropriate dma ring, then since they are guaranteed
++       * to be issued in order, we just search backwards till we find the
++       * last one which has completed its word copy - this must be the
++       * one which had caused the DmaQueueOverflow trap ! */
++
++      ASSERT (ctxnum >= ELAN3_DMARING_BASE_CONTEXT_NUM && ctxnum < (ELAN3_DMARING_BASE_CONTEXT_NUM+EP3_NUM_RINGS));
++
++      spin_lock_irqsave (&dev->CProcLock, flags);
++
++      ring = &rail->DmaRings[ctxnum - ELAN3_DMARING_BASE_CONTEXT_NUM];
++      slot = DMA_RING_PREV_POS(ring, ring->Position);
++      
++      while (ring->pDoneBlk[slot] == EP3_EVENT_ACTIVE)
++          slot = DMA_RING_PREV_POS(ring, slot);
++      
++      elan3_sdram_copyq_from_sdram (rail->Device , DMA_RING_DMA(ring,slot), &dmabe, sizeof (E3_DMA));
++
++#if defined(DEBUG_ASSERT)
++      while (slot != DMA_RING_PREV_POS(ring, ring->Position))
++      {
++          ASSERT (ring->pDoneBlk[slot] != EP3_EVENT_ACTIVE);
++          
++          slot = DMA_RING_PREV_POS(ring, slot);
++      }
++#endif
++      spin_unlock_irqrestore (&dev->CProcLock, flags);
++
++      if (dmabe.s.dma_direction == DMA_WRITE)
++          cp = LookupEventCookie (rail, &rail->CookieTable, dmabe.s.dma_srcEvent);
++      else
++      {
++          ASSERT (dmabe.s.dma_direction = DMA_READ_REQUEUE);
++
++          cp = LookupEventCookie (rail, &rail->CookieTable, dmabe.s.dma_destEvent);
++      }
++
++#if defined(DEBUG_ASSERT)
++      if (dmabe.s.dma_direction == DMA_WRITE)
++          vp = dmabe.s.dma_destVProc;
++      else
++          vp = dmabe.s.dma_srcVProc;
++      
++      ASSERT (!EP_VP_ISDATA(vp) || (rail->Generic.Nodes[EP_VP_TO_NODE(vp)].State >= EP_NODE_CONNECTED &&
++                                    rail->Generic.Nodes[EP_VP_TO_NODE(vp)].State <= EP_NODE_LOCAL_PASSIVATE));
++#endif
++
++      if (cp != NULL)
++          cp->Operations->DmaRetry (rail, cp->Arg, &dmabe, EAGAIN);
++      else
++      {
++          ASSERT (dmabe.s.dma_direction == DMA_WRITE && dmabe.s.dma_srcEvent == 0 && dmabe.s.dma_isRemote);
++          
++          QueueDmaForRetry (rail, &dmabe, EP_RETRY_ANONYMOUS);
++      }
++      
++      return (OP_HANDLED);
++
++    case MI_EventQueueOverflow:
++      ASSERT (ctxnum == ELAN3_MRF_CONTEXT_NUM);
++
++      IncrStat (rail, CprocEventQueueOverflow);
++      
++      rail->CommandPortEventTrap = TRUE;
++      return (OP_HANDLED);
++      
++#if defined(PER_CPU_TIMEOUT)
++    case MI_SetEventReadWait:
++      if (ctxnum == ELAN3_MRF_CONTEXT_NUM && trap->FaultSave.s.EventAddress == EP_PACEMAKER_EVENTADDR)
++      {
++          HeartbeatPacemaker (rail);
++          return (OP_HANDLED);
++      }
++#endif
++
++    default:
++      printk ("ep3_cprocTrap : Context=%x Status=%x TrapType=%x\n", ctxnum, trap->Status.Status, trap->Status.s.TrapType);
++      printk ("               FaultAddr=%x EventAddr=%x FSR=%x\n",
++               trap->FaultSave.s.FaultAddress, trap->FaultSave.s.EventAddress,
++               trap->FaultSave.s.FSR.Status);
++      break;
++    }
++
++//    panic ("ep3_cprocTrap");
++
++    return (OP_HANDLED);
++}
++
++static int
++ep3_cprocReissue (ELAN3_CTXT *ctxt, CProcTrapBuf_BE *tbuf)
++{
++    EP3_RAIL   *rail    = (EP3_RAIL *) ctxt->Private;
++    unsigned  cmdoff = (tbuf->s.ContextType >> 5) & 0xFF;
++    int       ctxnum = (tbuf->s.ContextType >> 16) & MAX_ROOT_CONTEXT_MASK;
++    
++    if (ctxnum >= ELAN3_DMARING_BASE_CONTEXT_NUM && ctxnum < (ELAN3_DMARING_BASE_CONTEXT_NUM+EP3_NUM_RINGS))
++    {
++      EP3_DMA_RING *ring = &rail->DmaRings[ctxnum - ELAN3_DMARING_BASE_CONTEXT_NUM];
++
++      ASSERT ((cmdoff << 2) == offsetof (E3_CommandPort, SetEvent)); /* can only be setevent commands! */
++      ASSERT (tbuf->s.Addr >= DMA_RING_EVENT_ELAN(ring,0) && tbuf->s.Addr < DMA_RING_EVENT_ELAN(ring, ring->Entries));
++      
++      writel (tbuf->s.Addr, ring->CommandPort + (cmdoff << 2));
++    }
++    else
++    {
++      ASSERT (ctxnum == ELAN3_MRF_CONTEXT_NUM);
++
++      writel (tbuf->s.Addr, ctxt->CommandPort + (cmdoff << 2));
++    }
++    
++    return (OP_HANDLED);
++}
++
++static E3_uint8
++ep3_load8 (ELAN3_CTXT *ctxt, E3_Addr addr)
++{
++    EP3_RAIL    *rail  = (EP3_RAIL *) ctxt->Private;
++    ELAN3_DEV    *dev = ctxt->Device;
++    sdramaddr_t offset;
++    E3_uint8   *ptr;
++
++    if ((offset = ep_elan2sdram (&rail->Generic, addr)) != 0)
++      return (elan3_sdram_readb (dev, offset));
++    if ((ptr = ep_elan2main (&rail->Generic, addr)) != NULL)
++      return (*ptr);
++
++    printk ("ep3_load8: %08x\n", addr);
++    return (0);
++}
++
++static void
++ep3_store8 (ELAN3_CTXT *ctxt, E3_Addr addr, E3_uint8 val)
++{
++    EP3_RAIL   *rail  = (EP3_RAIL *) ctxt->Private;
++    ELAN3_DEV   *dev = ctxt->Device;
++    sdramaddr_t offset;
++    E3_uint8   *ptr;
++
++    if ((offset = ep_elan2sdram (&rail->Generic, addr)) != 0)
++      elan3_sdram_writeb (dev, offset, val);
++    else if ((ptr = ep_elan2main (&rail->Generic, addr)) != 0)
++      *ptr = val;
++    else
++      printk ("ep3_store8 %08x\n", addr);
++}
++
++static E3_uint16
++ep3_load16 (ELAN3_CTXT *ctxt, E3_Addr addr)
++{
++    EP3_RAIL   *rail  = (EP3_RAIL *) ctxt->Private;
++    ELAN3_DEV   *dev = ctxt->Device;
++    sdramaddr_t offset;
++    E3_uint16  *ptr;
++
++    if ((offset = ep_elan2sdram (&rail->Generic, addr)) != 0)
++      return (elan3_sdram_readw (dev, offset));
++    if ((ptr = ep_elan2main (&rail->Generic, addr)) != 0)
++      return (*ptr);
++
++    printk ("ep3_load16 %08x\n", addr);
++    return (0);
++}
++
++static void
++ep3_store16 (ELAN3_CTXT *ctxt, E3_Addr addr, E3_uint16 val)
++{
++    EP3_RAIL   *rail  = (EP3_RAIL *) ctxt->Private;
++    ELAN3_DEV   *dev = ctxt->Device;
++    sdramaddr_t offset;
++    E3_uint16  *ptr;
++
++    if ((offset = ep_elan2sdram (&rail->Generic, addr)) != 0)
++      elan3_sdram_writew (dev, offset, val);
++    else if ((ptr = ep_elan2main (&rail->Generic, addr)) != 0)
++      *ptr = val;
++    else
++      printk ("ep3_store16 %08x\n", addr);
++}
++
++static E3_uint32
++ep3_load32 (ELAN3_CTXT *ctxt, E3_Addr addr)
++{
++    EP3_RAIL   *rail  = (EP3_RAIL *) ctxt->Private;
++    ELAN3_DEV   *dev = ctxt->Device;
++    sdramaddr_t offset;
++    E3_uint32  *ptr;
++
++    if ((offset = ep_elan2sdram (&rail->Generic, addr)) != 0)
++      return (elan3_sdram_readl(dev, offset));
++    if ((ptr = ep_elan2main (&rail->Generic, addr)) != 0)
++      return (*ptr);
++    
++    printk ("ep3_load32 %08x\n", addr);
++    return (0);
++}
++
++static void
++ep3_store32 (ELAN3_CTXT *ctxt, E3_Addr addr, E3_uint32 val)
++{
++    EP3_RAIL   *rail  = (EP3_RAIL *) ctxt->Private;
++    ELAN3_DEV   *dev = ctxt->Device;
++    sdramaddr_t offset;
++    E3_uint32  *ptr;
++
++    if ((offset = ep_elan2sdram (&rail->Generic, addr)) != 0)
++      elan3_sdram_writel (dev, offset, val);
++    else if ((ptr = ep_elan2main (&rail->Generic, addr)) != 0)
++      *ptr = val;
++    else
++      printk ("ep3_store32 %08x\n", addr);
++}
++
++static E3_uint64
++ep3_load64 (ELAN3_CTXT *ctxt, E3_Addr addr)
++{
++    EP3_RAIL   *rail  = (EP3_RAIL *) ctxt->Private;
++    ELAN3_DEV   *dev = ctxt->Device;
++    sdramaddr_t offset;
++    E3_uint64  *ptr;
++
++    if ((offset = ep_elan2sdram (&rail->Generic, addr)) != 0)
++      return (elan3_sdram_readq (dev, offset));
++    if ((ptr = ep_elan2main (&rail->Generic, addr)) != 0)
++      return (*ptr);
++
++    printk ("ep3_load64 %08x\n", addr);
++    return (0);
++}
++
++static void
++ep3_store64 (ELAN3_CTXT *ctxt, E3_Addr addr, E3_uint64 val)
++{
++    EP3_RAIL   *rail  = (EP3_RAIL *) ctxt->Private;
++    ELAN3_DEV   *dev = ctxt->Device;
++    sdramaddr_t offset;
++    E3_uint64  *ptr;
++
++    if ((offset = ep_elan2sdram (&rail->Generic, addr)) != 0)
++      elan3_sdram_writeq (dev, offset, val);
++    else if ((ptr = ep_elan2main (&rail->Generic, addr)) != 0)
++      *ptr = val;
++    else
++      printk ("ep3_store64 %08x\n", addr);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/ep/support_elan4.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/support_elan4.c      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/support_elan4.c   2005-05-11 12:10:12.546916160 -0400
+@@ -0,0 +1,1184 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: support_elan4.c,v 1.18.2.3 2004/11/18 12:05:00 david Exp $ $Name: QSNETMODULES-4-31_20050321 $"
++/*      $Source: /cvs/master/quadrics/epmod/support_elan4.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <qsnet/kthread.h>
++
++#include <elan/kcomm.h>
++
++#include "kcomm_vp.h"
++#include "kcomm_elan4.h"
++#include "debug.h"
++
++#include <elan4/trtype.h>
++#include <elan4/debug.h>
++
++void
++ep4_register_intcookie (EP4_RAIL *rail, EP4_INTCOOKIE *cp, E4_uint64 cookie, void (*callback)(EP4_RAIL *r, void *arg), void *arg)
++{
++    unsigned long flags;
++    
++    cp->int_val      = cookie;
++    cp->int_callback = callback;
++    cp->int_arg      = arg;
++      
++    spin_lock_irqsave (&rail->r_intcookie_lock, flags);
++    list_add_tail (&cp->int_link, &rail->r_intcookie_hash[EP4_INTCOOKIE_HASH(cookie)]);
++    spin_unlock_irqrestore (&rail->r_intcookie_lock, flags);
++}
++
++void
++ep4_deregister_intcookie (EP4_RAIL *rail, EP4_INTCOOKIE *cp)
++{
++    unsigned long flags;
++    
++    spin_lock_irqsave (&rail->r_intcookie_lock, flags);
++    list_del (&cp->int_link);
++    spin_unlock_irqrestore (&rail->r_intcookie_lock, flags);
++}
++
++
++EP4_INTCOOKIE *
++ep4_lookup_intcookie (EP4_RAIL *rail, E4_uint64 cookie)
++{
++    struct list_head *el;
++    unsigned long flags;
++
++    spin_lock_irqsave (&rail->r_intcookie_lock, flags);
++    list_for_each (el, &rail->r_intcookie_hash[EP4_INTCOOKIE_HASH(cookie)]) {
++      EP4_INTCOOKIE *cp = list_entry (el, EP4_INTCOOKIE, int_link);
++
++      if (cp->int_val == cookie)
++      {
++          spin_unlock_irqrestore (&rail->r_intcookie_lock, flags);
++          return cp;
++      }
++    }
++    spin_unlock_irqrestore (&rail->r_intcookie_lock, flags);
++    return NULL;
++}
++
++E4_uint64
++ep4_neterr_cookie (EP4_RAIL *rail, unsigned int node)
++{
++    E4_uint64      cookie;
++    unsigned long  flags;
++
++    spin_lock_irqsave (&rail->r_cookie_lock, flags);
++    cookie = rail->r_cookies[node];
++
++    rail->r_cookies[node] += EP4_COOKIE_INC;
++    
++    spin_unlock_irqrestore (&rail->r_cookie_lock, flags);
++
++    return cookie;
++}
++
++void
++ep4_eproc_trap (ELAN4_CTXT *ctxt, E4_uint64 status)
++{
++    EP4_RAIL        *rail = EP4_CTXT_TO_RAIL (ctxt);
++    ELAN4_EPROC_TRAP trap;
++
++    elan4_extract_eproc_trap (ctxt->ctxt_dev, status, &trap, 0);
++
++    if (epdebug & DBG_EPTRAP)
++      elan4_display_eproc_trap (DBG_BUFFER, 0, "ep4_eproc_trap", &trap);
++
++    switch (EPROC_TrapType (status))
++    {
++    case EventProcNoFault:
++      EPRINTF1 (DBG_EPTRAP, "%s: EventProcNoFault\n", rail->r_generic.Name);
++      return;
++
++    default:
++      printk ("%s: unhandled eproc trap %d\n", rail->r_generic.Name, EPROC_TrapType (status));
++      elan4_display_eproc_trap (DBG_CONSOLE, 0, "ep4_eproc_trap", &trap);
++    }
++}
++
++void
++ep4_cproc_trap (ELAN4_CTXT *ctxt, E4_uint64 status, unsigned cqnum)
++{
++    EP4_RAIL        *rail = EP4_CTXT_TO_RAIL (ctxt);
++    ELAN4_CPROC_TRAP trap;
++    struct list_head *el;
++    register int      i;
++
++    elan4_extract_cproc_trap (ctxt->ctxt_dev, status, &trap, cqnum);
++
++    if (epdebug & DBG_EPTRAP)
++      elan4_display_cproc_trap (DBG_BUFFER, 0, "ep4_cproc_trap", &trap);
++      
++    switch (CPROC_TrapType (status))
++    {
++    case CommandProcInterruptQueueOverflow:
++      /*
++       * Try and handle a bunch of elan main interrupts
++       */
++      for (i = 0; i <EP4_NUM_ECQ; i++) {
++          list_for_each (el, &rail->r_ecq_list[i]) {
++              EP4_ECQ *ecq = list_entry (el, EP4_ECQ, ecq_link);
++          
++              if (elan4_cq2num (ecq->ecq_cq) == cqnum)
++              {
++                  printk ("%s: defer command queue %d after trap %x\n",
++                          rail->r_generic.Name, cqnum, CPROC_TrapType (status));
++      
++                  elan4_queue_mainintop (ctxt->ctxt_dev, &ecq->ecq_intop);
++                  return;
++              }
++          }
++      }
++      break;
++
++    case CommandProcDmaQueueOverflow:
++    case CommandProcThreadQueueOverflow:
++      for (i = 0; i <EP4_NUM_ECQ; i++) {
++          list_for_each (el, &rail->r_ecq_list[i]) {
++              EP4_ECQ *ecq = list_entry (el, EP4_ECQ, ecq_link);
++          
++              if (elan4_cq2num (ecq->ecq_cq) == cqnum)
++              {
++                  printk ("%s: restart command queue %d after trap %x\n",
++                          rail->r_generic.Name, cqnum, CPROC_TrapType (status));
++
++                  elan4_restartcq (ctxt->ctxt_dev, ecq->ecq_cq);
++                  return;
++              }
++          }
++      }
++      break;
++    }
++
++    printk ("%s: unhandled cproc trap %d for cqnum %d\n", rail->r_generic.Name, CPROC_TrapType (status), cqnum);
++    elan4_display_cproc_trap (DBG_CONSOLE, 0, "ep4_cproc_trap", &trap);
++}
++
++void
++ep4_dproc_trap (ELAN4_CTXT *ctxt, E4_uint64 status, unsigned unit)
++{
++    EP4_RAIL        *rail = EP4_CTXT_TO_RAIL (ctxt);
++    ELAN4_DPROC_TRAP trap;
++
++    elan4_extract_dproc_trap (ctxt->ctxt_dev, status, &trap, unit);
++
++    if (epdebug & DBG_EPTRAP)
++      elan4_display_dproc_trap (DBG_BUFFER, 0, "ep4_dproc_trap", &trap);
++
++    if (! DPROC_PrefetcherFault (trap.tr_status))
++    {
++      switch (DPROC_TrapType (trap.tr_status))
++      {
++      case DmaProcFailCountError:
++          goto retry_this_dma;
++
++      case DmaProcPacketAckError:
++          goto retry_this_dma;
++
++      case DmaProcQueueOverflow:
++          goto retry_this_dma;
++      }
++    }
++
++    printk ("%s: unhandled dproc trap\n", rail->r_generic.Name);
++    elan4_display_dproc_trap (DBG_CONSOLE, 0, "ep4_dproc_trap", &trap);
++    return;
++
++ retry_this_dma:
++    /*XXXX implement backoff .... */
++
++    ep4_queue_dma_retry (rail, &trap.tr_desc, EP_RETRY_LOW_PRI);
++}
++
++void
++ep4_tproc_trap (ELAN4_CTXT *ctxt, E4_uint64 status)
++{
++    EP4_RAIL         *rail = EP4_CTXT_TO_RAIL (ctxt);
++    ELAN4_TPROC_TRAP *trap = &rail->r_tproc_trap;
++
++    elan4_extract_tproc_trap (ctxt->ctxt_dev, status, trap);
++
++    if (epdebug & DBG_EPTRAP)
++      elan4_display_tproc_trap (DBG_BUFFER, 0, "ep4_tproc_trap", trap);
++      
++    printk ("%s: unhandled tproc trap\n", rail->r_generic.Name);
++    elan4_display_tproc_trap (DBG_CONSOLE, 0, "ep4_tproc_trap", trap);
++}
++
++void
++ep4_iproc_trap (ELAN4_CTXT *ctxt, E4_uint64 status, unsigned unit)
++{
++    EP4_RAIL         *rail = EP4_CTXT_TO_RAIL (ctxt);
++    ELAN4_IPROC_TRAP *trap = &rail->r_iproc_trap;
++
++    elan4_extract_iproc_trap (ctxt->ctxt_dev, status, trap, unit);
++
++    if (epdebug & DBG_EPTRAP)
++      elan4_display_iproc_trap (DBG_BUFFER, 0, "ep4_iproc_trap", trap);
++      
++    elan4_inspect_iproc_trap (trap);
++
++    switch (IPROC_TrapValue (trap->tr_transactions[trap->tr_trappedTrans].IProcStatusCntxAndTrType))
++    {
++    case InputDmaQueueOverflow:
++      ep4_queue_dma_retry (rail, (E4_DMA *) &trap->tr_dataBuffers[trap->tr_trappedTrans], EP_RETRY_LOW_PRI);
++      return;
++
++    case InputEventEngineTrapped:
++    {
++      E4_IprocTrapHeader *hdrp = &trap->tr_transactions[trap->tr_trappedTrans];
++      sdramaddr_t         inputq;
++      E4_Addr             event;
++
++      /* XXXX: flow control on the command queue which we issue to is 
++       * rather difficult, we don't want to have space for an event 
++       * for each possible context, nor the mechanism to hold the 
++       * context filter up until the event has been executed.  Given
++       * that the event engine will be restarted by this same interrupt
++       * and we're using high priority command queues, then we just use
++       * a single small command queue for this.
++       */
++      switch (IPROC_TransactionType(hdrp->IProcStatusCntxAndTrType) & TR_OPCODE_MASK)
++      {
++      case TR_SETEVENT & TR_OPCODE_MASK:
++          if (hdrp->TrAddr != 0)
++              ep4_set_event_cmd (rail->r_event_ecq, hdrp->TrAddr);
++          return;
++
++      case TR_INPUT_Q_COMMIT & TR_OPCODE_MASK:
++          if ((inputq = ep_elan2sdram (&rail->r_generic, hdrp->TrAddr)) == 0)
++              printk ("%s: TR_INPUT_Q_COMMIT at %llx is not sdram\n", rail->r_generic.Name, hdrp->TrAddr);
++          else
++          {
++              if ((event = elan4_sdram_readq (rail->r_ctxt.ctxt_dev, inputq + offsetof (E4_InputQueue, q_event))) != 0)
++                  ep4_set_event_cmd (rail->r_event_ecq, event);
++              return;
++          }
++      }
++      break;
++    }
++
++    case InputEopErrorOnWaitForEop:
++    case InputEopErrorTrap:
++    case InputCrcErrorAfterPAckOk:
++      if (! (trap->tr_flags & TR_FLAG_ACK_SENT) || (trap->tr_flags & TR_FLAG_EOP_BAD))
++          return;
++      
++      if (EP4_CONTEXT_ISDATA (IPROC_NetworkContext (status)))
++      {
++          unsigned int nodeId = EP4_CONTEXT_TO_NODE (IPROC_NetworkContext (status));
++
++          if ((trap->tr_flags & (TR_FLAG_DMA_PACKET | TR_FLAG_BAD_TRANS)) || 
++              ((trap->tr_flags & TR_FLAG_EOP_ERROR) && (trap->tr_identifyTrans == TR_TRANS_INVALID)))
++          {
++              printk ("%s: network error on dma packet from node %d\n", rail->r_generic.Name, nodeId);
++
++              ep_queue_network_error (&rail->r_generic, EP4_CONTEXT_TO_NODE(IPROC_NetworkContext (status)), EP_NODE_NETERR_DMA_PACKET, unit & 1, 0);
++              return;
++          }
++          
++          if (trap->tr_flags & TR_FLAG_EOP_ERROR)
++          {
++              E4_uint64        status = trap->tr_transactions[trap->tr_identifyTrans].IProcStatusCntxAndTrType;
++              EP_NETERR_COOKIE cookie = 0;
++
++              switch (IPROC_TransactionType (status) & TR_OPCODE_MASK)
++              {
++              case TR_SETEVENT_IDENTIFY & TR_OPCODE_MASK:
++                  if (IPROC_TrapValue(status) == InputNoFault)
++                      cookie = trap->tr_transactions[trap->tr_identifyTrans].TrAddr;
++                  else
++                      cookie = trap->tr_dataBuffers[trap->tr_identifyTrans].Data[0];
++                  printk ("%s: network error on setevent <%lld%s%s%s%s> from node %d\n", rail->r_generic.Name, EP4_COOKIE_STRING(cookie), nodeId);
++                  break;
++
++              case TR_INPUT_Q_COMMIT & TR_OPCODE_MASK:
++                  if (IPROC_TrapValue(status) == InputNoFault)
++                      cookie = trap->tr_transactions[trap->tr_identifyTrans].TrAddr;
++                  else
++                      cookie = trap->tr_dataBuffers[trap->tr_identifyTrans].Data[0];
++                  printk ("%s: network error on queue commit <%lld%s%s%s%s> from node %d\n", rail->r_generic.Name, EP4_COOKIE_STRING(cookie), nodeId);
++                  break;
++                  
++              case TR_REMOTEDMA & TR_OPCODE_MASK:
++                  cookie = trap->tr_transactions[trap->tr_identifyTrans].TrAddr;
++                  printk ("%s: network error on remote dma <%lld%s%s%s%s> from node %d\n", rail->r_generic.Name, EP4_COOKIE_STRING(cookie), nodeId);
++                  break;
++
++              case TR_IDENTIFY & TR_OPCODE_MASK:
++                  cookie = trap->tr_transactions[trap->tr_identifyTrans].TrAddr;
++                  printk ("%s: network error on identify <%lld%s%s%s%s> from node %d\n", rail->r_generic.Name, EP4_COOKIE_STRING(cookie), nodeId);
++                  break;
++
++              default:
++                  panic ("%s: unknown identify transaction type %x for eop error from node %d\n", rail->r_generic.Name,
++                          IPROC_TransactionType (trap->tr_transactions[trap->tr_identifyTrans].IProcStatusCntxAndTrType), nodeId);
++                  break;
++              }
++
++              ep_queue_network_error (&rail->r_generic, nodeId, EP_NODE_NETERR_ATOMIC_PACKET, unit & 1, cookie);
++          }
++      }
++      return;
++    }
++
++    printk ("%s: unhandled iproc trap\n", rail->r_generic.Name);
++    elan4_display_iproc_trap (DBG_CONSOLE, 0, "ep4_iproc_trap", trap);
++}
++
++void
++ep4_interrupt (ELAN4_CTXT *ctxt, E4_uint64 cookie)
++{
++    EP4_RAIL      *rail = EP4_CTXT_TO_RAIL (ctxt);
++    EP4_INTCOOKIE *cp  = ep4_lookup_intcookie (rail, cookie);
++
++    if (cp == NULL)
++    {
++      printk ("ep4_interrupt: cannot find event cookie for %016llx\n", (long long) cookie);
++      return;
++    }
++
++    cp->int_callback (rail, cp->int_arg);
++}
++
++ELAN4_TRAP_OPS ep4_trap_ops = 
++{
++    ep4_eproc_trap,
++    ep4_cproc_trap,
++    ep4_dproc_trap,
++    ep4_tproc_trap,
++    ep4_iproc_trap,
++    ep4_interrupt,
++};
++
++void
++ep4_flush_filters (EP_RAIL *r)
++{
++    /* nothing to do here as elan4_set_filter() flushes the context filter */
++}
++
++struct flush_queues_desc
++{
++    EP4_RAIL      *rail;
++    volatile int   done;
++} ;
++
++static void
++ep4_flush_queues_flushop (ELAN4_DEV *dev, void *arg, int qfull)
++{
++    struct flush_queues_desc *desc  = (struct flush_queues_desc *) arg;
++    EP4_RAIL                 *rail  = desc->rail;
++    E4_uint64               qptrs = read_reg64 (dev, DProcHighPriPtrs);
++    E4_uint32                 qsize = E4_QueueSize (E4_QueueSizeValue (qptrs));
++    E4_uint32                 qfptr = E4_QueueFrontPointer (qptrs);
++    E4_uint32                 qbptr = E4_QueueBackPointer (qptrs);
++    E4_DProcQueueEntry        qentry;
++    unsigned long             flags;
++
++    while ((qfptr != qbptr) || qfull)
++    {
++      E4_uint64 typeSize = elan4_sdram_readq (dev, qfptr + offsetof (E4_DProcQueueEntry, Desc.dma_typeSize));
++      
++      if (DMA_Context (qentry.Desc.dma_typeSize) == rail->r_ctxt.ctxt_num)
++      {
++          E4_uint64     vp       = elan4_sdram_readq (dev, qfptr + offsetof (E4_DProcQueueEntry, Desc.dma_vproc));
++          EP_NODE_RAIL *nodeRail = &rail->r_generic.Nodes[EP_VP_TO_NODE(vp)];
++          
++          EP4_ASSERT (rail, !EP_VP_ISDATA(vp) || (nodeRail->State >= EP_NODE_CONNECTED && nodeRail->State <= EP_NODE_LOCAL_PASSIVATE));
++          
++          if (EP_VP_ISDATA(vp) && nodeRail->State == EP_NODE_LOCAL_PASSIVATE)
++          {
++              /*
++               * This is a DMA going to the node which is being removed, 
++               * so move it onto the node dma list where it will get
++               * handled later.
++               */
++              qentry.Desc.dma_typeSize = typeSize;
++              qentry.Desc.dma_cookie   = elan4_sdram_readq (dev, qfptr + offsetof (E4_DProcQueueEntry, Desc.dma_cookie));
++              qentry.Desc.dma_vproc    = vp;
++              qentry.Desc.dma_srcAddr  = elan4_sdram_readq (dev, qfptr + offsetof (E4_DProcQueueEntry, Desc.dma_srcAddr));
++              qentry.Desc.dma_dstAddr  = elan4_sdram_readq (dev, qfptr + offsetof (E4_DProcQueueEntry, Desc.dma_dstAddr));
++              qentry.Desc.dma_srcEvent = elan4_sdram_readq (dev, qfptr + offsetof (E4_DProcQueueEntry, Desc.dma_srcEvent));
++              qentry.Desc.dma_dstEvent = elan4_sdram_readq (dev, qfptr + offsetof (E4_DProcQueueEntry, Desc.dma_dstEvent));
++              
++              EPRINTF4 (DBG_RETRY, "ep4_flush_dmas: %016llx %016llx %016llx %016llx\n", qentry.Desc.dma_typeSize, 
++                        qentry.Desc.dma_cookie, qentry.Desc.dma_vproc, qentry.Desc.dma_srcAddr);
++              EPRINTF3 (DBG_RETRY, "                %016llx %016llx %016llx\n", qentry.Desc.dma_dstAddr, 
++                        qentry.Desc.dma_srcEvent, qentry.Desc.dma_dstEvent);
++              
++              ep4_queue_dma_stalled (rail, &qentry.Desc);
++              
++              qentry.Desc.dma_typeSize = DMA_ShMemWrite | dev->dev_ctxt.ctxt_num;
++              qentry.Desc.dma_cookie   = 0;
++              qentry.Desc.dma_vproc    = 0;
++              qentry.Desc.dma_srcAddr  = 0;
++              qentry.Desc.dma_dstAddr  = 0;
++              qentry.Desc.dma_srcEvent = 0;
++              qentry.Desc.dma_dstEvent = 0;
++              
++              elan4_sdram_copyq_to_sdram (dev, &qentry, qfptr, sizeof (E4_DProcQueueEntry));
++          }
++      }
++
++      qfptr = (qfptr & ~(qsize-1)) | ((qfptr + sizeof (E4_DProcQueueEntry)) & (qsize-1));
++      qfull = 0;
++    }
++
++    spin_lock_irqsave (&rail->r_haltop_lock, flags);
++    desc->done = 1;
++    kcondvar_wakeupall (&rail->r_haltop_sleep, &rail->r_haltop_lock);
++    spin_unlock_irqrestore (&rail->r_haltop_lock, flags);
++}
++
++static void
++ep4_flush_queues_haltop (ELAN4_DEV *dev, void *arg)
++{
++    struct flush_queues_desc *desc = (struct flush_queues_desc *) arg;
++
++    elan4_queue_dma_flushop (dev, &desc->rail->r_flushop, 1);
++}
++
++void
++ep4_flush_queues (EP_RAIL *r)
++{
++    EP4_RAIL *rail = (EP4_RAIL *) r;
++    struct flush_queues_desc desc;
++    struct list_head *el, *nel;
++    unsigned long flags;
++    int i;
++
++    /* initialise descriptor */
++    desc.rail  = rail;
++    desc.done  = 0;
++
++    /* First -  stall the dma retry thread, so that it will no longer restart
++     *          any dma's from the retry list */
++    ep_kthread_stall (&rail->r_retry_thread);
++
++    /* Second - flush through all command queues targetted by events, thread etc */
++    ep4_flush_ecqs (rail);
++
++    /* Third - queue a halt operation to flush through all DMA's which are executing
++     *         or on the run queues */
++    kmutex_lock (&rail->r_haltop_mutex);
++
++    rail->r_haltop.op_mask      = INT_DProcHalted;
++    rail->r_haltop.op_function  = ep4_flush_queues_haltop;
++    rail->r_haltop.op_arg       = &desc;
++
++    rail->r_flushop.op_function = ep4_flush_queues_flushop;
++    rail->r_flushop.op_arg      = &desc;
++    
++    elan4_queue_haltop (rail->r_ctxt.ctxt_dev, &rail->r_haltop);
++
++    spin_lock_irqsave (&rail->r_haltop_lock, flags);
++    while (! desc.done)
++      kcondvar_wait (&rail->r_haltop_sleep, &rail->r_haltop_lock, &flags);
++    spin_unlock_irqrestore (&rail->r_haltop_lock, flags);
++    kmutex_unlock (&rail->r_haltop_mutex);
++
++    /* Fourth - run down the dma retry lists and move all entries to the cancelled
++     *          list.  Any dma's which were on the run queues have already been
++     *          moved there */
++    spin_lock_irqsave (&rail->r_dma_lock, flags);
++    for (i = EP_RETRY_BASE; i < EP_NUM_RETRIES; i++)
++    {
++      list_for_each_safe (el,nel, &rail->r_dma_retrylist[i]) {
++          EP4_DMA_RETRY *retry    = list_entry (el, EP4_DMA_RETRY, retry_link);
++          EP_NODE_RAIL  *nodeRail = &rail->r_generic.Nodes[EP_VP_TO_NODE(retry->retry_dma.dma_vproc)];
++
++          EP4_ASSERT (rail, nodeRail->State >= EP_NODE_CONNECTED && nodeRail->State <= EP_NODE_LOCAL_PASSIVATE);
++
++          if (nodeRail->State == EP_NODE_LOCAL_PASSIVATE)
++          {
++              list_del (&retry->retry_link);
++              list_add_tail (&retry->retry_link, &nodeRail->StalledDmas);
++          }
++      }
++    }
++    spin_unlock_irqrestore (&rail->r_dma_lock, flags);
++    
++    /* Finally - allow the retry thread to run again */
++    ep_kthread_resume (&rail->r_retry_thread);
++}
++
++struct write_qdesc_desc
++{
++    EP4_RAIL      *rail;
++    sdramaddr_t    qaddr;
++    E4_InputQueue *qdesc;
++    volatile int   done;
++} ;
++
++static void
++ep4_write_qdesc_haltop (ELAN4_DEV *dev, void *arg)
++{
++    struct write_qdesc_desc *desc = (struct write_qdesc_desc *) arg;
++    EP4_RAIL                *rail = desc->rail;
++    unsigned long            flags;
++
++    elan4_sdram_copyq_to_sdram (dev, desc->qdesc, desc->qaddr, sizeof (E4_InputQueue));
++
++    spin_lock_irqsave (&rail->r_haltop_lock, flags);
++    desc->done = 1;
++    kcondvar_wakeupall (&rail->r_haltop_sleep, &rail->r_haltop_lock);
++    spin_unlock_irqrestore (&rail->r_haltop_lock, flags);
++}
++
++void
++ep4_write_qdesc (EP4_RAIL *rail, sdramaddr_t qaddr, E4_InputQueue *qdesc)
++{
++    struct write_qdesc_desc desc;
++    unsigned long flags;
++
++    /* initialise descriptor */
++    desc.rail  = rail;
++    desc.qaddr = qaddr;
++    desc.qdesc = qdesc;
++    desc.done  = 0;
++
++    kmutex_lock (&rail->r_haltop_mutex);
++
++    rail->r_haltop.op_mask     = INT_DiscardingHighPri;
++    rail->r_haltop.op_function = ep4_write_qdesc_haltop;
++    rail->r_haltop.op_arg      = &desc;
++    
++    elan4_queue_haltop (rail->r_ctxt.ctxt_dev, &rail->r_haltop);
++
++    spin_lock_irqsave (&rail->r_haltop_lock, flags);
++    while (! desc.done)
++      kcondvar_wait (&rail->r_haltop_sleep, &rail->r_haltop_lock, &flags);
++    spin_unlock_irqrestore (&rail->r_haltop_lock, flags);
++    
++    kmutex_unlock (&rail->r_haltop_mutex);
++}
++#define CQ_SIZE_NWORDS ((CQ_Size (ecq->ecq_cq->cq_size) >> 3) - 8)    /* available number of dwords (less enough to flush) */
++EP4_ECQ *
++ep4_alloc_ecq (EP4_RAIL *rail, unsigned cqsize)
++{
++    EP4_ECQ *ecq;
++    unsigned long pgoff;
++
++    /* no space available, so allocate a new entry */
++    KMEM_ZALLOC (ecq, EP4_ECQ *, sizeof (EP4_ECQ), 1);
++
++    if (ecq == NULL)
++      return 0;
++
++    if ((ecq->ecq_cq = elan4_alloccq (&rail->r_ctxt, cqsize, CQ_EnableAllBits, CQ_Priority)) == NULL)
++    {
++      KMEM_FREE (ecq, sizeof (EP4_ECQ));
++      return 0;
++    }
++
++    pgoff = (ecq->ecq_cq->cq_mapping & (PAGE_SIZE-1));
++
++    ecq->ecq_addr  = ep_rmalloc (rail->r_ecq_rmap, PAGESIZE, 0) + pgoff;
++    ecq->ecq_avail = CQ_SIZE_NWORDS;                  /* available number of dwords (less enough to flush) */
++
++    ecq->ecq_intop.op_function = (ELAN4_HALTFN *) elan4_restartcq;
++    ecq->ecq_intop.op_arg      = ecq->ecq_cq;
++
++    ep4_ioaddr_map (&rail->r_generic, ecq->ecq_addr - pgoff, ecq->ecq_cq->cq_mapping - pgoff, PAGESIZE, EP_PERM_WRITE);
++
++    spin_lock_init (&ecq->ecq_lock);
++
++    return ecq;
++}
++
++void
++ep4_free_ecq (EP4_RAIL *rail, EP4_ECQ *ecq)
++{
++    unsigned long pgoff = (ecq->ecq_cq->cq_mapping & (PAGE_SIZE-1));
++
++    spin_lock_destroy (&ecq->ecq_lock);
++
++    ep4_unmap (&rail->r_generic, ecq->ecq_addr - pgoff, PAGESIZE);
++    ep_rmfree (rail->r_ecq_rmap, PAGESIZE, ecq->ecq_addr - pgoff);
++
++    elan4_freecq (&rail->r_ctxt, ecq->ecq_cq);
++    
++    KMEM_FREE (ecq, sizeof (EP4_ECQ));
++}
++
++EP4_ECQ *
++ep4_get_ecq (EP4_RAIL *rail, unsigned which, unsigned ndwords)
++{
++    ELAN4_DEV *dev = rail->r_ctxt.ctxt_dev;
++    struct list_head *el;
++    unsigned long flags;
++    EP4_ECQ *ecq;
++    
++    spin_lock_irqsave (&rail->r_ecq_lock, flags);
++    list_for_each (el, &rail->r_ecq_list[which]) {
++      EP4_ECQ *ecq = list_entry (el, EP4_ECQ, ecq_link);
++
++      if (ecq->ecq_avail >= ndwords)
++      {
++          ecq->ecq_avail -= ndwords;
++
++          spin_unlock_irqrestore (&rail->r_ecq_lock, flags);
++
++          return ecq;
++      }
++    }
++    spin_unlock_irqrestore (&rail->r_ecq_lock, flags);
++
++    if ((ecq = ep4_alloc_ecq (rail, EP4_ECQ_Size (which))) == NULL)
++      return NULL;
++
++    if (which == EP4_ECQ_EVENT)
++    {
++      if ((ecq->ecq_event = ep_alloc_elan (&rail->r_generic, sizeof (E4_Event32), 0, &ecq->ecq_event_addr)) == 0)
++      {
++          ep4_free_ecq (rail, ecq);
++          return NULL;
++      }
++      
++      elan4_sdram_writeq (dev, ecq->ecq_event + offsetof (E4_Event32, ev_CountAndType),
++                          E4_EVENT_INIT_VALUE (0, E4_EVENT_WRITE, E4_EVENT_DTYPE_LONG, 0));
++      elan4_sdram_writeq (dev, ecq->ecq_event + offsetof (E4_Event32, ev_WritePtr),
++                          ecq->ecq_addr);
++      elan4_sdram_writeq (dev, ecq->ecq_event + offsetof (E4_Event32, ev_WriteValue),
++                          SET_EVENT_CMD | (rail->r_elan_addr + offsetof (EP4_RAIL_ELAN, r_flush_event)));
++      
++      if ((ecq->ecq_flushcq = ep4_get_ecq (rail, EP4_ECQ_SINGLE, 1)) == NULL)
++      {
++          ep_free_elan (&rail->r_generic, ecq->ecq_event_addr, sizeof (E4_Event32));
++          ep4_free_ecq (rail, ecq);
++          return NULL;
++      }
++    }
++
++    spin_lock_irqsave (&rail->r_ecq_lock, flags);
++    list_add (&ecq->ecq_link, &rail->r_ecq_list[which]);
++
++    ecq->ecq_avail -= ndwords;
++    spin_unlock_irqrestore (&rail->r_ecq_lock, flags);
++
++    return ecq;
++}
++
++void
++ep4_put_ecq (EP4_RAIL *rail, EP4_ECQ *ecq, unsigned ndwords)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&rail->r_ecq_lock, flags);
++
++    ecq->ecq_avail += ndwords;
++    
++    if (ecq->ecq_avail !=  CQ_SIZE_NWORDS) 
++      spin_unlock_irqrestore (&rail->r_ecq_lock, flags);
++    else
++    {
++      list_del (&ecq->ecq_link);
++      spin_unlock_irqrestore (&rail->r_ecq_lock, flags);
++      
++      if (ecq->ecq_flushcq)
++          ep4_put_ecq (rail, ecq->ecq_flushcq, 1);
++      if (ecq->ecq_event_addr)
++          ep_free_elan (&rail->r_generic, ecq->ecq_event_addr, sizeof (E4_Event32));
++
++      ep4_free_ecq (rail, ecq);
++    }
++}
++
++void
++ep4_nop_cmd (EP4_ECQ *ecq, E4_uint64 tag)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&ecq->ecq_lock, flags);
++    elan4_nop_cmd (ecq->ecq_cq, tag);
++    spin_unlock_irqrestore (&ecq->ecq_lock, flags);
++    
++}
++
++void
++ep4_set_event_cmd (EP4_ECQ *ecq, E4_Addr event)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&ecq->ecq_lock, flags);
++    elan4_set_event_cmd (ecq->ecq_cq, event);
++    spin_unlock_irqrestore (&ecq->ecq_lock, flags);
++}
++
++void
++ep4_wait_event_cmd (EP4_ECQ *ecq, E4_Addr event, E4_uint64 candt, E4_uint64 param0, E4_uint64 param1)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&ecq->ecq_lock, flags);
++    elan4_wait_event_cmd (ecq->ecq_cq, event, candt, param0, param1);
++    spin_unlock_irqrestore (&ecq->ecq_lock, flags);
++}
++
++void
++ep4_flush_interrupt (EP4_RAIL *rail, void *arg)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&rail->r_ecq_lock, flags);
++    rail->r_flush_count = 0;
++    kcondvar_wakeupone (&rail->r_flush_sleep, &rail->r_ecq_lock);
++    spin_unlock_irqrestore (&rail->r_ecq_lock, flags);
++}
++
++void
++ep4_flush_ecqs (EP4_RAIL *rail)
++{
++    ELAN4_DEV *dev = rail->r_ctxt.ctxt_dev;
++    struct list_head *el;
++    unsigned long flags;
++    int i;
++
++    kmutex_lock (&rail->r_flush_mutex);
++
++    EP4_SDRAM_ASSERT (rail, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_flush_event), E4_EVENT_INIT_VALUE (0, E4_EVENT_WRITE, E4_EVENT_DTYPE_LONG,0));
++
++    spin_lock_irqsave (&rail->r_ecq_lock, flags);
++    /* first flush all the "event" queues */
++    list_for_each (el, &rail->r_ecq_list[EP4_ECQ_EVENT]) {
++      EP4_ECQ *ecq = list_entry (el, EP4_ECQ, ecq_link);
++
++      elan4_sdram_writeq (dev, ecq->ecq_event + offsetof (E4_Event32, ev_CountAndType),
++                          E4_EVENT_INIT_VALUE (-32, E4_EVENT_WRITE, E4_EVENT_DTYPE_LONG, 0));
++
++      ep4_set_event_cmd (ecq->ecq_flushcq, ecq->ecq_event_addr);
++
++      rail->r_flush_count++;
++    }
++
++    /* next issue the setevents to all the other queues */
++    for (i = EP4_ECQ_ATOMIC; i <EP4_NUM_ECQ; i++)
++    {
++      list_for_each (el,&rail->r_ecq_list[i]) {
++          EP4_ECQ *ecq = list_entry (el, EP4_ECQ, ecq_link);
++
++          ep4_set_event_cmd (ecq, rail->r_elan_addr + offsetof (EP4_RAIL_ELAN, r_flush_event));
++
++          rail->r_flush_count++;
++      }
++    }
++
++    /* issue the waitevent command */
++    ep4_wait_event_cmd (rail->r_flush_mcq,  rail->r_elan_addr + offsetof (EP4_RAIL_ELAN, r_flush_event),
++                      E4_EVENT_INIT_VALUE (-32 * rail->r_flush_count, E4_EVENT_WRITE, E4_EVENT_DTYPE_LONG,0),
++                      rail->r_flush_ecq->ecq_addr,
++                      INTERRUPT_CMD | (rail->r_flush_intcookie.int_val << E4_MAIN_INT_SHIFT));
++    
++    while (rail->r_flush_count)
++      kcondvar_wait (&rail->r_flush_sleep, &rail->r_ecq_lock, &flags);
++    
++    spin_unlock_irqrestore (&rail->r_ecq_lock, flags);
++
++    EP4_SDRAM_ASSERT (rail, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_flush_event), E4_EVENT_INIT_VALUE (0, E4_EVENT_WRITE, E4_EVENT_DTYPE_LONG,0));
++
++    kmutex_unlock (&rail->r_flush_mutex);
++}
++
++void
++ep4_init_thread (EP4_RAIL *rail, E4_ThreadRegs *regs, sdramaddr_t stackTop, 
++               EP_ADDR stackAddr, E4_Addr startpc, int nargs,...)
++{
++    sdramaddr_t   sp = stackTop - roundup (nargs * sizeof (E4_uint64), E4_STACK_ALIGN);
++    int           i;
++    va_list       ap;
++    
++    /*
++     * the thread start code expects the following :
++     *   %r1 = stack pointer
++     *   %r6 = frame pointer
++     *   %r2 = function to call
++     *
++     *   function args are store on stack above %sp
++     */
++
++    va_start(ap, nargs);
++    for (i = 0; i < nargs; i++)
++      elan4_sdram_writeq (rail->r_ctxt.ctxt_dev, sp + (i * sizeof (E4_uint64)), va_arg (ap, E4_uint64));
++    va_end (ap);
++    
++    regs->Registers[0] = ep_symbol (&rail->r_threadcode, ".thread_start");            /* %r0 - PC */
++    regs->Registers[1] = stackAddr - (stackTop - sp);                                 /* %r1 - stack pointer */
++    regs->Registers[2] = startpc;                                                     /* %r2 - start pc */
++    regs->Registers[3] = 0;
++    regs->Registers[4] = 0;
++    regs->Registers[5] = 0;
++    regs->Registers[6] = stackTop;                                                    /* %r6 - frame pointer */ 
++}
++
++/* retransmission thread */
++
++void
++ep4_add_retry_ops (EP4_RAIL *rail, EP4_RETRY_OPS *ops)
++{
++    ep_kthread_stall (&rail->r_retry_thread);
++    list_add_tail (&ops->op_link, &rail->r_retry_ops);
++    ep_kthread_resume (&rail->r_retry_thread);
++}
++
++void
++ep4_remove_retry_ops (EP4_RAIL *rail, EP4_RETRY_OPS *ops)
++{
++    ep_kthread_stall (&rail->r_retry_thread);
++    list_del (&ops->op_link);
++    ep_kthread_resume (&rail->r_retry_thread);
++}
++
++void
++ep4_retry_thread (EP4_RAIL *rail)
++{
++    struct list_head *el;
++
++    kernel_thread_init ("ep4_retry");
++    
++    for (;;)
++    {
++      long nextRunTime = 0;
++
++      list_for_each (el, &rail->r_retry_ops) {
++          EP4_RETRY_OPS *ops = list_entry (el, EP4_RETRY_OPS, op_link);
++
++          nextRunTime = ops->op_func (rail, ops->op_arg, nextRunTime);
++      }
++
++      if (ep_kthread_sleep (&rail->r_retry_thread, nextRunTime) < 0)
++          break;
++    }
++
++    ep_kthread_stopped (&rail->r_retry_thread);
++
++    kernel_thread_exit();
++}
++
++/* DMA retransmission */
++static unsigned ep4_dma_retry_times[EP_NUM_RETRIES];
++
++static unsigned long
++ep4_retry_dmas (EP4_RAIL *rail, void *arg, unsigned long nextRunTime)
++{
++    unsigned long yieldAt = lbolt + (hz/10);
++    unsigned long flags;
++    int           i;
++
++    for (i = EP_RETRY_BASE; i < EP_NUM_RETRIES; i++)
++    {
++      while (! list_empty (&rail->r_dma_retrylist[i]))
++      {
++          EP4_DMA_RETRY *retry = list_entry (rail->r_dma_retrylist[i].next, EP4_DMA_RETRY, retry_link);
++          
++          if (! AFTER(lbolt, retry->retry_time))
++              break;
++
++          if (ep_kthread_should_stall (&rail->r_retry_thread) || AFTER (lbolt, yieldAt))
++              goto cant_do_more;
++          
++          EPRINTF3 (DBG_RETRY, "%s: ep4_retry_dmas: flowcnt %llx %llx\n", rail->r_generic.Name, rail->r_dma_flowcnt, rail->r_main->r_dma_flowcnt);
++
++          if ((rail->r_dma_flowcnt - rail->r_main->r_dma_flowcnt) > EP4_DMA_RETRY_FLOWCNT)
++          {
++              printk ("ep4_retry_dmas: flowcnt %llx %llx\n", rail->r_dma_flowcnt, rail->r_main->r_dma_flowcnt);
++
++              goto cant_do_more;
++          }
++
++          EPRINTF4 (DBG_RETRY, "%s: ep4_retry_dmas: %016llx %016llx %016llx\n", rail->r_generic.Name,
++                    retry->retry_dma.dma_typeSize, retry->retry_dma.dma_cookie, retry->retry_dma.dma_vproc);
++          EPRINTF5 (DBG_RETRY, "%s:                  %016llx %016llx %016llx %016llx\n", rail->r_generic.Name,
++                    retry->retry_dma.dma_srcAddr, retry->retry_dma.dma_dstAddr, retry->retry_dma.dma_srcEvent, 
++                    retry->retry_dma.dma_dstEvent);
++
++          elan4_run_dma_cmd (rail->r_dma_ecq->ecq_cq, &retry->retry_dma);
++          elan4_write_dword_cmd (rail->r_dma_ecq->ecq_cq, rail->r_main_addr + offsetof (EP4_RAIL_MAIN, r_dma_flowcnt), ++rail->r_dma_flowcnt);
++
++          spin_lock_irqsave (&rail->r_dma_lock, flags);
++          list_del (&retry->retry_link);
++          list_add (&retry->retry_link, &rail->r_dma_freelist);
++          spin_unlock_irqrestore (&rail->r_dma_lock, flags);
++      }
++    }
++ cant_do_more:
++
++    /* re-compute the next retry time */
++    for (i = EP_RETRY_BASE; i < EP_NUM_RETRIES; i++)
++    {
++      if (! list_empty (&rail->r_dma_retrylist[i]))
++      {
++          EP4_DMA_RETRY *retry = list_entry (rail->r_dma_retrylist[i].next, EP4_DMA_RETRY, retry_link);
++
++          SET_NEXT_RUN_TIME (nextRunTime, retry->retry_time);
++      }
++    }
++
++    return nextRunTime;
++}
++
++void
++ep4_initialise_dma_retries (EP4_RAIL *rail)
++{
++    int i;
++
++    spin_lock_init (&rail->r_dma_lock);
++    
++    for (i = 0; i < EP_NUM_RETRIES; i++)
++      INIT_LIST_HEAD (&rail->r_dma_retrylist[i]);
++    
++    INIT_LIST_HEAD (&rail->r_dma_freelist);
++    
++    rail->r_dma_ecq = ep4_alloc_ecq (rail, EP4_DMA_RETRY_CQSIZE);
++    
++    rail->r_dma_allocated = 0;
++    rail->r_dma_reserved  = 0;
++
++    ep4_dma_retry_times[EP_RETRY_HIGH_PRI] = EP_RETRY_HIGH_PRI_TIME;
++
++    for (i =0 ; i < EP_NUM_BACKOFF; i++)
++      ep4_dma_retry_times[EP_RETRY_HIGH_PRI_RETRY+i] = EP_RETRY_HIGH_PRI_TIME << i;
++    
++    ep4_dma_retry_times[EP_RETRY_LOW_PRI] = EP_RETRY_LOW_PRI_TIME;
++
++    for (i =0 ; i < EP_NUM_BACKOFF; i++)
++      ep4_dma_retry_times[EP_RETRY_LOW_PRI_RETRY+i] = EP_RETRY_LOW_PRI_TIME << i;
++    
++    ep4_dma_retry_times[EP_RETRY_ANONYMOUS] = EP_RETRY_ANONYMOUS_TIME;
++    ep4_dma_retry_times[EP_RETRY_NETERR]    = EP_RETRY_NETERR_TIME;
++
++    rail->r_dma_ops.op_func = ep4_retry_dmas;
++    rail->r_dma_ops.op_arg  = NULL;
++
++    ep4_add_retry_ops (rail, &rail->r_dma_ops);
++}
++
++void
++ep4_finalise_dma_retries (EP4_RAIL *rail)
++{
++    ep4_remove_retry_ops (rail, &rail->r_dma_ops);
++
++    /* Everyone should have given back their retry dma's by now */
++    EP4_ASSERT (rail, rail->r_dma_reserved == 0);
++
++    while (! list_empty (&rail->r_dma_freelist))
++    {
++      EP4_DMA_RETRY *retry = list_entry (rail->r_dma_freelist.next, EP4_DMA_RETRY, retry_link);
++
++      list_del (&retry->retry_link);
++
++      KMEM_FREE (retry, sizeof (EP4_DMA_RETRY));
++    }
++
++    ep4_free_ecq (rail, rail->r_dma_ecq);
++
++    spin_lock_destroy (&rail->r_dma_lock);
++}
++
++int
++ep4_reserve_dma_retries (EP4_RAIL *rail, unsigned int count, EP_ATTRIBUTE attr)
++{
++    EP4_DMA_RETRY *retry;
++    unsigned int   remaining = count;
++    unsigned long  flags;
++
++    spin_lock_irqsave (&rail->r_dma_lock, flags);
++
++    if (remaining <= (rail->r_dma_allocated - rail->r_dma_reserved))
++    {
++      rail->r_dma_reserved += remaining;
++
++      spin_unlock_irqrestore (&rail->r_dma_lock, flags);
++
++      return 0;
++    }
++    
++    remaining -= (rail->r_dma_allocated - rail->r_dma_reserved);
++
++    rail->r_dma_reserved = rail->r_dma_allocated;
++
++    spin_unlock_irqrestore (&rail->r_dma_lock, flags);
++
++    while (remaining > 0)
++    {
++      KMEM_ALLOC (retry, EP4_DMA_RETRY *, sizeof (EP4_DMA_RETRY), !(attr & EP_NO_SLEEP));
++
++      if (retry == NULL)
++          goto failed;
++      
++      remaining--;
++
++      spin_lock_irqsave (&rail->r_dma_lock, flags);
++      list_add (&retry->retry_link, &rail->r_dma_freelist);
++
++      rail->r_dma_allocated++;
++      rail->r_dma_reserved++;
++      spin_unlock_irqrestore (&rail->r_dma_lock, flags);
++    }
++
++    return 0;
++
++ failed:
++    spin_lock_irqsave (&rail->r_dma_lock, flags);
++    rail->r_dma_reserved -= (count - remaining);
++    spin_unlock_irqrestore (&rail->r_dma_lock, flags);
++
++    return 1;
++}
++
++void
++ep4_release_dma_retries (EP4_RAIL *rail, unsigned int count)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave (&rail->r_dma_lock, flags);
++    rail->r_dma_reserved -= count;
++    spin_unlock_irqrestore (&rail->r_dma_lock, flags);
++}
++
++void
++ep4_queue_dma_retry (EP4_RAIL *rail, E4_DMA *dma, int interval)
++{
++    EP4_DMA_RETRY *retry;
++    unsigned long  flags;
++    
++    spin_lock_irqsave (&rail->r_dma_lock, flags);
++
++    EP4_ASSERT (rail, !list_empty (&rail->r_dma_freelist));
++    
++    /* take an item of the free list */
++    retry = list_entry (rail->r_dma_freelist.next, EP4_DMA_RETRY, retry_link);
++
++    list_del (&retry->retry_link);
++    
++    EPRINTF5 (DBG_RETRY, "%s: ep4_queue_dma_retry: %016llx %016llx %016llx %016llx\n", rail->r_generic.Name,
++            dma->dma_typeSize, dma->dma_cookie, dma->dma_vproc, dma->dma_srcAddr);
++    EPRINTF5 (DBG_RETRY, "%s:                      %016llx %016llx %016llx (%d)\n", rail->r_generic.Name,
++            dma->dma_dstAddr, dma->dma_srcEvent, dma->dma_dstEvent, interval);
++
++    retry->retry_dma.dma_typeSize = dma->dma_typeSize;
++    retry->retry_dma.dma_cookie   = dma->dma_cookie;
++    retry->retry_dma.dma_vproc    = dma->dma_vproc;
++    retry->retry_dma.dma_srcAddr  = dma->dma_srcAddr;
++    retry->retry_dma.dma_dstAddr  = dma->dma_dstAddr;
++    retry->retry_dma.dma_srcEvent = dma->dma_srcEvent;
++    retry->retry_dma.dma_dstEvent = dma->dma_dstEvent;
++
++    retry->retry_time             = lbolt + ep4_dma_retry_times[interval];
++
++    /* chain onto the end of the approriate retry list */
++    list_add_tail (&retry->retry_link, &rail->r_dma_retrylist[interval]);
++
++    ep_kthread_schedule (&rail->r_retry_thread, retry->retry_time);
++
++    spin_unlock_irqrestore (&rail->r_dma_lock, flags);
++}
++
++void
++ep4_queue_dma_stalled (EP4_RAIL *rail, E4_DMA *dma)
++{
++    EP_NODE_RAIL  *nodeRail = &rail->r_generic.Nodes[EP_VP_TO_NODE(dma->dma_vproc)];
++    EP4_DMA_RETRY *retry;
++    unsigned long  flags;
++    
++    spin_lock_irqsave (&rail->r_dma_lock, flags);
++
++    EP4_ASSERT (rail, !list_empty (&rail->r_dma_freelist));
++    
++    /* take an item of the free list */
++    retry = list_entry (rail->r_dma_freelist.next, EP4_DMA_RETRY, retry_link);
++
++    list_del (&retry->retry_link);
++    
++    EPRINTF5 (DBG_RETRY, "%s: ep4_queue_dma_stalled: %016llx %016llx %016llx %016llx\n", rail->r_generic.Name,
++            dma->dma_typeSize, dma->dma_cookie, dma->dma_vproc, dma->dma_srcAddr);
++    EPRINTF4 (DBG_RETRY, "%s:                        %016llx %016llx %016llx\n", rail->r_generic.Name,
++            dma->dma_dstAddr, dma->dma_srcEvent, dma->dma_dstEvent);
++
++    retry->retry_dma.dma_typeSize = dma->dma_typeSize;
++    retry->retry_dma.dma_cookie   = dma->dma_cookie;
++    retry->retry_dma.dma_vproc    = dma->dma_vproc;
++    retry->retry_dma.dma_srcAddr  = dma->dma_srcAddr;
++    retry->retry_dma.dma_dstAddr  = dma->dma_dstAddr;
++    retry->retry_dma.dma_srcEvent = dma->dma_srcEvent;
++    retry->retry_dma.dma_dstEvent = dma->dma_dstEvent;
++
++    /* chain onto the node cancelled dma list */
++    list_add_tail (&retry->retry_link, &nodeRail->StalledDmas);
++
++    spin_unlock_irqrestore (&rail->r_dma_lock, flags);
++}
++
++void
++ep4_free_stalled_dmas (EP4_RAIL *rail, unsigned int nodeId)
++{
++    EP_NODE_RAIL *nodeRail = &rail->r_generic.Nodes[nodeId];
++    struct list_head *el, *nel;
++    unsigned long flags;
++
++    spin_lock_irqsave (&rail->r_dma_lock, flags);
++    list_for_each_safe (el, nel, &nodeRail->StalledDmas) {
++      list_del (el);
++      list_add (el, &rail->r_dma_freelist);
++    }
++    spin_unlock_irqrestore (&rail->r_dma_lock, flags);
++}
++
++void
++ep4_display_rail (EP4_RAIL *rail)
++{
++    ELAN4_DEV        *dev = rail->r_ctxt.ctxt_dev;
++    struct list_head *el;
++    register int      i;
++    unsigned long     flags;
++
++    ep_debugf (DBG_DEBUG, "%s: vendorid=%x deviceid=%x\n", rail->r_generic.Name, 
++             rail->r_generic.Devinfo.dev_vendor_id, rail->r_generic.Devinfo.dev_device_id);
++
++    spin_lock_irqsave (&rail->r_ecq_lock, flags);
++    for (i = 0; i < EP4_NUM_ECQ; i++)
++    {
++      list_for_each (el, &rail->r_ecq_list[i]) {
++          EP4_ECQ *ecq = list_entry (el, EP4_ECQ, ecq_link);
++          
++          if (i == EP4_ECQ_EVENT)
++              ep_debugf (DBG_DEBUG, "   ECQ[%d] ecq=%p cqnum=%d addr=%llx avail=%d event=%llx,%llx,%llx\n",
++                         i, ecq, elan4_cq2num (ecq->ecq_cq), ecq->ecq_addr, ecq->ecq_avail,
++                         elan4_sdram_readq (dev, ecq->ecq_event + offsetof (E4_Event32, ev_CountAndType)),
++                         elan4_sdram_readq (dev, ecq->ecq_event + offsetof (E4_Event32, ev_WriteValue)),
++                         elan4_sdram_readq (dev, ecq->ecq_event + offsetof (E4_Event32, ev_WritePtr)));
++
++          else
++              ep_debugf (DBG_DEBUG, "   ECQ[%d] ecq=%p cqnum=%d addr=%llx avail=%d\n",
++                         i, ecq, elan4_cq2num (ecq->ecq_cq), ecq->ecq_addr, ecq->ecq_avail);
++      }
++    }
++    spin_unlock_irqrestore (&rail->r_ecq_lock, flags);
++
++    ep_debugf (DBG_DEBUG, "   flush count=%ld mcq=%p ecq=%p event %llx.%llx.%llx\n", 
++             rail->r_flush_count, rail->r_flush_mcq, rail->r_flush_ecq,
++             elan4_sdram_readq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_flush_event.ev_CountAndType)),
++             elan4_sdram_readq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_flush_event.ev_WritePtr)),
++             elan4_sdram_readq (dev, rail->r_elan + offsetof (EP4_RAIL_ELAN, r_flush_event.ev_WriteValue)));
++    
++    spin_lock_irqsave (&rail->r_dma_lock, flags);
++    for (i = 0; i < EP_NUM_RETRIES; i++)
++    {
++      list_for_each (el, &rail->r_dma_retrylist[i]) {
++          EP4_DMA_RETRY *retry = list_entry (el, EP4_DMA_RETRY, retry_link);
++          
++          ep_debugf (DBG_DEBUG, "    RETRY[%d] typeSize %llx cookie %llx vproc %llx events %llx %llx\n",
++                     i, retry->retry_dma.dma_typeSize, retry->retry_dma.dma_cookie,
++                     retry->retry_dma.dma_vproc, retry->retry_dma.dma_srcEvent, retry->retry_dma.dma_dstEvent);
++      }
++    }
++    spin_unlock_irqrestore (&rail->r_dma_lock, flags);
++}
+Index: linux-2.6.5/drivers/net/qsnet/ep/threadcode.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/threadcode.c 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/threadcode.c      2005-05-11 12:10:12.546916160 -0400
+@@ -0,0 +1,146 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: threadcode.c,v 1.11 2003/10/07 13:22:38 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/threadcode.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++
++EP_ADDR
++ep_symbol (EP_CODE *code, char *name)
++{
++    EP_SYMBOL *s = code->symbols;
++    
++    while (s->name && strcmp (s->name, name))
++      s++;
++    
++    return (s->name ? s->value : (EP_ADDR) 0);
++}
++
++int
++ep_loadcode (EP_RAIL *rail, EP_CODE *code)
++{
++    register int i;
++
++    EP_ADDR  _stext  = ep_symbol (code, "_stext");
++    EP_ADDR  _etext  = ep_symbol (code, "_etext");
++    EP_ADDR  _sdata  = ep_symbol (code, "_sdata");
++    EP_ADDR  _edata  = ep_symbol (code, "_edata");
++    EP_ADDR  _end    = ep_symbol (code, "_end");
++    EP_ADDR  _rodata = roundup (_etext, sizeof (uint64_t));
++
++    if (_stext == (EP_ADDR) 0 || _etext == (EP_ADDR) 0 ||
++      _sdata == (EP_ADDR) 0 || _edata == (EP_ADDR) 0 ||
++      _end == (EP_ADDR) 0)
++    {
++      printk ("ep_loadcode: symbols not defined correctly for code at %p\n", code);
++      return (EINVAL);
++    }
++
++    /*
++     * Include the rodata in the text segment
++     */
++    _etext = _rodata + code->rodata_size;
++
++    /*
++     * If _etext is in the same page as _sdata,  then allocate a contiguous
++     * chunk of memory and map it as read/write. otherwise allocate two chunks
++     * and map the code in as read-only.
++     */
++    if ((_etext & PAGEMASK) == (_sdata & PAGEMASK))
++    {
++      code->ntext  = btopr (_end - (_stext & PAGEMASK));
++      code->pptext = ep_alloc_memory_elan (rail, _stext & PAGEMASK, ptob (code->ntext), EP_PERM_EXECUTE, 0);
++
++      if (code->pptext == (sdramaddr_t) 0)
++          return (ENOMEM);
++      
++      code->_stext  = code->pptext + (_stext & PAGEOFFSET);
++      code->_rodata = code->_stext + (_rodata - _stext);
++      code->_sdata  = code->_stext + (_sdata - _stext);
++    }
++    else
++    {
++      code->ntext  = btopr (_etext - (_stext & PAGEMASK));
++      code->ndata  = btopr (_end - (_sdata & PAGEMASK));
++
++      if (code->ntext)
++      {
++          code->pptext = ep_alloc_memory_elan (rail, _stext & PAGEMASK, ptob (code->ntext), EP_PERM_EXECUTE, 0);
++
++          if (code->pptext == (sdramaddr_t) 0)
++              return (ENOMEM);
++
++          code->_stext  = code->pptext + (_stext & PAGEOFFSET);
++          code->_rodata = code->_stext + (_rodata - _stext);
++      }
++      
++      if (code->ndata)
++      {
++          code->ppdata = ep_alloc_memory_elan (rail, _sdata & PAGEMASK, ptob (code->ndata), EP_PERM_WRITE, 0);
++
++          if (code->ppdata == (sdramaddr_t) 0)
++          {
++              if (code->ntext) ep_free_memory_elan (rail, _sdata & PAGEMASK);
++              code->ntext = 0;
++
++              return (ENOMEM);
++          }
++          
++          code->_sdata = code->ppdata + (_sdata & PAGEOFFSET);
++      }
++    }
++    
++#ifdef __LITTLE_ENDIAN__
++#  define Flip        3
++#else
++#  define Flip  0
++#endif
++
++    /*
++     * Now copy the text and rodata into the SDRAM
++     * this is linked into the module to be byte 
++     * copied to the SDRAM, since we want to copy
++     * with word accesses we have to do the byte
++     * assembly correctly.
++     */
++    for (i = 0; i < code->text_size; i++)
++      rail->Operations.SdramWriteb (rail, code->_stext + i, code->text[i^Flip]);
++
++    for (i = 0; i < code->rodata_size; i++)
++      rail->Operations.SdramWriteb (rail, code->_rodata + i, code->rodata[i^Flip]);
++    
++    /*
++     * And the initialised data segment.
++     */
++    for (i = 0; i < code->data_size; i++)
++      rail->Operations.SdramWriteb (rail, code->_sdata + i, code->data[i^Flip]);
++
++    return (ESUCCESS);
++}
++
++void
++ep_unloadcode (EP_RAIL *rail, EP_CODE *code)
++{
++    EP_ADDR  _stext = ep_symbol (code, "_stext");
++    EP_ADDR  _sdata = ep_symbol (code, "_sdata");
++
++    if (code->pptext)
++      ep_free_memory_elan (rail, _stext & PAGEMASK);
++    if (code->ppdata)
++      ep_free_memory_elan (rail, _sdata & PAGEMASK);
++    code->pptext = code->ppdata = 0;
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/ep/threadcode_elan3.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/threadcode_elan3.c   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/threadcode_elan3.c        2005-05-11 12:10:12.547916008 -0400
+@@ -0,0 +1,85 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: threadcode_elan3.c,v 1.11 2003/10/07 13:22:38 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/threadcode_elan3.c,v $ */
++
++#include <qsnet/kernel.h>
++
++#include <elan/kcomm.h>
++
++#include "kcomm_elan3.h"
++#include "debug.h"
++
++#include <elan3/thread.h>
++
++E3_Addr
++ep3_init_thread (ELAN3_DEV  *dev,
++               E3_Addr     fn,                                /* Elan address of function */
++               E3_Addr     addr,                              /* Elan address of stack */
++               sdramaddr_t stack,                             /* sdram address of stack */
++               int           stackSize,                       /* stack size (in bytes) */
++               int           nargs,
++               ...)
++{
++    sdramaddr_t  frame;
++    sdramaddr_t  regs;
++    sdramaddr_t  argsp;
++    int                i;
++    va_list      ap;
++
++    /*
++     * Align the stack pointer at the top of the stack and leave space for a stack frame
++     */
++    stack = ((stack + stackSize) & ~(E3_STACK_ALIGN-1)) - sizeof (E3_Frame);
++    addr  = ((addr  + stackSize) & ~(E3_STACK_ALIGN-1)) - sizeof (E3_Frame);
++
++    va_start (ap, nargs);
++
++    if (nargs > 6)
++    {
++      stack -= (((nargs*sizeof (E3_uint32))+E3_STACK_ALIGN-1) & ~(E3_STACK_ALIGN-1));
++      addr  -= (((nargs*sizeof (E3_uint32))+E3_STACK_ALIGN-1) & ~(E3_STACK_ALIGN-1));
++    }
++    
++    frame  = stack;
++    regs   = stack - sizeof (E3_OutsRegs);
++
++    /*
++     * Initialise the registers, and stack frame.
++     */
++    elan3_sdram_writel (dev, regs + offsetof (E3_OutsRegs, o[6]), fn);
++    elan3_sdram_writel (dev, regs + offsetof (E3_OutsRegs, o[7]), 0);
++    
++    if (nargs <= 6)
++    {
++      for (i = 0; i < nargs; i++)
++          elan3_sdram_writel (dev, regs + offsetof (E3_OutsRegs, o[i]), va_arg (ap, E3_uint32));
++    }
++    else
++    {
++      for (i = 0; i < 6; i++)
++          elan3_sdram_writel (dev, regs + offsetof (E3_OutsRegs, o[i]), va_arg (ap, E3_uint32));
++      
++      for (argsp = frame + offsetof (E3_Frame, fr_argx[0]); i < nargs; i++, argsp += sizeof (E3_uint32))
++          elan3_sdram_writel (dev, argsp, va_arg (ap, int));
++    }
++
++    elan3_sdram_writel (dev, frame + offsetof (E3_Frame, fr_savefp), 0);
++    elan3_sdram_writel (dev, frame + offsetof (E3_Frame, fr_savepc), 0);
++
++    va_end (ap);
++
++    return (addr);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/ep/threadcode_elan3_Linux.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/threadcode_elan3_Linux.c     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/threadcode_elan3_Linux.c  2005-05-11 12:10:12.547916008 -0400
+@@ -0,0 +1,112 @@
++/* --------------------------------------------------------*/
++/* MACHINE GENERATED ELAN CODE                             */
++#include <qsnet/kernel.h>
++#include <elan/kcomm.h>
++#include "kcomm_elan3.h"
++static uint32_t threadcode_elan3_text[] = {
++0x80a0239c, 0x00001082, 0x00e0a280, 0x47008002, 0x0020a380, 0x20600288, 0x20200286, 0x43008002, 
++0x00000001, 0x0a006081, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 
++0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 
++0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 
++0x00000001, 0x00000001, 0xa800c613, 0xa300c609, 0x0020108a, 0x0080900b, 0x00006885, 0x0580a080, 
++0x06008002, 0x02a0a080, 0x06008022, 0xffff0296, 0x04008010, 0xff3f0398, 0x1f008010, 0x00201090, 
++0x00007081, 0x1600801c, 0x00000001, 0x60a0239c, 0x00a0a3c0, 0x20a0a3f0, 0x40a0a3e0, 0x00c03f3f, 
++0xf8e017be, 0x04e08f80, 0x06008012, 0x00000001, 0x00c01ffc, 0x0000a081, 0x06008010, 0x40a083e0, 
++0x14e007be, 0x00c01ffc, 0x0000a081, 0x40a083e0, 0x20a083f0, 0x00a083c0, 0x60a0039c, 0x00e0a280, 
++0xbfffbf12, 0x0020a380, 0x03008012, 0x02201090, 0x03201090, 0x08e0c381, 0x80a0039c, 0xe0a0239c, 
++0x60a023de, 0x80a0a3e0, 0xa0a0a3f0, 0x080010b8, 0x090010b0, 0x0a0010b2, 0x04000037, 0x402006b4, 
++0x50200690, 0x01201092, 0x20a0239c, 0x00a0a3f0, 0x00c03f3f, 0x8ce117be, 0x04e08f80, 0x06008012, 
++0x00000001, 0x00c01ff8, 0x0000b081, 0x06008010, 0x00a083f0, 0x14e007be, 0x00c01ff8, 0x0000b081, 
++0x00a083f0, 0x20a0039c, 0x582006d0, 0x0020a280, 0x05008002, 0x0900a280, 0x10008002, 0x50200690, 
++0xeaffbf30, 0x5c2006d4, 0x18001090, 0x19001092, 0x1b800294, 0x0a201096, 0x8affff7f, 0x05201098, 
++0x446026d0, 0x302027f4, 0xdfffbf10, 0x50200690, 0xfdffbf10, 0x446026c0, 0x5c2006e0, 0x0020a480, 
++0xf9ffbf06, 0x18001090, 0x19001092, 0x1b000494, 0x14201096, 0x7bffff7f, 0x0a201098, 0x0020a280, 
++0xf4ffbf22, 0x486026e0, 0x00007081, 0x1600801c, 0x00000001, 0x60a0239c, 0x00a0a3c0, 0x20a0a3f0, 
++0x40a0a3e0, 0x00c03f3f, 0x60e217be, 0x04e08f80, 0x06008012, 0x00000001, 0x00c01ffc, 0x0000a081, 
++0x06008010, 0x40a083e0, 0x14e007be, 0x00c01ffc, 0x0000a081, 0x40a083e0, 0x20a083f0, 0x00a083c0, 
++0x60a0039c, 0xff3f84a0, 0xe0ffbf1c, 0x18001090, 0xd5ffbf30, 0x60a003de, 0x80a083e0, 0xa0a083f0, 
++0x08e0c381, 0xe0a0039c, 0x00a1239c, 0x60a023de, 0x80a0a3e0, 0xa0a0a3f0, 0x44a123d0, 0x090010b0, 
++0x0a0010b6, 0x0b0010b8, 0x0c0010b4, 0x012010ba, 0xdca023fa, 0x142007d2, 0x082007d0, 0x084002b2, 
++0x000027c0, 0xf42006d0, 0x0020a280, 0x15008032, 0xf42006d0, 0x18200790, 0xdca003d2, 0x20a0239c, 
++0x00a0a3f0, 0x00c03f3f, 0x20e317be, 0x04e08f80, 0x06008012, 0x00000001, 0x00c01ff8, 0x0000b081, 
++0x06008010, 0x00a083f0, 0x14e007be, 0x00c01ff8, 0x0000b081, 0x00a083f0, 0x20a0039c, 0xf42006d0, 
++0x0020a280, 0x0a008022, 0xdca023c0, 0x042007d0, 0x0840a680, 0x06008032, 0xdca023c0, 0x18001082, 
++0x0220d091, 0xe1ffbf10, 0xf42006d0, 0x06008010, 0x190010a2, 0x042006d0, 0x00c026d0, 0x18001082, 
++0x0020d091, 0x042006d0, 0x01200290, 0x042026d0, 0x000006d0, 0x0020a280, 0x04008002, 0x18001090, 
++0x4f010040, 0x1b001092, 0xf02006e0, 0x0020a480, 0xf1ffbf02, 0x40b03611, 0x004004d2, 0x01201290, 
++0x0840a280, 0x0e018012, 0x10001096, 0x046004d0, 0x01208a80, 0x33008002, 0xa0200484, 0x0c2610ba, 
++0x000024fa, 0x00211090, 0x042024d0, 0x246004d0, 0x80200290, 0x082024d0, 0xec2004d0, 0x00210290, 
++0x0c2024d0, 0x102024c4, 0x186004d2, 0x02602a93, 0x098006d0, 0x0001003b, 0x1d000290, 0x098026d0, 
++0xc0ff3f3b, 0x1d000a90, 0x44a103fa, 0x606007d2, 0x00680292, 0x09001290, 0x4000003b, 0x1d001290, 
++0x142024d0, 0x206004d0, 0x10210290, 0x182024d0, 0x186004d0, 0x02202a91, 0x088006d2, 0x0001003b, 
++0x1d400292, 0x088026d2, 0xc0ff3f3b, 0x1d400a92, 0x186004d0, 0x00280290, 0x80000015, 0x0a001290, 
++0x08401292, 0x4000003b, 0x1d401292, 0x1c2024d2, 0x01201090, 0xa02024d0, 0x20200496, 0xa8200484, 
++0x306004d0, 0x0020a280, 0x2b008012, 0x00201098, 0x0c2610ba, 0x00c022fa, 0x04e022c0, 0xc0200490, 
++0x10e022d0, 0x186004d2, 0x02602a93, 0x098006d0, 0x0001003b, 0x1d000290, 0x098026d0, 0xc0ff3f3b, 
++0x1d000a90, 0x44a103fa, 0x606007d2, 0x00680292, 0x09001290, 0x4000003b, 0x1d001290, 0x14e022d0, 
++0x206004d0, 0x10210290, 0x18e022d0, 0x186004d0, 0x02202a91, 0x088006d2, 0x0001003b, 0x1d400292, 
++0x088026d2, 0xc0ff3f3b, 0x1d400a92, 0x186004d0, 0x00280290, 0x80000015, 0x0a001290, 0x08401292, 
++0x4000003b, 0x1d401292, 0x1ce022d2, 0x4f008010, 0x0020109a, 0x0c00109a, 0x306004d0, 0x0840a380, 
++0x3b00801a, 0xe02004c6, 0x0c2610ba, 0x00c022fa, 0x01202b91, 0x0c000290, 0x02202a91, 0x08400490, 
++0x382002d2, 0x04e022d2, 0x342002d0, 0x08e022d0, 0x0ce022c6, 0x10e022c4, 0x186004d0, 0x02202a91, 
++0x088006d2, 0x0001003b, 0x1d400292, 0x088026d2, 0xc0ff3f3b, 0x1d400a92, 0x44a103fa, 0x606007d0, 
++0x00280290, 0x08401292, 0x4000003b, 0x1d401292, 0x14e022d2, 0x206004d0, 0x10210290, 0x18e022d0, 
++0x186004d0, 0x02202a91, 0x088006d4, 0x0001003b, 0x1d800294, 0x088026d4, 0xc0ff3f3b, 0x1d800a94, 
++0x186004d0, 0x00280290, 0x80000013, 0x09001290, 0x08801294, 0x4000003b, 0x1d801294, 0x1ce022d4, 
++0x01201090, 0x008020d0, 0x04e002d0, 0x08c00086, 0x0840039a, 0x01200398, 0x20e00296, 0x306004d0, 
++0x0800a380, 0xc9ffbf0a, 0x08a00084, 0xc0200490, 0xf0ff22d0, 0xe42004d0, 0x0d00a280, 0x0b00801a, 
++0x00201098, 0x04008010, 0x10001096, 0x01200398, 0x20e00296, 0x306004d0, 0x0800a380, 0xfcffbf2a, 
++0x04e022c0, 0xfc3f109a, 0xe42024da, 0x10001082, 0x186004d0, 0x00280290, 0x08006081, 0x00000001, 
++0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 
++0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 
++0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00201098, 
++0x0c00109a, 0x142004fa, 0xec00823b, 0x3080d61b, 0x00006891, 0x0420a280, 0x3b008002, 0x0c00a280, 
++0x04008002, 0x00000001, 0x0120d091, 0x36008030, 0x7c2006d0, 0x01200290, 0x7c2026d0, 0x782006d0, 
++0x0020a280, 0x04008002, 0x78200690, 0x64000040, 0x40e00692, 0xf02004d0, 0x0020a280, 0x03008012, 
++0xf02026d0, 0x80e026c0, 0x7c2006d0, 0x40e026d0, 0x046004d0, 0x04208a80, 0x13008002, 0x1100108a, 
++0xec2004cc, 0x3fa00b8e, 0x40e0018e, 0x0780239c, 0x0080bbe0, 0x006099e0, 0x00a0b9e0, 0x406099e0, 
++0x40a0b9e0, 0x806099e0, 0x80a0b9e0, 0xc06099e0, 0xc0a0b9e0, 0x00809be0, 0x0780039c, 0x0e008010, 
++0xec2004d2, 0xec2004cc, 0x3fa00b8e, 0x40e0018e, 0x0780239c, 0x0080bbe0, 0x006099e0, 0x00a0b9e0, 
++0x406099e0, 0x40a0b9e0, 0x00809be0, 0x0780039c, 0xec2004d2, 0xe42004d0, 0x886222d0, 0x042006d0, 
++0x00c026d0, 0x000007d0, 0x01208a80, 0x05008012, 0x00000001, 0x142027f2, 0x06008010, 0xdca003fa, 
++0x142027f2, 0xfe3f0a90, 0x000027d0, 0xdca003fa, 0x016007ba, 0xdca023fa, 0x0c2007d0, 0x0840a680, 
++0x04008032, 0x082007d0, 0x03008010, 0x102007f2, 0x084006b2, 0x00007081, 0x1600801c, 0x00000001, 
++0x60a0239c, 0x00a0a3c0, 0x20a0a3f0, 0x40a0a3e0, 0x02c03f3f, 0x8ce017be, 0x04e08f80, 0x06008012, 
++0x00000001, 0x00c01ffc, 0x0000a081, 0x06008010, 0x40a083e0, 0x14e007be, 0x00c01ffc, 0x0000a081, 
++0x40a083e0, 0x20a083f0, 0x00a083c0, 0x60a0039c, 0x042007d0, 0x0840a680, 0xb3febf12, 0x190010a2, 
++0x8afebf10, 0xf42006d0, 0x60a003de, 0x80a083e0, 0xa0a083f0, 0x08e0c381, 0x00a1039c, 0x80a0239c, 
++0x042002c4, 0x004022c4, 0x18008030, 0x00007081, 0x16008012, 0x00000001, 0x60a0239c, 0x00a0a3c0, 
++0x20a0a3f0, 0x40a0a3e0, 0x02c03f3f, 0x24e117be, 0x04e08f80, 0x06008012, 0x00000001, 0x00c01ffc, 
++0x0000a081, 0x06008010, 0x40a083e0, 0x14e007be, 0x00c01ffc, 0x0000a081, 0x40a083e0, 0x20a083f0, 
++0x00a083c0, 0x60a0039c, 0x000002c4, 0x00a0a080, 0xe7ffbf12, 0x00000001, 0x042002c4, 0x01a00084, 
++0x042022c4, 0x000002c4, 0x00a0a080, 0xddffbf12, 0x00000001, 0x08e0c381, 0x80a0039c, };
++#define threadcode_elan3_text_size 0x97c
++static uint32_t threadcode_elan3_data[] = {
++0};
++#define threadcode_elan3_data_size 0x0
++static uint32_t threadcode_elan3_rodata[] = {
++0};
++#define threadcode_elan3_rodata_size 0x0
++static EP_SYMBOL threadcode_elan3_symbols[] = {
++    {"__bss_start", 0xff00297c},
++    {"_edata", 0xff00297c},
++    {"_end", 0xff002988},
++    {"_etext", 0xff00097c},
++    {"_sdata", 0xff00297c},
++    {"_stext", 0xff000000},
++    {"ep3_spinblock", 0xff0008dc},
++    {"ep3comms_rcvr", 0xff0002a8},
++    {"kcomm_probe", 0xff00013c},
++    {"r", 0xff00297c},
++    {"rail", 0xff002984},
++    {"rm", 0xff002980},
++    {0, 0}};
++EP_CODE threadcode_elan3 = {
++   (unsigned char *) threadcode_elan3_text,
++   threadcode_elan3_text_size,
++   (unsigned char *) threadcode_elan3_data,
++   threadcode_elan3_data_size,
++   (unsigned char *) threadcode_elan3_rodata,
++   threadcode_elan3_rodata_size,
++   threadcode_elan3_symbols,
++};
+Index: linux-2.6.5/drivers/net/qsnet/ep/threadcode_elan4_Linux.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/ep/threadcode_elan4_Linux.c     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/ep/threadcode_elan4_Linux.c  2005-05-11 12:10:12.548915856 -0400
+@@ -0,0 +1,112 @@
++/* --------------------------------------------------------*/
++/* MACHINE GENERATED ELAN CODE                             */
++#include <qsnet/kernel.h>
++#include <elan/kcomm.h>
++#include "kcomm_elan4.h"
++static uint32_t threadcode_elan4_text[] = {
++0x00a00087, 0xc04060cb, 0x00003080, 0x80001080, 0x02606180, 0x02004032, 0x807f60cb, 0x04606180, 
++0x02004032, 0x407f60d3, 0x08606180, 0x02004032, 0x007f60db, 0x10606180, 0x02004032, 0xc07e60e3, 
++0x20606180, 0x02004032, 0x807e60eb, 0x40606180, 0x02004032, 0x407e60f3, 0x80606180, 0x02004032, 
++0x007e60fb, 0x40001180, 0xc3801080, 0xc07f60c3, 0x20002000, 0x20002000, 0x20002000, 0x20002000, 
++0x407f8001, 0x4060c0c7, 0x4860c0d0, 0x5060c0d1, 0x5860c0d2, 0x6060c0d3, 0x6860c0d4, 0x00208292, 
++0x00608291, 0x00a08294, 0xff3f8088, 0x1c381293, 0xc04044c8, 0x13004290, 0xc000c5d0, 0x08004030, 
++0x00001088, 0x04204288, 0x0020b200, 0x04004003, 0x00208080, 0x9c010040, 0x00a08488, 0xc04044c8, 
++0x20381288, 0x0020b200, 0xf6ff7f13, 0x01208408, 0x11161282, 0x804094c2, 0xc04044c8, 0x20381288, 
++0x0020b200, 0xebff7f13, 0x00208080, 0x406040c7, 0x486040d0, 0x506040d1, 0x586040d2, 0x606040d3, 
++0x686040d4, 0x08e00180, 0xc0608001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 
++0x807e8001, 0x4060c0c7, 0x4860c0d0, 0x5060c0d1, 0x5860c0d2, 0x6060c0d3, 0x6860c0d4, 0x7060c0d5, 
++0x7860c0d6, 0x8060c0d7, 0x8860c0d8, 0x9060c0d9, 0x9860c0da, 0xa060c0db, 0xa860c0dc, 0xb060c0dd, 
++0xb860c0de, 0xc060c0df, 0x8061c0c8, 0x00608296, 0x00a0829a, 0x9861c0cb, 0xa061c0cc, 0xa861c0cd, 
++0x01208088, 0x3861c0c8, 0x08e042d2, 0x386140c9, 0x0900900a, 0xa06140c8, 0x986140cb, 0x18e042c9, 
++0x72010040, 0x05b4128a, 0x0020808c, 0x3861c0cc, 0x986140c9, 0xc04042c8, 0x0880b400, 0x39014003, 
++0xffff3f08, 0x90a0851c, 0xe023829f, 0x20f4179f, 0x10e3879f, 0xffff3f08, 0xe023829e, 0x20b4179e, 
++0x03a3879e, 0xffff3f08, 0xe023829d, 0x2074179d, 0x0363879d, 0x00a08495, 0x18a08408, 0x800012c2, 
++0x089a109b, 0x20f4169b, 0x20f8169b, 0x00e88609, 0x20741289, 0x01120008, 0x0a381288, 0x08408297, 
++0x45208088, 0x06341288, 0x806140ca, 0xc88042c8, 0x00288218, 0x04a08408, 0x800012c2, 0x089a1088, 
++0x20341288, 0x20381288, 0x00281299, 0x20a08408, 0x800012c2, 0x089a108a, 0x20b4128a, 0x20b8128a, 
++0x30a08408, 0x800012c2, 0x089a1093, 0x20f41493, 0x20f81493, 0x03f41689, 0x806140cb, 0x2922808c, 
++0x0334138c, 0xccc042c8, 0xc90042d1, 0x02604688, 0x0020b200, 0x03004002, 0x60a08214, 0x80a08214, 
++0x90a08509, 0x804012c8, 0x01208208, 0x804092c8, 0x046012c8, 0x043a1288, 0x0020b200, 0x04004003, 
++0xa86140c8, 0x67ffff7f, 0x00a0868a, 0x88a045d0, 0x0020b400, 0x12004013, 0x00208080, 0x800017c8, 
++0x808096c8, 0x72010040, 0x00a08588, 0x00208290, 0x90a08509, 0x804012c8, 0x01208208, 0x804092c8, 
++0x046012c8, 0x043a1288, 0x0020b200, 0x04004003, 0xa86140c8, 0x53ffff7f, 0x00a0868a, 0x804015c2, 
++0x159a1089, 0x20741289, 0x20781289, 0x40b03608, 0x01208288, 0x0840b200, 0x06004023, 0xa02344c4, 
++0x800017c8, 0x808096c8, 0xbb004010, 0xa8a045c8, 0x01604688, 0x00281288, 0x08009008, 0x00e0b400, 
++0x05004003, 0x3f381289, 0x13408209, 0x03004010, 0x05208088, 0x04208088, 0x09009220, 0x07341889, 
++0x0900840b, 0x05341888, 0x0023820a, 0x01604688, 0x0020b200, 0x1d004002, 0x0a00840c, 0xc900c4d7, 
++0x40c40f08, 0x09208288, 0x08e0c2c8, 0x0a608488, 0x10e0c2c8, 0x81001008, 0x0a341288, 0x18e0c2c8, 
++0x1d608488, 0x20e0c2c8, 0x28e0c2d8, 0x24608508, 0x800012c2, 0x089a1088, 0x20341288, 0x20381288, 
++0x80208208, 0x30e0c2c8, 0x00218108, 0x38e0c2c8, 0x40e0c2d4, 0x48e0c2cc, 0xca00c4df, 0x20608411, 
++0x80e0820b, 0x2020830c, 0x00e0b400, 0x13004013, 0x0020808e, 0xc0c0c2d7, 0x40c40f09, 0x09608289, 
++0x08e0c2c9, 0x0a608488, 0x10e0c2c8, 0x00040008, 0x18e0c2c8, 0x1d608488, 0x20e0c2c8, 0x28e0c2d8, 
++0x40e0c2d4, 0x48e0c2cc, 0xc000c3de, 0x00208083, 0x4c004010, 0x20608411, 0xb8238408, 0x800012c2, 
++0x089a108f, 0x20f4138f, 0x20f8138f, 0x00208083, 0x13c0b000, 0x2e00401b, 0x40c40f08, 0x092082a2, 
++0x00040021, 0xffff3f08, 0xe023828d, 0x2074138d, 0x1063838d, 0x0e808309, 0x0e408209, 0x02741289, 
++0x1540820a, 0x38a0820a, 0x808012c2, 0x0a9a108a, 0x20b4128a, 0x20b8128a, 0xc0c0c2d7, 0x08e0c2e2, 
++0x0a608488, 0x10e0c2c8, 0x20b41288, 0x21008288, 0x18e0c2c8, 0x1d608488, 0x20e0c2c8, 0x28e0c2d8, 
++0x15408209, 0x34608209, 0x804012c2, 0x099a1089, 0x20741289, 0x20781289, 0x30e0c2c9, 0x38e0c2cf, 
++0x40e0c2d4, 0x48e0c2cc, 0xc000c3cd, 0x0ac0830f, 0x0ac08003, 0x20608411, 0x80e0820b, 0x01a0830e, 
++0x1380b300, 0xdcff7f0b, 0x2020830c, 0xe03f830c, 0xc000c3dd, 0xbc238408, 0x800012c2, 0x089a1088, 
++0x20341288, 0x20381288, 0x0300b200, 0x0d00401b, 0x07341888, 0x0020888e, 0x0420b800, 0x08004019, 
++0x0800840b, 0x00040008, 0x18e0c2c8, 0x01a0830e, 0x04a0b300, 0xfdff7f09, 0x80e0820b, 0xfc3f8083, 
++0x07341888, 0x08008408, 0xa06140ca, 0xc00062e3, 0x402062f3, 0xc080e2e3, 0xc080e2f3, 0x982244c8, 
++0x88a0c5c8, 0x88a045c8, 0x0020b200, 0x05004013, 0x04604688, 0x88a08508, 0x80a0c5c8, 0x04604688, 
++0x0020b200, 0x0c004002, 0xd822c4c0, 0xc04065e3, 0x406065f3, 0xc000e1e3, 0x806065e3, 0x4020e1f3, 
++0xc06065f3, 0x8020e1e3, 0xc020e1f3, 0x07004010, 0x88228108, 0xc04065e3, 0x406065f3, 0xc000e1e3, 
++0x4020e1f3, 0x88228108, 0x08d61082, 0x800092c2, 0x03f41689, 0x806140cb, 0x2922808c, 0x0334138c, 
++0xccc042c8, 0xc900c2d1, 0x800017c8, 0x808096c8, 0xa8a045c8, 0x0880b400, 0x03004013, 0x00a18412, 
++0xa0a045d2, 0x98a045c8, 0x0020b200, 0x05004013, 0x386140c9, 0x986140c8, 0x0820c2d2, 0x386140c9, 
++0x01608209, 0xfe61b200, 0x0e004015, 0x3861c0c9, 0x00001088, 0x02204288, 0x0020b200, 0x05004003, 
++0x986140ca, 0x28000040, 0xa06140c8, 0x986140ca, 0xc08042c8, 0x0880b400, 0xd8fe7f13, 0x00a08495, 
++0x98a045cb, 0x00e0b200, 0xbafe7f03, 0x386140c9, 0xa06140c8, 0x60a08509, 0x48000040, 0xe03f808a, 
++0x986140cb, 0x08e0c2d2, 0x386140cc, 0x0120830c, 0xaffe7f10, 0x3861c0cc, 0x406040c7, 0x486040d0, 
++0x506040d1, 0x586040d2, 0x606040d3, 0x686040d4, 0x706040d5, 0x786040d6, 0x806040d7, 0x886040d8, 
++0x906040d9, 0x986040da, 0xa06040db, 0xa86040dc, 0xb06040dd, 0xb86040de, 0xc06040df, 0x08e00180, 
++0x80618001, 0x807f8001, 0xc040e0d3, 0x4060e0db, 0x00208490, 0x00208698, 0x00208080, 0x00208080, 
++0x00e08192, 0x02000040, 0x00608091, 0x14e08110, 0x17208097, 0xc000f2d3, 0xc04060d3, 0x406060db, 
++0x08a00080, 0x80608001, 0x407f8001, 0x4060e0d3, 0x8060e0db, 0x00208490, 0x00208698, 0x00208080, 
++0x00208080, 0x00e08192, 0x02000040, 0x00608091, 0x40e08110, 0xc040e0d1, 0x37208097, 0x3860c0d7, 
++0x00208490, 0x00e08597, 0x00208080, 0x00208080, 0x1f608290, 0x20b41291, 0x08638491, 0x00608092, 
++0x00208293, 0xc000f2d1, 0x406060d3, 0x806060db, 0x08a00080, 0xc0608001, 0x407f8001, 0x4060e0d3, 
++0x8060e0db, 0x00208490, 0x00208698, 0x00208080, 0x00208080, 0x00e08192, 0x02000040, 0x00608091, 
++0x54e08110, 0xc040e0d1, 0x37208097, 0x3860c0d7, 0x00208490, 0x00e08597, 0x00208080, 0x00208080, 
++0x1f608290, 0x20b41291, 0x08638491, 0x00608092, 0x00208293, 0x0ef41294, 0x0d208594, 0x17208095, 
++0x17208096, 0x17208097, 0xc000f2d3, 0x406060d3, 0x806060db, 0x08a00080, 0xc0608001, 0x01208097, 
++0xb0e3c0d7, 0x80a060d2, 0x98e28004, 0x98e2c0c0, 0x80a0c0c4, 0xc080c4c3, 0x01e0b400, 0x06004002, 
++0x00a08490, 0x00e08097, 0x02208097, 0xb0e3c0d7, 0xd8e2d0d0, 0xd8e2c0d0, 0x03208097, 0xb0e3c0d7, 
++0x00e08088, 0x0e004010, 0x00a060c3, 0x407f8001, 0x4060e0d3, 0x8060e0db, 0x00208490, 0x00208698, 
++0x00208080, 0x00208080, 0x01208089, 0x8820c2c9, 0x00608091, 0x00e08197, 0x0020f2d3, 0x406060d3, 
++0x806060db, 0x08e00180, 0xc0608001, };
++#define threadcode_elan4_text_size 0x90c
++static uint32_t threadcode_elan4_data[] = {
++0};
++#define threadcode_elan4_data_size 0x0
++static uint32_t threadcode_elan4_rodata[] = {
++0};
++#define threadcode_elan4_rodata_size 0x0
++static EP_SYMBOL threadcode_elan4_symbols[] = {
++    {".thread_restart", 0x00000000f800000c},
++    {".thread_start", 0x00000000f8000000},
++    {"__bss_start", 0x00000000f810090c},
++    {"_edata", 0x00000000f810090c},
++    {"_end", 0x00000000f8100910},
++    {"_etext", 0x00000000f800090c},
++    {"_sdata", 0x00000000f810090c},
++    {"_stext", 0x00000000f8000000},
++    {"c_queue_rxd", 0x00000000f800087c},
++    {"c_reschedule", 0x00000000f8000744},
++    {"c_stall_thread", 0x00000000f80008cc},
++    {"c_waitevent", 0x00000000f8000788},
++    {"c_waitevent_interrupt", 0x00000000f80007f8},
++    {"ep4_spinblock", 0x00000000f8000080},
++    {"ep4comms_rcvr", 0x00000000f8000140},
++    {0, 0}};
++EP_CODE threadcode_elan4 = {
++   (unsigned char *) threadcode_elan4_text,
++   threadcode_elan4_text_size,
++   (unsigned char *) threadcode_elan4_data,
++   threadcode_elan4_data_size,
++   (unsigned char *) threadcode_elan4_rodata,
++   threadcode_elan4_rodata_size,
++   threadcode_elan4_symbols,
++};
+Index: linux-2.6.5/drivers/net/qsnet/jtag/jtagdrv.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/jtag/jtagdrv.c  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/jtag/jtagdrv.c       2005-05-11 12:10:12.549915704 -0400
+@@ -0,0 +1,451 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: jtagdrv.c,v 1.12 2003/06/07 16:02:35 david Exp $"
++/*      $Source: /cvs/master/quadrics/jtagmod/jtagdrv.c,v $*/
++
++#include <qsnet/types.h>
++
++#include "jtagdrv.h"
++#include <jtag/jtagio.h>
++
++int
++jtagdrv_strobe_data (JTAG_DEV *dev, u_char data)
++{
++    u_char dsr;
++
++    PRINTF (DBG_ECPP, ("jtagdrv_strobe_data: %s %s %s -> ", (data & LPT_DATA_TRST) ? "TRST" : "trst", 
++                     (data & LPT_DATA_TDI) ? "TDI" : "tdi", (data & LPT_DATA_TMS) ? "TMS" : "tms"));
++
++
++    LPT_WRITE_DATA (dev, data); DELAY(5);                     /* Drive NEW values on data wires */
++    LPT_WRITE_CTRL (dev, LPT_CTRL_TCLK); DELAY(5);            /* Drive strobe low */
++    LPT_READ_STAT  (dev, dsr); DELAY(5);                      /* Sample TDI from ring */
++    LPT_WRITE_CTRL (dev, 0); DELAY(5);                                /* Drive strobe high */
++
++    PRINTF (DBG_ECPP, ("%s\n", (dsr & LPT_STAT_PE) ? "TDO" : "tdo"));
++
++    return ((dsr & LPT_STAT_PE) ? 1 : 0);
++}
++
++void
++jtagdrv_select_ring (JTAG_DEV *dev, u_int ring)
++{
++    PRINTF (DBG_ECPP, ("jtagdrv_select_ring: ring=0x%x\n", ring));
++
++    LPT_WRITE_CTRL (dev, 0); DELAY(5);                                /* Drive strobe and TCLK high */
++    LPT_WRITE_DATA (dev, ring);       DELAY(5);                       /* Drive ring address */
++    LPT_WRITE_CTRL (dev, LPT_CTRL_RCLK); DELAY(5);            /* Drive strobe low */
++    LPT_WRITE_CTRL (dev, 0); DELAY(5);                                /* Drive strobe high */
++}
++
++void
++jtagdrv_reset (JTAG_DEV *dev)
++{
++    register int i;
++
++    for (i = 0; i < 5; i++)
++      jtagdrv_strobe_data (dev, LPT_DATA_TRST | LPT_DATA_TMS);                /* 5 clocks to Reset from any state */
++    jtagdrv_strobe_data (dev, LPT_DATA_TRST);                                 /* to Run-Test/Idle */
++}
++
++void
++jtagdrv_shift_ir (JTAG_DEV *dev, u_char *value, int nbits)
++{
++    register int i;
++    register int bit;
++
++    jtagdrv_strobe_data (dev, LPT_DATA_TRST | LPT_DATA_TMS);                  /* to Select DR-Scan */
++    jtagdrv_strobe_data (dev, LPT_DATA_TRST | LPT_DATA_TMS);                  /* to Select IR-Scan */
++    jtagdrv_strobe_data (dev, LPT_DATA_TRST);                                 /* to Capture-IR */
++    jtagdrv_strobe_data (dev, LPT_DATA_TRST);                                 /* to Shift-IR */
++    
++    for (i = 0; i < nbits; i++)
++    {
++      /* strobe through the instruction bits,  asserting TMS on the last bit */
++
++      if (i == (nbits-1))
++          bit = jtagdrv_strobe_data (dev, LPT_DATA_TRST | LPT_DATA_TMS | (JTAG_BIT(value, i) ? LPT_DATA_TDI : 0));
++      else
++          bit = jtagdrv_strobe_data (dev, LPT_DATA_TRST | (JTAG_BIT(value, i) ? LPT_DATA_TDI : 0));
++      
++      if (bit)
++          JTAG_SET_BIT(value, i);
++      else
++          JTAG_CLR_BIT(value, i);
++    }
++    
++    jtagdrv_strobe_data (dev, LPT_DATA_TRST | LPT_DATA_TMS);                  /* to Update-IR */
++    jtagdrv_strobe_data (dev, LPT_DATA_TRST);                                 /* to Run-Test/Idle */
++}
++
++
++void
++jtagdrv_shift_dr (JTAG_DEV *dev, u_char *value, int nbits)
++{
++    register int i;
++    register int bit;
++
++    jtagdrv_strobe_data (dev, LPT_DATA_TRST | LPT_DATA_TMS);                  /* to Select DR-Scan */
++    jtagdrv_strobe_data (dev, LPT_DATA_TRST);                                 /* to Capture-DR */
++    jtagdrv_strobe_data (dev, LPT_DATA_TRST);                                 /* to Shift-DR */
++    
++    for (i = 0; i < nbits; i++)
++    {
++      /* strobe through the data bits,  asserting TMS on the last bit */
++
++      if (i == (nbits-1))
++          bit = jtagdrv_strobe_data (dev, LPT_DATA_TRST | LPT_DATA_TMS | (JTAG_BIT(value, i) ? LPT_DATA_TDI : 0));
++      else
++          bit = jtagdrv_strobe_data (dev, LPT_DATA_TRST | (JTAG_BIT(value, i) ? LPT_DATA_TDI : 0));
++      
++      if (bit)
++          JTAG_SET_BIT(value, i);
++      else
++          JTAG_CLR_BIT(value, i);
++    }
++    
++    jtagdrv_strobe_data (dev, LPT_DATA_TRST | LPT_DATA_TMS);                  /* to Update-DR */
++    jtagdrv_strobe_data (dev, LPT_DATA_TRST);                                 /* to Run-Test/Idle */
++}
++
++static int
++jtagdrv_i2c_start (JTAG_DEV *dev)
++{
++    u_char dsr;
++    int i;
++
++    PRINTF (DBG_ECPP, ("jtagdrv_i2c_start\n"));
++    
++    /* Issue a stop sequence */
++    LPT_WRITE_CTRL (dev,  LPT_CTRL_SCLK); DELAY(1);           /* SCLK low */
++    LPT_WRITE_DATA (dev, 0); DELAY(5);                                /* SDA low */
++    LPT_WRITE_CTRL (dev, 0); DELAY(5);                                /* SCLK high */
++    LPT_WRITE_DATA (dev, LPT_DATA_SDA); DELAY(5);             /* SDA high */
++    
++    /* sample the line to see if we're idle */
++    LPT_READ_STAT (dev, dsr);                                 /* sample SDA */
++    if ((dsr & LPT_STAT_SDA) == 0)                            /* Cannot start if SDA already driven */
++    {
++      PRINTF (DBG_ECPP, ("jtagdrv_i2c_start: cannot start - sda driven low\n"));
++
++      for (i = 0; i < 16 ; i++)
++      {
++          LPT_WRITE_CTRL (dev, LPT_CTRL_SCLK); DELAY(5);      /* SCLK low */
++          LPT_WRITE_CTRL (dev, 0); DELAY(5);                  /* SCLK high */
++          LPT_READ_STAT  (dev, dsr);
++          
++          if (dsr & LPT_STAT_SDA)
++          {
++              PRINTF (DBG_ECPP, ("jtagdrv_i2c_start - stopped after %d clocks\n", i));
++              break;
++          }
++      }
++
++      if ((dsr & LPT_STAT_SDA) == 0)
++      {
++          PRINTF (DBG_ECPP, ("jtagdrv_i2c_start - cannot start - not idle\n"));
++          return (0);
++      }
++
++      /* seen SDA float high, so issue a stop sequence */
++      LPT_WRITE_CTRL (dev, LPT_CTRL_SCLK); DELAY(1);          /* SCLK low */
++      LPT_WRITE_DATA (dev, 0); DELAY(5);                      /* SDA low */
++      LPT_WRITE_CTRL (dev, 0); DELAY(5);                      /* SCLK high */
++      LPT_WRITE_DATA (dev, LPT_DATA_SDA); DELAY(5);           /* SDA high */
++    }
++
++    LPT_WRITE_DATA (dev, 0); DELAY(4);                                /* drive SDA low */
++    return (1);
++}
++
++static void
++jtagdrv_i2c_stop (JTAG_DEV *dev)
++{
++    u_char dsr;
++    int    i;
++
++    PRINTF (DBG_ECPP, ("jtagdrv_i2c_stop\n"));
++
++    LPT_WRITE_CTRL (dev, LPT_CTRL_SCLK); DELAY(1);            /* SCLK low */
++    LPT_WRITE_DATA (dev, 0); DELAY(5);                                /* SDA low */
++    LPT_WRITE_CTRL (dev, 0); DELAY(5);                                /* SCLK high */
++    LPT_WRITE_DATA (dev, LPT_DATA_SDA); DELAY(5);             /* SDA high */
++
++    /* 
++     * bug fix for temperature sensor chip
++     * if it's still driving SDA, then clock
++     * it until it stops driving it 
++     */
++    LPT_READ_STAT (dev, dsr);
++    if ((dsr & LPT_STAT_SDA) == 0)
++    {
++      PRINTF (DBG_ECPP, ("jtagdrv_i2c_stop - slave not stodeved\n"));
++      for (i = 0; i < 16 ; i++)
++      {
++          LPT_WRITE_CTRL (dev, LPT_CTRL_SCLK); DELAY(5);      /* SCLK low */
++          LPT_WRITE_CTRL (dev, 0); DELAY(5);                  /* SCLK high */
++          LPT_READ_STAT  (dev, dsr);
++          
++          if (dsr & LPT_STAT_SDA)
++              break;
++      }
++      PRINTF (DBG_ECPP, ("jtagdrv_i2c_stop - stodeved after %d clocks\n", i));
++    }
++}
++
++static int
++jtagdrv_i2c_strobe (JTAG_DEV *dev, u_char data)
++{
++    u_char dsr;
++    
++    PRINTF (DBG_ECPP, ("jtagdrv_i2c_strobe : %s", (data & LPT_DATA_SDA) ? "SDA" : "sda"));
++
++    LPT_WRITE_CTRL (dev, LPT_CTRL_SCLK); DELAY(1);            /* SCLK low */
++    LPT_WRITE_DATA (dev, data);       DELAY(5);                       /* write data */
++    LPT_WRITE_CTRL (dev, 0);                                  /* SCLK high */
++    LPT_READ_STAT  (dev, dsr); DELAY(4);                      /* Sample SDA */
++
++    PRINTF (DBG_ECPP, (" -> %s\n", (dsr & LPT_STAT_SDA) ? "SDA" : "sda"));
++
++    return ((dsr & LPT_STAT_SDA) ? 1 : 0);
++}
++
++static int
++jtagdrv_i2c_get_ack (JTAG_DEV *dev)
++{
++    u_char dsr;
++
++    LPT_WRITE_CTRL (dev, LPT_CTRL_SCLK); DELAY(1);            /* SCLK low */
++    LPT_WRITE_DATA (dev, LPT_DATA_SDA);       DELAY(5);               /* SDA high */
++    LPT_WRITE_CTRL (dev, 0);                                  /* SCLK high */
++    LPT_READ_STAT  (dev, dsr); DELAY(4);                      /* Sample SDA */
++
++    PRINTF (DBG_ECPP, ("jtagdrv_i2c_get_ack -> %s\n", (dsr & LPT_STAT_SDA) ? "no ack" : "ack"));
++    
++    return ((dsr & LPT_STAT_SDA) ? 0 : 1);
++}
++
++static int
++jtagdrv_i2c_drive_ack (JTAG_DEV *dev, int nack)
++{
++    u_char dsr;
++
++    LPT_WRITE_CTRL (dev, LPT_CTRL_SCLK); DELAY(1);            /* SCLK low */
++    LPT_WRITE_DATA (dev, nack ? LPT_DATA_SDA : 0); DELAY(5);  /* SDA low for ack, high for nack */
++    LPT_WRITE_CTRL (dev, 0);                                  /* SCLK high */
++    LPT_READ_STAT  (dev, dsr); DELAY(4);                      /* Sample SDA for ack */
++
++    PRINTF (DBG_ECPP, ("jtagdrv_i2c_drive_ack %d -> %s\n", nack, (dsr & LPT_STAT_SDA) ? "done" : "more"));
++    
++    return ((dsr & LPT_STAT_SDA) ? 1 : 0);
++}
++
++static void
++jtagdrv_i2c_shift_addr (JTAG_DEV *dev, u_int address, int readNotWrite)
++{
++    register int i;
++
++    PRINTF (DBG_ECPP, ("jtagdrv_i2c_shift_addr: %x\n", address));
++
++    for (i = I2C_ADDR_LEN-1; i >= 0; i--)
++      jtagdrv_i2c_strobe (dev, (address & (1 << i)) ? LPT_DATA_SDA : 0);
++    
++    jtagdrv_i2c_strobe (dev, readNotWrite ? LPT_DATA_SDA : 0);
++}
++
++static u_char
++jtagdrv_i2c_shift_data (JTAG_DEV *dev, u_char data)
++{
++    register int i;
++    u_char val = 0;
++    
++    PRINTF (DBG_ECPP, ("jtagdrv_i2c_shift_data : %02x\n", data));
++
++    for (i = I2C_DATA_LEN-1; i >= 0; i--)
++      if (jtagdrv_i2c_strobe (dev, data & (1 << i) ? LPT_DATA_SDA : 0))
++          val |= (1 << i);
++
++    PRINTF (DBG_ECPP, ("jtagdrv_i2c_shift_data : -> %02x\n", val));
++
++    return (val);
++}
++
++int
++jtagdrv_i2c_write (JTAG_DEV *dev, u_int address, u_int count, u_char *data)
++{
++    register int i;
++
++    PRINTF (DBG_FN, ("jtagdrv_i2c_write: address=%x count=%d data=%02x\n", address, count, data[0]));
++
++    if (! jtagdrv_i2c_start (dev))
++      return (I2C_OP_NOT_IDLE);
++
++    jtagdrv_i2c_shift_addr (dev, address, 0);
++    
++    if (! jtagdrv_i2c_get_ack (dev))
++    {
++      PRINTF (DBG_FN, ("jtagdrv_i2c_write: no ack on address phase\n"));
++
++      jtagdrv_i2c_stop (dev);
++      return (I2C_OP_NO_DEVICE);
++    }
++    
++    for (i = 0; i < count; i++)
++    {
++      jtagdrv_i2c_shift_data (dev, data[i]);
++      
++      if (! jtagdrv_i2c_get_ack (dev))
++      {
++          PRINTF (DBG_FN, ("jtagdrv_i2c_write: no ack on data phase %d\n", i));
++
++          jtagdrv_i2c_stop (dev);
++          return (I2C_OP_WRITE_TO_BIG);
++      }
++    }
++
++    jtagdrv_i2c_stop (dev);
++    return (I2C_OP_SUCCESS);
++}
++
++int
++jtagdrv_i2c_read (JTAG_DEV *dev, u_int address, u_int count, u_char *data)
++{
++    register int i;
++
++    PRINTF (DBG_FN, ("jtagdrv_i2c_read: address=%x count=%d\n", address, count));
++
++    if (! jtagdrv_i2c_start (dev))
++      return (I2C_OP_NOT_IDLE);
++
++    jtagdrv_i2c_shift_addr (dev, address, 1);
++    
++    if (! jtagdrv_i2c_get_ack (dev))
++    {
++      PRINTF (DBG_FN, ("jtagdrv_i2c_read: no ack on address phase\n"));
++
++      jtagdrv_i2c_stop (dev);
++      return (I2C_OP_NO_DEVICE);
++    }
++    
++    for (i = 0; i < count; i++)
++    {
++      data[i] = jtagdrv_i2c_shift_data (dev, 0xff);
++
++      jtagdrv_i2c_drive_ack (dev, (i == (count-1) ? 1 : 0));
++    }
++
++    jtagdrv_i2c_stop (dev);
++    
++    return (I2C_OP_SUCCESS);
++}
++
++int
++jtagdrv_i2c_writereg (JTAG_DEV *dev, u_int address, u_int intaddress, u_int count, u_char *data)
++{
++    register int i;
++
++    PRINTF (DBG_FN, ("jtagdrv_i2c_writereg: address=%x count=%d\n", address, count));
++
++    if (! jtagdrv_i2c_start (dev))
++      return (I2C_OP_NOT_IDLE);
++
++    jtagdrv_i2c_shift_addr (dev, address, 0);
++    
++    if (! jtagdrv_i2c_get_ack (dev))
++    {
++      PRINTF (DBG_FN, ("jtagdrv_i2c_writereg: no ack on address phase\n"));
++
++      jtagdrv_i2c_stop (dev);
++      return (I2C_OP_NO_DEVICE);
++    }
++    
++    jtagdrv_i2c_shift_data (dev, intaddress);
++    
++    if (! jtagdrv_i2c_get_ack (dev))
++    {
++      PRINTF (DBG_FN, ("jtagdrv_i2c_writereg: no ack on intaddress phase\n"));
++      jtagdrv_i2c_stop (dev);
++      return (I2C_OP_NO_DEVICE);
++    }
++    
++    for (i = 0; i < count; i++)
++    {
++      jtagdrv_i2c_shift_data (dev, data[i]);
++      if (! jtagdrv_i2c_get_ack (dev))
++      {
++          PRINTF (DBG_FN, ("jtagdrv_i2c_writedate: no ack on byte %d\n", i));
++          jtagdrv_i2c_stop (dev);
++          return (I2C_OP_WRITE_TO_BIG);
++      }
++    }
++    
++    jtagdrv_i2c_stop (dev);
++    return (I2C_OP_SUCCESS);
++}
++
++int
++jtagdrv_i2c_readreg (JTAG_DEV *dev, u_int address, u_int intaddress, u_int count, u_char *data)
++{
++    PRINTF (DBG_FN, ("jtagdrv_i2c_readreg: address=%x count=%d\n", address, count));
++
++    if (! jtagdrv_i2c_start (dev))
++      return (I2C_OP_NOT_IDLE);
++
++    jtagdrv_i2c_shift_addr (dev, address, 0);
++    
++    if (! jtagdrv_i2c_get_ack (dev))
++    {
++      PRINTF (DBG_FN, ("jtagdrv_i2c_readreg: no ack on address phase\n"));
++
++      jtagdrv_i2c_stop (dev);
++      return (I2C_OP_NO_DEVICE);
++    }
++    
++    jtagdrv_i2c_shift_data (dev, intaddress);
++    
++    if (! jtagdrv_i2c_get_ack (dev))
++    {
++      PRINTF (DBG_FN, ("jtagdrv_i2c_readreg: no ack on intaddress phase\n"));
++      jtagdrv_i2c_stop (dev);
++      return (I2C_OP_NO_DEVICE);
++    }
++
++    jtagdrv_i2c_stop (dev);
++
++    return (jtagdrv_i2c_read (dev, address, count, data));
++}
++
++void
++jtagdrv_i2c_clock_shift (JTAG_DEV *dev, u_int t, u_int n, u_int m)
++{
++    int i;
++
++    for (i = 2; i >= 0; i--)
++    {
++      LPT_WRITE_DATA (dev, ((t & (1 << i)) ? LPT_DATA_TDI : 0)); DELAY(1);                    /* clock low  | data */
++      LPT_WRITE_DATA (dev, ((t & (1 << i)) ? LPT_DATA_TDI : 0) | LPT_DATA_TMS); DELAY(1);     /* clock high | data */
++    }
++
++    for (i = 1; i >= 0; i--)
++    {
++      LPT_WRITE_DATA (dev, ((n & (1 << i)) ? LPT_DATA_TDI : 0)); DELAY(1);                    /* clock low  | data */
++      LPT_WRITE_DATA (dev, ((n & (1 << i)) ? LPT_DATA_TDI : 0)| LPT_DATA_TMS); DELAY(1);      /* clock high | data */
++    }    
++
++    for (i = 6; i >= 0; i--)
++    {
++      LPT_WRITE_DATA (dev, ((m & (1 << i)) ? LPT_DATA_TDI : 0)); DELAY(1);                    /* clock low  | data */
++      LPT_WRITE_DATA (dev, ((m & (1 << i)) ? LPT_DATA_TDI : 0) | LPT_DATA_TMS); DELAY(1);     /* clock high | data */
++    }    
++
++    LPT_WRITE_DATA (dev, 0); DELAY(1);                                                                /* clock low  | 0 */
++
++    LPT_WRITE_CTRL (dev, LPT_CTRL_TCLK); DELAY(1);                                            /* strobe low */
++    LPT_WRITE_CTRL (dev, 0); DELAY(1);                                                                /* strobe low */
++}
++
+Index: linux-2.6.5/drivers/net/qsnet/jtag/jtagdrv.h
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/jtag/jtagdrv.h  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/jtag/jtagdrv.h       2005-05-11 12:10:12.549915704 -0400
+@@ -0,0 +1,57 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __JTAGDRV_COMMON_H
++#define __JTAGDRV_COMMON_H
++
++#ident "@(#)$Id: jtagdrv.h,v 1.5 2002/08/09 11:18:37 addy Exp $"
++/*      $Source: /cvs/master/quadrics/jtagmod/jtagdrv.h,v $*/
++
++#include <qsnet/config.h>
++
++/* include OS specific header file */
++#if defined(LINUX)
++#  include "jtagdrv_Linux.h"
++#elif defined(DIGITAL_UNIX)
++#  include "jtagdrv_OSF1.h"
++#elif defined(QNX)
++#  include "jtagdrv_QNX.h"
++#else
++#  error cannot determint os type
++#endif
++
++extern int jtagdebug;
++
++#define DBG_CFG               (1 << 0)
++#define DBG_OPEN      (1 << 1)
++#define DBG_IOCTL     (1 << 2)
++#define DBG_ECPP      (1 << 3)
++#define DBG_FN                (1 << 4)
++
++#define DRIVER_NAME    "jtag"
++
++#if defined(LINUX)
++#define PRINTF(n,X)   ((n) & jtagdebug ? (void) printk X : (void) 0)
++#define PRINTMSG(fmt, arg...) printk(KERN_INFO DRIVER_NAME ": " fmt, ##arg)
++#else
++#define PRINTF(n,X)   ((n) & jtagdebug ? (void) printf X : (void) 0)
++#define PRINTMSG(M, A)        printf ("jtag: " M, A)
++#endif
++
++extern void jtagdrv_select_ring (JTAG_DEV *pp, u_int ring);
++extern void jtagdrv_reset (JTAG_DEV *pp);
++extern void jtagdrv_shift_ir (JTAG_DEV *pp, u_char *value, int nbits);
++extern void jtagdrv_shift_dr (JTAG_DEV *pp, u_char *value, int nbits);
++
++extern int  jtagdrv_i2c_write (JTAG_DEV *pp, u_int address, u_int count, u_char *data);
++extern int  jtagdrv_i2c_read (JTAG_DEV *pp, u_int address, u_int count, u_char *data);
++extern int  jtagdrv_i2c_writereg (JTAG_DEV *pp, u_int address, u_int intaddress, u_int count, u_char *data);
++extern int  jtagdrv_i2c_readreg (JTAG_DEV *pp, u_int address, u_int intaddress, u_int count, u_char *data);
++extern void jtagdrv_i2c_clock_shift (JTAG_DEV *pp, u_int t, u_int n, u_int m);
++
++
++#endif /* __JTAGDRV_COMMON_H */
+Index: linux-2.6.5/drivers/net/qsnet/jtag/jtagdrv_Linux.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/jtag/jtagdrv_Linux.c    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/jtag/jtagdrv_Linux.c 2005-05-11 12:10:12.550915552 -0400
+@@ -0,0 +1,325 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++/*
++ * $Id: jtagdrv_Linux.c,v 1.18.2.1 2005/02/01 10:12:01 lee Exp $
++ * $Source: /cvs/master/quadrics/jtagmod/jtagdrv_Linux.c,v $
++ */
++
++#include "jtagdrv.h"
++#include <jtag/jtagio.h>
++
++#include <linux/module.h>
++#include <linux/ioport.h>
++
++MODULE_AUTHOR("Quadrics Ltd.");
++MODULE_DESCRIPTION("JTAG Parallel port QsNet switch interface");
++
++MODULE_LICENSE("GPL");
++
++#define MAJOR_INSTANCE        0       /* 0 is dynamic assign of device major  */ 
++#define MAX_JTAG_DEV  4
++
++int jtag_major = MAJOR_INSTANCE;
++int jtagdebug  = 0;
++MODULE_PARM(jtag_major, "i");
++MODULE_PARM(jtagdebug, "i");
++
++JTAG_DEV      jtag_devs[MAX_JTAG_DEV];
++
++int io[MAX_JTAG_DEV]= { 0, };
++MODULE_PARM(io, "1-4i");
++
++
++/* The fops functions */
++int jtag_open(struct inode *, struct file *);
++int jtag_close(struct inode *, struct file *);
++int jtag_ioctl(struct inode *, struct file *, unsigned int, unsigned long );
++
++struct file_operations jtag_fops = {
++    ioctl:   jtag_ioctl,
++    open:    jtag_open,
++    release: jtag_close,
++};
++
++int
++jtag_probe(void)
++{
++      int i=0;        
++      int default_io = 1;
++      JTAG_DEV *dev;
++      unsigned char value=0xff;
++
++
++      /* see if there are any user supplied io addr */
++      for ( i = 0; i < MAX_JTAG_DEV; i++) {
++              if ( io[i] != 0x00)
++                      default_io = 0;
++              jtag_devs[i].base = io[i];
++      }
++      
++      if ( default_io ) {
++              jtag_devs[0].base = 0x3bc;
++              jtag_devs[1].base = 0x378;
++              jtag_devs[2].base = 0x278;
++              jtag_devs[3].base = 0x268;
++      }
++
++      for ( i = 0 ; i < MAX_JTAG_DEV; i++) {
++              if ( jtag_devs[i].base == 0x3bc ) 
++                      jtag_devs[i].region = 3;
++              else
++                      jtag_devs[i].region = 8;
++              jtag_devs[i].present = 0;
++      }       
++
++
++      if( default_io )
++      {
++              for( i = 0 ; i < MAX_JTAG_DEV; i++) {
++                      dev=&(jtag_devs[i]);
++                      if(dev->base && request_region(dev->base, dev->region, "jtag")) {
++                              LPT_WRITE(dev, 0,0);
++                              LPT_READ(dev, 0,value);
++                              if ( value != 0xff) {
++                                      PRINTMSG("(%d , %d) present, io=0x%04lx\n",jtag_major,i,dev->base);
++                      
++                                      dev->present=1; 
++                              }
++                              else
++                                  release_region(dev->base, dev->region);
++                      }
++                      else
++                      {
++                          PRINTMSG("failed to request_region (%d , %d), io=0x%04lx\n",jtag_major,i,dev->base);
++                          return -1;
++                      }
++              }
++              return 0;
++       }     
++       else /* Force the region to be present, this makes the PCI parallel cards work */
++       {
++              for( i = 0 ; i < MAX_JTAG_DEV; i++) 
++              {
++                        dev=&(jtag_devs[i]);
++                        if(dev->base && request_region(dev->base, dev->region, "jtag") && (dev->base != 0)) 
++                      {
++                                PRINTMSG("(%d , %d) forced by user, io=0x%04lx\n",jtag_major,i,dev->base);
++                                        dev->present=1;
++                      }       
++                        else  
++                      {
++                                if( dev->base != 0)
++                                      release_region(dev->base, dev->region);
++                      }
++              }
++                return 0;
++       }
++}
++
++int init_module(void)
++{
++      int result,i;
++      result = register_chrdev(jtag_major, DRIVER_NAME, &jtag_fops);
++      if (result < 0) {
++              PRINTMSG("Couldn't register char device err == %d\n",jtag_major);
++              return -1;
++      }
++
++      if ( jtag_major == 0 ) 
++              jtag_major = result;
++
++      for ( i = 0; i < MAX_JTAG_DEV; i++) {
++              jtag_devs[i].base=io[i];        
++      }
++
++      jtag_probe();
++
++      PRINTMSG("Registered character device, major == %d\n",jtag_major);      
++      return 0;
++}     
++
++void cleanup_module(void)
++{
++      int i=0;
++
++      for( i = 0; i < MAX_JTAG_DEV; i++) {
++              if( jtag_devs[i].present)       
++                      release_region(jtag_devs[i].base, jtag_devs[i].region);
++      }
++                      
++      unregister_chrdev(jtag_major, DRIVER_NAME);
++      PRINTMSG("Unloaded char device\n");
++}
++
++
++int
++jtag_open (struct inode *inode, struct file *filp)
++{
++    int unit = MINOR(inode->i_rdev);
++    JTAG_DEV *dev = &jtag_devs[unit];
++    
++    if (unit < 0 || unit > MAX_JTAG_DEV || !dev->present)
++      return (-ENXIO);
++
++    /*
++     * Only allow a single open at a time 
++     */
++    if (dev->open)
++      return (-EBUSY);
++    dev->open = 1;
++    
++    /*
++     * Initialise the hardware registers
++     */
++   
++    LPT_WRITE (dev, LPT_CTRL, 0);
++    DELAY(50);
++    LPT_WRITE (dev, LPT_CTRL, LPT_CTRL_INIT);
++
++    MOD_INC_USE_COUNT;
++
++    return (0);
++}
++
++int
++jtag_close(struct inode *inode, struct file *filp)
++{
++  
++    int unit = MINOR(inode->i_rdev);
++    JTAG_DEV *dev = &jtag_devs[unit];
++    
++    if (unit < 0 || unit > MAX_JTAG_DEV || !dev->present)
++      return (-ENXIO);
++    
++    dev->open = 0;
++
++    MOD_DEC_USE_COUNT;
++
++    return (0);
++}
++
++int
++jtag_ioctl (struct inode *inode, struct file *filp, unsigned int io_cmd, unsigned long io_data)
++{
++    int                  unit = MINOR(inode->i_rdev);
++    JTAG_DEV             *dev = &jtag_devs[unit];
++    JTAG_RESET_ARGS      *resetargs;
++    JTAG_SHIFT_ARGS      *shiftargs;
++    I2C_ARGS           *i2cargs;
++    I2C_CLOCK_SHIFT_ARGS *clockargs;
++    u_char             *buf;
++    int                         freq;
++
++    if (unit < 0 || unit > MAX_JTAG_DEV || !dev->present)
++      return (-ENXIO);
++    
++    PRINTF (DBG_IOCTL, ("jtag_ioctl: device %d cmd=%x\n", unit, io_cmd));
++
++    switch (io_cmd)
++    {
++    case JTAG_RESET:
++      resetargs = (JTAG_RESET_ARGS *) io_data;
++
++      if (! VALID_JTAG_RING (resetargs->ring))
++          return (-EINVAL);
++      
++      jtagdrv_select_ring (dev, resetargs->ring);
++      jtagdrv_reset (dev);
++      return (0);
++      
++    case JTAG_SHIFT_IR:
++    case JTAG_SHIFT_DR:
++      shiftargs = (JTAG_SHIFT_ARGS *) io_data;
++      
++      if (! VALID_JTAG_RING (shiftargs->ring) || shiftargs->nbits > (JTAG_MAX_DATA_LEN*JTAG_MAX_CHIPS)) {
++          return (-EFAULT);
++              }
++
++      buf = (u_char *) kmalloc (JTAG_NBYTES(shiftargs->nbits), GFP_KERNEL);
++
++      if (buf == (u_char *) NULL)
++          return (-ENOMEM);
++      
++      if (copy_from_user (buf, shiftargs->value, JTAG_NBYTES(shiftargs->nbits)))
++      {
++          kfree(buf);
++          return (-EFAULT);
++      }
++
++
++      jtagdrv_select_ring (dev, shiftargs->ring);
++
++      if (io_cmd == JTAG_SHIFT_IR)
++          jtagdrv_shift_ir (dev, buf, shiftargs->nbits);
++      else
++          jtagdrv_shift_dr (dev, buf, shiftargs->nbits);
++      
++      if (copy_to_user (shiftargs->value, buf, JTAG_NBYTES (shiftargs->nbits)))
++      {
++          kfree (buf);
++          return (-EFAULT);
++      }
++
++      kfree (buf);
++      return (0);
++
++    case I2C_WRITE:
++    case I2C_READ:
++    case I2C_WRITEREG:
++    case I2C_READREG:
++      i2cargs = (I2C_ARGS *) io_data;
++
++      if (! VALID_I2C_RING(i2cargs->ring) || i2cargs->count > I2C_MAX_DATA_LEN)
++          return (-EFAULT);
++
++      jtagdrv_select_ring (dev, RING_I2C_BIT | i2cargs->ring);
++      switch (io_cmd)
++      {
++      case I2C_WRITE:
++          i2cargs->ok = jtagdrv_i2c_write (dev, i2cargs->device, i2cargs->count, i2cargs->data);
++          break;
++
++      case I2C_READ:
++          i2cargs->ok = jtagdrv_i2c_read (dev, i2cargs->device, i2cargs->count, i2cargs->data);
++          break;
++
++      case I2C_WRITEREG:
++          i2cargs->ok = jtagdrv_i2c_writereg (dev, i2cargs->device, i2cargs->reg, i2cargs->count, i2cargs->data);
++          break;
++
++      case I2C_READREG:
++          i2cargs->ok = jtagdrv_i2c_readreg (dev, i2cargs->device, i2cargs->reg, i2cargs->count, i2cargs->data);
++          break;
++      }
++      return (0);
++
++    case I2C_CLOCK_SHIFT:
++      clockargs = (I2C_CLOCK_SHIFT_ARGS *) io_data;
++
++      freq = (10 * clockargs->m / (1 << (((clockargs->n + 1) & 3))));
++      
++      /* validate the value, and initialise the ring */
++      if (clockargs->t != 0 || clockargs->n > 3 || clockargs->m > 127)
++          return (-EINVAL);
++      
++      jtagdrv_select_ring (dev, RING_I2C_BIT | RING_CLOCK_SHIFT);
++      jtagdrv_i2c_clock_shift (dev, clockargs->t, clockargs->n, clockargs->m);
++      jtagdrv_select_ring (dev, 0);
++      return (0);
++
++    default:
++      return (-EINVAL);
++    }
++    return (-EINVAL);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/jtag/jtagdrv_Linux.h
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/jtag/jtagdrv_Linux.h    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/jtag/jtagdrv_Linux.h 2005-05-11 12:10:12.550915552 -0400
+@@ -0,0 +1,174 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: jtagdrv_Linux.h,v 1.3 2002/08/09 11:18:37 addy Exp $"
++/*      $Source: /cvs/master/quadrics/jtagmod/jtagdrv_Linux.h,v $*/
++
++#ifndef __JTAGDRV_LINUX_H
++#define __JTAGDRV_LINUX_H
++
++#include <qsnet/kernel.h>
++#include <asm/io.h>
++
++typedef struct jtag_dev
++{
++    unsigned long     base;
++    int                 region;
++
++    u_int             present:1;
++    u_int             open:1;
++} JTAG_DEV;
++
++/*
++**
++**                    Hardware Defines
++**
++*/
++
++/*
++ * Assume that bit 4 of the Control Register is set to 1 (by default) 
++ * to enable the printer port (CS3).
++ *
++ * The default base address is 3BC-3BF. 
++ */
++
++#define LPT0  0x3BC                   /* CSR Base Address - note this can
++                                       * change depending on the setting
++                                       * in the Control Register 0.
++                                       *
++                                       * LPT1 0x378
++                                       * LPT2 0x278
++                                       * LPT3 0x268
++                                      */
++
++/*
++ *    Register offsets from the port base address
++ */
++
++#define LPT_REGISTER_0        0
++#define LPT_REGISTER_1        1
++#define LPT_REGISTER_2        2
++#define LPT_REGISTER_3        0x400
++#define LPT_REGISTER_4        0x401
++#define LPT_REGISTER_5        0x402
++
++/*
++ *    Chip control registers
++ */
++                                      /* Base address for Super I/O National*/
++
++#define SIO_BASE_ADDR 0x26e           /* Semiconductor PC87332VLJ combo-chip*/
++#define CR4_REG               0x04            /* index 4, printer control reg 4 */
++
++#define LPT_EPP               0x01            /* Enable bit for epp */
++#define LPT_ECP               0x04            /* Enable bit for ecp */
++
++/*
++ *    Registers for use with centronics, nibble and byte modes.
++ */
++
++#define LPT_DATA      LPT_REGISTER_0          /* line printer port data */
++#define LPT_STAT      LPT_REGISTER_1          /* LPT port status        */
++#define LPT_CTRL      LPT_REGISTER_2          /* LPT port control       */
++
++/*
++ *    Registers for use with ECP mode.
++ */ 
++
++#define LPT_DFIFO     LPT_REGISTER_3          /* r/w fifo register    */
++#define LPT_CFGB      LPT_REGISTER_4          /* Configuration B      */
++#define LPT_ECR               LPT_REGISTER_5          /* Exteded control      */
++
++/*
++ * Bit assignments for ECR register.
++ */
++
++      /* Bits 0-4 */
++
++#define LPT_ECR_EMPTY 0x01            /* FIFO is empty */
++#define LPT_ECR_FULL  0x02            /* FIFO is full */
++#define LPT_ECR_SERV  0x04            /* Service bit */
++#define LPT_ECR_DMA   0x08            /* DMA enable */
++#define LPT_ECR_nINTR 0x10            /* Interrupt disable */
++
++      /*
++       * Bits 5-7 are ECR modes.
++       */
++
++#define LPT_ECR_PAR   0x20            /* Parallel port FIFO mode */
++#define LPT_ECR_ECP   0x60            /* ECP mode */
++#define LPT_ECR_CFG   0xE0            /* Configuration mode */
++#define LPT_ECR_CLEAR ~0xE0           /* Cear mode bits */
++
++/*
++ * Bit assignments for the parallel port STATUS register:
++ */
++
++#define LPT_STAT_BIT0 0X1     /* Reserved. Bit always set.            */
++#define LPT_STAT_BIT1 0X2     /* Reserved. Bit always set.            */
++#define LPT_STAT_IRQ  0x4     /* interrupt status bit                 */
++#define LPT_STAT_ERROR        0x8     /* set to 0 to indicate error           */
++#define LPT_STAT_SLCT 0x10    /* status of SLCT lead from printer     */
++#define LPT_STAT_PE   0x20    /* set to 1 when out of paper           */
++#define LPT_STAT_ACK  0x40    /* acknowledge - set to 0 when ready    */
++#define LPT_STAT_nBUSY        0x80    /* busy status bit, 0=busy, 1=ready     */
++
++/*
++ * Bit assignments for the parallel port CONTROL register:
++ */
++
++#define LPT_CTRL_nSTROBE      0x1     /* Printer Strobe Control       */
++#define LPT_CTRL_nAUTOFD      0x2     /* Auto Feed Control            */
++#define LPT_CTRL_INIT         0x4     /* Initialize Printer Control   */
++#define LPT_CTRL_nSLCTIN      0x8     /* 0=select printer, 1=not selected */
++#define LPT_CTRL_IRQ          0x10    /* Interrupt Request Enable Control */
++#define LPT_CTRL_DIR          0x20    /* Direction control            */
++#define LPT_CTRL_BIT6         0X40    /* Reserved. Bit always set.    */
++#define LPT_CTRL_BIT7         0X80    /* Reserved. Bit always set.    */
++
++
++#define LPT_WRITE(dev, regname, value)        do { outb(value, (dev)->base + regname); } while (0)
++#define LPT_READ(dev, regname,value)  do { value = inb((dev)->base + regname); } while (0)
++
++
++
++/* Standard register access macros */
++#define LPT_WRITE_CTRL(dev, value)    LPT_WRITE(dev, LPT_CTRL, LPT_CTRL_INIT | value)
++#define LPT_WRITE_DATA(dev, value)    LPT_WRITE(dev, LPT_DATA, value)
++#define LPT_READ_STAT(dev, value)     LPT_READ(dev, LPT_STAT, value)
++
++/*
++ * The jtag signals are connected to the parallel port as follows :
++ *
++ *  TRST      bit 0
++ *  TDI               bit 1
++ *  TMS               bit 2
++ *  TCLK      AFX
++ *  TDO               PE
++ */
++#define LPT_DATA_TRST 1
++#define LPT_DATA_TDI  2
++#define LPT_DATA_TMS  4
++#define LPT_CTRL_TCLK LPT_CTRL_nAUTOFD
++#define LPT_STAT_TDO  LPT_STAT_PE
++
++/*
++ * The I2C signals are connected as follows :
++ */
++#define LPT_DATA_SDA  2
++#define LPT_CTRL_SCLK LPT_CTRL_nAUTOFD
++#define LPT_STAT_SDA  LPT_STAT_PE
++
++/*
++ * The ring selection signals are as follows :
++ *  addr      bit 0-7
++ *  clock     nSLCTIN
++ */
++#define LPT_CTRL_RCLK LPT_CTRL_nSLCTIN
++
++
++#endif /* __JTAGDRV_LINUX_H */
+Index: linux-2.6.5/drivers/net/qsnet/jtag/Makefile
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/jtag/Makefile   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/jtag/Makefile        2005-05-11 12:10:12.551915400 -0400
+@@ -0,0 +1,15 @@
++#
++# Makefile for Quadrics QsNet
++#
++# Copyright (c) 2002-2004 Quadrics Ltd
++#
++# File: drivers/net/qsnet/jtag/Makefile
++#
++
++
++#
++
++obj-$(CONFIG_JTAG)    += jtag.o
++jtag-objs     := jtagdrv_Linux.o jtagdrv.o
++
++EXTRA_CFLAGS          +=  -DDEBUG -DDEBUG_PRINTF -DDEBUG_ASSERT
+Index: linux-2.6.5/drivers/net/qsnet/jtag/Makefile.conf
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/jtag/Makefile.conf      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/jtag/Makefile.conf   2005-05-11 12:10:12.551915400 -0400
+@@ -0,0 +1,10 @@
++# Flags for generating QsNet Linux Kernel Makefiles
++MODNAME               =       jtag.o
++MODULENAME    =       jtag
++KOBJFILES     =       jtagdrv_Linux.o jtagdrv.o
++EXPORT_KOBJS  =       
++CONFIG_NAME   =       CONFIG_JTAG
++SGALFC                =       
++# EXTRALINES START
++
++# EXTRALINES END
+Index: linux-2.6.5/drivers/net/qsnet/jtag/quadrics_version.h
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/jtag/quadrics_version.h 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/jtag/quadrics_version.h      2005-05-11 12:10:12.551915400 -0400
+@@ -0,0 +1 @@
++#define QUADRICS_VERSION "4.31qsnet"
+Index: linux-2.6.5/drivers/net/qsnet/Kconfig
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/Kconfig 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/Kconfig      2005-05-11 12:10:12.552915248 -0400
+@@ -0,0 +1,79 @@
++#
++# Kconfig for Quadrics QsNet
++#
++# Copyright (c) 2004 Quadrics Ltd
++#
++# File: driver/net/qsnet/Kconfig
++#
++
++menu "Quadrics QsNet"
++        depends on NETDEVICES
++
++config QSNET
++        tristate "Quadrics QsNet support"
++      default m
++        depends on PCI
++        ---help---
++          Quadrics QsNet is a high bandwidth, ultra low latency cluster interconnect
++          which provides both user and kernel programmers with secure, direct access
++          to the Quadrics network.
++
++config ELAN3
++        tristate "Elan 3 device driver"
++      default m
++        depends on QSNET
++        ---help---
++          This is the main device driver for the Quadrics QsNet (Elan3) PCI device.
++          This is a high bandwidth, ultra low latency interconnect which provides
++          both user and kernel programmers with secure, direct access to the
++          Quadrics network.
++
++config ELAN4
++        tristate "Elan 4 device driver"
++      default m
++        depends on QSNET
++        ---help---
++          This is the main device driver for the Quadrics QsNetII (Elan4) PCI-X device.
++          This is a high bandwidth, ultra low latency interconnect which provides
++          both user and kernel programmers with secure, direct access to the
++          Quadrics network.
++
++config EP
++        tristate "Elan Kernel Comms"
++      default m
++        depends on QSNET && (ELAN4 || ELAN3)
++        ---help---
++          This module implements the QsNet kernel communications layer. This
++          is used to layer kernel level facilities on top of the basic Elan
++          device drivers. These can be used to implement subsystems such as
++          TCP/IP and remote filing systems over the QsNet interconnect.
++
++config EIP
++        tristate "Elan IP device driver"
++      default m
++        depends on QSNET && EP && NET
++        ---help---
++        This is a network IP device driver for the Quadrics QsNet device.
++        It allows the TCP/IP protocol to be run over the Quadrics interconnect.
++
++config RMS
++        tristate "Resource Management System support"
++      default m
++        depends on QSNET
++        ---help---
++        This is a support module for the Quadrics RMS resource manager. It provides kernel
++        services for monitoring and controlling user job execution, termination and cleanup.
++
++config JTAG
++        tristate "Switch monitoring"
++      default m
++        depends on QSNET
++        ---help---
++          The jtag interface is used to allow processes to send and retrieve jtag
++          information to a Quadrics QsNet Elite switch via the parallel port.
++          The module requires a /dev/jtag[0-3] entry (usually there is only a /dev/jtag0)
++          device and a particular device only allows one process at a time to access this
++          resource.
++          For more information about JTag interface, please refer to the IEEE document on
++          http://www.ieee.org/
++endmenu
+Index: linux-2.6.5/drivers/net/qsnet/Makefile
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/Makefile        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/Makefile     2005-05-11 12:10:12.552915248 -0400
+@@ -0,0 +1,15 @@
++#
++# Makefile for Quadrics QsNet
++#
++# Copyright (c) 2002-2004 Quadrics Ltd.
++#
++# File: driver/net/qsnet/Makefile
++#
++
++obj-$(CONFIG_QSNET)     += qsnet/ elan/
++obj-$(CONFIG_ELAN3)     += elan3/
++obj-$(CONFIG_ELAN4)     += elan4/
++obj-$(CONFIG_EP)      += ep/ 
++obj-$(CONFIG_EIP)       += eip/ 
++obj-$(CONFIG_RMS)       += rms/ 
++obj-$(CONFIG_JTAG)      += jtag/
+Index: linux-2.6.5/drivers/net/qsnet/qsnet/debug.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/qsnet/debug.c   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/qsnet/debug.c        2005-05-11 12:10:12.553915096 -0400
+@@ -0,0 +1,583 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: debug.c,v 1.21 2004/08/19 08:09:57 david Exp $"
++/*      $Source: /cvs/master/quadrics/qsnet/debug.c,v $ */
++
++#include <qsnet/kernel.h>
++#include <qsnet/debug.h>
++#include <qsnet/procfs_linux.h>
++
++caddr_t        qsnet_debug_buffer_ptr = NULL;
++int          qsnet_debug_front      = 0;
++int          qsnet_debug_back       = 0;
++int            qsnet_debug_lost_lines = 0;
++int          qsnet_debug_disabled   = 0;
++
++int          qsnet_debug_line_size  = 256;
++int          qsnet_debug_num_lines  = 8192;
++
++int          qsnet_assfail_mode     = 1;                      /* default to BUG() */
++
++int            qsnet_debug_running    = 0;
++int            kqsnet_debug_running   = 0;
++
++static spinlock_t qsnet_debug_lock;
++static kcondvar_t qsnet_debug_wait;
++static char       qsnet_debug_buffer_space[8192];
++
++#define QSNET_DEBUG_PREFIX_MAX_SIZE   32
++#define QSNET_DEBUG_MAX_WORDWRAP      15
++
++/* must be larger than  QSNET_DEBUG_PREFIX_MAX_SIZE +  QSNET_DEBUG_MAX_WORDWRAP + 2 */
++#if defined(DIGITAL_UNIX) 
++#define QSNET_DEBUG_CONSOLE_WIDTH 80
++#elif defined(LINUX)
++#define QSNET_DEBUG_CONSOLE_WIDTH 128
++#endif
++
++#define isspace(CH)    ((CH==' ') | (CH=='\t') | (CH=='\n'))
++
++#ifdef LINUX
++#define ALLOC_DEBUG_BUFFER(ptr)               do { (ptr) = (void *)__get_free_pages (GFP_KERNEL, get_order (qsnet_debug_num_lines * qsnet_debug_line_size)); } while (0)
++#define FREE_DEBUG_BUFFER(ptr)                free_pages ((unsigned long) ptr, get_order (qsnet_debug_num_lines * qsnet_debug_line_size))
++#else
++#define ALLOC_DEBUG_BUFFER(ptr)               KMEM_ALLOC (ptr, caddr_t, qsnet_debug_num_lines * qsnet_debug_line_size, 1)
++#define FREE_DEBUG_BUFFER(ptr)                KMEM_FREE (ptr, qsnet_debug_num_lines * qsnet_debug_line_size)
++#endif
++
++void
++qsnet_debug_init ()
++{
++      spin_lock_init (&qsnet_debug_lock);
++      kcondvar_init  (&qsnet_debug_wait);
++
++      qsnet_debug_front      = 0;
++      qsnet_debug_back       = 0;
++      qsnet_debug_lost_lines = 0;
++
++      if (qsnet_debug_line_size < (QSNET_DEBUG_PREFIX_MAX_SIZE + QSNET_DEBUG_MAX_WORDWRAP + 2))
++              qsnet_debug_line_size = 256;
++
++      qsnet_debug_running    = 1;
++
++      qsnet_proc_register_int (qsnet_procfs_config, "assfail_mode", &qsnet_assfail_mode, 0);
++}
++
++void
++qsnet_debug_fini()
++{
++      if (!qsnet_debug_running) return;
++
++      remove_proc_entry ("assfail_mode", qsnet_procfs_config);
++
++      spin_lock_destroy (&qsnet_debug_lock);
++      kcondvar_destroy  (&qsnet_debug_wait);
++      
++      if (qsnet_debug_buffer_ptr)
++              FREE_DEBUG_BUFFER (qsnet_debug_buffer_ptr);
++
++      qsnet_debug_buffer_ptr     = NULL;
++      qsnet_debug_lost_lines = 0;     
++      qsnet_debug_running    = 0;     
++}
++
++void
++qsnet_debug_disable(int val)
++{
++      qsnet_debug_disabled = val;
++}
++
++void
++qsnet_debug_alloc()
++{
++      caddr_t ptr;
++      unsigned long flags;
++
++      if (!qsnet_debug_running) return;
++
++      if (qsnet_debug_buffer_ptr == NULL)
++      {
++              ALLOC_DEBUG_BUFFER (ptr);
++
++              if (ptr != NULL)
++              {
++                      spin_lock_irqsave (&qsnet_debug_lock, flags);
++                      if (qsnet_debug_buffer_ptr == NULL)
++                      {
++                              qsnet_debug_buffer_ptr = ptr;
++                              spin_unlock_irqrestore (&qsnet_debug_lock, flags);
++                      }
++                      else
++                      {
++                              spin_unlock_irqrestore (&qsnet_debug_lock, flags);
++
++                              FREE_DEBUG_BUFFER (ptr);
++                      }
++              }
++      }
++      
++}
++
++static void 
++qsnet_prefix_debug(unsigned int mode, char *prefix, char *buffer) 
++{
++      /* assumes caller has lock */
++
++      int  prefixlen = strlen(prefix);
++      char pref[QSNET_DEBUG_PREFIX_MAX_SIZE];
++      int  prefix_done = 0;
++
++      if (!qsnet_debug_running) return;
++
++      if (qsnet_debug_disabled)
++              return;
++
++      if (prefixlen >= QSNET_DEBUG_PREFIX_MAX_SIZE) 
++      {
++              strncpy(pref,prefix,QSNET_DEBUG_PREFIX_MAX_SIZE -2);
++              strcpy (&pref[QSNET_DEBUG_PREFIX_MAX_SIZE-5],"... ");
++
++              prefix = pref;
++                prefixlen = strlen(prefix);
++      }
++
++#ifdef CONFIG_MPSAS
++      {
++              char *p;
++#define TRAP_PUTCHAR_B                        (0x17a - 256)
++#define SAS_PUTCHAR(c)                        do {\
++                      register int o0 asm ("o0") = (c);\
++\
++                      asm volatile ("ta %0; nop" \
++                                    : /* no outputs */\
++                                    : /* inputs */ "i" (TRAP_PUTCHAR_B), "r" (o0)\
++                                    : /* clobbered */ "o0");\
++\
++                      if (o0 == '\n') {\
++                              o0 = '\r';\
++\
++                              asm volatile ("ta %0; nop" \
++                                            : /* no outputs */\
++                                            : /* inputs */ "i" (TRAP_PUTCHAR_B), "r" (o0)\
++                                            : /* clobbered */ "o0");\
++                      }\
++              } while(0)
++
++              for (p = prefix; *p; p++)
++                      SAS_PUTCHAR (*p);
++
++              for (p = buffer; *p; p++)
++                      SAS_PUTCHAR (*p);
++      }
++#else
++      if (mode & QSNET_DEBUG_BUFFER)
++      {
++              if (qsnet_debug_buffer_ptr == NULL)
++                      qsnet_debug_lost_lines++;
++              else
++              {                   
++                      caddr_t base = &qsnet_debug_buffer_ptr[qsnet_debug_line_size * qsnet_debug_back];
++                      caddr_t lim  = base + qsnet_debug_line_size - 2;
++                      caddr_t p;
++              
++                      p = buffer; 
++                      prefix_done = 0;
++                      while (*p) 
++                      {
++                              /* sort out prefix */
++                              if ( prefix_done++ ) 
++                              {
++                                      int i;
++                                      for(i=0;i<prefixlen;i++)
++                                              base[i] = ' ';
++                                      /* memset(base,' ',prefixlen); */
++                              }
++                              else
++                                      strcpy(base,prefix);
++                              base += prefixlen; /* move the base on */
++
++                              /* copy data */
++                              for ( ; *p && (base < lim); )
++                                      *base++ = *p++;
++
++                              /* if line split then add \n */
++                              if ((base == lim) && (*base != '\n'))
++                              {
++                                      char *ptr;
++                                      int   count;
++
++                                      *base = '\n';
++                                      /* we added a \n cos it was end of line put next char was \n */
++                                      if (*p == '\n') 
++                                              p++;
++                                      else
++                                      {
++                                              /* lets see if we can back track and find a white space to break on */
++                                              ptr = base-1;
++                                              count = 1;
++                                              while ( ( !isspace(*ptr) ) && ( count < QSNET_DEBUG_MAX_WORDWRAP ))
++                                              {
++                                                      count++;
++                                                      ptr--;
++                                              }
++
++                                              if ( isspace(*ptr) ) 
++                                              {
++                                                      /* found somewhere to wrap to */
++                                                      p -= (count-1); /* need to loose the white space */
++                                                      base = ptr;
++                                                      *base = '\n';
++                                              }
++                                      }
++                                      base++;
++                              }
++                              *base = '\0';
++
++                              /* move on pointers */
++                              qsnet_debug_back = (++qsnet_debug_back == qsnet_debug_num_lines) ? 0 : qsnet_debug_back;            
++                              if (qsnet_debug_back == qsnet_debug_front)
++                              {
++                                      qsnet_debug_lost_lines++;
++                                      qsnet_debug_front = (++qsnet_debug_front == qsnet_debug_num_lines) ? 0 : qsnet_debug_front;
++                              }
++                              base  = &qsnet_debug_buffer_ptr[qsnet_debug_line_size * qsnet_debug_back];
++                              lim  =  base + qsnet_debug_line_size - 2;
++                      }
++                      kcondvar_wakeupone (&qsnet_debug_wait, &qsnet_debug_lock);
++              }
++      }
++
++      if (mode & QSNET_DEBUG_CONSOLE)
++      {
++              int     remaining = QSNET_DEBUG_CONSOLE_WIDTH - prefixlen;
++              caddr_t p;
++              char    line[QSNET_DEBUG_CONSOLE_WIDTH +2];
++              int     len;
++          
++              strcpy (pref,prefix);
++              prefix_done = 0;
++
++              p = buffer;
++              while ( *p )
++              {
++                      /* use the prefix only once */
++                      if  ( prefix_done++ > 0 ) 
++                              {
++                                      int i;
++                                      for(i=0;i<prefixlen;i++)
++                                              pref[i] = ' ';
++                                      /* memset(perf,' ',prefixlen); */
++                              }       
++
++                      len=strlen(p);
++                      if (len > remaining) len = remaining;
++                
++                      strncpy(line, p, len);
++                      line[len] = 0;
++                      p += len;
++                  
++                      /* word wrap */
++                      if ((len == remaining) && *p && !isspace(*p))
++                      {
++                              /* lets see if we can back track and find a white space to break on */
++                              char * ptr = &line[len-1];
++                              int    count = 1;
++
++                              while ( ( !isspace(*ptr) ) && ( count < QSNET_DEBUG_MAX_WORDWRAP ))
++                              {
++                                      count++;
++                                      ptr--;
++                              }
++
++                              if ( isspace(*ptr) ) 
++                              {
++                                      /* found somewhere to wrap to */
++                                      p -= (count-1); /* need to loose the white space */
++                                      len -= count;
++                              }               
++                      }
++
++                      if (line[len-1] != '\n' ) 
++                      {
++                              line[len] = '\n';
++                              line[len+1] = 0;
++                      }
++
++                      /* we put a \n in so dont need another one next */
++                      if ( *p == '\n')
++                              p++;
++
++#if defined(DIGITAL_UNIX)
++                      {
++                              char *pr;
++
++                              for (pr = pref; *pr; pr++)
++                                      cnputc (*pr);
++
++                              for (pr = line; *pr; pr++)
++                                      cnputc (*pr); 
++                      }
++#elif defined(LINUX)
++                      printk("%s%s",pref,line);
++#endif
++              }
++      }
++#endif /* CONFIG_MPSAS */
++}
++
++void
++qsnet_vdebugf (unsigned int mode, char *prefix, char *fmt, va_list ap)
++{
++      unsigned long flags;
++
++      if (!qsnet_debug_running) return;
++
++      spin_lock_irqsave (&qsnet_debug_lock, flags);
++
++      qsnet_debug_buffer_space[0] = '\0';
++
++#if defined(DIGITAL_UNIX)
++      prf (qsnet_debug_buffer_space+strlen(qsnet_debug_buffer_space), NULL, fmt, ap);
++#elif defined(LINUX)
++      vsprintf (qsnet_debug_buffer_space+strlen(qsnet_debug_buffer_space), fmt, ap);
++#endif
++
++      if (prefix == NULL)
++              printk ("qsnet_vdebugf: prefix==NULL\n");
++      else
++              qsnet_prefix_debug(mode, prefix, qsnet_debug_buffer_space);
++
++      spin_unlock_irqrestore (&qsnet_debug_lock, flags);
++}
++
++void kqsnet_debugf(char *fmt,...)
++{
++      if ( kqsnet_debug_running ) {
++              va_list ap;
++              char string[20];
++              
++              sprintf (string, "mm=%p:", current->mm);
++              va_start(ap, fmt);
++              qsnet_vdebugf(QSNET_DEBUG_BUFFER, string, fmt, ap);
++              va_end(ap);
++      }       
++}
++void 
++qsnet_debugf(unsigned int mode, char *fmt,...)
++{
++      va_list       ap;
++      unsigned long flags;
++
++      if (!qsnet_debug_running) return;
++
++      spin_lock_irqsave (&qsnet_debug_lock, flags);
++
++      qsnet_debug_buffer_space[0] = '\0';
++
++      va_start (ap, fmt);
++#if defined(DIGITAL_UNIX)
++      prf (qsnet_debug_buffer_space+strlen(qsnet_debug_buffer_space), NULL, fmt, ap);
++#elif defined(LINUX)
++      vsprintf (qsnet_debug_buffer_space+strlen(qsnet_debug_buffer_space), fmt, ap);
++#endif
++      va_end (ap);
++
++      qsnet_prefix_debug(mode, "", qsnet_debug_buffer_space); 
++
++      spin_unlock_irqrestore (&qsnet_debug_lock, flags);
++}
++
++int
++qsnet_debug_buffer (caddr_t ubuffer, int len)
++{
++      caddr_t buffer, ptr, base;
++      int     remain, len1;
++      unsigned long flags;
++      static  char qsnet_space[65536];
++
++      if (!qsnet_debug_running) return (0);
++
++      if (len < qsnet_debug_line_size)
++              return (-1);
++
++      if (len > (qsnet_debug_line_size * qsnet_debug_num_lines))
++              len = qsnet_debug_line_size * qsnet_debug_num_lines;
++    
++      if ( len > 65536 ) {
++              KMEM_ZALLOC (buffer, caddr_t, len, 1);
++      } else 
++              buffer = qsnet_space;
++
++      if (buffer == NULL)
++              return (-1);
++
++      if (qsnet_debug_buffer_ptr == NULL)
++              qsnet_debug_alloc();
++
++      if (qsnet_debug_buffer_ptr == NULL)
++      {
++              if ( len > 65536 )
++                      KMEM_FREE (buffer, len);
++              return (-1);
++      }
++
++      spin_lock_irqsave (&qsnet_debug_lock, flags);
++    
++      while (!qsnet_debug_lost_lines && (qsnet_debug_back == qsnet_debug_front))
++              if (kcondvar_waitsig (&qsnet_debug_wait, &qsnet_debug_lock, &flags) == 0)
++                      break;
++    
++      ptr    = buffer;
++      remain = len;
++
++      if (qsnet_debug_lost_lines)
++      {
++              qsnet_debug_lost_lines = 0;
++              strcpy (ptr, "Debug Buffer has overflowed!!\n");
++              len1 = strlen (ptr);
++
++              remain -= len1;
++              ptr    += len1;
++      }
++
++      while (qsnet_debug_front != qsnet_debug_back)
++      {
++              /* copy the line from DebugFront */
++              base = &qsnet_debug_buffer_ptr[qsnet_debug_front*qsnet_debug_line_size];
++
++              len1 = strlen (base);
++
++              if (len1 > remain)
++                      break;
++      
++              bcopy (base, ptr, len1);
++      
++              ptr += len1;
++              remain -= len1;
++
++              qsnet_debug_front = (++qsnet_debug_front == qsnet_debug_num_lines) ? 0 : qsnet_debug_front;
++      }
++
++      spin_unlock_irqrestore (&qsnet_debug_lock, flags);
++
++      len1 = ptr - buffer;
++
++      if (len1 != 0 && copyout (buffer, ubuffer, len1))
++              len1 = -1;
++
++      if ( len > 65536 )
++              KMEM_FREE (buffer, len);
++   
++      return (len1);
++}
++
++void
++qsnet_debug_buffer_on() 
++{
++      if (qsnet_debug_buffer_ptr == NULL)
++              qsnet_debug_alloc();
++}
++
++void 
++qsnet_debug_buffer_clear()
++{
++      unsigned long flags;
++
++      qsnet_debug_buffer_on();
++      
++      if (qsnet_debug_buffer_ptr != NULL){
++              spin_lock_irqsave (&qsnet_debug_lock, flags);
++              qsnet_debug_front      = 0;
++              qsnet_debug_back       = 0;
++              qsnet_prefix_debug(QSNET_DEBUG_BUFFER,"Clear","");
++              spin_unlock_irqrestore (&qsnet_debug_lock, flags);      
++      }
++}
++
++void 
++qsnet_debug_buffer_mark(char *str)
++{
++      unsigned long flags;    
++
++      qsnet_debug_buffer_on();
++
++      if (qsnet_debug_buffer_ptr != NULL) {
++              spin_lock_irqsave (&qsnet_debug_lock, flags);
++              qsnet_prefix_debug(QSNET_DEBUG_BUFFER,"Mark",str);
++              spin_unlock_irqrestore (&qsnet_debug_lock, flags);
++      }
++}
++int
++qsnet_debug_dump ()
++{
++      unsigned long flags;
++
++      if (!qsnet_debug_running) return (0);
++
++      if (qsnet_debug_buffer_ptr == NULL)
++              qsnet_debug_alloc();
++
++      if (qsnet_debug_buffer_ptr == NULL)
++              return (-1);
++
++      spin_lock_irqsave (&qsnet_debug_lock, flags);
++
++      while (qsnet_debug_front != qsnet_debug_back)
++      {
++              printk ("%s", &qsnet_debug_buffer_ptr[qsnet_debug_front*qsnet_debug_line_size]);
++
++              qsnet_debug_front = (++qsnet_debug_front == qsnet_debug_num_lines) ? 0 : qsnet_debug_front;
++      }
++
++      if (qsnet_debug_lost_lines)
++              printk ("\n**** Debug buffer has lost %d lines\n****\n",qsnet_debug_lost_lines);
++
++      spin_unlock_irqrestore (&qsnet_debug_lock, flags);
++
++      return (0);
++}
++
++int
++qsnet_debug_kmem (void *handle)
++{
++      if (!qsnet_debug_running) return (0);
++
++#ifdef KMEM_DEBUG
++      qsnet_kmem_display(handle);
++#endif
++      return (0);
++}
++
++int
++qsnet_assfail (char *ex, const char *func, char *file, int line)
++{
++      qsnet_debugf (QSNET_DEBUG_BUFFER, "qsnet: assertion failure: %s, function: %s, file %s, line: %d\n", ex, func, file, line);
++
++      printk (KERN_EMERG "qsnet: assertion failure: %s, function: %s, file %s, line: %d\n", ex, func, file, line);
++
++      if (panicstr)
++              return (0);
++
++      if (qsnet_assfail_mode & 1)                             /* return to BUG() */
++              return 1;
++
++      if (qsnet_assfail_mode & 2)
++              panic ("qsnet: assertion failure: %s, function: %s, file %s, line: %d\n", ex, func, file, line);
++      if (qsnet_assfail_mode & 4)
++              qsnet_debug_disable (1);
++
++      return 0;
++
++}
++
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/qsnet/i686_mmx.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/qsnet/i686_mmx.c        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/qsnet/i686_mmx.c     2005-05-11 12:10:12.553915096 -0400
+@@ -0,0 +1,99 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: i686_mmx.c,v 1.11 2004/01/05 12:08:25 mike Exp $"
++/*      $Source: /cvs/master/quadrics/qsnet/i686_mmx.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#if defined(LINUX_I386)
++
++#include <linux/config.h>
++#include <linux/sched.h>
++#include <asm/processor.h>
++#include <asm/i387.h>
++
++int mmx_disabled = 0;
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
++/* These functions are lifted from arch/i386/kernel/i387.c
++ * and MUST be kept in step with the kernel (currently 2.4.17)
++ * alternatively we should export the kernel_fpu_begin() function
++ */
++static inline void __save_init_fpu( struct task_struct *tsk )
++{
++      if ( cpu_has_fxsr ) {
++              asm volatile( "fxsave %0 ; fnclex"
++                            : "=m" (tsk->thread.i387.fxsave) );
++      } else {
++              asm volatile( "fnsave %0 ; fwait"
++                            : "=m" (tsk->thread.i387.fsave) );
++      }
++      tsk->flags &= ~PF_USEDFPU;
++}
++#if defined(MODULE)
++void kernel_fpu_begin(void)
++{
++      struct task_struct *tsk = current;
++
++      if (tsk->flags & PF_USEDFPU) {
++              __save_init_fpu(tsk);
++              return;
++      }
++      clts();
++}
++#endif
++#endif
++
++extern inline int
++mmx_preamble(void)
++{
++    if (mmx_disabled || in_interrupt())
++      return (0);
++
++    kernel_fpu_begin();
++
++    return (1);
++}
++
++extern inline void
++mmx_postamble(void)
++{
++    kernel_fpu_end();
++}
++
++extern u64
++qsnet_readq (volatile u64 *ptr)
++{
++    u64 value;
++
++    if (! mmx_preamble())
++      value = *ptr;
++    else
++    {
++      asm volatile ("movq (%0), %%mm0\n"
++                    "movq %%mm0, (%1)\n"
++                    : : "r" (ptr), "r" (&value) : "memory");
++      mmx_postamble();
++    }
++    return (value);
++}
++
++void
++qsnet_writeq(u64 value, volatile u64 *ptr)
++{
++    if (! mmx_preamble())
++      *ptr = value;
++    else
++    {
++      asm volatile ("movq (%0), %%mm0\n"
++                    "movq %%mm0, (%1)\n"
++                    : : "r" (&value), "r" (ptr) : "memory");
++      mmx_postamble();
++    }
++}
++#endif
+Index: linux-2.6.5/drivers/net/qsnet/qsnet/kernel_linux.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/qsnet/kernel_linux.c    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/qsnet/kernel_linux.c 2005-05-11 12:10:12.554914944 -0400
+@@ -0,0 +1,856 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: kernel_linux.c,v 1.71.2.3 2004/11/04 11:03:47 david Exp $"
++/*      $Source: /cvs/master/quadrics/qsnet/kernel_linux.c,v $*/
++
++#include <qsnet/kernel.h>
++#include <qsnet/ctrl_linux.h>
++#include <qsnet/kpte.h>
++
++#include <linux/sysctl.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++
++#include <qsnet/procfs_linux.h>
++
++#include <linux/smp.h>                /* for smp_call_function() prototype */
++#include <linux/smp_lock.h>
++#include <linux/mm.h>
++
++#include <linux/highmem.h>
++
++extern int mmx_disabled;
++extern int qsnet_debug_line_size;
++extern int qsnet_debug_num_lines;
++
++gid_t                qsnet_procfs_gid;
++struct proc_dir_entry *qsnet_procfs_root;
++struct proc_dir_entry *qsnet_procfs_config;
++
++MODULE_AUTHOR("Quadrics Ltd.");
++MODULE_DESCRIPTION("QsNet Kernel support code");
++
++MODULE_LICENSE("GPL");
++
++#if defined(LINUX_I386)
++MODULE_PARM(mmx_disabled, "i");
++#endif
++
++MODULE_PARM(qsnet_debug_line_size, "i");
++MODULE_PARM(qsnet_debug_num_lines, "i");
++
++MODULE_PARM(qsnet_procfs_gid, "i");
++
++#ifdef KMEM_DEBUG
++EXPORT_SYMBOL(qsnet_kmem_alloc_debug);
++EXPORT_SYMBOL(qsnet_kmem_free_debug);
++#else
++EXPORT_SYMBOL(qsnet_kmem_alloc);
++EXPORT_SYMBOL(qsnet_kmem_free);
++#endif
++
++EXPORT_SYMBOL(qsnet_kmem_display);
++EXPORT_SYMBOL(kmem_to_phys);
++
++EXPORT_SYMBOL(cpu_hold_all);
++EXPORT_SYMBOL(cpu_release_all);
++
++#if defined(LINUX_I386)
++EXPORT_SYMBOL(qsnet_readq);
++EXPORT_SYMBOL(qsnet_writeq);
++#endif
++
++/* debug.c */
++EXPORT_SYMBOL(qsnet_debugf);
++EXPORT_SYMBOL(kqsnet_debugf);
++EXPORT_SYMBOL(qsnet_vdebugf);
++EXPORT_SYMBOL(qsnet_debug_buffer);
++EXPORT_SYMBOL(qsnet_debug_alloc);
++EXPORT_SYMBOL(qsnet_debug_dump);
++EXPORT_SYMBOL(qsnet_debug_kmem);
++EXPORT_SYMBOL(qsnet_debug_disable);
++
++EXPORT_SYMBOL(qsnet_assfail);
++
++EXPORT_SYMBOL(qsnet_procfs_gid);
++EXPORT_SYMBOL(qsnet_procfs_root);
++
++static int qsnet_open    (struct inode *ino, struct file *fp);
++static int qsnet_release (struct inode *ino, struct file *fp);
++static int qsnet_ioctl   (struct inode *ino, struct file *fp, unsigned int cmd, unsigned long arg);
++
++static struct file_operations qsnet_ioctl_fops = 
++{
++      ioctl:   qsnet_ioctl,
++      open:    qsnet_open,
++      release: qsnet_release,
++};
++
++static int
++qsnet_open (struct inode *inode, struct file *fp)
++{
++      MOD_INC_USE_COUNT;
++      fp->private_data = NULL;
++      return (0);
++}
++
++static int
++qsnet_release (struct inode *inode, struct file *fp)
++{
++      MOD_DEC_USE_COUNT;
++      return (0);
++}
++
++static int 
++qsnet_ioctl(struct inode *inode, struct file *fp, unsigned int cmd, unsigned long arg)
++{
++      int res=0;
++
++      switch (cmd) 
++      {
++      case QSNETIO_DEBUG_KMEM:
++      {
++              QSNETIO_DEBUG_KMEM_STRUCT args;
++
++              if (copy_from_user (&args, (void *) arg, sizeof (QSNETIO_DEBUG_KMEM_STRUCT)))
++                      return (-EFAULT);
++
++              /* doesnt use handle as a pointer */
++              qsnet_kmem_display(args.handle);
++              break;
++      }
++
++      case QSNETIO_DEBUG_DUMP : 
++      {
++              res = qsnet_debug_dump();
++              break;
++      }
++
++      case QSNETIO_DEBUG_BUFFER :
++      {
++              QSNETIO_DEBUG_BUFFER_STRUCT args;
++
++              if (copy_from_user (&args, (void *) arg, sizeof (QSNETIO_DEBUG_BUFFER_STRUCT)))
++                      return (-EFAULT);
++
++              /* qsnet_debug_buffer uses copyout */
++              if ((res = qsnet_debug_buffer (args.addr, args.len)) != -1)
++              {
++                      args.len = res;
++                      if (copy_to_user ((void *) arg, &args, sizeof (QSNETIO_DEBUG_BUFFER_STRUCT)))
++                              return (-EFAULT);
++                      res = 0;
++              }
++              break;
++      }
++      default:
++              res = EINVAL;
++              break;
++      }
++
++      return ((res == 0) ? 0 : -res);
++}
++
++#ifdef KMEM_DEBUG
++static int qsnet_kmem_open    (struct inode *ino, struct file *fp);
++static int qsnet_kmem_release (struct inode *ino, struct file *fp);
++static ssize_t qsnet_kmem_read (struct file *file, char *buf, size_t count, loff_t *ppos);
++
++static struct file_operations qsnet_kmem_fops = 
++{
++      open:    qsnet_kmem_open,
++      release: qsnet_kmem_release,
++      read:    qsnet_kmem_read,
++};
++
++typedef struct qsnet_private_space
++{
++      char * space;
++      int    size;
++      struct qsnet_private_space *next;
++} QSNET_PRIVATE_SPACE;
++
++typedef struct qsnet_private  
++{
++      QSNET_PRIVATE_SPACE *space_chain;
++        QSNET_PRIVATE_SPACE *current_space;
++      int                  current_pos;
++
++} QSNET_PRIVATE;
++
++#define QSNET_KMEM_DEBUG_LINE_SIZE ((int)512)
++#define QSNET_PRIVATE_PAGE_SIZE    ((int)(4*1024))
++
++static int qsnet_kmem_fill(QSNET_PRIVATE *pd);
++
++void
++destroy_chain(QSNET_PRIVATE * pd)
++{
++      QSNET_PRIVATE_SPACE *mem, *next;
++      
++      if (pd == NULL) return;
++
++      for(mem = pd->space_chain ; mem != NULL; )
++      {
++              next = mem->next; 
++              if ( mem->space ) 
++                      kfree ( mem->space);
++              kfree(mem);
++              mem = next;
++      }
++      kfree (pd);
++}
++
++QSNET_PRIVATE *
++make_chain(int len)
++{
++      QSNET_PRIVATE       * pd;
++      QSNET_PRIVATE_SPACE * mem;
++      int                   i;
++
++      /* make the private data block */
++      if ((pd = kmalloc (sizeof (QSNET_PRIVATE), GFP_KERNEL)) == NULL)
++              return NULL;
++      pd->space_chain = NULL;
++
++      /* first make the holders */
++      for(i=0;i<len;i++)
++      {
++              if ((mem = kmalloc (sizeof (QSNET_PRIVATE_SPACE), GFP_KERNEL)) == NULL)
++              {
++                      destroy_chain(pd);
++                      return (NULL);
++              }
++              mem->next  = pd->space_chain;
++              mem->size  = 0;
++              mem->space = 0;
++              pd->space_chain = mem;
++
++              /* now add the space */
++              if ((mem->space = kmalloc (QSNET_PRIVATE_PAGE_SIZE, GFP_KERNEL)) == NULL)
++              {
++                      destroy_chain(pd);
++                      return (NULL);
++              }                       
++
++              mem->space[0] = 0;
++
++      }
++
++      pd->current_space = pd->space_chain;
++      pd->current_pos   = 0;
++
++      return pd;
++}
++
++static int
++qsnet_kmem_open (struct inode *inode, struct file *fp)
++{
++      MOD_INC_USE_COUNT;
++      fp->private_data = NULL;
++      return (0);
++}
++
++static int
++qsnet_kmem_release (struct inode *inode, struct file *fp)
++{
++      if ( fp->private_data )
++      {
++              QSNET_PRIVATE * pd = (QSNET_PRIVATE *) fp->private_data;
++
++              /* free the space */
++              if (pd->space_chain)
++                      kfree (pd->space_chain);        
++
++              /* free struct */
++              kfree (pd);
++      }
++      MOD_DEC_USE_COUNT;
++      return (0);
++}
++
++static ssize_t
++qsnet_kmem_read (struct file *file, char *buf, size_t count, loff_t *ppos)
++{
++      QSNET_PRIVATE * pd = (QSNET_PRIVATE *) file->private_data;
++      int             error;
++      int             output_count;
++      int             num_of_links=10;
++
++      /* make a buffer to output count bytes in */
++      if ((error = verify_area (VERIFY_WRITE, buf, count)) != 0)
++              return (error);
++
++      if ( pd == NULL) 
++      {
++              /* first time */
++
++              /* ok we have to guess at how much space we are going to need  */
++              /* if it fails we up the space and carry try again             */
++              /* we have to do it this way as we cant get more memory whilst */
++              /* holding the lock                                            */
++              if ((pd = make_chain(num_of_links)) == NULL)
++                      return (-ENOMEM);       
++
++              while ( qsnet_kmem_fill(pd) ) 
++              {
++                      destroy_chain(pd);
++                      num_of_links += 10;
++                      if ((pd = make_chain(num_of_links)) == NULL)
++                              return (-ENOMEM);       
++              }
++
++              /* we have the space and filled it */
++              file->private_data = (void *)pd;        
++      }
++              
++      /* output buffer */
++      if ( pd->current_pos >= pd->current_space->size )
++              return (0); /* finished */
++
++      output_count = pd->current_space->size - pd->current_pos;
++      if ( output_count > count ) 
++              output_count = count;
++
++      copy_to_user(buf, (pd->current_space->space + pd->current_pos), output_count);
++
++      pd->current_pos += output_count;
++      ppos            += output_count;
++
++      /* just check to see if we have finished the current space */
++      if ( pd->current_pos >= pd->current_space->size )
++      {
++              if ( pd->current_space->next )
++              {
++                      pd->current_space = pd->current_space->next;
++                      pd->current_pos   = 0;
++              }
++      }       
++
++      return (output_count);
++}
++#endif /* KMEM_DEBUG */
++
++static int
++proc_write_qsnetdebug(struct file *file, const char *buffer,
++                    unsigned long count, void *data)
++{
++      char    tmpbuf[128];
++      int     res;
++      
++      if (count > sizeof (tmpbuf)-1)
++              return (-EINVAL);
++      
++      MOD_INC_USE_COUNT;
++      
++      if (copy_from_user (tmpbuf, buffer, count))
++              res = -EFAULT;
++      else 
++      {
++              tmpbuf[count] = '\0';   
++              
++              if (tmpbuf[count-1] == '\n')
++                      tmpbuf[count-1] = '\0';
++              
++              if (! strcmp (tmpbuf, "on"))
++                      qsnet_debug_buffer_on();
++
++              if (! strcmp (tmpbuf, "clear"))
++                      qsnet_debug_buffer_clear();
++
++              if (! strncmp (tmpbuf, "mark",4))
++                      qsnet_debug_buffer_mark( &tmpbuf[4] );
++              
++              res = count;
++      }
++      
++      MOD_DEC_USE_COUNT;
++      
++      return (res);
++}
++
++static int
++proc_read_qsnetdebug(char *page, char **start, off_t off,
++                   int count, int *eof, void *data)
++{
++      int len = sprintf (page, "echo command > /proc/qsnet/config/qsnetdebug\ncommand = on | off | clear | mark text\n");
++      return (qsnet_proc_calc_metrics (page, start, off, count, eof, len));
++}
++
++#include "quadrics_version.h"
++extern int kqsnet_debug_running;
++static char       quadrics_version[] = QUADRICS_VERSION;
++
++static int __init qsnet_init(void)
++{
++      struct proc_dir_entry *p;
++
++      if ((qsnet_procfs_root = proc_mkdir ("qsnet", 0)) == NULL)
++      {
++              printk ("qsnet: failed to create /proc/qsnet \n");
++              return (-ENXIO);
++      }
++      
++      if ((p = create_proc_entry ("ioctl", S_IRUGO|S_IWUSR|S_IWGRP, qsnet_procfs_root)) == NULL)
++      {
++              printk ("qsnet: failed to register /proc/qsnet/ioctl\n");
++              return (-ENXIO);
++      }
++      p->proc_fops = &qsnet_ioctl_fops;
++      p->owner     = THIS_MODULE;
++      p->data      = NULL;
++      p->gid       = qsnet_procfs_gid;
++
++      qsnet_proc_register_str (qsnet_procfs_root, "version", quadrics_version, S_IRUGO);
++
++      if ((qsnet_procfs_config = proc_mkdir ("config", qsnet_procfs_root)) == NULL)
++      {
++              printk ("qsnet: failed to create /proc/qsnet/config \n");
++              return (-ENXIO);
++      }
++
++#ifdef KMEM_DEBUG
++      if ((p = create_proc_entry ("kmem_debug", S_IRUGO|S_IWUSR|S_IWGRP, qsnet_procfs_config)) == NULL)
++      {
++              printk ("qsnet: failed to register /proc/qsnet/config/kmem_debug\n");
++              return (-ENXIO);
++      }
++      p->proc_fops = &qsnet_kmem_fops;
++      p->owner     = THIS_MODULE;
++      p->data      = NULL;
++      p->gid       = qsnet_procfs_gid;
++#endif                
++      
++      qsnet_debug_init(); 
++
++      qsnet_proc_register_int (qsnet_procfs_config, "kqsnet_debug_running", &kqsnet_debug_running, 0);
++
++      if ((p = create_proc_entry ("qsnetdebug", S_IRUGO|S_IWUSR|S_IWGRP, qsnet_procfs_config)) == NULL)
++      {
++              printk ("qsnet: failed to register /proc/qsnet/config/qsnetdebug\n");
++              return (-ENXIO);
++      }
++      p->read_proc  = proc_read_qsnetdebug;
++      p->write_proc = proc_write_qsnetdebug;
++      p->owner      = THIS_MODULE;
++      p->data       = NULL;
++      p->gid        = qsnet_procfs_gid;
++      
++      return (0);
++}
++
++static void __exit qsnet_exit(void)
++{
++#ifdef KMEM_DEBUG
++      qsnet_kmem_display(0);
++#endif
++      qsnet_debug_fini();
++
++      remove_proc_entry ("qsnetdebug",           qsnet_procfs_config);
++      remove_proc_entry ("kqsnet_debug_running", qsnet_procfs_config);
++#ifdef KMEM_DEBUG
++      remove_proc_entry ("kmem_debug",           qsnet_procfs_config);
++#endif
++      remove_proc_entry ("config",               qsnet_procfs_root);
++
++      remove_proc_entry ("version", qsnet_procfs_root);
++      remove_proc_entry ("ioctl",   qsnet_procfs_root);
++
++      remove_proc_entry ("qsnet", 0);
++}
++
++/* Declare the module init and exit functions */
++module_init(qsnet_init);
++module_exit(qsnet_exit);
++
++#ifdef KMEM_DEBUG
++/*
++ * Kernel memory allocation.  We maintain our own list of allocated mem
++ * segments so we can free them on module cleanup.
++ * 
++ * We use kmalloc for allocations less than one page in size; vmalloc for
++ * larger sizes.
++ */
++
++typedef struct {
++      struct list_head list;
++      void            *ptr;
++      int             len;
++      int             used_vmalloc;
++      void            *owner;
++      void            *caller;
++      unsigned int     time;
++      int              line;
++      char             filename[20];
++} kmalloc_t;
++
++static LIST_HEAD(kmalloc_head);
++
++static spinlock_t     kmalloc_lock = SPIN_LOCK_UNLOCKED;
++
++/*
++ * Kernel memory allocation.  We use kmalloc for allocations less 
++ * than one page in size; vmalloc for larger sizes.
++ */
++
++static int
++qsnet_kmem_fill(QSNET_PRIVATE *pd)
++{
++      kmalloc_t *kp;
++      struct list_head *lp;
++      unsigned long flags;
++      char str[QSNET_KMEM_DEBUG_LINE_SIZE];
++      QSNET_PRIVATE_SPACE * current_space;
++      int                   current_pos;
++      int                   len;
++      current_space = pd->space_chain;
++      current_pos   = 0;
++      
++      
++      current_space->space[0] = 0;    
++      spin_lock_irqsave(&kmalloc_lock, flags);
++      for (lp = kmalloc_head.next; lp != &kmalloc_head;  lp = lp->next) {
++              kp = list_entry(lp, kmalloc_t, list);
++              
++              /* make the next line */
++              sprintf(str,"%p %d %d %p %p %u %d %s\n",
++                      kp->ptr, kp->len, kp->used_vmalloc, kp->caller, kp->owner, kp->time, kp->line, kp->filename);
++              len = strlen(str);
++              
++              /* does it fit on the current page */
++              if ( (current_pos + len + 1) >=  QSNET_PRIVATE_PAGE_SIZE)
++              {
++                      /* move onto next page */
++                      if ((current_space = current_space->next) == NULL)
++                      {
++                              /* run out of space !!!! */
++                              spin_unlock_irqrestore(&kmalloc_lock, flags);
++                              return (1);
++                      }
++                      current_space->space[0] = 0;    
++                      current_pos = 0;
++              }
++              strcat( current_space->space + current_pos, str);
++              current_pos += len;
++
++              /* remember how much we wrote to this page */
++              current_space->size = current_pos;
++
++      }
++      spin_unlock_irqrestore(&kmalloc_lock, flags);
++
++      return (0);
++}
++
++void * 
++qsnet_kmem_alloc_debug(int len, int cansleep, int zerofill, char *file, int line)
++{
++      void *new;
++      unsigned long flags;
++      kmalloc_t *kp;
++
++      if (len < PAGE_SIZE || !cansleep)
++              new = kmalloc(len, cansleep ? GFP_KERNEL : GFP_ATOMIC);
++      else
++              new = vmalloc(len);
++
++      if (len >= PAGE_SIZE)
++              ASSERT(PAGE_ALIGNED((uintptr_t) new));
++
++      if (new && zerofill)
++              memset(new,0,len);
++
++      /* record allocation */
++      kp = kmalloc(sizeof(kmalloc_t), cansleep ? GFP_KERNEL : GFP_ATOMIC);
++      ASSERT(kp != NULL);
++      kp->len = len;
++      kp->ptr = new;
++      kp->used_vmalloc = (len >= PAGE_SIZE || cansleep);
++      kp->owner  = current;
++      kp->caller = __builtin_return_address(0);
++      kp->time = lbolt;
++      kp->line = line;
++      len = strlen(file);
++
++      if (len > 18) 
++              strcpy(kp->filename,&file[len-18]);
++      else
++              strcpy(kp->filename,file);
++
++      spin_lock_irqsave(&kmalloc_lock, flags);
++      list_add(&kp->list, &kmalloc_head);
++      spin_unlock_irqrestore(&kmalloc_lock, flags);
++
++      return new;
++}
++
++void 
++qsnet_kmem_free_debug(void *ptr, int len, char *file, int line)
++{
++      unsigned long flags;
++      kmalloc_t *kp;
++      struct list_head *lp;
++
++      spin_lock_irqsave(&kmalloc_lock, flags);
++      for (lp = kmalloc_head.next; lp != &kmalloc_head; lp = lp->next) {
++              kp = list_entry(lp, kmalloc_t, list);
++              if (kp->ptr == ptr) {
++                      if (kp->len != len)
++                              printk("qsnet_kmem_free_debug(%p) ptr %p len %d mismatch: expected %d caller %p owner %p (%s:%d)\n",
++                                     current, ptr, len, kp->len, __builtin_return_address(0), kp->caller, file, line);
++                      list_del(lp);
++                      kfree(kp); /* free off descriptor */
++                      break;
++              }
++      }
++      spin_unlock_irqrestore(&kmalloc_lock, flags);
++
++      if (lp == &kmalloc_head) /* segment must be found */
++      {
++              printk( "qsnet_kmem_free_debug(%p) ptr %p len %d not found: caller %p (%s:%d)\n",
++                      current, ptr, len, __builtin_return_address(0), file, line);
++      }
++
++      if ((((unsigned long) ptr) >= VMALLOC_START && ((unsigned long) ptr) < VMALLOC_END)) 
++              vfree (ptr);
++      else
++              kfree (ptr);
++}
++
++#else /* !KMEM_DEBUG */
++
++void * 
++qsnet_kmem_alloc(int len, int cansleep, int zerofill)
++{
++      void *new;
++
++      if (len < PAGE_SIZE || !cansleep)
++              new = kmalloc(len, cansleep ? GFP_KERNEL : GFP_ATOMIC);
++      else
++              new = vmalloc(len);
++
++      if (len >= PAGE_SIZE)
++              ASSERT(PAGE_ALIGNED((unsigned long) new));
++
++      if (new && zerofill)
++              memset(new,0,len);
++
++      return new;
++}
++
++void 
++qsnet_kmem_free(void *ptr, int len)
++{
++      if ((((unsigned long) ptr) >= VMALLOC_START && ((unsigned long) ptr) < VMALLOC_END)) 
++              vfree (ptr);
++      else
++              kfree (ptr);
++}
++#endif /* !KMEM_DEBUG */
++
++void
++qsnet_kmem_display(void *handle)
++{
++#ifdef KMEM_DEBUG
++      kmalloc_t *kp;
++      struct list_head *lp;
++      unsigned long flags;
++      int count = 0, totsize = 0;
++
++      spin_lock_irqsave(&kmalloc_lock, flags);
++      for (lp = kmalloc_head.next; lp != &kmalloc_head;  lp = lp->next) {
++              kp = list_entry(lp, kmalloc_t, list);
++
++              if (!handle || handle == kp->owner)
++              {
++                      printk("qsnet_kmem_display(%p): mem %p len %d unfreed caller %p (%p) \n",
++                             handle, kp->ptr, kp->len, kp->caller, kp->owner);
++                  
++                      count++;
++                      totsize += kp->len;
++              }
++      }
++      spin_unlock_irqrestore(&kmalloc_lock, flags);
++
++      printk("qsnet_kmem_display(%p): %d bytes left in %d objects\n", handle, totsize, count);
++#endif
++}
++
++physaddr_t
++kmem_to_phys(void *ptr)
++{
++      virtaddr_t virt = (virtaddr_t) ptr;
++      physaddr_t phys;
++      pte_t     *pte;
++
++      if ((virt >= VMALLOC_START && virt < VMALLOC_END))
++      {
++              pte = find_pte_kernel(virt);
++              ASSERT(pte && !pte_none(*pte));
++              phys = pte_phys(*pte) + (virt & (PAGE_SIZE-1));
++      }
++#if defined(PKMAP_BASE)
++      else if (virt >= PKMAP_BASE && virt < (PKMAP_BASE + LAST_PKMAP * PAGE_SIZE))
++      {
++              pte = find_pte_kernel(virt);
++              ASSERT(pte && !pte_none(*pte));
++              phys = pte_phys(*pte) + (virt & (PAGE_SIZE-1));
++      }
++#endif
++#if defined(__ia64)
++      else if (virt >= __IA64_UNCACHED_OFFSET && virt < PAGE_OFFSET)
++      {
++              /* ia64 non-cached KSEG */
++              phys = ((physaddr_t) ptr - __IA64_UNCACHED_OFFSET);
++      }
++#endif
++      else /* otherwise it's KSEG */
++      {
++              phys = __pa(virt);
++      }
++          
++#if defined(CONFIG_ALPHA_GENERIC) || (defined(CONFIG_ALPHA_EV6) && !defined(USE_48_BIT_KSEG))
++      /* 
++       * with TS_BIAS as bit 40 - the tsunami pci space is mapped into
++       * the kernel at 0xfffff500.00000000 however we need to convert
++       * this to the true physical address 0x00000800.00000000.
++       *
++       * there is no need for PHYS_TWIDDLE since we knew we'd get a kernel
++       * virtual address already and handled this with __pa().
++       */
++      if (phys & (1ul << 40)) {
++              phys &= ~(1ul << 40);   /*   clear bit 40 (kseg I/O select) */
++              phys |= (1ul << 43);    /*   set   bit 43 (phys I/O select) */
++      }
++#endif
++      return phys;
++}
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
++
++EXPORT_SYMBOL(pci_resource_size);
++EXPORT_SYMBOL(pci_get_base_address);
++EXPORT_SYMBOL(pci_base_to_kseg);
++
++
++/*
++ * PCI stuff.  
++ *
++ * XXX pci_base_to_kseg() and pci_kseg_to_phys() are problematic
++ * in that they may not work on non-Tsunami (DS20, ES40, etc) 
++ * architectures, and may not work in non-zero PCI bus numbers.
++ */
++
++unsigned long 
++pci_get_base_address(struct pci_dev *pdev, int index)
++{
++      unsigned long base;
++
++      ASSERT(index >= 0 && index <= 5);
++      /* borrowed in part from drivers/scsi/sym53c8xx.c */
++      base = pdev->base_address[index++];
++
++#if BITS_PER_LONG > 32
++      if ((base & 0x7) == 0x4)
++              base |= (((unsigned long)pdev->base_address[index]) << 32);
++#endif
++      return base;
++}
++
++unsigned long 
++pci_resource_size(struct pci_dev *pdev, int index)
++{
++      u32 addr, mask, size;
++
++      static u32 bar_addr[] = {
++              PCI_BASE_ADDRESS_0, 
++              PCI_BASE_ADDRESS_1, 
++              PCI_BASE_ADDRESS_2,
++              PCI_BASE_ADDRESS_3, 
++              PCI_BASE_ADDRESS_4, 
++              PCI_BASE_ADDRESS_5, 
++      };
++      ASSERT(index >= 0 && index <= 5);
++
++      /* algorithm from Rubini book */
++      pci_read_config_dword (pdev,    bar_addr[index], &addr);
++      pci_write_config_dword(pdev,    bar_addr[index], ~0);
++      pci_read_config_dword (pdev,    bar_addr[index], &mask);
++      pci_write_config_dword(pdev,    bar_addr[index], addr);
++
++      mask &= PCI_BASE_ADDRESS_MEM_MASK;
++      size = ~mask + 1;
++      return size;
++}
++
++/*
++ * Convert BAR register value to KSEG address.
++ */
++void *
++pci_base_to_kseg(u64 baddr, int bus)
++{
++      u64 kseg;
++
++      /* XXX tsunami specific */
++      baddr &= ~(u64)0x100000000;  /* mask out hose bit */
++      kseg = TSUNAMI_MEM(bus) + baddr;
++      return (void *)kseg; 
++}
++
++#endif /* LINUX_VERSION_CODE <= KERNEL_VERSION(2,4,0) */
++
++/*
++ * Spin the other CPU's in an SMP system.
++ * smp_call_function() needed to be exported to modules.  It will be
++ * papered over in <linux/smp.h> if running on a non-SMP box.
++ */
++static spinlock_t hold_lock = SPIN_LOCK_UNLOCKED;
++
++#if 0
++static void cpu_hold(void *unused)
++{
++      spin_lock(&hold_lock);
++      spin_unlock(&hold_lock);
++}
++#endif
++
++void cpu_hold_all(void)
++{
++      spin_lock(&hold_lock);
++
++#if 0
++      {
++              int res;
++              int retries = 10; 
++          
++              /* XXXXX: cannot call smp_call_function() from interrupt context */
++          
++              do {
++                      /* only request blocking retry if not in interrupt context */
++                      res = smp_call_function(cpu_hold, NULL, !in_interrupt(), 0);
++                      if (res)
++                              mdelay(5);
++              } while (res && retries--);
++          
++              if (res)
++                      printk("cpu_hold_all: IPI timeout\n");
++      }
++#endif
++}
++
++void cpu_release_all(void)
++{
++      spin_unlock(&hold_lock);
++}
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/qsnet/Makefile
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/qsnet/Makefile  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/qsnet/Makefile       2005-05-11 12:10:12.555914792 -0400
+@@ -0,0 +1,15 @@
++#
++# Makefile for Quadrics QsNet
++#
++# Copyright (c) 2002-2004 Quadrics Ltd
++#
++# File: drivers/net/qsnet/qsnet/Makefile
++#
++
++
++#
++
++obj-$(CONFIG_QSNET)   += qsnet.o
++qsnet-objs    := debug.o kernel_linux.o i686_mmx.o
++
++EXTRA_CFLAGS          +=  -DDEBUG -DDEBUG_PRINTF -DDEBUG_ASSERT
+Index: linux-2.6.5/drivers/net/qsnet/qsnet/Makefile.conf
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/qsnet/Makefile.conf     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/qsnet/Makefile.conf  2005-05-11 12:10:12.555914792 -0400
+@@ -0,0 +1,10 @@
++# Flags for generating QsNet Linux Kernel Makefiles
++MODNAME               =       qsnet.o
++MODULENAME    =       qsnet
++KOBJFILES     =       debug.o kernel_linux.o i686_mmx.o
++EXPORT_KOBJS  =       kernel_linux.o
++CONFIG_NAME   =       CONFIG_QSNET
++SGALFC                =       
++# EXTRALINES START
++
++# EXTRALINES END
+Index: linux-2.6.5/drivers/net/qsnet/qsnet/qsnetkmem_linux.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/qsnet/qsnetkmem_linux.c 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/qsnet/qsnetkmem_linux.c      2005-05-11 12:10:12.555914792 -0400
+@@ -0,0 +1,325 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: qsnetkmem_linux.c,v 1.3 2003/08/13 10:03:27 fabien Exp $"
++/*      $Source: /cvs/master/quadrics/qsnet/qsnetkmem_linux.c,v $*/
++
++/* macro macros */
++#define MACRO_BEGIN     do {
++#define MACRO_END       } while (0)
++#define offsetof(T,F) ((int )&(((T *)0)->F))
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <ctype.h>
++#include <sys/types.h>
++#include <errno.h>
++#include <unistd.h>
++#include <string.h>
++#include <qsnet/config.h>
++#include <qsnet/list.h>
++#include <qsnet/procfs_linux.h>
++#include <signal.h>
++#include <sys/wait.h>
++
++#define LIST_HEAD_INIT(name) { &(name), &(name) }
++
++#define LIST_HEAD(name) \
++      struct list_head name = LIST_HEAD_INIT(name)
++
++typedef struct {
++      struct list_head list;
++      void            *ptr;
++      int             len;
++      int             used_vmalloc;
++      void            *owner;
++      void            *caller;
++      unsigned int     time;
++      int              mark;
++      int              line;
++      char             file[256];
++      
++} kmalloc_t;
++
++
++static LIST_HEAD(current_kmem);
++static LIST_HEAD(stored_kmem);
++
++void
++count_kmem(struct list_head * list, long * count, long * size )
++{
++      long              c,s;
++      struct list_head *tmp;
++      kmalloc_t        *kmem_ptr = NULL;
++
++
++      c = s = 0L;
++
++      list_for_each(tmp, list) {
++              kmem_ptr = list_entry(tmp, kmalloc_t , list);
++              c++;
++              s += kmem_ptr->len;
++      }       
++
++      *count = c;
++      *size  = s;
++}
++
++void
++clear_kmem(struct list_head * list)
++{
++      struct list_head *tmp,*tmp2;
++      kmalloc_t        *kmem_ptr = NULL;
++
++      list_for_each_safe(tmp, tmp2, list) {
++              kmem_ptr = list_entry(tmp, kmalloc_t , list);
++              list_del_init(&kmem_ptr->list);
++              free( kmem_ptr );
++      }
++}
++
++void 
++move_kmem(struct list_head * dest, struct list_head *src)
++{
++      struct list_head *tmp,*tmp2;
++      kmalloc_t        *kp= NULL;
++
++      list_for_each_safe(tmp, tmp2, src) {
++              kp = list_entry(tmp, kmalloc_t , list);
++              list_del_init(&kp->list);
++
++/*
++              printf("mem %p len %d (vm=%d)  caller %p owner %p (%s:%d)\n",
++                     kp->ptr, kp->len, kp->used_vmalloc, kp->caller, kp->owner, kp->file, kp->line);
++*/                
++
++              list_add_tail(&kp->list, dest);
++      }
++}
++
++void
++read_kmem(struct list_head * list)
++{
++      FILE      * fd;
++      char        line[1024];
++      int         line_size = 100;
++      char      * rep;
++      kmalloc_t * kp;
++
++      clear_kmem(list);
++
++      fd = fopen(QSNET_PROCFS_KMEM_DEBUG,"r");
++      if ( fd == NULL) 
++      {
++              printf("No Kmem Debug\n");
++              return;
++      }
++
++      rep = fgets(line,line_size, fd);
++
++      while ( rep != NULL ) 
++      {
++              kp = malloc(sizeof(kmalloc_t));
++
++              sscanf(line,"%p %d %d %p %p %u %d %s\n",
++                     &kp->ptr, &kp->len, &kp->used_vmalloc, &kp->caller, &kp->owner, &kp->time, &kp->line, &kp->file[0]);
++
++/*
++              printf(">>%s<<\n",line);
++              printf("%p %d %d %p %p %u %d %s\n",
++                     kp->ptr, kp->len, kp->used_vmalloc, kp->caller, kp->owner, kp->time, kp->line, kp->file);
++*/
++
++              list_add_tail(&kp->list, list);
++
++              rep = fgets(line,line_size, fd);
++      }
++      fclose(fd);
++}
++
++void
++mark_kmem(struct list_head * list, int mark)
++{
++      struct list_head *tmp;
++      kmalloc_t        *kp = NULL;
++
++      list_for_each(tmp, list) {
++              kp = list_entry(tmp, kmalloc_t , list);
++
++              kp->mark = mark;
++      }
++}
++
++kmalloc_t *
++find_kmem(kmalloc_t * value, struct list_head * list)
++{
++      struct list_head *tmp;
++      kmalloc_t        *kp = NULL;
++
++      
++      list_for_each(tmp, list) {
++              kp = list_entry(tmp, kmalloc_t , list);
++              if ( (kp->ptr == value->ptr)
++                   && (kp->len == value->len)
++                   && (kp->used_vmalloc  == value->used_vmalloc )
++                   && (kp->owner  == value->owner )
++                   && (kp->caller  == value->caller )
++                   && (kp->time  == value->time )
++                   && (kp->line  == value->line )
++                   && !(strcmp(kp->file,value->file) ))
++                      return kp;
++      }       
++      return NULL;
++}
++
++void 
++diff_kmem(struct list_head *curr, struct list_head *stored)
++{
++      struct list_head *tmp;
++      kmalloc_t        *kp = NULL;
++      long              c,s;
++
++      mark_kmem(stored,  0);
++      mark_kmem(curr,    0);
++      
++      list_for_each(tmp, stored) {
++              kp = list_entry(tmp, kmalloc_t , list);
++              if (find_kmem( kp, curr) != NULL) 
++                      kp->mark = 1;
++      }
++      
++      list_for_each(tmp, curr) {
++              kp = list_entry(tmp, kmalloc_t , list);
++              if (find_kmem( kp, stored) != NULL) 
++                      kp->mark = 1;
++      }               
++
++      c=s=0L;
++      list_for_each(tmp, stored) {
++              kp = list_entry(tmp, kmalloc_t , list);
++              if (kp->mark != 1)
++              {
++                      printf("-- mem %p len %d (vm=%d)  caller %p owner %p (%s:%d)\n",
++                             kp->ptr, kp->len, kp->used_vmalloc, kp->caller, kp->owner, kp->file, kp->line);
++                      c++;
++                      s+= kp->len;
++              }
++      }
++      printf("-- %4ld %10ld \n",c,s);
++      
++      c=s=0L;
++      list_for_each(tmp, curr) {
++              kp = list_entry(tmp, kmalloc_t , list);
++              if (kp->mark != 1)
++              {
++                      printf("++ mem %p len %d (vm=%d)  caller %p owner %p (%s:%d)\n",
++                             kp->ptr, kp->len, kp->used_vmalloc, kp->caller, kp->owner, kp->file, kp->line);
++                      c++;
++                      s+= kp->len;
++              }
++      }               
++      printf("++ %4ld %10ld \n",c,s);
++}
++
++
++void
++print_kmem(struct list_head * list)
++{
++      struct list_head *tmp;
++      kmalloc_t        *kp = NULL;
++
++      list_for_each(tmp, list) {
++              kp = list_entry(tmp, kmalloc_t , list);
++
++              printf("mem %p len %d (vm=%d)  caller %p owner %p (%s:%d)\n",
++                     kp->ptr, kp->len, kp->used_vmalloc, kp->caller, kp->owner, kp->file, kp->line);
++                  
++      }
++}
++
++void 
++print_cmds()
++{
++      long c,s;
++
++      printf("q : quits \n");
++      printf("r : read\n");
++      printf("c : print current\n");
++      printf("o : print stored\n");
++      printf("s : store\n");
++
++      count_kmem(&current_kmem, &c, &s );
++      printf("\ncurrent : %4ld %10ld\n", c , s);
++ 
++      count_kmem(&stored_kmem, &c, &s );
++      printf("store   : %4ld %10ld\n", c , s);
++ 
++}
++
++int
++main()
++{
++      char            line[128];
++      int             line_size=127;
++      int             len;
++
++
++      while (1)
++      {
++              
++              printf(">> ");
++              fgets(line,line_size, stdin);
++      
++              
++              len = strlen( line ) -1;
++              if ( len ) 
++              {
++                      switch ( tolower(line[0]) ) 
++                      {
++                      case 'q':
++                              exit(0);
++
++                      case 'r' :
++                              read_kmem(&current_kmem);
++                              break;
++
++                      case 'c' :
++                              print_kmem(&current_kmem);
++                              break;
++
++                      case 'o' :
++                              print_kmem(&stored_kmem);
++                              break;
++
++                      case 's' :
++                              clear_kmem(&stored_kmem);
++                              move_kmem(&stored_kmem, &current_kmem);
++                              break;
++
++                      case 'd' :
++                              diff_kmem(&current_kmem, &stored_kmem);
++                              break;
++
++                      default:
++                              print_cmds();   
++                      }
++
++              
++                      
++              }
++              else
++                      print_cmds();
++      }
++
++}
++
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/qsnet/qsnet/quadrics_version.h
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/qsnet/quadrics_version.h        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/qsnet/quadrics_version.h     2005-05-11 12:10:12.556914640 -0400
+@@ -0,0 +1 @@
++#define QUADRICS_VERSION "4.31qsnet"
+Index: linux-2.6.5/drivers/net/qsnet/rms/Makefile
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/rms/Makefile    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/rms/Makefile 2005-05-11 12:10:12.556914640 -0400
+@@ -0,0 +1,15 @@
++#
++# Makefile for Quadrics QsNet
++#
++# Copyright (c) 2002-2004 Quadrics Ltd
++#
++# File: drivers/net/qsnet/rms/Makefile
++#
++
++
++#
++
++obj-$(CONFIG_RMS)     += rms.o
++rms-objs      := rms_kern.o rms_kern_Linux.o
++
++EXTRA_CFLAGS          +=  -DDEBUG -DDEBUG_PRINTF -DDEBUG_ASSERT
+Index: linux-2.6.5/drivers/net/qsnet/rms/Makefile.conf
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/rms/Makefile.conf       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/rms/Makefile.conf    2005-05-11 12:10:12.556914640 -0400
+@@ -0,0 +1,10 @@
++# Flags for generating QsNet Linux Kernel Makefiles
++MODNAME               =       rms.o
++MODULENAME    =       rms
++KOBJFILES     =       rms_kern.o rms_kern_Linux.o
++EXPORT_KOBJS  =       
++CONFIG_NAME   =       CONFIG_RMS
++SGALFC                =       
++# EXTRALINES START
++
++# EXTRALINES END
+Index: linux-2.6.5/drivers/net/qsnet/rms/quadrics_version.h
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/rms/quadrics_version.h  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/rms/quadrics_version.h       2005-05-11 12:10:12.557914488 -0400
+@@ -0,0 +1 @@
++#define QUADRICS_VERSION "4.31qsnet"
+Index: linux-2.6.5/drivers/net/qsnet/rms/rms_kern.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/rms/rms_kern.c  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/rms/rms_kern.c       2005-05-11 12:10:12.559914184 -0400
+@@ -0,0 +1,1757 @@
++/*
++ * Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ * For licensing information please see the supplied COPYING file
++ *
++ * rms_kern.c:    RMS kernel module
++ *
++ * $Source: /cvs/master/quadrics/rmsmod/rms_kern.c,v $
++ */
++
++#ident "@(#)$Id: rms_kern.c,v 1.62.2.4 2005/01/18 11:05:45 duncan Exp $"
++
++#include <stddef.h>
++#include <qsnet/kernel.h>
++#include <qsnet/autoconf.h>
++#include <rms/rmscall.h>
++
++/*
++ * extend stats added in version 5
++ */
++#define RMS_MODVERSION 5
++
++#if defined(SOLARIS)
++
++#define CURUID() CURPROC()->p_cred->cr_uid
++#define RMS_NCPUS() 4
++#define PROC_STRUCT proc
++
++#include <sys/time.h>
++
++#elif defined(LINUX)
++
++#ifdef PROCESS_ACCT 
++#define TIMEVAL_TO_MSEC(tv) ((tv)->tv_sec * 1000 + (tv)->tv_usec / 1000)
++#define TIMEVAL_TO_CT(tv)   ((tv)->tv_sec * HZ + (tv)->tv_usec / (1000000L / HZ))
++#endif
++
++#ifdef RSS_ATOMIC
++#define PROC_RSS(proc)        ((proc)->mm ? atomic_read(&(proc)->mm->rss) : 0)
++#else
++#define PROC_RSS(proc)        ((proc)->mm ? (proc)->mm->rss : 0)
++#endif
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
++#     define  RMS_NCPUS()     smp_num_cpus
++#else
++#     define RMS_NCPUS()      num_online_cpus()
++#endif
++
++#define CURUID()      CURPROC()->uid
++#define p_pid         pid
++#define PROC_STRUCT   task_struct
++
++/* care needed with conversion to millisecs on 32-bit Linux */
++#ifdef LINUX
++#ifdef LINUX_I386
++#define CT_TO_MSEC(x) ct_to_msec(x)
++
++uint64_t ct_to_msec(clock_t t)
++{
++    uint64_t msecs;
++    if (t < 2000000)
++    {
++      t = (1000 * t)/HZ;
++      msecs = t;
++    }
++    else
++    {
++      t = t / HZ;
++      msecs = t * 1000;
++    }
++    return(msecs);
++}
++
++#else
++#define CT_TO_MSEC(x) (((x) * 1000)/HZ)
++#endif
++#endif
++
++#ifndef FALSE
++#define FALSE         (0)
++#define TRUE          (!FALSE)
++#endif
++
++#include <linux/time.h>
++#include <linux/proc_fs.h>
++#include <linux/ptrack.h>
++
++#include <linux/module.h>
++
++#elif defined(DIGITAL_UNIX)
++
++#define CURUID() CURPROC()->p_ruid
++extern  int ncpus;
++#define RMS_NCPUS() ncpus
++#define PROC_STRUCT proc
++#define TIMEVAL_TO_MSEC(tv) ((tv)->tv_sec * 1000 + (tv)->tv_usec / 1000)
++
++#include <sys/time.h>
++
++#else
++#error cannot determine operating system
++#endif
++
++int shm_cleanup(void);
++
++struct cap_desc {
++
++    struct cap_desc *next;
++    int              index;   /* index of capability in program */
++    ELAN_CAPABILITY  cap;     /* elan capability */
++
++};
++
++struct proc_desc {
++    
++    struct proc_desc    *next;
++    struct PROC_STRUCT  *proc;
++    struct prg_desc     *program;     /* controlling program         */
++    int                  mycap;               /* index of my capability      */
++    int                  myctx;               /* context number for process  */
++    int                  flags;
++    int                  vp;          /* elan virtual process number */
++};
++
++struct prg_desc {
++    
++    struct prg_desc  *next;           
++    int               id;     /* program id                          */
++    int               flags;  /* program status flags                */
++    uid_t             uid;    /* user id                             */
++    int               ncpus;  /* number of cpus allocated to program */
++    int               nprocs; /* number of processes in program      */
++    struct proc_desc *pdescs; /* processes in this program           */
++    int               ncaps;  /* number of capabilities              */
++    struct cap_desc  *caps;   /* elan capabilities                   */
++    char             *corepath;       /* core path for parallel program      */
++    int               psid;   /* processor set id                    */
++
++    uint64_t       cutime;    /* user time accumulated by children   */
++    uint64_t       cstime;    /* system time accumulated by children */
++    uint64_t       start_time;        /* time program created                */
++    uint64_t       end_time;  /* time last process exited            */
++    uint64_t       sched_time;        /* last time job was scheduled         */
++    uint64_t       accum_atime;       /* allocated time last deschedule      */
++    uint64_t       memint;    /* accumulated memory integral         */
++    uint64_t       ebytes;    /* data transferred by the Elan(s)     */
++    uint64_t       exfers;    /* number of Elan data transfers       */
++    long           maxrss;    /* maximum size to date                */
++    long           majflt;
++    
++#ifdef LINUX
++    struct proc_dir_entry *proc_entry;
++#endif
++
++};
++
++#if defined(LINUX)
++static int rms_ptrack_callback (void *arg, int phase, struct task_struct *child);
++#else
++static void rms_xd_callback(void *arg, int phase, void *ctask);
++static void rms_xa_callback (void *arg, int phase, void *ctask);
++#endif
++
++static void prgsignal(struct prg_desc *program, int signo);
++static uint64_t gettime(void);
++static void freeProgram(struct prg_desc *program);
++
++static struct prg_desc *programs = 0;
++
++kmutex_t rms_lock;
++
++int rms_init(void)
++{
++    kmutex_init (&rms_lock);
++
++    DBG(printk("rms: initialising\n"));
++
++    return(ESUCCESS);
++}
++
++int rms_reconfigure(void)
++{
++    return(ESUCCESS);
++}
++
++int rms_programs_registered(void)
++{
++    /*
++    ** Called when trying to unload rms.mod will not succeed
++    ** if programs registered
++    */
++ 
++   struct prg_desc *program, **pp;
++
++   kmutex_lock(&rms_lock);
++
++   for (program = programs; program; program = program->next)
++   {
++      if (program->nprocs != 0)
++      {
++            kmutex_unlock(&rms_lock);
++            return(EBUSY);
++      }
++   }
++
++   /*
++   ** We have traversed the programs list and no processes registered
++   ** Now free the memory
++   */
++      
++    pp = &programs;
++    while ((program = *pp) != NULL)
++    {
++        *pp = program->next;
++        freeProgram(program);
++    }
++    kmutex_unlock(&rms_lock);
++   
++    return(ESUCCESS);
++
++}
++
++int rms_fini(void)
++{
++    /*
++     * don't allow an unload if there are programs registered
++     */
++    if (rms_programs_registered())
++        return(EBUSY);
++
++    kmutex_destroy (&rms_lock);
++
++    DBG(printk("rms: removed\n"));
++
++    return(ESUCCESS);
++}
++
++#ifdef LINUX
++
++extern struct proc_dir_entry *rms_procfs_programs;
++
++/*
++ * display one pid per line if there isn't enough space 
++ * for another pid then add "...\n" and stop 
++ */
++int pids_callback(char* page, char** start, off_t off, int count, int* eof, void* data)
++{
++    struct prg_desc *program = (struct prg_desc *)data;
++    struct proc_desc *pdesc;
++    char *ptr = page;
++    int bytes = 0, nb;
++
++    kmutex_lock(&rms_lock);
++    
++    for (pdesc = program->pdescs; pdesc; pdesc = pdesc->next)
++    {
++      if (bytes > count - 15)
++      {
++          bytes += sprintf(ptr,"...\n");
++          break;
++      }
++        nb = sprintf(ptr, "%d %d\n", pdesc->proc->p_pid, pdesc->vp);
++      bytes += nb;
++      ptr += nb;
++    }
++    kmutex_unlock(&rms_lock);
++    
++    return(bytes);
++}
++
++int status_callback(char* page, char** start, off_t off, int count, int* eof, void* data)
++{
++    struct prg_desc *program = (struct prg_desc *)data;
++    int bytes;
++    if (program->flags & PRG_KILLED)
++      bytes = sprintf(page, "killed\n");
++    else
++      bytes = sprintf(page, "running\n");
++    return(bytes);
++}
++
++void rms_create_proc_entry(struct prg_desc *program)
++{
++    struct proc_dir_entry *p;
++    char name[32];
++
++    if (rms_procfs_programs)
++    {
++      sprintf(name,"%d", program->id);
++      if ((program->proc_entry = proc_mkdir(name, rms_procfs_programs)) != NULL)
++      {
++          if ((p = create_proc_entry ("pids", S_IRUGO, program->proc_entry)) != NULL)
++          {
++              p->owner = THIS_MODULE;
++              p->data = program;
++              p->read_proc = pids_callback;
++          }
++          if ((p = create_proc_entry ("status", S_IRUGO, program->proc_entry)) != NULL)
++          {
++              p->owner = THIS_MODULE;
++              p->data = program;
++              p->read_proc = status_callback;
++          }
++      }
++    }
++}
++
++void rms_remove_proc_entry(struct prg_desc *program)
++{
++    char name[32];
++    if (rms_procfs_programs)
++    {
++      if (program->proc_entry)
++      {
++          remove_proc_entry ("pids", program->proc_entry);
++          remove_proc_entry ("status", program->proc_entry);
++      }
++      sprintf(name,"%d", program->id);
++      remove_proc_entry (name, rms_procfs_programs);
++    }
++}
++
++#endif
++
++/*
++ * find a program from its index/pid
++ *
++ * Duncan:  make the lookup more efficient for large numbers of programs/processes
++ */
++static struct prg_desc *findProgram(const int id)
++{
++    struct prg_desc *program;
++    for (program = programs; program; program = program->next)
++      if (program->id == id)
++          return(program);
++    return(0);
++}
++
++static struct proc_desc *findProcess(const int pid)
++{
++    struct prg_desc *program;
++    struct proc_desc *pdesc;
++    for (program = programs; program; program = program->next)
++      for (pdesc = program->pdescs; pdesc; pdesc = pdesc->next)
++          if (pdesc->proc->p_pid == pid)
++              return(pdesc);
++    return(0);
++}
++
++static void freeProgram(struct prg_desc *program)
++{
++    struct proc_desc *pdesc;
++    struct cap_desc *cdesc;
++
++#ifdef LINUX
++    rms_remove_proc_entry(program);
++#endif
++
++    while ((pdesc = program->pdescs) != NULL)
++    {
++      program->pdescs = pdesc->next;
++      KMEM_FREE(pdesc, sizeof(struct proc_desc));
++    }
++
++    while ((cdesc = program->caps) != NULL)
++    {
++      program->caps = cdesc->next;
++      KMEM_FREE(cdesc, sizeof(struct cap_desc));
++    }
++
++    if (program->corepath)
++      KMEM_FREE(program->corepath, MAXCOREPATHLEN + 1);
++
++    KMEM_FREE(program, sizeof(struct prg_desc));
++
++#ifdef LINUX
++    MOD_DEC_USE_COUNT;
++#endif
++}
++
++/*
++ * rms_prgcreate
++ *
++ * create a new program description
++ */
++int rms_prgcreate(int id, uid_t uid, int cpus)
++{
++    struct prg_desc *program;
++    struct proc_desc *pdesc;
++    
++    DBG(printk("rms_prgcreate :: program %d pid %d uid %d cpus %d\n", id, CURPROC()->p_pid, uid, cpus));
++    
++    /*
++     * parallel programs are created as root by the rmsd as it forks the loader
++     */
++    if (CURUID())
++      return(EACCES);
++    
++    /*
++     * program ids must be unique
++     */
++    kmutex_lock(&rms_lock);
++    program = findProgram(id);
++    kmutex_unlock(&rms_lock);
++    if (program)
++      return(EINVAL);
++
++    /*
++     * create a new program description
++     */
++    KMEM_ALLOC(program, struct prg_desc *, sizeof(struct prg_desc), TRUE);
++    if (!program)
++      return(ENOMEM);
++
++    program->id = id;
++    program->flags = PRG_RUNNING;
++    program->ncpus = cpus;
++    program->nprocs = 1;
++    program->uid = uid;
++    program->ncaps = 0;
++    program->caps = 0;
++    program->corepath = 0;
++    program->psid = 0;
++    program->start_time = program->sched_time = gettime();
++    program->end_time = 0;
++    program->accum_atime = 0;
++    program->cutime = 0;
++    program->cstime = 0;
++    program->maxrss = 0;
++    program->memint = 0;
++    program->majflt = 0;
++    program->ebytes = 0;
++    program->exfers = 0;
++
++    KMEM_ALLOC(pdesc, struct proc_desc *, sizeof(struct proc_desc), TRUE);
++    if (!pdesc)
++      return(ENOMEM);
++
++    pdesc->proc = CURPROC();
++    pdesc->next = 0;
++    pdesc->mycap = ELAN_CAP_UNINITIALISED;
++    pdesc->myctx = ELAN_CAP_UNINITIALISED;
++    pdesc->vp = -1;           /* rmsloader */
++    pdesc->program = program;
++    program->pdescs = pdesc;
++    
++#ifdef LINUX
++    rms_create_proc_entry(program);
++#endif
++    
++    kmutex_lock(&rms_lock);
++
++#if defined(LINUX)
++    if (ptrack_register (rms_ptrack_callback, NULL) != 0)
++    {
++      kmutex_unlock(&rms_lock);
++        KMEM_FREE(pdesc,sizeof(struct proc_desc));
++        KMEM_FREE(program,sizeof(struct prg_desc));
++      return(ENOMEM);
++    }
++#else
++    /*
++     * install a fork handler
++     */
++    if (HANDLER_REGISTER((void *)(unsigned long)rms_xa_callback, NULL, XA_FORK | XA_EXIT | XA_IOF | XA_KOF | XA_KOE) == NULL)
++    {
++      kmutex_unlock(&rms_lock);
++        KMEM_FREE(pdesc,sizeof(struct proc_desc));
++        KMEM_FREE(program,sizeof(struct prg_desc));
++      return(ENOMEM);
++    }
++#endif
++
++    program->next = programs;
++    programs = program;
++    
++#ifdef LINUX
++    MOD_INC_USE_COUNT;
++#endif
++    
++    kmutex_unlock(&rms_lock);
++    return(ESUCCESS);
++}
++
++
++/*
++ * rms_prgdestroy
++ *
++ * destroy a program description
++ */
++int rms_prgdestroy(int id)
++{
++    struct prg_desc *program, **pp;
++    int status = ESRCH;
++
++    /*
++     * parallel programs are created and destroyed by the rmsd
++     */
++    if (CURUID())
++      return(EACCES);
++
++    kmutex_lock(&rms_lock);
++    
++    pp = &programs;
++    while ((program = *pp) != NULL)
++    {
++      if (program->id == id)
++      {
++          if (program->nprocs == 0)
++          {
++              DBG(printk("rms_prgdestro :: removing program %d\n", program->id));
++              *pp = program->next;
++              freeProgram(program);
++              status = ESUCCESS;
++          }
++          else
++          {
++              DBG(printk("rms_prgdestro :: failed to remove program %d: %d\n", program->id, program->nprocs));
++              status = ECHILD;
++              pp = &program->next;
++          }
++      }
++      else
++          pp = &program->next;
++    }
++    
++    kmutex_unlock(&rms_lock);
++    return(status);
++}
++
++/*
++ * rms_prgids
++ */
++int rms_prgids(int maxids, int *prgids, int *nprgs)
++{
++    struct prg_desc *program;
++    int count = 0, *buf, *bufp;
++    int status = ESUCCESS;
++
++    if (maxids < 1)
++        return(EINVAL);
++
++    kmutex_lock(&rms_lock);
++
++    for (program = programs; program; program = program->next)
++        count++;
++    count = MIN(count, maxids);
++
++    if (count > 0)
++    {
++        KMEM_ALLOC(buf, int *, count * sizeof(int), TRUE);
++      if (buf)
++      {                  
++          for (program = programs, bufp=buf; bufp < buf + count; 
++               program = program->next)
++              *bufp++ = program->id;
++      
++          if (copyout(buf, prgids, sizeof(int) * count))
++              status = EFAULT;
++
++          KMEM_FREE(buf, count * sizeof(int));
++      }
++      else
++          status = ENOMEM;
++    }
++    
++    if (copyout(&count, nprgs, sizeof(int)))
++      status = EFAULT;
++
++    kmutex_unlock(&rms_lock);
++    
++    return(status);
++}
++
++/*
++ * rms_prginfo
++ */
++int rms_prginfo(int id, int maxpids, pid_t *pids, int *nprocs)
++{
++    struct prg_desc *program;
++    struct proc_desc *pdesc;
++    pid_t *pidp, *buf;
++    int status = ESUCCESS;
++
++    kmutex_lock(&rms_lock);
++
++    if ((program = findProgram(id)) != NULL)
++    {
++      if (program->nprocs > 0)
++      {
++          KMEM_ALLOC(buf, pid_t *, program->nprocs * sizeof(pid_t), TRUE);
++          if (buf)
++          {
++              for (pidp = buf, pdesc = program->pdescs; pdesc; pdesc = pdesc->next)
++                  *pidp++ = pdesc->proc->p_pid;
++              
++              if (copyout(buf, pids, sizeof(pid_t) * MIN(program->nprocs, maxpids)))
++                  status = EFAULT;
++              
++              KMEM_FREE(buf, program->nprocs * sizeof(pid_t));
++          }
++          else
++              status = ENOMEM;
++      }
++      
++      if (copyout(&program->nprocs, nprocs, sizeof(int)))
++          status = EFAULT;
++    }
++    else
++      status = ESRCH;
++
++    kmutex_unlock(&rms_lock);
++    
++    return(status);
++}
++
++/*
++ * rmsmod always used to use psignal but this doesn't work
++ * on Linux 2.6.7 so we have changed to kill_proc
++ */
++static void prgsignal(struct prg_desc *program, int signo)
++{
++    struct proc_desc *pdesc;
++    for (pdesc = program->pdescs; pdesc; pdesc = pdesc->next)
++      kill_proc(pdesc->proc->p_pid, signo, 1);
++}
++
++
++int rms_prgsignal(int id, int signo)
++{
++    struct prg_desc *program;
++    int status = ESUCCESS;
++    
++    kmutex_lock(&rms_lock);
++    
++    if ((program = findProgram(id)) != NULL)
++    {
++      if (CURUID() == 0 || CURUID() == program->uid)
++      {
++          prgsignal(program, signo);
++          if (signo == SIGKILL)
++              program->flags |= PRG_KILLED;
++      }
++      else
++          status = EACCES;
++    }
++    else
++      status = ESRCH;
++    
++    kmutex_unlock(&rms_lock);
++    
++    return(status);
++}
++
++int rms_prgaddcap(int id, int index, ELAN_CAPABILITY *cap)
++{
++    struct prg_desc *program;
++    struct cap_desc *cdesc;
++    int status = ESUCCESS;
++
++    if (cap == NULL)
++        return(EINVAL);
++
++    kmutex_lock(&rms_lock);
++    if ((program = findProgram(id)) != NULL)
++    {
++      KMEM_ALLOC(cdesc, struct cap_desc *, sizeof(struct cap_desc), TRUE);
++      if (cdesc)
++      {
++          cdesc->index = index;
++          if (copyin(cap, &cdesc->cap, sizeof(ELAN_CAPABILITY)))
++          {
++              KMEM_FREE(cdesc, sizeof(struct cap_desc));
++              status = EFAULT;
++          }
++          else
++          {
++              DBG(printk("rms_prgaddcap :: program %d index %d context %d<-->%d\n",
++                         program->id, index, cdesc->cap.cap_lowcontext, cdesc->cap.cap_highcontext));
++              cdesc->next = program->caps;
++              program->caps = cdesc;
++              program->ncaps++;
++          }
++      }
++      else
++          status = ENOMEM;
++    }
++    else
++      status = ESRCH;
++
++    kmutex_unlock(&rms_lock);
++    return(status);
++}
++
++static uint64_t gettime(void)
++{
++    uint64_t now;
++
++#if defined(SOLARIS)
++    timespec_t tv;
++    gethrestime(&tv);
++    now = tv.tv_sec * 1000 + tv.tv_nsec / 1000000;
++#elif defined(LINUX)
++    struct timeval tv;
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,17)
++    get_fast_time(&tv);
++#else
++    do_gettimeofday(&tv);
++#endif
++    now = tv.tv_sec * 1000 + tv.tv_usec / 1000;
++#elif defined(DIGITAL_UNIX)
++    struct timeval tv;
++    microtime(&tv);
++    now = tv.tv_sec * 1000 + tv.tv_usec / 1000;
++#endif
++
++    return(now);
++}
++
++#ifdef DIGITAL_UNIX
++
++int rms_getrusage(struct proc_desc *pdesc, struct rusage *ru)
++{
++    task_t   task;
++    thread_t thread;
++    
++    if (!pdesc->proc)
++      return(-1);
++    
++    /*
++     * locking required unless called from the current proc
++     */
++    if (pdesc->proc != CURPROC())
++    {
++      if (!P_REF(pdesc->proc))
++          return(-1);
++      
++      task = proc_to_task(pdesc->proc);
++      if (!task) 
++      {
++          P_UNREF(pdesc->proc);
++          DBG(printk("rms_getrusage :: process (%d) has no task\n", pdesc->proc->p_pid));
++          return(-1);
++      }
++
++      task_reference(task);
++      task_lock(task);
++      
++      if (!queue_empty(&task->thread_list))
++          thread = (thread_t) queue_first(&task->thread_list);
++      else 
++      {
++          task_unlock(task);
++          task_deallocate(task);
++          P_UNREF(pdesc->proc);
++          return(-1);
++      }
++      
++      thread_reference(thread);
++      task_unlock(task);
++    }
++    
++    *ru = proc_to_utask(pdesc->proc)->uu_ru;
++    task_get_rusage(ru, proc_to_task(pdesc->proc));
++    
++    if (pdesc->proc != CURPROC())
++    {
++      task_deallocate(task);
++      thread_deallocate(thread);
++      P_UNREF(pdesc->proc);
++    }
++    return(0);
++}
++
++#endif
++
++/*
++ * new stats collection interface, 64-bit with addition of Elan stats
++ */
++int rms_prggetstats(int id, prgstats_t *stats)
++{
++#ifdef DIGITAL_UNIX
++    long ruixrss, ruidrss, ruisrss, rumaxrss, rumajflt;
++#endif
++    struct prg_desc *program = 0;
++    struct proc_desc *pdesc;
++    int status = ESUCCESS;
++    prgstats_t totals;
++    uint64_t now = gettime();
++#if defined(SOLARIS)
++    clock_t utime, stime;
++#elif defined(LINUX)
++    uint64_t utime, stime;
++#endif
++
++    long maxrss;
++
++    kmutex_lock(&rms_lock);
++    
++    if (id < 0)
++    {
++      if ((pdesc = findProcess(CURPROC()->p_pid)) != NULL)
++          program = pdesc->program;
++    }
++    else
++      program = findProgram(id);
++
++    if (program)
++    {
++      if (CURUID() == 0 || CURUID() == program->uid)
++      {
++          totals.flags = program->flags;
++          totals.ncpus = program->ncpus;
++          maxrss = 0;
++
++          if (program->nprocs > 0)
++              totals.etime = now - program->start_time;
++          else
++              totals.etime = program->end_time - program->start_time;
++          
++          totals.atime = program->accum_atime;
++          if (program->flags & PRG_RUNNING)
++              totals.atime += program->ncpus * (now - program->sched_time);
++          
++#if defined(SOLARIS)
++          utime = stime = 0;
++          for (pdesc = program->pdescs; pdesc; pdesc = pdesc->next)
++          {
++              utime += pdesc->proc->p_utime;
++              stime += pdesc->proc->p_stime;
++          }
++          totals.utime = TICK_TO_MSEC(utime);
++          totals.stime = TICK_TO_MSEC(stime);
++
++#elif defined(LINUX)
++          utime = stime = 0;
++          totals.memint = program->memint;
++          totals.pageflts = program->majflt;
++
++          for (pdesc = program->pdescs; pdesc; pdesc = pdesc->next)
++          {
++#ifdef  PROCESS_ACCT
++      DBG(printk("rms_prggetsta :: process %d utime %ld clks stime %ld clks\n", 
++                              pdesc->proc->p_pid, TIMEVAL_TO_CT(&pdesc->proc->utime),
++                              TIMEVAL_TO_CT(&pdesc->proc->stime)));                 
++      utime += TIMEVAL_TO_CT(&pdesc->proc->utime);                  
++      stime += TIMEVAL_TO_CT(&pdesc->proc->stime);                  
++#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
++              DBG(printk("rms_prggetsta :: process %d utime %ld clks stime %ld clks\n", 
++                         pdesc->proc->p_pid, pdesc->proc->times.tms_utime, 
++                         pdesc->proc->times.tms_stime));
++              utime += pdesc->proc->times.tms_utime;
++              stime += pdesc->proc->times.tms_stime;
++#else
++              DBG(printk("rms_prggetsta :: process %d utime %ld clks stime %ld clks\n", 
++                         pdesc->proc->p_pid, pdesc->proc->utime, pdesc->proc->stime));
++              utime += pdesc->proc->utime;
++              stime += pdesc->proc->stime;
++#endif
++
++              totals.pageflts += pdesc->proc->maj_flt; 
++
++              maxrss += PROC_RSS(pdesc->proc) >> (20 - PAGE_SHIFT);
++          }
++
++          /* convert user and system times to millisecs */
++          totals.utime = CT_TO_MSEC(utime);
++          totals.stime = CT_TO_MSEC(stime);
++          
++#elif defined(DIGITAL_UNIX)
++          totals.utime = totals.stime = 0;
++          totals.memint = program->memint;
++          totals.pageflts = program->majflt;
++
++          for (pdesc = program->pdescs; pdesc; pdesc = pdesc->next)
++          {
++              struct rusage ru;
++              if (rms_getrusage(pdesc, &ru) < 0)
++                  continue;
++              
++              totals.utime += TIMEVAL_TO_MSEC(&ru.ru_utime);
++              totals.stime += TIMEVAL_TO_MSEC(&ru.ru_stime);
++              
++              /* convert maxrss to megabytes */
++              rumaxrss = ru.ru_maxrss >> 10;
++              rumajflt = ru.ru_majflt;
++              totals.pageflts += rumajflt;
++              
++              /*
++               * memory intergals are still broken in 5.1
++               */
++              
++#ifdef FIXED_MEMINIT
++              
++              /* convert from pages * clock ticks to Mbytes * secs */
++              ruixrss = (ru.ru_ixrss >> (20 - PAGE_SHIFT)) / hz;
++              ruidrss = (ru.ru_idrss >> (20 - PAGE_SHIFT)) / hz;
++              ruisrss = (ru.ru_isrss >> (20 - PAGE_SHIFT)) / hz;
++              
++              DBG(printk("rms_prggetsta :: process %d mem %d int %d %d %d flt %d\n", pdesc->proc->p_pid, 
++                         rumaxrss, ruixrss, ruidrss, ruisrss, rumajflt));
++              
++              totals.memint += ruixrss + ruidrss + ruisrss;
++#else
++              DBG(printk("rms_prggetsta :: process %d mem %d flt %d\n", pdesc->proc->p_pid, rumaxrss, rumajflt));
++              totals.memint = 0;
++#endif
++              maxrss += rumaxrss;
++          }
++#endif /* DIGITAL_UNIX */
++
++          if (maxrss > program->maxrss)
++              program->maxrss = maxrss;
++          
++          totals.utime += program->cutime;
++          totals.stime += program->cstime;
++          totals.mem = program->maxrss;
++          totals.ebytes = program->ebytes;
++          totals.exfers = program->exfers;
++
++          DBG(printk("rms_prggetsta :: program %d mem %d flt %d\n", program->id, totals.mem, totals.pageflts));
++          
++          if (copyout(&totals, stats, sizeof(prgstats_t)))
++              status = EFAULT;
++      }
++      else
++          status = EACCES;
++    }
++    else
++      status = ESRCH;
++    
++    kmutex_unlock(&rms_lock);
++    return(status);
++}
++
++/*
++ * preserve the old stats stats collection interface
++ */
++
++int rms_prggetoldstats(int id, prgstats_old_t *stats)
++{
++#ifdef DIGITAL_UNIX
++    long ruixrss, ruidrss, ruisrss, rumaxrss, rumajflt;
++#endif
++    struct prg_desc *program = 0;
++    struct proc_desc *pdesc;
++    int status = ESUCCESS;
++    prgstats_old_t totals;
++    uint64_t now = gettime();
++#if defined(SOLARIS) || defined(LINUX)
++    clock_t utime, stime;
++#endif
++    long maxrss;
++
++    kmutex_lock(&rms_lock);
++    
++    if (id < 0)
++    {
++      if ((pdesc = findProcess(CURPROC()->p_pid)) != NULL)
++          program = pdesc->program;
++    }
++    else
++      program = findProgram(id);
++
++    if (program)
++    {
++      if (CURUID() == 0 || CURUID() == program->uid)
++      {
++          totals.flags = program->flags;
++          totals.ncpus = program->ncpus;
++          maxrss = 0;
++
++          if (program->nprocs > 0)
++              totals.etime = now - program->start_time;
++          else
++              totals.etime = program->end_time - program->start_time;
++          
++          totals.atime = program->accum_atime;
++          if (program->flags & PRG_RUNNING)
++              totals.atime += program->ncpus * (now - program->sched_time);
++          
++#if defined(SOLARIS)
++          utime = stime = 0;
++          for (pdesc = program->pdescs; pdesc; pdesc = pdesc->next)
++          {
++              utime += pdesc->proc->p_utime;
++              stime += pdesc->proc->p_stime;
++          }
++          totals.utime = TICK_TO_MSEC(utime);
++          totals.stime = TICK_TO_MSEC(stime);
++
++#elif defined(LINUX)
++          utime = stime = 0;
++          totals.memint = program->memint;
++          totals.pageflts = program->majflt;
++
++          for (pdesc = program->pdescs; pdesc; pdesc = pdesc->next)
++          {
++#ifdef  PROCESS_ACCT
++      DBG(printk("rms_getoldsta :: process %d utime %ld clks stime %ld clks\n", 
++                              pdesc->proc->p_pid, TIMEVAL_TO_CT(&pdesc->proc->utime),
++                              TIMEVAL_TO_CT(&pdesc->proc->stime)));                 
++      utime += TIMEVAL_TO_CT(&pdesc->proc->utime);                  
++      stime += TIMEVAL_TO_CT(&pdesc->proc->stime);                  
++#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
++              DBG(printk("rms_getoldsta :: process %d utime %ld clks stime %ld clks\n", 
++                         pdesc->proc->p_pid, pdesc->proc->times.tms_utime, 
++                         pdesc->proc->times.tms_stime));
++              utime += pdesc->proc->times.tms_utime;
++              stime += pdesc->proc->times.tms_stime;
++#else
++              DBG(printk("rms_getoldsta :: process %d utime %ld clks stime %ld clks\n", 
++                         pdesc->proc->p_pid, pdesc->proc->utime, pdesc->proc->stime));
++              utime += pdesc->proc->utime;
++              stime += pdesc->proc->stime;
++#endif
++
++              totals.pageflts += pdesc->proc->maj_flt; 
++              maxrss += PROC_RSS(pdesc->proc) >> (20 - PAGE_SHIFT);
++          }
++
++          /* convert user and system times to millisecs */
++          totals.utime = CT_TO_MSEC(utime);
++          totals.stime = CT_TO_MSEC(stime);
++          
++#elif defined(DIGITAL_UNIX)
++          totals.utime = totals.stime = 0;
++          totals.memint = program->memint;
++          totals.pageflts = program->majflt;
++
++          for (pdesc = program->pdescs; pdesc; pdesc = pdesc->next)
++          {
++              struct rusage ru;
++              if (rms_getrusage(pdesc, &ru) < 0)
++                  continue;
++              
++              totals.utime += TIMEVAL_TO_MSEC(&ru.ru_utime);
++              totals.stime += TIMEVAL_TO_MSEC(&ru.ru_stime);
++              
++              /* convert maxrss to megabytes */
++              rumaxrss = ru.ru_maxrss >> 10;
++              rumajflt = ru.ru_majflt;
++              totals.pageflts += rumajflt;
++              
++              /*
++               * memory intergals are still broken in 5.1
++               */
++              
++#ifdef FIXED_MEMINIT
++              
++              /* convert from pages * clock ticks to Mbytes * secs */
++              ruixrss = (ru.ru_ixrss >> (20 - PAGE_SHIFT)) / hz;
++              ruidrss = (ru.ru_idrss >> (20 - PAGE_SHIFT)) / hz;
++              ruisrss = (ru.ru_isrss >> (20 - PAGE_SHIFT)) / hz;
++              
++              DBG(printk("rms_getoldsta :: process %d mem %d int %d %d %d flt %d\n", pdesc->proc->p_pid, 
++                         rumaxrss, ruixrss, ruidrss, ruisrss, rumajflt));
++              
++              totals.memint += ruixrss + ruidrss + ruisrss;
++#else
++              DBG(printk("rms_getoldsta :: process %d mem %d flt %d\n", pdesc->proc->p_pid, rumaxrss, rumajflt));
++              totals.memint = 0;
++#endif
++              maxrss += rumaxrss;
++          }
++#endif /* DIGITAL_UNIX */
++
++          if (maxrss > program->maxrss)
++              program->maxrss = maxrss;
++          
++          totals.utime += program->cutime;
++          totals.stime += program->cstime;
++          totals.mem = program->maxrss;
++          
++          DBG(printk("rms_getoldsta :: program %d mem %d flt %d\n", program->id, totals.mem, totals.pageflts));
++          
++          if (copyout(&totals, stats, sizeof(prgstats_old_t)))
++              status = EFAULT;
++      }
++      else
++          status = EACCES;
++    }
++    else
++      status = ESRCH;
++    
++    kmutex_unlock(&rms_lock);
++    return(status);
++}
++
++
++int rms_prgsuspend(int id)
++{
++    struct prg_desc *program;
++    int status = ESUCCESS;
++
++    kmutex_lock(&rms_lock);
++    
++    if ((program = findProgram(id)) != NULL)
++    {
++      if (CURUID() == 0 || CURUID() == program->uid)
++      {
++          program->flags &= ~PRG_RUNNING;
++          program->flags |=  PRG_SUSPEND;
++          program->accum_atime += program->ncpus * (gettime() - program->sched_time);
++
++          /* suspend/resume just use signals for now */
++          prgsignal(program, SIGSTOP);
++      }
++      else
++          status = EACCES;
++    }
++    else
++      status = ESRCH;
++
++    kmutex_unlock(&rms_lock);
++    return(status);
++}
++
++int rms_prgresume(int id)
++{
++    struct prg_desc *program;
++    int status = ESUCCESS;
++
++    kmutex_lock(&rms_lock);
++    
++    if ((program = findProgram(id)) != NULL)
++    {
++      if (CURUID() == 0 || CURUID() == program->uid)
++      {
++          program->flags &= ~PRG_SUSPEND;
++          program->flags |=  PRG_RUNNING;
++          program->sched_time = gettime();
++          prgsignal(program, SIGCONT);
++      }
++      else
++          status = EACCES;
++    }
++    else
++      status = ESRCH;
++
++    kmutex_unlock(&rms_lock);
++    return(status);
++}
++
++
++int rms_ncaps(int *ncaps)
++{
++    struct proc_desc *pdesc;
++    int status = ESUCCESS;
++    
++    kmutex_lock(&rms_lock);
++    if ((pdesc = findProcess(CURPROC()->p_pid)) != NULL)
++    {
++      if (copyout(&pdesc->program->ncaps, ncaps, sizeof(int)))
++          status = EFAULT;
++    }
++    else
++      status = ESRCH;
++
++    kmutex_unlock(&rms_lock);
++    return(status);
++}
++
++int rms_getprgid(pid_t pid, int *id)
++{
++    struct proc_desc *pdesc;
++    int status = ESUCCESS;
++    
++    if (pid == 0)
++      pid = CURPROC()->p_pid;
++    
++    kmutex_lock(&rms_lock);
++    if ((pdesc = findProcess(pid)) != NULL)
++    {
++      if (copyout(&pdesc->program->id, id, sizeof(int)))
++          status = EFAULT;
++    }
++    else
++      status = ESRCH;
++
++    kmutex_unlock(&rms_lock);
++    return(status);
++}
++
++int rms_setcap(int index, int ctx)
++{
++    struct proc_desc *pdesc;
++    struct cap_desc *cdesc;
++    int status = EINVAL;
++    
++    DBG(printk("rms_setcap    :: process %d cap %d ctx %d\n",CURPROC()->p_pid,index,ctx));
++
++    kmutex_lock(&rms_lock);
++    if ((pdesc = findProcess(CURPROC()->p_pid)) != NULL)
++    {
++      for (cdesc = pdesc->program->caps; cdesc; cdesc = cdesc->next)
++          if (cdesc->index == index && 0 <= ctx && ctx <= (cdesc->cap.cap_highcontext - cdesc->cap.cap_lowcontext + 1))
++          {
++              pdesc->mycap = index;
++              pdesc->myctx = cdesc->cap.cap_lowcontext + ctx;
++              status = ESUCCESS;
++          }
++    }
++    else
++      status = ESRCH;
++
++    kmutex_unlock(&rms_lock);
++    return(status);
++}
++
++
++int rms_mycap(int *index)
++{
++    struct proc_desc *pdesc;
++    int status = ESUCCESS;
++    
++    DBG(printk("rms_mycap :: process %d\n", CURPROC()->p_pid));
++    
++    kmutex_lock(&rms_lock);
++    if ((pdesc = findProcess(CURPROC()->p_pid)) != NULL)
++    {
++      DBG(printk("rms_mycap :: found process %d mycap = %d\n", CURPROC()->p_pid, pdesc->mycap));
++      if (copyout(&pdesc->mycap, index, sizeof(int)))
++          status = EFAULT;
++    }
++    else
++      status = ESRCH;
++
++    kmutex_unlock(&rms_lock);
++    return(status);
++}
++
++int rms_getcap(int index, ELAN_CAPABILITY *cap)
++{
++    struct proc_desc *pdesc;
++    struct cap_desc *cdesc;
++    int status = ESUCCESS;
++    
++    kmutex_lock(&rms_lock);
++    if ((pdesc = findProcess(CURPROC()->p_pid)) != NULL)
++    {
++      for (cdesc = pdesc->program->caps; cdesc; cdesc = cdesc->next)
++          if (cdesc->index == index)
++              break;
++      
++      if (cdesc)
++      {
++          /* tell each process about its own context */
++          cdesc->cap.cap_mycontext = pdesc->myctx;
++          
++          if (copyout(&cdesc->cap, cap, ELAN_CAP_SIZE(&cdesc->cap)))
++              status = EFAULT;
++          
++          DBG(printk("rms_getcap    :: program %d index %d context %d<-->%d\n", pdesc->program->id, 
++                     cdesc->index, cdesc->cap.cap_lowcontext, cdesc->cap.cap_highcontext));
++      }
++      else
++          status = EINVAL;
++    }
++    else
++      status = ESRCH;
++    
++    kmutex_unlock(&rms_lock);
++    return(status);
++}
++
++
++static int
++rms_fork_callback (struct PROC_STRUCT *curproc, struct PROC_STRUCT *child)
++{
++    struct prg_desc *program;
++    struct proc_desc *parent;
++    struct proc_desc *pdesc = NULL;
++
++    kmutex_lock(&rms_lock);
++    
++    DBG(printk("rms_fork_func :: phase is fork pid %d child %d\n", curproc->p_pid, child->p_pid));
++
++    /*
++     * find the process that forked
++     */
++    if ((parent = findProcess(curproc->p_pid)) != NULL)
++    {
++      program = parent->program;
++      
++      DBG(printk("rms_fork_func :: program is %d flags %d\n", program->id, program->flags));
++      
++      /*
++       * processes can be blocked in fork while prgsignal is in progress
++       * so check to see if the PRG_KILLED flag is set
++       */
++      if (program->flags & PRG_KILLED)
++          DBG(printk("rms_fork_func :: fork handler called after program killed\n"));
++      else
++      {
++          /*
++           * create a new process description and add to program
++           */
++          KMEM_ALLOC(pdesc, struct proc_desc *, sizeof(struct proc_desc), TRUE);
++          if (pdesc)
++          {
++              pdesc->next = program->pdescs;
++              program->pdescs = pdesc;
++              pdesc->proc = child;
++              pdesc->mycap = parent->mycap;
++              pdesc->myctx = parent->myctx;
++              pdesc->program = program;
++              pdesc->vp = -1;              /* assigned by elaninitdone */
++              program->nprocs++;
++          }
++          else
++              printk("rms_fork_func :: memory allocation failed\n");
++      }
++    }
++    else
++      DBG(printk("rms_fork_func :: no program\n"));
++    
++    kmutex_unlock (&rms_lock);
++
++    return pdesc == NULL;
++}
++
++static void
++rms_exit_callback (struct PROC_STRUCT *curproc)
++{
++    struct prg_desc *program;
++    struct proc_desc *pdesc, **pdescp, *p;
++#ifdef DIGITAL_UNIX
++    struct rusage ru;
++#endif
++    long maxrss;
++
++    kmutex_lock(&rms_lock);
++    
++    DBG(printk("rms_exit_func :: process %d exiting\n", curproc->p_pid));
++
++    /*
++     * find the process that exited and accumulate 
++     * resource usage in its parent program
++     */
++    for (program = programs, pdesc = 0; program && !pdesc; program = program->next)
++    {
++      pdescp = &program->pdescs;
++      while ((pdesc = *pdescp) != NULL)
++      {
++          if (pdesc->proc->p_pid == curproc->p_pid)
++          {
++              /*
++               * keep track of the resources used
++               */
++#if defined(SOLARIS)
++              program->cutime += TICK_TO_MSEC(pdesc->proc->p_utime);
++              program->cstime += TICK_TO_MSEC(pdesc->proc->p_stime);
++              
++#elif defined(LINUX)
++#ifdef PROCESS_ACCT
++      DBG(printk("rms_exit_func :: process %d exit utime %ld clks stime %ld clks\n",
++                              pdesc->proc->p_pid,
++                              TIMEVAL_TO_CT(&pdesc->proc->utime),                
++                              TIMEVAL_TO_CT(&pdesc->proc->stime)));              
++      program->cutime += TIMEVAL_TO_MSEC(&pdesc->proc->utime);      
++      program->cstime += TIMEVAL_TO_MSEC(&pdesc->proc->stime);      
++#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)                           
++              DBG(printk("rms_exit_func :: process %d exit utime %ld clks stime %ld clks\n", 
++                         pdesc->proc->p_pid, pdesc->proc->times.tms_utime, 
++                         pdesc->proc->times.tms_stime));
++              
++              program->cutime += CT_TO_MSEC(pdesc->proc->times.tms_utime);
++              program->cstime += CT_TO_MSEC(pdesc->proc->times.tms_stime);
++#else
++              DBG(printk("rms_exit_func :: process %d exit utime %ld clks stime %ld clks\n", 
++                         pdesc->proc->p_pid, pdesc->proc->utime, pdesc->proc->stime));
++              
++              program->cutime += CT_TO_MSEC(pdesc->proc->utime);
++              program->cstime += CT_TO_MSEC(pdesc->proc->stime);
++#endif
++              program->majflt += pdesc->proc->maj_flt;
++              maxrss = PROC_RSS(pdesc->proc) >> (20 - PAGE_SHIFT);
++              
++#elif defined(DIGITAL_UNIX)
++              if (rms_getrusage(pdesc, &ru) == 0)
++              {
++                  program->cutime += TIMEVAL_TO_MSEC(&ru.ru_utime);
++                  program->cstime += TIMEVAL_TO_MSEC(&ru.ru_stime);
++                  program->majflt += ru.ru_majflt;
++                  
++                  /* convert maxrss to megabytes */
++                  maxrss = ru.ru_maxrss >> 10;
++              }
++#endif
++              
++              /*
++               * shared memory segment cleanup
++               */
++#if defined(DIGITAL_UNIX)
++              rms_shmcleanup(-1);
++#elif defined(LINUX)
++              shm_cleanup();
++#endif 
++              
++              /* 
++               * remove process from program 
++               */
++              *pdescp = pdesc->next;
++              KMEM_FREE(pdesc, sizeof(struct proc_desc));
++              program->nprocs--;
++              
++              /*
++               * update the memory high water mark for the program
++               */
++              for (p = program->pdescs; p; p = p->next)
++              {
++#if defined(DIGITAL_UNIX)
++                  if (rms_getrusage(p, &ru) < 0)
++                      continue;
++                  
++                  /* convert maxrss to megabytes */
++                  maxrss += ru.ru_maxrss >> 10;
++                  
++#elif defined(LINUX)                  
++                  maxrss += PROC_RSS(p->proc) >> (20 - PAGE_SHIFT);
++#endif
++              }
++              if (maxrss > program->maxrss)
++                  program->maxrss = maxrss;
++              
++              DBG(printk("rms_exit_func :: program %d procs %d mem %ld\n", program->id, program->nprocs, program->maxrss));
++              
++              /*
++               * final update to the program if this is the last process
++               */
++              if (program->nprocs == 0)
++              {
++                  program->end_time = gettime();
++                  program->flags &= ~PRG_RUNNING;
++                  program->accum_atime += program->ncpus * (program->end_time - program->sched_time);
++                  DBG(printk("rms_exit_func :: last process has gone\n"));
++              }
++              break;
++          }
++          else
++              pdescp = &pdesc->next;
++      }
++    }
++    kmutex_unlock  (&rms_lock);
++}
++
++#if defined(LINUX)
++static int
++rms_ptrack_callback (void *arg, int phase, struct task_struct *child)
++{
++    switch (phase)
++    {
++    case PTRACK_PHASE_CLONE:
++      if (rms_fork_callback (current, child))
++          return PTRACK_DENIED;
++      else
++          return PTRACK_INNHERIT;
++
++    case PTRACK_PHASE_CLONE_FAIL:
++      DBG(printk("rms_fork_func :: fork failed pid %d child %d\n", current->p_pid, child->p_pid));
++      rms_exit_callback(child);
++      break;
++
++    case PTRACK_PHASE_EXIT:
++      rms_exit_callback(current);
++      break;
++    }
++    return PTRACK_FINISHED;
++}
++
++#else
++
++static void
++rms_xa_callback (void *arg, int phase, void *ctask)
++{
++    switch (phase)
++    {
++    case XA_FORK:
++      if (rms_fork_callback (CURPROC(), (struct PROC_STRUCT *)task_to_proc(ctask)))
++          psignal(task_to_proc(ctask), SIGKILL);
++      break;
++    case XA_EXIT:
++      rms_exit_callback (CURPROC());
++      break;
++    }
++}
++
++#endif
++
++#ifdef DIGITAL_UNIX
++
++/*
++ * NB: These functions will only work on steelos.
++ */
++
++/*
++ * rms_setcorepath
++ *
++ * set a path at which to dump core if the task aborts  
++ *
++ * enhanced core file names must be enabled for this to work
++ */
++int rms_setcorepath(char *corepath)
++{
++    int    length;
++    char  *path;
++    int    status; 
++    struct proc_desc *pdesc;
++    
++    /* 
++     * access restricted - we don't want users moving
++     * their corepath and generating a huge I/O load
++     */
++    if (CURUID())
++      return(EACCES);
++    
++    if (!(pdesc = findProcess(CURPROC()->p_pid)))
++      return(ESRCH);
++    
++    if (pdesc->program->corepath)
++      return(EEXIST);
++    
++    KMEM_ALLOC(path, char *, MAXCOREPATHLEN + 1, TRUE);
++    if (path == 0)
++      return(ENOMEM);
++    
++    if (copyinstr(corepath, path, MAXCOREPATHLEN, &length))
++      return(EFAULT);
++    
++    path[length] = 0;
++    status = add_corepath(path);
++    
++    DBG(printk("rms_setcorepa :: id %d corepath %s status %d\n", pdesc->program->id, path, status));
++    
++    if (status == ESUCCESS)
++      pdesc->program->corepath = path;
++    else
++      KMEM_FREE(path, MAXCOREPATHLEN + 1);
++    
++    return(status);
++}
++
++static int find_corepath(pid_t pid, char *path, int len)
++{
++    struct proc *procp;
++    struct utask *utask;
++    int status = ESUCCESS;
++
++    procp = pfind(pid);
++    if (procp == NULL)
++        return(ENOENT);
++    
++    utask = proc_to_utask(procp);
++    
++    if (utask->uu_coredir)
++        bcopy(utask->uu_coredir,path,len);
++    else
++        status = ENOENT;
++    
++    /* pfind takes out a reference */
++    P_UNREF(procp);
++
++    return(status);
++}
++
++int rms_getcorepath(pid_t pid, char *corepath, int maxlen)
++{
++    char src[MAXCOREPATHLEN];
++    int len;
++    int status;
++    
++    if (maxlen < 2)
++      return(EINVAL);
++    
++    len = MIN(maxlen, MAXCOREPATHLEN);
++    
++    status = find_corepath(pid, src, len);
++    
++    if (status == ESUCCESS)
++        len = strlen(src)+1;
++    else if (status == ENOENT) 
++    {
++      len = 2;
++      src[0] = '.';
++        src[1] = '\0';
++        status = ESUCCESS;
++    }
++    
++    if (copyout(src, corepath, len))
++      return(EFAULT);
++    
++    return(status);
++}
++
++#endif
++
++/*
++ * rms_elaninitdone - mark a process as having successfully completed elan initialisation
++ */
++int rms_elaninitdone(int vp)
++{
++    int status = ESUCCESS;
++    struct proc_desc *pdesc;
++    
++    DBG(printk("rms_elaninit  :: process %d vp %d\n", CURPROC()->p_pid, vp));
++    
++    kmutex_lock(&rms_lock);
++    if ((pdesc = findProcess(CURPROC()->p_pid)) != NULL)
++      pdesc->vp = vp;
++    else
++      status = ESRCH;
++    kmutex_unlock(&rms_lock);
++    return(status);
++}
++
++
++/*
++ * rms_prgelanpids - return the ids of processes that have completed elan initialisation
++ */
++int rms_prgelanpids(int id, int maxpids, int *vps, pid_t *pids, int *npids)
++{
++    struct prg_desc *program;
++    struct proc_desc *pdesc;
++    pid_t *pidbuf;
++    int status = ESUCCESS, count = 0, *vpbuf;
++    
++    DBG(printk("rms_elanpids  :: process %d id %d\n", CURPROC()->p_pid, id));
++    
++    kmutex_lock(&rms_lock);
++    
++    if ((program = findProgram(id)) != NULL)
++    {
++      if (program->nprocs > 0)
++      {
++          KMEM_ALLOC(pidbuf, pid_t *, program->nprocs * sizeof(pid_t), TRUE);
++          KMEM_ALLOC(vpbuf, int *, program->nprocs * sizeof(int), TRUE);
++          if (pidbuf && vpbuf)
++          {
++              for (pdesc = program->pdescs; pdesc; pdesc = pdesc->next)
++                  if (pdesc->vp >= 0)
++                  {
++                      pidbuf[count] = pdesc->proc->p_pid;
++                      vpbuf[count] = pdesc->vp;
++                      count++;
++                  }
++          
++              if (count > 0 && (copyout(pidbuf, pids, sizeof(pid_t) * MIN(count, maxpids)) ||
++                                copyout(vpbuf, vps, sizeof(int) * MIN(count, maxpids))))
++                  status = EFAULT;
++              
++              KMEM_FREE(pidbuf, program->nprocs * sizeof(pid_t));
++              KMEM_FREE(vpbuf, program->nprocs * sizeof(int));
++          }
++          else
++              status = ENOMEM;
++      }
++
++      if (copyout(&count, npids, sizeof(int)))
++          status = EFAULT;
++    }
++    else
++      status = ESRCH;
++
++    kmutex_unlock(&rms_lock);
++    
++    return(status);
++
++}
++
++int rms_setpset(int psid)
++{
++    struct prg_desc *program;
++    struct proc_desc *pdesc;
++    int status = ESUCCESS;
++
++    if (CURUID())
++      return(EACCES);
++
++    kmutex_lock(&rms_lock);
++    
++    if ((pdesc = findProcess(CURPROC()->p_pid)) != NULL)
++    {
++      program = pdesc->program;
++      program->psid = psid;
++    }
++    else
++      status = ESRCH;
++
++    kmutex_unlock(&rms_lock);
++    return(status);
++}
++
++
++int rms_getpset(int id, int *psid)
++{
++    struct prg_desc *program;
++    int status = ESUCCESS;
++    
++    kmutex_lock(&rms_lock);
++    if ((program = findProgram(id)) != NULL)
++    {
++      if (copyout(&program->psid, psid, sizeof(int)))
++          status = EFAULT;
++    }
++    else
++      status = ESRCH;
++    
++    kmutex_unlock(&rms_lock);
++    return(status);
++}
++
++int
++rms_setelanstats(int id, uint64_t ebytes, uint64_t exfers)
++{
++    struct prg_desc *program;
++    int status = ESUCCESS;
++    
++    DBG(printk("rms_setelanst :: process %d id %d\n", CURPROC()->p_pid, id));
++
++    kmutex_lock(&rms_lock);
++    if ((program = findProgram(id)) != NULL)
++    {
++      if (CURUID() == 0 || CURUID() == program->uid)
++      {
++          program->ebytes = ebytes;
++          program->exfers = exfers;
++      }
++      else
++          status = EACCES;
++    }
++    else
++      status = ESRCH;
++    
++    kmutex_unlock(&rms_lock);
++    return(status);
++}
++
++rms_modversion()
++{
++    return(RMS_MODVERSION);
++}
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++
++
++
++
++
++
++
+Index: linux-2.6.5/drivers/net/qsnet/rms/rms_kern_Linux.c
+===================================================================
+--- linux-2.6.5.orig/drivers/net/qsnet/rms/rms_kern_Linux.c    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/drivers/net/qsnet/rms/rms_kern_Linux.c 2005-05-11 12:10:12.560914032 -0400
+@@ -0,0 +1,430 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "$Id: rms_kern_Linux.c,v 1.20 2004/05/14 08:55:57 duncan Exp $"
++/*      $Source: /cvs/master/quadrics/rmsmod/rms_kern_Linux.c,v $*/
++
++#include <qsnet/kernel.h>
++
++#include <linux/sysctl.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/proc_fs.h>
++
++#include <rms/rmscall.h>
++#include <rms/rmsio.h>
++
++MODULE_AUTHOR("Quadrics Ltd");
++MODULE_DESCRIPTION("RMS support module");
++MODULE_LICENSE("GPL");
++
++int rms_debug = 0;
++
++ctl_table rms_table[] = {
++    {
++      .ctl_name = 1,
++      .procname = "rms_debug",
++      .data     = &rms_debug,
++      .maxlen   = sizeof(int),
++      .mode     = 0644,
++      .child    = NULL,
++      .proc_handler = &proc_dointvec,
++    },
++    {0}
++};
++
++ctl_table rms_root_table[] = {
++    {
++      .ctl_name = CTL_DEBUG,
++      .procname = "rms",
++      .data     = NULL,
++      .maxlen   = 0,
++      .mode     = 0555,
++      .child    = rms_table,
++    },
++    {0}
++};
++
++static struct ctl_table_header *rms_sysctl_header;
++
++static int rms_open (struct inode *ino, struct file *fp);
++static int rms_release (struct inode *ino, struct file *fp);
++static int rms_ioctl (struct inode *inode, struct file *fp, unsigned int cmd, unsigned long arg);
++
++#if defined(CONFIG_PPC64) || defined(CONFIG_SPARC64) || defined(CONFIG_X86_64)
++static int
++rms_ioctl32_cmds[] =
++{
++    RMSIO_GETPRGID32,
++    RMSIO_GETCAP32
++};
++
++static int      rms_ioctl32 (unsigned int fd, unsigned int cmd, 
++                           unsigned long arg, struct file *file);
++#endif
++
++static struct file_operations rms_fops =
++{
++    .owner   = THIS_MODULE,
++    .ioctl   = rms_ioctl,
++    .open    = rms_open,
++    .release = rms_release,
++};
++
++struct proc_dir_entry *rms_procfs_programs;
++static struct proc_dir_entry *rms_procfs_root;
++
++int version_callback(char* page, char** start, off_t off, int count, int* eof, void* data)
++{
++    return(sprintf(page, "$Id: rms_kern_Linux.c,v 1.20 2004/05/14 08:55:57 duncan Exp $\n"));
++}
++
++static int __init rms_start(void)
++{
++    struct proc_dir_entry *p;
++    int res;
++
++    if ((rms_sysctl_header = register_sysctl_table(rms_root_table, 1)) == 0)
++    {
++      printk ("rmsmod: failed to register sysctl table\n");
++      return (-ENXIO);
++    }
++    
++    if ((rms_procfs_root = proc_mkdir("rms",  NULL)) == NULL ||
++      (rms_procfs_programs = proc_mkdir("programs",  rms_procfs_root)) == NULL ||
++      (p = create_proc_entry ("control", S_IRUGO, rms_procfs_root)) == NULL)
++    {
++      unregister_sysctl_table (rms_sysctl_header);
++      printk ("rmsmod: failed to register /proc/rms\n");
++      return (-ENXIO);
++    }
++    p->proc_fops = &rms_fops;
++    p->owner     = THIS_MODULE;
++    p->data      = NULL;
++
++    if ((p = create_proc_entry ("version", S_IRUGO, rms_procfs_root)) != NULL)
++    {
++      p->owner = THIS_MODULE;
++      p->data = NULL;
++      p->read_proc = version_callback;
++    }
++
++    if ((res = rms_init()) != ESUCCESS)
++    {
++      remove_proc_entry ("programs", rms_procfs_root);
++      remove_proc_entry ("control", rms_procfs_root);
++      remove_proc_entry ("rms", NULL);
++      unregister_sysctl_table (rms_sysctl_header);
++      return (-res);
++    }
++
++#if defined(CONFIG_PPC64) || defined(CONFIG_SPARC64) || defined(CONFIG_X86_64)
++    lock_kernel();
++    {
++      extern int register_ioctl32_conversion(unsigned int cmd, int (*handler)(unsigned int, unsigned int, unsigned long, struct file *));
++      register int i;
++      for (i = 0; i < sizeof (rms_ioctl32_cmds)/sizeof(rms_ioctl32_cmds[0]); i++)
++          register_ioctl32_conversion (rms_ioctl32_cmds[i], rms_ioctl32);
++    }
++    unlock_kernel();
++#endif
++    return (0);
++}
++
++static void __exit rms_exit(void)
++{
++    rms_fini();
++
++#if defined(CONFIG_PPC64) || defined(CONFIG_SPARC64) || defined(CONFIG_X86_64)
++    lock_kernel();
++    {
++      extern void unregister_ioctl32_conversion(unsigned int cmd);
++      register int i;
++
++      for (i = 0; i < sizeof (rms_ioctl32_cmds)/sizeof(rms_ioctl32_cmds[0]); i++)
++          unregister_ioctl32_conversion (rms_ioctl32_cmds[i]);
++    }
++    unlock_kernel();
++#endif
++
++    remove_proc_entry ("version", rms_procfs_root);
++    remove_proc_entry ("programs", rms_procfs_root);
++    remove_proc_entry ("control", rms_procfs_root);
++    remove_proc_entry ("rms", NULL);
++    unregister_sysctl_table(rms_sysctl_header);
++}
++
++/* Declare the module init and exit functions */
++module_init(rms_start);
++module_exit(rms_exit);
++
++static int
++rms_open (struct inode *inode, struct file *fp)
++{
++    MOD_INC_USE_COUNT;
++    fp->private_data = NULL;
++
++    return (0);
++}
++
++static int
++rms_release (struct inode *inode, struct file *fp)
++{
++    MOD_DEC_USE_COUNT;
++    return (0);
++}
++
++static int 
++rms_ioctl(struct inode *inode, struct file *fp, unsigned int cmd, unsigned long arg)
++{
++    int res;
++
++    switch (cmd) 
++    {
++/* no corepath support in Linux yet */
++#if 0
++    case RMSIO_SETCOREPATH:
++      res = rms_setcorepath((caddr_t)arg);
++      break;
++      
++    case RMSIO_GETCOREPATH:
++    {
++      RMSIO_GETCOREPATH_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (args)))
++          return (-EFAULT);
++
++      res = rms_getcorepath(args.pid, args.corepath, args.maxlen);
++      break;
++    }
++#endif
++      
++    case RMSIO_PRGCREATE:
++    {
++      RMSIO_PRGCREATE_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (args)))
++          return (-EFAULT);
++
++      res = rms_prgcreate(args.id, args.uid, args.cpus);
++      break;
++    }
++
++    case RMSIO_PRGDESTROY:
++      res = rms_prgdestroy(arg);
++      break;
++      
++    case RMSIO_PRGIDS:
++    {
++      RMSIO_PRGIDS_STRUCT args;
++      
++      if (copy_from_user (&args, (void *) arg, sizeof (args)))
++          return (-EFAULT);
++
++      res = rms_prgids(args.maxids, args.prgids, args.nprgs);
++      break;
++    }
++
++    case RMSIO_PRGINFO:
++    {
++      RMSIO_PRGINFO_STRUCT args;
++      
++      if (copy_from_user (&args, (void *) arg, sizeof (args)))
++          return (-EFAULT);
++
++      res = rms_prginfo(args.id, args.maxpids, args.pids, args.nprocs);
++      break;
++    }
++      
++    case RMSIO_PRGSIGNAL:
++    {
++      RMSIO_PRGSIGNAL_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (args)))
++          return (-EFAULT);
++
++      res = rms_prgsignal(args.id, args.signo);
++      break;
++    }
++      
++    case RMSIO_PRGADDCAP:
++    {
++      RMSIO_PRGADDCAP_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (args)))
++          return (-EFAULT);
++
++      res = rms_prgaddcap(args.id, args.index, args.cap);
++      break;
++    }
++
++    case RMSIO_SETCAP:
++    {
++      RMSIO_SETCAP_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (args)))
++          return (-EFAULT);
++
++      res = rms_setcap(args.index, args.ctx);
++      break;
++    }
++      
++    case RMSIO_NCAPS:
++      res = rms_ncaps((int *)arg);
++      break;
++      
++    case RMSIO_GETPRGID:
++    {
++      RMSIO_GETPRGID_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (args)))
++          return (-EFAULT);
++
++      res = rms_getprgid(args.pid, args.id);
++      break;
++    }
++
++    case RMSIO_GETMYCAP:
++      res = rms_mycap((int *)arg);
++      break;
++      
++    case RMSIO_GETCAP:
++    {
++      RMSIO_GETCAP_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (args)))
++          return (-EFAULT);
++
++      res = rms_getcap(args.index, args.cap);
++      break;
++    }
++
++    case RMSIO_PRGGETSTATS:
++    {
++      RMSIO_PRGGETSTATS_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (args)))
++          return (-EFAULT);
++
++      res = rms_prggetoldstats(args.id, args.stats);
++      break;
++    }
++
++    case RMSIO_PRGGETSTATS2:
++    {
++      RMSIO_PRGGETSTATS2_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (args)))
++          return (-EFAULT);
++
++      res = rms_prggetstats(args.id, args.stats);
++      break;
++    }
++
++    case RMSIO_PRGSUSPEND:
++      res = rms_prgsuspend(arg);
++      break;
++      
++    case RMSIO_PRGRESUME:
++      res = rms_prgresume(arg);
++      break;
++
++    case RMSIO_ELANINITDONE:
++      res = rms_elaninitdone(arg);
++      break;
++
++    case RMSIO_PRGELANPIDS:
++    {
++      RMSIO_PRGELANPIDS_STRUCT args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (args)))
++          return (-EFAULT);
++
++      res = rms_prgelanpids(args.id, args.maxpids, args.vps, args.pids, args.npids);
++      break;
++    }
++
++    case RMSIO_SETELANSTATS:
++    {
++      RMSIO_SETELANSTATS_STRUCT args;
++      elanstats_t estats;
++
++      if (copy_from_user(&args, (void *)arg, sizeof(args)) ||
++          copy_from_user(&estats, (void *)args.estats, sizeof(estats)))
++          return(-EFAULT);
++      
++      res = rms_setelanstats(args.id, estats.ebytes, estats.exfers);
++      break;
++    }
++
++    case RMSIO_MODVERSION:
++    {
++      RMSIO_MODVERSION_STRUCT args;
++      int version = rms_modversion();
++      
++      if (copy_from_user (&args, (void *)arg, sizeof (args)))
++          return (-EFAULT);
++      
++      if (copyout(&version, args.version, sizeof(int)))
++          res = EFAULT;
++      else
++          res = ESUCCESS;
++
++      break;
++    }
++
++    default:
++      res = EINVAL;
++      break;
++    }
++
++    return ((res == 0) ? 0 : -res);
++}
++
++#if defined(CONFIG_PPC64) || defined(CONFIG_SPARC64) || defined(CONFIG_X86_64)
++static int
++rms_ioctl32 (unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file)
++{
++    int res;
++
++    switch (cmd)
++    {
++    case RMSIO_GETPRGID32:
++    {
++      RMSIO_GETPRGID_STRUCT32 args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (args)))
++          return (-EFAULT);
++
++      res = rms_getprgid(args.pid, (int *)(unsigned long) args.idptr);
++      break;
++    }
++      
++    case RMSIO_GETCAP32:
++    {
++      RMSIO_GETCAP_STRUCT32 args;
++
++      if (copy_from_user (&args, (void *) arg, sizeof (args)))
++          return (-EFAULT);
++
++      res = rms_getcap(args.index, (ELAN_CAPABILITY *)(unsigned long) args.capptr);
++      break;
++    }
++
++    default:
++      return (sys_ioctl (fd, cmd, arg));
++    }
++
++    return ((res == 0) ? 0 : -res);
++}
++#endif
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/drivers/net/Kconfig
+===================================================================
+--- linux-2.6.5.orig/drivers/net/Kconfig       2005-02-01 16:56:02.000000000 -0500
++++ linux-2.6.5/drivers/net/Kconfig    2005-05-11 12:10:12.562913728 -0400
+@@ -2541,6 +2541,8 @@
+ source "drivers/net/tokenring/Kconfig"
++source "drivers/net/qsnet/Kconfig"
++
+ config NET_FC
+       bool "Fibre Channel driver support"
+       depends on NETDEVICES && SCSI && PCI
+Index: linux-2.6.5/drivers/net/Makefile
+===================================================================
+--- linux-2.6.5.orig/drivers/net/Makefile      2005-02-01 16:56:02.000000000 -0500
++++ linux-2.6.5/drivers/net/Makefile   2005-05-11 12:10:12.562913728 -0400
+@@ -200,3 +200,5 @@
+ obj-$(CONFIG_NETCONSOLE) += netconsole.o
+ obj-$(CONFIG_XPNET) += xpnet.o
++
++obj-$(CONFIG_QSNET) += qsnet/
+Index: linux-2.6.5/fs/exec.c
+===================================================================
+--- linux-2.6.5.orig/fs/exec.c 2005-02-01 16:56:09.000000000 -0500
++++ linux-2.6.5/fs/exec.c      2005-05-11 12:10:12.563913576 -0400
+@@ -65,6 +65,8 @@
+ #include <linux/kmod.h>
+ #endif
++#include <linux/ptrack.h>
++
+ int core_uses_pid;
+ char core_pattern[65] = "core";
+ /* The maximal length of core_pattern is also specified in sysctl.c */
+@@ -1197,6 +1199,9 @@
+       if (retval < 0)
+               goto out;
++      /* notify any ptrack callbacks of the process exec */
++      ptrack_call_callbacks(PTRACK_PHASE_EXEC, NULL);
++
+       retval = search_binary_handler(&bprm,regs);
+       if (retval >= 0) {
+               TRIG_EVENT(exec_hook, file->f_dentry->d_name.len,
+Index: linux-2.6.5/fs/select.c
+===================================================================
+--- linux-2.6.5.orig/fs/select.c       2005-02-01 16:55:41.000000000 -0500
++++ linux-2.6.5/fs/select.c    2005-05-11 12:10:12.564913424 -0400
+@@ -649,3 +649,4 @@
+       }
+       return -EIOCBRETRY;
+ }
++EXPORT_SYMBOL_GPL(sys_poll);
+Index: linux-2.6.5/fs/read_write.c
+===================================================================
+--- linux-2.6.5.orig/fs/read_write.c   2005-02-01 16:56:02.000000000 -0500
++++ linux-2.6.5/fs/read_write.c        2005-05-11 14:08:49.220017400 -0400
+@@ -339,6 +339,7 @@
+       return ret;
+ }
++EXPORT_SYMBOL(sys_write);
+ asmlinkage ssize_t sys_pread64(unsigned int fd, char __user *buf,
+                            size_t count, loff_t pos)
+Index: linux-2.6.5/include/elan/bitmap.h
+===================================================================
+--- linux-2.6.5.orig/include/elan/bitmap.h     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan/bitmap.h  2005-05-11 12:10:12.564913424 -0400
+@@ -0,0 +1,74 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __QSNET_BITMAP_H
++#define __QSNET_BITMAP_H
++
++#ident "$Id: bitmap.h,v 1.5 2004/01/20 17:32:15 david Exp $"
++/*      $Source: /cvs/master/quadrics/elanmod/modsrc/bitmap.h,v $ */
++
++typedef unsigned int                  bitmap_t;
++
++#define BT_NBIPUL                     32                      /* n bits per bitmap_t */
++#define BT_ULSHIFT                    5                       /* log 2 BT_NBIPUL to extract word index */
++#define BT_ULMASK                     0x1f                    /* to extract bit index */
++
++#define BT_WIM(bitmap,bitindex)               ((bitmap)[(bitindex) >> BT_ULSHIFT])            /* word in map */
++#define BT_BIW(bitindex)              (1 << ((bitindex) & BT_ULMASK))         /* bit in word */
++
++/* BT_BITOUL -- n bits to n words */
++#define BT_BITOUL(nbits)              (((nbits) + BT_NBIPUL -1) / BT_NBIPUL)
++
++#define BT_TEST(bitmap,bitindex)      ((BT_WIM((bitmap), (bitindex)) & BT_BIW(bitindex)) ? 1 : 0)
++#define BT_SET(bitmap,bitindex)               do { BT_WIM((bitmap), (bitindex)) |= BT_BIW(bitindex); } while (0)
++#define BT_CLEAR(bitmap,bitindex)     do { BT_WIM((bitmap), (bitindex)) &= ~BT_BIW(bitindex); } while (0)
++
++/* return first free bit in the bitmap, or -1 for failure */
++extern int  bt_freebit (bitmap_t *bitmap, int nbits);
++
++/* return the index of the lowest set bit in the bitmap or -1 for failure */
++extern int bt_lowbit (bitmap_t *bitmap, int nbits);
++
++/* return the index of the next set/clear bit in the bitmap or -1 for failure */
++extern int bt_nextbit (bitmap_t *bitmap, int nbits, int last, int isset);
++
++/* copy/zero/fill/compare a bit map */
++extern void bt_copy (bitmap_t *a, bitmap_t *b, int nbits);
++extern void bt_zero (bitmap_t *a, int nbits);
++extern void bt_fill (bitmap_t *a, int nbits);
++extern int  bt_cmp (bitmap_t *a, bitmap_t *b, int nbits);
++
++/* intersect bitmap 'a' with bitmap 'b' and return in 'a' */
++extern void bt_intersect (bitmap_t *a, bitmap_t *b, int nbits);
++
++/* remove/add bitmap 'b' from bitmap 'a' */
++extern void bt_remove (bitmap_t *a, bitmap_t *b, int nbits);
++extern void bt_add (bitmap_t *a, bitmap_t *b, int nbits);
++
++/* check whether bitmap 'a' spans bitmap 'b' */
++extern int  bt_spans (bitmap_t *a, bitmap_t *b, int nbits);
++
++/* copy [base,base+nbits-1] from 'a' to 'b' */
++extern void bt_subset (bitmap_t *a, bitmap_t *b, int base, int nbits);
++
++/* find bits clear in 'a' and set in 'b', put result in 'c' */
++extern void bt_up (bitmap_t *a, bitmap_t *b, bitmap_t *c, int nbits);
++
++/* find bits set in 'a' and clear in 'b', put result in 'c' */
++extern void bt_down (bitmap_t *a, bitmap_t *b, bitmap_t *c, int nbits);
++
++/* return number of bits set in bitmap */
++extern int  bt_nbits (bitmap_t *a, int nbits);
++
++
++#endif /* __QSNET_BITMAP_H */
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.6.5/include/elan/capability.h
+===================================================================
+--- linux-2.6.5.orig/include/elan/capability.h 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan/capability.h      2005-05-11 12:10:12.565913272 -0400
+@@ -0,0 +1,197 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Limited.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: capability.h,v 1.16 2004/07/20 10:15:33 david Exp $"
++/*      $Source: /cvs/master/quadrics/elanmod/modsrc/capability.h,v $*/
++
++#ifndef __ELAN_CAPABILITY_H
++#define __ELAN_CAPABILITY_H
++
++#include <elan/bitmap.h>
++
++/* Maximum number of rails */
++#define ELAN_MAX_RAILS          (31)
++/* Maximum number of virtual processes we support */
++#define ELAN_MAX_VPS          (16384)
++
++/* Number of words in a bitmap capability */
++#define ELAN_BITMAPSIZE               BT_BITOUL(ELAN_MAX_VPS)
++
++/* Guaranteed invalid values */
++#define ELAN_INVALID_PROCESS  (0x7fffffff)            /* A GUARANTEED invalid process # */
++#define ELAN_INVALID_NODE     (0xFFFF)
++#define ELAN_INVALID_CONTEXT  (0xFFFF)
++
++/* Number of values in a user key */
++#define ELAN_USERKEY_ENTRIES  4
++
++typedef void * ELAN_CAP_OWNER;
++
++/* 
++ * When used in userspace this is relative to the base of
++ * the capabality but is an absolute location for kernel space.
++ */
++typedef struct elan_location
++{
++      unsigned short loc_node;
++      unsigned short loc_context;
++} ELAN_LOCATION;
++
++typedef struct elan_userkey
++{
++      unsigned        key_values[ELAN_USERKEY_ENTRIES];
++} ELAN_USERKEY;
++
++typedef struct elan_capability
++{
++      ELAN_USERKEY    cap_userkey;                            /* User defined protection */
++
++      int             cap_version;                            /* Version number */
++      unsigned short  cap_type;                               /* Capability Type */
++      unsigned short  cap_spare;                              /* spare was cap_elan_type */
++
++      int             cap_lowcontext;                         /* low context number in block */
++      int             cap_highcontext;                        /* high context number in block */
++      int             cap_mycontext;                          /* my context number */
++    
++      int             cap_lownode;                            /* low elan id of group */
++      int             cap_highnode;                           /* high elan id of group */
++
++      unsigned int    cap_railmask;                           /* which rails this capability is valid for */
++      
++      bitmap_t        cap_bitmap[ELAN_BITMAPSIZE];            /* Bitmap of process to processor translation */
++} ELAN_CAPABILITY;
++
++#define ELAN_CAP_UNINITIALISED                (-1)
++
++#define ELAN_CAP_VERSION_NUMBER               (0x00010002)
++
++#define ELAN_CAP_NUM_NODES(cap)               ((cap)->cap_highnode - (cap)->cap_lownode + 1)
++#define ELAN_CAP_NUM_CONTEXTS(cap)    ((cap)->cap_highcontext - (cap)->cap_lowcontext + 1)
++
++/* using or defining our own MIN/MAX had confilicts with dunix so we define ELAN_ ones */
++#define ELAN_MIN(a,b) ((a) > (b) ? (b) : (a))
++#define ELAN_MAX(a,b) ((a) > (b) ? (a) : (b))
++#define ELAN_CAP_BITMAPSIZE(cap)      (ELAN_MAX (ELAN_MIN (ELAN_CAP_NUM_NODES(cap) * ELAN_CAP_NUM_CONTEXTS(cap), ELAN_MAX_VPS), 0))
++
++#define ELAN_CAP_SIZE(cap)            (offsetof (ELAN_CAPABILITY, cap_bitmap[BT_BITOUL(ELAN_CAP_BITMAPSIZE(cap))]))
++#define ELAN_CAP_ENTRIES(cap)           (((cap)->cap_type & ELAN_CAP_TYPE_NO_BITMAP) ? ELAN_CAP_BITMAPSIZE((cap)) : bt_nbits((cap)->cap_bitmap, ELAN_CAP_BITMAPSIZE((cap))))
++
++#define ELAN_CAP_IS_RAIL_SET(cap,rail)  ((cap)->cap_railmask & (1<<rail))
++
++#define ELAN_CAP_KEY_MATCH(cap1,cap2) ((cap1)->cap_userkey.key_values[0] == (cap2)->cap_userkey.key_values[0] && \
++                                       (cap1)->cap_userkey.key_values[1] == (cap2)->cap_userkey.key_values[1] && \
++                                       (cap1)->cap_userkey.key_values[2] == (cap2)->cap_userkey.key_values[2] && \
++                                       (cap1)->cap_userkey.key_values[3] == (cap2)->cap_userkey.key_values[3])
++
++#define ELAN_CAP_TYPE_MATCH(cap1,cap2)  ((cap1)->cap_version           == (cap2)->cap_version           && \
++                                       (cap1)->cap_type              == (cap2)->cap_type)
++
++#define ELAN_CAP_GEOM_MATCH(cap1,cap2)        ((cap1)->cap_lowcontext        == (cap2)->cap_lowcontext        && \
++                                       (cap1)->cap_highcontext       == (cap2)->cap_highcontext       && \
++                                       (cap1)->cap_lownode           == (cap2)->cap_lownode           && \
++                                       (cap1)->cap_highnode          == (cap2)->cap_highnode          && \
++                                         (cap1)->cap_railmask          == (cap2)->cap_railmask          && \
++                                       !bcmp (&(cap1)->cap_bitmap[0], &(cap2)->cap_bitmap[0],            \
++                                              BT_BITOUL(ELAN_CAP_BITMAPSIZE(cap1)*sizeof(bitmap_t))))
++
++#define ELAN_CAP_MATCH(cap1,cap2)     (ELAN_CAP_KEY_MATCH (cap1, cap2)  && \
++                                       ELAN_CAP_TYPE_MATCH (cap1, cap2) && \
++                                       ELAN_CAP_GEOM_MATCH (cap1, cap2))
++
++#define ELAN_CAP_VALID_MYCONTEXT(cap)   (    ((cap)->cap_lowcontext  != ELAN_CAP_UNINITIALISED)     \
++                                        && ((cap)->cap_mycontext   != ELAN_CAP_UNINITIALISED)     \
++                                        && ((cap)->cap_highcontext != ELAN_CAP_UNINITIALISED)     \
++                                        && ((cap)->cap_lowcontext <= (cap)->cap_mycontext)        \
++                                        && ((cap)->cap_mycontext <= (cap)->cap_highcontext)) 
++
++/*
++ * Definitions for type 
++ */
++#define ELAN_CAP_TYPE_BLOCK           1               /* Block distribution */
++#define ELAN_CAP_TYPE_CYCLIC          2               /* Cyclic distribution */
++#define ELAN_CAP_TYPE_KERNEL          3               /* Kernel capability */
++
++#define ELAN_CAP_TYPE_MASK            (0xFFF)         /* Mask for type */
++
++/* OR these bits in for extra features */
++#define ELAN_CAP_TYPE_HWTEST          (1 << 12)       /* Hardware test capability type */
++#define ELAN_CAP_TYPE_MULTI_RAIL      (1 << 13)       /* "new" multi rail capability */
++#define ELAN_CAP_TYPE_NO_BITMAP               (1 << 14)       /* don't use bit map */
++#define ELAN_CAP_TYPE_BROADCASTABLE   (1 << 15)       /* broadcastable */
++
++
++extern void          elan_nullcap     (ELAN_CAPABILITY *cap);
++extern char         *elan_capability_string (ELAN_CAPABILITY *cap, char *str);
++extern ELAN_LOCATION elan_vp2location (unsigned process, ELAN_CAPABILITY *cap);
++extern int           elan_location2vp (ELAN_LOCATION location, ELAN_CAPABILITY *cap);
++extern int           elan_nvps        (ELAN_CAPABILITY *cap);
++extern int           elan_nlocal      (int node, ELAN_CAPABILITY *cap);
++extern int           elan_maxlocal    (ELAN_CAPABILITY *cap);
++extern int           elan_localvps    (int node, ELAN_CAPABILITY *cap, int *vps, int size);
++extern int           elan_nrails      (ELAN_CAPABILITY *cap);
++extern int           elan_rails       (ELAN_CAPABILITY *cap, int *rails);
++extern int           elan_cap_overlap (ELAN_CAPABILITY *cap1, ELAN_CAPABILITY *cap2);
++
++/*
++ * capability creation/access fns provide for running
++ * new libelan code on old OS releases
++ */
++extern int elan_lowcontext(ELAN_CAPABILITY *cap);
++extern int elan_mycontext(ELAN_CAPABILITY *cap);
++extern int elan_highcontext(ELAN_CAPABILITY *cap);
++extern int elan_lownode(ELAN_CAPABILITY *cap);
++extern int elan_highnode(ELAN_CAPABILITY *cap);
++extern int elan_captype(ELAN_CAPABILITY *cap);
++extern int elan_railmask(ELAN_CAPABILITY *cap);
++
++extern int elan_getenvCap (ELAN_CAPABILITY *cap, int index);
++extern ELAN_CAPABILITY *elan_createCapability(void);
++extern ELAN_CAPABILITY *elan_copyCapability(ELAN_CAPABILITY *from, int ctxShift);
++extern int elan_generateCapability(char *string);
++
++typedef struct elan_cap_struct
++{
++      ELAN_CAP_OWNER   owner;
++      ELAN_CAPABILITY  cap;
++
++      unsigned int     attached; /* count of people attached */
++      unsigned int     active;   /* ie not being destroyed   */
++} ELAN_CAP_STRUCT;
++
++#if ! defined(__KERNEL__)
++extern void          elan_get_random_key(ELAN_USERKEY *key);
++extern int           elan_prefrails(ELAN_CAPABILITY *cap, int *pref, int nvp);
++#endif
++
++#if defined(__KERNEL__)
++/* capability.c */
++extern int elan_validate_cap  (ELAN_CAPABILITY *cap);
++extern int elan_validate_map  (ELAN_CAPABILITY *cap, ELAN_CAPABILITY *map);
++
++extern int elan_create_cap  (ELAN_CAP_OWNER owner, ELAN_CAPABILITY *cap);
++extern int elan_destroy_cap (ELAN_CAP_OWNER owner, ELAN_CAPABILITY *cap);
++extern int elan_create_vp   (ELAN_CAP_OWNER owner, ELAN_CAPABILITY *cap, ELAN_CAPABILITY *map);
++extern int elan_destroy_vp  (ELAN_CAP_OWNER owner, ELAN_CAPABILITY *cap, ELAN_CAPABILITY *map);
++
++typedef       void (*ELAN_DESTROY_CB)(void *args, ELAN_CAPABILITY *cap, ELAN_CAPABILITY *map);
++
++extern int elan_attach_cap  (ELAN_CAPABILITY *cap, unsigned int rail, void *args, ELAN_DESTROY_CB callback);
++extern int elan_detach_cap  (ELAN_CAPABILITY *cap, unsigned int rail);
++
++extern int elan_get_caps    (uint *number_of_results, uint array_size, ELAN_CAP_STRUCT *caps);
++extern int elan_cap_dump    (void);
++#endif /* __KERNEL__ */
++
++
++#endif /* __ELAN_CAPABILITY_H */
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.6.5/include/elan/cm.h
+===================================================================
+--- linux-2.6.5.orig/include/elan/cm.h 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan/cm.h      2005-05-11 12:10:12.566913120 -0400
+@@ -0,0 +1,412 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN_CM_H
++#define __ELAN_CM_H
++
++#ident "@(#)$Id: cm.h,v 1.14.2.1 2004/11/12 10:54:50 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/cm.h,v $*/
++
++#include <elan/statemap.h>
++
++#if defined(DIGITAL_UNIX)
++/*
++ * On Tru64 - SMP doesn't mean Symmetric - cpu 0 is a master cpu and is responsible
++ * for handling all PCI interrupts and "funneled" operations.  When a kernel thread
++ * is made runnable, the scheduler will choose which cpu it will run on at that time,
++ * and will only execute a higher priority thread from another cpu's run queue when 
++ * it becomes totally idle (apparently also including user processes).  Also the 
++ * assert_wait_mesg_timo function uses a per-cpu timeout - these can only get executed
++ * at "preemptable" places - so again have no guarantee on when they will execute if
++ * they happen to be queued on a "hogged" cpu. The combination of these mean that the Tru64
++ * is incapable of scheduling a high priority kernel  thread within a deterministic time
++ * of when it should have become runnable - wonderfull.
++ *
++ * Hence the solution Compaq have proposed it to schedule a timeout onto all of the
++ * cpu's timeouts lists at the maximum frequency that we could want to execute code,
++ * then to handle the scheduling of work between these ourselves.  With a bit of luck
++ * ..... at least one cpu will be sufficiently unloaded to allow us to get a chance
++ * to do our important work.
++ *
++ * However ..... this still is not reliable, since timeouts under Tru64 are still 
++ * only run when the currently running kernel thread "co-operates" by calling one
++ * of a number of functions which is permitted to run the "lwc"s AND is not holding
++ * any spinlocks AND is running ai IPL 0.   However Compaq are unable to provide
++ * any upper limit on the time between the "lwc"'s being run and so it is possible
++ * for all 4 cpus to not run them for an unbounded time.
++ *
++ * The solution proposed is to use the RM_TEMP_BACKDOOR hook which was added to 
++ * hardclock() to "solve" this problem for Memory Channel.  However, since it
++ * is called within the clock interrupt it is not permissible to aquire any
++ * spinlocks, nor to run for "too long".  This means that it is not possible to
++ * call the heartbeat algorithm from this hook.  
++ *
++ * Our solution to these limitations is to use the hook to cause an elan interrupt 
++ * to be delivered, by issueing a mis-aligned SetEvent command - this causes the device 
++ * to trap and ep_cprocTrap() can then run the heartbeat code.  However there is a lock 
++ * order violation between the elan_dev::IntrLock and ep_dev::Lock, so we have to 
++ * use a trylock and if we fail, then hope that when the interrupt is delievered again
++ * some time later we will succeed.
++ *
++ * However this only works if the kernel is able to respond to the Elan interrupt,
++ * so we panic inside the RM_TEMP_BACKDOOR hook if the SetEvent's interrupt has
++ * not been taken for more than an CM_TIMER_SCHEDULE_TIMEOUT interval.
++ *
++ * In fact this is exactly the mechanism that other operating systems use to
++ * execute timeouts, since the hardclock interrupt posts a low priority 
++ * "soft interrupt" which "pre-eempts" the currently running thread and then
++ * executes the timeouts.To block timeouts you use splsoftclock() the same as 
++ * in Tru64.
++ */
++#define PER_CPU_TIMEOUT                       TRUE
++#endif
++
++
++#define CM_SGMTS_PER_LEVEL            8                       /* maximum nodes in each segment */
++#define CM_MAX_LEVELS                 6                       /* maximum depth of tree */
++
++/* message buffers/dmas/events etc */
++#define CM_NUM_NODE_MSG_BUFFERS               (CM_MAX_LEVELS * CM_SGMTS_PER_LEVEL) /* subordinates and leader */
++#define CM_NUM_SPARE_MSG_BUFFERS      8                       /* spare msg buffers for non-connected nodes */
++#define CM_NUM_MSG_BUFFERS            (CM_NUM_NODE_MSG_BUFFERS + CM_NUM_SPARE_MSG_BUFFERS)
++
++#define CM_INPUTQ_ENTRIES             128                     /* # entries in input queue */
++
++#define CM_PERIODIC_DISCOVER_INTERVAL (5000)          /* 5s (infrequent resolution of established leader conflicts) */
++#define CM_URGENT_DISCOVER_INTERVAL   (50)            /* 0.05s (more frequently than heartbeats 'cause they don't retry) */
++#define CM_HEARTBEAT_INTERVAL         (125)           /* 0.125s */
++#define CM_TIMER_SCHEDULE_TIMEOUT     (4000)          /* 4s     Maximum time before a timer that's secheduled to run gets to run (eg blocked in interrupt handlers etc) */
++#define CM_THREAD_SCHEDULE_TIMEOUT    (30000)         /* 30s    Maximum time before a thread that's scheduled to run gets to run */
++#define CM_THREAD_RUNNING_TIMEOUT     (30000)         /* 30s    Don't expect the manager thread to be running longer than this */
++
++#ifdef PER_CPU_TIMEOUT
++#define CM_PERCPU_TIMEOUT_INTERVAL    (50)            /* 0.05s (must be less than all above intervals) */
++#define CM_PACEMAKER_INTERVAL         (500)           /* 0.05s */
++
++#define CM_HEARTBEAT_OVERDUE          (250)           /* 0.25s Maximum time a timeout can be overdue before taking extreme action */
++#endif
++
++#define CM_P2P_DMA_RETRIES            31
++
++/* We expect at least 1 point-to-point message in CM_P2P_MSG_RETRIES
++ * attempts to send one to be successfully received */
++#define CM_P2P_MSG_RETRIES            8
++
++/* We expect at least 1 broadcast message in CM_BCAST_MSG_RETRIES attempts
++ * to send one to be successfully received. */
++#define CM_BCAST_MSG_RETRIES          40
++
++/* Heartbeat timeout allows for a node stalling and still getting its
++ * heartbeat. The 2 is to allow for unsynchronised polling times. */
++#define CM_HEARTBEAT_TIMEOUT          (CM_TIMER_SCHEDULE_TIMEOUT + (2 + CM_P2P_MSG_RETRIES) * CM_HEARTBEAT_INTERVAL)
++
++/* Discover timeout must be > CM_HEARTBEAT_TIMEOUT to guarantee that people
++ * who don't see discovery are considered dead by their leader.  This
++ * ensures that by the time a node "discovers" it is a leader of a segment,
++ * the previous leader of that segment will have been deemed to be dead by
++ * its the parent segment's leader */
++#define CM_DISCOVER_TIMEOUT           (CM_TIMER_SCHEDULE_TIMEOUT + (2 + CM_BCAST_MSG_RETRIES) * CM_URGENT_DISCOVER_INTERVAL)
++
++#define CM_WAITING_TIMEOUT            (CM_DISCOVER_TIMEOUT * 100)
++
++/*
++ * Convert all timeouts specified in mS into "ticks"
++ */
++#define MSEC2TICKS(MSEC)              (((MSEC)*HZ)/1000)
++
++
++/* statemap entry */
++typedef struct cm_state_entry
++{
++    int16_t           level;                  /* cluster level to apply to */
++    int16_t         offset;                   /* from statemap_findchange() */
++    uint16_t          seg[BT_NBIPUL/16];      /* ditto */
++} CM_STATEMAP_ENTRY;
++
++/* offset is >= 0 for a change to apply and */
++#define STATEMAP_NOMORECHANGES        (-1)            /* end of a set of updates */
++#define STATEMAP_RESET                (-2)            /* reset the target map */
++#define STATEMAP_NOOP         (-3)            /* null token */
++
++/* CM message format */
++typedef int8_t        CM_SEQ;                         /* heartbeat sequence numbers; at least 2 bits, signed */
++
++/*
++ * The message header is received into the last 64 byte block of 
++ * the input queue and the Version *MUST* be the last word of the 
++ * block to ensure that we can see that the whole of the message
++ * has reached main memory after we've seen the input queue pointer
++ * have been updated.
++ */
++typedef struct ep_cm_hdr
++{
++    uint32_t         Pad0;
++    uint32_t         Pad1;
++
++    uint8_t          Type;
++    uint8_t          Level;
++    CM_SEQ           Seq;                     /* precision at least 2 bits each*/
++    CM_SEQ           AckSeq;
++    
++    uint16_t         NumMaps;
++    uint16_t         MachineId;
++
++    uint16_t         NodeId;
++    uint16_t         Checksum;
++
++    uint32_t           Timestamp;
++    uint32_t           ParamHash;
++    uint32_t         Version;
++} CM_HDR;
++
++#define CM_HDR_SIZE       sizeof (CM_HDR)
++
++typedef struct cm_msg
++{
++    union {
++      CM_STATEMAP_ENTRY   Statemaps[1];               /* piggy-backed statemap updates start here */
++      uint8_t             Space[EP_SYSTEMQ_MSG_MAX - CM_HDR_SIZE];
++    } Payload;
++    
++    CM_HDR                Hdr;
++} CM_MSG;
++
++/* The maximum number of statemap entries that can fit within an EP_CM_MSG_BUFFER */
++#define CM_MSG_MAXMAPS                (offsetof (CM_MSG, Hdr) / sizeof (CM_STATEMAP_ENTRY))
++#define CM_MSG_MAP(mapno)     (CM_MSG_MAXMAPS - (mapno) - 1)
++
++/* The actual special message base & size, including 'nmaps' piggy-backed statemap entries */
++#define CM_MSG_BASE(nmaps)    (nmaps == 0 ? offsetof (CM_MSG, Hdr) : offsetof (CM_MSG, Payload.Statemaps[CM_MSG_MAXMAPS - nmaps]))
++#define CM_MSG_SIZE(nmaps)    (sizeof (CM_MSG) - CM_MSG_BASE(nmaps))
++
++#define CM_MSG_VERSION                                0xcad00005
++#define CM_MSG_TYPE_RESOLVE_LEADER            0
++#define CM_MSG_TYPE_DISCOVER_LEADER           1
++#define CM_MSG_TYPE_NOTIFY                    2
++#define CM_MSG_TYPE_DISCOVER_SUBORDINATE      3
++#define CM_MSG_TYPE_IMCOMING                  4
++#define CM_MSG_TYPE_HEARTBEAT                 5
++#define CM_MSG_TYPE_REJOIN                    6
++
++/* CM machine segment */
++typedef struct cm_sgmtMaps
++{
++    u_char       InputMapValid;                       /* Input map has been set */
++    u_char       OutputMapValid;              /* Output map has been set */
++    u_char       SentChanges;                 /* got an outstanding STATEMAP_NOMORECHANGES to send */
++    statemap_t  *OutputMap;                   /* state to send */
++    statemap_t  *InputMap;                    /* state received */
++    statemap_t  *CurrentInputMap;             /* state being received */
++} CM_SGMTMAPS;
++
++typedef struct cm_sgmt
++{
++   u_char       State;
++   u_char       SendMaps;
++   u_char       MsgAcked;
++   CM_SEQ     MsgSeq;
++   CM_SEQ     AckSeq;
++   u_int      NodeId;
++   long               UpdateTick;
++   long               WaitingTick;
++   uint32_t   Timestamp;
++   CM_SGMTMAPS  Maps[CM_MAX_LEVELS];          /* Maps[i] == state for cluster level i */
++   u_short      MsgNumber;                    /* msg buffer to use */
++   u_short    NumMaps;                        /* # maps in message buffer */
++   u_short      Level;
++   u_short      Sgmt;
++} CM_SGMT;
++
++#define CM_SGMT_ABSENT                0               /* no one there at all */
++#define CM_SGMT_WAITING               1               /* waiting for subtree to connect */
++#define CM_SGMT_COMING                2               /* expecting a subtree to reconnect */
++#define CM_SGMT_PRESENT               3               /* connected */
++
++typedef struct cm_level
++{
++    int              SwitchLevel;
++    u_int            MinNodeId;
++    u_int              NumNodes;
++    u_int              NumSegs;
++    u_int              MySgmt;
++   
++    /* SubordinateMap[i] == OR of all subordinate maps on this level and down for cluster level i */
++    u_char             SubordinateMapValid[CM_MAX_LEVELS];
++    statemap_t        *SubordinateMap[CM_MAX_LEVELS];
++
++    /* maps/flags for this cluster level */
++    u_int              Online:1;                              /* I've gone online (seen myself running) */
++    u_int            Restarting:1;                            /* driving my owm restart bit */
++    u_char           OfflineReasons;                          /* forced offline by broadcast */
++
++    u_char             GlobalMapValid;
++    u_char             SubTreeMapValid;
++    u_long           Connected;
++
++    statemap_t        *LocalMap;              /* state bits I drive */
++    statemap_t        *SubTreeMap;            /* OR of my and my subtree states */
++    statemap_t        *GlobalMap;             /* OR of all node states */
++    statemap_t        *LastGlobalMap;         /* last map I saw */
++    statemap_t        *TmpMap;                        /* scratchpad */
++
++    CM_SGMT          Sgmts[CM_SGMTS_PER_LEVEL];
++} CM_LEVEL;
++
++#define CM_ROLE_LEADER_CANDIDATE      0
++#define CM_ROLE_LEADER                        1
++#define CM_ROLE_SUBORDINATE           2
++
++/* global status bits */
++#define CM_GSTATUS_STATUS_MASK                0x03    /* bits nodes drive to broadcast their status */
++#define CM_GSTATUS_ABSENT             0x00    /* Off the network */
++#define CM_GSTATUS_STARTING           0x01    /* I'm waiting for everyone to see me online */
++#define CM_GSTATUS_RUNNING              0x03  /* up and running */
++#define CM_GSTATUS_CLOSING            0x02    /* I'm waiting for everyone to see me offline */
++
++#define CM_GSTATUS_ACK_MASK           0x0c    /* bits node drive to ack other status */
++#define CM_GSTATUS_MAY_START          0x04    /* Everyone thinks I may not start */
++#define CM_GSTATUS_MAY_RUN            0x08    /* Everyone thinks I may not run */
++
++#define CM_GSTATUS_RESTART            0x10    /* Someone thinks I should restart */
++#define CM_GSTATUS_BITS                       5
++
++#define CM_GSTATUS_BASE(node)         ((node) * CM_GSTATUS_BITS)
++
++#if defined(PER_CPU_TIMEOUT)
++typedef struct cm_timeout_data
++{
++    long              ScheduledAt;                            /* lbolt timeout was scheduled to run at */
++
++    unsigned long       EarlyCount;                           /* # times run early than NextRun */
++    unsigned long     MissedCount;                            /* # times run on time - but someone else was running it */
++    unsigned long       WastedCount;                          /* # times we failed to get the spinlock */
++    unsigned long     WorkCount;                              /* # times we're the one running */
++
++    unsigned long     WorstDelay;                             /* worst scheduling delay */
++    unsigned long     BestDelay;                              /* best scheduling delay */
++
++    unsigned long     WorstLockDelay;                         /* worst delay before getting rail->Lock */
++
++    unsigned long     WorstHearbeatDelay;                     /* worst delay before calling DoHeartbeatWork */
++} CM_TIMEOUT_DATA;
++#endif
++
++typedef struct cm_rail
++{
++    EP_RAIL         *Rail;                                    /* rail we're associated with */
++    struct list_head   Link;                                  /*   and linked on the CM_SUBSYS */
++
++    uint32_t         ParamHash;                               /* hash of critical parameters */
++    uint32_t           Timestamp;
++    long             DiscoverStartTick;                       /* when discovery start */
++
++    unsigned int       NodeId;                                        /* my node id */
++    unsigned int       NumNodes;                              /*   and number of nodes */
++    unsigned int       NumLevels;                             /* number of levels computed from machine size */
++    int                      BroadcastLevel;
++    long             BroadcastLevelTick;
++    unsigned int       TopLevel;                              /* level at which I'm not a leader */
++    unsigned char      Role;                                  /* state at TopLevel */
++
++    EP_INPUTQ       *PolledQueue;                             /* polled input queue */
++    EP_INPUTQ       *IntrQueue;                               /* intr input queue */
++    EP_OUTPUTQ              *MsgQueue;                                /* message  */
++    unsigned int       NextSpareMsg;                          /* next "spare" message buffer to use */
++
++    EP_CM_RAIL_STATS   Stats;                                 /* statistics */
++
++    kmutex_t         Mutex;
++    spinlock_t               Lock;
++    
++    long             NextHeartbeatTime;                       /* next time to check/send heartbeats */
++    long             NextDiscoverTime;                        /* next time to progress discovery  */
++    long             NextRunTime;                             /* the earlier of the above two or intr requires inputq poll*/
++
++    unsigned int       OfflineReasons;                                /* forced offline by procfs/manager thread stuck */
++
++#if defined(PER_CPU_TIMEOUT)
++    spinlock_t               HeartbeatTimeoutsLock;                   /* spinlock to sequentialise per-cpu timeouts */
++    long             HeartbeatTimeoutsStarted;                /* bitmap of which timeouts have started */
++    long             HeartbeatTimeoutsStopped;                /* bitmap of which timeouts have stopped */
++    long             HeartbeatTimeoutsShouldStop;             /* flag to indicate timeouts should stop */
++    kcondvar_t               HeartbeatTimeoutsWait;                   /* place to sleep waiting for timeouts to stop */
++    long             HeartbeatTimeoutRunning;                 /* someone is running the timeout - don't try for the lock */
++
++    long             HeartbeatTimeoutOverdue;                 /* heartbeat seen as overdue - interrupt requested */
++
++    CM_TIMEOUT_DATA   *HeartbeatTimeoutsData;                 /* per timeout data */
++#else
++    struct timer_list  HeartbeatTimer;                                /* timer for heartbeat/discovery */
++#endif
++
++    CM_LEVEL           Levels[CM_MAX_LEVELS];
++} CM_RAIL;
++
++/* OfflineReasons (both per-rail and  */
++#define CM_OFFLINE_BROADCAST          (1 << 0)
++#define CM_OFFLINE_PROCFS             (1 << 1)
++#define CM_OFFLINE_MANAGER            (1 << 2)
++
++typedef struct cm_subsys
++{
++    EP_SUBSYS         Subsys;
++    CM_RAIL            *Rails[EP_MAX_RAILS];
++} CM_SUBSYS;
++
++extern int  MachineId;
++
++extern void cm_node_disconnected (EP_RAIL *rail, unsigned nodeId);
++extern void cm_restart_node (EP_RAIL *rail, unsigned nodeId);
++extern void cm_restart_comms (CM_RAIL *cmRail);
++extern int  cm_init (EP_SYS *sys);
++
++extern void DisplayRail(EP_RAIL *rail);
++extern void DisplaySegs (EP_RAIL *rail);
++extern void DisplayStatus (EP_RAIL *rail);
++
++typedef struct proc_private
++{
++    struct nodeset_private *pr_next;
++    EP_RAIL                *pr_rail;
++    char                 *pr_data;
++    int                     pr_data_len;
++    unsigned              pr_off;
++    unsigned              pr_len;
++    DisplayInfo             pr_di;
++} PROC_PRIVATE;
++
++extern void    proc_character_fill (long mode, char *fmt, ...);
++extern int     proc_release (struct inode *inode, struct file *file);
++extern ssize_t proc_read (struct file *file, char *buf, size_t count, loff_t *ppos);
++
++
++extern void DisplayNodeMaps  (DisplayInfo *di, CM_RAIL *cmRail);
++extern void DisplayNodeSgmts (DisplayInfo *di, CM_RAIL *cmRail);
++extern void DisplayRailDo    (DisplayInfo *di, EP_RAIL *rail);
++
++extern int    cm_read_cluster(EP_RAIL *rail,char *page);
++extern void   cm_force_offline (EP_RAIL *rail, int offline, unsigned int reason);
++
++extern int    cm_svc_indicator_set      (EP_RAIL *rail, int svc_indicator);
++extern int    cm_svc_indicator_clear    (EP_RAIL *rail, int svc_indicator);
++extern int    cm_svc_indicator_is_set   (EP_RAIL *rail, int svc_indicator, int nodeId);
++extern int    cm_svc_indicator_bitmap   (EP_RAIL *rail, int svc_indicator, bitmap_t * bitmap, int low, int nnodes);
++
++/* cm_procfs.c */
++extern void   cm_procfs_init (CM_SUBSYS *subsys);
++extern void   cm_procfs_fini (CM_SUBSYS *subsys);
++extern void   cm_procfs_rail_init (CM_RAIL *rail);
++extern void   cm_procfs_rail_fini (CM_RAIL *rail);
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++#endif /* __ELAN_CM_H */
++
+Index: linux-2.6.5/include/elan/compat.h
+===================================================================
+--- linux-2.6.5.orig/include/elan/compat.h     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan/compat.h  2005-05-11 12:10:12.566913120 -0400
+@@ -0,0 +1,23 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: compat.h,v 1.1 2003/12/03 13:18:48 david Exp $ $Name: QSNETMODULES-4-31_20050321 $"
++/*      $Source: /cvs/master/quadrics/elanmod/modsrc/compat.h,v $*/
++
++#ifndef __ELAN_COMPAT_H
++#define __ELAN_COMPAT_H
++
++#define ELANMOD_STATS_MAP     ELAN_STATS_MAP
++
++#endif  /* __ELAN_COMPAT_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/include/elan/device.h
+===================================================================
+--- linux-2.6.5.orig/include/elan/device.h     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan/device.h  2005-05-11 12:10:12.566913120 -0400
+@@ -0,0 +1,62 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Limited.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: device.h,v 1.5 2003/09/24 13:55:37 david Exp $"
++/*      $Source: /cvs/master/quadrics/elanmod/modsrc/device.h,v $*/
++
++#ifndef __ELAN_DEVICE_H
++#define __ELAN_DEVICE_H
++
++/* non-kernel headings */
++typedef unsigned int ELAN_DEV_IDX;
++
++#if defined(__KERNEL__)
++
++/* device callbacks */
++#define ELAN_DEV_OPS_VERSION ((u_int)1)
++
++typedef struct elan_dev_ops
++{
++      /* dev info */
++      int (*get_position)          (void *user_data, ELAN_POSITION *position);
++      int (*set_position)          (void *user_data, unsigned short nodeId, unsigned short numNodes);
++
++      /* cap */
++
++      u_int  ops_version;
++} ELAN_DEV_OPS;
++
++typedef struct elan_dev_struct
++{
++      struct list_head node;
++
++      ELAN_DEV_IDX     devidx;
++      ELAN_DEVINFO    *devinfo;
++      void            *user_data;
++      ELAN_DEV_OPS *ops;
++} ELAN_DEV_STRUCT;
++
++/* device.c */
++extern ELAN_DEV_IDX         elan_dev_register   (ELAN_DEVINFO    *devinfo, 
++                                                  ELAN_DEV_OPS *ops,
++                                                  void            *userdata);
++extern int                  elan_dev_deregister (ELAN_DEVINFO *devinfo);
++
++extern ELAN_DEV_STRUCT * elan_dev_find       (ELAN_DEV_IDX devidx);
++
++extern ELAN_DEV_STRUCT * elan_dev_find_byrail(unsigned short deviceid, unsigned rail);
++extern int                  elan_dev_dump       (void);
++
++#endif /* __KERNEL__ */
++
++#endif /* __ELAN_DEVICE_H */
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.6.5/include/elan/devinfo.h
+===================================================================
+--- linux-2.6.5.orig/include/elan/devinfo.h    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan/devinfo.h 2005-05-11 12:10:12.567912968 -0400
+@@ -0,0 +1,92 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Limited.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: devinfo.h,v 1.11.2.1 2005/02/01 12:36:40 david Exp $"
++/*      $Source: /cvs/master/quadrics/elanmod/modsrc/devinfo.h,v $*/
++
++#ifndef __ELAN_DEVINFO_H
++#define __ELAN_DEVINFO_H
++
++#define ELAN_MAX_LEVELS                       8                       /* maximum number of levels in switch network */
++
++typedef struct elan_position
++{
++      unsigned        pos_mode;                               /* mode we're operating in */
++      unsigned        pos_nodeid;                             /* port this device connected to */
++      unsigned        pos_levels;                             /* number of levels to top switch */
++      unsigned        pos_nodes;                              /* number of nodes in the machine */
++      unsigned        pos_random_disabled;                    /* levels at which "random" routing is not possible */
++      unsigned char   pos_arity[ELAN_MAX_LEVELS];             /* number of downlinks per switch level */
++} ELAN_POSITION;
++
++#define ELAN4_PARAM_PCI_PADDING_FLAGS         0               /* A bit field, representing good places to burst across the pci                      */
++#define ELAN4_PARAM_EVENT_COPY_WIN            1               /* The num of cmds when it becomes quicker to send via event copy than write directly */
++#define ELAN4_PARAM_WRITE_COMBINING           2               /* If set the device supports bursts accesses across the pci bus                      */
++#define ELAN4_PARAM_DRIVER_FEATURES           11              /* device driver features */
++#define ELAN4_PARAM_COUNT                     12
++
++/* values for ELAN4_PARAM_DRIVER_FEATURES, dev_features */
++#define ELAN4_FEATURE_PCI_MAP         (1 << 0)                        /* must use pci mapping functions */
++#define ELAN4_FEATURE_64BIT_READ      (1 << 1)                        /* must perform 64 bit PIO reads */
++#define ELAN4_FEATURE_PIN_DOWN                (1 << 2)                        /* must pin down pages */
++#define ELAN4_FEATURE_NO_WRITE_COMBINE        (1 << 3)                        /* don't allow write combinig at all */
++#define ELAN4_FEATURE_NO_IOPROC               (1 << 4)                        /* unpatched kernel or disabled by procfs */
++#define ELAN4_FEATURE_NO_IOPROC_UPDATE        (1 << 5)                        /* don't do coproc update xlation loading */
++#define ELAN4_FEATURE_NO_PAGEFAULT    (1 << 6)                        /* don't do pagefaulting */
++#define ELAN4_FEATURE_NO_PREFETCH     (1 << 7)                        /* don't allow prefetching of elan sdram/cports */
++
++typedef struct elan_params
++{
++      unsigned        values[ELAN4_PARAM_COUNT];
++} ELAN_PARAMS;
++
++/* values for pos_mode */
++#define ELAN_POS_UNKNOWN              0                       /* network position unknown */
++#define ELAN_POS_MODE_SWITCHED                1                       /* connected to a switch */
++#define ELAN_POS_MODE_LOOPBACK                2                       /* loopback connector */
++#define ELAN_POS_MODE_BACKTOBACK      3                       /* cabled back-to-back to another node */
++
++typedef struct elan_devinfo
++{
++      unsigned short  dev_vendor_id;                          /* pci vendor id */
++      unsigned short  dev_device_id;                          /* pci device id */
++      unsigned char   dev_revision_id;                        /* pci revision id */
++      unsigned char   dev_instance;                           /* device instance number */
++      unsigned char   dev_rail;                               /* device rail number */
++
++      unsigned short  dev_driver_version;                     /* device driver version */
++      unsigned short  dev_params_mask;                        /* mask for valid entries in dev_params array */
++      ELAN_PARAMS     dev_params;                             /* device parametization */
++
++      unsigned        dev_num_down_links_value;               /* MRH hint as to machine size NEEDS coding XXXXX */
++} ELAN_DEVINFO;
++
++#define PCI_VENDOR_ID_QUADRICS                0x14fc
++#define PCI_DEVICE_ID_ELAN3           0x0000
++#define   PCI_REVISION_ID_ELAN3_REVA  0x0000
++#define   PCI_REVISION_ID_ELAN3_REVB  0x0001
++#define PCI_DEVICE_ID_ELAN4           0x0001
++#define   PCI_REVISION_ID_ELAN4_REVA  0x0000
++#define   PCI_REVISION_ID_ELAN4_REVB  0x0001
++
++#if defined(__KERNEL__)
++/* devinfo.c */
++#include <elan/capability.h>
++#include <elan/device.h>
++extern int elan_get_devinfo  (ELAN_DEV_IDX devidx, ELAN_DEVINFO  *devinfo);
++extern int elan_get_position (ELAN_DEV_IDX devidx, ELAN_POSITION *position);
++extern int elan_set_position (ELAN_DEV_IDX devidx, unsigned short nodeId, unsigned short numNodes);
++#endif /* __KERNEL__ */
++
++
++#endif /* __ELAN_DEVINFO_H */
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.6.5/include/elan/elanmoddebug.h
+===================================================================
+--- linux-2.6.5.orig/include/elan/elanmoddebug.h       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan/elanmoddebug.h    2005-05-11 12:10:12.567912968 -0400
+@@ -0,0 +1,63 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef _ELAN_DEBUG_H
++#define _ELAN_DEBUG_H
++
++
++#ident "$Id: elanmoddebug.h,v 1.5 2003/09/24 13:55:37 david Exp $"
++/*      $Source: /cvs/master/quadrics/elanmod/modsrc/elanmoddebug.h,v $ */
++
++#if defined(__KERNEL__)
++
++/* 0 | QSNET_DEBUG_BUFFER | QSNET_DEBUG_CONSOLE */
++extern int elan_debug_mode; 
++extern int elan_debug_mask;
++
++#define ELAN_DBG_VP           0x00000001
++#define ELAN_DBG_CAP            0x00000002
++#define ELAN_DBG_CTRL           0x00000004
++#define ELAN_DBG_SYS_FN         0x00000008
++#define ELAN_DBG_ALL          0xffffffff
++
++
++#if defined(DEBUG_PRINTF)
++#  define ELAN_DEBUG0(m,fmt)                  ((elan_debug_mask&(m)) ? qsnet_debugf(elan_debug_mode,fmt)             : (void)0)
++#  define ELAN_DEBUG1(m,fmt,a)                        ((elan_debug_mask&(m)) ? qsnet_debugf(elan_debug_mode,fmt,a)           : (void)0)
++#  define ELAN_DEBUG2(m,fmt,a,b)              ((elan_debug_mask&(m)) ? qsnet_debugf(elan_debug_mode,fmt,a,b)         : (void)0)
++#  define ELAN_DEBUG3(m,fmt,a,b,c)            ((elan_debug_mask&(m)) ? qsnet_debugf(elan_debug_mode,fmt,a,b,c)       : (void)0)
++#  define ELAN_DEBUG4(m,fmt,a,b,c,d)          ((elan_debug_mask&(m)) ? qsnet_debugf(elan_debug_mode,fmt,a,b,c,d)     : (void)0)
++#  define ELAN_DEBUG5(m,fmt,a,b,c,d,e)                ((elan_debug_mask&(m)) ? qsnet_debugf(elan_debug_mode,fmt,a,b,c,d,e)   : (void)0)
++#  define ELAN_DEBUG6(m,fmt,a,b,c,d,e,f)      ((elan_debug_mask&(m)) ? qsnet_debugf(elan_debug_mode,fmt,a,b,c,d,e,f) : (void)0)
++#ifdef __GNUC__
++#  define ELAN_DEBUG(m,args...)                       ((elan_debug_mask&(m)) ? qsnet_debugf(elan_debug_mode, ##args)         : (void)0)
++#endif
++
++#else
++
++#  define ELAN_DEBUG0(m,fmt)                  (0)
++#  define ELAN_DEBUG1(m,fmt,a)                        (0)
++#  define ELAN_DEBUG2(m,fmt,a,b)              (0)
++#  define ELAN_DEBUG3(m,fmt,a,b,c)            (0)
++#  define ELAN_DEBUG4(m,fmt,a,b,c,d)          (0)
++#  define ELAN_DEBUG5(m,fmt,a,b,c,d,e)                (0)
++#  define ELAN_DEBUG6(m,fmt,a,b,c,d,e,f)      (0)
++#ifdef __GNUC__
++#  define ELAN_DEBUG(m,args...)
++#endif
++
++#endif /* DEBUG_PRINTF */
++
++
++#endif /* __KERNEL__ */
++#endif /* _ELAN_DEBUG_H */
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.6.5/include/elan/elanmod.h
+===================================================================
+--- linux-2.6.5.orig/include/elan/elanmod.h    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan/elanmod.h 2005-05-11 12:10:12.567912968 -0400
+@@ -0,0 +1,59 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Limited.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: elanmod.h,v 1.10 2004/06/18 09:28:16 mike Exp $"
++/*      $Source: /cvs/master/quadrics/elanmod/modsrc/elanmod.h,v $*/
++
++#ifndef __ELAN_MOD_H
++#define __ELAN_MOD_H
++
++#include <elan/devinfo.h>
++#include <elan/device.h>
++#include <elan/capability.h>
++#include <elan/stats.h>
++
++#if defined(__KERNEL__)
++
++#include <elan/elanmoddebug.h>
++
++extern kmutex_t elan_mutex;
++
++/* elan_general.c */
++extern int elan_init(void);
++extern int elan_fini(void);
++
++/* return codes, -ve => errno, +ve => success */
++#define ELAN_CAP_OK  (0)
++#define ELAN_CAP_RMS (1)
++
++#define ELAN_USER_ATTACH    (1)
++#define ELAN_USER_DETACH    (2)
++#define ELAN_USER_P2P       (3)
++#define ELAN_USER_BROADCAST (4)
++
++extern int elanmod_classify_cap (ELAN_POSITION *position, ELAN_CAPABILITY *cap, unsigned use);
++
++#define ELAN_USER_BASE_CONTEXT_NUM    0x000                   /* first user allowable context */
++#define ELAN_USER_TOP_CONTEXT_NUM     0x7FF                   /* last user allowable context */
++
++#define ELAN_RMS_BASE_CONTEXT_NUM     0x400                   /* reserved for RMS allocation */
++#define ELAN_RMS_TOP_CONTEXT_NUM      0x7FF
++
++#define ELAN_USER_CONTEXT(ctx)        ((ctx) >= ELAN_USER_BASE_CONTEXT_NUM && \
++                                       (ctx) <= ELAN_USER_TOP_CONTEXT_NUM)    
++
++#define ELAN_RMS_CONTEXT(ctx)          ((ctx) >= ELAN_RMS_BASE_CONTEXT_NUM && \
++                                       (ctx) <= ELAN_RMS_TOP_CONTEXT_NUM)    
++#endif /* __KERNEL__ */
++
++#endif /* __ELAN_MOD_H */
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.6.5/include/elan/elanmod_linux.h
+===================================================================
+--- linux-2.6.5.orig/include/elan/elanmod_linux.h      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan/elanmod_linux.h   2005-05-11 12:10:12.568912816 -0400
+@@ -0,0 +1,140 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: elanmod_linux.h,v 1.6 2003/09/29 15:36:20 mike Exp $"
++/*      $Source: /cvs/master/quadrics/elanmod/modsrc/elanmod_linux.h,v $*/
++
++#ifndef __ELAN_MOD_LINUX_H
++#define __ELAN_MOD_LINUX_H
++
++#define ELANCRTL_USER_BASE            0x40
++
++/* stats */
++typedef struct elanctrl_stats_get_next_struct
++{
++      ELAN_STATS_IDX   statidx; 
++      ELAN_STATS_IDX  *next_statidx; /* return value */
++} ELANCTRL_STATS_GET_NEXT_STRUCT;
++#define ELANCTRL_STATS_GET_NEXT   _IOR   ('e', ELANCRTL_USER_BASE + 0,  ELANCTRL_STATS_GET_NEXT_STRUCT)
++
++typedef struct elanctrl_stats_find_index_struct
++{
++      caddr_t          block_name;
++      ELAN_STATS_IDX  *statidx; /* return value */
++      uint        *num_entries; /* return value */
++} ELANCTRL_STATS_FIND_INDEX_STRUCT;
++#define ELANCTRL_STATS_FIND_INDEX   _IOR   ('e', ELANCRTL_USER_BASE + 1,  ELANCTRL_STATS_FIND_INDEX_STRUCT)
++
++typedef struct elanctrl_stats_get_block_info_struct
++{
++      ELAN_STATS_IDX  statidx; 
++      caddr_t       block_name; /* return value */
++      uint        *num_entries; /* return value */
++} ELANCTRL_STATS_GET_BLOCK_INFO_STRUCT;
++#define ELANCTRL_STATS_GET_BLOCK_INFO   _IOR   ('e', ELANCRTL_USER_BASE + 2, ELANCTRL_STATS_GET_BLOCK_INFO_STRUCT)
++
++typedef struct elanctrl_stats_get_index_name_struct
++{
++      ELAN_STATS_IDX statidx; 
++      uint           index;
++      caddr_t        name; /* return value */
++} ELANCTRL_STATS_GET_INDEX_NAME_STRUCT;
++#define ELANCTRL_STATS_GET_INDEX_NAME   _IOR   ('e', ELANCRTL_USER_BASE + 3, ELANCTRL_STATS_GET_INDEX_NAME_STRUCT)
++
++typedef struct elanctrl_stats_clear_block_struct
++{
++      ELAN_STATS_IDX statidx; 
++} ELANCTRL_STATS_CLEAR_BLOCK_STRUCT;
++#define ELANCTRL_STATS_CLEAR_BLOCK   _IOR   ('e', ELANCRTL_USER_BASE + 4, ELANCTRL_STATS_CLEAR_BLOCK_STRUCT)
++
++typedef struct elanctrl_stats_get_block_struct
++{
++      ELAN_STATS_IDX statidx; 
++      uint           entries;  
++      ulong         *values; /* return values */
++} ELANCTRL_STATS_GET_BLOCK_STRUCT;
++#define ELANCTRL_STATS_GET_BLOCK        _IOR   ('e', ELANCRTL_USER_BASE + 5, ELANCTRL_STATS_GET_BLOCK_STRUCT)
++
++
++typedef struct elanctrl_get_devinfo_struct
++{
++      ELAN_DEV_IDX  devidx; 
++      ELAN_DEVINFO *devinfo; /* return values */
++} ELANCTRL_GET_DEVINFO_STRUCT;
++#define ELANCTRL_GET_DEVINFO        _IOR   ('e', ELANCRTL_USER_BASE + 6, ELANCTRL_GET_DEVINFO_STRUCT)
++
++typedef struct elanctrl_get_position_struct
++{
++      ELAN_DEV_IDX   devidx; 
++      ELAN_POSITION *position; /* return values */
++} ELANCTRL_GET_POSITION_STRUCT;
++#define ELANCTRL_GET_POSITION        _IOR   ('e', ELANCRTL_USER_BASE + 7, ELANCTRL_GET_POSITION_STRUCT)
++
++typedef struct elanctrl_set_position_struct
++{
++      ELAN_DEV_IDX   devidx; 
++      unsigned short nodeId;
++      unsigned short numNodes;
++} ELANCTRL_SET_POSITION_STRUCT;
++#define ELANCTRL_SET_POSITION        _IOR   ('e', ELANCRTL_USER_BASE + 8, ELANCTRL_SET_POSITION_STRUCT)
++
++typedef struct elanctrl_create_cap_struct
++{
++      ELAN_CAPABILITY cap;
++} ELANCTRL_CREATE_CAP_STRUCT;
++#define ELANCTRL_CREATE_CAP             _IOW   ('e', ELANCRTL_USER_BASE + 9, ELANCTRL_CREATE_CAP_STRUCT)
++
++typedef struct elanctrl_destroy_cap_struct
++{
++      ELAN_CAPABILITY cap;
++} ELANCTRL_DESTROY_CAP_STRUCT;
++#define ELANCTRL_DESTROY_CAP             _IOW   ('e', ELANCRTL_USER_BASE + 10, ELANCTRL_DESTROY_CAP_STRUCT)
++
++typedef struct elanctrl_create_vp_struct
++{
++      ELAN_CAPABILITY cap;
++      ELAN_CAPABILITY map;
++} ELANCTRL_CREATE_VP_STRUCT;
++#define ELANCTRL_CREATE_VP             _IOW   ('e', ELANCRTL_USER_BASE + 11, ELANCTRL_CREATE_VP_STRUCT)
++
++typedef struct elanctrl_destroy_vp_struct
++{
++      ELAN_CAPABILITY cap;
++      ELAN_CAPABILITY map;
++} ELANCTRL_DESTROY_VP_STRUCT;
++#define ELANCTRL_DESTROY_VP          _IOW   ('e', ELANCRTL_USER_BASE + 12, ELANCTRL_DESTROY_VP_STRUCT)
++
++#define ELANCTRL_DEBUG_DUMP          _IO    ('e', ELANCRTL_USER_BASE + 13)
++
++typedef struct elanctrl_get_caps_struct
++{
++      uint            *number_of_results;
++      uint             array_size;
++      ELAN_CAP_STRUCT *caps;
++} ELANCTRL_GET_CAPS_STRUCT;
++#define ELANCTRL_GET_CAPS          _IOW   ('e', ELANCRTL_USER_BASE + 14, ELANCTRL_GET_CAPS_STRUCT)
++
++
++typedef struct elanctrl_debug_buffer_struct
++{
++      caddr_t buffer;
++      int     size;
++} ELANCTRL_DEBUG_BUFFER_STRUCT;
++#define ELANCTRL_DEBUG_BUFFER _IOW ('e', ELANCRTL_USER_BASE + 15, ELANCTRL_DEBUG_BUFFER_STRUCT)
++
++#define ELANMOD_PROCFS_IOCTL      "/proc/qsnet/elan/ioctl"
++#define ELANMOD_PROCFS_VERSION    "/proc/qsnet/elan/version"
++#define ELANMOD_PROCFS_DEBUG_MASK "/proc/qsnet/elan/debug_mask"
++#define ELANMOD_PROCFS_DEBUG_MODE "/proc/qsnet/elan/debug_mode"
++
++#endif /* __ELAN_MOD_LINUX_H */
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.6.5/include/elan/elanmod_subsystem.h
+===================================================================
+--- linux-2.6.5.orig/include/elan/elanmod_subsystem.h  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan/elanmod_subsystem.h       2005-05-11 12:10:12.568912816 -0400
+@@ -0,0 +1,138 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Limited.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN_SUBSYSTEM_H
++#define __ELAN_SUBSYSTEM_H
++
++#include <sys/types.h>
++#include <sys/param.h>
++
++#if defined( __KERNEL__) 
++int elan_configure(
++    cfg_op_t op,
++    caddr_t  indata,
++    ulong    indata_size,
++    caddr_t  outdata,
++    ulong    outdata_size);
++#endif
++
++#define ELAN_KMOD_CODE(x)     ((x)+CFG_OP_SUBSYS_MIN)
++#define ELAN_MAX_KMOD_CODES 100
++
++#define ELAN_SUBSYS "elan"
++
++#define ELAN_STATS_GET_NEXT   0x01
++typedef struct {
++      ELAN_STATS_IDX statidx;
++      ELAN_STATS_IDX *next_statidx;   
++} elan_stats_get_next_struct;
++
++
++#define ELAN_STATS_FIND_INDEX   0x02
++typedef struct {
++      caddr_t          block_name;
++      ELAN_STATS_IDX  *statidx; /* return value */
++      uint        *num_entries; /* return value */
++} elan_stats_find_index_struct;
++
++#define ELAN_STATS_GET_BLOCK_INFO  0x03
++typedef struct {
++      ELAN_STATS_IDX  statidx; 
++      caddr_t       block_name; /* return value */
++      uint        *num_entries; /* return value */
++} elan_stats_get_block_info_struct;
++
++#define ELAN_STATS_GET_INDEX_NAME  0x04
++typedef struct {
++      ELAN_STATS_IDX statidx; 
++      uint           index;
++      caddr_t        name; /* return value */
++} elan_stats_get_index_name_struct;
++
++#define ELAN_STATS_CLEAR_BLOCK  0x05
++typedef struct {
++      ELAN_STATS_IDX statidx; 
++} elan_stats_clear_block_struct;
++
++#define ELAN_STATS_GET_BLOCK     0x06
++typedef struct 
++{
++      ELAN_STATS_IDX statidx; 
++      uint           entries;  
++      ulong         *values; /* return values */
++} elan_stats_get_block_struct;
++
++#define ELAN_GET_DEVINFO     0x07
++typedef struct 
++{
++      ELAN_DEV_IDX  devidx; 
++      ELAN_DEVINFO *devinfo; /* return values */
++} elan_get_devinfo_struct;
++
++#define ELAN_GET_POSITION  0x08
++typedef struct {
++      ELAN_DEV_IDX   devidx; 
++      ELAN_POSITION *position; /* return values */
++} elan_get_position_struct;
++
++#define ELAN_SET_POSITION   0x09
++typedef struct {
++      ELAN_DEV_IDX   devidx; 
++      unsigned short nodeId;
++      unsigned short numNodes;
++} elan_set_position_struct;
++
++#define ELAN_CREATE_CAP  0x0a
++typedef struct {
++      ELAN_CAPABILITY cap;
++} elan_create_cap_struct;
++
++#define ELAN_DESTROY_CAP    0x0b
++typedef struct {
++      ELAN_CAPABILITY cap;
++} elan_destroy_cap_struct;
++
++#define ELAN_CREATE_VP   0x0c
++typedef struct {
++      ELAN_CAPABILITY cap;
++      ELAN_CAPABILITY map;
++} elan_create_vp_struct;
++
++#define ELAN_DESTROY_VP    0x0d
++typedef struct {
++      ELAN_CAPABILITY cap;
++      ELAN_CAPABILITY map;
++} elan_destroy_vp_struct;
++
++
++#define ELAN_DEBUG_DUMP   0x0e
++
++#define ELAN_GET_CAPS    0x0f
++typedef struct {
++      uint            *number_of_results;
++      uint             array_size;
++      ELAN_CAP_STRUCT *caps;
++} elan_get_caps_struct;
++
++#define ELAN_DEBUG_BUFFER 0x10
++typedef struct {
++      caddr_t addr;
++      int     len;
++} elan_debug_buffer_struct;
++
++#define ELANMOD_PROCFS_IOCTL      "/proc/qsnet/elan/ioctl"
++#define ELANMOD_PROCFS_VERSION    "/proc/qsnet/elan/version"
++#define ELANMOD_PROCFS_DEBUG_MASK "/proc/qsnet/elan/debug_mask"
++#define ELANMOD_PROCFS_DEBUG_MODE "/proc/qsnet/elan/debug_mode"
++
++#endif /* __ELAN_SUBSYSTEM_H */
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.6.5/include/elan/epcomms.h
+===================================================================
+--- linux-2.6.5.orig/include/elan/epcomms.h    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan/epcomms.h 2005-05-11 12:10:12.569912664 -0400
+@@ -0,0 +1,635 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN_EPCOMMS_H
++#define __ELAN_EPCOMMS_H
++
++#ident "$Id: epcomms.h,v 1.44.2.2 2004/11/12 10:54:50 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/epcomms.h,v $ */
++
++#include <elan/kcomm.h>
++#include <elan/bitmap.h>
++
++#define EPCOMMS_SUBSYS_NAME   "epcomms"
++
++/* message service numbers */
++#define EP_MSG_SVC_EIP512             0x00                            /* Quadrics EIP services */
++#define EP_MSG_SVC_EIP1K              0x01
++#define EP_MSG_SVC_EIP2K              0x02
++#define EP_MSG_SVC_EIP4K              0x03
++#define EP_MSG_SVC_EIP8K              0x04
++#define EP_MSG_SVC_EIP16K             0x05
++#define EP_MSG_SVC_EIP32K             0x06
++#define EP_MSG_SVC_EIP64K             0x07
++#define EP_MSG_SVC_EIP128K            0x08
++
++#define EP_MSG_SVC_PFS                        0x09                            /* Quadrics PFS rpc service */
++
++#define EP_MSG_SVC_PORTALS_SMALL      0x10                            /* Lustre Portals */
++#define EP_MSG_SVC_PORTALS_LARGE      0x11
++
++#define EP_MSG_NSVC                   0x40                            /* Max number of services */
++
++#define EP_MSGQ_ADDR(qnum)            (EP_EPCOMMS_QUEUE_BASE + (qnum) * EP_QUEUE_DESC_SIZE)
++
++/*
++ * EP_ENVELOPE
++ *   Messages are sent by sending an envelope to the destination
++ *   describing the source buffers to transfer.  The receiving thread
++ *   then allocates a receive buffer and fetches the data by issuing
++ *   "get" dmas.
++ *
++ * NOTE:  envelopes are not explicitly converted to network byte order
++ *        since they are always transferred little endian as they are
++ *        copied to/from elan memory using word operations.
++ */
++typedef struct ep_envelope
++{
++    uint32_t    Version;                                      /* Protocol version field */
++
++    EP_ATTRIBUTE  Attr;                                       /* Attributes */
++
++    EP_XID      Xid;                                          /* transaction id */
++
++    uint32_t    NodeId;                                       /* Source processor */
++    uint32_t    Range;                                        /* range we're sending to (high << 16 | low) */
++
++    EP_ADDR     TxdRail;                                      /* address of per-rail txd */
++    EP_NMD      TxdMain;                                      /* address of main memory portion of txd */
++
++    uint32_t      nFrags;                                     /* # fragments */
++    EP_NMD      Frags[EP_MAXFRAG];                            /* network mapping handles of source data */
++
++    uint32_t      CheckSum;                                     /* holds the check sum value when active 
++                                                               * must be after all members to be checksum'd
++                                                               */
++
++    uint32_t    Pad[6];                                       /* Pad to 128 bytes */
++} EP_ENVELOPE;
++
++#define EP_ENVELOPE_VERSION           0xdac10001
++#define EP_ENVELOPE_SIZE              roundup (sizeof (EP_ENVELOPE), EP_BLK_SIZE)
++
++/*
++ * RPC payload - this small amount of data is transfered in
++ * the envelope for RPCs
++ */
++typedef struct ep_payload
++{
++    uint32_t  Data[128/sizeof(uint32_t)];
++} EP_PAYLOAD;
++
++#define EP_PAYLOAD_SIZE                       roundup (sizeof (EP_PAYLOAD), EP_BLK_SIZE)
++
++#define EP_INPUTQ_SIZE                        (EP_ENVELOPE_SIZE + EP_PAYLOAD_SIZE)
++
++/*
++ * EP_STATUSBLK
++ *   RPC completion transfers a status block to the client.
++ */
++typedef struct ep_statusblk
++{
++    uint32_t  Data[128/sizeof(uint32_t)];
++} EP_STATUSBLK;
++
++#define EP_STATUSBLK_SIZE             roundup (sizeof(EP_STATUSBLK), EP_BLK_SIZE)
++
++#define EP_RANGE(low,high)            ((high) << 16 | (low))
++#define EP_RANGE_LOW(range)           ((range) & 0xFFFF)
++#define EP_RANGE_HIGH(range)          (((range) >> 16) & 0xFFFF)
++
++/* return codes from functions, + 'res' parameter to txd callback, ep_rxd_status() */
++typedef enum
++{
++    EP_SUCCESS                = 0,                                    /* message sent/received successfully */
++    EP_RXD_PENDING    = -1,                                   /* rxd not completed by thread */
++    EP_CONN_RESET     = -2,                                   /* virtual circuit reset */
++    EP_NODE_DOWN      = -3,                                   /* node down - transmit not attempted */
++    EP_MSG_TOO_BIG      = -4,                                 /* received message larger than buffer */
++    EP_ENOMEM         = -5,                                   /* memory alloc failed */
++    EP_EINVAL         = -6,                                   /* invalid parameters */
++    EP_SHUTDOWN               = -7,                                   /* receiver is being shut down */
++} EP_STATUS;
++
++/* forward declarations */
++typedef struct ep_rxd                 EP_RXD;
++typedef struct ep_txd                 EP_TXD;
++typedef struct ep_rcvr_rail   EP_RCVR_RAIL;
++typedef struct ep_rcvr                EP_RCVR;
++typedef struct ep_xmtr_rail   EP_XMTR_RAIL;
++typedef struct ep_xmtr                EP_XMTR;
++typedef struct ep_comms_rail    EP_COMMS_RAIL;
++typedef struct ep_comms_subsys  EP_COMMS_SUBSYS;
++
++typedef struct ep_rcvr_stats          EP_RCVR_STATS;
++typedef struct ep_xmtr_stats          EP_XMTR_STATS;
++typedef struct ep_rcvr_rail_stats     EP_RCVR_RAIL_STATS;
++typedef struct ep_xmtr_rail_stats     EP_XMTR_RAIL_STATS;
++
++typedef void (EP_RXH)(EP_RXD *rxd);                           /* callback function from receive completion */
++typedef void (EP_TXH)(EP_TXD *txd, void *arg, EP_STATUS res); /* callback function from transmit completion  */
++
++/* Main memory portion shared descriptor */
++typedef struct ep_rxd_main
++{
++    EP_ENVELOPE               Envelope;                               /* 128 byte aligned envelope */
++    EP_PAYLOAD                Payload;                                /* 128 byte aligned payload */
++    bitmap_t          Bitmap[BT_BITOUL(EP_MAX_NODES)];        /* broadcast bitmap */
++    EP_STATUSBLK      StatusBlk;                              /* RPC status block to return */
++    uint64_t          Next;                                   /* linked list when on active list (main address) */
++    int32_t           Len;                                    /* Length of message received */
++} EP_RXD_MAIN;
++
++#define EP_RXD_MAIN_SIZE      roundup (sizeof (EP_RXD_MAIN), EP_BLK_SIZE)
++
++/* Phases for message/rpc */
++#ifndef __ELAN__
++
++/* Kernel memory portion of per-rail receive descriptor */
++typedef struct ep_rxd_rail
++{
++    struct list_head    Link;                                 /* linked on freelist */
++    EP_RCVR_RAIL       *RcvrRail;                             /* rvcr we're associated with */
++    
++    EP_RXD           *Rxd;                                    /* receive descriptor we're bound to */
++} EP_RXD_RAIL;
++
++#define RXD_BOUND2RAIL(rxdRail,rcvrRail)      ((rxdRail) != NULL && ((EP_RXD_RAIL *) (rxdRail))->RcvrRail == (EP_RCVR_RAIL *) rcvrRail)
++
++struct ep_rxd
++{
++    struct list_head  Link;                                   /* linked on free/active list */
++    EP_RCVR          *Rcvr;                                   /* owning receiver */
++
++    EP_RXD_MAIN              *RxdMain;                                /* shared main memory portion. */
++    EP_NMD            NmdMain;                                /*  and network mapping descriptor */
++
++    EP_RXD_RAIL              *RxdRail;                                /* per-rail rxd we're bound to */
++    
++    EP_RXH           *Handler;                                /* completion function */
++    void             *Arg;                                    /*    and arguement */
++
++    unsigned int      State;                                  /* RXD status (active,stalled,failed) */
++
++    EP_NMD            Data;                                   /* network mapping descriptor for user buffer */
++
++    int                       nFrags;                                 /* network mapping descriptor for put/get/complete */
++    EP_NMD            Local[EP_MAXFRAG];
++    EP_NMD            Remote[EP_MAXFRAG];
++
++    long              NextRunTime;                            /* time to resend failover/map requests */
++    EP_XID            MsgXid;                                 /*   and transaction id */
++
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++    struct list_head  CheckSumLink;                           /* linked on check sum list */
++#endif
++};
++
++#define EP_NUM_RXD_PER_BLOCK  16
++
++/* rxd->State */
++#define EP_RXD_FREE           0
++
++#define EP_RXD_RECEIVE_UNBOUND        1
++#define EP_RXD_RECEIVE_ACTIVE 2
++
++#define EP_RXD_PUT_ACTIVE     3
++#define EP_RXD_PUT_STALLED    4
++#define EP_RXD_GET_ACTIVE     5
++#define EP_RXD_GET_STALLED    6
++
++#define EP_RXD_COMPLETE_ACTIVE        7
++#define EP_RXD_COMPLETE_STALLED       8
++
++#define EP_RXD_RPC_IN_PROGRESS        9
++#define EP_RXD_COMPLETED      10      
++
++#define EP_RXD_BEEN_ABORTED   11                              /* rxd was aborted while in a private state */
++
++typedef struct ep_rxd_block
++{
++    struct list_head  Link;
++
++    EP_NMD            NmdMain;
++
++    EP_RXD            Rxd[EP_NUM_RXD_PER_BLOCK];
++} EP_RXD_BLOCK;
++
++struct ep_rcvr_rail_stats 
++{
++    EP_STATS_COUNT rx;
++    EP_STATS_COUNT rx_len;
++};
++
++struct ep_rcvr_rail
++{
++    EP_RCVR          *Rcvr;                                   /* associated receiver */
++    EP_COMMS_RAIL      *CommsRail;                            /* comms rail */
++
++    struct proc_dir_entry *procfs_root;                         /* root of this rcvr_rail's procfs entry */
++    EP_RCVR_RAIL_STATS     stats;                               /* generic rcvr_rail stats */
++};
++
++struct ep_rcvr_stats
++{
++    EP_STATS_COUNT rx;
++    EP_STATS_COUNT rx_len;
++};
++
++struct ep_rcvr
++{
++    struct list_head  Link;                                   /* queued on subsystem */
++    EP_COMMS_SUBSYS  *Subsys;                                 /* kernel comms subsystem */
++    EP_SERVICE        Service;                                /* service number */
++
++    unsigned int      InputQueueEntries;                      /* # entries on receive queue */
++
++    EP_RAILMASK             RailMask;                                 /* bitmap of which rails are available */
++    EP_RCVR_RAIL     *Rails[EP_MAX_RAILS];
++
++    spinlock_t              Lock;                                     /* spinlock for rails/receive lists */
++
++    struct list_head  ActiveDescList;                         /* List of pending/active receive descriptors */
++
++    EP_XID_CACHE      XidCache;                                       /* XID cache (protected by Lock) */
++
++    struct list_head  FreeDescList;                           /* List of free receive descriptors */
++    unsigned int      FreeDescCount;                          /*   and number on free list */
++    unsigned int      TotalDescCount;                           /*   total number created */
++    spinlock_t              FreeDescLock;                             /*   and lock for free list */
++    kcondvar_t              FreeDescSleep;                            /*   with place to sleep for rx desc */
++    int                     FreeDescWanted;                           /*   and flag */
++    struct list_head  DescBlockList;
++
++    unsigned int      ForwardRxdCount;                                /* count of rxd's being forwarded */
++    unsigned int      CleanupWaiting;                         /* waiting for cleanup */
++    kcondvar_t              CleanupSleep;                             /*   and place to sleep */
++
++    struct proc_dir_entry *procfs_root;                         /* place where this rcvr's proc entry is */
++    EP_RCVR_STATS          stats;                                    
++};
++
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++#define EP_ENVELOPE_CHECK_SUM      (1<<31)
++extern uint32_t ep_calc_check_sum (EP_SYS *sys, EP_ENVELOPE *env, EP_NMD *nmd, int nFrags);
++#endif
++
++#endif /* ! __ELAN__ */
++
++typedef struct ep_txd_main
++{
++    EP_STATUSBLK      StatusBlk;                              /* RPC status block */
++    bitmap_t          Bitmap[BT_BITOUL(EP_MAX_NODES)];                /* broadcast bitmap */
++} EP_TXD_MAIN;
++
++#define EP_TXD_MAIN_SIZE      roundup (sizeof (EP_TXD_MAIN), EP_BLK_SIZE)
++
++#ifndef __ELAN__
++typedef struct ep_txd_rail
++{
++    struct list_head  Link;                                   /* linked on freelist */
++    EP_XMTR_RAIL     *XmtrRail;                                       /* xmtr we're associated with */
++
++    EP_TXD         *Txd;                                      /* txd we're bound to */
++} EP_TXD_RAIL;
++
++#define TXD_BOUND2RAIL(rxdRail,xmtrRail)      ((txdRail) != NULL && ((EP_TXD_RAIL *) (txdRail))->XmtrRail == (EP_XMTR_RAIL *) xmtrRail)
++
++struct ep_txd
++{
++    struct list_head  Link;                                   /* linked on free/active list */
++    EP_XMTR        *Xmtr;                                     /* service we're associated with */
++
++    EP_TXD_MAIN            *TxdMain;                                  /* shared main memory portion */
++    EP_NMD          NmdMain;                                  /*   and network mapping descriptor */
++
++    EP_TXD_RAIL      *TxdRail;                                        /* per-rail txd for this phase */
++
++    EP_TXH         *Handler;                                  /* completion function */
++    void           *Arg;                                      /*    and arguement */
++    
++    unsigned short    NodeId;                                 /* node transmit is to. */
++    EP_SERVICE        Service;                                        /*    and seervice */
++
++    long              TimeStamp;                                 /* time we where created at, to find sends taking too long */
++    long            RetryTime;
++    EP_BACKOFF              Backoff;
++
++    EP_ENVELOPE             Envelope;                                 /* envelope for transmit */
++    EP_PAYLOAD              Payload;                                  /* payload for transmit */
++};
++
++#define EP_NUM_TXD_PER_BLOCK  16
++
++/* "phase" parameter to BindTxd */
++#define EP_TXD_PHASE_ACTIVE           1
++#define EP_TXD_PHASE_PASSIVE          2
++
++typedef struct ep_txd_block
++{
++    struct list_head  Link;
++    EP_NMD            NmdMain;
++    EP_TXD            Txd[EP_NUM_TXD_PER_BLOCK];              /* transmit descriptors */
++} EP_TXD_BLOCK;
++
++struct ep_xmtr_rail_stats
++{
++    EP_STATS_COUNT tx;
++    EP_STATS_COUNT tx_len;
++};
++
++struct ep_xmtr_rail
++{
++    EP_COMMS_RAIL      *CommsRail;                            /* associated comms rail */
++    EP_XMTR          *Xmtr;                                   /* associated transmitter */
++
++    struct proc_dir_entry *procfs_root;                         /* place where this xmtr's proc entry is */
++
++    EP_XMTR_RAIL_STATS     stats;
++};
++
++struct ep_xmtr_stats
++{
++    EP_STATS_COUNT tx;
++    EP_STATS_COUNT tx_len;
++};
++
++struct ep_xmtr
++{
++    struct list_head  Link;                                   /* Linked on subsys */
++    EP_COMMS_SUBSYS  *Subsys;                                 /* kernel comms subsystem */
++
++    EP_RAILMASK             RailMask;                                 /* bitmap of which rails are available */
++    EP_XMTR_RAIL     *Rails[EP_MAX_RAILS];                    /* per-rail state */
++
++    spinlock_t              Lock;                                     /* lock for active descriptor list */
++
++    struct list_head  ActiveDescList;                         /* list of active transmit descriptors */
++
++    EP_XID_CACHE      XidCache;                                       /* XID cache (protected by Lock) */
++
++    struct list_head  FreeDescList;                           /* List of free receive descriptors */
++    unsigned int      FreeDescCount;                          /*   and number on free list */
++    unsigned int      TotalDescCount;
++    spinlock_t              FreeDescLock;                             /*   and lock for free list */
++    kcondvar_t              FreeDescSleep;                            /*   with place to sleep for rx desc */
++    int                     FreeDescWanted;                           /*   and flag */
++    struct list_head  DescBlockList;
++
++    struct proc_dir_entry *procfs_root;                         /* place where this rcvr's proc entry is */
++    EP_XMTR_STATS          stats;   
++};
++
++/* forward descriptor */
++#define EP_TREE_ARITY         3
++
++typedef struct ep_fwd_desc
++{
++    struct list_head    Link;                                 /* linked on forward/free lists */
++    EP_RXD           *Rxd;                                    /* rxd to forward */
++    EP_NMD            Data;                                   /* nmd of subset of receive buffer */
++    unsigned          NumChildren;                            /*   number of places we're forwarding */
++    unsigned          Children[EP_TREE_ARITY];
++} EP_FWD_DESC;
++
++typedef struct ep_comms_ops
++{
++    void           (*DelRail) (EP_COMMS_RAIL *rail);
++    void           (*DisplayRail) (EP_COMMS_RAIL *rail);
++
++    struct {
++      void         (*AddRail) (EP_RCVR *rcvr, EP_COMMS_RAIL *rail);
++      void         (*DelRail) (EP_RCVR *rcvr, EP_COMMS_RAIL *rail);
++
++      long         (*Check) (EP_RCVR_RAIL *rcvrRail, long nextRunTime);
++
++      int          (*QueueRxd) (EP_RXD *rxd, EP_RCVR_RAIL *rcvrRail);
++      void         (*RpcPut)(EP_RXD *rxd, EP_NMD *local, EP_NMD *remote, unsigned nFrags);
++      void         (*RpcGet)(EP_RXD *rxd, EP_NMD *local, EP_NMD *remote, unsigned nFrags);
++      void         (*RpcComplete)(EP_RXD *rxd, EP_NMD *local, EP_NMD *remote, unsigned nFrags);
++
++      EP_RXD      *(*StealRxd)(EP_RCVR_RAIL *rcvrRail);
++
++      void         (*DisplayRcvr) (DisplayInfo *di, EP_RCVR_RAIL *rcvrRail);
++      void         (*DisplayRxd)  (DisplayInfo *di, EP_RXD_RAIL *rxdRail);
++
++      void         (*FillOutRailStats) (EP_RCVR_RAIL *rcvr_rail, char *str);
++
++    } Rcvr;
++
++    struct {
++      void         (*AddRail) (EP_XMTR *xmtr, EP_COMMS_RAIL *rail);
++      void         (*DelRail) (EP_XMTR *xmtr, EP_COMMS_RAIL *rail);
++
++      long         (*Check) (EP_XMTR_RAIL *xmtrRail, long nextRunTime);
++      
++      int          (*BindTxd) (EP_TXD *txd, EP_XMTR_RAIL *xmtrRail, unsigned int phase);
++      void         (*UnbindTxd) (EP_TXD *txd, unsigned int phase);
++      int          (*PollTxd) (EP_XMTR_RAIL *xmtrRail, EP_TXD_RAIL *txdRail, int how);
++
++      void         (*DisplayXmtr) (DisplayInfo *di, EP_XMTR_RAIL *xmtrRail);
++      void         (*DisplayTxd)  (DisplayInfo *di, EP_TXD_RAIL *txdRail);
++
++      int          (*CheckTxdState) (EP_TXD *txd);
++
++      void         (*FillOutRailStats) (EP_XMTR_RAIL *xmtr_rail, char *str);
++
++    } Xmtr;
++} EP_COMMS_OPS;
++
++#define EP_RAIL_OP(commsRail, Which)  (commsRail)->Ops.Which
++#define EP_RCVR_OP(rcvrRail, Which)   (rcvrRail)->CommsRail->Ops.Rcvr.Which
++#define EP_XMTR_OP(xmtrRail, Which)   (xmtrRail)->CommsRail->Ops.Xmtr.Which
++
++/* "how" parameter to PollTxd */
++#define POLL_TX_LIST          0
++#define ENABLE_TX_CALLBACK    1
++#define DISABLE_TX_CALLBACK   2
++
++struct ep_comms_rail
++{
++    struct list_head  Link;                                   /* Linked on subsys */
++    EP_RAIL          *Rail;                                   /* kernel comms rail */
++    EP_COMMS_SUBSYS    *Subsys;
++    EP_COMMS_OPS        Ops;
++
++    EP_COMMS_RAIL_STATS Stats;                                        /* statistics */
++};
++
++struct ep_comms_subsys
++{
++    EP_SUBSYS         Subsys;                                 /* is a kernel comms subsystem */
++
++    kmutex_t          Lock;                                   /* global lock */
++
++    EP_COMMS_STATS    Stats;                                  /* statistics */
++
++    struct list_head  Rails;                                  /* list of all rails */
++
++    struct list_head    Receivers;                            /* list of receivers */
++    struct list_head  Transmitters;                           /* and transmitters */
++
++    /* forward/allocator thread */
++    EP_KTHREAD                Thread;                                 /* place thread sleeps */
++
++    /* message passing "broadcast" forward lists */
++    spinlock_t                ForwardDescLock;                        /* Lock for broadcast forwarding */
++    struct list_head    ForwardDescList;                      /* List of rxd's to forward */
++
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++    spinlock_t                CheckSumDescLock;                       /* Lock for CheckSums */
++    struct list_head    CheckSumDescList;                     /* List of rxd's to be CheckSumed */
++#endif
++
++    EP_XMTR          *ForwardXmtr;                            /* and transmitter to forward with */
++};
++
++/* epcomms.c subsystem initialisation */
++extern unsigned int   epcomms_forward_limit;
++
++extern int          ep_comms_init (EP_SYS *sys);
++extern void           ep_comms_display (EP_SYS *sys, char *how);
++extern EP_RAILMASK    ep_rcvr_railmask (EP_SYS *epsys, EP_SERVICE service);
++
++/* epcomms_elan3.c */
++extern EP_COMMS_RAIL *ep3comms_add_rail (EP_SUBSYS *s, EP_SYS *sys, EP_RAIL *rail);
++
++/* epcomms_elan4.c */
++extern EP_COMMS_RAIL *ep4comms_add_rail (EP_SUBSYS *s, EP_SYS *sys, EP_RAIL *rail);
++
++/* epcommsTx.c */
++extern int            TxdShouldStabalise (EP_TXD_RAIL *txdRail, EP_RAIL *rail);
++extern void         FreeTxd (EP_XMTR *xmtr, EP_TXD *txd);
++
++extern unsigned int   ep_txd_lowat;
++extern long           ep_check_xmtr (EP_XMTR *xmtr, long nextRunTime);
++extern void           ep_display_xmtr (DisplayInfo *di, EP_XMTR *xmtr);
++extern void           ep_xmtr_flush_callback (EP_XMTR *xmtr, EP_XMTR_RAIL *xmtrRail);
++extern void           ep_xmtr_reloc_callback (EP_XMTR *xmtr, EP_XMTR_RAIL *xmtrRail);
++
++extern void           ep_xmtr_fillout_stats      (EP_XMTR      *xmtr,      char *str);
++extern void           ep_xmtr_rail_fillout_stats (EP_XMTR_RAIL *xmtr_rail, char *str);
++
++extern void           ep_xmtr_txd_stat (EP_XMTR *xmtr, EP_TXD *txd);
++
++/* epcommsRx.c */
++extern EP_RXD        *StealRxdFromOtherRail (EP_RCVR *rcvr);
++
++extern unsigned int   ep_rxd_lowat;
++extern long         ep_check_rcvr (EP_RCVR *rcvr, long nextRunTime);
++extern void           ep_rcvr_flush_callback (EP_RCVR *rcvr, EP_RCVR_RAIL *rcvrRail);
++extern void           ep_rcvr_reloc_callback (EP_RCVR *rcvr, EP_RCVR_RAIL *rcvrRail);
++extern void           ep_display_rcvr (DisplayInfo *di, EP_RCVR *rcvr, int full);
++
++extern long           ep_forward_rxds (EP_COMMS_SUBSYS *subsys, long nextRunTime);
++
++extern void           ep_rcvr_fillout_stats      (EP_RCVR      *rcvr,      char *str);
++extern void           ep_rcvr_rail_fillout_stats (EP_RCVR_RAIL *rcvr_rail, char *str);
++
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++extern void           ep_csum_rxds    (EP_COMMS_SUBSYS *subsys);
++extern void           ep_rxd_queue_csum (EP_RXD *rxd);
++#endif
++
++extern void           ep_rxd_received     (EP_RXD *rxd);
++extern void           ep_rxd_received_now (EP_RXD *rxd);
++
++/* ep_procfs.c */
++extern struct proc_dir_entry *ep_procfs_root;
++
++extern void ep_procfs_rcvr_xmtr_init(void);
++extern void ep_procfs_rcvr_xmtr_fini(void);
++
++extern void ep_procfs_rcvr_add(EP_RCVR *rcvr);
++extern void ep_procfs_rcvr_del(EP_RCVR *rcvr);
++
++extern void ep_procfs_rcvr_add_rail(EP_RCVR_RAIL *rcvrRail);
++extern void ep_procfs_rcvr_del_rail(EP_RCVR_RAIL *rcvrRail);
++
++extern void ep_procfs_xmtr_add(EP_XMTR *xmtr);
++extern void ep_procfs_xmtr_del(EP_XMTR *xmtr);
++
++extern void ep_procfs_xmtr_add_rail(EP_XMTR_RAIL *xmtrRail);
++extern void ep_procfs_xmtr_del_rail(EP_XMTR_RAIL *xmtrRail);
++
++
++/* Public Interface */
++
++
++/* epcomms.c message xmtr functions */
++extern EP_XMTR       *ep_alloc_xmtr (EP_SYS *sys);
++extern void           ep_free_xmtr (EP_XMTR *xmtr);
++
++extern EP_STATUS      ep_transmit_message (EP_XMTR *xmtr, unsigned int dest, EP_SERVICE service, EP_ATTRIBUTE attr, 
++                                         EP_TXH *handler, void *arg, EP_PAYLOAD *payload,
++                                         EP_NMD *nmd, int nFrag);
++extern EP_STATUS      ep_multicast_message (EP_XMTR *xmtr, unsigned int destLo, unsigned int destHi, bitmap_t *bitmap, 
++                                          EP_SERVICE service, EP_ATTRIBUTE attr, EP_TXH *handler, void *arg, 
++                                          EP_PAYLOAD *payload, EP_NMD *nmd, int nFrag);
++extern EP_STATUS      ep_transmit_rpc (EP_XMTR *xmtr, unsigned int dest, EP_SERVICE service, EP_ATTRIBUTE attr, 
++                                     EP_TXH *handler, void *arg, EP_PAYLOAD *payload,
++                                     EP_NMD *nmd, int nFrag);
++extern EP_STATUS      ep_multicast_forward (EP_XMTR *xmtr, unsigned int dest, EP_SERVICE service, EP_ATTRIBUTE attr, 
++                                          EP_TXH *handler, void *arg, EP_ENVELOPE *env, EP_PAYLOAD *payload, 
++                                          bitmap_t *bitmap, EP_NMD *nmd, int nFrags);
++
++/* epcomms.c functions for use with polled transmits */
++extern int            ep_poll_transmits (EP_XMTR *xmtr);
++extern int            ep_enable_txcallbacks (EP_XMTR *xmtr);
++extern int            ep_disable_txcallbacks (EP_XMTR *xmtr);
++
++/* epcomms.c message rcvr functions */
++extern EP_RCVR       *ep_alloc_rcvr (EP_SYS *sys, EP_SERVICE svc, unsigned int nenvelopes);
++extern void         ep_free_rcvr (EP_RCVR *rcvr);
++
++extern EP_STATUS      ep_queue_receive (EP_RCVR *rcvr, EP_RXH *handler, void *arg, EP_NMD *nmd, EP_ATTRIBUTE attr);
++extern void         ep_requeue_receive (EP_RXD *rxd, EP_RXH *handler, void *arg, EP_NMD *nmd, EP_ATTRIBUTE attr);
++extern EP_STATUS      ep_rpc_put (EP_RXD *rxd, EP_RXH *handler, void *arg, EP_NMD *from, EP_NMD *to, int nFrags);
++extern EP_STATUS      ep_rpc_get (EP_RXD *rxd, EP_RXH *handler, void *arg, EP_NMD *from, EP_NMD *to, int nFrags);
++extern EP_STATUS      ep_complete_rpc (EP_RXD *rxd, EP_RXH *handler, void *arg, EP_STATUSBLK *blk, 
++                                     EP_NMD *from, EP_NMD *to, int nFrags);
++extern void         ep_complete_receive (EP_RXD *rxd);
++
++/* railhints.c */
++extern int            ep_xmtr_bcastrail (EP_XMTR *xmtr, EP_RAILMASK allowedRails);
++extern int            ep_xmtr_prefrail (EP_XMTR *xmtr, EP_RAILMASK allowedRails, unsigned nodeId);
++extern EP_RAILMASK    ep_xmtr_availrails (EP_XMTR *xmtr);
++extern EP_RAILMASK    ep_xmtr_noderails (EP_XMTR *xmtr, unsigned nodeId);
++extern int            ep_rcvr_prefrail (EP_RCVR *rcvr, EP_RAILMASK allowedRails);
++extern EP_RAILMASK    ep_rcvr_availrails (EP_RCVR *rcvr);
++extern EP_RAILMASK    ep_rxd_railmask (EP_RXD *rxd);
++
++/* epcomms.c functions for accessing fields of rxds */
++extern void          *ep_rxd_arg(EP_RXD *rxd);
++extern int            ep_rxd_len(EP_RXD *rxd);
++extern EP_STATUS      ep_rxd_status(EP_RXD *rxd);
++extern int            ep_rxd_isrpc(EP_RXD *rxd);
++extern EP_ENVELOPE   *ep_rxd_envelope(EP_RXD *rxd);
++extern EP_PAYLOAD    *ep_rxd_payload(EP_RXD *rxd);
++extern int            ep_rxd_node(EP_RXD *rxd);
++extern EP_STATUSBLK  *ep_rxd_statusblk(EP_RXD *rxd);
++
++/* functions for accessing fields of txds */
++extern int            ep_txd_node(EP_TXD *txd);
++extern EP_STATUSBLK  *ep_txd_statusblk(EP_TXD *txd);
++
++/* functions for controlling how many processes are using module */
++extern void              ep_mod_dec_usecount (void);
++extern void              ep_mod_inc_usecount (void);
++
++extern EP_RAILMASK ep_xmtr_svc_indicator_railmask (EP_XMTR *xmtr, int svc_indicator, int nodeId);
++extern int ep_xmtr_svc_indicator_bitmap (EP_XMTR *xmtr, int svc_indicator, bitmap_t * bitmap, int low, int nnodes);
++
++#endif /* ! __ELAN__ */
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++#endif /* __ELAN_EPCOMMS_H */
++
+Index: linux-2.6.5/include/elan/epsvc.h
+===================================================================
+--- linux-2.6.5.orig/include/elan/epsvc.h      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan/epsvc.h   2005-05-11 12:10:12.570912512 -0400
+@@ -0,0 +1,36 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN_EPSVC_H
++#define __ELAN_EPSVC_H
++
++#ident "@(#)$Id: epsvc.h,v 1.9 2004/02/13 10:03:27 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/epsvc.h,v $ */
++
++
++#define EP_SVC_NUM_INDICATORS       8
++#define EP_SVC_INDICATOR_MAX_NAME  32
++
++#define EP_SVC_EIP    0
++#define EP_SVC_NAMES  {"eip", "1", "2", "3", "4", "5", "6", "7"};
++
++#if defined(__KERNEL__)
++extern int         ep_svc_indicator_set      (EP_SYS *epsys, int svc_indicator);
++extern int         ep_svc_indicator_clear    (EP_SYS *epsys, int svc_indicator);
++extern int         ep_svc_indicator_is_set   (EP_SYS *epsys, int svc_indicator, int nodeId);
++extern int         ep_svc_indicator_bitmap   (EP_SYS *epsys, int svc_indicator, bitmap_t * bitmap, int low, int nnodes);
++extern EP_RAILMASK ep_svc_indicator_railmask (EP_SYS *epsys, int svc_indicator, int nodeId);
++#endif
++
++#endif /* __ELAN_EPSVC_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/include/elan/kalloc.h
+===================================================================
+--- linux-2.6.5.orig/include/elan/kalloc.h     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan/kalloc.h  2005-05-11 12:10:12.570912512 -0400
+@@ -0,0 +1,108 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN3_KALLOC_H
++#define __ELAN3_KALLOC_H
++
++#ident "$Id: kalloc.h,v 1.11 2004/05/19 10:23:59 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/kalloc.h,v $ */
++
++#include <elan/rmap.h>
++
++/*
++ * Memory allocator
++ */
++#define LN2_MIN_SIZE  6                                       /* 64 bytes */
++#define LN2_MAX_SIZE  16                                      /* 64k bytes */
++#define NUM_FREELISTS (LN2_MAX_SIZE-LN2_MIN_SIZE + 1)
++#define MIN_SIZE      (1 << LN2_MIN_SIZE)
++#define MAX_SIZE      (1 << LN2_MAX_SIZE)
++
++#define HASHSHIFT     LN2_MAX_SIZE
++#define NHASH         32
++#define HASH(addr)    (((addr) >> HASHSHIFT) & (NHASH-1))
++
++typedef enum
++{
++    EP_ALLOC_TYPE_PRIVATE_SDRAM,
++    EP_ALLOC_TYPE_PRIVATE_MAIN,
++    EP_ALLOC_TYPE_SHARED_MAIN,
++} EP_ALLOC_TYPE;
++
++typedef struct ep_pool
++{
++    EP_NMH              Handle;                               /* network mapping handle */
++
++    struct list_head    HashBase;                             /* linked on hash lists */
++    struct list_head    HashTop;                              /* linked on hash lists */
++
++    struct list_head    Link[NUM_FREELISTS];                  /* linked on free lists */
++    bitmap_t           *Bitmaps[NUM_FREELISTS];               /* bitmaps for each size */
++
++    union {
++      sdramaddr_t     Sdram;
++      unsigned long   Ptr;
++    } Buffer;
++} EP_POOL;
++
++typedef struct ep_alloc
++{
++    spinlock_t             Lock;
++    
++    EP_ALLOC_TYPE    Type;
++    unsigned int     Perm;
++
++    EP_RMAP        *ResourceMap;
++
++    struct list_head HashBase[NHASH];
++    struct list_head HashTop[NHASH];
++    struct list_head Freelists[NUM_FREELISTS];
++
++    union {
++      struct {
++          EP_SYS             *System;
++          struct list_head    Rails;
++      } Shared;
++      
++      struct {
++          EP_RAIL            *Rail;
++      } Private;
++    } Data;
++} EP_ALLOC;
++
++extern void           ep_display_alloc (EP_ALLOC *alloc);
++
++extern void           ep_alloc_init (EP_RAIL *rail);
++extern void           ep_alloc_fini (EP_RAIL *rail);
++
++extern sdramaddr_t    ep_alloc_memory_elan (EP_RAIL *rail, EP_ADDR addr, unsigned size, unsigned int perm, EP_ATTRIBUTE attr);
++extern void           ep_free_memory_elan (EP_RAIL *rail, EP_ADDR addr);
++
++extern sdramaddr_t    ep_alloc_elan (EP_RAIL *rail, unsigned size, EP_ATTRIBUTE attr, EP_ADDR *addrp);
++extern void           ep_free_elan (EP_RAIL *rail, EP_ADDR addr, unsigned size);
++extern void          *ep_alloc_main (EP_RAIL *rail, unsigned size, EP_ATTRIBUTE attr, EP_ADDR *addr);
++extern void           ep_free_main (EP_RAIL *rail, EP_ADDR addr, unsigned size);
++
++extern sdramaddr_t    ep_elan2sdram (EP_RAIL *rail, EP_ADDR addr);
++extern void            *ep_elan2main (EP_RAIL *rail, EP_ADDR addr);
++
++extern void           ep_shared_alloc_init (EP_SYS *sys);
++extern void           ep_shared_alloc_fini (EP_SYS *sys);
++extern int            ep_shared_alloc_add_rail (EP_SYS *sys, EP_RAIL *rail);
++extern void           ep_shared_alloc_remove_rail (EP_SYS *sys, EP_RAIL *rail);
++
++extern void          *ep_shared_alloc_main (EP_SYS *sys, unsigned size, EP_ATTRIBUTE attr, EP_NMD *nmd);
++extern void           ep_shared_free_main (EP_SYS *sys, EP_NMD *nmd);
++
++#endif /* __ELAN_KALLOC_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/include/elan/kcomm.h
+===================================================================
+--- linux-2.6.5.orig/include/elan/kcomm.h      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan/kcomm.h   2005-05-11 12:10:12.572912208 -0400
+@@ -0,0 +1,839 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN_KCOMM_H
++#define __ELAN_KCOMM_H
++
++#ident "$Id: kcomm.h,v 1.71.2.8 2004/12/14 10:19:14 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/kcomm.h,v $*/
++#define EP_KCOMM_MAJOR_VERSION                3
++#define EP_KCOMM_MINOR_VERSION                1
++
++#define EP_PROTOCOL_VERSION           1                       /* CM/KCOMM protocol revision */
++
++#define EP_MAX_NODES                  2048                    /* Max nodes we support */
++#define EP_MAX_RAILS                  16                      /* max number of rails (we use an unsigned short for bitmaps !) */
++#define EP_MAXFRAG                    4                       /* max number of fragments */
++
++#define EP_BLK_SIZE                   64                      /* align objects for elan access */
++
++/* Elan virtual address address space */
++#define EP_SYSTEM_QUEUE_BASE          0x00010000              /* Base address for system queues */
++#define EP_MSGSYS_QUEUE_BASE          0x00020000              /* Base address for msgsys queues */
++#define EP_EPCOMMS_QUEUE_BASE         0x00030000              /* Base address for message queues */
++#define EP_DVMA_BASE                  0x10000000              /* elan address range for dvma mapping. */
++#define EP_DVMA_TOP                   0xE0000000
++
++#define EP_SHARED_BASE                        0xE0000000              /* shared main/elan allocators */
++#define EP_SHARED_TOP                 0xF0000000
++
++#define EP_PRIVATE_BASE                       0xF0000000              /* private main/elan allocators */
++#define EP_PRIVATE_TOP                        0xF8000000
++
++#define EP_DVMA_RMAP_SIZE             1024                    /* size of resource map for dvma address space */
++#define EP_SHARED_RMAP_SIZE           1024                    /* size of resource map for shared address space */
++#define EP_PRIVATE_RMAP_SIZE          1024                    /* size of resource map for private address space */
++
++/* Input queue descriptors fit into 64 bytes */
++#define EP_QUEUE_DESC_SIZE            64
++
++/* Timeouts for checking network position */
++#define EP_POSITION_TIMEOUT           (4*HZ)          /* 1s   time to notice CheckNetworkPosition changes */
++#define EP_WITHDRAW_TIMEOUT           (2*HZ)          /* 2s   time before withdrawing from unreachable nodes */
++
++/* Time to try again due to resource failue (eg malloc etc) */
++#define RESOURCE_RETRY_TIME           (HZ/20)
++
++/* Time to retransmit message when send failed */
++#define MSGBUSY_RETRY_TIME            (HZ/20)
++
++/* Time between retransmits of messages network flush requests */
++#define MESSAGE_RETRY_TIME            (HZ/5)
++
++/* time to hold the context filter up to ensure that the
++ * next packet of a dma is guaranteed to get nacked (8mS) */
++#define NETWORK_ERROR_TIMEOUT         (1 + roundup (HZ * 8 / 1000, 1))
++
++/* Time between retransmits of message failover requests */
++#define FAILOVER_RETRY_TIME           (HZ/5)
++
++/* compute earliest time */
++#define SET_NEXT_RUN_TIME(nextRunTime, time) \
++do { \
++    if ((nextRunTime) == 0 || AFTER(nextRunTime, (time)))\
++      (nextRunTime) = (time);\
++} while (0)
++
++/* DMA retry backoff/priorities/issue rings */
++#define EP_NUM_BACKOFF                        8
++#define EP_RETRY_STABALISING            0
++#define EP_RETRY_BASE                 1
++
++#define EP_RETRY_CRITICAL             EP_RETRY_BASE
++#define EP_RETRY_HIGH_PRI             (EP_RETRY_CRITICAL + 1)
++#define EP_RETRY_HIGH_PRI_TIME                (1)
++#define EP_RETRY_HIGH_PRI_RETRY               (EP_RETRY_HIGH_PRI + 1)
++#define EP_RETRY_HIGH_PRI_RETRY_TIME  (2)
++#define EP_RETRY_LOW_PRI              (EP_RETRY_HIGH_PRI_RETRY + EP_NUM_BACKOFF)
++#define EP_RETRY_LOW_PRI_TIME         (2)
++#define EP_RETRY_LOW_PRI_RETRY                (EP_RETRY_LOW_PRI + 1)
++#define EP_RETRY_LOW_PRI_RETRY_TIME   (4)
++#define EP_RETRY_ANONYMOUS            (EP_RETRY_LOW_PRI_RETRY + EP_NUM_BACKOFF)
++#define EP_RETRY_ANONYMOUS_TIME               (10)
++#define EP_RETRY_NETERR                       (EP_RETRY_ANONYMOUS + EP_NUM_BACKOFF)
++#define EP_RETRY_NETERR_TIME          (10)
++#define EP_NUM_RETRIES                        (EP_RETRY_NETERR + 1)
++
++typedef unsigned short EP_SERVICE;
++
++/* EP_ATTRIBUTE 32 bits 
++ *
++ * 0-2
++ *   for initial call :-
++ *     0 (0x1) EP_NO_ALLOC                            used once
++ *     1 (0x2) EP_NO_SLEEP                            used once
++ *     2 (0x4) EP_NOT_MYSELF                          used once
++ *
++ *   when stored and transmited :-
++ *     0 (0x0) EP_MULTICAST                           envelope
++ *     1 (0x2) EP_RPC                                 envelope
++ *     2 (0x4) EP_HAS_PAYLOAD                         envelope
++ *
++ * 3-11
++ *     3   (0x08) EP_PREFRAIL_SET                     preserved
++ *     4-7 (0xf0) Pref Rail
++ *     8  (0x100) EP_NO_INTERUPT
++ *     9  (0x200) EP_NO_FAILOVER
++ *
++ *    10 (0x400) EP_INTERRUPT_ENABLED                 internal
++ *    11 (0x800) EP_TXD_STABALISING                   internal
++ *
++ * 12-13 Not Used.
++ * 
++ * 14-15 (0xC000) Data Type.                          passed in
++ *    00 none. 
++ *    01 Service Indicator.
++ *    10 TimeOut.
++ *    11 RailMask
++ *          
++ * 16-31 (0x10000)  Data.  Service Indicator, TimeOut, RailMask, Pref Rail.
++ *         
++*/
++
++typedef uint32_t EP_ATTRIBUTE;
++
++#define EP_LOCAL_ATTR_MASK 0x07
++#define EP_CLEAR_LOCAL_ATTR(ATTR)  ( (ATTR) & ~EP_LOCAL_ATTR_MASK )
++
++#define EP_NO_ALLOC      0x01 /* Don't call allocators if no free descriptors */
++#define EP_NO_SLEEP      0x02 /* Don't sleep if no free descriptors */
++#define EP_NOT_MYSELF    0x04 /* Don't send multicast to me */
++
++#define EP_MULTICAST         0x01     /* Message is a multicast */
++#define EP_RPC               0x02     /* Wait for RPC reply */
++#define EP_HAS_PAYLOAD_BIT   0x04     /* transfer payload */
++
++
++#define EP_PREFRAIL_SET  0x08 /* preferred rail is set (otherwise pick one from the NMDs) */
++
++#define EP_PREFRAIL_SHIFT  (4)
++#define EP_PREFRAIL_MASK   0xf0
++#define EP_IS_PREFRAIL_SET(ATTR)      (((ATTR) &  EP_PREFRAIL_SET) != 0)
++#define EP_CLEAR_PREFRAIL(ATTR)       (((ATTR) & ~EP_PREFRAIL_SET) & ~EP_PREFRAIL_MASK) 
++#define EP_SET_PREFRAIL(ATTR,RAIL)    (EP_CLEAR_PREFRAIL(ATTR) | (((RAIL) <<  EP_PREFRAIL_SHIFT ) &  EP_PREFRAIL_MASK ) |  EP_PREFRAIL_SET)
++
++
++#define EP_ATTR2PREFRAIL(ATTR)            (((ATTR) & EP_PREFRAIL_MASK) >> EP_PREFRAIL_SHIFT)
++
++
++#define EP_INTERRUPT_ENABLED 0x400    /* event interrupt enabled on EP_NO_INTERRUPT */
++#define EP_TXD_STABALISING   0x800      /* flag to indicate this is attempting to stabalise */
++
++#define EP_IS_MULTICAST(ATTR)                 (((ATTR) &  EP_MULTICAST) != 0)
++#define EP_SET_MULTICAST(ATTR)                ( (ATTR) |  EP_MULTICAST)
++#define EP_CLEAR_MULTICAST(ATTR)              ( (ATTR) & ~EP_MULTICAST)
++
++#define EP_IS_RPC(ATTR)                       (((ATTR) &  EP_RPC) != 0)
++#define EP_SET_RPC(ATTR)                      ( (ATTR) |  EP_RPC)
++#define EP_CLEAR_RPC(ATTR)                    ( (ATTR) & ~EP_RPC)
++
++#define EP_HAS_PAYLOAD(ATTR)                  (((ATTR) &  EP_HAS_PAYLOAD_BIT) != 0)
++#define EP_SET_HAS_PAYLOAD(ATTR)              ( (ATTR) |  EP_HAS_PAYLOAD_BIT)
++#define EP_CLEAR_HAS_PAYLOAD(ATTR)            ( (ATTR) & ~EP_HAS_PAYLOAD_BIT)
++
++#define EP_IS_INTERRUPT_ENABLED(ATTR)         (((ATTR) &  EP_INTERRUPT_ENABLED) != 0)
++#define EP_SET_INTERRUPT_ENABLED(ATTR)        ( (ATTR) |  EP_INTERRUPT_ENABLED)
++#define EP_CLEAR_INTERRUPT_ENABLED(ATTR)      ( (ATTR) & ~EP_INTERRUPT_ENABLED)
++
++#define EP_IS_TXD_STABALISING(ATTR)           (((ATTR) &  EP_TXD_STABALISING) != 0)
++#define EP_SET_TXD_STABALISING(ATTR)          ( (ATTR) |  EP_TXD_STABALISING)
++#define EP_CLEAR_TXD_STABALISING(ATTR)        ( (ATTR) & ~EP_TXD_STABALISING)
++
++#define EP_NO_INTERRUPT      0x100    /* Don't generate completion interrupt (tx) */
++#define EP_NO_FAILOVER       0x200    /* don't attempt rail failover, just abort */
++
++#define EP_IS_NO_INTERRUPT(ATTR)    (((ATTR) &  EP_NO_INTERRUPT) != 0)
++#define EP_SET_NO_INTERRUPT(ATTR)   ( (ATTR) |  EP_NO_INTERRUPT)
++#define EP_CLEAR_NO_INTERRUPT(ATTR) ( (ATTR) & ~EP_NO_INTERRUPT)
++
++#define EP_IS_NO_FAILOVER(ATTR)    (((ATTR) &  EP_NO_FAILOVER) != 0)
++#define EP_SET_NO_FAILOVER(ATTR)   ( (ATTR) |  EP_NO_FAILOVER)
++#define EP_CLEAR_NO_FAILOVER(ATTR) ( (ATTR) & ~EP_NO_FAILOVER)
++
++#define EP_TYPE_MASK           0xC000
++#define EP_TYPE_SVC_INDICATOR  0x4000
++#define EP_TYPE_TIMEOUT        0x8000
++#define EP_TYPE_RAILMASK       0xC000
++
++#define EP_ATTR2TYPE(ATTR)        ( (ATTR) & EP_TYPE_MASK )
++
++#define EP_IS_SVC_INDICATOR(ATTR) (EP_ATTR2TYPE(ATTR) == EP_TYPE_SVC_INDICATOR)
++#define EP_IS_TIMEOUT(ATTR)       (EP_ATTR2TYPE(ATTR) == EP_TYPE_TIMEOUT)
++#define EP_IS_RAILMASK(ATTR)      (EP_ATTR2TYPE(ATTR) == EP_TYPE_RAILMASK)
++#define EP_IS_NO_TYPE(ATTR)       (EP_ATTR2TYPE(ATTR) == 0)
++
++#define EP_DATA_SHIFT          (16)
++#define EP_DATA_MASK           0xffff0000
++
++#define EP_ATTR2DATA(ATTR)     (((ATTR) & EP_DATA_MASK) >> EP_DATA_SHIFT)
++#define EP_DATA2ATTR(DATA)     (((DATA) <<  EP_DATA_SHIFT) & EP_DATA_MASK)
++
++#define EP_CLEAR_DATA(ATTR)    (((ATTR) & ~EP_TYPE_MASK) & ~EP_DATA_MASK)
++#define EP_SET_DATA(ATTR,TYPE,DATA)   (EP_CLEAR_DATA(ATTR) | ((TYPE) & EP_TYPE_MASK) | (((DATA) <<  EP_DATA_SHIFT) & EP_DATA_MASK))
++
++#define EP_DEFAULT_TIMEOUT     (HZ*30)
++
++#if !defined(offsetof)
++#define offsetof(s, m)                (unsigned long)(&(((s *)0)->m))
++#endif
++#if !defined(roundup)
++#define roundup(x, y)                 ((((x)+((y)-1))/(y))*(y))
++#endif
++
++/* 
++ * Message transaction ID's - these are unique 64 bts 
++ * numbers which include the initial rail number.
++ */
++typedef struct ep_xid
++{
++    uint32_t  Generation;
++    uint32_t  Handle;
++    uint64_t  Unique;
++} EP_XID;
++
++#define EP_INVALIDATE_XID(xid)        ((xid).Generation = (xid).Handle = (xid).Unique = 0)
++
++#define EP_XID_INVALID(xid)   ((xid).Generation == 0 && (xid).Handle == 0 && (xid).Unique == 0)
++#define EP_XIDS_MATCH(a,b)    ((a).Generation == (b).Generation && (a).Handle == (b).Handle && (a).Unique == (b).Unique)
++
++typedef struct ep_backoff
++{
++    unsigned char     type;
++    unsigned char     indx;
++    unsigned short    count;
++} EP_BACKOFF;
++
++/* values for "type" */
++#define EP_BACKOFF_FREE               0
++#define EP_BACKOFF_ENVELOPE   1
++#define EP_BACKOFF_FETCH      2
++#define EP_BACKOFF_DATA               3
++#define EP_BACKOFF_DONE               4
++#define EP_BACKOFF_STABILISE  5
++
++#ifndef __ELAN__
++
++/* forward declaration of types */
++typedef struct ep_rail        EP_RAIL;
++typedef struct ep_sys EP_SYS;
++
++#include <elan/nmh.h>
++#include <elan/kmap.h>
++#include <elan/statemap.h>
++#include <elan/kalloc.h>
++#include <elan/kthread.h>
++#include <elan/kcomm_stats.h>
++#include <elan/devinfo.h>
++
++typedef struct ep_callback
++{
++    struct ep_callback *Next;
++    void              (*Routine)(void *, statemap_t *);
++    void             *Arg;
++} EP_CALLBACK;
++
++#define EP_CB_FLUSH_FILTERING         0
++#define EP_CB_FLUSH_FLUSHING          1
++#define EP_CB_PASSIVATED              2
++#define EP_CB_FAILOVER                        3
++#define EP_CB_DISCONNECTING           4
++#define EP_CB_DISCONNECTED            5
++#define EP_CB_NODESET                 6
++#define EP_CB_COUNT                   7
++
++#endif /* !defined(__ELAN__) */
++
++/* Small unreliable system message queues */
++#define EP_SYSTEMQ_INTR                       0                       /* input queue for cluster membership generating an interrupt */
++#define EP_SYSTEMQ_POLLED             1                       /* input queue for cluster membership polled on clock tick */
++#define EP_SYSTEMQ_MANAGER            2                       /* input queue for manager messages */
++#define EP_NUM_SYSTEMQ                        64
++
++#define EP_SYSTEMQ_ADDR(qnum)         (EP_SYSTEM_QUEUE_BASE + (qnum) * EP_QUEUE_DESC_SIZE)
++#define EP_SYSTEMQ_DESC(base,qnum)    ((base) + (qnum) * EP_QUEUE_DESC_SIZE)
++
++#define EP_SYSTEMQ_MSG_ALIGN          64                      /* message sizes aligned to 64 byte boundaries */
++#define EP_SYSTEMQ_MSG_MAX            (4*64)                  /* max message size */
++
++/* Special flag for Version field to indicate message not
++ * seen in main memory yet and time limit to poll for it */
++#define EP_SYSTEMQ_UNRECEIVED                 0xdeadbabe
++#define EP_SYSTEMQ_UNRECEIVED_TLIMIT          16384                   /* 1023 uS */
++
++#ifndef __ELAN__
++
++typedef void (EP_INPUTQ_HANDLER) (EP_RAIL *rail, void *arg, void *msg);
++typedef void (EP_INPUTQ_CALLBACK) (EP_RAIL *rail, void *arg);
++
++typedef struct ep_inputq
++{
++    unsigned long     q_hidden;                               /* implementation hidden as ep3 or ep4 */
++} EP_INPUTQ;
++
++typedef struct ep_outputq
++{
++    unsigned long     q_hidden;                               /* implementation hidden as ep3 or ep4 */
++} EP_OUTPUTQ;
++
++/* returned values for ep_outputq_state */
++#define EP_OUTPUTQ_BUSY               0
++#define EP_OUTPUTQ_FAILED     1
++#define EP_OUTPUTQ_FINISHED   2
++
++typedef struct ep_switch
++{
++    unsigned    present:1;
++    unsigned  invalid:1;
++    unsigned  link:3;
++    unsigned  bcast:3;
++    unsigned  lnr;
++} EP_SWITCH;
++
++/*
++ * Network error fixup, flush, relocation messges
++ */
++typedef struct ep_map_nmd_body
++{
++    uint32_t          nFrags;
++    EP_RAILMASK               Railmask;
++    EP_NMD            Nmd[EP_MAXFRAG];
++} EP_MAP_NMD_BODY;
++
++typedef struct ep_failover_body
++{
++    EP_XID            Xid;
++    EP_RAILMASK               Railmask;
++} EP_FAILOVER_BODY;
++
++typedef struct ep_failover_txd
++{
++    EP_XID            Xid;
++    uint32_t          Rail;
++    EP_ADDR           TxdRail;
++} EP_FAILOVER_TXD;
++
++typedef uint64_t EP_NETERR_COOKIE;
++
++#define EP_PANIC_STRLEN               31
++
++typedef struct ep_node_state
++{
++    unsigned char       State;
++    unsigned char       NetworkErrorState;
++    EP_RAILMASK         Railmask;
++} EP_NODE_STATE;
++
++#define EP_MANAGER_MSG_SIZE           (2 * EP_SYSTEMQ_MSG_ALIGN)
++
++typedef struct ep_manager_msg_hdr
++{
++    EP_XID            Xid;                                    /* Message transaction id */
++
++    uint16_t          NodeId;                                 /* Originating node number */
++    uint16_t          DestId;                                 /* destination node id */
++
++    uint16_t          Checksum;                               /* Message checksum */
++    uint8_t           Rail;                                   /* Rail message associated with */
++    uint8_t           Type;                                   /* Message type */
++
++    uint32_t          Pad;                                    /* pad to 32 bytes */
++
++    uint32_t          Version;                                /* Message Version */
++} EP_MANAGER_MSG_HDR;
++
++typedef union ep_manager_msg_body
++{
++    unsigned char       Space[EP_MANAGER_MSG_SIZE - sizeof (EP_MANAGER_MSG_HDR)];
++
++    EP_NETERR_COOKIE  Cookies[2];                             /* EP_MSG_TYPE_NETERR */
++    EP_MAP_NMD_BODY   MapNmd;                                 /* EP_MSG_TYPE_MAP_NMD */
++    EP_FAILOVER_BODY  Failover;                               /* EP_MSG_TYPE_FAILOVER_REQUEST */
++    EP_FAILOVER_TXD   FailoverTxd;                            /* EP_MSG_TYPE_FAILOVER_RESPONSE */
++    unsigned char       PanicReason[EP_PANIC_STRLEN+1];               /* EP_MSG_TYPE_REMOTE_PANIC */
++    EP_NODE_STATE       NodeState;                              /* EP_MSG_TYPE_GET_NODE_STATE_RESPONSE */   
++    EP_SERVICE          Service;                                /* EP_MSG_TYPE_GET_NODE_STATE */
++} EP_MANAGER_MSG_BODY;
++
++typedef struct ep_manager_msg
++{
++    EP_MANAGER_MSG_BODY Body;
++    EP_MANAGER_MSG_HDR  Hdr;
++} EP_MANAGER_MSG;
++
++#define EP_MANAGER_MSG_VERSION                                0xcad01000
++#define EP_MANAGER_MSG_TYPE_REMOTE_PANIC              0x00
++#define EP_MANAGER_MSG_TYPE_NETERR_REQUEST            0x01
++#define EP_MANAGER_MSG_TYPE_NETERR_RESPONSE           0x02
++#define EP_MANAGER_MSG_TYPE_FLUSH_REQUEST             0x03
++#define EP_MANAGER_MSG_TYPE_FLUSH_RESPONSE            0x04
++#define EP_MANAGER_MSG_TYPE_MAP_NMD_REQUEST           0x05
++#define EP_MANAGER_MSG_TYPE_MAP_NMD_RESPONSE          0x06
++#define EP_MANAGER_MSG_TYPE_FAILOVER_REQUEST          0x07
++#define EP_MANAGER_MSG_TYPE_FAILOVER_RESPONSE         0x08
++#define EP_MANAGER_MSG_TYPE_GET_NODE_STATE              0x09
++#define EP_MANAGER_MSG_TYPE_GET_NODE_STATE_RESPONSE     0x0a
++
++/* Message types which should only be sent when a rail is connected */
++#define EP_MANAGER_MSG_TYPE_CONNECTED(type)           (((type) & 1) == 1)
++
++#define EP_MANAGER_OUTPUTQ_SLOTS      128                     /* # entries in outputq */
++#define EP_MANAGER_INPUTQ_SLOTS               128                     /* # entries in inputq */
++#define EP_MANAGER_OUTPUTQ_RETRIES    31                      /* # retries for manager messages */
++
++/* XID's are allocated from a cache, which doesn't
++ * require locking since it relies on the caller to
++ * manage the locking for us.
++ */
++typedef struct ep_xid_cache
++{
++    struct list_head  Link;
++
++    uint32_t          Handle;                                 /* my XID cache handle */
++    uint64_t          Current;                                /* range of XID.Unique we can allocate from */
++    uint64_t          Last;
++
++    void            (*MessageHandler)(void *arg, EP_MANAGER_MSG *);
++    void             *Arg;
++} EP_XID_CACHE;
++
++#define EP_XID_CACHE_CHUNKS   (10000)
++
++typedef struct ep_node_rail
++{
++    struct list_head    Link;                                 /* can be linked on work lists */
++
++    unsigned char       State;                                        /* node connection state */
++    unsigned char     NetworkErrorState;                      /* reasons for keeping the context filter up */
++    unsigned char     MessageState;                           /* state of messages during passivate/relocate */
++
++    EP_XID            MsgXid;                                 /* neterr/flush transaction id */
++    long              NextRunTime;                            /* time to drop context filter for destroyed dma packet, or to send next request */
++    EP_NETERR_COOKIE  NetworkErrorCookies[2];                 /* identify cookie for destroyed atomic packet */
++
++    uint32_t          Cookie;                                 /* per-node network error cookie */
++    spinlock_t                CookieLock;                             /* and spinlock for it. */
++
++    struct list_head    StalledDmas;                          /* list of stalled DMAs */
++} EP_NODE_RAIL;
++
++#define EP_NODE_DISCONNECTED          0                       /* node is disconnected */
++#define EP_NODE_CONNECTING            1                       /* awaiting connection */
++#define EP_NODE_CONNECTED             2                       /* node is connected */
++#define EP_NODE_LEAVING_CONNECTED     3                       /* node is starting to disconnect */
++#define EP_NODE_LOCAL_PASSIVATE       4                       /* flushing context filter/run queues */
++#define EP_NODE_REMOTE_PASSIVATE      5                       /* stalling for neterr flush */
++#define EP_NODE_PASSIVATED            6                       /* relocating active/passive messages */
++#define EP_NODE_DISCONNECTING         7                       /* entering disconncted - abort remaining comms */
++#define EP_NODE_NUM_STATES            8
++
++#define EP_NODE_NETERR_ATOMIC_PACKET  (1 << 0)
++#define EP_NODE_NETERR_DMA_PACKET     (1 << 1)
++
++#define EP_NODE_PASSIVE_MESSAGES      (1 << 0)
++#define EP_NODE_ACTIVE_MESSAGES               (1 << 1)
++
++/*
++ * Kernel thread code is loaded as a table.
++ */
++typedef struct ep_symbol
++{
++    char   *name;
++    EP_ADDR value;
++} EP_SYMBOL;
++
++typedef struct ep_code
++{
++    u_char        *text;
++    u_int        text_size;
++    u_char        *data;
++    u_int        data_size;
++    u_char        *rodata;
++    u_int        rodata_size;
++    EP_SYMBOL     *symbols;
++    
++    int                  ntext;
++    sdramaddr_t    pptext;
++    EP_ADDR      etext;
++    sdramaddr_t   _stext;
++    sdramaddr_t         _rodata;
++
++    int                  ndata;
++    sdramaddr_t    ppdata;
++    EP_ADDR      edata;
++    sdramaddr_t   _sdata;
++} EP_CODE;
++
++typedef struct ep_switchstate
++{
++    unsigned char       linkid;
++    unsigned char       LNR;
++    unsigned char       bcast;
++    unsigned char       uplink;
++} EP_SWITCHSTATE;
++
++typedef struct ep_rail_ops
++{
++    void      (*DestroyRail) (EP_RAIL *rail);
++
++    int       (*StartRail) (EP_RAIL *rail);
++    void      (*StallRail) (EP_RAIL *rail);
++    void      (*StopRail) (EP_RAIL *rail);
++
++    sdramaddr_t (*SdramAlloc) (EP_RAIL *rail, EP_ADDR addr, unsigned size);
++    void        (*SdramFree) (EP_RAIL *rail, sdramaddr_t addr, unsigned size);
++    void        (*SdramWriteb) (EP_RAIL *rail, sdramaddr_t addr, unsigned char val);
++    
++    void      (*KaddrMap) (EP_RAIL *rail, EP_ADDR eaddr, virtaddr_t kaddr, unsigned len, unsigned int perm, int ep_attr);
++    void      (*SdramMap) (EP_RAIL *rail, EP_ADDR eaddr, sdramaddr_t saddr, unsigned len, unsigned int perm, int ep_attr);
++    void      (*Unmap) (EP_RAIL *rail, EP_ADDR eaddr, unsigned len);
++
++    void       *(*DvmaReserve) (EP_RAIL *rail, EP_ADDR eaddr, unsigned npages);
++    void      (*DvmaRelease) (EP_RAIL *rail, EP_ADDR eaddr, unsigned npages, void *private);
++    void      (*DvmaSetPte) (EP_RAIL *rail, void *private, unsigned index, physaddr_t phys, unsigned int perm);
++    physaddr_t        (*DvmaReadPte) (EP_RAIL *rail, void *private, unsigned index);
++    void      (*DvmaUnload)(EP_RAIL *rail, void *private, unsigned index, unsigned npages);
++    void      (*FlushTlb) (EP_RAIL *rail);
++
++    int       (*ProbeRoute) (EP_RAIL *r, int level, int sw, int nodeid, int *linkup, 
++                             int *linkdown, int attempts, EP_SWITCH *lsw);
++    void      (*PositionFound) (EP_RAIL *rail, ELAN_POSITION *pos);
++    int               (*CheckPosition) (EP_RAIL *rail);
++    void      (*NeterrFixup) (EP_RAIL *rail, unsigned int nodeId, EP_NETERR_COOKIE *cookies);
++
++    void      (*LoadSystemRoute) (EP_RAIL *rail, unsigned int vp, unsigned int lowNode, unsigned int highNode);
++
++    void      (*LoadNodeRoute) (EP_RAIL *rail, unsigned nodeId);
++    void      (*UnloadNodeRoute) (EP_RAIL *rail, unsigned nodeId);
++    void      (*LowerFilter) (EP_RAIL *rail, unsigned nodeId);
++    void      (*RaiseFilter) (EP_RAIL *rail, unsigned nodeId);
++    void      (*NodeDisconnected) (EP_RAIL *rail, unsigned nodeId);
++
++    void      (*FlushFilters) (EP_RAIL *rail);
++    void      (*FlushQueues) (EP_RAIL *rail);
++
++
++    EP_INPUTQ  *(*AllocInputQ) (EP_RAIL *rail, unsigned qnum, unsigned slotSize, unsigned slotCount,
++                              void (*callback)(EP_RAIL *rail, void *arg), void *arg);
++    void      (*FreeInputQ) (EP_RAIL *rail, EP_INPUTQ *q);
++    void      (*EnableInputQ) (EP_RAIL *rail, EP_INPUTQ *q);
++    void      (*DisableInputQ) (EP_RAIL *rail, EP_INPUTQ *q);
++    int               (*PollInputQ) (EP_RAIL *rail, EP_INPUTQ *q, int maxCount, EP_INPUTQ_HANDLER *handler, void *arg);
++
++    EP_OUTPUTQ *(*AllocOutputQ) (EP_RAIL *rail, unsigned slotSize, unsigned slotCount);
++    void      (*FreeOutputQ) (EP_RAIL *rail, EP_OUTPUTQ *outputq);
++    void       *(*OutputQMsg) (EP_RAIL *rail, EP_OUTPUTQ *outputq, unsigned slotNum);
++    int         (*OutputQState) (EP_RAIL *rail, EP_OUTPUTQ *outputq, unsigned slotNum);
++    int               (*OutputQSend) (EP_RAIL *rail, EP_OUTPUTQ *outputq, unsigned slotNum, unsigned size,
++                              unsigned vp, unsigned qnum, unsigned retries);
++
++    void        (*FillOutStats) (EP_RAIL *rail, char *str);
++    void      (*Debug) (EP_RAIL *rail);
++
++} EP_RAIL_OPS;
++
++#define ep_alloc_inputq(rail,qnum,slotSize,slotCount,callback,arg) \
++      (rail)->Operations.AllocInputQ(rail,qnum,slotSize,slotCount,callback,arg)
++#define ep_free_inputq(rail,inputq) \
++      (rail)->Operations.FreeInputQ(rail,inputq)
++#define ep_enable_inputq(rail,inputq) \
++      (rail)->Operations.EnableInputQ(rail,inputq)
++#define ep_disable_inputq(rail,inputq) \
++      (rail)->Operations.DisableInputQ(rail,inputq)
++#define ep_poll_inputq(rail,inputq,maxCount,handler,arg) \
++      (rail)->Operations.PollInputQ(rail,inputq,maxCount,handler,arg)
++#define ep_alloc_outputq(rail,slotSize,slotCount)\
++      (rail)->Operations.AllocOutputQ(rail,slotSize,slotCount)
++#define ep_free_outputq(rail,outputq)\
++      (rail)->Operations.FreeOutputQ(rail,outputq)
++#define ep_outputq_msg(rail,outputq,slotNum)\
++      (rail)->Operations.OutputQMsg(rail,outputq,slotNum)
++#define ep_outputq_state(rail,outputq,slotNum)\
++      (rail)->Operations.OutputQState(rail,outputq,slotNum)
++#define ep_outputq_send(rail,outputq,slotNum,size,vp,qnum,retries)\
++      (rail)->Operations.OutputQSend(rail,outputq,slotNum,size,vp,qnum,retries)
++
++struct ep_rail
++{
++    EP_SYS           *System;                                 /* "system" we've attached to */
++
++    unsigned char     Number;                                 /* Rail number */
++    unsigned char       State;                                        /* Rail state */
++    char              Name[32];                               /* Rail name */
++
++    struct list_head    ManagerLink;                          /* linked on ManagedRails list */
++
++    ELAN_DEVINFO      Devinfo;                                /* Device information for this rail */
++    ELAN_POSITION       Position;                             /* Position on switch device is connected to */
++
++    EP_RAIL_OPS               Operations;                             /* device specific operations */
++    EP_RAIL_STATS     Stats;                                  /* statistics */
++
++    EP_ALLOC            ElanAllocator;                                /* per-rail elan memory allocator */
++    EP_ALLOC            MainAllocator;                                /* per-rail main memory allocator */
++
++    unsigned          TlbFlushRequired;                       /* lazy TLB flushing */
++
++    int               SwitchBroadcastLevel;                   /* current switch level ok for broadcast */
++    unsigned long       SwitchBroadcastLevelTick;
++
++    int                       SwitchProbeLevel;                       /* result of last switch probe */
++    EP_SWITCHSTATE      SwitchState[ELAN_MAX_LEVELS];
++    EP_SWITCHSTATE      SwitchLast[ELAN_MAX_LEVELS];
++    unsigned long       SwitchProbeTick[ELAN_MAX_LEVELS];
++    
++    /* Node disconnecting/connecting state */
++    EP_CALLBACK        *CallbackList[EP_CB_COUNT];            /* List of callbacks */
++    kmutex_t          CallbackLock;                           /*   and lock for it. */
++    unsigned          CallbackStep;                           /*  step through UpdateConnectionState. */
++
++    /* back pointer for cluster membership */
++    void             *ClusterRail;
++
++    /* Per node state for message passing */
++    EP_NODE_RAIL       *Nodes;                                        /* array of per-node state */
++    statemap_t         *NodeSet;                              /* per-rail statemap of connected nodes */
++    statemap_t               *NodeChangeMap;                          /* statemap of nodes to being connected/disconnected */
++    statemap_t               *NodeChangeTmp;                          /*   and temporary copies */
++
++    struct list_head    NetworkErrorList;                     /* list of nodes resolving network errors */
++    struct list_head    LocalPassivateList;                   /* list of nodes in state LOCAL_PASSIVATE */
++    struct list_head    RemotePassivateList;                  /* list of nodes waiting for remote network error flush */
++    struct list_head    PassivatedList;                               /* list of nodes performing message relocation */
++    struct list_head    DisconnectingList;                    /* list of nodes transitioning to disconnected */
++
++    EP_XID_CACHE      XidCache;                               /* XID cache for node messages (single threaded access) */
++
++    /* Manager messages */
++    EP_INPUTQ        *ManagerInputQ;
++    EP_OUTPUTQ               *ManagerOutputQ;
++    unsigned          ManagerOutputQNextSlot;
++    spinlock_t                ManagerOutputQLock;
++
++    /* /proc entries */
++    struct proc_dir_entry *ProcDir;
++    struct proc_dir_entry *SvcIndicatorDir;
++    int                    CallbackRegistered;
++};
++
++/* values for State */
++#define EP_RAIL_STATE_UNINITIALISED   0                       /* device uninitialised */
++#define EP_RAIL_STATE_STARTED         1                       /* device started but network position unknown */
++#define EP_RAIL_STATE_RUNNING         2                       /* device started and position known */
++#define EP_RAIL_STATE_INCOMPATIBLE    3                       /* device started, but position incompatible */
++
++typedef struct ep_rail_entry
++{
++    struct list_head  Link;
++    EP_RAIL          *Rail;
++} EP_RAIL_ENTRY;
++
++typedef struct ep_subsys
++{
++    EP_SYS           *Sys;
++
++    struct list_head  Link;                                   /* Linked on sys->Subsystems */
++    char             *Name;                                   /* Name to lookup */
++    
++    void             (*Destroy)    (struct ep_subsys *subsys, EP_SYS *sys);
++
++    int                      (*AddRail)    (struct ep_subsys *subsys, EP_SYS *sys, EP_RAIL *rail);
++    void             (*RemoveRail) (struct ep_subsys *subsys, EP_SYS *sys, EP_RAIL *rail);
++} EP_SUBSYS;
++
++typedef struct ep_node
++{
++    EP_RAILMASK               ConnectedRails;
++} EP_NODE;
++
++struct ep_sys
++{
++    EP_RAIL         *Rails[EP_MAX_RAILS];                     /* array of all available devices */
++
++    kmutex_t       StartStopLock;                             /* lock for starting stopping rails */
++
++    ELAN_POSITION    Position;                                        /* primary node position */
++
++    EP_NMH_TABLE     MappingTable;                            /* Network mapping handle table */
++
++    EP_ALLOC       Allocator;                                 /* shared main memory allocator */
++
++    EP_DVMA_STATE    DvmaState;                                       /* dvma state */
++
++    kmutex_t       SubsysLock;                               /* lock on the Subsytems list */
++    struct list_head Subsystems;                              /* list of subsystems */
++
++    /* device manager state */
++    struct list_head ManagedRails;                            /* list of managed devices */
++    EP_KTHREAD       ManagerThread;                           /* place for manager thread to sleep */
++
++    /* global node state */
++    spinlock_t             NodeLock;                                  /* spinlock for node state (including per-device node state) */
++    EP_NODE       *Nodes;                                     /* system wide node state */
++    statemap_t      *NodeSet;                                 /* system wide nodeset */
++    struct list_head NodesetCallbackList;                     /* list of "callbacks" */
++
++    /* Transaction Id */
++    struct list_head XidCacheList;                            /* list of XID caches */
++    uint32_t       XidGeneration;                             /* XID generation number (distinguishes reboots) */
++    uint32_t       XidHandle;                                 /* XID handles (distinguishes XID caches) */
++    uint64_t       XidNext;                                   /* next XID to prime cache */
++    spinlock_t             XidLock;                                   /*   and it's spinlock  */
++
++    /* Shutdown/Panic */
++    unsigned int     Shutdown;                                        /* node has shutdown/panic'd */
++};
++
++#if defined(DEBUG_ASSERT)
++extern int ep_assfail (EP_RAIL *rail, const char *string, const char *func, const char *file, const int line);
++extern int sdram_assert;
++extern int assfail_mode;
++
++#define EP_ASSERT(rail, EX)   do { \
++    if (!(EX) && ep_assfail ((EP_RAIL *) (rail), #EX, __FUNCTION__, __FILE__, __LINE__)) { \
++      BUG(); \
++    } \
++} while (0)
++#define EP_ASSFAIL(rail,EX)   do { \
++   if (ep_assfail ((EP_RAIL *) (rail), EX, __FUNCTION__, __FILE__, __LINE__)) { \
++      BUG(); \
++    } \
++} while (0)
++#define SDRAM_ASSERT(EX)      (sdram_assert ? (EX) : 1)
++#else
++#define EP_ASSERT(rail, EX)   ((void) 0)
++#define EP_ASSFAIL(rail,str)  ((void) 0)
++#define SDRAM_ASSERT(EX)      (1)
++#endif
++
++/* conf_osdep.c */
++extern EP_SYS    *ep_system(void);
++extern void       ep_mod_dec_usecount (void);
++extern void       ep_mod_inc_usecount (void);
++
++/* procfs_osdep.c */
++extern struct proc_dir_entry *ep_procfs_root;
++extern struct proc_dir_entry *ep_config_root;
++
++/* kcomm.c */
++extern int        ep_sys_init (EP_SYS *sys);
++extern void       ep_sys_fini (EP_SYS *sys);
++extern void     ep_shutdown (EP_SYS *sys);
++extern int        ep_init_rail (EP_SYS *sys, EP_RAIL *rail);
++extern void       ep_destroy_rail (EP_RAIL *rail);
++extern int        ep_start_rail (EP_RAIL *rail);
++extern void       ep_stop_rail (EP_RAIL *rail);
++
++extern void       ep_connect_node (EP_RAIL *rail, int nodeId);
++extern int        ep_disconnect_node (EP_RAIL *rail, int nodeId);
++
++extern EP_XID     ep_xid_cache_alloc (EP_SYS *sys, EP_XID_CACHE *cache);
++extern void       ep_xid_cache_init (EP_SYS *sys, EP_XID_CACHE *cache);
++extern void       ep_xid_cache_destroy (EP_SYS *sys, EP_XID_CACHE *cache);
++
++extern int        ep_send_message (EP_RAIL *rail, int nodeId, int type, EP_XID xid, EP_MANAGER_MSG_BODY *body);
++
++extern void       ep_panic_node (EP_SYS *sys, int nodeId, unsigned char *reason);
++
++extern void     ep_subsys_add (EP_SYS *sys, EP_SUBSYS *subsys);
++extern void     ep_subsys_del (EP_SYS *sys, EP_SUBSYS *subsys);
++extern EP_SUBSYS *ep_subsys_find (EP_SYS *sys, char *name);
++
++extern void       DisplayNodes (EP_RAIL *rail);
++
++extern void       ep_fillout_stats(EP_RAIL *rail, char *str);
++
++/* neterr.c */
++extern void       ep_queue_network_error (EP_RAIL *rail, int nodeId, int what, int channel, EP_NETERR_COOKIE cookie);
++
++/* kcomm_elan3.c */
++extern unsigned int ep3_create_rails (EP_SYS *sys, unsigned int disabled);
++
++/* kcomm_elan4.c */
++extern unsigned int ep4_create_rails (EP_SYS *sys, unsigned int disabled);
++
++/* probenetwork.c */
++extern int      ProbeNetwork (EP_RAIL *rail, ELAN_POSITION *pos);
++extern void     CheckPosition (EP_RAIL *rail);
++
++extern uint16_t   CheckSum (char *msg, int nob);
++
++/* threadcode.c */
++extern EP_ADDR    ep_symbol (EP_CODE *code, char *name);
++extern int        ep_loadcode (EP_RAIL *rail, EP_CODE *code);
++extern void       ep_unloadcode (EP_RAIL *rail, EP_CODE *code);
++
++/* Public interface */
++/* debug.c */
++extern int              ep_sprintf_bitmap (char *str, unsigned nbytes, bitmap_t *bitmap, int base, int count, int off);
++extern void             ep_display_bitmap (char *prefix, char *tag, bitmap_t *bitmap, unsigned base, unsigned nbits);
++
++/* epcomms.c */
++extern int              ep_waitfor_nodeid (EP_SYS *sys);
++extern int              ep_nodeid (EP_SYS *sys);
++extern int              ep_numnodes (EP_SYS *sys);
++
++/* railhints.c */
++extern int              ep_pickRail(EP_RAILMASK railmask);
++
++/* support.c */
++extern int              ep_register_nodeset_callback (EP_SYS *sys, void (*routine)(void *, statemap_t *), void *arg);
++extern void             ep_remove_nodeset_callback (EP_SYS *sys, void (*routine)(void *, statemap_t *), void *arg);
++extern void             ep_call_nodeset_callbacks (EP_SYS *sys, statemap_t *map);
++
++extern int              ep_register_callback (EP_RAIL *rail, unsigned idx, void (*routine)(void *, statemap_t *), void *arg);
++extern void             ep_remove_callback (EP_RAIL *rail, unsigned idx, void (*routine)(void *, statemap_t *), void *arg);
++extern void             ep_call_callbacks (EP_RAIL *rail, unsigned idx, statemap_t *);
++extern unsigned int     ep_backoff (EP_BACKOFF *backoff, int type);
++
++#endif /* !__ELAN__ */
++
++typedef struct display_info {
++    void (*func)(long, char *, ...);
++    long arg;
++} DisplayInfo;
++
++extern DisplayInfo di_ep_debug;
++
++
++#endif /* __ELAN_KCOMM_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/include/elan/kcomm_stats.h
+===================================================================
+--- linux-2.6.5.orig/include/elan/kcomm_stats.h        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan/kcomm_stats.h     2005-05-11 12:10:12.572912208 -0400
+@@ -0,0 +1,153 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __EP_EPSTATS_H
++#define __EP_EPSTATS_H
++
++#ident "$Id: kcomm_stats.h,v 1.4.8.1 2004/11/12 10:54:51 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/kcomm_stats.h,v $ */
++
++#define EP_BUCKET_SLOTS                       8
++
++#define BucketStat(obj,stat,size)     ((size) < 128   ? (obj)->Stats.stat[0]++ : \
++                                       (size) < 512   ? (obj)->Stats.stat[1]++ : \
++                                       (size) < 1024  ? (obj)->Stats.stat[2]++ : \
++                                       (size) < 8192  ? (obj)->Stats.stat[3]++ : \
++                                       (size) < 16384 ? (obj)->Stats.stat[4]++ : \
++                                       (size) < 32768 ? (obj)->Stats.stat[5]++ : \
++                                       (size) < 65536 ? (obj)->Stats.stat[6]++ : \
++                                       (obj)->Stats.stat[7]++)
++#define IncrStat(obj,stat)            ((obj)->Stats.stat++)
++
++
++#define EP3_NUM_DMA_FAIL              11      /* NOTE - the same as EP_NUM_RETRIES */
++
++#define ADD_STAT(STATS,STAT,VALUE) { unsigned long now = lbolt;\
++   STATS.STAT.total += VALUE; \
++   if  ( ( now - STATS.STAT.last_time ) > HZ ) { \
++ STATS.STAT.last_per_sec = ( STATS.STAT.total - STATS.STAT.last_count)/ ( (( now - STATS.STAT.last_time ) + (HZ/2)) / HZ);\
++ STATS.STAT.last_time = now; \
++ STATS.STAT.last_count = STATS.STAT.total; \
++   }} \
++
++#define INC_STAT(STATS,STAT) ADD_STAT(STATS,STAT,1)
++
++#define GET_STAT_PER_SEC(STATS, STAT) (  (( lbolt - STATS.STAT.last_time ) < (HZ * 5)) ? STATS.STAT.last_per_sec : 0 )
++#define GET_STAT_TOTAL(STATS, STAT) ( STATS.STAT.total )
++
++struct ep_stats_count 
++{
++    unsigned long total;
++    unsigned long last_time;
++    unsigned long last_count;
++    unsigned long last_per_sec;
++};
++
++typedef struct ep_stats_count         EP_STATS_COUNT;
++
++typedef struct ep3_rail_stats
++{
++    unsigned long     IssueDmaFail[EP3_NUM_DMA_FAIL];
++
++    unsigned long     DmaQueueLength[EP_BUCKET_SLOTS];
++    unsigned long     CprocDmaQueueOverflow;
++    unsigned long     DprocDmaQueueOverflow;
++    unsigned long     IprocDmaQueueOverflow;
++    unsigned long     CprocEventQueueOverflow;
++    unsigned long     DprocEventQueueOverflow;
++    unsigned long     IprocEventQueueOverflow;
++
++    unsigned long     QueueingPacketTrap;
++    unsigned long     DmaIdentifyTrap;
++    unsigned long     ThreadIdentifyTrap;
++    unsigned long     DmaPacketTrap;
++} EP3_RAIL_STATS;
++
++typedef struct ep4_rail_stats
++{
++    unsigned long       somestatsgohere;
++} EP4_RAIL_STATS;
++
++typedef struct ep_rail_stats
++{
++    unsigned long     SendMessageFailed;
++    unsigned long     NeterrAtomicPacket;
++    unsigned long       NeterrDmaPacket;
++
++    EP_STATS_COUNT      rx;
++    EP_STATS_COUNT      rx_len;
++
++    EP_STATS_COUNT      tx;
++    EP_STATS_COUNT      tx_len;
++
++} EP_RAIL_STATS;
++
++typedef struct ep_cm_rail_stats
++{
++    /* cluster membership statistics */
++    unsigned long     HeartbeatsSent;
++    unsigned long     HeartbeatsRcvd;
++    
++    unsigned long     RetryHeartbeat;
++    unsigned long     RejoinRequest;
++    unsigned long     RejoinTooSlow;
++    unsigned long     LaunchMessageFail;
++    unsigned long     MapChangesSent;
++
++    /* Heartbeat scheduling stats */
++    unsigned long     HeartbeatOverdue;
++} EP_CM_RAIL_STATS;
++
++typedef struct ep_comms_rail_stats
++{
++    /* kernel comms large message statistics */
++    unsigned long     TxEnveEvent;
++    unsigned long     TxDataEvent;
++    unsigned long     TxDoneEvent;
++    unsigned long     RxDoneEvent;
++    unsigned long     MulticastTxDone;
++    unsigned long     QueueReceive;
++
++    unsigned long     TxEnveRetry;
++    unsigned long     TxDataRetry;
++    unsigned long     TxDoneRetry;
++    unsigned long     RxThrdEvent;
++    unsigned long     RxDataRetry;
++    unsigned long     RxDoneRetry;
++    unsigned long     StallThread;
++    unsigned long     ThrdWaiting;
++    unsigned long     CompleteEnvelope;
++
++    unsigned long     NoFreeTxds;
++    unsigned long     NoFreeRxds;
++
++    unsigned long     LockRcvrTrapped;
++} EP_COMMS_RAIL_STATS;
++
++typedef struct ep_comms_stats
++{
++    unsigned long     DataXmit[8];
++    unsigned long     McastXmit[8];
++    unsigned long     RPCXmit[8];
++    unsigned long     RPCPut[8];
++    unsigned long     RPCGet[8];
++    unsigned long     CompleteRPC[8];
++    unsigned long     RxData[8];
++    unsigned long     RxMcast[8];
++
++    unsigned long     NoFreeTxds;
++    unsigned long     NoFreeRxds;
++} EP_COMMS_STATS;
++
++#endif /* __EP_EPSTATS_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/include/elan/kmap.h
+===================================================================
+--- linux-2.6.5.orig/include/elan/kmap.h       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan/kmap.h    2005-05-11 12:10:12.572912208 -0400
+@@ -0,0 +1,68 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN_KMAP_H
++#define __ELAN_KMAP_H
++
++#ident "$Id: kmap.h,v 1.3.8.1 2004/12/14 10:19:14 mike Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/kmap.h,v $ */
++
++#include <elan/rmap.h>
++
++extern void ep_perrail_kaddr_map (EP_RAIL *rail, EP_ADDR eaddr, virtaddr_t vaddr, unsigned long len, unsigned int perm, int ep_attr);
++extern void ep_perrail_sdram_map (EP_RAIL *rail, EP_ADDR eaddr, sdramaddr_t saddr, unsigned long len, unsigned int perm, int ep_attr);
++extern void ep_perrail_unmap (EP_RAIL *rail, EP_ADDR eaddr, unsigned long len);
++extern void ep_perrail_dvma_sync (EP_RAIL *rail);
++
++typedef struct ep_dvma_nmh
++{
++    EP_NMH            dvma_nmh;
++    
++    struct list_head  dvma_link;                              /* chained on ep_dvma_state */
++    unsigned          dvma_perm;                              /* permissions for region */
++
++    spinlock_t                dvma_lock;
++    EP_RAILMASK               dvma_railmask;                          /* bitmap of rails */
++    EP_RAIL          *dvma_rails[EP_MAX_RAILS];               /* assoicated rails */
++    void             *dvma_private[EP_MAX_RAILS];             /* pointers to rail private data */
++    unsigned int        dvma_attrs[1];                                /* bitmap of which rails pages are loaded NOTE - max 32 rails */
++} EP_DVMA_NMH;
++
++/* values for dvma_perm */
++#define EP_PERM_EXECUTE               0
++#define EP_PERM_READ          1
++#define EP_PERM_WRITE         2
++#define EP_PERM_ALL           3
++
++typedef struct ep_dvma_state
++{
++    kmutex_t          dvma_lock;
++    struct list_head    dvma_handles;
++    struct list_head    dvma_rails;
++    EP_RMAP          *dvma_rmap;
++} EP_DVMA_STATE;
++
++extern void    ep_dvma_init (EP_SYS *sys);
++extern void    ep_dvma_fini (EP_SYS *sys);
++extern EP_NMH *ep_dvma_reserve (EP_SYS *sys, unsigned npages, unsigned perm);
++extern void    ep_dvma_release (EP_SYS *sys, EP_NMH *nmh);
++extern void    ep_dvma_load (EP_SYS *sys, void *map, caddr_t vaddr, unsigned len, 
++                           EP_NMH *nmh, unsigned index, EP_RAILMASK *hints, EP_NMD *subset);
++extern void    ep_dvma_unload (EP_SYS *sys, EP_NMH *nmh, EP_NMD *nmd);
++  
++extern void    ep_dvma_remove_rail (EP_SYS *sys, EP_RAIL *rail);
++extern int     ep_dvma_add_rail (EP_SYS *sys, EP_RAIL *rail);
++
++extern uint16_t rolling_check_sum (char *msg, int nob, uint16_t sum);
++
++#endif /* __ELAN_KMAP_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/include/elan/kmsg.h
+===================================================================
+--- linux-2.6.5.orig/include/elan/kmsg.h       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan/kmsg.h    2005-05-11 12:10:12.573912056 -0400
+@@ -0,0 +1,14 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN_KMSG_H
++#define __ELAN_KMSG_H
++
++#ident "@(#)$Id: kmsg.h,v 1.1 2003/09/23 13:55:12 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/kmsg.h,v $ */
++
++#endif /* __ELAN_KMSG_H */
+Index: linux-2.6.5/include/elan/kthread.h
+===================================================================
+--- linux-2.6.5.orig/include/elan/kthread.h    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan/kthread.h 2005-05-11 12:10:12.573912056 -0400
+@@ -0,0 +1,53 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN3_KTHREAD_H
++#define __ELAN3_KTHREAD_H
++
++#ident "@(#)$Id: kthread.h,v 1.4 2004/05/06 14:24:08 david Exp $ $Name: QSNETMODULES-4-31_20050321 $"
++/*      $Source: /cvs/master/quadrics/epmod/kthread.h,v $*/
++
++typedef struct ep_kthread
++{
++      kcondvar_t      wait;                                   /* place to sleep */
++      spinlock_t      lock;                                   /* and lock */
++      long            next_run;                               /* tick when thread should next run */
++      long            running;                                /* tick when thread started to run */
++      unsigned short  should_stall;
++      unsigned char   state;
++      unsigned int    started:1;
++      unsigned int    should_stop:1;
++      unsigned int    stopped:1;
++} EP_KTHREAD;
++
++#define KT_STATE_SLEEPING             0
++#define KT_STATE_SCHEDULED            1
++#define KT_STATE_RUNNING              2
++#define KT_STATE_STALLED              3
++
++#define AFTER(a, b)                   ((((long)(a)) - ((long)(b))) > 0)
++#define BEFORE(a,b)                   ((((long)(a)) - ((long)(b))) < 0)
++
++extern void ep_kthread_init (EP_KTHREAD *kt);
++extern void ep_kthread_destroy (EP_KTHREAD *kt);
++extern void ep_kthread_started (EP_KTHREAD *kt);
++extern void ep_kthread_stopped (EP_KTHREAD *kt);
++extern int  ep_kthread_should_stall (EP_KTHREAD *kth);
++extern int  ep_kthread_sleep (EP_KTHREAD *kth, long next_run);
++extern void ep_kthread_schedule (EP_KTHREAD *kt, long when);
++extern void ep_kthread_stall (EP_KTHREAD *kth);
++extern void ep_kthread_resume (EP_KTHREAD *kt);
++extern void ep_kthread_stop (EP_KTHREAD *kt);
++extern int  ep_kthread_state (EP_KTHREAD *kt, long *time);
++#endif /* __ELAN3_KTHREAD_H */
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.6.5/include/elan/nmh.h
+===================================================================
+--- linux-2.6.5.orig/include/elan/nmh.h        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan/nmh.h     2005-05-11 12:10:12.573912056 -0400
+@@ -0,0 +1,95 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN3_NMH_H
++#define __ELAN3_NMH_H
++
++#ident "@(#)$Id: nmh.h,v 1.7 2004/01/06 10:29:55 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/nmh.h,v $*/
++
++
++/* Forward declarations */
++typedef struct ep_nmd         EP_NMD;
++typedef struct ep_nmh_ops     EP_NMH_OPS;
++typedef struct ep_nmh         EP_NMH;
++
++/* Railmask held in 16 bit field (packs with nodeId into NMD */
++typedef uint16_t              EP_RAILMASK;
++
++#define EP_RAIL2RAILMASK(rnum)        (1 << (rnum))
++#define EP_RAILMASK_ALL               0xffff
++
++/* kernel comms elan network address */
++typedef uint32_t              EP_ADDR;
++
++/* network mapping descriptor - this is returned to the user from a map operation,
++ * and is what is passed to all communication functions */
++struct ep_nmd
++{
++    EP_ADDR   nmd_addr;                                       /* base address */
++    uint32_t  nmd_len;                                        /* size in bytes */
++    uint32_t  nmd_attr;                                       /* nodeid << 16 | railmask */
++};
++
++#define EP_NMD_ATTR(nodeid,railmask)  (((nodeid) << 16) | (railmask))
++#define EP_NMD_NODEID(nmd)            ((nmd)->nmd_attr >> 16)
++#define EP_NMD_RAILMASK(nmd)          ((nmd)->nmd_attr & EP_RAILMASK_ALL)
++
++#if !defined(__ELAN__)
++
++struct ep_nmh_ops
++{
++    int          (*op_map_rails) (EP_SYS *sys, EP_NMH *nmh, EP_NMD *nmd, EP_RAILMASK mask);   /* add mappings to different rail(s) */
++
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++    uint16_t (*op_calc_check_sum) (EP_SYS *sys, EP_NMH *nmh, EP_NMD *nmd, uint16_t check_sum); /* calculates check sum              */
++#endif
++};
++
++struct ep_nmh
++{
++    EP_NMD         nmh_nmd;                                   /* public field */
++    struct list_head nmh_link;                                        /* linked on hash table */
++    EP_NMH_OPS            *nmh_ops;                                   /* operations to perform on object */
++};
++
++#define EP_NMH_NUMHASH                        (32 - 11 + 1)           /* one hash table for each power of 2 above pagesize */
++#define EP_NMH_HASHSIZE                       (64)                    /* max size of each hash table */
++
++typedef struct ep_nmh_table
++{
++    struct list_head *tbl_hash[EP_NMH_NUMHASH];
++    unsigned        tbl_size[EP_NMH_NUMHASH];
++} EP_NMH_TABLE;
++
++extern int         ep_nmh_init (EP_NMH_TABLE *tbl);
++extern void        ep_nmh_fini (EP_NMH_TABLE *tbl);
++
++extern void        ep_nmh_insert (EP_NMH_TABLE *tbl, EP_NMH *nmd);
++extern void        ep_nmh_remove (EP_NMH_TABLE *tbl, EP_NMH *nmd);
++extern EP_NMH     *ep_nmh_find (EP_NMH_TABLE *tbl, EP_NMD *nmh);
++
++#if ! defined(CONFIG_EP_NO_CHECK_SUM)
++extern uint32_t    ep_nmd_calc_data_check_sum(EP_SYS *sys, EP_NMD *nmd, int nFrags);
++#endif
++
++/* Public interface */
++extern EP_RAILMASK ep_nmd2railmask (EP_NMD *frags, int nFrags);
++extern void        ep_nmd_subset (EP_NMD *subset, EP_NMD *nmd, unsigned off, unsigned len);
++extern int       ep_nmd_merge (EP_NMD *merged, EP_NMD *a, EP_NMD *b);
++extern int         ep_nmd_map_rails (EP_SYS *sys, EP_NMD *nmd, unsigned railmask);
++
++#endif /* __ELAN__ */
++
++#endif /* __ELAN3_NMH_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/include/elan/rmap.h
+===================================================================
+--- linux-2.6.5.orig/include/elan/rmap.h       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan/rmap.h    2005-05-11 12:10:12.573912056 -0400
+@@ -0,0 +1,49 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN_RMAP_H
++#define __ELAN_RMAP_H
++
++#ident "$Id: rmap.h,v 1.8 2004/05/19 10:24:40 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/rmap.h,v $ */
++
++
++typedef struct ep_rmap_entry 
++{
++    size_t    m_size;
++    u_long    m_addr;
++} EP_RMAP_ENTRY;
++
++typedef struct ep_rmap 
++{
++    spinlock_t             m_lock;
++    kcondvar_t       m_wait;
++    u_int          m_size;
++    u_int          m_free;
++    u_int          m_want;
++    char            *m_name;
++    EP_RMAP_ENTRY    m_map[1];
++} EP_RMAP;
++
++extern void        ep_display_rmap (EP_RMAP *map);
++
++extern void          ep_rmapinit (EP_RMAP *rmap, char *name, u_int mapsize);
++extern unsigned long ep_rmalloc (EP_RMAP *rmap, size_t size, int cansleep);
++extern unsigned long ep_rmalloc_constrained (EP_RMAP *mp, size_t size, unsigned long alo, unsigned long ahi, unsigned long align, int cansleep);
++extern void          ep_rmfree (EP_RMAP *rmap, size_t size, unsigned long addr);
++extern unsigned long ep_rmget (EP_RMAP *rmap, size_t size, unsigned long addr);
++extern EP_RMAP      *ep_rmallocmap (size_t size, char *name, int cansleep);
++extern void          ep_rmfreemap (EP_RMAP *map);
++
++#endif /* __ELAN3_RMAP_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/include/elan/statemap.h
+===================================================================
+--- linux-2.6.5.orig/include/elan/statemap.h   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan/statemap.h        2005-05-11 12:10:12.574911904 -0400
+@@ -0,0 +1,52 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN_STATEMAP_H
++#define __ELAN_STATEMAP_H
++
++#ident "$Id: statemap.h,v 1.8 2003/10/07 13:22:38 david Exp $"
++/*      $Source: /cvs/master/quadrics/epmod/statemap.h,v $ */
++
++#include <elan/bitmap.h>
++
++/******************************** global state bitmap stuff **********************************/
++typedef struct
++{
++   unsigned int size;
++   unsigned int nob;
++   unsigned int changemap_nob;
++   unsigned int bitmap_nob;
++   bitmap_t    *changemap0;
++   bitmap_t    *changemap1;
++   bitmap_t    *changemap2;
++   bitmap_t    *bitmap;
++} statemap_t;
++
++extern bitmap_t             statemap_getseg (statemap_t *map, unsigned int offset);
++extern void           statemap_setseg (statemap_t *map, unsigned int offset, bitmap_t seg);
++extern bitmap_t       statemap_getbits (statemap_t *map, unsigned int offset, int nbits);
++extern void           statemap_setbits (statemap_t *map, unsigned int offset, bitmap_t bits, int nbits);
++extern void           statemap_zero (statemap_t *map);
++extern void           statemap_setmap (statemap_t *dst, statemap_t *src);
++extern void           statemap_ormap (statemap_t *dst, statemap_t *src);
++extern int          statemap_findchange (statemap_t *map, bitmap_t *newseg, int clearchange);
++extern int            statemap_changed (statemap_t *map);
++extern void           statemap_reset (statemap_t *map);
++extern void           statemap_copy (statemap_t *dst, statemap_t *src);
++extern void           statemap_clearchanges (statemap_t *map);
++extern bitmap_t      *statemap_tobitmap (statemap_t *map);
++extern statemap_t    *statemap_create (int size);
++extern void           statemap_destroy (statemap_t *map);
++
++#endif /* __ELAN_STATEMAP_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/include/elan/stats.h
+===================================================================
+--- linux-2.6.5.orig/include/elan/stats.h      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan/stats.h   2005-05-11 12:10:12.574911904 -0400
+@@ -0,0 +1,85 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Limited.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: stats.h,v 1.5 2003/09/24 13:55:37 david Exp $"
++/*      $Source: /cvs/master/quadrics/elanmod/modsrc/stats.h,v $*/
++
++#ifndef __ELAN_STATS_H
++#define __ELAN_STATS_H
++
++
++/* non-kernel headings */
++#define ELAN_STATS_NAME_MAX_LEN ((uint)64)
++typedef unsigned int ELAN_STATS_IDX;
++
++typedef struct elan_stats_map
++{
++      char entry_name[ELAN_STATS_NAME_MAX_LEN];
++      int  index;
++} ELAN_STATS_MAP;
++
++#if defined(__KERNEL__)
++
++/* stats callbacks */
++#define ELAN_STATS_OPS_VERSION ((u_int)1)
++typedef struct elan_stats_ops
++{
++      u_int  ops_version;
++
++      int (*elan_stats_get_name)    (void * arg, uint index,   caddr_t  name);
++      int (*elan_stats_get_block)   (void * arg, uint entries, ulong   *values);
++      int (*elan_stats_clear_block) (void * arg);
++
++} ELAN_STATS_OPS;
++
++typedef struct elan_stats_struct
++{
++      struct list_head   node;
++
++      ELAN_STATS_IDX     statidx;
++      char               block_name[ELAN_STATS_NAME_MAX_LEN];
++      uint               num_entries;
++      ELAN_STATS_OPS *ops;
++      void              *arg;
++
++} ELAN_STATS_STRUCT;
++
++/* stats.c */
++extern int                   elan_stats_register   (ELAN_STATS_IDX    *statidx, 
++                                                     char              *block_name, 
++                                                     uint               num_entries,
++                                                     ELAN_STATS_OPS *ops,
++                                                     void              *arg);
++
++extern int                   elan_stats_deregister  (ELAN_STATS_IDX  statidx);
++extern ELAN_STATS_STRUCT *elan_stats_find        (ELAN_STATS_IDX  statidx);
++extern ELAN_STATS_STRUCT *elan_stats_find_by_name(caddr_t         block_name);
++extern ELAN_STATS_STRUCT *elan_stats_find_next   (ELAN_STATS_IDX  statidx);
++
++
++/* elan_stats.c */
++extern int elan_stats_get_next_index (ELAN_STATS_IDX statidx, ELAN_STATS_IDX *next_statidx);
++
++extern int elan_stats_find_index     (caddr_t  block_name, ELAN_STATS_IDX *statidx, uint *num_entries);
++
++extern int elan_stats_get_block_info (ELAN_STATS_IDX statidx, caddr_t block_name, uint *num_entries);
++
++extern int elan_stats_get_index_name (ELAN_STATS_IDX statidx, uint index, caddr_t name);
++
++extern int elan_stats_get_block      (ELAN_STATS_IDX statidx, uint entries, ulong   *values);
++
++extern int elan_stats_clear_block    (ELAN_STATS_IDX statidx);
++
++#endif /* __KERNEL__ */
++
++#endif /* __ELAN_STATS_H */
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.6.5/include/elan3/compat.h
+===================================================================
+--- linux-2.6.5.orig/include/elan3/compat.h    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan3/compat.h 2005-05-11 12:10:12.575911752 -0400
+@@ -0,0 +1,177 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: compat.h,v 1.4 2004/06/09 09:07:03 mike Exp $ $Name: QSNETMODULES-4-31_20050321 $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/compat.h,v $*/
++
++#ifndef __ELAN3_COMPAT_H
++#define __ELAN3_COMPAT_H
++
++/* compatibility header to allow Eagle branch QSNETLIBS 
++ * to compile against head kernel */
++
++#define ELAN_EAGLE_COMPAT
++
++/* vmseg.h */
++#define ELAN_FLAGSTATS                                ELAN3_FLAGSTATS
++
++/* uregs.h */
++#define ELAN_STATS_NAME                               ELAN3_STATS_NAME
++#define elan3_stats_names                     elan_stats_names
++
++/* spinlock.h */
++#define ELAN_SPINLOCK                         ELAN3_SPINLOCK
++#define ELAN_SPINLOCK_MAIN                    ELAN3_SPINLOCK_MAIN
++#define ELAN_SPINLOCK_ELAN                    ELAN3_SPINLOCK_ELAN
++#define ELAN_ME_SPINENTER                     ELAN3_ME_SPINENTER
++#define ELAN_ME_FORCEENTER                    ELAN3_ME_FORCEENTER
++#define ELAN_ME_SPINEXIT                      ELAN3_ME_SPINEXIT
++#define ELAN_SPINENTER                                ELAN3_SPINENTER
++#define ELAN_SPINEXIT                         ELAN3_SPINEXIT
++#define elan3_me_spinblock                    elan_me_spinblock
++#define elan3_spinenter                               elan_spinenter
++
++/* elanio.h */
++#define ELANIO_CONTROL_PATHNAME                       ELAN3IO_CONTROL_PATHNAME
++#define ELANIO_USER_PATHNAME                  ELAN3IO_USER_PATHNAME
++#define ELANIO_SDRAM_PATHNAME                 ELAN3IO_SDRAM_PATHNAME
++#define ELANIO_MAX_PATHNAMELEN                        ELAN3IO_MAX_PATHNAMELEN
++
++#define ELANIO_SET_BOUNDARY_SCAN              ELAN3IO_SET_BOUNDARY_SCAN
++#define ELANIO_CLEAR_BOUNDARY_SCAN            ELAN3IO_CLEAR_BOUNDARY_SCAN
++#define ELANIO_READ_LINKVAL                   ELAN3IO_READ_LINKVAL
++#define ELANIO_WRITE_LINKVAL                  ELAN3IO_WRITE_LINKVAL
++#define ELANIO_SET_DEBUG_STRUCT                       ELAN3IO_SET_DEBUG_STRUCT
++#define ELANIO_SET_DEBUG                      ELAN3IO_SET_DEBUG
++#define ELANIO_DEBUG_BUFFER_STRUCT            ELAN3IO_DEBUG_BUFFER_STRUCT
++#define ELANIO_DEBUG_BUFFER                   ELAN3IO_DEBUG_BUFFER
++#define ELANIO_NETERR_SERVER_STRUCT           ELAN3IO_NETERR_SERVER_STRUCT
++#define ELANIO_NETERR_SERVER                  ELAN3IO_NETERR_SERVER
++#define ELANIO_NETERR_FIXUP                   ELAN3IO_NETERR_FIXUP
++
++#define ELANIO_FREE                           ELAN3IO_FREE
++#define ELANIO_ATTACH                         ELAN3IO_ATTACH
++#define ELANIO_DETACH                         ELAN3IO_DETACH
++#define ELANIO_ADDVP_STRUCT                   ELAN3IO_ADDVP_STRUCT
++#define ELANIO_ADDVP                          ELAN3IO_ADDVP
++#define ELANIO_REMOVEVP                               ELAN3IO_REMOVEVP
++#define ELANIO_BCASTVP_STRUCT                 ELAN3IO_BCASTVP_STRUCT
++#define ELANIO_BCASTVP                                ELAN3IO_BCASTVP
++#define ELANIO_LOAD_ROUTE_STRUCT              ELAN3IO_LOAD_ROUTE_STRUCT
++#define ELANIO_LOAD_ROUTE                     ELAN3IO_LOAD_ROUTE
++#define ELANIO_PROCESS                                ELAN3IO_PROCESS
++#define ELANIO_SETPERM_STRUCT                 ELAN3IO_SETPERM_STRUCT
++#define ELANIO_SETPERM                                ELAN3IO_SETPERM
++#define ELANIO_CLEARPERM_STRUCT                       ELAN3IO_CLEARPERM_STRUCT
++#define ELANIO_CLEARPERM                      ELAN3IO_CLEARPERM
++#define ELANIO_CHANGEPERM_STRUCT              ELAN3IO_CHANGEPERM_STRUCT
++#define ELANIO_CHANGEPERM                     ELAN3IO_CHANGEPERM
++#define ELANIO_HELPER_THREAD                  ELAN3IO_HELPER_THREAD
++#define ELANIO_WAITCOMMAND                    ELAN3IO_WAITCOMMAND
++#define ELANIO_BLOCK_INPUTTER                 ELAN3IO_BLOCK_INPUTTER
++#define ELANIO_SET_FLAGS                      ELAN3IO_SET_FLAGS
++#define ELANIO_WAITEVENT                      ELAN3IO_WAITEVENT
++#define ELANIO_ALLOC_EVENTCOOKIE              ELAN3IO_ALLOC_EVENTCOOKIE
++#define ELANIO_FREE_EVENTCOOKIE                       ELAN3IO_FREE_EVENTCOOKIE
++#define ELANIO_ARM_EVENTCOOKIE                        ELAN3IO_ARM_EVENTCOOKIE
++#define ELANIO_WAIT_EVENTCOOKIE                       ELAN3IO_WAIT_EVENTCOOKIE
++#define ELANIO_SWAPSPACE                      ELAN3IO_SWAPSPACE
++#define ELANIO_EXCEPTION_SPACE                        ELAN3IO_EXCEPTION_SPACE
++#define ELANIO_GET_EXCEPTION                  ELAN3IO_GET_EXCEPTION
++#define ELANIO_UNLOAD_STRUCT                  ELAN3IO_UNLOAD_STRUCT
++#define ELANIO_UNLOAD                         ELAN3IO_UNLOAD
++#define ELANIO_GET_ROUTE_STRUCT                       ELAN3IO_GET_ROUTE_STRUCT
++#define ELANIO_GET_ROUTE                      ELAN3IO_GET_ROUTE
++#define ELANIO_RESET_ROUTE_STRUCT             ELAN3IO_RESET_ROUTE_STRUCT
++#define ELANIO_RESET_ROUTE                    ELAN3IO_RESET_ROUTE
++#define ELANIO_CHECK_ROUTE_STRUCT             ELAN3IO_CHECK_ROUTE_STRUCT
++#define ELANIO_CHECK_ROUTE                    ELAN3IO_CHECK_ROUTE
++#define ELANIO_VP2NODEID_STRUCT                       ELAN3IO_VP2NODEID_STRUCT
++#define ELANIO_VP2NODEID                      ELAN3IO_VP2NODEID
++#define ELANIO_SET_SIGNAL                     ELAN3IO_SET_SIGNAL
++#define ELANIO_PROCESS_2_LOCATION_STRUCT      ELAN3IO_PROCESS_2_LOCATION_STRUCT
++#define ELANIO_PROCESS_2_LOCATION             ELAN3IO_PROCESS_2_LOCATION
++#define ELANIO_GET_DEVINFO_STRUCT             ELAN3IO_GET_DEVINFO_STRUCT
++#define ELANIO_GET_DEVINFO                    ELAN3IO_GET_DEVINFO
++#define ELANIO_GET_POSITION_STRUCT            ELAN3IO_GET_POSITION_STRUCT
++#define ELANIO_GET_POSITION                   ELAN3IO_GET_POSITION
++#define ELANIO_STATS_STRUCT                   ELAN3IO_STATS_STRUCT
++#define ELANIO_STATS                          ELAN3IO_STATS
++#  define ELAN_SYS_STATS_DEVICE                       ELAN3_SYS_STATS_DEVICE
++#  define ELAN_SYS_STATS_ELAN3MMU             ELAN3_SYS_STATS_MMU
++
++#define ELANIO_OFF_FLAG_PAGE                  ELAN3IO_OFF_FLAG_PAGE
++#define ELANIO_OFF_UREG_PAGE                  ELAN3IO_OFF_UREG_PAGE
++#define ELANIO_OFF_COMMAND_PAGE                       ELAN3IO_OFF_COMMAND_PAGE
++
++
++/* elanvp.h */
++#define ELAN_ROUTE_SUCCESS                    ELAN3_ROUTE_SUCCESS
++#define ELAN_ROUTE_SYSCALL_FAILED             ELAN3_ROUTE_SYSCALL_FAILED
++#define ELAN_ROUTE_INVALID                    ELAN3_ROUTE_INVALID
++#define ELAN_ROUTE_TOO_LONG                   ELAN3_ROUTE_TOO_LONG
++#define ELAN_ROUTE_LOAD_FAILED                        ELAN3_ROUTE_LOAD_FAILED
++#define ELAN_ROUTE_PROC_RANGE                 ELAN3_ROUTE_PROC_RANGE
++#define ELAN_ROUTE_INVALID_LEVEL              ELAN3_ROUTE_INVALID_LEVEL
++#define ELAN_ROUTE_OCILATES                   ELAN3_ROUTE_OCILATES
++#define ELAN_ROUTE_WRONG_DEST                 ELAN3_ROUTE_WRONG_DEST
++#define ELAN_ROUTE_TURN_LEVEL                 ELAN3_ROUTE_TURN_LEVEL
++#define ELAN_ROUTE_NODEID_UNKNOWN             ELAN3_ROUTE_NODEID_UNKNOWN
++
++/* elandev.h */
++#define ELAN_STATS                            ELAN3_STATS
++#define ELAN_STATS_VERSION                    ELAN3_STATS_VERSION
++
++/* perm.h */
++#define ELAN_PERM_NOREMOTE                    ELAN3_PERM_NOREMOTE
++#define ELAN_PERM_LOCAL_READ                  ELAN3_PERM_LOCAL_READ
++#define ELAN_PERM_REMOTEALL                   ELAN3_PERM_REMOTEALL
++
++/* threadsyscall.h */
++#define ELAN_ABORT_TRAPNUM                    ELAN3_ABORT_TRAPNUM
++#define ELAN_ELANCALL_TRAPNUM                 ELAN3_ELANCALL_TRAPNUM
++#define ELAN_SYSCALL_TRAPNUM                  ELAN3_SYSCALL_TRAPNUM
++#define ELAN_SYS_close                                ELAN3_SYS_close
++#define ELAN_SYS_getpid                               ELAN3_SYS_getpid
++#define ELAN_SYS_ioctl                                ELAN3_SYS_ioctl
++#define ELAN_SYS_kill                         ELAN3_SYS_kill
++#define ELAN_SYS_lseek                                ELAN3_SYS_lseek
++#define ELAN_SYS_mmap                         ELAN3_SYS_mmap
++#define ELAN_SYS_munmap                               ELAN3_SYS_munmap
++#define ELAN_SYS_open                         ELAN3_SYS_open
++#define ELAN_SYS_poll                         ELAN3_SYS_poll
++#define ELAN_SYS_read                         ELAN3_SYS_read
++#define ELAN_SYS_write                                ELAN3_SYS_write
++#define ELAN_T_SYSCALL_CODE                   ELAN3_T_SYSCALL_CODE
++#define ELAN_T_SYSCALL_ERRNO                  ELAN3_T_SYSCALL_ERRNO
++
++/* elansyscall.h */
++#define ELAN_SYS_FLAG_DMA_BADVP                       ELAN3_SYS_FLAG_DMA_BADVP
++#define ELAN_SYS_FLAG_THREAD_BADVP            ELAN3_SYS_FLAG_THREAD_BADVP
++#define ELAN_SYS_FLAG_DMAFAIL                 ELAN3_SYS_FLAG_DMAFAIL
++#define ELAN_SYS_FLAG_NETERR                  ELAN3_SYS_FLAG_NETERR
++
++/* intrinsics.h */
++#define elan_copy64w                          elan3_copy64w
++#define elan_read64dw                         elan3_read64dw
++#define elan_write64dw                                elan3_write64dw
++
++#ifndef ELAN_POLL_EVENT
++#define ELAN_POLL_EVENT                               ELAN3_POLL_EVENT
++#endif
++#ifndef ELAN_WAIT_EVENT
++#define ELAN_WAIT_EVENT                               ELAN3_WAIT_EVENT
++#endif
++
++#endif /* __ELAN3_COMPAT_H */
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++
+Index: linux-2.6.5/include/elan3/dma.h
+===================================================================
+--- linux-2.6.5.orig/include/elan3/dma.h       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan3/dma.h    2005-05-11 12:10:12.575911752 -0400
+@@ -0,0 +1,213 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN3_DMA_H
++#define __ELAN3_DMA_H
++
++#ident "$Id: dma.h,v 1.38 2002/08/21 12:43:27 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/dma.h,v $ */
++
++#include <elan3/e3types.h>
++#include <elan3/events.h>
++
++/* Alignment for a DMA descriptor */
++#define E3_DMA_ALIGN          (32)
++
++/* The maximum size a DMA can be (i.e. < 2GB) */
++#define E3_MAX_DMA_SIZE               0x7fffffff
++
++/* This macro returns TRUE if a fixup for the ELAN_REVB_BUG_2 problem is required 
++ * i.e. if the DMA begins in the last 64-bytes of a page and its size causes it to enter the
++ * next page, hence causing the Elan to issue 2 (64-byte) block reads to different pages.
++ * See GNAT hw-elan3/3263
++ */
++#define E3_DMA_REVB_BUG_2(SIZE, ADDR, PAGESIZE)       \
++      ( (((int) (ADDR) & (PAGESIZE-64)) == (PAGESIZE-64)) && (-(((int) (ADDR) | ~(PAGESIZE-1))) < (SIZE)) )
++
++/* There is a point where a dma runs quicker from main memory than
++ * when running from sdram and having to copy all the data down
++ * first.
++ */
++#define E3_DMA_SDRAM_CUTOFF   128
++
++typedef union _e3_DmaType
++{
++    E3_uint32 type;
++    struct
++    {
++#if defined(__LITTLE_ENDIAN__)
++      E3_uint32 dataType:2;   /* Bits 0 to 1   */
++      E3_uint32 direction:3;  /* Bit  4 to 2   */
++      E3_uint32 opCode:4;     /* Bits 5 to 8   */
++      E3_uint32 failCount:6;  /* Bits 9 to 14  */
++      E3_uint32 isRemote:1;   /* Bit  15       */
++      E3_uint32 Context:13;   /* Bits 16 to 28 */
++      E3_uint32 :3;           /* Bits 29 to 31 */
++#else
++      E3_uint32 :3;           /* Bits 29 to 31 */
++      E3_uint32 Context:13;   /* Bits 16 to 28 */
++      E3_uint32 isRemote:1;   /* Bit  15       */
++      E3_uint32 failCount:6;  /* Bits 9 to 14  */
++      E3_uint32 opCode:4;     /* Bits 5 to 8   */
++      E3_uint32 direction:3;  /* Bit  4 to 2   */
++      E3_uint32 dataType:2;   /* Bits 0 to 1   */
++#endif
++    } s;
++} E3_DmaType;
++
++#define E3_DMA_CONTEXT_MASK   (ALL_CONTEXT_BITS << 16)
++
++#define E3_DMA_CONTEXT(type)  (((type) >> 16) & ALL_CONTEXT_BITS)
++#define E3_DMA_ISREMOTE(type) (((type) >> 15) & 1)
++#define E3_DMA_FAILCOUNT(type)        (((type) >> 9) & 0x3F)
++#define E3_DMA_OPCODE(type)   (((type) >> 5) & 0xF)
++#define E3_DMA_DIRECTION(type)        (((type) >> 2) & 0x7)
++#define EP_DMA_DATATYPE(type) (((type) >> 0) & 0x3)
++
++#define E3_DMA_TYPE(dataType, direction, opCode, failCount) \
++    (((dataType) & 0x3) | (((direction) & 7) << 2) | (((opCode) & 0xF) << 5) | (((failCount) & 0x3F) << 9))
++
++
++typedef union _e3_CookieVProc
++{
++    E3_uint32 cookie_vproc;
++    struct
++    {
++#if defined(__LITTLE_ENDIAN__)
++      E3_uint32 vproc:16;                     /* Bit  15 to 0  */
++      E3_uint32 cookie:16;                    /* Bits 31 to 16 */
++#else
++      E3_uint32 cookie:16;                    /* Bits 31 to 16 */
++      E3_uint32 vproc:16;                     /* Bit  15 to 0  */
++#endif
++    } s;
++} E3_CookieVProc;
++
++#define E3_DMA_COOKIE_PROC(Cookie, VProc)  (((VProc) & 0xffff) | (((Cookie) << 16)))
++
++#define DMA_COOKIE_MASK               (0xffff0000)
++#define DMA_PROCESS_MASK      (0x0000ffff)
++
++/* We use the bottom bit of the cookie to
++ * distinguish main/thread generated cookies
++ */
++#define DMA_COOKIE_THREAD     (0x01 << 16)
++
++/* We use the next bit of the cookie to
++ * distinguish locally/remotely generated cookies 
++ */
++#define DMA_COOKIE_REMOTE     (0x02 << 16)
++
++/* Assign and increment cookie (NB: we have reserved the bottom two bits)
++ */
++#define DMA_COOKIE(COOKIE, VPROC)      ((((COOKIE) += (0x4 << 16)) & DMA_COOKIE_MASK) | VPROC)
++#define DMA_REMOTE_COOKIE(COOKIE, VPROC) ((((COOKIE) += (0x4 << 16)) & DMA_COOKIE_MASK) | DMA_COOKIE_REMOTE | VPROC)
++
++#define DMA_COOKIE_REFRESH(COOKIEVP, COOKIE)                          \
++do {                                                                  \
++      COOKIEVP &= ~DMA_COOKIE_MASK;           /* Clear cookie */      \
++      COOKIEVP |= DMA_COOKIE(COOKIE,0);       /* Assign new cookie */ \
++} while (0)
++
++typedef struct e3_dma
++{
++    E3_DmaType                dma_u;
++    E3_uint32         dma_size;
++    E3_Addr           dma_source;
++    E3_Addr           dma_dest;
++    E3_Addr           dma_destEvent;
++    E3_CookieVProc    dma_destCookieProc;
++    E3_Addr           dma_srcEvent;
++    E3_CookieVProc    dma_srcCookieProc;
++} E3_DMA;
++
++
++/*
++ * Word-swapped version of DMA descriptor.
++ * This is used by the UltraSPARC code to format the descriptor
++ * in main memory before block-copying it down to Elan SDRAM.
++ * In the process it does a dword (64-bit) conversion and so swaps
++ * the word order on a double-word pair basis
++ */
++typedef struct e3_dma_swapped
++{
++    E3_uint32         dma_size;
++    E3_DmaType                dma_u;
++    E3_Addr           dma_dest;
++    E3_Addr           dma_source;
++    E3_CookieVProc    dma_destCookieProc;
++    E3_Addr           dma_destEvent;
++    E3_CookieVProc    dma_srcCookieProc;
++    E3_Addr           dma_srcEvent;
++} E3_DMA_SWAPPED;
++
++/* Define a Main memory structure for DMA desc based on Endianess of machine */
++#if defined(__LITTLE_ENDIAN__)
++#define E3_DMA_MAIN E3_DMA
++#else
++#define E3_DMA_MAIN E3_DMA_SWAPPED;
++#endif
++
++#define dma_type       dma_u.type
++#define dma_failCount    dma_u.s.failCount
++#define dma_isRemote     dma_u.s.isRemote
++#define dma_opCode       dma_u.s.opCode
++#define dma_direction    dma_u.s.direction
++#define dma_dataType     dma_u.s.dataType
++#define dma_queueContext dma_u.s.Context
++
++#define dma_destCookieVProc   dma_destCookieProc.cookie_vproc
++#define dma_destVProc       dma_destCookieProc.s.vproc
++#define dma_destCookie              dma_destCookieProc.s.cookie
++#define dma_srcCookieVProc    dma_srcCookieProc.cookie_vproc
++#define dma_srcVProc        dma_srcCookieProc.s.vproc
++#define dma_srcCookie       dma_srcCookieProc.s.cookie
++
++/*
++ * Values for dma_opCode
++ */
++#define DMA_NORMAL                    0
++#define DMA_QUEUED                    1
++#define DMA_NORMAL_BROADCAST          2
++#define DMA_QUEUED_BROADCAST          3
++#define DMA_NORMAL_UNSAFE             4
++#define DMA_QUEUED_UNSAFE             5
++#define DMA_NORMAL_BROADCAST_UNSAFE   6
++#define DMA_QUEUED_BROADCAST_UNSAFE   7
++
++/*
++ * Values for dma_direction
++ */
++#define DMA_WRITE             0
++#define DMA_READ_REQUEUE      1
++#define DMA_READ              3
++#define DMA_READ_BROADCAST    7
++
++/*
++ * Values for dma_dataType
++ */
++#define DMA_BYTE              0
++#define DMA_HALFWORD          1
++#define DMA_WORD              2
++#define DMA_DOUBLE            3
++
++/* OUT OF DATE ?
++  #define DMA_OPCODE_SHIFT    3
++  #define DMA_FAILCOUNT_SHIFT 9
++*/
++#define DMA_TYPE_ISREMOTE     (1 << 15)
++#define DMA_TYPE_READ         (3 << 2)
++#define DMA_TYPE_READ_REQUEUE (1 << 2)
++#define DMA_TYPE_DIRECTION_MASK       (3 << 2)
++
++#endif /* __ELAN3_DMA_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/include/elan3/e3types.h
+===================================================================
+--- linux-2.6.5.orig/include/elan3/e3types.h   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan3/e3types.h        2005-05-11 12:10:12.582910688 -0400
+@@ -0,0 +1,82 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN3_E3TYPES_H
++#define __ELAN3_E3TYPES_H
++
++#ident "$Id: e3types.h,v 1.18 2002/08/09 11:23:33 addy Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/e3types.h,v $ */
++
++#include <qsnet/config.h>
++/*
++ * "flip" values for correctly indexing into
++ * block data which was copied from the Elan
++ * using 64 bit accesses.
++ */
++#if defined(__LITTLE_ENDIAN__)
++#  define ByteEndianFlip  0
++#  define ShortEndianFlip 0
++#  define WordEndianFlip  0
++#else
++#  define ByteEndianFlip  7
++#  define ShortEndianFlip 3
++#  define WordEndianFlip  1
++#endif
++
++
++#ifndef _ASM
++
++typedef signed int       E3_int;
++typedef unsigned int             E3_uint;
++
++typedef signed char      E3_int8;
++typedef unsigned char            E3_uint8;
++
++typedef signed short     E3_int16;
++typedef unsigned short           E3_uint16;
++
++typedef signed int       E3_int32;
++typedef unsigned int             E3_uint32;
++
++#ifdef __ELAN3__
++typedef signed long long   E3_int64;
++typedef unsigned long long E3_uint64;
++#ifdef _MAIN_LP64
++/* NOTE: If the Main is 64-bit we declare the Elan thread's
++ * E3_uintptr to be 64-bits too
++ */
++typedef unsigned long long E3_uintptr;
++#else
++typedef unsigned long      E3_uintptr;
++#endif
++
++#else
++
++#ifdef _LP64
++typedef signed long        E3_int64;
++typedef unsigned long      E3_uint64;
++typedef unsigned long      E3_uintptr;
++#else /* _ILP32 */
++typedef signed long long   E3_int64;
++typedef unsigned long long E3_uint64;
++typedef unsigned long      E3_uintptr;
++#endif
++
++#endif /* __ELAN3__ */
++
++/* 32-bit Elan3 address */
++typedef E3_uint32        E3_Addr;
++
++#endif /* _ASM */
++
++#endif /* __ELAN3_E3TYPES_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/include/elan3/elan3mmu.h
+===================================================================
+--- linux-2.6.5.orig/include/elan3/elan3mmu.h  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan3/elan3mmu.h       2005-05-11 12:10:12.583910536 -0400
+@@ -0,0 +1,346 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN3_ELAN3MMU_H
++#define __ELAN3_ELAN3MMU_H
++
++#ident "$Id: elan3mmu.h,v 1.40.2.1 2004/12/14 10:19:48 mike Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/elan3mmu.h,v $*/
++
++
++#include <elan3/pte.h>
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++typedef struct elan3mmu_global_stats
++{
++    int               version;
++    int               pteload;
++    int               pteunload;
++    int               ptereload;
++
++    int               streamable_alloc;
++    int               streamable_free;
++    int               streamable_alloc_failed;
++
++    int               num_ptbl_level[4]; /* number of level N  ptbls */
++
++    int               create_ptbl_failed;                     /* count of ptbl creation failure */
++
++    int         lX_alloc_l3;                          /* count of l3 ptbls used as lX */
++    int         lX_freed_l3;                          /* count of lX ptbls freed as l3 */
++
++    int               l2_alloc_l3;                            /* count of l3 ptbls used as l2 */
++    int               l2_freed_l3;                            /* count of l2 ptbls freed as l3 */
++
++    int               stolen_ptbls;                           /* count of l3 ptbls stolen */
++} ELAN3MMU_GLOBAL_STATS;
++
++#define ELAN3MMU_STATS_VERSION                1
++
++#define ELAN3MMU_STAT(what)           (elan3mmu_global_stats.what++)
++#define ELAN3MMU_SET_STAT(what,count) (elan3mmu_global_stats.what = count)
++
++#ifdef __KERNEL__
++
++#define ELAN3_PT_SHIFT        (ELAN3_L2_SHIFT + 2)
++
++typedef struct elan3_ptbl
++{
++    struct elan3_ptbl  *ptbl_parent;                          /* Parent page table, or next on freelist */
++    struct elan3mmu    *ptbl_elan3mmu;                        /* elan3mmu we're allocated for */
++    E3_Addr             ptbl_base;                            /* Virtual address we're mapping */
++    u_char              ptbl_index;                           /* Index in ptbl group */
++    u_char              ptbl_valid;                           /* Number of valid entries */
++    u_char              ptbl_flags;                           /* Flags, defined below. */
++    u_char              ptbl_spare;
++} ELAN3_PTBL;
++
++#define ptbl_next     ptbl_parent                             /* Parent pointer is next pointer when on free list */
++
++#define PTBL_LEVEL_X            0x00
++#define PTBL_LEVEL_1          0x01
++#define PTBL_LEVEL_2          0x02
++#define PTBL_LEVEL_3          0x03
++#define PTBL_LEVEL_MASK               0x03
++#define PTBL_LOCKED           0x04                            /* Page table is locked,  protects all fields */
++#define PTBL_KEEP             0x08                            /* This ptbl is not to be stolen */
++#define PTBL_ALLOCED          0x10                            /* This ptbl has been allocated, and is not free */
++#define PTBL_GROUPED          0x20                            /* This ptbl is a member of a group of ptbls */
++#define PTBL_KERNEL           0x80                            /* This ptbl is allocated for the kernel */
++
++#define PTBL_LEVEL(flags)     ((flags) & PTBL_LEVEL_MASK)
++#define PTBL_IS_LOCKED(flags) (((flags) & (PTBL_LOCKED|PTBL_ALLOCED)) == (PTBL_LOCKED|PTBL_ALLOCED))
++
++#if ELAN3_PAGE_SHIFT == 13
++#  define PTBL_GROUP_SIZE     8192                            /* page table groups are 8k bytes */
++#  define PTBLS_PER_GROUP_L1  8                               /* Number of level 1 tables in a group */
++#  define PTBLS_PER_GROUP_L2  32                              /*   ... level 2 */
++#  define PTBLS_PER_GROUP_L3  32                              /*   ... level 3 */
++#  define PTBLS_PER_GROUP_LX  32                              /*   ... level X */
++#  define PTBLS_PER_GROUP_MAX 32                              /*  max of l1,l2,l3,lX */
++#else
++#  define PTBL_GROUP_SIZE     4096                            /* page table groups are 4k bytes */
++#  define PTBLS_PER_GROUP_L1  4                               /* Number of level 1 tables in a group */
++#  define PTBLS_PER_GROUP_L2  16                              /*   ... level 2 */
++#  define PTBLS_PER_GROUP_L3  8                               /*   ... level 3 */
++#  define PTBLS_PER_GROUP_LX  16                              /*   ... level X */
++#  define PTBLS_PER_GROUP_MAX 16                              /*  max of l1,l2,l3,lX */
++#endif
++
++#define HMES_PER_GROUP                (PTBLS_PER_GROUP_L3*ELAN3_L3_ENTRIES)
++
++#if ELAN3_PAGE_SHIFT == 13
++#  define PTBLS_PER_PTBL_L1   4                               /* 256 PTPs */
++#  define PTBLS_PER_PTBL_L2   1                               /* 64 PTPs */
++#  define PTBLS_PER_PTBL_L3   1                               /* 32 PTEs */
++#else
++#  define PTBLS_PER_PTBL_L1   4                               /* 256 PTPs */
++#  define PTBLS_PER_PTBL_L2   1                               /* 64 PTPs */
++#  define PTBLS_PER_PTBL_L3   2                               /* 64 PTEs */
++#endif
++
++#define ELAN3_LX_ENTRIES     (32) 
++#define PTBLS_PER_PTBL_LX   (1)       
++
++#define L1_VA_PER_PTBL        (ELAN3_L1_SIZE*(ELAN3_L1_ENTRIES/PTBLS_PER_PTBL_L1))    /* 4 ptbl for L1 */
++#define L2_VA_PER_PTBL        (ELAN3_L2_SIZE*(ELAN3_L2_ENTRIES/PTBLS_PER_PTBL_L2))    /* 1 ptbl for L2 */
++#define L3_VA_PER_PTBL        (ELAN3_L3_SIZE*(ELAN3_L3_ENTRIES/PTBLS_PER_PTBL_L3))    /* 1 ptbl for L3 */
++
++typedef struct elan3_ptbl_gr
++{
++    struct elan3_ptbl_gr *pg_next;                            /* Next in list. */
++    int                        pg_level;                              /* Level PG allocated for */
++    sdramaddr_t                pg_addr;                               /* sdram offset of ptes/ptps */    
++    ELAN3_PTBL                 pg_ptbls[PTBLS_PER_GROUP_MAX];         /* The actual page tables */
++} ELAN3_PTBL_GR;
++
++
++/*
++ * The elan3mmu structure is the mmu dependant hardware address translation
++ * structure linked to the address space structure to show the translatioms
++ * provided by the elan for an address sapce.
++ *
++ * We also have a doubly linked list of 'regions' which allow the 
++ * elan3mmu code to determine the access permissions for the elan 
++ * dependant on the virtual address that the translation is being
++ * loaded at.
++ */
++
++typedef struct elan3mmu_rgn
++{
++    struct elan3mmu_rgn *rgn_mnext;                           /* Doubly linked list of regions */
++    struct elan3mmu_rgn *rgn_mprev;                           /*   sorted on main address */ 
++    caddr_t            rgn_mbase;                             /* main address of base of region */
++
++    struct elan3mmu_rgn *rgn_enext;                           /* Doubly linked list of regions */
++    struct elan3mmu_rgn *rgn_eprev;                           /*   sorted on elan address */
++    E3_Addr            rgn_ebase;                             /* elan address of base of region */
++
++    u_int              rgn_len;                               /* length of region */
++    u_int              rgn_perm;                              /* elan access permission */
++} ELAN3MMU_RGN;
++
++typedef struct elan3mmu
++{
++    spinlock_t                    elan3mmu_lock;                      /* spinlock lock for regions */
++    ELAN3MMU_RGN           *elan3mmu_mrgns;                   /* Doubly linked list of memory regions */
++    ELAN3MMU_RGN         *elan3mmu_mtail;                     /* Last memory region on list */
++    ELAN3MMU_RGN         *elan3mmu_mrgnlast;                  /* Last region 'hit' */
++
++    ELAN3MMU_RGN           *elan3mmu_ergns;                   /* Doubly linked list of memory regions */
++    ELAN3MMU_RGN         *elan3mmu_etail;                     /* Last memory region on list */
++    ELAN3MMU_RGN         *elan3mmu_ergnlast;                  /* Last region 'hit' */
++
++    struct elan3_dev        *elan3mmu_dev;                    /* Elan device we're using. */
++    struct elan3_ctxt    *elan3mmu_ctxt;                      /* Elan ctxt we're associated with */
++
++    sdramaddr_t             elan3mmu_ctp;                     /* Context table entry for our context */
++    ELAN3_PTBL                   *elan3mmu_l1ptbl;                    /* Level 1 Page table (first of 4) */
++
++    spinlock_t                    elan3mmu_lXptbl_lock;               /* spinlock for level X table list */
++    ELAN3_PTBL              *elan3mmu_lXptbl;                    /* Level X Page table list         */
++
++#ifdef LINUX
++    struct mm_struct       *elan3mmu_coproc_mm;                       /* Linux mm we're mapping */
++#endif
++} ELAN3MMU;
++
++_NOTE(LOCK_ORDER(elan3mmu::elan3mmu_lock elan3_dev::IntrLock))
++
++_NOTE(MUTEX_PROTECTS_DATA(elan3mmu::elan3mmu_lock,
++                        elan3mmu::elan3mmu_mrgns elan3mmu::elan3mmu_mtail
++                        elan3mmu::elan3mmu_ergns elan3mmu::elan3mmu_etail))
++/* protected by dev->IntrLock for read by device driver */
++_NOTE(DATA_READABLE_WITHOUT_LOCK(elan3mmu::elan3mmu_mrgns elan3mmu::elan3mmu_mtail
++                               elan3mmu::elan3mmu_ergns elan3mmu::elan3mmu_etail))
++
++_NOTE(SCHEME_PROTECTS_DATA("only set to valid region", 
++                         elan3mmu::elan3mmu_ergnlast elan3mmu::elan3mmu_mrgnlast))
++
++_NOTE(MUTEX_PROTECTS_DATA(elan3_dev::IntrLock,
++                        elan3mmu::elan3mmu_l1ptbl 
++                        elan3mmu::elan3mmu_ctp 
++                        elan3mmu::elan3mmu_dev))
++
++_NOTE(DATA_READABLE_WITHOUT_LOCK(elan3mmu::elan3mmu_l1ptbl
++                               elan3mmu::elan3mmu_ctp 
++                               elan3mmu::elan3mmu_dev))
++
++/*
++ * Macros for accessing ptes/ptbls/ptbl_grs
++ */
++
++#define OFFSETOF(object,member)       /* calculate offset of structure member */ \
++      ((size_t) (&(((object *)0)->member)))
++#define PTBL_TO_GR(ptbl)      /* convert ptbl to ptbl group */ \
++      ((ELAN3_PTBL_GR *) ((caddr_t) ((ptbl) - (ptbl)->ptbl_index) - OFFSETOF(ELAN3_PTBL_GR,pg_ptbls[0])))
++#define PTBL_TO_PTADDR(ptbl)  /* convert ptbl to a ptp pointing at it */ \
++        (PTBL_TO_GR(ptbl)->pg_addr + ((ptbl)->ptbl_index<<ELAN3_PT_SHIFT))
++#define PTE_TO_HME(ptbl,pte)  /* convert pte to corresponding hme */ \
++        (PTBL_TO_GR(ptbl)->pg_hmes + ((pte) - (ELAN3_PTE *) PTBL_TO_GR(ptbl)->pg_vaddr))
++#define HME_TO_PTE(ptebl,hme) /* convert hme to corresponding pte */ \
++        ((ELAN3_PTE *) PTBL_TO_GR(ptbl)->pg_vaddr + ((hme) - (PTBL_TO_GR(ptbl)->pg_hmes)))
++
++
++/* Flags for lock_ptbl */
++#define LK_PTBL_NOWAIT                0x1
++#define LK_PTBL_FAILOK                0x2
++
++/* Return values for lock_ptbl */
++#define LK_PTBL_OK            0x0
++#define LK_PTBL_MISMATCH      0x1
++#define LK_PTBL_FAILED                0x2
++
++/* Flags for elan3mmu_ptesync */
++#define       NO_MLIST_LOCK   0
++#define       MLIST_LOCKED    1
++
++/* Flags for elan3mmu_pteload */
++#define PTE_LOAD              0x00
++#define PTE_LOAD_LOCK         0x01                            /* translation should be locked */
++#define PTE_LOAD_NOSYNC               0x02                            /* ref/mod bits should not be sync'ed to page */
++#define PTE_NO_SLEEP            0x04                            /* true if we cant sleep */
++#define PTE_NO_STEAL          0x08                            /* true if we don't want to steal ptbls */
++
++#define PTE_LOAD_ENDIAN_MASK  0x10                            /* mask for endian-ness */
++#define PTE_LOAD_LITTLE_ENDIAN        0x00                            /* translation is to little-endian memory */
++#define PTE_LOAD_BIG_ENDIAN   0x10                            /* translation is to big-endian memory */
++
++
++/* Flags for elan3mmu_unload */
++#define PTE_UNLOAD            0x00
++#define PTE_UNLOAD_UNLOCK     0x01
++#define PTE_UNLOAD_NOFLUSH    0x02
++#define PTE_UNLOAD_NOSYNC     0x04
++
++extern int        elan3mmu_debug;
++#ifdef DEBUG_PRINTF
++#  define HAT_PRINTF0(n,msg)           ((elan3mmu_debug & n) ? (void) elan3_debugf (NULL, DBG_HAT, msg)             : (void) 0)
++#  define HAT_PRINTF1(n,msg,a)                 ((elan3mmu_debug & n) ? (void) elan3_debugf (NULL, DBG_HAT, msg,a)           : (void) 0)
++#  define HAT_PRINTF2(n,msg,a,b)       ((elan3mmu_debug & n) ? (void) elan3_debugf (NULL, DBG_HAT, msg,a,b)         : (void) 0)
++#  define HAT_PRINTF3(n,msg,a,b,c)     ((elan3mmu_debug & n) ? (void) elan3_debugf (NULL, DBG_HAT, msg,a,b,c)       : (void) 0)
++#  define HAT_PRINTF4(n,msg,a,b,c,d)   ((elan3mmu_debug & n) ? (void) elan3_debugf (NULL, DBG_HAT, msg,a,b,c,d)     : (void) 0)
++#  define HAT_PRINTF5(n,msg,a,b,c,d,e)         ((elan3mmu_debug & n) ? (void) elan3_debugf (NULL, DBG_HAT, msg,a,b,c,d,e)   : (void) 0)
++#  define HAT_PRINTF6(n,msg,a,b,c,d,e,f) ((elan3mmu_debug & n) ? (void) elan3_debugf (NULL, DBG_HAT, msg,a,b,c,d,e,f) : (void) 0)
++#  ifdef LINUX
++#    define HAT_PRINTF(n,args...)        ((elan3mmu_debug & n) ? (void) elan3_debugf(NULL, DBG_HAT, ##args) : (void) 0)
++#  endif
++#else
++#  define HAT_PRINTF0(n,msg)
++#  define HAT_PRINTF1(n,msg,a)
++#  define HAT_PRINTF2(n,msg,a,b)
++#  define HAT_PRINTF3(n,msg,a,b,c)
++#  define HAT_PRINTF4(n,msg,a,b,c,d)
++#  define HAT_PRINTF5(n,msg,a,b,c,d,e)
++#  define HAT_PRINTF6(n,msg,a,b,c,d,e,f)
++#  ifdef LINUX
++#    define HAT_PRINTF(n,args...)
++#  endif
++#endif
++
++/* elan3mmu_generic.c */
++extern ELAN3MMU_GLOBAL_STATS elan3mmu_global_stats;
++
++extern void        elan3mmu_init (void);
++extern void        elan3mmu_fini (void);
++
++extern ELAN3MMU          *elan3mmu_alloc (struct elan3_ctxt *ctxt);
++extern void       elan3mmu_free (ELAN3MMU *elan3mmu);
++
++extern void          elan3mmu_set_context_filter (ELAN3_DEV *dev, int ctx, int disabled, E3_uint32 Pend, E3_uint32 *Maskp);
++extern int         elan3mmu_attach (ELAN3_DEV *dev, int ctx, ELAN3MMU *elan3mmu, sdramaddr_t routeTable, E3_uint32 routeMask);
++extern void        elan3mmu_detach (ELAN3_DEV *dev, int ctx);
++
++extern ELAN3MMU_RGN *elan3mmu_findrgn_elan (ELAN3MMU *elan3mmu, E3_Addr addr, int tail);
++extern int           elan3mmu_addrgn_elan (ELAN3MMU *elan3mmu, ELAN3MMU_RGN *nrgn);
++extern ELAN3MMU_RGN *elan3mmu_removergn_elan (ELAN3MMU *elan3mmu, E3_Addr addr);
++extern ELAN3MMU_RGN *elan3mmu_rgnat_elan (ELAN3MMU *elan3mmu, E3_Addr addr);
++extern ELAN3MMU_RGN *elan3mmu_findrgn_main (ELAN3MMU *elan3mmu, caddr_t addr, int tail);
++extern int           elan3mmu_addrgn_main (ELAN3MMU *elan3mmu, ELAN3MMU_RGN *nrgn);
++extern ELAN3MMU_RGN *elan3mmu_removergn_main (ELAN3MMU *elan3mmu, caddr_t addr);
++extern ELAN3MMU_RGN *elan3mmu_rgnat_main (ELAN3MMU *elan3mmu, caddr_t addr);
++
++extern int         elan3mmu_setperm (ELAN3MMU *elan3mmu, caddr_t maddr, E3_Addr eaddr, u_int len, u_int perm);
++extern void        elan3mmu_clrperm (ELAN3MMU *elan3mmu, E3_Addr addr, u_int len);
++extern int         elan3mmu_checkperm (ELAN3MMU *elan3mmu, E3_Addr addr, u_int len, u_int access);
++extern caddr_t             elan3mmu_mainaddr (ELAN3MMU *elan3mmu, E3_Addr addr);
++extern E3_Addr             elan3mmu_elanaddr (ELAN3MMU *elan3mmu, caddr_t addr);
++
++extern void          elan3mmu_expand (ELAN3MMU *elan3mmu, E3_Addr addr, int len, int level, int attr);
++extern void          elan3mmu_reserve (ELAN3MMU *elan3mmu, E3_Addr addr, u_int npages, sdramaddr_t *);
++extern void          elan3mmu_release (ELAN3MMU *elan3mmu, E3_Addr addr, u_int npages, sdramaddr_t *);
++
++extern void          elan3mmu_pteload (ELAN3MMU *elan3mmu, int level, E3_Addr addr, physaddr_t paddr, int perm, int attr);
++extern void        elan3mmu_unload (ELAN3MMU *elan3mmu, E3_Addr addr, u_int len, int flags);
++extern void        elan3mmu_sync (ELAN3MMU *elan3mmu, E3_Addr addr, u_int len, u_int clearflag);
++extern void        elan3mmu_pteunload (ELAN3_PTBL *ptbl, sdramaddr_t pte, int flags, int got_mlist_lock);
++extern void        elan3mmu_ptesync (ELAN3_PTBL *ptbl, sdramaddr_t pte, int flags, int got_mlist_lock);
++extern sdramaddr_t   elan3mmu_ptp2pte (ELAN3MMU *elan3mmu, sdramaddr_t ptp, int level);
++extern sdramaddr_t   elan3mmu_ptefind (ELAN3MMU *elan3mmu, E3_Addr, int *level, ELAN3_PTBL **pptbl, spinlock_t **plock, unsigned long *flags);
++extern sdramaddr_t   elan3mmu_ptealloc (ELAN3MMU *elan3mmu, E3_Addr, int level, ELAN3_PTBL **pptbl, spinlock_t **plock, int attr, unsigned long *flags);
++extern void        elan3mmu_l1inval (ELAN3MMU *elan3mmu, ELAN3_PTBL *l1ptbl, int flags);
++extern int           elan3mmu_l2inval (ELAN3MMU *elan3mmu, ELAN3_PTBL *l2ptbl, int flags, E3_Addr addr, spinlock_t **pl2lock, unsigned long *lock_flags);
++extern int           elan3mmu_l3inval (ELAN3MMU *elan3mmu, ELAN3_PTBL *l3ptbl, int flags, E3_Addr addr, spinlock_t **pl3lock, unsigned long *lock_flags);
++
++extern void          elan3mmu_free_l1ptbl (ELAN3_DEV *dev, ELAN3_PTBL *ptbl, spinlock_t *lock, unsigned long flags);
++extern void          elan3mmu_free_l2ptbl (ELAN3_DEV *dev, ELAN3_PTBL *ptbl, spinlock_t *lock, unsigned long flags);
++extern void          elan3mmu_free_l3ptbl (ELAN3_DEV *dev, ELAN3_PTBL *ptbl, spinlock_t *lock, unsigned long flags);
++
++extern int         elan3mmu_lock_this_ptbl (ELAN3_PTBL *ptbl, int flag, spinlock_t **plock, unsigned long *flags);
++extern int           elan3mmu_lock_ptbl (ELAN3_PTBL *ptbl, u_int flag, ELAN3MMU *elan3mmu, E3_Addr va, int level, spinlock_t **plock, unsigned long *flags);
++extern void        elan3mmu_unlock_ptbl (ELAN3_PTBL *ptbl, spinlock_t *lock, unsigned long flags);
++
++/* elan3mmu_osdep.c */
++extern void        elan3mmu_init_osdep (void);
++extern void        elan3mmu_fini_osdep (void);
++extern void        elan3mmu_alloc_osdep (ELAN3MMU *elan3mmu);
++extern void        elan3mmu_free_osdep (ELAN3MMU *elan3mmu);
++extern ELAN3_PTE     elan3mmu_phys_to_pte (ELAN3_DEV *dev, physaddr_t paddr, int perm);
++extern ELAN3_PTE     elan3mmu_kernel_invalid_pte (ELAN3MMU *elan3mmu);
++
++#if defined (DIGITAL_UNIX)
++#  include <elan3/elan3mmu_dunix.h>
++#elif defined (LINUX)
++#  include <elan3/elan3mmu_linux.h>
++#endif
++
++#endif /* __KERNEL__ */
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* __ELAN3_ELAN3MMU_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/include/elan3/elan3mmu_linux.h
+===================================================================
+--- linux-2.6.5.orig/include/elan3/elan3mmu_linux.h    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan3/elan3mmu_linux.h 2005-05-11 12:10:12.584910384 -0400
+@@ -0,0 +1,39 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN3_MMU_LINUX_H
++#define __ELAN3_MMU_LINUX_H
++
++#ident "$Id: elan3mmu_linux.h,v 1.12 2003/09/24 13:57:24 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/elan3mmu_linux.h,v $*/
++
++/* XXX copy of elan3mmu_dunix.h */
++
++#define ALLOC_ELAN3MMU(ptr,cansleep)          KMEM_ALLOC(ptr, ELAN3MMU *, sizeof (ELAN3MMU), cansleep)
++#define ALLOC_PTBL_GR(ptr,cansleep)           KMEM_ALLOC(ptr, ELAN3_PTBL_GR *, sizeof (ELAN3_PTBL_GR), cansleep)
++#define ALLOC_ELAN3MMU_RGN(ptr,cansleep)      KMEM_ALLOC(ptr, ELAN3MMU_RGN *, sizeof (ELAN3MMU_RGN), cansleep)
++#define ALLOC_HMENTS(ptr,cansleep)            KMEM_ALLOC((ptr,ELAN3_HMENT *, sizeof (ELAN3_HMENT), cansleep)
++
++#define FREE_ELAN3MMU(ptr)                    KMEM_FREE(ptr,sizeof (ELAN3MMU))
++#define FREE_PTBL_GR(ptr)                     KMEM_FREE(ptr,sizeof (ELAN3_PTBL_GR))
++#define FREE_ELAN3MMU_RGN(ptr)                        KMEM_FREE(ptr,sizeof (ELAN3MMU_RGN))
++#define FREE_HMENTS(ptr)                      KMEM_FREE(ptr,sizeof (ELAN3_HMENT))
++
++extern void        elan3mmu_init_osdep(void);
++extern void        elan3mmu_fini_osdep(void);
++
++extern void          elan3mmu_pte_range_unload (ELAN3MMU *elan3mmu, struct mm_struct *mm, caddr_t addr, unsigned long len);
++extern void          elan3mmu_pte_range_update (ELAN3MMU *elan3mmu, struct mm_struct *mm, caddr_t addr, unsigned long len);
++extern void          elan3mmu_pte_ctxt_unload(ELAN3MMU *elan3mmu);
++
++#endif
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/include/elan3/elan3ops.h
+===================================================================
+--- linux-2.6.5.orig/include/elan3/elan3ops.h  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan3/elan3ops.h       2005-05-11 12:10:12.584910384 -0400
+@@ -0,0 +1,42 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++/* $Id: elan3ops.h,v 1.3 2003/09/24 13:57:24 david Exp $ */
++/* $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/elan3ops.h,v $ */
++
++#ifndef _ELAN3_OPS_H
++#define _ELAN3_OPS_H
++
++int get_position          (void *arg, ELAN_POSITION *position);
++int set_position          (void *arg, unsigned short nodeId, unsigned short numNodes);
++
++int elan3mod_create_cap   (void *arg, ELAN_CAP_OWNER owner, ELAN_CAPABILITY *cap);
++int elan3mod_destroy_cap  (void *arg, ELAN_CAP_OWNER owner, ELAN_CAPABILITY *cap);
++
++int elan3mod_create_vp    (void *arg, ELAN_CAP_OWNER owner, ELAN_CAPABILITY *cap, ELAN_CAPABILITY *map);
++int elan3mod_destroy_vp   (void *arg, ELAN_CAP_OWNER owner, ELAN_CAPABILITY *cap, ELAN_CAPABILITY *map);
++
++int elan3mod_attach_cap   (void *arg_ctxt, ELAN_CAPABILITY *cap);
++int elan3mod_detach_cap   (void *arg_ctxt);
++
++extern ELAN_DEV_OPS elan3_dev_ops;
++
++int stats_get_index_name  (void *arg, uint index, caddr_t name);
++int stats_get_block       (void *arg, uint entries, ulong *value);
++int stats_clear_block     (void *arg);
++
++int elan3_register_dev_stats   (ELAN3_DEV * dev);
++void elan3_deregister_dev_stats (ELAN3_DEV * dev);
++
++
++#endif /* __ELAN3_OPS_H */
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.6.5/include/elan3/elanctxt.h
+===================================================================
+--- linux-2.6.5.orig/include/elan3/elanctxt.h  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan3/elanctxt.h       2005-05-11 12:10:12.586910080 -0400
+@@ -0,0 +1,856 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef _ELAN3_ELANCTXT_H
++#define _ELAN3_ELANCTXT_H
++
++#ident "$Id: elanctxt.h,v 1.81 2003/09/24 13:57:24 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/elanctxt.h,v $*/
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <elan3/elanregs.h>
++#include <elan3/vmseg.h>
++
++#define BumpUserStat(ctxt, stat)      ((ctxt)->FlagPage->stat++)
++
++#if defined(__LITTLE_ENDIAN__)
++
++typedef union _CProcTrapBuf
++{
++    E3_uint64 Align64;
++    struct
++    {
++      E3_uint32 Areg;
++      E3_uint32 Breg;
++    } r;
++    struct
++    {
++      E3_uint32 Addr;
++      E3_uint32 ContextType;
++    } s;
++} CProcTrapBuf_BE;
++
++typedef E3_EventInt        E3_EventInt_BE;
++typedef E3_IprocTrapHeader E3_IprocTrapHeader_BE;
++typedef E3_IprocTrapData   E3_IprocTrapData_BE;
++typedef E3_FaultSave     E3_FaultSave_BE;
++
++typedef union
++{
++    E3_uint64 Align64;
++    E3_DMA      s;
++} E3_DMA_BE;
++
++typedef E3_ThreadQueue     E3_ThreadQueue_BE;
++
++#else
++
++/* "Big-Endian" data structures copied by 64 bit loads, these are 32 bit word flipped */
++/* from the corresponding data structure. */
++
++typedef union _CProcTrapBuf
++{
++    E3_uint64 Align64;
++    struct
++    {
++      E3_uint32 Breg;
++      E3_uint32 Areg;
++    } r;
++    struct
++    {
++      E3_uint32 ContextType;
++      E3_uint32 Addr;
++    } s;
++} CProcTrapBuf_BE;
++
++typedef union _E3_EventInt_BE
++{
++    E3_uint64   Align64;
++    struct {
++      E3_uint32 EventContext; /* Bits 16 to 28 */
++      E3_uint32 IntCookie;
++    } s;
++} E3_EventInt_BE;
++
++typedef union _E3_IprocTrapHeader_BE
++{
++   E3_uint64           Align64;
++
++   struct
++   {
++      E3_uint32                TrAddr;
++      E3_TrTypeCntx    TrTypeCntx;
++      union
++      {
++       E3_IProcStatus_Reg u_IProcStatus;
++       E3_uint32          u_TrData1;
++      } ipsotd;
++      E3_uint32                TrData0;
++   } s;
++} E3_IprocTrapHeader_BE;
++
++typedef E3_IprocTrapData E3_IprocTrapData_BE;
++
++typedef union _E3_FaultSave_be
++{
++    E3_uint64                 Align64;
++    struct {
++      volatile E3_uint32      FaultContext;
++      E3_FaultStatusReg       FSR;
++      volatile E3_uint32      EventAddress;
++      volatile E3_uint32      FaultAddress;
++    } s;
++} E3_FaultSave_BE;
++
++typedef union _e3_dma_be
++{
++    E3_uint64         Align64;
++    struct {
++      E3_uint32       dma_size;
++      E3_DmaType      dma_u;
++      E3_Addr         dma_dest;
++      E3_Addr         dma_source;
++      E3_CookieVProc  dma_destCookieProc;
++      E3_Addr         dma_destEvent;
++      E3_CookieVProc  dma_srcCookieProc;
++      E3_Addr         dma_srcEvent;
++    } s;
++} E3_DMA_BE;
++
++typedef union _E3_ThreadQueue_BE
++{
++   E3_uint64  Align64;
++   struct
++   {
++       /* copied by 64 bit copy from elan to main */
++       E3_uint32 :3;          /* Bits 29 to 31 */
++       E3_uint32 Context:13;  /* Bits 16 to 28 */
++       E3_uint32 :16;         /* Bits 0  to 15 */
++       E3_Addr         Thread;        /* Bits 32 to 63 */
++   } s;
++} E3_ThreadQueue_BE;
++
++#endif /* defined(LITTLE_ENDIAN) || defined(__LITTLE_ENDIAN__) */
++
++typedef struct neterr_msg
++{
++    E3_uint32         Rail;                                   /* Rail error received on */
++    ELAN_CAPABILITY   SrcCapability;                          /* Capability of source of packet */
++    ELAN_CAPABILITY   DstCapability;                          /* Capability of dest of packet */
++
++    E3_uint32         DstProcess;                             /* Virtual Process of dest of packet */
++    E3_Addr           CookieAddr;                             /* Cookie Address (or NULL for DMA) */
++    E3_uint32         CookieVProc;                            /* Cookie and VP (identifies DMA) */
++    E3_uint32         NextCookie;                             /* Next Cookie value (for thread) */
++    E3_uint32         WaitForEop;                             /* Wait for EOP transaction */
++} NETERR_MSG;
++
++#ifdef __KERNEL__
++
++/*
++ * Associated with each input channel can be a network error
++ * resolver structure, which can be queued on the network 
++ * error resolver threads to perform RPCs to the other kernels
++ * when a network error occurs with an identify transaction
++ * included
++ */
++typedef struct neterr_resolver
++{
++    struct neterr_resolver *Next;
++
++    spinlock_t                    Lock;
++
++    struct elan3_ctxt       *Ctxt;
++    ELAN_LOCATION         Location;
++
++    int                           Completed;
++    int                           Status;
++    long                  Timestamp;
++
++    NETERR_MSG                    Message;
++} NETERR_RESOLVER;
++
++
++typedef struct neterr_fixup
++{
++    struct neterr_fixup          *Next;
++
++    kcondvar_t                    Wait;
++    int                           Completed;
++    int                           Status;
++
++    NETERR_MSG                    Message;
++} NETERR_FIXUP;
++
++#endif /* __KERNEL__ */
++
++/* Each of the following structures must be padded to a whole */
++/* number of 64 bit words since the kernel uses 64 bit load/stores */
++/* to transfer the elan register state. */
++typedef struct command_trap
++{
++    E3_Status_Reg     Status;                                 /* 4  bytes */
++    E3_uint32         Pad;                                    /* 4  bytes */
++    E3_FaultSave_BE   FaultSave;                              /* 16 bytes */
++    CProcTrapBuf_BE           TrapBuf;                                /* 8  bytes */
++} COMMAND_TRAP;
++
++typedef struct thread_trap
++{
++    E3_uint32         Registers[32];                          /* 128 bytes */
++#define REG_GLOBALS   0
++#define REG_OUTS      8
++#define REG_LOCALS    16
++#define REG_INS               24
++
++    E3_FaultSave_BE   FaultSave;                              /* 16 bytes */
++    E3_FaultSave_BE     DataFaultSave;                                /* 16 bytes */
++    E3_FaultSave_BE     InstFaultSave;                                /* 16 bytes */
++    E3_FaultSave_BE     OpenFaultSave;                                /* 16 bytes */
++    
++    E3_Status_Reg     Status;                                 /* 4 bytes */
++
++    E3_Addr           pc;                                     /* 4 bytes */
++    E3_Addr           npc;                                    /* 4 bytes */
++    E3_Addr           StartPC;                                /* 4 bytes */
++    E3_Addr           sp;                                     /* 4 bytes */
++    E3_uint32         mi;                                     /* 4 bytes */
++    E3_TrapBits               TrapBits;                               /* 4 bytes */
++    E3_DirtyBits      DirtyBits;                              /* 4 bytes */
++} THREAD_TRAP;
++
++typedef struct dma_trap
++{
++    E3_DMA_BE         Desc;                                   /* 32 bytes */
++    E3_FaultSave_BE   FaultSave;                              /* 16 bytes */
++    E3_FaultSave_BE   Data0;                                  /* 16 bytes */
++    E3_FaultSave_BE   Data1;                                  /* 16 bytes */
++    E3_FaultSave_BE   Data2;                                  /* 16 bytes */
++    E3_FaultSave_BE   Data3;                                  /* 16 bytes */
++    E3_Status_Reg     Status;                                 /* 4 bytes */
++    E3_DmaInfo                PacketInfo;                             /* 4 bytes */
++} DMA_TRAP;
++
++typedef struct input_trap
++{
++    E3_uint32            State;                               /* 4 bytes */
++    E3_Status_Reg        Status;                              /* 4 bytes */
++    E3_FaultSave_BE      FaultSave;                           /* 16 bytes */
++    
++    u_int                NumTransactions;                     /* 4 bytes */
++    u_int                Overflow;                            /* 4 bytes */
++    u_int                AckSent;                             /* 4 bytes */
++    u_int                BadTransaction;                      /* 4 bytes */
++
++    E3_IprocTrapHeader_BE *TrappedTransaction;                        /* 4 bytes */
++    E3_IprocTrapData_BE   *TrappedDataBuffer;                 /* 4 bytes */
++    E3_IprocTrapHeader_BE *WaitForEopTransaction;             /* 4 bytes */
++    E3_IprocTrapData_BE   *WaitForEopDataBuffer;              /* 4 bytes */
++    E3_IprocTrapHeader_BE *DmaIdentifyTransaction;            /* 4 bytes */
++    E3_IprocTrapHeader_BE *ThreadIdentifyTransaction;         /* 4 bytes */
++    E3_Addr              LockQueuePointer;                    /* 4 bytes */
++    E3_Addr              UnlockQueuePointer;                  /* 4 bytes */
++
++    E3_IprocTrapHeader_BE  Transactions[MAX_TRAPPED_TRANS];   /* n * 8 bytes */
++    E3_IprocTrapData_BE          DataBuffers[MAX_TRAPPED_TRANS];      /* n * 64 bytes */
++} INPUT_TRAP;
++
++typedef struct input_fault_save
++{
++    struct input_fault_save *Next;
++    E3_Addr                Addr;
++    E3_uint32              Count;
++} INPUT_FAULT_SAVE;
++
++#define NUM_INPUT_FAULT_SAVE  32
++#define MIN_INPUT_FAULT_PAGES 8
++#define MAX_INPUT_FAULT_PAGES 128
++
++typedef E3_uint32 EVENT_COOKIE;
++
++#ifdef __KERNEL__
++
++typedef struct event_cookie_entry
++{
++    struct event_cookie_entry *ent_next;
++    struct event_cookie_entry *ent_prev;
++
++    spinlock_t                       ent_lock;
++    unsigned                 ent_ref;
++
++    EVENT_COOKIE             ent_cookie;
++    EVENT_COOKIE             ent_fired;
++    kcondvar_t                       ent_wait;
++} EVENT_COOKIE_ENTRY;
++
++typedef struct event_cookie_table
++{
++    struct event_cookie_table *tbl_next;
++    struct event_cookie_table *tbl_prev;
++
++    unsigned long              tbl_task;
++    unsigned long              tbl_handle;
++
++    spinlock_t                       tbl_lock;
++    unsigned                 tbl_ref;
++    EVENT_COOKIE_ENTRY        *tbl_entries;
++} EVENT_COOKIE_TABLE;
++
++#define NBYTES_PER_SMALL_ROUTE        8
++#define NBYTES_PER_LARGE_ROUTE        16
++
++#define ROUTE_BLOCK_SIZE      ELAN3_PAGE_SIZE
++#define NROUTES_PER_BLOCK     (ROUTE_BLOCK_SIZE/NBYTES_PER_LARGE_ROUTE)
++
++typedef struct elan3_routes
++{
++    struct elan3_routes               *Next;                                  /* Can be chained together */
++
++    sdramaddr_t                        Routes;                                /* sdram offset of route entries */
++    bitmap_t                   Bitmap[BT_BITOUL(NROUTES_PER_BLOCK)];  /* Bitmap of which entries are used */
++} ELAN3_ROUTES; 
++
++
++typedef struct elan3_route_table
++{
++    spinlock_t                 Lock;                          /* Route lock */
++    sdramaddr_t                Table;                         /* Kernel address for route table */
++    u_int              Size;                          /* # entries in route table */
++
++    ELAN3_ROUTES      *LargeRoutes;                   /* Large routes */
++} ELAN3_ROUTE_TABLE;
++
++typedef struct elan3_vpseg
++{
++    struct elan3_vpseg                *Next;
++    int                                Process;                       /* Virtual process */
++    int                                Entries;                       /*  and # processes */
++    int                                Type;                          /* Type of cookie */
++
++    union
++    {
++      
++      ELAN_CAPABILITY Capability;                     /* Capability of remote segment */
++#  define SegCapability               SegUnion.Capability
++      struct {
++          u_short             LowProc;                        /* Base process number */
++          u_short             HighProc;                       /*   and high process number */
++#  define SegLowProc          SegUnion.BROADCAST.LowProc
++#  define SegHighProc         SegUnion.BROADCAST.HighProc
++      } BROADCAST;
++    } SegUnion;
++} ELAN3_VPSEG;
++
++#define ELAN3_VPSEG_UNINT     0                               /* Unitialised */
++#define ELAN3_VPSEG_P2P               1                               /* Point to Point */
++#define ELAN3_VPSEG_BROADCAST 2                               /* Broadcast */
++
++#define NUM_LISTS     7                                       /* Number of "swap" lists */
++
++typedef struct elan3_ctxt
++{
++    struct elan3_ctxt    *Next;                                       /* can be queued on a task */
++    struct elan3_ctxt    *Prev;
++
++    CtxtHandle                 Handle;                                /* user handle */
++    int                        RefCnt;                                /* reference count */
++
++    ELAN3MMU          *Elan3mmu;                              /* elan3mmu allocated for Elan translations */
++
++    struct elan3_ops     *Operations;                         /* User supplied helper functions */
++    void              *Private;                               /* Users private pointer */
++
++    int                        Status;                                /* Status (guarded by dev_mutex) */
++    int                        OthersState;                           /* State of halt queueing for dma/thread */
++    int                        LwpCount;                              /* Number of lwp's running */
++
++    ELAN3_DEV         *Device;                                /* Elan device */
++
++    ELAN_CAPABILITY    Capability;                            /* Capability I've attached as */
++    ELAN_POSITION      Position;                              /* Position when I was created */
++    
++    ELAN3_VPSEG               *VpSegs;                                /* List of virtual process segments */
++    ELAN3_ROUTE_TABLE    *RouteTable;
++
++    krwlock_t          VpLock;                                /* Reader/writer lock for vp list */
++    kmutex_t           SwapListsLock;                         /* mutex to lock swap lists */
++    kmutex_t           CmdLock;                               /* mutex to lock trapped dma command */
++    kmutex_t           CmdPortLock;                           /* mutex to load/unload commandport xlation */
++
++    kcondvar_t                 Wait;                                  /* Condition variable to sleep on */
++    kcondvar_t                 CommandPortWait;                       /* Condition variable to wait for commandport */
++    kcondvar_t                 LwpWait;                               /* Condition variable to wait for lwps to stop */
++    kcondvar_t                 HaltWait;                              /* Condition variable to wait for halt */
++    int                        Halted;                                /*  and flag for halt cv */
++
++    caddr_t            CommandPageMapping;                    /* user virtual address for command page mapping */
++    ioaddr_t             CommandPage;                         /* Elan command port mapping page */
++    DeviceMappingHandle  CommandPageHandle;                   /* DDI Handle */
++    ioaddr_t           CommandPort;                           /* Elan command port */
++    void              *CommandPortItem;                       /* Item we're re-issuing to commandport */
++
++    ELAN3_FLAGSTATS      *FlagPage;                           /* Page visible to user process */
++
++    COMMAND_TRAP      *CommandTraps;                          /* Command port traps */
++    ELAN3_SPLIT_QUEUE     CommandTrapQ;
++                                                                 
++    CProcTrapBuf_BE   *Commands;                              /* Overflowed commands */
++    ELAN3_QUEUE           CommandQ;
++
++    THREAD_TRAP               *ThreadTraps;                           /* Thread processor traps */
++    ELAN3_QUEUE                ThreadTrapQ;
++    
++    DMA_TRAP          *DmaTraps;                              /* Dma processor tra[ed */
++    ELAN3_QUEUE                DmaTrapQ;
++
++    INPUT_TRAP                 Input0Trap;                            /* Inputter channel 0 trap */
++    INPUT_TRAP                 Input1Trap;                            /* Inputter channel 1 trap */
++    NETERR_RESOLVER   *Input0Resolver;                        /* Inputter channel 0 network error resolver */
++    NETERR_RESOLVER   *Input1Resolver;                        /* Inputter channel 1 network error resolver */
++
++    INPUT_FAULT_SAVE   InputFaults[NUM_INPUT_FAULT_SAVE];     /* stored writeblock addresses */
++    INPUT_FAULT_SAVE    *InputFaultList;                      /* organized in list for LRU */
++    spinlock_t                 InputFaultLock;                        /* and lock for list */
++
++    kmutex_t           NetworkErrorLock;
++    NETERR_FIXUP      *NetworkErrorFixups;
++
++    EVENT_COOKIE        *EventCookies;                                /* Event cookies. */
++    ELAN3_QUEUE                EventCookieQ;
++
++    E3_Addr           *SwapThreads;                           /* Swapped Thread Queue */
++    ELAN3_QUEUE                SwapThreadQ;
++
++    E3_DMA_BE         *SwapDmas;                              /* Swapped Dmas Queue */
++    ELAN3_QUEUE                SwapDmaQ;
++
++    int                        ItemCount[NUM_LISTS];                  /* Count of items on each swap list */
++    int                        inhibit;                               /* if set lwp not to reload translations */
++
++    int                  Disabled;
++} ELAN3_CTXT;
++
++_NOTE(MUTEX_PROTECTS_DATA(elan3_dev::IntrLock, 
++                        elan3_ctxt::Status elan3_ctxt::OthersState
++                        elan3_ctxt::CommandTrapQ elan3_ctxt::CommandQ elan3_ctxt::ThreadTrapQ elan3_ctxt::DmaTrapQ 
++                        elan3_ctxt::Input0Trap elan3_ctxt::Input1Trap elan3_ctxt::EventCookieQ elan3_ctxt::SwapThreadQ 
++                        elan3_ctxt::SwapDmaQ elan3_ctxt::CommandPortItem elan3_ctxt::LwpCount))
++_NOTE(MUTEX_PROTECTS_DATA(elan3_ctxt::SwapListsLock, 
++                        elan3_ctxt::ItemCount))
++_NOTE(RWLOCK_PROTECTS_DATA(elan3_ctxt::VpLock, 
++                         elan3_ctxt::VpSegs elan3_vpseg::Next elan3_vpseg::Process 
++                         elan3_vpseg::Entries elan3_vpseg::Type))
++
++_NOTE(DATA_READABLE_WITHOUT_LOCK(elan3_ctxt::ItemCount elan3_ctxt::Status elan3_ctxt::CommandPortItem))
++
++_NOTE(LOCK_ORDER(elan3_ctxt::SwapListsLock elan3_ctxt::CmdLock elan3_dev::IntrLock))
++_NOTE(LOCK_ORDER(elan3_ctxt::SwapListsLock as::a_lock))                       /* implicit by pagefault */
++
++#define CTXT_DETACHED                         (1 << 0)                /* Context is detached. */
++#define CTXT_NO_LWPS                          (1 << 1)                /* No lwp's to handle faults */
++#define CTXT_EXITING                          (1 << 2)                /* User process is exiting */
++
++#define CTXT_SWAPPING_OUT                     (1 << 3)                /* Context is swapping out */
++#define CTXT_SWAPPED_OUT                      (1 << 4)                /* Context is swapped out */
++
++#define CTXT_SWAP_FREE                                (1 << 5)                /* Swap buffer is free */
++#define CTXT_SWAP_VALID                               (1 << 6)                /* Swap buffer has queue entries in it */
++
++#define CTXT_DMA_QUEUE_FULL                   (1 << 7)                /* Dma trap queue is full */
++#define CTXT_THREAD_QUEUE_FULL                        (1 << 8)                /* Thread trap queue is full */
++#define CTXT_EVENT_QUEUE_FULL                 (1 << 9)                /* Event interrupt queue is full */
++#define CTXT_COMMAND_OVERFLOW_ERROR           (1 << 10)               /* Trap queue overflow */
++
++#define CTXT_SWAP_WANTED                      (1 << 11)               /* Some one wanted to swap */
++#define CTXT_WAITING_SWAPIN                   (1 << 12)               /* Someone waiting on swapin */
++
++#define CTXT_WAITING_COMMAND                  (1 << 13)               /* swgelan waiting on command port */
++#define CTXT_COMMAND_MAPPED_MAIN              (1 << 14)               /* segelan has mapped command port */
++
++#define CTXT_QUEUES_EMPTY                     (1 << 15)               /* dma/thread run queues are empty */
++#define CTXT_QUEUES_EMPTYING                  (1 << 16)               /* dma/thread run queues are being emptied */
++
++#define CTXT_USER_FILTERING                   (1 << 17)               /* user requested context filter */
++
++#define CTXT_KERNEL                           (1 << 18)               /* context is a kernel context */
++#define CTXT_COMMAND_MAPPED_ELAN              (1 << 19)               /* command port is mapped for elan */
++#define CTXT_FIXUP_NETERR                     (1 << 20)               /* fixing up a network error */
++
++
++#define CTXT_SWAPPED_REASONS          (CTXT_NO_LWPS   |               \
++                                       CTXT_DETACHED  |               \
++                                       CTXT_EXITING   |               \
++                                       CTXT_FIXUP_NETERR)
++
++#define CTXT_OTHERS_REASONS           (CTXT_EVENT_QUEUE_FULL  |       \
++                                       CTXT_DMA_QUEUE_FULL    |       \
++                                       CTXT_THREAD_QUEUE_FULL |       \
++                                       CTXT_COMMAND_OVERFLOW_ERROR |  \
++                                       CTXT_SWAPPED_REASONS)
++
++#define CTXT_INPUTTER_REASONS         (CTXT_USER_FILTERING |          \
++                                       CTXT_OTHERS_REASONS)
++
++#define CTXT_COMMAND_MAPPED           (CTXT_COMMAND_MAPPED_MAIN |     \
++                                       CTXT_COMMAND_MAPPED_ELAN)
++
++#define CTXT_IS_KERNEL(ctxt)          ((ctxt)->Status & CTXT_KERNEL)
++
++/*
++ * State values for ctxt_inputterState/ctxt_commandportStats
++ */
++#define CTXT_STATE_OK                 0
++#define CTXT_STATE_TRAPPED            1               /* Inputter channel 0 trapped */
++#define CTXT_STATE_RESOLVING          2               /* An LWP is resolving the trap */
++#define CTXT_STATE_NEEDS_RESTART      3               /* Th trapped packet needs to be executed */
++#define CTXT_STATE_NETWORK_ERROR      4               /* We're waiting on an RPC for the identify transaction */
++#define CTXT_STATE_EXECUTING          5               /* An LWP is executing the trapped packet */
++
++/*
++ * State values for OthersState.
++ */
++#define CTXT_OTHERS_RUNNING           0
++#define CTXT_OTHERS_HALTING           1
++#define CTXT_OTHERS_SWAPPING          2
++#define CTXT_OTHERS_HALTING_MORE      3
++#define CTXT_OTHERS_SWAPPING_MORE     4
++#define CTXT_OTHERS_SWAPPED           5
++
++typedef struct elan3_ops
++{
++    u_int  Version;
++
++    int        (*Exception)   (ELAN3_CTXT *ctxt, int type, int proc, void *trap, va_list ap);
++
++    /* swap item list functions */
++    int  (*GetWordItem)               (ELAN3_CTXT *ctxt, int list, void **itemp, E3_uint32 *valuep);
++    int  (*GetBlockItem)      (ELAN3_CTXT *ctxt, int list, void **itemp, E3_Addr *valuep);
++    void (*PutWordItem)               (ELAN3_CTXT *ctxt, int list, E3_Addr value);
++    void (*PutBlockItem)      (ELAN3_CTXT *ctxt, int list, E3_uint32 *ptr);
++    void (*PutbackItem)               (ELAN3_CTXT *ctxt, int list, void *item);
++    void (*FreeWordItem)      (ELAN3_CTXT *ctxt, void *item);
++    void (*FreeBlockItem)     (ELAN3_CTXT *ctxt, void *item);
++    int  (*CountItems)                (ELAN3_CTXT *ctxt, int list);
++
++    /* event interrupt cookie */
++    int  (*Event)             (ELAN3_CTXT *ctxt, E3_uint32 cookie, int flag);
++
++    /* swapin/swapout functions. */
++    void (*Swapin)            (ELAN3_CTXT *ctxt);
++    void (*Swapout)           (ELAN3_CTXT *ctxt);
++
++    /* Free of private data */
++    void (*FreePrivate)               (ELAN3_CTXT *ctxt);
++
++    /* Fixup a network error */
++    int  (*FixupNetworkError) (ELAN3_CTXT *ctxt, NETERR_FIXUP *nef);
++
++    /* Interrupt handler trap interface */
++    int  (*DProcTrap)         (ELAN3_CTXT *ctxt, DMA_TRAP *trap);
++    int  (*TProcTrap)         (ELAN3_CTXT *ctxt, THREAD_TRAP *trap);
++    int        (*IProcTrap)           (ELAN3_CTXT *ctxt, INPUT_TRAP *trap, int chan);
++    int        (*CProcTrap)           (ELAN3_CTXT *ctxt, COMMAND_TRAP *trap);
++    int  (*CProcReissue)        (ELAN3_CTXT *ctxt, CProcTrapBuf_BE *TrapBuf);
++
++    /* User memory access functions */
++    int             (*StartFaultCheck)(ELAN3_CTXT *ctxt);
++    void      (*EndFaultCheck)  (ELAN3_CTXT *ctxt);
++
++    E3_uint8  (*Load8)                (ELAN3_CTXT *ctxt, E3_Addr addr);
++    void      (*Store8)               (ELAN3_CTXT *ctxt, E3_Addr addr, E3_uint8 val);
++    E3_uint16 (*Load16)               (ELAN3_CTXT *ctxt, E3_Addr addr);
++    void      (*Store16)      (ELAN3_CTXT *ctxt, E3_Addr addr, E3_uint16 val);
++    E3_uint32 (*Load32)               (ELAN3_CTXT *ctxt, E3_Addr addr);
++    void      (*Store32)      (ELAN3_CTXT *ctxt, E3_Addr addr, E3_uint32 val);
++    E3_uint64 (*Load64)               (ELAN3_CTXT *ctxt, E3_Addr addr);
++    void      (*Store64)      (ELAN3_CTXT *ctxt, E3_Addr addr, E3_uint64 val);
++
++} ELAN3_OPS;
++
++#define ELAN3_OPS_VERSION     0xdeef0001
++
++/*
++ * Flags for ops_event.
++ */
++#define OP_INTR                       0               /* Called from interrupt handler */
++#define OP_LWP                        1               /* Called from "lwp" */
++
++/*
++ * Return codes for "ops" functions.
++ */
++#define OP_DEFER              0               /* Defer to next lower interrupt */
++#define OP_IGNORE             1               /* No event hander, so ignore it */
++#define OP_HANDLED            2               /* Handled event (resume thread) */
++#define OP_FAILED             3               /* Failed */
++
++#define ELAN3_CALL_OP(ctxt,fn)                                ((ctxt)->Operations && (ctxt)->Operations->fn) ? (ctxt)->Operations->fn 
++
++#define ELAN3_OP_EXCEPTION(ctxt,type,proc,trap,ap)    (ELAN3_CALL_OP(ctxt,Exception)    (ctxt,type,proc,trap,ap)      : OP_IGNORE)
++#define ELAN3_OP_GET_WORD_ITEM(ctxt,list,itemp,valuep)        (ELAN3_CALL_OP(ctxt,GetWordItem)  (ctxt,list,itemp,valuep)      : 0)
++#define ELAN3_OP_GET_BLOCK_ITEM(ctxt,list,itemp,valuep)       (ELAN3_CALL_OP(ctxt,GetBlockItem) (ctxt,list,itemp,valuep)      : 0)
++#define ELAN3_OP_PUT_WORD_ITEM(ctxt,list,value)               (ELAN3_CALL_OP(ctxt,PutWordItem)  (ctxt,list,value)             : (void)0)
++#define ELAN3_OP_PUT_BLOCK_ITEM(ctxt,list,ptr)                (ELAN3_CALL_OP(ctxt,PutBlockItem) (ctxt,list,ptr)               : (void)0)
++#define ELAN3_OP_PUTBACK_ITEM(ctxt,list,item)         (ELAN3_CALL_OP(ctxt,PutbackItem)  (ctxt,list,item)              : (void)0)
++#define ELAN3_OP_FREE_WORD_ITEM(ctxt,item)            (ELAN3_CALL_OP(ctxt,FreeWordItem) (ctxt,item)                   : (void)0)
++#define ELAN3_OP_FREE_BLOCK_ITEM(ctxt,item)           (ELAN3_CALL_OP(ctxt,FreeBlockItem)(ctxt,item)                   : (void)0)
++#define ELAN3_OP_COUNT_ITEMS(ctxt,list)                       (ELAN3_CALL_OP(ctxt,CountItems)(ctxt,list)                      : 0)
++#define ELAN3_OP_EVENT(ctxt,cookie,flag)              (ELAN3_CALL_OP(ctxt,Event)(ctxt,cookie,flag)                    : OP_IGNORE)
++#define ELAN3_OP_SWAPIN(ctxt)                         (ELAN3_CALL_OP(ctxt,Swapin)(ctxt)                               : (void)0)
++#define ELAN3_OP_SWAPOUT(ctxt)                                (ELAN3_CALL_OP(ctxt,Swapout)(ctxt)                              : (void)0)
++#define ELAN3_OP_FREE_PRIVATE(ctxt)                   (ELAN3_CALL_OP(ctxt,FreePrivate)(ctxt)                          : (void)0)
++#define ELAN3_OP_FIXUP_NETWORK_ERROR(ctxt, nef)               (ELAN3_CALL_OP(ctxt,FixupNetworkError)(ctxt,nef)                        : OP_FAILED)
++
++#define ELAN3_OP_DPROC_TRAP(ctxt, trap)                       (ELAN3_CALL_OP(ctxt,DProcTrap)(ctxt,trap)                       : OP_DEFER)
++#define ELAN3_OP_TPROC_TRAP(ctxt, trap)                       (ELAN3_CALL_OP(ctxt,TProcTrap)(ctxt,trap)                       : OP_DEFER)
++#define ELAN3_OP_IPROC_TRAP(ctxt, trap, chan)         (ELAN3_CALL_OP(ctxt,IProcTrap)(ctxt,trap,chan)                  : OP_DEFER)
++#define ELAN3_OP_CPROC_TRAP(ctxt, trap)                       (ELAN3_CALL_OP(ctxt,CProcTrap)(ctxt,trap)                       : OP_DEFER)
++#define ELAN3_OP_CPROC_REISSUE(ctxt,tbuf)             (ELAN3_CALL_OP(ctxt,CProcReissue)(ctxt, tbuf)                   : OP_DEFER)
++
++#define ELAN3_OP_START_FAULT_CHECK(ctxt)              (ELAN3_CALL_OP(ctxt,StartFaultCheck)(ctxt)                      : 0)
++#define ELAN3_OP_END_FAULT_CHECK(ctxt)                        (ELAN3_CALL_OP(ctxt,EndFaultCheck)(ctxt)                                : (void)0)
++#define ELAN3_OP_LOAD8(ctxt,addr)                     (ELAN3_CALL_OP(ctxt,Load8)(ctxt,addr)                           : 0)
++#define ELAN3_OP_STORE8(ctxt,addr,val)                        (ELAN3_CALL_OP(ctxt,Store8)(ctxt,addr,val)                      : (void)0)
++#define ELAN3_OP_LOAD16(ctxt,addr)                    (ELAN3_CALL_OP(ctxt,Load16)(ctxt,addr)                          : 0)
++#define ELAN3_OP_STORE16(ctxt,addr,val)                       (ELAN3_CALL_OP(ctxt,Store16)(ctxt,addr,val)                     : (void)0)
++#define ELAN3_OP_LOAD32(ctxt,addr)                    (ELAN3_CALL_OP(ctxt,Load32)(ctxt,addr)                          : 0)
++#define ELAN3_OP_STORE32(ctxt,addr,val)                       (ELAN3_CALL_OP(ctxt,Store32)(ctxt,addr,val)                     : (void)0)
++#define ELAN3_OP_LOAD64(ctxt,addr)                    (ELAN3_CALL_OP(ctxt,Load64)(ctxt,addr)                          : 0)
++#define ELAN3_OP_STORE64(ctxt,addr,val)                       (ELAN3_CALL_OP(ctxt,Store64)(ctxt,addr,val)                     : (void)0)
++
++#endif /* __KERNEL__ */
++
++/* "list" arguement to ops functions */
++#define LIST_DMA_PTR          0
++#define LIST_DMA_DESC         1
++#define LIST_THREAD                   2
++#define LIST_COMMAND          3
++#define LIST_SETEVENT         4
++#define LIST_FREE_WORD                5
++#define LIST_FREE_BLOCK               6
++
++#define MAX_LISTS             7
++
++#if defined(__KERNEL__) && MAX_LISTS != NUM_LISTS
++#  error Check NUM_LISTS == MAX_LISTS
++#endif
++
++/*
++ * Values for the 'type' field to PostException().
++ */
++#define EXCEPTION_INVALID_ADDR                1               /* FaultArea, res */
++#define EXCEPTION_UNIMP_INSTR         2               /* instr */
++#define EXCEPTION_INVALID_PROCESS     3               /* proc, res */
++#define EXCEPTION_SIMULATION_FAILED   4               /* */
++#define EXCEPTION_UNIMPLEMENTED               5               /* */
++#define EXCEPTION_SWAP_FAULT          6               /* */
++#define EXCEPTION_SWAP_FAILED         7               /* */
++#define EXCEPTION_BAD_PACKET          8               /* */
++#define EXCEPTION_FAULTED             9               /* addr */
++#define EXCEPTION_QUEUE_OVERFLOW      10              /* FaultArea, TrapType */
++#define EXCEPTION_COMMAND_OVERFLOW    11              /* count */
++#define EXCEPTION_DMA_RETRY_FAIL      12              /* */
++#define EXCEPTION_CHAINED_EVENT               13              /* EventAddr */
++#define EXCEPTION_THREAD_KILLED               14              /* */
++#define EXCEPTION_CANNOT_SAVE_THREAD  15
++#define EXCEPTION_BAD_SYSCALL         16              /* */
++#define EXCEPTION_DEBUG                       17
++#define EXCEPTION_BAD_EVENT           18              /* */
++#define EXCEPTION_NETWORK_ERROR               19              /* rvp */
++#define EXCEPTION_BUS_ERROR           20
++#define EXCEPTION_COOKIE_ERROR                21
++#define EXCEPTION_PACKET_TIMEOUT      22
++#define EXCEPTION_BAD_DMA             23              /* */
++#define EXCEPTION_ENOMEM              24
++
++/*
++ * Values for the 'proc' field to ElanException().
++ */
++#define COMMAND_PROC                  1
++#define THREAD_PROC                   2
++#define DMA_PROC                      3
++#define INPUT_PROC                    4
++#define EVENT_PROC                    5
++
++/* Flags to IssueDmaCommand */
++#define ISSUE_COMMAND_FOR_CPROC               1
++#define ISSUE_COMMAND_CANT_WAIT               2
++
++/* Return code from IssueDmaCommand.*/
++#define ISSUE_COMMAND_OK              0
++#define ISSUE_COMMAND_TRAPPED         1
++#define ISSUE_COMMAND_RETRY           2
++#define ISSUE_COMMAND_WAIT            3
++
++#ifdef __KERNEL__
++
++extern ELAN3_CTXT *elan3_alloc(ELAN3_DEV *dev, int kernel);
++extern void      elan3_free      (ELAN3_CTXT *ctxt);
++
++extern int       elan3_attach    (ELAN3_CTXT *ctxt, ELAN_CAPABILITY *cap);
++extern int         elan3_doattach  (ELAN3_CTXT *ctxt, ELAN_CAPABILITY *cap);
++extern void      elan3_detach    (ELAN3_CTXT *ctxt);
++extern void        elan3_dodetach  (ELAN3_CTXT *ctxt);
++
++extern int       elan3_addvp     (ELAN3_CTXT *ctxt, int process, ELAN_CAPABILITY *cap);
++extern int       elan3_removevp  (ELAN3_CTXT *ctxt, int process);
++extern int       elan3_addbcastvp(ELAN3_CTXT *ctxt, int process, int base, int count);
++
++extern int         elan3_process   (ELAN3_CTXT *ctxt);
++
++extern int       elan3_load_route (ELAN3_CTXT *ctxt, int process, E3_uint16 *flits);
++extern int       elan3_check_route(ELAN3_CTXT *ctxt, int process, E3_uint16 *flits, E3_uint32 *routeError);
++
++extern int       elan3_lwp       (ELAN3_CTXT *ctxt);
++
++extern void      elan3_swapin (ELAN3_CTXT *ctxt, int reason);
++extern void      elan3_swapout (ELAN3_CTXT *ctxt, int reason);
++extern int         elan3_pagefault (ELAN3_CTXT *ctxt, E3_FaultSave_BE *FaultSave, int npages);
++extern void        elan3_block_inputter (ELAN3_CTXT *ctxt, int block);
++
++
++extern E3_Addr     elan3_init_thread (ELAN3_DEV *dev, E3_Addr fn, E3_Addr addr, sdramaddr_t stack, int stackSize, int nargs, ...);
++
++extern void      SetInputterState (ELAN3_CTXT *ctxt, E3_uint32 Pend, E3_uint32 *Maskp);
++extern void      SetInputterStateForContext (ELAN3_CTXT *ctxt, E3_uint32 Pend, E3_uint32 *Maskp);
++extern void        UnloadCommandPageMapping (ELAN3_CTXT *ctxt);
++extern void      StartSwapoutContext (ELAN3_CTXT *ctxt, E3_uint32 Pend, E3_uint32 *Maskp);
++
++extern int       HandleExceptions (ELAN3_CTXT *ctxt, unsigned long *flags);
++extern int       RestartContext (ELAN3_CTXT *ctxt, unsigned long *flags);
++extern int         CheckCommandQueueFlushed (ELAN3_CTXT *ctxt, E3_uint32 cflags, int how, unsigned long *flags);
++extern int       IssueCommand (ELAN3_CTXT *ctxt, unsigned cmdoff, E3_Addr value, int flags);
++extern int       IssueDmaCommand (ELAN3_CTXT *ctxt, E3_Addr value, void *item, int flags);
++extern int         WaitForDmaCommand (ELAN3_CTXT *ctxt, void *item, int flags);
++extern void      FixupEventTrap (ELAN3_CTXT *ctxt, int proc, void *trap, E3_uint32 TrapType, 
++                                 E3_FaultSave_BE *FaultSaveArea, int flags);
++extern int       SimulateBlockCopy (ELAN3_CTXT *ctxt, E3_Addr EventAddress);
++extern void      ReissueEvent (ELAN3_CTXT *ctxt, E3_Addr addr,int flags);
++extern int         SetEventsNeedRestart (ELAN3_CTXT *ctxt);
++extern void        RestartSetEvents (ELAN3_CTXT *ctxt);
++extern int       RunEventType (ELAN3_CTXT *ctxt, E3_FaultSave_BE *FaultSaveArea, E3_uint32 EventType);
++extern void        WakeupLwp (ELAN3_DEV *dev, void *arg);
++extern void      QueueEventInterrupt (ELAN3_CTXT *ctxt, E3_uint32 cookie);
++extern int         WaitForCommandPort (ELAN3_CTXT *ctxt);
++
++extern int       ElanException (ELAN3_CTXT *ctxt, int type, int proc, void *trap, ...);
++
++/* context_osdep.c */
++extern int       LoadElanTranslation (ELAN3_CTXT *ctxt, E3_Addr elanAddr, int len, int protFault, int writeable);
++extern void      LoadCommandPortTranslation (ELAN3_CTXT *ctxt);
++
++#if defined(DIGITAL_UNIX)
++/* seg_elan.c */
++extern caddr_t           elan3_segelan3_create (ELAN3_CTXT *ctxt);
++extern void      elan3_segelan3_destroy (ELAN3_CTXT *ctxt);
++extern int         elan3_segelan3_map (ELAN3_CTXT *ctxt);
++extern void        elan3_segelan3_unmap (ELAN3_CTXT *ctxt);
++
++/* seg_elanmem.c */
++extern int       elan3_segelanmem_create (ELAN3_DEV *dev, unsigned object, unsigned off, vm_offset_t *addrp, int len);
++#endif /* defined(DIGITAL_UNIX) */
++
++/* route_table.c */
++extern ELAN3_ROUTE_TABLE *AllocateRouteTable (ELAN3_DEV *dev, int size);
++extern void              FreeRouteTable  (ELAN3_DEV *dev, ELAN3_ROUTE_TABLE *tbl);
++extern int               LoadRoute       (ELAN3_DEV *dev, ELAN3_ROUTE_TABLE *tbl, int vp, int ctxnum, int nflits, E3_uint16 *flits);
++extern int               GetRoute        (ELAN3_DEV *dev, ELAN3_ROUTE_TABLE *tbl, int process, E3_uint16 *flits);
++extern void            InvalidateRoute (ELAN3_DEV *dev, ELAN3_ROUTE_TABLE *tbl, int vp);
++extern void            ValidateRoute   (ELAN3_DEV *dev, ELAN3_ROUTE_TABLE *tbl, int vp);
++extern void            ClearRoute      (ELAN3_DEV *dev, ELAN3_ROUTE_TABLE *tbl, int vp);
++
++extern int               GenerateRoute (ELAN_POSITION *pos, E3_uint16 *flits, int lowid, int highid, int timeout, int highPri);
++extern int               GenerateProbeRoute (E3_uint16 *flits, int nodeid, int level, int *linkup, int *linkdown, int adaptive);
++extern int               GenerateCheckRoute (ELAN_POSITION *pos, E3_uint16 *flits, int level, int adaptive);
++
++/* virtual_process.c */
++extern ELAN_LOCATION  ProcessToLocation     (ELAN3_CTXT *ctxt, ELAN3_VPSEG *seg, int process, ELAN_CAPABILITY *cap);
++extern int          ResolveVirtualProcess (ELAN3_CTXT *ctxt, int process);
++extern caddr_t        CapabilityString      (ELAN_CAPABILITY *cap);
++extern void           UnloadVirtualProcess  (ELAN3_CTXT *ctxt, ELAN_CAPABILITY *cap);
++
++extern int           elan3_get_route   (ELAN3_CTXT *ctxt, int process, E3_uint16 *flits);
++extern int           elan3_reset_route (ELAN3_CTXT *ctxt, int process);
++
++/* cproc.c */
++extern int      NextCProcTrap (ELAN3_CTXT *ctxt, COMMAND_TRAP *trap);
++extern void     ResolveCProcTrap (ELAN3_CTXT *ctxt);
++extern int      RestartCProcTrap (ELAN3_CTXT *ctxt);
++
++/* iproc.c */
++extern void       InspectIProcTrap (ELAN3_CTXT *ctxt, INPUT_TRAP *trap);
++extern void     ResolveIProcTrap (ELAN3_CTXT *ctxt, INPUT_TRAP *trap, NETERR_RESOLVER **rvp);
++extern int      RestartIProcTrap (ELAN3_CTXT *ctxt, INPUT_TRAP *trap);
++extern char      *IProcTrapString (E3_IprocTrapHeader_BE *hdrp, E3_IprocTrapData *datap);
++extern void       SimulateUnlockQueue (ELAN3_CTXT *ctxt, E3_Addr QueuePointer, int SentAck);
++
++/* tproc.c */
++extern int      NextTProcTrap (ELAN3_CTXT *ctxt, THREAD_TRAP *trap);
++extern void     ResolveTProcTrap (ELAN3_CTXT *ctxt, THREAD_TRAP *trap);
++extern int      TProcNeedsRestart (ELAN3_CTXT *ctxt);
++extern void     RestartTProcItems (ELAN3_CTXT *ctxt);
++extern E3_Addr    SaveThreadToStack (ELAN3_CTXT *ctxt, THREAD_TRAP *trap, int SkipInstruction);
++extern void       ReissueStackPointer (ELAN3_CTXT *ctxt, E3_Addr StackPointer);
++
++/* tprocinsts.c */
++extern int        RollThreadToClose (ELAN3_CTXT *ctxt, THREAD_TRAP *trap, E3_uint32 PAckVal);
++
++/* tproc_osdep.c */
++extern int        ThreadSyscall (ELAN3_CTXT *ctxt, THREAD_TRAP *trap, int *skip);
++extern int      ThreadElancall (ELAN3_CTXT *ctxt, THREAD_TRAP *trap, int *skip);
++
++/* dproc.c */
++extern int      NextDProcTrap (ELAN3_CTXT *ctxt, DMA_TRAP *trap);
++extern void     ResolveDProcTrap (ELAN3_CTXT *ctxt, DMA_TRAP *trap);
++extern int      DProcNeedsRestart (ELAN3_CTXT *ctxt);
++extern void     RestartDProcItems (ELAN3_CTXT *ctxt);
++extern void       RestartDmaDesc (ELAN3_CTXT *ctxt, E3_DMA_BE *desc);
++extern void       RestartDmaTrap (ELAN3_CTXT *ctxt, DMA_TRAP *trap);
++extern void     RestartDmaPtr (ELAN3_CTXT *ctxt, E3_Addr ptr);
++
++/* network_error.c */
++extern void       InitialiseNetworkErrorResolver (void);
++extern void       FinaliseNetworkErrorResolver (void);
++extern int        QueueNetworkErrorResolver (ELAN3_CTXT *ctxt, INPUT_TRAP *trap, NETERR_RESOLVER **rvpp);
++extern void     FreeNetworkErrorResolver (NETERR_RESOLVER *rvp);
++extern void       CancelNetworkErrorResolver (NETERR_RESOLVER *rvp);
++extern int      ExecuteNetworkErrorFixup (NETERR_MSG *msg);
++extern void     CompleteNetworkErrorFixup (ELAN3_CTXT *ctxt, NETERR_FIXUP *nef, int status);
++
++extern int        AddNeterrServerSyscall (int elanId, void *configp, void *addrp, char *namep);
++
++/* eventcookie.c */
++extern void                cookie_init(void);
++extern void                cookie_fini(void);
++extern EVENT_COOKIE_TABLE *cookie_alloc_table (unsigned long task, unsigned long handle);
++extern void                cookie_free_table (EVENT_COOKIE_TABLE *tbl);
++extern int                 cookie_alloc_cookie (EVENT_COOKIE_TABLE *tbl, EVENT_COOKIE cookie);
++extern int                 cookie_free_cookie (EVENT_COOKIE_TABLE *tbl, EVENT_COOKIE cookie);
++extern int                 cookie_fire_cookie (EVENT_COOKIE_TABLE *tbl, EVENT_COOKIE cookie);
++extern int                 cookie_wait_cookie (EVENT_COOKIE_TABLE *tbl, EVENT_COOKIE cookie);
++extern int                 cookie_arm_cookie (EVENT_COOKIE_TABLE *tbl, EVENT_COOKIE cookie);
++
++/* routecheck.c */
++extern int elan3_route_check          (ELAN3_CTXT *ctxt, E3_uint16 *flits, int destNode);
++extern int elan3_route_broadcast_check(ELAN3_CTXT *ctxt, E3_uint16 *flitsA, int lowNode, int highNode);
++
++
++#endif /* __KERNEL__ */
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* _ELAN3_ELANCTXT_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/include/elan3/elandebug.h
+===================================================================
+--- linux-2.6.5.orig/include/elan3/elandebug.h 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan3/elandebug.h      2005-05-11 12:10:12.586910080 -0400
+@@ -0,0 +1,106 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef _ELAN3_ELANDEBUG_H
++#define _ELAN3_ELANDEBUG_H
++
++#ident "$Id: elandebug.h,v 1.38 2003/09/24 13:57:24 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/elandebug.h,v $ */
++
++#if defined(__KERNEL__)
++
++extern u_int elan3_debug;
++extern u_int elan3_debug_console;
++extern u_int elan3_debug_buffer;
++extern u_int elan3_debug_ignore_dev;
++extern u_int elan3_debug_ignore_kcomm;
++extern u_int elan3_debug_ignore_ctxt;
++extern u_int elan3_debug_display_ctxt;
++
++#define DBG_CONFIG    0x00000001                      /* Module configuration */
++#define DBG_HAT               0x00000002
++#define DBG_FN                0x00000004
++#define DBG_SEG               0x00000008
++#define DBG_INTR      0x00000010
++#define DBG_LWP               0x00000020
++#define DBG_FAULT     0x00000040
++#define DBG_EVENT     0x00000080
++#define DBG_CPROC     0x00000100
++#define DBG_TPROC     0x00000200
++#define DBG_DPROC     0x00000400
++#define DBG_IPROC     0x00000800
++#define DBG_SWAP      0x00001000
++#define DBG_CMD               0x00002000
++#define DBG_VP                0x00004000
++#define DBG_SYSCALL   0x00008000
++#define DBG_BSCAN     0x00010000
++#define DBG_LINKERR   0x00020000
++#define DBG_NETERR    0x00040000
++#define DBG_NETRPC    0x00080000
++#define DBG_EVENTCOOKIE 0x00100000
++#define DBG_SDRAM     0x00200000
++
++#define DBG_EP                0x10000000
++#define DBG_EPCONSOLE 0x20000000
++
++#define DBG_EIP               0x40000000
++#define DBG_EIPFAIL   0x80000000
++
++#define DBG_ALL               0xffffffff
++
++/* values to pass as "ctxt" rather than a "ctxt" pointer */
++#define DBG_DEVICE    ((void *) 0)
++#define DBG_KCOMM     ((void *) 1)
++#define DBG_ICS               ((void *) 2)
++#define DBG_USER      ((void *) 3)
++#define DBG_NTYPES    64
++
++#if defined(DEBUG_PRINTF)
++#  define DBG(m,fn)                           ((elan3_debug&(m)) ? (void)(fn) : (void)0)
++#  define PRINTF0(ctxt,m,fmt)                 ((elan3_debug&(m)) ? elan3_debugf(ctxt,m,fmt)             : (void)0)
++#  define PRINTF1(ctxt,m,fmt,a)                       ((elan3_debug&(m)) ? elan3_debugf(ctxt,m,fmt,a)           : (void)0)
++#  define PRINTF2(ctxt,m,fmt,a,b)             ((elan3_debug&(m)) ? elan3_debugf(ctxt,m,fmt,a,b)         : (void)0)
++#  define PRINTF3(ctxt,m,fmt,a,b,c)           ((elan3_debug&(m)) ? elan3_debugf(ctxt,m,fmt,a,b,c)       : (void)0)
++#  define PRINTF4(ctxt,m,fmt,a,b,c,d)         ((elan3_debug&(m)) ? elan3_debugf(ctxt,m,fmt,a,b,c,d)     : (void)0)
++#  define PRINTF5(ctxt,m,fmt,a,b,c,d,e)               ((elan3_debug&(m)) ? elan3_debugf(ctxt,m,fmt,a,b,c,d,e)   : (void)0)
++#  define PRINTF6(ctxt,m,fmt,a,b,c,d,e,f)     ((elan3_debug&(m)) ? elan3_debugf(ctxt,m,fmt,a,b,c,d,e,f) : (void)0)
++#ifdef __GNUC__
++#  define PRINTF(ctxt,m,args...)              ((elan3_debug&(m)) ? elan3_debugf(ctxt,m, ##args)         : (void)0)
++#endif
++
++#else
++
++#  define DBG(m, fn)                          do { ; } while (0)
++#  define PRINTF0(ctxt,m,fmt)                 do { ; } while (0)
++#  define PRINTF1(ctxt,m,fmt,a)                       do { ; } while (0)
++#  define PRINTF2(ctxt,m,fmt,a,b)             do { ; } while (0)
++#  define PRINTF3(ctxt,m,fmt,a,b,c)           do { ; } while (0)
++#  define PRINTF4(ctxt,m,fmt,a,b,c,d)         do { ; } while (0)
++#  define PRINTF5(ctxt,m,fmt,a,b,c,d,e)               do { ; } while (0)
++#  define PRINTF6(ctxt,m,fmt,a,b,c,d,e,f)     do { ; } while (0)
++#ifdef __GNUC__
++#  define PRINTF(ctxt,m,args...)              do { ; } while (0)
++#endif
++
++#endif /* DEBUG_PRINTF */
++
++#ifdef __GNUC__
++extern void       elan3_debugf (void *ctxt, unsigned int mode, char *fmt, ...)
++    __attribute__ ((format (printf,3,4)));
++#else
++extern void       elan3_debugf (void *ctxt, unsigned int mode, char *fmt, ...);
++#endif
++
++
++#endif /* __KERNEL__ */
++#endif /* _ELAN3_ELANDEBUG_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/include/elan3/elandev.h
+===================================================================
+--- linux-2.6.5.orig/include/elan3/elandev.h   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan3/elandev.h        2005-05-11 12:10:12.587909928 -0400
+@@ -0,0 +1,581 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN3_ELANDEV_H
++#define __ELAN3_ELANDEV_H
++
++#ident "$Id: elandev.h,v 1.74.2.2 2004/12/10 11:10:19 mike Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/elandev.h,v $ */
++
++#include <elan/bitmap.h>
++#include <elan/devinfo.h>
++#include <elan/stats.h>
++
++#if defined(DIGITAL_UNIX)
++#  include <elan3/elandev_dunix.h>
++#elif defined(LINUX)
++#  include <elan3/elandev_linux.h>
++#elif defined(SOLARIS)
++#  include <elan3/elandev_solaris.h>
++#endif
++
++#ifndef TRUE
++#  define TRUE 1
++#endif
++#ifndef FALSE
++#  define FALSE 0
++#endif
++
++/*
++ * Elan base address registers defined as follows :
++ */
++#define ELAN3_BAR_SDRAM               0
++#define ELAN3_BAR_COMMAND_PORT        1
++#define ELAN3_BAR_REGISTERS   2
++#define ELAN3_BAR_EBUS                3
++
++/* Macro to generate 'offset' to mmap "mem" device */
++#define OFF_TO_SPACE(off)     ((off) >> 28)
++#define OFF_TO_OFFSET(off)    ((off) & 0x0FFFFFFF)
++#define GEN_OFF(space,off)    (((space) << 28) | ((off) & 0x0FFFFFFF))
++
++#ifdef __KERNEL__
++
++/*
++ * Elan EBUS is configured as follows :
++ */
++#define ELAN3_EBUS_ROM_OFFSET         0x000000                /* rom */
++#define ELAN3_EBUS_INTPAL_OFFSET      0x180000                /* interrupt pal (write only) */
++
++#define ELAN3_EBUS_ROM_SIZE           0x100000
++
++/*
++ * Elan SDRAM is arranged as follows :
++ */
++#define ELAN3_TANDQ_SIZE              0x0020000               /* Trap And Queue Size */
++#define ELAN3_CONTEXT_SIZE            0x0010000               /* Context Table Size */
++#define ELAN3_COMMAND_TRAP_SIZE               0x0010000               /* Command Port Trap Size */
++
++#ifdef MPSAS
++#define ELAN3_LN2_NUM_CONTEXTS        8                               /* Support 256 contexts */
++#else
++#define ELAN3_LN2_NUM_CONTEXTS        12                              /* Support 4096 contexts */
++#endif
++#define ELAN3_NUM_CONTEXTS    (1 << ELAN3_LN2_NUM_CONTEXTS)   /* Entries in context table */
++
++#define ELAN3_SDRAM_NUM_BANKS 4                               /* Elan supports 4 Banks of Sdram */
++#define ELAN3_SDRAM_BANK_SHIFT        26                              /* each of which can be 64 mbytes ? */
++#define ELAN3_SDRAM_BANK_SIZE (1 << ELAN3_SDRAM_BANK_SHIFT)
++
++#define ELAN3_MAX_CACHE_SIZE  (64 * 1024)                     /* Maximum cache size */
++#define ELAN3_CACHE_SIZE      (64 * 4 * E3_CACHELINE_SIZE)    /* Elan3 has 8K cache */
++
++#ifndef offsetof
++#define offsetof(s, m)                (size_t)(&(((s *)0)->m))
++#endif
++
++/*
++ * circular queue and macros to access members.
++ */
++typedef struct
++{
++    u_int     q_back;                 /* Next free space */
++    u_int     q_front;                /* First object to remove */
++    u_int     q_size;                 /* Size of queue */
++    u_int     q_count;                /* Current number of entries */
++    u_int     q_slop;                 /* FULL <=> (count+slop) == size */
++} ELAN3_QUEUE;
++
++typedef struct 
++{
++    u_int     q_back;                 /* Next free space */
++    u_int     q_middle;               /* Middle pointer */
++    u_int     q_front;                /* First object to remove */
++    u_int     q_size;                 /* Size of queue */
++    u_int     q_count;                /* Current number of entries */
++    u_int     q_slop;                 /* FULL <=> (count+slop) == size */
++} ELAN3_SPLIT_QUEUE;
++
++#define ELAN3_QUEUE_INIT(q,num,slop)  ((q).q_size = (num), (q).q_slop = (slop)+1, (q).q_front = (q).q_back = 0, (q).q_count = 0)
++#define ELAN3_QUEUE_FULL(q)           ((q).q_count == ((q).q_size - (q).q_slop))
++#define ELAN3_QUEUE_REALLY_FULL(q)    ((q).q_count == (q).q_size - 1)
++#define ELAN3_QUEUE_EMPTY(q)          ((q).q_count == 0)
++#define ELAN3_QUEUE_FRONT_EMPTY(q)    ((q).q_front == (q).q_middle)
++#define ELAN3_QUEUE_BACK_EMPTY(q)     ((q).q_middle == (q).q_back)
++#define ELAN3_QUEUE_ADD(q)            ((q).q_back = ((q).q_back+1) % (q).q_size, (q).q_count++)
++#define ELAN3_QUEUE_REMOVE(q)         ((q).q_front = ((q).q_front+1) % (q).q_size, (q).q_count--)
++#define ELAN3_QUEUE_ADD_FRONT(q)              ((q).q_front = ((q).q_front-1) % (q).q_size, (q).q_count++)
++#define ELAN3_QUEUE_CONSUME(q)                ((q).q_middle = ((q).q_middle+1) % (q).q_size)
++#define ELAN3_QUEUE_FRONT(q,qArea)    (&(qArea)[(q).q_front])
++#define ELAN3_QUEUE_MIDDLE(q,qArea)   (&(qArea)[(q).q_middle])
++#define ELAN3_QUEUE_BACK(q,qArea)     (&(qArea)[(q).q_back])
++
++#define SDRAM_MIN_BLOCK_SHIFT 10
++#define SDRAM_NUM_FREE_LISTS  17                              /* allows max 64Mb block */
++#define SDRAM_MIN_BLOCK_SIZE  (1 << SDRAM_MIN_BLOCK_SHIFT)
++#define SDRAM_MAX_BLOCK_SIZE  (SDRAM_MIN_BLOCK_SIZE << (SDRAM_NUM_FREE_LISTS-1))
++#define SDRAM_FREELIST_TRIGGER        32
++
++typedef struct elan3_sdram_bank
++{
++    u_int             Size;                                   /* Size of bank of memory */
++
++    ioaddr_t          Mapping;                                /* Where mapped in the kernel */
++    DeviceMappingHandle Handle;                                       /* and mapping handle */
++
++    struct elan3_ptbl_gr **PtblGroups;
++    
++    bitmap_t         *Bitmaps[SDRAM_NUM_FREE_LISTS];
++} ELAN3_SDRAM_BANK;
++
++typedef struct elan3_haltop
++{
++    struct elan3_haltop         *Next;                                /* Chain to next in list. */
++    E3_uint32            Mask;                                /* Interrupt mask to see before calling function */
++    
++    void               (*Function)(void *, void *);           /* Function to call */
++    void                *Arguement;                           /* Arguement to pass to function */
++} ELAN3_HALTOP;
++
++#define HALTOP_BATCH  32
++
++#endif /* __KERNEL__ */
++
++typedef struct elan3_stats
++{
++    u_long    Version;                                        /* version field */
++    u_long    Interrupts;                                     /* count of elan interrupts */
++    u_long    TlbFlushes;                                     /* count of tlb flushes */
++    u_long    InvalidContext;                                 /* count of traps with invalid context */
++    u_long    ComQueueHalfFull;                               /* count of interrupts due to com queue being half full */
++
++    u_long    CProcTraps;                                     /* count of cproc traps */
++    u_long    DProcTraps;                                     /* count of dproc traps */
++    u_long    TProcTraps;                                     /* cound of tproc traps */
++    u_long    IProcTraps;                                     /* count of iproc traps */
++    u_long    EventInterrupts;                                /* count of event interrupts */
++
++    u_long    PageFaults;                                     /* count of elan page faults */
++
++    /* inputter related */
++    u_long    EopBadAcks;                                     /* count of EOP_BAD_ACKs */
++    u_long    EopResets;                                      /* count of EOP_ERROR_RESET */
++    u_long      InputterBadLength;                            /* count of BadLength */
++    u_long      InputterCRCDiscards;                          /* count of CRC_STATUS_DISCARD */
++    u_long      InputterCRCErrors;                            /* count of CRC_STATUS_ERROR */
++    u_long      InputterCRCBad;                                       /* count of CRC_STATUS_BAD */
++    u_long    DmaNetworkErrors;                               /* count of errors in dma data */
++    u_long    DmaIdentifyNetworkErrors;                       /* count of errors after dma identify */
++    u_long    ThreadIdentifyNetworkErrors;                    /* count of errors after thread identify */
++
++    /* dma related */
++    u_long    DmaRetries;                                     /* count of dma retries (due to retry fail count) */    
++    u_long    DmaOutputTimeouts;                              /* count of dma output timeouts */
++    u_long    DmaPacketAckErrors;                             /* count of dma packet ack errors */
++
++    /* thread related */
++    u_long    ForcedTProcTraps;                               /* count of forced tproc traps */
++    u_long    TrapForTooManyInsts;                            /* count of too many instruction traps */
++    u_long    ThreadOutputTimeouts;                           /* count of thread output timeouts */
++    u_long       ThreadPacketAckErrors;                               /* count of thread packet ack errors */
++
++    /* link related */
++    u_long    LockError;                                      /* count of RegPtr->Exts.LinkErrorTypes:LS_LockError */
++    u_long    DeskewError;                                    /* count of RegPtr->Exts.LinkErrorTypes:LS_DeskewError */
++    u_long    PhaseError;                                     /* count of RegPtr->Exts.LinkErrorTypes:LS_PhaseError */
++    u_long    DataError;                                      /* count of RegPtr->Exts.LinkErrorTypes:LS_DataError */
++    u_long    FifoOvFlow0;                                    /* count of RegPtr->Exts.LinkErrorTypes:LS_FifoOvFlow0 */
++    u_long    FifoOvFlow1;                                    /* count of RegPtr->Exts.LinkErrorTypes:LS_FifoOvFlow1 */
++    u_long    LinkErrorValue;                                 /* link error value on data error */
++
++    /* memory related */
++    u_long    CorrectableErrors;                              /* count of correctable ecc errors */
++    u_long    UncorrectableErrors;                            /* count of uncorrectable ecc errors */
++    u_long       MultipleErrors;                                      /* count of multiple ecc errors */
++    u_long    SdramBytesFree;                                 /* count of sdram bytes free */
++    
++    /* Interrupt related */
++    u_long    LongestInterrupt;                               /* length of longest interrupt in ticks */
++
++    u_long    EventPunts;                                     /* count of punts of event interrupts to thread */
++    u_long    EventRescheds;                                  /* count of reschedules of event interrupt thread */
++} ELAN3_STATS;
++
++#define ELAN3_STATS_VERSION   (ulong)2
++#define ELAN3_NUM_STATS               (sizeof (ELAN3_STATS)/sizeof (u_long))
++
++#define ELAN3_STATS_DEV_FMT   "elan3_stats_dev_%d"
++
++#ifdef __KERNEL__
++
++#define BumpStat(dev,stat)    ((dev)->Stats.stat++)
++
++typedef struct elan3_level_ptbl_block
++{
++    spinlock_t                    PtblLock;                           /* Page table freelist lock */
++    int                           PtblTotal;                          /* Count of level N page tables allocated */
++    int                           PtblFreeCount;                      /* Count of free level N page tables */
++    struct elan3_ptbl    *PtblFreeList;                       /* Free level N page tables */
++    struct elan3_ptbl_gr         *PtblGroupList;                      /* List of Groups of level N page tables */
++} ELAN3_LEVEL_PTBL_BLOCK;
++ 
++typedef struct elan3_dev
++{
++    ELAN3_DEV_OSDEP       Osdep;                              /* OS specific entries */
++    int                           Instance;                           /* Device number */
++    ELAN_DEVINFO            Devinfo;                          
++    ELAN_POSITION         Position;                           /* position in switch network (for user code) */
++    ELAN_DEV_IDX          DeviceIdx;                          /* device index registered with elanmod */
++
++    int                           ThreadsShouldStop;                  /* flag that kernel threads should stop */
++
++    spinlock_t                    IntrLock;
++    spinlock_t                    TlbLock;
++    spinlock_t                    CProcLock;
++    kcondvar_t                    IntrWait;                           /* place event interrupt thread sleeps */
++    unsigned              EventInterruptThreadStarted:1;      /* event interrupt thread started */
++    unsigned              EventInterruptThreadStopped:1;      /* event interrupt thread stopped */
++    
++    DeviceMappingHandle           RegHandle;                          /* DDI Handle */
++    ioaddr_t              RegPtr;                             /* Elan Registers */
++
++    volatile E3_uint32            InterruptMask;                      /* copy of RegPtr->InterruptMask */
++    volatile E3_uint32            Event_Int_Queue_FPtr;               /* copy of RegPtr->Event_Int_Queue_FPtr */
++    volatile E3_uint32      SchCntReg;                                /* copy of RegPtr->SchCntReg */
++    volatile E3_uint32      Cache_Control_Reg;                        /* true value for RegPtr->Cache_Control_Reg */
++    
++    ELAN3_SDRAM_BANK      SdramBanks[ELAN3_SDRAM_NUM_BANKS];  /* Elan sdram banks */
++    spinlock_t                    SdramLock;                          /* Sdram allocator */
++    sdramaddr_t                   SdramFreeLists[SDRAM_NUM_FREE_LISTS];
++    unsigned              SdramFreeCounts[SDRAM_NUM_FREE_LISTS];
++              
++    sdramaddr_t                   TAndQBase;                          /* Trap and Queue area */
++    sdramaddr_t                   ContextTable;                       /* Elan Context Table */
++    u_int                 ContextTableSize;                   /* # entries in context table */
++
++    struct elan3_ctxt      **CtxtTable;                         /* array of ctxt pointers or nulls */
++
++    sdramaddr_t                   CommandPortTraps[2];                /* Command port trap overflow */
++    int                           CurrentCommandPortTrap;             /* Which overflow queue we're using */
++    
++    u_int                 HaltAllCount;                       /* Count of reasons to halt context 0 queues */
++    u_int                 HaltNonContext0Count;               /* Count of reasons to halt non-context 0 queues */
++    u_int                 HaltDmaDequeueCount;                /* Count of reasons to halt dma from dequeuing */
++    u_int                 HaltThreadCount;                    /* Count of reasons to halt the thread processor */
++    u_int                 FlushCommandCount;                  /* Count of reasons to flush command queues */
++    u_int                 DiscardAllCount;                    /* Count of reasons to discard context 0 */
++    u_int                 DiscardNonContext0Count;            /* Count of reasons to discard non context 0 */
++
++    struct thread_trap           *ThreadTrap;                         /* Thread Processor trap space */
++    struct dma_trap      *DmaTrap;                            /* DMA Processor trap space */
++
++    spinlock_t                    FreeHaltLock;                       /* Lock for haltop free list */
++    ELAN3_HALTOP                 *FreeHaltOperations;                 /* Free list of haltops */
++    u_int                 NumHaltOperations;                  /* Number of haltops allocated */
++    u_int                 ReservedHaltOperations;             /* Number of haltops reserved */
++
++    ELAN3_HALTOP                 *HaltOperations;                     /* List of operations to call */
++    ELAN3_HALTOP                **HaltOperationsTailpp;               /* Pointer to last "next" pointer in list */
++    E3_uint32             HaltOperationsMask;                 /* Or of all bits in list of operations */
++
++    physaddr_t                    SdramPhysBase;                      /* Physical address of SDRAM */
++    physaddr_t                    SdramPhysMask;                      /* and mask of significant bits */ 
++    
++    physaddr_t                    PciPhysBase;                        /* physical address of local PCI segment */
++    physaddr_t                    PciPhysMask;                        /* and mask of significant bits */
++
++    long                  ErrorTime;                          /* lbolt at last error (link,ecc etc) */
++    long                  ErrorsPerTick;                      /* count of errors for this tick */
++    timer_fn_t                    ErrorTimeoutId;                     /* id of timeout when errors masked out */
++    timer_fn_t                    DmaPollTimeoutId;                   /* id of timeout to poll for "bad" dmas */
++    int                           FilterHaltQueued;
++
++    /*
++     * HAT layer specific entries.
++     */
++    ELAN3_LEVEL_PTBL_BLOCK   Level[4];
++    spinlock_t                    PtblGroupLock;                      /* Lock for Page Table group lists */
++    struct elan3_ptbl_gr    *Level3PtblGroupHand;             /* Hand for ptbl stealing */
++
++    /*
++     * Per-Context Information structures.
++     */
++    struct elan3_info    *Infos;                              /* List of "infos" for this device */
++
++    char                    LinkShutdown;                       /* link forced into reset by panic/shutdown/dump */
++
++    /*
++     * Device statistics.
++     */
++    ELAN3_STATS                   Stats;
++    ELAN_STATS_IDX          StatsIndex;
++
++    struct {
++      E3_Regs            *RegPtr;
++      char               *Sdram[ELAN3_SDRAM_NUM_BANKS];
++    } PanicState;
++} ELAN3_DEV;
++
++#define ELAN3_DEV_CTX_TABLE(dev,ctxtn) ( (dev)->CtxtTable[ (ctxtn) &  MAX_ROOT_CONTEXT_MASK] )
++
++/* macros for accessing dev->RegPtr.Tags/Sets. */
++#define write_cache_tag(dev,what,val) writeq (val, dev->RegPtr + offsetof (E3_Regs, Tags.what))
++#define read_cache_tag(dev,what)      readq (dev->RegPtr + offsetof (E3_Regs, Tags.what))
++#define write_cache_set(dev,what,val) writeq (val, dev->RegPtr + offsetof (E3_Regs, Sets.what))
++#define read_cache_set(dev,what)      readq (dev->RegPtr + offsetof (E3_Regs, Sets.what))
++
++/* macros for accessing dev->RegPtr.Regs. */
++#define write_reg64(dev,what,val)     writeq (val, dev->RegPtr + offsetof (E3_Regs, Regs.what))
++#define write_reg32(dev,what,val)     writel (val, dev->RegPtr + offsetof (E3_Regs, Regs.what))
++#define read_reg64(dev,what)          readq (dev->RegPtr + offsetof (E3_Regs, Regs.what))
++#define read_reg32(dev,what)          readl (dev->RegPtr + offsetof (E3_Regs, Regs.what))
++
++/* macros for accessing dev->RegPtr.uRegs. */
++#define write_ureg64(dev,what,val)    writeq (val, dev->RegPtr + offsetof (E3_Regs, URegs.what))
++#define write_ureg32(dev,what,val)    writel (val, dev->RegPtr + offsetof (E3_Regs, URegs.what))
++#define read_ureg64(dev,what)         readq (dev->RegPtr + offsetof (E3_Regs, URegs.what))
++#define read_ureg32(dev,what)         readl (dev->RegPtr + offsetof (E3_Regs, URegs.what))
++
++/* macros for accessing dma descriptor/thread regs */
++#define copy_dma_regs(dev, desc) \
++MACRO_BEGIN \
++    register int i;  \
++    for (i = 0; i < sizeof (E3_DMA)/sizeof(E3_uint64); i++) \
++      ((E3_uint64 *) desc)[i] = readq (dev->RegPtr + offsetof (E3_Regs, Regs.Dma_Desc) + i*sizeof (E3_uint64)); \
++MACRO_END
++
++#define copy_thread_regs(dev, regs) \
++MACRO_BEGIN \
++    register int i;  \
++    for (i = 0; i < (32*sizeof (E3_uint32))/sizeof(E3_uint64); i++) \
++      ((E3_uint64 *) regs)[i] = readq (dev->RegPtr + offsetof (E3_Regs, Regs.Globals[0]) + i*sizeof (E3_uint64)); \
++MACRO_END
++
++_NOTE(MUTEX_PROTECTS_DATA(elan3_dev::IntrLock, 
++                        _E3_DataBusMap::Exts _E3_DataBusMap::Input_Context_Fil_Flush
++                        elan3_dev::CurrentCommandPortTrap elan3_dev::HaltAllCount elan3_dev::HaltDmaDequeueCount
++                        elan3_dev::FlushCommandCount elan3_dev::DiscardAllCount elan3_dev::DiscardNonContext0Count
++                        elan3_dev::HaltOperations elan3_dev::HaltOperationsMask))
++_NOTE(MUTEX_PROTECTS_DATA(elan3_dev::TlbLock, 
++                        _E3_DataBusMap::Cache_Control_Reg))
++_NOTE(MUTEX_PROTECTS_DATA(elan3_dev::InfoLock, 
++                        elan3_dev::Infos elan3_dev::InfoTable))
++_NOTE(MUTEX_PROTECTS_DATA(elan3_dev::FreeHaltLock, 
++                        elan3_dev::FreeHaltOperations elan3_dev::NumHaltOperations elan3_dev::ReservedHaltOperations))
++_NOTE(MUTEX_PROTECTS_DATA(elan3_dev::PageFreeListLock, 
++                        elan3_dev::PageFreeList elan3_dev::PageFreeListSize))
++_NOTE(MUTEX_PROTECTS_DATA(elan3_dev::Level1PtblLock,
++                        elan3_dev::Level1PtblTotal elan3_dev::Level1PtblFreeCount elan3_dev::Level1PtblFreeList))
++_NOTE(MUTEX_PROTECTS_DATA(elan3_dev::Level2PtblLock,
++                        elan3_dev::Level2PtblTotal elan3_dev::Level2PtblFreeCount elan3_dev::Level2PtblFreeList))
++_NOTE(MUTEX_PROTECTS_DATA(elan3_dev::Level3PtblLock,
++                        elan3_dev::Level3PtblTotal elan3_dev::Level3PtblFreeCount elan3_dev::Level3PtblFreeList))
++_NOTE(MUTEX_PROTECTS_DATA(elan3_dev::PtblGroupLock,
++                        elan3_dev::Level1PtblGroupList elan3_dev::Level2PtblGroupList elan3_dev::Level3PtblGroupList))
++
++_NOTE(DATA_READABLE_WITHOUT_LOCK(elan3_dev::InfoTable elan3_dev::Level1PtblFreeList
++                               elan3_dev::Level2PtblFreeList elan3_dev::Level3PtblFreeList))
++
++_NOTE(LOCK_ORDER(elan3_dev::InfoLock elan3_dev::IntrLock))
++_NOTE(LOCK_ORDER(as::a_lock elan3_dev::InfoLock))
++_NOTE(LOCK_ORDER(as::a_lock elan3_dev::IntrLock))
++
++#define SET_INT_MASK(dev,Mask)                MACRO_BEGIN write_reg32 (dev, Exts.InterruptMask, ((dev)->InterruptMask = (Mask)));  mmiob(); MACRO_END
++#define ENABLE_INT_MASK(dev, bits)    MACRO_BEGIN write_reg32 (dev, Exts.InterruptMask, ((dev->InterruptMask |= (bits)))); mmiob(); MACRO_END
++#define DISABLE_INT_MASK(dev, bits)   MACRO_BEGIN write_reg32 (dev, Exts.InterruptMask, ((dev->InterruptMask &= ~(bits)))); mmiob(); MACRO_END
++
++#define INIT_SCHED_STATUS(dev, val) \
++MACRO_BEGIN \
++      (dev)->SchCntReg = (val); \
++      write_reg32 (dev, Exts.SchCntReg, (dev)->SchCntReg); \
++      mmiob(); \
++MACRO_END
++
++#define SET_SCHED_STATUS(dev, val) \
++MACRO_BEGIN \
++      ASSERT (((val) & HaltStopAndExtTestMask) == (val)); \
++      (dev)->SchCntReg |= (val); \
++      write_reg32 (dev, Exts.SchCntReg, (dev)->SchCntReg); \
++        mmiob (); \
++MACRO_END
++
++#define CLEAR_SCHED_STATUS(dev, val) \
++MACRO_BEGIN \
++      ASSERT (((val) & HaltStopAndExtTestMask) == (val)); \
++      (dev)->SchCntReg &= ~(val); \
++      write_reg32 (dev, Exts.SchCntReg, (dev)->SchCntReg); \
++        mmiob(); \
++MACRO_END
++
++#define MODIFY_SCHED_STATUS(dev, SetBits, ClearBits) \
++MACRO_BEGIN \
++      ASSERT ((((SetBits)|(ClearBits)) & HaltStopAndExtTestMask) == ((SetBits)|(ClearBits))); \
++      (dev)->SchCntReg = (((dev)->SchCntReg | (SetBits)) & ~(ClearBits)); \
++      write_reg32 (dev, Exts.SchCntReg, (dev)->SchCntReg); \
++      mmiob(); \
++MACRO_END
++
++#define PULSE_SCHED_STATUS(dev, RestartBits) \
++MACRO_BEGIN \
++      ASSERT (((RestartBits) & HaltStopAndExtTestMask) == 0); \
++      write_reg32 (dev, Exts.SchCntReg, (dev)->SchCntReg | (RestartBits)); \
++      mmiob(); \
++MACRO_END
++
++#define SET_SCHED_LINK_VALUE(dev, enabled, val) \
++MACRO_BEGIN \
++      (dev)->SchCntReg = (((dev)->SchCntReg & HaltAndStopMask) | ((enabled) ? LinkBoundaryScan : 0) | LinkSetValue(val, 0)); \
++      write_reg32 (dev, Exts.SchCntReg, (dev)->SchCntReg); \
++      mmiob(); \
++MACRO_END
++
++#ifdef DEBUG_ASSERT
++#  define ELAN3_ASSERT(dev, EX)       ((void)((EX) || elan3_assfail(dev, #EX, __FILE__, __LINE__)))
++#else
++#  define ELAN3_ASSERT(dev, EX)
++#endif
++
++/* elandev_generic.c */
++extern int        InitialiseElan (ELAN3_DEV *dev, ioaddr_t CmdPort);
++extern void       FinaliseElan (ELAN3_DEV *dev);
++extern int        InterruptHandler (ELAN3_DEV *dev);
++extern void       PollForDmaHungup (void *arg);
++
++extern int        SetLinkBoundaryScan (ELAN3_DEV *dev);
++extern void       ClearLinkBoundaryScan (ELAN3_DEV *dev);
++extern int        WriteBoundaryScanValue (ELAN3_DEV *dev, int value);
++extern int        ReadBoundaryScanValue(ELAN3_DEV *dev, int link);
++
++extern int        ReadVitalProductData (ELAN3_DEV *dev, int *CasLatency);
++
++extern struct elan3_ptbl_gr *ElanGetPtblGr (ELAN3_DEV *dev, sdramaddr_t offset);
++extern void       ElanSetPtblGr (ELAN3_DEV *dev, sdramaddr_t offset, struct elan3_ptbl_gr *ptg);
++
++extern void       ElanFlushTlb (ELAN3_DEV *dev);
++
++extern void       SetSchedStatusRegister (ELAN3_DEV *dev, E3_uint32 Pend, volatile E3_uint32 *Maskp);
++extern void     FreeHaltOperation (ELAN3_DEV *dev, ELAN3_HALTOP *op);
++extern int      ReserveHaltOperations (ELAN3_DEV *dev, int count, int cansleep);
++extern void     ReleaseHaltOperations (ELAN3_DEV *dev, int count);
++extern void     ProcessHaltOperations (ELAN3_DEV *dev, E3_uint32 Pend);
++extern void     QueueHaltOperation (ELAN3_DEV *dev, E3_uint32 Pend, volatile E3_uint32 *Maskp,
++                                    E3_uint32 ReqMask, void (*Function)(ELAN3_DEV *, void *), void *Arguement);
++
++extern int        ComputePosition (ELAN_POSITION *pos, unsigned NodeId, unsigned NumNodes, unsigned numDownLinksVal);
++
++extern caddr_t          MiToName (int mi);
++extern void     ElanBusError (ELAN3_DEV *dev);
++
++extern void     TriggerLsa (ELAN3_DEV *dev);
++
++extern ELAN3_DEV  *elan3_device (int instance);
++extern int      DeviceRegisterSize (ELAN3_DEV *dev, int rnumber, int *sizep);
++extern int      MapDeviceRegister (ELAN3_DEV *dev, int rnumber, ioaddr_t *addrp, int offset, 
++                                   int len, DeviceMappingHandle *handlep);
++extern void       UnmapDeviceRegister (ELAN3_DEV *dev, DeviceMappingHandle *handlep);
++
++
++/* sdram.c */
++/* sdram accessing functions - define 4 different types for 8,16,32,64 bit accesses */
++extern unsigned char      elan3_sdram_readb (ELAN3_DEV *dev, sdramaddr_t ptr);
++extern unsigned short     elan3_sdram_readw (ELAN3_DEV *dev, sdramaddr_t ptr);
++extern unsigned int       elan3_sdram_readl (ELAN3_DEV *dev, sdramaddr_t ptr);
++extern unsigned long long elan3_sdram_readq (ELAN3_DEV *dev, sdramaddr_t ptr);
++extern void               elan3_sdram_writeb (ELAN3_DEV *dev, sdramaddr_t ptr, unsigned char val);
++extern void               elan3_sdram_writew (ELAN3_DEV *dev, sdramaddr_t ptr, unsigned short val);
++extern void               elan3_sdram_writel (ELAN3_DEV *dev, sdramaddr_t ptr, unsigned int val);
++extern void               elan3_sdram_writeq (ELAN3_DEV *dev, sdramaddr_t ptr, unsigned long long val);
++
++extern void             elan3_sdram_zerob_sdram (ELAN3_DEV *dev, sdramaddr_t ptr, int nbytes);
++extern void             elan3_sdram_zerow_sdram (ELAN3_DEV *dev, sdramaddr_t ptr, int nbytes);
++extern void             elan3_sdram_zerol_sdram (ELAN3_DEV *dev, sdramaddr_t ptr, int nbytes);
++extern void             elan3_sdram_zeroq_sdram (ELAN3_DEV *dev, sdramaddr_t ptr, int nbytes);
++
++extern void               elan3_sdram_copyb_from_sdram (ELAN3_DEV *dev, sdramaddr_t from, void *to, int nbytes);
++extern void               elan3_sdram_copyw_from_sdram (ELAN3_DEV *dev, sdramaddr_t from, void *to, int nbytes);
++extern void               elan3_sdram_copyl_from_sdram (ELAN3_DEV *dev, sdramaddr_t from, void *to, int nbytes);
++extern void               elan3_sdram_copyq_from_sdram (ELAN3_DEV *dev, sdramaddr_t from, void *to, int nbytes);
++extern void               elan3_sdram_copyb_to_sdram (ELAN3_DEV *dev, void *from, sdramaddr_t to, int nbytes);
++extern void               elan3_sdram_copyw_to_sdram (ELAN3_DEV *dev, void *from, sdramaddr_t to, int nbytes);
++extern void               elan3_sdram_copyl_to_sdram (ELAN3_DEV *dev, void *from, sdramaddr_t to, int nbytes);
++extern void               elan3_sdram_copyq_to_sdram (ELAN3_DEV *dev, void *from, sdramaddr_t to, int nbytes);
++
++extern void             elan3_sdram_init (ELAN3_DEV *dev);
++extern void               elan3_sdram_fini (ELAN3_DEV *dev);
++extern void             elan3_sdram_add (ELAN3_DEV *dev, sdramaddr_t base, sdramaddr_t top);
++extern sdramaddr_t        elan3_sdram_alloc (ELAN3_DEV *dev, int nbytes);
++extern void               elan3_sdram_free (ELAN3_DEV *dev, sdramaddr_t ptr, int nbytes);
++extern physaddr_t         elan3_sdram_to_phys (ELAN3_DEV *dev, sdramaddr_t addr);
++
++/* cproc.c */
++extern void     HandleCProcTrap (ELAN3_DEV *dev, E3_uint32 Pend, E3_uint32 *Mask);
++
++/* iproc.c */
++extern void     HandleIProcTrap (ELAN3_DEV *dev, int Channel, E3_uint32 Pend, sdramaddr_t FaultSaveOff, 
++                                 sdramaddr_t TransactionsOff, sdramaddr_t DataOff);
++
++/* tproc.c */
++extern int      HandleTProcTrap (ELAN3_DEV *dev, E3_uint32 *RestartBits);
++extern void     DeliverTProcTrap (ELAN3_DEV *dev, struct thread_trap *threadTrap, E3_uint32 Pend);
++
++/* dproc.c */
++extern int      HandleDProcTrap (ELAN3_DEV *dev, E3_uint32 *RestartBits);
++extern void     DeliverDProcTrap (ELAN3_DEV *dev, struct dma_trap *dmaTrap, E3_uint32 Pend);
++
++#if defined(LINUX)
++/* procfs_linux.h */
++extern struct proc_dir_entry *elan3_procfs_root;
++extern struct proc_dir_entry *elan3_config_root;
++
++extern void elan3_procfs_init(void);
++extern void elan3_procfs_fini(void);
++extern void elan3_procfs_device_init (ELAN3_DEV *dev);
++extern void elan3_procfs_device_fini (ELAN3_DEV *dev);
++#endif /* defined(LINUX) */
++
++/* elan3_osdep.c */
++extern int        BackToBackMaster;
++extern int        BackToBackSlave;
++
++#define ELAN_REG_REC_MAX (100)
++#define ELAN_REG_REC(REG)  {                                         \
++elan_reg_rec_file [elan_reg_rec_index] = __FILE__;                   \
++elan_reg_rec_line [elan_reg_rec_index] = __LINE__;                   \
++elan_reg_rec_reg  [elan_reg_rec_index] = REG;                        \
++elan_reg_rec_cpu  [elan_reg_rec_index] = smp_processor_id();         \
++elan_reg_rec_lbolt[elan_reg_rec_index] = lbolt;                      \
++elan_reg_rec_index = ((elan_reg_rec_index+1) % ELAN_REG_REC_MAX);}
++
++extern char *    elan_reg_rec_file [ELAN_REG_REC_MAX];
++extern int       elan_reg_rec_line [ELAN_REG_REC_MAX];
++extern long      elan_reg_rec_lbolt[ELAN_REG_REC_MAX];
++extern int       elan_reg_rec_cpu  [ELAN_REG_REC_MAX];
++extern E3_uint32 elan_reg_rec_reg  [ELAN_REG_REC_MAX];
++extern int       elan_reg_rec_index;
++ 
++#endif /* __KERNEL__ */
++
++
++#define ELAN3_PROCFS_ROOT          "/proc/qsnet/elan3"
++#define ELAN3_PROCFS_VERSION       "/proc/qsnet/elan3/version"
++#define ELAN3_PROCFS_DEBUG         "/proc/qsnet/elan3/config/elandebug"
++#define ELAN3_PROCFS_DEBUG_CONSOLE "/proc/qsnet/elan3/config/elandebug_console"
++#define ELAN3_PROCFS_DEBUG_BUFFER  "/proc/qsnet/elan3/config/elandebug_buffer"
++#define ELAN3_PROCFS_MMU_DEBUG     "/proc/qsnet/elan3/config/elan3mmu_debug"
++#define ELAN3_PROCFS_PUNT_LOOPS    "/proc/qsnet/elan3/config/eventint_punt_loops"
++
++#define ELAN3_PROCFS_DEVICE_STATS_FMT    "/proc/qsnet/elan3/device%d/stats"
++#define ELAN3_PROCFS_DEVICE_POSITION_FMT "/proc/qsnet/elan3/device%d/position"
++#define ELAN3_PROCFS_DEVICE_NODESET_FMT  "/proc/qsnet/elan3/device%d/nodeset"
++
++#endif /* __ELAN3_ELANDEV_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/include/elan3/elandev_linux.h
+===================================================================
+--- linux-2.6.5.orig/include/elan3/elandev_linux.h     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan3/elandev_linux.h  2005-05-11 12:10:12.588909776 -0400
+@@ -0,0 +1,74 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELANDEV_LINUX_H
++#define __ELANDEV_LINUX_H
++
++#ident "$Id: elandev_linux.h,v 1.11.2.1 2005/03/07 16:27:42 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/elandev_linux.h,v $*/
++
++#ifdef __KERNEL__
++#include <linux/mm.h>
++#include <linux/sched.h>
++#include <linux/pci.h>
++
++#include <qsnet/autoconf.h>
++
++#if !defined(NO_COPROC)                               /* The older coproc kernel patch is applied */
++#include <linux/coproc.h>
++
++#define ioproc_ops            coproc_ops_struct
++#define ioproc_register_ops   register_coproc_ops
++#define ioproc_unregister_ops unregister_coproc_ops
++
++#define IOPROC_MM_STRUCT_ARG  1
++#define IOPROC_PATCH_APPLIED  1
++
++#elif !defined(NO_IOPROC)                     /* The new ioproc kernel patch is applied */
++#include <linux/ioproc.h>
++
++#define IOPROC_PATCH_APPLIED  1
++#endif
++#endif
++
++#define ELAN3_MAJOR              60
++#define ELAN3_NAME               "elan3"
++#define ELAN3_MAX_CONTROLLER     16                      /* limited to 4 bits */
++ 
++#define ELAN3_MINOR_DEVNUM(m)    ((m) & 0x0f)            /* card number */
++#define ELAN3_MINOR_DEVFUN(m)    (((m) >> 4) & 0x0f)     /* function */
++#define ELAN3_MINOR_CONTROL      0                       /* function values */
++#define ELAN3_MINOR_MEM          1
++#define ELAN3_MINOR_USER              2
++ 
++typedef void                  *DeviceMappingHandle;
++
++/* task and ctxt handle types */
++typedef struct mm_struct      *TaskHandle;
++typedef int                   CtxtHandle;
++ 
++#define ELAN3_MY_TASK_HANDLE()        (current->mm)
++#define KERNEL_TASK_HANDLE()  (get_kern_mm())
++ 
++/*
++ * OS-dependent component of ELAN3_DEV struct.
++ */
++typedef struct elan3_dev_osdep
++{
++      struct pci_dev  *pci;                   /* PCI config data */
++      int             ControlDeviceOpen;      /* flag to indicate control */
++                                              /*   device open */
++       struct proc_dir_entry *procdir;
++} ELAN3_DEV_OSDEP;
++
++#endif /* __ELANDEV_LINUX_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/include/elan3/elanio.h
+===================================================================
+--- linux-2.6.5.orig/include/elan3/elanio.h    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan3/elanio.h 2005-05-11 12:10:12.588909776 -0400
+@@ -0,0 +1,226 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN3_ELAN3IO_H
++#define __ELAN3_ELAN3IO_H
++
++#ident "$Id: elanio.h,v 1.19 2003/12/08 15:40:26 mike Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/elanio.h,v $*/
++
++#define ELAN3IO_CONTROL_PATHNAME      "/dev/elan3/control%d"
++#define ELAN3IO_MEM_PATHNAME  "/dev/elan3/mem%d"
++#define ELAN3IO_USER_PATHNAME "/dev/elan3/user%d"
++#define ELAN3IO_SDRAM_PATHNAME   "/dev/elan3/sdram%d"
++#define ELAN3IO_MAX_PATHNAMELEN       32
++
++/* ioctls on /dev/elan3/control */
++#define ELAN3IO_CONTROL_BASE          0
++
++#define ELAN3IO_SET_BOUNDARY_SCAN     _IO   ('e', ELAN3IO_CONTROL_BASE + 0)
++#define ELAN3IO_CLEAR_BOUNDARY_SCAN   _IO   ('e', ELAN3IO_CONTROL_BASE + 1)
++#define ELAN3IO_READ_LINKVAL          _IOWR ('e', ELAN3IO_CONTROL_BASE + 2, E3_uint32)
++#define ELAN3IO_WRITE_LINKVAL         _IOWR ('e', ELAN3IO_CONTROL_BASE + 3, E3_uint32)
++
++typedef struct elanio_set_debug_struct
++{
++    char      what[32];
++    u_long    value;
++} ELAN3IO_SET_DEBUG_STRUCT;
++#define ELAN3IO_SET_DEBUG             _IOW  ('e', ELAN3IO_CONTROL_BASE + 4, ELAN3IO_SET_DEBUG_STRUCT)
++
++typedef struct elanio_debug_buffer_struct
++{
++    caddr_t   addr;
++    size_t    len;
++} ELAN3IO_DEBUG_BUFFER_STRUCT;
++#define ELAN3IO_DEBUG_BUFFER          _IOWR ('e', ELAN3IO_CONTROL_BASE + 5, ELAN3IO_DEBUG_BUFFER_STRUCT)
++
++typedef struct elanio_neterr_server_struct
++{
++    u_int             elanid;
++    void             *addr;
++    char             *name;
++} ELAN3IO_NETERR_SERVER_STRUCT;
++#define ELAN3IO_NETERR_SERVER         _IOW  ('e', ELAN3IO_CONTROL_BASE + 6, ELAN3IO_NETERR_SERVER_STRUCT)
++#define ELAN3IO_NETERR_FIXUP          _IOWR ('e', ELAN3IO_CONTROL_BASE + 7, NETERR_MSG)
++
++typedef struct elanio_set_position_struct
++{
++    u_int             device;
++    unsigned short      nodeId;
++    unsigned short      numNodes;
++} ELAN3IO_SET_POSITION_STRUCT;
++#define ELAN3IO_SET_POSITION          _IOW ('e', ELAN3IO_CONTROL_BASE + 8, ELAN3IO_SET_POSITION_STRUCT)
++
++#if defined(LINUX)
++
++/* ioctls on /dev/elan3/sdram */
++#define ELAN3IO_SDRAM_BASE            20
++
++/* ioctls on /dev/elan3/user */
++#define ELAN3IO_USER_BASE             30
++
++#define ELAN3IO_FREE                  _IO  ('e', ELAN3IO_USER_BASE + 0)
++
++#define ELAN3IO_ATTACH                        _IOWR('e', ELAN3IO_USER_BASE + 1, ELAN_CAPABILITY)
++#define ELAN3IO_DETACH                        _IO  ('e', ELAN3IO_USER_BASE + 2)
++
++typedef struct elanio_addvp_struct
++{
++    u_int             process;
++    ELAN_CAPABILITY     capability;
++} ELAN3IO_ADDVP_STRUCT;
++#define ELAN3IO_ADDVP                 _IOWR('e', ELAN3IO_USER_BASE + 3, ELAN3IO_ADDVP_STRUCT)
++#define ELAN3IO_REMOVEVP                      _IOW ('e', ELAN3IO_USER_BASE + 4, int)
++
++typedef struct elanio_bcastvp_struct
++{
++    u_int             process;
++    u_int             lowvp;
++    u_int             highvp;
++} ELAN3IO_BCASTVP_STRUCT;
++#define ELAN3IO_BCASTVP                       _IOW ('e', ELAN3IO_USER_BASE + 5, ELAN3IO_BCASTVP_STRUCT)
++
++typedef struct elanio_loadroute_struct
++{
++    u_int             process;
++    E3_uint16         flits[MAX_FLITS];
++} ELAN3IO_LOAD_ROUTE_STRUCT;
++#define ELAN3IO_LOAD_ROUTE            _IOW ('e', ELAN3IO_USER_BASE + 6, ELAN3IO_LOAD_ROUTE_STRUCT)
++
++#define ELAN3IO_PROCESS                       _IO  ('e', ELAN3IO_USER_BASE + 7)
++
++typedef struct elanio_setperm_struct
++{
++    caddr_t           maddr;
++    E3_Addr           eaddr;
++    size_t            len;
++    int                       perm;
++} ELAN3IO_SETPERM_STRUCT;
++#define ELAN3IO_SETPERM                       _IOW ('e', ELAN3IO_USER_BASE + 8, ELAN3IO_SETPERM_STRUCT)
++
++typedef struct elanio_clearperm_struct
++{
++    E3_Addr           eaddr;
++    size_t            len;
++} ELAN3IO_CLEARPERM_STRUCT;
++#define ELAN3IO_CLEARPERM             _IOW ('e', ELAN3IO_USER_BASE + 9, ELAN3IO_CLEARPERM_STRUCT)
++
++typedef struct elanio_changeperm_struct
++{
++    E3_Addr           eaddr;
++    size_t            len;
++    int                       perm;
++} ELAN3IO_CHANGEPERM_STRUCT;
++#define ELAN3IO_CHANGEPERM            _IOW ('e', ELAN3IO_USER_BASE + 10, ELAN3IO_CHANGEPERM_STRUCT)
++
++
++#define ELAN3IO_HELPER_THREAD         _IO  ('e', ELAN3IO_USER_BASE + 11)
++#define ELAN3IO_WAITCOMMAND           _IO  ('e', ELAN3IO_USER_BASE + 12)
++#define ELAN3IO_BLOCK_INPUTTER                _IOW ('e', ELAN3IO_USER_BASE + 13, int)
++#define ELAN3IO_SET_FLAGS             _IOW ('e', ELAN3IO_USER_BASE + 14, int)
++
++#define ELAN3IO_WAITEVENT             _IOW ('e', ELAN3IO_USER_BASE + 15, E3_Event)
++#define ELAN3IO_ALLOC_EVENTCOOKIE     _IOW ('e', ELAN3IO_USER_BASE + 16, EVENT_COOKIE)
++#define ELAN3IO_FREE_EVENTCOOKIE              _IOW ('e', ELAN3IO_USER_BASE + 17, EVENT_COOKIE)
++#define ELAN3IO_ARM_EVENTCOOKIE               _IOW ('e', ELAN3IO_USER_BASE + 18, EVENT_COOKIE)
++#define ELAN3IO_WAIT_EVENTCOOKIE              _IOW ('e', ELAN3IO_USER_BASE + 19, EVENT_COOKIE)
++
++#define ELAN3IO_SWAPSPACE             _IOW ('e', ELAN3IO_USER_BASE + 20, SYS_SWAP_SPACE)
++#define ELAN3IO_EXCEPTION_SPACE               _IOW ('e', ELAN3IO_USER_BASE + 21, SYS_EXCEPTION_SPACE)
++#define ELAN3IO_GET_EXCEPTION         _IOR ('e', ELAN3IO_USER_BASE + 22, SYS_EXCEPTION)
++
++typedef struct elanio_unload_struct
++{
++    void      *addr;
++    size_t     len;
++} ELAN3IO_UNLOAD_STRUCT;
++#define ELAN3IO_UNLOAD                        _IOW ('e', ELAN3IO_USER_BASE + 23, ELAN3IO_UNLOAD_STRUCT)
++
++
++
++typedef struct elanio_getroute_struct
++{
++    u_int             process;
++    E3_uint16         flits[MAX_FLITS];
++} ELAN3IO_GET_ROUTE_STRUCT;
++#define ELAN3IO_GET_ROUTE             _IOW ('e', ELAN3IO_USER_BASE + 24, ELAN3IO_GET_ROUTE_STRUCT)
++
++typedef struct elanio_resetroute_struct
++{
++    u_int             process;
++} ELAN3IO_RESET_ROUTE_STRUCT;
++#define ELAN3IO_RESET_ROUTE           _IOW ('e', ELAN3IO_USER_BASE + 25, ELAN3IO_RESET_ROUTE_STRUCT)
++
++typedef struct elanio_checkroute_struct
++{
++    u_int             process;
++    E3_uint32           routeError;
++    E3_uint16         flits[MAX_FLITS];
++} ELAN3IO_CHECK_ROUTE_STRUCT;
++#define ELAN3IO_CHECK_ROUTE           _IOW ('e', ELAN3IO_USER_BASE + 26, ELAN3IO_CHECK_ROUTE_STRUCT)
++
++typedef struct elanio_vp2nodeId_struct
++{
++    u_int             process;
++    unsigned short      nodeId;
++    ELAN_CAPABILITY   cap;
++} ELAN3IO_VP2NODEID_STRUCT;
++#define ELAN3IO_VP2NODEID     _IOWR('e', ELAN3IO_USER_BASE + 27, ELAN3IO_VP2NODEID_STRUCT)
++
++#define ELAN3IO_SET_SIGNAL    _IOW ('e', ELAN3IO_USER_BASE + 28, int)
++
++typedef struct elanio_process_2_location_struct
++{
++    u_int             process;
++    ELAN_LOCATION       loc;
++} ELAN3IO_PROCESS_2_LOCATION_STRUCT;
++#define ELAN3IO_PROCESS_2_LOCATION    _IOW ('e', ELAN3IO_USER_BASE + 29, ELAN3IO_PROCESS_2_LOCATION_STRUCT)
++
++
++
++/* ioctls on all device */
++#define ELAN3IO_GENERIC_BASE          100
++typedef struct elanio_get_devinfo_struct
++{
++    ELAN_DEVINFO *devinfo;
++} ELAN3IO_GET_DEVINFO_STRUCT;
++#define ELAN3IO_GET_DEVINFO           _IOR ('e', ELAN3IO_GENERIC_BASE + 0, ELAN_DEVINFO)
++
++typedef struct elanio_get_position_struct
++{
++    ELAN_POSITION *position;
++} ELAN3IO_GET_POSITION_STRUCT;
++#define ELAN3IO_GET_POSITION             _IOR ('e', ELAN3IO_GENERIC_BASE + 1, ELAN_POSITION)
++
++typedef struct elanio_stats_struct
++{
++    int               which;
++    void       *ptr;
++} ELAN3IO_STATS_STRUCT;
++#define ELAN3IO_STATS                 _IOR ('e', ELAN3IO_GENERIC_BASE + 2, ELAN3IO_STATS_STRUCT)
++#  define ELAN3_SYS_STATS_DEVICE      0
++#  define ELAN3_SYS_STATS_MMU         1
++
++/* offsets on /dev/elan3/control */
++
++/* offsets on /dev/elan3/mem */
++
++/* page numbers on /dev/elan3/user */
++#define ELAN3IO_OFF_COMMAND_PAGE              0
++#define ELAN3IO_OFF_FLAG_PAGE         1
++#define ELAN3IO_OFF_UREG_PAGE         2
++
++#endif /* LINUX */
++
++#endif /* __ELAN3_ELAN3IO_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/include/elan3/elanregs.h
+===================================================================
+--- linux-2.6.5.orig/include/elan3/elanregs.h  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan3/elanregs.h       2005-05-11 12:10:12.590909472 -0400
+@@ -0,0 +1,1063 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++/*
++ * Header file for internal slave mapping of the ELAN3 registers
++ */
++
++#ifndef _ELAN3_ELANREGS_H
++#define _ELAN3_ELANREGS_H
++
++#ident "$Id: elanregs.h,v 1.87 2004/04/22 12:27:21 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/elanregs.h,v $*/
++
++#include <elan3/e3types.h>
++#include <elan3/dma.h>
++#include <elan3/elanuregs.h>
++
++#define MAX_ROOT_CONTEXT_MASK 0xfff
++#define SYS_CONTEXT_BIT               0x1000
++#define ALL_CONTEXT_BITS      (MAX_ROOT_CONTEXT_MASK | SYS_CONTEXT_BIT)
++#define ROOT_TAB_OFFSET(Cntxt)        (((Cntxt) & MAX_ROOT_CONTEXT_MASK) << 4)
++#define CLEAR_SYS_BIT(Cntxt)  ((Cntxt) & ~SYS_CONTEXT_BIT)
++
++#define E3_CACHELINE_SIZE     (32)
++#define E3_CACHE_SIZE         (8192)
++
++typedef volatile struct _E3_CacheSets
++{
++   E3_uint64  Set0[256];      /* 2k bytes per set */
++   E3_uint64  Set1[256];      /* 2k bytes per set */
++   E3_uint64  Set2[256];      /* 2k bytes per set */
++   E3_uint64  Set3[256];      /* 2k bytes per set */
++} E3_CacheSets;
++
++typedef union e3_cache_tag
++{
++   E3_uint64  Value;
++   struct {
++#if defined(__LITTLE_ENDIAN__)
++       E3_uint32 pad2:8;              /* Undefined value when read */
++       E3_uint32 LineError:1;         /* A line error has occured */
++       E3_uint32 Modified:1;          /* Cache data is modified */
++       E3_uint32 FillPending:1;               /* Pipelined fill occuring*/
++       E3_uint32 AddrTag27to11:17;    /* Tag address bits 27 to 11 */
++       E3_uint32 pad1:4;              /* Undefined value when read */
++       E3_uint32 pad0;                        /* Undefined value when read */
++#else
++       E3_uint32 pad0;                        /* Undefined value when read */
++       E3_uint32 pad1:4;              /* Undefined value when read */
++       E3_uint32 AddrTag27to11:17;    /* Tag address bits 27 to 11 */
++       E3_uint32 FillPending:1;               /* Pipelined fill occuring*/
++       E3_uint32 Modified:1;          /* Cache data is modified */
++       E3_uint32 LineError:1;         /* A line error has occured */
++       E3_uint32 pad2:8;              /* Undefined value when read */
++#endif
++   } s;
++} E3_CacheTag;
++
++#define E3_NumCacheLines      64
++#define E3_NumCacheSets               4
++
++typedef volatile struct _E3_CacheTags
++{
++   E3_CacheTag Tags[E3_NumCacheLines][E3_NumCacheSets];       /* 2k bytes per set */
++} E3_CacheTags;
++
++typedef union E3_IProcStatus_Reg
++{
++    E3_uint32 Status;
++    struct
++    {
++#if defined(__LITTLE_ENDIAN__)
++      E3_uint32 TrapType:8;           /* iprocs trap ucode address */
++      E3_uint32 SuspendAddr:8;        /* iprocs suspend address */
++      E3_uint32 EopType:2;            /* Type of Eop Received */
++      E3_uint32 QueueingPacket:1;     /* receiving a queueing packet */
++      E3_uint32 AckSent:1;            /* a packet ack has been sent */
++      E3_uint32 Reject:1;             /* a packet nack has been sent */
++      E3_uint32 CrcStatus:2;          /* Crc Status value */
++      E3_uint32 BadLength:1;          /* Eop was received in a bad place */
++      E3_uint32 Chan1:1;              /* This packet received on v chan1 */
++      E3_uint32 First:1;              /* This is the first transaction in the packet */
++      E3_uint32 Last:1;               /* This is the last transaction in the packet */
++      E3_uint32 Unused:2;
++      E3_uint32 WakeupFunction:3;     /* iprocs wakeup function */
++#else
++      E3_uint32 WakeupFunction:3;     /* iprocs wakeup function */
++      E3_uint32 Unused:2;
++      E3_uint32 Last:1;               /* This is the last transaction in the packet */
++      E3_uint32 First:1;              /* This is the first transaction in the packet */
++      E3_uint32 Chan1:1;              /* This packet received on v chan1 */
++      E3_uint32 BadLength:1;          /* Eop was received in a bad place */
++      E3_uint32 CrcStatus:2;          /* Crc Status value */
++      E3_uint32 Reject:1;             /* a packet nack has been sent */
++      E3_uint32 AckSent:1;            /* a packet ack has been sent */
++      E3_uint32 QueueingPacket:1;     /* receiving a queueing packet */
++      E3_uint32 EopType:2;            /* Type of Eop Received */
++      E3_uint32 SuspendAddr:8;        /* iprocs suspend address */
++      E3_uint32 TrapType:8;           /* iprocs trap ucode address */
++#endif
++    } s;
++} E3_IProcStatus_Reg;
++
++#define CRC_STATUS_GOOD    (0 << 21)
++#define CRC_STATUS_DISCARD (1 << 21)
++#define CRC_STATUS_ERROR   (2 << 21)
++#define CRC_STATUS_BAD     (3 << 21)
++
++#define CRC_MASK         (3 << 21)
++
++#define EOP_GOOD         (1 << 16)
++#define EOP_BADACK       (2 << 16)
++#define EOP_ERROR_RESET          (3 << 16)
++
++#define E3_IPS_LastTrans      (1 << 26)
++#define E3_IPS_FirstTrans     (1 << 25)
++#define E3_IPS_VChan1         (1 << 24)
++#define E3_IPS_BadLength      (1 << 23)
++#define E3_IPS_CrcMask                (3 << 21)
++#define E3_IPS_Rejected               (1 << 20)
++#define E3_IPS_AckSent                (1 << 19)
++#define E3_IPS_QueueingPacket (1 << 18)
++#define E3_IPS_EopType                (3 << 16)
++
++typedef union E3_Status_Reg
++{
++    E3_uint32 Status;
++    struct
++    {
++#if defined(__LITTLE_ENDIAN__)
++      E3_uint32 TrapType:8;           /* procs trap ucode address */
++      E3_uint32 SuspendAddr:8;        /* procs suspend address */
++      E3_uint32 Context:13;           /* procs current context */
++      E3_uint32 WakeupFunction:3;     /* procs wakeup function */
++#else
++      E3_uint32 WakeupFunction:3;     /* procs wakeup function */
++      E3_uint32 Context:13;           /* procs current context */
++      E3_uint32 SuspendAddr:8;        /* procs suspend address */
++      E3_uint32 TrapType:8;           /* procs trap ucode address */
++#endif
++    } s;
++} E3_Status_Reg;
++
++/* values for WakeupFunction */
++#define SleepOneTick                  0
++#define WakeupToSendTransOrEop                1
++#define SleepOneTickThenRunnable      2
++#define WakeupNever                   4
++/* extra dma wakeup functions */
++#define WakupeToSendTransOrEop                1
++#define WakeupForPacketAck            3
++#define WakeupToSendTrans             5
++/* extra thread wakup function */
++#define WakeupStopped                 3
++/* extra cproc wakup function */
++#define WakeupSetEvent                        3
++
++#define GET_STATUS_CONTEXT(Ptr)      ((Ptr.Status >> 16) & 0x1fff)
++#define GET_STATUS_SUSPEND_ADDR(Ptr) ((Ptr.Status >> 8) & 0xff)
++#define GET_STATUS_TRAPTYPE(Ptr)     ((E3_uint32)(Ptr.Status & 0xff))
++
++/*
++ * Interrupt register bits
++ */
++#define INT_PciMemErr                 (1<<15) /* Pci memory access error */
++#define INT_SDRamInt                  (1<<14) /* SDRam ECC interrupt */
++#define INT_EventInterrupt            (1<<13) /* Event Interrupt */
++#define INT_LinkError                 (1<<12) /* Link Error */
++#define INT_ComQueue                  (1<<11) /* a comm queue half full */
++#define INT_TProcHalted                       (1<<10) /* Tproc Halted */
++#define INT_DProcHalted                       (1<<9) /* Dmas Halted */
++#define INT_DiscardingNonSysCntx      (1<<8) /* Inputters Discarding Non-SysCntx */
++#define INT_DiscardingSysCntx         (1<<7) /* Inputters Discarding SysCntx */
++#define INT_TProc                     (1<<6) /* tproc interrupt */
++#define INT_CProc                     (1<<5) /* cproc interrupt */
++#define INT_DProc                     (1<<4) /* dproc interrupt */
++#define INT_IProcCh1NonSysCntx                (1<<3) /* iproc non-SysCntx interrupt */
++#define INT_IProcCh1SysCntx           (1<<2) /* iproc SysCntx interrupt */
++#define INT_IProcCh0NonSysCntx                (1<<1) /* iproc non-SysCntx interrupt */
++#define INT_IProcCh0SysCntx           (1<<0) /* iproc SysCntx interrupt */
++
++#define INT_Inputters         (INT_IProcCh0SysCntx | INT_IProcCh0NonSysCntx | INT_IProcCh1SysCntx | INT_IProcCh1NonSysCntx)
++#define INT_Discarding        (INT_DiscardingSysCntx | INT_DiscardingNonSysCntx)
++#define INT_Halted            (INT_DProcHalted | INT_TProcHalted)
++#define INT_ErrorInterrupts   (INT_PciMemErr | INT_SDRamInt | INT_LinkError)
++
++/*
++ * Link state bits.
++ */
++#define LS_LinkNotReady       (1 << 0) /* Link is in reset or recovering from an error */
++#define LS_Locked     (1 << 1) /* Linkinput PLL is locked */
++#define LS_LockError  (1 << 2) /* Linkinput PLL was unable to lock onto the input clock. */
++#define LS_DeskewError        (1 << 3) /* Linkinput was unable to Deskew all the inputs. (Broken wire?) */
++#define LS_PhaseError (1 << 4) /* Linkinput Phase alignment error. */
++#define LS_DataError  (1 << 5) /* Received value was neither good data or a token. */
++#define LS_FifoOvFlow0        (1 << 6) /* Channel 0 input fifo overflowed. */
++#define LS_FifoOvFlow1        (1 << 7) /* Channel 1 input fifo overflowed. */
++
++/*
++ * Link State Constant defines, used for writing to LinkSetValue
++ */
++
++#define LRS_DataDel0          0x0
++#define LRS_DataDel1          0x1
++#define LRS_DataDel2          0x2
++#define LRS_DataDel3          0x3
++#define LRS_DataDel4          0x4
++#define LRS_DataDel5          0x5
++#define LRS_DataDel6          0x6
++#define LRS_DataDel7          0x7
++#define LRS_DataDel8          0x8
++#define LRS_PllDelValue               0x9
++#define LRS_ClockEven         0xA
++#define LRS_ClockOdd          0xB
++#define LRS_ErrorLSW          0xC
++#define LRS_ErrorMSW          0xD
++#define LRS_FinCoarseDeskew   0xE
++#define LRS_LinkInValue               0xF
++#define LRS_NumLinkDels         0x10
++
++#define LRS_Pllfast             0x40
++ 
++union Sched_Status
++{
++    E3_uint32 Status;
++    struct
++    {
++#if defined(__LITTLE_ENDIAN__)
++      E3_uint32 StopNonSysCntxs:1;
++      E3_uint32 FlushCommandQueues:1;
++      E3_uint32 HaltDmas:1;
++      E3_uint32 HaltDmaDequeue:1;
++      E3_uint32 HaltThread:1;
++      E3_uint32 CProcStop:1;
++      E3_uint32 DiscardSysCntxIn:1;
++      E3_uint32 DiscardNonSysCntxIn:1;
++      E3_uint32 RestartCh0SysCntx:1;
++      E3_uint32 RestartCh0NonSysCntx:1;
++      E3_uint32 RestartCh1SysCntx:1;
++      E3_uint32 RestartCh1NonSysCntx:1;
++      E3_uint32 RestartDProc:1;
++      E3_uint32 RestartTProc:1;
++      E3_uint32 RestartCProc:1;
++      E3_uint32 ClearLinkErrorInt:1;
++      E3_uint32 :3;
++      E3_uint32 LinkSetValue:10; 
++      E3_uint32 FixLinkDelays:1;
++      E3_uint32 LinkBoundaryScan:1;
++#else
++      E3_uint32 LinkBoundaryScan:1;
++      E3_uint32 FixLinkDelays:1;
++      E3_uint32 LinkSetValue:10; 
++      E3_uint32 :3;
++      E3_uint32 ClearLinkErrorInt:1;
++      E3_uint32 RestartCProc:1;
++      E3_uint32 RestartTProc:1;
++      E3_uint32 RestartDProc:1;
++      E3_uint32 RestartCh1NonSysCntx:1;
++      E3_uint32 RestartCh1SysCntx:1;
++      E3_uint32 RestartCh0NonSysCntx:1;
++      E3_uint32 RestartCh0SysCntx:1;
++      E3_uint32 DiscardNonSysCntxIn:1;
++      E3_uint32 DiscardSysCntxIn:1;
++      E3_uint32 CProcStop:1;
++      E3_uint32 HaltThread:1;
++      E3_uint32 HaltDmaDequeue:1;
++      E3_uint32 HaltDmas:1;
++      E3_uint32 FlushCommandQueues:1;
++      E3_uint32 StopNonSysCntxs:1;
++#endif
++    } s;
++};
++
++#define LinkBoundaryScan      ((E3_uint32) 1<<31) /* Clears the link error interrupt */
++#define FixLinkDelays         ((E3_uint32) 1<<30) /* Clears the link error interrupt */
++#define LinkSetValue(Val, OldVal) ((E3_uint32) (((Val) & 0x3ff) << 20) | ((OldVal) & ((~0x3ff) << 20)))
++
++#define ClearLinkErrorInt     ((E3_uint32) 1<<16) /* Clears the link error interrupt */
++#define RestartCProc          ((E3_uint32) 1<<15) /* Clears command proc interrupt */
++#define RestartTProc          ((E3_uint32) 1<<14) /* Clears thread interrupt */
++#define RestartDProc          ((E3_uint32) 1<<13) /* Clears dma0 interrupt */
++#define RestartCh1NonSysCntx  ((E3_uint32) 1<<12) /* Clears interrupt */
++#define RestartCh1SysCntx     ((E3_uint32) 1<<11) /* Clears interrupt */
++#define RestartCh0NonSysCntx  ((E3_uint32) 1<<10) /* Clears interrupt */
++#define RestartCh0SysCntx     ((E3_uint32) 1<<9) /* Clears interrupt */
++#define CProcStopped          ((E3_uint32) 1<<9) /* Read value only */
++
++#define TraceSetEvents                ((E3_uint32) 1<<8)
++#define DiscardNonSysCntxIn   ((E3_uint32) 1<<7)
++#define DiscardSysCntxIn      ((E3_uint32) 1<<6)
++#define CProcStop             ((E3_uint32) 1<<5) /* Will empty all the command port queues. */
++#define HaltThread            ((E3_uint32) 1<<4) /* Will stop the thread proc and clear the tproc command queue */
++#define HaltDmaDequeue                ((E3_uint32) 1<<3) /* Will stop the dmaers starting new dma's. */
++#define HaltDmas              ((E3_uint32) 1<<2) /* Will stop the dmaers and clear the dma command queues */
++#define FlushCommandQueues    ((E3_uint32) 1<<1) /* Causes the command ports to be flushed. */
++#define StopNonSysCntxs               ((E3_uint32) 1<<0) /* Prevents a non-SysCntx from starting. */
++
++/* Initial value of schedule status register */
++#define LinkResetToken                0x00F
++
++#define Sched_Initial_Value   (LinkBoundaryScan | (LinkResetToken << 20) | \
++                               DiscardSysCntxIn | DiscardNonSysCntxIn | HaltThread | HaltDmas)
++
++#define StopDmaQueues      (HaltDmaDequeue | HaltDmas | \
++                            DiscardNonSysCntxIn | DiscardSysCntxIn)
++#define CheckDmaQueueStopped (INT_DiscardingNonSysCntx | INT_DiscardingSysCntx | INT_DProcHalted)
++
++#define HaltStopAndExtTestMask        0xfff001ff
++#define HaltAndStopMask               0x000001ff
++
++
++#define DmaComQueueNotEmpty   (1<<0)
++#define ThreadComQueueNotEmpty        (1<<1)
++#define EventComQueueNotEmpty (1<<2)
++#define DmaComQueueHalfFull   (1<<3)
++#define ThreadComQueueHalfFull        (1<<4)
++#define EventComQueueHalfFull (1<<5)
++#define DmaComQueueError      (1<<6)
++#define ThreadComQueueError   (1<<7)
++#define EventComQueueError    (1<<8)
++
++#define ComQueueNotEmpty      (DmaComQueueNotEmpty | ThreadComQueueNotEmpty | EventComQueueNotEmpty)
++#define ComQueueError         (DmaComQueueError | ThreadComQueueError | EventComQueueError)
++
++typedef union _E3_DmaInfo
++{
++    E3_uint32  Value;
++    struct
++    {
++#if defined(__LITTLE_ENDIAN__)
++      E3_uint32 DmaOutputOpen:1;      /* The packet is currently open */
++      E3_uint32 :7;
++      E3_uint32 TimeSliceCount:2;     /* Time left to timeslice */
++      E3_uint32 UseRemotePriv:1;      /* Set for remote read dmas */
++      E3_uint32 DmaLastPacket:1;      /* Set for the last packet of a dma */
++      E3_uint32 PacketAckValue:2;     /* Packet ack type. Valid if AckBufferValid set. */
++      E3_uint32 PacketTimeout:1;      /* Packet timeout. Sent an EopError. Valid if AckBufferValid set. */
++      E3_uint32 AckBufferValid:1;     /* Packet ack is valid. */
++      E3_uint32 :16;                  /* read as Zero */
++#else
++      E3_uint32 :16;                  /* read as Zero */
++      E3_uint32 AckBufferValid:1;     /* Packet ack is valid. */
++      E3_uint32 PacketTimeout:1;      /* Packet timeout. Sent an EopError. Valid if AckBufferValid set. */
++      E3_uint32 PacketAckValue:2;     /* Packet ack type. Valid if AckBufferValid set. */
++      E3_uint32 DmaLastPacket:1;      /* Set for the last packet of a dma */
++      E3_uint32 UseRemotePriv:1;      /* Set for remote read dmas */
++      E3_uint32 TimeSliceCount:2;     /* Time left to timeslice */
++      E3_uint32 :7;
++      E3_uint32 DmaOutputOpen:1;      /* The packet is currently open */
++#endif
++    } s;
++} E3_DmaInfo;
++
++typedef volatile struct _E3_DmaRds
++{
++   E3_uint32          DMA_Source4to0AndTwoReads;
++   E3_uint32          pad13;
++   E3_uint32          DMA_BytesToRead;
++   E3_uint32          pad14;
++   E3_uint32          DMA_MinusPacketSize;
++   E3_uint32          pad15;
++   E3_uint32          DMA_MaxMinusPacketSize;
++   E3_uint32          pad16;
++   E3_uint32          DMA_DmaOutputOpen;
++   E3_uint32          pad16a;
++   E3_DmaInfo         DMA_PacketInfo;
++   E3_uint32          pad17[7];
++   E3_uint32          IProcTrapBase;
++   E3_uint32          pad18;
++   E3_uint32          IProcBlockTrapBase;
++   E3_uint32          pad19[11];
++} E3_DmaRds;
++   
++typedef volatile struct _E3_DmaWrs
++{
++   E3_uint64          pad0;
++   E3_uint64          LdAlignment;
++   E3_uint64          ResetAckNLdBytesToWr;
++   E3_uint64          SetAckNLdBytesToWr;
++   E3_uint64          LdBytesToRd;
++   E3_uint64          LdDmaType;
++   E3_uint64          SendRoutes;
++   E3_uint64          SendEop;
++   E3_uint64          pad1[8];
++} E3_DmaWrs;
++
++typedef volatile struct _E3_Exts
++{
++   E3_uint32          CurrContext;                            /* 0x12a00 */
++   E3_uint32          pad0;
++   E3_Status_Reg      DProcStatus;                            /* 0x12a08 */
++   E3_uint32          pad1;
++   E3_Status_Reg      CProcStatus;                            /* 0x12a10 */
++   E3_uint32          pad2;
++   E3_Status_Reg      TProcStatus;                            /* 0x12a18 */
++   E3_uint32          pad3;
++   E3_IProcStatus_Reg IProcStatus;                            /* 0x12a20 */
++   E3_uint32          pad4[3];
++
++   E3_uint32          IProcTypeContext;                       /* 0x12a30 */
++   E3_uint32          pad5;
++   E3_uint32          IProcTransAddr;                         /* 0x12a38 */
++   E3_uint32          pad6;
++   E3_uint32          IProcCurrTransData0;                    /* 0x12a40 */
++   E3_uint32          pad7;
++   E3_uint32          IProcCurrTransData1;                    /* 0x12a48 */
++   E3_uint32          pad8;
++
++   E3_uint32          SchCntReg;                              /* 0x12a50 */
++   E3_uint32          pad9;
++   E3_uint32          InterruptReg;                           /* 0x12a58 */
++   E3_uint32          pad10;
++   E3_uint32          InterruptMask;                          /* 0x12a60 */
++   E3_uint32          pad11;
++   E3_uint32          LinkErrorTypes;                         /* 0x12a68 */
++   E3_uint32          pad12[3];
++   E3_uint32          LinkState;      /* a read here returens the DataDel value for the */
++                                      /* link that has just been defined by a write to */
++                                      /* Regs.Exts.SchCntReg.LinkSetValue */
++   E3_uint32          pad13;
++
++   union                                                      /* 0x12a80 */
++   {
++      E3_DmaWrs               DmaWrs;
++      E3_DmaRds               DmaRds;
++   } Dmas;
++} E3_Exts;
++
++typedef union com_port_entry
++{
++    E3_uint64  type;
++    struct
++    {
++      E3_uint32 Address;              /* Command VAddr */
++#if defined(__LITTLE_ENDIAN__)
++      E3_uint32 Context0Issue:1;      /* Issue was for context 0 */
++      E3_uint32 EventNotCommand:1;    /* Issue address bit 3 */
++      E3_uint32 RemoteDesc:1;         /* Issue address bit 5 */
++      E3_uint32 :13;                  /* read as Zero */
++      E3_uint32 Context:12;           /* Command Context */
++      E3_uint32 :4;                   /* read as Zero */
++#else
++      E3_uint32 :4;                   /* read as Zero */
++      E3_uint32 Context:12;           /* Command Context */
++      E3_uint32 :13;                  /* read as Zero */
++      E3_uint32 RemoteDesc:1;         /* Issue address bit 5 */
++      E3_uint32 EventNotCommand:1;    /* Issue address bit 3 */
++      E3_uint32 Context0Issue:1;      /* Issue was for context 0 */
++#endif
++    } s;
++} E3_ComPortEntry;
++
++/* control reg bits */
++#define CONT_MMU_ENABLE               (1 << 0) /* bit 0 enables mmu */
++#define CONT_ENABLE_8K_PAGES  (1 << 1) /* When set smallest page is 8k instead of 4k. */
++#define CONT_EN_ALL_SETS      (1 << 2) /* enable cache */
++#define CONT_CACHE_LEVEL0     (1 << 3) /* cache context table */
++#define CONT_CACHE_LEVEL1     (1 << 4) /* cache up level 1 PTD/PTE */
++#define CONT_CACHE_LEVEL2     (1 << 5) /* cache up level 2 PTD/PTE */
++#define CONT_CACHE_LEVEL3     (1 << 6) /* cache up level 3 PTD/PTE */
++#define CONT_CACHE_TRAPS      (1 << 7) /* cache up traps */
++#define CONT_CACHE_LEV0_ROUTES        (1 << 8) /* cache up small routes */
++#define CONT_CACHE_LEV1_ROUTES        (1 << 9) /* cache up large routes */
++#define CONT_CACHE_ALL                (CONT_CACHE_LEVEL0 | CONT_CACHE_LEVEL1 | CONT_CACHE_LEVEL2 | \
++                               CONT_CACHE_LEVEL3 | CONT_CACHE_TRAPS | \
++                               CONT_CACHE_LEV0_ROUTES | CONT_CACHE_LEV1_ROUTES)
++
++#define CONT_SYNCHRONOUS      (1 << 10) /* PCI running sync */
++#define CONT_SER              (1 << 11) /* Single bit output (Elan1 SER bit) */
++#define CONT_SIR              (1 << 12) /* Writing 1 resets elan. */
++
++#define CONT_PSYCHO_MODE      (1 << 13) /* Enables all the perversion required by psycho */
++#define CONT_ENABLE_ECC               (1 << 14) /* Enables error detecting on the ECC */
++#define CONT_SDRAM_TESTING    (1 << 15) /* Switches to test mode for checking EEC data bits */
++
++/* defines SDRam CasLatency. Once set will not change again unless reset is reasserted. */
++/* 1 = Cas Latency is 3, 0 = Cas Latency is 2 */
++#define CAS_LATENCY_2         (0 << 16)
++#define CAS_LATENCY_3         (1 << 16)
++#define REFRESH_RATE_2US      (0 << 17) /* defines 2us SDRam Refresh rate. */
++#define REFRESH_RATE_4US      (1 << 17) /* defines 4us SDRam Refresh rate. */
++#define REFRESH_RATE_8US      (2 << 17) /* defines 8us SDRam Refresh rate. */
++#define REFRESH_RATE_16US     (3 << 17) /* defines 16us SDRam Refresh rate. */
++
++#define CONT_PCI_ERR          (1 << 19) /* Read 1 if PCI Error */
++#define CONT_CLEAR_PCI_ERROR  (1 << 19) /* Clears an PCI error. */
++
++/* Will cause the PCI error bit to become set. This is used to force the threads proc
++   and the uProc to start to stall. */
++#define CONT_SET_PCI_ERROR    (1 << 20)
++
++/* Writes SDram control reg when set. Also starts SDram memory system refreshing. */
++#define SETUP_SDRAM           (1 << 21)
++
++/* Flushes the tlb */
++#define MMU_FLUSH             (1 << 22)
++/* and read back when it's finished */
++#define MMU_FLUSHED           (1 << 0)
++
++/* Clears any ECC error detected by SDRam interface */
++#define CLEAR_SDRAM_ERROR     (1 << 23)
++
++#define ECC_ADDR_MASK         0x0ffffff8
++#define ECC_UE_MASK           0x1 
++#define ECC_CE_MASK           0x2
++#define ECC_ME_MASK           0x4 
++#define ECC_SYN_MASK          0xff
++
++/* define page table entry bit fields */
++#define TLB_PageSizeBits      (3 << 0)
++#define TLB_ACCBits           (7 << 2)
++#define TLB_LocalBit          (1 << 5)
++#define TLB_PCI64BitTargetBit (1 << 6)
++#define TLB_PCIBigEndianBit   (1 << 7)
++
++#define TLB_ModifiedBit               (1 << 55)
++#define TLB_ReferencedBit     (1 << 63)
++
++/* Used to read values from the tlb. */
++#define TLB_TlbReadCntBitsSh  56
++#define TLB_UseSelAddrSh      (1ULL << 60)
++#define TLB_WriteTlbLine      (1ULL << 61)
++
++#define TLB_SEL_LINE(LineNo) (TLB_UseSelAddrSh | \
++                            ((E3_uint64)((LineNo) & 0xf) << TLB_TlbReadCntBitsSh))
++
++typedef union _E3_CacheContReg
++{
++    E3_uint32 ContReg;
++    struct
++    {
++#if defined(__LITTLE_ENDIAN__)
++      E3_uint32 MMU_Enable:1;         /* wr 1 to enable the MMU */
++      E3_uint32 Set8kPages:1;         /* wr 1 smallest page is 8k. */
++      E3_uint32 EnableAllSets:1;      /* wr 1 All the cache sets are enabled */
++      E3_uint32 Cache_Level0:1;       /* wr 1 lev0 page tabs will be cached */
++      E3_uint32 Cache_Level1:1;       /* wr 1 lev1 page tabs will be cached */
++      E3_uint32 Cache_Level2:1;       /* wr 1 lev2 page tabs will be cached */
++      E3_uint32 Cache_Level3:1;       /* wr 1 lev3 page tabs will be cached */
++      E3_uint32 Cache_Traps:1;        /* wr 1 trap info will be cached */
++      E3_uint32 Cache_Lev0_Routes:1;  /* wr 1 small routes will be cached */
++      E3_uint32 Cache_Lev1_Routes:1;  /* wr 1 big routes will be cached */
++      E3_uint32 PCI_Synchronous:1;    /* Pci and sys clocks are running synchronously*/
++      E3_uint32 SER:1;                /* 1 bit output port */
++      E3_uint32 SIR:1;                /* write 1 will reset elan */
++      E3_uint32 PsychoMode:1;         /* Enables psycho perversion mode. */
++      E3_uint32 CasLatency:1;         /* 1=cas latency=3, 1=cas latency=2 */
++      E3_uint32 RefreshRate:2;        /* 0=2us, 1=4us, 2=8us, 3=16us */
++      E3_uint32 Pci_Err:1;            /* pci error. Write 1 clears err */
++      E3_uint32 Set_Pci_Error:1;      /* Will simulate an Pci error */
++      E3_uint32 StartSDRam:1;         /* Starts the sdram subsystem */
++      E3_uint32 FlushTlb:1;           /* Flush the contence of the tlb */
++      E3_uint32 :11;
++#else
++      E3_uint32 :11;
++      E3_uint32 FlushTlb:1;           /* Flush the contence of the tlb */
++      E3_uint32 StartSDRam:1;         /* Starts the sdram subsystem */
++      E3_uint32 Set_Pci_Error:1;      /* Will simulate an Pci error */
++      E3_uint32 Pci_Err:1;            /* pci error. Write 1 clears err */
++      E3_uint32 RefreshRate:2;        /* 0=2us, 1=4us, 2=8us, 3=16us */
++      E3_uint32 CasLatency:1;         /* 1=cas latency=3, 1=cas latency=2 */
++      E3_uint32 PsychoMode:1;         /* Enables psycho perversion mode. */
++      E3_uint32 SIR:1;                /* write 1 will reset elan */
++      E3_uint32 SER:1;                /* 1 bit output port */
++      E3_uint32 PCI_Synchronous:1;    /* Pci and sys clocks are running synchronously*/
++      E3_uint32 Cache_Lev1_Routes:1;  /* wr 1 big routes will be cached */
++      E3_uint32 Cache_Lev0_Routes:1;  /* wr 1 small routes will be cached */
++      E3_uint32 Cache_Traps:1;        /* wr 1 trap info will be cached */
++      E3_uint32 Cache_Level3:1;       /* wr 1 lev3 page tabs will be cached */
++      E3_uint32 Cache_Level2:1;       /* wr 1 lev2 page tabs will be cached */
++      E3_uint32 Cache_Level1:1;       /* wr 1 lev1 page tabs will be cached */
++      E3_uint32 Cache_Level0:1;       /* wr 1 lev0 page tabs will be cached */
++      E3_uint32 EnableAllSets:1;      /* wr 1 All the cache sets are enabled */
++      E3_uint32 Set8kPages:1;         /* wr 1 smallest page is 8k. */
++      E3_uint32 MMU_Enable:1;         /* wr 1 to enable the MMU */
++#endif
++    } s;
++} E3_CacheContReg;
++
++typedef union _E3_TrapBits
++{
++    volatile E3_uint32 Bits;
++    struct
++    {
++#if defined(__LITTLE_ENDIAN__)
++      E3_uint32 ForcedTProcTrap:1;     /* The theads proc has been halted */
++      E3_uint32 InstAccessException:1; /* An instruction access exception */
++      E3_uint32 Unimplemented:1;       /* Unimplemented instruction executed */
++      E3_uint32 DataAccessException:1; /* A data access exception */  
++
++      E3_uint32 ThreadTimeout:1;       /* The threads outputer has timed out */
++      E3_uint32 OpenException:1;       /* Invalid sequence of open, sendtr or close */
++      E3_uint32 OpenRouteFetch:1;      /* Fault while fetching routes for previous open*/
++      E3_uint32 TrapForTooManyInsts:1; /* Thread has been executing for too long */
++      
++      E3_uint32 PacketAckValue:2;      /* Packet ack type. Valid if AckBufferValid set. */
++      E3_uint32 PacketTimeout:1;       /* Packet timeout. Sent an EopError. Valid if AckBufferValid set. */
++
++      E3_uint32 AckBufferValid:1;      /* The PacketAckValue bits are valid */
++      E3_uint32 OutputWasOpen:1;       /* The output was open when tproc trapped */
++      E3_uint32 TProcDeschedule:2;     /* The reason the tproc stopped running. */
++      E3_uint32 :17;
++#else
++      E3_uint32 :17;
++      E3_uint32 TProcDeschedule:2;     /* The reason the tproc stopped running. */
++      E3_uint32 OutputWasOpen:1;       /* The output was open when tproc trapped */
++      E3_uint32 AckBufferValid:1;      /* The PacketAckValue bits are valid */
++      
++      E3_uint32 PacketTimeout:1;       /* Packet timeout. Sent an EopError. Valid if AckBufferValid set. */
++      E3_uint32 PacketAckValue:2;      /* Packet ack type. Valid if AckBufferValid set. */
++      
++      E3_uint32 TrapForTooManyInsts:1; /* Thread has been executing for too long */
++      E3_uint32 OpenRouteFetch:1;      /* Fault while fetching routes for previous open*/
++      E3_uint32 OpenException:1;       /* Invalid sequence of open, sendtr or close */
++      E3_uint32 ThreadTimeout:1;       /* The threads outputer has timed out */
++
++      E3_uint32 DataAccessException:1; /* A data access exception */
++      E3_uint32 Unimplemented:1;       /* Unimplemented instruction executed */
++      E3_uint32 InstAccessException:1; /* An instruction access exception */
++      E3_uint32 ForcedTProcTrap:1;     /* The theads proc has been halted */
++#endif
++    } s;
++} E3_TrapBits;
++
++typedef union _E3_DirtyBits
++{
++    volatile E3_uint32 Bits;
++    struct
++    {
++#if defined(__LITTLE_ENDIAN__)
++      E3_uint32 GlobalsDirty:8;
++      E3_uint32 OutsDirty:8;          /* will always read as dirty. */
++      E3_uint32 LocalsDirty:8;
++      E3_uint32 InsDirty:8;
++#else
++      E3_uint32 InsDirty:8;
++      E3_uint32 LocalsDirty:8;
++      E3_uint32 OutsDirty:8;          /* will always read as dirty. */
++      E3_uint32 GlobalsDirty:8;
++#endif
++    } s;
++} E3_DirtyBits;
++
++#define E3_TProcDescheduleMask    0x6000
++#define E3_TProcDescheduleWait    0x2000
++#define E3_TProcDescheduleSuspend 0x4000
++#define E3_TProcDescheduleBreak   0x6000
++
++#define E3_TrapBitsMask         0x7fff
++
++#define ThreadRestartFromTrapBit      1
++#define ThreadReloadAllRegs           2
++
++#define E3_PAckOk     0
++#define E3_PAckTestFail       1
++#define E3_PAckDiscard        2
++#define E3_PAckError  3
++
++typedef volatile struct _E3_DataBusMap
++{
++   E3_uint64           Dma_Alignment_Port[8];         /* 0x00002800 */
++   E3_uint32           pad0[0x30];                    /* 0x00002840 */
++
++   E3_uint32           Input_Trans0_Data[0x10];       /* 0x00002900 */
++   E3_uint32           Input_Trans1_Data[0x10];
++   E3_uint32           Input_Trans2_Data[0x10];
++   E3_uint32           Input_Trans3_Data[0x10];
++
++/* this is the start of the exts directly addressable from the ucode. */
++   E3_Exts             Exts;                          /* 0x00002a00 */
++
++/* this is the start of the registers directly addressable from the ucode. */
++   E3_DMA              Dma_Desc;                      /* 0x00002b00 */
++
++   E3_uint32           Dma_Last_Packet_Size;          /* 0x00002b20 */
++   E3_uint32           Dma_This_Packet_Size;          /* 0x00002b24 */
++   E3_uint32           Dma_Tmp_Source;                /* 0x00002b28 */
++   E3_uint32           Dma_Tmp_Dest;                  /* 0x00002b2c */
++
++   E3_Addr             Thread_SP_Save_Ptr;    /* points to the thread desched save word. */
++   E3_uint32           Dma_Desc_Size_InProg;          /* 0x00002b34 */
++
++   E3_uint32           Thread_Desc_SP;                /* 0x00002b38 */
++   E3_uint32           Thread_Desc_Context;           /* 0x00002b3c */
++
++   E3_uint32           uCode_TMP[0x10];               /* 0x00002b40 */
++
++   E3_uint32           TProc_NonSysCntx_FPtr;         /* 0x00002b80 */
++   E3_uint32           TProc_NonSysCntx_BPtr;         /* 0x00002b84 */
++   E3_uint32           TProc_SysCntx_FPtr;            /* 0x00002b88 */
++   E3_uint32           TProc_SysCntx_BPtr;            /* 0x00002b8c */
++   E3_uint32           DProc_NonSysCntx_FPtr;         /* 0x00002b90 */
++   E3_uint32           DProc_NonSysCntx_BPtr;         /* 0x00002b94 */
++   E3_uint32           DProc_SysCntx_FPtr;            /* 0x00002b98 */
++   E3_uint32           DProc_SysCntx_BPtr;            /* 0x00002b9c */
++
++   E3_uint32           Input_Trap_Base;               /* 0x00002ba0 */
++   E3_uint32           Input_Queue_Offset;            /* 0x00002ba4 */
++   E3_uint32           CProc_TrapSave_Addr;           /* 0x00002ba8 */
++   E3_uint32           Input_Queue_Addr;              /* 0x00002bac */
++   E3_uint32           uCode_TMP10;                   /* 0x00002bb0 */
++   E3_uint32           uCode_TMP11;                   /* 0x00002bb4 */
++   E3_uint32           Event_Trace_Ptr;               /* 0x00002bb8 */
++   E3_uint32           Event_Trace_Mask;              /* 0x00002bbc */
++
++   E3_ComPortEntry     DmaComQueue[3];                /* 0x00002bc0 */
++
++   E3_uint32           Event_Int_Queue_FPtr;          /* 0x00002bd8 */
++   E3_uint32           Event_Int_Queue_BPtr;          /* 0x00002bdc */
++
++   E3_ComPortEntry     ThreadComQueue[2];             /* 0x00002be0 */
++   E3_ComPortEntry     SetEventComQueue[2];           /* 0x00002bf0 */
++
++   E3_uint32           pad1[96];                      /* 0x00002c00 */
++   E3_uint32           ComQueueStatus;                /* 0x00002d80 */
++   E3_uint32           pad2[31];                      /* 0x00002d84 */
++
++/* These are the internal registers of the threads proc. */
++   E3_uint32           Globals[8];                    /* 0x00002e00 */
++   E3_uint32           Outs[8];
++   E3_uint32           Locals[8];
++   E3_uint32           Ins[8];
++
++   E3_uint32           pad3[16];
++
++   E3_uint32           IBufferReg[4];
++
++   E3_uint32           ExecuteNPC;
++   E3_uint32           ExecutePC;
++
++   E3_uint32           StartPC;
++   E3_uint32           pad4;
++
++   E3_uint32           StartnPC;
++   E3_uint32           pad5;
++
++   E3_TrapBits                 TrapBits;
++   E3_DirtyBits                DirtyBits;
++   E3_uint64           LoadDataReg;
++   E3_uint64           StoreDataReg;
++
++   E3_uint32           ECC_STATUS0;
++   E3_uint32           ECC_STATUS1;
++   E3_uint32           pad6[0xe];
++
++/* Pci slave port regs */
++   E3_uint32           PciSlaveReadCache[0x10];
++
++   E3_uint32           Fault_Base_Ptr;
++   E3_uint32           pad7;
++   E3_uint32           Context_Ptr;
++   E3_uint32           pad8;
++   E3_uint32           Input_Context_Filter;      /* write only, No data */
++   E3_uint32           Input_Context_Fil_Flush;   /* write only, No data */
++   E3_CacheContReg     Cache_Control_Reg;
++   E3_uint32           pad9;
++
++   E3_uint64           Tlb_Line_Value;
++   
++   E3_uint32           Walk_Datareg1;
++   E3_uint32           Walk_VAddr_Tab_Base;
++   E3_uint32           Walk_Datareg;
++   E3_uint32           Walk_ContextReg;
++   E3_uint32           Walk_FaultAddr;
++   E3_uint32           Walk_EventAddr;
++
++/* outputers output cont ext registers. */
++   E3_uint64           Dma_Route_012345_Context;
++   E3_uint64           pad10;
++   E3_uint64           Dma_Route_01234567;
++   E3_uint64           Dma_Route_89ABCDEF;
++
++   E3_uint64           Thread_Route_012345_Context;
++   E3_uint64           pad11;
++   E3_uint64           Thread_Route_01234567;
++   E3_uint64           Thread_Route_89ABCDEF;
++} E3_DataBusMap;
++
++typedef volatile struct _E3_Regs
++{
++   E3_CacheSets                 Sets;                         /* 0x00000000 */
++   E3_CacheTags                 Tags;                         /* 0x00002000 */
++   E3_DataBusMap        Regs;                         /* 0x00002800 */
++   E3_uint32            pad1[0x400];
++   E3_User_Regs         URegs;
++} E3_Regs;
++
++#define MAX_TRAPPED_TRANS     16
++#define TRANS_DATA_WORDS      16
++#define TRANS_DATA_BYTES      64
++
++/*
++ * Event interrupt
++ */
++typedef volatile union _E3_EventInt
++{
++   E3_uint64   ForceAlign;
++   struct {
++       E3_uint32 IntCookie;
++       E3_uint32 EventContext;        /* Bits 16 to 28 */
++    } s;
++} E3_EventInt;
++
++#define GET_EVENT_CONTEXT(Ptr) ((Ptr->s.EventContext >> 16) & MAX_ROOT_CONTEXT_MASK)
++
++typedef volatile union _E3_ThreadQueue
++{
++   E3_uint64  ForceAlign;
++   struct
++   {
++       E3_Addr         Thread;
++#if defined(__LITTLE_ENDIAN__)
++       E3_uint32 :16;         /* Bits 0  to 15 */
++       E3_uint32 Context:13;  /* Bits 16 to 28 */
++       E3_uint32 :3;          /* Bits 29 to 31 */
++#else
++       E3_uint32 :3;          /* Bits 29 to 31 */
++       E3_uint32 Context:13;  /* Bits 16 to 28 */
++       E3_uint32 :16;         /* Bits 0  to 15 */
++#endif
++   } s;
++} E3_ThreadQueue;
++
++typedef volatile union _E3_FaultStatusReg
++{
++   E3_uint32 Status;
++   struct
++   {
++#if defined(__LITTLE_ENDIAN__)
++      E3_uint32 AccTypePerm:3;        /* Access permission. See below. Bits 0 to 2 */
++      E3_uint32 AccSize:4;    /* Access size. See below for different types. Bits 3 to 6 */
++      E3_uint32 WrAcc:1;      /* Access was a write. Bit 7 */
++      E3_uint32 NonAllocAcc:1;        /* Access was a cache non allocate type. Bit 8 */
++      E3_uint32 BlkDataType:2;        /* Data size used for endian flips. Bits 9 to 10 */
++      E3_uint32 RdLine:1;     /* Access was a dma read line. Bit 11 */
++      E3_uint32 RdMult:1;     /* Access was a dma read multiple. Bit 12 */
++      E3_uint32 Walking:1;    /* The fault occued when walking. Bit 13 */
++      E3_uint32 Level:2;      /* Page table level when the fault occued. Bits 14 to 15 */
++      E3_uint32 ProtFault:1;  /* A protection fault occured. Bit 16 */
++      E3_uint32 FaultPte:2;   /* Page table type when the fault occured. Bit 17 */
++      E3_uint32 AlignmentErr:1;       /* Address alignment did not match the access size. Bit 19 */
++      E3_uint32 VProcSizeErr:1;       /* VProc number is out of range. Bit 20 */
++      E3_uint32 WalkBadData:1;        /* Memory CRC error during a walk. Bit 21 */
++      E3_uint32 :10;          /* Bits 22 to 31 */
++#else
++      E3_uint32 :10;          /* Bits 22 to 31 */
++      E3_uint32 WalkBadData:1;        /* Memory CRC error during a walk. Bit 21 */
++      E3_uint32 VProcSizeErr:1;       /* VProc number is out of range. Bit 20 */
++      E3_uint32 AlignmentErr:1;       /* Address alignment did not match the access size. Bit 19 */
++      E3_uint32 FaultPte:2;   /* Page table type when the fault occured. Bit 17 */
++      E3_uint32 ProtFault:1;  /* A protection fault occured. Bit 16 */
++      E3_uint32 Level:2;      /* Page table level when the fault occued. Bits 14 to 15 */
++      E3_uint32 Walking:1;    /* The fault occued when walking. Bit 13 */
++      E3_uint32 RdMult:1;     /* Access was a dma read multiple. Bit 12 */
++      E3_uint32 RdLine:1;     /* Access was a dma read line. Bit 11 */
++      E3_uint32 BlkDataType:2;        /* Data size used for endian flips. Bits 9 to 10 */
++      E3_uint32 NonAllocAcc:1;        /* Access was a cache non allocate type. Bit 8 */
++      E3_uint32 WrAcc:1;      /* Access was a write. Bit 7 */
++      E3_uint32 AccSize:4;    /* Access size. See below for different types. Bits 3 to 6 */
++      E3_uint32 AccTypePerm:3;        /* Access permission. See below. Bits 0 to 2 */
++#endif
++   } s;
++} E3_FaultStatusReg;
++
++typedef union _E3_FaultSave
++{
++   E3_uint64           ForceAlign;
++   struct {
++      E3_FaultStatusReg        FSR;
++      volatile E3_uint32 FaultContext;
++      volatile E3_uint32 FaultAddress;
++      volatile E3_uint32 EventAddress;
++   } s;
++} E3_FaultSave;
++
++/* MMU fault status reg bit positions. */
++#define FSR_WritePermBit      0       /* 1=Write access perm, 0=Read access perm */
++#define FSR_RemotePermBit     1       /* 1=Remote access perm, 0=local access perm */
++#define FSR_EventPermBit      2       /* 1=Event access perm, 0=data access perm */
++#define FSR_Size0Bit          3
++#define FSR_Size1Bit          4
++#define FSR_Size2Bit          5
++#define FSR_Size3Bit          6
++#define FSR_WriteAccBit               7       /* 1=Write access, 0=Read access. */
++#define FSR_NonAllocBit               8       /* 1=Do not fill cache with this data */
++#define FSR_BlkDataTy0Bit     9
++#define FSR_BlkDataTy1Bit     10
++#define FSR_ReadLineBit               11
++#define FSR_ReadMultipleBit   12
++
++#define FSR_PermMask          (0xf << FSR_WritePermBit)
++#define FSR_SizeMask          (0xf << FSR_Size0Bit)
++#define FSR_AccTypeMask               (3 << FSR_WriteAccBit)
++#define FSR_BlkDataTyMask     (3 << FSR_BlkDataTy0Bit)
++#define FSR_PciAccTyMask      (3 << FSR_ReadLineBit)
++#define FSR_Walking           (0x1 << 13)
++#define FSR_Level_Mask                (0x3 << 14)
++#define FSR_ProtFault         (0x1 << 16)
++#define FSR_FaultPTEType      (0x2 << 17)
++#define FSR_AddrSizeError     (0x1 << 19)
++#define FSR_VProcSizeError    (0x1 << 20)
++#define FSR_WalkBadData               (0x1 << 21)
++
++#define FSR_PermRead          0
++#define FSR_PermWrite         1
++#define FSR_PermRemoteRead    2
++#define FSR_PermRemoteWrite   3
++#define FSR_PermEventRd               4
++#define FSR_PermEventWr               5
++#define FSR_PermRemoteEventRd 6
++#define FSR_PermRemoteEventWr 7
++
++/* AT size values for each access type */
++#define FSR_Word              (0x0 << FSR_Size0Bit)
++#define FSR_DWord             (0x1 << FSR_Size0Bit)
++#define FSR_QWord             (0x2 << FSR_Size0Bit)
++#define FSR_Block32           (0x3 << FSR_Size0Bit)
++#define FSR_ReservedBlock     (0x6 << FSR_Size0Bit)
++#define FSR_Block64           (0x7 << FSR_Size0Bit)
++#define FSR_GetCntxFilter     (0x8 << FSR_Size0Bit)
++#define FSR_QueueDWord                (0x9 << FSR_Size0Bit)
++#define FSR_RouteFetch                (0xa << FSR_Size0Bit)
++#define FSR_QueueBlock                (0xb << FSR_Size0Bit)
++#define FSR_Block32PartWrite  (0xe << FSR_Size0Bit)
++#define FSR_Block64PartWrite  (0xf << FSR_Size0Bit)
++
++#define FSR_AllocRead         (0 << FSR_WriteAccBit)
++#define FSR_AllocWrite                (1 << FSR_WriteAccBit)
++#define FSR_NonAllocRd                (2 << FSR_WriteAccBit)
++#define FSR_NonAllocWr                (3 << FSR_WriteAccBit)
++
++#define FSR_TypeByte          (0 << FSR_BlkDataTy0Bit)
++#define FSR_TypeHWord         (1 << FSR_BlkDataTy0Bit)
++#define FSR_TypeWord          (2 << FSR_BlkDataTy0Bit)
++#define FSR_TypeDWord         (3 << FSR_BlkDataTy0Bit)
++
++typedef union E3_TrTypeCntx
++{
++   E3_uint32 TypeContext;
++   struct
++   {
++#if defined(__LITTLE_ENDIAN__)
++      E3_uint32 Type:16;              /* Transaction type field */
++      E3_uint32 Context:13;           /* Transaction context */
++      E3_uint32 TypeCntxInvalid:1;    /* Bit  29 */
++      E3_uint32 StatusRegValid:1;     /* Bit  30 */
++      E3_uint32 LastTrappedTrans:1;   /* Bit  31 */
++#else
++      E3_uint32 LastTrappedTrans:1;   /* Bit  31 */
++      E3_uint32 StatusRegValid:1;     /* Bit  30 */
++      E3_uint32 TypeCntxInvalid:1;    /* Bit  29 */
++      E3_uint32 Context:13;           /* Transaction context */
++      E3_uint32 Type:16;              /* Transaction type field */
++#endif
++   } s;
++} E3_TrTypeCntx;
++
++#define GET_TRAP_TYPE(Ptr)    (Ptr.TypeContext & 0xfff)
++#define GET_TRAP_CONTEXT(Ptr) ((Ptr.TypeContext >> 16) & 0x1fff)
++
++/* Words have been swapped for big endian access when fetched with dword access from elan.*/
++typedef union _E3_IprocTrapHeader
++{
++   E3_uint64  forceAlign;
++
++   struct
++   {
++      E3_TrTypeCntx    TrTypeCntx;
++      E3_uint32                TrAddr;
++      E3_uint32                TrData0;
++      union
++      {
++       E3_IProcStatus_Reg u_IProcStatus;
++       E3_uint32          u_TrData1;
++      } ipsotd;
++   } s;
++} E3_IprocTrapHeader;
++
++#define IProcTrapStatus ipsotd.u_IProcStatus
++#define TrData1               ipsotd.u_TrData1
++
++typedef struct E3_IprocTrapData
++{
++   E3_uint32 TrData[TRANS_DATA_WORDS];
++} E3_IprocTrapData;
++
++/*
++ * 64 kbytes of elan local memory. Must be aligned on a 64k boundary
++ */
++#define E3_NonSysCntxQueueSize        0x400
++#define E3_SysCntxQueueSize   0x100
++
++typedef struct _E3_TrapAndQueue
++{
++   E3_DMA             NonSysCntxDmaQueue[E3_NonSysCntxQueueSize];                     /* 0x000000 */
++   E3_DMA             SysCntxDmaQueue[E3_SysCntxQueueSize];                           /* 0x008000 */
++   E3_EventInt                EventIntQueue[E3_NonSysCntxQueueSize];                          /* 0x00A000 */
++   E3_ThreadQueue     NonSysCntxThreadQueue[E3_NonSysCntxQueueSize];                  /* 0x00C000 */  
++   E3_ThreadQueue     SysCntxThreadQueue[E3_SysCntxQueueSize];                        /* 0x00E000 */
++   E3_FaultSave               IProcSysCntx;                                                   /* 0x00E800 */
++   E3_Addr            Thread_SP_Save;                                                 /* 0x00E810 */
++   E3_uint32          dummy0[3];                                                      /* 0x00E814 */
++   E3_FaultSave               ThreadProcData;                                                 /* 0x00E820 */
++   E3_FaultSave               ThreadProcInst;                                                 /* 0x00E830 */
++   E3_FaultSave               dummy1[2];                                                      /* 0x00E840 */  
++   E3_FaultSave               ThreadProcOpen;                                                 /* 0x00E860 */
++   E3_FaultSave               dummy2;                                                         /* 0x00E870 */
++   E3_FaultSave               IProcNonSysCntx;                                                /* 0x00E880 */
++   E3_FaultSave               DProc;                                                          /* 0x00E890 */
++   E3_FaultSave               CProc;                                                          /* 0x00E8A0 */
++   E3_FaultSave               TProc;                                                          /* 0x00E8B0 */
++   E3_FaultSave               DProcData0;                                                     /* 0x00E8C0 */
++   E3_FaultSave               DProcData1;                                                     /* 0x00E8D0 */
++   E3_FaultSave               DProcData2;                                                     /* 0x00E8E0 */
++   E3_FaultSave               DProcData3;                                                     /* 0x00E8F0 */
++   E3_uint32          dummy3[0xc0];                                                   /* 0x00E900 */
++   E3_IprocTrapHeader VCh0_C0_TrHead[MAX_TRAPPED_TRANS];
++   E3_IprocTrapHeader VCh0_NonC0_TrHead[MAX_TRAPPED_TRANS];
++   E3_IprocTrapHeader VCh1_C0_TrHead[MAX_TRAPPED_TRANS];
++   E3_IprocTrapHeader VCh1_NonC0_TrHead[MAX_TRAPPED_TRANS];
++   E3_IprocTrapData   VCh0_C0_TrData[MAX_TRAPPED_TRANS];
++   E3_IprocTrapData   VCh0_NonC0_TrData[MAX_TRAPPED_TRANS];
++   E3_IprocTrapData   VCh1_C0_TrData[MAX_TRAPPED_TRANS];
++   E3_IprocTrapData   VCh1_NonC0_TrData[MAX_TRAPPED_TRANS];
++   E3_uint64          DmaOverflowQueueSpace[0x1000];
++   E3_uint64          ThreadOverflowQueueSpace[0x800];
++   E3_uint64          EventOverflowQueueSpace[0x800];
++} E3_TrapAndQueue;
++
++
++typedef struct _E3_ContextControlBlock 
++{
++   E3_uint32  rootPTP;
++   E3_uint32  filter;
++   E3_uint32  VPT_ptr;
++   E3_uint32  VPT_mask;
++} E3_ContextControlBlock;
++
++#define E3_CCB_CNTX0          (0x20000000)
++#define E3_CCB_DISCARD_ALL    (0x40000000)
++#define E3_CCB_ACKOK_ALL      (0x80000000)
++#define E3_CCB_MASK           (0xc0000000)
++
++#define E3_NUM_CONTEXT_0      (0x20)
++
++/* Macros to manipulate event queue pointers */
++/*     generate index in EventIntQueue */
++#define E3_EVENT_INTQ_INDEX(fptr)     (((fptr) & 0x1fff) >> 3)
++/*     generate next fptr */
++#define E3_EVENT_INTQ_NEXT(fptr)      ((((fptr) + 8) & ~0x4000) | 0x2000)
++
++
++#endif /* notdef _ELAN3_ELANREGS_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/include/elan3/elansyscall.h
+===================================================================
+--- linux-2.6.5.orig/include/elan3/elansyscall.h       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan3/elansyscall.h    2005-05-11 12:10:12.591909320 -0400
+@@ -0,0 +1,124 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN3_ELANSYSCALL_H
++#define __ELAN3_ELANSYSCALL_H
++
++#ident "$Id: elansyscall.h,v 1.34 2004/06/07 13:50:06 mike Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/elansyscall.h,v $*/
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#ifndef _ASM
++
++typedef struct sys_word_item
++{
++    struct sys_word_item *Next;
++    E3_uint32           Value;
++} SYS_WORD_ITEM;
++
++typedef struct sys_block_item
++{
++    struct sys_block_item *Next;
++    E3_uint32           *Pointer;
++} SYS_BLOCK_ITEM;
++
++typedef struct sys_swap_space
++{
++    int                Magic;
++    void      *ItemListsHead[MAX_LISTS];
++    void       **ItemListsTailp[MAX_LISTS];
++} SYS_SWAP_SPACE;
++
++typedef struct sys_exception
++{
++    int                       Type;
++    int                       Proc;
++    u_long            Res;
++    u_long            Value;
++    E3_FaultSave_BE   FaultArea;
++    
++    union
++    {
++      DMA_TRAP        Dma;
++      THREAD_TRAP     Thread;
++      COMMAND_TRAP    Command;
++      INPUT_TRAP      Input;
++    }                 Union;
++} SYS_EXCEPTION;
++
++typedef struct sys_exception_space
++{
++    struct sys_exception_space *Next;
++    int                               Magic;
++    int                               Front;
++    int                               Back;
++    int                               Count;
++    int                               Overflow;
++    SYS_EXCEPTION             Exceptions[1];
++} SYS_EXCEPTION_SPACE;
++
++#ifdef __KERNEL__
++
++typedef struct sys_ctxt
++{
++    SYS_SWAP_SPACE      *Swap;
++    SYS_EXCEPTION_SPACE *Exceptions;
++    kmutex_t           Lock;
++
++    spinlock_t                 WaitLock;
++    kcondvar_t                 NetworkErrorWait;
++
++    int                        Armed;
++    int                        Backoff;
++    long               Time;
++
++    u_long             Flags;
++    int                  signal;
++
++    EVENT_COOKIE_TABLE  *Table;
++} SYS_CTXT;
++
++extern SYS_CTXT *sys_init (ELAN3_CTXT *ctxt);
++extern int       sys_waitevent (ELAN3_CTXT *ctxt, E3_Event *event);
++extern void      sys_addException (SYS_CTXT *sctx, int type, int proc, caddr_t ptr, int size, 
++                                 E3_FaultSave_BE *, u_long res, u_long value);
++extern int       sys_getException (SYS_CTXT *sctx, SYS_EXCEPTION *ex);
++
++/* returns -ve error or ELAN_CAP_OK or ELAN_CAP_RMS */
++/* use = ELAN_USER_ATTACH, ELAN_USER_P2P, ELAN_USER_BROADCAST */
++extern int  elan3_validate_cap (ELAN3_DEV *dev, ELAN_CAPABILITY *cap ,int use);
++
++#endif /* __KERNEL__ */
++
++#endif /* _ASM */
++
++/* values for "Flags" */
++#define ELAN3_SYS_FLAG_DMA_BADVP              1
++#define ELAN3_SYS_FLAG_THREAD_BADVP   2
++#define ELAN3_SYS_FLAG_DMAFAIL                4
++#define ELAN3_SYS_FLAG_NETERR         8
++
++#define SYS_SWAP_MAGIC                0xB23C52DF
++#define SYS_EXCEPTION_MAGIC   0xC34D63E0
++
++#define EXCEPTION_GLOBAL_STRING       "elan3_exceptions"
++#define EXCEPTION_ABORT_STRING  "elan3_abortstring"
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* __ELAN3_ELANSYSCALL_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/include/elan3/elanuregs.h
+===================================================================
+--- linux-2.6.5.orig/include/elan3/elanuregs.h 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan3/elanuregs.h      2005-05-11 12:10:12.591909320 -0400
+@@ -0,0 +1,295 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN3_ELANUREGS_H
++#define __ELAN3_ELANUREGS_H
++
++#ident "$Id: elanuregs.h,v 1.10 2003/09/24 13:57:24 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/elanuregs.h,v $*/
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++/*
++ * Statistic control reg values
++ * Each 4-bit nibble of the control word specifies what statistic
++ * is to be recorded in each of the 8 statistic counters
++ */
++
++/* Count reg 0 */
++#define STC_INPUT_TRANSACTIONS                0
++#define STP_DMA_EOP_WAIT_ACK          1
++#define STP_THREAD_RUNNING            2
++#define STP_UCODE_WAIT_MEM            3
++#define STC_CACHE_WRITE_BACKS         4
++#define STC_PCI_SLAVE_READS           5
++#define STC_REG0_UNUSED6              6
++#define STP_REG0_UNUSED7              7
++
++#define STATS_REG0_NAMES {            \
++        "STC_INPUT_TRANSACTIONS",     \
++        "STP_DMA_EOP_WAIT_ACK",       \
++        "STP_THREAD_RUNNING",         \
++        "STP_UCODE_WAIT_MEM",         \
++        "STC_CACHE_WRITE_BACKS",      \
++        "STC_PCI_SLAVE_READS",        \
++        "STC_REG0_UNUSED6",           \
++        "STP_REG0_UNUSED7"            \
++}
++
++/* Count reg 1 */
++#define STC_INPUT_WRITE_BLOCKS                (0 << 4)
++#define STP_DMA_DATA_TRANSMITTING     (1 << 4)
++#define STP_THEAD_WAITING_INST                (2 << 4)
++#define STC_REG1_UNUSED3              (3 << 4)
++#define STP_FETCHING_ROUTES           (4 << 4)
++#define STC_REG1_UNUSED5              (5 << 4)
++#define STC_PCI_SLAVE_WRITES          (6 << 4)
++#define STP_PCI_SLAVE_READ_WAITING    (7 << 4)
++
++#define STATS_REG1_NAMES {            \
++      "STC_INPUT_WRITE_BLOCKS",               \
++        "STP_DMA_DATA_TRANSMITTING",  \
++        "STP_THEAD_WAITING_INST",     \
++        "STC_REG1_UNUSED3",           \
++        "STP_FETCHING_ROUTES",        \
++        "STC_REG1_UNUSED5",           \
++        "STC_PCI_SLAVE_WRITES",       \
++        "STP_PCI_SLAVE_READ_WAITING"  \
++}
++
++/* Count reg 2 */
++#define STC_INPUT_PKTS                        (0 << 8)
++#define STP_DMA_WAITING_MEM           (1 << 8)
++#define STP_THREAD_WAIT_OPEN_PKT      (2 << 8)
++#define STC_REG2_UNUSED3              (3 << 8)
++#define STC_ROUTE_FETCHES             (4 << 8)
++#define STC_CACHE_NON_ALLOC_MISSES    (5 << 8)
++#define STC_REG2_UNUSED6              (6 << 8)
++#define STP_PCI_SLAVE_WRITE_WAITING   (7 << 8)
++
++#define STATS_REG2_NAMES {            \
++      "STC_INPUT_PKTS",                       \
++        "STP_DMA_WAITING_MEM",        \
++        "STP_THREAD_WAIT_OPEN_PKT",   \
++        "STC_REG2_UNUSED3",           \
++        "STC_ROUTE_FETCHES",          \
++        "STC_CACHE_NON_ALLOC_MISSES", \
++        "STC_REG2_UNUSED6",           \
++        "STP_PCI_SLAVE_WRITE_WAITING" \
++}
++
++/* Count reg 3 */
++#define STC_INPUT_PKTS_REJECTED               (0 << 12)
++#define STP_DMA_WAIT_NETWORK_BUSY     (1 << 12)
++#define STP_THREAD_WAIT_PACK          (2 << 12)
++#define STP_UCODE_BLOCKED_UCODE               (3 << 12)
++#define STC_TLB_HITS                  (4 << 12)
++#define STC_REG3_UNUSED5              (5 << 12)
++#define STC_PCI_MASTER_READS          (6 << 12)
++#define STP_PCI_MASTER_WRITE_WAITING  (7 << 12)
++
++#define STATS_REG3_NAMES {            \
++      "STC_INPUT_PKTS_REJECTED",      \
++        "STP_DMA_WAIT_NETWORK_BUSY",  \
++        "STP_THREAD_WAIT_PACK",       \
++        "STP_UCODE_BLOCKED_UCODE",    \
++        "STC_TLB_HITS",               \
++        "STC_REG3_UNUSED5",           \
++        "STC_PCI_MASTER_READS",       \
++        "STP_PCI_MASTER_WRITE_WAITING"\
++}
++
++/* Count reg 4 */
++#define STP_INPUT_DATA_TRANSMITTING   (0 << 16)
++#define STC_DMA_NON_CTX0_PKTS         (1 << 16)
++#define STP_THREAD_EOP_WAIT_ACK               (2 << 16)
++#define STP_UCODE_DPROC_RUNNING               (3 << 16)
++#define STC_TLB_MEM_WALKS             (4 << 16)
++#define STC_REG4_UNUSED5              (5 << 16)
++#define STC_PCI_MASTER_WRITES         (6 << 16)
++#define STP_PCI_MASTER_READ_WAITING   (7 << 16)
++
++#define STATS_REG4_NAMES {            \
++      "STP_INPUT_DATA_TRANSMITTING",  \
++        "STC_DMA_NON_CTX0_PKTS",      \
++        "STP_THREAD_EOP_WAIT_ACK",    \
++        "STP_UCODE_DPROC_RUNNING",    \
++        "STC_TLB_MEM_WALKS",          \
++        "STC_REG4_UNUSED5",           \
++        "STC_PCI_MASTER_WRITES",      \
++        "STP_PCI_MASTER_READ_WAITING" \
++}
++
++/* Count reg 5 */
++#define STP_INPUT_WAITING_NETWORK_DATA        (0 << 20)
++#define STC_DMA_NON_CTX0_PKTS_REJECTED        (1 << 20)
++#define STP_THREAD_WAITING_DATA               (2 << 20)
++#define STP_UCODE_CPROC_RUNNING               (3 << 20)
++#define STP_THREAD_TRANSMITTING_DATA  (4 << 20)
++#define STP_PCI_WAITING_MAIN          (5 << 20)
++#define STC_REG5_UNUSED6              (6 << 20)
++#define STC_REG5_UNUSED7              (7 << 20)
++
++#define STATS_REG5_NAMES {                    \
++      "STP_INPUT_WAITING_NETWORK_DATA",               \
++        "STC_DMA_NON_CTX0_PKTS_REJECTED",     \
++        "STP_THREAD_WAITING_DATA",            \
++        "STP_UCODE_CPROC_RUNNING",            \
++        "STP_THREAD_TRANSMITTING_DATA",       \
++        "STP_PCI_WAITING_MAIN",               \
++        "STC_REG5_UNUSED6",                   \
++        "STC_REG5_UNUSED7"                    \
++}
++
++/* Count reg 6 */
++#define STP_INPUT_WAITING_MEMORY      (0 << 24)
++#define STC_DMA_CTX0_PKTS             (1 << 24)
++#define STP_THREAD_WAITING_MEMORY     (2 << 24)
++#define STP_UCODE_TPROC_RUNNING               (3 << 24)
++#define STC_CACHE_HITS                        (4 << 24)
++#define STP_PCI_WAITING_ELAN          (5 << 24)
++#define STC_REG6_UNUSED4              (6 << 24)
++#define STC_REG6_UNUSED7              (7 << 24)
++
++#define STATS_REG6_NAMES {            \
++      "STP_INPUT_WAITING_MEMORY",     \
++        "STC_DMA_CTX0_PKTS",          \
++        "STP_THREAD_WAITING_MEMORY",  \
++        "STP_UCODE_TPROC_RUNNING",    \
++        "STC_CACHE_HITS",             \
++        "STP_PCI_WAITING_ELAN",       \
++        "STC_REG6_UNUSED4",           \
++        "STC_REG6_UNUSED7"            \
++}
++
++/* Count reg 7 */
++#define STC_INPUT_CTX_FILTER_FILL     (0 << 28)       
++#define STC_DMA_CTX0_PKTS_REJECTED    (1 << 28)
++#define STP_THREAD_WAIT_NETWORK_BUSY  (2 << 28)
++#define STP_UCODE_IPROC_RUNNING               (3 << 28)
++#define STP_TLB_MEM_WALKING           (4 << 28)
++#define STC_CACHE_ALLOC_MISSES                (5 << 28)
++#define STP_PCI_DATA_TRANSFER         (6 << 28)
++#define STC_REG7_UNUSED7              (7 << 28)
++
++#define STATS_REG7_NAMES {            \
++      "STC_INPUT_CTX_FILTER_FILL",    \
++        "STC_DMA_CTX0_PKTS_REJECTED", \
++        "STP_THREAD_WAIT_NETWORK_BUSY",\
++        "STP_UCODE_IPROC_RUNNING",    \
++        "STP_TLB_MEM_WALKING",        \
++        "STC_CACHE_ALLOC_MISSES",     \
++        "STP_PCI_DATA_TRANSFER",      \
++        "STC_REG7_UNUSED7"            \
++}
++
++#define STATS_REG_NAMES { \
++    STATS_REG0_NAMES, \
++    STATS_REG1_NAMES, \
++    STATS_REG2_NAMES, \
++    STATS_REG3_NAMES, \
++    STATS_REG4_NAMES, \
++    STATS_REG5_NAMES, \
++    STATS_REG6_NAMES, \
++    STATS_REG7_NAMES, \
++}
++
++extern const char *elan3_stats_names[8][8];
++
++#define ELAN3_STATS_NAME(COUNT, CONTROL) (elan3_stats_names[(COUNT)][(CONTROL) & 7])
++
++typedef volatile union e3_StatsControl
++{
++   E3_uint32 StatsControl;
++   struct
++   {
++#if defined(__LITTLE_ENDIAN__)
++      E3_uint32 StatCont0:4;
++      E3_uint32 StatCont1:4;
++      E3_uint32 StatCont2:4;
++      E3_uint32 StatCont3:4;
++      E3_uint32 StatCont4:4;
++      E3_uint32 StatCont5:4;
++      E3_uint32 StatCont6:4;
++      E3_uint32 StatCont7:4;
++#else
++      E3_uint32 StatCont7:4;
++      E3_uint32 StatCont6:4;
++      E3_uint32 StatCont5:4;
++      E3_uint32 StatCont4:4;
++      E3_uint32 StatCont3:4;
++      E3_uint32 StatCont2:4;
++      E3_uint32 StatCont1:4;
++      E3_uint32 StatCont0:4;
++#endif
++   } s;
++} E3_StatsControl;
++
++typedef volatile union e3_StatsCount
++{
++   E3_uint64    ClockStat; 
++   struct
++   {
++       E3_uint32 ClockLSW;    /* read only */
++       E3_uint32 StatsCount;
++   } s;
++} E3_StatsCount;
++
++typedef volatile union e3_clock
++{
++   E3_uint64 NanoSecClock;
++   struct
++   {
++      E3_uint32 ClockLSW;
++      E3_uint32 ClockMSW;
++   } s;
++} E3_Clock;
++#define E3_TIME( X ) ((X).NanoSecClock)
++
++typedef volatile struct _E3_User_Regs
++{
++   E3_StatsCount      StatCounts[8];
++   E3_StatsCount      InstCount;
++   E3_uint32          pad0;
++   E3_StatsControl    StatCont;
++   E3_Clock           Clock;
++   E3_uint32          pad1[0x7ea];
++} E3_User_Regs;
++
++typedef volatile struct _E3_CommandPort 
++{
++   E3_Addr            PutDma;         /* 0x000 */
++   E3_uint32          Pad1;
++   E3_Addr            GetDma;         /* 0x008 */
++   E3_uint32          Pad2;
++   E3_Addr            RunThread;      /* 0x010 */
++   E3_uint32          Pad3[3];
++   E3_Addr            WaitEvent0;     /* 0x020 */
++   E3_uint32          Pad4;
++   E3_Addr            WaitEvent1;     /* 0x028 */
++   E3_uint32          Pad5;
++   E3_Addr            SetEvent;       /* 0x030 */
++   E3_uint32          Pad6[3];
++   E3_uint32          Pad7[0x7f0];    /* Fill out to an 8K page */
++} E3_CommandPort;
++/* Should have the new structures for the top four pages of the elan3 space */
++
++#define E3_COMMANDPORT_SIZE   (sizeof (E3_CommandPort))
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* __ELAN3_ELANUREGS_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/include/elan3/elanvp.h
+===================================================================
+--- linux-2.6.5.orig/include/elan3/elanvp.h    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan3/elanvp.h 2005-05-11 12:10:12.592909168 -0400
+@@ -0,0 +1,165 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef _ELAN3_ELANVP_H
++#define _ELAN3_ELANVP_H
++
++#ident "$Id: elanvp.h,v 1.45 2004/06/18 09:28:06 mike Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/elanvp.h,v $ */
++
++#include <elan3/e3types.h>
++#include <elan/bitmap.h>
++#include <elan/capability.h>
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++/*
++ * Context number allocation.
++ * [0-31]     system contexts
++ * [32-63]    hardware test
++ * [64-1023]  available
++ * [1024-2047]        RMS allocatable
++ * [2048-4095]        kernel comms data contexts
++ */
++#define ELAN3_KCOMM_CONTEXT_NUM               0x001                   /* old kernel comms context (system) */
++#define ELAN3_CM_CONTEXT_NUM          0x002                   /* new cluster member ship comms context (system) */
++#define ELAN3_MRF_CONTEXT_NUM         0x003                   /* multi-rail kernel comms context */
++#define ELAN3_DMARING_BASE_CONTEXT_NUM        0x010                   /* 16 contexts for dma ring issue (system) */
++#define ELAN3_DMARING_TOP_CONTEXT_NUM 0x01f
++
++#define ELAN3_HWTEST_BASE_CONTEXT_NUM 0x020                   /* reserved for hardware test */
++#define ELAN3_HWTEST_TOP_CONTEXT_NUM  0x03f
++
++#define ELAN3_KCOMM_BASE_CONTEXT_NUM  0x800                   /* kernel comms data transfer contexts */
++#define ELAN3_KCOMM_TOP_CONTEXT_NUM   0xfff
++
++#define ELAN3_HWTEST_CONTEXT(ctx)             ((ctx) >= ELAN3_HWTEST_BASE_CONTEXT_NUM && \
++                                       (ctx) <= ELAN3_HWTEST_TOP_CONTEXT_NUM)    
++
++#define ELAN3_SYSTEM_CONTEXT(ctx)     (((ctx) & SYS_CONTEXT_BIT) != 0 || \
++                                       (ctx) < E3_NUM_CONTEXT_0 || \
++                                       (ctx) >= ELAN3_KCOMM_BASE_CONTEXT_NUM)
++
++/* Maximum number of virtual processes */
++#define ELAN3_MAX_VPS         (16384)
++
++#define ELAN3_INVALID_PROCESS (0x7fffffff)            /* A GUARANTEED invalid process # */
++#define ELAN3_INVALID_NODE    (0xFFFF)
++#define ELAN3_INVALID_CONTEXT (0xFFFF)
++
++
++
++#if defined(__KERNEL__) && !defined(__ELAN3__)
++
++/*
++ * Contexts are accessible via Elan capabilities,
++ * for each context that can be "attached" to there
++ * is a ELAN3_CTXT_INFO structure created by its
++ * "owner".  This also "remembers" all remote 
++ * segments that have "blazed" a trail to it.
++ *
++ * If the "owner" goes away the soft info is 
++ * destroyed when it is no longer "attached" or 
++ * "referenced" by a remote segment.
++ *
++ * If the owner changes the capability, then 
++ * the soft info must be not "referenced" or 
++ * "attached" before a new process can "attach"
++ * to it.
++ */
++
++_NOTE(MUTEX_PROTECTS_DATA(elan3_dev::InfoLock,
++                        elan3_info::Next elan3_info::Prev elan3_info::Device elan3_info::Owner
++                        elan3_info::Capability elan3_info::AttachedCapability elan3_info::Context))
++_NOTE(MUTEX_PROTECTS_DATA(elan3_dev::IntrLock,
++                        elan3_info::Nacking elan3_info::Disabled))
++_NOTE(DATA_READABLE_WITHOUT_LOCK(elan3_info::Context elan3_info::Device elan3_info::Capability))
++
++#endif /* __KERNEL__ */
++
++#define LOW_ROUTE_PRIORITY    0
++#define HIGH_ROUTE_PRIORITY   1
++
++#define DEFAULT_ROUTE_TIMEOUT 3
++#define DEFAULT_ROUTE_PRIORITY        LOW_ROUTE_PRIORITY
++
++
++/* a small route is 4 flits (8 bytes), a big route  */
++/* is 8 flits (16 bytes) - each packed route is 4 bits */
++/* so giving us a maximum of 28 as flit0 does not contain */
++/* packed routes */
++#define MAX_FLITS             8
++#define MAX_PACKED            28
++
++/* bit definitions for 64 bit route pointer */
++#define ROUTE_VALID           (1ULL << 63)
++#define ROUTE_PTR             (1ULL << 62)
++#define ROUTE_CTXT_SHIFT      48
++#define ROUTE_PTR_MASK                ((1ull << ROUTE_CTXT_SHIFT)-1)
++#define ROUTE_GET_CTXT          ((VAL >> ROUTE_CTXT_SHIFT) & 0x3fff )
++
++#define SMALL_ROUTE(flits, context)   (((E3_uint64) (flits)[0] <<  0) | ((E3_uint64) (flits)[1] << 16) | \
++                                       ((E3_uint64) (flits)[2] << 32) | ((E3_uint64) (context) << ROUTE_CTXT_SHIFT) | \
++                                       ROUTE_VALID)
++
++#define BIG_ROUTE_PTR(paddr, context) ((E3_uint64) (paddr) | ((E3_uint64) context << ROUTE_CTXT_SHIFT) | ROUTE_VALID | ROUTE_PTR)
++
++#define BIG_ROUTE0(flits)             (((E3_uint64) (flits)[0] <<  0) | ((E3_uint64) (flits)[1] << 16) | \
++                                       ((E3_uint64) (flits)[2] << 32) | ((E3_uint64) (flits)[3] << 48))
++#define BIG_ROUTE1(flits)             (((E3_uint64) (flits)[4] <<  0) | ((E3_uint64) (flits)[5] << 16) | \
++                                       ((E3_uint64) (flits)[6] << 32) | ((E3_uint64) (flits)[7] << 48))
++
++
++/* defines for first flit of a route */
++#define FIRST_HIGH_PRI                (1 << 15)
++#define FIRST_AGE(Val)                ((Val) << 11)
++#define FIRST_TIMEOUT(Val)    ((Val) << 9)
++#define FIRST_PACKED(X)               ((X) << 7)
++#define FIRST_ROUTE(Val)      (Val)
++#define FIRST_ADAPTIVE                (0x30)
++#define FIRST_BCAST_TREE      (0x20)
++#define FIRST_MYLINK          (0x10)
++#define FIRST_BCAST(Top, Bot) (0x40 | ((Top) << 3) | (Bot))
++
++/* defines for 3 bit packed entries for subsequent flits */
++#define PACKED_ROUTE(Val)     (8 | (Val))
++#define PACKED_ADAPTIVE               (3)
++#define PACKED_BCAST_TREE     (2)
++#define PACKED_MYLINK         (1)
++#define PACKED_BCAST0(Top,Bot)        (4 | (Bot & 3))
++#define PACKED_BCAST1(Top,Bot)        ((Top << 1) | (Bot >> 2))
++
++/* ---------------------------------------------------------- 
++ * elan3_route functions 
++ * return ELAN3_ROUTE_xxx codes
++ * ---------------------------------------------------------- */
++
++#define ELAN3_ROUTE_SUCCESS        (0x00)
++#define ELAN3_ROUTE_SYSCALL_FAILED (0x01)
++#define ELAN3_ROUTE_INVALID        (0x02)
++#define ELAN3_ROUTE_TOO_LONG       (0x04)
++#define ELAN3_ROUTE_LOAD_FAILED    (0x08)
++#define ELAN3_ROUTE_PROC_RANGE     (0x0f)
++#define ELAN3_ROUTE_INVALID_LEVEL  (0x10)
++#define ELAN3_ROUTE_OCILATES       (0x20)
++#define ELAN3_ROUTE_WRONG_DEST     (0x40)
++#define ELAN3_ROUTE_TURN_LEVEL     (0x80)
++#define ELAN3_ROUTE_NODEID_UNKNOWN (0xf0)
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* _ELAN3_ELANVP_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/include/elan3/events.h
+===================================================================
+--- linux-2.6.5.orig/include/elan3/events.h    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan3/events.h 2005-05-11 12:10:12.592909168 -0400
+@@ -0,0 +1,183 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef _ELAN3_EVENTS_H
++#define _ELAN3_EVENTS_H
++
++#ident "$Id: events.h,v 1.45 2003/09/24 13:57:24 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/events.h,v $*/
++
++/*
++ * Alignments for events, event queues and blockcopy blocks.
++ */
++#define E3_EVENT_ALIGN                (8)
++#define E3_QUEUE_ALIGN                (32)
++#define E3_BLK_ALIGN          (64)
++#define E3_BLK_SIZE           (64)
++#define E3_BLK_PATTERN                (0xfeedface)
++
++#define E3_EVENT_FREE         ((0 << 4) | EV_WCOPY)
++#define E3_EVENT_PENDING      ((1 << 4) | EV_WCOPY)
++#define E3_EVENT_ACTIVE               ((2 << 4) | EV_WCOPY)
++#define E3_EVENT_FIRED                ((3 << 4) | EV_WCOPY)
++#define E3_EVENT_FAILED               ((4 << 4) | EV_WCOPY)
++#define E3_EVENT_DONE         ((5 << 4) | EV_WCOPY)
++#define E3_EVENT_PRIVATE      ((6 << 4) | EV_WCOPY)
++
++/*
++ * Event values and masks
++ *
++ * Block Copy event   xxxxxxxxxxxxxxxx1
++ * Chained event      30 bit ptr ....0x
++ * Event interrupt    29 bit cookie 01x
++ * Dma event          28 bit ptr   011x
++ * thread event               28 bit ptr   111x
++ */
++#define EV_CLEAR              (0x00000000)
++#define EV_TYPE_BCOPY         (0x00000001)
++#define EV_TYPE_CHAIN         (0x00000000)
++#define EV_TYPE_EVIRQ         (0x00000002)
++#define EV_TYPE_DMA           (0x00000006)
++#define EV_TYPE_THREAD                (0x0000000e)
++
++#define EV_TYPE_BCOPY_BYTE    (0)
++#define EV_TYPE_BCOPY_HWORD   (1)
++#define EV_TYPE_BCOPY_WORD    (2)
++#define EV_TYPE_BCOPY_DWORD   (3)
++
++/*
++ * Data type is in the lowest two bits of the Dest pointer.
++ */
++#define EV_BCOPY_DTYPE_MASK   (3)
++#define EV_WCOPY              (1)     /* [DestWord] = Source */
++#define EV_BCOPY              (0)     /* [DestBlock] = [SourceBlock] */
++
++#define EV_TYPE_MASK          (0x0000000e)
++#define EV_TYPE_MASK_BCOPY    (0x00000001)
++#define EV_TYPE_MASK_CHAIN    (0x00000002)
++#define EV_TYPE_MASK_EVIRQ    (0x00000006)
++#define EV_TYPE_MASK_DMA      (0x0000000e)
++#define EV_TYPE_MASK_THREAD   (0x0000000e)
++#define EV_TYPE_MASK2         (0x0000000f)
++
++/*
++ * Min/Max size for Elan queue entries 
++ */
++#define E3_QUEUE_MIN  E3_BLK_SIZE
++#define E3_QUEUE_MAX  (E3_BLK_SIZE * 5)
++
++/*
++ * Elan queue state bits
++ */
++#define E3_QUEUE_FULL (1<<0)
++#define E3_QUEUE_LOCKED       (1<<8)
++
++#ifndef _ASM
++
++typedef union _E3_Event
++{
++   E3_uint64  ev_Int64;
++   struct {
++      volatile E3_int32       u_Count;
++      E3_uint32               u_Type;
++   } ev_u;
++} E3_Event;
++
++typedef union _E3_BlockCopyEvent
++{
++   E3_uint64 ev_ForceAlign;
++   struct E3_BlockCopyEvent_u {
++      volatile E3_int32       u_Count;
++      E3_uint32               u_Type;
++      E3_Addr         u_Source;
++      E3_Addr         u_Dest;   /* lowest bits are the data type for endian conversion */
++   } ev_u;
++} E3_BlockCopyEvent;
++
++#define ev_Type   ev_u.u_Type
++#define ev_Count  ev_u.u_Count
++#define ev_Source ev_u.u_Source
++#define ev_Dest   ev_u.u_Dest
++
++typedef union _E3_WaitEvent0
++{
++   E3_uint64            we_ForceAlign;
++   struct {
++      E3_Addr         u_EventLoc;
++      E3_int32        u_WaitCount;
++   } we_u;
++} E3_WaitEvent0;
++#define we_EventLoc we_u.u_EventLoc
++#define we_WaitCount we_u.u_WaitCount
++
++typedef union _E3_Event_Blk
++{
++    E3_uint8  eb_Bytes[E3_BLK_SIZE];
++    E3_uint32 eb_Int32[E3_BLK_SIZE/sizeof (E3_uint32)];
++    E3_uint64 eb_Int64[E3_BLK_SIZE/sizeof (E3_uint64)];
++} E3_Event_Blk;
++
++/* We make eb_done the last word of the blk
++ * so that we can guarantee the rest of the blk is
++ * correct when this value is set.
++ * However, when the TPORT code copies the envelope
++ * info into the blk, it uses a dword endian type.
++ * Thus we must correct for this when initialising
++ * the pattern in the Elan SDRAM blk (eeb_done)
++ */
++#define eb_done eb_Int32[15]
++#define eeb_done eb_Int32[15^WordEndianFlip]
++
++#define EVENT_WORD_READY(WORD) (*((volatile E3_uint32 *) WORD) != 0)
++#define EVENT_BLK_READY(BLK) (((volatile E3_Event_Blk *) (BLK))->eb_done != 0)
++#define EVENT_READY(EVENT)   (((volatile E3_Event *) (EVENT))->ev_Count <= 0)
++
++#define ELAN3_WAIT_EVENT (0)
++#define ELAN3_POLL_EVENT (-1)
++
++#define SETUP_EVENT_TYPE(ptr,typeval) (((unsigned long)(ptr)) | (typeval))
++
++#define E3_RESET_BCOPY_BLOCK(BLK)                                                     \
++      do {                                                                            \
++              (BLK)->eb_done = 0;                                                     \
++      } while (0)
++
++typedef struct e3_queue
++{
++   volatile E3_uint32 q_state;        /* queue is full=bit0, queue is locked=bit8 */
++   volatile E3_Addr   q_bptr;         /* block aligned ptr to current back item */
++   E3_uint32          q_size;         /* size of queue item; 0x1 <= size <= (0x40 * 5) */
++   E3_Addr            q_top;          /* block aligned ptr to last queue item */
++   E3_Addr            q_base;         /* block aligned ptr to first queue item */
++   volatile E3_Addr   q_fptr;         /* block aligned ptr to current front item */
++   E3_Event           q_event;        /* queue event */
++} E3_Queue;
++
++typedef struct e3_blockcopy_queue
++{
++   volatile E3_uint32 q_state;        /* queue is full=bit0, queue is locked=bit8 */
++   volatile E3_Addr   q_bptr;         /* block aligned ptr to current back item */
++   E3_uint32          q_size;         /* size of queue item; 0x1 <= size <= (0x40 * 5) */
++   E3_Addr            q_top;          /* block aligned ptr to last queue item */
++   E3_Addr            q_base;         /* block aligned ptr to first queue item */
++   volatile E3_Addr   q_fptr;         /* block aligned ptr to current front item */
++   E3_BlockCopyEvent  q_event;        /* queue event */
++   E3_uint32          q_pad[6];
++} E3_BlockCopyQueue;
++
++#define E3_QUEUE_EVENT_OFFSET 24
++#define QUEUE_FULL(Q)         ((Q)->q_state & E3_QUEUE_FULL)          
++
++#endif /* ! _ASM */
++
++#endif /* _ELAN3_EVENTS_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/include/elan3/intrinsics.h
+===================================================================
+--- linux-2.6.5.orig/include/elan3/intrinsics.h        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan3/intrinsics.h     2005-05-11 12:10:12.593909016 -0400
+@@ -0,0 +1,320 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Limited.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef _ELAN3_INTRINSICS_H
++#define _ELAN3_INTRINSICS_H
++
++#ident "$Id: intrinsics.h,v 1.35 2003/09/24 13:57:24 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/intrinsics.h,v $ */
++
++#include <elan3/e3types.h>
++#include <elan3/events.h>
++
++/* 
++ * This file contains definitions of the macros for accessing the QSW
++ * specific instructions, as if they were functions.
++ * The results from the function 
++ */
++
++#define C_ACK_OK      0                       /* return from c_close() */
++#define C_ACK_TESTFAIL        1                       /* return from c_close() */
++#define C_ACK_DISCARD 2                       /* return from c_close() */
++#define C_ACK_ERROR   3                       /* return from c_close() */
++
++/*
++ * Elan asi's for tproc block accesses
++ */
++#define EASI_BYTE     0
++#define EASI_HALF     1
++#define EASI_WORD     2
++#define EASI_DOUBLE   3
++
++#if defined(__ELAN3__) && !defined (_ASM)
++
++extern inline void c_abort(void) 
++{
++    asm volatile (".word 0x0000               ! die you thread you " : : );
++}
++
++extern inline void c_suspend(void) 
++{
++    asm volatile (
++      "set 1f, %%i7                   ! RevB bug fix. get address of the wakeup inst\n"
++      "andcc %%i7,0x4,%%g0            ! RevB bug fix. check alignment\n"
++      "bne 1f                         ! RevB bug fix. jump to other alignment\n"
++      "nop                            ! RevB bug fix. delay slot\n"
++      "ldd [%%i7],%%i6                ! RevB bug fix. data fetch of instructions\n"
++      "suspend                        ! do the real suspend\n"
++      "1: add %%i7,5*4,%%i7           ! RevB bug fix. Point i7 to first ldblock\n"
++      "ldd [%%i7],%%i6                ! RevB bug fix. data fetch of instructions\n"
++      "suspend                        ! do the real suspend\n" : : );
++}
++
++extern inline int c_close(void) 
++{
++    register int rc asm("o0");
++
++    asm volatile ("close %0" : "=r" (rc) : );
++
++    return (rc);
++}
++
++extern inline int c_close_cookie(volatile E3_uint32 *cookiep, E3_uint32 next)
++{
++    register int rc asm("o0");
++
++    asm volatile ("close      %0              ! close the packet\n"
++                "bz,a         1f              ! ack received\n"
++                "st           %1, [%2]        ! update cookie on ack\n"
++                "1:                           ! label for not-ack\n"
++                : "=r" (rc) : "r" (next), "r" (cookiep));
++
++    return (rc);
++}
++
++extern inline void c_break_busywait(void)
++{
++    asm volatile (
++      "breaktest                      ! test to see if break necessary\n"
++      "bpos 1f                        ! no other thread ready\n"
++      "nop                            ! delay slot\n"
++      "sub     %%sp,3*8*4,%%sp        ! Space to save the registers\n"
++      "stblock %%g0,[%%sp+0]          ! save the globals\n"
++      "stblock %%i0,[%%sp+8*4]        ! save the ins\n"
++      "stblock %%l0,[%%sp+16*4]       ! save the locals\n"
++      "set 2f, %%i7                   ! RevB bug fix. get address of the wakeup inst\n"
++      "andcc %%i7,0x4,%%g0            ! RevB bug fix. check alignment\n"
++      "bne 3f                         ! RevB bug fix. jump to other alignment\n"
++      "nop                            ! RevB bug fix. delay slot\n"
++      "ldd [%%i7],%%i6                ! RevB bug fix. data fetch of instructions\n"
++      "break                          ! do the real break\n"
++      "2: b 4f                        ! RevB bug fix. Branch over other alignment case\n"
++      " ldblock [%%sp+16*4],%%l0      ! RevB bug fix. restore locals in delay slot\n"
++      "3: add %%i7,5*4,%%i7           ! RevB bug fix. Point i7 to first ldblock\n"
++      "ldd [%%i7],%%i6                ! RevB bug fix. data fetch of instructions\n"
++      "break                          ! do the real break\n"
++      "ldblock [%%sp+16*4],%%l0       ! restore locals\n"
++      "4: ldblock [%%sp+8*4], %%i0    ! restore ins\n"
++      "ldblock [%%sp+0],%%g0          ! restore globals\n"
++      "add     %%sp,3*8*4,%%sp        ! restore stack pointer\n"
++      "1: " : : );
++}
++
++extern inline void c_break(void)
++{
++    asm volatile (
++      "breaktest                      ! test to see if break necessary\n"
++      "bne 1f                         ! haven't exceeded our inst count yet\n"
++      "nop                            ! delay slot\n"
++      "sub     %%sp,3*8*4,%%sp        ! Space to save the registers\n"
++      "stblock %%g0,[%%sp+0]          ! save the globals\n"
++      "stblock %%i0,[%%sp+8*4]        ! save the ins\n"
++      "stblock %%l0,[%%sp+16*4]       ! save the locals\n"
++      "set 2f, %%i7                   ! RevB bug fix. get address of the wakeup inst\n"
++      "andcc %%i7,0x4,%%g0            ! RevB bug fix. check alignment\n"
++      "bne 3f                         ! RevB bug fix. jump to other alignment\n"
++      "nop                            ! RevB bug fix. delay slot\n"
++      "ldd [%%i7],%%i6                ! RevB bug fix. data fetch of instructions\n"
++      "break                          ! do the real break\n"
++      "2: b 4f                        ! RevB bug fix. Branch over other alignment case\n"
++      " ldblock [%%sp+16*4],%%l0      ! RevB bug fix. restore locals in delay slot\n"
++      "3: add %%i7,5*4,%%i7           ! RevB bug fix. Point i7 to first ldblock\n"
++      "ldd [%%i7],%%i6                ! RevB bug fix. data fetch of instructions\n"
++      "break                          ! do the real break\n"
++      "ldblock [%%sp+16*4],%%l0       ! restore locals\n"
++      "4: ldblock [%%sp+8*4], %%i0    ! restore ins\n"
++      "ldblock [%%sp+0],%%g0          ! restore globals\n"
++      "add     %%sp,3*8*4,%%sp        ! restore stack pointer\n"
++      "1: " : : );
++}
++
++extern inline void c_open( const int arg ) 
++{
++    asm volatile ("open %0" : : "r" (arg) );
++    asm volatile ("nop; nop; nop; nop");
++    asm volatile ("nop; nop; nop; nop");
++    asm volatile ("nop; nop; nop; nop");
++    asm volatile ("nop; nop; nop; nop");
++    asm volatile ("nop; nop; nop; nop");
++    asm volatile ("nop; nop; nop; nop");
++}
++
++extern inline void c_waitevent( volatile E3_Event *const ptr,
++                              const int count) 
++{
++    register volatile E3_Event *a_unlikely asm("o0") = ptr;
++    register int a_very_unlikely asm("o1") = count;
++
++    asm volatile (
++        "sub     %%sp,1*8*4,%%sp      ! Space to save the registers\n"
++        "stblock %%i0,[%%sp+0]                ! save the ins\n"
++      "set    2f, %%i7                ! RevB bug fix. get address of the wakeup inst\n"
++      "andcc %%i7,0x4,%%g0            ! RevB bug fix. check alignment\n"
++      "bne 3f                         ! RevB bug fix. jump to other alignment\n"
++      "nop                            ! RevB bug fix. delay slot\n"
++      "ldd [%%i7],%%i4                ! RevB bug fix. data fetch of instructions\n"
++        "waitevent                    ! do the business\n"
++      "2: b 4f                        ! RevB bug fix. Branch over other alignment case\n"
++        "  ldblock [%%sp+0],%%i0      ! RevB bug fix. restore ins in delay slot\n"
++      "3: add %%i7,5*4,%%i7           ! RevB bug fix. Point i7 to first ldblock\n"
++      "ldd [%%i7],%%i4                ! RevB bug fix. data fetch of instructions\n"
++        "waitevent                    ! do the business\n"
++        "ldblock [%%sp+0],%%i0                ! restore ins\n"
++        "4: add     %%sp,1*8*4,%%sp   ! restore stack pointer\n"
++        : /* no outputs */
++        : /* inputs */ "r" (a_unlikely), "r" (a_very_unlikely)
++        : /* clobbered */ "g0", "g1", "g2", "g3", "g4", "g5", "g6", "g7",
++                                "l0", "l1", "l2", "l3", "l4", "l5", "l6", "l7" );
++
++}
++
++#define c_sendtrans0(type,dest)                       \
++      asm volatile ("sendtrans %0, %%g0, %1" : : "i" (type), "r" (dest))
++
++#define c_sendtrans1(type,dest,arg)           \
++      asm volatile ("sendtrans %0, %2, %1" : : "i" (type), "r" (dest), "r" (arg))
++
++#define c_sendtrans2(type,dest,arg1,arg2)     \
++      do {                                    \
++           register const unsigned long a_unlikely_1 asm("o4") = arg1;                        \
++           register const unsigned long a_unlikely_2 asm("o5") = arg2;                        \
++           asm volatile ("sendtrans %0, %2, %1"                                       \
++               : : "i" (type), "r" (dest), "r" (a_unlikely_1), "r" (a_unlikely_2));   \
++      } while(0)
++
++#define c_sendmem(type,dest,ptr)              \
++      asm volatile ("sendtrans %0, [%2], %1" : : "i" (type), "r" (dest), "r" (ptr))
++
++/* Copy a single 64-byte block (src blk is read using a BYTE endian type) */
++extern inline void elan3_copy64b(void *src, void *dst)
++{
++    /* Copy 64 bytes using ldblock/stblock
++     * We save and restore the locals/ins because if we don't gcc
++     * really makes a bad job of optimisising the rest of the thread code!
++     *
++     * We force the parameters in g5, g6 so that they aren't
++     * trashed by the loadblk32 into the locals/ins
++     */
++    register void *tmp1 asm("g5") = src;
++    register void *tmp2 asm("g6") = dst;
++
++    asm volatile (
++      "and     %%sp,63,%%g7           ! Calculate stack alignment\n"
++      "sub     %%sp,2*8*4,%%sp        ! Space to save the registers\n"
++      "sub     %%sp,%%g7,%%sp         ! align stack\n" 
++      "stblock64 %%l0,[%%sp]          ! save the locals and ins\n"
++      "ldblock64a [%0]%2,%%l0         ! load 64-byte block into locals/ins\n"
++      "stblock64a %%l0,[%1]%2         ! store 64-byte block from local/ins\n"
++      "ldblock64 [%%sp],%%l0          ! restore locals and ins\n"
++      "add     %%sp,%%g7, %%sp        ! undo alignment\n"
++      "add     %%sp,2*8*4,%%sp        ! restore stack pointer\n"
++      : /* outputs */
++      : /* inputs */ "r" (tmp1), "r" (tmp2), "n" (EASI_BYTE)
++      : /* clobbered */ "g5", "g6", "g7" );
++}
++
++/* Copy a single 64-byte block (src blk is read using a WORD endian type) */
++extern inline void elan3_copy64w(void *src, void *dst)
++{
++    /* Copy 64 bytes using ldblock/stblock
++     * We save and restore the locals/ins because if we don't gcc
++     * really makes a bad job of optimisising the rest of the thread code!
++     *
++     * We force the parameters in g5, g6 so that they aren't
++     * trashed by the loadblk32 into the locals/ins
++     */
++    register void *tmp1 asm("g5") = src;
++    register void *tmp2 asm("g6") = dst;
++
++    asm volatile (
++      "and     %%sp,63,%%g7           ! Calculate stack alignment\n"
++      "sub     %%sp,2*8*4,%%sp        ! Space to save the registers\n"
++      "sub     %%sp,%%g7,%%sp         ! align stack\n" 
++      "stblock64 %%l0,[%%sp]          ! save the locals and ins\n"
++      "ldblock64a [%0]%2,%%l0         ! load 64-byte block into locals/ins\n"
++      "stblock64a %%l0,[%1]%2         ! store 64-byte block from local/ins\n"
++      "ldblock64 [%%sp],%%l0          ! restore locals and ins\n"
++      "add     %%sp,%%g7, %%sp        ! undo alignment\n"
++      "add     %%sp,2*8*4,%%sp        ! restore stack pointer\n"
++      : /* outputs */
++      : /* inputs */ "r" (tmp1), "r" (tmp2), "n" (EASI_WORD)
++      : /* clobbered */ "g5", "g6", "g7" );
++}
++
++/* Read a 64-bit value with a WORD (32-bit) endian type */
++extern inline E3_uint64 elan3_read64w( volatile E3_uint64 *const ptr )
++{
++    E3_uint64 result;
++
++    asm volatile (
++      "ldblock8a [%1]%2, %0\n"
++      : /* outputs */ "=r" (result)
++      : /* inputs */ "r" (ptr), "n" (EASI_WORD) );
++
++    return( result );
++}
++
++/* Read a 64-bit value with a DOUBLEWORD (64-bit) endian type */
++extern inline E3_uint64 elan3_read64dw( volatile E3_uint64 *const ptr )
++{
++    E3_uint64 result;
++
++    asm volatile (
++      "ldblock8a [%1]%2, %0\n"
++      : /* outputs */ "=r" (result)
++      : /* inputs */ "r" (ptr), "n" (EASI_DOUBLE) );
++
++    return( result );
++}
++
++/* Write a 32-bit value with a WORD (32-bit) endian type */
++extern inline void elan3_write64w( volatile E3_uint64 *const ptr, E3_uint64 value )
++{
++    asm volatile (
++      "stblock8a %1, [%0]%2\n"
++      : /* no outputs */
++      : /* inputs */ "r" (ptr), "r" (value), "n" (EASI_WORD) );
++}
++
++/* Write a 64-bit value with a DOUBLEWORD (64-bit) endian type */
++extern inline void elan3_write64dw( volatile E3_uint64 *const ptr, E3_uint64 value )
++{
++    asm volatile (
++      "stblock8a %1, [%0]%2\n"
++      : /* no outputs */
++      : /* inputs */ "r" (ptr), "r" (value), "n" (EASI_DOUBLE) );
++}
++
++extern inline E3_uint32 c_swap(volatile E3_uint32 *source, E3_uint32 result)
++{
++   asm volatile("swap [%1],%0\n"
++              : "=r" (result)
++              : "r" (source) ,"0" (result)
++              : "memory");
++   return result;
++}
++
++extern inline E3_uint32 c_swap_save(volatile E3_uint32 *source, const E3_uint32 result)
++{
++   register E3_uint32 a_unlikely;
++   asm volatile("" : "=r" (a_unlikely) : );
++
++   asm volatile("mov %2,%0; swap [%1],%0\n"
++              : "=r" (a_unlikely)
++              : "r" (source) ,"r" (result), "0" (a_unlikely)
++              : "memory");
++   return a_unlikely;
++}
++#endif /* (__ELAN3__) && !(_ASM) */
++
++#endif /* _ELAN3_INTRINSICS_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/include/elan3/minames.h
+===================================================================
+--- linux-2.6.5.orig/include/elan3/minames.h   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan3/minames.h        2005-05-11 12:10:12.594908864 -0400
+@@ -0,0 +1,256 @@
++{MI_WaitForRemoteDescRead,    "MI_WaitForRemoteDescRead"},
++{MI_WaitForRemoteDescRead2,   "MI_WaitForRemoteDescRead2"},
++{MI_WaitForRemoteDescRead2_seq1,      "MI_WaitForRemoteDescRead2_seq1"},
++{MI_SendRemoteDmaRoutes,      "MI_SendRemoteDmaRoutes"},
++{MI_IProcTrapped,     "MI_IProcTrapped"},
++{MI_DProcTrapped,     "MI_DProcTrapped"},
++{MI_CProcTrapped,     "MI_CProcTrapped"},
++{MI_TProcTrapped,     "MI_TProcTrapped"},
++{MI_TestWhichDmaQueue,        "MI_TestWhichDmaQueue"},
++{MI_TestWhichDmaQueue_seq1,   "MI_TestWhichDmaQueue_seq1"},
++{MI_InputRemoteDmaUpdateBPtr, "MI_InputRemoteDmaUpdateBPtr"},
++{MI_FixupQueueContextAndRemoteBit,    "MI_FixupQueueContextAndRemoteBit"},
++{MI_FixupQueueContextAndRemoteBit_seq1,       "MI_FixupQueueContextAndRemoteBit_seq1"},
++{MI_FixupQueueContextAndRemoteBit_seq2,       "MI_FixupQueueContextAndRemoteBit_seq2"},
++{MI_FixupQueueContextAndRemoteBit_seq3,       "MI_FixupQueueContextAndRemoteBit_seq3"},
++{MI_FixupQueueContextAndRemoteBit_seq4,       "MI_FixupQueueContextAndRemoteBit_seq4"},
++{MI_RunDmaCommand,    "MI_RunDmaCommand"},
++{MI_DoSendRemoteDmaDesc,      "MI_DoSendRemoteDmaDesc"},
++{MI_DequeueNonSysCntxDma,     "MI_DequeueNonSysCntxDma"},
++{MI_WaitForRemoteDescRead1,   "MI_WaitForRemoteDescRead1"},
++{MI_RemoteDmaCommand, "MI_RemoteDmaCommand"},
++{MI_WaitForRemoteRoutes,      "MI_WaitForRemoteRoutes"},
++{MI_DequeueSysCntxDma,        "MI_DequeueSysCntxDma"},
++{MI_ExecuteDmaDescriptorForQueue,     "MI_ExecuteDmaDescriptorForQueue"},
++{MI_ExecuteDmaDescriptor1,    "MI_ExecuteDmaDescriptor1"},
++{MI_ExecuteDmaDescriptor1_seq1,       "MI_ExecuteDmaDescriptor1_seq1"},
++{MI_ExecuteDmaDescriptor1_seq2,       "MI_ExecuteDmaDescriptor1_seq2"},
++{MI_ExecuteDmaDescriptor1_seq3,       "MI_ExecuteDmaDescriptor1_seq3"},
++{MI_GetNewSizeInProg, "MI_GetNewSizeInProg"},
++{MI_GetNewSizeInProg_seq1,    "MI_GetNewSizeInProg_seq1"},
++{MI_FirstBlockRead,   "MI_FirstBlockRead"},
++{MI_ExtraFirstBlockRead,      "MI_ExtraFirstBlockRead"},
++{MI_UnimplementedError,       "MI_UnimplementedError"},
++{MI_UpdateDescriptor, "MI_UpdateDescriptor"},
++{MI_UpdateDescriptor_seq1,    "MI_UpdateDescriptor_seq1"},
++{MI_UpdateDescriptor_seq2,    "MI_UpdateDescriptor_seq2"},
++{MI_UpdateDescriptor_seq3,    "MI_UpdateDescriptor_seq3"},
++{MI_UpdateDescriptor_seq4,    "MI_UpdateDescriptor_seq4"},
++{MI_UpdateDescriptor_seq5,    "MI_UpdateDescriptor_seq5"},
++{MI_GetNextSizeInProg,        "MI_GetNextSizeInProg"},
++{MI_DoStopThisDma,    "MI_DoStopThisDma"},
++{MI_DoStopThisDma_seq1,       "MI_DoStopThisDma_seq1"},
++{MI_GenNewBytesToRead,        "MI_GenNewBytesToRead"},
++{MI_WaitForEventReadTy1,      "MI_WaitForEventReadTy1"},
++{MI_WaitUpdateEvent,  "MI_WaitUpdateEvent"},
++{MI_WaitUpdateEvent_seq1,     "MI_WaitUpdateEvent_seq1"},
++{MI_DoSleepOneTickThenRunable,        "MI_DoSleepOneTickThenRunable"},
++{MI_RunEvent, "MI_RunEvent"},
++{MI_EnqueueThread,    "MI_EnqueueThread"},
++{MI_CheckContext0,    "MI_CheckContext0"},
++{MI_EnqueueDma,       "MI_EnqueueDma"},
++{MI_CprocTrapping,    "MI_CprocTrapping"},
++{MI_CprocTrapping_seq1,       "MI_CprocTrapping_seq1"},
++{MI_WaitForRemoteRoutes1,     "MI_WaitForRemoteRoutes1"},
++{MI_SetEventCommand,  "MI_SetEventCommand"},
++{MI_DoSetEvent,       "MI_DoSetEvent"},
++{MI_DoRemoteSetEventNowOrTrapQueueingDma,     "MI_DoRemoteSetEventNowOrTrapQueueingDma"},
++{MI_DoRemoteSetEventNowOrTrapQueueingDma_seq1,        "MI_DoRemoteSetEventNowOrTrapQueueingDma_seq1"},
++{MI_SendRemoteDmaRoutes2,     "MI_SendRemoteDmaRoutes2"},
++{MI_WaitForRemoteRoutes2,     "MI_WaitForRemoteRoutes2"},
++{MI_WaitEventCommandTy0,      "MI_WaitEventCommandTy0"},
++{MI_DequeueNonSysCntxDma2,    "MI_DequeueNonSysCntxDma2"},
++{MI_WaitEventCommandTy1,      "MI_WaitEventCommandTy1"},
++{MI_WaitEventCommandTy1_seq1, "MI_WaitEventCommandTy1_seq1"},
++{MI_DequeueNonSysCntxThread,  "MI_DequeueNonSysCntxThread"},
++{MI_DequeueSysCntxDma1,       "MI_DequeueSysCntxDma1"},
++{MI_DequeueSysCntxThread,     "MI_DequeueSysCntxThread"},
++{MI_TestNonSysCntxDmaQueueEmpty,      "MI_TestNonSysCntxDmaQueueEmpty"},
++{MI_TestNonSysCntxDmaQueueEmpty_seq1, "MI_TestNonSysCntxDmaQueueEmpty_seq1"},
++{MI_TestNonSysCntxDmaQueueEmpty_seq2, "MI_TestNonSysCntxDmaQueueEmpty_seq2"},
++{MI_RunThreadCommand, "MI_RunThreadCommand"},
++{MI_SetEventWaitForLastAcess, "MI_SetEventWaitForLastAcess"},
++{MI_SetEventReadWait, "MI_SetEventReadWait"},
++{MI_SetEventReadWait_seq1,    "MI_SetEventReadWait_seq1"},
++{MI_TestEventType,    "MI_TestEventType"},
++{MI_TestEventType_seq1,       "MI_TestEventType_seq1"},
++{MI_TestEventBit2,    "MI_TestEventBit2"},
++{MI_DmaDescOrBlockCopyOrChainedEvent, "MI_DmaDescOrBlockCopyOrChainedEvent"},
++{MI_RunThread,        "MI_RunThread"},
++{MI_RunThread1,       "MI_RunThread1"},
++{MI_RunThread1_seq1,  "MI_RunThread1_seq1"},
++{MI_IncDmaSysCntxBPtr,        "MI_IncDmaSysCntxBPtr"},
++{MI_IncDmaSysCntxBPtr_seq1,   "MI_IncDmaSysCntxBPtr_seq1"},
++{MI_IncDmaSysCntxBPtr_seq2,   "MI_IncDmaSysCntxBPtr_seq2"},
++{MI_WaitForCntxDmaDescRead,   "MI_WaitForCntxDmaDescRead"},
++{MI_FillInContext,    "MI_FillInContext"},
++{MI_FillInContext_seq1,       "MI_FillInContext_seq1"},
++{MI_WriteNewDescToQueue,      "MI_WriteNewDescToQueue"},
++{MI_WriteNewDescToQueue_seq1, "MI_WriteNewDescToQueue_seq1"},
++{MI_TestForQueueWrap, "MI_TestForQueueWrap"},
++{MI_TestForQueueWrap_seq1,    "MI_TestForQueueWrap_seq1"},
++{MI_TestQueueIsFull,  "MI_TestQueueIsFull"},
++{MI_TestQueueIsFull_seq1,     "MI_TestQueueIsFull_seq1"},
++{MI_TestQueueIsFull_seq2,     "MI_TestQueueIsFull_seq2"},
++{MI_CheckPsychoShitFixup,     "MI_CheckPsychoShitFixup"},
++{MI_PsychoShitFixupForcedRead,        "MI_PsychoShitFixupForcedRead"},
++{MI_PrepareDMATimeSlice,      "MI_PrepareDMATimeSlice"},
++{MI_PrepareDMATimeSlice_seq1, "MI_PrepareDMATimeSlice_seq1"},
++{MI_TProcRestartFromTrapOrTestEventBit2,      "MI_TProcRestartFromTrapOrTestEventBit2"},
++{MI_TProcRestartFromTrapOrTestEventBit2_seq1, "MI_TProcRestartFromTrapOrTestEventBit2_seq1"},
++{MI_WaitForGlobalsRead,       "MI_WaitForGlobalsRead"},
++{MI_WaitForNPCRead,   "MI_WaitForNPCRead"},
++{MI_EventInterrupt,   "MI_EventInterrupt"},
++{MI_EventInterrupt_seq1,      "MI_EventInterrupt_seq1"},
++{MI_EventInterrupt_seq2,      "MI_EventInterrupt_seq2"},
++{MI_EventInterrupt_seq3,      "MI_EventInterrupt_seq3"},
++{MI_TestSysCntxDmaQueueEmpty, "MI_TestSysCntxDmaQueueEmpty"},
++{MI_TestSysCntxDmaQueueEmpty_seq1,    "MI_TestSysCntxDmaQueueEmpty_seq1"},
++{MI_TestIfRemoteDesc, "MI_TestIfRemoteDesc"},
++{MI_DoDmaLocalSetEvent,       "MI_DoDmaLocalSetEvent"},
++{MI_DoDmaLocalSetEvent_seq1,  "MI_DoDmaLocalSetEvent_seq1"},
++{MI_DoDmaLocalSetEvent_seq2,  "MI_DoDmaLocalSetEvent_seq2"},
++{MI_DmaLoop1, "MI_DmaLoop1"},
++{MI_ExitDmaLoop,      "MI_ExitDmaLoop"},
++{MI_ExitDmaLoop_seq1, "MI_ExitDmaLoop_seq1"},
++{MI_RemoteDmaTestPAckType,    "MI_RemoteDmaTestPAckType"},
++{MI_PacketDiscardOrTestFailRecIfCCis0,        "MI_PacketDiscardOrTestFailRecIfCCis0"},
++{MI_PacketDiscardOrTestFailRecIfCCis0_seq1,   "MI_PacketDiscardOrTestFailRecIfCCis0_seq1"},
++{MI_TestNackFailIsZero2,      "MI_TestNackFailIsZero2"},
++{MI_TestNackFailIsZero3,      "MI_TestNackFailIsZero3"},
++{MI_DmaFailCountError,        "MI_DmaFailCountError"},
++{MI_TestDmaForSysCntx,        "MI_TestDmaForSysCntx"},
++{MI_TestDmaForSysCntx_seq1,   "MI_TestDmaForSysCntx_seq1"},
++{MI_TestDmaForSysCntx_seq2,   "MI_TestDmaForSysCntx_seq2"},
++{MI_TestAeqB2,        "MI_TestAeqB2"},
++{MI_TestAeqB2_seq1,   "MI_TestAeqB2_seq1"},
++{MI_GetNextDmaDescriptor,     "MI_GetNextDmaDescriptor"},
++{MI_DequeueSysCntxDma2,       "MI_DequeueSysCntxDma2"},
++{MI_InputSetEvent,    "MI_InputSetEvent"},
++{MI_PutBackSysCntxDma,        "MI_PutBackSysCntxDma"},
++{MI_PutBackSysCntxDma_seq1,   "MI_PutBackSysCntxDma_seq1"},
++{MI_PutBackSysCntxDma_seq2,   "MI_PutBackSysCntxDma_seq2"},
++{MI_InputRemoteDma,   "MI_InputRemoteDma"},
++{MI_InputRemoteDma_seq1,      "MI_InputRemoteDma_seq1"},
++{MI_WaitOneTickForWakeup1,    "MI_WaitOneTickForWakeup1"},
++{MI_SendRemoteDmaDesc,        "MI_SendRemoteDmaDesc"},
++{MI_InputLockQueue,   "MI_InputLockQueue"},
++{MI_CloseTheTrappedPacketIfCCis1,     "MI_CloseTheTrappedPacketIfCCis1"},
++{MI_CloseTheTrappedPacketIfCCis1_seq1,        "MI_CloseTheTrappedPacketIfCCis1_seq1"},
++{MI_PostDmaInterrupt, "MI_PostDmaInterrupt"},
++{MI_InputUnLockQueue, "MI_InputUnLockQueue"},
++{MI_WaitForUnLockDescRead,    "MI_WaitForUnLockDescRead"},
++{MI_SendEOPforRemoteDma,      "MI_SendEOPforRemoteDma"},
++{MI_LookAtRemoteAck,  "MI_LookAtRemoteAck"},
++{MI_InputWriteBlockQueue,     "MI_InputWriteBlockQueue"},
++{MI_WaitForSpStore,   "MI_WaitForSpStore"},
++{MI_TProcNext,        "MI_TProcNext"},
++{MI_TProcStoppedRunning,      "MI_TProcStoppedRunning"},
++{MI_InputWriteBlock,  "MI_InputWriteBlock"},
++{MI_RunDmaOrDeqNonSysCntxDma, "MI_RunDmaOrDeqNonSysCntxDma"},
++{MI_ExecuteDmaDescriptorForRun,       "MI_ExecuteDmaDescriptorForRun"},
++{MI_ConfirmQueueLock, "MI_ConfirmQueueLock"},
++{MI_DmaInputIdentify, "MI_DmaInputIdentify"},
++{MI_TProcStoppedRunning2,     "MI_TProcStoppedRunning2"},
++{MI_TProcStoppedRunning2_seq1,        "MI_TProcStoppedRunning2_seq1"},
++{MI_TProcStoppedRunning2_seq2,        "MI_TProcStoppedRunning2_seq2"},
++{MI_ThreadInputIdentify,      "MI_ThreadInputIdentify"},
++{MI_InputIdWriteAddrAndType3, "MI_InputIdWriteAddrAndType3"},
++{MI_IProcTrappedWriteStatus,  "MI_IProcTrappedWriteStatus"},
++{MI_FinishTrappingEop,        "MI_FinishTrappingEop"},
++{MI_InputTestTrans,   "MI_InputTestTrans"},
++{MI_TestAeqB3,        "MI_TestAeqB3"},
++{MI_ThreadUpdateNonSysCntxBack,       "MI_ThreadUpdateNonSysCntxBack"},
++{MI_ThreadQueueOverflow,      "MI_ThreadQueueOverflow"},
++{MI_RunContext0Thread,        "MI_RunContext0Thread"},
++{MI_RunContext0Thread_seq1,   "MI_RunContext0Thread_seq1"},
++{MI_RunContext0Thread_seq2,   "MI_RunContext0Thread_seq2"},
++{MI_RunDmaDesc,       "MI_RunDmaDesc"},
++{MI_RunDmaDesc_seq1,  "MI_RunDmaDesc_seq1"},
++{MI_RunDmaDesc_seq2,  "MI_RunDmaDesc_seq2"},
++{MI_TestAeqB, "MI_TestAeqB"},
++{MI_WaitForNonCntxDmaDescRead,        "MI_WaitForNonCntxDmaDescRead"},
++{MI_DmaQueueOverflow, "MI_DmaQueueOverflow"},
++{MI_BlockCopyEvent,   "MI_BlockCopyEvent"},
++{MI_BlockCopyEventReadBlock,  "MI_BlockCopyEventReadBlock"},
++{MI_BlockCopyWaitForReadData, "MI_BlockCopyWaitForReadData"},
++{MI_InputWriteWord,   "MI_InputWriteWord"},
++{MI_TraceSetEvents,   "MI_TraceSetEvents"},
++{MI_TraceSetEvents_seq1,      "MI_TraceSetEvents_seq1"},
++{MI_TraceSetEvents_seq2,      "MI_TraceSetEvents_seq2"},
++{MI_InputWriteDoubleWd,       "MI_InputWriteDoubleWd"},
++{MI_SendLockTransIfCCis1,     "MI_SendLockTransIfCCis1"},
++{MI_WaitForDmaRoutes1,        "MI_WaitForDmaRoutes1"},
++{MI_LoadDmaContext,   "MI_LoadDmaContext"},
++{MI_InputTestAndSetWord,      "MI_InputTestAndSetWord"},
++{MI_InputTestAndSetWord_seq1, "MI_InputTestAndSetWord_seq1"},
++{MI_GetDestEventValue,        "MI_GetDestEventValue"},
++{MI_SendDmaIdentify,  "MI_SendDmaIdentify"},
++{MI_InputAtomicAddWord,       "MI_InputAtomicAddWord"},
++{MI_LoadBFromTransD0, "MI_LoadBFromTransD0"},
++{MI_ConditionalWriteBackCCTrue,       "MI_ConditionalWriteBackCCTrue"},
++{MI_WaitOneTickForWakeup,     "MI_WaitOneTickForWakeup"},
++{MI_SendFinalUnlockTrans,     "MI_SendFinalUnlockTrans"},
++{MI_SendDmaEOP,       "MI_SendDmaEOP"},
++{MI_GenLastAddrForPsycho,     "MI_GenLastAddrForPsycho"},
++{MI_FailedAckIfCCis0, "MI_FailedAckIfCCis0"},
++{MI_FailedAckIfCCis0_seq1,    "MI_FailedAckIfCCis0_seq1"},
++{MI_WriteDmaSysCntxDesc,      "MI_WriteDmaSysCntxDesc"},
++{MI_TimesliceDmaQueueOverflow,        "MI_TimesliceDmaQueueOverflow"},
++{MI_DequeueNonSysCntxThread1, "MI_DequeueNonSysCntxThread1"},
++{MI_DequeueNonSysCntxThread1_seq1,    "MI_DequeueNonSysCntxThread1_seq1"},
++{MI_TestThreadQueueEmpty,     "MI_TestThreadQueueEmpty"},
++{MI_ClearThreadQueueIfCC,     "MI_ClearThreadQueueIfCC"},
++{MI_DequeueSysCntxThread1,    "MI_DequeueSysCntxThread1"},
++{MI_DequeueSysCntxThread1_seq1,       "MI_DequeueSysCntxThread1_seq1"},
++{MI_TProcStartUpGeneric,      "MI_TProcStartUpGeneric"},
++{MI_WaitForPCload2,   "MI_WaitForPCload2"},
++{MI_WaitForNPCWrite,  "MI_WaitForNPCWrite"},
++{MI_WaitForEventWaitAddr,     "MI_WaitForEventWaitAddr"},
++{MI_WaitForWaitEventAccess,   "MI_WaitForWaitEventAccess"},
++{MI_WaitForWaitEventAccess_seq1,      "MI_WaitForWaitEventAccess_seq1"},
++{MI_WaitForWaitEventDesc,     "MI_WaitForWaitEventDesc"},
++{MI_WaitForEventReadTy0,      "MI_WaitForEventReadTy0"},
++{MI_SendCondTestFail, "MI_SendCondTestFail"},
++{MI_InputMoveToNextTrans,     "MI_InputMoveToNextTrans"},
++{MI_ThreadUpdateSysCntxBack,  "MI_ThreadUpdateSysCntxBack"},
++{MI_FinishedSetEvent, "MI_FinishedSetEvent"},
++{MI_EventIntUpdateBPtr,       "MI_EventIntUpdateBPtr"},
++{MI_EventQueueOverflow,       "MI_EventQueueOverflow"},
++{MI_MaskLowerSource,  "MI_MaskLowerSource"},
++{MI_DmaLoop,  "MI_DmaLoop"},
++{MI_SendNullSetEvent, "MI_SendNullSetEvent"},
++{MI_SendFinalSetEvent,        "MI_SendFinalSetEvent"},
++{MI_TestNackFailIsZero1,      "MI_TestNackFailIsZero1"},
++{MI_DmaPacketTimedOutOrPacketError,   "MI_DmaPacketTimedOutOrPacketError"},
++{MI_NextPacketIsLast, "MI_NextPacketIsLast"},
++{MI_TestForZeroLengthDma,     "MI_TestForZeroLengthDma"},
++{MI_WaitForPCload,    "MI_WaitForPCload"},
++{MI_ReadInIns,        "MI_ReadInIns"},
++{MI_WaitForInsRead,   "MI_WaitForInsRead"},
++{MI_WaitForLocals,    "MI_WaitForLocals"},
++{MI_WaitForOutsWrite, "MI_WaitForOutsWrite"},
++{MI_WaitForWaitEvWrBack,      "MI_WaitForWaitEvWrBack"},
++{MI_WaitForLockRead,  "MI_WaitForLockRead"},
++{MI_TestQueueLock,    "MI_TestQueueLock"},
++{MI_InputIdWriteAddrAndType,  "MI_InputIdWriteAddrAndType"},
++{MI_InputIdWriteAddrAndType2, "MI_InputIdWriteAddrAndType2"},
++{MI_ThreadInputIdentify2,     "MI_ThreadInputIdentify2"},
++{MI_WriteIntoTrapArea0,       "MI_WriteIntoTrapArea0"},
++{MI_GenQueueBlockWrAddr,      "MI_GenQueueBlockWrAddr"},
++{MI_InputDiscardFreeLock,     "MI_InputDiscardFreeLock"},
++{MI_WriteIntoTrapArea1,       "MI_WriteIntoTrapArea1"},
++{MI_WriteIntoTrapArea2,       "MI_WriteIntoTrapArea2"},
++{MI_ResetBPtrToBase,  "MI_ResetBPtrToBase"},
++{MI_InputDoTrap,      "MI_InputDoTrap"},
++{MI_RemoteDmaCntxt0Update,    "MI_RemoteDmaCntxt0Update"},
++{MI_ClearQueueLock,   "MI_ClearQueueLock"},
++{MI_IProcTrappedBlockWriteData,       "MI_IProcTrappedBlockWriteData"},
++{MI_FillContextFilter,        "MI_FillContextFilter"},
++{MI_IProcTrapped4,    "MI_IProcTrapped4"},
++{MI_RunSysCntxDma,    "MI_RunSysCntxDma"},
++{MI_ChainedEventError,        "MI_ChainedEventError"},
++{MI_InputTrappingEOP, "MI_InputTrappingEOP"},
++{MI_CheckForRunIfZero,        "MI_CheckForRunIfZero"},
++{MI_TestForBreakOrSuspend,    "MI_TestForBreakOrSuspend"},
++{MI_SwapForRunable,   "MI_SwapForRunable"},
+Index: linux-2.6.5/include/elan3/neterr_rpc.h
+===================================================================
+--- linux-2.6.5.orig/include/elan3/neterr_rpc.h        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan3/neterr_rpc.h     2005-05-11 12:10:12.594908864 -0400
+@@ -0,0 +1,68 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN3_NETERR_RPC_H
++#define __ELAN3_NETERR_RPC_H
++
++#ident "$Id: neterr_rpc.h,v 1.20 2003/06/26 16:05:22 fabien Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/neterr_rpc.h,v $*/
++
++#define NETERR_SERVICE        "neterr-srv"
++#define NETERR_PROGRAM        ((u_long) 170002)
++#define NETERR_VERSION        ((u_long) 1)
++
++#define NETERR_NULL_RPC               0
++#define NETERR_FIXUP_RPC      1
++
++/* network error rpc timeout */
++#define NETERR_RPC_TIMEOUT    5
++
++/*
++ * XDR functions for Tru64 and Linux in userspace. 
++ *  NB Linux kernelspace xdr routines are in network_error.
++ *     and *must* be kept consistent.
++ */
++#if defined(DIGITAL_UNIX) || !defined(__KERNEL__)
++bool_t
++xdr_capability (XDR *xdrs, void *arg)
++{
++    ELAN_CAPABILITY *cap = (ELAN_CAPABILITY *) arg;
++
++    return (xdr_opaque (xdrs, (caddr_t) &cap->cap_userkey, sizeof (cap->cap_userkey)) &&
++          xdr_int (xdrs, &cap->cap_version) &&
++          xdr_u_short (xdrs, &cap->cap_type) &&
++          xdr_int (xdrs, &cap->cap_lowcontext) &&
++          xdr_int (xdrs, &cap->cap_highcontext) &&
++          xdr_int (xdrs, &cap->cap_mycontext) &&
++          xdr_int (xdrs, &cap->cap_lownode) &&
++          xdr_int (xdrs, &cap->cap_highnode) &&
++          xdr_u_int (xdrs, &cap->cap_railmask) &&
++          xdr_opaque (xdrs, (caddr_t) &cap->cap_bitmap[0], sizeof (cap->cap_bitmap)));
++}
++
++bool_t
++xdr_neterr_msg (XDR *xdrs, void *req)
++{
++    NETERR_MSG *msg = (NETERR_MSG *) req;
++
++    return (xdr_u_int (xdrs, &msg->Rail) &&
++          xdr_capability (xdrs, &msg->SrcCapability) &&
++          xdr_capability (xdrs, &msg->DstCapability) &&
++          xdr_u_int (xdrs, &msg->DstProcess) &&
++          xdr_u_int (xdrs, &msg->CookieAddr) &&
++          xdr_u_int (xdrs, &msg->CookieVProc) &&
++          xdr_u_int (xdrs, &msg->NextCookie) &&
++          xdr_u_int (xdrs, &msg->WaitForEop));
++}
++#endif /* INCLUDE_XDR_INLINE */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++#endif /* __ELAN3_NETERR_RPC_H */
+Index: linux-2.6.5/include/elan3/perm.h
+===================================================================
+--- linux-2.6.5.orig/include/elan3/perm.h      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan3/perm.h   2005-05-11 12:10:12.594908864 -0400
+@@ -0,0 +1,29 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN3_PERM_H
++#define __ELAN3_PERM_H
++
++#ident "$Id: perm.h,v 1.7 2003/09/24 13:57:24 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/perm.h,v $*/
++
++#define ELAN3_PERM_NULL               0x00
++#define ELAN3_PERM_LOCAL_READ 0x04
++#define ELAN3_PERM_READ               0x08
++#define ELAN3_PERM_NOREMOTE   0x0c
++#define ELAN3_PERM_REMOTEREAD 0x10
++#define ELAN3_PERM_REMOTEWRITE        0x14
++#define ELAN3_PERM_REMOTEEVENT        0x18
++#define ELAN3_PERM_REMOTEALL  0x1c
++
++#endif /* __ELAN3_PERM_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/include/elan3/pte.h
+===================================================================
+--- linux-2.6.5.orig/include/elan3/pte.h       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan3/pte.h    2005-05-11 12:10:12.595908712 -0400
+@@ -0,0 +1,139 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN3_PTE_H
++#define __ELAN3_PTE_H
++
++#ident "$Id: pte.h,v 1.26 2003/09/24 13:57:24 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/pte.h,v $*/
++
++#ifdef        __cplusplus
++extern          "C"
++{
++#endif
++
++#include <elan3/e3types.h>
++#include <elan3/perm.h>
++
++typedef E3_uint64 ELAN3_PTE;
++typedef E3_uint32 ELAN3_PTP;
++
++#define ELAN3_PTE_SIZE                (8)
++#define ELAN3_PTP_SIZE                (4)
++
++#define ELAN3_PTE_REF         ((E3_uint64) 1 << 63)           /* 63      - referenced bit */
++#define ELAN3_PTE_MOD         ((E3_uint64) 1 << 55)           /* 55      - modified bit */
++#define ELAN3_RM_MASK         (ELAN3_PTE_REF | ELAN3_PTE_MOD)
++
++#define ELAN3_PTE_PFN_MASK    0x0000fffffffff000ull           /* [12:48] - Physical address */
++
++#define ELAN3_PTE_BIG_ENDIAN  0x80                            /* 7       - big endian */
++#define ELAN3_PTE_64_BIT              0x40                            /* 6       - 64 bit pci address */
++#define ELAN3_PTE_LOCAL               0x20                            /* 5       - local sdram */
++
++#define ELAN3_PTE_PERM_MASK   0x1c                            /* [2:4]   - Permissions */
++#define ELAN3_PTE_PERM_SHIFT     2
++
++#define ELAN3_ET_MASK         0x3
++#define ELAN3_ET_INVALID              0x0                                     /* [0:1] */
++#define ELAN3_ET_PTP          0x1
++#define ELAN3_ET_PTE          0x2
++
++#define ELAN3_INVALID_PTP     ((ELAN3_PTP) 0)
++#define ELAN3_INVALID_PTE     ((ELAN3_PTE) 0)
++
++#define ELAN3_PTP_TYPE(ptp)   ((ptp) & ELAN3_ET_MASK)
++#define ELAN3_PTE_TYPE(pte)   ((pte) & ELAN3_ET_MASK)
++#define ELAN3_PTE_PERM(pte)   ((pte) & ELAN3_PTE_PERM_MASK)
++#define ELAN3_PTE_VALID(pte)  (((pte) & ELAN3_ET_MASK) == ELAN3_ET_PTE)
++#define ELAN3_PTE_ISREF(pte)  ((pte) & ELAN3_PTE_REF)
++#define ELAN3_PTE_ISMOD(pte)  ((pte) & ELAN3_PTE_MOD)
++#define ELAN3_PTE_WRITEABLE(pte)      (ELAN3_PERM_WRITEABLE(ELAN3_PTE_PERM(pte)))
++
++#define ELAN3_PERM_WRITEABLE(perm)    ((perm) == ELAN3_PERM_NOREMOTE || (perm) > ELAN3_PERM_REMOTEREAD)
++#define ELAN3_PERM_REMOTE(perm)               ((perm) > ELAN3_PERM_NOREMOTE)
++
++#define ELAN3_PERM_READONLY(perm)     ((perm) == ELAN3_PERM_NOREMOTE ? ELAN3_PERM_LOCAL_READ : \
++                                       (perm) > ELAN3_PERM_REMOTEREAD ? ELAN3_PERM_READ : (perm))
++#if PAGE_SHIFT == 12
++#  define ELAN3_PAGE_SHIFT    12
++#else
++#  define ELAN3_PAGE_SHIFT    13
++#endif
++
++#define ELAN3_PAGE_SIZE               (1 << ELAN3_PAGE_SHIFT)
++#define ELAN3_PAGE_OFFSET     (ELAN3_PAGE_SIZE-1)
++#define ELAN3_PAGE_MASK               (~ELAN3_PAGE_OFFSET)
++
++#if ELAN3_PAGE_SHIFT == 13
++#  define ELAN3_L3_SHIFT              5
++#else
++#  define ELAN3_L3_SHIFT              6
++#endif
++#define ELAN3_L2_SHIFT                6
++#define ELAN3_L1_SHIFT                8
++
++/* Number of entries in a given level ptbl */
++#define ELAN3_L3_ENTRIES              (1 << ELAN3_L3_SHIFT)
++#define ELAN3_L2_ENTRIES              (1 << ELAN3_L2_SHIFT)
++#define ELAN3_L1_ENTRIES              (1 << ELAN3_L1_SHIFT)
++
++/* Virtual address spanned by each entry */
++#define ELAN3_L3_SIZE         (1 << (ELAN3_PAGE_SHIFT))
++#define ELAN3_L2_SIZE         (1 << (ELAN3_L3_SHIFT+ELAN3_PAGE_SHIFT))
++#define ELAN3_L1_SIZE         (1 << (ELAN3_L3_SHIFT+ELAN3_L2_SHIFT+ELAN3_PAGE_SHIFT))
++
++/* Virtual address size of page table */
++#define ELAN3_L1_PTSIZE          (ELAN3_L1_ENTRIES * ELAN3_L1_SIZE)
++#define ELAN3_L3_PTSIZE               (ELAN3_L3_ENTRIES * ELAN3_L3_SIZE)
++#define ELAN3_L2_PTSIZE               (ELAN3_L2_ENTRIES * ELAN3_L2_SIZE)
++
++/* Mask for offset into page table */
++#define ELAN3_L1_PTOFFSET        ((ELAN3_L1_SIZE*ELAN3_L1_ENTRIES)-1)
++#define ELAN3_L3_PTOFFSET     ((ELAN3_L3_SIZE*ELAN3_L3_ENTRIES)-1)
++#define ELAN3_L2_PTOFFSET     ((ELAN3_L2_SIZE*ELAN3_L2_ENTRIES)-1)
++
++#define ELAN3_L1_INDEX(addr)  (((E3_Addr) (addr) & 0xFF000000) >> (ELAN3_L2_SHIFT+ELAN3_L3_SHIFT+ELAN3_PAGE_SHIFT))
++#define ELAN3_L2_INDEX(addr)  (((E3_Addr) (addr) & 0x00FD0000) >> (ELAN3_L3_SHIFT+ELAN3_PAGE_SHIFT))
++#define ELAN3_L3_INDEX(addr)  (((E3_Addr) (addr) & 0x0003F000) >> ELAN3_PAGE_SHIFT)
++
++#define       ELAN3_L1_BASE(addr)     (((E3_Addr)(addr)) & 0x00000000)
++#define       ELAN3_L2_BASE(addr)     (((E3_Addr)(addr)) & 0xFF000000)
++#define       ELAN3_L3_BASE(addr)     (((E3_Addr)(addr)) & 0xFFFC0000)
++
++/* Convert a page table pointer entry to the PT */
++#define PTP_TO_PT_PADDR(ptp)  ((E3_Addr)(ptp & 0xFFFFFFFC))
++
++#ifdef __KERNEL__
++/*
++ * incompatible access for permission macro.
++ */
++extern  u_char  elan3mmu_permissionTable[8];
++#define ELAN3_INCOMPAT_ACCESS(perm,access) (! (elan3mmu_permissionTable[(perm)>>ELAN3_PTE_PERM_SHIFT] & (1 << (access))))
++
++#define elan3_readptp(dev, ptp)               (elan3_sdram_readl (dev, ptp))
++#define elan3_writeptp(dev, ptp, value)       (elan3_sdram_writel (dev, ptp, value))
++#define elan3_readpte(dev, pte)               (elan3_sdram_readq (dev, pte))
++#define elan3_writepte(dev,pte, value)        (elan3_sdram_writeq (dev, pte, value))
++
++#define elan3_invalidatepte(dev, pte) (elan3_sdram_writel (dev, pte, 0))
++#define elan3_modifypte(dev,pte,new)  (elan3_sdram_writel (dev, pte, (int) (new)))
++#define elan3_clrref(dev,pte)         (elan3_sdram_writeb (dev, pte + 7)
++
++#endif        /* __KERNEL__ */
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* __ELAN3_PTE_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/include/elan3/spinlock.h
+===================================================================
+--- linux-2.6.5.orig/include/elan3/spinlock.h  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan3/spinlock.h       2005-05-11 12:10:12.595908712 -0400
+@@ -0,0 +1,195 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef _ELAN3_SPINLOCK_
++#define _ELAN3_SPINLOCK_
++
++#ident "$Id: spinlock.h,v 1.31 2003/09/24 13:57:24 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/spinlock.h,v $*/
++
++/*
++ * This spinlock is designed for main/elan processor interactions.
++ * The lock is split over Elan/Main memory in such a way that
++ * we don't end up busy-polling over the PCI.
++ * In the Elan memory we have two words; one is a sequence number
++ * and the other is a lock word for main.
++ * In main memory we have a copy of the sequence number which main polls when it is
++ * waiting for the Elan to drop the lock. Main polls this word until it becomes
++ * equal to the sequence number it sampled.
++ * The Elan drops the lock by writing the current sequence number to main memory.
++ * It is coded to always give priority to the Elan thread, and so when both go for the
++ * lock, main will back off first.
++ *
++ * 18/3/98
++ * This has been extended to avoid a starvation case where both the main and thread claim the
++ * lock and so both backoff (thread does a break). So now, main attempts to claim the
++ * lock by writing 'mainLock' then samples the 'sl_seq' and if it has the lock
++ * it sets 'mainGotLock'. The thread will now see the 'sl_mainLock' set, but will only
++ * backoff with a c_break_busywait() if 'mainGotLock' is set too.
++ */
++typedef struct elan3_spinlock_elan {
++    union {
++      volatile E3_uint64      mainLocks;              /* main writes this dble word */
++      struct {
++          volatile E3_uint32  mainLock;               /* main wants a lock */
++          volatile E3_uint32  mainGotLock;            /* main has the lock */
++      } s;
++    } sl_u;
++    volatile E3_uint32                sl_seq;                 /* thread owns this word */
++    volatile E3_uint32        sl_mainWait;            /* performance counter */
++    volatile E3_uint32                sl_elanWait;            /* performance counter */
++    volatile E3_uint32                sl_elanBusyWait;        /* performance counter */
++    /* NOTE: The lock/seq words must be within the same 32-byte Elan cache-line */
++    E3_uint64                   sl_pad[5];            /* pad to 64-bytes */
++} ELAN3_SPINLOCK_ELAN;
++
++#define sl_mainLocks sl_u.mainLocks
++#define sl_mainLock  sl_u.s.mainLock
++#define sl_mainGotLock sl_u.s.mainGotLock
++
++#define SL_MAIN_RECESSIVE     1
++#define SL_MAIN_DOMINANT      2
++
++/* Declare this as a main memory cache block for efficiency */
++typedef union elan3_spinlock_main {
++    volatile E3_uint32                sl_seq;                 /* copy of seq number updated by Elan */
++    volatile E3_uint32                sl_Int32[E3_BLK_SIZE/sizeof (E3_uint32)];
++} ELAN3_SPINLOCK_MAIN;
++
++/* Main/Main or Elan/Elan lock word */
++typedef volatile int  ELAN3_SPINLOCK;
++
++#ifdef __ELAN3__
++
++/* Main/Elan interlock */
++
++#define ELAN3_ME_SPINENTER(SLE,SL) do {\
++                              asm volatile ("! elan3_spinlock store barrier");\
++                      (SLE)->sl_seq++; \
++                      if ((SLE)->sl_mainLock) \
++                        elan3_me_spinblock(SLE, SL);\
++                      asm volatile ("! elan3_spinlock store barrier");\
++              } while (0)
++#define ELAN3_ME_SPINEXIT(SLE,SL) do {\
++                      asm volatile ("! elan3_spinlock store barrier");\
++                      (SL)->sl_seq = (SLE)->sl_seq;\
++                      asm volatile ("! elan3_spinlock store barrier");\
++              } while (0)
++
++
++/* Elan/Elan interlock */
++#define ELAN3_SPINENTER(L)    do {\
++                         asm volatile ("! store barrier");\
++                         if (c_swap ((L), 1)) elan3_spinenter(L);\
++                         asm volatile ("! store barrier");\
++                      } while (0)
++#define ELAN3_SPINEXIT(L)     do {\
++                         asm volatile ("! store barrier");\
++                         c_swap((L), 0);\
++                         asm volatile ("! store barrier");\
++                      } while (0)
++
++extern void elan3_me_spinblock (ELAN3_SPINLOCK_ELAN *sle, ELAN3_SPINLOCK_MAIN *sl);
++extern void elan3_spinenter (ELAN3_SPINLOCK *l);
++
++#else                    
++
++/* Main/Elan interlock */
++#ifdef DEBUG
++#define ELAN3_ME_SPINENTER(SDRAM,SLE,SL) do {\
++                      register E3_int32 maxLoops = 0x7fffffff;        \
++                      register E3_uint32 seq;\
++                      elan3_write32_sdram(SDRAM, (SLE) + offsetof(ELAN3_SPINLOCK_ELAN, sl_mainLock), SL_MAIN_RECESSIVE); \
++                      MEMBAR_STORELOAD(); \
++                      seq = elan3_read32_sdram(SDRAM, (SLE) + offsetof(ELAN3_SPINLOCK_ELAN, sl_seq)); \
++                      while (seq != (SL)->sl_seq) {\
++                          elan3_write32_sdram(SDRAM, (SLE) + offsetof(ELAN3_SPINLOCK_ELAN, sl_mainLock), 0); \
++                          while ((SL)->sl_seq == (seq-1) && maxLoops--) ; \
++                          if (maxLoops < 0) { \
++                              printf("Failed to get ME lock %lx/%lx seq %d sle_seq %d sl_seq %d\n", \
++                                     SL, SLE, seq, \
++                                     elan3_read32_sdram(SDRAM, (SLE) + offsetof(ELAN3_SPINLOCK_ELAN, sl_seq)), \
++                                     (SL)->sl_seq); \
++                          } \
++                          elan3_write32_sdram(SDRAM, (SLE) + offsetof(ELAN3_SPINLOCK_ELAN, sl_mainLock), SL_MAIN_RECESSIVE); \
++                          MEMBAR_STORELOAD(); \
++                          seq = elan3_read32_sdram(SDRAM, (SLE) + offsetof(ELAN3_SPINLOCK_ELAN, sl_seq)); \
++                      }\
++                      elan3_write32_sdram(SDRAM, (SLE) + offsetof(ELAN3_SPINLOCK_ELAN, sl_mainGotLock), 1); \
++                      MEMBAR_LOADLOAD();\
++              } while (0)
++#else
++#define ELAN3_ME_SPINENTER(SDRAM,SLE,SL) do {\
++                      register E3_uint32 seq;\
++                      elan3_write32_sdram(SDRAM, SLE + offsetof(ELAN3_SPINLOCK_ELAN, sl_mainLock), SL_MAIN_RECESSIVE); \
++                      MEMBAR_STORELOAD(); \
++                      seq = elan3_read32_sdram(SDRAM, SLE + offsetof(ELAN3_SPINLOCK_ELAN, sl_seq)); \
++                      while (seq != (SL)->sl_seq) {\
++                          elan3_write32_sdram(SDRAM, SLE + offsetof(ELAN3_SPINLOCK_ELAN, sl_mainLock), 0); \
++                          while ((SL)->sl_seq == (seq-1)) ; \
++                          elan3_write32_sdram(SDRAM, SLE + offsetof(ELAN3_SPINLOCK_ELAN, sl_mainLock), SL_MAIN_RECESSIVE); \
++                          MEMBAR_STORELOAD(); \
++                          seq = elan3_read32_sdram(SDRAM, SLE + offsetof(ELAN3_SPINLOCK_ELAN, sl_seq)); \
++                      }\
++                      elan3_write32_sdram(SDRAM, SLE + offsetof(ELAN3_SPINLOCK_ELAN, sl_mainGotLock), 1); \
++                      MEMBAR_LOADLOAD();\
++              } while (0)
++#endif
++#define ELAN3_ME_FORCEENTER(SDRAM,SLE,SL) do { \
++      register E3_uint32 seq; \
++      MEMBAR_STORELOAD(); \
++      elan3_write32_sdram(SDRAM, SLE + offsetof(ELAN3_SPINLOCK_ELAN, sl_mainLock), SL_MAIN_DOMINANT); \
++      MEMBAR_STORELOAD(); \
++      seq = elan3_read32_sdram(SDRAM, SLE + offsetof(ELAN3_SPINLOCK_ELAN, sl_seq)); \
++      while (seq != (SL)->sl_seq) \
++      { \
++              /* NOTE: we MUST call elan3_usecspin here for kernel comms */\
++              while ((SL)->sl_seq == (seq)-1) \
++                      elan3_usecspin (1); \
++              seq = elan3_read32_sdram(SDRAM, SLE + offsetof(ELAN3_SPINLOCK_ELAN, sl_seq)); \
++      } \
++      elan3_write32_sdram(SDRAM, SLE + offsetof(ELAN3_SPINLOCK_ELAN, sl_mainGotLock), 1); \
++      MEMBAR_LOADLOAD(); \
++} while (0)
++
++#define ELAN3_ME_TRYENTER(SDRAM,SLE,SL,SEQ) do { \
++    elan3_write32_sdram(SDRAM, SLE + offsetof(ELAN3_SPINLOCK_ELAN, sl_mainLock), SL_MAIN_RECESSIVE); \
++    MEMBAR_STORELOAD(); \
++    SEQ = elan3_read32_sdram(SDRAM, SLE + offsetof(ELAN3_SPINLOCK_ELAN, sl_seq)); \
++} while (0)
++
++#define ELAN3_ME_CHECKENTER(SDRAM,SLE,SL,SEQ) do { \
++    if ((SEQ) == ((SL)->sl_seq)) { \
++       elan3_write32_sdram(SDRAM, SLE + offsetof(ELAN3_SPINLOCK_ELAN, sl_mainGotLock), 1); \
++       MEMBAR_LOADLOAD();\
++    } \
++    else ELAN3_ME_SPINENTER(SLE,SL); \
++} while (0)
++      
++#define ELAN3_ME_SPINEXIT(SDRAM,SLE,SL) do {\
++                      MEMBAR_STORESTORE(); \
++                      elan3_write64_sdram(SDRAM, SLE + offsetof(ELAN3_SPINLOCK_ELAN, sl_mainLocks), 0); \
++                      MEMBAR_STORESTORE(); \
++              } while (0)
++
++
++/* Main/Main */
++#define ELAN3_SPINENTER(L)    do {\
++                         while (c_swap ((L), 1)) ; \
++                      } while (0)
++#define ELAN3_SPINEXIT(L)     do {\
++                         c_swap((L), 0);\
++                      } while (0)
++#endif /* _ELAN3_ */
++
++#endif /* _ELAN3_SPINLOCK_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/include/elan3/thread.h
+===================================================================
+--- linux-2.6.5.orig/include/elan3/thread.h    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan3/thread.h 2005-05-11 12:10:12.596908560 -0400
+@@ -0,0 +1,137 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef _ELAN3_THREAD_H
++#define _ELAN3_THREAD_H
++
++#ident "$Id: thread.h,v 1.17 2002/08/09 11:23:34 addy Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/thread.h,v $*/
++
++/* Alignment for a stack frame */
++#define E3_STACK_ALIGN                (64)
++
++typedef struct _E3_Frame {
++    E3_uint32        fr_local[8];             /* saved locals (not used) */
++    E3_uint32        fr_arg[6];               /* saved arguements o0 -> o5 */
++    E3_Addr          fr_savefp;               /* saved frame pointer o6 */
++    E3_Addr          fr_savepc;               /* saved program counter o7 */
++    E3_Addr          fr_stret;                /* stuct return addr */
++    E3_uint32        fr_argd[6];              /* arg dump area */
++    E3_uint32        fr_argx[1];              /* array of args past the sixth */
++} E3_Frame;
++
++typedef struct _E3_Stack {
++    E3_uint32         Locals[8];
++    E3_uint32         Ins[8];
++    E3_uint32         Globals[8];
++    E3_uint32         Outs[8];
++} E3_Stack;
++
++typedef struct _E3_OutsRegs {
++   E3_uint32  o[8];                           /* o6 == pc, o7 == fptr */
++} E3_OutsRegs;
++
++/*
++ * "Magic" value for stack pointer to be ignored.
++ */
++#define VanishingStackPointer 0x42
++
++
++/*
++ * When the Elan traps the N & Z CC bits are held in the NPC
++ * and the V & C bits are in the PC
++ */
++#define PSR_C_BIT     (1)
++#define PSR_V_BIT     (2)
++#define PSR_Z_BIT     (1)
++#define PSR_N_BIT     (2)
++#define CC_MASK               (3)
++#define PC_MASK       (~3)
++#define SP_MASK               (~3)
++
++/*
++ * Threads processor Opcodes.
++ */
++#define OPCODE_MASK           (0xC1F80000)
++#define OPCODE_IMM            (1 << 13)
++
++#define OPCODE_CLASS(instr)   ((instr) & 0xC0000000)
++#define OPCODE_CLASS_0                0x00000000
++#define OPCODE_CLASS_1                0x40000000
++#define OPCODE_CLASS_2                0x80000000
++#define OPCODE_CLASS_3                0xC0000000
++
++#define OPCODE_CPOP           0x81B00000
++#define OPCODE_Ticc           0x81D00000
++
++#define OPCODE_FCODE_SHIFT    19
++#define OPCODE_FCODE_MASK     0x1f
++#define OPCODE_NOT_ALUOP      0x01000000
++
++#define OPCODE_SLL            0x81280000
++#define OPCODE_SRL            0x81300000
++#define OPCODE_SRA            0x81380000
++
++#define OPCODE_OPEN           0x81600000
++#define OPCODE_CLOSE          0x81680000
++#define OPCODE_BREAKTEST      0x81700000
++
++#define OPCODE_BREAK          0x81a00000
++#define OPCODE_SUSPEND                0x81a80000
++#define OPCODE_WAIT           0x81b00000
++
++#define OPCODE_JMPL           0x81c00000
++
++#define OPCODE_LD             0xC0000000
++#define OPCODE_LDD            0xC0180000
++
++#define OPCODE_LDBLOCK16      0xC0900000
++#define OPCODE_LDBLOCK32      0xC0800000
++#define OPCODE_LDBLOCK64      0xC0980000
++
++#define OPCODE_ST             0xC0200000
++#define OPCODE_STD            0xC0380000
++
++#define OPCODE_SWAP           0xC0780000
++
++#define OPCODE_STBLOCK16      0xC0b00000
++#define OPCODE_STBLOCK32      0xC0a00000
++#define OPCODE_STBLOCK64      0xC0b80000
++
++#define OPCODE_CLASS0_MASK    0xC1C00000
++#define OPCODE_SETHI          0x01000000
++#define OPCODE_BICC           0x00800000
++#define OPCODE_SENDREG                0x01800000
++#define OPCODE_SENDMEM                0x01c00000
++
++#define OPCODE_BICC_BN                0x00000000
++#define OPCODE_BICC_BE                0x02000000
++#define OPCODE_BICC_BLE               0x04000000
++#define OPCODE_BICC_BL                0x06000000
++#define OPCODE_BICC_BLEU      0x08000000
++#define OPCODE_BICC_BCS               0x0A000000
++#define OPCODE_BICC_BNEG      0x0C000000
++#define OPCODE_BICC_BVS               0x0E000000
++
++#define OPCODE_BICC_MASK      0x0E000000
++#define OPCODE_BICC_ANNUL     0x20000000
++
++#define INSTR_RS2(instr)      (((instr) >>  0) & 0x1F)
++#define INSTR_RS1(instr)      (((instr) >> 14) & 0x1F)
++#define INSTR_RD(instr)               (((instr) >> 25) & 0x1F)
++#define INSTR_IMM(instr)      (((instr) & 0x1000) ? ((instr) & 0xFFF) | 0xFFFFF000 : (instr) & 0xFFF)
++
++#define Ticc_COND(instr)      INSTR_RD(instr)
++#define Ticc_TA                       8
++
++#endif /* _ELAN3_THREAD_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/include/elan3/threadlinkage.h
+===================================================================
+--- linux-2.6.5.orig/include/elan3/threadlinkage.h     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan3/threadlinkage.h  2005-05-11 12:10:12.596908560 -0400
+@@ -0,0 +1,103 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN3_THREADLINKAGE_H
++#define       __ELAN3_THREADLINKAGE_H
++
++#ident "$Id: threadlinkage.h,v 1.6 2002/08/09 11:23:34 addy Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/threadlinkage.h,v $*/
++
++#ifdef        __cplusplus
++extern "C" {
++#endif
++
++#if defined(_ASM) || defined(__LANGUAGE_ASSEMBLY__)
++
++/*
++ * Macro to define weak symbol aliases. These are similar to the ANSI-C
++ *    #pragma weak name = _name
++ * except a compiler can determine type. The assembler must be told. Hence,
++ * the second parameter must be the type of the symbol (i.e.: function,...)
++ */
++#define       ANSI_PRAGMA_WEAK(sym, stype)    \
++      .weak   sym; \
++      .type sym, #stype; \
++/* CSTYLED */ \
++sym   = _/**/sym
++
++/*
++ * ENTRY provides the standard procedure entry code
++ */
++#define       ENTRY(x) \
++      .section        ".text"; \
++      .align  4; \
++      .global x; \
++x:
++
++/*
++ * ENTRY2 is identical to ENTRY but provides two labels for the entry point.
++ */
++#define       ENTRY2(x, y) \
++      .section        ".text"; \
++      .align  4; \
++      .global x, y; \
++/* CSTYLED */ \
++x:    ; \
++y:
++
++
++/*
++ * ALTENTRY provides for additional entry points.
++ */
++#define       ALTENTRY(x) \
++      .global x; \
++x:
++
++/*
++ * DGDEF and DGDEF2 provide global data declarations.
++ *
++ * DGDEF provides a word aligned word of storage.
++ *
++ * DGDEF2 allocates "sz" bytes of storage with **NO** alignment.  This
++ * implies this macro is best used for byte arrays.
++ *
++ * DGDEF3 allocates "sz" bytes of storage with "algn" alignment.
++ */
++#define       DGDEF2(name, sz) \
++      .section        ".data"; \
++      .global name; \
++      .size   name, sz; \
++name:
++
++#define       DGDEF3(name, sz, algn) \
++      .section        ".data"; \
++      .align  algn; \
++      .global name; \
++      .size   name, sz; \
++name:
++
++#define       DGDEF(name)     DGDEF3(name, 4, 4)
++
++/*
++ * SET_SIZE trails a function and set the size for the ELF symbol table.
++ */
++#define       SET_SIZE(x) \
++      .size   x, (.-x)
++
++#endif /* _ASM || __LANGUAGE_ASSEMBLY__ */
++
++#ifdef        __cplusplus
++}
++#endif
++
++#endif        /* __ELAN3_THREADLINKAGE_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/include/elan3/threadsyscall.h
+===================================================================
+--- linux-2.6.5.orig/include/elan3/threadsyscall.h     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan3/threadsyscall.h  2005-05-11 12:10:12.596908560 -0400
+@@ -0,0 +1,64 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN3_SYSCALL_H
++#define __ELAN3_SYSCALL_H
++
++#ident "$Id: threadsyscall.h,v 1.12 2003/09/24 13:57:24 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/threadsyscall.h,v $*/
++
++/* 
++ * This file contains the system calls supported from the Elan.
++ */
++#define ELAN3_DEBUG_TRAPNUM   5       /* thread debugging trap */
++#define ELAN3_ABORT_TRAPNUM   6       /* bad abort trap */
++#define ELAN3_ELANCALL_TRAPNUM        7       /* elansyscall trap */
++#define ELAN3_SYSCALL_TRAPNUM 8       /* new syscall trap */
++
++#define ELAN3_T_SYSCALL_CODE  0       /* offsets in struct elan3_t_syscall */
++#define ELAN3_T_SYSCALL_ERRNO 4
++
++#define ELAN3_SYS_open                1
++#define ELAN3_SYS_close               2
++#define ELAN3_SYS_write               3
++#define ELAN3_SYS_read                4
++#define ELAN3_SYS_poll                5
++#define ELAN3_SYS_ioctl               6
++#define ELAN3_SYS_lseek               7
++#define ELAN3_SYS_mmap                8
++#define ELAN3_SYS_munmap      9
++#define ELAN3_SYS_kill                10
++#define ELAN3_SYS_getpid      11
++
++#if !defined(SYS_getpid) && defined(__NR_getxpid) 
++#define SYS_getpid __NR_getxpid               /* for linux */
++#endif
++
++#if !defined(_ASM) && !defined(__LANGUAGE_ASSEMBLY__)
++
++extern int     elan3_t_open (const char *, int, ...);
++extern ssize_t elan3_t_write (int, const void *, unsigned);
++extern ssize_t elan3_t_read(int, void *, unsigned);
++extern int     elan3_t_ioctl(int, int, ...);
++extern int     elan3_t_close(int);
++extern off_t   elan3_t_lseek(int filedes, off_t offset, int whence);
++
++extern caddr_t elan3_t_mmap(caddr_t, size_t, int, int, int, off_t);
++extern int     elan3_t_munmap(caddr_t, size_t);
++
++extern int     elan3_t_getpid(void);
++extern void    elan3_t_abort(char *str);
++
++#endif /* !_ASM && ! __LANGUAGE_ASSEMBLY__ */
++
++#endif /* __ELAN3_SYSCALL_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/include/elan3/trtype.h
+===================================================================
+--- linux-2.6.5.orig/include/elan3/trtype.h    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan3/trtype.h 2005-05-11 12:10:12.597908408 -0400
+@@ -0,0 +1,116 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef _ELAN3_TRTYPE_H
++#define _ELAN3_TRTYPE_H
++
++#ident "$Id: trtype.h,v 1.13 2002/08/09 11:23:34 addy Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/trtype.h,v $ */
++
++/*<15>        ackNow  */
++#define TR_SENDACK    (1 << 15)
++
++#define TR_SIZE_SHIFT 12
++#define TR_SIZE_MASK  7
++
++/*<14:12> Size        0, 1, 2, 4, 8, 16, 32, 64  Double Words
++          Bit 14 is forced to zero currently so that only size 0, 1, 2, 4 are
++        allowed    */
++
++#define TR_SIZE0      (0 << TR_SIZE_SHIFT)
++#define TR_SIZE1      (1 << TR_SIZE_SHIFT)
++#define TR_SIZE2      (2 << TR_SIZE_SHIFT)
++#define TR_SIZE4      (3 << TR_SIZE_SHIFT)
++#define TR_SIZE8      (4 << TR_SIZE_SHIFT)
++
++#define TR_64_BIT_ADDR        (1 << 11)
++#define TR_LAST_TRANS (1 << 10)
++
++#define TR_WRITEBLOCK_BIT     (1 << 9)
++#define TR_WRITEBLOCK         (TR_WRITEBLOCK_BIT | TR_SIZE8)
++
++
++#define TR_WRITEBLOCK_SIZE    64
++
++/*
++ * write-block
++ */
++/*    WriteBlock      <8:7>   Data type
++                      <6:0>   Part write size */
++#define TR_TYPE_SHIFT 7
++#define TR_TYPE_MASK  ((1 << 2) - 1)
++
++#define TR_TYPE_BYTE  0
++#define TR_TYPE_SHORT 1
++#define TR_TYPE_WORD  2
++#define TR_TYPE_DWORD 3
++
++#define TR_PARTSIZE_MASK ((1 << 7) -1)
++
++#define TR_WAIT_FOR_EOP       (1 << 8)
++
++/*
++ * trace-route format 
++ */
++#define TR_TRACEROUTE0_CHANID(val)            ((val) & 1)                     /* 0     Chan Id */
++#define TR_TRACEROUTE0_LINKID(val)            (((val) >> 1) & 7)              /* 1:3   Link Id */
++#define TR_TRACEROUTE0_REVID(val)             (((val) >> 4) & 7)              /* 4:6   Revision ID */
++#define TR_TRACEROUTE0_BCAST_TOP_PIN(val)     (((val) >> 7) & 1)              /* 7     Broadcast Top Pin (REV B) */
++#define TR_TRACEROUTE0_LNR(val)                       ((val) >> 8)                    /* 8:15  Global Link Not Ready */
++
++#define TR_TRACEROUTE1_PRIO(val)              ((val & 0xF))                   /* 0:3   Arrival Priority (REV A) */
++#define TR_TRACEROUTE1_AGE(val)                       (((val) >> 4) & 0xF)            /* 4:7   Priority Held(Age) (REV A) */
++#define TR_TRACEROUTE1_ROUTE_SELECTED(val)    ((val) & 0xFF)                  /* 0:7   Arrival age (REV B) */
++#define TR_TRACEROUTE1_BCAST_TOP(val)         (((val) >> 8) & 7)              /* 8:10  Broadcast Top */
++#define TR_TRACEROUTE1_ADAPT(val)             (((val) >> 12) & 3)             /* 12:13 This Adaptive Value (REV A) */
++#define TR_TRACEROUTE1_BCAST_BOT(val)         (((val) >> 12) & 7)             /* 12:14 Broadcast Bottom (REV B) */
++
++#define TR_TRACEROUTE2_ARRIVAL_AGE(val)               ((val) & 0xF)                   /* 0:3   Arrival Age (REV B) */
++#define TR_TRACEROUTE2_CURR_AGE(val)          (((val) >> 4) & 0xF)            /* 4:7   Current Age (REV B) */
++#define TR_TRACEROUTE2_BUSY(val)              (((val) >> 8) & 0xFF)           /* 8:15  Busy (REV B) */
++
++#define TR_TRACEROUTE_SIZE    32
++#define TR_TRACEROUTE_ENTRIES (TR_TRACEROUTE_SIZE/2)
++
++/*
++ * non-write block
++ */
++#define TR_OPCODE_MASK                (((1 << 8) - 1) |                       \
++                               (TR_SIZE_MASK << TR_SIZE_SHIFT) |      \
++                               TR_WRITEBLOCK_BIT)
++
++#define TR_NOP_TRANS          (0x0 | TR_SIZE0)
++#define TR_SETEVENT           (0x0 | TR_SIZE0 | TR_SENDACK | TR_LAST_TRANS)
++#define TR_REMOTEDMA          (0x1 | TR_SIZE4 | TR_SENDACK | TR_LAST_TRANS)
++#define TR_LOCKQUEUE          (0x2 | TR_SIZE0)
++#define TR_UNLOCKQUEUE                (0x3 | TR_SIZE0 | TR_SENDACK | TR_LAST_TRANS)
++
++#define TR_SENDDISCARD                (0x4 | TR_SIZE0)
++#define TR_TRACEROUTE         (0x5 | TR_SIZE4)
++
++#define TR_DMAIDENTIFY                (0x6 | TR_SIZE0)
++#define TR_THREADIDENTIFY     (0x7 | TR_SIZE1)
++
++#define TR_GTE                        (0x8 | TR_SIZE1)
++#define TR_LT                 (0x9 | TR_SIZE1)
++#define TR_EQ                 (0xA | TR_SIZE1)
++#define TR_NEQ                        (0xB | TR_SIZE1)
++
++#define TR_WRITEWORD          (0xC | TR_SIZE1)
++#define TR_WRITEDOUBLEWORD    (0xD | TR_SIZE1)
++#define TR_TESTANDWRITE       (0xE | TR_SIZE1)
++#define TR_ATOMICADDWORD      (0xF | TR_SIZE1 | TR_SENDACK | TR_LAST_TRANS)
++#define TR_OPCODE_TYPE_MASK   0xff
++
++
++#endif /* notdef _ELAN3_TRTYPE_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/include/elan3/urom_addrs.h
+===================================================================
+--- linux-2.6.5.orig/include/elan3/urom_addrs.h        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan3/urom_addrs.h     2005-05-11 12:10:12.597908408 -0400
+@@ -0,0 +1,262 @@
++#define MI_WaitForRemoteDescRead 0x0
++#define MI_WaitForRemoteDescRead2 0x1
++#define MI_WaitForRemoteDescRead2_seq1 0x2
++#define MI_SendRemoteDmaRoutes 0x3
++#define MI_IProcTrapped 0x4
++#define MI_DProcTrapped 0x5
++#define MI_CProcTrapped 0x6
++#define MI_TProcTrapped 0x7
++#define MI_TestWhichDmaQueue 0x8
++#define MI_TestWhichDmaQueue_seq1 0x9
++#define MI_InputRemoteDmaUpdateBPtr 0xa
++#define MI_FixupQueueContextAndRemoteBit 0xb
++#define MI_FixupQueueContextAndRemoteBit_seq1 0xc
++#define MI_FixupQueueContextAndRemoteBit_seq2 0xd
++#define MI_FixupQueueContextAndRemoteBit_seq3 0xe
++#define MI_FixupQueueContextAndRemoteBit_seq4 0xf
++#define MI_RunDmaCommand 0x10
++#define MI_DoSendRemoteDmaDesc 0x11
++#define MI_DequeueNonSysCntxDma 0x12
++#define MI_WaitForRemoteDescRead1 0x13
++#define MI_RemoteDmaCommand 0x14
++#define MI_WaitForRemoteRoutes 0x15
++#define MI_DequeueSysCntxDma 0x16
++#define MI_ExecuteDmaDescriptorForQueue 0x17
++#define MI_ExecuteDmaDescriptor1 0x18
++#define MI_ExecuteDmaDescriptor1_seq1 0x19
++#define MI_ExecuteDmaDescriptor1_seq2 0x1a
++#define MI_ExecuteDmaDescriptor1_seq3 0x1b
++#define MI_GetNewSizeInProg 0x1c
++#define MI_GetNewSizeInProg_seq1 0x1d
++#define MI_FirstBlockRead 0x1e
++#define MI_ExtraFirstBlockRead 0x1f
++#define MI_UnimplementedError 0x20
++#define MI_UpdateDescriptor 0x21
++#define MI_UpdateDescriptor_seq1 0x22
++#define MI_UpdateDescriptor_seq2 0x23
++#define MI_UpdateDescriptor_seq3 0x24
++#define MI_UpdateDescriptor_seq4 0x25
++#define MI_UpdateDescriptor_seq5 0x26
++#define MI_GetNextSizeInProg 0x27
++#define MI_DoStopThisDma 0x28
++#define MI_DoStopThisDma_seq1 0x29
++#define MI_GenNewBytesToRead 0x2a
++#define MI_WaitForEventReadTy1 0x2b
++#define MI_WaitUpdateEvent 0x2c
++#define MI_WaitUpdateEvent_seq1 0x2d
++#define MI_DoSleepOneTickThenRunable 0x2e
++#define MI_RunEvent 0x2f
++#define MI_EnqueueThread 0x30
++#define MI_CheckContext0 0x31
++#define MI_EnqueueDma 0x32
++#define MI_CprocTrapping 0x33
++#define MI_CprocTrapping_seq1 0x34
++#define MI_WaitForRemoteRoutes1 0x35
++#define MI_SetEventCommand 0x36
++#define MI_DoSetEvent 0x37
++#define MI_DoRemoteSetEventNowOrTrapQueueingDma 0x38
++#define MI_DoRemoteSetEventNowOrTrapQueueingDma_seq1 0x39
++#define MI_SendRemoteDmaRoutes2 0x3a
++#define MI_WaitForRemoteRoutes2 0x3b
++#define MI_WaitEventCommandTy0 0x3c
++#define MI_DequeueNonSysCntxDma2 0x3d
++#define MI_WaitEventCommandTy1 0x3e
++#define MI_WaitEventCommandTy1_seq1 0x3f
++#define MI_DequeueNonSysCntxThread 0x40
++#define MI_DequeueSysCntxDma1 0x41
++#define MI_DequeueSysCntxThread 0x42
++#define MI_TestNonSysCntxDmaQueueEmpty 0x43
++#define MI_TestNonSysCntxDmaQueueEmpty_seq1 0x44
++#define MI_TestNonSysCntxDmaQueueEmpty_seq2 0x45
++#define MI_RunThreadCommand 0x46
++#define MI_SetEventWaitForLastAcess 0x47
++#define MI_SetEventReadWait 0x48
++#define MI_SetEventReadWait_seq1 0x49
++#define MI_TestEventType 0x4a
++#define MI_TestEventType_seq1 0x4b
++#define MI_TestEventBit2 0x4c
++#define MI_DmaDescOrBlockCopyOrChainedEvent 0x4d
++#define MI_RunThread 0x4e
++#define MI_RunThread1 0x4f
++#define MI_RunThread1_seq1 0x50
++#define MI_IncDmaSysCntxBPtr 0x51
++#define MI_IncDmaSysCntxBPtr_seq1 0x52
++#define MI_IncDmaSysCntxBPtr_seq2 0x53
++#define MI_WaitForCntxDmaDescRead 0x54
++#define MI_FillInContext 0x55
++#define MI_FillInContext_seq1 0x56
++#define MI_WriteNewDescToQueue 0x57
++#define MI_WriteNewDescToQueue_seq1 0x58
++#define MI_TestForQueueWrap 0x59
++#define MI_TestForQueueWrap_seq1 0x5a
++#define MI_TestQueueIsFull 0x5b
++#define MI_TestQueueIsFull_seq1 0x5c
++#define MI_TestQueueIsFull_seq2 0x5d
++#define MI_CheckPsychoShitFixup 0x5e
++#define MI_PsychoShitFixupForcedRead 0x5f
++#define MI_PrepareDMATimeSlice 0x60
++#define MI_PrepareDMATimeSlice_seq1 0x61
++#define MI_TProcRestartFromTrapOrTestEventBit2 0x62
++#define MI_TProcRestartFromTrapOrTestEventBit2_seq1 0x63
++#define MI_WaitForGlobalsRead 0x64
++#define MI_WaitForNPCRead 0x65
++#define MI_EventInterrupt 0x66
++#define MI_EventInterrupt_seq1 0x67
++#define MI_EventInterrupt_seq2 0x68
++#define MI_EventInterrupt_seq3 0x69
++#define MI_TestSysCntxDmaQueueEmpty 0x6a
++#define MI_TestSysCntxDmaQueueEmpty_seq1 0x6b
++#define MI_TestIfRemoteDesc 0x6c
++#define MI_DoDmaLocalSetEvent 0x6d
++#define MI_DoDmaLocalSetEvent_seq1 0x6e
++#define MI_DoDmaLocalSetEvent_seq2 0x6f
++#define MI_DmaLoop1 0x70
++#define MI_ExitDmaLoop 0x71
++#define MI_ExitDmaLoop_seq1 0x72
++#define MI_RemoteDmaTestPAckType 0x73
++#define MI_PacketDiscardOrTestFailRecIfCCis0 0x74
++#define MI_PacketDiscardOrTestFailRecIfCCis0_seq1 0x75
++#define MI_TestNackFailIsZero2 0x76
++#define MI_TestNackFailIsZero3 0x77
++#define MI_DmaFailCountError 0x78
++#define MI_TestDmaForSysCntx 0x79
++#define MI_TestDmaForSysCntx_seq1 0x7a
++#define MI_TestDmaForSysCntx_seq2 0x7b
++#define MI_TestAeqB2 0x7c
++#define MI_TestAeqB2_seq1 0x7d
++#define MI_GetNextDmaDescriptor 0x7e
++#define MI_DequeueSysCntxDma2 0x7f
++#define MI_InputSetEvent 0x80
++#define MI_PutBackSysCntxDma 0x81
++#define MI_PutBackSysCntxDma_seq1 0x82
++#define MI_PutBackSysCntxDma_seq2 0x83
++#define MI_InputRemoteDma 0x84
++#define MI_InputRemoteDma_seq1 0x85
++#define MI_WaitOneTickForWakeup1 0x86
++#define MI_SendRemoteDmaDesc 0x87
++#define MI_InputLockQueue 0x88
++#define MI_CloseTheTrappedPacketIfCCis1 0x89
++#define MI_CloseTheTrappedPacketIfCCis1_seq1 0x8a
++#define MI_PostDmaInterrupt 0x8b
++#define MI_InputUnLockQueue 0x8c
++#define MI_WaitForUnLockDescRead 0x8d
++#define MI_SendEOPforRemoteDma 0x8e
++#define MI_LookAtRemoteAck 0x8f
++#define MI_InputWriteBlockQueue 0x90
++#define MI_WaitForSpStore 0x91
++#define MI_TProcNext 0x92
++#define MI_TProcStoppedRunning 0x93
++#define MI_InputWriteBlock 0x94
++#define MI_RunDmaOrDeqNonSysCntxDma 0x95
++#define MI_ExecuteDmaDescriptorForRun 0x96
++#define MI_ConfirmQueueLock 0x97
++#define MI_DmaInputIdentify 0x98
++#define MI_TProcStoppedRunning2 0x99
++#define MI_TProcStoppedRunning2_seq1 0x9a
++#define MI_TProcStoppedRunning2_seq2 0x9b
++#define MI_ThreadInputIdentify 0x9c
++#define MI_InputIdWriteAddrAndType3 0x9d
++#define MI_IProcTrappedWriteStatus 0x9e
++#define MI_FinishTrappingEop 0x9f
++#define MI_InputTestTrans 0xa0
++#define MI_TestAeqB3 0xa1
++#define MI_ThreadUpdateNonSysCntxBack 0xa2
++#define MI_ThreadQueueOverflow 0xa3
++#define MI_RunContext0Thread 0xa4
++#define MI_RunContext0Thread_seq1 0xa5
++#define MI_RunContext0Thread_seq2 0xa6
++#define MI_RunDmaDesc 0xa7
++#define MI_RunDmaDesc_seq1 0xa8
++#define MI_RunDmaDesc_seq2 0xa9
++#define MI_TestAeqB 0xaa
++#define MI_WaitForNonCntxDmaDescRead 0xab
++#define MI_DmaQueueOverflow 0xac
++#define MI_BlockCopyEvent 0xad
++#define MI_BlockCopyEventReadBlock 0xae
++#define MI_BlockCopyWaitForReadData 0xaf
++#define MI_InputWriteWord 0xb0
++#define MI_TraceSetEvents 0xb1
++#define MI_TraceSetEvents_seq1 0xb2
++#define MI_TraceSetEvents_seq2 0xb3
++#define MI_InputWriteDoubleWd 0xb4
++#define MI_SendLockTransIfCCis1 0xb5
++#define MI_WaitForDmaRoutes1 0xb6
++#define MI_LoadDmaContext 0xb7
++#define MI_InputTestAndSetWord 0xb8
++#define MI_InputTestAndSetWord_seq1 0xb9
++#define MI_GetDestEventValue 0xba
++#define MI_SendDmaIdentify 0xbb
++#define MI_InputAtomicAddWord 0xbc
++#define MI_LoadBFromTransD0 0xbd
++#define MI_ConditionalWriteBackCCTrue 0xbe
++#define MI_WaitOneTickForWakeup 0xbf
++#define MI_SendFinalUnlockTrans 0xc0
++#define MI_SendDmaEOP 0xc1
++#define MI_GenLastAddrForPsycho 0xc2
++#define MI_FailedAckIfCCis0 0xc3
++#define MI_FailedAckIfCCis0_seq1 0xc4
++#define MI_WriteDmaSysCntxDesc 0xc5
++#define MI_TimesliceDmaQueueOverflow 0xc6
++#define MI_DequeueNonSysCntxThread1 0xc7
++#define MI_DequeueNonSysCntxThread1_seq1 0xc8
++#define MI_TestThreadQueueEmpty 0xc9
++#define MI_ClearThreadQueueIfCC 0xca
++#define MI_DequeueSysCntxThread1 0xcb
++#define MI_DequeueSysCntxThread1_seq1 0xcc
++#define MI_TProcStartUpGeneric 0xcd
++#define MI_WaitForPCload2 0xce
++#define MI_WaitForNPCWrite 0xcf
++#define MI_WaitForEventWaitAddr 0xd0
++#define MI_WaitForWaitEventAccess 0xd1
++#define MI_WaitForWaitEventAccess_seq1 0xd2
++#define MI_WaitForWaitEventDesc 0xd3
++#define MI_WaitForEventReadTy0 0xd4
++#define MI_SendCondTestFail 0xd5
++#define MI_InputMoveToNextTrans 0xd6
++#define MI_ThreadUpdateSysCntxBack 0xd7
++#define MI_FinishedSetEvent 0xd8
++#define MI_EventIntUpdateBPtr 0xd9
++#define MI_EventQueueOverflow 0xda
++#define MI_MaskLowerSource 0xdb
++#define MI_DmaLoop 0xdc
++#define MI_SendNullSetEvent 0xdd
++#define MI_SendFinalSetEvent 0xde
++#define MI_TestNackFailIsZero1 0xdf
++#define MI_DmaPacketTimedOutOrPacketError 0xe0
++#define MI_NextPacketIsLast 0xe1
++#define MI_TestForZeroLengthDma 0xe2
++#define MI_WaitForPCload 0xe3
++#define MI_ReadInIns 0xe4
++#define MI_WaitForInsRead 0xe5
++#define MI_WaitForLocals 0xe6
++#define MI_WaitForOutsWrite 0xe7
++#define MI_WaitForWaitEvWrBack 0xe8
++#define MI_WaitForLockRead 0xe9
++#define MI_TestQueueLock 0xea
++#define MI_InputIdWriteAddrAndType 0xeb
++#define MI_InputIdWriteAddrAndType2 0xec
++#define MI_ThreadInputIdentify2 0xed
++#define MI_WriteIntoTrapArea0 0xee
++#define MI_GenQueueBlockWrAddr 0xef
++#define MI_InputDiscardFreeLock 0xf0
++#define MI_WriteIntoTrapArea1 0xf1
++#define MI_WriteIntoTrapArea2 0xf2
++#define MI_ResetBPtrToBase 0xf3
++#define MI_InputDoTrap 0xf4
++#define MI_RemoteDmaCntxt0Update 0xf5
++#define MI_ClearQueueLock 0xf6
++#define MI_IProcTrappedBlockWriteData 0xf7
++#define MI_FillContextFilter 0xf8
++#define MI_IProcTrapped4 0xf9
++#define MI_RunSysCntxDma 0xfa
++#define MI_ChainedEventError 0xfb
++#define MI_InputTrappingEOP 0xfc
++#define MI_CheckForRunIfZero 0xfd
++#define MI_TestForBreakOrSuspend 0xfe
++#define MI_SwapForRunable 0xff
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/include/elan3/vmseg.h
+===================================================================
+--- linux-2.6.5.orig/include/elan3/vmseg.h     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan3/vmseg.h  2005-05-11 12:10:12.598908256 -0400
+@@ -0,0 +1,75 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef _VM_SEG_ELAN3_H
++#define _VM_SEG_ELAN3_H
++
++#ident "$Id: vmseg.h,v 1.20 2003/09/24 13:57:24 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/vmseg.h,v $*/
++
++#include <elan3/elanuregs.h>
++
++/*
++ * This segment maps Elan registers,  it is fixed size and has 8K 
++ * pages split up as follows
++ *
++ *    ----------------------------------------
++ *    |    Performance Counters (read-only)  |
++ *    ----------------------------------------
++ *    |    Flag Page (read-only)           |
++ *    ----------------------------------------
++ *    |    Command Port                            |
++ *    ----------------------------------------
++ */
++typedef volatile struct elan3_flagstats 
++{
++    u_int     CommandFlag;
++    u_int     PageFaults;
++    u_int     CProcTraps;
++    u_int     DProcTraps;
++    u_int     TProcTraps;
++    u_int     IProcTraps;
++    u_int     EopBadAcks;
++    u_int     EopResets;
++    u_int     DmaNetworkErrors;
++    u_int     DmaIdentifyNetworkErrors;
++    u_int     ThreadIdentifyNetworkErrors;
++    u_int     DmaRetries;
++    u_int     ThreadSystemCalls;
++    u_int     ThreadElanCalls;
++    u_int     LoadVirtualProcess;
++} ELAN3_FLAGSTATS;
++
++#ifdef DIGITAL_UNIX
++typedef volatile union elan3_flagpage
++{
++    u_char       Padding[8192];
++    ELAN3_FLAGSTATS Stats;
++} ELAN3_FLAGPAGE;
++
++typedef volatile struct elan3_vmseg
++{
++    E3_CommandPort CommandPort;
++    ELAN3_FLAGPAGE  FlagPage;
++    E3_User_Regs   UserRegs;
++} ELAN3_VMSEG;
++
++#define SEGELAN3_SIZE   (sizeof (ELAN3_VMSEG))
++
++#define SEGELAN3_COMMAND_PORT 0
++#define SEGELAN3_FLAG_PAGE    1
++#define SEGELAN3_PERF_COUNTERS        2
++
++#endif /* DIGITAL_UNIX */
++
++#endif        /* _VM_SEG_ELAN3_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/include/elan3/vpd.h
+===================================================================
+--- linux-2.6.5.orig/include/elan3/vpd.h       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan3/vpd.h    2005-05-11 12:10:12.598908256 -0400
+@@ -0,0 +1,47 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "$Id: vpd.h,v 1.5 2002/08/09 11:23:34 addy Exp $"
++/*      $Source: /cvs/master/quadrics/elan3mod/elan3/elan3/vpd.h,v $*/
++
++#ifndef __ELAN3_VPD_H
++#define __ELAN3_VPD_H
++
++#define LARGE_RESOURCE_BIT                    0x80
++
++#define SMALL_RESOURCE_COMPATIBLE_DEVICE_ID   0x3
++#define SMALL_RESOURCE_VENDOR_DEFINED         0xE
++#define SMALL_RESOURCE_END_TAG                        0xF
++
++#define LARGE_RESOURCE_STRING                 0x2
++#define LARGE_RESOURCE_VENDOR_DEFINED         0x4
++#define LARGE_RESOURCE_VITAL_PRODUCT_DATA     0x10
++
++#define VPD_PART_NUMBER                       "PN"
++#define VPD_FRU_PART_NUMBER           "FN"
++#define VPD_EC_LEVEL                  "EC"
++#define VPD_MANUFACTURE_ID            "MN"
++#define VPD_SERIAL_NUMBER             "SN"
++
++#define VPD_LOAD_ID                   "LI"
++#define VPD_ROM_LEVEL                 "RL"
++#define VPD_ALTERABLE_ROM_LEVEL               "RM"
++#define VPD_NETWORK_ADDRESS           "NA"
++#define VPD_DEVICE_DRIVER_LEVEL               "DD"
++#define VPD_DIAGNOSTIC_LEVEL          "DG"
++#define VPD_LOADABLE_MICROCODE_LEVEL  "LL"
++#define VPD_VENDOR_ID                 "VI"
++#define VPD_FUNCTION_NUMBER           "FU"
++#define VPD_SUBSYSTEM_VENDOR_ID               "SI"
++
++#endif /* __ELAN3_VPD_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/include/elan4/commands.h
+===================================================================
+--- linux-2.6.5.orig/include/elan4/commands.h  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan4/commands.h       2005-05-11 12:10:12.599908104 -0400
+@@ -0,0 +1,247 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN4_COMMANDS_H
++#define __ELAN4_COMMANDS_H
++
++#ident "$Id: commands.h,v 1.29 2004/06/16 15:45:02 addy Exp $"
++/*      $Source: /cvs/master/quadrics/elan4hdr/commands.h,v $*/
++
++/*
++ * This header file describes the command format for the Elan 4
++ *    See CommandFormat.doc
++ */
++
++/*
++ * Number of channels in traced elanlib_trace.c
++ */
++#define TRACE_MAX_CHANNELS    2
++
++/*
++ * Define encoding for the commands issued into the command queues
++ */
++#define RUN_THREAD_CMD       0x00
++#define OPEN_STEN_PKT_CMD    0x01
++#define WRITE_DWORD_CMD      0x02
++#define ADD_DWORD_CMD        0x03
++#define COPY64_CMD           0x05
++#define GUARD_CMD            0x06
++#define SET_EVENT_CMD        0x07
++#define SEND_TRANS_CMD       0x09
++#define INTERRUPT_CMD        0x0d
++#define RUN_DMA_CMD          0x0e
++#define SET_EVENTN_CMD       0x0f
++#define NOP_CMD                    0x17
++#define MAKE_EXT_CLEAN_CMD   0x37
++#define WAIT_EVENT_CMD       0x1f
++
++/*
++ * Define the portion of the data word the user is NOT
++ * allowed to use. This varies with Commmand type
++ */
++#define RUN_THREAD_CMD_MASK       0x03
++#define OPEN_STEN_PKT_CMD_MASK    0x0f
++#define WRITE_DWORD_CMD_MASK      0x07
++#define ADD_DWORD_CMD_MASK        0x07
++#define COPY64_CMD_MASK           0x0f
++#define GUARD_CMD_MASK            0x0f
++#define SET_EVENT_CMD_MASK        0x1f
++#define SEND_TRANS_CMD_MASK       0x1f
++#define INTERRUPT_CMD_MASK        0x0f
++#define RUN_DMA_CMD_MASK          0x0f
++#define SET_EVENTN_CMD_MASK       0x1f
++#define NOP_CMD_MASK            0x3f
++#define MAKE_EXT_CLEAN_MASK     0x3f
++#define WAIT_EVENT_CMD_MASK       0x1f
++
++#define COPY64_DATA_TYPE_SHIFT    0x4
++#define COPY64_DTYPE_BYTE       (0 << COPY64_DATA_TYPE_SHIFT)
++#define COPY64_DTYPE_SHORT      (1 << COPY64_DATA_TYPE_SHIFT)
++#define COPY64_DTYPE_WORD       (2 << COPY64_DATA_TYPE_SHIFT)
++#define COPY64_DTYPE_LONG       (3 << COPY64_DATA_TYPE_SHIFT)
++
++/*
++ * SET_EVENTN - word 1 has following form
++ * [63:5]     Event Address
++ * [4:0]      Part Set Value.
++ */
++#define SET_EVENT_PART_SET_MASK      0x1f
++
++/* OPEN_STEN_PKT_CMD 
++ *   [63:32]  Vproc
++ *   [31]     Use Test
++ *   [30:28]  unused
++ *   [27:21]  Test Acceptable PAck code
++ *   [20:16]  Test Ack Channel Number
++ *   [15:9]   Acceptable PAck code
++ *   [8:4]    Ack Channel Number (1 bit on Elan4)
++ *   [3:0]    Command type
++ */
++/* Acceptable PAck code */
++#define PACK_OK                       (1 << 0)
++#define PACK_TESTFAIL         (1 << 1)
++#define PACK_DISCARD          (1 << 2)
++#define RESTART_COUNT_ZERO    (1 << 3)
++#define PACK_ERROR            (1 << 7)
++#define PACK_TIMEOUT          (1 << 8)
++
++/*
++ *#ifndef USE_DIRTY_COMMANDS
++ *#define USE_DIRTY_COMMANDS
++ *#endif
++ */
++#ifdef USE_DIRTY_COMMANDS
++#define OPEN_PACKET_USED_MASK    0x00000000780f00e0ULL
++#define SEND_TRANS_USED_MASK     0xffffffff0000fff0ULL
++#define COPY64_WRITE_USED_MASK   0x000000000000000fULL
++#define MAIN_INT_USED_MASK       0x0000000000003ff0ULL
++#define GUARD_USED_MASK          0xfffffe007000fde0ULL
++#define DMA_TYPESIZE_USED_MASK   0x000000000000fff0ULL
++#define SETEVENTN_USED_MASK      0xffffffffffffffe0ULL
++#define NOP_USED_MASK            0xffffffffffffffc0ULL
++#define EXT_CLEAN_USED_MASK      0xffffffffffffffc0ULL
++#define WAIT_CNT_TYPE_USED_MASK  0x00000000fffff800ULL
++#else
++#define OPEN_PACKET_USED_MASK    0x0ULL
++#define SEND_TRANS_USED_MASK     0x0ULL
++#define COPY64_WRITE_USED_MASK   0x0ULL
++#define MAIN_INT_USED_MASK       0x0ULL
++#define GUARD_USED_MASK          0x0ULL
++#define DMA_TYPESIZE_USED_MASK   0x0ULL
++#define SETEVENTN_USED_MASK      0x0ULL
++#define NOP_USED_MASK            0x0ULL
++#define EXT_CLEAN_USED_MASK      0x0ULL
++#define WAIT_CNT_TYPE_USED_MASK  0x0ULL
++#endif
++
++#define OPEN_PACKET(chan, code, vproc) \
++      ((((chan) & 1) << 4) | (((code) & 0x7f) << 9) | ((E4_uint64)(vproc) << 32) | OPEN_STEN_PKT_CMD)
++
++#define OPEN_PACKET_TEST(chan, code, vproc, tchan, tcode) \
++      ((((chan) & 1) << 4) | (((code) & 0x7f) << 9) | ((E4_uint64)(vproc) << 32) | \
++       (((tchan) & 1) << 16) | (((tcode) & 0x7f) << 21) | (((E4_uint64) 1) << 31) | OPEN_STEN_PKT_CMD)
++
++/*
++ * GUARD_CMD
++ *   [63:41]  unused
++ *   [40]     Reset Restart Fail Count        // only performed if the Guard executes the next command.
++ *   [39:32]  New Restart Fail Count value
++ *   [31]     Use Test
++ *   [30:28]  unused
++ *   [27:21]  Test Acceptable PAck code
++ *   [20:16]  Test Ack Channel Number
++ *   [15:9]   unused
++ *   [8:4]    Ack Channel Number
++ *   [3:0]    Command type
++ */
++/* GUARD_CHANNEL(chan)
++ */
++#define GUARD_ALL_CHANNELS    ((1 << 9) | GUARD_CMD)
++#define GUARD_CHANNEL(chan)   ((((chan) & 1) << 4) | GUARD_CMD)
++#define GUARD_TEST(chan,code) ((1ull << 31) | (((code) & 0x7f) << 21) | (((chan) & 1) << 16))
++#define GUARD_RESET(count)    ((1ull << 40) | ((((E4_uint64) count) & 0xff) << 32))
++
++#define GUARD_CHANNEL_TEST(chan,tchan,tcode) \
++      ((((chan) & 1) << 4) | (((tchan) & 1) << 16) | (((tcode) & 0x7f) << 21) | \
++       (((E4_uint64) 1) << 31) | GUARD_CMD)
++
++/*
++ * SEND_TRANS_CMD
++ * [63:32]    unused
++ * [31:16]    transaction type
++ * [15:4]     unused
++ * [3:0]      Command type
++ */
++#define SEND_TRANS(TransType) (((TransType) << 16) | SEND_TRANS_CMD)
++
++/*
++ * Command port trace debug levels
++ */
++#define TRACE_CMD_BUFFER      0x01
++#define TRACE_CMD_TYPE                0x02
++#define TRACE_CHANNEL_OPENS   0x04
++#define TRACE_GUARDED_ATOMICS 0x08
++#define TRACE_CMD_TIMEOUT     0x10
++
++/*
++ * Commands that should be preceeded by a GUARD_CMD.
++ */
++#define IS_ATOMIC_CMD(cmd)                                                            \
++   ((cmd) == RUN_THREAD_CMD || (cmd) == ADD_DWORD_CMD || (cmd) == INTERRUPT_CMD ||    \
++    (cmd) == RUN_DMA_CMD    || (cmd) == SET_EVENT_CMD || (cmd) == SET_EVENTN_CMD ||   \
++    (cmd) == WAIT_EVENT_CMD)
++
++#ifndef _ASM
++
++/*
++ * These structures are used to build event copy command streams. They are intended to be included
++ * in a larger structure to form a self documenting command sequence that can be easily coped and manipulated.
++ */
++
++typedef struct e4_runthreadcmd
++{
++   E4_Addr    PC;
++   E4_uint64  r[6];
++} E4_RunThreadCmd;
++
++typedef E4_uint64 E4_OpenCmd;
++
++typedef struct e4_writecmd
++{
++   E4_Addr    WriteAddr;
++   E4_uint64  WriteValue;
++} E4_WriteCmd;
++
++typedef struct e4_addcmd
++{
++   E4_Addr    AddAddr;
++   E4_uint64  AddValue;
++} E4_AddCmd;
++
++typedef struct e4_copycmd
++{
++   E4_Addr    SrcAddr;
++   E4_Addr    DstAddr;
++} E4_CopyCmd;
++
++typedef E4_uint64 E4_GaurdCmd;
++typedef E4_uint64 E4_SetEventCmd;
++
++/*
++ * The data to this command must be declared as a vector after the use of this.
++ */
++typedef struct e4_sendtranscmd
++{
++   E4_Addr    Type;
++   E4_Addr    Addr;
++} E4_SendTransCmd;
++
++typedef E4_uint64 E4_IntCmd;
++
++/* The normal Dma struc can be used here. */
++
++typedef struct e4_seteventncmd
++{
++   E4_Addr    Event;
++   E4_Addr    SetCount;
++} E4_SetEventNCmd;
++
++typedef E4_uint64 E4_NopCmd;
++typedef E4_uint64 E4_MakeExtCleanCmd;
++
++typedef struct e4_waitcmd
++{
++   E4_Addr    ev_Event;
++   E4_Addr    ev_CountType;
++   E4_Addr    ev_Params[2];
++} E4_WaitCmd;
++
++#endif /* _ASM */
++
++#endif /* __ELAN4_COMMANDS_H  */
++
+Index: linux-2.6.5/include/elan4/debug.h
+===================================================================
+--- linux-2.6.5.orig/include/elan4/debug.h     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan4/debug.h  2005-05-11 12:10:12.599908104 -0400
+@@ -0,0 +1,113 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef _ELAN4_ELANDEBUG_H
++#define _ELAN4_ELANDEBUG_H
++
++#ident "$Id: debug.h,v 1.19.6.1 2005/01/18 14:36:10 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/debug.h,v $ */
++
++/* values for "type" field - note a "ctxt" is permissible */
++/* and BUFFER/CONSOLE are for explict calls to elan4_debugf() */
++#define DBG_DEVICE    ((void *) 0)
++#define DBG_USER      ((void *) 1)
++
++#define DBG_BUFFER    ((void *) 62)
++#define DBG_CONSOLE   ((void *) 63)
++#define DBG_NTYPES    64
++
++/* values for "mode" field */
++#define DBG_CONFIG    0x00000001
++#define DBG_INTR      0x00000002
++#define DBG_MAININT   0x00000004
++#define DBG_SDRAM     0x00000008
++#define DBG_MMU               0x00000010
++#define DBG_REGISTER  0x00000020
++#define DBG_CQ                0x00000040
++#define DBG_NETWORK_CTX       0x00000080
++
++#define DBG_FLUSH     0x00000100
++#define DBG_FILE      0x00000200
++#define DBG_CONTROL   0x00000400
++#define DBG_MEM               0x00000800
++
++#define DBG_PERM      0x00001000
++#define DBG_FAULT     0x00002000
++#define DBG_SWAP      0x00004000
++#define DBG_TRAP      0x00008000
++#define DBG_DDCQ      0x00010000
++#define DBG_VP                0x00020000
++#define DBG_RESTART   0x00040000
++#define DBG_RESUME    0x00080000
++#define DBG_CPROC     0x00100000
++#define DBG_DPROC     0x00200000
++#define DBG_EPROC     0x00400000
++#define DBG_IPROC     0x00800000
++#define DBG_TPROC     0x01000000
++#define DBG_IOPROC    0x02000000
++#define DBG_ROUTE     0x04000000
++#define DBG_NETERR    0x08000000
++
++#define DBG_ALL               0x7FFFFFFF
++
++
++#ifdef DEBUG_PRINTF
++
++#  define PRINTF0(type,m,fmt)                 ((elan4_debug&(m) || (type) == DBG_CONSOLE) ? elan4_debugf(type,m,fmt)                  : (void)0)
++#  define PRINTF1(type,m,fmt,a)                       ((elan4_debug&(m) || (type) == DBG_CONSOLE) ? elan4_debugf(type,m,fmt,a)                : (void)0)
++#  define PRINTF2(type,m,fmt,a,b)             ((elan4_debug&(m) || (type) == DBG_CONSOLE) ? elan4_debugf(type,m,fmt,a,b)              : (void)0)
++#  define PRINTF3(type,m,fmt,a,b,c)           ((elan4_debug&(m) || (type) == DBG_CONSOLE) ? elan4_debugf(type,m,fmt,a,b,c)            : (void)0)
++#  define PRINTF4(type,m,fmt,a,b,c,d)         ((elan4_debug&(m) || (type) == DBG_CONSOLE) ? elan4_debugf(type,m,fmt,a,b,c,d)          : (void)0)
++#  define PRINTF5(type,m,fmt,a,b,c,d,e)               ((elan4_debug&(m) || (type) == DBG_CONSOLE) ? elan4_debugf(type,m,fmt,a,b,c,d,e)        : (void)0)
++#  define PRINTF6(type,m,fmt,a,b,c,d,e,f)     ((elan4_debug&(m) || (type) == DBG_CONSOLE) ? elan4_debugf(type,m,fmt,a,b,c,d,e,f)      : (void)0)
++#  define PRINTF7(type,m,fmt,a,b,c,d,e,f,g)   ((elan4_debug&(m) || (type) == DBG_CONSOLE) ? elan4_debugf(type,m,fmt,a,b,c,d,e,f,g)    : (void)0)
++#  define PRINTF8(type,m,fmt,a,b,c,d,e,f,g,h) ((elan4_debug&(m) || (type) == DBG_CONSOLE) ? elan4_debugf(type,m,fmt,a,b,c,d,e,f,g,h)  : (void)0)
++#  define PRINTF9(type,m,fmt,a,b,c,d,e,f,g,h,i)       ((elan4_debug&(m) || (type) == DBG_CONSOLE) ? elan4_debugf(type,m,fmt,a,b,c,d,e,f,g,h,i): (void)0)
++#ifdef __GNUC__
++#  define PRINTF(type,m,args...)              ((elan4_debug&(m) || (type) == DBG_CONSOLE) ? elan4_debugf(type,m, ##args)              : (void)0)
++#endif
++#  define DBGCMD(type,m,cmd)                  ((elan4_debug&(m) || (type) == DBG_CONSOLE)  ? (void) (cmd) : (void) 0)
++
++#else
++
++#  define PRINTF0(type,m,fmt)                 (0)
++#  define PRINTF1(type,m,fmt,a)                       (0)
++#  define PRINTF2(type,m,fmt,a,b)             (0)
++#  define PRINTF3(type,m,fmt,a,b,c)           (0)
++#  define PRINTF4(type,m,fmt,a,b,c,d)         (0)
++#  define PRINTF5(type,m,fmt,a,b,c,d,e)               (0)
++#  define PRINTF6(type,m,fmt,a,b,c,d,e,f)     (0)
++#  define PRINTF7(type,m,fmt,a,b,c,d,e,f,g)   (0)
++#  define PRINTF8(type,m,fmt,a,b,c,d,e,f,g,h) (0)
++#  define PRINTF9(type,m,fmt,a,b,c,d,e,f,g,h,i)       (0)
++#ifdef __GNUC__
++#  define PRINTF(type,m,args...)
++#endif
++#  define DBGCMD(type,m,cmd)                  ((void) 0)
++
++#endif /* DEBUG_PRINTF */
++
++extern unsigned   elan4_debug;
++extern unsigned   elan4_debug_toconsole;
++extern unsigned   elan4_debug_tobuffer;
++extern unsigned   elan4_debug_display_ctxt;
++extern unsigned   elan4_debug_ignore_ctxt;
++extern unsigned   elan4_debug_ignore_type;
++
++extern void     elan4_debug_init(void);
++extern void     elan4_debug_fini(void);
++extern void       elan4_debugf (void *type, int mode, char *fmt, ...);
++extern int        elan4_debug_snapshot (caddr_t ubuffer, int len);
++extern int      elan4_debug_display (void);
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++#endif /* _ELAN4_ELANDEBUG_H */
+Index: linux-2.6.5/include/elan4/device.h
+===================================================================
+--- linux-2.6.5.orig/include/elan4/device.h    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan4/device.h 2005-05-11 12:10:12.601907800 -0400
+@@ -0,0 +1,811 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN4_ELANDEV_H
++#define __ELAN4_ELANDEV_H
++
++#ident "$Id: device.h,v 1.68.2.12 2005/03/09 12:00:08 addy Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/device.h,v $ */
++
++#include <elan/devinfo.h>
++#include <elan/capability.h>
++
++#include <elan4/pci.h>
++#include <elan4/sdram.h>
++#include <elan4/dma.h>
++#include <elan4/events.h>
++#include <elan4/registers.h>
++
++#include <elan4/mmu.h>
++#include <elan4/trap.h>
++#include <elan4/stats.h>
++#include <elan4/neterr.h>
++
++#ifdef CONFIG_MPSAS
++#include <elan4/mpsas.h>
++#endif
++
++#if defined(LINUX)
++#include <elan4/device_Linux.h>
++#elif defined(TRU64UNIX)
++#include <elan4/device_OSF1.h>
++#elif defined(SOLARIS)
++#include <elan4/device_SunOS.h>
++#endif
++
++/*
++ * Network context number allocation.
++ * [0]          neterr fixup system context
++ * [1]          kernel comms system context
++ * [2048-4095]        kernel comms data contexts
++ */
++#define ELAN4_NETERR_CONTEXT_NUM      0x00                    /* network error fixup context number */
++#define ELAN4_KCOMM_CONTEXT_NUM               0x01                    /* kernel comms context number */
++#define ELAN4_KCOMM_BASE_CONTEXT_NUM  0x800                   /* kernel comms data transfer contexts */
++#define ELAN4_KCOMM_TOP_CONTEXT_NUM   0xfff
++
++#define ELAN4_SYSTEM_CONTEXT(ctx)  ((ctx) >= ELAN4_KCOMM_BASE_CONTEXT_NUM)
++
++typedef void (ELAN4_HALTFN)(struct elan4_dev *dev, void *arg);
++
++typedef struct elan4_haltop
++{
++    struct list_head    op_link;                              /* chain on a list */
++    E4_uint32         op_mask;                                /* Interrupt mask to see before calling function */
++    
++    ELAN4_HALTFN       *op_function;                          /* function to call */
++    void             *op_arg;                                 /* arguement to pass to function */
++} ELAN4_HALTOP;
++
++typedef void (ELAN4_DMA_FLUSHFN)(struct elan4_dev *dev, void *arg, int qfull);
++
++typedef struct elan4_dma_flushop
++{
++    struct list_head    op_link;                              /* chain on a list */
++    ELAN4_DMA_FLUSHFN  *op_function;                          /* function to call */
++    void             *op_arg;                                 /* arguement to pass to function */
++} ELAN4_DMA_FLUSHOP;
++
++typedef void (ELAN4_INTFN)(struct elan4_dev *dev, void *arg);
++
++typedef struct elan4_intop
++{
++    struct list_head    op_link;                              /* chain on a list */
++    ELAN4_INTFN        *op_function;                          /* function to call */
++    void             *op_arg;                                 /* arguement to pass to function */
++    E4_uint64         op_cookie;                              /* and main interrupt cookie */
++} ELAN4_INTOP;
++
++typedef struct elan4_eccerrs
++{
++    E4_uint64        EccStatus;
++    E4_uint64        ConfigReg;
++    E4_uint32        ErrorCount;
++} ELAN4_ECCERRS;
++
++#define SDRAM_MIN_BLOCK_SHIFT 10
++#define SDRAM_NUM_FREE_LISTS  19                                      /* allows max 256 Mb block */
++#define SDRAM_MIN_BLOCK_SIZE  (1 << SDRAM_MIN_BLOCK_SHIFT)
++#define SDRAM_MAX_BLOCK_SIZE  (SDRAM_MIN_BLOCK_SIZE << (SDRAM_NUM_FREE_LISTS-1))
++
++#if PAGE_SHIFT < 13
++#define SDRAM_PAGE_SIZE               8192
++#define SDRAM_PGOFF_OFFSET    1
++#define SDRAM_PGOFF_MASK      (~SDRAM_PGOFF_OFFSET)
++#else
++#define SDRAM_PAGE_SIZE               PAGE_SIZE
++#define SDRAM_PGOFF_OFFSET    0
++#define SDRAM_PGOFF_MASK      (~SDRAM_PGOFF_OFFSET)
++#endif
++
++typedef struct elan4_sdram
++{
++    sdramaddr_t       b_base;                                 /* offset in sdram bar */
++    unsigned          b_size;                                 /* size of bank */
++    ioaddr_t          b_ioaddr;                               /* ioaddr where mapped into the kernel */
++    ELAN4_MAP_HANDLE  b_handle;                               /*    and mapping handle */
++    bitmap_t         *b_bitmaps[SDRAM_NUM_FREE_LISTS];        /* buddy allocator bitmaps */
++} ELAN4_SDRAM_BANK;
++
++/* command queue */
++typedef struct elan4_cq 
++{
++    struct elan4_cqa    *cq_cqa;                                      /* command queue allocator this belongs to */
++    unsigned           cq_idx;                                        /*  and which command queue this is */
++
++    sdramaddr_t                cq_space;                                      /* sdram backing up command queue */
++    unsigned           cq_size;                                       /* size value */
++    unsigned           cq_perm;                                       /* permissions */
++    ioaddr_t           cq_mapping;                                    /* mapping of command queue page */
++    ELAN4_MAP_HANDLE   cq_handle;                                     /*    and mapping handle */
++} ELAN4_CQ;
++
++/* cqtype flags to elan4_alloccq() */
++#define CQ_Priority   (1 << 0)
++#define CQ_Reorder    (1 << 1)
++
++/* command queues are allocated in chunks,so that all the
++ * command ports are in a single system page */
++#define ELAN4_CQ_PER_CQA      MAX(1, (PAGESIZE/CQ_CommandMappingSize))
++
++/* maximum number of command queues per context */
++#define ELAN4_MAX_CQA         (256 / ELAN4_CQ_PER_CQA)
++
++typedef struct elan4_cqa
++{
++    struct list_head  cqa_link;                                       /* linked together */
++    bitmap_t          cqa_bitmap[BT_BITOUL(ELAN4_CQ_PER_CQA)];        /* bitmap of which are free */
++    unsigned int        cqa_type;                                     /* allocation type */
++    unsigned int      cqa_cqnum;                                      /* base cq number */
++    unsigned int      cqa_ref;                                        /* "mappings" to a queue */
++    unsigned int      cqa_idx;                                        /* index number */
++    ELAN4_CQ          cqa_cq[ELAN4_CQ_PER_CQA];                       /* command queue entries */
++} ELAN4_CQA;
++
++#define elan4_cq2num(cq)      ((cq)->cq_cqa->cqa_cqnum + (cq)->cq_idx)
++#define elan4_cq2idx(cq)      ((cq)->cq_cqa->cqa_idx * ELAN4_CQ_PER_CQA + (cq)->cq_idx)
++
++typedef struct elan4_ctxt
++{
++    struct elan4_dev      *ctxt_dev;                                  /* device we're associated with */
++    struct list_head       ctxt_link;                                 /* chained on device */
++    
++    struct elan4_trap_ops *ctxt_ops;                                  /* client specific operations */
++
++    signed               ctxt_num;                                    /* local context number */
++
++    struct list_head     ctxt_cqalist;                                /* link list of command queue allocators */
++    bitmap_t             ctxt_cqamap[BT_BITOUL(ELAN4_MAX_CQA)];       /*   bitmap for allocating cqa_idx */
++
++    ELAN4_HASH_ENTRY     **ctxt_mmuhash[2];                           /* software hash tables */
++    spinlock_t                   ctxt_mmulock;                                /*   and spinlock. */
++} ELAN4_CTXT;
++
++typedef struct elan4_trap_ops
++{
++    void       (*op_eproc_trap) (ELAN4_CTXT *ctxt, E4_uint64 status);
++    void       (*op_cproc_trap) (ELAN4_CTXT *ctxt, E4_uint64 status, unsigned cqnum);
++    void       (*op_dproc_trap) (ELAN4_CTXT *ctxt, E4_uint64 status, unsigned unit);
++    void       (*op_tproc_trap) (ELAN4_CTXT *ctxt, E4_uint64 status);
++    void       (*op_iproc_trap) (ELAN4_CTXT *ctxt, E4_uint64 status, unsigned unit);
++    void       (*op_interrupt)  (ELAN4_CTXT *ctxt, E4_uint64 cookie);
++    void       (*op_neterrmsg)  (ELAN4_CTXT *ctxt, ELAN4_NETERR_MSG *msg);
++} ELAN4_TRAP_OPS;
++
++typedef struct elan4_route_table
++{
++    spinlock_t  tbl_lock;
++    unsigned  tbl_size;
++    sdramaddr_t tbl_entries;
++} ELAN4_ROUTE_TABLE;
++
++#ifdef ELAN4_LARGE_PAGE_SUPPORT
++#define NUM_HASH_TABLES               2
++#else
++#define NUM_HASH_TABLES               1
++#endif
++
++#define DEV_STASH_ROUTE_COUNT 20
++
++typedef struct elan4_route_ringbuf {
++    int start;
++    int end;
++    E4_VirtualProcessEntry routes[DEV_STASH_ROUTE_COUNT]; 
++} ELAN4_ROUTE_RINGBUF;
++
++#define elan4_ringbuf_init(ringbuf) memset(&ringbuf, 0, sizeof(ELAN4_ROUTE_RINGBUF));
++
++typedef struct elan4_dev
++{
++    ELAN4_CTXT                 dev_ctxt;                                      /* context for device operations */
++
++    ELAN4_DEV_OSDEP    dev_osdep;                                     /* OS specific entries */
++
++    int                        dev_instance;                                  /* device number */
++    ELAN_DEVINFO       dev_devinfo;                                   /* device information (revision etc */
++    ELAN_POSITION      dev_position;                                  /* position connected to switch */
++    ELAN_DEV_IDX       dev_idx;                                       /* device idx registered with elanmod */
++
++    kmutex_t           dev_lock;                                      /* lock for device state/references */
++    unsigned           dev_state;                                     /* device state */
++    unsigned           dev_references;                                /*  # references */
++
++    ioaddr_t           dev_regs;                                      /* Mapping of device registers */
++    ELAN4_MAP_HANDLE   dev_regs_handle;
++    ioaddr_t           dev_rom;                                       /* Mapping of rom */
++    ELAN4_MAP_HANDLE   dev_rom_handle;
++    ioaddr_t           dev_i2c;                                       /* Mapping of I2C registers */
++    ELAN4_MAP_HANDLE   dev_i2c_handle;
++    
++    E4_uint64          dev_sdram_cfg;                                 /* SDRAM config value (from ROM) */
++    E4_uint64          dev_sdram_initial_ecc_val;                     /* power on ECC register value */
++    int                        dev_sdram_numbanks;                            /* # banks of sdram */
++    ELAN4_SDRAM_BANK   dev_sdram_banks[SDRAM_MAX_BANKS];              /* Mapping of sdram banks */
++    spinlock_t                 dev_sdram_lock;                                /* spinlock for buddy allocator */
++    sdramaddr_t                dev_sdram_freelists[SDRAM_NUM_FREE_LISTS];
++    unsigned           dev_sdram_freecounts[SDRAM_NUM_FREE_LISTS];
++
++    sdramaddr_t                dev_cacheflush_space;                          /* sdram reserved for cache flush operation */
++
++    sdramaddr_t                dev_faultarea;                                 /* fault areas for each unit */
++    sdramaddr_t                dev_inputtraparea;                             /* trap area for trapped transactions */
++    sdramaddr_t                dev_ctxtable;                                  /* context table (E4_ContextControlBlock) */
++    int                        dev_ctxtableshift;                             /* and size (in bits) */
++
++    E4_uint32          dev_syscontrol;                                /* copy of system control register */
++    spinlock_t                 dev_syscontrol_lock;                           /*   spinlock to sequentialise modifications */
++    unsigned           dev_direct_map_pci_writes;                     /*   # counts for CONT_DIRECT_MAP_PCI_WRITES */
++
++    volatile E4_uint32         dev_intmask;                                   /* copy of interrupt mask register */
++    spinlock_t                 dev_intmask_lock;                              /*   spinlock to sequentialise modifications */
++
++    /* i2c section */
++    spinlock_t                 dev_i2c_lock;                                  /* spinlock for i2c operations */
++    unsigned int         dev_i2c_led_disabled;                                /* count of reasons led auto update disabled */
++
++    /* mmu section */
++    unsigned           dev_pagesizeval[NUM_HASH_TABLES];              /* page size value */
++    unsigned           dev_pageshift[NUM_HASH_TABLES];                        /* pageshift in bits. */
++    unsigned           dev_hashsize[NUM_HASH_TABLES];                 /* # entries in mmu hash table */
++    sdramaddr_t                dev_hashtable[NUM_HASH_TABLES];                /* mmu hash table */
++    ELAN4_HASH_ENTRY  *dev_mmuhash[NUM_HASH_TABLES];                  /*   and software shadow */
++    ELAN4_HASH_ENTRY   **dev_mmufree[NUM_HASH_TABLES];                        /*   and partially free blocks */
++    ELAN4_HASH_ENTRY    *dev_mmufreelist;                             /*   and free blocks */
++    spinlock_t           dev_mmulock;
++    E4_uint16          dev_topaddr[4];                                /* top address values */
++    unsigned char      dev_topaddrvalid;
++    unsigned char      dev_topaddrmode;
++    unsigned char      dev_pteval;                                    /* allow setting of relaxed order/dont snoop attributes */
++
++    unsigned           dev_rsvd_hashmask[NUM_HASH_TABLES];
++    unsigned           dev_rsvd_hashval[NUM_HASH_TABLES];
++
++    /* run queues */
++    sdramaddr_t                dev_comqlowpri;                                /* CProc low & high pri run queues */
++    sdramaddr_t                dev_comqhighpri;
++
++    sdramaddr_t                dev_dmaqlowpri;                                /* DProc,TProc,Interrupt queues */
++    sdramaddr_t                dev_dmaqhighpri;
++    sdramaddr_t                dev_threadqlowpri;
++    sdramaddr_t                dev_threadqhighpri;
++    sdramaddr_t                dev_interruptq;
++
++    E4_uint32          dev_interruptq_nfptr;                          /* cache next main interrupt fptr */
++    struct list_head     dev_interruptq_list;                         /*   list of operations to call when space in interruptq*/
++
++    /* command queue section */
++    sdramaddr_t                dev_cqaddr;                                    /* SDRAM address of command queues */
++    unsigned           dev_cqoffset;                                  /* offset for command queue alignment constraints */
++    unsigned           dev_cqcount;                                   /* number of command queue descriptors */
++    bitmap_t          *dev_cqamap;                                    /* bitmap for allocation */
++    spinlock_t                 dev_cqlock;                                    /* spinlock to protect bitmap */
++    unsigned           dev_cqreorder;                                 /* offset for first re-ordering queue with mtrr */
++
++    /* halt operation section */
++    struct list_head     dev_haltop_list;                             /* list of operations to call when units halted */
++    E4_uint32          dev_haltop_mask;                               /* mask of which ones to halt */
++    E4_uint32          dev_haltop_active;                             /* mask of which haltops are executing */
++    spinlock_t                 dev_haltop_lock;                               /*    and their spinlock */
++
++    struct {
++      struct list_head list;                                          /* list of halt operations for DMAs */
++      ELAN4_CQ        *cq;                                            /*   and command queue's */
++      ELAN4_INTOP      intop;                                         /*   and main interrupt op */
++      E4_uint64        status;                                        /*   status register (when waiting for intop)*/
++    }                  dev_dma_flushop[2];
++
++    unsigned           dev_halt_all_count;                            /* count of reasons to halt all units */
++    unsigned           dev_halt_lowpri_count;                         /* count of reasons to halt lowpri queues */
++    unsigned           dev_halt_cproc_count;                          /* count of reasons to halt command processor */
++    unsigned           dev_halt_dproc_count;                          /* count of reasons to halt dma processor */
++    unsigned           dev_halt_tproc_count;                          /* count of reasons to halt thread processor */
++    unsigned           dev_discard_all_count;                         /* count of reasons to discard all packets */
++    unsigned           dev_discard_lowpri_count;                      /* count of reasons to discard non-system packets */
++    unsigned           dev_discard_highpri_count;                     /* count of reasons to discard system packets */
++
++    E4_uint32          dev_schedstatus;                               /* copy of schedule status register */
++
++    /* local context allocation section */
++    spinlock_t                 dev_ctxlock;                                   /* spinlock to protect bitmap */
++    bitmap_t          *dev_ctxmap;                                    /* bitmap for local context allocation */
++
++    spinlock_t                 dev_ctxt_lock;                                 /* spinlock to protect context list */
++    struct list_head     dev_ctxt_list;                                       /* linked list of contexts */
++
++    /* locks to sequentialise interrupt handling */
++    spinlock_t                 dev_trap_lock;                                 /* spinlock while handling a trap */
++    spinlock_t                 dev_requeue_lock;                              /* spinlock sequentialising cproc requeue */
++
++    /* error rate interrupt section */
++    long               dev_error_time;                                /* lbolt at start of sampling period */
++    unsigned           dev_errors_per_period;                         /* errors so far this sampling period */
++    timer_fn_t                 dev_error_timeoutid;                           /* timeout to re-enable error interrupts */
++    timer_fn_t                 dev_linkerr_timeoutid;                         /* timeout to clear link error led */
++
++    /* kernel threads */
++    unsigned           dev_stop_threads:1;                            /* kernel threads should exit */
++
++    /* main interrupt thread */
++    kcondvar_t                 dev_mainint_wait;                              /* place for mainevent interrupt thread to sleep */
++    spinlock_t                 dev_mainint_lock;                              /*   and it's spinlock */
++    unsigned           dev_mainint_started:1;
++    unsigned           dev_mainint_stopped:1;
++
++    /* device context - this is used to flush insert cache/instruction cache/dmas & threads */
++    ELAN4_CPROC_TRAP     dev_cproc_trap;                              /* space to extract cproc trap into */
++
++    struct list_head     dev_intop_list;                              /* list of main interrupt operations */
++    spinlock_t                 dev_intop_lock;                                /*   and spinlock */
++    E4_uint64          dev_intop_cookie;                              /*   and next cookie to use */
++
++    spinlock_t                 dev_flush_lock;                                /* spinlock for flushing */
++    kcondvar_t                 dev_flush_wait;                                /*  and place to sleep */
++
++    ELAN4_CQ          *dev_flush_cq[COMMAND_INSERTER_CACHE_ENTRIES];  /* command queues to flush the insert cache */
++    ELAN4_INTOP          dev_flush_op[COMMAND_INSERTER_CACHE_ENTRIES];        /* and a main interrupt operation for each one */
++    unsigned           dev_flush_finished;                            /* flush command finished */
++
++    ELAN4_HALTOP       dev_iflush_haltop;                             /* halt operation for icache flush */
++    unsigned           dev_iflush_queued:1;                           /* icache haltop queued */
++
++    ELAN4_ROUTE_TABLE   *dev_routetable;                              /* virtual process table (for dma queue flush)*/
++    sdramaddr_t          dev_sdrampages[2];                           /* pages of sdram to hold suspend code sequence */
++    E4_Addr            dev_tproc_suspend;                             /*  st8suspend instruction */
++    E4_Addr            dev_tproc_space;                               /*     and target memory */
++
++    sdramaddr_t                dev_neterr_inputq;                             /* network error input queue descriptor & event */
++    sdramaddr_t                dev_neterr_slots;                              /* network error message slots */
++    ELAN4_CQ          *dev_neterr_msgcq;                              /* command queue for sending messages */
++    ELAN4_CQ          *dev_neterr_intcq;                              /* command queue for message received interrupt */
++    ELAN4_INTOP                dev_neterr_intop;                              /*   and it's main interrupt operation */
++    E4_uint64          dev_neterr_queued;                             /* # message queued in msgcq */
++    spinlock_t           dev_neterr_lock;                             /*   and spinlock .... */
++
++    ELAN4_DEV_STATS    dev_stats;                                     /* device statistics */
++    ELAN4_ECCERRS      dev_sdramerrs[30];                             /* last few sdram errors for procfs */
++
++    unsigned int      *dev_ack_errors;                                /* Map of source of dproc ack errors */
++    ELAN4_ROUTE_RINGBUF  dev_ack_error_routes;
++    unsigned int        *dev_dproc_timeout;                             /* Ditto dproc timeout errors */
++    ELAN4_ROUTE_RINGBUF  dev_dproc_timeout_routes;
++    unsigned int        *dev_cproc_timeout;                             /* Ditto cproc timeout errors */
++    ELAN4_ROUTE_RINGBUF  dev_cproc_timeout_routes;
++
++    unsigned           dev_linkerr_signalled;                         /* linkerror signalled to switch controller */
++
++    struct list_head     dev_hc_list;                                   /* list of the allocated hash_chunks */
++
++    ELAN4_IPROC_TRAP     dev_iproc_trap;                              /* space for iproc trap */
++} ELAN4_DEV;
++
++/* values for dev_state */
++#define ELAN4_STATE_STOPPED           (1 << 0)                        /* device initialised but not started */
++#define ELAN4_STATE_STARTING          (1 << 1)                        /* device in process of starting */
++#define ELAN4_STATE_STARTED           (1 << 2)                        /* device started */
++#define ELAN4_STATE_STOPPING          (1 << 3)                        /* device in process of stopping */
++
++extern __inline__ unsigned long long
++__elan4_readq (ELAN4_DEV *dev, ioaddr_t addr)
++{
++#if defined(__i386)
++    if (dev->dev_devinfo.dev_params.values[ELAN4_PARAM_DRIVER_FEATURES] & ELAN4_FEATURE_64BIT_READ)
++    {
++      uint64_t save, rval;
++      unsigned long flags;
++    
++      local_irq_save (flags);
++      asm volatile ("sfence\n" \
++                    "movq %%xmm0, %0\n" \
++                    "sfence\n" \
++                    "movq (%2), %%xmm0\n" \
++                    "sfence\n" \
++                    "movq %%xmm0, %1\n"
++                    "sfence\n"
++                    "movq %0, %%xmm0\n"
++                    "sfence\n"
++                    : "=m" (save), "=m" (rval) : "r" (addr) : "memory");
++      
++      local_irq_restore(flags);
++      
++      return rval;
++    }
++#endif
++    return readq ((void *)addr);
++}
++
++extern __inline__ unsigned int
++__elan4_readl (ELAN4_DEV *dev, ioaddr_t addr)
++{
++    if (dev->dev_devinfo.dev_params.values[ELAN4_PARAM_DRIVER_FEATURES] & ELAN4_FEATURE_64BIT_READ)
++    {
++      uint64_t val = __elan4_readq (dev, ((unsigned long) addr & ~7));
++      return ((val >> (((unsigned long) addr & 7) << 3)) & 0xffffffff);
++    }
++    return readl ((void *)addr);
++}
++
++extern __inline__ unsigned int
++__elan4_readw (ELAN4_DEV *dev, ioaddr_t addr)
++{
++    if (dev->dev_devinfo.dev_params.values[ELAN4_PARAM_DRIVER_FEATURES] & ELAN4_FEATURE_64BIT_READ)
++    {
++      uint64_t val = __elan4_readq (dev, ((unsigned long) addr & ~7));
++      return ((val >> (((unsigned long) addr & 7) << 3)) & 0xffff);
++    }
++    return readw ((void *)addr);
++}
++
++extern __inline__ unsigned int
++__elan4_readb (ELAN4_DEV *dev, ioaddr_t addr)
++{
++    if (dev->dev_devinfo.dev_params.values[ELAN4_PARAM_DRIVER_FEATURES] & ELAN4_FEATURE_64BIT_READ)
++    {
++      uint64_t val = __elan4_readq (dev, ((unsigned long) addr & ~7));
++      return ((val >> (((unsigned long) addr & 7) << 3)) & 0xff);
++    }
++    return readb ((void *)addr);
++}
++
++/* macros for accessing dev->dev_regs.Tags. */
++#define write_tag(dev,what,val)               writeq (val, (void *) (dev->dev_regs + offsetof (E4_Registers, Tags.what)))
++#define read_tag(dev,what)            __elan4_readq (dev, dev->dev_regs + offsetof (E4_Registers, Tags.what))
++
++/* macros for accessing dev->dev_regs.Regs. */
++#define write_reg64(dev,what,val)     writeq (val, (void *) (dev->dev_regs + offsetof (E4_Registers, Regs.what)))
++#define write_reg32(dev,what,val)     writel (val, (void *) (dev->dev_regs + offsetof (E4_Registers, Regs.what)))
++#define read_reg64(dev,what)          __elan4_readq (dev, dev->dev_regs + offsetof (E4_Registers, Regs.what))
++#define read_reg32(dev,what)          __elan4_readl (dev, dev->dev_regs + offsetof (E4_Registers, Regs.what))
++
++/* macros for accessing dev->dev_regs.uRegs. */
++#define write_ureg64(dev,what,val)    writeq (val, (void *) (dev->dev_regs + offsetof (E4_Registers, uRegs.what)))
++#define write_ureg32(dev,what,val)    writel (val, (void *) (dev->dev_regs + offsetof (E4_Registers, uRegs.what)))
++#define read_ureg64(dev,what)         __elan4_readq (dev, dev->dev_regs + offsetof (E4_Registers, uRegs.what))
++#define read_ureg32(dev,what)         __elan4_readl (dev, dev->dev_regs + offsetof (E4_Registers, uRegs.what))
++
++/* macros for accessing dev->dev_i2c */
++#define write_i2c(dev,what,val)               writeb (val, (void *) (dev->dev_i2c + offsetof (E4_I2C, what)))
++#define read_i2c(dev,what)            __elan4_readb (dev, dev->dev_i2c + offsetof (E4_I2C, what))
++
++/* macros for accessing dev->dev_rom */
++#define read_ebus_rom(dev,off)                __elan4_readb (dev, dev->dev_rom + off)
++
++/* PIO flush operations - ensure writes to registers/sdram are ordered */
++#ifdef CONFIG_IA64_SGI_SN2
++#define pioflush_reg(dev)             read_reg32(dev,InterruptReg)
++#define pioflush_sdram(dev)           elan4_sdram_readl(dev, 0)
++#else
++#define pioflush_reg(dev)             mb()
++#define pioflush_sdram(dev)           mb()
++#endif
++
++/* macros for manipulating the interrupt mask register */
++#define SET_INT_MASK(dev,value)       \
++do { \
++    write_reg32(dev, InterruptMask, (dev)->dev_intmask = (value)); \
++    pioflush_reg(dev);\
++} while (0)
++
++#define CHANGE_INT_MASK(dev, value) \
++do { \
++    if ((dev)->dev_intmask != (value)) \
++    {\
++      write_reg32 (dev, InterruptMask, (dev)->dev_intmask = (value));\
++      pioflush_reg(dev);\
++    }\
++} while (0)
++
++#define ENABLE_INT_MASK(dev,value) \
++do { \
++    unsigned long flags; \
++ \
++    spin_lock_irqsave (&(dev)->dev_intmask_lock, flags); \
++    write_reg32(dev, InterruptMask, (dev)->dev_intmask |= (value)); \
++    pioflush_reg(dev);\
++    spin_unlock_irqrestore (&(dev)->dev_intmask_lock, flags); \
++} while (0)
++
++#define DISABLE_INT_MASK(dev,value) \
++do { \
++    unsigned long flags; \
++ \
++    spin_lock_irqsave (&(dev)->dev_intmask_lock, flags); \
++    write_reg32(dev, InterruptMask, (dev)->dev_intmask &= ~(value)); \
++    pioflush_reg(dev);\
++    spin_unlock_irqrestore (&(dev)->dev_intmask_lock, flags); \
++} while (0)
++
++#define SET_SYSCONTROL(dev,what,value) \
++do { \
++    unsigned long flags; \
++\
++    spin_lock_irqsave (&(dev)->dev_syscontrol_lock, flags); \
++    if ((dev)->what++ == 0) \
++        write_reg64 (dev, SysControlReg, (dev)->dev_syscontrol |= (value)); \
++    pioflush_reg(dev);\
++    spin_unlock_irqrestore (&(dev)->dev_syscontrol_lock, flags); \
++} while (0)
++
++#define CLEAR_SYSCONTROL(dev,what,value) \
++do { \
++    unsigned long flags; \
++\
++    spin_lock_irqsave (&(dev)->dev_syscontrol_lock, flags); \
++    if (--(dev)->what == 0)\
++       write_reg64 (dev, SysControlReg, (dev)->dev_syscontrol &= ~(value)); \
++    pioflush_reg (dev); \
++    spin_unlock_irqrestore (&(dev)->dev_syscontrol_lock, flags); \
++} while (0)
++
++#define PULSE_SYSCONTROL(dev,value) \
++do { \
++    unsigned long flags; \
++\
++    spin_lock_irqsave (&(dev)->dev_syscontrol_lock, flags); \
++    write_reg64 (dev, SysControlReg, (dev)->dev_syscontrol | (value)); \
++    pioflush_reg (dev); \
++    spin_unlock_irqrestore (&(dev)->dev_syscontrol_lock, flags); \
++} while (0)
++
++#define CHANGE_SYSCONTROL(dev,add,sub) \
++do { \
++    unsigned long flags; \
++\
++    spin_lock_irqsave (&(dev)->dev_syscontrol_lock, flags); \
++    dev->dev_syscontrol |= (add);\
++    dev->dev_syscontrol &= ~(sub);\
++    write_reg64 (dev, SysControlReg, (dev)->dev_syscontrol);\
++    pioflush_reg (dev); \
++    spin_unlock_irqrestore (&(dev)->dev_syscontrol_lock, flags); \
++} while (0)
++
++#define SET_SCHED_STATUS(dev, value)\
++do {\
++    write_reg32 (dev, SchedStatus.Status, (dev)->dev_schedstatus = (value));\
++    pioflush_reg (dev);\
++} while (0)
++
++#define CHANGE_SCHED_STATUS(dev, value)\
++do {\
++    if ((dev)->dev_schedstatus != (value))\
++    {\
++      write_reg32 (dev, SchedStatus.Status, (dev)->dev_schedstatus = (value));\
++      pioflush_reg (dev);\
++    }\
++} while (0)
++
++#define PULSE_SCHED_RESTART(dev,value)\
++do {\
++    write_reg32 (dev, SchedStatus.Restart, value);\
++    pioflush_reg (dev);\
++} while (0)
++
++/* device context elan address space */
++#define DEVICE_TPROC_SUSPEND_ADDR             (0x1000000000000000ull)
++#define DEVICE_TPROC_SPACE_ADDR                       (0x1000000000000000ull + SDRAM_PAGE_SIZE)
++#if defined(__LITTLE_ENDIAN__)
++#  define DEVICE_TPROC_SUSPEND_INSTR          0xd3f040c0 /* st64suspend %r16, [%r1] */
++#else
++#  define DEVICE_TPROC_SUSPEND_INSTR          0xc040f0d3 /* st64suspend %r16, [%r1] */
++#endif
++
++#define DEVICE_NETERR_INPUTQ_ADDR             (0x2000000000000000ull)
++#define DEVICE_NETERR_INTCQ_ADDR              (0x2000000000000000ull + SDRAM_PAGE_SIZE)
++#define DEVICE_NETERR_SLOTS_ADDR              (0x2000000000000000ull + SDRAM_PAGE_SIZE*2)
++
++/*
++ * Interrupt operation cookie space
++ * [50:48]    type
++ * [47:0]     value
++ */
++#define INTOP_PERSISTENT                      (0x1000000000000ull)
++#define INTOP_ONESHOT                         (0x2000000000000ull)
++#define INTOP_TYPE_MASK                               (0x3000000000000ull)
++#define INTOP_VALUE_MASK                      (0x0ffffffffffffull)
++
++/* functions for accessing sdram - sdram.c */
++extern unsigned char      elan4_sdram_readb (ELAN4_DEV *dev, sdramaddr_t ptr);
++extern unsigned short     elan4_sdram_readw (ELAN4_DEV *dev, sdramaddr_t ptr);
++extern unsigned int       elan4_sdram_readl (ELAN4_DEV *dev, sdramaddr_t ptr);
++extern unsigned long long elan4_sdram_readq (ELAN4_DEV *dev, sdramaddr_t ptr);
++extern void               elan4_sdram_writeb (ELAN4_DEV *dev, sdramaddr_t ptr, unsigned char val);
++extern void               elan4_sdram_writew (ELAN4_DEV *dev, sdramaddr_t ptr, unsigned short val);
++extern void               elan4_sdram_writel (ELAN4_DEV *dev, sdramaddr_t ptr, unsigned int val);
++extern void               elan4_sdram_writeq (ELAN4_DEV *dev, sdramaddr_t ptr, unsigned long long val);
++
++extern void             elan4_sdram_zerob_sdram (ELAN4_DEV *dev, sdramaddr_t ptr, int nbytes);
++extern void             elan4_sdram_zerow_sdram (ELAN4_DEV *dev, sdramaddr_t ptr, int nbytes);
++extern void             elan4_sdram_zerol_sdram (ELAN4_DEV *dev, sdramaddr_t ptr, int nbytes);
++extern void             elan4_sdram_zeroq_sdram (ELAN4_DEV *dev, sdramaddr_t ptr, int nbytes);
++
++extern void               elan4_sdram_copyb_from_sdram (ELAN4_DEV *dev, sdramaddr_t from, void *to, int nbytes);
++extern void               elan4_sdram_copyw_from_sdram (ELAN4_DEV *dev, sdramaddr_t from, void *to, int nbytes);
++extern void               elan4_sdram_copyl_from_sdram (ELAN4_DEV *dev, sdramaddr_t from, void *to, int nbytes);
++extern void               elan4_sdram_copyq_from_sdram (ELAN4_DEV *dev, sdramaddr_t from, void *to, int nbytes);
++extern void               elan4_sdram_copyb_to_sdram (ELAN4_DEV *dev, void *from, sdramaddr_t to, int nbytes);
++extern void               elan4_sdram_copyw_to_sdram (ELAN4_DEV *dev, void *from, sdramaddr_t to, int nbytes);
++extern void               elan4_sdram_copyl_to_sdram (ELAN4_DEV *dev, void *from, sdramaddr_t to, int nbytes);
++extern void               elan4_sdram_copyq_to_sdram (ELAN4_DEV *dev, void *from, sdramaddr_t to, int nbytes);
++
++/* device.c - configuration */
++extern unsigned int elan4_hash_0_size_val;
++extern unsigned int elan4_hash_1_size_val;
++extern unsigned int elan4_ctxt_table_shift;
++extern unsigned int elan4_ln2_max_cqs;
++extern unsigned int elan4_dmaq_highpri_size;
++extern unsigned int elan4_threadq_highpri_size;
++extern unsigned int elan4_dmaq_lowpri_size;
++extern unsigned int elan4_threadq_lowpri_size;
++extern unsigned int elan4_interruptq_size;
++extern unsigned int elan4_mainint_punt_loops;
++extern unsigned int elan4_mainint_resched_ticks;
++extern unsigned int elan4_linkport_lock;
++extern unsigned int elan4_eccerr_recheck;
++
++/* device.c */
++extern void               elan4_set_schedstatus (ELAN4_DEV *dev, E4_uint32 intreg);
++extern void               elan4_queue_haltop (ELAN4_DEV *dev, ELAN4_HALTOP *op);
++extern void             elan4_queue_intop (ELAN4_DEV *dev, ELAN4_CQ *cq, ELAN4_INTOP *op);
++extern void             elan4_register_intop (ELAN4_DEV *dev, ELAN4_INTOP *op);
++extern void             elan4_deregister_intop (ELAN4_DEV *dev, ELAN4_INTOP *op);
++extern void             elan4_queue_dma_flushop (ELAN4_DEV *dev, ELAN4_DMA_FLUSHOP *op, int hipri);
++extern void             elan4_queue_mainintop (ELAN4_DEV *dev, ELAN4_INTOP *op);
++
++extern int                elan4_1msi0 (ELAN4_DEV *dev);
++
++extern int                elan4_insertctxt (ELAN4_DEV *dev, ELAN4_CTXT *ctxt, ELAN4_TRAP_OPS *ops);
++extern void               elan4_removectxt (ELAN4_DEV *dev, ELAN4_CTXT *ctxt);
++extern ELAN4_CTXT        *elan4_localctxt (ELAN4_DEV *dev, unsigned num);
++extern ELAN4_CTXT        *elan4_networkctxt (ELAN4_DEV *dev, unsigned num);
++
++extern int                elan4_attach_filter (ELAN4_CTXT *ctxt, unsigned int ctxnum);
++extern void               elan4_detach_filter (ELAN4_CTXT *ctxt, unsigned int ctxnum);
++extern void             elan4_set_filter (ELAN4_CTXT *ctxt, unsigned int ctxnum, E4_uint32 state);
++extern void             elan4_set_routetable (ELAN4_CTXT *ctxt, ELAN4_ROUTE_TABLE *tbl);
++
++extern ELAN4_CQA *        elan4_getcqa (ELAN4_CTXT *ctxt, unsigned int idx);
++extern void               elan4_putcqa (ELAN4_CTXT *ctxt, unsigned int idx);
++extern ELAN4_CQ          *elan4_alloccq (ELAN4_CTXT *ctxt, unsigned cqsize, unsigned cqperm, unsigned cqtype);
++extern void               elan4_freecq (ELAN4_CTXT *ctxt, ELAN4_CQ *cq);
++extern void               elan4_restartcq (ELAN4_DEV *dev, ELAN4_CQ *cq);
++extern void               elan4_flushcq (ELAN4_DEV *dev, ELAN4_CQ *cq);
++extern void               elan4_updatecq (ELAN4_DEV *dev, ELAN4_CQ *cq, unsigned perm, unsigned restart);
++
++extern void             elan4_flush_icache (ELAN4_CTXT *ctxt);
++extern void             elan4_flush_icache_halted (ELAN4_CTXT *ctxt);
++
++extern int                elan4_initialise_device (ELAN4_DEV *dev);
++extern void               elan4_finalise_device (ELAN4_DEV *dev);
++extern int                elan4_start_device (ELAN4_DEV *dev);
++extern void               elan4_stop_device (ELAN4_DEV *dev);
++
++extern int              elan4_compute_position (ELAN_POSITION *pos, unsigned nodeid, unsigned numnodes, unsigned aritiyval);
++extern int              elan4_get_position (ELAN4_DEV *dev, ELAN_POSITION *pos);
++extern int              elan4_set_position (ELAN4_DEV *dev, ELAN_POSITION *pos);
++extern void             elan4_get_params   (ELAN4_DEV *dev, ELAN_PARAMS *params, unsigned short *mask);
++extern void             elan4_set_params   (ELAN4_DEV *dev, ELAN_PARAMS *params, unsigned short mask);
++
++
++extern int                elan4_read_vpd(ELAN4_DEV *dev, unsigned char *tag, unsigned char *result) ;
++
++
++/* device_osdep.c */
++extern unsigned int     elan4_pll_cfg;
++extern int              elan4_pll_div;
++extern int              elan4_mod45disable;
++
++extern int                elan4_pciinit (ELAN4_DEV *dev);
++extern void               elan4_pcifini (ELAN4_DEV *dev);
++extern void             elan4_updatepll (ELAN4_DEV *dev, unsigned int val);
++extern void               elan4_pcierror (ELAN4_DEV *dev);
++
++extern ELAN4_DEV       *elan4_reference_device (int instance, int state);
++extern void             elan4_dereference_device (ELAN4_DEV *dev);
++
++extern ioaddr_t           elan4_map_device (ELAN4_DEV *dev, unsigned bar, unsigned off, unsigned size, ELAN4_MAP_HANDLE *handlep);
++extern void               elan4_unmap_device (ELAN4_DEV *dev, ioaddr_t ptr, unsigned size, ELAN4_MAP_HANDLE *handlep);
++extern unsigned long      elan4_resource_len (ELAN4_DEV *dev, unsigned bar);
++
++extern void               elan4_configure_writecombining (ELAN4_DEV *dev);
++extern void             elan4_unconfigure_writecombining (ELAN4_DEV *dev);
++
++/* i2c.c */
++extern int              i2c_disable_auto_led_update (ELAN4_DEV *dev);
++extern void             i2c_enable_auto_led_update (ELAN4_DEV *dev);
++extern int              i2c_write (ELAN4_DEV *dev, unsigned int addr, unsigned int count, unsigned char *data);
++extern int              i2c_read (ELAN4_DEV *dev, unsigned int addr, unsigned int count, unsigned char *data);
++extern int              i2c_writereg (ELAN4_DEV *dev, unsigned int addr, unsigned int reg, unsigned int count, unsigned char *data);
++extern int              i2c_readreg (ELAN4_DEV *dev, unsigned int addr, unsigned int reg, unsigned int count, unsigned char *data);
++extern int              i2c_read_rom (ELAN4_DEV *dev, unsigned int addr, unsigned int count, unsigned char *data);
++
++#if defined(__linux__)
++/* procfs_Linux.c */
++extern void             elan4_procfs_device_init (ELAN4_DEV *dev);
++extern void             elan4_procfs_device_fini (ELAN4_DEV *dev);
++extern void             elan4_procfs_init(void);
++extern void             elan4_procfs_fini(void);
++
++extern struct proc_dir_entry *elan4_procfs_root;
++extern struct proc_dir_entry *elan4_config_root;
++#endif
++
++/* sdram.c */
++extern void             elan4_sdram_init (ELAN4_DEV *dev);
++extern void               elan4_sdram_fini (ELAN4_DEV *dev);
++extern void               elan4_sdram_setup_delay_lines (ELAN4_DEV *dev, int factor);
++extern int                elan4_sdram_init_bank (ELAN4_DEV *dev, ELAN4_SDRAM_BANK *bank);
++extern void               elan4_sdram_fini_bank (ELAN4_DEV *dev, ELAN4_SDRAM_BANK *bank);
++extern void             elan4_sdram_add_bank (ELAN4_DEV *dev, ELAN4_SDRAM_BANK *bank);
++extern sdramaddr_t        elan4_sdram_alloc (ELAN4_DEV *dev, int nbytes);
++extern void               elan4_sdram_free (ELAN4_DEV *dev, sdramaddr_t ptr, int nbytes);
++extern void               elan4_sdram_flushcache (ELAN4_DEV *dev, sdramaddr_t base, int nbytes);
++extern char              *elan4_sdramerr2str (ELAN4_DEV *dev, E4_uint64 status, E4_uint64 ConfigReg, char *str);
++
++/* traps.c */
++extern void               elan4_display_eproc_trap (void *type, int mode, char *str, ELAN4_EPROC_TRAP *trap);
++extern void               elan4_display_cproc_trap (void *type, int mode, char *str, ELAN4_CPROC_TRAP *trap);
++extern void               elan4_display_dproc_trap (void *type, int mode, char *str, ELAN4_DPROC_TRAP *trap);
++extern void               elan4_display_tproc_trap (void *type, int mode, char *str, ELAN4_TPROC_TRAP *trap);
++extern void               elan4_display_iproc_trap (void *type, int mode, char *str, ELAN4_IPROC_TRAP *trap);
++
++
++extern void               elan4_extract_eproc_trap (ELAN4_DEV *dev, E4_uint64 status, ELAN4_EPROC_TRAP *trap, int iswaitevent);
++extern void               elan4_extract_cproc_trap (ELAN4_DEV *dev, E4_uint64 status, ELAN4_CPROC_TRAP *trap, unsigned cqnum);
++extern void               elan4_extract_dproc_trap (ELAN4_DEV *dev, E4_uint64 status, ELAN4_DPROC_TRAP *trap, unsigned unit);
++extern void               elan4_extract_tproc_trap (ELAN4_DEV *dev, E4_uint64 status, ELAN4_TPROC_TRAP *trap);
++extern void               elan4_extract_iproc_trap (ELAN4_DEV *dev, E4_uint64 status, ELAN4_IPROC_TRAP *trap, unsigned unit);
++extern void elan4_ringbuf_store(ELAN4_ROUTE_RINGBUF *ringbuf, E4_VirtualProcessEntry *route, ELAN4_DEV *dev);
++extern int                cproc_open_extract_vp (ELAN4_DEV *dev, ELAN4_CQ *cq, int chan);
++
++extern void               elan4_inspect_iproc_trap (ELAN4_IPROC_TRAP *trap);
++extern E4_uint64          elan4_trapped_open_command (ELAN4_DEV *dev, ELAN4_CQ *cq);
++
++/* mmu.c */
++extern void               elan4mmu_flush_tlb (ELAN4_DEV *dev);
++extern ELAN4_HASH_ENTRY  *elan4mmu_ptealloc (ELAN4_CTXT *ctxt, int tbl, E4_Addr vaddr, unsigned int *tagidxp);
++extern int                elan4mmu_pteload (ELAN4_CTXT *ctxt, int tbl, E4_Addr vaddr, E4_uint64 pte);
++extern void               elan4mmu_unload_range (ELAN4_CTXT *ctxt, int tbl, E4_Addr start, unsigned long len);
++extern void               elan4mmu_invalidate_ctxt (ELAN4_CTXT *ctxt);
++
++extern ELAN4_HASH_CACHE  *elan4mmu_reserve (ELAN4_CTXT *ctxt, int tbl, E4_Addr start, unsigned int npages, int cansleep);
++extern void               elan4mmu_release (ELAN4_CTXT *ctxt, ELAN4_HASH_CACHE *hc);
++extern void               elan4mmu_set_pte (ELAN4_CTXT *ctxt, ELAN4_HASH_CACHE *hc, unsigned int idx, E4_uint64 newpte);
++extern E4_uint64          elan4mmu_get_pte (ELAN4_CTXT *ctxt, ELAN4_HASH_CACHE *hc, unsigned int idx);
++extern void               elan4mmu_clear_pte (ELAN4_CTXT *ctxt, ELAN4_HASH_CACHE *hc, unsigned int idx);
++
++/* mmu_osdep.c */
++extern int              elan4mmu_categorise_paddr (ELAN4_DEV *dev, physaddr_t *physp);
++extern int                elan4mmu_alloc_topaddr (ELAN4_DEV *dev, physaddr_t paddr, unsigned type);
++extern E4_uint64          elan4mmu_phys2pte (ELAN4_DEV *dev, physaddr_t paddr, unsigned perm);
++extern physaddr_t       elan4mmu_pte2phys (ELAN4_DEV *dev, E4_uint64 pte);
++
++/* neterr.c */
++extern int                elan4_neterr_init (ELAN4_DEV *dev);
++extern void               elan4_neterr_destroy (ELAN4_DEV *dev);
++extern int                elan4_neterr_sendmsg (ELAN4_DEV *dev, unsigned int nodeid, unsigned int retries, ELAN4_NETERR_MSG *msg);
++extern int                elan4_neterr_iproc_trap (ELAN4_DEV *dev, ELAN4_IPROC_TRAP *trap);
++
++/* routetable.c */
++extern ELAN4_ROUTE_TABLE *elan4_alloc_routetable (ELAN4_DEV *dev, unsigned size);
++extern void               elan4_free_routetable (ELAN4_DEV *dev, ELAN4_ROUTE_TABLE *tbl);
++extern void               elan4_write_route (ELAN4_DEV *dev, ELAN4_ROUTE_TABLE *tbl, unsigned vp, E4_VirtualProcessEntry *entry);
++extern void               elan4_read_route (ELAN4_DEV *dev, ELAN4_ROUTE_TABLE *tbl, unsigned vp, E4_VirtualProcessEntry *entry);
++extern void               elan4_invalidate_route (ELAN4_DEV *dev, ELAN4_ROUTE_TABLE *tbl, unsigned vp);
++extern int                elan4_generate_route (ELAN_POSITION *pos, E4_VirtualProcessEntry *route, unsigned ctxnum,
++                                              unsigned lowid, unsigned highid, unsigned options);
++extern int              elan4_check_route (ELAN_POSITION *pos, ELAN_LOCATION location, E4_VirtualProcessEntry *route, unsigned flags);
++
++/* user.c */
++extern int        __categorise_command (E4_uint64 command, int *cmdSize);
++extern int        __whole_command (sdramaddr_t *commandPtr, sdramaddr_t insertPtr, unsigned int cqSize, unsigned int cmdSize);
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++#endif /* __ELAN4_ELANDEV_H */
+Index: linux-2.6.5/include/elan4/device_Linux.h
+===================================================================
+--- linux-2.6.5.orig/include/elan4/device_Linux.h      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan4/device_Linux.h   2005-05-11 12:10:12.601907800 -0400
+@@ -0,0 +1,117 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN4_ELANDEV_LINUX_H
++#define __ELAN4_ELANDEV_LINUX_H
++
++#ident "$Id: device_Linux.h,v 1.19.2.1 2005/03/07 16:29:06 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/device_Linux.h,v $*/
++
++#include <qsnet/autoconf.h>
++
++#if !defined(NO_COPROC)                               /* The older coproc kernel patch is applied */
++#include <linux/coproc.h>
++
++#define ioproc_ops            coproc_ops_struct
++#define ioproc_register_ops   register_coproc_ops
++#define ioproc_unregister_ops unregister_coproc_ops
++
++#define IOPROC_MM_STRUCT_ARG  1
++#define IOPROC_PATCH_APPLIED  1
++
++#elif !defined(NO_IOPROC)                     /* The new ioproc kernel patch is applied */
++#include <linux/ioproc.h>
++
++#define IOPROC_PATCH_APPLIED  1
++#endif
++
++
++#if defined(MPSAS)
++#include <elan4/mpsas.h>
++#endif
++
++#if defined(CONFIG_DEVFS_FS)
++#include <linux/devfs_fs_kernel.h>
++#endif
++
++#define ELAN4_MAJOR              61
++#define ELAN4_NAME               "elan4"
++#define ELAN4_MAX_CONTROLLER     16          /* limited to 4 bits */
++ 
++/* OS dependant component of ELAN4_DEV struct */
++typedef struct elan4_dev_osdep
++{
++    struct pci_dev      *pdev;                        /* PCI config data */
++
++    struct proc_dir_entry *procdir;
++    struct proc_dir_entry *configdir;
++    struct proc_dir_entry *statsdir;
++
++#if defined(CONFIG_DEVFS_FS)
++    devfs_handle_t devfs_control;
++    devfs_handle_t devfs_sdram;
++    devfs_handle_t devfs_user;
++#endif
++
++#if defined(CONFIG_MTRR)
++    int                          sdram_mtrr;
++    int                          regs_mtrr;
++#endif
++} ELAN4_DEV_OSDEP;
++
++/* /dev/elan/rmsX */
++
++/* /dev/elan4/controlX */
++typedef struct control_private
++{
++    struct elan4_dev   *pr_dev;
++    unsigned          pr_boundary_scan;
++} CONTROL_PRIVATE;
++
++/* /dev/elan4/sdramX */
++typedef struct mem_page
++{
++    struct mem_page *pg_next;
++    sdramaddr_t      pg_addr;
++    unsigned long    pg_pgoff;
++    unsigned       pg_ref;
++} MEM_PAGE;
++
++#define MEM_HASH_SIZE 32
++#define MEM_HASH(pgoff)       ((pgoff) & (MEM_HASH_SIZE-1))
++
++typedef struct mem_private
++{
++    struct elan4_dev *pr_dev;
++    MEM_PAGE         *pr_pages[MEM_HASH_SIZE];
++    spinlock_t        pr_lock;
++} MEM_PRIVATE;
++
++/* /dev/elan4/userX */
++typedef struct user_private
++{
++    atomic_t        pr_ref;
++    struct user_ctxt *pr_uctx;
++    struct mm_struct *pr_mm;
++
++#if defined(IOPROC_PATCH_APPLIED)
++    struct ioproc_ops pr_ioproc;
++#endif
++} USER_PRIVATE;
++
++/* No mapping handles on linux */
++typedef void *ELAN4_MAP_HANDLE;
++
++#define ELAN4_TASK_HANDLE()   ((unsigned long) current->mm)
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++#endif /* __ELAN4_ELANDEV_LINUX_H */
+Index: linux-2.6.5/include/elan4/dma.h
+===================================================================
+--- linux-2.6.5.orig/include/elan4/dma.h       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan4/dma.h    2005-05-11 12:10:12.602907648 -0400
+@@ -0,0 +1,82 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN4_DMA_H
++#define __ELAN4_DMA_H
++
++#ident "$Id: dma.h,v 1.16 2003/09/04 12:39:17 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan4hdr/dma.h,v $*/
++
++#include <elan4/types.h>
++
++/* Alignment for a DMA descriptor */
++#define E4_DMA_ALIGN          (64)
++
++/* Maximum size of a single DMA ((1 << 31)-1) */
++#define E4_MAX_DMA_SIZE               (0x7fffffff)
++
++/* 
++ * dma_typeSize
++ *
++ * [63:32]    Size
++ * [31]               unused
++ * [30]               IsRemote
++ * [29]               QueueWrite
++ * [28]               ShmemWrite
++ * [27:26]    DataType
++ * [25]               Broadcast
++ * [24]               AlignPackets
++ * [23:16]    FailCount
++ * [15:14]    unused
++ * [13:0]     Context
++ */
++
++#define DMA_FailCount(val)    (((val) & 0xff) << 16)
++#define DMA_AlignPackets      (1 << 24)
++#define DMA_Broadcast         (1 << 25)
++#define DMA_ShMemWrite                (1 << 28)
++#define DMA_QueueWrite                (1 << 29)
++#define DMA_IsRemote          (1 << 30)
++#define DMA_Context(val)      ((unsigned) (val) & 0x3ff)
++#define DMA_ContextMask               0x3fffull
++#define Dma_TypeSizeMask      0xfffffffffff00000ull
++
++#define DMA_DataTypeByte      (E4_DATATYPE_BYTE  << 26)
++#define DMA_DataTypeShort     (E4_DATATYPE_SHORT << 26)
++#define DMA_DataTypeWord      (E4_DATATYPE_WORD  << 26)
++#define DMA_DataTypeLong      (E4_DATATYPE_DWORD << 26)
++
++#define E4_DMA_TYPE_SIZE(size, dataType, flags, failCount)    \
++    ((((E4_uint64)(size)) << 32) |  ((dataType) & DMA_DataTypeLong) | \
++     (flags) | DMA_FailCount(failCount))
++
++typedef volatile struct e4_dma
++{
++    E4_uint64         dma_typeSize;
++    E4_uint64         dma_cookie;
++    E4_uint64         dma_vproc;
++    E4_Addr           dma_srcAddr;
++    E4_Addr           dma_dstAddr;
++    E4_Addr           dma_srcEvent;
++    E4_Addr           dma_dstEvent;
++} E4_DMA;
++
++/* Same as above but padded to 64-bytes */
++typedef volatile struct e4_dma64
++{
++    E4_uint64         dma_typeSize;
++    E4_uint64         dma_cookie;
++    E4_uint64         dma_vproc;
++    E4_Addr           dma_srcAddr;
++    E4_Addr           dma_dstAddr;
++    E4_Addr           dma_srcEvent;
++    E4_Addr           dma_dstEvent;
++    E4_Addr           dma_pad;
++} E4_DMA64;
++
++#endif /* __ELAN4_DMA_H */
+Index: linux-2.6.5/include/elan4/events.h
+===================================================================
+--- linux-2.6.5.orig/include/elan4/events.h    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan4/events.h 2005-05-11 12:10:12.602907648 -0400
+@@ -0,0 +1,179 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN4_EVENTS_H
++#define __ELAN4_EVENTS_H
++
++#ident "$Id: events.h,v 1.22 2004/06/23 11:07:18 addy Exp $"
++/*      $Source: /cvs/master/quadrics/elan4hdr/events.h,v $*/
++
++#define E4_EVENT_ALIGN                32
++#define E4_EVENTBLOCK_SIZE    64
++
++#ifndef _ASM
++/*
++ * Event locations must be aligned to a 32 byte boundary. It is very much more efficent to place
++ * them in elan local memory but is not essential.
++ */
++typedef struct _E4_Event
++{
++    volatile E4_uint64        ev_CountAndType;
++    E4_uint64         ev_Params[2];
++} E4_Event;
++
++/* Same as above but padded to correct Event alignment */
++typedef struct _E4_Event32
++{
++    volatile E4_uint64        ev_CountAndType;
++    E4_uint64         ev_Params[2];
++    E4_uint64         ev_pad;
++} E4_Event32;
++
++/*
++ * An E4_EVENTBLOCK_SIZE aligned block of Main or Elan memory
++ */
++typedef union _E4_Event_Blk
++{
++    /* Padded to 64-bytes in case a cache-line write is more efficient */
++    volatile E4_uint8  eb_unit8[E4_EVENTBLOCK_SIZE];
++    volatile E4_uint32 eb_uint32[E4_EVENTBLOCK_SIZE/sizeof(E4_uint32)];
++    volatile E4_uint64 eb_uint64[E4_EVENTBLOCK_SIZE/sizeof(E4_uint64)];
++} E4_Event_Blk;
++#define eb_done       eb_uint32[14]
++#define eb_done_dword eb_uint64[7]
++
++#endif /* ! _ASM */
++
++/*
++ * ev_CountAndType
++ *  [63:31]   Count
++ *  [10]      CopyType
++ *  [9:8]     DataType
++ *  [7:0]     CopySize
++ */
++#define E4_EVENT_TYPE_MASK    0x00000000ffffffffull
++#define E4_EVENT_COUNT_MASK   0xffffffff00000000ull
++#define E4_EVENT_COUNT_SHIFT  32
++#define E4_EVENT_COPY_TYPE_MASK       (1 << 10)
++#define E4_EVENT_DATA_TYPE_MASK       (3 << 8)
++#define E4_EVENT_COPY_SIZE_MASK       (0xff)
++
++/* CopyType */
++#define E4_EVENT_COPY         (0 << 10)
++#define E4_EVENT_WRITE                (1 << 10)
++
++/* DataType */
++#define E4_EVENT_DTYPE_BYTE   (0 << 8)
++#define E4_EVENT_DTYPE_SHORT  (1 << 8)
++#define E4_EVENT_DTYPE_WORD   (2 << 8)
++#define E4_EVENT_DTYPE_LONG   (3 << 8)
++
++#define EVENT_COUNT(EventPtr) ((E4_int32)(elan4_load64 (&(EventPtr)->ev_CountAndType) >> E4_EVENT_COUNT_SHIFT))
++#define EVENT_TYPE(EventPtr)  ((E4_uint32)(elan4_load64 (&(EventPtr)->ev_CountAndType) & E4_EVENT_TYPE_MASK))
++
++#define E4_WAITEVENT_COUNT_TYPE_VALUE(Count, EventType, DataType, CopySize) \
++      (((E4_uint64)(Count) << E4_EVENT_COUNT_SHIFT) | (EventType) | (DataType) | (CopySize))
++
++#define E4_EVENT_TYPE_VALUE(EventType, DataType, CopySize)    \
++      ((EventType) | (DataType) | (CopySize))
++
++#define E4_EVENT_INIT_VALUE(InitialCount, EventType, DataType, CopySize)      \
++      (((E4_uint64)(InitialCount) << E4_EVENT_COUNT_SHIFT) | E4_EVENT_TYPE_VALUE(EventType, DataType, CopySize))
++
++#define ev_CopySource ev_Params[0]
++#define ev_CopyDest   ev_Params[1]
++#define ev_WritePtr   ev_Params[0]
++#define ev_WriteValue ev_Params[1]
++
++#define EVENT_BLK_READY(BLK) ((BLK)->eb_done != 0)
++#define EVENT_READY(EVENT)   ((E4_uint32)((((volatile E4_Event *) (EVENT))->ev_CountAndType) >> E4_EVENT_COUNT_SHIFT) >= 0)
++
++#define ELAN_WAIT_EVENT (0)
++#define ELAN_POLL_EVENT (-1)
++
++#define E4_BLK_PATTERN        ((E4_uint32)0xfeedface)
++
++#define E4_INIT_COPY_EVENT(EVENT, BLK_ELAN, BLK, SIZE)                                                        \
++      do {                                                                                            \
++         elan4_store64 (E4_EVENT_INIT_VALUE(0, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, SIZE), &(EVENT)->ev_CountAndType); \
++           elan4_store64 ((BLK_ELAN), &(EVENT)->ev_CopySource); \
++         elan4_store64 ((BLK), &(EVENT)->ev_CopyDest); \
++      } while (0)
++
++#define E4_INIT_WRITE_EVENT(EVENT, DWORD)                                                             \
++      do {                                                                                            \
++          elan4_store64 (E4_EVENT_INIT_VALUE(0, E4_EVENT_WRITE, E4_EVENT_DTYPE_LONG, 0), &(EVENT)->ev_CountAndType);  \
++          elan4_store64 ((DWORD), &(EVENT)->ev_WritePtr); \
++          elan4_store64 ((E4_Addr) (E4_BLK_PATTERN), &(EVENT)->ev_WriteValue); \
++      } while (0)
++
++#define E4_RESET_BLK_EVENT(BLK)                                       \
++      do {                                                            \
++              (BLK)->eb_done = (0);                                   \
++      } while (0)
++
++#define E4_PRIME_BLK_EVENT(EVENT, COUNT)                              \
++      do {                                                            \
++         elan4_store64 (E4_EVENT_INIT_VALUE(COUNT, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, 8), &(EVENT)->ev_CountAndType);\
++      } while (0)
++
++#define E4_PRIME_COPY_EVENT(EVENT, SIZE, COUNT)                               \
++      do {                                                            \
++         elan4_store64 (E4_EVENT_INIT_VALUE(COUNT, E4_EVENT_COPY, E4_EVENT_DTYPE_LONG, (SIZE >> 3)), &(EVENT)->ev_CountAndType);\
++      } while (0)
++
++#define E4_PRIME_WRITE_EVENT(EVENT, COUNT)                                    \
++      do {                                                                    \
++         elan4_store64 (E4_EVENT_INIT_VALUE(COUNT, E4_EVENT_WRITE, E4_EVENT_DTYPE_LONG, 0), &(EVENT)->ev_CountAndType);\
++      } while (0)
++
++#ifndef _ASM
++
++#define E4_INPUTQ_ALIGN                       32      /* Descriptor must be 32-byte aligned */
++
++typedef struct _E4_InputQueue
++{
++   volatile E4_Addr   q_bptr;         /* 64 bit aligned ptr to current back item */
++   E4_Addr            q_fptr;         /* 64 bit aligned ptr to current front item */
++   E4_uint64          q_control;      /* this defines the last item, item size, and offset back to the first item. */
++   E4_Addr            q_event;        /* queue event */
++} E4_InputQueue;
++
++#define E4_INPUTQ_LASTITEM_MASK       0x00000000ffffffffULL
++#define E4_INPUTQ_ITEMSIZE_MASK               0x000000ff00000000ULL
++#define E4_INPUTQ_LASTITEM_OFFSET_MASK        0xffffff0000000000ULL
++#define E4_INPUTQ_LASTITEM_SHIFT      0
++#define E4_INPUTQ_ITEMSIZE_SHIFT      32
++#define E4_INPUTQ_LASTITEM_OFFSET_SHIFT       40
++
++/*
++ * Macro to initialise the InputQueue control word given the FirstItem, LastItem & ItemSize
++ * FirstItem and LastItem are 64 bit double word aligned elan addresses.
++ */
++#define E4_InputQueueControl(FirstItem, LastItem, ItemSizeInBytes)\
++   (((((E4_uint64)(LastItem)))                                                      & E4_INPUTQ_LASTITEM_MASK) |\
++    ((((E4_uint64)(ItemSizeInBytes))        << (E4_INPUTQ_ITEMSIZE_SHIFT-3))        & E4_INPUTQ_ITEMSIZE_MASK)  |\
++    ((((E4_uint64)((FirstItem)-(LastItem))) << (E4_INPUTQ_LASTITEM_OFFSET_SHIFT-3)) & E4_INPUTQ_LASTITEM_OFFSET_MASK))    
++
++/* 
++ * LastItemOffset is a sign extended -ve quantity with LastItemOffset[26:3] == q_control[63:40]
++ * we sign extend this by setting LastItemOffset[63:27] to be #one.
++ */
++#define E4_InputQueueLastItemOffset(control)  ((((E4_int64) -1) << (64 - (E4_INPUTQ_LASTITEM_OFFSET_SHIFT-3))) | \
++                                             ((E4_int64) (((control) & E4_INPUTQ_LASTITEM_OFFSET_MASK) >> (E4_INPUTQ_LASTITEM_OFFSET_SHIFT-3))))
++#define E4_InputQueueItemSize(control)              (((control) & E4_INPUTQ_ITEMSIZE_MASK) >> (E4_INPUTQ_ITEMSIZE_SHIFT-3))
++
++/*
++ * Macro to increment the InputQ front pointer taking into account wrap 
++ */
++#define E4_InputQueueFptrIncrement(Q, FirstItem, LastItem, ItemSizeInBytes) \
++      ((Q)->q_fptr = ( ((Q)->q_fptr == (LastItem)) ? (FirstItem) : ((Q)->q_fptr + (ItemSizeInBytes))) )
++
++#endif /* _ASM */
++
++#endif /* __ELAN4_EVENTS_H */
+Index: linux-2.6.5/include/elan4/i2c.h
+===================================================================
+--- linux-2.6.5.orig/include/elan4/i2c.h       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan4/i2c.h    2005-05-11 12:10:12.602907648 -0400
+@@ -0,0 +1,47 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef _ELAN4_I2C_H
++#define _ELAN4_I2C_H
++
++#ident "@(#)$Id: i2c.h,v 1.10 2003/12/02 16:11:22 lee Exp $ $Name: QSNETMODULES-4-31_20050321 $"
++/*      $Source: /cvs/master/quadrics/elan4hdr/i2c.h,v $*/
++
++/* I2C address space - bits[7:1] */
++#define I2C_LED_I2C_ADDR                      0x20
++#define I2C_TEMP_ADDR                         0x48
++#define I2C_EEPROM_ADDR                               0x50
++
++#define I2C_WRITE_ADDR(addr)                  ((addr) << 1 | 0)
++#define I2C_READ_ADDR(addr)                   ((addr) << 1 | 1)
++
++/* I2C EEPROM appears as 8 I2C 256 byte devices */
++#define I2C_24LC16B_BLOCKSIZE                 (256)
++#define I2C_24LC16B_BLOCKADDR(addr)           ((addr) >> 8)
++#define I2C_24LC16B_BLOCKOFFSET(addr)         ((addr) & 0xff)
++
++#define I2C_ELAN_EEPROM_PCI_BASEADDR          0       /* PCI config starts at addr 0 in the EEPROM */
++#define I2C_ELAN_EEPROM_VPD_BASEADDR          256     /* VPD data start                            */
++#define I2C_ELAN_EEPROM_PCI_SIZE              256     /* PCI data max size                         */
++#define I2C_ELAN_EEPROM_VPD_SIZE              256     /* VPD data max size                         */
++
++#define I2C_ELAN_EEPROM_SIZE                  2048
++
++#define I2C_ELAN_EEPROM_DEVICE_ID             0xA0
++#define I2C_ELAN_EEPROM_FAIL_LIMIT              8
++
++#define I2C_ELAN_EEPROM_ADDR_BLOCKSIZE_SHIFT  0x8
++#define I2C_ELAN_EEPROM_ADDR_BLOCK_MASK               0x7
++#define I2C_ELAN_EEPROM_ADDR_BLOCK_SHIFT      0x1
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++#endif /* _ELAN4_I2C_H */
+Index: linux-2.6.5/include/elan4/intcookie.h
+===================================================================
+--- linux-2.6.5.orig/include/elan4/intcookie.h 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan4/intcookie.h      2005-05-11 12:10:12.603907496 -0400
+@@ -0,0 +1,62 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: intcookie.h,v 1.10 2004/08/09 14:02:37 daniel Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/intcookie.h,v $*/
++
++#ifndef __ELAN4_INTCOOKIE_H
++#define __ELAN4_INTCOOKIE_H
++
++typedef E4_uint64 ELAN4_INTCOOKIE;
++
++#ifdef __KERNEL__
++
++typedef struct intcookie_entry
++{
++    struct intcookie_entry    *ent_next;
++    struct intcookie_entry    *ent_prev;
++
++    spinlock_t                       ent_lock;
++    unsigned                 ent_ref;
++
++    ELAN4_INTCOOKIE          ent_cookie;
++    ELAN4_INTCOOKIE          ent_fired;
++    kcondvar_t                       ent_wait;
++} INTCOOKIE_ENTRY;
++
++typedef struct intcookie_table
++{
++    struct intcookie_table    *tbl_next;
++    struct intcookie_table    *tbl_prev;
++
++    ELAN_CAPABILITY           *tbl_cap;
++
++    spinlock_t                       tbl_lock;
++    unsigned                 tbl_ref;
++    INTCOOKIE_ENTRY           *tbl_entries;
++} INTCOOKIE_TABLE;
++
++extern void                intcookie_init(void);
++extern void                intcookie_fini(void);
++extern INTCOOKIE_TABLE    *intcookie_alloc_table (ELAN_CAPABILITY *cap);
++extern void                intcookie_free_table (INTCOOKIE_TABLE *tbl);
++extern int                 intcookie_alloc (INTCOOKIE_TABLE *tbl, ELAN4_INTCOOKIE cookie);
++extern int                 intcookie_free (INTCOOKIE_TABLE *tbl, ELAN4_INTCOOKIE cookie);
++extern int                 intcookie_fire (INTCOOKIE_TABLE *tbl, ELAN4_INTCOOKIE cookie);
++extern int                 intcookie_fire_cap (ELAN_CAPABILITY *cap, ELAN4_INTCOOKIE cookie);
++extern int                 intcookie_wait (INTCOOKIE_TABLE *tbl, ELAN4_INTCOOKIE cookie);
++extern int                 intcookie_arm (INTCOOKIE_TABLE *tbl, ELAN4_INTCOOKIE cookie);
++
++#endif /* __KERNEL */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++#endif /* __ELAN4_INTCOOKIE_H */
+Index: linux-2.6.5/include/elan4/ioctl.h
+===================================================================
+--- linux-2.6.5.orig/include/elan4/ioctl.h     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan4/ioctl.h  2005-05-11 12:10:12.603907496 -0400
+@@ -0,0 +1,320 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN4_IOCTL_H
++#define __ELAN4_IOCTL_H
++
++#ident "@(#)$Id: ioctl.h,v 1.27.6.2 2005/01/11 12:15:39 duncant Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/ioctl.h,v $*/
++
++#include <elan/devinfo.h>
++#include <elan/capability.h>
++
++#include <elan4/dma.h>
++#include <elan4/neterr.h>
++#include <elan4/registers.h>
++#include <elan4/intcookie.h>
++
++#define ELAN4IO_CONTROL_PATHNAME      "/dev/elan4/control%d"
++#define ELAN4IO_USER_PATHNAME         "/dev/elan4/user%d"
++#define ELAN4IO_SDRAM_PATHNAME        "/dev/elan4/sdram%d"
++#define ELAN4IO_MAX_PATHNAMELEN       32
++
++/*
++ * NOTE - ioctl values 0->0x1f are defined for 
++ *        generic/control usage.
++ */
++
++/* Macro to generate 'offset' to mmap "control" device */
++#define OFF_TO_BAR(off)               (((off) >> 28) & 0xF)
++#define OFF_TO_OFFSET(off)    ((off) & 0x0FFFFFFF)
++#define GEN_OFF(bar,off)      (((bar) << 28) | ((off) & 0x0FFFFFFF))
++
++/* Definiations for generic ioctls */
++#define ELAN4IO_GENERIC_BASE          0x00
++
++typedef struct elan4io_stats_struct
++{
++    int                      which;
++    unsigned long long ptr;                                   /* always pass pointer as 64 bit */
++} ELAN4IO_STATS_STRUCT;
++
++#define ELAN4IO_STATS                 _IOR ('e', ELAN4IO_GENERIC_BASE + 0, ELAN4IO_STATS_STRUCT)
++#define ELAN4IO_DEVINFO                       _IOR ('e', ELAN4IO_GENERIC_BASE + 1, ELAN_DEVINFO)
++#define ELAN4IO_POSITION              _IOR ('e', ELAN4IO_GENERIC_BASE + 2, ELAN_POSITION)
++
++
++/* 
++ * Definitions for /dev/elan4/controlX
++ */
++#define ELAN4IO_CONTROL_BASE          0x20
++
++#define ELAN4IO_GET_POSITION          _IOR ('e', ELAN4IO_CONTROL_BASE + 0, ELAN_POSITION)
++#define ELAN4IO_SET_POSITION          _IOW ('e', ELAN4IO_CONTROL_BASE + 1, ELAN_POSITION)
++#define ELAN4IO_DEBUG_SNAPSHOT                _IOW ('e', ELAN4IO_CONTROL_BASE + 2, )
++
++typedef struct elan4io_params_mask_struct
++{
++    unsigned short    p_mask;
++    ELAN_PARAMS               p_params;
++} ELAN4IO_PARAMS_STRUCT;
++#define ELAN4IO_GET_PARAMS            _IOR ('e', ELAN4IO_CONTROL_BASE + 3, ELAN4IO_PARAMS_STRUCT)
++#define ELAN4IO_SET_PARAMS            _IOW ('e', ELAN4IO_CONTROL_BASE + 4, ELAN4IO_PARAMS_STRUCT)
++
++/* old versions - implicit p_mask == 3 */
++#define ELAN4IO_OLD_GET_PARAMS                _IOR ('e', ELAN4IO_CONTROL_BASE + 3, ELAN_PARAMS)
++#define ELAN4IO_OLD_SET_PARAMS                _IOW ('e', ELAN4IO_CONTROL_BASE + 4, ELAN_PARAMS)
++
++/*
++ * Definitions for /dev/elan4/userX
++ */
++#define ELAN4IO_USER_BASE             0x40
++
++#define ELAN4IO_FREE                  _IO   ('e', ELAN4IO_USER_BASE + 0)
++#define ELAN4IO_ATTACH                        _IOWR ('e', ELAN4IO_USER_BASE + 1, ELAN_CAPABILITY)
++#define ELAN4IO_DETACH                        _IOWR ('e', ELAN4IO_USER_BASE + 2, ELAN_CAPABILITY)
++#define ELAN4IO_BLOCK_INPUTTER                _IO   ('e', ELAN4IO_USER_BASE + 3)
++
++typedef struct elan4io_add_p2pvp_struct 
++{
++    unsigned        vp_process;
++    ELAN_CAPABILITY vp_capability;
++} ELAN4IO_ADD_P2PVP_STRUCT;
++
++#define ELAN4IO_ADD_P2PVP             _IOW  ('e', ELAN4IO_USER_BASE + 4, ELAN4IO_ADD_P2PVP_STRUCT)
++
++typedef struct elan4io_add_bcastvp_struct
++{
++    unsigned int      vp_process;
++    unsigned int      vp_lowvp;
++    unsigned int      vp_highvp;
++} ELAN4IO_ADD_BCASTVP_STRUCT;
++
++#define ELAN4IO_ADD_BCASTVP           _IOW  ('e', ELAN4IO_USER_BASE + 5, ELAN4IO_ADD_BCASTVP_STRUCT)
++
++#define ELAN4IO_REMOVEVP              _IO   ('e', ELAN4IO_USER_BASE + 6)
++
++typedef struct elan4io_route_struct
++{
++    unsigned int         rt_process;
++    unsigned int         rt_error;
++    E4_VirtualProcessEntry rt_route;
++} ELAN4IO_ROUTE_STRUCT;
++
++#define ELAN4IO_SET_ROUTE             _IOW  ('e', ELAN4IO_USER_BASE + 7, ELAN4IO_ROUTE_STRUCT)
++#define ELAN4IO_RESET_ROUTE           _IOW  ('e', ELAN4IO_USER_BASE + 9, ELAN4IO_ROUTE_STRUCT)
++#define ELAN4IO_GET_ROUTE             _IOWR ('e', ELAN4IO_USER_BASE + 8, ELAN4IO_ROUTE_STRUCT)
++#define ELAN4IO_CHECK_ROUTE           _IOWR ('e', ELAN4IO_USER_BASE + 10, ELAN4IO_ROUTE_STRUCT)
++
++typedef struct elan4io_alloc_cq_struct
++{
++    unsigned int cq_size;                                     /* input: size of queue */
++    unsigned int cq_perm;                                     /* input: requested permissions */
++    unsigned int cq_type;                                     /* input: queue type */
++    unsigned int cq_indx;                                     /* output: queue number */
++} ELAN4IO_ALLOCCQ_STRUCT;
++
++#define ELAN4IO_ALLOCCQ                       _IOWR ('e', ELAN4IO_USER_BASE + 11, ELAN4IO_ALLOCCQ_STRUCT)
++#define ELAN4IO_FREECQ                _IOWR ('e', ELAN4IO_USER_BASE + 12, unsigned)
++
++#define ELAN4IO_CQ_TYPE_REORDER               1                       /* revb reordering command queue */
++
++typedef struct elan4io_perm_struct
++{
++    E4_Addr            ps_eaddr;
++    E4_uint64          ps_len;
++    unsigned long      ps_maddr;
++    unsigned int       ps_perm;
++} ELAN4IO_PERM_STRUCT;
++
++typedef struct elan4io_perm_struct32
++{
++    E4_Addr            ps_eaddr;
++    E4_uint64          ps_len;
++    unsigned int       ps_maddr;
++    unsigned int       ps_perm;
++} ELAN4IO_PERM_STRUCT32;
++
++#define ELAN4IO_SETPERM                       _IOWR ('e', ELAN4IO_USER_BASE + 13, ELAN4IO_PERM_STRUCT)
++#define ELAN4IO_SETPERM32             _IOWR ('e', ELAN4IO_USER_BASE + 13, ELAN4IO_PERM_STRUCT32)
++#define ELAN4IO_CLRPERM                       _IOWR ('e', ELAN4IO_USER_BASE + 14, ELAN4IO_PERM_STRUCT)
++#define ELAN4IO_CLRPERM32             _IOWR ('e', ELAN4IO_USER_BASE + 14, ELAN4IO_PERM_STRUCT32)
++
++typedef struct elan4io_trapsig_struct
++{
++    int               ts_signo;
++} ELAN4IO_TRAPSIG_STRUCT;
++#define ELAN4IO_TRAPSIG                       _IOW  ('e', ELAN4IO_USER_BASE + 15, ELAN4IO_TRAPSIG_STRUCT)
++
++typedef struct elan4io_traphandler_struct
++{
++    unsigned int       th_nticks;                             /* number of ticks to sleep for next trap */
++    unsigned int       th_proc;                                       /* elan processor involved */
++    unsigned long      th_trapp;                              /* space to store trap */
++} ELAN4IO_TRAPHANDLER_STRUCT;
++
++typedef struct elan4io_traphandler_struct32
++{
++    unsigned int       th_nticks;                             /* number of ticks to sleep for next trap */
++    unsigned int       th_proc;                                       /* elan processor involved */
++    unsigned int       th_trapp;                              /* space to store trap */
++} ELAN4IO_TRAPHANDLER_STRUCT32;
++
++#define ELAN4IO_TRAPHANDLER           _IOW  ('e', ELAN4IO_USER_BASE + 16, ELAN4IO_TRAPHANDLER_STRUCT)
++#define ELAN4IO_TRAPHANDLER32         _IOW  ('e', ELAN4IO_USER_BASE + 16, ELAN4IO_TRAPHANDLER_STRUCT32)
++
++typedef struct elan4io_required_mappings_struct
++{
++    E4_Addr    rm_upage_addr;                                 /* elan address of user page */
++    E4_Addr    rm_trestart_addr;                              /* elan address of tproc restart trampoline */
++} ELAN4IO_REQUIRED_MAPPINGS_STRUCT;
++#define ELAN4IO_REQUIRED_MAPPINGS     _IOW  ('e', ELAN4IO_USER_BASE + 17, ELAN4IO_REQUIRED_MAPPINGS_STRUCT)
++
++typedef struct elan4io_resume_eproc_trap_struct
++{
++    E4_Addr             rs_addr;
++} ELAN4IO_RESUME_EPROC_TRAP_STRUCT;
++#define ELAN4IO_RESUME_EPROC_TRAP     _IOW  ('e', ELAN4IO_USER_BASE + 18, ELAN4IO_RESUME_EPROC_TRAP_STRUCT)
++
++typedef struct elan4io_resume_cproc_trap_struct
++{
++    unsigned int      rs_indx;
++} ELAN4IO_RESUME_CPROC_TRAP_STRUCT;
++#define ELAN4IO_RESUME_CPROC_TRAP     _IOW  ('e', ELAN4IO_USER_BASE + 19, ELAN4IO_RESUME_CPROC_TRAP_STRUCT)
++
++typedef struct elan4io_resume_dproc_trap_struct
++{
++    E4_DMA            rs_desc;
++} ELAN4IO_RESUME_DPROC_TRAP_STRUCT;
++#define ELAN4IO_RESUME_DPROC_TRAP     _IOW  ('e', ELAN4IO_USER_BASE + 20, ELAN4IO_RESUME_DPROC_TRAP_STRUCT)
++
++typedef struct elan4io_resume_tproc_trap_struct
++{
++    E4_ThreadRegs     rs_regs;
++} ELAN4IO_RESUME_TPROC_TRAP_STRUCT;
++#define ELAN4IO_RESUME_TPROC_TRAP     _IOW  ('e', ELAN4IO_USER_BASE + 21, ELAN4IO_RESUME_TPROC_TRAP_STRUCT)
++
++typedef struct elan4io_resume_iproc_trap_struct
++{
++    unsigned int      rs_channel;
++    unsigned int      rs_trans;
++    E4_IprocTrapHeader  rs_header;
++    E4_IprocTrapData    rs_data;
++} ELAN4IO_RESUME_IPROC_TRAP_STRUCT;
++#define ELAN4IO_RESUME_IPROC_TRAP     _IOW  ('e', ELAN4IO_USER_BASE + 22, ELAN4IO_RESUME_IPROC_TRAP_STRUCT)
++
++#define ELAN4IO_FLUSH_ICACHE          _IO   ('e', ELAN4IO_USER_BASE + 23)
++#define ELAN4IO_STOP_CTXT             _IO   ('e', ELAN4IO_USER_BASE + 24)
++
++#define ELAN4IO_ALLOC_INTCOOKIE               _IOW  ('e', ELAN4IO_USER_BASE + 25, ELAN4_INTCOOKIE)
++#define ELAN4IO_FREE_INTCOOKIE                _IOW  ('e', ELAN4IO_USER_BASE + 26, ELAN4_INTCOOKIE)
++#define ELAN4IO_ARM_INTCOOKIE         _IOW  ('e', ELAN4IO_USER_BASE + 27, ELAN4_INTCOOKIE)
++#define ELAN4IO_WAIT_INTCOOKIE                _IOW  ('e', ELAN4IO_USER_BASE + 28, ELAN4_INTCOOKIE)
++
++typedef struct elan4io_alloc_trap_queues_struct
++{
++    unsigned int      tq_ndproc_traps;
++    unsigned int      tq_neproc_traps;
++    unsigned int      tq_ntproc_traps;
++    unsigned int      tq_nthreads;
++    unsigned int      tq_ndmas;
++} ELAN4IO_ALLOC_TRAP_QUEUES_STRUCT;
++#define ELAN4IO_ALLOC_TRAP_QUEUES     _IOW  ('e', ELAN4IO_USER_BASE + 29, ELAN4IO_ALLOC_TRAP_QUEUES_STRUCT)
++
++typedef struct elan4io_neterr_msg_struct
++{
++    unsigned int      nm_vp;
++    unsigned int      nm_nctx;
++    unsigned int      nm_retries;
++    unsigned int        nm_pad;
++    ELAN4_NETERR_MSG    nm_msg;
++} ELAN4IO_NETERR_MSG_STRUCT;
++#define ELAN4IO_NETERR_MSG            _IOW ('e', ELAN4IO_USER_BASE + 30, ELAN4IO_NETERR_MSG_STRUCT)
++
++typedef struct elan4io_neterr_timer_struct 
++{
++    unsigned int      nt_usecs;
++} ELAN4IO_NETERR_TIMER_STUCT;
++
++#define ELAN4IO_NETERR_TIMER          _IO  ('e', ELAN4IO_USER_BASE + 31)
++
++typedef struct elan4io_neterr_fixup_struct
++{
++    E4_uint64         nf_cookie;
++    unsigned int      nf_waitforeop;
++    unsigned int      nf_sten;
++    unsigned int      nf_vp;
++    unsigned int      nf_pad;
++} ELAN4IO_NETERR_FIXUP_STRUCT;
++
++#define ELAN4IO_NETERR_FIXUP          _IOW ('e', ELAN4IO_USER_BASE + 32, ELAN4IO_NETERR_FIXUP_STRUCT)
++
++typedef struct elan4io_firecap_struct 
++{
++    ELAN_CAPABILITY     fc_capability;
++    ELAN4_INTCOOKIE     fc_cookie;
++} ELAN4IO_FIRECAP_STRUCT;
++
++#define ELAN4IO_FIRE_INTCOOKIE                _IOW  ('e', ELAN4IO_USER_BASE + 33, ELAN4IO_FIRECAP_STRUCT)
++
++#define ELAN4IO_ALLOC_INTCOOKIE_TABLE _IOW  ('e', ELAN4IO_USER_BASE + 34, ELAN_CAPABILITY)
++#define ELAN4IO_FREE_INTCOOKIE_TABLE  _IO   ('e', ELAN4IO_USER_BASE + 35)
++
++typedef struct elan4io_translation
++{
++    E4_Addr           tr_addr;
++    unsigned long     tr_len;
++    unsigned int      tr_access;
++} ELAN4IO_TRANSLATION_STRUCT;
++
++#define ELAN4IO_LOAD_TRANSLATION      _IOW  ('e', ELAN4IO_USER_BASE + 36, ELAN4IO_TRANSLATION_STRUCT)
++#define ELAN4IO_UNLOAD_TRANSLATION    _IOW  ('e', ELAN4IO_USER_BASE + 37, ELAN4IO_TRANSLATION_STRUCT)
++
++typedef struct elan4io_dumpcq_struct32
++{
++    E4_uint64 cq_space;      /* output: sdram addr of q, used to decode ptrs */
++    E4_uint32 cq_size;       /* output: The real size of the command queue */
++    E4_uint32 bufsize;       /* input: The size of the buffer to dump to */
++    E4_uint32 cq_indx;       /* input: index of cq to dump */
++    unsigned int buffer;     /* input: user address of rgs->buffer to dump to */
++} ELAN4IO_DUMPCQ_STRUCT32;
++
++typedef struct elan4io_dumpcq_struct
++{
++    E4_uint64 cq_space;      /* output: sdram addr of q, used to decode ptrs */
++    E4_uint32 cq_size;       /* output: The real size of the command queue */
++    E4_uint32 bufsize;       /* input: The size of the buffer to dump to */
++    E4_uint32 cq_indx;       /* input: index of cq to dump */
++    unsigned long buffer;    /* input: user address of rgs->buffer to dump to */
++} ELAN4IO_DUMPCQ_STRUCT;
++
++#define ELAN4IO_DUMPCQ                        _IOWR ('e', ELAN4IO_USER_BASE + 38, ELAN4IO_DUMPCQ_STRUCT)
++#define ELAN4IO_DUMPCQ32                      _IOWR ('e', ELAN4IO_USER_BASE + 38, ELAN4IO_DUMPCQ_STRUCT32)
++
++/* mmap offsets - - we define the file offset space as follows:
++ *
++ * page 0 - 4095 - command queues
++ * page 4096   - device user registers
++ * page 4097   - flag page/user stats
++ * page 4098   - device stats
++ * page 4099     - tproc trampoline
++ */
++
++#define ELAN4_OFF_COMMAND_QUEUES      0
++#define ELAN4_OFF_USER_REGS           4096
++#define ELAN4_OFF_USER_PAGE           4097
++#define ELAN4_OFF_DEVICE_STATS                4098
++#define ELAN4_OFF_TPROC_TRAMPOLINE    4099
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++#endif /* __ELAN4_IOCTL_H */
+Index: linux-2.6.5/include/elan4/mmu.h
+===================================================================
+--- linux-2.6.5.orig/include/elan4/mmu.h       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan4/mmu.h    2005-05-11 12:10:12.604907344 -0400
+@@ -0,0 +1,94 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: mmu.h,v 1.11 2004/04/21 12:04:24 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/mmu.h,v $*/
++
++
++#ifndef __ELAN4_MMU_H
++#define __ELAN4_MMU_H
++
++typedef struct elan4_hash_entry
++{
++    struct elan4_hash_entry   *he_next;
++    struct elan4_hash_entry   *he_prev;
++
++    sdramaddr_t                        he_entry;
++    
++    struct elan4_hash_entry   *he_chain[2];
++    E4_uint64                  he_tag[2];
++    E4_uint32                  he_pte[2];
++} ELAN4_HASH_ENTRY;
++
++#define ELAN4_HENT_CHUNKS     16              /* SDRAM_MIN_BLOCK_SIZE/sizeof (E4_HashTableEntry) */
++
++typedef struct elan4_hash_chunk
++{
++    struct list_head            hc_link;
++    ELAN4_HASH_ENTRY          hc_hents[ELAN4_HENT_CHUNKS];
++} ELAN4_HASH_CHUNK;
++
++typedef struct elan4_hash_cache
++{
++    E4_Addr           hc_start;
++    E4_Addr           hc_end;
++    int                     hc_tbl;
++
++    ELAN4_HASH_ENTRY *hc_hes[1];
++} ELAN4_HASH_CACHE;
++
++/* 
++ * he_pte is really 4 bytes of pte "type" one for each pte
++ * entry - however we declare it as an "int" so we can
++ * easily determine that all 4 entries are invalid 
++ */
++#define HE_SET_PTE(he,tagidx,pteidx,val)      (((E4_uint8 *) &(he->he_pte[tagidx]))[pteidx] = (val))
++#define HE_GET_PTE(he,tagidx,pteidx)          (((E4_uint8 *) &(he->he_pte[tagidx]))[pteidx])
++
++/*
++ * he_tag has the following form :
++ *     [63:27]        tag
++ *     [20:17]  pte valid
++ *     [16]     locked
++ *     [15]     copy
++ *     [14]     valid
++ *     [13:0] context
++ */
++
++#define HE_TAG_VALID          (1 << 14)
++#define HE_TAG_COPY           (1 << 15)
++#define HE_TAG_LOCKED         (1 << 16)
++
++#define INVALID_CONTEXT               0
++
++extern u_char elan4_permtable[];
++#define ELAN4_INCOMPAT_ACCESS(perm,access) ((elan4_permtable[(perm)] & (1 << (access))) == 0)
++extern u_char elan4_permreadonly[];
++#define ELAN4_PERM_READONLY(perm)       (elan4_permreadonly[(perm)])
++
++/* return code from elan4mmu_categorise_paddr */
++#define ELAN4MMU_PADDR_SDRAM          0
++#define ELAN4MMU_PADDR_COMMAND                1
++#define ELAN4MMU_PADDR_LOCALPCI               2
++#define ELAN4MMU_PADDR_PAGE           3
++#define ELAN4MMU_PADDR_OTHER          4
++
++extern int elan4_debug_mmu;
++
++#ifdef DEBUG_PRINTF
++#  define MPRINTF(ctxt,lvl,args...)   (elan4_debug_mmu > (lvl) ? elan4_debugf(ctxt,DBG_MMU, ##args) : (void)0)
++#else
++#  define MPRINTF(ctxt,lvl,args...)   ((void) 0)
++#endif
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++#endif /* __ELAN4_MMU_H */
+Index: linux-2.6.5/include/elan4/neterr.h
+===================================================================
+--- linux-2.6.5.orig/include/elan4/neterr.h    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan4/neterr.h 2005-05-11 12:10:12.604907344 -0400
+@@ -0,0 +1,40 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2004 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN4_NETERR_H
++#define __ELAN4_NETERR_H
++
++#ident "@(#)$Id: neterr.h,v 1.1 2004/01/19 14:38:34 david Exp $ $Name: QSNETMODULES-4-31_20050321 $"
++/*      $Source: /cvs/master/quadrics/elan4mod/neterr.h,v $*/
++
++typedef struct elan4_neterr_msg
++{
++    E4_uint8          msg_type;
++    E4_uint8          msg_waitforeop;
++    E4_uint16         msg_context;                            /* network context # message sent to */
++    E4_int16          msg_found;                              /* # cookie found (response) */
++
++    ELAN_LOCATION     msg_sender;                             /* nodeid/context # message sent from */
++    E4_uint32         msg_pad;
++
++    E4_uint64         msg_cookies[6];                         /* 64 bit cookies from identify packets */
++} ELAN4_NETERR_MSG;
++
++#define ELAN4_NETERR_MSG_SIZE         sizeof (ELAN4_NETERR_MSG)
++#define ELAN4_NETERR_MSG_REQUEST      1
++#define ELAN4_NETERR_MSG_RESPONSE     2
++
++#define ELAN4_NETERR_MAX_COOKIES      (sizeof (((ELAN4_NETERR_MSG *) 0)->msg_cookies) / \
++                                       sizeof (((ELAN4_NETERR_MSG *) 0)->msg_cookies[0]))
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++#endif /* __ELAN4_NETERR_H */
+Index: linux-2.6.5/include/elan4/pci.h
+===================================================================
+--- linux-2.6.5.orig/include/elan4/pci.h       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan4/pci.h    2005-05-11 12:10:12.605907192 -0400
+@@ -0,0 +1,227 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN4_PCI_H 
++#define __ELAN4_PCI_H
++
++#ident "$Id: pci.h,v 1.32 2003/09/04 12:39:17 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan4hdr/pci.h,v $*/
++
++/* Elan has 2 64 bit bars */
++#define ELAN4_BAR_SDRAM                       0
++#define ELAN4_BAR_REGISTERS           2
++
++#define PCI_VENDOR_ID_QUADRICS                0x14fc
++#define PCI_DEVICE_ID_ELAN3           0x0000
++#define   PCI_REVISION_ID_ELAN3_REVA  0x0000
++#define   PCI_REVISION_ID_ELAN3_REVB  0x0001
++#define PCI_DEVICE_ID_ELAN4           0x0001
++#define   PCI_REVISION_ID_ELAN4_REVA  0x0000
++#define   PCI_REVISION_ID_ELAN4_REVB  0x0001
++
++/* support standard pseudo bars */
++#define ELAN4_PSEUDO_BAR_ROM          8
++
++/* Elan PCI control
++ configuration space register. ElanControlRegister */
++#define PCI_ELAN_PARITY_ADDR_LO               0x40
++#define PCI_ELAN_PARITY_ADDR_HI               0x44
++#define PCI_ELAN_PARITY_TYPE          0x48
++#define PCI_ELAN_CONTROL              0x4c
++#define PCI_ELAN_PLL_CONTROL          0x50
++#define PCI_ELAN_SPLIT_MESSAGE_ATTR   0x54
++#define PCI_ELAN_SPLIT_MESSAGE_VALUE  0x54
++#define PCI_ELAN_RAMBIST_FAILED               0x54
++#define PCI_ELAN_TOPPHYSADDR(i)               (0x58 + ((i)<<1))
++
++/*
++ * [31]          PciM66EN             This is set it the bus is running in PCI2.3 - 66MHz mode.
++ * [30:28] InitPattern                This gives the PCI-X startup mode. See "Pci intialisation patterns" below.
++ * [27]          notBusIs64Bits       If set the bus is running 32 bits wide. If Clear it is a 64 bit bus.
++ * [26:24] RamBistCntl                Used to control the Elan4 RAM BIST. Not acitive it zero.
++ * [23]          RamBistFinished      Only used when performing the RAM BIST test.
++ * [22]          SelectSplitMessAttr  See ECTRL_SELECT_SPLIT_MESS_ATTR below.
++ * [21]          ReceivedSplitCompError See ECTRL_REC_SPLIT_COMP_MESSAGE below
++ * [20:16] WriteHighPriTime   Used with ReadHighPriTime to control the ratio of PCI master write to PCI master
++ *                            read bandwidth under heavy load. The high the value of WriteHighPriTime the longer
++ *                            the PCI write bursts will be allowed without interruption from a read transfer.
++ * [15]    DisableCouplingTest        This is only used as part of the RAM BIST test. It effects the testing of the main
++ *                            cache tag RAMS.
++ * [14:13] Not used           Will read as zero.
++ * [12:8]  ReadHighPriTime    Used with WriteHighPriTime to control the ratio of PCI master write to PCI master
++ *                            read bandwidth under heavy load. The high the value of ReadHighPriTime the longer
++ *                            the PCI read bursts will be allowed without interruption from a write transfer.
++ * [7] EnableLatencyCountReset  This bit effect the behaviour of disconnects due to the removal of GNT# after the latency
++ *                            counter has expired. If set it will allow the latency counter to be reset each time the
++ *                            GNT# is reasserted. If asserted it should provided improved bandwidth on the PCI bus
++ *                            without increasing the maximum latency another device would have for access to the bus.
++ *                            It will increase the average latency of other devices.
++ * [6] ExtraMasterAddrBits    This bit used to control the physical PCI addresses generated by the MMU.
++ * [5] ReducedPciDecode               If set the PCI local memory BAR will decode 256Mbytes of PCI address space. If clear it
++ *                            will decode 2Gbyte of PCI address space.
++ * [4] ConfigInEBusRom                If set the constant values of the Elan4 PCI configuration space will be taken from the
++ *                            EEPROM. If clear the internal values will be used.
++ * [3] EnableRd2_2Bursts      This bit only effects the behaviour of burst reads when the PCI bus is operating in
++ *                            PCI-2.2 mode. It allows adjacent reads to be merged into longer bursts for higher
++ *                            performance.
++ * [2] SoftIntReset           If set this bit will cause the Elan4 to reset itself with the exception of the PCI
++ *                            configuation space. All internal state machines will be put into the reset state.
++ * [1] EnableWrBursts         This bit allows much longer PCI-X write bursts. If set it will stop the Elan4 from
++ *                            being completely PCI-X compliant as the Elan4 may request a long PCI-X write burst that
++ *                            it does not complete. However it should significantly increase the maximum PCI-X write
++ *                            bandwidth and is unlikely to cause problems with many PCI-X bridge chips.
++ * [0] InvertMSIPriority      This bit effect the way MSI interrupts are generated. It provides flexiblity to generate
++ *                            the MSI interrupts in a different way to allow for different implimentations of MSI
++ *                            logic and still give the correct priority of Elan4 interrupts.
++ *
++ *    {PciM66EN, InitPattern, notBusIs64Bits, RamBistCntl, RamBistFinished,
++ *     SelectSplitMessAttr, ReceivedSplitCompError, WriteHighPriTime,
++ *     DisableCouplingTest, 2'h0, ReadHighPriTime,
++ *     EnableLatencyCountReset, ExtraMasterAddrBits, ReducedPciDecode, ConfigInEBusRom,
++ *     EnableRd2_2Bursts, SoftIntReset, EnableWrBursts, InvertMSIPriority}
++ */
++
++#define ECTRL_INVERT_MSI_PRIO         (1 << 0)
++#define ECTRL_ENABLE_WRITEBURSTS      (1 << 1)
++#define ECTRL_SOFTWARE_INTERNAL_RESET (1 << 2)
++#define ECTRL_ENABLE_2_2READBURSTS    (1 << 3)
++#define ECTRL_CONFIG_IN_EBUS_ROM      (1 << 4)
++#define ECTRL_28_NOT_30_BIT_LOCAL_BAR (1 << 5)
++#define ECTRL_ExtraMasterAddrBits     (1 << 6)
++#define ECTRL_ENABLE_LATENCY_RESET      (1 << 7)
++#define ECTRL_DISABLE_COUPLING_TEST   (1 << 15)
++
++/*
++ * Ratio of the following two registers set the relative bandwidth given to intputer data
++ * versus other PCI pci traffic when scheduling new PCI master accesses.
++ */
++#define ECTRL_OTHER_HIGH_PRI_TIME_SHIFT       (8)     /* Sets top 4 bits of 8 bit counter */
++#define ECTRL_OTHER_HIGH_PRI_TIME_MASK        (0x1f)
++
++
++#define ECTRL_IPROC_HIGH_PRI_TIME_SHIFT       (16)    /* Sets top 4 bits of 8 bit counter */
++#define ECTRL_IPROC_HIGH_PRI_TIME_MASK        (0x1f)
++
++/*
++ * This is set if a split completion message is received.
++ * This will cause a PCI error interrupt.
++ * This error is cleared by writting a 1 to this bit.
++ */
++#define ECTRL_REC_SPLIT_COMP_MESSAGE  (1 << 21)
++/*
++ * This bit is used to select reading of either the Split message attribute value when
++ * set or the split completion message data value from 0x54 in the config space
++ * if the ECTRL_REC_SPLIT_COMP_MESSAGE bit is set. 0x54 returns the the BistFailed flags
++ * if any of the BIST control bits are set (bits 26 to 24)
++ */
++#define ECTRL_SELECT_SPLIT_MESS_ATTR  (1 << 22)
++
++// Internal RAM bist control bits.
++// Three bits of state control the RAM BIST (Built in self test).
++//
++// These bits must not be set unless the ECTRL_SOFTWARE_INTERNAL_RESET bit has also been set!
++//
++// For a normal fast ram test assert ECTRL_BIST_FAST_TEST. 
++// For a data retention test first write ECTRL_START_RETENTION_TEST then wait the retention period of
++// at least 1ms and preferably much longer then write ECTRL_CONTINUE_RETENTION_TEST then wait
++// again and finallly write ECTRL_FINISH_RETENTION_TEST.
++// 
++// The read only bit ECTRL_BIST_FINISHED_TEST can be polled to check that the test has compleated.
++#define ECTRL_BIST_CTRL_SHIFT         (24)
++#define ECTRL_BIST_CTRL_MASK          (7 << 24)
++
++#define ECTRL_BIST_FAST_TEST          ((7 << 24) | ECTRL_SOFTWARE_INTERNAL_RESET)     // old scheme
++#define ECTRL_START_RETENTION_TEST    ((1 << 24) | ECTRL_SOFTWARE_INTERNAL_RESET)
++#define ECTRL_CONTINUE_RETENTION_TEST ((3 << 24) | ECTRL_SOFTWARE_INTERNAL_RESET)
++#define ECTRL_FINISH_RETENTION_TEST   ((7 << 24) | ECTRL_SOFTWARE_INTERNAL_RESET)
++
++#define ECTRL_BIST_KICK_OFF           ((1 << 24) | ECTRL_SOFTWARE_INTERNAL_RESET)     // new scheme
++#define ECTRL_BIST_MOVE_ON_ODD                ((3 << 24) | ECTRL_SOFTWARE_INTERNAL_RESET)
++#define ECTRL_BIST_MOVE_ON_EVEN               ((5 << 24) | ECTRL_SOFTWARE_INTERNAL_RESET)
++#define ECTRL_BIST_SCREAM_THROUGH     ((7 << 24) | ECTRL_SOFTWARE_INTERNAL_RESET)
++
++#define ECTRL_CLEAR_BIST_TEST         (0 << 24)
++#define ECTRL_BIST_FINISHED_TEST      (1 << 23)
++
++// Read only current PCI bus type.
++#define ECTRL_RUNNING_32BIT_MODE      (1 << 27)
++#define ECTRL_INITIALISATION_MODE     (7 << 28)
++#define ECTRL_RUNNING_M66EN_MODE      (1 << 31)
++
++#define ECTRL_INIT_PATTERN_SHIFT      (28)
++#define ECTRL_INIT_PATTERN_MASK               (0x7)
++
++// Pci intialisation patterns
++#define Pci2_2                                (0 << 28)
++#define PciX50To66MHz                 (1 << 28)
++#define PciX66to100MHz                        (2 << 28)
++#define PciX100to133MHz                       (3 << 28)
++#define PciXReserved1                 (4 << 28)
++#define PciXReserved2                 (5 << 28)
++#define PciXReserved3                 (6 << 28)
++#define PciXReserved4                 (7 << 28)
++
++/* Elan PCI pll and pad control configuration space register. ElanPllControlReg */
++// This overrides the default PCI pll control settings.
++#define PciPll_FeedForwardISel0               (1 << 0)        // Lsi name Z0
++#define PciPll_FeedForwardISel1               (1 << 1)        // Lsi name Z1
++#define PciPll_ChargePumpISel0                (1 << 2)        // Lsi name P0
++#define PciPll_ChargePumpISel1                (1 << 3)        // Lsi name P1
++#define PciPll_EnableAutoReset                (1 << 4)        // Lsi name ENARST
++#define PciPll_RSEL200500             (1 << 5)        // Lsi name Range Select, 0: 100 - 250MHz, 1: 200 - 500MHz
++#define PciPll_DivideFeedback         (1 << 6)        // Just used for test - This divides the shortcut feedback to the PCI PLL so that it can lock to the tester clock.
++#define PciPll_CutFeedback            (1 << 7)        // Just used for test - This disables the shortcut feedback.
++
++// This overrides the default PCI BZ controler settings.
++#define PciBZ_UPDI                    (0xf << 8)
++#define PciBZ_WAIT_INT                        (0xf << 12)
++
++// This overrides the default Sys and SDRam pll control settings.
++#define SysPll_FeedForwardISel0               (1 << 16)       // Lsi name P0     
++#define SysPll_FeedForwardISel1               (1 << 17)       // Lsi name P1     
++#define SysPll_ChargePumpISel0                (1 << 18)       // Lsi name Z0    
++#define SysPll_ChargePumpISel1                (1 << 19)       // Lsi name Z1    
++#define SysPll_EnableAutoReset                (1 << 20)       // Lsi name ENARST
++#define SysPll_DivPhaseCompInBy2      (1 << 21)       // Lsi name NODIV (Should be DIV)
++#define SysPll_PllTestClkSel          (1 << 22)       // If asserted the master clock source is not taken from the pll.
++
++#define Pll_ForceEBusADTristate               (1 << 23)       // Required to enable the testing of EnableAutoReset. Enables use of EBusAD[7] (rev A)
++#define Pll_LinkErrDirectToSDA                (1 << 23)       // Access to link error flag for triggering (rev B)
++
++
++#define ECTRL_SYS_CLOCK_RATIO_SHIFT   (24)
++// Config: with 800MHz                Speeds are 266 200 160 133.
++//         0 = 133/133 (1:1)  6:6     1
++//       1 = 160/133 (6:5)    5:6     1.2
++//         2 = 200/133 (3:2)  4:6     1.5
++//       3 = 266/133 (2:1)    3:6     2
++//       4 = 200/200 (1:1)    4:4     1
++//       5 = 266/200 (4:3)    3:4     1.33
++
++// Config: with 600MHz                Speeds are 200 150 120 100
++//         0 = 100/100 (1:1)  6:6     1
++//       1 = 120/100 (6:5)    5:6     1.2
++//         2 = 150/100 (3:2)  4:6     1.5
++//       3 = 200/100 (2:1)    3:6     2
++//       4 = 150/150 (1:1)    4:4     1
++//       5 = 200/150 (4:3)    3:4     1.33
++
++#define ECTRL_SYS_CLOCK_RATIO_SHIFT   (24)
++#define ECTRL_SYS_CLOCK_RATIO_1_1Slow (0 << ECTRL_SYS_CLOCK_RATIO_SHIFT)
++#define ECTRL_SYS_CLOCK_RATIO_6_5     (1 << ECTRL_SYS_CLOCK_RATIO_SHIFT)
++#define ECTRL_SYS_CLOCK_RATIO_3_2     (2 << ECTRL_SYS_CLOCK_RATIO_SHIFT)
++#define ECTRL_SYS_CLOCK_RATIO_2_1     (3 << ECTRL_SYS_CLOCK_RATIO_SHIFT)
++#define ECTRL_SYS_CLOCK_RATIO_1_1Fast (4 << ECTRL_SYS_CLOCK_RATIO_SHIFT)
++#define ECTRL_SYS_CLOCK_RATIO_4_3     (5 << ECTRL_SYS_CLOCK_RATIO_SHIFT)
++#define ECTRL_SYS_CLOCK_MAX_NORMAL    (6)                                     /* used to generate a valid random value */
++#define GET_RANDOM_CLOCK_RATIO                (Random(ECTRL_SYS_CLOCK_MAX_NORMAL) << ECTRL_SYS_CLOCK_RATIO_SHIFT)
++#define ECTRL_SYS_CLOCK_RATIO_PLL_TEST        (6 << ECTRL_SYS_CLOCK_RATIO_SHIFT)
++#define ECTRL_SYS_CLOCK_RATIO_TEST    (7 << ECTRL_SYS_CLOCK_RATIO_SHIFT)
++#define ECTRL_SYS_CLOCK_RATIO_MASK    (7 << ECTRL_SYS_CLOCK_RATIO_SHIFT)
++
++#endif /* __ELAN4_PCI_H */
+Index: linux-2.6.5/include/elan4/registers.h
+===================================================================
+--- linux-2.6.5.orig/include/elan4/registers.h 2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan4/registers.h      2005-05-11 12:10:12.608906736 -0400
+@@ -0,0 +1,1587 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef _ELAN4_REGISTERS_H
++#define _ELAN4_REGISTERS_H
++
++#ident "$Id: registers.h,v 1.117.2.3 2005/03/03 16:29:57 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan4hdr/registers.h,v $*/
++
++/*
++ * Header file for internal slave mapping of the ELAN4 registers
++ */
++
++#define E4_CACHELINE_SIZE     (64)
++#define E4_STACK_ALIGN                (64)
++
++#ifndef _ASM
++
++#include <elan4/types.h>
++#include <elan4/dma.h>
++#include <elan4/userregs.h>
++
++typedef volatile struct _E4_CacheSets
++{
++   E4_uint64  Set0[1024];     /* 8k bytes per set */
++   E4_uint64  Set1[1024];     /* 8k bytes per set */
++   E4_uint64  Set2[1024];     /* 8k bytes per set */
++   E4_uint64  Set3[1024];     /* 8k bytes per set */
++} E4_CacheSets;
++
++typedef union e4_cache_tag
++{
++   struct {
++       E4_uint32 pad0;                        /* Undefined value when read */
++#if (BYTE_ORDER == LITTLE_ENDIAN) || defined(__LITTLE_ENDIAN__)
++       E4_uint32 :10;                                         /* 0-9   - reserved */
++       E4_uint32 LineError:1;                                 /* 10    - line error */
++       E4_uint32 Modified:1;                                  /* 11    - modified */
++       E4_uint32 FillPending:1;                                       /* 12    - fill pending */
++       E4_uint32 AddrTag30to13:18;                            /* 30-13 - tag */
++       E4_uint32 :1;                                          /* 31    -  */
++#else
++       E4_uint32 :1;                                          /* 31    -  */
++       E4_uint32 AddrTag30to13:18;                            /* 30-13 - tag */
++       E4_uint32 FillPending:1;                                       /* 12    - fill pending */
++       E4_uint32 Modified:1;                                  /* 11    - modified */
++       E4_uint32 LineError:1;                                 /* 10    - line error */
++       E4_uint32 :10;                                         /* 0-9   - reserved */
++#endif
++   } s;
++   E4_uint64  Value;
++} E4_CacheTag;
++
++typedef volatile struct _E4_CacheTags
++{
++   E4_CacheTag Tags[4][128];  /* 8k bytes per set, 64 byte cache line */
++} E4_CacheTags;
++
++#define E4_NumCacheSets               4
++#define E4_NumCacheLines      128
++#define E4_CacheLineSize      64
++#define E4_CacheSize          (E4_NumCacheSets * E4_NumCacheLines * E4_CacheLineSize)
++#define E4_CacheSetSize       (E4_NumCacheLines * E4_CacheLineSize)
++
++/*
++ * Run Queue pointers 
++ *
++ * [62:35]    FrontPointer[30:3]
++ * [33:32]    Size Value
++ * [30:3]     BackPointer[30:3]
++ */
++#define E4_QueuePtrMask               (0x7ffffff8ULL)
++#define E4_QueueSizeMask      3
++#define E4_QueueEntrySize       sizeof (E4_uint64)
++
++#define E4_Queue8KBytes               0
++#define E4_Queue64KBytes      1
++#define E4_Queue512KBytes     2
++#define E4_Queue4MBytes               3
++
++#define E4_QueueFrontValue(val,size)  ((val) | (size))
++#define E4_QueueValue(queue,size)     (((E4_uint64) E4_QueueFrontValue(queue,size)) << 32 | ((E4_uint64) (queue)))
++
++#define E4_QueueFrontPointer(val)     /* extract queue front pointer from register */\
++      (((val) >> 32) & E4_QueuePtrMask)
++#define E4_QueueBackPointer(val)      /* extract queue back pointer from register */ \
++      ((val) & E4_QueuePtrMask)
++#define E4_QueueSizeValue(val)                /* extract queue size value from register */ \
++      (((val) >> 32) & E4_QueueSizeMask)
++#define E4_QueueSize(value)           /* queue size in bytes from size value */ \
++      (1 << (((value)*3) + 13))
++#define E4_QueueOffsetMask(fptr)\
++        ((8192 << (((fptr) & E4_QueueSizeMask) << 3)) - 1)
++#define E4_QueueOffset(fptr)\
++        ((fptr) & E4_QueueOffsetMask(fptr))
++#define E4_QueueFrontPointerInc(fptr)   \
++        ( ((fptr) & ~E4_QueueOffsetMask(fptr)) | ((E4_QueueOffset(fptr) + 8) & E4_QueueOffsetMask(fptr)) )
++
++typedef union _E4_QueuePtr
++{
++   E4_uint64  Value;
++   struct {
++       E4_uint32 Back;
++       E4_uint32 Front;
++   } s;
++} E4_QueuePtr;
++
++/*
++ * DMA processor status register.
++ *
++ * [48]               FirstSendTrans          Set for the first packet of a dma.
++ * [47:46]    TimeSliceCount          Time left to timeslice.
++ * [45]               DmaLastPacket           Set for the last packet of a dma.
++ * [44]               CurrPrefetchDma         Dma descriptor the prefetcher is valid for.
++ * [43:39]    PrefetcherState         Dma prefetcher's state machines value.
++ * [38:33]    PacketAssemblyState     Packet assembler's state machines value.
++ * [32:31]    PrefetcherWakeupFnt     Dma prefetcher's wakeup function.
++ * [30:28]    PacketAssWakeupFnt      Packet assembler's wakeup function.
++ * [27]               AckBufferValid          Packet ack is valid.
++ * [26]               PrefetchedDataProblem   Had either a data read fault or data error. Valid if AckBufferValid.
++ * [25]               PrefetcherHalting       Prefetch data about to stop for halt. Valid if AckBufferValid.
++ * [24]               PacketTimeout           Packet timeout. Sent an EopError. Valid if AckBufferValid set.
++ * [23:22]    PacketAckValue          Packet ack type. Valid if AckBufferValid set.
++ * [21:20]    FaultUnitNo             Set if the dma prefetcher has faulted.
++ * [19:17]    TrapType                Packet assembler's trap type.
++ * [16]               PrefetcherFault         Set if the dma prefetcher has faulted for this DMA unit.
++ * [15]               Remote                  The Dma had been issued remotly
++ * [14]               Priority                Running at high priority.
++ * [13:0]     Context                 procs current context.
++ */
++
++#define DPROC_FirstSendTrans(s)               ((unsigned)((s) >> 48) & 1)
++#define DPROC_TimeSliceCount(s)               ((unsigned)(((s) >> 46) & 3)
++#define DPROC_DmaLastPacket(s)                ((unsigned)((s) >> 45) & 1)
++#define DPROC_CurrPrefetchDma(s)      ((unsigned)((s) >> 44) & 1)
++#define DPROC_PrefetcerState(s)               ((unsigned)((s) >> 39) & 0x1f)
++#define DPROC_PacketAssemblerState(s) ((unsigned)((s) >> 33) & 0x1f)
++#define DPROC_PrefetcherWakeupFn(s)   ((unsigned)((s) >> 31) & 3)
++#define DPROC_PacketAssemblerWakeupFn(s)((unsigned)((s) >> 28) & 3)
++#define DPROC_AckBufferValid(s)               ((unsigned)((s) >> 27) & 1)
++#define DPROC_PrefetcherDataProblem(s)        ((unsigned)((s) >> 26) & 1)
++#define DPROC_PrefetcherHalting(s)    ((unsigned)((s) >> 25) & 1)
++#define DPROC_PacketTimeout(s)                ((unsigned)((s) >> 24) & 1)
++#define DPROC_PacketAckValue(s)               ((unsigned)((s) >> 22) & 3)
++#define DPROC_FaultUnitNo(s)          ((unsigned)((s) >> 20) & 3)
++#define DPROC_TrapType(s)             ((unsigned)((s) >> 17) & 7)
++#define DPROC_PrefetcherFault(s)      ((unsigned)((s) >> 16) & 1)
++#define DPROC_Remote(s)                       ((unsigned)((s) >> 15) & 1)
++#define DPROC_Priority(s)             ((unsigned)((s) >> 14) & 1)
++#define DPROC_Context(s)              ((unsigned)(s) & 0x3fff)
++
++/*
++ * Command processor status register.
++ *
++ * [26:21]    CPState         procs current state.
++ * [20]               WakeupFnt       procs wakeup function.
++ * [19:16]    TrapValue       procs trap value.
++ * [15]               Remote          Issued remotely.
++ * [14]               Priority        Running at high priority.
++ * [13:0]     Context         procs current context.
++ */
++
++#define CPROC_TrapType(s)             ((unsigned)((s) >> 16) & 0xf)
++#define CPROC_Remote(s)                       ((unsigned)((s) >> 15) & 0x1)
++#define CPROC_Priority(s)             ((unsigned)((s) >> 14) & 0x1)
++#define CPROC_Context(s)              ((unsigned)(s) & 0x3fff)
++
++/*
++ * Event processor status register.
++ *
++ * [34:30]    CPState         event procs current state.
++ * [29:28]    WakeupFnt       event procs wakeup function.
++ * [27:20]    EventCopySize   This is the number of DWords to still be copied on a copy dword event.
++ * [19]               EProcPort1Fault CUN_EventProc1 has taken a translation fault.
++ * [18]               EProcPort0Fault CUN_EventProc0 has taken a translation fault.
++ * [17:16]    TrapValue       event proc's trap value.
++ * [15]               Remote          Issued remotely.
++ * [14]               Priority        Running at high priority.
++ * [13:0]     Context         procs current context.
++ */
++
++#define EPROC_CPState(s)              ((unsigned)((s) >> 30) & 0x1f)
++#define EPROC_WakeupFunction(s)               ((unsigned)((s) >> 28) & 3)
++#define EPROC_CopySize(s)             ((unsigned)((s) >> 20) & 0xFF)
++#define EPROC_Port1Fault(s)           ((unsigned)((s) >> 19) & 1)
++#define EPROC_Port0Fault(s)           ((unsigned)((s) >> 18) & 1)
++#define EPROC_TrapType(s)             ((unsigned)((s) >> 16) & 3)
++#define EPROC_Remote(s)                       ((unsigned)((s) >> 15) & 1)
++#define EPROC_Priority(s)             ((unsigned)((s) >> 14) & 1)
++#define EPROC_Context(s)              ((unsigned)(s) & 0x3fff)
++
++/*
++ * Thread processor status register.
++ *
++ * [39:24]    MemPortBusy             16 bits of port busy flags for all FFU memory ports.
++ * [23:21]    Reads as zero
++ * [20:18]    TQState                 State vector for thread queuing proc.
++ * [17]               HighRunQueueFull        High priority run queue is full
++ * [16]               LowRunQueueFull         Low priority run queue is full
++ * [15]               ReadyHigh               More runable threads at high priority
++ * [14]               ReadyLow                More runable threads at low priority
++ * [13:0]     Context                 procs current context.
++ */
++#define TPROC_HighRunQueueFull(s)     ((unsigned)((s) >> 17) & 1)
++#define TPROC_LowRunQueueFull(s)      ((unsigned)((s) >> 16) & 1)
++#define TPROC_ReadyHigh(s)            ((unsigned)((s) >> 15) & 1)
++#define TPROC_ReadyLow(s)             ((unsigned)((s) >> 14) & 1)
++#define TPROC_Context(s)              ((unsigned)((s) & 0x3fff))
++
++/*
++ * Input processor status register
++ *
++ * [55]               Last Trans (~EOP)
++ * [54]               First Trans (~EOP)
++ * [53]               Channel (~EOP) 
++ * [52]               Bad Length (~EOP)
++ * [51:50]    Trans CRC Status (~EOP)
++ * [49:48]    EOP type
++ * [47]               EOP trap
++ * [46]               Trapping priority
++ * [45]               Trapping Channel
++ * [44:43]    Bad ack sent
++ * [42:41]    Good ack sent
++ * [40]               Queueing Packet (~EOP)
++ * [39:36]    Channel trapped bits
++ * [35:32]    IProc Trap Value
++ * [31:16]    Network Context (~EOP)
++ * [15:0]     Transaction Type (~EOP)
++ */
++#define IPROC_LastTrans(s)            ((unsigned)((s) >> 55) & 0x1)
++#define IPROC_FirstTrans(s)           ((unsigned)((s) >> 54) & 0x1)
++#define IPROC_Channel(s)              ((unsigned)((s) >> 53) & 0x1)
++#define IPROC_BadLength(s)            ((unsigned)((s) >> 52) & 0x1)
++#define IPROC_TransCRCStatus(s)               ((unsigned)((s) >> 50) & 0x3)
++#define IPROC_EOPType(s)              ((unsigned)((s) >> 48) & 0x3)
++#define IPROC_EOPTrap(s)              ((unsigned)((s) >> 47) & 0x1)
++#define IPROC_InputterPri(s)          ((unsigned)((s) >> 46) & 0x1)
++#define IPROC_InputterChan(s)         ((unsigned)((s) >> 45) & 0x1)
++#define IPROC_BadAckSent(s)           ((unsigned)((s) >> 43) & 0x3)
++#define IPROC_GoodAckSent(s)          ((unsigned)((s) >> 41) & 0x3)
++#define IPROC_QueueingPacket(s)               ((unsigned)((s) >> 40) & 0x1)
++#define IPROC_ChannelTrapped(s)               ((unsigned)((s) >> 36) & 0xF)
++#define IPROC_TrapValue(s)            ((unsigned)((s) >> 32) & 0xF)
++#define IPROC_NetworkContext(s)               ((unsigned)((s) >> 16) & 0xFFFF)
++#define IPROC_TransactionType(s)      ((unsigned)(s) & 0xFFFF)
++
++/* values for IPROC_TransCRCStatus */
++#define CRC_STATUS_GOOD    (0)
++#define CRC_STATUS_DISCARD (1)
++#define CRC_STATUS_ERROR   (2)
++#define CRC_STATUS_BAD     (3)
++
++/* values for IPROC_EOPType */
++#define EOP_GOOD         (1)
++#define EOP_BADACK       (2)
++#define EOP_ERROR_RESET          (3)
++
++/*
++ * Interrupt register bits
++ *
++ * There are up to four sources of interrupt for the MSI port.
++ * The Elan will request 4 ports but may only get either 2 or 1 port. The Interrupts are assigned
++ * as shown below:
++ * No Of MSI ints     Low Prioity                                                     High Prioity
++ *    4               Event Ints      OtherInts               Inputer Ints            Hard Error ints.
++ *               i.e.                 Dproc, Tproc, Sten.     HighPri and LowPri      Link errs, ECC errs,
++ *
++ *    2               Event Ints      All other interrupts.
++ *    1               All together.
++ * 
++ * It is not safe to change the number of sources of interrupt while there may be outstanding,
++ * unserviced interrupts pending.
++ * There two forms of encoding. This has been provided in case an MSI implimentation assumes either
++ * a high value to have a high priority or a low value to have a high priority. This is controled
++ * by a bit in the Elan Pci Control register.
++ */
++#define INT_LinkPortKeyFail           (1<<18)
++#define INT_PciMemErr                 (1<<17)
++#define INT_SDRamInt                  (1<<16)
++#define INT_LinkError                 (1<<15)
++#define INT_IProcCh1HighPri           (1<<14)
++#define INT_IProcCh0HighPri           (1<<13)
++#define INT_IProcCh1LowPri            (1<<12)
++#define INT_IProcCh0LowPri            (1<<11)
++#define INT_DiscardingHighPri         (1<<10)
++#define INT_DiscardingLowPri          (1<<9)
++#define INT_CProcHalted                       (1<<8)
++#define INT_TProcHalted                       (1<<7)
++#define INT_DProcHalted                       (1<<6)
++#define INT_EProc                     (1<<5)
++#define INT_TProc                     (1<<4)
++#define INT_CProc                     (1<<3)
++#define INT_Dma1Proc                  (1<<2)
++#define INT_Dma0Proc                  (1<<1)
++#define INT_MainInterrupt             (1<<0)
++
++#define INT_Units             (INT_EProc | INT_TProc | INT_CProc | INT_Dma1Proc | INT_Dma0Proc)
++#define INT_Inputters         (INT_IProcCh1HighPri | INT_IProcCh0HighPri | INT_IProcCh1LowPri | INT_IProcCh0LowPri)
++#define INT_Discarding        (INT_DiscardingHighPri | INT_DiscardingLowPri)
++#define INT_Halted            (INT_CProcHalted | INT_TProcHalted | INT_DProcHalted)
++#define INT_ErrorInterrupts   (INT_PciMemErr | INT_SDRamInt | INT_LinkError)
++
++#define INT_MSI0              INT_MainInterrupt
++#define INT_MSI1              (INT_Units | INT_Discarding | INT_Halted)
++#define INT_MSI2              (INT_Inputters)
++#define INT_MSI3              (INT_ErrorInterrupts)
++
++#define E4_INTERRUPT_REG_SHIFT        32
++#define E4_INTERRUPT_MASK_MASK        (0xffffffffULL)
++
++/*
++ * Trap type values - see trapvalues.v
++ */
++
++#define CommandProcInserterError              0x1
++#define CommandProcPermissionTrap             0x2
++#define CommandProcSendTransInvalid           0x3
++#define CommandProcSendTransExpected          0x4
++#define CommandProcDmaQueueOverflow           0x5
++#define CommandProcInterruptQueueOverflow     0x6
++#define CommandProcMemoryFault                        0x7
++#define CommandProcRouteFetchFault            0x8
++#define CommandProcFailCountZero              0x9
++#define CommandProcAddressAlignment           0xa
++#define CommandProcWaitTrap                   0xb
++#define CommandProcMultipleGuards             0xc
++#define CommandProcOpenOnGuardedChan          0xd
++#define CommandProcThreadQueueOverflow                0xe
++#define CommandProcBadData                      0xf
++
++#define DmaProcNoFault                                0x0
++#define DmaProcRouteFetchFault                        0x1
++#define DmaProcFailCountError                 0x2
++#define DmaProcPacketAckError                 0x3
++#define DmaProcRunQueueReadFault              0x4
++#define DmaProcQueueOverflow                  0x5
++
++#define EventProcNoFault                      0x0
++#define EventProcAddressAlignment             0x1
++#define EventProcMemoryFault                  0x2
++#define EventProcCountWrapError                       0x3
++
++#define InputNoFault                          0x0
++#define InputAddressAlignment                 0x1
++#define InputMemoryFault                      0x2
++#define InputInvalidTransType                 0x3
++#define InputDmaQueueOverflow                 0x4
++#define InputEventEngineTrapped                       0x5
++#define InputCrcErrorAfterPAckOk              0x6
++#define InputEopErrorOnWaitForEop             0x7
++#define InputEopErrorTrap                     0x8
++#define InputDiscardAfterAckOk                        0x9
++ 
++typedef struct _E4_Sched_Status
++{
++    E4_uint32 Status;
++    E4_uint32 Restart;
++} E4_Sched_Status;
++ 
++typedef struct _E4_Input_Ptrs
++{
++    E4_uint32 ContextFilterTable;
++    E4_uint32 TrapBasePtr;
++} E4_Input_Ptrs;
++
++#define SCH_StopLowPriQueues          (1 << 0)
++#define SCH_DProcHalt                 (1 << 1)
++#define SCH_TProcHalt                 (1 << 2)
++#define SCH_CProcHalt                 (1 << 3)
++
++#define SCH_CProcTimeout600ns         (1 << 4)
++#define SCH_CProcTimeout1p4us         (2 << 4)
++#define SCH_CProcTimeout3p0us         (3 << 4)
++#define SCH_CProcTimeout6p2us         (4 << 4)
++#define SCH_CProcTimeout12p6us                (5 << 4)
++#define SCH_CProcTimeout25p4us                (6 << 4)
++#define SCH_CProcTimeout51p0us                (7 << 4)
++#define SCH_DiscardLowPriInput                (1 << 7)
++#define SCH_DiscardHighPriInput               (1 << 8)
++
++#define SCH_DProcTimeslice64us                (0 << 9)
++#define SCH_DProcTimeslice128us               (1 << 9)
++#define SCH_DProcTimeslice256us               (2 << 9)
++#define SCH_DProcTimeslice512us               (3 << 9)
++
++#define SCH_Halt                      (SCH_StopLowPriQueues | SCH_DProcHalt | SCH_TProcHalt | SCH_CProcHalt)
++#define SCH_Discard                   (SCH_DiscardLowPriInput | SCH_DiscardHighPriInput)
++
++#define SCH_RestartCProc              (1 << 0)
++#define SCH_RestartTProc              (1 << 1)
++#define SCH_RestartEProc              (1 << 2)
++#define SCH_RestartDma0Proc           (1 << 3)
++#define SCH_RestartDma1Proc           (1 << 4)
++#define SCH_RestartDmaPrefetchProc    (1 << 5)
++#define SCH_RestartCh0LowPriInput     (1 << 6)
++#define SCH_RestartCh1LowPriInput     (1 << 7)
++#define SCH_RestartCh0HighPriInput    (1 << 8)
++#define SCH_RestartCh1HighPriInput    (1 << 9)
++#define SCH_ClearLinkErrorInt         (1 << 10)
++#define SCH_ContextFilterFlush                (1 << 11)
++
++/*
++ * Link state bits.
++ */
++#define LS_LinkNotReady               (1 << 0) /* Link is in reset or recovering from an error */
++#define LS_Locked             (1 << 1) /* Linkinput PLL is locked */
++#define LS_LockError          (1 << 2) /* Linkinput PLL was unable to lock onto the input clock. */
++#define LS_DeskewError                (1 << 3) /* Linkinput was unable to Deskew all the inputs. (Broken wire?) */
++#define LS_PhaseError         (1 << 4) /* Linkinput Phase alignment error. */
++#define LS_DataError          (1 << 5) /* Received value was neither good data or a token. */
++#define LS_FifoOvFlow0                (1 << 6) /* Channel 0 input fifo overflowed. */
++#define LS_FifoOvFlow1                (1 << 7) /* Channel 1 input fifo overflowed. */
++#define LS_Mod45Changed               (1 << 8) /* Mod45 bit has changed. Error setr to force reset. */
++#define LS_PAckNotSeenError   (1 << 9) /* PAck value not returned for this packet. */
++
++/*
++ * Link State Constant defines, used for writing to LinkSetValue
++ */
++
++#define LRS_DataDel0          0x0
++#define LRS_DataDel1          0x1
++#define LRS_DataDel2          0x2
++#define LRS_DataDel3          0x3
++#define LRS_DataDel4          0x4
++#define LRS_DataDel5          0x5
++#define LRS_DataDel6          0x6
++#define LRS_DataDel7          0x7
++#define LRS_DataDel8          0x8
++#define LRS_LinkInValue               0x9
++#define LRS_PllDelValue               0xA
++#define LRS_ClockEven         0xB
++#define LRS_ErrorVal8to0      0xC
++#define LRS_ErrorVal17to9     0xD
++#define LRS_ErrorVal26to18    0xE
++#define LRS_ErrorVal35to27    0xF
++#define LRS_NumLinkDels         0x10
++
++#define LRS_Pllfast             0x40
++
++typedef struct _E4_CommandControl
++{
++    volatile E4_uint32 CommandQueueDescsBase;
++    volatile E4_uint32 CommandRequeuePtr;
++} E4_CommandControl;
++
++#define E4_CommandRequeueBusy         0x80000000      /* Test against read value of CommandRequeuePtr */
++#define E4_CommandRequeueHighPri      0x1             /* Will requeue onto the high pri queue */
++#define E4_QueueDescPtrMask           0x7fffffe0
++
++typedef struct _E4_CommandQueueDesc
++{
++    E4_uint64 CQ_QueuePtrs;
++    E4_uint64 CQ_HoldingValue;                /* 32 bit value for 32 bit accesses or OutOfOrderMask*/
++    E4_uint64 CQ_AckBuffers;          /* Space for 32 4 bit ack buffer values. */
++    E4_uint64 CQ_Control;
++} E4_CommandQueueDesc;
++
++/*
++ * Rev A - CQ_QueuePtrs
++ * [63]               Unused          Should be set to zero.
++ * [62:51]    Unused          (reads as top of InsertPtr)
++ * [50:35]    CompletedPtr    Completed pointer. This is alligned to a byte address.
++ * [34]               Trapped         Will be set if the command has trapped.
++ * [33:32]    Size            Size of queue.
++ * [31]               Used            Will be set if the descriptor has been changed and written back by the elan.
++ * [30:3]     InsertPtr       Insert pointer. This is alligned to a byte address.
++ * [2]                TimedOut        Will be set if the queue timedout executing a command.
++ * [1]                Priority        When set the queue runs at high priority.
++ * [0]                Error           If this becomes set all new data written to the queue is * discarded.
++ *
++ * Rev B - CQ_QueuePtrs
++ * [63]               TimedOut        Will be set if the queue timedout executing a command.
++ * [62]               Priority        When set the queue runs at high priority.
++ * [61]               QueueType       1=will accept unordered 64 bit PCI writes. 0=will accept ordered 32 or 64 bit PCI writes.
++ * [60:51]    Unused          (reads as top of InsertPtr)
++ * [50:35]    CompletedPtr    Completed pointer. This is alligned to a byte address.
++ * [34]               Trapped         Will be set if the command has trapped.
++ * [33:32]    Size            Size of queue.
++ * [31]               Used            Will be set if the descriptor has been changed and written back by the elan.
++ * [30:3]     InsertPtr       Insert pointer. This is alligned to a byte address.
++ * [2]                OrderControl    Holds bit 8 of last PCI accesses. Used by a reordering queue.
++ * [1:0]      ErrorType       This field has the current error status of the queue.
++ */
++
++/* Common between revA and RevB */
++#define CQ_PtrMask            (0x7ffffff8)                    /* 31 bit sdram address */
++#define CQ_PtrOffsetMask      (0x7fff8)
++#define CQ_PtrBaseMask                (0x7ff80000)
++
++#define CQ_InsertPtrShift     (3 - 3)                         /* InsertPtr is 64 bit aligned */
++#define CQ_SizeShift          (32)
++#  define CQ_Size1K           0
++#  define CQ_Size8K           1
++#  define CQ_Size64K          2
++#  define CQ_Size512K         3
++#  define CQ_SizeMask         3
++
++#define CQ_CompletedPtrShift  (35 - 3)                        /* CompletedPtr is 64 but aligned */
++
++#define CQ_Used                       (1ull << 31)
++#define CQ_Trapped            (1ull << 34)
++
++#define CQ_QueuePtrsValue(Size,Inserter,Completer) \
++      (((E4_uint64) (Size) << CQ_SizeShift) | \
++       ((E4_uint64) (Inserter) << CQ_InsertPtrShift) | \
++       ((E4_uint64) (Completer) << CQ_CompletedPtrShift))
++
++#define CQ_InsertPtr(QueuePtrs) \
++      (((E4_uint64) QueuePtrs) & CQ_PtrMask)
++
++#define CQ_CompletedPtr(QueuePtrs) \
++      (((E4_uint32)((QueuePtrs) >> CQ_CompletedPtrShift) & CQ_PtrOffsetMask) | \
++       (CQ_InsertPtr(QueuePtrs) & CQ_PtrBaseMask))
++
++#define CQ_Size(SizeVal)              (1024 * (1 << ((SizeVal)*3)))
++
++/* Rev A specific */
++#define CQ_RevA_Error                 (1 << 0)
++#define CQ_RevA_Priority              (1 << 1)
++#define CQ_RevA_TimedOut              (1 << 2)
++
++/* Rev B specific */
++#define CQ_RevB_ErrorType(QueuePtr)   ((QueuePtr) & (3 << 0))
++#  define CQ_RevB_NoError             (0ull << 0)
++#  define CQ_RevB_Overflowed          (1ull << 0)
++#  define CQ_RevB_InvalidWriteSize    (2ull << 0)
++#  define CQ_RevB_InvalidWriteOrder   (3ull << 0)
++#define CQ_RevB_OrderControl          (1ull << 2)
++
++#define CQ_RevB_QueueType(QueuePtr)   ((QueuePtr) & (1ull << 61))
++#  define CQ_RevB_ReorderingQueue     (1ull << 61)
++#  define CQ_RevB_32bitWriteQueue     (0ull << 61)
++
++#define CQ_RevB_Priority              (1ull << 62)
++#define CQ_RevB_TimedOut              (1ull << 62)
++
++/* 
++ * CQ_AckBuffers - Packet Ack Values
++ */
++#define PackOk                        (0x0)
++#define PackTestFail          (0x1)
++#define PackDiscard           (0x2)
++#define PackError             (0x7)
++#define PackTimeout           (0x8)
++#define PackWaiting           (0xF)
++#define PackValue(val,chan)   (((val) >> ((chan) * 4)) & 0xf)
++
++/*
++ * CQ_Control
++ * [63:35]    ExtractPtr
++ * [34]               Unused
++ * [33:32]    ChannelNotCompleted
++ * [31:24]    Permissions
++ * [23:16]    RestartCount            Decremented after each restart. Will trap when zero
++ * [15:14]    Unused                  Should be set to zero
++ * [13:0]     Context
++ */
++#define CQ_Context(Control)           ((E4_uint32) ((Control) >>  0) & 0x3fff)
++#define CQ_RestartCount(Control)      ((E4_uint32) ((Control) >> 16) & 0x7f)
++#define CQ_ChannelNotCompleted(Control)       ((E4_uint32) ((Control) >> 32) & 3)
++#define CQ_ExtractPtr(Control)                ((E4_uint32) ((Control) >> 32) & 0xFFFFFFF8)
++
++#define CQ_RestartCountShift          16
++
++#define CQ_SetEventEnableBit  (1 << 24)
++#define CQ_WaitEventEnableBit (1 << 25)
++#define CQ_ModifyEnableBit    (1 << 26)
++#define CQ_WriteEnableBit     (1 << 27)
++#define CQ_ThreadStartEnableBit       (1 << 28)
++#define CQ_DmaStartEnableBit  (1 << 29)
++#define CQ_STENEnableBit      (1 << 30)
++#define CQ_InterruptEnableBit (1 << 31)
++#define CQ_EnableAllBits        (0xFF000000)
++#define CQ_PermissionMask     (0xFF000000)
++
++#define CQ_ControlValue(Cntx, RestartCount, Permissions) \
++      (((Cntx) & 0x3fff) | (((RestartCount) & 0xff) << 16) | ((Permissions) & CQ_PermissionMask))
++
++/*
++ * This file describes the slave address map of Elan4.
++ *
++ * Elan4 has two PCI 64 bit base address registers. One is setup for elan
++ * local memory and the other is for the command port, elan registers and ebus.
++ *
++ * This file describes the command port, elan registers and ebus BAR. This is a
++ * 26 bit base address register and is split up as follows:
++ * 1 The ebus requires 21 bits of address. 26'h3e00000 to 26'h3ffffff
++ * 2 The control regsiters requires 16 bits of address. 26'h3df0000 to 26'h3dfffff
++ * 3 The command port has the rest. This give just under 8k command ports or about 123 per
++ *   processor of a 64 node SMP.
++ */
++
++/* BAR1 contains the command queues followed by the registers and the Ebus - and is 26 bits */
++/* each command queue has an 8K page associated with it */
++#define CQ_CommandMappingSize         (1 << 13)
++#define CQ_NumCommandDescs            ((1 << (26 - 13)))
++#define CQ_CommandDescsAlignment      ((1 << (26 - 13)) * sizeof (E4_CommandQueueDesc))
++
++/* control reg bits i.e. E4_DataBusMap.SysControlReg */
++#define CONT_EN_ALL_SETS              (1ULL << 0) /* enable cache */
++#define CONT_MMU_ENABLE                       (1ULL << 1) /* bit 0 enables mmu */
++#define CONT_CACHE_HASH_TABLE         (1ULL << 2) /* cache up hash table entries */
++#define CONT_CACHE_CHAINS             (1ULL << 3) /* cache up chain entries */
++#define CONT_CACHE_ROOT_CNTX          (1ULL << 4) /* cache root context table for routes and filters. */
++#define CONT_CACHE_STEN_ROUTES                (1ULL << 5) /* cache up sten packet routes */
++#define CONT_CACHE_DMA_ROUTES         (1ULL << 6) /* cache up dma packet routes */
++
++#define CONT_CACHE_NONE               0ULL
++#define CONT_CACHE_ALL                (CONT_CACHE_HASH_TABLE | CONT_CACHE_CHAINS | CONT_CACHE_ROOT_CNTX | \
++                               CONT_CACHE_STEN_ROUTES | CONT_CACHE_DMA_ROUTES)
++
++/* This controls the format size and position of the MMU hash tables. */
++#define CONT_INHIBIT_MAX_CHAIN_ITEMS  (1ULL << 7)     /* Prevents the MaxChainItems value of 1024 from forcing a translation miss */
++#define CONT_TABLE0_MASK_SIZE_SHIFT   8               /* Defines the size of hash table 0 */
++#define CONT_TABLE0_PAGE_SIZE_SHIFT   13              /* Set the page size for hash table 0 */
++#define CONT_TABLE1_MASK_SIZE_SHIFT   16              /* Defines the size of hash table 1 */
++#define CONT_TABLE1_PAGE_SIZE_SHIFT   21              /* Set the page size for hash table 1 */
++#define CONT_TWO_HASH_TABLES          (1ULL << 24)    /* Sets the MMU to use two hash tables. If not set only 0 used. */
++#define CONT_2K_NOT_1K_DMA_PACKETS    (1ULL << 25)    /* Used to select the default DMA packet size. */
++#define CONT_ALIGN_ALL_DMA_PACKETS    (1ULL << 26)    /* Will force all dma packets to be aligned to a page.*/
++#define CONT_DIRECT_MAP_PCI_WRITES    (1ULL << 27)    /* Will force pci writes to write and flush the dcache.*/
++#define CONT_TLB_FLUSH                        (1ULL << 28)    /* Invalidates the TLB and indicates when flushed */
++#define CONT_CLEAR_WALK_WROTE_TABLES  (1ULL << 29)    /* Used to guarantee that the elan is using new PTE values. */
++#define CONT_ROUTE_FLUSH              (1ULL << 30)    /* Invalidates all route cache entries. */
++#define CONT_CLEAR_LINKPORT_INT               (1ULL << 31)    /* Clears the Linkport key fail interrupt. Reads as 0. */
++#define CONT_CLEAR_SDRAM_ERROR                (1ULL << 32)    /* Clears an EEC error interrupt. Reads as 0. */
++
++/*
++ * These are extra control bits used for testing the DLLs of the SDRAM interface. Most of the Sdram
++ * control bits are defined in xsdram.h
++ */
++#define SDRAM_FIXED_DLL_DELAY_SHIFT   47
++#define SDRAM_FIXED_DLL_DELAY_BITS    5
++#define SDRAM_FIXED_DLL_DELAY_MASK    ((1ULL << SDRAM_FIXED_DLL_DELAY_BITS) - 1ULL)
++#define SDRAM_FIXED_DLL_DELAY(Value)  ((SDRAM_FIXED_DLL_DELAY_MASK & (Value)) << SDRAM_FIXED_DLL_DELAY_SHIFT)
++#define SDRAM_FIXED_DELAY_ENABLE      (1ULL << 52)
++#define SDRAM_GET_DLL_DELAY(Value)    (((Value) >> SDRAM_FIXED_DLL_DELAY_SHIFT) & SDRAM_FIXED_DLL_DELAY_MASK)
++
++#define SDRAM_166_DLL_CORRECTION_FACTOR       3       /* This is to allow for SSO and ringing on the DQ lines */
++#define SDRAM_150_DLL_CORRECTION_FACTOR       2       /* This is to allow for SSO and ringing on the DQ lines */
++
++#define PAGE_SIZE_4K  0x0
++#define PAGE_SIZE_8K  0x1
++#define PAGE_SIZE_64K 0x2
++#define PAGE_SIZE_512K        0x3
++#define PAGE_SIZE_2M  0x4
++#define PAGE_SIZE_4M  0x5
++#define PAGE_SIZE_64M 0x6
++#define PAGE_SIZE_512M        0x7
++
++#define PAGE_SIZE_MASK        0x7
++#define PAGE_MASK_MASK        0x1f
++
++/* control reg bits i.e. E4_DataBusMap.LinkControlReg */
++#define LCONT_REVA_GREEN_LED          (1 << 0)
++#define LCONT_REVA_YELLOW_LED         (1 << 1)
++#define LCONT_REVA_RED_LED            (1 << 2)
++#define LCONT_REVA_ENABLE_LED_DRIVE   (1 << 3) /* Enable manual setting of the Leds to the bits set above. */
++
++#define LCONT_REVB_DISABLE_TLB_PREFETCH       (1 << 0)
++#define LCONT_REVB_DISABLE_CRC_ERROR_CHECKING (1 << 1)
++
++
++#define LCONT_EN_SYS_WRITES           (1 << 4) /* Enable linkport writes to sys registers. i.e. all of E4_DataBusMap. */
++#define LCONT_EN_SYS_READS            (1 << 5) /* Enable linkport reads from sys registers. i.e. all of E4_DataBusMap. */
++#define LCONT_EN_USER_WRITES          (1 << 6) /* Enable linkport writes to user registers. i.e. all of E4_User_Regs. */
++#define LCONT_EN_USER_READS           (1 << 7) /* Enable linkport reads from user registers. i.e. all of E4_User_Regs. */
++
++#define LCONT_TEST_VALUE_MASK         0x3ff    /* Value used for test writes and link boundary scan. */
++#define LCONT_TEST_VALUE_SHIFT                8
++#define LCONT_TEST_VALUE(Value)               ((LCONT_LINK_STATE_MASK & (Value)) << LCONT_TEST_VALUE_SHIFT)
++
++/*
++ * State read from LINK_STATE when TEST_VALUE is set to the following values.
++ * TEST_VALUE   LINK_STATE read       TEST_VALUE        LINK_STATE read
++ *    000     -   Data delay count 0     008       -  Data delay count 8
++ *    001     -   Data delay count 1     009       -  Link in value
++ *    002     -   Data delay count 2     00a       -  PLL delay
++ *    003     -   Data delay count 3     00b       -  Clock Delay
++ *    004     -   Data delay count 4     00c       ?  ErrorVal8to0
++ *    005     -   Data delay count 5     00d       ?  ErrorVal17to9
++ *    006     -   Data delay count 6     00e       ?  ErrorVal26to18
++ *    007     -   Data delay count 7     00f       ?  ErrorVal35to27
++ */
++
++#define LCONT_TEST_CONTROL_MASK               0x3     /* Selects and controls the action of the LINK_STATE value. */
++#define LCONT_TEST_CONTROL_SHIFT      18
++
++#define LCONT_READ_ERRORS             0       /* {Mod45RequestChanged, FifoOverflowError, DataError, PhaseError,
++                                               *      DeskewError, LockError, Locked, LinkNotReady} */
++#define LCONT_READ_STATE              1       /* Read valus addressed by TEST_CONTROL value */
++#define LCONT_FIX_LINK_DELAYS         2       /* Sets delays to TEST_CONTROL value */
++#define LCONT_BOUNDARY_SCAN           3       /* Puts link into boundary scan. Outputs TEST_CONTROL value to link,
++                                               * reads LINK_STATE from link. */ 
++
++#define LCONT_LINK_STATE_MASK         0x3ff   /* Read only */
++#define LCONT_LINK_STATE_SHIFT                20      /* Read only */
++#define LCONT_LINK_STATE(ControlRegValue)     (LCONT_LINK_STATE_MASK & ((ControlRegValue) >> LCONT_LINK_STATE_SHIFT))
++
++/* control reg bits i.e. E4_DataBusMap.LinkContSettings */
++#define LCONT_MOD45_DISABLE           (1 << 0) /* is set the link will try to run in TNB mode. */
++#define LCONT_CONFIG_PHASE_MASK               0x7     /* This set the delay through the phase alignment buffer. */
++#define LCONT_CONFIG_PHASE_SHIFT      1
++
++#define LCONT_PLL_REF_VAL_BITS_MASK   0x7f    /* This is the divide value on the LinkIn clock to form the comms PLL */
++#define LCONT_PLL_REF_VAL_BITS_SHIFT  4       /* reference clock. Div value is (n - 2). e.g. to Divide by 7 set to 5. */
++
++#define LCONT_FORCE_COMMSCLK_LOCAL    (1 << 11) /* This must be set at one end of a back to back Elan configuration. */
++#define LCONT_LVDS_VOLTAGE_BITS_MASK  0x3     /* This is used to set the voltage swing on the LVDS link output pads. */
++#define LCONT_LVDS_VOLTAGE_BITS_SHIFT 12      /* reference clock. Div value is (n - 2). e.g. to Divide by 7 set to 5. */
++
++#define LCONT_VOD_170                 0       /* Approximate differential voltage swing in mV of link outputs into */
++#define LCONT_VOD_360                 1       /* a 100 ohm diferential load. */
++#define LCONT_VOD_460                 2
++#define LCONT_VOD_550                 3
++
++#define LCONT_LVDS_TERMINATION_MASK   0x3     /* This set the resistor values of the internal single ended termation */
++#define LCONT_LVDS_TERMINATION_SHIFT  14      /* resistors of the link input and comms input clcok. */
++
++#define LCONT_TERM_55_OHM             0       /* Resistor values for internal termination of LVDS pads. */
++#define LCONT_TERM_50_OHM             1
++#define LCONT_TERM_AUTO_OHM           2       /* Should normally be set to auto. */
++#define LCONT_TERM_45_OHM             3
++
++#define LCONT_LVDS_EN_TERM_UPDATE     (1 << 47) /* This should be asserted and deasserted if LCONT_LVDS_TERMINATION is changed. */
++
++/* Macros used to access and construct MMU hash table and chain entries. */
++/*
++ * Each hash entry is made up of a 64 byte block. Each entry hash two tags where each
++ * tag has 4 PTE's. PTE's 0 to 2 use the bottom 48 bits of a 64 bit word and PTE 3
++ * uses the top 16 bits of 3 64 bit words.
++ *
++ * These macros can be used to build a single PTE. PTE3 needs to be built into a 48 bit
++ * object before they can be used.
++ */
++#define PTE_ENTRY_MASK                0x0000ffffffffffffULL
++#define PTE_TYPE_MASK         0x000000000000000fULL   
++#define PTE_PERM_MASK         0x00000000000000f0ULL
++#define PTE_PERM_TYPE_MASK    0x00000000000000ffULL
++#define PTE_REF_MASK          0x0000000000000100ULL
++#define PTE_PPN_MASK          0x00007ffffffffe00ULL
++#define PTE_MOD_MASK          0x0000800000000000ULL
++#define PTE_TOPADDR_MASK      0x0000600000000000ULL
++
++#define PTE_MOD_SHIFT         47
++#define PTE_PPN_SHIFT         9
++#define PTE_REF_SHIFT         8
++#define PTE_PERM_SHIFT                4
++#define PTE_TYPE_SHIFT                0
++
++#define PTE_PADDR_SHIFT               (12 - 9)                /* Physical addresses are shifted down 3 this to go into the PTE */
++
++
++/* Values required for tag 3 */
++#define PTE_REF_3                     0x0100000000000000ULL
++#define PTE_MOD_3                     0x8000000000000000ULL
++#define PTE_ENTRY_MASK_3              0xffff000000000000ULL
++#define PTE_PERM_TYPE_MASK_3          0x00ff000000000000ULL
++#define PTE_ENTRY_3_FOR_0(NewPte)     ((NewPte << (48)) & PTE_ENTRY_MASK_3)
++#define PTE_ENTRY_3_FOR_1(NewPte)     ((NewPte << (32)) & PTE_ENTRY_MASK_3)
++#define PTE_ENTRY_3_FOR_2(NewPte)     ((NewPte << (16)) & PTE_ENTRY_MASK_3)
++
++/* Values required for the tags */
++#define TAG_CONTEXT_MASK              0x0000000000003fffULL
++#define TAG_ADDRESS_MASK              0xfffffffff8000000ULL
++#define TAG_CHAINPTR_18TO6_MASK               0x0000000007ffc000ULL
++#define TAG_CHAINPTR_LOW_SHIFT                (14 - 6)
++#define TAG_CHAINPTR_30TO19_MASK      0x0000000003ffc000ULL
++#define TAG_CHAINPTR_HIGH_SHIFT               (19 - 14)
++#define TAG_COPY_BIT                  0x0000000004000000ULL
++
++/*
++ * This takes number loaded into the control register and returns the page size as a power of two.
++ */
++
++#define E4_PAGE_SIZE_TABLE            E4_uint32 const PageSizeTable[] = {12, 13, 16, 19, 21, 22, 26, 29}
++#define E4_PAGE_SIZE_TABLE_SIZE               (sizeof(PageSizeTable)/sizeof(PageSizeTable[0]))
++
++/*
++ * This macro generates a hash block index.
++ *
++ * Cntx                This is the 14 bit context. It should not be larger than 14 bits.
++ * VAddr       This is the 64 bit virtual address. It does not require any masking and can be a byte address.
++ * PageSize    This is the value loaded into the control register for this hash table.
++ * HashTableMask This should be set mask out upper bits past the end of the hash table.
++ */
++#define E4MMU_SHIFT_ADDR(VAddr, Shift) \
++    ((((E4_uint32)(VAddr)) >> (Shift)) | (((E4_uint32)((VAddr) >> 32)) << (32 - (Shift))))
++
++#define E4MMU_CONTEXT_SCRAMBLE(Cntx) \
++            ((((Cntx) << 8) | ((Cntx) >> 6)) ^ (((Cntx) << 15) | ((Cntx) << 1)))
++
++#define E4MMU_HASH_INDEX(Cntx, VAddr, PageShift, HashTableMask)               \
++          ((E4MMU_SHIFT_ADDR(VAddr, (PageShift) + 2) ^ E4MMU_CONTEXT_SCRAMBLE(Cntx)) & (HashTableMask))
++
++#define E4MMU_TAG(vaddr,ctx)  (((vaddr) & TAG_ADDRESS_MASK) | ((ctx) & TAG_CONTEXT_MASK))
++
++#define E4MMU_TAG2VADDR(tag,hashidx,PageShift,HashTableMask)  \
++              (((tag) & TAG_ADDRESS_MASK) | ((((hashidx) ^ E4MMU_CONTEXT_SCRAMBLE((tag) & TAG_CONTEXT_MASK)) & (HashTableMask)) << ((PageShift + 2))))
++
++/*
++ * Detailed bit descriptions for the tags and PTE's are better done with the macros
++ * defined above.
++ */
++typedef struct _E4_HashTableEntry
++{
++   E4_uint64  Tag[2];
++   E4_uint64  TagPTE[2][3];
++} E4_HashTableEntry;
++
++#define E4MMU_TAG_OFFSET(tag)         ((tag) << 3)
++#define E4MMU_PTE_LOW_OFFSET(tag,pte) ((((tag)*3 + (pte) + 2) << 3))
++#define E4MMU_PTE_HIGH_OFFSET(tag,pte)        ((((tag)*3 + (pte) + 2) << 3) + 4)
++#define E4MMU_PTE3_WORD0_OFFSET(tag)  ((((tag)*3 + 2) << 3) + 6)
++#define E4MMU_PTE3_WORD1_OFFSET(tag)  ((((tag)*3 + 3) << 3) + 6)
++#define E4MMU_PTE3_WORD2_OFFSET(tag)  ((((tag)*3 + 4) << 3) + 6)
++
++
++/*
++ * Hash0AddrBits is the size of the hash table in bytes as a power of 2.
++ * e.g. 11 would give 32 hash entries where each entry is 64 bytes.
++ */
++#define SETUP_HASH_TABLES(Hash0PageSize, Hash0AddrBits, Hash1PageSize, Hash1AddrBits) \
++                        (((Hash0PageSize) << CONT_TABLE0_PAGE_SIZE_SHIFT) |   \
++                         ((Hash0AddrBits) << CONT_TABLE0_MASK_SIZE_SHIFT) |   \
++                         ((Hash1PageSize) << CONT_TABLE1_PAGE_SIZE_SHIFT) |   \
++                         ((Hash1AddrBits) << CONT_TABLE1_MASK_SIZE_SHIFT))
++
++/* ECC status register */
++#define ECC_Addr(s)                   ((s) & 0x7ffffff8ULL)
++#define ECC_Syndrome(s)                       (((s) >> 32) & 0xffffULL)
++#define ECC_RisingDQSSyndrome(s)      (((s) >> 32) & 0xffULL)
++#define ECC_FallingDQSSyndrome(s)     (((s) >> 40) & 0xffULL)
++#define ECC_UncorrectableErr(s)       (((s) >> 48) & 1ULL)
++#define ECC_MultUncorrectErrs(s)      (((s) >> 49) & 1ULL)
++#define ECC_CorrectableErr(s)         (((s) >> 50) & 1ULL)
++#define ECC_MultCorrectErrs(s)                (((s) >> 51) & 1ULL)
++
++/* Permission type saved in a PTE. This is a four bit field */
++#define PERM_Disabled         0x0
++#define PERM_Unused           0x1
++#define PERM_LocDataRead      0x2
++#define PERM_LocDataWrite     0x3
++#define PERM_LocRead          0x4
++#define PERM_LocExecute               0x5
++#define PERM_ReadOnly         0x6
++#define PERM_LocWrite         0x7
++#define PERM_LocEventOnly     0x8
++#define PERM_LocEventWrite    0x9
++#define PERM_RemoteEvent      0xa
++#define PERM_RemoteAll                0xb
++#define PERM_RemoteReadOnly   0xc
++#define PERM_RemoteWriteLocRead       0xd
++#define PERM_DataReadWrite    0xe
++#define PERM_NoFault          0xf
++
++#define PERM_Mask             0xf
++
++/* Permission type hints to device driver */
++#define PERM_Preload          0x10
++
++#define PTE_SetPerm(Perm)     (((Perm) & PERM_Mask) << 4)
++
++/* Control info saved in the lookup field of the TLB */
++#define PTE_PciNotLocal               (1ULL << 0)             /* Directs the access to the PCI interface */
++#define PTE_BigEndian         (1ULL << 1)             /* Valid for PCI entries only */
++#define PTE_RelaxedOrder      (1ULL << 2)             /* Valid for PCI entries only */
++#define PTE_DontSnoop         (1ULL << 3)             /* Valid for PCI entries only */
++
++#define PTE_UseFixedSet               (1ULL << 1)             /* Value for non PCI entries only */
++#define PTE_CommandQueue      (1ULL << 2)             /* Value for non PCI entries only */
++#define PTE_SetFixedSetNo(Set)        ((((Set) & 3) << 2) | PTE_UseFixedSet)
++
++#define PTE_TypeBitsMask      (0xfULL)
++#define PTE_PermissionTypeMask        (0xfULL << 4)
++#define PTE_Referenced                (1ULL << 8)
++#define PTE_PhysicalPageNoMask        (0x7ffffffffe00ULL)
++#define PTE_Modified          (1ULL << 47)
++
++#define PTE_PhysicalAddrShiftIntoPTE  (12 - 9)
++
++/* define page table entry bit fields */
++#define TLB_PageSizeBits      (3 << 0)
++#define TLB_ACCBits           (7 << 2)
++#define TLB_LocalBit          (1 << 5)
++#define TLB_PCI64BitTargetBit (1 << 6)
++#define TLB_PCIBigEndianBit   (1 << 7)
++
++#define TLB_ModifiedBit               (1 << 55)
++#define TLB_ReferencedBit     (1 << 63)
++
++/* Used to read values from the tlb. */
++#define TLB_TlbReadCntBitsSh  56
++#define TLB_UseSelAddrSh      (1ULL << 60)
++#define TLB_WriteTlbLine      (1ULL << 61)
++
++#define TLB_SEL_LINE(LineNo) (TLB_UseSelAddrSh | \
++                            ((E4_uint64)((LineNo) & 0xf) << TLB_TlbReadCntBitsSh))
++
++#define TLB_NUM_ENTRIES               16
++/*
++ * The following macros are used with the test access port (TlbLineValue) for the TLBs.
++ */
++#define TLV_DoPciAccess                       (1ULL << 0)
++#define TLV_CommandAccess             (1ULL << 1)
++#define TLV_DoCacheAccess             (1ULL << 2)
++#define TLV_notStartTLBWalk           (1ULL << 3)
++#define TLV_UseFixedSet                       (1ULL << 4)
++#define TLV_BigEndian                 (1ULL << 4)
++#define TLV_RelaxedOrder              (1ULL << 5)
++#define TLV_DontSnoop                 (1ULL << 6)
++#define TLV_FixedSetNo_MASK           (3ULL << 5)
++#define TLV_PciTypeBits_MASK          (7ULL << 4)
++#define TLV_LookupBits_MASK           (0x7fULL)
++#define TLV_MissErr                   (1ULL << 7)
++#define TLV_TypeBits                  (0xffULL)
++
++#define TLV_PhysicalAddr_MASK         (0x3fffffffff000ULL)
++
++#define TLV_TlbTesting                        (1ULL << 51)
++#define TLV_SelectUnitsTlbRead                (1ULL << 52)
++#define TLV_SelectTProcTlbRead                (1ULL << 53)
++
++#define TLV_TlbLineSelect_MASK                (0xf)
++#define TLV_UnitsTlbLineSelect_SHIFT  (54)
++#define TLV_TProcTlbLineSelect_SHIFT  (59)
++#define TLV_EnableUnitsTlbRead                (1ULL << 58)
++#define TLV_EnableTProcTlbRead                (1ULL << 63)
++
++/*
++ * Use this macro to enable direct testing of the Units TLB.
++ * When Line is in the range 0 to 15 a TLB line is selected for reading or writing.
++ * When Line is set to -1 the tlb will be activated to perform a match.
++ */
++#define TLV_UnitsTlbLineSel(Line) (((Line) == -1) ? 0ULL : \
++    (TLV_EnableUnitsTlbRead | ((E4_uint64)((Line) & TLV_TlbLineSelect_MASK) << TLV_UnitsTlbLineSelect_SHIFT)))
++#define TLV_TProcTlbLineSel(Line) (((Line) == -1) ? 0ULL : \
++    (TLV_EnableTProcTlbRead | ((E4_uint64)((Line) & TLV_TlbLineSelect_MASK) << TLV_TProcTlbLineSelect_SHIFT)))
++ 
++/* 
++ * Thread_Trap_State
++ *  see f_RegFileControl.v TProcStatus
++ */
++#define TS_HaltThread               (1 << 0)
++#define TS_TrapForTooManyInstructions (1 << 1)
++#define TS_InstAccessException              (1 << 2)
++#define TS_Unimplemented            (1 << 3)
++#define TS_DataAccessException              (1 << 4)
++#define TS_DataAlignmentError       (1 << 5)
++#define TS_TrapForUsingBadData              (1 << 6)
++#define TS_TrapTypeMask                     (0x7f)
++#define TS_DataPortNo(ts)           (((ts) >> 7) & 7)
++#define TS_TrappedFlag                      (1 << 10)
++#define TS_MemLock                  (1 << 11)
++#define TS_XCCshift                 12
++#define TS_XCCmask                  0xff
++#define TS_ICC(ts)                  (((ts) >> 12) & 15)
++#define TS_XCC(ts)                  (((ts) >> 16) & 15)
++#define TS_InstValid_F                      (1 << 20)
++#define TS_InstValid_R                      (1 << 21)
++#define TS_InstValid_E                      (1 << 22)
++#define TS_InstValid_W                      (1 << 23)
++#define TS_HighPriority                     (1 << 24)
++#define TS_RemoteThread                     (1 << 25)
++#define TS_TProcTranslationInProgress (1 << 26)
++#define TS_MemLock_E                (1 << 27)
++
++/* Thread run queue entries */
++typedef struct E4_ThreadRegs
++{
++    E4_uint64 Registers[7];
++} E4_ThreadRegs;
++
++typedef struct E4_TProcQueueEntry
++{
++    E4_ThreadRegs     Regs;                   /* XXXX: jon check this */
++    E4_uint64         Context;                /* XXXX: jon check this */
++} E4_TProcQueueEntry;
++
++typedef struct E4_DProcQueueEntry
++{
++    E4_DMA            Desc;
++    E4_uint64         Pad;
++} E4_DProcQueueEntry;
++
++/*
++ * Packet acknowledge values.
++ */
++#define E4_PAckOk     0
++#define E4_PAckTestFail       1
++#define E4_PAckDiscard        2
++#define E4_PAckError  3
++
++/*
++ * return values from breaktest instruction.
++ */
++#define ICC_CARRY_BIT           (0x1ULL << 0)  /* Breaktest: Load pending         */
++#define ICC_ZERO_BIT            (0x1ULL << 1)  /* Breaktest: Time to break        */
++#define ICC_SIGNED_BIT          (0x1ULL << 2)  /* Breaktest: Another thread ready */
++#define ICC_TPROC_RDY_LOW_PRI   (0x1ULL << 3)
++#define ICC_TPROC_RDY_HIGH_PRI  (0x1ULL << 4)
++#define ICC_RUNNING_HIGH_PRI    (0x1ULL << 5)
++#define ICC_RUNNING_AS_REMOTE   (0x1ULL << 6)
++#define ICC_TIME_TO_BREAK       (0x1ULL << 7)
++#define ICC_RS1LOAD_PENDING     (0x1ULL << 8)
++#define ICC_TPROC_HALT          (0x1ULL << 9)
++
++/*
++ * Main Interrupt cookies
++ * [63:14]    user cookie
++ * [13:0]     context
++ */
++#define E4_MAIN_INT_SHIFT             14
++#define E4_MAIN_INT_COOKIE(cookie)    ((cookie) >> E4_MAIN_INT_SHIFT)
++#define E4_MAIN_INT_CTX(cookie)               ((cookie) & 0x3FFF)
++
++typedef E4_uint64 E4_MainIntEntry;
++
++#define E4_MainIntEntrySize   sizeof (E4_MainIntEntry)
++
++/*
++ * The internal databus is 64 bits wide.
++ * All writes to the internal registers MUST be made with 64 bit write operations.
++ * These can be made up of pairs 32 bit writes on the PCI bus. The writes will be
++ * treated as nops if they are performed with two separate 32 bit writes.
++ */
++typedef volatile struct _E4_DataBusMap
++{
++   E4_uint64          InputTrans[4][16];                                                                      /* 0x000 */
++
++   E4_uint64          Dma0TransAddr;                                                                          /* 0x200 */
++   E4_DMA             Dma0Desc;       /* Current Dma0 registers */                                            /* 0x208 */
++
++   E4_uint64          Dma1TransAddr;                                                                          /* 0x240 */
++   E4_DMA             Dma1Desc;       /* Current Dma1 registers */                                            /* 0x248 */
++  
++   E4_uint64          Dma0LastPacketSize;                                                                     /* 0x280 */
++   E4_uint64          Dma0ThisPacketSize;                                                                     /* 0x288 */
++   E4_uint64          Dma0DescSizeInProg;                                                                     /* 0x290 */
++   E4_uint64          Dma0BytesToPrefetch;                                                                    /* 0x298 */
++   E4_uint64          Dma0PrefetchAddr;                                                                       /* 0x2a0 */
++   E4_uint64          EventCountAndType;                                                                      /* 0x2a8 */
++   E4_uint64          EventParameters[2];                                                                     /* 0x2b0 */
++  
++   E4_uint64          Dma1LastPacketSize;                                                                     /* 0x2c0 */
++   E4_uint64          Dma1ThisPacketSize;                                                                     /* 0x2c8 */
++   E4_uint64          Dma1DescSizeInProg;                                                                     /* 0x2d0 */
++   E4_uint64          Dma1BytesToPrefetch;                                                                    /* 0x2d8 */
++   E4_uint64          Dma1PrefetchAddr;                                                                       /* 0x2e0 */
++   E4_Input_Ptrs      InputTrapAndFilter;                                                                     /* 0x2e8 */
++   E4_uint64          EventAddress;                                                                           /* 0x2f0 */
++   E4_QueuePtr                MainIntQueuePtrs;                                                                       /* 0x2f8 */
++   
++   E4_uint64          Event_Copy[16];                                                                         /* 0x300 */
++
++   E4_uint64          CommandCopy[7];                                                                         /* 0x380 */
++   E4_uint64          CommandHold;                                                                            /* 0x3b8 */
++
++   E4_uint64          InputQueueDesc[4];                                                                      /* 0x3c0 */
++
++   /* Run queue Pointers */
++   E4_uint64          DProcLowPriPtrs;                                                                        /* 0x3e0 */
++   E4_uint64          DProcHighPriPtrs;                                                                       /* 0x3e8 */
++   E4_uint64          TProcLowPriPtrs;                                                                        /* 0x3f0 */
++   E4_uint64          TProcHighPriPtrs;                                                                       /* 0x3f8 */
++
++   E4_uint64          CProcStatus;                                                                            /* 0x400 */
++   E4_uint64          TProcStatus;                                                                            /* 0x408 */
++   E4_uint64          IProcStatus;                                                                            /* 0x410 */
++   E4_uint64          EProcStatus;                                                                            /* 0x418 */
++   E4_uint64          DProc0Status;                                                                           /* 0x420 */
++   E4_uint64          DProc1Status;                                                                           /* 0x428 */
++   E4_Sched_Status    SchedStatus;                                                                            /* 0x430 */
++
++   E4_uint64          LoadIProcCntxFilter;    /* Will load one of 4 cntx filter regs. Write only */           /* 0x438 */
++
++   E4_CommandControl  CommandControl;                                                                         /* 0x440 */
++   E4_uint64          CommandCacheTestPort;                                                                   /* 0x448 */
++   E4_uint64          CommandLowPriRunPtrs;                                                                   /* 0x450 */
++   E4_uint64          CommandHighPriRunPtrs;                                                                  /* 0x458 */
++   E4_uint64          CommandSchedDataPort[4];                                                                /* 0x460 */
++
++   E4_uint64          DmaRouteBuffer[2][2];   /* Write only. Should not be written to. */                     /* 0x480 */
++   E4_uint64          StenRouteBuffer[2];     /* Write only. Should not be written to. */                     /* 0x4a0 */
++   E4_uint64          pad4[0x098 - 0x096];                                                                    /* 0x4b0 */
++
++   E4_uint64          DmaAlignmentPort[8];    /* Write only. Should only be written to clear the prev reg. */ /* 0x4c0 */
++
++   E4_uint64          MmuBlockEntry[8];       /* Used for hash table and chain fetches */                     /* 0x500 */
++   E4_uint64          WriteUnitsTlbLine[3];                                                                   /* 0x550 */
++   E4_uint64          pad5;                                                                                   /* 0x540 */
++   E4_uint64          WriteTProcTlbLine[3];                                                                   /* 0x568 */
++   E4_uint64          pad6;                                                                                   /* 0x540 */
++
++   E4_uint64          MmuTableBasePtrs;       /* Both tables packed into a single 64 bit value */             /* 0x580 */
++   E4_uint64          MmuFaultAndRootCntxPtr; /* Both packed into a single 64 bit value */                    /* 0x588 */
++   E4_uint64          UnitsVAddr;                                                                             /* 0x590 */
++   E4_uint64          TProcVAddr;                                                                             /* 0x598 */
++   E4_uint64          UnitsCntx;                                                                              /* 0x5a0 */
++   E4_uint64          TProcCntx;              /* Read only. Writes access VProcCacheWritePort */              /* 0x5a8 */
++   E4_uint64          FaultAddrReg;                                                                           /* 0x5b0 */
++   E4_uint64          FaultTypeAndContextReg;                                                                 /* 0x5b8 */
++
++   E4_uint32          SysControlReg;                                                                          /* 0x5c0 */
++   E4_uint32          CacheTagValue;                                                                          /* 0x5c4 */
++   E4_uint64          TlbLineValue;                                                                           /* 0x5c8 */
++   E4_uint64          SDRamConfigReg;                                                                         /* 0x5d0 */
++   E4_uint32          InterruptMask;                                                                          /* 0x5d8 */
++   E4_uint32          InterruptReg;                                                                           /* 0x5dc */
++   E4_uint64          SDRamECCStatus;                                                                         /* 0x5e0 */
++   E4_uint32          LinkControlReg;                                                                         /* 0x5e8 */
++   E4_uint32          LinkContSettings;                                                                       /* 0x5ec */
++   E4_uint64          LinkPortKey;                                                                            /* 0x5f0 */
++   E4_uint64          LinkPortLock;                                                                           /* 0x5f8 */
++
++   E4_uint64          SDRamWriteBuffer[4][8];                                                                 /* 0x600 */
++   E4_uint64          SDRamReadBuffer[4][8];                                                                  /* 0x700 */
++
++   E4_uint64          TProcRegs[64];                                                                          /* 0x800 */
++   E4_uint64          TProcStartUp[8];        /* Not to be used except by the elan itself */                  /* 0xa00 */
++
++   E4_uint64          LoadPending;                                                                            /* 0xa40 */
++   E4_uint64          StortPending;                                                                           /* 0xa48 */
++   E4_uint64          DirtyBits;                                                                              /* 0xa50 */
++   E4_uint64          BadBits;                                                                                /* 0xa58 */
++
++   E4_uint64          ICachePort_Cntl_Addr;                                                                   /* 0xa60 */
++   E4_uint64          Thread_Trap_State;                                                                      /* 0xa68 */
++
++/* Instruction buffer (4 * 32 bit words) */
++   E4_uint64          nPC_W;                                                                                  /* 0xa70 */
++   E4_uint64          PC_W;                                                                                   /* 0xa78 */
++
++   E4_uint64          ICacheFillData[8];                                                                      /* 0xa80 */
++   E4_uint64          ICachePort[8];                                                                          /* 0xac0 */
++
++   E4_uint64          PciDataBufs[4][8];                                                                      /* 0xb00 */
++
++   E4_uint64          CommandQueueBuffer[128];                                                                /* 0xc00 */
++} E4_DataBusMap;
++
++/*
++ * These macros are used to setup the thread pcoessors ICache.
++ */
++#define E4_ICacheTagAddrShift         6
++#define E4_AccessICacheRams           1
++#define E4_InvalidTagValue            0xffffffffffffffffULL
++#define E4_ICacheSizeInBytes          (1024*16)
++#define E4_ICacheLineSizeInBytes      (64)
++#define E4_ICacheLines                        (E4_ICacheSizeInBytes/E4_ICacheLineSizeInBytes)
++#define E4_ICachePortSize             ( (sizeof((E4_DataBusMap *) 0)->ICachePort) /   \
++                                        (sizeof((E4_DataBusMap *) 0)->ICachePort[0]))
++
++#define E4_ICacheFixupInsn            0xc0b02f95ull           /* st1 [%r0 +  0xf95] */
++#define E4_ICacheFixupAddr            0xf95ull
++#define E4_ICacheFixupOffset          0xfc0
++
++/*
++ * Event interrupt
++ */
++typedef volatile union _E4_EventInt
++{
++   E4_uint64   ForceAlign;
++   struct {
++       E4_uint32 IntCookie;
++       E4_uint32 EventContext;        /* Bits 16 to 28 */
++    } s;
++} E4_EventInt;
++
++/*
++ * The following are used to interpret a fault status register.
++ */
++
++/*
++ * FSR[14:0] - AccessType
++ *
++ * T = Type bit
++ * S = size bit. Size is in units of 64 bits or 8 bytes.
++ * E = Byte end pointer. Used to define the last written byte of the last 64 bits written.
++ * D = Data type bit. Used for endian conversion in the PCI interface.
++ * C = Used by the cache to decide if this access should allocate a cache line.
++ * d = Set if dma read or write data data. This is used to guarantee order at the PCI interface.
++ * A = Access type used to check permissions by the MMU in a virtual access.
++ * P = Part Write. If set some byte enables may be used. Effects the action of a cache miss.
++ */
++
++/* FSR[7:0] */
++/* bit 7 => virtual write */
++#define AT_VirtualWriteAccBit         (1 << 7)                /* AAADDdC1EEESSSS = Virtual Write */
++#define AT_VirtualWriteSizeMask               0xf                     /* size of write access (0 => 128 bytes) */
++#define AT_VirtualWriteEndPtrShift    4                       /* end byte pointer for part write block */
++#define AT_VirtualWriteEndPtrMask     0x7
++
++/* else bit 6 => virtual read */
++#define AT_VirtualReadAccBit          (1 << 6)                /* AAADDdC01SSSSSS = Virtual Read */
++#define AT_VirtualReadSizeMask                0x3f                    /* size of read access (0 => 512 bytes) */
++
++/* else => special access */
++#define AT_SelBitsMask                        0xf                     /* Bits to select the type of acces from */
++#define AT_SelBitsShift                       0x4
++#define AT_SpecialRd                  (0x0 << 4)              /* AAADDdC0000TTTT = Special read Access */
++#define AT_SpecialWr                  (0x1 << 4)              /* AAADDdC0001TTTT = Special write Access */
++#define AT_PhysicalRd                 (0x2 << 4)              /* AAADDdC00100SSS = Physical Read */
++#define AT_PhysicalWr                 (0x3 << 4)              /* AAADDdC0011PSSS = Physical write */
++
++#define AT_OtherSizeMask              0xf                     /* Size bits used by all other accesses. 0=128 bytes */
++#define AT_SpecialBitsMask            0xf                     /* Bits used to define the special access types */
++#define AT_CacheSizeBitsMask          0x7                     /* Size bits used for local accesses. 0=64 */
++#define AT_CachePhysPartWriteBit      0x8                     /* This bit is set if the access is a part write to the cache */
++
++/* Special memory access operations */
++#define AT_RegAccess                  0x0
++#define AT_GetCntxFilter              0xe                     /* Only used by special reads */
++#define AT_RouteFetch                 0xf                     /* Only used by special reads */
++
++/* FSR[9:8] */
++#define AT_NonAlloc                   (1 << 8)                /* 1=Do not fill cache with this data */
++#define AT_DmaData                    (1 << 9)                /* This is a DMA read access. Required to guarantee dma read order. */
++
++/* FSR[11:10] - Data Type - defines data type for endian conversion in PCI interface*/
++#define AT_BlkDataTyMask              0x3
++#define AT_BlkDataTyShift             10
++
++#define AT_BlkDataType(FSR)           (((FSR) >> AT_BlkDataTyShift) & AT_BlkDataTyMask)
++#define AT_TypeByte                   0x0
++#define AT_TypeHWord                  0x1
++#define AT_TypeWord                   0x2
++#define AT_TypeDWord                  0x3
++
++/* FSR[14:12] - Access Permissions */
++#define AT_PermBitsMask                       0x7
++#define AT_PermBitsShift              12
++
++#define AT_Perm(FSR)                  (((FSR) >> AT_PermBitsShift) & AT_PermBitsMask)
++#define AT_PermLocalDataRead          0x0
++#define AT_PermLocalDataWrite         0x1
++#define AT_PermRemoteRead             0x2
++#define AT_PermRemoteWrite            0x3
++#define AT_PermExecute                        0x4
++#define AT_PermLocalEvent             0x5
++#define AT_PermRemoteEvent            0x7
++
++/* FSR[22:15] - reason for fault */
++
++#define FSR_WalkForThread             (1 << 15) /* The thread processor caused the fault */
++#define FSR_Walking                   (1 << 16) /* The fault was caused during a hash table access */
++#define FSR_NoTranslationsFound               (1 << 17) /* The hash table did not contain a matching tag */
++#define FSR_WalkingProtectionFault    (1 << 18) /* A protection fault was detected while walking */
++#define FSR_HashTable1                        (1 << 19) /* Was accessing hash table 1 not 0 */
++#define FSR_RouteVProcErr             (1 << 20) /* This is an invalid vproc for a route fetch */
++#define FSR_FaultForBadData           (1 << 21) /* Bad data (double bit ECC error) while performing a walk access */
++#define FSR_FaultForMaxChainCount     (1 << 22) /* The Elan4 has walked a chain of 1024 items. */
++
++typedef volatile struct _E4_FaultSave
++{
++    E4_uint64 FSRAndFaultContext;                 /* Bits 0-31 : FaultContext. Bits 32-63 : FaultStatus Register */
++    E4_uint64 FaultAddress;
++} E4_FaultSave;
++
++#define FaultSaveContext(FSRAndFaultContext)  ((E4_uint32) ((FSRAndFaultContext) & 0xFFFFFFFF))
++#define FaultSaveFSR(FSRAndFaultContext)      ((E4_uint32) ((FSRAndFaultContext) >> 32))
++
++typedef union E4_TrTypeCntx
++{
++   E4_uint32 TypeContext;
++   struct
++   {
++#if (BYTE_ORDER == LITTLE_ENDIAN) || defined(__LITTLE_ENDIAN__)
++      E4_uint32 Type:16;              /* Transaction type field */
++      E4_uint32 Context:13;           /* Transaction context */
++      E4_uint32 TypeCntxInvalid:1;    /* Bit  29 */
++      E4_uint32 StatusRegValid:1;     /* Bit  30 */
++      E4_uint32 LastTrappedTrans:1;   /* Bit  31 */
++#else
++      E4_uint32 LastTrappedTrans:1;   /* Bit  31 */
++      E4_uint32 StatusRegValid:1;     /* Bit  30 */
++      E4_uint32 TypeCntxInvalid:1;    /* Bit  29 */
++      E4_uint32 Context:13;           /* Transaction context */
++      E4_uint32 Type:16;              /* Transaction type field */
++#endif
++   } s;
++} E4_TrTypeCntx;
++
++#define MAX_TRAPPED_TRANS     28
++#define TRANS_DATA_DWORDS     16
++#define TRANS_DATA_BYTES      128
++#define NO_OF_INPUT_CHANNELS  4
++
++#define CH0_LOW_PRI_CHAN      0
++#define CH1_LOW_PRI_CHAN      1
++#define CH0_HIGH_PRI_CHAN     2
++#define CH1_HIGH_PRI_CHAN     3
++
++/* Words have been swapped for big endian access when fetched with dword access from elan.*/
++typedef struct _E4_IprocTrapHeader
++{
++   E4_uint64  TrAddr;
++   E4_uint64  IProcStatusCntxAndTrType;
++} E4_IprocTrapHeader;
++
++typedef struct _E4_IprocTrapData
++{
++   E4_uint64 Data[TRANS_DATA_DWORDS];
++} E4_IprocTrapData;
++
++/*
++ * This struct defines the trap state for the inputers. It requires a contiguous 16K byte block of local memory.
++ * The channel bits have been grouped to the low end of the address to force all Identify cookies to use the
++ * same cache line.
++ */
++typedef struct _E4_IprocTrapState
++{
++   E4_IprocTrapData   TrData[MAX_TRAPPED_TRANS][NO_OF_INPUT_CHANNELS];
++   E4_IprocTrapHeader TrHeader[MAX_TRAPPED_TRANS][NO_OF_INPUT_CHANNELS];
++   E4_uint64        pad[8*NO_OF_INPUT_CHANNELS];
++} E4_IprocTrapState;
++
++/*
++ * 64 kbytes of elan local memory. Must be aligned on a 64k boundary
++ */
++#define E4_LowPriQueueSize    0x400
++#define E4_HighPriQueueSize   0x100
++
++typedef struct _E4_FaultSaveArea
++{
++   E4_FaultSave               TProcData[8];
++   E4_FaultSave               TProcInst;
++   E4_FaultSave               Dummy[7];
++   E4_FaultSave               SchedProc;
++   E4_FaultSave               DProc;
++   E4_FaultSave               EventProc;
++   E4_FaultSave               IProc;
++   E4_FaultSave               DProcData[4];
++   E4_FaultSave               QReadData[8];
++} E4_FaultSaveArea;
++
++/* Macros to manipulate event queue pointers */
++/*     generate index in EventIntQueue */
++#define E4_EVENT_INTQ_INDEX(fptr)     (((fptr) & 0x1fff) >> 3)
++/*     generate next fptr */
++#define E4_EVENT_INTQ_NEXT(fptr)      ((((fptr) + 8) & ~0x4000) | 0x2000)
++
++typedef struct _E4_CommandPort
++{
++   volatile E4_uint64 Command[1024];  /* a whole 8k page */
++} E4_CommandPort;
++
++/*
++ * This is the allocation of unit numbers within the ELAN. It is used to extract the fault address
++ * and fault type after a unit has trapped on a memory fetch. Only units that can generate traps
++ * have been included.
++ */
++#define CUN_TProcData0                0x00
++#define CUN_TProcData1                0x01
++#define CUN_TProcData2                0x02
++#define CUN_TProcData3                0x03
++#define CUN_TProcData4                0x04
++#define CUN_TProcData5                0x05
++#define CUN_TProcData6                0x06
++#define CUN_TProcData7                0x07
++#define CUN_TProcInst         0x08
++
++/* memory current unit numbers
++ * TProc data bus */
++#define CUN_DProcPA0          0x10
++#define CUN_DProcPA1          0x11
++#define CUN_DProcPrefetch     0x12
++#define CUN_CommandProc               0x13
++#define CUN_DProcData0                0x14    /* Dma prefetch reads. */
++#define CUN_DProcData1                0x15    /* Dma prefetch reads. */
++#define CUN_DProcData2                0x16    /* Dma prefetch reads. */
++#define CUN_DProcData3                0x17    /* Dma prefetch reads. */
++
++#define CUN_IProcLowPri               0x18
++#define CUN_IProcHighPri      0x19
++#define CUN_Spare0            0x1A
++#define CUN_Spare1            0x1B
++#define CUN_Spare2            0x1C
++#define CUN_ThreadQueue               0x1D
++#define CUN_EventProc0                0x1e
++#define CUN_EventProc1                0x1f
++
++#define CUN_Entries           0x20
++
++typedef struct E4_Registers
++{
++   E4_CacheTags               Tags;                           /* 4k bytes  c000 -> cfff */
++   E4_DataBusMap      Regs;                           /* 4k bytes  d000 -> dfff */
++   E4_User_Regs               uRegs;                          /* 8k bytes  e000 -> ffff */
++} E4_Registers;
++
++#define I2cCntl_I2cPortWrite          (0 << 0)
++#define I2cCntl_I2cPortRead           (1 << 0)
++#define I2cCntl_I2cPortGenStopBit     (1 << 1)
++#define I2cCntl_I2cPortGenRestartBit  (1 << 2)
++#define I2cCntl_I2cPortAccFailed      (1 << 3)
++#define I2cCntl_I2cStopped            (1 << 4)
++#define I2cCntl_I2cWakeupFailed               (1 << 5)
++#define I2cCntl_I2cFastMode           (1 << 6)
++#define I2cCntl_I2cPortBusy           (1 << 7)
++
++#define I2cCntl_LedI2cRegBase_Mask    0x7f
++#define I2cCntl_I2cUpdatingLedReg     (1 << 7)
++
++#define I2cCntl_InvertLedValues               (1 << 0)                /* read/write */
++#define I2cCntl_LedRegWriteFailed     (1 << 1)                /* read only */
++#define I2cCntl_EEPromLoadFailed      (1 << 2)                /* read only */
++#define I2cCntl_InhibitI2CRom         (1 << 3)                /* read only */
++#define I2cCntl_BadRomCrc             (1 << 4)                /* read only */
++#define I2cCntl_MapInI2cConfigData    (1 << 5)                /* read/write */
++#define I2cCntl_SampleNewLedValues    (1 << 6)                /* read/write */
++#define I2cCntl_ClearLinkError                (1 << 7)                /* write only */
++
++typedef struct E4_I2C
++{
++   volatile E4_uint8    I2cWrData;
++   volatile E4_uint8    I2cRdData;
++   volatile E4_uint8    I2cPortControl;
++   volatile E4_uint8  I2cLedBase;
++   volatile E4_uint8    I2cStatus;
++   volatile E4_uint8    I2cLedsValue;
++   volatile E4_uint16 I2cPad;
++ 
++   E4_uint8           pad[256 - sizeof(E4_uint64)];
++
++   E4_uint8           UnchangedElan4ConfigRegs[256];
++   E4_uint8           I2cRomConfigShadowValues[256];
++   E4_uint8           ChangedElan4ConfigRegs[256];
++} E4_I2C;
++
++typedef struct _E4_ContextControlBlock 
++{
++    E4_uint32 Filter;                 /* Use a Network context to index for this value */
++    E4_uint32 VirtualProcessTable;    /* Use a local context to index for this value */
++} E4_ContextControlBlock;
++
++/*
++ * Filter
++ *   [13:0]   Context
++ *   [14]     DiscardAll
++ *   [15]     AckAll
++ *   [16]     HighPri
++ *   [17]     CountStats
++ *   [31:18]  Unused
++ */
++#define E4_FILTER_STATS               (1 << 17)
++#define E4_FILTER_HIGH_PRI    (1 << 16)
++#define E4_FILTER_ACKOK_ALL   (1 << 15)
++#define E4_FILTER_DISCARD_ALL (1 << 14)
++#define E4_FILTER_CONTEXT_MASK        (0x3FFF)
++
++/*
++ * VirtualProcessTable
++ *   [8:0]    Unused  
++ *   [12:9]   Size       num vp entries = 512 << Size
++ *   [30:13]  Pointer
++ *   [31]     Valid
++ */
++#define E4_VPT_MIN_ENTRIES      512
++#define E4_VPT_VALID          ((unsigned)1 << 31)
++#define E4_VPT_PTR_SHIFT      0
++#define E4_VPT_SIZE_SHIFT     9
++#define E4_VPT_SIZE_MASK        0xf
++#define E4_VPT_NUM_VP(vpt_val)  (E4_VPT_MIN_ENTRIES << (((vpt_val) >> E4_VPT_SIZE_SHIFT) & E4_VPT_SIZE_MASK))
++#define E4_VPT_VALUE(ptr,size)        (((ptr) << E4_VPT_PTR_SHIFT) | ((size) << E4_VPT_SIZE_SHIFT))
++
++
++/* Virtual Process Table */
++typedef struct _E4_VirtualProcessEntry
++{
++    E4_uint64 Values[2];
++} E4_VirtualProcessEntry;
++
++/*
++ * Entries have the following format - rtX is a packed route 
++ *
++ * |rt11|rt10|rt9 |rt8 |rt7 |rt6 |rt5 |rt4 |rt3 |rt2 |rt2 |rt0 |PAAADD       RRRRRR|
++ * |output context     |rt23|rt22|rt21|rt20|rt19|rt18|rt17|rt16|rt15|rt14|rt13|rt12|
++ */
++
++#define ROUTE_CTXT_SHIFT      48
++#define ROUTE_CTXT_MASK               (~((1ull << ROUTE_CTXT_SHIFT)-1))
++#define ROUTE_CTXT_VALUE(ctx) (((E4_uint64) ctx) << ROUTE_CTXT_SHIFT)
++
++#define ROUTE_PACKED_OFFSET   16
++#define ROUTE_NUM_PACKED      24
++
++/* defines for first flit of a route */
++#define FIRST_TIMEOUT(Val)    ((Val) << 14)                   /* [15:14]  */
++#define FIRST_SYSTEM_PACKET     (1 << 13)                       /* [13]     */
++#define FIRST_FLOOD_PACKET      (1 << 12)                       /* [12]     */
++#define FIRST_HIGH_PRI                (1 << 11)                       /* [11]    */
++#define FIRST_AGE(Val)                ((Val) << 7)                    /* [10:7] */
++#define FIRST_OPTIONS_MASK    (0xFF80)
++
++/* [6:0] unpacked 1st route value */
++#define FIRST_INVALID         (0)
++#define FIRST_ROUTE(Val)      (0x08 | (Val))
++#define FIRST_ADAPTIVE                (0x30)
++#define FIRST_BCAST_TREE      (0x20)
++#define FIRST_MYLINK          (0x10)
++#define FIRST_BCAST(Top, Bot) (0x40 | ((Top) << 3) | (Bot))
++
++/* defines for 3 bit packed entries for subsequent flits */
++#define PACKED_INVALID                (0)
++#define PACKED_ROUTE(Val)     (8 | (Val))
++#define PACKED_ADAPTIVE               (3)
++#define PACKED_BCAST_TREE     (2)
++#define PACKED_MYLINK         (1)
++#define PACKED_BCAST0(Top,Bot)        (4 | (Bot & 3))
++#define PACKED_BCAST1(Top,Bot)        ((Top << 1) | (Bot >> 2))
++
++#endif /* _ASM */
++/* The MMU root context pointer has a mask to bounds check 
++ * it - this is computed as follows.
++ */
++#define E4_CONTEXT_MASK(num)  (((num) >= 0x2000) ? 0x00 :     \
++                               ((num) >= 0x1000) ? 0x80 :     \
++                               ((num) >= 0x0800) ? 0xc0 :     \
++                               ((num) >= 0x0400) ? 0xe0 :     \
++                               ((num) >= 0x0200) ? 0xf0 :     \
++                               ((num) >= 0x0100) ? 0xf8 :     \
++                               ((num) >= 0x0080) ? 0xfc :     \
++                               ((num) >= 0x0040) ? 0xfe : 0xff)
++/*
++ * This generates the size field for a virtual process table.
++ * Size defined as 2^n no of 8K pages.
++ * Single cycle route fetches are possible if the minimum vproc table size is 8k.
++ */
++#define E4_GEN_VPT_SIZE(Size)         (((Size) & E4_VPT_SIZE_MASK) << E4_VPT_SIZE_SHIFT)
++
++#define COMMAND_RUN_QUEUE_BITS                (13 + 2) /* 8K entries of 4 bytes. This is fixed in hardware. */
++#define COMMAND_DESCS_SPACE_BITS      (13 + 5) /* 8K entries of 32 bytes. This is fixed in hardware. */
++#define COMMAND_INSERTER_CACHE_ENTRIES        16
++
++#define COM_TEST_PORT_ADDR_MASK               0xfULL
++#define COM_TEST_PORT_ADDR_SH         0
++
++/*
++ * The flush register is accessed through the CommandControl register.
++ * The address is naturally alligned. It also positions the command descriptors in memory.
++ * When no command queues need flushing it should be or with COM_FLUSH_INVALID. This sets
++ * it to the top command queue descriptor. This cannot be accessed from the PCI.
++ */
++#define COM_ENABLE_DEQUEUE            (1 << 4)
++#define COM_FLUSH_DESCRIPTOR_MASK     0x7fffffe0ULL
++#define COM_FLUSH_INVALID             0x0003ffe0ULL
++
++
++/*
++ * Elan4 BAR1 is split up as follows :
++ *
++ * RevA
++ *     0x3f00000 EBUS other
++ *     0x3e00000 EBUS ROM
++ *     0x3dfc000 registers
++ *     0x0000000 command ports
++ *
++ * RevB
++ *     0x3ffc000 registers
++ *     0x3ff8000 padding
++ *     0x3ff6000 i2c registers
++ *     0x0000000 command ports
++ */
++#define ELAN4_BAR1_SIZE                       (1 << 26)       /* 64M */
++#define ELAN4_REG_SIZE                        (1 << 14)       /* 16K */
++
++#define ELAN4_REVA_EBUS_SIZE          (1 << 21)       /* 2M */
++#define ELAN4_REVA_EBUS_OFFSET                (ELAN4_BAR1_SIZE - ELAN4_REVA_EBUS_SIZE)
++#define ELAN4_REVA_REG_OFFSET         (ELAN4_REVA_EBUS_OFFSET - ELAN4_REG_SIZE)
++#define ELAN4_REVA_NUM_COMMAND_QUEUES (ELAN4_REVA_REG_OFFSET >> 13)
++
++#define ELAN4_REVA_EBUS_ROM_SIZE      (1 << 20)       /* 1M */
++#define ELAN4_REVA_EBUS_ROM_OFFSET    0
++
++#define ELAN4_REVB_I2C_PADDING                (1 << 14)       /* 16K */
++#define ELAN4_REVB_I2C_SIZE           (1 << 13)       /* 8k */
++#define ELAN4_REVB_REG_OFFSET         (ELAN4_BAR1_SIZE - ELAN4_REG_SIZE)
++#define ELAN4_REVB_I2C_OFFSET         (ELAN4_REVB_REG_OFFSET - ELAN4_REVB_I2C_PADDING - ELAN4_REVB_I2C_SIZE)
++#define ELAN4_REVB_NUM_COMMAND_QUEUES (ELAN4_REVB_I2C_OFFSET >> 13)
++
++#endif /* notdef _ELAN4_REGISTERS_H */
+Index: linux-2.6.5/include/elan4/sdram.h
+===================================================================
+--- linux-2.6.5.orig/include/elan4/sdram.h     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan4/sdram.h  2005-05-11 12:10:12.608906736 -0400
+@@ -0,0 +1,41 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN4_SDRAM_H
++#define __ELAN4_SDRAM_H
++
++#ident "$Id: sdram.h,v 1.8 2003/09/24 13:55:55 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan4hdr/sdram.h,v $*/
++
++/* Include header file generated by sdram configuration program */
++#include <elan4/xsdram.h> 
++
++/* SDRAM bank shift definitions */
++#define SDRAM_0_CS_SHIFT      25
++#define SDRAM_1_CS_SHIFT      27
++#define SDRAM_2_CS_SHIFT      28
++#define SDRAM_3_CS_SHIFT      29
++
++#define SDRAM_BANK_SHIFT(cfg) \
++      (((cfg >> SDRAM_RamSize_SH) & 3) == 0 ? SDRAM_0_CS_SHIFT : \
++       ((cfg >> SDRAM_RamSize_SH) & 3) == 1 ? SDRAM_1_CS_SHIFT : \
++       ((cfg >> SDRAM_RamSize_SH) & 3) == 2 ? SDRAM_2_CS_SHIFT : SDRAM_3_CS_SHIFT)
++
++#define SDRAM_BANK_SIZE(cfg)          (1ULL << SDRAM_BANK_SHIFT(cfg))
++#define SDRAM_BANK_OFFSET(cfg,bank)   ((unsigned long long)(bank) << SDRAM_BANK_SHIFT(cfg))
++#define SDRAM_NUM_BANKS(cfg)          (4)
++#define SDRAM_MAX_BANKS                       4
++
++/* When the elan access sdram it passes eaddr[12] as sdramaddr[12] when
++ * running with a 4k page size, however PCI accesses pass paddr[12], so
++ * we must ensure that sdram pages are allocated such that eaddr[12] is the
++ * same as paddr[12] - the easiest way is to allocate sdram in 8k chunks and
++ * ensure that maddr[12] == eaddr[12] == pgoff[0] */
++#define SDRAM_MIN_PAGE_SIZE           (8192)
++
++#endif /* __ELAN4_SDRAM_H */
+Index: linux-2.6.5/include/elan4/stats.h
+===================================================================
+--- linux-2.6.5.orig/include/elan4/stats.h     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan4/stats.h  2005-05-11 12:10:12.609906584 -0400
+@@ -0,0 +1,83 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: stats.h,v 1.10.12.1 2004/10/06 11:09:12 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/stats.h,v $*/
++
++#ifndef __ELAN4_STATS_H
++#define __ELAN4_STATS_H
++
++#define ELAN4_DEV_STATS_BUCKETS               8
++
++
++typedef struct elan4_dev_stats
++{
++    unsigned long     s_interrupts;
++    
++    unsigned long       s_mainints[ELAN4_DEV_STATS_BUCKETS];
++    unsigned long     s_mainint_punts;
++    unsigned long     s_mainint_rescheds;
++
++    unsigned long       s_haltints;
++
++    unsigned long     s_cproc_traps;
++    unsigned long     s_dproc_traps;
++    unsigned long     s_eproc_traps;
++    unsigned long     s_iproc_traps;
++    unsigned long     s_tproc_traps;
++
++    unsigned long       s_cproc_trap_types[0x10];
++    unsigned long       s_dproc_trap_types[6];
++    unsigned long       s_eproc_trap_types[4];
++    unsigned long       s_iproc_trap_types[0xa];
++    unsigned long       s_tproc_trap_types[7];
++
++    unsigned long       s_correctable_errors;
++    unsigned long       s_multiple_errors;
++    
++    unsigned long       s_link_errors;
++    unsigned long       s_lock_errors;
++    unsigned long       s_deskew_errors;
++    unsigned long       s_phase_errors;
++    unsigned long     s_data_errors;
++    unsigned long     s_fifo_overflow0;
++    unsigned long     s_fifo_overflow1;
++    unsigned long       s_mod45changed;
++    unsigned long       s_pack_not_seen;
++    unsigned long       s_linkport_keyfail;
++
++    unsigned long     s_eop_reset;
++    unsigned long       s_bad_length;
++    unsigned long       s_crc_bad;
++    unsigned long       s_crc_error;
++
++    unsigned long     s_cproc_timeout;
++    unsigned long     s_dproc_timeout;
++
++    unsigned long     s_sdram_bytes_free;
++} ELAN4_DEV_STATS;
++
++#define MainIntBuckets                ((int[ELAN4_DEV_STATS_BUCKETS-1]) {1, 2, 3, 4, 8, 16, 32})
++
++#define BumpDevStat(dev,stat) ((dev)->dev_stats.stat++)
++#define BucketDevStat(dev,stat,n,bucket)      ((n) <= (bucket)[0] ? (dev)->dev_stats.stat[0]++ : \
++                                               (n) <= (bucket)[1] ? (dev)->dev_stats.stat[1]++ : \
++                                               (n) <= (bucket)[2] ? (dev)->dev_stats.stat[2]++ : \
++                                               (n) <= (bucket)[3] ? (dev)->dev_stats.stat[3]++ : \
++                                               (n) <= (bucket)[4] ? (dev)->dev_stats.stat[4]++ : \
++                                               (n) <= (bucket)[5] ? (dev)->dev_stats.stat[5]++ : \
++                                               (n) <= (bucket)[6] ? (dev)->dev_stats.stat[6]++ : \
++                                                                    (dev)->dev_stats.stat[7]++)
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++#endif /*__ELAN4_STATS_H */
+Index: linux-2.6.5/include/elan4/tprintf.h
+===================================================================
+--- linux-2.6.5.orig/include/elan4/tprintf.h   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan4/tprintf.h        2005-05-11 12:10:12.609906584 -0400
+@@ -0,0 +1,24 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN4_TPRINTF_H
++#define __ELAN4_TPRINTF_H
++
++#ident "$Id: tprintf.h,v 1.6 2003/09/04 12:39:17 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan4hdr/tprintf.h,v $*/
++
++
++#ifdef _ASM
++#define TPRINTF0(string)          add %r0, __LINE__, %r0
++#define TPRINTF1(string,reg)      add reg, __LINE__, %r0
++#else
++#define TPRINTF0(string)          asm volatile ("add %%r0, %0, %%r0" : : "i" (__LINE__))
++#define TPRINTF1(string, value)           asm volatile ("add %0,   %1, %%r0" : : "r" (value), "i" (__LINE__))
++#endif /* _ASM */
++
++#endif /* __ELAN4_TPRINTF_H */
+Index: linux-2.6.5/include/elan4/trap.h
+===================================================================
+--- linux-2.6.5.orig/include/elan4/trap.h      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan4/trap.h   2005-05-11 12:10:12.614905824 -0400
+@@ -0,0 +1,95 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: trap.h,v 1.10 2003/10/07 12:11:10 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/trap.h,v $*/
++
++#ifndef __ELAN4_TRAP_H
++#define __ELAN4_TRAP_H
++
++/*
++ * If the EProc Faults whilst performing an action (e.g. Read/Write on the data src or dest Addr)
++ *  the Eproc increments the Addr(s) by a block size (64 bytes):
++ *  1: Fault on Read: 
++ *                     Src EventAddr = Read Addr + block
++ *  2: Fault on Write:
++ *                     Src EventAddr = Read Addr + block
++ *                     Dst EventAddr = Read Addr + block
++ *                     Size          = Size - block ndwords
++ *  We must rewind the addr correctly to completely the transfer successfully
++ */
++#define EVENT_COPY_NDWORDS    0x8
++#define EVENT_COPY_BLOCK_SIZE 0x40
++
++typedef struct elan4_eproc_trap
++{
++    E4_uint64         tr_status;
++    E4_FaultSave      tr_faultarea;
++    E4_Event          tr_event;
++    E4_Addr           tr_eventaddr;
++} ELAN4_EPROC_TRAP;
++
++typedef struct elan4_cproc_trap
++{
++    E4_uint64         tr_status;                                      /* cproc status register */
++    E4_uint64         tr_command;                                     /* cproc command */
++    E4_CommandQueueDesc tr_qdesc;                                     /* copy of command queue descriptor */
++    E4_FaultSave      tr_faultarea;                                   /* fault area for mmu traps */
++    ELAN4_EPROC_TRAP  tr_eventtrap;                                   /* associated event trap (waitevent) */
++} ELAN4_CPROC_TRAP;
++
++typedef struct elan4_dproc_trap
++{
++    E4_DMA            tr_desc;
++    E4_FaultSave      tr_packAssemFault;
++    E4_FaultSave      tr_prefetchFault;
++    E4_uint64         tr_status;
++} ELAN4_DPROC_TRAP;
++
++typedef struct elan4_tproc_trap
++{
++    E4_uint64         tr_regs[64];
++    E4_FaultSave      tr_dataFault;
++    E4_FaultSave      tr_instFault;
++    E4_uint64         tr_status;
++    E4_uint64         tr_state;
++    E4_Addr           tr_pc;
++    E4_Addr           tr_npc;
++    E4_uint64         tr_dirty;
++    E4_uint64         tr_bad;
++} ELAN4_TPROC_TRAP;
++
++typedef struct elan4_iproc_trap
++{
++    E4_uint32           tr_numTransactions;
++    E4_uint32           tr_flags;
++    E4_uint32           tr_trappedTrans;
++    E4_uint32           tr_waitForEopTrans;
++    E4_uint32           tr_identifyTrans;
++    E4_uint32           tr_pad;
++
++    E4_FaultSave          tr_faultarea;
++    E4_IprocTrapHeader    tr_transactions[MAX_TRAPPED_TRANS];
++    E4_IprocTrapData      tr_dataBuffers[MAX_TRAPPED_TRANS];
++} ELAN4_IPROC_TRAP;
++
++#define TR_FLAG_ACK_SENT      (1 << 0)
++#define TR_FLAG_EOP_ERROR     (1 << 1)
++#define TR_FLAG_BAD_TRANS     (1 << 2)
++#define TR_FLAG_DMA_PACKET    (1 << 3)
++#define TR_FLAG_EOP_BAD               (1 << 4)
++#define TR_FLAG_TOOMANY_TRANS (1 << 5)
++
++#define TR_TRANS_INVALID      (0xffffffff)
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++#endif /* __ELAN4_TRAP_H */
+Index: linux-2.6.5/include/elan4/trtype.h
+===================================================================
+--- linux-2.6.5.orig/include/elan4/trtype.h    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan4/trtype.h 2005-05-11 12:10:12.615905672 -0400
+@@ -0,0 +1,112 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef _ELAN4_TRTYPE_H
++#define _ELAN4_TRTYPE_H
++
++#ident "$Id: trtype.h,v 1.20 2004/02/06 10:38:21 mike Exp $"
++/*      $Source: /cvs/master/quadrics/elan4hdr/trtype.h,v $*/
++
++/*<15:11> Size field is used to give the number of additional 64 bit data values.
++        A value from 0 to 16 inclusive is valid. */
++
++#include <elan4/types.h>
++
++#define TR_SIZE_SHIFT         (11)
++#define TR_SIZE_MASK          (0x1f << TR_SIZE_SHIFT)
++#define SET_TR_SIZE(Size)     (((Size) << TR_SIZE_SHIFT) & TR_SIZE_MASK)
++
++/* <10:9> Last Transaction and AckNow bits, marks the last transaction and
++          enables a PACK_OK to be sent. */
++#define TR_LAST_AND_SEND_ACK  (3 << 9)
++
++
++/* <8>  Only valid on the last transaction. Delays execution until an EOP_GOOD is received.
++ *      Any other EOP type will abort execution of this transaction. */
++#define TR_WAIT_FOR_EOP               (1 << 8)
++
++/*
++ * Data type. This is used by transactions of variable data type. It controls any endian
++ * converion required if the destiantion host processor has a big endian memory format.
++ */
++/*    WriteBlock      <8:7>   Data type
++                      <6:0>   Part write size */
++#define TR_DATATYPE_SHIFT     (6)
++#define TR_DATATYPE_MASK      ((1 << 2) - 1)
++
++#define TR_DATATYPE_BYTE      E4_DATATYPE_BYTE        
++#define TR_DATATYPE_SHORT     E4_DATATYPE_SHORT
++#define TR_DATATYPE_WORD      E4_DATATYPE_WORD        
++#define TR_DATATYPE_DWORD     E4_DATATYPE_DWORD
++
++/* <5:0> Transaction Type
++ *       For Writeblock <5:3> 000 => Write, 0001 => Read
++ *                      <2:0> End Byte Addr */
++#define TR_OPCODE_MASK                0x3F
++#define TR_BLOCK_OPCODE_MASK  0x38
++
++#define TR_WRITEBLOCK         0x0
++#define TR_ENDBYTE_MASK               0x7
++#define TR_WRITE(Size, EndByte, DataType)                                             \
++                      (0x0 | SET_TR_SIZE(Size) | ((EndByte) & TR_ENDBYTE_MASK) |      \
++                       (((DataType) & TR_DATATYPE_MASK) << TR_DATATYPE_SHIFT))
++
++#define TR_NOP_TRANS          (0x10 | SET_TR_SIZE(0))
++#define TR_SETEVENT           0x10
++#define TR_SETEVENT_NOIDENT   (TR_SETEVENT | SET_TR_SIZE(0) | TR_LAST_AND_SEND_ACK)
++#define TR_SETEVENT_IDENTIFY  (TR_SETEVENT | SET_TR_SIZE(1) | TR_LAST_AND_SEND_ACK)
++#define TR_REMOTEDMA          (0x11 | SET_TR_SIZE(7) | TR_LAST_AND_SEND_ACK)
++#define TR_SENDDISCARD                (0x12 | SET_TR_SIZE(0))
++
++/*
++ * Conditional transactions that might return PAckTestFail.
++ * All will allow further exection of the packet if ([Address] operator DataValue) is true.
++ * e.g. for TR_GTE further execution if ([Address] >= DataValue) is true.
++ * These should be used where a definite TRUE/FALSE answer is required.
++ */
++#define TR_GTE                        (0x14 | SET_TR_SIZE(1))
++#define TR_LT                 (0x15 | SET_TR_SIZE(1))
++#define TR_EQ                 (0x16 | SET_TR_SIZE(1))
++#define TR_NEQ                        (0x17 | SET_TR_SIZE(1))
++
++/*
++ * Conditional transactions that might return PAckDiscard.
++ * All will allow further exection of the packet if ([Address] operator DataValue) is true.
++ * e.g. for TR_GTE further execution if ([Address] >= DataValue) is true.
++ * These should be used where eventually a TRUE answer is expected but the node might not be ready yet.
++ * These can be mixed with the normal conditionals to allow a single packet to test for readyness and
++ * a TRUE/FALSE answer.
++ */
++#define TR_GTE_DISCARD                (0x34 | SET_TR_SIZE(1))
++#define TR_LT_DISCARD         (0x35 | SET_TR_SIZE(1))
++#define TR_EQ_DISCARD         (0x36 | SET_TR_SIZE(1))
++#define TR_NEQ_DISCARD                (0x37 | SET_TR_SIZE(1))
++
++#define TR_TRACEROUTE_TRANS   0x18
++#define TR_TRACEROUTE(Size)   (TR_TRACEROUTE_TRANS | (TR_DATATYPE_WORD << TR_DATATYPE_SHIFT) |SET_TR_SIZE(Size))
++#define TR_IDENTIFY           (0x19 | SET_TR_SIZE(0))
++
++#define TR_ADDWORD            (0x1c | SET_TR_SIZE(2) | TR_LAST_AND_SEND_ACK)
++#define TR_INPUT_Q_COMMIT     (0x1d | SET_TR_SIZE(1) | TR_LAST_AND_SEND_ACK)
++#define TR_TESTANDWRITE       (0x1e | SET_TR_SIZE(3) | TR_LAST_AND_SEND_ACK)
++#define TR_INPUT_Q_GETINDEX   (0x1f | SET_TR_SIZE(0))
++
++
++
++/* TraceRoute formate */
++#define TR_TRACEROUTE0_CHANID(val)            ((val) & 1)                     /* 0     Chan Id */
++#define TR_TRACEROUTE0_LINKID(val)            (((val) >> 1) & 7)              /* 1:3   Link Id */
++#define TR_TRACEROUTE0_REVID(val)             (((val) >> 4) & 7)              /* 4:6   Revision Id */
++#define TR_TRACEROUTE0_BCAST_PIN(val)         (((val) >> 7) & 1)              /* 7     Bcast Top Pin */
++#define TR_TRACEROUTE0_LNR(val)                       (((val) >> 8) & 0xFF)           /* 8:15  Global Link Not Ready */
++
++#define TR_TRACEROUTE1_ROUTES_SELECTED(val)   ((val & 0xFF))                  /* 0:7   Routes Selected */
++#define TR_TRACEROUTE1_BCAST_TOP(val)         (((val) >> 8) & 7)              /* 8:10  Broadcast Top */
++#define TR_TRACEROUTE1_BCAST_BOTTOM(val)      (((val) >> 12) & 7)             /* 12:14 Broadcast Bottom */
++
++#endif /* _ELAN4_TRANSACTIONTYPE_H */
+Index: linux-2.6.5/include/elan4/types.h
+===================================================================
+--- linux-2.6.5.orig/include/elan4/types.h     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan4/types.h  2005-05-11 12:10:12.615905672 -0400
+@@ -0,0 +1,69 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN4_TYPES_H
++#define __ELAN4_TYPES_H
++
++#ident "@(#)$Id: types.h,v 1.9 2003/09/04 12:39:17 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan4hdr/types.h,v $*/
++
++#include <qsnet/config.h>
++/*
++ * "flip" values for correctly indexing into
++ * block data which was copied from the Elan
++ * using 64 bit accesses.
++ */
++#if defined(__LITTLE_ENDIAN__)
++#  define ByteEndianFlip  0
++#  define ShortEndianFlip 0
++#  define WordEndianFlip  0
++#else
++#  define ByteEndianFlip  7
++#  define ShortEndianFlip 3
++#  define WordEndianFlip  1
++#endif
++
++
++#ifndef _ASM
++
++typedef signed int       E4_int;
++typedef unsigned int             E4_uint;
++
++typedef signed char      E4_int8;
++typedef unsigned char            E4_uint8;
++
++typedef signed short     E4_int16;
++typedef unsigned short           E4_uint16;
++
++typedef signed int       E4_int32;
++typedef unsigned int             E4_uint32;
++
++#ifdef _LP64
++typedef signed long        E4_int64;
++typedef unsigned long      E4_uint64;
++#else
++typedef signed long long   E4_int64;
++typedef unsigned long long E4_uint64;
++#endif
++
++/* 64-bit Elan4 */
++typedef E4_uint64        E4_Addr;
++typedef E4_uint32        E4_LocPhysAddr;      /* Really 31 bits */
++
++#define OneK  (1024)
++#define EightK        (8*OneK)
++
++#define E4_DATATYPE_BYTE      0
++#define E4_DATATYPE_SHORT     1
++#define E4_DATATYPE_WORD      2
++#define E4_DATATYPE_DWORD     3
++
++#endif /* _ASM */
++
++#endif /* __ELAN4_TYPES_H */
++
+Index: linux-2.6.5/include/elan4/user.h
+===================================================================
+--- linux-2.6.5.orig/include/elan4/user.h      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan4/user.h   2005-05-11 12:10:12.616905520 -0400
+@@ -0,0 +1,344 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: user.h,v 1.37.2.2 2004/11/18 17:54:17 duncant Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/user.h,v $*/
++
++#ifndef __ELAN4_USER_H
++#define __ELAN4_USER_H
++
++#include <elan/capability.h>
++#include <elan4/usertrap.h>
++#include <elan4/intcookie.h>
++
++typedef struct trap_queue
++{
++    unsigned  q_back;                 /* Next free space */
++    unsigned  q_front;                /* First object to remove */
++    unsigned  q_size;                 /* Size of queue */
++    unsigned  q_count;                /* Current number of entries */
++    unsigned  q_slop;                 /* FULL <=> (count+slop) == size */
++} RING_QUEUE;
++
++#define RING_QUEUE_INIT(q,num,slop)   ((q).q_size = (num), (q).q_slop = (slop), (q).q_front = (q).q_back = 0, (q).q_count = 0)
++#define RING_QUEUE_FULL(q)            ((q).q_count >= ((q).q_size - (q).q_slop))
++#define RING_QUEUE_REALLY_FULL(q)     ((q).q_count == (q).q_size)
++#define RING_QUEUE_EMPTY(q)           ((q).q_count == 0)
++#define RING_QUEUE_NEXT(q,indx)               ((indx) = (((indx)+1) % (q).q_size))
++#define RING_QUEUE_PREV(q,indx)               ((indx) = (((indx)+(q).q_size-1) % (q).q_size))
++#define RING_QUEUE_ADD(q)             (RING_QUEUE_NEXT(q ,(q).q_back),  (++(q).q_count) >= ((q).q_size - (q).q_slop))
++#define RING_QUEUE_REMOVE(q)          (RING_QUEUE_NEXT(q, (q).q_front), (--(q).q_count) == 0)
++#define RING_QUEUE_ADD_FRONT(q)               (RING_QUEUE_PREV(q, (q).q_front), (++(q).q_count) >= ((q).q_size - (q).q_slop))
++#define RING_QUEUE_ENTRY(qArea,indx)  (&(qArea)[(indx)])
++#define RING_QUEUE_FRONT(q,qArea)     RING_QUEUE_ENTRY(qArea, (q).q_front)
++#define RING_QUEUE_BACK(q,qArea)      RING_QUEUE_ENTRY(qArea, (q).q_back)
++#define RING_QUEUE_ITERATE(q,idx)     for (idx = (q).q_front; idx != (q).q_back; idx = (((idx) + 1) % (q).q_size))
++
++typedef struct user_rgn
++{
++    struct user_rgn *rgn_mnext;                                       /* Doubly linked list of regions */
++    struct user_rgn *rgn_mprev;                                       /*   sorted on main address */ 
++    virtaddr_t       rgn_mbase;                                       /* main address of base of region */
++
++    struct user_rgn *rgn_enext;                                       /* Doubly linked list of regions */
++    struct user_rgn *rgn_eprev;                                       /*   sorted on elan address */
++    E4_Addr        rgn_ebase;                                 /* elan address of base of region */
++
++    unsigned long    rgn_len;                                 /* length of region */
++    unsigned       rgn_perm;                                  /* elan access permission */
++} USER_RGN;
++
++typedef struct user_vpseg
++{ 
++    struct list_head  vps_link;
++
++    unsigned short    vps_process;                            /* virtual process number */
++    unsigned short    vps_entries;                            /*   and # virtual processes */
++
++    unsigned          vps_type;
++    union
++    {
++      struct {
++          ELAN_CAPABILITY        *cap;
++          E4_VirtualProcessEntry *routes;
++      } p2p;
++#define vps_p2p_cap   vps_u.p2p.cap
++#define vps_p2p_routes  vps_u.p2p.routes
++
++      struct {
++          unsigned short lowvp;
++          unsigned short highvp;
++      } bcast;
++#define vps_bcast_lowvp               vps_u.bcast.lowvp
++#define vps_bcast_highvp      vps_u.bcast.highvp
++    } vps_u;
++} USER_VPSEG;
++
++/* values for vps_type */
++#define USER_VPSEG_P2P                0
++#define USER_VPSEG_BCAST      1
++
++typedef struct user_cq
++{
++    struct list_head ucq_link;
++
++    ELAN4_CQ      *ucq_cq;                                    /* the real command queue */
++
++    unsigned char    ucq_state;                                       /* command queue state */
++    unsigned char    ucq_errored;                             /* command queue has errored */
++    unsigned char    ucq_flags;                                       /* flags */
++    ELAN4_CPROC_TRAP ucq_trap;                                        /* trap state */
++
++    atomic_t       ucq_ref;                                   /* # references to this cq (mmaps) */
++} USER_CQ;
++
++/* values for ucq_state */
++#define UCQ_RUNNING                    0                      /* command queue is running */
++#define UCQ_TRAPPED                    1                      /* command queue has trapped */
++#define UCQ_NEEDS_RESTART                2                    /* command queue has trapped, and needs restarting */
++#define UCQ_STOPPED                    3                      /* command queue has trapped, and delivered to user */
++
++/* values for ucq_flags */
++#define UCQ_SYSTEM            (1 << 0)
++#define UCQ_REORDER           (1 << 1)
++
++extern int num_fault_save;
++extern int min_fault_pages;
++extern int max_fault_pages;
++
++typedef struct fault_save
++{
++    struct fault_save           *next;
++    E4_Addr                      addr;
++    E4_uint32                    count;
++} FAULT_SAVE;
++
++typedef struct user_iproc_trap
++{
++    unsigned char     ut_state;
++    ELAN4_IPROC_TRAP  ut_trap;
++} USER_IPROC_TRAP;
++
++/* values for ut_state */
++#define UTS_IPROC_RUNNING                     0
++#define UTS_IPROC_TRAPPED                     1
++#define UTS_IPROC_RESOLVING                   2
++#define UTS_IPROC_EXECUTE_PACKET              3
++#define UTS_IPROC_EXECUTING                   4
++#define UTS_IPROC_NETWORK_ERROR                       5
++#define UTS_IPROC_STOPPED                     6
++
++typedef struct user_ctxt_entry
++{
++    struct list_head   cent_link;                                     /* entry chained on context */
++    ELAN_CAPABILITY   *cent_cap;                                      /* capability we attached with */
++} USER_CTXT_ENTRY;
++
++typedef struct user_ctxt
++{
++    ELAN4_CTXT         uctx_ctxt;                             /* is also an elan context */
++
++    spinlock_t               uctx_spinlock;                           /* spinlock for items used with interrupt handler */
++    kcondvar_t               uctx_wait;                               /* place to sleep (traphandler/swapout/swapin/neterr fixup) */
++
++    unsigned         uctx_status;                             /* status                               (uctx_spinlock) */
++
++    pid_t            uctx_trap_pid;                           /* pid to deliver signals to on trap */
++    int                      uctx_trap_signo;                         /* signal number to deliver */
++    unsigned         uctx_trap_state;                         /* state of trap handling code */
++    unsigned         uctx_trap_count;                         /* count of "thread" in user_trap_handler() */
++
++    unsigned         uctx_int_count;                          /* # interrupts since last zeroed */
++    unsigned long      uctx_int_start;                                /* tick when int_count last zeroed */
++    unsigned long      uctx_int_delay;                                /* # ticks to delay next wakeup */
++    struct timer_list  uctx_int_timer;                                /* and timer to use to delay signal */
++
++    struct timer_list  uctx_neterr_timer;                     /* network error timer */
++
++    struct list_head   uctx_vpseg_list;                               /* list of vp segments we've got */
++    kmutex_t           uctx_vpseg_lock;                               /*   and lock to protect it. */
++    ELAN4_ROUTE_TABLE *uctx_routetable;                               /* our virtual process table */
++    ELAN_POSITION      uctx_position;                         /* position in network */
++
++    struct list_head   uctx_cent_list;                                /* list of attached network contexts */
++
++    USER_CQ         *uctx_ddcq;                               /* command queue for re-issueing traps */
++    E4_uint64        uctx_ddcq_insertcnt;                     /* # dwords inserted into command queue */
++    E4_uint64          uctx_ddcq_completed;                   /* last "completed" write was here */
++    int                      uctx_ddcq_intr;                          /* count of outstanding ddcq interrupts */
++
++    ELAN4_HALTOP       uctx_haltop;                           /* halt operation for flushing */
++    ELAN4_DMA_FLUSHOP  uctx_dma_flushop;                      /* flush operation for flushing dma runqueue */
++
++    INTCOOKIE_TABLE   *uctx_intcookie_table;                  /* table of interrupt cookies (shared with other uctxs for this task) */
++
++    kmutex_t         uctx_cqlock;                             /* lock for create/destory cqs */
++    struct list_head   uctx_cqlist;                           /* list of command queues               (uctx_cqlock,uctx_spinlock) */
++
++    ELAN4_DPROC_TRAP  *uctx_dprocTraps;                               /* queue of dproc traps to resolve/reissue */
++    RING_QUEUE               uctx_dprocTrapQ;
++
++    ELAN4_TPROC_TRAP  *uctx_tprocTraps;                               /* queue of tproc traps to resolve/reissue */
++    RING_QUEUE         uctx_tprocTrapQ;
++
++    ELAN4_EPROC_TRAP  *uctx_eprocTraps;                               /* queue of eproc traps to resolve */
++    RING_QUEUE               uctx_eprocTrapQ;
++
++    USER_IPROC_TRAP    uctx_iprocTrap[2];                     /* input trap state, 1 per virtual channel */
++
++    E4_DMA          *uctx_dmas;                               /* queue of dmas to restart */
++    RING_QUEUE         uctx_dmaQ;
++    
++    E4_ThreadRegs     *uctx_threads;                          /* queue of threads to restart */
++    RING_QUEUE         uctx_threadQ;
++
++    ELAN4_NETERR_MSG  *uctx_msgs;                             /* queue of neterr messages */
++    RING_QUEUE               uctx_msgQ;
++    kmutex_t         uctx_rgnmutex;                           /* lock for create/destroy regions */
++    spinlock_t               uctx_rgnlock;                            /* spinlock to protect linked lists */
++    USER_RGN        *uctx_mrgns;                              /* Doubly linked list of memory regions (uctx_rgnlock) */
++    USER_RGN        *uctx_mtail;                              /* Last memory region on list           (uctx_rgnlock) */
++    USER_RGN        *uctx_mrgnlast;                           /* Last region 'hit'                    (uctx_rgnlock) */
++
++    USER_RGN        *uctx_ergns;                              /* Doubly linked list of memory regions (uctx_rgnlock) */
++    USER_RGN        *uctx_etail;                              /* Last memory region on list           (uctx_rgnlock) */
++    USER_RGN        *uctx_ergnlast;                           /* Last region 'hit'                    (uctx_rgnlock) */
++
++    ELAN4_USER_PAGE   *uctx_upage;                            /* kernel page shared with user */
++    sdramaddr_t              uctx_trampoline;                         /* sdram page for tproc trampoline */
++
++    E4_Addr          uctx_upage_addr;                         /*   elan addr page mapped into */
++    E4_Addr          uctx_trestart_addr;                      /* address of thread restart code */
++    FAULT_SAVE         *uctx_faults;
++    FAULT_SAVE         *uctx_fault_list;
++    int                 uctx_num_fault_save;
++    spinlock_t          uctx_fault_lock;
++} USER_CTXT;
++
++/* bit values for uctx_status */
++#define UCTX_EXITING                          (1 << 0)                /* context is exiting. */
++#define UCTX_USER_FILTERING                   (1 << 1)                /* user requested context filter */
++#define UCTX_USER_STOPPED                     (1 << 2)                /* user requested stop */
++
++#define UCTX_SWAPPING                         (1 << 3)                /* context is swapping out */
++#define UCTX_SWAPPED                          (1 << 4)                /* context is swapped out */
++
++#define UCTX_STOPPING                         (1 << 5)                /* stopping elan from running this context */
++#define UCTX_STOPPED                          (1 << 6)                /* elan no longer running this context */
++
++#define UCTX_EPROC_QUEUE_FULL                 (1 << 7)                /* reasons for stopping running */
++#define UCTX_DPROC_QUEUE_FULL                 (1 << 8)
++#define UCTX_TPROC_QUEUE_FULL                 (1 << 9)
++#define UCTX_IPROC_CH0_TRAPPED                        (1 << 10)
++#define UCTX_IPROC_CH1_TRAPPED                        (1 << 11)
++
++#define UCTX_NETERR_TIMER                     (1 << 12)
++#define UCTX_NETERR_FIXUP                     (1 << 13)
++
++#define UCTX_EPROC_QUEUE_OVERFLOW             (1 << 14)
++#define UCTX_DPROC_QUEUE_OVERFLOW             (1 << 15)
++#define UCTX_TPROC_QUEUE_OVERFLOW             (1 << 16)
++
++#define UCTX_EPROC_QUEUE_ERROR                        (1 << 17)
++#define UCTX_DPROC_QUEUE_ERROR                        (1 << 18)
++#define UCTX_TPROC_QUEUE_ERROR                        (1 << 19)
++
++#define UCTX_STOPPED_REASONS                  (UCTX_EPROC_QUEUE_FULL | UCTX_DPROC_QUEUE_FULL | UCTX_TPROC_QUEUE_FULL)
++#define UCTX_SWAPPED_REASONS                  (UCTX_EXITING | UCTX_USER_STOPPED | UCTX_NETERR_FIXUP)
++#define UCTX_NACKING_REASONS                  (UCTX_USER_FILTERING | UCTX_IPROC_CH0_TRAPPED | UCTX_IPROC_CH1_TRAPPED)
++
++#define UCTX_OVERFLOW_REASONS                 (UCTX_EPROC_QUEUE_OVERFLOW | UCTX_DPROC_QUEUE_OVERFLOW | UCTX_TPROC_QUEUE_OVERFLOW)
++#define UCTX_ERROR_REASONS                    (UCTX_EPROC_QUEUE_ERROR | UCTX_DPROC_QUEUE_ERROR | UCTX_TPROC_QUEUE_ERROR)
++
++#define UCTX_RUNNABLE(uctx)                   (((uctx)->uctx_status & (UCTX_SWAPPED_REASONS | UCTX_STOPPED_REASONS)) == 0)
++#define UCTX_NACKING(uctx)                    (((uctx)->uctx_status & (UCTX_SWAPPED_REASONS | UCTX_STOPPED_REASONS | UCTX_NACKING_REASONS)) != 0)
++
++/* values for uctx_trap_signalled */
++#define UCTX_TRAP_IDLE                                0
++#define UCTX_TRAP_SLEEPING                    1
++#define UCTX_TRAP_SIGNALLED                   2
++#define UCTX_TRAP_ACTIVE                      3
++
++extern int        user_p2p_route_options;
++extern int        user_bcast_route_options;
++extern int      user_dproc_retry_count;
++extern int      user_cproc_retry_count;
++
++extern USER_CTXT *user_alloc (ELAN4_DEV *dev);
++extern void       user_free (USER_CTXT *uctx);
++extern void       user_swapout (USER_CTXT *uctx, unsigned reason);
++extern void       user_swapin (USER_CTXT *uctx, unsigned reason);
++extern int        user_attach (USER_CTXT *uctx, ELAN_CAPABILITY *cap);
++extern void       user_detach (USER_CTXT *uctx, ELAN_CAPABILITY *cap);
++extern void       user_block_inputter (USER_CTXT *uctx, unsigned blocked);
++extern int        user_alloc_trap_queues (USER_CTXT *uctx, unsigned ndproc_traps, unsigned neproc_traps, 
++                                        unsigned ntproc_traps, unsigned nthreads, unsigned ndmas);
++
++extern int        user_add_p2pvp (USER_CTXT *uctx, unsigned process, ELAN_CAPABILITY *cap);
++extern int        user_add_bcastvp (USER_CTXT *uctx, unsigned process, unsigned lowvp, unsigned highvp);
++extern int        user_removevp (USER_CTXT *uctx, unsigned process);
++
++extern int        user_set_route (USER_CTXT *uctx, unsigned process, E4_VirtualProcessEntry *route);
++extern int        user_reset_route (USER_CTXT *uctx, unsigned process);
++extern int        user_get_route (USER_CTXT *uctx, unsigned process, E4_VirtualProcessEntry *route);
++extern int        user_check_route (USER_CTXT *uctx, unsigned process, E4_VirtualProcessEntry *route, unsigned *error);
++extern int      user_send_neterr_msg (USER_CTXT *uctx, unsigned int vp, unsigned int nctx, unsigned int retries, ELAN4_NETERR_MSG *msg);
++extern int        user_neterr_sten (USER_CTXT *uctx, unsigned int vp, E4_uint64 cookie, int waitforeop);
++extern int        user_neterr_dma (USER_CTXT *uctx, unsigned int vp, E4_uint64 cookie, int waitforeop);
++
++extern int        user_resume_eproc_trap (USER_CTXT *uctx, E4_Addr addr);
++extern int        user_resume_cproc_trap (USER_CTXT *uctx, unsigned indx);
++extern int        user_resume_dproc_trap (USER_CTXT *uctx, E4_DMA *dma);
++extern int        user_resume_tproc_trap (USER_CTXT *uctx, E4_ThreadRegs *regs);
++extern int        user_resume_iproc_trap (USER_CTXT *uctx, unsigned channel, unsigned trans,
++                                        E4_IprocTrapHeader *hdrp, E4_IprocTrapData *datap);
++
++extern int        user_trap_handler (USER_CTXT *uctx, ELAN4_USER_TRAP *utrapp, int nticks);
++extern USER_CQ   *user_findcq (USER_CTXT *uctx, unsigned num);
++extern USER_CQ   *user_alloccq (USER_CTXT *uctx, unsigned size, unsigned perm, unsigned flags);
++extern void       user_freecq (USER_CTXT *uctx, USER_CQ *cq);
++extern void       user_dropcq (USER_CTXT *uctx, USER_CQ *cq);
++
++/* user_osdep.c */
++extern int        user_load_range (USER_CTXT *uctx, E4_Addr addr, unsigned long nbytes, E4_uint32 fsr);
++extern void       user_update_main (USER_CTXT *uctx, struct mm_struct *mm, unsigned long start, unsigned long len);
++extern void       user_unload_main (USER_CTXT *uctx, unsigned long start, unsigned long len);
++
++
++/* regions.c */
++extern USER_RGN  *user_findrgn_elan (USER_CTXT *uctx, E4_Addr addr, int tail);
++extern USER_RGN  *user_findrgn_main (USER_CTXT *uctx, virtaddr_t addr, int tail);
++extern USER_RGN  *user_rgnat_elan (USER_CTXT *uctx, E4_Addr addr);
++extern USER_RGN  *user_rgnat_main (USER_CTXT *uctx, virtaddr_t addr);
++extern int        user_setperm (USER_CTXT *uctx, virtaddr_t maddr, E4_Addr eaddr, unsigned long len, unsigned perm);
++extern void       user_clrperm (USER_CTXT *uctx, E4_Addr addr, unsigned long len);
++extern int        user_checkperm (USER_CTXT *uctx, E4_Addr raddr, unsigned long rsize, unsigned access);
++extern virtaddr_t user_elan2main (USER_CTXT *uctx, E4_Addr addr);
++extern E4_Addr    user_main2elan (USER_CTXT *uctx, virtaddr_t addr);
++extern void       user_preload_main (USER_CTXT *uctx, virtaddr_t addr, unsigned long len);
++extern void       user_freergns (USER_CTXT *uctx);
++
++/* user_ddcq.c */
++extern int        user_ddcq_check (USER_CTXT *uctx, unsigned num);
++extern int        user_ddcq_flush (USER_CTXT *uctx);
++extern void       user_ddcq_intr (USER_CTXT *uctx);
++extern void       user_ddcq_write_dword (USER_CTXT *uctx, E4_Addr addr, E4_uint64 value);
++extern void       user_ddcq_interrupt (USER_CTXT *uctx, E4_uint64 cookie);
++extern void       user_ddcq_run_dma (USER_CTXT *uctx, E4_DMA *dma);
++extern void       user_ddcq_run_thread (USER_CTXT *uctx, E4_ThreadRegs *regs);
++extern void       user_ddcq_setevent (USER_CTXT *uctx, E4_Addr addr);
++extern void       user_ddcq_seteventn (USER_CTXT *uctx, E4_Addr addr, E4_uint32 count);
++extern void       user_ddcq_waitevent (USER_CTXT *uctx, E4_Addr addr, E4_uint64 CountAndType, E4_uint64 Param0, E4_uint64 Param1);
++
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++#endif /* __ELAN4_USER_H */
+Index: linux-2.6.5/include/elan4/userregs.h
+===================================================================
+--- linux-2.6.5.orig/include/elan4/userregs.h  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan4/userregs.h       2005-05-11 12:10:12.617905368 -0400
+@@ -0,0 +1,383 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN4_USERREGS_H
++#define __ELAN4_USERREGS_H
++
++#ident "$Id: userregs.h,v 1.14.2.1 2004/10/07 10:57:40 addy Exp $"
++/*      $Source: /cvs/master/quadrics/elan4hdr/userregs.h,v $*/
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++/*
++ * Statistic control reg values
++ * Each 4-bit nibble of the control word specifies what statistic
++ * is to be recorded in each of the 8 statistic counters
++ */
++#define COUNT_REG0_SHIFT   32ull
++#define COUNT_REG1_SHIFT   36ull
++#define COUNT_REG2_SHIFT   40ull
++#define COUNT_REG3_SHIFT   44ull
++#define COUNT_REG4_SHIFT   48ull
++#define COUNT_REG5_SHIFT   52ull
++#define COUNT_REG6_SHIFT   56ull
++#define COUNT_REG7_SHIFT   60ull
++
++
++/* Count reg 0 */
++#define STC_INPUT_NON_WRITE_BLOCKS    (0x0ull << COUNT_REG0_SHIFT)
++#define STP_DMA_EOP_WAIT_ACK          (0x1ull << COUNT_REG0_SHIFT)
++#define STP_TPROC_RUNNING             (0x2ull << COUNT_REG0_SHIFT)
++#define STC_STEN_PKTS_OPEN              (0x3ull << COUNT_REG0_SHIFT)
++#define STP_CPROC_HOLDS_FFU_DP        (0x4ull << COUNT_REG0_SHIFT)
++#define STC_TLB_TABLE_WALKS             (0x5ull << COUNT_REG0_SHIFT)
++#define STC_CACHE_HITS                  (0x6ull << COUNT_REG0_SHIFT)
++#define STC_PCI_SLAVE_READS             (0x7ull << COUNT_REG0_SHIFT)
++#define STP_PCI_WAITING_FOR_GNT         (0x8ull << COUNT_REG0_SHIFT)
++#define STP_SYS_CLOCK_RATE0           (0xfull << COUNT_REG0_SHIFT)
++
++#define STATS_REG0_NAMES {                     \
++          "STC_INPUT_NON_WRITE_BLOCKS",        \
++          "STP_DMA_EOP_WAIT_ACK",              \
++          "STP_TPROC_RUNNING",                 \
++          "STC_STEN_PKTS_OPEN",                \
++          "STP_CPROC_HOLDS_FFU_DP",            \
++          "STC_TLB_TABLE_WALKS",               \
++          "STC_CACHE_HITS",                    \
++          "STC_PCI_SLAVE_READS",               \
++          "STP_PCI_WAITING_FOR_GNT",           \
++          "STP_SYS_CLOCK_RATE0"                \
++}
++
++/* Count reg 1 */
++#define STC_INPUT_WRITE_BLOCKS        (0x0ull << COUNT_REG1_SHIFT)
++#define STP_DMA_DATA_TRANSMITTING     (0x1ull << COUNT_REG1_SHIFT)
++#define STC_CPROC_VALUES_EXE          (0x2ull << COUNT_REG1_SHIFT)
++#define STC_STEN_TRANS_SENT           (0x3ull << COUNT_REG1_SHIFT)
++#define STP_TPROC_DQ_HOLDS_FFU_DP     (0x4ull << COUNT_REG1_SHIFT)
++#define STC_TPROC_TLB_HITS            (0x5ull << COUNT_REG1_SHIFT)
++#define STC_CACHE_ALLOC_MISSES        (0x6ull << COUNT_REG1_SHIFT)
++#define STP_PCI_MASTER_READ_WAITING   (0x7ull << COUNT_REG1_SHIFT)
++#define STP_PCI_WAITING_FOR_DEVSEL      (0x8ull << COUNT_REG1_SHIFT)
++#define STP_SYS_CLOCK_RATE1           (0xfull << COUNT_REG1_SHIFT)
++
++#define STATS_REG1_NAMES {                   \
++          "STC_INPUT_WRITE_BLOCKS",            \
++          "STP_DMA_DATA_TRANSMITTING",         \
++          "STC_CPROC_VALUES_EXE",              \
++          "STC_STEN_TRANS_SENT",               \
++          "STP_TPROC_DQ_HOLDS_FFU_DP",         \
++          "STC_TPROC_TLB_HITS",                \
++          "STC_CACHE_ALLOC_MISSES",            \
++          "STP_PCI_MASTER_READ_WAITING",       \
++          "STP_PCI_WAITING_FOR_DEVSEL",        \
++          "STP_SYS_CLOCK_RATE1"                \
++}
++
++/* Count reg 2 */
++#define STC_INPUT_PKTS                (0x0ull << COUNT_REG2_SHIFT)
++#define STP_DMA_WAITING_MEM           (0x1ull << COUNT_REG2_SHIFT)
++#define STC_CPROC_TRANSFERS             (0x2ull << COUNT_REG2_SHIFT)
++#define STP_STEN_WAIT_NETWORK_BUSY    (0x3ull << COUNT_REG2_SHIFT)
++#define STP_IPROC_HOLDS_FFU_DP        (0x4ull << COUNT_REG2_SHIFT)
++#define STC_UNITS_TLB_HITS            (0x5ull << COUNT_REG2_SHIFT)
++#define STC_CACHE_NON_ALLOC_MISSES      (0x6ull << COUNT_REG2_SHIFT)
++#define STP_PCI_MASTER_WRITE_WAITING  (0x7ull << COUNT_REG2_SHIFT)
++#define STC_PCI_OUT_OF_ORDER_SPLIT_COMP (0x8ull << COUNT_REG2_SHIFT)
++#define STP_SYS_CLOCK_RATE2           (0xfull << COUNT_REG2_SHIFT)
++
++#define STATS_REG2_NAMES {                   \
++          "STC_INPUT_PKTS",                    \
++          "STP_DMA_WAITING_MEM",               \
++          "STC_CPROC_TRANSFERS",               \
++          "STP_STEN_WAIT_NETWORK_BUSY",        \
++          "STP_IPROC_HOLDS_FFU_DP",            \
++          "STC_UNITS_TLB_HITS",                \
++          "STC_CACHE_NON_ALLOC_MISSES",        \
++          "STP_PCI_MASTER_WRITE_WAITING",      \
++          "STC_PCI_OUT_OF_ORDER_SPLIT_COMP",   \
++          "STP_SYS_CLOCK_RATE2"                \
++}
++
++/* Count reg 3 */
++#define STC_INPUT_PKTS_REJECTED         (0x0ull << COUNT_REG3_SHIFT)
++#define STP_DMA_WAIT_NETWORK_BUSY       (0x1ull << COUNT_REG3_SHIFT)
++#define STC_CPROC_PREFETCH_SDRAM        (0x2ull << COUNT_REG3_SHIFT)
++#define STP_STEN_BLOCKED_ACKS_OR_VC     (0x3ull << COUNT_REG3_SHIFT)
++#define STP_EPROC_HOLDS_FFU_DP          (0x4ull << COUNT_REG3_SHIFT)
++#define STP_TPROC_BLOCKED_MEMSYS        (0x5ull << COUNT_REG3_SHIFT)
++#define STC_CACHE_WRITE_BACKS           (0x6ull << COUNT_REG3_SHIFT)
++#define STP_PCI_SLAVE_READ_WAITING      (0x7ull << COUNT_REG3_SHIFT)
++#define STP_PCI_IDLE_CYCLES           (0x8ull << COUNT_REG3_SHIFT)
++#define STP_SYS_CLOCK_RATE3           (0xfull << COUNT_REG3_SHIFT)
++
++#define STATS_REG3_NAMES {                   \
++          "STC_INPUT_PKTS_REJECTED",           \
++          "STP_DMA_WAIT_NETWORK_BUSY",         \
++          "STC_CPROC_PREFETCH_SDRAM",          \
++          "STP_STEN_BLOCKED_ACKS_OR_VC",       \
++          "STP_EPROC_HOLDS_FFU_DP",            \
++          "STP_TPROC_BLOCKED_MEMSYS",          \
++          "STC_CACHE_WRITE_BACKS",             \
++          "STP_PCI_SLAVE_READ_WAITING",        \
++          "STP_PCI_IDLE_CYCLES",               \
++          "STP_SYS_CLOCK_RATE3"                \
++}
++
++/* Count reg 4 */
++#define STP_INPUT_DATA_TRANSMITTING   (0x0ull << COUNT_REG4_SHIFT)
++#define STC_DMA_PKTS_ACCEPTED         (0x1ull << COUNT_REG4_SHIFT)
++#define STC_CPROC_FLUSH_REQ_SDRAM     (0x2ull << COUNT_REG4_SHIFT)
++#define STP_STEN_EOP_WAIT_ACK         (0x3ull << COUNT_REG4_SHIFT)
++#define STP_DMA_HOLDS_FFU_DP          (0x4ull << COUNT_REG4_SHIFT)
++#define STP_UNIT_BLOCKED_MEMSYS       (0x5ull << COUNT_REG4_SHIFT)
++#define STC_PCI_MASTER_READS          (0x6ull << COUNT_REG4_SHIFT)
++#define STP_PCI_SLAVE_WRITE_WAITING   (0x7ull << COUNT_REG4_SHIFT)
++#define STC_INPUT_PACKETS_DISCARDED   (0x8ull << COUNT_REG4_SHIFT)
++#define STP_SYS_CLOCK_RATE4           (0xfull << COUNT_REG4_SHIFT)
++
++#define STATS_REG4_NAMES {                   \
++          "STP_INPUT_DATA_TRANSMITTING",       \
++          "STC_DMA_PKTS_ACCEPTED",             \
++          "STC_CPROC_FLUSH_REQ_SDRAM",         \
++          "STP_STEN_EOP_WAIT_ACK",             \
++          "STP_DMA_HOLDS_FFU_DP",              \
++          "STP_UNIT_BLOCKED_MEMSYS",           \
++          "STC_PCI_MASTER_READS",              \
++          "STP_PCI_SLAVE_WRITE_WAITING",       \
++          "STC_INPUT_PACKETS_DISCARDED",       \
++          "STP_SYS_CLOCK_RATE4"                \
++}
++
++/* Count reg 5 */
++#define STP_INPUT_WAITING_NETWORK_DATA  (0x0ull << COUNT_REG5_SHIFT)
++#define STC_DMA_PKTS_REJECTED           (0x1ull << COUNT_REG5_SHIFT)
++#define STC_CPROC_INSERT_CACHE_MISSES   (0x2ull << COUNT_REG5_SHIFT)
++#define STP_STEN_TRANSMITTING_DATA      (0x3ull << COUNT_REG5_SHIFT)
++#define FFU_BLOCKED_DIFF_FFU_PROC       (0x4ull << COUNT_REG5_SHIFT)
++#define STP_TABLE_WALKS_BLOCKED_MEMSYS  (0x5ull << COUNT_REG5_SHIFT)
++#define STC_PCI_MASTER_WRITES           (0x6ull << COUNT_REG5_SHIFT)
++#define STP_PCI_MASTER_HOLDS_BUS        (0x7ull << COUNT_REG5_SHIFT)
++#define STC_PCI_NO_SPLIT_COMPS                (0x8ull << COUNT_REG5_SHIFT)
++#define STP_SYS_CLOCK_RATE5           (0xfull << COUNT_REG5_SHIFT)
++
++#define STATS_REG5_NAMES {                   \
++          "STP_INPUT_WAITING_NETWORK_DATA",    \
++          "STC_DMA_PKTS_REJECTED",             \
++          "STC_CPROC_INSERT_CACHE_MISSES",     \
++          "STP_STEN_TRANSMITTING_DATA",        \
++          "FFU_BLOCKED_DIFF_FFU_PROC",         \
++          "STP_TABLE_WALKS_BLOCKED_MEMSYS",    \
++          "STC_PCI_MASTER_WRITES",             \
++          "STP_PCI_MASTER_HOLDS_BUS",          \
++          "STC_PCI_NO_SPLIT_COMPS",            \
++          "STP_SYS_CLOCK_RATE5"                \
++}
++
++/* Count reg 6 */
++#define STP_INPUT_BLOCKED_WAITING_TRANS (0x0ull << COUNT_REG6_SHIFT)
++#define STP_TPROC_INST_STALL                  (0x1ull << COUNT_REG6_SHIFT)
++#define STP_CPROC_WAITING_DESCHED             (0x2ull << COUNT_REG6_SHIFT)
++#define STP_STEN_PKT_OPEN_WAITING_DATA        (0x3ull << COUNT_REG6_SHIFT)
++#define STP_TLB_HASH_TABLE_ACCESSES           (0x4ull << COUNT_REG6_SHIFT)
++#define STP_PCI_SLAVE_BLOCKED_MEMSYS          (0x5ull << COUNT_REG6_SHIFT)
++#define STP_PCI_TRANSFERRING_DATA       (0x6ull << COUNT_REG6_SHIFT)
++#define STP_PCI_MASTER_WAITING_BUS      (0x7ull << COUNT_REG6_SHIFT)
++#define STP_PCI_READ_LATENCY          (0x8ull << COUNT_REG6_SHIFT)
++#define STP_SYS_CLOCK_RATE6           (0xfull << COUNT_REG6_SHIFT)
++
++#define STATS_REG6_NAMES {                   \
++          "STP_INPUT_BLOCKED_WAITING_TRANS",   \
++          "STP_TPROC_INST_STALL",              \
++          "STP_CPROC_WAITING_DESCHED",         \
++          "STP_STEN_PKT_OPEN_WAITING_DATA",    \
++          "STP_TLB_HASH_TABLE_ACCESSES",       \
++          "STP_PCI_SLAVE_BLOCKED_MEMSYS",      \
++          "STP_PCI_TRANSFERRING_DATA",         \
++          "STP_PCI_MASTER_WAITING_BUS",        \
++          "STP_PCI_READ_LATENCY",              \
++          "STP_SYS_CLOCK_RATE6"                \
++}
++
++/* Count reg 7 */
++#define STC_INPUT_CTX_FILTER_FILL       (0x0ull << COUNT_REG7_SHIFT)  
++#define STP_TPROC_LOAD_STORE_STALL      (0x1ull << COUNT_REG7_SHIFT)
++#define STC_CPROC_TIMEOUTS              (0x2ull << COUNT_REG7_SHIFT)
++#define STP_STEN_BLOCKED_NETWORK        (0x3ull << COUNT_REG7_SHIFT)
++#define STP_TLB_CHAIN_ACCESSES          (0x4ull << COUNT_REG7_SHIFT)
++#define STP_CPROC_SCHED_BLOCKED_MEMSYS  (0x5ull << COUNT_REG7_SHIFT)
++#define STC_PCI_SLAVE_WRITES            (0x6ull << COUNT_REG7_SHIFT)
++#define STC_PCI_DISCONNECTS_RETRIES     (0x7ull << COUNT_REG7_SHIFT)
++#define STC_RING_OSCILLATOR           (0x8ull << COUNT_REG7_SHIFT)
++#define STP_SYS_CLOCK_RATE7           (0xfull << COUNT_REG7_SHIFT)
++
++#define STATS_REG7_NAMES {                   \
++          "STC_INPUT_CTX_FILTER_FILL",         \
++          "STP_TPROC_LOAD_STORE_STALL",        \
++          "STC_CPROC_TIMEOUTS",                \
++          "STP_STEN_BLOCKED_NETWORK",          \
++          "STP_TLB_CHAIN_ACCESSES",            \
++          "STP_CPROC_SCHED_BLOCKED_MEMSYS",    \
++          "STC_PCI_SLAVE_WRITES",              \
++          "STC_PCI_DISCONNECTS_RETRIES",       \
++          "STC_RING_OSCILLATOR",               \
++          "STP_SYS_CLOCK_RATE7"                \
++}
++
++#define STATS_REG_NAMES { \
++    STATS_REG0_NAMES, \
++    STATS_REG1_NAMES, \
++    STATS_REG2_NAMES, \
++    STATS_REG3_NAMES, \
++    STATS_REG4_NAMES, \
++    STATS_REG5_NAMES, \
++    STATS_REG6_NAMES, \
++    STATS_REG7_NAMES, \
++}
++
++
++#define INPUT_PERF_STATS        (STC_INPUT_NON_WRITE_BLOCKS | STC_INPUT_WRITE_BLOCKS |              \
++                               STC_INPUT_PKTS | STC_INPUT_PKTS_REJECTED |                         \
++                                 STC_INPUT_CTX_FILTER_FILL | STP_INPUT_DATA_TRANSMITTING |           \
++                               STP_INPUT_WAITING_NETWORK_DATA | STP_INPUT_BLOCKED_WAITING_TRANS | STC_INPUT_PACKETS_DISCARDED) 
++
++#define DMA_PERF_STATS          (STC_DMA_PKTS_ACCEPTED | STC_DMA_PKTS_REJECTED |                    \
++                                 STP_DMA_EOP_WAIT_ACK | STP_DMA_DATA_TRANSMITTING |                 \
++                               STP_DMA_WAITING_MEM | STP_DMA_WAIT_NETWORK_BUSY)                 
++
++
++#define TPROC_PERF_STATS        (STP_TPROC_RUNNING | STP_TPROC_INST_STALL |                         \
++                                 STP_TPROC_LOAD_STORE_STALL)
++
++#define CPROC_PERF_STATS        (STC_CPROC_VALUES_EXE | STC_CPROC_TRANSFERS |                       \
++                               STC_CPROC_PREFETCH_SDRAM | STC_CPROC_FLUSH_REQ_SDRAM |             \
++                               STC_CPROC_INSERT_CACHE_MISSES | STP_CPROC_WAITING_DESCHED |        \
++                               STC_CPROC_TIMEOUTS)
++
++#define STEN_PERF_STATS         (STC_STEN_PKTS_OPEN | STC_STEN_TRANS_SENT |                         \
++                               STP_STEN_WAIT_NETWORK_BUSY | STP_STEN_BLOCKED_ACKS_OR_VC |         \
++                               STP_STEN_EOP_WAIT_ACK | STP_STEN_TRANSMITTING_DATA |               \
++                               STP_STEN_PKT_OPEN_WAITING_DATA | STP_STEN_BLOCKED_NETWORK)
++
++#define FFU_PREF_STATS          (STP_CPROC_HOLDS_FFU_DP | STP_TPROC_DQ_HOLDS_FFU_DP |               \
++                               STP_IPROC_HOLDS_FFU_DP | STP_EPROC_HOLDS_FFU_DP |                  \
++                               STP_DMA_HOLDS_FFU_DP | FFU_BLOCKED_DIFF_FFU_PROC)
++
++#define TABLE_WALK_PERF_STATS   (STC_TPROC_TLB_HITS | STC_UNITS_TLB_HITS |                          \
++                               STP_TLB_HASH_TABLE_ACCESSES | STP_TLB_CHAIN_ACCESSES |             \
++                               STC_TLB_TABLE_WALKS)
++
++#define ADDRESS_ARB_PERF_STATS  (STP_UNIT_BLOCKED_MEMSYS | STP_TPROC_BLOCKED_MEMSYS |               \
++                               STP_TABLE_WALKS_BLOCKED_MEMSYS | STP_CPROC_SCHED_BLOCKED_MEMSYS |  \
++                               STP_PCI_SLAVE_BLOCKED_MEMSYS)
++
++#define CACHE_PERF_STATS        (STC_CACHE_HITS | STC_CACHE_ALLOC_MISSES |                          \
++                               STC_CACHE_NON_ALLOC_MISSES | STC_CACHE_WRITE_BACKS)
++
++
++#define PCI_PERF_STATS          (STC_PCI_SLAVE_READS | STP_PCI_MASTER_READ_WAITING |                \
++                                 STP_PCI_MASTER_WRITE_WAITING | STP_PCI_SLAVE_READ_WAITING |        \
++                                 STP_PCI_SLAVE_WRITE_WAITING | STC_PCI_MASTER_WRITES |              \
++                                 STP_PCI_TRANSFERRING_DATA | STC_PCI_SLAVE_WRITES)
++
++#define PCIBUS_PERF_STATS       (STP_PCI_WAITING_FOR_GNT | STP_PCI_WAITING_FOR_DEVSEL |                   \
++                               STC_PCI_OUT_OF_ORDER_SPLIT_COMP | STP_PCI_IDLE_CYCLES |            \
++                               STC_PCI_MASTER_READS | STP_PCI_MASTER_HOLDS_BUS |                  \
++                               STP_PCI_MASTER_WAITING_BUS | STC_PCI_DISCONNECTS_RETRIES)
++
++                               
++    extern const char *elan_stats_names[8][10];
++
++#define ELAN_STATS_NAME(COUNT, CONTROL) (elan_stats_names[(COUNT)][(CONTROL) & 7])
++
++    typedef volatile union e4_StatsControl
++    {
++      E4_uint64 StatsControl;
++      struct
++      {
++#if (BYTE_ORDER == LITTLE_ENDIAN) || defined(__LITTLE_ENDIAN__)
++          E4_uint32 StatCont0:4;
++          E4_uint32 StatCont1:4;
++          E4_uint32 StatCont2:4;
++          E4_uint32 StatCont3:4;
++          E4_uint32 StatCont4:4;
++          E4_uint32 StatCont5:4;
++          E4_uint32 StatCont6:4;
++          E4_uint32 StatCont7:4;
++#else
++          E4_uint32 StatCont7:4;
++          E4_uint32 StatCont6:4;
++          E4_uint32 StatCont5:4;
++
++          E4_uint32 StatCont4:4;
++          E4_uint32 StatCont3:4;
++          E4_uint32 StatCont2:4;
++          E4_uint32 StatCont1:4;
++          E4_uint32 StatCont0:4;
++#endif
++          E4_uint32 pad;
++      } s;
++    } E4_StatsControl;
++
++typedef volatile union e4_StatsCount
++{
++   E4_uint64    ClockStat; 
++   struct
++   {
++       E4_uint32 ClockLSW;    /* read only */
++       E4_uint32 StatsCount;
++   } s;
++} E4_StatsCount;
++
++typedef volatile union e4_clock
++{
++   E4_uint64 NanoSecClock;
++   struct
++   {
++      E4_uint32 ClockLSW;
++      E4_uint32 ClockMSW;
++   } s;
++} E4_Clock;
++#define E4_TIME( X ) ((X).NanoSecClock)
++
++#define ELAN4_COMMS_CLOCK_FREQUENCY   660             /* In Mhz. This is half the bit rate. */
++#define ELAN4_CLOCK_ADD_VALUE         200             /* For 200ns increment rate */
++#define ELAN4_CLOCK_COMMS_DIV_VALUE   (((ELAN4_COMMS_CLOCK_FREQUENCY * ELAN4_CLOCK_ADD_VALUE) / (1000 * 4)) - 1)
++#define ELAN4_CLOCK_TICK_RATE         ((ELAN4_CLOCK_ADD_VALUE << 8) + ELAN4_CLOCK_COMMS_DIV_VALUE)
++
++typedef volatile union e4_clocktickrate
++{
++   E4_uint64 NanoSecClock;
++   struct
++   {
++      E4_uint32 pad1;
++      E4_uint32 TickRates;
++   } s;
++} E4_ClockTickRate;
++
++/*
++ * This is made into an 8k byte object.
++ */
++typedef volatile struct _E4_User_Regs
++{
++   E4_StatsCount      StatCounts[8];
++   E4_StatsCount      InstCount;
++   E4_Clock           Clock;
++   E4_StatsControl    StatCont;
++   E4_ClockTickRate   ClockTickRate;
++   E4_uint8           pad1[EightK - ((sizeof(E4_StatsCount)*9)+sizeof(E4_StatsControl)+
++                                      sizeof(E4_Clock)+sizeof(E4_ClockTickRate))];
++} E4_User_Regs;
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* __ELAN4_USERREGS_H */
+Index: linux-2.6.5/include/elan4/usertrap.h
+===================================================================
+--- linux-2.6.5.orig/include/elan4/usertrap.h  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan4/usertrap.h       2005-05-11 12:10:12.617905368 -0400
+@@ -0,0 +1,114 @@
++/*
++ *    Copyright (c) 2001-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: usertrap.h,v 1.17 2004/05/05 09:08:35 david Exp $"
++/*      $Source: /cvs/master/quadrics/elan4mod/usertrap.h,v $*/
++
++#ifndef __ELAN4_USERTRAP_H
++#define __ELAN4_USERTRAP_H
++
++#ifndef _ASM
++typedef struct elan4_user_page
++{
++    E4_uint64         upage_ddcq_completed;
++} ELAN4_USER_PAGE;
++
++typedef struct elan4_user_trap
++{
++    int                               ut_type;
++    unsigned                  ut_proc;
++    unsigned                  ut_args[4];
++
++    union {
++      ELAN4_EPROC_TRAP        eproc;
++      ELAN4_CPROC_TRAP        cproc;
++      ELAN4_DPROC_TRAP        dproc;
++      ELAN4_IPROC_TRAP        iproc;
++      ELAN4_TPROC_TRAP        tproc;
++      ELAN4_NETERR_MSG        msg;
++    }                         ut_trap;
++} ELAN4_USER_TRAP;
++
++#endif /* _ASM */
++
++
++/* value for ut_type */
++#define UTS_FINISHED          0                               /* all pending traps have been handled */
++#define UTS_RESCHEDULE                1                               /* must return to user mode and re-enter */
++#define UTS_UNIMP_INSTR               2                               /* unimplemented thread instruction */
++#define UTS_EXECUTE_PACKET    3                               /* iproc trap needs packet executing */
++#define UTS_NETWORK_ERROR_TRAP        4                               /* network error on this trap */
++#define UTS_NETWORK_ERROR_MSG 5                               /* network error message  */
++#define UTS_NETWORK_ERROR_TIMER       6                               /* network error timer expired */
++
++#define UTS_EFAULT            -1                              /* failed to copyout trap */
++#define UTS_INVALID_ADDR      -2                              /* all -ve codes mean trap could not be resolved. */
++#define UTS_INVALID_VPROC     -3
++#define UTS_INVALID_COMMAND   -4
++#define UTS_BAD_TRAP          -5
++#define UTS_ALIGNMENT_ERROR   -6
++#define UTS_QUEUE_OVERFLOW    -7
++#define UTS_QUEUE_ERROR               -8
++#define UTS_INVALID_TRANS     -9
++#define UTS_PERMISSION_DENIED -10
++#define UTS_CPROC_ERROR               -11
++#define UTS_INVALID_COOKIE    -12
++#define UTS_NETERR_ERROR      -13
++
++/* "special" values for registering handlers */
++#define UTS_ALL_TRAPS         -9999
++
++/* value for ut_proc */
++#define UTS_NOPROC            0
++#define UTS_EPROC             1
++#define UTS_CPROC             2
++#define UTS_DPROC             3
++#define UTS_TPROC             4
++#define UTS_IPROC             5
++#define UTS_NETERR_MSG                6
++
++/* unimplemented trap numbers for thread processor */
++#define ELAN4_T_TRAP_INSTR(t) (0x80202000 | ((t) & 0xFF))
++
++#define ELAN4_T_SYSCALL_TRAP  1
++#  define ELAN4_T_OPEN                0
++#  define ELAN4_T_WRITE               1
++#  define ELAN4_T_READ                2
++#  define ELAN4_T_IOCTL               3
++#  define ELAN4_T_LSEEK               4
++#  define ELAN4_T_POLL                5
++#  define ELAN4_T_CLOSE               6
++#  define ELAN4_T_KILL                7
++#  define ELAN4_T_MMAP                8
++#  define ELAN4_T_MUNMAP      9
++#  define ELAN4_T_ABORT               100
++#  define ELAN4_T_DEBUG               101
++#  define ELAN4_T_REGDUMP     102
++
++#define ELAN4_T_REGDUMP_TRAP  2
++
++#define ELAN4_T_LIBELAN_TRAP  3
++#  define ELAN4_T_TPORT_NEWBUF        0
++#  define ELAN4_T_TPORT_GC    1
++#  define ELAN4_T_TPORT_DEBUG 2
++
++#define ELAN4_T_ALLOC_TRAP    4
++#  define ELAN4_T_ALLOC_ELAN  0
++#  define ELAN4_T_ALLOC_MAIN  1
++#  define ELAN4_T_FREE_ELAN   2
++#  define ELAN4_T_FREE_MAIN   3
++
++/* reserved main interrupt cookies */
++#define ELAN4_INT_COOKIE_DDCQ 0
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
++#endif /* __ELAN4_USERTRAP_H */
+Index: linux-2.6.5/include/elan4/xsdram.h
+===================================================================
+--- linux-2.6.5.orig/include/elan4/xsdram.h    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/elan4/xsdram.h 2005-05-11 12:10:12.617905368 -0400
+@@ -0,0 +1,59 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2003 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __ELAN4_XSDRAM_H
++#define __ELAN4_XSDRAM_H
++
++#ident "@(#)$Id: xsdram.h,v 1.13 2004/03/05 12:32:04 jon Exp $ $Name: QSNETMODULES-4-31_20050321 $"
++/*      $Source: /cvs/master/quadrics/elan4hdr/xsdram.h,v $*/
++
++/* SAMSUNG K4H281638D-TCB3 */
++
++#define SDRAM_tRCF_1_SH         0
++#define SDRAM_tRP_1_SH          4
++#define SDRAM_tRCD_SH           8
++#define SDRAM_tRRD_SH           12
++#define SDRAM_tEndWr_SH         16
++#define SDRAM_tEndRd_SH         20
++#define SDRAM_Burst_SH          24
++#define SDRAM_CL_SH             28
++#define SDRAM_DsblBypass       (1ULL << 31)
++#define SDRAM_RefreshRate_SH    32
++#define SDRAM_RamSize_SH        34
++#define SDRAM_ReadLtncy_1_SH    36
++#define SDRAM_RdOffset_SH       40
++#define SDRAM_FlightDelay_SH    42
++
++#define SDRAM_ENABLE_ECC       (1ULL << 44) // Enables error detecting on the ECC.
++#define SDRAM_SDRAM_TESTING    (1ULL << 45) // Switches to test mode for checking EEC data bits
++#define SDRAM_SETUP            (1ULL << 46) // Writes SDram control reg when set. Also starts
++
++#define SDRAM_CS_MODE0          0ULL         // 64Mbit, 128Mbit, 256Mbit, 512Mbit or 1Gbit (16-bit output)
++#define SDRAM_CS_MODE1          1ULL         // 64Mbit, 128Mbit, 256Mbit or 512Mbit (8-bit output)
++#define SDRAM_CS_MODE2          2ULL         // 2Gbit (16-bit output) or 1Gbit (8-bit output)
++#define SDRAM_CS_MODE3          3ULL         // 4Gbit (16-bit output) or 2Gbit (8-bit output)
++
++#if defined(LINUX) && !defined(CONFIG_MPSAS)
++#define SDRAM_STARTUP_VALUE   ((0xbULL << SDRAM_tRCF_1_SH)      | (0x2ULL << SDRAM_tRP_1_SH)       | \
++                               (0x3ULL << SDRAM_tRCD_SH)        | (0x2ULL << SDRAM_tRRD_SH)        | \
++                               (0xaULL << SDRAM_tEndWr_SH)      | (0x6ULL << SDRAM_tEndRd_SH)      | \
++                               (0x8ULL << SDRAM_Burst_SH)       | (0x6ULL << SDRAM_CL_SH)          | \
++                               (0x2ULL << SDRAM_RefreshRate_SH) | (0x3ULL << SDRAM_RamSize_SH)     | \
++                               (0x1ULL << SDRAM_RdOffset_SH)    | (0x1ULL << SDRAM_FlightDelay_SH) | \
++                               (0x4ULL << SDRAM_ReadLtncy_1_SH))
++#else
++#define SDRAM_STARTUP_VALUE   ((0xbULL << SDRAM_tRCF_1_SH)      | (0x2ULL << SDRAM_tRP_1_SH)       | \
++                               (0x3ULL << SDRAM_tRCD_SH)        | (0x2ULL << SDRAM_tRRD_SH)        | \
++                               (0xaULL << SDRAM_tEndWr_SH)      | (0x6ULL << SDRAM_tEndRd_SH)      | \
++                               (0x8ULL << SDRAM_Burst_SH)       | (0x6ULL << SDRAM_CL_SH)          | \
++                               (0x0ULL << SDRAM_RefreshRate_SH) | (0x0ULL << SDRAM_RamSize_SH)     | \
++                               (0x1ULL << SDRAM_RdOffset_SH)    | (0x1ULL << SDRAM_FlightDelay_SH) | \
++                               (0x4ULL << SDRAM_ReadLtncy_1_SH) | SDRAM_ENABLE_ECC | SDRAM_SETUP)
++#endif
++
++#endif /* __ELAN4_XSDRAM_H */
+Index: linux-2.6.5/include/jtag/jtagio.h
+===================================================================
+--- linux-2.6.5.orig/include/jtag/jtagio.h     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/jtag/jtagio.h  2005-05-11 12:10:12.618905216 -0400
+@@ -0,0 +1,106 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "$Id: jtagio.h,v 1.7.8.1 2005/01/27 15:21:47 lee Exp $"
++/*             $Source: /cvs/master/quadrics/jtagmod/jtagio.h,v $*/
++
++
++#ifndef __SYS_JTAGMOD_H
++#define __SYS_JTAGMOD_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#define JTAG_MAX_CHIPS                8
++#define JTAG_MAX_INSTR_LEN    8
++#define JTAG_MAX_BITS           (JTAG_MAX_CHIPS * JTAG_MAX_INSTR_LEN)
++#define JTAG_MAX_DATA_LEN     1024
++
++#define JTAG_BYPASS           0xFF
++
++#define I2C_ADDR_LEN          7                               /* 7 bits of address */
++#define I2C_DATA_LEN          8                               /* 8 bits of data */
++#define I2C_MAX_DATA_LEN      9                               /* and upto 9 bytes worth */
++
++#define BITS_PER_BYTE         8
++#define JTAG_NBYTES(nbits)    (((nbits)+BITS_PER_BYTE-1)/BITS_PER_BYTE)
++#define JTAG_BIT(v, num)      (((v)[(num) / BITS_PER_BYTE] >> ((num) % BITS_PER_BYTE)) & 1)
++#define JTAG_SET_BIT(v, num)  ((v)[(num) / BITS_PER_BYTE] |= (1 << ((num) % BITS_PER_BYTE)))
++#define JTAG_CLR_BIT(v, num)  ((v)[(num) / BITS_PER_BYTE] &= ~(1 << ((num) % BITS_PER_BYTE)))
++
++#define RING_CLOCK_CARD               (0x3D)
++#define RING_CLOCK_SHIFT      (0x3E)
++#define RING_JTAG_LOOPBACK    (0x3F)
++#define RING_MAX              (0x40)
++
++#define RING_QUAD_BIT         (0x40)
++#define RING_I2C_BIT          (0x80)
++
++#define VALID_JTAG_RING(ring) ((ring) < 0x20 || (ring) == RING_JTAG_LOOPBACK)
++#define VALID_I2C_RING(ring)  ((ring) < 0x20 || (ring) == RING_CLOCK_CARD)
++
++
++typedef struct jtag_value
++{
++    u_char    bytes[JTAG_NBYTES(JTAG_MAX_DATA_LEN)];
++} JTAG_VALUE;
++
++/* arguements to JTAG_SHIFT_IR/JTAG_SHIFT_DR */
++typedef struct jtag_reset_args
++{
++    u_int     ring;
++} JTAG_RESET_ARGS;
++
++typedef struct jtag_shift_args
++{
++    u_int     ring;
++    u_int     nbits;
++    u_char     *value;
++} JTAG_SHIFT_ARGS;
++
++typedef struct i2c_args
++{
++    u_int     ring;
++    u_int     device;
++    u_int     reg;
++    u_int     count;
++    u_int     ok;
++    u_char    data[I2C_MAX_DATA_LEN];
++} I2C_ARGS;
++
++/* values for 'ok' - the return value from i2c_xx functions */
++#define I2C_OP_SUCCESS                0
++#define I2C_OP_ERROR          1
++#define I2C_OP_NOT_IDLE               2
++#define I2C_OP_NO_DEVICE      3
++#define I2C_OP_WRITE_TO_BIG   4
++#define I2C_OP_BAD_RESOURCE   5
++
++typedef struct i2c_clock_shift_args
++{
++    u_int     t;
++    u_int     n;
++    u_int     m;
++} I2C_CLOCK_SHIFT_ARGS;
++
++#define JTAG_RESET            _IOWR('j', '0', JTAG_RESET_ARGS)
++#define JTAG_SHIFT_IR         _IOWR('j', '1', JTAG_SHIFT_ARGS)
++#define JTAG_SHIFT_DR         _IOWR('j', '2', JTAG_SHIFT_ARGS)
++
++#define I2C_CLOCK_SHIFT               _IOWR('j', '4', I2C_CLOCK_SHIFT_ARGS)
++#define I2C_WRITE             _IOWR('j', '5', I2C_ARGS)
++#define I2C_READ              _IOWR('j', '6', I2C_ARGS)
++#define I2C_WRITEREG          _IOWR('j', '7', I2C_ARGS)
++#define I2C_READREG           _IOWR('j', '8', I2C_ARGS)
++
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* __SYS_JTAGMOD_H */
+Index: linux-2.6.5/include/linux/init_task.h
+===================================================================
+--- linux-2.6.5.orig/include/linux/init_task.h 2005-02-01 16:56:02.000000000 -0500
++++ linux-2.6.5/include/linux/init_task.h      2005-05-11 12:10:12.618905216 -0400
+@@ -3,6 +3,7 @@
+ #include <linux/file.h>
+ #include <linux/pagg.h>
++#include <linux/ptrack.h>
+ #define INIT_FILES \
+ {                                                     \
+@@ -116,6 +117,7 @@
+       .map_base       = __TASK_UNMAPPED_BASE,                         \
+       .io_wait        = NULL,                                         \
+       INIT_TASK_PAGG(tsk)                                             \
++      INIT_TASK_PTRACK(tsk),                                          \
+ }
+Index: linux-2.6.5/include/linux/ioproc.h
+===================================================================
+--- linux-2.6.5.orig/include/linux/ioproc.h    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/linux/ioproc.h 2005-05-11 12:10:12.619905064 -0400
+@@ -0,0 +1,271 @@
++/* -*- linux-c -*-
++ *
++ *    Copyright (C) 2002-2004 Quadrics Ltd.
++ *
++ *    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.
++ *
++ *    This program is distributed in the hope that it will be useful,
++ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *    GNU General Public License for more details.
++ *
++ *    You should have received a copy of the GNU General Public License
++ *    along with this program; if not, write to the Free Software
++ *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ *
++ *
++ */
++
++/*
++ * Callbacks for IO processor page table updates.
++ */
++
++#ifndef __LINUX_IOPROC_H__
++#define __LINUX_IOPROC_H__
++
++#include <linux/sched.h>
++#include <linux/mm.h>
++
++typedef struct ioproc_ops {
++      struct ioproc_ops *next;
++      void *arg;
++
++      void (*release)(void *arg, struct mm_struct *mm);
++      void (*sync_range)(void *arg, struct vm_area_struct *vma, unsigned long start, unsigned long end);
++      void (*invalidate_range)(void *arg, struct vm_area_struct *vma, unsigned long start, unsigned long end);
++      void (*update_range)(void *arg, struct vm_area_struct *vma, unsigned long start, unsigned long end);
++
++      void (*change_protection)(void *arg, struct vm_area_struct *vma, unsigned long start, unsigned long end, pgprot_t newprot);
++
++      void (*sync_page)(void *arg, struct vm_area_struct *vma, unsigned long address);
++      void (*invalidate_page)(void *arg, struct vm_area_struct *vma, unsigned long address);
++      void (*update_page)(void *arg, struct vm_area_struct *vma, unsigned long address);
++
++} ioproc_ops_t;
++
++/* IOPROC Registration
++ * 
++ * Called by the IOPROC device driver to register its interest in page table
++ * changes for the process associated with the supplied mm_struct
++ *
++ * The caller should first allocate and fill out an ioproc_ops structure with 
++ * the function pointers initialised to the device driver specific code for
++ * each callback. If the device driver doesn't have code for a particular 
++ * callback then it should set the function pointer to be NULL.
++ * The ioproc_ops arg parameter will be passed unchanged as the first argument
++ * to each callback function invocation.
++ *
++ * The ioproc registration is not inherited across fork() and should be called
++ * once for each process that the IOPROC device driver is interested in.
++ *
++ * Must be called holding the mm->page_table_lock
++ */
++extern int ioproc_register_ops(struct mm_struct *mm, struct ioproc_ops *ip);
++
++
++/* IOPROC De-registration
++ * 
++ * Called by the IOPROC device driver when it is no longer interested in page 
++ * table changes for the process associated with the supplied mm_struct
++ *
++ * Normally this is not needed to be called as the ioproc_release() code will
++ * automatically unlink the ioproc_ops struct from the mm_struct as the
++ * process exits
++ *
++ * Must be called holding the mm->page_table_lock
++ */
++extern int ioproc_unregister_ops(struct mm_struct *mm, struct ioproc_ops *ip);
++
++#ifdef CONFIG_IOPROC
++
++/* IOPROC Release
++ *
++ * Called during exit_mmap() as all vmas are torn down and unmapped.
++ *
++ * Also unlinks the ioproc_ops structure from the mm list as it goes.
++ *
++ * No need for locks as the mm can no longer be accessed at this point
++ *
++ */
++static inline void 
++ioproc_release(struct mm_struct *mm)
++{
++      struct ioproc_ops *cp;
++
++      while ((cp = mm->ioproc_ops) != NULL) {
++              mm->ioproc_ops = cp->next;
++        
++              if (cp->release)
++                      cp->release(cp->arg, mm);
++      }
++}
++
++/* IOPROC SYNC RANGE
++ *
++ * Called when a memory map is synchronised with its disk image i.e. when the 
++ * msync() syscall is invoked. Any future read or write to the associated 
++ * pages by the IOPROC should cause the page to be marked as referenced or 
++ * modified.
++ *
++ * Called holding the mm->page_table_lock
++ */
++static inline void 
++ioproc_sync_range(struct vm_area_struct *vma, unsigned long start, unsigned long end)
++{
++      struct ioproc_ops *cp;
++
++      for (cp = vma->vm_mm->ioproc_ops; cp; cp = cp->next)
++              if (cp->sync_range)
++                      cp->sync_range(cp->arg, vma, start, end);
++}
++
++/* IOPROC INVALIDATE RANGE
++ *
++ * Called whenever a valid PTE is unloaded e.g. when a page is unmapped by the
++ * user or paged out by the kernel. 
++ *
++ * After this call the IOPROC must not access the physical memory again unless
++ * a new translation is loaded.
++ *
++ * Called holding the mm->page_table_lock
++ */
++static inline void 
++ioproc_invalidate_range(struct vm_area_struct *vma, unsigned long start, unsigned long end)
++{
++      struct ioproc_ops *cp;
++      
++      for (cp = vma->vm_mm->ioproc_ops; cp; cp = cp->next)
++              if (cp->invalidate_range)
++                      cp->invalidate_range(cp->arg, vma, start, end);
++}
++
++/* IOPROC UPDATE RANGE
++ *
++ * Called whenever a valid PTE is loaded e.g. mmaping memory, moving the brk 
++ * up, when breaking COW or faulting in an anonymous page of memory.
++ *
++ * These give the IOPROC device driver the opportunity to load translations 
++ * speculatively, which can improve performance by avoiding device translation
++ * faults.
++ *
++ * Called holding the mm->page_table_lock
++ */
++static inline void 
++ioproc_update_range(struct vm_area_struct *vma, unsigned long start, unsigned long end)
++{
++      struct ioproc_ops *cp;
++
++      for (cp = vma->vm_mm->ioproc_ops; cp; cp = cp->next)
++              if (cp->update_range)
++                      cp->update_range(cp->arg, vma, start, end);
++}
++
++
++/* IOPROC CHANGE PROTECTION
++ *
++ * Called when the protection on a region of memory is changed i.e. when the 
++ * mprotect() syscall is invoked.
++ *
++ * The IOPROC must not be able to write to a read-only page, so if the 
++ * permissions are downgraded then it must honour them. If they are upgraded 
++ * it can treat this in the same way as the ioproc_update_[range|sync]() calls
++ *
++ * Called holding the mm->page_table_lock
++ */
++static inline void 
++ioproc_change_protection(struct vm_area_struct *vma, unsigned long start, unsigned long end, pgprot_t newprot)
++{
++      struct ioproc_ops *cp;
++
++      for (cp = vma->vm_mm->ioproc_ops; cp; cp = cp->next)
++              if (cp->change_protection)
++                      cp->change_protection(cp->arg, vma, start, end, newprot);
++}
++
++/* IOPROC SYNC PAGE
++ *
++ * Called when a memory map is synchronised with its disk image i.e. when the 
++ * msync() syscall is invoked. Any future read or write to the associated page
++ * by the IOPROC should cause the page to be marked as referenced or modified.
++ *
++ * Not currently called as msync() calls ioproc_sync_range() instead
++ *
++ * Called holding the mm->page_table_lock
++ */
++static inline void 
++ioproc_sync_page(struct vm_area_struct *vma, unsigned long addr)
++{
++      struct ioproc_ops *cp;
++
++      for (cp = vma->vm_mm->ioproc_ops; cp; cp = cp->next)
++              if (cp->sync_page)
++                      cp->sync_page(cp->arg, vma, addr);
++}
++
++/* IOPROC INVALIDATE PAGE
++ *
++ * Called whenever a valid PTE is unloaded e.g. when a page is unmapped by the
++ * user or paged out by the kernel. 
++ *
++ * After this call the IOPROC must not access the physical memory again unless
++ * a new translation is loaded.
++ *
++ * Called holding the mm->page_table_lock
++ */
++static inline void 
++ioproc_invalidate_page(struct vm_area_struct *vma, unsigned long addr)
++{
++      struct ioproc_ops *cp;
++
++      for (cp = vma->vm_mm->ioproc_ops; cp; cp = cp->next)
++              if (cp->invalidate_page)
++                      cp->invalidate_page(cp->arg, vma, addr);
++}
++
++/* IOPROC UPDATE PAGE
++ *
++ * Called whenever a valid PTE is loaded e.g. mmaping memory, moving the brk 
++ * up, when breaking COW or faulting in an anoymous page of memory.
++ *
++ * These give the IOPROC device the opportunity to load translations 
++ * speculatively, which can improve performance by avoiding device translation
++ * faults.
++ *
++ * Called holding the mm->page_table_lock
++ */
++static inline void 
++ioproc_update_page(struct vm_area_struct *vma, unsigned long addr)
++{
++      struct ioproc_ops *cp;
++
++      for (cp = vma->vm_mm->ioproc_ops; cp; cp = cp->next)
++              if (cp->update_page)
++                      cp->update_page(cp->arg, vma, addr);
++}
++
++#else
++
++/* ! CONFIG_IOPROC so make all hooks empty */
++
++#define ioproc_release(mm)                    do { } while (0)
++
++#define ioproc_sync_range(vma,start,end)              do { } while (0)
++
++#define ioproc_invalidate_range(vma, start,end)       do { } while (0)
++
++#define ioproc_update_range(vma, start, end)  do { } while (0)
++
++#define ioproc_change_protection(vma, start, end, prot)       do { } while (0)
++
++#define ioproc_sync_page(vma, addr)           do { } while (0)
++
++#define ioproc_invalidate_page(vma, addr)     do { } while (0)
++
++#define ioproc_update_page(vma, addr)         do { } while (0)
++
++#endif /* CONFIG_IOPROC */
++
++#endif /* __LINUX_IOPROC_H__ */
+Index: linux-2.6.5/include/linux/ptrack.h
+===================================================================
+--- linux-2.6.5.orig/include/linux/ptrack.h    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/linux/ptrack.h 2005-05-11 12:10:12.619905064 -0400
+@@ -0,0 +1,65 @@
++/*
++ *    Copyright (C) 2000  Regents of the University of California
++ *
++ *    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.
++ *
++ *    This program is distributed in the hope that it will be useful,
++ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *    GNU General Public License for more details.
++ *
++ *    You should have received a copy of the GNU General Public License
++ *    along with this program; if not, write to the Free Software
++ *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ *
++ * Derived from exit_actn.c by
++ *    Copyright (C) 2003 Quadrics Ltd.
++ *
++ */
++#ifndef __LINUX_PTRACK_H
++#define __LINUX_PTRACK_H
++
++/* 
++ * Process tracking - this allows a module to keep track of processes
++ * in order that it can manage all tasks derived from a single process.
++ */
++
++#define PTRACK_PHASE_CLONE    1
++#define PTRACK_PHASE_CLONE_FAIL       2
++#define PTRACK_PHASE_EXEC     3
++#define PTRACK_PHASE_EXIT             4
++
++#define PTRACK_FINISHED               0
++#define PTRACK_INNHERIT               1
++#define PTRACK_DENIED         2
++
++#ifdef CONFIG_PTRACK
++
++typedef int (*ptrack_callback_t)(void *arg, int phase, struct task_struct *child);
++
++struct ptrack_desc {
++       struct list_head        link;
++       ptrack_callback_t       callback;
++       void                   *arg;
++};
++
++extern int     ptrack_register (ptrack_callback_t callback, void *arg);
++extern void    ptrack_deregister (ptrack_callback_t callback, void *arg);
++extern int     ptrack_registered (ptrack_callback_t callback, void *arg);
++
++extern int     ptrack_call_callbacks (int phase, struct task_struct *child);
++
++#define INIT_TASK_PTRACK(tsk) \
++      .ptrack_list = LIST_HEAD_INIT(tsk.ptrack_list)
++
++#else
++#define ptrack_call_callbacks (phase, child) (0)
++
++#define INIT_TASK_PTRACK(tsk)
++
++#endif
++
++#endif /* __LINUX_PTRACK_H */
+Index: linux-2.6.5/include/linux/sched.h
+===================================================================
+--- linux-2.6.5.orig/include/linux/sched.h     2005-02-01 16:56:07.000000000 -0500
++++ linux-2.6.5/include/linux/sched.h  2005-05-11 12:10:12.620904912 -0400
+@@ -188,6 +188,9 @@
+ extern int max_timeslice, min_timeslice;
+ struct namespace;
++#ifdef CONFIG_IOPROC
++struct ioproc_ops;
++#endif
+ /* Maximum number of active map areas.. This is a random (large) number */
+ #define DEFAULT_MAX_MAP_COUNT 65536
+@@ -241,6 +244,15 @@
+       struct kioctx           default_kioctx;
+       unsigned long hiwater_rss, hiwater_vm;
++
++#ifdef CONFIG_IOPROC
++      /* hooks for io devices with advanced RDMA capabilities */
++      struct ioproc_ops       *ioproc_ops;
++#endif
++#ifdef CONFIG_PTRACK
++/* process tracking callback */
++      struct list_head ptrack_list;
++#endif
+ };
+ extern int mmlist_nr;
+@@ -601,6 +613,10 @@
+       struct rw_semaphore pagg_sem;
+ #endif
++#ifdef CONFIG_PTRACK
++/* process tracking callback */
++      struct list_head ptrack_list;
++#endif
+ };
+ static inline pid_t process_group(struct task_struct *tsk)
+Index: linux-2.6.5/include/qsnet/autoconf.h
+===================================================================
+--- linux-2.6.5.orig/include/qsnet/autoconf.h  2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/qsnet/autoconf.h       2005-05-11 12:17:53.578828656 -0400
+@@ -0,0 +1,38 @@
++/*
++ *    Copyright (c) 2004 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ * NOTE: This file has been automatically generated:
++ *       node   : milano
++ *       kernel : /src/linux/2.6/linux-2.6.5
++ *       date   : Wed May 11 12:17:34 EDT 2005
++ *
++ */
++
++#include <linux/version.h>
++#undef        NO_RMAP
++#undef        AC
++#undef        NO_O1_SCHED
++#undef        NO_NPTL
++#undef        NO_ABI
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
++#define       PROCESS_ACCT
++#endif
++#undef        RSS_ATOMIC
++#define       NO_COPROC
++#undef        NO_IOPROC
++#undef        NO_PTRACK
++#define       NO_PANIC_NOTIFIER
++#undef        NO_SHM_CLEANUP
++#undef        NO_PDE
++
++
++#define       CONFIG_EIP
++#define       CONFIG_ELAN
++#define       CONFIG_ELAN3
++#define       CONFIG_ELAN4
++#define       CONFIG_EP
++#define       CONFIG_JTAG
++#define       CONFIG_QSNET
++#define       CONFIG_RMS
+Index: linux-2.6.5/include/qsnet/condvar.h
+===================================================================
+--- linux-2.6.5.orig/include/qsnet/condvar.h   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/qsnet/condvar.h        2005-05-11 12:10:12.621904760 -0400
+@@ -0,0 +1,140 @@
++/*
++ *    Copyright (C) 2000  Regents of the University of California
++ *
++ *    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.
++ *
++ *    This program is distributed in the hope that it will be useful,
++ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *    GNU General Public License for more details.
++ *
++ *    You should have received a copy of the GNU General Public License
++ *    along with this program; if not, write to the Free Software
++ *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ *
++ */
++
++#if   !defined(_LINUX_CONDVAR_H)
++#define       _LINUX_CONDVAR_H
++
++#if   defined(__KERNEL__)
++
++#include <linux/list.h>
++#include <qsnet/debug.h>
++
++#define CV_RET_SIGPENDING     0
++#define CV_RET_TIMEOUT                (-1)
++#define CV_RET_NORMAL         1
++
++struct kcondvar_task {
++      struct task_struct      *task;          /* need to wrap task in this */
++      struct list_head        list;           /*   to thread as a list */
++      int                     blocked;
++};
++
++typedef struct {
++      struct list_head        task_list;      /* list of kcondvar_task's */
++} kcondvar_t;
++
++#define kcondvar_wait(c,l,fl)                 debug_kcondvar_wait(c, l, fl, 0,  TASK_UNINTERRUPTIBLE)
++#define kcondvar_waitsig(c,l,fl)              debug_kcondvar_wait(c, l, fl, 0,  TASK_INTERRUPTIBLE)
++#define kcondvar_timedwait(c,l,fl,to)         debug_kcondvar_wait(c, l, fl, to, TASK_UNINTERRUPTIBLE)
++#define kcondvar_timedwaitsig(c,l,fl,to)      debug_kcondvar_wait(c, l, fl, to, TASK_INTERRUPTIBLE)
++#define kcondvar_wakeupone(c,l)                       kcondvar_wakeup(c, l, 0)
++#define kcondvar_wakeupall(c,l)                       kcondvar_wakeup(c, l, 1)
++ 
++extern __inline__ void
++kcondvar_init(kcondvar_t *c)
++{
++      INIT_LIST_HEAD(&c->task_list);
++}
++
++extern __inline__ void
++kcondvar_destroy(kcondvar_t *c)
++{
++      ASSERT(list_empty(&c->task_list));
++}
++
++/*
++ * We thread a struct kcondvar_task, allocated on the stack, onto the kcondvar_t's
++ * task_list, and take it off again when we wake up.
++ */
++extern __inline__ int
++debug_kcondvar_wait(kcondvar_t *c, spinlock_t *l, unsigned long *fl, long tmo, int state)
++{
++      struct kcondvar_task cvt;
++      int ret = CV_RET_NORMAL;
++
++      ASSERT(!in_interrupt());                /* we can block */
++      ASSERT(SPINLOCK_HELD(l));               /* enter holding lock */
++
++      cvt.task = current;
++      cvt.blocked = 1;
++      list_add(&cvt.list, &c->task_list);
++      do {
++             /* Note: we avoid using TASK_UNINTERRUPTIBLE here because avenrun()
++              * (linux/kernel/timer.c:calc_load())
++              * computation treats it like TASK_RUNNABLE hence creates false high
++              * load averages when we create kernel threads.
++              * The cvt.blocked flag distinguishes a signal wakeup from a kcondvar_wakeup.
++              *
++              * However, if we do take a signal we could end up busily spinning here, if
++              * we ignore it (state == TASK_UNINTERRUPTIBLE) so once we see a signal
++              * pending we do sleep TASK_UNINTERRUPTIBLE to stop a busy spin.
++              * I have now blocked all signals for kernel threads to prevent this
++              * happening but other users of kcondvar_wait may still hit this spin.
++              */
++              set_current_state (signal_pending(current) ? state : TASK_INTERRUPTIBLE);
++
++              if (fl)
++                  spin_unlock_irqrestore(l, *fl);
++              else
++                  spin_unlock(l);
++              if (tmo) {
++                      if (tmo <= jiffies || !schedule_timeout(tmo - jiffies))
++                              ret = CV_RET_TIMEOUT;
++              } else
++                      schedule();
++              if (fl)
++                  spin_lock_irqsave (l, *fl);
++              else
++                  spin_lock(l);
++              
++              /* signal_pending - Only exit the loop if the user was waiting TASK_INTERRUPTIBLE */
++              if ((state == TASK_INTERRUPTIBLE) && signal_pending(current))
++                      ret = CV_RET_SIGPENDING;
++
++      } while (cvt.blocked && ret == CV_RET_NORMAL);
++      list_del(&cvt.list);
++
++      /* Reset task state in case we didn't sleep above */
++      set_current_state (TASK_RUNNING);
++
++      return ret;                             /* return holding lock */
++}
++
++extern __inline__ void
++kcondvar_wakeup(kcondvar_t *c, spinlock_t *l, int wakeall)
++{
++      struct list_head *lp;
++      struct kcondvar_task *cvtp;
++
++      ASSERT(SPINLOCK_HELD(l));                       /* already holding lock */
++      for (lp = c->task_list.next; lp != &c->task_list; lp = lp->next) {
++              cvtp = list_entry(lp, struct kcondvar_task, list);
++              if (cvtp->blocked) {
++                      cvtp->blocked = 0;
++                      /* wake_up_process added to kernel/ksyms.c */
++                      wake_up_process(cvtp->task); 
++                      if (!wakeall)
++                              break;
++              }
++      }
++}                                             /* return still holding lock */
++
++
++#endif /* __KERNEL__ */
++#endif /* _LINUX_CONDVAR_H */
+Index: linux-2.6.5/include/qsnet/config.h
+===================================================================
+--- linux-2.6.5.orig/include/qsnet/config.h    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/qsnet/config.h 2005-05-11 12:10:12.622904608 -0400
+@@ -0,0 +1,195 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef _QSNET_CONFIG_H
++#define _QSNET_CONFIG_H
++
++#ident "$Id: config.h,v 1.23 2003/07/24 21:31:19 robin Exp $"
++/*      $Source: /cvs/master/quadrics/qsnet/config.h,v $*/
++
++
++/*
++ * QSNET standard defines :
++ *
++ *   Target operating system defines
++ *            SOLARIS
++ *            TRU64UNIX/DIGITAL_UNIX
++ *            LINUX
++ *
++ *   Target processor defines
++ *            SPARC
++ *            ALPHA
++ *            I386
++ *            IA64
++ *            X86_64
++ *
++ *   Byte order defines
++ *            __LITTLE_ENDIAN__
++ *            __BIG_ENDIAN__
++ *
++ *   Data size defines
++ *            _LP64                   - LP64 - long/pointer is 64 bits
++ *            _ILP32                  - LP32 - long/pointer is 32 bits
++ *
++ *   Elan defines for main processor
++ *            __MAIN_LITTLE_ENDIAN__  - main byte order (for thread code)
++ *            __MAIN_BIG_ENDIAN__
++ *            _MAIN_LP64              - main long size (for thread code)
++ *            _MAIN_ILP32
++ *
++ *   Compiling for kernel (defined in makefile)
++ *            _KERNEL
++ *
++ */
++
++#if defined(__LP64__) && !defined(_LP64)
++#  define _LP64
++#endif
++
++#if defined(__arch64__) && !defined(_LP64) && !defined(_ILP32)
++#  define _LP64
++#endif
++
++#if defined(__alpha__) && !defined(_LP64) && !defined(_ILP32)
++#  define _LP64
++#endif
++
++#if !defined(__arch64__) && !defined(_ILP32) && !defined(_LP64)
++#  define _ILP32
++#endif
++
++#if defined(__ELAN__) || defined(__ELAN3__)
++
++#define __LITTLE_ENDIAN__
++
++#if defined(__host_solaris) && defined(__host_sparc)
++#define SOLARIS
++#define SPARC
++#define SOLARIS_SPARC
++#define _MAIN_ILP32
++#define __MAIN_BIG_ENDIAN__
++
++#elif defined(__host_osf)
++#define TRU64UNIX
++#define DIGITAL_UNIX
++#define ALPHA
++#define _MAIN_LP64
++#define __MAIN_LITTLE_ENDIAN__
++
++#elif defined(__host_linux) && defined(__host_alpha)
++#define LINUX
++#define ALPHA
++#define LINUX_ALPHA
++#define _MAIN_LP64
++#define __MAIN_LITTLE_ENDIAN__
++
++#elif defined(__host_linux) && defined(__host_sparc)
++#define LINUX
++#define SPARC
++#define LINUX_SPARC
++#define __MAIN_BIG_ENDIAN__
++#ifdef __KERNEL__
++#  define _MAIN_LP64
++#else
++#  define _MAIN_ILP32
++#endif
++
++#elif defined(__host_linux) && defined(__host_i386)
++#define LINUX
++#define I386
++#define LINUX_I386
++#define _MAIN_ILP32
++#define __MAIN_LITTLE_ENDIAN__
++
++#elif defined(__host_linux) && defined(__host_ia64)
++#define LINUX
++#define IA64
++#define LINUX_IA64
++#define _MAIN_LP64
++#define __MAIN_LITTLE_ENDIAN__
++
++#elif defined(__host_linux) && defined(__host_x86_64)
++#define LINUX
++#define X86_64
++#define LINUX_X86_64
++#define _MAIN_LP64
++#define __MAIN_LITTLE_ENDIAN__
++
++#else
++#error Cannot determine operating system/processor architecture.
++#endif
++
++#else /* !defined(__ELAN3__) */
++
++#if (defined(sun) || defined(__sun)) && defined(sparc) && !defined(__sparcv9) /* Sun Solaris 5.6 */
++#define SOLARIS
++#define SPARC
++#define SOLARIS_SPARC
++#ifndef __BIG_ENDIAN__
++#define __BIG_ENDIAN__
++#endif 
++
++#elif (defined(sun) || defined(__sun)) && defined(sparc) && defined(__sparcv9)        /* Sun Solaris 5.7 */
++#define SOLARIS
++#define SPARC
++#define SOLARIS_SPARC
++#define __BIG_ENDIAN__
++
++#elif defined(__osf__) && defined(__alpha)                                    /* Digital Unix */
++#define TRU64UNIX
++#define DIGITAL_UNIX
++#define ALPHA
++#define __LITTLE_ENDIAN__
++
++#elif (defined(linux) || defined(__linux__)) && defined(__alpha)              /* Linux Alpha */
++
++#define LINUX
++#define ALPHA
++#define LINUX_ALPHA
++#define __LITTLE_ENDIAN__
++
++#elif (defined(linux) || defined(__linux__)) && defined(__sparc)              /* Linux Sparc */
++
++#define LINUX
++#define SPARC
++#define LINUX_SPARC
++#define __BIG_ENDIAN__
++
++#elif (defined(linux) || defined(__linux__)) && defined(__i386)                       /* Linux i386 */
++
++#define LINUX
++#define I386
++#define LINUX_I386
++#define __LITTLE_ENDIAN__
++
++#elif (defined(linux) || defined(__linux__)) && defined(__ia64)                       /* Linux ia64 */
++
++#define LINUX
++#define IA64
++#define LINUX_IA64
++#define __LITTLE_ENDIAN__
++
++#elif (defined(linux) || defined(__linux__)) && defined(__x86_64)                     /* Linux x86_64 */
++
++#define LINUX
++#define X86_64
++#define LINUX_X86_64
++#define __LITTLE_ENDIAN__
++
++#elif defined(__QNXNTO__)
++#define QNX
++#define I386
++#define __LITTLE_ENDIAN__
++#else
++#error Cannot determine operating system/processor architecture.
++#endif
++
++#endif
++
++#include <qsnet/workarounds.h>
++
++#endif /* _QSNET_CONFIG_H */
+Index: linux-2.6.5/include/qsnet/crwlock.h
+===================================================================
+--- linux-2.6.5.orig/include/qsnet/crwlock.h   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/qsnet/crwlock.h        2005-05-11 12:10:12.622904608 -0400
+@@ -0,0 +1,207 @@
++/* 
++ *    Copyright (C) 2000  Regents of the University of California
++ *
++ *    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.
++ *
++ *    This program is distributed in the hope that it will be useful,
++ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *    GNU General Public License for more details.
++ *
++ *    You should have received a copy of the GNU General Public License
++ *    along with this program; if not, write to the Free Software
++ *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ *
++ */
++
++/*
++ *    Complex - Reader/Writer locks
++ *    Ref: "UNIX Systems for Modern Architectures", by Curt Schimmel, 
++ *    sec 11.6.3. 
++ *
++ *    This implementation is based on semaphores and may not be called from 
++ *    interrupt handlers.
++ *
++ */
++
++#if   !defined(_LINUX_RWLOCK_H)
++#define       _LINUX_RWLOCK_H
++
++#if   defined(__KERNEL__)
++
++typedef enum { RD, WRT, ANY } crwlock_type_t;
++
++#define crwlock_write_held(l) debug_crwlock_held(l, WRT, __BASE_FILE__,__LINE__)
++#define crwlock_read_held(l) debug_crwlock_held(l, RD, __BASE_FILE__, __LINE__)
++#define crwlock_held(l)      debug_crwlock_held(l, ANY, __BASE_FILE__, __LINE__)
++
++#define crwlock_read(l)            debug_crwlock_read(l, __BASE_FILE__, __LINE__)
++#define crwlock_write(l)     debug_crwlock_write(l, __BASE_FILE__, __LINE__)
++#define crwlock_done(l)      debug_crwlock_done(l, __BASE_FILE__, __LINE__)
++
++#if     defined(DEBUG_RWLOCK) && defined(__alpha__) && !defined(DEBUG_SPINLOCK)
++#define DEBUG_SPINLOCK
++#endif
++
++#include <linux/spinlock.h>
++#include <asm/semaphore.h>
++#include <qsnet/debug.h>
++#include <qsnet/mutex.h>
++#include <linux/version.h>
++
++#if   !defined(DEBUG_SPINLOCK)
++#define debug_spin_lock(lock, file, line)       spin_lock(lock)
++#endif
++
++typedef struct {
++        spinlock_t            m_lock;         /* protects cnt fields below */
++        int                     m_rdcnt;        /* # of rdrs in crit section */
++        int                     m_wrcnt;        /* # of wrtrs in crit section */
++        int                     m_rdwcnt;       /* # of waiting readers */
++        int                     m_wrwcnt;       /* # of waiting writers */
++        struct semaphore        m_rdwait;       /* sema where readers wait */
++        struct semaphore        m_wrwait;       /* sema where writers wait */
++        pid_t                 m_wrholder;     /* task holding write lock */
++} crwlock_t;
++ 
++extern __inline__ void 
++crwlock_init(crwlock_t *l)
++{
++      l->m_lock = SPIN_LOCK_UNLOCKED;
++#if   LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
++      l->m_rdwait = MUTEX_LOCKED;
++      l->m_wrwait = MUTEX_LOCKED;
++#else
++      sema_init(&l->m_rdwait,0);
++      sema_init(&l->m_wrwait,0);
++#endif
++      l->m_rdcnt = l->m_wrcnt = l->m_rdwcnt = l->m_wrwcnt = 0;
++      l->m_wrholder = PID_NONE;
++}
++
++extern __inline__ void 
++crwlock_destroy(crwlock_t *l)
++{
++      ASSERT(l->m_rdcnt == 0 && l->m_wrcnt == 0);
++}
++
++/*
++ * If a writer has the lock presently or there are writers waiting,
++ * then we have to wait.
++ */
++extern __inline__ void 
++debug_crwlock_read(crwlock_t *l, char *file, int line)
++{
++      ASSERT(!in_interrupt());
++      spin_lock(&l->m_lock);
++      if (l->m_wrcnt || l->m_wrwcnt) {
++              l->m_rdwcnt++;
++              spin_unlock(&l->m_lock);
++              down(&l->m_rdwait); /* P */
++      } else {
++              l->m_rdcnt++;
++              spin_unlock(&l->m_lock);
++      }
++}
++
++/*
++ * If we're the last reader, and a writer is waiting,
++ * then let the writer go now.
++ */
++/* private */
++extern __inline__ void 
++debug_crwlock_read_done(crwlock_t *l, char *file, int line)
++{
++      spin_lock(&l->m_lock);
++      l->m_rdcnt--;
++      if (l->m_wrwcnt && l->m_rdcnt == 0) {
++              l->m_wrcnt = 1;
++              l->m_wrwcnt--;
++              spin_unlock(&l->m_lock);
++              up(&l->m_wrwait); /* V */       
++              return;
++      }
++      spin_unlock(&l->m_lock);
++}
++
++extern __inline__ void 
++debug_crwlock_write(crwlock_t *l, char *file, int line)
++{
++      ASSERT(!in_interrupt());
++      spin_lock(&l->m_lock);
++      if (l->m_wrcnt || l->m_rdcnt) {         /* block if lock is in use */
++              l->m_wrwcnt++;
++              spin_unlock(&l->m_lock);
++              down(&l->m_wrwait); /* P */
++      } else {                                /* lock is not in use */
++              l->m_wrcnt = 1;
++              spin_unlock(&l->m_lock);
++      }
++      l->m_wrholder = current->pid;
++}
++
++/* private */
++extern __inline__ void
++debug_crwlock_write_done(crwlock_t *l, char *file, int line)
++{
++      int rdrs;
++
++      spin_lock(&l->m_lock);
++      l->m_wrholder = PID_NONE;
++      if (l->m_rdwcnt) {                      /* let any readers go first */
++              l->m_wrcnt = 0;
++              rdrs = l->m_rdwcnt;
++              l->m_rdcnt = rdrs;
++              l->m_rdwcnt = 0;
++              spin_unlock(&l->m_lock);
++              while (rdrs--)
++                      up(&l->m_rdwait); /* V */
++      } else if (l->m_wrwcnt) {               /* or let any writer go */
++              l->m_wrwcnt--;
++              spin_unlock(&l->m_lock);
++              up(&l->m_wrwait); /* V */
++      } else {                                /* nobody waiting, unlock */
++              l->m_wrcnt = 0;
++              spin_unlock(&l->m_lock);
++      }
++}
++
++extern __inline__ void
++debug_crwlock_done(crwlock_t *l, char *file, int line)
++{
++      if (l->m_wrholder == current->pid)
++              debug_crwlock_write_done(l, file, line);
++      else
++              debug_crwlock_read_done(l, file, line);
++}
++
++/*
++ * Return nonzero if lock is held
++ */
++extern __inline__ int  
++debug_crwlock_held(crwlock_t *l, crwlock_type_t t, char *file, int line)
++{
++      int res;
++
++      spin_lock(&l->m_lock);
++      switch(t) {
++              case RD:
++                      res = l->m_rdcnt;
++                      break;
++              case WRT:
++                      res = l->m_wrcnt;
++                      break;
++              case ANY:
++                      res = l->m_wrcnt + l->m_rdcnt;
++                      break;
++      }
++      spin_unlock(&l->m_lock);
++
++      return res;
++}
++
++#endif /* __KERNEL__ */
++#endif /* _LINUX_RWLOCK_H */
+Index: linux-2.6.5/include/qsnet/ctrl_linux.h
+===================================================================
+--- linux-2.6.5.orig/include/qsnet/ctrl_linux.h        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/qsnet/ctrl_linux.h     2005-05-11 12:10:12.622904608 -0400
+@@ -0,0 +1,37 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __QSNET_CTRL_LINUX_H
++#define __QSNET_CTRL_LINUX_H
++
++#ident "$Id: ctrl_linux.h,v 1.3 2003/03/26 09:32:03 mike Exp $"
++/*      $Source: /cvs/master/quadrics/qsnet/ctrl_linux.h,v $*/
++
++#define QSNETIO_USER_BASE             0x40
++
++#define QSNETIO_DEBUG_DUMP            _IO   ('e', QSNETIO_USER_BASE + 0)
++
++typedef struct qsnetio_debug_buffer_struct
++{
++      caddr_t addr; 
++      size_t  len;
++} QSNETIO_DEBUG_BUFFER_STRUCT;
++#define QSNETIO_DEBUG_BUFFER          _IOWR ('e', QSNETIO_USER_BASE + 1, QSNETIO_DEBUG_BUFFER_STRUCT)
++
++typedef struct qsnetio_debug_kmem_struct
++{
++      void *handle;
++} QSNETIO_DEBUG_KMEM_STRUCT;
++#define QSNETIO_DEBUG_KMEM            _IOWR   ('e', QSNETIO_USER_BASE + 2, QSNETIO_DEBUG_KMEM_STRUCT)
++
++#endif /* __QSNET_CTRL_LINUX_H */
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.6.5/include/qsnet/debug.h
+===================================================================
+--- linux-2.6.5.orig/include/qsnet/debug.h     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/qsnet/debug.h  2005-05-11 12:10:12.623904456 -0400
+@@ -0,0 +1,68 @@
++/*
++ *    Copyright (C) 2000  Regents of the University of California
++ *
++ *    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.
++ *
++ *    This program is distributed in the hope that it will be useful,
++ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *    GNU General Public License for more details.
++ *
++ *    You should have received a copy of the GNU General Public License
++ *    along with this program; if not, write to the Free Software
++ *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ *
++ */
++#ifndef _QSNET_DEBUG_H
++#define _QSNET_DEBUG_H
++
++#if defined(DIGITAL_UNIX) 
++#include <kern/assert.h>
++#elif defined(LINUX)
++extern int qsnet_assfail (char *ex, const char *func, char *file, int line);
++
++#define ASSERT(EX)    do { \
++      if (!(EX) && qsnet_assfail (#EX, __FUNCTION__, __BASE_FILE__, __LINE__)) { \
++              BUG(); \
++      } \
++} while (0)
++#endif /* DIGITAL_UNIX */
++
++/* debug.c */
++extern void qsnet_debug_init(void);
++extern void qsnet_debug_fini(void);
++extern void qsnet_debug_disable(int);
++extern void qsnet_debug_alloc(void);
++
++#define QSNET_DEBUG_BUFFER  ((unsigned int)(0x01))
++#define QSNET_DEBUG_CONSOLE ((unsigned int)(0x02))
++#define QSNET_DEBUG_BUF_CON ( QSNET_DEBUG_BUFFER | QSNET_DEBUG_CONSOLE )
++
++#ifdef __GNUC__
++extern void qsnet_debugf      (unsigned int mode, char *fmt, ...)
++      __attribute__ ((format (printf,2,3)));
++extern void kqsnet_debugf      (char *fmt, ...)
++      __attribute__ ((format (printf,1,2)));
++#else
++extern void qsnet_debugf      (unsigned int mode, char *fmt, ...);
++extern void kqsnet_debugf     (char *fmt, ...);
++#endif
++extern void qsnet_vdebugf     (unsigned int mode, char * prefix, char *fmt,  va_list ap);
++extern int  qsnet_debug_buffer(caddr_t ubuffer, int len);
++extern int  qsnet_debug_dump  (void);
++extern int  qsnet_debug_kmem  (void *handle);
++
++extern void qsnet_debug_buffer_on(void);
++extern void qsnet_debug_buffer_clear(void);
++extern void qsnet_debug_buffer_mark(char *str);
++
++#endif /* _QSNET_DEBUG_H */
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.6.5/include/qsnet/fence.h
+===================================================================
+--- linux-2.6.5.orig/include/qsnet/fence.h     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/qsnet/fence.h  2005-05-11 12:10:12.623904456 -0400
+@@ -0,0 +1,178 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++/* $Id: fence.h,v 1.21.6.4 2004/11/23 14:34:45 addy Exp $ */
++/*             $Source: /cvs/master/quadrics/qsnet/fence.h,v $*/
++
++#ifndef _CONFIG_FENCE_H
++#define _CONFIG_FENCE_H
++
++#ident "$Id: fence.h,v 1.21.6.4 2004/11/23 14:34:45 addy Exp $"
++
++#ifdef        __cplusplus
++extern "C" {
++#endif
++
++#if defined(__ELAN__) || defined(__ELAN3__)
++
++/* no memory barriers required on elan3/elan4 */
++
++#elif defined QSNET_MEMBARS_ASSERT
++
++#include <assert.h>
++#define MEMBAR_MEMISSUE()       assert(0);
++#define MEMBAR_SYNC()           assert(0);
++#define MEMBAR_STORELOAD()      assert(0);
++#define MEMBAR_LOADSTORE()      assert(0);
++#define MEMBAR_STORESTORE()     assert(0);
++#define MEMBAR_LOADLOAD()       assert(0);
++#define MEMBAR_VISIBLE()        assert(0);
++#define MEMBAR_DRAIN()          assert(0);
++    
++#elif defined(__alpha)
++
++/* Memory barrier instructions */
++#if defined(__DECC) || defined(__DECXX)
++long   asm( const char *,...);
++#pragma intrinsic( asm )
++#define MEMBAR_MEMISSUE()     asm("mb")
++#define MEMBAR_SYNC()                 asm("mb")
++#define MEMBAR_STORELOAD()    asm("wmb")
++#define MEMBAR_LOADSTORE()    asm("mb")
++#define MEMBAR_STORESTORE()   asm("wmb")
++#define MEMBAR_LOADLOAD()     asm("mb")
++#define MEMBAR_VISIBLE()      asm("")
++#define MEMBAR_DRAIN()                asm("wmb")
++
++#else
++/* Assume gcc */
++#define MEMBAR_MEMISSUE()     asm volatile ("mb"::)
++#define MEMBAR_SYNC()                 asm volatile ("mb"::)
++#define MEMBAR_STORELOAD()    asm volatile ("wmb"::)
++#define MEMBAR_LOADSTORE()    asm volatile ("mb"::)
++#define MEMBAR_STORESTORE()   asm volatile ("wmb"::)
++#define MEMBAR_LOADLOAD()     asm volatile ("mb"::)
++#define MEMBAR_VISIBLE()      asm volatile (""   ::: "memory")
++#define MEMBAR_DRAIN()                asm volatile ("wmb"::: "memory")
++
++#endif /* __DECC */
++
++#elif defined(__sparc)
++
++/* UltraSPARC with WRITE MERGING enabled */
++#define MEMBAR_MEMISSUE()     asm volatile ("membar #MemIssue");
++#define MEMBAR_SYNC()         asm volatile ("membar #Sync");
++#define MEMBAR_STORELOAD()    asm volatile ("membar #StoreLoad");
++#define MEMBAR_LOADSTORE()    asm volatile ("membar #LoadStore");
++#define MEMBAR_STORESTORE()   asm volatile ("membar #StoreStore");
++#define MEMBAR_LOADLOAD()     asm volatile ("membar #LoadLoad");
++#define MEMBAR_VISIBLE()      asm volatile (""::: "memory")
++#define MEMBAR_DRAIN()                asm volatile (""::: "memory")
++
++#elif defined(__linux__)
++
++#if defined(__INTEL_COMPILER)
++
++/* NB: Intel compiler version 8.0 now also defines __GNUC__ unless you set the -no-gcc cmdline option
++ * I've moved the check for __INTEL_COMPILER to be first to get around this
++ */
++#ifdef __ECC
++
++#include <ia64intrin.h>
++
++#define MEMBAR_MEMISSUE()       __mf()
++#define MEMBAR_SYNC()           __mf()
++#define MEMBAR_STORELOAD()      __mf()
++#define MEMBAR_LOADSTORE()      __mf()
++#define MEMBAR_STORESTORE()     __mf()
++#define MEMBAR_LOADLOAD()       __mf()
++#define MEMBAR_VISIBLE()      __mf()
++#define MEMBAR_DRAIN()                __mf()
++
++#else
++
++#warning Membars not implemented with this compiler.
++#define MEMBAR_MEMISSUE()       ;
++#define MEMBAR_SYNC()           ;
++#define MEMBAR_STORELOAD()      ;
++#define MEMBAR_LOADSTORE()      ;
++#define MEMBAR_STORESTORE()     ;
++#define MEMBAR_LOADLOAD()       ;
++#define MEMBAR_VISIBLE()        ;
++#define MEMBAR_DRAIN()          ;
++
++#endif /* __ECC */
++
++#elif defined(__GNUC__)
++
++#ifndef __ia64
++
++/* These are needed by <asm/system.h> on AMD64 */
++#include <asm/types.h>
++#include <asm/bitops.h>
++
++#ifndef __cplusplus
++/* this header file has a parameter called "new" - great huh */
++#include <asm/system.h>
++#endif
++
++#else
++#  define mb()        __asm__ __volatile__ ("mf" ::: "memory")
++#  define rmb()       mb()
++#  define wmb()       mb()
++#endif /* !__ia64 */
++
++#if defined(__x86_64) || defined(__i386)
++/* For some reason the AMD64 definition (glibc-devel 2.3.X) of this 
++ * is not useful (compiler only directive) so we overload it here
++ */
++/* I don't trust the IA32 header files either as with mtrr enabled
++ * we really need a membar and not a compiler directive
++ * NB: sfence is only available with X86_FEATURE_XMM CPUs
++ */
++#undef wmb
++#define wmb()    asm volatile("sfence":::"memory");
++#endif /* __x86_64 */
++
++#define MEMBAR_MEMISSUE()     mb()
++#define MEMBAR_SYNC()                 mb()
++#define MEMBAR_STORELOAD()    wmb()
++#define MEMBAR_LOADSTORE()    mb()
++#define MEMBAR_STORESTORE()   wmb()
++#define MEMBAR_LOADLOAD()     mb()
++
++#ifdef __ia64
++#define MEMBAR_VISIBLE()      asm volatile ("mf.a;;mf;;"::: "memory")
++#define MEMBAR_DRAIN()                asm volatile ("mf;"::: "memory")
++#else
++#define MEMBAR_VISIBLE()      asm volatile (""::: "memory")
++#define MEMBAR_DRAIN()                wmb()
++#endif
++
++#else /* elif __GNUC__ */
++
++#error Membars not implemented for this architecture/compiler.
++
++#endif /* __INTEL_COMPILER */
++
++#else /* elif __linux__ */
++
++#error Membars not implemented for this architecture/compiler.
++
++#endif
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* _CONFIG_FENCE_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/include/qsnet/kernel.h
+===================================================================
+--- linux-2.6.5.orig/include/qsnet/kernel.h    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/qsnet/kernel.h 2005-05-11 12:10:12.623904456 -0400
+@@ -0,0 +1,38 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __QSNET_KERNEL_H
++#define __QSNET_KERNEL_H
++
++#ident "$Id: kernel.h,v 1.8 2003/03/14 10:18:22 mike Exp $"
++/*      $Source: /cvs/master/quadrics/qsnet/kernel.h,v $*/
++
++#include <qsnet/config.h>
++#include <qsnet/types.h>
++
++#if defined(SOLARIS)
++#include <qsnet/kernel_solaris.h>
++#endif
++
++#if defined(DIGITAL_UNIX)
++#include <qsnet/kernel_dunix.h>
++#endif
++
++#if defined(LINUX)
++#include <qsnet/kernel_linux.h>
++#endif
++
++#include <qsnet/debug.h>
++
++#endif /* __QSNET_KERNEL_H */
++
++
++
++
++
++
++
+Index: linux-2.6.5/include/qsnet/kernel_linux.h
+===================================================================
+--- linux-2.6.5.orig/include/qsnet/kernel_linux.h      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/qsnet/kernel_linux.h   2005-05-11 12:10:12.624904304 -0400
+@@ -0,0 +1,352 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __QSNET_KERNEL_LINUX_H
++#define __QSNET_KERNEL_LINUX_H
++
++#ident "$Id: kernel_linux.h,v 1.62.6.6 2005/03/07 16:43:32 david Exp $"
++/*      $Source: /cvs/master/quadrics/qsnet/kernel_linux.h,v $*/
++
++#if defined(MODVERSIONS)
++#include <linux/modversions.h>
++#endif
++
++#include <linux/autoconf.h>
++#include <linux/module.h>
++
++
++/* ASSERT(spin_is_locked(l)) would always fail on UP kernels */
++#if defined(CONFIG_SMP)
++#define SPINLOCK_HELD(l)      spin_is_locked(l)
++#else
++#define SPINLOCK_HELD(l)      (1) 
++#endif
++
++#include <asm/io.h>
++#include <asm/uaccess.h>
++
++#include <linux/types.h>
++#include <linux/time.h>
++
++#include <linux/delay.h>
++#include <linux/smp_lock.h>
++#include <linux/spinlock.h>
++#include <linux/module.h>
++
++#include <linux/highmem.h>
++
++#include <qsnet/mutex.h>
++#include <qsnet/condvar.h>
++#include <qsnet/crwlock.h>
++
++#if defined(LINUX_ALPHA)
++#  include <asm/core_tsunami.h>       /* for TSUNAMI_MEM */
++#endif
++
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)
++#     undef   MOD_INC_USE_COUNT
++#     undef   MOD_DEC_USE_COUNT
++#     define  MOD_INC_USE_COUNT
++#     define  MOD_DEC_USE_COUNT
++#endif
++
++#define MIN(a,b)      ((a) > (b) ? (b) : (a))
++#define MAX(a,b)      ((a) > (b) ? (a) : (b))
++
++/* stray types */
++typedef u64              u_longlong_t;
++typedef unsigned long    uintptr_t;
++typedef int              bool_t;
++
++typedef unsigned long    virtaddr_t;                          /* virtual address */
++typedef unsigned long      ioaddr_t;                          /* io address */
++typedef unsigned long      sdramaddr_t;                               /* elan sdram offset */
++
++/* 386 kernel can be compiled with PAE enabled to use a 44 bit physical address */
++#if defined(CONFIG_X86_PAE)
++typedef unsigned long long physaddr_t;
++#else
++typedef unsigned long    physaddr_t;
++#endif
++
++/* ticks since reboot, and tick freq */
++#define lbolt         jiffies 
++#define hz            HZ
++
++/* System page size and friends */
++#define PAGESIZE      PAGE_SIZE
++#define PAGESHIFT     PAGE_SHIFT
++#define PAGEOFFSET    (PAGE_SIZE - 1)
++#define PAGEMASK      PAGE_MASK
++
++#define PAGE_ALIGNED(a)       (((a) & PAGE_MASK) == a)
++
++/* convert between bytes and pages */
++#define btop(b)         ((unsigned long)(b) >> PAGE_SHIFT)    /* rnd down */ 
++#define btopr(b)        btop(PAGE_ALIGN((unsigned long) b))   /* rnd up */
++#define ptob(p)               ((unsigned long)(p) << PAGE_SHIFT)
++
++/* round up sz to the nearest multiple of blk */
++#define roundup(sz,blk) ((blk) * ((sz) / (blk) + ((sz) % (blk) ? 1 : 0)))     
++
++/* send a signal to a process */
++#define psignal(pr,sig)       send_sig(sig,pr,0)
++
++/* microsecond delay */
++#define DELAY(us)     udelay(us)
++
++/* macro macros */
++#define MACRO_BEGIN     do {
++#define MACRO_END       } while (0)
++
++/* D-Unix compatable errno values */
++#define ESUCCESS        0
++#define EFAIL           255
++
++/* ASSERT(NO_LOCKS_HELD) will be a no-op */
++#define NO_LOCKS_HELD 1
++
++/* misc */
++typedef int           label_t;
++#define on_fault(ljp) ((ljp) == NULL)
++#define _NOTE(X)
++#define no_fault()    ((void) 0)
++#define panicstr      0
++
++/* return from system call is -EXXX on linux */
++#define set_errno(e)  (-(e))
++
++/* 
++ * BSD-style byte ops 
++ */
++
++#define bcmp(src1,src2,len)           memcmp(src1,src2,len)
++#define bzero(dst,len)                        memset(dst,0,len)
++#define bcopy(src,dst,len)            memcpy(dst,src,len)
++
++#define preemptable_start             do { long must_yield_at = lbolt + (hz/10);
++#define preemptable_end                       } while (0)
++#define preemptable_check()           do {\
++                                            if ((lbolt - must_yield_at) > 0)\
++                                          {\
++                                              preemptable_yield() ; \
++                                              must_yield_at = lbolt + (hz/10);\
++                                          }\
++                                      } while (0)
++
++#define preemptable_yield()           schedule()
++
++#define CURPROC()                       current
++#define CURTHREAD()                     current
++#define SUSER()                               suser()
++
++/* 64 bit IO operations on 32 bit intel cpus using MMX */
++#if defined(LINUX_I386)
++extern u64        qsnet_readq (volatile u64 *ptr);
++extern void       qsnet_writeq (u64 value, volatile u64 *ptr);
++
++#define readq(ptr)            qsnet_readq((void *) ptr)
++#define writeq(val,ptr)               qsnet_writeq(val, (void *)ptr)
++#endif
++
++/*
++ * Memory barriers
++ */
++#ifndef mmiob
++#  define mmiob()                     mb()
++#endif
++
++/* 
++ * Exit handlers
++ */
++#define HANDLER_REGISTER(func,arg,flags)   xa_handler_register(func,arg,flags)
++#define HANDLER_UNREGISTER(func,arg,flags) xa_handler_unregister(func,arg,flags)
++
++/* 
++ * KMEM_GETPAGES and KMEM_ALLOC both call kmem_alloc, which 
++ * translates the call to kmalloc if < PAGE_SIZE, or vmalloc 
++ * if >= PAGE_SIZE.  vmalloc will always return a page-aligned 
++ * region rounded up to the nearest page, while kmalloc will 
++ * return bits and pieces of a page.
++ */
++
++#ifdef KMEM_DEBUG
++extern void          *qsnet_kmem_alloc_debug(int len, int sleep, int zerofill, char *file, int line);
++extern void           qsnet_kmem_free_debug(void *ptr, int len, char *file, int line);
++#define KMEM_ALLOC(ptr,type,len,sleep) \
++      { KMEM_ASSERT(sleep); (ptr)=(type)qsnet_kmem_alloc_debug(len,sleep,0,__FILE__,__LINE__); }
++#define KMEM_ZALLOC(ptr,type,len,sleep) \
++      { KMEM_ASSERT(sleep); (ptr)=(type)qsnet_kmem_alloc_debug(len,sleep,1,__FILE__,__LINE__); }
++
++#define KMEM_FREE(ptr,len)              qsnet_kmem_free_debug((void *)ptr,len,__FILE__,__LINE__)
++
++#else
++
++extern void          *qsnet_kmem_alloc(int len, int sleep, int zerofill);
++extern void           qsnet_kmem_free(void *ptr, int len);
++
++#define KMEM_ALLOC(ptr,type,len,sleep) \
++      { KMEM_ASSERT(sleep); (ptr)=(type)qsnet_kmem_alloc(len,sleep,0); }
++#define KMEM_ZALLOC(ptr,type,len,sleep) \
++      { KMEM_ASSERT(sleep); (ptr)=(type)qsnet_kmem_alloc(len,sleep,1); }
++
++#define KMEM_FREE(ptr,len)              qsnet_kmem_free((void *)ptr,len)
++
++#endif
++extern void       qsnet_kmem_display(void *handle);
++extern physaddr_t kmem_to_phys(void *ptr);
++
++#define KMEM_ASSERT(sleep)             ASSERT(!(in_interrupt() && sleep))
++
++
++#define KMEM_GETPAGES(ptr,type,pgs,sleep) KMEM_ZALLOC(ptr,type,ptob(pgs),sleep)
++#define KMEM_FREEPAGES(ptr,pgs)         KMEM_FREE(ptr,ptob(pgs));
++
++/*
++ * Copying from user space -> kernel space (perms checked)
++ */
++#define copyin(up,kp,size)            copy_from_user(kp,up,size)
++#define copyin_noerr(up,kp,size)      copy_from_user(kp,up,size)
++
++/* get_user() gets xfer width right */
++#define fulinux(ret, up)              (get_user(ret, (up)) == 0 ? ret : -1)
++#define fulinuxp(ret, up)             (get_user(ret, (up)) == 0 ? ret : NULL)
++
++extern __inline__ int fubyte    (u8  *up) { u8  ret;   return fulinux(ret, up);}
++extern __inline__ int fusword   (u16 *up) { u16 ret;   return fulinux(ret, up);}
++extern __inline__ int fuword    (u32 *up) { u32 ret;   return fulinux(ret, up);}
++#if BITS_PER_LONG > 32
++extern __inline__ u64 fulonglong(u64 *up) { u64 ret;   return fulinux(ret, up);}
++#else
++extern __inline__ u64 fulonglong(u64 *up) { return ((u64) fuword((u32 *)up) | (((u64) fuword(((u32 *)up)+1))<<32)); }
++#endif
++extern __inline__ void *fuptr (void **up) { void *ret; return fulinuxp(ret,up);}
++
++#define fubyte_noerr(up)              fubyte(up)
++#define fusword_noerr(up)             fusword(up)
++#define fuword_noerr(up)              fuword(up)
++#define fulonglong_noerr(up)          fulonglong(up)
++#define fuptr_noerr(up)                       fuptr(up)
++
++extern __inline__ int copyinstr(char *up, char *kp, int max, int *size)
++{ 
++      for (*size = 1; *size <= max; (*size)++) {
++              if (get_user(*kp, up++) != 0)
++                      return EFAULT;  /* bad user space addr */
++              if (*kp++ == '\0')
++                      return 0;       /* success */
++      }
++      *size = max;
++      return ENAMETOOLONG;            /* runaway string */
++}
++ 
++/*
++ * Copying from kernel space -> user space (perms checked)
++ */
++
++#define copyout(kp,up,size)           copy_to_user(up,kp,size)
++#define copyout_noerr(kp,up,size)     copy_to_user(up,kp,size)
++
++/* put_user() gets xfer width right */
++#define sulinux(val, up)              (put_user(val, (up)) == 0 ? 0 : -1)
++
++extern __inline__ int subyte    (u8  *up, u8  val) { return sulinux(val, up); }
++extern __inline__ int susword   (u16 *up, u16 val) { return sulinux(val, up); }
++extern __inline__ int suword    (u32 *up, u32 val) { return sulinux(val, up); }
++#if BITS_PER_LONG > 32
++extern __inline__ int sulonglong(u64 *up, u64 val) { return sulinux(val, up); }
++#else
++extern __inline__ int sulonglong(u64 *up, u64 val) { return (suword((u32 *) up, (u32) val) == 0 ? 
++                                                           suword(((u32 *) up)+1, (u32) (val >> 32)) : -1); }
++#endif
++extern __inline__ int suptr   (void **up,void *val){ return sulinux(val, up); }
++
++#define subyte_noerr(up,val)          subyte(up,val)  
++#define susword_noerr(up,val)         susword(up,val) 
++#define suword_noerr(up,val)          suword(up,val)  
++#define sulonglong_noerr(up,val)      sulonglong(up,val)      
++#define suptr_noerr(up,val)           suptr(up,val)   
++
++/*
++ * /proc/qsnet interface
++ */
++extern inline int
++str_append(char *buf, char *add, int size)
++{
++#define TRUNC_MSG      "[Output truncated]\n"
++      int full = 0;
++      int max = size - strlen(TRUNC_MSG) - strlen(add) - 1;
++
++      if (strlen(buf) > max) {
++              strcat(buf, TRUNC_MSG);
++              full = 1;
++      } else
++              strcat(buf, add);
++      return full;
++}
++
++/* Spinlocks */
++#define spin_lock_destroy(l)          ((void) 0)
++
++/* Complex - Reader/Writer locks - we added <linux/crwlock.h> */
++typedef crwlock_t                     krwlock_t;
++#define krwlock_init(l)                       crwlock_init(l)
++#define krwlock_destroy(l)            crwlock_destroy(l)
++#define krwlock_write(l)              crwlock_write(l)
++#define krwlock_read(l)                       crwlock_read(l)
++#define krwlock_done(l)                       crwlock_done(l)
++#define krwlock_is_locked(l)          crwlock_held(l)
++#define krwlock_is_write_locked(l)    crwlock_write_held(l)
++#define krwlock_is_read_locked(l)     crwlock_read_held(l)
++
++/*
++ * Timeouts - Solaris style.
++ */
++typedef struct timer_list timer_fn_t;
++
++extern inline void
++schedule_timer_fn(timer_fn_t *timer, void (*fun)(void *), void *arg, long hz_delay)
++{
++      init_timer(timer);
++
++      timer->function = (void (*)(unsigned long)) fun;
++      timer->data     = (unsigned long) arg;
++      timer->expires  = jiffies + hz_delay;
++
++      add_timer(timer);
++}
++
++/* returns 1 if timer_fn was cancelled */
++extern inline int
++cancel_timer_fn(timer_fn_t *timer)
++{
++    return (del_timer_sync(timer));
++}
++
++extern inline int
++timer_fn_queued(timer_fn_t *timer)
++{
++    return (timer_pending (timer));
++}
++/*
++ * Hold/release CPU's.
++ */
++
++extern void   cpu_hold_all(void);
++extern void   cpu_release_all(void);
++#define CAPTURE_CPUS()                cpu_hold_all()
++#define RELEASE_CPUS()                cpu_release_all()
++
++#define IASSERT ASSERT
++
++#endif /* __QSNET_KERNEL_LINUX_H */
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.6.5/include/qsnet/kpte.h
+===================================================================
+--- linux-2.6.5.orig/include/qsnet/kpte.h      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/qsnet/kpte.h   2005-05-11 12:10:12.624904304 -0400
+@@ -0,0 +1,109 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2004 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __QSNET_KPTE_H
++#define __QSNET_KPTE_H
++
++#ident "@(#)$Id: kpte.h,v 1.1.2.2 2005/03/02 09:51:49 david Exp $ $Name: QSNETMODULES-4-31_20050321 $"
++/*      $Source: /cvs/master/quadrics/qsnet/kpte.h,v $*/
++
++#include <qsnet/autoconf.h>
++
++#ifdef NO_RMAP
++#     define pte_offset_kernel pte_offset
++#     define pte_offset_map    pte_offset
++#       define pte_unmap(A)      do { ; } while (0)
++#endif
++
++/* 
++ * Pte stuff
++ */
++static __inline__ struct mm_struct *
++get_kern_mm(void)
++{
++        return &init_mm;
++}
++
++static __inline__ pte_t *
++find_pte_map(struct mm_struct *mm, unsigned long vaddr)
++{
++        pgd_t *pgd;
++        pmd_t *pmd;
++      pte_t *ptep;
++
++/* XXXX - handle hugh tlb code */
++      pgd = pgd_offset(mm, vaddr);
++      if (pgd_none(*pgd) || pgd_bad(*pgd))
++              goto out;
++      
++      pmd = pmd_offset(pgd, vaddr);
++      if (pmd_none(*pmd) || pmd_bad (*pmd))
++              goto out;
++
++      ptep = pte_offset_map (pmd, vaddr);
++      if (! ptep)
++              goto out;
++      
++      if (pte_present (*ptep))
++              return ptep;
++
++      pte_unmap (ptep);
++out:
++      return NULL;
++}
++
++static __inline__ pte_t *
++find_pte_kernel(unsigned long vaddr)
++{
++        pgd_t *pgd;
++        pmd_t *pmd;
++      pte_t *pte;
++
++      pgd = pgd_offset_k(vaddr);
++      if (pgd && !pgd_none(*pgd)) {
++          pmd = pmd_offset(pgd, vaddr);
++          if (pmd && pmd_present(*pmd)) {
++              pte = pte_offset_kernel(pmd, vaddr);
++              if (pte && pte_present(*pte))
++                  return (pte);
++          }
++      }
++      return (NULL);
++}
++
++static __inline__ physaddr_t
++pte_phys(pte_t pte)
++{
++#if defined(LINUX_ALPHA)
++      /* RedHat 7.1 2.4.3-12 
++       * They have now enabled Monster windows on Tsunami
++       * and so can use the Main's phys pte value 
++       */
++      return (pte_val(pte) >> (32-PAGE_SHIFT));
++#elif defined(LINUX_I386) || defined(LINUX_X86_64)
++#if defined(_PAGE_NX)
++      return (pte_val(pte) & ~((1 << PAGE_SHIFT)-1) & ~_PAGE_NX);
++#else
++      return (pte_val(pte) & ~((1 << PAGE_SHIFT)-1));
++#endif
++#elif defined(LINUX_SPARC)
++      return (pte_val(pte) & _PAGE_PADDR);
++#elif defined(LINUX_IA64)
++      return (pte_val(pte) & _PFN_MASK);
++#else
++#error Unknown architecture
++#endif
++}
++
++#endif /* __QSNET_KPTE_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/include/qsnet/kthread.h
+===================================================================
+--- linux-2.6.5.orig/include/qsnet/kthread.h   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/qsnet/kthread.h        2005-05-11 12:10:12.630903392 -0400
+@@ -0,0 +1,71 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *    Copyright (c) 2002-2004 by Quadrics Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __QSNET_KTHREAD_H
++#define __QSNET_KTHREAD_H
++
++#ident "@(#)$Id: kthread.h,v 1.1 2004/10/28 11:50:29 david Exp $ $Name: QSNETMODULES-4-31_20050321 $"
++/*      $Source: /cvs/master/quadrics/qsnet/kthread.h,v $*/
++
++#include <qsnet/autoconf.h>
++
++/* 
++ * kernel threads 
++ */
++extern __inline__ void
++kernel_thread_init(char *comm)
++{
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
++#ifndef NO_NPTL
++#     define sigmask_lock                     sighand->siglock
++#endif
++      lock_kernel();
++      daemonize();
++        reparent_to_init();
++
++        /* avoid getting signals */
++        spin_lock_irq(&current->sigmask_lock);
++        flush_signals(current);
++        sigfillset(&current->blocked);
++      
++#ifdef NO_NPTL
++        recalc_sigpending(current);
++#else
++        recalc_sigpending();
++#endif
++
++        spin_unlock_irq(&current->sigmask_lock);
++
++      /* set our name for identification purposes */
++      strncpy(current->comm, comm, sizeof(current->comm));
++
++      unlock_kernel();
++#else
++      daemonize(comm);
++#endif
++}
++
++extern __inline__ void *
++kernel_thread_wrap(caddr_t stk, int stksize, void (*proc)(void *), void *arg)
++{
++        ASSERT(stk == NULL && stksize == 0);
++        kernel_thread((int (*)(void *))proc, arg, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
++        return (void *)1; /* non-null value */
++}
++
++#define kernel_thread_create(proc,arg)  kernel_thread_wrap(NULL,0,(void (*)(void *))proc,arg)
++#define kernel_thread_exit()          ((void) 0)
++#define kernel_thread_become_highpri()        ((void) 0)
++
++#endif /* __QSNET_KTHREAD_H */
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.6.5/include/qsnet/list.h
+===================================================================
+--- linux-2.6.5.orig/include/qsnet/list.h      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/qsnet/list.h   2005-05-11 12:10:12.631903240 -0400
+@@ -0,0 +1,80 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Limited.
++ * 
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: list.h,v 1.5 2003/10/27 13:55:33 david Exp $"
++/*      $Source: /cvs/master/quadrics/qsnet/list.h,v $*/
++
++#ifndef __QSNET_LIST_H
++#define __QSNET_LIST_H
++
++/* Implementation of doubly linked lists - compatible with linux */
++struct list_head 
++{
++    struct list_head *next;
++    struct list_head *prev;
++};
++
++#if !defined(LINUX)
++#if ! defined( offsetof ) 
++#define offsetof(T,F) ((int )&(((T *)0)->F))
++#endif
++
++#define LIST_HEAD_INIT(name) { &(name), &(name) }
++
++#define LIST_HEAD(name) \
++      struct list_head name = LIST_HEAD_INIT(name)
++#endif
++
++#define list_entry(ptr, type, off) \
++      ((type *) ((unsigned long)(ptr) - offsetof (type,off)))
++
++#define INIT_LIST_HEAD(list) \
++MACRO_BEGIN \
++      (list)->next = (list)->prev = (list); \
++MACRO_END
++
++#define list_add(new, list) \
++MACRO_BEGIN \
++      (list)->next->prev = (new); \
++      (new)->next = (list)->next; \
++      (new)->prev = (list); \
++      (list)->next = (new); \
++MACRO_END
++
++#define list_add_tail(new, list) \
++MACRO_BEGIN \
++      (list)->prev->next = new; \
++      (new)->prev = (list)->prev; \
++      (new)->next = (list); \
++      (list)->prev = (new); \
++MACRO_END
++
++#define list_del(entry) \
++MACRO_BEGIN \
++      (entry)->prev->next = (entry)->next; \
++      (entry)->next->prev = (entry)->prev; \
++MACRO_END
++
++#define list_del_init(entry) \
++MACRO_BEGIN \
++      (entry)->prev->next = (entry)->next; \
++      (entry)->next->prev = (entry)->prev; \
++      (entry)->next = (entry)->prev = (entry); \
++MACRO_END
++
++#define list_empty(list) \
++      ((list)->next == (list))
++
++#define list_for_each(pos,list) \
++      for (pos = (list)->next; pos != (list); \
++           pos = (pos)->next)
++
++#define list_for_each_safe(pos,n,list) \
++      for (pos = (list)->next, n = (pos)->next; pos != (list); \
++           pos = n, n = (pos)->next)
++
++#endif /* __QSNET_LIST_H */
+Index: linux-2.6.5/include/qsnet/mutex.h
+===================================================================
+--- linux-2.6.5.orig/include/qsnet/mutex.h     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/qsnet/mutex.h  2005-05-11 12:10:12.631903240 -0400
+@@ -0,0 +1,91 @@
++/*
++ *    Copyright (C) 2000  Regents of the University of California
++ *
++ *    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.
++ *
++ *    This program is distributed in the hope that it will be useful,
++ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *    GNU General Public License for more details.
++ *
++ *    You should have received a copy of the GNU General Public License
++ *    along with this program; if not, write to the Free Software
++ *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ *
++ */
++
++#if   !defined(_LINUX_MUTEX_H)
++#define       _LINUX_MUTEX_H
++#if   defined(__KERNEL__)
++
++#include <asm/smp.h>
++#include <linux/spinlock.h>
++#include <asm/semaphore.h>
++#include <qsnet/debug.h>
++#include <linux/interrupt.h>
++#include <linux/version.h>
++
++#define PID_NONE      0
++
++typedef struct
++{
++    struct semaphore sem;
++    pid_t          holder;
++} kmutex_t;
++
++extern __inline__ void
++kmutex_init (kmutex_t *l)
++{
++#if   LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
++    l->sem = MUTEX;
++#else
++    init_MUTEX(&l->sem);
++#endif
++    l->holder = PID_NONE;
++}
++
++extern __inline__ void
++kmutex_destroy (kmutex_t *l) 
++{
++    ASSERT (l->holder == PID_NONE); 
++}
++
++extern __inline__ void
++kmutex_lock (kmutex_t *l) 
++{ 
++    ASSERT(l->holder != current->pid);
++    down (&l->sem);
++    l->holder = current->pid; 
++}
++
++extern __inline__ void
++kmutex_unlock (kmutex_t *l) 
++{
++    ASSERT(l->holder == current->pid);
++
++    l->holder = PID_NONE;
++    up (&l->sem);
++}
++
++extern __inline__ int
++kmutex_trylock (kmutex_t *l) 
++{
++    if (down_trylock (&l->sem) == 0) 
++    {
++      l->holder = current->pid;
++      return (1);
++    }
++    return (0);
++}
++
++extern __inline__ int
++kmutex_is_locked (kmutex_t *l) 
++{
++    return (l->holder == current->pid);
++}
++
++#endif /* __KERNEL__ */
++#endif /* _LINUX_MUTEX_H */
+Index: linux-2.6.5/include/qsnet/procfs_linux.h
+===================================================================
+--- linux-2.6.5.orig/include/qsnet/procfs_linux.h      2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/qsnet/procfs_linux.h   2005-05-11 12:10:12.632903088 -0400
+@@ -0,0 +1,234 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __PROCFS_LINUX_H
++#define __PROCFS_LINUX_H
++
++#ident "$Id: procfs_linux.h,v 1.6.2.6 2004/12/06 17:36:24 robin Exp $"
++/*      $Source: /cvs/master/quadrics/qsnet/procfs_linux.h,v $ */
++
++#if defined(__KERNEL__)
++
++#include <qsnet/kernel_linux.h>
++#include <qsnet/autoconf.h>
++#include <linux/proc_fs.h>
++
++extern gid_t qsnet_procfs_gid;
++
++/* borrowed from fs/proc/proc_misc - helper for proc_read_int */
++static inline int 
++qsnet_proc_calc_metrics(char *page, char **start, off_t off, int count, int *eof, int len)
++{
++      if (len <= off+count) *eof = 1;
++      *start = page + off;
++      len -= off;
++      if (len>count) len = count;
++      if (len<0) len = 0;
++      return len;
++}
++
++static inline int
++qsnet_proc_write_int(struct file *file, const char *buf, unsigned long count, void *data)
++{
++      char tmpbuf[16];
++      int  res = count;
++      
++      if (count > sizeof(tmpbuf) - 1)
++              return (-EINVAL);
++      
++      MOD_INC_USE_COUNT;
++      if (copy_from_user(tmpbuf, buf, count))
++              res = -EFAULT;
++      else
++      {
++              tmpbuf[count] = '\0'; 
++              *(int *)data = simple_strtoul(tmpbuf, NULL, 0);
++      }
++      MOD_DEC_USE_COUNT;
++      
++      return (res);
++}
++
++static inline int
++qsnet_proc_read_int(char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++      int len, res;
++      
++      MOD_INC_USE_COUNT;
++      
++      len = sprintf(page, "%d\n", *(int *)data);
++      res = qsnet_proc_calc_metrics(page, start, off, count, eof, len);
++      
++      MOD_DEC_USE_COUNT;
++      return (res);
++}
++
++static inline struct proc_dir_entry *
++qsnet_proc_register_int(struct proc_dir_entry *dir, char *path, int *var, int read_only)
++{
++      struct proc_dir_entry *p;
++      
++      p = create_proc_entry(path, read_only ? S_IRUGO : S_IRUGO|S_IWUSR|S_IWGRP, dir);
++      if (p) {
++              if (! read_only) 
++                      p->write_proc = qsnet_proc_write_int;
++              p->read_proc  = qsnet_proc_read_int;
++              p->data       = var;
++              p->owner      = THIS_MODULE;
++              p->gid        = qsnet_procfs_gid;
++      }
++      return p;
++}
++
++static inline int
++qsnet_proc_write_hex(struct file *file, const char *buf, unsigned long count, void *data)
++{
++      char tmpbuf[16];
++      int  res = count;
++      
++      if (count > sizeof(tmpbuf) - 1)
++              return (-EINVAL);
++      
++      MOD_INC_USE_COUNT;
++      if (copy_from_user(tmpbuf, buf, count))
++              res = -EFAULT;
++      else
++      {
++              tmpbuf[count] = '\0'; 
++              *(int *)data = simple_strtoul(tmpbuf, NULL, 0);
++      }
++      MOD_DEC_USE_COUNT;
++      
++      return (res);
++}
++
++static inline int
++qsnet_proc_read_hex(char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++      int len, res;
++      
++      MOD_INC_USE_COUNT;
++      
++      len = sprintf(page, "0x%x\n", *(int *)data);
++      res = qsnet_proc_calc_metrics(page, start, off, count, eof, len);
++      
++      MOD_DEC_USE_COUNT;
++      return (res);
++}
++
++static inline struct proc_dir_entry *
++qsnet_proc_register_hex(struct proc_dir_entry *dir, char *path, int *var, int read_only)
++{
++      struct proc_dir_entry *p;
++      
++      p = create_proc_entry(path, read_only ? S_IRUGO : S_IRUGO|S_IWUSR|S_IWGRP, dir);
++      if (p) {
++              if (! read_only) 
++                      p->write_proc = qsnet_proc_write_hex;
++              p->read_proc  = qsnet_proc_read_hex;
++              p->data       = var;
++              p->owner      = THIS_MODULE;
++              p->gid        = qsnet_procfs_gid;
++      }
++      return p;
++}
++
++#define QSNET_PROC_STR_LEN_MAX ((int)256)
++
++static inline int
++qsnet_proc_write_str(struct file *file, const char *buf, unsigned long count, void *data)
++{
++      int  res = count;
++      
++      if (count > (QSNET_PROC_STR_LEN_MAX - 1))
++              return (-EINVAL);
++      
++      MOD_INC_USE_COUNT;
++      if (copy_from_user((char *)data, buf, count))
++              res = -EFAULT;
++      else
++      {
++              ((char *)data)[count] = '\0'; 
++              /* remove linefeed */
++              if ( (count) && (((char *)data)[count -1] == '\n'))
++                      ((char *)data)[count -1] = '\0';
++      }
++      MOD_DEC_USE_COUNT;
++      
++      return (res);
++}
++
++static inline int
++qsnet_proc_read_str(char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++      int len, res;
++      
++      if ( strlen(data) > (count + 1))
++              return (-EINVAL);       
++
++      MOD_INC_USE_COUNT;
++      
++      /* cant output too much */
++      if ( strlen(data) > (count + 1))
++      {
++              MOD_DEC_USE_COUNT;
++              return (-EINVAL);       
++      }
++
++
++      len = sprintf(page, "%s\n", (char *)data);
++      if (len > count)
++      {
++              MOD_DEC_USE_COUNT;
++              return (-EINVAL);       
++      }
++
++      res = qsnet_proc_calc_metrics(page, start, off, count, eof, len);
++      
++      MOD_DEC_USE_COUNT;
++      return (res);
++}
++
++static inline struct proc_dir_entry *
++qsnet_proc_register_str(struct proc_dir_entry *dir, char *path, char *var, int read_only)
++{
++      struct proc_dir_entry *p;
++      
++      p = create_proc_entry(path, read_only ? S_IRUGO : S_IRUGO|S_IWUSR|S_IWGRP, dir);
++      if (p) {
++              if (! read_only) 
++                      p->write_proc = qsnet_proc_write_str;
++              p->read_proc  = qsnet_proc_read_str;
++              p->data       = var;
++              p->owner      = THIS_MODULE;
++              p->gid        = qsnet_procfs_gid;
++      }
++      return p;
++}
++
++extern struct proc_dir_entry *qsnet_procfs_root; 
++extern struct proc_dir_entry *qsnet_procfs_config;
++
++#ifdef NO_PDE
++static inline struct proc_dir_entry *PDE(const struct inode *inode)
++{
++    return inode->u.generic_ip;
++}
++#endif
++#endif /* __KERNEL__ */
++
++#define QSNET_PROCFS_IOCTL      "/proc/qsnet/ioctl"
++#define QSNET_PROCFS_KMEM_DEBUG "/proc/qsnet/kmem_debug"
++#define QSNET_PROCFS_VERSION    "/proc/qsnet/version"
++
++#endif /* __PROCFS_LINUX_H */
++
++/*
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.6.5/include/qsnet/pthread.h
+===================================================================
+--- linux-2.6.5.orig/include/qsnet/pthread.h   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/qsnet/pthread.h        2005-05-11 12:10:12.632903088 -0400
+@@ -0,0 +1,59 @@
++/*
++ *    Copyright (c) 2003 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++/* $Id: pthread.h,v 1.5 2004/06/07 10:47:06 addy Exp $ */
++/*             $Source: /cvs/master/quadrics/qsnet/pthread.h,v $*/
++
++#ifndef _CONFIG_PTHREAD_H
++#define _CONFIG_PTHREAD_H
++
++#ifdef        __cplusplus
++extern "C" {
++#endif
++
++#if defined(__ELAN__)
++
++/* No pthread support on Elan co-processor */
++
++#define MUTEX                   unsigned long long
++#define MUTEX_INIT(X)         ;
++#define       MUTEX_LOCK(X)           ;
++#define       MUTEX_UNLOCK(X)         ;
++
++#else
++#if defined(DIGITAL_UNIX)
++#include <tis.h>
++#define MUTEX                 pthread_mutex_t
++#define MUTEX_INIT(X)         tis_mutex_init(X)
++#define       MUTEX_LOCK(X)           tis_mutex_lock(X)
++#define       MUTEX_UNLOCK(X)         tis_mutex_unlock(X)
++#define       MUTEX_TRYLOCK(X)        (tis_mutex_trylock(X) == 0)
++
++#else /* Linux... */
++
++/* Use standard pthread calls */
++#include <pthread.h>
++#define MUTEX                 pthread_mutex_t
++#define MUTEX_INIT(X)         pthread_mutex_init(X, NULL)
++#define       MUTEX_LOCK(X)           pthread_mutex_lock(X)
++#define       MUTEX_UNLOCK(X)         pthread_mutex_unlock(X)
++#define       MUTEX_TRYLOCK(X)        (pthread_mutex_trylock(X) == 0)
++
++#endif /* DIGITAL_UNIX */
++#endif /* __ELAN__ */
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* _CONFIG_PTHREAD_H */
++
++/*
++ * Local variables:
++ * c-file-style: "stroustrup"
++ * End:
++ */
+Index: linux-2.6.5/include/qsnet/statsformat.h
+===================================================================
+--- linux-2.6.5.orig/include/qsnet/statsformat.h       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/qsnet/statsformat.h    2005-05-11 12:10:12.632903088 -0400
+@@ -0,0 +1,25 @@
++#ifndef _QSNET_STATSFORMAT_H
++#define _QSNET_STATSFORMAT_H
++
++#ident "$Id: statsformat.h,v 1.2 2003/05/22 19:37:14 addy Exp $"
++/*      $Source: /cvs/master/quadrics/qsnet/statsformat.h,v $*/
++
++#include <qsnet/config.h>
++
++/*
++ * format of an Elan stats record
++ *
++ * type    char(8), type of statistic, e.g. FPAGE, ELAN3, TPORT
++ * time    uint64, 10 digits, time in millisecs since counters initialised
++ * device  uint, 2 digits, Elan device id
++ * name    char(32), name of the statistic
++ * value   uint64, current value of statistic
++ */
++    
++#ifdef _ILP32
++#define ELAN_STATSFORMAT "%-8s %10llu %2d %-32s %llu\n"
++#else
++#define ELAN_STATSFORMAT "%-8s %10lu %2d %-32s %lu\n"
++#endif
++
++#endif
+Index: linux-2.6.5/include/qsnet/types.h
+===================================================================
+--- linux-2.6.5.orig/include/qsnet/types.h     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/qsnet/types.h  2005-05-11 12:10:12.632903088 -0400
+@@ -0,0 +1,90 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef __QSNET_TYPES_H
++#define __QSNET_TYPES_H
++
++#ident "$Id: types.h,v 1.16 2003/08/01 16:21:38 addy Exp $"
++/*      $Source: /cvs/master/quadrics/qsnet/types.h,v $*/
++
++/*
++ * Include typedefs for ISO/IEC 9899:1990 standard types
++ *
++ *
++ *    The following integer typedefs are used:
++ *
++ *    int8_t, int16_t, int32_t, int64_t, intptr_t
++ *    uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t
++ *    uchar_t, ushort_t, uint_t, ulong_t
++ *
++ *    <sys/types.h> also defines the following:
++ *    u_char, u_short, u_int, u_long, caddr_t
++ */
++
++#include <qsnet/config.h>
++
++#if defined(SOLARIS) && defined(__KERNEL__)
++#  include <sys/inttypes.h>
++#endif
++
++#if defined(SOLARIS) && !defined(__KERNEL__)
++#  include <inttypes.h>
++#  include <sys/types.h>
++#endif
++
++#if defined(DIGITAL_UNIX) && defined(__KERNEL__)
++#  include <sys/bitypes.h>
++#endif
++
++#if defined(DIGITAL_UNIX) && !defined(__KERNEL__)
++#  include <inttypes.h>
++#  include <sys/types.h>
++#endif
++
++#if defined(LINUX) && defined(__KERNEL__)
++#  include <linux/types.h>
++#endif
++
++#if defined(LINUX) && !defined(__KERNEL__)
++#  include <stdint.h>
++#  include <inttypes.h>
++#  include <sys/types.h>
++
++typedef unsigned char  uchar_t;
++typedef unsigned short ushort_t;
++typedef unsigned int   uint_t;
++typedef unsigned long  ulong_t;
++#endif
++
++#if defined(QNX)
++#  include <inttypes.h>
++#  include <sys/types.h>
++#endif
++
++/* Define a type that will represent a Main CPU pointer
++ * on both the Main and the Elan
++ */
++#ifdef __ELAN__
++
++#if defined(_MAIN_LP64)
++#define QSNET_MAIN_PTR        uint64_t
++#else
++#define QSNET_MAIN_PTR        uint32_t
++#endif
++
++#else
++
++#ifdef _LP64
++#define QSNET_MAIN_PTR        uint64_t
++#else
++#define QSNET_MAIN_PTR        uint32_t
++#endif
++
++#endif
++
++
++#endif /* __QSNET_TYPES_H */
+Index: linux-2.6.5/include/qsnet/workarounds.h
+===================================================================
+--- linux-2.6.5.orig/include/qsnet/workarounds.h       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/qsnet/workarounds.h    2005-05-11 12:10:12.633902936 -0400
+@@ -0,0 +1,24 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ifndef _QSNET_WORKAROUNDS_H
++#define _QSNET_WORKAROUNDS_H
++
++#ident "$Id: workarounds.h,v 1.11 2002/08/09 11:15:55 addy Exp $"
++/*      $Source: /cvs/master/quadrics/qsnet/workarounds.h,v $ */
++
++/* Elan workarounds */
++#undef  ELAN_REVA_SUPPORTED   /* rev a elans no longer supported. */
++#undef  ELITE_REVA_SUPPORTED  /* removed since RMS disables broadcast on rev A elites. */
++#define ELAN_REVB_BUG_1
++/* WORKAROUND for GNAT hw-elan3/3263 */
++#define ELAN_REVB_BUG_2
++
++/* WORKAROUND for GNATs ic-elan3/3637 & ic-elan3/3550 */
++#define ELAN_REVB_BUG_3
++
++#endif /* _QSNET_WORKAROUNDS_H */
+Index: linux-2.6.5/include/rms/rmscall.h
+===================================================================
+--- linux-2.6.5.orig/include/rms/rmscall.h     2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/rms/rmscall.h  2005-05-11 12:10:12.633902936 -0400
+@@ -0,0 +1,144 @@
++/*
++ * Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ * For licensing information please see the supplied COPYING file
++ *
++ * rmscall.h:  user interface to rms kernel module
++ *
++ * $Id: rmscall.h,v 1.25 2004/05/14 08:55:57 duncan Exp $
++ * $Source: /cvs/master/quadrics/rmsmod/rmscall.h,v $
++ *
++ */
++
++#ifndef RMSCALL_H_INCLUDED
++#define RMSCALL_H_INCLUDED 1
++
++#ident        "$Id: rmscall.h,v 1.25 2004/05/14 08:55:57 duncan Exp $"
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++/*
++ * flags for rms_fork_register
++ *
++ * RMS_IOF is not in a public header file 
++ */
++#define RMS_IOF 1             /* inherit on fork */
++
++#ifndef __KERNEL__
++#include <sys/types.h>
++#endif
++
++#include <qsnet/types.h>
++#include <elan/capability.h>
++
++#define MAXCOREPATHLEN 32
++
++#if defined(SOLARIS)
++typedef long long rmstime_t;
++#else  /* DIGITAL_UNIX */
++typedef long rmstime_t;
++#endif
++
++typedef enum {
++    
++    PRG_RUNNING  = 0x01,      /* program is running                  */
++    PRG_ZOMBIE   = 0x02,      /* last process on a node has exited   */
++    PRG_NODE     = 0x04,      /* stats are complete for this node    */
++    PRG_KILLED   = 0x08,      /* program was killed                  */
++    PRG_SUSPEND  = 0x10               /* program is suspended                */
++
++} PRGSTATUS_FLAGS;
++
++/*
++ * program time statistics extended in version 5 of the kernel module
++ */
++typedef struct {
++    rmstime_t etime;          /* elapsed cpu time (milli-secs)       */
++    rmstime_t atime;          /* allocated cpu time (cpu milli-secs) */
++    rmstime_t utime;          /* user cpu time (cpu milli-secs)      */
++    rmstime_t stime;          /* system cpu time (cpu milli-secs)    */
++    int ncpus;                        /* number of cpus allocated            */
++    int flags;                        /* program status flags                */
++    int mem;                  /* max memory size in MBytes           */
++    int pageflts;             /* number of page faults               */
++    rmstime_t memint;         /* memory integral                     */
++} prgstats_old_t;
++
++typedef struct {
++    uint64_t etime;           /* elapsed cpu time (milli-secs)       */
++    uint64_t atime;           /* allocated cpu time (cpu milli-secs) */
++    uint64_t utime;           /* user cpu time (cpu milli-secs)      */
++    uint64_t stime;           /* system cpu time (cpu milli-secs)    */
++    uint64_t pageflts;                /* number of page faults               */
++    uint64_t memint;          /* memory integral                     */
++    uint64_t ebytes;          /* data transferred by the Elan(s)     */
++    uint64_t exfers;          /* number of Elan data transfers       */
++    uint64_t spare64[4];      /* expansion space                     */
++    int ncpus;                        /* number of cpus allocated            */
++    int flags;                        /* program status flags                */
++    int mem;                  /* max memory size in MBytes           */
++    int spare32[5];             /* expansion space                     */
++} prgstats_t;
++
++int  rmsmod_init(void);
++void rmsmod_fini(void);
++
++int rms_setcorepath(caddr_t path);
++int rms_getcorepath(pid_t pid, caddr_t path, int maxlen);
++int rms_prgcreate(int id, uid_t uid, int cpus);
++int rms_prgdestroy(int id);
++int rms_prgids(int maxids, int *prgids, int *nprgs);
++int rms_prginfo(int id, int maxpids, pid_t *pids, int *nprocs);
++int rms_prgaddcap(int id, int index, ELAN_CAPABILITY *cap);
++
++int rms_prgsuspend(int id);
++int rms_prgresume(int id);
++int rms_prgsignal(int id, int signo);
++
++int rms_getprgid(pid_t pid, int *id);
++int rms_ncaps(int *ncaps);
++int rms_getcap(int index, ELAN_CAPABILITY *cap);
++int rms_mycap(int *index);
++int rms_setcap(int index, int ctx);
++int rms_prefcap(int nprocess, int *index);
++
++int   rms_prggetstats(int id, prgstats_t *stats);
++void  rms_accumulatestats(prgstats_t *total, prgstats_t *stats);
++char *rms_statsreport(prgstats_t *stats, char *buf);
++
++int rms_elaninitdone(int vp);
++int rms_prgelanpids(int id, int maxpids, int *vps, pid_t *pids, int *npids);
++int rms_setelanstats(int id, uint64_t ebytes, uint64_t exfers);
++
++int rms_setpset(int psid);
++int rms_getpset(int id, int *psid);
++int rms_modversion();
++
++#ifdef __cplusplus
++}
++#endif
++
++
++#if defined(__KERNEL__)
++
++int rms_init(void);
++int rms_fini(void);
++int rms_reconfigure(void);
++
++extern int rms_debug;
++
++#if 1
++#define DBG(x) do if (rms_debug) x ; while (0)
++#else
++#define DBG(x)
++#endif
++
++#endif
++
++#endif /* RMSCALL_H_INCLUDED */
++
++
++
++
+Index: linux-2.6.5/include/rms/rmsio.h
+===================================================================
+--- linux-2.6.5.orig/include/rms/rmsio.h       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/include/rms/rmsio.h    2005-05-11 12:10:12.634902784 -0400
+@@ -0,0 +1,185 @@
++/*
++ *    Copyright (c) 1996-2002 by Quadrics Supercomputers World Ltd.
++ *
++ *    For licensing information please see the supplied COPYING file
++ *
++ */
++
++#ident "@(#)$Id: rmsio.h,v 1.6 2004/05/14 08:55:57 duncan Exp $"
++/*      $Source: /cvs/master/quadrics/rmsmod/rmsio.h,v $*/
++
++
++#ifndef __RMSMOD_RMSIO_H
++#define __RMSMOD_RMSIO_H
++
++/* arg is corepath string */
++#define RMSIO_SETCOREPATH     _IOW ('r', 1, char)
++
++typedef struct rmsio_getcorepath_struct
++{
++    pid_t             pid;
++    char              *corepath;
++    int                       maxlen;
++} RMSIO_GETCOREPATH_STRUCT;
++#define RMSIO_GETCOREPATH     _IOW ('r', 2, RMSIO_GETCOREPATH_STRUCT)
++
++typedef struct rmsio_prgcreate_struct
++{
++    int                       id;
++    uid_t             uid;
++    int                       cpus;
++} RMSIO_PRGCREATE_STRUCT;
++#define RMSIO_PRGCREATE               _IOW ('r', 3, RMSIO_PRGCREATE_STRUCT)
++
++typedef struct rmsio_prginfo_struct
++{
++    int                       id;
++    int                       maxpids;
++    pid_t             *pids;
++    int                       *nprocs;
++} RMSIO_PRGINFO_STRUCT;
++#define RMSIO_PRGINFO         _IOW ('r', 4, RMSIO_PRGINFO_STRUCT)
++
++typedef struct rmsio_prgsignal_struct
++{
++    int                       id;
++    int                       signo;
++} RMSIO_PRGSIGNAL_STRUCT;
++#define RMSIO_PRGSIGNAL               _IOW ('r', 5, RMSIO_PRGSIGNAL_STRUCT)
++
++typedef struct rmsio_prgaddcap_struct
++{
++    int                       id;
++    int                       index;
++    ELAN_CAPABILITY   *cap;
++} RMSIO_PRGADDCAP_STRUCT;
++#define RMSIO_PRGADDCAP               _IOW ('r', 6, RMSIO_PRGADDCAP_STRUCT)
++typedef struct rmsio_setcap_struct
++{
++    int                       index;
++    int                       ctx;
++} RMSIO_SETCAP_STRUCT;
++#define RMSIO_SETCAP          _IOW ('r', 7, RMSIO_SETCAP_STRUCT)
++
++typedef struct rmsio_getcap_struct
++{
++    int                       index;
++    ELAN_CAPABILITY     *cap;
++} RMSIO_GETCAP_STRUCT;
++#define RMSIO_GETCAP          _IOW ('r', 8, RMSIO_GETCAP_STRUCT)
++
++typedef struct rmsio_getcap_struct32
++{
++    int                       index;
++    unsigned int        capptr;
++} RMSIO_GETCAP_STRUCT32;
++#define RMSIO_GETCAP32                _IOW ('r', 8, RMSIO_GETCAP_STRUCT32)
++
++/* arg is pointer to ncaps */
++#define RMSIO_NCAPS           _IOW ('r', 9, int)
++
++typedef struct rmsio_prggetstats_struct
++{
++    int                       id;
++    prgstats_old_t    *stats;
++} RMSIO_PRGGETSTATS_STRUCT;
++#define RMSIO_PRGGETSTATS     _IOW ('r', 10, RMSIO_PRGGETSTATS_STRUCT)
++
++/* arg is program id */
++#define RMSIO_PRGSUSPEND      _IOW ('r', 11, int)
++#define RMSIO_PRGRESUME               _IOW ('r', 12, int)
++#define RMSIO_PRGDESTROY      _IOW ('r', 13, int)
++
++typedef struct rmsio_getprgid_struct
++{
++    pid_t             pid;
++    int                       *id;
++} RMSIO_GETPRGID_STRUCT;
++#define RMSIO_GETPRGID                _IOW ('r', 14, RMSIO_GETPRGID_STRUCT)
++
++typedef struct rmsio_getprgid_struct32
++{
++    pid_t             pid;
++    unsigned int      idptr;
++} RMSIO_GETPRGID_STRUCT32;
++#define RMSIO_GETPRGID32      _IOW ('r', 14, RMSIO_GETPRGID_STRUCT32)
++
++/* arg is pointer to index */
++#define RMSIO_GETMYCAP                _IOW ('r', 15, int)
++
++typedef struct rmsio_prgids_struct
++{
++    int                       maxids;
++    int                       *prgids;
++    int                       *nprgs;
++} RMSIO_PRGIDS_STRUCT;
++#define RMSIO_PRGIDS          _IOW ('r', 16, RMSIO_PRGIDS_STRUCT)
++
++/* arg is pointer to vp */
++#define RMSIO_ELANINITDONE    _IOW ('r', 17, int)
++
++typedef struct rmsio_prgelanpids_struct
++{
++    int    id;
++    int    maxpids;
++    int   *vps;
++    int   *pids;
++    int   *npids;
++} RMSIO_PRGELANPIDS_STRUCT;
++#define RMSIO_PRGELANPIDS     _IOW ('r', 18, RMSIO_PRGELANPIDS_STRUCT)
++
++typedef struct rmsio_setpset_struct
++{
++    int    id;
++    int    psid;
++} RMSIO_SETPSET_STRUCT;
++#define RMSIO_SETPSET         _IOW ('r', 19, RMSIO_SETPSET_STRUCT)
++
++typedef struct rmsio_getpset_struct
++{
++    int    id;
++    int   *psid;
++} RMSIO_GETPSET_STRUCT;
++#define RMSIO_GETPSET         _IOW ('r', 20, RMSIO_GETPSET_STRUCT)
++
++/*
++ * have to pass a pointer to the stats, the switch
++ * statement goes wrong in the module of the size
++ * is too large
++ */
++typedef struct {
++    uint64_t ebytes;
++    uint64_t exfers;
++} elanstats_t;
++
++typedef struct rmsio_setelanstats_struct
++{
++    int    id;
++    elanstats_t *estats;
++} RMSIO_SETELANSTATS_STRUCT;
++#define RMSIO_SETELANSTATS      _IOW ('r', 21, RMSIO_SETELANSTATS_STRUCT)
++
++typedef struct rmsio_prggetstats2_struct
++{
++    int                       id;
++    prgstats_t                *stats;
++} RMSIO_PRGGETSTATS2_STRUCT;
++#define RMSIO_PRGGETSTATS2    _IOW ('r', 22, RMSIO_PRGGETSTATS2_STRUCT)
++
++typedef struct rmsio_modversion_struct
++{
++    int *version;
++} RMSIO_MODVERSION_STRUCT;
++#define RMSIO_MODVERSION      _IOW ('r', 23, RMSIO_MODVERSION_STRUCT)
++
++
++#endif /* __RMSMOD_RMSIO_H */
++
++
++
++
++
++
++
++
++
+Index: linux-2.6.5/ipc/shm.c
+===================================================================
+--- linux-2.6.5.orig/ipc/shm.c 2005-02-01 16:55:41.000000000 -0500
++++ linux-2.6.5/ipc/shm.c      2005-05-11 12:10:12.634902784 -0400
+@@ -27,6 +27,7 @@
+ #include <linux/shmem_fs.h>
+ #include <linux/security.h>
+ #include <linux/audit.h>
++#include <linux/module.h>
+ #include <linux/trigevent_hooks.h>
+ #include <asm/uaccess.h>
+@@ -871,6 +872,44 @@
+       return audit_result(retval);
+ }
++/*
++ * Mark all segments created by this process for destruction
++ */
++int shm_cleanup (void)
++{
++      int i;
++
++      down(&shm_ids.sem);
++
++      for (i = 0; i <= shm_ids.max_id; i++) {
++              struct shmid_kernel *shp;
++
++              shp = shm_lock(i);
++              if (shp != NULL) {
++                      /* mark this segment for destruction if we created it */
++                      if (current->pid == shp->shm_cprid)
++                      {
++                              /* copy of IPC_RMID code */
++                              if (shp->shm_nattch) {
++                                      shp->shm_flags |= SHM_DEST;
++                                      /* do not find it any more */
++                                      shp->shm_perm.key = IPC_PRIVATE;
++                              } else {
++                                      shm_destroy(shp);
++                                      continue;
++                              }
++                      }
++
++                      shm_unlock(shp);
++              }
++      }
++
++      up(&shm_ids.sem);
++
++      return 0;
++}
++EXPORT_SYMBOL_GPL(shm_cleanup);
++
+ #ifdef CONFIG_PROC_FS
+ static int sysvipc_shm_read_proc(char *buffer, char **start, off_t offset, int length, int *eof, void *data)
+ {
+Index: linux-2.6.5/kernel/exit.c
+===================================================================
+--- linux-2.6.5.orig/kernel/exit.c     2005-02-01 16:56:07.000000000 -0500
++++ linux-2.6.5/kernel/exit.c  2005-05-11 12:10:12.684895184 -0400
+@@ -40,6 +40,8 @@
+ /* tng related changes */
+ int (*tng_exitfunc)(int) = NULL;
++#include <linux/ptrack.h>
++
+ extern void sem_exit (void);
+ extern struct task_struct *child_reaper;
+ void (*do_eop_acct) (int, struct task_struct *);
+@@ -840,6 +842,8 @@
+               audit_exit(tsk, code);
+       audit_free(tsk->audit);
+ #endif
++      /* Notify any ptrack callbacks of the process exit */
++      ptrack_call_callbacks (PTRACK_PHASE_EXIT, NULL);
+       __exit_mm(tsk);
+       if (unlikely(tng_exitfunc))
+Index: linux-2.6.5/kernel/fork.c
+===================================================================
+--- linux-2.6.5.orig/kernel/fork.c     2005-02-01 16:56:06.000000000 -0500
++++ linux-2.6.5/kernel/fork.c  2005-05-11 12:10:12.685895032 -0400
+@@ -14,6 +14,7 @@
+ #include <linux/config.h>
+ #include <linux/slab.h>
+ #include <linux/init.h>
++#include <linux/ptrack.h>
+ #include <linux/unistd.h>
+ #include <linux/smp_lock.h>
+ #include <linux/module.h>
+@@ -432,6 +433,9 @@
+       mm->page_table_lock = SPIN_LOCK_UNLOCKED;
+       mm->ioctx_list_lock = RW_LOCK_UNLOCKED;
+       mm->ioctx_list = NULL;
++#ifdef CONFIG_IOPROC
++      mm->ioproc_ops = NULL;
++#endif
+       mm->default_kioctx = (struct kioctx)INIT_KIOCTX(mm->default_kioctx, *mm);
+       mm->free_area_cache = TASK_UNMAPPED_BASE;
+@@ -1267,6 +1271,11 @@
+                     audit_fork(current, p);
+ #endif
++              if (ptrack_call_callbacks(PTRACK_PHASE_CLONE, p)) {
++                      sigaddset(&p->pending.signal, SIGKILL);
++                      set_tsk_thread_flag(p, TIF_SIGPENDING);
++              }
++
+               /* Trace the event  */
+               TRIG_EVENT(fork_hook, clone_flags, p, pid);
+               if (!(clone_flags & CLONE_STOPPED)) {
+Index: linux-2.6.5/kernel/Kconfig
+===================================================================
+--- linux-2.6.5.orig/kernel/Kconfig    2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/kernel/Kconfig 2005-05-11 12:10:12.685895032 -0400
+@@ -0,0 +1,14 @@
++#
++# Kernel subsystem specific config
++# 
++
++# Support for Process Tracking callbacks
++#
++config PTRACK
++      bool "Enable PTRACK process tracking hooks"
++      default y
++      help
++      This option enables hooks to be called when processes are
++      created and destoryed in order for a resource management 
++      system to know which processes are a member of a "job" and 
++      to be able to clean up when the job is terminated.
+Index: linux-2.6.5/kernel/Makefile
+===================================================================
+--- linux-2.6.5.orig/kernel/Makefile   2005-05-11 12:10:11.148128808 -0400
++++ linux-2.6.5/kernel/Makefile        2005-05-11 12:10:12.685895032 -0400
+@@ -26,6 +26,7 @@
+ obj-$(CONFIG_EVLOG) += evlbuf.o evlapi.o evlposix.o
+ obj-$(CONFIG_HOOK) += hook.o
+ obj-$(CONFIG_TRIGEVENT_HOOKS) += trigevent_hooks.o
++obj-$(CONFIG_PTRACK) += ptrack.o
+ obj-$(CONFIG_LTT) += ltt/
+ obj-$(CONFIG_KPROBES) += kprobes.o
+ obj-$(CONFIG_CPUSETS) += cpuset.o
+Index: linux-2.6.5/kernel/ptrack.c
+===================================================================
+--- linux-2.6.5.orig/kernel/ptrack.c   2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/kernel/ptrack.c        2005-05-11 12:10:12.686894880 -0400
+@@ -0,0 +1,145 @@
++/*
++ *    Copyright (C) 2000  Regents of the University of California
++ *
++ *    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.
++ *
++ *    This program is distributed in the hope that it will be useful,
++ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *    GNU General Public License for more details.
++ *
++ *    You should have received a copy of the GNU General Public License
++ *    along with this program; if not, write to the Free Software
++ *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ *
++ * Derived from exit_actn.c by
++ *    Copyright (C) 2003 Quadrics Ltd.
++ */
++
++
++#include <linux/module.h>
++#include <linux/spinlock.h>
++#include <linux/sched.h>
++#include <linux/ptrack.h>
++#include <linux/slab.h>
++#include <linux/list.h>
++
++#include <asm/errno.h>
++
++int
++ptrack_register (ptrack_callback_t callback, void *arg)
++{
++       struct ptrack_desc *desc = kmalloc (sizeof (struct ptrack_desc), GFP_KERNEL);
++       
++       if (desc == NULL)
++               return -ENOMEM;
++
++      desc->callback = callback;
++      desc->arg      = arg;
++       
++       list_add_tail (&desc->link, &current->ptrack_list);
++       
++       return 0;
++}
++
++void
++ptrack_deregister (ptrack_callback_t callback, void *arg)
++{      
++       struct list_head *el, *nel;
++       
++       list_for_each_safe (el, nel, &current->ptrack_list) {
++               struct ptrack_desc *desc = list_entry (el, struct ptrack_desc, link);
++               
++               if (desc->callback == callback && desc->arg == arg) {
++                       list_del (&desc->link);
++                       kfree (desc);
++               }
++       }
++}
++
++int
++ptrack_registered (ptrack_callback_t callback, void *arg)
++{
++       struct list_head *el;
++       
++       list_for_each (el, &current->ptrack_list) {
++               struct ptrack_desc *desc = list_entry (el, struct ptrack_desc, link);
++               
++               if (desc->callback == callback && desc->arg == arg)
++                       return 1;
++       }
++       return 0;
++}      
++        
++int
++ptrack_call_callbacks (int phase, struct task_struct *child)
++{
++       struct list_head *el, *nel;
++       struct ptrack_desc *new;
++      int res;
++
++       if (phase == PTRACK_PHASE_CLONE)
++               INIT_LIST_HEAD (&child->ptrack_list);
++
++       list_for_each_safe (el, nel, &current->ptrack_list) {
++               struct ptrack_desc *desc = list_entry (el, struct ptrack_desc, link);
++               
++             res = desc->callback (desc->arg, phase, child);
++               
++               switch (phase)
++               {
++               case PTRACK_PHASE_EXIT:
++                       list_del (&desc->link);
++                       kfree (desc);
++                       break;
++                       
++               case PTRACK_PHASE_CLONE:
++                     switch (res)
++                     {
++                     case PTRACK_FINISHED:
++                             break;
++
++                     case PTRACK_INNHERIT:
++                             if ((new = kmalloc (sizeof (struct ptrack_desc), GFP_ATOMIC)) == NULL)
++                             {
++                                     /* allocation failed - notify that this process is not going
++                                      * to be started by signalling clone failure.
++                                      */
++                                     desc->callback (desc->arg, PTRACK_PHASE_CLONE_FAIL, child);
++                                     
++                                     goto failed;
++                             }
++
++                              new->callback = desc->callback;
++                              new->arg      = desc->arg;
++                               
++                               list_add_tail (&new->link, &child->ptrack_list);
++                             break;
++
++                     case PTRACK_DENIED:
++                             goto failed;
++                       }
++                     break;
++               }
++       }
++
++       return 0;
++
++ failed:
++       while (! list_empty (&child->ptrack_list))
++       {
++             struct ptrack_desc *desc = list_entry (child->ptrack_list.next, struct ptrack_desc, link);
++             
++             desc->callback (desc->arg, PTRACK_PHASE_CLONE_FAIL, child);
++
++             list_del (&desc->link);
++             kfree (desc);
++       }
++       return 1;
++}
++EXPORT_SYMBOL(ptrack_register);
++EXPORT_SYMBOL(ptrack_deregister);
++EXPORT_SYMBOL(ptrack_registered);
+Index: linux-2.6.5/kernel/signal.c
+===================================================================
+--- linux-2.6.5.orig/kernel/signal.c   2005-02-01 16:56:05.000000000 -0500
++++ linux-2.6.5/kernel/signal.c        2005-05-11 12:10:12.688894576 -0400
+@@ -2271,6 +2271,7 @@
+       read_unlock(&tasklist_lock);
+       return audit_lresult(error);
+ }
++EXPORT_SYMBOL_GPL(sys_kill);
+ asmlinkage long
+ sys_rt_sigqueueinfo(int pid, int sig, siginfo_t __user *uinfo)
+Index: linux-2.6.5/mm/fremap.c
+===================================================================
+--- linux-2.6.5.orig/mm/fremap.c       2005-02-01 16:55:36.000000000 -0500
++++ linux-2.6.5/mm/fremap.c    2005-05-11 12:10:12.688894576 -0400
+@@ -14,6 +14,7 @@
+ #include <linux/swapops.h>
+ #include <linux/objrmap.h>
+ #include <linux/module.h>
++#include <linux/ioproc.h>
+ #include <asm/mmu_context.h>
+ #include <asm/cacheflush.h>
+@@ -29,6 +30,7 @@
+       if (pte_present(pte)) {
+               unsigned long pfn = pte_pfn(pte);
++              ioproc_invalidate_page(vma, addr);
+               flush_cache_page(vma, addr);
+               pte = ptep_clear_flush(vma, addr, ptep);
+               if (pfn_valid(pfn)) {
+@@ -80,6 +82,7 @@
+       pte_val = *pte;
+       pte_unmap(pte);
+       update_mmu_cache(vma, addr, pte_val);
++      ioproc_update_page(vma, addr);
+       err = 0;
+ err_unlock:
+@@ -118,6 +121,7 @@
+       pte_val = *pte;
+       pte_unmap(pte);
+       update_mmu_cache(vma, addr, pte_val);
++      ioproc_update_page(vma, addr);
+       spin_unlock(&mm->page_table_lock);
+       return 0;
+Index: linux-2.6.5/mm/ioproc.c
+===================================================================
+--- linux-2.6.5.orig/mm/ioproc.c       2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/mm/ioproc.c    2005-05-11 12:10:12.688894576 -0400
+@@ -0,0 +1,58 @@
++/* -*- linux-c -*-
++ *
++ *    Copyright (C) 2002-2004 Quadrics Ltd.
++ *
++ *    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.
++ *
++ *    This program is distributed in the hope that it will be useful,
++ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *    GNU General Public License for more details.
++ *
++ *    You should have received a copy of the GNU General Public License
++ *    along with this program; if not, write to the Free Software
++ *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ *
++ *
++ */
++
++/*
++ * Registration for IO processor page table updates.
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++
++#include <linux/mm.h>
++#include <linux/ioproc.h>
++
++int
++ioproc_register_ops(struct mm_struct *mm, struct ioproc_ops *ip)
++{
++      ip->next = mm->ioproc_ops;
++      mm->ioproc_ops = ip;
++
++      return 0;
++}
++
++EXPORT_SYMBOL_GPL(ioproc_register_ops);
++
++int
++ioproc_unregister_ops(struct mm_struct *mm, struct ioproc_ops *ip)
++{
++      struct ioproc_ops **tmp;
++
++      for (tmp = &mm->ioproc_ops; *tmp && *tmp != ip; tmp= &(*tmp)->next)
++              ;
++      if (*tmp) {
++              *tmp = ip->next;
++              return 0;
++      }
++
++      return -EINVAL;
++}
++
++EXPORT_SYMBOL_GPL(ioproc_unregister_ops);
+Index: linux-2.6.5/mm/Kconfig
+===================================================================
+--- linux-2.6.5.orig/mm/Kconfig        2004-02-23 16:02:56.000000000 -0500
++++ linux-2.6.5/mm/Kconfig     2005-05-11 12:10:12.689894424 -0400
+@@ -0,0 +1,15 @@
++#
++# VM subsystem specific config
++# 
++
++# Support for IO processors which have advanced RDMA capabilities
++#
++config IOPROC
++      bool "Enable IOPROC VM hooks"
++      depends on MMU
++      default y
++      help
++      This option enables hooks in the VM subsystem so that IO devices which
++      incorporate advanced RDMA capabilities can be kept in sync with CPU 
++      page table changes.
++      See Documentation/vm/ioproc.txt for more details.
+Index: linux-2.6.5/mm/Makefile
+===================================================================
+--- linux-2.6.5.orig/mm/Makefile       2005-02-01 16:55:36.000000000 -0500
++++ linux-2.6.5/mm/Makefile    2005-05-11 12:10:12.689894424 -0400
+@@ -15,4 +15,5 @@
+ obj-$(CONFIG_SWAP)    += page_io.o swap_state.o swapfile.o
+ obj-$(CONFIG_PROC_MM) += proc_mm.o
+ obj-$(CONFIG_NUMA)    += policy.o
++obj-$(CONFIG_IOPROC)    += ioproc.o
+Index: linux-2.6.5/mm/memory.c
+===================================================================
+--- linux-2.6.5.orig/mm/memory.c       2005-02-01 16:56:06.000000000 -0500
++++ linux-2.6.5/mm/memory.c    2005-05-11 12:10:12.691894120 -0400
+@@ -43,6 +43,7 @@
+ #include <linux/swap.h>
+ #include <linux/highmem.h>
+ #include <linux/pagemap.h>
++#include <linux/ioproc.h>
+ #include <linux/objrmap.h>
+ #include <linux/module.h>
+ #include <linux/acct.h>
+@@ -630,6 +631,7 @@
+       lru_add_drain();
+       spin_lock(&mm->page_table_lock);
++      ioproc_invalidate_range(vma, address, end);
+       tlb = tlb_gather_mmu(mm, 0);
+       unmap_vmas(&tlb, mm, vma, address, end, &nr_accounted, details);
+       tlb_finish_mmu(tlb, address, end);
+@@ -936,6 +938,7 @@
+               BUG();
+       spin_lock(&mm->page_table_lock);
++      ioproc_invalidate_range(vma, beg, end);
+       do {
+               pmd_t *pmd = pmd_alloc(mm, dir, address);
+               error = -ENOMEM;
+@@ -950,6 +953,7 @@
+       /*
+        * Why flush? zeromap_pte_range has a BUG_ON for !pte_none()
+        */
++      ioproc_update_range(vma, beg, end);
+       flush_tlb_range(vma, beg, end);
+       spin_unlock(&mm->page_table_lock);
+       return error;
+@@ -1020,6 +1024,7 @@
+               BUG();
+       spin_lock(&mm->page_table_lock);
++      ioproc_invalidate_range(vma, beg, end);
+       do {
+               pmd_t *pmd = pmd_alloc(mm, dir, from);
+               error = -ENOMEM;
+@@ -1034,6 +1039,7 @@
+       /*
+        * Why flush? remap_pte_range has a BUG_ON for !pte_none()
+        */
++      ioproc_update_range(vma, beg, end);
+       flush_tlb_range(vma, beg, end);
+       spin_unlock(&mm->page_table_lock);
+       return error;
+@@ -1120,6 +1126,7 @@
+                       ptep_establish(vma, address, page_table, entry);
+                       update_mmu_cache(vma, address, entry);
+                       pte_unmap(page_table);
++                      ioproc_update_page(vma, address);
+                       spin_unlock(&mm->page_table_lock);
+                       return VM_FAULT_MINOR;
+               }
+@@ -1155,6 +1162,7 @@
+               }
+               page_remove_rmap(old_page);
++              ioproc_invalidate_page(vma, address);
+               break_cow(vma, new_page, address, page_table);
+               page_add_rmap(new_page, vma, address, 1);
+               lru_cache_add_active(new_page);
+@@ -1163,6 +1171,7 @@
+               new_page = old_page;
+       }
+       pte_unmap(page_table);
++      ioproc_update_page(vma, address);
+       page_cache_release(new_page);
+       page_cache_release(old_page);
+       spin_unlock(&mm->page_table_lock);
+@@ -1469,6 +1478,7 @@
+       /* No need to invalidate - it was non-present before */
+       update_mmu_cache(vma, address, pte);
+       pte_unmap(page_table);
++      ioproc_update_page(vma, address);
+       spin_unlock(&mm->page_table_lock);
+ out:
+       return ret;
+@@ -1530,6 +1540,7 @@
+       /* No need to invalidate - it was non-present before */
+       update_mmu_cache(vma, addr, entry);
++      ioproc_update_page(vma, addr);
+       spin_unlock(&mm->page_table_lock);
+       ret = VM_FAULT_MINOR;
+@@ -1669,6 +1680,7 @@
+       /* no need to invalidate: a not-present page shouldn't be cached */
+       update_mmu_cache(vma, address, entry);
++      ioproc_update_page(vma, address);
+       spin_unlock(&mm->page_table_lock);
+  out:
+       return ret;
+@@ -1768,6 +1780,7 @@
+       spin_unlock(&mm->page_table_lock);
+       return VM_FAULT_MINOR;
+ }
++EXPORT_SYMBOL(make_pages_present);
+ /* Can be overwritten by the architecture */
+Index: linux-2.6.5/mm/mmap.c
+===================================================================
+--- linux-2.6.5.orig/mm/mmap.c 2005-02-01 16:56:10.000000000 -0500
++++ linux-2.6.5/mm/mmap.c      2005-05-11 12:10:12.692893968 -0400
+@@ -25,6 +25,7 @@
+ #include <linux/init.h>
+ #include <linux/file.h>
+ #include <linux/fs.h>
++#include <linux/ioproc.h>
+ #include <linux/personality.h>
+ #include <linux/security.h>
+ #include <linux/hugetlb.h>
+@@ -1378,6 +1379,7 @@
+       unsigned long nr_accounted = 0;
+       lru_add_drain();
++      ioproc_invalidate_range(vma, start, end);
+       tlb = tlb_gather_mmu(mm, 0);
+       unmap_vmas(&tlb, mm, vma, start, end, &nr_accounted, NULL);
+       vm_unacct_memory(nr_accounted);
+@@ -1697,6 +1699,7 @@
+       spin_lock(&mm->page_table_lock);
++      ioproc_release(mm);
+       tlb = tlb_gather_mmu(mm, 1);
+       flush_cache_mm(mm);
+       /* Use ~0UL here to ensure all VMAs in the mm are unmapped */
+Index: linux-2.6.5/mm/mprotect.c
+===================================================================
+--- linux-2.6.5.orig/mm/mprotect.c     2005-02-01 16:55:59.000000000 -0500
++++ linux-2.6.5/mm/mprotect.c  2005-05-11 12:10:12.692893968 -0400
+@@ -10,6 +10,7 @@
+ #include <linux/mm.h>
+ #include <linux/hugetlb.h>
++#include <linux/ioproc.h>
+ #include <linux/slab.h>
+ #include <linux/shm.h>
+ #include <linux/mman.h>
+@@ -99,6 +100,7 @@
+       if (start >= end)
+               BUG();
+       spin_lock(&current->mm->page_table_lock);
++      ioproc_change_protection(vma, start, end, newprot);
+       do {
+               change_pmd_range(dir, start, end - start, newprot);
+               start = (start + PGDIR_SIZE) & PGDIR_MASK;
+Index: linux-2.6.5/mm/mremap.c
+===================================================================
+--- linux-2.6.5.orig/mm/mremap.c       2005-02-01 16:56:02.000000000 -0500
++++ linux-2.6.5/mm/mremap.c    2005-05-11 12:10:12.693893816 -0400
+@@ -9,6 +9,7 @@
+ #include <linux/mm.h>
+ #include <linux/hugetlb.h>
++#include <linux/ioproc.h>
+ #include <linux/slab.h>
+ #include <linux/shm.h>
+ #include <linux/mman.h>
+@@ -144,6 +145,8 @@
+ {
+       unsigned long offset = len;
++      ioproc_invalidate_range(vma, old_addr, old_addr + len);
++      ioproc_invalidate_range(vma, new_addr, new_addr + len);
+       flush_cache_range(vma, old_addr, old_addr + len);
+       /*
+Index: linux-2.6.5/mm/msync.c
+===================================================================
+--- linux-2.6.5.orig/mm/msync.c        2005-02-01 16:55:36.000000000 -0500
++++ linux-2.6.5/mm/msync.c     2005-05-11 12:10:12.693893816 -0400
+@@ -12,6 +12,7 @@
+ #include <linux/mm.h>
+ #include <linux/mman.h>
+ #include <linux/hugetlb.h>
++#include <linux/ioproc.h>
+ #include <asm/pgtable.h>
+ #include <asm/pgalloc.h>
+@@ -116,6 +117,7 @@
+       if (address >= end)
+               BUG();
++      ioproc_sync_range(vma, address, end);
+       do {
+               error |= filemap_sync_pmd_range(dir, address, end, vma, flags);
+               address = (address + PGDIR_SIZE) & PGDIR_MASK;
+Index: linux-2.6.5/mm/objrmap.c
+===================================================================
+--- linux-2.6.5.orig/mm/objrmap.c      2005-02-01 16:56:06.000000000 -0500
++++ linux-2.6.5/mm/objrmap.c   2005-05-11 12:10:12.694893664 -0400
+@@ -29,6 +29,7 @@
+ #include <linux/swapops.h>
+ #include <linux/objrmap.h>
+ #include <linux/init.h>
++#include <linux/ioproc.h>
+ #include <asm/tlbflush.h>
+ kmem_cache_t * anon_vma_cachep;
+@@ -393,6 +394,8 @@
+ {
+       pte_t pteval;
++      ioproc_invalidate_page(vma, address);
++
+       flush_cache_page(vma, address);
+       pteval = ptep_clear_flush(vma, address, pte);
index 2dae026..381d490 100644 (file)
@@ -1,7 +1,7 @@
 Index: linux-2.4.21/crypto/cipher.c
 ===================================================================
---- linux-2.4.21.orig/crypto/cipher.c  2004-12-21 13:51:10.000000000 -0500
-+++ linux-2.4.21/crypto/cipher.c       2004-12-21 13:58:10.000000000 -0500
+--- linux-2.4.21.orig/crypto/cipher.c  2005-06-01 22:51:50.000000000 -0400
++++ linux-2.4.21/crypto/cipher.c       2005-06-01 23:07:51.067582960 -0400
 @@ -88,12 +88,21 @@
  
        walk->sg = sg;
@@ -26,8 +26,8 @@ Index: linux-2.4.21/crypto/cipher.c
  static void scatterwalk_map(struct scatter_walk *walk, int out)
 Index: linux-2.4.21/crypto/digest.c
 ===================================================================
---- linux-2.4.21.orig/crypto/digest.c  2004-12-21 13:51:10.000000000 -0500
-+++ linux-2.4.21/crypto/digest.c       2004-12-21 13:58:10.000000000 -0500
+--- linux-2.4.21.orig/crypto/digest.c  2005-06-01 22:51:50.000000000 -0400
++++ linux-2.4.21/crypto/digest.c       2005-06-01 23:07:51.068582808 -0400
 @@ -29,7 +29,11 @@
        unsigned int i;
        
@@ -54,8 +54,8 @@ Index: linux-2.4.21/crypto/digest.c
                crypto_kunmap(p, 0);
 Index: linux-2.4.21/crypto/hmac.c
 ===================================================================
---- linux-2.4.21.orig/crypto/hmac.c    2004-12-21 13:51:10.000000000 -0500
-+++ linux-2.4.21/crypto/hmac.c 2004-12-21 13:58:10.000000000 -0500
+--- linux-2.4.21.orig/crypto/hmac.c    2005-06-01 22:51:50.000000000 -0400
++++ linux-2.4.21/crypto/hmac.c 2005-06-01 23:07:51.068582808 -0400
 @@ -25,8 +25,14 @@
  {
        struct scatterlist tmp;
@@ -116,8 +116,8 @@ Index: linux-2.4.21/crypto/hmac.c
        crypto_digest_update(tfm, &tmp, 1);
 Index: linux-2.4.21/crypto/tcrypt.c
 ===================================================================
---- linux-2.4.21.orig/crypto/tcrypt.c  2004-12-21 13:51:10.000000000 -0500
-+++ linux-2.4.21/crypto/tcrypt.c       2004-12-21 13:58:10.000000000 -0500
+--- linux-2.4.21.orig/crypto/tcrypt.c  2005-06-01 22:51:50.000000000 -0400
++++ linux-2.4.21/crypto/tcrypt.c       2005-06-01 23:07:51.071582352 -0400
 @@ -24,6 +24,15 @@
  #include <linux/highmem.h>
  #include "tcrypt.h"
@@ -1189,8 +1189,8 @@ Index: linux-2.4.21/crypto/tcrypt.c
                printk("%s\n", memcmp(q, c5_tv[i].ciphertext,
 Index: linux-2.4.21/drivers/ide/ide-dma.c
 ===================================================================
---- linux-2.4.21.orig/drivers/ide/ide-dma.c    2004-12-21 13:51:31.000000000 -0500
-+++ linux-2.4.21/drivers/ide/ide-dma.c 2004-12-21 13:58:10.000000000 -0500
+--- linux-2.4.21.orig/drivers/ide/ide-dma.c    2005-06-01 22:52:05.000000000 -0400
++++ linux-2.4.21/drivers/ide/ide-dma.c 2005-06-01 23:07:51.072582200 -0400
 @@ -280,14 +280,25 @@
                memset(&sg[nents], 0, sizeof(*sg));
  
@@ -1244,8 +1244,8 @@ Index: linux-2.4.21/drivers/ide/ide-dma.c
  #else
 Index: linux-2.4.21/drivers/scsi/dpt_i2o.c
 ===================================================================
---- linux-2.4.21.orig/drivers/scsi/dpt_i2o.c   2004-12-21 13:51:20.000000000 -0500
-+++ linux-2.4.21/drivers/scsi/dpt_i2o.c        2004-12-21 13:58:10.000000000 -0500
+--- linux-2.4.21.orig/drivers/scsi/dpt_i2o.c   2005-06-01 22:51:54.000000000 -0400
++++ linux-2.4.21/drivers/scsi/dpt_i2o.c        2005-06-01 23:07:51.074581896 -0400
 @@ -2152,7 +2152,13 @@
                for(i = 0 ; i < cmd->use_sg; i++) {
                        *mptr++ = direction|0x10000000|sg->length;
@@ -1263,7 +1263,7 @@ Index: linux-2.4.21/drivers/scsi/dpt_i2o.c
 Index: linux-2.4.21/drivers/scsi/scsi_debug.c
 ===================================================================
 --- linux-2.4.21.orig/drivers/scsi/scsi_debug.c        2002-11-28 18:53:14.000000000 -0500
-+++ linux-2.4.21/drivers/scsi/scsi_debug.c     2004-12-21 13:58:10.000000000 -0500
++++ linux-2.4.21/drivers/scsi/scsi_debug.c     2005-06-01 23:07:51.075581744 -0400
 @@ -186,7 +186,13 @@
                struct scatterlist *sgpnt = (struct scatterlist *)
                                                SCpnt->request_buffer;
@@ -1336,8 +1336,8 @@ Index: linux-2.4.21/drivers/scsi/scsi_debug.c
                } else if (nbytes > 0)
 Index: linux-2.4.21/drivers/scsi/scsi_lib.c
 ===================================================================
---- linux-2.4.21.orig/drivers/scsi/scsi_lib.c  2004-12-21 13:51:31.000000000 -0500
-+++ linux-2.4.21/drivers/scsi/scsi_lib.c       2004-12-21 13:58:10.000000000 -0500
+--- linux-2.4.21.orig/drivers/scsi/scsi_lib.c  2005-06-01 22:52:05.000000000 -0400
++++ linux-2.4.21/drivers/scsi/scsi_lib.c       2005-06-01 23:07:51.076581592 -0400
 @@ -554,7 +554,13 @@
                if (bbpnt) {
                        for (i = 0; i < SCpnt->use_sg; i++) {
@@ -1378,8 +1378,8 @@ Index: linux-2.4.21/drivers/scsi/scsi_lib.c
                }
 Index: linux-2.4.21/drivers/scsi/scsi_merge.c
 ===================================================================
---- linux-2.4.21.orig/drivers/scsi/scsi_merge.c        2004-12-21 13:51:06.000000000 -0500
-+++ linux-2.4.21/drivers/scsi/scsi_merge.c     2004-12-21 13:58:10.000000000 -0500
+--- linux-2.4.21.orig/drivers/scsi/scsi_merge.c        2005-06-01 22:51:45.000000000 -0400
++++ linux-2.4.21/drivers/scsi/scsi_merge.c     2005-06-01 23:07:51.077581440 -0400
 @@ -144,11 +144,21 @@
         */
        for(jj=0; jj < SCpnt->use_sg; jj++)
@@ -1500,8 +1500,8 @@ Index: linux-2.4.21/drivers/scsi/scsi_merge.c
  
 Index: linux-2.4.21/drivers/scsi/sg.c
 ===================================================================
---- linux-2.4.21.orig/drivers/scsi/sg.c        2004-12-21 13:51:21.000000000 -0500
-+++ linux-2.4.21/drivers/scsi/sg.c     2004-12-21 13:58:10.000000000 -0500
+--- linux-2.4.21.orig/drivers/scsi/sg.c        2005-06-01 22:52:05.000000000 -0400
++++ linux-2.4.21/drivers/scsi/sg.c     2005-06-01 23:07:51.080580984 -0400
 @@ -1077,7 +1077,11 @@
  
          for (k = 0; k < rsv_schp->k_use_sg; ++k, ++sclp) {
@@ -1708,8 +1708,8 @@ Index: linux-2.4.21/drivers/scsi/sg.c
                    sfp->save_scat_len = num;
 Index: linux-2.4.21/drivers/scsi/sr.c
 ===================================================================
---- linux-2.4.21.orig/drivers/scsi/sr.c        2004-12-21 13:51:21.000000000 -0500
-+++ linux-2.4.21/drivers/scsi/sr.c     2004-12-21 13:58:10.000000000 -0500
+--- linux-2.4.21.orig/drivers/scsi/sr.c        2005-06-01 22:51:55.000000000 -0400
++++ linux-2.4.21/drivers/scsi/sr.c     2005-06-01 23:07:51.080580984 -0400
 @@ -343,7 +343,12 @@
  
        i = 0;
@@ -1752,7 +1752,7 @@ Index: linux-2.4.21/drivers/scsi/sr.c
 Index: linux-2.4.21/drivers/scsi/sim710.c
 ===================================================================
 --- linux-2.4.21.orig/drivers/scsi/sim710.c    2002-08-02 20:39:44.000000000 -0400
-+++ linux-2.4.21/drivers/scsi/sim710.c 2004-12-21 13:58:10.000000000 -0500
++++ linux-2.4.21/drivers/scsi/sim710.c 2005-06-01 23:07:51.082580680 -0400
 @@ -1164,7 +1164,11 @@
  
      for (i = 0; cmd->use_sg ? (i < cmd->use_sg) : !i; i++) {
@@ -1767,8 +1767,8 @@ Index: linux-2.4.21/drivers/scsi/sim710.c
        u32 cnt = cmd->use_sg ?
 Index: linux-2.4.21/drivers/scsi/advansys.c
 ===================================================================
---- linux-2.4.21.orig/drivers/scsi/advansys.c  2004-12-21 13:51:30.000000000 -0500
-+++ linux-2.4.21/drivers/scsi/advansys.c       2004-12-21 13:58:10.000000000 -0500
+--- linux-2.4.21.orig/drivers/scsi/advansys.c  2005-06-01 22:52:05.000000000 -0400
++++ linux-2.4.21/drivers/scsi/advansys.c       2005-06-01 23:07:51.097578400 -0400
 @@ -6803,7 +6803,11 @@
          slp = (struct scatterlist *) scp->request_buffer;
          for (sgcnt = 0; sgcnt < scp->use_sg; sgcnt++, slp++) {
@@ -1796,7 +1796,7 @@ Index: linux-2.4.21/drivers/scsi/advansys.c
 Index: linux-2.4.21/drivers/scsi/pci2000.c
 ===================================================================
 --- linux-2.4.21.orig/drivers/scsi/pci2000.c   2001-11-09 17:05:06.000000000 -0500
-+++ linux-2.4.21/drivers/scsi/pci2000.c        2004-12-21 13:58:10.000000000 -0500
++++ linux-2.4.21/drivers/scsi/pci2000.c        2005-06-01 23:07:51.098578248 -0400
 @@ -513,7 +513,11 @@
                        
                        if ( SCpnt->use_sg )
@@ -1824,7 +1824,7 @@ Index: linux-2.4.21/drivers/scsi/pci2000.c
 Index: linux-2.4.21/drivers/scsi/pci2220i.c
 ===================================================================
 --- linux-2.4.21.orig/drivers/scsi/pci2220i.c  2001-11-09 17:05:06.000000000 -0500
-+++ linux-2.4.21/drivers/scsi/pci2220i.c       2004-12-21 13:58:10.000000000 -0500
++++ linux-2.4.21/drivers/scsi/pci2220i.c       2005-06-01 23:07:51.100577944 -0400
 @@ -463,7 +463,11 @@
                        {
                        if ( padapter->nextSg < padapter->SCpnt->use_sg )
@@ -1852,7 +1852,7 @@ Index: linux-2.4.21/drivers/scsi/pci2220i.c
 Index: linux-2.4.21/drivers/scsi/BusLogic.c
 ===================================================================
 --- linux-2.4.21.orig/drivers/scsi/BusLogic.c  2001-12-21 12:41:55.000000000 -0500
-+++ linux-2.4.21/drivers/scsi/BusLogic.c       2004-12-21 13:58:10.000000000 -0500
++++ linux-2.4.21/drivers/scsi/BusLogic.c       2005-06-01 23:07:51.104577336 -0400
 @@ -3402,7 +3402,12 @@
          CCB->ScatterGatherList[Segment].SegmentByteCount =
            ScatterList[Segment].length;
@@ -1869,7 +1869,7 @@ Index: linux-2.4.21/drivers/scsi/BusLogic.c
 Index: linux-2.4.21/drivers/scsi/ultrastor.c
 ===================================================================
 --- linux-2.4.21.orig/drivers/scsi/ultrastor.c 2002-08-02 20:39:44.000000000 -0400
-+++ linux-2.4.21/drivers/scsi/ultrastor.c      2004-12-21 13:58:10.000000000 -0500
++++ linux-2.4.21/drivers/scsi/ultrastor.c      2005-06-01 23:07:51.105577184 -0400
 @@ -666,7 +666,11 @@
        sl = (struct scatterlist *) SCpnt->request_buffer;
        max = SCpnt->use_sg;
@@ -1885,7 +1885,7 @@ Index: linux-2.4.21/drivers/scsi/ultrastor.c
 Index: linux-2.4.21/drivers/scsi/aha152x.c
 ===================================================================
 --- linux-2.4.21.orig/drivers/scsi/aha152x.c   2003-06-13 10:51:36.000000000 -0400
-+++ linux-2.4.21/drivers/scsi/aha152x.c        2004-12-21 13:58:10.000000000 -0500
++++ linux-2.4.21/drivers/scsi/aha152x.c        2005-06-01 23:07:51.108576728 -0400
 @@ -603,7 +603,11 @@
  #define SCSEM(SCpnt)          SCDATA(SCpnt)->sem
  
@@ -1901,7 +1901,7 @@ Index: linux-2.4.21/drivers/scsi/aha152x.c
 Index: linux-2.4.21/drivers/scsi/aha1542.c
 ===================================================================
 --- linux-2.4.21.orig/drivers/scsi/aha1542.c   2001-10-12 18:35:53.000000000 -0400
-+++ linux-2.4.21/drivers/scsi/aha1542.c        2004-12-21 13:58:10.000000000 -0500
++++ linux-2.4.21/drivers/scsi/aha1542.c        2005-06-01 23:07:51.109576576 -0400
 @@ -69,8 +69,13 @@
  {
        printk(KERN_CRIT "sgpnt[%d:%d] addr %p/0x%lx length %d\n",
@@ -1957,7 +1957,7 @@ Index: linux-2.4.21/drivers/scsi/aha1542.c
 Index: linux-2.4.21/drivers/scsi/aha1740.c
 ===================================================================
 --- linux-2.4.21.orig/drivers/scsi/aha1740.c   2001-09-30 15:26:07.000000000 -0400
-+++ linux-2.4.21/drivers/scsi/aha1740.c        2004-12-21 13:58:10.000000000 -0500
++++ linux-2.4.21/drivers/scsi/aha1740.c        2005-06-01 23:07:51.110576424 -0400
 @@ -397,7 +397,11 @@
        for(i=0; i<SCpnt->use_sg; i++)
        {
@@ -1972,8 +1972,8 @@ Index: linux-2.4.21/drivers/scsi/aha1740.c
        host->ecb[ecbno].dataptr = virt_to_bus(cptr);
 Index: linux-2.4.21/drivers/scsi/aic7xxx_old.c
 ===================================================================
---- linux-2.4.21.orig/drivers/scsi/aic7xxx_old.c       2004-12-21 13:51:20.000000000 -0500
-+++ linux-2.4.21/drivers/scsi/aic7xxx_old.c    2004-12-21 13:58:10.000000000 -0500
+--- linux-2.4.21.orig/drivers/scsi/aic7xxx_old.c       2005-06-01 22:51:54.000000000 -0400
++++ linux-2.4.21/drivers/scsi/aic7xxx_old.c    2005-06-01 23:07:51.118575208 -0400
 @@ -2845,7 +2845,11 @@
            struct scatterlist *sg;
  
@@ -1989,7 +1989,7 @@ Index: linux-2.4.21/drivers/scsi/aic7xxx_old.c
 Index: linux-2.4.21/drivers/scsi/fdomain.c
 ===================================================================
 --- linux-2.4.21.orig/drivers/scsi/fdomain.c   2002-11-28 18:53:14.000000000 -0500
-+++ linux-2.4.21/drivers/scsi/fdomain.c        2004-12-21 13:58:10.000000000 -0500
++++ linux-2.4.21/drivers/scsi/fdomain.c        2005-06-01 23:07:51.120574904 -0400
 @@ -1564,7 +1564,11 @@
            if (current_SC->SCp.buffers_residual) {
               --current_SC->SCp.buffers_residual;
@@ -2029,7 +2029,7 @@ Index: linux-2.4.21/drivers/scsi/fdomain.c
 Index: linux-2.4.21/drivers/scsi/in2000.c
 ===================================================================
 --- linux-2.4.21.orig/drivers/scsi/in2000.c    2003-06-13 10:51:36.000000000 -0400
-+++ linux-2.4.21/drivers/scsi/in2000.c 2004-12-21 13:58:10.000000000 -0500
++++ linux-2.4.21/drivers/scsi/in2000.c 2005-06-01 23:07:51.122574600 -0400
 @@ -355,7 +355,11 @@
     if (cmd->use_sg) {
        cmd->SCp.buffer = (struct scatterlist *)cmd->buffer;
@@ -2057,7 +2057,7 @@ Index: linux-2.4.21/drivers/scsi/in2000.c
 Index: linux-2.4.21/drivers/scsi/NCR5380.c
 ===================================================================
 --- linux-2.4.21.orig/drivers/scsi/NCR5380.c   2003-06-13 10:51:36.000000000 -0400
-+++ linux-2.4.21/drivers/scsi/NCR5380.c        2004-12-21 13:58:10.000000000 -0500
++++ linux-2.4.21/drivers/scsi/NCR5380.c        2005-06-01 23:07:51.124574296 -0400
 @@ -337,7 +337,11 @@
        if (cmd->use_sg) {
                cmd->SCp.buffer = (struct scatterlist *) cmd->buffer;
@@ -2085,7 +2085,7 @@ Index: linux-2.4.21/drivers/scsi/NCR5380.c
 Index: linux-2.4.21/drivers/scsi/NCR53c406a.c
 ===================================================================
 --- linux-2.4.21.orig/drivers/scsi/NCR53c406a.c        2001-09-30 15:26:07.000000000 -0400
-+++ linux-2.4.21/drivers/scsi/NCR53c406a.c     2004-12-21 13:58:10.000000000 -0500
++++ linux-2.4.21/drivers/scsi/NCR53c406a.c     2005-06-01 23:07:51.125574144 -0400
 @@ -895,7 +895,11 @@
                  sgcount = current_SC->use_sg;
                  sglist = current_SC->request_buffer;
@@ -2113,7 +2113,7 @@ Index: linux-2.4.21/drivers/scsi/NCR53c406a.c
 Index: linux-2.4.21/drivers/scsi/sym53c416.c
 ===================================================================
 --- linux-2.4.21.orig/drivers/scsi/sym53c416.c 2001-09-30 15:26:07.000000000 -0400
-+++ linux-2.4.21/drivers/scsi/sym53c416.c      2004-12-21 13:58:10.000000000 -0500
++++ linux-2.4.21/drivers/scsi/sym53c416.c      2005-06-01 23:07:51.126573992 -0400
 @@ -448,7 +448,11 @@
                                        sglist = current_command->request_buffer;
                                        while(sgcount--)
@@ -2141,7 +2141,7 @@ Index: linux-2.4.21/drivers/scsi/sym53c416.c
 Index: linux-2.4.21/drivers/scsi/qlogicfas.c
 ===================================================================
 --- linux-2.4.21.orig/drivers/scsi/qlogicfas.c 2003-06-13 10:51:36.000000000 -0400
-+++ linux-2.4.21/drivers/scsi/qlogicfas.c      2004-12-21 13:58:10.000000000 -0500
++++ linux-2.4.21/drivers/scsi/qlogicfas.c      2005-06-01 23:07:51.126573992 -0400
 @@ -393,7 +393,11 @@
                                        REG0;
                                        return ((qabort == 1 ? DID_ABORT : DID_RESET) << 16);
@@ -2157,7 +2157,7 @@ Index: linux-2.4.21/drivers/scsi/qlogicfas.c
 Index: linux-2.4.21/drivers/scsi/qla1280.c
 ===================================================================
 --- linux-2.4.21.orig/drivers/scsi/qla1280.c   2001-09-30 15:26:07.000000000 -0400
-+++ linux-2.4.21/drivers/scsi/qla1280.c        2004-12-21 13:58:10.000000000 -0500
++++ linux-2.4.21/drivers/scsi/qla1280.c        2005-06-01 23:07:51.131573232 -0400
 @@ -3985,8 +3985,13 @@
                      {
                          DEBUG(sprintf(debug_buff,"SG Segment ap=0x%p, len=0x%x\n\r",sg->address,sg->length));
@@ -2212,8 +2212,8 @@ Index: linux-2.4.21/drivers/scsi/qla1280.c
                          }
 Index: linux-2.4.21/drivers/scsi/seagate.c
 ===================================================================
---- linux-2.4.21.orig/drivers/scsi/seagate.c   2004-12-21 13:51:00.000000000 -0500
-+++ linux-2.4.21/drivers/scsi/seagate.c        2004-12-21 13:58:10.000000000 -0500
+--- linux-2.4.21.orig/drivers/scsi/seagate.c   2005-06-01 22:51:40.000000000 -0400
++++ linux-2.4.21/drivers/scsi/seagate.c        2005-06-01 23:07:51.133572928 -0400
 @@ -1070,7 +1070,11 @@
  
                        buffer = (struct scatterlist *) SCint->buffer;
@@ -2254,7 +2254,7 @@ Index: linux-2.4.21/drivers/scsi/seagate.c
 Index: linux-2.4.21/drivers/scsi/53c7,8xx.c
 ===================================================================
 --- linux-2.4.21.orig/drivers/scsi/53c7,8xx.c  2002-02-25 14:38:04.000000000 -0500
-+++ linux-2.4.21/drivers/scsi/53c7,8xx.c       2004-12-21 13:58:10.000000000 -0500
++++ linux-2.4.21/drivers/scsi/53c7,8xx.c       2005-06-01 23:07:51.137572320 -0400
 @@ -3787,7 +3787,11 @@
      for (i = 0; cmd->use_sg ? (i < cmd->use_sg) : !i; cmd_datain += 4, 
        cmd_dataout += 4, ++i) {
@@ -2295,8 +2295,8 @@ Index: linux-2.4.21/drivers/scsi/53c7,8xx.c
                offset = ptr - (char *) (cmd->request_buffer);
 Index: linux-2.4.21/drivers/scsi/eata_dma.c
 ===================================================================
---- linux-2.4.21.orig/drivers/scsi/eata_dma.c  2004-12-21 13:51:30.000000000 -0500
-+++ linux-2.4.21/drivers/scsi/eata_dma.c       2004-12-21 13:58:10.000000000 -0500
+--- linux-2.4.21.orig/drivers/scsi/eata_dma.c  2005-06-01 22:52:05.000000000 -0400
++++ linux-2.4.21/drivers/scsi/eata_dma.c       2005-06-01 23:07:51.138572168 -0400
 @@ -571,7 +571,11 @@
        ccb->cp_datalen = htonl(cmd->use_sg * sizeof(struct eata_sg_list));
        sl=(struct scatterlist *)cmd->request_buffer;
@@ -2312,7 +2312,7 @@ Index: linux-2.4.21/drivers/scsi/eata_dma.c
 Index: linux-2.4.21/drivers/scsi/eata_pio.c
 ===================================================================
 --- linux-2.4.21.orig/drivers/scsi/eata_pio.c  2001-09-30 15:26:07.000000000 -0400
-+++ linux-2.4.21/drivers/scsi/eata_pio.c       2004-12-21 13:58:10.000000000 -0500
++++ linux-2.4.21/drivers/scsi/eata_pio.c       2005-06-01 23:07:51.139572016 -0400
 @@ -99,7 +99,11 @@
        else
        {
@@ -2340,7 +2340,7 @@ Index: linux-2.4.21/drivers/scsi/eata_pio.c
 Index: linux-2.4.21/drivers/scsi/wd7000.c
 ===================================================================
 --- linux-2.4.21.orig/drivers/scsi/wd7000.c    2001-09-30 15:26:08.000000000 -0400
-+++ linux-2.4.21/drivers/scsi/wd7000.c 2004-12-21 13:58:10.000000000 -0500
++++ linux-2.4.21/drivers/scsi/wd7000.c 2005-06-01 23:07:51.140571864 -0400
 @@ -1189,7 +1189,11 @@
        any2scsi (scb->maxlen, SCpnt->use_sg * sizeof (Sgb));
  
@@ -2356,7 +2356,7 @@ Index: linux-2.4.21/drivers/scsi/wd7000.c
 Index: linux-2.4.21/drivers/scsi/scsiiom.c
 ===================================================================
 --- linux-2.4.21.orig/drivers/scsi/scsiiom.c   2000-12-31 14:06:00.000000000 -0500
-+++ linux-2.4.21/drivers/scsi/scsiiom.c        2004-12-21 13:58:10.000000000 -0500
++++ linux-2.4.21/drivers/scsi/scsiiom.c        2005-06-01 23:07:51.142571560 -0400
 @@ -379,7 +379,11 @@
                pSRB->pSegmentList++;
                psgl = pSRB->pSegmentList;
@@ -2474,7 +2474,7 @@ Index: linux-2.4.21/drivers/scsi/scsiiom.c
 Index: linux-2.4.21/drivers/scsi/tmscsim.c
 ===================================================================
 --- linux-2.4.21.orig/drivers/scsi/tmscsim.c   2001-12-21 12:41:55.000000000 -0500
-+++ linux-2.4.21/drivers/scsi/tmscsim.c        2004-12-21 13:58:10.000000000 -0500
++++ linux-2.4.21/drivers/scsi/tmscsim.c        2005-06-01 23:07:51.144571256 -0400
 @@ -1146,7 +1146,11 @@
      {
        pSRB->SGcount = 1;
@@ -2490,7 +2490,7 @@ Index: linux-2.4.21/drivers/scsi/tmscsim.c
 Index: linux-2.4.21/drivers/scsi/AM53C974.c
 ===================================================================
 --- linux-2.4.21.orig/drivers/scsi/AM53C974.c  2001-09-30 15:26:07.000000000 -0400
-+++ linux-2.4.21/drivers/scsi/AM53C974.c       2004-12-21 13:58:10.000000000 -0500
++++ linux-2.4.21/drivers/scsi/AM53C974.c       2005-06-01 23:07:51.146570952 -0400
 @@ -842,7 +842,11 @@
        if (cmd->use_sg) {
                cmd->SCp.buffer = (struct scatterlist *) cmd->buffer;
@@ -2517,8 +2517,8 @@ Index: linux-2.4.21/drivers/scsi/AM53C974.c
                if (cmd->SCp.this_residual) {
 Index: linux-2.4.21/drivers/scsi/megaraid2.c
 ===================================================================
---- linux-2.4.21.orig/drivers/scsi/megaraid2.c 2004-12-21 13:51:19.000000000 -0500
-+++ linux-2.4.21/drivers/scsi/megaraid2.c      2004-12-21 13:58:10.000000000 -0500
+--- linux-2.4.21.orig/drivers/scsi/megaraid2.c 2005-06-01 22:51:54.000000000 -0400
++++ linux-2.4.21/drivers/scsi/megaraid2.c      2005-06-01 23:07:51.149570496 -0400
 @@ -2180,7 +2180,11 @@
                        if( cmd->use_sg ) {
                                sgl = (struct scatterlist *)
@@ -2533,8 +2533,8 @@ Index: linux-2.4.21/drivers/scsi/megaraid2.c
                                c = *(u8 *)cmd->request_buffer;
 Index: linux-2.4.21/drivers/scsi/megaraid.c
 ===================================================================
---- linux-2.4.21.orig/drivers/scsi/megaraid.c  2004-12-21 13:51:19.000000000 -0500
-+++ linux-2.4.21/drivers/scsi/megaraid.c       2004-12-21 13:58:10.000000000 -0500
+--- linux-2.4.21.orig/drivers/scsi/megaraid.c  2005-06-01 22:51:54.000000000 -0400
++++ linux-2.4.21/drivers/scsi/megaraid.c       2005-06-01 23:07:51.152570040 -0400
 @@ -1201,8 +1201,13 @@
        case READ_CAPACITY:
                if ( SCpnt->use_sg ) {
@@ -2565,7 +2565,7 @@ Index: linux-2.4.21/drivers/scsi/megaraid.c
 Index: linux-2.4.21/drivers/scsi/atp870u.c
 ===================================================================
 --- linux-2.4.21.orig/drivers/scsi/atp870u.c   2002-08-02 20:39:44.000000000 -0400
-+++ linux-2.4.21/drivers/scsi/atp870u.c        2004-12-21 13:58:10.000000000 -0500
++++ linux-2.4.21/drivers/scsi/atp870u.c        2005-06-01 23:07:51.154569736 -0400
 @@ -804,7 +804,11 @@
                sgpnt = (struct scatterlist *) workrequ->request_buffer;
                i = 0;
@@ -2581,7 +2581,7 @@ Index: linux-2.4.21/drivers/scsi/atp870u.c
 Index: linux-2.4.21/drivers/scsi/gdth.c
 ===================================================================
 --- linux-2.4.21.orig/drivers/scsi/gdth.c      2003-06-13 10:51:36.000000000 -0400
-+++ linux-2.4.21/drivers/scsi/gdth.c   2004-12-21 13:58:10.000000000 -0500
++++ linux-2.4.21/drivers/scsi/gdth.c   2005-06-01 23:07:51.158569128 -0400
 @@ -2662,7 +2662,11 @@
              if (cpsum+cpnow > cpcount) 
                  cpnow = cpcount - cpsum;
@@ -2621,7 +2621,7 @@ Index: linux-2.4.21/drivers/scsi/gdth.c
 Index: linux-2.4.21/drivers/scsi/ini9100u.c
 ===================================================================
 --- linux-2.4.21.orig/drivers/scsi/ini9100u.c  2001-09-30 15:26:07.000000000 -0400
-+++ linux-2.4.21/drivers/scsi/ini9100u.c       2004-12-21 13:58:10.000000000 -0500
++++ linux-2.4.21/drivers/scsi/ini9100u.c       2005-06-01 23:07:51.159568976 -0400
 @@ -489,7 +489,11 @@
        if (SCpnt->use_sg) {
                pSrbSG = (struct scatterlist *) SCpnt->request_buffer;
@@ -2649,7 +2649,7 @@ Index: linux-2.4.21/drivers/scsi/ini9100u.c
 Index: linux-2.4.21/drivers/scsi/inia100.c
 ===================================================================
 --- linux-2.4.21.orig/drivers/scsi/inia100.c   2001-09-30 15:26:07.000000000 -0400
-+++ linux-2.4.21/drivers/scsi/inia100.c        2004-12-21 13:58:10.000000000 -0500
++++ linux-2.4.21/drivers/scsi/inia100.c        2005-06-01 23:07:51.159568976 -0400
 @@ -494,7 +494,11 @@
                        pSCB->SCB_SGLen = (U32) (SCpnt->use_sg * 8);
                        pSrbSG = (struct scatterlist *) SCpnt->request_buffer;
@@ -2664,8 +2664,8 @@ Index: linux-2.4.21/drivers/scsi/inia100.c
                        }
 Index: linux-2.4.21/drivers/scsi/ide-scsi.c
 ===================================================================
---- linux-2.4.21.orig/drivers/scsi/ide-scsi.c  2004-12-21 13:51:31.000000000 -0500
-+++ linux-2.4.21/drivers/scsi/ide-scsi.c       2004-12-21 13:58:10.000000000 -0500
+--- linux-2.4.21.orig/drivers/scsi/ide-scsi.c  2005-06-01 22:52:05.000000000 -0400
++++ linux-2.4.21/drivers/scsi/ide-scsi.c       2005-06-01 23:07:51.160568824 -0400
 @@ -154,7 +154,11 @@
                        return;
                }
@@ -2720,8 +2720,8 @@ Index: linux-2.4.21/drivers/scsi/ide-scsi.c
                        sg++;
 Index: linux-2.4.21/drivers/scsi/ppa.c
 ===================================================================
---- linux-2.4.21.orig/drivers/scsi/ppa.c       2004-12-21 13:51:30.000000000 -0500
-+++ linux-2.4.21/drivers/scsi/ppa.c    2004-12-21 13:58:10.000000000 -0500
+--- linux-2.4.21.orig/drivers/scsi/ppa.c       2005-06-01 22:52:05.000000000 -0400
++++ linux-2.4.21/drivers/scsi/ppa.c    2005-06-01 23:07:51.161568672 -0400
 @@ -740,7 +740,11 @@
            if (cmd->SCp.buffers_residual--) {
                cmd->SCp.buffer++;
@@ -2748,8 +2748,8 @@ Index: linux-2.4.21/drivers/scsi/ppa.c
            cmd->SCp.buffer = NULL;
 Index: linux-2.4.21/drivers/scsi/imm.c
 ===================================================================
---- linux-2.4.21.orig/drivers/scsi/imm.c       2004-12-21 13:51:30.000000000 -0500
-+++ linux-2.4.21/drivers/scsi/imm.c    2004-12-21 13:58:10.000000000 -0500
+--- linux-2.4.21.orig/drivers/scsi/imm.c       2005-06-01 22:52:05.000000000 -0400
++++ linux-2.4.21/drivers/scsi/imm.c    2005-06-01 23:07:51.162568520 -0400
 @@ -834,7 +834,11 @@
            if (cmd->SCp.buffers_residual--) {
                cmd->SCp.buffer++;
@@ -2776,8 +2776,8 @@ Index: linux-2.4.21/drivers/scsi/imm.c
            cmd->SCp.buffer = NULL;
 Index: linux-2.4.21/drivers/scsi/st.c
 ===================================================================
---- linux-2.4.21.orig/drivers/scsi/st.c        2004-12-21 13:51:30.000000000 -0500
-+++ linux-2.4.21/drivers/scsi/st.c     2004-12-21 13:58:10.000000000 -0500
+--- linux-2.4.21.orig/drivers/scsi/st.c        2005-06-01 22:52:04.000000000 -0400
++++ linux-2.4.21/drivers/scsi/st.c     2005-06-01 23:07:51.164568216 -0400
 @@ -3409,6 +3409,12 @@
  }
  \f
@@ -2924,8 +2924,8 @@ Index: linux-2.4.21/drivers/scsi/st.c
                src_offset += count;
 Index: linux-2.4.21/drivers/scsi/osst.c
 ===================================================================
---- linux-2.4.21.orig/drivers/scsi/osst.c      2004-12-21 13:51:30.000000000 -0500
-+++ linux-2.4.21/drivers/scsi/osst.c   2004-12-21 13:58:10.000000000 -0500
+--- linux-2.4.21.orig/drivers/scsi/osst.c      2005-06-01 22:52:04.000000000 -0400
++++ linux-2.4.21/drivers/scsi/osst.c   2005-06-01 23:07:51.169567456 -0400
 @@ -477,7 +477,11 @@
        if (STp->raw) {
                if (STp->buffer->syscall_result) {
@@ -3111,7 +3111,7 @@ Index: linux-2.4.21/drivers/scsi/osst.c
 Index: linux-2.4.21/drivers/scsi/pcmcia/nsp_cs.c
 ===================================================================
 --- linux-2.4.21.orig/drivers/scsi/pcmcia/nsp_cs.c     2003-06-13 10:51:36.000000000 -0400
-+++ linux-2.4.21/drivers/scsi/pcmcia/nsp_cs.c  2004-12-21 13:58:10.000000000 -0500
++++ linux-2.4.21/drivers/scsi/pcmcia/nsp_cs.c  2005-06-01 23:07:51.170567304 -0400
 @@ -201,7 +201,11 @@
           SCp.phase            : current state of the command */
        if (SCpnt->use_sg) {
@@ -3150,8 +3150,8 @@ Index: linux-2.4.21/drivers/scsi/pcmcia/nsp_cs.c
  
 Index: linux-2.4.21/drivers/scsi/ips.c
 ===================================================================
---- linux-2.4.21.orig/drivers/scsi/ips.c       2004-12-21 13:51:19.000000000 -0500
-+++ linux-2.4.21/drivers/scsi/ips.c    2004-12-21 13:58:10.000000000 -0500
+--- linux-2.4.21.orig/drivers/scsi/ips.c       2005-06-01 22:51:54.000000000 -0400
++++ linux-2.4.21/drivers/scsi/ips.c    2005-06-01 23:07:51.175566544 -0400
 @@ -217,7 +217,11 @@
  #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0)
  #include <linux/blk.h>
@@ -3166,9 +3166,9 @@ Index: linux-2.4.21/drivers/scsi/ips.c
  #ifndef __devexit_p
 Index: linux-2.4.21/drivers/scsi/libata-core.c
 ===================================================================
---- linux-2.4.21.orig/drivers/scsi/libata-core.c       2004-12-21 13:51:26.000000000 -0500
-+++ linux-2.4.21/drivers/scsi/libata-core.c    2004-12-21 13:58:10.000000000 -0500
-@@ -1915,8 +1915,13 @@
+--- linux-2.4.21.orig/drivers/scsi/libata-core.c       2005-06-01 22:51:58.000000000 -0400
++++ linux-2.4.21/drivers/scsi/libata-core.c    2005-06-01 23:36:21.244596536 -0400
+@@ -2068,8 +2068,13 @@
        qc->buf_virt = buf;
  
        sg = qc->sg;
@@ -3180,29 +3180,41 @@ Index: linux-2.4.21/drivers/scsi/libata-core.c
        sg->offset = (unsigned long) buf & ~PAGE_MASK;
 +#endif /* !SMALL_SCATTERLIST */
        sg_dma_len(sg) = buflen;
+ }
  
-       /* WARN_ON(buflen > PAGE_SIZE); */
-@@ -2143,9 +2148,15 @@
+@@ -2297,8 +2302,13 @@
        if (qc->cursect == (qc->nsect - 1))
                ap->pio_task_state = PIO_ST_LAST;
  
 +#if SMALL_SCATTERLIST
 +      page = sg[qc->cursg].u.page.page;
-+      buf = kmap(page) +
-+            sg[qc->cursg].u.page.offset + (qc->cursg_ofs * ATA_SECT_SIZE);
++      offset = sg[qc->cursg].u.page.offset + qc->cursg_ofs * ATA_SECT_SIZE;
 +#else
        page = sg[qc->cursg].page;
-       buf = kmap(page) +
-             sg[qc->cursg].offset + (qc->cursg_ofs * ATA_SECT_SIZE);
+       offset = sg[qc->cursg].offset + qc->cursg_ofs * ATA_SECT_SIZE;
 +#endif /* SMALL_SCATTERLIST */
  
-       qc->cursect++;
-       qc->cursg_ofs++;
+       /* get the current page and offset */
+       page = nth_page(page, (offset >> PAGE_SHIFT));
+@@ -2339,8 +2349,13 @@
+       sg = &qc->sg[qc->cursg];
+ next_page:
++#if SMALL_SCATTERLIST
++      page = sg->u.page.page;
++      offset = sg->u.page.offset + qc->cursg_ofs;
++#else
+       page = sg->page;
+       offset = sg->offset + qc->cursg_ofs;
++#endif
+       /* get the current page and offset */
+       page = nth_page(page, (offset >> PAGE_SHIFT));
 Index: linux-2.4.21/drivers/scsi/libata-scsi.c
 ===================================================================
---- linux-2.4.21.orig/drivers/scsi/libata-scsi.c       2004-12-21 13:51:26.000000000 -0500
-+++ linux-2.4.21/drivers/scsi/libata-scsi.c    2004-12-21 13:58:10.000000000 -0500
-@@ -686,7 +686,11 @@
+--- linux-2.4.21.orig/drivers/scsi/libata-scsi.c       2005-06-01 22:51:58.000000000 -0400
++++ linux-2.4.21/drivers/scsi/libata-scsi.c    2005-06-01 23:12:15.112442040 -0400
+@@ -689,7 +689,11 @@
                struct scatterlist *sg;
  
                sg = (struct scatterlist *) cmd->request_buffer;
@@ -3214,22 +3226,22 @@ Index: linux-2.4.21/drivers/scsi/libata-scsi.c
                buflen = sg->length;
        } else {
                buf = cmd->request_buffer;
-@@ -714,7 +718,11 @@
+@@ -717,7 +721,11 @@
                struct scatterlist *sg;
  
                sg = (struct scatterlist *) cmd->request_buffer;
 +#if SMALL_SCATTERLIST
-+              kunmap_atomic(sg->u.page.page, KM_USER0);
++              kunmap_atomic(buf - sg->u.page.offset, KM_USER0);
 +#else
-               kunmap_atomic(sg->page, KM_USER0);
+               kunmap_atomic(buf - sg->offset, KM_USER0);
 +#endif /* !SMALL_SCATTERLIST */
        }
  }
  
 Index: linux-2.4.21/drivers/block/cpqarray.c
 ===================================================================
---- linux-2.4.21.orig/drivers/block/cpqarray.c 2004-12-21 13:51:20.000000000 -0500
-+++ linux-2.4.21/drivers/block/cpqarray.c      2004-12-21 13:58:10.000000000 -0500
+--- linux-2.4.21.orig/drivers/block/cpqarray.c 2005-06-01 22:51:54.000000000 -0400
++++ linux-2.4.21/drivers/block/cpqarray.c      2005-06-01 23:07:51.183565328 -0400
 @@ -1003,9 +1003,14 @@
                } else {
                        if (seg == SG_MAX)
@@ -3271,8 +3283,8 @@ Index: linux-2.4.21/drivers/block/cpqarray.c
        c->req.hdr.sg_cnt = seg;
 Index: linux-2.4.21/drivers/block/cciss.c
 ===================================================================
---- linux-2.4.21.orig/drivers/block/cciss.c    2004-12-21 13:51:19.000000000 -0500
-+++ linux-2.4.21/drivers/block/cciss.c 2004-12-21 13:58:10.000000000 -0500
+--- linux-2.4.21.orig/drivers/block/cciss.c    2005-06-01 22:51:54.000000000 -0400
++++ linux-2.4.21/drivers/block/cciss.c 2005-06-01 23:07:51.185565024 -0400
 @@ -2491,9 +2491,14 @@
                } else {
                        if (seg == MAXSGENTRIES)
@@ -3310,8 +3322,8 @@ Index: linux-2.4.21/drivers/block/cciss.c
                  c->SG[i].Ext = 0;  /* we are not chaining */
 Index: linux-2.4.21/drivers/block/sx8.c
 ===================================================================
---- linux-2.4.21.orig/drivers/block/sx8.c      2004-12-21 13:51:26.000000000 -0500
-+++ linux-2.4.21/drivers/block/sx8.c   2004-12-21 13:58:10.000000000 -0500
+--- linux-2.4.21.orig/drivers/block/sx8.c      2005-06-01 22:51:58.000000000 -0400
++++ linux-2.4.21/drivers/block/sx8.c   2005-06-01 23:07:51.186564872 -0400
 @@ -1103,9 +1103,15 @@
                } else {
                        if (unlikely(n_elem == CARM_MAX_REQ_SG))
@@ -3330,8 +3342,8 @@ Index: linux-2.4.21/drivers/block/sx8.c
                }
 Index: linux-2.4.21/drivers/ieee1394/dma.c
 ===================================================================
---- linux-2.4.21.orig/drivers/ieee1394/dma.c   2003-06-13 10:51:34.000000000 -0400
-+++ linux-2.4.21/drivers/ieee1394/dma.c        2004-12-21 13:58:10.000000000 -0500
+--- linux-2.4.21.orig/drivers/ieee1394/dma.c   2005-06-01 22:52:05.000000000 -0400
++++ linux-2.4.21/drivers/ieee1394/dma.c        2005-06-01 23:07:51.187564720 -0400
 @@ -97,8 +97,12 @@
        /* fill scatter/gather list with pages */
        for(i = 0; i < dma->n_pages; i++) {
@@ -3349,7 +3361,7 @@ Index: linux-2.4.21/drivers/ieee1394/dma.c
 Index: linux-2.4.21/drivers/ieee1394/sbp2.c
 ===================================================================
 --- linux-2.4.21.orig/drivers/ieee1394/sbp2.c  2003-06-13 10:51:34.000000000 -0400
-+++ linux-2.4.21/drivers/ieee1394/sbp2.c       2004-12-21 13:58:10.000000000 -0500
++++ linux-2.4.21/drivers/ieee1394/sbp2.c       2005-06-01 23:07:51.189564416 -0400
 @@ -2036,11 +2036,19 @@
                        command->dma_dir = dma_dir;
                        command->dma_size = sgpnt[0].length;
@@ -3373,7 +3385,7 @@ Index: linux-2.4.21/drivers/ieee1394/sbp2.c
 Index: linux-2.4.21/drivers/message/i2o/i2o_scsi.c
 ===================================================================
 --- linux-2.4.21.orig/drivers/message/i2o/i2o_scsi.c   2002-08-02 20:39:44.000000000 -0400
-+++ linux-2.4.21/drivers/message/i2o/i2o_scsi.c        2004-12-21 13:58:10.000000000 -0500
++++ linux-2.4.21/drivers/message/i2o/i2o_scsi.c        2005-06-01 23:07:51.190564264 -0400
 @@ -693,7 +693,11 @@
                        {
                                *mptr++=direction|0x10000000|sg->length;
@@ -3401,7 +3413,7 @@ Index: linux-2.4.21/drivers/message/i2o/i2o_scsi.c
 Index: linux-2.4.21/drivers/net/fc/iph5526.c
 ===================================================================
 --- linux-2.4.21.orig/drivers/net/fc/iph5526.c 2003-06-13 10:51:34.000000000 -0400
-+++ linux-2.4.21/drivers/net/fc/iph5526.c      2004-12-21 13:58:10.000000000 -0500
++++ linux-2.4.21/drivers/net/fc/iph5526.c      2005-06-01 23:07:51.193563808 -0400
 @@ -4249,7 +4249,11 @@
                        if (Cmnd->use_sg) {
                        int count = 0, j;
@@ -3450,8 +3462,8 @@ Index: linux-2.4.21/drivers/net/fc/iph5526.c
                                                update_EDB_indx(fi);
 Index: linux-2.4.21/drivers/net/wireless/airo.c
 ===================================================================
---- linux-2.4.21.orig/drivers/net/wireless/airo.c      2004-12-21 13:51:20.000000000 -0500
-+++ linux-2.4.21/drivers/net/wireless/airo.c   2004-12-21 13:58:10.000000000 -0500
+--- linux-2.4.21.orig/drivers/net/wireless/airo.c      2005-06-01 22:51:55.000000000 -0400
++++ linux-2.4.21/drivers/net/wireless/airo.c   2005-06-01 23:07:51.198563048 -0400
 @@ -1584,11 +1584,20 @@
                aes_counter[12] = (u8)(counter >> 24);
                counter++;
@@ -3476,7 +3488,7 @@ Index: linux-2.4.21/drivers/net/wireless/airo.c
 Index: linux-2.4.21/drivers/usb/microtek.c
 ===================================================================
 --- linux-2.4.21.orig/drivers/usb/microtek.c   2002-11-28 18:53:14.000000000 -0500
-+++ linux-2.4.21/drivers/usb/microtek.c        2004-12-21 13:58:10.000000000 -0500
++++ linux-2.4.21/drivers/usb/microtek.c        2005-06-01 23:07:51.199562896 -0400
 @@ -623,7 +623,11 @@
        context->fragment++;
        mts_int_submit_urb(transfer,
@@ -3504,7 +3516,7 @@ Index: linux-2.4.21/drivers/usb/microtek.c
 Index: linux-2.4.21/drivers/usb/hpusbscsi.c
 ===================================================================
 --- linux-2.4.21.orig/drivers/usb/hpusbscsi.c  2003-06-13 10:51:36.000000000 -0400
-+++ linux-2.4.21/drivers/usb/hpusbscsi.c       2004-12-21 13:58:10.000000000 -0500
++++ linux-2.4.21/drivers/usb/hpusbscsi.c       2005-06-01 23:07:51.199562896 -0400
 @@ -576,6 +576,17 @@
                hpusbscsi->state = HP_STATE_WORKING;
        PDEBUG(2, "state= %s", states[hpusbscsi->state]);
@@ -3534,7 +3546,7 @@ Index: linux-2.4.21/drivers/usb/hpusbscsi.c
 Index: linux-2.4.21/drivers/usb/storage/protocol.c
 ===================================================================
 --- linux-2.4.21.orig/drivers/usb/storage/protocol.c   2002-08-02 20:39:45.000000000 -0400
-+++ linux-2.4.21/drivers/usb/storage/protocol.c        2004-12-21 13:58:10.000000000 -0500
++++ linux-2.4.21/drivers/usb/storage/protocol.c        2005-06-01 23:07:51.200562744 -0400
 @@ -72,7 +72,11 @@
                struct scatterlist *sg;
  
@@ -3549,8 +3561,8 @@ Index: linux-2.4.21/drivers/usb/storage/protocol.c
  
 Index: linux-2.4.21/drivers/usb/storage/transport.c
 ===================================================================
---- linux-2.4.21.orig/drivers/usb/storage/transport.c  2004-12-21 13:51:15.000000000 -0500
-+++ linux-2.4.21/drivers/usb/storage/transport.c       2004-12-21 13:58:10.000000000 -0500
+--- linux-2.4.21.orig/drivers/usb/storage/transport.c  2005-06-01 22:51:52.000000000 -0400
++++ linux-2.4.21/drivers/usb/storage/transport.c       2005-06-01 23:07:51.201562592 -0400
 @@ -592,11 +592,20 @@
                        if (transfer_amount - total_transferred >= 
                                        sg[i].length) {
@@ -3575,8 +3587,8 @@ Index: linux-2.4.21/drivers/usb/storage/transport.c
                        /* if we get an error, end the loop here */
 Index: linux-2.4.21/drivers/usb/storage/usb.c
 ===================================================================
---- linux-2.4.21.orig/drivers/usb/storage/usb.c        2004-12-21 13:51:15.000000000 -0500
-+++ linux-2.4.21/drivers/usb/storage/usb.c     2004-12-21 13:58:10.000000000 -0500
+--- linux-2.4.21.orig/drivers/usb/storage/usb.c        2005-06-01 22:51:52.000000000 -0400
++++ linux-2.4.21/drivers/usb/storage/usb.c     2005-06-01 23:07:51.202562440 -0400
 @@ -291,13 +291,21 @@
        if (us->srb->use_sg) {
                sg = (struct scatterlist *)us->srb->request_buffer;
@@ -3602,7 +3614,7 @@ Index: linux-2.4.21/drivers/usb/storage/usb.c
 Index: linux-2.4.21/drivers/usb/storage/shuttle_usbat.c
 ===================================================================
 --- linux-2.4.21.orig/drivers/usb/storage/shuttle_usbat.c      2003-06-13 10:51:37.000000000 -0400
-+++ linux-2.4.21/drivers/usb/storage/shuttle_usbat.c   2004-12-21 13:58:10.000000000 -0500
++++ linux-2.4.21/drivers/usb/storage/shuttle_usbat.c   2005-06-01 23:07:51.202562440 -0400
 @@ -217,7 +217,11 @@
                sg = (struct scatterlist *)data;
                for (i=0; i<use_sg && transferred<len; i++) {
@@ -3648,7 +3660,7 @@ Index: linux-2.4.21/drivers/usb/storage/shuttle_usbat.c
 Index: linux-2.4.21/drivers/usb/storage/sddr09.c
 ===================================================================
 --- linux-2.4.21.orig/drivers/usb/storage/sddr09.c     2003-06-13 10:51:37.000000000 -0400
-+++ linux-2.4.21/drivers/usb/storage/sddr09.c  2004-12-21 13:58:10.000000000 -0500
++++ linux-2.4.21/drivers/usb/storage/sddr09.c  2005-06-01 23:07:51.204562136 -0400
 @@ -387,7 +387,11 @@
                        unsigned char *buf;
                        unsigned int length;
@@ -3799,7 +3811,7 @@ Index: linux-2.4.21/drivers/usb/storage/sddr09.c
 Index: linux-2.4.21/drivers/usb/storage/sddr55.c
 ===================================================================
 --- linux-2.4.21.orig/drivers/usb/storage/sddr55.c     2003-06-13 10:51:37.000000000 -0400
-+++ linux-2.4.21/drivers/usb/storage/sddr55.c  2004-12-21 13:58:10.000000000 -0500
++++ linux-2.4.21/drivers/usb/storage/sddr55.c  2005-06-01 23:07:51.204562136 -0400
 @@ -402,9 +402,15 @@
        if (use_sg) {
                transferred = 0;
@@ -3835,7 +3847,7 @@ Index: linux-2.4.21/drivers/usb/storage/sddr55.c
 Index: linux-2.4.21/drivers/usb/storage/freecom.c
 ===================================================================
 --- linux-2.4.21.orig/drivers/usb/storage/freecom.c    2003-06-13 10:51:37.000000000 -0400
-+++ linux-2.4.21/drivers/usb/storage/freecom.c 2004-12-21 13:58:10.000000000 -0500
++++ linux-2.4.21/drivers/usb/storage/freecom.c 2005-06-01 23:07:51.205561984 -0400
 @@ -144,11 +144,20 @@
                        if (transfer_amount - total_transferred >= 
                                        sg[i].length) {
@@ -3861,7 +3873,7 @@ Index: linux-2.4.21/drivers/usb/storage/freecom.c
 Index: linux-2.4.21/drivers/usb/storage/isd200.c
 ===================================================================
 --- linux-2.4.21.orig/drivers/usb/storage/isd200.c     2003-06-13 10:51:37.000000000 -0400
-+++ linux-2.4.21/drivers/usb/storage/isd200.c  2004-12-21 13:58:10.000000000 -0500
++++ linux-2.4.21/drivers/usb/storage/isd200.c  2005-06-01 23:07:51.206561832 -0400
 @@ -494,13 +494,21 @@
                              sg[i].length) {
                                  result = isd200_transfer_partial(us, 
@@ -3906,7 +3918,7 @@ Index: linux-2.4.21/drivers/usb/storage/isd200.c
 Index: linux-2.4.21/drivers/usb/storage/datafab.c
 ===================================================================
 --- linux-2.4.21.orig/drivers/usb/storage/datafab.c    2003-06-13 10:51:37.000000000 -0400
-+++ linux-2.4.21/drivers/usb/storage/datafab.c 2004-12-21 13:58:10.000000000 -0500
++++ linux-2.4.21/drivers/usb/storage/datafab.c 2005-06-01 23:07:51.207561680 -0400
 @@ -265,18 +265,30 @@
                        while (sg_idx < use_sg && transferred < len) {
                                if (len - transferred >= sg[sg_idx].length - current_sg_offset) {
@@ -3972,7 +3984,7 @@ Index: linux-2.4.21/drivers/usb/storage/datafab.c
 Index: linux-2.4.21/drivers/usb/storage/jumpshot.c
 ===================================================================
 --- linux-2.4.21.orig/drivers/usb/storage/jumpshot.c   2003-06-13 10:51:37.000000000 -0400
-+++ linux-2.4.21/drivers/usb/storage/jumpshot.c        2004-12-21 13:58:10.000000000 -0500
++++ linux-2.4.21/drivers/usb/storage/jumpshot.c        2005-06-01 23:07:51.208561528 -0400
 @@ -341,18 +341,30 @@
                          while (sg_idx < use_sg && transferred < len) {
                                  if (len - transferred >= sg[sg_idx].length - current_sg_offset) {
@@ -4038,8 +4050,8 @@ Index: linux-2.4.21/drivers/usb/storage/jumpshot.c
                                          break;
 Index: linux-2.4.21/drivers/usb/storage/scsiglue.c
 ===================================================================
---- linux-2.4.21.orig/drivers/usb/storage/scsiglue.c   2004-12-21 13:51:15.000000000 -0500
-+++ linux-2.4.21/drivers/usb/storage/scsiglue.c        2004-12-21 13:58:10.000000000 -0500
+--- linux-2.4.21.orig/drivers/usb/storage/scsiglue.c   2005-06-01 22:51:52.000000000 -0400
++++ linux-2.4.21/drivers/usb/storage/scsiglue.c        2005-06-01 23:07:51.209561376 -0400
 @@ -606,7 +606,11 @@
            }
  
@@ -4114,8 +4126,8 @@ Index: linux-2.4.21/drivers/usb/storage/scsiglue.c
                  element++;
 Index: linux-2.4.21/drivers/addon/ips_70015/ips.c
 ===================================================================
---- linux-2.4.21.orig/drivers/addon/ips_70015/ips.c    2004-12-21 13:51:19.000000000 -0500
-+++ linux-2.4.21/drivers/addon/ips_70015/ips.c 2004-12-21 14:02:57.000000000 -0500
+--- linux-2.4.21.orig/drivers/addon/ips_70015/ips.c    2005-06-01 22:51:54.000000000 -0400
++++ linux-2.4.21/drivers/addon/ips_70015/ips.c 2005-06-01 23:07:51.214560616 -0400
 @@ -207,7 +207,11 @@
  #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0)
  #include <linux/blk.h>
@@ -4130,8 +4142,8 @@ Index: linux-2.4.21/drivers/addon/ips_70015/ips.c
  #ifndef __devexit_p
 Index: linux-2.4.21/drivers/addon/megaraid_2106/megaraid2.c
 ===================================================================
---- linux-2.4.21.orig/drivers/addon/megaraid_2106/megaraid2.c  2004-12-21 13:51:19.000000000 -0500
-+++ linux-2.4.21/drivers/addon/megaraid_2106/megaraid2.c       2004-12-21 14:00:26.000000000 -0500
+--- linux-2.4.21.orig/drivers/addon/megaraid_2106/megaraid2.c  2005-06-01 22:51:54.000000000 -0400
++++ linux-2.4.21/drivers/addon/megaraid_2106/megaraid2.c       2005-06-01 23:07:51.217560160 -0400
 @@ -2198,7 +2198,11 @@
                        if( cmd->use_sg ) {
                                sgl = (struct scatterlist *)
@@ -4146,8 +4158,8 @@ Index: linux-2.4.21/drivers/addon/megaraid_2106/megaraid2.c
                                c = *(u8 *)cmd->request_buffer;
 Index: linux-2.4.21/drivers/addon/iscsi_sfnet/iscsi.c
 ===================================================================
---- linux-2.4.21.orig/drivers/addon/iscsi_sfnet/iscsi.c        2004-12-21 13:51:27.000000000 -0500
-+++ linux-2.4.21/drivers/addon/iscsi_sfnet/iscsi.c     2004-12-21 14:37:53.000000000 -0500
+--- linux-2.4.21.orig/drivers/addon/iscsi_sfnet/iscsi.c        2005-06-01 22:51:59.000000000 -0400
++++ linux-2.4.21/drivers/addon/iscsi_sfnet/iscsi.c     2005-06-01 23:07:51.229558336 -0400
 @@ -118,6 +118,16 @@
  #include "iscsi-probe.h"
  #include "iscsi-crc.h"
@@ -4282,8 +4294,8 @@ Index: linux-2.4.21/drivers/addon/iscsi_sfnet/iscsi.c
  }
 Index: linux-2.4.21/include/asm-i386/pci.h
 ===================================================================
---- linux-2.4.21.orig/include/asm-i386/pci.h   2004-12-21 13:51:05.000000000 -0500
-+++ linux-2.4.21/include/asm-i386/pci.h        2004-12-21 14:36:22.000000000 -0500
+--- linux-2.4.21.orig/include/asm-i386/pci.h   2005-06-01 22:51:45.000000000 -0400
++++ linux-2.4.21/include/asm-i386/pci.h        2005-06-01 23:21:02.208311288 -0400
 @@ -157,6 +157,17 @@
         * temporary 2.4 hack
         */
@@ -4313,7 +4325,7 @@ Index: linux-2.4.21/include/asm-i386/pci.h
 Index: linux-2.4.21/include/asm-i386/scatterlist.h
 ===================================================================
 --- linux-2.4.21.orig/include/asm-i386/scatterlist.h   2002-11-28 18:53:15.000000000 -0500
-+++ linux-2.4.21/include/asm-i386/scatterlist.h        2004-12-21 13:58:10.000000000 -0500
++++ linux-2.4.21/include/asm-i386/scatterlist.h        2005-06-01 23:07:51.230558184 -0400
 @@ -19,7 +19,25 @@
   *
   * and that's it. There's no excuse for not highmem enabling YOUR driver. /jens
@@ -4350,8 +4362,8 @@ Index: linux-2.4.21/include/asm-i386/scatterlist.h
  #define ISA_DMA_THRESHOLD (0x00ffffff)
 Index: linux-2.4.21/net/xfrm/xfrm_algo.c
 ===================================================================
---- linux-2.4.21.orig/net/xfrm/xfrm_algo.c     2004-12-21 13:51:11.000000000 -0500
-+++ linux-2.4.21/net/xfrm/xfrm_algo.c  2004-12-21 13:58:10.000000000 -0500
+--- linux-2.4.21.orig/net/xfrm/xfrm_algo.c     2005-06-01 22:51:50.000000000 -0400
++++ linux-2.4.21/net/xfrm/xfrm_algo.c  2005-06-01 23:07:51.231558032 -0400
 @@ -487,9 +487,14 @@
        if (copy > 0) {
                if (copy > len)
@@ -4415,8 +4427,8 @@ Index: linux-2.4.21/net/xfrm/xfrm_algo.c
                        if (!(len -= copy))
 Index: linux-2.4.21/net/ipv6/addrconf.c
 ===================================================================
---- linux-2.4.21.orig/net/ipv6/addrconf.c      2004-12-21 13:51:11.000000000 -0500
-+++ linux-2.4.21/net/ipv6/addrconf.c   2004-12-21 13:58:10.000000000 -0500
+--- linux-2.4.21.orig/net/ipv6/addrconf.c      2005-06-01 22:51:50.000000000 -0400
++++ linux-2.4.21/net/ipv6/addrconf.c   2005-06-01 23:07:51.232557880 -0400
 @@ -1047,11 +1047,18 @@
        u8 digest[16];
        struct scatterlist sg[2];
diff --git a/lustre/kernel_patches/patches/statfs64-cast-unsigned-2.4-rhel.patch b/lustre/kernel_patches/patches/statfs64-cast-unsigned-2.4-rhel.patch
new file mode 100644 (file)
index 0000000..a1063ae
--- /dev/null
@@ -0,0 +1,28 @@
+Index: linux-2.4.21/fs/open.c
+===================================================================
+--- linux-2.4.21.orig/fs/open.c        2005-05-04 16:09:11.702098704 -0400
++++ linux-2.4.21/fs/open.c     2005-05-04 16:17:31.597103112 -0400
+@@ -92,15 +92,15 @@
+               if (retval)
+                       return retval;
+               /* Stuff the 32 bit values into the 64 bit struct */
+-              buf->f_type = st.f_type;
+-              buf->f_bsize = st.f_bsize;
+-              buf->f_blocks = st.f_blocks;
+-              buf->f_bfree = st.f_bfree;
+-              buf->f_bavail = st.f_bavail;
+-              buf->f_files = st.f_files;
+-              buf->f_ffree = st.f_ffree;
++              buf->f_type = (unsigned long) st.f_type;
++              buf->f_bsize = (unsigned long) st.f_bsize;
++              buf->f_blocks = (unsigned long) st.f_blocks;
++              buf->f_bfree = (unsigned long) st.f_bfree;
++              buf->f_bavail = (unsigned long) st.f_bavail;
++              buf->f_files = (unsigned long) st.f_files;
++              buf->f_ffree = (unsigned long) st.f_ffree;
+               buf->f_fsid = st.f_fsid;
+-              buf->f_namelen = st.f_namelen;
++              buf->f_namelen = (unsigned long) st.f_namelen;
+               memset(buf->f_spare, 0, sizeof(buf->f_spare));
+       }
+       return 0;
diff --git a/lustre/kernel_patches/patches/tcp-zero-copy-2.4.21-suse-171.patch b/lustre/kernel_patches/patches/tcp-zero-copy-2.4.21-suse-171.patch
deleted file mode 100644 (file)
index 8ef6cd2..0000000
+++ /dev/null
@@ -1,456 +0,0 @@
-Index: linux-2.4.21-171/include/linux/skbuff.h
-===================================================================
---- linux-2.4.21-171.orig/include/linux/skbuff.h       2004-03-31 14:58:38.000000000 -0500
-+++ linux-2.4.21-171/include/linux/skbuff.h    2004-04-03 16:43:15.000000000 -0500
-@@ -116,6 +116,30 @@
-       __u16 size;
- };
-+/* Support for callback when skb data has been released */
-+typedef struct zccd                           /* Zero Copy Callback Descriptor */
-+{                                             /* (embed as first member of custom struct) */
-+      atomic_t        zccd_count;             /* reference count */
-+      void           (*zccd_destructor)(struct zccd *); /* callback when refcount reaches zero */
-+} zccd_t;
-+
-+static inline void zccd_init (zccd_t *d, void (*callback)(zccd_t *))
-+{
-+      atomic_set (&d->zccd_count, 1);
-+      d->zccd_destructor = callback;
-+}
-+
-+static inline void zccd_get (zccd_t *d)               /* take a reference */
-+{
-+      atomic_inc (&d->zccd_count);
-+}
-+
-+static inline void zccd_put (zccd_t *d)               /* release a reference */
-+{
-+      if (atomic_dec_and_test (&d->zccd_count))
-+              (d->zccd_destructor)(d);
-+}
-+
- /* This data is invariant across clones and lives at
-  * the end of the header data, ie. at skb->end.
-  */
-@@ -123,6 +147,12 @@
-       atomic_t        dataref;
-       unsigned int    nr_frags;
-       struct sk_buff  *frag_list;
-+      zccd_t          *zccd;                  /* zero copy descriptor */
-+      zccd_t          *zccd2;                 /* 2nd zero copy descriptor */
-+      /* NB we expect zero-copy data to be at least 1 packet, so
-+       * having 2 zccds means we don't unneccessarily split the packet
-+       * where consecutive zero-copy sends abutt.
-+       */
-       skb_frag_t      frags[MAX_SKB_FRAGS];
- };
-Index: linux-2.4.21-171/include/net/tcp.h
-===================================================================
---- linux-2.4.21-171.orig/include/net/tcp.h    2004-03-31 15:07:31.000000000 -0500
-+++ linux-2.4.21-171/include/net/tcp.h 2004-04-03 16:46:15.000000000 -0500
-@@ -646,6 +646,8 @@
- extern int                    tcp_sendmsg(struct sock *sk, struct msghdr *msg, int size);
- extern ssize_t                        tcp_sendpage(struct socket *sock, struct page *page, int offset, size_t size, int flags);
-+extern ssize_t                        tcp_sendpage_zccd(struct socket *sock, struct page *page, int offset, ssize_t size,
-+                                                int flags, zccd_t *zccd);
- extern int                    tcp_ioctl(struct sock *sk, 
-                                         int cmd, 
-@@ -742,6 +744,9 @@
-                                           struct msghdr *msg,
-                                           int len, int nonblock, 
-                                           int flags, int *addr_len);
-+extern int                    tcp_recvpackets(struct sock *sk,
-+                                              struct sk_buff_head *packets,
-+                                              int len, int nonblock);
- extern int                    tcp_listen_start(struct sock *sk);
-Index: linux-2.4.21-171/net/netsyms.c
-===================================================================
---- linux-2.4.21-171.orig/net/netsyms.c        2004-02-24 14:03:22.000000000 -0500
-+++ linux-2.4.21-171/net/netsyms.c     2004-04-03 16:13:53.000000000 -0500
-@@ -407,6 +407,8 @@
- #endif
-+EXPORT_SYMBOL(tcp_sendpage_zccd);
-+EXPORT_SYMBOL(tcp_recvpackets);
- EXPORT_SYMBOL(tcp_read_sock);
- EXPORT_SYMBOL(netlink_set_err);
-Index: linux-2.4.21-171/net/core/skbuff.c
-===================================================================
---- linux-2.4.21-171.orig/net/core/skbuff.c    2004-02-24 14:03:22.000000000 -0500
-+++ linux-2.4.21-171/net/core/skbuff.c 2004-04-03 16:13:53.000000000 -0500
-@@ -208,6 +208,8 @@
-       atomic_set(&(skb_shinfo(skb)->dataref), 1);
-       skb_shinfo(skb)->nr_frags = 0;
-       skb_shinfo(skb)->frag_list = NULL;
-+      skb_shinfo(skb)->zccd = NULL;           /* skbuffs kick off with NO user zero copy descriptors */
-+      skb_shinfo(skb)->zccd2 = NULL;
-       return skb;
- nodata:
-@@ -277,6 +279,10 @@
- {
-       if (!skb->cloned ||
-           atomic_dec_and_test(&(skb_shinfo(skb)->dataref))) {
-+              if (skb_shinfo(skb)->zccd != NULL) /* zero copy callback descriptor? */
-+                      zccd_put (skb_shinfo(skb)->zccd); /* release hold */
-+              if (skb_shinfo(skb)->zccd2 != NULL) /* 2nd zero copy callback descriptor? */
-+                      zccd_put (skb_shinfo(skb)->zccd2); /* release hold */
-               if (skb_shinfo(skb)->nr_frags) {
-                       int i;
-                       for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
-@@ -535,6 +541,8 @@
-       atomic_set(&(skb_shinfo(skb)->dataref), 1);
-       skb_shinfo(skb)->nr_frags = 0;
-       skb_shinfo(skb)->frag_list = NULL;
-+      skb_shinfo(skb)->zccd = NULL;           /* copied data => no user zero copy descriptor */
-+      skb_shinfo(skb)->zccd2 = NULL;
-       /* We are no longer a clone, even if we were. */
-       skb->cloned = 0;
-@@ -581,6 +589,14 @@
-       n->data_len = skb->data_len;
-       n->len = skb->len;
-+      if (skb_shinfo(skb)->zccd != NULL)      /* user zero copy descriptor? */
-+              zccd_get (skb_shinfo(skb)->zccd); /* 1 more ref (pages are shared) */
-+      skb_shinfo(n)->zccd = skb_shinfo(skb)->zccd;
-+
-+      if (skb_shinfo(skb)->zccd2 != NULL)     /* 2nd user zero copy descriptor? */
-+              zccd_get (skb_shinfo(skb)->zccd2); /* 1 more ref (pages are shared) */
-+      skb_shinfo(n)->zccd2 = skb_shinfo(skb)->zccd2;
-+
-       if (skb_shinfo(skb)->nr_frags) {
-               int i;
-@@ -623,6 +639,8 @@
-       u8 *data;
-       int size = nhead + (skb->end - skb->head) + ntail;
-       long off;
-+      zccd_t *zccd = skb_shinfo(skb)->zccd;   /* stash user zero copy descriptor */
-+      zccd_t *zccd2 = skb_shinfo(skb)->zccd2; /* stash 2nd user zero copy descriptor */
-       if (skb_shared(skb))
-               BUG();
-@@ -644,6 +662,11 @@
-       if (skb_shinfo(skb)->frag_list)
-               skb_clone_fraglist(skb);
-+      if (zccd != NULL)                       /* user zero copy descriptor? */
-+              zccd_get (zccd);                /* extra ref (pages are shared) */
-+      if (zccd2 != NULL)                      /* 2nd user zero copy descriptor? */
-+              zccd_get (zccd2);               /* extra ref (pages are shared) */
-+
-       skb_release_data(skb);
-       off = (data+nhead) - skb->head;
-@@ -658,6 +681,8 @@
-       skb->nh.raw += off;
-       skb->cloned = 0;
-       atomic_set(&skb_shinfo(skb)->dataref, 1);
-+      skb_shinfo(skb)->zccd = zccd;
-+      skb_shinfo(skb)->zccd2 = zccd2;
-       return 0;
- nodata:
-Index: linux-2.4.21-171/net/ipv4/tcp.c
-===================================================================
---- linux-2.4.21-171.orig/net/ipv4/tcp.c       2004-02-24 13:42:30.000000000 -0500
-+++ linux-2.4.21-171/net/ipv4/tcp.c    2004-04-03 16:43:05.000000000 -0500
-@@ -748,7 +748,7 @@
-       goto out;
- }
--ssize_t do_tcp_sendpages(struct sock *sk, struct page **pages, int poffset, size_t psize, int flags);
-+ssize_t do_tcp_sendpages(struct sock *sk, struct page **pages, int poffset, size_t psize, int flags, zccd_t *zccd);
- static inline int
- can_coalesce(struct sk_buff *skb, int i, struct page *page, int off)
-@@ -827,7 +827,7 @@
-       return err;
- }
--ssize_t do_tcp_sendpages(struct sock *sk, struct page **pages, int poffset, size_t psize, int flags)
-+ssize_t do_tcp_sendpages(struct sock *sk, struct page **pages, int poffset, size_t psize, int flags, zccd_t *zccd)
- {
-       struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
-       int mss_now;
-@@ -875,6 +875,17 @@
-                       copy = size;
-               i = skb_shinfo(skb)->nr_frags;
-+
-+              if (zccd != NULL &&             /* this is a zcc I/O */
-+                  skb_shinfo(skb)->zccd != NULL && /* skb is part of a zcc I/O */
-+                  skb_shinfo(skb)->zccd2 != NULL &&
-+                  skb_shinfo(skb)->zccd != zccd && /* not the same one */
-+                  skb_shinfo(skb)->zccd2 != zccd)
-+              {
-+                      tcp_mark_push (tp, skb);
-+                      goto new_segment;
-+              }
-+
-               if (can_coalesce(skb, i, page, offset)) {
-                       skb_shinfo(skb)->frags[i-1].size += copy;
-               } else if (i < MAX_SKB_FRAGS) {
-@@ -885,6 +896,20 @@
-                       goto new_segment;
-               }
-+              if (zccd != NULL &&     /* this is a zcc I/O */
-+                  skb_shinfo(skb)->zccd != zccd && /* not already referencing this zccd */
-+                  skb_shinfo(skb)->zccd2 != zccd)
-+              {
-+                      zccd_get (zccd);        /* bump ref count */
-+
-+                      BUG_TRAP (skb_shinfo(skb)->zccd2 == NULL);
-+
-+                      if (skb_shinfo(skb)->zccd == NULL) /* reference this zccd */
-+                              skb_shinfo(skb)->zccd = zccd;
-+                      else
-+                              skb_shinfo(skb)->zccd2 = zccd;
-+              }
-+
-               skb->len += copy;
-               skb->data_len += copy;
-               skb->ip_summed = CHECKSUM_HW;
-@@ -948,7 +973,28 @@
-       lock_sock(sk);
-       TCP_CHECK_TIMER(sk);
--      res = do_tcp_sendpages(sk, &page, offset, size, flags);
-+      res = do_tcp_sendpages(sk, &page, offset, size, flags, NULL);
-+      TCP_CHECK_TIMER(sk);
-+      release_sock(sk);
-+      return res;
-+}
-+
-+ssize_t tcp_sendpage_zccd(struct socket *sock, struct page *page, int offset, ssize_t size,
-+                      int flags, zccd_t *zccd)
-+{
-+      ssize_t res;
-+      struct sock *sk = sock->sk;
-+
-+#define TCP_ZC_CSUM_FLAGS (NETIF_F_IP_CSUM|NETIF_F_NO_CSUM|NETIF_F_HW_CSUM)
-+
-+      if (!(sk->route_caps & NETIF_F_SG) ||   /* caller shouldn't waste her time */
-+          !(sk->route_caps & TCP_ZC_CSUM_FLAGS)) /* on double mapping */
-+              BUG ();
-+
-+      lock_sock(sk);
-+      TCP_CHECK_TIMER(sk);
-+
-+      res = do_tcp_sendpages(sk, &page, 0, size, flags, zccd);
-       TCP_CHECK_TIMER(sk);
-       release_sock(sk);
-       return res;
-@@ -1772,6 +1818,202 @@
-       goto out;
- }
-+int tcp_recvpackets (struct sock *sk, struct sk_buff_head *packets,
-+                   int len, int nonblock)
-+{
-+      struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
-+      int copied;
-+      long timeo;
-+
-+      BUG_TRAP (len > 0);
-+      /*BUG_TRAP ((flags & (MSG_OOB | MSG_PEEK | MSG_TRUNC)) == 0);*/
-+
-+      lock_sock(sk);
-+
-+      TCP_CHECK_TIMER(sk);
-+
-+      copied = -ENOTCONN;
-+      if (sk->state == TCP_LISTEN)
-+              goto out;
-+
-+      copied = 0;
-+      timeo = sock_rcvtimeo(sk, nonblock);
-+
-+      do {
-+              struct sk_buff * skb;
-+              u32 offset;
-+              unsigned long used;
-+              int exhausted;
-+              int eaten;
-+
-+              /* Are we at urgent data? Stop if we have read anything. */
-+              if (copied && tp->urg_data && tp->urg_seq == tp->copied_seq)
-+                      break;
-+
-+              /* We need to check signals first, to get correct SIGURG
-+               * handling. FIXME: Need to check this doesnt impact 1003.1g
-+               * and move it down to the bottom of the loop
-+               */
-+              if (signal_pending(current)) {
-+                      if (copied)
-+                              break;
-+                      copied = timeo ? sock_intr_errno(timeo) : -EAGAIN;
-+                      break;
-+              }
-+
-+              /* Next get a buffer. */
-+
-+              skb = skb_peek(&sk->receive_queue);
-+
-+              if (skb == NULL)                /* nothing ready */
-+              {
-+                      if (copied) {
-+                              if (sk->err ||
-+                                  sk->state == TCP_CLOSE ||
-+                                  (sk->shutdown & RCV_SHUTDOWN) ||
-+                                  !timeo ||
-+                                  (0))
-+                                      break;
-+                      } else {
-+                              if (sk->done)
-+                                      break;
-+
-+                              if (sk->err) {
-+                                      copied = sock_error(sk);
-+                                      break;
-+                              }
-+
-+                              if (sk->shutdown & RCV_SHUTDOWN)
-+                                      break;
-+
-+                              if (sk->state == TCP_CLOSE) {
-+                                      if (!sk->done) {
-+                                              /* This occurs when user tries to read
-+                                               * from never connected socket.
-+                                               */
-+                                              copied = -ENOTCONN;
-+                                              break;
-+                                      }
-+                                      break;
-+                              }
-+
-+                              if (!timeo) {
-+                                      copied = -EAGAIN;
-+                                      break;
-+                              }
-+                      }
-+
-+                      cleanup_rbuf(sk, copied);
-+                      timeo = tcp_data_wait(sk, timeo);
-+                      continue;
-+              }
-+
-+              BUG_TRAP (atomic_read (&skb->users) == 1);
-+
-+              exhausted = eaten = 0;
-+
-+              offset = tp->copied_seq - TCP_SKB_CB(skb)->seq;
-+              if (skb->h.th->syn)
-+                      offset--;
-+
-+              used = skb->len - offset;
-+
-+              if (tp->urg_data) {
-+                      u32 urg_offset = tp->urg_seq - tp->copied_seq;
-+                      if (urg_offset < used) {
-+                              if (!urg_offset) { /* at urgent date */
-+                                      if (!sk->urginline) {
-+                                              tp->copied_seq++; /* discard the single byte of urgent data */
-+                                              offset++;
-+                                              used--;
-+                                      }
-+                              } else          /* truncate read */
-+                                      used = urg_offset;
-+                      }
-+              }
-+
-+              BUG_TRAP (used >= 0);
-+              if (len < used)
-+                      used = len;
-+
-+              if (used == 0)
-+                      exhausted = 1;
-+              else
-+              {
-+                      if (skb_is_nonlinear (skb))
-+                      {
-+                              int   rc = skb_linearize (skb, GFP_KERNEL);
-+
-+                              printk ("tcp_recvpackets(): linearising: %d\n", rc);
-+
-+                              if (rc)
-+                              {
-+                                      if (!copied)
-+                                              copied = rc;
-+                                      break;
-+                              }
-+                      }
-+
-+                      if ((offset + used) == skb->len) /* consuming the whole packet */
-+                      {
-+                              __skb_unlink (skb, &sk->receive_queue);
-+                              dst_release (skb->dst);
-+                              skb_orphan (skb);
-+                              __skb_pull (skb, offset);
-+                              __skb_queue_tail (packets, skb);
-+                              exhausted = eaten = 1;
-+                      }
-+                      else                    /* consuming only part of the packet */
-+                      {
-+                              struct sk_buff *skb2 = skb_clone (skb, GFP_KERNEL);
-+
-+                              if (skb2 == NULL)
-+                              {
-+                                      if (!copied)
-+                                              copied = -ENOMEM;
-+                                      break;
-+                              }
-+
-+                              dst_release (skb2->dst);
-+                              __skb_pull (skb2, offset);
-+                              __skb_trim (skb2, used);
-+                              __skb_queue_tail (packets, skb2);
-+                      }
-+
-+                      tp->copied_seq += used;
-+                      copied += used;
-+                      len -= used;
-+              }
-+
-+              if (tp->urg_data && after(tp->copied_seq,tp->urg_seq)) {
-+                      tp->urg_data = 0;
-+                      tcp_fast_path_check(sk, tp);
-+              }
-+
-+              if (!exhausted)
-+                      continue;
-+
-+              if (skb->h.th->fin)
-+              {
-+                      tp->copied_seq++;
-+                      if (!eaten)
-+                              tcp_eat_skb (sk, skb);
-+                      break;
-+              }
-+
-+              if (!eaten)
-+                      tcp_eat_skb (sk, skb);
-+
-+      } while (len > 0);
-+
-+ out:
-+      /* Clean up data we have read: This will do ACK frames. */
-+      cleanup_rbuf(sk, copied);
-+      TCP_CHECK_TIMER(sk);
-+      release_sock(sk);
-+      return copied;
-+}
-+
- /*
-  *    State processing on a close. This implements the state shift for
-  *    sending our FIN frame. Note that we only send a FIN for some
diff --git a/lustre/kernel_patches/patches/uml-exprt-clearuser.patch b/lustre/kernel_patches/patches/uml-exprt-clearuser.patch
new file mode 100644 (file)
index 0000000..22992c1
--- /dev/null
@@ -0,0 +1,24 @@
+Index: uml-2.4.24/arch/um/kernel/tt/ksyms.c
+===================================================================
+--- uml-2.4.24.orig/arch/um/kernel/tt/ksyms.c  2005-05-04 13:59:58.000000000 +0300
++++ uml-2.4.24/arch/um/kernel/tt/ksyms.c       2005-05-05 23:49:51.508354472 +0300
+@@ -12,6 +12,7 @@
+ EXPORT_SYMBOL(__do_strncpy_from_user);
+ EXPORT_SYMBOL(__do_strnlen_user); 
+ EXPORT_SYMBOL(__do_clear_user);
++EXPORT_SYMBOL(clear_user_tt);
+ EXPORT_SYMBOL(tracing_pid);
+ EXPORT_SYMBOL(honeypot);
+Index: uml-2.4.24/arch/um/kernel/ksyms.c
+===================================================================
+--- uml-2.4.24.orig/arch/um/kernel/ksyms.c     2005-05-04 13:54:24.000000000 +0300
++++ uml-2.4.24/arch/um/kernel/ksyms.c  2005-05-05 23:50:19.649076432 +0300
+@@ -53,6 +53,7 @@
+ #ifdef CONFIG_MODE_SKAS
+ EXPORT_SYMBOL(copy_to_user_skas);
+ EXPORT_SYMBOL(copy_from_user_skas);
++EXPORT_SYMBOL(clear_user_skas);
+ #endif
+ EXPORT_SYMBOL(os_stat_fd);
index ade809d..9b62f6e 100644 (file)
@@ -13455,7 +13455,7 @@ diff -Naur -X ../exclude-files orig/arch/um/fs/hostfs/hostfs_user.c um/arch/um/f
 +              if(chown(file, -1, attrs->ia_gid)) return(-errno);
 +      }
 +      if(attrs->ia_valid & HOSTFS_ATTR_SIZE){
-+              if(truncate(file, attrs->ia_size)) return(-errno);
++              if(truncate64(file, attrs->ia_size)) return(-errno);
 +      }
 +      ma = HOSTFS_ATTR_ATIME_SET | HOSTFS_ATTR_MTIME_SET;
 +      if((attrs->ia_valid & ma) == ma){
diff --git a/lustre/kernel_patches/patches/uml-patch-2.4.29-1.patch b/lustre/kernel_patches/patches/uml-patch-2.4.29-1.patch
new file mode 100644 (file)
index 0000000..16f449d
--- /dev/null
@@ -0,0 +1,46719 @@
+Index: linux-2.4.29/arch/um/config_block.in
+===================================================================
+--- linux-2.4.29.orig/arch/um/config_block.in  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/config_block.in       2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,22 @@
++mainmenu_option next_comment
++comment 'Block Devices'
++
++bool 'Virtual block device' CONFIG_BLK_DEV_UBD
++dep_bool '  Always do synchronous disk IO for UBD' CONFIG_BLK_DEV_UBD_SYNC $CONFIG_BLK_DEV_UBD
++bool 'COW device' CONFIG_COW
++
++if [ "$CONFIG_BLK_DEV_UBD" = "y" -o "$CONFIG_COW" = "y" ] ; then
++      define_bool CONFIG_COW_COMMON y
++fi
++
++tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP
++dep_tristate 'Network block device support' CONFIG_BLK_DEV_NBD $CONFIG_NET
++tristate 'RAM disk support' CONFIG_BLK_DEV_RAM
++if [ "$CONFIG_BLK_DEV_RAM" = "y" -o "$CONFIG_BLK_DEV_RAM" = "m" ]; then
++      int '   Default RAM disk size' CONFIG_BLK_DEV_RAM_SIZE 4096
++fi
++dep_bool '  Initial RAM disk (initrd) support' CONFIG_BLK_DEV_INITRD $CONFIG_BLK_DEV_RAM
++
++tristate 'Example IO memory driver' CONFIG_MMAPPER
++
++endmenu
+Index: linux-2.4.29/arch/um/config_char.in
+===================================================================
+--- linux-2.4.29.orig/arch/um/config_char.in   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/config_char.in        2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,37 @@
++mainmenu_option next_comment
++comment 'Character Devices'
++
++define_bool CONFIG_STDIO_CONSOLE y
++
++bool 'Virtual serial line' CONFIG_SSL
++
++bool 'file descriptor channel support' CONFIG_FD_CHAN
++bool 'null channel support' CONFIG_NULL_CHAN
++bool 'port channel support' CONFIG_PORT_CHAN
++bool 'pty channel support' CONFIG_PTY_CHAN
++bool 'tty channel support' CONFIG_TTY_CHAN
++bool 'xterm channel support' CONFIG_XTERM_CHAN
++string 'Default main console channel initialization' CONFIG_CON_ZERO_CHAN \
++            "fd:0,fd:1"
++string 'Default console channel initialization' CONFIG_CON_CHAN "xterm"
++string 'Default serial line channel initialization' CONFIG_SSL_CHAN "pty"
++
++
++bool 'Unix98 PTY support' CONFIG_UNIX98_PTYS
++if [ "$CONFIG_UNIX98_PTYS" = "y" ]; then
++   int 'Maximum number of Unix98 PTYs in use (0-2048)' CONFIG_UNIX98_PTY_COUNT 256
++fi
++
++bool 'Watchdog Timer Support' CONFIG_WATCHDOG
++dep_bool '  Disable watchdog shutdown on close' CONFIG_WATCHDOG_NOWAYOUT \
++      $CONFIG_WATCHDOG
++dep_tristate '  Software Watchdog' CONFIG_SOFT_WATCHDOG $CONFIG_WATCHDOG
++dep_tristate '  UML watchdog' CONFIG_UML_WATCHDOG $CONFIG_WATCHDOG
++
++tristate 'Sound support' CONFIG_UML_SOUND
++define_tristate CONFIG_SOUND $CONFIG_UML_SOUND
++define_tristate CONFIG_HOSTAUDIO $CONFIG_UML_SOUND
++
++bool 'Enable tty logging' CONFIG_TTY_LOG
++
++endmenu
+Index: linux-2.4.29/arch/um/config.in
+===================================================================
+--- linux-2.4.29.orig/arch/um/config.in        1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/config.in     2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,123 @@
++define_bool CONFIG_USERMODE y
++
++mainmenu_name "Linux/Usermode Kernel Configuration"
++
++define_bool CONFIG_ISA n
++define_bool CONFIG_SBUS n
++define_bool CONFIG_PCI n
++
++define_bool CONFIG_UID16 y
++
++define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM y
++
++mainmenu_option next_comment
++comment 'Code maturity level options'
++bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL
++endmenu
++
++mainmenu_option next_comment
++comment 'General Setup'
++
++bool 'Separate kernel address space support' CONFIG_MODE_SKAS
++
++# This is to ensure that at least one of the modes is enabled.  When neither
++# is present in defconfig, they default to N, which is bad.
++if [ "$CONFIG_MODE_SKAS" != "y" ]; then
++   define_bool CONFIG_MODE_TT y
++fi
++
++bool 'Tracing thread support' CONFIG_MODE_TT
++if [ "$CONFIG_MODE_TT" != "y" ]; then
++   bool 'Statically linked binary when CONFIG_MODE_TT is disabled' CONFIG_STATIC_LINK
++fi
++bool 'Networking support' CONFIG_NET
++bool 'System V IPC' CONFIG_SYSVIPC
++bool 'BSD Process Accounting' CONFIG_BSD_PROCESS_ACCT
++bool 'Sysctl support' CONFIG_SYSCTL
++tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT
++tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
++tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC
++
++tristate 'Host filesystem' CONFIG_HOSTFS
++tristate 'Usable host filesystem' CONFIG_HUMFS
++
++if [ "$CONFIG_HOSTFS" = "y" -o "$CONFIG_HUMFS" = "y" ]; then
++    define_tristate CONFIG_EXTERNFS y
++fi
++
++tristate 'Honeypot proc filesystem' CONFIG_HPPFS
++bool 'Management console' CONFIG_MCONSOLE
++dep_bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ $CONFIG_MCONSOLE
++bool '2G/2G host address space split' CONFIG_HOST_2G_2G
++
++bool 'Symmetric multi-processing support' CONFIG_UML_SMP
++define_bool CONFIG_SMP $CONFIG_UML_SMP
++if [ "$CONFIG_SMP" = "y" ]; then
++    int  'Maximum number of CPUs (2-32)' CONFIG_NR_CPUS 32
++fi
++
++int 'Nesting level' CONFIG_NEST_LEVEL 0
++int 'Kernel address space size (in .5G units)' CONFIG_KERNEL_HALF_GIGS 1
++bool 'Highmem support' CONFIG_HIGHMEM
++bool '/proc/mm' CONFIG_PROC_MM
++int 'Kernel stack size order' CONFIG_KERNEL_STACK_ORDER 2
++bool 'Real-time Clock' CONFIG_UML_REAL_TIME_CLOCK
++endmenu
++
++mainmenu_option next_comment
++comment 'Loadable module support'
++bool 'Enable loadable module support' CONFIG_MODULES
++if [ "$CONFIG_MODULES" = "y" ]; then
++# MODVERSIONS does not yet work in this architecture
++#   bool '  Set version information on all module symbols' CONFIG_MODVERSIONS
++    bool '  Kernel module loader' CONFIG_KMOD
++fi
++endmenu
++
++source arch/um/config_char.in
++
++source arch/um/config_block.in
++
++define_bool CONFIG_NETDEVICES $CONFIG_NET
++
++if [ "$CONFIG_NET" = "y" ]; then
++   source arch/um/config_net.in
++   source net/Config.in
++fi
++
++source fs/Config.in
++
++mainmenu_option next_comment
++comment 'SCSI support'
++
++tristate 'SCSI support' CONFIG_SCSI
++
++if [ "$CONFIG_SCSI" != "n" ]; then
++   source arch/um/config_scsi.in
++fi
++endmenu
++
++source drivers/md/Config.in
++
++source drivers/mtd/Config.in
++
++source lib/Config.in
++
++source crypto/Config.in
++
++mainmenu_option next_comment
++comment 'Kernel hacking'
++bool 'Debug memory allocations' CONFIG_DEBUG_SLAB
++bool 'Enable kernel debugging symbols' CONFIG_DEBUGSYM
++if [ "$CONFIG_XTERM_CHAN" = "y" ]; then
++   dep_bool 'Enable ptrace proxy' CONFIG_PT_PROXY $CONFIG_DEBUGSYM
++else 
++   define_bool CONFIG_PT_PROXY n
++fi
++
++if [ "$CONFIG_MODE_TT" = "n" ]; then
++   dep_bool 'Enable gprof support' CONFIG_GPROF $CONFIG_DEBUGSYM
++fi
++
++dep_bool 'Enable gcov support' CONFIG_GCOV $CONFIG_DEBUGSYM
++endmenu
+Index: linux-2.4.29/arch/um/config_net.in
+===================================================================
+--- linux-2.4.29.orig/arch/um/config_net.in    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/config_net.in 2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,48 @@
++mainmenu_option next_comment
++comment 'Network Devices'
++
++# UML virtual driver
++bool 'Virtual network device' CONFIG_UML_NET
++
++dep_bool '  Ethertap transport' CONFIG_UML_NET_ETHERTAP $CONFIG_UML_NET
++dep_bool '  TUN/TAP transport' CONFIG_UML_NET_TUNTAP $CONFIG_UML_NET
++dep_bool '  SLIP transport' CONFIG_UML_NET_SLIP $CONFIG_UML_NET
++dep_bool '  SLiRP transport' CONFIG_UML_NET_SLIRP $CONFIG_UML_NET
++dep_bool '  Daemon transport' CONFIG_UML_NET_DAEMON $CONFIG_UML_NET
++dep_bool '  Multicast transport' CONFIG_UML_NET_MCAST $CONFIG_UML_NET
++dep_bool '  pcap transport' CONFIG_UML_NET_PCAP $CONFIG_UML_NET
++
++# Below are hardware-independent drivers mirrored from
++# drivers/net/Config.in. It would be nice if Linux
++# had HW independent drivers separated from the other
++# but it does not. Until then each non-ISA/PCI arch
++# needs to provide it's own menu of network drivers
++
++tristate 'Dummy net driver support' CONFIG_DUMMY
++tristate 'Bonding driver support' CONFIG_BONDING
++tristate 'EQL (serial line load balancing) support' CONFIG_EQUALIZER
++tristate 'Universal TUN/TAP device driver support' CONFIG_TUN
++if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
++   if [ "$CONFIG_NETLINK" = "y" ]; then
++      tristate 'Ethertap network tap (OBSOLETE)' CONFIG_ETHERTAP
++   fi
++fi
++
++tristate 'PPP (point-to-point protocol) support' CONFIG_PPP
++if [ ! "$CONFIG_PPP" = "n" ]; then
++   dep_bool '  PPP multilink support (EXPERIMENTAL)' CONFIG_PPP_MULTILINK $CONFIG_EXPERIMENTAL
++   dep_bool '  PPP filtering' CONFIG_PPP_FILTER $CONFIG_FILTER
++   dep_tristate '  PPP support for async serial ports' CONFIG_PPP_ASYNC $CONFIG_PPP
++   dep_tristate '  PPP support for sync tty ports' CONFIG_PPP_SYNC_TTY $CONFIG_PPP
++   dep_tristate '  PPP Deflate compression' CONFIG_PPP_DEFLATE $CONFIG_PPP
++   dep_tristate '  PPP BSD-Compress compression' CONFIG_PPP_BSDCOMP $CONFIG_PPP
++   dep_tristate '  PPP over Ethernet (EXPERIMENTAL)' CONFIG_PPPOE $CONFIG_PPP $CONFIG_EXPERIMENTAL
++   dep_tristate '  PPP MPPE compression (encryption)' CONFIG_PPP_MPPE $CONFIG_PPP
++fi
++
++tristate 'SLIP (serial line) support' CONFIG_SLIP
++dep_bool '  CSLIP compressed headers' CONFIG_SLIP_COMPRESSED $CONFIG_SLIP
++dep_bool '  Keepalive and linefill' CONFIG_SLIP_SMART $CONFIG_SLIP
++dep_bool '  Six bit SLIP encapsulation' CONFIG_SLIP_MODE_SLIP6 $CONFIG_SLIP
++
++endmenu
+Index: linux-2.4.29/arch/um/config.release
+===================================================================
+--- linux-2.4.29.orig/arch/um/config.release   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/config.release        2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,302 @@
++#
++# Automatically generated make config: don't edit
++#
++CONFIG_USERMODE=y
++# CONFIG_ISA is not set
++# CONFIG_SBUS is not set
++# CONFIG_PCI is not set
++CONFIG_UID16=y
++CONFIG_RWSEM_XCHGADD_ALGORITHM=y
++
++#
++# Code maturity level options
++#
++CONFIG_EXPERIMENTAL=y
++
++#
++# General Setup
++#
++CONFIG_NET=y
++CONFIG_SYSVIPC=y
++CONFIG_BSD_PROCESS_ACCT=y
++CONFIG_SYSCTL=y
++CONFIG_BINFMT_AOUT=y
++CONFIG_BINFMT_ELF=y
++CONFIG_BINFMT_MISC=y
++CONFIG_HOSTFS=y
++# CONFIG_HPPFS is not set
++CONFIG_MCONSOLE=y
++CONFIG_MAGIC_SYSRQ=y
++# CONFIG_HOST_2G_2G is not set
++# CONFIG_UML_SMP is not set
++# CONFIG_SMP is not set
++CONFIG_NEST_LEVEL=0
++CONFIG_KERNEL_HALF_GIGS=1
++
++#
++# Loadable module support
++#
++CONFIG_MODULES=y
++CONFIG_KMOD=y
++
++#
++# Character Devices
++#
++CONFIG_STDIO_CONSOLE=y
++CONFIG_SSL=y
++CONFIG_FD_CHAN=y
++# CONFIG_NULL_CHAN is not set
++CONFIG_PORT_CHAN=y
++CONFIG_PTY_CHAN=y
++CONFIG_TTY_CHAN=y
++CONFIG_XTERM_CHAN=y
++CONFIG_CON_ZERO_CHAN="fd:0,fd:1"
++CONFIG_CON_CHAN="xterm"
++CONFIG_SSL_CHAN="pty"
++CONFIG_UNIX98_PTYS=y
++CONFIG_UNIX98_PTY_COUNT=256
++# CONFIG_WATCHDOG is not set
++CONFIG_UML_SOUND=y
++CONFIG_SOUND=y
++CONFIG_HOSTAUDIO=y
++# CONFIG_TTY_LOG is not set
++
++#
++# Block Devices
++#
++CONFIG_BLK_DEV_UBD=y
++# CONFIG_BLK_DEV_UBD_SYNC is not set
++CONFIG_BLK_DEV_LOOP=y
++CONFIG_BLK_DEV_NBD=y
++CONFIG_BLK_DEV_RAM=y
++CONFIG_BLK_DEV_RAM_SIZE=4096
++CONFIG_BLK_DEV_INITRD=y
++# CONFIG_MMAPPER is not set
++CONFIG_NETDEVICES=y
++
++#
++# Network Devices
++#
++CONFIG_UML_NET=y
++CONFIG_UML_NET_ETHERTAP=y
++CONFIG_UML_NET_TUNTAP=y
++CONFIG_UML_NET_SLIP=y
++CONFIG_UML_NET_DAEMON=y
++CONFIG_UML_NET_MCAST=y
++CONFIG_DUMMY=y
++CONFIG_BONDING=m
++CONFIG_EQUALIZER=m
++CONFIG_TUN=y
++CONFIG_PPP=m
++CONFIG_PPP_MULTILINK=y
++# CONFIG_PPP_ASYNC is not set
++CONFIG_PPP_SYNC_TTY=m
++CONFIG_PPP_DEFLATE=m
++CONFIG_PPP_BSDCOMP=m
++CONFIG_PPPOE=m
++CONFIG_SLIP=m
++
++#
++# Networking options
++#
++CONFIG_PACKET=y
++CONFIG_PACKET_MMAP=y
++# CONFIG_NETLINK_DEV is not set
++# CONFIG_NETFILTER is not set
++# CONFIG_FILTER is not set
++CONFIG_UNIX=y
++CONFIG_INET=y
++# CONFIG_IP_MULTICAST is not set
++# CONFIG_IP_ADVANCED_ROUTER is not set
++# CONFIG_IP_PNP is not set
++# CONFIG_NET_IPIP is not set
++# CONFIG_NET_IPGRE is not set
++# CONFIG_ARPD is not set
++# CONFIG_INET_ECN is not set
++# CONFIG_SYN_COOKIES is not set
++# CONFIG_IPV6 is not set
++# CONFIG_KHTTPD is not set
++# CONFIG_ATM is not set
++# CONFIG_VLAN_8021Q is not set
++
++#
++#  
++#
++# CONFIG_IPX is not set
++# CONFIG_ATALK is not set
++
++#
++# Appletalk devices
++#
++# CONFIG_DECNET is not set
++# CONFIG_BRIDGE is not set
++# CONFIG_X25 is not set
++# CONFIG_LAPB is not set
++# CONFIG_LLC is not set
++# CONFIG_NET_DIVERT is not set
++# CONFIG_ECONET is not set
++# CONFIG_WAN_ROUTER is not set
++# CONFIG_NET_FASTROUTE is not set
++# CONFIG_NET_HW_FLOWCONTROL is not set
++
++#
++# QoS and/or fair queueing
++#
++# CONFIG_NET_SCHED is not set
++
++#
++# Network testing
++#
++# CONFIG_NET_PKTGEN is not set
++
++#
++# File systems
++#
++CONFIG_QUOTA=y
++CONFIG_AUTOFS_FS=m
++CONFIG_AUTOFS4_FS=m
++CONFIG_REISERFS_FS=m
++# CONFIG_REISERFS_CHECK is not set
++# CONFIG_REISERFS_PROC_INFO is not set
++CONFIG_ADFS_FS=m
++# CONFIG_ADFS_FS_RW is not set
++CONFIG_AFFS_FS=m
++CONFIG_HFS_FS=m
++CONFIG_BFS_FS=m
++CONFIG_EXT3_FS=y
++CONFIG_JBD=y
++# CONFIG_JBD_DEBUG is not set
++CONFIG_FAT_FS=y
++CONFIG_MSDOS_FS=y
++CONFIG_UMSDOS_FS=y
++CONFIG_VFAT_FS=y
++CONFIG_EFS_FS=m
++CONFIG_CRAMFS=m
++CONFIG_TMPFS=y
++CONFIG_RAMFS=y
++CONFIG_ISO9660_FS=y
++# CONFIG_JOLIET is not set
++# CONFIG_ZISOFS is not set
++CONFIG_MINIX_FS=m
++CONFIG_VXFS_FS=m
++# CONFIG_NTFS_FS is not set
++CONFIG_HPFS_FS=m
++CONFIG_PROC_FS=y
++CONFIG_DEVFS_FS=y
++CONFIG_DEVFS_MOUNT=y
++# CONFIG_DEVFS_DEBUG is not set
++CONFIG_DEVPTS_FS=y
++CONFIG_QNX4FS_FS=m
++# CONFIG_QNX4FS_RW is not set
++CONFIG_ROMFS_FS=m
++CONFIG_EXT2_FS=y
++CONFIG_SYSV_FS=m
++CONFIG_UDF_FS=m
++# CONFIG_UDF_RW is not set
++CONFIG_UFS_FS=m
++# CONFIG_UFS_FS_WRITE is not set
++
++#
++# Network File Systems
++#
++# CONFIG_CODA_FS is not set
++# CONFIG_INTERMEZZO_FS is not set
++CONFIG_NFS_FS=y
++CONFIG_NFS_V3=y
++CONFIG_NFSD=y
++CONFIG_NFSD_V3=y
++CONFIG_SUNRPC=y
++CONFIG_LOCKD=y
++CONFIG_LOCKD_V4=y
++# CONFIG_SMB_FS is not set
++# CONFIG_NCP_FS is not set
++# CONFIG_ZISOFS_FS is not set
++CONFIG_ZLIB_FS_INFLATE=m
++
++#
++# Partition Types
++#
++# CONFIG_PARTITION_ADVANCED is not set
++CONFIG_MSDOS_PARTITION=y
++# CONFIG_SMB_NLS is not set
++CONFIG_NLS=y
++
++#
++# Native Language Support
++#
++CONFIG_NLS_DEFAULT="iso8859-1"
++# CONFIG_NLS_CODEPAGE_437 is not set
++# CONFIG_NLS_CODEPAGE_737 is not set
++# CONFIG_NLS_CODEPAGE_775 is not set
++# CONFIG_NLS_CODEPAGE_850 is not set
++# CONFIG_NLS_CODEPAGE_852 is not set
++# CONFIG_NLS_CODEPAGE_855 is not set
++# CONFIG_NLS_CODEPAGE_857 is not set
++# CONFIG_NLS_CODEPAGE_860 is not set
++# CONFIG_NLS_CODEPAGE_861 is not set
++# CONFIG_NLS_CODEPAGE_862 is not set
++# CONFIG_NLS_CODEPAGE_863 is not set
++# CONFIG_NLS_CODEPAGE_864 is not set
++# CONFIG_NLS_CODEPAGE_865 is not set
++# CONFIG_NLS_CODEPAGE_866 is not set
++# CONFIG_NLS_CODEPAGE_869 is not set
++# CONFIG_NLS_CODEPAGE_936 is not set
++# CONFIG_NLS_CODEPAGE_950 is not set
++# CONFIG_NLS_CODEPAGE_932 is not set
++# CONFIG_NLS_CODEPAGE_949 is not set
++# CONFIG_NLS_CODEPAGE_874 is not set
++# CONFIG_NLS_ISO8859_8 is not set
++# CONFIG_NLS_CODEPAGE_1250 is not set
++# CONFIG_NLS_CODEPAGE_1251 is not set
++# CONFIG_NLS_ISO8859_1 is not set
++# CONFIG_NLS_ISO8859_2 is not set
++# CONFIG_NLS_ISO8859_3 is not set
++# CONFIG_NLS_ISO8859_4 is not set
++# CONFIG_NLS_ISO8859_5 is not set
++# CONFIG_NLS_ISO8859_6 is not set
++# CONFIG_NLS_ISO8859_7 is not set
++# CONFIG_NLS_ISO8859_9 is not set
++# CONFIG_NLS_ISO8859_13 is not set
++# CONFIG_NLS_ISO8859_14 is not set
++# CONFIG_NLS_ISO8859_15 is not set
++# CONFIG_NLS_KOI8_R is not set
++# CONFIG_NLS_KOI8_U is not set
++# CONFIG_NLS_UTF8 is not set
++
++#
++# SCSI support
++#
++CONFIG_SCSI=y
++
++#
++# SCSI support type (disk, tape, CD-ROM)
++#
++# CONFIG_BLK_DEV_SD is not set
++# CONFIG_CHR_DEV_ST is not set
++# CONFIG_BLK_DEV_SR is not set
++# CONFIG_CHR_DEV_SG is not set
++
++#
++# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
++#
++# CONFIG_SCSI_DEBUG_QUEUES is not set
++# CONFIG_SCSI_MULTI_LUN is not set
++# CONFIG_SCSI_CONSTANTS is not set
++# CONFIG_SCSI_LOGGING is not set
++CONFIG_SCSI_DEBUG=m
++
++#
++# Multi-device support (RAID and LVM)
++#
++# CONFIG_MD is not set
++
++#
++# Memory Technology Devices (MTD)
++#
++# CONFIG_MTD is not set
++
++#
++# Kernel hacking
++#
++# CONFIG_DEBUG_SLAB is not set
++# CONFIG_DEBUGSYM is not set
+Index: linux-2.4.29/arch/um/config_scsi.in
+===================================================================
+--- linux-2.4.29.orig/arch/um/config_scsi.in   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/config_scsi.in        2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,30 @@
++comment 'SCSI support type (disk, tape, CD-ROM)'
++
++dep_tristate '  SCSI disk support' CONFIG_BLK_DEV_SD $CONFIG_SCSI
++
++if [ "$CONFIG_BLK_DEV_SD" != "n" ]; then
++   int  'Maximum number of SCSI disks that can be loaded as modules' CONFIG_SD_EXTRA_DEVS 40
++fi
++
++dep_tristate '  SCSI tape support' CONFIG_CHR_DEV_ST $CONFIG_SCSI
++
++dep_tristate '  SCSI CD-ROM support' CONFIG_BLK_DEV_SR $CONFIG_SCSI
++
++if [ "$CONFIG_BLK_DEV_SR" != "n" ]; then
++   bool '    Enable vendor-specific extensions (for SCSI CDROM)' CONFIG_BLK_DEV_SR_VENDOR
++   int  'Maximum number of CDROM devices that can be loaded as modules' CONFIG_SR_EXTRA_DEVS 2
++fi
++dep_tristate '  SCSI generic support' CONFIG_CHR_DEV_SG $CONFIG_SCSI
++
++comment 'Some SCSI devices (e.g. CD jukebox) support multiple LUNs'
++
++#if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
++   bool '  Enable extra checks in new queueing code' CONFIG_SCSI_DEBUG_QUEUES
++#fi
++
++bool '  Probe all LUNs on each SCSI device' CONFIG_SCSI_MULTI_LUN
++  
++bool '  Verbose SCSI error reporting (kernel size +=12K)' CONFIG_SCSI_CONSTANTS
++bool '  SCSI logging facility' CONFIG_SCSI_LOGGING
++
++dep_tristate 'SCSI debugging host simulator (EXPERIMENTAL)' CONFIG_SCSI_DEBUG $CONFIG_SCSI
+Index: linux-2.4.29/arch/um/defconfig
+===================================================================
+--- linux-2.4.29.orig/arch/um/defconfig        1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/defconfig     2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,430 @@
++#
++# Automatically generated make config: don't edit
++#
++CONFIG_USERMODE=y
++# CONFIG_ISA is not set
++# CONFIG_SBUS is not set
++# CONFIG_PCI is not set
++CONFIG_UID16=y
++CONFIG_RWSEM_XCHGADD_ALGORITHM=y
++
++#
++# Code maturity level options
++#
++CONFIG_EXPERIMENTAL=y
++
++#
++# General Setup
++#
++CONFIG_MODE_SKAS=y
++CONFIG_MODE_TT=y
++CONFIG_NET=y
++CONFIG_SYSVIPC=y
++CONFIG_BSD_PROCESS_ACCT=y
++CONFIG_SYSCTL=y
++CONFIG_BINFMT_AOUT=y
++CONFIG_BINFMT_ELF=y
++CONFIG_BINFMT_MISC=y
++CONFIG_HOSTFS=y
++CONFIG_HUMFS=y
++CONFIG_EXTERNFS=y
++CONFIG_HPPFS=y
++CONFIG_MCONSOLE=y
++CONFIG_MAGIC_SYSRQ=y
++# CONFIG_HOST_2G_2G is not set
++# CONFIG_UML_SMP is not set
++# CONFIG_SMP is not set
++CONFIG_NEST_LEVEL=0
++CONFIG_KERNEL_HALF_GIGS=1
++# CONFIG_HIGHMEM is not set
++CONFIG_PROC_MM=y
++CONFIG_KERNEL_STACK_ORDER=2
++CONFIG_UML_REAL_TIME_CLOCK=y
++
++#
++# Loadable module support
++#
++CONFIG_MODULES=y
++# CONFIG_KMOD is not set
++
++#
++# Character Devices
++#
++CONFIG_STDIO_CONSOLE=y
++CONFIG_SSL=y
++CONFIG_FD_CHAN=y
++CONFIG_NULL_CHAN=y
++CONFIG_PORT_CHAN=y
++CONFIG_PTY_CHAN=y
++CONFIG_TTY_CHAN=y
++CONFIG_XTERM_CHAN=y
++CONFIG_CON_ZERO_CHAN="fd:0,fd:1"
++CONFIG_CON_CHAN="xterm"
++CONFIG_SSL_CHAN="pty"
++CONFIG_UNIX98_PTYS=y
++CONFIG_UNIX98_PTY_COUNT=256
++# CONFIG_WATCHDOG is not set
++# CONFIG_WATCHDOG_NOWAYOUT is not set
++# CONFIG_SOFT_WATCHDOG is not set
++# CONFIG_UML_WATCHDOG is not set
++CONFIG_UML_SOUND=y
++CONFIG_SOUND=y
++CONFIG_HOSTAUDIO=y
++# CONFIG_TTY_LOG is not set
++
++#
++# Block Devices
++#
++CONFIG_BLK_DEV_UBD=y
++# CONFIG_BLK_DEV_UBD_SYNC is not set
++# CONFIG_COW is not set
++CONFIG_COW_COMMON=y
++CONFIG_BLK_DEV_LOOP=y
++CONFIG_BLK_DEV_NBD=y
++CONFIG_BLK_DEV_RAM=y
++CONFIG_BLK_DEV_RAM_SIZE=4096
++CONFIG_BLK_DEV_INITRD=y
++# CONFIG_MMAPPER is not set
++CONFIG_NETDEVICES=y
++
++#
++# Network Devices
++#
++CONFIG_UML_NET=y
++CONFIG_UML_NET_ETHERTAP=y
++CONFIG_UML_NET_TUNTAP=y
++CONFIG_UML_NET_SLIP=y
++CONFIG_UML_NET_SLIRP=y
++CONFIG_UML_NET_DAEMON=y
++CONFIG_UML_NET_MCAST=y
++# CONFIG_UML_NET_PCAP is not set
++CONFIG_DUMMY=y
++# CONFIG_BONDING is not set
++# CONFIG_EQUALIZER is not set
++CONFIG_TUN=y
++CONFIG_PPP=y
++# CONFIG_PPP_MULTILINK is not set
++# CONFIG_PPP_FILTER is not set
++# CONFIG_PPP_ASYNC is not set
++# CONFIG_PPP_SYNC_TTY is not set
++# CONFIG_PPP_DEFLATE is not set
++# CONFIG_PPP_BSDCOMP is not set
++# CONFIG_PPPOE is not set
++# CONFIG_PPP_MPPE is not set
++CONFIG_SLIP=y
++# CONFIG_SLIP_COMPRESSED is not set
++# CONFIG_SLIP_SMART is not set
++# CONFIG_SLIP_MODE_SLIP6 is not set
++
++#
++# Networking options
++#
++CONFIG_PACKET=y
++CONFIG_PACKET_MMAP=y
++# CONFIG_NETLINK_DEV is not set
++# CONFIG_NETFILTER is not set
++# CONFIG_FILTER is not set
++CONFIG_UNIX=y
++CONFIG_INET=y
++# CONFIG_IP_MULTICAST is not set
++# CONFIG_IP_ADVANCED_ROUTER is not set
++# CONFIG_IP_PNP is not set
++# CONFIG_NET_IPIP is not set
++# CONFIG_NET_IPGRE is not set
++# CONFIG_ARPD is not set
++# CONFIG_INET_ECN is not set
++# CONFIG_SYN_COOKIES is not set
++# CONFIG_IPV6 is not set
++# CONFIG_KHTTPD is not set
++
++#
++#    SCTP Configuration (EXPERIMENTAL)
++#
++CONFIG_IPV6_SCTP__=y
++# CONFIG_IP_SCTP is not set
++# CONFIG_ATM is not set
++# CONFIG_VLAN_8021Q is not set
++
++#
++#  
++#
++# CONFIG_IPX is not set
++# CONFIG_ATALK is not set
++
++#
++# Appletalk devices
++#
++# CONFIG_DEV_APPLETALK is not set
++# CONFIG_DECNET is not set
++# CONFIG_BRIDGE is not set
++# CONFIG_X25 is not set
++# CONFIG_LAPB is not set
++# CONFIG_LLC is not set
++# CONFIG_NET_DIVERT is not set
++# CONFIG_ECONET is not set
++# CONFIG_WAN_ROUTER is not set
++# CONFIG_NET_FASTROUTE is not set
++# CONFIG_NET_HW_FLOWCONTROL is not set
++
++#
++# QoS and/or fair queueing
++#
++# CONFIG_NET_SCHED is not set
++
++#
++# Network testing
++#
++# CONFIG_NET_PKTGEN is not set
++
++#
++# File systems
++#
++CONFIG_QUOTA=y
++# CONFIG_QFMT_V2 is not set
++CONFIG_AUTOFS_FS=y
++CONFIG_AUTOFS4_FS=y
++CONFIG_REISERFS_FS=y
++# CONFIG_REISERFS_CHECK is not set
++# CONFIG_REISERFS_PROC_INFO is not set
++# CONFIG_ADFS_FS is not set
++# CONFIG_ADFS_FS_RW is not set
++# CONFIG_AFFS_FS is not set
++# CONFIG_HFS_FS is not set
++# CONFIG_HFSPLUS_FS is not set
++# CONFIG_BEFS_FS is not set
++# CONFIG_BEFS_DEBUG is not set
++# CONFIG_BFS_FS is not set
++CONFIG_EXT3_FS=y
++CONFIG_JBD=y
++# CONFIG_JBD_DEBUG is not set
++CONFIG_FAT_FS=y
++CONFIG_MSDOS_FS=y
++CONFIG_UMSDOS_FS=y
++CONFIG_VFAT_FS=y
++# CONFIG_EFS_FS is not set
++CONFIG_JFFS_FS=y
++CONFIG_JFFS_FS_VERBOSE=0
++CONFIG_JFFS_PROC_FS=y
++CONFIG_JFFS2_FS=y
++CONFIG_JFFS2_FS_DEBUG=0
++# CONFIG_CRAMFS is not set
++CONFIG_TMPFS=y
++CONFIG_RAMFS=y
++CONFIG_ISO9660_FS=y
++# CONFIG_JOLIET is not set
++# CONFIG_ZISOFS is not set
++# CONFIG_JFS_FS is not set
++# CONFIG_JFS_DEBUG is not set
++# CONFIG_JFS_STATISTICS is not set
++CONFIG_MINIX_FS=y
++# CONFIG_VXFS_FS is not set
++# CONFIG_NTFS_FS is not set
++# CONFIG_NTFS_RW is not set
++# CONFIG_HPFS_FS is not set
++CONFIG_PROC_FS=y
++CONFIG_DEVFS_FS=y
++CONFIG_DEVFS_MOUNT=y
++# CONFIG_DEVFS_DEBUG is not set
++CONFIG_DEVPTS_FS=y
++# CONFIG_QNX4FS_FS is not set
++# CONFIG_QNX4FS_RW is not set
++# CONFIG_ROMFS_FS is not set
++CONFIG_EXT2_FS=y
++# CONFIG_SYSV_FS is not set
++# CONFIG_UDF_FS is not set
++# CONFIG_UDF_RW is not set
++# CONFIG_UFS_FS is not set
++# CONFIG_UFS_FS_WRITE is not set
++# CONFIG_XFS_FS is not set
++# CONFIG_XFS_QUOTA is not set
++# CONFIG_XFS_RT is not set
++# CONFIG_XFS_TRACE is not set
++# CONFIG_XFS_DEBUG is not set
++
++#
++# Network File Systems
++#
++# CONFIG_CODA_FS is not set
++# CONFIG_INTERMEZZO_FS is not set
++# CONFIG_NFS_FS is not set
++# CONFIG_NFS_V3 is not set
++# CONFIG_NFS_DIRECTIO is not set
++# CONFIG_ROOT_NFS is not set
++# CONFIG_NFSD is not set
++# CONFIG_NFSD_V3 is not set
++# CONFIG_NFSD_TCP is not set
++# CONFIG_SUNRPC is not set
++# CONFIG_LOCKD is not set
++# CONFIG_SMB_FS is not set
++# CONFIG_NCP_FS is not set
++# CONFIG_NCPFS_PACKET_SIGNING is not set
++# CONFIG_NCPFS_IOCTL_LOCKING is not set
++# CONFIG_NCPFS_STRONG is not set
++# CONFIG_NCPFS_NFS_NS is not set
++# CONFIG_NCPFS_OS2_NS is not set
++# CONFIG_NCPFS_SMALLDOS is not set
++# CONFIG_NCPFS_NLS is not set
++# CONFIG_NCPFS_EXTRAS is not set
++# CONFIG_ZISOFS_FS is not set
++
++#
++# Partition Types
++#
++# CONFIG_PARTITION_ADVANCED is not set
++CONFIG_MSDOS_PARTITION=y
++# CONFIG_SMB_NLS is not set
++CONFIG_NLS=y
++
++#
++# Native Language Support
++#
++CONFIG_NLS_DEFAULT="iso8859-1"
++# CONFIG_NLS_CODEPAGE_437 is not set
++# CONFIG_NLS_CODEPAGE_737 is not set
++# CONFIG_NLS_CODEPAGE_775 is not set
++# CONFIG_NLS_CODEPAGE_850 is not set
++# CONFIG_NLS_CODEPAGE_852 is not set
++# CONFIG_NLS_CODEPAGE_855 is not set
++# CONFIG_NLS_CODEPAGE_857 is not set
++# CONFIG_NLS_CODEPAGE_860 is not set
++# CONFIG_NLS_CODEPAGE_861 is not set
++# CONFIG_NLS_CODEPAGE_862 is not set
++# CONFIG_NLS_CODEPAGE_863 is not set
++# CONFIG_NLS_CODEPAGE_864 is not set
++# CONFIG_NLS_CODEPAGE_865 is not set
++# CONFIG_NLS_CODEPAGE_866 is not set
++# CONFIG_NLS_CODEPAGE_869 is not set
++# CONFIG_NLS_CODEPAGE_936 is not set
++# CONFIG_NLS_CODEPAGE_950 is not set
++# CONFIG_NLS_CODEPAGE_932 is not set
++# CONFIG_NLS_CODEPAGE_949 is not set
++# CONFIG_NLS_CODEPAGE_874 is not set
++# CONFIG_NLS_ISO8859_8 is not set
++# CONFIG_NLS_CODEPAGE_1250 is not set
++# CONFIG_NLS_CODEPAGE_1251 is not set
++# CONFIG_NLS_ISO8859_1 is not set
++# CONFIG_NLS_ISO8859_2 is not set
++# CONFIG_NLS_ISO8859_3 is not set
++# CONFIG_NLS_ISO8859_4 is not set
++# CONFIG_NLS_ISO8859_5 is not set
++# CONFIG_NLS_ISO8859_6 is not set
++# CONFIG_NLS_ISO8859_7 is not set
++# CONFIG_NLS_ISO8859_9 is not set
++# CONFIG_NLS_ISO8859_13 is not set
++# CONFIG_NLS_ISO8859_14 is not set
++# CONFIG_NLS_ISO8859_15 is not set
++# CONFIG_NLS_KOI8_R is not set
++# CONFIG_NLS_KOI8_U is not set
++# CONFIG_NLS_UTF8 is not set
++
++#
++# SCSI support
++#
++CONFIG_SCSI=y
++
++#
++# SCSI support type (disk, tape, CD-ROM)
++#
++# CONFIG_BLK_DEV_SD is not set
++# CONFIG_CHR_DEV_ST is not set
++# CONFIG_BLK_DEV_SR is not set
++# CONFIG_CHR_DEV_SG is not set
++
++#
++# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
++#
++# CONFIG_SCSI_DEBUG_QUEUES is not set
++# CONFIG_SCSI_MULTI_LUN is not set
++# CONFIG_SCSI_CONSTANTS is not set
++# CONFIG_SCSI_LOGGING is not set
++CONFIG_SCSI_DEBUG=y
++
++#
++# Multi-device support (RAID and LVM)
++#
++# CONFIG_MD is not set
++# CONFIG_BLK_DEV_MD is not set
++# CONFIG_MD_LINEAR is not set
++# CONFIG_MD_RAID0 is not set
++# CONFIG_MD_RAID1 is not set
++# CONFIG_MD_RAID5 is not set
++# CONFIG_MD_MULTIPATH is not set
++# CONFIG_BLK_DEV_LVM is not set
++
++#
++# Memory Technology Devices (MTD)
++#
++CONFIG_MTD=y
++# CONFIG_MTD_DEBUG is not set
++# CONFIG_MTD_PARTITIONS is not set
++# CONFIG_MTD_CONCAT is not set
++# CONFIG_MTD_REDBOOT_PARTS is not set
++# CONFIG_MTD_CMDLINE_PARTS is not set
++
++#
++# User Modules And Translation Layers
++#
++CONFIG_MTD_CHAR=y
++CONFIG_MTD_BLOCK=y
++# CONFIG_FTL is not set
++# CONFIG_NFTL is not set
++
++#
++# RAM/ROM/Flash chip drivers
++#
++# CONFIG_MTD_CFI is not set
++# CONFIG_MTD_JEDECPROBE is not set
++# CONFIG_MTD_GEN_PROBE is not set
++# CONFIG_MTD_CFI_INTELEXT is not set
++# CONFIG_MTD_CFI_AMDSTD is not set
++# CONFIG_MTD_CFI_STAA is not set
++# CONFIG_MTD_RAM is not set
++# CONFIG_MTD_ROM is not set
++# CONFIG_MTD_ABSENT is not set
++# CONFIG_MTD_OBSOLETE_CHIPS is not set
++# CONFIG_MTD_AMDSTD is not set
++# CONFIG_MTD_SHARP is not set
++# CONFIG_MTD_JEDEC is not set
++
++#
++# Mapping drivers for chip access
++#
++# CONFIG_MTD_PHYSMAP is not set
++# CONFIG_MTD_PCI is not set
++# CONFIG_MTD_PCMCIA is not set
++
++#
++# Self-contained MTD device drivers
++#
++# CONFIG_MTD_PMC551 is not set
++# CONFIG_MTD_SLRAM is not set
++# CONFIG_MTD_MTDRAM is not set
++CONFIG_MTD_BLKMTD=y
++
++#
++# Disk-On-Chip Device Drivers
++#
++# CONFIG_MTD_DOC1000 is not set
++# CONFIG_MTD_DOC2000 is not set
++# CONFIG_MTD_DOC2001 is not set
++# CONFIG_MTD_DOCPROBE is not set
++
++#
++# NAND Flash Device Drivers
++#
++# CONFIG_MTD_NAND is not set
++
++#
++# Library routines
++#
++# CONFIG_CRC32 is not set
++CONFIG_ZLIB_INFLATE=y
++CONFIG_ZLIB_DEFLATE=y
++
++#
++# Kernel hacking
++#
++# CONFIG_DEBUG_SLAB is not set
++CONFIG_DEBUGSYM=y
++CONFIG_PT_PROXY=y
++# CONFIG_GCOV is not set
+Index: linux-2.4.29/arch/um/drivers/chan_kern.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/chan_kern.c      1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/chan_kern.c   2005-05-03 22:28:14.196452024 +0300
+@@ -0,0 +1,568 @@
++/* 
++ * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <linux/stddef.h>
++#include <linux/kernel.h>
++#include <linux/list.h>
++#include <linux/slab.h>
++#include <linux/tty.h>
++#include <linux/string.h>
++#include <linux/tty_flip.h>
++#include <asm/irq.h>
++#include "chan_kern.h"
++#include "user_util.h"
++#include "kern.h"
++#include "irq_user.h"
++#include "sigio.h"
++#include "line.h"
++#include "os.h"
++
++static void *not_configged_init(char *str, int device, struct chan_opts *opts)
++{
++      printk(KERN_ERR "Using a channel type which is configured out of "
++             "UML\n");
++      return(NULL);
++}
++
++static int not_configged_open(int input, int output, int primary, void *data,
++                            char **dev_out)
++{
++      printk(KERN_ERR "Using a channel type which is configured out of "
++             "UML\n");
++      return(-ENODEV);
++}
++
++static void not_configged_close(int fd, void *data)
++{
++      printk(KERN_ERR "Using a channel type which is configured out of "
++             "UML\n");
++}
++
++static int not_configged_read(int fd, char *c_out, void *data)
++{
++      printk(KERN_ERR "Using a channel type which is configured out of "
++             "UML\n");
++      return(-EIO);
++}
++
++static int not_configged_write(int fd, const char *buf, int len, void *data)
++{
++      printk(KERN_ERR "Using a channel type which is configured out of "
++             "UML\n");
++      return(-EIO);
++}
++
++static int not_configged_console_write(int fd, const char *buf, int len,
++                                     void *data)
++{
++      printk(KERN_ERR "Using a channel type which is configured out of "
++             "UML\n");
++      return(-EIO);
++}
++
++static int not_configged_window_size(int fd, void *data, unsigned short *rows,
++                                   unsigned short *cols)
++{
++      printk(KERN_ERR "Using a channel type which is configured out of "
++             "UML\n");
++      return(-ENODEV);
++}
++
++static void not_configged_free(void *data)
++{
++      printk(KERN_ERR "Using a channel type which is configured out of "
++             "UML\n");
++}
++
++static struct chan_ops not_configged_ops = {
++      .init           = not_configged_init,
++      .open           = not_configged_open,
++      .close          = not_configged_close,
++      .read           = not_configged_read,
++      .write          = not_configged_write,
++      .console_write  = not_configged_console_write,
++      .window_size    = not_configged_window_size,
++      .free           = not_configged_free,
++      .winch          = 0,
++};
++
++void generic_close(int fd, void *unused)
++{
++      os_close_file(fd);
++}
++
++int generic_read(int fd, char *c_out, void *unused)
++{
++      int n;
++
++      n = os_read_file(fd, c_out, sizeof(*c_out));
++
++      if(n == -EAGAIN)
++              return(0);
++      else if(n == 0)
++              return(-EIO);
++      return(n);
++}
++
++/* XXX Trivial wrapper around os_write_file */
++
++int generic_write(int fd, const char *buf, int n, void *unused)
++{
++      return(os_write_file(fd, buf, n));
++}
++
++int generic_window_size(int fd, void *unused, unsigned short *rows_out,
++                      unsigned short *cols_out)
++{
++      int rows, cols;
++      int ret;
++
++      ret = os_window_size(fd, &rows, &cols);
++      if(ret < 0)
++              return(ret);
++
++      ret = ((*rows_out != rows) || (*cols_out != cols));
++
++      *rows_out = rows;
++      *cols_out = cols;
++
++      return(ret);
++}
++
++void generic_free(void *data)
++{
++      kfree(data);
++}
++
++static void tty_receive_char(struct tty_struct *tty, char ch)
++{
++      if(tty == NULL) return;
++
++      if(I_IXON(tty) && !I_IXOFF(tty) && !tty->raw) {
++              if(ch == STOP_CHAR(tty)){
++                      stop_tty(tty);
++                      return;
++              }
++              else if(ch == START_CHAR(tty)){
++                      start_tty(tty);
++                      return;
++              }
++      }
++
++      if((tty->flip.flag_buf_ptr == NULL) || 
++         (tty->flip.char_buf_ptr == NULL))
++              return;
++      tty_insert_flip_char(tty, ch, TTY_NORMAL);
++}
++
++static int open_one_chan(struct chan *chan, int input, int output, int primary)
++{
++      int fd;
++
++      if(chan->opened) return(0);
++      if(chan->ops->open == NULL) fd = 0;
++      else fd = (*chan->ops->open)(input, output, primary, chan->data,
++                                   &chan->dev);
++      if(fd < 0) return(fd);
++      chan->fd = fd;
++
++      chan->opened = 1;
++      return(0);
++}
++
++int open_chan(struct list_head *chans)
++{
++      struct list_head *ele;
++      struct chan *chan;
++      int ret, err = 0;
++
++      list_for_each(ele, chans){
++              chan = list_entry(ele, struct chan, list);
++              ret = open_one_chan(chan, chan->input, chan->output,
++                                  chan->primary);
++              if(chan->primary) err = ret;
++      }
++      return(err);
++}
++
++void chan_enable_winch(struct list_head *chans, void *line)
++{
++      struct list_head *ele;
++      struct chan *chan;
++
++      list_for_each(ele, chans){
++              chan = list_entry(ele, struct chan, list);
++              if(chan->primary && chan->output && chan->ops->winch){
++                      register_winch(chan->fd, line);
++                      return;
++              }
++      }
++}
++
++void enable_chan(struct list_head *chans, void *data)
++{
++      struct list_head *ele;
++      struct chan *chan;
++
++      list_for_each(ele, chans){
++              chan = list_entry(ele, struct chan, list);
++              if(!chan->opened) continue;
++
++              line_setup_irq(chan->fd, chan->input, chan->output, data);
++      }
++}
++
++void close_chan(struct list_head *chans)
++{
++      struct list_head *ele;
++      struct chan *chan;
++
++      /* Close in reverse order as open in case more than one of them
++       * refers to the same device and they save and restore that device's
++       * state.  Then, the first one opened will have the original state,
++       * so it must be the last closed.
++       */
++        for(ele = chans->prev; ele != chans; ele = ele->prev){
++                chan = list_entry(ele, struct chan, list);
++              if(!chan->opened) continue;
++              if(chan->ops->close != NULL)
++                      (*chan->ops->close)(chan->fd, chan->data);
++              chan->opened = 0;
++              chan->fd = -1;
++      }
++}
++
++int write_chan(struct list_head *chans, const char *buf, int len, 
++             int write_irq)
++{
++      struct list_head *ele;
++      struct chan *chan;
++      int n, ret = 0;
++
++      list_for_each(ele, chans){
++              chan = list_entry(ele, struct chan, list);
++              if(!chan->output || (chan->ops->write == NULL)) continue;
++              n = chan->ops->write(chan->fd, buf, len, chan->data);
++              if(chan->primary){
++                      ret = n;
++                      if((ret == -EAGAIN) || ((ret >= 0) && (ret < len))){
++                              reactivate_fd(chan->fd, write_irq);
++                              if(ret == -EAGAIN) ret = 0;
++                      }
++              }
++      }
++      return(ret);
++}
++
++int console_write_chan(struct list_head *chans, const char *buf, int len)
++{
++      struct list_head *ele;
++      struct chan *chan;
++      int n, ret = 0;
++
++      list_for_each(ele, chans){
++              chan = list_entry(ele, struct chan, list);
++              if(!chan->output || (chan->ops->console_write == NULL))
++                      continue;
++              n = chan->ops->console_write(chan->fd, buf, len, chan->data);
++              if(chan->primary) ret = n;
++      }
++      return(ret);
++}
++
++int chan_window_size(struct list_head *chans, unsigned short *rows_out,
++                    unsigned short *cols_out)
++{
++      struct list_head *ele;
++      struct chan *chan;
++
++      list_for_each(ele, chans){
++              chan = list_entry(ele, struct chan, list);
++              if(chan->primary){
++                      if(chan->ops->window_size == NULL) return(0);
++                      return(chan->ops->window_size(chan->fd, chan->data,
++                                                    rows_out, cols_out));
++              }
++      }
++      return(0);
++}
++
++void free_one_chan(struct chan *chan)
++{
++      list_del(&chan->list);
++      if(chan->ops->free != NULL)
++              (*chan->ops->free)(chan->data);
++      free_irq_by_fd(chan->fd);
++      if(chan->primary && chan->output) ignore_sigio_fd(chan->fd);
++      kfree(chan);
++}
++
++void free_chan(struct list_head *chans)
++{
++      struct list_head *ele, *next;
++      struct chan *chan;
++
++      list_for_each_safe(ele, next, chans){
++              chan = list_entry(ele, struct chan, list);
++              free_one_chan(chan);
++      }
++}
++
++static int one_chan_config_string(struct chan *chan, char *str, int size,
++                                char **error_out)
++{
++      int n = 0;
++
++      if(chan == NULL){
++              CONFIG_CHUNK(str, size, n, "none", 1);
++              return(n);
++      }
++
++      CONFIG_CHUNK(str, size, n, chan->ops->type, 0);
++
++      if(chan->dev == NULL){
++              CONFIG_CHUNK(str, size, n, "", 1);
++              return(n);
++      }
++
++      CONFIG_CHUNK(str, size, n, ":", 0);
++      CONFIG_CHUNK(str, size, n, chan->dev, 0);
++
++      return(n);
++}
++
++static int chan_pair_config_string(struct chan *in, struct chan *out, 
++                                 char *str, int size, char **error_out)
++{
++      int n;
++
++      n = one_chan_config_string(in, str, size, error_out);
++      str += n;
++      size -= n;
++
++      if(in == out){
++              CONFIG_CHUNK(str, size, n, "", 1);
++              return(n);
++      }
++
++      CONFIG_CHUNK(str, size, n, ",", 1);
++      n = one_chan_config_string(out, str, size, error_out);
++      str += n;
++      size -= n;
++      CONFIG_CHUNK(str, size, n, "", 1);
++
++      return(n);
++}
++
++int chan_config_string(struct list_head *chans, char *str, int size, 
++                     char **error_out)
++{
++      struct list_head *ele;
++      struct chan *chan, *in = NULL, *out = NULL;
++
++      list_for_each(ele, chans){
++              chan = list_entry(ele, struct chan, list);
++              if(!chan->primary)
++                      continue;
++              if(chan->input)
++                      in = chan;
++              if(chan->output)
++                      out = chan;
++      }
++
++      return(chan_pair_config_string(in, out, str, size, error_out));
++}
++
++struct chan_type {
++      char *key;
++      struct chan_ops *ops;
++};
++
++struct chan_type chan_table[] = {
++#ifdef CONFIG_FD_CHAN
++      { "fd", &fd_ops },
++#else
++      { "fd", &not_configged_ops },
++#endif
++
++#ifdef CONFIG_NULL_CHAN
++      { "null", &null_ops },
++#else
++      { "null", &not_configged_ops },
++#endif
++
++#ifdef CONFIG_PORT_CHAN
++      { "port", &port_ops },
++#else
++      { "port", &not_configged_ops },
++#endif
++
++#ifdef CONFIG_PTY_CHAN
++      { "pty", &pty_ops },
++      { "pts", &pts_ops },
++#else
++      { "pty", &not_configged_ops },
++      { "pts", &not_configged_ops },
++#endif
++
++#ifdef CONFIG_TTY_CHAN
++      { "tty", &tty_ops },
++#else
++      { "tty", &not_configged_ops },
++#endif
++
++#ifdef CONFIG_XTERM_CHAN
++      { "xterm", &xterm_ops },
++#else
++      { "xterm", &not_configged_ops },
++#endif
++};
++
++static struct chan *parse_chan(char *str, int pri, int device, 
++                             struct chan_opts *opts)
++{
++      struct chan_type *entry;
++      struct chan_ops *ops;
++      struct chan *chan;
++      void *data;
++      int i;
++
++      ops = NULL;
++      data = NULL;
++      for(i = 0; i < sizeof(chan_table)/sizeof(chan_table[0]); i++){
++              entry = &chan_table[i];
++              if(!strncmp(str, entry->key, strlen(entry->key))){
++                      ops = entry->ops;
++                      str += strlen(entry->key);
++                      break;
++              }
++      }
++      if(ops == NULL){
++              printk(KERN_ERR "parse_chan couldn't parse \"%s\"\n", 
++                     str);
++              return(NULL);
++      }
++      if(ops->init == NULL) return(NULL); 
++      data = (*ops->init)(str, device, opts);
++      if(data == NULL) return(NULL);
++
++      chan = kmalloc(sizeof(*chan), GFP_KERNEL);
++      if(chan == NULL) return(NULL);
++      *chan = ((struct chan) { .list          = LIST_HEAD_INIT(chan->list),
++                               .primary       = 1,
++                               .input         = 0,
++                               .output        = 0,
++                               .opened        = 0,
++                               .fd            = -1,
++                               .pri           = pri,
++                               .ops           = ops,
++                               .data          = data });
++      return(chan);
++}
++
++int parse_chan_pair(char *str, struct list_head *chans, int pri, int device,
++                  struct chan_opts *opts)
++{
++      struct chan *new, *chan;
++      char *in, *out;
++
++      if(!list_empty(chans)){
++              chan = list_entry(chans->next, struct chan, list);
++              if(chan->pri >= pri) return(0);
++              free_chan(chans);
++              INIT_LIST_HEAD(chans);
++      }
++
++      out = strchr(str, ',');
++      if(out != NULL){
++              in = str;
++              *out = '\0';
++              out++;
++              new = parse_chan(in, pri, device, opts);
++              if(new == NULL) return(-1);
++              new->input = 1;
++              list_add(&new->list, chans);
++
++              new = parse_chan(out, pri, device, opts);
++              if(new == NULL) return(-1);
++              list_add(&new->list, chans);
++              new->output = 1;
++      }
++      else {
++              new = parse_chan(str, pri, device, opts);
++              if(new == NULL) return(-1);
++              list_add(&new->list, chans);
++              new->input = 1;
++              new->output = 1;
++      }
++      return(0);
++}
++
++int chan_out_fd(struct list_head *chans)
++{
++      struct list_head *ele;
++      struct chan *chan;
++
++      list_for_each(ele, chans){
++              chan = list_entry(ele, struct chan, list);
++              if(chan->primary && chan->output)
++                      return(chan->fd);
++      }
++      return(-1);
++}
++
++void chan_interrupt(struct list_head *chans, struct tq_struct *task,
++                  struct tty_struct *tty, int irq, void *dev)
++{
++      struct list_head *ele, *next;
++      struct chan *chan;
++      int err;
++      char c;
++
++      list_for_each_safe(ele, next, chans){
++              chan = list_entry(ele, struct chan, list);
++              if(!chan->input || (chan->ops->read == NULL)) continue;
++              do {
++                      if((tty != NULL) && 
++                         (tty->flip.count >= TTY_FLIPBUF_SIZE)){
++                              queue_task(task, &tq_timer);
++                              goto out;
++                      }
++                      err = chan->ops->read(chan->fd, &c, chan->data);
++                      if(err > 0) 
++                              tty_receive_char(tty, c);
++              } while(err > 0);
++
++              if(err == 0) reactivate_fd(chan->fd, irq);
++              if(err == -EIO){
++                      if(chan->primary){
++                              if(tty != NULL) 
++                                      tty_hangup(tty);
++                              line_disable(dev, irq);
++                              close_chan(chans);
++                              free_chan(chans);
++                              return;
++                      }
++                      else {
++                              if(chan->ops->close != NULL)
++                                      chan->ops->close(chan->fd, chan->data);
++                              free_one_chan(chan);
++                      }
++              }
++      }
++ out:
++      if(tty) tty_flip_buffer_push(tty);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/chan_user.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/chan_user.c      1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/chan_user.c   2005-05-03 22:28:14.197451872 +0300
+@@ -0,0 +1,172 @@
++/* 
++ * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com)
++ * Licensed under the GPL
++ */
++
++#include <unistd.h>
++#include <stdlib.h>
++#include <errno.h>
++#include <termios.h>
++#include <string.h>
++#include <signal.h>
++#include <sys/stat.h>
++#include <sys/ioctl.h>
++#include <sys/socket.h>
++#include "kern_util.h"
++#include "user_util.h"
++#include "chan_user.h"
++#include "user.h"
++#include "helper.h"
++#include "os.h"
++#include "choose-mode.h"
++#include "mode.h"
++
++static void winch_handler(int sig)
++{
++}
++
++struct winch_data {
++      int pty_fd;
++      int pipe_fd;
++      int close_me;
++};
++
++/* XXX This breaks horribly (by hanging UML) when moved to chan_kern.c - 
++ * needs investigation
++ */
++int generic_console_write(int fd, const char *buf, int n, void *unused)
++{
++      struct termios save, new;
++      int err;
++
++      if(isatty(fd)){
++              tcgetattr(fd, &save);
++              new = save;
++              new.c_oflag |= OPOST;
++              tcsetattr(fd, TCSAFLUSH, &new);
++      }
++      err = generic_write(fd, buf, n, NULL);
++      if(isatty(fd)) tcsetattr(fd, TCSAFLUSH, &save);
++      return(err);
++}
++
++static int winch_thread(void *arg)
++{
++      struct winch_data *data = arg;
++      sigset_t sigs;
++      int pty_fd, pipe_fd;
++      int count, err;
++      char c = 1;
++
++      os_close_file(data->close_me);
++      pty_fd = data->pty_fd;
++      pipe_fd = data->pipe_fd;
++      count = os_write_file(pipe_fd, &c, sizeof(c));
++      if(count != sizeof(c))
++              printk("winch_thread : failed to write synchronization "
++                     "byte, err = %d\n", -count);
++
++      signal(SIGWINCH, winch_handler);
++      sigfillset(&sigs);
++      sigdelset(&sigs, SIGWINCH);
++      if(sigprocmask(SIG_SETMASK, &sigs, NULL) < 0){
++              printk("winch_thread : sigprocmask failed, errno = %d\n", 
++                     errno);
++              exit(1);
++      }
++
++      if(setsid() < 0){
++              printk("winch_thread : setsid failed, errno = %d\n", errno);
++              exit(1);
++      }
++
++      err = os_new_tty_pgrp(pty_fd, os_getpid());
++      if(err < 0){
++              printk("winch_thread : new_tty_pgrp failed, err = %d\n", -err);
++              exit(1);
++      }
++
++      count = os_read_file(pipe_fd, &c, sizeof(c));
++      if(count != sizeof(c))
++              printk("winch_thread : failed to read synchronization byte, "
++                     "err = %d\n", -count);
++
++      while(1){
++              pause();
++
++              count = os_write_file(pipe_fd, &c, sizeof(c));
++              if(count != sizeof(c))
++                      printk("winch_thread : write failed, err = %d\n",
++                             -count);
++      }
++}
++
++static int winch_tramp(int fd, void *device_data, int *fd_out)
++{
++      struct winch_data data;
++      unsigned long stack;
++      int fds[2], pid, n, err;
++      char c;
++
++      err = os_pipe(fds, 1, 1);
++      if(err < 0){
++              printk("winch_tramp : os_pipe failed, err = %d\n", -err);
++              return(err);
++      }
++
++      data = ((struct winch_data) { .pty_fd           = fd,
++                                    .pipe_fd          = fds[1],
++                                    .close_me         = fds[0] } );
++      pid = run_helper_thread(winch_thread, &data, 0, &stack, 0);
++      if(pid < 0){
++              printk("fork of winch_thread failed - errno = %d\n", errno);
++              return(pid);
++      }
++
++      os_close_file(fds[1]);
++      *fd_out = fds[0];
++      n = os_read_file(fds[0], &c, sizeof(c));
++      if(n != sizeof(c)){
++              printk("winch_tramp : failed to read synchronization byte\n");
++              printk("read failed, err = %d\n", -n);
++              printk("fd %d will not support SIGWINCH\n", fd);
++              *fd_out = -1;
++      }
++      return(pid);
++}
++
++void register_winch(int fd, void *device_data)
++{
++      int pid, thread, thread_fd;
++      int count;
++      char c = 1;
++
++      if(!isatty(fd)) 
++              return;
++
++      pid = tcgetpgrp(fd);
++      if(!CHOOSE_MODE_PROC(is_tracer_winch, is_skas_winch, pid, fd, 
++                           device_data) && (pid == -1)){
++              thread = winch_tramp(fd, device_data, &thread_fd);
++              if(fd != -1){
++                      register_winch_irq(thread_fd, fd, thread, device_data);
++
++                      count = os_write_file(thread_fd, &c, sizeof(c));
++                      if(count != sizeof(c))
++                              printk("register_winch : failed to write "
++                                     "synchronization byte, err = %d\n",
++                                      -count);
++              }
++      }
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/cow.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/cow.h    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/cow.h 2005-05-03 22:43:32.718815400 +0300
+@@ -0,0 +1,41 @@
++#ifndef __COW_H__
++#define __COW_H__
++
++#include <asm/types.h>
++
++#if __BYTE_ORDER == __BIG_ENDIAN
++# define ntohll(x) (x)
++# define htonll(x) (x)
++#elif __BYTE_ORDER == __LITTLE_ENDIAN
++# define ntohll(x)  bswap_64(x)
++# define htonll(x)  bswap_64(x)
++#else
++#error "__BYTE_ORDER not defined"
++#endif
++
++extern int init_cow_file(int fd, char *cow_file, char *backing_file, 
++                       int sectorsize, int alignment, int *bitmap_offset_out, 
++                       unsigned long *bitmap_len_out, int *data_offset_out);
++
++extern int file_reader(__u64 offset, char *buf, int len, void *arg);
++extern int read_cow_header(int (*reader)(__u64, char *, int, void *), 
++                         void *arg, __u32 *version_out, 
++                         char **backing_file_out, time_t *mtime_out, 
++                         __u64 *size_out, int *sectorsize_out, 
++                         __u32 *align_out, int *bitmap_offset_out);
++
++extern int write_cow_header(char *cow_file, int fd, char *backing_file, 
++                          int sectorsize, int alignment, long long *size);
++
++extern void cow_sizes(int version, __u64 size, int sectorsize, int align,
++                    int bitmap_offset, unsigned long *bitmap_len_out, 
++                    int *data_offset_out);
++
++#endif
++
++/*
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/cow_kern.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/cow_kern.c       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/cow_kern.c    2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,630 @@
++#define COW_MAJOR 60
++#define MAJOR_NR COW_MAJOR
++
++#include <linux/stddef.h>
++#include <linux/kernel.h>
++#include <linux/ctype.h>
++#include <linux/stat.h>
++#include <linux/vmalloc.h>
++#include <linux/blkdev.h>
++#include <linux/blk.h>
++#include <linux/fs.h>
++#include <linux/genhd.h>
++#include <linux/devfs_fs.h>
++#include <asm/uaccess.h>
++#include "2_5compat.h"
++#include "cow.h"
++#include "ubd_user.h"
++
++#define COW_SHIFT 4
++
++struct cow {
++      int count;
++      char *cow_path;
++      dev_t cow_dev;
++      struct block_device *cow_bdev;
++      char *backing_path;
++      dev_t backing_dev;
++      struct block_device *backing_bdev;
++      int sectorsize;
++      unsigned long *bitmap;
++      unsigned long bitmap_len;
++      int bitmap_offset;
++      int data_offset;
++      devfs_handle_t devfs;
++      struct semaphore sem;
++      struct semaphore io_sem;
++      atomic_t working;
++      spinlock_t io_lock;
++      struct buffer_head *bh;
++      struct buffer_head *bhtail;
++      void *end_io;
++};
++
++#define DEFAULT_COW { \
++      .count                  = 0, \
++      .cow_path               = NULL, \
++      .cow_dev                = 0, \
++      .backing_path           = NULL, \
++      .backing_dev            = 0, \
++        .bitmap                       = NULL, \
++      .bitmap_len             = 0, \
++      .bitmap_offset          = 0, \
++        .data_offset          = 0, \
++      .devfs                  = NULL, \
++      .working                = ATOMIC_INIT(0), \
++      .io_lock                = SPIN_LOCK_UNLOCKED, \
++}
++
++#define MAX_DEV (8)
++#define MAX_MINOR (MAX_DEV << COW_SHIFT)
++
++struct cow cow_dev[MAX_DEV] = { [ 0 ... MAX_DEV - 1 ] = DEFAULT_COW };
++
++/* Not modified by this driver */
++static int blk_sizes[MAX_MINOR] = { [ 0 ... MAX_MINOR - 1 ] = BLOCK_SIZE };
++static int hardsect_sizes[MAX_MINOR] = { [ 0 ... MAX_MINOR - 1 ] = 512 };
++
++/* Protected by cow_lock */
++static int sizes[MAX_MINOR] = { [ 0 ... MAX_MINOR - 1 ] = 0 };
++
++static struct hd_struct       cow_part[MAX_MINOR] =
++      { [ 0 ... MAX_MINOR - 1 ] = { 0, 0, 0 } };
++
++/* Protected by io_request_lock */
++static request_queue_t *cow_queue;
++
++static int cow_open(struct inode *inode, struct file *filp);
++static int cow_release(struct inode * inode, struct file * file);
++static int cow_ioctl(struct inode * inode, struct file * file,
++                   unsigned int cmd, unsigned long arg);
++static int cow_revalidate(kdev_t rdev);
++
++static struct block_device_operations cow_blops = {
++      .open           = cow_open,
++      .release        = cow_release,
++      .ioctl          = cow_ioctl,
++      .revalidate     = cow_revalidate,
++};
++
++/* Initialized in an initcall, and unchanged thereafter */
++devfs_handle_t cow_dir_handle;
++
++#define INIT_GENDISK(maj, name, parts, shift, bsizes, max, blops) \
++{ \
++      .major          = maj, \
++      .major_name     = name, \
++      .minor_shift    = shift, \
++      .max_p          = 1 << shift, \
++      .part           = parts, \
++      .sizes          = bsizes, \
++      .nr_real        = max, \
++      .real_devices   = NULL, \
++      .next           = NULL, \
++      .fops           = blops, \
++      .de_arr         = NULL, \
++      .flags          = 0 \
++}
++
++static spinlock_t cow_lock = SPIN_LOCK_UNLOCKED;
++
++static struct gendisk cow_gendisk = INIT_GENDISK(MAJOR_NR, "cow", cow_part,
++                                               COW_SHIFT, sizes, MAX_DEV, 
++                                               &cow_blops);
++
++static int cow_add(int n)
++{
++      struct cow *dev = &cow_dev[n];
++      char name[sizeof("nnnnnn\0")];
++      int err = -ENODEV;
++
++      if(dev->cow_path == NULL)
++              goto out;
++
++      sprintf(name, "%d", n);
++      dev->devfs = devfs_register(cow_dir_handle, name, DEVFS_FL_REMOVABLE,
++                                  MAJOR_NR, n << COW_SHIFT, S_IFBLK | 
++                                  S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP,
++                                  &cow_blops, NULL);
++
++      init_MUTEX_LOCKED(&dev->sem);
++      init_MUTEX(&dev->io_sem);
++
++      return(0);
++
++ out:
++      return(err);
++}
++
++/*
++ * Add buffer_head to back of pending list
++ */
++static void cow_add_bh(struct cow *cow, struct buffer_head *bh)
++{
++      unsigned long flags;
++
++      spin_lock_irqsave(&cow->io_lock, flags);
++      if(cow->bhtail != NULL){
++              cow->bhtail->b_reqnext = bh;
++              cow->bhtail = bh;
++      }
++      else {
++              cow->bh = bh;
++              cow->bhtail = bh;
++      }
++      spin_unlock_irqrestore(&cow->io_lock, flags);
++}
++
++/*
++ * Grab first pending buffer
++ */
++static struct buffer_head *cow_get_bh(struct cow *cow)
++{
++      struct buffer_head *bh;
++
++      spin_lock_irq(&cow->io_lock);
++      bh = cow->bh;
++      if(bh != NULL){
++              if(bh == cow->bhtail)
++                      cow->bhtail = NULL;
++              cow->bh = bh->b_reqnext;
++              bh->b_reqnext = NULL;
++      }
++      spin_unlock_irq(&cow->io_lock);
++
++      return(bh);
++}
++
++static void cow_handle_bh(struct cow *cow, struct buffer_head *bh, 
++                        struct buffer_head **cow_bh, int ncow_bh)
++{
++      int i;
++
++      if(ncow_bh > 0)
++              ll_rw_block(WRITE, ncow_bh, cow_bh);
++
++      for(i = 0; i < ncow_bh ; i++){
++              wait_on_buffer(cow_bh[i]);
++              brelse(cow_bh[i]);
++      }
++
++      ll_rw_block(WRITE, 1, &bh);
++      brelse(bh);
++}
++
++static struct buffer_head *cow_new_bh(struct cow *dev, int sector)
++{
++      struct buffer_head *bh;
++
++      sector = (dev->bitmap_offset + sector / 8) / dev->sectorsize;
++      bh = getblk(dev->cow_dev, sector, dev->sectorsize);
++      memcpy(bh->b_data, dev->bitmap + sector / (8 * sizeof(dev->bitmap[0])),
++             dev->sectorsize);
++      return(bh);
++}
++
++/* Copied from loop.c, needed to avoid deadlocking in make_request. */
++
++static int cow_thread(void *data)
++{
++      struct cow *dev = data;
++      struct buffer_head *bh;
++
++      daemonize();
++      exit_files(current);
++
++      sprintf(current->comm, "cow%d", dev - cow_dev);
++
++      spin_lock_irq(&current->sigmask_lock);
++      sigfillset(&current->blocked);
++      flush_signals(current);
++      spin_unlock_irq(&current->sigmask_lock);
++
++      atomic_inc(&dev->working);
++
++      current->policy = SCHED_OTHER;
++      current->nice = -20;
++
++      current->flags |= PF_NOIO;
++
++      /*
++       * up sem, we are running
++       */
++      up(&dev->sem);
++
++      for(;;){
++              int start, len, nbh, i, update_bitmap = 0;
++              struct buffer_head *cow_bh[2];
++
++              down_interruptible(&dev->io_sem);
++              /*
++               * could be upped because of tear-down, not because of
++               * pending work
++               */
++              if(!atomic_read(&dev->working))
++                      break;
++
++              bh = cow_get_bh(dev);
++              if(bh == NULL){
++                      printk(KERN_ERR "cow: missing bh\n");
++                      continue;
++              }
++
++              start = bh->b_blocknr * bh->b_size / dev->sectorsize;
++              len = bh->b_size / dev->sectorsize;
++              for(i = 0; i < len ; i++){
++                      if(ubd_test_bit(start + i, 
++                                      (unsigned char *) dev->bitmap))
++                              continue;
++
++                      update_bitmap = 1;
++                      ubd_set_bit(start + i, (unsigned char *) dev->bitmap);
++              }
++
++              cow_bh[0] = NULL;
++              cow_bh[1] = NULL;
++              nbh = 0;
++              if(update_bitmap){
++                      cow_bh[0] = cow_new_bh(dev, start);
++                      nbh++;
++                      if(start / dev->sectorsize != 
++                         (start + len) / dev->sectorsize){
++                              cow_bh[1] = cow_new_bh(dev, start + len);
++                              nbh++;
++                      }
++              }
++              
++              bh->b_dev = dev->cow_dev;
++              bh->b_blocknr += dev->data_offset / dev->sectorsize;
++
++              cow_handle_bh(dev, bh, cow_bh, nbh);
++
++              /*
++               * upped both for pending work and tear-down, lo_pending
++               * will hit zero then
++               */
++              if(atomic_dec_and_test(&dev->working))
++                      break;
++      }
++
++      up(&dev->sem);
++      return(0);
++}
++
++static int cow_make_request(request_queue_t *q, int rw, struct buffer_head *bh)
++{
++      struct cow *dev;
++      int n, minor;
++
++      minor = MINOR(bh->b_rdev);
++      n = minor >> COW_SHIFT;
++      dev = &cow_dev[n];
++
++      dev->end_io = NULL;
++      if(ubd_test_bit(bh->b_rsector, (unsigned char *) dev->bitmap)){
++              bh->b_rdev = dev->cow_dev;
++              bh->b_rsector += dev->data_offset / dev->sectorsize;
++      }
++      else if(rw == WRITE){
++              bh->b_dev = dev->cow_dev;
++              bh->b_blocknr += dev->data_offset / dev->sectorsize;
++
++              cow_add_bh(dev, bh);
++              up(&dev->io_sem);
++              return(0);
++      }
++      else {
++              bh->b_rdev = dev->backing_dev;
++      }
++
++      return(1);
++}
++
++int cow_init(void)
++{
++      int i;
++
++      cow_dir_handle = devfs_mk_dir (NULL, "cow", NULL);
++      if (devfs_register_blkdev(MAJOR_NR, "cow", &cow_blops)) {
++              printk(KERN_ERR "cow: unable to get major %d\n", MAJOR_NR);
++              return -1;
++      }
++      read_ahead[MAJOR_NR] = 8;               /* 8 sector (4kB) read-ahead */
++      blksize_size[MAJOR_NR] = blk_sizes;
++      blk_size[MAJOR_NR] = sizes;
++      INIT_HARDSECT(hardsect_size, MAJOR_NR, hardsect_sizes);
++
++      cow_queue = BLK_DEFAULT_QUEUE(MAJOR_NR);
++      blk_init_queue(cow_queue, NULL);
++      INIT_ELV(cow_queue, &cow_queue->elevator);
++      blk_queue_make_request(cow_queue, cow_make_request);
++
++        add_gendisk(&cow_gendisk);
++
++      for(i=0;i<MAX_DEV;i++) 
++              cow_add(i);
++
++      return(0);
++}
++
++__initcall(cow_init);
++
++static int reader(__u64 start, char *buf, int count, void *arg)
++{
++      dev_t dev = *((dev_t *) arg);
++      struct buffer_head *bh;
++      __u64 block;
++      int cur, offset, left, n, blocksize = get_hardsect_size(dev);
++
++      if(blocksize == 0)
++              panic("Zero blocksize");
++
++      block = start / blocksize;
++      offset = start % blocksize;
++      left = count;
++      cur = 0;
++      while(left > 0){
++              n = (left > blocksize) ? blocksize : left;
++
++              bh = bread(dev, block, (n < 512) ? 512 : n);
++              if(bh == NULL)
++                      return(-EIO);
++
++              n -= offset;
++              memcpy(&buf[cur], bh->b_data + offset, n);
++              block++;
++              left -= n;
++              cur += n;
++              offset = 0;
++              brelse(bh);
++      }
++
++      return(count);
++}
++
++static int cow_open(struct inode *inode, struct file *filp)
++{
++      int (*dev_ioctl)(struct inode *, struct file *, unsigned int, 
++                       unsigned long);
++      mm_segment_t fs;
++      struct cow *dev;
++      __u64 size;
++      __u32 version, align;
++      time_t mtime;
++      char *backing_file;
++      int n, offset, err = 0;
++
++      n = DEVICE_NR(inode->i_rdev);
++      if(n >= MAX_DEV)
++              return(-ENODEV);
++      dev = &cow_dev[n];
++      offset = n << COW_SHIFT;
++
++      spin_lock(&cow_lock);
++
++      if(dev->count == 0){
++              dev->cow_dev = name_to_kdev_t(dev->cow_path);
++              if(dev->cow_dev == 0){
++                      printk(KERN_ERR "cow_open - name_to_kdev_t(\"%s\") "
++                             "failed\n", dev->cow_path);
++                      err = -ENODEV;
++              }
++
++              dev->backing_dev = name_to_kdev_t(dev->backing_path);
++              if(dev->backing_dev == 0){
++                      printk(KERN_ERR "cow_open - name_to_kdev_t(\"%s\") "
++                             "failed\n", dev->backing_path);
++                      err = -ENODEV;
++              }
++
++              if(err) 
++                      goto out;
++
++              dev->cow_bdev = bdget(dev->cow_dev);
++              if(dev->cow_bdev == NULL){
++                      printk(KERN_ERR "cow_open - bdget(\"%s\") failed\n", 
++                             dev->cow_path);
++                      err = -ENOMEM;
++              }
++              dev->backing_bdev = bdget(dev->backing_dev);
++              if(dev->backing_bdev == NULL){
++                      printk(KERN_ERR "cow_open - bdget(\"%s\") failed\n", 
++                             dev->backing_path);
++                      err = -ENOMEM;
++              }
++
++              if(err) 
++                      goto out;
++
++              err = blkdev_get(dev->cow_bdev, FMODE_READ|FMODE_WRITE, 0, 
++                               BDEV_RAW);
++              if(err){
++                      printk("cow_open - blkdev_get of COW device failed, "
++                             "error = %d\n", err);
++                      goto out;
++              }
++              
++              err = blkdev_get(dev->backing_bdev, FMODE_READ, 0, BDEV_RAW);
++              if(err){
++                      printk("cow_open - blkdev_get of backing device "
++                             "failed, error = %d\n", err);
++                      goto out;
++              }
++              
++              err = read_cow_header(reader, &dev->cow_dev, &version, 
++                                    &backing_file, &mtime, &size,
++                                    &dev->sectorsize, &align, 
++                                    &dev->bitmap_offset);
++              if(err){
++                      printk(KERN_ERR "cow_open - read_cow_header failed, "
++                             "err = %d\n", err);
++                      goto out;
++              }
++
++              cow_sizes(version, size, dev->sectorsize, align, 
++                        dev->bitmap_offset, &dev->bitmap_len, 
++                        &dev->data_offset);
++              dev->bitmap = (void *) vmalloc(dev->bitmap_len);
++              if(dev->bitmap == NULL){
++                      err = -ENOMEM;
++                      printk(KERN_ERR "Failed to vmalloc COW bitmap\n");
++                      goto out;
++              }
++              flush_tlb_kernel_vm();
++              
++              err = reader(dev->bitmap_offset, (char *) dev->bitmap, 
++                           dev->bitmap_len, &dev->cow_dev);
++              if(err < 0){
++                      printk(KERN_ERR "Failed to read COW bitmap\n");
++                      vfree(dev->bitmap);
++                      goto out;
++              }
++
++              dev_ioctl = dev->backing_bdev->bd_op->ioctl;
++              fs = get_fs();
++              set_fs(KERNEL_DS);
++              err = (*dev_ioctl)(inode, filp, BLKGETSIZE, 
++                                 (unsigned long) &sizes[offset]);
++              set_fs(fs);
++              if(err){
++                      printk(KERN_ERR "cow_open - BLKGETSIZE failed, "
++                             "error = %d\n", err);
++                      goto out;
++              }
++
++              kernel_thread(cow_thread, dev, 
++                            CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
++              down(&dev->sem);
++      }
++      dev->count++;
++ out:
++      spin_unlock(&cow_lock);
++      return(err);
++}
++
++static int cow_release(struct inode * inode, struct file * file)
++{
++      struct cow *dev;
++      int n, err;
++
++      n = DEVICE_NR(inode->i_rdev);
++      if(n >= MAX_DEV)
++              return(-ENODEV);
++      dev = &cow_dev[n];
++
++      spin_lock(&cow_lock);
++
++      if(--dev->count > 0)
++              goto out;
++
++      err = blkdev_put(dev->cow_bdev, BDEV_RAW);
++      if(err)
++              printk("cow_release - blkdev_put of cow device failed, "
++                     "error = %d\n", err);
++      bdput(dev->cow_bdev);
++      dev->cow_bdev = 0;
++
++      err = blkdev_put(dev->backing_bdev, BDEV_RAW);
++      if(err)
++              printk("cow_release - blkdev_put of backing device failed, "
++                     "error = %d\n", err);
++      bdput(dev->backing_bdev);
++      dev->backing_bdev = 0;
++
++ out:
++      spin_unlock(&cow_lock);
++      return(0);
++}
++
++static int cow_ioctl(struct inode * inode, struct file * file,
++                   unsigned int cmd, unsigned long arg)
++{
++      struct cow *dev;
++      int (*dev_ioctl)(struct inode *, struct file *, unsigned int, 
++                       unsigned long);
++      int n;
++
++      n = DEVICE_NR(inode->i_rdev);
++      if(n >= MAX_DEV)
++              return(-ENODEV);
++      dev = &cow_dev[n];
++
++      dev_ioctl = dev->backing_bdev->bd_op->ioctl;
++      return((*dev_ioctl)(inode, file, cmd, arg));
++}
++
++static int cow_revalidate(kdev_t rdev)
++{
++      printk(KERN_ERR "Need to implement cow_revalidate\n");
++      return(0);
++}
++
++static int parse_unit(char **ptr)
++{
++      char *str = *ptr, *end;
++      int n = -1;
++
++      if(isdigit(*str)) {
++              n = simple_strtoul(str, &end, 0);
++              if(end == str)
++                      return(-1);
++              *ptr = end;
++      }
++      else if (('a' <= *str) && (*str <= 'h')) {
++              n = *str - 'a';
++              str++;
++              *ptr = str;
++      }
++      return(n);
++}
++
++static int cow_setup(char *str)
++{
++      struct cow *dev;
++      char *cow_name, *backing_name;
++      int unit;
++
++      unit = parse_unit(&str);
++      if(unit < 0){
++              printk(KERN_ERR "cow_setup - Couldn't parse unit number\n");
++              return(1);
++      }
++
++      if(*str != '='){
++              printk(KERN_ERR "cow_setup - Missing '=' after unit "
++                     "number\n");
++              return(1);
++      }
++      str++;
++
++      cow_name = str;
++      backing_name = strchr(str, ',');
++      if(backing_name == NULL){
++              printk(KERN_ERR "cow_setup - missing backing device name\n");
++              return(0);
++      }
++      *backing_name = '\0';
++      backing_name++;
++
++      spin_lock(&cow_lock);
++
++      dev = &cow_dev[unit];
++      dev->cow_path = cow_name;
++      dev->backing_path = backing_name;
++      
++      spin_unlock(&cow_lock);
++      return(0);
++}
++
++__setup("cow", cow_setup);
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/cow_sys.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/cow_sys.h        1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/cow_sys.h     2005-05-03 22:43:34.768503800 +0300
+@@ -0,0 +1,48 @@
++#ifndef __COW_SYS_H__
++#define __COW_SYS_H__
++
++#include "kern_util.h"
++#include "user_util.h"
++#include "os.h"
++#include "user.h"
++
++static inline void *cow_malloc(int size)
++{
++      return(um_kmalloc(size));
++}
++
++static inline void cow_free(void *ptr)
++{
++      kfree(ptr);
++}
++
++#define cow_printf printk
++
++static inline char *cow_strdup(char *str)
++{
++      return(uml_strdup(str));
++}
++
++static inline int cow_seek_file(int fd, __u64 offset)
++{
++      return(os_seek_file(fd, offset));
++}
++
++static inline int cow_file_size(char *file, __u64 *size_out)
++{
++      return(os_file_size(file, size_out));
++}
++
++static inline int cow_write_file(int fd, char *buf, int size)
++{
++      return(os_write_file(fd, buf, size));
++}
++
++#endif
++
++/*
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/cow_user.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/cow_user.c       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/cow_user.c    2005-05-03 22:28:14.203450960 +0300
+@@ -0,0 +1,375 @@
++#include <stddef.h>
++#include <string.h>
++#include <errno.h>
++#include <unistd.h>
++#include <byteswap.h>
++#include <sys/time.h>
++#include <sys/param.h>
++#include <sys/user.h>
++#include <netinet/in.h>
++
++#include "os.h"
++
++#include "cow.h"
++#include "cow_sys.h"
++
++#define PATH_LEN_V1 256
++
++struct cow_header_v1 {
++      int magic;
++      int version;
++      char backing_file[PATH_LEN_V1];
++      time_t mtime;
++      __u64 size;
++      int sectorsize;
++};
++
++#define PATH_LEN_V2 MAXPATHLEN
++
++struct cow_header_v2 {
++      __u32 magic;
++      __u32 version;
++      char backing_file[PATH_LEN_V2];
++      time_t mtime;
++      __u64 size;
++      int sectorsize;
++};
++
++/* Define PATH_LEN_V3 as the usual value of MAXPATHLEN, just hard-code it in 
++ * case other systems have different values for MAXPATHLEN
++ */
++#define PATH_LEN_V3 4096
++
++/* Changes from V2 - 
++ *    PATH_LEN_V3 as described above
++ *    Explicitly specify field bit lengths for systems with different
++ *            lengths for the usual C types.  Not sure whether char or
++ *            time_t should be changed, this can be changed later without
++ *            breaking compatibility
++ *    Add alignment field so that different alignments can be used for the
++ *            bitmap and data
++ *    Add cow_format field to allow for the possibility of different ways
++ *            of specifying the COW blocks.  For now, the only value is 0,
++ *            for the traditional COW bitmap.
++ *    Move the backing_file field to the end of the header.  This allows
++ *            for the possibility of expanding it into the padding required
++ *            by the bitmap alignment.
++ *    The bitmap and data portions of the file will be aligned as specified
++ *            by the alignment field.  This is to allow COW files to be
++ *            put on devices with restrictions on access alignments, such as
++ *            /dev/raw, with a 512 byte alignment restriction.  This also
++ *            allows the data to be more aligned more strictly than on
++ *            sector boundaries.  This is needed for ubd-mmap, which needs
++ *            the data to be page aligned.
++ *    Fixed (finally!) the rounding bug
++ */
++
++struct cow_header_v3 {
++      __u32 magic;
++      __u32 version;
++      time_t mtime;
++      __u64 size;
++      __u32 sectorsize;
++      __u32 alignment;
++      __u32 cow_format;
++      char backing_file[PATH_LEN_V3];
++};
++
++/* COW format definitions - for now, we have only the usual COW bitmap */
++#define COW_BITMAP 0
++
++union cow_header {
++      struct cow_header_v1 v1;
++      struct cow_header_v2 v2;
++      struct cow_header_v3 v3;
++};
++
++#define COW_MAGIC 0x4f4f4f4d  /* MOOO */
++#define COW_VERSION 3
++
++#define DIV_ROUND(x, len) (((x) + (len) - 1) / (len))
++#define ROUND_UP(x, align) DIV_ROUND(x, align) * (align)
++
++void cow_sizes(int version, __u64 size, int sectorsize, int align, 
++             int bitmap_offset, unsigned long *bitmap_len_out, 
++             int *data_offset_out)
++{
++      if(version < 3){
++              *bitmap_len_out = (size + sectorsize - 1) / (8 * sectorsize);
++
++              *data_offset_out = bitmap_offset + *bitmap_len_out;
++              *data_offset_out = (*data_offset_out + sectorsize - 1) / 
++                      sectorsize;
++              *data_offset_out *= sectorsize;
++      }
++      else {
++              *bitmap_len_out = DIV_ROUND(size, sectorsize);
++              *bitmap_len_out = DIV_ROUND(*bitmap_len_out, 8);
++
++              *data_offset_out = bitmap_offset + *bitmap_len_out;
++              *data_offset_out = ROUND_UP(*data_offset_out, align);
++      }
++}
++
++static int absolutize(char *to, int size, char *from)
++{
++      char save_cwd[256], *slash;
++      int remaining;
++
++      if(getcwd(save_cwd, sizeof(save_cwd)) == NULL) {
++              cow_printf("absolutize : unable to get cwd - errno = %d\n", 
++                         errno);
++              return(-1);
++      }
++      slash = strrchr(from, '/');
++      if(slash != NULL){
++              *slash = '\0';
++              if(chdir(from)){
++                      *slash = '/';
++                      cow_printf("absolutize : Can't cd to '%s' - " 
++                                 "errno = %d\n", from, errno);
++                      return(-1);
++              }
++              *slash = '/';
++              if(getcwd(to, size) == NULL){
++                      cow_printf("absolutize : unable to get cwd of '%s' - "
++                             "errno = %d\n", from, errno);
++                      return(-1);
++              }
++              remaining = size - strlen(to);
++              if(strlen(slash) + 1 > remaining){
++                      cow_printf("absolutize : unable to fit '%s' into %d "
++                             "chars\n", from, size);
++                      return(-1);
++              }
++              strcat(to, slash);
++      }
++      else {
++              if(strlen(save_cwd) + 1 + strlen(from) + 1 > size){
++                      cow_printf("absolutize : unable to fit '%s' into %d "
++                             "chars\n", from, size);
++                      return(-1);
++              }
++              strcpy(to, save_cwd);
++              strcat(to, "/");
++              strcat(to, from);
++      }
++      chdir(save_cwd);
++      return(0);
++}
++
++int write_cow_header(char *cow_file, int fd, char *backing_file, 
++                   int sectorsize, int alignment, long long *size)
++{
++      struct cow_header_v3 *header;
++      unsigned long modtime;
++      int err;
++
++      err = cow_seek_file(fd, 0);
++      if(err < 0){
++              cow_printf("write_cow_header - lseek failed, err = %d\n", -err);
++              goto out;
++      }
++
++      err = -ENOMEM;
++      header = cow_malloc(sizeof(*header));
++      if(header == NULL){
++              cow_printf("Failed to allocate COW V3 header\n");
++              goto out;
++      }
++      header->magic = htonl(COW_MAGIC);
++      header->version = htonl(COW_VERSION);
++
++      err = -EINVAL;
++      if(strlen(backing_file) > sizeof(header->backing_file) - 1){
++              cow_printf("Backing file name \"%s\" is too long - names are "
++                         "limited to %d characters\n", backing_file, 
++                         sizeof(header->backing_file) - 1);
++              goto out_free;
++      }
++
++      if(absolutize(header->backing_file, sizeof(header->backing_file), 
++                    backing_file))
++              goto out_free;
++
++      err = os_file_modtime(header->backing_file, &modtime);
++      if(err < 0){
++              cow_printf("Backing file '%s' mtime request failed, "
++                         "err = %d\n", header->backing_file, -err);
++              goto out_free;
++      }
++
++      err = cow_file_size(header->backing_file, size);
++      if(err < 0){
++              cow_printf("Couldn't get size of backing file '%s', "
++                         "err = %d\n", header->backing_file, -err);
++              goto out_free;
++      }
++
++      header->mtime = htonl(modtime);
++      header->size = htonll(*size);
++      header->sectorsize = htonl(sectorsize);
++      header->alignment = htonl(alignment);
++      header->cow_format = COW_BITMAP;
++
++      err = os_write_file(fd, header, sizeof(*header));
++      if(err != sizeof(*header)){
++              cow_printf("Write of header to new COW file '%s' failed, "
++                         "err = %d\n", cow_file, -err);
++              goto out_free;
++      }
++      err = 0;
++ out_free:
++      cow_free(header);
++ out:
++      return(err);
++}
++
++int file_reader(__u64 offset, char *buf, int len, void *arg)
++{
++      int fd = *((int *) arg);
++
++      return(pread(fd, buf, len, offset));
++}
++
++/* XXX Need to sanity-check the values read from the header */
++
++int read_cow_header(int (*reader)(__u64, char *, int, void *), void *arg, 
++                  __u32 *version_out, char **backing_file_out, 
++                  time_t *mtime_out, __u64 *size_out, 
++                  int *sectorsize_out, __u32 *align_out, 
++                  int *bitmap_offset_out)
++{
++      union cow_header *header;
++      char *file;
++      int err, n;
++      unsigned long version, magic;
++
++      header = cow_malloc(sizeof(*header));
++      if(header == NULL){
++              cow_printf("read_cow_header - Failed to allocate header\n");
++              return(-ENOMEM);
++      }
++      err = -EINVAL;
++      n = (*reader)(0, (char *) header, sizeof(*header), arg);
++      if(n < offsetof(typeof(header->v1), backing_file)){
++              cow_printf("read_cow_header - short header\n");
++              goto out;
++      }
++
++      magic = header->v1.magic;
++      if(magic == COW_MAGIC) {
++              version = header->v1.version;
++      }
++      else if(magic == ntohl(COW_MAGIC)){
++              version = ntohl(header->v1.version);
++      }
++      /* No error printed because the non-COW case comes through here */
++      else goto out;
++
++      *version_out = version;
++
++      if(version == 1){
++              if(n < sizeof(header->v1)){
++                      cow_printf("read_cow_header - failed to read V1 "
++                                 "header\n");
++                      goto out;
++              }
++              *mtime_out = header->v1.mtime;
++              *size_out = header->v1.size;
++              *sectorsize_out = header->v1.sectorsize;
++              *bitmap_offset_out = sizeof(header->v1);
++              *align_out = *sectorsize_out;
++              file = header->v1.backing_file;
++      }
++      else if(version == 2){
++              if(n < sizeof(header->v2)){
++                      cow_printf("read_cow_header - failed to read V2 "
++                                 "header\n");
++                      goto out;
++              }
++              *mtime_out = ntohl(header->v2.mtime);
++              *size_out = ntohll(header->v2.size);
++              *sectorsize_out = ntohl(header->v2.sectorsize);
++              *bitmap_offset_out = sizeof(header->v2);
++              *align_out = *sectorsize_out;
++              file = header->v2.backing_file;
++      }
++      else if(version == 3){
++              if(n < sizeof(header->v3)){
++                      cow_printf("read_cow_header - failed to read V2 "
++                                 "header\n");
++                      goto out;
++              }
++              *mtime_out = ntohl(header->v3.mtime);
++              *size_out = ntohll(header->v3.size);
++              *sectorsize_out = ntohl(header->v3.sectorsize);
++              *align_out = ntohl(header->v3.alignment);
++              *bitmap_offset_out = ROUND_UP(sizeof(header->v3), *align_out);
++              file = header->v3.backing_file;
++      }
++      else {
++              cow_printf("read_cow_header - invalid COW version\n");
++              goto out;               
++      }
++      err = -ENOMEM;
++      *backing_file_out = cow_strdup(file);
++      if(*backing_file_out == NULL){
++              cow_printf("read_cow_header - failed to allocate backing "
++                         "file\n");
++              goto out;
++      }
++      err = 0;
++ out:
++      cow_free(header);
++      return(err);
++}
++
++int init_cow_file(int fd, char *cow_file, char *backing_file, int sectorsize,
++                int alignment, int *bitmap_offset_out, 
++                unsigned long *bitmap_len_out, int *data_offset_out)
++{
++      __u64 size, offset;
++      char zero = 0;
++      int err;
++
++      err = write_cow_header(cow_file, fd, backing_file, sectorsize, 
++                             alignment, &size);
++      if(err) 
++              goto out;
++      
++      *bitmap_offset_out = ROUND_UP(sizeof(struct cow_header_v3), alignment);
++      cow_sizes(COW_VERSION, size, sectorsize, alignment, *bitmap_offset_out,
++                bitmap_len_out, data_offset_out);
++
++      offset = *data_offset_out + size - sizeof(zero);
++      err = cow_seek_file(fd, offset);
++      if(err < 0){
++              cow_printf("cow bitmap lseek failed : err = %d\n", -err);
++              goto out;
++      }
++
++      /* does not really matter how much we write it is just to set EOF 
++       * this also sets the entire COW bitmap
++       * to zero without having to allocate it 
++       */
++      err = cow_write_file(fd, &zero, sizeof(zero));
++      if(err != sizeof(zero)){
++              cow_printf("Write of bitmap to new COW file '%s' failed, "
++                         "err = %d\n", cow_file, -err);
++              err = -EINVAL;
++              goto out;
++      }
++
++      return(0);
++
++ out:
++      return(err);
++}
++
++/*
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/daemon.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/daemon.h 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/daemon.h      2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,35 @@
++/* 
++ * Copyright (C) 2001 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "net_user.h"
++
++#define SWITCH_VERSION 3
++
++struct daemon_data {
++      char *sock_type;
++      char *ctl_sock;
++      void *ctl_addr;
++      void *data_addr;
++      void *local_addr;
++      int fd;
++      int control;
++      void *dev;
++};
++
++extern struct net_user_info daemon_user_info;
++
++extern int daemon_user_write(int fd, void *buf, int len, 
++                           struct daemon_data *pri);
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/daemon_kern.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/daemon_kern.c    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/daemon_kern.c 2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,113 @@
++/*
++ * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and 
++ * James Leu (jleu@mindspring.net).
++ * Copyright (C) 2001 by various other people who didn't put their name here.
++ * Licensed under the GPL.
++ */
++
++#include "linux/kernel.h"
++#include "linux/init.h"
++#include "linux/netdevice.h"
++#include "linux/etherdevice.h"
++#include "net_kern.h"
++#include "net_user.h"
++#include "daemon.h"
++
++struct daemon_init {
++      char *sock_type;
++      char *ctl_sock;
++};
++
++void daemon_init(struct net_device *dev, void *data)
++{
++      struct uml_net_private *pri;
++      struct daemon_data *dpri;
++      struct daemon_init *init = data;
++
++      init_etherdev(dev, 0);
++      pri = dev->priv;
++      dpri = (struct daemon_data *) pri->user;
++      *dpri = ((struct daemon_data)
++              { .sock_type            = init->sock_type,
++                .ctl_sock             = init->ctl_sock,
++                .ctl_addr             = NULL,
++                .data_addr            = NULL,
++                .local_addr           = NULL,
++                .fd                   = -1,
++                .control              = -1,
++                .dev                  = dev });
++
++      printk("daemon backend (uml_switch version %d) - %s:%s", 
++             SWITCH_VERSION, dpri->sock_type, dpri->ctl_sock);
++      printk("\n");
++}
++
++static int daemon_read(int fd, struct sk_buff **skb, 
++                     struct uml_net_private *lp)
++{
++      *skb = ether_adjust_skb(*skb, ETH_HEADER_OTHER);
++      if(*skb == NULL) return(-ENOMEM);
++      return(net_recvfrom(fd, (*skb)->mac.raw, 
++                          (*skb)->dev->mtu + ETH_HEADER_OTHER));
++}
++
++static int daemon_write(int fd, struct sk_buff **skb,
++                      struct uml_net_private *lp)
++{
++      return(daemon_user_write(fd, (*skb)->data, (*skb)->len, 
++                               (struct daemon_data *) &lp->user));
++}
++
++static struct net_kern_info daemon_kern_info = {
++      .init                   = daemon_init,
++      .protocol               = eth_protocol,
++      .read                   = daemon_read,
++      .write                  = daemon_write,
++};
++
++int daemon_setup(char *str, char **mac_out, void *data)
++{
++      struct daemon_init *init = data;
++      char *remain;
++
++      *init = ((struct daemon_init)
++              { .sock_type            = "unix",
++                .ctl_sock             = "/tmp/uml.ctl" });
++      
++      remain = split_if_spec(str, mac_out, &init->sock_type, &init->ctl_sock,
++                             NULL);
++      if(remain != NULL)
++              printk(KERN_WARNING "daemon_setup : Ignoring data socket "
++                     "specification\n");
++      
++      return(1);
++}
++
++static struct transport daemon_transport = {
++      .list           = LIST_HEAD_INIT(daemon_transport.list),
++      .name           = "daemon",
++      .setup          = daemon_setup,
++      .user           = &daemon_user_info,
++      .kern           = &daemon_kern_info,
++      .private_size   = sizeof(struct daemon_data),
++      .setup_size     = sizeof(struct daemon_init),
++};
++
++static int register_daemon(void)
++{
++      register_transport(&daemon_transport);
++      return(1);
++}
++
++__initcall(register_daemon);
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/daemon_user.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/daemon_user.c    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/daemon_user.c 2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,197 @@
++/*
++ * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and 
++ * James Leu (jleu@mindspring.net).
++ * Copyright (C) 2001 by various other people who didn't put their name here.
++ * Licensed under the GPL.
++ */
++
++#include <errno.h>
++#include <unistd.h>
++#include <stdint.h>
++#include <sys/socket.h>
++#include <sys/un.h>
++#include <sys/time.h>
++#include "net_user.h"
++#include "daemon.h"
++#include "kern_util.h"
++#include "user_util.h"
++#include "user.h"
++#include "os.h"
++
++#define MAX_PACKET (ETH_MAX_PACKET + ETH_HEADER_OTHER)
++
++enum request_type { REQ_NEW_CONTROL };
++
++#define SWITCH_MAGIC 0xfeedface
++
++struct request_v3 {
++      uint32_t magic;
++      uint32_t version;
++      enum request_type type;
++      struct sockaddr_un sock;
++};
++
++static struct sockaddr_un *new_addr(void *name, int len)
++{
++      struct sockaddr_un *sun;
++
++      sun = um_kmalloc(sizeof(struct sockaddr_un));
++      if(sun == NULL){
++              printk("new_addr: allocation of sockaddr_un failed\n");
++              return(NULL);
++      }
++      sun->sun_family = AF_UNIX;
++      memcpy(sun->sun_path, name, len);
++      return(sun);
++}
++
++static int connect_to_switch(struct daemon_data *pri)
++{
++      struct sockaddr_un *ctl_addr = pri->ctl_addr;
++      struct sockaddr_un *local_addr = pri->local_addr;
++      struct sockaddr_un *sun;
++      struct request_v3 req;
++      int fd, n, err;
++
++      pri->control = socket(AF_UNIX, SOCK_STREAM, 0);
++      if(pri->control < 0){
++              printk("daemon_open : control socket failed, errno = %d\n", 
++                     errno);          
++              return(-errno);
++      }
++
++      if(connect(pri->control, (struct sockaddr *) ctl_addr, 
++                 sizeof(*ctl_addr)) < 0){
++              printk("daemon_open : control connect failed, errno = %d\n",
++                     errno);
++              err = -errno;
++              goto out;
++      }
++
++      fd = socket(AF_UNIX, SOCK_DGRAM, 0);
++      if(fd < 0){
++              printk("daemon_open : data socket failed, errno = %d\n", 
++                     errno);
++              err = -errno;
++              goto out;
++      }
++      if(bind(fd, (struct sockaddr *) local_addr, sizeof(*local_addr)) < 0){
++              printk("daemon_open : data bind failed, errno = %d\n", 
++                     errno);
++              err = -errno;
++              goto out_close;
++      }
++
++      sun = um_kmalloc(sizeof(struct sockaddr_un));
++      if(sun == NULL){
++              printk("new_addr: allocation of sockaddr_un failed\n");
++              err = -ENOMEM;
++              goto out_close;
++      }
++
++      req.magic = SWITCH_MAGIC;
++      req.version = SWITCH_VERSION;
++      req.type = REQ_NEW_CONTROL;
++      req.sock = *local_addr;
++      n = os_write_file(pri->control, &req, sizeof(req));
++      if(n != sizeof(req)){
++              printk("daemon_open : control setup request failed, err = %d\n",
++                     -n);
++              err = -ENOTCONN;
++              goto out;               
++      }
++
++      n = os_read_file(pri->control, sun, sizeof(*sun));
++      if(n != sizeof(*sun)){
++              printk("daemon_open : read of data socket failed, err = %d\n", 
++                     -n);
++              err = -ENOTCONN;
++              goto out_close;         
++      }
++
++      pri->data_addr = sun;
++      return(fd);
++
++ out_close:
++      os_close_file(fd);
++ out:
++      os_close_file(pri->control);
++      return(err);
++}
++
++static void daemon_user_init(void *data, void *dev)
++{
++      struct daemon_data *pri = data;
++      struct timeval tv;
++      struct {
++              char zero;
++              int pid;
++              int usecs;
++      } name;
++
++      if(!strcmp(pri->sock_type, "unix"))
++              pri->ctl_addr = new_addr(pri->ctl_sock, 
++                                       strlen(pri->ctl_sock) + 1);
++      name.zero = 0;
++      name.pid = os_getpid();
++      gettimeofday(&tv, NULL);
++      name.usecs = tv.tv_usec;
++      pri->local_addr = new_addr(&name, sizeof(name));
++      pri->dev = dev;
++      pri->fd = connect_to_switch(pri);
++      if(pri->fd < 0){
++              kfree(pri->local_addr);
++              pri->local_addr = NULL;
++      }
++}
++
++static int daemon_open(void *data)
++{
++      struct daemon_data *pri = data;
++      return(pri->fd);
++}
++
++static void daemon_remove(void *data)
++{
++      struct daemon_data *pri = data;
++
++      os_close_file(pri->fd);
++      os_close_file(pri->control);
++      if(pri->data_addr != NULL) kfree(pri->data_addr);
++      if(pri->ctl_addr != NULL) kfree(pri->ctl_addr);
++      if(pri->local_addr != NULL) kfree(pri->local_addr);
++}
++
++int daemon_user_write(int fd, void *buf, int len, struct daemon_data *pri)
++{
++      struct sockaddr_un *data_addr = pri->data_addr;
++
++      return(net_sendto(fd, buf, len, data_addr, sizeof(*data_addr)));
++}
++
++static int daemon_set_mtu(int mtu, void *data)
++{
++      return(mtu);
++}
++
++struct net_user_info daemon_user_info = {
++      .init           = daemon_user_init,
++      .open           = daemon_open,
++      .close          = NULL,
++      .remove         = daemon_remove,
++      .set_mtu        = daemon_set_mtu,
++      .add_address    = NULL,
++      .delete_address = NULL,
++      .max_packet     = MAX_PACKET - ETH_HEADER_OTHER
++};
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/fd.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/fd.c     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/fd.c  2005-05-03 22:28:14.208450200 +0300
+@@ -0,0 +1,108 @@
++/* 
++ * Copyright (C) 2001 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <termios.h>
++#include <errno.h>
++#include "user.h"
++#include "user_util.h"
++#include "chan_user.h"
++
++struct fd_chan {
++      int fd;
++      int raw;
++      struct termios tt;
++      char str[sizeof("1234567890\0")];
++};
++
++void *fd_init(char *str, int device, struct chan_opts *opts)
++{
++      struct fd_chan *data;
++      char *end;
++      int n;
++
++      if(*str != ':'){
++              printk("fd_init : channel type 'fd' must specify a file "
++                     "descriptor\n");
++              return(NULL);
++      }
++      str++;
++      n = strtoul(str, &end, 0);
++      if((*end != '\0') || (end == str)){
++              printk("fd_init : couldn't parse file descriptor '%s'\n", str);
++              return(NULL);
++      }
++      data = um_kmalloc(sizeof(*data));
++      if(data == NULL) return(NULL);
++      *data = ((struct fd_chan) { .fd         = n,
++                                  .raw        = opts->raw });
++      return(data);
++}
++
++int fd_open(int input, int output, int primary, void *d, char **dev_out)
++{
++      struct fd_chan *data = d;
++      int err;
++
++      if(data->raw && isatty(data->fd)){
++              CATCH_EINTR(err = tcgetattr(data->fd, &data->tt));
++              if(err)
++                      return(err);
++
++              err = raw(data->fd);
++              if(err)
++                      return(err);
++      }
++      sprintf(data->str, "%d", data->fd);
++      *dev_out = data->str;
++      return(data->fd);
++}
++
++void fd_close(int fd, void *d)
++{
++      struct fd_chan *data = d;
++      int err;
++
++      if(data->raw && isatty(fd)){
++              CATCH_EINTR(err = tcsetattr(fd, TCSAFLUSH, &data->tt));
++              if(err)
++                      printk("Failed to restore terminal state - " 
++                             "errno = %d\n", -err);
++              data->raw = 0;
++      }
++}
++
++int fd_console_write(int fd, const char *buf, int n, void *d)
++{
++      struct fd_chan *data = d;
++
++      return(generic_console_write(fd, buf, n, &data->tt));
++}
++
++struct chan_ops fd_ops = {
++      .type           = "fd",
++      .init           = fd_init,
++      .open           = fd_open,
++      .close          = fd_close,
++      .read           = generic_read,
++      .write          = generic_write,
++      .console_write  = fd_console_write,
++      .window_size    = generic_window_size,
++      .free           = generic_free,
++      .winch          = 1,
++};
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/harddog_kern.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/harddog_kern.c   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/harddog_kern.c        2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,194 @@
++/* UML hardware watchdog, shamelessly stolen from:
++ *
++ *    SoftDog 0.05:   A Software Watchdog Device
++ *
++ *    (c) Copyright 1996 Alan Cox <alan@redhat.com>, All Rights Reserved.
++ *                            http://www.redhat.com
++ *
++ *    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.
++ *    
++ *    Neither Alan Cox nor CymruNet Ltd. admit liability nor provide 
++ *    warranty for any of this software. This material is provided 
++ *    "AS-IS" and at no charge.       
++ *
++ *    (c) Copyright 1995    Alan Cox <alan@lxorguk.ukuu.org.uk>
++ *
++ *    Software only watchdog driver. Unlike its big brother the WDT501P
++ *    driver this won't always recover a failed machine.
++ *
++ *  03/96: Angelo Haritsis <ah@doc.ic.ac.uk> :
++ *    Modularised.
++ *    Added soft_margin; use upon insmod to change the timer delay.
++ *    NB: uses same minor as wdt (WATCHDOG_MINOR); we could use separate
++ *        minors.
++ *
++ *  19980911 Alan Cox
++ *    Made SMP safe for 2.3.x
++ *
++ *  20011127 Joel Becker (jlbec@evilplan.org>
++ *    Added soft_noboot; Allows testing the softdog trigger without 
++ *    requiring a recompile.
++ *    Added WDIOC_GETTIMEOUT and WDIOC_SETTIMOUT.
++ */
++ 
++#include <linux/module.h>
++#include <linux/config.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/fs.h>
++#include <linux/mm.h>
++#include <linux/miscdevice.h>
++#include <linux/watchdog.h>
++#include <linux/reboot.h>
++#include <linux/smp_lock.h>
++#include <linux/init.h>
++#include <asm/uaccess.h>
++#include "helper.h"
++#include "mconsole.h"
++
++MODULE_LICENSE("GPL");
++
++/* Locked by the BKL in harddog_open and harddog_release */
++static int timer_alive;
++static int harddog_in_fd = -1;
++static int harddog_out_fd = -1;
++
++/*
++ *    Allow only one person to hold it open
++ */
++ 
++extern int start_watchdog(int *in_fd_ret, int *out_fd_ret, char *sock);
++
++static int harddog_open(struct inode *inode, struct file *file)
++{
++      int err;
++      char *sock = NULL;
++
++      lock_kernel();
++      if(timer_alive)
++              return -EBUSY;
++#ifdef CONFIG_HARDDOG_NOWAYOUT         
++      MOD_INC_USE_COUNT;
++#endif
++
++#ifdef CONFIG_MCONSOLE
++      sock = mconsole_notify_socket();
++#endif
++      err = start_watchdog(&harddog_in_fd, &harddog_out_fd, sock);
++      if(err) return(err);
++
++      timer_alive = 1;
++      unlock_kernel();
++      return 0;
++}
++
++extern void stop_watchdog(int in_fd, int out_fd);
++
++static int harddog_release(struct inode *inode, struct file *file)
++{
++      /*
++       *      Shut off the timer.
++       */
++      lock_kernel();
++
++      stop_watchdog(harddog_in_fd, harddog_out_fd);
++      harddog_in_fd = -1;
++      harddog_out_fd = -1;
++
++      timer_alive=0;
++      unlock_kernel();
++      return 0;
++}
++
++extern int ping_watchdog(int fd);
++
++static ssize_t harddog_write(struct file *file, const char *data, size_t len,
++                           loff_t *ppos)
++{
++      /*  Can't seek (pwrite) on this device  */
++      if (ppos != &file->f_pos)
++              return -ESPIPE;
++
++      /*
++       *      Refresh the timer.
++       */
++      if(len)
++              return(ping_watchdog(harddog_out_fd));
++      return 0;
++}
++
++static int harddog_ioctl(struct inode *inode, struct file *file,
++                       unsigned int cmd, unsigned long arg)
++{
++      static struct watchdog_info ident = {
++              WDIOF_SETTIMEOUT,
++              0,
++              "UML Hardware Watchdog"
++      };
++      switch (cmd) {
++              default:
++                      return -ENOTTY;
++              case WDIOC_GETSUPPORT:
++                      if(copy_to_user((struct harddog_info *)arg, &ident,
++                                      sizeof(ident)))
++                              return -EFAULT;
++                      return 0;
++              case WDIOC_GETSTATUS:
++              case WDIOC_GETBOOTSTATUS:
++                      return put_user(0,(int *)arg);
++              case WDIOC_KEEPALIVE:
++                      return(ping_watchdog(harddog_out_fd));
++      }
++}
++
++static struct file_operations harddog_fops = {
++      .owner          = THIS_MODULE,
++      .write          = harddog_write,
++      .ioctl          = harddog_ioctl,
++      .open           = harddog_open,
++      .release        = harddog_release,
++};
++
++static struct miscdevice harddog_miscdev = {
++      .minor          = WATCHDOG_MINOR,
++      .name           = "watchdog",
++      .fops           = &harddog_fops,
++};
++
++static char banner[] __initdata = KERN_INFO "UML Watchdog Timer\n";
++
++static int __init harddog_init(void)
++{
++      int ret;
++
++      ret = misc_register(&harddog_miscdev);
++
++      if (ret)
++              return ret;
++
++      printk(banner);
++
++      return(0);
++}
++
++static void __exit harddog_exit(void)
++{
++      misc_deregister(&harddog_miscdev);
++}
++
++module_init(harddog_init);
++module_exit(harddog_exit);
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/harddog_user.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/harddog_user.c   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/harddog_user.c        2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,143 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <stdio.h>
++#include <unistd.h>
++#include <errno.h>
++#include "user_util.h"
++#include "user.h"
++#include "helper.h"
++#include "mconsole.h"
++#include "os.h"
++#include "choose-mode.h"
++#include "mode.h"
++
++struct dog_data {
++      int stdin;
++      int stdout;
++      int close_me[2];
++};
++
++static void pre_exec(void *d)
++{
++      struct dog_data *data = d;
++
++      dup2(data->stdin, 0);
++      dup2(data->stdout, 1);
++      dup2(data->stdout, 2);
++      os_close_file(data->stdin);
++      os_close_file(data->stdout);
++      os_close_file(data->close_me[0]);
++      os_close_file(data->close_me[1]);
++}
++
++int start_watchdog(int *in_fd_ret, int *out_fd_ret, char *sock)
++{
++      struct dog_data data;
++      int in_fds[2], out_fds[2], pid, n, err;
++      char pid_buf[sizeof("nnnnn\0")], c;
++      char *pid_args[] = { "/usr/bin/uml_watchdog", "-pid", pid_buf, NULL };
++      char *mconsole_args[] = { "/usr/bin/uml_watchdog", "-mconsole", NULL, 
++                                NULL };
++      char **args = NULL;
++
++      err = os_pipe(in_fds, 1, 0);
++      if(err < 0){
++              printk("harddog_open - os_pipe failed, err = %d\n", -err);
++              goto out;
++      }
++
++      err = os_pipe(out_fds, 1, 0);
++      if(err < 0){
++              printk("harddog_open - os_pipe failed, err = %d\n", -err);
++              goto out_close_in;
++      }
++
++      data.stdin = out_fds[0];
++      data.stdout = in_fds[1];
++      data.close_me[0] = out_fds[1];
++      data.close_me[1] = in_fds[0];
++
++      if(sock != NULL){
++              mconsole_args[2] = sock;
++              args = mconsole_args;
++      }
++      else {
++              /* XXX The os_getpid() is not SMP correct */
++              sprintf(pid_buf, "%d", CHOOSE_MODE(tracing_pid, os_getpid()));
++              args = pid_args;
++      }
++
++      pid = run_helper(pre_exec, &data, args, NULL);
++
++      os_close_file(out_fds[0]);
++      os_close_file(in_fds[1]);
++
++      if(pid < 0){
++              err = -pid;
++              printk("harddog_open - run_helper failed, errno = %d\n", -err);
++              goto out_close_out;
++      }
++
++      n = os_read_file(in_fds[0], &c, sizeof(c));
++      if(n == 0){
++              printk("harddog_open - EOF on watchdog pipe\n");
++              helper_wait(pid);
++              err = -EIO;
++              goto out_close_out;
++      }
++      else if(n < 0){
++              printk("harddog_open - read of watchdog pipe failed, "
++                     "err = %d\n", -n);
++              helper_wait(pid);
++              err = n;
++              goto out_close_out;
++      }
++      *in_fd_ret = in_fds[0];
++      *out_fd_ret = out_fds[1];
++      return(0);
++
++ out_close_in:
++      os_close_file(in_fds[0]);
++      os_close_file(in_fds[1]);
++ out_close_out:
++      os_close_file(out_fds[0]);
++      os_close_file(out_fds[1]);
++ out:
++      return(err);
++}
++
++void stop_watchdog(int in_fd, int out_fd)
++{
++      os_close_file(in_fd);
++      os_close_file(out_fd);
++}
++
++int ping_watchdog(int fd)
++{
++      int n;
++      char c = '\n';
++
++      n = os_write_file(fd, &c, sizeof(c));
++      if(n != sizeof(c)){
++              printk("ping_watchdog - write failed, err = %d\n", -n);
++              if(n < 0) 
++                      return(n);
++              return(-EIO);
++      }
++      return 1;
++
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/hostaudio_kern.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/hostaudio_kern.c 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/hostaudio_kern.c      2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,352 @@
++/* 
++ * Copyright (C) 2002 Steve Schmidtke 
++ * Licensed under the GPL
++ */
++
++#include "linux/config.h"
++#include "linux/module.h"
++#include "linux/init.h"
++#include "linux/slab.h"
++#include "linux/fs.h"
++#include "linux/sound.h"
++#include "linux/soundcard.h"
++#include "asm/uaccess.h"
++#include "kern_util.h"
++#include "init.h"
++#include "os.h"
++
++struct hostaudio_state {
++      int fd;
++};
++
++struct hostmixer_state {
++      int fd;
++};
++
++#define HOSTAUDIO_DEV_DSP "/dev/sound/dsp"
++#define HOSTAUDIO_DEV_MIXER "/dev/sound/mixer"
++
++/* Only changed from linux_main at boot time */
++char *dsp = HOSTAUDIO_DEV_DSP;
++char *mixer = HOSTAUDIO_DEV_MIXER;
++
++#define DSP_HELP \
++"    This is used to specify the host dsp device to the hostaudio driver.\n" \
++"    The default is \"" HOSTAUDIO_DEV_DSP "\".\n\n"
++
++#define MIXER_HELP \
++"    This is used to specify the host mixer device to the hostaudio driver.\n"\
++"    The default is \"" HOSTAUDIO_DEV_MIXER "\".\n\n"
++
++#ifndef MODULE
++static int set_dsp(char *name, int *add)
++{
++      dsp = name;
++      return(0);
++}
++
++__uml_setup("dsp=", set_dsp, "dsp=<dsp device>\n" DSP_HELP);
++
++static int set_mixer(char *name, int *add)
++{
++      mixer = name;
++      return(0);
++}
++
++__uml_setup("mixer=", set_mixer, "mixer=<mixer device>\n" MIXER_HELP);
++
++#else /*MODULE*/
++
++MODULE_PARM(dsp, "s");
++MODULE_PARM_DESC(dsp, DSP_HELP);
++
++MODULE_PARM(mixer, "s");
++MODULE_PARM_DESC(mixer, MIXER_HELP);
++
++#endif
++
++/* /dev/dsp file operations */
++
++static ssize_t hostaudio_read(struct file *file, char *buffer, size_t count, 
++                            loff_t *ppos)
++{
++        struct hostaudio_state *state = file->private_data;
++      void *kbuf;
++      int err;
++
++#ifdef DEBUG
++        printk("hostaudio: read called, count = %d\n", count);
++#endif
++
++      kbuf = kmalloc(count, GFP_KERNEL);
++      if(kbuf == NULL)
++              return(-ENOMEM);
++
++      err = os_read_file(state->fd, kbuf, count);
++      if(err < 0)
++              goto out;
++
++      if(copy_to_user(buffer, kbuf, err))
++              err = -EFAULT;
++
++ out:
++      kfree(kbuf);
++      return(err);
++}
++
++static ssize_t hostaudio_write(struct file *file, const char *buffer, 
++                             size_t count, loff_t *ppos)
++{
++        struct hostaudio_state *state = file->private_data;
++      void *kbuf;
++      int err;
++
++#ifdef DEBUG
++        printk("hostaudio: write called, count = %d\n", count);
++#endif
++
++      kbuf = kmalloc(count, GFP_KERNEL);
++      if(kbuf == NULL)
++              return(-ENOMEM);
++
++      err = -EFAULT;
++      if(copy_from_user(kbuf, buffer, count))
++              goto out;
++
++      err = os_write_file(state->fd, kbuf, count);
++      if(err < 0)
++              goto out;
++      *ppos += err;
++
++ out:
++      kfree(kbuf);
++      return(err);
++}
++
++static unsigned int hostaudio_poll(struct file *file, 
++                                 struct poll_table_struct *wait)
++{
++        unsigned int mask = 0;
++
++#ifdef DEBUG
++        printk("hostaudio: poll called (unimplemented)\n");
++#endif
++
++        return(mask);
++}
++
++static int hostaudio_ioctl(struct inode *inode, struct file *file, 
++                         unsigned int cmd, unsigned long arg)
++{
++        struct hostaudio_state *state = file->private_data;
++      unsigned long data = 0;
++      int err;
++
++#ifdef DEBUG
++        printk("hostaudio: ioctl called, cmd = %u\n", cmd);
++#endif
++      switch(cmd){
++      case SNDCTL_DSP_SPEED:
++      case SNDCTL_DSP_STEREO:
++      case SNDCTL_DSP_GETBLKSIZE:
++      case SNDCTL_DSP_CHANNELS:
++      case SNDCTL_DSP_SUBDIVIDE:
++      case SNDCTL_DSP_SETFRAGMENT:
++              if(get_user(data, (int *) arg))
++                      return(-EFAULT);
++              break;
++      default:
++              break;
++      }
++
++      err = os_ioctl_generic(state->fd, cmd, (unsigned long) &data);
++
++      switch(cmd){
++      case SNDCTL_DSP_SPEED:
++      case SNDCTL_DSP_STEREO:
++      case SNDCTL_DSP_GETBLKSIZE:
++      case SNDCTL_DSP_CHANNELS:
++      case SNDCTL_DSP_SUBDIVIDE:
++      case SNDCTL_DSP_SETFRAGMENT:
++              if(put_user(data, (int *) arg))
++                      return(-EFAULT);
++              break;
++      default:
++              break;
++      }
++
++      return(err);
++}
++
++static int hostaudio_open(struct inode *inode, struct file *file)
++{
++        struct hostaudio_state *state;
++        int r = 0, w = 0;
++        int ret;
++
++#ifdef DEBUG
++        printk("hostaudio: open called (host: %s)\n", dsp);
++#endif
++
++        state = kmalloc(sizeof(struct hostaudio_state), GFP_KERNEL);
++        if(state == NULL) 
++              return(-ENOMEM);
++
++        if(file->f_mode & FMODE_READ) r = 1;
++        if(file->f_mode & FMODE_WRITE) w = 1;
++
++      ret = os_open_file(dsp, of_set_rw(OPENFLAGS(), r, w), 0);
++        if(ret < 0){
++              kfree(state);
++              return(ret);
++        }
++
++      state->fd = ret;
++        file->private_data = state;
++        return(0);
++}
++
++static int hostaudio_release(struct inode *inode, struct file *file)
++{
++        struct hostaudio_state *state = file->private_data;
++
++#ifdef DEBUG
++        printk("hostaudio: release called\n");
++#endif
++
++      os_close_file(state->fd);
++        kfree(state);
++
++        return(0);
++}
++
++/* /dev/mixer file operations */
++
++static int hostmixer_ioctl_mixdev(struct inode *inode, struct file *file, 
++                                unsigned int cmd, unsigned long arg)
++{
++        struct hostmixer_state *state = file->private_data;
++
++#ifdef DEBUG
++        printk("hostmixer: ioctl called\n");
++#endif
++
++      return(os_ioctl_generic(state->fd, cmd, arg));
++}
++
++static int hostmixer_open_mixdev(struct inode *inode, struct file *file)
++{
++        struct hostmixer_state *state;
++        int r = 0, w = 0;
++        int ret;
++
++#ifdef DEBUG
++        printk("hostmixer: open called (host: %s)\n", mixer);
++#endif
++
++        state = kmalloc(sizeof(struct hostmixer_state), GFP_KERNEL);
++        if(state == NULL) return(-ENOMEM);
++
++        if(file->f_mode & FMODE_READ) r = 1;
++        if(file->f_mode & FMODE_WRITE) w = 1;
++
++      ret = os_open_file(mixer, of_set_rw(OPENFLAGS(), r, w), 0);
++        
++        if(ret < 0){
++              printk("hostaudio_open_mixdev failed to open '%s', err = %d\n",
++                     dsp, -ret);
++              kfree(state);
++              return(ret);
++        }
++
++        file->private_data = state;
++        return(0);
++}
++
++static int hostmixer_release(struct inode *inode, struct file *file)
++{
++        struct hostmixer_state *state = file->private_data;
++
++#ifdef DEBUG
++        printk("hostmixer: release called\n");
++#endif
++
++      os_close_file(state->fd);
++        kfree(state);
++
++        return(0);
++}
++
++
++/* kernel module operations */
++
++static struct file_operations hostaudio_fops = {
++        .owner          = THIS_MODULE,
++        .llseek         = no_llseek,
++        .read           = hostaudio_read,
++        .write          = hostaudio_write,
++        .poll           = hostaudio_poll,
++        .ioctl          = hostaudio_ioctl,
++        .mmap           = NULL,
++        .open           = hostaudio_open,
++        .release        = hostaudio_release,
++};
++
++static struct file_operations hostmixer_fops = {
++        .owner          = THIS_MODULE,
++        .llseek         = no_llseek,
++        .ioctl          = hostmixer_ioctl_mixdev,
++        .open           = hostmixer_open_mixdev,
++        .release        = hostmixer_release,
++};
++
++struct {
++      int dev_audio;
++      int dev_mixer;
++} module_data;
++
++MODULE_AUTHOR("Steve Schmidtke");
++MODULE_DESCRIPTION("UML Audio Relay");
++MODULE_LICENSE("GPL");
++
++static int __init hostaudio_init_module(void)
++{
++        printk(KERN_INFO "UML Audio Relay (host dsp = %s, host mixer = %s)\n",
++             dsp, mixer);
++
++      module_data.dev_audio = register_sound_dsp(&hostaudio_fops, -1);
++        if(module_data.dev_audio < 0){
++                printk(KERN_ERR "hostaudio: couldn't register DSP device!\n");
++                return -ENODEV;
++        }
++
++      module_data.dev_mixer = register_sound_mixer(&hostmixer_fops, -1);
++        if(module_data.dev_mixer < 0){
++                printk(KERN_ERR "hostmixer: couldn't register mixer "
++                     "device!\n");
++                unregister_sound_dsp(module_data.dev_audio);
++                return -ENODEV;
++        }
++
++        return 0;
++}
++
++static void __exit hostaudio_cleanup_module (void)
++{
++       unregister_sound_mixer(module_data.dev_mixer);
++       unregister_sound_dsp(module_data.dev_audio);
++}
++
++module_init(hostaudio_init_module);
++module_exit(hostaudio_cleanup_module);
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/line.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/line.c   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/line.c        2005-05-03 22:28:14.214449288 +0300
+@@ -0,0 +1,610 @@
++/* 
++ * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/sched.h"
++#include "linux/slab.h"
++#include "linux/list.h"
++#include "linux/devfs_fs_kernel.h"
++#include "asm/irq.h"
++#include "asm/uaccess.h"
++#include "chan_kern.h"
++#include "irq_user.h"
++#include "line.h"
++#include "kern.h"
++#include "user_util.h"
++#include "kern_util.h"
++#include "os.h"
++#include "irq_kern.h"
++
++#define LINE_BUFSIZE 4096
++
++static void line_interrupt(int irq, void *data, struct pt_regs *unused)
++{
++      struct line *dev = data;
++
++      if(dev->count > 0) 
++              chan_interrupt(&dev->chan_list, &dev->task, dev->tty, irq, 
++                             dev);
++}
++
++static void line_timer_cb(void *arg)
++{
++      struct line *dev = arg;
++
++      line_interrupt(dev->driver->read_irq, dev, NULL);
++}
++
++static int write_room(struct line *dev)
++{
++      int n;
++
++      if(dev->buffer == NULL) return(LINE_BUFSIZE - 1);
++
++      n = dev->head - dev->tail;
++      if(n <= 0) n = LINE_BUFSIZE + n;
++      return(n - 1);
++}
++
++static int buffer_data(struct line *line, const char *buf, int len)
++{
++      int end, room;
++
++      if(line->buffer == NULL){
++              line->buffer = kmalloc(LINE_BUFSIZE, GFP_ATOMIC);
++              if(line->buffer == NULL){
++                      printk("buffer_data - atomic allocation failed\n");
++                      return(0);
++              }
++              line->head = line->buffer;
++              line->tail = line->buffer;
++      }
++
++      room = write_room(line);
++      len = (len > room) ? room : len;
++
++      end = line->buffer + LINE_BUFSIZE - line->tail;
++      if(len < end){
++              memcpy(line->tail, buf, len);
++              line->tail += len;
++      }
++      else {
++              memcpy(line->tail, buf, end);
++              buf += end;
++              len -= end;
++              memcpy(line->buffer, buf, len);
++              line->tail = line->buffer + len;
++      }
++
++      return(len);
++}
++
++static int flush_buffer(struct line *line)
++{
++      int n, count;
++
++      if((line->buffer == NULL) || (line->head == line->tail)) return(1);
++
++      if(line->tail < line->head){
++              count = line->buffer + LINE_BUFSIZE - line->head;
++              n = write_chan(&line->chan_list, line->head, count,
++                             line->driver->write_irq);
++              if(n < 0) return(n);
++              if(n == count) line->head = line->buffer;
++              else {
++                      line->head += n;
++                      return(0);
++              }
++      }
++
++      count = line->tail - line->head;
++      n = write_chan(&line->chan_list, line->head, count, 
++                     line->driver->write_irq);
++      if(n < 0) return(n);
++
++      line->head += n;
++      return(line->head == line->tail);
++}
++
++int line_write(struct line *lines, struct tty_struct *tty, int from_user,
++             const char *buf, int len)
++{
++      struct line *line;
++      char *new;
++      unsigned long flags;
++      int n, err, i, ret = 0;
++
++      if(tty->stopped) return 0;
++
++      if(from_user){
++              new = kmalloc(len, GFP_KERNEL);
++              if(new == NULL)
++                      return(0);
++              n = copy_from_user(new, buf, len);
++              buf = new;
++              if(n == len){
++                      len = -EFAULT;
++                      goto out_free;
++              }
++
++              len -= n;
++      }
++
++      i = minor(tty->device) - tty->driver.minor_start;
++      line = &lines[i];
++
++      down(&line->sem);
++      if(line->head != line->tail){
++              local_irq_save(flags);
++              ret += buffer_data(line, buf, len);
++              err = flush_buffer(line);
++              local_irq_restore(flags);
++              if(err <= 0)
++                      goto out_up;
++      }
++      else {
++              n = write_chan(&line->chan_list, buf, len, 
++                             line->driver->write_irq);
++              if(n < 0){
++                      ret = n;
++                      goto out_up;
++              }
++
++              len -= n;
++              ret += n;
++              if(len > 0)
++                      ret += buffer_data(line, buf + n, len);
++      }
++ out_up:
++      up(&line->sem);
++
++ out_free:
++      if(from_user)
++              kfree(buf);
++      return(ret);
++}
++
++static void line_write_interrupt(int irq, void *data, struct pt_regs *unused)
++{
++      struct line *dev = data;
++      struct tty_struct *tty = dev->tty;
++      int err;
++
++      err = flush_buffer(dev);
++      if(err == 0) return;
++      else if(err < 0){
++              dev->head = dev->buffer;
++              dev->tail = dev->buffer;
++      }
++
++      if(tty == NULL) return;
++
++      if(test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) &&
++         (tty->ldisc.write_wakeup != NULL))
++              (tty->ldisc.write_wakeup)(tty);
++      
++      /* BLOCKING mode
++       * In blocking mode, everything sleeps on tty->write_wait.
++       * Sleeping in the console driver would break non-blocking
++       * writes.
++       */
++
++      if (waitqueue_active(&tty->write_wait))
++              wake_up_interruptible(&tty->write_wait);
++
++}
++
++int line_setup_irq(int fd, int input, int output, void *data)
++{
++      struct line *line = data;
++      struct line_driver *driver = line->driver;
++      int err = 0, flags = SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM;
++
++      if(input) err = um_request_irq(driver->read_irq, fd, IRQ_READ, 
++                                     line_interrupt, flags, 
++                                     driver->read_irq_name, line);
++      if(err) return(err);
++      if(output) err = um_request_irq(driver->write_irq, fd, IRQ_WRITE, 
++                                      line_write_interrupt, flags, 
++                                      driver->write_irq_name, line);
++      line->have_irq = 1;
++      return(err);
++}
++
++void line_disable(struct line *line, int current_irq)
++{
++      if(!line->have_irq) return;
++
++      if(line->driver->read_irq == current_irq)
++              free_irq_later(line->driver->read_irq, line);
++      else
++              free_irq(line->driver->read_irq, line);
++
++      if(line->driver->write_irq == current_irq)
++              free_irq_later(line->driver->write_irq, line);
++      else
++              free_irq(line->driver->write_irq, line);
++
++      line->have_irq = 0;
++}
++
++int line_open(struct line *lines, struct tty_struct *tty,
++            struct chan_opts *opts)
++{
++      struct line *line;
++      int n, err = 0;
++
++      if(tty == NULL) n = 0;
++      else n = minor(tty->device) - tty->driver.minor_start;
++      line = &lines[n];
++
++      down(&line->sem);
++      if(line->count == 0){
++              if(!line->valid){
++                      err = -ENODEV;
++                      goto out;
++              }
++              if(list_empty(&line->chan_list)){
++                      err = parse_chan_pair(line->init_str, &line->chan_list,
++                                            line->init_pri, n, opts);
++                      if(err) goto out;
++                      err = open_chan(&line->chan_list);
++                      if(err) goto out;
++              }
++              enable_chan(&line->chan_list, line);
++              INIT_TQUEUE(&line->task, line_timer_cb, line);
++      }
++
++      if(!line->sigio){
++              chan_enable_winch(&line->chan_list, line);
++              line->sigio = 1;
++      }
++
++      /* This is outside the if because the initial console is opened
++       * with tty == NULL
++       */
++      line->tty = tty;
++
++      if(tty != NULL){
++              tty->driver_data = line;
++              chan_window_size(&line->chan_list, &tty->winsize.ws_row, 
++                               &tty->winsize.ws_col);
++      }
++
++      line->count++;
++ out:
++      up(&line->sem);
++      return(err);
++}
++
++void line_close(struct line *lines, struct tty_struct *tty)
++{
++      struct line *line;
++      int n;
++
++      if(tty == NULL) n = 0;
++      else n = minor(tty->device) - tty->driver.minor_start;
++      line = &lines[n];
++
++      down(&line->sem);
++      line->count--;
++
++      /* I don't like this, but I can't think of anything better.  What's
++       * going on is that the tty is in the process of being closed for
++       * the last time.  Its count hasn't been dropped yet, so it's still
++       * at 1.  This may happen when line->count != 0 because of the initial
++       * console open (without a tty) bumping it up to 1.
++       */
++      if((line->tty != NULL) && (line->tty->count == 1))
++              line->tty = NULL;
++      if(line->count == 0)
++              line_disable(line, -1);
++      up(&line->sem);
++}
++
++void close_lines(struct line *lines, int nlines)
++{
++      int i;
++
++      for(i = 0; i < nlines; i++)
++              close_chan(&lines[i].chan_list);
++}
++
++int line_setup(struct line *lines, int num, char *init, int all_allowed)
++{
++      int i, n;
++      char *end;
++
++      if(*init == '=') n = -1;
++      else {
++              n = simple_strtoul(init, &end, 0);
++              if(*end != '='){
++                      printk(KERN_ERR "line_setup failed to parse \"%s\"\n", 
++                             init);
++                      return(0);
++              }
++              init = end;
++      }
++      init++;
++      if((n >= 0) && (n >= num)){
++              printk("line_setup - %d out of range ((0 ... %d) allowed)\n",
++                     n, num);
++              return(0);
++      }
++      else if(n >= 0){
++              if(lines[n].count > 0){
++                      printk("line_setup - device %d is open\n", n);
++                      return(0);
++              }
++              if(lines[n].init_pri <= INIT_ONE){
++                      lines[n].init_pri = INIT_ONE;
++                      if(!strcmp(init, "none")) lines[n].valid = 0;
++                      else {
++                              lines[n].init_str = init;
++                              lines[n].valid = 1;
++                      }       
++              }
++      }
++      else if(!all_allowed){
++              printk("line_setup - can't configure all devices from "
++                     "mconsole\n");
++              return(0);
++      }
++      else {
++              for(i = 0; i < num; i++){
++                      if(lines[i].init_pri <= INIT_ALL){
++                              lines[i].init_pri = INIT_ALL;
++                              if(!strcmp(init, "none")) lines[i].valid = 0;
++                              else {
++                                      lines[i].init_str = init;
++                                      lines[i].valid = 1;
++                              }
++                      }
++              }
++      }
++      return(1);
++}
++
++int line_config(struct line *lines, int num, char *str)
++{
++      char *new = uml_strdup(str);
++
++      if(new == NULL){
++              printk("line_config - uml_strdup failed\n");
++              return(-ENOMEM);
++      }
++      return(!line_setup(lines, num, new, 0));
++}
++
++int line_get_config(char *name, struct line *lines, int num, char *str, 
++                  int size, char **error_out)
++{
++      struct line *line;
++      char *end;
++      int dev, n = 0;
++
++      dev = simple_strtoul(name, &end, 0);
++      if((*end != '\0') || (end == name)){
++              *error_out = "line_get_config failed to parse device number";
++              return(0);
++      }
++
++      if((dev < 0) || (dev >= num)){
++              *error_out = "device number of of range";
++              return(0);
++      }
++
++      line = &lines[dev];
++
++      down(&line->sem);
++      if(!line->valid)
++              CONFIG_CHUNK(str, size, n, "none", 1);
++      else if(line->count == 0)
++              CONFIG_CHUNK(str, size, n, line->init_str, 1);
++      else n = chan_config_string(&line->chan_list, str, size, error_out);
++      up(&line->sem);
++
++      return(n);
++}
++
++int line_remove(struct line *lines, int num, char *str)
++{
++      char config[sizeof("conxxxx=none\0")];
++
++      sprintf(config, "%s=none", str);
++      return(!line_setup(lines, num, config, 0));
++}
++
++static int line_write_room(struct tty_struct *tty)
++{
++      struct line *dev = tty->driver_data;
++
++      return(write_room(dev));
++}
++
++void line_register_devfs(struct lines *set, struct line_driver *line_driver, 
++                       struct tty_driver *driver, struct line *lines,
++                       int nlines)
++{
++      int err, i, n;
++      char *from, *to;
++
++      driver->driver_name = line_driver->name;
++      driver->name = line_driver->devfs_name;
++      driver->major = line_driver->major;
++      driver->minor_start = line_driver->minor_start;
++      driver->type = line_driver->type;
++      driver->subtype = line_driver->subtype;
++      driver->magic = TTY_DRIVER_MAGIC;
++      driver->flags = TTY_DRIVER_REAL_RAW;
++
++      n = set->num;
++      driver->num = n;
++      driver->table = kmalloc(n * sizeof(driver->table[0]), GFP_KERNEL);
++      driver->termios = kmalloc(n * sizeof(driver->termios[0]), GFP_KERNEL);
++      driver->termios_locked = kmalloc(n * sizeof(driver->termios_locked[0]),
++                                       GFP_KERNEL);
++      if((driver->table == NULL) || (driver->termios == NULL) ||
++         (driver->termios_locked == NULL))
++              panic("Failed to allocate driver table");
++
++      memset(driver->table, 0, n * sizeof(driver->table[0]));
++      memset(driver->termios, 0, n * sizeof(driver->termios[0]));
++      memset(driver->termios_locked, 0, 
++             n * sizeof(driver->termios_locked[0]));
++
++      driver->write_room = line_write_room;
++      driver->init_termios = tty_std_termios;
++
++      if (tty_register_driver(driver))
++              panic("line_register_devfs : Couldn't register driver\n");
++
++      from = line_driver->symlink_from;
++      to = line_driver->symlink_to;
++      err = devfs_mk_symlink(NULL, from, 0, to, NULL, NULL);
++      if(err) printk("Symlink creation from /dev/%s to /dev/%s "
++                     "returned %d\n", from, to, err);
++
++      for(i = 0; i < nlines; i++){
++              if(!lines[i].valid) 
++                      tty_unregister_devfs(driver, driver->minor_start + i);
++      }
++
++      mconsole_register_dev(&line_driver->mc);
++}
++
++void lines_init(struct line *lines, int nlines)
++{
++      struct line *line;
++      int i;
++
++      for(i = 0; i < nlines; i++){
++              line = &lines[i];
++              INIT_LIST_HEAD(&line->chan_list);
++              sema_init(&line->sem, 1);
++              if(line->init_str != NULL){
++                      line->init_str = uml_strdup(line->init_str);
++                      if(line->init_str == NULL)
++                              printk("lines_init - uml_strdup returned "
++                                     "NULL\n");
++              }
++      }
++}
++
++struct winch {
++      struct list_head list;
++      int fd;
++      int tty_fd;
++      int pid;
++      struct line *line;
++};
++
++void winch_interrupt(int irq, void *data, struct pt_regs *unused)
++{
++      struct winch *winch = data;
++      struct tty_struct *tty;
++      int err;
++      char c;
++
++      if(winch->fd != -1){
++              err = generic_read(winch->fd, &c, NULL);
++              if(err < 0){
++                      if(err != -EAGAIN){
++                              printk("winch_interrupt : read failed, "
++                                     "errno = %d\n", -err);
++                              printk("fd %d is losing SIGWINCH support\n", 
++                                     winch->tty_fd);
++                              return;
++                      }
++                      goto out;
++              }
++      }
++      tty = winch->line->tty;
++      if(tty != NULL){
++              chan_window_size(&winch->line->chan_list, 
++                               &tty->winsize.ws_row, 
++                               &tty->winsize.ws_col);
++              kill_pg(tty->pgrp, SIGWINCH, 1);
++      }
++ out:
++      if(winch->fd != -1)
++              reactivate_fd(winch->fd, WINCH_IRQ);
++}
++
++DECLARE_MUTEX(winch_handler_sem);
++LIST_HEAD(winch_handlers);
++
++void register_winch_irq(int fd, int tty_fd, int pid, void *line)
++{
++      struct winch *winch;
++
++      down(&winch_handler_sem);
++      winch = kmalloc(sizeof(*winch), GFP_KERNEL);
++      if(winch == NULL){
++              printk("register_winch_irq - kmalloc failed\n");
++              goto out;
++      }
++      *winch = ((struct winch) { .list        = LIST_HEAD_INIT(winch->list),
++                                 .fd          = fd,
++                                 .tty_fd      = tty_fd,
++                                 .pid         = pid,
++                                 .line        = line });
++      list_add(&winch->list, &winch_handlers);
++      if(um_request_irq(WINCH_IRQ, fd, IRQ_READ, winch_interrupt, 
++                        SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM, 
++                        "winch", winch) < 0)
++              printk("register_winch_irq - failed to register IRQ\n");
++ out:
++      up(&winch_handler_sem);
++}
++
++static void winch_cleanup(void)
++{
++      struct list_head *ele;
++      struct winch *winch;
++
++      list_for_each(ele, &winch_handlers){
++              winch = list_entry(ele, struct winch, list);
++              if(winch->fd != -1){
++                      deactivate_fd(winch->fd, WINCH_IRQ);
++                      os_close_file(winch->fd);
++              }
++              if(winch->pid != -1) 
++                      os_kill_process(winch->pid, 1);
++      }
++}
++
++__uml_exitcall(winch_cleanup);
++
++char *add_xterm_umid(char *base)
++{
++      char *umid, *title;
++      int len;
++
++      umid = get_umid(1);
++      if(umid == NULL) return(base);
++      
++      len = strlen(base) + strlen(" ()") + strlen(umid) + 1;
++      title = kmalloc(len, GFP_KERNEL);
++      if(title == NULL){
++              printk("Failed to allocate buffer for xterm title\n");
++              return(base);
++      }
++
++      strncpy(title, base, len);
++      len -= strlen(title);
++      snprintf(&title[strlen(title)], len, " (%s)", umid);
++      return(title);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/Makefile
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/Makefile 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/Makefile      2005-05-03 22:28:14.215449136 +0300
+@@ -0,0 +1,97 @@
++# 
++# Copyright (C) 2000, 2002, 2003 Jeff Dike (jdike@karaya.com)
++# Licensed under the GPL
++#
++
++O_TARGET := built-in.o 
++
++CHAN_OBJS := chan_kern.o chan_user.o line.o 
++
++list-multi := slip.o slirp.o daemon.o mcast.o mconsole.o net.o ubd.o \
++      hostaudio.o pcap.o port.o harddog.o
++
++slip-objs := slip_kern.o slip_user.o
++slirp-objs := slirp_kern.o slirp_user.o
++daemon-objs := daemon_kern.o daemon_user.o
++mcast-objs := mcast_kern.o mcast_user.o
++pcap-objs := pcap_kern.o pcap_user.o
++pcap-libs := -lpcap -L/usr/lib
++net-objs := net_kern.o net_user.o
++mconsole-objs := mconsole_kern.o mconsole_user.o
++hostaudio-objs := hostaudio_kern.o
++ubd-objs := ubd_kern.o ubd_user.o
++port-objs := port_kern.o port_user.o
++harddog-objs := harddog_kern.o harddog_user.o
++
++export-objs := mconsole_kern.o
++
++obj-y = 
++obj-$(CONFIG_SSL) += ssl.o 
++obj-$(CONFIG_UML_NET_SLIP) += slip.o
++obj-$(CONFIG_UML_NET_SLIRP) += slirp.o
++obj-$(CONFIG_UML_NET_DAEMON) += daemon.o 
++obj-$(CONFIG_UML_NET_MCAST) += mcast.o 
++obj-$(CONFIG_UML_NET_PCAP) += pcap.o 
++obj-$(CONFIG_UML_NET) += net.o 
++obj-$(CONFIG_MCONSOLE) += mconsole.o
++obj-$(CONFIG_MMAPPER) += mmapper_kern.o 
++obj-$(CONFIG_BLK_DEV_UBD) += ubd.o 
++obj-$(CONFIG_HOSTAUDIO) += hostaudio.o
++obj-$(CONFIG_FD_CHAN) += fd.o 
++obj-$(CONFIG_NULL_CHAN) += null.o 
++obj-$(CONFIG_PORT_CHAN) += port.o
++obj-$(CONFIG_PTY_CHAN) += pty.o
++obj-$(CONFIG_TTY_CHAN) += tty.o 
++obj-$(CONFIG_XTERM_CHAN) += xterm.o xterm_kern.o
++obj-$(CONFIG_UML_WATCHDOG) += harddog.o
++obj-$(CONFIG_COW) += cow_kern.o
++obj-$(CONFIG_COW_COMMON) += cow_user.o
++
++CFLAGS_pcap_user.o = -I/usr/include/pcap
++
++obj-y += stdio_console.o $(CHAN_OBJS)
++
++USER_SINGLE_OBJS = $(foreach f,$(patsubst %.o,%,$(obj-y) $(obj-m)),$($(f)-objs))
++
++USER_OBJS = $(filter %_user.o,$(obj-y) $(obj-m) $(USER_SINGLE_OBJS)) fd.o \
++      null.o pty.o tty.o xterm.o
++
++include $(TOPDIR)/Rules.make
++
++$(USER_OBJS) : %.o: %.c
++      $(CC) $(CFLAGS_$@) $(USER_CFLAGS) -c -o $@ $<
++
++clean:
++
++modules:
++
++fastdep:
++
++dep:
++
++archmrproper:
++
++daemon.o : $(daemon-objs)
++
++slip.o : $(slip-objs)
++
++slirp.o : $(slirp-objs)
++
++mcast.o : $(mcast-objs)
++
++pcap.o : $(pcap-objs)
++
++mconsole.o : $(mconsole-objs)
++
++net.o : $(net-objs)
++
++hostaudio.o : $(hostaudio-objs)
++
++ubd.o : $(ubd-objs)
++
++port.o : $(port-objs)
++
++harddog.o : $(harddog-objs)
++
++$(list-multi) : # This doesn't work, but should : '%.o : $(%-objs)'
++      $(LD) -r -o $@ $($(patsubst %.o,%,$@)-objs) $($(patsubst %.o,%,$@)-libs)
+Index: linux-2.4.29/arch/um/drivers/mcast.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/mcast.h  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/mcast.h       2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,30 @@
++/* 
++ * Copyright (C) 2001 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "net_user.h"
++
++struct mcast_data {
++      char *addr;
++      unsigned short port;
++      void *mcast_addr;
++      int ttl;
++      void *dev;
++};
++
++extern struct net_user_info mcast_user_info;
++
++extern int mcast_user_write(int fd, void *buf, int len, 
++                          struct mcast_data *pri);
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/mcast_kern.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/mcast_kern.c     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/mcast_kern.c  2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,145 @@
++/*
++ * user-mode-linux networking multicast transport
++ * Copyright (C) 2001 by Harald Welte <laforge@gnumonks.org>
++ *
++ * based on the existing uml-networking code, which is
++ * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and 
++ * James Leu (jleu@mindspring.net).
++ * Copyright (C) 2001 by various other people who didn't put their name here.
++ *
++ * Licensed under the GPL.
++ */
++
++#include "linux/kernel.h"
++#include "linux/init.h"
++#include "linux/netdevice.h"
++#include "linux/etherdevice.h"
++#include "linux/in.h"
++#include "linux/inet.h"
++#include "net_kern.h"
++#include "net_user.h"
++#include "mcast.h"
++
++struct mcast_init {
++      char *addr;
++      int port;
++      int ttl;
++};
++
++void mcast_init(struct net_device *dev, void *data)
++{
++      struct uml_net_private *pri;
++      struct mcast_data *dpri;
++      struct mcast_init *init = data;
++
++      init_etherdev(dev, 0);
++      pri = dev->priv;
++      dpri = (struct mcast_data *) pri->user;
++      *dpri = ((struct mcast_data)
++              { .addr         = init->addr,
++                .port         = init->port,
++                .ttl          = init->ttl,
++                .mcast_addr   = NULL,
++                .dev          = dev });
++      printk("mcast backend ");
++      printk("multicast adddress: %s:%u, TTL:%u ",
++             dpri->addr, dpri->port, dpri->ttl);
++
++      printk("\n");
++}
++
++static int mcast_read(int fd, struct sk_buff **skb, struct uml_net_private *lp)
++{
++      *skb = ether_adjust_skb(*skb, ETH_HEADER_OTHER);
++      if(*skb == NULL) return(-ENOMEM);
++      return(net_recvfrom(fd, (*skb)->mac.raw, 
++                          (*skb)->dev->mtu + ETH_HEADER_OTHER));
++}
++
++static int mcast_write(int fd, struct sk_buff **skb,
++                      struct uml_net_private *lp)
++{
++      return mcast_user_write(fd, (*skb)->data, (*skb)->len, 
++                               (struct mcast_data *) &lp->user);
++}
++
++static struct net_kern_info mcast_kern_info = {
++      .init                   = mcast_init,
++      .protocol               = eth_protocol,
++      .read                   = mcast_read,
++      .write                  = mcast_write,
++};
++
++int mcast_setup(char *str, char **mac_out, void *data)
++{
++      struct mcast_init *init = data;
++      char *port_str = NULL, *ttl_str = NULL, *remain;
++      char *last;
++      int n;
++
++      *init = ((struct mcast_init)
++              { .addr         = "239.192.168.1",
++                .port         = 1102,
++                .ttl          = 1 });
++
++      remain = split_if_spec(str, mac_out, &init->addr, &port_str, &ttl_str,
++                             NULL);
++      if(remain != NULL){
++              printk(KERN_ERR "mcast_setup - Extra garbage on "
++                     "specification : '%s'\n", remain);
++              return(0);
++      }
++      
++      if(port_str != NULL){
++              n = simple_strtoul(port_str, &last, 10);
++              if((*last != '\0') || (last == port_str)){
++                      printk(KERN_ERR "mcast_setup - Bad port : '%s'\n", 
++                             port_str);
++                      return(0);
++              }
++              init->port = htons(n);
++      }
++
++      if(ttl_str != NULL){
++              init->ttl = simple_strtoul(ttl_str, &last, 10);
++              if((*last != '\0') || (last == ttl_str)){
++                      printk(KERN_ERR "mcast_setup - Bad ttl : '%s'\n", 
++                             ttl_str);
++                      return(0);
++              }
++      }
++
++      printk(KERN_INFO "Configured mcast device: %s:%u-%u\n", init->addr,
++             init->port, init->ttl);
++
++      return(1);
++}
++
++static struct transport mcast_transport = {
++      .list           = LIST_HEAD_INIT(mcast_transport.list),
++      .name           = "mcast",
++      .setup          = mcast_setup,
++      .user           = &mcast_user_info,
++      .kern           = &mcast_kern_info,
++      .private_size   = sizeof(struct mcast_data),
++      .setup_size     = sizeof(struct mcast_init),
++};
++
++static int register_mcast(void)
++{
++      register_transport(&mcast_transport);
++      return(1);
++}
++
++__initcall(register_mcast);
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/mcast_user.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/mcast_user.c     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/mcast_user.c  2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,177 @@
++/*
++ * user-mode-linux networking multicast transport
++ * Copyright (C) 2001 by Harald Welte <laforge@gnumonks.org>
++ *
++ * based on the existing uml-networking code, which is
++ * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and 
++ * James Leu (jleu@mindspring.net).
++ * Copyright (C) 2001 by various other people who didn't put their name here.
++ *
++ * Licensed under the GPL.
++ *
++ */
++
++#include <errno.h>
++#include <unistd.h>
++#include <linux/inet.h>
++#include <sys/socket.h>
++#include <sys/un.h>
++#include <sys/time.h>
++#include <netinet/in.h>
++#include "net_user.h"
++#include "mcast.h"
++#include "kern_util.h"
++#include "user_util.h"
++#include "user.h"
++#include "os.h"
++
++#define MAX_PACKET (ETH_MAX_PACKET + ETH_HEADER_OTHER)
++
++static struct sockaddr_in *new_addr(char *addr, unsigned short port)
++{
++      struct sockaddr_in *sin;
++
++      sin = um_kmalloc(sizeof(struct sockaddr_in));
++      if(sin == NULL){
++              printk("new_addr: allocation of sockaddr_in failed\n");
++              return(NULL);
++      }
++      sin->sin_family = AF_INET;
++      sin->sin_addr.s_addr = in_aton(addr);
++      sin->sin_port = port;
++      return(sin);
++}
++
++static void mcast_user_init(void *data, void *dev)
++{
++      struct mcast_data *pri = data;
++
++      pri->mcast_addr = new_addr(pri->addr, pri->port);
++      pri->dev = dev;
++}
++
++static int mcast_open(void *data)
++{
++      struct mcast_data *pri = data;
++      struct sockaddr_in *sin = pri->mcast_addr;
++      struct ip_mreq mreq;
++      int fd, yes = 1;
++
++
++      if ((sin->sin_addr.s_addr == 0) || (sin->sin_port == 0)) {
++              fd = -EINVAL;
++              goto out;
++      }
++
++      fd = socket(AF_INET, SOCK_DGRAM, 0);
++      if (fd < 0){
++              printk("mcast_open : data socket failed, errno = %d\n", 
++                     errno);
++              fd = -ENOMEM;
++              goto out;
++      }
++
++      if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
++              printk("mcast_open: SO_REUSEADDR failed, errno = %d\n",
++                      errno);
++              os_close_file(fd);
++              fd = -EINVAL;
++              goto out;
++      }
++
++      /* set ttl according to config */
++      if (setsockopt(fd, SOL_IP, IP_MULTICAST_TTL, &pri->ttl,
++                     sizeof(pri->ttl)) < 0) {
++              printk("mcast_open: IP_MULTICAST_TTL failed, error = %d\n",
++                      errno);
++              os_close_file(fd);
++              fd = -EINVAL;
++              goto out;
++      }
++
++      /* set LOOP, so data does get fed back to local sockets */
++      if (setsockopt(fd, SOL_IP, IP_MULTICAST_LOOP, &yes, sizeof(yes)) < 0) {
++              printk("mcast_open: IP_MULTICAST_LOOP failed, error = %d\n",
++                      errno);
++              os_close_file(fd);
++              fd = -EINVAL;
++              goto out;
++      }
++
++      /* bind socket to mcast address */
++      if (bind(fd, (struct sockaddr *) sin, sizeof(*sin)) < 0) {
++              printk("mcast_open : data bind failed, errno = %d\n", errno);
++              os_close_file(fd);
++              fd = -EINVAL;
++              goto out;
++      }               
++      
++      /* subscribe to the multicast group */
++      mreq.imr_multiaddr.s_addr = sin->sin_addr.s_addr;
++      mreq.imr_interface.s_addr = 0;
++      if (setsockopt(fd, SOL_IP, IP_ADD_MEMBERSHIP, 
++                     &mreq, sizeof(mreq)) < 0) {
++              printk("mcast_open: IP_ADD_MEMBERSHIP failed, error = %d\n",
++                      errno);
++              printk("There appears not to be a multicast-capable network "
++                     "interface on the host.\n");
++              printk("eth0 should be configured in order to use the "
++                     "multicast transport.\n");
++              os_close_file(fd);
++              fd = -EINVAL;
++      }
++
++ out:
++      return(fd);
++}
++
++static void mcast_close(int fd, void *data)
++{
++      struct ip_mreq mreq;
++      struct mcast_data *pri = data;
++      struct sockaddr_in *sin = pri->mcast_addr;
++
++      mreq.imr_multiaddr.s_addr = sin->sin_addr.s_addr;
++      mreq.imr_interface.s_addr = 0;
++      if (setsockopt(fd, SOL_IP, IP_DROP_MEMBERSHIP,
++                     &mreq, sizeof(mreq)) < 0) {
++              printk("mcast_open: IP_DROP_MEMBERSHIP failed, error = %d\n",
++                      errno);
++      }
++
++      os_close_file(fd);
++}
++
++int mcast_user_write(int fd, void *buf, int len, struct mcast_data *pri)
++{
++      struct sockaddr_in *data_addr = pri->mcast_addr;
++
++      return(net_sendto(fd, buf, len, data_addr, sizeof(*data_addr)));
++}
++
++static int mcast_set_mtu(int mtu, void *data)
++{
++      return(mtu);
++}
++
++struct net_user_info mcast_user_info = {
++      .init           = mcast_user_init,
++      .open           = mcast_open,
++      .close          = mcast_close,
++      .remove         = NULL,
++      .set_mtu        = mcast_set_mtu,
++      .add_address    = NULL,
++      .delete_address = NULL,
++      .max_packet     = MAX_PACKET - ETH_HEADER_OTHER
++};
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/mconsole_kern.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/mconsole_kern.c  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/mconsole_kern.c       2005-05-03 22:28:14.222448072 +0300
+@@ -0,0 +1,560 @@
++/*
++ * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org)
++ * Copyright (C) 2001 - 2003 Jeff Dike (jdike@addtoit.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/kernel.h"
++#include "linux/slab.h"
++#include "linux/init.h"
++#include "linux/notifier.h"
++#include "linux/reboot.h"
++#include "linux/utsname.h"
++#include "linux/ctype.h"
++#include "linux/interrupt.h"
++#include "linux/sysrq.h"
++#include "linux/tqueue.h"
++#include "linux/module.h"
++#include "linux/file.h"
++#include "linux/fs.h"
++#include "linux/proc_fs.h"
++#include "asm/irq.h"
++#include "asm/uaccess.h"
++#include "user_util.h"
++#include "kern_util.h"
++#include "kern.h"
++#include "mconsole.h"
++#include "mconsole_kern.h"
++#include "irq_user.h"
++#include "init.h"
++#include "os.h"
++#include "umid.h"
++#include "irq_kern.h"
++
++static int do_unlink_socket(struct notifier_block *notifier, 
++                          unsigned long what, void *data)
++{
++      return(mconsole_unlink_socket());
++}
++
++
++static struct notifier_block reboot_notifier = {
++      .notifier_call          = do_unlink_socket,
++      .priority               = 0,
++};
++
++/* Safe without explicit locking for now.  Tasklets provide their own 
++ * locking, and the interrupt handler is safe because it can't interrupt
++ * itself and it can only happen on CPU 0.
++ */
++
++LIST_HEAD(mc_requests);
++
++static void mc_task_proc(void *unused)
++{
++      struct mconsole_entry *req;
++      unsigned long flags;
++
++      while(!list_empty(&mc_requests)){
++              local_irq_save(flags);
++              req = list_entry(mc_requests.next, struct mconsole_entry, 
++                               list);
++              list_del(&req->list);
++              local_irq_restore(flags);
++              req->request.cmd->handler(&req->request);
++              kfree(req);
++      }
++}
++
++struct tq_struct mconsole_task = {
++      .routine        = mc_task_proc,
++      .data           = NULL
++};
++
++static void mconsole_interrupt(int irq, void *dev_id, struct pt_regs *regs)
++{
++      int fd;
++      struct mconsole_entry *new;
++      struct mc_request req;
++
++      fd = (int) dev_id;
++      while (mconsole_get_request(fd, &req)){
++              if(req.cmd->context == MCONSOLE_INTR) 
++                      (*req.cmd->handler)(&req);
++              else {
++                      new = kmalloc(sizeof(*new), GFP_ATOMIC);
++                      if(new == NULL)
++                              mconsole_reply(&req, "Out of memory", 1, 0);
++                      else {
++                              new->request = req;
++                              list_add(&new->list, &mc_requests);
++                      }
++              }
++      }
++
++      if(!list_empty(&mc_requests))
++              schedule_task(&mconsole_task);
++      reactivate_fd(fd, MCONSOLE_IRQ);
++}
++
++void mconsole_version(struct mc_request *req)
++{
++      char version[256];
++
++      sprintf(version, "%s %s %s %s %s", system_utsname.sysname, 
++              system_utsname.nodename, system_utsname.release, 
++              system_utsname.version, system_utsname.machine);
++      mconsole_reply(req, version, 0, 0);
++}
++
++void mconsole_log(struct mc_request *req)
++{
++      int len;
++      char *ptr = req->request.data;
++      
++      ptr += strlen("log ");
++
++      len = req->len - (ptr - req->request.data);
++      printk("%.*s", len, ptr);
++      mconsole_reply(req, "", 0, 0);
++}
++
++void mconsole_proc(struct mc_request *req)
++{
++      struct nameidata nd;
++      struct file_system_type *proc;
++      struct super_block *super;
++      struct file *file;
++      int n, err;
++      char *ptr = req->request.data, *buf;
++      
++      ptr += strlen("proc");
++      while(isspace(*ptr)) ptr++;
++
++      proc = get_fs_type("proc");
++      if(proc == NULL){
++              mconsole_reply(req, "procfs not registered", 1, 0);
++              goto out;
++      }
++
++      super = get_anon_super(proc, NULL, NULL);
++      if(super == NULL){
++              mconsole_reply(req, "Failed to get procfs superblock", 1, 0);
++              goto out_put;
++      }
++
++      if(super->s_root == NULL){
++              super = (*proc->read_super)(super, NULL, 0);
++              if(super == NULL){
++                      mconsole_reply(req, "Failed to read superblock", 1, 0);
++                      goto out_put;
++              }
++      }
++      up_write(&super->s_umount);
++
++      nd.dentry = super->s_root;
++      nd.mnt = NULL;
++      nd.flags = O_RDONLY + 1;
++      nd.last_type = LAST_ROOT;
++
++      err = link_path_walk(ptr, &nd);
++      if(err){
++              mconsole_reply(req, "Failed to look up file", 1, 0);
++              goto out_kill;
++      }
++
++      file = dentry_open(nd.dentry, nd.mnt, O_RDONLY);
++      if(IS_ERR(file)){
++              mconsole_reply(req, "Failed to open file", 1, 0);
++              goto out_kill;
++      }
++
++      buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
++      if(buf == NULL){
++              mconsole_reply(req, "Failed to allocate buffer", 1, 0);
++              goto out_fput;
++      }
++
++      if((file->f_op != NULL) && (file->f_op->read != NULL)){
++              do {
++                      n = (*file->f_op->read)(file, buf, PAGE_SIZE - 1, 
++                                              &file->f_pos);
++                      if(n >= 0){
++                              buf[n] = '\0';
++                              mconsole_reply(req, buf, 0, (n > 0));
++                      }
++                      else {
++                              mconsole_reply(req, "Read of file failed", 
++                                             1, 0);
++                              goto out_free;
++                      }
++              } while(n > 0);
++      }
++      else mconsole_reply(req, "", 0, 0);
++
++ out_free:
++      kfree(buf);
++ out_fput:
++      fput(file);
++ out_kill:
++      kill_super(super);
++ out_put:
++      /* put_filesystem(proc); */
++ out: ;
++}
++
++#define UML_MCONSOLE_HELPTEXT \
++"Commands: \n\
++    version - Get kernel version \n\
++    help - Print this message \n\
++    halt - Halt UML \n\
++    reboot - Reboot UML \n\
++    config <dev>=<config> - Add a new device to UML;  \n\
++      same syntax as command line \n\
++    config <dev> - Query the configuration of a device \n\
++    remove <dev> - Remove a device from UML \n\
++    sysrq <letter> - Performs the SysRq action controlled by the letter \n\
++    cad - invoke the Ctl-Alt-Del handler \n\
++    stop - pause the UML; it will do nothing until it receives a 'go' \n\
++    go - continue the UML after a 'stop' \n\
++    log <string> - make UML enter <string> into the kernel log\n\
++    proc <file> - returns the contents of the UML's /proc/<file>\n\
++"
++
++void mconsole_help(struct mc_request *req)
++{
++      mconsole_reply(req, UML_MCONSOLE_HELPTEXT, 0, 0);
++}
++
++void mconsole_halt(struct mc_request *req)
++{
++      mconsole_reply(req, "", 0, 0);
++      machine_halt();
++}
++
++void mconsole_reboot(struct mc_request *req)
++{
++      mconsole_reply(req, "", 0, 0);
++      machine_restart(NULL);
++}
++
++extern void ctrl_alt_del(void);
++
++void mconsole_cad(struct mc_request *req)
++{
++      mconsole_reply(req, "", 0, 0);
++      ctrl_alt_del();
++}
++
++void mconsole_go(struct mc_request *req)
++{
++      mconsole_reply(req, "Not stopped", 1, 0);
++}
++
++void mconsole_stop(struct mc_request *req)
++{
++      deactivate_fd(req->originating_fd, MCONSOLE_IRQ);
++      os_set_fd_block(req->originating_fd, 1);
++      mconsole_reply(req, "", 0, 0);
++      while(mconsole_get_request(req->originating_fd, req)){
++              if(req->cmd->handler == mconsole_go) break;
++              (*req->cmd->handler)(req);
++      }
++      os_set_fd_block(req->originating_fd, 0);
++      reactivate_fd(req->originating_fd, MCONSOLE_IRQ);
++      mconsole_reply(req, "", 0, 0);
++}
++
++/* This list is populated by __initcall routines. */
++
++LIST_HEAD(mconsole_devices);
++
++void mconsole_register_dev(struct mc_device *new)
++{
++      list_add(&new->list, &mconsole_devices);
++}
++
++static struct mc_device *mconsole_find_dev(char *name)
++{
++      struct list_head *ele;
++      struct mc_device *dev;
++
++      list_for_each(ele, &mconsole_devices){
++              dev = list_entry(ele, struct mc_device, list);
++              if(!strncmp(name, dev->name, strlen(dev->name)))
++                      return(dev);
++      }
++      return(NULL);
++}
++
++#define CONFIG_BUF_SIZE 64
++
++static void mconsole_get_config(int (*get_config)(char *, char *, int, 
++                                                char **),
++                              struct mc_request *req, char *name)
++{
++      char default_buf[CONFIG_BUF_SIZE], *error, *buf;
++      int n, size;
++
++      if(get_config == NULL){
++              mconsole_reply(req, "No get_config routine defined", 1, 0);
++              return;
++      }
++
++      error = NULL;
++      size = sizeof(default_buf)/sizeof(default_buf[0]);
++      buf = default_buf;
++
++      while(1){
++              n = (*get_config)(name, buf, size, &error);
++              if(error != NULL){
++                      mconsole_reply(req, error, 1, 0);
++                      goto out;
++              }
++
++              if(n <= size){
++                      mconsole_reply(req, buf, 0, 0);
++                      goto out;
++              }
++
++              if(buf != default_buf)
++                      kfree(buf);
++
++              size = n;
++              buf = kmalloc(size, GFP_KERNEL);
++              if(buf == NULL){
++                      mconsole_reply(req, "Failed to allocate buffer", 1, 0);
++                      return;
++              }
++      }
++ out:
++      if(buf != default_buf)
++              kfree(buf);
++      
++}
++
++void mconsole_config(struct mc_request *req)
++{
++      struct mc_device *dev;
++      char *ptr = req->request.data, *name;
++      int err;
++
++      ptr += strlen("config");
++      while(isspace(*ptr)) ptr++;
++      dev = mconsole_find_dev(ptr);
++      if(dev == NULL){
++              mconsole_reply(req, "Bad configuration option", 1, 0);
++              return;
++      }
++
++      name = &ptr[strlen(dev->name)];
++      ptr = name;
++      while((*ptr != '=') && (*ptr != '\0'))
++              ptr++;
++
++      if(*ptr == '='){
++              err = (*dev->config)(name);
++              mconsole_reply(req, "", err, 0);
++      }
++      else mconsole_get_config(dev->get_config, req, name);
++}
++
++void mconsole_remove(struct mc_request *req)
++{
++      struct mc_device *dev;  
++      char *ptr = req->request.data;
++      int err;
++
++      ptr += strlen("remove");
++      while(isspace(*ptr)) ptr++;
++      dev = mconsole_find_dev(ptr);
++      if(dev == NULL){
++              mconsole_reply(req, "Bad remove option", 1, 0);
++              return;
++      }
++      err = (*dev->remove)(&ptr[strlen(dev->name)]);
++      mconsole_reply(req, "", err, 0);
++}
++
++#ifdef CONFIG_MAGIC_SYSRQ
++void mconsole_sysrq(struct mc_request *req)
++{
++      char *ptr = req->request.data;
++
++      ptr += strlen("sysrq");
++      while(isspace(*ptr)) ptr++;
++
++      mconsole_reply(req, "", 0, 0);
++      handle_sysrq(*ptr, &current->thread.regs, NULL, NULL);
++}
++#else
++void mconsole_sysrq(struct mc_request *req)
++{
++      mconsole_reply(req, "Sysrq not compiled in", 1, 0);
++}
++#endif
++
++/* Changed by mconsole_setup, which is __setup, and called before SMP is
++ * active.
++ */
++static char *notify_socket = NULL; 
++
++int mconsole_init(void)
++{
++      int err, sock;
++      char file[256];
++
++      if(umid_file_name("mconsole", file, sizeof(file))) return(-1);
++      snprintf(mconsole_socket_name, sizeof(file), "%s", file);
++
++      sock = os_create_unix_socket(file, sizeof(file), 1);
++      if (sock < 0){
++              printk("Failed to initialize management console\n");
++              return(1);
++      }
++
++      register_reboot_notifier(&reboot_notifier);
++
++      err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
++                           SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM,
++                           "mconsole", (void *)sock);
++      if (err){
++              printk("Failed to get IRQ for management console\n");
++              return(1);
++      }
++
++      if(notify_socket != NULL){
++              notify_socket = uml_strdup(notify_socket);
++              if(notify_socket != NULL)
++                      mconsole_notify(notify_socket, MCONSOLE_SOCKET,
++                                      mconsole_socket_name, 
++                                      strlen(mconsole_socket_name) + 1);
++              else printk(KERN_ERR "mconsole_setup failed to strdup "
++                          "string\n");
++      }
++
++      printk("mconsole (version %d) initialized on %s\n", 
++             MCONSOLE_VERSION, mconsole_socket_name);
++      return(0);
++}
++
++__initcall(mconsole_init);
++
++static int write_proc_mconsole(struct file *file, const char *buffer,
++                             unsigned long count, void *data)
++{
++      char *buf;
++
++      buf = kmalloc(count + 1, GFP_KERNEL);
++      if(buf == NULL) 
++              return(-ENOMEM);
++
++      if(copy_from_user(buf, buffer, count)){
++              count = -EFAULT;
++              goto out;
++      }
++
++      buf[count] = '\0';
++
++      mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
++ out:
++      kfree(buf);
++      return(count);
++}
++
++static int create_proc_mconsole(void)
++{
++      struct proc_dir_entry *ent;
++
++      if(notify_socket == NULL) return(0);
++
++      ent = create_proc_entry("mconsole", S_IFREG | 0200, NULL);
++      if(ent == NULL){
++              printk("create_proc_mconsole : create_proc_entry failed\n");
++              return(0);
++      }
++
++      ent->read_proc = NULL;
++      ent->write_proc = write_proc_mconsole;
++      return(0);
++}
++
++static spinlock_t notify_spinlock = SPIN_LOCK_UNLOCKED;
++
++void lock_notify(void)
++{
++      spin_lock(&notify_spinlock);
++}
++
++void unlock_notify(void)
++{
++      spin_unlock(&notify_spinlock);
++}
++
++__initcall(create_proc_mconsole);
++
++#define NOTIFY "=notify:"
++
++static int mconsole_setup(char *str)
++{
++      if(!strncmp(str, NOTIFY, strlen(NOTIFY))){
++              str += strlen(NOTIFY);
++              notify_socket = str;
++      }
++      else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str);
++      return(1);
++}
++
++__setup("mconsole", mconsole_setup);
++
++__uml_help(mconsole_setup,
++"mconsole=notify:<socket>\n"
++"    Requests that the mconsole driver send a message to the named Unix\n"
++"    socket containing the name of the mconsole socket.  This also serves\n"
++"    to notify outside processes when UML has booted far enough to respond\n"
++"    to mconsole requests.\n\n"
++);
++
++static int notify_panic(struct notifier_block *self, unsigned long unused1,
++                      void *ptr)
++{
++      char *message = ptr;
++
++      if(notify_socket == NULL) return(0);
++
++      mconsole_notify(notify_socket, MCONSOLE_PANIC, message, 
++                      strlen(message) + 1);
++      return(0);
++}
++
++static struct notifier_block panic_exit_notifier = {
++      .notifier_call          = notify_panic,
++      .next                   = NULL,
++      .priority               = 1
++};
++
++static int add_notifier(void)
++{
++      notifier_chain_register(&panic_notifier_list, &panic_exit_notifier);
++      return(0);
++}
++
++__initcall(add_notifier);
++
++char *mconsole_notify_socket(void)
++{
++      return(notify_socket);
++}
++
++EXPORT_SYMBOL(mconsole_notify_socket);
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/mconsole_user.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/mconsole_user.c  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/mconsole_user.c       2005-05-03 22:28:14.223447920 +0300
+@@ -0,0 +1,215 @@
++/*
++ * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org)
++ * Copyright (C) 2001 - 2003 Jeff Dike (jdike@addtoit.com)
++ * Licensed under the GPL
++ */
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <errno.h>
++#include <signal.h>
++#include <sys/socket.h>
++#include <sys/types.h>
++#include <sys/uio.h>
++#include <sys/un.h>
++#include <unistd.h>
++#include "user.h"
++#include "mconsole.h"
++#include "umid.h"
++
++static struct mconsole_command commands[] = {
++      { "version", mconsole_version, MCONSOLE_INTR },
++      { "halt", mconsole_halt, MCONSOLE_PROC },
++      { "reboot", mconsole_reboot, MCONSOLE_PROC },
++      { "config", mconsole_config, MCONSOLE_PROC },
++      { "remove", mconsole_remove, MCONSOLE_PROC },
++      { "sysrq", mconsole_sysrq, MCONSOLE_INTR },
++      { "help", mconsole_help, MCONSOLE_INTR },
++      { "cad", mconsole_cad, MCONSOLE_INTR },
++      { "stop", mconsole_stop, MCONSOLE_PROC },
++      { "go", mconsole_go, MCONSOLE_INTR },
++      { "log", mconsole_log, MCONSOLE_INTR },
++      { "proc", mconsole_proc, MCONSOLE_PROC },
++};
++
++/* Initialized in mconsole_init, which is an initcall */
++char mconsole_socket_name[256];
++
++int mconsole_reply_v0(struct mc_request *req, char *reply)
++{
++        struct iovec iov;
++        struct msghdr msg;
++
++        iov.iov_base = reply;
++        iov.iov_len = strlen(reply);
++
++        msg.msg_name = &(req->origin);
++        msg.msg_namelen = req->originlen;
++        msg.msg_iov = &iov;
++        msg.msg_iovlen = 1;
++        msg.msg_control = NULL;
++        msg.msg_controllen = 0;
++        msg.msg_flags = 0;
++
++        return sendmsg(req->originating_fd, &msg, 0);
++}
++
++static struct mconsole_command *mconsole_parse(struct mc_request *req)
++{
++      struct mconsole_command *cmd;
++      int i;
++
++      for(i=0;i<sizeof(commands)/sizeof(commands[0]);i++){
++              cmd = &commands[i];
++              if(!strncmp(req->request.data, cmd->command, 
++                          strlen(cmd->command))){
++                      return(cmd);
++              }
++      }
++      return(NULL);
++}
++
++#define MIN(a,b) ((a)<(b) ? (a):(b))
++
++#define STRINGX(x) #x
++#define STRING(x) STRINGX(x)
++
++int mconsole_get_request(int fd, struct mc_request *req)
++{
++      int len;
++
++      req->originlen = sizeof(req->origin);
++      req->len = recvfrom(fd, &req->request, sizeof(req->request), 0,
++                          (struct sockaddr *) req->origin, &req->originlen);
++      if (req->len < 0)
++              return 0;
++
++      req->originating_fd = fd;
++
++      if(req->request.magic != MCONSOLE_MAGIC){
++              /* Unversioned request */
++              len = MIN(sizeof(req->request.data) - 1, 
++                        strlen((char *) &req->request));
++              memmove(req->request.data, &req->request, len);
++              req->request.data[len] = '\0';
++
++              req->request.magic = MCONSOLE_MAGIC;
++              req->request.version = 0;
++              req->request.len = len;
++
++              mconsole_reply_v0(req, "ERR Version 0 mconsole clients are "
++                                "not supported by this driver");
++              return(0);
++      }
++
++      if(req->request.len >= MCONSOLE_MAX_DATA){
++              mconsole_reply(req, "Request too large", 1, 0);
++              return(0);
++      }
++      if(req->request.version != MCONSOLE_VERSION){
++              mconsole_reply(req, "This driver only supports version " 
++                               STRING(MCONSOLE_VERSION) " clients", 1, 0);
++      }
++      
++      req->request.data[req->request.len] = '\0';
++      req->cmd = mconsole_parse(req);
++      if(req->cmd == NULL){
++              mconsole_reply(req, "Unknown command", 1, 0);
++              return(0);
++      }
++
++      return(1);
++}
++
++int mconsole_reply(struct mc_request *req, char *str, int err, int more)
++{
++      struct mconsole_reply reply;
++      int total, len, n;
++
++      total = strlen(str);
++      do {
++              reply.err = err;
++
++              /* err can only be true on the first packet */
++              err = 0;
++
++              len = MIN(total, MCONSOLE_MAX_DATA - 1);
++
++              if(len == total) reply.more = more;
++              else reply.more = 1;
++
++              memcpy(reply.data, str, len);
++              reply.data[len] = '\0';
++              total -= len;
++              str += len;
++              reply.len = len + 1;
++
++              len = sizeof(reply) + reply.len - sizeof(reply.data);
++
++              n = sendto(req->originating_fd, &reply, len, 0,
++                         (struct sockaddr *) req->origin, req->originlen);
++
++              if(n < 0) return(-errno);
++      } while(total > 0);
++      return(0);
++}
++
++int mconsole_unlink_socket(void)
++{
++      unlink(mconsole_socket_name);
++      return 0;
++}
++
++static int notify_sock = -1;
++
++int mconsole_notify(char *sock_name, int type, const void *data, int len)
++{
++      struct sockaddr_un target;
++      struct mconsole_notify packet;
++      int n, err = 0;
++
++      lock_notify();
++      if(notify_sock < 0){
++              notify_sock = socket(PF_UNIX, SOCK_DGRAM, 0);
++              if(notify_sock < 0){
++                      printk("mconsole_notify - socket failed, errno = %d\n",
++                             errno);
++                      err = -errno;
++              }
++      }
++      unlock_notify();
++      
++      if(err)
++              return(err);
++
++      target.sun_family = AF_UNIX;
++      strcpy(target.sun_path, sock_name);
++
++      packet.magic = MCONSOLE_MAGIC;
++      packet.version = MCONSOLE_VERSION;
++      packet.type = type;
++      len = (len > sizeof(packet.data)) ? sizeof(packet.data) : len;
++      packet.len = len;
++      memcpy(packet.data, data, len);
++
++      err = 0;
++      len = sizeof(packet) + packet.len - sizeof(packet.data);
++      n = sendto(notify_sock, &packet, len, 0, (struct sockaddr *) &target, 
++                 sizeof(target));
++      if(n < 0){
++              printk("mconsole_notify - sendto failed, errno = %d\n", errno);
++              err = -errno;
++      }
++      return(err);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/mmapper_kern.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/mmapper_kern.c   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/mmapper_kern.c        2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,151 @@
++/*
++ * arch/um/drivers/mmapper_kern.c
++ *
++ * BRIEF MODULE DESCRIPTION
++ *
++ * Copyright (C) 2000 RidgeRun, Inc.
++ * Author: RidgeRun, Inc.
++ *         Greg Lonnon glonnon@ridgerun.com or info@ridgerun.com
++ *
++ */
++#include <linux/kdev_t.h>
++#include <linux/time.h>
++#include <linux/devfs_fs_kernel.h>
++#include <linux/module.h>
++#include <linux/mm.h> 
++#include <linux/slab.h>
++#include <linux/init.h> 
++#include <asm/uaccess.h>
++#include <asm/irq.h>
++#include <asm/smplock.h>
++#include <asm/pgtable.h>
++#include "mem_user.h"
++#include "user_util.h"
++ 
++/* These are set in mmapper_init, which is called at boot time */
++static unsigned long mmapper_size;
++static unsigned long p_buf = 0;
++static char *v_buf = NULL;
++
++static ssize_t
++mmapper_read(struct file *file, char *buf, size_t count, loff_t *ppos)
++{
++      if(*ppos > mmapper_size)
++              return -EINVAL;
++
++      if(count + *ppos > mmapper_size)
++              count = count + *ppos - mmapper_size;
++
++      if(count < 0)
++              return -EINVAL;
++ 
++      copy_to_user(buf,&v_buf[*ppos],count);
++      
++      return count;
++}
++
++static ssize_t
++mmapper_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
++{
++      if(*ppos > mmapper_size)
++              return -EINVAL;
++
++      if(count + *ppos > mmapper_size)
++              count = count + *ppos - mmapper_size;
++
++      if(count < 0)
++              return -EINVAL;
++
++      copy_from_user(&v_buf[*ppos],buf,count);
++      
++      return count;
++}
++
++static int 
++mmapper_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
++       unsigned long arg)
++{
++      return(-ENOIOCTLCMD);
++}
++
++static int 
++mmapper_mmap(struct file *file, struct vm_area_struct * vma)
++{
++      int ret = -EINVAL;
++      int size;
++
++      lock_kernel();
++      if (vma->vm_pgoff != 0)
++              goto out;
++      
++      size = vma->vm_end - vma->vm_start;
++      if(size > mmapper_size) return(-EFAULT);
++
++      /* XXX A comment above remap_page_range says it should only be
++       * called when the mm semaphore is held
++       */
++      if (remap_page_range(vma->vm_start, p_buf, size, vma->vm_page_prot))
++              goto out;
++      ret = 0;
++out:
++      unlock_kernel();
++      return ret;
++}
++
++static int
++mmapper_open(struct inode *inode, struct file *file)
++{
++      return 0;
++}
++
++static int 
++mmapper_release(struct inode *inode, struct file *file)
++{
++      return 0;
++}
++
++static struct file_operations mmapper_fops = {
++      .owner          = THIS_MODULE,
++      .read           = mmapper_read,
++      .write          = mmapper_write,
++      .ioctl          = mmapper_ioctl,
++      .mmap           = mmapper_mmap,
++      .open           = mmapper_open,
++      .release        = mmapper_release,
++};
++
++static int __init mmapper_init(void)
++{
++      printk(KERN_INFO "Mapper v0.1\n");
++
++      v_buf = (char *) find_iomem("mmapper", &mmapper_size);
++      if(mmapper_size == 0){
++              printk(KERN_ERR "mmapper_init - find_iomem failed\n");
++              return(0);
++      }
++
++      p_buf = __pa(v_buf);
++
++      devfs_register (NULL, "mmapper", DEVFS_FL_DEFAULT, 
++                      30, 0, S_IFCHR | S_IRUGO | S_IWUGO, 
++                      &mmapper_fops, NULL); 
++      devfs_mk_symlink(NULL, "mmapper0", DEVFS_FL_DEFAULT, "mmapper",
++                       NULL, NULL);
++      return(0);
++}
++
++static void mmapper_exit(void)
++{
++}
++
++module_init(mmapper_init);
++module_exit(mmapper_exit);
++
++MODULE_AUTHOR("Greg Lonnon <glonnon@ridgerun.com>");
++MODULE_DESCRIPTION("DSPLinux simulator mmapper driver");
++/*
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/net_kern.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/net_kern.c       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/net_kern.c    2005-05-03 22:28:14.228447160 +0300
+@@ -0,0 +1,903 @@
++/*
++ * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and 
++ * James Leu (jleu@mindspring.net).
++ * Copyright (C) 2001 by various other people who didn't put their name here.
++ * Licensed under the GPL.
++ */
++
++#include "linux/config.h"
++#include "linux/kernel.h"
++#include "linux/netdevice.h"
++#include "linux/rtnetlink.h"
++#include "linux/skbuff.h"
++#include "linux/socket.h"
++#include "linux/spinlock.h"
++#include "linux/module.h"
++#include "linux/init.h"
++#include "linux/etherdevice.h"
++#include "linux/list.h"
++#include "linux/inetdevice.h"
++#include "linux/ctype.h"
++#include "linux/bootmem.h"
++#include "linux/ethtool.h"
++#include "asm/uaccess.h"
++#include "user_util.h"
++#include "kern_util.h"
++#include "net_kern.h"
++#include "net_user.h"
++#include "mconsole_kern.h"
++#include "init.h"
++#include "irq_user.h"
++#include "irq_kern.h"
++
++static spinlock_t opened_lock = SPIN_LOCK_UNLOCKED;
++LIST_HEAD(opened);
++
++static int uml_net_rx(struct net_device *dev)
++{
++      struct uml_net_private *lp = dev->priv;
++      int pkt_len;
++      struct sk_buff *skb;
++
++      /* If we can't allocate memory, try again next round. */
++      skb = dev_alloc_skb(dev->mtu);
++      if (skb == NULL) {
++              lp->stats.rx_dropped++;
++              return 0;
++      }
++
++      skb->dev = dev;
++      skb_put(skb, dev->mtu);
++      skb->mac.raw = skb->data;
++      pkt_len = (*lp->read)(lp->fd, &skb, lp);
++
++      if (pkt_len > 0) {
++              skb_trim(skb, pkt_len);
++              skb->protocol = (*lp->protocol)(skb);
++              netif_rx(skb);
++
++              lp->stats.rx_bytes += skb->len;
++              lp->stats.rx_packets++;
++              return pkt_len;
++      }
++
++      kfree_skb(skb);
++      return pkt_len;
++}
++
++void uml_net_interrupt(int irq, void *dev_id, struct pt_regs *regs)
++{
++      struct net_device *dev = dev_id;
++      struct uml_net_private *lp = dev->priv;
++      int err;
++
++      if(!netif_running(dev))
++              return;
++
++      spin_lock(&lp->lock);
++      while((err = uml_net_rx(dev)) > 0) ;
++      if(err < 0) {
++              printk(KERN_ERR 
++                     "Device '%s' read returned %d, shutting it down\n", 
++                     dev->name, err);
++              dev_close(dev);
++              goto out;
++      }
++      reactivate_fd(lp->fd, UM_ETH_IRQ);
++
++ out:
++      spin_unlock(&lp->lock);
++}
++
++static int uml_net_open(struct net_device *dev)
++{
++      struct uml_net_private *lp = dev->priv;
++      char addr[sizeof("255.255.255.255\0")];
++      int err;
++
++      spin_lock(&lp->lock);
++
++      if(lp->fd >= 0){
++              err = -ENXIO;
++              goto out;
++      }
++
++      if(!lp->have_mac){
++              dev_ip_addr(dev, addr, &lp->mac[2]);
++              set_ether_mac(dev, lp->mac);
++      }
++
++      lp->fd = (*lp->open)(&lp->user);
++      if(lp->fd < 0){
++              err = lp->fd;
++              goto out;
++      }
++
++      err = um_request_irq(dev->irq, lp->fd, IRQ_READ, uml_net_interrupt,
++                           SA_INTERRUPT | SA_SHIRQ, dev->name, dev);
++      if(err != 0){
++              printk(KERN_ERR "uml_net_open: failed to get irq(%d)\n", err);
++              if(lp->close != NULL) (*lp->close)(lp->fd, &lp->user);
++              lp->fd = -1;
++              err = -ENETUNREACH;
++      }
++
++      lp->tl.data = (unsigned long) &lp->user;
++      netif_start_queue(dev);
++
++      spin_lock(&opened_lock);
++      list_add(&lp->list, &opened);
++      spin_unlock(&opened_lock);
++       /* clear buffer - it can happen that the host side of the interface
++        * is full when we get here.  In this case, new data is never queued,
++        * SIGIOs never arrive, and the net never works.
++        */
++      while((err = uml_net_rx(dev)) > 0) ;
++
++      MOD_INC_USE_COUNT;
++ out:
++      spin_unlock(&lp->lock);
++      return(err);
++}
++
++static int uml_net_close(struct net_device *dev)
++{
++      struct uml_net_private *lp = dev->priv;
++      
++      netif_stop_queue(dev);
++      spin_lock(&lp->lock);
++
++      free_irq(dev->irq, dev);
++      if(lp->close != NULL) (*lp->close)(lp->fd, &lp->user);
++      lp->fd = -1;
++      spin_lock(&opened_lock);
++      list_del(&lp->list);
++      spin_unlock(&opened_lock);
++
++      MOD_DEC_USE_COUNT;
++      spin_unlock(&lp->lock);
++      return 0;
++}
++
++static int uml_net_start_xmit(struct sk_buff *skb, struct net_device *dev)
++{
++      struct uml_net_private *lp = dev->priv;
++      unsigned long flags;
++      int len;
++
++      netif_stop_queue(dev);
++
++      spin_lock_irqsave(&lp->lock, flags);
++
++      len = (*lp->write)(lp->fd, &skb, lp);
++
++      if(len == skb->len) {
++              lp->stats.tx_packets++;
++              lp->stats.tx_bytes += skb->len;
++              dev->trans_start = jiffies;
++              netif_start_queue(dev);
++
++              /* this is normally done in the interrupt when tx finishes */
++              netif_wake_queue(dev);
++      } 
++      else if(len == 0){
++              netif_start_queue(dev);
++              lp->stats.tx_dropped++;
++      }
++      else {
++              netif_start_queue(dev);
++              printk(KERN_ERR "uml_net_start_xmit: failed(%d)\n", len);
++      }
++
++      spin_unlock_irqrestore(&lp->lock, flags);
++
++      dev_kfree_skb(skb);
++
++      return 0;
++}
++
++static struct net_device_stats *uml_net_get_stats(struct net_device *dev)
++{
++      struct uml_net_private *lp = dev->priv;
++      return &lp->stats;
++}
++
++static void uml_net_set_multicast_list(struct net_device *dev)
++{
++      if (dev->flags & IFF_PROMISC) return;
++      else if (dev->mc_count) dev->flags |= IFF_ALLMULTI;
++      else dev->flags &= ~IFF_ALLMULTI;
++}
++
++static void uml_net_tx_timeout(struct net_device *dev)
++{
++      dev->trans_start = jiffies;
++      netif_wake_queue(dev);
++}
++
++static int uml_net_set_mac(struct net_device *dev, void *addr)
++{
++      struct uml_net_private *lp = dev->priv;
++      struct sockaddr *hwaddr = addr;
++
++      spin_lock(&lp->lock);
++      memcpy(dev->dev_addr, hwaddr->sa_data, ETH_ALEN);
++      spin_unlock(&lp->lock);
++
++      return(0);
++}
++
++static int uml_net_change_mtu(struct net_device *dev, int new_mtu)
++{
++      struct uml_net_private *lp = dev->priv;
++      int err = 0;
++
++      spin_lock(&lp->lock);
++
++      new_mtu = (*lp->set_mtu)(new_mtu, &lp->user);
++      if(new_mtu < 0){
++              err = new_mtu;
++              goto out;
++      }
++
++      dev->mtu = new_mtu;
++
++ out:
++      spin_unlock(&lp->lock);
++      return err;
++}
++
++static int uml_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
++{
++      static const struct ethtool_drvinfo info = {
++              .cmd     = ETHTOOL_GDRVINFO,
++              .driver  = "uml virtual ethernet",
++              .version = "42",
++      };
++      void *useraddr;
++      u32 ethcmd;
++
++      switch (cmd) {
++      case SIOCETHTOOL:
++              useraddr = ifr->ifr_data;
++              if (copy_from_user(&ethcmd, useraddr, sizeof(ethcmd)))
++                      return -EFAULT;
++              switch (ethcmd) {
++              case ETHTOOL_GDRVINFO:
++                      if (copy_to_user(useraddr, &info, sizeof(info)))
++                              return -EFAULT;
++                      return 0;
++              default:
++                      return -EOPNOTSUPP;
++              }
++      default:
++              return -EINVAL;
++      }
++}
++
++void uml_net_user_timer_expire(unsigned long _conn)
++{
++#ifdef undef
++      struct connection *conn = (struct connection *)_conn;
++
++      dprintk(KERN_INFO "uml_net_user_timer_expire [%p]\n", conn);
++      do_connect(conn);
++#endif
++}
++
++/*
++ * default do nothing hard header packet routines for struct net_device init.
++ * real ethernet transports will overwrite with real routines.
++ */
++static int uml_net_hard_header(struct sk_buff *skb, struct net_device *dev,
++                 unsigned short type, void *daddr, void *saddr, unsigned len)
++{
++      return(0); /* no change */
++}
++
++static int uml_net_rebuild_header(struct sk_buff *skb)
++{
++      return(0); /* ignore */ 
++}
++
++static int uml_net_header_cache(struct neighbour *neigh, struct hh_cache *hh)
++{
++      return(-1); /* fail */
++}
++
++static void uml_net_header_cache_update(struct hh_cache *hh,
++                 struct net_device *dev, unsigned char * haddr)
++{
++      /* ignore */
++}
++
++static int uml_net_header_parse(struct sk_buff *skb, unsigned char *haddr)
++{
++      return(0); /* nothing */
++}
++
++static spinlock_t devices_lock = SPIN_LOCK_UNLOCKED;
++static struct list_head devices = LIST_HEAD_INIT(devices);
++
++static int eth_configure(int n, void *init, char *mac,
++                       struct transport *transport)
++{
++      struct uml_net *device;
++      struct net_device *dev;
++      struct uml_net_private *lp;
++      int save, err, size;
++
++      size = transport->private_size + sizeof(struct uml_net_private) + 
++              sizeof(((struct uml_net_private *) 0)->user);
++
++      device = kmalloc(sizeof(*device), GFP_KERNEL);
++      if(device == NULL){
++              printk(KERN_ERR "eth_configure failed to allocate uml_net\n");
++              return(1);
++      }
++
++      *device = ((struct uml_net) { .list     = LIST_HEAD_INIT(device->list),
++                                    .dev      = NULL,
++                                    .index    = n,
++                                    .mac      = { [ 0 ... 5 ] = 0 },
++                                    .have_mac = 0 });
++
++      spin_lock(&devices_lock);
++      list_add(&device->list, &devices);
++      spin_unlock(&devices_lock);
++
++      if(setup_etheraddr(mac, device->mac))
++              device->have_mac = 1;
++
++      printk(KERN_INFO "Netdevice %d ", n);
++      if(device->have_mac) printk("(%02x:%02x:%02x:%02x:%02x:%02x) ",
++                                  device->mac[0], device->mac[1], 
++                                  device->mac[2], device->mac[3], 
++                                  device->mac[4], device->mac[5]);
++      printk(": ");
++      dev = kmalloc(sizeof(*dev) + size, GFP_KERNEL);
++      if(dev == NULL){
++              printk(KERN_ERR "eth_configure: failed to allocate device\n");
++              return(1);
++      }
++      memset(dev, 0, sizeof(*dev) + size);
++
++      snprintf(dev->name, sizeof(dev->name), "eth%d", n);
++      dev->priv = (void *) &dev[1];
++      device->dev = dev;
++
++        dev->hard_header = uml_net_hard_header;
++        dev->rebuild_header = uml_net_rebuild_header;
++        dev->hard_header_cache = uml_net_header_cache;
++        dev->header_cache_update= uml_net_header_cache_update;
++        dev->hard_header_parse = uml_net_header_parse;
++
++      (*transport->kern->init)(dev, init);
++
++      dev->mtu = transport->user->max_packet;
++      dev->open = uml_net_open;
++      dev->hard_start_xmit = uml_net_start_xmit;
++      dev->stop = uml_net_close;
++      dev->get_stats = uml_net_get_stats;
++      dev->set_multicast_list = uml_net_set_multicast_list;
++      dev->tx_timeout = uml_net_tx_timeout;
++      dev->set_mac_address = uml_net_set_mac;
++      dev->change_mtu = uml_net_change_mtu;
++      dev->do_ioctl = uml_net_ioctl;
++      dev->watchdog_timeo = (HZ >> 1);
++      dev->irq = UM_ETH_IRQ;
++
++      rtnl_lock();
++      err = register_netdevice(dev);
++      rtnl_unlock();
++      if(err)
++              return(1);
++      lp = dev->priv;
++
++      /* lp.user is the first four bytes of the transport data, which
++       * has already been initialized.  This structure assignment will
++       * overwrite that, so we make sure that .user gets overwritten with
++       * what it already has.
++       */
++      save = lp->user[0];
++      *lp = ((struct uml_net_private) 
++              { .list                 = LIST_HEAD_INIT(lp->list),
++                .lock                 = SPIN_LOCK_UNLOCKED,
++                .dev                  = dev,
++                .fd                   = -1,
++                .mac                  = { 0xfe, 0xfd, 0x0, 0x0, 0x0, 0x0},
++                .have_mac             = device->have_mac,
++                .protocol             = transport->kern->protocol,
++                .open                 = transport->user->open,
++                .close                = transport->user->close,
++                .remove               = transport->user->remove,
++                .read                 = transport->kern->read,
++                .write                = transport->kern->write,
++                .add_address          = transport->user->add_address,
++                .delete_address       = transport->user->delete_address,
++                .set_mtu              = transport->user->set_mtu,
++                .user                 = { save } });
++      init_timer(&lp->tl);
++      lp->tl.function = uml_net_user_timer_expire;
++      memset(&lp->stats, 0, sizeof(lp->stats));
++      if(lp->have_mac) memcpy(lp->mac, device->mac, sizeof(lp->mac));
++
++      if(transport->user->init) 
++              (*transport->user->init)(&lp->user, dev);
++
++      if(device->have_mac)
++              set_ether_mac(dev, device->mac);
++      return(0);
++}
++
++static struct uml_net *find_device(int n)
++{
++      struct uml_net *device;
++      struct list_head *ele;
++
++      spin_lock(&devices_lock);
++      list_for_each(ele, &devices){
++              device = list_entry(ele, struct uml_net, list);
++              if(device->index == n)
++                      goto out;
++      }
++      device = NULL;
++ out:
++      spin_unlock(&devices_lock);
++      return(device);
++}
++
++static int eth_parse(char *str, int *index_out, char **str_out)
++{
++      char *end;
++      int n;
++
++      n = simple_strtoul(str, &end, 0);
++      if(end == str){
++              printk(KERN_ERR "eth_setup: Failed to parse '%s'\n", str);
++              return(1);
++      }
++      if(n < 0){
++              printk(KERN_ERR "eth_setup: device %d is negative\n", n);
++              return(1);
++      }
++      str = end;
++      if(*str != '='){
++              printk(KERN_ERR 
++                     "eth_setup: expected '=' after device number\n");
++              return(1);
++      }
++      str++;
++      if(find_device(n)){
++              printk(KERN_ERR "eth_setup: Device %d already configured\n",
++                     n);
++              return(1);
++      }
++      if(index_out) *index_out = n;
++      *str_out = str;
++      return(0);
++}
++
++struct eth_init {
++      struct list_head list;
++      char *init;
++      int index;
++};
++
++/* Filled in at boot time.  Will need locking if the transports become
++ * modular.
++ */
++struct list_head transports = LIST_HEAD_INIT(transports);
++
++/* Filled in during early boot */
++struct list_head eth_cmd_line = LIST_HEAD_INIT(eth_cmd_line);
++
++static int check_transport(struct transport *transport, char *eth, int n,
++                         void **init_out, char **mac_out)
++{
++      int len;
++
++      len = strlen(transport->name);
++      if(strncmp(eth, transport->name, len))
++              return(0);
++
++      eth += len;
++      if(*eth == ',')
++              eth++;
++      else if(*eth != '\0')
++              return(0);
++
++      *init_out = kmalloc(transport->setup_size, GFP_KERNEL);
++      if(*init_out == NULL)
++              return(1);
++
++      if(!transport->setup(eth, mac_out, *init_out)){
++              kfree(*init_out);
++              *init_out = NULL;
++      }
++      return(1);
++}
++
++void register_transport(struct transport *new)
++{
++      struct list_head *ele, *next;
++      struct eth_init *eth;
++      void *init;
++      char *mac = NULL;
++      int match;
++
++      list_add(&new->list, &transports);
++
++      list_for_each_safe(ele, next, &eth_cmd_line){
++              eth = list_entry(ele, struct eth_init, list);
++              match = check_transport(new, eth->init, eth->index, &init,
++                                      &mac);
++              if(!match)
++                      continue;
++              else if(init != NULL){
++                      eth_configure(eth->index, init, mac, new);
++                      kfree(init);
++              }
++              list_del(&eth->list);
++      }
++}
++
++static int eth_setup_common(char *str, int index)
++{
++      struct list_head *ele;
++      struct transport *transport;
++      void *init;
++      char *mac = NULL;
++
++      list_for_each(ele, &transports){
++              transport = list_entry(ele, struct transport, list);
++              if(!check_transport(transport, str, index, &init, &mac))
++                      continue;
++              if(init != NULL){
++                      eth_configure(index, init, mac, transport);
++                      kfree(init);
++              }
++              return(1);
++      }
++      return(0);
++}
++
++static int eth_setup(char *str)
++{
++      struct eth_init *new;
++      int n, err;
++
++      err = eth_parse(str, &n, &str);
++      if(err) return(1);
++
++      new = alloc_bootmem(sizeof(new));
++      if(new == NULL){
++              printk("eth_init : alloc_bootmem failed\n");
++              return(1);
++      }
++      *new = ((struct eth_init) { .list       = LIST_HEAD_INIT(new->list),
++                                  .index      = n,
++                                  .init       = str });
++      list_add_tail(&new->list, &eth_cmd_line);
++      return(1);
++}
++
++__setup("eth", eth_setup);
++__uml_help(eth_setup,
++"eth[0-9]+=<transport>,<options>\n"
++"    Configure a network device.\n\n"
++);
++
++static int eth_init(void)
++{
++      struct list_head *ele, *next;
++      struct eth_init *eth;
++
++      list_for_each_safe(ele, next, &eth_cmd_line){
++              eth = list_entry(ele, struct eth_init, list);
++
++              if(eth_setup_common(eth->init, eth->index))
++                      list_del(&eth->list);
++      }
++      
++      return(1);
++}
++
++__initcall(eth_init);
++
++static int net_config(char *str)
++{
++      int n, err;
++
++      err = eth_parse(str, &n, &str);
++      if(err) return(err);
++
++      str = uml_strdup(str);
++      if(str == NULL){
++              printk(KERN_ERR "net_config failed to strdup string\n");
++              return(-1);
++      }
++      err = !eth_setup_common(str, n);
++      if(err) 
++              kfree(str);
++      return(err);
++}
++
++static int net_remove(char *str)
++{
++      struct uml_net *device;
++      struct net_device *dev;
++      struct uml_net_private *lp;
++      char *end;
++      int n;
++
++      n = simple_strtoul(str, &end, 0);
++      if((*end != '\0') || (end == str))
++              return(-1);
++
++      device = find_device(n);
++      if(device == NULL)
++              return(0);
++
++      dev = device->dev;
++      lp = dev->priv;
++      if(lp->fd > 0) return(-1);
++      if(lp->remove != NULL) (*lp->remove)(&lp->user);
++      unregister_netdev(dev);
++
++      list_del(&device->list);
++      kfree(device);
++      return(0);
++}
++
++static struct mc_device net_mc = {
++      .name           = "eth",
++      .config         = net_config,
++      .get_config     = NULL,
++      .remove         = net_remove,
++};
++
++static int uml_inetaddr_event(struct notifier_block *this, unsigned long event,
++                            void *ptr)
++{
++      struct in_ifaddr *ifa = ptr;
++      u32 addr = ifa->ifa_address;
++      u32 netmask = ifa->ifa_mask;
++      struct net_device *dev = ifa->ifa_dev->dev;
++      struct uml_net_private *lp;
++      void (*proc)(unsigned char *, unsigned char *, void *);
++      unsigned char addr_buf[4], netmask_buf[4];
++
++      if(dev->open != uml_net_open) return(NOTIFY_DONE);
++
++      lp = dev->priv;
++
++      proc = NULL;
++      switch (event){
++      case NETDEV_UP:
++              proc = lp->add_address;
++              break;
++      case NETDEV_DOWN:
++              proc = lp->delete_address;
++              break;
++      }
++      if(proc != NULL){
++              addr_buf[0] = addr & 0xff;
++              addr_buf[1] = (addr >> 8) & 0xff;
++              addr_buf[2] = (addr >> 16) & 0xff;
++              addr_buf[3] = addr >> 24;
++              netmask_buf[0] = netmask & 0xff;
++              netmask_buf[1] = (netmask >> 8) & 0xff;
++              netmask_buf[2] = (netmask >> 16) & 0xff;
++              netmask_buf[3] = netmask >> 24;
++              (*proc)(addr_buf, netmask_buf, &lp->user);
++      }
++      return(NOTIFY_DONE);
++}
++
++struct notifier_block uml_inetaddr_notifier = {
++      .notifier_call          = uml_inetaddr_event,
++};
++
++static int uml_net_init(void)
++{
++      struct list_head *ele;
++      struct uml_net_private *lp;     
++      struct in_device *ip;
++      struct in_ifaddr *in;
++
++      mconsole_register_dev(&net_mc);
++      register_inetaddr_notifier(&uml_inetaddr_notifier);
++
++      /* Devices may have been opened already, so the uml_inetaddr_notifier
++       * didn't get a chance to run for them.  This fakes it so that
++       * addresses which have already been set up get handled properly.
++       */
++      list_for_each(ele, &opened){
++              lp = list_entry(ele, struct uml_net_private, list);
++              ip = lp->dev->ip_ptr;
++              if(ip == NULL) continue;
++              in = ip->ifa_list;
++              while(in != NULL){
++                      uml_inetaddr_event(NULL, NETDEV_UP, in);
++                      in = in->ifa_next;
++              }
++      }       
++
++      return(0);
++}
++
++__initcall(uml_net_init);
++
++static void close_devices(void)
++{
++      struct list_head *ele;
++      struct uml_net_private *lp;     
++
++      list_for_each(ele, &opened){
++              lp = list_entry(ele, struct uml_net_private, list);
++              if(lp->close != NULL) (*lp->close)(lp->fd, &lp->user);
++              if(lp->remove != NULL) (*lp->remove)(&lp->user);
++      }
++}
++
++__uml_exitcall(close_devices);
++
++int setup_etheraddr(char *str, unsigned char *addr)
++{
++      char *end;
++      int i;
++
++      if(str == NULL)
++              return(0);
++      for(i=0;i<6;i++){
++              addr[i] = simple_strtoul(str, &end, 16);
++              if((end == str) ||
++                 ((*end != ':') && (*end != ',') && (*end != '\0'))){
++                      printk(KERN_ERR 
++                             "setup_etheraddr: failed to parse '%s' "
++                             "as an ethernet address\n", str);
++                      return(0);
++              }
++              str = end + 1;
++      }
++      if(addr[0] & 1){
++              printk(KERN_ERR 
++                     "Attempt to assign a broadcast ethernet address to a "
++                     "device disallowed\n");
++              return(0);
++      }
++      return(1);
++}
++
++void dev_ip_addr(void *d, char *buf, char *bin_buf)
++{
++      struct net_device *dev = d;
++      struct in_device *ip = dev->ip_ptr;
++      struct in_ifaddr *in;
++      u32 addr;
++
++      if((ip == NULL) || ((in = ip->ifa_list) == NULL)){
++              printk(KERN_WARNING "dev_ip_addr - device not assigned an "
++                     "IP address\n");
++              return;
++      }
++      addr = in->ifa_address;
++      sprintf(buf, "%d.%d.%d.%d", addr & 0xff, (addr >> 8) & 0xff, 
++              (addr >> 16) & 0xff, addr >> 24);
++      if(bin_buf){
++              bin_buf[0] = addr & 0xff;
++              bin_buf[1] = (addr >> 8) & 0xff;
++              bin_buf[2] = (addr >> 16) & 0xff;
++              bin_buf[3] = addr >> 24;
++      }
++}
++
++void set_ether_mac(void *d, unsigned char *addr)
++{
++      struct net_device *dev = d;
++
++      memcpy(dev->dev_addr, addr, ETH_ALEN);  
++}
++
++struct sk_buff *ether_adjust_skb(struct sk_buff *skb, int extra)
++{
++      if((skb != NULL) && (skb_tailroom(skb) < extra)){
++              struct sk_buff *skb2;
++
++              skb2 = skb_copy_expand(skb, 0, extra, GFP_ATOMIC);
++              dev_kfree_skb(skb);
++              skb = skb2;
++      }
++      if(skb != NULL) skb_put(skb, extra);
++      return(skb);
++}
++
++void iter_addresses(void *d, void (*cb)(unsigned char *, unsigned char *, 
++                                      void *), 
++                  void *arg)
++{
++      struct net_device *dev = d;
++      struct in_device *ip = dev->ip_ptr;
++      struct in_ifaddr *in;
++      unsigned char address[4], netmask[4];
++
++      if(ip == NULL) return;
++      in = ip->ifa_list;
++      while(in != NULL){
++              address[0] = in->ifa_address & 0xff;
++              address[1] = (in->ifa_address >> 8) & 0xff;
++              address[2] = (in->ifa_address >> 16) & 0xff;
++              address[3] = in->ifa_address >> 24;
++              netmask[0] = in->ifa_mask & 0xff;
++              netmask[1] = (in->ifa_mask >> 8) & 0xff;
++              netmask[2] = (in->ifa_mask >> 16) & 0xff;
++              netmask[3] = in->ifa_mask >> 24;
++              (*cb)(address, netmask, arg);
++              in = in->ifa_next;
++      }
++}
++
++int dev_netmask(void *d, void *m)
++{
++      struct net_device *dev = d;
++      struct in_device *ip = dev->ip_ptr;
++      struct in_ifaddr *in;
++      __u32 *mask_out = m;
++
++      if(ip == NULL) 
++              return(1);
++
++      in = ip->ifa_list;
++      if(in == NULL) 
++              return(1);
++
++      *mask_out = in->ifa_mask;
++      return(0);
++}
++
++void *get_output_buffer(int *len_out)
++{
++      void *ret;
++
++      ret = (void *) __get_free_pages(GFP_KERNEL, 0);
++      if(ret) *len_out = PAGE_SIZE;
++      else *len_out = 0;
++      return(ret);
++}
++
++void free_output_buffer(void *buffer)
++{
++      free_pages((unsigned long) buffer, 0);
++}
++
++int tap_setup_common(char *str, char *type, char **dev_name, char **mac_out, 
++                   char **gate_addr)
++{
++      char *remain;
++
++      remain = split_if_spec(str, dev_name, mac_out, gate_addr, NULL);
++      if(remain != NULL){
++              printk("tap_setup_common - Extra garbage on specification : "
++                     "'%s'\n", remain);
++              return(1);
++      }
++
++      return(0);
++}
++
++unsigned short eth_protocol(struct sk_buff *skb)
++{
++      return(eth_type_trans(skb, skb->dev));
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/net_user.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/net_user.c       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/net_user.c    2005-05-03 22:28:14.229447008 +0300
+@@ -0,0 +1,253 @@
++/* 
++ * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <stddef.h>
++#include <stdarg.h>
++#include <unistd.h>
++#include <stdio.h>
++#include <errno.h>
++#include <stdlib.h>
++#include <string.h>
++#include <sys/socket.h>
++#include <sys/wait.h>
++#include "user.h"
++#include "user_util.h"
++#include "kern_util.h"
++#include "net_user.h"
++#include "helper.h"
++#include "os.h"
++
++int tap_open_common(void *dev, char *gate_addr)
++{
++      int tap_addr[4];
++
++      if(gate_addr == NULL) return(0);
++      if(sscanf(gate_addr, "%d.%d.%d.%d", &tap_addr[0], 
++                &tap_addr[1], &tap_addr[2], &tap_addr[3]) != 4){
++              printk("Invalid tap IP address - '%s'\n", gate_addr);
++              return(-EINVAL);
++      }
++      return(0);
++}
++
++void tap_check_ips(char *gate_addr, char *eth_addr)
++{
++      int tap_addr[4];
++
++      if((gate_addr != NULL) && 
++         (sscanf(gate_addr, "%d.%d.%d.%d", &tap_addr[0], 
++                 &tap_addr[1], &tap_addr[2], &tap_addr[3]) == 4) &&
++         (eth_addr[0] == tap_addr[0]) && 
++         (eth_addr[1] == tap_addr[1]) && 
++         (eth_addr[2] == tap_addr[2]) && 
++         (eth_addr[3] == tap_addr[3])){
++              printk("The tap IP address and the UML eth IP address"
++                     " must be different\n");
++      }
++}
++
++void read_output(int fd, char *output, int len)
++{
++      int remain, n, actual;
++      char c;
++
++      if(output == NULL){
++              output = &c;
++              len = sizeof(c);
++      }
++              
++      *output = '\0';
++      n = os_read_file(fd, &remain, sizeof(remain));
++      if(n != sizeof(remain)){
++              printk("read_output - read of length failed, err = %d\n", -n);
++              return;
++      }
++
++      while(remain != 0){
++              n = (remain < len) ? remain : len;
++              actual = os_read_file(fd, output, n);
++              if(actual != n){
++                      printk("read_output - read of data failed, "
++                             "err = %d\n", -actual);
++                      return;
++              }
++              remain -= actual;
++      }
++      return;
++}
++
++int net_read(int fd, void *buf, int len)
++{
++      int n;
++
++      n = os_read_file(fd,  buf,  len);
++
++      if(n == -EAGAIN) 
++              return(0);
++      else if(n == 0) 
++              return(-ENOTCONN);
++      return(n);
++}
++
++int net_recvfrom(int fd, void *buf, int len)
++{
++      int n;
++
++      while(((n = recvfrom(fd,  buf,  len, 0, NULL, NULL)) < 0) && 
++            (errno == EINTR)) ;
++
++      if(n < 0){
++              if(errno == EAGAIN) return(0);
++              return(-errno);
++      }
++      else if(n == 0) return(-ENOTCONN);
++      return(n);
++}
++
++int net_write(int fd, void *buf, int len)
++{
++      int n;
++
++      n = os_write_file(fd, buf, len);
++
++      if(n == -EAGAIN) 
++              return(0);
++      else if(n == 0) 
++              return(-ENOTCONN);
++      return(n);
++}
++
++int net_send(int fd, void *buf, int len)
++{
++      int n;
++
++      while(((n = send(fd, buf, len, 0)) < 0) && (errno == EINTR)) ;
++      if(n < 0){
++              if(errno == EAGAIN) return(0);
++              return(-errno);
++      }
++      else if(n == 0) return(-ENOTCONN);
++      return(n);      
++}
++
++int net_sendto(int fd, void *buf, int len, void *to, int sock_len)
++{
++      int n;
++
++      while(((n = sendto(fd, buf, len, 0, (struct sockaddr *) to,
++                         sock_len)) < 0) && (errno == EINTR)) ;
++      if(n < 0){
++              if(errno == EAGAIN) return(0);
++              return(-errno);
++      }
++      else if(n == 0) return(-ENOTCONN);
++      return(n);      
++}
++
++struct change_pre_exec_data {
++      int close_me;
++      int stdout;
++};
++
++static void change_pre_exec(void *arg)
++{
++      struct change_pre_exec_data *data = arg;
++
++      os_close_file(data->close_me);
++      dup2(data->stdout, 1);
++}
++
++static int change_tramp(char **argv, char *output, int output_len)
++{
++      int pid, fds[2], err;
++      struct change_pre_exec_data pe_data;
++
++      err = os_pipe(fds, 1, 0);
++      if(err < 0){
++              printk("change_tramp - pipe failed, err = %d\n", -err);
++              return(err);
++      }
++      pe_data.close_me = fds[0];
++      pe_data.stdout = fds[1];
++      pid = run_helper(change_pre_exec, &pe_data, argv, NULL);
++
++      read_output(fds[0], output, output_len);
++      os_close_file(fds[0]);
++      os_close_file(fds[1]);
++      CATCH_EINTR(waitpid(pid, NULL, 0));     
++      return(pid);
++}
++
++static void change(char *dev, char *what, unsigned char *addr,
++                 unsigned char *netmask)
++{
++      char addr_buf[sizeof("255.255.255.255\0")];
++      char netmask_buf[sizeof("255.255.255.255\0")];
++      char version[sizeof("nnnnn\0")];
++      char *argv[] = { "uml_net", version, what, dev, addr_buf, 
++                       netmask_buf, NULL };
++      char *output;
++      int output_len, pid;
++
++      sprintf(version, "%d", UML_NET_VERSION);
++      sprintf(addr_buf, "%d.%d.%d.%d", addr[0], addr[1], addr[2], addr[3]);
++      sprintf(netmask_buf, "%d.%d.%d.%d", netmask[0], netmask[1], 
++              netmask[2], netmask[3]);
++
++      output_len = page_size();
++      output = um_kmalloc(output_len);
++      if(output == NULL)
++              printk("change : failed to allocate output buffer\n");
++
++      pid = change_tramp(argv, output, output_len);
++      if(pid < 0) return;
++
++      if(output != NULL){
++              printk("%s", output);
++              kfree(output);
++      }
++}
++
++void open_addr(unsigned char *addr, unsigned char *netmask, void *arg)
++{
++      change(arg, "add", addr, netmask);
++}
++
++void close_addr(unsigned char *addr, unsigned char *netmask, void *arg)
++{
++      change(arg, "del", addr, netmask);
++}
++
++char *split_if_spec(char *str, ...)
++{
++      char **arg, *end;
++      va_list ap;
++
++      va_start(ap, str);
++      while((arg = va_arg(ap, char **)) != NULL){
++              if(*str == '\0')
++                      return(NULL);
++              end = strchr(str, ',');
++              if(end != str)
++                      *arg = str;
++              if(end == NULL)
++                      return(NULL);
++              *end++ = '\0';
++              str = end;
++      }
++      va_end(ap);
++      return(str);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/null.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/null.c   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/null.c        2005-05-03 22:28:14.230446856 +0300
+@@ -0,0 +1,55 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <stdlib.h>
++#include <errno.h>
++#include "chan_user.h"
++#include "os.h"
++
++static int null_chan;
++
++void *null_init(char *str, int device, struct chan_opts *opts)
++{
++      return(&null_chan);
++}
++
++int null_open(int input, int output, int primary, void *d, char **dev_out)
++{
++      *dev_out = NULL;
++      return(os_open_file(DEV_NULL, of_rdwr(OPENFLAGS()), 0));
++}
++
++int null_read(int fd, char *c_out, void *unused)
++{
++      return(-ENODEV);
++}
++
++void null_free(void *data)
++{
++}
++
++struct chan_ops null_ops = {
++      .type           = "null",
++      .init           = null_init,
++      .open           = null_open,
++      .close          = generic_close,
++      .read           = null_read,
++      .write          = generic_write,
++      .console_write  = generic_console_write,
++      .window_size    = generic_window_size,
++      .free           = null_free,
++      .winch          = 0,
++};
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/pcap_kern.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/pcap_kern.c      1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/pcap_kern.c   2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,127 @@
++/*
++ * Copyright (C) 2002 Jeff Dike <jdike@karaya.com>
++ * Licensed under the GPL.
++ */
++
++#include "linux/init.h"
++#include "linux/netdevice.h"
++#include "linux/etherdevice.h"
++#include "net_kern.h"
++#include "net_user.h"
++#include "pcap_user.h"
++
++struct pcap_init {
++      char *host_if;
++      int promisc;
++      int optimize;
++      char *filter;
++};
++
++void pcap_init(struct net_device *dev, void *data)
++{
++      struct uml_net_private *pri;
++      struct pcap_data *ppri;
++      struct pcap_init *init = data;
++
++      init_etherdev(dev, 0);
++      pri = dev->priv;
++      ppri = (struct pcap_data *) pri->user;
++      *ppri = ((struct pcap_data)
++              { .host_if      = init->host_if,
++                .promisc      = init->promisc,
++                .optimize     = init->optimize,
++                .filter       = init->filter,
++                .compiled     = NULL,
++                .pcap         = NULL });
++}
++
++static int pcap_read(int fd, struct sk_buff **skb, 
++                     struct uml_net_private *lp)
++{
++      *skb = ether_adjust_skb(*skb, ETH_HEADER_OTHER);
++      if(*skb == NULL) return(-ENOMEM);
++      return(pcap_user_read(fd, (*skb)->mac.raw, 
++                            (*skb)->dev->mtu + ETH_HEADER_OTHER,
++                            (struct pcap_data *) &lp->user));
++}
++
++static int pcap_write(int fd, struct sk_buff **skb, struct uml_net_private *lp)
++{
++      return(-EPERM);
++}
++
++static struct net_kern_info pcap_kern_info = {
++      .init                   = pcap_init,
++      .protocol               = eth_protocol,
++      .read                   = pcap_read,
++      .write                  = pcap_write,
++};
++
++int pcap_setup(char *str, char **mac_out, void *data)
++{
++      struct pcap_init *init = data;
++      char *remain, *host_if = NULL, *options[2] = { NULL, NULL };
++      int i;
++
++      *init = ((struct pcap_init)
++              { .host_if      = "eth0",
++                .promisc      = 1,
++                .optimize     = 0,
++                .filter       = NULL });
++
++      remain = split_if_spec(str, &host_if, &init->filter, 
++                             &options[0], &options[1], NULL);
++      if(remain != NULL){
++              printk(KERN_ERR "pcap_setup - Extra garbage on "
++                     "specification : '%s'\n", remain);
++              return(0);
++      }
++
++      if(host_if != NULL)
++              init->host_if = host_if;
++
++      for(i = 0; i < sizeof(options)/sizeof(options[0]); i++){
++              if(options[i] == NULL)
++                      continue;
++              if(!strcmp(options[i], "promisc"))
++                      init->promisc = 1;
++              else if(!strcmp(options[i], "nopromisc"))
++                      init->promisc = 0;
++              else if(!strcmp(options[i], "optimize"))
++                      init->optimize = 1;
++              else if(!strcmp(options[i], "nooptimize"))
++                      init->optimize = 0;
++              else printk("pcap_setup : bad option - '%s'\n", options[i]);
++      }
++
++      return(1);
++}
++
++static struct transport pcap_transport = {
++      .list           = LIST_HEAD_INIT(pcap_transport.list),
++      .name           = "pcap",
++      .setup          = pcap_setup,
++      .user           = &pcap_user_info,
++      .kern           = &pcap_kern_info,
++      .private_size   = sizeof(struct pcap_data),
++      .setup_size     = sizeof(struct pcap_init),
++};
++
++static int register_pcap(void)
++{
++      register_transport(&pcap_transport);
++      return(1);
++}
++
++__initcall(register_pcap);
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/pcap_user.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/pcap_user.c      1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/pcap_user.c   2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,143 @@
++/*
++ * Copyright (C) 2002 Jeff Dike <jdike@karaya.com>
++ * Licensed under the GPL.
++ */
++
++#include <unistd.h>
++#include <stdlib.h>
++#include <string.h>
++#include <errno.h>
++#include <pcap.h>
++#include <asm/types.h>
++#include "net_user.h"
++#include "pcap_user.h"
++#include "user.h"
++
++#define MAX_PACKET (ETH_MAX_PACKET + ETH_HEADER_OTHER)
++
++#define PCAP_FD(p) (*(int *)(p))
++
++static void pcap_user_init(void *data, void *dev)
++{
++      struct pcap_data *pri = data;
++      pcap_t *p;
++      char errors[PCAP_ERRBUF_SIZE];
++
++      p = pcap_open_live(pri->host_if, MAX_PACKET, pri->promisc, 0, errors);
++      if(p == NULL){
++              printk("pcap_user_init : pcap_open_live failed - '%s'\n", 
++                     errors);
++              return;
++      }
++
++      pri->dev = dev;
++      pri->pcap = p;
++}
++
++static int pcap_open(void *data)
++{
++      struct pcap_data *pri = data;
++      __u32 netmask;
++      int err;
++
++      if(pri->pcap == NULL)
++              return(-ENODEV);
++
++      if(pri->filter != NULL){
++              err = dev_netmask(pri->dev, &netmask);
++              if(err < 0){
++                      printk("pcap_open : dev_netmask failed\n");
++                      return(-EIO);
++              }
++
++              pri->compiled = um_kmalloc(sizeof(struct bpf_program));
++              if(pri->compiled == NULL){
++                      printk("pcap_open : kmalloc failed\n");
++                      return(-ENOMEM);
++              }
++              
++              err = pcap_compile(pri->pcap, 
++                                 (struct bpf_program *) pri->compiled, 
++                                 pri->filter, pri->optimize, netmask);
++              if(err < 0){
++                      printk("pcap_open : pcap_compile failed - '%s'\n", 
++                             pcap_geterr(pri->pcap));
++                      return(-EIO);
++              }
++
++              err = pcap_setfilter(pri->pcap, pri->compiled);
++              if(err < 0){
++                      printk("pcap_open : pcap_setfilter failed - '%s'\n", 
++                             pcap_geterr(pri->pcap));
++                      return(-EIO);
++              }
++      }
++      
++      return(PCAP_FD(pri->pcap));
++}
++
++static void pcap_remove(void *data)
++{
++      struct pcap_data *pri = data;
++
++      if(pri->compiled != NULL)
++              pcap_freecode(pri->compiled);
++
++      pcap_close(pri->pcap);
++}
++
++struct pcap_handler_data {
++      char *buffer;
++      int len;
++};
++
++static void handler(u_char *data, const struct pcap_pkthdr *header, 
++                  const u_char *packet)
++{
++      int len;
++
++      struct pcap_handler_data *hdata = (struct pcap_handler_data *) data;
++
++      len = hdata->len < header->caplen ? hdata->len : header->caplen;
++      memcpy(hdata->buffer, packet, len);
++      hdata->len = len;
++}
++
++int pcap_user_read(int fd, void *buffer, int len, struct pcap_data *pri)
++{
++      struct pcap_handler_data hdata = ((struct pcap_handler_data)
++                                        { .buffer     = buffer,
++                                          .len        = len });
++      int n;
++
++      n = pcap_dispatch(pri->pcap, 1, handler, (u_char *) &hdata);
++      if(n < 0){
++              printk("pcap_dispatch failed - %s\n", pcap_geterr(pri->pcap));
++              return(-EIO);
++      }
++      else if(n == 0) 
++              return(0);
++      return(hdata.len);
++}
++
++struct net_user_info pcap_user_info = {
++      .init           = pcap_user_init,
++      .open           = pcap_open,
++      .close          = NULL,
++      .remove         = pcap_remove,
++      .set_mtu        = NULL,
++      .add_address    = NULL,
++      .delete_address = NULL,
++      .max_packet     = MAX_PACKET - ETH_HEADER_OTHER
++};
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/pcap_user.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/pcap_user.h      1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/pcap_user.h   2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,31 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "net_user.h"
++
++struct pcap_data {
++      char *host_if;
++      int promisc;
++      int optimize;
++      char *filter;
++      void *compiled;
++      void *pcap;
++      void *dev;
++};
++
++extern struct net_user_info pcap_user_info;
++
++extern int pcap_user_read(int fd, void *buf, int len, struct pcap_data *pri);
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/port.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/port.h   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/port.h        2005-05-03 22:28:14.234446248 +0300
+@@ -0,0 +1,30 @@
++/* 
++ * Copyright (C) 2001 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __PORT_H__
++#define __PORT_H__
++
++extern void *port_data(int port);
++extern int port_wait(void *data);
++extern void port_kern_close(void *d);
++extern int port_connection(int fd, int *socket_out, int *pid_out);
++extern int port_listen_fd(int port);
++extern void port_read(int fd, void *data);
++extern void port_kern_free(void *d);
++extern int port_rcv_fd(int fd);
++extern void port_remove_dev(void *d);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/port_kern.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/port_kern.c      1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/port_kern.c   2005-05-03 22:28:14.235446096 +0300
+@@ -0,0 +1,303 @@
++/* 
++ * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/list.h"
++#include "linux/sched.h"
++#include "linux/slab.h"
++#include "linux/irq.h"
++#include "linux/spinlock.h"
++#include "linux/errno.h"
++#include "asm/semaphore.h"
++#include "asm/errno.h"
++#include "kern_util.h"
++#include "kern.h"
++#include "irq_user.h"
++#include "irq_kern.h"
++#include "port.h"
++#include "init.h"
++#include "os.h"
++
++struct port_list {
++      struct list_head list;
++      int has_connection;
++      struct semaphore sem;
++      int port;
++      int fd;
++      spinlock_t lock;
++      struct list_head pending;
++      struct list_head connections;
++};
++
++struct port_dev {
++      struct port_list *port;
++      int helper_pid;
++      int telnetd_pid;
++};
++
++struct connection {
++      struct list_head list;
++      int fd;
++      int helper_pid;
++      int socket[2];
++      int telnetd_pid;
++      struct port_list *port;
++};
++
++static void pipe_interrupt(int irq, void *data, struct pt_regs *regs)
++{
++      struct connection *conn = data;
++      int fd;
++
++      fd = os_rcv_fd(conn->socket[0], &conn->helper_pid);
++      if(fd < 0){
++              if(fd == -EAGAIN)
++                      return;
++
++              printk(KERN_ERR "pipe_interrupt : os_rcv_fd returned %d\n", 
++                     -fd);
++              os_close_file(conn->fd);
++      }
++
++      list_del(&conn->list);
++
++      conn->fd = fd;
++      list_add(&conn->list, &conn->port->connections);
++
++      up(&conn->port->sem);
++}
++
++static int port_accept(struct port_list *port)
++{
++      struct connection *conn;
++      int fd, socket[2], pid, ret = 0;
++
++      fd = port_connection(port->fd, socket, &pid);
++      if(fd < 0){
++              if(fd != -EAGAIN)
++                      printk(KERN_ERR "port_accept : port_connection "
++                             "returned %d\n", -fd);
++              goto out;
++      }
++
++      conn = kmalloc(sizeof(*conn), GFP_ATOMIC);
++      if(conn == NULL){
++              printk(KERN_ERR "port_accept : failed to allocate "
++                     "connection\n");
++              goto out_close;
++      }
++      *conn = ((struct connection) 
++              { .list         = LIST_HEAD_INIT(conn->list),
++                .fd           = fd,
++                .socket       = { socket[0], socket[1] },
++                .telnetd_pid  = pid,
++                .port         = port });
++
++      if(um_request_irq(TELNETD_IRQ, socket[0], IRQ_READ, pipe_interrupt, 
++                        SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM, 
++                        "telnetd", conn)){
++              printk(KERN_ERR "port_accept : failed to get IRQ for "
++                     "telnetd\n");
++              goto out_free;
++      }
++
++      list_add(&conn->list, &port->pending);
++      return(1);
++
++ out_free:
++      kfree(conn);
++ out_close:
++      os_close_file(fd);
++      if(pid != -1) 
++              os_kill_process(pid, 1);
++ out:
++      return(ret);
++} 
++
++DECLARE_MUTEX(ports_sem);
++struct list_head ports = LIST_HEAD_INIT(ports);
++
++void port_task_proc(void *unused)
++{
++      struct port_list *port;
++      struct list_head *ele;
++      unsigned long flags;
++
++      save_flags(flags);
++      list_for_each(ele, &ports){
++              port = list_entry(ele, struct port_list, list);
++              if(!port->has_connection)
++                      continue;
++              reactivate_fd(port->fd, ACCEPT_IRQ);
++              while(port_accept(port)) ;
++              port->has_connection = 0;
++      }
++      restore_flags(flags);
++}
++
++struct tq_struct port_task = {
++      .routine        = port_task_proc,
++      .data           = NULL
++};
++
++static void port_interrupt(int irq, void *data, struct pt_regs *regs)
++{
++      struct port_list *port = data;
++
++      port->has_connection = 1;
++      schedule_task(&port_task);
++} 
++
++void *port_data(int port_num)
++{
++      struct list_head *ele;
++      struct port_list *port;
++      struct port_dev *dev = NULL;
++      int fd;
++
++      down(&ports_sem);
++      list_for_each(ele, &ports){
++              port = list_entry(ele, struct port_list, list);
++              if(port->port == port_num) goto found;
++      }
++      port = kmalloc(sizeof(struct port_list), GFP_KERNEL);
++      if(port == NULL){
++              printk(KERN_ERR "Allocation of port list failed\n");
++              goto out;
++      }
++
++      fd = port_listen_fd(port_num);
++      if(fd < 0){
++              printk(KERN_ERR "binding to port %d failed, errno = %d\n",
++                     port_num, -fd);
++              goto out_free;
++      }
++      if(um_request_irq(ACCEPT_IRQ, fd, IRQ_READ, port_interrupt, 
++                        SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM, "port",
++                        port)){
++              printk(KERN_ERR "Failed to get IRQ for port %d\n", port_num);
++              goto out_close;
++      }
++
++      *port = ((struct port_list) 
++              { .list                 = LIST_HEAD_INIT(port->list),
++                .has_connection       = 0,
++                .sem                  = __SEMAPHORE_INITIALIZER(port->sem, 
++                                                                0),
++                .lock                 = SPIN_LOCK_UNLOCKED,
++                .port                 = port_num,
++                .fd                   = fd,
++                .pending              = LIST_HEAD_INIT(port->pending),
++                .connections          = LIST_HEAD_INIT(port->connections) });
++      list_add(&port->list, &ports);
++
++ found:
++      dev = kmalloc(sizeof(struct port_dev), GFP_KERNEL);
++      if(dev == NULL){
++              printk(KERN_ERR "Allocation of port device entry failed\n");
++              goto out;
++      }
++
++      *dev = ((struct port_dev) { .port               = port,
++                                  .helper_pid         = -1,
++                                  .telnetd_pid        = -1 });
++      goto out;
++
++ out_free:
++      kfree(port);
++ out_close:
++      os_close_file(fd);
++ out:
++      up(&ports_sem);
++      return(dev);
++}
++
++int port_wait(void *data)
++{
++      struct port_dev *dev = data;
++      struct connection *conn;
++      struct port_list *port = dev->port;
++      int fd;
++
++      while(1){
++              if(down_interruptible(&port->sem))
++                      return(-ERESTARTSYS);
++
++              spin_lock(&port->lock);
++
++              conn = list_entry(port->connections.next, struct connection, 
++                                list);
++              list_del(&conn->list);
++              spin_unlock(&port->lock);
++
++              os_shutdown_socket(conn->socket[0], 1, 1);
++              os_close_file(conn->socket[0]);
++              os_shutdown_socket(conn->socket[1], 1, 1);
++              os_close_file(conn->socket[1]); 
++
++              /* This is done here because freeing an IRQ can't be done
++               * within the IRQ handler.  So, pipe_interrupt always ups
++               * the semaphore regardless of whether it got a successful
++               * connection.  Then we loop here throwing out failed 
++               * connections until a good one is found.
++               */
++              free_irq(TELNETD_IRQ, conn);
++
++              if(conn->fd >= 0) break;
++              os_close_file(conn->fd);
++              kfree(conn);
++      }
++
++      fd = conn->fd;
++      dev->helper_pid = conn->helper_pid;
++      dev->telnetd_pid = conn->telnetd_pid;
++      kfree(conn);
++
++      return(fd);
++}
++
++void port_remove_dev(void *d)
++{
++      struct port_dev *dev = d;
++
++      if(dev->helper_pid != -1)
++              os_kill_process(dev->helper_pid, 0);
++      if(dev->telnetd_pid != -1)
++              os_kill_process(dev->telnetd_pid, 1);
++      dev->helper_pid = -1;
++      dev->telnetd_pid = -1;
++}
++
++void port_kern_free(void *d)
++{
++      struct port_dev *dev = d;
++
++      port_remove_dev(dev);
++      kfree(dev);
++}
++
++static void free_port(void)
++{
++      struct list_head *ele;
++      struct port_list *port;
++
++      list_for_each(ele, &ports){
++              port = list_entry(ele, struct port_list, list);
++              free_irq_by_fd(port->fd);
++              os_close_file(port->fd);
++      }
++}
++
++__uml_exitcall(free_port);
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/port_user.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/port_user.c      1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/port_user.c   2005-05-03 22:28:14.237445792 +0300
+@@ -0,0 +1,224 @@
++/* 
++ * Copyright (C) 2001 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <stdio.h>
++#include <stddef.h>
++#include <stdlib.h>
++#include <string.h>
++#include <errno.h>
++#include <unistd.h>
++#include <termios.h>
++#include <sys/socket.h>
++#include <sys/un.h>
++#include <netinet/in.h>
++#include "user_util.h"
++#include "kern_util.h"
++#include "user.h"
++#include "chan_user.h"
++#include "port.h"
++#include "helper.h"
++#include "os.h"
++
++struct port_chan {
++      int raw;
++      struct termios tt;
++      void *kernel_data;
++      char dev[sizeof("32768\0")];
++};
++
++void *port_init(char *str, int device, struct chan_opts *opts)
++{
++      struct port_chan *data;
++      void *kern_data;
++      char *end;
++      int port;
++
++      if(*str != ':'){
++              printk("port_init : channel type 'port' must specify a "
++                     "port number\n");
++              return(NULL);
++      }
++      str++;
++      port = strtoul(str, &end, 0);
++      if((*end != '\0') || (end == str)){
++              printk("port_init : couldn't parse port '%s'\n", str);
++              return(NULL);
++      }
++
++      kern_data = port_data(port);
++      if(kern_data == NULL) 
++              return(NULL);
++
++      data = um_kmalloc(sizeof(*data));
++      if(data == NULL) 
++              goto err;
++
++      *data = ((struct port_chan) { .raw              = opts->raw,
++                                    .kernel_data      = kern_data });
++      sprintf(data->dev, "%d", port);
++
++      return(data);
++ err:
++      port_kern_free(kern_data);
++      return(NULL);
++}
++
++void port_free(void *d)
++{
++      struct port_chan *data = d;
++
++      port_kern_free(data->kernel_data);
++      kfree(data);
++}
++
++int port_open(int input, int output, int primary, void *d, char **dev_out)
++{
++      struct port_chan *data = d;
++      int fd, err;
++
++      fd = port_wait(data->kernel_data);
++      if((fd >= 0) && data->raw){
++              CATCH_EINTR(err = tcgetattr(fd, &data->tt));
++              if(err)
++                      return(err);
++
++              err = raw(fd);
++              if(err)
++                      return(err);
++      }
++      *dev_out = data->dev;
++      return(fd);
++}
++
++void port_close(int fd, void *d)
++{
++      struct port_chan *data = d;
++
++      port_remove_dev(data->kernel_data);
++      os_close_file(fd);
++}
++
++int port_console_write(int fd, const char *buf, int n, void *d)
++{
++      struct port_chan *data = d;
++
++      return(generic_console_write(fd, buf, n, &data->tt));
++}
++
++struct chan_ops port_ops = {
++      .type           = "port",
++      .init           = port_init,
++      .open           = port_open,
++      .close          = port_close,
++      .read           = generic_read,
++      .write          = generic_write,
++      .console_write  = port_console_write,
++      .window_size    = generic_window_size,
++      .free           = port_free,
++      .winch          = 1,
++};
++
++int port_listen_fd(int port)
++{
++      struct sockaddr_in addr;
++      int fd, err, arg;
++
++      fd = socket(PF_INET, SOCK_STREAM, 0);
++      if(fd == -1) 
++              return(-errno);
++
++      arg = 1;
++      if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &arg, sizeof(arg)) < 0){
++              err = -errno;
++              goto out;
++      }
++
++      addr.sin_family = AF_INET;
++      addr.sin_port = htons(port);
++      addr.sin_addr.s_addr = htonl(INADDR_ANY);
++      if(bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0){
++              err = -errno;
++              goto out;
++      }
++  
++      if(listen(fd, 1) < 0){
++              err = -errno;
++              goto out;
++      }
++
++      err = os_set_fd_block(fd, 0);
++      if(err < 0)
++              goto out;
++
++      return(fd);
++ out:
++      os_close_file(fd);
++      return(err);
++}
++
++struct port_pre_exec_data {
++      int sock_fd;
++      int pipe_fd;
++};
++
++void port_pre_exec(void *arg)
++{
++      struct port_pre_exec_data *data = arg;
++
++      dup2(data->sock_fd, 0);
++      dup2(data->sock_fd, 1);
++      dup2(data->sock_fd, 2);
++      os_close_file(data->sock_fd);
++      dup2(data->pipe_fd, 3);
++      os_shutdown_socket(3, 1, 0);
++      os_close_file(data->pipe_fd);
++}
++
++int port_connection(int fd, int *socket, int *pid_out)
++{
++      int new, err;
++      char *argv[] = { "/usr/sbin/in.telnetd", "-L", 
++                       "/usr/lib/uml/port-helper", NULL };
++      struct port_pre_exec_data data;
++
++      new = os_accept_connection(fd);
++      if(new < 0)
++              return(new);
++
++      err = os_pipe(socket, 0, 0);
++      if(err < 0) 
++              goto out_close;
++
++      data = ((struct port_pre_exec_data)
++              { .sock_fd              = new,
++                .pipe_fd              = socket[1] });
++
++      err = run_helper(port_pre_exec, &data, argv, NULL);
++      if(err < 0) 
++              goto out_shutdown;
++
++      *pid_out = err;
++      return(new);
++
++ out_shutdown:
++      os_shutdown_socket(socket[0], 1, 1);
++      os_close_file(socket[0]);
++      os_shutdown_socket(socket[1], 1, 1);    
++      os_close_file(socket[1]);
++ out_close:
++      os_close_file(new);
++      return(err);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/pty.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/pty.c    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/pty.c 2005-05-03 22:28:14.238445640 +0300
+@@ -0,0 +1,159 @@
++/* 
++ * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <stdio.h>
++#include <unistd.h>
++#include <string.h>
++#include <errno.h>
++#include <termios.h>
++#include "chan_user.h"
++#include "user.h"
++#include "user_util.h"
++#include "kern_util.h"
++#include "os.h"
++
++struct pty_chan {
++      void (*announce)(char *dev_name, int dev);
++      int dev;
++      int raw;
++      struct termios tt;
++      char dev_name[sizeof("/dev/pts/0123456\0")];
++};
++
++void *pty_chan_init(char *str, int device, struct chan_opts *opts)
++{
++      struct pty_chan *data;
++
++      data = um_kmalloc(sizeof(*data));
++      if(data == NULL) return(NULL);
++      *data = ((struct pty_chan) { .announce          = opts->announce, 
++                                   .dev               = device,
++                                   .raw               = opts->raw });
++      return(data);
++}
++
++int pts_open(int input, int output, int primary, void *d, char **dev_out)
++{
++      struct pty_chan *data = d;
++      char *dev;
++      int fd, err;
++
++      fd = get_pty();
++      if(fd < 0){
++              printk("open_pts : Failed to open pts\n");
++              return(-errno);
++      }
++      if(data->raw){
++              CATCH_EINTR(err = tcgetattr(fd, &data->tt));
++              if(err)
++                      return(err);
++
++              err = raw(fd);
++              if(err)
++                      return(err);
++      }
++
++      dev = ptsname(fd);
++      sprintf(data->dev_name, "%s", dev);
++      *dev_out = data->dev_name;
++      if(data->announce) (*data->announce)(dev, data->dev);
++      return(fd);
++}
++
++int getmaster(char *line)
++{
++      char *pty, *bank, *cp;
++      int master, err;
++
++      pty = &line[strlen("/dev/ptyp")];
++      for (bank = "pqrs"; *bank; bank++) {
++              line[strlen("/dev/pty")] = *bank;
++              *pty = '0';
++              if (os_stat_file(line, NULL) < 0)
++                      break;
++              for (cp = "0123456789abcdef"; *cp; cp++) {
++                      *pty = *cp;
++                      master = os_open_file(line, of_rdwr(OPENFLAGS()), 0);
++                      if (master >= 0) {
++                              char *tp = &line[strlen("/dev/")];
++
++                              /* verify slave side is usable */
++                              *tp = 't';
++                              err = os_access(line, OS_ACC_RW_OK);
++                              *tp = 'p';
++                              if(err == 0) return(master);
++                              (void) os_close_file(master);
++                      }
++              }
++      }
++      return(-1);
++}
++
++int pty_open(int input, int output, int primary, void *d, char **dev_out)
++{
++      struct pty_chan *data = d;
++      int fd, err;
++      char dev[sizeof("/dev/ptyxx\0")] = "/dev/ptyxx";
++
++      fd = getmaster(dev);
++      if(fd < 0) 
++              return(-errno);
++      
++      if(data->raw){
++              err = raw(fd);
++              if(err)
++                      return(err);
++      }
++      
++      if(data->announce) (*data->announce)(dev, data->dev);
++
++      sprintf(data->dev_name, "%s", dev);
++      *dev_out = data->dev_name;
++      return(fd);
++}
++
++int pty_console_write(int fd, const char *buf, int n, void *d)
++{
++      struct pty_chan *data = d;
++
++      return(generic_console_write(fd, buf, n, &data->tt));
++}
++
++struct chan_ops pty_ops = {
++      .type           = "pty",
++      .init           = pty_chan_init,
++      .open           = pty_open,
++      .close          = generic_close,
++      .read           = generic_read,
++      .write          = generic_write,
++      .console_write  = pty_console_write,
++      .window_size    = generic_window_size,
++      .free           = generic_free,
++      .winch          = 0,
++};
++
++struct chan_ops pts_ops = {
++      .type           = "pts",
++      .init           = pty_chan_init,
++      .open           = pts_open,
++      .close          = generic_close,
++      .read           = generic_read,
++      .write          = generic_write,
++      .console_write  = pty_console_write,
++      .window_size    = generic_window_size,
++      .free           = generic_free,
++      .winch          = 0,
++};
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/slip.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/slip.h   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/slip.h        2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,39 @@
++#ifndef __UM_SLIP_H
++#define __UM_SLIP_H
++
++#define BUF_SIZE 1500
++ /* two bytes each for a (pathological) max packet of escaped chars +  * 
++  * terminating END char + initial END char                            */
++#define ENC_BUF_SIZE (2 * BUF_SIZE + 2)
++
++struct slip_data {
++      void *dev;
++      char name[sizeof("slnnnnn\0")];
++      char *addr;
++      char *gate_addr;
++      int slave;
++      char ibuf[ENC_BUF_SIZE];
++      char obuf[ENC_BUF_SIZE];
++      int more; /* more data: do not read fd until ibuf has been drained */
++      int pos;
++      int esc;
++};
++
++extern struct net_user_info slip_user_info;
++
++extern int set_umn_addr(int fd, char *addr, char *ptp_addr);
++extern int slip_user_read(int fd, void *buf, int len, struct slip_data *pri);
++extern int slip_user_write(int fd, void *buf, int len, struct slip_data *pri);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/slip_kern.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/slip_kern.c      1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/slip_kern.c   2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,109 @@
++#include "linux/config.h"
++#include "linux/kernel.h"
++#include "linux/stddef.h"
++#include "linux/init.h"
++#include "linux/netdevice.h"
++#include "linux/if_arp.h"
++#include "net_kern.h"
++#include "net_user.h"
++#include "kern.h"
++#include "slip.h"
++
++struct slip_init {
++      char *gate_addr;
++};
++
++void slip_init(struct net_device *dev, void *data)
++{
++      struct uml_net_private *private;
++      struct slip_data *spri;
++      struct slip_init *init = data;
++
++      private = dev->priv;
++      spri = (struct slip_data *) private->user;
++      *spri = ((struct slip_data)
++              { .name         = { '\0' },
++                .addr         = NULL,
++                .gate_addr    = init->gate_addr,
++                .slave        = -1,
++                .ibuf         = { '\0' },
++                .obuf         = { '\0' },
++                .pos          = 0,
++                .esc          = 0,
++                .dev          = dev });
++
++      dev->init = NULL;
++      dev->hard_header_len = 0;
++      dev->addr_len = 4;
++      dev->type = ARPHRD_ETHER;
++      dev->tx_queue_len = 256;
++      dev->flags = IFF_NOARP;
++      printk("SLIP backend - SLIP IP = %s\n", spri->gate_addr);
++}
++
++static unsigned short slip_protocol(struct sk_buff *skbuff)
++{
++      return(htons(ETH_P_IP));
++}
++
++static int slip_read(int fd, struct sk_buff **skb, 
++                     struct uml_net_private *lp)
++{
++      return(slip_user_read(fd, (*skb)->mac.raw, (*skb)->dev->mtu, 
++                            (struct slip_data *) &lp->user));
++}
++
++static int slip_write(int fd, struct sk_buff **skb,
++                    struct uml_net_private *lp)
++{
++      return(slip_user_write(fd, (*skb)->data, (*skb)->len, 
++                             (struct slip_data *) &lp->user));
++}
++
++struct net_kern_info slip_kern_info = {
++      .init                   = slip_init,
++      .protocol               = slip_protocol,
++      .read                   = slip_read,
++      .write                  = slip_write,
++};
++
++static int slip_setup(char *str, char **mac_out, void *data)
++{
++      struct slip_init *init = data;
++
++      *init = ((struct slip_init)
++              { .gate_addr            = NULL });
++
++      if(str[0] != '\0') 
++              init->gate_addr = str;
++      return(1);
++}
++
++static struct transport slip_transport = {
++      .list           = LIST_HEAD_INIT(slip_transport.list),
++      .name           = "slip",
++      .setup          = slip_setup,
++      .user           = &slip_user_info,
++      .kern           = &slip_kern_info,
++      .private_size   = sizeof(struct slip_data),
++      .setup_size     = sizeof(struct slip_init),
++};
++
++static int register_slip(void)
++{
++      register_transport(&slip_transport);
++      return(1);
++}
++
++__initcall(register_slip);
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/slip_proto.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/slip_proto.h     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/slip_proto.h  2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,93 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __UM_SLIP_PROTO_H__
++#define __UM_SLIP_PROTO_H__
++
++/* SLIP protocol characters. */
++#define SLIP_END             0300     /* indicates end of frame       */
++#define SLIP_ESC             0333     /* indicates byte stuffing      */
++#define SLIP_ESC_END         0334     /* ESC ESC_END means END 'data' */
++#define SLIP_ESC_ESC         0335     /* ESC ESC_ESC means ESC 'data' */
++
++static inline int slip_unesc(unsigned char c,char *buf,int *pos, int *esc)
++{
++      int ret;
++
++      switch(c){
++      case SLIP_END:
++              *esc = 0;
++              ret=*pos;
++              *pos=0;
++              return(ret);
++      case SLIP_ESC:
++              *esc = 1;
++              return(0);
++      case SLIP_ESC_ESC:
++              if(*esc){
++                      *esc = 0;
++                      c = SLIP_ESC;
++              }
++              break;
++      case SLIP_ESC_END:
++              if(*esc){
++                      *esc = 0;
++                      c = SLIP_END;
++              }
++              break;
++      }
++      buf[(*pos)++] = c;
++      return(0);
++}
++
++static inline int slip_esc(unsigned char *s, unsigned char *d, int len)
++{
++      unsigned char *ptr = d;
++      unsigned char c;
++
++      /*
++       * Send an initial END character to flush out any
++       * data that may have accumulated in the receiver
++       * due to line noise.
++       */
++
++      *ptr++ = SLIP_END;
++
++      /*
++       * For each byte in the packet, send the appropriate
++       * character sequence, according to the SLIP protocol.
++       */
++
++      while (len-- > 0) {
++              switch(c = *s++) {
++              case SLIP_END:
++                      *ptr++ = SLIP_ESC;
++                      *ptr++ = SLIP_ESC_END;
++                      break;
++              case SLIP_ESC:
++                      *ptr++ = SLIP_ESC;
++                      *ptr++ = SLIP_ESC_ESC;
++                      break;
++              default:
++                      *ptr++ = c;
++                      break;
++              }
++      }
++      *ptr++ = SLIP_END;
++      return (ptr - d);
++}
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/slip_user.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/slip_user.c      1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/slip_user.c   2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,276 @@
++#include <stdio.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <stddef.h>
++#include <sched.h>
++#include <string.h>
++#include <errno.h>
++#include <sys/termios.h>
++#include <sys/wait.h>
++#include <sys/signal.h>
++#include "user_util.h"
++#include "kern_util.h"
++#include "user.h"
++#include "net_user.h"
++#include "slip.h"
++#include "slip_proto.h"
++#include "helper.h"
++#include "os.h"
++
++void slip_user_init(void *data, void *dev)
++{
++      struct slip_data *pri = data;
++
++      pri->dev = dev;
++}
++
++static int set_up_tty(int fd)
++{
++      int i;
++      struct termios tios;
++
++      if (tcgetattr(fd, &tios) < 0) {
++              printk("could not get initial terminal attributes\n");
++              return(-1);
++      }
++
++      tios.c_cflag = CS8 | CREAD | HUPCL | CLOCAL;
++      tios.c_iflag = IGNBRK | IGNPAR;
++      tios.c_oflag = 0;
++      tios.c_lflag = 0;
++      for (i = 0; i < NCCS; i++)
++              tios.c_cc[i] = 0;
++      tios.c_cc[VMIN] = 1;
++      tios.c_cc[VTIME] = 0;
++
++      cfsetospeed(&tios, B38400);
++      cfsetispeed(&tios, B38400);
++
++      if (tcsetattr(fd, TCSAFLUSH, &tios) < 0) {
++              printk("failed to set terminal attributes\n");
++              return(-1);
++      }
++      return(0);
++}
++
++struct slip_pre_exec_data {
++      int stdin;
++      int stdout;
++      int close_me;
++};
++
++static void slip_pre_exec(void *arg)
++{
++      struct slip_pre_exec_data *data = arg;
++
++      if(data->stdin >= 0) dup2(data->stdin, 0);
++      dup2(data->stdout, 1);
++      if(data->close_me >= 0) os_close_file(data->close_me);
++}
++
++static int slip_tramp(char **argv, int fd)
++{
++      struct slip_pre_exec_data pe_data;
++      char *output;
++      int status, pid, fds[2], err, output_len;
++
++      err = os_pipe(fds, 1, 0);
++      if(err < 0){
++              printk("slip_tramp : pipe failed, err = %d\n", -err);
++              return(err);
++      }
++
++      err = 0;
++      pe_data.stdin = fd;
++      pe_data.stdout = fds[1];
++      pe_data.close_me = fds[0];
++      pid = run_helper(slip_pre_exec, &pe_data, argv, NULL);
++
++      if(pid < 0) err = pid;
++      else {
++              output_len = page_size();
++              output = um_kmalloc(output_len);
++              if(output == NULL)
++                      printk("slip_tramp : failed to allocate output "
++                             "buffer\n");
++
++              os_close_file(fds[1]);
++              read_output(fds[0], output, output_len);
++              if(output != NULL){
++                      printk("%s", output);
++                      kfree(output);
++              }
++              CATCH_EINTR(err = waitpid(pid, &status, 0));
++              if(err < 0)
++                      err = errno;
++              else if(!WIFEXITED(status) || (WEXITSTATUS(status) != 0)){
++                      printk("'%s' didn't exit with status 0\n", argv[0]);
++                      err = -EINVAL;
++              }
++      }
++      return(err);
++}
++
++static int slip_open(void *data)
++{
++      struct slip_data *pri = data;
++      char version_buf[sizeof("nnnnn\0")];
++      char gate_buf[sizeof("nnn.nnn.nnn.nnn\0")];
++      char *argv[] = { "uml_net", version_buf, "slip", "up", gate_buf, 
++                       NULL };
++      int sfd, mfd, err;
++
++      mfd = get_pty();
++      if(mfd < 0){
++              printk("umn : Failed to open pty, err = %d\n", -mfd);
++              return(mfd);
++      }
++      sfd = os_open_file(ptsname(mfd), of_rdwr(OPENFLAGS()), 0);
++      if(sfd < 0){
++              printk("Couldn't open tty for slip line, err = %d\n", -sfd);
++              return(sfd);
++      }
++      if(set_up_tty(sfd)) return(-1);
++      pri->slave = sfd;
++      pri->pos = 0;
++      pri->esc = 0;
++      if(pri->gate_addr != NULL){
++              sprintf(version_buf, "%d", UML_NET_VERSION);
++              strcpy(gate_buf, pri->gate_addr);
++
++              err = slip_tramp(argv, sfd);
++
++              if(err < 0){
++                      printk("slip_tramp failed - err = %d\n", -err);
++                      return(err);
++              }
++              err = os_get_ifname(pri->slave, pri->name);
++              if(err < 0){
++                      printk("get_ifname failed, err = %d\n", -err);
++                      return(err);
++              }
++              iter_addresses(pri->dev, open_addr, pri->name);
++      }
++      else {
++              err = os_set_slip(sfd);
++              if(err < 0){
++                      printk("Failed to set slip discipline encapsulation - "
++                             "err = %d\n", -err);
++                      return(err);
++              }
++      }
++      return(mfd);
++}
++
++static void slip_close(int fd, void *data)
++{
++      struct slip_data *pri = data;
++      char version_buf[sizeof("nnnnn\0")];
++      char *argv[] = { "uml_net", version_buf, "slip", "down", pri->name, 
++                       NULL };
++      int err;
++
++      if(pri->gate_addr != NULL)
++              iter_addresses(pri->dev, close_addr, pri->name);
++
++      sprintf(version_buf, "%d", UML_NET_VERSION);
++
++      err = slip_tramp(argv, -1);
++
++      if(err != 0)
++              printk("slip_tramp failed - errno = %d\n", -err);
++      os_close_file(fd);
++      os_close_file(pri->slave);
++      pri->slave = -1;
++}
++
++int slip_user_read(int fd, void *buf, int len, struct slip_data *pri)
++{
++      int i, n, size, start;
++
++      if(pri->more>0) {
++              i = 0;
++              while(i < pri->more) {
++                      size = slip_unesc(pri->ibuf[i++],
++                                      pri->ibuf, &pri->pos, &pri->esc);
++                      if(size){
++                              memcpy(buf, pri->ibuf, size);
++                              memmove(pri->ibuf, &pri->ibuf[i], pri->more-i);
++                              pri->more=pri->more-i; 
++                              return(size);
++                      }
++              }
++              pri->more=0;
++      }
++
++      n = net_read(fd, &pri->ibuf[pri->pos], sizeof(pri->ibuf) - pri->pos);
++      if(n <= 0) return(n);
++
++      start = pri->pos;
++      for(i = 0; i < n; i++){
++              size = slip_unesc(pri->ibuf[start + i],
++                              pri->ibuf, &pri->pos, &pri->esc);
++              if(size){
++                      memcpy(buf, pri->ibuf, size);
++                      memmove(pri->ibuf, &pri->ibuf[start+i+1], n-(i+1));
++                      pri->more=n-(i+1); 
++                      return(size);
++              }
++      }
++      return(0);
++}
++
++int slip_user_write(int fd, void *buf, int len, struct slip_data *pri)
++{
++      int actual, n;
++
++      actual = slip_esc(buf, pri->obuf, len);
++      n = net_write(fd, pri->obuf, actual);
++      if(n < 0) return(n);
++      else return(len);
++}
++
++static int slip_set_mtu(int mtu, void *data)
++{
++      return(mtu);
++}
++
++static void slip_add_addr(unsigned char *addr, unsigned char *netmask,
++                        void *data)
++{
++      struct slip_data *pri = data;
++
++      if(pri->slave < 0) return;
++      open_addr(addr, netmask, pri->name);
++}
++
++static void slip_del_addr(unsigned char *addr, unsigned char *netmask,
++                          void *data)
++{
++      struct slip_data *pri = data;
++
++      if(pri->slave < 0) return;
++      close_addr(addr, netmask, pri->name);
++}
++
++struct net_user_info slip_user_info = {
++      .init           = slip_user_init,
++      .open           = slip_open,
++      .close          = slip_close,
++      .remove         = NULL,
++      .set_mtu        = slip_set_mtu,
++      .add_address    = slip_add_addr,
++      .delete_address = slip_del_addr,
++      .max_packet     = BUF_SIZE
++};
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/slirp.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/slirp.h  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/slirp.h       2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,51 @@
++#ifndef __UM_SLIRP_H
++#define __UM_SLIRP_H
++
++#define BUF_SIZE 1500
++ /* two bytes each for a (pathological) max packet of escaped chars +  * 
++  * terminating END char + initial END char                            */
++#define ENC_BUF_SIZE (2 * BUF_SIZE + 2)
++
++#define SLIRP_MAX_ARGS 100
++/*
++ * XXX this next definition is here because I don't understand why this
++ * initializer doesn't work in slirp_kern.c:
++ *
++ *   argv :  { init->argv[ 0 ... SLIRP_MAX_ARGS-1 ] },
++ *
++ * or why I can't typecast like this:
++ *
++ *   argv :  (char* [SLIRP_MAX_ARGS])(init->argv), 
++ */
++struct arg_list_dummy_wrapper { char *argv[SLIRP_MAX_ARGS]; };
++
++struct slirp_data {
++      void *dev;
++      struct arg_list_dummy_wrapper argw;
++      int pid;
++      int slave;
++      char ibuf[ENC_BUF_SIZE];
++      char obuf[ENC_BUF_SIZE];
++      int more; /* more data: do not read fd until ibuf has been drained */
++      int pos;
++      int esc;
++};
++
++extern struct net_user_info slirp_user_info;
++
++extern int set_umn_addr(int fd, char *addr, char *ptp_addr);
++extern int slirp_user_read(int fd, void *buf, int len, struct slirp_data *pri);
++extern int slirp_user_write(int fd, void *buf, int len, struct slirp_data *pri);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/slirp_kern.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/slirp_kern.c     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/slirp_kern.c  2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,132 @@
++#include "linux/kernel.h"
++#include "linux/stddef.h"
++#include "linux/init.h"
++#include "linux/netdevice.h"
++#include "linux/if_arp.h"
++#include "net_kern.h"
++#include "net_user.h"
++#include "kern.h"
++#include "slirp.h"
++
++struct slirp_init {
++      struct arg_list_dummy_wrapper argw;  /* XXX should be simpler... */
++};
++
++void slirp_init(struct net_device *dev, void *data)
++{
++      struct uml_net_private *private;
++      struct slirp_data *spri;
++      struct slirp_init *init = data;
++      int i;
++
++      private = dev->priv;
++      spri = (struct slirp_data *) private->user;
++      *spri = ((struct slirp_data)
++              { .argw         = init->argw,
++                .pid          = -1,
++                .slave        = -1,
++                .ibuf         = { '\0' },
++                .obuf         = { '\0' },
++                .pos          = 0,
++                .esc          = 0,
++                .dev          = dev });
++
++      dev->init = NULL;
++      dev->hard_header_len = 0;
++      dev->addr_len = 4;
++      dev->type = ARPHRD_ETHER;
++      dev->tx_queue_len = 256;
++      dev->flags = IFF_NOARP;
++      printk("SLIRP backend - command line:");
++      for(i=0;spri->argw.argv[i]!=NULL;i++) {
++              printk(" '%s'",spri->argw.argv[i]);
++      }
++      printk("\n");
++}
++
++static unsigned short slirp_protocol(struct sk_buff *skbuff)
++{
++      return(htons(ETH_P_IP));
++}
++
++static int slirp_read(int fd, struct sk_buff **skb, 
++                     struct uml_net_private *lp)
++{
++      return(slirp_user_read(fd, (*skb)->mac.raw, (*skb)->dev->mtu, 
++                            (struct slirp_data *) &lp->user));
++}
++
++static int slirp_write(int fd, struct sk_buff **skb,
++                    struct uml_net_private *lp)
++{
++      return(slirp_user_write(fd, (*skb)->data, (*skb)->len, 
++                             (struct slirp_data *) &lp->user));
++}
++
++struct net_kern_info slirp_kern_info = {
++      .init                   = slirp_init,
++      .protocol               = slirp_protocol,
++      .read                   = slirp_read,
++      .write                  = slirp_write,
++};
++
++static int slirp_setup(char *str, char **mac_out, void *data)
++{
++      struct slirp_init *init = data;
++      int i=0;
++
++      *init = ((struct slirp_init)
++              { argw :                { { "slirp", NULL  } } });
++
++      str = split_if_spec(str, mac_out, NULL);
++
++      if(str == NULL) { /* no command line given after MAC addr */
++              return(1);
++      }
++
++      do {
++              if(i>=SLIRP_MAX_ARGS-1) {
++                      printk("slirp_setup: truncating slirp arguments\n");
++                      break;
++              }
++              init->argw.argv[i++] = str;
++              while(*str && *str!=',') {
++                      if(*str=='_') *str=' ';
++                      str++;
++              }
++              if(*str!=',')
++                      break;
++              *str++='\0';
++      } while(1);
++      init->argw.argv[i]=NULL;
++      return(1);
++}
++
++static struct transport slirp_transport = {
++      .list           = LIST_HEAD_INIT(slirp_transport.list),
++      .name           = "slirp",
++      .setup          = slirp_setup,
++      .user           = &slirp_user_info,
++      .kern           = &slirp_kern_info,
++      .private_size   = sizeof(struct slirp_data),
++      .setup_size     = sizeof(struct slirp_init),
++};
++
++static int register_slirp(void)
++{
++      register_transport(&slirp_transport);
++      return(1);
++}
++
++__initcall(register_slirp);
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/slirp_user.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/slirp_user.c     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/slirp_user.c  2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,201 @@
++#include <stdio.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <stddef.h>
++#include <sched.h>
++#include <string.h>
++#include <errno.h>
++#include <sys/wait.h>
++#include <sys/signal.h>
++#include "user_util.h"
++#include "kern_util.h"
++#include "user.h"
++#include "net_user.h"
++#include "slirp.h"
++#include "slip_proto.h"
++#include "helper.h"
++#include "os.h"
++
++void slirp_user_init(void *data, void *dev)
++{
++      struct slirp_data *pri = data;
++
++      pri->dev = dev;
++}
++
++struct slirp_pre_exec_data {
++      int stdin;
++      int stdout;
++};
++
++static void slirp_pre_exec(void *arg)
++{
++      struct slirp_pre_exec_data *data = arg;
++
++      if(data->stdin != -1) dup2(data->stdin, 0);
++      if(data->stdout != -1) dup2(data->stdout, 1);
++}
++
++static int slirp_tramp(char **argv, int fd)
++{
++      struct slirp_pre_exec_data pe_data;
++      int pid;
++
++      pe_data.stdin = fd;
++      pe_data.stdout = fd;
++      pid = run_helper(slirp_pre_exec, &pe_data, argv, NULL);
++
++      return(pid);
++}
++
++/* XXX This is just a trivial wrapper around os_pipe */ 
++static int slirp_datachan(int *mfd, int *sfd)
++{
++      int fds[2], err;
++
++      err = os_pipe(fds, 1, 1);
++      if(err < 0){
++              printk("slirp_datachan: Failed to open pipe, err = %d\n", -err);
++              return(err);
++      }
++
++      *mfd = fds[0];
++      *sfd = fds[1];
++      return(0);
++}
++
++static int slirp_open(void *data)
++{
++      struct slirp_data *pri = data;
++      int sfd, mfd, pid, err;
++
++      err = slirp_datachan(&mfd, &sfd);
++      if(err)
++              return(err);
++
++      pid = slirp_tramp(pri->argw.argv, sfd);
++
++      if(pid < 0){
++              printk("slirp_tramp failed - errno = %d\n", -pid);
++              os_close_file(sfd);     
++              os_close_file(mfd);     
++              return(pid);
++      }
++
++      pri->slave = sfd;
++      pri->pos = 0;
++      pri->esc = 0;
++
++      pri->pid = pid;
++
++      return(mfd);
++}
++
++static void slirp_close(int fd, void *data)
++{
++      struct slirp_data *pri = data;
++      int status,err;
++
++      os_close_file(fd);
++      os_close_file(pri->slave);
++
++      pri->slave = -1;
++
++      if(pri->pid<1) {
++              printk("slirp_close: no child process to shut down\n");
++              return;
++      }
++
++#if 0
++      if(kill(pri->pid, SIGHUP)<0) {
++              printk("slirp_close: sending hangup to %d failed (%d)\n",
++                      pri->pid, errno);
++      }
++#endif
++
++      CATCH_EINTR(err = waitpid(pri->pid, &status, WNOHANG));
++      if(err < 0) {
++              printk("slirp_close: waitpid returned %d\n", errno);
++              return;
++      }
++
++      if(err == 0) {
++              printk("slirp_close: process %d has not exited\n");
++              return;
++      }
++
++      pri->pid = -1;
++}
++
++int slirp_user_read(int fd, void *buf, int len, struct slirp_data *pri)
++{
++      int i, n, size, start;
++
++      if(pri->more>0) {
++              i = 0;
++              while(i < pri->more) {
++                      size = slip_unesc(pri->ibuf[i++],
++                                      pri->ibuf,&pri->pos,&pri->esc);
++                      if(size){
++                              memcpy(buf, pri->ibuf, size);
++                              memmove(pri->ibuf, &pri->ibuf[i], pri->more-i);
++                              pri->more=pri->more-i; 
++                              return(size);
++                      }
++              }
++              pri->more=0;
++      }
++
++      n = net_read(fd, &pri->ibuf[pri->pos], sizeof(pri->ibuf) - pri->pos);
++      if(n <= 0) return(n);
++
++      start = pri->pos;
++      for(i = 0; i < n; i++){
++              size = slip_unesc(pri->ibuf[start + i],
++                              pri->ibuf,&pri->pos,&pri->esc);
++              if(size){
++                      memcpy(buf, pri->ibuf, size);
++                      memmove(pri->ibuf, &pri->ibuf[start+i+1], n-(i+1));
++                      pri->more=n-(i+1); 
++                      return(size);
++              }
++      }
++      return(0);
++}
++
++int slirp_user_write(int fd, void *buf, int len, struct slirp_data *pri)
++{
++      int actual, n;
++
++      actual = slip_esc(buf, pri->obuf, len);
++      n = net_write(fd, pri->obuf, actual);
++      if(n < 0) return(n);
++      else return(len);
++}
++
++static int slirp_set_mtu(int mtu, void *data)
++{
++      return(mtu);
++}
++
++struct net_user_info slirp_user_info = {
++      .init           = slirp_user_init,
++      .open           = slirp_open,
++      .close          = slirp_close,
++      .remove         = NULL,
++      .set_mtu        = slirp_set_mtu,
++      .add_address    = NULL,
++      .delete_address = NULL,
++      .max_packet     = BUF_SIZE
++};
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/ssl.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/ssl.c    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/ssl.c 2005-05-03 22:28:14.247444272 +0300
+@@ -0,0 +1,300 @@
++/* 
++ * Copyright (C) 2000, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/config.h"
++#include "linux/fs.h"
++#include "linux/tty.h"
++#include "linux/tty_driver.h"
++#include "linux/major.h"
++#include "linux/mm.h"
++#include "linux/init.h"
++#include "linux/console.h"
++#include "asm/termbits.h"
++#include "asm/irq.h"
++#include "line.h"
++#include "ssl.h"
++#include "chan_kern.h"
++#include "user_util.h"
++#include "kern_util.h"
++#include "kern.h"
++#include "init.h"
++#include "irq_user.h"
++#include "mconsole_kern.h"
++#include "2_5compat.h"
++
++static int ssl_version = 1;
++
++/* Referenced only by tty_driver below - presumably it's locked correctly
++ * by the tty driver.
++ */
++static int ssl_refcount = 0;
++
++static struct tty_driver ssl_driver;
++
++#define NR_PORTS 64
++
++void ssl_announce(char *dev_name, int dev)
++{
++      printk(KERN_INFO "Serial line %d assigned device '%s'\n", dev,
++             dev_name);
++}
++
++static struct chan_opts opts = {
++      .announce       = ssl_announce,
++      .xterm_title    = "Serial Line #%d",
++      .raw            = 1,
++      .tramp_stack    = 0,
++      .in_kernel      = 1,
++};
++
++static int ssl_config(char *str);
++static int ssl_get_config(char *dev, char *str, int size, char **error_out);
++static int ssl_remove(char *str);
++
++static struct line_driver driver = {
++      .name                   = "UML serial line",
++      .devfs_name             = "tts/%d",
++      .major                  = TTY_MAJOR,
++      .minor_start            = 64,
++      .type                   = TTY_DRIVER_TYPE_SERIAL,
++      .subtype                = 0,
++      .read_irq               = SSL_IRQ,
++      .read_irq_name          = "ssl",
++      .write_irq              = SSL_WRITE_IRQ,
++      .write_irq_name         = "ssl-write",
++      .symlink_from           = "serial",
++      .symlink_to             = "tts",
++      .mc  = {
++              .name           = "ssl",
++              .config         = ssl_config,
++              .get_config     = ssl_get_config,
++              .remove         = ssl_remove,
++      },
++};
++
++/* The array is initialized by line_init, which is an initcall.  The 
++ * individual elements are protected by individual semaphores.
++ */
++static struct line serial_lines[NR_PORTS] =
++      { [0 ... NR_PORTS - 1] = LINE_INIT(CONFIG_SSL_CHAN, &driver) };
++
++static struct lines lines = LINES_INIT(NR_PORTS);
++
++static int ssl_config(char *str)
++{
++      return(line_config(serial_lines, 
++                         sizeof(serial_lines)/sizeof(serial_lines[0]), str));
++}
++
++static int ssl_get_config(char *dev, char *str, int size, char **error_out)
++{
++      return(line_get_config(dev, serial_lines, 
++                             sizeof(serial_lines)/sizeof(serial_lines[0]), 
++                             str, size, error_out));
++}
++
++static int ssl_remove(char *str)
++{
++      return(line_remove(serial_lines, 
++                         sizeof(serial_lines)/sizeof(serial_lines[0]), str));
++}
++
++int ssl_open(struct tty_struct *tty, struct file *filp)
++{
++      return(line_open(serial_lines, tty, &opts));
++}
++
++static void ssl_close(struct tty_struct *tty, struct file * filp)
++{
++      line_close(serial_lines, tty);
++}
++
++static int ssl_write(struct tty_struct * tty, int from_user,
++                   const unsigned char *buf, int count)
++{
++      return(line_write(serial_lines, tty, from_user, buf, count));
++}
++
++static void ssl_put_char(struct tty_struct *tty, unsigned char ch)
++{
++      line_write(serial_lines, tty, 0, &ch, sizeof(ch));
++}
++
++static void ssl_flush_chars(struct tty_struct *tty)
++{
++      return;
++}
++
++static int ssl_chars_in_buffer(struct tty_struct *tty)
++{
++      return(0);
++}
++
++static void ssl_flush_buffer(struct tty_struct *tty)
++{
++      return;
++}
++
++static int ssl_ioctl(struct tty_struct *tty, struct file * file,
++                   unsigned int cmd, unsigned long arg)
++{
++      int ret;
++
++      ret = 0;
++      switch(cmd){
++      case TCGETS:
++      case TCSETS:
++      case TCFLSH:
++      case TCSETSF:
++      case TCSETSW:
++      case TCGETA:
++      case TIOCMGET:
++      case TCSBRK:
++      case TCSBRKP:
++      case TIOCMSET:
++              ret = -ENOIOCTLCMD;
++              break;
++      default:
++              printk(KERN_ERR 
++                     "Unimplemented ioctl in ssl_ioctl : 0x%x\n", cmd);
++              ret = -ENOIOCTLCMD;
++              break;
++      }
++      return(ret);
++}
++
++static void ssl_throttle(struct tty_struct * tty)
++{
++      printk(KERN_ERR "Someone should implement ssl_throttle\n");
++}
++
++static void ssl_unthrottle(struct tty_struct * tty)
++{
++      printk(KERN_ERR "Someone should implement ssl_unthrottle\n");
++}
++
++static void ssl_set_termios(struct tty_struct *tty, 
++                          struct termios *old_termios)
++{
++}
++
++static void ssl_stop(struct tty_struct *tty)
++{
++      printk(KERN_ERR "Someone should implement ssl_stop\n");
++}
++
++static void ssl_start(struct tty_struct *tty)
++{
++      printk(KERN_ERR "Someone should implement ssl_start\n");
++}
++
++void ssl_hangup(struct tty_struct *tty)
++{
++}
++
++static struct tty_driver ssl_driver = {
++      .refcount               = &ssl_refcount,
++      .open                   = ssl_open,
++      .close                  = ssl_close,
++      .write                  = ssl_write,
++      .put_char               = ssl_put_char,
++      .flush_chars            = ssl_flush_chars,
++      .chars_in_buffer        = ssl_chars_in_buffer,
++      .flush_buffer           = ssl_flush_buffer,
++      .ioctl                  = ssl_ioctl,
++      .throttle               = ssl_throttle,
++      .unthrottle             = ssl_unthrottle,
++      .set_termios            = ssl_set_termios,
++      .stop                   = ssl_stop,
++      .start                  = ssl_start,
++      .hangup                 = ssl_hangup
++};
++
++/* Changed by ssl_init and referenced by ssl_exit, which are both serialized
++ * by being an initcall and exitcall, respectively.
++ */
++static int ssl_init_done = 0;
++
++static void ssl_console_write(struct console *c, const char *string, 
++                            unsigned len)
++{
++      struct line *line = &serial_lines[c->index];
++      if(ssl_init_done)
++              down(&line->sem);
++      console_write_chan(&line->chan_list, string, len);
++      if(ssl_init_done)
++              up(&line->sem);
++}
++
++static kdev_t ssl_console_device(struct console *c)
++{
++      return mk_kdev(TTY_MAJOR, c->index);
++}
++
++static int ssl_console_setup(struct console *co, char *options)
++{
++      return(0);
++}
++
++static struct console ssl_cons = {
++      name:           "ttyS",
++      write:          ssl_console_write,
++      device:         ssl_console_device,
++      setup:          ssl_console_setup,
++      flags:          CON_PRINTBUFFER,
++      index:          -1,
++};
++
++int ssl_init(void)
++{
++      char *new_title;
++
++      printk(KERN_INFO "Initializing software serial port version %d\n", 
++             ssl_version);
++
++      line_register_devfs(&lines, &driver, &ssl_driver, serial_lines, 
++                          sizeof(serial_lines)/sizeof(serial_lines[0]));
++
++      lines_init(serial_lines, sizeof(serial_lines)/sizeof(serial_lines[0]));
++
++      new_title = add_xterm_umid(opts.xterm_title);
++      if(new_title != NULL) opts.xterm_title = new_title;
++
++      register_console(&ssl_cons);
++      ssl_init_done = 1;
++      return(0);
++}
++
++__initcall(ssl_init);
++
++static int ssl_chan_setup(char *str)
++{
++      return(line_setup(serial_lines, 
++                        sizeof(serial_lines)/sizeof(serial_lines[0]), 
++                        str, 1));
++}
++
++__setup("ssl", ssl_chan_setup);
++__channel_help(ssl_chan_setup, "ssl");
++
++static void ssl_exit(void)
++{
++      if(!ssl_init_done) return;
++      close_lines(serial_lines, 
++                  sizeof(serial_lines)/sizeof(serial_lines[0]));
++}
++
++__uml_exitcall(ssl_exit);
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/ssl.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/ssl.h    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/ssl.h 2005-05-03 22:28:14.248444120 +0300
+@@ -0,0 +1,23 @@
++/* 
++ * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __SSL_H__
++#define __SSL_H__
++
++extern int ssl_read(int fd, int line);
++extern void ssl_receive_char(int line, char ch);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/stdio_console.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/stdio_console.c  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/stdio_console.c       2005-05-03 22:28:14.249443968 +0300
+@@ -0,0 +1,258 @@
++/* 
++ * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/config.h"
++#include "linux/posix_types.h"
++#include "linux/tty.h"
++#include "linux/tty_flip.h"
++#include "linux/types.h"
++#include "linux/major.h"
++#include "linux/kdev_t.h"
++#include "linux/console.h"
++#include "linux/string.h"
++#include "linux/sched.h"
++#include "linux/list.h"
++#include "linux/init.h"
++#include "linux/interrupt.h"
++#include "linux/slab.h"
++#include "asm/current.h"
++#include "asm/softirq.h"
++#include "asm/hardirq.h"
++#include "asm/irq.h"
++#include "stdio_console.h"
++#include "line.h"
++#include "chan_kern.h"
++#include "user_util.h"
++#include "kern_util.h"
++#include "irq_user.h"
++#include "mconsole_kern.h"
++#include "init.h"
++#include "2_5compat.h"
++
++#define MAX_TTYS (8)
++
++/* Referenced only by tty_driver below - presumably it's locked correctly
++ * by the tty driver.
++ */
++
++static struct tty_driver console_driver;
++
++static int console_refcount = 0;
++
++static struct chan_ops init_console_ops = {
++      .type           = "you shouldn't see this",
++      .init           = NULL,
++      .open           = NULL,
++      .close          = NULL,
++      .read           = NULL,
++      .write          = NULL,
++      .console_write  = generic_write,
++      .window_size    = NULL,
++      .free           = NULL,
++      .winch          = 0,
++};
++
++static struct chan init_console_chan = {
++      .list           = { },
++      .primary        = 1,
++      .input          = 0,
++      .output         = 1,
++      .opened         = 1,
++      .fd             = 1,
++      .pri            = INIT_STATIC,
++      .ops            = &init_console_ops,
++      .data           = NULL
++};
++
++void stdio_announce(char *dev_name, int dev)
++{
++      printk(KERN_INFO "Virtual console %d assigned device '%s'\n", dev,
++             dev_name);
++}
++
++static struct chan_opts opts = {
++      .announce       = stdio_announce,
++      .xterm_title    = "Virtual Console #%d",
++      .raw            = 1,
++      .tramp_stack    = 0,
++      .in_kernel      = 1,
++};
++
++static int con_config(char *str);
++static int con_get_config(char *dev, char *str, int size, char **error_out);
++static int con_remove(char *str);
++
++static struct line_driver driver = {
++      .name                   = "UML console",
++      .devfs_name             = "vc/%d",
++      .major                  = TTY_MAJOR,
++      .minor_start            = 0,
++      .type                   = TTY_DRIVER_TYPE_CONSOLE,
++      .subtype                = SYSTEM_TYPE_CONSOLE,
++      .read_irq               = CONSOLE_IRQ,
++      .read_irq_name          = "console",
++      .write_irq              = CONSOLE_WRITE_IRQ,
++      .write_irq_name         = "console-write",
++      .symlink_from           = "ttys",
++      .symlink_to             = "vc",
++      .mc  = {
++              .name           = "con",
++              .config         = con_config,
++              .get_config     = con_get_config,
++              .remove         = con_remove,
++      },
++};
++
++static struct lines console_lines = LINES_INIT(MAX_TTYS);
++
++/* The array is initialized by line_init, which is an initcall.  The 
++ * individual elements are protected by individual semaphores.
++ */
++struct line vts[MAX_TTYS] = { LINE_INIT(CONFIG_CON_ZERO_CHAN, &driver),
++                            [ 1 ... MAX_TTYS - 1 ] = 
++                            LINE_INIT(CONFIG_CON_CHAN, &driver) };
++
++static int con_config(char *str)
++{
++      return(line_config(vts, sizeof(vts)/sizeof(vts[0]), str));
++}
++
++static int con_get_config(char *dev, char *str, int size, char **error_out)
++{
++      return(line_get_config(dev, vts, sizeof(vts)/sizeof(vts[0]), str, 
++                             size, error_out));
++}
++
++static int con_remove(char *str)
++{
++      return(line_remove(vts, sizeof(vts)/sizeof(vts[0]), str));
++}
++
++static int open_console(struct tty_struct *tty)
++{
++      return(line_open(vts, tty, &opts));
++}
++
++static int con_open(struct tty_struct *tty, struct file *filp)
++{
++      return(open_console(tty));
++}
++
++static void con_close(struct tty_struct *tty, struct file *filp)
++{
++      line_close(vts, tty);
++}
++
++static int con_write(struct tty_struct *tty, int from_user, 
++                   const unsigned char *buf, int count)
++{
++       return(line_write(vts, tty, from_user, buf, count));
++}
++
++static void set_termios(struct tty_struct *tty, struct termios * old)
++{
++}
++
++static int chars_in_buffer(struct tty_struct *tty)
++{
++      return(0);
++}
++
++static int con_init_done = 0;
++
++int stdio_init(void)
++{
++      char *new_title;
++
++      printk(KERN_INFO "Initializing stdio console driver\n");
++
++      line_register_devfs(&console_lines, &driver, &console_driver, vts, 
++                          sizeof(vts)/sizeof(vts[0]));
++
++      lines_init(vts, sizeof(vts)/sizeof(vts[0]));
++
++      new_title = add_xterm_umid(opts.xterm_title);
++      if(new_title != NULL) opts.xterm_title = new_title;
++
++      open_console(NULL);
++      con_init_done = 1;
++      return(0);
++}
++
++__initcall(stdio_init);
++
++static void console_write(struct console *console, const char *string, 
++                        unsigned len)
++{
++      struct line *line = &vts[console->index];
++
++      if(con_init_done)
++              down(&line->sem);
++      console_write_chan(&line->chan_list, string, len);
++      if(con_init_done)
++              up(&line->sem);
++}
++
++static struct tty_driver console_driver = {
++      .refcount               = &console_refcount,
++      .open                   = con_open,
++      .close                  = con_close,
++      .write                  = con_write,
++      .chars_in_buffer        = chars_in_buffer,
++      .set_termios            = set_termios
++};
++
++static kdev_t console_device(struct console *c)
++{
++      return mk_kdev(TTY_MAJOR, c->index);
++}
++
++static int console_setup(struct console *co, char *options)
++{
++      return(0);
++}
++
++static struct console stdiocons = {
++      name:           "tty",
++      write:          console_write,
++      device:         console_device,
++      setup:          console_setup,
++      flags:          CON_PRINTBUFFER,
++      index:          -1,
++};
++
++void stdio_console_init(void)
++{
++      INIT_LIST_HEAD(&vts[0].chan_list);
++      list_add(&init_console_chan.list, &vts[0].chan_list);
++      register_console(&stdiocons);
++}
++
++static int console_chan_setup(char *str)
++{
++      return(line_setup(vts, sizeof(vts)/sizeof(vts[0]), str, 1));
++}
++
++__setup("con", console_chan_setup);
++__channel_help(console_chan_setup, "con");
++
++static void console_exit(void)
++{
++      if(!con_init_done) return;
++      close_lines(vts, sizeof(vts)/sizeof(vts[0]));
++}
++
++__uml_exitcall(console_exit);
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/stdio_console.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/stdio_console.h  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/stdio_console.h       2005-05-03 22:28:14.250443816 +0300
+@@ -0,0 +1,21 @@
++/* 
++ * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __STDIO_CONSOLE_H
++#define __STDIO_CONSOLE_H
++
++extern void save_console_flags(void);
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/tty.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/tty.c    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/tty.c 2005-05-03 22:28:14.251443664 +0300
+@@ -0,0 +1,91 @@
++/* 
++ * Copyright (C) 2001 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <stdio.h>
++#include <termios.h>
++#include <errno.h>
++#include <unistd.h>
++#include "chan_user.h"
++#include "user_util.h"
++#include "user.h"
++#include "os.h"
++
++struct tty_chan {
++      char *dev;
++      int raw;
++      struct termios tt;
++};
++
++void *tty_chan_init(char *str, int device, struct chan_opts *opts)
++{
++      struct tty_chan *data;
++
++      if(*str != ':'){
++              printk("tty_init : channel type 'tty' must specify "
++                     "a device\n");
++              return(NULL);
++      }
++      str++;
++
++      data = um_kmalloc(sizeof(*data)); 
++      if(data == NULL) 
++              return(NULL);
++      *data = ((struct tty_chan) { .dev       = str,
++                                   .raw       = opts->raw });
++                                   
++      return(data);
++}
++
++int tty_open(int input, int output, int primary, void *d, char **dev_out)
++{
++      struct tty_chan *data = d;
++      int fd, err;
++
++      fd = os_open_file(data->dev, of_set_rw(OPENFLAGS(), input, output), 0);
++      if(fd < 0) return(fd);
++      if(data->raw){
++              CATCH_EINTR(err = tcgetattr(fd, &data->tt));
++              if(err)
++                      return(err);
++
++              err = raw(fd);
++              if(err)
++                      return(err);
++      }
++
++      *dev_out = data->dev;
++      return(fd);
++}
++
++int tty_console_write(int fd, const char *buf, int n, void *d)
++{
++      struct tty_chan *data = d;
++
++      return(generic_console_write(fd, buf, n, &data->tt));
++}
++
++struct chan_ops tty_ops = {
++      .type           = "tty",
++      .init           = tty_chan_init,
++      .open           = tty_open,
++      .close          = generic_close,
++      .read           = generic_read,
++      .write          = generic_write,
++      .console_write  = tty_console_write,
++      .window_size    = generic_window_size,
++      .free           = generic_free,
++      .winch          = 0,
++};
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/ubd_kern.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/ubd_kern.c       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/ubd_kern.c    2005-05-03 22:28:14.257442752 +0300
+@@ -0,0 +1,1380 @@
++/* 
++ * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++/* 2001-09-28...2002-04-17
++ * Partition stuff by James_McMechan@hotmail.com
++ * old style ubd by setting UBD_SHIFT to 0
++ */
++
++#define MAJOR_NR UBD_MAJOR
++#define UBD_SHIFT 4
++
++#include "linux/config.h"
++#include "linux/blk.h"
++#include "linux/blkdev.h"
++#include "linux/hdreg.h"
++#include "linux/init.h"
++#include "linux/devfs_fs_kernel.h"
++#include "linux/cdrom.h"
++#include "linux/proc_fs.h"
++#include "linux/ctype.h"
++#include "linux/capability.h"
++#include "linux/mm.h"
++#include "linux/vmalloc.h"
++#include "linux/blkpg.h"
++#include "linux/genhd.h"
++#include "linux/spinlock.h"
++#include "asm/segment.h"
++#include "asm/uaccess.h"
++#include "asm/irq.h"
++#include "asm/types.h"
++#include "user_util.h"
++#include "mem_user.h"
++#include "kern_util.h"
++#include "kern.h"
++#include "mconsole_kern.h"
++#include "init.h"
++#include "irq_user.h"
++#include "irq_kern.h"
++#include "ubd_user.h"
++#include "2_5compat.h"
++#include "os.h"
++#include "mem.h"
++#include "mem_kern.h"
++
++static int ubd_open(struct inode * inode, struct file * filp);
++static int ubd_release(struct inode * inode, struct file * file);
++static int ubd_ioctl(struct inode * inode, struct file * file,
++                   unsigned int cmd, unsigned long arg);
++static int ubd_revalidate(kdev_t rdev);
++static int ubd_revalidate1(kdev_t rdev);
++
++#define MAX_DEV (8)
++#define MAX_MINOR (MAX_DEV << UBD_SHIFT)
++
++/* Changed in early boot */
++static int ubd_do_mmap = 0;
++#define UBD_MMAP_BLOCK_SIZE PAGE_SIZE
++
++/* Not modified by this driver */
++static int blk_sizes[MAX_MINOR] = { [ 0 ... MAX_MINOR - 1 ] = BLOCK_SIZE };
++static int hardsect_sizes[MAX_MINOR] = { [ 0 ... MAX_MINOR - 1 ] = 512 };
++
++/* Protected by ubd_lock */
++static int sizes[MAX_MINOR] = { [ 0 ... MAX_MINOR - 1 ] = 0 };
++
++static struct block_device_operations ubd_blops = {
++        .open         = ubd_open,
++        .release      = ubd_release,
++        .ioctl                = ubd_ioctl,
++        .revalidate   = ubd_revalidate,
++};
++
++/* Protected by ubd_lock, except in prepare_request and ubd_ioctl because 
++ * the block layer should ensure that the device is idle before closing it.
++ */
++static struct hd_struct       ubd_part[MAX_MINOR] =
++      { [ 0 ... MAX_MINOR - 1 ] = { 0, 0, 0 } };
++
++/* Protected by io_request_lock */
++static request_queue_t *ubd_queue;
++
++/* Protected by ubd_lock */
++static int fake_major = MAJOR_NR;
++
++static spinlock_t ubd_lock = SPIN_LOCK_UNLOCKED;
++
++#define INIT_GENDISK(maj, name, parts, shift, bsizes, max, blops) \
++{ \
++      .major          = maj, \
++      .major_name     = name, \
++      .minor_shift    = shift, \
++      .max_p          = 1 << shift, \
++      .part           = parts, \
++      .sizes          = bsizes, \
++      .nr_real        = max, \
++      .real_devices   = NULL, \
++      .next           = NULL, \
++      .fops           = blops, \
++      .de_arr         = NULL, \
++      .flags          = 0 \
++}
++
++static struct gendisk ubd_gendisk = INIT_GENDISK(MAJOR_NR, "ubd", ubd_part,
++                                               UBD_SHIFT, sizes, MAX_DEV, 
++                                               &ubd_blops);
++static struct gendisk fake_gendisk = INIT_GENDISK(0, "ubd", ubd_part, 
++                                                UBD_SHIFT, sizes, MAX_DEV, 
++                                                &ubd_blops);
++
++#ifdef CONFIG_BLK_DEV_UBD_SYNC
++#define OPEN_FLAGS ((struct openflags) { .r = 1, .w = 1, .s = 1, .c = 0, \
++                                       .cl = 1 })
++#else
++#define OPEN_FLAGS ((struct openflags) { .r = 1, .w = 1, .s = 0, .c = 0, \
++                                       .cl = 1 })
++#endif
++
++/* Not protected - changed only in ubd_setup_common and then only to
++ * to enable O_SYNC.
++ */
++static struct openflags global_openflags = OPEN_FLAGS;
++
++struct cow {
++      char *file;
++      int fd;
++      unsigned long *bitmap;
++      unsigned long bitmap_len;
++      int bitmap_offset;
++        int data_offset;
++};
++
++struct ubd {
++      char *file;
++      int count;
++      int fd;
++      __u64 size;
++      struct openflags boot_openflags;
++      struct openflags openflags;
++      devfs_handle_t devfs;
++      int no_cow;
++      struct cow cow;
++
++      int map_writes;
++      int map_reads;
++      int nomap_writes;
++      int nomap_reads;
++      int write_maps;
++};
++
++#define DEFAULT_COW { \
++      .file                   = NULL, \
++        .fd                   = -1, \
++        .bitmap                       = NULL, \
++      .bitmap_offset          = 0, \
++        .data_offset          = 0, \
++}
++
++#define DEFAULT_UBD { \
++      .file                   = NULL, \
++      .count                  = 0, \
++      .fd                     = -1, \
++      .size                   = -1, \
++      .boot_openflags         = OPEN_FLAGS, \
++      .openflags              = OPEN_FLAGS, \
++      .devfs                  = NULL, \
++      .no_cow                 = 0, \
++        .cow                  = DEFAULT_COW, \
++      .map_writes             = 0, \
++      .map_reads              = 0, \
++      .nomap_writes           = 0, \
++      .nomap_reads            = 0, \
++      .write_maps             = 0, \
++}
++
++struct ubd ubd_dev[MAX_DEV] = { [ 0 ... MAX_DEV - 1 ] = DEFAULT_UBD };
++
++static int ubd0_init(void)
++{
++      struct ubd *dev = &ubd_dev[0];
++
++      if(dev->file == NULL)
++              dev->file = "root_fs";
++      return(0);
++}
++
++__initcall(ubd0_init);
++
++/* Only changed by fake_ide_setup which is a setup */
++static int fake_ide = 0;
++static struct proc_dir_entry *proc_ide_root = NULL;
++static struct proc_dir_entry *proc_ide = NULL;
++
++static void make_proc_ide(void)
++{
++      proc_ide_root = proc_mkdir("ide", 0);
++      proc_ide = proc_mkdir("ide0", proc_ide_root);
++}
++
++static int proc_ide_read_media(char *page, char **start, off_t off, int count,
++                             int *eof, void *data)
++{
++      int len;
++
++      strcpy(page, "disk\n");
++      len = strlen("disk\n");
++      len -= off;
++      if (len < count){
++              *eof = 1;
++              if (len <= 0) return 0;
++      }
++      else len = count;
++      *start = page + off;
++      return len;
++}
++
++static void make_ide_entries(char *dev_name)
++{
++      struct proc_dir_entry *dir, *ent;
++      char name[64];
++
++      if(!fake_ide) return;
++
++      /* Without locking this could race if a UML was booted with no 
++       * disks and then two mconsole requests which add disks came in 
++       * at the same time.
++       */
++      spin_lock(&ubd_lock);
++      if(proc_ide_root == NULL) make_proc_ide();
++      spin_unlock(&ubd_lock);
++
++      dir = proc_mkdir(dev_name, proc_ide);
++      if(!dir) return;
++
++      ent = create_proc_entry("media", S_IFREG|S_IRUGO, dir);
++      if(!ent) return;
++      ent->nlink = 1;
++      ent->data = NULL;
++      ent->read_proc = proc_ide_read_media;
++      ent->write_proc = NULL;
++      sprintf(name,"ide0/%s", dev_name);
++      proc_symlink(dev_name, proc_ide_root, name);
++}
++
++static int fake_ide_setup(char *str)
++{
++      fake_ide = 1;
++      return(1);
++}
++
++__setup("fake_ide", fake_ide_setup);
++
++__uml_help(fake_ide_setup,
++"fake_ide\n"
++"    Create ide0 entries that map onto ubd devices.\n\n"
++);
++
++static int parse_unit(char **ptr)
++{
++      char *str = *ptr, *end;
++      int n = -1;
++
++      if(isdigit(*str)) {
++              n = simple_strtoul(str, &end, 0);
++              if(end == str)
++                      return(-1);
++              *ptr = end;
++      }
++      else if (('a' <= *str) && (*str <= 'h')) {
++              n = *str - 'a';
++              str++;
++              *ptr = str;
++      }
++      return(n);
++}
++
++static int ubd_setup_common(char *str, int *index_out)
++{
++      struct openflags flags = global_openflags;
++      struct ubd *dev;
++      char *backing_file;
++      int n, err;
++
++      if(index_out) *index_out = -1;
++      n = *str;
++      if(n == '='){
++              char *end;
++              int major;
++
++              str++;
++              if(!strcmp(str, "mmap")){
++                      CHOOSE_MODE(printk("mmap not supported by the ubd "
++                                         "driver in tt mode\n"),
++                                  ubd_do_mmap = 1);
++                      return(0);
++              }
++
++              if(!strcmp(str, "sync")){
++                      global_openflags.s = 1;
++                      return(0);
++              }
++              major = simple_strtoul(str, &end, 0);
++              if((*end != '\0') || (end == str)){
++                      printk(KERN_ERR 
++                             "ubd_setup : didn't parse major number\n");
++                      return(1);
++              }
++
++              err = 1;
++              spin_lock(&ubd_lock);
++              if(fake_major != MAJOR_NR){
++                      printk(KERN_ERR "Can't assign a fake major twice\n");
++                      goto out1;
++              }
++
++              fake_gendisk.major = major;
++              fake_major = major;
++      
++              printk(KERN_INFO "Setting extra ubd major number to %d\n",
++                     major);
++              err = 0;
++      out1:
++              spin_unlock(&ubd_lock);
++              return(err);
++      }
++
++      n = parse_unit(&str);
++      if(n < 0){
++              printk(KERN_ERR "ubd_setup : couldn't parse unit number "
++                     "'%s'\n", str);
++              return(1);
++      }
++
++      if(n >= MAX_DEV){
++              printk(KERN_ERR "ubd_setup : index %d out of range "
++                     "(%d devices)\n", n, MAX_DEV);   
++              return(1);
++      }
++
++      err = 1;
++      spin_lock(&ubd_lock);
++
++      dev = &ubd_dev[n];
++      if(dev->file != NULL){
++              printk(KERN_ERR "ubd_setup : device already configured\n");
++              goto out2;
++      }
++
++      if(index_out) *index_out = n;
++
++      if(*str == 'r'){
++              flags.w = 0;
++              str++;
++      }
++      if(*str == 's'){
++              flags.s = 1;
++              str++;
++      }
++      if(*str == 'd'){
++              dev->no_cow = 1;
++              str++;
++      }
++
++      if(*str++ != '='){
++              printk(KERN_ERR "ubd_setup : Expected '='\n");
++              goto out2;
++      }
++
++      err = 0;
++      backing_file = strchr(str, ',');
++      if(backing_file){
++              if(dev->no_cow)
++                      printk(KERN_ERR "Can't specify both 'd' and a "
++                             "cow file\n");
++              else {
++                      *backing_file = '\0';
++                      backing_file++;
++              }
++      }
++      dev->file = str;
++      dev->cow.file = backing_file;
++      dev->boot_openflags = flags;
++ out2:
++      spin_unlock(&ubd_lock);
++      return(err);
++}
++
++static int ubd_setup(char *str)
++{
++      ubd_setup_common(str, NULL);
++      return(1);
++}
++
++__setup("ubd", ubd_setup);
++__uml_help(ubd_setup,
++"ubd<n>=<filename>\n"
++"    This is used to associate a device with a file in the underlying\n"
++"    filesystem. Usually, there is a filesystem in the file, but \n"
++"    that's not required. Swap devices containing swap files can be\n"
++"    specified like this. Also, a file which doesn't contain a\n"
++"    filesystem can have its contents read in the virtual \n"
++"    machine by running dd on the device. n must be in the range\n"
++"    0 to 7. Appending an 'r' to the number will cause that device\n"
++"    to be mounted read-only. For example ubd1r=./ext_fs. Appending\n"
++"    an 's' (has to be _after_ 'r', if there is one) will cause data\n"
++"    to be written to disk on the host immediately.\n\n"
++);
++
++static int fakehd(char *str)
++{
++      printk(KERN_INFO 
++             "fakehd : Changing ubd_gendisk.major_name to \"hd\".\n");
++      ubd_gendisk.major_name = "hd";
++      return(1);
++}
++
++__setup("fakehd", fakehd);
++__uml_help(fakehd,
++"fakehd\n"
++"    Change the ubd device name to \"hd\".\n\n"
++);
++
++static void do_ubd_request(request_queue_t * q);
++
++/* Only changed by ubd_init, which is an initcall. */
++int thread_fd = -1;
++
++/* Changed by ubd_handler, which is serialized because interrupts only
++ * happen on CPU 0.
++ */
++int intr_count = 0;
++
++static void ubd_finish(int error)
++{
++      int nsect;
++
++      if(error){
++              end_request(0);
++              return;
++      }
++      nsect = CURRENT->current_nr_sectors;
++      CURRENT->sector += nsect;
++      CURRENT->buffer += nsect << 9;
++      CURRENT->errors = 0;
++      CURRENT->nr_sectors -= nsect;
++      CURRENT->current_nr_sectors = 0;
++      end_request(1);
++}
++
++static void ubd_handler(void)
++{
++      struct io_thread_req req;
++      int n, err;
++
++      DEVICE_INTR = NULL;
++      intr_count++;
++      n = read_ubd_fs(thread_fd, &req, sizeof(req));
++      if(n != sizeof(req)){
++              printk(KERN_ERR "Pid %d - spurious interrupt in ubd_handler, "
++                     "err = %d\n", os_getpid(), -n);
++              spin_lock(&io_request_lock);
++              end_request(0);
++              spin_unlock(&io_request_lock);
++              return;
++      }
++        
++        if((req.op != UBD_MMAP) && 
++         ((req.offset != ((__u64) (CURRENT->sector)) << 9) ||
++          (req.length != (CURRENT->current_nr_sectors) << 9)))
++              panic("I/O op mismatch");
++
++      if(req.map_fd != -1){
++              err = physmem_subst_mapping(req.buffer, req.map_fd, 
++                                          req.map_offset, 1);
++              if(err)
++                      printk("ubd_handler - physmem_subst_mapping failed, "
++                             "err = %d\n", -err);
++      }
++
++      spin_lock(&io_request_lock);
++      ubd_finish(req.error);
++      reactivate_fd(thread_fd, UBD_IRQ);      
++      do_ubd_request(ubd_queue);
++      spin_unlock(&io_request_lock);
++}
++
++static void ubd_intr(int irq, void *dev, struct pt_regs *unused)
++{
++      ubd_handler();
++}
++
++/* Only changed by ubd_init, which is an initcall. */
++static int io_pid = -1;
++
++void kill_io_thread(void)
++{
++      if(io_pid != -1)
++              os_kill_process(io_pid, 1);
++}
++
++__uml_exitcall(kill_io_thread);
++
++/* Initialized in an initcall, and unchanged thereafter */
++devfs_handle_t ubd_dir_handle;
++
++static int ubd_add(int n)
++{
++      struct ubd *dev = &ubd_dev[n];
++      char name[sizeof("nnnnnn\0")], dev_name[sizeof("ubd0x")];
++      int err = -EISDIR;
++
++      if(dev->file == NULL)
++              goto out;
++
++      err = ubd_revalidate1(MKDEV(MAJOR_NR, n << UBD_SHIFT));
++      if(err)
++              goto out;
++
++      if(dev->cow.file == NULL)
++              blk_sizes[n] = UBD_MMAP_BLOCK_SIZE;
++
++      sprintf(name, "%d", n);
++      dev->devfs = devfs_register(ubd_dir_handle, name, DEVFS_FL_REMOVABLE,
++                                  MAJOR_NR, n << UBD_SHIFT, S_IFBLK | 
++                                  S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP,
++                                  &ubd_blops, NULL);
++
++#if 0 /* 2.5 ... */
++      sprintf(disk->disk_name, "ubd%c", 'a' + unit);
++#endif
++
++      sprintf(dev_name, "%s%c", ubd_gendisk.major_name, 
++                   n + 'a');
++
++      make_ide_entries(dev_name);
++      return(0);
++
++ out:
++      return(err);
++}
++
++static int ubd_config(char *str)
++{
++      int n, err;
++
++      str = uml_strdup(str);
++      if(str == NULL){
++              printk(KERN_ERR "ubd_config failed to strdup string\n");
++              return(1);
++      }
++      err = ubd_setup_common(str, &n);
++      if(err){
++              kfree(str);
++              return(-1);
++      }
++      if(n == -1) return(0);
++
++      spin_lock(&ubd_lock);
++      err = ubd_add(n);
++      if(err)
++              ubd_dev[n].file = NULL;
++      spin_unlock(&ubd_lock);
++
++      return(err);
++}
++
++static int ubd_get_config(char *name, char *str, int size, char **error_out)
++{
++      struct ubd *dev;
++      char *end;
++      int n, len = 0;
++
++      n = simple_strtoul(name, &end, 0);
++      if((*end != '\0') || (end == name)){
++              *error_out = "ubd_get_config : didn't parse device number";
++              return(-1);
++      }
++
++      if((n >= MAX_DEV) || (n < 0)){
++              *error_out = "ubd_get_config : device number out of range";
++              return(-1);
++      }
++
++      dev = &ubd_dev[n];
++      spin_lock(&ubd_lock);
++
++      if(dev->file == NULL){
++              CONFIG_CHUNK(str, size, len, "", 1);
++              goto out;
++      }
++
++      CONFIG_CHUNK(str, size, len, dev->file, 0);
++
++      if(dev->cow.file != NULL){
++              CONFIG_CHUNK(str, size, len, ",", 0);
++              CONFIG_CHUNK(str, size, len, dev->cow.file, 1);
++      }
++      else CONFIG_CHUNK(str, size, len, "", 1);
++
++ out:
++      spin_unlock(&ubd_lock);
++      return(len);
++}
++
++static int ubd_remove(char *str)
++{
++      struct ubd *dev;
++      int n, err = -ENODEV;
++
++      if(isdigit(*str)){
++              char *end;
++              n = simple_strtoul(str, &end, 0);
++              if ((*end != '\0') || (end == str)) 
++                      return(err);
++      }
++      else if (('a' <= *str) && (*str <= 'h'))
++              n = *str - 'a';
++      else
++              return(err);    /* it should be a number 0-7/a-h */
++
++      if((n < 0) || (n >= MAX_DEV))
++              return(err);
++
++      dev = &ubd_dev[n];
++
++      spin_lock(&ubd_lock);
++      err = 0;
++      if(dev->file == NULL)
++              goto out;
++      err = -1;
++      if(dev->count > 0)
++              goto out;
++      if(dev->devfs != NULL) 
++              devfs_unregister(dev->devfs);
++
++      *dev = ((struct ubd) DEFAULT_UBD);
++      err = 0;
++ out:
++      spin_unlock(&ubd_lock);
++      return(err);
++}
++
++static struct mc_device ubd_mc = {
++      .name           = "ubd",
++      .config         = ubd_config,
++      .get_config     = ubd_get_config,
++      .remove         = ubd_remove,
++};
++
++static int ubd_mc_init(void)
++{
++      mconsole_register_dev(&ubd_mc);
++      return(0);
++}
++
++__initcall(ubd_mc_init);
++
++static request_queue_t *ubd_get_queue(kdev_t device)
++{
++      return(ubd_queue);
++}
++
++int ubd_init(void)
++{
++      unsigned long stack;
++        int i, err;
++
++      ubd_dir_handle = devfs_mk_dir (NULL, "ubd", NULL);
++      if (devfs_register_blkdev(MAJOR_NR, "ubd", &ubd_blops)) {
++              printk(KERN_ERR "ubd: unable to get major %d\n", MAJOR_NR);
++              return -1;
++      }
++      read_ahead[MAJOR_NR] = 8;               /* 8 sector (4kB) read-ahead */
++      blksize_size[MAJOR_NR] = blk_sizes;
++      blk_size[MAJOR_NR] = sizes;
++      INIT_HARDSECT(hardsect_size, MAJOR_NR, hardsect_sizes);
++
++      ubd_queue = BLK_DEFAULT_QUEUE(MAJOR_NR);
++      blk_init_queue(ubd_queue, DEVICE_REQUEST);
++      INIT_ELV(ubd_queue, &ubd_queue->elevator);
++
++        add_gendisk(&ubd_gendisk);
++      if (fake_major != MAJOR_NR){
++              /* major number 0 is used to auto select */
++              err = devfs_register_blkdev(fake_major, "fake", &ubd_blops);
++              if(fake_major == 0){
++              /* auto device number case */
++                      fake_major = err;
++                      if(err == 0)
++                              return(-ENODEV);
++              } 
++              else if (err){
++                      /* not auto so normal error */
++                      printk(KERN_ERR "ubd: error %d getting major %d\n", 
++                             -err, fake_major);
++                      return(-ENODEV);
++              }
++
++              blk_dev[fake_major].queue = ubd_get_queue;
++              read_ahead[fake_major] = 8;     /* 8 sector (4kB) read-ahead */
++              blksize_size[fake_major] = blk_sizes;
++              blk_size[fake_major] = sizes;
++              INIT_HARDSECT(hardsect_size, fake_major, hardsect_sizes);
++                add_gendisk(&fake_gendisk);
++      }
++
++      for(i=0;i<MAX_DEV;i++) 
++              ubd_add(i);
++
++      if(global_openflags.s){
++              printk(KERN_INFO "ubd : Synchronous mode\n");
++              return(0);
++      }
++      stack = alloc_stack(0, 0);
++      io_pid = start_io_thread(stack + PAGE_SIZE - sizeof(void *), 
++                               &thread_fd);
++      if(io_pid < 0){
++              io_pid = -1;
++              printk(KERN_ERR 
++                     "ubd : Failed to start I/O thread (errno = %d) - "
++                     "falling back to synchronous I/O\n", -io_pid);
++              return(0);
++      }
++      err = um_request_irq(UBD_IRQ, thread_fd, IRQ_READ, ubd_intr, 
++                           SA_INTERRUPT, "ubd", ubd_dev);
++      if(err != 0) 
++              printk(KERN_ERR "um_request_irq failed - errno = %d\n", -err);
++      return(err);
++}
++
++__initcall(ubd_init);
++
++static void ubd_close(struct ubd *dev)
++{
++      if(ubd_do_mmap)
++              physmem_forget_descriptor(dev->fd);
++      os_close_file(dev->fd);
++      if(dev->cow.file != NULL)
++              return;
++
++      if(ubd_do_mmap)
++              physmem_forget_descriptor(dev->cow.fd);
++      os_close_file(dev->cow.fd);
++      vfree(dev->cow.bitmap);
++      dev->cow.bitmap = NULL;
++}
++
++static int ubd_open_dev(struct ubd *dev)
++{
++      struct openflags flags;
++      char **back_ptr;
++      int err, create_cow, *create_ptr;
++
++      dev->openflags = dev->boot_openflags;
++      create_cow = 0;
++      create_ptr = (dev->cow.file != NULL) ? &create_cow : NULL;
++      back_ptr = dev->no_cow ? NULL : &dev->cow.file;
++      dev->fd = open_ubd_file(dev->file, &dev->openflags, back_ptr,
++                              &dev->cow.bitmap_offset, &dev->cow.bitmap_len, 
++                              &dev->cow.data_offset, create_ptr);
++
++      if((dev->fd == -ENOENT) && create_cow){
++              dev->fd = create_cow_file(dev->file, dev->cow.file, 
++                                        dev->openflags, 1 << 9, PAGE_SIZE,
++                                        &dev->cow.bitmap_offset, 
++                                        &dev->cow.bitmap_len,
++                                        &dev->cow.data_offset);
++              if(dev->fd >= 0){
++                      printk(KERN_INFO "Creating \"%s\" as COW file for "
++                             "\"%s\"\n", dev->file, dev->cow.file);
++              }
++      }
++
++      if(dev->fd < 0) return(dev->fd);
++
++      if(dev->cow.file != NULL){
++              err = -ENOMEM;
++              dev->cow.bitmap = (void *) vmalloc(dev->cow.bitmap_len);
++              if(dev->cow.bitmap == NULL){
++                      printk(KERN_ERR "Failed to vmalloc COW bitmap\n");
++                      goto error;
++              }
++              flush_tlb_kernel_vm();
++
++              err = read_cow_bitmap(dev->fd, dev->cow.bitmap, 
++                                    dev->cow.bitmap_offset, 
++                                    dev->cow.bitmap_len);
++              if(err < 0) 
++                      goto error;
++
++              flags = dev->openflags;
++              flags.w = 0;
++              err = open_ubd_file(dev->cow.file, &flags, NULL, NULL, NULL, 
++                                  NULL, NULL);
++              if(err < 0) goto error;
++              dev->cow.fd = err;
++      }
++      return(0);
++ error:
++      os_close_file(dev->fd);
++      return(err);
++}
++
++static int ubd_file_size(struct ubd *dev, __u64 *size_out)
++{
++      char *file;
++
++      file = dev->cow.file ? dev->cow.file : dev->file;
++      return(os_file_size(file, size_out));
++}
++
++static int ubd_open(struct inode *inode, struct file *filp)
++{
++      struct ubd *dev;
++      int n, offset, err = 0;
++
++      n = DEVICE_NR(inode->i_rdev);
++      dev = &ubd_dev[n];
++      if(n >= MAX_DEV)
++              return -ENODEV;
++
++      spin_lock(&ubd_lock);
++      offset = n << UBD_SHIFT;
++
++      if(dev->count == 0){
++              err = ubd_open_dev(dev);
++              if(err){
++                      printk(KERN_ERR "ubd%d: Can't open \"%s\": "
++                             "errno = %d\n", n, dev->file, -err);
++                      goto out;
++              }
++              err = ubd_file_size(dev, &dev->size);
++              if(err < 0)
++                      goto out;
++              sizes[offset] = dev->size / BLOCK_SIZE;
++              ubd_part[offset].nr_sects = dev->size / hardsect_sizes[offset];
++      }
++      dev->count++;
++      if((filp->f_mode & FMODE_WRITE) && !dev->openflags.w){
++              if(--dev->count == 0) ubd_close(dev);
++              err = -EROFS;
++      }
++ out:
++      spin_unlock(&ubd_lock);
++      return(err);
++}
++
++static int ubd_release(struct inode * inode, struct file * file)
++{
++        int n, offset;
++
++      n =  DEVICE_NR(inode->i_rdev);
++      offset = n << UBD_SHIFT;
++      if(n >= MAX_DEV)
++              return -ENODEV;
++
++      spin_lock(&ubd_lock);
++      if(--ubd_dev[n].count == 0)
++              ubd_close(&ubd_dev[n]);
++      spin_unlock(&ubd_lock);
++
++      return(0);
++}
++
++static void cowify_bitmap(__u64 io_offset, int length, unsigned long *cow_mask, 
++                        __u64 *cow_offset, unsigned long *bitmap, 
++                        __u64 bitmap_offset, unsigned long *bitmap_words,
++                        __u64 bitmap_len)
++{
++      __u64 sector = io_offset >> 9;
++      int i, update_bitmap = 0;
++
++      for(i = 0; i < length >> 9; i++){
++              if(cow_mask != NULL)
++                      ubd_set_bit(i, (unsigned char *) cow_mask);
++              if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
++                      continue;
++
++              update_bitmap = 1;
++              ubd_set_bit(sector + i, (unsigned char *) bitmap);
++      }
++
++      if(!update_bitmap)
++              return;
++
++      *cow_offset = sector / (sizeof(unsigned long) * 8);
++
++      /* This takes care of the case where we're exactly at the end of the
++       * device, and *cow_offset + 1 is off the end.  So, just back it up
++       * by one word.  Thanks to Lynn Kerby for the fix and James McMechan
++       * for the original diagnosis.
++       */
++      if(*cow_offset == ((bitmap_len + sizeof(unsigned long) - 1) / 
++                         sizeof(unsigned long) - 1))
++              (*cow_offset)--;
++
++      bitmap_words[0] = bitmap[*cow_offset];
++      bitmap_words[1] = bitmap[*cow_offset + 1];
++
++      *cow_offset *= sizeof(unsigned long);
++      *cow_offset += bitmap_offset;
++}
++
++static void cowify_req(struct io_thread_req *req, unsigned long *bitmap, 
++                     __u64 bitmap_offset, __u64 bitmap_len)
++{
++      __u64 sector = req->offset >> 9;
++        int i;
++
++      if(req->length > (sizeof(req->sector_mask) * 8) << 9)
++              panic("Operation too long");
++
++      if(req->op == UBD_READ) {
++              for(i = 0; i < req->length >> 9; i++){
++                      if(ubd_test_bit(sector + i, (unsigned char *) bitmap)){
++                              ubd_set_bit(i, (unsigned char *) 
++                                          &req->sector_mask);
++                      }
++                }
++        } 
++        else cowify_bitmap(req->offset, req->length, &req->sector_mask,
++                         &req->cow_offset, bitmap, bitmap_offset, 
++                         req->bitmap_words, bitmap_len);
++}
++
++static int mmap_fd(struct request *req, struct ubd *dev, __u64 offset)
++{
++      __u64 sector;
++      unsigned char *bitmap;
++      int bit, i;
++
++      /* mmap must have been requested on the command line */
++      if(!ubd_do_mmap)
++              return(-1);
++
++      /* The buffer must be page aligned */
++      if(((unsigned long) req->buffer % UBD_MMAP_BLOCK_SIZE) != 0)
++              return(-1);
++
++      /* The request must be a page long */
++      if((req->current_nr_sectors << 9) != PAGE_SIZE)
++              return(-1);
++
++      if(dev->cow.file == NULL)
++              return(dev->fd);
++
++      sector = offset >> 9;
++      bitmap = (unsigned char *) dev->cow.bitmap;
++      bit = ubd_test_bit(sector, bitmap);
++
++      for(i = 1; i < req->current_nr_sectors; i++){
++              if(ubd_test_bit(sector + i, bitmap) != bit)
++                      return(-1);
++      }
++
++      if(bit || (req->cmd == WRITE))
++              offset += dev->cow.data_offset;
++
++      /* The data on disk must be page aligned */
++      if((offset % UBD_MMAP_BLOCK_SIZE) != 0)
++              return(-1);
++
++      return(bit ? dev->fd : dev->cow.fd);
++}
++
++static int prepare_mmap_request(struct ubd *dev, int fd, __u64 offset, 
++                              struct request *req, 
++                              struct io_thread_req *io_req)
++{
++      int err;
++
++      if(req->cmd == WRITE){
++              /* Writes are almost no-ops since the new data is already in the
++               * host page cache
++               */
++              dev->map_writes++;
++              if(dev->cow.file != NULL)
++                      cowify_bitmap(io_req->offset, io_req->length, 
++                                    &io_req->sector_mask, &io_req->cow_offset,
++                                    dev->cow.bitmap, dev->cow.bitmap_offset,
++                                    io_req->bitmap_words, 
++                                    dev->cow.bitmap_len);
++      }
++      else {
++              int w;
++
++              if((dev->cow.file != NULL) && (fd == dev->cow.fd))
++                      w = 0;
++              else w = dev->openflags.w;
++
++              if((dev->cow.file != NULL) && (fd == dev->fd))
++                      offset += dev->cow.data_offset;
++
++              err = physmem_subst_mapping(req->buffer, fd, offset, w);
++              if(err){
++                      printk("physmem_subst_mapping failed, err = %d\n", 
++                             -err);
++                      return(1);
++              }
++              dev->map_reads++;
++      }
++      io_req->op = UBD_MMAP;
++      io_req->buffer = req->buffer;
++      return(0);
++}
++
++static int prepare_request(struct request *req, struct io_thread_req *io_req)
++{
++      struct ubd *dev;
++      __u64 offset;
++      int minor, n, len, fd;
++
++      if(req->rq_status == RQ_INACTIVE) return(1);
++
++      minor = MINOR(req->rq_dev);
++      n = minor >> UBD_SHIFT;
++      dev = &ubd_dev[n];
++
++      if(IS_WRITE(req) && !dev->openflags.w){
++              printk("Write attempted on readonly ubd device %d\n", n);
++              end_request(0);
++              return(1);
++      }
++
++        req->sector += ubd_part[minor].start_sect;
++      offset = ((__u64) req->sector) << 9;
++      len = req->current_nr_sectors << 9;
++
++      io_req->fds[0] = (dev->cow.file != NULL) ? dev->cow.fd : dev->fd;
++      io_req->fds[1] = dev->fd;
++      io_req->map_fd = -1;
++      io_req->cow_offset = -1;
++      io_req->offset = offset;
++      io_req->length = len;
++      io_req->error = 0;
++      io_req->sector_mask = 0;
++
++      fd = mmap_fd(req, dev, io_req->offset);
++      if(fd > 0){
++              /* If mmapping is otherwise OK, but the first access to the 
++               * page is a write, then it's not mapped in yet.  So we have 
++               * to write the data to disk first, then we can map the disk
++               * page in and continue normally from there.
++               */
++              if((req->cmd == WRITE) && !is_remapped(req->buffer, dev->fd, io_req->offset + dev->cow.data_offset)){
++                      io_req->map_fd = dev->fd;
++                      io_req->map_offset = io_req->offset + 
++                              dev->cow.data_offset;
++                      dev->write_maps++;
++              }
++              else return(prepare_mmap_request(dev, fd, io_req->offset, req, 
++                                               io_req));
++      }
++
++      if(req->cmd == READ)
++              dev->nomap_reads++;
++      else dev->nomap_writes++;
++
++      io_req->op = (req->cmd == READ) ? UBD_READ : UBD_WRITE;
++      io_req->offsets[0] = 0;
++      io_req->offsets[1] = dev->cow.data_offset;
++      io_req->buffer = req->buffer;
++      io_req->sectorsize = 1 << 9;
++
++        if(dev->cow.file != NULL) 
++              cowify_req(io_req, dev->cow.bitmap, dev->cow.bitmap_offset,
++                         dev->cow.bitmap_len);
++      return(0);
++}
++
++static void do_ubd_request(request_queue_t *q)
++{
++      struct io_thread_req io_req;
++      struct request *req;
++      int err, n;
++
++      if(thread_fd == -1){
++              while(!list_empty(&q->queue_head)){
++                      req = blkdev_entry_next_request(&q->queue_head);
++                      err = prepare_request(req, &io_req);
++                      if(!err){
++                              do_io(&io_req);
++                              ubd_finish(io_req.error);
++                      }
++              }
++      }
++      else {
++              if(DEVICE_INTR || list_empty(&q->queue_head)) return;
++              req = blkdev_entry_next_request(&q->queue_head);
++              err = prepare_request(req, &io_req);
++              if(!err){
++                      SET_INTR(ubd_handler);
++                      n = write_ubd_fs(thread_fd, (char *) &io_req, 
++                                       sizeof(io_req));
++                      if(n != sizeof(io_req))
++                              printk("write to io thread failed, "
++                                     "errno = %d\n", -n);
++              }
++      }
++}
++
++static int ubd_ioctl(struct inode * inode, struct file * file,
++                   unsigned int cmd, unsigned long arg)
++{
++      struct hd_geometry *loc = (struct hd_geometry *) arg;
++      struct ubd *dev;
++      int n, minor, err;
++      struct hd_driveid ubd_id = {
++              .cyls           = 0,
++              .heads          = 128,
++              .sectors        = 32,
++      };
++      
++        if(!inode) return(-EINVAL);
++      minor = MINOR(inode->i_rdev);
++      n = minor >> UBD_SHIFT;
++      if(n >= MAX_DEV)
++              return(-EINVAL);
++      dev = &ubd_dev[n];
++      switch (cmd) {
++              struct hd_geometry g;
++              struct cdrom_volctrl volume;
++      case HDIO_GETGEO:
++              if(!loc) return(-EINVAL);
++              g.heads = 128;
++              g.sectors = 32;
++              g.cylinders = dev->size / (128 * 32 * hardsect_sizes[minor]);
++              g.start = ubd_part[minor].start_sect;
++              return(copy_to_user(loc, &g, sizeof(g)) ? -EFAULT : 0);
++      case BLKGETSIZE:   /* Return device size */
++              if(!arg) return(-EINVAL);
++              err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
++              if(err)
++                      return(err);
++              put_user(ubd_part[minor].nr_sects, (long *) arg);
++              return(0);
++      case BLKRRPART: /* Re-read partition tables */
++              return(ubd_revalidate(inode->i_rdev));
++
++      case HDIO_GET_IDENTITY:
++              ubd_id.cyls = dev->size / (128 * 32 * hardsect_sizes[minor]);
++              if(copy_to_user((char *) arg, (char *) &ubd_id, 
++                               sizeof(ubd_id)))
++                      return(-EFAULT);
++              return(0);
++              
++      case CDROMVOLREAD:
++              if(copy_from_user(&volume, (char *) arg, sizeof(volume)))
++                      return(-EFAULT);
++              volume.channel0 = 255;
++              volume.channel1 = 255;
++              volume.channel2 = 255;
++              volume.channel3 = 255;
++              if(copy_to_user((char *) arg, &volume, sizeof(volume)))
++                      return(-EFAULT);
++              return(0);
++
++      default:
++              return blk_ioctl(inode->i_rdev, cmd, arg);
++      }
++}
++
++static int ubd_revalidate1(kdev_t rdev)
++{
++      int i, n, offset, err = 0, pcount = 1 << UBD_SHIFT;
++      struct ubd *dev;
++      struct hd_struct *part;
++
++      n = DEVICE_NR(rdev);
++      offset = n << UBD_SHIFT;
++      dev = &ubd_dev[n];
++
++      part = &ubd_part[offset];
++
++      /* clear all old partition counts */
++      for(i = 1; i < pcount; i++) {
++              part[i].start_sect = 0;
++              part[i].nr_sects = 0;
++      }
++
++      /* If it already has been opened we can check the partitions 
++       * directly 
++       */
++      if(dev->count){
++              part->start_sect = 0;
++              register_disk(&ubd_gendisk, MKDEV(MAJOR_NR, offset), pcount, 
++                            &ubd_blops, part->nr_sects);
++      } 
++      else if(dev->file){
++              err = ubd_open_dev(dev);
++              if(err){
++                      printk(KERN_ERR "unable to open %s for validation\n",
++                             dev->file);
++                      goto out;
++              }
++
++              /* have to recompute sizes since we opened it */
++              err = ubd_file_size(dev, &dev->size);
++              if(err < 0) {
++                      ubd_close(dev);
++                      goto out;
++              }
++              part->start_sect = 0;
++              part->nr_sects = dev->size / hardsect_sizes[offset];
++              register_disk(&ubd_gendisk, MKDEV(MAJOR_NR, offset), pcount, 
++                            &ubd_blops, part->nr_sects);
++
++              /* we are done so close it */
++              ubd_close(dev);
++      } 
++      else err = -ENODEV;
++ out:
++      return(err);
++}
++
++static int ubd_revalidate(kdev_t rdev)
++{
++      int err;
++
++      spin_lock(&ubd_lock);
++      err = ubd_revalidate1(rdev);
++      spin_unlock(&ubd_lock);
++      return(err);
++}
++
++static int ubd_check_remapped(int fd, unsigned long address, int is_write,
++                            __u64 offset, int is_user)
++{
++      __u64 bitmap_offset;
++      unsigned long new_bitmap[2];
++      int i, err, n;
++
++      /* This can only fix kernelspace faults */
++      if(is_user)
++              return(0);
++
++      /* ubd-mmap is only enabled in skas mode */
++      if(CHOOSE_MODE(1, 0))
++              return(0);
++
++      /* If it's not a write access, we can't do anything about it */
++      if(!is_write)
++              return(0);
++
++      /* We have a write */
++      for(i = 0; i < sizeof(ubd_dev) / sizeof(ubd_dev[0]); i++){
++              struct ubd *dev = &ubd_dev[i];
++
++              if((dev->fd != fd) && (dev->cow.fd != fd))
++                      continue;
++
++              /* It's a write to a ubd device */
++
++              if(!dev->openflags.w){
++                      /* It's a write access on a read-only device - probably
++                       * shouldn't happen.  If the kernel is trying to change
++                       * something with no intention of writing it back out,
++                       * then this message will clue us in that this needs
++                       * fixing
++                       */
++                      printk("Write access to mapped page from readonly ubd "
++                             "device %d\n", i);
++                      return(0);
++              }
++
++              /* It's a write to a writeable ubd device - it must be COWed
++               * because, otherwise, the page would have been mapped in 
++               * writeable
++               */
++
++              if(!dev->cow.file)
++                      panic("Write fault on writeable non-COW ubd device %d",
++                            i);
++
++              /* It should also be an access to the backing file since the 
++               * COW pages should be mapped in read-write
++               */
++
++              if(fd == dev->fd)
++                      panic("Write fault on a backing page of ubd "
++                            "device %d\n", i);
++
++              /* So, we do the write, copying the backing data to the COW 
++               * file... 
++               */
++
++              err = os_seek_file(dev->fd, offset + dev->cow.data_offset);
++              if(err < 0)
++                      panic("Couldn't seek to %lld in COW file of ubd "
++                            "device %d, err = %d", 
++                            offset + dev->cow.data_offset, i, -err);
++
++              n = os_write_file(dev->fd, (void *) address, PAGE_SIZE);
++              if(n != PAGE_SIZE)
++                      panic("Couldn't copy data to COW file of ubd "
++                            "device %d, err = %d", i, -n);
++
++              /* ... updating the COW bitmap... */
++
++              cowify_bitmap(offset, PAGE_SIZE, NULL, &bitmap_offset, 
++                            dev->cow.bitmap, dev->cow.bitmap_offset, 
++                            new_bitmap, dev->cow.bitmap_len);
++
++              err = os_seek_file(dev->fd, bitmap_offset);
++              if(err < 0)
++                      panic("Couldn't seek to %lld in COW file of ubd "
++                            "device %d, err = %d", bitmap_offset, i, -err);
++
++              n = os_write_file(dev->fd, new_bitmap, sizeof(new_bitmap));
++              if(n != sizeof(new_bitmap))
++                      panic("Couldn't update bitmap  of ubd device %d, "
++                            "err = %d", i, -n);
++              
++              /* Maybe we can map the COW page in, and maybe we can't.  If
++               * it is a pre-V3 COW file, we can't, since the alignment will 
++               * be wrong.  If it is a V3 or later COW file which has been 
++               * moved to a system with a larger page size, then maybe we 
++               * can't, depending on the exact location of the page.
++               */
++
++              offset += dev->cow.data_offset;
++
++              /* Remove the remapping, putting the original anonymous page
++               * back.  If the COW file can be mapped in, that is done.
++               * Otherwise, the COW page is read in.
++               */
++
++              if(!physmem_remove_mapping((void *) address))
++                      panic("Address 0x%lx not remapped by ubd device %d", 
++                            address, i);
++              if((offset % UBD_MMAP_BLOCK_SIZE) == 0)
++                      physmem_subst_mapping((void *) address, dev->fd, 
++                                            offset, 1);
++              else {
++                      err = os_seek_file(dev->fd, offset);
++                      if(err < 0)
++                              panic("Couldn't seek to %lld in COW file of "
++                                    "ubd device %d, err = %d", offset, i, 
++                                    -err);
++
++                      n = os_read_file(dev->fd, (void *) address, PAGE_SIZE);
++                      if(n != PAGE_SIZE)
++                              panic("Failed to read page from offset %llx of "
++                                    "COW file of ubd device %d, err = %d",
++                                    offset, i, -n);
++              }
++
++              return(1);
++      }
++
++      /* It's not a write on a ubd device */
++      return(0);
++}
++
++static struct remapper ubd_remapper = {
++      .list   = LIST_HEAD_INIT(ubd_remapper.list),
++      .proc   = ubd_check_remapped,
++};
++
++static int ubd_remapper_setup(void)
++{
++      if(ubd_do_mmap)
++              register_remapper(&ubd_remapper);
++
++      return(0);
++}
++
++__initcall(ubd_remapper_setup);
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/ubd_user.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/ubd_user.c       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/ubd_user.c    2005-05-03 22:28:14.259442448 +0300
+@@ -0,0 +1,379 @@
++/* 
++ * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Copyright (C) 2001 Ridgerun,Inc (glonnon@ridgerun.com)
++ * Licensed under the GPL
++ */
++
++#include <stddef.h>
++#include <unistd.h>
++#include <errno.h>
++#include <sched.h>
++#include <signal.h>
++#include <string.h>
++#include <netinet/in.h>
++#include <sys/time.h>
++#include <sys/socket.h>
++#include <sys/mman.h>
++#include <sys/param.h>
++#include "asm/types.h"
++#include "user_util.h"
++#include "kern_util.h"
++#include "user.h"
++#include "ubd_user.h"
++#include "os.h"
++#include "cow.h"
++
++#include <endian.h>
++#include <byteswap.h>
++
++static int same_backing_files(char *from_cmdline, char *from_cow, char *cow)
++{
++      struct uml_stat buf1, buf2;
++      int err;
++
++      if(from_cmdline == NULL) return(1);
++      if(!strcmp(from_cmdline, from_cow)) return(1);
++
++      err = os_stat_file(from_cmdline, &buf1);
++      if(err < 0){
++              printk("Couldn't stat '%s', err = %d\n", from_cmdline, -err);
++              return(1);
++      }
++      err = os_stat_file(from_cow, &buf2);
++      if(err < 0){
++              printk("Couldn't stat '%s', err = %d\n", from_cow, -err);
++              return(1);
++      }
++      if((buf1.ust_major == buf2.ust_major) && 
++         (buf1.ust_minor == buf2.ust_minor) && 
++         (buf1.ust_ino == buf2.ust_ino))
++              return(1);
++
++      printk("Backing file mismatch - \"%s\" requested,\n"
++             "\"%s\" specified in COW header of \"%s\"\n",
++             from_cmdline, from_cow, cow);
++      return(0);
++}
++
++static int backing_file_mismatch(char *file, __u64 size, time_t mtime)
++{
++      unsigned long modtime;
++      long long actual;
++      int err;
++
++      err = os_file_modtime(file, &modtime);
++      if(err < 0){
++              printk("Failed to get modification time of backing file "
++                     "\"%s\", err = %d\n", file, -err);
++              return(err);
++      }
++
++      err = os_file_size(file, &actual);
++      if(err < 0){
++              printk("Failed to get size of backing file \"%s\", "
++                     "err = %d\n", file, -err);
++              return(err);
++      }
++
++      if(actual != size){
++              printk("Size mismatch (%ld vs %ld) of COW header vs backing "
++                     "file\n", size, actual);
++              return(-EINVAL);
++      }
++      if(modtime != mtime){
++              printk("mtime mismatch (%ld vs %ld) of COW header vs backing "
++                     "file\n", mtime, modtime);
++              return(-EINVAL);
++      }
++      return(0);
++}
++
++int read_cow_bitmap(int fd, void *buf, int offset, int len)
++{
++      int err;
++
++      err = os_seek_file(fd, offset);
++      if(err < 0) 
++              return(err);
++
++      err = os_read_file(fd, buf, len);
++      if(err < 0) 
++              return(err);
++
++      return(0);
++}
++
++int open_ubd_file(char *file, struct openflags *openflags, 
++                char **backing_file_out, int *bitmap_offset_out, 
++                unsigned long *bitmap_len_out, int *data_offset_out, 
++                int *create_cow_out)
++{
++      time_t mtime;
++      __u64 size;
++      __u32 version, align;
++      char *backing_file;
++      int fd, err, sectorsize, same, mode = 0644;
++
++      fd = os_open_file(file, *openflags, mode);
++      if(fd < 0){
++              if((fd == -ENOENT) && (create_cow_out != NULL))
++                      *create_cow_out = 1;
++                if(!openflags->w ||
++                   ((errno != EROFS) && (errno != EACCES))) return(-errno);
++              openflags->w = 0;
++              fd = os_open_file(file, *openflags, mode); 
++              if(fd < 0) 
++                      return(fd);
++        }
++
++      err = os_lock_file(fd, openflags->w);
++      if(err < 0){
++              printk("Failed to lock '%s', err = %d\n", file, -err);
++              goto out_close;
++      }
++      
++      if(backing_file_out == NULL) return(fd);
++
++      err = read_cow_header(file_reader, &fd, &version, &backing_file, &mtime,
++                            &size, &sectorsize, &align, bitmap_offset_out);
++      if(err && (*backing_file_out != NULL)){
++              printk("Failed to read COW header from COW file \"%s\", "
++                     "errno = %d\n", file, -err);
++              goto out_close;
++      }
++      if(err) return(fd);
++
++      if(backing_file_out == NULL) return(fd);
++      
++      same = same_backing_files(*backing_file_out, backing_file, file);
++
++      if(!same && !backing_file_mismatch(*backing_file_out, size, mtime)){
++              printk("Switching backing file to '%s'\n", *backing_file_out);
++              err = write_cow_header(file, fd, *backing_file_out,
++                                     sectorsize, align, &size);
++              if(err){
++                      printk("Switch failed, errno = %d\n", -err);
++                      return(err);
++              }
++      }
++      else {
++              *backing_file_out = backing_file;
++              err = backing_file_mismatch(*backing_file_out, size, mtime);
++              if(err) goto out_close;
++      }
++
++      cow_sizes(version, size, sectorsize, align, *bitmap_offset_out, 
++                bitmap_len_out, data_offset_out);
++
++        return(fd);
++ out_close:
++      os_close_file(fd);
++      return(err);
++}
++
++int create_cow_file(char *cow_file, char *backing_file, struct openflags flags,
++                  int sectorsize, int alignment, int *bitmap_offset_out, 
++                  unsigned long *bitmap_len_out, int *data_offset_out)
++{
++      int err, fd;
++
++      flags.c = 1;
++      fd = open_ubd_file(cow_file, &flags, NULL, NULL, NULL, NULL, NULL);
++      if(fd < 0){
++              err = fd;
++              printk("Open of COW file '%s' failed, errno = %d\n", cow_file,
++                     -err);
++              goto out;
++      }
++
++      err = init_cow_file(fd, cow_file, backing_file, sectorsize, alignment,
++                          bitmap_offset_out, bitmap_len_out, 
++                          data_offset_out);
++      if(!err)
++              return(fd);
++
++      os_close_file(fd);
++ out:
++      return(err);
++}
++
++/* XXX Just trivial wrappers around os_read_file and os_write_file */
++int read_ubd_fs(int fd, void *buffer, int len)
++{
++      return(os_read_file(fd, buffer, len));
++}
++
++int write_ubd_fs(int fd, char *buffer, int len)
++{
++      return(os_write_file(fd, buffer, len));
++}
++
++static int update_bitmap(struct io_thread_req *req)
++{
++      int n;
++
++      if(req->cow_offset == -1)
++              return(0);
++
++      n = os_seek_file(req->fds[1], req->cow_offset);
++      if(n < 0){
++              printk("do_io - bitmap lseek failed : err = %d\n", -n);
++              return(1);
++      }
++
++      n = os_write_file(req->fds[1], &req->bitmap_words,
++                        sizeof(req->bitmap_words));
++      if(n != sizeof(req->bitmap_words)){
++              printk("do_io - bitmap update failed, err = %d fd = %d\n", -n,
++                     req->fds[1]);
++              return(1);
++      }
++
++      return(0);
++}
++
++void do_io(struct io_thread_req *req)
++{
++      char *buf;
++      unsigned long len;
++      int n, nsectors, start, end, bit;
++      int err;
++      __u64 off;
++
++      if(req->op == UBD_MMAP){
++              /* Touch the page to force the host to do any necessary IO to 
++               * get it into memory 
++               */
++              n = *((volatile int *) req->buffer);
++              req->error = update_bitmap(req);
++              return;
++      }
++
++      nsectors = req->length / req->sectorsize;
++      start = 0;
++      do {
++              bit = ubd_test_bit(start, (unsigned char *) &req->sector_mask);
++              end = start;
++              while((end < nsectors) && 
++                    (ubd_test_bit(end, (unsigned char *) 
++                                  &req->sector_mask) == bit))
++                      end++;
++
++              off = req->offset + req->offsets[bit] + 
++                      start * req->sectorsize;
++              len = (end - start) * req->sectorsize;
++              buf = &req->buffer[start * req->sectorsize];
++
++              err = os_seek_file(req->fds[bit], off);
++              if(err < 0){
++                      printk("do_io - lseek failed : err = %d\n", -err);
++                      req->error = 1;
++                      return;
++              }
++              if(req->op == UBD_READ){
++                      n = 0;
++                      do {
++                              buf = &buf[n];
++                              len -= n;
++                              n = os_read_file(req->fds[bit], buf, len);
++                              if (n < 0) {
++                                      printk("do_io - read failed, err = %d "
++                                             "fd = %d\n", -n, req->fds[bit]);
++                                      req->error = 1;
++                                      return;
++                              }
++                      } while((n < len) && (n != 0));
++                      if (n < len) memset(&buf[n], 0, len - n);
++              }
++              else {
++                      n = os_write_file(req->fds[bit], buf, len);
++                      if(n != len){
++                              printk("do_io - write failed err = %d "
++                                     "fd = %d\n", -n, req->fds[bit]);
++                              req->error = 1;
++                              return;
++                      }
++              }
++
++              start = end;
++      } while(start < nsectors);
++
++      req->error = update_bitmap(req);
++}
++
++/* Changed in start_io_thread, which is serialized by being called only
++ * from ubd_init, which is an initcall.
++ */
++int kernel_fd = -1;
++
++/* Only changed by the io thread */
++int io_count = 0;
++
++int io_thread(void *arg)
++{
++      struct io_thread_req req;
++      int n;
++
++      signal(SIGWINCH, SIG_IGN);
++      while(1){
++              n = os_read_file(kernel_fd, &req, sizeof(req));
++              if(n != sizeof(req)){
++                      if(n < 0)
++                              printk("io_thread - read failed, fd = %d, "
++                                     "err = %d\n", kernel_fd, -n);
++                      else {
++                              printk("io_thread - short read, fd = %d, "
++                                     "length = %d\n", kernel_fd, n);
++                      }
++                      continue;
++              }
++              io_count++;
++              do_io(&req);
++              n = os_write_file(kernel_fd, &req, sizeof(req));
++              if(n != sizeof(req))
++                      printk("io_thread - write failed, fd = %d, err = %d\n",
++                             kernel_fd, -n);
++      }
++}
++
++int start_io_thread(unsigned long sp, int *fd_out)
++{
++      int pid, fds[2], err;
++
++      err = os_pipe(fds, 1, 1);
++      if(err < 0){
++              printk("start_io_thread - os_pipe failed, err = %d\n", -err);
++              goto out;
++      }
++
++      kernel_fd = fds[0];
++      *fd_out = fds[1];
++
++      pid = clone(io_thread, (void *) sp, CLONE_FILES | CLONE_VM | SIGCHLD,
++                  NULL);
++      if(pid < 0){
++              printk("start_io_thread - clone failed : errno = %d\n", errno);
++              goto out_close;
++      }
++
++      return(pid);
++
++ out_close:
++      os_close_file(fds[0]);
++      os_close_file(fds[1]);
++      kernel_fd = -1;
++      *fd_out = -1;
++ out:
++      return(err);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/xterm.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/xterm.c  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/xterm.c       2005-05-03 22:28:14.260442296 +0300
+@@ -0,0 +1,213 @@
++/* 
++ * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <string.h>
++#include <errno.h>
++#include <termios.h>
++#include <signal.h>
++#include <sched.h>
++#include <sys/socket.h>
++#include "kern_util.h"
++#include "chan_user.h"
++#include "helper.h"
++#include "user_util.h"
++#include "user.h"
++#include "os.h"
++#include "xterm.h"
++
++struct xterm_chan {
++      int pid;
++      int helper_pid;
++      char *title;
++      int device;
++      int raw;
++      struct termios tt;
++      unsigned long stack;
++      int direct_rcv;
++};
++
++void *xterm_init(char *str, int device, struct chan_opts *opts)
++{
++      struct xterm_chan *data;
++
++      data = malloc(sizeof(*data));
++      if(data == NULL) return(NULL);
++      *data = ((struct xterm_chan) { .pid             = -1, 
++                                     .helper_pid      = -1,
++                                     .device          = device, 
++                                     .title           = opts->xterm_title,
++                                     .raw             = opts->raw,
++                                     .stack           = opts->tramp_stack,
++                                     .direct_rcv      = !opts->in_kernel } );
++      return(data);
++}
++
++/* Only changed by xterm_setup, which is a setup */
++static char *terminal_emulator = "xterm";
++static char *title_switch = "-T";
++static char *exec_switch = "-e";
++
++static int __init xterm_setup(char *line, int *add)
++{
++      *add = 0;
++      terminal_emulator = line;
++
++      line = strchr(line, ',');
++      if(line == NULL) return(0);
++      *line++ = '\0';
++      if(*line) title_switch = line;
++
++      line = strchr(line, ',');
++      if(line == NULL) return(0);
++      *line++ = '\0';
++      if(*line) exec_switch = line;
++
++      return(0);
++}
++
++__uml_setup("xterm=", xterm_setup,
++"xterm=<terminal emulator>,<title switch>,<exec switch>\n"
++"    Specifies an alternate terminal emulator to use for the debugger,\n"
++"    consoles, and serial lines when they are attached to the xterm channel.\n"
++"    The values are the terminal emulator binary, the switch it uses to set\n"
++"    its title, and the switch it uses to execute a subprocess,\n"
++"    respectively.  The title switch must have the form '<switch> title',\n"
++"    not '<switch>=title'.  Similarly, the exec switch must have the form\n"
++"    '<switch> command arg1 arg2 ...'.\n"
++"    The default values are 'xterm=xterm,-T,-e'.  Values for gnome-terminal\n"
++"    are 'xterm=gnome-terminal,-t,-x'.\n\n"
++);
++
++/* XXX This badly needs some cleaning up in the error paths */
++int xterm_open(int input, int output, int primary, void *d, char **dev_out)
++{
++      struct xterm_chan *data = d;
++      unsigned long stack;
++      int pid, fd, new, err;
++      char title[256], file[] = "/tmp/xterm-pipeXXXXXX";
++      char *argv[] = { terminal_emulator, title_switch, title, exec_switch, 
++                       "/usr/lib/uml/port-helper", "-uml-socket",
++                       file, NULL };
++
++      if(os_access(argv[4], OS_ACC_X_OK) < 0)
++              argv[4] = "port-helper";
++
++      fd = mkstemp(file);
++      if(fd < 0){
++              printk("xterm_open : mkstemp failed, errno = %d\n", errno);
++              return(-errno);
++      }
++
++      if(unlink(file)){
++              printk("xterm_open : unlink failed, errno = %d\n", errno);
++              return(-errno);
++      }
++      os_close_file(fd);
++
++      fd = os_create_unix_socket(file, sizeof(file), 1);
++      if(fd < 0){
++              printk("xterm_open : create_unix_socket failed, errno = %d\n", 
++                     -fd);
++              return(fd);
++      }
++
++      sprintf(title, data->title, data->device);
++      stack = data->stack;
++      pid = run_helper(NULL, NULL, argv, &stack);
++      if(pid < 0){
++              printk("xterm_open : run_helper failed, errno = %d\n", -pid);
++              return(pid);
++      }
++
++      if(data->stack == 0) free_stack(stack, 0);
++
++      if(data->direct_rcv)
++              new = os_rcv_fd(fd, &data->helper_pid);
++      else {
++              err = os_set_fd_block(fd, 0);
++              if(err < 0){
++                      printk("xterm_open : failed to set descriptor "
++                             "non-blocking, err = %d\n", -err);
++                      return(err);
++              }
++              new = xterm_fd(fd, &data->helper_pid);
++      }
++      if(new < 0){
++              printk("xterm_open : os_rcv_fd failed, err = %d\n", -new);
++              goto out;
++      }
++
++      CATCH_EINTR(err = tcgetattr(new, &data->tt));
++      if(err){
++              new = err;
++              goto out;
++      }
++
++      if(data->raw){
++              err = raw(new);
++              if(err){
++                      new = err;
++                      goto out;
++              }
++      }
++
++      data->pid = pid;
++      *dev_out = NULL;
++ out:
++      unlink(file);
++      return(new);
++}
++
++void xterm_close(int fd, void *d)
++{
++      struct xterm_chan *data = d;
++      
++      if(data->pid != -1) 
++              os_kill_process(data->pid, 1);
++      data->pid = -1;
++      if(data->helper_pid != -1) 
++              os_kill_process(data->helper_pid, 0);
++      data->helper_pid = -1;
++      os_close_file(fd);
++}
++
++void xterm_free(void *d)
++{
++      free(d);
++}
++
++int xterm_console_write(int fd, const char *buf, int n, void *d)
++{
++      struct xterm_chan *data = d;
++
++      return(generic_console_write(fd, buf, n, &data->tt));
++}
++
++struct chan_ops xterm_ops = {
++      .type           = "xterm",
++      .init           = xterm_init,
++      .open           = xterm_open,
++      .close          = xterm_close,
++      .read           = generic_read,
++      .write          = generic_write,
++      .console_write  = xterm_console_write,
++      .window_size    = generic_window_size,
++      .free           = xterm_free,
++      .winch          = 1,
++};
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/xterm.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/xterm.h  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/xterm.h       2005-05-03 22:28:14.261442144 +0300
+@@ -0,0 +1,22 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __XTERM_H__
++#define __XTERM_H__
++
++extern int xterm_fd(int socket, int *pid_out);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/drivers/xterm_kern.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/drivers/xterm_kern.c     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/drivers/xterm_kern.c  2005-05-03 22:28:14.262441992 +0300
+@@ -0,0 +1,82 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/errno.h"
++#include "linux/slab.h"
++#include "asm/semaphore.h"
++#include "asm/irq.h"
++#include "irq_user.h"
++#include "irq_kern.h"
++#include "kern_util.h"
++#include "os.h"
++#include "xterm.h"
++
++struct xterm_wait {
++      struct semaphore sem;
++      int fd;
++      int pid;
++      int new_fd;
++};
++
++static void xterm_interrupt(int irq, void *data, struct pt_regs *regs)
++{
++      struct xterm_wait *xterm = data;
++      int fd;
++
++      fd = os_rcv_fd(xterm->fd, &xterm->pid);
++      if(fd == -EAGAIN)
++              return;
++
++      xterm->new_fd = fd;
++      up(&xterm->sem);
++}
++
++int xterm_fd(int socket, int *pid_out)
++{
++      struct xterm_wait *data;
++      int err, ret;
++
++      data = kmalloc(sizeof(*data), GFP_KERNEL);
++      if(data == NULL){
++              printk(KERN_ERR "xterm_fd : failed to allocate xterm_wait\n");
++              return(-ENOMEM);
++      }
++      *data = ((struct xterm_wait) 
++              { .sem          = __SEMAPHORE_INITIALIZER(data->sem, 0),
++                .fd           = socket,
++                .pid          = -1,
++                .new_fd       = -1 });
++
++      err = um_request_irq(XTERM_IRQ, socket, IRQ_READ, xterm_interrupt, 
++                           SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM, 
++                           "xterm", data);
++      if(err){
++              printk(KERN_ERR "xterm_fd : failed to get IRQ for xterm, "
++                     "err = %d\n",  err);
++              ret = err;
++              goto out;
++      }
++      down(&data->sem);
++
++      free_irq(XTERM_IRQ, data);
++
++      ret = data->new_fd;
++      *pid_out = data->pid;
++ out:
++      kfree(data);
++
++      return(ret);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/fs/hostfs/externfs.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/fs/hostfs/externfs.c     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/fs/hostfs/externfs.c  2005-05-03 22:28:14.269440928 +0300
+@@ -0,0 +1,1283 @@
++/* 
++ * Copyright (C) 2000 - 2004 Jeff Dike (jdike@addtoit.com)
++ * Licensed under the GPL
++ */
++
++#include <linux/stddef.h>
++#include <linux/fs.h>
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/pagemap.h>
++#include <linux/blkdev.h>
++#include <asm/uaccess.h>
++#include "hostfs.h"
++#include "kern_util.h"
++#include "kern.h"
++#include "user_util.h"
++#include "2_5compat.h"
++#include "mem.h"
++#include "filehandle.h"
++
++struct externfs {
++      struct list_head list;
++      struct externfs_mount_ops *mount_ops;
++      struct file_system_type type;
++};
++
++static inline struct externfs_inode *EXTERNFS_I(struct inode *inode)
++{
++      return(inode->u.generic_ip);
++}
++
++#define file_externfs_i(file) EXTERNFS_I((file)->f_dentry->d_inode)
++
++int externfs_d_delete(struct dentry *dentry)
++{
++      return(1);
++}
++
++struct dentry_operations externfs_dentry_ops = {
++};
++
++#define EXTERNFS_SUPER_MAGIC 0x00c0ffee
++
++static struct inode_operations externfs_iops;
++static struct inode_operations externfs_dir_iops;
++static struct address_space_operations externfs_link_aops;
++
++static char *dentry_name(struct dentry *dentry, int extra)
++{
++      struct dentry *parent;
++      char *name;
++      int len;
++
++      len = 0;
++      parent = dentry;
++      while(parent->d_parent != parent){
++              len += parent->d_name.len + 1;
++              parent = parent->d_parent;
++      }
++      
++      name = kmalloc(len + extra + 1, GFP_KERNEL);
++      if(name == NULL) return(NULL);
++
++      name[len] = '\0';
++      parent = dentry;
++      while(parent->d_parent != parent){
++              len -= parent->d_name.len + 1;
++              name[len] = '/';
++              strncpy(&name[len + 1], parent->d_name.name, 
++                      parent->d_name.len);
++              parent = parent->d_parent;
++      }
++
++      return(name);
++}
++
++char *inode_name(struct inode *ino, int extra)
++{
++      struct dentry *dentry;
++
++      dentry = list_entry(ino->i_dentry.next, struct dentry, d_alias);
++      return(dentry_name(dentry, extra));
++}
++
++char *inode_name_prefix(struct inode *inode, char *prefix)
++{
++      int len;
++      char *name;
++
++      len = strlen(prefix);
++      name = inode_name(inode, len);
++      if(name == NULL)
++              return(name);
++
++      memmove(&name[len], name, strlen(name) + 1);
++      memcpy(name, prefix, strlen(prefix));
++      return(name);
++}
++
++static int read_name(struct inode *ino, char *name)
++{
++      struct externfs_file_ops *ops = EXTERNFS_I(ino)->ops;
++      /* The non-int inode fields are copied into ints by stat_file and
++       * then copied into the inode because passing the actual pointers
++       * in and having them treated as int * breaks on big-endian machines
++       */
++      int err;
++      int i_dev, i_mode, i_nlink, i_blksize;
++      unsigned long long i_size;
++      unsigned long long i_ino;
++      unsigned long long i_blocks;
++
++      err = (*ops->stat_file)(name, ino->i_sb->u.generic_sbp, 
++                              (dev_t *) &i_dev, &i_ino, &i_mode, &i_nlink,
++                              &ino->i_uid, &ino->i_gid, &i_size, 
++                              &ino->i_atime, &ino->i_mtime, &ino->i_ctime, 
++                              &i_blksize, &i_blocks);
++      if(err) return(err);
++      ino->i_ino = i_ino;
++      ino->i_dev = i_dev;
++      ino->i_mode = i_mode;
++      ino->i_nlink = i_nlink;
++      ino->i_size = i_size;
++      ino->i_blksize = i_blksize;
++      ino->i_blocks = i_blocks;
++      return(0);
++}
++
++static char *follow_link(char *link, 
++                       int (*do_read_link)(char *path, int uid, int gid,
++                                           char *buf, int size, 
++                                           struct externfs_data *ed),
++                       int uid, int gid, struct externfs_data *ed)
++{
++      int len, n;
++      char *name, *resolved, *end;
++
++      len = 64;
++      while(1){
++              n = -ENOMEM;
++              name = kmalloc(len, GFP_KERNEL);
++              if(name == NULL)
++                      goto out;
++
++              n = (*do_read_link)(link, uid, gid, name, len, ed);
++              if(n < len)
++                      break;
++              len *= 2;
++              kfree(name);
++      }
++      if(n < 0)
++              goto out_free;
++
++      if(*name == '/')
++              return(name);
++
++      end = strrchr(link, '/');
++      if(end == NULL)
++              return(name);
++
++      *(end + 1) = '\0';
++      len = strlen(link) + strlen(name) + 1;
++
++      resolved = kmalloc(len, GFP_KERNEL);
++      if(resolved == NULL){
++              n = -ENOMEM;
++              goto out_free;
++      }
++
++      sprintf(resolved, "%s%s", link, name);
++      kfree(name);
++      return(resolved);
++
++ out_free:
++      kfree(name);
++ out:
++      return(ERR_PTR(n));
++}
++
++static int read_inode(struct inode *ino)
++{
++      struct externfs_file_ops *ops = EXTERNFS_I(ino)->ops;
++      struct externfs_data *ed = ino->i_sb->u.generic_sbp;
++      char *name, *new;
++      int err, type;
++
++      err = -ENOMEM;
++      name = inode_name(ino, 0);
++      if(name == NULL) 
++              goto out;
++
++      type = (*ops->file_type)(name, NULL, ed);
++      if(type < 0){
++              err = type;
++              goto out_free;
++      }
++
++      if(type == OS_TYPE_SYMLINK){
++              new = follow_link(name, ops->read_link, current->fsuid,
++                                current->fsgid, ed);
++              if(IS_ERR(new)){
++                      err = PTR_ERR(new);
++                      goto out_free;
++              }
++              kfree(name);
++              name = new;
++      }
++      
++      err = read_name(ino, name);
++ out_free:
++      kfree(name);
++ out:
++      return(err);
++}
++
++void externfs_delete_inode(struct inode *ino)
++{
++      struct externfs_inode *ext = EXTERNFS_I(ino);
++      struct externfs_file_ops *ops = ext->ops;
++
++      (*ops->close_file)(ext, ino->i_size);
++
++      clear_inode(ino);
++}
++
++int externfs_statfs(struct super_block *sb, struct statfs *sf)
++{
++      /* do_statfs uses struct statfs64 internally, but the linux kernel
++       * struct statfs still has 32-bit versions for most of these fields,
++       * so we convert them here
++       */
++      int err;
++      long long f_blocks;
++      long long f_bfree;
++      long long f_bavail;
++      long long f_files;
++      long long f_ffree;
++      struct externfs_data *ed = sb->u.generic_sbp;
++      
++      err = (*ed->file_ops->statfs)(&sf->f_bsize, &f_blocks, &f_bfree, 
++                                    &f_bavail, &f_files, &f_ffree, 
++                                    &sf->f_fsid, sizeof(sf->f_fsid), 
++                                    &sf->f_namelen, sf->f_spare, ed);
++      if(err)
++              return(err);
++
++      sf->f_blocks = f_blocks;
++      sf->f_bfree = f_bfree;
++      sf->f_bavail = f_bavail;
++      sf->f_files = f_files;
++      sf->f_ffree = f_ffree;
++      sf->f_type = EXTERNFS_SUPER_MAGIC;
++      return(0);
++}
++
++static struct super_operations externfs_sbops = { 
++      .delete_inode   = externfs_delete_inode,
++      .statfs         = externfs_statfs,
++};
++
++int externfs_readdir(struct file *file, void *ent, filldir_t filldir)
++{
++      void *dir;
++      char *name;
++      unsigned long long next, ino;
++      int error, len;
++      struct externfs_file_ops *ops = file_externfs_i(file)->ops;
++      struct externfs_data *ed = 
++              file->f_dentry->d_inode->i_sb->u.generic_sbp;
++
++      name = dentry_name(file->f_dentry, 0);
++      if(name == NULL) 
++              return(-ENOMEM);
++
++      dir = (*ops->open_dir)(name, current->fsuid, current->fsgid, ed);
++      kfree(name);
++      if(IS_ERR(dir)) 
++              return(PTR_ERR(dir));
++
++      next = file->f_pos;
++      while((name = (*ops->read_dir)(dir, &next, &ino, &len, ed)) != NULL){
++              error = (*filldir)(ent, name, len, file->f_pos, ino, 
++                                 DT_UNKNOWN);
++              if(error) 
++                      break;
++              file->f_pos = next;
++      }
++      (*ops->close_dir)(dir, ed);
++      return(0);
++}
++
++int externfs_file_open(struct inode *ino, struct file *file)
++{
++      ino->i_nlink++;
++      return(0);
++}
++
++int externfs_dir_open(struct inode *ino, struct file *file)
++{
++      return(0);      
++}
++
++int externfs_dir_release(struct inode *ino, struct file *file)
++{
++      return(0);
++}
++
++int externfs_fsync(struct file *file, struct dentry *dentry, int datasync)
++{
++      struct externfs_file_ops *ops = file_externfs_i(file)->ops;
++      struct inode *inode = dentry->d_inode;
++      struct externfs_data *ed = inode->i_sb->u.generic_sbp;
++
++      return((*ops->truncate_file)(EXTERNFS_I(inode), inode->i_size, ed));
++}
++
++static struct file_operations externfs_file_fops = {
++      .owner          = NULL,
++      .read           = generic_file_read,
++      .write          = generic_file_write,
++      .mmap           = generic_file_mmap,
++      .open           = externfs_file_open,
++      .release        = NULL,
++      .fsync          = externfs_fsync,
++};
++
++static struct file_operations externfs_dir_fops = {
++      .owner          = NULL,
++      .readdir        = externfs_readdir,
++      .open           = externfs_dir_open,
++      .release        = externfs_dir_release,
++      .fsync          = externfs_fsync,
++};
++
++struct wp_info {
++      struct page *page;
++      int count;
++      unsigned long long start;
++      unsigned long long size;
++      int (*truncate)(struct externfs_inode *ei, __u64 size, 
++                      struct externfs_data *ed);
++      struct externfs_inode *ei;
++      struct externfs_data *ed;
++};
++
++static void externfs_finish_writepage(char *buffer, int res, void *arg)
++{
++      struct wp_info *wp = arg;
++
++      if(res == wp->count){
++              ClearPageError(wp->page);
++              if(wp->start + res > wp->size)
++                      (*wp->truncate)(wp->ei, wp->size, wp->ed);
++      }
++      else {
++              SetPageError(wp->page);
++              ClearPageUptodate(wp->page);
++      }               
++
++      kunmap(wp->page);
++      unlock_page(wp->page);
++      kfree(wp);
++}
++
++static int externfs_writepage(struct page *page)
++{
++      struct address_space *mapping = page->mapping;
++      struct inode *inode = mapping->host;
++      struct externfs_file_ops *ops = EXTERNFS_I(inode)->ops;
++      struct wp_info *wp;
++      struct externfs_data *ed = inode->i_sb->u.generic_sbp;
++      char *buffer;
++      unsigned long long base;
++      int count = PAGE_CACHE_SIZE;
++      int end_index = inode->i_size >> PAGE_CACHE_SHIFT;
++      int err, offset;
++
++      base = ((unsigned long long) page->index) << PAGE_CACHE_SHIFT;
++
++      /* If we are entirely outside the file, then return an error */
++      err = -EIO;
++      offset = inode->i_size & (PAGE_CACHE_SIZE-1);
++      if (page->index > end_index || 
++          ((page->index == end_index) && !offset))
++              goto out_unlock;
++
++      err = -ENOMEM;
++      wp = kmalloc(sizeof(*wp), GFP_KERNEL);
++      if(wp == NULL)
++              goto out_unlock;
++
++      *wp = ((struct wp_info) { .page         = page,
++                                .count        = count,
++                                .start        = base,
++                                .size         = inode->i_size,
++                                .truncate     = ops->truncate_file,
++                                .ei           = EXTERNFS_I(inode),
++                                .ed           = ed });
++
++      buffer = kmap(page);
++      err = (*ops->write_file)(EXTERNFS_I(inode), base, buffer, 0, 
++                               count, externfs_finish_writepage, wp, ed);
++
++      return err;
++
++ out_unlock:
++      unlock_page(page);
++      return(err);
++}
++
++static void externfs_finish_readpage(char *buffer, int res, void *arg)
++{
++      struct page *page = arg;
++      struct inode *inode;
++
++      if(res < 0){
++              SetPageError(page);
++              goto out;
++      }
++
++      inode = page->mapping->host;
++      if(inode->i_size >> PAGE_CACHE_SHIFT == page->index)
++              res = inode->i_size % PAGE_CACHE_SIZE;
++
++      memset(&buffer[res], 0, PAGE_CACHE_SIZE - res);
++
++      flush_dcache_page(page);
++      SetPageUptodate(page);
++      if (PageError(page)) 
++              ClearPageError(page);
++ out:
++      kunmap(page);
++      unlock_page(page);
++}
++
++static int externfs_readpage(struct file *file, struct page *page)
++{
++      struct inode *ino = page->mapping->host;
++      struct externfs_file_ops *ops = EXTERNFS_I(ino)->ops;
++      struct externfs_data *ed = ino->i_sb->u.generic_sbp;
++      char *buffer;
++      long long start;
++      int err = 0;
++
++      start = (long long) page->index << PAGE_CACHE_SHIFT;
++      buffer = kmap(page);
++
++      if(ops->map_file_page != NULL){
++              /* XXX What happens when PAGE_SIZE != PAGE_CACHE_SIZE? */
++              err = (*ops->map_file_page)(file_externfs_i(file), start, 
++                                          buffer, file->f_mode & FMODE_WRITE,
++                                          ed);
++              if(!err)
++                      err = PAGE_CACHE_SIZE;
++      }
++      else err = (*ops->read_file)(file_externfs_i(file), start, buffer,
++                                   PAGE_CACHE_SIZE, 0, 0, 
++                                   externfs_finish_readpage, page, ed);
++
++      if(err > 0)
++              err = 0;
++      return(err);
++}
++
++struct writepage_info {
++      struct semaphore sem;
++      int res;
++};
++
++static void externfs_finish_prepare(char *buffer, int res, void *arg)
++{
++      struct writepage_info *wp = arg;
++
++      wp->res = res;
++      up(&wp->sem);
++}
++
++int externfs_prepare_write(struct file *file, struct page *page, 
++                       unsigned int from, unsigned int to)
++{
++      struct address_space *mapping = page->mapping;
++      struct inode *inode = mapping->host;
++      struct externfs_file_ops *ops = EXTERNFS_I(inode)->ops;
++      struct externfs_data *ed = inode->i_sb->u.generic_sbp;
++      char *buffer;
++      long long start;
++      int err;
++      struct writepage_info wp;
++
++      if(Page_Uptodate(page))
++              return(0);
++
++      start = (long long) page->index << PAGE_CACHE_SHIFT;
++      buffer = kmap(page);
++
++      if(ops->map_file_page != NULL){
++              err = (*ops->map_file_page)(file_externfs_i(file), start, 
++                                          buffer, file->f_mode & FMODE_WRITE,
++                                          ed);
++              goto out;
++      }
++
++      init_MUTEX_LOCKED(&wp.sem);
++      err = (*ops->read_file)(file_externfs_i(file), start, buffer,
++                              PAGE_CACHE_SIZE, from, to, 
++                              externfs_finish_prepare, &wp, ed);
++      down(&wp.sem);
++      if(err < 0) 
++              goto out;
++
++      err = wp.res;
++      if(err < 0)
++              goto out;
++
++      if(from > 0)
++              memset(buffer, 0, from);
++      if(to < PAGE_CACHE_SIZE)
++              memset(buffer + to, 0, PAGE_CACHE_SIZE - to);
++
++      SetPageUptodate(page);
++      err = 0;
++ out:
++      kunmap(page);
++      return(err);
++}
++
++static int externfs_commit_write(struct file *file, struct page *page, 
++                             unsigned from, unsigned to)
++{
++      struct address_space *mapping = page->mapping;
++      struct inode *inode = mapping->host;
++      struct externfs_file_ops *ops = EXTERNFS_I(inode)->ops;
++      unsigned long long size;
++      long long start;
++      int err;
++
++      start = (long long) (page->index << PAGE_CACHE_SHIFT);
++
++      if(ops->map_file_page != NULL)
++              err = to - from;
++      else {
++              size = start + to;
++              if(size > inode->i_size){
++                      inode->i_size = size;
++                      mark_inode_dirty(inode);
++              }
++      }
++
++      set_page_dirty(page);
++      return(to - from);
++}
++
++static void externfs_removepage(struct page *page)
++{
++      physmem_remove_mapping(page_address(page));
++}
++
++static struct address_space_operations externfs_aops = {
++      .writepage      = externfs_writepage,
++      .readpage       = externfs_readpage,
++      .removepage     = externfs_removepage,
++/*    .set_page_dirty = __set_page_dirty_nobuffers, */
++      .prepare_write  = externfs_prepare_write,
++      .commit_write   = externfs_commit_write
++};
++
++static struct inode *get_inode(struct super_block *sb, struct dentry *dentry,
++                             int need_fh)
++{
++      struct inode *inode;
++      struct externfs_data *ed = sb->u.generic_sbp;
++      struct externfs_mount_ops *ops = ed->mount_ops;
++      char *name = NULL;
++      int type, err = -ENOMEM, rdev;
++
++      if(dentry){
++              name = dentry_name(dentry, 0);
++              if(name == NULL)
++                      goto out;
++              type = (*ed->file_ops->file_type)(name, &rdev, ed);
++      }
++      else type = OS_TYPE_DIR;
++
++      inode = new_inode(sb);
++      if(inode == NULL)
++              goto out_free;
++
++      insert_inode_hash(inode);
++
++      if(type == OS_TYPE_SYMLINK)
++              inode->i_op = &page_symlink_inode_operations;
++      else if(type == OS_TYPE_DIR)
++              inode->i_op = &externfs_dir_iops;
++      else inode->i_op = &externfs_iops;
++
++      if(type == OS_TYPE_DIR) inode->i_fop = &externfs_dir_fops;
++      else inode->i_fop = &externfs_file_fops;
++
++      if(type == OS_TYPE_SYMLINK) 
++              inode->i_mapping->a_ops = &externfs_link_aops;
++      else inode->i_mapping->a_ops = &externfs_aops;
++
++      switch (type) {
++      case OS_TYPE_CHARDEV:
++              init_special_inode(inode, S_IFCHR, rdev);
++              break;
++      case OS_TYPE_BLOCKDEV:
++              init_special_inode(inode, S_IFBLK, rdev);
++              break;
++      case OS_TYPE_FIFO:
++              init_special_inode(inode, S_IFIFO, 0);
++              break;
++      case OS_TYPE_SOCK:
++              init_special_inode(inode, S_IFSOCK, 0);
++              break;
++      case OS_TYPE_SYMLINK:
++              inode->i_mode = S_IFLNK | S_IRWXUGO;
++      }
++      
++      if(need_fh){
++              struct externfs_inode *ei;
++
++              err = -ENOMEM;
++              ei = (*ops->init_file)(ed);
++              if(ei == NULL)
++                      goto out_put;
++
++              *ei = ((struct externfs_inode) { .ops   = ed->file_ops });
++              inode->u.generic_ip = ei;
++
++              err = (*ed->file_ops->open_file)(ei, name, current->fsuid, 
++                                               current->fsgid, inode, ed);
++              if(err && ((err != -ENOENT) && (err != -EISDIR)))
++                      goto out_put;
++      }
++
++      return(inode);
++
++ out_put:
++      iput(inode);
++ out_free:
++      kfree(name);
++ out:
++      return(ERR_PTR(err));
++}
++
++int externfs_create(struct inode *dir, struct dentry *dentry, int mode)
++{
++      struct externfs_file_ops *ops = EXTERNFS_I(dir)->ops;
++      struct inode *inode;
++      struct externfs_data *ed = dir->i_sb->u.generic_sbp;
++      struct externfs_inode *ei;
++      char *name;
++      int err = -ENOMEM;
++
++      name = dentry_name(dentry, 0);
++      if(name == NULL)
++              goto out;
++
++      inode = get_inode(dir->i_sb, dentry, 0);
++      if(IS_ERR(inode)){
++              err = PTR_ERR(inode);
++              goto out_free;
++      }
++
++      ei = (*ed->mount_ops->init_file)(ed);
++      if(ei == NULL)
++              /* XXX need a free_file() */
++              goto out_put;
++
++      *ei = ((struct externfs_inode) { .ops   = ed->file_ops });
++      inode->u.generic_ip = ei;
++
++      err = (*ops->create_file)(ei, name, mode, current->fsuid, 
++                                current->fsuid, inode, ed);
++      if(err)
++              goto out_put;
++
++      err = read_name(inode, name);
++      if(err)
++              goto out_rm;
++
++      inode->i_nlink++;
++      d_instantiate(dentry, inode);
++ out_free:
++      kfree(name);
++ out:
++      return(err);
++
++ out_rm:
++      (*ops->unlink_file)(name, ed);
++ out_put:
++      inode->i_nlink = 0;
++      iput(inode);
++      goto out_free;
++}
++ 
++struct dentry *externfs_lookup(struct inode *ino, struct dentry *dentry)
++{
++      struct inode *inode;
++      char *name;
++      int err;
++
++      inode = get_inode(ino->i_sb, dentry, 1);
++      if(IS_ERR(inode)){
++              err = PTR_ERR(inode);
++              goto out;
++      }
++
++      err = -ENOMEM;
++      name = dentry_name(dentry, 0);
++      if(name == NULL)
++              goto out_put;
++
++      err = read_name(inode, name);
++      kfree(name);
++      if(err){
++              if(err != -ENOENT)
++                      goto out_put;
++
++              inode->i_nlink = 0;
++              iput(inode);
++              inode = NULL;
++      }
++      d_add(dentry, inode);
++      dentry->d_op = &externfs_dentry_ops;
++      return(NULL);
++
++ out_put:
++      inode->i_nlink = 0;
++      iput(inode);
++ out:
++      return(ERR_PTR(err));
++}
++
++static char *inode_dentry_name(struct inode *ino, struct dentry *dentry)
++{
++        char *file;
++      int len;
++
++      file = inode_name(ino, dentry->d_name.len + 1);
++      if(file == NULL) return(NULL);
++        strcat(file, "/");
++      len = strlen(file);
++        strncat(file, dentry->d_name.name, dentry->d_name.len);
++      file[len + dentry->d_name.len] = '\0';
++        return(file);
++}
++
++int externfs_link(struct dentry *to, struct inode *ino, struct dentry *from)
++{
++      struct externfs_file_ops *ops = EXTERNFS_I(ino)->ops;
++      struct externfs_data *ed = ino->i_sb->u.generic_sbp;
++        char *from_name, *to_name;
++        int err = -ENOMEM;
++
++        from_name = inode_dentry_name(ino, from); 
++        if(from_name == NULL) 
++              goto out;
++
++        to_name = dentry_name(to, 0);
++      if(to_name == NULL)
++              goto out_free_from;
++
++        err = (*ops->link_file)(to_name, from_name, current->fsuid, 
++                              current->fsgid, ed);
++      if(err)
++              goto out_free_to;
++
++      d_instantiate(from, to->d_inode);
++      to->d_inode->i_nlink++;
++      atomic_inc(&to->d_inode->i_count);
++
++ out_free_to:
++        kfree(to_name);
++ out_free_from:
++        kfree(from_name);
++ out:
++        return(err);
++}
++
++int externfs_unlink(struct inode *ino, struct dentry *dentry)
++{
++      struct inode *inode;
++      struct externfs_file_ops *ops = EXTERNFS_I(ino)->ops;
++      struct externfs_data *ed = ino->i_sb->u.generic_sbp;
++      char *file;
++      int err;
++
++      file = inode_dentry_name(ino, dentry);
++      if(file == NULL) 
++              return(-ENOMEM);
++
++      inode = dentry->d_inode;
++      if((inode->i_nlink == 1) && (ops->invisible != NULL))
++              (*ops->invisible)(EXTERNFS_I(inode));
++
++      err = (*ops->unlink_file)(file, ed);
++      kfree(file);
++
++      inode->i_nlink--;
++
++      return(err);
++}
++
++int externfs_symlink(struct inode *ino, struct dentry *dentry, const char *to)
++{
++      struct externfs_file_ops *ops = EXTERNFS_I(ino)->ops;
++      struct externfs_data *ed = ino->i_sb->u.generic_sbp;
++      struct inode *inode;
++      char *file;
++      int err;
++
++      file = inode_dentry_name(ino, dentry);
++      if(file == NULL) 
++              return(-ENOMEM);
++      err = (*ops->make_symlink)(file, to, current->fsuid, current->fsgid,
++                                 ed);
++      kfree(file);
++
++      inode = get_inode(ino->i_sb, dentry, 1);
++      if(IS_ERR(inode)){
++              err = PTR_ERR(inode);
++              goto out;
++      }
++
++      d_instantiate(dentry, inode);
++      inode->i_nlink++;
++      iput(inode);
++ out:
++      return(err);
++}
++
++int externfs_make_dir(struct inode *ino, struct dentry *dentry, int mode)
++{
++      struct externfs_file_ops *ops = EXTERNFS_I(ino)->ops;
++      struct externfs_data *ed = ino->i_sb->u.generic_sbp;
++      struct inode *inode;
++      char *file;
++      int err;
++
++      file = inode_dentry_name(ino, dentry);
++      if(file == NULL) 
++              return(-ENOMEM);
++      err = (*ops->make_dir)(file, mode, current->fsuid, current->fsgid, ed);
++
++      inode = get_inode(ino->i_sb, dentry, 1);
++      if(IS_ERR(inode)){
++              err = PTR_ERR(inode);
++              goto out_free;
++      }
++
++      err = read_name(inode, file);
++      kfree(file);
++      if(err)
++              goto out_put;
++
++      d_instantiate(dentry, inode);
++      inode->i_nlink = 2;
++      inode->i_mode = S_IFDIR | mode;
++      iput(inode);
++
++      ino->i_nlink++;
++ out:
++      return(err);
++ out_free:
++      kfree(file);
++ out_put:
++      inode->i_nlink = 0;
++      iput(inode);
++      goto out;       
++}
++
++int externfs_remove_dir(struct inode *ino, struct dentry *dentry)
++{
++      struct externfs_file_ops *ops = EXTERNFS_I(ino)->ops;
++      void *mount = ino->i_sb->u.generic_sbp;
++      char *file;
++      int err;
++
++      file = inode_dentry_name(ino, dentry);
++      if(file == NULL) 
++              return(-ENOMEM);
++      err = (*ops->remove_dir)(file, current->fsuid, current->fsgid, mount);
++      kfree(file);
++
++      dentry->d_inode->i_nlink = 0;
++      ino->i_nlink--;
++      return(err);
++}
++
++int externfs_make_node(struct inode *dir, struct dentry *dentry, int mode, 
++                   int dev)
++{
++      struct externfs_file_ops *ops = EXTERNFS_I(dir)->ops;
++      struct externfs_data *ed = dir->i_sb->u.generic_sbp;
++      struct inode *inode;
++      char *name;
++      int err = -ENOMEM;
++ 
++      name = dentry_name(dentry, 0);
++      if(name == NULL)
++              goto out;
++
++      inode = get_inode(dir->i_sb, dentry, 1);
++      if(IS_ERR(inode)){
++              err = PTR_ERR(inode);
++              goto out_free;
++      }
++
++      init_special_inode(inode, mode, dev);
++      err = (*ops->make_node)(name, mode & S_IRWXUGO, current->fsuid, 
++                              current->fsgid, mode & S_IFMT, major(dev), 
++                              minor(dev), ed);
++      if(err)
++              goto out_put;
++      
++      err = read_name(inode, name);
++      if(err)
++              goto out_rm;
++
++      d_instantiate(dentry, inode);
++ out_free:
++      kfree(name);
++ out:
++      return(err);
++
++ out_rm:
++      (*ops->unlink_file)(name, ed);
++ out_put:
++      inode->i_nlink = 0;
++      iput(inode);
++      goto out_free;
++}
++
++int externfs_rename(struct inode *from_ino, struct dentry *from,
++                struct inode *to_ino, struct dentry *to)
++{
++      struct externfs_file_ops *ops = EXTERNFS_I(from_ino)->ops;
++      struct externfs_data *ed = from_ino->i_sb->u.generic_sbp;
++      char *from_name, *to_name;
++      int err;
++
++      from_name = inode_dentry_name(from_ino, from);
++      if(from_name == NULL)
++              return(-ENOMEM);
++      to_name = inode_dentry_name(to_ino, to);
++      if(to_name == NULL){
++              kfree(from_name);
++              return(-ENOMEM);
++      }
++      err = (*ops->rename_file)(from_name, to_name, ed);
++      kfree(from_name);
++      kfree(to_name);
++
++      from_ino->i_nlink--;
++      to_ino->i_nlink++;
++      return(err);
++}
++
++void externfs_truncate(struct inode *ino)
++{
++      struct externfs_file_ops *ops = EXTERNFS_I(ino)->ops;
++      struct externfs_data *ed = ino->i_sb->u.generic_sbp;
++
++      (*ops->truncate_file)(EXTERNFS_I(ino), ino->i_size, ed);
++}
++
++int externfs_permission(struct inode *ino, int desired)
++{
++      struct externfs_file_ops *ops = EXTERNFS_I(ino)->ops;
++      struct externfs_data *ed = ino->i_sb->u.generic_sbp;
++      char *name;
++      int r = 0, w = 0, x = 0, err;
++
++      if(ops->access_file == NULL)
++              return(vfs_permission(ino, desired));
++
++      if(desired & MAY_READ) r = 1;
++      if(desired & MAY_WRITE) w = 1;
++      if(desired & MAY_EXEC) x = 1;
++      name = inode_name(ino, 0);
++      if(name == NULL) 
++              return(-ENOMEM);
++
++      err = (*ops->access_file)(name, r, w, x, current->fsuid, 
++                                current->fsgid, ed);
++      kfree(name);
++
++      if(!err) 
++              err = vfs_permission(ino, desired);
++      return(err);
++}
++
++int externfs_setattr(struct dentry *dentry, struct iattr *attr)
++{
++      struct externfs_file_ops *ops = EXTERNFS_I(dentry->d_inode)->ops;
++      struct externfs_data *ed = dentry->d_inode->i_sb->u.generic_sbp;
++      struct externfs_iattr attrs;
++      char *name;
++      int err;
++      
++      attrs.ia_valid = 0;
++      if(attr->ia_valid & ATTR_MODE){
++              attrs.ia_valid |= EXTERNFS_ATTR_MODE;
++              attrs.ia_mode = attr->ia_mode;
++      }
++      if(attr->ia_valid & ATTR_UID){
++              attrs.ia_valid |= EXTERNFS_ATTR_UID;
++              attrs.ia_uid = attr->ia_uid;
++      }
++      if(attr->ia_valid & ATTR_GID){
++              attrs.ia_valid |= EXTERNFS_ATTR_GID;
++              attrs.ia_gid = attr->ia_gid;
++      }
++      if(attr->ia_valid & ATTR_SIZE){
++              attrs.ia_valid |= EXTERNFS_ATTR_SIZE;
++              attrs.ia_size = attr->ia_size;
++      }
++      if(attr->ia_valid & ATTR_ATIME){
++              attrs.ia_valid |= EXTERNFS_ATTR_ATIME;
++              attrs.ia_atime = attr->ia_atime;
++      }
++      if(attr->ia_valid & ATTR_MTIME){
++              attrs.ia_valid |= EXTERNFS_ATTR_MTIME;
++              attrs.ia_mtime = attr->ia_mtime;
++      }
++      if(attr->ia_valid & ATTR_CTIME){
++              attrs.ia_valid |= EXTERNFS_ATTR_CTIME;
++              attrs.ia_ctime = attr->ia_ctime;
++      }
++      if(attr->ia_valid & ATTR_ATIME_SET){
++              attrs.ia_valid |= EXTERNFS_ATTR_ATIME_SET;
++      }
++      if(attr->ia_valid & ATTR_MTIME_SET){
++              attrs.ia_valid |= EXTERNFS_ATTR_MTIME_SET;
++      }
++      name = dentry_name(dentry, 0);
++      if(name == NULL) 
++              return(-ENOMEM);
++      err = (*ops->set_attr)(name, &attrs, ed);
++      kfree(name);
++      if(err)
++              return(err);
++
++      return(inode_setattr(dentry->d_inode, attr));
++}
++
++int externfs_getattr(struct dentry *dentry, struct iattr *attr)
++{
++      not_implemented();
++      return(-EINVAL);
++}
++
++static struct inode_operations externfs_iops = {
++      .create         = externfs_create,
++      .link           = externfs_link,
++      .unlink         = externfs_unlink,
++      .symlink        = externfs_symlink,
++      .mkdir          = externfs_make_dir,
++      .rmdir          = externfs_remove_dir,
++      .mknod          = externfs_make_node,
++      .rename         = externfs_rename,
++      .truncate       = externfs_truncate,
++      .permission     = externfs_permission,
++      .setattr        = externfs_setattr,
++      .getattr        = externfs_getattr,
++};
++
++static struct inode_operations externfs_dir_iops = {
++      .create         = externfs_create,
++      .lookup         = externfs_lookup,
++      .link           = externfs_link,
++      .unlink         = externfs_unlink,
++      .symlink        = externfs_symlink,
++      .mkdir          = externfs_make_dir,
++      .rmdir          = externfs_remove_dir,
++      .mknod          = externfs_make_node,
++      .rename         = externfs_rename,
++      .truncate       = externfs_truncate,
++      .permission     = externfs_permission,
++      .setattr        = externfs_setattr,
++      .getattr        = externfs_getattr,
++};
++
++int externfs_link_readpage(struct file *file, struct page *page)
++{
++      struct inode *ino = page->mapping->host;
++      struct externfs_file_ops *ops = EXTERNFS_I(ino)->ops;
++      struct externfs_data *ed = ino->i_sb->u.generic_sbp;
++      char *buffer, *name;
++      long long start;
++      int err;
++
++      start = page->index << PAGE_CACHE_SHIFT;
++      buffer = kmap(page);
++      name = inode_name(ino, 0);
++      if(name == NULL) 
++              return(-ENOMEM);
++
++      err = (*ops->read_link)(name, current->fsuid, current->fsgid, buffer, 
++                              PAGE_CACHE_SIZE, ed);
++
++      kfree(name);
++      if(err == PAGE_CACHE_SIZE)
++              err = -E2BIG;
++      else if(err > 0){
++              flush_dcache_page(page);
++              SetPageUptodate(page);
++              if (PageError(page)) ClearPageError(page);
++              err = 0;
++      }
++      kunmap(page);
++      UnlockPage(page);
++      return(err);
++}
++
++static int externfs_flushpage(struct page *page, unsigned long offset)
++{
++      return(externfs_writepage(page));
++}
++
++struct externfs_data *inode_externfs_info(struct inode *inode)
++{
++      return(inode->i_sb->u.generic_sbp);
++}
++
++static struct address_space_operations externfs_link_aops = {
++      .readpage       = externfs_link_readpage,
++      .removepage     = externfs_removepage,
++      .flushpage      = externfs_flushpage,
++};
++
++DECLARE_MUTEX(externfs_sem);
++struct list_head externfses = LIST_HEAD_INIT(externfses);
++
++static struct externfs *find_externfs(struct file_system_type *type)
++{
++      struct list_head *ele;
++      struct externfs *fs;
++
++      down(&externfs_sem);
++      list_for_each(ele, &externfses){
++              fs = list_entry(ele, struct externfs, list);
++              if(&fs->type == type)
++                      goto out;
++      }
++      fs = NULL;
++ out:
++      up(&externfs_sem);
++      return(fs);
++}
++
++#define DEFAULT_ROOT "/"
++
++char *host_root_filename(char *mount_arg)
++{
++      char *root = DEFAULT_ROOT;
++
++      if((mount_arg != NULL) && (*mount_arg != '\0'))
++              root = mount_arg;
++
++      return(uml_strdup(root));
++}
++
++struct super_block *externfs_read_super(struct super_block *sb, void *data, 
++                                      int silent)
++{
++      struct externfs *fs;
++      struct inode *root_inode;
++      struct externfs_data *sb_data;
++      int err = -EINVAL;
++
++      sb->s_blocksize = 1024;
++      sb->s_blocksize_bits = 10;
++      sb->s_magic = EXTERNFS_SUPER_MAGIC;
++      sb->s_op = &externfs_sbops;
++
++      fs = find_externfs(sb->s_type);
++      if(fs == NULL){
++              printk("Couldn't find externfs for filesystem '%s'\n",
++                     sb->s_type->name);
++              goto out;
++      }
++
++      sb_data = (*fs->mount_ops->mount)(data);
++      if(IS_ERR(sb_data)){
++              err = PTR_ERR(sb_data);
++              goto out;
++      }
++
++      sb->u.generic_sbp = sb_data;
++      sb_data->mount_ops = fs->mount_ops;
++
++      root_inode = get_inode(sb, NULL, 1);
++      if(IS_ERR(root_inode))
++              goto out;
++
++      sb->s_root = d_alloc_root(root_inode);
++      if(sb->s_root == NULL)
++              goto out_put;
++
++      if(read_inode(root_inode))
++              goto out_dput;
++      return(sb);
++
++ out_dput:
++      /* dput frees the inode */
++      dput(sb->s_root);
++      return(NULL);
++ out_put:
++      root_inode->i_nlink = 0;
++      make_bad_inode(root_inode);
++      iput(root_inode);
++ out:
++      return(NULL);
++}
++
++void init_externfs(struct externfs_data *ed, struct externfs_file_ops *ops)
++{
++      ed->file_ops = ops;
++}
++
++int register_externfs(char *name, struct externfs_mount_ops *mount_ops)
++{
++      struct externfs *new;
++      int err = -ENOMEM;
++
++      new = kmalloc(sizeof(*new), GFP_KERNEL);
++      if(new == NULL)
++              goto out;
++
++      memset(new, 0, sizeof(*new));
++      *new = ((struct externfs) { .list       = LIST_HEAD_INIT(new->list),
++                                  .mount_ops  = mount_ops,
++                                  .type = { .name     = name,
++                                            .read_super = externfs_read_super,
++                                            .fs_flags = 0,
++                                            .owner    = THIS_MODULE } });
++      list_add(&new->list, &externfses);
++
++      err = register_filesystem(&new->type);
++      if(err)
++              goto out_del;
++      return(0);
++
++ out_del:
++      list_del(&new->list);
++      kfree(new);
++ out:
++      return(err);
++}
++
++void unregister_externfs(char *name)
++{
++      struct list_head *ele;
++      struct externfs *fs;
++
++      down(&externfs_sem);
++      list_for_each(ele, &externfses){
++              fs = list_entry(ele, struct externfs, list);
++              if(!strcmp(fs->type.name, name)){
++                      list_del(ele);
++                      up(&externfs_sem);
++                      return;
++              }
++      }
++      up(&externfs_sem);
++      printk("Unregister_externfs - filesystem '%s' not found\n", name);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/fs/hostfs/host_file.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/fs/hostfs/host_file.c    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/fs/hostfs/host_file.c 2005-05-03 22:28:14.271440624 +0300
+@@ -0,0 +1,441 @@
++/* 
++ * Copyright (C) 2004 Jeff Dike (jdike@addtoit.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/stddef.h"
++#include "linux/string.h"
++#include "linux/errno.h"
++#include "linux/types.h"
++#include "linux/slab.h"
++#include "linux/blkdev.h"
++#include "asm/fcntl.h"
++#include "hostfs.h"
++
++extern int append;
++
++char *get_path(const char *path[], char *buf, int size)
++{
++      const char **s;
++      char *p;
++      int new = 1;
++
++      for(s = path; *s != NULL; s++){
++              new += strlen(*s);
++              if((*(s + 1) != NULL) && (strlen(*s) > 0) && 
++                 ((*s)[strlen(*s) - 1] != '/'))
++                      new++;
++      }
++
++      if(new > size){
++              buf = kmalloc(new, GFP_KERNEL);
++              if(buf == NULL)
++                      return(NULL);
++      }
++
++      p = buf;
++      for(s = path; *s != NULL; s++){
++              strcpy(p, *s);
++              p += strlen(*s);
++              if((*(s + 1) != NULL) && (strlen(*s) > 0) && 
++                 ((*s)[strlen(*s) - 1] != '/'))
++                      strcpy(p++, "/");
++      }
++              
++      return(buf);
++}
++
++void free_path(const char *buf, char *tmp)
++{
++      if((buf != tmp) && (buf != NULL))
++              kfree((char *) buf);
++}
++
++int host_open_file(const char *path[], int r, int w, struct file_handle *fh)
++{
++      char tmp[HOSTFS_BUFSIZE], *file;
++      int mode = 0, err;
++      struct openflags flags = OPENFLAGS();
++
++      if (r)
++              flags = of_read(flags);
++      if (w)
++              flags = of_write(flags);
++      if(append)
++              flags = of_append(flags);
++
++      err = -ENOMEM;
++      file = get_path(path, tmp, sizeof(tmp));
++      if(file == NULL)
++              goto out;
++      
++      err = open_filehandle(file, flags, mode, fh);
++ out:
++      free_path(file, tmp);
++      return(err);
++}
++
++void *host_open_dir(const char *path[])
++{
++      char tmp[HOSTFS_BUFSIZE], *file;
++      void *dir = ERR_PTR(-ENOMEM);
++
++      file = get_path(path, tmp, sizeof(tmp));
++      if(file == NULL)
++              goto out;
++      
++      dir = open_dir(file);
++ out:
++      free_path(file, tmp);
++      return(dir);
++}
++
++char *host_read_dir(void *stream, unsigned long long *pos, 
++                  unsigned long long *ino_out, int *len_out)
++{
++      int err;
++      char *name;
++
++      err = os_seek_dir(stream, *pos);
++      if(err)
++              return(ERR_PTR(err));
++
++      err = os_read_dir(stream, ino_out, &name);
++      if(err)
++              return(ERR_PTR(err));
++
++      if(name == NULL)
++              return(NULL);
++
++      *len_out = strlen(name);
++      *pos = os_tell_dir(stream);
++      return(name);
++}
++
++int host_file_type(const char *path[], int *rdev)
++{
++      char tmp[HOSTFS_BUFSIZE], *file;
++      struct uml_stat buf;
++      int ret;
++
++      ret = -ENOMEM;
++      file = get_path(path, tmp, sizeof(tmp));
++      if(file == NULL)
++              goto out;
++
++      if(rdev != NULL){
++              ret = os_lstat_file(file, &buf);
++              if(ret)
++                      goto out;
++              *rdev = MKDEV(buf.ust_rmajor, buf.ust_rminor);
++      }
++
++      ret = os_file_type(file);
++ out:
++      free_path(file, tmp);
++      return(ret);
++}
++
++int host_create_file(const char *path[], int mode, struct file_handle *fh)
++{
++      char tmp[HOSTFS_BUFSIZE], *file;
++      int err = -ENOMEM;
++
++      file = get_path(path, tmp, sizeof(tmp));
++      if(file == NULL)
++              goto out;
++
++      err = open_filehandle(file, of_create(of_rdwr(OPENFLAGS())), mode, fh);
++ out:
++      free_path(file, tmp);
++      return(err);
++}
++
++static int do_stat_file(const char *path, dev_t *dev_out, 
++                      unsigned long long *inode_out, int *mode_out, 
++                      int *nlink_out, int *uid_out, int *gid_out, 
++                      unsigned long long *size_out, unsigned long *atime_out,
++                      unsigned long *mtime_out, unsigned long *ctime_out,
++                      int *blksize_out, unsigned long long *blocks_out)
++{
++      struct uml_stat buf;
++      int err;
++
++      err = os_lstat_file(path, &buf);
++      if(err < 0)
++              return(err);
++
++      if(dev_out != NULL) *dev_out = MKDEV(buf.ust_major, buf.ust_minor);
++      if(inode_out != NULL) *inode_out = buf.ust_ino;
++      if(mode_out != NULL) *mode_out = buf.ust_mode;
++      if(nlink_out != NULL) *nlink_out = buf.ust_nlink;
++      if(uid_out != NULL) *uid_out = buf.ust_uid;
++      if(gid_out != NULL) *gid_out = buf.ust_gid;
++      if(size_out != NULL) *size_out = buf.ust_size;
++      if(atime_out != NULL) *atime_out = buf.ust_atime;
++      if(mtime_out != NULL) *mtime_out = buf.ust_mtime;
++      if(ctime_out != NULL) *ctime_out = buf.ust_ctime;
++      if(blksize_out != NULL) *blksize_out = buf.ust_blksize;
++      if(blocks_out != NULL) *blocks_out = buf.ust_blocks;
++
++      return(0);
++}
++
++int host_stat_file(const char *path[], dev_t *dev_out, 
++                 unsigned long long *inode_out, int *mode_out, 
++                 int *nlink_out, int *uid_out, int *gid_out, 
++                 unsigned long long *size_out, unsigned long *atime_out,
++                 unsigned long *mtime_out, unsigned long *ctime_out,
++                 int *blksize_out, unsigned long long *blocks_out)
++{
++      char tmp[HOSTFS_BUFSIZE], *file;
++      int err;
++
++      err = -ENOMEM;
++      file = get_path(path, tmp, sizeof(tmp));
++      if(file == NULL)
++              goto out;
++
++      err = do_stat_file(file, dev_out, inode_out, mode_out, nlink_out,
++                         uid_out, gid_out, size_out, atime_out, mtime_out,
++                         ctime_out, blksize_out, blocks_out);
++ out:
++      free_path(file, tmp);
++      return(err);
++}
++
++int host_set_attr(const char *path[], struct externfs_iattr *attrs)
++{
++      char tmp[HOSTFS_BUFSIZE], *file;
++      unsigned long time;
++      int err = 0, ma;
++
++      if(append && (attrs->ia_valid & EXTERNFS_ATTR_SIZE))
++              return(-EPERM);
++
++      err = -ENOMEM;
++      file = get_path(path, tmp, sizeof(tmp));
++      if(file == NULL)
++              goto out;
++
++      if(attrs->ia_valid & EXTERNFS_ATTR_MODE){
++              err = os_set_file_perms(file, attrs->ia_mode);
++              if(err < 0)
++                      goto out;
++      }
++      if(attrs->ia_valid & EXTERNFS_ATTR_UID){
++              err = os_set_file_owner(file, attrs->ia_uid, -1);
++              if(err < 0)
++                      goto out;
++      }
++      if(attrs->ia_valid & EXTERNFS_ATTR_GID){
++              err = os_set_file_owner(file, -1, attrs->ia_gid);
++              if(err < 0)
++                      goto out;
++      }
++      if(attrs->ia_valid & EXTERNFS_ATTR_SIZE){
++              err = os_truncate_file(file, attrs->ia_size);
++              if(err < 0)
++                      goto out;
++      }
++      ma = EXTERNFS_ATTR_ATIME_SET | EXTERNFS_ATTR_MTIME_SET;
++      if((attrs->ia_valid & ma) == ma){
++              err = os_set_file_time(file, attrs->ia_atime, attrs->ia_mtime);
++              if(err)
++                      goto out;
++      }
++      else {
++              if(attrs->ia_valid & EXTERNFS_ATTR_ATIME_SET){
++                      err = do_stat_file(file, NULL, NULL, NULL, NULL, NULL, 
++                                         NULL, NULL, NULL, &time, 
++                                         NULL, NULL, NULL);
++                      if(err != 0)
++                              goto out;
++
++                      err = os_set_file_time(file, attrs->ia_atime, time);
++                      if(err)
++                              goto out;
++              }
++              if(attrs->ia_valid & EXTERNFS_ATTR_MTIME_SET){
++                      err = do_stat_file(file, NULL, NULL, NULL, NULL, NULL, 
++                                         NULL, NULL, &time, NULL, 
++                                         NULL, NULL, NULL);
++                      if(err != 0)
++                              goto out;
++
++                      err = os_set_file_time(file, time, attrs->ia_mtime);
++                      if(err)
++                              goto out;
++              }
++      }
++      if(attrs->ia_valid & EXTERNFS_ATTR_CTIME) ;
++      if(attrs->ia_valid & (EXTERNFS_ATTR_ATIME | EXTERNFS_ATTR_MTIME)){
++              err = do_stat_file(file, NULL, NULL, NULL, NULL, NULL, 
++                                 NULL, NULL, &attrs->ia_atime, 
++                                 &attrs->ia_mtime, NULL, NULL, NULL);
++              if(err != 0)
++                      goto out;
++      }
++
++      err = 0;
++ out:
++      free_path(file, tmp);
++      return(err);
++}
++
++int host_make_symlink(const char *from[], const char *to)
++{
++      char tmp[HOSTFS_BUFSIZE], *file;
++      int err = -ENOMEM;
++
++      file = get_path(from, tmp, sizeof(tmp));
++      if(file == NULL)
++              goto out;
++      
++      err = os_make_symlink(to, file);
++ out:
++      free_path(file, tmp);
++      return(err);
++}
++
++int host_unlink_file(const char *path[])
++{
++      char tmp[HOSTFS_BUFSIZE], *file;
++      int err = -ENOMEM;
++
++      if(append)
++              return(-EPERM);
++
++      file = get_path(path, tmp, sizeof(tmp));
++      if(file == NULL)
++              goto out;
++
++      err = os_remove_file(file);
++ out:
++      free_path(file, tmp);
++      return(err);
++}
++
++int host_make_dir(const char *path[], int mode)
++{
++      char tmp[HOSTFS_BUFSIZE], *file;
++      int err = -ENOMEM;
++
++      file = get_path(path, tmp, sizeof(tmp));
++      if(file == NULL)
++              goto out;
++
++      err = os_make_dir(file, mode);
++ out:
++      free_path(file, tmp);
++      return(err);
++}
++
++int host_remove_dir(const char *path[])
++{
++      char tmp[HOSTFS_BUFSIZE], *file;
++      int err = -ENOMEM;
++
++      file = get_path(path, tmp, sizeof(tmp));
++      if(file == NULL)
++              goto out;
++
++      err = os_remove_dir(file);
++ out:
++      free_path(file, tmp);
++      return(err);
++}
++
++int host_link_file(const char *to[], const char *from[])
++{
++      char from_tmp[HOSTFS_BUFSIZE], *f, to_tmp[HOSTFS_BUFSIZE], *t;
++      int err = -ENOMEM;
++
++      f = get_path(from, from_tmp, sizeof(from_tmp));
++      t = get_path(to, to_tmp, sizeof(to_tmp));
++      if((f == NULL) || (t == NULL))
++              goto out;
++
++      err = os_link_file(t, f);
++ out:
++      free_path(f, from_tmp);
++      free_path(t, to_tmp);
++      return(err);
++}
++
++int host_read_link(const char *path[], char *buf, int size)
++{
++      char tmp[HOSTFS_BUFSIZE], *file;
++      int n = -ENOMEM;
++
++      file = get_path(path, tmp, sizeof(tmp));
++      if(file == NULL)
++              goto out;
++
++      n = os_read_symlink(file, buf, size);
++      if(n < size) 
++              buf[n] = '\0';
++ out:
++      free_path(file, tmp);
++      return(n);
++}
++
++int host_rename_file(const char *from[], const char *to[])
++{
++      char from_tmp[HOSTFS_BUFSIZE], *f, to_tmp[HOSTFS_BUFSIZE], *t;
++      int err = -ENOMEM;
++
++      f = get_path(from, from_tmp, sizeof(from_tmp));
++      t = get_path(to, to_tmp, sizeof(to_tmp));
++      if((f == NULL) || (t == NULL))
++              goto out;
++
++      err = os_move_file(f, t);
++ out:
++      free_path(f, from_tmp);
++      free_path(t, to_tmp);
++      return(err);
++}
++
++int host_stat_fs(const char *path[], long *bsize_out, long long *blocks_out, 
++               long long *bfree_out, long long *bavail_out, 
++               long long *files_out, long long *ffree_out, void *fsid_out, 
++               int fsid_size, long *namelen_out, long *spare_out)
++{
++      char tmp[HOSTFS_BUFSIZE], *file;
++      int err = -ENOMEM;
++
++      file = get_path(path, tmp, sizeof(tmp));
++      if(file == NULL)
++              goto out;
++
++      err = os_stat_filesystem(file, bsize_out, blocks_out, bfree_out, 
++                               bavail_out, files_out, ffree_out, fsid_out, 
++                               fsid_size, namelen_out, spare_out);
++ out:
++      free_path(file, tmp);
++      return(err);
++}
++
++char *generic_host_read_dir(void *stream, unsigned long long *pos, 
++                          unsigned long long *ino_out, int *len_out, 
++                          void *mount)
++{
++      return(host_read_dir(stream, pos, ino_out, len_out));
++}
++
++int generic_host_truncate_file(struct file_handle *fh, __u64 size, void *m)
++{
++      return(truncate_file(fh, size));
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/fs/hostfs/host_fs.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/fs/hostfs/host_fs.c      1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/fs/hostfs/host_fs.c   2005-05-03 22:28:14.273440320 +0300
+@@ -0,0 +1,465 @@
++/* 
++ * Copyright (C) 2000 - 2004 Jeff Dike (jdike@addtoit.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/stddef.h"
++#include "linux/string.h"
++#include "linux/types.h"
++#include "linux/errno.h"
++#include "linux/slab.h"
++#include "linux/init.h"
++#include "linux/fs.h"
++#include "linux/stat.h"
++#include "hostfs.h"
++#include "kern.h"
++#include "init.h"
++#include "kern_util.h"
++#include "filehandle.h"
++#include "os.h"
++
++/* Changed in hostfs_args before the kernel starts running */
++static char *jail_dir = "/";
++int append = 0;
++
++static int __init hostfs_args(char *options, int *add)
++{
++      char *ptr;
++
++      ptr = strchr(options, ',');
++      if(ptr != NULL)
++              *ptr++ = '\0';
++      if(*options != '\0')
++              jail_dir = options;
++
++      options = ptr;
++      while(options){
++              ptr = strchr(options, ',');
++              if(ptr != NULL)
++                      *ptr++ = '\0';
++              if(*options != '\0'){
++                      if(!strcmp(options, "append"))
++                              append = 1;
++                      else printf("hostfs_args - unsupported option - %s\n",
++                                  options);
++              }
++              options = ptr;
++      }
++      return(0);
++}
++
++__uml_setup("hostfs=", hostfs_args,
++"hostfs=<root dir>,<flags>,...\n"
++"    This is used to set hostfs parameters.  The root directory argument\n"
++"    is used to confine all hostfs mounts to within the specified directory\n"
++"    tree on the host.  If this isn't specified, then a user inside UML can\n"
++"    mount anything on the host that's accessible to the user that's running\n"
++"    it.\n"
++"    The only flag currently supported is 'append', which specifies that all\n"
++"    files opened by hostfs will be opened in append mode.\n\n"
++);
++
++struct hostfs_data {
++      struct externfs_data ext;
++      char *mount;
++};
++
++struct hostfs_file {
++      struct externfs_inode ext;
++      struct file_handle fh;
++};
++
++static int hostfs_access_file(char *file, int r, int w, int x, int uid, 
++                            int gid, struct externfs_data *ed)
++{
++      char *mount = container_of(ed, struct hostfs_data, ext)->mount;
++      const char *path[] = { jail_dir, mount, file, NULL };
++      char tmp[HOSTFS_BUFSIZE];
++      int err, mode = 0;
++
++      if(r) mode = OS_ACC_R_OK;
++      if(w) mode |= OS_ACC_W_OK;
++      if(x) mode |= OS_ACC_X_OK;
++      
++      err = -ENOMEM;
++      file = get_path(path, tmp, sizeof(tmp));
++      if(file == NULL)
++              goto out;
++
++      err = os_access(file, mode);
++      free_path(file, tmp);
++ out:
++      return(err);
++}
++
++static int hostfs_make_node(const char *file, int mode, int uid, int gid, 
++                          int type, int major, int minor, 
++                          struct externfs_data *ed)
++{
++      char *mount = container_of(ed, struct hostfs_data, ext)->mount;
++      const char *path[] = { jail_dir, mount, file, NULL };
++      char tmp[HOSTFS_BUFSIZE];
++      int err = -ENOMEM;
++
++      file = get_path(path, tmp, sizeof(tmp));
++      if(file == NULL)
++              goto out;
++
++      /* XXX Pass type in an OS-independent way */
++      mode |= type;
++
++      err = os_make_dev(file, mode, major, minor);
++      free_path(file, tmp);
++ out:
++      return(err);
++}
++
++static int hostfs_stat_file(const char *file, struct externfs_data *ed, 
++                          dev_t *dev_out, unsigned long long *inode_out, 
++                          int *mode_out, int *nlink_out, int *uid_out, 
++                          int *gid_out, unsigned long long *size_out, 
++                          unsigned long *atime_out, unsigned long *mtime_out,
++                          unsigned long *ctime_out, int *blksize_out, 
++                          unsigned long long *blocks_out)
++{
++      char *mount = container_of(ed, struct hostfs_data, ext)->mount;
++      const char *path[] = { jail_dir, mount, file, NULL };
++
++      /* XXX Why pretend everything is owned by root? */
++      *uid_out = 0;
++      *gid_out = 0;
++      return(host_stat_file(path, dev_out, inode_out, mode_out, nlink_out, 
++                            NULL, NULL, size_out, atime_out, mtime_out, 
++                            ctime_out, blksize_out, blocks_out));
++}
++
++static int hostfs_file_type(const char *file, int *rdev, 
++                           struct externfs_data *ed)
++{
++      char *mount = container_of(ed, struct hostfs_data, ext)->mount;
++      const char *path[] = { jail_dir, mount, file, NULL };
++
++      return(host_file_type(path, rdev));
++}
++
++static char *hostfs_name(struct inode *inode)
++{
++      struct externfs_data *ed = inode_externfs_info(inode);
++      char *mount = container_of(ed, struct hostfs_data, ext)->mount;
++
++      return(inode_name_prefix(inode, mount));        
++}
++
++static struct externfs_inode *hostfs_init_file(struct externfs_data *ed)
++{
++      struct hostfs_file *hf;
++
++      hf = kmalloc(sizeof(*hf), GFP_KERNEL);
++      if(hf == NULL)
++              return(NULL);
++
++      hf->fh.fd = -1;
++      return(&hf->ext);
++}
++
++static int hostfs_open_file(struct externfs_inode *ext, char *file, 
++                          int uid, int gid, struct inode *inode, 
++                          struct externfs_data *ed)
++{
++      struct hostfs_file *hf = container_of(ext, struct hostfs_file, ext);
++      char *mount = container_of(ed, struct hostfs_data, ext)->mount;
++      const char *path[] = { jail_dir, mount, file, NULL };
++      int err;
++
++      err = host_open_file(path, 1, 1, &hf->fh);
++      if(err == -EISDIR)
++              goto out;
++
++      if(err == -EACCES)
++              err = host_open_file(path, 1, 0, &hf->fh);
++
++      if(err)
++              goto out;
++
++      is_reclaimable(&hf->fh, hostfs_name, inode);
++ out:
++      return(err);
++}
++
++static void *hostfs_open_dir(char *file, int uid, int gid, 
++                           struct externfs_data *ed)
++{
++      char *mount = container_of(ed, struct hostfs_data, ext)->mount;
++      const char *path[] = { jail_dir, mount, file, NULL };
++
++      return(host_open_dir(path));
++}
++
++static void hostfs_close_dir(void *stream, struct externfs_data *ed)
++{
++      os_close_dir(stream);
++}
++
++static char *hostfs_read_dir(void *stream, unsigned long long *pos, 
++                           unsigned long long *ino_out, int *len_out, 
++                           struct externfs_data *ed)
++{
++      char *mount = container_of(ed, struct hostfs_data, ext)->mount;
++
++      return(generic_host_read_dir(stream, pos, ino_out, len_out, mount));
++}
++
++static int hostfs_read_file(struct externfs_inode *ext, 
++                          unsigned long long offset, char *buf, int len, 
++                          int ignore_start, int ignore_end,
++                          void (*completion)(char *, int, void *), void *arg,
++                          struct externfs_data *ed)
++{
++      struct hostfs_file *hf = container_of(ext, struct hostfs_file, ext);
++      int err = 0;
++
++      if(ignore_start != 0){
++              err = read_file(&hf->fh, offset, buf, ignore_start);
++              if(err < 0)
++                      goto out;
++      }
++
++      if(ignore_end != len)
++              err = read_file(&hf->fh, offset + ignore_end, buf + ignore_end,
++                              len - ignore_end);
++
++ out:
++
++      (*completion)(buf, err, arg);
++      if (err > 0)
++              err = 0;
++      return(err);
++}
++
++static int hostfs_write_file(struct externfs_inode *ext,
++                           unsigned long long offset, const char *buf, 
++                           int start, int len, 
++                           void (*completion)(char *, int, void *), 
++                           void *arg, struct externfs_data *ed)
++{
++      struct file_handle *fh;
++      int err;
++
++      fh = &container_of(ext, struct hostfs_file, ext)->fh;
++      err = write_file(fh, offset + start, buf + start, len);
++
++      (*completion)((char *) buf, err, arg);
++      if (err > 0)
++              err = 0;
++
++      return(err);
++}
++
++static int hostfs_create_file(struct externfs_inode *ext, char *file, int mode,
++                            int uid, int gid, struct inode *inode, 
++                            struct externfs_data *ed)
++{
++      struct hostfs_file *hf = container_of(ext, struct hostfs_file, 
++                                            ext);
++      char *mount = container_of(ed, struct hostfs_data, ext)->mount;
++      const char *path[] = { jail_dir, mount, file, NULL };
++      int err = -ENOMEM;
++
++      err = host_create_file(path, mode, &hf->fh);
++      if(err)
++              goto out;
++
++      is_reclaimable(&hf->fh, hostfs_name, inode);
++ out:
++      return(err);
++}
++
++static int hostfs_set_attr(const char *file, struct externfs_iattr *attrs, 
++                         struct externfs_data *ed)
++{
++      char *mount = container_of(ed, struct hostfs_data, ext)->mount;
++      const char *path[] = { jail_dir, mount, file, NULL };
++
++      return(host_set_attr(path, attrs));
++}
++
++static int hostfs_make_symlink(const char *from, const char *to, int uid, 
++                             int gid, struct externfs_data *ed)
++{
++      char *mount = container_of(ed, struct hostfs_data, ext)->mount;
++      const char *path[] = { jail_dir, mount, from, NULL };
++
++      return(host_make_symlink(path, to));
++}
++
++static int hostfs_link_file(const char *to, const char *from, int uid, int gid,
++                          struct externfs_data *ed)
++{
++      char *mount = container_of(ed, struct hostfs_data, ext)->mount;
++      const char *to_path[] = { jail_dir, mount, to, NULL };
++      const char *from_path[] = { jail_dir, mount, from, NULL };
++
++      return(host_link_file(to_path, from_path));
++}
++
++static int hostfs_unlink_file(const char *file, struct externfs_data *ed)
++{
++      char *mount = container_of(ed, struct hostfs_data, ext)->mount;
++      const char *path[] = { jail_dir, mount, file, NULL };
++
++      return(host_unlink_file(path));
++}
++
++static int hostfs_make_dir(const char *file, int mode, int uid, int gid, 
++                          struct externfs_data *ed)
++{
++      char *mount = container_of(ed, struct hostfs_data, ext)->mount;
++      const char *path[] = { jail_dir, mount, file, NULL };
++
++      return(host_make_dir(path, mode));
++}
++
++static int hostfs_remove_dir(const char *file, int uid, int gid, 
++                           struct externfs_data *ed)
++{
++      char *mount = container_of(ed, struct hostfs_data, ext)->mount;
++      const char *path[] = { jail_dir, mount, file, NULL };
++
++      return(host_remove_dir(path));
++}
++
++static int hostfs_read_link(char *file, int uid, int gid, char *buf, int size, 
++                          struct externfs_data *ed)
++{
++      char *mount = container_of(ed, struct hostfs_data, ext)->mount;
++      const char *path[] = { jail_dir, mount, file, NULL };
++
++      return(host_read_link(path, buf, size));
++}
++
++static int hostfs_rename_file(char *from, char *to, struct externfs_data *ed)
++{
++      char *mount = container_of(ed, struct hostfs_data, ext)->mount;
++      const char *to_path[] = { jail_dir, mount, to, NULL };
++      const char *from_path[] = { jail_dir, mount, from, NULL };
++
++      return(host_rename_file(from_path, to_path));
++}
++
++static int hostfs_stat_fs(long *bsize_out, long long *blocks_out, 
++                        long long *bfree_out, long long *bavail_out, 
++                        long long *files_out, long long *ffree_out,
++                        void *fsid_out, int fsid_size, long *namelen_out, 
++                        long *spare_out, struct externfs_data *ed)
++{
++      char *mount = container_of(ed, struct hostfs_data, ext)->mount;
++      const char *path[] = { jail_dir, mount, NULL };
++
++      return(host_stat_fs(path, bsize_out, blocks_out, bfree_out, bavail_out,
++                          files_out, ffree_out, fsid_out, fsid_size, 
++                          namelen_out, spare_out));
++}
++
++static void hostfs_close_file(struct externfs_inode *ext,
++                            unsigned long long size)
++{
++      struct hostfs_file *hf = container_of(ext, struct hostfs_file, ext);
++
++      if(hf->fh.fd == -1)
++              return;
++
++      truncate_file(&hf->fh, size);
++      close_file(&hf->fh);
++}
++
++static int hostfs_truncate_file(struct externfs_inode *ext, __u64 size, 
++                              struct externfs_data *ed)
++{
++      struct hostfs_file *hf = container_of(ext, struct hostfs_file, ext);
++
++      return(truncate_file(&hf->fh, size));
++}
++
++static struct externfs_file_ops hostfs_file_ops = {
++      .stat_file              = hostfs_stat_file,
++      .file_type              = hostfs_file_type,
++      .access_file            = hostfs_access_file,
++      .open_file              = hostfs_open_file,
++      .open_dir               = hostfs_open_dir,
++      .read_dir               = hostfs_read_dir,
++      .read_file              = hostfs_read_file,
++      .write_file             = hostfs_write_file,
++      .map_file_page          = NULL,
++      .close_file             = hostfs_close_file,
++      .close_dir              = hostfs_close_dir,
++      .invisible              = NULL,
++      .create_file            = hostfs_create_file,
++      .set_attr               = hostfs_set_attr,
++      .make_symlink           = hostfs_make_symlink,
++      .unlink_file            = hostfs_unlink_file,
++      .make_dir               = hostfs_make_dir,
++      .remove_dir             = hostfs_remove_dir,
++      .make_node              = hostfs_make_node,
++      .link_file              = hostfs_link_file,
++      .read_link              = hostfs_read_link,
++      .rename_file            = hostfs_rename_file,
++      .statfs                 = hostfs_stat_fs,
++      .truncate_file          = hostfs_truncate_file
++};
++
++static struct externfs_data *mount_fs(char *mount_arg)
++{
++      struct hostfs_data *hd;
++      int err = -ENOMEM;
++
++      hd = kmalloc(sizeof(*hd), GFP_KERNEL);
++      if(hd == NULL)
++              goto out;
++
++      hd->mount = host_root_filename(mount_arg);
++      if(hd->mount == NULL)
++              goto out_free;
++
++      init_externfs(&hd->ext, &hostfs_file_ops);
++
++      return(&hd->ext);
++ out_free:
++      kfree(hd);
++ out:
++      return(ERR_PTR(err));
++}
++
++static struct externfs_mount_ops hostfs_mount_ops = {
++      .init_file              = hostfs_init_file,
++      .mount                  = mount_fs,
++};
++
++static int __init init_hostfs(void)
++{
++      return(register_externfs("hostfs", &hostfs_mount_ops));
++}
++
++static void __exit exit_hostfs(void)
++{
++      unregister_externfs("hostfs");
++}
++
++__initcall(init_hostfs);
++__exitcall(exit_hostfs);
++
++#if 0
++module_init(init_hostfs)
++module_exit(exit_hostfs)
++MODULE_LICENSE("GPL");
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/fs/hostfs/hostfs.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/fs/hostfs/hostfs.h       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/fs/hostfs/hostfs.h    2005-05-03 23:46:13.801043992 +0300
+@@ -0,0 +1,200 @@
++/* 
++ * Copyright (C) 2004 Jeff Dike (jdike@addtoit.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __UM_FS_HOSTFS
++#define __UM_FS_HOSTFS
++
++#include "linux/fs.h"
++#include "linux/blkdev.h"
++#include "filehandle.h"
++#include "os.h"
++
++/* These are exactly the same definitions as in fs.h, but the names are 
++ * changed so that this file can be included in both kernel and user files.
++ */
++
++#define EXTERNFS_ATTR_MODE    1
++#define EXTERNFS_ATTR_UID     2
++#define EXTERNFS_ATTR_GID     4
++#define EXTERNFS_ATTR_SIZE    8
++#define EXTERNFS_ATTR_ATIME   16
++#define EXTERNFS_ATTR_MTIME   32
++#define EXTERNFS_ATTR_CTIME   64
++#define EXTERNFS_ATTR_ATIME_SET       128
++#define EXTERNFS_ATTR_MTIME_SET       256
++#define EXTERNFS_ATTR_FORCE   512     /* Not a change, but a change it */
++#define EXTERNFS_ATTR_ATTR_FLAG       1024
++
++/**
++ * container_of - cast a member of a structure out to the containing structure
++ *
++ * @ptr:        the pointer to the member.
++ * @type:       the type of the container struct this is embedded in.
++ * @member:     the name of the member within the struct.
++ *
++ */
++#define container_of(ptr, type, member) ({                      \
++        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
++        (type *)( (char *)__mptr - offsetof(type,member) );})
++
++struct externfs_iattr {
++      unsigned int    ia_valid;
++      mode_t          ia_mode;
++      uid_t           ia_uid;
++      gid_t           ia_gid;
++      loff_t          ia_size;
++      time_t          ia_atime;
++      time_t          ia_mtime;
++      time_t          ia_ctime;
++      unsigned int    ia_attr_flags;
++};
++
++struct externfs_data {
++      struct externfs_file_ops *file_ops;
++      struct externfs_mount_ops *mount_ops;
++};
++
++struct externfs_inode {
++      struct externfs_file_ops *ops;
++};
++
++struct externfs_mount_ops {
++      struct externfs_data *(*mount)(char *mount_arg);
++      struct externfs_inode *(*init_file)(struct externfs_data *ed);
++};
++
++struct externfs_file_ops {
++      int (*stat_file)(const char *path, struct externfs_data *ed, 
++                       dev_t *dev_out, unsigned long long *inode_out, 
++                       int *mode_out, int *nlink_out, int *uid_out, 
++                       int *gid_out, unsigned long long *size_out, 
++                       unsigned long *atime_out, unsigned long *mtime_out,
++                       unsigned long *ctime_out, int *blksize_out, 
++                       unsigned long long *blocks_out);
++      int (*file_type)(const char *path, int *rdev, 
++                       struct externfs_data *ed);
++      int (*access_file)(char *path, int r, int w, int x, int uid, int gid, 
++                         struct externfs_data *ed);
++      int (*open_file)(struct externfs_inode *ext, char *file, 
++                       int uid, int gid, struct inode *inode, 
++                       struct externfs_data *ed);
++      void (*close_file)(struct externfs_inode *ext, 
++                         unsigned long long size);
++      void *(*open_dir)(char *path, int uid, int gid, 
++                        struct externfs_data *ed);
++      char *(*read_dir)(void *stream, unsigned long long *pos, 
++                        unsigned long long *ino_out, int *len_out, 
++                        struct externfs_data *ed);
++      int (*read_file)(struct externfs_inode *ext, 
++                       unsigned long long offset, char *buf, int len, 
++                       int ignore_start, int ignore_end,
++                       void (*completion)(char *, int, void *), void *arg, 
++                       struct externfs_data *ed);
++      int (*write_file)(struct externfs_inode *ext, 
++                        unsigned long long offset, const char *buf, 
++                        int start, int len, 
++                        void (*completion)(char *, int, void *), void *arg, 
++                        struct externfs_data *ed);
++      int (*map_file_page)(struct externfs_inode *ext, 
++                           unsigned long long offset, char *buf, int w, 
++                           struct externfs_data *ed);
++      void (*close_dir)(void *stream, struct externfs_data *ed);
++      void (*invisible)(struct externfs_inode *ext);
++      int (*create_file)(struct externfs_inode *ext, char *path, 
++                         int mode, int uid, int gid, struct inode *inode, 
++                         struct externfs_data *ed);
++      int (*set_attr)(const char *path, struct externfs_iattr *attrs, 
++                      struct externfs_data *ed);
++      int (*make_symlink)(const char *from, const char *to, int uid, int gid,
++                          struct externfs_data *ed);
++      int (*unlink_file)(const char *path, struct externfs_data *ed);
++      int (*make_dir)(const char *path, int mode, int uid, int gid, 
++                      struct externfs_data *ed);
++      int (*remove_dir)(const char *path, int uid, int gid, 
++                        struct externfs_data *ed);
++      int (*make_node)(const char *path, int mode, int uid, int gid, 
++                       int type, int maj, int min, struct externfs_data *ed);
++      int (*link_file)(const char *to, const char *from, int uid, int gid, 
++                       struct externfs_data *ed);
++      int (*read_link)(char *path, int uid, int gid, char *buf, int size, 
++                       struct externfs_data *ed);
++      int (*rename_file)(char *from, char *to, struct externfs_data *ed);
++      int (*statfs)(long *bsize_out, long long *blocks_out, 
++                    long long *bfree_out, long long *bavail_out, 
++                    long long *files_out, long long *ffree_out,
++                    void *fsid_out, int fsid_size, long *namelen_out, 
++                    long *spare_out, struct externfs_data *ed);
++      int (*truncate_file)(struct externfs_inode *ext, __u64 size, 
++                           struct externfs_data *ed);
++};
++
++#define HOSTFS_BUFSIZE 64
++
++extern int register_externfs(char *name, struct externfs_mount_ops *mount_ops);
++extern void unregister_externfs(char *name);
++extern void init_externfs(struct externfs_data *ed, 
++                        struct externfs_file_ops *ops);
++struct externfs_data *inode_externfs_info(struct inode *inode);
++
++extern char *generic_root_filename(char *mount_arg);
++extern void host_close_file(void *stream);
++extern int host_read_file(int fd, unsigned long long offset, char *buf, 
++                        int len);
++extern int host_open_file(const char *path[], int r, int w,
++                        struct file_handle *fh);
++extern void *host_open_dir(const char *path[]);
++extern char *host_read_dir(void *stream, unsigned long long *pos, 
++                         unsigned long long *ino_out, int *len_out);
++extern int host_file_type(const char *path[], int *rdev);
++extern char *host_root_filename(char *mount_arg);
++extern char *get_path(const char *path[], char *buf, int size);
++extern void free_path(const char *buf, char *tmp);
++extern int host_create_file(const char *path[], int mode, 
++                          struct file_handle *fh);
++extern int host_set_attr(const char *path[], struct externfs_iattr *attrs);
++extern int host_make_symlink(const char *from[], const char *to);
++extern int host_unlink_file(const char *path[]);
++extern int host_make_dir(const char *path[], int mode);
++extern int host_remove_dir(const char *path[]);
++extern int host_link_file(const char *to[], const char *from[]);
++extern int host_read_link(const char *path[], char *buf, int size);
++extern int host_rename_file(const char *from[], const char *to[]);
++extern int host_stat_fs(const char *path[], long *bsize_out, 
++                      long long *blocks_out, long long *bfree_out, 
++                      long long *bavail_out, long long *files_out, 
++                      long long *ffree_out, void *fsid_out, int fsid_size, 
++                      long *namelen_out, long *spare_out);
++extern int host_stat_file(const char *path[], dev_t *dev_out, 
++                        unsigned long long *inode_out, int *mode_out, 
++                        int *nlink_out, int *uid_out, int *gid_out, 
++                        unsigned long long *size_out, 
++                        unsigned long *atime_out, unsigned long *mtime_out,
++                        unsigned long *ctime_out, int *blksize_out,
++                        unsigned long long *blocks_out);
++
++extern char *generic_host_read_dir(void *stream, unsigned long long *pos, 
++                            unsigned long long *ino_out, int *len_out, 
++                            void *mount);
++extern int generic_host_read_file(int fd, unsigned long long offset, char *buf,
++                           int len, void *mount);
++extern void generic_host_close_file(void *stream, unsigned long long size,
++                                  void *mount);
++extern int generic_host_truncate_file(struct file_handle *fh, __u64 size, 
++                                    void *m);
++
++extern char *inode_name_prefix(struct inode *inode, char *prefix);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/fs/hostfs/humfs.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/fs/hostfs/humfs.c        1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/fs/hostfs/humfs.c     2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,1024 @@
++/* 
++ * Copyright (C) 2004 Jeff Dike (jdike@addtoit.com)
++ * Licensed under the GPL
++ */
++
++#include <linux/kernel.h>
++#include <linux/list.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/stat.h>
++#include <linux/tqueue.h>
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/string.h>
++#include <linux/kdev_t.h>
++#include <asm/irq.h>
++#include "hostfs.h"
++#include "mem.h"
++#include "os.h"
++#include "mode.h"
++#include "aio.h"
++#include "irq_user.h"
++#include "irq_kern.h"
++#include "filehandle.h"
++#include "metadata.h"
++
++#define HUMFS_VERSION 2
++
++static int humfs_stat_file(const char *path, struct externfs_data *ed, 
++                         dev_t *dev_out, unsigned long long *inode_out, 
++                         int *mode_out, int *nlink_out, int *uid_out, 
++                         int *gid_out, unsigned long long *size_out, 
++                         unsigned long *atime_out, unsigned long *mtime_out, 
++                         unsigned long *ctime_out, int *blksize_out, 
++                         unsigned long long *blocks_out)
++{
++      struct humfs *mount = container_of(ed, struct humfs, ext);
++      const char *data_path[3] = { mount->data, path, NULL };
++      int err, mode, perms, major, minor;
++      char type;
++
++      err = host_stat_file(data_path, dev_out, inode_out, mode_out, 
++                           nlink_out, NULL, NULL, size_out, atime_out, 
++                           mtime_out, ctime_out, blksize_out, blocks_out);
++      if(err)
++              return(err);
++
++      err = (*mount->meta->ownerships)(path, &perms, uid_out, gid_out, 
++                                       &type, &major, &minor, mount);
++      if(err)
++              return(err);
++
++      *mode_out = (*mode_out & ~S_IRWXUGO) | perms;
++
++      mode = 0;
++      switch(type){
++      case 'c':
++              mode = S_IFCHR;
++              break;
++      case 'b':
++              mode = S_IFBLK;
++              break;
++      case 's':
++              mode = S_IFSOCK;
++              break;
++      default:
++              break;
++      }
++
++      if(mode != 0)
++              *mode_out = (*mode_out & ~S_IFMT) | mode;
++
++      return(0);
++}
++
++static int meta_type(const char *path, int *dev_out, void *m)
++{
++      struct humfs *mount = m;
++      int err, type, maj, min;
++      char c;
++
++      err = (*mount->meta->ownerships)(path, NULL, NULL, NULL, &c, &maj, 
++                                       &min, mount);
++      if(err)
++              return(err);
++
++      if(c == 0)
++              return(0);
++
++      if(dev_out)
++              *dev_out = MKDEV(maj, min);
++
++      switch(c){
++      case 'c':
++              type = OS_TYPE_CHARDEV;
++              break;
++      case 'b':
++              type = OS_TYPE_BLOCKDEV;
++              break;
++      case 'p':
++              type = OS_TYPE_FIFO;
++              break;
++      case 's':
++              type = OS_TYPE_SOCK;
++              break;
++      default:
++              type = -EINVAL;
++              break;
++      }
++
++      return(type);
++}
++
++static int humfs_file_type(const char *path, int *dev_out, 
++                         struct externfs_data *ed)
++{
++      struct humfs *mount = container_of(ed, struct humfs, ext);
++      const char *data_path[3] = { mount->data, path, NULL };
++      int type;
++
++      type = meta_type(path, dev_out, mount);
++      if(type != 0)
++              return(type);
++
++      return(host_file_type(data_path, dev_out));
++}
++
++static char *humfs_data_name(struct inode *inode)
++{
++      struct externfs_data *ed = inode_externfs_info(inode);
++      struct humfs *mount = container_of(ed, struct humfs, ext);
++
++      return(inode_name_prefix(inode, mount->data));
++}
++
++static struct externfs_inode *humfs_init_file(struct externfs_data *ed)
++{
++      struct humfs *mount = container_of(ed, struct humfs, ext);
++      struct humfs_file *hf;
++
++      hf = (*mount->meta->init_file)();
++      if(IS_ERR(hf))
++              return((struct externfs_inode *) hf);
++
++      hf->data.fd = -1;
++      return(&hf->ext);
++}
++
++static int humfs_open_file(struct externfs_inode *ext, char *path, int uid, 
++                         int gid, struct inode *inode, 
++                         struct externfs_data *ed)
++{
++      struct humfs *mount = container_of(ed, struct humfs, ext);
++      struct humfs_file *hf = container_of(ext, struct humfs_file, ext);
++      const char *data_path[3] = { mount->data, path, NULL };
++      struct openflags flags;
++      char tmp[HOSTFS_BUFSIZE], *file;
++      int err = -ENOMEM;
++
++      file = get_path(data_path, tmp, sizeof(tmp));
++      if(file == NULL)
++              goto out;
++
++      flags = of_rdwr(OPENFLAGS());
++      if(mount->direct)
++              flags = of_direct(flags);
++
++      if(path == NULL)
++              path = "";
++      err = (*mount->meta->open_file)(hf, path, inode, mount);
++      if(err)
++              goto out_free;
++
++      err = open_filehandle(file, flags, 0, &hf->data);
++      if(err == -EISDIR)
++              goto out;
++      else if(err == -EPERM){
++              flags = of_set_rw(flags, 1, 0);
++              err = open_filehandle(file, flags, 0, &hf->data);
++      }
++      
++      if(err)
++              goto out_close;
++
++      hf->mount = mount;
++      is_reclaimable(&hf->data, humfs_data_name, inode);
++
++ out_free:
++      free_path(file, tmp);
++ out: 
++      return(err);
++
++ out_close:
++      (*mount->meta->close_file)(hf);
++      goto out_free;
++}
++
++static void *humfs_open_dir(char *path, int uid, int gid, 
++                          struct externfs_data *ed)
++{
++      struct humfs *mount = container_of(ed, struct humfs, ext);
++      const char *data_path[3] = { mount->data, path, NULL };
++
++      return(host_open_dir(data_path));
++}
++
++static void humfs_close_dir(void *stream, struct externfs_data *ed)
++{
++      os_close_dir(stream);
++}
++
++static char *humfs_read_dir(void *stream, unsigned long long *pos, 
++                          unsigned long long *ino_out, int *len_out, 
++                          struct externfs_data *ed)
++{
++      struct humfs *mount = container_of(ed, struct humfs, ext);
++
++      return(generic_host_read_dir(stream, pos, ino_out, len_out, mount));
++}
++
++LIST_HEAD(humfs_replies);
++
++struct humfs_aio {
++      struct aio_context aio;
++      struct list_head list;
++      void (*completion)(char *, int, void *);
++      char *buf;
++      int real_len;
++      int err;
++      void *data;
++};
++
++static int humfs_reply_fd = -1;
++
++struct humfs_aio last_task_aio, last_intr_aio;
++struct humfs_aio *last_task_aio_ptr, *last_intr_aio_ptr;
++
++void humfs_task_proc(void *unused)
++{
++      struct humfs_aio *aio;
++      unsigned long flags;
++
++      while(!list_empty(&humfs_replies)){
++              local_irq_save(flags);
++              aio = list_entry(humfs_replies.next, struct humfs_aio, list);
++
++              last_task_aio = *aio;
++              last_task_aio_ptr = aio;
++
++              list_del(&aio->list);
++              local_irq_restore(flags);
++
++              if(aio->err >= 0)
++                      aio->err = aio->real_len;
++              (*aio->completion)(aio->buf, aio->err, aio->data);
++              kfree(aio);
++      }
++}
++
++struct tq_struct humfs_task = {
++      .routine        = humfs_task_proc,
++      .data           = NULL
++};
++
++static void humfs_interrupt(int irq, void *dev_id, struct pt_regs *unused)
++{
++      struct aio_thread_reply reply;
++      struct humfs_aio *aio;
++      int err, fd = (int) dev_id;
++
++      while(1){
++              err = os_read_file(fd, &reply, sizeof(reply));
++              if(err < 0){
++                      if(err == -EAGAIN)
++                              break;
++                      printk("humfs_interrupt - read returned err %d\n", 
++                             -err);
++                      return;
++              }
++              aio = reply.data;
++              aio->err = reply.err;
++              list_add(&aio->list, &humfs_replies);
++              last_intr_aio = *aio;
++              last_intr_aio_ptr = aio;
++      }
++
++      if(!list_empty(&humfs_replies))
++              schedule_task(&humfs_task);
++      reactivate_fd(fd, HUMFS_IRQ);
++}
++
++static int init_humfs_aio(void)
++{
++      int fds[2], err;
++
++      err = os_pipe(fds, 1, 1);
++      if(err){
++              printk("init_humfs_aio - pipe failed, err = %d\n", -err);
++              goto out;
++      }
++
++      err = um_request_irq(HUMFS_IRQ, fds[0], IRQ_READ, humfs_interrupt,
++                           SA_INTERRUPT | SA_SAMPLE_RANDOM, "humfs", 
++                           (void *) fds[0]);
++      if(err){
++              printk("init_humfs_aio - : um_request_irq failed, err = %d\n",
++                     err);
++              goto out_close;
++      }
++
++      humfs_reply_fd = fds[1];
++      goto out;
++      
++ out_close:
++      os_close_file(fds[0]);
++      os_close_file(fds[1]);
++ out:
++      return(0);
++}
++
++__initcall(init_humfs_aio);
++
++static int humfs_aio(enum aio_type type, int fd, unsigned long long offset,
++                   char *buf, int len, int real_len,
++                   void (*completion)(char *, int, void *), void *arg)
++{
++      struct humfs_aio *aio;
++      int err = -ENOMEM;
++
++      aio = kmalloc(sizeof(*aio), GFP_KERNEL);
++      if(aio == NULL)
++              goto out;
++      *aio = ((struct humfs_aio) { .aio       = INIT_AIO_CONTEXT,
++                                   .list      = LIST_HEAD_INIT(aio->list),
++                                   .completion= completion,
++                                   .buf       = buf,
++                                   .err       = 0,
++                                   .real_len  = real_len,
++                                   .data      = arg });
++
++      err = submit_aio(type, fd, buf, len, offset, humfs_reply_fd, aio);
++      if(err)
++              (*completion)(buf, err, arg);
++
++ out:
++      return(err);
++}
++
++static int humfs_read_file(struct externfs_inode *ext,
++                         unsigned long long offset, char *buf, int len, 
++                         int ignore_start, int ignore_end,
++                         void (*completion)(char *, int, void *), void *arg, 
++                         struct externfs_data *ed)
++{
++      struct humfs_file *hf = container_of(ext, struct humfs_file, ext);
++      int fd = filehandle_fd(&hf->data);
++
++      if(fd < 0){
++              (*completion)(buf, fd, arg);
++              return(fd);
++      }
++
++      return(humfs_aio(AIO_READ, fd, offset, buf, len, len, completion, 
++                       arg));
++}
++
++static int humfs_write_file(struct externfs_inode *ext,
++                          unsigned long long offset, const char *buf, 
++                          int start, int len, 
++                          void (*completion)(char *, int, void *), void *arg,
++                          struct externfs_data *ed)
++{
++      struct humfs *mount = container_of(ed, struct humfs, ext);
++      struct humfs_file *hf = container_of(ext, struct humfs_file, ext);
++      int err, orig_len = len, fd = filehandle_fd(&hf->data);
++
++      if(fd < 0){
++              (*completion)((char *) buf, fd, arg);
++              return(fd);
++      }
++
++      if(mount->direct)
++              len = PAGE_SIZE;
++      else {
++              offset += start;
++              buf += start;
++      }
++
++      err = humfs_aio(AIO_WRITE, fd, offset, (char *) buf, len, orig_len, 
++                      completion, arg);
++
++      if(err < 0)
++              return(err);
++
++      if(mount->direct)
++              err = orig_len;
++
++      return(err);
++}
++
++static int humfs_map_file_page(struct externfs_inode *ext, 
++                             unsigned long long offset, char *buf, int w, 
++                             struct externfs_data *ed)
++{
++      struct humfs_file *hf = container_of(ext, struct humfs_file, ext);
++      unsigned long long size, need;
++      int err, fd = filehandle_fd(&hf->data);
++
++      if(fd < 0)
++              return(fd);
++
++      err = os_fd_size(fd, &size);
++      if(err)
++              return(err);
++
++      need = offset + PAGE_SIZE;
++      if(size < need){
++              err = os_truncate_fd(fd, need);
++              if(err)
++                      return(err);
++      }
++      
++      return(physmem_subst_mapping(buf, fd, offset, w));
++}
++
++static void humfs_close_file(struct externfs_inode *ext,
++                           unsigned long long size)
++{
++      struct humfs_file *hf = container_of(ext, struct humfs_file, ext);
++      int fd;
++
++      if(hf->data.fd == -1)
++              return;
++
++      fd = filehandle_fd(&hf->data);
++      physmem_forget_descriptor(fd);
++      truncate_file(&hf->data, size);
++      close_file(&hf->data);
++
++      (*hf->mount->meta->close_file)(hf);
++}
++
++/* XXX Assumes that you can't make a normal file */
++
++static int humfs_make_node(const char *path, int mode, int uid, int gid, 
++                         int type, int major, int minor, 
++                         struct externfs_data *ed)
++{
++      struct humfs *mount = container_of(ed, struct humfs, ext);
++      struct file_handle fh;
++      const char *data_path[3] = { mount->data, path, NULL };
++      int err;
++      char t;
++
++      err = host_create_file(data_path, S_IRWXUGO, &fh);
++      if(err)
++              goto out;
++
++      close_file(&fh);
++
++      switch(type){
++      case S_IFCHR:
++              t = 'c';
++              break;
++      case S_IFBLK:
++              t = 'b';
++              break;
++      case S_IFIFO:
++              t = 'p';
++              break;
++      case S_IFSOCK:
++              t = 's';
++              break;
++      default:
++              err = -EINVAL;
++              printk("humfs_make_node - bad node type : %d\n", type);
++              goto out_rm;
++      }
++
++      err = (*mount->meta->make_node)(path, mode, uid, gid, t, major, minor, 
++                                      mount);
++      if(err)
++              goto out_rm;
++
++ out:
++      return(err);
++
++ out_rm:
++      host_unlink_file(data_path);
++      goto out;
++}
++              
++static int humfs_create_file(struct externfs_inode *ext, char *path, int mode, 
++                           int uid, int gid, struct inode *inode, 
++                           struct externfs_data *ed)
++{
++      struct humfs *mount = container_of(ed, struct humfs, ext);
++      struct humfs_file *hf = container_of(ext, struct humfs_file, ext);
++      const char *data_path[3] = { mount->data, path, NULL };
++      int err;
++
++      err = (*mount->meta->create_file)(hf, path, mode, uid, gid, inode, 
++                                        mount);
++      if(err)
++              goto out;
++
++      err = host_create_file(data_path, S_IRWXUGO, &hf->data);
++      if(err)
++              goto out_rm;
++
++      
++      is_reclaimable(&hf->data, humfs_data_name, inode);
++
++      return(0);
++
++ out_rm:
++      (*mount->meta->remove_file)(path, mount);
++      (*mount->meta->close_file)(hf);
++ out:
++      return(err);
++}
++
++static int humfs_set_attr(const char *path, struct externfs_iattr *attrs, 
++                        struct externfs_data *ed)
++{
++      struct humfs *mount = container_of(ed, struct humfs, ext);
++      const char *data_path[3] = { mount->data, path, NULL };
++      int (*chown)(const char *, int, int, int, struct humfs *);
++      int err;
++
++      chown = mount->meta->change_ownerships;
++      if(attrs->ia_valid & EXTERNFS_ATTR_MODE){
++              err = (*chown)(path, attrs->ia_mode, -1, -1, mount);
++              if(err)
++                      return(err);
++      }
++      if(attrs->ia_valid & EXTERNFS_ATTR_UID){
++              err = (*chown)(path, -1, attrs->ia_uid, -1, mount);
++              if(err)
++                      return(err);
++      }
++      if(attrs->ia_valid & EXTERNFS_ATTR_GID){
++              err = (*chown)(path, -1, -1, attrs->ia_gid, mount);
++              if(err)
++                      return(err);
++      }
++
++      attrs->ia_valid &= ~(EXTERNFS_ATTR_MODE | EXTERNFS_ATTR_UID | 
++                           EXTERNFS_ATTR_GID);
++
++      return(host_set_attr(data_path, attrs));
++}
++
++static int humfs_make_symlink(const char *from, const char *to, int uid, 
++                            int gid, struct externfs_data *ed)
++{
++      struct humfs *mount = container_of(ed, struct humfs, ext);
++      struct humfs_file *hf;
++      const char *data_path[3] = { mount->data, from, NULL };
++      int err = -ENOMEM;
++
++      hf = (*mount->meta->init_file)();
++      if(hf == NULL)
++              goto out;
++
++      err = (*mount->meta->create_file)(hf, from, S_IRWXUGO, uid, gid, NULL, 
++                                        mount);
++      if(err)
++              goto out_close;
++
++      err = host_make_symlink(data_path, to);
++      if(err)
++              (*mount->meta->remove_file)(from, mount);
++
++ out_close:
++      (*mount->meta->close_file)(hf);
++ out:
++      return(err);
++}
++
++static int humfs_link_file(const char *to, const char *from, int uid, int gid, 
++                         struct externfs_data *ed)
++{
++      struct humfs *mount = container_of(ed, struct humfs, ext);
++      const char *data_path_from[3] = { mount->data, from, NULL };
++      const char *data_path_to[3] = { mount->data, to, NULL };
++      int err;
++
++      err = (*mount->meta->create_link)(to, from, mount);
++      if(err)
++              return(err);
++
++      err = host_link_file(data_path_to, data_path_from);
++      if(err)
++              (*mount->meta->remove_file)(from, mount);
++      
++      return(err);
++}
++
++static int humfs_unlink_file(const char *path, struct externfs_data *ed)
++{
++      struct humfs *mount = container_of(ed, struct humfs, ext);
++      const char *data_path[3] = { mount->data, path, NULL };
++      int err;
++
++      err = (*mount->meta->remove_file)(path, mount);
++      if (err)
++              return err;
++
++      (*mount->meta->remove_file)(path, mount);
++      return(host_unlink_file(data_path));
++}
++
++static void humfs_invisible(struct externfs_inode *ext)
++{
++      struct humfs_file *hf = container_of(ext, struct humfs_file, ext);
++      struct humfs *mount = hf->mount;
++      
++      (*mount->meta->invisible)(hf);
++      not_reclaimable(&hf->data);
++}
++
++static int humfs_make_dir(const char *path, int mode, int uid, int gid, 
++                        struct externfs_data *ed)
++{
++      struct humfs *mount = container_of(ed, struct humfs, ext);
++      const char *data_path[3] = { mount->data, path, NULL };
++      int err;
++
++      err = (*mount->meta->create_dir)(path, mode, uid, gid, mount);
++      if(err)
++              return(err);
++      
++      err = host_make_dir(data_path, S_IRWXUGO);
++      if(err)
++              (*mount->meta->remove_dir)(path, mount);
++
++      return(err);
++}
++
++static int humfs_remove_dir(const char *path, int uid, int gid, 
++                          struct externfs_data *ed)
++{
++      struct humfs *mount = container_of(ed, struct humfs, ext);
++      const char *data_path[3] = { mount->data, path, NULL };
++      int err;
++
++      err = host_remove_dir(data_path);
++      if (err)
++              return err;
++
++      (*mount->meta->remove_dir)(path, mount);
++
++      return(err);
++}
++
++static int humfs_read_link(char *file, int uid, int gid, char *buf, int size, 
++                         struct externfs_data *ed)
++{
++      struct humfs *mount = container_of(ed, struct humfs, ext);
++      const char *data_path[3] = { mount->data, file, NULL };
++
++      return(host_read_link(data_path, buf, size));
++}
++
++struct humfs *inode_humfs_info(struct inode *inode)
++{
++      return(container_of(inode_externfs_info(inode), struct humfs, ext));
++}
++
++static int humfs_rename_file(char *from, char *to, struct externfs_data *ed)
++{
++      struct humfs *mount = container_of(ed, struct humfs, ext);
++      const char *data_path_from[3] = { mount->data, from, NULL };
++      const char *data_path_to[3] = { mount->data, to, NULL };
++      int err;
++
++      err = (*mount->meta->rename_file)(from, to, mount);
++      if(err)
++              return(err);
++      
++      err = host_rename_file(data_path_from, data_path_to);
++      if(err)
++              (*mount->meta->rename_file)(to, from, mount);
++
++      return(err);
++}
++
++static int humfs_stat_fs(long *bsize_out, long long *blocks_out, 
++                       long long *bfree_out, long long *bavail_out, 
++                       long long *files_out, long long *ffree_out, 
++                       void *fsid_out, int fsid_size, long *namelen_out, 
++                       long *spare_out, struct externfs_data *ed)
++{
++      struct humfs *mount = container_of(ed, struct humfs, ext);
++      const char *data_path[3] = { mount->data, NULL };
++      int err;
++
++      /* XXX Needs to maintain this info as metadata */
++      err = host_stat_fs(data_path, bsize_out, blocks_out, bfree_out, 
++                         bavail_out, files_out, ffree_out, fsid_out, 
++                         fsid_size, namelen_out, spare_out);
++      if(err)
++              return(err);
++
++      *blocks_out = mount->total / *bsize_out;
++      *bfree_out = (mount->total - mount->used) / *bsize_out;
++      *bavail_out = (mount->total - mount->used) / *bsize_out;
++      return(0);
++}
++
++int humfs_truncate_file(struct externfs_inode *ext, __u64 size, 
++                      struct externfs_data *ed)
++{
++      struct humfs_file *hf = container_of(ext, struct humfs_file, ext);
++
++      return(truncate_file(&hf->data, size));
++}
++
++char *humfs_path(char *dir, char *file)
++{
++      int need_slash, len = strlen(dir) + strlen(file);
++      char *new;
++
++      need_slash = (dir[strlen(dir) - 1] != '/');
++      if(need_slash)
++              len++;
++
++      new = kmalloc(len + 1, GFP_KERNEL);
++      if(new == NULL)
++              return(NULL);
++
++      strcpy(new, dir);
++      if(need_slash)
++              strcat(new, "/");
++      strcat(new, file);
++
++      return(new);
++}
++
++DECLARE_MUTEX(meta_sem);
++struct list_head metas = LIST_HEAD_INIT(metas);
++
++static struct humfs_meta_ops *find_meta(const char *name)
++{
++      struct list_head *ele;
++      struct humfs_meta_ops *m;
++ 
++      down(&meta_sem);
++      list_for_each(ele, &metas){
++              m = list_entry(ele, struct humfs_meta_ops, list);
++              if(!strcmp(m->name, name))
++                      goto out;
++      }
++      m = NULL;
++ out:
++      up(&meta_sem);
++      return(m);
++}
++
++void register_meta(struct humfs_meta_ops *ops)
++{
++      down(&meta_sem);
++      list_add(&ops->list, &metas);
++      up(&meta_sem);
++}
++ 
++void unregister_meta(struct humfs_meta_ops *ops)
++{
++      down(&meta_sem);
++      list_del(&ops->list);
++      up(&meta_sem);
++}
++ 
++static struct humfs *read_superblock(char *root)
++{
++      struct humfs *mount;
++      struct humfs_meta_ops *meta = NULL;
++      struct file_handle *fh;
++      const char *path[] = { root, "superblock", NULL };
++      u64 used, total;
++      char meta_buf[33], line[HOSTFS_BUFSIZE], *newline;
++      unsigned long long pos;
++      int version, i, n, err;
++
++      fh = kmalloc(sizeof(*fh), GFP_KERNEL);
++      if(fh == NULL)
++              return(ERR_PTR(-ENOMEM));
++
++      err = host_open_file(path, 1, 0, fh);
++      if(err){
++              printk("Failed to open %s/%s, errno = %d\n", path[0],
++                     path[1], err);
++              return(ERR_PTR(err));
++      }
++
++      used = 0;
++      total = 0;
++      pos = 0;
++      i = 0;
++      while(1){
++              n = read_file(fh, pos, &line[i], sizeof(line) - i - 1);
++              if((n == 0) && (i == 0))
++                      break;
++              if(n < 0)
++                      return(ERR_PTR(n));
++
++              pos += n;
++              if(n > 0)
++                      line[n + i] = '\0';
++
++              newline = strchr(line, '\n');
++              if(newline == NULL){
++                      printk("read_superblock - line too long : '%s'\n", 
++                             line);
++                      return(ERR_PTR(-EINVAL));
++              }
++              newline++;
++
++              if(sscanf(line, "version %d\n", &version) == 1){
++                      if(version != HUMFS_VERSION){
++                              printk("humfs version mismatch - want version "
++                                     "%d, got version %d.\n", HUMFS_VERSION,
++                                     version);
++                              return(ERR_PTR(-EINVAL));
++                      }
++              }
++              else if(sscanf(line, "used %Lu\n", &used) == 1) ;
++              else if(sscanf(line, "total %Lu\n", &total) == 1) ;
++              else if(sscanf(line, "metadata %32s\n", meta_buf) == 1){
++                      meta = find_meta(meta_buf);
++                      if(meta == NULL){
++                              printk("read_superblock - meta api \"%s\" not "
++                                     "registered\n", meta_buf);
++                              return(ERR_PTR(-EINVAL));
++                      }
++              }
++              
++              else {
++                      printk("read_superblock - bogus line : '%s'\n", line);
++                      return(ERR_PTR(-EINVAL));
++              }
++
++              i = newline - line;
++              memmove(line, newline, sizeof(line) - i);
++              i = strlen(line);
++      }
++
++      if(used == 0){
++              printk("read_superblock - used not specified or set to "
++                     "zero\n");
++              return(ERR_PTR(-EINVAL));
++      }
++      if(total == 0){
++              printk("read_superblock - total not specified or set to "
++                     "zero\n");
++              return(ERR_PTR(-EINVAL));
++      }
++      if(used > total){
++              printk("read_superblock - used is greater than total\n");
++              return(ERR_PTR(-EINVAL));
++      }
++
++      if(meta == NULL){
++              meta = find_meta("shadow_fs");
++      }
++
++      if(meta == NULL){
++              printk("read_superblock - valid meta api was not specified\n");
++              return(ERR_PTR(-EINVAL));
++      }
++
++      mount = (*meta->init_mount)(root);
++      if(IS_ERR(mount))
++              return(mount);
++
++      *mount = ((struct humfs) { .total       = total,
++                                 .used        = used,
++                                 .meta        = meta });
++      return(mount);
++}
++
++struct externfs_file_ops humfs_no_mmap_file_ops = {
++      .stat_file              = humfs_stat_file,
++      .file_type              = humfs_file_type,
++      .access_file            = NULL,
++      .open_file              = humfs_open_file,
++      .open_dir               = humfs_open_dir,
++      .read_dir               = humfs_read_dir,
++      .read_file              = humfs_read_file,
++      .write_file             = humfs_write_file,
++      .map_file_page          = NULL,
++      .close_file             = humfs_close_file,
++      .close_dir              = humfs_close_dir,
++      .invisible              = humfs_invisible,
++      .create_file            = humfs_create_file,
++      .set_attr               = humfs_set_attr,
++      .make_symlink           = humfs_make_symlink,
++      .unlink_file            = humfs_unlink_file,
++      .make_dir               = humfs_make_dir,
++      .remove_dir             = humfs_remove_dir,
++      .make_node              = humfs_make_node,
++      .link_file              = humfs_link_file,
++      .read_link              = humfs_read_link,
++      .rename_file            = humfs_rename_file,
++      .statfs                 = humfs_stat_fs,
++      .truncate_file          = humfs_truncate_file
++};
++
++struct externfs_file_ops humfs_mmap_file_ops = {
++      .stat_file              = humfs_stat_file,
++      .file_type              = humfs_file_type,
++      .access_file            = NULL,
++      .open_file              = humfs_open_file,
++      .open_dir               = humfs_open_dir,
++      .read_dir               = humfs_read_dir,
++      .read_file              = humfs_read_file,
++      .write_file             = humfs_write_file,
++      .map_file_page          = humfs_map_file_page,
++      .close_file             = humfs_close_file,
++      .close_dir              = humfs_close_dir,
++      .invisible              = humfs_invisible,
++      .create_file            = humfs_create_file,
++      .set_attr               = humfs_set_attr,
++      .make_symlink           = humfs_make_symlink,
++      .unlink_file            = humfs_unlink_file,
++      .make_dir               = humfs_make_dir,
++      .remove_dir             = humfs_remove_dir,
++      .make_node              = humfs_make_node,
++      .link_file              = humfs_link_file,
++      .read_link              = humfs_read_link,
++      .rename_file            = humfs_rename_file,
++      .statfs                 = humfs_stat_fs,
++      .truncate_file          = humfs_truncate_file
++};
++
++static struct externfs_data *mount_fs(char *mount_arg)
++{
++      char *root, *data, *flags;
++      struct humfs *mount;
++      struct externfs_file_ops *file_ops;
++      int err, do_mmap = 0;
++
++      if(mount_arg == NULL){
++              printk("humfs - no host directory specified\n");
++              return(NULL);
++      }
++
++      flags = strchr((char *) mount_arg, ',');
++      if(flags != NULL){
++              do {
++                      *flags++ = '\0';
++
++                      if(!strcmp(flags, "mmap"))
++                              do_mmap = 1;
++
++                      flags = strchr(flags, ',');
++              } while(flags != NULL);
++      }
++
++      err = -ENOMEM;
++      root = host_root_filename(mount_arg);
++      if(root == NULL)
++              goto err;
++
++      mount = read_superblock(root);
++      if(IS_ERR(mount)){
++              err = PTR_ERR(mount);
++              goto err_free_root;
++      }
++
++      data = humfs_path(root, "data/");
++      if(data == NULL)
++              goto err_free_mount;
++
++      if(CHOOSE_MODE(do_mmap, 0)){
++              printk("humfs doesn't support mmap in tt mode\n");
++              do_mmap = 0;
++      }
++
++      mount->data = data;
++      mount->mmap = do_mmap;
++
++      file_ops = do_mmap ? &humfs_mmap_file_ops : &humfs_no_mmap_file_ops;
++      init_externfs(&mount->ext, file_ops);
++
++      return(&mount->ext);
++
++ err_free_mount:
++      kfree(mount);
++ err_free_root:
++      kfree(root);
++ err:
++      return(NULL);
++}
++
++struct externfs_mount_ops humfs_mount_ops = {
++      .init_file              = humfs_init_file,
++      .mount                  = mount_fs,
++};
++
++static int __init init_humfs(void)
++{
++      return(register_externfs("humfs", &humfs_mount_ops));
++}
++
++static void __exit exit_humfs(void)
++{
++      unregister_externfs("humfs");
++}
++
++__initcall(init_humfs);
++__exitcall(exit_humfs);
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/fs/hostfs/Makefile
+===================================================================
+--- linux-2.4.29.orig/arch/um/fs/hostfs/Makefile       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/fs/hostfs/Makefile    2005-05-03 22:28:14.284438648 +0300
+@@ -0,0 +1,14 @@
++# 
++# Copyright (C) 2000 - 2004 Jeff Dike (jdike@addtoit.com)
++# Licensed under the GPL
++#
++
++O_TARGET := hostfs.o
++
++obj-$(CONFIG_EXTERNFS) += externfs.o
++obj-$(CONFIG_HOSTFS) += host_fs.o host_file.o
++obj-$(CONFIG_HUMFS) += humfs.o host_file.o meta_fs.o
++
++obj-m = $(O_TARGET)
++
++include $(TOPDIR)/Rules.make
+Index: linux-2.4.29/arch/um/fs/hostfs/metadata.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/fs/hostfs/metadata.h     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/fs/hostfs/metadata.h  2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,84 @@
++/* 
++ * Copyright (C) 2004 Piotr Neuman (sikkh@wp.pl) and 
++ * Jeff Dike (jdike@addtoit.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __UM_FS_METADATA
++#define __UM_FS_METADATA
++
++#include "linux/fs.h"
++#include "linux/list.h"
++#include "os.h"
++#include "hostfs.h"
++#include "filehandle.h"
++
++#define container_of(ptr, type, member) ({                      \
++        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
++        (type *)( (char *)__mptr - offsetof(type,member) );})
++
++struct humfs {
++      struct externfs_data ext;
++      __u64 used;
++      __u64 total;
++      char *data;
++      int mmap;
++      int direct;
++      struct humfs_meta_ops *meta;
++};
++
++struct humfs_file {
++      struct humfs *mount;
++      struct file_handle data;
++      struct externfs_inode ext;
++};
++
++struct humfs_meta_ops {
++      struct list_head list;
++      char *name;
++      struct humfs_file *(*init_file)(void);
++      int (*open_file)(struct humfs_file *hf, const char *path, 
++                       struct inode *inode, struct humfs *humfs);
++      int (*create_file)(struct humfs_file *hf, const char *path, int mode, 
++                         int uid, int gid, struct inode *inode, 
++                         struct humfs *humfs);
++      void (*close_file)(struct humfs_file *humfs);
++      int (*ownerships)(const char *path, int *mode_out, int *uid_out, 
++                        int *gid_out, char *type_out, int *maj_out, 
++                        int *min_out, struct humfs *humfs);
++      int (*make_node)(const char *path, int mode, int uid, int gid,
++                       int type, int major, int minor, struct humfs *humfs);
++      int (*create_link)(const char *to, const char *from, 
++                         struct humfs *humfs);
++      int (*remove_file)(const char *path, struct humfs *humfs);
++      int (*create_dir)(const char *path, int mode, int uid, int gid, 
++                        struct humfs *humfs);
++      int (*remove_dir)(const char *path, struct humfs *humfs);
++      int (*change_ownerships)(const char *path, int mode, int uid, int gid,
++                               struct humfs *humfs);
++      int (*rename_file)(const char *from, const char *to, 
++                         struct humfs *humfs);
++      void (*invisible)(struct humfs_file *hf);
++      struct humfs *(*init_mount)(char *root);
++      void (*free_mount)(struct humfs *humfs);
++};
++
++void register_meta(struct humfs_meta_ops *ops);
++void unregister_meta(struct humfs_meta_ops *ops);
++
++char *humfs_path(char *dir, char *file);
++char *humfs_name(struct inode *inode, char *prefix);
++extern struct humfs *inode_humfs_info(struct inode *inode);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/fs/hostfs/meta_fs.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/fs/hostfs/meta_fs.c      1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/fs/hostfs/meta_fs.c   2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,519 @@
++/* 
++ * Copyright (C) 2004 Jeff Dike (jdike@addtoit.com)
++ * Licensed under the GPL
++ */
++
++#include <linux/slab.h>
++#include "hostfs.h"
++#include "metadata.h"
++#include "kern_util.h"
++
++#define METADATA_FILE_PATH(meta) (meta)->root, "file_metadata"
++#define METADATA_DIR_PATH(meta) (meta)->root, "dir_metadata"
++
++struct meta_fs {
++      struct humfs humfs;
++      char *root;
++};
++
++struct meta_file {
++      struct humfs_file humfs;
++      struct file_handle fh;
++};
++
++static int meta_file_path(const char *path, struct meta_fs *meta, 
++                        const char *path_out[])
++{
++      const char *data_path[] = { meta->root, "data", path, NULL };
++      char data_tmp[HOSTFS_BUFSIZE];
++      char *data_file = get_path(data_path, data_tmp, sizeof(data_tmp));
++
++      if(data_file == NULL)
++              return(-ENOMEM);
++
++      path_out[0] = meta->root;
++      path_out[2] = path;
++      if(os_file_type(data_file) == OS_TYPE_DIR){
++              path_out[1] = "dir_metadata";
++              path_out[3] = "metadata";
++              path_out[4] = NULL;
++      }
++      else {
++              path_out[1] = "file_metadata";
++              path_out[3] = NULL;
++      }
++
++      return(0);
++}
++
++static int open_meta_file(const char *path, struct humfs *humfs,
++                        struct file_handle *fh)
++{
++      struct meta_fs *meta = container_of(humfs, struct meta_fs, humfs);
++      const char *meta_path[5];
++      char meta_tmp[HOSTFS_BUFSIZE];
++      char *meta_file;
++      int err;
++
++      err = meta_file_path(path, meta, meta_path);
++      if(err)
++              goto out;
++
++      meta_file = get_path(meta_path, meta_tmp, sizeof(meta_tmp));
++      if(meta_file == NULL)
++              goto out;
++      
++      err = open_filehandle(meta_file, of_rdwr(OPENFLAGS()), 0, fh);
++
++ out:
++      return(err);
++}
++
++static char *meta_fs_name(struct inode *inode)
++{
++      struct humfs *mount = inode->i_sb->u.generic_sbp;
++      struct meta_fs *meta = container_of(mount, struct meta_fs, humfs);
++      const char *metadata_path[5];
++      char tmp[HOSTFS_BUFSIZE], *name, *file;
++
++      if(meta_file_path("", meta, metadata_path))
++              return(NULL);
++
++      file = get_path(metadata_path, tmp, sizeof(tmp));
++      if(file == NULL)
++              return(NULL);
++
++      name = inode_name_prefix(inode, file);
++
++      free_path(file, tmp);
++      return(name);
++}
++
++static void metafs_invisible(struct humfs_file *hf)
++{
++      struct meta_file *mf = container_of(hf, struct meta_file, humfs);
++
++      not_reclaimable(&mf->fh);
++}
++
++static struct humfs_file *metafs_init_file(void)
++{
++      struct meta_file *mf;
++      int err = -ENOMEM;
++
++      mf = kmalloc(sizeof(*mf), GFP_KERNEL);
++      if(mf == NULL)
++              return(ERR_PTR(err));
++
++      return(&mf->humfs);
++}
++
++static int metafs_open_file(struct humfs_file *hf, const char *path, 
++                          struct inode *inode, struct humfs *humfs)
++{
++      struct meta_file *mf = container_of(hf, struct meta_file, humfs);
++      int err;
++
++      err = open_meta_file(path, humfs, &mf->fh);
++      if(err)
++              return(err);
++
++      is_reclaimable(&mf->fh, meta_fs_name, inode);
++
++      return(0);
++}
++
++static void metafs_close_file(struct humfs_file *hf)
++{
++      struct meta_file *meta = container_of(hf, struct meta_file, humfs);
++
++      close_file(&meta->fh);
++      kfree(meta);
++}
++
++static int metafs_create_file(struct humfs_file *hf, const char *path, 
++                            int mode, int uid, int gid, struct inode *inode, 
++                            struct humfs *humfs)
++{
++      struct meta_fs *meta = container_of(humfs, struct meta_fs, humfs);
++      struct meta_file *mf = container_of(hf, struct meta_file, humfs);
++      char tmp[HOSTFS_BUFSIZE];
++      const char *metadata_path[] = { METADATA_FILE_PATH(meta), path, NULL };
++      char *file = get_path(metadata_path, tmp, sizeof(tmp));
++      char buf[sizeof("mmmm uuuuuuuuuu gggggggggg")];
++      int err = -ENOMEM;
++
++      if(file == NULL)
++              goto out;
++
++      err = open_filehandle(file, of_write(of_create(OPENFLAGS())), 0644, 
++                            &mf->fh);
++      if(err)
++              goto out_free_path;
++
++      if(inode != NULL)
++              is_reclaimable(&mf->fh, meta_fs_name, inode);
++
++      sprintf(buf, "%d %d %d\n", mode  & S_IRWXUGO, uid, gid);
++      err = write_file(&mf->fh, 0, buf, strlen(buf));
++      if(err < 0)
++              goto out_rm;
++
++      free_path(file, tmp);
++      return(0);
++
++ out_rm:
++      close_file(&mf->fh);
++      os_remove_file(file);
++ out_free_path:
++      free_path(file, tmp);
++ out:
++      return(err);
++}
++
++static int metafs_create_link(const char *to, const char *from, 
++                            struct humfs *humfs)
++{
++      struct meta_fs *meta = container_of(humfs, struct meta_fs, humfs);
++      const char *path_to[] = { METADATA_FILE_PATH(meta), to,  NULL };
++      const char *path_from[] = { METADATA_FILE_PATH(meta), from, NULL };
++
++      return(host_link_file(path_to, path_from));
++}
++
++static int metafs_remove_file(const char *path, struct humfs *humfs)
++{
++      struct meta_fs *meta = container_of(humfs, struct meta_fs, humfs);
++      char tmp[HOSTFS_BUFSIZE];
++      const char *metadata_path[] = { METADATA_FILE_PATH(meta), path, NULL };
++      char *file = get_path(metadata_path, tmp, sizeof(tmp));
++      int err = -ENOMEM;
++
++      if(file == NULL)
++              goto out;
++
++      err = os_remove_file(file);
++
++ out:
++      free_path(file, tmp);
++      return(err);
++}
++
++static int metafs_create_directory(const char *path, int mode, int uid, 
++                                 int gid, struct humfs *humfs)
++{
++      struct meta_fs *meta = container_of(humfs, struct meta_fs, humfs);
++      char tmp[HOSTFS_BUFSIZE];
++      const char *dir_path[] = { METADATA_DIR_PATH(meta), path, NULL, NULL };
++      const char *file_path[] = { METADATA_FILE_PATH(meta), path, NULL, 
++                                  NULL };
++      char *file, dir_meta[sizeof("mmmm uuuuuuuuuu gggggggggg\n")];
++      int err, fd;
++
++      err = host_make_dir(dir_path, 0755);
++      if(err)
++              goto out;
++
++      err = host_make_dir(file_path, 0755);
++      if(err)
++              goto out_rm;
++
++      /* This to make the index independent of the number of elements in
++       * METADATA_DIR_PATH().
++       */
++      dir_path[sizeof(dir_path) / sizeof(dir_path[0]) - 2] = "metadata";
++
++      err = -ENOMEM;
++      file = get_path(dir_path, tmp, sizeof(tmp));
++      if(file == NULL)
++              goto out;
++
++      fd = os_open_file(file, of_create(of_rdwr(OPENFLAGS())), 0644);
++      if(fd < 0){
++              err = fd;
++              goto out_free;
++      }
++
++      sprintf(dir_meta, "%d %d %d\n", mode & S_IRWXUGO, uid, gid);
++      err = os_write_file(fd, dir_meta, strlen(dir_meta));
++      if(err > 0)
++              err = 0;
++
++      os_close_file(fd);
++
++ out_free:
++      free_path(file, tmp);
++ out_rm:
++      host_remove_dir(dir_path);
++ out:
++      return(err);
++}
++
++static int metafs_remove_directory(const char *path, struct humfs *humfs)
++{
++      struct meta_fs *meta = container_of(humfs, struct meta_fs, humfs);
++      char tmp[HOSTFS_BUFSIZE], *file;
++      const char *dir_path[] = { METADATA_DIR_PATH(meta), path, "metadata", 
++                                 NULL };
++      const char *file_path[] = { METADATA_FILE_PATH(meta), path, NULL };
++      char *slash;
++      int err;
++
++      err = -ENOMEM;
++      file = get_path(dir_path, tmp, sizeof(tmp));
++      if(file == NULL)
++              goto out;
++
++      err = os_remove_file(file);
++      if(err)
++              goto out_free;
++
++      slash = strrchr(file, '/');
++      if(slash == NULL){
++              printk("remove_shadow_directory failed to find last slash\n");
++              goto out_free;
++      }
++      *slash = '\0';
++      err = os_remove_dir(file);
++      free_path(file, tmp);
++
++      file = get_path(file_path, tmp, sizeof(tmp));
++      if(file == NULL)
++              goto out;
++
++      err = os_remove_dir(file);
++      if(err)
++              goto out_free;
++
++ out:
++      return(err);
++ out_free:
++      free_path(file, tmp);
++      goto out;
++}
++
++static int metafs_make_node(const char *path, int mode, int uid, int gid, 
++                          int type, int maj, int min, struct humfs *humfs)
++{
++      struct meta_fs *meta = container_of(humfs, struct meta_fs, humfs);
++      struct file_handle fh;
++      char tmp[HOSTFS_BUFSIZE];
++      const char *metadata_path[] = { METADATA_FILE_PATH(meta), path, NULL };
++      int err;
++      char buf[sizeof("mmmm uuuuuuuuuu gggggggggg x nnn mmm\n")], *file;
++
++      sprintf(buf, "%d %d %d %c %d %d\n", mode & S_IRWXUGO, uid, gid, type, 
++              maj, min);
++
++      err = -ENOMEM;
++      file = get_path(metadata_path, tmp, sizeof(tmp));
++      if(file == NULL)
++              goto out;
++
++      err = open_filehandle(file, 
++                            of_create(of_rdwr(OPENFLAGS())), 0644, &fh);
++      if(err)
++              goto out_free;
++
++      err = write_file(&fh, 0, buf, strlen(buf));
++      if(err > 0)
++              err = 0;
++
++      close_file(&fh);
++
++ out_free:
++      free_path(file, tmp);
++ out:
++      return(err);
++}
++
++static int metafs_ownerships(const char *path, int *mode_out, int *uid_out, 
++                           int *gid_out, char *type_out, int *maj_out, 
++                           int *min_out, struct humfs *humfs)
++{
++      struct file_handle fh;
++      char buf[sizeof("mmmm uuuuuuuuuu gggggggggg x nnn mmm\n")];
++      int err, n, mode, uid, gid, maj, min;
++      char type;
++
++      err = open_meta_file(path, humfs, &fh);
++      if(err)
++              goto out;
++
++      err = os_read_file(fh.fd, buf, sizeof(buf) - 1);
++      if(err < 0)
++              goto out_close;
++
++      buf[err] = '\0';
++      err = 0;
++
++      n = sscanf(buf, "%d %d %d %c %d %d", &mode, &uid, &gid, &type, &maj, 
++                 &min);
++      if(n == 3){
++              maj = -1;
++              min = -1;
++              type = 0;
++              err = 0;
++      }
++      else if(n != 6)
++              err = -EINVAL;
++
++      if(mode_out != NULL)
++              *mode_out = mode;
++      if(uid_out != NULL)
++              *uid_out = uid;
++      if(gid_out != NULL)
++              *gid_out = uid;
++      if(type_out != NULL)
++              *type_out = type;
++      if(maj_out != NULL)
++              *maj_out = maj;
++      if(min_out != NULL)
++              *min_out = min;
++
++ out_close:
++      close_file(&fh);
++ out:
++      return(err);
++}
++
++static int metafs_change_ownerships(const char *path, int mode, int uid, 
++                                  int gid, struct humfs *humfs)
++{
++      struct file_handle fh;
++      char type;
++      char buf[sizeof("mmmm uuuuuuuuuu gggggggggg x nnn mmm\n")];
++      int err = -ENOMEM, old_mode, old_uid, old_gid, n, maj, min;
++
++      err = open_meta_file(path, humfs, &fh);
++      if(err)
++              goto out;
++
++      err = read_file(&fh, 0, buf, sizeof(buf) - 1);
++      if(err < 0)
++              goto out_close;
++
++      buf[err] = '\0';
++
++      n = sscanf(buf, "%d %d %d %c %d %d\n", &old_mode, &old_uid, &old_gid,
++                 &type, &maj, &min);
++      if((n != 3) && (n != 6)){
++              err = -EINVAL;
++              goto out_close;
++      }
++
++      if(mode == -1)
++                mode = old_mode;
++      if(uid == -1)
++              uid = old_uid;
++      if(gid == -1)
++              gid = old_gid;
++
++      if(n == 3)
++              sprintf(buf, "%d %d %d\n", mode & S_IRWXUGO, uid, gid);
++      else
++              sprintf(buf, "%d %d %d %c %d %d\n", mode & S_IRWXUGO, uid, gid,
++                      type, maj, min);
++
++      err = write_file(&fh, 0, buf, strlen(buf));
++      if(err > 0)
++              err = 0;
++
++      err = truncate_file(&fh, strlen(buf));
++
++ out_close:
++      close_file(&fh);
++ out:
++      return(err);
++}
++
++static int metafs_rename_file(const char *from, const char *to, 
++                            struct humfs *humfs)
++{
++      struct meta_fs *meta = container_of(humfs, struct meta_fs, humfs);
++      const char *metadata_path_from[5], *metadata_path_to[5];
++      int err;
++
++      err = meta_file_path(from, meta, metadata_path_from);
++      if(err)
++              return(err);
++
++      err = meta_file_path(to, meta, metadata_path_to);
++      if(err)
++              return(err);
++
++      return(host_rename_file(metadata_path_from, metadata_path_to));
++}
++
++static struct humfs *metafs_init_mount(char *root)
++{
++      struct meta_fs *meta;
++      int err = -ENOMEM;
++
++      meta = kmalloc(sizeof(*meta), GFP_KERNEL);
++      if(meta == NULL)
++              goto out;
++
++      meta->root = uml_strdup(root);
++      if(meta->root == NULL)
++              goto out_free_meta;
++
++      return(&meta->humfs);
++
++ out_free_meta:
++      kfree(meta);
++ out:
++      return(ERR_PTR(err));
++}
++
++static void metafs_free_mount(struct humfs *humfs)
++{
++      struct meta_fs *meta = container_of(humfs, struct meta_fs, humfs);
++      
++      kfree(meta);
++}
++
++struct humfs_meta_ops hum_fs_meta_fs_ops = {
++      .list                   = LIST_HEAD_INIT(hum_fs_meta_fs_ops.list),
++      .name                   = "shadow_fs",
++      .init_file              = metafs_init_file,
++      .open_file              = metafs_open_file,
++      .close_file             = metafs_close_file,
++      .ownerships             = metafs_ownerships,
++      .make_node              = metafs_make_node,
++      .create_file            = metafs_create_file,
++      .create_link            = metafs_create_link,
++      .remove_file            = metafs_remove_file,
++      .create_dir             = metafs_create_directory,
++      .remove_dir             = metafs_remove_directory,
++      .change_ownerships      = metafs_change_ownerships,
++      .rename_file            = metafs_rename_file,
++      .invisible              = metafs_invisible,
++      .init_mount             = metafs_init_mount,
++      .free_mount             = metafs_free_mount,
++};
++
++static int __init init_meta_fs(void)
++{
++      register_meta(&hum_fs_meta_fs_ops);
++      return(0);
++}
++
++static void __exit exit_meta_fs(void)
++{
++      unregister_meta(&hum_fs_meta_fs_ops);
++}
++
++__initcall(init_meta_fs);
++__exitcall(exit_meta_fs);
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/fs/hppfs/hppfs_kern.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/fs/hppfs/hppfs_kern.c    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/fs/hppfs/hppfs_kern.c 2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,737 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <linux/fs.h>
++#include <linux/module.h>
++#include <linux/slab.h>
++#include <linux/list.h>
++#include <linux/kernel.h>
++#include <linux/ctype.h>
++#include <asm/uaccess.h>
++#include "os.h"
++
++struct hppfs_data {
++      struct list_head list;
++      char contents[PAGE_SIZE - sizeof(struct list_head)];
++};
++
++struct hppfs_private {
++      struct file proc_file;
++      int host_fd;
++      loff_t len;
++      struct hppfs_data *contents;
++};
++
++#define HPPFS_SUPER_MAGIC 0xb00000ee
++
++static struct super_operations hppfs_sbops;
++
++static struct inode *get_inode(struct super_block *sb, struct dentry *dentry,
++                             int *error);
++
++static int is_pid(struct dentry *dentry)
++{
++      struct super_block *sb;
++      int i;
++
++      sb = dentry->d_sb;
++      if((sb->s_op != &hppfs_sbops) || (dentry->d_parent != sb->s_root))
++              return(0);
++
++      for(i = 0; i < dentry->d_name.len; i++){
++              if(!isdigit(dentry->d_name.name[i]))
++                      return(0);
++      }
++      return(1);
++}
++
++static char *dentry_name(struct dentry *dentry, int extra)
++{
++      struct dentry *parent;
++      char *root, *name;
++      const char *seg_name;
++      int len, seg_len;
++
++      len = 0;
++      parent = dentry;
++      while(parent->d_parent != parent){
++              if(is_pid(parent))
++                      len += strlen("pid") + 1;
++              else len += parent->d_name.len + 1;
++              parent = parent->d_parent;
++      }
++      
++      root = "proc";
++      len += strlen(root);
++      name = kmalloc(len + extra + 1, GFP_KERNEL);
++      if(name == NULL) return(NULL);
++
++      name[len] = '\0';
++      parent = dentry;
++      while(parent->d_parent != parent){
++              if(is_pid(parent)){
++                      seg_name = "pid";
++                      seg_len = strlen("pid");
++              }
++              else {
++                      seg_name = parent->d_name.name;
++                      seg_len = parent->d_name.len;
++              }
++
++              len -= seg_len + 1;
++              name[len] = '/';
++              strncpy(&name[len + 1], seg_name, seg_len);
++              parent = parent->d_parent;
++      }
++      strncpy(name, root, strlen(root));
++      return(name);
++}
++
++struct dentry_operations hppfs_dentry_ops = {
++};
++
++static int file_removed(struct dentry *dentry, const char *file)
++{
++      char *host_file;
++      int extra, fd;
++
++      extra = 0;
++      if(file != NULL) extra += strlen(file) + 1;
++
++      host_file = dentry_name(dentry, extra + strlen("/remove"));
++      if(host_file == NULL){
++              printk("file_removed : allocation failed\n");
++              return(-ENOMEM);
++      }
++
++      if(file != NULL){
++              strcat(host_file, "/");
++              strcat(host_file, file);
++      }
++      strcat(host_file, "/remove");
++
++      fd = os_open_file(host_file, of_read(OPENFLAGS()), 0);
++      kfree(host_file);
++      if(fd >= 0){
++              os_close_file(fd);
++              return(1);
++      }
++      return(0);
++}
++
++static struct dentry *hppfs_lookup(struct inode *ino, struct dentry *dentry)
++{
++      struct dentry *proc_dentry;
++      struct inode *inode;
++      int err, deleted;
++
++      deleted = file_removed(dentry, NULL);
++      if(deleted < 0)
++              return(ERR_PTR(deleted));
++      else if(deleted)
++              return(ERR_PTR(-ENOENT));
++
++      proc_dentry = lookup_hash(&dentry->d_name, ino->u.hppfs_i.proc_dentry);
++      if(IS_ERR(proc_dentry))
++              return(proc_dentry);
++
++      inode = get_inode(ino->i_sb, proc_dentry, &err);
++      if(err != 0) 
++              return(ERR_PTR(err));
++
++      d_add(dentry, inode);
++      dentry->d_op = &hppfs_dentry_ops;
++      return(NULL);
++}
++
++static struct inode_operations hppfs_file_iops = {
++};
++
++static struct inode_operations hppfs_dir_iops = {
++      .lookup         = hppfs_lookup,
++};
++
++static ssize_t read_proc(struct file *file, char *buf, ssize_t count, 
++                       loff_t *ppos, int is_user)
++{
++      ssize_t (*read)(struct file *, char *, size_t, loff_t *);
++      ssize_t n;
++
++      read = file->f_dentry->d_inode->i_fop->read;
++      if(read == NULL)
++              return(-EOPNOTSUPP);
++
++      if(!is_user)
++              set_fs(KERNEL_DS);
++              
++      n = (*read)(file, buf, count, &file->f_pos);
++
++      if(!is_user)
++              set_fs(USER_DS);
++
++      if(ppos) *ppos = file->f_pos;
++      return(n);
++}
++
++static ssize_t hppfs_read_file(int fd, char *buf, ssize_t count)
++{
++      ssize_t n;
++      int cur, err;
++      char *new_buf;
++
++      n = -ENOMEM;
++      new_buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
++      if(new_buf == NULL){
++              printk("hppfs_read_file : kmalloc failed\n");
++              goto out;
++      }
++      n = 0;
++      while(count > 0){
++              cur = min_t(ssize_t, count, PAGE_SIZE);
++              err = os_read_file(fd, new_buf, cur);
++              if(err < 0){
++                      printk("hppfs_read : read failed, err = %d\n", -err);
++                      n = err;
++                      goto out_free;
++              }
++              else if(err == 0)
++                      break;
++
++              if(copy_to_user(buf, new_buf, err)){
++                      n = -EFAULT;
++                      goto out_free;
++              }
++              n += err;
++              count -= err;
++      }
++ out_free:
++      kfree(new_buf);
++ out:
++      return(n);
++}
++
++static ssize_t hppfs_read(struct file *file, char *buf, size_t count, 
++                        loff_t *ppos)
++{
++      struct hppfs_private *hppfs = file->private_data;
++      struct hppfs_data *data;
++      loff_t off;
++      int err;
++
++      if(hppfs->contents != NULL){
++              if(*ppos >= hppfs->len) return(0);
++
++              data = hppfs->contents;
++              off = *ppos;
++              while(off >= sizeof(data->contents)){
++                      data = list_entry(data->list.next, struct hppfs_data,
++                                        list);
++                      off -= sizeof(data->contents);
++              }
++
++              if(off + count > hppfs->len)
++                      count = hppfs->len - off;
++              copy_to_user(buf, &data->contents[off], count);
++              *ppos += count;
++      }
++      else if(hppfs->host_fd != -1){
++              err = os_seek_file(hppfs->host_fd, *ppos);
++              if(err < 0){
++                      printk("hppfs_read : seek failed, err = %d\n", -err);
++                      return(err);
++              }
++              count = hppfs_read_file(hppfs->host_fd, buf, count);
++              if(count > 0)
++                      *ppos += count;
++      }
++      else count = read_proc(&hppfs->proc_file, buf, count, ppos, 1);
++
++      return(count);
++}
++
++static ssize_t hppfs_write(struct file *file, const char *buf, size_t len, 
++                         loff_t *ppos)
++{
++      struct hppfs_private *data = file->private_data;
++      struct file *proc_file = &data->proc_file;
++      ssize_t (*write)(struct file *, const char *, size_t, loff_t *);
++      int err;
++
++      write = proc_file->f_dentry->d_inode->i_fop->write;
++      if(write == NULL)
++              return(-EOPNOTSUPP);
++
++      proc_file->f_pos = file->f_pos;
++      err = (*write)(proc_file, buf, len, &proc_file->f_pos);
++      file->f_pos = proc_file->f_pos;
++
++      return(err);
++}
++
++static int open_host_sock(char *host_file, int *filter_out)
++{
++      char *end;
++      int fd;
++
++      end = &host_file[strlen(host_file)];
++      strcpy(end, "/rw");
++      *filter_out = 1;
++      fd = os_connect_socket(host_file);
++      if(fd >= 0)
++              return(fd);
++
++      strcpy(end, "/r");
++      *filter_out = 0;
++      fd = os_connect_socket(host_file);
++      return(fd);
++}
++
++static void free_contents(struct hppfs_data *head)
++{
++      struct hppfs_data *data;
++      struct list_head *ele, *next;
++
++      if(head == NULL) return;
++
++      list_for_each_safe(ele, next, &head->list){
++              data = list_entry(ele, struct hppfs_data, list);
++              kfree(data);
++      }
++      kfree(head);
++}
++
++static struct hppfs_data *hppfs_get_data(int fd, int filter, 
++                                       struct file *proc_file, 
++                                       struct file *hppfs_file, 
++                                       loff_t *size_out)
++{
++      struct hppfs_data *data, *new, *head;
++      int n, err;
++
++      err = -ENOMEM;
++      data = kmalloc(sizeof(*data), GFP_KERNEL);
++      if(data == NULL){
++              printk("hppfs_get_data : head allocation failed\n");
++              goto failed;
++      }
++
++      INIT_LIST_HEAD(&data->list);
++
++      head = data;
++      *size_out = 0;
++
++      if(filter){
++              while((n = read_proc(proc_file, data->contents,
++                                   sizeof(data->contents), NULL, 0)) > 0) {
++                      err = os_write_file(fd, data->contents, n);
++                      if(err != n)
++                              printk("hppfs_get_data : failed to write out "
++                                     "%d bytes, err = %d\n", n, -err);
++              }
++              err = os_shutdown_socket(fd, 0, 1);
++              if(err < 0){
++                      printk("hppfs_get_data : failed to shut down "
++                             "socket\n");
++                      goto failed_free;
++              }
++      }
++      while(1){
++              n = os_read_file(fd, data->contents, sizeof(data->contents));
++              if(n < 0){
++                      err = n;
++                      printk("hppfs_get_data : read failed, err = %d\n", -n);
++                      goto failed_free;
++              }
++              else if(n == 0)
++                      break;
++
++              *size_out += n;
++
++              if(n < sizeof(data->contents))
++                      break;
++
++              new = kmalloc(sizeof(*data), GFP_KERNEL);
++              if(new == 0){
++                      printk("hppfs_get_data : data allocation failed\n");
++                      err = -ENOMEM;
++                      goto failed_free;
++              }
++      
++              INIT_LIST_HEAD(&new->list);
++              list_add(&new->list, &data->list);
++              data = new;
++      }
++      return(head);
++
++ failed_free:
++      free_contents(head);
++ failed:              
++      return(ERR_PTR(err));
++}
++
++static struct hppfs_private *hppfs_data(void)
++{
++      struct hppfs_private *data;
++
++      data = kmalloc(sizeof(*data), GFP_KERNEL);
++      if(data == NULL)
++              return(data);
++
++      *data = ((struct hppfs_private ) { .host_fd             = -1,
++                                         .len                 = -1,
++                                         .contents            = NULL } );
++      return(data);
++}
++
++static int hppfs_open(struct inode *inode, struct file *file)
++{
++      struct hppfs_private *data;
++      struct dentry *proc_dentry;
++      char *host_file;
++      int err, fd, type, filter;
++
++      err = -ENOMEM;
++      data = hppfs_data();
++      if(data == NULL)
++              goto out;
++
++      host_file = dentry_name(file->f_dentry, strlen("/rw"));
++      if(host_file == NULL)
++              goto out_free2;
++
++      proc_dentry = inode->u.hppfs_i.proc_dentry;
++      err = init_private_file(&data->proc_file, proc_dentry, file->f_mode);
++      if(err)
++              goto out_free1;
++
++      type = os_file_type(host_file);
++      if(type == OS_TYPE_FILE){
++              fd = os_open_file(host_file, of_read(OPENFLAGS()), 0);
++              if(fd >= 0) 
++                      data->host_fd = fd;
++              else printk("hppfs_open : failed to open '%s', err = %d\n",
++                          host_file, -fd);
++
++              data->contents = NULL;
++      }
++      else if(type == OS_TYPE_DIR){
++              fd = open_host_sock(host_file, &filter);
++              if(fd >= 0){
++                      data->contents = hppfs_get_data(fd, filter, 
++                                                      &data->proc_file, 
++                                                      file, &data->len);
++                      if(!IS_ERR(data->contents))
++                              data->host_fd = fd;
++              }
++              else printk("hppfs_open : failed to open a socket in "
++                          "'%s', err = %d\n", host_file, -fd);
++      }
++      kfree(host_file);
++
++      file->private_data = data;
++      return(0);
++
++ out_free1:
++      kfree(host_file);
++ out_free2:
++      free_contents(data->contents);
++      kfree(data);
++ out:
++      return(err);
++}
++
++static int hppfs_dir_open(struct inode *inode, struct file *file)
++{
++      struct hppfs_private *data;
++      struct dentry *proc_dentry;
++      int err;
++
++      err = -ENOMEM;
++      data = hppfs_data();
++      if(data == NULL)
++              goto out;
++
++      proc_dentry = inode->u.hppfs_i.proc_dentry;
++      err = init_private_file(&data->proc_file, proc_dentry, file->f_mode);
++      if(err)
++              goto out_free;
++
++      file->private_data = data;
++      return(0);
++
++ out_free:
++      kfree(data);
++ out:
++      return(err);
++}
++
++static loff_t hppfs_llseek(struct file *file, loff_t off, int where)
++{
++      struct hppfs_private *data = file->private_data;
++      struct file *proc_file = &data->proc_file;
++      loff_t (*llseek)(struct file *, loff_t, int);
++      loff_t ret;
++
++      llseek = proc_file->f_dentry->d_inode->i_fop->llseek;
++      if(llseek != NULL){
++              ret = (*llseek)(proc_file, off, where);
++              if(ret < 0)
++                      return(ret);
++      }
++
++      return(default_llseek(file, off, where));
++}
++
++struct hppfs_dirent {
++      void *vfs_dirent;
++      filldir_t filldir;
++      struct dentry *dentry;
++};
++
++static int hppfs_filldir(void *d, const char *name, int size, 
++                       loff_t offset, ino_t inode, unsigned int type)
++{
++      struct hppfs_dirent *dirent = d;
++
++      if(file_removed(dirent->dentry, name))
++              return(0);
++
++      return((*dirent->filldir)(dirent->vfs_dirent, name, size, offset, 
++                                inode, type));
++}
++
++static int hppfs_readdir(struct file *file, void *ent, filldir_t filldir)
++{
++      struct hppfs_private *data = file->private_data;
++      struct file *proc_file = &data->proc_file;
++      int (*readdir)(struct file *, void *, filldir_t);
++      struct hppfs_dirent dirent = ((struct hppfs_dirent)
++                                    { .vfs_dirent     = ent,
++                                      .filldir        = filldir,
++                                      .dentry         = file->f_dentry } );
++      int err;
++
++      readdir = proc_file->f_dentry->d_inode->i_fop->readdir;
++      if(readdir == NULL)
++              return(-EOPNOTSUPP);
++
++      proc_file->f_pos = file->f_pos;
++      err = (*readdir)(proc_file, &dirent, hppfs_filldir);
++      file->f_pos = proc_file->f_pos;
++
++      return(err);
++}
++
++static int hppfs_fsync(struct file *file, struct dentry *dentry, int datasync)
++{
++      return(0);
++}
++
++static struct file_operations hppfs_file_fops = {
++      .owner          = NULL,
++      .llseek         = hppfs_llseek,
++      .read           = hppfs_read,
++      .write          = hppfs_write,
++      .open           = hppfs_open,
++};
++
++static struct file_operations hppfs_dir_fops = {
++      .owner          = NULL,
++      .readdir        = hppfs_readdir,
++      .open           = hppfs_dir_open,
++      .fsync          = hppfs_fsync,
++};
++
++static int hppfs_statfs(struct super_block *sb, struct statfs *sf)
++{
++      sf->f_blocks = 0;
++      sf->f_bfree = 0;
++      sf->f_bavail = 0;
++      sf->f_files = 0;
++      sf->f_ffree = 0;
++      sf->f_type = HPPFS_SUPER_MAGIC;
++      return(0);
++}
++
++static struct super_operations hppfs_sbops = { 
++      .put_inode      = force_delete,
++      .delete_inode   = NULL,
++      .statfs         = hppfs_statfs,
++};
++
++static int hppfs_readlink(struct dentry *dentry, char *buffer, int buflen)
++{
++      struct file proc_file;
++      struct dentry *proc_dentry;
++      int (*readlink)(struct dentry *, char *, int);
++      int err, n;
++
++      proc_dentry = dentry->d_inode->u.hppfs_i.proc_dentry;
++      err = init_private_file(&proc_file, proc_dentry, FMODE_READ);
++      if(err) 
++              return(err);
++
++      readlink = proc_dentry->d_inode->i_op->readlink;
++      if(readlink == NULL)
++              return(-EOPNOTSUPP);
++      n = (*readlink)(proc_dentry, buffer, buflen);
++
++      if(proc_file.f_op->release)
++              (*proc_file.f_op->release)(proc_dentry->d_inode, &proc_file);
++      
++      return(n);
++}
++
++static int hppfs_follow_link(struct dentry *dentry, struct nameidata *nd)
++{
++      struct file proc_file;
++      struct dentry *proc_dentry;
++      int (*follow_link)(struct dentry *, struct nameidata *);
++      int err, n;
++
++      proc_dentry = dentry->d_inode->u.hppfs_i.proc_dentry;
++      err = init_private_file(&proc_file, proc_dentry, FMODE_READ);
++      if(err) 
++              return(err);
++
++      follow_link = proc_dentry->d_inode->i_op->follow_link;
++      if(follow_link == NULL)
++              return(-EOPNOTSUPP);
++      n = (*follow_link)(proc_dentry, nd);
++
++      if(proc_file.f_op->release)
++              (*proc_file.f_op->release)(proc_dentry->d_inode, &proc_file);
++      
++      return(n);
++}
++
++static struct inode_operations hppfs_link_iops = {
++      .readlink       = hppfs_readlink,
++      .follow_link    = hppfs_follow_link,
++};
++
++static void read_inode(struct inode *ino)
++{
++      struct inode *proc_ino;
++
++      proc_ino = ino->u.hppfs_i.proc_dentry->d_inode;
++      ino->i_uid = proc_ino->i_uid;
++      ino->i_gid = proc_ino->i_gid;
++      ino->i_atime = proc_ino->i_atime;
++      ino->i_mtime = proc_ino->i_mtime;
++      ino->i_ctime = proc_ino->i_ctime;
++      ino->i_ino = proc_ino->i_ino;
++      ino->i_dev = proc_ino->i_dev;
++      ino->i_mode = proc_ino->i_mode;
++      ino->i_nlink = proc_ino->i_nlink;
++      ino->i_size = proc_ino->i_size;
++      ino->i_blksize = proc_ino->i_blksize;
++      ino->i_blocks = proc_ino->i_blocks;
++}
++
++static struct inode *get_inode(struct super_block *sb, struct dentry *dentry,
++                             int *error)
++{
++      struct inode *inode;
++      int err = -ENOMEM;
++
++      inode = new_inode(sb);
++      if(inode == NULL) 
++              goto out;
++
++      insert_inode_hash(inode);
++      if(S_ISDIR(dentry->d_inode->i_mode)){
++              inode->i_op = &hppfs_dir_iops;
++              inode->i_fop = &hppfs_dir_fops;
++      }
++      else if(S_ISLNK(dentry->d_inode->i_mode)){
++              inode->i_op = &hppfs_link_iops;
++              inode->i_fop = &hppfs_file_fops;
++      }
++      else {
++              inode->i_op = &hppfs_file_iops;
++              inode->i_fop = &hppfs_file_fops;
++      }
++
++      inode->i_sb = sb;
++      inode->u.hppfs_i.proc_dentry = dentry;
++
++      read_inode(inode);
++      err = 0;
++
++      if(error) *error = err;
++      return(inode);
++ out:
++      if(error) *error = err;
++      return(NULL);
++}
++
++static struct super_block *hppfs_read_super(struct super_block *sb, void *d, 
++                                          int silent)
++{
++      struct inode *root_inode;
++      struct file_system_type *procfs;
++      struct super_block *proc_sb;
++
++      procfs = get_fs_type("proc");
++      if(procfs == NULL) 
++              goto out;
++
++      if(list_empty(&procfs->fs_supers))
++              goto out;
++
++      proc_sb = list_entry(procfs->fs_supers.next, struct super_block,
++                           s_instances);
++      
++      sb->s_blocksize = 1024;
++      sb->s_blocksize_bits = 10;
++      sb->s_magic = HPPFS_SUPER_MAGIC;
++      sb->s_op = &hppfs_sbops;
++
++      dget(proc_sb->s_root);
++      root_inode = get_inode(sb, proc_sb->s_root, NULL);
++      if(root_inode == NULL)
++              goto out_dput;
++
++      sb->s_root = d_alloc_root(root_inode);
++      if(sb->s_root == NULL)
++              goto out_put;
++
++      return(sb);
++
++ out_put:
++      iput(root_inode);
++ out_dput:
++      dput(proc_sb->s_root);
++ out:
++      return(NULL);
++}
++
++DECLARE_FSTYPE(hppfs_type, "hppfs", hppfs_read_super, 0);
++
++static int __init init_hppfs(void)
++{
++      return(register_filesystem(&hppfs_type));
++}
++
++static void __exit exit_hppfs(void)
++{
++      unregister_filesystem(&hppfs_type);
++}
++
++module_init(init_hppfs)
++module_exit(exit_hppfs)
++MODULE_LICENSE("GPL");
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/fs/hppfs/Makefile
+===================================================================
+--- linux-2.4.29.orig/arch/um/fs/hppfs/Makefile        1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/fs/hppfs/Makefile     2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,10 @@
++O_TARGET := hppfs.o
++obj-y = hppfs_kern.o #hppfs_user.o
++obj-m = $(O_TARGET)
++
++CFLAGS_hppfs_kern.o := $(CFLAGS)
++#CFLAGS_hppfs_user.o := $(USER_CFLAGS)
++
++override CFLAGS =  
++
++include $(TOPDIR)/Rules.make
+Index: linux-2.4.29/arch/um/fs/Makefile
+===================================================================
+--- linux-2.4.29.orig/arch/um/fs/Makefile      1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/fs/Makefile   2005-05-03 22:28:14.291437584 +0300
+@@ -0,0 +1,23 @@
++# 
++# Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
++# Licensed under the GPL
++#
++
++O_TARGET := built-in.o
++
++subdir-y =
++subdir-m =
++
++subdir-$(CONFIG_HOSTFS) += hostfs
++subdir-$(CONFIG_HPPFS) += hppfs
++
++obj-y += $(join $(subdir-y),$(subdir-y:%=/%.o))
++obj-m += $(join $(subdir-m),$(subdir-m:%=/%.o))
++
++include $(TOPDIR)/Rules.make
++
++dep:
++
++clean:
++
++archmrproper:
+Index: linux-2.4.29/arch/um/include/2_5compat.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/2_5compat.h      1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/2_5compat.h   2005-05-03 22:28:14.292437432 +0300
+@@ -0,0 +1,33 @@
++/* 
++ * Copyright (C) 2001 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __2_5_COMPAT_H__
++#define __2_5_COMPAT_H__
++
++#include "linux/version.h"
++
++#define INIT_ELV(queue, elv) elevator_init(elv, ELV_NOOP)
++
++#define ELV_NOOP ELEVATOR_NOOP
++
++#define INIT_HARDSECT(arr, maj, sizes) arr[maj] = sizes
++
++#define IS_WRITE(req) ((req)->cmd == WRITE)
++
++#define SET_PRI(task) \
++      do { (task)->nice = 20; (task)->counter = -100; } while(0);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/aio.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/aio.h    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/aio.h 2005-05-03 22:28:14.293437280 +0300
+@@ -0,0 +1,36 @@
++/* 
++ * Copyright (C) 2004 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef AIO_H__
++#define AIO_H__
++
++enum aio_type { AIO_READ, AIO_WRITE, AIO_MMAP };
++
++struct aio_thread_reply {
++      void *data;
++      int err;
++};
++
++struct aio_context {
++      int reply_fd;
++};
++
++#define INIT_AIO_CONTEXT { .reply_fd  = -1 }
++
++extern int submit_aio(enum aio_type type, int fd, char *buf, int len, 
++                    unsigned long long offset, int reply_fd, void *data);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/chan_kern.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/chan_kern.h      1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/chan_kern.h   2005-05-03 22:28:14.294437128 +0300
+@@ -0,0 +1,56 @@
++/* 
++ * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __CHAN_KERN_H__
++#define __CHAN_KERN_H__
++
++#include "linux/tty.h"
++#include "linux/list.h"
++#include "chan_user.h"
++
++struct chan {
++      struct list_head list;
++      char *dev;
++      unsigned int primary:1;
++      unsigned int input:1;
++      unsigned int output:1;
++      unsigned int opened:1;
++      int fd;
++      enum chan_init_pri pri;
++      struct chan_ops *ops;
++      void *data;
++};
++
++extern void chan_interrupt(struct list_head *chans, struct tq_struct *task,
++                         struct tty_struct *tty, int irq, void *dev);
++extern int parse_chan_pair(char *str, struct list_head *chans, int pri, 
++                         int device, struct chan_opts *opts);
++extern int open_chan(struct list_head *chans);
++extern int write_chan(struct list_head *chans, const char *buf, int len,
++                           int write_irq);
++extern int console_write_chan(struct list_head *chans, const char *buf, 
++                            int len);
++extern void close_chan(struct list_head *chans);
++extern void chan_enable_winch(struct list_head *chans, void *line);
++extern void enable_chan(struct list_head *chans, void *data);
++extern int chan_window_size(struct list_head *chans, 
++                           unsigned short *rows_out, 
++                           unsigned short *cols_out);
++extern int chan_out_fd(struct list_head *chans);
++extern int chan_config_string(struct list_head *chans, char *str, int size,
++                            char **error_out);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/chan_user.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/chan_user.h      1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/chan_user.h   2005-05-03 22:28:14.295436976 +0300
+@@ -0,0 +1,66 @@
++/* 
++ * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __CHAN_USER_H__
++#define __CHAN_USER_H__
++
++#include "init.h"
++
++struct chan_opts {
++      void (*announce)(char *dev_name, int dev);
++      char *xterm_title;
++      int raw;
++      unsigned long tramp_stack;
++      int in_kernel;
++};
++
++enum chan_init_pri { INIT_STATIC, INIT_ALL, INIT_ONE };
++
++struct chan_ops {
++      char *type;
++      void *(*init)(char *, int, struct chan_opts *);
++      int (*open)(int, int, int, void *, char **);
++      void (*close)(int, void *);
++      int (*read)(int, char *, void *);
++      int (*write)(int, const char *, int, void *);
++      int (*console_write)(int, const char *, int, void *);
++      int (*window_size)(int, void *, unsigned short *, unsigned short *);
++      void (*free)(void *);
++      int winch;
++};
++
++extern struct chan_ops fd_ops, null_ops, port_ops, pts_ops, pty_ops, tty_ops,
++      xterm_ops;
++
++extern void generic_close(int fd, void *unused);
++extern int generic_read(int fd, char *c_out, void *unused);
++extern int generic_write(int fd, const char *buf, int n, void *unused);
++extern int generic_console_write(int fd, const char *buf, int n, void *state);
++extern int generic_window_size(int fd, void *unused, unsigned short *rows_out,
++                             unsigned short *cols_out);
++extern void generic_free(void *data);
++
++extern void register_winch(int fd, void *device_data);
++extern void register_winch_irq(int fd, int tty_fd, int pid, void *line);
++
++#define __channel_help(fn, prefix) \
++__uml_help(fn, prefix "[0-9]*=<channel description>\n" \
++"    Attach a console or serial line to a host channel.  See\n" \
++"    http://user-mode-linux.sourceforge.net/input.html for a complete\n" \
++"    description of this switch.\n\n" \
++);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/choose-mode.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/choose-mode.h    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/choose-mode.h 2005-05-03 22:28:14.295436976 +0300
+@@ -0,0 +1,35 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __CHOOSE_MODE_H__
++#define __CHOOSE_MODE_H__
++
++#include "uml-config.h"
++
++#if defined(UML_CONFIG_MODE_TT) && defined(UML_CONFIG_MODE_SKAS)
++#define CHOOSE_MODE(tt, skas) (mode_tt ? (tt) : (skas))
++
++#elif defined(UML_CONFIG_MODE_SKAS)
++#define CHOOSE_MODE(tt, skas) (skas)
++
++#elif defined(UML_CONFIG_MODE_TT)
++#define CHOOSE_MODE(tt, skas) (tt)
++#endif
++
++#define CHOOSE_MODE_PROC(tt, skas, args...) \
++      CHOOSE_MODE(tt(args), skas(args))
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/filehandle.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/filehandle.h     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/filehandle.h  2005-05-03 22:28:14.296436824 +0300
+@@ -0,0 +1,51 @@
++/* 
++ * Copyright (C) 2004 Jeff Dike (jdike@addtoit.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __FILEHANDLE_H__
++#define __FILEHANDLE_H__
++
++#include "linux/list.h"
++#include "linux/fs.h"
++#include "os.h"
++
++struct file_handle {
++      struct list_head list;
++      int fd;
++      char *(*get_name)(struct inode *);
++      struct inode *inode;
++      struct openflags flags;
++};
++
++extern struct file_handle bad_filehandle;
++
++extern int open_file(char *name, struct openflags flags, int mode);
++extern void *open_dir(char *file);
++extern int open_filehandle(char *name, struct openflags flags, int mode, 
++                         struct file_handle *fh);
++extern int read_file(struct file_handle *fh, unsigned long long offset, 
++                   char *buf, int len);
++extern int write_file(struct file_handle *fh, unsigned long long offset, 
++                    const char *buf, int len);
++extern int truncate_file(struct file_handle *fh, unsigned long long size);
++extern int close_file(struct file_handle *fh);
++extern void not_reclaimable(struct file_handle *fh);
++extern void is_reclaimable(struct file_handle *fh, 
++                         char *(name_proc)(struct inode *),
++                         struct inode *inode);
++extern int filehandle_fd(struct file_handle *fh);
++extern int make_pipe(struct file_handle *fhs);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/frame.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/frame.h  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/frame.h       2005-05-03 22:28:14.297436672 +0300
+@@ -0,0 +1,53 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __FRAME_H_
++#define __FRAME_H_
++
++#include "sysdep/frame.h"
++
++struct frame_common {
++      void *data;
++      int len;
++      int sig_index;
++      int sr_index;
++      int sr_relative;
++      int sp_index;
++      struct arch_frame_data arch;
++};
++
++struct sc_frame {
++      struct frame_common common;
++      int sc_index;
++};
++
++extern struct sc_frame signal_frame_sc;
++
++extern struct sc_frame signal_frame_sc_sr;
++
++struct si_frame {
++      struct frame_common common;
++      int sip_index;
++      int si_index;
++      int ucp_index;
++      int uc_index;
++};
++
++extern struct si_frame signal_frame_si;
++
++extern void capture_signal_stack(void);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/frame_kern.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/frame_kern.h     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/frame_kern.h  2005-05-03 22:28:14.298436520 +0300
+@@ -0,0 +1,34 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __FRAME_KERN_H_
++#define __FRAME_KERN_H_
++
++#include "frame.h"
++#include "sysdep/frame_kern.h"
++
++extern int setup_signal_stack_sc(unsigned long stack_top, int sig, 
++                               unsigned long handler,
++                               void (*restorer)(void), 
++                               struct pt_regs *regs, 
++                               sigset_t *mask);
++extern int setup_signal_stack_si(unsigned long stack_top, int sig, 
++                               unsigned long handler, 
++                               void (*restorer)(void), 
++                               struct pt_regs *regs, siginfo_t *info, 
++                               sigset_t *mask);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/frame_user.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/frame_user.h     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/frame_user.h  2005-05-03 22:28:14.299436368 +0300
+@@ -0,0 +1,23 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __FRAME_USER_H_
++#define __FRAME_USER_H_
++
++#include "sysdep/frame_user.h"
++#include "frame.h"
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/helper.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/helper.h 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/helper.h      2005-05-03 22:28:14.300436216 +0300
+@@ -0,0 +1,27 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __HELPER_H__
++#define __HELPER_H__
++
++extern int run_helper(void (*pre_exec)(void *), void *pre_data, char **argv,
++                    unsigned long *stack_out);
++extern int run_helper_thread(int (*proc)(void *), void *arg, 
++                           unsigned int flags, unsigned long *stack_out,
++                           int stack_order);
++extern int helper_wait(int pid);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/init.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/init.h   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/init.h        2005-05-03 22:28:14.301436064 +0300
+@@ -0,0 +1,124 @@
++#ifndef _LINUX_UML_INIT_H
++#define _LINUX_UML_INIT_H
++
++/* These macros are used to mark some functions or
++ * initialized data (doesn't apply to uninitialized data)
++ * as `initialization' functions. The kernel can take this
++ * as hint that the function is used only during the initialization
++ * phase and free up used memory resources after
++ *
++ * Usage:
++ * For functions:
++ *
++ * You should add __init immediately before the function name, like:
++ *
++ * static void __init initme(int x, int y)
++ * {
++ *    extern int z; z = x * y;
++ * }
++ *
++ * If the function has a prototype somewhere, you can also add
++ * __init between closing brace of the prototype and semicolon:
++ *
++ * extern int initialize_foobar_device(int, int, int) __init;
++ *
++ * For initialized data:
++ * You should insert __initdata between the variable name and equal
++ * sign followed by value, e.g.:
++ *
++ * static int init_variable __initdata = 0;
++ * static char linux_logo[] __initdata = { 0x32, 0x36, ... };
++ *
++ * Don't forget to initialize data not at file scope, i.e. within a function,
++ * as gcc otherwise puts the data into the bss section and not into the init
++ * section.
++ *
++ * Also note, that this data cannot be "const".
++ */
++
++#ifndef _LINUX_INIT_H
++typedef int (*initcall_t)(void);
++typedef void (*exitcall_t)(void);
++
++#define __init          __attribute__ ((__section__ (".text.init")))
++#define __exit          __attribute__ ((unused, __section__(".text.exit")))
++#define __initdata      __attribute__ ((__section__ (".data.init")))
++
++#endif
++
++#ifndef MODULE
++struct uml_param {
++        const char *str;
++        int (*setup_func)(char *, int *);
++};
++
++extern initcall_t __uml_initcall_start, __uml_initcall_end;
++extern initcall_t __uml_postsetup_start, __uml_postsetup_end;
++extern const char *__uml_help_start, *__uml_help_end;
++#endif
++
++#define __uml_initcall(fn)                                            \
++      static initcall_t __uml_initcall_##fn __uml_init_call = fn
++
++#define __uml_exitcall(fn)                                            \
++      static exitcall_t __uml_exitcall_##fn __uml_exit_call = fn
++
++extern struct uml_param __uml_setup_start, __uml_setup_end;
++
++#define __uml_postsetup(fn)                                           \
++      static initcall_t __uml_postsetup_##fn __uml_postsetup_call = fn
++
++#define __non_empty_string(dummyname,string)                          \
++      struct __uml_non_empty_string_struct_##dummyname                \
++      {                                                               \
++              char _string[sizeof(string)-2];                         \
++      }
++
++#ifndef MODULE
++#define __uml_setup(str, fn, help...)                                 \
++      __non_empty_string(fn ##_setup, str);                           \
++      __uml_help(fn, help);                                           \
++      static char __uml_setup_str_##fn[] __initdata = str;            \
++      static struct uml_param __uml_setup_##fn __uml_init_setup = { __uml_setup_str_##fn, fn }
++#else
++#define __uml_setup(str, fn, help...)                                 \
++
++#endif
++
++#define __uml_help(fn, help...)                                               \
++      __non_empty_string(fn ##__help, help);                          \
++      static char __uml_help_str_##fn[] __initdata = help;            \
++      static const char *__uml_help_##fn __uml_setup_help = __uml_help_str_##fn
++
++/*
++ * Mark functions and data as being only used at initialization
++ * or exit time.
++ */
++#define __uml_init_setup      __attribute__ ((unused,__section__ (".uml.setup.init")))
++#define __uml_setup_help      __attribute__ ((unused,__section__ (".uml.help.init")))
++#define __uml_init_call               __attribute__ ((unused,__section__ (".uml.initcall.init")))
++#define __uml_postsetup_call  __attribute__ ((unused,__section__ (".uml.postsetup.init")))
++#define __uml_exit_call               __attribute__ ((unused,__section__ (".uml.exitcall.exit")))
++
++#ifndef __KERNEL__
++
++#define __initcall(fn) static initcall_t __initcall_##fn __init_call = fn
++#define __exitcall(fn) static exitcall_t __exitcall_##fn __exit_call = fn
++
++#define __init_call __attribute__ ((unused,__section__ (".initcall.init")))
++#define __exit_call __attribute__ ((unused,__section__ (".exitcall.exit")))
++
++#endif
++
++#endif /* _LINUX_UML_INIT_H */
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/initrd.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/initrd.h 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/initrd.h      2005-05-03 22:28:14.301436064 +0300
+@@ -0,0 +1,22 @@
++/*
++ * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __INITRD_USER_H__
++#define __INITRD_USER_H__
++
++extern int load_initrd(char *filename, void *buf, int size);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/irq_kern.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/irq_kern.h       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/irq_kern.h    2005-05-03 22:28:14.303435760 +0300
+@@ -0,0 +1,27 @@
++/* 
++ * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __IRQ_KERN_H__
++#define __IRQ_KERN_H__
++
++#include "linux/interrupt.h"
++
++extern int um_request_irq(unsigned int irq, int fd, int type,
++                        void (*handler)(int, void *, struct pt_regs *),
++                        unsigned long irqflags,  const char * devname,
++                        void *dev_id);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/irq_user.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/irq_user.h       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/irq_user.h    2005-05-03 22:28:14.304435608 +0300
+@@ -0,0 +1,36 @@
++/* 
++ * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __IRQ_USER_H__
++#define __IRQ_USER_H__
++
++enum { IRQ_READ, IRQ_WRITE };
++
++extern void sigio_handler(int sig, union uml_pt_regs *regs);
++extern int activate_fd(int irq, int fd, int type, void *dev_id);
++extern void free_irq_by_irq_and_dev(int irq, void *dev_id);
++extern void free_irq_by_fd(int fd);
++extern void reactivate_fd(int fd, int irqnum);
++extern void deactivate_fd(int fd, int irqnum);
++extern int deactivate_all_fds(void);
++extern void forward_interrupts(int pid);
++extern void init_irq_signals(int on_sigstack);
++extern void forward_ipi(int fd, int pid);
++extern void free_irq_later(int irq, void *dev_id);
++extern int activate_ipi(int fd, int pid);
++extern unsigned long irq_lock(void);
++extern void irq_unlock(unsigned long flags);
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/kern.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/kern.h   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/kern.h        2005-05-03 22:28:14.304435608 +0300
+@@ -0,0 +1,48 @@
++/* 
++ * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __KERN_H__
++#define __KERN_H__
++
++/* These are all user-mode things which are convenient to call directly
++ * from kernel code and for which writing a wrapper is too much of a pain.
++ * The regular include files can't be included because this file is included
++ * only into kernel code, and user-space includes conflict with kernel
++ * includes.
++ */
++
++extern int errno;
++
++extern int clone(int (*proc)(void *), void *sp, int flags, void *data);
++extern int sleep(int);
++extern int printf(char *fmt, ...);
++extern char *strerror(int errnum);
++extern char *ptsname(int __fd);
++extern int munmap(void *, int);
++extern void *sbrk(int increment);
++extern void *malloc(int size);
++extern void perror(char *err);
++extern int kill(int pid, int sig);
++extern int getuid(void);
++extern int pause(void);
++extern int write(int, const void *, int);
++extern int exit(int);
++extern int close(int);
++extern int read(unsigned int, char *, int);
++extern int pipe(int *);
++extern int sched_yield(void);
++extern int ptrace(int op, int pid, long addr, long data);
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/kern_util.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/kern_util.h      1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/kern_util.h   2005-05-03 22:28:14.306435304 +0300
+@@ -0,0 +1,123 @@
++/* 
++ * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __KERN_UTIL_H__
++#define __KERN_UTIL_H__
++
++#include "sysdep/ptrace.h"
++
++extern int ncpus;
++extern char *linux_prog;
++extern char *gdb_init;
++extern int kmalloc_ok;
++extern int timer_irq_inited;
++extern int jail;
++extern int nsyscalls;
++
++#define UML_ROUND_DOWN(addr) ((void *)(((unsigned long) addr) & PAGE_MASK))
++#define UML_ROUND_UP(addr) \
++      UML_ROUND_DOWN(((unsigned long) addr) + PAGE_SIZE - 1)
++
++extern int kernel_fork(unsigned long flags, int (*fn)(void *), void * arg);
++extern unsigned long stack_sp(unsigned long page);
++extern int kernel_thread_proc(void *data);
++extern void syscall_segv(int sig);
++extern int current_pid(void);
++extern unsigned long alloc_stack(int order, int atomic);
++extern int do_signal(int error);
++extern int is_stack_fault(unsigned long sp);
++extern unsigned long segv(unsigned long address, unsigned long ip, 
++                        int is_write, int is_user, void *sc);
++extern unsigned long handle_page_fault(unsigned long address, unsigned long ip,
++                                     int is_write, int is_user, 
++                                     int *code_out);
++extern void syscall_ready(void);
++extern int segv_syscall(void);
++extern void kern_finish_exec(void *task, int new_pid, unsigned long stack);
++extern int page_size(void);
++extern int page_mask(void);
++extern int need_finish_fork(void);
++extern void free_stack(unsigned long stack, int order);
++extern void add_input_request(int op, void (*proc)(int), void *arg);
++extern int sys_execve(char *file, char **argv, char **env);
++extern char *current_cmd(void);
++extern void timer_handler(int sig, union uml_pt_regs *regs);
++extern int set_signals(int enable);
++extern void force_sigbus(void);
++extern int pid_to_processor_id(int pid);
++extern void block_signals(void);
++extern void unblock_signals(void);
++extern void deliver_signals(void *t);
++extern int next_syscall_index(int max);
++extern int next_trap_index(int max);
++extern void cpu_idle(void);
++extern void finish_fork(void);
++extern void paging_init(void);
++extern void init_flush_vm(void);
++extern void *syscall_sp(void *t);
++extern void syscall_trace(void);
++extern int hz(void);
++extern void idle_timer(void);
++extern unsigned int do_IRQ(int irq, union uml_pt_regs *regs);
++extern int external_pid(void *t);
++extern void boot_timer_handler(int sig);
++extern void interrupt_end(void);
++extern void initial_thread_cb(void (*proc)(void *), void *arg);
++extern int debugger_signal(int status, int pid);
++extern void debugger_parent_signal(int status, int pid);
++extern void child_signal(int pid, int status);
++extern int init_ptrace_proxy(int idle_pid, int startup, int stop);
++extern int init_parent_proxy(int pid);
++extern void check_stack_overflow(void *ptr);
++extern void relay_signal(int sig, union uml_pt_regs *regs);
++extern void not_implemented(void);
++extern int user_context(unsigned long sp);
++extern void timer_irq(union uml_pt_regs *regs);
++extern void unprotect_stack(unsigned long stack);
++extern void do_uml_exitcalls(void);
++extern int attach_debugger(int idle_pid, int pid, int stop);
++extern void bad_segv(unsigned long address, unsigned long ip, int is_write);
++extern int config_gdb(char *str);
++extern int remove_gdb(void);
++extern char *uml_strdup(char *string);
++extern void unprotect_kernel_mem(void);
++extern void protect_kernel_mem(void);
++extern void set_kmem_end(unsigned long);
++extern void uml_cleanup(void);
++extern void set_current(void *t);
++extern void lock_signalled_task(void *t);
++extern void IPI_handler(int cpu);
++extern int jail_setup(char *line, int *add);
++extern void *get_init_task(void);
++extern int clear_user_proc(void *buf, int size);
++extern int copy_to_user_proc(void *to, void *from, int size);
++extern int copy_from_user_proc(void *to, void *from, int size);
++extern int strlen_user_proc(char *str);
++extern void bus_handler(int sig, union uml_pt_regs *regs);
++extern void winch(int sig, union uml_pt_regs *regs);
++extern long execute_syscall(void *r);
++extern int smp_sigio_handler(void);
++extern void *get_current(void);
++extern struct task_struct *get_task(int pid, int require);
++extern void machine_halt(void);
++extern int is_syscall(unsigned long addr);
++extern void arch_switch(void);
++extern void free_irq(unsigned int, void *);
++extern int um_in_interrupt(void);
++extern int cpu(void);
++extern unsigned long long time_stamp(void);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/line.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/line.h   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/line.h        2005-05-03 22:28:14.307435152 +0300
+@@ -0,0 +1,103 @@
++/* 
++ * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __LINE_H__
++#define __LINE_H__
++
++#include "linux/list.h"
++#include "linux/tqueue.h"
++#include "linux/tty.h"
++#include "asm/semaphore.h"
++#include "chan_user.h"
++#include "mconsole_kern.h"
++
++struct line_driver {
++      char *name;
++      char *devfs_name;
++      short major;
++      short minor_start;
++      short type;
++      short subtype;
++      int read_irq;
++      char *read_irq_name;
++      int write_irq;
++      char *write_irq_name;
++      char *symlink_from;
++      char *symlink_to;
++      struct mc_device mc;
++};
++
++struct line {
++      char *init_str;
++      int init_pri;
++      struct list_head chan_list;
++      int valid;
++      int count;
++      struct tty_struct *tty;
++      struct semaphore sem;
++      char *buffer;
++      char *head;
++      char *tail;
++      int sigio;
++      struct tq_struct task;
++      struct line_driver *driver;
++      int have_irq;
++};
++
++#define LINE_INIT(str, d) \
++      { init_str :    str, \
++        init_pri :    INIT_STATIC, \
++        chan_list :   { }, \
++        valid :       1, \
++        count :       0, \
++        tty :         NULL, \
++        sem :         { }, \
++        buffer :      NULL, \
++        head :        NULL, \
++        tail :        NULL, \
++        sigio :       0, \
++        driver :      d, \
++          have_irq :  0 }
++
++struct lines {
++      int num;
++};
++
++#define LINES_INIT(n) {  num :                n }
++
++extern void line_close(struct line *lines, struct tty_struct *tty);
++extern int line_open(struct line *lines, struct tty_struct *tty, 
++                   struct chan_opts *opts);
++extern int line_setup(struct line *lines, int num, char *init, 
++                    int all_allowed);
++extern int line_write(struct line *line, struct tty_struct *tty, int from_user,
++                    const char *buf, int len);
++extern char *add_xterm_umid(char *base);
++extern int line_setup_irq(int fd, int input, int output, void *data);
++extern void line_close_chan(struct line *line);
++extern void line_disable(struct line *line, int current_irq);
++extern void line_register_devfs(struct lines *set, 
++                              struct line_driver *line_driver, 
++                              struct tty_driver *driver, struct line *lines,
++                              int nlines);
++extern void lines_init(struct line *lines, int nlines);
++extern void close_lines(struct line *lines, int nlines);
++extern int line_config(struct line *lines, int num, char *str);
++extern int line_remove(struct line *lines, int num, char *str);
++extern int line_get_config(char *dev, struct line *lines, int num, char *str, 
++                         int size, char **error_out);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/Makefile
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/Makefile 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/Makefile      2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,7 @@
++all : sc.h
++
++sc.h : ../util/mk_sc
++      ../util/mk_sc > $@
++
++../util/mk_sc :
++      $(MAKE) -C ../util mk_sc
+Index: linux-2.4.29/arch/um/include/mconsole.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/mconsole.h       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/mconsole.h    2005-05-03 22:28:14.309434848 +0300
+@@ -0,0 +1,103 @@
++/*
++ * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org)
++ * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __MCONSOLE_H__
++#define __MCONSOLE_H__
++
++#ifndef __KERNEL__
++#include <stdint.h>
++#define u32 uint32_t
++#endif
++
++#define MCONSOLE_MAGIC (0xcafebabe)
++#define MCONSOLE_MAX_DATA (512)
++#define MCONSOLE_VERSION 2
++
++struct mconsole_request {
++      u32 magic;
++      u32 version;
++      u32 len;
++      char data[MCONSOLE_MAX_DATA];
++};
++
++struct mconsole_reply {
++      u32 err;
++      u32 more;
++      u32 len;
++      char data[MCONSOLE_MAX_DATA];
++};
++
++struct mconsole_notify {
++      u32 magic;
++      u32 version;    
++      enum { MCONSOLE_SOCKET, MCONSOLE_PANIC, MCONSOLE_HANG,
++             MCONSOLE_USER_NOTIFY } type;
++      u32 len;
++      char data[MCONSOLE_MAX_DATA];
++};
++
++struct mc_request;
++
++enum mc_context { MCONSOLE_INTR, MCONSOLE_PROC };
++
++struct mconsole_command
++{
++      char *command;
++      void (*handler)(struct mc_request *req);
++      enum mc_context context;
++};
++
++struct mc_request
++{
++      int len;
++      int as_interrupt;
++
++      int originating_fd;
++      int originlen;
++      unsigned char origin[128];                      /* sockaddr_un */
++
++      struct mconsole_request request;
++      struct mconsole_command *cmd;
++};
++
++extern char mconsole_socket_name[];
++
++extern int mconsole_unlink_socket(void);
++extern int mconsole_reply(struct mc_request *req, char *reply, int err,
++                        int more);
++
++extern void mconsole_version(struct mc_request *req);
++extern void mconsole_help(struct mc_request *req);
++extern void mconsole_halt(struct mc_request *req);
++extern void mconsole_reboot(struct mc_request *req);
++extern void mconsole_config(struct mc_request *req);
++extern void mconsole_remove(struct mc_request *req);
++extern void mconsole_sysrq(struct mc_request *req);
++extern void mconsole_cad(struct mc_request *req);
++extern void mconsole_stop(struct mc_request *req);
++extern void mconsole_go(struct mc_request *req);
++extern void mconsole_log(struct mc_request *req);
++extern void mconsole_proc(struct mc_request *req);
++
++extern int mconsole_get_request(int fd, struct mc_request *req);
++extern int mconsole_notify(char *sock_name, int type, const void *data, 
++                         int len);
++extern char *mconsole_notify_socket(void);
++extern void lock_notify(void);
++extern void unlock_notify(void);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/mconsole_kern.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/mconsole_kern.h  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/mconsole_kern.h       2005-05-03 22:28:14.310434696 +0300
+@@ -0,0 +1,62 @@
++/*
++ * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __MCONSOLE_KERN_H__
++#define __MCONSOLE_KERN_H__
++
++#include "linux/config.h"
++#include "linux/list.h"
++#include "mconsole.h"
++
++struct mconsole_entry {
++      struct list_head list;
++      struct mc_request request;
++};
++
++struct mc_device {
++      struct list_head list;
++      char *name;
++      int (*config)(char *);
++      int (*get_config)(char *, char *, int, char **);
++      int (*remove)(char *);
++};
++
++#define CONFIG_CHUNK(str, size, current, chunk, end) \
++do { \
++      current += strlen(chunk); \
++      if(current >= size) \
++              str = NULL; \
++      if(str != NULL){ \
++              strcpy(str, chunk); \
++              str += strlen(chunk); \
++      } \
++      if(end) \
++              current++; \
++} while(0)
++
++#ifdef CONFIG_MCONSOLE
++
++extern void mconsole_register_dev(struct mc_device *new);
++
++#else
++
++static inline void mconsole_register_dev(struct mc_device *new)
++{
++}
++
++#endif
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/mem.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/mem.h    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/mem.h 2005-05-03 22:28:14.310434696 +0300
+@@ -0,0 +1,29 @@
++/* 
++ * Copyright (C) 2002, 2003 Jeff Dike (jdike@addtoit.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __MEM_H__
++#define __MEM_H__
++
++#include "linux/types.h"
++
++extern void set_kmem_end(unsigned long new);
++extern int phys_mapping(unsigned long phys, __u64 *offset_out);
++extern int physmem_subst_mapping(void *virt, int fd, __u64 offset, int w);
++extern int is_remapped(const void *virt, int fd, __u64 offset);
++extern int physmem_remove_mapping(void *virt);
++extern void physmem_forget_descriptor(int fd);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/mem_kern.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/mem_kern.h       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/mem_kern.h    2005-05-03 22:28:14.311434544 +0300
+@@ -0,0 +1,30 @@
++/* 
++ * Copyright (C) 2003 Jeff Dike (jdike@addtoit.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __MEM_KERN_H__
++#define __MEM_KERN_H__
++
++#include "linux/list.h"
++#include "linux/types.h"
++
++struct remapper {
++      struct list_head list;
++      int (*proc)(int, unsigned long, int, __u64, int);
++};
++
++extern void register_remapper(struct remapper *info);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/mem_user.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/mem_user.h       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/mem_user.h    2005-05-03 22:28:14.313434240 +0300
+@@ -0,0 +1,82 @@
++/*
++ * arch/um/include/mem_user.h
++ *
++ * BRIEF MODULE DESCRIPTION
++ * user side memory interface for support IO memory inside user mode linux
++ *
++ * Copyright (C) 2001 RidgeRun, Inc.
++ * Author: RidgeRun, Inc.
++ *         Greg Lonnon glonnon@ridgerun.com or info@ridgerun.com
++ *
++ *  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.
++ *
++ *  THIS  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED
++ *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
++ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
++ *  NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT,
++ *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
++ *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
++ *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
++ *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
++ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
++ *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ *
++ *  You should have received a copy of the  GNU General Public License along
++ *  with this program; if not, write  to the Free Software Foundation, Inc.,
++ *  675 Mass Ave, Cambridge, MA 02139, USA.
++ */
++
++#ifndef _MEM_USER_H
++#define _MEM_USER_H
++
++struct iomem_region {
++      struct iomem_region *next;
++      char *driver;
++      int fd;
++      int size;
++      unsigned long phys;
++      unsigned long virt;
++};
++
++extern struct iomem_region *iomem_regions;
++extern int iomem_size;
++
++#define ROUND_4M(n) ((((unsigned long) (n)) + (1 << 22)) & ~((1 << 22) - 1))
++
++extern unsigned long host_task_size;
++extern unsigned long task_size;
++
++extern int init_mem_user(void);
++extern int create_mem_file(unsigned long len);
++extern void setup_memory(void *entry);
++extern unsigned long find_iomem(char *driver, unsigned long *len_out);
++extern int init_maps(unsigned long physmem, unsigned long iomem, 
++                   unsigned long highmem);
++extern unsigned long get_vm(unsigned long len);
++extern void setup_physmem(unsigned long start, unsigned long usable,
++                        unsigned long len, unsigned long highmem);
++extern void add_iomem(char *name, int fd, unsigned long size);
++extern unsigned long phys_offset(unsigned long phys);
++extern void unmap_physmem(void);
++extern void map_memory(unsigned long virt, unsigned long phys, 
++                     unsigned long len, int r, int w, int x);
++extern int protect_memory(unsigned long addr, unsigned long len, 
++                        int r, int w, int x, int must_succeed);
++extern unsigned long get_kmem_end(void);
++extern void check_tmpexec(void);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/mode.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/mode.h   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/mode.h        2005-05-03 22:28:14.313434240 +0300
+@@ -0,0 +1,30 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __MODE_H__
++#define __MODE_H__
++
++#include "uml-config.h"
++
++#ifdef UML_CONFIG_MODE_TT
++#include "../kernel/tt/include/mode.h"
++#endif
++
++#ifdef UML_CONFIG_MODE_SKAS
++#include "../kernel/skas/include/mode.h"
++#endif
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/mode_kern.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/mode_kern.h      1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/mode_kern.h   2005-05-03 22:28:14.314434088 +0300
+@@ -0,0 +1,30 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __MODE_KERN_H__
++#define __MODE_KERN_H__
++
++#include "linux/config.h"
++
++#ifdef CONFIG_MODE_TT
++#include "../kernel/tt/include/mode_kern.h"
++#endif
++
++#ifdef CONFIG_MODE_SKAS
++#include "../kernel/skas/include/mode_kern.h"
++#endif
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/net_kern.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/net_kern.h       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/net_kern.h    2005-05-03 22:28:14.315433936 +0300
+@@ -0,0 +1,81 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __UM_NET_KERN_H
++#define __UM_NET_KERN_H
++
++#include "linux/netdevice.h"
++#include "linux/skbuff.h"
++#include "linux/socket.h"
++#include "linux/list.h"
++
++struct uml_net {
++      struct list_head list;
++      struct net_device *dev;
++      int index;
++      unsigned char mac[ETH_ALEN];
++      int have_mac;
++};
++
++struct uml_net_private {
++      struct list_head list;
++      spinlock_t lock;
++      struct net_device *dev;
++      struct timer_list tl;
++      struct net_device_stats stats;
++      int fd;
++      unsigned char mac[ETH_ALEN];
++      int have_mac;
++      unsigned short (*protocol)(struct sk_buff *);
++      int (*open)(void *);
++      void (*close)(int, void *);
++      void (*remove)(void *);
++      int (*read)(int, struct sk_buff **skb, struct uml_net_private *);
++      int (*write)(int, struct sk_buff **skb, struct uml_net_private *);
++      
++      void (*add_address)(unsigned char *, unsigned char *, void *);
++      void (*delete_address)(unsigned char *, unsigned char *, void *);
++      int (*set_mtu)(int mtu, void *);
++      int user[1];
++};
++
++struct net_kern_info {
++      void (*init)(struct net_device *, void *);
++      unsigned short (*protocol)(struct sk_buff *);
++      int (*read)(int, struct sk_buff **skb, struct uml_net_private *);
++      int (*write)(int, struct sk_buff **skb, struct uml_net_private *);
++};
++
++struct transport {
++      struct list_head list;
++      char *name;
++      int (*setup)(char *, char **, void *);
++      struct net_user_info *user;
++      struct net_kern_info *kern;
++      int private_size;
++      int setup_size;
++};
++
++extern struct net_device *ether_init(int);
++extern unsigned short ether_protocol(struct sk_buff *);
++extern int setup_etheraddr(char *str, unsigned char *addr);
++extern struct sk_buff *ether_adjust_skb(struct sk_buff *skb, int extra);
++extern int tap_setup_common(char *str, char *type, char **dev_name, 
++                          char **mac_out, char **gate_addr);
++extern void register_transport(struct transport *new);
++extern unsigned short eth_protocol(struct sk_buff *skb);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/net_user.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/net_user.h       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/net_user.h    2005-05-03 22:28:14.316433784 +0300
+@@ -0,0 +1,66 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __UM_NET_USER_H__
++#define __UM_NET_USER_H__
++
++#define ETH_ADDR_LEN (6)
++#define ETH_HEADER_ETHERTAP (16)
++#define ETH_HEADER_OTHER (14)
++#define ETH_MAX_PACKET (1500)
++
++#define UML_NET_VERSION (4)
++
++struct net_user_info {
++      void (*init)(void *, void *);
++      int (*open)(void *);
++      void (*close)(int, void *);
++      void (*remove)(void *);
++      int (*set_mtu)(int mtu, void *);
++      void (*add_address)(unsigned char *, unsigned char *, void *);
++      void (*delete_address)(unsigned char *, unsigned char *, void *);
++      int max_packet;
++};
++
++extern void ether_user_init(void *data, void *dev);
++extern void dev_ip_addr(void *d, char *buf, char *bin_buf);
++extern void set_ether_mac(void *d, unsigned char *addr);
++extern void iter_addresses(void *d, void (*cb)(unsigned char *, 
++                                             unsigned char *, void *), 
++                         void *arg);
++
++extern void *get_output_buffer(int *len_out);
++extern void free_output_buffer(void *buffer);
++
++extern int tap_open_common(void *dev, char *gate_addr);
++extern void tap_check_ips(char *gate_addr, char *eth_addr);
++
++extern void read_output(int fd, char *output_out, int len);
++
++extern int net_read(int fd, void *buf, int len);
++extern int net_recvfrom(int fd, void *buf, int len);
++extern int net_write(int fd, void *buf, int len);
++extern int net_send(int fd, void *buf, int len);
++extern int net_sendto(int fd, void *buf, int len, void *to, int sock_len);
++
++extern void open_addr(unsigned char *addr, unsigned char *netmask, void *arg);
++extern void close_addr(unsigned char *addr, unsigned char *netmask, void *arg);
++
++extern char *split_if_spec(char *str, ...);
++
++extern int dev_netmask(void *d, void *m);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/os.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/os.h     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/os.h  2005-05-03 22:28:14.318433480 +0300
+@@ -0,0 +1,221 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __OS_H__
++#define __OS_H__
++
++#include "asm/types.h"
++#include "../os/include/file.h"
++
++#define OS_TYPE_FILE 1 
++#define OS_TYPE_DIR 2 
++#define OS_TYPE_SYMLINK 3 
++#define OS_TYPE_CHARDEV 4
++#define OS_TYPE_BLOCKDEV 5
++#define OS_TYPE_FIFO 6
++#define OS_TYPE_SOCK 7
++
++/* os_access() flags */
++#define OS_ACC_F_OK    0       /* Test for existence.  */
++#define OS_ACC_X_OK    1       /* Test for execute permission.  */
++#define OS_ACC_W_OK    2       /* Test for write permission.  */
++#define OS_ACC_R_OK    4       /* Test for read permission.  */
++#define OS_ACC_RW_OK   (OS_ACC_W_OK | OS_ACC_R_OK) /* Test for RW permission */
++
++/*
++ * types taken from stat_file() in hostfs_user.c
++ * (if they are wrong here, they are wrong there...).
++ */
++struct uml_stat {
++      int                ust_major;      /* device */
++      int                ust_minor;
++      unsigned long long ust_ino;        /* inode */
++      int                ust_mode;       /* protection */
++      int                ust_nlink;      /* number of hard links */
++      int                ust_uid;        /* user ID of owner */
++      int                ust_gid;        /* group ID of owner */
++      unsigned long long ust_size;       /* total size, in bytes */
++      int                ust_blksize;    /* blocksize for filesystem I/O */
++      unsigned long long ust_blocks;     /* number of blocks allocated */
++      unsigned long      ust_atime;      /* time of last access */
++      unsigned long      ust_mtime;      /* time of last modification */
++      unsigned long      ust_ctime;      /* time of last change */
++      int                ust_rmajor;
++      int                ust_rminor;
++};
++
++struct openflags {
++      unsigned int r : 1;
++      unsigned int w : 1;
++      unsigned int s : 1;     /* O_SYNC */
++      unsigned int c : 1;     /* O_CREAT */
++      unsigned int t : 1;     /* O_TRUNC */
++      unsigned int a : 1;     /* O_APPEND */
++      unsigned int e : 1;     /* O_EXCL */
++      unsigned int cl : 1;    /* FD_CLOEXEC */
++      unsigned int d : 1;     /* O_DIRECT */
++};
++
++#define OPENFLAGS() ((struct openflags) { .r = 0, .w = 0, .s = 0, .c = 0, \
++                                        .t = 0, .a = 0, .e = 0, .cl = 0, \
++                                        .d = 0 })
++
++static inline struct openflags of_read(struct openflags flags)
++{
++      flags.r = 1; 
++      return(flags);
++}
++
++static inline struct openflags of_write(struct openflags flags)
++{
++      flags.w = 1; 
++      return(flags); 
++}
++
++static inline struct openflags of_rdwr(struct openflags flags)
++{
++      return(of_read(of_write(flags)));
++}
++
++static inline struct openflags of_set_rw(struct openflags flags, int r, int w)
++{
++      flags.r = r;
++      flags.w = w;
++      return(flags);
++}
++
++static inline struct openflags of_sync(struct openflags flags)
++{ 
++      flags.s = 1; 
++      return(flags); 
++}
++
++static inline struct openflags of_create(struct openflags flags)
++{ 
++      flags.c = 1; 
++      return(flags); 
++}
++ 
++static inline struct openflags of_trunc(struct openflags flags)
++{ 
++      flags.t = 1; 
++      return(flags); 
++}
++ 
++static inline struct openflags of_append(struct openflags flags)
++{ 
++      flags.a = 1; 
++      return(flags); 
++}
++ 
++static inline struct openflags of_excl(struct openflags flags)
++{ 
++      flags.e = 1; 
++      return(flags); 
++}
++
++static inline struct openflags of_cloexec(struct openflags flags)
++{ 
++      flags.cl = 1; 
++      return(flags); 
++}
++ 
++static inline struct openflags of_direct(struct openflags flags)
++{ 
++      flags.d = 1; 
++      return(flags); 
++}
++ 
++extern int os_stat_file(const char *file_name, struct uml_stat *buf);
++extern int os_lstat_file(const char *file_name, struct uml_stat *ubuf);
++extern int os_stat_fd(const int fd, struct uml_stat *buf);
++extern int os_access(const char *file, int mode);
++extern int os_set_file_time(const char *file, unsigned long access, 
++                          unsigned long mod);
++extern int os_set_file_perms(const char *file, int mode);
++extern int os_set_file_owner(const char *file, int owner, int group);
++extern void os_print_error(int error, const char* str);
++extern int os_get_exec_close(int fd, int *close_on_exec);
++extern int os_set_exec_close(int fd, int close_on_exec);
++extern int os_ioctl_generic(int fd, unsigned int cmd, unsigned long arg);
++extern int os_window_size(int fd, int *rows, int *cols);
++extern int os_new_tty_pgrp(int fd, int pid);
++extern int os_get_ifname(int fd, char *namebuf);
++extern int os_set_slip(int fd);
++extern int os_set_owner(int fd, int pid);
++extern int os_sigio_async(int master, int slave);
++extern int os_mode_fd(int fd, int mode);
++
++extern int os_seek_file(int fd, __u64 offset);
++extern int os_open_file(char *file, struct openflags flags, int mode);
++extern void *os_open_dir(char *dir, int *err_out);
++extern int os_seek_dir(void *stream, unsigned long long pos);
++extern int os_read_dir(void *stream, unsigned long long *ino_out, 
++                     char **name_out);
++extern int os_tell_dir(void *stream);
++extern int os_close_dir(void *stream);
++extern int os_remove_file(const char *file);
++extern int os_move_file(const char *from, const char *to);
++extern int os_truncate_file(const char *file, unsigned long long len);
++extern int os_truncate_fd(int fd, unsigned long long len);
++extern int os_read_file(int fd, void *buf, int len);
++extern int os_write_file(int fd, const void *buf, int count);
++extern int os_file_size(char *file, long long *size_out);
++extern int os_fd_size(int fd, long long *size_out);
++extern int os_file_modtime(char *file, unsigned long *modtime);
++extern int os_pipe(int *fd, int stream, int close_on_exec);
++extern int os_set_fd_async(int fd, int owner);
++extern int os_clear_fd_async(int fd);
++extern int os_set_fd_block(int fd, int blocking);
++extern int os_accept_connection(int fd);
++extern int os_create_unix_socket(char *file, int len, int close_on_exec);
++extern int os_make_symlink(const char *to, const char *from);
++extern int os_read_symlink(const char *file, char *buf, int size);
++extern int os_link_file(const char *to, const char *from);
++extern int os_make_dir(const char *dir, int mode);
++extern int os_remove_dir(const char *dir);
++extern int os_make_dev(const char *name, int mode, int major, int minor);
++extern int os_shutdown_socket(int fd, int r, int w);
++extern void os_close_file(int fd);
++extern int os_rcv_fd(int fd, int *helper_pid_out);
++extern int create_unix_socket(char *file, int len, int close_on_exec);
++extern int os_connect_socket(char *name);
++extern int os_file_type(char *file);
++extern int os_file_mode(char *file, struct openflags *mode_out);
++extern int os_lock_file(int fd, int excl);
++
++extern unsigned long os_process_pc(int pid);
++extern int os_process_parent(int pid);
++extern void os_stop_process(int pid);
++extern void os_kill_process(int pid, int reap_child);
++extern void os_usr1_process(int pid);
++extern int os_getpid(void);
++
++extern int os_map_memory(void *virt, int fd, unsigned long long off, 
++                       unsigned long len, int r, int w, int x);
++extern int os_protect_memory(void *addr, unsigned long len, 
++                           int r, int w, int x);
++extern int os_unmap_memory(void *addr, int len);
++extern void os_flush_stdout(void);
++extern int os_stat_filesystem(char *path, long *bsize_out, 
++                            long long *blocks_out, long long *bfree_out,
++                            long long *bavail_out, long long *files_out,
++                            long long *ffree_out, void *fsid_out, 
++                            int fsid_size, long *namelen_out, 
++                            long *spare_out);
++extern unsigned long long os_usecs(void);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/process.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/process.h        1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/process.h     2005-05-03 22:28:14.319433328 +0300
+@@ -0,0 +1,25 @@
++/* 
++ * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __PROCESS_H__
++#define __PROCESS_H__
++
++#include <asm/sigcontext.h>
++
++extern void sig_handler(int sig, struct sigcontext sc);
++extern void alarm_handler(int sig, struct sigcontext sc);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/ptrace_user.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/ptrace_user.h    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/ptrace_user.h 2005-05-03 22:28:14.319433328 +0300
+@@ -0,0 +1,25 @@
++/* 
++ * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __PTRACE_USER_H__
++#define __PTRACE_USER_H__
++
++#include "sysdep/ptrace_user.h"
++
++/* syscall emulation path in ptrace */
++#ifndef PTRACE_SYSEMU
++#define PTRACE_SYSEMU 31
++#endif
++
++extern int use_sysemu;
++
++extern int ptrace_getregs(long pid, unsigned long *regs_out);
++extern int ptrace_setregs(long pid, unsigned long *regs_in);
++extern int ptrace_getfpregs(long pid, unsigned long *regs_out);
++extern void arch_enter_kernel(void *task, int pid);
++extern void arch_leave_kernel(void *task, int pid);
++extern void ptrace_pokeuser(unsigned long addr, unsigned long data);
++
++#endif
+Index: linux-2.4.29/arch/um/include/sigcontext.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/sigcontext.h     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/sigcontext.h  2005-05-03 22:28:14.320433176 +0300
+@@ -0,0 +1,25 @@
++/* 
++ * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __UML_SIGCONTEXT_H__
++#define __UML_SIGCONTEXT_H__
++
++#include "sysdep/sigcontext.h"
++
++extern int sc_size(void *data);
++extern void sc_to_sc(void *to_ptr, void *from_ptr);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/sigio.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/sigio.h  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/sigio.h       2005-05-03 22:28:14.321433024 +0300
+@@ -0,0 +1,28 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __SIGIO_H__
++#define __SIGIO_H__
++
++extern int write_sigio_irq(int fd);
++extern int register_sigio_fd(int fd);
++extern int read_sigio_fd(int fd);
++extern int add_sigio_fd(int fd, int read);
++extern int ignore_sigio_fd(int fd);
++extern void sigio_lock(void);
++extern void sigio_unlock(void);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/signal_kern.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/signal_kern.h    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/signal_kern.h 2005-05-03 22:28:14.322432872 +0300
+@@ -0,0 +1,22 @@
++/* 
++ * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __SIGNAL_KERN_H__
++#define __SIGNAL_KERN_H__
++
++extern int have_signals(void *t);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/signal_user.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/signal_user.h    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/signal_user.h 2005-05-03 22:28:14.323432720 +0300
+@@ -0,0 +1,26 @@
++/* 
++ * Copyright (C) 2001 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __SIGNAL_USER_H__
++#define __SIGNAL_USER_H__
++
++extern int signal_stack_size;
++
++extern int change_sig(int signal, int on);
++extern void set_sigstack(void *stack, int size);
++extern void set_handler(int sig, void (*handler)(int), int flags, ...);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/skas_ptrace.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/skas_ptrace.h    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/skas_ptrace.h 2005-05-03 22:28:14.323432720 +0300
+@@ -0,0 +1,36 @@
++/* 
++ * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __SKAS_PTRACE_H
++#define __SKAS_PTRACE_H
++
++struct ptrace_faultinfo {
++      int is_write;
++      unsigned long addr;
++};
++
++struct ptrace_ldt {
++      int func;
++      void *ptr;
++      unsigned long bytecount;
++};
++
++#define PTRACE_FAULTINFO 52
++#define PTRACE_SIGPENDING 53
++#define PTRACE_LDT 54
++#define PTRACE_SWITCH_MM 55
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/syscall_user.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/syscall_user.h   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/syscall_user.h        2005-05-03 22:28:14.324432568 +0300
+@@ -0,0 +1,23 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __SYSCALL_USER_H
++#define __SYSCALL_USER_H
++
++extern int record_syscall_start(int syscall);
++extern void record_syscall_end(int index, int result);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/sysdep-i386/checksum.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/sysdep-i386/checksum.h   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/sysdep-i386/checksum.h        2005-05-03 22:28:14.326432264 +0300
+@@ -0,0 +1,218 @@
++/* 
++ * Licensed under the GPL
++ */
++
++#ifndef __UM_SYSDEP_CHECKSUM_H
++#define __UM_SYSDEP_CHECKSUM_H
++
++#include "linux/string.h"
++#include "asm/uaccess.h"
++
++/*
++ * computes the checksum of a memory block at buff, length len,
++ * and adds in "sum" (32-bit)
++ *
++ * returns a 32-bit number suitable for feeding into itself
++ * or csum_tcpudp_magic
++ *
++ * this function must be called with even lengths, except
++ * for the last fragment, which may be odd
++ *
++ * it's best to have buff aligned on a 32-bit boundary
++ */
++unsigned int csum_partial(const unsigned char * buff, int len, 
++                        unsigned int sum);
++
++/*
++ * the same as csum_partial, but copies from src while it
++ * checksums, and handles user-space pointer exceptions correctly, when needed.
++ *
++ * here even more important to align src and dst on a 32-bit (or even
++ * better 64-bit) boundary
++ */
++
++unsigned int csum_partial_copy_to(const char *src, char *dst, int len, 
++                                int sum, int *err_ptr);
++unsigned int csum_partial_copy_from(const char *src, char *dst, int len, 
++                                  int sum, int *err_ptr);
++
++/*
++ *    Note: when you get a NULL pointer exception here this means someone
++ *    passed in an incorrect kernel address to one of these functions.
++ *
++ *    If you use these functions directly please don't forget the
++ *    verify_area().
++ */
++
++static __inline__
++unsigned int csum_partial_copy_nocheck(const char *src, char *dst,
++                                     int len, int sum)
++{
++      memcpy(dst, src, len);
++      return(csum_partial(dst, len, sum));
++}
++
++static __inline__
++unsigned int csum_partial_copy_from_user(const char *src, char *dst,
++                                       int len, int sum, int *err_ptr)
++{
++      return csum_partial_copy_from(src, dst, len, sum, err_ptr);
++}
++
++/*
++ * These are the old (and unsafe) way of doing checksums, a warning message 
++ * will be printed if they are used and an exception occurs.
++ *
++ * these functions should go away after some time.
++ */
++
++#define csum_partial_copy_fromuser csum_partial_copy_from_user
++unsigned int csum_partial_copy( const char *src, char *dst, int len, int sum);
++
++/*
++ *    This is a version of ip_compute_csum() optimized for IP headers,
++ *    which always checksum on 4 octet boundaries.
++ *
++ *    By Jorge Cwik <jorge@laser.satlink.net>, adapted for linux by
++ *    Arnt Gulbrandsen.
++ */
++static inline unsigned short ip_fast_csum(unsigned char * iph,
++                                        unsigned int ihl)
++{
++      unsigned int sum;
++
++      __asm__ __volatile__(
++          "movl (%1), %0      ;\n"
++          "subl $4, %2        ;\n"
++          "jbe 2f             ;\n"
++          "addl 4(%1), %0     ;\n"
++          "adcl 8(%1), %0     ;\n"
++          "adcl 12(%1), %0    ;\n"
++"1:       adcl 16(%1), %0     ;\n"
++          "lea 4(%1), %1      ;\n"
++          "decl %2            ;\n"
++          "jne 1b             ;\n"
++          "adcl $0, %0        ;\n"
++          "movl %0, %2        ;\n"
++          "shrl $16, %0       ;\n"
++          "addw %w2, %w0      ;\n"
++          "adcl $0, %0        ;\n"
++          "notl %0            ;\n"
++"2:                           ;\n"
++      /* Since the input registers which are loaded with iph and ipl
++         are modified, we must also specify them as outputs, or gcc
++         will assume they contain their original values. */
++      : "=r" (sum), "=r" (iph), "=r" (ihl)
++      : "1" (iph), "2" (ihl));
++      return(sum);
++}
++
++/*
++ *    Fold a partial checksum
++ */
++
++static inline unsigned int csum_fold(unsigned int sum)
++{
++      __asm__(
++              "addl %1, %0            ;\n"
++              "adcl $0xffff, %0       ;\n"
++              : "=r" (sum)
++              : "r" (sum << 16), "0" (sum & 0xffff0000)
++      );
++      return (~sum) >> 16;
++}
++
++static inline unsigned long csum_tcpudp_nofold(unsigned long saddr,
++                                                 unsigned long daddr,
++                                                 unsigned short len,
++                                                 unsigned short proto,
++                                                 unsigned int sum)
++{
++    __asm__(
++      "addl %1, %0    ;\n"
++      "adcl %2, %0    ;\n"
++      "adcl %3, %0    ;\n"
++      "adcl $0, %0    ;\n"
++      : "=r" (sum)
++      : "g" (daddr), "g"(saddr), "g"((ntohs(len)<<16)+proto*256), "0"(sum));
++    return sum;
++}
++
++/*
++ * computes the checksum of the TCP/UDP pseudo-header
++ * returns a 16-bit checksum, already complemented
++ */
++static inline unsigned short int csum_tcpudp_magic(unsigned long saddr,
++                                                 unsigned long daddr,
++                                                 unsigned short len,
++                                                 unsigned short proto,
++                                                 unsigned int sum)
++{
++      return csum_fold(csum_tcpudp_nofold(saddr,daddr,len,proto,sum));
++}
++
++/*
++ * this routine is used for miscellaneous IP-like checksums, mainly
++ * in icmp.c
++ */
++
++static inline unsigned short ip_compute_csum(unsigned char * buff, int len)
++{
++    return csum_fold (csum_partial(buff, len, 0));
++}
++
++#define _HAVE_ARCH_IPV6_CSUM
++static __inline__ unsigned short int csum_ipv6_magic(struct in6_addr *saddr,
++                                                   struct in6_addr *daddr,
++                                                   __u32 len,
++                                                   unsigned short proto,
++                                                   unsigned int sum)
++{
++      __asm__(
++              "addl 0(%1), %0         ;\n"
++              "adcl 4(%1), %0         ;\n"
++              "adcl 8(%1), %0         ;\n"
++              "adcl 12(%1), %0        ;\n"
++              "adcl 0(%2), %0         ;\n"
++              "adcl 4(%2), %0         ;\n"
++              "adcl 8(%2), %0         ;\n"
++              "adcl 12(%2), %0        ;\n"
++              "adcl %3, %0            ;\n"
++              "adcl %4, %0            ;\n"
++              "adcl $0, %0            ;\n"
++              : "=&r" (sum)
++              : "r" (saddr), "r" (daddr),
++                "r"(htonl(len)), "r"(htonl(proto)), "0"(sum));
++
++      return csum_fold(sum);
++}
++
++/*
++ *    Copy and checksum to user
++ */
++#define HAVE_CSUM_COPY_USER
++static __inline__ unsigned int csum_and_copy_to_user(const char *src, 
++                                                   char *dst, int len,
++                                                   int sum, int *err_ptr)
++{
++      if (access_ok(VERIFY_WRITE, dst, len))
++              return(csum_partial_copy_to(src, dst, len, sum, err_ptr));
++
++      if (len)
++              *err_ptr = -EFAULT;
++
++      return -1; /* invalid checksum */
++}
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/sysdep-i386/frame.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/sysdep-i386/frame.h      1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/sysdep-i386/frame.h   2005-05-03 22:28:14.351428464 +0300
+@@ -0,0 +1,29 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __FRAME_I386_H
++#define __FRAME_I386_H
++
++struct arch_frame_data_raw {
++      unsigned long fp_start;
++      unsigned long sr;
++};
++
++struct arch_frame_data {
++      int fpstate_size;
++};
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/sysdep-i386/frame_kern.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/sysdep-i386/frame_kern.h 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/sysdep-i386/frame_kern.h      2005-05-03 22:28:14.352428312 +0300
+@@ -0,0 +1,69 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __FRAME_KERN_I386_H
++#define __FRAME_KERN_I386_H
++
++/* This is called from sys_sigreturn.  It takes the sp at the point of the
++ * sigreturn system call and returns the address of the sigcontext struct
++ * on the stack.
++ */
++
++static inline void *sp_to_sc(unsigned long sp)
++{
++      return((void *) sp);
++}
++
++static inline void *sp_to_uc(unsigned long sp)
++{
++      unsigned long uc;
++
++      uc = sp + signal_frame_si.uc_index - 
++              signal_frame_si.common.sp_index - 4;
++      return((void *) uc);
++}
++
++static inline void *sp_to_rt_sc(unsigned long sp)
++{
++      unsigned long sc;
++
++      sc = sp - signal_frame_si.common.sp_index + 
++              signal_frame_si.common.len - 4;
++      return((void *) sc);
++}
++
++static inline void *sp_to_mask(unsigned long sp)
++{
++      unsigned long mask;
++
++      mask = sp - signal_frame_sc.common.sp_index + 
++              signal_frame_sc.common.len - 8;
++      return((void *) mask);
++}
++
++extern int sc_size(void *data);
++
++static inline void *sp_to_rt_mask(unsigned long sp)
++{
++      unsigned long mask;
++
++      mask = sp - signal_frame_si.common.sp_index + 
++              signal_frame_si.common.len + 
++              sc_size(&signal_frame_si.common.arch) - 4;
++      return((void *) mask);
++}
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/sysdep-i386/frame_user.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/sysdep-i386/frame_user.h 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/sysdep-i386/frame_user.h      2005-05-03 22:28:14.353428160 +0300
+@@ -0,0 +1,91 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __FRAME_USER_I386_H
++#define __FRAME_USER_I386_H
++
++#include <asm/page.h>
++#include "sysdep/frame.h"
++
++/* This stuff is to calculate the size of the fp state struct at runtime
++ * because it has changed between 2.2 and 2.4 and it would be good for a
++ * UML compiled on one to work on the other.
++ * So, setup_arch_frame_raw fills in the arch struct with the raw data, which
++ * just contains the address of the end of the sigcontext.  This is invoked
++ * from the signal handler.
++ * setup_arch_frame uses that data to figure out what 
++ * arch_frame_data.fpstate_size should be.  It really has no idea, since it's
++ * not allowed to do sizeof(struct fpstate) but it's safe to consider that it's
++ * everything from the end of the sigcontext up to the top of the stack.  So,
++ * it masks off the page number to get the offset within the page and subtracts
++ * that from the page size, and that's how big the fpstate struct will be
++ * considered to be.
++ */
++
++static inline void setup_arch_frame_raw(struct arch_frame_data_raw *data,
++                                      void *end, unsigned long srp)
++{
++      unsigned long sr = *((unsigned long *) srp);
++
++      data->fp_start = (unsigned long) end;
++      if((sr & PAGE_MASK) == ((unsigned long) end & PAGE_MASK))
++              data->sr = sr;
++      else data->sr = 0;
++}
++
++static inline void setup_arch_frame(struct arch_frame_data_raw *in, 
++                                  struct arch_frame_data *out)
++{
++      unsigned long fpstate_start = in->fp_start;
++
++      if(in->sr == 0){
++              fpstate_start &= ~PAGE_MASK;
++              out->fpstate_size = PAGE_SIZE - fpstate_start;
++      }
++      else {
++              out->fpstate_size = in->sr - fpstate_start;
++      }
++}
++
++/* This figures out where on the stack the SA_RESTORER function address
++ * is stored.  For i386, it's the signal handler return address, so it's
++ * located next to the frame pointer.
++ * This is inlined, so __builtin_frame_address(0) is correct.  Otherwise,
++ * it would have to be __builtin_frame_address(1).
++ */
++
++#define frame_restorer() \
++({ \
++      unsigned long *fp; \
++\
++      fp = __builtin_frame_address(0); \
++      ((unsigned long) (fp + 1)); \
++})
++
++/* Similarly, this returns the value of sp when the handler was first
++ * entered.  This is used to calculate the proper sp when delivering
++ * signals.
++ */
++
++#define frame_sp() \
++({ \
++      unsigned long *fp; \
++\
++      fp = __builtin_frame_address(0); \
++      ((unsigned long) (fp + 1)); \
++})
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/sysdep-i386/ptrace.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/sysdep-i386/ptrace.h     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/sysdep-i386/ptrace.h  2005-05-03 22:28:14.355427856 +0300
+@@ -0,0 +1,193 @@
++/* 
++ * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __SYSDEP_I386_PTRACE_H
++#define __SYSDEP_I386_PTRACE_H
++
++#include "uml-config.h"
++
++#ifdef UML_CONFIG_MODE_TT
++#include "ptrace-tt.h"
++#endif
++
++#ifdef UML_CONFIG_MODE_SKAS
++#include "ptrace-skas.h"
++#endif
++
++#include "choose-mode.h"
++
++union uml_pt_regs {
++#ifdef UML_CONFIG_MODE_TT
++      struct tt_regs {
++              long syscall;
++              void *sc;
++      } tt;
++#endif
++#ifdef UML_CONFIG_MODE_SKAS
++      struct skas_regs {
++              unsigned long regs[HOST_FRAME_SIZE];
++              unsigned long fp[HOST_FP_SIZE];
++              unsigned long xfp[HOST_XFP_SIZE];
++              unsigned long fault_addr;
++              unsigned long fault_type;
++              unsigned long trap_type;
++              long syscall;
++              int is_user;
++      } skas;
++#endif
++};
++
++#define EMPTY_UML_PT_REGS { }
++
++extern int mode_tt;
++
++#define UPT_SC(r) ((r)->tt.sc)
++#define UPT_IP(r) \
++      CHOOSE_MODE(SC_IP(UPT_SC(r)), REGS_IP((r)->skas.regs))
++#define UPT_SP(r) \
++      CHOOSE_MODE(SC_SP(UPT_SC(r)), REGS_SP((r)->skas.regs))
++#define UPT_EFLAGS(r) \
++      CHOOSE_MODE(SC_EFLAGS(UPT_SC(r)), REGS_EFLAGS((r)->skas.regs))
++#define UPT_EAX(r) \
++      CHOOSE_MODE(SC_EAX(UPT_SC(r)), REGS_EAX((r)->skas.regs))
++#define UPT_EBX(r) \
++      CHOOSE_MODE(SC_EBX(UPT_SC(r)), REGS_EBX((r)->skas.regs))
++#define UPT_ECX(r) \
++      CHOOSE_MODE(SC_ECX(UPT_SC(r)), REGS_ECX((r)->skas.regs))
++#define UPT_EDX(r) \
++      CHOOSE_MODE(SC_EDX(UPT_SC(r)), REGS_EDX((r)->skas.regs))
++#define UPT_ESI(r) \
++      CHOOSE_MODE(SC_ESI(UPT_SC(r)), REGS_ESI((r)->skas.regs))
++#define UPT_EDI(r) \
++      CHOOSE_MODE(SC_EDI(UPT_SC(r)), REGS_EDI((r)->skas.regs))
++#define UPT_EBP(r) \
++      CHOOSE_MODE(SC_EBP(UPT_SC(r)), REGS_EBP((r)->skas.regs))
++#define UPT_ORIG_EAX(r) \
++      CHOOSE_MODE((r)->tt.syscall, (r)->skas.syscall)
++#define UPT_CS(r) \
++      CHOOSE_MODE(SC_CS(UPT_SC(r)), REGS_CS((r)->skas.regs))
++#define UPT_SS(r) \
++      CHOOSE_MODE(SC_SS(UPT_SC(r)), REGS_SS((r)->skas.regs))
++#define UPT_DS(r) \
++      CHOOSE_MODE(SC_DS(UPT_SC(r)), REGS_DS((r)->skas.regs))
++#define UPT_ES(r) \
++      CHOOSE_MODE(SC_ES(UPT_SC(r)), REGS_ES((r)->skas.regs))
++#define UPT_FS(r) \
++      CHOOSE_MODE(SC_FS(UPT_SC(r)), REGS_FS((r)->skas.regs))
++#define UPT_GS(r) \
++      CHOOSE_MODE(SC_GS(UPT_SC(r)), REGS_GS((r)->skas.regs))
++
++#define UPT_SYSCALL_ARG1(r) UPT_EBX(r)
++#define UPT_SYSCALL_ARG2(r) UPT_ECX(r)
++#define UPT_SYSCALL_ARG3(r) UPT_EDX(r)
++#define UPT_SYSCALL_ARG4(r) UPT_ESI(r)
++#define UPT_SYSCALL_ARG5(r) UPT_EDI(r)
++#define UPT_SYSCALL_ARG6(r) UPT_EBP(r)
++
++extern int user_context(unsigned long sp);
++
++#define UPT_IS_USER(r) \
++      CHOOSE_MODE(user_context(UPT_SP(r)), (r)->skas.is_user)
++
++struct syscall_args {
++      unsigned long args[6];
++};
++
++#define SYSCALL_ARGS(r) ((struct syscall_args) \
++                        { .args = { UPT_SYSCALL_ARG1(r), \
++                                    UPT_SYSCALL_ARG2(r), \
++                                  UPT_SYSCALL_ARG3(r), \
++                                    UPT_SYSCALL_ARG4(r), \
++                                  UPT_SYSCALL_ARG5(r), \
++                                    UPT_SYSCALL_ARG6(r) } } )
++
++#define UPT_REG(regs, reg) \
++      ({      unsigned long val; \
++              switch(reg){ \
++              case EIP: val = UPT_IP(regs); break; \
++              case UESP: val = UPT_SP(regs); break; \
++              case EAX: val = UPT_EAX(regs); break; \
++              case EBX: val = UPT_EBX(regs); break; \
++              case ECX: val = UPT_ECX(regs); break; \
++              case EDX: val = UPT_EDX(regs); break; \
++              case ESI: val = UPT_ESI(regs); break; \
++              case EDI: val = UPT_EDI(regs); break; \
++              case EBP: val = UPT_EBP(regs); break; \
++              case ORIG_EAX: val = UPT_ORIG_EAX(regs); break; \
++              case CS: val = UPT_CS(regs); break; \
++              case SS: val = UPT_SS(regs); break; \
++              case DS: val = UPT_DS(regs); break; \
++              case ES: val = UPT_ES(regs); break; \
++              case FS: val = UPT_FS(regs); break; \
++              case GS: val = UPT_GS(regs); break; \
++              case EFL: val = UPT_EFLAGS(regs); break; \
++              default :  \
++                      panic("Bad register in UPT_REG : %d\n", reg);  \
++                      val = -1; \
++              } \
++              val; \
++      })
++      
++
++#define UPT_SET(regs, reg, val) \
++      do { \
++              switch(reg){ \
++              case EIP: UPT_IP(regs) = val; break; \
++              case UESP: UPT_SP(regs) = val; break; \
++              case EAX: UPT_EAX(regs) = val; break; \
++              case EBX: UPT_EBX(regs) = val; break; \
++              case ECX: UPT_ECX(regs) = val; break; \
++              case EDX: UPT_EDX(regs) = val; break; \
++              case ESI: UPT_ESI(regs) = val; break; \
++              case EDI: UPT_EDI(regs) = val; break; \
++              case EBP: UPT_EBP(regs) = val; break; \
++              case ORIG_EAX: UPT_ORIG_EAX(regs) = val; break; \
++              case CS: UPT_CS(regs) = val; break; \
++              case SS: UPT_SS(regs) = val; break; \
++              case DS: UPT_DS(regs) = val; break; \
++              case ES: UPT_ES(regs) = val; break; \
++              case FS: UPT_FS(regs) = val; break; \
++              case GS: UPT_GS(regs) = val; break; \
++              case EFL: UPT_EFLAGS(regs) = val; break; \
++              default :  \
++                      panic("Bad register in UPT_SET : %d\n", reg);  \
++                      break; \
++              } \
++      } while (0)
++
++#define UPT_SET_SYSCALL_RETURN(r, res) \
++      CHOOSE_MODE(SC_SET_SYSCALL_RETURN(UPT_SC(r), (res)), \
++                    REGS_SET_SYSCALL_RETURN((r)->skas.regs, (res)))
++
++#define UPT_RESTART_SYSCALL(r) \
++      CHOOSE_MODE(SC_RESTART_SYSCALL(UPT_SC(r)), \
++                  REGS_RESTART_SYSCALL((r)->skas.regs))
++
++#define UPT_ORIG_SYSCALL(r) UPT_EAX(r)
++#define UPT_SYSCALL_NR(r) UPT_ORIG_EAX(r)
++#define UPT_SYSCALL_RET(r) UPT_EAX(r)
++
++#define UPT_SEGV_IS_FIXABLE(r) \
++      CHOOSE_MODE(SC_SEGV_IS_FIXABLE(UPT_SC(r)), \
++                    REGS_SEGV_IS_FIXABLE(&r->skas))
++
++#define UPT_FAULT_ADDR(r) \
++      CHOOSE_MODE(SC_FAULT_ADDR(UPT_SC(r)), REGS_FAULT_ADDR(&r->skas))
++
++#define UPT_FAULT_WRITE(r) \
++      CHOOSE_MODE(SC_FAULT_WRITE(UPT_SC(r)), REGS_FAULT_WRITE(&r->skas))
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/sysdep-i386/ptrace_user.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/sysdep-i386/ptrace_user.h        1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/sysdep-i386/ptrace_user.h     2005-05-03 22:28:14.356427704 +0300
+@@ -0,0 +1,62 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __SYSDEP_I386_PTRACE_USER_H__
++#define __SYSDEP_I386_PTRACE_USER_H__
++
++#include <asm/ptrace.h>
++
++#define PT_OFFSET(r) ((r) * sizeof(long))
++
++#define PT_SYSCALL_NR(regs) ((regs)[ORIG_EAX])
++#define PT_SYSCALL_NR_OFFSET PT_OFFSET(ORIG_EAX)
++
++#define PT_SYSCALL_ARG1_OFFSET PT_OFFSET(EBX)
++#define PT_SYSCALL_ARG2_OFFSET PT_OFFSET(ECX)
++#define PT_SYSCALL_ARG3_OFFSET PT_OFFSET(EDX)
++#define PT_SYSCALL_ARG4_OFFSET PT_OFFSET(ESI)
++#define PT_SYSCALL_ARG5_OFFSET PT_OFFSET(EDI)
++
++#define PT_SYSCALL_RET_OFFSET PT_OFFSET(EAX)
++
++#define PT_IP_OFFSET PT_OFFSET(EIP)
++#define PT_IP(regs) ((regs)[EIP])
++#define PT_SP(regs) ((regs)[UESP])
++
++#ifndef FRAME_SIZE
++#define FRAME_SIZE (17)
++#endif
++#define FRAME_SIZE_OFFSET (FRAME_SIZE * sizeof(unsigned long))
++
++#define FP_FRAME_SIZE (27)
++#define FPX_FRAME_SIZE (128)
++
++#ifdef PTRACE_GETREGS
++#define UM_HAVE_GETREGS
++#endif
++
++#ifdef PTRACE_SETREGS
++#define UM_HAVE_SETREGS
++#endif
++
++#ifdef PTRACE_GETFPREGS
++#define UM_HAVE_GETFPREGS
++#endif
++
++#ifdef PTRACE_SETFPREGS
++#define UM_HAVE_SETFPREGS
++#endif
++
++#ifdef PTRACE_GETFPXREGS
++#define UM_HAVE_GETFPXREGS
++#endif
++
++#ifdef PTRACE_SETFPXREGS
++#define UM_HAVE_SETFPXREGS
++#endif
++
++extern void update_debugregs(int seq);
++
++#endif
+Index: linux-2.4.29/arch/um/include/sysdep-i386/sigcontext.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/sysdep-i386/sigcontext.h 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/sysdep-i386/sigcontext.h      2005-05-03 22:28:14.357427552 +0300
+@@ -0,0 +1,49 @@
++/* 
++ * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __SYS_SIGCONTEXT_I386_H
++#define __SYS_SIGCONTEXT_I386_H
++
++#include "sc.h"
++
++#define IP_RESTART_SYSCALL(ip) ((ip) -= 2)
++
++#define SC_RESTART_SYSCALL(sc) IP_RESTART_SYSCALL(SC_IP(sc))
++#define SC_SET_SYSCALL_RETURN(sc, result) SC_EAX(sc) = (result)
++
++#define SC_FAULT_ADDR(sc) SC_CR2(sc)
++#define SC_FAULT_TYPE(sc) SC_ERR(sc)
++
++#define FAULT_WRITE(err) (err & 2)
++#define TO_SC_ERR(is_write) ((is_write) ? 2 : 0)
++
++#define SC_FAULT_WRITE(sc) (FAULT_WRITE(SC_ERR(sc)))
++
++#define SC_TRAP_TYPE(sc) SC_TRAPNO(sc)
++
++/* ptrace expects that, at the start of a system call, %eax contains
++ * -ENOSYS, so this makes it so.
++ */
++#define SC_START_SYSCALL(sc) do SC_EAX(sc) = -ENOSYS; while(0)
++
++/* This is Page Fault */
++#define SEGV_IS_FIXABLE(trap) (trap == 14)
++
++#define SC_SEGV_IS_FIXABLE(sc) (SEGV_IS_FIXABLE(SC_TRAPNO(sc)))
++
++extern unsigned long *sc_sigmask(void *sc_ptr);
++extern int sc_get_fpregs(unsigned long buf, void *sc_ptr);
++
++#endif
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/sysdep-i386/syscalls.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/sysdep-i386/syscalls.h   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/sysdep-i386/syscalls.h        2005-05-03 22:28:14.358427400 +0300
+@@ -0,0 +1,61 @@
++/* 
++ * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "asm/unistd.h"
++#include "sysdep/ptrace.h"
++
++typedef long syscall_handler_t(struct pt_regs);
++
++#define EXECUTE_SYSCALL(syscall, regs) \
++      ((long (*)(struct syscall_args)) (*sys_call_table[syscall]))(SYSCALL_ARGS(&regs->regs))
++
++extern syscall_handler_t sys_modify_ldt;
++extern syscall_handler_t old_mmap_i386;
++extern syscall_handler_t old_select;
++extern syscall_handler_t sys_ni_syscall;
++
++#define ARCH_SYSCALLS \
++      [ __NR_mmap ] = old_mmap_i386, \
++      [ __NR_select ] = old_select, \
++      [ __NR_vm86old ] = sys_ni_syscall, \
++        [ __NR_modify_ldt ] = sys_modify_ldt, \
++      [ __NR_lchown32 ] = sys_lchown, \
++      [ __NR_getuid32 ] = sys_getuid, \
++      [ __NR_getgid32 ] = sys_getgid, \
++      [ __NR_geteuid32 ] = sys_geteuid, \
++      [ __NR_getegid32 ] = sys_getegid, \
++      [ __NR_setreuid32 ] = sys_setreuid, \
++      [ __NR_setregid32 ] = sys_setregid, \
++      [ __NR_getgroups32 ] = sys_getgroups, \
++      [ __NR_setgroups32 ] = sys_setgroups, \
++      [ __NR_fchown32 ] = sys_fchown, \
++      [ __NR_setresuid32 ] = sys_setresuid, \
++      [ __NR_getresuid32 ] = sys_getresuid, \
++      [ __NR_setresgid32 ] = sys_setresgid, \
++      [ __NR_getresgid32 ] = sys_getresgid, \
++      [ __NR_chown32 ] = sys_chown, \
++      [ __NR_setuid32 ] = sys_setuid, \
++      [ __NR_setgid32 ] = sys_setgid, \
++      [ __NR_setfsuid32 ] = sys_setfsuid, \
++      [ __NR_setfsgid32 ] = sys_setfsgid, \
++      [ __NR_pivot_root ] = sys_pivot_root, \
++      [ __NR_mincore ] = sys_mincore, \
++      [ __NR_madvise ] = sys_madvise, \
++        [ 222 ] = sys_ni_syscall, 
++        
++/* 222 doesn't yet have a name in include/asm-i386/unistd.h */
++
++#define LAST_ARCH_SYSCALL 222
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/sysdep-ia64/ptrace.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/sysdep-ia64/ptrace.h     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/sysdep-ia64/ptrace.h  2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,26 @@
++/* 
++ * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __SYSDEP_IA64_PTRACE_H
++#define __SYSDEP_IA64_PTRACE_H
++
++struct sys_pt_regs {
++  int foo;
++};
++
++#define EMPTY_REGS { 0 }
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/sysdep-ia64/sigcontext.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/sysdep-ia64/sigcontext.h 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/sysdep-ia64/sigcontext.h      2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,20 @@
++/* 
++ * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __SYSDEP_IA64_SIGCONTEXT_H
++#define __SYSDEP_IA64_SIGCONTEXT_H
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/sysdep-ia64/syscalls.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/sysdep-ia64/syscalls.h   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/sysdep-ia64/syscalls.h        2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,20 @@
++/* 
++ * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __SYSDEP_IA64_SYSCALLS_H
++#define __SYSDEP_IA64_SYSCALLS_H
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/sysdep-ppc/ptrace.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/sysdep-ppc/ptrace.h      1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/sysdep-ppc/ptrace.h   2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,104 @@
++/* 
++ * Licensed under the GPL
++ */
++
++#ifndef __SYS_PTRACE_PPC_H
++#define __SYS_PTRACE_PPC_H
++
++#include "linux/config.h"
++#include "linux/types.h"
++
++/* the following taken from <asm-ppc/ptrace.h> */
++
++#ifdef CONFIG_PPC64
++#define PPC_REG unsigned long /*long*/
++#else
++#define PPC_REG unsigned long
++#endif
++struct sys_pt_regs_s {
++      PPC_REG gpr[32];
++      PPC_REG nip;
++      PPC_REG msr;
++      PPC_REG orig_gpr3;      /* Used for restarting system calls */
++      PPC_REG ctr;
++      PPC_REG link;
++      PPC_REG xer;
++      PPC_REG ccr;
++      PPC_REG mq;             /* 601 only (not used at present) */
++                              /* Used on APUS to hold IPL value. */
++      PPC_REG trap;           /* Reason for being here */
++      PPC_REG dar;            /* Fault registers */
++      PPC_REG dsisr;
++      PPC_REG result;         /* Result of a system call */
++};
++
++#define NUM_REGS (sizeof(struct sys_pt_regs_s) / sizeof(PPC_REG))
++
++struct sys_pt_regs {
++    PPC_REG regs[sizeof(struct sys_pt_regs_s) / sizeof(PPC_REG)];
++};
++
++#define UM_MAX_REG (PT_FPR0)
++#define UM_MAX_REG_OFFSET (UM_MAX_REG * sizeof(PPC_REG))
++
++#define EMPTY_REGS { { [ 0 ... NUM_REGS - 1] = 0 } }
++
++#define UM_REG(r, n) ((r)->regs[n])
++
++#define UM_SYSCALL_RET(r) UM_REG(r, PT_R3)
++#define UM_SP(r) UM_REG(r, PT_R1)
++#define UM_IP(r) UM_REG(r, PT_NIP)
++#define UM_ELF_ZERO(r) UM_REG(r, PT_FPSCR)
++#define UM_SYSCALL_NR(r) UM_REG(r, PT_R0)
++#define UM_SYSCALL_ARG1(r) UM_REG(r, PT_ORIG_R3)
++#define UM_SYSCALL_ARG2(r) UM_REG(r, PT_R4)
++#define UM_SYSCALL_ARG3(r) UM_REG(r, PT_R5)
++#define UM_SYSCALL_ARG4(r) UM_REG(r, PT_R6)
++#define UM_SYSCALL_ARG5(r) UM_REG(r, PT_R7)
++#define UM_SYSCALL_ARG6(r) UM_REG(r, PT_R8)
++
++#define UM_SYSCALL_NR_OFFSET (PT_R0 * sizeof(PPC_REG))
++#define UM_SYSCALL_RET_OFFSET (PT_R3 * sizeof(PPC_REG))
++#define UM_SYSCALL_ARG1_OFFSET (PT_R3 * sizeof(PPC_REG))
++#define UM_SYSCALL_ARG2_OFFSET (PT_R4 * sizeof(PPC_REG))
++#define UM_SYSCALL_ARG3_OFFSET (PT_R5 * sizeof(PPC_REG))
++#define UM_SYSCALL_ARG4_OFFSET (PT_R6 * sizeof(PPC_REG))
++#define UM_SYSCALL_ARG5_OFFSET (PT_R7 * sizeof(PPC_REG))
++#define UM_SYSCALL_ARG6_OFFSET (PT_R8 * sizeof(PPC_REG))
++#define UM_SP_OFFSET (PT_R1 * sizeof(PPC_REG))
++#define UM_IP_OFFSET (PT_NIP * sizeof(PPC_REG))
++#define UM_ELF_ZERO_OFFSET (PT_R3 * sizeof(PPC_REG))
++
++#define UM_SET_SYSCALL_RETURN(_regs, result)          \
++do {                                                    \
++        if (result < 0) {                             \
++              (_regs)->regs[PT_CCR] |= 0x10000000;    \
++              UM_SYSCALL_RET((_regs)) = -result;      \
++        } else {                                      \
++              UM_SYSCALL_RET((_regs)) = result;       \
++        }                                               \
++} while(0)
++
++extern void shove_aux_table(unsigned long sp);
++#define UM_FIX_EXEC_STACK(sp) shove_aux_table(sp);
++
++/* These aren't actually defined.  The undefs are just to make sure
++ * everyone's clear on the concept.
++ */
++#undef UML_HAVE_GETREGS
++#undef UML_HAVE_GETFPREGS
++#undef UML_HAVE_SETREGS
++#undef UML_HAVE_SETFPREGS
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/sysdep-ppc/sigcontext.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/sysdep-ppc/sigcontext.h  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/sysdep-ppc/sigcontext.h       2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,62 @@
++/* 
++ * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __SYS_SIGCONTEXT_PPC_H
++#define __SYS_SIGCONTEXT_PPC_H
++
++#define DSISR_WRITE 0x02000000
++
++#define SC_FAULT_ADDR(sc) ({ \
++              struct sigcontext *_sc = (sc); \
++              long retval = -1; \
++              switch (_sc->regs->trap) { \
++              case 0x300: \
++                      /* data exception */ \
++                      retval = _sc->regs->dar; \
++                      break; \
++              case 0x400: \
++                      /* instruction exception */ \
++                      retval = _sc->regs->nip; \
++                      break; \
++              default: \
++                      panic("SC_FAULT_ADDR: unhandled trap type\n"); \
++              } \
++              retval; \
++      })
++
++#define SC_FAULT_WRITE(sc) ({ \
++              struct sigcontext *_sc = (sc); \
++              long retval = -1; \
++              switch (_sc->regs->trap) { \
++              case 0x300: \
++                      /* data exception */ \
++                      retval = !!(_sc->regs->dsisr & DSISR_WRITE); \
++                      break; \
++              case 0x400: \
++                      /* instruction exception: not a write */ \
++                      retval = 0; \
++                      break; \
++              default: \
++                      panic("SC_FAULT_ADDR: unhandled trap type\n"); \
++              } \
++              retval; \
++      })
++
++#define SC_IP(sc) ((sc)->regs->nip)
++#define SC_SP(sc) ((sc)->regs->gpr[1])
++#define SEGV_IS_FIXABLE(sc) (1)
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/sysdep-ppc/syscalls.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/sysdep-ppc/syscalls.h    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/sysdep-ppc/syscalls.h 2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,50 @@
++/* 
++ * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++typedef long syscall_handler_t(unsigned long arg1, unsigned long arg2,
++                             unsigned long arg3, unsigned long arg4,
++                             unsigned long arg5, unsigned long arg6);
++
++#define EXECUTE_SYSCALL(syscall, regs) \
++        (*sys_call_table[syscall])(UM_SYSCALL_ARG1(&regs), \
++                                 UM_SYSCALL_ARG2(&regs), \
++                                 UM_SYSCALL_ARG3(&regs), \
++                                 UM_SYSCALL_ARG4(&regs), \
++                                 UM_SYSCALL_ARG5(&regs), \
++                                 UM_SYSCALL_ARG6(&regs))
++
++extern syscall_handler_t sys_mincore;
++extern syscall_handler_t sys_madvise;
++
++/* old_mmap needs the correct prototype since syscall_kern.c includes
++ * this file.
++ */
++int old_mmap(unsigned long addr, unsigned long len,
++           unsigned long prot, unsigned long flags,
++           unsigned long fd, unsigned long offset);
++
++#define ARCH_SYSCALLS \
++      [ __NR_modify_ldt ] = sys_ni_syscall, \
++      [ __NR_pciconfig_read ] = sys_ni_syscall, \
++      [ __NR_pciconfig_write ] = sys_ni_syscall, \
++      [ __NR_pciconfig_iobase ] = sys_ni_syscall, \
++      [ __NR_pivot_root ] = sys_ni_syscall, \
++      [ __NR_multiplexer ] = sys_ni_syscall, \
++      [ __NR_mmap ] = old_mmap, \
++      [ __NR_madvise ] = sys_madvise, \
++      [ __NR_mincore ] = sys_mincore, 
++
++#define LAST_ARCH_SYSCALL __NR_mincore
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/sysrq.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/sysrq.h  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/sysrq.h       2005-05-03 22:28:14.364426488 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_SYSRQ_H
++#define __UM_SYSRQ_H
++
++extern void show_trace(unsigned long *stack);
++
++#endif
+Index: linux-2.4.29/arch/um/include/tempfile.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/tempfile.h       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/tempfile.h    2005-05-03 22:28:14.365426336 +0300
+@@ -0,0 +1,21 @@
++/* 
++ * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __TEMPFILE_H__
++#define __TEMPFILE_H__
++
++extern int make_tempfile(const char *template, char **tempname, int do_unlink);
++
++#endif
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/time_user.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/time_user.h      1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/time_user.h   2005-05-03 22:28:14.366426184 +0300
+@@ -0,0 +1,18 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __TIME_USER_H__
++#define __TIME_USER_H__
++
++extern void timer(void);
++extern void switch_timers(int to_real);
++extern void set_interval(int timer_type);
++extern void idle_sleep(int secs);
++extern void enable_timer(void);
++extern void disable_timer(void);
++extern unsigned long time_lock(void);
++extern void time_unlock(unsigned long);
++
++#endif
+Index: linux-2.4.29/arch/um/include/tlb.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/tlb.h    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/tlb.h 2005-05-03 22:28:14.367426032 +0300
+@@ -0,0 +1,23 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __TLB_H__
++#define __TLB_H__
++
++extern void mprotect_kernel_vm(int w);
++extern void force_flush_all(void);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/ubd_user.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/ubd_user.h       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/ubd_user.h    2005-05-03 22:28:14.368425880 +0300
+@@ -0,0 +1,79 @@
++/* 
++ * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
++ * Copyright (C) 2001 RidgeRun, Inc (glonnon@ridgerun.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __UM_UBD_USER_H
++#define __UM_UBD_USER_H
++
++#include "os.h"
++
++enum ubd_req { UBD_READ, UBD_WRITE, UBD_MMAP };
++
++struct io_thread_req {
++      enum ubd_req op;
++      int fds[2];
++      unsigned long offsets[2];
++      unsigned long long offset;
++      unsigned long length;
++      char *buffer;
++      int sectorsize;
++      unsigned long sector_mask;
++      unsigned long long cow_offset;
++      unsigned long bitmap_words[2];
++      int map_fd;
++      unsigned long long map_offset;
++      int error;
++};
++
++extern int open_ubd_file(char *file, struct openflags *openflags, 
++                       char **backing_file_out, int *bitmap_offset_out, 
++                       unsigned long *bitmap_len_out, int *data_offset_out,
++                       int *create_cow_out);
++extern int create_cow_file(char *cow_file, char *backing_file, 
++                         struct openflags flags, int sectorsize, 
++                         int alignment, int *bitmap_offset_out, 
++                         unsigned long *bitmap_len_out,
++                         int *data_offset_out);
++extern int read_cow_bitmap(int fd, void *buf, int offset, int len);
++extern int read_ubd_fs(int fd, void *buffer, int len);
++extern int write_ubd_fs(int fd, char *buffer, int len);
++extern int start_io_thread(unsigned long sp, int *fds_out);
++extern void do_io(struct io_thread_req *req);
++
++static inline int ubd_test_bit(__u64 bit, unsigned char *data)
++{
++      __u64 n;
++      int bits, off;
++
++      bits = sizeof(data[0]) * 8;
++      n = bit / bits;
++      off = bit % bits;
++      return((data[n] & (1 << off)) != 0);
++}
++
++static inline void ubd_set_bit(__u64 bit, unsigned char *data)
++{
++      __u64 n;
++      int bits, off;
++
++      bits = sizeof(data[0]) * 8;
++      n = bit / bits;
++      off = bit % bits;
++      data[n] |= (1 << off);
++}
++
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/umid.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/umid.h   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/umid.h        2005-05-03 22:28:14.368425880 +0300
+@@ -0,0 +1,22 @@
++/*
++ * Copyright (C) 2001 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __UMID_H__
++#define __UMID_H__
++
++extern int umid_file_name(char *name, char *buf, int len);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/uml_uaccess.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/uml_uaccess.h    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/uml_uaccess.h 2005-05-03 22:28:14.369425728 +0300
+@@ -0,0 +1,28 @@
++/*
++ * Copyright (C) 2001 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __UML_UACCESS_H__
++#define __UML_UACCESS_H__
++
++extern int __do_copy_to_user(void *to, const void *from, int n,
++                                void **fault_addr, void **fault_catcher);
++extern unsigned long __do_user_copy(void *to, const void *from, int n,
++                                  void **fault_addr, void **fault_catcher,
++                                  void (*op)(void *to, const void *from,
++                                             int n), int *faulted_out);
++void __do_copy(void *to, const void *from, int n);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/um_mmu.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/um_mmu.h 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/um_mmu.h      2005-05-03 22:28:14.370425576 +0300
+@@ -0,0 +1,40 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __ARCH_UM_MMU_H
++#define __ARCH_UM_MMU_H
++
++#include "linux/config.h"
++#include "choose-mode.h"
++
++#ifdef CONFIG_MODE_TT
++#include "../kernel/tt/include/mmu.h"
++#endif
++
++#ifdef CONFIG_MODE_SKAS
++#include "../kernel/skas/include/mmu.h"
++#endif
++
++typedef union {
++#ifdef CONFIG_MODE_TT
++      struct mmu_context_tt tt;
++#endif
++#ifdef CONFIG_MODE_SKAS
++      struct mmu_context_skas skas;
++#endif
++} mm_context_t;
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/umn.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/umn.h    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/umn.h 2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,27 @@
++/* 
++ * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __UMN_H
++#define __UMN_H
++
++extern int open_umn_tty(int *slave_out, int *slipno_out);
++extern void close_umn_tty(int master, int slave);
++extern int umn_send_packet(int fd, void *data, int len);
++extern int set_umn_addr(int fd, char *addr, char *ptp_addr);
++extern void slip_unesc(unsigned char s);
++extern void umn_read(int fd);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/um_uaccess.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/um_uaccess.h     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/um_uaccess.h  2005-05-03 22:28:14.372425272 +0300
+@@ -0,0 +1,124 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __ARCH_UM_UACCESS_H
++#define __ARCH_UM_UACCESS_H
++
++#include "linux/config.h"
++#include "choose-mode.h"
++
++#ifdef CONFIG_MODE_TT
++#include "../kernel/tt/include/uaccess.h"
++#endif
++
++#ifdef CONFIG_MODE_SKAS
++#include "../kernel/skas/include/uaccess.h"
++#endif
++
++#define access_ok(type, addr, size) \
++      CHOOSE_MODE_PROC(access_ok_tt, access_ok_skas, type, addr, size)
++
++static inline int verify_area(int type, const void * addr, unsigned long size)
++{
++      return(CHOOSE_MODE_PROC(verify_area_tt, verify_area_skas, type, addr,
++                              size));
++}
++
++static inline int copy_from_user(void *to, const void *from, int n)
++{
++      return(CHOOSE_MODE_PROC(copy_from_user_tt, copy_from_user_skas, to,
++                              from, n));
++}
++
++static inline int copy_to_user(void *to, const void *from, int n)
++{
++      return(CHOOSE_MODE_PROC(copy_to_user_tt, copy_to_user_skas, to, 
++                              from, n));
++}
++
++/*
++ * strncpy_from_user: - Copy a NUL terminated string from userspace.
++ * @dst:   Destination address, in kernel space.  This buffer must be at
++ *         least @count bytes long.
++ * @src:   Source address, in user space.
++ * @count: Maximum number of bytes to copy, including the trailing NUL.
++ * 
++ * Copies a NUL-terminated string from userspace to kernel space.
++ *
++ * On success, returns the length of the string (not including the trailing
++ * NUL).
++ *
++ * If access to userspace fails, returns -EFAULT (some data may have been
++ * copied).
++ *
++ * If @count is smaller than the length of the string, copies @count bytes
++ * and returns @count.
++ */
++
++static inline int strncpy_from_user(char *dst, const char *src, int count)
++{
++      return(CHOOSE_MODE_PROC(strncpy_from_user_tt, strncpy_from_user_skas,
++                              dst, src, count));
++}
++
++/*
++ * __clear_user: - Zero a block of memory in user space, with less checking.
++ * @to:   Destination address, in user space.
++ * @n:    Number of bytes to zero.
++ *
++ * Zero a block of memory in user space.  Caller must check
++ * the specified block with access_ok() before calling this function.
++ *
++ * Returns number of bytes that could not be cleared.
++ * On success, this will be zero.
++ */
++static inline int __clear_user(void *mem, int len)
++{
++      return(CHOOSE_MODE_PROC(__clear_user_tt, __clear_user_skas, mem, len));
++}
++
++/*
++ * clear_user: - Zero a block of memory in user space.
++ * @to:   Destination address, in user space.
++ * @n:    Number of bytes to zero.
++ *
++ * Zero a block of memory in user space.
++ *
++ * Returns number of bytes that could not be cleared.
++ * On success, this will be zero.
++ */
++static inline int clear_user(void *mem, int len)
++{
++      return(CHOOSE_MODE_PROC(clear_user_tt, clear_user_skas, mem, len));
++}
++
++/*
++ * strlen_user: - Get the size of a string in user space.
++ * @str: The string to measure.
++ * @n:   The maximum valid length
++ *
++ * Get the size of a NUL-terminated string in user space.
++ *
++ * Returns the size of the string INCLUDING the terminating NUL.
++ * On exception, returns 0.
++ * If the string is too long, returns a value greater than @n.
++ */
++static inline int strnlen_user(const void *str, int len)
++{
++      return(CHOOSE_MODE_PROC(strnlen_user_tt, strnlen_user_skas, str, len));
++}
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/user.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/user.h   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/user.h        2005-05-03 22:28:14.373425120 +0300
+@@ -0,0 +1,31 @@
++/* 
++ * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __USER_H__
++#define __USER_H__
++
++extern void panic(const char *fmt, ...);
++extern int printk(const char *fmt, ...);
++extern void schedule(void);
++extern void *um_kmalloc(int size);
++extern void *um_kmalloc_atomic(int size);
++extern void kfree(void *ptr);
++extern int in_aton(char *str);
++extern int open_gdb_chan(void);
++extern void *um_vmalloc(int size);
++extern void vfree(void *ptr);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/include/user_util.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/include/user_util.h      1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/include/user_util.h   2005-05-03 22:28:14.374424968 +0300
+@@ -0,0 +1,105 @@
++/* 
++ * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __USER_UTIL_H__
++#define __USER_UTIL_H__
++
++#include "sysdep/ptrace.h"
++
++#define CATCH_EINTR(expr) while (((expr) < 0) && (errno == EINTR))
++
++extern int mode_tt;
++
++extern int grantpt(int __fd);
++extern int unlockpt(int __fd);
++extern char *ptsname(int __fd);
++
++struct cpu_task {
++      int pid;
++      void *task;
++};
++
++extern struct cpu_task cpu_tasks[];
++
++struct signal_info {
++      void (*handler)(int, union uml_pt_regs *);
++      int is_irq;
++};
++
++extern struct signal_info sig_info[];
++
++extern unsigned long low_physmem;
++extern unsigned long high_physmem;
++extern unsigned long uml_physmem;
++extern unsigned long uml_reserved;
++extern unsigned long end_vm;
++extern unsigned long start_vm;
++extern unsigned long highmem;
++
++extern char host_info[];
++
++extern char saved_command_line[];
++extern char command_line[];
++
++extern char *tempdir;
++
++extern unsigned long _stext, _etext, _sdata, _edata, __bss_start, _end;
++extern unsigned long _unprotected_end;
++extern unsigned long brk_start;
++
++extern int pty_output_sigio;
++extern int pty_close_sigio;
++
++extern void stop(void);
++extern void stack_protections(unsigned long address);
++extern void task_protections(unsigned long address);
++extern int wait_for_stop(int pid, int sig, int cont_type, void *relay);
++extern void *add_signal_handler(int sig, void (*handler)(int));
++extern int start_fork_tramp(void *arg, unsigned long temp_stack, 
++                          int clone_flags, int (*tramp)(void *));
++extern int linux_main(int argc, char **argv);
++extern void set_cmdline(char *cmd);
++extern void input_cb(void (*proc)(void *), void *arg, int arg_len);
++extern int get_pty(void);
++extern void *um_kmalloc(int size);
++extern int switcheroo(int fd, int prot, void *from, void *to, int size);
++extern void setup_machinename(char *machine_out);
++extern void setup_hostinfo(void);
++extern void add_arg(char *cmd_line, char *arg);
++extern void init_new_thread_stack(void *sig_stack, void (*usr1_handler)(int));
++extern void init_new_thread_signals(int altstack);
++extern void do_exec(int old_pid, int new_pid);
++extern void tracer_panic(char *msg, ...);
++extern char *get_umid(int only_if_set);
++extern void do_longjmp(void *p, int val);
++extern int detach(int pid, int sig);
++extern int attach(int pid);
++extern void kill_child_dead(int pid);
++extern int cont(int pid);
++extern void check_ptrace(void);
++extern void check_sigio(void);
++extern int run_kernel_thread(int (*fn)(void *), void *arg, void **jmp_ptr);
++extern void write_sigio_workaround(void);
++extern void arch_check_bugs(void);
++extern int cpu_feature(char *what, char *buf, int len);
++extern int arch_handle_signal(int sig, union uml_pt_regs *regs);
++extern int arch_fixup(unsigned long address, void *sc_ptr);
++extern int can_do_skas(void);
++extern void arch_init_thread(void);
++extern int setjmp_wrapper(void (*proc)(void *, void *), ...);
++extern int raw(int fd);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/checksum.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/checksum.c        1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/checksum.c     2005-05-03 22:28:14.375424816 +0300
+@@ -0,0 +1,42 @@
++#include "asm/uaccess.h"
++#include "linux/errno.h"
++
++extern unsigned int arch_csum_partial(const char *buff, int len, int sum);
++
++extern unsigned int csum_partial(char *buff, int len, int sum)
++{
++      return(arch_csum_partial(buff, len, sum));
++}
++
++unsigned int csum_partial_copy_to(const char *src, char *dst, int len, 
++                                int sum, int *err_ptr)
++{
++      if(copy_to_user(dst, src, len)){
++              *err_ptr = -EFAULT;
++              return(-1);
++      }
++
++      return(arch_csum_partial(src, len, sum));
++}
++
++unsigned int csum_partial_copy_from(const char *src, char *dst, int len, 
++                                  int sum, int *err_ptr)
++{
++      if(copy_from_user(dst, src, len)){
++              *err_ptr = -EFAULT;
++              return(-1);
++      }
++
++      return(arch_csum_partial(dst, len, sum));
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/common.ld.in
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/common.ld.in      1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/common.ld.in   2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,53 @@
++  .kstrtab : { *(.kstrtab) }
++
++  . = ALIGN(16);              /* Exception table */
++  __start___ex_table = .;
++  __ex_table : { *(__ex_table) }
++  __stop___ex_table = .;
++
++  __start___ksymtab = .;      /* Kernel symbol table */
++  __ksymtab : { *(__ksymtab) }
++  __stop___ksymtab = .;
++
++  .unprotected : { *(.unprotected) }
++  . = ALIGN(4096);
++  PROVIDE (_unprotected_end = .);
++
++  . = ALIGN(4096);
++  __uml_setup_start = .;
++  .uml.setup.init : { *(.uml.setup.init) }
++  __uml_setup_end = .;
++  __uml_help_start = .;
++  .uml.help.init : { *(.uml.help.init) }
++  __uml_help_end = .;
++  __uml_postsetup_start = .;
++  .uml.postsetup.init : { *(.uml.postsetup.init) }
++  __uml_postsetup_end = .;
++  __setup_start = .;
++  .setup.init : { *(.setup.init) }
++  __setup_end = .;
++  __initcall_start = .;
++  .initcall.init : { *(.initcall.init) }
++  __initcall_end = .;
++  __uml_initcall_start = .;
++  .uml.initcall.init : { *(.uml.initcall.init) }
++  __uml_initcall_end = .;
++  __init_end = .;
++  __exitcall_begin = .;
++  .exitcall : { *(.exitcall.exit) }
++  __exitcall_end = .;
++  __uml_exitcall_begin = .;
++  .uml.exitcall : { *(.uml.exitcall.exit) }
++  __uml_exitcall_end = .;
++
++  __preinit_array_start = .;
++  .preinit_array : { *(.preinit_array) }
++  __preinit_array_end = .;
++  __init_array_start = .;
++  .init_array : { *(.init_array) }
++  __init_array_end = .;
++  __fini_array_start = .;
++  .fini_array : { *(.fini_array) }
++  __fini_array_end = .;
++
++  .data.init : { *(.data.init) }
+Index: linux-2.4.29/arch/um/kernel/config.c.in
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/config.c.in       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/config.c.in    2005-05-03 22:28:14.406420104 +0300
+@@ -0,0 +1,32 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <stdio.h>
++#include <stdlib.h>
++#include "init.h"
++
++static __initdata char *config = "CONFIG";
++
++static int __init print_config(char *line, int *add)
++{
++      printf("%s", config);
++      exit(0);
++}
++
++__uml_setup("--showconfig", print_config,
++"--showconfig\n"
++"    Prints the config file that this UML binary was generated from.\n\n"
++);
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/dyn_link.ld.in
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/dyn_link.ld.in    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/dyn_link.ld.in 2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,169 @@
++OUTPUT_FORMAT("ELF_FORMAT")
++OUTPUT_ARCH(ELF_ARCH)
++ENTRY(_start)
++
++SECTIONS
++{
++  . = START() + SIZEOF_HEADERS;
++  .interp         : { *(.interp) }
++  __binary_start = .;
++  . = ALIGN(4096);            /* Init code and data */
++  _stext = .;
++  __init_begin = .;
++  .text.init : { *(.text.init) }
++
++  . = ALIGN(4096);
++
++  /* Read-only sections, merged into text segment: */
++  .hash           : { *(.hash) }
++  .dynsym         : { *(.dynsym) }
++  .dynstr         : { *(.dynstr) }
++  .gnu.version    : { *(.gnu.version) }
++  .gnu.version_d  : { *(.gnu.version_d) }
++  .gnu.version_r  : { *(.gnu.version_r) }
++  .rel.init       : { *(.rel.init) }
++  .rela.init      : { *(.rela.init) }
++  .rel.text       : { *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) }
++  .rela.text      : { *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) }
++  .rel.fini       : { *(.rel.fini) }
++  .rela.fini      : { *(.rela.fini) }
++  .rel.rodata     : { *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) }
++  .rela.rodata    : { *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) }
++  .rel.data       : { *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) }
++  .rela.data      : { *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) }
++  .rel.tdata    : { *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) }
++  .rela.tdata   : { *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) }
++  .rel.tbss     : { *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) }
++  .rela.tbss    : { *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) }
++  .rel.ctors      : { *(.rel.ctors) }
++  .rela.ctors     : { *(.rela.ctors) }
++  .rel.dtors      : { *(.rel.dtors) }
++  .rela.dtors     : { *(.rela.dtors) }
++  .rel.got        : { *(.rel.got) }
++  .rela.got       : { *(.rela.got) }
++  .rel.bss        : { *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) }
++  .rela.bss       : { *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) }
++  .rel.plt        : { *(.rel.plt) }
++  .rela.plt       : { *(.rela.plt) }
++  .init           : {
++    KEEP (*(.init))
++  } =0x90909090
++  .plt            : { *(.plt) }
++  .text           : {
++    *(.text .stub .text.* .gnu.linkonce.t.*)
++    /* .gnu.warning sections are handled specially by elf32.em.  */
++    *(.gnu.warning)
++  } =0x90909090
++  .fini           : {
++    KEEP (*(.fini))
++  } =0x90909090
++
++  PROVIDE (__etext = .);
++  PROVIDE (_etext = .);
++  PROVIDE (etext = .);
++  .rodata         : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
++  .rodata1        : { *(.rodata1) }
++  .eh_frame_hdr : { *(.eh_frame_hdr) }
++
++
++  . = ALIGN(4096);
++  PROVIDE (_sdata = .);
++
++include(`arch/um/kernel/common.ld.in')
++
++  /* Ensure the __preinit_array_start label is properly aligned.  We
++     could instead move the label definition inside the section, but
++     the linker would then create the section even if it turns out to
++     be empty, which isn't pretty.  */
++  . = ALIGN(32 / 8);
++  .preinit_array     : { *(.preinit_array) }
++  .init_array     : { *(.init_array) }
++  .fini_array     : { *(.fini_array) }
++  .data           : {
++    . = ALIGN(KERNEL_STACK_SIZE);             /* init_task */
++    *(.data.init_task)
++    *(.data .data.* .gnu.linkonce.d.*)
++    SORT(CONSTRUCTORS)
++  }
++  .data1          : { *(.data1) }
++  .tdata        : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
++  .tbss                 : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
++  .eh_frame       : { KEEP (*(.eh_frame)) }
++  .gcc_except_table   : { *(.gcc_except_table) }
++  .dynamic        : { *(.dynamic) }
++  .ctors          : {
++    /* gcc uses crtbegin.o to find the start of
++       the constructors, so we make sure it is
++       first.  Because this is a wildcard, it
++       doesn't matter if the user does not
++       actually link against crtbegin.o; the
++       linker won't look for a file to match a
++       wildcard.  The wildcard also means that it
++       doesn't matter which directory crtbegin.o
++       is in.  */
++    KEEP (*crtbegin.o(.ctors))
++    /* We don't want to include the .ctor section from
++       from the crtend.o file until after the sorted ctors.
++       The .ctor section from the crtend file contains the
++       end of ctors marker and it must be last */
++    KEEP (*(EXCLUDE_FILE (*crtend.o ) .ctors))
++    KEEP (*(SORT(.ctors.*)))
++    KEEP (*(.ctors))
++  }
++  .dtors          : {
++    KEEP (*crtbegin.o(.dtors))
++    KEEP (*(EXCLUDE_FILE (*crtend.o ) .dtors))
++    KEEP (*(SORT(.dtors.*)))
++    KEEP (*(.dtors))
++  }
++  .jcr            : { KEEP (*(.jcr)) }
++  .got            : { *(.got.plt) *(.got) }
++  _edata = .;
++  PROVIDE (edata = .);
++  __bss_start = .;
++  .bss            : {
++   *(.dynbss)
++   *(.bss .bss.* .gnu.linkonce.b.*)
++   *(COMMON)
++   /* Align here to ensure that the .bss section occupies space up to
++      _end.  Align after .bss to ensure correct alignment even if the
++      .bss section disappears because there are no input sections.  */
++   . = ALIGN(32 / 8);
++  . = ALIGN(32 / 8);
++  }
++  _end = .;
++  PROVIDE (end = .);
++   /* Stabs debugging sections.  */
++  .stab          0 : { *(.stab) }
++  .stabstr       0 : { *(.stabstr) }
++  .stab.excl     0 : { *(.stab.excl) }
++  .stab.exclstr  0 : { *(.stab.exclstr) }
++  .stab.index    0 : { *(.stab.index) }
++  .stab.indexstr 0 : { *(.stab.indexstr) }
++  .comment       0 : { *(.comment) }
++  /* DWARF debug sections.
++     Symbols in the DWARF debugging sections are relative to the beginning
++     of the section so we begin them at 0.  */
++  /* DWARF 1 */
++  .debug          0 : { *(.debug) }
++  .line           0 : { *(.line) }
++  /* GNU DWARF 1 extensions */
++  .debug_srcinfo  0 : { *(.debug_srcinfo) }
++  .debug_sfnames  0 : { *(.debug_sfnames) }
++  /* DWARF 1.1 and DWARF 2 */
++  .debug_aranges  0 : { *(.debug_aranges) }
++  .debug_pubnames 0 : { *(.debug_pubnames) }
++  /* DWARF 2 */
++  .debug_info     0 : { *(.debug_info .gnu.linkonce.wi.*) }
++  .debug_abbrev   0 : { *(.debug_abbrev) }
++  .debug_line     0 : { *(.debug_line) }
++  .debug_frame    0 : { *(.debug_frame) }
++  .debug_str      0 : { *(.debug_str) }
++  .debug_loc      0 : { *(.debug_loc) }
++  .debug_macinfo  0 : { *(.debug_macinfo) }
++  /* SGI/MIPS DWARF 2 extensions */
++  .debug_weaknames 0 : { *(.debug_weaknames) }
++  .debug_funcnames 0 : { *(.debug_funcnames) }
++  .debug_typenames 0 : { *(.debug_typenames) }
++  .debug_varnames  0 : { *(.debug_varnames) }
++}
+Index: linux-2.4.29/arch/um/kernel/exec_kern.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/exec_kern.c       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/exec_kern.c    2005-05-03 22:28:14.408419800 +0300
+@@ -0,0 +1,86 @@
++/* 
++ * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/slab.h"
++#include "linux/smp_lock.h"
++#include "asm/ptrace.h"
++#include "asm/pgtable.h"
++#include "asm/pgalloc.h"
++#include "asm/uaccess.h"
++#include "user_util.h"
++#include "kern_util.h"
++#include "mem_user.h"
++#include "kern.h"
++#include "irq_user.h"
++#include "tlb.h"
++#include "2_5compat.h"
++#include "os.h"
++#include "time_user.h"
++#include "choose-mode.h"
++#include "mode_kern.h"
++
++void flush_thread(void)
++{
++      CHOOSE_MODE(flush_thread_tt(), flush_thread_skas());
++}
++
++void start_thread(struct pt_regs *regs, unsigned long eip, unsigned long esp)
++{
++      CHOOSE_MODE_PROC(start_thread_tt, start_thread_skas, regs, eip, esp);
++}
++
++extern void log_exec(char **argv, void *tty);
++
++static int execve1(char *file, char **argv, char **env)
++{
++        int error;
++
++#ifdef CONFIG_TTY_LOG
++      log_exec(argv, current->tty);
++#endif
++        error = do_execve(file, argv, env, &current->thread.regs);
++        if (error == 0){
++                current->ptrace &= ~PT_DTRACE;
++                set_cmdline(current_cmd());
++        }
++        return(error);
++}
++
++int um_execve(char *file, char **argv, char **env)
++{
++      int err;
++
++      err = execve1(file, argv, env);
++      if(!err) 
++              do_longjmp(current->thread.exec_buf, 1);
++      return(err);
++}
++
++int sys_execve(char *file, char **argv, char **env)
++{
++      int error;
++      char *filename;
++
++      lock_kernel();
++      filename = getname((char *) file);
++      error = PTR_ERR(filename);
++      if (IS_ERR(filename)) goto out;
++      error = execve1(filename, argv, env);
++      putname(filename);
++ out:
++      unlock_kernel();
++      return(error);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/exitcode.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/exitcode.c        1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/exitcode.c     2005-05-03 22:28:14.409419648 +0300
+@@ -0,0 +1,73 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/init.h"
++#include "linux/ctype.h"
++#include "linux/proc_fs.h"
++#include "asm/uaccess.h"
++
++/* If read and write race, the read will still atomically read a valid
++ * value.
++ */
++int uml_exitcode = 0;
++
++static int read_proc_exitcode(char *page, char **start, off_t off,
++                            int count, int *eof, void *data)
++{
++      int len;
++
++      len = sprintf(page, "%d\n", uml_exitcode);
++      len -= off;
++      if(len <= off+count) *eof = 1;
++      *start = page + off;
++      if(len > count) len = count;
++      if(len < 0) len = 0;
++      return(len);
++}
++
++static int write_proc_exitcode(struct file *file, const char *buffer,
++                             unsigned long count, void *data)
++{
++      char *end, buf[sizeof("nnnnn\0")];
++      int tmp;
++
++      if(copy_from_user(buf, buffer, count))
++              return(-EFAULT);
++      tmp = simple_strtol(buf, &end, 0);
++      if((*end != '\0') && !isspace(*end))
++              return(-EINVAL);
++      uml_exitcode = tmp;
++      return(count);
++}
++
++static int make_proc_exitcode(void)
++{
++      struct proc_dir_entry *ent;
++
++      ent = create_proc_entry("exitcode", 0600, &proc_root);
++      if(ent == NULL){
++              printk("make_proc_exitcode : Failed to register "
++                     "/proc/exitcode\n");
++              return(0);
++      }
++
++      ent->read_proc = read_proc_exitcode;
++      ent->write_proc = write_proc_exitcode;
++      
++      return(0);
++}
++
++__initcall(make_proc_exitcode);
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/filehandle.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/filehandle.c      1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/filehandle.c   2005-05-03 22:28:14.410419496 +0300
+@@ -0,0 +1,250 @@
++/* 
++ * Copyright (C) 2004 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/slab.h"
++#include "linux/list.h"
++#include "linux/spinlock.h"
++#include "linux/fs.h"
++#include "linux/errno.h"
++#include "filehandle.h"
++#include "os.h"
++#include "kern_util.h"
++
++static spinlock_t open_files_lock = SPIN_LOCK_UNLOCKED;
++static struct list_head open_files = LIST_HEAD_INIT(open_files);
++
++#define NUM_RECLAIM 128
++
++static void reclaim_fds(void)
++{
++      struct file_handle *victim;
++      int closed = NUM_RECLAIM;
++
++      spin_lock(&open_files_lock);
++      while(!list_empty(&open_files) && closed--){
++              victim = list_entry(open_files.prev, struct file_handle, list);
++              os_close_file(victim->fd);
++              victim->fd = -1;
++              list_del_init(&victim->list);
++      }
++      spin_unlock(&open_files_lock);
++}
++
++int open_file(char *name, struct openflags flags, int mode)
++{
++      int fd;
++
++      fd = os_open_file(name, flags, mode);
++      if(fd != -EMFILE)
++              return(fd);
++
++      reclaim_fds();
++      fd = os_open_file(name, flags, mode);
++
++      return(fd);
++}
++
++void *open_dir(char *file)
++{
++      void *dir;
++      int err;
++
++      dir = os_open_dir(file, &err);
++      if(dir != NULL)
++              return(dir);
++      if(err != -EMFILE)
++              return(ERR_PTR(err));
++
++      reclaim_fds();
++
++      dir = os_open_dir(file, &err);
++      if(dir == NULL)
++              dir = ERR_PTR(err);
++
++      return(dir);
++}
++
++void not_reclaimable(struct file_handle *fh)
++{
++      char *name;
++
++      if(fh->get_name == NULL)
++              return;
++
++      if(list_empty(&fh->list)){
++              name = (*fh->get_name)(fh->inode);
++              if(name != NULL){
++                      fh->fd = open_file(name, fh->flags, 0);
++                      kfree(name);
++              }
++              else printk("File descriptor %d has no name\n", fh->fd);
++      }
++      else {
++              spin_lock(&open_files_lock);
++              list_del_init(&fh->list);
++              spin_unlock(&open_files_lock);
++      }
++}
++
++void is_reclaimable(struct file_handle *fh, char *(name_proc)(struct inode *),
++                  struct inode *inode)
++{
++      fh->get_name = name_proc;
++      fh->inode = inode;
++
++      spin_lock(&open_files_lock);
++      list_add(&fh->list, &open_files);
++      spin_unlock(&open_files_lock);
++}
++
++static int active_handle(struct file_handle *fh)
++{
++      int fd;
++      char *name;
++
++      if(!list_empty(&fh->list))
++              list_move(&fh->list, &open_files);
++
++      if(fh->fd != -1)
++              return(0);
++
++      if(fh->inode == NULL)
++              return(-ENOENT);
++
++      name = (*fh->get_name)(fh->inode);
++      if(name == NULL)
++              return(-ENOMEM);
++
++      fd = open_file(name, fh->flags, 0);
++      kfree(name);
++      if(fd < 0)
++              return(fd);
++
++      fh->fd = fd;
++      is_reclaimable(fh, fh->get_name, fh->inode);
++
++      return(0);
++}
++
++int filehandle_fd(struct file_handle *fh)
++{
++      int err;
++
++      err = active_handle(fh);
++      if(err)
++              return(err);
++
++      return(fh->fd);
++}
++
++static void init_fh(struct file_handle *fh, int fd, struct openflags flags)
++{
++      flags.c = 0;
++      *fh = ((struct file_handle) { .list     = LIST_HEAD_INIT(fh->list),
++                                    .fd       = fd,
++                                    .get_name = NULL,
++                                    .inode    = NULL,
++                                    .flags    = flags });
++}
++
++int open_filehandle(char *name, struct openflags flags, int mode, 
++                  struct file_handle *fh)
++{
++      int fd;
++
++      fd = open_file(name, flags, mode);
++      if(fd < 0)
++              return(fd);
++
++      init_fh(fh, fd, flags);
++      return(0);
++}
++
++int close_file(struct file_handle *fh)
++{
++      spin_lock(&open_files_lock);
++      list_del(&fh->list);
++      spin_unlock(&open_files_lock);
++
++      os_close_file(fh->fd);
++
++      fh->fd = -1;
++      return(0);
++}
++
++int read_file(struct file_handle *fh, unsigned long long offset, char *buf,
++            int len)
++{
++      int err;
++
++      err = active_handle(fh);
++      if(err)
++              return(err);
++
++      err = os_seek_file(fh->fd, offset);
++      if(err)
++              return(err);
++
++      return(os_read_file(fh->fd, buf, len));
++}
++
++int write_file(struct file_handle *fh, unsigned long long offset, 
++             const char *buf, int len)
++{
++      int err;
++
++      err = active_handle(fh);
++      if(err)
++              return(err);
++
++      if(offset != -1)
++              err = os_seek_file(fh->fd, offset);
++      if(err)
++              return(err);
++
++      return(os_write_file(fh->fd, buf, len));
++}
++
++int truncate_file(struct file_handle *fh, unsigned long long size)
++{
++      int err;
++
++      err = active_handle(fh);
++      if(err)
++              return(err);
++
++      return(os_truncate_fd(fh->fd, size));
++}
++
++int make_pipe(struct file_handle *fhs)
++{
++      int fds[2], err;
++
++      err = os_pipe(fds, 1, 1);
++      if(err && (err != -EMFILE))
++              return(err);
++
++      if(err){
++              reclaim_fds();
++              err = os_pipe(fds, 1, 1);
++      }
++      if(err)
++              return(err);
++
++      init_fh(&fhs[0], fds[0], OPENFLAGS());
++      init_fh(&fhs[1], fds[1], OPENFLAGS());
++      return(0);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/frame.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/frame.c   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/frame.c        2005-05-03 22:28:14.412419192 +0300
+@@ -0,0 +1,343 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <string.h>
++#include <signal.h>
++#include <wait.h>
++#include <sched.h>
++#include <errno.h>
++#include <sys/ptrace.h>
++#include <sys/syscall.h>
++#include <sys/mman.h>
++#include <asm/page.h>
++#include <asm/ptrace.h>
++#include <asm/sigcontext.h>
++#include "sysdep/ptrace.h"
++#include "sysdep/sigcontext.h"
++#include "frame_user.h"
++#include "kern_util.h"
++#include "user_util.h"
++#include "ptrace_user.h"
++#include "os.h"
++
++static int capture_stack(int (*child)(void *arg), void *arg, void *sp,
++                       unsigned long top, void **data_out)
++{
++      unsigned long regs[FRAME_SIZE];
++      int pid, status, n, len;
++
++      /* Start the child as a thread */
++      pid = clone(child, sp, CLONE_VM | SIGCHLD, arg);
++      if(pid < 0){
++              printf("capture_stack : clone failed - errno = %d\n", errno);
++              exit(1);
++      }
++
++      /* Wait for it to stop itself and continue it with a SIGUSR1 to force 
++       * it into the signal handler.
++       */
++      CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
++      if(n < 0){
++              printf("capture_stack : waitpid failed - errno = %d\n", errno);
++              exit(1);
++      }
++      if(!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGSTOP)){
++              fprintf(stderr, "capture_stack : Expected SIGSTOP, "
++                      "got status = 0x%x\n", status);
++              exit(1);
++      }
++      if(ptrace(PTRACE_CONT, pid, 0, SIGUSR1) < 0){
++              printf("capture_stack : PTRACE_CONT failed - errno = %d\n", 
++                     errno);
++              exit(1);
++      }
++
++      /* Wait for it to stop itself again and grab its registers again.  
++       * At this point, the handler has stuffed the addresses of
++       * sig, sc, and SA_RESTORER in raw.
++       */
++      CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
++      if(n < 0){
++              printf("capture_stack : waitpid failed - errno = %d\n", errno);
++              exit(1);
++      }
++      if(!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGSTOP)){
++              fprintf(stderr, "capture_stack : Expected SIGSTOP, "
++                      "got status = 0x%x\n", status);
++              exit(1);
++      }
++      if(ptrace(PTRACE_GETREGS, pid, 0, regs) < 0){
++              printf("capture_stack : PTRACE_GETREGS failed - errno = %d\n", 
++                     errno);
++              exit(1);
++      }
++
++      /* It has outlived its usefulness, so continue it so it can exit */
++      if(ptrace(PTRACE_CONT, pid, 0, 0) < 0){
++              printf("capture_stack : PTRACE_CONT failed - errno = %d\n", 
++                     errno);
++              exit(1);
++      }
++      CATCH_EINTR(n = waitpid(pid, &status, 0));
++      if(n < 0){
++              printf("capture_stack : waitpid failed - errno = %d\n", errno);
++              exit(1);
++      }
++      if(!WIFSIGNALED(status) || (WTERMSIG(status) != 9)){
++              printf("capture_stack : Expected exit signal 9, "
++                     "got status = 0x%x\n", status);
++              exit(1);
++      }
++
++      /* The frame that we want is the top of the signal stack */
++
++      len = top - PT_SP(regs);
++      *data_out = malloc(len);
++      if(*data_out == NULL){
++              printf("capture_stack : malloc failed - errno = %d\n", errno);
++              exit(1);
++      }
++      memcpy(*data_out, (void *) PT_SP(regs), len);
++
++      return(len);
++}
++
++struct common_raw {
++      void *stack;
++      int size;
++      unsigned long sig;
++      unsigned long sr;
++      unsigned long sp;       
++      struct arch_frame_data_raw arch;
++};
++
++#define SA_RESTORER (0x04000000)
++
++typedef unsigned long old_sigset_t;
++
++struct old_sigaction {
++      __sighandler_t handler;
++      old_sigset_t sa_mask;
++      unsigned long sa_flags;
++      void (*sa_restorer)(void);
++};
++
++static void child_common(struct common_raw *common, sighandler_t handler,
++                       int restorer, int flags)
++{
++      stack_t ss = ((stack_t) { .ss_sp        = common->stack,
++                                .ss_flags     = 0,
++                                .ss_size      = common->size });
++      int err;
++
++      if(ptrace(PTRACE_TRACEME, 0, 0, 0) < 0){
++              printf("PTRACE_TRACEME failed, errno = %d\n", errno);
++      }
++      if(sigaltstack(&ss, NULL) < 0){
++              printf("sigaltstack failed - errno = %d\n", errno);
++              kill(getpid(), SIGKILL);
++      }
++
++      if(restorer){
++              struct sigaction sa;
++
++              sa.sa_handler = handler;
++              sigemptyset(&sa.sa_mask);
++              sa.sa_flags = SA_ONSTACK | flags;
++              err = sigaction(SIGUSR1, &sa, NULL);
++      }
++      else {
++              struct old_sigaction sa;
++
++              sa.handler = handler;
++              sa.sa_mask = 0;
++              sa.sa_flags = (SA_ONSTACK | flags) & ~SA_RESTORER;
++              err = syscall(__NR_sigaction, SIGUSR1, &sa, NULL);
++      }
++      
++      if(err < 0){
++              printf("sigaction failed - errno = %d\n", errno);
++              kill(getpid(), SIGKILL);
++      }
++
++      os_stop_process(os_getpid());
++}
++
++/* Changed only during early boot */
++struct sc_frame signal_frame_sc;
++
++struct sc_frame signal_frame_sc_sr;
++
++struct sc_frame_raw {
++      struct common_raw common;
++      unsigned long sc;
++      int restorer;
++};
++
++/* Changed only during early boot */
++static struct sc_frame_raw *raw_sc = NULL;
++
++static void sc_handler(int sig, struct sigcontext sc)
++{
++      raw_sc->common.sig = (unsigned long) &sig;
++      raw_sc->common.sr = frame_restorer();
++      raw_sc->common.sp = frame_sp();
++      raw_sc->sc = (unsigned long) &sc;
++      setup_arch_frame_raw(&raw_sc->common.arch, &sc + 1, raw_sc->common.sr);
++
++      os_stop_process(os_getpid());
++      kill(getpid(), SIGKILL);
++}
++
++static int sc_child(void *arg)
++{
++      raw_sc = arg;
++      child_common(&raw_sc->common, (sighandler_t) sc_handler, 
++                   raw_sc->restorer, 0);
++      return(-1);
++}
++
++/* Changed only during early boot */
++struct si_frame signal_frame_si;
++
++struct si_frame_raw {
++      struct common_raw common;
++      unsigned long sip;
++      unsigned long si;
++      unsigned long ucp;
++      unsigned long uc;
++};
++
++/* Changed only during early boot */
++static struct si_frame_raw *raw_si = NULL;
++
++static void si_handler(int sig, siginfo_t *si, struct ucontext *ucontext)
++{
++      raw_si->common.sig = (unsigned long) &sig;
++      raw_si->common.sr = frame_restorer();
++      raw_si->common.sp = frame_sp();
++      raw_si->sip = (unsigned long) &si;
++      raw_si->si = (unsigned long) si;
++      raw_si->ucp = (unsigned long) &ucontext;
++      raw_si->uc = (unsigned long) ucontext;
++      setup_arch_frame_raw(&raw_si->common.arch, 
++                           ucontext->uc_mcontext.fpregs, raw_si->common.sr);
++      
++      os_stop_process(os_getpid());
++      kill(getpid(), SIGKILL);
++}
++
++static int si_child(void *arg)
++{
++      raw_si = arg;
++      child_common(&raw_si->common, (sighandler_t) si_handler, 1, 
++                   SA_SIGINFO);
++      return(-1);
++}
++
++static int relative_sr(unsigned long sr, int sr_index, void *stack, 
++                     void *framep)
++{
++      unsigned long *srp = (unsigned long *) sr;
++      unsigned long frame = (unsigned long) framep;
++
++      if((*srp & PAGE_MASK) == (unsigned long) stack){
++              *srp -= sr;
++              *((unsigned long *) (frame + sr_index)) = *srp;
++              return(1);
++      }
++      else return(0);
++}
++
++static unsigned long capture_stack_common(int (*proc)(void *), void *arg, 
++                                        struct common_raw *common_in, 
++                                        void *top, void *sigstack, 
++                                        int stack_len, 
++                                        struct frame_common *common_out)
++{
++      unsigned long sig_top = (unsigned long) sigstack + stack_len, base;
++
++      common_in->stack = (void *) sigstack;
++      common_in->size = stack_len;
++      common_out->len = capture_stack(proc, arg, top, sig_top, 
++                                      &common_out->data);
++      base = sig_top - common_out->len;
++      common_out->sig_index = common_in->sig - base;
++      common_out->sp_index = common_in->sp - base;
++      common_out->sr_index = common_in->sr - base;
++      common_out->sr_relative = relative_sr(common_in->sr, 
++                                            common_out->sr_index, sigstack, 
++                                            common_out->data);
++      return(base);
++}
++
++void capture_signal_stack(void)
++{
++      struct sc_frame_raw raw_sc;
++      struct si_frame_raw raw_si;
++      void *stack, *sigstack;
++      unsigned long top, base;
++
++      stack = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC,
++                   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
++      sigstack = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC,
++                      MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
++      if((stack == MAP_FAILED) || (sigstack == MAP_FAILED)){
++              printf("capture_signal_stack : mmap failed - errno = %d\n", 
++                     errno);
++              exit(1);
++      }
++
++      top = (unsigned long) stack + PAGE_SIZE - sizeof(void *);
++
++      /* Get the sigcontext, no sigrestorer layout */
++      raw_sc.restorer = 0;
++      base = capture_stack_common(sc_child, &raw_sc, &raw_sc.common, 
++                                  (void *) top, sigstack, PAGE_SIZE, 
++                                  &signal_frame_sc.common);
++
++      signal_frame_sc.sc_index = raw_sc.sc - base;
++      setup_arch_frame(&raw_sc.common.arch, &signal_frame_sc.common.arch);
++
++      /* Ditto for the sigcontext, sigrestorer layout */
++      raw_sc.restorer = 1;
++      base = capture_stack_common(sc_child, &raw_sc, &raw_sc.common, 
++                                  (void *) top, sigstack, PAGE_SIZE, 
++                                  &signal_frame_sc_sr.common);
++      signal_frame_sc_sr.sc_index = raw_sc.sc - base;
++      setup_arch_frame(&raw_sc.common.arch, &signal_frame_sc_sr.common.arch);
++
++      /* And the siginfo layout */
++
++      base = capture_stack_common(si_child, &raw_si, &raw_si.common, 
++                                  (void *) top, sigstack, PAGE_SIZE, 
++                                  &signal_frame_si.common);
++      signal_frame_si.sip_index = raw_si.sip - base;
++      signal_frame_si.si_index = raw_si.si - base;
++      signal_frame_si.ucp_index = raw_si.ucp - base;
++      signal_frame_si.uc_index = raw_si.uc - base;
++      setup_arch_frame(&raw_si.common.arch, &signal_frame_si.common.arch);
++
++      if((munmap(stack, PAGE_SIZE) < 0) || 
++         (munmap(sigstack, PAGE_SIZE) < 0)){
++              printf("capture_signal_stack : munmap failed - errno = %d\n", 
++                     errno);
++              exit(1);
++      }
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/frame_kern.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/frame_kern.c      1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/frame_kern.c   2005-05-03 22:28:14.413419040 +0300
+@@ -0,0 +1,173 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "asm/ptrace.h"
++#include "asm/uaccess.h"
++#include "asm/signal.h"
++#include "asm/ucontext.h"
++#include "frame_kern.h"
++#include "sigcontext.h"
++#include "sysdep/ptrace.h"
++#include "choose-mode.h"
++#include "mode.h"
++
++int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from)
++{
++      if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t)))
++              return -EFAULT;
++      if (from->si_code < 0)
++              return __copy_to_user(to, from, sizeof(siginfo_t));
++      else {
++              int err;
++
++              /* If you change siginfo_t structure, please be sure
++                 this code is fixed accordingly.
++                 It should never copy any pad contained in the structure
++                 to avoid security leaks, but must copy the generic
++                 3 ints plus the relevant union member.  */
++              err = __put_user(from->si_signo, &to->si_signo);
++              err |= __put_user(from->si_errno, &to->si_errno);
++              err |= __put_user((short)from->si_code, &to->si_code);
++              /* First 32bits of unions are always present.  */
++              err |= __put_user(from->si_pid, &to->si_pid);
++              switch (from->si_code >> 16) {
++              case __SI_FAULT >> 16:
++                      break;
++              case __SI_CHLD >> 16:
++                      err |= __put_user(from->si_utime, &to->si_utime);
++                      err |= __put_user(from->si_stime, &to->si_stime);
++                      err |= __put_user(from->si_status, &to->si_status);
++              default:
++                      err |= __put_user(from->si_uid, &to->si_uid);
++                      break;
++              }
++              return err;
++      }
++}
++
++static int copy_restorer(void (*restorer)(void), unsigned long start, 
++                       unsigned long sr_index, int sr_relative)
++{
++      unsigned long sr;
++
++      if(sr_relative){
++              sr = (unsigned long) restorer;
++              sr += start + sr_index;
++              restorer = (void (*)(void)) sr;
++      }
++
++      return(copy_to_user((void *) (start + sr_index), &restorer, 
++                          sizeof(restorer)));
++}
++
++extern int userspace_pid[];
++
++static int copy_sc_to_user(void *to, void *fp, struct pt_regs *from, 
++                         struct arch_frame_data *arch)
++{
++      return(CHOOSE_MODE(copy_sc_to_user_tt(to, fp, UPT_SC(&from->regs), 
++                                            arch),
++                         copy_sc_to_user_skas(userspace_pid[0], to, fp, 
++                                              &from->regs,
++                                              current->thread.cr2,
++                                              current->thread.err)));
++}
++
++static int copy_ucontext_to_user(struct ucontext *uc, void *fp, sigset_t *set,
++                               unsigned long sp)
++{
++      int err = 0;
++
++      err |= put_user(current->sas_ss_sp, &uc->uc_stack.ss_sp);
++      err |= put_user(sas_ss_flags(sp), &uc->uc_stack.ss_flags);
++      err |= put_user(current->sas_ss_size, &uc->uc_stack.ss_size);
++      err |= copy_sc_to_user(&uc->uc_mcontext, fp, &current->thread.regs,
++                             &signal_frame_si.common.arch);
++      err |= copy_to_user(&uc->uc_sigmask, set, sizeof(*set));
++      return(err);
++}
++
++int setup_signal_stack_si(unsigned long stack_top, int sig, 
++                        unsigned long handler, void (*restorer)(void), 
++                        struct pt_regs *regs, siginfo_t *info, 
++                        sigset_t *mask)
++{
++      unsigned long start;
++      void *sip, *ucp, *fp;
++
++      start = stack_top - signal_frame_si.common.len;
++      sip = (void *) (start + signal_frame_si.si_index);
++      ucp = (void *) (start + signal_frame_si.uc_index);
++      fp = (void *) (((unsigned long) ucp) + sizeof(struct ucontext));
++
++      if(restorer == NULL)
++              panic("setup_signal_stack_si - no restorer");
++
++      if(copy_to_user((void *) start, signal_frame_si.common.data,
++                      signal_frame_si.common.len) ||
++         copy_to_user((void *) (start + signal_frame_si.common.sig_index), 
++                      &sig, sizeof(sig)) ||
++         copy_siginfo_to_user(sip, info) ||
++         copy_to_user((void *) (start + signal_frame_si.sip_index), &sip,
++                      sizeof(sip)) ||
++         copy_ucontext_to_user(ucp, fp, mask, PT_REGS_SP(regs)) ||
++         copy_to_user((void *) (start + signal_frame_si.ucp_index), &ucp,
++                      sizeof(ucp)) ||
++         copy_restorer(restorer, start, signal_frame_si.common.sr_index,
++                       signal_frame_si.common.sr_relative))
++              return(1);
++      
++      PT_REGS_IP(regs) = handler;
++      PT_REGS_SP(regs) = start + signal_frame_si.common.sp_index;
++      return(0);
++}
++
++int setup_signal_stack_sc(unsigned long stack_top, int sig, 
++                        unsigned long handler, void (*restorer)(void), 
++                        struct pt_regs *regs, sigset_t *mask)
++{
++      struct frame_common *frame = &signal_frame_sc_sr.common;
++      void *user_sc;
++      int sig_size = (_NSIG_WORDS - 1) * sizeof(unsigned long);
++      unsigned long sigs, sr;
++      unsigned long start = stack_top - frame->len - sig_size;
++
++      user_sc = (void *) (start + signal_frame_sc_sr.sc_index);
++      if(restorer == NULL){
++              frame = &signal_frame_sc.common;
++              user_sc = (void *) (start + signal_frame_sc.sc_index);
++              sr = (unsigned long) frame->data;
++              sr += frame->sr_index;
++              sr = *((unsigned long *) sr);
++              restorer = ((void (*)(void)) sr);
++      }
++
++      sigs = start + frame->len;
++      if(copy_to_user((void *) start, frame->data, frame->len) ||
++         copy_to_user((void *) (start + frame->sig_index), &sig, 
++                      sizeof(sig)) ||
++         copy_sc_to_user(user_sc, NULL, regs, 
++                         &signal_frame_sc.common.arch) ||
++         copy_to_user(sc_sigmask(user_sc), mask, sizeof(mask->sig[0])) ||
++         copy_to_user((void *) sigs, &mask->sig[1], sig_size) ||
++         copy_restorer(restorer, start, frame->sr_index, frame->sr_relative))
++              return(1);
++
++      PT_REGS_IP(regs) = handler;
++      PT_REGS_SP(regs) = start + frame->sp_index;
++
++      return(0);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/gmon_syms.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/gmon_syms.c       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/gmon_syms.c    2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,20 @@
++/* 
++ * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/module.h"
++
++extern void __bb_init_func(void *);
++EXPORT_SYMBOL(__bb_init_func);
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/gprof_syms.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/gprof_syms.c      1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/gprof_syms.c   2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,20 @@
++/* 
++ * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/module.h"
++
++extern void mcount(void);
++EXPORT_SYMBOL(mcount);
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/helper.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/helper.c  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/helper.c       2005-05-03 22:28:14.416418584 +0300
+@@ -0,0 +1,167 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <errno.h>
++#include <sched.h>
++#include <sys/signal.h>
++#include <sys/wait.h>
++#include "user.h"
++#include "kern_util.h"
++#include "user_util.h"
++#include "os.h"
++
++struct helper_data {
++      void (*pre_exec)(void*);
++      void *pre_data;
++      char **argv;
++      int fd;
++};
++
++/* Debugging aid, changed only from gdb */
++int helper_pause = 0;
++
++static void helper_hup(int sig)
++{
++}
++
++static int helper_child(void *arg)
++{
++      struct helper_data *data = arg;
++      char **argv = data->argv;
++      int errval;
++
++      if(helper_pause){
++              signal(SIGHUP, helper_hup);
++              pause();
++      }
++      if(data->pre_exec != NULL)
++              (*data->pre_exec)(data->pre_data);
++      execvp(argv[0], argv);
++      errval = errno;
++      printk("execvp of '%s' failed - errno = %d\n", argv[0], errno);
++      os_write_file(data->fd, &errval, sizeof(errval));
++      os_kill_process(os_getpid(), 0);
++      return(0);
++}
++
++/* XXX The alloc_stack here breaks if this is called in the tracing thread */
++
++int run_helper(void (*pre_exec)(void *), void *pre_data, char **argv,
++             unsigned long *stack_out)
++{
++      struct helper_data data;
++      unsigned long stack, sp;
++      int pid, fds[2], err, n;
++
++      if((stack_out != NULL) && (*stack_out != 0))
++              stack = *stack_out;
++      else stack = alloc_stack(0, um_in_interrupt());
++      if(stack == 0) 
++              return(-ENOMEM);
++
++      err = os_pipe(fds, 1, 0);
++      if(err < 0){
++              printk("run_helper : pipe failed, err = %d\n", -err);
++              goto out_free;
++      }
++
++      err = os_set_exec_close(fds[1], 1);
++      if(err < 0){
++              printk("run_helper : setting FD_CLOEXEC failed, err = %d\n",
++                     -err);
++              goto out_close;
++      }
++
++      sp = stack + page_size() - sizeof(void *);
++      data.pre_exec = pre_exec;
++      data.pre_data = pre_data;
++      data.argv = argv;
++      data.fd = fds[1];
++      pid = clone(helper_child, (void *) sp, CLONE_VM | SIGCHLD, &data);
++      if(pid < 0){
++              printk("run_helper : clone failed, errno = %d\n", errno);
++              err = -errno;
++              goto out_close;
++      }
++
++      os_close_file(fds[1]);
++      n = os_read_file(fds[0], &err, sizeof(err));
++      if(n < 0){
++              printk("run_helper : read on pipe failed, err = %d\n", -n);
++              err = n;
++              os_kill_process(pid, 1);
++      }
++      else if(n != 0){
++              CATCH_EINTR(n = waitpid(pid, NULL, 0));
++              pid = -errno;
++      }
++      err = pid;
++
++ out_close:
++      os_close_file(fds[0]);
++ out_free:
++      if(stack_out == NULL) 
++              free_stack(stack, 0);
++        else *stack_out = stack;
++      return(err);
++}
++
++int run_helper_thread(int (*proc)(void *), void *arg, unsigned int flags, 
++                    unsigned long *stack_out, int stack_order)
++{
++      unsigned long stack, sp;
++      int pid, status;
++
++      stack = alloc_stack(stack_order, um_in_interrupt());
++      if(stack == 0) return(-ENOMEM);
++
++      sp = stack + (page_size() << stack_order) - sizeof(void *);
++      pid = clone(proc, (void *) sp, flags | SIGCHLD, arg);
++      if(pid < 0){
++              printk("run_helper_thread : clone failed, errno = %d\n", 
++                     errno);
++              return(-errno);
++      }
++      if(stack_out == NULL){
++              CATCH_EINTR(pid = waitpid(pid, &status, 0));
++              if(pid < 0){
++                      printk("run_helper_thread - wait failed, errno = %d\n",
++                             errno);
++                      pid = -errno;
++              }
++              if(!WIFEXITED(status) || (WEXITSTATUS(status) != 0))
++                      printk("run_helper_thread - thread returned status "
++                             "0x%x\n", status);
++              free_stack(stack, stack_order);
++      }
++        else *stack_out = stack;
++      return(pid);
++}
++
++int helper_wait(int pid, int block)
++{
++      int ret;
++
++      CATCH_EINTR(ret = waitpid(pid, NULL, WNOHANG));
++      if(ret < 0){
++              printk("helper_wait : waitpid failed, errno = %d\n", errno);
++              return(-errno);
++      }
++      return(ret);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/initrd_kern.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/initrd_kern.c     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/initrd_kern.c  2005-05-03 22:28:14.417418432 +0300
+@@ -0,0 +1,59 @@
++/*
++ * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/init.h"
++#include "linux/bootmem.h"
++#include "linux/blk.h"
++#include "asm/types.h"
++#include "user_util.h"
++#include "kern_util.h"
++#include "initrd.h"
++#include "init.h"
++#include "os.h"
++
++/* Changed by uml_initrd_setup, which is a setup */
++static char *initrd __initdata = NULL;
++
++static int __init read_initrd(void)
++{
++      void *area;
++      long long size;
++      int err;
++
++      if(initrd == NULL) return 0;
++      err = os_file_size(initrd, &size);
++      if(err) return 0;
++      area = alloc_bootmem(size);
++      if(area == NULL) return 0;
++      if(load_initrd(initrd, area, size) == -1) return 0;
++      initrd_start = (unsigned long) area;
++      initrd_end = initrd_start + size;
++      return 0;
++}
++
++__uml_postsetup(read_initrd);
++
++static int __init uml_initrd_setup(char *line, int *add)
++{
++      initrd = line;
++      return 0;
++}
++
++__uml_setup("initrd=", uml_initrd_setup, 
++"initrd=<initrd image>\n"
++"    This is used to boot UML from an initrd image.  The argument is the\n"
++"    name of the file containing the image.\n\n"
++);
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/initrd_user.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/initrd_user.c     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/initrd_user.c  2005-05-03 22:28:14.418418280 +0300
+@@ -0,0 +1,44 @@
++/*
++ * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <unistd.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <errno.h>
++
++#include "user_util.h"
++#include "kern_util.h"
++#include "user.h"
++#include "initrd.h"
++#include "os.h"
++
++int load_initrd(char *filename, void *buf, int size)
++{
++      int fd, n;
++
++      fd = os_open_file(filename, of_read(OPENFLAGS()), 0);
++      if(fd < 0){
++              printk("Opening '%s' failed - err = %d\n", filename, -fd);
++              return(-1);
++      }
++      n = os_read_file(fd, buf, size);
++      if(n != size){
++              printk("Read of %d bytes from '%s' failed, err = %d\n", size, 
++                     filename, -n);
++              return(-1);
++      }
++      return(0);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/init_task.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/init_task.c       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/init_task.c    2005-05-03 22:28:14.419418128 +0300
+@@ -0,0 +1,61 @@
++/* 
++ * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/config.h"
++#include "linux/mm.h"
++#include "linux/sched.h"
++#include "linux/version.h"
++#include "asm/uaccess.h"
++#include "asm/pgtable.h"
++#include "user_util.h"
++#include "mem_user.h"
++
++static struct fs_struct init_fs = INIT_FS;
++static struct files_struct init_files = INIT_FILES;
++static struct signal_struct init_signals = INIT_SIGNALS;
++struct mm_struct init_mm = INIT_MM(init_mm);
++
++/*
++ * Initial task structure.
++ *
++ * We need to make sure that this is 16384-byte aligned due to the
++ * way process stacks are handled. This is done by having a special
++ * "init_task" linker map entry..
++ */
++
++union task_union init_task_union 
++__attribute__((__section__(".data.init_task"))) = 
++{ INIT_TASK(init_task_union.task) };
++
++struct task_struct *alloc_task_struct(void)
++{
++      return((struct task_struct *) 
++             __get_free_pages(GFP_KERNEL, CONFIG_KERNEL_STACK_ORDER));
++}
++
++void unprotect_stack(unsigned long stack)
++{
++      protect_memory(stack, (1 << CONFIG_KERNEL_STACK_ORDER) * PAGE_SIZE, 
++                     1, 1, 0, 1);
++}
++
++void free_task_struct(struct task_struct *task)
++{
++      /* free_pages decrements the page counter and only actually frees
++       * the pages if they are now not accessed by anything.
++       */
++      free_pages((unsigned long) task, CONFIG_KERNEL_STACK_ORDER);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/irq.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/irq.c     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/irq.c  2005-05-03 22:28:14.422417672 +0300
+@@ -0,0 +1,840 @@
++/* 
++ * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ * Derived (i.e. mostly copied) from arch/i386/kernel/irq.c:
++ *    Copyright (C) 1992, 1998 Linus Torvalds, Ingo Molnar
++ */
++
++#include "linux/config.h"
++#include "linux/kernel.h"
++#include "linux/smp.h"
++#include "linux/irq.h"
++#include "linux/kernel_stat.h"
++#include "linux/interrupt.h"
++#include "linux/random.h"
++#include "linux/slab.h"
++#include "linux/file.h"
++#include "linux/proc_fs.h"
++#include "linux/init.h"
++#include "linux/seq_file.h"
++#include "asm/irq.h"
++#include "asm/hw_irq.h"
++#include "asm/hardirq.h"
++#include "asm/atomic.h"
++#include "asm/signal.h"
++#include "asm/system.h"
++#include "asm/errno.h"
++#include "asm/uaccess.h"
++#include "user_util.h"
++#include "kern_util.h"
++#include "irq_user.h"
++#include "irq_kern.h"
++
++static void register_irq_proc (unsigned int irq);
++
++irq_desc_t irq_desc[NR_IRQS] __cacheline_aligned =
++        { [0 ... NR_IRQS-1] = { 0, &no_irq_type, NULL, 0, SPIN_LOCK_UNLOCKED}};
++
++/*
++ * Generic no controller code
++ */
++
++static void enable_none(unsigned int irq) { }
++static unsigned int startup_none(unsigned int irq) { return 0; }
++static void disable_none(unsigned int irq) { }
++static void ack_none(unsigned int irq)
++{
++/*
++ * 'what should we do if we get a hw irq event on an illegal vector'.
++ * each architecture has to answer this themselves, it doesnt deserve
++ * a generic callback i think.
++ */
++#if CONFIG_X86
++      printk(KERN_ERR "unexpected IRQ trap at vector %02x\n", irq);
++#ifdef CONFIG_X86_LOCAL_APIC
++      /*
++       * Currently unexpected vectors happen only on SMP and APIC.
++       * We _must_ ack these because every local APIC has only N
++       * irq slots per priority level, and a 'hanging, unacked' IRQ
++       * holds up an irq slot - in excessive cases (when multiple
++       * unexpected vectors occur) that might lock up the APIC
++       * completely.
++       */
++      ack_APIC_irq();
++#endif
++#endif
++}
++
++/* startup is the same as "enable", shutdown is same as "disable" */
++#define shutdown_none disable_none
++#define end_none      enable_none
++
++struct hw_interrupt_type no_irq_type = {
++      "none",
++      startup_none,
++      shutdown_none,
++      enable_none,
++      disable_none,
++      ack_none,
++      end_none
++};
++
++/*
++ * Generic, controller-independent functions:
++ */
++
++int get_irq_list(char *buf)
++{
++      int i, j;
++      unsigned long flags;
++      struct irqaction * action;
++      char *p = buf;
++
++      p += sprintf(p, "           ");
++      for (j=0; j<smp_num_cpus; j++)
++              p += sprintf(p, "CPU%d       ",j);
++      *p++ = '\n';
++
++      for (i = 0 ; i < NR_IRQS ; i++) {
++              spin_lock_irqsave(&irq_desc[i].lock, flags);
++              action = irq_desc[i].action;
++              if (!action) 
++                      goto end;
++              p += sprintf(p, "%3d: ",i);
++#ifndef CONFIG_SMP
++              p += sprintf(p, "%10u ", kstat_irqs(i));
++#else
++              for (j = 0; j < smp_num_cpus; j++)
++                      p += sprintf(p, "%10u ",
++                              kstat.irqs[cpu_logical_map(j)][i]);
++#endif
++              p += sprintf(p, " %14s", irq_desc[i].handler->typename);
++              p += sprintf(p, "  %s", action->name);
++
++              for (action=action->next; action; action = action->next)
++                      p += sprintf(p, ", %s", action->name);
++              *p++ = '\n';
++      end:
++              spin_unlock_irqrestore(&irq_desc[i].lock, flags);
++      }
++      p += sprintf(p, "\n");
++#ifdef notdef
++#if CONFIG_SMP
++      p += sprintf(p, "LOC: ");
++      for (j = 0; j < smp_num_cpus; j++)
++              p += sprintf(p, "%10u ",
++                      apic_timer_irqs[cpu_logical_map(j)]);
++      p += sprintf(p, "\n");
++#endif
++#endif
++      p += sprintf(p, "ERR: %10lu\n", 0L);
++      return p - buf;
++}
++
++/*
++ * This should really return information about whether
++ * we should do bottom half handling etc. Right now we
++ * end up _always_ checking the bottom half, which is a
++ * waste of time and is not what some drivers would
++ * prefer.
++ */
++int handle_IRQ_event(unsigned int irq, struct pt_regs * regs, 
++                   struct irqaction * action)
++{
++      int status;
++      int cpu = smp_processor_id();
++
++      irq_enter(cpu, irq);
++
++      status = 1;     /* Force the "do bottom halves" bit */
++
++      if (!(action->flags & SA_INTERRUPT))
++              __sti();
++
++      do {
++              status |= action->flags;
++              action->handler(irq, action->dev_id, regs);
++              action = action->next;
++      } while (action);
++      if (status & SA_SAMPLE_RANDOM)
++              add_interrupt_randomness(irq);
++      __cli();
++
++      irq_exit(cpu, irq);
++
++      return status;
++}
++
++/*
++ * Generic enable/disable code: this just calls
++ * down into the PIC-specific version for the actual
++ * hardware disable after having gotten the irq
++ * controller lock. 
++ */
++ 
++/**
++ *    disable_irq_nosync - disable an irq without waiting
++ *    @irq: Interrupt to disable
++ *
++ *    Disable the selected interrupt line. Disables of an interrupt
++ *    stack. Unlike disable_irq(), this function does not ensure existing
++ *    instances of the IRQ handler have completed before returning.
++ *
++ *    This function may be called from IRQ context.
++ */
++ 
++void inline disable_irq_nosync(unsigned int irq)
++{
++      irq_desc_t *desc = irq_desc + irq;
++      unsigned long flags;
++
++      spin_lock_irqsave(&desc->lock, flags);
++      if (!desc->depth++) {
++              desc->status |= IRQ_DISABLED;
++              desc->handler->disable(irq);
++      }
++      spin_unlock_irqrestore(&desc->lock, flags);
++}
++
++/**
++ *    disable_irq - disable an irq and wait for completion
++ *    @irq: Interrupt to disable
++ *
++ *    Disable the selected interrupt line. Disables of an interrupt
++ *    stack. That is for two disables you need two enables. This
++ *    function waits for any pending IRQ handlers for this interrupt
++ *    to complete before returning. If you use this function while
++ *    holding a resource the IRQ handler may need you will deadlock.
++ *
++ *    This function may be called - with care - from IRQ context.
++ */
++ 
++void disable_irq(unsigned int irq)
++{
++      disable_irq_nosync(irq);
++
++      if (!local_irq_count(smp_processor_id())) {
++              do {
++                      barrier();
++              } while (irq_desc[irq].status & IRQ_INPROGRESS);
++      }
++}
++
++/**
++ *    enable_irq - enable interrupt handling on an irq
++ *    @irq: Interrupt to enable
++ *
++ *    Re-enables the processing of interrupts on this IRQ line
++ *    providing no disable_irq calls are now in effect.
++ *
++ *    This function may be called from IRQ context.
++ */
++ 
++void enable_irq(unsigned int irq)
++{
++      irq_desc_t *desc = irq_desc + irq;
++      unsigned long flags;
++
++      spin_lock_irqsave(&desc->lock, flags);
++      switch (desc->depth) {
++      case 1: {
++              unsigned int status = desc->status & ~IRQ_DISABLED;
++              desc->status = status;
++              if ((status & (IRQ_PENDING | IRQ_REPLAY)) == IRQ_PENDING) {
++                      desc->status = status | IRQ_REPLAY;
++                      hw_resend_irq(desc->handler,irq);
++              }
++              desc->handler->enable(irq);
++              /* fall-through */
++      }
++      default:
++              desc->depth--;
++              break;
++      case 0:
++              printk(KERN_ERR "enable_irq() unbalanced from %p\n",
++                     __builtin_return_address(0));
++      }
++      spin_unlock_irqrestore(&desc->lock, flags);
++}
++
++/*
++ * do_IRQ handles all normal device IRQ's (the special
++ * SMP cross-CPU interrupts have their own specific
++ * handlers).
++ */
++unsigned int do_IRQ(int irq, union uml_pt_regs *regs)
++{     
++      /* 
++       * 0 return value means that this irq is already being
++       * handled by some other CPU. (or is disabled)
++       */
++      int cpu = smp_processor_id();
++      irq_desc_t *desc = irq_desc + irq;
++      struct irqaction * action;
++      unsigned int status;
++
++      kstat.irqs[cpu][irq]++;
++      spin_lock(&desc->lock);
++      desc->handler->ack(irq);
++      /*
++         REPLAY is when Linux resends an IRQ that was dropped earlier
++         WAITING is used by probe to mark irqs that are being tested
++         */
++      status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING);
++      status |= IRQ_PENDING; /* we _want_ to handle it */
++
++      /*
++       * If the IRQ is disabled for whatever reason, we cannot
++       * use the action we have.
++       */
++      action = NULL;
++      if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) {
++              action = desc->action;
++              status &= ~IRQ_PENDING; /* we commit to handling */
++              status |= IRQ_INPROGRESS; /* we are handling it */
++      }
++      desc->status = status;
++
++      /*
++       * If there is no IRQ handler or it was disabled, exit early.
++         Since we set PENDING, if another processor is handling
++         a different instance of this same irq, the other processor
++         will take care of it.
++       */
++      if (!action)
++              goto out;
++
++      /*
++       * Edge triggered interrupts need to remember
++       * pending events.
++       * This applies to any hw interrupts that allow a second
++       * instance of the same irq to arrive while we are in do_IRQ
++       * or in the handler. But the code here only handles the _second_
++       * instance of the irq, not the third or fourth. So it is mostly
++       * useful for irq hardware that does not mask cleanly in an
++       * SMP environment.
++       */
++      for (;;) {
++              spin_unlock(&desc->lock);
++              handle_IRQ_event(irq, (struct pt_regs *) regs, action);
++              spin_lock(&desc->lock);
++              
++              if (!(desc->status & IRQ_PENDING))
++                      break;
++              desc->status &= ~IRQ_PENDING;
++      }
++      desc->status &= ~IRQ_INPROGRESS;
++out:
++      /*
++       * The ->end() handler has to deal with interrupts which got
++       * disabled while the handler was running.
++       */
++      desc->handler->end(irq);
++      spin_unlock(&desc->lock);
++
++      if (softirq_pending(cpu))
++              do_softirq();
++      return 1;
++}
++
++/**
++ *    request_irq - allocate an interrupt line
++ *    @irq: Interrupt line to allocate
++ *    @handler: Function to be called when the IRQ occurs
++ *    @irqflags: Interrupt type flags
++ *    @devname: An ascii name for the claiming device
++ *    @dev_id: A cookie passed back to the handler function
++ *
++ *    This call allocates interrupt resources and enables the
++ *    interrupt line and IRQ handling. From the point this
++ *    call is made your handler function may be invoked. Since
++ *    your handler function must clear any interrupt the board 
++ *    raises, you must take care both to initialise your hardware
++ *    and to set up the interrupt handler in the right order.
++ *
++ *    Dev_id must be globally unique. Normally the address of the
++ *    device data structure is used as the cookie. Since the handler
++ *    receives this value it makes sense to use it.
++ *
++ *    If your interrupt is shared you must pass a non NULL dev_id
++ *    as this is required when freeing the interrupt.
++ *
++ *    Flags:
++ *
++ *    SA_SHIRQ                Interrupt is shared
++ *
++ *    SA_INTERRUPT            Disable local interrupts while processing
++ *
++ *    SA_SAMPLE_RANDOM        The interrupt can be used for entropy
++ *
++ */
++ 
++int request_irq(unsigned int irq,
++              void (*handler)(int, void *, struct pt_regs *),
++              unsigned long irqflags, 
++              const char * devname,
++              void *dev_id)
++{
++      int retval;
++      struct irqaction * action;
++
++#if 1
++      /*
++       * Sanity-check: shared interrupts should REALLY pass in
++       * a real dev-ID, otherwise we'll have trouble later trying
++       * to figure out which interrupt is which (messes up the
++       * interrupt freeing logic etc).
++       */
++      if (irqflags & SA_SHIRQ) {
++              if (!dev_id)
++                      printk(KERN_ERR "Bad boy: %s (at 0x%x) called us "
++                             "without a dev_id!\n", devname, (&irq)[-1]);
++      }
++#endif
++
++      if (irq >= NR_IRQS)
++              return -EINVAL;
++      if (!handler)
++              return -EINVAL;
++
++      action = (struct irqaction *)
++                      kmalloc(sizeof(struct irqaction), GFP_KERNEL);
++      if (!action)
++              return -ENOMEM;
++
++      action->handler = handler;
++      action->flags = irqflags;
++      action->mask = 0;
++      action->name = devname;
++      action->next = NULL;
++      action->dev_id = dev_id;
++
++      retval = setup_irq(irq, action);
++      if (retval)
++              kfree(action);
++      return retval;
++}
++
++int um_request_irq(unsigned int irq, int fd, int type,
++                 void (*handler)(int, void *, struct pt_regs *),
++                 unsigned long irqflags, const char * devname,
++                 void *dev_id)
++{
++      int err;
++
++      err = request_irq(irq, handler, irqflags, devname, dev_id);
++      if(err) 
++              return(err);
++
++      if(fd != -1)
++              err = activate_fd(irq, fd, type, dev_id);
++      return(err);
++}
++
++/* this was setup_x86_irq but it seems pretty generic */
++int setup_irq(unsigned int irq, struct irqaction * new)
++{
++      int shared = 0;
++      unsigned long flags;
++      struct irqaction *old, **p;
++      irq_desc_t *desc = irq_desc + irq;
++
++      /*
++       * Some drivers like serial.c use request_irq() heavily,
++       * so we have to be careful not to interfere with a
++       * running system.
++       */
++      if (new->flags & SA_SAMPLE_RANDOM) {
++              /*
++               * This function might sleep, we want to call it first,
++               * outside of the atomic block.
++               * Yes, this might clear the entropy pool if the wrong
++               * driver is attempted to be loaded, without actually
++               * installing a new handler, but is this really a problem,
++               * only the sysadmin is able to do this.
++               */
++              rand_initialize_irq(irq);
++      }
++
++      /*
++       * The following block of code has to be executed atomically
++       */
++      spin_lock_irqsave(&desc->lock,flags);
++      p = &desc->action;
++      old = *p;
++      if (old != NULL) {
++              /* Can't share interrupts unless both agree to */
++              if (!(old->flags & new->flags & SA_SHIRQ)) {
++                      spin_unlock_irqrestore(&desc->lock,flags);
++                      return -EBUSY;
++              }
++
++              /* add new interrupt at end of irq queue */
++              do {
++                      p = &old->next;
++                      old = *p;
++              } while (old);
++              shared = 1;
++      }
++
++      *p = new;
++
++      if (!shared) {
++              desc->depth = 0;
++              desc->status &= ~IRQ_DISABLED;
++              desc->handler->startup(irq);
++      }
++      spin_unlock_irqrestore(&desc->lock,flags);
++
++      register_irq_proc(irq);
++      return 0;
++}
++
++/**
++ *    free_irq - free an interrupt
++ *    @irq: Interrupt line to free
++ *    @dev_id: Device identity to free
++ *
++ *    Remove an interrupt handler. The handler is removed and if the
++ *    interrupt line is no longer in use by any driver it is disabled.
++ *    On a shared IRQ the caller must ensure the interrupt is disabled
++ *    on the card it drives before calling this function. The function
++ *    does not return until any executing interrupts for this IRQ
++ *    have completed.
++ *
++ *    This function may be called from interrupt context. 
++ *
++ *    Bugs: Attempting to free an irq in a handler for the same irq hangs
++ *          the machine.
++ */
++ 
++void free_irq(unsigned int irq, void *dev_id)
++{
++      irq_desc_t *desc;
++      struct irqaction **p;
++      unsigned long flags;
++
++      if (irq >= NR_IRQS)
++              return;
++
++      desc = irq_desc + irq;
++      spin_lock_irqsave(&desc->lock,flags);
++      p = &desc->action;
++      for (;;) {
++              struct irqaction * action = *p;
++              if (action) {
++                      struct irqaction **pp = p;
++                      p = &action->next;
++                      if (action->dev_id != dev_id)
++                              continue;
++
++                      /* Found it - now remove it from the list of entries */
++                      *pp = action->next;
++                      if (!desc->action) {
++                              desc->status |= IRQ_DISABLED;
++                              desc->handler->shutdown(irq);
++                      }
++                      free_irq_by_irq_and_dev(irq, dev_id);
++                      spin_unlock_irqrestore(&desc->lock,flags);
++
++#ifdef CONFIG_SMP
++                      /* Wait to make sure it's not being used on another CPU */
++                      while (desc->status & IRQ_INPROGRESS)
++                              barrier();
++#endif
++                      kfree(action);
++                      return;
++              }
++              printk(KERN_ERR "Trying to free free IRQ%d\n",irq);
++              spin_unlock_irqrestore(&desc->lock,flags);
++              return;
++      }
++}
++
++/* These are initialized by sysctl_init, which is called from init/main.c */
++static struct proc_dir_entry * root_irq_dir;
++static struct proc_dir_entry * irq_dir [NR_IRQS];
++static struct proc_dir_entry * smp_affinity_entry [NR_IRQS];
++
++/* These are read and written as longs, so a read won't see a partial write
++ * even during a race.
++ */
++static unsigned long irq_affinity [NR_IRQS] = { [0 ... NR_IRQS-1] = ~0UL };
++
++#define HEX_DIGITS 8
++
++static int irq_affinity_read_proc (char *page, char **start, off_t off,
++                      int count, int *eof, void *data)
++{
++      if (count < HEX_DIGITS+1)
++              return -EINVAL;
++      return sprintf (page, "%08lx\n", irq_affinity[(long)data]);
++}
++
++static unsigned int parse_hex_value (const char *buffer,
++              unsigned long count, unsigned long *ret)
++{
++      unsigned char hexnum [HEX_DIGITS];
++      unsigned long value;
++      int i;
++
++      if (!count)
++              return -EINVAL;
++      if (count > HEX_DIGITS)
++              count = HEX_DIGITS;
++      if (copy_from_user(hexnum, buffer, count))
++              return -EFAULT;
++
++      /*
++       * Parse the first HEX_DIGITS characters as a hex string, any non-hex
++       * char is end-of-string. '00e1', 'e1', '00E1', 'E1' are all the same.
++       */
++      value = 0;
++
++      for (i = 0; i < count; i++) {
++              unsigned int c = hexnum[i];
++
++              switch (c) {
++                      case '0' ... '9': c -= '0'; break;
++                      case 'a' ... 'f': c -= 'a'-10; break;
++                      case 'A' ... 'F': c -= 'A'-10; break;
++              default:
++                      goto out;
++              }
++              value = (value << 4) | c;
++      }
++out:
++      *ret = value;
++      return 0;
++}
++
++static int irq_affinity_write_proc (struct file *file, const char *buffer,
++                                      unsigned long count, void *data)
++{
++      int irq = (long) data, full_count = count, err;
++      unsigned long new_value;
++
++      if (!irq_desc[irq].handler->set_affinity)
++              return -EIO;
++
++      err = parse_hex_value(buffer, count, &new_value);
++
++#if CONFIG_SMP
++      /*
++       * Do not allow disabling IRQs completely - it's a too easy
++       * way to make the system unusable accidentally :-) At least
++       * one online CPU still has to be targeted.
++       */
++      if (!(new_value & cpu_online_map))
++              return -EINVAL;
++#endif
++
++      irq_affinity[irq] = new_value;
++      irq_desc[irq].handler->set_affinity(irq, new_value);
++
++      return full_count;
++}
++
++static int prof_cpu_mask_read_proc (char *page, char **start, off_t off,
++                      int count, int *eof, void *data)
++{
++      unsigned long *mask = (unsigned long *) data;
++      if (count < HEX_DIGITS+1)
++              return -EINVAL;
++      return sprintf (page, "%08lx\n", *mask);
++}
++
++static int prof_cpu_mask_write_proc (struct file *file, const char *buffer,
++                                      unsigned long count, void *data)
++{
++      unsigned long *mask = (unsigned long *) data, full_count = count, err;
++      unsigned long new_value;
++
++      err = parse_hex_value(buffer, count, &new_value);
++      if (err)
++              return err;
++
++      *mask = new_value;
++      return full_count;
++}
++
++#define MAX_NAMELEN 10
++
++static void register_irq_proc (unsigned int irq)
++{
++      struct proc_dir_entry *entry;
++      char name [MAX_NAMELEN];
++
++      if (!root_irq_dir || (irq_desc[irq].handler == &no_irq_type) ||
++          irq_dir[irq])
++              return;
++
++      memset(name, 0, MAX_NAMELEN);
++      sprintf(name, "%d", irq);
++
++      /* create /proc/irq/1234 */
++      irq_dir[irq] = proc_mkdir(name, root_irq_dir);
++
++      /* create /proc/irq/1234/smp_affinity */
++      entry = create_proc_entry("smp_affinity", 0600, irq_dir[irq]);
++
++      entry->nlink = 1;
++      entry->data = (void *)(long)irq;
++      entry->read_proc = irq_affinity_read_proc;
++      entry->write_proc = irq_affinity_write_proc;
++
++      smp_affinity_entry[irq] = entry;
++}
++
++/* Read and written as a long */
++unsigned long prof_cpu_mask = -1;
++
++void __init init_irq_proc (void)
++{
++      struct proc_dir_entry *entry;
++      int i;
++
++      /* create /proc/irq */
++      root_irq_dir = proc_mkdir("irq", 0);
++
++      /* create /proc/irq/prof_cpu_mask */
++      entry = create_proc_entry("prof_cpu_mask", 0600, root_irq_dir);
++
++      entry->nlink = 1;
++      entry->data = (void *)&prof_cpu_mask;
++      entry->read_proc = prof_cpu_mask_read_proc;
++      entry->write_proc = prof_cpu_mask_write_proc;
++
++      /*
++       * Create entries for all existing IRQs.
++       */
++      for (i = 0; i < NR_IRQS; i++)
++              register_irq_proc(i);
++}
++
++static spinlock_t irq_spinlock = SPIN_LOCK_UNLOCKED;
++
++unsigned long irq_lock(void)
++{
++      unsigned long flags;
++
++      spin_lock_irqsave(&irq_spinlock, flags);
++      return(flags);
++}
++
++void irq_unlock(unsigned long flags)
++{
++      spin_unlock_irqrestore(&irq_spinlock, flags);
++}
++
++unsigned long probe_irq_on(void)
++{
++      return(0);
++}
++
++int probe_irq_off(unsigned long val)
++{
++      return(0);
++}
++
++static unsigned int startup_SIGIO_irq(unsigned int irq)
++{
++      return(0);
++}
++
++static void shutdown_SIGIO_irq(unsigned int irq)
++{
++}
++
++static void enable_SIGIO_irq(unsigned int irq)
++{
++}
++
++static void disable_SIGIO_irq(unsigned int irq)
++{
++}
++
++static void mask_and_ack_SIGIO(unsigned int irq)
++{
++}
++
++static void end_SIGIO_irq(unsigned int irq)
++{
++}
++
++static unsigned int startup_SIGVTALRM_irq(unsigned int irq)
++{
++      return(0);
++}
++
++static void shutdown_SIGVTALRM_irq(unsigned int irq)
++{
++}
++
++static void enable_SIGVTALRM_irq(unsigned int irq)
++{
++}
++
++static void disable_SIGVTALRM_irq(unsigned int irq)
++{
++}
++
++static void mask_and_ack_SIGVTALRM(unsigned int irq)
++{
++}
++
++static void end_SIGVTALRM_irq(unsigned int irq)
++{
++}
++
++static struct hw_interrupt_type SIGIO_irq_type = {
++      "SIGIO",
++      startup_SIGIO_irq,
++      shutdown_SIGIO_irq,
++      enable_SIGIO_irq,
++      disable_SIGIO_irq,
++      mask_and_ack_SIGIO,
++      end_SIGIO_irq,
++      NULL
++};
++
++static struct hw_interrupt_type SIGVTALRM_irq_type = {
++      "SIGVTALRM",
++      startup_SIGVTALRM_irq,
++      shutdown_SIGVTALRM_irq,
++      enable_SIGVTALRM_irq,
++      disable_SIGVTALRM_irq,
++      mask_and_ack_SIGVTALRM,
++      end_SIGVTALRM_irq,
++      NULL
++};
++
++void __init init_IRQ(void)
++{
++      int i;
++
++      irq_desc[TIMER_IRQ].status = IRQ_DISABLED;
++      irq_desc[TIMER_IRQ].action = 0;
++      irq_desc[TIMER_IRQ].depth = 1;
++      irq_desc[TIMER_IRQ].handler = &SIGVTALRM_irq_type;
++      enable_irq(TIMER_IRQ);
++      for(i=1;i<NR_IRQS;i++){
++              irq_desc[i].status = IRQ_DISABLED;
++              irq_desc[i].action = 0;
++              irq_desc[i].depth = 1;
++              irq_desc[i].handler = &SIGIO_irq_type;
++              enable_irq(i);
++      }
++      init_irq_signals(0);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/irq_user.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/irq_user.c        1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/irq_user.c     2005-05-03 22:28:14.424417368 +0300
+@@ -0,0 +1,438 @@
++/* 
++ * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <stdlib.h>
++#include <unistd.h>
++#include <errno.h>
++#include <signal.h>
++#include <string.h>
++#include <sys/poll.h>
++#include <sys/types.h>
++#include <sys/time.h>
++#include "user_util.h"
++#include "kern_util.h"
++#include "user.h"
++#include "process.h"
++#include "signal_user.h"
++#include "sigio.h"
++#include "irq_user.h"
++#include "os.h"
++
++struct irq_fd {
++      struct irq_fd *next;
++      void *id;
++      int fd;
++      int type;
++      int irq;
++      int pid;
++      int events;
++      int current_events;
++      int freed;
++};
++
++static struct irq_fd *active_fds = NULL;
++static struct irq_fd **last_irq_ptr = &active_fds;
++
++static struct pollfd *pollfds = NULL;
++static int pollfds_num = 0;
++static int pollfds_size = 0;
++
++extern int io_count, intr_count;
++
++void sigio_handler(int sig, union uml_pt_regs *regs)
++{
++      struct irq_fd *irq_fd, *next;
++      int i, n;
++
++      if(smp_sigio_handler()) return;
++      while(1){
++              n = poll(pollfds, pollfds_num, 0);
++              if(n < 0){
++                      if(errno == EINTR) continue;
++                      printk("sigio_handler : poll returned %d, "
++                             "errno = %d\n", n, errno);
++                      break;
++              }
++              if(n == 0) break;
++
++              irq_fd = active_fds;
++              for(i = 0; i < pollfds_num; i++){
++                      if(pollfds[i].revents != 0){
++                              irq_fd->current_events = pollfds[i].revents;
++                              pollfds[i].fd = -1;
++                      }
++                      irq_fd = irq_fd->next;
++              }
++
++              for(irq_fd = active_fds; irq_fd != NULL; irq_fd = next){
++                      next = irq_fd->next;
++                      if(irq_fd->current_events != 0){
++                              irq_fd->current_events = 0;
++                              do_IRQ(irq_fd->irq, regs);
++
++                              /* This is here because the next irq may be
++                               * freed in the handler.  If a console goes
++                               * away, both the read and write irqs will be
++                               * freed.  After do_IRQ, ->next will point to
++                               * a good IRQ.
++                               * Irqs can't be freed inside their handlers,
++                               * so the next best thing is to have them
++                               * marked as needing freeing, so that they
++                               * can be freed here.
++                               */
++                              next = irq_fd->next;
++                              if(irq_fd->freed)
++                                      free_irq(irq_fd->irq, irq_fd->id);
++                      }
++              }
++      }
++}
++
++int activate_ipi(int fd, int pid)
++{
++      return(os_set_fd_async(fd, pid));
++}
++
++static void maybe_sigio_broken(int fd, int type)
++{
++      if(isatty(fd)){
++              if((type == IRQ_WRITE) && !pty_output_sigio){
++                      write_sigio_workaround();
++                      add_sigio_fd(fd, 0);
++              }
++              else if((type == IRQ_READ) && !pty_close_sigio){
++                      write_sigio_workaround();
++                      add_sigio_fd(fd, 1);                    
++              }
++      }
++}
++
++int activate_fd(int irq, int fd, int type, void *dev_id)
++{
++      struct pollfd *tmp_pfd;
++      struct irq_fd *new_fd, *irq_fd;
++      unsigned long flags;
++      int pid, events, err, n, size;
++
++      pid = os_getpid();
++      err = os_set_fd_async(fd, pid);
++      if(err < 0)
++              goto out;
++
++      new_fd = um_kmalloc(sizeof(*new_fd));
++      err = -ENOMEM;
++      if(new_fd == NULL)
++              goto out;
++
++      if(type == IRQ_READ) events = POLLIN | POLLPRI;
++      else events = POLLOUT;
++      *new_fd = ((struct irq_fd) { .next              = NULL,
++                                   .id                = dev_id,
++                                   .fd                = fd,
++                                   .type              = type,
++                                   .irq               = irq,
++                                   .pid               = pid,
++                                   .events            = events,
++                                   .current_events    = 0,
++                                   .freed             = 0  } );
++
++      /* Critical section - locked by a spinlock because this stuff can
++       * be changed from interrupt handlers.  The stuff above is done 
++       * outside the lock because it allocates memory.
++       */
++
++      /* Actually, it only looks like it can be called from interrupt
++       * context.  The culprit is reactivate_fd, which calls 
++       * maybe_sigio_broken, which calls write_sigio_workaround,
++       * which calls activate_fd.  However, write_sigio_workaround should
++       * only be called once, at boot time.  That would make it clear that
++       * this is called only from process context, and can be locked with
++       * a semaphore.
++       */
++      flags = irq_lock();
++      for(irq_fd = active_fds; irq_fd != NULL; irq_fd = irq_fd->next){
++              if((irq_fd->fd == fd) && (irq_fd->type == type)){
++                      printk("Registering fd %d twice\n", fd);
++                      printk("Irqs : %d, %d\n", irq_fd->irq, irq);
++                      printk("Ids : 0x%x, 0x%x\n", irq_fd->id, dev_id);
++                      goto out_unlock;
++              }
++      }
++
++      n = pollfds_num;
++      if(n == pollfds_size){
++              while(1){
++                      /* Here we have to drop the lock in order to call 
++                       * kmalloc, which might sleep.  If something else
++                       * came in and changed the pollfds array, we free
++                       * the buffer and try again.
++                       */
++                      irq_unlock(flags);
++                      size = (pollfds_num + 1) * sizeof(pollfds[0]);
++                      tmp_pfd = um_kmalloc(size);
++                      flags = irq_lock();
++                      if(tmp_pfd == NULL)
++                              goto out_unlock;
++                      if(n == pollfds_size)
++                              break;
++                      kfree(tmp_pfd);
++              }
++              if(pollfds != NULL){
++                      memcpy(tmp_pfd, pollfds,
++                             sizeof(pollfds[0]) * pollfds_size);
++                      kfree(pollfds);
++              }
++              pollfds = tmp_pfd;
++              pollfds_size++;
++      }
++
++      if(type == IRQ_WRITE) 
++              fd = -1;
++
++      pollfds[pollfds_num] = ((struct pollfd) { .fd   = fd,
++                                                .events       = events,
++                                                .revents      = 0 });
++      pollfds_num++;
++
++      *last_irq_ptr = new_fd;
++      last_irq_ptr = &new_fd->next;
++
++      irq_unlock(flags);
++
++      /* This calls activate_fd, so it has to be outside the critical
++       * section.
++       */
++      maybe_sigio_broken(fd, type);
++
++      return(0);
++
++ out_unlock:
++      irq_unlock(flags);
++      kfree(new_fd);
++ out:
++      return(err);
++}
++
++static void free_irq_by_cb(int (*test)(struct irq_fd *, void *), void *arg)
++{
++      struct irq_fd **prev;
++      unsigned long flags;
++      int i = 0;
++
++      flags = irq_lock();
++      prev = &active_fds;
++      while(*prev != NULL){
++              if((*test)(*prev, arg)){
++                      struct irq_fd *old_fd = *prev;
++                      if((pollfds[i].fd != -1) && 
++                         (pollfds[i].fd != (*prev)->fd)){
++                              printk("free_irq_by_cb - mismatch between "
++                                     "active_fds and pollfds, fd %d vs %d\n",
++                                     (*prev)->fd, pollfds[i].fd);
++                              goto out;
++                      }
++                      memcpy(&pollfds[i], &pollfds[i + 1],
++                             (pollfds_num - i - 1) * sizeof(pollfds[0]));
++                      pollfds_num--;
++                      if(last_irq_ptr == &old_fd->next) 
++                              last_irq_ptr = prev;
++                      *prev = (*prev)->next;
++                      if(old_fd->type == IRQ_WRITE) 
++                              ignore_sigio_fd(old_fd->fd);
++                      kfree(old_fd);
++                      continue;
++              }
++              prev = &(*prev)->next;
++              i++;
++      }
++ out:
++      irq_unlock(flags);
++}
++
++struct irq_and_dev {
++      int irq;
++      void *dev;
++};
++
++static int same_irq_and_dev(struct irq_fd *irq, void *d)
++{
++      struct irq_and_dev *data = d;
++
++      return((irq->irq == data->irq) && (irq->id == data->dev));
++}
++
++void free_irq_by_irq_and_dev(int irq, void *dev)
++{
++      struct irq_and_dev data = ((struct irq_and_dev) { .irq  = irq,
++                                                        .dev  = dev });
++
++      free_irq_by_cb(same_irq_and_dev, &data);
++}
++
++static int same_fd(struct irq_fd *irq, void *fd)
++{
++      return(irq->fd == *((int *) fd));
++}
++
++void free_irq_by_fd(int fd)
++{
++      free_irq_by_cb(same_fd, &fd);
++}
++
++static struct irq_fd *find_irq_by_fd(int fd, int irqnum, int *index_out)
++{
++      struct irq_fd *irq;
++      int i = 0;
++
++      for(irq=active_fds; irq != NULL; irq = irq->next){
++              if((irq->fd == fd) && (irq->irq == irqnum)) break;
++              i++;
++      }
++      if(irq == NULL){
++              printk("find_irq_by_fd doesn't have descriptor %d\n", fd);
++              goto out;
++      }
++      if((pollfds[i].fd != -1) && (pollfds[i].fd != fd)){
++              printk("find_irq_by_fd - mismatch between active_fds and "
++                     "pollfds, fd %d vs %d, need %d\n", irq->fd, 
++                     pollfds[i].fd, fd);
++              irq = NULL;
++              goto out;
++      }
++      *index_out = i;
++ out:
++      return(irq);
++}
++
++void free_irq_later(int irq, void *dev_id)
++{
++      struct irq_fd *irq_fd;
++      unsigned long flags;
++
++      flags = irq_lock();
++      for(irq_fd = active_fds; irq_fd != NULL; irq_fd = irq_fd->next){
++              if((irq_fd->irq == irq) && (irq_fd->id == dev_id))
++                      break;
++      }
++      if(irq_fd == NULL){
++              printk("free_irq_later found no irq, irq = %d, "
++                     "dev_id = 0x%p\n", irq, dev_id);
++              goto out;
++      }
++      irq_fd->freed = 1;
++ out:
++      irq_unlock(flags);
++}
++
++void reactivate_fd(int fd, int irqnum)
++{
++      struct irq_fd *irq;
++      unsigned long flags;
++      int i;
++
++      flags = irq_lock();
++      irq = find_irq_by_fd(fd, irqnum, &i);
++      if(irq == NULL){
++              irq_unlock(flags);
++              return;
++      }
++
++      pollfds[i].fd = irq->fd;
++
++      irq_unlock(flags);
++
++      /* This calls activate_fd, so it has to be outside the critical
++       * section.
++       */
++      maybe_sigio_broken(fd, irq->type);
++}
++
++void deactivate_fd(int fd, int irqnum)
++{
++      struct irq_fd *irq;
++      unsigned long flags;
++      int i;
++
++      flags = irq_lock();
++      irq = find_irq_by_fd(fd, irqnum, &i);
++      if(irq == NULL)
++              goto out;
++      pollfds[i].fd = -1;
++ out:
++      irq_unlock(flags);
++}
++
++int deactivate_all_fds(void)
++{
++      struct irq_fd *irq;
++      int err;
++
++      for(irq=active_fds;irq != NULL;irq = irq->next){
++              err = os_clear_fd_async(irq->fd);
++              if(err)
++                      return(err);
++      }
++
++      return(0);
++}
++
++void forward_ipi(int fd, int pid)
++{
++      int err;
++
++      err = os_set_owner(fd, pid);
++      if(err < 0)
++              printk("forward_ipi: set_owner failed, fd = %d, me = %d, "
++                     "target = %d, err = %d\n", fd, os_getpid(), pid, -err);
++}
++
++void forward_interrupts(int pid)
++{
++      struct irq_fd *irq;
++      unsigned long flags;
++      int err;
++
++      flags = irq_lock();
++      for(irq=active_fds;irq != NULL;irq = irq->next){
++              err = os_set_owner(irq->fd, pid);
++              if(err < 0){
++                      /* XXX Just remove the irq rather than
++                       * print out an infinite stream of these
++                       */
++                      printk("Failed to forward %d to pid %d, err = %d\n",
++                             irq->fd, pid, -err);
++              }
++
++              irq->pid = pid;
++      }
++      irq_unlock(flags);
++}
++
++void init_irq_signals(int on_sigstack)
++{
++      __sighandler_t h;
++      int flags;
++
++      flags = on_sigstack ? SA_ONSTACK : 0;
++      if(timer_irq_inited) h = (__sighandler_t) alarm_handler;
++      else h = boot_timer_handler;
++
++      set_handler(SIGVTALRM, h, flags | SA_RESTART, 
++                  SIGUSR1, SIGIO, SIGWINCH, SIGALRM, -1);
++      set_handler(SIGIO, (__sighandler_t) sig_handler, flags | SA_RESTART,
++                  SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1);
++      signal(SIGWINCH, SIG_IGN);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/ksyms.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/ksyms.c   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/ksyms.c        2005-05-03 23:56:02.752509760 +0300
+@@ -0,0 +1,124 @@
++/* 
++ * Copyright (C) 2001 - 2004 Jeff Dike (jdike@addtoit.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/config.h"
++#include "linux/module.h"
++#include "linux/string.h"
++#include "linux/smp_lock.h"
++#include "linux/spinlock.h"
++#include "asm/current.h"
++#include "asm/delay.h"
++#include "asm/processor.h"
++#include "asm/unistd.h"
++#include "asm/pgalloc.h"
++#include "asm/pgtable.h"
++#include "asm/page.h"
++#include "kern_util.h"
++#include "user_util.h"
++#include "mem_user.h"
++#include "os.h"
++#include "helper.h"
++
++EXPORT_SYMBOL(stop);
++EXPORT_SYMBOL(strtok);
++EXPORT_SYMBOL(uml_physmem);
++EXPORT_SYMBOL(set_signals);
++EXPORT_SYMBOL(get_signals);
++EXPORT_SYMBOL(kernel_thread);
++EXPORT_SYMBOL(__const_udelay);
++EXPORT_SYMBOL(__udelay);
++EXPORT_SYMBOL(sys_waitpid);
++EXPORT_SYMBOL(task_size);
++EXPORT_SYMBOL(flush_tlb_range);
++EXPORT_SYMBOL(host_task_size);
++EXPORT_SYMBOL(arch_validate);
++EXPORT_SYMBOL(get_kmem_end);
++
++EXPORT_SYMBOL(high_physmem);
++EXPORT_SYMBOL(empty_zero_page);
++EXPORT_SYMBOL(um_virt_to_phys);
++EXPORT_SYMBOL(__virt_to_page);
++EXPORT_SYMBOL(to_phys);
++EXPORT_SYMBOL(to_virt);
++EXPORT_SYMBOL(mode_tt);
++EXPORT_SYMBOL(handle_page_fault);
++EXPORT_SYMBOL(find_iomem);
++
++#ifdef CONFIG_MODE_TT
++EXPORT_SYMBOL(strncpy_from_user_tt);
++EXPORT_SYMBOL(copy_from_user_tt);
++EXPORT_SYMBOL(copy_to_user_tt);
++#endif
++
++#ifdef CONFIG_MODE_SKAS
++EXPORT_SYMBOL(strncpy_from_user_skas);
++EXPORT_SYMBOL(copy_to_user_skas);
++EXPORT_SYMBOL(copy_from_user_skas);
++#endif
++
++EXPORT_SYMBOL(os_stat_fd);
++EXPORT_SYMBOL(os_stat_file);
++EXPORT_SYMBOL(os_access);
++EXPORT_SYMBOL(os_print_error);
++EXPORT_SYMBOL(os_get_exec_close);
++EXPORT_SYMBOL(os_set_exec_close);
++EXPORT_SYMBOL(os_getpid);
++EXPORT_SYMBOL(os_open_file);
++EXPORT_SYMBOL(os_read_file);
++EXPORT_SYMBOL(os_write_file);
++EXPORT_SYMBOL(os_seek_file);
++EXPORT_SYMBOL(os_lock_file);
++EXPORT_SYMBOL(os_pipe);
++EXPORT_SYMBOL(os_file_type);
++EXPORT_SYMBOL(os_file_mode);
++EXPORT_SYMBOL(os_file_size);
++EXPORT_SYMBOL(os_flush_stdout);
++EXPORT_SYMBOL(os_close_file);
++EXPORT_SYMBOL(os_set_fd_async);
++EXPORT_SYMBOL(os_set_fd_block);
++EXPORT_SYMBOL(helper_wait);
++EXPORT_SYMBOL(os_shutdown_socket);
++EXPORT_SYMBOL(os_create_unix_socket);
++EXPORT_SYMBOL(os_connect_socket);
++EXPORT_SYMBOL(os_accept_connection);
++EXPORT_SYMBOL(os_ioctl_generic);
++EXPORT_SYMBOL(os_rcv_fd);
++EXPORT_SYMBOL(run_helper);
++EXPORT_SYMBOL(start_thread);
++EXPORT_SYMBOL(dump_thread);
++
++/* This is here because UML expands open to sys_open, not to a system
++ * call instruction.
++ */
++EXPORT_SYMBOL(sys_open);
++EXPORT_SYMBOL(sys_lseek);
++EXPORT_SYMBOL(sys_read);
++EXPORT_SYMBOL(sys_wait4);
++
++#ifdef CONFIG_SMP
++
++/* required for SMP */
++
++extern void FASTCALL( __write_lock_failed(rwlock_t *rw));
++EXPORT_SYMBOL_NOVERS(__write_lock_failed);
++
++extern void FASTCALL( __read_lock_failed(rwlock_t *rw));
++EXPORT_SYMBOL_NOVERS(__read_lock_failed);
++
++EXPORT_SYMBOL(kernel_flag_cacheline);
++EXPORT_SYMBOL(smp_num_cpus);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/link.ld
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/link.ld   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/link.ld        2005-05-03 22:43:46.000000000 +0300
+@@ -0,0 +1,147 @@
++OUTPUT_FORMAT("elf32-i386")
++OUTPUT_ARCH(i386)
++ENTRY(_start)
++
++SECTIONS
++{
++  . = 2684354560 + SIZEOF_HEADERS;
++
++  __binary_start = .;
++
++  .thread_private : {
++    __start_thread_private = .;
++    errno = .;
++    . += 4;
++    arch/um/kernel/tt/unmap_fin.o (.data)
++    __end_thread_private = .;
++  }
++  . = ALIGN(4096);
++  .remap : { arch/um/kernel/tt/unmap_fin.o (.text) }
++
++  . = ALIGN(4096);            /* Init code and data */
++  _stext = .;
++  __init_begin = .;
++  .text.init : { *(.text.init) }
++  . = ALIGN(4096);
++  .text      :
++  {
++    *(.text)
++    /* .gnu.warning sections are handled specially by elf32.em.  */
++    *(.gnu.warning)
++    *(.gnu.linkonce.t*)
++  }
++  .fini      : { *(.fini)    } =0x9090
++  .rodata    : { *(.rodata) *(.gnu.linkonce.r*) }
++  .rodata1   : { *(.rodata1) }
++  _etext = .;
++  PROVIDE (etext = .);
++
++  . = ALIGN(4096);
++  PROVIDE (_sdata = .);
++
++  .kstrtab : { *(.kstrtab) }
++
++  . = ALIGN(16);              /* Exception table */
++  __start___ex_table = .;
++  __ex_table : { *(__ex_table) }
++  __stop___ex_table = .;
++
++  __start___ksymtab = .;      /* Kernel symbol table */
++  __ksymtab : { *(__ksymtab) }
++  __stop___ksymtab = .;
++
++  .unprotected : { *(.unprotected) }
++  . = ALIGN(4096);
++  PROVIDE (_unprotected_end = .);
++
++  . = ALIGN(4096);
++  __uml_setup_start = .;
++  .uml.setup.init : { *(.uml.setup.init) }
++  __uml_setup_end = .;
++  __uml_help_start = .;
++  .uml.help.init : { *(.uml.help.init) }
++  __uml_help_end = .;
++  __uml_postsetup_start = .;
++  .uml.postsetup.init : { *(.uml.postsetup.init) }
++  __uml_postsetup_end = .;
++  __setup_start = .;
++  .setup.init : { *(.setup.init) }
++  __setup_end = .;
++  __initcall_start = .;
++  .initcall.init : { *(.initcall.init) }
++  __initcall_end = .;
++  __uml_initcall_start = .;
++  .uml.initcall.init : { *(.uml.initcall.init) }
++  __uml_initcall_end = .;
++  __init_end = .;
++  __exitcall_begin = .;
++  .exitcall : { *(.exitcall.exit) }
++  __exitcall_end = .;
++  __uml_exitcall_begin = .;
++  .uml.exitcall : { *(.uml.exitcall.exit) }
++  __uml_exitcall_end = .;
++
++  __preinit_array_start = .;
++  .preinit_array : { *(.preinit_array) }
++  __preinit_array_end = .;
++  __init_array_start = .;
++  .init_array : { *(.init_array) }
++  __init_array_end = .;
++  __fini_array_start = .;
++  .fini_array : { *(.fini_array) }
++  __fini_array_end = .;
++
++  .data.init : { *(.data.init) }
++
++
++  .data    :
++  {
++    . = ALIGN(32768);         /* init_task */
++    *(.data.init_task)
++    *(.data)
++    *(.gnu.linkonce.d*)
++    CONSTRUCTORS
++  }
++  .data1   : { *(.data1) }
++  .ctors         :
++  {
++    *(.ctors)
++  }
++  .dtors         :
++  {
++    *(.dtors)
++  }
++
++  .got           : { *(.got.plt) *(.got) }
++  .dynamic       : { *(.dynamic) }
++  /* We want the small data sections together, so single-instruction offsets
++     can access them all, and initialized data all before uninitialized, so
++     we can shorten the on-disk segment size.  */
++  .sdata     : { *(.sdata) }
++  _edata  =  .;
++  PROVIDE (edata = .);
++  . = ALIGN(0x1000);
++  .sbss      : 
++  {
++   __bss_start = .;
++   PROVIDE(_bss_start = .);
++   *(.sbss) 
++   *(.scommon) 
++  }
++  .bss       :
++  {
++   *(.dynbss)
++   *(.bss)
++   *(COMMON)
++  }
++  _end = . ;
++  PROVIDE (end = .);
++  /* Stabs debugging sections.  */
++  .stab 0 : { *(.stab) }
++  .stabstr 0 : { *(.stabstr) }
++  .stab.excl 0 : { *(.stab.excl) }
++  .stab.exclstr 0 : { *(.stab.exclstr) }
++  .stab.index 0 : { *(.stab.index) }
++  .stab.indexstr 0 : { *(.stab.indexstr) }
++  .comment 0 : { *(.comment) }
++}
+Index: linux-2.4.29/arch/um/kernel/link.ld.in
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/link.ld.in        1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/link.ld.in     2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,94 @@
++OUTPUT_FORMAT("ELF_FORMAT")
++OUTPUT_ARCH(ELF_ARCH)
++ENTRY(_start)
++
++SECTIONS
++{
++  . = START() + SIZEOF_HEADERS;
++
++  __binary_start = .;
++ifdef(`MODE_TT', `
++  .thread_private : {
++    __start_thread_private = .;
++    errno = .;
++    . += 4;
++    arch/um/kernel/tt/unmap_fin.o (.data)
++    __end_thread_private = .;
++  }
++  . = ALIGN(4096);
++  .remap : { arch/um/kernel/tt/unmap_fin.o (.text) }
++')
++  . = ALIGN(4096);            /* Init code and data */
++  _stext = .;
++  __init_begin = .;
++  .text.init : { *(.text.init) }
++  . = ALIGN(4096);
++  .text      :
++  {
++    *(.text)
++    /* .gnu.warning sections are handled specially by elf32.em.  */
++    *(.gnu.warning)
++    *(.gnu.linkonce.t*)
++  }
++  .fini      : { *(.fini)    } =0x9090
++  .rodata    : { *(.rodata) *(.gnu.linkonce.r*) }
++  .rodata1   : { *(.rodata1) }
++  _etext = .;
++  PROVIDE (etext = .);
++
++  . = ALIGN(4096);
++  PROVIDE (_sdata = .);
++
++include(`arch/um/kernel/common.ld.in')
++
++  .data    :
++  {
++    . = ALIGN(KERNEL_STACK_SIZE);             /* init_task */
++    *(.data.init_task)
++    *(.data)
++    *(.gnu.linkonce.d*)
++    CONSTRUCTORS
++  }
++  .data1   : { *(.data1) }
++  .ctors         :
++  {
++    *(.ctors)
++  }
++  .dtors         :
++  {
++    *(.dtors)
++  }
++
++  .got           : { *(.got.plt) *(.got) }
++  .dynamic       : { *(.dynamic) }
++  /* We want the small data sections together, so single-instruction offsets
++     can access them all, and initialized data all before uninitialized, so
++     we can shorten the on-disk segment size.  */
++  .sdata     : { *(.sdata) }
++  _edata  =  .;
++  PROVIDE (edata = .);
++  . = ALIGN(0x1000);
++  .sbss      : 
++  {
++   __bss_start = .;
++   PROVIDE(_bss_start = .);
++   *(.sbss) 
++   *(.scommon) 
++  }
++  .bss       :
++  {
++   *(.dynbss)
++   *(.bss)
++   *(COMMON)
++  }
++  _end = . ;
++  PROVIDE (end = .);
++  /* Stabs debugging sections.  */
++  .stab 0 : { *(.stab) }
++  .stabstr 0 : { *(.stabstr) }
++  .stab.excl 0 : { *(.stab.excl) }
++  .stab.exclstr 0 : { *(.stab.exclstr) }
++  .stab.index 0 : { *(.stab.index) }
++  .stab.indexstr 0 : { *(.stab.indexstr) }
++  .comment 0 : { *(.comment) }
++}
+Index: linux-2.4.29/arch/um/kernel/main.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/main.c    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/main.c 2005-05-03 22:28:14.429416608 +0300
+@@ -0,0 +1,250 @@
++/* 
++ * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <unistd.h>
++#include <stdio.h> 
++#include <stdlib.h>
++#include <string.h>
++#include <signal.h>
++#include <errno.h>
++#include <sys/resource.h>
++#include <sys/mman.h>
++#include <sys/user.h>
++#include <asm/page.h>
++#include "user_util.h"
++#include "kern_util.h"
++#include "mem_user.h"
++#include "signal_user.h"
++#include "time_user.h"
++#include "irq_user.h"
++#include "user.h"
++#include "init.h"
++#include "mode.h"
++#include "choose-mode.h"
++#include "uml-config.h"
++
++/* Set in set_stklim, which is called from main and __wrap_malloc.  
++ * __wrap_malloc only calls it if main hasn't started.
++ */
++unsigned long stacksizelim;
++
++/* Set in main */
++char *linux_prog;
++
++#define PGD_BOUND (4 * 1024 * 1024)
++#define STACKSIZE (8 * 1024 * 1024)
++#define THREAD_NAME_LEN (256)
++
++static void set_stklim(void)
++{
++      struct rlimit lim;
++
++      if(getrlimit(RLIMIT_STACK, &lim) < 0){
++              perror("getrlimit");
++              exit(1);
++      }
++      if((lim.rlim_cur == RLIM_INFINITY) || (lim.rlim_cur > STACKSIZE)){
++              lim.rlim_cur = STACKSIZE;
++              if(setrlimit(RLIMIT_STACK, &lim) < 0){
++                      perror("setrlimit");
++                      exit(1);
++              }
++      }
++      stacksizelim = (lim.rlim_cur + PGD_BOUND - 1) & ~(PGD_BOUND - 1);
++}
++
++static __init void do_uml_initcalls(void)
++{
++      initcall_t *call;
++
++      call = &__uml_initcall_start;
++      while (call < &__uml_initcall_end){;
++              (*call)();
++              call++;
++      }
++}
++
++static void last_ditch_exit(int sig)
++{
++      CHOOSE_MODE(kmalloc_ok = 0, (void) 0);
++      signal(SIGINT, SIG_DFL);
++      signal(SIGTERM, SIG_DFL);
++      signal(SIGHUP, SIG_DFL);
++      uml_cleanup();
++      exit(1);
++}
++
++extern int uml_exitcode;
++
++int main(int argc, char **argv, char **envp)
++{
++      char **new_argv;
++      sigset_t mask;
++      int ret, i;
++
++      /* Enable all signals except SIGIO - in some environments, we can 
++       * enter with some signals blocked
++       */
++
++      sigemptyset(&mask);
++      sigaddset(&mask, SIGIO);
++      if(sigprocmask(SIG_SETMASK, &mask, NULL) < 0){
++              perror("sigprocmask");
++              exit(1);
++      }
++
++#ifdef UML_CONFIG_MODE_TT
++      /* Allocate memory for thread command lines */
++      if(argc < 2 || strlen(argv[1]) < THREAD_NAME_LEN - 1){
++
++              char padding[THREAD_NAME_LEN] = { 
++                      [ 0 ...  THREAD_NAME_LEN - 2] = ' ', '\0' 
++              };
++
++              new_argv = malloc((argc + 2) * sizeof(char*));
++              if(!new_argv) {
++                      perror("Allocating extended argv");
++                      exit(1);
++              }       
++              
++              new_argv[0] = argv[0];
++              new_argv[1] = padding;
++              
++              for(i = 2; i <= argc; i++)
++                      new_argv[i] = argv[i - 1];
++              new_argv[argc + 1] = NULL;
++              
++              execvp(new_argv[0], new_argv);
++              perror("execing with extended args");
++              exit(1);
++      }       
++#endif
++
++      linux_prog = argv[0];
++
++      set_stklim();
++
++      new_argv = malloc((argc + 1) * sizeof(char *));
++      if(new_argv == NULL){
++              perror("Mallocing argv");
++              exit(1);
++      }
++      for(i=0;i<argc;i++){
++              new_argv[i] = strdup(argv[i]);
++              if(new_argv[i] == NULL){
++                      perror("Mallocing an arg");
++                      exit(1);
++              }
++      }
++      new_argv[argc] = NULL;
++
++      set_handler(SIGINT, last_ditch_exit, SA_ONESHOT | SA_NODEFER, -1);
++      set_handler(SIGTERM, last_ditch_exit, SA_ONESHOT | SA_NODEFER, -1);
++      set_handler(SIGHUP, last_ditch_exit, SA_ONESHOT | SA_NODEFER, -1);
++
++      do_uml_initcalls();
++      ret = linux_main(argc, argv);
++      
++      /* Reboot */
++      if(ret){
++              int err;
++
++              printf("\n");
++
++              /* Let any pending signals fire, then disable them.  This 
++               * ensures that they won't be delivered after the exec, when 
++               * they are definitely not expected.
++               */
++              unblock_signals();
++              disable_timer();
++              err = deactivate_all_fds();
++              if(err)
++                      printf("deactivate_all_fds failed, errno = %d\n", -err);
++
++              execvp(new_argv[0], new_argv);
++              perror("Failed to exec kernel");
++              ret = 1;
++      }
++      printf("\n");
++      return(uml_exitcode);
++}
++
++#define CAN_KMALLOC() \
++      (kmalloc_ok && CHOOSE_MODE((getpid() != tracing_pid), 1))
++
++extern void *__real_malloc(int);
++
++void *__wrap_malloc(int size)
++{
++      void *ret;
++
++      if(!CAN_KMALLOC())
++              return(__real_malloc(size));
++      else if(size <= PAGE_SIZE) /* finding contiguos pages is hard */
++              ret = um_kmalloc(size);
++      else ret = um_vmalloc(size);
++
++      /* glibc people insist that if malloc fails, errno should be
++       * set by malloc as well. So we do.
++       */
++      if(ret == NULL)
++              errno = ENOMEM;
++
++      return(ret);
++}
++
++void *__wrap_calloc(int n, int size)
++{
++      void *ptr = __wrap_malloc(n * size);
++
++      if(ptr == NULL) return(NULL);
++      memset(ptr, 0, n * size);
++      return(ptr);
++}
++
++extern void __real_free(void *);
++
++extern unsigned long high_physmem;
++
++void __wrap_free(void *ptr)
++{
++      unsigned long addr = (unsigned long) ptr;
++
++      /* We need to know how the allocation happened, so it can be correctly
++       * freed.  This is done by seeing what region of memory the pointer is
++       * in -
++       *      physical memory - kmalloc/kfree
++       *      kernel virtual memory - vmalloc/vfree
++       *      anywhere else - malloc/free
++       * If kmalloc is not yet possible, then the kernel memory regions
++       * may not be set up yet, and the variables not set up.  So,
++       * free is called.
++       *
++       * CAN_KMALLOC is checked because it would be bad to free a buffer
++       * with kmalloc/vmalloc after they have been turned off during 
++       * shutdown.
++       */
++
++      if((addr >= uml_physmem) && (addr < high_physmem)){
++              if(CAN_KMALLOC())
++                      kfree(ptr);
++      }
++      else if((addr >= start_vm) && (addr < end_vm)){
++              if(CAN_KMALLOC())
++                      vfree(ptr);
++      }
++      else __real_free(ptr);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/Makefile
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/Makefile  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/Makefile       2005-05-03 22:28:14.430416456 +0300
+@@ -0,0 +1,73 @@
++# 
++# Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++# Licensed under the GPL
++#
++
++O_TARGET = built-in.o
++
++obj-y = config.o checksum.o exec_kern.o exitcode.o filehandle.o frame_kern.o \
++      frame.o helper.o init_task.o irq.o irq_user.o ksyms.o main.o mem.o \
++      mem_user.o physmem.o process.o process_kern.o ptrace.o reboot.o \
++      resource.o sigio_user.o sigio_kern.o signal_kern.o signal_user.o \
++      smp.o syscall_kern.o syscall_user.o sysrq.o sys_call_table.o \
++      tempfile.o time.o time_kern.o tlb.o trap_kern.o trap_user.o \
++      uaccess_user.o um_arch.o umid.o user_syms.o user_util.o
++
++obj-$(CONFIG_BLK_DEV_INITRD) += initrd_kern.o initrd_user.o
++obj-$(CONFIG_GPROF) += gprof_syms.o
++obj-$(CONFIG_GCOV) += gmon_syms.o
++obj-$(CONFIG_TTY_LOG) += tty_log.o
++
++subdir-$(CONFIG_MODE_TT) += tt
++subdir-$(CONFIG_MODE_SKAS) += skas
++
++user-objs-$(CONFIG_TTY_LOG) += tty_log.o
++
++obj-y += $(join $(subdir-y),$(subdir-y:%=/%.o))
++
++# user_syms.o not included here because Rules.make has its own ideas about
++# building anything in export-objs
++
++USER_OBJS = $(filter %_user.o,$(obj-y)) $(user-objs-y) config.o helper.o \
++      main.o process.o tempfile.o time.o umid.o user_util.o 
++
++DMODULES-$(CONFIG_MODULES) = -D__CONFIG_MODULES__
++DMODVERSIONS-$(CONFIG_MODVERSIONS) = -D__CONFIG_MODVERSIONS__
++
++export-objs-$(CONFIG_GPROF) += gprof_syms.o
++export-objs-$(CONFIG_GCOV) += gmon_syms.o
++
++export-objs = ksyms.o process_kern.o signal_kern.o user_syms.o $(export-objs-y)
++
++CFLAGS_user_syms.o = -D__AUTOCONF_INCLUDED__ $(DMODULES-y) $(DMODVERSIONS-y) \
++      -I/usr/include -I../include
++
++CFLAGS_frame.o := $(patsubst -fomit-frame-pointer,,$(USER_CFLAGS))
++
++include $(TOPDIR)/Rules.make
++
++$(USER_OBJS) : %.o: %.c
++      $(CC) $(CFLAGS_$@) $(USER_CFLAGS) -c -o $@ $<
++
++# This has to be separate because it needs be compiled with frame pointers
++# regardless of how the rest of the kernel is built.
++
++frame.o: frame.c
++      $(CC) $(CFLAGS_$@) -c -o $@ $<
++
++QUOTE = 'my $$config=`cat $(TOPDIR)/.config`; $$config =~ s/"/\\"/g ; $$config =~ s/\n/\\n"\n"/g ; while(<STDIN>) { $$_ =~ s/CONFIG/$$config/; print $$_ }'
++
++config.c : config.c.in $(TOPDIR)/.config
++      $(PERL) -e $(QUOTE) < config.c.in > $@
++
++clean:
++      $(RM) config.c
++      for dir in $(subdir-y) ; do $(MAKE) -C $$dir clean; done
++
++modules:
++
++fastdep:
++
++dep:
++
++archmrproper: clean
+Index: linux-2.4.29/arch/um/kernel/mem.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/mem.c     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/mem.c  2005-05-03 22:28:14.431416304 +0300
+@@ -0,0 +1,336 @@
++/* 
++ * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/stddef.h"
++#include "linux/kernel.h"
++#include "linux/mm.h"
++#include "linux/bootmem.h"
++#include "linux/highmem.h"
++#include "asm/page.h"
++#include "asm/fixmap.h"
++#include "asm/pgalloc.h"
++#include "user_util.h"
++#include "kern_util.h"
++#include "kern.h"
++#include "mem_user.h"
++#include "uml_uaccess.h"
++#include "os.h"
++
++extern char __binary_start;
++
++/* Changed during early boot */
++unsigned long *empty_zero_page = NULL;
++unsigned long *empty_bad_page = NULL;
++pgd_t swapper_pg_dir[1024];
++unsigned long highmem;
++int kmalloc_ok = 0;
++
++static unsigned long brk_end;
++static unsigned long totalram_pages = 0;
++
++void unmap_physmem(void)
++{
++      os_unmap_memory((void *) brk_end, uml_reserved - brk_end);
++}
++
++static void map_cb(void *unused)
++{
++      map_memory(brk_end, __pa(brk_end), uml_reserved - brk_end, 1, 1, 0);
++}
++
++#ifdef CONFIG_HIGHMEM
++static void setup_highmem(unsigned long highmem_start, 
++                        unsigned long highmem_len)
++{
++      struct page *page;
++      unsigned long highmem_pfn;
++      int i;
++
++      highmem_start_page = virt_to_page(highmem_start);
++
++      highmem_pfn = __pa(highmem_start) >> PAGE_SHIFT;
++      for(i = 0; i < highmem_len >> PAGE_SHIFT; i++){
++              page = &mem_map[highmem_pfn + i];
++              ClearPageReserved(page);
++              set_bit(PG_highmem, &page->flags);
++              atomic_set(&page->count, 1);
++              __free_page(page);
++      }
++}
++#endif
++
++void mem_init(void)
++{
++      unsigned long start;
++
++        /* clear the zero-page */
++        memset((void *) empty_zero_page, 0, PAGE_SIZE);
++
++      /* Map in the area just after the brk now that kmalloc is about
++       * to be turned on.
++       */
++      brk_end = (unsigned long) UML_ROUND_UP(sbrk(0));
++      map_cb(NULL);
++      initial_thread_cb(map_cb, NULL);
++      free_bootmem(__pa(brk_end), uml_reserved - brk_end);
++      uml_reserved = brk_end;
++
++      /* Fill in any hole at the start of the binary */
++      start = (unsigned long) &__binary_start;
++      if(uml_physmem != start){
++              map_memory(uml_physmem, __pa(uml_physmem), start - uml_physmem,
++                         1, 1, 0);
++      }
++
++      /* this will put all low memory onto the freelists */
++      totalram_pages = free_all_bootmem();
++      totalram_pages += highmem >> PAGE_SHIFT;
++      num_physpages = totalram_pages;
++      printk(KERN_INFO "Memory: %luk available\n", 
++             (unsigned long) nr_free_pages() << (PAGE_SHIFT-10));
++      kmalloc_ok = 1;
++
++#ifdef CONFIG_HIGHMEM
++      setup_highmem(end_iomem, highmem);
++#endif
++}
++
++static void __init fixrange_init(unsigned long start, unsigned long end, 
++                               pgd_t *pgd_base)
++{
++      pgd_t *pgd;
++      pmd_t *pmd;
++      pte_t *pte;
++      int i, j;
++      unsigned long vaddr;
++
++      vaddr = start;
++      i = __pgd_offset(vaddr);
++      j = __pmd_offset(vaddr);
++      pgd = pgd_base + i;
++
++      for ( ; (i < PTRS_PER_PGD) && (vaddr < end); pgd++, i++) {
++              pmd = (pmd_t *)pgd;
++              for (; (j < PTRS_PER_PMD) && (vaddr != end); pmd++, j++) {
++                      if (pmd_none(*pmd)) {
++                              pte = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE);
++                              set_pmd(pmd, __pmd(_KERNPG_TABLE + 
++                                                 (unsigned long) __pa(pte)));
++                              if (pte != pte_offset(pmd, 0))
++                                      BUG();
++                      }
++                      vaddr += PMD_SIZE;
++              }
++              j = 0;
++      }
++}
++
++#ifdef CONFIG_HIGHMEM
++pte_t *kmap_pte;
++pgprot_t kmap_prot;
++
++#define kmap_get_fixmap_pte(vaddr)                                    \
++      pte_offset(pmd_offset(pgd_offset_k(vaddr), (vaddr)), (vaddr))
++
++void __init kmap_init(void)
++{
++      unsigned long kmap_vstart;
++
++      /* cache the first kmap pte */
++      kmap_vstart = __fix_to_virt(FIX_KMAP_BEGIN);
++      kmap_pte = kmap_get_fixmap_pte(kmap_vstart);
++
++      kmap_prot = PAGE_KERNEL;
++}
++
++static void init_highmem(void)
++{
++      pgd_t *pgd;
++      pmd_t *pmd;
++      pte_t *pte;
++      unsigned long vaddr;
++
++      /*
++       * Permanent kmaps:
++       */
++      vaddr = PKMAP_BASE;
++      fixrange_init(vaddr, vaddr + PAGE_SIZE*LAST_PKMAP, swapper_pg_dir);
++
++      pgd = swapper_pg_dir + __pgd_offset(vaddr);
++      pmd = pmd_offset(pgd, vaddr);
++      pte = pte_offset(pmd, vaddr);
++      pkmap_page_table = pte;
++
++      kmap_init();
++}
++
++#endif /* CONFIG_HIGHMEM */
++
++void paging_init(void)
++{
++      unsigned long zones_size[MAX_NR_ZONES], vaddr;
++      int i;
++
++      empty_zero_page = (unsigned long *) alloc_bootmem_low_pages(PAGE_SIZE);
++      empty_bad_page = (unsigned long *) alloc_bootmem_low_pages(PAGE_SIZE);
++      for(i=0;i<sizeof(zones_size)/sizeof(zones_size[0]);i++) 
++              zones_size[i] = 0;
++      zones_size[0] = (end_iomem >> PAGE_SHIFT) - (uml_physmem >> PAGE_SHIFT);
++      zones_size[2] = highmem >> PAGE_SHIFT;
++      free_area_init(zones_size);
++
++      /*
++       * Fixed mappings, only the page table structure has to be
++       * created - mappings will be set by set_fixmap():
++       */
++      vaddr = __fix_to_virt(__end_of_fixed_addresses - 1) & PMD_MASK;
++      fixrange_init(vaddr, FIXADDR_TOP, swapper_pg_dir);
++
++#if CONFIG_HIGHMEM
++      init_highmem();
++#endif
++}
++
++struct page *arch_validate(struct page *page, int mask, int order)
++{
++      unsigned long addr, zero = 0;
++      int i;
++
++ again:
++      if(page == NULL) return(page);
++      if(PageHighMem(page)) return(page);
++
++      addr = (unsigned long) page_address(page);
++      for(i = 0; i < (1 << order); i++){
++              current->thread.fault_addr = (void *) addr;
++              if(__do_copy_to_user((void *) addr, &zero, 
++                                   sizeof(zero),
++                                   &current->thread.fault_addr,
++                                   &current->thread.fault_catcher)){
++                      if(!(mask & __GFP_WAIT)) return(NULL);
++                      else break;
++              }
++              addr += PAGE_SIZE;
++      }
++      if(i == (1 << order)) return(page);
++      page = _alloc_pages(mask, order);
++      goto again;
++}
++
++/* This can't do anything because nothing in the kernel image can be freed
++ * since it's not in kernel physical memory.
++ */
++
++void free_initmem(void)
++{
++}
++
++#ifdef CONFIG_BLK_DEV_INITRD
++
++void free_initrd_mem(unsigned long start, unsigned long end)
++{
++      if (start < end)
++              printk ("Freeing initrd memory: %ldk freed\n", 
++                      (end - start) >> 10);
++      for (; start < end; start += PAGE_SIZE) {
++              ClearPageReserved(virt_to_page(start));
++              set_page_count(virt_to_page(start), 1);
++              free_page(start);
++              totalram_pages++;
++      }
++}
++      
++#endif
++
++int do_check_pgt_cache(int low, int high)
++{
++        int freed = 0;
++        if(pgtable_cache_size > high) {
++                do {
++                        if (pgd_quicklist) {
++                                free_pgd_slow(get_pgd_fast());
++                                freed++;
++                        }
++                        if (pmd_quicklist) {
++                                pmd_free_slow(pmd_alloc_one_fast(NULL, 0));
++                                freed++;
++                        }
++                        if (pte_quicklist) {
++                                pte_free_slow(pte_alloc_one_fast(NULL, 0));
++                                freed++;
++                        }
++                } while(pgtable_cache_size > low);
++        }
++        return freed;
++}
++
++void show_mem(void)
++{
++        int i, total = 0, reserved = 0;
++        int shared = 0, cached = 0;
++        int highmem = 0;
++
++        printk("Mem-info:\n");
++        show_free_areas();
++        printk("Free swap:       %6dkB\n", nr_swap_pages<<(PAGE_SHIFT-10));
++        i = max_mapnr;
++        while(i-- > 0) {
++                total++;
++                if(PageHighMem(mem_map + i))
++                        highmem++;
++                if(PageReserved(mem_map + i))
++                        reserved++;
++                else if(PageSwapCache(mem_map + i))
++                        cached++;
++                else if(page_count(mem_map + i))
++                        shared += page_count(mem_map + i) - 1;
++        }
++        printk("%d pages of RAM\n", total);
++        printk("%d pages of HIGHMEM\n", highmem);
++        printk("%d reserved pages\n", reserved);
++        printk("%d pages shared\n", shared);
++        printk("%d pages swap cached\n", cached);
++        printk("%ld pages in page table cache\n", pgtable_cache_size);
++        show_buffers();
++}
++
++/* Changed by meminfo_compat, which is a setup */
++static int meminfo_22 = 0;
++
++static int meminfo_compat(char *str)
++{
++      meminfo_22 = 1;
++      return(1);
++}
++
++__setup("22_meminfo", meminfo_compat);
++
++void si_meminfo(struct sysinfo *val)
++{
++      val->totalram = totalram_pages;
++      val->sharedram = 0;
++      val->freeram = nr_free_pages();
++      val->bufferram = atomic_read(&buffermem_pages);
++      val->totalhigh = highmem >> PAGE_SHIFT;
++      val->freehigh = nr_free_highpages();
++      val->mem_unit = PAGE_SIZE;
++      if(meminfo_22){
++              val->freeram <<= PAGE_SHIFT;
++              val->bufferram <<= PAGE_SHIFT;
++              val->totalram <<= PAGE_SHIFT;
++              val->sharedram <<= PAGE_SHIFT;
++      }
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/mem_user.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/mem_user.c        1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/mem_user.c     2005-05-03 22:28:14.433416000 +0300
+@@ -0,0 +1,271 @@
++/*
++ * arch/um/kernel/mem_user.c
++ *
++ * BRIEF MODULE DESCRIPTION
++ * user side memory routines for supporting IO memory inside user mode linux
++ *
++ * Copyright (C) 2001 RidgeRun, Inc.
++ * Author: RidgeRun, Inc.
++ *         Greg Lonnon glonnon@ridgerun.com or info@ridgerun.com
++ *
++ *  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.
++ *
++ *  THIS  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED
++ *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
++ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
++ *  NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT,
++ *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
++ *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
++ *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
++ *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
++ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
++ *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ *
++ *  You should have received a copy of the  GNU General Public License along
++ *  with this program; if not, write  to the Free Software Foundation, Inc.,
++ *  675 Mass Ave, Cambridge, MA 02139, USA.
++ */
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <stddef.h>
++#include <stdarg.h>
++#include <unistd.h>
++#include <errno.h>
++#include <string.h>
++#include <fcntl.h>
++#include <sys/types.h>
++#include <sys/mman.h>
++#include "kern_util.h"
++#include "user.h"
++#include "user_util.h"
++#include "mem_user.h"
++#include "init.h"
++#include "os.h"
++#include "tempfile.h"
++#include "kern_constants.h"
++
++extern struct mem_region physmem_region;
++
++#define TEMPNAME_TEMPLATE "vm_file-XXXXXX"
++
++static int create_tmp_file(unsigned long len)
++{
++      int fd, err;
++      char zero;
++
++      fd = make_tempfile(TEMPNAME_TEMPLATE, NULL, 1);
++      if(fd < 0) {
++              os_print_error(fd, "make_tempfile");
++              exit(1);
++      }
++
++      err = os_mode_fd(fd, 0777);
++      if(err < 0){
++              os_print_error(err, "os_mode_fd");
++              exit(1);
++      }
++      err = os_seek_file(fd, len);
++      if(err < 0){
++              os_print_error(err, "os_seek_file");
++              exit(1);
++      }
++      zero = 0;
++      err = os_write_file(fd, &zero, 1);
++      if(err != 1){
++              os_print_error(err, "os_write_file");
++              exit(1);
++      }
++
++      return(fd);
++}
++
++void check_tmpexec(void)
++{
++      void *addr;
++      int err, fd = create_tmp_file(UM_KERN_PAGE_SIZE);
++
++      addr = mmap(NULL, UM_KERN_PAGE_SIZE, 
++                  PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, fd, 0);
++      printf("Checking PROT_EXEC mmap in /tmp...");
++      fflush(stdout);
++      if(addr == MAP_FAILED){
++              err = errno;
++              perror("failed");
++              if(err == EPERM)
++                      printf("/tmp must be not mounted noexec\n");
++              exit(1);
++      }
++      printf("OK\n");
++      munmap(addr, UM_KERN_PAGE_SIZE);
++}
++
++static int have_devanon(void)
++{
++      int fd;
++
++      printk("Checking for /dev/anon on the host...");
++      fd = open("/dev/anon", O_RDWR);
++      if(fd < 0){
++              printk("Not available (open failed with errno %d)\n", errno);
++              return(0);
++      }
++
++      printk("OK\n");
++      return(1);
++}
++
++static int create_anon_file(unsigned long len)
++{
++      void *addr;
++      int fd;
++
++      fd = open("/dev/anon", O_RDWR);
++      if(fd < 0) {
++              os_print_error(fd, "opening /dev/anon");
++              exit(1);
++      }
++
++      addr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
++      if(addr == MAP_FAILED){
++              os_print_error((int) addr, "mapping physmem file");
++              exit(1);
++      }
++      munmap(addr, len);
++
++      return(fd);
++}
++
++int create_mem_file(unsigned long len)
++{
++      int err, fd;
++
++      if(have_devanon())
++              fd = create_anon_file(len);
++      else fd = create_tmp_file(len);
++
++      err = os_set_exec_close(fd, 1);
++      if(err < 0)
++              os_print_error(err, "exec_close");
++      return(fd);
++}
++
++struct iomem_region *iomem_regions = NULL;
++int iomem_size = 0;
++
++static int __init parse_iomem(char *str, int *add)
++{
++      struct iomem_region *new;
++      struct uml_stat buf;
++      char *file, *driver;
++      int fd, err, size;
++
++      driver = str;
++      file = strchr(str,',');
++      if(file == NULL){
++              printf("parse_iomem : failed to parse iomem\n");
++              goto out;
++      }
++      *file = '\0';
++      file++;
++      fd = os_open_file(file, of_rdwr(OPENFLAGS()), 0);
++      if(fd < 0){
++              os_print_error(fd, "parse_iomem - Couldn't open io file");
++              goto out;
++      }
++
++      err = os_stat_fd(fd, &buf);
++      if(err < 0){
++              os_print_error(err, "parse_iomem - cannot stat_fd file");
++              goto out_close;
++      }
++
++      new = malloc(sizeof(*new));
++      if(new == NULL){
++              perror("Couldn't allocate iomem_region struct");
++              goto out_close;
++      }
++
++      size = (buf.ust_size + UM_KERN_PAGE_SIZE) & ~(UM_KERN_PAGE_SIZE - 1);
++
++      *new = ((struct iomem_region) { .next           = iomem_regions,
++                                      .driver         = driver,
++                                      .fd             = fd,
++                                      .size           = size,
++                                      .phys           = 0,
++                                      .virt           = 0 });
++      iomem_regions = new;
++      iomem_size += new->size + UM_KERN_PAGE_SIZE;
++
++      return(0);
++ out_close:
++      os_close_file(fd);
++ out:
++      return(1);
++}
++
++__uml_setup("iomem=", parse_iomem,
++"iomem=<name>,<file>\n"
++"    Configure <file> as an IO memory region named <name>.\n\n"
++);
++
++int protect_memory(unsigned long addr, unsigned long len, int r, int w, int x,
++                 int must_succeed)
++{
++      int err;
++
++      err = os_protect_memory((void *) addr, len, r, w, x);
++      if(err < 0){
++                if(must_succeed)
++                        panic("protect failed, err = %d", -err);
++                else return(err);
++      }
++      return(0);
++}
++
++#if 0
++/* Debugging facility for dumping stuff out to the host, avoiding the timing
++ * problems that come with printf and breakpoints.
++ * Enable in case of emergency.
++ */
++
++int logging = 1;
++int logging_fd = -1;
++
++int logging_line = 0;
++char logging_buf[512];
++
++void log(char *fmt, ...)
++{
++        va_list ap;
++        struct timeval tv;
++        struct openflags flags;
++
++        if(logging == 0) return;
++        if(logging_fd < 0){
++                flags = of_create(of_trunc(of_rdwr(OPENFLAGS())));
++                logging_fd = os_open_file("log", flags, 0644);
++        }
++        gettimeofday(&tv, NULL);
++        sprintf(logging_buf, "%d\t %u.%u  ", logging_line++, tv.tv_sec, 
++                tv.tv_usec);
++        va_start(ap, fmt);
++        vsprintf(&logging_buf[strlen(logging_buf)], fmt, ap);
++        va_end(ap);
++        write(logging_fd, logging_buf, strlen(logging_buf));
++}
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/physmem.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/physmem.c 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/physmem.c      2005-05-03 22:28:14.436415544 +0300
+@@ -0,0 +1,480 @@
++/* 
++ * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/mm.h"
++#include "linux/rbtree.h"
++#include "linux/slab.h"
++#include "linux/vmalloc.h"
++#include "linux/bootmem.h"
++#include "asm/types.h"
++#include "asm/pgtable.h"
++#include "kern_util.h"
++#include "user_util.h"
++#include "mode_kern.h"
++#include "mem.h"
++#include "mem_user.h"
++#include "os.h"
++#include "kern.h"
++#include "init.h"
++
++struct phys_desc {
++      struct rb_node_s rb;
++      int fd;
++      __u64 offset;
++      void *virt;
++      unsigned long phys;
++      struct list_head list;
++};
++
++static struct rb_root_s phys_mappings = RB_ROOT;
++
++static struct rb_node_s **find_rb(void *virt)
++{
++      struct rb_node_s **n = &phys_mappings.rb_node;
++      struct phys_desc *d;
++
++      while(*n != NULL){
++              d = rb_entry(n, struct phys_desc, rb);
++              if(d->virt == virt)
++                      return(n);
++
++              if(d->virt > virt)
++                      n = &(*n)->rb_left;
++              else
++                      n = &(*n)->rb_right;
++      }
++
++      return(n);
++}
++
++static struct phys_desc *find_phys_mapping(void *virt)
++{
++      struct rb_node_s **n = find_rb(virt);
++
++      if(*n == NULL)
++              return(NULL);
++
++      return(rb_entry(n, struct phys_desc, rb));
++}
++
++static void insert_phys_mapping(struct phys_desc *desc)
++{
++      struct rb_node_s **n = find_rb(desc->virt);
++
++      if(*n != NULL)
++              panic("Physical remapping for %p already present", 
++                    desc->virt);
++
++      rb_link_node(&desc->rb, (*n)->rb_parent, n);
++      rb_insert_color(&desc->rb, &phys_mappings);
++}
++
++LIST_HEAD(descriptor_mappings);
++
++struct desc_mapping {
++      int fd;
++      struct list_head list;
++      struct list_head pages;
++};
++
++static struct desc_mapping *find_mapping(int fd)
++{
++      struct desc_mapping *desc;
++      struct list_head *ele;
++
++      list_for_each(ele, &descriptor_mappings){
++              desc = list_entry(ele, struct desc_mapping, list);
++              if(desc->fd == fd)
++                      return(desc);
++      }
++
++      return(NULL);
++}
++
++static struct desc_mapping *descriptor_mapping(int fd)
++{
++      struct desc_mapping *desc;
++
++      desc = find_mapping(fd);
++      if(desc != NULL)
++              return(desc);
++
++      desc = kmalloc(sizeof(*desc), GFP_ATOMIC);
++      if(desc == NULL)
++              return(NULL);
++
++      *desc = ((struct desc_mapping) 
++              { .fd =         fd,
++                .list =       LIST_HEAD_INIT(desc->list),
++                .pages =      LIST_HEAD_INIT(desc->pages) });
++      list_add(&desc->list, &descriptor_mappings);
++
++      return(desc);
++}
++
++int physmem_subst_mapping(void *virt, int fd, __u64 offset, int w)
++{
++      struct desc_mapping *fd_maps;
++      struct phys_desc *desc;
++      unsigned long phys;
++      int err;
++
++      phys = __pa(virt);
++      desc = find_phys_mapping(virt);
++      if(desc != NULL){
++              if((virt != desc->virt) || (fd != desc->fd) || 
++                 (offset != desc->offset))
++                      panic("Address 0x%p is already substituted\n", virt);
++              return(0);
++      }
++
++      fd_maps = descriptor_mapping(fd);
++      if(fd_maps == NULL)
++              return(-ENOMEM);
++
++      err = -ENOMEM;
++      desc = kmalloc(sizeof(*desc), GFP_ATOMIC);
++      if(desc == NULL)
++              goto out;
++
++      *desc = ((struct phys_desc) 
++              { .fd =                 fd,
++                .offset =             offset,
++                .virt =               virt,
++                .phys =               __pa(virt),
++                .list =               LIST_HEAD_INIT(desc->list) });
++      insert_phys_mapping(desc);
++
++      list_add(&desc->list, &fd_maps->pages);
++
++      virt = (void *) ((unsigned long) virt & PAGE_MASK);
++      err = os_map_memory(virt, fd, offset, PAGE_SIZE, 1, w, 0);
++      if(!err)
++              goto out;
++
++      rb_erase(&desc->rb, &phys_mappings);
++      kfree(desc);
++ out:
++      return(err);
++}
++
++static int physmem_fd = -1;
++
++static void remove_mapping(struct phys_desc *desc)
++{
++      void *virt = desc->virt;
++      int err;
++
++      rb_erase(&desc->rb, &phys_mappings);
++      list_del(&desc->list);
++      kfree(desc);
++
++      err = os_map_memory(virt, physmem_fd, __pa(virt), PAGE_SIZE, 1, 1, 0);
++      if(err)
++              panic("Failed to unmap block device page from physical memory, "
++                    "errno = %d", -err);
++}
++
++int physmem_remove_mapping(void *virt)
++{
++      struct phys_desc *desc;
++
++      virt = (void *) ((unsigned long) virt & PAGE_MASK);
++      desc = find_phys_mapping(virt);
++      if(desc == NULL)
++              return(0);
++
++      remove_mapping(desc);
++      return(1);
++}
++
++void physmem_forget_descriptor(int fd)
++{
++      struct desc_mapping *desc;
++      struct phys_desc *page;
++      struct list_head *ele, *next;
++      __u64 offset;
++      void *addr;
++      int err;
++
++      desc = find_mapping(fd);
++      if(desc == NULL)
++              return;
++
++      if(!list_empty(&desc->pages))
++              printk("Still have mapped pages on fd %d\n", fd);
++
++      list_for_each_safe(ele, next, &desc->pages){
++              page = list_entry(ele, struct phys_desc, list);
++              offset = page->offset;
++              addr = page->virt;
++              remove_mapping(page);
++              err = os_seek_file(fd, offset);
++              if(err)
++                      panic("physmem_forget_descriptor - failed to seek "
++                            "to %lld in fd %d, error = %d\n",
++                            offset, fd, -err);
++              err = os_read_file(fd, addr, PAGE_SIZE);
++              if(err < 0)
++                      panic("physmem_forget_descriptor - failed to read "
++                            "from fd %d to 0x%p, error = %d\n",
++                            fd, addr, -err);
++      }
++
++      list_del(&desc->list);
++      kfree(desc);
++}
++
++void arch_free_page(struct page *page, int order)
++{
++      void *virt;
++      int i;
++
++      for(i = 0; i < (1 << order); i++){
++              virt = __va(page_to_phys(page + i));
++              physmem_remove_mapping(virt);
++      }
++}
++
++int is_remapped(const void *virt, int fd, __u64 offset)
++{
++      struct phys_desc *desc;
++
++      desc = find_phys_mapping((void *) virt);
++      if(desc == NULL)
++              return(0);
++      if(offset != desc->offset)
++              printk("offset mismatch\n");
++      return(find_phys_mapping((void *) virt) != NULL);
++}
++
++/* Changed during early boot */
++unsigned long high_physmem;
++
++extern unsigned long physmem_size;
++
++void *to_virt(unsigned long phys)
++{
++      return((void *) uml_physmem + phys);
++}
++
++unsigned long to_phys(void *virt)
++{
++      return(((unsigned long) virt) - uml_physmem);
++}
++
++int init_maps(unsigned long physmem, unsigned long iomem, unsigned long highmem)
++{
++      struct page *p, *map;
++      unsigned long phys_len, phys_pages, highmem_len, highmem_pages;
++      unsigned long iomem_len, iomem_pages, total_len, total_pages;
++      int i;
++
++      phys_pages = physmem >> PAGE_SHIFT;
++      phys_len = phys_pages * sizeof(struct page);
++
++      iomem_pages = iomem >> PAGE_SHIFT;
++      iomem_len = iomem_pages * sizeof(struct page);
++
++      highmem_pages = highmem >> PAGE_SHIFT;
++      highmem_len = highmem_pages * sizeof(struct page);
++
++      total_pages = phys_pages + iomem_pages + highmem_pages;
++      total_len = phys_len + iomem_pages + highmem_len;
++
++      if(kmalloc_ok){
++              map = kmalloc(total_len, GFP_KERNEL);
++              if(map == NULL) 
++                      map = vmalloc(total_len);
++      }
++      else map = alloc_bootmem_low_pages(total_len);
++
++      if(map == NULL)
++              return(-ENOMEM);
++
++      for(i = 0; i < total_pages; i++){
++              p = &map[i];
++              set_page_count(p, 0);
++              SetPageReserved(p);
++              INIT_LIST_HEAD(&p->list);
++      }
++
++      mem_map = map;
++      max_mapnr = total_pages;
++      return(0);
++}
++
++struct page *phys_to_page(const unsigned long phys)
++{
++      return(&mem_map[phys >> PAGE_SHIFT]);
++}
++
++struct page *__virt_to_page(const unsigned long virt)
++{
++      return(&mem_map[__pa(virt) >> PAGE_SHIFT]);
++}
++
++unsigned long page_to_phys(struct page *page)
++{
++      return((page - mem_map) << PAGE_SHIFT);
++}
++
++pte_t mk_pte(struct page *page, pgprot_t pgprot)
++{
++      pte_t pte;
++
++      pte_val(pte) = page_to_phys(page) + pgprot_val(pgprot);
++      if(pte_present(pte)) pte_mknewprot(pte_mknewpage(pte));
++      return(pte);
++}
++
++/* Changed during early boot */
++static unsigned long kmem_top = 0;
++
++unsigned long get_kmem_end(void)
++{
++      if(kmem_top == 0) 
++              kmem_top = CHOOSE_MODE(kmem_end_tt, kmem_end_skas);
++      return(kmem_top);
++}
++
++void map_memory(unsigned long virt, unsigned long phys, unsigned long len, 
++              int r, int w, int x)
++{
++      __u64 offset;
++      int fd, err;
++
++      fd = phys_mapping(phys, &offset);
++      err = os_map_memory((void *) virt, fd, offset, len, r, w, x);
++      if(err)
++              panic("map_memory(0x%lx, %d, 0x%llx, %ld, %d, %d, %d) failed, "
++                    "err = %d\n", virt, fd, offset, len, r, w, x, err);
++}
++
++#define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT)
++
++void setup_physmem(unsigned long start, unsigned long reserve_end,
++                 unsigned long len, unsigned long highmem)
++{
++      unsigned long reserve = reserve_end - start;
++      int pfn = PFN_UP(__pa(reserve_end));
++      int delta = (len - reserve) >> PAGE_SHIFT;
++      int err, offset, bootmap_size;
++
++      physmem_fd = create_mem_file(len + highmem);
++
++      offset = uml_reserved - uml_physmem;
++      err = os_map_memory((void *) uml_reserved, physmem_fd, offset, 
++                          len - offset, 1, 1, 0);
++      if(err < 0){
++              os_print_error(err, "Mapping memory");
++              exit(1);
++      }
++
++      bootmap_size = init_bootmem(pfn, pfn + delta);
++      free_bootmem(__pa(reserve_end) + bootmap_size,
++                   len - bootmap_size - reserve);
++}
++
++int phys_mapping(unsigned long phys, __u64 *offset_out)
++{
++      struct phys_desc *desc = find_phys_mapping(__va(phys & PAGE_MASK));
++      int fd = -1;
++
++      if(desc != NULL){
++              fd = desc->fd;
++              *offset_out = desc->offset;
++      }
++      else if(phys < physmem_size){
++              fd = physmem_fd;
++              *offset_out = phys;
++      }
++      else if(phys < __pa(end_iomem)){
++              struct iomem_region *region = iomem_regions;
++      
++              while(region != NULL){
++                      if((phys >= region->phys) && 
++                         (phys < region->phys + region->size)){
++                              fd = region->fd;
++                              *offset_out = phys - region->phys;
++                              break;
++                      }
++                      region = region->next;
++              }
++      }
++      else if(phys < __pa(end_iomem) + highmem){
++              fd = physmem_fd;
++              *offset_out = phys - iomem_size;
++      }
++
++      return(fd);
++}
++
++static int __init uml_mem_setup(char *line, int *add)
++{
++      char *retptr;
++      physmem_size = memparse(line,&retptr);
++      return 0;
++}
++__uml_setup("mem=", uml_mem_setup,
++"mem=<Amount of desired ram>\n"
++"    This controls how much \"physical\" memory the kernel allocates\n"
++"    for the system. The size is specified as a number followed by\n"
++"    one of 'k', 'K', 'm', 'M', which have the obvious meanings.\n"
++"    This is not related to the amount of memory in the host.  It can\n"
++"    be more, and the excess, if it's ever used, will just be swapped out.\n"
++"     Example: mem=64M\n\n"
++);
++
++unsigned long find_iomem(char *driver, unsigned long *len_out)
++{
++      struct iomem_region *region = iomem_regions;
++      
++      while(region != NULL){
++              if(!strcmp(region->driver, driver)){
++                      *len_out = region->size;
++                      return(region->virt);
++              }
++      }
++
++      return(0);
++}
++
++int setup_iomem(void)
++{
++      struct iomem_region *region = iomem_regions;
++      unsigned long iomem_start = high_physmem + PAGE_SIZE;
++      int err;
++
++      while(region != NULL){
++              err = os_map_memory((void *) iomem_start, region->fd, 0, 
++                                  region->size, 1, 1, 0);
++              if(err)
++                      printk("Mapping iomem region for driver '%s' failed, "
++                             "errno = %d\n", region->driver, -err);
++              else {
++                      region->virt = iomem_start;
++                      region->phys = __pa(region->virt);
++              }
++
++              iomem_start += region->size + PAGE_SIZE;
++              region = region->next;
++      }
++
++      return(0);
++}
++
++__initcall(setup_iomem);
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/process.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/process.c 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/process.c      2005-05-03 22:28:14.437415392 +0300
+@@ -0,0 +1,310 @@
++/* 
++ * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <stdio.h>
++#include <unistd.h>
++#include <signal.h>
++#include <sched.h>
++#include <errno.h>
++#include <stdarg.h>
++#include <stdlib.h>
++#include <setjmp.h>
++#include <sys/time.h>
++#include <sys/ptrace.h>
++#include <sys/wait.h>
++#include <sys/mman.h>
++#include <asm/ptrace.h>
++#include <asm/sigcontext.h>
++#include <asm/unistd.h>
++#include <asm/page.h>
++#include <asm/user.h>
++#include "user_util.h"
++#include "kern_util.h"
++#include "user.h"
++#include "process.h"
++#include "signal_kern.h"
++#include "signal_user.h"
++#include "sysdep/ptrace.h"
++#include "sysdep/sigcontext.h"
++#include "irq_user.h"
++#include "ptrace_user.h"
++#include "time_user.h"
++#include "init.h"
++#include "os.h"
++#include "uml-config.h"
++#include "choose-mode.h"
++#include "mode.h"
++#ifdef UML_CONFIG_MODE_SKAS
++#include "skas.h"
++#include "skas_ptrace.h"
++#endif
++
++void init_new_thread_stack(void *sig_stack, void (*usr1_handler)(int))
++{
++      int flags = 0, pages;
++
++      if(sig_stack != NULL){
++              pages = (1 << UML_CONFIG_KERNEL_STACK_ORDER);
++              set_sigstack(sig_stack, pages * page_size());
++              flags = SA_ONSTACK;
++      }
++      if(usr1_handler) set_handler(SIGUSR1, usr1_handler, flags, -1);
++}
++
++void init_new_thread_signals(int altstack)
++{
++      int flags = altstack ? SA_ONSTACK : 0;
++
++      set_handler(SIGSEGV, (__sighandler_t) sig_handler, flags, 
++                  SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1);
++      set_handler(SIGTRAP, (__sighandler_t) sig_handler, flags, 
++                  SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1);
++      set_handler(SIGFPE, (__sighandler_t) sig_handler, flags, 
++                  SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1);
++      set_handler(SIGILL, (__sighandler_t) sig_handler, flags, 
++                  SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1);
++      set_handler(SIGBUS, (__sighandler_t) sig_handler, flags, 
++                  SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1);
++      set_handler(SIGWINCH, (__sighandler_t) sig_handler, flags, 
++                  SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1);
++      set_handler(SIGUSR2, (__sighandler_t) sig_handler, 
++                  flags, SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1);
++      signal(SIGHUP, SIG_IGN);
++
++      init_irq_signals(altstack);
++}
++
++struct tramp {
++      int (*tramp)(void *);
++      void *tramp_data;
++      unsigned long temp_stack;
++      int flags;
++      int pid;
++};
++
++/* See above for why sigkill is here */
++
++int sigkill = SIGKILL;
++
++int outer_tramp(void *arg)
++{
++      struct tramp *t;
++      int sig = sigkill;
++
++      t = arg;
++      t->pid = clone(t->tramp, (void *) t->temp_stack + page_size()/2,
++                     t->flags, t->tramp_data);
++      if(t->pid > 0) wait_for_stop(t->pid, SIGSTOP, PTRACE_CONT, NULL);
++      kill(os_getpid(), sig);
++      _exit(0);
++}
++
++int start_fork_tramp(void *thread_arg, unsigned long temp_stack, 
++                   int clone_flags, int (*tramp)(void *))
++{
++      struct tramp arg;
++      unsigned long sp;
++      int new_pid, status, err;
++
++      /* The trampoline will run on the temporary stack */
++      sp = stack_sp(temp_stack);
++
++      clone_flags |= CLONE_FILES | SIGCHLD;
++
++      arg.tramp = tramp;
++      arg.tramp_data = thread_arg;
++      arg.temp_stack = temp_stack;
++      arg.flags = clone_flags;
++
++      /* Start the process and wait for it to kill itself */
++      new_pid = clone(outer_tramp, (void *) sp, clone_flags, &arg);
++      if(new_pid < 0) 
++              return(new_pid);
++
++      CATCH_EINTR(err = waitpid(new_pid, &status, 0));
++      if(err < 0) 
++              panic("Waiting for outer trampoline failed - errno = %d", 
++                    errno);
++
++      if(!WIFSIGNALED(status) || (WTERMSIG(status) != SIGKILL))
++              panic("outer trampoline didn't exit with SIGKILL, "
++                    "status = %d", status);
++
++      return(arg.pid);
++}
++
++static int ptrace_child(void *arg)
++{
++      int pid = os_getpid();
++
++      if(ptrace(PTRACE_TRACEME, 0, 0, 0) < 0){
++              perror("ptrace");
++              os_kill_process(pid, 0);
++      }
++      os_stop_process(pid);
++      _exit(os_getpid() == pid);
++}
++
++static int start_ptraced_child(void **stack_out)
++{
++      void *stack;
++      unsigned long sp;
++      int pid, n, status;
++      
++      stack = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC,
++                   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
++      if(stack == MAP_FAILED)
++              panic("check_ptrace : mmap failed, errno = %d", errno);
++      sp = (unsigned long) stack + PAGE_SIZE - sizeof(void *);
++      pid = clone(ptrace_child, (void *) sp, SIGCHLD, NULL);
++      if(pid < 0)
++              panic("check_ptrace : clone failed, errno = %d", errno);
++      CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
++      if(n < 0)
++              panic("check_ptrace : wait failed, errno = %d", errno);
++      if(!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGSTOP))
++              panic("check_ptrace : expected SIGSTOP, got status = %d",
++                    status);
++
++      *stack_out = stack;
++      return(pid);
++}
++
++static void stop_ptraced_child(int pid, void *stack, int exitcode)
++{
++      int status, n;
++
++      if(ptrace(PTRACE_CONT, pid, 0, 0) < 0)
++              panic("check_ptrace : ptrace failed, errno = %d", errno);
++      CATCH_EINTR(n = waitpid(pid, &status, 0));
++      if(!WIFEXITED(status) || (WEXITSTATUS(status) != exitcode))
++              panic("check_ptrace : child exited with status 0x%x", status);
++
++      if(munmap(stack, PAGE_SIZE) < 0)
++              panic("check_ptrace : munmap failed, errno = %d", errno);
++}
++
++int use_sysemu = 0;
++
++void __init check_ptrace(void)
++{
++      void *stack;
++      int pid, syscall, n, status;
++
++      printk("Checking that ptrace can change system call numbers...");
++      pid = start_ptraced_child(&stack);
++
++      while(1){
++              if(ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0)
++                      panic("check_ptrace : ptrace failed, errno = %d", 
++                            errno);
++              CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
++              if(n < 0)
++                      panic("check_ptrace : wait failed, errno = %d", errno);
++              if(!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGTRAP))
++                      panic("check_ptrace : expected SIGTRAP, "
++                            "got status = %d", status);
++              
++              syscall = ptrace(PTRACE_PEEKUSER, pid, PT_SYSCALL_NR_OFFSET,
++                               0);
++              if(syscall == __NR_getpid){
++                      n = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_NR_OFFSET,
++                                 __NR_getppid);
++                      if(n < 0)
++                              panic("check_ptrace : failed to modify system "
++                                    "call, errno = %d", errno);
++                      break;
++              }
++      }
++      stop_ptraced_child(pid, stack, 0);
++      printk("OK\n");
++
++      printk("Checking syscall emulation patch for ptrace...");
++      pid = start_ptraced_child(&stack);
++      if(ptrace(PTRACE_SYSEMU, pid, 0, 0) >= 0) {
++              CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
++              if(n < 0)
++                      panic("check_ptrace : wait failed, errno = %d", errno);
++
++              if(!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGTRAP))
++                      panic("check_ptrace : expected SIGTRAP, "
++                            "got status = %d", status);
++
++              
++              n = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_RET_OFFSET,
++                         os_getpid());
++              if(n < 0)
++                      panic("check_ptrace : failed to modify system "
++                            "call return, errno = %d", errno);
++
++              stop_ptraced_child(pid, stack, 0);
++
++              printk("OK\n");
++              use_sysemu = 1;
++      }
++      else {
++              printk("missing\n");
++              stop_ptraced_child(pid, stack, 1);
++      }
++}
++
++int run_kernel_thread(int (*fn)(void *), void *arg, void **jmp_ptr)
++{
++      sigjmp_buf buf;
++      int n;
++
++      *jmp_ptr = &buf;
++      n = sigsetjmp(buf, 1);
++      if(n != 0)
++              return(n);
++      (*fn)(arg);
++      return(0);
++}
++
++int can_do_skas(void)
++{
++#ifdef UML_CONFIG_MODE_SKAS
++      struct ptrace_faultinfo fi;
++      void *stack;
++      int pid, n, ret = 1;
++
++      printf("Checking for the skas3 patch in the host...");
++      pid = start_ptraced_child(&stack);
++
++      n = ptrace(PTRACE_FAULTINFO, pid, 0, &fi);
++      if(n < 0){
++              if(errno == EIO)
++                      printf("not found\n");
++              else printf("No (unexpected errno - %d)\n", errno);
++              ret = 0;
++      }
++      else printf("found\n");
++
++      init_registers(pid);
++      stop_ptraced_child(pid, stack, 1);
++
++      printf("Checking for /proc/mm...");
++      if(os_access("/proc/mm", OS_ACC_W_OK) < 0){
++              printf("not found\n");
++              ret = 0;
++      }
++      else printf("found\n");
++
++      return(ret);
++#else
++      return(0);
++#endif
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/process_kern.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/process_kern.c    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/process_kern.c 2005-05-03 22:28:14.439415088 +0300
+@@ -0,0 +1,413 @@
++/* 
++ * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/config.h"
++#include "linux/kernel.h"
++#include "linux/sched.h"
++#include "linux/interrupt.h"
++#include "linux/mm.h"
++#include "linux/slab.h"
++#include "linux/utsname.h"
++#include "linux/fs.h"
++#include "linux/utime.h"
++#include "linux/smp_lock.h"
++#include "linux/module.h"
++#include "linux/init.h"
++#include "linux/capability.h"
++#include "linux/vmalloc.h"
++#include "linux/ptrace.h"
++#include "asm/unistd.h"
++#include "asm/mman.h"
++#include "asm/segment.h"
++#include "asm/stat.h"
++#include "asm/pgtable.h"
++#include "asm/processor.h"
++#include "asm/pgalloc.h"
++#include "asm/spinlock.h"
++#include "asm/uaccess.h"
++#include "asm/user.h"
++#include "user_util.h"
++#include "kern_util.h"
++#include "kern.h"
++#include "signal_kern.h"
++#include "signal_user.h"
++#include "init.h"
++#include "irq_user.h"
++#include "mem_user.h"
++#include "time_user.h"
++#include "tlb.h"
++#include "frame_kern.h"
++#include "sigcontext.h"
++#include "2_5compat.h"
++#include "os.h"
++#include "mode.h"
++#include "mode_kern.h"
++#include "choose-mode.h"
++
++/* This is a per-cpu array.  A processor only modifies its entry and it only
++ * cares about its entry, so it's OK if another processor is modifying its
++ * entry.
++ */
++struct cpu_task cpu_tasks[NR_CPUS] = { [0 ... NR_CPUS - 1] = { -1, NULL } };
++
++struct task_struct *get_task(int pid, int require)
++{
++        struct task_struct *ret;
++
++        read_lock(&tasklist_lock);
++      ret = find_task_by_pid(pid);
++        read_unlock(&tasklist_lock);
++
++        if(require && (ret == NULL)) panic("get_task couldn't find a task\n");
++        return(ret);
++}
++
++int external_pid(void *t)
++{
++      struct task_struct *task = t ? t : current;
++
++      return(CHOOSE_MODE_PROC(external_pid_tt, external_pid_skas, task));
++}
++
++int pid_to_processor_id(int pid)
++{
++      int i;
++
++      for(i = 0; i < smp_num_cpus; i++){
++              if(cpu_tasks[i].pid == pid) return(i);
++      }
++      return(-1);
++}
++
++void free_stack(unsigned long stack, int order)
++{
++      free_pages(stack, order);
++}
++
++unsigned long alloc_stack(int order, int atomic)
++{
++      unsigned long page;
++      int flags = GFP_KERNEL;
++
++      if(atomic) flags |= GFP_ATOMIC;
++      page = __get_free_pages(flags, order);
++      if(page == 0)
++              return(0);
++      stack_protections(page);
++      return(page);
++}
++
++int arch_kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
++{
++      int pid;
++
++      current->thread.request.u.thread.proc = fn;
++      current->thread.request.u.thread.arg = arg;
++      pid = do_fork(CLONE_VM | flags, 0, NULL, 0);
++#if 0 /* CLONE_UNTRACED for 2.6 */
++      pid = do_fork(CLONE_VM | CLONE_UNTRACED | flags, 0, NULL, 0);
++#endif
++      if(pid < 0) 
++              panic("do_fork failed in kernel_thread, errno = %d", pid);
++      return(pid);
++}
++
++void switch_mm(struct mm_struct *prev, struct mm_struct *next, 
++             struct task_struct *tsk, unsigned cpu)
++{
++      if (prev != next) 
++              clear_bit(cpu, &prev->cpu_vm_mask);
++      set_bit(cpu, &next->cpu_vm_mask);
++}
++
++void set_current(void *t)
++{
++      struct task_struct *task = t;
++
++      cpu_tasks[task->processor] = ((struct cpu_task) 
++              { external_pid(task), task });
++}
++
++void *_switch_to(void *prev, void *next)
++{
++      return(CHOOSE_MODE(_switch_to_tt(prev, next), 
++                         _switch_to_skas(prev, next)));
++}
++
++void interrupt_end(void)
++{
++      if(current->need_resched) schedule();
++      if(current->sigpending != 0) do_signal(0);
++}
++
++void release_thread(struct task_struct *task)
++{
++      CHOOSE_MODE(release_thread_tt(task), release_thread_skas(task));
++}
++
++void exit_thread(void)
++{
++      CHOOSE_MODE(exit_thread_tt(), exit_thread_skas());
++      unprotect_stack((unsigned long) current);
++}
++
++void *get_current(void)
++{
++      return(current);
++}
++
++int copy_thread(int nr, unsigned long clone_flags, unsigned long sp,
++              unsigned long stack_top, struct task_struct * p, 
++              struct pt_regs *regs)
++{
++      p->thread = (struct thread_struct) INIT_THREAD;
++      return(CHOOSE_MODE_PROC(copy_thread_tt, copy_thread_skas, nr, 
++                              clone_flags, sp, stack_top, p, regs));
++}
++
++void initial_thread_cb(void (*proc)(void *), void *arg)
++{
++      int save_kmalloc_ok = kmalloc_ok;
++
++      kmalloc_ok = 0;
++      CHOOSE_MODE_PROC(initial_thread_cb_tt, initial_thread_cb_skas, proc, 
++                       arg);
++      kmalloc_ok = save_kmalloc_ok;
++}
++
++unsigned long stack_sp(unsigned long page)
++{
++      return(page + PAGE_SIZE - sizeof(void *));
++}
++
++int current_pid(void)
++{
++      return(current->pid);
++}
++
++void cpu_idle(void)
++{
++      CHOOSE_MODE(init_idle_tt(), init_idle_skas());
++
++      atomic_inc(&init_mm.mm_count);
++      current->mm = &init_mm;
++      current->active_mm = &init_mm;
++
++      while(1){
++              /* endless idle loop with no priority at all */
++              SET_PRI(current);
++
++              /*
++               * although we are an idle CPU, we do not want to
++               * get into the scheduler unnecessarily.
++               */
++              if (current->need_resched) {
++                      schedule();
++                      check_pgt_cache();
++              }
++              idle_sleep(10);
++      }
++}
++
++int page_size(void)
++{
++      return(PAGE_SIZE);
++}
++
++int page_mask(void)
++{
++      return(PAGE_MASK);
++}
++
++void *um_virt_to_phys(struct task_struct *task, unsigned long addr, 
++                    pte_t *pte_out)
++{
++      pgd_t *pgd;
++      pmd_t *pmd;
++      pte_t *pte;
++
++      if(task->mm == NULL) 
++              return(ERR_PTR(-EINVAL));
++      pgd = pgd_offset(task->mm, addr);
++      pmd = pmd_offset(pgd, addr);
++      if(!pmd_present(*pmd)) 
++              return(ERR_PTR(-EINVAL));
++      pte = pte_offset(pmd, addr);
++      if(!pte_present(*pte)) 
++              return(ERR_PTR(-EINVAL));
++      if(pte_out != NULL)
++              *pte_out = *pte;
++      return((void *) (pte_val(*pte) & PAGE_MASK) + (addr & ~PAGE_MASK));
++}
++
++char *current_cmd(void)
++{
++#if defined(CONFIG_SMP) || defined(CONFIG_HIGHMEM)
++      return("(Unknown)");
++#else
++      void *addr = um_virt_to_phys(current, current->mm->arg_start, NULL);
++      return IS_ERR(addr) ? "(Unknown)": __va((unsigned long) addr);
++#endif
++}
++
++void force_sigbus(void)
++{
++      printk(KERN_ERR "Killing pid %d because of a lack of memory\n", 
++             current->pid);
++      lock_kernel();
++      sigaddset(&current->pending.signal, SIGBUS);
++      recalc_sigpending(current);
++      current->flags |= PF_SIGNALED;
++      do_exit(SIGBUS | 0x80);
++}
++
++void dump_thread(struct pt_regs *regs, struct user *u)
++{
++}
++
++void enable_hlt(void)
++{
++      panic("enable_hlt");
++}
++
++void disable_hlt(void)
++{
++      panic("disable_hlt");
++}
++
++extern int signal_frame_size;
++
++void *um_kmalloc(int size)
++{
++      return(kmalloc(size, GFP_KERNEL));
++}
++
++void *um_kmalloc_atomic(int size)
++{
++      return(kmalloc(size, GFP_ATOMIC));
++}
++
++void *um_vmalloc(int size)
++{
++      return(vmalloc(size));
++}
++
++unsigned long get_fault_addr(void)
++{
++      return((unsigned long) current->thread.fault_addr);
++}
++
++EXPORT_SYMBOL(get_fault_addr);
++
++void not_implemented(void)
++{
++      printk(KERN_DEBUG "Something isn't implemented in here\n");
++}
++
++EXPORT_SYMBOL(not_implemented);
++
++int user_context(unsigned long sp)
++{
++      unsigned long stack;
++
++      stack = sp & (PAGE_MASK << CONFIG_KERNEL_STACK_ORDER);
++      return(stack != current);
++}
++
++extern void remove_umid_dir(void);
++
++__uml_exitcall(remove_umid_dir);
++
++extern exitcall_t __uml_exitcall_begin, __uml_exitcall_end;
++
++void do_uml_exitcalls(void)
++{
++      exitcall_t *call;
++
++      call = &__uml_exitcall_end;
++      while (--call >= &__uml_exitcall_begin)
++              (*call)();
++}
++
++char *uml_strdup(char *string)
++{
++      char *new;
++
++      new = kmalloc(strlen(string) + 1, GFP_KERNEL);
++      if(new == NULL) return(NULL);
++      strcpy(new, string);
++      return(new);
++}
++
++void *get_init_task(void)
++{
++      return(&init_task_union.task);
++}
++
++int copy_to_user_proc(void *to, void *from, int size)
++{
++      return(copy_to_user(to, from, size));
++}
++
++int copy_from_user_proc(void *to, void *from, int size)
++{
++      return(copy_from_user(to, from, size));
++}
++
++int clear_user_proc(void *buf, int size)
++{
++      return(clear_user(buf, size));
++}
++
++int strlen_user_proc(char *str)
++{
++      return(strlen_user(str));
++}
++
++int smp_sigio_handler(void)
++{
++#ifdef CONFIG_SMP
++      int cpu = current->processor;
++
++      IPI_handler(cpu);
++      if(cpu != 0)
++              return(1);
++#endif
++      return(0);
++}
++
++int um_in_interrupt(void)
++{
++      return(in_interrupt());
++}
++
++int cpu(void)
++{
++        return(current->processor);
++}
++
++int singlestepping(void * t)
++{
++      struct task_struct *task = t ? t : current;
++
++      if ( ! (task->ptrace & PT_DTRACE) )
++              return(0);
++
++      if (task->thread.singlestep_syscall)
++              return(0);
++
++      return 1;
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/ptrace.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/ptrace.c  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/ptrace.c       2005-05-03 22:28:14.441414784 +0300
+@@ -0,0 +1,341 @@
++/* 
++ * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/sched.h"
++#include "linux/mm.h"
++#include "linux/errno.h"
++#include "linux/smp_lock.h"
++#ifdef CONFIG_PROC_MM
++#include "linux/proc_mm.h"
++#endif
++#include "asm/ptrace.h"
++#include "asm/uaccess.h"
++#include "kern_util.h"
++#include "ptrace_user.h"
++
++/*
++ * Called by kernel/ptrace.c when detaching..
++ */
++void ptrace_disable(struct task_struct *child)
++{ 
++      child->ptrace &= ~PT_DTRACE;
++      child->thread.singlestep_syscall = 0;
++}
++
++extern long do_mmap2(struct task_struct *task, unsigned long addr, 
++                   unsigned long len, unsigned long prot, 
++                   unsigned long flags, unsigned long fd,
++                   unsigned long pgoff);
++
++int sys_ptrace(long request, long pid, long addr, long data)
++{
++      struct task_struct *child;
++      int i, ret;
++
++      lock_kernel();
++      ret = -EPERM;
++      if (request == PTRACE_TRACEME) {
++              /* are we already being traced? */
++              if (current->ptrace & PT_PTRACED)
++                      goto out;
++              /* set the ptrace bit in the process flags. */
++              current->ptrace |= PT_PTRACED;
++              ret = 0;
++              goto out;
++      }
++      ret = -ESRCH;
++      read_lock(&tasklist_lock);
++      child = find_task_by_pid(pid);
++      if (child)
++              get_task_struct(child);
++      read_unlock(&tasklist_lock);
++      if (!child)
++              goto out;
++
++      ret = -EPERM;
++      if (pid == 1)           /* you may not mess with init */
++              goto out_tsk;
++
++      if (request == PTRACE_ATTACH) {
++              ret = ptrace_attach(child);
++              goto out_tsk;
++      }
++
++      ret = ptrace_check_attach(child, request == PTRACE_KILL);
++      if (ret < 0)
++              goto out_tsk;
++
++      switch (request) {
++              /* when I and D space are separate, these will need to be fixed. */
++      case PTRACE_PEEKTEXT: /* read word at location addr. */ 
++      case PTRACE_PEEKDATA: {
++              unsigned long tmp;
++              int copied;
++
++              ret = -EIO;
++              copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
++              if (copied != sizeof(tmp))
++                      break;
++              ret = put_user(tmp,(unsigned long *) data);
++              break;
++      }
++
++      /* read the word at location addr in the USER area. */
++      case PTRACE_PEEKUSR: {
++              unsigned long tmp;
++
++              ret = -EIO;
++              if ((addr & 3) || addr < 0) 
++                      break;
++
++              tmp = 0;  /* Default return condition */
++              if(addr < FRAME_SIZE_OFFSET){
++                      tmp = getreg(child, addr);
++              }
++              else if((addr >= offsetof(struct user, u_debugreg[0])) &&
++                      (addr <= offsetof(struct user, u_debugreg[7]))){
++                      addr -= offsetof(struct user, u_debugreg[0]);
++                      addr = addr >> 2;
++                      tmp = child->thread.arch.debugregs[addr];
++              }
++              ret = put_user(tmp, (unsigned long *) data);
++              break;
++      }
++
++      /* when I and D space are separate, this will have to be fixed. */
++      case PTRACE_POKETEXT: /* write the word at location addr. */
++      case PTRACE_POKEDATA:
++              ret = -EIO;
++              if (access_process_vm(child, addr, &data, sizeof(data), 
++                                    1) != sizeof(data))
++                      break;
++              ret = 0;
++              break;
++
++      case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
++              ret = -EIO;
++              if ((addr & 3) || addr < 0)
++                      break;
++
++              if (addr < FRAME_SIZE_OFFSET) {
++                      ret = putreg(child, addr, data);
++                      break;
++              }
++              else if((addr >= offsetof(struct user, u_debugreg[0])) &&
++                      (addr <= offsetof(struct user, u_debugreg[7]))){
++                        addr -= offsetof(struct user, u_debugreg[0]);
++                        addr = addr >> 2;
++                        if((addr == 4) || (addr == 5)) break;
++                        child->thread.arch.debugregs[addr] = data;
++                        ret = 0;
++              }
++
++              break;
++
++      case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
++      case PTRACE_CONT: { /* restart after signal. */
++              ret = -EIO;
++              if ((unsigned long) data > _NSIG)
++                      break;
++
++              child->ptrace &= ~PT_DTRACE;
++              child->thread.singlestep_syscall = 0;
++
++              if (request == PTRACE_SYSCALL)
++                      child->ptrace |= PT_TRACESYS;
++              else
++                      child->ptrace &= ~PT_TRACESYS;
++              child->exit_code = data;
++              wake_up_process(child);
++              ret = 0;
++              break;
++      }
++
++/*
++ * make the child exit.  Best I can do is send it a sigkill. 
++ * perhaps it should be put in the status that it wants to 
++ * exit.
++ */
++      case PTRACE_KILL: {
++              ret = 0;
++              if (child->state == TASK_ZOMBIE)        /* already dead */
++                      break;
++
++              child->ptrace &= ~PT_DTRACE;
++              child->thread.singlestep_syscall = 0;
++              child->exit_code = SIGKILL;
++              wake_up_process(child);
++              break;
++      }
++
++      case PTRACE_SINGLESTEP: {  /* set the trap flag. */
++              ret = -EIO;
++              if ((unsigned long) data > _NSIG)
++                      break;
++              child->ptrace &= ~PT_TRACESYS;
++              child->ptrace |= PT_DTRACE;
++              child->thread.singlestep_syscall = 0;
++              child->exit_code = data;
++              /* give it a chance to run. */
++              wake_up_process(child);
++              ret = 0;
++              break;
++      }
++
++      case PTRACE_DETACH:
++              /* detach a process that was attached. */
++              ret = ptrace_detach(child, data);
++              break;
++
++#ifdef PTRACE_GETREGS
++      case PTRACE_GETREGS: { /* Get all gp regs from the child. */
++              if (!access_ok(VERIFY_WRITE, (unsigned long *)data, 
++                             FRAME_SIZE_OFFSET)) {
++                      ret = -EIO;
++                      break;
++              }
++              for ( i = 0; i < FRAME_SIZE_OFFSET; i += sizeof(long) ) {
++                      __put_user(getreg(child, i), (unsigned long *) data);
++                      data += sizeof(long);
++              }
++              ret = 0;
++              break;
++      }
++#endif
++#ifdef PTRACE_SETREGS
++      case PTRACE_SETREGS: { /* Set all gp regs in the child. */
++              unsigned long tmp = 0;
++              if (!access_ok(VERIFY_READ, (unsigned *)data, 
++                             FRAME_SIZE_OFFSET)) {
++                      ret = -EIO;
++                      break;
++              }
++              for ( i = 0; i < FRAME_SIZE_OFFSET; i += sizeof(long) ) {
++                      __get_user(tmp, (unsigned long *) data);
++                      putreg(child, i, tmp);
++                      data += sizeof(long);
++              }
++              ret = 0;
++              break;
++      }
++#endif
++#ifdef PTRACE_GETFPREGS
++      case PTRACE_GETFPREGS: /* Get the child FPU state. */
++              ret = get_fpregs(data, child);
++              break;
++#endif
++#ifdef PTRACE_SETFPREGS
++      case PTRACE_SETFPREGS: /* Set the child FPU state. */
++              ret = set_fpregs(data, child);
++              break;
++#endif
++#ifdef PTRACE_GETFPXREGS
++      case PTRACE_GETFPXREGS: /* Get the child FPU state. */
++              ret = get_fpxregs(data, child);
++              break;
++#endif
++#ifdef PTRACE_SETFPXREGS
++      case PTRACE_SETFPXREGS: /* Set the child FPU state. */
++              ret = set_fpxregs(data, child);
++              break;
++#endif
++      case PTRACE_FAULTINFO: {
++              struct ptrace_faultinfo fault;
++
++              fault = ((struct ptrace_faultinfo) 
++                      { .is_write     = child->thread.err,
++                        .addr         = child->thread.cr2 });
++              ret = copy_to_user((unsigned long *) data, &fault, 
++                                 sizeof(fault));
++              if(ret)
++                      break;
++              break;
++      }
++      case PTRACE_SIGPENDING:
++              ret = copy_to_user((unsigned long *) data, 
++                                 &child->pending.signal,
++                                 sizeof(child->pending.signal));
++              break;
++
++      case PTRACE_LDT: {
++              struct ptrace_ldt ldt;
++
++              if(copy_from_user(&ldt, (unsigned long *) data, 
++                                sizeof(ldt))){
++                      ret = -EIO;
++                      break;
++              }
++
++              /* This one is confusing, so just punt and return -EIO for 
++               * now
++               */
++              ret = -EIO;
++              break;
++      }
++#ifdef CONFIG_PROC_MM
++      case PTRACE_SWITCH_MM: {
++              struct mm_struct *old = child->mm;
++              struct mm_struct *new = proc_mm_get_mm(data);
++
++              if(IS_ERR(new)){
++                      ret = PTR_ERR(new);
++                      break;
++              }
++
++              atomic_inc(&new->mm_users);
++              child->mm = new;
++              child->active_mm = new;
++              mmput(old);
++              ret = 0;
++              break;
++      }
++#endif
++      default:
++              ret = -EIO;
++              break;
++      }
++ out_tsk:
++      free_task_struct(child);
++ out:
++      unlock_kernel();
++      return ret;
++}
++
++void syscall_trace(void)
++{
++      int is_singlestep = (current->ptrace & PT_DTRACE);
++
++      if ((current->ptrace & (PT_PTRACED|PT_TRACESYS))
++          != (PT_PTRACED|PT_TRACESYS) && !is_singlestep)
++              return;
++      current->exit_code = SIGTRAP;
++      current->state = TASK_STOPPED;
++      notify_parent(current, SIGCHLD);
++
++      schedule();
++      /*
++       * this isn't the same as continuing with a signal, but it will do
++       * for normal use.  strace only continues with a signal if the
++       * stopping signal is not SIGTRAP.  -brl
++       */
++      if (current->exit_code) {
++              send_sig(current->exit_code, current, 1);
++              current->exit_code = 0;
++      }
++
++      if(is_syscall(PT_REGS_IP(&current->thread.regs)))
++              current->thread.singlestep_syscall = 1;
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/reboot.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/reboot.c  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/reboot.c       2005-05-03 22:28:14.441414784 +0300
+@@ -0,0 +1,73 @@
++/* 
++ * Copyright (C) 2000, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/sched.h"
++#include "user_util.h"
++#include "kern_util.h"
++#include "kern.h"
++#include "os.h"
++#include "mode.h"
++#include "choose-mode.h"
++
++#ifdef CONFIG_SMP
++static void kill_idlers(int me)
++{
++#ifdef CONFIG_MODE_TT
++      struct task_struct *p;
++      int i;
++
++      for(i = 0; i < sizeof(init_tasks)/sizeof(init_tasks[0]); i++){
++              p = init_tasks[i];
++              if((p != NULL) && (p->thread.mode.tt.extern_pid != me) &&
++                 (p->thread.mode.tt.extern_pid != -1))
++                      os_kill_process(p->thread.mode.tt.extern_pid, 0);
++      }
++#endif
++}
++#endif
++
++static void kill_off_processes(void)
++{
++      CHOOSE_MODE(kill_off_processes_tt(), kill_off_processes_skas());
++#ifdef CONFIG_SMP
++      kill_idlers(os_getpid());
++#endif
++}
++
++void uml_cleanup(void)
++{
++      kill_off_processes();
++      do_uml_exitcalls();
++}
++
++void machine_restart(char * __unused)
++{
++      do_uml_exitcalls();
++      kill_off_processes();
++      CHOOSE_MODE(reboot_tt(), reboot_skas());
++}
++
++void machine_power_off(void)
++{
++      do_uml_exitcalls();
++      kill_off_processes();
++      CHOOSE_MODE(halt_tt(), halt_skas());
++}
++
++void machine_halt(void)
++{
++      machine_power_off();
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/resource.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/resource.c        1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/resource.c     2005-05-03 22:28:14.442414632 +0300
+@@ -0,0 +1,23 @@
++/* 
++ * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/pci.h"
++
++unsigned long resource_fixup(struct pci_dev * dev, struct resource * res,
++                           unsigned long start, unsigned long size)
++{
++      return start;
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/sigio_kern.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/sigio_kern.c      1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/sigio_kern.c   2005-05-03 22:28:14.443414480 +0300
+@@ -0,0 +1,61 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/kernel.h"
++#include "linux/list.h"
++#include "linux/slab.h"
++#include "asm/irq.h"
++#include "init.h"
++#include "sigio.h"
++#include "irq_user.h"
++#include "irq_kern.h"
++
++/* Protected by sigio_lock() called from write_sigio_workaround */
++static int sigio_irq_fd = -1;
++
++static void sigio_interrupt(int irq, void *data, struct pt_regs *unused)
++{
++      read_sigio_fd(sigio_irq_fd);
++      reactivate_fd(sigio_irq_fd, SIGIO_WRITE_IRQ);
++}
++
++int write_sigio_irq(int fd)
++{
++      int err;
++
++      err = um_request_irq(SIGIO_WRITE_IRQ, fd, IRQ_READ, sigio_interrupt,
++                           SA_INTERRUPT | SA_SAMPLE_RANDOM, "write sigio", 
++                           NULL);
++      if(err){
++              printk("write_sigio_irq : um_request_irq failed, err = %d\n",
++                     err);
++              return(-1);
++      }
++      sigio_irq_fd = fd;
++      return(0);
++}
++
++static spinlock_t sigio_spinlock = SPIN_LOCK_UNLOCKED;
++
++void sigio_lock(void)
++{
++      spin_lock(&sigio_spinlock);
++}
++
++void sigio_unlock(void)
++{
++      spin_unlock(&sigio_spinlock);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/sigio_user.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/sigio_user.c      1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/sigio_user.c   2005-05-03 22:28:14.445414176 +0300
+@@ -0,0 +1,436 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <unistd.h>
++#include <stdlib.h>
++#include <termios.h>
++#include <pty.h>
++#include <signal.h>
++#include <errno.h>
++#include <string.h>
++#include <sched.h>
++#include <sys/socket.h>
++#include <sys/poll.h>
++#include "init.h"
++#include "user.h"
++#include "kern_util.h"
++#include "user_util.h"
++#include "sigio.h"
++#include "helper.h"
++#include "os.h"
++
++/* Changed during early boot */
++int pty_output_sigio = 0;
++int pty_close_sigio = 0;
++
++/* Used as a flag during SIGIO testing early in boot */
++static volatile int got_sigio = 0;
++
++void __init handler(int sig)
++{
++      got_sigio = 1;
++}
++
++struct openpty_arg {
++      int master;
++      int slave;
++      int err;
++};
++
++static void openpty_cb(void *arg)
++{
++      struct openpty_arg *info = arg;
++
++      info->err = 0;
++      if(openpty(&info->master, &info->slave, NULL, NULL, NULL))
++              info->err = -errno;
++}
++
++void __init check_one_sigio(void (*proc)(int, int))
++{
++      struct sigaction old, new;
++      struct openpty_arg pty = { .master = -1, .slave = -1 };
++      int master, slave, err;
++
++      initial_thread_cb(openpty_cb, &pty);
++      if(pty.err){
++              printk("openpty failed, errno = %d\n", -pty.err);
++              return;
++      }
++
++      master = pty.master;
++      slave = pty.slave;
++
++      if((master == -1) || (slave == -1)){
++              printk("openpty failed to allocate a pty\n");
++              return;
++      }
++
++      /* Not now, but complain so we now where we failed. */
++      err = raw(master);
++      if (err < 0)
++              panic("check_sigio : __raw failed, errno = %d\n", -err);
++
++      err = os_sigio_async(master, slave);
++      if(err < 0)
++              panic("tty_fds : sigio_async failed, err = %d\n", -err);
++
++      if(sigaction(SIGIO, NULL, &old) < 0)
++              panic("check_sigio : sigaction 1 failed, errno = %d\n", errno);
++      new = old;
++      new.sa_handler = handler;
++      if(sigaction(SIGIO, &new, NULL) < 0)
++              panic("check_sigio : sigaction 2 failed, errno = %d\n", errno);
++
++      got_sigio = 0;
++      (*proc)(master, slave);
++              
++      os_close_file(master);
++      os_close_file(slave);
++
++      if(sigaction(SIGIO, &old, NULL) < 0)
++              panic("check_sigio : sigaction 3 failed, errno = %d\n", errno);
++}
++
++static void tty_output(int master, int slave)
++{
++      int n;
++      char buf[512];
++
++      printk("Checking that host ptys support output SIGIO...");
++
++      memset(buf, 0, sizeof(buf));
++
++      while(os_write_file(master, buf, sizeof(buf)) > 0) ;
++      if(errno != EAGAIN)
++              panic("check_sigio : write failed, errno = %d\n", errno);
++      while(((n = os_read_file(slave, buf, sizeof(buf))) > 0) && !got_sigio) ;
++
++      if(got_sigio){
++              printk("Yes\n");
++              pty_output_sigio = 1;
++      }
++      else if(n == -EAGAIN) printk("No, enabling workaround\n");
++      else panic("check_sigio : read failed, err = %d\n", n);
++}
++
++static void tty_close(int master, int slave)
++{
++      printk("Checking that host ptys support SIGIO on close...");
++
++      os_close_file(slave);
++      if(got_sigio){
++              printk("Yes\n");
++              pty_close_sigio = 1;
++      }
++      else printk("No, enabling workaround\n");
++}
++
++void __init check_sigio(void)
++{
++      if((os_access("/dev/ptmx", OS_ACC_R_OK) < 0) &&
++         (os_access("/dev/ptyp0", OS_ACC_R_OK) < 0)){
++              printk("No pseudo-terminals available - skipping pty SIGIO "
++                     "check\n");
++              return;
++      }
++      check_one_sigio(tty_output);
++      check_one_sigio(tty_close);
++}
++
++/* Protected by sigio_lock(), also used by sigio_cleanup, which is an 
++ * exitcall.
++ */
++static int write_sigio_pid = -1;
++
++/* These arrays are initialized before the sigio thread is started, and
++ * the descriptors closed after it is killed.  So, it can't see them change.
++ * On the UML side, they are changed under the sigio_lock.
++ */
++static int write_sigio_fds[2] = { -1, -1 };
++static int sigio_private[2] = { -1, -1 };
++
++struct pollfds {
++      struct pollfd *poll;
++      int size;
++      int used;
++};
++
++/* Protected by sigio_lock().  Used by the sigio thread, but the UML thread
++ * synchronizes with it.
++ */
++struct pollfds current_poll = {
++      .poll           = NULL,
++      .size           = 0,
++      .used           = 0
++};
++
++struct pollfds next_poll = {
++      .poll           = NULL,
++      .size           = 0,
++      .used           = 0
++};
++
++static int write_sigio_thread(void *unused)
++{
++      struct pollfds *fds, tmp;
++      struct pollfd *p;
++      int i, n, respond_fd;
++      char c;
++
++      fds = &current_poll;
++      while(1){
++              n = poll(fds->poll, fds->used, -1);
++              if(n < 0){
++                      if(errno == EINTR) continue;
++                      printk("write_sigio_thread : poll returned %d, "
++                             "errno = %d\n", n, errno);
++              }
++              for(i = 0; i < fds->used; i++){
++                      p = &fds->poll[i];
++                      if(p->revents == 0) continue;
++                      if(p->fd == sigio_private[1]){
++                              n = os_read_file(sigio_private[1], &c, sizeof(c));
++                              if(n != sizeof(c))
++                                      printk("write_sigio_thread : "
++                                             "read failed, err = %d\n", -n);
++                              tmp = current_poll;
++                              current_poll = next_poll;
++                              next_poll = tmp;
++                              respond_fd = sigio_private[1];
++                      }
++                      else {
++                              respond_fd = write_sigio_fds[1];
++                              fds->used--;
++                              memmove(&fds->poll[i], &fds->poll[i + 1],
++                                      (fds->used - i) * sizeof(*fds->poll));
++                      }
++
++                      n = os_write_file(respond_fd, &c, sizeof(c));
++                      if(n != sizeof(c))
++                              printk("write_sigio_thread : write failed, "
++                                     "err = %d\n", -n);
++              }
++      }
++}
++
++static int need_poll(int n)
++{
++      if(n <= next_poll.size){
++              next_poll.used = n;
++              return(0);
++      }
++      if(next_poll.poll != NULL) kfree(next_poll.poll);
++      next_poll.poll = um_kmalloc_atomic(n * sizeof(struct pollfd));
++      if(next_poll.poll == NULL){
++              printk("need_poll : failed to allocate new pollfds\n");
++              next_poll.size = 0;
++              next_poll.used = 0;
++              return(-1);
++      }
++      next_poll.size = n;
++      next_poll.used = n;
++      return(0);
++}
++
++static void update_thread(void)
++{
++      unsigned long flags;
++      int n;
++      char c;
++
++      flags = set_signals(0);
++      n = os_write_file(sigio_private[0], &c, sizeof(c));
++      if(n != sizeof(c)){
++              printk("update_thread : write failed, err = %d\n", -n);
++              goto fail;
++      }
++
++      n = os_read_file(sigio_private[0], &c, sizeof(c));
++      if(n != sizeof(c)){
++              printk("update_thread : read failed, err = %d\n", -n);
++              goto fail;
++      }
++
++      set_signals(flags);
++      return;
++ fail:
++      sigio_lock();
++      if(write_sigio_pid != -1) 
++              os_kill_process(write_sigio_pid, 1);
++      write_sigio_pid = -1;
++      os_close_file(sigio_private[0]);
++      os_close_file(sigio_private[1]);        
++      os_close_file(write_sigio_fds[0]);
++      os_close_file(write_sigio_fds[1]);
++      sigio_unlock();
++      set_signals(flags);
++}
++
++int add_sigio_fd(int fd, int read)
++{
++      int err = 0, i, n, events;
++
++      sigio_lock();
++      for(i = 0; i < current_poll.used; i++){
++              if(current_poll.poll[i].fd == fd) 
++                      goto out;
++      }
++
++      n = current_poll.used + 1;
++      err = need_poll(n);
++      if(err) 
++              goto out;
++
++      for(i = 0; i < current_poll.used; i++)
++              next_poll.poll[i] = current_poll.poll[i];
++
++      if(read) events = POLLIN;
++      else events = POLLOUT;
++
++      next_poll.poll[n - 1] = ((struct pollfd) { .fd          = fd,
++                                                 .events      = events,
++                                                 .revents     = 0 });
++      update_thread();
++ out:
++      sigio_unlock();
++      return(err);
++}
++
++int ignore_sigio_fd(int fd)
++{
++      struct pollfd *p;
++      int err = 0, i, n = 0;
++
++      sigio_lock();
++      for(i = 0; i < current_poll.used; i++){
++              if(current_poll.poll[i].fd == fd) break;
++      }
++      if(i == current_poll.used)
++              goto out;
++      
++      err = need_poll(current_poll.used - 1);
++      if(err)
++              goto out;
++
++      for(i = 0; i < current_poll.used; i++){
++              p = &current_poll.poll[i];
++              if(p->fd != fd) next_poll.poll[n++] = current_poll.poll[i];
++      }
++      if(n == i){
++              printk("ignore_sigio_fd : fd %d not found\n", fd);
++              err = -1;
++              goto out;
++      }
++
++      update_thread();
++ out:
++      sigio_unlock();
++      return(err);
++}
++
++static int setup_initial_poll(int fd)
++{
++      struct pollfd *p;
++
++      p = um_kmalloc(sizeof(struct pollfd));
++      if(p == NULL){
++              printk("setup_initial_poll : failed to allocate poll\n");
++              return(-1);
++      }
++      *p = ((struct pollfd) { .fd     = fd,
++                              .events         = POLLIN,
++                              .revents        = 0 });
++      current_poll = ((struct pollfds) { .poll        = p,
++                                         .used        = 1,
++                                         .size        = 1 });
++      return(0);
++}
++
++void write_sigio_workaround(void)
++{
++      unsigned long stack;
++      int err;
++
++      sigio_lock();
++      if(write_sigio_pid != -1)
++              goto out;
++
++      err = os_pipe(write_sigio_fds, 1, 1);
++      if(err < 0){
++              printk("write_sigio_workaround - os_pipe 1 failed, "
++                     "err = %d\n", -err);
++              goto out;
++      }
++      err = os_pipe(sigio_private, 1, 1);
++      if(err < 0){
++              printk("write_sigio_workaround - os_pipe 2 failed, "
++                     "err = %d\n", -err);
++              goto out_close1;
++      }
++      if(setup_initial_poll(sigio_private[1]))
++              goto out_close2;
++
++      write_sigio_pid = run_helper_thread(write_sigio_thread, NULL, 
++                                          CLONE_FILES | CLONE_VM, &stack, 0);
++
++      if(write_sigio_pid < 0) goto out_close2;
++
++      if(write_sigio_irq(write_sigio_fds[0])) 
++              goto out_kill;
++
++ out:
++      sigio_unlock();
++      return;
++
++ out_kill:
++      os_kill_process(write_sigio_pid, 1);
++      write_sigio_pid = -1;
++ out_close2:
++      os_close_file(sigio_private[0]);
++      os_close_file(sigio_private[1]);        
++ out_close1:
++      os_close_file(write_sigio_fds[0]);
++      os_close_file(write_sigio_fds[1]);
++      sigio_unlock();
++}
++
++int read_sigio_fd(int fd)
++{
++      int n;
++      char c;
++
++      n = os_read_file(fd, &c, sizeof(c));
++      if(n != sizeof(c)){
++              if(n < 0) {
++                      printk("read_sigio_fd - read failed, err = %d\n", -n);
++                      return(n);
++              } 
++              else { 
++                      printk("read_sigio_fd - short read, bytes = %d\n", n);
++                      return(-EIO);
++              }
++      }
++      return(n);
++}
++
++static void sigio_cleanup(void)
++{
++      if(write_sigio_pid != -1)
++              os_kill_process(write_sigio_pid, 1);
++}
++
++__uml_exitcall(sigio_cleanup);
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/signal_kern.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/signal_kern.c     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/signal_kern.c  2005-05-03 22:28:14.447413872 +0300
+@@ -0,0 +1,368 @@
++/* 
++ * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/config.h"
++#include "linux/stddef.h"
++#include "linux/sys.h"
++#include "linux/sched.h"
++#include "linux/wait.h"
++#include "linux/kernel.h"
++#include "linux/smp_lock.h"
++#include "linux/module.h"
++#include "linux/slab.h"
++#include "asm/signal.h"
++#include "asm/uaccess.h"
++#include "asm/ucontext.h"
++#include "user_util.h"
++#include "kern_util.h"
++#include "signal_kern.h"
++#include "signal_user.h"
++#include "kern.h"
++#include "frame_kern.h"
++#include "sigcontext.h"
++#include "mode.h"
++
++EXPORT_SYMBOL(block_signals);
++EXPORT_SYMBOL(unblock_signals);
++
++static void force_segv(int sig)
++{
++      if(sig == SIGSEGV){
++              struct k_sigaction *ka;
++
++              ka = &current->sig->action[SIGSEGV - 1];
++              ka->sa.sa_handler = SIG_DFL;
++      }
++      force_sig(SIGSEGV, current);
++}
++
++#define _S(nr) (1<<((nr)-1))
++
++#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))
++
++/*
++ * OK, we're invoking a handler
++ */   
++static void handle_signal(struct pt_regs *regs, unsigned long signr, 
++                        struct k_sigaction *ka, siginfo_t *info, 
++                        sigset_t *oldset, int error)
++{
++        __sighandler_t handler;
++      void (*restorer)(void);
++      unsigned long sp;
++      sigset_t save;
++      int err, ret;
++
++      err = PT_REGS_SYSCALL_RET(&current->thread.regs);
++      ret = 0;
++      switch(err){
++      case -ERESTARTNOHAND:
++              ret = -EINTR;
++              break;
++
++      case -ERESTARTSYS:
++              if (!(ka->sa.sa_flags & SA_RESTART)) {
++                      ret = -EINTR;
++                      break;
++              }
++              /* fallthrough */
++      case -ERESTARTNOINTR:
++              PT_REGS_RESTART_SYSCALL(regs);
++              PT_REGS_ORIG_SYSCALL(regs) = PT_REGS_SYSCALL_NR(regs);
++
++              /* This is because of the UM_SET_SYSCALL_RETURN and the fact
++               * that on i386 the system call number and return value are
++               * in the same register.  When the system call restarts, %eax
++               * had better have the system call number in it.  Since the
++               * return value doesn't matter (except that it shouldn't be
++               * -ERESTART*), we'll stick the system call number there.
++               */
++              ret = PT_REGS_SYSCALL_NR(regs);
++              break;
++      }
++
++      handler = ka->sa.sa_handler;
++      save = *oldset;
++
++      if (ka->sa.sa_flags & SA_ONESHOT)
++              ka->sa.sa_handler = SIG_DFL;
++
++      if (!(ka->sa.sa_flags & SA_NODEFER)) {
++              spin_lock_irq(&current->sigmask_lock);
++              sigorsets(&current->blocked, &current->blocked, 
++                        &ka->sa.sa_mask);
++              sigaddset(&current->blocked, signr);
++              recalc_sigpending(current);
++              spin_unlock_irq(&current->sigmask_lock);
++      }
++
++      sp = PT_REGS_SP(regs);
++
++      if((ka->sa.sa_flags & SA_ONSTACK) && (sas_ss_flags(sp) == 0))
++              sp = current->sas_ss_sp + current->sas_ss_size;
++      
++      if(error != 0) 
++              PT_REGS_SET_SYSCALL_RETURN(regs, ret);
++
++      if (ka->sa.sa_flags & SA_RESTORER) restorer = ka->sa.sa_restorer;
++      else restorer = NULL;
++
++      if(ka->sa.sa_flags & SA_SIGINFO)
++              err = setup_signal_stack_si(sp, signr, (unsigned long) handler,
++                                          restorer, regs, info, &save);
++      else
++              err = setup_signal_stack_sc(sp, signr, (unsigned long) handler,
++                                          restorer, regs, &save);
++      if(err)
++              force_segv(signr);
++}
++
++/*
++ * Note that 'init' is a special process: it doesn't get signals it doesn't
++ * want to handle. Thus you cannot kill init even with a SIGKILL even by
++ * mistake.
++ */
++
++static int kern_do_signal(struct pt_regs *regs, sigset_t *oldset, int error)
++{
++      siginfo_t info;
++      struct k_sigaction *ka;
++
++      if (!oldset)
++              oldset = &current->blocked;
++
++      for (;;) {
++              unsigned long signr;
++
++              spin_lock_irq(&current->sigmask_lock);
++              signr = dequeue_signal(&current->blocked, &info);
++              spin_unlock_irq(&current->sigmask_lock);
++
++              if (!signr)
++                      break;
++
++              if ((current->ptrace & PT_PTRACED) && signr != SIGKILL) {
++                      /* Let the debugger run.  */
++                      current->exit_code = signr;
++                      current->state = TASK_STOPPED;
++                      notify_parent(current, SIGCHLD);
++                      schedule();
++
++                      /* We're back.  Did the debugger cancel the sig?  */
++                      signr = current->exit_code;
++                      if (!signr)
++                              continue;
++                      current->exit_code = 0;
++
++                      /* The debugger continued.  Ignore SIGSTOP.  */
++                      if (signr == SIGSTOP)
++                              continue;
++
++                      /* Update the siginfo structure.  Is this good?  */
++                      if (signr != info.si_signo) {
++                              info.si_signo = signr;
++                              info.si_errno = 0;
++                              info.si_code = SI_USER;
++                              info.si_pid = current->p_pptr->pid;
++                              info.si_uid = current->p_pptr->uid;
++                      }
++
++                      /* If the (new) signal is now blocked, requeue it.  */
++                      if (sigismember(&current->blocked, signr)) {
++                              send_sig_info(signr, &info, current);
++                              continue;
++                      }
++              }
++
++              ka = &current->sig->action[signr-1];
++              if (ka->sa.sa_handler == SIG_IGN) {
++                      if (signr != SIGCHLD)
++                              continue;
++                      /* Check for SIGCHLD: it's special.  */
++                      while (sys_wait4(-1, NULL, WNOHANG, NULL) > 0)
++                              /* nothing */;
++                      continue;
++              }
++
++              if (ka->sa.sa_handler == SIG_DFL) {
++                      int exit_code = signr;
++
++                      /* Init gets no signals it doesn't want.  */
++                      if (current->pid == 1)
++                              continue;
++
++                      switch (signr) {
++                      case SIGCONT: case SIGCHLD: case SIGWINCH: case SIGURG:
++                              continue;
++
++                      case SIGTSTP: case SIGTTIN: case SIGTTOU:
++                              if (is_orphaned_pgrp(current->pgrp))
++                                      continue;
++                              /* FALLTHRU */
++
++                        case SIGSTOP: {
++                                struct signal_struct *sig;
++                              current->state = TASK_STOPPED;
++                              current->exit_code = signr;
++                                sig = current->p_pptr->sig;
++                                if (sig && !(sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP))
++                                      notify_parent(current, SIGCHLD);
++                              schedule();
++                              continue;
++                      }
++                      case SIGQUIT: case SIGILL: case SIGTRAP:
++                      case SIGABRT: case SIGFPE: case SIGSEGV:
++                      case SIGBUS: case SIGSYS: case SIGXCPU: case SIGXFSZ:
++                              if (do_coredump(signr, &current->thread.regs))
++                                      exit_code |= 0x80;
++                              /* FALLTHRU */
++
++                      default:
++                              sig_exit(signr, exit_code, &info);
++                              /* NOTREACHED */
++                      }
++              }
++
++              /* Whee!  Actually deliver the signal.  */
++              handle_signal(regs, signr, ka, &info, oldset, error);
++              return(1);
++      }
++
++      /* Did we come from a system call? */
++      if(PT_REGS_SYSCALL_NR(regs) >= 0){
++              /* Restart the system call - no handlers present */
++              if(PT_REGS_SYSCALL_RET(regs) == -ERESTARTNOHAND ||
++                 PT_REGS_SYSCALL_RET(regs) == -ERESTARTSYS ||
++                 PT_REGS_SYSCALL_RET(regs) == -ERESTARTNOINTR){
++                      PT_REGS_ORIG_SYSCALL(regs) = PT_REGS_SYSCALL_NR(regs);
++                      PT_REGS_RESTART_SYSCALL(regs);
++              }
++      }
++
++      /* This closes a way to execute a system call on the host.  If
++       * you set a breakpoint on a system call instruction and singlestep
++       * from it, the tracing thread used to PTRACE_SINGLESTEP the process
++       * rather than PTRACE_SYSCALL it, allowing the system call to execute
++       * on the host.  The tracing thread will check this flag and 
++       * PTRACE_SYSCALL if necessary.
++       */
++      if((current->ptrace & PT_DTRACE) && 
++         is_syscall(PT_REGS_IP(&current->thread.regs)))
++              current->thread.singlestep_syscall = 1;
++
++      return(0);
++}
++
++int do_signal(int error)
++{
++      return(kern_do_signal(&current->thread.regs, NULL, error));
++}
++
++/*
++ * Atomically swap in the new signal mask, and wait for a signal.
++ */
++int sys_sigsuspend(int history0, int history1, old_sigset_t mask)
++{
++      sigset_t saveset;
++
++      mask &= _BLOCKABLE;
++      spin_lock_irq(&current->sigmask_lock);
++      saveset = current->blocked;
++      siginitset(&current->blocked, mask);
++      recalc_sigpending(current);
++      spin_unlock_irq(&current->sigmask_lock);
++
++      PT_REGS_SYSCALL_RET(&current->thread.regs) = -EINTR;
++      while (1) {
++              current->state = TASK_INTERRUPTIBLE;
++              schedule();
++              if(kern_do_signal(&current->thread.regs, &saveset, -EINTR))
++                      return(-EINTR);
++      }
++}
++
++int sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize)
++{
++      sigset_t saveset, newset;
++
++      /* XXX: Don't preclude handling different sized sigset_t's.  */
++      if (sigsetsize != sizeof(sigset_t))
++              return -EINVAL;
++
++      if (copy_from_user(&newset, unewset, sizeof(newset)))
++              return -EFAULT;
++      sigdelsetmask(&newset, ~_BLOCKABLE);
++
++      spin_lock_irq(&current->sigmask_lock);
++      saveset = current->blocked;
++      current->blocked = newset;
++      recalc_sigpending(current);
++      spin_unlock_irq(&current->sigmask_lock);
++
++      PT_REGS_SYSCALL_RET(&current->thread.regs) = -EINTR;
++      while (1) {
++              current->state = TASK_INTERRUPTIBLE;
++              schedule();
++              if (kern_do_signal(&current->thread.regs, &saveset, -EINTR))
++                      return(-EINTR);
++      }
++}
++
++extern int userspace_pid[];
++
++static int copy_sc_from_user(struct pt_regs *to, void *from, 
++                           struct arch_frame_data *arch)
++{
++      int ret;
++
++      ret = CHOOSE_MODE(copy_sc_from_user_tt(UPT_SC(&to->regs), from, arch),
++                        copy_sc_from_user_skas(userspace_pid[0], 
++                                               &to->regs, from));
++      return(ret);
++}
++
++int sys_sigreturn(struct pt_regs regs)
++{
++      void *sc = sp_to_sc(PT_REGS_SP(&current->thread.regs));
++      void *mask = sp_to_mask(PT_REGS_SP(&current->thread.regs));
++      int sig_size = (_NSIG_WORDS - 1) * sizeof(unsigned long);
++
++      spin_lock_irq(&current->sigmask_lock);
++      copy_from_user(&current->blocked.sig[0], sc_sigmask(sc), 
++                     sizeof(current->blocked.sig[0]));
++      copy_from_user(&current->blocked.sig[1], mask, sig_size);
++      sigdelsetmask(&current->blocked, ~_BLOCKABLE);
++      recalc_sigpending(current);
++      spin_unlock_irq(&current->sigmask_lock);
++      copy_sc_from_user(&current->thread.regs, sc, 
++                        &signal_frame_sc.common.arch);
++      return(PT_REGS_SYSCALL_RET(&current->thread.regs));
++}
++
++int sys_rt_sigreturn(struct pt_regs regs)
++{
++      struct ucontext *uc = sp_to_uc(PT_REGS_SP(&current->thread.regs));
++      int sig_size = _NSIG_WORDS * sizeof(unsigned long);
++
++      spin_lock_irq(&current->sigmask_lock);
++      copy_from_user(&current->blocked, &uc->uc_sigmask, sig_size);
++      sigdelsetmask(&current->blocked, ~_BLOCKABLE);
++      recalc_sigpending(current);
++      spin_unlock_irq(&current->sigmask_lock);
++      copy_sc_from_user(&current->thread.regs, &uc->uc_mcontext,
++                        &signal_frame_si.common.arch);
++      return(PT_REGS_SYSCALL_RET(&current->thread.regs));
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/signal_user.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/signal_user.c     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/signal_user.c  2005-05-03 22:28:14.448413720 +0300
+@@ -0,0 +1,142 @@
++/* 
++ * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <stdio.h>
++#include <unistd.h>
++#include <stdlib.h>
++#include <signal.h>
++#include <errno.h>
++#include <stdarg.h>
++#include <string.h>
++#include <sys/mman.h>
++#include "user_util.h"
++#include "kern_util.h"
++#include "user.h"
++#include "signal_user.h"
++#include "signal_kern.h"
++#include "sysdep/sigcontext.h"
++#include "sigcontext.h"
++
++void set_sigstack(void *sig_stack, int size)
++{
++      stack_t stack = ((stack_t) { .ss_flags  = 0,
++                                   .ss_sp     = (__ptr_t) sig_stack,
++                                   .ss_size   = size - sizeof(void *) });
++
++      if(sigaltstack(&stack, NULL) != 0)
++              panic("enabling signal stack failed, errno = %d\n", errno);
++}
++
++void set_handler(int sig, void (*handler)(int), int flags, ...)
++{
++      struct sigaction action;
++      va_list ap;
++      int mask;
++
++      va_start(ap, flags);
++      action.sa_handler = handler;
++      sigemptyset(&action.sa_mask);
++      while((mask = va_arg(ap, int)) != -1){
++              sigaddset(&action.sa_mask, mask);
++      }
++      action.sa_flags = flags;
++      action.sa_restorer = NULL;
++      if(sigaction(sig, &action, NULL) < 0)
++              panic("sigaction failed");
++}
++
++int change_sig(int signal, int on)
++{
++      sigset_t sigset, old;
++
++      sigemptyset(&sigset);
++      sigaddset(&sigset, signal);
++      sigprocmask(on ? SIG_UNBLOCK : SIG_BLOCK, &sigset, &old);
++      return(!sigismember(&old, signal));
++}
++
++static void change_signals(int type)
++{
++      sigset_t mask;
++
++      sigemptyset(&mask);
++      sigaddset(&mask, SIGVTALRM);
++      sigaddset(&mask, SIGALRM);
++      sigaddset(&mask, SIGIO);
++      sigaddset(&mask, SIGPROF);
++      if(sigprocmask(type, &mask, NULL) < 0)
++              panic("Failed to change signal mask - errno = %d", errno);
++}
++
++void block_signals(void)
++{
++      change_signals(SIG_BLOCK);
++}
++
++void unblock_signals(void)
++{
++      change_signals(SIG_UNBLOCK);
++}
++
++#define SIGIO_BIT 0
++#define SIGVTALRM_BIT 1
++
++static int enable_mask(sigset_t *mask)
++{
++      int sigs;
++
++      sigs = sigismember(mask, SIGIO) ? 0 : 1 << SIGIO_BIT;
++      sigs |= sigismember(mask, SIGVTALRM) ? 0 : 1 << SIGVTALRM_BIT;
++      sigs |= sigismember(mask, SIGALRM) ? 0 : 1 << SIGVTALRM_BIT;
++      return(sigs);
++}
++
++int get_signals(void)
++{
++      sigset_t mask;
++      
++      if(sigprocmask(SIG_SETMASK, NULL, &mask) < 0)
++              panic("Failed to get signal mask");
++      return(enable_mask(&mask));
++}
++
++int set_signals(int enable)
++{
++      sigset_t mask;
++      int ret;
++
++      sigemptyset(&mask);
++      if(enable & (1 << SIGIO_BIT)) 
++              sigaddset(&mask, SIGIO);
++      if(enable & (1 << SIGVTALRM_BIT)){
++              sigaddset(&mask, SIGVTALRM);
++              sigaddset(&mask, SIGALRM);
++      }
++      if(sigprocmask(SIG_UNBLOCK, &mask, &mask) < 0)
++              panic("Failed to enable signals");
++      ret = enable_mask(&mask);
++      sigemptyset(&mask);
++      if((enable & (1 << SIGIO_BIT)) == 0) 
++              sigaddset(&mask, SIGIO);
++      if((enable & (1 << SIGVTALRM_BIT)) == 0){
++              sigaddset(&mask, SIGVTALRM);
++              sigaddset(&mask, SIGALRM);
++      }
++      if(sigprocmask(SIG_BLOCK, &mask, NULL) < 0)
++              panic("Failed to block signals");
++
++      return(ret);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/skas/exec_kern.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/skas/exec_kern.c  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/skas/exec_kern.c       2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,41 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/kernel.h"
++#include "asm/current.h"
++#include "asm/page.h"
++#include "asm/signal.h"
++#include "asm/ptrace.h"
++#include "asm/uaccess.h"
++#include "asm/mmu_context.h"
++#include "tlb.h"
++#include "skas.h"
++#include "mmu.h"
++#include "os.h"
++
++void flush_thread_skas(void)
++{
++      force_flush_all();
++      switch_mm_skas(current->mm->context.skas.mm_fd);
++}
++
++void start_thread_skas(struct pt_regs *regs, unsigned long eip, 
++                     unsigned long esp)
++{
++      set_fs(USER_DS);
++        PT_REGS_IP(regs) = eip;
++      PT_REGS_SP(regs) = esp;
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/skas/exec_user.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/skas/exec_user.c  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/skas/exec_user.c       2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,63 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <stdlib.h>
++#include <errno.h>
++#include <signal.h>
++#include <sched.h>
++#include <sys/wait.h>
++#include <sys/ptrace.h>
++#include "user.h"
++#include "kern_util.h"
++#include "user_util.h"
++#include "os.h"
++#include "time_user.h"
++
++static int user_thread_tramp(void *arg)
++{
++      if(ptrace(PTRACE_TRACEME, 0, 0, 0) < 0)
++              panic("user_thread_tramp - PTRACE_TRACEME failed, "
++                    "errno = %d\n", errno);
++      enable_timer();
++      os_stop_process(os_getpid());
++      return(0);
++}
++
++int user_thread(unsigned long stack, int flags)
++{
++      int pid, status, err;
++
++      pid = clone(user_thread_tramp, (void *) stack_sp(stack), 
++                  flags | CLONE_FILES | SIGCHLD, NULL);
++      if(pid < 0){
++              printk("user_thread - clone failed, errno = %d\n", errno);
++              return(pid);
++      }
++
++      CATCH_EINTR(err = waitpid(pid, &status, WUNTRACED));
++      if(err < 0){
++              printk("user_thread - waitpid failed, errno = %d\n", errno);
++              return(-errno);
++      }
++
++      if(!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGSTOP)){
++              printk("user_thread - trampoline didn't stop, status = %d\n", 
++                     status);
++              return(-EINVAL);
++      }
++
++      return(pid);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/skas/include/mmu.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/skas/include/mmu.h        1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/skas/include/mmu.h     2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,27 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __SKAS_MMU_H
++#define __SKAS_MMU_H
++
++#include "linux/list.h"
++#include "linux/spinlock.h"
++
++struct mmu_context_skas {
++      int mm_fd;
++};
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/skas/include/mode.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/skas/include/mode.h       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/skas/include/mode.h    2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,39 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __MODE_SKAS_H__
++#define __MODE_SKAS_H__
++
++#include <sysdep/ptrace.h>
++
++extern unsigned long exec_regs[];
++extern unsigned long exec_fp_regs[];
++extern unsigned long exec_fpx_regs[];
++extern int have_fpx_regs;
++
++extern void user_time_init_skas(void);
++extern int copy_sc_from_user_skas(int pid, union uml_pt_regs *regs, 
++                                void *from_ptr);
++extern int copy_sc_to_user_skas(int pid, void *to_ptr, void *fp, 
++                              union uml_pt_regs *regs, 
++                              unsigned long fault_addr, int fault_type);
++extern void sig_handler_common_skas(int sig, void *sc_ptr);
++extern void halt_skas(void);
++extern void reboot_skas(void);
++extern void kill_off_processes_skas(void);
++extern int is_skas_winch(int pid, int fd, void *data);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/skas/include/mode_kern.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/skas/include/mode_kern.h  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/skas/include/mode_kern.h       2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,51 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __SKAS_MODE_KERN_H__
++#define __SKAS_MODE_KERN_H__
++
++#include "linux/sched.h"
++#include "asm/page.h"
++#include "asm/ptrace.h"
++
++extern void flush_thread_skas(void);
++extern void *_switch_to_skas(void *prev, void *next);
++extern void start_thread_skas(struct pt_regs *regs, unsigned long eip, 
++                            unsigned long esp);
++extern int copy_thread_skas(int nr, unsigned long clone_flags, 
++                          unsigned long sp, unsigned long stack_top, 
++                          struct task_struct *p, struct pt_regs *regs);
++extern void release_thread_skas(struct task_struct *task);
++extern void exit_thread_skas(void);
++extern void initial_thread_cb_skas(void (*proc)(void *), void *arg);
++extern void init_idle_skas(void);
++extern void flush_tlb_kernel_vm_skas(void);
++extern void __flush_tlb_one_skas(unsigned long addr);
++extern void flush_tlb_range_skas(struct mm_struct *mm, unsigned long start, 
++                               unsigned long end);
++extern void flush_tlb_mm_skas(struct mm_struct *mm);
++extern void force_flush_all_skas(void);
++extern long execute_syscall_skas(void *r);
++extern void before_mem_skas(unsigned long unused);
++extern unsigned long set_task_sizes_skas(int arg, unsigned long *host_size_out,
++                                       unsigned long *task_size_out);
++extern int start_uml_skas(void);
++extern int external_pid_skas(struct task_struct *task);
++extern int thread_pid_skas(struct thread_struct *thread);
++
++#define kmem_end_skas (host_task_size - 1024 * 1024)
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/skas/include/proc_mm.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/skas/include/proc_mm.h    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/skas/include/proc_mm.h 2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,55 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __SKAS_PROC_MM_H
++#define __SKAS_PROC_MM_H
++
++#define MM_MMAP 54
++#define MM_MUNMAP 55
++#define MM_MPROTECT 56
++#define MM_COPY_SEGMENTS 57
++
++struct mm_mmap {
++      unsigned long addr;
++      unsigned long len;
++      unsigned long prot;
++      unsigned long flags;
++      unsigned long fd;
++      unsigned long offset;
++};
++
++struct mm_munmap {
++      unsigned long addr;
++      unsigned long len;      
++};
++
++struct mm_mprotect {
++      unsigned long addr;
++      unsigned long len;
++        unsigned int prot;
++};
++
++struct proc_mm_op {
++      int op;
++      union {
++              struct mm_mmap mmap;
++              struct mm_munmap munmap;
++              struct mm_mprotect mprotect;
++              int copy_segments;
++      } u;
++};
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/skas/include/ptrace-skas.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/skas/include/ptrace-skas.h        1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/skas/include/ptrace-skas.h     2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,57 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __PTRACE_SKAS_H
++#define __PTRACE_SKAS_H
++
++#include "uml-config.h"
++
++#ifdef UML_CONFIG_MODE_SKAS
++
++#include "skas_ptregs.h"
++
++#define HOST_FRAME_SIZE 17
++
++#define REGS_IP(r) ((r)[HOST_IP])
++#define REGS_SP(r) ((r)[HOST_SP])
++#define REGS_EFLAGS(r) ((r)[HOST_EFLAGS])
++#define REGS_EAX(r) ((r)[HOST_EAX])
++#define REGS_EBX(r) ((r)[HOST_EBX])
++#define REGS_ECX(r) ((r)[HOST_ECX])
++#define REGS_EDX(r) ((r)[HOST_EDX])
++#define REGS_ESI(r) ((r)[HOST_ESI])
++#define REGS_EDI(r) ((r)[HOST_EDI])
++#define REGS_EBP(r) ((r)[HOST_EBP])
++#define REGS_CS(r) ((r)[HOST_CS])
++#define REGS_SS(r) ((r)[HOST_SS])
++#define REGS_DS(r) ((r)[HOST_DS])
++#define REGS_ES(r) ((r)[HOST_ES])
++#define REGS_FS(r) ((r)[HOST_FS])
++#define REGS_GS(r) ((r)[HOST_GS])
++
++#define REGS_SET_SYSCALL_RETURN(r, res) REGS_EAX(r) = (res)
++
++#define REGS_RESTART_SYSCALL(r) IP_RESTART_SYSCALL(REGS_IP(r))
++
++#define REGS_SEGV_IS_FIXABLE(r) SEGV_IS_FIXABLE((r)->trap_type)
++
++#define REGS_FAULT_ADDR(r) ((r)->fault_addr)
++
++#define REGS_FAULT_WRITE(r) FAULT_WRITE((r)->fault_type)
++
++#endif
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/skas/include/skas.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/skas/include/skas.h       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/skas/include/skas.h    2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,48 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __SKAS_H
++#define __SKAS_H
++
++#include "sysdep/ptrace.h"
++
++extern int userspace_pid[];
++
++extern void switch_threads(void *me, void *next);
++extern void thread_wait(void *sw, void *fb);
++extern void new_thread(void *stack, void **switch_buf_ptr, void **fork_buf_ptr,
++                       void (*handler)(int));
++extern int start_idle_thread(void *stack, void *switch_buf_ptr, 
++                           void **fork_buf_ptr);
++extern int user_thread(unsigned long stack, int flags);
++extern void userspace(union uml_pt_regs *regs);
++extern void new_thread_proc(void *stack, void (*handler)(int sig));
++extern void remove_sigstack(void);
++extern void new_thread_handler(int sig);
++extern void handle_syscall(union uml_pt_regs *regs);
++extern void map(int fd, unsigned long virt, unsigned long phys, 
++              unsigned long len, int r, int w, int x);
++extern int unmap(int fd, void *addr, int len);
++extern int protect(int fd, unsigned long addr, unsigned long len, 
++                 int r, int w, int x, int must_succeed);
++extern void user_signal(int sig, union uml_pt_regs *regs);
++extern int new_mm(int from);
++extern void save_registers(union uml_pt_regs *regs);
++extern void restore_registers(union uml_pt_regs *regs);
++extern void start_userspace(int cpu);
++extern void init_registers(int pid);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/skas/include/uaccess.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/skas/include/uaccess.h    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/skas/include/uaccess.h 2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,40 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __SKAS_UACCESS_H
++#define __SKAS_UACCESS_H
++
++#include "asm/errno.h"
++
++#define access_ok_skas(type, addr, size) \
++      ((segment_eq(get_fs(), KERNEL_DS)) || \
++       (((unsigned long) (addr) < TASK_SIZE) && \
++        ((unsigned long) (addr) + (size) <= TASK_SIZE)))
++
++static inline int verify_area_skas(int type, const void * addr, 
++                                 unsigned long size)
++{
++      return(access_ok_skas(type, addr, size) ? 0 : -EFAULT);
++}
++
++extern int copy_from_user_skas(void *to, const void *from, int n);
++extern int copy_to_user_skas(void *to, const void *from, int n);
++extern int strncpy_from_user_skas(char *dst, const char *src, int count);
++extern int __clear_user_skas(void *mem, int len);
++extern int clear_user_skas(void *mem, int len);
++extern int strnlen_user_skas(const void *str, int len);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/skas/Makefile
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/skas/Makefile     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/skas/Makefile  2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,31 @@
++# 
++# Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++# Licensed under the GPL
++#
++
++O_TARGET = skas.o
++
++obj-y = exec_kern.o exec_user.o mem.o mem_user.o mmu.o process.o \
++      process_kern.o syscall_kern.o syscall_user.o time.o tlb.o trap_user.o \
++      uaccess.o
++
++subdir-y = sys-$(SUBARCH)
++
++obj-y += $(join $(subdir-y),$(subdir-y:%=/%.o))
++
++USER_OBJS = $(filter %_user.o,$(obj-y)) process.o time.o
++
++include $(TOPDIR)/Rules.make
++
++include/skas_ptregs.h : util/mk_ptregs
++      util/mk_ptregs > $@
++
++util/mk_ptregs :
++      $(MAKE) -C util
++
++$(USER_OBJS) : %.o: %.c
++      $(CC) $(CFLAGS_$@) $(USER_CFLAGS) -c -o $@ $<
++
++clean :
++      $(MAKE) -C util clean
++      $(RM) -f include/skas_ptregs.h
+Index: linux-2.4.29/arch/um/kernel/skas/mem.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/skas/mem.c        1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/skas/mem.c     2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,30 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/config.h"
++#include "linux/mm.h"
++#include "mem_user.h"
++
++unsigned long set_task_sizes_skas(int arg, unsigned long *host_size_out, 
++                                unsigned long *task_size_out)
++{
++      /* Round up to the nearest 4M */
++      unsigned long top = ROUND_4M((unsigned long) &arg);
++
++      *host_size_out = top;
++      *task_size_out = top;
++      return(((unsigned long) set_task_sizes_skas) & ~0xffffff);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/skas/mem_user.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/skas/mem_user.c   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/skas/mem_user.c        2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,105 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <errno.h>
++#include <sys/mman.h>
++#include <sys/ptrace.h>
++#include "mem_user.h"
++#include "mem.h"
++#include "user.h"
++#include "os.h"
++#include "proc_mm.h"
++
++void map(int fd, unsigned long virt, unsigned long phys, unsigned long len, 
++       int r, int w, int x)
++{
++      struct proc_mm_op map;
++      __u64 offset;
++      int prot, n, phys_fd;
++
++      prot = (r ? PROT_READ : 0) | (w ? PROT_WRITE : 0) | 
++              (x ? PROT_EXEC : 0);
++      phys_fd = phys_mapping(phys, &offset);
++
++      map = ((struct proc_mm_op) { .op        = MM_MMAP,
++                                   .u         = 
++                                   { .mmap    = 
++                                     { .addr          = virt,
++                                       .len           = len,
++                                       .prot          = prot,
++                                       .flags         = MAP_SHARED | 
++                                                        MAP_FIXED,
++                                       .fd            = phys_fd,
++                                       .offset        = offset
++                                     } } } );
++      n = os_write_file(fd, &map, sizeof(map));
++      if(n != sizeof(map)) 
++              printk("map : /proc/mm map failed, err = %d\n", -n);
++}
++
++int unmap(int fd, void *addr, int len)
++{
++      struct proc_mm_op unmap;
++      int n;
++
++      unmap = ((struct proc_mm_op) { .op      = MM_MUNMAP,
++                                     .u       = 
++                                     { .munmap        = 
++                                       { .addr        = (unsigned long) addr,
++                                         .len         = len } } } );
++      n = os_write_file(fd, &unmap, sizeof(unmap));
++      if(n != sizeof(unmap)) {
++              if(n < 0) 
++                      return(n);
++              else if(n > 0) 
++                      return(-EIO);
++      }
++
++      return(0);
++}
++
++int protect(int fd, unsigned long addr, unsigned long len, int r, int w, 
++          int x, int must_succeed)
++{
++      struct proc_mm_op protect;
++      int prot, n;
++
++      prot = (r ? PROT_READ : 0) | (w ? PROT_WRITE : 0) | 
++              (x ? PROT_EXEC : 0);
++
++      protect = ((struct proc_mm_op) { .op    = MM_MPROTECT,
++                                     .u       = 
++                                     { .mprotect      = 
++                                       { .addr        = (unsigned long) addr,
++                                         .len         = len,
++                                         .prot        = prot } } } );
++
++      n = os_write_file(fd, &protect, sizeof(protect));
++      if(n != sizeof(protect)) {
++              if(n == 0) return(0);
++
++              if(must_succeed)
++                      panic("protect failed, err = %d", -n);
++
++              return(-EIO);
++      }
++
++      return(0);
++}
++
++void before_mem_skas(unsigned long unused)
++{
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/skas/mmu.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/skas/mmu.c        1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/skas/mmu.c     2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,46 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/list.h"
++#include "linux/spinlock.h"
++#include "linux/slab.h"
++#include "asm/segment.h"
++#include "asm/mmu.h"
++#include "os.h"
++#include "skas.h"
++
++int init_new_context_skas(struct task_struct *task, struct mm_struct *mm)
++{
++      int from;
++
++      if((current->mm != NULL) && (current->mm != &init_mm))
++              from = current->mm->context.skas.mm_fd;
++      else from = -1;
++
++      mm->context.skas.mm_fd = new_mm(from);
++      if(mm->context.skas.mm_fd < 0){
++              printk("init_new_context_skas - new_mm failed, errno = %d\n",
++                     mm->context.skas.mm_fd);
++              return(mm->context.skas.mm_fd);
++      }
++
++      return(0);
++}
++
++void destroy_context_skas(struct mm_struct *mm)
++{
++      os_close_file(mm->context.skas.mm_fd);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/skas/process.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/skas/process.c    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/skas/process.c 2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,400 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <stdlib.h>
++#include <unistd.h>
++#include <errno.h>
++#include <signal.h>
++#include <setjmp.h>
++#include <sched.h>
++#include <sys/wait.h>
++#include <sys/ptrace.h>
++#include <sys/mman.h>
++#include <sys/user.h>
++#include <asm/unistd.h>
++#include "user.h"
++#include "ptrace_user.h"
++#include "time_user.h"
++#include "sysdep/ptrace.h"
++#include "user_util.h"
++#include "kern_util.h"
++#include "skas.h"
++#include "sysdep/sigcontext.h"
++#include "os.h"
++#include "proc_mm.h"
++#include "skas_ptrace.h"
++#include "chan_user.h"
++
++int is_skas_winch(int pid, int fd, void *data)
++{
++      if(pid != getpid())
++              return(0);
++
++      register_winch_irq(-1, fd, -1, data);
++      return(1);
++}
++
++/* These are set once at boot time and not changed thereafter */
++
++unsigned long exec_regs[FRAME_SIZE];
++unsigned long exec_fp_regs[HOST_FP_SIZE];
++unsigned long exec_fpx_regs[HOST_XFP_SIZE];
++int have_fpx_regs = 1;
++
++static void handle_segv(int pid)
++{
++      struct ptrace_faultinfo fault;
++      int err;
++
++      err = ptrace(PTRACE_FAULTINFO, pid, 0, &fault);
++      if(err)
++              panic("handle_segv - PTRACE_FAULTINFO failed, errno = %d\n",
++                    errno);
++
++      segv(fault.addr, 0, FAULT_WRITE(fault.is_write), 1, NULL);
++}
++
++static void handle_trap(int pid, union uml_pt_regs *regs)
++{
++      int err, syscall_nr, status;
++
++      syscall_nr = PT_SYSCALL_NR(regs->skas.regs);
++      UPT_SYSCALL_NR(regs) = syscall_nr;
++      if(syscall_nr < 1){
++              relay_signal(SIGTRAP, regs);
++              return;
++      }
++
++      if(!use_sysemu){
++              err = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_NR_OFFSET, 
++                           __NR_getpid);
++              if(err < 0)
++                      panic("handle_trap - nullifying syscall failed, "
++                            "errno = %d\n", errno);
++
++              err = ptrace(PTRACE_SYSCALL, pid, 0, 0);
++              if(err < 0)
++                      panic("handle_trap - continuing to end of syscall "
++                            "failed, errno = %d\n", errno);
++
++              CATCH_EINTR(err = waitpid(pid, &status, WUNTRACED));
++              if((err < 0) || !WIFSTOPPED(status) || 
++                 (WSTOPSIG(status) != SIGTRAP))
++                      panic("handle_trap - failed to wait at end of "
++                            "syscall, errno = %d, status = %d\n", errno, 
++                            status);
++      }
++
++      handle_syscall(regs);
++}
++
++static int userspace_tramp(void *arg)
++{
++      init_new_thread_signals(0);
++      enable_timer();
++      ptrace(PTRACE_TRACEME, 0, 0, 0);
++      os_stop_process(os_getpid());
++      return(0);
++}
++
++/* Each element set once, and only accessed by a single processor anyway */
++#define NR_CPUS 1
++int userspace_pid[NR_CPUS];
++
++void start_userspace(int cpu)
++{
++      void *stack;
++      unsigned long sp;
++      int pid, status, n;
++
++      stack = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC,
++                   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
++      if(stack == MAP_FAILED)
++              panic("start_userspace : mmap failed, errno = %d", errno);
++      sp = (unsigned long) stack + PAGE_SIZE - sizeof(void *);
++
++      pid = clone(userspace_tramp, (void *) sp, 
++                  CLONE_FILES | CLONE_VM | SIGCHLD, NULL);
++      if(pid < 0)
++              panic("start_userspace : clone failed, errno = %d", errno);
++
++      do {
++              CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
++              if(n < 0)
++                      panic("start_userspace : wait failed, errno = %d", 
++                            errno);
++      } while(WIFSTOPPED(status) && (WSTOPSIG(status) == SIGVTALRM));
++
++      if(!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGSTOP))
++              panic("start_userspace : expected SIGSTOP, got status = %d",
++                    status);
++
++      if(munmap(stack, PAGE_SIZE) < 0)
++              panic("start_userspace : munmap failed, errno = %d\n", errno);
++
++      userspace_pid[cpu] = pid;
++}
++
++void userspace(union uml_pt_regs *regs)
++{
++      int err, status, op, do_syscall, pid = userspace_pid[0];
++
++      do_syscall = use_sysemu ? PTRACE_SYSEMU : PTRACE_SYSCALL;
++      restore_registers(regs);
++              
++      err = ptrace(do_syscall, pid, 0, 0);
++      if(err)
++              panic("userspace - PTRACE_SYSCALL failed, errno = %d\n", 
++                     errno);
++      while(1){
++              CATCH_EINTR(err = waitpid(pid, &status, WUNTRACED));
++              if(err < 0)
++                      panic("userspace - waitpid failed, errno = %d\n", 
++                            errno);
++
++              regs->skas.is_user = 1;
++              save_registers(regs);
++
++              if(WIFSTOPPED(status)){
++                      switch(WSTOPSIG(status)){
++                      case SIGSEGV:
++                              handle_segv(pid);
++                              break;
++                      case SIGTRAP:
++                              handle_trap(pid, regs);
++                              break;
++                      case SIGIO:
++                      case SIGVTALRM:
++                      case SIGILL:
++                      case SIGBUS:
++                      case SIGFPE:
++                      case SIGWINCH:
++                              user_signal(WSTOPSIG(status), regs);
++                              break;
++                      default:
++                              printk("userspace - child stopped with signal "
++                                     "%d\n", WSTOPSIG(status));
++                      }
++                      interrupt_end();
++              }
++
++              restore_registers(regs);
++
++              op = singlestepping(NULL) ? PTRACE_SINGLESTEP : do_syscall;
++              err = ptrace(op, pid, 0, 0);
++              if(err)
++                      panic("userspace - PTRACE_SYSCALL failed, "
++                            "errno = %d\n", errno);
++      }
++}
++
++void new_thread(void *stack, void **switch_buf_ptr, void **fork_buf_ptr,
++              void (*handler)(int))
++{
++      sigjmp_buf switch_buf, fork_buf;
++
++      *switch_buf_ptr = &switch_buf;
++      *fork_buf_ptr = &fork_buf;
++
++      if(sigsetjmp(fork_buf, 1) == 0)
++              new_thread_proc(stack, handler);
++
++      remove_sigstack();
++}
++
++void thread_wait(void *sw, void *fb)
++{
++      sigjmp_buf buf, **switch_buf = sw, *fork_buf;
++
++      *switch_buf = &buf;
++      fork_buf = fb;
++      if(sigsetjmp(buf, 1) == 0)
++              siglongjmp(*fork_buf, 1);
++}
++
++static int move_registers(int pid, int int_op, int fp_op, 
++                        union uml_pt_regs *regs, unsigned long *fp_regs)
++{
++      if(ptrace(int_op, pid, 0, regs->skas.regs) < 0)
++              return(-errno);
++      if(ptrace(fp_op, pid, 0, fp_regs) < 0)
++              return(-errno);
++      return(0);
++}
++
++void save_registers(union uml_pt_regs *regs)
++{
++      unsigned long *fp_regs;
++      int err, fp_op;
++
++      if(have_fpx_regs){
++              fp_op = PTRACE_GETFPXREGS;
++              fp_regs = regs->skas.xfp;
++      }
++      else {
++              fp_op = PTRACE_GETFPREGS;
++              fp_regs = regs->skas.fp;
++      }
++
++      err = move_registers(userspace_pid[0], PTRACE_GETREGS, fp_op, regs, 
++                           fp_regs);
++      if(err)
++              panic("save_registers - saving registers failed, errno = %d\n",
++                    -err);
++}
++
++void restore_registers(union uml_pt_regs *regs)
++{
++      unsigned long *fp_regs;
++      int err, fp_op;
++
++      if(have_fpx_regs){
++              fp_op = PTRACE_SETFPXREGS;
++              fp_regs = regs->skas.xfp;
++      }
++      else {
++              fp_op = PTRACE_SETFPREGS;
++              fp_regs = regs->skas.fp;
++      }
++
++      err = move_registers(userspace_pid[0], PTRACE_SETREGS, fp_op, regs, 
++                           fp_regs);
++      if(err)
++              panic("restore_registers - saving registers failed, "
++                    "errno = %d\n", -err);
++}
++
++void switch_threads(void *me, void *next)
++{
++      sigjmp_buf my_buf, **me_ptr = me, *next_buf = next;
++      
++      *me_ptr = &my_buf;
++      if(sigsetjmp(my_buf, 1) == 0)
++              siglongjmp(*next_buf, 1);
++}
++
++static sigjmp_buf initial_jmpbuf;
++
++/* XXX Make these percpu */
++static void (*cb_proc)(void *arg);
++static void *cb_arg;
++static sigjmp_buf *cb_back;
++
++int start_idle_thread(void *stack, void *switch_buf_ptr, void **fork_buf_ptr)
++{
++      sigjmp_buf **switch_buf = switch_buf_ptr;
++      int n;
++
++      *fork_buf_ptr = &initial_jmpbuf;
++      n = sigsetjmp(initial_jmpbuf, 1);
++      if(n == 0)
++              new_thread_proc((void *) stack, new_thread_handler);
++      else if(n == 1)
++              remove_sigstack();
++      else if(n == 2){
++              (*cb_proc)(cb_arg);
++              siglongjmp(*cb_back, 1);
++      }
++      else if(n == 3){
++              kmalloc_ok = 0;
++              return(0);
++      }
++      else if(n == 4){
++              kmalloc_ok = 0;
++              return(1);
++      }
++      siglongjmp(**switch_buf, 1);
++}
++
++void remove_sigstack(void)
++{
++      stack_t stack = ((stack_t) { .ss_flags  = SS_DISABLE,
++                                   .ss_sp     = NULL,
++                                   .ss_size   = 0 });
++
++      if(sigaltstack(&stack, NULL) != 0)
++              panic("disabling signal stack failed, errno = %d\n", errno);
++}
++
++void initial_thread_cb_skas(void (*proc)(void *), void *arg)
++{
++      sigjmp_buf here;
++
++      cb_proc = proc;
++      cb_arg = arg;
++      cb_back = &here;
++
++      block_signals();
++      if(sigsetjmp(here, 1) == 0)
++              siglongjmp(initial_jmpbuf, 2);
++      unblock_signals();
++
++      cb_proc = NULL;
++      cb_arg = NULL;
++      cb_back = NULL;
++}
++
++void halt_skas(void)
++{
++      block_signals();
++      siglongjmp(initial_jmpbuf, 3);
++}
++
++void reboot_skas(void)
++{
++      block_signals();
++      siglongjmp(initial_jmpbuf, 4);
++}
++
++void switch_mm_skas(int mm_fd)
++{
++      int err;
++
++#warning need cpu pid in switch_mm_skas
++      err = ptrace(PTRACE_SWITCH_MM, userspace_pid[0], 0, mm_fd);
++      if(err)
++              panic("switch_mm_skas - PTRACE_SWITCH_MM failed, errno = %d\n",
++                    errno);
++}
++
++void kill_off_processes_skas(void)
++{
++#warning need to loop over userspace_pids in kill_off_processes_skas
++      os_kill_process(userspace_pid[0], 1);
++}
++
++void init_registers(int pid)
++{
++      int err;
++
++      if(ptrace(PTRACE_GETREGS, pid, 0, exec_regs) < 0)
++              panic("check_ptrace : PTRACE_GETREGS failed, errno = %d", 
++                    errno);
++
++      err = ptrace(PTRACE_GETFPXREGS, pid, 0, exec_fpx_regs);
++      if(!err)
++              return;
++
++      have_fpx_regs = 0;
++      if(errno != EIO)
++              panic("check_ptrace : PTRACE_GETFPXREGS failed, errno = %d", 
++                    errno);
++
++      err = ptrace(PTRACE_GETFPREGS, pid, 0, exec_fp_regs);
++      if(err)
++              panic("check_ptrace : PTRACE_GETFPREGS failed, errno = %d", 
++                    errno);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/skas/process_kern.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/skas/process_kern.c       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/skas/process_kern.c    2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,211 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/sched.h"
++#include "linux/slab.h"
++#include "kern_util.h"
++#include "time_user.h"
++#include "signal_user.h"
++#include "skas.h"
++#include "os.h"
++#include "user_util.h"
++#include "tlb.h"
++#include "frame.h"
++#include "kern.h"
++#include "mode.h"
++#include "filehandle.h"
++#include "proc_mm.h"
++
++void *_switch_to_skas(void *prev, void *next)
++{
++      struct task_struct *from, *to;
++
++      from = prev;
++      to = next;
++
++      /* XXX need to check runqueues[cpu].idle */
++      if(current->pid == 0)
++              switch_timers(0);
++
++      to->thread.prev_sched = from;
++      set_current(to);
++
++      switch_threads(&from->thread.mode.skas.switch_buf, 
++                     to->thread.mode.skas.switch_buf);
++
++      if(current->pid == 0)
++              switch_timers(1);
++
++      return(current->thread.prev_sched);
++}
++
++extern void schedule_tail(struct task_struct *prev);
++
++void new_thread_handler(int sig)
++{
++      int (*fn)(void *), n;
++      void *arg;
++
++      fn = current->thread.request.u.thread.proc;
++      arg = current->thread.request.u.thread.arg;
++      change_sig(SIGUSR1, 1);
++      thread_wait(&current->thread.mode.skas.switch_buf, 
++                  current->thread.mode.skas.fork_buf);
++
++      if(current->thread.prev_sched != NULL)
++              schedule_tail(current->thread.prev_sched);
++      current->thread.prev_sched = NULL;
++
++      /* The return value is 1 if the kernel thread execs a process,
++       * 0 if it just exits
++       */
++      n = run_kernel_thread(fn, arg, &current->thread.exec_buf);
++      if(n == 1)
++              userspace(&current->thread.regs.regs);
++      else do_exit(0);
++}
++
++void new_thread_proc(void *stack, void (*handler)(int sig))
++{
++      init_new_thread_stack(stack, handler);
++      os_usr1_process(os_getpid());
++}
++
++void release_thread_skas(struct task_struct *task)
++{
++}
++
++void exit_thread_skas(void)
++{
++}
++
++void fork_handler(int sig)
++{
++        change_sig(SIGUSR1, 1);
++      thread_wait(&current->thread.mode.skas.switch_buf, 
++                  current->thread.mode.skas.fork_buf);
++      
++      force_flush_all();
++      if(current->thread.prev_sched != NULL)
++              schedule_tail(current->thread.prev_sched);
++      current->thread.prev_sched = NULL;
++      unblock_signals();
++
++      userspace(&current->thread.regs.regs);
++}
++
++int copy_thread_skas(int nr, unsigned long clone_flags, unsigned long sp,
++                   unsigned long stack_top, struct task_struct * p, 
++                   struct pt_regs *regs)
++{
++      void (*handler)(int);
++
++      if(current->thread.forking){
++              memcpy(&p->thread.regs.regs.skas, 
++                     &current->thread.regs.regs.skas, 
++                     sizeof(p->thread.regs.regs.skas));
++              REGS_SET_SYSCALL_RETURN(p->thread.regs.regs.skas.regs, 0);
++              if(sp != 0) REGS_SP(p->thread.regs.regs.skas.regs) = sp;
++
++              handler = fork_handler;
++      }
++      else {
++              memcpy(p->thread.regs.regs.skas.regs, exec_regs, 
++                     sizeof(p->thread.regs.regs.skas.regs));
++              memcpy(p->thread.regs.regs.skas.fp, exec_fp_regs, 
++                     sizeof(p->thread.regs.regs.skas.fp));
++              memcpy(p->thread.regs.regs.skas.xfp, exec_fpx_regs, 
++                     sizeof(p->thread.regs.regs.skas.xfp));
++                p->thread.request.u.thread = current->thread.request.u.thread;
++              handler = new_thread_handler;
++      }
++
++      new_thread(p, &p->thread.mode.skas.switch_buf, 
++                 &p->thread.mode.skas.fork_buf, handler);
++      return(0);
++}
++
++int new_mm(int from)
++{
++      struct proc_mm_op copy;
++      int n;
++      int fd = open_file("/proc/mm", of_cloexec(of_write(OPENFLAGS())), 0);
++
++      if(fd < 0)
++              return(fd);
++
++      if(from != -1){
++              copy = ((struct proc_mm_op) { .op       = MM_COPY_SEGMENTS,
++                                            .u        = 
++                                            { .copy_segments  = from } } );
++              n = os_write_file(fd, &copy, sizeof(copy));
++              if(n != sizeof(copy)) 
++                      printk("new_mm : /proc/mm copy_segments failed, "
++                             "err = %d\n", -n);
++      }
++
++      return(fd);
++}
++
++void init_idle_skas(void)
++{
++      cpu_tasks[current->processor].pid = os_getpid();
++}
++
++extern void start_kernel(void);
++
++static int start_kernel_proc(void *unused)
++{
++      int pid;
++
++      block_signals();
++      pid = os_getpid();
++
++      cpu_tasks[0].pid = pid;
++      cpu_tasks[0].task = current;
++#ifdef CONFIG_SMP
++      cpu_online_map = 1;
++#endif
++      start_kernel();
++      return(0);
++}
++
++int start_uml_skas(void)
++{
++      start_userspace(0);
++      capture_signal_stack();
++
++      init_new_thread_signals(1);
++      idle_timer();
++
++      init_task.thread.request.u.thread.proc = start_kernel_proc;
++      init_task.thread.request.u.thread.arg = NULL;
++      return(start_idle_thread(&init_task,
++                               &init_task.thread.mode.skas.switch_buf,
++                               &init_task.thread.mode.skas.fork_buf));
++}
++
++int external_pid_skas(struct task_struct *task)
++{
++#warning Need to look up userspace_pid by cpu 
++      return(userspace_pid[0]);
++}
++
++int thread_pid_skas(struct thread_struct *thread)
++{
++#warning Need to look up userspace_pid by cpu 
++      return(userspace_pid[0]);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/skas/syscall_kern.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/skas/syscall_kern.c       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/skas/syscall_kern.c    2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,43 @@
++/* 
++ * Copyright (C) 2002 - 2003 Jeff Dike (jdike@addtoit.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/sys.h"
++#include "linux/ptrace.h"
++#include "asm/errno.h"
++#include "asm/unistd.h"
++#include "asm/ptrace.h"
++#include "asm/current.h"
++#include "sysdep/syscalls.h"
++#include "kern_util.h"
++
++extern syscall_handler_t *sys_call_table[];
++
++long execute_syscall_skas(void *r)
++{
++      struct pt_regs *regs = r;
++      long res;
++      int syscall;
++
++      current->thread.nsyscalls++;
++      nsyscalls++;
++      syscall = UPT_SYSCALL_NR(&regs->regs);
++
++      if((syscall >= NR_syscalls) || (syscall < 1))
++              res = -ENOSYS;
++      else res = EXECUTE_SYSCALL(syscall, regs);
++
++      return(res);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/skas/syscall_user.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/skas/syscall_user.c       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/skas/syscall_user.c    2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,46 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <stdlib.h>
++#include <signal.h>
++#include "kern_util.h"
++#include "syscall_user.h"
++#include "sysdep/ptrace.h"
++#include "sysdep/sigcontext.h"
++
++/* XXX Bogus */
++#define ERESTARTSYS   512
++#define ERESTARTNOINTR        513
++#define ERESTARTNOHAND        514
++
++void handle_syscall(union uml_pt_regs *regs)
++{
++      long result;
++      int index;
++
++      index = record_syscall_start(UPT_SYSCALL_NR(regs));
++
++      syscall_trace();
++      result = execute_syscall(regs);
++
++      REGS_SET_SYSCALL_RETURN(regs->skas.regs, result);
++      if((result == -ERESTARTNOHAND) || (result == -ERESTARTSYS) || 
++         (result == -ERESTARTNOINTR))
++              do_signal(result);
++
++      syscall_trace();
++      record_syscall_end(index, result);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/skas/sys-i386/Makefile
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/skas/sys-i386/Makefile    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/skas/sys-i386/Makefile 2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,17 @@
++# 
++# Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++# Licensed under the GPL
++#
++
++O_TARGET = sys-i386.o
++
++obj-y = sigcontext.o
++
++USER_OBJS = sigcontext.o
++
++include $(TOPDIR)/Rules.make
++
++$(USER_OBJS) : %.o: %.c
++      $(CC) $(CFLAGS_$@) $(USER_CFLAGS) -c -o $@ $<
++
++clean :
+Index: linux-2.4.29/arch/um/kernel/skas/sys-i386/sigcontext.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/skas/sys-i386/sigcontext.c        1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/skas/sys-i386/sigcontext.c     2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,114 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <errno.h>
++#include <asm/sigcontext.h>
++#include <sys/ptrace.h>
++#include <linux/ptrace.h>
++#include "sysdep/ptrace.h"
++#include "sysdep/ptrace_user.h"
++#include "kern_util.h"
++#include "user.h"
++#include "sigcontext.h"
++#include "mode.h"
++
++int copy_sc_from_user_skas(int pid, union uml_pt_regs *regs, void *from_ptr)
++{
++      struct sigcontext sc, *from = from_ptr;
++      unsigned long fpregs[FP_FRAME_SIZE];
++      int err;
++
++      err = copy_from_user_proc(&sc, from, sizeof(sc));
++      err |= copy_from_user_proc(fpregs, sc.fpstate, sizeof(fpregs));
++      if(err)
++              return(err);
++
++      regs->skas.regs[GS] = sc.gs;
++      regs->skas.regs[FS] = sc.fs;
++      regs->skas.regs[ES] = sc.es;
++      regs->skas.regs[DS] = sc.ds;
++      regs->skas.regs[EDI] = sc.edi;
++      regs->skas.regs[ESI] = sc.esi;
++      regs->skas.regs[EBP] = sc.ebp;
++      regs->skas.regs[UESP] = sc.esp;
++      regs->skas.regs[EBX] = sc.ebx;
++      regs->skas.regs[EDX] = sc.edx;
++      regs->skas.regs[ECX] = sc.ecx;
++      regs->skas.regs[EAX] = sc.eax;
++      regs->skas.regs[EIP] = sc.eip;
++      regs->skas.regs[CS] = sc.cs;
++      regs->skas.regs[EFL] = sc.eflags;
++      regs->skas.regs[SS] = sc.ss;
++      regs->skas.fault_addr = sc.cr2;
++      regs->skas.fault_type = FAULT_WRITE(sc.err);
++      regs->skas.trap_type = sc.trapno;
++
++      err = ptrace(PTRACE_SETFPREGS, pid, 0, fpregs);
++      if(err < 0){
++              printk("copy_sc_to_user - PTRACE_SETFPREGS failed, "
++                     "errno = %d\n", errno);
++              return(1);
++      }
++
++      return(0);
++}
++
++int copy_sc_to_user_skas(int pid, void *to_ptr, void *fp, 
++                       union uml_pt_regs *regs, unsigned long fault_addr, 
++                       int fault_type)
++{
++      struct sigcontext sc, *to = to_ptr;
++      struct _fpstate *to_fp;
++      unsigned long fpregs[FP_FRAME_SIZE];
++      int err;
++
++      sc.gs = regs->skas.regs[GS];
++      sc.fs = regs->skas.regs[FS];
++      sc.es = regs->skas.regs[ES];
++      sc.ds = regs->skas.regs[DS];
++      sc.edi = regs->skas.regs[EDI];
++      sc.esi = regs->skas.regs[ESI];
++      sc.ebp = regs->skas.regs[EBP];
++      sc.esp = regs->skas.regs[UESP];
++      sc.ebx = regs->skas.regs[EBX];
++      sc.edx = regs->skas.regs[EDX];
++      sc.ecx = regs->skas.regs[ECX];
++      sc.eax = regs->skas.regs[EAX];
++      sc.eip = regs->skas.regs[EIP];
++      sc.cs = regs->skas.regs[CS];
++      sc.eflags = regs->skas.regs[EFL];
++      sc.esp_at_signal = regs->skas.regs[UESP];
++      sc.ss = regs->skas.regs[SS];
++      sc.cr2 = fault_addr;
++      sc.err = TO_SC_ERR(fault_type);
++      sc.trapno = regs->skas.trap_type;
++
++      err = ptrace(PTRACE_GETFPREGS, pid, 0, fpregs);
++      if(err < 0){
++              printk("copy_sc_to_user - PTRACE_GETFPREGS failed, "
++                     "errno = %d\n", errno);
++              return(1);
++      }
++      to_fp = (struct _fpstate *) 
++              (fp ? (unsigned long) fp : ((unsigned long) to + sizeof(*to)));
++      sc.fpstate = to_fp;
++
++      if(err)
++              return(err);
++
++      return(copy_to_user_proc(to, &sc, sizeof(sc)) ||
++             copy_to_user_proc(to_fp, fpregs, sizeof(fpregs)));
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/skas/time.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/skas/time.c       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/skas/time.c    2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,30 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <sys/signal.h>
++#include <sys/time.h>
++#include "time_user.h"
++#include "process.h"
++#include "user.h"
++
++void user_time_init_skas(void)
++{
++        if(signal(SIGALRM, (__sighandler_t) alarm_handler) == SIG_ERR)
++                panic("Couldn't set SIGALRM handler");
++      if(signal(SIGVTALRM, (__sighandler_t) alarm_handler) == SIG_ERR)
++              panic("Couldn't set SIGVTALRM handler");
++      set_interval(ITIMER_VIRTUAL);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/skas/tlb.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/skas/tlb.c        1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/skas/tlb.c     2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,153 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/stddef.h"
++#include "linux/sched.h"
++#include "asm/page.h"
++#include "asm/pgtable.h"
++#include "asm/mmu.h"
++#include "user_util.h"
++#include "mem_user.h"
++#include "skas.h"
++#include "os.h"
++
++static void fix_range(struct mm_struct *mm, unsigned long start_addr,
++                    unsigned long end_addr, int force)
++{
++      pgd_t *npgd;
++      pmd_t *npmd;
++      pte_t *npte;
++      unsigned long addr;
++      int r, w, x, err, fd;
++
++      if(mm == NULL) return;
++      fd = mm->context.skas.mm_fd;
++      for(addr = start_addr; addr < end_addr;){
++              npgd = pgd_offset(mm, addr);
++              npmd = pmd_offset(npgd, addr);
++              if(pmd_present(*npmd)){
++                      npte = pte_offset(npmd, addr);
++                      r = pte_read(*npte);
++                      w = pte_write(*npte);
++                      x = pte_exec(*npte);
++                      if(!pte_dirty(*npte)) w = 0;
++                      if(!pte_young(*npte)){
++                              r = 0;
++                              w = 0;
++                      }
++                      if(force || pte_newpage(*npte)){
++                              err = unmap(fd, (void *) addr, PAGE_SIZE);
++                              if(err < 0)
++                                      panic("munmap failed, errno = %d\n",
++                                            -err);
++                              if(pte_present(*npte))
++                                      map(fd, addr, 
++                                          pte_val(*npte) & PAGE_MASK,
++                                          PAGE_SIZE, r, w, x);
++                      }
++                      else if(pte_newprot(*npte)){
++                              protect(fd, addr, PAGE_SIZE, r, w, x, 1);
++                      }
++                      *npte = pte_mkuptodate(*npte);
++                      addr += PAGE_SIZE;
++              }
++              else {
++                      if(force || pmd_newpage(*npmd)){
++                              err = unmap(fd, (void *) addr, PMD_SIZE);
++                              if(err < 0)
++                                      panic("munmap failed, errno = %d\n",
++                                            -err);
++                              pmd_mkuptodate(*npmd);
++                      }
++                      addr += PMD_SIZE;
++              }
++      }
++}
++
++static void flush_kernel_vm_range(unsigned long start, unsigned long end)
++{
++      struct mm_struct *mm;
++      pgd_t *pgd;
++      pmd_t *pmd;
++      pte_t *pte;
++      unsigned long addr;
++      int updated = 0, err;
++
++      mm = &init_mm;
++      for(addr = start; addr < end;){
++              pgd = pgd_offset(mm, addr);
++              pmd = pmd_offset(pgd, addr);
++              if(pmd_present(*pmd)){
++                      pte = pte_offset(pmd, addr);
++                      if(!pte_present(*pte) || pte_newpage(*pte)){
++                              updated = 1;
++                              err = os_unmap_memory((void *) addr, 
++                                                    PAGE_SIZE);
++                              if(err < 0)
++                                      panic("munmap failed, errno = %d\n",
++                                            -err);
++                              if(pte_present(*pte))
++                                      map_memory(addr, 
++                                                 pte_val(*pte) & PAGE_MASK,
++                                                 PAGE_SIZE, 1, 1, 1);
++                      }
++                      else if(pte_newprot(*pte)){
++                              updated = 1;
++                              protect_memory(addr, PAGE_SIZE, 1, 1, 1, 1);
++                      }
++                      addr += PAGE_SIZE;
++              }
++              else {
++                      if(pmd_newpage(*pmd)){
++                              updated = 1;
++                              err = os_unmap_memory((void *) addr, PMD_SIZE);
++                              if(err < 0)
++                                      panic("munmap failed, errno = %d\n",
++                                            -err);
++                      }
++                      addr += PMD_SIZE;
++              }
++      }
++}
++
++void flush_tlb_kernel_vm_skas(void)
++{
++      flush_kernel_vm_range(start_vm, end_vm);
++}
++
++void __flush_tlb_one_skas(unsigned long addr)
++{
++      flush_kernel_vm_range(addr, addr + PAGE_SIZE);
++}
++
++void flush_tlb_range_skas(struct mm_struct *mm, unsigned long start, 
++                   unsigned long end)
++{
++      if(mm == NULL)
++              flush_kernel_vm_range(start, end);
++      else fix_range(mm, start, end, 0);
++}
++
++void flush_tlb_mm_skas(struct mm_struct *mm)
++{
++      flush_tlb_kernel_vm_skas();
++      fix_range(mm, 0, host_task_size, 0);
++}
++
++void force_flush_all_skas(void)
++{
++      fix_range(current->mm, 0, host_task_size, 1);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/skas/trap_user.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/skas/trap_user.c  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/skas/trap_user.c       2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,66 @@
++/* 
++ * Copyright (C) 2002 - 2003 Jeff Dike (jdike@addtoit.com)
++ * Licensed under the GPL
++ */
++
++#include <signal.h>
++#include <errno.h>
++#include <asm/sigcontext.h>
++#include "sysdep/ptrace.h"
++#include "signal_user.h"
++#include "user_util.h"
++#include "kern_util.h"
++#include "task.h"
++#include "sigcontext.h"
++
++void sig_handler_common_skas(int sig, void *sc_ptr)
++{
++      struct sigcontext *sc = sc_ptr;
++      struct skas_regs *r;
++      struct signal_info *info;
++      int save_errno = errno;
++      int save_user;
++
++      r = &TASK_REGS(get_current())->skas;
++      save_user = r->is_user;
++      r->is_user = 0;
++      r->fault_addr = SC_FAULT_ADDR(sc);
++      r->fault_type = SC_FAULT_TYPE(sc);
++      r->trap_type = SC_TRAP_TYPE(sc);
++
++      change_sig(SIGUSR1, 1);
++      info = &sig_info[sig];
++      if(!info->is_irq) unblock_signals();
++
++      (*info->handler)(sig, (union uml_pt_regs *) r);
++
++      errno = save_errno;
++      r->is_user = save_user;
++}
++
++extern int missed_ticks[];
++
++void user_signal(int sig, union uml_pt_regs *regs)
++{
++      struct signal_info *info;
++
++      regs->skas.is_user = 1;
++      regs->skas.fault_addr = 0;
++      regs->skas.fault_type = 0;
++      regs->skas.trap_type = 0;
++      info = &sig_info[sig];
++      (*info->handler)(sig, regs);
++
++      unblock_signals();
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/skas/uaccess.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/skas/uaccess.c    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/skas/uaccess.c 2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,248 @@
++/* 
++ * Copyright (C) 2002 - 2003 Jeff Dike (jdike@addtoit.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/stddef.h"
++#include "linux/kernel.h"
++#include "linux/string.h"
++#include "linux/fs.h"
++#include "linux/highmem.h"
++#include "asm/page.h"
++#include "asm/pgtable.h"
++#include "asm/uaccess.h"
++#include "kern_util.h"
++#include "user_util.h"
++
++extern void *um_virt_to_phys(struct task_struct *task, unsigned long addr, 
++                           pte_t *pte_out);
++
++static unsigned long maybe_map(unsigned long virt, int is_write)
++{
++      pte_t pte;
++
++      void *phys = um_virt_to_phys(current, virt, &pte);
++      int dummy_code;
++
++      if(IS_ERR(phys) || (is_write && !pte_write(pte))){
++              if(!handle_page_fault(virt, 0, is_write, 1, &dummy_code))
++                      return(0);
++              phys = um_virt_to_phys(current, virt, NULL);
++      }
++      return((unsigned long) phys);
++}
++
++static int do_op(unsigned long addr, int len, int is_write, 
++               int (*op)(unsigned long addr, int len, void *arg), void *arg)
++{
++      struct page *page;
++      int n;
++
++      addr = maybe_map(addr, is_write);
++      if(addr == -1)
++              return(-1);
++
++      page = phys_to_page(addr);
++      addr = (unsigned long) kmap(page) + (addr & ~PAGE_MASK);
++      n = (*op)(addr, len, arg);
++      kunmap(page);
++
++      return(n);
++}
++
++static void do_buffer_op(void *jmpbuf, void *arg_ptr)
++{
++      va_list args = *((va_list *) arg_ptr);
++      unsigned long addr = va_arg(args, unsigned long);
++      int len = va_arg(args, int);
++      int is_write = va_arg(args, int);
++      int (*op)(unsigned long, int, void *) = va_arg(args, void *);
++      void *arg = va_arg(args, void *);
++      int *res = va_arg(args, int *);
++      int size = min(PAGE_ALIGN(addr) - addr, (unsigned long) len);
++      int remain = len, n;
++
++      current->thread.fault_catcher = jmpbuf;
++      n = do_op(addr, size, is_write, op, arg);
++      if(n != 0){
++              *res = (n < 0 ? remain : 0);
++              goto out;
++      }
++
++      addr += size;
++      remain -= size;
++      if(remain == 0){
++              *res = 0;
++              goto out;
++      }
++
++      while(addr < ((addr + remain) & PAGE_MASK)){
++              n = do_op(addr, PAGE_SIZE, is_write, op, arg);
++              if(n != 0){
++                      *res = (n < 0 ? remain : 0);
++                      goto out;
++              }
++
++              addr += PAGE_SIZE;
++              remain -= PAGE_SIZE;
++      }
++      if(remain == 0){
++              *res = 0;
++              goto out;
++      }
++
++      n = do_op(addr, remain, is_write, op, arg);
++      if(n != 0)
++              *res = (n < 0 ? remain : 0);
++      else *res = 0;
++ out:
++      current->thread.fault_catcher = NULL;
++}
++
++static int buffer_op(unsigned long addr, int len, int is_write,
++                   int (*op)(unsigned long addr, int len, void *arg),
++                   void *arg)
++{
++      int faulted, res;
++      
++      faulted = setjmp_wrapper(do_buffer_op, addr, len, is_write, op, arg, 
++                               &res);
++      if(!faulted)
++              return(res);
++
++      return(addr + len - (unsigned long) current->thread.fault_addr);
++}
++
++static int copy_chunk_from_user(unsigned long from, int len, void *arg)
++{
++      unsigned long *to_ptr = arg, to = *to_ptr;
++
++      memcpy((void *) to, (void *) from, len);
++      *to_ptr += len;
++      return(0);
++}
++
++int copy_from_user_skas(void *to, const void *from, int n)
++{
++      if(segment_eq(get_fs(), KERNEL_DS)){
++              memcpy(to, from, n);
++              return(0);
++      }
++
++      return(access_ok_skas(VERIFY_READ, from, n) ?
++             buffer_op((unsigned long) from, n, 0, copy_chunk_from_user, &to):
++             n);
++}
++
++static int copy_chunk_to_user(unsigned long to, int len, void *arg)
++{
++      unsigned long *from_ptr = arg, from = *from_ptr;
++
++      memcpy((void *) to, (void *) from, len);
++      *from_ptr += len;
++      return(0);
++}
++
++int copy_to_user_skas(void *to, const void *from, int n)
++{
++      if(segment_eq(get_fs(), KERNEL_DS)){
++              memcpy(to, from, n);
++              return(0);
++      }
++
++      return(access_ok_skas(VERIFY_WRITE, to, n) ?
++             buffer_op((unsigned long) to, n, 1, copy_chunk_to_user, &from) :
++             n);
++}
++
++static int strncpy_chunk_from_user(unsigned long from, int len, void *arg)
++{
++      char **to_ptr = arg, *to = *to_ptr;
++      int n;
++
++      strncpy(to, (void *) from, len);
++      n = strnlen(to, len);
++      *to_ptr += n;
++
++      if(n < len) 
++              return(1);
++      return(0);
++}
++
++int strncpy_from_user_skas(char *dst, const char *src, int count)
++{
++      int n;
++      char *ptr = dst;
++
++      if(segment_eq(get_fs(), KERNEL_DS)){
++              strncpy(dst, src, count);
++              return(strnlen(dst, count));
++      }
++
++      if(!access_ok_skas(VERIFY_READ, src, 1))
++              return(-EFAULT);
++
++      n = buffer_op((unsigned long) src, count, 0, strncpy_chunk_from_user, 
++                    &ptr);
++      if(n != 0)
++              return(-EFAULT);
++      return(strnlen(dst, count));
++}
++
++static int clear_chunk(unsigned long addr, int len, void *unused)
++{
++      memset((void *) addr, 0, len);
++      return(0);
++}
++
++int __clear_user_skas(void *mem, int len)
++{
++      return(buffer_op((unsigned long) mem, len, 1, clear_chunk, NULL));
++}
++
++int clear_user_skas(void *mem, int len)
++{
++      if(segment_eq(get_fs(), KERNEL_DS)){
++              memset(mem, 0, len);
++              return(0);
++      }
++
++      return(access_ok_skas(VERIFY_WRITE, mem, len) ? 
++             buffer_op((unsigned long) mem, len, 1, clear_chunk, NULL) : len);
++}
++
++static int strnlen_chunk(unsigned long str, int len, void *arg)
++{
++      int *len_ptr = arg, n;
++
++      n = strnlen((void *) str, len);
++      *len_ptr += n;
++
++      if(n < len)
++              return(1);
++      return(0);
++}
++
++int strnlen_user_skas(const void *str, int len)
++{
++      int count = 0, n;
++
++      if(segment_eq(get_fs(), KERNEL_DS))
++              return(strnlen(str, len) + 1);
++
++      n = buffer_op((unsigned long) str, len, 0, strnlen_chunk, &count);
++      if(n == 0)
++              return(count + 1);
++      return(-EFAULT);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/skas/util/Makefile
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/skas/util/Makefile        1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/skas/util/Makefile     2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,10 @@
++all: mk_ptregs
++
++mk_ptregs : mk_ptregs.o
++      $(HOSTCC) -o mk_ptregs mk_ptregs.o
++
++mk_ptregs.o : mk_ptregs.c
++      $(HOSTCC) -c $< 
++
++clean : 
++      $(RM) -f mk_ptregs *.o *~
+Index: linux-2.4.29/arch/um/kernel/skas/util/mk_ptregs.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/skas/util/mk_ptregs.c     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/skas/util/mk_ptregs.c  2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,51 @@
++#include <stdio.h>
++#include <asm/ptrace.h>
++#include <asm/user.h>
++
++#define PRINT_REG(name, val) printf("#define HOST_%s %d\n", (name), (val))
++
++int main(int argc, char **argv)
++{
++      printf("/* Automatically generated by "
++             "arch/um/kernel/skas/util/mk_ptregs */\n");
++      printf("\n");
++      printf("#ifndef __SKAS_PT_REGS_\n");
++      printf("#define __SKAS_PT_REGS_\n");
++      printf("\n");
++      printf("#define HOST_FRAME_SIZE %d\n", FRAME_SIZE);
++      printf("#define HOST_FP_SIZE %d\n", 
++             sizeof(struct user_i387_struct) / sizeof(unsigned long));
++      printf("#define HOST_XFP_SIZE %d\n", 
++             sizeof(struct user_fxsr_struct) / sizeof(unsigned long));
++
++      PRINT_REG("IP", EIP);
++      PRINT_REG("SP", UESP);
++      PRINT_REG("EFLAGS", EFL);
++      PRINT_REG("EAX", EAX);
++      PRINT_REG("EBX", EBX);
++      PRINT_REG("ECX", ECX);
++      PRINT_REG("EDX", EDX);
++      PRINT_REG("ESI", ESI);
++      PRINT_REG("EDI", EDI);
++      PRINT_REG("EBP", EBP);
++      PRINT_REG("CS", CS);
++      PRINT_REG("SS", SS);
++      PRINT_REG("DS", DS);
++      PRINT_REG("FS", FS);
++      PRINT_REG("ES", ES);
++      PRINT_REG("GS", GS);
++      printf("\n");
++      printf("#endif\n");
++      return(0);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/smp.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/smp.c     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/smp.c  2005-05-03 22:28:14.476409464 +0300
+@@ -0,0 +1,329 @@
++/* 
++ * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/config.h"
++
++#ifdef CONFIG_SMP
++
++#include "linux/sched.h"
++#include "linux/threads.h"
++#include "linux/interrupt.h"
++#include "asm/smp.h"
++#include "asm/processor.h"
++#include "asm/spinlock.h"
++#include "asm/softirq.h"
++#include "asm/hardirq.h"
++#include "asm/tlb.h"
++#include "user_util.h"
++#include "kern_util.h"
++#include "irq_user.h"
++#include "kern.h"
++#include "os.h"
++
++/* Total count of live CPUs, set by smp_boot_cpus */
++int smp_num_cpus = 1;
++
++/* The 'big kernel lock' */
++spinlock_cacheline_t kernel_flag_cacheline = {SPIN_LOCK_UNLOCKED};
++
++/* Per CPU bogomips and other parameters */
++
++/* The only piece used here is the ipi pipe, which is set before SMP is
++ * started and never changed.
++ */
++struct cpuinfo_um cpu_data[NR_CPUS];
++
++/* CPU online map, set by smp_boot_cpus */
++unsigned long cpu_online_map;
++
++atomic_t global_bh_count;
++
++/* Set when the idlers are all forked */
++int smp_threads_ready = 0;
++
++/* Not used by UML */
++unsigned char global_irq_holder = 0;
++unsigned volatile long global_irq_lock;
++
++/* A statistic, can be a little off */
++static int num_reschedules_sent = 0;
++
++mmu_gather_t mmu_gathers[NR_CPUS];
++
++void smp_send_reschedule(int cpu)
++{
++      os_write_file(cpu_data[cpu].ipi_pipe[1], "R", 1);
++      num_reschedules_sent++;
++}
++
++static void show(char * str)
++{
++      int cpu = smp_processor_id();
++
++      printk(KERN_INFO "\n%s, CPU %d:\n", str, cpu);
++}
++      
++#define MAXCOUNT 100000000
++
++static inline void wait_on_bh(void)
++{
++      int count = MAXCOUNT;
++      do {
++              if (!--count) {
++                      show("wait_on_bh");
++                      count = ~0;
++              }
++              /* nothing .. wait for the other bh's to go away */
++      } while (atomic_read(&global_bh_count) != 0);
++}
++
++/*
++ * This is called when we want to synchronize with
++ * bottom half handlers. We need to wait until
++ * no other CPU is executing any bottom half handler.
++ *
++ * Don't wait if we're already running in an interrupt
++ * context or are inside a bh handler. 
++ */
++void synchronize_bh(void)
++{
++      if (atomic_read(&global_bh_count) && !in_interrupt())
++              wait_on_bh();
++}
++
++void smp_send_stop(void)
++{
++      int i;
++ 
++      printk(KERN_INFO "Stopping all CPUs...");
++      for(i = 0; i < ncpus; i++){
++              if(i == current->processor)
++                      continue;
++              os_write_file(cpu_data[i].ipi_pipe[1], "S", 1);
++      }
++      printk("done\n");
++}
++
++
++static atomic_t smp_commenced = ATOMIC_INIT(0);
++static volatile unsigned long smp_callin_map = 0;
++
++void smp_commence(void)
++{
++      printk("All CPUs are go!\n");
++
++      wmb();
++      atomic_set(&smp_commenced, 1);
++}
++
++static int idle_proc(void *unused)
++{
++      int cpu, err;
++
++      set_current(current);
++      del_from_runqueue(current);
++      unhash_process(current);
++
++      cpu = current->processor;
++      err = os_pipe(cpu_data[cpu].ipi_pipe, 1, 1);
++      if(err < 0)
++              panic("CPU#%d failed to create IPI pipe, err = %d", cpu, -err);
++
++      activate_ipi(cpu_data[cpu].ipi_pipe[0], 
++                   current->thread.mode.tt.extern_pid);
++ 
++      wmb();
++      if (test_and_set_bit(current->processor, &smp_callin_map)) {
++              printk("huh, CPU#%d already present??\n", current->processor);
++              BUG();
++      }
++
++      while (!atomic_read(&smp_commenced))
++              cpu_relax();
++
++      init_idle();
++      cpu_idle();
++      return(0);
++}
++
++static int idle_thread(int (*fn)(void *), int cpu)
++{
++      struct task_struct *new_task;
++      int pid;
++      unsigned char c;
++
++        current->thread.request.u.thread.proc = fn;
++        current->thread.request.u.thread.arg = NULL;
++      pid = do_fork(CLONE_VM | CLONE_PID, 0, NULL, 0);
++      if(pid < 0) 
++              panic("do_fork failed in idle_thread");
++      new_task = get_task(pid, 1);
++
++      cpu_tasks[cpu] = ((struct cpu_task) 
++                        { .pid =      new_task->thread.mode.tt.extern_pid,
++                          .task =     new_task } );
++      init_tasks[cpu] = new_task;
++      new_task->processor = cpu;
++      new_task->cpus_allowed = 1 << cpu;
++      new_task->cpus_runnable = new_task->cpus_allowed;
++      CHOOSE_MODE(({ struct file_handle *pipe;
++                     pipe = new_task->thread.mode.tt.switch_pipe;
++                     write_file(&pipe[1], -1, &c, sizeof(c)); }),
++                  ({ panic("skas mode doesn't support SMP"); }));
++      return(new_task->thread.mode.tt.extern_pid);
++}
++
++void smp_boot_cpus(void)
++{
++      int err;
++
++      set_bit(0, &cpu_online_map);
++      set_bit(0, &smp_callin_map);
++
++      err = os_pipe(cpu_data[0].ipi_pipe, 1, 1);
++      if(err < 0) 
++              panic("CPU#0 failed to create IPI pipe, err = %d", -err);
++
++      activate_ipi(cpu_data[0].ipi_pipe[0], 
++                   current->thread.mode.tt.extern_pid);
++
++      if(ncpus < 1){
++              printk(KERN_INFO "ncpus set to 1\n");
++              ncpus = 1;
++      }
++      else if(ncpus > NR_CPUS){
++              printk(KERN_INFO 
++                     "ncpus can't be greater than NR_CPUS, set to %d\n",
++                     NR_CPUS);
++              ncpus = NR_CPUS;
++      }
++
++      if(ncpus > 1){
++              int i, pid;
++
++              printk(KERN_INFO "Starting up other processors:\n");
++              for(i=1;i<ncpus;i++){
++                      int waittime;
++
++                      /* Do this early, for hard_smp_processor_id()  */
++                      cpu_tasks[i].pid = -1;
++                      set_bit(i, &cpu_online_map);
++                      smp_num_cpus++;
++
++                      pid = idle_thread(idle_proc, i);
++                      printk(KERN_INFO "\t#%d - idle thread pid = %d.. ",
++                             i, pid);
++
++                      waittime = 200000000;
++                      while (waittime-- && !test_bit(i, &smp_callin_map))
++                              cpu_relax();
++
++                      if (test_bit(i, &smp_callin_map))
++                              printk("online\n");
++                      else {
++                              printk("failed\n");
++                              clear_bit(i, &cpu_online_map);
++                      }
++              }
++      }
++}
++
++int setup_profiling_timer(unsigned int multiplier)
++{
++      printk(KERN_INFO "setup_profiling_timer\n");
++      return(0);
++}
++
++void smp_call_function_slave(int cpu);
++
++void IPI_handler(int cpu)
++{
++      unsigned char c;
++      int fd;
++
++      fd = cpu_data[cpu].ipi_pipe[0];
++      while (os_read_file(fd, &c, 1) == 1) {
++              switch (c) {
++              case 'C':
++                      smp_call_function_slave(cpu);
++                      break;
++
++              case 'R':
++                      current->need_resched = 1;
++                      break;
++
++              case 'S':
++                      printk("CPU#%d stopping\n", cpu);
++                      while(1)
++                              pause();
++                      break;
++
++              default:
++                      printk("CPU#%d received unknown IPI [%c]!\n", cpu, c);
++                      break;
++              }
++      }
++}
++
++int hard_smp_processor_id(void)
++{
++      return(pid_to_processor_id(os_getpid()));
++}
++
++static spinlock_t call_lock = SPIN_LOCK_UNLOCKED;
++static atomic_t scf_started;
++static atomic_t scf_finished;
++static void (*func)(void *info);
++static void *info;
++
++void smp_call_function_slave(int cpu)
++{
++      atomic_inc(&scf_started);
++      (*func)(info);
++      atomic_inc(&scf_finished);
++}
++
++int smp_call_function(void (*_func)(void *info), void *_info, int nonatomic, 
++                    int wait)
++{
++      int cpus = smp_num_cpus - 1;
++      int i;
++
++      if (!cpus)
++              return 0;
++
++      spin_lock_bh(&call_lock);
++      atomic_set(&scf_started, 0);
++      atomic_set(&scf_finished, 0);
++      func = _func;
++      info = _info;
++
++      for (i=0;i<NR_CPUS;i++)
++              if (i != current->processor && test_bit(i, &cpu_online_map))
++                      os_write_file(cpu_data[i].ipi_pipe[1], "C", 1);
++
++      while (atomic_read(&scf_started) != cpus)
++              barrier();
++
++      if (wait)
++              while (atomic_read(&scf_finished) != cpus)
++                      barrier();
++
++      spin_unlock_bh(&call_lock);
++      return 0;
++}
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/syscall_kern.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/syscall_kern.c    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/syscall_kern.c 2005-05-03 22:28:14.477409312 +0300
+@@ -0,0 +1,343 @@
++/* 
++ * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/sched.h"
++#include "linux/file.h"
++#include "linux/smp_lock.h"
++#include "linux/mm.h"
++#include "linux/utsname.h"
++#include "linux/msg.h"
++#include "linux/shm.h"
++#include "linux/sys.h"
++#include "linux/unistd.h"
++#include "linux/slab.h"
++#include "linux/utime.h"
++#include "asm/mman.h"
++#include "asm/uaccess.h"
++#include "asm/ipc.h"
++#include "kern_util.h"
++#include "user_util.h"
++#include "sysdep/syscalls.h"
++#include "mode_kern.h"
++#include "choose-mode.h"
++
++/*  Unlocked, I don't care if this is a bit off */
++int nsyscalls = 0;
++
++long um_mount(char * dev_name, char * dir_name, char * type,
++            unsigned long new_flags, void * data)
++{
++      if(type == NULL) type = "";
++      return(sys_mount(dev_name, dir_name, type, new_flags, data));
++}
++
++long sys_fork(void)
++{
++      long ret;
++
++      current->thread.forking = 1;
++        ret = do_fork(SIGCHLD, 0, NULL, 0);
++      current->thread.forking = 0;
++      return(ret);
++}
++
++long sys_clone(unsigned long clone_flags, unsigned long newsp)
++{
++      long ret;
++
++      current->thread.forking = 1;
++      ret = do_fork(clone_flags, newsp, NULL, 0);
++      current->thread.forking = 0;
++      return(ret);
++}
++
++long sys_vfork(void)
++{
++      long ret;
++
++      current->thread.forking = 1;
++      ret = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, 0, NULL, 0);
++      current->thread.forking = 0;
++      return(ret);
++}
++
++/* common code for old and new mmaps */
++long do_mmap2(struct mm_struct *mm, unsigned long addr, unsigned long len,
++            unsigned long prot, unsigned long flags, unsigned long fd,
++            unsigned long pgoff)
++{
++      int error = -EBADF;
++      struct file * file = NULL;
++
++      flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
++      if (!(flags & MAP_ANONYMOUS)) {
++              file = fget(fd);
++              if (!file)
++                      goto out;
++      }
++
++      down_write(&mm->mmap_sem);
++      error = do_mmap_pgoff(mm, file, addr, len, prot, flags, pgoff);
++      up_write(&mm->mmap_sem);
++
++      if (file)
++              fput(file);
++ out:
++      return error;
++}
++
++long sys_mmap2(unsigned long addr, unsigned long len,
++             unsigned long prot, unsigned long flags,
++             unsigned long fd, unsigned long pgoff)
++{
++      return do_mmap2(current->mm, addr, len, prot, flags, fd, pgoff);
++}
++
++/*
++ * Perform the select(nd, in, out, ex, tv) and mmap() system
++ * calls. Linux/i386 didn't use to be able to handle more than
++ * 4 system call parameters, so these system calls used a memory
++ * block for parameter passing..
++ */
++
++struct mmap_arg_struct {
++      unsigned long addr;
++      unsigned long len;
++      unsigned long prot;
++      unsigned long flags;
++      unsigned long fd;
++      unsigned long offset;
++};
++
++int old_mmap(unsigned long addr, unsigned long len,
++           unsigned long prot, unsigned long flags,
++           unsigned long fd, unsigned long offset)
++{
++      int err = -EINVAL;
++      if (offset & ~PAGE_MASK)
++              goto out;
++
++      err = do_mmap2(current->mm, addr, len, prot, flags, fd, 
++                     offset >> PAGE_SHIFT);
++ out:
++      return err;
++}
++/*
++ * sys_pipe() is the normal C calling standard for creating
++ * a pipe. It's not the way unix traditionally does this, though.
++ */
++int sys_pipe(unsigned long * fildes)
++{
++        int fd[2];
++        int error;
++
++        error = do_pipe(fd);
++        if (!error) {
++              if (copy_to_user(fildes, fd, sizeof(fd)))
++                        error = -EFAULT;
++        }
++        return error;
++}
++
++int sys_pause(void)
++{
++      current->state = TASK_INTERRUPTIBLE;
++      schedule();
++      return -ERESTARTNOHAND;
++}
++
++int sys_sigaction(int sig, const struct old_sigaction *act,
++                       struct old_sigaction *oact)
++{
++      struct k_sigaction new_ka, old_ka;
++      int ret;
++
++      if (act) {
++              old_sigset_t mask;
++              if (verify_area(VERIFY_READ, act, sizeof(*act)) ||
++                  __get_user(new_ka.sa.sa_handler, &act->sa_handler) ||
++                  __get_user(new_ka.sa.sa_restorer, &act->sa_restorer))
++                      return -EFAULT;
++              __get_user(new_ka.sa.sa_flags, &act->sa_flags);
++              __get_user(mask, &act->sa_mask);
++              siginitset(&new_ka.sa.sa_mask, mask);
++      }
++
++      ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);
++
++      if (!ret && oact) {
++              if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) ||
++                  __put_user(old_ka.sa.sa_handler, &oact->sa_handler) ||
++                  __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer))
++                      return -EFAULT;
++              __put_user(old_ka.sa.sa_flags, &oact->sa_flags);
++              __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask);
++      }
++
++      return ret;
++}
++
++/*
++ * sys_ipc() is the de-multiplexer for the SysV IPC calls..
++ *
++ * This is really horribly ugly.
++ */
++int sys_ipc (uint call, int first, int second,
++           int third, void *ptr, long fifth)
++{
++      int version, ret;
++
++      version = call >> 16; /* hack for backward compatibility */
++      call &= 0xffff;
++
++      switch (call) {
++      case SEMOP:
++              return sys_semop (first, (struct sembuf *)ptr, second);
++      case SEMGET:
++              return sys_semget (first, second, third);
++      case SEMCTL: {
++              union semun fourth;
++              if (!ptr)
++                      return -EINVAL;
++              if (get_user(fourth.__pad, (void **) ptr))
++                      return -EFAULT;
++              return sys_semctl (first, second, third, fourth);
++      }
++
++      case MSGSND:
++              return sys_msgsnd (first, (struct msgbuf *) ptr, 
++                                 second, third);
++      case MSGRCV:
++              switch (version) {
++              case 0: {
++                      struct ipc_kludge tmp;
++                      if (!ptr)
++                              return -EINVAL;
++                      
++                      if (copy_from_user(&tmp,
++                                         (struct ipc_kludge *) ptr, 
++                                         sizeof (tmp)))
++                              return -EFAULT;
++                      return sys_msgrcv (first, tmp.msgp, second,
++                                         tmp.msgtyp, third);
++              }
++              default:
++                      panic("msgrcv with version != 0");
++                      return sys_msgrcv (first,
++                                         (struct msgbuf *) ptr,
++                                         second, fifth, third);
++              }
++      case MSGGET:
++              return sys_msgget ((key_t) first, second);
++      case MSGCTL:
++              return sys_msgctl (first, second, (struct msqid_ds *) ptr);
++
++      case SHMAT:
++              switch (version) {
++              default: {
++                      ulong raddr;
++                      ret = sys_shmat (first, (char *) ptr, second, &raddr);
++                      if (ret)
++                              return ret;
++                      return put_user (raddr, (ulong *) third);
++              }
++              case 1: /* iBCS2 emulator entry point */
++                      if (!segment_eq(get_fs(), get_ds()))
++                              return -EINVAL;
++                      return sys_shmat (first, (char *) ptr, second, (ulong *) third);
++              }
++      case SHMDT: 
++              return sys_shmdt ((char *)ptr);
++      case SHMGET:
++              return sys_shmget (first, second, third);
++      case SHMCTL:
++              return sys_shmctl (first, second,
++                                 (struct shmid_ds *) ptr);
++      default:
++              return -EINVAL;
++      }
++}
++
++int sys_uname(struct old_utsname * name)
++{
++      int err;
++      if (!name)
++              return -EFAULT;
++      down_read(&uts_sem);
++      err=copy_to_user(name, &system_utsname, sizeof (*name));
++      up_read(&uts_sem);
++      return err?-EFAULT:0;
++}
++
++int sys_olduname(struct oldold_utsname * name)
++{
++      int error;
++
++      if (!name)
++              return -EFAULT;
++      if (!access_ok(VERIFY_WRITE,name,sizeof(struct oldold_utsname)))
++              return -EFAULT;
++  
++      down_read(&uts_sem);
++      
++      error = __copy_to_user(&name->sysname,&system_utsname.sysname,
++                             __OLD_UTS_LEN);
++      error |= __put_user(0,name->sysname+__OLD_UTS_LEN);
++      error |= __copy_to_user(&name->nodename,&system_utsname.nodename,
++                              __OLD_UTS_LEN);
++      error |= __put_user(0,name->nodename+__OLD_UTS_LEN);
++      error |= __copy_to_user(&name->release,&system_utsname.release,
++                              __OLD_UTS_LEN);
++      error |= __put_user(0,name->release+__OLD_UTS_LEN);
++      error |= __copy_to_user(&name->version,&system_utsname.version,
++                              __OLD_UTS_LEN);
++      error |= __put_user(0,name->version+__OLD_UTS_LEN);
++      error |= __copy_to_user(&name->machine,&system_utsname.machine,
++                              __OLD_UTS_LEN);
++      error |= __put_user(0,name->machine+__OLD_UTS_LEN);
++      
++      up_read(&uts_sem);
++      
++      error = error ? -EFAULT : 0;
++
++      return error;
++}
++
++int sys_sigaltstack(const stack_t *uss, stack_t *uoss)
++{
++      return(do_sigaltstack(uss, uoss, PT_REGS_SP(&current->thread.regs)));
++}
++
++long execute_syscall(void *r)
++{
++      return(CHOOSE_MODE_PROC(execute_syscall_tt, execute_syscall_skas, r));
++}
++
++spinlock_t syscall_lock = SPIN_LOCK_UNLOCKED;
++
++static int syscall_index = 0;
++
++int next_syscall_index(int limit)
++{
++      int ret;
++
++      spin_lock(&syscall_lock);
++      ret = syscall_index;
++      if(++syscall_index == limit)
++              syscall_index = 0;
++      spin_unlock(&syscall_lock);
++      return(ret);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/sys_call_table.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/sys_call_table.c  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/sys_call_table.c       2005-05-03 22:28:14.480408856 +0300
+@@ -0,0 +1,496 @@
++/* 
++ * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/unistd.h"
++#include "linux/version.h"
++#include "linux/sys.h"
++#include "asm/signal.h"
++#include "sysdep/syscalls.h"
++#include "kern_util.h"
++
++extern syscall_handler_t sys_ni_syscall;
++extern syscall_handler_t sys_exit;
++extern syscall_handler_t sys_fork;
++extern syscall_handler_t sys_creat;
++extern syscall_handler_t sys_link;
++extern syscall_handler_t sys_unlink;
++extern syscall_handler_t sys_chdir;
++extern syscall_handler_t sys_mknod;
++extern syscall_handler_t sys_chmod;
++extern syscall_handler_t sys_lchown16;
++extern syscall_handler_t sys_ni_syscall;
++extern syscall_handler_t sys_stat;
++extern syscall_handler_t sys_getpid;
++extern syscall_handler_t sys_oldumount;
++extern syscall_handler_t sys_setuid16;
++extern syscall_handler_t sys_getuid16;
++extern syscall_handler_t sys_ptrace;
++extern syscall_handler_t sys_alarm;
++extern syscall_handler_t sys_fstat;
++extern syscall_handler_t sys_pause;
++extern syscall_handler_t sys_utime;
++extern syscall_handler_t sys_ni_syscall;
++extern syscall_handler_t sys_ni_syscall;
++extern syscall_handler_t sys_access;
++extern syscall_handler_t sys_nice;
++extern syscall_handler_t sys_ni_syscall;
++extern syscall_handler_t sys_sync;
++extern syscall_handler_t sys_kill;
++extern syscall_handler_t sys_rename;
++extern syscall_handler_t sys_mkdir;
++extern syscall_handler_t sys_rmdir;
++extern syscall_handler_t sys_pipe;
++extern syscall_handler_t sys_times;
++extern syscall_handler_t sys_ni_syscall;
++extern syscall_handler_t sys_brk;
++extern syscall_handler_t sys_setgid16;
++extern syscall_handler_t sys_getgid16;
++extern syscall_handler_t sys_signal;
++extern syscall_handler_t sys_geteuid16;
++extern syscall_handler_t sys_getegid16;
++extern syscall_handler_t sys_acct;
++extern syscall_handler_t sys_umount;
++extern syscall_handler_t sys_ni_syscall;
++extern syscall_handler_t sys_ioctl;
++extern syscall_handler_t sys_fcntl;
++extern syscall_handler_t sys_ni_syscall;
++extern syscall_handler_t sys_setpgid;
++extern syscall_handler_t sys_ni_syscall;
++extern syscall_handler_t sys_olduname;
++extern syscall_handler_t sys_umask;
++extern syscall_handler_t sys_chroot;
++extern syscall_handler_t sys_ustat;
++extern syscall_handler_t sys_dup2;
++extern syscall_handler_t sys_getppid;
++extern syscall_handler_t sys_getpgrp;
++extern syscall_handler_t sys_sigaction;
++extern syscall_handler_t sys_sgetmask;
++extern syscall_handler_t sys_ssetmask;
++extern syscall_handler_t sys_setreuid16;
++extern syscall_handler_t sys_setregid16;
++extern syscall_handler_t sys_sigsuspend;
++extern syscall_handler_t sys_sigpending;
++extern syscall_handler_t sys_sethostname;
++extern syscall_handler_t sys_setrlimit;
++extern syscall_handler_t sys_old_getrlimit;
++extern syscall_handler_t sys_getrusage;
++extern syscall_handler_t sys_gettimeofday;
++extern syscall_handler_t sys_settimeofday;
++extern syscall_handler_t sys_getgroups16;
++extern syscall_handler_t sys_setgroups16;
++extern syscall_handler_t sys_symlink;
++extern syscall_handler_t sys_lstat;
++extern syscall_handler_t sys_readlink;
++extern syscall_handler_t sys_uselib;
++extern syscall_handler_t sys_swapon;
++extern syscall_handler_t sys_reboot;
++extern syscall_handler_t old_readdir;
++extern syscall_handler_t sys_munmap;
++extern syscall_handler_t sys_truncate;
++extern syscall_handler_t sys_ftruncate;
++extern syscall_handler_t sys_fchmod;
++extern syscall_handler_t sys_fchown16;
++extern syscall_handler_t sys_getpriority;
++extern syscall_handler_t sys_setpriority;
++extern syscall_handler_t sys_ni_syscall;
++extern syscall_handler_t sys_statfs;
++extern syscall_handler_t sys_fstatfs;
++extern syscall_handler_t sys_ni_syscall;
++extern syscall_handler_t sys_socketcall;
++extern syscall_handler_t sys_syslog;
++extern syscall_handler_t sys_setitimer;
++extern syscall_handler_t sys_getitimer;
++extern syscall_handler_t sys_newstat;
++extern syscall_handler_t sys_newlstat;
++extern syscall_handler_t sys_newfstat;
++extern syscall_handler_t sys_uname;
++extern syscall_handler_t sys_ni_syscall;
++extern syscall_handler_t sys_vhangup;
++extern syscall_handler_t sys_ni_syscall;
++extern syscall_handler_t sys_ni_syscall;
++extern syscall_handler_t sys_swapoff;
++extern syscall_handler_t sys_sysinfo;
++extern syscall_handler_t sys_ipc;
++extern syscall_handler_t sys_fsync;
++extern syscall_handler_t sys_sigreturn;
++extern syscall_handler_t sys_rt_sigreturn;
++extern syscall_handler_t sys_clone;
++extern syscall_handler_t sys_setdomainname;
++extern syscall_handler_t sys_newuname;
++extern syscall_handler_t sys_ni_syscall;
++extern syscall_handler_t sys_adjtimex;
++extern syscall_handler_t sys_mprotect;
++extern syscall_handler_t sys_sigprocmask;
++extern syscall_handler_t sys_create_module;
++extern syscall_handler_t sys_init_module;
++extern syscall_handler_t sys_delete_module;
++extern syscall_handler_t sys_get_kernel_syms;
++extern syscall_handler_t sys_quotactl;
++extern syscall_handler_t sys_getpgid;
++extern syscall_handler_t sys_fchdir;
++extern syscall_handler_t sys_bdflush;
++extern syscall_handler_t sys_sysfs;
++extern syscall_handler_t sys_personality;
++extern syscall_handler_t sys_ni_syscall;
++extern syscall_handler_t sys_setfsuid16;
++extern syscall_handler_t sys_setfsgid16;
++extern syscall_handler_t sys_llseek;
++extern syscall_handler_t sys_getdents;
++extern syscall_handler_t sys_flock;
++extern syscall_handler_t sys_msync;
++extern syscall_handler_t sys_readv;
++extern syscall_handler_t sys_writev;
++extern syscall_handler_t sys_getsid;
++extern syscall_handler_t sys_fdatasync;
++extern syscall_handler_t sys_sysctl;
++extern syscall_handler_t sys_mlock;
++extern syscall_handler_t sys_munlock;
++extern syscall_handler_t sys_mlockall;
++extern syscall_handler_t sys_munlockall;
++extern syscall_handler_t sys_sched_setparam;
++extern syscall_handler_t sys_sched_getparam;
++extern syscall_handler_t sys_sched_setscheduler;
++extern syscall_handler_t sys_sched_getscheduler;
++extern syscall_handler_t sys_sched_get_priority_max;
++extern syscall_handler_t sys_sched_get_priority_min;
++extern syscall_handler_t sys_sched_rr_get_interval;
++extern syscall_handler_t sys_nanosleep;
++extern syscall_handler_t sys_mremap;
++extern syscall_handler_t sys_setresuid16;
++extern syscall_handler_t sys_getresuid16;
++extern syscall_handler_t sys_ni_syscall;
++extern syscall_handler_t sys_query_module;
++extern syscall_handler_t sys_poll;
++extern syscall_handler_t sys_nfsservctl;
++extern syscall_handler_t sys_setresgid16;
++extern syscall_handler_t sys_getresgid16;
++extern syscall_handler_t sys_prctl;
++extern syscall_handler_t sys_ni_syscall;
++extern syscall_handler_t sys_rt_sigaction;
++extern syscall_handler_t sys_rt_sigprocmask;
++extern syscall_handler_t sys_rt_sigpending;
++extern syscall_handler_t sys_rt_sigtimedwait;
++extern syscall_handler_t sys_rt_sigqueueinfo;
++extern syscall_handler_t sys_rt_sigsuspend;
++extern syscall_handler_t sys_pread;
++extern syscall_handler_t sys_pwrite;
++extern syscall_handler_t sys_chown16;
++extern syscall_handler_t sys_getcwd;
++extern syscall_handler_t sys_capget;
++extern syscall_handler_t sys_capset;
++extern syscall_handler_t sys_sigaltstack;
++extern syscall_handler_t sys_sendfile;
++extern syscall_handler_t sys_ni_syscall;
++extern syscall_handler_t sys_ni_syscall;
++extern syscall_handler_t sys_vfork;
++extern syscall_handler_t sys_getrlimit;
++extern syscall_handler_t sys_mmap2;
++extern syscall_handler_t sys_truncate64;
++extern syscall_handler_t sys_ftruncate64;
++extern syscall_handler_t sys_stat64;
++extern syscall_handler_t sys_lstat64;
++extern syscall_handler_t sys_fstat64;
++extern syscall_handler_t sys_lchown;
++extern syscall_handler_t sys_getuid;
++extern syscall_handler_t sys_getgid;
++extern syscall_handler_t sys_geteuid;
++extern syscall_handler_t sys_getegid;
++extern syscall_handler_t sys_setreuid;
++extern syscall_handler_t sys_setregid;
++extern syscall_handler_t sys_getgroups;
++extern syscall_handler_t sys_setgroups;
++extern syscall_handler_t sys_fchown;
++extern syscall_handler_t sys_setresuid;
++extern syscall_handler_t sys_getresuid;
++extern syscall_handler_t sys_setresgid;
++extern syscall_handler_t sys_getresgid;
++extern syscall_handler_t sys_chown;
++extern syscall_handler_t sys_setuid;
++extern syscall_handler_t sys_setgid;
++extern syscall_handler_t sys_setfsuid;
++extern syscall_handler_t sys_setfsgid;
++extern syscall_handler_t sys_pivot_root;
++extern syscall_handler_t sys_mincore;
++extern syscall_handler_t sys_madvise;
++extern syscall_handler_t sys_fcntl64;
++extern syscall_handler_t sys_getdents64;
++extern syscall_handler_t sys_gettid;
++extern syscall_handler_t sys_readahead;
++extern syscall_handler_t sys_tkill;
++extern syscall_handler_t sys_setxattr;
++extern syscall_handler_t sys_lsetxattr;
++extern syscall_handler_t sys_fsetxattr;
++extern syscall_handler_t sys_getxattr;
++extern syscall_handler_t sys_lgetxattr;
++extern syscall_handler_t sys_fgetxattr;
++extern syscall_handler_t sys_listxattr;
++extern syscall_handler_t sys_llistxattr;
++extern syscall_handler_t sys_flistxattr;
++extern syscall_handler_t sys_removexattr;
++extern syscall_handler_t sys_lremovexattr;
++extern syscall_handler_t sys_fremovexattr;
++extern syscall_handler_t sys_sendfile64;
++
++extern syscall_handler_t um_mount;
++extern syscall_handler_t um_time;
++extern syscall_handler_t um_stime;
++
++#define LAST_GENERIC_SYSCALL __NR_exit_group
++
++#if LAST_GENERIC_SYSCALL > LAST_ARCH_SYSCALL
++#define LAST_SYSCALL LAST_GENERIC_SYSCALL
++#else
++#define LAST_SYSCALL LAST_ARCH_SYSCALL
++#endif
++
++syscall_handler_t *sys_call_table[] = {
++      [ 0 ] = sys_ni_syscall,
++      [ __NR_exit ] = sys_exit,
++      [ __NR_fork ] = sys_fork,
++      [ __NR_read ] = (syscall_handler_t *) sys_read,
++      [ __NR_write ] = (syscall_handler_t *) sys_write,
++
++      /* These three are declared differently in asm/unistd.h */
++      [ __NR_open ] = (syscall_handler_t *) sys_open,
++      [ __NR_close ] = (syscall_handler_t *) sys_close,
++      [ __NR_waitpid ] = (syscall_handler_t *) sys_waitpid,
++      [ __NR_creat ] = sys_creat,
++      [ __NR_link ] = sys_link,
++      [ __NR_unlink ] = sys_unlink,
++
++      /* declared differently in kern_util.h */
++      [ __NR_execve ] = (syscall_handler_t *) sys_execve,
++      [ __NR_chdir ] = sys_chdir,
++      [ __NR_time ] = um_time,
++      [ __NR_mknod ] = sys_mknod,
++      [ __NR_chmod ] = sys_chmod,
++      [ __NR_lchown ] = sys_lchown16,
++      [ __NR_break ] = sys_ni_syscall,
++      [ __NR_oldstat ] = sys_stat,
++      [ __NR_lseek ] = (syscall_handler_t *) sys_lseek,
++      [ __NR_getpid ] = sys_getpid,
++      [ __NR_mount ] = um_mount,
++      [ __NR_umount ] = sys_oldumount,
++      [ __NR_setuid ] = sys_setuid16,
++      [ __NR_getuid ] = sys_getuid16,
++      [ __NR_stime ] = um_stime,
++      [ __NR_ptrace ] = sys_ptrace,
++      [ __NR_alarm ] = sys_alarm,
++      [ __NR_oldfstat ] = sys_fstat,
++      [ __NR_pause ] = sys_pause,
++      [ __NR_utime ] = sys_utime,
++      [ __NR_stty ] = sys_ni_syscall,
++      [ __NR_gtty ] = sys_ni_syscall,
++      [ __NR_access ] = sys_access,
++      [ __NR_nice ] = sys_nice,
++      [ __NR_ftime ] = sys_ni_syscall,
++      [ __NR_sync ] = sys_sync,
++      [ __NR_kill ] = sys_kill,
++      [ __NR_rename ] = sys_rename,
++      [ __NR_mkdir ] = sys_mkdir,
++      [ __NR_rmdir ] = sys_rmdir,
++
++      /* Declared differently in asm/unistd.h */
++      [ __NR_dup ] = (syscall_handler_t *) sys_dup,
++      [ __NR_pipe ] = sys_pipe,
++      [ __NR_times ] = sys_times,
++      [ __NR_prof ] = sys_ni_syscall,
++      [ __NR_brk ] = sys_brk,
++      [ __NR_setgid ] = sys_setgid16,
++      [ __NR_getgid ] = sys_getgid16,
++      [ __NR_signal ] = sys_signal,
++      [ __NR_geteuid ] = sys_geteuid16,
++      [ __NR_getegid ] = sys_getegid16,
++      [ __NR_acct ] = sys_acct,
++      [ __NR_umount2 ] = sys_umount,
++      [ __NR_lock ] = sys_ni_syscall,
++      [ __NR_ioctl ] = sys_ioctl,
++      [ __NR_fcntl ] = sys_fcntl,
++      [ __NR_mpx ] = sys_ni_syscall,
++      [ __NR_setpgid ] = sys_setpgid,
++      [ __NR_ulimit ] = sys_ni_syscall,
++      [ __NR_oldolduname ] = sys_olduname,
++      [ __NR_umask ] = sys_umask,
++      [ __NR_chroot ] = sys_chroot,
++      [ __NR_ustat ] = sys_ustat,
++      [ __NR_dup2 ] = sys_dup2,
++      [ __NR_getppid ] = sys_getppid,
++      [ __NR_getpgrp ] = sys_getpgrp,
++      [ __NR_setsid ] = (syscall_handler_t *) sys_setsid,
++      [ __NR_sigaction ] = sys_sigaction,
++      [ __NR_sgetmask ] = sys_sgetmask,
++      [ __NR_ssetmask ] = sys_ssetmask,
++      [ __NR_setreuid ] = sys_setreuid16,
++      [ __NR_setregid ] = sys_setregid16,
++      [ __NR_sigsuspend ] = sys_sigsuspend,
++      [ __NR_sigpending ] = sys_sigpending,
++      [ __NR_sethostname ] = sys_sethostname,
++      [ __NR_setrlimit ] = sys_setrlimit,
++      [ __NR_getrlimit ] = sys_old_getrlimit,
++      [ __NR_getrusage ] = sys_getrusage,
++      [ __NR_gettimeofday ] = sys_gettimeofday,
++      [ __NR_settimeofday ] = sys_settimeofday,
++      [ __NR_getgroups ] = sys_getgroups16,
++      [ __NR_setgroups ] = sys_setgroups16,
++      [ __NR_symlink ] = sys_symlink,
++      [ __NR_oldlstat ] = sys_lstat,
++      [ __NR_readlink ] = sys_readlink,
++      [ __NR_uselib ] = sys_uselib,
++      [ __NR_swapon ] = sys_swapon,
++      [ __NR_reboot ] = sys_reboot,
++      [ __NR_readdir ] = old_readdir,
++      [ __NR_munmap ] = sys_munmap,
++      [ __NR_truncate ] = sys_truncate,
++      [ __NR_ftruncate ] = sys_ftruncate,
++      [ __NR_fchmod ] = sys_fchmod,
++      [ __NR_fchown ] = sys_fchown16,
++      [ __NR_getpriority ] = sys_getpriority,
++      [ __NR_setpriority ] = sys_setpriority,
++      [ __NR_profil ] = sys_ni_syscall,
++      [ __NR_statfs ] = sys_statfs,
++      [ __NR_fstatfs ] = sys_fstatfs,
++      [ __NR_ioperm ] = sys_ni_syscall,
++      [ __NR_socketcall ] = sys_socketcall,
++      [ __NR_syslog ] = sys_syslog,
++      [ __NR_setitimer ] = sys_setitimer,
++      [ __NR_getitimer ] = sys_getitimer,
++      [ __NR_stat ] = sys_newstat,
++      [ __NR_lstat ] = sys_newlstat,
++      [ __NR_fstat ] = sys_newfstat,
++      [ __NR_olduname ] = sys_uname,
++      [ __NR_iopl ] = sys_ni_syscall,
++      [ __NR_vhangup ] = sys_vhangup,
++      [ __NR_idle ] = sys_ni_syscall,
++      [ __NR_wait4 ] = (syscall_handler_t *) sys_wait4,
++      [ __NR_swapoff ] = sys_swapoff,
++      [ __NR_sysinfo ] = sys_sysinfo,
++      [ __NR_ipc ] = sys_ipc,
++      [ __NR_fsync ] = sys_fsync,
++      [ __NR_sigreturn ] = sys_sigreturn,
++      [ __NR_clone ] = sys_clone,
++      [ __NR_setdomainname ] = sys_setdomainname,
++      [ __NR_uname ] = sys_newuname,
++      [ __NR_adjtimex ] = sys_adjtimex,
++      [ __NR_mprotect ] = sys_mprotect,
++      [ __NR_sigprocmask ] = sys_sigprocmask,
++      [ __NR_create_module ] = sys_create_module,
++      [ __NR_init_module ] = sys_init_module,
++      [ __NR_delete_module ] = sys_delete_module,
++      [ __NR_get_kernel_syms ] = sys_get_kernel_syms,
++      [ __NR_quotactl ] = sys_quotactl,
++      [ __NR_getpgid ] = sys_getpgid,
++      [ __NR_fchdir ] = sys_fchdir,
++      [ __NR_bdflush ] = sys_bdflush,
++      [ __NR_sysfs ] = sys_sysfs,
++      [ __NR_personality ] = sys_personality,
++      [ __NR_afs_syscall ] = sys_ni_syscall,
++      [ __NR_setfsuid ] = sys_setfsuid16,
++      [ __NR_setfsgid ] = sys_setfsgid16,
++      [ __NR__llseek ] = sys_llseek,
++      [ __NR_getdents ] = sys_getdents,
++      [ __NR__newselect ] = (syscall_handler_t *) sys_select,
++      [ __NR_flock ] = sys_flock,
++      [ __NR_msync ] = sys_msync,
++      [ __NR_readv ] = sys_readv,
++      [ __NR_writev ] = sys_writev,
++      [ __NR_getsid ] = sys_getsid,
++      [ __NR_fdatasync ] = sys_fdatasync,
++      [ __NR__sysctl ] = sys_sysctl,
++      [ __NR_mlock ] = sys_mlock,
++      [ __NR_munlock ] = sys_munlock,
++      [ __NR_mlockall ] = sys_mlockall,
++      [ __NR_munlockall ] = sys_munlockall,
++      [ __NR_sched_setparam ] = sys_sched_setparam,
++      [ __NR_sched_getparam ] = sys_sched_getparam,
++      [ __NR_sched_setscheduler ] = sys_sched_setscheduler,
++      [ __NR_sched_getscheduler ] = sys_sched_getscheduler,
++      [ __NR_sched_yield ] = (syscall_handler_t *) yield,
++      [ __NR_sched_get_priority_max ] = sys_sched_get_priority_max,
++      [ __NR_sched_get_priority_min ] = sys_sched_get_priority_min,
++      [ __NR_sched_rr_get_interval ] = sys_sched_rr_get_interval,
++      [ __NR_nanosleep ] = sys_nanosleep,
++      [ __NR_mremap ] = sys_mremap,
++      [ __NR_setresuid ] = sys_setresuid16,
++      [ __NR_getresuid ] = sys_getresuid16,
++      [ __NR_vm86 ] = sys_ni_syscall,
++      [ __NR_query_module ] = sys_query_module,
++      [ __NR_poll ] = sys_poll,
++      [ __NR_nfsservctl ] = sys_nfsservctl,
++      [ __NR_setresgid ] = sys_setresgid16,
++      [ __NR_getresgid ] = sys_getresgid16,
++      [ __NR_prctl ] = sys_prctl,
++      [ __NR_rt_sigreturn ] = sys_rt_sigreturn,
++      [ __NR_rt_sigaction ] = sys_rt_sigaction,
++      [ __NR_rt_sigprocmask ] = sys_rt_sigprocmask,
++      [ __NR_rt_sigpending ] = sys_rt_sigpending,
++      [ __NR_rt_sigtimedwait ] = sys_rt_sigtimedwait,
++      [ __NR_rt_sigqueueinfo ] = sys_rt_sigqueueinfo,
++      [ __NR_rt_sigsuspend ] = sys_rt_sigsuspend,
++      [ __NR_pread ] = sys_pread,
++      [ __NR_pwrite ] = sys_pwrite,
++      [ __NR_chown ] = sys_chown16,
++      [ __NR_getcwd ] = sys_getcwd,
++      [ __NR_capget ] = sys_capget,
++      [ __NR_capset ] = sys_capset,
++      [ __NR_sigaltstack ] = sys_sigaltstack,
++      [ __NR_sendfile ] = sys_sendfile,
++      [ __NR_getpmsg ] = sys_ni_syscall,
++      [ __NR_putpmsg ] = sys_ni_syscall,
++      [ __NR_vfork ] = sys_vfork,
++      [ __NR_ugetrlimit ] = sys_getrlimit,
++      [ __NR_mmap2 ] = sys_mmap2,
++      [ __NR_truncate64 ] = sys_truncate64,
++      [ __NR_ftruncate64 ] = sys_ftruncate64,
++      [ __NR_stat64 ] = sys_stat64,
++      [ __NR_lstat64 ] = sys_lstat64,
++      [ __NR_fstat64 ] = sys_fstat64,
++      [ __NR_fcntl64 ] = sys_fcntl64,
++      [ __NR_getdents64 ] = sys_getdents64,
++        [ __NR_security ] = sys_ni_syscall,
++      [ __NR_gettid ] = sys_gettid,
++      [ __NR_readahead ] = sys_readahead,
++      [ __NR_setxattr ] = sys_setxattr,
++      [ __NR_lsetxattr ] = sys_lsetxattr,
++      [ __NR_fsetxattr ] = sys_fsetxattr,
++      [ __NR_getxattr ] = sys_getxattr,
++      [ __NR_lgetxattr ] = sys_lgetxattr,
++      [ __NR_fgetxattr ] = sys_fgetxattr,
++      [ __NR_listxattr ] = sys_listxattr,
++      [ __NR_llistxattr ] = sys_llistxattr,
++      [ __NR_flistxattr ] = sys_flistxattr,
++      [ __NR_removexattr ] = sys_removexattr,
++      [ __NR_lremovexattr ] = sys_lremovexattr,
++      [ __NR_fremovexattr ] = sys_fremovexattr,
++      [ __NR_tkill ] = sys_tkill,
++      [ __NR_sendfile64 ] = sys_sendfile64,
++      [ __NR_futex ] = sys_ni_syscall,
++      [ __NR_sched_setaffinity ] = sys_ni_syscall,
++      [ __NR_sched_getaffinity ] = sys_ni_syscall,
++      [ __NR_set_thread_area ] = sys_ni_syscall,
++      [ __NR_get_thread_area ] = sys_ni_syscall,
++      [ __NR_io_setup ] = sys_ni_syscall,
++      [ __NR_io_destroy ] = sys_ni_syscall,
++      [ __NR_io_getevents ] = sys_ni_syscall,
++      [ __NR_io_submit ] = sys_ni_syscall,
++      [ __NR_io_cancel ] = sys_ni_syscall,
++      [ __NR_alloc_hugepages ] = sys_ni_syscall,
++      [ __NR_free_hugepages ] = sys_ni_syscall,
++      [ __NR_exit_group ] = sys_ni_syscall,
++
++      ARCH_SYSCALLS
++      [ LAST_SYSCALL + 1 ... NR_syscalls ] = 
++              (syscall_handler_t *) sys_ni_syscall
++};
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/syscall_user.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/syscall_user.c    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/syscall_user.c 2005-05-03 22:28:14.481408704 +0300
+@@ -0,0 +1,48 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <stdlib.h>
++#include <sys/time.h>
++#include "kern_util.h"
++#include "syscall_user.h"
++
++struct {
++      int syscall;
++      int pid;
++      int result;
++      struct timeval start;
++      struct timeval end;
++} syscall_record[1024];
++
++int record_syscall_start(int syscall)
++{
++      int max, index;
++
++      max = sizeof(syscall_record)/sizeof(syscall_record[0]);
++      index = next_syscall_index(max);
++
++      syscall_record[index].syscall = syscall;
++      syscall_record[index].pid = current_pid();
++      syscall_record[index].result = 0xdeadbeef;
++      gettimeofday(&syscall_record[index].start, NULL);
++      return(index);
++}
++
++void record_syscall_end(int index, int result)
++{
++      syscall_record[index].result = result;
++      gettimeofday(&syscall_record[index].end, NULL);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/sysrq.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/sysrq.c   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/sysrq.c        2005-05-03 22:28:14.482408552 +0300
+@@ -0,0 +1,98 @@
++/* 
++ * Copyright (C) 2001 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/sched.h"
++#include "linux/kernel.h"
++#include "linux/module.h"
++#include "asm/page.h"
++#include "asm/processor.h"
++#include "sysrq.h"
++#include "user_util.h"
++
++ /*
++  * If the address is either in the .text section of the
++  * kernel, or in the vmalloc'ed module regions, it *may* 
++  * be the address of a calling routine
++  */
++ 
++#ifdef CONFIG_MODULES
++
++extern struct module *module_list;
++extern struct module kernel_module;
++
++static inline int kernel_text_address(unsigned long addr)
++{
++      int retval = 0;
++      struct module *mod;
++
++      if (addr >= (unsigned long) &_stext &&
++          addr <= (unsigned long) &_etext)
++              return 1;
++
++      for (mod = module_list; mod != &kernel_module; mod = mod->next) {
++              /* mod_bound tests for addr being inside the vmalloc'ed
++               * module area. Of course it'd be better to test only
++               * for the .text subset... */
++              if (mod_bound(addr, 0, mod)) {
++                      retval = 1;
++                      break;
++              }
++      }
++
++      return retval;
++}
++
++#else
++
++static inline int kernel_text_address(unsigned long addr)
++{
++      return (addr >= (unsigned long) &_stext &&
++              addr <= (unsigned long) &_etext);
++}
++
++#endif
++
++void show_trace(unsigned long * stack)
++{
++        int i;
++        unsigned long addr;
++
++        if (!stack)
++                stack = (unsigned long*) &stack;
++
++        printk("Call Trace: ");
++        i = 1;
++        while (((long) stack & (THREAD_SIZE-1)) != 0) {
++                addr = *stack++;
++              if (kernel_text_address(addr)) {
++                      if (i && ((i % 6) == 0))
++                              printk("\n   ");
++                      printk("[<%08lx>] ", addr);
++                      i++;
++                }
++        }
++        printk("\n");
++}
++
++void show_trace_task(struct task_struct *tsk)
++{
++      unsigned long esp = PT_REGS_SP(&tsk->thread.regs);
++
++      /* User space on another CPU? */
++      if ((esp ^ (unsigned long)tsk) & (PAGE_MASK<<1))
++              return;
++      show_trace((unsigned long *)esp);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/tempfile.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/tempfile.c        1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/tempfile.c     2005-05-03 22:28:14.483408400 +0300
+@@ -0,0 +1,82 @@
++/*
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <string.h>
++#include <errno.h>
++#include <sys/param.h>
++#include "init.h"
++
++/* Modified from create_mem_file and start_debugger */
++static char *tempdir = NULL;
++
++static void __init find_tempdir(void)
++{
++      char *dirs[] = { "TMP", "TEMP", "TMPDIR", NULL };
++      int i;
++      char *dir = NULL;
++
++      if(tempdir != NULL) return;     /* We've already been called */
++      for(i = 0; dirs[i]; i++){
++              dir = getenv(dirs[i]);
++              if((dir != NULL) && (*dir != '\0'))
++                      break;
++      }
++      if((dir == NULL) || (*dir == '\0')) 
++              dir = "/tmp";
++
++      tempdir = malloc(strlen(dir) + 2);
++      if(tempdir == NULL){
++              fprintf(stderr, "Failed to malloc tempdir, "
++                      "errno = %d\n", errno);
++              return;
++      }
++      strcpy(tempdir, dir);
++      strcat(tempdir, "/");
++}
++
++int make_tempfile(const char *template, char **out_tempname, int do_unlink)
++{
++      char tempname[MAXPATHLEN];
++      int fd;
++
++      find_tempdir();
++      if (*template != '/')
++              strcpy(tempname, tempdir);
++      else
++              *tempname = 0;
++      strcat(tempname, template);
++      fd = mkstemp(tempname);
++      if(fd < 0){
++              fprintf(stderr, "open - cannot create %s: %s\n", tempname, 
++                      strerror(errno));
++              return -1;
++      }
++      if(do_unlink && (unlink(tempname) < 0)){
++              perror("unlink");
++              return -1;
++      }
++      if(out_tempname){
++              *out_tempname = strdup(tempname);
++              if(*out_tempname == NULL){
++                      perror("strdup");
++                      return -1;
++              }
++      }
++      return(fd);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/time.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/time.c    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/time.c 2005-05-03 22:28:14.484408248 +0300
+@@ -0,0 +1,144 @@
++/* 
++ * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <time.h>
++#include <sys/time.h>
++#include <signal.h>
++#include <errno.h>
++#include "user_util.h"
++#include "kern_util.h"
++#include "user.h"
++#include "process.h"
++#include "signal_user.h"
++#include "time_user.h"
++
++extern struct timeval xtime;
++
++struct timeval local_offset = { 0, 0 };
++
++void timer(void)
++{
++      gettimeofday(&xtime, NULL);
++      timeradd(&xtime, &local_offset, &xtime);
++}
++
++void set_interval(int timer_type)
++{
++      int usec = 1000000/hz();
++      struct itimerval interval = ((struct itimerval) { { 0, usec },
++                                                        { 0, usec } });
++
++      if(setitimer(timer_type, &interval, NULL) == -1)
++              panic("setitimer failed - errno = %d\n", errno);
++}
++
++void enable_timer(void)
++{
++      int usec = 1000000/hz();
++      struct itimerval enable = ((struct itimerval) { { 0, usec },
++                                                      { 0, usec }});
++      if(setitimer(ITIMER_VIRTUAL, &enable, NULL))
++              printk("enable_timer - setitimer failed, errno = %d\n",
++                     errno);
++}
++
++void disable_timer(void)
++{
++      struct itimerval disable = ((struct itimerval) { { 0, 0 }, { 0, 0 }});
++      if((setitimer(ITIMER_VIRTUAL, &disable, NULL) < 0) ||
++         (setitimer(ITIMER_REAL, &disable, NULL) < 0))
++              printk("disnable_timer - setitimer failed, errno = %d\n",
++                     errno);
++}
++
++void switch_timers(int to_real)
++{
++      struct itimerval disable = ((struct itimerval) { { 0, 0 }, { 0, 0 }});
++      struct itimerval enable = ((struct itimerval) { { 0, 1000000/hz() },
++                                                      { 0, 1000000/hz() }});
++      int old, new;
++
++      if(to_real){
++              old = ITIMER_VIRTUAL;
++              new = ITIMER_REAL;
++      }
++      else {
++              old = ITIMER_REAL;
++              new = ITIMER_VIRTUAL;
++      }
++
++      if((setitimer(old, &disable, NULL) < 0) ||
++         (setitimer(new, &enable, NULL)))
++              printk("switch_timers - setitimer failed, errno = %d\n",
++                     errno);
++}
++
++void idle_timer(void)
++{
++      if(signal(SIGVTALRM, SIG_IGN) == SIG_ERR)
++              panic("Couldn't unset SIGVTALRM handler");
++      
++      set_handler(SIGALRM, (__sighandler_t) alarm_handler, 
++                  SA_RESTART, SIGUSR1, SIGIO, SIGWINCH, SIGVTALRM, -1);
++      set_interval(ITIMER_REAL);
++}
++
++void time_init(void)
++{
++      /* XXX This is to fill xtime with something real - otherwise by the
++       * time /proc is mounted, no timers have fired, and xtime is still 0,
++       * meaning it shows times of Jan 1 1970.  The real fix is to figure
++       * out why no timers have happened by then.
++       */
++      timer();
++
++      if(signal(SIGVTALRM, boot_timer_handler) == SIG_ERR)
++              panic("Couldn't set SIGVTALRM handler");
++      set_interval(ITIMER_VIRTUAL);
++}
++
++void do_gettimeofday(struct timeval *tv)
++{
++      unsigned long flags;
++
++      flags = time_lock();
++      gettimeofday(tv, NULL);
++      timeradd(tv, &local_offset, tv);
++      time_unlock(flags);
++}
++
++void do_settimeofday(struct timeval *tv)
++{
++      struct timeval now;
++      unsigned long flags;
++
++      flags = time_lock();
++      gettimeofday(&now, NULL);
++      timersub(tv, &now, &local_offset);
++      time_unlock(flags);
++}
++
++void idle_sleep(int secs)
++{
++      struct timespec ts;
++
++      ts.tv_sec = secs;
++      ts.tv_nsec = 0;
++      nanosleep(&ts, NULL);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/time_kern.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/time_kern.c       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/time_kern.c    2005-05-03 22:28:14.485408096 +0300
+@@ -0,0 +1,209 @@
++/* 
++ * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/kernel.h"
++#include "linux/unistd.h"
++#include "linux/stddef.h"
++#include "linux/spinlock.h"
++#include "linux/sched.h"
++#include "linux/interrupt.h"
++#include "linux/init.h"
++#include "linux/delay.h"
++#include "asm/irq.h"
++#include "asm/param.h"
++#include "asm/current.h"
++#include "kern_util.h"
++#include "user_util.h"
++#include "time_user.h"
++#include "mode.h"
++#include "os.h"
++
++extern rwlock_t xtime_lock;
++
++int hz(void)
++{
++      return(HZ);
++}
++
++/* Changed at early boot */
++int timer_irq_inited = 0;
++
++/* missed_ticks will be modified after kernel memory has been 
++ * write-protected, so this puts it in a section which will be left 
++ * write-enabled.
++ */
++int __attribute__ ((__section__ (".unprotected"))) missed_ticks[NR_CPUS];
++
++static int first_tick;
++static unsigned long long prev_usecs;
++static long long delta;               /* Deviation per interval */
++
++#define MILLION 1000000
++
++void timer_irq(union uml_pt_regs *regs)
++{
++      unsigned long long ticks = 0;
++
++      if(!timer_irq_inited){
++              /* This is to ensure that ticks don't pile up when
++               * the timer handler is suspended */
++              first_tick = 0;
++              return;
++      }
++
++      if(first_tick){
++#if defined(CONFIG_UML_REAL_TIME_CLOCK)
++              /* We've had 1 tick */
++              unsigned long long usecs = os_usecs();
++
++              delta += usecs - prev_usecs;
++              prev_usecs = usecs;
++
++              /* Protect against the host clock being set backwards */
++              if(delta < 0)
++                      delta = 0;
++
++              ticks += (delta * HZ) / MILLION;
++              delta -= (ticks * MILLION) / HZ;
++#else
++              ticks = 1;
++#endif
++      }
++      else {
++              prev_usecs = os_usecs();
++              first_tick = 1;
++      }
++
++      while(ticks > 0){
++              do_IRQ(TIMER_IRQ, regs);
++              ticks--;
++      }
++}
++
++void boot_timer_handler(int sig)
++{
++      struct pt_regs regs;
++
++      CHOOSE_MODE((void) 
++                  (UPT_SC(&regs.regs) = (struct sigcontext *) (&sig + 1)),
++                  (void) (regs.regs.skas.is_user = 0));
++      do_timer(&regs);
++}
++
++void um_timer(int irq, void *dev, struct pt_regs *regs)
++{
++      do_timer(regs);
++      write_lock(&xtime_lock);
++      vxtime_lock();
++      timer();
++      vxtime_unlock();
++      write_unlock(&xtime_lock);
++}
++
++long um_time(int * tloc)
++{
++      struct timeval now;
++
++      do_gettimeofday(&now);
++      if (tloc) {
++              if (put_user(now.tv_sec,tloc))
++                      now.tv_sec = -EFAULT;
++      }
++      return now.tv_sec;
++}
++
++long um_stime(int * tptr)
++{
++      int value;
++      struct timeval new;
++
++      if (get_user(value, tptr))
++                return -EFAULT;
++      new.tv_sec = value;
++      new.tv_usec = 0;
++      do_settimeofday(&new);
++      return 0;
++}
++
++/* XXX Needs to be moved under sys-i386 */
++void __delay(um_udelay_t time)
++{
++      /* Stolen from the i386 __loop_delay */
++      int d0;
++      __asm__ __volatile__(
++              "\tjmp 1f\n"
++              ".align 16\n"
++              "1:\tjmp 2f\n"
++              ".align 16\n"
++              "2:\tdecl %0\n\tjns 2b"
++              :"=&a" (d0)
++              :"0" (time));
++}
++
++void __udelay(um_udelay_t usecs)
++{
++      int i, n;
++
++      n = (loops_per_jiffy * HZ * usecs) / MILLION;
++      for(i=0;i<n;i++) ;
++}
++
++void __const_udelay(um_udelay_t usecs)
++{
++      int i, n;
++
++      n = (loops_per_jiffy * HZ * usecs) / MILLION;
++      for(i=0;i<n;i++) ;
++}
++
++void timer_handler(int sig, union uml_pt_regs *regs)
++{
++#ifdef CONFIG_SMP
++      update_process_times(user_context(UPT_SP(regs)));
++#endif
++      if(current->processor == 0)
++              timer_irq(regs);
++}
++
++static spinlock_t timer_spinlock = SPIN_LOCK_UNLOCKED;
++
++unsigned long time_lock(void)
++{
++      unsigned long flags;
++
++      spin_lock_irqsave(&timer_spinlock, flags);
++      return(flags);
++}
++
++void time_unlock(unsigned long flags)
++{
++      spin_unlock_irqrestore(&timer_spinlock, flags);
++}
++
++int __init timer_init(void)
++{
++      int err;
++
++      CHOOSE_MODE(user_time_init_tt(), user_time_init_skas());
++      err = request_irq(TIMER_IRQ, um_timer, SA_INTERRUPT, "timer", NULL);
++      if(err != 0)
++              printk(KERN_ERR "timer_init : request_irq failed - "
++                     "errno = %d\n", -err);
++      timer_irq_inited = 1;
++      return(0);
++}
++
++__initcall(timer_init);
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/tlb.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/tlb.c     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/tlb.c  2005-05-03 22:28:14.486407944 +0300
+@@ -0,0 +1,80 @@
++/* 
++ * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/mm.h"
++#include "asm/page.h"
++#include "asm/pgalloc.h"
++#include "choose-mode.h"
++#include "mode_kern.h"
++
++void flush_tlb_page(struct vm_area_struct *vma, unsigned long address)
++{
++      address &= PAGE_MASK;
++      flush_tlb_range(vma->vm_mm, address, address + PAGE_SIZE);
++}
++
++void flush_tlb_all(void)
++{
++      flush_tlb_mm(current->mm);
++}
++
++void flush_tlb_kernel_vm(void)
++{
++      CHOOSE_MODE(flush_tlb_kernel_vm_tt(), flush_tlb_kernel_vm_skas());
++}
++
++void __flush_tlb_one(unsigned long addr)
++{
++      CHOOSE_MODE_PROC(__flush_tlb_one_tt, __flush_tlb_one_skas, addr);
++}
++
++void flush_tlb_range(struct mm_struct *mm, unsigned long start, 
++                   unsigned long end)
++{
++      CHOOSE_MODE_PROC(flush_tlb_range_tt, flush_tlb_range_skas, mm, start, 
++                       end);
++}
++
++void flush_tlb_mm(struct mm_struct *mm)
++{
++      CHOOSE_MODE_PROC(flush_tlb_mm_tt, flush_tlb_mm_skas, mm);
++}
++
++void force_flush_all(void)
++{
++      CHOOSE_MODE(force_flush_all_tt(), force_flush_all_skas());
++}
++
++
++pgd_t *pgd_offset_proc(struct mm_struct *mm, unsigned long address)
++{
++      return(pgd_offset(mm, address));
++}
++
++pmd_t *pmd_offset_proc(pgd_t *pgd, unsigned long address)
++{
++      return(pmd_offset(pgd, address));
++}
++
++pte_t *pte_offset_proc(pmd_t *pmd, unsigned long address)
++{
++      return(pte_offset(pmd, address));
++}
++
++pte_t *addr_pte(struct task_struct *task, unsigned long addr)
++{
++      return(pte_offset(pmd_offset(pgd_offset(task->mm, addr), addr), addr));
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/trap_kern.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/trap_kern.c       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/trap_kern.c    2005-05-03 22:28:14.487407792 +0300
+@@ -0,0 +1,220 @@
++/* 
++ * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/kernel.h"
++#include "linux/sched.h"
++#include "linux/mm.h"
++#include "linux/spinlock.h"
++#include "linux/config.h"
++#include "linux/init.h"
++#include "asm/semaphore.h"
++#include "asm/pgtable.h"
++#include "asm/pgalloc.h"
++#include "asm/a.out.h"
++#include "asm/current.h"
++#include "asm/irq.h"
++#include "user_util.h"
++#include "kern_util.h"
++#include "kern.h"
++#include "chan_kern.h"
++#include "mconsole_kern.h"
++#include "2_5compat.h"
++#include "mem.h"
++#include "mem_kern.h"
++
++unsigned long handle_page_fault(unsigned long address, unsigned long ip, 
++                              int is_write, int is_user, int *code_out)
++{
++      struct mm_struct *mm = current->mm;
++      struct vm_area_struct *vma;
++      pgd_t *pgd;
++      pmd_t *pmd;
++      pte_t *pte;
++      unsigned long page;
++      int handled = 0;
++
++      *code_out = SEGV_MAPERR;
++      down_read(&mm->mmap_sem);
++      vma = find_vma(mm, address);
++      if(!vma) 
++              goto out;
++      else if(vma->vm_start <= address) 
++              goto good_area;
++      else if(!(vma->vm_flags & VM_GROWSDOWN)) 
++              goto out;
++      else if(expand_stack(vma, address)) 
++              goto out;
++
++ good_area:
++      *code_out = SEGV_ACCERR;
++      if(is_write && !(vma->vm_flags & VM_WRITE)) 
++              goto out;
++      page = address & PAGE_MASK;
++      if(page == (unsigned long) current + PAGE_SIZE)
++              panic("Kernel stack overflow");
++      pgd = pgd_offset(mm, page);
++      pmd = pmd_offset(pgd, page);
++      do {
++ survive:
++              switch (handle_mm_fault(mm, vma, address, is_write)) {
++              case 1:
++                      current->min_flt++;
++                      break;
++              case 2:
++                      current->maj_flt++;
++                      break;
++              default:
++                      if (current->pid == 1) {
++                              up_read(&mm->mmap_sem);
++                              yield();
++                              down_read(&mm->mmap_sem);
++                              goto survive;
++                      }
++                      /* Fall through to bad area case */
++              case 0:
++                      goto out;
++              }
++              pte = pte_offset(pmd, page);
++      } while(!pte_present(*pte));
++      handled = 1;
++      *pte = pte_mkyoung(*pte);
++      if(pte_write(*pte)) *pte = pte_mkdirty(*pte);
++      flush_tlb_page(vma, page);
++ out:
++      up_read(&mm->mmap_sem);
++      return(handled);
++}
++
++LIST_HEAD(physmem_remappers);
++
++void register_remapper(struct remapper *info)
++{
++      list_add(&info->list, &physmem_remappers);
++}
++
++static int check_remapped_addr(unsigned long address, int is_write, int is_user)
++{
++      struct remapper *remapper;
++      struct list_head *ele;
++      __u64 offset;
++      int fd;
++
++      fd = phys_mapping(__pa(address), &offset);
++      if(fd == -1)
++              return(0);
++
++      list_for_each(ele, &physmem_remappers){
++              remapper = list_entry(ele, struct remapper, list);
++              if((*remapper->proc)(fd, address, is_write, offset, is_user))
++                      return(1);
++      }
++
++      return(0);
++}
++
++unsigned long segv(unsigned long address, unsigned long ip, int is_write, 
++                 int is_user, void *sc)
++{
++      struct siginfo si;
++      void *catcher;
++      int handled;
++
++        if(!is_user && (address >= start_vm) && (address < end_vm)){
++                flush_tlb_kernel_vm();
++                return(0);
++        }
++      else if(check_remapped_addr(address & PAGE_MASK, is_write, is_user))
++              return(0);
++      else if(current->mm == NULL)
++              panic("Segfault with no mm");
++
++      handled = handle_page_fault(address, ip, is_write, is_user, 
++                                  &si.si_code);
++
++      catcher = current->thread.fault_catcher;
++      if(handled)
++              return(0);
++      else if(catcher != NULL){
++              current->thread.fault_addr = (void *) address;
++              do_longjmp(catcher, 1);
++      } 
++      else if(current->thread.fault_addr != NULL)
++              panic("fault_addr set but no fault catcher");
++      else if(arch_fixup(ip, sc))
++              return(0);
++
++      if(!is_user) 
++              panic("Kernel mode fault at addr 0x%lx, ip 0x%lx", 
++                    address, ip);
++      si.si_signo = SIGSEGV;
++      si.si_addr = (void *) address;
++      current->thread.cr2 = address;
++      current->thread.err = is_write;
++      force_sig_info(SIGSEGV, &si, current);
++      return(0);
++}
++
++void bad_segv(unsigned long address, unsigned long ip, int is_write)
++{
++      struct siginfo si;
++
++      si.si_signo = SIGSEGV;
++      si.si_code = SEGV_ACCERR;
++      si.si_addr = (void *) address;
++      current->thread.cr2 = address;
++      current->thread.err = is_write;
++      force_sig_info(SIGSEGV, &si, current);
++}
++
++void relay_signal(int sig, union uml_pt_regs *regs)
++{
++      if(arch_handle_signal(sig, regs)) return;
++      if(!UPT_IS_USER(regs))
++              panic("Kernel mode signal %d", sig);
++      force_sig(sig, current);
++}
++
++void bus_handler(int sig, union uml_pt_regs *regs)
++{
++      if(current->thread.fault_catcher != NULL)
++              do_longjmp(current->thread.fault_catcher, 1);
++      else relay_signal(sig, regs);
++}
++
++void winch(int sig, union uml_pt_regs *regs)
++{
++      do_IRQ(WINCH_IRQ, regs);
++}
++
++void trap_init(void)
++{
++}
++
++spinlock_t trap_lock = SPIN_LOCK_UNLOCKED;
++
++static int trap_index = 0;
++
++int next_trap_index(int limit)
++{
++      int ret;
++
++      spin_lock(&trap_lock);
++      ret = trap_index;
++      if(++trap_index == limit)
++              trap_index = 0;
++      spin_unlock(&trap_lock);
++      return(ret);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/trap_user.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/trap_user.c       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/trap_user.c    2005-05-03 22:28:14.489407488 +0300
+@@ -0,0 +1,145 @@
++/* 
++ * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <stdlib.h>
++#include <errno.h>
++#include <setjmp.h>
++#include <signal.h>
++#include <sys/time.h>
++#include <sys/ptrace.h>
++#include <sys/wait.h>
++#include <asm/page.h>
++#include <asm/unistd.h>
++#include <asm/ptrace.h>
++#include "init.h"
++#include "sysdep/ptrace.h"
++#include "sigcontext.h"
++#include "sysdep/sigcontext.h"
++#include "irq_user.h"
++#include "frame_user.h"
++#include "signal_user.h"
++#include "time_user.h"
++#include "task.h"
++#include "mode.h"
++#include "choose-mode.h"
++#include "kern_util.h"
++#include "user_util.h"
++#include "os.h"
++
++void kill_child_dead(int pid)
++{
++      kill(pid, SIGKILL);
++      kill(pid, SIGCONT);
++      do {
++              int n;
++              CATCH_EINTR(n = waitpid(pid, NULL, 0));
++              if (n > 0)
++                      kill(pid, SIGCONT);
++              else
++                      break;
++      } while(1);
++}
++
++/* Unlocked - don't care if this is a bit off */
++int nsegfaults = 0;
++
++struct {
++      unsigned long address;
++      int is_write;
++      int pid;
++      unsigned long sp;
++      int is_user;
++} segfault_record[1024];
++
++void segv_handler(int sig, union uml_pt_regs *regs)
++{
++      int index, max;
++
++      if(UPT_IS_USER(regs) && !UPT_SEGV_IS_FIXABLE(regs)){
++              bad_segv(UPT_FAULT_ADDR(regs), UPT_IP(regs), 
++                       UPT_FAULT_WRITE(regs));
++              return;
++      }
++      max = sizeof(segfault_record)/sizeof(segfault_record[0]);
++      index = next_trap_index(max);
++
++      nsegfaults++;
++      segfault_record[index].address = UPT_FAULT_ADDR(regs);
++      segfault_record[index].pid = os_getpid();
++      segfault_record[index].is_write = UPT_FAULT_WRITE(regs);
++      segfault_record[index].sp = UPT_SP(regs);
++      segfault_record[index].is_user = UPT_IS_USER(regs);
++      segv(UPT_FAULT_ADDR(regs), UPT_IP(regs), UPT_FAULT_WRITE(regs),
++           UPT_IS_USER(regs), regs);
++}
++
++void usr2_handler(int sig, union uml_pt_regs *regs)
++{
++      CHOOSE_MODE(syscall_handler_tt(sig, regs), (void) 0);
++}
++
++struct signal_info sig_info[] = {
++      [ SIGTRAP ] { .handler          = relay_signal,
++                    .is_irq           = 0 },
++      [ SIGFPE ] { .handler           = relay_signal,
++                   .is_irq            = 0 },
++      [ SIGILL ] { .handler           = relay_signal,
++                   .is_irq            = 0 },
++      [ SIGWINCH ] { .handler         = winch,
++                     .is_irq          = 1 },
++      [ SIGBUS ] { .handler           = bus_handler,
++                   .is_irq            = 0 },
++      [ SIGSEGV] { .handler           = segv_handler,
++                   .is_irq            = 0 },
++      [ SIGIO ] { .handler            = sigio_handler,
++                  .is_irq             = 1 },
++      [ SIGVTALRM ] { .handler        = timer_handler,
++                      .is_irq         = 1 },
++        [ SIGALRM ] { .handler          = timer_handler,
++                      .is_irq           = 1 },
++      [ SIGUSR2 ] { .handler          = usr2_handler,
++                    .is_irq           = 0 },
++};
++
++void sig_handler(int sig, struct sigcontext sc)
++{
++      CHOOSE_MODE_PROC(sig_handler_common_tt, sig_handler_common_skas,
++                       sig, &sc);
++}
++
++extern int timer_irq_inited, missed_ticks[];
++
++void alarm_handler(int sig, struct sigcontext sc)
++{
++      if(!timer_irq_inited) return;
++      missed_ticks[cpu()]++;
++
++      if(sig == SIGALRM)
++              switch_timers(0);
++
++      CHOOSE_MODE_PROC(sig_handler_common_tt, sig_handler_common_skas,
++                       sig, &sc);
++
++      if(sig == SIGALRM)
++              switch_timers(1);
++}
++
++void do_longjmp(void *b, int val)
++{
++      sigjmp_buf *buf = b;
++
++      siglongjmp(*buf, val);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/tt/exec_kern.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/tt/exec_kern.c    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/tt/exec_kern.c 2005-05-03 22:28:14.490407336 +0300
+@@ -0,0 +1,86 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/kernel.h"
++#include "linux/mm.h"
++#include "asm/signal.h"
++#include "asm/ptrace.h"
++#include "asm/uaccess.h"
++#include "asm/pgalloc.h"
++#include "user_util.h"
++#include "kern_util.h"
++#include "irq_user.h"
++#include "time_user.h"
++#include "mem_user.h"
++#include "signal_user.h"
++#include "os.h"
++#include "tlb.h"
++#include "mode.h"
++
++static int exec_tramp(void *sig_stack)
++{
++      init_new_thread_stack(sig_stack, NULL);
++      init_new_thread_signals(1);
++      os_stop_process(os_getpid());
++      return(0);
++}
++
++void flush_thread_tt(void)
++{
++      unsigned long stack;
++      int new_pid;
++
++      stack = alloc_stack(0, 0);
++      if(stack == 0){
++              printk(KERN_ERR 
++                     "flush_thread : failed to allocate temporary stack\n");
++              do_exit(SIGKILL);
++      }
++              
++      new_pid = start_fork_tramp(current, stack, 0, exec_tramp);
++      if(new_pid < 0){
++              printk(KERN_ERR 
++                     "flush_thread : new thread failed, errno = %d\n",
++                     -new_pid);
++              do_exit(SIGKILL);
++      }
++
++      if(current->processor == 0)
++              forward_interrupts(new_pid);
++      current->thread.request.op = OP_EXEC;
++      current->thread.request.u.exec.pid = new_pid;
++      unprotect_stack((unsigned long) current);
++      os_usr1_process(os_getpid());
++      change_sig(SIGUSR1, 1);
++
++      change_sig(SIGUSR1, 0);
++      enable_timer();
++      free_page(stack);
++      protect_memory(uml_reserved, high_physmem - uml_reserved, 1, 1, 0, 1);
++      task_protections((unsigned long) current);
++      force_flush_all();
++      unblock_signals();
++}
++
++void start_thread_tt(struct pt_regs *regs, unsigned long eip, 
++                   unsigned long esp)
++{
++      set_fs(USER_DS);
++      flush_tlb_mm(current->mm);
++      PT_REGS_IP(regs) = eip;
++      PT_REGS_SP(regs) = esp;
++      PT_FIX_EXEC_STACK(esp);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/tt/exec_user.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/tt/exec_user.c    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/tt/exec_user.c 2005-05-03 22:28:14.491407184 +0300
+@@ -0,0 +1,54 @@
++/* 
++ * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <stdio.h>
++#include <unistd.h>
++#include <stdlib.h>
++#include <sched.h>
++#include <errno.h>
++#include <sys/wait.h>
++#include <sys/ptrace.h>
++#include <signal.h>
++#include "user_util.h"
++#include "kern_util.h"
++#include "user.h"
++#include "ptrace_user.h"
++
++void do_exec(int old_pid, int new_pid)
++{
++      unsigned long regs[FRAME_SIZE];
++      int err;
++
++      if((ptrace(PTRACE_ATTACH, new_pid, 0, 0) < 0) ||
++         (ptrace(PTRACE_CONT, new_pid, 0, 0) < 0))
++              tracer_panic("do_exec failed to attach proc - errno = %d",
++                           errno);
++
++      CATCH_EINTR(err = waitpid(new_pid, 0, WUNTRACED));
++      if (err < 0)
++              tracer_panic("do_exec failed to attach proc in waitpid - errno = %d",
++                           errno);
++
++      if(ptrace_getregs(old_pid, regs) < 0)
++              tracer_panic("do_exec failed to get registers - errno = %d",
++                           errno);
++
++      kill(old_pid, SIGKILL);
++
++      if(ptrace_setregs(new_pid, regs) < 0)
++              tracer_panic("do_exec failed to start new proc - errno = %d",
++                           errno);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/tt/gdb.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/tt/gdb.c  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/tt/gdb.c       2005-05-03 22:28:14.492407032 +0300
+@@ -0,0 +1,278 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <errno.h>
++#include <string.h>
++#include <signal.h>
++#include <sys/ptrace.h>
++#include <sys/types.h>
++#include "uml-config.h"
++#include "kern_constants.h"
++#include "chan_user.h"
++#include "init.h"
++#include "user.h"
++#include "debug.h"
++#include "kern_util.h"
++#include "user_util.h"
++#include "tt.h"
++#include "sysdep/thread.h"
++
++extern int debugger_pid;
++extern int debugger_fd;
++extern int debugger_parent;
++
++int detach(int pid, int sig)
++{
++      return(ptrace(PTRACE_DETACH, pid, 0, sig));
++}
++
++int attach(int pid)
++{
++      int err;
++
++      err = ptrace(PTRACE_ATTACH, pid, 0, 0);
++      if(err < 0) return(-errno);
++      else return(err);
++}
++
++int cont(int pid)
++{
++      return(ptrace(PTRACE_CONT, pid, 0, 0));
++}
++
++#ifdef UML_CONFIG_PT_PROXY
++
++int debugger_signal(int status, pid_t pid)
++{
++      return(debugger_proxy(status, pid));
++}
++
++void child_signal(pid_t pid, int status)
++{
++      child_proxy(pid, status);
++}
++
++static void gdb_announce(char *dev_name, int dev)
++{
++      printf("gdb assigned device '%s'\n", dev_name);
++}
++
++static struct chan_opts opts = {
++      .announce       = gdb_announce,
++      .xterm_title    = "UML kernel debugger",
++      .raw            = 0,
++      .tramp_stack    = 0,
++      .in_kernel      = 0,
++};
++
++/* Accessed by the tracing thread, which automatically serializes access */
++static void *xterm_data;
++static int xterm_fd;
++
++extern void *xterm_init(char *, int, struct chan_opts *);
++extern int xterm_open(int, int, int, void *, char **);
++extern void xterm_close(int, void *);
++
++int open_gdb_chan(void)
++{
++      char stack[UM_KERN_PAGE_SIZE], *dummy;
++
++      opts.tramp_stack = (unsigned long) stack;
++      xterm_data = xterm_init("", 0, &opts);
++      xterm_fd = xterm_open(1, 1, 1, xterm_data, &dummy);
++      return(xterm_fd);
++}
++
++static void exit_debugger_cb(void *unused)
++{
++      if(debugger_pid != -1){
++              if(gdb_pid != -1){
++                      fake_child_exit();
++                      gdb_pid = -1;
++              }
++              else kill_child_dead(debugger_pid);
++              debugger_pid = -1;
++              if(debugger_parent != -1)
++                      detach(debugger_parent, SIGINT);
++      }
++      if(xterm_data != NULL) xterm_close(xterm_fd, xterm_data);
++}
++
++static void exit_debugger(void)
++{
++      initial_thread_cb(exit_debugger_cb, NULL);
++}
++
++__uml_exitcall(exit_debugger);
++
++struct gdb_data {
++      char *str;
++      int err;
++};
++
++static void config_gdb_cb(void *arg)
++{
++      struct gdb_data *data = arg;
++      void *task;
++      int pid;
++
++      data->err = -1;
++      if(debugger_pid != -1) exit_debugger_cb(NULL);
++      if(!strncmp(data->str, "pid,", strlen("pid,"))){
++              data->str += strlen("pid,");
++              pid = strtoul(data->str, NULL, 0);
++              task = cpu_tasks[0].task;
++              debugger_pid = attach_debugger(TASK_EXTERN_PID(task), pid, 0);
++              if(debugger_pid != -1){
++                      data->err = 0;
++                      gdb_pid = pid;
++              }
++              return;
++      }
++      data->err = 0;
++      debugger_pid = start_debugger(linux_prog, 0, 0, &debugger_fd);
++      init_proxy(debugger_pid, 0, 0);
++}
++
++int gdb_config(char *str)
++{
++      struct gdb_data data;
++
++      if(*str++ != '=') return(-1);
++      data.str = str;
++      initial_thread_cb(config_gdb_cb, &data);
++      return(data.err);
++}
++
++void remove_gdb_cb(void *unused)
++{
++      exit_debugger_cb(NULL);
++}
++
++int gdb_remove(char *unused)
++{
++      initial_thread_cb(remove_gdb_cb, NULL);
++      return(0);
++}
++
++void signal_usr1(int sig)
++{
++      if(debugger_pid != -1){
++              printk(UM_KERN_ERR "The debugger is already running\n");
++              return;
++      }
++      debugger_pid = start_debugger(linux_prog, 0, 0, &debugger_fd);
++      init_proxy(debugger_pid, 0, 0);
++}
++
++int init_ptrace_proxy(int idle_pid, int startup, int stop)
++{
++      int pid, status;
++
++      pid = start_debugger(linux_prog, startup, stop, &debugger_fd);
++      status = wait_for_stop(idle_pid, SIGSTOP, PTRACE_CONT, NULL);
++      if(pid < 0){
++              cont(idle_pid);
++              return(-1);
++      }
++      init_proxy(pid, 1, status);
++      return(pid);
++}
++
++int attach_debugger(int idle_pid, int pid, int stop)
++{
++      int status = 0, err;
++
++      err = attach(pid);
++      if(err < 0){
++              printf("Failed to attach pid %d, errno = %d\n", pid, -err);
++              return(-1);
++      }
++      if(stop) status = wait_for_stop(idle_pid, SIGSTOP, PTRACE_CONT, NULL);
++      init_proxy(pid, 1, status);
++      return(pid);
++}
++
++#ifdef notdef /* Put this back in when it does something useful */
++static int __init uml_gdb_init_setup(char *line, int *add)
++{
++      gdb_init = uml_strdup(line);
++      return 0;
++}
++
++__uml_setup("gdb=", uml_gdb_init_setup, 
++"gdb=<channel description>\n\n"
++);
++#endif
++
++static int __init uml_gdb_pid_setup(char *line, int *add)
++{
++      gdb_pid = strtoul(line, NULL, 0);
++      *add = 0;
++      return 0;
++}
++
++__uml_setup("gdb-pid=", uml_gdb_pid_setup, 
++"gdb-pid=<pid>\n"
++"    gdb-pid is used to attach an external debugger to UML.  This may be\n"
++"    an already-running gdb or a debugger-like process like strace.\n\n"
++);
++
++#else
++
++int debugger_signal(int status, pid_t pid){ return(0); }
++void child_signal(pid_t pid, int status){ }
++int init_ptrace_proxy(int idle_pid, int startup, int stop)
++{
++      printk(UM_KERN_ERR "debug requested when CONFIG_PT_PROXY is off\n");
++      kill_child_dead(idle_pid);
++      exit(1);
++}
++
++void signal_usr1(int sig)
++{
++      printk(UM_KERN_ERR "debug requested when CONFIG_PT_PROXY is off\n");
++}
++
++int attach_debugger(int idle_pid, int pid, int stop)
++{
++      printk(UM_KERN_ERR "attach_debugger called when CONFIG_PT_PROXY "
++             "is off\n");
++      return(-1);
++}
++
++int config_gdb(char *str)
++{
++      return(-1);
++}
++
++int remove_gdb(void)
++{
++      return(-1);
++}
++
++int init_parent_proxy(int pid)
++{
++      return(-1);
++}
++
++void debugger_parent_signal(int status, int pid)
++{
++}
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/tt/gdb_kern.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/tt/gdb_kern.c     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/tt/gdb_kern.c  2005-05-03 22:28:14.493406880 +0300
+@@ -0,0 +1,40 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/init.h"
++#include "linux/config.h"
++#include "mconsole_kern.h"
++
++#ifdef CONFIG_MCONSOLE
++
++extern int gdb_config(char *str);
++extern int gdb_remove(char *unused);
++
++static struct mc_device gdb_mc = {
++      .name           = "gdb",
++      .config         = gdb_config,
++      .remove         = gdb_remove,
++};
++
++int gdb_mc_init(void)
++{
++      mconsole_register_dev(&gdb_mc);
++      return(0);
++}
++
++__initcall(gdb_mc_init);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/tt/include/debug.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/tt/include/debug.h        1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/tt/include/debug.h     2005-05-03 22:28:14.494406728 +0300
+@@ -0,0 +1,29 @@
++/* 
++ * Copyright (C) 2000, 2001, 2002  Jeff Dike (jdike@karaya.com) and
++ * Lars Brinkhoff.
++ * Licensed under the GPL
++ */
++
++#ifndef __DEBUG_H
++#define __DEBUG_H
++
++extern int debugger_proxy(int status, pid_t pid);
++extern void child_proxy(pid_t pid, int status);
++extern void init_proxy (pid_t pid, int waiting, int status);
++extern int start_debugger(char *prog, int startup, int stop, int *debugger_fd);
++extern void fake_child_exit(void);
++extern int gdb_config(char *str);
++extern int gdb_remove(char *unused);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/tt/include/mmu.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/tt/include/mmu.h  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/tt/include/mmu.h       2005-05-03 22:28:14.495406576 +0300
+@@ -0,0 +1,23 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __TT_MMU_H
++#define __TT_MMU_H
++
++struct mmu_context_tt {
++};
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/tt/include/mode.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/tt/include/mode.h 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/tt/include/mode.h      2005-05-03 22:28:14.496406424 +0300
+@@ -0,0 +1,38 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __MODE_TT_H__
++#define __MODE_TT_H__
++
++#include "sysdep/ptrace.h"
++
++enum { OP_NONE, OP_EXEC, OP_FORK, OP_TRACE_ON, OP_REBOOT, OP_HALT, OP_CB };
++
++extern int tracing_pid;
++
++extern int tracer(int (*init_proc)(void *), void *sp);
++extern void user_time_init_tt(void);
++extern int copy_sc_from_user_tt(void *to_ptr, void *from_ptr, void *data);
++extern int copy_sc_to_user_tt(void *to_ptr, void *fp, void *from_ptr, 
++                            void *data);
++extern void sig_handler_common_tt(int sig, void *sc);
++extern void syscall_handler_tt(int sig, union uml_pt_regs *regs);
++extern void reboot_tt(void);
++extern void halt_tt(void);
++extern int is_tracer_winch(int pid, int fd, void *data);
++extern void kill_off_processes_tt(void);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/tt/include/mode_kern.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/tt/include/mode_kern.h    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/tt/include/mode_kern.h 2005-05-03 22:28:14.496406424 +0300
+@@ -0,0 +1,52 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __TT_MODE_KERN_H__
++#define __TT_MODE_KERN_H__
++
++#include "linux/sched.h"
++#include "asm/page.h"
++#include "asm/ptrace.h"
++#include "asm/uaccess.h"
++
++extern void *_switch_to_tt(void *prev, void *next);
++extern void flush_thread_tt(void);
++extern void start_thread_tt(struct pt_regs *regs, unsigned long eip, 
++                         unsigned long esp);
++extern int copy_thread_tt(int nr, unsigned long clone_flags, unsigned long sp,
++                        unsigned long stack_top, struct task_struct *p, 
++                        struct pt_regs *regs);
++extern void release_thread_tt(struct task_struct *task);
++extern void exit_thread_tt(void);
++extern void initial_thread_cb_tt(void (*proc)(void *), void *arg);
++extern void init_idle_tt(void);
++extern void flush_tlb_kernel_vm_tt(void);
++extern void __flush_tlb_one_tt(unsigned long addr);
++extern void flush_tlb_range_tt(struct mm_struct *mm, unsigned long start, 
++                             unsigned long end);
++extern void flush_tlb_mm_tt(struct mm_struct *mm);
++extern void force_flush_all_tt(void);
++extern long execute_syscall_tt(void *r);
++extern void before_mem_tt(unsigned long brk_start);
++extern unsigned long set_task_sizes_tt(int arg, unsigned long *host_size_out, 
++                                     unsigned long *task_size_out);
++extern int start_uml_tt(void);
++extern int external_pid_tt(struct task_struct *task);
++extern int thread_pid_tt(struct thread_struct *thread);
++
++#define kmem_end_tt (host_task_size - ABOVE_KMEM)
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/tt/include/ptrace-tt.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/tt/include/ptrace-tt.h    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/tt/include/ptrace-tt.h 2005-05-03 22:28:14.497406272 +0300
+@@ -0,0 +1,26 @@
++/* 
++ * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __PTRACE_TT_H
++#define __PTRACE_TT_H
++
++#include "uml-config.h"
++
++#ifdef UML_CONFIG_MODE_TT
++#include "sysdep/sc.h"
++#endif
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/tt/include/tt.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/tt/include/tt.h   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/tt/include/tt.h        2005-05-03 22:28:14.498406120 +0300
+@@ -0,0 +1,44 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __TT_H__
++#define __TT_H__
++
++#include "sysdep/ptrace.h"
++
++extern int gdb_pid;
++extern int debug;
++extern int debug_stop;
++extern int debug_trace;
++
++extern int honeypot;
++
++extern int fork_tramp(void *sig_stack);
++extern int do_proc_op(void *t, int proc_id);
++extern int tracer(int (*init_proc)(void *), void *sp);
++extern void attach_process(int pid);
++extern void tracer_panic(char *format, ...);
++extern void set_init_pid(int pid);
++extern int set_user_mode(void *task);
++extern void set_tracing(void *t, int tracing);
++extern int is_tracing(void *task);
++extern void syscall_handler(int sig, union uml_pt_regs *regs);
++extern void exit_kernel(int pid, void *task);
++extern int do_syscall(void *task, int pid);
++extern int is_valid_pid(int pid);
++extern void remap_data(void *segment_start, void *segment_end, int w);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/tt/include/uaccess.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/tt/include/uaccess.h      1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/tt/include/uaccess.h   2005-05-03 22:28:14.499405968 +0300
+@@ -0,0 +1,71 @@
++/* 
++ * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __TT_UACCESS_H
++#define __TT_UACCESS_H
++
++#include "linux/string.h"
++#include "linux/sched.h"
++#include "asm/processor.h"
++#include "asm/errno.h"
++#include "asm/current.h"
++#include "asm/a.out.h"
++#include "uml_uaccess.h"
++
++#define ABOVE_KMEM (16 * 1024 * 1024)
++
++extern unsigned long end_vm;
++extern unsigned long uml_physmem;
++
++#define under_task_size(addr, size) \
++      (((unsigned long) (addr) < TASK_SIZE) && \
++         (((unsigned long) (addr) + (size)) < TASK_SIZE))
++
++#define is_stack(addr, size) \
++      (((unsigned long) (addr) < STACK_TOP) && \
++       ((unsigned long) (addr) >= STACK_TOP - ABOVE_KMEM) && \
++       (((unsigned long) (addr) + (size)) <= STACK_TOP))
++
++#define access_ok_tt(type, addr, size) \
++      ((type == VERIFY_READ) || (segment_eq(get_fs(), KERNEL_DS)) || \
++         (((unsigned long) (addr) <= ((unsigned long) (addr) + (size))) && \
++          (under_task_size(addr, size) || is_stack(addr, size))))
++
++static inline int verify_area_tt(int type, const void * addr, 
++                               unsigned long size)
++{
++      return(access_ok_tt(type, addr, size) ? 0 : -EFAULT);
++}
++
++extern unsigned long get_fault_addr(void);
++
++extern int __do_copy_from_user(void *to, const void *from, int n,
++                             void **fault_addr, void **fault_catcher);
++extern int __do_strncpy_from_user(char *dst, const char *src, size_t n,
++                                void **fault_addr, void **fault_catcher);
++extern int __do_clear_user(void *mem, size_t len, void **fault_addr,
++                         void **fault_catcher);
++extern int __do_strnlen_user(const char *str, unsigned long n,
++                           void **fault_addr, void **fault_catcher);
++
++extern int copy_from_user_tt(void *to, const void *from, int n);
++extern int copy_to_user_tt(void *to, const void *from, int n);
++extern int strncpy_from_user_tt(char *dst, const char *src, int count);
++extern int __clear_user_tt(void *mem, int len);
++extern int clear_user_tt(void *mem, int len);
++extern int strnlen_user_tt(const void *str, int len);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/tt/ksyms.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/tt/ksyms.c        1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/tt/ksyms.c     2005-05-03 22:28:14.500405816 +0300
+@@ -0,0 +1,28 @@
++/* 
++ * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/module.h"
++#include "asm/uaccess.h"
++#include "mode.h"
++
++EXPORT_SYMBOL(__do_copy_from_user);
++EXPORT_SYMBOL(__do_copy_to_user);
++EXPORT_SYMBOL(__do_strncpy_from_user);
++EXPORT_SYMBOL(__do_strnlen_user); 
++EXPORT_SYMBOL(__do_clear_user);
++
++EXPORT_SYMBOL(tracing_pid);
++EXPORT_SYMBOL(honeypot);
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/tt/Makefile
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/tt/Makefile       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/tt/Makefile    2005-05-03 22:28:14.501405664 +0300
+@@ -0,0 +1,39 @@
++# 
++# Copyright (C) 2002 - 2003 Jeff Dike (jdike@addtoit.com)
++# Licensed under the GPL
++#
++
++O_TARGET = tt.o
++
++obj-y = exec_kern.o exec_user.o gdb.o ksyms.o mem.o mem_user.o process_kern.o \
++      syscall_kern.o syscall_user.o time.o tlb.o tracer.o trap_user.o \
++      uaccess.o uaccess_user.o
++
++obj-$(CONFIG_PT_PROXY) += gdb_kern.o 
++
++subdir-y = sys-$(SUBARCH)
++subdir-$(CONFIG_PT_PROXY) += ptproxy
++
++obj-y += $(join $(subdir-y),$(subdir-y:%=/%.o))
++
++export-objs = ksyms.o
++
++USER_OBJS = $(filter %_user.o,$(obj-y)) gdb.o time.o tracer.o
++
++UNMAP_CFLAGS := $(patsubst -pg -DPROFILING,,$(USER_CFLAGS))
++UNMAP_CFLAGS := $(patsubst -fprofile-arcs -ftest-coverage,,$(UNMAP_CFLAGS))
++
++include $(TOPDIR)/Rules.make
++
++$(USER_OBJS) : %.o: %.c
++      $(CC) $(CFLAGS_$@) $(USER_CFLAGS) -c -o $@ $<
++
++$(O_TARGET) : unmap_fin.o
++
++unmap.o: unmap.c
++      $(CC) $(UNMAP_CFLAGS) -c -o $@ $<
++
++unmap_fin.o : unmap.o
++      ld -r -o $@ $< -lc -L/usr/lib
++
++clean :
+Index: linux-2.4.29/arch/um/kernel/tt/mem.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/tt/mem.c  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/tt/mem.c       2005-05-03 22:28:14.502405512 +0300
+@@ -0,0 +1,51 @@
++/* 
++ * Copyright (C) 2002 - 2004 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/stddef.h"
++#include "linux/config.h"
++#include "linux/mm.h"
++#include "asm/uaccess.h"
++#include "mem_user.h"
++#include "kern_util.h"
++#include "user_util.h"
++#include "kern.h"
++#include "tt.h"
++
++void before_mem_tt(unsigned long brk_start)
++{
++      if(!jail || debug)
++              remap_data(UML_ROUND_DOWN(&_stext), UML_ROUND_UP(&_etext), 1);
++      remap_data(UML_ROUND_DOWN(&_sdata), UML_ROUND_UP(&_edata), 1);
++      remap_data(UML_ROUND_DOWN(&__bss_start), UML_ROUND_UP(&_end), 1);
++}
++
++#ifdef CONFIG_HOST_2G_2G
++#define TOP 0x80000000
++#else
++#define TOP 0xc0000000
++#endif
++
++#define SIZE ((CONFIG_NEST_LEVEL + CONFIG_KERNEL_HALF_GIGS) * 0x20000000)
++#define START (TOP - SIZE)
++
++unsigned long set_task_sizes_tt(int arg, unsigned long *host_size_out, 
++                              unsigned long *task_size_out)
++{
++      /* Round up to the nearest 4M */
++      *host_size_out = ROUND_4M((unsigned long) &arg);
++      *task_size_out = START;
++      return(START);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/tt/mem_user.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/tt/mem_user.c     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/tt/mem_user.c  2005-05-03 22:28:14.502405512 +0300
+@@ -0,0 +1,49 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <stdlib.h>
++#include <stdio.h>
++#include <unistd.h>
++#include <string.h>
++#include <errno.h>
++#include <sys/mman.h>
++#include "tt.h"
++#include "mem_user.h"
++#include "user_util.h"
++
++void remap_data(void *segment_start, void *segment_end, int w)
++{
++      void *addr;
++      unsigned long size;
++      int data, prot;
++
++      if(w) prot = PROT_WRITE;
++      else prot = 0;
++      prot |= PROT_READ | PROT_EXEC;
++      size = (unsigned long) segment_end - 
++              (unsigned long) segment_start;
++      data = create_mem_file(size);
++      addr = mmap(NULL, size, PROT_WRITE | PROT_READ, MAP_SHARED, data, 0);
++      if(addr == MAP_FAILED){
++              perror("mapping new data segment");
++              exit(1);
++      }
++      memcpy(addr, segment_start, size);
++      if(switcheroo(data, prot, addr, segment_start, size) < 0){
++              printf("switcheroo failed\n");
++              exit(1);
++      }
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/tt/process_kern.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/tt/process_kern.c 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/tt/process_kern.c      2005-05-03 22:28:14.526401864 +0300
+@@ -0,0 +1,615 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/sched.h"
++#include "linux/signal.h"
++#include "linux/kernel.h"
++#include "linux/slab.h"
++#include "asm/system.h"
++#include "asm/pgalloc.h"
++#include "asm/ptrace.h"
++#include "irq_user.h"
++#include "signal_user.h"
++#include "kern_util.h"
++#include "user_util.h"
++#include "os.h"
++#include "kern.h"
++#include "sigcontext.h"
++#include "time_user.h"
++#include "mem_user.h"
++#include "tlb.h"
++#include "mode.h"
++#include "init.h"
++#include "tt.h"
++#include "filehandle.h"
++
++void *_switch_to_tt(void *prev, void *next)
++{
++      struct task_struct *from, *to, *prev_sched;
++      struct file_handle *pipe;
++      unsigned long flags;
++      int err, vtalrm, alrm, prof, cpu;
++      char c;
++      /* jailing and SMP are incompatible, so this doesn't need to be 
++       * made per-cpu 
++       */
++      static int reading;
++
++      from = prev;
++      to = next;
++
++      to->thread.prev_sched = from;
++
++      cpu = from->processor;
++      if(cpu == 0)
++              forward_interrupts(to->thread.mode.tt.extern_pid);
++#ifdef CONFIG_SMP
++      forward_ipi(cpu_data[cpu].ipi_pipe[0], to->thread.mode.tt.extern_pid);
++#endif
++      local_irq_save(flags);
++
++      vtalrm = change_sig(SIGVTALRM, 0);
++      alrm = change_sig(SIGALRM, 0);
++      prof = change_sig(SIGPROF, 0);
++
++      c = 0;
++      set_current(to);
++
++      reading = 0;
++      pipe = to->thread.mode.tt.switch_pipe;
++      err = write_file(&pipe[1], -1, &c, sizeof(c));
++      if(err != sizeof(c))
++              panic("write of switch_pipe failed, err = %d", -err);
++
++      reading = 1;
++      if(from->state == TASK_ZOMBIE)
++              os_kill_process(os_getpid(), 0);
++
++      pipe = from->thread.mode.tt.switch_pipe;
++      err = read_file(&pipe[0], -1, &c, sizeof(c));
++      if(err != sizeof(c))
++              panic("read of switch_pipe failed, errno = %d", -err);
++
++      /* If the process that we have just scheduled away from has exited,
++       * then it needs to be killed here.  The reason is that, even though
++       * it will kill itself when it next runs, that may be too late.  Its
++       * stack will be freed, possibly before then, and if that happens,
++       * we have a use-after-free situation.  So, it gets killed here
++       * in case it has not already killed itself.
++       */
++      prev_sched = current->thread.prev_sched;
++      if(prev_sched->state == TASK_ZOMBIE)
++              os_kill_process(prev_sched->thread.mode.tt.extern_pid, 1);
++
++      /* This works around a nasty race with 'jail'.  If we are switching
++       * between two threads of a threaded app and the incoming process 
++       * runs before the outgoing process reaches the read, and it makes
++       * it all the way out to userspace, then it will have write-protected 
++       * the outgoing process stack.  Then, when the outgoing process 
++       * returns from the write, it will segfault because it can no longer
++       * write its own stack.  So, in order to avoid that, the incoming 
++       * thread sits in a loop yielding until 'reading' is set.  This 
++       * isn't entirely safe, since there may be a reschedule from a timer
++       * happening between setting 'reading' and sleeping in read.  But,
++       * it should get a whole quantum in which to reach the read and sleep,
++       * which should be enough.
++       */
++
++      if(jail){
++              while(!reading) sched_yield();
++      }
++
++      change_sig(SIGVTALRM, vtalrm);
++      change_sig(SIGALRM, alrm);
++      change_sig(SIGPROF, prof);
++
++      arch_switch();
++
++      flush_tlb_all();
++      local_irq_restore(flags);
++
++      return(current->thread.prev_sched);
++}
++
++void release_thread_tt(struct task_struct *task)
++{
++      os_kill_process(task->thread.mode.tt.extern_pid, 0);
++}
++
++void exit_thread_tt(void)
++{
++      struct file_handle *pipe = current->thread.mode.tt.switch_pipe;
++
++      close_file(&pipe[0]);
++      close_file(&pipe[1]);
++      kfree(pipe);
++}
++
++static void suspend_new_thread(struct file_handle *fh)
++{
++      char c;
++
++      os_stop_process(os_getpid());
++
++      if(read_file(fh, -1, &c, sizeof(c)) != sizeof(c))
++              panic("read failed in suspend_new_thread");
++}
++
++extern void schedule_tail(struct task_struct *prev);
++
++static void new_thread_handler(int sig)
++{
++      struct file_handle *pipe;
++      unsigned long disable;
++      int (*fn)(void *);
++      void *arg;
++
++      fn = current->thread.request.u.thread.proc;
++      arg = current->thread.request.u.thread.arg;
++
++      UPT_SC(&current->thread.regs.regs) = (void *) (&sig + 1);
++      disable = (1 << (SIGVTALRM - 1)) | (1 << (SIGALRM - 1)) |
++              (1 << (SIGIO - 1)) | (1 << (SIGPROF - 1));
++      SC_SIGMASK(UPT_SC(&current->thread.regs.regs)) &= ~disable;
++
++      pipe = current->thread.mode.tt.switch_pipe;
++      suspend_new_thread(&pipe[0]);
++
++      init_new_thread_signals(1);
++      enable_timer();
++      free_page(current->thread.temp_stack);
++      set_cmdline("(kernel thread)");
++      force_flush_all();
++
++      if(current->thread.prev_sched != NULL)
++              schedule_tail(current->thread.prev_sched);
++      current->thread.prev_sched = NULL;
++
++      change_sig(SIGUSR1, 1);
++      change_sig(SIGVTALRM, 1);
++      change_sig(SIGPROF, 1);
++      sti();
++      if(!run_kernel_thread(fn, arg, &current->thread.exec_buf))
++              do_exit(0);
++      
++      /* XXX No set_user_mode here because a newly execed process will
++       * immediately segfault on its non-existent IP, coming straight back
++       * to the signal handler, which will call set_user_mode on its way
++       * out.  This should probably change since it's confusing.
++       */
++}
++
++static int new_thread_proc(void *stack)
++{
++      /* cli is needed to block out signals until this thread is properly
++       * scheduled.  Otherwise, the tracing thread will get mighty upset 
++       * about any signals that arrive before that.  
++       * This has the complication that it sets the saved signal mask in
++       * the sigcontext to block signals.  This gets restored when this
++       * thread (or a descendant, since they get a copy of this sigcontext)
++       * returns to userspace.
++       * So, this is compensated for elsewhere.
++       * XXX There is still a small window until cli() actually finishes
++       * where signals are possible - shouldn't be a problem in practice 
++       * since SIGIO hasn't been forwarded here yet, and the cli should 
++       * finish before a SIGVTALRM has time to be delivered.
++       */
++      cli();
++      init_new_thread_stack(stack, new_thread_handler);
++      os_usr1_process(os_getpid());
++      change_sig(SIGUSR1, 1);
++      return(0);
++}
++
++/* Signal masking - signals are blocked at the start of fork_tramp.  They
++ * are re-enabled when finish_fork_handler is entered by fork_tramp hitting
++ * itself with a SIGUSR1.  set_user_mode has to be run with SIGUSR1 off,
++ * so it is blocked before it's called.  They are re-enabled on sigreturn
++ * despite the fact that they were blocked when the SIGUSR1 was issued because
++ * copy_thread copies the parent's sigcontext, including the signal mask
++ * onto the signal frame.
++ */
++
++static void finish_fork_handler(int sig)
++{
++      struct file_handle *pipe = current->thread.mode.tt.switch_pipe;
++
++      UPT_SC(&current->thread.regs.regs) = (void *) (&sig + 1);
++      suspend_new_thread(&pipe[0]);
++      
++      init_new_thread_signals(1);
++      enable_timer();
++      sti();
++      force_flush_all();
++      if(current->mm != current->p_pptr->mm)
++              protect_memory(uml_reserved, high_physmem - uml_reserved, 1, 
++                             1, 0, 1);
++      task_protections((unsigned long) current);
++
++      if(current->thread.prev_sched != NULL)
++              schedule_tail(current->thread.prev_sched);
++      current->thread.prev_sched = NULL;
++
++      free_page(current->thread.temp_stack);
++      cli();
++      change_sig(SIGUSR1, 0);
++      set_user_mode(current);
++}
++
++int fork_tramp(void *stack)
++{
++      cli();
++      arch_init_thread();
++      init_new_thread_stack(stack, finish_fork_handler);
++      os_usr1_process(os_getpid());
++      change_sig(SIGUSR1, 1);
++      return(0);
++}
++
++struct file_handle *make_switch_pipe(void)
++{
++      struct file_handle *pipe;
++      int err;
++
++      pipe = kmalloc(sizeof(struct file_handle [2]), GFP_KERNEL);
++      if(pipe == NULL){
++              pipe = ERR_PTR(-ENOMEM);
++              goto out;
++      }
++
++      err = make_pipe(pipe);
++      if(err)
++              goto out_free;
++
++ out:
++      return(pipe);
++
++ out_free:
++      kfree(pipe);
++      pipe = ERR_PTR(err);
++      goto out;
++}
++
++int copy_thread_tt(int nr, unsigned long clone_flags, unsigned long sp,
++                 unsigned long stack_top, struct task_struct * p, 
++                 struct pt_regs *regs)
++{
++      int (*tramp)(void *);
++      int new_pid, err;
++      unsigned long stack;
++      
++      if(current->thread.forking)
++              tramp = fork_tramp;
++      else {
++              tramp = new_thread_proc;
++              p->thread.request.u.thread = current->thread.request.u.thread;
++      }
++
++      p->thread.mode.tt.switch_pipe = make_switch_pipe();
++      if(IS_ERR(p->thread.mode.tt.switch_pipe)){
++              err = PTR_ERR(p->thread.mode.tt.switch_pipe);
++              goto out;
++      }
++
++      stack = alloc_stack(0, 0);
++      if(stack == 0){
++              printk(KERN_ERR "copy_thread : failed to allocate "
++                     "temporary stack\n");
++              err = -ENOMEM;
++              goto out_close;
++      }
++
++      clone_flags &= CLONE_VM;
++      p->thread.temp_stack = stack;
++      new_pid = start_fork_tramp(p, stack, clone_flags, tramp);
++      if(new_pid < 0){
++              printk(KERN_ERR "copy_thread : clone failed - errno = %d\n", 
++                     -new_pid);
++              err = new_pid;
++              goto out_stack;
++      }
++
++      if(current->thread.forking){
++              sc_to_sc(UPT_SC(&p->thread.regs.regs), 
++                       UPT_SC(&current->thread.regs.regs));
++              SC_SET_SYSCALL_RETURN(UPT_SC(&p->thread.regs.regs), 0);
++              if(sp != 0) SC_SP(UPT_SC(&p->thread.regs.regs)) = sp;
++      }
++      p->thread.mode.tt.extern_pid = new_pid;
++
++      current->thread.request.op = OP_FORK;
++      current->thread.request.u.fork.pid = new_pid;
++      os_usr1_process(os_getpid());
++
++      /* Enable the signal and then disable it to ensure that it is handled
++       * here, and nowhere else.
++       */
++      change_sig(SIGUSR1, 1);
++
++      change_sig(SIGUSR1, 0);
++      err = 0;
++
++ out:
++      return(err);
++
++ out_stack:
++      free_stack(stack, 0);
++ out_close:
++      close_file(&((struct file_handle *) p->thread.mode.tt.switch_pipe)[0]);
++      close_file(&((struct file_handle *) p->thread.mode.tt.switch_pipe)[1]);
++      kfree(p->thread.mode.tt.switch_pipe);
++      goto out;
++}
++
++void reboot_tt(void)
++{
++      current->thread.request.op = OP_REBOOT;
++      os_usr1_process(os_getpid());
++      change_sig(SIGUSR1, 1);
++}
++
++void halt_tt(void)
++{
++      current->thread.request.op = OP_HALT;
++      os_usr1_process(os_getpid());
++      change_sig(SIGUSR1, 1);
++}
++
++void kill_off_processes_tt(void)
++{
++      struct task_struct *p;
++      int me;
++
++      me = os_getpid();
++      for_each_task(p){
++              int pid = p->thread.mode.tt.extern_pid;
++              if((pid != me) && (pid != -1))
++                      os_kill_process(p->thread.mode.tt.extern_pid, 0);
++      }
++      if((init_task.thread.mode.tt.extern_pid != me) &&
++         (init_task.thread.mode.tt.extern_pid != -1))
++              os_kill_process(init_task.thread.mode.tt.extern_pid, 0);
++}
++
++void initial_thread_cb_tt(void (*proc)(void *), void *arg)
++{
++      if(os_getpid() == tracing_pid){
++              (*proc)(arg);
++      }
++      else {
++              current->thread.request.op = OP_CB;
++              current->thread.request.u.cb.proc = proc;
++              current->thread.request.u.cb.arg = arg;
++              os_usr1_process(os_getpid());
++              change_sig(SIGUSR1, 1);
++
++              change_sig(SIGUSR1, 0);
++      }
++}
++
++int do_proc_op(void *t, int proc_id)
++{
++      struct task_struct *task;
++      struct thread_struct *thread;
++      int op, pid;
++
++      task = t;
++      thread = &task->thread;
++      op = thread->request.op;
++      switch(op){
++      case OP_NONE:
++      case OP_TRACE_ON:
++              break;
++      case OP_EXEC:
++              pid = thread->request.u.exec.pid;
++              do_exec(thread->mode.tt.extern_pid, pid);
++              thread->mode.tt.extern_pid = pid;
++              cpu_tasks[task->processor].pid = pid;
++              break;
++      case OP_FORK:
++              attach_process(thread->request.u.fork.pid);
++              break;
++      case OP_CB:
++              (*thread->request.u.cb.proc)(thread->request.u.cb.arg);
++              break;
++      case OP_REBOOT:
++      case OP_HALT:
++              break;
++      default:
++              tracer_panic("Bad op in do_proc_op");
++              break;
++      }
++      thread->request.op = OP_NONE;
++      return(op);
++}
++
++void init_idle_tt(void)
++{
++      idle_timer();
++}
++
++/* Changed by jail_setup, which is a setup */
++int jail = 0;
++
++int __init jail_setup(char *line, int *add)
++{
++      int ok = 1;
++
++      if(jail) return(0);
++#ifdef CONFIG_SMP
++      printf("'jail' may not used used in a kernel with CONFIG_SMP "
++             "enabled\n");
++      ok = 0;
++#endif
++#ifdef CONFIG_HOSTFS
++      printf("'jail' may not used used in a kernel with CONFIG_HOSTFS "
++             "enabled\n");
++      ok = 0;
++#endif
++#ifdef CONFIG_MODULES
++      printf("'jail' may not used used in a kernel with CONFIG_MODULES "
++             "enabled\n");
++      ok = 0;
++#endif        
++      if(!ok) exit(1);
++
++      /* CAP_SYS_RAWIO controls the ability to open /dev/mem and /dev/kmem.
++       * Removing it from the bounding set eliminates the ability of anything
++       * to acquire it, and thus read or write kernel memory.
++       */
++      cap_lower(cap_bset, CAP_SYS_RAWIO);
++      jail = 1;
++      return(0);
++}
++
++__uml_setup("jail", jail_setup,
++"jail\n"
++"    Enables the protection of kernel memory from processes.\n\n"
++);
++
++static void mprotect_kernel_mem(int w)
++{
++      unsigned long start, end;
++      int pages;
++
++      if(!jail || (current == &init_task)) return;
++
++      pages = (1 << CONFIG_KERNEL_STACK_ORDER);
++
++      start = (unsigned long) current + PAGE_SIZE;
++      end = (unsigned long) current + PAGE_SIZE * pages;
++      protect_memory(uml_reserved, start - uml_reserved, 1, w, 1, 1);
++      protect_memory(end, high_physmem - end, 1, w, 1, 1);
++
++      start = (unsigned long) UML_ROUND_DOWN(&_stext);
++      end = (unsigned long) UML_ROUND_UP(&_etext);
++      protect_memory(start, end - start, 1, w, 1, 1);
++
++      start = (unsigned long) UML_ROUND_DOWN(&_unprotected_end);
++      end = (unsigned long) UML_ROUND_UP(&_edata);
++      protect_memory(start, end - start, 1, w, 1, 1);
++
++      start = (unsigned long) UML_ROUND_DOWN(&__bss_start);
++      end = (unsigned long) UML_ROUND_UP(&_end);
++      protect_memory(start, end - start, 1, w, 1, 1);
++
++      mprotect_kernel_vm(w);
++}
++
++void unprotect_kernel_mem(void)
++{
++      mprotect_kernel_mem(1);
++}
++
++void protect_kernel_mem(void)
++{
++      mprotect_kernel_mem(0);
++}
++
++extern void start_kernel(void);
++
++static int start_kernel_proc(void *unused)
++{
++      int pid;
++
++      block_signals();
++      pid = os_getpid();
++
++      cpu_tasks[0].pid = pid;
++      cpu_tasks[0].task = current;
++#ifdef CONFIG_SMP
++      cpu_online_map = 1;
++#endif
++      if(debug) os_stop_process(pid);
++      start_kernel();
++      return(0);
++}
++
++void set_tracing(void *task, int tracing)
++{
++      ((struct task_struct *) task)->thread.mode.tt.tracing = tracing;
++}
++
++int is_tracing(void *t)
++{
++      return (((struct task_struct *) t)->thread.mode.tt.tracing);
++}
++
++int set_user_mode(void *t)
++{
++      struct task_struct *task;
++
++      task = t ? t : current;
++      if(task->thread.mode.tt.tracing) 
++              return(1);
++      task->thread.request.op = OP_TRACE_ON;
++      os_usr1_process(os_getpid());
++      return(0);
++}
++
++/* This is static rather than kmalloced because this happens before kmalloc
++ * is initialized.  Also, it is always needed, so might as well be static on
++ * this ground.
++ */
++static struct file_handle init_switch_pipe[2];
++
++void set_init_pid(int pid)
++{
++      int err;
++
++      init_task.thread.mode.tt.extern_pid = pid;
++
++      err = make_pipe(init_switch_pipe);
++      if(err)
++              panic("set_init_pid - make_pipe failed, errno = %d", err);
++      init_task.thread.mode.tt.switch_pipe = init_switch_pipe;
++}
++
++int start_uml_tt(void)
++{
++      void *sp;
++      int pages;
++
++      pages = (1 << CONFIG_KERNEL_STACK_ORDER);
++      sp = (void *) ((unsigned long) &init_task) + pages * PAGE_SIZE - 
++              sizeof(unsigned long);
++      return(tracer(start_kernel_proc, sp));
++}
++
++int external_pid_tt(struct task_struct *task)
++{
++      return(task->thread.mode.tt.extern_pid);
++}
++
++int thread_pid_tt(struct thread_struct *thread)
++{
++      return(thread->mode.tt.extern_pid);
++}
++
++int is_valid_pid(int pid)
++{
++      struct task_struct *task;
++
++        read_lock(&tasklist_lock);
++        for_each_task(task){
++                if(task->thread.mode.tt.extern_pid == pid){
++                      read_unlock(&tasklist_lock);
++                      return(1);
++                }
++        }
++      read_unlock(&tasklist_lock);
++      return(0);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/tt/ptproxy/Makefile
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/tt/ptproxy/Makefile       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/tt/ptproxy/Makefile    2005-05-03 22:28:14.526401864 +0300
+@@ -0,0 +1,12 @@
++O_TARGET = ptproxy.o
++
++obj-y = proxy.o ptrace.o sysdep.o wait.o
++
++USER_OBJS = $(obj-y)
++
++include $(TOPDIR)/Rules.make
++
++$(USER_OBJS) : %.o: %.c
++      $(CC) $(CFLAGS_$@) $(USER_CFLAGS) -c -o $@ $<
++
++clean:
+Index: linux-2.4.29/arch/um/kernel/tt/ptproxy/proxy.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/tt/ptproxy/proxy.c        1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/tt/ptproxy/proxy.c     2005-05-03 22:28:14.529401408 +0300
+@@ -0,0 +1,371 @@
++/**********************************************************************
++proxy.c
++
++Copyright (C) 1999 Lars Brinkhoff.  See the file COPYING for licensing
++terms and conditions.
++
++Jeff Dike (jdike@karaya.com) : Modified for integration into uml
++**********************************************************************/
++
++/* XXX This file shouldn't refer to CONFIG_* */
++
++#include <errno.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <signal.h>
++#include <string.h>
++#include <termios.h>
++#include <sys/wait.h>
++#include <sys/types.h>
++#include <sys/ptrace.h>
++#include <sys/ioctl.h>
++#include <asm/unistd.h>
++
++#include "ptproxy.h"
++#include "sysdep.h"
++#include "wait.h"
++
++#include "user_util.h"
++#include "user.h"
++#include "os.h"
++#include "tempfile.h"
++
++static int debugger_wait(debugger_state *debugger, int *status, int options,
++                       int (*syscall)(debugger_state *debugger, pid_t child),
++                       int (*normal_return)(debugger_state *debugger, 
++                                            pid_t unused),
++                       int (*wait_return)(debugger_state *debugger, 
++                                          pid_t unused))
++{
++      if(debugger->real_wait){
++              debugger->handle_trace = normal_return;
++              syscall_continue(debugger->pid);
++              debugger->real_wait = 0;
++              return(1);
++      }
++      debugger->wait_status_ptr = status;
++      debugger->wait_options = options;
++      if((debugger->debugee != NULL) && debugger->debugee->event){
++              syscall_continue(debugger->pid);
++              wait_for_stop(debugger->pid, SIGTRAP, PTRACE_SYSCALL,
++                            NULL);
++              (*wait_return)(debugger, -1);
++              return(0);
++      }
++      else if(debugger->wait_options & WNOHANG){
++              syscall_cancel(debugger->pid, 0);
++              debugger->handle_trace = syscall;
++              return(0);
++      }
++      else {
++              syscall_pause(debugger->pid);
++              debugger->handle_trace = wait_return;
++              debugger->waiting = 1;
++      }
++      return(1);
++}
++
++/*
++ * Handle debugger trap, i.e. syscall.
++ */
++
++int debugger_syscall(debugger_state *debugger, pid_t child)
++{
++      long arg1, arg2, arg3, arg4, arg5, result;
++      int syscall, ret = 0;
++
++      syscall = get_syscall(debugger->pid, &arg1, &arg2, &arg3, &arg4, 
++                            &arg5);
++
++      switch(syscall){
++      case __NR_execve:
++              /* execve never returns */
++              debugger->handle_trace = debugger_syscall; 
++              break;
++
++      case __NR_ptrace:
++              if(debugger->debugee->pid != 0) arg2 = debugger->debugee->pid;
++              if(!debugger->debugee->in_context) 
++                      child = debugger->debugee->pid;
++              result = proxy_ptrace(debugger, arg1, arg2, arg3, arg4, child,
++                                    &ret);
++              syscall_cancel(debugger->pid, result);
++              debugger->handle_trace = debugger_syscall;
++              return(ret);
++
++      case __NR_waitpid:
++      case __NR_wait4:
++              if(!debugger_wait(debugger, (int *) arg2, arg3, 
++                                debugger_syscall, debugger_normal_return, 
++                                proxy_wait_return))
++                      return(0);
++              break;
++
++      case __NR_kill:
++              if(!debugger->debugee->in_context) 
++                      child = debugger->debugee->pid;
++              if(arg1 == debugger->debugee->pid){
++                      result = kill(child, arg2);
++                      syscall_cancel(debugger->pid, result);
++                      debugger->handle_trace = debugger_syscall;
++                      return(0);
++              }
++              else debugger->handle_trace = debugger_normal_return;
++              break;
++
++      default:
++              debugger->handle_trace = debugger_normal_return;
++      }
++
++      syscall_continue(debugger->pid);
++      return(0);
++}
++
++/* Used by the tracing thread */
++static debugger_state parent;
++static int parent_syscall(debugger_state *debugger, int pid);
++
++int init_parent_proxy(int pid)
++{
++      parent = ((debugger_state) { .pid               = pid,
++                                   .wait_options      = 0,
++                                   .wait_status_ptr   = NULL,
++                                   .waiting           = 0,
++                                   .real_wait         = 0,
++                                   .expecting_child   = 0,
++                                   .handle_trace      = parent_syscall,
++                                   .debugee           = NULL } );
++      return(0);
++}
++
++int parent_normal_return(debugger_state *debugger, pid_t unused)
++{
++      debugger->handle_trace = parent_syscall;
++      syscall_continue(debugger->pid);
++      return(0);
++}
++
++static int parent_syscall(debugger_state *debugger, int pid)
++{
++      long arg1, arg2, arg3, arg4, arg5;
++      int syscall;
++
++      syscall = get_syscall(pid, &arg1, &arg2, &arg3, &arg4, &arg5);
++              
++      if((syscall == __NR_waitpid) || (syscall == __NR_wait4)){
++              debugger_wait(&parent, (int *) arg2, arg3, parent_syscall,
++                            parent_normal_return, parent_wait_return);
++      }
++      else ptrace(PTRACE_SYSCALL, pid, 0, 0);
++      return(0);
++}
++
++int debugger_normal_return(debugger_state *debugger, pid_t unused)
++{
++      debugger->handle_trace = debugger_syscall;
++      syscall_continue(debugger->pid);
++      return(0);
++}
++
++void debugger_cancelled_return(debugger_state *debugger, int result)
++{
++      debugger->handle_trace = debugger_syscall;
++      syscall_set_result(debugger->pid, result);
++      syscall_continue(debugger->pid);
++}
++
++/* Used by the tracing thread */
++static debugger_state debugger;
++static debugee_state debugee;
++
++void init_proxy (pid_t debugger_pid, int stopped, int status)
++{
++      debugger.pid = debugger_pid;
++      debugger.handle_trace = debugger_syscall;
++      debugger.debugee = &debugee;
++      debugger.waiting = 0;
++      debugger.real_wait = 0;
++      debugger.expecting_child = 0;
++
++      debugee.pid = 0;
++      debugee.traced = 0;
++      debugee.stopped = stopped;
++      debugee.event = 0;
++      debugee.zombie = 0;
++      debugee.died = 0;
++      debugee.wait_status = status;
++      debugee.in_context = 1;
++}
++
++int debugger_proxy(int status, int pid)
++{
++      int ret = 0, sig;
++
++      if(WIFSTOPPED(status)){
++              sig = WSTOPSIG(status);
++              if (sig == SIGTRAP)
++                      ret = (*debugger.handle_trace)(&debugger, pid);
++                                                     
++              else if(sig == SIGCHLD){
++                      if(debugger.expecting_child){
++                              ptrace(PTRACE_SYSCALL, debugger.pid, 0, sig);
++                              debugger.expecting_child = 0;
++                      }
++                      else if(debugger.waiting)
++                              real_wait_return(&debugger);
++                      else {
++                              ptrace(PTRACE_SYSCALL, debugger.pid, 0, sig);
++                              debugger.real_wait = 1;
++                      }
++              }
++              else ptrace(PTRACE_SYSCALL, debugger.pid, 0, sig);
++      }
++      else if(WIFEXITED(status)){
++              tracer_panic("debugger (pid %d) exited with status %d", 
++                           debugger.pid, WEXITSTATUS(status));
++      }
++      else if(WIFSIGNALED(status)){
++              tracer_panic("debugger (pid %d) exited with signal %d", 
++                           debugger.pid, WTERMSIG(status));
++      }
++      else {
++              tracer_panic("proxy got unknown status (0x%x) on debugger "
++                           "(pid %d)", status, debugger.pid);
++      }
++      return(ret);
++}
++
++void child_proxy(pid_t pid, int status)
++{
++      debugee.event = 1;
++      debugee.wait_status = status;
++
++      if(WIFSTOPPED(status)){
++              debugee.stopped = 1;
++              debugger.expecting_child = 1;
++              kill(debugger.pid, SIGCHLD);
++      }
++      else if(WIFEXITED(status) || WIFSIGNALED(status)){
++              debugee.zombie = 1;
++              debugger.expecting_child = 1;
++              kill(debugger.pid, SIGCHLD);
++      }
++      else panic("proxy got unknown status (0x%x) on child (pid %d)", 
++                 status, pid);
++}
++
++void debugger_parent_signal(int status, int pid)
++{
++      int sig;
++
++      if(WIFSTOPPED(status)){
++              sig = WSTOPSIG(status);
++              if(sig == SIGTRAP) (*parent.handle_trace)(&parent, pid);
++              else ptrace(PTRACE_SYSCALL, pid, 0, sig);
++      }
++}
++
++void fake_child_exit(void)
++{
++      int status, pid;
++
++      child_proxy(1, W_EXITCODE(0, 0));
++      while(debugger.waiting == 1){
++              CATCH_EINTR(pid = waitpid(debugger.pid, &status, WUNTRACED));
++              if(pid != debugger.pid){
++                      printk("fake_child_exit - waitpid failed, "
++                             "errno = %d\n", errno);
++                      return;
++              }
++              debugger_proxy(status, debugger.pid);
++      }
++      CATCH_EINTR(pid = waitpid(debugger.pid, &status, WUNTRACED));
++      if(pid != debugger.pid){
++              printk("fake_child_exit - waitpid failed, "
++                     "errno = %d\n", errno);
++              return;
++      }
++      if(ptrace(PTRACE_DETACH, debugger.pid, 0, SIGCONT) < 0)
++              printk("fake_child_exit - PTRACE_DETACH failed, errno = %d\n",
++                     errno);
++}
++
++char gdb_init_string[] = 
++"att 1 \n\
++b panic \n\
++b stop \n\
++handle SIGWINCH nostop noprint pass \n\
++";
++
++int start_debugger(char *prog, int startup, int stop, int *fd_out)
++{
++      int slave, child;
++
++      slave = open_gdb_chan();
++      child = fork();
++      if(child == 0){
++              char *tempname = NULL;
++              int fd;
++
++              if(setsid() < 0) perror("setsid");
++              if((dup2(slave, 0) < 0) || (dup2(slave, 1) < 0) || 
++                 (dup2(slave, 2) < 0)){
++                      printk("start_debugger : dup2 failed, errno = %d\n",
++                             errno);
++                      exit(1);
++              }
++              if(ioctl(0, TIOCSCTTY, 0) < 0){
++                      printk("start_debugger : TIOCSCTTY failed, "
++                             "errno = %d\n", errno);
++                      exit(1);
++              }
++              if(tcsetpgrp (1, os_getpid()) < 0){
++                      printk("start_debugger : tcsetpgrp failed, "
++                             "errno = %d\n", errno);
++#ifdef notdef
++                      exit(1);
++#endif
++              }
++              fd = make_tempfile("/tmp/gdb_init-XXXXXX", &tempname, 0);
++              if(fd < 0){
++                      printk("start_debugger : make_tempfile failed,"
++                             "err = %d\n", -fd);
++                      exit(1);
++              }
++              os_write_file(fd, gdb_init_string, sizeof(gdb_init_string) - 1);
++              if(startup){
++                      if(stop){
++                              os_write_file(fd, "b start_kernel\n",
++                                    strlen("b start_kernel\n"));
++                      }
++                      os_write_file(fd, "c\n", strlen("c\n"));
++              }
++              if(ptrace(PTRACE_TRACEME, 0, 0, 0) < 0){
++                      printk("start_debugger :  PTRACE_TRACEME failed, "
++                             "errno = %d\n", errno);
++                      exit(1);
++              }
++              execlp("gdb", "gdb", "--command", tempname, prog, NULL);
++              printk("start_debugger : exec of gdb failed, errno = %d\n",
++                     errno);
++      }
++      if(child < 0){
++              printk("start_debugger : fork for gdb failed, errno = %d\n",
++                     errno);
++              return(-1);
++      }
++      *fd_out = slave;
++      return(child);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/tt/ptproxy/ptproxy.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/tt/ptproxy/ptproxy.h      1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/tt/ptproxy/ptproxy.h   2005-05-03 22:28:14.529401408 +0300
+@@ -0,0 +1,61 @@
++/**********************************************************************
++ptproxy.h
++
++Copyright (C) 1999 Lars Brinkhoff.  See the file COPYING for licensing
++terms and conditions.
++**********************************************************************/
++
++#ifndef __PTPROXY_H
++#define __PTPROXY_H
++
++#include <sys/types.h>
++
++typedef struct debugger debugger_state;
++typedef struct debugee debugee_state;
++
++struct debugger
++{
++      pid_t pid;
++      int wait_options;
++      int *wait_status_ptr;
++      unsigned int waiting : 1;
++      unsigned int real_wait : 1;
++      unsigned int expecting_child : 1;
++      int (*handle_trace) (debugger_state *, pid_t);
++
++      debugee_state *debugee;
++};
++
++struct debugee
++{
++      pid_t pid;
++      int wait_status;
++      unsigned int died : 1;
++      unsigned int event : 1;
++      unsigned int stopped : 1;
++      unsigned int trace_singlestep : 1;
++      unsigned int trace_syscall : 1;
++      unsigned int traced : 1;
++      unsigned int zombie : 1;
++      unsigned int in_context : 1;
++};
++
++extern int debugger_syscall(debugger_state *debugger, pid_t pid);
++extern int debugger_normal_return (debugger_state *debugger, pid_t unused);
++
++extern long proxy_ptrace (struct debugger *, int, pid_t, long, long, pid_t,
++                        int *strace_out);
++extern void debugger_cancelled_return(debugger_state *debugger, int result);
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/tt/ptproxy/ptrace.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/tt/ptproxy/ptrace.c       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/tt/ptproxy/ptrace.c    2005-05-03 22:28:14.531401104 +0300
+@@ -0,0 +1,239 @@
++/**********************************************************************
++ptrace.c
++
++Copyright (C) 1999 Lars Brinkhoff.  See the file COPYING for licensing
++terms and conditions.
++
++Jeff Dike (jdike@karaya.com) : Modified for integration into uml
++**********************************************************************/
++
++#include <errno.h>
++#include <unistd.h>
++#include <signal.h>
++#include <sys/types.h>
++#include <sys/time.h>
++#include <sys/ptrace.h>
++#include <sys/wait.h>
++#include <asm/ptrace.h>
++
++#include "ptproxy.h"
++#include "debug.h"
++#include "user_util.h"
++#include "kern_util.h"
++#include "ptrace_user.h"
++#include "tt.h"
++
++long proxy_ptrace(struct debugger *debugger, int arg1, pid_t arg2,
++                long arg3, long arg4, pid_t child, int *ret)
++{
++      sigset_t relay;
++      long result;
++      int status;
++
++      *ret = 0;
++      if(debugger->debugee->died) return(-ESRCH);
++
++      switch(arg1){
++      case PTRACE_ATTACH:
++              if(debugger->debugee->traced) return(-EPERM);
++
++              debugger->debugee->pid = arg2;
++              debugger->debugee->traced = 1;
++
++              if(is_valid_pid(arg2) && (arg2 != child)){
++                      debugger->debugee->in_context = 0;
++                      kill(arg2, SIGSTOP);
++                      debugger->debugee->event = 1;
++                      debugger->debugee->wait_status = W_STOPCODE(SIGSTOP);
++              }
++              else {
++                      debugger->debugee->in_context = 1;
++                      if(debugger->debugee->stopped) 
++                              child_proxy(child, W_STOPCODE(SIGSTOP));
++                      else kill(child, SIGSTOP);
++              }
++
++              return(0);
++
++      case PTRACE_DETACH:
++              if(!debugger->debugee->traced) return(-EPERM);
++              
++              debugger->debugee->traced = 0;
++              debugger->debugee->pid = 0;
++              if(!debugger->debugee->in_context)
++                      kill(child, SIGCONT);
++
++              return(0);
++
++      case PTRACE_CONT:
++              if(!debugger->debugee->in_context) return(-EPERM);
++              *ret = PTRACE_CONT;
++              return(ptrace(PTRACE_CONT, child, arg3, arg4));
++
++#ifdef UM_HAVE_GETFPREGS
++      case PTRACE_GETFPREGS:
++      {
++              long regs[FP_FRAME_SIZE];
++              int i, result;
++
++              result = ptrace(PTRACE_GETFPREGS, child, 0, regs);
++              if(result == -1) return(-errno);
++              
++              for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++)
++                      ptrace(PTRACE_POKEDATA, debugger->pid, arg4 + 4 * i,
++                             regs[i]);
++              return(result);
++      }
++#endif
++
++#ifdef UM_HAVE_GETFPXREGS
++      case PTRACE_GETFPXREGS:
++      {
++              long regs[FPX_FRAME_SIZE];
++              int i, result;
++
++              result = ptrace(PTRACE_GETFPXREGS, child, 0, regs);
++              if(result == -1) return(-errno);
++              
++              for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++)
++                      ptrace(PTRACE_POKEDATA, debugger->pid, arg4 + 4 * i,
++                             regs[i]);
++              return(result);
++      }
++#endif
++
++#ifdef UM_HAVE_GETREGS
++      case PTRACE_GETREGS:
++      {
++              long regs[FRAME_SIZE];
++              int i, result;
++
++              result = ptrace(PTRACE_GETREGS, child, 0, regs);
++              if(result == -1) return(-errno);
++
++              for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++)
++                      ptrace (PTRACE_POKEDATA, debugger->pid,
++                              arg4 + 4 * i, regs[i]);
++              return(result);
++      }
++      break;
++#endif
++
++      case PTRACE_KILL:
++              result = ptrace(PTRACE_KILL, child, arg3, arg4);
++              if(result == -1) return(-errno);
++
++              return(result);
++
++      case PTRACE_PEEKDATA:
++      case PTRACE_PEEKTEXT:
++      case PTRACE_PEEKUSER:
++              /* The value being read out could be -1, so we have to 
++               * check errno to see if there's an error, and zero it
++               * beforehand so we're not faked out by an old error
++               */
++
++              errno = 0;
++              result = ptrace(arg1, child, arg3, 0);
++              if((result == -1) && (errno != 0)) return(-errno);
++
++              result = ptrace(PTRACE_POKEDATA, debugger->pid, arg4, result);
++              if(result == -1) return(-errno);
++                      
++              return(result);
++
++      case PTRACE_POKEDATA:
++      case PTRACE_POKETEXT:
++      case PTRACE_POKEUSER:
++              result = ptrace(arg1, child, arg3, arg4);
++              if(result == -1) return(-errno);
++
++              if(arg1 == PTRACE_POKEUSER) ptrace_pokeuser(arg3, arg4);
++              return(result);
++
++#ifdef UM_HAVE_SETFPREGS
++      case PTRACE_SETFPREGS:
++      {
++              long regs[FP_FRAME_SIZE];
++              int i;
++
++              for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++)
++                      regs[i] = ptrace (PTRACE_PEEKDATA, debugger->pid,
++                                        arg4 + 4 * i, 0);
++              result = ptrace(PTRACE_SETFPREGS, child, 0, regs);
++              if(result == -1) return(-errno);
++
++              return(result);
++      }
++#endif
++
++#ifdef UM_HAVE_SETFPXREGS
++      case PTRACE_SETFPXREGS:
++      {
++              long regs[FPX_FRAME_SIZE];
++              int i;
++
++              for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++)
++                      regs[i] = ptrace (PTRACE_PEEKDATA, debugger->pid,
++                                        arg4 + 4 * i, 0);
++              result = ptrace(PTRACE_SETFPXREGS, child, 0, regs);
++              if(result == -1) return(-errno);
++
++              return(result);
++      }
++#endif
++
++#ifdef UM_HAVE_SETREGS
++      case PTRACE_SETREGS:
++      {
++              long regs[FRAME_SIZE];
++              int i;
++
++              for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++)
++                      regs[i] = ptrace(PTRACE_PEEKDATA, debugger->pid,
++                                       arg4 + 4 * i, 0);
++              result = ptrace(PTRACE_SETREGS, child, 0, regs);
++              if(result == -1) return(-errno);
++
++              return(result);
++      }
++#endif
++
++      case PTRACE_SINGLESTEP:
++              if(!debugger->debugee->in_context) return(-EPERM);
++              sigemptyset(&relay);
++              sigaddset(&relay, SIGSEGV);
++              sigaddset(&relay, SIGILL);
++              sigaddset(&relay, SIGBUS);
++              result = ptrace(PTRACE_SINGLESTEP, child, arg3, arg4);
++              if(result == -1) return(-errno);
++              
++              status = wait_for_stop(child, SIGTRAP, PTRACE_SINGLESTEP,
++                                     &relay);
++              child_proxy(child, status);
++              return(result);
++
++      case PTRACE_SYSCALL:
++              if(!debugger->debugee->in_context) return(-EPERM);
++              result = ptrace(PTRACE_SYSCALL, child, arg3, arg4);
++              if(result == -1) return(-errno);
++
++              *ret = PTRACE_SYSCALL;
++              return(result);
++
++      case PTRACE_TRACEME:
++      default:
++              return(-EINVAL);
++      }
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/tt/ptproxy/sysdep.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/tt/ptproxy/sysdep.c       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/tt/ptproxy/sysdep.c    2005-05-03 22:28:14.532400952 +0300
+@@ -0,0 +1,72 @@
++/**********************************************************************
++sysdep.c
++
++Copyright (C) 1999 Lars Brinkhoff.  See the file COPYING for licensing
++terms and conditions.
++**********************************************************************/
++
++#include <stdio.h>
++#include <string.h>
++#include <stdlib.h>
++#include <signal.h>
++#include <errno.h>
++#include <sys/types.h>
++#include <sys/ptrace.h>
++#include <asm/ptrace.h>
++#include <linux/unistd.h>
++#include "ptrace_user.h"
++#include "user_util.h"
++#include "user.h"
++
++int get_syscall(pid_t pid, long *arg1, long *arg2, long *arg3, long *arg4, 
++              long *arg5)
++{
++      *arg1 = ptrace(PTRACE_PEEKUSER, pid, PT_SYSCALL_ARG1_OFFSET, 0);
++      *arg2 = ptrace(PTRACE_PEEKUSER, pid, PT_SYSCALL_ARG2_OFFSET, 0);
++      *arg3 = ptrace(PTRACE_PEEKUSER, pid, PT_SYSCALL_ARG3_OFFSET, 0);
++      *arg4 = ptrace(PTRACE_PEEKUSER, pid, PT_SYSCALL_ARG4_OFFSET, 0);
++      *arg5 = ptrace(PTRACE_PEEKUSER, pid, PT_SYSCALL_ARG5_OFFSET, 0);
++      return(ptrace(PTRACE_PEEKUSER, pid, PT_SYSCALL_NR_OFFSET, 0));
++}
++
++void syscall_cancel(pid_t pid, int result)
++{
++      if((ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_NR_OFFSET, 
++                 __NR_getpid) < 0) ||
++         (ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0) ||
++         (wait_for_stop(pid, SIGTRAP, PTRACE_SYSCALL, NULL) < 0) ||
++         (ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_RET_OFFSET, result) < 0) ||
++         (ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0))
++              printk("ptproxy: couldn't cancel syscall: errno = %d\n", 
++                     errno);
++}
++
++void syscall_set_result(pid_t pid, long result)
++{
++      ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_RET_OFFSET, result);
++}
++
++void syscall_continue(pid_t pid)
++{
++      ptrace(PTRACE_SYSCALL, pid, 0, 0);
++}
++
++int syscall_pause(pid_t pid) 
++{
++      if(ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_NR_OFFSET, __NR_pause) < 0){
++              printk("syscall_change - ptrace failed, errno = %d\n", errno);
++              return(-1);
++      }
++      return(0);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/tt/ptproxy/sysdep.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/tt/ptproxy/sysdep.h       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/tt/ptproxy/sysdep.h    2005-05-03 22:28:14.533400800 +0300
+@@ -0,0 +1,25 @@
++/**********************************************************************
++sysdep.h
++
++Copyright (C) 1999 Lars Brinkhoff.
++Copyright (C) 2001 Jeff Dike (jdike@karaya.com)
++See the file COPYING for licensing terms and conditions.
++**********************************************************************/
++
++extern int get_syscall(pid_t pid, long *arg1, long *arg2, long *arg3, 
++                     long *arg4, long *arg5);
++extern void syscall_cancel (pid_t pid, long result);
++extern void syscall_set_result (pid_t pid, long result);
++extern void syscall_continue (pid_t pid);
++extern int syscall_pause(pid_t pid);
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/tt/ptproxy/wait.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/tt/ptproxy/wait.c 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/tt/ptproxy/wait.c      2005-05-03 22:28:14.534400648 +0300
+@@ -0,0 +1,88 @@
++/**********************************************************************
++wait.c
++
++Copyright (C) 1999 Lars Brinkhoff.  See the file COPYING for licensing
++terms and conditions.
++
++**********************************************************************/
++
++#include <errno.h>
++#include <signal.h>
++#include <sys/wait.h>
++#include <sys/ptrace.h>
++#include <asm/ptrace.h>
++
++#include "ptproxy.h"
++#include "sysdep.h"
++#include "wait.h"
++#include "user_util.h"
++#include "sysdep/ptrace.h"
++#include "sysdep/ptrace_user.h"
++#include "sysdep/sigcontext.h"
++
++int proxy_wait_return(struct debugger *debugger, pid_t unused)
++{
++      debugger->waiting = 0;
++
++      if(debugger->debugee->died || (debugger->wait_options & __WCLONE)){
++              debugger_cancelled_return(debugger, -ECHILD);
++              return(0);
++      }
++
++      if(debugger->debugee->zombie && debugger->debugee->event)
++              debugger->debugee->died = 1;
++
++      if(debugger->debugee->event){
++              debugger->debugee->event = 0;
++              ptrace(PTRACE_POKEDATA, debugger->pid,
++                     debugger->wait_status_ptr, 
++                     debugger->debugee->wait_status);
++              /* if (wait4)
++                 ptrace (PTRACE_POKEDATA, pid, rusage_ptr, ...); */
++              debugger_cancelled_return(debugger, debugger->debugee->pid);
++              return(0);
++      }
++
++      /* pause will return -EINTR, which happens to be right for wait */
++      debugger_normal_return(debugger, -1);
++      return(0);
++}
++
++int parent_wait_return(struct debugger *debugger, pid_t unused)
++{
++      return(debugger_normal_return(debugger, -1));
++}
++
++int real_wait_return(struct debugger *debugger)
++{
++      unsigned long ip;
++      int pid;
++
++      pid = debugger->pid;
++
++      ip = ptrace(PTRACE_PEEKUSER, pid, PT_IP_OFFSET, 0);
++      IP_RESTART_SYSCALL(ip);
++
++      if(ptrace(PTRACE_POKEUSER, pid, PT_IP_OFFSET, ip) < 0)
++              tracer_panic("real_wait_return : Failed to restart system "
++                           "call, errno = %d\n", errno);
++
++      if((ptrace(PTRACE_SYSCALL, debugger->pid, 0, SIGCHLD) < 0) ||
++         (ptrace(PTRACE_SYSCALL, debugger->pid, 0, 0) < 0) ||
++         (ptrace(PTRACE_SYSCALL, debugger->pid, 0, 0) < 0) ||
++         debugger_normal_return(debugger, -1))
++              tracer_panic("real_wait_return : gdb failed to wait, "
++                           "errno = %d\n", errno);
++      return(0);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/tt/ptproxy/wait.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/tt/ptproxy/wait.h 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/tt/ptproxy/wait.h      2005-05-03 22:28:14.534400648 +0300
+@@ -0,0 +1,15 @@
++/**********************************************************************
++wait.h
++
++Copyright (C) 1999 Lars Brinkhoff.  See the file COPYING for licensing
++terms and conditions.
++**********************************************************************/
++
++#ifndef __PTPROXY_WAIT_H
++#define __PTPROXY_WAIT_H
++
++extern int proxy_wait_return(struct debugger *debugger, pid_t unused);
++extern int real_wait_return(struct debugger *debugger);
++extern int parent_wait_return(struct debugger *debugger, pid_t unused);
++
++#endif
+Index: linux-2.4.29/arch/um/kernel/tt/syscall_kern.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/tt/syscall_kern.c 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/tt/syscall_kern.c      2005-05-03 22:28:14.536400344 +0300
+@@ -0,0 +1,136 @@
++/* 
++ * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/types.h"
++#include "linux/utime.h"
++#include "linux/sys.h"
++#include "asm/unistd.h"
++#include "asm/ptrace.h"
++#include "asm/uaccess.h"
++#include "sysdep/syscalls.h"
++#include "kern_util.h"
++
++static inline int check_area(void *ptr, int size)
++{
++      return(verify_area(VERIFY_WRITE, ptr, size));
++}
++
++static int check_readlink(struct pt_regs *regs)
++{
++      return(check_area((void *) UPT_SYSCALL_ARG1(&regs->regs),
++                        UPT_SYSCALL_ARG2(&regs->regs)));
++}
++
++static int check_utime(struct pt_regs *regs)
++{
++      return(check_area((void *) UPT_SYSCALL_ARG1(&regs->regs),
++                        sizeof(struct utimbuf)));
++}
++
++static int check_oldstat(struct pt_regs *regs)
++{
++      return(check_area((void *) UPT_SYSCALL_ARG1(&regs->regs), 
++                        sizeof(struct __old_kernel_stat)));
++}
++
++static int check_stat(struct pt_regs *regs)
++{
++      return(check_area((void *) UPT_SYSCALL_ARG1(&regs->regs), 
++                        sizeof(struct stat)));
++}
++
++static int check_stat64(struct pt_regs *regs)
++{
++      return(check_area((void *) UPT_SYSCALL_ARG1(&regs->regs), 
++                        sizeof(struct stat64)));
++}
++
++struct bogus {
++      int kernel_ds;
++      int (*check_params)(struct pt_regs *);
++};
++
++struct bogus this_is_bogus[256] = {
++      [ __NR_mknod ] = { 1, NULL },
++      [ __NR_mkdir ] = { 1, NULL },
++      [ __NR_rmdir ] = { 1, NULL },
++      [ __NR_unlink ] = { 1, NULL },
++      [ __NR_symlink ] = { 1, NULL },
++      [ __NR_link ] = { 1, NULL },
++      [ __NR_rename ] = { 1, NULL },
++      [ __NR_umount ] = { 1, NULL },
++      [ __NR_mount ] = { 1, NULL },
++      [ __NR_pivot_root ] = { 1, NULL },
++      [ __NR_chdir ] = { 1, NULL },
++      [ __NR_chroot ] = { 1, NULL },
++      [ __NR_open ] = { 1, NULL },
++      [ __NR_quotactl ] = { 1, NULL },
++      [ __NR_sysfs ] = { 1, NULL },
++      [ __NR_readlink ] = { 1, check_readlink },
++      [ __NR_acct ] = { 1, NULL },
++      [ __NR_execve ] = { 1, NULL },
++      [ __NR_uselib ] = { 1, NULL },
++      [ __NR_statfs ] = { 1, NULL },
++      [ __NR_truncate ] = { 1, NULL },
++      [ __NR_access ] = { 1, NULL },
++      [ __NR_chmod ] = { 1, NULL },
++      [ __NR_chown ] = { 1, NULL },
++      [ __NR_lchown ] = { 1, NULL },
++      [ __NR_utime ] = { 1, check_utime },
++      [ __NR_oldlstat ] = { 1, check_oldstat },
++      [ __NR_oldstat ] = { 1, check_oldstat },
++      [ __NR_stat ] = { 1, check_stat },
++      [ __NR_lstat ] = { 1, check_stat },
++      [ __NR_stat64 ] = { 1, check_stat64 },
++      [ __NR_lstat64 ] = { 1, check_stat64 },
++      [ __NR_chown32 ] = { 1, NULL },
++};
++
++/* sys_utimes */
++
++static int check_bogosity(struct pt_regs *regs)
++{
++      struct bogus *bogon = &this_is_bogus[UPT_SYSCALL_NR(&regs->regs)];
++
++      if(!bogon->kernel_ds) return(0);
++      if(bogon->check_params && (*bogon->check_params)(regs))
++              return(-EFAULT);
++      set_fs(KERNEL_DS);
++      return(0);
++}
++
++extern syscall_handler_t *sys_call_table[];
++
++long execute_syscall_tt(void *r)
++{
++      struct pt_regs *regs = r;
++      long res;
++      int syscall;
++
++      current->thread.nsyscalls++;
++      nsyscalls++;
++      syscall = UPT_SYSCALL_NR(&regs->regs);
++
++      if((syscall >= NR_syscalls) || (syscall < 0))
++              res = -ENOSYS;
++      else if(honeypot && check_bogosity(regs))
++              res = -EFAULT;
++      else res = EXECUTE_SYSCALL(syscall, regs);
++
++      set_fs(USER_DS);
++
++      return(res);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/tt/syscall_user.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/tt/syscall_user.c 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/tt/syscall_user.c      2005-05-03 22:28:14.537400192 +0300
+@@ -0,0 +1,92 @@
++/* 
++ * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <unistd.h>
++#include <signal.h>
++#include <errno.h>
++#include <sys/ptrace.h>
++#include <asm/unistd.h>
++#include "sysdep/ptrace.h"
++#include "sigcontext.h"
++#include "ptrace_user.h"
++#include "task.h"
++#include "user_util.h"
++#include "kern_util.h"
++#include "syscall_user.h"
++#include "tt.h"
++
++/* XXX Bogus */
++#define ERESTARTSYS   512
++#define ERESTARTNOINTR        513
++#define ERESTARTNOHAND        514
++
++void syscall_handler_tt(int sig, union uml_pt_regs *regs)
++{
++      void *sc;
++      long result;
++      int index, syscall;
++
++      syscall = UPT_SYSCALL_NR(regs);
++      sc = UPT_SC(regs);
++      SC_START_SYSCALL(sc);
++
++      index = record_syscall_start(syscall);
++      syscall_trace();
++      result = execute_syscall(regs);
++
++      /* regs->sc may have changed while the system call ran (there may
++       * have been an interrupt or segfault), so it needs to be refreshed.
++       */
++      UPT_SC(regs) = sc;
++
++      SC_SET_SYSCALL_RETURN(sc, result);
++      if((result == -ERESTARTNOHAND) || (result == -ERESTARTSYS) || 
++         (result == -ERESTARTNOINTR))
++              do_signal(result);
++
++      syscall_trace();
++      record_syscall_end(index, result);
++}
++
++int do_syscall(void *task, int pid)
++{
++      unsigned long proc_regs[FRAME_SIZE];
++      union uml_pt_regs *regs;
++      int syscall;
++
++      if(ptrace_getregs(pid, proc_regs) < 0)
++              tracer_panic("Couldn't read registers");
++      syscall = PT_SYSCALL_NR(proc_regs);
++
++      regs = TASK_REGS(task);
++      UPT_SYSCALL_NR(regs) = syscall;
++
++      if(syscall < 1) return(0);
++
++      if((syscall != __NR_sigreturn) &&
++         ((unsigned long *) PT_IP(proc_regs) >= &_stext) && 
++         ((unsigned long *) PT_IP(proc_regs) <= &_etext))
++              tracer_panic("I'm tracing myself and I can't get out");
++
++      if(use_sysemu) 
++              return(1);
++
++      if(ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_NR_OFFSET, 
++                __NR_getpid) < 0)
++              tracer_panic("do_syscall : Nullifying syscall failed, "
++                           "errno = %d", errno);
++      return(1);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/tt/sys-i386/Makefile
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/tt/sys-i386/Makefile      1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/tt/sys-i386/Makefile   2005-05-03 22:28:14.538400040 +0300
+@@ -0,0 +1,17 @@
++# 
++# Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++# Licensed under the GPL
++#
++
++O_TARGET = sys-i386.o
++
++obj-y = sigcontext.o
++
++USER_OBJS = sigcontext.o
++
++include $(TOPDIR)/Rules.make
++
++$(USER_OBJS) : %.o: %.c
++      $(CC) $(CFLAGS_$@) $(USER_CFLAGS) -c -o $@ $<
++
++clean :
+Index: linux-2.4.29/arch/um/kernel/tt/sys-i386/sigcontext.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/tt/sys-i386/sigcontext.c  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/tt/sys-i386/sigcontext.c       2005-05-03 22:28:14.539399888 +0300
+@@ -0,0 +1,60 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <stdlib.h>
++#include <asm/sigcontext.h>
++#include "kern_util.h"
++#include "sysdep/frame.h"
++
++int copy_sc_from_user_tt(void *to_ptr, void *from_ptr, void *data)
++{
++      struct arch_frame_data *arch = data;
++      struct sigcontext *to = to_ptr, *from = from_ptr;
++      struct _fpstate *to_fp, *from_fp;
++      unsigned long sigs;
++      int err;
++
++      to_fp = to->fpstate;
++      from_fp = from->fpstate;
++      sigs = to->oldmask;
++      err = copy_from_user_proc(to, from, sizeof(*to));
++      to->oldmask = sigs;
++      if(to_fp != NULL){
++              err |= copy_from_user_proc(&to->fpstate, &to_fp,
++                                         sizeof(to->fpstate));
++              err |= copy_from_user_proc(to_fp, from_fp, arch->fpstate_size);
++      }
++      return(err);
++}
++
++int copy_sc_to_user_tt(void *to_ptr, void *fp, void *from_ptr, void *data)
++{
++      struct arch_frame_data *arch = data;
++      struct sigcontext *to = to_ptr, *from = from_ptr;
++      struct _fpstate *to_fp, *from_fp;
++      int err;
++
++      to_fp = (struct _fpstate *) 
++              (fp ? (unsigned long) fp : ((unsigned long) to + sizeof(*to)));
++      from_fp = from->fpstate;
++      err = copy_to_user_proc(to, from, sizeof(*to));
++      if(from_fp != NULL){
++              err |= copy_to_user_proc(&to->fpstate, &to_fp,
++                                       sizeof(to->fpstate));
++              err |= copy_to_user_proc(to_fp, from_fp, arch->fpstate_size);
++      }
++      return(err);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/tt/time.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/tt/time.c 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/tt/time.c      2005-05-03 22:28:14.540399736 +0300
+@@ -0,0 +1,28 @@
++/* 
++ * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <signal.h>
++#include <sys/time.h>
++#include <time_user.h>
++#include "process.h"
++#include "user.h"
++
++void user_time_init_tt(void)
++{
++      if(signal(SIGVTALRM, (__sighandler_t) alarm_handler) == SIG_ERR)
++              panic("Couldn't set SIGVTALRM handler");
++      set_interval(ITIMER_VIRTUAL);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/tt/tlb.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/tt/tlb.c  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/tt/tlb.c       2005-05-03 22:28:14.541399584 +0300
+@@ -0,0 +1,220 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/stddef.h"
++#include "linux/kernel.h"
++#include "linux/sched.h"
++#include "asm/page.h"
++#include "asm/pgtable.h"
++#include "asm/uaccess.h"
++#include "user_util.h"
++#include "mem_user.h"
++#include "os.h"
++
++static void fix_range(struct mm_struct *mm, unsigned long start_addr, 
++                    unsigned long end_addr, int force)
++{
++      pgd_t *npgd;
++      pmd_t *npmd;
++      pte_t *npte;
++      unsigned long addr;
++      int r, w, x, err;
++
++      if((current->thread.mode.tt.extern_pid != -1) && 
++         (current->thread.mode.tt.extern_pid != os_getpid()))
++              panic("fix_range fixing wrong address space, current = 0x%p",
++                    current);
++      if(mm == NULL) return;
++      for(addr=start_addr;addr<end_addr;){
++              if(addr == TASK_SIZE){
++                      /* Skip over kernel text, kernel data, and physical
++                       * memory, which don't have ptes, plus kernel virtual
++                       * memory, which is flushed separately, and remap
++                       * the process stack.  The only way to get here is
++                       * if (end_addr == STACK_TOP) > TASK_SIZE, which is
++                       * only true in the honeypot case.
++                       */
++                      addr = STACK_TOP - ABOVE_KMEM;
++                      continue;
++              }
++              npgd = pgd_offset(mm, addr);
++              npmd = pmd_offset(npgd, addr);
++              if(pmd_present(*npmd)){
++                      npte = pte_offset(npmd, addr);
++                      r = pte_read(*npte);
++                      w = pte_write(*npte);
++                      x = pte_exec(*npte);
++                      if(!pte_dirty(*npte)) w = 0;
++                      if(!pte_young(*npte)){
++                              r = 0;
++                              w = 0;
++                      }
++                      if(force || pte_newpage(*npte)){
++                              err = os_unmap_memory((void *) addr, 
++                                                    PAGE_SIZE);
++                              if(err < 0)
++                                      panic("munmap failed, errno = %d\n",
++                                            -err);
++                              if(pte_present(*npte))
++                                      map_memory(addr, 
++                                                 pte_val(*npte) & PAGE_MASK,
++                                                 PAGE_SIZE, r, w, x);
++                      }
++                      else if(pte_newprot(*npte)){
++                              protect_memory(addr, PAGE_SIZE, r, w, x, 1);
++                      }
++                      *npte = pte_mkuptodate(*npte);
++                      addr += PAGE_SIZE;
++              }
++              else {
++                      if(force || pmd_newpage(*npmd)){
++                              err = os_unmap_memory((void *) addr, PMD_SIZE);
++                              if(err < 0)
++                                      panic("munmap failed, errno = %d\n",
++                                            -err);
++                              pmd_mkuptodate(*npmd);
++                      }
++                      addr += PMD_SIZE;
++              }
++      }
++}
++
++atomic_t vmchange_seq = ATOMIC_INIT(1);
++
++static void flush_kernel_vm_range(unsigned long start, unsigned long end,
++                                int update_seq)
++{
++      struct mm_struct *mm;
++      pgd_t *pgd;
++      pmd_t *pmd;
++      pte_t *pte;
++      unsigned long addr;
++      int updated = 0, err;
++
++      mm = &init_mm;
++      for(addr = start; addr < end;){
++              pgd = pgd_offset(mm, addr);
++              pmd = pmd_offset(pgd, addr);
++              if(pmd_present(*pmd)){
++                      pte = pte_offset(pmd, addr);
++                      if(!pte_present(*pte) || pte_newpage(*pte)){
++                              updated = 1;
++                              err = os_unmap_memory((void *) addr, 
++                                                    PAGE_SIZE);
++                              if(err < 0)
++                                      panic("munmap failed, errno = %d\n",
++                                            -err);
++                              if(pte_present(*pte))
++                                      map_memory(addr, 
++                                                 pte_val(*pte) & PAGE_MASK,
++                                                 PAGE_SIZE, 1, 1, 1);
++                      }
++                      else if(pte_newprot(*pte)){
++                              updated = 1;
++                              protect_memory(addr, PAGE_SIZE, 1, 1, 1, 1);
++                      }
++                      addr += PAGE_SIZE;
++              }
++              else {
++                      if(pmd_newpage(*pmd)){
++                              updated = 1;
++                              err = os_unmap_memory((void *) addr, PMD_SIZE);
++                              if(err < 0)
++                                      panic("munmap failed, errno = %d\n",
++                                            -err);
++                      }
++                      addr += PMD_SIZE;
++              }
++      }
++      if(updated && update_seq) atomic_inc(&vmchange_seq);
++}
++
++static void protect_vm_page(unsigned long addr, int w, int must_succeed)
++{
++      int err;
++
++      err = protect_memory(addr, PAGE_SIZE, 1, w, 1, must_succeed);
++      if(err == 0) return;
++      else if((err == -EFAULT) || (err == -ENOMEM)){
++              flush_kernel_vm_range(addr, addr + PAGE_SIZE, 1);
++              protect_vm_page(addr, w, 1);
++      }
++      else panic("protect_vm_page : protect failed, errno = %d\n", err);
++}
++
++void mprotect_kernel_vm(int w)
++{
++      struct mm_struct *mm;
++      pgd_t *pgd;
++      pmd_t *pmd;
++      pte_t *pte;
++      unsigned long addr;
++      
++      mm = &init_mm;
++      for(addr = start_vm; addr < end_vm;){
++              pgd = pgd_offset(mm, addr);
++              pmd = pmd_offset(pgd, addr);
++              if(pmd_present(*pmd)){
++                      pte = pte_offset(pmd, addr);
++                      if(pte_present(*pte)) protect_vm_page(addr, w, 0);
++                      addr += PAGE_SIZE;
++              }
++              else addr += PMD_SIZE;
++      }
++}
++
++void flush_tlb_kernel_vm_tt(void)
++{
++      flush_kernel_vm_range(start_vm, end_vm, 1);
++}
++
++void __flush_tlb_one_tt(unsigned long addr)
++{
++      flush_kernel_vm_range(addr, addr + PAGE_SIZE, 1);
++}
++
++void flush_tlb_range_tt(struct mm_struct *mm, unsigned long start, 
++                   unsigned long end)
++{
++      if(mm != current->mm) return;
++
++      /* Assumes that the range start ... end is entirely within
++       * either process memory or kernel vm
++       */
++      if((start >= start_vm) && (start < end_vm)) 
++              flush_kernel_vm_range(start, end, 1);
++      else fix_range(mm, start, end, 0);
++}
++
++void flush_tlb_mm_tt(struct mm_struct *mm)
++{
++      unsigned long seq;
++
++      if(mm != current->mm) return;
++
++      fix_range(mm, 0, STACK_TOP, 0);
++
++      seq = atomic_read(&vmchange_seq);
++      if(current->thread.mode.tt.vm_seq == seq) return;
++      current->thread.mode.tt.vm_seq = seq;
++      flush_kernel_vm_range(start_vm, end_vm, 0);
++}
++
++void force_flush_all_tt(void)
++{
++      fix_range(current->mm, 0, STACK_TOP, 1);
++      flush_kernel_vm_range(start_vm, end_vm, 0);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/tt/tracer.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/tt/tracer.c       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/tt/tracer.c    2005-05-03 22:28:14.543399280 +0300
+@@ -0,0 +1,457 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <stdarg.h>
++#include <unistd.h>
++#include <signal.h>
++#include <errno.h>
++#include <sched.h>
++#include <string.h>
++#include <sys/mman.h>
++#include <sys/ptrace.h>
++#include <sys/time.h>
++#include <sys/wait.h>
++#include "user.h"
++#include "sysdep/ptrace.h"
++#include "sigcontext.h"
++#include "sysdep/sigcontext.h"
++#include "os.h"
++#include "signal_user.h"
++#include "user_util.h"
++#include "mem_user.h"
++#include "process.h"
++#include "kern_util.h"
++#include "frame.h"
++#include "chan_user.h"
++#include "ptrace_user.h"
++#include "mode.h"
++#include "tt.h"
++
++static int tracer_winch[2];
++
++int is_tracer_winch(int pid, int fd, void *data)
++{
++      if(pid != tracing_pid)
++              return(0);
++
++      register_winch_irq(tracer_winch[0], fd, -1, data);
++      return(1);
++}
++
++static void tracer_winch_handler(int sig)
++{
++      int n;
++      char c = 1;
++
++      n = os_write_file(tracer_winch[1], &c, sizeof(c));
++      if(n != sizeof(c))
++              printk("tracer_winch_handler - write failed, err = %d\n", -n);
++}
++
++/* Called only by the tracing thread during initialization */
++
++static void setup_tracer_winch(void)
++{
++      int err;
++
++      err = os_pipe(tracer_winch, 1, 1);
++      if(err < 0){
++              printk("setup_tracer_winch : os_pipe failed, err = %d\n", -err);
++              return;
++      }
++      signal(SIGWINCH, tracer_winch_handler);
++}
++
++void attach_process(int pid)
++{
++      if((ptrace(PTRACE_ATTACH, pid, 0, 0) < 0) ||
++         (ptrace(PTRACE_CONT, pid, 0, 0) < 0))
++              tracer_panic("OP_FORK failed to attach pid");
++      wait_for_stop(pid, SIGSTOP, PTRACE_CONT, NULL);
++      if(ptrace(PTRACE_CONT, pid, 0, 0) < 0)
++              tracer_panic("OP_FORK failed to continue process");
++}
++
++void tracer_panic(char *format, ...)
++{
++      va_list ap;
++
++      va_start(ap, format);
++      vprintf(format, ap);
++      printf("\n");
++      while(1) pause();
++}
++
++static void tracer_segv(int sig, struct sigcontext sc)
++{
++      printf("Tracing thread segfault at address 0x%lx, ip 0x%lx\n",
++             SC_FAULT_ADDR(&sc), SC_IP(&sc));
++      while(1)
++              pause();
++}
++
++/* Changed early in boot, and then only read */
++int debug = 0;
++int debug_stop = 1;
++int debug_parent = 0;
++int honeypot = 0;
++
++static int signal_tramp(void *arg)
++{
++      int (*proc)(void *);
++
++      if(honeypot && munmap((void *) (host_task_size - 0x10000000),
++                            0x10000000)) 
++              panic("Unmapping stack failed");
++      if(ptrace(PTRACE_TRACEME, 0, 0, 0) < 0)
++              panic("ptrace PTRACE_TRACEME failed");
++      os_stop_process(os_getpid());
++      change_sig(SIGWINCH, 0);
++      signal(SIGUSR1, SIG_IGN);
++      change_sig(SIGCHLD, 0);
++      signal(SIGSEGV, (__sighandler_t) sig_handler);
++      set_cmdline("(idle thread)");
++      set_init_pid(os_getpid());
++      proc = arg;
++      return((*proc)(NULL));
++}
++
++static void sleeping_process_signal(int pid, int sig)
++{
++      switch(sig){
++      /* These two result from UML being ^Z-ed and bg-ed.  PTRACE_CONT is
++       * right because the process must be in the kernel already.
++       */
++      case SIGCONT:
++      case SIGTSTP:
++              if(ptrace(PTRACE_CONT, pid, 0, sig) < 0)
++                      tracer_panic("sleeping_process_signal : Failed to "
++                                   "continue pid %d, signal = %d, "
++                                   "errno = %d\n", pid, sig, errno);
++              break;
++
++      /* This happens when the debugger (e.g. strace) is doing system call 
++       * tracing on the kernel.  During a context switch, the current task
++       * will be set to the incoming process and the outgoing process will
++       * hop into write and then read.  Since it's not the current process
++       * any more, the trace of those will land here.  So, we need to just 
++       * PTRACE_SYSCALL it.
++       */
++      case SIGTRAP:
++              if(ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0)
++                      tracer_panic("sleeping_process_signal : Failed to "
++                                   "PTRACE_SYSCALL pid %d, errno = %d\n",
++                                   pid, errno);
++              break;
++      case SIGSTOP:
++              break;
++      default:
++              tracer_panic("sleeping process %d got unexpected "
++                           "signal : %d\n", pid, sig);
++              break;
++      }
++}
++
++/* Accessed only by the tracing thread */
++int debugger_pid = -1;
++int debugger_parent = -1;
++int debugger_fd = -1;
++int gdb_pid = -1;
++
++struct {
++      int pid;
++      int signal;
++      unsigned long addr;
++      struct timeval time;
++} signal_record[1024][32];
++
++int signal_index[32];
++int nsignals = 0;
++int debug_trace = 0;
++extern int io_nsignals, io_count, intr_count;
++
++extern void signal_usr1(int sig);
++
++int tracing_pid = -1;
++
++int tracer(int (*init_proc)(void *), void *sp)
++{
++      void *task = NULL;
++      unsigned long eip = 0;
++      int status, pid = 0, sig = 0, cont_type, tracing = 0, op = 0;
++      int last_index, proc_id = 0, n, err, old_tracing = 0, strace = 0;
++      int cont_syscall;
++
++      capture_signal_stack();
++      signal(SIGPIPE, SIG_IGN);
++      setup_tracer_winch();
++      tracing_pid = os_getpid();
++      printf("tracing thread pid = %d\n", tracing_pid);
++
++      pid = clone(signal_tramp, sp, CLONE_FILES | SIGCHLD, init_proc);
++      CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
++      if(n < 0){
++              printf("waitpid on idle thread failed, errno = %d\n", errno);
++              exit(1);
++      }
++      if((ptrace(PTRACE_CONT, pid, 0, 0) < 0)){
++              printf("Failed to continue idle thread, errno = %d\n", errno);
++              exit(1);
++      }
++
++      signal(SIGSEGV, (sighandler_t) tracer_segv);
++      signal(SIGUSR1, signal_usr1);
++      if(debug_trace){
++              printf("Tracing thread pausing to be attached\n");
++              stop();
++      }
++      if(debug){
++              if(gdb_pid != -1) 
++                      debugger_pid = attach_debugger(pid, gdb_pid, 1);
++              else debugger_pid = init_ptrace_proxy(pid, 1, debug_stop);
++              if(debug_parent){
++                      debugger_parent = os_process_parent(debugger_pid);
++                      init_parent_proxy(debugger_parent);
++                      err = attach(debugger_parent);
++                      if(err){
++                              printf("Failed to attach debugger parent %d, "
++                                     "errno = %d\n", debugger_parent, -err);
++                              debugger_parent = -1;
++                      }
++                      else {
++                              if(ptrace(PTRACE_SYSCALL, debugger_parent, 
++                                        0, 0) < 0){
++                                      printf("Failed to continue debugger "
++                                             "parent, errno = %d\n", errno);
++                                      debugger_parent = -1;
++                              }
++                      }
++              }
++      }
++      set_cmdline("(tracing thread)");
++      while(1){
++              CATCH_EINTR(pid = waitpid(-1, &status, WUNTRACED));
++              if(pid <= 0){
++                      if(errno != ECHILD){
++                              printf("wait failed - errno = %d\n", errno);
++                      }
++                      continue;
++              }
++              if(pid == debugger_pid){
++                      int cont = 0;
++
++                      if(WIFEXITED(status) || WIFSIGNALED(status))
++                              debugger_pid = -1;
++                      /* XXX Figure out how to deal with gdb and SMP */
++                      else cont = debugger_signal(status, cpu_tasks[0].pid);
++                      if(cont == PTRACE_SYSCALL) strace = 1;
++                      continue;
++              }
++              else if(pid == debugger_parent){
++                      debugger_parent_signal(status, pid);
++                      continue;
++              }
++              nsignals++;
++              if(WIFEXITED(status)) ;
++#ifdef notdef
++              {
++                      printf("Child %d exited with status %d\n", pid, 
++                             WEXITSTATUS(status));
++              }
++#endif
++              else if(WIFSIGNALED(status)){
++                      sig = WTERMSIG(status);
++                      if(sig != 9){
++                              printf("Child %d exited with signal %d\n", pid,
++                                     sig);
++                      }
++              }
++              else if(WIFSTOPPED(status)){
++                      proc_id = pid_to_processor_id(pid);
++                      sig = WSTOPSIG(status);
++                      if(signal_index[proc_id] == 1024){
++                              signal_index[proc_id] = 0;
++                              last_index = 1023;
++                      }
++                      else last_index = signal_index[proc_id] - 1;
++                      if(((sig == SIGPROF) || (sig == SIGVTALRM) || 
++                          (sig == SIGALRM)) &&
++                         (signal_record[proc_id][last_index].signal == sig)&&
++                         (signal_record[proc_id][last_index].pid == pid))
++                              signal_index[proc_id] = last_index;
++                      signal_record[proc_id][signal_index[proc_id]].pid = pid;
++                      gettimeofday(&signal_record[proc_id][signal_index[proc_id]].time, NULL);
++                      eip = ptrace(PTRACE_PEEKUSER, pid, PT_IP_OFFSET, 0);
++                      signal_record[proc_id][signal_index[proc_id]].addr = eip;
++                      signal_record[proc_id][signal_index[proc_id]++].signal = sig;
++                      
++                      if(proc_id == -1){
++                              sleeping_process_signal(pid, sig);
++                              continue;
++                      }
++
++                      task = cpu_tasks[proc_id].task;
++                      tracing = is_tracing(task);
++                      old_tracing = tracing;
++
++                      cont_syscall = use_sysemu ? PTRACE_SYSEMU : 
++                              PTRACE_SYSCALL;
++                      switch(sig){
++                      case SIGUSR1:
++                              sig = 0;
++                              op = do_proc_op(task, proc_id);
++                              switch(op){
++                              case OP_TRACE_ON:
++                                      arch_leave_kernel(task, pid);
++                                      tracing = 1;
++                                      break;
++                              case OP_REBOOT:
++                              case OP_HALT:
++                                      unmap_physmem();
++                                      kmalloc_ok = 0;
++                                      ptrace(PTRACE_KILL, pid, 0, 0);
++                                      return(op == OP_REBOOT);
++                              case OP_NONE:
++                                      printf("Detaching pid %d\n", pid);
++                                      detach(pid, SIGSTOP);
++                                      continue;
++                              default:
++                                      break;
++                              }
++                              /* OP_EXEC switches host processes on us,
++                               * we want to continue the new one.
++                               */
++                              pid = cpu_tasks[proc_id].pid;
++                              break;
++                      case SIGTRAP:
++                              if(!tracing && (debugger_pid != -1)){
++                                      child_signal(pid, status);
++                                      continue;
++                              }
++                              tracing = 0;
++                              if(do_syscall(task, pid))
++                                      sig = SIGUSR2;
++                              break;
++                      case SIGPROF:
++                              if(tracing) sig = 0;
++                              break;
++                      case SIGCHLD:
++                      case SIGHUP:
++                              sig = 0;
++                              break;
++                      case SIGSEGV:
++                      case SIGIO:
++                      case SIGALRM:
++                      case SIGVTALRM:
++                      case SIGFPE:
++                      case SIGBUS:
++                      case SIGILL:
++                      case SIGWINCH:
++                      default:
++                              tracing = 0;
++                              break;
++                      }
++                      set_tracing(task, tracing);
++
++                      if(!tracing && old_tracing)
++                              arch_enter_kernel(task, pid);
++
++                      if(!tracing && (debugger_pid != -1) && (sig != 0) &&
++                              (sig != SIGALRM) && (sig != SIGVTALRM) &&
++                              (sig != SIGSEGV) && (sig != SIGTRAP) &&
++                              (sig != SIGUSR2) && (sig != SIGIO) &&
++                              (sig != SIGFPE)){
++                              child_signal(pid, status);
++                              continue;
++                      }
++
++                      if(tracing){
++                              if(singlestepping(task))
++                                      cont_type = PTRACE_SINGLESTEP;
++                              else cont_type = cont_syscall;
++                      }
++                      else cont_type = PTRACE_CONT;
++
++                      if((cont_type == PTRACE_CONT) && 
++                         (debugger_pid != -1) && strace)
++                              cont_type = PTRACE_SYSCALL;
++
++                      if(ptrace(cont_type, pid, 0, sig) != 0){
++                              tracer_panic("ptrace failed to continue "
++                                           "process - errno = %d\n", 
++                                           errno);
++                      }
++              }
++      }
++      return(0);
++}
++
++static int __init uml_debug_setup(char *line, int *add)
++{
++      char *next;
++
++      debug = 1;
++      *add = 0;
++      if(*line != '=') return(0);
++      line++;
++
++      while(line != NULL){
++              next = strchr(line, ',');
++              if(next) *next++ = '\0';
++              
++              if(!strcmp(line, "go")) debug_stop = 0;
++              else if(!strcmp(line, "parent")) debug_parent = 1;
++              else printf("Unknown debug option : '%s'\n", line);
++
++              line = next;
++      }
++      return(0);
++}
++
++__uml_setup("debug", uml_debug_setup,
++"debug\n"
++"    Starts up the kernel under the control of gdb. See the \n"
++"    kernel debugging tutorial and the debugging session pages\n"
++"    at http://user-mode-linux.sourceforge.net/ for more information.\n\n"
++);
++
++static int __init uml_debugtrace_setup(char *line, int *add)
++{
++      debug_trace = 1;
++      return 0;
++}
++__uml_setup("debugtrace", uml_debugtrace_setup,
++"debugtrace\n"
++"    Causes the tracing thread to pause until it is attached by a\n"
++"    debugger and continued.  This is mostly for debugging crashes\n"
++"    early during boot, and should be pretty much obsoleted by\n"
++"    the debug switch.\n\n"
++);
++
++static int __init uml_honeypot_setup(char *line, int *add)
++{
++      jail_setup("", add);
++      honeypot = 1;
++      return 0;
++}
++__uml_setup("honeypot", uml_honeypot_setup, 
++"honeypot\n"
++"    This makes UML put process stacks in the same location as they are\n"
++"    on the host, allowing expoits such as stack smashes to work against\n"
++"    UML.  This implies 'jail'.\n\n"
++);
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/tt/trap_user.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/tt/trap_user.c    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/tt/trap_user.c 2005-05-03 22:28:14.544399128 +0300
+@@ -0,0 +1,64 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <stdlib.h>
++#include <errno.h>
++#include <signal.h>
++#include <asm/sigcontext.h>
++#include "sysdep/ptrace.h"
++#include "signal_user.h"
++#include "user_util.h"
++#include "kern_util.h"
++#include "task.h"
++#include "tt.h"
++
++void sig_handler_common_tt(int sig, void *sc_ptr)
++{
++      struct sigcontext *sc = sc_ptr;
++      struct tt_regs save_regs, *r;
++      struct signal_info *info;
++      int save_errno = errno, is_user;
++
++      unprotect_kernel_mem();
++
++      /* This is done because to allow SIGSEGV to be delivered inside a SEGV
++       * handler.  This can happen in copy_user, and if SEGV is disabled,
++       * the process will die.
++       */
++      if(sig == SIGSEGV)
++              change_sig(SIGSEGV, 1);
++
++      r = &TASK_REGS(get_current())->tt;
++      save_regs = *r;
++      is_user = user_context(SC_SP(sc));
++      r->sc = sc;
++      if(sig != SIGUSR2) 
++              r->syscall = -1;
++
++      info = &sig_info[sig];
++      if(!info->is_irq) unblock_signals();
++
++      (*info->handler)(sig, (union uml_pt_regs *) r);
++
++      if(is_user){
++              interrupt_end();
++              block_signals();
++              set_user_mode(NULL);
++      }
++      *r = save_regs;
++      errno = save_errno;
++      if(is_user) protect_kernel_mem();
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/tt/uaccess.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/tt/uaccess.c      1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/tt/uaccess.c   2005-05-03 22:28:14.545398976 +0300
+@@ -0,0 +1,73 @@
++/* 
++ * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/sched.h"
++#include "asm/uaccess.h"
++
++int copy_from_user_tt(void *to, const void *from, int n)
++{
++      if(!access_ok_tt(VERIFY_READ, from, n)) 
++              return(n);
++
++      return(__do_copy_from_user(to, from, n, &current->thread.fault_addr,
++                                 &current->thread.fault_catcher));
++}
++
++int copy_to_user_tt(void *to, const void *from, int n)
++{
++      if(!access_ok_tt(VERIFY_WRITE, to, n))
++              return(n);
++              
++      return(__do_copy_to_user(to, from, n, &current->thread.fault_addr,
++                               &current->thread.fault_catcher));
++}
++
++int strncpy_from_user_tt(char *dst, const char *src, int count)
++{
++      int n;
++
++      if(!access_ok_tt(VERIFY_READ, src, 1)) 
++              return(-EFAULT);
++
++      n = __do_strncpy_from_user(dst, src, count, 
++                                 &current->thread.fault_addr,
++                                 &current->thread.fault_catcher);
++      if(n < 0) return(-EFAULT);
++      return(n);
++}
++
++int __clear_user_tt(void *mem, int len)
++{
++      return(__do_clear_user(mem, len,
++                             &current->thread.fault_addr,
++                             &current->thread.fault_catcher));
++}
++
++int clear_user_tt(void *mem, int len)
++{
++      if(!access_ok_tt(VERIFY_WRITE, mem, len))
++              return(len);
++
++      return(__do_clear_user(mem, len, &current->thread.fault_addr,
++                             &current->thread.fault_catcher));
++}
++
++int strnlen_user_tt(const void *str, int len)
++{
++      return(__do_strnlen_user(str, len,
++                               &current->thread.fault_addr,
++                               &current->thread.fault_catcher));
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/tt/uaccess_user.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/tt/uaccess_user.c 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/tt/uaccess_user.c      2005-05-03 22:28:14.546398824 +0300
+@@ -0,0 +1,98 @@
++/* 
++ * Copyright (C) 2001 Chris Emerson (cemerson@chiark.greenend.org.uk)
++ * Copyright (C) 2001 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <setjmp.h>
++#include <string.h>
++#include "user_util.h"
++#include "uml_uaccess.h"
++#include "task.h"
++#include "kern_util.h"
++
++int __do_copy_from_user(void *to, const void *from, int n,
++                      void **fault_addr, void **fault_catcher)
++{
++      struct tt_regs save = TASK_REGS(get_current())->tt;
++      unsigned long fault;
++      int faulted;
++
++      fault = __do_user_copy(to, from, n, fault_addr, fault_catcher,
++                             __do_copy, &faulted);
++      TASK_REGS(get_current())->tt = save;
++
++      if(!faulted) return(0);
++      else return(n - (fault - (unsigned long) from));
++}
++
++static void __do_strncpy(void *dst, const void *src, int count)
++{
++      strncpy(dst, src, count);
++}     
++
++int __do_strncpy_from_user(char *dst, const char *src, unsigned long count,
++                         void **fault_addr, void **fault_catcher)
++{
++      struct tt_regs save = TASK_REGS(get_current())->tt;
++      unsigned long fault;
++      int faulted;
++
++      fault = __do_user_copy(dst, src, count, fault_addr, fault_catcher,
++                             __do_strncpy, &faulted);
++      TASK_REGS(get_current())->tt = save;
++
++      if(!faulted) return(strlen(dst));
++      else return(-1);
++}
++
++static void __do_clear(void *to, const void *from, int n)
++{
++      memset(to, 0, n);
++}     
++
++int __do_clear_user(void *mem, unsigned long len,
++                  void **fault_addr, void **fault_catcher)
++{
++      struct tt_regs save = TASK_REGS(get_current())->tt;
++      unsigned long fault;
++      int faulted;
++
++      fault = __do_user_copy(mem, NULL, len, fault_addr, fault_catcher,
++                             __do_clear, &faulted);
++      TASK_REGS(get_current())->tt = save;
++
++      if(!faulted) return(0);
++      else return(len - (fault - (unsigned long) mem));
++}
++
++int __do_strnlen_user(const char *str, unsigned long n,
++                    void **fault_addr, void **fault_catcher)
++{
++      struct tt_regs save = TASK_REGS(get_current())->tt;
++      int ret;
++      unsigned long *faddrp = (unsigned long *)fault_addr;
++      sigjmp_buf jbuf;
++
++      *fault_catcher = &jbuf;
++      if(sigsetjmp(jbuf, 1) == 0)
++              ret = strlen(str) + 1;
++      else ret = *faddrp - (unsigned long) str;
++
++      *fault_addr = NULL;
++      *fault_catcher = NULL;
++
++      TASK_REGS(get_current())->tt = save;
++      return ret;
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/tt/unmap.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/tt/unmap.c        1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/tt/unmap.c     2005-05-03 22:28:14.547398672 +0300
+@@ -0,0 +1,31 @@
++/* 
++ * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <sys/mman.h>
++
++int switcheroo(int fd, int prot, void *from, void *to, int size)
++{
++      if(munmap(to, size) < 0){
++              return(-1);
++      }
++      if(mmap(to, size, prot, MAP_SHARED | MAP_FIXED, fd, 0) != to){
++              return(-1);
++      }
++      if(munmap(from, size) < 0){
++              return(-1);
++      }
++      return(0);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/tty_log.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/tty_log.c 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/tty_log.c      2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,230 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) and 
++ * geoffrey hing <ghing@net.ohio-state.edu>
++ * Licensed under the GPL
++ */
++
++#include <errno.h>
++#include <string.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <sys/time.h>
++#include "init.h"
++#include "user.h"
++#include "kern_util.h"
++#include "os.h"
++
++#define TTY_LOG_DIR "./"
++
++/* Set early in boot and then unchanged */
++static char *tty_log_dir = TTY_LOG_DIR;
++static int tty_log_fd = -1;
++
++#define TTY_LOG_OPEN 1
++#define TTY_LOG_CLOSE 2
++#define TTY_LOG_WRITE 3
++#define TTY_LOG_EXEC 4
++
++#define TTY_READ 1
++#define TTY_WRITE 2
++
++struct tty_log_buf {
++      int what;
++      unsigned long tty;
++      int len;
++      int direction;
++      unsigned long sec;
++      unsigned long usec;
++};
++
++int open_tty_log(void *tty, void *current_tty)
++{
++      struct timeval tv;
++      struct tty_log_buf data;
++      char buf[strlen(tty_log_dir) + sizeof("01234567890-01234567\0")];
++      int fd;
++
++      gettimeofday(&tv, NULL);
++      if(tty_log_fd != -1){
++              data = ((struct tty_log_buf) { .what    = TTY_LOG_OPEN,
++                                             .tty  = (unsigned long) tty,
++                                             .len  = sizeof(current_tty),
++                                             .direction = 0,
++                                             .sec = tv.tv_sec,
++                                             .usec = tv.tv_usec } );
++              os_write_file(tty_log_fd, &data, sizeof(data));
++              os_write_file(tty_log_fd, &current_tty, data.len);
++              return(tty_log_fd);
++      }
++
++      sprintf(buf, "%s/%0u-%0u", tty_log_dir, (unsigned int) tv.tv_sec, 
++              (unsigned int) tv.tv_usec);
++
++      fd = os_open_file(buf, of_append(of_create(of_rdwr(OPENFLAGS()))),
++                        0644);
++      if(fd < 0){
++              printk("open_tty_log : couldn't open '%s', errno = %d\n",
++                     buf, -fd);
++      }
++      return(fd);
++}
++
++void close_tty_log(int fd, void *tty)
++{
++      struct tty_log_buf data;
++      struct timeval tv;
++
++      if(tty_log_fd != -1){
++              gettimeofday(&tv, NULL);
++              data = ((struct tty_log_buf) { .what    = TTY_LOG_CLOSE,
++                                             .tty  = (unsigned long) tty,
++                                             .len  = 0,
++                                             .direction = 0,
++                                             .sec = tv.tv_sec,
++                                             .usec = tv.tv_usec } );
++              os_write_file(tty_log_fd, &data, sizeof(data));
++              return;
++      }
++      os_close_file(fd);
++}
++
++static int log_chunk(int fd, const char *buf, int len)
++{
++      int total = 0, try, missed, n;
++      char chunk[64];
++
++      while(len > 0){
++              try = (len > sizeof(chunk)) ? sizeof(chunk) : len;
++              missed = copy_from_user_proc(chunk, (char *) buf, try);
++              try -= missed;
++              n = os_write_file(fd, chunk, try);
++              if(n != try) {
++                      if(n < 0) 
++                              return(n);
++                      return(-EIO);
++              }
++              if(missed != 0)
++                      return(-EFAULT);
++
++              len -= try;
++              total += try;
++              buf += try;
++      }
++
++      return(total);
++}
++
++int write_tty_log(int fd, const char *buf, int len, void *tty, int is_read)
++{
++      struct timeval tv;
++      struct tty_log_buf data;
++      int direction;
++
++      if(fd == tty_log_fd){
++              gettimeofday(&tv, NULL);
++              direction = is_read ? TTY_READ : TTY_WRITE;
++              data = ((struct tty_log_buf) { .what    = TTY_LOG_WRITE,
++                                             .tty  = (unsigned long) tty,
++                                             .len  = len,
++                                             .direction = direction,
++                                             .sec = tv.tv_sec,
++                                             .usec = tv.tv_usec } );
++              os_write_file(tty_log_fd, &data, sizeof(data));
++      }
++
++      return(log_chunk(fd, buf, len));
++}
++
++void log_exec(char **argv, void *tty)
++{
++      struct timeval tv;
++      struct tty_log_buf data;
++      char **ptr,*arg;
++      int len;
++      
++      if(tty_log_fd == -1) return;
++
++      gettimeofday(&tv, NULL);
++
++      len = 0;
++      for(ptr = argv; ; ptr++){
++              if(copy_from_user_proc(&arg, ptr, sizeof(arg)))
++                      return;
++              if(arg == NULL) break;
++              len += strlen_user_proc(arg);
++      }
++
++      data = ((struct tty_log_buf) { .what    = TTY_LOG_EXEC,
++                                     .tty  = (unsigned long) tty,
++                                     .len  = len,
++                                     .direction = 0,
++                                     .sec = tv.tv_sec,
++                                     .usec = tv.tv_usec } );
++      os_write_file(tty_log_fd, &data, sizeof(data));
++
++      for(ptr = argv; ; ptr++){
++              if(copy_from_user_proc(&arg, ptr, sizeof(arg)))
++                      return;
++              if(arg == NULL) break;
++              log_chunk(tty_log_fd, arg, strlen_user_proc(arg));
++      }
++}
++
++extern void register_tty_logger(int (*opener)(void *, void *),
++                              int (*writer)(int, const char *, int, 
++                                            void *, int),
++                              void (*closer)(int, void *));
++
++static int register_logger(void)
++{
++      register_tty_logger(open_tty_log, write_tty_log, close_tty_log);
++      return(0);
++}
++
++__uml_initcall(register_logger);
++
++static int __init set_tty_log_dir(char *name, int *add)
++{
++      tty_log_dir = name;
++      return 0;
++}
++
++__uml_setup("tty_log_dir=", set_tty_log_dir,
++"tty_log_dir=<directory>\n"
++"    This is used to specify the directory where the logs of all pty\n"
++"    data from this UML machine will be written.\n\n"
++);
++
++static int __init set_tty_log_fd(char *name, int *add)
++{
++      char *end;
++
++      tty_log_fd = strtoul(name, &end, 0);
++      if((*end != '\0') || (end == name)){
++              printf("set_tty_log_fd - strtoul failed on '%s'\n", name);
++              tty_log_fd = -1;
++      }
++
++      *add = 0;
++      return 0;
++}
++
++__uml_setup("tty_log_fd=", set_tty_log_fd,
++"tty_log_fd=<fd>\n"
++"    This is used to specify a preconfigured file descriptor to which all\n"
++"    tty data will be written.  Preconfigure the descriptor with something\n"
++"    like '10>tty_log tty_log_fd=10'.\n\n"
++);
++
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/uaccess_user.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/uaccess_user.c    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/uaccess_user.c 2005-05-03 22:28:14.549398368 +0300
+@@ -0,0 +1,64 @@
++/* 
++ * Copyright (C) 2001 Chris Emerson (cemerson@chiark.greenend.org.uk)
++ * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <setjmp.h>
++#include <string.h>
++
++/* These are here rather than tt/uaccess.c because skas mode needs them in
++ * order to do SIGBUS recovery when a tmpfs mount runs out of room.
++ */
++
++unsigned long __do_user_copy(void *to, const void *from, int n,
++                           void **fault_addr, void **fault_catcher,
++                           void (*op)(void *to, const void *from,
++                                      int n), int *faulted_out)
++{
++      unsigned long *faddrp = (unsigned long *) fault_addr, ret;
++
++      sigjmp_buf jbuf;
++      *fault_catcher = &jbuf;
++      if(sigsetjmp(jbuf, 1) == 0){
++              (*op)(to, from, n);
++              ret = 0;
++              *faulted_out = 0;
++      } 
++      else {
++              ret = *faddrp;
++              *faulted_out = 1;
++      }
++      *fault_addr = NULL;
++      *fault_catcher = NULL;
++      return ret;
++}
++
++void __do_copy(void *to, const void *from, int n)
++{
++      memcpy(to, from, n);
++}     
++
++
++int __do_copy_to_user(void *to, const void *from, int n,
++                    void **fault_addr, void **fault_catcher)
++{
++      unsigned long fault;
++      int faulted;
++
++      fault = __do_user_copy(to, from, n, fault_addr, fault_catcher,
++                             __do_copy, &faulted);
++      if(!faulted) return(0);
++      else return(n - (fault - (unsigned long) to));
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/um_arch.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/um_arch.c 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/um_arch.c      2005-05-03 22:28:14.552397912 +0300
+@@ -0,0 +1,445 @@
++/* 
++ * Copyright (C) 2000, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/config.h"
++#include "linux/kernel.h"
++#include "linux/sched.h"
++#include "linux/notifier.h"
++#include "linux/mm.h"
++#include "linux/types.h"
++#include "linux/tty.h"
++#include "linux/init.h"
++#include "linux/bootmem.h"
++#include "linux/spinlock.h"
++#include "linux/utsname.h"
++#include "linux/sysrq.h"
++#include "linux/seq_file.h"
++#include "linux/delay.h"
++#include "asm/page.h"
++#include "asm/pgtable.h"
++#include "asm/ptrace.h"
++#include "asm/elf.h"
++#include "asm/user.h"
++#include "ubd_user.h"
++#include "asm/current.h"
++#include "user_util.h"
++#include "kern_util.h"
++#include "kern.h"
++#include "mem_user.h"
++#include "mem.h"
++#include "umid.h"
++#include "initrd.h"
++#include "init.h"
++#include "os.h"
++#include "choose-mode.h"
++#include "mode_kern.h"
++#include "mode.h"
++
++#define DEFAULT_COMMAND_LINE "root=/dev/ubd0"
++
++struct cpuinfo_um boot_cpu_data = { 
++      .loops_per_jiffy        = 0,
++      .pgd_quick              = NULL,
++      .pmd_quick              = NULL,
++      .pte_quick              = NULL,
++      .pgtable_cache_sz       = 0,
++      .ipi_pipe               = { -1, -1 }
++};
++
++unsigned long thread_saved_pc(struct thread_struct *thread)
++{
++      return(os_process_pc(CHOOSE_MODE_PROC(thread_pid_tt, thread_pid_skas,
++                                            thread)));
++}
++
++static int show_cpuinfo(struct seq_file *m, void *v)
++{
++      int index = 0;
++
++#ifdef CONFIG_SMP
++      index = (struct cpuinfo_um *)v - cpu_data;
++      if (!(cpu_online_map & (1 << index)))
++              return 0;
++#endif
++
++      seq_printf(m, "processor\t: %d\n", index);
++      seq_printf(m, "vendor_id\t: User Mode Linux\n");
++      seq_printf(m, "model name\t: UML\n");
++      seq_printf(m, "mode\t\t: %s\n", CHOOSE_MODE("tt", "skas"));
++      seq_printf(m, "host\t\t: %s\n", host_info);
++      seq_printf(m, "bogomips\t: %lu.%02lu\n\n",
++                 loops_per_jiffy/(500000/HZ),
++                 (loops_per_jiffy/(5000/HZ)) % 100);
++
++      return(0);
++}
++
++static void *c_start(struct seq_file *m, loff_t *pos)
++{
++      return *pos < NR_CPUS ? cpu_data + *pos : NULL;
++}
++
++static void *c_next(struct seq_file *m, void *v, loff_t *pos)
++{
++      ++*pos;
++      return c_start(m, pos);
++}
++
++static void c_stop(struct seq_file *m, void *v)
++{
++}
++
++struct seq_operations cpuinfo_op = {
++      .start  = c_start,
++      .next   = c_next,
++      .stop   = c_stop,
++      .show   = show_cpuinfo,
++};
++
++pte_t * __bad_pagetable(void)
++{
++      panic("Someone should implement __bad_pagetable");
++      return(NULL);
++}
++
++/* Set in linux_main */
++unsigned long host_task_size;
++unsigned long task_size;
++unsigned long uml_start;
++
++/* Set in early boot */
++unsigned long uml_physmem;
++unsigned long uml_reserved;
++unsigned long start_vm;
++unsigned long end_vm;
++int ncpus = 1;
++
++#ifdef CONFIG_MODE_TT
++/* Pointer set in linux_main, the array itself is private to each thread,
++ * and changed at address space creation time so this poses no concurrency
++ * problems.
++ */
++static char *argv1_begin = NULL;
++static char *argv1_end = NULL;
++#endif
++
++/* Set in early boot */
++static int have_root __initdata = 0;
++long physmem_size = 32 * 1024 * 1024;
++
++void set_cmdline(char *cmd)
++{
++#ifdef CONFIG_MODE_TT
++      char *umid, *ptr;
++
++      if(CHOOSE_MODE(honeypot, 0)) return;
++
++      umid = get_umid(1);
++      if(umid != NULL){
++              snprintf(argv1_begin, 
++                       (argv1_end - argv1_begin) * sizeof(*ptr), 
++                       "(%s) ", umid);
++              ptr = &argv1_begin[strlen(argv1_begin)];
++      }
++      else ptr = argv1_begin;
++
++      snprintf(ptr, (argv1_end - ptr) * sizeof(*ptr), "[%s]", cmd);
++      memset(argv1_begin + strlen(argv1_begin), '\0', 
++             argv1_end - argv1_begin - strlen(argv1_begin));
++#endif
++}
++
++static char *usage_string = 
++"User Mode Linux v%s\n"
++"     available at http://user-mode-linux.sourceforge.net/\n\n";
++
++static int __init uml_version_setup(char *line, int *add)
++{
++      printf("%s\n", system_utsname.release);
++      exit(0);
++}
++
++__uml_setup("--version", uml_version_setup,
++"--version\n"
++"    Prints the version number of the kernel.\n\n"
++);
++
++static int __init uml_root_setup(char *line, int *add)
++{
++      have_root = 1;
++      return 0;
++}
++
++__uml_setup("root=", uml_root_setup,
++"root=<file containing the root fs>\n"
++"    This is actually used by the generic kernel in exactly the same\n"
++"    way as in any other kernel. If you configure a number of block\n"
++"    devices and want to boot off something other than ubd0, you \n"
++"    would use something like:\n"
++"        root=/dev/ubd5\n\n"
++);
++
++#ifdef CONFIG_SMP
++static int __init uml_ncpus_setup(char *line, int *add)
++{
++       if (!sscanf(line, "%d", &ncpus)) {
++               printf("Couldn't parse [%s]\n", line);
++               return -1;
++       }
++
++       return 0;
++}
++
++__uml_setup("ncpus=", uml_ncpus_setup,
++"ncpus=<# of desired CPUs>\n"
++"    This tells an SMP kernel how many virtual processors to start.\n\n" 
++);
++#endif
++
++int force_tt = 0;
++
++#if defined(CONFIG_MODE_TT) && defined(CONFIG_MODE_SKAS)
++#define DEFAULT_TT 0
++
++static int __init mode_tt_setup(char *line, int *add)
++{
++      force_tt = 1;
++      return(0);
++}
++
++#else
++#ifdef CONFIG_MODE_SKAS
++
++#define DEFAULT_TT 0
++
++static int __init mode_tt_setup(char *line, int *add)
++{
++      printf("CONFIG_MODE_TT disabled - 'mode=tt' ignored\n");
++      return(0);
++}
++
++#else
++#ifdef CONFIG_MODE_TT
++
++#define DEFAULT_TT 1
++
++static int __init mode_tt_setup(char *line, int *add)
++{
++      printf("CONFIG_MODE_SKAS disabled - 'mode=tt' redundant\n");
++      return(0);
++}
++
++#else
++
++#error Either CONFIG_MODE_TT or CONFIG_MODE_SKAS must be enabled
++
++#endif
++#endif
++#endif
++
++__uml_setup("mode=tt", mode_tt_setup,
++"mode=tt\n"
++"    When both CONFIG_MODE_TT and CONFIG_MODE_SKAS are enabled, this option\n"
++"    forces UML to run in tt (tracing thread) mode.  It is not the default\n"
++"    because it's slower and less secure than skas mode.\n\n"
++);
++
++int mode_tt = DEFAULT_TT;
++
++static int __init Usage(char *line, int *add)
++{
++      const char **p;
++
++      printf(usage_string, system_utsname.release);
++      p = &__uml_help_start;
++      while (p < &__uml_help_end) {
++              printf("%s", *p);
++              p++;
++      }
++      exit(0);
++}
++
++__uml_setup("--help", Usage,
++"--help\n"
++"    Prints this message.\n\n"
++);
++
++static int __init uml_checksetup(char *line, int *add)
++{
++      struct uml_param *p;
++
++      p = &__uml_setup_start;
++      while(p < &__uml_setup_end) {
++              int n;
++
++              n = strlen(p->str);
++              if(!strncmp(line, p->str, n)){
++                      if (p->setup_func(line + n, add)) return 1;
++              }
++              p++;
++      }
++      return 0;
++}
++
++static void __init uml_postsetup(void)
++{
++      initcall_t *p;
++
++      p = &__uml_postsetup_start;
++      while(p < &__uml_postsetup_end){
++              (*p)();
++              p++;
++      }
++      return;
++}
++
++/* Set during early boot */
++unsigned long brk_start;
++unsigned long end_iomem;
++
++#define MIN_VMALLOC (32 * 1024 * 1024)
++
++int linux_main(int argc, char **argv)
++{
++      unsigned long avail, diff;
++      unsigned long virtmem_size, max_physmem;
++      unsigned int i, add;
++
++      for (i = 1; i < argc; i++){
++              if((i == 1) && (argv[i][0] == ' ')) continue;
++              add = 1;
++              uml_checksetup(argv[i], &add);
++              if(add) add_arg(saved_command_line, argv[i]);
++      }
++      if(have_root == 0) add_arg(saved_command_line, DEFAULT_COMMAND_LINE);
++
++      mode_tt = force_tt ? 1 : !can_do_skas();
++      uml_start = CHOOSE_MODE_PROC(set_task_sizes_tt, set_task_sizes_skas, 0,
++                                   &host_task_size, &task_size);
++
++      /* Need to check this early because mmapping happens before the
++       * kernel is running.
++       */
++      check_tmpexec();
++
++      brk_start = (unsigned long) sbrk(0);
++      CHOOSE_MODE_PROC(before_mem_tt, before_mem_skas, brk_start);
++      /* Increase physical memory size for exec-shield users
++      so they actually get what they asked for. This should
++      add zero for non-exec shield users */
++      
++      diff = UML_ROUND_UP(brk_start) - UML_ROUND_UP(&_end);
++      if(diff > 1024 * 1024){
++              printf("Adding %ld bytes to physical memory to account for "
++                     "exec-shield gap\n", diff);
++              physmem_size += UML_ROUND_UP(brk_start) - UML_ROUND_UP(&_end);
++      }
++
++      uml_physmem = uml_start;
++
++      /* Reserve up to 4M after the current brk */
++      uml_reserved = ROUND_4M(brk_start) + (1 << 22);
++
++      setup_machinename(system_utsname.machine);
++
++#ifdef CONFIG_MODE_TT
++      argv1_begin = argv[1];
++      argv1_end = &argv[1][strlen(argv[1])];
++#endif
++  
++      highmem = 0;
++      iomem_size = (iomem_size + PAGE_SIZE - 1) & PAGE_MASK;
++      max_physmem = get_kmem_end() - uml_physmem - iomem_size - MIN_VMALLOC;
++
++      /* Zones have to begin on a 1 << MAX_ORDER page boundary,
++       * so this makes sure that's true for highmem
++       */
++      max_physmem &= ~((1 << (PAGE_SHIFT + MAX_ORDER)) - 1);
++      if(physmem_size + iomem_size > max_physmem){
++              highmem = physmem_size + iomem_size - max_physmem;
++              physmem_size -= highmem;
++#ifndef CONFIG_HIGHMEM
++              highmem = 0;
++              printf("CONFIG_HIGHMEM not enabled - physical memory shrunk "
++                     "to %ld bytes\n", physmem_size);
++#endif
++      }
++
++      high_physmem = uml_physmem + physmem_size;
++      end_iomem = high_physmem + iomem_size;
++      high_memory = (void *) end_iomem;
++
++      start_vm = VMALLOC_START;
++
++      setup_physmem(uml_physmem, uml_reserved, physmem_size, highmem);
++      if(init_maps(physmem_size, iomem_size, highmem)){
++              printf("Failed to allocate mem_map for %ld bytes of physical "
++                     "memory and %ld bytes of highmem\n", physmem_size,
++                     highmem);
++              exit(1);
++      }
++
++      virtmem_size = physmem_size;
++      avail = get_kmem_end() - start_vm;
++      if(physmem_size > avail) virtmem_size = avail;
++      end_vm = start_vm + virtmem_size;
++
++      if(virtmem_size < physmem_size)
++              printf("Kernel virtual memory size shrunk to %ld bytes\n",
++                     virtmem_size);
++
++      uml_postsetup();
++
++      task_protections((unsigned long) &init_task);
++      os_flush_stdout();
++
++      return(CHOOSE_MODE(start_uml_tt(), start_uml_skas()));
++}
++
++extern int uml_exitcode;
++
++static int panic_exit(struct notifier_block *self, unsigned long unused1,
++                    void *unused2)
++{
++#ifdef CONFIG_MAGIC_SYSRQ
++      handle_sysrq('p', &current->thread.regs, NULL, NULL);
++#endif
++      uml_exitcode = 1;
++      machine_halt();
++      return(0);
++}
++
++static struct notifier_block panic_exit_notifier = {
++      .notifier_call          = panic_exit,
++      .next                   = NULL,
++      .priority               = 0
++};
++
++void __init setup_arch(char **cmdline_p)
++{
++      notifier_chain_register(&panic_notifier_list, &panic_exit_notifier);
++      paging_init();
++      strcpy(command_line, saved_command_line);
++      *cmdline_p = command_line;
++      setup_hostinfo();
++}
++
++void __init check_bugs(void)
++{
++      arch_check_bugs();
++      check_ptrace();
++      check_sigio();
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/umid.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/umid.c    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/umid.c 2005-05-03 22:28:14.554397608 +0300
+@@ -0,0 +1,328 @@
++/* 
++ * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <stdio.h>
++#include <unistd.h>
++#include <errno.h>
++#include <string.h>
++#include <stdlib.h>
++#include <dirent.h>
++#include <signal.h>
++#include <sys/stat.h>
++#include <sys/param.h>
++#include "user.h"
++#include "umid.h"
++#include "init.h"
++#include "os.h"
++#include "user_util.h"
++#include "choose-mode.h"
++
++#define UMID_LEN 64
++#define UML_DIR "~/.uml/"
++
++/* Changed by set_umid and make_umid, which are run early in boot */
++static char umid[UMID_LEN] = { 0 };
++
++/* Changed by set_uml_dir and make_uml_dir, which are run early in boot */
++static char *uml_dir = UML_DIR;
++
++/* Changed by set_umid */
++static int umid_is_random = 1;
++static int umid_inited = 0;
++
++static int make_umid(int (*printer)(const char *fmt, ...));
++
++static int __init set_umid(char *name, int is_random, 
++                         int (*printer)(const char *fmt, ...))
++{
++      if(umid_inited){
++              (*printer)("Unique machine name can't be set twice\n");
++              return(-1);
++      }
++
++      if(strlen(name) > UMID_LEN - 1)
++              (*printer)("Unique machine name is being truncated to %d "
++                         "characters\n", UMID_LEN);
++      strncpy(umid, name, UMID_LEN - 1);
++      umid[UMID_LEN - 1] = '\0';
++
++      umid_is_random = is_random;
++      umid_inited = 1;
++      return 0;
++}
++
++static int __init set_umid_arg(char *name, int *add)
++{
++      *add = 0;
++      return(set_umid(name, 0, printf));
++}
++
++__uml_setup("umid=", set_umid_arg,
++"umid=<name>\n"
++"    This is used to assign a unique identity to this UML machine and\n"
++"    is used for naming the pid file and management console socket.\n\n"
++);
++
++int __init umid_file_name(char *name, char *buf, int len)
++{
++      int n;
++
++      if(!umid_inited && make_umid(printk)) return(-1);
++
++      n = strlen(uml_dir) + strlen(umid) + strlen(name) + 1;
++      if(n > len){
++              printk("umid_file_name : buffer too short\n");
++              return(-1);
++      }
++
++      sprintf(buf, "%s%s/%s", uml_dir, umid, name);
++      return(0);
++}
++
++extern int tracing_pid;
++
++static int __init create_pid_file(void)
++{
++      char file[strlen(uml_dir) + UMID_LEN + sizeof("/pid\0")];
++      char pid[sizeof("nnnnn\0")];
++      int fd, n;
++
++      if(umid_file_name("pid", file, sizeof(file))) return 0;
++
++      fd = os_open_file(file, of_create(of_excl(of_rdwr(OPENFLAGS()))), 
++                        0644);
++      if(fd < 0){
++              printf("Open of machine pid file \"%s\" failed - "
++                     "err = %d\n", file, -fd);
++              return 0;
++      }
++
++      sprintf(pid, "%d\n", os_getpid());
++      n = os_write_file(fd, pid, strlen(pid));
++      if(n != strlen(pid))
++              printf("Write of pid file failed - err = %d\n", -n);
++      os_close_file(fd);
++      return 0;
++}
++
++static int actually_do_remove(char *dir)
++{
++      DIR *directory;
++      struct dirent *ent;
++      int len;
++      char file[256];
++
++      directory = opendir(dir);
++      if(directory == NULL){
++              printk("actually_do_remove : couldn't open directory '%s', "
++                     "errno = %d\n", dir, errno);
++              return(1);
++      }
++      while((ent = readdir(directory)) != NULL){
++              if(!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
++                      continue;
++              len = strlen(dir) + sizeof("/") + strlen(ent->d_name) + 1;
++              if(len > sizeof(file)){
++                      printk("Not deleting '%s' from '%s' - name too long\n",
++                             ent->d_name, dir);
++                      continue;
++              }
++              sprintf(file, "%s/%s", dir, ent->d_name);
++              if(unlink(file) < 0){
++                      printk("actually_do_remove : couldn't remove '%s' "
++                             "from '%s', errno = %d\n", ent->d_name, dir, 
++                             errno);
++                      return(1);
++              }
++      }
++      if(rmdir(dir) < 0){
++              printk("actually_do_remove : couldn't rmdir '%s', "
++                     "errno = %d\n", dir, errno);
++              return(1);
++      }
++      return(0);
++}
++
++void remove_umid_dir(void)
++{
++      char dir[strlen(uml_dir) + UMID_LEN + 1];
++      if(!umid_inited) return;
++
++      sprintf(dir, "%s%s", uml_dir, umid);
++      actually_do_remove(dir);
++}
++
++char *get_umid(int only_if_set)
++{
++      if(only_if_set && umid_is_random) return(NULL);
++      return(umid);
++}
++
++int not_dead_yet(char *dir)
++{
++      char file[strlen(uml_dir) + UMID_LEN + sizeof("/pid\0")];
++      char pid[sizeof("nnnnn\0")], *end;
++      int dead, fd, p, n;
++
++      sprintf(file, "%s/pid", dir);
++      dead = 0;
++      fd = os_open_file(file, of_read(OPENFLAGS()), 0);
++      if(fd < 0){
++              if(fd != -ENOENT){
++                      printk("not_dead_yet : couldn't open pid file '%s', "
++                             "err = %d\n", file, -fd);
++                      return(1);
++              }
++              dead = 1;
++      }
++      if(fd > 0){
++              n = os_read_file(fd, pid, sizeof(pid));
++              if(n < 0){
++                      printk("not_dead_yet : couldn't read pid file '%s', "
++                             "err = %d\n", file, -n);
++                      return(1);
++              }
++              p = strtoul(pid, &end, 0);
++              if(end == pid){
++                      printk("not_dead_yet : couldn't parse pid file '%s', "
++                             "errno = %d\n", file, errno);
++                      dead = 1;
++              }
++              if(((kill(p, 0) < 0) && (errno == ESRCH)) ||
++                 (p == CHOOSE_MODE(tracing_pid, os_getpid())))
++                      dead = 1;
++      }
++      if(!dead) return(1);
++      return(actually_do_remove(dir));
++}
++
++static int __init set_uml_dir(char *name, int *add)
++{
++      if((strlen(name) > 0) && (name[strlen(name) - 1] != '/')){
++              uml_dir = malloc(strlen(name) + 2);
++              if(uml_dir == NULL){
++                      printf("Failed to malloc uml_dir - error = %d\n",
++                             errno);
++                      uml_dir = name;
++                      /* Return 0 here because do_initcalls doesn't look at
++                       * the return value.
++                       */
++                      return(0);
++              }
++              sprintf(uml_dir, "%s/", name);
++      }
++      else uml_dir = name;
++      return(0);
++}
++
++static int __init make_uml_dir(void)
++{
++      char dir[MAXPATHLEN + 1] = { '\0' };
++      int len;
++
++      if(*uml_dir == '~'){
++              char *home = getenv("HOME");
++
++              if(home == NULL){
++                      printf("make_uml_dir : no value in environment for "
++                             "$HOME\n");
++                      exit(1);
++              }
++              strncpy(dir, home, sizeof(dir));
++              uml_dir++;
++      }
++      len = strlen(dir);
++      strncat(dir, uml_dir, sizeof(dir) - len);
++      len = strlen(dir);
++      if((len > 0) && (len < sizeof(dir) - 1) && (dir[len - 1] != '/')){
++              dir[len] = '/';
++              dir[len + 1] = '\0';
++      }
++
++      uml_dir = malloc(strlen(dir) + 1);
++      if(uml_dir == NULL){
++              printf("make_uml_dir : malloc failed, errno = %d\n", errno);
++              exit(1);
++      }
++      strcpy(uml_dir, dir);
++      
++      if((mkdir(uml_dir, 0777) < 0) && (errno != EEXIST)){
++              printf("Failed to mkdir %s - errno = %i\n", uml_dir, errno);
++              return(-1);
++      }
++      return 0;
++}
++
++static int __init make_umid(int (*printer)(const char *fmt, ...))
++{
++      int fd, err;
++      char tmp[strlen(uml_dir) + UMID_LEN + 1];
++
++      strncpy(tmp, uml_dir, sizeof(tmp) - 1);
++      tmp[sizeof(tmp) - 1] = '\0';
++
++      if(!umid_inited){
++              strcat(tmp, "XXXXXX");
++              fd = mkstemp(tmp);
++              if(fd < 0){
++                      (*printer)("make_umid - mkstemp failed, errno = %d\n",
++                                 errno);
++                      return(1);
++              }
++
++              os_close_file(fd);
++              /* There's a nice tiny little race between this unlink and
++               * the mkdir below.  It'd be nice if there were a mkstemp
++               * for directories.
++               */
++              unlink(tmp);
++              set_umid(&tmp[strlen(uml_dir)], 1, printer);
++      }
++      
++      sprintf(tmp, "%s%s", uml_dir, umid);
++
++      err = mkdir(tmp, 0777);
++      if(err < 0){
++              if(errno == EEXIST){
++                      if(not_dead_yet(tmp)){
++                              (*printer)("umid '%s' is in use\n", umid);
++                              return(-1);
++                      }
++                      err = mkdir(tmp, 0777);
++              }
++      }
++      if(err < 0){
++              (*printer)("Failed to create %s - errno = %d\n", umid, errno);
++              return(-1);
++      }
++
++      return(0);
++}
++
++__uml_setup("uml_dir=", set_uml_dir,
++"uml_dir=<directory>\n"
++"    The location to place the pid and umid files.\n\n"
++);
++
++__uml_postsetup(make_uml_dir);
++
++static int __init make_umid_setup(void)
++{
++      return(make_umid(printf));
++}
++
++__uml_postsetup(make_umid_setup);
++__uml_postsetup(create_pid_file);
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/kernel/user_syms.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/user_syms.c       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/user_syms.c    2005-05-03 23:38:38.888201256 +0300
+@@ -0,0 +1,85 @@
++#include "linux/types.h"
++#include "linux/module.h"
++
++/* XXX Deleted a number of symbols because they clashed strangely with headers
++ * Add them back later.
++ */
++
++#if 1
++/* Some of this are builtin function (some are not but could in the future),
++ * so I *must* declare good prototypes for them and then EXPORT them.
++ * The kernel code uses the macro defined by include/linux/string.h,
++ * so I undef macros; the userspace code does not include that and I
++ * add an EXPORT for the glibc one.*/
++
++#undef strlen
++#undef memcpy
++#undef memset
++
++//extern size_t strlen(const char *);
++extern void *memcpy(void *, const void *, size_t);
++extern void *memset(void *, int, size_t);
++//extern int printf(const char *, ...);
++
++//EXPORT_SYMBOL(strlen);
++EXPORT_SYMBOL(memset);
++EXPORT_SYMBOL(memcpy);
++#undef strstr
++
++EXPORT_SYMBOL(strstr);
++
++#endif
++
++/* Here, instead, I can provide a fake prototype. Yes, someone cares: genksyms.
++ * However, the modules will use the CRC defined *here*, no matter if it is 
++ * good; so the versions of these symbols will always match
++ */
++#define EXPORT_SYMBOL_PROTO(sym)       \
++       int sym(void);                  \
++       EXPORT_SYMBOL(sym);
++
++EXPORT_SYMBOL_PROTO(__errno_location);
++
++EXPORT_SYMBOL_PROTO(access);
++EXPORT_SYMBOL_PROTO(open);
++EXPORT_SYMBOL_PROTO(open64);
++EXPORT_SYMBOL_PROTO(close);
++EXPORT_SYMBOL_PROTO(read);
++EXPORT_SYMBOL_PROTO(write);
++EXPORT_SYMBOL_PROTO(dup2);
++EXPORT_SYMBOL_PROTO(__xstat);
++EXPORT_SYMBOL_PROTO(__lxstat);
++EXPORT_SYMBOL_PROTO(__lxstat64);
++EXPORT_SYMBOL_PROTO(lseek);
++EXPORT_SYMBOL_PROTO(lseek64);
++EXPORT_SYMBOL_PROTO(chown);
++EXPORT_SYMBOL_PROTO(truncate);
++EXPORT_SYMBOL_PROTO(utime);
++EXPORT_SYMBOL_PROTO(chmod);
++EXPORT_SYMBOL_PROTO(rename);
++EXPORT_SYMBOL_PROTO(__xmknod);
++
++EXPORT_SYMBOL_PROTO(symlink);
++EXPORT_SYMBOL_PROTO(link);
++EXPORT_SYMBOL_PROTO(unlink);
++EXPORT_SYMBOL_PROTO(readlink);
++
++EXPORT_SYMBOL_PROTO(mkdir);
++EXPORT_SYMBOL_PROTO(rmdir);
++EXPORT_SYMBOL_PROTO(opendir);
++EXPORT_SYMBOL_PROTO(readdir);
++EXPORT_SYMBOL_PROTO(closedir);
++EXPORT_SYMBOL_PROTO(seekdir);
++EXPORT_SYMBOL_PROTO(telldir);
++
++EXPORT_SYMBOL_PROTO(ioctl);
++
++EXPORT_SYMBOL_PROTO(pread64);
++EXPORT_SYMBOL_PROTO(pwrite64);
++
++EXPORT_SYMBOL_PROTO(statfs);
++EXPORT_SYMBOL_PROTO(statfs64);
++
++EXPORT_SYMBOL_PROTO(getuid);
++
++EXPORT_SYMBOL_PROTO(printf);
+Index: linux-2.4.29/arch/um/kernel/user_util.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/kernel/user_util.c       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/kernel/user_util.c    2005-05-03 22:28:14.556397304 +0300
+@@ -0,0 +1,188 @@
++/* 
++ * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <limits.h>
++#include <setjmp.h>
++#include <sys/mman.h> 
++#include <sys/stat.h>
++#include <sys/ptrace.h>
++#include <sys/utsname.h>
++#include <sys/param.h>
++#include <sys/time.h>
++#include "asm/types.h"
++#include <ctype.h>
++#include <signal.h>
++#include <wait.h>
++#include <errno.h>
++#include <stdarg.h>
++#include <sched.h>
++#include <termios.h>
++#include <string.h>
++#include "user_util.h"
++#include "kern_util.h"
++#include "user.h"
++#include "mem_user.h"
++#include "init.h"
++#include "helper.h"
++#include "uml-config.h"
++
++#define COMMAND_LINE_SIZE _POSIX_ARG_MAX
++
++/* Changed in linux_main and setup_arch, which run before SMP is started */
++char saved_command_line[COMMAND_LINE_SIZE] = { 0 };
++char command_line[COMMAND_LINE_SIZE] = { 0 };
++
++void add_arg(char *cmd_line, char *arg)
++{
++      if (strlen(cmd_line) + strlen(arg) + 1 > COMMAND_LINE_SIZE) {
++              printf("add_arg: Too much command line!\n");
++              exit(1);
++      }
++      if(strlen(cmd_line) > 0) strcat(cmd_line, " ");
++      strcat(cmd_line, arg);
++}
++
++void stop(void)
++{
++      while(1) sleep(1000000);
++}
++
++void stack_protections(unsigned long address)
++{
++      int prot = PROT_READ | PROT_WRITE | PROT_EXEC;
++
++        if(mprotect((void *) address, page_size(), prot) < 0)
++              panic("protecting stack failed, errno = %d", errno);
++}
++
++void task_protections(unsigned long address)
++{
++      unsigned long guard = address + page_size();
++      unsigned long stack = guard + page_size();
++      int prot = 0, pages;
++#ifdef notdef
++      if(mprotect((void *) guard, page_size(), prot) < 0)
++              panic("protecting guard page failed, errno = %d", errno);
++#endif
++      pages = (1 << UML_CONFIG_KERNEL_STACK_ORDER) - 2;
++      prot = PROT_READ | PROT_WRITE | PROT_EXEC;
++      if(mprotect((void *) stack, pages * page_size(), prot) < 0)
++              panic("protecting stack failed, errno = %d", errno);
++}
++
++int wait_for_stop(int pid, int sig, int cont_type, void *relay)
++{
++      sigset_t *relay_signals = relay;
++      int status, ret;
++
++      while(1){
++              CATCH_EINTR(ret = waitpid(pid, &status, WUNTRACED));
++              if((ret < 0) ||
++                 !WIFSTOPPED(status) || (WSTOPSIG(status) != sig)){
++                      if(ret < 0){
++                              printk("wait failed, errno = %d\n",
++                                     errno);
++                      }
++                      else if(WIFEXITED(status)) 
++                              printk("process %d exited with status %d\n", 
++                                     pid, WEXITSTATUS(status));
++                      else if(WIFSIGNALED(status))
++                              printk("process %d exited with signal %d\n", 
++                                     pid, WTERMSIG(status));
++                      else if((WSTOPSIG(status) == SIGVTALRM) ||
++                              (WSTOPSIG(status) == SIGALRM) ||
++                              (WSTOPSIG(status) == SIGIO) ||
++                              (WSTOPSIG(status) == SIGPROF) ||
++                              (WSTOPSIG(status) == SIGCHLD) ||
++                              (WSTOPSIG(status) == SIGWINCH) ||
++                              (WSTOPSIG(status) == SIGINT)){
++                              ptrace(cont_type, pid, 0, WSTOPSIG(status));
++                              continue;
++                      }
++                      else if((relay_signals != NULL) &&
++                              sigismember(relay_signals, WSTOPSIG(status))){
++                              ptrace(cont_type, pid, 0, WSTOPSIG(status));
++                              continue;
++                      }
++                      else printk("process %d stopped with signal %d\n", 
++                                  pid, WSTOPSIG(status));
++                      panic("wait_for_stop failed to wait for %d to stop "
++                            "with %d\n", pid, sig);
++              }
++              return(status);
++      }
++}
++
++int raw(int fd)
++{
++      struct termios tt;
++      int err;
++
++      CATCH_EINTR(err = tcgetattr(fd, &tt));
++      if (err < 0) {
++              printk("tcgetattr failed, errno = %d\n", errno);
++              return(-errno);
++      }
++
++      cfmakeraw(&tt);
++
++      CATCH_EINTR(err = tcsetattr(fd, TCSADRAIN, &tt));
++      if (err < 0) {
++              printk("tcsetattr failed, errno = %d\n", errno);
++              return(-errno);
++      }
++
++      /* XXX tcsetattr could have applied only some changes
++       * (and cfmakeraw() is a set of changes) */
++      return(0);
++}
++
++void setup_machinename(char *machine_out)
++{
++      struct utsname host;
++
++      uname(&host);
++      strcpy(machine_out, host.machine);
++}
++
++char host_info[(_UTSNAME_LENGTH + 1) * 4 + _UTSNAME_NODENAME_LENGTH + 1];
++
++void setup_hostinfo(void)
++{
++      struct utsname host;
++
++      uname(&host);
++      sprintf(host_info, "%s %s %s %s %s", host.sysname, host.nodename,
++              host.release, host.version, host.machine);
++}
++
++int setjmp_wrapper(void (*proc)(void *, void *), ...)
++{
++        va_list args;
++      sigjmp_buf buf;
++      int n;
++
++      n = sigsetjmp(buf, 1);
++      if(n == 0){
++              va_start(args, proc);
++              (*proc)(&buf, &args);
++      }
++      va_end(args);
++      return(n);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/Makefile
+===================================================================
+--- linux-2.4.29.orig/arch/um/Makefile 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/Makefile      2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,191 @@
++# 
++# Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++# Licensed under the GPL
++#
++
++OS := $(shell uname -s)
++
++ARCH_DIR = arch/um
++
++core-y := kernel sys-$(SUBARCH) os-$(OS)
++drivers-y := fs drivers
++subdir-y := $(core-y) $(drivers-y)
++SUBDIRS += $(foreach dir,$(subdir-y),$(ARCH_DIR)/$(dir))
++
++CORE_FILES += $(foreach dir,$(core-y),$(ARCH_DIR)/$(dir)/built-in.o)
++DRIVERS += $(foreach dir,$(drivers-y),$(ARCH_DIR)/$(dir)/built-in.o)
++
++include $(ARCH_DIR)/Makefile-$(SUBARCH)
++include $(ARCH_DIR)/Makefile-os-$(OS)
++
++MAKEFILE-$(CONFIG_MODE_TT) += Makefile-tt
++MAKEFILE-$(CONFIG_MODE_SKAS) += Makefile-skas
++
++ifneq ($(MAKEFILE-y),)
++  include $(addprefix $(ARCH_DIR)/,$(MAKEFILE-y))
++endif
++
++EXTRAVERSION := $(EXTRAVERSION)-1um
++
++include/linux/version.h: arch/$(ARCH)/Makefile
++
++# We require bash because the vmlinux link and loader script cpp use bash
++# features.
++SHELL := /bin/bash
++
++# Recalculate MODLIB to reflect the EXTRAVERSION changes (via KERNELRELEASE)
++# The way the toplevel Makefile is written EXTRAVERSION is not supposed
++# to be changed outside the toplevel Makefile, but recalculating MODLIB is
++# a sufficient workaround until we no longer need architecture dependent
++# EXTRAVERSION...
++MODLIB := $(INSTALL_MOD_PATH)/lib/modules/$(KERNELRELEASE)
++
++ifeq ($(CONFIG_DEBUGSYM),y)
++CFLAGS := $(subst -fomit-frame-pointer,,$(CFLAGS))
++endif
++
++CFLAGS-$(CONFIG_DEBUGSYM) += -g
++
++ARCH_INCLUDE = -I$(TOPDIR)/$(ARCH_DIR)/include
++
++# -Derrno=kernel_errno - This turns all kernel references to errno into
++# kernel_errno to separate them from the libc errno.  This allows -fno-common
++# in CFLAGS.  Otherwise, it would cause ld to complain about the two different
++# errnos.
++
++CFLAGS += $(ARCH_CFLAGS) $(CFLAGS-y) -D__arch_um__ -DSUBARCH=\"$(SUBARCH)\" \
++      -D_LARGEFILE64_SOURCE $(ARCH_INCLUDE) -Derrno=kernel_errno \
++      $(MODE_INCLUDE)
++
++check_gcc = $(shell if $(CC) $(1) -S -o /dev/null -xc /dev/null > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi)
++
++CFLAGS += $(call check_gcc,-fno-unit-at-a-time,)
++
++LINKFLAGS += -r
++
++LINK_WRAPS = -Wl,--wrap,malloc -Wl,--wrap,free -Wl,--wrap,calloc
++
++# These are needed for clean and mrproper, since in that case .config is not
++# included; the values here are meaningless
++
++CONFIG_NEST_LEVEL ?= 0
++CONFIG_KERNEL_HALF_GIGS ?= 0
++
++SIZE = (($(CONFIG_NEST_LEVEL) + $(CONFIG_KERNEL_HALF_GIGS)) * 0x20000000)
++
++# These aren't in Makefile-tt because they are needed in the !CONFIG_MODE_TT +
++# CONFIG_MODE_SKAS + CONFIG_STATIC_LINK case.
++
++LINK_TT = -static
++LD_SCRIPT_TT := link.ld
++
++ifeq ($(CONFIG_STATIC_LINK),y)
++  LINK-y += $(LINK_TT)
++  LD_SCRIPT-y := $(LD_SCRIPT_TT)
++else
++ifeq ($(CONFIG_MODE_TT),y)
++  LINK-y += $(LINK_TT)
++  LD_SCRIPT-y := $(LD_SCRIPT_TT)
++else
++ifeq ($(CONFIG_MODE_SKAS),y)
++  LINK-y += $(LINK_SKAS)
++  LD_SCRIPT-y := $(LD_SCRIPT_SKAS)
++endif
++endif
++endif
++
++LD_SCRIPT-y := $(ARCH_DIR)/kernel/$(LD_SCRIPT-y)
++M4_MODE_TT := $(shell [ "$(CONFIG_MODE_TT)" = "y" ] && echo -DMODE_TT)
++
++ifndef START
++  START = $$(($(TOP_ADDR) - $(SIZE)))
++endif
++
++$(LD_SCRIPT-y): $(LD_SCRIPT-y).in
++      pages=$$(( 1 << $(CONFIG_KERNEL_STACK_ORDER) )) ; \
++      m4 -DSTART=$(START) -DELF_ARCH=$(ELF_ARCH) \
++              -DELF_FORMAT=$(ELF_FORMAT) $(M4_MODE_TT) \
++              -DKERNEL_STACK_SIZE=$$(( 4096 * $$pages )) $< > $@
++
++SYMLINK_HEADERS = archparam.h system.h sigcontext.h processor.h ptrace.h \
++      arch-signal.h
++SYMLINK_HEADERS := $(foreach header,$(SYMLINK_HEADERS),include/asm-um/$(header))
++
++ARCH_SYMLINKS = include/asm-um/arch arch/um/include/sysdep arch/um/os \
++      $(SYMLINK_HEADERS) $(ARCH_DIR)/include/uml-config.h
++
++ifeq ($(CONFIG_MODE_SKAS), y)
++$(SYS_HEADERS) : $(ARCH_DIR)/kernel/skas/include/skas_ptregs.h
++endif
++
++GEN_HEADERS += $(ARCH_DIR)/include/task.h $(ARCH_DIR)/include/kern_constants.h 
++
++setup: $(ARCH_SYMLINKS) $(SYS_HEADERS) $(GEN_HEADERS) 
++
++linux: setup vmlinux $(LD_SCRIPT-y)
++      mv vmlinux vmlinux.o
++      $(CC) -Wl,-T,$(LD_SCRIPT-y) $(LINK-y) $(LINK_WRAPS) \
++              -o linux vmlinux.o -L/usr/lib -lutil
++
++USER_CFLAGS := $(patsubst -I%,,$(CFLAGS))
++USER_CFLAGS := $(patsubst -Derrno=kernel_errno,,$(USER_CFLAGS))
++USER_CFLAGS := $(patsubst -D__KERNEL__,,$(USER_CFLAGS)) $(ARCH_INCLUDE) \
++      $(MODE_INCLUDE)
++
++# To get a definition of F_SETSIG
++USER_CFLAGS += -D_GNU_SOURCE
++
++CLEAN_FILES += linux x.i gmon.out $(ARCH_DIR)/link.ld $(ARCH_DIR)/dyn_link.ld \
++      $(GEN_HEADERS) 
++# $(ARCH_DIR)/include/uml-config.h removed temporarily because this causes
++# make to fail after make clean
++
++archmrproper:
++      rm -f $(SYMLINK_HEADERS) $(ARCH_SYMLINKS) include/asm \
++              $(LD_SCRIPT) $(addprefix $(ARCH_DIR)/kernel/,$(KERN_SYMLINKS))
++
++archclean: sysclean
++      find . \( -name '*.bb' -o -name '*.bbg' -o -name '*.da' \
++              -o -name '*.gcov' \) -type f -print | xargs rm -f
++      cd $(ARCH_DIR) ; \
++      for dir in $(subdir-y) util ; do $(MAKE) -C $$dir clean; done
++
++archdep: 
++
++$(SYMLINK_HEADERS):
++      cd $(TOPDIR)/$(dir $@) ; \
++      ln -sf $(basename $(notdir $@))-$(SUBARCH)$(suffix $@) $(notdir $@)
++
++include/asm-um/arch:
++      cd $(TOPDIR)/include/asm-um && ln -sf ../asm-$(SUBARCH) arch
++
++arch/um/include/sysdep:
++      cd $(TOPDIR)/arch/um/include && ln -sf sysdep-$(SUBARCH) sysdep
++
++arch/um/os:
++      cd $(ARCH_DIR) && ln -sf os-$(OS) os
++
++$(ARCH_DIR)/include/task.h : $(ARCH_DIR)/util/mk_task
++      $< > $@
++
++$(ARCH_DIR)/include/kern_constants.h : $(ARCH_DIR)/util/mk_constants
++      $< > $@
++
++$(ARCH_DIR)/include/uml-config.h : $(TOPDIR)/include/linux/autoconf.h
++      sed 's/ CONFIG/ UML_CONFIG/' $(TOPDIR)/include/linux/autoconf.h > $@
++
++$(ARCH_DIR)/util/mk_task : $(ARCH_DIR)/util/mk_task_user.c \
++      $(ARCH_DIR)/util/mk_task_kern.c $(SYS_HEADERS)
++      $(MAKE) $(MFLAGS) -C $(ARCH_DIR)/util mk_task
++
++$(ARCH_DIR)/util/mk_constants : $(ARCH_DIR)/util/mk_constants_user.c \
++      $(ARCH_DIR)/util/mk_constants_kern.c 
++      $(MAKE) $(MFLAGS) -C $(ARCH_DIR)/util mk_constants
++
++export SUBARCH USER_CFLAGS OS
++
++all: linux
++
++define archhelp
++  echo  '* linux      - Binary kernel image (./linux)'
++endef
+Index: linux-2.4.29/arch/um/Makefile-i386
+===================================================================
+--- linux-2.4.29.orig/arch/um/Makefile-i386    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/Makefile-i386 2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,46 @@
++ifeq ($(CONFIG_HOST_2G_2G),y)
++TOP_ADDR = 0x80000000
++else
++TOP_ADDR = 0xc0000000
++endif
++
++ifeq ($(CONFIG_MODE_SKAS),y)
++  ifneq ($(CONFIG_MODE_TT),y)
++     START = 0x8048000
++  endif
++endif
++
++ARCH_CFLAGS = -U__$(SUBARCH)__ -U$(SUBARCH)
++
++ifneq ($(CONFIG_GPROF),y)
++ARCH_CFLAGS += -DUM_FASTCALL
++endif
++
++ELF_ARCH = $(SUBARCH)
++ELF_FORMAT = elf32-$(SUBARCH)
++
++I386_H = $(ARCH_DIR)/include/sysdep-i386
++SYS = $(ARCH_DIR)/sys-i386
++UTIL = $(SYS)/util
++SUBDIRS += $(UTIL)
++
++SYS_HEADERS = $(I386_H)/sc.h $(I386_H)/thread.h
++
++$(I386_H)/sc.h : $(UTIL)/mk_sc
++      $(UTIL)/mk_sc > $@
++
++$(I386_H)/thread.h : $(UTIL)/mk_thread
++      $(UTIL)/mk_thread > $@
++
++$(UTIL)/mk_sc : $(UTIL)/mk_sc.c
++      $(MAKE) -C $(UTIL) mk_sc
++
++$(UTIL)/mk_thread : $(UTIL)/mk_thread_user.c $(UTIL)/mk_thread_kern.c \
++      $(I386_H)/sc.h
++      $(MAKE) -C $(UTIL) mk_thread
++
++sysclean :
++      rm -f $(SYS_HEADERS)
++      $(MAKE) -C $(UTIL) clean
++      $(MAKE) -C $(SYS) clean
++
+Index: linux-2.4.29/arch/um/Makefile-ia64
+===================================================================
+--- linux-2.4.29.orig/arch/um/Makefile-ia64    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/Makefile-ia64 2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1 @@
++START_ADDR = 0x1000000000000000
+Index: linux-2.4.29/arch/um/Makefile-os-Linux
+===================================================================
+--- linux-2.4.29.orig/arch/um/Makefile-os-Linux        1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/Makefile-os-Linux     2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,7 @@
++# 
++# Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
++# Licensed under the GPL
++#
++
++SUBDIRS += $(ARCH_DIR)/os-$(OS)/drivers
++DRIVERS += $(ARCH_DIR)/os-$(OS)/drivers/drivers.o
+Index: linux-2.4.29/arch/um/Makefile-ppc
+===================================================================
+--- linux-2.4.29.orig/arch/um/Makefile-ppc     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/Makefile-ppc  2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,9 @@
++ifeq ($(CONFIG_HOST_2G_2G), y)
++START_ADDR = 0x80000000
++else
++START_ADDR = 0xc0000000
++endif
++ARCH_CFLAGS = -U__powerpc__ -D__UM_PPC__
++
++# The arch is ppc, but the elf32 name is powerpc
++ELF_SUBARCH = powerpc
+Index: linux-2.4.29/arch/um/Makefile-skas
+===================================================================
+--- linux-2.4.29.orig/arch/um/Makefile-skas    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/Makefile-skas 2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,20 @@
++# 
++# Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++# Licensed under the GPL
++#
++
++PROFILE += -pg
++
++CFLAGS-$(CONFIG_GCOV) += -fprofile-arcs -ftest-coverage
++CFLAGS-$(CONFIG_GPROF) += $(PROFILE)
++LINK-$(CONFIG_GPROF) += $(PROFILE)
++
++MODE_INCLUDE += -I$(TOPDIR)/$(ARCH_DIR)/kernel/skas/include
++
++LINK_SKAS = -Wl,-rpath,/lib 
++LD_SCRIPT_SKAS = dyn_link.ld
++
++GEN_HEADERS += $(ARCH_DIR)/kernel/skas/include/skas_ptregs.h
++
++$(ARCH_DIR)/kernel/skas/include/skas_ptregs.h :
++      $(MAKE) -C $(ARCH_DIR)/kernel/skas include/skas_ptregs.h
+Index: linux-2.4.29/arch/um/Makefile-tt
+===================================================================
+--- linux-2.4.29.orig/arch/um/Makefile-tt      1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/Makefile-tt   2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,7 @@
++# 
++# Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++# Licensed under the GPL
++#
++
++MODE_INCLUDE += -I$(TOPDIR)/$(ARCH_DIR)/kernel/tt/include
++
+Index: linux-2.4.29/arch/um/os-Linux/aio.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/os-Linux/aio.c   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/os-Linux/aio.c        2005-05-03 22:28:14.563396240 +0300
+@@ -0,0 +1,404 @@
++/* 
++ * Copyright (C) 2004 Jeff Dike (jdike@addtoit.com)
++ * Licensed under the GPL
++ */
++
++#include <stdlib.h>
++#include <unistd.h>
++#include <signal.h>
++#include <errno.h>
++#include <sched.h>
++#include <sys/syscall.h>
++#include "os.h"
++#include "helper.h"
++#include "aio.h"
++#include "init.h"
++#include "user.h"
++#include "mode.h"
++
++struct aio_thread_req {
++      enum aio_type type;
++      int io_fd;
++      unsigned long long offset;
++      char *buf;
++      int len;
++      int reply_fd;
++      void *data;
++};
++
++static int aio_req_fd_r = -1;
++static int aio_req_fd_w = -1;
++
++#if defined(HAVE_AIO_ABI)
++#include <linux/aio_abi.h>
++
++/* If we have the headers, we are going to build with AIO enabled.
++ * If we don't have aio in libc, we define the necessary stubs here.
++ */
++
++#if !defined(HAVE_AIO_LIBC)
++
++#define __NR_io_setup 245
++#define __NR_io_getevents 247
++#define __NR_io_submit 248
++
++static long io_setup(int n, aio_context_t *ctxp)
++{
++  return(syscall(__NR_io_setup, n, ctxp));
++}
++
++static long io_submit(aio_context_t ctx, long nr, struct iocb **iocbpp)
++{
++  return(syscall(__NR_io_submit, ctx, nr, iocbpp));
++}
++
++static long io_getevents(aio_context_t ctx_id, long min_nr, long nr,
++                       struct io_event *events, struct timespec *timeout)
++{
++  return(syscall(__NR_io_getevents, ctx_id, min_nr, nr, events, timeout));
++}
++
++#endif
++
++/* The AIO_MMAP cases force the mmapped page into memory here
++ * rather than in whatever place first touches the data.  I used
++ * to do this by touching the page, but that's delicate because
++ * gcc is prone to optimizing that away.  So, what's done here
++ * is we read from the descriptor from which the page was 
++ * mapped.  The caller is required to pass an offset which is
++ * inside the page that was mapped.  Thus, when the read 
++ * returns, we know that the page is in the page cache, and
++ * that it now backs the mmapped area.
++ */
++
++static int do_aio(aio_context_t ctx, enum aio_type type, int fd, char *buf, 
++                int len, unsigned long long offset, void *data)
++{
++      struct iocb iocb, *iocbp = &iocb;
++      char c;
++      int err;
++
++      iocb = ((struct iocb) { .aio_data       = (unsigned long) data,
++                              .aio_reqprio    = 0,
++                              .aio_fildes     = fd,
++                              .aio_buf        = (unsigned long) buf,
++                              .aio_nbytes     = len,
++                              .aio_offset     = offset,
++                              .aio_reserved1  = 0,
++                              .aio_reserved2  = 0,
++                              .aio_reserved3  = 0 });
++
++      switch(type){
++      case AIO_READ:
++              iocb.aio_lio_opcode = IOCB_CMD_PREAD;
++              err = io_submit(ctx, 1, &iocbp);
++              break;
++      case AIO_WRITE:
++              iocb.aio_lio_opcode = IOCB_CMD_PWRITE;
++              err = io_submit(ctx, 1, &iocbp);
++              break;
++      case AIO_MMAP:
++              iocb.aio_lio_opcode = IOCB_CMD_PREAD;
++              iocb.aio_buf = (unsigned long) &c;
++              iocb.aio_nbytes = sizeof(c);
++              err = io_submit(ctx, 1, &iocbp);
++              break;
++      default:
++              printk("Bogus op in do_aio - %d\n", type);
++              err = -EINVAL;
++              break;
++      }
++      if(err > 0)
++              err = 0;
++
++      return(err);    
++}
++
++static aio_context_t ctx = 0;
++
++static int aio_thread(void *arg)
++{
++      struct aio_thread_reply reply;
++      struct io_event event;
++      int err, n, reply_fd;
++
++      signal(SIGWINCH, SIG_IGN);
++
++      while(1){
++              n = io_getevents(ctx, 1, 1, &event, NULL);
++              if(n < 0){
++                      if(errno == EINTR)
++                              continue;
++                      printk("aio_thread - io_getevents failed, "
++                             "errno = %d\n", errno);
++              }
++              else {
++                      reply = ((struct aio_thread_reply) 
++                              { .data = (void *) event.data,
++                                .err  = event.res });
++                      reply_fd = 
++                              ((struct aio_context *) event.data)->reply_fd;
++                      err = os_write_file(reply_fd, &reply, sizeof(reply));
++                      if(err != sizeof(reply))
++                              printk("not_aio_thread - write failed, "
++                                     "fd = %d, err = %d\n", 
++                                     aio_req_fd_r, -err);
++              }
++      }
++      return(0);
++}
++
++#endif
++
++static int do_not_aio(struct aio_thread_req *req)
++{
++      char c;
++      int err;
++
++      switch(req->type){
++      case AIO_READ:
++              err = os_seek_file(req->io_fd, req->offset);
++              if(err)
++                      goto out;
++
++              err = os_read_file(req->io_fd, req->buf, req->len);
++              break;
++      case AIO_WRITE:
++              err = os_seek_file(req->io_fd, req->offset);
++              if(err)
++                      goto out;
++
++              err = os_write_file(req->io_fd, req->buf, req->len);
++              break;
++      case AIO_MMAP:
++              err = os_seek_file(req->io_fd, req->offset);
++              if(err)
++                      goto out;
++
++              err = os_read_file(req->io_fd, &c, sizeof(c));
++              break;
++      default:
++              printk("do_not_aio - bad request type : %d\n", req->type);
++              err = -EINVAL;
++              break;
++      }
++
++ out:
++      return(err);
++}
++
++static int not_aio_thread(void *arg)
++{
++      struct aio_thread_req req;
++      struct aio_thread_reply reply;
++      int err;
++
++      signal(SIGWINCH, SIG_IGN);
++      while(1){
++              err = os_read_file(aio_req_fd_r, &req, sizeof(req));
++              if(err != sizeof(req)){
++                      if(err < 0)
++                              printk("not_aio_thread - read failed, fd = %d, "
++                                     "err = %d\n", aio_req_fd_r, -err);
++                      else {
++                              printk("not_aio_thread - short read, fd = %d, "
++                                     "length = %d\n", aio_req_fd_r, err);
++                      }
++                      continue;
++              }
++              err = do_not_aio(&req);
++              reply = ((struct aio_thread_reply) { .data      = req.data,
++                                                   .err       = err });
++              err = os_write_file(req.reply_fd, &reply, sizeof(reply));
++              if(err != sizeof(reply))
++                      printk("not_aio_thread - write failed, fd = %d, "
++                             "err = %d\n", aio_req_fd_r, -err);
++      }
++}
++
++static int aio_pid = -1;
++
++static int init_aio_24(void)
++{
++      unsigned long stack;
++      int fds[2], err;
++      
++      err = os_pipe(fds, 1, 1);
++      if(err)
++              goto out;
++
++      aio_req_fd_w = fds[0];
++      aio_req_fd_r = fds[1];
++      err = run_helper_thread(not_aio_thread, NULL, 
++                              CLONE_FILES | CLONE_VM | SIGCHLD, &stack, 0);
++      if(err < 0)
++              goto out_close_pipe;
++
++      aio_pid = err;
++      goto out;
++
++ out_close_pipe:
++      os_close_file(fds[0]);
++      os_close_file(fds[1]);
++      aio_req_fd_w = -1;
++      aio_req_fd_r = -1;      
++ out:
++      return(0);
++}
++
++#ifdef HAVE_AIO_ABI
++#define DEFAULT_24_AIO 0
++static int init_aio_26(void)
++{
++      unsigned long stack;
++      int err;
++      
++      if(io_setup(256, &ctx)){
++              printk("aio_thread failed to initialize context, err = %d\n",
++                     errno);
++              return(-errno);
++      }
++
++      err = run_helper_thread(aio_thread, NULL, 
++                              CLONE_FILES | CLONE_VM | SIGCHLD, &stack, 0);
++      if(err < 0)
++              return(-errno);
++
++      aio_pid = err;
++      err = 0;
++ out:
++      return(err);
++}
++
++int submit_aio_26(enum aio_type type, int io_fd, char *buf, int len, 
++                unsigned long long offset, int reply_fd, void *data)
++{
++      struct aio_thread_reply reply;
++      int err;
++
++      ((struct aio_context *) data)->reply_fd = reply_fd;
++
++      err = do_aio(ctx, type, io_fd, buf, len, offset, data);
++      if(err){
++              reply = ((struct aio_thread_reply) { .data = data,
++                                                   .err  = err });
++              err = os_write_file(reply_fd, &reply, sizeof(reply));
++              if(err != sizeof(reply))
++                      printk("submit_aio_26 - write failed, "
++                             "fd = %d, err = %d\n", reply_fd, -err);
++              else err = 0;
++      }
++
++      return(err);
++}
++
++#else
++#define DEFAULT_24_AIO 1
++static int init_aio_26(void)
++{
++      return(-ENOSYS);
++}
++
++int submit_aio_26(enum aio_type type, int io_fd, char *buf, int len, 
++                unsigned long long offset, int reply_fd, void *data)
++{
++      return(-ENOSYS);
++}
++#endif
++
++static int aio_24 = DEFAULT_24_AIO;
++
++static int __init set_aio_24(char *name, int *add)
++{
++      aio_24 = 1;
++      return(0);
++}
++
++__uml_setup("aio=2.4", set_aio_24,
++"aio=2.4\n"
++"    This is used to force UML to use 2.4-style AIO even when 2.6 AIO is\n"
++"    available.  2.4 AIO is a single thread that handles one request at a\n"
++"    time, synchronously.  2.6 AIO is a thread which uses 2.5 AIO interface\n"
++"    to handle an arbitrary number of pending requests.  2.6 AIO is not\n"
++"    available in tt mode, on 2.4 hosts, or when UML is built with\n"
++"    /usr/include/linux/aio_abi no available.\n\n"
++);
++
++static int init_aio(void)
++{
++      int err;
++
++      CHOOSE_MODE(({ 
++              if(!aio_24){ 
++                      printk("Disabling 2.6 AIO in tt mode\n");
++                      aio_24 = 1;
++              } }), (void) 0);
++
++      if(!aio_24){
++              err = init_aio_26();
++              if(err && (errno == ENOSYS)){
++                      printk("2.6 AIO not supported on the host - "
++                             "reverting to 2.4 AIO\n");
++                      aio_24 = 1;
++              }
++              else return(err);
++      }
++
++      if(aio_24)
++              return(init_aio_24());
++
++      return(0);
++}
++
++__initcall(init_aio);
++
++static void exit_aio(void)
++{
++      if(aio_pid != -1)
++              os_kill_process(aio_pid, 1);
++}
++
++__uml_exitcall(exit_aio);
++
++int submit_aio_24(enum aio_type type, int io_fd, char *buf, int len, 
++                unsigned long long offset, int reply_fd, void *data)
++{
++      struct aio_thread_req req = { .type             = type,
++                                    .io_fd            = io_fd,
++                                    .offset           = offset,
++                                    .buf              = buf,
++                                    .len              = len,
++                                    .reply_fd         = reply_fd,
++                                    .data             = data,
++      };
++      int err;
++
++      err = os_write_file(aio_req_fd_w, &req, sizeof(req));
++      if(err == sizeof(req))
++              err = 0;
++
++      return(err);
++}
++
++int submit_aio(enum aio_type type, int io_fd, char *buf, int len, 
++             unsigned long long offset, int reply_fd, void *data)
++{
++      if(aio_24)
++              return(submit_aio_24(type, io_fd, buf, len, offset, reply_fd, 
++                                   data));
++      else {
++              return(submit_aio_26(type, io_fd, buf, len, offset, reply_fd, 
++                                   data));
++      }
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/os-Linux/drivers/etap.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/os-Linux/drivers/etap.h  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/os-Linux/drivers/etap.h       2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,27 @@
++/* 
++ * Copyright (C) 2001 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "net_user.h"
++
++struct ethertap_data {
++      char *dev_name;
++      char *gate_addr;
++      int data_fd;
++      int control_fd;
++      void *dev;
++};
++
++extern struct net_user_info ethertap_user_info;
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/os-Linux/drivers/ethertap_kern.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/os-Linux/drivers/ethertap_kern.c 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/os-Linux/drivers/ethertap_kern.c      2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,121 @@
++/*
++ * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and 
++ * James Leu (jleu@mindspring.net).
++ * Copyright (C) 2001 by various other people who didn't put their name here.
++ * Licensed under the GPL.
++ */
++
++#include "linux/init.h"
++#include "linux/netdevice.h"
++#include "linux/etherdevice.h"
++#include "net_kern.h"
++#include "net_user.h"
++#include "etap.h"
++
++struct ethertap_init {
++      char *dev_name;
++      char *gate_addr;
++};
++
++static void etap_init(struct net_device *dev, void *data)
++{
++      struct uml_net_private *pri;
++      struct ethertap_data *epri;
++      struct ethertap_init *init = data;
++
++      init_etherdev(dev, 0);
++      pri = dev->priv;
++      epri = (struct ethertap_data *) pri->user;
++      *epri = ((struct ethertap_data)
++              { .dev_name             = init->dev_name,
++                .gate_addr            = init->gate_addr,
++                .data_fd              = -1,
++                .control_fd           = -1,
++                .dev                  = dev });
++
++      printk("ethertap backend - %s", epri->dev_name);
++      if(epri->gate_addr != NULL) 
++              printk(", IP = %s", epri->gate_addr);
++      printk("\n");
++}
++
++static int etap_read(int fd, struct sk_buff **skb, struct uml_net_private *lp)
++{
++      int len;
++
++      *skb = ether_adjust_skb(*skb, ETH_HEADER_ETHERTAP);
++      if(*skb == NULL) return(-ENOMEM);
++      len = net_recvfrom(fd, (*skb)->mac.raw, 
++                         (*skb)->dev->mtu + 2 * ETH_HEADER_ETHERTAP);
++      if(len <= 0) return(len);
++      skb_pull(*skb, 2);
++      len -= 2;
++      return(len);
++}
++
++static int etap_write(int fd, struct sk_buff **skb, struct uml_net_private *lp)
++{
++      if(skb_headroom(*skb) < 2){
++              struct sk_buff *skb2;
++
++              skb2 = skb_realloc_headroom(*skb, 2);
++              dev_kfree_skb(*skb);
++              if (skb2 == NULL) return(-ENOMEM);
++              *skb = skb2;
++      }
++      skb_push(*skb, 2);
++      return(net_send(fd, (*skb)->data, (*skb)->len));
++}
++
++struct net_kern_info ethertap_kern_info = {
++      .init                   = etap_init,
++      .protocol               = eth_protocol,
++      .read                   = etap_read,
++      .write                  = etap_write,
++};
++
++int ethertap_setup(char *str, char **mac_out, void *data)
++{
++      struct ethertap_init *init = data;
++
++      *init = ((struct ethertap_init)
++              { .dev_name     = NULL,
++                .gate_addr    = NULL });
++      if(tap_setup_common(str, "ethertap", &init->dev_name, mac_out,
++                          &init->gate_addr))
++              return(0);
++      if(init->dev_name == NULL){
++              printk("ethertap_setup : Missing tap device name\n");
++              return(0);
++      }
++
++      return(1);
++}
++
++static struct transport ethertap_transport = {
++      .list           = LIST_HEAD_INIT(ethertap_transport.list),
++      .name           = "ethertap",
++      .setup          = ethertap_setup,
++      .user           = &ethertap_user_info,
++      .kern           = &ethertap_kern_info,
++      .private_size   = sizeof(struct ethertap_data),
++};
++
++static int register_ethertap(void)
++{
++      register_transport(&ethertap_transport);
++      return(1);
++}
++
++__initcall(register_ethertap);
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/os-Linux/drivers/ethertap_user.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/os-Linux/drivers/ethertap_user.c 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/os-Linux/drivers/ethertap_user.c      2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,240 @@
++/*
++ * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and 
++ * James Leu (jleu@mindspring.net).
++ * Copyright (C) 2001 by various other people who didn't put their name here.
++ * Licensed under the GPL.
++ */
++
++#include <stdio.h>
++#include <unistd.h>
++#include <stddef.h>
++#include <stdlib.h>
++#include <sys/errno.h>
++#include <sys/socket.h>
++#include <sys/wait.h>
++#include <sys/un.h>
++#include <net/if.h>
++#include "user.h"
++#include "kern_util.h"
++#include "user_util.h"
++#include "net_user.h"
++#include "etap.h"
++#include "helper.h"
++#include "os.h"
++
++#define MAX_PACKET ETH_MAX_PACKET
++
++void etap_user_init(void *data, void *dev)
++{
++      struct ethertap_data *pri = data;
++
++      pri->dev = dev;
++}
++
++struct addr_change {
++      enum { ADD_ADDR, DEL_ADDR } what;
++      unsigned char addr[4];
++      unsigned char netmask[4];
++};
++
++static void etap_change(int op, unsigned char *addr, unsigned char *netmask,
++                      int fd)
++{
++      struct addr_change change;
++      void *output;
++      int n;
++
++      change.what = op;
++      memcpy(change.addr, addr, sizeof(change.addr));
++      memcpy(change.netmask, netmask, sizeof(change.netmask));
++      n = os_write_file(fd, &change, sizeof(change));
++      if(n != sizeof(change))
++              printk("etap_change - request failed, err = %d\n", -n);
++      output = um_kmalloc(page_size());
++      if(output == NULL)
++              printk("etap_change : Failed to allocate output buffer\n");
++      read_output(fd, output, page_size());
++      if(output != NULL){
++              printk("%s", output);
++              kfree(output);
++      }
++}
++
++static void etap_open_addr(unsigned char *addr, unsigned char *netmask,
++                         void *arg)
++{
++      etap_change(ADD_ADDR, addr, netmask, *((int *) arg));
++}
++
++static void etap_close_addr(unsigned char *addr, unsigned char *netmask,
++                          void *arg)
++{
++      etap_change(DEL_ADDR, addr, netmask, *((int *) arg));
++}
++
++struct etap_pre_exec_data {
++      int control_remote;
++      int control_me;
++      int data_me;
++};
++
++static void etap_pre_exec(void *arg)
++{
++      struct etap_pre_exec_data *data = arg;
++
++      dup2(data->control_remote, 1);
++      os_close_file(data->data_me);
++      os_close_file(data->control_me);
++}
++
++static int etap_tramp(char *dev, char *gate, int control_me, 
++                    int control_remote, int data_me, int data_remote)
++{
++      struct etap_pre_exec_data pe_data;
++      int pid, status, err, n;
++      char version_buf[sizeof("nnnnn\0")];
++      char data_fd_buf[sizeof("nnnnnn\0")];
++      char gate_buf[sizeof("nnn.nnn.nnn.nnn\0")];
++      char *setup_args[] = { "uml_net", version_buf, "ethertap", dev,
++                             data_fd_buf, gate_buf, NULL };
++      char *nosetup_args[] = { "uml_net", version_buf, "ethertap", 
++                               dev, data_fd_buf, NULL };
++      char **args, c;
++
++      sprintf(data_fd_buf, "%d", data_remote);
++      sprintf(version_buf, "%d", UML_NET_VERSION);
++      if(gate != NULL){
++              strcpy(gate_buf, gate);
++              args = setup_args;
++      }
++      else args = nosetup_args;
++
++      err = 0;
++      pe_data.control_remote = control_remote;
++      pe_data.control_me = control_me;
++      pe_data.data_me = data_me;
++      pid = run_helper(etap_pre_exec, &pe_data, args, NULL);
++
++      if(pid < 0) err = pid;
++      os_close_file(data_remote);
++      os_close_file(control_remote);
++      n = os_read_file(control_me, &c, sizeof(c));
++      if(n != sizeof(c)){
++              printk("etap_tramp : read of status failed, err = %d\n", -n);
++              return(-EINVAL);
++      }
++      if(c != 1){
++              printk("etap_tramp : uml_net failed\n");
++              err = -EINVAL;
++              CATCH_EINTR(n = waitpid(pid, &status, 0));
++              if(n < 0)
++                      err = -errno;
++              else if(!WIFEXITED(status) || (WEXITSTATUS(status) != 1))
++                      printk("uml_net didn't exit with status 1\n");
++      }
++      return(err);
++}
++
++static int etap_open(void *data)
++{
++      struct ethertap_data *pri = data;
++      char *output;
++      int data_fds[2], control_fds[2], err, output_len;
++
++      err = tap_open_common(pri->dev, pri->gate_addr);
++      if(err) return(err);
++
++      err = os_pipe(data_fds, 0, 0);
++      if(err < 0){
++              printk("data os_pipe failed - err = %d\n", -err);
++              return(err);
++      }
++
++      err = os_pipe(control_fds, 1, 0);
++      if(err < 0){
++              printk("control os_pipe failed - err = %d\n", -err);
++              return(err);
++      }
++      
++      err = etap_tramp(pri->dev_name, pri->gate_addr, control_fds[0], 
++                       control_fds[1], data_fds[0], data_fds[1]);
++      output_len = page_size();
++      output = um_kmalloc(output_len);
++      read_output(control_fds[0], output, output_len);
++
++      if(output == NULL)
++              printk("etap_open : failed to allocate output buffer\n");
++      else {
++              printk("%s", output);
++              kfree(output);
++      }
++
++      if(err < 0){
++              printk("etap_tramp failed - err = %d\n", -err);
++              return(err);
++      }
++
++      pri->data_fd = data_fds[0];
++      pri->control_fd = control_fds[0];
++      iter_addresses(pri->dev, etap_open_addr, &pri->control_fd);
++      return(data_fds[0]);
++}
++
++static void etap_close(int fd, void *data)
++{
++      struct ethertap_data *pri = data;
++
++      iter_addresses(pri->dev, etap_close_addr, &pri->control_fd);
++      os_close_file(fd);
++      os_shutdown_socket(pri->data_fd, 1, 1);
++      os_close_file(pri->data_fd);
++      pri->data_fd = -1;
++      os_close_file(pri->control_fd);
++      pri->control_fd = -1;
++}
++
++static int etap_set_mtu(int mtu, void *data)
++{
++      return(mtu);
++}
++
++static void etap_add_addr(unsigned char *addr, unsigned char *netmask,
++                        void *data)
++{
++      struct ethertap_data *pri = data;
++
++      tap_check_ips(pri->gate_addr, addr);
++      if(pri->control_fd == -1) return;
++      etap_open_addr(addr, netmask, &pri->control_fd);
++}
++
++static void etap_del_addr(unsigned char *addr, unsigned char *netmask, 
++                        void *data)
++{
++      struct ethertap_data *pri = data;
++
++      if(pri->control_fd == -1) return;
++      etap_close_addr(addr, netmask, &pri->control_fd);
++}
++
++struct net_user_info ethertap_user_info = {
++      .init           = etap_user_init,
++      .open           = etap_open,
++      .close          = etap_close,
++      .remove         = NULL,
++      .set_mtu        = etap_set_mtu,
++      .add_address    = etap_add_addr,
++      .delete_address = etap_del_addr,
++      .max_packet     = MAX_PACKET - ETH_HEADER_ETHERTAP
++};
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/os-Linux/drivers/Makefile
+===================================================================
+--- linux-2.4.29.orig/arch/um/os-Linux/drivers/Makefile        1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/os-Linux/drivers/Makefile     2005-05-03 22:28:14.568395480 +0300
+@@ -0,0 +1,31 @@
++# 
++# Copyright (C) 2000, 2002 Jeff Dike (jdike@karaya.com)
++# Licensed under the GPL
++#
++
++O_TARGET := drivers.o
++
++list-multi := tuntap.o ethertap.o
++
++ethertap-objs := ethertap_kern.o ethertap_user.o
++tuntap-objs := tuntap_kern.o tuntap_user.o
++
++obj-y = 
++obj-$(CONFIG_UML_NET_ETHERTAP) += ethertap.o
++obj-$(CONFIG_UML_NET_TUNTAP) += tuntap.o
++
++USER_SINGLE_OBJS = $(foreach f,$(patsubst %.o,%,$(obj-y)),$($(f)-objs))
++
++USER_OBJS = $(filter %_user.o,$(obj-y) $(USER_SINGLE_OBJS))
++
++include $(TOPDIR)/Rules.make
++
++$(USER_OBJS) : %.o: %.c
++      $(CC) $(CFLAGS_$@) $(USER_CFLAGS) -c -o $@ $<
++
++ethertap.o : $(ethertap-objs)
++
++tuntap.o : $(tuntap-objs)
++
++$(list-multi) : # This doesn't work, but should : '%.o : $(%-objs)'
++      $(LD) $(LD_RFLAG) -r -o $@ $($(patsubst %.o,%,$@)-objs)
+Index: linux-2.4.29/arch/um/os-Linux/drivers/tuntap.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/os-Linux/drivers/tuntap.h        1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/os-Linux/drivers/tuntap.h     2005-05-03 22:28:14.568395480 +0300
+@@ -0,0 +1,32 @@
++/* 
++ * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __UM_TUNTAP_H
++#define __UM_TUNTAP_H
++
++#include "net_user.h"
++
++struct tuntap_data {
++      char *dev_name;
++      int fixed_config;
++      char *gate_addr;
++      int fd;
++      void *dev;
++};
++
++extern struct net_user_info tuntap_user_info;
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/os-Linux/drivers/tuntap_kern.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/os-Linux/drivers/tuntap_kern.c   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/os-Linux/drivers/tuntap_kern.c        2005-05-03 22:28:14.569395328 +0300
+@@ -0,0 +1,105 @@
++/* 
++ * Copyright (C) 2001 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/stddef.h"
++#include "linux/netdevice.h"
++#include "linux/etherdevice.h"
++#include "linux/skbuff.h"
++#include "linux/init.h"
++#include "asm/errno.h"
++#include "net_kern.h"
++#include "net_user.h"
++#include "tuntap.h"
++
++struct tuntap_init {
++      char *dev_name;
++      char *gate_addr;
++};
++
++static void tuntap_init(struct net_device *dev, void *data)
++{
++      struct uml_net_private *pri;
++      struct tuntap_data *tpri;
++      struct tuntap_init *init = data;
++
++      init_etherdev(dev, 0);
++      pri = dev->priv;
++      tpri = (struct tuntap_data *) pri->user;
++      *tpri = ((struct tuntap_data)
++              { .dev_name             = init->dev_name,
++                .fixed_config         = (init->dev_name != NULL),
++                .gate_addr            = init->gate_addr,
++                .fd                   = -1,
++                .dev                  = dev });
++      printk("TUN/TAP backend - ");
++      if(tpri->gate_addr != NULL) 
++              printk("IP = %s", tpri->gate_addr);
++      printk("\n");
++}
++
++static int tuntap_read(int fd, struct sk_buff **skb, 
++                     struct uml_net_private *lp)
++{
++      *skb = ether_adjust_skb(*skb, ETH_HEADER_OTHER);
++      if(*skb == NULL) return(-ENOMEM);
++      return(net_read(fd, (*skb)->mac.raw, 
++                      (*skb)->dev->mtu + ETH_HEADER_OTHER));
++}
++
++static int tuntap_write(int fd, struct sk_buff **skb, 
++                      struct uml_net_private *lp)
++{
++      return(net_write(fd, (*skb)->data, (*skb)->len));
++}
++
++struct net_kern_info tuntap_kern_info = {
++      .init                   = tuntap_init,
++      .protocol               = eth_protocol,
++      .read                   = tuntap_read,
++      .write                  = tuntap_write,
++};
++
++int tuntap_setup(char *str, char **mac_out, void *data)
++{
++      struct tuntap_init *init = data;
++
++      *init = ((struct tuntap_init)
++              { .dev_name     = NULL,
++                .gate_addr    = NULL });
++      if(tap_setup_common(str, "tuntap", &init->dev_name, mac_out,
++                          &init->gate_addr))
++              return(0);
++
++      return(1);
++}
++
++static struct transport tuntap_transport = {
++      .list           = LIST_HEAD_INIT(tuntap_transport.list),
++      .name           = "tuntap",
++      .setup          = tuntap_setup,
++      .user           = &tuntap_user_info,
++      .kern           = &tuntap_kern_info,
++      .private_size   = sizeof(struct tuntap_data),
++      .setup_size     = sizeof(struct tuntap_init),
++};
++
++static int register_tuntap(void)
++{
++      register_transport(&tuntap_transport);
++      return(1);
++}
++
++__initcall(register_tuntap);
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/os-Linux/drivers/tuntap_user.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/os-Linux/drivers/tuntap_user.c   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/os-Linux/drivers/tuntap_user.c        2005-05-03 22:28:14.571395024 +0300
+@@ -0,0 +1,225 @@
++/* 
++ * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <stdio.h>
++#include <stddef.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <errno.h>
++#include <sys/wait.h>
++#include <sys/socket.h>
++#include <sys/un.h>
++#include <sys/uio.h>
++#include <sys/ioctl.h>
++#include <net/if.h>
++#include <linux/if_tun.h>
++#include "net_user.h"
++#include "tuntap.h"
++#include "kern_util.h"
++#include "user_util.h"
++#include "user.h"
++#include "helper.h"
++#include "os.h"
++
++#define MAX_PACKET ETH_MAX_PACKET
++
++void tuntap_user_init(void *data, void *dev)
++{
++      struct tuntap_data *pri = data;
++
++      pri->dev = dev;
++}
++
++static void tuntap_add_addr(unsigned char *addr, unsigned char *netmask,
++                          void *data)
++{
++      struct tuntap_data *pri = data;
++
++      tap_check_ips(pri->gate_addr, addr);
++      if((pri->fd == -1) || pri->fixed_config) return;
++      open_addr(addr, netmask, pri->dev_name);
++}
++
++static void tuntap_del_addr(unsigned char *addr, unsigned char *netmask,
++                          void *data)
++{
++      struct tuntap_data *pri = data;
++
++      if((pri->fd == -1) || pri->fixed_config) return;
++      close_addr(addr, netmask, pri->dev_name);
++}
++
++struct tuntap_pre_exec_data {
++      int stdout;
++      int close_me;
++};
++
++static void tuntap_pre_exec(void *arg)
++{
++      struct tuntap_pre_exec_data *data = arg;
++      
++      dup2(data->stdout, 1);
++      os_close_file(data->close_me);
++}
++
++static int tuntap_open_tramp(char *gate, int *fd_out, int me, int remote,
++                           char *buffer, int buffer_len, int *used_out)
++{
++      struct tuntap_pre_exec_data data;
++      char version_buf[sizeof("nnnnn\0")];
++      char *argv[] = { "uml_net", version_buf, "tuntap", "up", gate,
++                       NULL };
++      char buf[CMSG_SPACE(sizeof(*fd_out))];
++      struct msghdr msg;
++      struct cmsghdr *cmsg;
++      struct iovec iov;
++      int pid, n;
++
++      sprintf(version_buf, "%d", UML_NET_VERSION);
++
++      data.stdout = remote;
++      data.close_me = me;
++
++      pid = run_helper(tuntap_pre_exec, &data, argv, NULL);
++
++      if(pid < 0) return(-pid);
++
++      os_close_file(remote);
++
++      msg.msg_name = NULL;
++      msg.msg_namelen = 0;
++      if(buffer != NULL){
++              iov = ((struct iovec) { buffer, buffer_len });
++              msg.msg_iov = &iov;
++              msg.msg_iovlen = 1;
++      }
++      else {
++              msg.msg_iov = NULL;
++              msg.msg_iovlen = 0;
++      }
++      msg.msg_control = buf;
++      msg.msg_controllen = sizeof(buf);
++      msg.msg_flags = 0;
++      n = recvmsg(me, &msg, 0);
++      *used_out = n;
++      if(n < 0){
++              printk("tuntap_open_tramp : recvmsg failed - errno = %d\n", 
++                     errno);
++              return(-errno);
++      }
++      CATCH_EINTR(waitpid(pid, NULL, 0));
++
++      cmsg = CMSG_FIRSTHDR(&msg);
++      if(cmsg == NULL){
++              printk("tuntap_open_tramp : didn't receive a message\n");
++              return(-EINVAL);
++      }
++      if((cmsg->cmsg_level != SOL_SOCKET) || 
++         (cmsg->cmsg_type != SCM_RIGHTS)){
++              printk("tuntap_open_tramp : didn't receive a descriptor\n");
++              return(-EINVAL);
++      }
++      *fd_out = ((int *) CMSG_DATA(cmsg))[0];
++      return(0);
++}
++
++static int tuntap_open(void *data)
++{
++      struct ifreq ifr;
++      struct tuntap_data *pri = data;
++      char *output, *buffer;
++      int err, fds[2], len, used;
++
++      err = tap_open_common(pri->dev, pri->gate_addr);
++      if(err < 0) 
++              return(err);
++
++      if(pri->fixed_config){
++              pri->fd = os_open_file("/dev/net/tun", of_rdwr(OPENFLAGS()), 0);
++              if(pri->fd < 0){
++                      printk("Failed to open /dev/net/tun, err = %d\n",
++                             -pri->fd);
++                      return(pri->fd);
++              }
++              memset(&ifr, 0, sizeof(ifr));
++              ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
++              strncpy(ifr.ifr_name, pri->dev_name, sizeof(ifr.ifr_name) - 1);
++              if(ioctl(pri->fd, TUNSETIFF, (void *) &ifr) < 0){
++                      printk("TUNSETIFF failed, errno = %d\n", errno);
++                      os_close_file(pri->fd);
++                      return(-errno);
++              }
++      }
++      else {
++              err = os_pipe(fds, 0, 0);
++              if(err < 0){
++                      printk("tuntap_open : os_pipe failed - err = %d\n",
++                             -err);
++                      return(err);
++              }
++
++              buffer = get_output_buffer(&len);
++              if(buffer != NULL) len--;
++              used = 0;
++
++              err = tuntap_open_tramp(pri->gate_addr, &pri->fd, fds[0],
++                                      fds[1], buffer, len, &used);
++
++              output = buffer;
++              if(err < 0) {
++                      printk("%s", output);
++                      free_output_buffer(buffer);
++                      printk("tuntap_open_tramp failed - err = %d\n", -err);
++                      return(err);
++              }
++
++              pri->dev_name = uml_strdup(buffer);
++              output += IFNAMSIZ;
++              printk("%s", output);
++              free_output_buffer(buffer);
++
++              os_close_file(fds[0]);
++              iter_addresses(pri->dev, open_addr, pri->dev_name);
++      }
++
++      return(pri->fd);
++}
++
++static void tuntap_close(int fd, void *data)
++{
++      struct tuntap_data *pri = data;
++
++      if(!pri->fixed_config) 
++              iter_addresses(pri->dev, close_addr, pri->dev_name);
++      os_close_file(fd);
++      pri->fd = -1;
++}
++
++static int tuntap_set_mtu(int mtu, void *data)
++{
++      return(mtu);
++}
++
++struct net_user_info tuntap_user_info = {
++      .init           = tuntap_user_init,
++      .open           = tuntap_open,
++      .close          = tuntap_close,
++      .remove         = NULL,
++      .set_mtu        = tuntap_set_mtu,
++      .add_address    = tuntap_add_addr,
++      .delete_address = tuntap_del_addr,
++      .max_packet     = MAX_PACKET
++};
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/os-Linux/file.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/os-Linux/file.c  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/os-Linux/file.c       2005-05-03 22:28:14.574394568 +0300
+@@ -0,0 +1,942 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <stdio.h>
++#include <unistd.h>
++#include <errno.h>
++#include <fcntl.h>
++#include <signal.h>
++#include <utime.h>
++#include <dirent.h>
++#include <linux/kdev_t.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <sys/socket.h>
++#include <sys/un.h>
++#include <sys/ioctl.h>
++#include <sys/mount.h>
++#include <sys/uio.h>
++#include <sys/utsname.h>
++#include <sys/vfs.h>
++#include "os.h"
++#include "user.h"
++#include "kern_util.h"
++
++static void copy_stat(struct uml_stat *dst, struct stat64 *src)
++{
++      *dst = ((struct uml_stat) {
++              .ust_major   = MAJOR(src->st_dev),     /* device */
++              .ust_minor   = MINOR(src->st_dev),
++              .ust_ino     = src->st_ino,     /* inode */
++              .ust_mode    = src->st_mode,    /* protection */
++              .ust_nlink   = src->st_nlink,   /* number of hard links */
++              .ust_uid     = src->st_uid,     /* user ID of owner */
++              .ust_gid     = src->st_gid,     /* group ID of owner */
++              .ust_size    = src->st_size,    /* total size, in bytes */
++              .ust_blksize = src->st_blksize, /* blocksize for filesys I/O */
++              .ust_blocks  = src->st_blocks,  /* number of blocks allocated */
++              .ust_atime   = src->st_atime,   /* time of last access */
++              .ust_mtime   = src->st_mtime,   /* time of last modification */
++              .ust_ctime   = src->st_ctime,   /* time of last change */
++              .ust_rmajor  = MAJOR(src->st_rdev),
++              .ust_rminor  = MINOR(src->st_rdev),
++      });
++}
++
++int os_stat_fd(const int fd, struct uml_stat *ubuf)
++{
++      struct stat64 sbuf;
++      int err;
++
++      do {
++              err = fstat64(fd, &sbuf);
++      } while((err < 0) && (errno == EINTR)) ;
++
++      if(err < 0) 
++              return(-errno);
++
++      if(ubuf != NULL)
++              copy_stat(ubuf, &sbuf);
++      return(err);
++}
++
++int os_stat_file(const char *file_name, struct uml_stat *ubuf)
++{
++      struct stat64 sbuf;
++      int err;
++
++      do {
++              err = stat64(file_name, &sbuf);
++      } while((err < 0) && (errno == EINTR)) ;
++
++      if(err < 0) 
++              return(-errno);
++
++      if(ubuf != NULL)
++              copy_stat(ubuf, &sbuf);
++      return(err);
++}
++
++int os_lstat_file(const char *file_name, struct uml_stat *ubuf)
++{
++      struct stat64 sbuf;
++      int err;
++
++      do {
++              err = lstat64(file_name, &sbuf);
++      } while((err < 0) && (errno == EINTR)) ;
++
++      if(err < 0) 
++              return(-errno);
++
++      if(ubuf != NULL)
++              copy_stat(ubuf, &sbuf);
++      return(err);
++}
++
++int os_access(const char *file, int mode)
++{
++      int amode, err;
++
++      amode=(mode& OS_ACC_R_OK ? R_OK : 0) | (mode& OS_ACC_W_OK ? W_OK : 0) |
++            (mode& OS_ACC_X_OK ? X_OK : 0) | (mode& OS_ACC_F_OK ? F_OK : 0) ;
++
++      err = access(file, amode);
++      if(err < 0)
++              return(-errno);
++
++      return(0);
++}
++
++int os_set_file_time(const char *file, unsigned long access, unsigned long mod)
++{
++      struct utimbuf buf = ((struct utimbuf){ .actime = access, 
++                                              .modtime = mod });
++      int err;
++
++      err = utime(file, &buf);
++      if(err < 0)
++              return(-errno);
++
++      return(0);
++}
++
++int os_set_file_perms(const char *file, int mode)
++{
++      int err;
++
++      err = chmod(file, mode);
++      if(err < 0)
++              return(-errno);
++
++      return(0);
++}
++
++int os_set_file_owner(const char *file, int owner, int group)
++{
++      int err;
++
++      err = chown(file, owner, group);
++      if(err < 0)
++              return(-errno);
++
++      return(0);
++}
++
++void os_print_error(int error, const char* str)
++{
++      errno = error < 0 ? -error : error;
++
++      perror(str);
++}
++
++/* FIXME? required only by hostaudio (because it passes ioctls verbatim) */
++int os_ioctl_generic(int fd, unsigned int cmd, unsigned long arg)
++{
++      int err;
++
++      err = ioctl(fd, cmd, arg);
++      if(err < 0)
++              return(-errno);
++
++      return(err);
++}
++
++int os_window_size(int fd, int *rows, int *cols)
++{
++      struct winsize size;
++
++      if(ioctl(fd, TIOCGWINSZ, &size) < 0)
++              return(-errno);
++
++      *rows = size.ws_row;
++      *cols = size.ws_col;
++
++      return(0);
++}
++
++int os_new_tty_pgrp(int fd, int pid)
++{
++      if(ioctl(fd, TIOCSCTTY, 0) < 0){
++              printk("TIOCSCTTY failed, errno = %d\n", errno);
++              return(-errno);
++      }
++
++      if(tcsetpgrp(fd, pid) < 0){
++              printk("tcsetpgrp failed, errno = %d\n", errno);
++              return(-errno);
++      }
++
++      return(0);
++}
++
++/* FIXME: ensure namebuf in os_get_if_name is big enough */
++int os_get_ifname(int fd, char* namebuf)
++{
++      if(ioctl(fd, SIOCGIFNAME, namebuf) < 0)
++              return(-errno);
++
++      return(0);
++}
++
++int os_set_slip(int fd)
++{
++      int disc, sencap;
++
++      disc = N_SLIP;
++      if(ioctl(fd, TIOCSETD, &disc) < 0){
++              printk("Failed to set slip line discipline - "
++                     "errno = %d\n", errno);
++              return(-errno);
++      }
++
++      sencap = 0;
++      if(ioctl(fd, SIOCSIFENCAP, &sencap) < 0){
++              printk("Failed to set slip encapsulation - "
++                     "errno = %d\n", errno);
++              return(-errno);
++      }
++
++      return(0);
++}
++
++int os_set_owner(int fd, int pid)
++{
++      if(fcntl(fd, F_SETOWN, pid) < 0){
++              int save_errno = errno;
++
++              if(fcntl(fd, F_GETOWN, 0) != pid)
++                      return(-save_errno);
++      }
++
++      return(0);
++}
++
++/* FIXME? moved wholesale from sigio_user.c to get fcntls out of that file */ 
++int os_sigio_async(int master, int slave)
++{
++      int flags;
++
++      flags = fcntl(master, F_GETFL);
++      if(flags < 0) {
++              printk("fcntl F_GETFL failed, errno = %d\n", errno);
++              return(-errno);
++      }
++
++      if((fcntl(master, F_SETFL, flags | O_NONBLOCK | O_ASYNC) < 0) ||
++         (fcntl(master, F_SETOWN, os_getpid()) < 0)){
++              printk("fcntl F_SETFL or F_SETOWN failed, errno = %d\n", 
++                     errno);
++              return(-errno);
++      }
++
++      if((fcntl(slave, F_SETFL, flags | O_NONBLOCK) < 0)){
++              printk("fcntl F_SETFL failed, errno = %d\n", errno);
++              return(-errno);
++      }
++
++      return(0);
++}
++
++int os_mode_fd(int fd, int mode)
++{
++      int err;
++
++      do {
++              err = fchmod(fd, mode);
++      } while((err < 0) && (errno==EINTR)) ;
++
++      if(err < 0)
++              return(-errno);
++
++      return(0);
++}
++
++int os_file_type(char *file)
++{
++      struct uml_stat buf;
++      int err;
++
++      err = os_lstat_file(file, &buf);
++      if(err < 0)
++              return(err);
++
++      if(S_ISDIR(buf.ust_mode)) return(OS_TYPE_DIR);
++      else if(S_ISLNK(buf.ust_mode)) return(OS_TYPE_SYMLINK);
++      else if(S_ISCHR(buf.ust_mode)) return(OS_TYPE_CHARDEV);
++      else if(S_ISBLK(buf.ust_mode)) return(OS_TYPE_BLOCKDEV);
++      else if(S_ISFIFO(buf.ust_mode)) return(OS_TYPE_FIFO);
++      else if(S_ISSOCK(buf.ust_mode)) return(OS_TYPE_SOCK);
++      else return(OS_TYPE_FILE);
++}
++
++int os_file_mode(char *file, struct openflags *mode_out)
++{
++      int err;
++
++      *mode_out = OPENFLAGS();
++
++      err = os_access(file, OS_ACC_W_OK);
++      if((err < 0) && (err != -EACCES))
++              return(err);
++
++      *mode_out = of_write(*mode_out);
++
++      err = os_access(file, OS_ACC_R_OK);
++      if((err < 0) && (err != -EACCES))
++              return(err);
++
++      *mode_out = of_read(*mode_out);
++
++      return(0);
++}
++
++int os_open_file(char *file, struct openflags flags, int mode)
++{
++      int fd, f = 0;
++
++      if(flags.r && flags.w) f = O_RDWR;
++      else if(flags.r) f = O_RDONLY;
++      else if(flags.w) f = O_WRONLY;
++      else f = 0;
++
++      if(flags.s) f |= O_SYNC;
++      if(flags.c) f |= O_CREAT;
++      if(flags.t) f |= O_TRUNC;
++      if(flags.e) f |= O_EXCL;
++      if(flags.d) f |= O_DIRECT;
++
++      fd = open64(file, f, mode);
++      if(fd < 0)
++              return(-errno);
++
++      if(flags.cl && fcntl(fd, F_SETFD, 1)){
++              os_close_file(fd);
++              return(-errno);
++      }
++
++      return(fd);
++}
++
++void *os_open_dir(char *path, int *err_out)
++{
++      void *dir;
++
++      dir = opendir(path);
++      *err_out = -errno;
++      return(dir);
++}
++
++int os_seek_dir(void *stream, unsigned long long pos)
++{
++      seekdir(stream, pos);
++      return(0);
++}
++
++int os_read_dir(void *stream, unsigned long long *ino_out, char **name_out)
++{
++      struct dirent *ent;
++
++      errno = 0;
++      ent = readdir(stream);
++      if(ent == NULL){
++              if(errno != 0)
++                      return(-errno);
++              *name_out = NULL;
++              return(0);
++      }
++
++      *ino_out = ent->d_ino;
++      *name_out = ent->d_name;
++      return(0);
++}
++
++int os_tell_dir(void *stream)
++{
++      return(telldir(stream));
++}
++
++int os_close_dir(void *stream)
++{
++      int err;
++
++      err = closedir(stream);
++      if(err < 0)
++              return(-errno);
++      return(0);
++}
++
++int os_remove_file(const char *file)
++{
++      int err;
++
++      err = unlink(file);
++      if(err)
++              return(-errno);
++
++      return(0);
++}
++
++int os_move_file(const char *from, const char *to)
++{
++      int err;
++
++      err = rename(from, to);
++      if(err)
++              return(-errno);
++
++      return(0);
++}
++
++int os_truncate_fd(int fd, unsigned long long len)
++{
++      int err;
++
++      err = ftruncate64(fd, len);
++      if(err)
++              return(-errno);
++      return(0);
++}
++
++int os_truncate_file(const char *file, unsigned long long len)
++{
++      int err;
++
++      err = truncate64(file, len);
++      if(err)
++              return(-errno);
++      return(0);
++}
++
++int os_connect_socket(char *name)
++{
++      struct sockaddr_un sock;
++      int fd, err;
++
++      sock.sun_family = AF_UNIX;
++      snprintf(sock.sun_path, sizeof(sock.sun_path), "%s", name);
++
++      fd = socket(AF_UNIX, SOCK_STREAM, 0);
++      if(fd < 0)
++              return(fd);
++
++      err = connect(fd, (struct sockaddr *) &sock, sizeof(sock));
++      if(err)
++              return(-errno);
++
++      return(fd);
++}
++
++void os_close_file(int fd)
++{
++      close(fd);
++}
++
++int os_seek_file(int fd, __u64 offset)
++{
++      __u64 actual;
++
++      actual = lseek64(fd, offset, SEEK_SET);
++      if(actual != offset)
++              return(-errno);
++      return(0);
++}
++
++static int fault_buffer(void *start, int len, 
++                      int (*copy_proc)(void *addr, void *buf, int len))
++{
++      int page = getpagesize(), i;
++      char c;
++
++      for(i = 0; i < len; i += page){
++              if((*copy_proc)(start + i, &c, sizeof(c)))
++                      return(-EFAULT);
++      }
++      if((len % page) != 0){
++              if((*copy_proc)(start + len - 1, &c, sizeof(c)))
++                      return(-EFAULT);
++      }
++      return(0);
++}
++
++static int file_io(int fd, void *buf, int len,
++                 int (*io_proc)(int fd, void *buf, int len),
++                 int (*copy_user_proc)(void *addr, void *buf, int len))
++{
++      int n, err;
++
++      do {
++              n = (*io_proc)(fd, buf, len);
++              if((n < 0) && (errno == EFAULT)){
++                      err = fault_buffer(buf, len, copy_user_proc);
++                      if(err)
++                              return(err);
++                      n = (*io_proc)(fd, buf, len);
++              }
++      } while((n < 0) && (errno == EINTR));
++
++      if(n < 0)
++              return(-errno);
++      return(n);
++}
++
++int os_read_file(int fd, void *buf, int len)
++{
++      return(file_io(fd, buf, len, (int (*)(int, void *, int)) read, 
++                     copy_from_user_proc));
++}
++
++int os_write_file(int fd, const void *buf, int len)
++{
++      return(file_io(fd, (void *) buf, len, 
++                     (int (*)(int, void *, int)) write, copy_to_user_proc));
++}
++
++int os_file_size(char *file, long long *size_out)
++{
++      struct uml_stat buf;
++      int err;
++
++      err = os_stat_file(file, &buf);
++      if(err < 0){
++              printk("Couldn't stat \"%s\" : err = %d\n", file, -err);
++              return(err);
++      }
++
++      if(S_ISBLK(buf.ust_mode)){
++              int fd, blocks;
++
++              fd = os_open_file(file, of_read(OPENFLAGS()), 0);
++              if(fd < 0){
++                      printk("Couldn't open \"%s\", errno = %d\n", file, -fd);
++                      return(fd);
++              }
++              if(ioctl(fd, BLKGETSIZE, &blocks) < 0){
++                      printk("Couldn't get the block size of \"%s\", "
++                             "errno = %d\n", file, errno);
++                      err = -errno;
++                      os_close_file(fd);
++                      return(err);
++              }
++              *size_out = ((long long) blocks) * 512;
++              os_close_file(fd);
++              return(0);
++      }
++      *size_out = buf.ust_size;
++      return(0);
++}
++
++int os_fd_size(int fd, long long *size_out)
++{
++      struct stat buf;
++      int err;
++
++      err = fstat(fd, &buf);
++      if(err)
++              return(-errno);
++
++      *size_out = buf.st_size;
++      return(0);
++}
++
++int os_file_modtime(char *file, unsigned long *modtime)
++{
++      struct uml_stat buf;
++      int err;
++
++      err = os_stat_file(file, &buf);
++      if(err < 0){
++              printk("Couldn't stat \"%s\" : err = %d\n", file, -err);
++              return(err);
++      }
++
++      *modtime = buf.ust_mtime;
++      return(0);
++}
++
++int os_get_exec_close(int fd, int* close_on_exec)
++{
++      int ret;
++
++      do {
++              ret = fcntl(fd, F_GETFD);
++      } while((ret < 0) && (errno == EINTR)) ;
++
++      if(ret < 0)
++              return(-errno);
++
++      *close_on_exec = (ret&FD_CLOEXEC) ? 1 : 0;
++      return(ret);
++}
++
++int os_set_exec_close(int fd, int close_on_exec)
++{
++      int flag, err;
++
++      if(close_on_exec) flag = FD_CLOEXEC;
++      else flag = 0;
++
++      do {
++              err = fcntl(fd, F_SETFD, flag);
++      } while((err < 0) && (errno == EINTR)) ;
++
++      if(err < 0)
++              return(-errno);
++      return(err);
++}
++
++int os_pipe(int *fds, int stream, int close_on_exec)
++{
++      int err, type = stream ? SOCK_STREAM : SOCK_DGRAM;
++
++      err = socketpair(AF_UNIX, type, 0, fds);
++      if(err < 0) 
++              return(-errno);
++
++      if(!close_on_exec)
++              return(0);
++
++      err = os_set_exec_close(fds[0], 1);
++      if(err < 0)
++              goto error;
++
++      err = os_set_exec_close(fds[1], 1);
++      if(err < 0)
++              goto error;
++
++      return(0);
++
++ error:
++      printk("os_pipe : Setting FD_CLOEXEC failed, err = %d\n", -err);
++      os_close_file(fds[1]);
++      os_close_file(fds[0]);
++      return(err);
++}
++
++int os_set_fd_async(int fd, int owner)
++{
++      /* XXX This should do F_GETFL first */
++      if(fcntl(fd, F_SETFL, O_ASYNC | O_NONBLOCK) < 0){
++              printk("os_set_fd_async : failed to set O_ASYNC and "
++                     "O_NONBLOCK on fd # %d, errno = %d\n", fd, errno);
++              return(-errno);
++      }
++#ifdef notdef
++      if(fcntl(fd, F_SETFD, 1) < 0){
++              printk("os_set_fd_async : Setting FD_CLOEXEC failed, "
++                     "errno = %d\n", errno);
++      }
++#endif
++
++      if((fcntl(fd, F_SETSIG, SIGIO) < 0) ||
++         (fcntl(fd, F_SETOWN, owner) < 0)){
++              printk("os_set_fd_async : Failed to fcntl F_SETOWN "
++                     "(or F_SETSIG) fd %d to pid %d, errno = %d\n", fd, 
++                     owner, errno);
++              return(-errno);
++      }
++
++      return(0);
++}
++
++int os_clear_fd_async(int fd)
++{
++      int flags = fcntl(fd, F_GETFL);
++
++      flags &= ~(O_ASYNC | O_NONBLOCK);
++      if(fcntl(fd, F_SETFL, flags) < 0)
++              return(-errno);
++      return(0);
++}
++
++int os_set_fd_block(int fd, int blocking)
++{
++      int flags;
++
++      flags = fcntl(fd, F_GETFL);
++
++      if(blocking) flags &= ~O_NONBLOCK;
++      else flags |= O_NONBLOCK;
++
++      if(fcntl(fd, F_SETFL, flags) < 0){
++              printk("Failed to change blocking on fd # %d, errno = %d\n",
++                     fd, errno);
++              return(-errno);
++      }
++      return(0);
++}
++
++int os_accept_connection(int fd)
++{
++      int new;
++
++      new = accept(fd, NULL, 0);
++      if(new < 0) 
++              return(-errno);
++      return(new);
++}
++
++#ifndef SHUT_RD
++#define SHUT_RD 0
++#endif
++
++#ifndef SHUT_WR
++#define SHUT_WR 1
++#endif
++
++#ifndef SHUT_RDWR
++#define SHUT_RDWR 2
++#endif
++
++int os_shutdown_socket(int fd, int r, int w)
++{
++      int what, err;
++
++      if(r && w) what = SHUT_RDWR;
++      else if(r) what = SHUT_RD;
++      else if(w) what = SHUT_WR;
++      else {
++              printk("os_shutdown_socket : neither r or w was set\n");
++              return(-EINVAL);
++      }
++      err = shutdown(fd, what);
++      if(err < 0)
++              return(-errno);
++      return(0);
++}
++
++int os_rcv_fd(int fd, int *helper_pid_out)
++{
++      int new, n;
++      char buf[CMSG_SPACE(sizeof(new))];
++      struct msghdr msg;
++      struct cmsghdr *cmsg;
++      struct iovec iov;
++
++      msg.msg_name = NULL;
++      msg.msg_namelen = 0;
++      iov = ((struct iovec) { .iov_base  = helper_pid_out,
++                              .iov_len   = sizeof(*helper_pid_out) });
++      msg.msg_iov = &iov;
++      msg.msg_iovlen = 1;
++      msg.msg_control = buf;
++      msg.msg_controllen = sizeof(buf);
++      msg.msg_flags = 0;
++
++      n = recvmsg(fd, &msg, 0);
++      if(n < 0)
++              return(-errno);
++
++      else if(n != sizeof(iov.iov_len))
++              *helper_pid_out = -1;
++
++      cmsg = CMSG_FIRSTHDR(&msg);
++      if(cmsg == NULL){
++              printk("rcv_fd didn't receive anything, error = %d\n", errno);
++              return(-1);
++      }
++      if((cmsg->cmsg_level != SOL_SOCKET) || 
++         (cmsg->cmsg_type != SCM_RIGHTS)){
++              printk("rcv_fd didn't receive a descriptor\n");
++              return(-1);
++      }
++
++      new = ((int *) CMSG_DATA(cmsg))[0];
++      return(new);
++}
++
++int os_create_unix_socket(char *file, int len, int close_on_exec)
++{
++      struct sockaddr_un addr;
++      int sock, err;
++
++      sock = socket(PF_UNIX, SOCK_DGRAM, 0);
++      if (sock < 0){
++              printk("create_unix_socket - socket failed, errno = %d\n",
++                     errno);
++              return(-errno);
++      }
++
++      if(close_on_exec) {
++              err = os_set_exec_close(sock, 1);
++              if(err < 0)
++                      printk("create_unix_socket : close_on_exec failed, "
++                     "err = %d", -err);
++      }
++
++      addr.sun_family = AF_UNIX;
++
++      /* XXX Be more careful about overflow */
++      snprintf(addr.sun_path, len, "%s", file);
++
++      err = bind(sock, (struct sockaddr *) &addr, sizeof(addr));
++      if (err < 0){
++              printk("create_listening_socket at '%s' - bind failed, "
++                     "errno = %d\n", file, errno);
++              return(-errno);
++      }
++
++      return(sock);
++}
++
++int os_make_symlink(const char *to, const char *from)
++{
++      int err;
++
++      err = symlink(to, from);
++      if(err)
++              return(-errno);
++
++      return(0);
++}
++
++int os_read_symlink(const char *file, char *buf, int size)
++{
++      int err;
++
++      err = readlink(file, buf, size);
++      if(err < 0)
++              return(-errno);
++
++      return(err);
++}
++
++int os_link_file(const char *to, const char *from)
++{
++      int err;
++
++      err = link(to, from);
++      if(err)
++              return(-errno);
++
++      return(0);
++}
++
++int os_make_dir(const char *dir, int mode)
++{
++      int err;
++
++      err = mkdir(dir, mode);
++      if(err)
++              return(-errno);
++
++      return(0);
++}
++
++int os_make_dev(const char *name, int mode, int major, int minor)
++{
++      int err;
++
++      err = mknod(name, mode, MKDEV(major, minor));
++      if(err)
++              return(-errno);
++
++      return(0);
++}
++
++int os_remove_dir(const char *dir)
++{
++      int err;
++
++      err = rmdir(dir);
++      if(err)
++              return(-errno);
++
++      return(0);
++}
++
++void os_flush_stdout(void)
++{
++      fflush(stdout);
++}
++
++int os_lock_file(int fd, int excl)
++{
++      int type = excl ? F_WRLCK : F_RDLCK;
++      struct flock lock = ((struct flock) { .l_type   = type,
++                                            .l_whence = SEEK_SET,
++                                            .l_start  = 0,
++                                            .l_len    = 0 } );
++      int err, save;
++
++      err = fcntl(fd, F_SETLK, &lock);
++      if(!err)
++              goto out;
++
++      save = -errno;
++      err = fcntl(fd, F_GETLK, &lock);
++      if(err){
++              err = -errno;
++              goto out;
++      }
++
++      printk("F_SETLK failed, file already locked by pid %d\n", lock.l_pid);
++      err = save;
++ out:
++      return(err);
++}
++
++int os_stat_filesystem(char *path, long *bsize_out, long long *blocks_out, 
++                     long long *bfree_out, long long *bavail_out, 
++                     long long *files_out, long long *ffree_out, 
++                     void *fsid_out, int fsid_size, long *namelen_out, 
++                     long *spare_out)
++{
++      struct statfs64 buf;
++      int err;
++
++      err = statfs64(path, &buf);
++      if(err < 0)
++              return(-errno);
++
++      *bsize_out = buf.f_bsize;
++      *blocks_out = buf.f_blocks;
++      *bfree_out = buf.f_bfree;
++      *bavail_out = buf.f_bavail;
++      *files_out = buf.f_files;
++      *ffree_out = buf.f_ffree;
++      memcpy(fsid_out, &buf.f_fsid, 
++             sizeof(buf.f_fsid) > fsid_size ? fsid_size : 
++             sizeof(buf.f_fsid));
++      *namelen_out = buf.f_namelen;
++      spare_out[0] = buf.f_spare[0];
++      spare_out[1] = buf.f_spare[1];
++      spare_out[2] = buf.f_spare[2];
++      spare_out[3] = buf.f_spare[3];
++      spare_out[4] = buf.f_spare[4];
++      spare_out[5] = buf.f_spare[5];
++      return(0);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/os-Linux/include/file.h
+===================================================================
+--- linux-2.4.29.orig/arch/um/os-Linux/include/file.h  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/os-Linux/include/file.h       2005-05-03 22:28:14.575394416 +0300
+@@ -0,0 +1,22 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __OS_FILE_H__
++#define __OS_FILE_H__
++
++#define DEV_NULL "/dev/null"
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/os-Linux/Makefile
+===================================================================
+--- linux-2.4.29.orig/arch/um/os-Linux/Makefile        1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/os-Linux/Makefile     2005-05-03 22:28:14.575394416 +0300
+@@ -0,0 +1,23 @@
++# 
++# Copyright (C) 2000 - 2004 Jeff Dike (jdike@addtoit.com)
++# Licensed under the GPL
++#
++
++O_TARGET = built-in.o
++
++obj-y = aio.o file.o process.o time.o tty.o
++
++HAVE_AIO_ABI = $(shell [ -e /usr/include/linux/aio_abi.h ] && \
++      echo -DHAVE_AIO_ABI)
++HAVE_AIO_LIBC = $(shell objdump -T /lib/libc-*.so | grep io_submit && \
++      echo -DHAVE_AIO_LIBC)
++CFLAGS_aio.o = $(HAVE_AIO_ABI) $(HAVE_AIO_LIBC)
++
++include $(TOPDIR)/Rules.make
++
++$(obj-y) : %.o: %.c
++      $(CC) $(CFLAGS_$@) $(USER_CFLAGS) -c -o $@ $<
++
++clean :
++
++archmrproper:
+Index: linux-2.4.29/arch/um/os-Linux/process.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/os-Linux/process.c       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/os-Linux/process.c    2005-05-03 22:28:14.577394112 +0300
+@@ -0,0 +1,151 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@addtoit.com)
++ * Licensed under the GPL
++ */
++
++#include <unistd.h>
++#include <stdio.h>
++#include <errno.h>
++#include <signal.h>
++#include <sys/mman.h>
++#include <sys/wait.h>
++#include "os.h"
++#include "user.h"
++#include "user_util.h"
++
++#define ARBITRARY_ADDR -1
++#define FAILURE_PID    -1
++
++#define STAT_PATH_LEN sizeof("/proc/#######/stat\0")
++#define COMM_SCANF "%*[^)])"
++
++unsigned long os_process_pc(int pid)
++{
++      char proc_stat[STAT_PATH_LEN], buf[256];
++      unsigned long pc;
++      int fd, err;
++
++      sprintf(proc_stat, "/proc/%d/stat", pid);
++      fd = os_open_file(proc_stat, of_read(OPENFLAGS()), 0);
++      if(fd < 0){
++              printk("os_process_pc - couldn't open '%s', err = %d\n", 
++                     proc_stat, -fd);
++              return(ARBITRARY_ADDR);
++      }
++      err = os_read_file(fd, buf, sizeof(buf));
++      if(err < 0){
++              printk("os_process_pc - couldn't read '%s', err = %d\n", 
++                     proc_stat, -err);
++              os_close_file(fd);
++              return(ARBITRARY_ADDR);
++      }
++      os_close_file(fd);
++      pc = ARBITRARY_ADDR;
++      if(sscanf(buf, "%*d " COMM_SCANF " %*c %*d %*d %*d %*d %*d %*d %*d "
++                "%*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d "
++                "%*d %*d %*d %*d %*d %lu", &pc) != 1){
++              printk("os_process_pc - couldn't find pc in '%s'\n", buf);
++      }
++      return(pc);
++}
++
++int os_process_parent(int pid)
++{
++      char stat[STAT_PATH_LEN];
++      char data[256];
++      int parent, n, fd;
++
++      if(pid == -1) return(-1);
++
++      snprintf(stat, sizeof(stat), "/proc/%d/stat", pid);
++      fd = os_open_file(stat, of_read(OPENFLAGS()), 0);
++      if(fd < 0){
++              printk("Couldn't open '%s', err = %d\n", stat, -fd);
++              return(FAILURE_PID);
++      }
++
++      n = os_read_file(fd, data, sizeof(data));
++      os_close_file(fd);
++
++      if(n < 0){
++              printk("Couldn't read '%s', err = %d\n", stat, -n);
++              return(FAILURE_PID);
++      }
++
++      parent = FAILURE_PID;
++      n = sscanf(data, "%*d " COMM_SCANF " %*c %d", &parent);
++      if(n != 1) 
++              printk("Failed to scan '%s'\n", data);
++
++      return(parent);
++}
++
++void os_stop_process(int pid)
++{
++      kill(pid, SIGSTOP);
++}
++
++void os_kill_process(int pid, int reap_child)
++{
++      kill(pid, SIGKILL);
++      if(reap_child)
++              CATCH_EINTR(waitpid(pid, NULL, 0));
++              
++}
++
++void os_usr1_process(int pid)
++{
++      kill(pid, SIGUSR1);
++}
++
++int os_getpid(void)
++{
++      return(getpid());
++}
++
++int os_map_memory(void *virt, int fd, unsigned long long off, unsigned long len,
++                int r, int w, int x)
++{
++      void *loc;
++      int prot;
++
++      prot = (r ? PROT_READ : 0) | (w ? PROT_WRITE : 0) | 
++              (x ? PROT_EXEC : 0);
++
++      loc = mmap64((void *) virt, len, prot, MAP_SHARED | MAP_FIXED, 
++                   fd, off);
++      if(loc == MAP_FAILED)
++              return(-errno);
++      return(0);
++}
++
++int os_protect_memory(void *addr, unsigned long len, int r, int w, int x)
++{
++        int prot = ((r ? PROT_READ : 0) | (w ? PROT_WRITE : 0) | 
++                  (x ? PROT_EXEC : 0));
++
++        if(mprotect(addr, len, prot) < 0)
++              return(-errno);
++        return(0);
++}
++
++int os_unmap_memory(void *addr, int len)
++{
++        int err;
++
++        err = munmap(addr, len);
++      if(err < 0)
++              return(-errno);
++        return(0);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/os-Linux/time.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/os-Linux/time.c  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/os-Linux/time.c       2005-05-03 22:28:14.578393960 +0300
+@@ -0,0 +1,21 @@
++#include <stdlib.h>
++#include <sys/time.h>
++
++unsigned long long os_usecs(void)
++{
++      struct timeval tv;
++
++      gettimeofday(&tv, NULL);
++      return((unsigned long long) tv.tv_sec * 1000000 + tv.tv_usec);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/os-Linux/tty.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/os-Linux/tty.c   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/os-Linux/tty.c        2005-05-03 22:28:14.579393808 +0300
+@@ -0,0 +1,61 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <stdlib.h>
++#include <errno.h>
++#include "os.h"
++#include "user.h"
++#include "kern_util.h"
++
++struct grantpt_info {
++      int fd;
++      int res;
++      int err;
++};
++
++static void grantpt_cb(void *arg)
++{
++      struct grantpt_info *info = arg;
++
++      info->res = grantpt(info->fd);
++      info->err = errno;
++}
++
++int get_pty(void)
++{
++      struct grantpt_info info;
++      int fd;
++
++      fd = os_open_file("/dev/ptmx", of_rdwr(OPENFLAGS()), 0);
++      if(fd < 0){
++              printk("get_pty : Couldn't open /dev/ptmx - err = %d\n", -fd);
++              return(fd);
++      }
++
++      info.fd = fd;
++      initial_thread_cb(grantpt_cb, &info);
++
++      if(info.res < 0){
++              printk("get_pty : Couldn't grant pty - errno = %d\n", 
++                     -info.err);
++              return(-1);
++      }
++      if(unlockpt(fd) < 0){
++              printk("get_pty : Couldn't unlock pty - errno = %d\n", errno);
++              return(-1);
++      }
++      return(fd);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/sys-i386/bugs.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/sys-i386/bugs.c  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/sys-i386/bugs.c       2005-05-03 22:28:14.580393656 +0300
+@@ -0,0 +1,222 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <unistd.h>
++#include <errno.h>
++#include <string.h>
++#include <sys/signal.h>
++#include <asm/ldt.h>
++#include "kern_util.h"
++#include "user.h"
++#include "sysdep/ptrace.h"
++#include "task.h"
++#include "os.h"
++
++#define MAXTOKEN 64
++
++/* Set during early boot */
++int host_has_cmov = 1;
++int host_has_xmm = 0;
++
++static char token(int fd, char *buf, int len, char stop)
++{
++      int n;
++      char *ptr, *end, c;
++
++      ptr = buf;
++      end = &buf[len];
++      do {
++              n = os_read_file(fd, ptr, sizeof(*ptr));
++              c = *ptr++;
++              if(n != sizeof(*ptr)){
++                      if(n == 0) return(0);
++                      printk("Reading /proc/cpuinfo failed, err = %d\n", -n);
++                      if(n < 0) 
++                              return(n);
++                      else 
++                              return(-EIO);
++              }
++      } while((c != '\n') && (c != stop) && (ptr < end));
++
++      if(ptr == end){
++              printk("Failed to find '%c' in /proc/cpuinfo\n", stop);
++              return(-1);
++      }
++      *(ptr - 1) = '\0';
++      return(c);
++}
++
++static int find_cpuinfo_line(int fd, char *key, char *scratch, int len)
++{
++      int n;
++      char c;
++
++      scratch[len - 1] = '\0';
++      while(1){
++              c = token(fd, scratch, len - 1, ':');
++              if(c <= 0)
++                      return(0);
++              else if(c != ':'){
++                      printk("Failed to find ':' in /proc/cpuinfo\n");
++                      return(0);
++              }
++
++              if(!strncmp(scratch, key, strlen(key))) 
++                      return(1);
++
++              do {
++                      n = os_read_file(fd, &c, sizeof(c));
++                      if(n != sizeof(c)){
++                              printk("Failed to find newline in "
++                                     "/proc/cpuinfo, err = %d\n", -n);
++                              return(0);
++                      }
++              } while(c != '\n');
++      }
++      return(0);
++}
++
++int cpu_feature(char *what, char *buf, int len)
++{
++      int fd, ret = 0;
++
++      fd = os_open_file("/proc/cpuinfo", of_read(OPENFLAGS()), 0);
++      if(fd < 0){
++              printk("Couldn't open /proc/cpuinfo, err = %d\n", -fd);
++              return(0);
++      }
++
++      if(!find_cpuinfo_line(fd, what, buf, len)){
++              printk("Couldn't find '%s' line in /proc/cpuinfo\n", what);
++              goto out_close;
++      }
++
++      token(fd, buf, len, '\n');
++      ret = 1;
++
++ out_close:
++      os_close_file(fd);
++      return(ret);
++}
++
++static int check_cpu_flag(char *feature, int *have_it)
++{
++      char buf[MAXTOKEN], c;
++      int fd, len = sizeof(buf)/sizeof(buf[0]);
++
++      printk("Checking for host processor %s support...", feature);
++      fd = os_open_file("/proc/cpuinfo", of_read(OPENFLAGS()), 0);
++      if(fd < 0){
++              printk("Couldn't open /proc/cpuinfo, err = %d\n", -fd);
++              return(0);
++      }
++
++      *have_it = 0;
++      if(!find_cpuinfo_line(fd, "flags", buf, sizeof(buf) / sizeof(buf[0])))
++              goto out;
++
++      c = token(fd, buf, len - 1, ' ');
++      if(c < 0) goto out;
++      else if(c != ' '){
++              printk("Failed to find ' ' in /proc/cpuinfo\n");
++              goto out;
++      }
++
++      while(1){
++              c = token(fd, buf, len - 1, ' ');
++              if(c < 0) goto out;
++              else if(c == '\n') break;
++
++              if(!strcmp(buf, feature)){
++                      *have_it = 1;
++                      goto out;
++              }
++      }
++ out:
++      if(*have_it == 0) printk("No\n");
++      else if(*have_it == 1) printk("Yes\n");
++      os_close_file(fd);
++      return(1);
++}
++
++#if 0 /* This doesn't work in tt mode, plus it's causing compilation problems
++       * for some people.
++       */
++static void disable_lcall(void)
++{
++      struct modify_ldt_ldt_s ldt;
++      int err;
++
++      bzero(&ldt, sizeof(ldt));
++      ldt.entry_number = 7;
++      ldt.base_addr = 0;
++      ldt.limit = 0;
++      err = modify_ldt(1, &ldt, sizeof(ldt));
++      if(err)
++              printk("Failed to disable lcall7 - errno = %d\n", errno);
++}
++#endif
++
++void arch_init_thread(void)
++{
++#if 0
++      disable_lcall();
++#endif
++}
++
++void arch_check_bugs(void)
++{
++      int have_it;
++
++      if(os_access("/proc/cpuinfo", OS_ACC_R_OK) < 0){
++              printk("/proc/cpuinfo not available - skipping CPU capability "
++                     "checks\n");
++              return;
++      }
++      if(check_cpu_flag("cmov", &have_it)) 
++              host_has_cmov = have_it;
++      if(check_cpu_flag("xmm", &have_it)) 
++              host_has_xmm = have_it;
++}
++
++int arch_handle_signal(int sig, union uml_pt_regs *regs)
++{
++      unsigned char tmp[2];
++
++      /* This is testing for a cmov (0x0f 0x4x) instruction causing a
++       * SIGILL in init.
++       */
++      if((sig != SIGILL) || (TASK_PID(get_current()) != 1)) return(0);
++
++      if (copy_from_user_proc(tmp, (void *) UPT_IP(regs), 2))
++              panic("SIGILL in init, could not read instructions!\n");
++      if((tmp[0] != 0x0f) || ((tmp[1] & 0xf0) != 0x40))
++              return(0);
++
++      if(host_has_cmov == 0)
++              panic("SIGILL caused by cmov, which this processor doesn't "
++                    "implement, boot a filesystem compiled for older "
++                    "processors");
++      else if(host_has_cmov == 1)
++              panic("SIGILL caused by cmov, which this processor claims to "
++                    "implement");
++      else if(host_has_cmov == -1)
++              panic("SIGILL caused by cmov, couldn't tell if this processor "
++                    "implements it, boot a filesystem compiled for older "
++                    "processors");
++      else panic("Bad value for host_has_cmov (%d)", host_has_cmov);
++      return(0);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/sys-i386/checksum.S
+===================================================================
+--- linux-2.4.29.orig/arch/um/sys-i386/checksum.S      1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/sys-i386/checksum.S   2005-05-03 22:28:14.582393352 +0300
+@@ -0,0 +1,460 @@
++/*
++ * INET               An implementation of the TCP/IP protocol suite for the LINUX
++ *            operating system.  INET is implemented using the  BSD Socket
++ *            interface as the means of communication with the user level.
++ *
++ *            IP/TCP/UDP checksumming routines
++ *
++ * Authors:   Jorge Cwik, <jorge@laser.satlink.net>
++ *            Arnt Gulbrandsen, <agulbra@nvg.unit.no>
++ *            Tom May, <ftom@netcom.com>
++ *              Pentium Pro/II routines:
++ *              Alexander Kjeldaas <astor@guardian.no>
++ *              Finn Arne Gangstad <finnag@guardian.no>
++ *            Lots of code moved from tcp.c and ip.c; see those files
++ *            for more names.
++ *
++ * Changes:     Ingo Molnar, converted csum_partial_copy() to 2.1 exception
++ *                         handling.
++ *            Andi Kleen,  add zeroing on error
++ *                   converted to pure assembler
++ *
++ *            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 <linux/config.h>
++#include <asm/errno.h>
++                              
++/*
++ * computes a partial checksum, e.g. for TCP/UDP fragments
++ */
++
++/*    
++unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum)
++ */
++              
++.text
++.align 4
++.globl arch_csum_partial                                                              
++              
++#ifndef CONFIG_X86_USE_PPRO_CHECKSUM
++
++        /*            
++         * Experiments with Ethernet and SLIP connections show that buff
++         * is aligned on either a 2-byte or 4-byte boundary.  We get at
++         * least a twofold speedup on 486 and Pentium if it is 4-byte aligned.
++         * Fortunately, it is easy to convert 2-byte alignment to 4-byte
++         * alignment for the unrolled loop.
++         */           
++arch_csum_partial:    
++      pushl %esi
++      pushl %ebx
++      movl 20(%esp),%eax      # Function arg: unsigned int sum
++      movl 16(%esp),%ecx      # Function arg: int len
++      movl 12(%esp),%esi      # Function arg: unsigned char *buff
++      testl $2, %esi          # Check alignment.
++      jz 2f                   # Jump if alignment is ok.
++      subl $2, %ecx           # Alignment uses up two bytes.
++      jae 1f                  # Jump if we had at least two bytes.
++      addl $2, %ecx           # ecx was < 2.  Deal with it.
++      jmp 4f
++1:    movw (%esi), %bx
++      addl $2, %esi
++      addw %bx, %ax
++      adcl $0, %eax
++2:
++      movl %ecx, %edx
++      shrl $5, %ecx
++      jz 2f
++      testl %esi, %esi
++1:    movl (%esi), %ebx
++      adcl %ebx, %eax
++      movl 4(%esi), %ebx
++      adcl %ebx, %eax
++      movl 8(%esi), %ebx
++      adcl %ebx, %eax
++      movl 12(%esi), %ebx
++      adcl %ebx, %eax
++      movl 16(%esi), %ebx
++      adcl %ebx, %eax
++      movl 20(%esi), %ebx
++      adcl %ebx, %eax
++      movl 24(%esi), %ebx
++      adcl %ebx, %eax
++      movl 28(%esi), %ebx
++      adcl %ebx, %eax
++      lea 32(%esi), %esi
++      dec %ecx
++      jne 1b
++      adcl $0, %eax
++2:    movl %edx, %ecx
++      andl $0x1c, %edx
++      je 4f
++      shrl $2, %edx           # This clears CF
++3:    adcl (%esi), %eax
++      lea 4(%esi), %esi
++      dec %edx
++      jne 3b
++      adcl $0, %eax
++4:    andl $3, %ecx
++      jz 7f
++      cmpl $2, %ecx
++      jb 5f
++      movw (%esi),%cx
++      leal 2(%esi),%esi
++      je 6f
++      shll $16,%ecx
++5:    movb (%esi),%cl
++6:    addl %ecx,%eax
++      adcl $0, %eax 
++7:    
++      popl %ebx
++      popl %esi
++      ret
++
++#else
++
++/* Version for PentiumII/PPro */
++
++arch_csum_partial:
++      pushl %esi
++      pushl %ebx
++      movl 20(%esp),%eax      # Function arg: unsigned int sum
++      movl 16(%esp),%ecx      # Function arg: int len
++      movl 12(%esp),%esi      # Function arg: const unsigned char *buf
++
++      testl $2, %esi         
++      jnz 30f                 
++10:
++      movl %ecx, %edx
++      movl %ecx, %ebx
++      andl $0x7c, %ebx
++      shrl $7, %ecx
++      addl %ebx,%esi
++      shrl $2, %ebx  
++      negl %ebx
++      lea 45f(%ebx,%ebx,2), %ebx
++      testl %esi, %esi
++      jmp *%ebx
++
++      # Handle 2-byte-aligned regions
++20:   addw (%esi), %ax
++      lea 2(%esi), %esi
++      adcl $0, %eax
++      jmp 10b
++
++30:   subl $2, %ecx          
++      ja 20b                 
++      je 32f
++      movzbl (%esi),%ebx      # csumming 1 byte, 2-aligned
++      addl %ebx, %eax
++      adcl $0, %eax
++      jmp 80f
++32:
++      addw (%esi), %ax        # csumming 2 bytes, 2-aligned
++      adcl $0, %eax
++      jmp 80f
++
++40: 
++      addl -128(%esi), %eax
++      adcl -124(%esi), %eax
++      adcl -120(%esi), %eax
++      adcl -116(%esi), %eax   
++      adcl -112(%esi), %eax   
++      adcl -108(%esi), %eax
++      adcl -104(%esi), %eax
++      adcl -100(%esi), %eax
++      adcl -96(%esi), %eax
++      adcl -92(%esi), %eax
++      adcl -88(%esi), %eax
++      adcl -84(%esi), %eax
++      adcl -80(%esi), %eax
++      adcl -76(%esi), %eax
++      adcl -72(%esi), %eax
++      adcl -68(%esi), %eax
++      adcl -64(%esi), %eax     
++      adcl -60(%esi), %eax     
++      adcl -56(%esi), %eax     
++      adcl -52(%esi), %eax   
++      adcl -48(%esi), %eax   
++      adcl -44(%esi), %eax
++      adcl -40(%esi), %eax
++      adcl -36(%esi), %eax
++      adcl -32(%esi), %eax
++      adcl -28(%esi), %eax
++      adcl -24(%esi), %eax
++      adcl -20(%esi), %eax
++      adcl -16(%esi), %eax
++      adcl -12(%esi), %eax
++      adcl -8(%esi), %eax
++      adcl -4(%esi), %eax
++45:
++      lea 128(%esi), %esi
++      adcl $0, %eax
++      dec %ecx
++      jge 40b
++      movl %edx, %ecx
++50:   andl $3, %ecx
++      jz 80f
++
++      # Handle the last 1-3 bytes without jumping
++      notl %ecx               # 1->2, 2->1, 3->0, higher bits are masked
++      movl $0xffffff,%ebx     # by the shll and shrl instructions
++      shll $3,%ecx
++      shrl %cl,%ebx
++      andl -128(%esi),%ebx    # esi is 4-aligned so should be ok
++      addl %ebx,%eax
++      adcl $0,%eax
++80: 
++      popl %ebx
++      popl %esi
++      ret
++                              
++#endif
++
++/*
++unsigned int csum_partial_copy_generic (const char *src, char *dst,
++                                int len, int sum, int *src_err_ptr, int *dst_err_ptr)
++ */ 
++
++/*
++ * Copy from ds while checksumming, otherwise like csum_partial
++ *
++ * The macros SRC and DST specify the type of access for the instruction.
++ * thus we can call a custom exception handler for all access types.
++ *
++ * FIXME: could someone double-check whether I haven't mixed up some SRC and
++ *      DST definitions? It's damn hard to trigger all cases.  I hope I got
++ *      them all but there's no guarantee.
++ */
++
++#define SRC(y...)                     \
++      9999: y;                        \
++      .section __ex_table, "a";       \
++      .long 9999b, 6001f      ;       \
++      .previous
++
++#define DST(y...)                     \
++      9999: y;                        \
++      .section __ex_table, "a";       \
++      .long 9999b, 6002f      ;       \
++      .previous
++
++.align 4
++.globl csum_partial_copy_generic_i386
++                              
++#ifndef CONFIG_X86_USE_PPRO_CHECKSUM
++
++#define ARGBASE 16            
++#define FP            12
++              
++csum_partial_copy_generic_i386:
++      subl  $4,%esp   
++      pushl %edi
++      pushl %esi
++      pushl %ebx
++      movl ARGBASE+16(%esp),%eax      # sum
++      movl ARGBASE+12(%esp),%ecx      # len
++      movl ARGBASE+4(%esp),%esi       # src
++      movl ARGBASE+8(%esp),%edi       # dst
++
++      testl $2, %edi                  # Check alignment. 
++      jz 2f                           # Jump if alignment is ok.
++      subl $2, %ecx                   # Alignment uses up two bytes.
++      jae 1f                          # Jump if we had at least two bytes.
++      addl $2, %ecx                   # ecx was < 2.  Deal with it.
++      jmp 4f
++SRC(1:        movw (%esi), %bx        )
++      addl $2, %esi
++DST(  movw %bx, (%edi)        )
++      addl $2, %edi
++      addw %bx, %ax   
++      adcl $0, %eax
++2:
++      movl %ecx, FP(%esp)
++      shrl $5, %ecx
++      jz 2f
++      testl %esi, %esi
++SRC(1:        movl (%esi), %ebx       )
++SRC(  movl 4(%esi), %edx      )
++      adcl %ebx, %eax
++DST(  movl %ebx, (%edi)       )
++      adcl %edx, %eax
++DST(  movl %edx, 4(%edi)      )
++
++SRC(  movl 8(%esi), %ebx      )
++SRC(  movl 12(%esi), %edx     )
++      adcl %ebx, %eax
++DST(  movl %ebx, 8(%edi)      )
++      adcl %edx, %eax
++DST(  movl %edx, 12(%edi)     )
++
++SRC(  movl 16(%esi), %ebx     )
++SRC(  movl 20(%esi), %edx     )
++      adcl %ebx, %eax
++DST(  movl %ebx, 16(%edi)     )
++      adcl %edx, %eax
++DST(  movl %edx, 20(%edi)     )
++
++SRC(  movl 24(%esi), %ebx     )
++SRC(  movl 28(%esi), %edx     )
++      adcl %ebx, %eax
++DST(  movl %ebx, 24(%edi)     )
++      adcl %edx, %eax
++DST(  movl %edx, 28(%edi)     )
++
++      lea 32(%esi), %esi
++      lea 32(%edi), %edi
++      dec %ecx
++      jne 1b
++      adcl $0, %eax
++2:    movl FP(%esp), %edx
++      movl %edx, %ecx
++      andl $0x1c, %edx
++      je 4f
++      shrl $2, %edx                   # This clears CF
++SRC(3:        movl (%esi), %ebx       )
++      adcl %ebx, %eax
++DST(  movl %ebx, (%edi)       )
++      lea 4(%esi), %esi
++      lea 4(%edi), %edi
++      dec %edx
++      jne 3b
++      adcl $0, %eax
++4:    andl $3, %ecx
++      jz 7f
++      cmpl $2, %ecx
++      jb 5f
++SRC(  movw (%esi), %cx        )
++      leal 2(%esi), %esi
++DST(  movw %cx, (%edi)        )
++      leal 2(%edi), %edi
++      je 6f
++      shll $16,%ecx
++SRC(5:        movb (%esi), %cl        )
++DST(  movb %cl, (%edi)        )
++6:    addl %ecx, %eax
++      adcl $0, %eax
++7:
++5000:
++
++# Exception handler:
++.section .fixup, "ax"                                                 
++
++6001:
++      movl ARGBASE+20(%esp), %ebx     # src_err_ptr
++      movl $-EFAULT, (%ebx)
++
++      # zero the complete destination - computing the rest
++      # is too much work 
++      movl ARGBASE+8(%esp), %edi      # dst
++      movl ARGBASE+12(%esp), %ecx     # len
++      xorl %eax,%eax
++      rep ; stosb
++
++      jmp 5000b
++
++6002:
++      movl ARGBASE+24(%esp), %ebx     # dst_err_ptr
++      movl $-EFAULT,(%ebx)
++      jmp 5000b
++
++.previous
++
++      popl %ebx
++      popl %esi
++      popl %edi
++      popl %ecx                       # equivalent to addl $4,%esp
++      ret     
++
++#else
++
++/* Version for PentiumII/PPro */
++
++#define ROUND1(x) \
++      SRC(movl x(%esi), %ebx  )       ;       \
++      addl %ebx, %eax                 ;       \
++      DST(movl %ebx, x(%edi)  )       ; 
++
++#define ROUND(x) \
++      SRC(movl x(%esi), %ebx  )       ;       \
++      adcl %ebx, %eax                 ;       \
++      DST(movl %ebx, x(%edi)  )       ;
++
++#define ARGBASE 12
++              
++csum_partial_copy_generic_i386:
++      pushl %ebx
++      pushl %edi
++      pushl %esi
++      movl ARGBASE+4(%esp),%esi       #src
++      movl ARGBASE+8(%esp),%edi       #dst    
++      movl ARGBASE+12(%esp),%ecx      #len
++      movl ARGBASE+16(%esp),%eax      #sum
++#     movl %ecx, %edx  
++      movl %ecx, %ebx  
++      movl %esi, %edx
++      shrl $6, %ecx     
++      andl $0x3c, %ebx  
++      negl %ebx
++      subl %ebx, %esi  
++      subl %ebx, %edi  
++      lea  -1(%esi),%edx
++      andl $-32,%edx
++      lea 3f(%ebx,%ebx), %ebx
++      testl %esi, %esi 
++      jmp *%ebx
++1:    addl $64,%esi
++      addl $64,%edi 
++      SRC(movb -32(%edx),%bl) ; SRC(movb (%edx),%bl)
++      ROUND1(-64) ROUND(-60) ROUND(-56) ROUND(-52)    
++      ROUND (-48) ROUND(-44) ROUND(-40) ROUND(-36)    
++      ROUND (-32) ROUND(-28) ROUND(-24) ROUND(-20)    
++      ROUND (-16) ROUND(-12) ROUND(-8)  ROUND(-4)     
++3:    adcl $0,%eax
++      addl $64, %edx
++      dec %ecx
++      jge 1b
++4:    movl ARGBASE+12(%esp),%edx      #len
++      andl $3, %edx
++      jz 7f
++      cmpl $2, %edx
++      jb 5f
++SRC(  movw (%esi), %dx         )
++      leal 2(%esi), %esi
++DST(  movw %dx, (%edi)         )
++      leal 2(%edi), %edi
++      je 6f
++      shll $16,%edx
++5:
++SRC(  movb (%esi), %dl         )
++DST(  movb %dl, (%edi)         )
++6:    addl %edx, %eax
++      adcl $0, %eax
++7:
++.section .fixup, "ax"
++6001: movl    ARGBASE+20(%esp), %ebx  # src_err_ptr   
++      movl $-EFAULT, (%ebx)
++      # zero the complete destination (computing the rest is too much work)
++      movl ARGBASE+8(%esp),%edi       # dst
++      movl ARGBASE+12(%esp),%ecx      # len
++      xorl %eax,%eax
++      rep; stosb
++      jmp 7b
++6002: movl ARGBASE+24(%esp), %ebx     # dst_err_ptr
++      movl $-EFAULT, (%ebx)
++      jmp  7b                 
++.previous                             
++
++      popl %esi
++      popl %edi
++      popl %ebx
++      ret
++                              
++#undef ROUND
++#undef ROUND1         
++              
++#endif
+Index: linux-2.4.29/arch/um/sys-i386/fault.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/sys-i386/fault.c 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/sys-i386/fault.c      2005-05-03 22:28:14.583393200 +0300
+@@ -0,0 +1,34 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <signal.h>
++#include "sysdep/ptrace.h"
++#include "sysdep/sigcontext.h"
++
++extern unsigned long search_exception_table(unsigned long addr);
++
++int arch_fixup(unsigned long address, void *sc_ptr)
++{
++      struct sigcontext *sc = sc_ptr;
++      unsigned long fixup;
++
++      fixup = search_exception_table(address);
++      if(fixup != 0){
++              sc->eip = fixup;
++              return(1);
++      }
++      return(0);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/sys-i386/ksyms.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/sys-i386/ksyms.c 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/sys-i386/ksyms.c      2005-05-03 22:28:14.584393048 +0300
+@@ -0,0 +1,17 @@
++#include "linux/module.h"
++#include "linux/in6.h"
++#include "linux/rwsem.h"
++#include "asm/byteorder.h"
++#include "asm/semaphore.h"
++#include "asm/uaccess.h"
++#include "asm/checksum.h"
++#include "asm/errno.h"
++
++EXPORT_SYMBOL(__down_failed);
++EXPORT_SYMBOL(__down_failed_interruptible);
++EXPORT_SYMBOL(__down_failed_trylock);
++EXPORT_SYMBOL(__up_wakeup);
++
++/* Networking helper routines. */
++EXPORT_SYMBOL(csum_partial_copy_from);
++EXPORT_SYMBOL(csum_partial_copy_to);
+Index: linux-2.4.29/arch/um/sys-i386/ldt.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/sys-i386/ldt.c   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/sys-i386/ldt.c        2005-05-03 22:28:14.585392896 +0300
+@@ -0,0 +1,94 @@
++/*
++ * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/config.h"
++#include "linux/slab.h"
++#include "asm/uaccess.h"
++#include "asm/ptrace.h"
++#include "choose-mode.h"
++#include "kern.h"
++
++#ifdef CONFIG_MODE_TT
++extern int modify_ldt(int func, void *ptr, unsigned long bytecount);
++
++/* XXX this needs copy_to_user and copy_from_user */
++
++int sys_modify_ldt_tt(int func, void *ptr, unsigned long bytecount)
++{
++      if(verify_area(VERIFY_READ, ptr, bytecount)) return(-EFAULT);
++      return(modify_ldt(func, ptr, bytecount));
++}
++#endif
++
++#ifdef CONFIG_MODE_SKAS
++extern int userspace_pid;
++
++int sys_modify_ldt_skas(int func, void *ptr, unsigned long bytecount)
++{
++      struct ptrace_ldt ldt;
++      void *buf;
++      int res, n;
++
++      buf = kmalloc(bytecount, GFP_KERNEL);
++      if(buf == NULL)
++              return(-ENOMEM);
++
++      res = 0;
++
++      switch(func){
++      case 1:
++      case 0x11:
++              res = copy_from_user(buf, ptr, bytecount);
++              break;
++      }
++
++      if(res != 0){
++              res = -EFAULT;
++              goto out;
++      }
++
++      ldt = ((struct ptrace_ldt) { .func      = func,
++                                   .ptr       = buf,
++                                   .bytecount = bytecount });
++      res = ptrace(PTRACE_LDT, userspace_pid, 0, (unsigned long) &ldt);
++      if(res < 0)
++              goto out;
++
++      switch(func){
++      case 0:
++      case 2:
++              n = res;
++              res = copy_to_user(ptr, buf, n);
++              if(res != 0)
++                      res = -EFAULT;
++              else 
++                      res = n;
++              break;
++      }
++
++ out:
++      kfree(buf);
++      return(res);
++}
++#endif
++
++int sys_modify_ldt(int func, void *ptr, unsigned long bytecount)
++{
++      return(CHOOSE_MODE_PROC(sys_modify_ldt_tt, sys_modify_ldt_skas, func, 
++                              ptr, bytecount));
++}
++
++
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/sys-i386/Makefile
+===================================================================
+--- linux-2.4.29.orig/arch/um/sys-i386/Makefile        1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/sys-i386/Makefile     2005-05-03 22:28:14.586392744 +0300
+@@ -0,0 +1,46 @@
++# 
++# Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++# Licensed under the GPL
++#
++
++O_TARGET = built-in.o
++
++obj-y = bugs.o checksum.o extable.o fault.o ksyms.o ldt.o ptrace.o \
++      ptrace_user.o semaphore.o sigcontext.o syscalls.o sysrq.o
++export-objs = ksyms.o
++
++USER_OBJS = bugs.o ptrace_user.o sigcontext.o fault.o
++
++SYMLINKS = semaphore.c extable.c
++
++semaphore.c-dir = kernel
++extable.c-dir = mm
++
++include $(TOPDIR)/Rules.make
++
++$(USER_OBJS) : %.o: %.c
++      $(CC) $(CFLAGS_$@) $(USER_CFLAGS) -c -o $@ $<
++
++define make_link
++      -rm -f $1
++      ln -sf $(TOPDIR)/arch/i386/$($1-dir)/$1 $1
++endef
++
++$(SYMLINKS): 
++      $(call make_link,$@)
++
++clean:
++      $(MAKE) -C util clean
++      rm -f $(SYMLINKS)
++
++fastdep:
++
++dep:
++
++archmrproper:
++
++archclean:
++
++archdep:
++
++modules:
+Index: linux-2.4.29/arch/um/sys-i386/ptrace.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/sys-i386/ptrace.c        1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/sys-i386/ptrace.c     2005-05-03 22:28:14.588392440 +0300
+@@ -0,0 +1,367 @@
++/* 
++ * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/sched.h"
++#include "asm/elf.h"
++#include "asm/ptrace.h"
++#include "asm/uaccess.h"
++#include "asm/unistd.h"
++#include "ptrace_user.h"
++#include "sysdep/sigcontext.h"
++#include "sysdep/sc.h"
++
++void arch_switch(void)
++{
++      update_debugregs(current->thread.arch.debugregs_seq);
++}
++
++int is_syscall(unsigned long addr)
++{
++      unsigned short instr;
++      int n;
++
++      n = copy_from_user(&instr, (void *) addr, sizeof(instr));
++      if(n){
++              printk("is_syscall : failed to read instruction from 0x%lx\n", 
++                     addr);
++              return(0);
++      }
++      /* int 0x80 or sysenter */
++      return((instr == 0x80cd) || (instr == 0x340f));
++}
++
++/* determines which flags the user has access to. */
++/* 1 = access 0 = no access */
++#define FLAG_MASK 0x00044dd5
++
++int putreg(struct task_struct *child, int regno, unsigned long value)
++{
++      regno >>= 2;
++      switch (regno) {
++      case FS:
++              if (value && (value & 3) != 3)
++                      return -EIO;
++              PT_REGS_FS(&child->thread.regs) = value;
++              return 0;
++      case GS:
++              if (value && (value & 3) != 3)
++                      return -EIO;
++              PT_REGS_GS(&child->thread.regs) = value;
++              return 0;
++      case DS:
++      case ES:
++              if (value && (value & 3) != 3)
++                      return -EIO;
++              value &= 0xffff;
++              break;
++      case SS:
++      case CS:
++              if ((value & 3) != 3)
++                      return -EIO;
++              value &= 0xffff;
++              break;
++      case EFL:
++              value &= FLAG_MASK;
++              value |= PT_REGS_EFLAGS(&child->thread.regs);
++              break;
++      }
++      PT_REGS_SET(&child->thread.regs, regno, value);
++      return 0;
++}
++
++unsigned long getreg(struct task_struct *child, int regno)
++{
++      unsigned long retval = ~0UL;
++
++      regno >>= 2;
++      switch (regno) {
++      case FS:
++      case GS:
++      case DS:
++      case ES:
++      case SS:
++      case CS:
++              retval = 0xffff;
++              /* fall through */
++      default:
++              retval &= PT_REG(&child->thread.regs, regno);
++      }
++      return retval;
++}
++
++struct i387_fxsave_struct {
++      unsigned short  cwd;
++      unsigned short  swd;
++      unsigned short  twd;
++      unsigned short  fop;
++      long    fip;
++      long    fcs;
++      long    foo;
++      long    fos;
++      long    mxcsr;
++      long    reserved;
++      long    st_space[32];   /* 8*16 bytes for each FP-reg = 128 bytes */
++      long    xmm_space[32];  /* 8*16 bytes for each XMM-reg = 128 bytes */
++      long    padding[56];
++};
++
++/*
++ * FPU tag word conversions.
++ */
++
++static inline unsigned short twd_i387_to_fxsr( unsigned short twd )
++{
++      unsigned int tmp; /* to avoid 16 bit prefixes in the code */
++ 
++      /* Transform each pair of bits into 01 (valid) or 00 (empty) */
++        tmp = ~twd;
++        tmp = (tmp | (tmp>>1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */
++        /* and move the valid bits to the lower byte. */
++        tmp = (tmp | (tmp >> 1)) & 0x3333; /* 00VV00VV00VV00VV */
++        tmp = (tmp | (tmp >> 2)) & 0x0f0f; /* 0000VVVV0000VVVV */
++        tmp = (tmp | (tmp >> 4)) & 0x00ff; /* 00000000VVVVVVVV */
++        return tmp;
++}
++
++static inline unsigned long twd_fxsr_to_i387( struct i387_fxsave_struct *fxsave )
++{
++      struct _fpxreg *st = NULL;
++      unsigned long twd = (unsigned long) fxsave->twd;
++      unsigned long tag;
++      unsigned long ret = 0xffff0000;
++      int i;
++
++#define FPREG_ADDR(f, n)      ((char *)&(f)->st_space + (n) * 16);
++
++      for ( i = 0 ; i < 8 ; i++ ) {
++              if ( twd & 0x1 ) {
++                      st = (struct _fpxreg *) FPREG_ADDR( fxsave, i );
++
++                      switch ( st->exponent & 0x7fff ) {
++                      case 0x7fff:
++                              tag = 2;                /* Special */
++                              break;
++                      case 0x0000:
++                              if ( !st->significand[0] &&
++                                   !st->significand[1] &&
++                                   !st->significand[2] &&
++                                   !st->significand[3] ) {
++                                      tag = 1;        /* Zero */
++                              } else {
++                                      tag = 2;        /* Special */
++                              }
++                              break;
++                      default:
++                              if ( st->significand[3] & 0x8000 ) {
++                                      tag = 0;        /* Valid */
++                              } else {
++                                      tag = 2;        /* Special */
++                              }
++                              break;
++                      }
++              } else {
++                      tag = 3;                        /* Empty */
++              }
++              ret |= (tag << (2 * i));
++              twd = twd >> 1;
++      }
++      return ret;
++}
++
++/*
++ * FXSR floating point environment conversions.
++ */
++
++#ifdef CONFIG_MODE_TT
++static inline int convert_fxsr_to_user_tt(struct _fpstate *buf, 
++                                        struct pt_regs *regs)
++{
++      struct i387_fxsave_struct *fxsave = SC_FXSR_ENV(PT_REGS_SC(regs));
++      unsigned long env[7];
++      struct _fpreg *to;
++      struct _fpxreg *from;
++      int i;
++
++      env[0] = (unsigned long)fxsave->cwd | 0xffff0000;
++      env[1] = (unsigned long)fxsave->swd | 0xffff0000;
++      env[2] = twd_fxsr_to_i387(fxsave);
++      env[3] = fxsave->fip;
++      env[4] = fxsave->fcs | ((unsigned long)fxsave->fop << 16);
++      env[5] = fxsave->foo;
++      env[6] = fxsave->fos;
++
++      if ( __copy_to_user( buf, env, 7 * sizeof(unsigned long) ) )
++              return 1;
++
++      to = &buf->_st[0];
++      from = (struct _fpxreg *) &fxsave->st_space[0];
++      for ( i = 0 ; i < 8 ; i++, to++, from++ ) {
++              if ( __copy_to_user( to, from, sizeof(*to) ) )
++                      return 1;
++      }
++      return 0;
++}
++#endif
++
++static inline int convert_fxsr_to_user(struct _fpstate *buf, 
++                                     struct pt_regs *regs)
++{
++      return(CHOOSE_MODE(convert_fxsr_to_user_tt(buf, regs), 0));
++}
++
++#ifdef CONFIG_MODE_TT
++static inline int convert_fxsr_from_user_tt(struct pt_regs *regs,
++                                          struct _fpstate *buf)
++{
++      struct i387_fxsave_struct *fxsave = SC_FXSR_ENV(PT_REGS_SC(regs));
++      unsigned long env[7];
++      struct _fpxreg *to;
++      struct _fpreg *from;
++      int i;
++
++      if ( __copy_from_user( env, buf, 7 * sizeof(long) ) )
++              return 1;
++
++      fxsave->cwd = (unsigned short)(env[0] & 0xffff);
++      fxsave->swd = (unsigned short)(env[1] & 0xffff);
++      fxsave->twd = twd_i387_to_fxsr((unsigned short)(env[2] & 0xffff));
++      fxsave->fip = env[3];
++      fxsave->fop = (unsigned short)((env[4] & 0xffff0000) >> 16);
++      fxsave->fcs = (env[4] & 0xffff);
++      fxsave->foo = env[5];
++      fxsave->fos = env[6];
++
++      to = (struct _fpxreg *) &fxsave->st_space[0];
++      from = &buf->_st[0];
++      for ( i = 0 ; i < 8 ; i++, to++, from++ ) {
++              if ( __copy_from_user( to, from, sizeof(*from) ) )
++                      return 1;
++      }
++      return 0;
++}
++#endif
++
++static inline int convert_fxsr_from_user(struct pt_regs *regs, 
++                                       struct _fpstate *buf)
++{
++      return(CHOOSE_MODE(convert_fxsr_from_user_tt(regs, buf), 0));
++}
++
++int get_fpregs(unsigned long buf, struct task_struct *child)
++{
++      int err;
++
++      err = convert_fxsr_to_user((struct _fpstate *) buf, 
++                                 &child->thread.regs);
++      if(err) return(-EFAULT);
++      else return(0);
++}
++
++int set_fpregs(unsigned long buf, struct task_struct *child)
++{
++      int err;
++
++      err = convert_fxsr_from_user(&child->thread.regs, 
++                                   (struct _fpstate *) buf);
++      if(err) return(-EFAULT);
++      else return(0);
++}
++
++#ifdef CONFIG_MODE_TT
++int get_fpxregs_tt(unsigned long buf, struct task_struct *tsk)
++{
++      struct pt_regs *regs = &tsk->thread.regs;
++      struct i387_fxsave_struct *fxsave = SC_FXSR_ENV(PT_REGS_SC(regs));
++      int err;
++
++      err = __copy_to_user((void *) buf, fxsave,
++                           sizeof(struct user_fxsr_struct));
++      if(err) return -EFAULT;
++      else return 0;
++}
++#endif
++
++int get_fpxregs(unsigned long buf, struct task_struct *tsk)
++{
++      return(CHOOSE_MODE(get_fpxregs_tt(buf, tsk), 0));
++}
++
++#ifdef CONFIG_MODE_TT
++int set_fpxregs_tt(unsigned long buf, struct task_struct *tsk)
++{
++      struct pt_regs *regs = &tsk->thread.regs;
++      struct i387_fxsave_struct *fxsave = SC_FXSR_ENV(PT_REGS_SC(regs));
++      int err;
++
++      err = __copy_from_user(fxsave, (void *) buf,
++                             sizeof(struct user_fxsr_struct) );
++      if(err) return -EFAULT;
++      else return 0;
++}
++#endif
++
++int set_fpxregs(unsigned long buf, struct task_struct *tsk)
++{
++      return(CHOOSE_MODE(set_fpxregs_tt(buf, tsk), 0));
++}
++
++#ifdef notdef
++int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpu)
++{
++      fpu->cwd = (((SC_FP_CW(PT_REGS_SC(regs)) & 0xffff) << 16) |
++                  (SC_FP_SW(PT_REGS_SC(regs)) & 0xffff));
++      fpu->swd = SC_FP_CSSEL(PT_REGS_SC(regs)) & 0xffff;
++      fpu->twd = SC_FP_IPOFF(PT_REGS_SC(regs));
++      fpu->fip = SC_FP_CSSEL(PT_REGS_SC(regs)) & 0xffff;
++      fpu->fcs = SC_FP_DATAOFF(PT_REGS_SC(regs));
++      fpu->foo = SC_FP_DATASEL(PT_REGS_SC(regs));
++      fpu->fos = 0;
++      memcpy(fpu->st_space, (void *) SC_FP_ST(PT_REGS_SC(regs)),
++             sizeof(fpu->st_space));
++      return(1);
++}
++#endif
++
++#ifdef CONFIG_MODE_TT
++static inline void copy_fpu_fxsave_tt(struct pt_regs *regs,
++                                    struct user_i387_struct *buf)
++{
++      struct i387_fxsave_struct *fpu = SC_FXSR_ENV(PT_REGS_SC(regs));
++      unsigned short *to;
++      unsigned short *from;
++      int i;
++
++      memcpy( buf, fpu, 7 * sizeof(long) );
++
++      to = (unsigned short *) &buf->st_space[0];
++      from = (unsigned short *) &fpu->st_space[0];
++      for ( i = 0 ; i < 8 ; i++, to += 5, from += 8 ) {
++              memcpy( to, from, 5 * sizeof(unsigned short) );
++      }
++}
++#endif
++
++static inline void copy_fpu_fxsave(struct pt_regs *regs,
++                                 struct user_i387_struct *buf)
++{
++      (void) CHOOSE_MODE(copy_fpu_fxsave_tt(regs, buf), 0);
++}
++
++int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpu )
++{
++      copy_fpu_fxsave(regs, (struct user_i387_struct *) fpu);
++      return(1);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/sys-i386/ptrace_user.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/sys-i386/ptrace_user.c   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/sys-i386/ptrace_user.c        2005-05-03 22:28:14.589392288 +0300
+@@ -0,0 +1,118 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <stdio.h>
++#include <errno.h>
++#include <unistd.h>
++#include <linux/stddef.h>
++#include <sys/ptrace.h>
++#include <asm/ptrace.h>
++#include <asm/user.h>
++#include "kern_util.h"
++#include "sysdep/thread.h"
++#include "user.h"
++#include "os.h"
++
++int ptrace_getregs(long pid, unsigned long *regs_out)
++{
++      return(ptrace(PTRACE_GETREGS, pid, 0, regs_out));
++}
++
++int ptrace_setregs(long pid, unsigned long *regs)
++{
++      return(ptrace(PTRACE_SETREGS, pid, 0, regs));
++}
++
++int ptrace_getfpregs(long pid, unsigned long *regs)
++{
++      return(ptrace(PTRACE_GETFPREGS, pid, 0, regs));
++}
++
++static void write_debugregs(int pid, unsigned long *regs)
++{
++      struct user *dummy;
++      int nregs, i;
++
++      dummy = NULL;
++      nregs = sizeof(dummy->u_debugreg)/sizeof(dummy->u_debugreg[0]);
++      for(i = 0; i < nregs; i++){
++              if((i == 4) || (i == 5)) continue;
++              if(ptrace(PTRACE_POKEUSER, pid, &dummy->u_debugreg[i],
++                        regs[i]) < 0)
++                      printk("write_debugregs - ptrace failed on "
++                             "register %d, value = 0x%x, errno = %d\n", i, 
++                             regs[i], errno);
++      }
++}
++
++static void read_debugregs(int pid, unsigned long *regs)
++{
++      struct user *dummy;
++      int nregs, i;
++
++      dummy = NULL;
++      nregs = sizeof(dummy->u_debugreg)/sizeof(dummy->u_debugreg[0]);
++      for(i = 0; i < nregs; i++){
++              regs[i] = ptrace(PTRACE_PEEKUSER, pid, 
++                               &dummy->u_debugreg[i], 0);
++      }
++}
++
++/* Accessed only by the tracing thread */
++static unsigned long kernel_debugregs[8] = { [ 0 ... 7 ] = 0 };
++static int debugregs_seq = 0;
++
++void arch_enter_kernel(void *task, int pid)
++{
++      read_debugregs(pid, TASK_DEBUGREGS(task));
++      write_debugregs(pid, kernel_debugregs);
++}
++
++void arch_leave_kernel(void *task, int pid)
++{
++      read_debugregs(pid, kernel_debugregs);
++      write_debugregs(pid, TASK_DEBUGREGS(task));
++}
++
++void ptrace_pokeuser(unsigned long addr, unsigned long data)
++{
++      if((addr < offsetof(struct user, u_debugreg[0])) ||
++         (addr > offsetof(struct user, u_debugreg[7])))
++              return;
++      addr -= offsetof(struct user, u_debugreg[0]);
++      addr = addr >> 2;
++      if(kernel_debugregs[addr] == data) return;
++
++      kernel_debugregs[addr] = data;
++      debugregs_seq++;
++}
++
++static void update_debugregs_cb(void *arg)
++{
++      int pid = *((int *) arg);
++
++      write_debugregs(pid, kernel_debugregs);
++}
++
++void update_debugregs(int seq)
++{
++      int me;
++
++      if(seq == debugregs_seq) return;
++
++      me = os_getpid();
++      initial_thread_cb(update_debugregs_cb, &me);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/sys-i386/sigcontext.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/sys-i386/sigcontext.c    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/sys-i386/sigcontext.c 2005-05-03 22:28:14.590392136 +0300
+@@ -0,0 +1,80 @@
++/* 
++ * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include <stddef.h>
++#include <string.h>
++#include <asm/ptrace.h>
++#include <asm/sigcontext.h>
++#include "sysdep/ptrace.h"
++#include "kern_util.h"
++#include "frame_user.h"
++
++int sc_size(void *data)
++{
++      struct arch_frame_data *arch = data;
++
++      return(sizeof(struct sigcontext) + arch->fpstate_size);
++}
++
++void sc_to_sc(void *to_ptr, void *from_ptr)
++{
++      struct sigcontext *to = to_ptr, *from = from_ptr;
++      int size = sizeof(*to) + signal_frame_sc.common.arch.fpstate_size;
++
++      memcpy(to, from, size);
++      if(from->fpstate != NULL) to->fpstate = (struct _fpstate *) (to + 1);
++}
++
++unsigned long *sc_sigmask(void *sc_ptr)
++{
++      struct sigcontext *sc = sc_ptr;
++
++      return(&sc->oldmask);
++}
++
++int sc_get_fpregs(unsigned long buf, void *sc_ptr)
++{
++      struct sigcontext *sc = sc_ptr;
++      struct _fpstate *from = sc->fpstate, *to = (struct _fpstate *) buf;
++      int err = 0;
++
++      if(from == NULL){
++              err |= clear_user_proc(&to->cw, sizeof(to->cw));
++              err |= clear_user_proc(&to->sw, sizeof(to->sw));
++              err |= clear_user_proc(&to->tag, sizeof(to->tag));
++              err |= clear_user_proc(&to->ipoff, sizeof(to->ipoff));
++              err |= clear_user_proc(&to->cssel, sizeof(to->cssel));
++              err |= clear_user_proc(&to->dataoff, sizeof(to->dataoff));
++              err |= clear_user_proc(&to->datasel, sizeof(to->datasel));
++              err |= clear_user_proc(&to->_st, sizeof(to->_st));
++      }
++      else {
++              err |= copy_to_user_proc(&to->cw, &from->cw, sizeof(to->cw));
++              err |= copy_to_user_proc(&to->sw, &from->sw, sizeof(to->sw));
++              err |= copy_to_user_proc(&to->tag, &from->tag, 
++                                       sizeof(to->tag));
++              err |= copy_to_user_proc(&to->ipoff, &from->ipoff, 
++                                       sizeof(to->ipoff));
++              err |= copy_to_user_proc(&to->cssel,& from->cssel, 
++                                       sizeof(to->cssel));
++              err |= copy_to_user_proc(&to->dataoff, &from->dataoff, 
++                                  sizeof(to->dataoff));
++              err |= copy_to_user_proc(&to->datasel, &from->datasel, 
++                                  sizeof(to->datasel));
++              err |= copy_to_user_proc(to->_st, from->_st, sizeof(to->_st));
++      }
++      return(err);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/sys-i386/syscalls.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/sys-i386/syscalls.c      1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/sys-i386/syscalls.c   2005-05-03 22:28:14.590392136 +0300
+@@ -0,0 +1,68 @@
++/* 
++ * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "asm/mman.h"
++#include "asm/uaccess.h"
++#include "asm/unistd.h"
++
++/*
++ * Perform the select(nd, in, out, ex, tv) and mmap() system
++ * calls. Linux/i386 didn't use to be able to handle more than
++ * 4 system call parameters, so these system calls used a memory
++ * block for parameter passing..
++ */
++
++struct mmap_arg_struct {
++      unsigned long addr;
++      unsigned long len;
++      unsigned long prot;
++      unsigned long flags;
++      unsigned long fd;
++      unsigned long offset;
++};
++
++extern int old_mmap(unsigned long addr, unsigned long len,
++                  unsigned long prot, unsigned long flags,
++                  unsigned long fd, unsigned long offset);
++
++int old_mmap_i386(struct mmap_arg_struct *arg)
++{
++      struct mmap_arg_struct a;
++      int err = -EFAULT;
++
++      if (copy_from_user(&a, arg, sizeof(a)))
++              goto out;
++
++      err = old_mmap(a.addr, a.len, a.prot, a.flags, a.fd, a.offset);
++ out:
++      return err;
++}
++
++struct sel_arg_struct {
++      unsigned long n;
++      fd_set *inp, *outp, *exp;
++      struct timeval *tvp;
++};
++
++int old_select(struct sel_arg_struct *arg)
++{
++      struct sel_arg_struct a;
++
++      if (copy_from_user(&a, arg, sizeof(a)))
++              return -EFAULT;
++      /* sys_select() does the appropriate kernel locking */
++      return sys_select(a.n, a.inp, a.outp, a.exp, a.tvp);
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/sys-i386/sysrq.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/sys-i386/sysrq.c 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/sys-i386/sysrq.c      2005-05-03 22:28:14.591391984 +0300
+@@ -0,0 +1,30 @@
++#include "linux/kernel.h"
++#include "linux/smp.h"
++#include "linux/sched.h"
++#include "asm/ptrace.h"
++#include "sysrq.h"
++
++void show_regs(struct pt_regs *regs)
++{
++        printk("\n");
++        printk("EIP: %04lx:[<%08lx>] CPU: %d %s", 
++             0xffff & PT_REGS_CS(regs), PT_REGS_IP(regs),
++             smp_processor_id(), print_tainted());
++        if (PT_REGS_CS(regs) & 3)
++                printk(" ESP: %04lx:%08lx", 0xffff & PT_REGS_SS(regs),
++                     PT_REGS_SP(regs));
++        printk(" EFLAGS: %08lx\n    %s\n", PT_REGS_EFLAGS(regs),
++             print_tainted());
++        printk("EAX: %08lx EBX: %08lx ECX: %08lx EDX: %08lx\n",
++                PT_REGS_EAX(regs), PT_REGS_EBX(regs), 
++             PT_REGS_ECX(regs), 
++             PT_REGS_EDX(regs));
++        printk("ESI: %08lx EDI: %08lx EBP: %08lx",
++             PT_REGS_ESI(regs), PT_REGS_EDI(regs), 
++             PT_REGS_EBP(regs));
++        printk(" DS: %04lx ES: %04lx\n",
++             0xffff & PT_REGS_DS(regs), 
++             0xffff & PT_REGS_ES(regs));
++
++        show_trace((unsigned long *) &regs);
++}
+Index: linux-2.4.29/arch/um/sys-i386/util/Makefile
+===================================================================
+--- linux-2.4.29.orig/arch/um/sys-i386/util/Makefile   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/sys-i386/util/Makefile        2005-05-03 22:28:14.592391832 +0300
+@@ -0,0 +1,28 @@
++EXE = mk_sc mk_thread
++
++include $(TOPDIR)/Rules.make
++
++all : $(EXE)
++
++mk_sc : mk_sc.o
++      $(HOSTCC) -o mk_sc mk_sc.o
++
++mk_sc.o : mk_sc.c
++      $(HOSTCC) -c $< 
++
++mk_thread : mk_thread_user.o mk_thread_kern.o
++      $(HOSTCC) -o mk_thread mk_thread_user.o mk_thread_kern.o
++
++mk_thread_user.o : mk_thread_user.c
++      $(HOSTCC) -c $< 
++
++mk_thread_kern.o : mk_thread_kern.c
++      $(HOSTCC) $(CFLAGS) -c $< 
++
++clean :
++      $(RM) $(EXE) *.o
++
++archmrproper : clean
++
++fastdep :
++
+Index: linux-2.4.29/arch/um/sys-i386/util/mk_sc.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/sys-i386/util/mk_sc.c    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/sys-i386/util/mk_sc.c 2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,52 @@
++#include <stdio.h>
++#include <signal.h>
++#include <linux/stddef.h>
++
++#define SC_OFFSET(name, field) \
++  printf("#define " name "(sc) *((unsigned long *) &(((char *) (sc))[%d]))\n",\
++       offsetof(struct sigcontext, field))
++
++#define SC_FP_OFFSET(name, field) \
++  printf("#define " name \
++       "(sc) *((unsigned long *) &(((char *) (SC_FPSTATE(sc)))[%d]))\n",\
++       offsetof(struct _fpstate, field))
++
++#define SC_FP_OFFSET_PTR(name, field, type) \
++  printf("#define " name \
++       "(sc) ((" type " *) &(((char *) (SC_FPSTATE(sc)))[%d]))\n",\
++       offsetof(struct _fpstate, field))
++
++int main(int argc, char **argv)
++{
++  SC_OFFSET("SC_IP", eip);
++  SC_OFFSET("SC_SP", esp);
++  SC_OFFSET("SC_FS", fs);
++  SC_OFFSET("SC_GS", gs);
++  SC_OFFSET("SC_DS", ds);
++  SC_OFFSET("SC_ES", es);
++  SC_OFFSET("SC_SS", ss);
++  SC_OFFSET("SC_CS", cs);
++  SC_OFFSET("SC_EFLAGS", eflags);
++  SC_OFFSET("SC_EAX", eax);
++  SC_OFFSET("SC_EBX", ebx);
++  SC_OFFSET("SC_ECX", ecx);
++  SC_OFFSET("SC_EDX", edx);
++  SC_OFFSET("SC_EDI", edi);
++  SC_OFFSET("SC_ESI", esi);
++  SC_OFFSET("SC_EBP", ebp);
++  SC_OFFSET("SC_TRAPNO", trapno);
++  SC_OFFSET("SC_ERR", err);
++  SC_OFFSET("SC_CR2", cr2);
++  SC_OFFSET("SC_FPSTATE", fpstate);
++  SC_OFFSET("SC_SIGMASK", oldmask);
++  SC_FP_OFFSET("SC_FP_CW", cw);
++  SC_FP_OFFSET("SC_FP_SW", sw);
++  SC_FP_OFFSET("SC_FP_TAG", tag);
++  SC_FP_OFFSET("SC_FP_IPOFF", ipoff);
++  SC_FP_OFFSET("SC_FP_CSSEL", cssel);
++  SC_FP_OFFSET("SC_FP_DATAOFF", dataoff);
++  SC_FP_OFFSET("SC_FP_DATASEL", datasel);
++  SC_FP_OFFSET_PTR("SC_FP_ST", _st, "struct _fpstate");
++  SC_FP_OFFSET_PTR("SC_FXSR_ENV", _fxsr_env, "void");
++  return(0);
++}
+Index: linux-2.4.29/arch/um/sys-i386/util/mk_thread_kern.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/sys-i386/util/mk_thread_kern.c   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/sys-i386/util/mk_thread_kern.c        2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,22 @@
++#include "linux/config.h"
++#include "linux/stddef.h"
++#include "linux/sched.h"
++
++extern void print_head(void);
++extern void print_constant_ptr(char *name, int value);
++extern void print_constant(char *name, char *type, int value);
++extern void print_tail(void);
++
++#define THREAD_OFFSET(field) offsetof(struct task_struct, thread.field)
++
++int main(int argc, char **argv)
++{
++  print_head();
++  print_constant_ptr("TASK_DEBUGREGS", THREAD_OFFSET(arch.debugregs));
++#ifdef CONFIG_MODE_TT
++  print_constant("TASK_EXTERN_PID", "int", THREAD_OFFSET(mode.tt.extern_pid));
++#endif
++  print_tail();
++  return(0);
++}
++
+Index: linux-2.4.29/arch/um/sys-i386/util/mk_thread_user.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/sys-i386/util/mk_thread_user.c   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/sys-i386/util/mk_thread_user.c        2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,30 @@
++#include <stdio.h>
++
++void print_head(void)
++{
++  printf("/*\n");
++  printf(" * Generated by mk_thread\n");
++  printf(" */\n");
++  printf("\n");
++  printf("#ifndef __UM_THREAD_H\n");
++  printf("#define __UM_THREAD_H\n");
++  printf("\n");
++}
++
++void print_constant_ptr(char *name, int value)
++{
++  printf("#define %s(task) ((unsigned long *) "
++       "&(((char *) (task))[%d]))\n", name, value);
++}
++
++void print_constant(char *name, char *type, int value)
++{
++  printf("#define %s(task) *((%s *) &(((char *) (task))[%d]))\n", name, type, 
++       value);
++}
++
++void print_tail(void)
++{
++  printf("\n");
++  printf("#endif\n");
++}
+Index: linux-2.4.29/arch/um/sys-ia64/Makefile
+===================================================================
+--- linux-2.4.29.orig/arch/um/sys-ia64/Makefile        1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/sys-ia64/Makefile     2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,26 @@
++OBJ = sys.o
++
++OBJS =
++
++all: $(OBJ)
++
++$(OBJ): $(OBJS)
++      rm -f $@
++      $(LD) $(LINKFLAGS) --start-group $^ --end-group -o $@
++clean:
++      rm -f $(OBJS)
++
++fastdep:
++
++archmrproper:
++
++archclean:
++      rm -f link.ld
++      @$(MAKEBOOT) clean
++
++archdep:
++      @$(MAKEBOOT) dep
++
++modules:
++
++include $(TOPDIR)/Rules.make
+Index: linux-2.4.29/arch/um/sys-ppc/Makefile
+===================================================================
+--- linux-2.4.29.orig/arch/um/sys-ppc/Makefile 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/sys-ppc/Makefile      2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,80 @@
++OBJ = sys.o
++
++.S.o:
++      $(CC) $(AFLAGS) -D__ASSEMBLY__ -D__UM_PPC__ -c $< -o $*.o
++
++OBJS = ptrace.o sigcontext.o semaphore.o checksum.o miscthings.o misc.o \
++      ptrace_user.o sysrq.o
++
++EXTRA_AFLAGS := -DCONFIG_ALL_PPC -I. -I$(TOPDIR)/arch/ppc/kernel
++
++all: $(OBJ)
++
++$(OBJ): $(OBJS)
++      rm -f $@
++      $(LD) $(LINKFLAGS) --start-group $^ --end-group -o $@
++
++ptrace_user.o: ptrace_user.c
++      $(CC) -D__KERNEL__ $(USER_CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
++
++sigcontext.o: sigcontext.c
++      $(CC) $(USER_CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
++
++semaphore.c:
++      rm -f $@
++      ln -s $(TOPDIR)/arch/ppc/kernel/$@ $@
++
++checksum.S:
++      rm -f $@
++      ln -s $(TOPDIR)/arch/ppc/lib/$@ $@
++
++mk_defs.c:
++      rm -f $@
++      ln -s $(TOPDIR)/arch/ppc/kernel/$@ $@
++
++ppc_defs.head:
++      rm -f $@
++      ln -s $(TOPDIR)/arch/ppc/kernel/$@ $@
++
++ppc_defs.h: mk_defs.c ppc_defs.head \
++              $(TOPDIR)/include/asm-ppc/mmu.h \
++              $(TOPDIR)/include/asm-ppc/processor.h \
++              $(TOPDIR)/include/asm-ppc/pgtable.h \
++              $(TOPDIR)/include/asm-ppc/ptrace.h
++#     $(CC) $(CFLAGS) -S mk_defs.c
++      cp ppc_defs.head ppc_defs.h
++# for bk, this way we can write to the file even if it's not checked out
++      echo '#define THREAD 608' >> ppc_defs.h
++      echo '#define PT_REGS 8' >> ppc_defs.h
++      echo '#define CLONE_VM 256' >> ppc_defs.h
++#     chmod u+w ppc_defs.h
++#     grep '^#define' mk_defs.s >> ppc_defs.h
++#     rm mk_defs.s
++
++# the asm link is horrible, and breaks the other targets.  This is also
++# not going to work with parallel makes.
++
++checksum.o: checksum.S
++      rm -f asm
++      ln -s $(TOPDIR)/include/asm-ppc asm
++      $(CC) $(EXTRA_AFLAGS) $(AFLAGS) -D__ASSEMBLY__ -D__UM_PPC__ -c $< -o $*.o
++      rm -f asm
++
++misc.o: misc.S ppc_defs.h
++      rm -f asm
++      ln -s $(TOPDIR)/include/asm-ppc asm
++      $(CC) $(EXTRA_AFLAGS) $(AFLAGS) -D__ASSEMBLY__ -D__UM_PPC__ -c $< -o $*.o
++      rm -f asm
++
++clean:
++      rm -f $(OBJS)
++      rm -f ppc_defs.h
++      rm -f checksum.S semaphore.c mk_defs.c
++
++fastdep:
++
++dep:
++
++modules:
++
++include $(TOPDIR)/Rules.make
+Index: linux-2.4.29/arch/um/sys-ppc/misc.S
+===================================================================
+--- linux-2.4.29.orig/arch/um/sys-ppc/misc.S   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/sys-ppc/misc.S        2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,116 @@
++/*
++ * This file contains miscellaneous low-level functions.
++ *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
++ *
++ * Largely rewritten by Cort Dougan (cort@cs.nmt.edu)
++ * and Paul Mackerras.
++ *
++ * A couple of functions stolen from arch/ppc/kernel/misc.S for UML
++ * by Chris Emerson.
++ *
++ * 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 <linux/config.h>
++#include <asm/processor.h>
++#include "ppc_asm.h"
++
++#if defined(CONFIG_4xx) || defined(CONFIG_8xx)
++#define CACHE_LINE_SIZE               16
++#define LG_CACHE_LINE_SIZE    4
++#define MAX_COPY_PREFETCH     1
++#elif !defined(CONFIG_PPC64BRIDGE)
++#define CACHE_LINE_SIZE               32
++#define LG_CACHE_LINE_SIZE    5
++#define MAX_COPY_PREFETCH     4
++#else
++#define CACHE_LINE_SIZE               128
++#define LG_CACHE_LINE_SIZE    7
++#define MAX_COPY_PREFETCH     1
++#endif /* CONFIG_4xx || CONFIG_8xx */
++
++      .text
++
++/*
++ * Clear a page using the dcbz instruction, which doesn't cause any
++ * memory traffic (except to write out any cache lines which get
++ * displaced).  This only works on cacheable memory.
++ */
++_GLOBAL(clear_page)
++      li      r0,4096/CACHE_LINE_SIZE
++      mtctr   r0
++#ifdef CONFIG_8xx
++      li      r4, 0
++1:    stw     r4, 0(r3)
++      stw     r4, 4(r3)
++      stw     r4, 8(r3)
++      stw     r4, 12(r3)
++#else
++1:    dcbz    0,r3
++#endif
++      addi    r3,r3,CACHE_LINE_SIZE
++      bdnz    1b
++      blr
++
++/*
++ * Copy a whole page.  We use the dcbz instruction on the destination
++ * to reduce memory traffic (it eliminates the unnecessary reads of
++ * the destination into cache).  This requires that the destination
++ * is cacheable.
++ */
++#define COPY_16_BYTES         \
++      lwz     r6,4(r4);       \
++      lwz     r7,8(r4);       \
++      lwz     r8,12(r4);      \
++      lwzu    r9,16(r4);      \
++      stw     r6,4(r3);       \
++      stw     r7,8(r3);       \
++      stw     r8,12(r3);      \
++      stwu    r9,16(r3)
++
++_GLOBAL(copy_page)
++      addi    r3,r3,-4
++      addi    r4,r4,-4
++      li      r5,4
++
++#ifndef CONFIG_8xx
++#if MAX_COPY_PREFETCH > 1
++      li      r0,MAX_COPY_PREFETCH
++      li      r11,4
++      mtctr   r0
++11:   dcbt    r11,r4
++      addi    r11,r11,CACHE_LINE_SIZE
++      bdnz    11b
++#else /* MAX_COPY_PREFETCH == 1 */
++      dcbt    r5,r4
++      li      r11,CACHE_LINE_SIZE+4
++#endif /* MAX_COPY_PREFETCH */
++#endif /* CONFIG_8xx */
++
++      li      r0,4096/CACHE_LINE_SIZE
++      mtctr   r0
++1:
++#ifndef CONFIG_8xx
++      dcbt    r11,r4
++      dcbz    r5,r3
++#endif
++      COPY_16_BYTES
++#if CACHE_LINE_SIZE >= 32
++      COPY_16_BYTES
++#if CACHE_LINE_SIZE >= 64
++      COPY_16_BYTES
++      COPY_16_BYTES
++#if CACHE_LINE_SIZE >= 128
++      COPY_16_BYTES
++      COPY_16_BYTES
++      COPY_16_BYTES
++      COPY_16_BYTES
++#endif
++#endif
++#endif
++      bdnz    1b
++      blr
+Index: linux-2.4.29/arch/um/sys-ppc/miscthings.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/sys-ppc/miscthings.c     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/sys-ppc/miscthings.c  2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,53 @@
++#include "linux/threads.h"
++#include "linux/stddef.h"  // for NULL
++#include "linux/elf.h"  // for AT_NULL
++
++/* The following function nicked from arch/ppc/kernel/process.c and
++ * adapted slightly */
++/*
++ * XXX ld.so expects the auxiliary table to start on
++ * a 16-byte boundary, so we have to find it and
++ * move it up. :-(
++ */
++void shove_aux_table(unsigned long sp)
++{
++      int argc;
++      char *p;
++      unsigned long e;
++      unsigned long aux_start, offset;
++
++      argc = *(int *)sp;
++      sp += sizeof(int) + (argc + 1) * sizeof(char *);
++      /* skip over the environment pointers */
++      do {
++              p = *(char **)sp;
++              sp += sizeof(char *);
++      } while (p != NULL);
++      aux_start = sp;
++      /* skip to the end of the auxiliary table */
++      do {
++              e = *(unsigned long *)sp;
++              sp += 2 * sizeof(unsigned long);
++      } while (e != AT_NULL);
++      offset = ((aux_start + 15) & ~15) - aux_start;
++      if (offset != 0) {
++              do {
++                      sp -= sizeof(unsigned long);
++                      e = *(unsigned long *)sp;
++                      *(unsigned long *)(sp + offset) = e;
++              } while (sp > aux_start);
++      }
++}
++/* END stuff taken from arch/ppc/kernel/process.c */
++
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/sys-ppc/ptrace.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/sys-ppc/ptrace.c 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/sys-ppc/ptrace.c      2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,28 @@
++#include "linux/sched.h"
++#include "asm/ptrace.h"
++
++int putreg(struct task_struct *child, unsigned long regno, 
++                unsigned long value)
++{
++      child->thread.process_regs.regs[regno >> 2] = value;
++      return 0;
++}
++
++unsigned long getreg(struct task_struct *child, unsigned long regno)
++{
++      unsigned long retval = ~0UL;
++
++      retval &= child->thread.process_regs.regs[regno >> 2];
++      return retval;
++}
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/sys-ppc/ptrace_user.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/sys-ppc/ptrace_user.c    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/sys-ppc/ptrace_user.c 2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,40 @@
++#include <sys/ptrace.h>
++#include <errno.h>
++#include <asm/ptrace.h>
++#include "sysdep/ptrace.h"
++
++int ptrace_getregs(long pid, unsigned long *regs_out)
++{
++    int i;
++    for (i=0; i < sizeof(struct sys_pt_regs)/sizeof(PPC_REG); ++i) {
++      errno = 0;
++      regs_out->regs[i] = ptrace(PTRACE_PEEKUSER, pid, i*4, 0);
++      if (errno) {
++          return -errno;
++      }
++    }
++    return 0;
++}
++
++int ptrace_setregs(long pid, unsigned long *regs_in)
++{
++    int i;
++    for (i=0; i < sizeof(struct sys_pt_regs)/sizeof(PPC_REG); ++i) {
++      if (i != 34 /* FIXME: PT_ORIG_R3 */ && i <= PT_MQ) {
++          if (ptrace(PTRACE_POKEUSER, pid, i*4, regs_in->regs[i]) < 0) {
++              return -errno;
++          }
++      }
++    }
++    return 0;
++}
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/sys-ppc/sigcontext.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/sys-ppc/sigcontext.c     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/sys-ppc/sigcontext.c  2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,15 @@
++#include "asm/ptrace.h"
++#include "asm/sigcontext.h"
++#include "sysdep/ptrace.h"
++#include "user_util.h"
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/sys-ppc/sysrq.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/sys-ppc/sysrq.c  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/sys-ppc/sysrq.c       2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,43 @@
++/* 
++ * Copyright (C) 2001 Chris Emerson (cemerson@chiark.greenend.org.uk)
++ * Licensed under the GPL
++ */
++
++#include "linux/kernel.h"
++#include "linux/smp.h"
++#include "asm/ptrace.h"
++#include "sysrq.h"
++
++void show_regs(struct pt_regs_subarch *regs)
++{
++      printk("\n");
++      printk("show_regs(): insert regs here.\n");
++#if 0
++        printk("\n");
++        printk("EIP: %04x:[<%08lx>] CPU: %d",0xffff & regs->xcs, regs->eip,
++             smp_processor_id());
++        if (regs->xcs & 3)
++                printk(" ESP: %04x:%08lx",0xffff & regs->xss, regs->esp);
++        printk(" EFLAGS: %08lx\n", regs->eflags);
++        printk("EAX: %08lx EBX: %08lx ECX: %08lx EDX: %08lx\n",
++                regs->eax, regs->ebx, regs->ecx, regs->edx);
++        printk("ESI: %08lx EDI: %08lx EBP: %08lx",
++                regs->esi, regs->edi, regs->ebp);
++        printk(" DS: %04x ES: %04x\n",
++                0xffff & regs->xds, 0xffff & regs->xes);
++#endif
++
++        show_trace(&regs->gpr[1]);
++}
++
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/arch/um/util/Makefile
+===================================================================
+--- linux-2.4.29.orig/arch/um/util/Makefile    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/util/Makefile 2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,26 @@
++ALL = mk_task mk_constants
++
++all : $(ALL)
++
++mk_task : mk_task_user.o mk_task_kern.o
++      $(HOSTCC) -o mk_task mk_task_user.o mk_task_kern.o
++
++mk_task_user.o : mk_task_user.c
++      $(HOSTCC) -c $< 
++
++mk_task_kern.o : mk_task_kern.c
++      $(HOSTCC) $(CFLAGS) -c $< 
++
++mk_constants : mk_constants_user.o mk_constants_kern.o
++      $(HOSTCC) -o mk_constants mk_constants_user.o mk_constants_kern.o
++
++mk_constants_user.o : mk_constants_user.c
++      $(HOSTCC) -c $< 
++
++mk_constants_kern.o : mk_constants_kern.c
++      $(HOSTCC) $(CFLAGS) -c $< 
++
++clean :
++      $(RM) $(ALL) *.o *~
++
++archmrproper : clean
+Index: linux-2.4.29/arch/um/util/mk_constants_kern.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/util/mk_constants_kern.c 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/util/mk_constants_kern.c      2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,25 @@
++#include "linux/kernel.h"
++#include "linux/stringify.h"
++#include "asm/page.h"
++
++extern void print_head(void);
++extern void print_constant_str(char *name, char *value);
++extern void print_constant_int(char *name, int value);
++extern void print_tail(void);
++
++int main(int argc, char **argv)
++{
++  print_head();
++  print_constant_int("UM_KERN_PAGE_SIZE", PAGE_SIZE);
++
++  print_constant_str("UM_KERN_EMERG", KERN_EMERG);
++  print_constant_str("UM_KERN_ALERT", KERN_ALERT);
++  print_constant_str("UM_KERN_CRIT", KERN_CRIT);
++  print_constant_str("UM_KERN_ERR", KERN_ERR);
++  print_constant_str("UM_KERN_WARNING", KERN_WARNING);
++  print_constant_str("UM_KERN_NOTICE", KERN_NOTICE);
++  print_constant_str("UM_KERN_INFO", KERN_INFO);
++  print_constant_str("UM_KERN_DEBUG", KERN_DEBUG);
++  print_tail();
++  return(0);
++}
+Index: linux-2.4.29/arch/um/util/mk_constants_user.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/util/mk_constants_user.c 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/util/mk_constants_user.c      2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,28 @@
++#include <stdio.h>
++
++void print_head(void)
++{
++  printf("/*\n");
++  printf(" * Generated by mk_constants\n");
++  printf(" */\n");
++  printf("\n");
++  printf("#ifndef __UM_CONSTANTS_H\n");
++  printf("#define __UM_CONSTANTS_H\n");
++  printf("\n");
++}
++
++void print_constant_str(char *name, char *value)
++{
++  printf("#define %s \"%s\"\n", name, value);
++}
++
++void print_constant_int(char *name, int value)
++{
++  printf("#define %s %d\n", name, value);
++}
++
++void print_tail(void)
++{
++  printf("\n");
++  printf("#endif\n");
++}
+Index: linux-2.4.29/arch/um/util/mk_task_kern.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/util/mk_task_kern.c      1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/util/mk_task_kern.c   2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,17 @@
++#include "linux/sched.h"
++#include "linux/stddef.h"
++
++extern void print(char *name, char *type, int offset);
++extern void print_ptr(char *name, char *type, int offset);
++extern void print_head(void);
++extern void print_tail(void);
++
++int main(int argc, char **argv)
++{
++  print_head();
++  print_ptr("TASK_REGS", "union uml_pt_regs", 
++          offsetof(struct task_struct, thread.regs));
++  print("TASK_PID", "int", offsetof(struct task_struct, pid));
++  print_tail();
++  return(0);
++}
+Index: linux-2.4.29/arch/um/util/mk_task_user.c
+===================================================================
+--- linux-2.4.29.orig/arch/um/util/mk_task_user.c      1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/arch/um/util/mk_task_user.c   2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,30 @@
++#include <stdio.h>
++
++void print(char *name, char *type, int offset)
++{
++  printf("#define %s(task) *((%s *) &(((char *) (task))[%d]))\n", name, type,
++       offset);
++}
++
++void print_ptr(char *name, char *type, int offset)
++{
++  printf("#define %s(task) ((%s *) &(((char *) (task))[%d]))\n", name, type,
++       offset);
++}
++
++void print_head(void)
++{
++  printf("/*\n");
++  printf(" * Generated by mk_task\n");
++  printf(" */\n");
++  printf("\n");
++  printf("#ifndef __TASK_H\n");
++  printf("#define __TASK_H\n");
++  printf("\n");
++}
++
++void print_tail(void)
++{
++  printf("\n");
++  printf("#endif\n");
++}
+Index: linux-2.4.29/CREDITS
+===================================================================
+--- linux-2.4.29.orig/CREDITS  2005-05-03 21:08:24.000000000 +0300
++++ linux-2.4.29/CREDITS       2005-05-03 22:28:14.000000000 +0300
+@@ -434,6 +434,7 @@
+ E: lars@nocrew.org
+ W: http://lars.nocrew.org/
+ D: dsp56k device driver
++D: ptrace proxy in user mode kernel port
+ S: Kopmansg 2
+ S: 411 13  Goteborg
+ S: Sweden
+@@ -727,7 +728,7 @@
+ E: jdike@karaya.com
+ W: http://user-mode-linux.sourceforge.net
+ D: User mode kernel port
+-S: RR1 Box 67C
++S: 375 Tubbs Hill Rd
+ S: Deering NH 03244
+ S: USA
+Index: linux-2.4.29/Documentation/Configure.help
+===================================================================
+--- linux-2.4.29.orig/Documentation/Configure.help     2005-05-03 21:09:27.000000000 +0300
++++ linux-2.4.29/Documentation/Configure.help  2005-05-03 23:55:57.615290736 +0300
+@@ -16184,6 +16184,63 @@
+   The module will be called speedtch.o. If you want to compile it as
+   a module, say M here and read <file:Documentation/modules.txt>.
++Support for /proc/mm
++CONFIG_PROC_MM
++  Enables support for address space separation through /proc/mm.
++  A host kernel needs to have this enabled in order for UML to
++  run in skas mode.  UML kernels do not need to have this option
++  unless they will host sub-UMLs.
++
++  If you don't know what this does just say Y.
++
++Separate Kernel Address Space support
++CONFIG_MODE_SKAS
++  This option controls whether skas (separate kernel address space)
++  support is compiled in.  If you have applied the skas patch to the
++  host and enabled support for /proc/mm in the host kernel, then you
++  certainly want to say Y here (and consider saying N to
++  CONFIG_MODE_TT).  Otherwise, it is safe to say Y.  Disabling this
++  option will shrink the UML binary slightly.
++
++Tracing thread support
++CONFIG_MODE_TT
++  This option controls whether tracing thread support is compiled
++  into UML.  Normally, this should be set to Y.  If you intend to
++  use only skas mode (and the host has the skas patch applied to it),
++  then it is OK to say N here.
++
++Force a static link
++CONFIG_STATIC_LINK
++  If CONFIG_MODE_TT is disabled, then this option gives you the ability
++  to force a static link of UML.  Normally, if only skas mode is built
++  in to UML, it will be linked as a shared binary.  This is inconvenient
++  for use in a chroot jail.  So, if you intend to run UML inside a
++  chroot, and you disable CONFIG_MODE_TT, you probably want to say Y
++  here.
++
++2G/2G host address space split
++CONFIG_HOST_2G_2G
++  Most Linux machines are configured so that the kernel occupies the
++  upper 1G of the 4G address space and processes use the lower 3G.
++  However, some machine are configured with a 2G/2G split, with the
++  kernel occupying the upper 2G and processes using the lower 2G.
++
++  To allow UML to run on a such host you have to say Y here. N should be
++  a safe choice most of the time.
++
++Kernel stack size order
++CONFIG_KERNEL_STACK_ORDER
++  This option determines the size of UML kernel stacks.  They will
++  be 1 << order pages.  The default is OK unless you're running Valgrind
++  on UML, in which case, set this to 3.
++
++UML ubd block driver
++CONFIG_BLK_DEV_UBD
++  The User-Mode Linux port includes a driver called UBD which will let
++  you access arbitrary files on the host computer as block devices.
++  Unless you know that you do not need such virtual block devices say
++  Y here.
++
+ CONFIG_USB_GADGET
+   USB is a master/slave protocol, organized with one master
+   host (such as a PC) controlling up to 127 peripheral devices.
+@@ -16289,17 +16346,15 @@
+ Always do synchronous disk IO for UBD
+ CONFIG_BLK_DEV_UBD_SYNC
+-  The User-Mode Linux port includes a driver called UBD which will let
+-  you access arbitrary files on the host computer as block devices.
+-  Writes to such a block device are not immediately written to the
+-  host's disk; this may cause problems if, for example, the User-Mode
+-  Linux 'Virtual Machine' uses a journalling file system and the host
+-  computer crashes.
++  Writes to the virtual block device are not immediately written to the host's
++  disk; this may cause problems if, for example, the User-Mode Linux
++  'Virtual Machine' uses a journalling filesystem and the host computer
++  crashes.
+   Synchronous operation (i.e. always writing data to the host's disk
+   immediately) is configurable on a per-UBD basis by using a special
+   kernel command line option.  Alternatively, you can say Y here to
+-  turn on synchronous operation by default for all block.
++  turn on synchronous operation by default for all block devices.
+   If you're running a journalling file system (like reiserfs, for
+   example) in your virtual machine, you will want to say Y here.  If
+@@ -16311,6 +16366,7 @@
+ CONFIG_PT_PROXY
+   This option enables a debugging interface which allows gdb to debug
+   the kernel without needing to actually attach to kernel threads.
++  CONFIG_XTERM_CHAN must be enabled in order to enable CONFIG_PT_PROXY.
+   If you want to do kernel debugging, say Y here; otherwise say N.
+ Management console
+@@ -16357,6 +16413,9 @@
+   See <http://user-mode-linux.sourceforge.net/gprof.html> for more
+   details.
++  This option requires that CONFIG_MODE_TT be disabled, as UML will
++  not build with both enabled.
++
+   If you're involved in UML kernel development and want to use gprof,
+   say Y.  If you're unsure, say N.
+@@ -16380,6 +16439,19 @@
+   If you'd like to be able to work with files stored on the host, 
+   say Y or M here; otherwise say N.
++HoneyPot ProcFS
++CONFIG_HPPFS
++  hppfs (HoneyPot ProcFS) is a filesystem which allows UML /proc
++  entries to be overridden, removed, or fabricated from the host.
++  Its purpose is to allow a UML to appear to be a physical machine
++  by removing or changing anything in /proc which gives away the
++  identity of a UML.
++
++  See <http://user-mode-linux.sf.net/hppfs.html> for more information.
++
++  You only need this if you are setting up a UML honeypot.  Otherwise,
++  it is safe to say 'N' here.
++
+ Example IO Memory driver
+ CONFIG_MMAPPER
+   The User-Mode Linux port can provide support for IO Memory
+@@ -16395,6 +16467,21 @@
+   If you'd like to be able to provide a simulated IO port space for
+   User-Mode Linux processes, say Y.  If unsure, say N.
++Anonymous Memory support
++CONFIG_DEV_ANON
++  Don't ask. Just say Y.
++
++Support for software watchdog inside UML
++CONFIG_UML_WATCHDOG
++  Support for a virtual hardware watchdog. It's safe to say N here.
++
++COW block device
++CONFIG_COW
++  This is a layered driver which sits above two other block devices.
++  One is read-only, and the other is a read-write layer which stores
++  all changes.  This provides the illusion that the read-only layer
++  can be mounted read-write and changed.
++
+ Virtual Serial Line
+ CONFIG_SSL
+   The User-Mode Linux environment allows you to create virtual serial
+@@ -16505,26 +16592,197 @@
+ SLIP transport
+ CONFIG_UML_NET_SLIP
+-  The Slip User-Mode Linux network transport allows a running UML to
++  The slip User-Mode Linux network transport allows a running UML to
+   network with its host over a point-to-point link.  Unlike Ethertap,
+   which can carry any Ethernet frame (and hence even non-IP packets),
+-  the Slip transport can only carry IP packets.
++  the slip transport can only carry IP packets.
+-  To use this, your host must support Slip devices.
++  To use this, your host must support slip devices.
+   For more information, see
+   <http://user-mode-linux.sourceforge.net/networking.html>.  That site
+-  has examples of the UML command line to use to enable Slip
++  has examples of the UML command line to use to enable slip
+   networking, and details of a few quirks with it.
+-  The Ethertap Transport is preferred over Slip because of its
+-  limitation.  If you prefer Slip, however, say Y here.  Otherwise
++  The Ethertap Transport is preferred over slip because of its
++  limitations.  If you prefer slip, however, say Y here.  Otherwise
+   choose the Multicast transport (to network multiple UMLs on 
+   multiple hosts), Ethertap (to network with the host and the
+   outside world), and/or the Daemon transport (to network multiple
+   UMLs on a single host).  You may choose more than one without
+   conflict.  If you don't need UML networking, say N.
++SLiRP transport
++CONFIG_UML_NET_SLIRP
++  The SLiRP User-Mode Linux network transport allows a running UML
++  to network by invoking a program that can handle SLIP encapsulated
++  packets.  This is commonly (but not limited to) the application
++  known as SLiRP, a program that can re-socket IP packets back onto
++  the host on which it is run.  Only IP packets are supported,
++  unlike other network transports that can handle all Ethernet
++  frames.  In general, slirp allows the UML the same IP connectivity
++  to the outside world that the host user is permitted, and unlike
++  other transports, SLiRP works without the need of root level
++  privleges, setuid binaries, or SLIP devices on the host.  This
++  also means not every type of connection is possible, but most
++  situations can be accomodated with carefully crafted slirp
++  commands that can be passed along as part of the network device's
++  setup string.  The effect of this transport on the UML is similar
++  that of a host behind a firewall that masquerades all network
++  connections passing through it (but is less secure).
++
++  To use this you should first have slirp compiled somewhere
++  accessible on the host, and have read its documentation.  If you
++  don't need UML networking, say N.
++
++  Startup example: "eth0=slirp,FE:FD:01:02:03:04,/usr/local/bin/slirp"
++
++pcap transport
++CONFIG_UML_NET_PCAP
++ The pcap transport makes a pcap packet stream on the host look
++  like an ethernet device inside UML.  This is useful for making
++  UML act as a network monitor for the host.  You must have libcap
++  installed in order to build the pcap transport into UML.
++
++  For more information, see
++  <http://user-mode-linux.sourceforge.net/networking.html>  That site
++  has examples of the UML command line to use to enable this option.
++
++  If you intend to use UML as a network monitor for the host, say
++  Y here.  Otherwise, say N.
++
++Default main console channel initialization
++CONFIG_CON_ZERO_CHAN
++  This is the string describing the channel to which the main console
++  will be attached by default.  This value can be overridden from the
++  command line.  The default value is "fd:0,fd:1", which attaches the
++  main console to stdin and stdout.
++  It is safe to leave this unchanged.
++
++Default console channel initialization
++CONFIG_CON_CHAN
++  This is the string describing the channel to which all consoles
++  except the main console will be attached by default.  This value can
++  be overridden from the command line.  The default value is "xterm",
++  which brings them up in xterms.
++  It is safe to leave this unchanged, although you may wish to change
++  this if you expect the UML that you build to be run in environments
++  which don't have X or xterm available.
++
++Default serial line channel initialization
++CONFIG_SSL_CHAN
++  This is the string describing the channel to which the serial lines
++  will be attached by default.  This value can be overridden from the
++  command line.  The default value is "pty", which attaches them to
++  traditional pseudo-terminals.
++  It is safe to leave this unchanged, although you may wish to change
++  this if you expect the UML that you build to be run in environments
++  which don't have a set of /dev/pty* devices.
++
++Nesting level
++CONFIG_NEST_LEVEL
++  This is set to the number of layers of UMLs that this UML will be run
++  in.  Normally, this is zero, meaning that it will run directly on the
++  host.  Setting it to one will build a UML that can run inside a UML
++  that is running on the host.  Generally, if you intend this UML to run
++  inside another UML, set CONFIG_NEST_LEVEL to one more than the host UML.
++  Note that if the hosting UML has its CONFIG_KERNEL_HALF_GIGS set to 
++  greater than one, then the guest UML should have its CONFIG_NEST_LEVEL 
++  set to the host's CONFIG_NEST_LEVEL + CONFIG_KERNEL_HALF_GIGS.
++  Only change this if you are running nested UMLs.
++
++Kernel address space size (in .5G units)
++CONFIG_KERNEL_HALF_GIGS
++  This determines the amount of address space that UML will allocate for
++  its own, measured in half Gigabyte units.  The default is 1.
++  Change this only if you need to boot UML with an unusually large amount
++  of physical memory.
++
++UML sound support
++CONFIG_UML_SOUND
++  This option enables UML sound support.  If enabled, it will pull in
++  soundcore and the UML hostaudio relay, which acts as a intermediary
++  between the host's dsp and mixer devices and the UML sound system.
++  It is safe to say 'Y' here.
++
++UML SMP support
++CONFIG_UML_SMP
++  This option enables UML SMP support.  UML implements virtual SMP by
++  allowing as many processes to run simultaneously on the host as
++  there are virtual processors configured.  Obviously, if the host is
++  a uniprocessor, those processes will timeshare, but, inside UML,
++  will appear to be running simultaneously.  If the host is a
++  multiprocessor, then UML processes may run simultaneously, depending
++  on the host scheduler.
++  CONFIG_SMP will be set to whatever this option is set to.
++  It is safe to leave this unchanged.
++
++file descriptor channel support
++CONFIG_FD_CHAN
++  This option enables support for attaching UML consoles and serial
++  lines to already set up file descriptors.  Generally, the main
++  console is attached to file descriptors 0 and 1 (stdin and stdout),
++  so it would be wise to leave this enabled unless you intend to
++  attach it to some other host device.
++
++null device channel support
++CONFIG_NULL_CHAN
++  This option enables support for attaching UML consoles and serial
++  lines to a device similar to /dev/null.  Data written to it disappears
++  and there is never any data to be read.
++
++port channel support
++CONFIG_PORT_CHAN
++  This option enables support for attaching UML consoles and serial
++  lines to host portals.  They may be accessed with 'telnet <host>
++  <port number>'.  Any number of consoles and serial lines may be
++  attached to a single portal, although what UML device you get when
++  you telnet to that portal will be unpredictable.
++  It is safe to say 'Y' here.
++
++pty channel support
++CONFIG_PTY_CHAN
++  This option enables support for attaching UML consoles and serial
++  lines to host pseudo-terminals.  Access to both traditional
++  pseudo-terminals (/dev/pty*) and pts pseudo-terminals are controlled
++  with this option.  The assignment of UML devices to host devices
++  will be announced in the kernel message log.
++  It is safe to say 'Y' here.
++
++tty channel support
++CONFIG_TTY_CHAN
++  This option enables support for attaching UML consoles and serial
++  lines to host terminals.  Access to both virtual consoles
++  (/dev/tty*) and the slave side of pseudo-terminals (/dev/ttyp* and
++  /dev/pts/*) are controlled by this option.
++  It is safe to say 'Y' here.
++
++xterm channel support
++CONFIG_XTERM_CHAN
++  This option enables support for attaching UML consoles and serial
++  lines to xterms.  Each UML device so assigned will be brought up in
++  its own xterm.
++  If you disable this option, then CONFIG_PT_PROXY will be disabled as
++  well, since UML's gdb currently requires an xterm.
++  It is safe to say 'Y' here.
++
++tty logging
++CONFIG_TTY_LOG
++  This option enables logging of all data going through pseudo-terminals
++  to the host.  This is primarily useful for honeypots, where you want
++  secure keystroke logging that can't be detected or disabled by root.
++  Say 'N' unless you are setting up a UML honeypot or otherwise know that
++  you want this option.
++
++UML real-time clock support
++CONFIG_UML_REAL_TIME_CLOCK
++  This option ties the UML clock to the host clock, so that time passes at
++  the same rate as on the host, regardless of how much CPU time the UML is
++  getting.  This should normally be enabled.  The exception would be if you're
++  debugging UML.  In this case, time spent staring at the debugger with UML
++  stopped will cause lots of timer ticks to be backed up, and UML will spent
++  lots of time calling the timer when it is finally continued.
++
+ Microtek USB scanner support
+ CONFIG_USB_MICROTEK
+   Say Y here if you want support for the Microtek X6USB and
+Index: linux-2.4.29/drivers/char/Makefile
+===================================================================
+--- linux-2.4.29.orig/drivers/char/Makefile    2005-05-03 21:09:35.000000000 +0300
++++ linux-2.4.29/drivers/char/Makefile 2005-05-03 22:28:14.000000000 +0300
+@@ -114,6 +114,12 @@
+   endif
+ endif
++ifeq ($(ARCH),um)
++  KEYMAP   =
++  KEYBD    =
++  CONSOLE  =
++endif
++
+ ifeq ($(ARCH),sh)
+   KEYMAP   =
+   KEYBD    =
+Index: linux-2.4.29/drivers/char/mem.c
+===================================================================
+--- linux-2.4.29.orig/drivers/char/mem.c       2005-05-03 21:07:25.000000000 +0300
++++ linux-2.4.29/drivers/char/mem.c    2005-05-03 22:28:14.000000000 +0300
+@@ -220,7 +220,7 @@
+       ssize_t read = 0;
+       ssize_t virtr = 0;
+       char * kbuf; /* k-addr because vread() takes vmlist_lock rwlock */
+-              
++
+       if (p < (unsigned long) high_memory) {
+               read = count;
+               if (count > (unsigned long) high_memory - p)
+@@ -292,6 +292,8 @@
+                       wrote = (unsigned long) high_memory - p;
+               wrote = do_write_mem(file, (void*)p, p, buf, wrote, ppos);
++              if(wrote < 0)
++                      return(wrote);
+               p += wrote;
+               buf += wrote;
+@@ -664,6 +666,8 @@
+       write:          write_full,
+ };
++extern struct file_operations anon_file_operations;
++
+ static int memory_open(struct inode * inode, struct file * filp)
+ {
+       switch (MINOR(inode->i_rdev)) {
+@@ -693,6 +697,9 @@
+               case 9:
+                       filp->f_op = &urandom_fops;
+                       break;
++              case 10:
++                      filp->f_op = &anon_file_operations;
++                      break;
+               default:
+                       return -ENXIO;
+       }
+@@ -719,7 +726,8 @@
+       {5, "zero",    S_IRUGO | S_IWUGO,           &zero_fops},
+       {7, "full",    S_IRUGO | S_IWUGO,           &full_fops},
+       {8, "random",  S_IRUGO | S_IWUSR,           &random_fops},
+-      {9, "urandom", S_IRUGO | S_IWUSR,           &urandom_fops}
++      {9, "urandom", S_IRUGO | S_IWUSR,           &urandom_fops},
++      {10, "anon", S_IRUGO | S_IWUSR,             &anon_file_operations},
+     };
+     int i;
+Index: linux-2.4.29/drivers/char/n_tty.c
+===================================================================
+--- linux-2.4.29.orig/drivers/char/n_tty.c     2005-05-03 21:05:08.000000000 +0300
++++ linux-2.4.29/drivers/char/n_tty.c  2005-05-03 22:28:14.000000000 +0300
+@@ -25,9 +25,11 @@
+  *            who actually finally proved there really was a race.
+  *
+  * 2002/03/18   Implemented n_tty_wakeup to send SIGIO POLL_OUTs to
+- *            waiting writing processes-Sapan Bhatia <sapan@corewars.org>.
+- *            Also fixed a bug in BLOCKING mode where write_chan returns
+- *            EAGAIN
++ *            waiting writing processes-Sapan Bhatia <sapan@corewars.org>
++ *
++ * 2002/03/19   Fixed write_chan to stay put if console driver returns
++ *              EAGAIN and not return since it returns an EAGAIN in a 
++ *            non-blocking operation-Sapan Bhatia <sapan@corewars.org>
+  */
+ #include <linux/types.h>
+@@ -1393,9 +1395,9 @@
+               if (O_OPOST(tty) && !(test_bit(TTY_HW_COOK_OUT, &tty->flags))) {
+                       while (nr > 0) {
+                               ssize_t num = opost_block(tty, b, nr);
+-                              if (num < 0) {
+-                                      if (num == -EAGAIN)
+-                                              break;
++                              if (num < 0){
++                                      if(num == -EAGAIN)
++                                              break;
+                                       retval = num;
+                                       goto break_out;
+                               }
+Index: linux-2.4.29/drivers/char/tty_io.c
+===================================================================
+--- linux-2.4.29.orig/drivers/char/tty_io.c    2005-05-03 21:07:50.000000000 +0300
++++ linux-2.4.29/drivers/char/tty_io.c 2005-05-03 22:28:14.000000000 +0300
+@@ -967,6 +967,23 @@
+       tty_wakeup(tty);
+ }
++#ifdef CONFIG_TTY_LOG
++
++int (*open_log)(void *, void *) = NULL;
++int (*write_log)(int, const char *, int, void *, int) = NULL;
++void (*close_log)(int, void *) = NULL;
++
++void register_tty_logger(int (*opener)(void *, void *),
++                       int (*writer)(int, const char *, int, void *, int),
++                       void (*closer)(int, void *))
++{
++        open_log = opener;
++      write_log = writer;
++      close_log = closer;
++}
++
++#endif
++
+ static ssize_t tty_read(struct file * file, char * buf, size_t count, 
+                       loff_t *ppos)
+ {
+@@ -1012,8 +1029,13 @@
+               i = -EIO;
+       tty_ldisc_deref(ld);
+       unlock_kernel();
+-      if (i > 0)
++      if (i > 0){
+               inode->i_atime = CURRENT_TIME;
++#ifdef CONFIG_TTY_LOG
++              if((tty->log_fd >= 0) && (write_log != NULL))
++                      (*write_log)(tty->log_fd, buf, i, tty, 1);
++#endif
++      }
+       return i;
+ }
+@@ -1067,6 +1089,10 @@
+       if (written) {
+               file->f_dentry->d_inode->i_mtime = CURRENT_TIME;
+               ret = written;
++#ifdef CONFIG_TTY_LOG
++              if((tty->log_fd >= 0) && (write_log != NULL))
++                      (*write_log)(tty->log_fd, buf - ret, ret, tty, 0);
++#endif
+       }
+       up(&tty->atomic_write);
+       return ret;
+@@ -1662,6 +1688,11 @@
+               tty_set_termios_ldisc(o_tty,N_TTY);
+       }
++#ifdef CONFIG_TTY_LOG
++      if((tty->log_fd >= 0) && (close_log != NULL))
++              (*close_log)(tty->log_fd, tty);
++#endif
++
+       /* 
+        * The release_mem function takes care of the details of clearing
+        * the slots and preserving the termios structure.
+@@ -1820,6 +1851,11 @@
+                       nr_warns++;
+               }
+       }
++
++#ifdef CONFIG_TTY_LOG
++      if((tty->log_fd < 0) && (open_log != NULL))
++             tty->log_fd = (*open_log)(tty, current->tty);
++#endif
+       return 0;
+ }
+@@ -2467,6 +2503,9 @@
+       spin_lock_init(&tty->read_lock);
+       INIT_LIST_HEAD(&tty->tty_files);
+       INIT_TQUEUE(&tty->SAK_tq, 0, 0);
++#ifdef CONFIG_TTY_LOG
++      tty->log_fd = -1;
++#endif
+ }
+ /*
+Index: linux-2.4.29/drivers/net/setup.c
+===================================================================
+--- linux-2.4.29.orig/drivers/net/setup.c      2005-05-03 21:09:27.000000000 +0300
++++ linux-2.4.29/drivers/net/setup.c   2005-05-03 22:28:14.000000000 +0300
+@@ -28,7 +28,6 @@
+ extern int lmc_setup(void);
+ extern int madgemc_probe(void);
+-extern int uml_net_probe(void);
+ /* Pad device name to IFNAMSIZ=16. F.e. __PAD6 is string of 9 zeros. */
+ #define __PAD6 "\0\0\0\0\0\0\0\0\0"
+@@ -103,9 +102,6 @@
+ #ifdef CONFIG_MADGEMC
+       {madgemc_probe, 0},
+ #endif
+-#ifdef CONFIG_UML_NET
+-      {uml_net_probe, 0},
+-#endif
+  
+       {NULL, 0},
+ };
+Index: linux-2.4.29/fs/bad_inode.c
+===================================================================
+--- linux-2.4.29.orig/fs/bad_inode.c   2005-05-03 21:05:44.000000000 +0300
++++ linux-2.4.29/fs/bad_inode.c        2005-05-03 22:28:14.000000000 +0300
+@@ -83,6 +83,7 @@
+  
+ void make_bad_inode(struct inode * inode) 
+ {
++      inode->i_state = 0;
+       inode->i_mode = S_IFREG;
+       inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+       inode->i_op = &bad_inode_ops;   
+Index: linux-2.4.29/include/asm-i386/hardirq.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-i386/hardirq.h       2005-05-03 21:08:39.000000000 +0300
++++ linux-2.4.29/include/asm-i386/hardirq.h    2005-05-03 23:41:09.908242720 +0300
+@@ -4,6 +4,7 @@
+ #include <linux/config.h>
+ #include <linux/threads.h>
+ #include <linux/irq.h>
++#include <asm/processor.h>            /* for cpu_relax */
+ /* assembly code in softirq.h is sensitive to the offsets of these fields */
+ typedef struct {
+Index: linux-2.4.29/include/asm-um/a.out.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/a.out.h   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/a.out.h        2005-05-03 22:28:14.909343648 +0300
+@@ -0,0 +1,20 @@
++#ifndef __UM_A_OUT_H
++#define __UM_A_OUT_H
++
++#include "linux/config.h"
++#include "asm/arch/a.out.h"
++#include "choose-mode.h"
++
++#undef STACK_TOP
++
++extern unsigned long stacksizelim;
++
++extern unsigned long host_task_size;
++
++#define STACK_ROOM (stacksizelim)
++
++extern int honeypot;
++#define STACK_TOP \
++      CHOOSE_MODE((honeypot ? host_task_size : task_size), task_size)
++
++#endif
+Index: linux-2.4.29/include/asm-um/archparam-i386.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/archparam-i386.h  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/archparam-i386.h       2005-05-03 22:28:14.911343344 +0300
+@@ -0,0 +1,80 @@
++/* 
++ * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __UM_ARCHPARAM_I386_H
++#define __UM_ARCHPARAM_I386_H
++
++/********* Bits for asm-um/elf.h ************/
++
++#include "user.h"
++
++#define ELF_PLATFORM "i586"
++
++#define ELF_ET_DYN_BASE (2 * TASK_SIZE / 3)
++
++typedef struct user_i387_struct elf_fpregset_t;
++typedef unsigned long elf_greg_t;
++
++#define ELF_NGREG (sizeof (struct user_regs_struct) / sizeof(elf_greg_t))
++typedef elf_greg_t elf_gregset_t[ELF_NGREG];
++
++#define ELF_DATA        ELFDATA2LSB
++#define ELF_ARCH        EM_386
++
++#define ELF_PLAT_INIT(regs, load_addr) do { \
++      PT_REGS_EBX(regs) = 0; \
++      PT_REGS_ECX(regs) = 0; \
++      PT_REGS_EDX(regs) = 0; \
++      PT_REGS_ESI(regs) = 0; \
++      PT_REGS_EDI(regs) = 0; \
++      PT_REGS_EBP(regs) = 0; \
++      PT_REGS_EAX(regs) = 0; \
++} while(0)
++
++/* Shamelessly stolen from include/asm-i386/elf.h */
++
++#define ELF_CORE_COPY_REGS(pr_reg, regs) do { \
++      pr_reg[0] = PT_REGS_EBX(regs);          \
++      pr_reg[1] = PT_REGS_ECX(regs);          \
++      pr_reg[2] = PT_REGS_EDX(regs);          \
++      pr_reg[3] = PT_REGS_ESI(regs);          \
++      pr_reg[4] = PT_REGS_EDI(regs);          \
++      pr_reg[5] = PT_REGS_EBP(regs);          \
++      pr_reg[6] = PT_REGS_EAX(regs);          \
++      pr_reg[7] = PT_REGS_DS(regs);           \
++      pr_reg[8] = PT_REGS_ES(regs);           \
++      /* fake once used fs and gs selectors? */       \
++      pr_reg[9] = PT_REGS_DS(regs);           \
++      pr_reg[10] = PT_REGS_DS(regs);          \
++      pr_reg[11] = PT_REGS_SYSCALL_NR(regs);  \
++      pr_reg[12] = PT_REGS_IP(regs);          \
++      pr_reg[13] = PT_REGS_CS(regs);          \
++      pr_reg[14] = PT_REGS_EFLAGS(regs);      \
++      pr_reg[15] = PT_REGS_SP(regs);          \
++      pr_reg[16] = PT_REGS_SS(regs);          \
++} while(0);
++
++/********* Bits for asm-um/delay.h **********/
++
++typedef unsigned long um_udelay_t;
++
++/********* Nothing for asm-um/hardirq.h **********/
++
++/********* Nothing for asm-um/hw_irq.h **********/
++
++/********* Nothing for asm-um/string.h **********/
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/include/asm-um/archparam-ppc.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/archparam-ppc.h   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/archparam-ppc.h        2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,41 @@
++#ifndef __UM_ARCHPARAM_PPC_H
++#define __UM_ARCHPARAM_PPC_H
++
++/********* Bits for asm-um/elf.h ************/
++
++#define ELF_PLATFORM (0)
++
++#define ELF_ET_DYN_BASE (0x08000000)
++
++/* the following stolen from asm-ppc/elf.h */
++#define ELF_NGREG     48      /* includes nip, msr, lr, etc. */
++#define ELF_NFPREG    33      /* includes fpscr */
++/* General registers */
++typedef unsigned long elf_greg_t;
++typedef elf_greg_t elf_gregset_t[ELF_NGREG];
++
++/* Floating point registers */
++typedef double elf_fpreg_t;
++typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG];
++
++#define ELF_DATA        ELFDATA2MSB
++#define ELF_ARCH      EM_PPC
++
++/********* Bits for asm-um/delay.h **********/
++
++typedef unsigned int um_udelay_t;
++
++/********* Bits for asm-um/hw_irq.h **********/
++
++struct hw_interrupt_type;
++
++/********* Bits for asm-um/hardirq.h **********/
++
++#define irq_enter(cpu, irq) hardirq_enter(cpu)
++#define irq_exit(cpu, irq) hardirq_exit(cpu)
++
++/********* Bits for asm-um/string.h **********/
++
++#define __HAVE_ARCH_STRRCHR
++
++#endif
+Index: linux-2.4.29/include/asm-um/arch-signal-i386.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/arch-signal-i386.h        1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/arch-signal-i386.h     2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,24 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __UM_ARCH_SIGNAL_I386_H
++#define __UM_ARCH_SIGNAL_I386_H
++
++struct arch_signal_context {
++      unsigned long extrasigs[_NSIG_WORDS];
++};
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/include/asm-um/atomic.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/atomic.h  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/atomic.h       2005-05-03 23:41:08.099517688 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_ATOMIC_H
++#define __UM_ATOMIC_H
++
++#include "asm/arch/atomic.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/bitops.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/bitops.h  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/bitops.h       2005-05-03 23:41:08.093518600 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_BITOPS_H
++#define __UM_BITOPS_H
++
++#include "asm/arch/bitops.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/boot.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/boot.h    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/boot.h 2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_BOOT_H
++#define __UM_BOOT_H
++
++#include "asm/arch/boot.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/bugs.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/bugs.h    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/bugs.h 2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_BUGS_H
++#define __UM_BUGS_H
++
++void check_bugs(void);
++
++#endif
+Index: linux-2.4.29/include/asm-um/byteorder.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/byteorder.h       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/byteorder.h    2005-05-03 22:37:45.347623848 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_BYTEORDER_H
++#define __UM_BYTEORDER_H
++
++#include "asm/arch/byteorder.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/cache.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/cache.h   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/cache.h        2005-05-03 22:28:14.917342432 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_CACHE_H
++#define __UM_CACHE_H
++
++#define        L1_CACHE_BYTES  32
++
++#endif
+Index: linux-2.4.29/include/asm-um/checksum.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/checksum.h        1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/checksum.h     2005-05-03 22:28:14.917342432 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_CHECKSUM_H
++#define __UM_CHECKSUM_H
++
++#include "sysdep/checksum.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/cobalt.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/cobalt.h  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/cobalt.h       2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_COBALT_H
++#define __UM_COBALT_H
++
++#include "asm/arch/cobalt.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/current.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/current.h 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/current.h      2005-05-03 23:41:08.083520120 +0300
+@@ -0,0 +1,34 @@
++/* 
++ * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __UM_CURRENT_H
++#define __UM_CURRENT_H
++
++#ifndef __ASSEMBLY__
++
++#include "linux/config.h"
++#include "asm/page.h"
++
++struct task_struct;
++
++#define CURRENT_TASK(dummy) (((unsigned long) &dummy) & \
++                           (PAGE_MASK << CONFIG_KERNEL_STACK_ORDER))
++
++#define current ({ int dummy; (struct task_struct *) CURRENT_TASK(dummy); })
++
++#endif /* __ASSEMBLY__ */
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/include/asm-um/delay.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/delay.h   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/delay.h        2005-05-03 22:28:14.919342128 +0300
+@@ -0,0 +1,7 @@
++#ifndef __UM_DELAY_H
++#define __UM_DELAY_H
++
++#include "asm/arch/delay.h"
++#include "asm/archparam.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/desc.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/desc.h    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/desc.h 2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_DESC_H
++#define __UM_DESC_H
++
++#include "asm/arch/desc.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/div64.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/div64.h   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/div64.h        2005-05-03 22:28:14.941338784 +0300
+@@ -0,0 +1,6 @@
++#ifndef _UM_DIV64_H
++#define _UM_DIV64_H
++
++#include "asm/arch/div64.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/dma.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/dma.h     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/dma.h  2005-05-03 22:37:48.244183504 +0300
+@@ -0,0 +1,10 @@
++#ifndef __UM_DMA_H
++#define __UM_DMA_H
++
++#include "asm/io.h"
++
++extern unsigned long uml_physmem;
++
++#define MAX_DMA_ADDRESS (uml_physmem)
++
++#endif
+Index: linux-2.4.29/include/asm-um/elf.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/elf.h     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/elf.h  2005-05-03 22:28:14.943338480 +0300
+@@ -0,0 +1,18 @@
++#ifndef __UM_ELF_H
++#define __UM_ELF_H
++
++#include "asm/archparam.h"
++
++#define ELF_HWCAP (0)
++
++#define SET_PERSONALITY(ex, ibcs2) do ; while(0)
++
++#define ELF_EXEC_PAGESIZE 4096
++
++#define elf_check_arch(x) (1)
++
++#define ELF_CLASS ELFCLASS32
++
++#define USE_ELF_CORE_DUMP
++
++#endif
+Index: linux-2.4.29/include/asm-um/errno.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/errno.h   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/errno.h        2005-05-03 22:28:14.944338328 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_ERRNO_H
++#define __UM_ERRNO_H
++
++#include "asm/arch/errno.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/fcntl.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/fcntl.h   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/fcntl.h        2005-05-03 22:28:14.945338176 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_FCNTL_H
++#define __UM_FCNTL_H
++
++#include "asm/arch/fcntl.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/fixmap.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/fixmap.h  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/fixmap.h       2005-05-03 23:41:11.208045120 +0300
+@@ -0,0 +1,89 @@
++#ifndef __UM_FIXMAP_H
++#define __UM_FIXMAP_H
++
++#include <linux/config.h>
++#include <asm/kmap_types.h>
++
++/*
++ * Here we define all the compile-time 'special' virtual
++ * addresses. The point is to have a constant address at
++ * compile time, but to set the physical address only
++ * in the boot process. We allocate these special  addresses
++ * from the end of virtual memory (0xfffff000) backwards.
++ * Also this lets us do fail-safe vmalloc(), we
++ * can guarantee that these special addresses and
++ * vmalloc()-ed addresses never overlap.
++ *
++ * these 'compile-time allocated' memory buffers are
++ * fixed-size 4k pages. (or larger if used with an increment
++ * highger than 1) use fixmap_set(idx,phys) to associate
++ * physical memory with fixmap indices.
++ *
++ * TLB entries of such buffers will not be flushed across
++ * task switches.
++ */
++
++/*
++ * on UP currently we will have no trace of the fixmap mechanizm,
++ * no page table allocations, etc. This might change in the
++ * future, say framebuffers for the console driver(s) could be
++ * fix-mapped?
++ */
++enum fixed_addresses {
++#ifdef CONFIG_HIGHMEM
++      FIX_KMAP_BEGIN, /* reserved pte's for temporary kernel mappings */
++      FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1,
++#endif
++      __end_of_fixed_addresses
++};
++
++extern void __set_fixmap (enum fixed_addresses idx,
++                        unsigned long phys, pgprot_t flags);
++
++#define set_fixmap(idx, phys) \
++              __set_fixmap(idx, phys, PAGE_KERNEL)
++/*
++ * Some hardware wants to get fixmapped without caching.
++ */
++#define set_fixmap_nocache(idx, phys) \
++              __set_fixmap(idx, phys, PAGE_KERNEL_NOCACHE)
++/*
++ * used by vmalloc.c.
++ *
++ * Leave one empty page between vmalloc'ed areas and
++ * the start of the fixmap, and leave one page empty
++ * at the top of mem..
++ */
++extern unsigned long get_kmem_end(void);
++
++#define FIXADDR_TOP   (get_kmem_end() - 0x2000)
++#define FIXADDR_SIZE  (__end_of_fixed_addresses << PAGE_SHIFT)
++#define FIXADDR_START (FIXADDR_TOP - FIXADDR_SIZE)
++
++#define __fix_to_virt(x)      (FIXADDR_TOP - ((x) << PAGE_SHIFT))
++
++extern void __this_fixmap_does_not_exist(void);
++
++/*
++ * 'index to address' translation. If anyone tries to use the idx
++ * directly without tranlation, we catch the bug with a NULL-deference
++ * kernel oops. Illegal ranges of incoming indices are caught too.
++ */
++static inline unsigned long fix_to_virt(const unsigned int idx)
++{
++      /*
++       * this branch gets completely eliminated after inlining,
++       * except when someone tries to use fixaddr indices in an
++       * illegal way. (such as mixing up address types or using
++       * out-of-range indices).
++       *
++       * If it doesn't get removed, the linker will complain
++       * loudly with a reasonably clear error message..
++       */
++      if (idx >= __end_of_fixed_addresses)
++              __this_fixmap_does_not_exist();
++
++        return __fix_to_virt(idx);
++}
++
++#endif
+Index: linux-2.4.29/include/asm-um/floppy.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/floppy.h  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/floppy.h       2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_FLOPPY_H
++#define __UM_FLOPPY_H
++
++#include "asm/arch/floppy.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/hardirq.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/hardirq.h 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/hardirq.h      2005-05-03 23:41:09.909242568 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_HARDIRQ_H
++#define __UM_HARDIRQ_H
++
++#include "asm/arch/hardirq.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/hdreg.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/hdreg.h   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/hdreg.h        2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_HDREG_H
++#define __UM_HDREG_H
++
++#include "asm/arch/hdreg.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/highmem.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/highmem.h 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/highmem.h      2005-05-03 23:41:11.210044816 +0300
+@@ -0,0 +1,12 @@
++#ifndef __UM_HIGHMEM_H
++#define __UM_HIGHMEM_H
++
++#include "asm/page.h"
++#include "asm/fixmap.h"
++#include "asm/arch/highmem.h"
++
++#undef PKMAP_BASE
++
++#define PKMAP_BASE ((FIXADDR_START - LAST_PKMAP * PAGE_SIZE) & PMD_MASK)
++
++#endif
+Index: linux-2.4.29/include/asm-um/hw_irq.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/hw_irq.h  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/hw_irq.h       2005-05-03 22:37:48.105204632 +0300
+@@ -0,0 +1,10 @@
++#ifndef _ASM_UM_HW_IRQ_H
++#define _ASM_UM_HW_IRQ_H
++
++#include "asm/irq.h"
++#include "asm/archparam.h"
++
++static inline void hw_resend_irq(struct hw_interrupt_type *h, unsigned int i)
++{}
++
++#endif
+Index: linux-2.4.29/include/asm-um/ide.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/ide.h     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/ide.h  2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_IDE_H
++#define __UM_IDE_H
++
++#include "asm/arch/ide.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/init.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/init.h    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/init.h 2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,11 @@
++#ifndef _UM_INIT_H
++#define _UM_INIT_H
++
++#ifdef notdef
++#define __init
++#define __initdata
++#define __initfunc(__arginit) __arginit
++#define __cacheline_aligned 
++#endif
++
++#endif
+Index: linux-2.4.29/include/asm-um/ioctl.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/ioctl.h   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/ioctl.h        2005-05-03 22:28:14.952337112 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_IOCTL_H
++#define __UM_IOCTL_H
++
++#include "asm/arch/ioctl.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/ioctls.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/ioctls.h  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/ioctls.h       2005-05-03 22:37:45.509599224 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_IOCTLS_H
++#define __UM_IOCTLS_H
++
++#include "asm/arch/ioctls.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/io.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/io.h      1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/io.h   2005-05-03 22:37:48.176193840 +0300
+@@ -0,0 +1,25 @@
++#ifndef __UM_IO_H
++#define __UM_IO_H
++
++#include "asm/page.h"
++
++#define IO_SPACE_LIMIT 0xdeadbeef /* Sure hope nothing uses this */
++
++static inline int inb(unsigned long i) { return(0); }
++static inline void outb(char c, unsigned long i) { }
++
++/*
++ * Change virtual addresses to physical addresses and vv.
++ * These are pretty trivial
++ */
++static inline unsigned long virt_to_phys(volatile void * address)
++{
++      return __pa((void *) address);
++}
++
++static inline void * phys_to_virt(unsigned long address)
++{
++      return __va(address);
++}
++
++#endif
+Index: linux-2.4.29/include/asm-um/ipcbuf.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/ipcbuf.h  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/ipcbuf.h       2005-05-03 22:28:14.954336808 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_IPCBUF_H
++#define __UM_IPCBUF_H
++
++#include "asm/arch/ipcbuf.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/ipc.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/ipc.h     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/ipc.h  2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_IPC_H
++#define __UM_IPC_H
++
++#include "asm/arch/ipc.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/irq.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/irq.h     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/irq.h  2005-05-03 22:28:14.956336504 +0300
+@@ -0,0 +1,23 @@
++#ifndef __UM_IRQ_H
++#define __UM_IRQ_H
++
++#define TIMER_IRQ             0
++#define UMN_IRQ                       1
++#define CONSOLE_IRQ           2
++#define CONSOLE_WRITE_IRQ     3
++#define UBD_IRQ                       4
++#define UM_ETH_IRQ            5
++#define SSL_IRQ                       6
++#define SSL_WRITE_IRQ         7
++#define ACCEPT_IRQ            8
++#define MCONSOLE_IRQ          9
++#define WINCH_IRQ             10
++#define SIGIO_WRITE_IRQ       11
++#define TELNETD_IRQ           12
++#define XTERM_IRQ             13
++#define HUMFS_IRQ             14
++
++#define LAST_IRQ HUMFS_IRQ
++#define NR_IRQS (LAST_IRQ + 1)
++
++#endif
+Index: linux-2.4.29/include/asm-um/keyboard.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/keyboard.h        1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/keyboard.h     2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_KEYBOARD_H
++#define __UM_KEYBOARD_H
++
++#include "asm/arch/keyboard.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/kmap_types.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/kmap_types.h      1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/kmap_types.h   2005-05-03 22:28:14.957336352 +0300
+@@ -0,0 +1,11 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __UM_KMAP_TYPES_H
++#define __UM_KMAP_TYPES_H
++
++#include "asm/arch/kmap_types.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/linux_logo.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/linux_logo.h      1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/linux_logo.h   2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_LINUX_LOGO_H
++#define __UM_LINUX_LOGO_H
++
++#include "asm/arch/linux_logo.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/locks.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/locks.h   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/locks.h        2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_LOCKS_H
++#define __UM_LOCKS_H
++
++#include "asm/arch/locks.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/mca_dma.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/mca_dma.h 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/mca_dma.h      2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,6 @@
++#ifndef mca___UM_DMA_H
++#define mca___UM_DMA_H
++
++#include "asm/arch/mca_dma.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/mman.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/mman.h    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/mman.h 2005-05-03 22:28:14.961335744 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_MMAN_H
++#define __UM_MMAN_H
++
++#include "asm/arch/mman.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/mmu_context.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/mmu_context.h     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/mmu_context.h  2005-05-03 23:41:09.000000000 +0300
+@@ -0,0 +1,72 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __UM_MMU_CONTEXT_H
++#define __UM_MMU_CONTEXT_H
++
++#include "linux/sched.h"
++#include "choose-mode.h"
++
++#define get_mmu_context(task) do ; while(0)
++#define activate_context(tsk) do ; while(0)
++
++static inline void activate_mm(struct mm_struct *old, struct mm_struct *new)
++{
++}
++
++extern void switch_mm_skas(int mm_fd);
++
++static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, 
++                           struct task_struct *tsk, unsigned cpu)
++{
++      if(prev != next){
++              clear_bit(cpu, &prev->cpu_vm_mask);
++              set_bit(cpu, &next->cpu_vm_mask);
++              if(next != &init_mm)
++                      CHOOSE_MODE((void) 0, 
++                                  switch_mm_skas(next->context.skas.mm_fd));
++      }
++}
++
++static inline void enter_lazy_tlb(struct mm_struct *mm, 
++                                struct task_struct *tsk, unsigned cpu)
++{
++}
++
++extern int init_new_context_skas(struct task_struct *task, 
++                               struct mm_struct *mm);
++
++static inline int init_new_context_tt(struct task_struct *task, 
++                                    struct mm_struct *mm)
++{
++      return(0);
++}
++
++static inline int init_new_context(struct task_struct *task, 
++                                 struct mm_struct *mm)
++{
++      return(CHOOSE_MODE_PROC(init_new_context_tt, init_new_context_skas, 
++                              task, mm));
++}
++
++extern void destroy_context_skas(struct mm_struct *mm);
++
++static inline void destroy_context(struct mm_struct *mm)
++{
++      CHOOSE_MODE((void) 0, destroy_context_skas(mm));
++}
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/include/asm-um/mmu.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/mmu.h     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/mmu.h  2005-05-03 22:28:14.962335592 +0300
+@@ -0,0 +1,22 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __MMU_H
++#define __MMU_H
++
++#include "um_mmu.h"
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/include/asm-um/module.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/module.h  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/module.h       2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_MODULE_H
++#define __UM_MODULE_H
++
++#include "asm/arch/module.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/msgbuf.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/msgbuf.h  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/msgbuf.h       2005-05-03 22:28:14.964335288 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_MSGBUF_H
++#define __UM_MSGBUF_H
++
++#include "asm/arch/msgbuf.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/mtrr.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/mtrr.h    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/mtrr.h 2005-05-03 22:37:48.000000000 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_MTRR_H
++#define __UM_MTRR_H
++
++#include "asm/arch/mtrr.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/namei.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/namei.h   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/namei.h        2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_NAMEI_H
++#define __UM_NAMEI_H
++
++#include "asm/arch/namei.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/page.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/page.h    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/page.h 2005-05-03 22:37:45.335625672 +0300
+@@ -0,0 +1,68 @@
++/* 
++ * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __UM_PAGE_H
++#define __UM_PAGE_H
++
++struct page;
++
++#include "asm/arch/page.h"
++
++#undef BUG
++#undef PAGE_BUG
++#undef __pa
++#undef __va
++#undef virt_to_page
++#undef VALID_PAGE
++#undef PAGE_OFFSET
++#undef KERNELBASE
++
++extern unsigned long uml_physmem;
++
++#define PAGE_OFFSET (uml_physmem)
++#define KERNELBASE PAGE_OFFSET
++
++#ifndef __ASSEMBLY__
++
++extern void stop(void);
++
++#define BUG() do { \
++      panic("kernel BUG at %s:%d!\n", __FILE__, __LINE__); \
++} while (0)
++
++#define PAGE_BUG(page) do { \
++      BUG(); \
++} while (0)
++
++#endif /* __ASSEMBLY__ */
++
++#define __va_space (8*1024*1024)
++
++extern unsigned long to_phys(void *virt);
++extern void *to_virt(unsigned long phys);
++
++#define __pa(virt) to_phys((void *) virt)
++#define __va(phys) to_virt((unsigned long) phys)
++
++#define VALID_PAGE(page) ((page - mem_map) < max_mapnr)
++
++extern struct page *arch_validate(struct page *page, int mask, int order);
++#define HAVE_ARCH_VALIDATE
++
++extern void arch_free_page(struct page *page, int order);
++#define HAVE_ARCH_FREE_PAGE
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/include/asm-um/page_offset.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/page_offset.h     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/page_offset.h  2005-05-03 22:28:14.967334832 +0300
+@@ -0,0 +1 @@
++#define PAGE_OFFSET_RAW (uml_physmem)
+Index: linux-2.4.29/include/asm-um/param.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/param.h   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/param.h        2005-05-03 22:28:14.968334680 +0300
+@@ -0,0 +1,22 @@
++#ifndef _UM_PARAM_H
++#define _UM_PARAM_H
++
++#define HZ 100
++
++#define EXEC_PAGESIZE   4096
++
++#ifndef NGROUPS
++#define NGROUPS         32
++#endif
++
++#ifndef NOGROUP
++#define NOGROUP         (-1)
++#endif
++
++#define MAXHOSTNAMELEN  64      /* max length of hostname */
++
++#ifdef __KERNEL__
++# define CLOCKS_PER_SEC 100    /* frequency at which times() counts */
++#endif
++
++#endif
+Index: linux-2.4.29/include/asm-um/pci.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/pci.h     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/pci.h  2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_PCI_H
++#define __UM_PCI_H
++
++#define PCI_DMA_BUS_IS_PHYS     (1)
++
++#endif
+Index: linux-2.4.29/include/asm-um/pgalloc.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/pgalloc.h 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/pgalloc.h      2005-05-03 23:41:11.209044968 +0300
+@@ -0,0 +1,164 @@
++/* 
++ * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Derived from include/asm-i386/pgalloc.h and include/asm-i386/pgtable.h
++ * Licensed under the GPL
++ */
++
++#ifndef __UM_PGALLOC_H
++#define __UM_PGALLOC_H
++
++#include "linux/config.h"
++#include "linux/mm.h"
++#include "asm/fixmap.h"
++#include "choose-mode.h"
++
++#define pgd_quicklist (current_cpu_data.pgd_quick)
++#define pmd_quicklist (current_cpu_data.pmd_quick)
++#define pte_quicklist (current_cpu_data.pte_quick)
++#define pgtable_cache_size (current_cpu_data.pgtable_cache_sz)
++
++#define pmd_populate(mm, pmd, pte) set_pmd(pmd, __pmd(_PAGE_TABLE + __pa(pte)))
++
++/*
++ * Allocate and free page tables.
++ */
++
++static inline pgd_t *get_pgd_slow_tt(void)
++{
++      pgd_t *pgd = (pgd_t *)__get_free_page(GFP_KERNEL);
++
++      if (pgd) {
++              memset(pgd, 0, USER_PTRS_PER_PGD * sizeof(pgd_t));
++              memcpy(pgd + USER_PTRS_PER_PGD, 
++                     swapper_pg_dir + USER_PTRS_PER_PGD, 
++                     (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t));
++      }
++      return pgd;
++}
++
++static inline pgd_t *get_pgd_slow_skas(void)
++{
++      pgd_t *pgd = (pgd_t *)__get_free_page(GFP_KERNEL);
++
++      if (pgd)
++              memset(pgd, 0, USER_PTRS_PER_PGD * sizeof(pgd_t));
++      return pgd;
++}
++
++static inline pgd_t *get_pgd_slow(void)
++{
++      return(CHOOSE_MODE(get_pgd_slow_tt(), get_pgd_slow_skas()));
++}
++
++static inline pgd_t *get_pgd_fast(void)
++{
++      unsigned long *ret;
++
++      ret = pgd_quicklist;
++      if (ret != NULL) {
++              pgd_quicklist = (unsigned long *)(*ret);
++              ret[0] = 0;
++              pgtable_cache_size--;
++      } else
++              ret = (unsigned long *)get_pgd_slow();
++      return (pgd_t *)ret;
++}
++
++static inline void free_pgd_fast(pgd_t *pgd)
++{
++      *(unsigned long *)pgd = (unsigned long) pgd_quicklist;
++      pgd_quicklist = (unsigned long *) pgd;
++      pgtable_cache_size++;
++}
++
++static inline void free_pgd_slow(pgd_t *pgd)
++{
++      free_page((unsigned long)pgd);
++}
++
++static inline pte_t *pte_alloc_one(struct mm_struct *mm, unsigned long address)
++{
++      pte_t *pte;
++
++      pte = (pte_t *) __get_free_page(GFP_KERNEL);
++      if (pte)
++              clear_page(pte);
++      return pte;
++}
++
++static inline pte_t *pte_alloc_one_fast(struct mm_struct *mm, unsigned long address)
++{
++      unsigned long *ret;
++
++      ret = (unsigned long *)pte_quicklist;
++      if (ret != NULL) {
++              pte_quicklist = (unsigned long *)(*ret);
++              ret[0] = ret[1];
++              pgtable_cache_size--;
++      }
++      return (pte_t *)ret;
++}
++
++static inline void pte_free_fast(pte_t *pte)
++{
++      *(unsigned long *)pte = (unsigned long) pte_quicklist;
++      pte_quicklist = (unsigned long *) pte;
++      pgtable_cache_size++;
++}
++
++static inline void pte_free_slow(pte_t *pte)
++{
++      free_page((unsigned long)pte);
++}
++
++#define pte_free(pte)           pte_free_fast(pte)
++#define pgd_free(pgd)           free_pgd_slow(pgd)
++#define pgd_alloc(mm)           get_pgd_fast()
++
++/*
++ * allocating and freeing a pmd is trivial: the 1-entry pmd is
++ * inside the pgd, so has no extra memory associated with it.
++ */
++
++#define pmd_alloc_one_fast(mm, addr)  ({ BUG(); ((pmd_t *)1); })
++#define pmd_alloc_one(mm, addr)               ({ BUG(); ((pmd_t *)2); })
++#define pmd_free_slow(x)              do { } while (0)
++#define pmd_free_fast(x)              do { } while (0)
++#define pmd_free(x)                   do { } while (0)
++#define pgd_populate(mm, pmd, pte)    BUG()
++
++/*
++ * TLB flushing:
++ *
++ *  - flush_tlb() flushes the current mm struct TLBs
++ *  - flush_tlb_all() flushes all processes TLBs
++ *  - flush_tlb_mm(mm) flushes the specified mm context TLB's
++ *  - flush_tlb_page(vma, vmaddr) flushes one page
++ *  - flush_tlb_kernel_vm() flushes the kernel vm area
++ *  - flush_tlb_range(mm, start, end) flushes a range of pages
++ *  - flush_tlb_pgtables(mm, start, end) flushes a range of page tables
++ */
++
++extern void flush_tlb_all(void);
++extern void flush_tlb_mm(struct mm_struct *mm);
++extern void flush_tlb_range(struct mm_struct *mm, unsigned long start, 
++                          unsigned long end);
++extern void flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr);
++extern void flush_tlb_kernel_vm(void);
++
++static inline void flush_tlb_pgtables(struct mm_struct *mm,
++                                    unsigned long start, unsigned long end)
++{
++}
++
++#endif
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/include/asm-um/pgtable.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/pgtable.h 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/pgtable.h      2005-05-03 23:41:09.906243024 +0300
+@@ -0,0 +1,413 @@
++/* 
++ * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Derived from include/asm-i386/pgtable.h
++ * Licensed under the GPL
++ */
++
++#ifndef __UM_PGTABLE_H
++#define __UM_PGTABLE_H
++
++#include "linux/sched.h"
++#include "asm/processor.h"
++#include "asm/page.h"
++
++extern pgd_t swapper_pg_dir[1024];
++
++#define flush_cache_all() do ; while (0)
++#define flush_cache_mm(mm) do ; while (0)
++#define flush_cache_range(vma, start, end) do ; while (0)
++#define flush_cache_page(vma, vmaddr) do ; while (0)
++#define flush_page_to_ram(page) do ; while (0)
++#define flush_dcache_page(page)       do ; while (0)
++#define flush_icache_range(from, to) do ; while (0)
++#define flush_icache_page(vma,pg) do ; while (0)
++#define flush_icache_user_range(vma,pg,adr,len)       do ; while (0)
++
++extern void __flush_tlb_one(unsigned long addr);
++
++extern void pte_free(pte_t *pte);
++
++extern void pgd_free(pgd_t *pgd);
++
++extern int do_check_pgt_cache(int, int);
++
++extern void *um_virt_to_phys(struct task_struct *task, unsigned long virt,
++                           pte_t *pte_out);
++
++/* zero page used for uninitialized stuff */
++extern unsigned long *empty_zero_page;
++
++#define pgtable_cache_init() do ; while (0)
++
++/* PMD_SHIFT determines the size of the area a second-level page table can map */
++#define PMD_SHIFT     22
++#define PMD_SIZE      (1UL << PMD_SHIFT)
++#define PMD_MASK      (~(PMD_SIZE-1))
++
++/* PGDIR_SHIFT determines what a third-level page table entry can map */
++#define PGDIR_SHIFT   22
++#define PGDIR_SIZE    (1UL << PGDIR_SHIFT)
++#define PGDIR_MASK    (~(PGDIR_SIZE-1))
++
++/*
++ * entries per page directory level: the i386 is two-level, so
++ * we don't really have any PMD directory physically.
++ */
++#define PTRS_PER_PTE  1024
++#define PTRS_PER_PMD  1
++#define PTRS_PER_PGD  1024
++#define USER_PTRS_PER_PGD     (TASK_SIZE/PGDIR_SIZE)
++#define FIRST_USER_PGD_NR       0
++
++#define pte_ERROR(e) \
++        printk("%s:%d: bad pte %08lx.\n", __FILE__, __LINE__, pte_val(e))
++#define pmd_ERROR(e) \
++        printk("%s:%d: bad pmd %08lx.\n", __FILE__, __LINE__, pmd_val(e))
++#define pgd_ERROR(e) \
++        printk("%s:%d: bad pgd %08lx.\n", __FILE__, __LINE__, pgd_val(e))
++
++/*
++ * pgd entries used up by user/kernel:
++ */
++
++#define USER_PGD_PTRS (TASK_SIZE >> PGDIR_SHIFT)
++#define KERNEL_PGD_PTRS (PTRS_PER_PGD-USER_PGD_PTRS)
++
++#ifndef __ASSEMBLY__
++/* Just any arbitrary offset to the start of the vmalloc VM area: the
++ * current 8MB value just means that there will be a 8MB "hole" after the
++ * physical memory until the kernel virtual memory starts.  That means that
++ * any out-of-bounds memory accesses will hopefully be caught.
++ * The vmalloc() routines leaves a hole of 4kB between each vmalloced
++ * area for the same reason. ;)
++ */
++
++extern unsigned long end_iomem;
++
++#define VMALLOC_OFFSET        (__va_space)
++#define VMALLOC_START ((end_iomem + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1))
++#define VMALLOC_VMADDR(x) ((unsigned long)(x))
++
++#if CONFIG_HIGHMEM
++# define VMALLOC_END  (PKMAP_BASE-2*PAGE_SIZE)
++#else
++# define VMALLOC_END  (FIXADDR_START-2*PAGE_SIZE)
++#endif
++
++#define _PAGE_PRESENT 0x001
++#define _PAGE_NEWPAGE 0x002
++#define _PAGE_PROTNONE        0x004   /* If not present */
++#define _PAGE_RW      0x008
++#define _PAGE_USER    0x010
++#define _PAGE_ACCESSED        0x020
++#define _PAGE_DIRTY   0x040
++#define _PAGE_NEWPROT   0x080
++
++#define REGION_MASK   0xf0000000
++#define REGION_SHIFT  28
++
++#define _PAGE_TABLE   (_PAGE_PRESENT | _PAGE_RW | _PAGE_USER | _PAGE_ACCESSED | _PAGE_DIRTY)
++#define _KERNPG_TABLE (_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY)
++#define _PAGE_CHG_MASK        (PAGE_MASK | _PAGE_ACCESSED | _PAGE_DIRTY)
++
++#define PAGE_NONE     __pgprot(_PAGE_PROTNONE | _PAGE_ACCESSED)
++#define PAGE_SHARED   __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_USER | _PAGE_ACCESSED)
++#define PAGE_COPY     __pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_ACCESSED)
++#define PAGE_READONLY __pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_ACCESSED)
++#define PAGE_KERNEL   __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | _PAGE_ACCESSED)
++#define PAGE_KERNEL_RO        __pgprot(_PAGE_PRESENT | _PAGE_DIRTY | _PAGE_ACCESSED)
++
++/*
++ * The i386 can't do page protection for execute, and considers that the same are read.
++ * Also, write permissions imply read permissions. This is the closest we can get..
++ */
++#define __P000        PAGE_NONE
++#define __P001        PAGE_READONLY
++#define __P010        PAGE_COPY
++#define __P011        PAGE_COPY
++#define __P100        PAGE_READONLY
++#define __P101        PAGE_READONLY
++#define __P110        PAGE_COPY
++#define __P111        PAGE_COPY
++
++#define __S000        PAGE_NONE
++#define __S001        PAGE_READONLY
++#define __S010        PAGE_SHARED
++#define __S011        PAGE_SHARED
++#define __S100        PAGE_READONLY
++#define __S101        PAGE_READONLY
++#define __S110        PAGE_SHARED
++#define __S111        PAGE_SHARED
++
++/*
++ * Define this if things work differently on an i386 and an i486:
++ * it will (on an i486) warn about kernel memory accesses that are
++ * done without a 'verify_area(VERIFY_WRITE,..)'
++ */
++#undef TEST_VERIFY_AREA
++
++/* page table for 0-4MB for everybody */
++extern unsigned long pg0[1024];
++
++/*
++ * BAD_PAGETABLE is used when we need a bogus page-table, while
++ * BAD_PAGE is used for a bogus page.
++ *
++ * ZERO_PAGE is a global shared page that is always zero: used
++ * for zero-mapped memory areas etc..
++ */
++extern pte_t __bad_page(void);
++extern pte_t * __bad_pagetable(void);
++
++#define BAD_PAGETABLE __bad_pagetable()
++#define BAD_PAGE __bad_page()
++
++#define ZERO_PAGE(vaddr) virt_to_page(empty_zero_page)
++
++/* number of bits that fit into a memory pointer */
++#define BITS_PER_PTR                  (8*sizeof(unsigned long))
++
++/* to align the pointer to a pointer address */
++#define PTR_MASK                      (~(sizeof(void*)-1))
++
++/* sizeof(void*)==1<<SIZEOF_PTR_LOG2 */
++/* 64-bit machines, beware!  SRB. */
++#define SIZEOF_PTR_LOG2                       2
++
++/* to find an entry in a page-table */
++#define PAGE_PTR(address) \
++((unsigned long)(address)>>(PAGE_SHIFT-SIZEOF_PTR_LOG2)&PTR_MASK&~PAGE_MASK)
++
++#define pte_none(x)   !(pte_val(x) & ~_PAGE_NEWPAGE)
++#define pte_present(x)        (pte_val(x) & (_PAGE_PRESENT | _PAGE_PROTNONE))
++
++#define pte_clear(xp) do { pte_val(*(xp)) = _PAGE_NEWPAGE; } while (0)
++
++#define pmd_none(x)   (!(pmd_val(x) & ~_PAGE_NEWPAGE))
++#define       pmd_bad(x)      ((pmd_val(x) & (~PAGE_MASK & ~_PAGE_USER)) != _KERNPG_TABLE)
++#define pmd_present(x)        (pmd_val(x) & _PAGE_PRESENT)
++#define pmd_clear(xp) do { pmd_val(*(xp)) = _PAGE_NEWPAGE; } while (0)
++
++#define pmd_newpage(x)  (pmd_val(x) & _PAGE_NEWPAGE)
++#define pmd_mkuptodate(x) (pmd_val(x) &= ~_PAGE_NEWPAGE)
++
++/*
++ * The "pgd_xxx()" functions here are trivial for a folded two-level
++ * setup: the pgd is never bad, and a pmd always exists (as it's folded
++ * into the pgd entry)
++ */
++static inline int pgd_none(pgd_t pgd)         { return 0; }
++static inline int pgd_bad(pgd_t pgd)          { return 0; }
++static inline int pgd_present(pgd_t pgd)      { return 1; }
++static inline void pgd_clear(pgd_t * pgdp)    { }
++
++#define pages_to_mb(x) ((x) >> (20-PAGE_SHIFT))
++
++#define pte_page(pte) virt_to_page(__va(pte_val(pte)))
++#define pmd_page(pmd) ((unsigned long) __va(pmd_val(pmd) & PAGE_MASK))
++
++extern struct page *phys_to_page(const unsigned long phys);
++extern struct page *__virt_to_page(const unsigned long virt);
++#define virt_to_page(addr) __virt_to_page((const unsigned long) addr)
++
++static inline pte_t pte_mknewprot(pte_t pte)
++{
++      pte_val(pte) |= _PAGE_NEWPROT;
++      return(pte);
++}
++
++static inline pte_t pte_mknewpage(pte_t pte)
++{
++      pte_val(pte) |= _PAGE_NEWPAGE;
++      return(pte);
++}
++
++static inline void set_pte(pte_t *pteptr, pte_t pteval)
++{
++      /* If it's a swap entry, it needs to be marked _PAGE_NEWPAGE so
++       * fix_range knows to unmap it.  _PAGE_NEWPROT is specific to
++       * mapped pages.
++       */
++      *pteptr = pte_mknewpage(pteval);
++      if(pte_present(*pteptr)) *pteptr = pte_mknewprot(*pteptr);
++}
++
++/*
++ * (pmds are folded into pgds so this doesnt get actually called,
++ * but the define is needed for a generic inline function.)
++ */
++#define set_pmd(pmdptr, pmdval) (*(pmdptr) = pmdval)
++#define set_pgd(pgdptr, pgdval) (*(pgdptr) = pgdval)
++
++/*
++ * The following only work if pte_present() is true.
++ * Undefined behaviour if not..
++ */
++static inline int pte_read(pte_t pte)
++{ 
++      return((pte_val(pte) & _PAGE_USER) && 
++             !(pte_val(pte) & _PAGE_PROTNONE));
++}
++
++static inline int pte_exec(pte_t pte){
++      return((pte_val(pte) & _PAGE_USER) &&
++             !(pte_val(pte) & _PAGE_PROTNONE));
++}
++
++static inline int pte_write(pte_t pte)
++{
++      return((pte_val(pte) & _PAGE_RW) &&
++             !(pte_val(pte) & _PAGE_PROTNONE));
++}
++
++static inline int pte_dirty(pte_t pte)        { return pte_val(pte) & _PAGE_DIRTY; }
++static inline int pte_young(pte_t pte)        { return pte_val(pte) & _PAGE_ACCESSED; }
++static inline int pte_newpage(pte_t pte) { return pte_val(pte) & _PAGE_NEWPAGE; }
++static inline int pte_newprot(pte_t pte)
++{ 
++      return(pte_present(pte) && (pte_val(pte) & _PAGE_NEWPROT)); 
++}
++
++static inline pte_t pte_rdprotect(pte_t pte)
++{ 
++      pte_val(pte) &= ~_PAGE_USER; 
++      return(pte_mknewprot(pte));
++}
++
++static inline pte_t pte_exprotect(pte_t pte)
++{ 
++      pte_val(pte) &= ~_PAGE_USER;
++      return(pte_mknewprot(pte));
++}
++
++static inline pte_t pte_mkclean(pte_t pte)
++{
++      pte_val(pte) &= ~_PAGE_DIRTY; 
++      return(pte);
++}
++
++static inline pte_t pte_mkold(pte_t pte)      
++{ 
++      pte_val(pte) &= ~_PAGE_ACCESSED; 
++      return(pte);
++}
++
++static inline pte_t pte_wrprotect(pte_t pte)
++{ 
++      pte_val(pte) &= ~_PAGE_RW; 
++      return(pte_mknewprot(pte)); 
++}
++
++static inline pte_t pte_mkread(pte_t pte)
++{ 
++      pte_val(pte) |= _PAGE_USER; 
++      return(pte_mknewprot(pte)); 
++}
++
++static inline pte_t pte_mkexec(pte_t pte)
++{ 
++      pte_val(pte) |= _PAGE_USER; 
++      return(pte_mknewprot(pte)); 
++}
++
++static inline pte_t pte_mkdirty(pte_t pte)
++{ 
++      pte_val(pte) |= _PAGE_DIRTY; 
++      return(pte);
++}
++
++static inline pte_t pte_mkyoung(pte_t pte)
++{
++      pte_val(pte) |= _PAGE_ACCESSED; 
++      return(pte);
++}
++
++static inline pte_t pte_mkwrite(pte_t pte)    
++{
++      pte_val(pte) |= _PAGE_RW; 
++      return(pte_mknewprot(pte)); 
++}
++
++static inline pte_t pte_mkuptodate(pte_t pte) 
++{
++      pte_val(pte) &= ~_PAGE_NEWPAGE;
++      if(pte_present(pte)) pte_val(pte) &= ~_PAGE_NEWPROT;
++      return(pte); 
++}
++
++extern unsigned long page_to_phys(struct page *page);
++
++/*
++ * Conversion functions: convert a page and protection to a page entry,
++ * and a page entry and page directory to the page they refer to.
++ */
++
++extern pte_t mk_pte(struct page *page, pgprot_t pgprot);
++
++/* This takes a physical page address that is used by the remapping 
++ * functions 
++ */
++#define mk_pte_phys(phys, pgprot) \
++      (pte_mknewpage(mk_pte(virt_to_page(__va(phys)), pgprot)))
++
++static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
++{
++      pte_val(pte) = (pte_val(pte) & _PAGE_CHG_MASK) | pgprot_val(newprot);
++      if(pte_present(pte)) pte = pte_mknewpage(pte_mknewprot(pte));
++      return pte; 
++}
++
++/* to find an entry in a page-table-directory. */
++#define pgd_index(address) ((address >> PGDIR_SHIFT) & (PTRS_PER_PGD-1))
++#define __pgd_offset(address) pgd_index(address)
++
++/* to find an entry in a page-table-directory */
++#define pgd_offset(mm, address) \
++((mm)->pgd + ((address) >> PGDIR_SHIFT))
++
++/* to find an entry in a kernel page-table-directory */
++#define pgd_offset_k(address) pgd_offset(&init_mm, address)
++
++#define __pmd_offset(address) \
++              (((address) >> PMD_SHIFT) & (PTRS_PER_PMD-1))
++
++/* Find an entry in the second-level page table.. */
++static inline pmd_t * pmd_offset(pgd_t * dir, unsigned long address)
++{
++      return (pmd_t *) dir;
++}
++
++/* Find an entry in the third-level page table.. */ 
++#define pte_offset(pmd, address) \
++      ((pte_t *) (pmd_page(*pmd) + ((address>>10) & ((PTRS_PER_PTE-1)<<2))))
++
++#define update_mmu_cache(vma,address,pte) do ; while (0)
++
++/* Encode and de-code a swap entry */
++#define SWP_TYPE(x)                   (((x).val >> 3) & 0x7f)
++#define SWP_OFFSET(x)                 ((x).val >> 10)
++
++#define SWP_ENTRY(type, offset) \
++      ((swp_entry_t) { ((type) << 3) | ((offset) << 10) })
++#define pte_to_swp_entry(pte) \
++      ((swp_entry_t) { pte_val(pte_mkuptodate(pte)) })
++#define swp_entry_to_pte(x)           ((pte_t) { (x).val })
++
++#define PageSkip(x) (0)
++#define kern_addr_valid(addr) (1)
++
++#include <asm-generic/pgtable.h>
++
++#endif
++
++#endif
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/include/asm-um/poll.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/poll.h    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/poll.h 2005-05-03 22:28:14.973333920 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_POLL_H
++#define __UM_POLL_H
++
++#include "asm/arch/poll.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/posix_types.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/posix_types.h     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/posix_types.h  2005-05-03 22:28:14.974333768 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_POSIX_TYPES_H
++#define __UM_POSIX_TYPES_H
++
++#include "asm/arch/posix_types.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/processor-generic.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/processor-generic.h       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/processor-generic.h    2005-05-03 23:41:08.094518448 +0300
+@@ -0,0 +1,183 @@
++/* 
++ * Copyright (C) 2000 - 2004 Jeff Dike (jdike@addtoit.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __UM_PROCESSOR_GENERIC_H
++#define __UM_PROCESSOR_GENERIC_H
++
++struct pt_regs;
++
++struct task_struct;
++
++#include "linux/config.h"
++#include "linux/signal.h"
++#include "asm/ptrace.h"
++#include "asm/siginfo.h"
++#include "choose-mode.h"
++
++struct mm_struct;
++
++#define current_text_addr() ((void *) 0)
++
++#define cpu_relax()   do ; while (0)
++
++struct thread_struct {
++      int forking;
++      int nsyscalls;
++      struct pt_regs regs;
++      unsigned long cr2;
++      int err;
++      unsigned long trap_no;
++      int singlestep_syscall;
++      void *fault_addr;
++      void *fault_catcher;
++      struct task_struct *prev_sched;
++      unsigned long temp_stack;
++      void *exec_buf;
++      struct arch_thread arch;
++      union {
++#ifdef CONFIG_MODE_TT
++              struct {
++                      int extern_pid;
++                      int tracing;
++                      /* XXX This is really two filehandles, but they contain
++                       * lists, and list.h includes processor.h through
++                       * prefetch.h before defining struct list, so this
++                       * makes the lists' sizes unknown at this point.
++                       * So, this is a void *, and allocated separately.
++                       * Check to see if this is fixed in 2.6.
++                       */
++                      void *switch_pipe;
++                      int singlestep_syscall;
++                      int vm_seq;
++              } tt;
++#endif
++#ifdef CONFIG_MODE_SKAS
++              struct {
++                      void *switch_buf;
++                      void *fork_buf;
++                      int mm_count;
++              } skas;
++#endif
++      } mode;
++      struct {
++              int op;
++              union {
++                      struct {
++                              int pid;
++                      } fork, exec;
++                      struct {
++                              int (*proc)(void *);
++                              void *arg;
++                      } thread;
++                      struct {
++                              void (*proc)(void *);
++                              void *arg;
++                      } cb;
++              } u;
++      } request;
++};
++
++#define INIT_THREAD \
++{ \
++      .forking                = 0, \
++      .nsyscalls              = 0, \
++        .regs                 = EMPTY_REGS, \
++      .cr2                    = 0, \
++      .err                    = 0, \
++      .fault_addr             = NULL, \
++      .prev_sched             = NULL, \
++      .temp_stack             = 0, \
++      .exec_buf               = NULL, \
++      .arch                   = INIT_ARCH_THREAD, \
++      .request                = { 0 } \
++}
++
++#define THREAD_SIZE ((1 << CONFIG_KERNEL_STACK_ORDER) * PAGE_SIZE)
++
++typedef struct {
++      unsigned long seg;
++} mm_segment_t;
++
++extern struct task_struct *alloc_task_struct(void);
++extern void free_task_struct(struct task_struct *task);
++
++#define get_task_struct(tsk)      atomic_inc(&virt_to_page(tsk)->count)
++
++extern void release_thread(struct task_struct *);
++extern int arch_kernel_thread(int (*fn)(void *), void * arg, 
++                            unsigned long flags);
++extern void dump_thread(struct pt_regs *regs, struct user *u);
++
++extern unsigned long thread_saved_pc(struct thread_struct *t);
++
++static inline void mm_copy_segments(struct mm_struct *from_mm, 
++                                  struct mm_struct *new_mm)
++{
++}
++
++static inline void copy_segments(struct task_struct *p, 
++                               struct mm_struct *new_mm)
++{
++}
++
++static inline void release_segments(struct mm_struct *mm)
++{
++}
++
++#define init_task     (init_task_union.task)
++#define init_stack    (init_task_union.stack)
++
++/*
++ * User space process size: 3GB (default).
++ */
++extern unsigned long task_size;
++
++#define TASK_SIZE     (task_size)
++
++/* This decides where the kernel will search for a free chunk of vm
++ * space during mmap's.
++ */
++#define TASK_UNMAPPED_BASE    (0x40000000)
++
++extern void start_thread(struct pt_regs *regs, unsigned long entry, 
++                       unsigned long stack);
++
++struct cpuinfo_um {
++      unsigned long loops_per_jiffy;
++      unsigned long *pgd_quick;
++      unsigned long *pmd_quick;
++      unsigned long *pte_quick;
++      unsigned long pgtable_cache_sz;  
++      int ipi_pipe[2];
++};
++
++extern struct cpuinfo_um boot_cpu_data;
++
++#define my_cpu_data           cpu_data[smp_processor_id()]
++
++#ifdef CONFIG_SMP
++extern struct cpuinfo_um cpu_data[];
++#define current_cpu_data cpu_data[smp_processor_id()]
++#else
++#define cpu_data (&boot_cpu_data)
++#define current_cpu_data boot_cpu_data
++#endif
++
++#define KSTK_EIP(tsk) (PT_REGS_IP(&tsk->thread.regs))
++#define KSTK_ESP(tsk) (PT_REGS_SP(&tsk->thread.regs))
++#define get_wchan(p) (0)
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/include/asm-um/processor-i386.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/processor-i386.h  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/processor-i386.h       2005-05-03 23:41:08.095518296 +0300
+@@ -0,0 +1,35 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __UM_PROCESSOR_I386_H
++#define __UM_PROCESSOR_I386_H
++
++extern int cpu_has_xmm;
++extern int cpu_has_cmov;
++
++struct arch_thread {
++      unsigned long debugregs[8];
++      int debugregs_seq;
++};
++
++#define INIT_ARCH_THREAD { .debugregs                 = { [ 0 ... 7 ] = 0 }, \
++                           .debugregs_seq     = 0 }
++
++#include "asm/arch/user.h"
++
++#include "asm/processor-generic.h"
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/include/asm-um/processor-ppc.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/processor-ppc.h   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/processor-ppc.h        2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,15 @@
++#ifndef __UM_PROCESSOR_PPC_H
++#define __UM_PROCESSOR_PPC_H
++
++#if defined(__ASSEMBLY__)
++
++#define CONFIG_ALL_PPC
++#include "arch/processor.h"
++
++#else
++
++#include "asm/processor-generic.h"
++
++#endif
++
++#endif
+Index: linux-2.4.29/include/asm-um/ptrace-generic.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/ptrace-generic.h  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/ptrace-generic.h       2005-05-03 23:41:08.084519968 +0300
+@@ -0,0 +1,74 @@
++/* 
++ * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __UM_PTRACE_GENERIC_H
++#define __UM_PTRACE_GENERIC_H
++
++#ifndef __ASSEMBLY__
++
++#include "linux/config.h"
++
++#include "asm/current.h"
++
++#define pt_regs pt_regs_subarch
++#define show_regs show_regs_subarch
++
++#include "asm/arch/ptrace.h"
++
++#undef pt_regs
++#undef show_regs
++#undef user_mode
++#undef instruction_pointer
++
++#include "sysdep/ptrace.h"
++#include "skas_ptrace.h"
++
++struct pt_regs {
++      union uml_pt_regs regs;
++};
++
++#define EMPTY_REGS { regs : EMPTY_UML_PT_REGS }
++
++#define PT_REGS_IP(r) UPT_IP(&(r)->regs)
++#define PT_REGS_SP(r) UPT_SP(&(r)->regs)
++
++#define PT_REG(r, reg) UPT_REG(&(r)->regs, reg)
++#define PT_REGS_SET(r, reg, val) UPT_SET(&(r)->regs, reg, val)
++
++#define PT_REGS_SET_SYSCALL_RETURN(r, res) \
++      UPT_SET_SYSCALL_RETURN(&(r)->regs, res)
++#define PT_REGS_RESTART_SYSCALL(r) UPT_RESTART_SYSCALL(&(r)->regs)
++
++#define PT_REGS_SYSCALL_NR(r) UPT_SYSCALL_NR(&(r)->regs)
++
++#define PT_REGS_SC(r) UPT_SC(&(r)->regs)
++
++struct task_struct;
++
++extern unsigned long getreg(struct task_struct *child, int regno);
++extern int putreg(struct task_struct *child, int regno, unsigned long value);
++extern int get_fpregs(unsigned long buf, struct task_struct *child);
++extern int set_fpregs(unsigned long buf, struct task_struct *child);
++extern int get_fpxregs(unsigned long buf, struct task_struct *child);
++extern int set_fpxregs(unsigned long buf, struct task_struct *tsk);
++
++extern void show_regs(struct pt_regs *regs);
++
++#define INIT_TASK_SIZE ((1 << CONFIG_KERNEL_STACK_ORDER) * PAGE_SIZE)
++
++#endif
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/include/asm-um/ptrace-i386.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/ptrace-i386.h     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/ptrace-i386.h  2005-05-03 23:41:08.085519816 +0300
+@@ -0,0 +1,46 @@
++/* 
++ * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __UM_PTRACE_I386_H
++#define __UM_PTRACE_I386_H
++
++#include "sysdep/ptrace.h"
++#include "asm/ptrace-generic.h"
++
++#define PT_REGS_EAX(r) UPT_EAX(&(r)->regs)
++#define PT_REGS_EBX(r) UPT_EBX(&(r)->regs)
++#define PT_REGS_ECX(r) UPT_ECX(&(r)->regs)
++#define PT_REGS_EDX(r) UPT_EDX(&(r)->regs)
++#define PT_REGS_ESI(r) UPT_ESI(&(r)->regs)
++#define PT_REGS_EDI(r) UPT_EDI(&(r)->regs)
++#define PT_REGS_EBP(r) UPT_EBP(&(r)->regs)
++
++#define PT_REGS_CS(r) UPT_CS(&(r)->regs)
++#define PT_REGS_SS(r) UPT_SS(&(r)->regs)
++#define PT_REGS_DS(r) UPT_DS(&(r)->regs)
++#define PT_REGS_ES(r) UPT_ES(&(r)->regs)
++#define PT_REGS_FS(r) UPT_FS(&(r)->regs)
++#define PT_REGS_GS(r) UPT_GS(&(r)->regs)
++
++#define PT_REGS_EFLAGS(r) UPT_EFLAGS(&(r)->regs)
++
++#define PT_REGS_ORIG_SYSCALL(r) PT_REGS_EAX(r)
++#define PT_REGS_SYSCALL_RET(r) PT_REGS_EAX(r)
++#define PT_FIX_EXEC_STACK(sp) do ; while(0)
++
++#define user_mode(r) UPT_IS_USER(&(r)->regs)
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/include/asm-um/resource.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/resource.h        1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/resource.h     2005-05-03 22:28:14.980332856 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_RESOURCE_H
++#define __UM_RESOURCE_H
++
++#include "asm/arch/resource.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/rwlock.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/rwlock.h  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/rwlock.h       2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_RWLOCK_H
++#define __UM_RWLOCK_H
++
++#include "asm/arch/rwlock.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/rwsem.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/rwsem.h   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/rwsem.h        2005-05-03 23:41:08.109516168 +0300
+@@ -0,0 +1,10 @@
++#ifndef __UM_RWSEM_H__
++#define __UM_RWSEM_H__
++
++#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 96)
++#define __builtin_expect(exp,c) (exp)
++#endif
++
++#include "asm/arch/rwsem.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/scatterlist.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/scatterlist.h     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/scatterlist.h  2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_SCATTERLIST_H
++#define __UM_SCATTERLIST_H
++
++#include "asm/arch/scatterlist.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/segment.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/segment.h 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/segment.h      2005-05-03 22:28:14.983332400 +0300
+@@ -0,0 +1,4 @@
++#ifndef __UM_SEGMENT_H
++#define __UM_SEGMENT_H
++
++#endif
+Index: linux-2.4.29/include/asm-um/semaphore.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/semaphore.h       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/semaphore.h    2005-05-03 23:41:08.110516016 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_SEMAPHORE_H
++#define __UM_SEMAPHORE_H
++
++#include "asm/arch/semaphore.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/sembuf.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/sembuf.h  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/sembuf.h       2005-05-03 22:28:14.984332248 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_SEMBUF_H
++#define __UM_SEMBUF_H
++
++#include "asm/arch/sembuf.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/serial.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/serial.h  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/serial.h       2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_SERIAL_H
++#define __UM_SERIAL_H
++
++#include "asm/arch/serial.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/shmbuf.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/shmbuf.h  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/shmbuf.h       2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_SHMBUF_H
++#define __UM_SHMBUF_H
++
++#include "asm/arch/shmbuf.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/shmparam.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/shmparam.h        1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/shmparam.h     2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_SHMPARAM_H
++#define __UM_SHMPARAM_H
++
++#include "asm/arch/shmparam.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/sigcontext-generic.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/sigcontext-generic.h      1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/sigcontext-generic.h   2005-05-03 22:28:14.987331792 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_SIGCONTEXT_GENERIC_H
++#define __UM_SIGCONTEXT_GENERIC_H
++
++#include "asm/arch/sigcontext.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/sigcontext-i386.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/sigcontext-i386.h 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/sigcontext-i386.h      2005-05-03 22:28:14.988331640 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_SIGCONTEXT_I386_H
++#define __UM_SIGCONTEXT_I386_H
++
++#include "asm/sigcontext-generic.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/sigcontext-ppc.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/sigcontext-ppc.h  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/sigcontext-ppc.h       2005-05-03 22:28:14.000000000 +0300
+@@ -0,0 +1,10 @@
++#ifndef __UM_SIGCONTEXT_PPC_H
++#define __UM_SIGCONTEXT_PPC_H
++
++#define pt_regs sys_pt_regs
++
++#include "asm/sigcontext-generic.h"
++
++#undef pt_regs
++
++#endif
+Index: linux-2.4.29/include/asm-um/siginfo.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/siginfo.h 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/siginfo.h      2005-05-03 23:41:08.092518752 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_SIGINFO_H
++#define __UM_SIGINFO_H
++
++#include "asm/arch/siginfo.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/signal.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/signal.h  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/signal.h       2005-05-03 23:41:08.090519056 +0300
+@@ -0,0 +1,22 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __UM_SIGNAL_H
++#define __UM_SIGNAL_H
++
++#include "asm/arch/signal.h"
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/include/asm-um/smp.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/smp.h     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/smp.h  2005-05-03 23:41:09.801258984 +0300
+@@ -0,0 +1,19 @@
++#ifndef __UM_SMP_H
++#define __UM_SMP_H
++
++#ifdef CONFIG_SMP
++
++#include "linux/config.h"
++#include "asm/current.h"
++
++#define smp_processor_id() (current->processor)
++#define cpu_logical_map(n) (n)
++#define cpu_number_map(n) (n)
++#define PROC_CHANGE_PENALTY   15 /* Pick a number, any number */
++extern int hard_smp_processor_id(void);
++extern unsigned long cpu_online_map;
++#define NO_PROC_ID -1
++
++#endif
++
++#endif
+Index: linux-2.4.29/include/asm-um/smplock.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/smplock.h 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/smplock.h      2005-05-03 23:41:09.912242112 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_SMPLOCK_H
++#define __UM_SMPLOCK_H
++
++#include "asm/arch/smplock.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/socket.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/socket.h  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/socket.h       2005-05-03 22:37:45.384618224 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_SOCKET_H
++#define __UM_SOCKET_H
++
++#include "asm/arch/socket.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/sockios.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/sockios.h 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/sockios.h      2005-05-03 22:28:14.994330728 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_SOCKIOS_H
++#define __UM_SOCKIOS_H
++
++#include "asm/arch/sockios.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/softirq.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/softirq.h 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/softirq.h      2005-05-03 23:41:09.910242416 +0300
+@@ -0,0 +1,13 @@
++#ifndef __UM_SOFTIRQ_H
++#define __UM_SOFTIRQ_H
++
++#include "linux/smp.h"
++#include "asm/system.h"
++#include "asm/processor.h"
++
++/* A gratuitous name change */
++#define i386_bh_lock um_bh_lock
++#include "asm/arch/softirq.h"
++#undef i386_bh_lock
++
++#endif
+Index: linux-2.4.29/include/asm-um/spinlock.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/spinlock.h        1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/spinlock.h     2005-05-03 23:41:08.101517384 +0300
+@@ -0,0 +1,10 @@
++#ifndef __UM_SPINLOCK_H
++#define __UM_SPINLOCK_H
++
++#include "linux/config.h"
++
++#ifdef CONFIG_SMP
++#include "asm/arch/spinlock.h"
++#endif
++
++#endif
+Index: linux-2.4.29/include/asm-um/statfs.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/statfs.h  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/statfs.h       2005-05-03 23:41:08.104516928 +0300
+@@ -0,0 +1,6 @@
++#ifndef _UM_STATFS_H
++#define _UM_STATFS_H
++
++#include "asm/arch/statfs.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/stat.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/stat.h    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/stat.h 2005-05-03 22:28:14.997330272 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_STAT_H
++#define __UM_STAT_H
++
++#include "asm/arch/stat.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/string.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/string.h  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/string.h       2005-05-03 22:28:14.998330120 +0300
+@@ -0,0 +1,7 @@
++#ifndef __UM_STRING_H
++#define __UM_STRING_H
++
++#include "asm/arch/string.h"
++#include "asm/archparam.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/system-generic.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/system-generic.h  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/system-generic.h       2005-05-03 23:41:08.098517840 +0300
+@@ -0,0 +1,50 @@
++#ifndef __UM_SYSTEM_GENERIC_H
++#define __UM_SYSTEM_GENERIC_H
++
++#include "asm/arch/system.h"
++
++#undef prepare_to_switch
++#undef switch_to
++#undef __save_flags
++#undef save_flags
++#undef __restore_flags
++#undef restore_flags
++#undef __cli
++#undef __sti
++#undef cli
++#undef sti
++#undef local_irq_save
++#undef local_irq_restore
++#undef local_irq_disable
++#undef local_irq_enable
++
++#define prepare_to_switch() do ; while(0)
++
++void *_switch_to(void *prev, void *next);
++
++#define switch_to(prev, next, last) prev = _switch_to(prev, next)
++
++extern int get_signals(void);
++extern int set_signals(int enable);
++extern void block_signals(void);
++extern void unblock_signals(void);
++
++#define local_irq_save(flags) do { (flags) = set_signals(0); } while(0)
++
++#define local_irq_restore(flags) do { set_signals(flags); } while(0)
++
++#define local_irq_enable() unblock_signals()
++#define local_irq_disable() block_signals()
++
++#define __sti() unblock_signals()
++#define sti() unblock_signals()
++#define __cli() block_signals()
++#define cli() block_signals()
++
++#define __save_flags(x) do { (x) = get_signals(); } while(0)
++#define save_flags(x) __save_flags(x)
++
++#define __restore_flags(x) local_irq_restore(x)
++#define restore_flags(x) __restore_flags(x)
++
++#endif
+Index: linux-2.4.29/include/asm-um/system-i386.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/system-i386.h     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/system-i386.h  2005-05-03 23:41:08.098517840 +0300
+@@ -0,0 +1,8 @@
++#ifndef __UM_SYSTEM_I386_H
++#define __UM_SYSTEM_I386_H
++
++#include "asm/system-generic.h"
++
++#define __HAVE_ARCH_CMPXCHG 1
++
++#endif
+Index: linux-2.4.29/include/asm-um/system-ppc.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/system-ppc.h      1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/system-ppc.h   2005-05-03 22:28:15.000000000 +0300
+@@ -0,0 +1,12 @@
++#ifndef __UM_SYSTEM_PPC_H
++#define __UM_SYSTEM_PPC_H
++
++#define _switch_to _ppc_switch_to
++
++#include "asm/arch/system.h"
++
++#undef _switch_to
++ 
++#include "asm/system-generic.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/termbits.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/termbits.h        1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/termbits.h     2005-05-03 22:37:45.506599680 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_TERMBITS_H
++#define __UM_TERMBITS_H
++
++#include "asm/arch/termbits.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/termios.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/termios.h 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/termios.h      2005-05-03 22:37:45.512598768 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_TERMIOS_H
++#define __UM_TERMIOS_H
++
++#include "asm/arch/termios.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/timex.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/timex.h   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/timex.h        2005-05-03 23:41:09.799259288 +0300
+@@ -0,0 +1,18 @@
++#ifndef __UM_TIMEX_H
++#define __UM_TIMEX_H
++
++#include "linux/time.h"
++
++typedef unsigned long cycles_t;
++
++#define cacheflush_time (0)
++
++static inline cycles_t get_cycles (void)
++{
++      return 0;
++}
++
++#define vxtime_lock()         do ; while (0)
++#define vxtime_unlock()               do ; while (0)
++
++#endif
+Index: linux-2.4.29/include/asm-um/tlb.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/tlb.h     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/tlb.h  2005-05-03 22:28:15.000000000 +0300
+@@ -0,0 +1 @@
++#include <asm-generic/tlb.h>
+Index: linux-2.4.29/include/asm-um/types.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/types.h   1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/types.h        2005-05-03 22:28:15.006328904 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_TYPES_H
++#define __UM_TYPES_H
++
++#include "asm/arch/types.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/uaccess.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/uaccess.h 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/uaccess.h      2005-05-03 23:41:09.913241960 +0300
+@@ -0,0 +1,99 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __UM_UACCESS_H
++#define __UM_UACCESS_H
++
++#include "linux/sched.h"
++
++#define VERIFY_READ 0
++#define VERIFY_WRITE 1
++
++/*
++ * The fs value determines whether argument validity checking should be
++ * performed or not.  If get_fs() == USER_DS, checking is performed, with
++ * get_fs() == KERNEL_DS, checking is bypassed.
++ *
++ * For historical reasons, these macros are grossly misnamed.
++ */
++
++#define MAKE_MM_SEG(s)        ((mm_segment_t) { (s) })
++
++#define KERNEL_DS     MAKE_MM_SEG(0xFFFFFFFF)
++#define USER_DS               MAKE_MM_SEG(TASK_SIZE)
++
++#define get_ds()      (KERNEL_DS)
++#define get_fs()      (current->addr_limit)
++#define set_fs(x)     (current->addr_limit = (x))
++
++#define segment_eq(a, b) ((a).seg == (b).seg)
++
++#include "um_uaccess.h"
++
++#define __copy_from_user(to, from, n) copy_from_user(to, from, n)
++
++#define __copy_to_user(to, from, n) copy_to_user(to, from, n)
++
++#define __get_user(x, ptr) \
++({ \
++        const __typeof__(ptr) __private_ptr = ptr; \
++        __typeof__(*(__private_ptr)) __private_val; \
++        int __private_ret = -EFAULT; \
++        (x) = 0; \
++      if (__copy_from_user(&__private_val, (__private_ptr), \
++          sizeof(*(__private_ptr))) == 0) {\
++              (x) = (__typeof__(*(__private_ptr))) __private_val; \
++              __private_ret = 0; \
++      } \
++        __private_ret; \
++}) 
++
++#define get_user(x, ptr) \
++({ \
++        const __typeof__((*ptr)) *private_ptr = (ptr); \
++        (access_ok(VERIFY_READ, private_ptr, sizeof(*private_ptr)) ? \
++       __get_user(x, private_ptr) : ((x) = 0, -EFAULT)); \
++})
++
++#define __put_user(x, ptr) \
++({ \
++        __typeof__(ptr) __private_ptr = ptr; \
++        __typeof__(*(__private_ptr)) __private_val; \
++        int __private_ret = -EFAULT; \
++        __private_val = (__typeof__(*(__private_ptr))) (x); \
++        if (__copy_to_user((__private_ptr), &__private_val, \
++                         sizeof(*(__private_ptr))) == 0) { \
++              __private_ret = 0; \
++      } \
++        __private_ret; \
++})
++
++#define put_user(x, ptr) \
++({ \
++        __typeof__(*(ptr)) *private_ptr = (ptr); \
++        (access_ok(VERIFY_WRITE, private_ptr, sizeof(*private_ptr)) ? \
++       __put_user(x, private_ptr) : -EFAULT); \
++})
++
++#define strlen_user(str) strnlen_user(str, ~0UL >> 1)
++
++struct exception_table_entry
++{
++        unsigned long insn;
++      unsigned long fixup;
++};
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/include/asm-um/ucontext.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/ucontext.h        1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/ucontext.h     2005-05-03 22:28:15.008328600 +0300
+@@ -0,0 +1,6 @@
++#ifndef _ASM_UM_UCONTEXT_H
++#define _ASM_UM_UCONTEXT_H
++
++#include "asm/arch/ucontext.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/unaligned.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/unaligned.h       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/unaligned.h    2005-05-03 22:28:15.000000000 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_UNALIGNED_H
++#define __UM_UNALIGNED_H
++
++#include "asm/arch/unaligned.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/unistd.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/unistd.h  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/unistd.h       2005-05-03 23:41:11.206045424 +0300
+@@ -0,0 +1,121 @@
++/* 
++ * Copyright (C) 2000, 2001  Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef _UM_UNISTD_H_
++#define _UM_UNISTD_H_
++
++#include "linux/resource.h"
++#include "asm/uaccess.h"
++
++extern long sys_open(const char *filename, int flags, int mode);
++extern long sys_dup(unsigned int fildes);
++extern long sys_close(unsigned int fd);
++extern int um_execve(const char *file, char *const argv[], char *const env[]);
++extern long sys_setsid(void);
++extern long sys_waitpid(pid_t pid, unsigned int * stat_addr, int options);
++extern long sys_wait4(pid_t pid,unsigned int *stat_addr, int options, 
++                    struct rusage *ru);
++extern long sys_mount(char *dev_name, char *dir_name, char *type, 
++                    unsigned long flags, void *data);
++extern long sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, 
++                     struct timeval *tvp);
++extern long sys_lseek(unsigned int fildes, unsigned long offset, int whence);
++extern long sys_read(unsigned int fildes, char *buf, int len);
++extern long sys_write(unsigned int fildes, char *buf, int len);
++
++#ifdef __KERNEL_SYSCALLS__
++
++#define KERNEL_CALL(ret_t, sys, args...)      \
++      mm_segment_t fs = get_fs();             \
++      ret_t ret;                              \
++      set_fs(KERNEL_DS);                      \
++      ret = sys(args);                        \
++      set_fs(fs);                             \
++      if (ret >= 0)                           \
++              return ret;                     \
++      errno = -(long)ret;                     \
++      return -1;
++
++static inline long open(const char *pathname, int flags, int mode) 
++{
++      KERNEL_CALL(int, sys_open, pathname, flags, mode)
++}
++
++static inline long dup(unsigned int fd)
++{
++      KERNEL_CALL(int, sys_dup, fd);
++}
++
++static inline long close(unsigned int fd)
++{
++      KERNEL_CALL(int, sys_close, fd);
++}
++
++static inline int execve(const char *filename, char *const argv[], 
++                       char *const envp[])
++{
++      KERNEL_CALL(int, um_execve, filename, argv, envp);
++}
++
++static inline long waitpid(pid_t pid, unsigned int *status, int options)
++{
++      KERNEL_CALL(pid_t, sys_wait4, pid, status, options, NULL)
++}
++
++static inline pid_t wait(int *status)
++{
++      KERNEL_CALL(pid_t, sys_wait4, -1, status, 0, NULL)
++}
++
++static inline pid_t setsid(void)
++{
++      KERNEL_CALL(pid_t, sys_setsid)
++}
++
++static inline long lseek(unsigned int fd, off_t offset, unsigned int whence)
++{
++      KERNEL_CALL(long, sys_lseek, fd, offset, whence)
++}
++
++static inline int read(unsigned int fd, char * buf, int len)
++{
++      KERNEL_CALL(int, sys_read, fd, buf, len)
++}
++
++static inline int write(unsigned int fd, char * buf, int len)
++{
++      KERNEL_CALL(int, sys_write, fd, buf, len)
++}
++
++#endif
++
++/* Save the value of __KERNEL_SYSCALLS__, undefine it, include the underlying
++ * arch's unistd.h for the system call numbers, and restore the old 
++ * __KERNEL_SYSCALLS__.
++ */
++
++#ifdef __KERNEL_SYSCALLS__
++#define __SAVE_KERNEL_SYSCALLS__ __KERNEL_SYSCALLS__
++#endif
++
++#undef __KERNEL_SYSCALLS__
++#include "asm/arch/unistd.h"
++
++#ifdef __KERNEL_SYSCALLS__
++#define __KERNEL_SYSCALLS__ __SAVE_KERNEL_SYSCALLS__
++#endif
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/include/asm-um/user.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/user.h    1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/user.h 2005-05-03 23:43:26.305507184 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_USER_H
++#define __UM_USER_H
++
++#include "asm/arch/user.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/vga.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/vga.h     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/vga.h  2005-05-03 22:28:15.000000000 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_VGA_H
++#define __UM_VGA_H
++
++#include "asm/arch/vga.h"
++
++#endif
+Index: linux-2.4.29/include/asm-um/xor.h
+===================================================================
+--- linux-2.4.29.orig/include/asm-um/xor.h     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/asm-um/xor.h  2005-05-03 22:28:15.000000000 +0300
+@@ -0,0 +1,6 @@
++#ifndef __UM_XOR_H
++#define __UM_XOR_H
++
++#include "asm-generic/xor.h"
++
++#endif
+Index: linux-2.4.29/include/linux/blk.h
+===================================================================
+--- linux-2.4.29.orig/include/linux/blk.h      2005-05-03 21:09:03.000000000 +0300
++++ linux-2.4.29/include/linux/blk.h   2005-05-03 23:41:26.547713136 +0300
+@@ -320,6 +320,24 @@
+ #define DEVICE_REQUEST do_ida_request
+ #define DEVICE_NR(device) (MINOR(device) >> 4)
++#elif (MAJOR_NR == UBD_MAJOR)
++
++#define DEVICE_NAME "User-mode block device"
++#define DEVICE_INTR do_ubd
++#define DEVICE_REQUEST do_ubd_request
++#define DEVICE_NR(device) (MINOR(device) >> UBD_SHIFT)
++#define DEVICE_ON(device)
++#define DEVICE_OFF(device)
++
++#elif (MAJOR_NR == COW_MAJOR)
++
++#define DEVICE_NAME "COW device"
++#define DEVICE_INTR do_cow
++#define DEVICE_REQUEST do_cow_request
++#define DEVICE_NR(device) (MINOR(device) >> COW_SHIFT)
++#define DEVICE_ON(device)
++#define DEVICE_OFF(device)
++
+ #endif /* MAJOR_NR == whatever */
+ /* provide DEVICE_xxx defaults, if not explicitly defined
+Index: linux-2.4.29/include/linux/fs.h
+===================================================================
+--- linux-2.4.29.orig/include/linux/fs.h       2005-05-03 21:06:01.000000000 +0300
++++ linux-2.4.29/include/linux/fs.h    2005-05-03 23:56:00.359873496 +0300
+@@ -322,6 +322,8 @@
+ #include <linux/ncp_fs_i.h>
+ #include <linux/proc_fs_i.h>
+ #include <linux/usbdev_fs_i.h>
++#include <linux/hostfs_fs_i.h>
++#include <linux/hppfs_fs_i.h>
+ #include <linux/jffs2_fs_i.h>
+ #include <linux/cramfs_fs_sb.h>
+@@ -518,7 +520,9 @@
+               struct proc_inode_info          proc_i;
+               struct socket                   socket_i;
+               struct usbdev_inode_info        usbdev_i;
+-              struct jffs2_inode_info         jffs2_i;
++              struct hostfs_inode_info        hostfs_i;
++              struct hppfs_inode_info         hppfs_i;
++              struct jffs2_inode_info         jffs2_i;
+               void                            *generic_ip;
+       } u;
+ };
+@@ -866,6 +870,8 @@
+       unsigned int (*poll) (struct file *, struct poll_table_struct *);
+       int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
+       int (*mmap) (struct file *, struct vm_area_struct *);
++      void (*munmap) (struct file *, struct vm_area_struct *, 
++                      unsigned long start, unsigned long len);
+       int (*open) (struct inode *, struct file *);
+       int (*flush) (struct file *);
+       int (*release) (struct inode *, struct file *);
+Index: linux-2.4.29/include/linux/ghash.h
+===================================================================
+--- linux-2.4.29.orig/include/linux/ghash.h    2005-05-03 21:09:50.000000000 +0300
++++ linux-2.4.29/include/linux/ghash.h 2005-05-03 22:28:15.000000000 +0300
+@@ -153,6 +153,26 @@
+       return NULL;\
+ }
++/* LINKAGE - empty or "static", depending on whether you want the definitions to
++ *    be public or not
++ * NAME - a string to stick in names to make this hash table type distinct from
++ *    any others
++ * HASHSIZE - number of buckets
++ * TYPE - type of data contained in the buckets - must be a structure, one 
++ *    field is of type NAME_ptrs, another is the hash key
++ * PTRS - TYPE must contain a field of type NAME_ptrs, PTRS is the name of that
++ *    field
++ * KEYTYPE - type of the key field within TYPE
++ * KEY - name of the key field within TYPE
++ * KEYCMP - pointer to function that compares KEYTYPEs to each other - the
++ *    prototype is int KEYCMP(KEYTYPE, KEYTYPE), it returns zero for equal, 
++ *    non-zero for not equal
++ * HASHFN - the hash function - the prototype is int HASHFN(KEYTYPE),
++ *    it returns a number in the range 0 ... HASHSIZE - 1
++ * Call DEF_HASH_STRUCTS, define your hash table as a NAME_table, then call
++ * DEF_HASH.
++ */
++
+ #define DEF_HASH_STRUCTS(NAME,HASHSIZE,TYPE) \
+ \
+ struct NAME##_table {\
+@@ -165,7 +185,7 @@
+       TYPE * prev_hash;\
+ };
+-#define DEF_HASH(LINKAGE,NAME,HASHSIZE,TYPE,PTRS,KEYTYPE,KEY,KEYCMP,KEYEQ,HASHFN)\
++#define DEF_HASH(LINKAGE,NAME,TYPE,PTRS,KEYTYPE,KEY,KEYCMP,HASHFN)\
+ \
+ LINKAGE void insert_##NAME##_hash(struct NAME##_table * tbl, TYPE * elem)\
+ {\
+@@ -206,12 +226,10 @@
+ \
+ LINKAGE TYPE * find_##NAME##_hash(struct NAME##_table * tbl, KEYTYPE pos)\
+ {\
+-      int ix = hashfn(pos);\
++      int ix = HASHFN(pos);\
+       TYPE * ptr = tbl->hashtable[ix];\
+       while(ptr && KEYCMP(ptr->KEY, pos))\
+               ptr = ptr->PTRS.next_hash;\
+-      if(ptr && !KEYEQ(ptr->KEY, pos))\
+-              ptr = NULL;\
+       return ptr;\
+ }
+Index: linux-2.4.29/include/linux/hostfs_fs_i.h
+===================================================================
+--- linux-2.4.29.orig/include/linux/hostfs_fs_i.h      1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/linux/hostfs_fs_i.h   2005-05-03 22:28:15.053321760 +0300
+@@ -0,0 +1,25 @@
++#ifndef _HOSTFS_FS_I
++#define _HOSTFS_FS_I
++
++#include "filehandle.h"
++
++struct externfs_file_ops;
++
++struct hostfs_inode_info {
++      struct externfs_file_ops *ops;
++      struct file_handle *fh;
++      int mode;
++};
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/include/linux/hppfs_fs_i.h
+===================================================================
+--- linux-2.4.29.orig/include/linux/hppfs_fs_i.h       1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/linux/hppfs_fs_i.h    2005-05-03 22:28:15.054321608 +0300
+@@ -0,0 +1,19 @@
++#ifndef _HPPFS_FS_I
++#define _HPPFS_FS_I
++
++struct hppfs_inode_info {
++      struct dentry *proc_dentry;
++};
++
++#endif
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/include/linux/kernel.h
+===================================================================
+--- linux-2.4.29.orig/include/linux/kernel.h   2005-05-03 21:04:57.000000000 +0300
++++ linux-2.4.29/include/linux/kernel.h        2005-05-03 23:41:08.088519360 +0300
+@@ -49,7 +49,7 @@
+ # define ATTRIB_NORET  __attribute__((noreturn))
+ # define NORET_AND     noreturn,
+-#ifdef __i386__
++#if defined(__i386__) || defined(UM_FASTCALL)
+ #define FASTCALL(x)   x __attribute__((regparm(3)))
+ #define fastcall      __attribute__((regparm(3)))
+ #else
+Index: linux-2.4.29/include/linux/kernel_stat.h
+===================================================================
+--- linux-2.4.29.orig/include/linux/kernel_stat.h      2005-05-03 21:09:36.000000000 +0300
++++ linux-2.4.29/include/linux/kernel_stat.h   2005-05-03 23:41:09.913241960 +0300
+@@ -12,7 +12,7 @@
+  * used by rstatd/perfmeter
+  */
+-#define DK_MAX_MAJOR 16
++#define DK_MAX_MAJOR 99
+ #define DK_MAX_DISK 16
+ struct kernel_stat {
+Index: linux-2.4.29/include/linux/mm.h
+===================================================================
+--- linux-2.4.29.orig/include/linux/mm.h       2005-05-03 21:05:15.000000000 +0300
++++ linux-2.4.29/include/linux/mm.h    2005-05-03 23:55:59.724970016 +0300
+@@ -441,6 +441,18 @@
+ extern struct page * FASTCALL(__alloc_pages(unsigned int gfp_mask, unsigned int order, zonelist_t *zonelist));
+ extern struct page * alloc_pages_node(int nid, unsigned int gfp_mask, unsigned int order);
++#ifndef HAVE_ARCH_VALIDATE
++static inline struct page *arch_validate(struct page *page, 
++                                       unsigned int gfp_mask, int order)
++{
++        return(page);
++}
++#endif
++
++#ifndef HAVE_ARCH_FREE_PAGE
++static inline void arch_free_page(struct page *page, int order) { }
++#endif
++
+ static inline struct page * alloc_pages(unsigned int gfp_mask, unsigned int order)
+ {
+       /*
+@@ -448,7 +460,7 @@
+        */
+       if (order >= MAX_ORDER)
+               return NULL;
+-      return _alloc_pages(gfp_mask, order);
++      return arch_validate(_alloc_pages(gfp_mask, order), gfp_mask, order);
+ }
+ #define alloc_page(gfp_mask) alloc_pages(gfp_mask, 0)
+@@ -508,6 +520,9 @@
+ int get_user_pages(struct task_struct *tsk, struct mm_struct *mm, unsigned long start,
+               int len, int write, int force, struct page **pages, struct vm_area_struct **vmas);
++extern long do_mprotect(struct mm_struct *mm, unsigned long start, 
++                      size_t len, unsigned long prot);
++
+ /*
+  * On a two-level page table, this ends up being trivial. Thus the
+  * inlining and the symmetry break with pte_alloc() that does all
+@@ -555,9 +570,10 @@
+ extern unsigned long get_unmapped_area(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
+-extern unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,
+-      unsigned long len, unsigned long prot,
+-      unsigned long flag, unsigned long pgoff);
++extern unsigned long do_mmap_pgoff(struct mm_struct *mm, struct file *file, 
++                                 unsigned long addr, unsigned long len,
++                                 unsigned long prot, unsigned long flag,
++                                 unsigned long pgoff);
+ static inline unsigned long do_mmap(struct file *file, unsigned long addr,
+       unsigned long len, unsigned long prot,
+@@ -567,7 +583,8 @@
+       if ((offset + PAGE_ALIGN(len)) < offset)
+               goto out;
+       if (!(offset & ~PAGE_MASK))
+-              ret = do_mmap_pgoff(file, addr, len, prot, flag, offset >> PAGE_SHIFT);
++              ret = do_mmap_pgoff(current->mm, file, addr, len, prot, flag, 
++                                  offset >> PAGE_SHIFT);
+ out:
+       return ret;
+ }
+Index: linux-2.4.29/include/linux/proc_mm.h
+===================================================================
+--- linux-2.4.29.orig/include/linux/proc_mm.h  1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/include/linux/proc_mm.h       2005-05-03 23:46:00.225107848 +0300
+@@ -0,0 +1,48 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#ifndef __PROC_MM_H
++#define __PROC_MM_H
++
++#include "linux/sched.h"
++
++#define MM_MMAP 54
++#define MM_MUNMAP 55
++#define MM_MPROTECT 56
++#define MM_COPY_SEGMENTS 57
++
++struct mm_mmap {
++      unsigned long addr;
++      unsigned long len;
++      unsigned long prot;
++      unsigned long flags;
++      unsigned long fd;
++      unsigned long offset;
++};
++
++struct mm_munmap {
++      unsigned long addr;
++      unsigned long len;      
++};
++
++struct mm_mprotect {
++      unsigned long addr;
++      unsigned long len;
++        unsigned int prot;
++};
++
++struct proc_mm_op {
++      int op;
++      union {
++              struct mm_mmap mmap;
++              struct mm_munmap munmap;
++              struct mm_mprotect mprotect;
++              int copy_segments;
++      } u;
++};
++
++extern struct mm_struct *proc_mm_get_mm(int fd);
++
++#endif
+Index: linux-2.4.29/include/linux/shmem_fs.h
+===================================================================
+--- linux-2.4.29.orig/include/linux/shmem_fs.h 2005-05-03 21:09:04.000000000 +0300
++++ linux-2.4.29/include/linux/shmem_fs.h      2005-05-03 22:28:15.082317352 +0300
+@@ -22,6 +22,8 @@
+       unsigned long           next_index;
+       swp_entry_t             i_direct[SHMEM_NR_DIRECT]; /* for the first blocks */
+       void                  **i_indirect; /* indirect blocks */
++      unsigned long           map_direct[SHMEM_NR_DIRECT];
++      void                  **map_indirect;
+       unsigned long           swapped;    /* data pages assigned to swap */
+       unsigned long           flags;
+       struct list_head        list;
+Index: linux-2.4.29/include/linux/tty.h
+===================================================================
+--- linux-2.4.29.orig/include/linux/tty.h      2005-05-03 21:07:23.000000000 +0300
++++ linux-2.4.29/include/linux/tty.h   2005-05-03 23:41:09.901243784 +0300
+@@ -310,6 +310,9 @@
+       spinlock_t read_lock;
+       /* If the tty has a pending do_SAK, queue it here - akpm */
+       struct tq_struct SAK_tq;
++#ifdef CONFIG_TTY_LOG
++        int log_fd;
++#endif
+ };
+ /* tty magic number */
+@@ -368,6 +371,7 @@
+ extern int specialix_init(void);
+ extern int espserial_init(void);
+ extern int macserial_init(void);
++extern int stdio_init(void);
+ extern int a2232board_init(void);
+ extern int tty_paranoia_check(struct tty_struct *tty, kdev_t device,
+@@ -434,5 +438,7 @@
+ extern int vt_ioctl(struct tty_struct *tty, struct file * file,
+                   unsigned int cmd, unsigned long arg);
++extern void stdio_console_init(void);
++
+ #endif /* __KERNEL__ */
+ #endif
+Index: linux-2.4.29/init/do_mounts.c
+===================================================================
+--- linux-2.4.29.orig/init/do_mounts.c 2005-05-03 21:09:10.000000000 +0300
++++ linux-2.4.29/init/do_mounts.c      2005-05-03 22:28:15.000000000 +0300
+@@ -154,6 +154,22 @@
+       { "pf",         0x2f00 },
+       { "apblock", APBLOCK_MAJOR << 8},
+       { "ddv", DDV_MAJOR << 8},
++      { "ubd0", UBD_MAJOR << 8 | 0 << 4},
++      { "ubda", UBD_MAJOR << 8 | 0 << 4},
++      { "ubd1", UBD_MAJOR << 8 | 1 << 4},
++      { "ubdb", UBD_MAJOR << 8 | 1 << 4},
++      { "ubd2", UBD_MAJOR << 8 | 2 << 4},
++      { "ubdc", UBD_MAJOR << 8 | 2 << 4},
++      { "ubd3", UBD_MAJOR << 8 | 3 << 4},
++      { "ubdd", UBD_MAJOR << 8 | 3 << 4},
++      { "ubd4", UBD_MAJOR << 8 | 4 << 4},
++      { "ubde", UBD_MAJOR << 8 | 4 << 4},
++      { "ubd5", UBD_MAJOR << 8 | 5 << 4},
++      { "ubdf", UBD_MAJOR << 8 | 5 << 4},
++      { "ubd6", UBD_MAJOR << 8 | 6 << 4},
++      { "ubdg", UBD_MAJOR << 8 | 6 << 4},
++      { "ubd7", UBD_MAJOR << 8 | 7 << 4},
++      { "ubdh", UBD_MAJOR << 8 | 7 << 4},
+       { "jsfd",    JSFD_MAJOR << 8},
+ #if defined(CONFIG_ARCH_S390)
+       { "dasda", (DASD_MAJOR << MINORBITS) },
+Index: linux-2.4.29/kernel/panic.c
+===================================================================
+--- linux-2.4.29.orig/kernel/panic.c   2005-05-03 21:09:35.000000000 +0300
++++ linux-2.4.29/kernel/panic.c        2005-05-03 22:28:15.000000000 +0300
+@@ -74,7 +74,7 @@
+       smp_send_stop();
+ #endif
+-      notifier_call_chain(&panic_notifier_list, 0, NULL);
++      notifier_call_chain(&panic_notifier_list, 0, buf);
+       if (panic_timeout > 0)
+       {
+Index: linux-2.4.29/MAINTAINERS
+===================================================================
+--- linux-2.4.29.orig/MAINTAINERS      2005-05-03 21:07:57.000000000 +0300
++++ linux-2.4.29/MAINTAINERS   2005-05-03 22:28:15.000000000 +0300
+@@ -2120,6 +2120,14 @@
+ L:    linux-usb-devel@lists.sourceforge.net
+ W:    http://usb.in.tum.de
+ S:    Maintained
++
++USER-MODE PORT
++P:    Jeff Dike
++M:    jdike@karaya.com
++L:    user-mode-linux-devel@lists.sourceforge.net
++L:    user-mode-linux-user@lists.sourceforge.net
++W:    http://user-mode-linux.sourceforge.net
++S:    Maintained
+       
+ USB "USBNET" DRIVER
+ P:    David Brownell
+Index: linux-2.4.29/Makefile
+===================================================================
+--- linux-2.4.29.orig/Makefile 2005-05-03 21:08:14.000000000 +0300
++++ linux-2.4.29/Makefile      2005-05-03 22:28:15.000000000 +0300
+@@ -5,7 +5,15 @@
+ KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
+-ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
++# SUBARCH tells the usermode build what the underlying arch is.  That is set
++# first, and if a usermode build is happening, the "ARCH=um" on the command
++# line overrides the setting of ARCH below.  If a native build is happening,
++# then ARCH is assigned, getting whatever value it gets normally, and 
++# SUBARCH is subsequently ignored.
++
++SUBARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
++ARCH := $(SUBARCH)
++
+ KERNELPATH=kernel-$(shell echo $(KERNELRELEASE) | sed -e "s/-//g")
+ CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
+Index: linux-2.4.29/mm/Makefile
+===================================================================
+--- linux-2.4.29.orig/mm/Makefile      2005-05-03 21:08:06.000000000 +0300
++++ linux-2.4.29/mm/Makefile   2005-05-03 22:28:15.000000000 +0300
+@@ -17,5 +17,6 @@
+           shmem.o
+ obj-$(CONFIG_HIGHMEM) += highmem.o
++obj-$(CONFIG_PROC_MM) += proc_mm.o
+ include $(TOPDIR)/Rules.make
+Index: linux-2.4.29/mm/mmap.c
+===================================================================
+--- linux-2.4.29.orig/mm/mmap.c        2005-05-03 21:07:59.000000000 +0300
++++ linux-2.4.29/mm/mmap.c     2005-05-03 22:28:15.000000000 +0300
+@@ -391,10 +391,11 @@
+       return 0;
+ }
+-unsigned long do_mmap_pgoff(struct file * file, unsigned long addr, unsigned long len,
+-      unsigned long prot, unsigned long flags, unsigned long pgoff)
++unsigned long do_mmap_pgoff(struct mm_struct *mm, struct file * file, 
++                          unsigned long addr, unsigned long len,
++                          unsigned long prot, unsigned long flags, 
++                          unsigned long pgoff)
+ {
+-      struct mm_struct * mm = current->mm;
+       struct vm_area_struct * vma, * prev;
+       unsigned int vm_flags;
+       int correct_wcount = 0;
+@@ -1000,6 +1001,11 @@
+               remove_shared_vm_struct(mpnt);
+               mm->map_count--;
++              if((mpnt->vm_file != NULL) && (mpnt->vm_file->f_op != NULL) &&
++                 (mpnt->vm_file->f_op->munmap != NULL))
++                      mpnt->vm_file->f_op->munmap(mpnt->vm_file, mpnt, st, 
++                                                  size);
++
+               zap_page_range(mm, st, size);
+               /*
+Index: linux-2.4.29/mm/mprotect.c
+===================================================================
+--- linux-2.4.29.orig/mm/mprotect.c    2005-05-03 21:06:44.000000000 +0300
++++ linux-2.4.29/mm/mprotect.c 2005-05-03 22:28:15.000000000 +0300
+@@ -264,7 +264,8 @@
+       return 0;
+ }
+-asmlinkage long sys_mprotect(unsigned long start, size_t len, unsigned long prot)
++long do_mprotect(struct mm_struct *mm, unsigned long start, size_t len, 
++               unsigned long prot)
+ {
+       unsigned long nstart, end, tmp;
+       struct vm_area_struct * vma, * next, * prev;
+@@ -281,9 +282,9 @@
+       if (end == start)
+               return 0;
+-      down_write(&current->mm->mmap_sem);
++      down_write(&mm->mmap_sem);
+-      vma = find_vma_prev(current->mm, start, &prev);
++      vma = find_vma_prev(mm, start, &prev);
+       error = -ENOMEM;
+       if (!vma || vma->vm_start > start)
+               goto out;
+@@ -332,6 +333,11 @@
+               prev->vm_mm->map_count--;
+       }
+ out:
+-      up_write(&current->mm->mmap_sem);
++      up_write(&mm->mmap_sem);
+       return error;
+ }
++
++asmlinkage long sys_mprotect(unsigned long start, size_t len, unsigned long prot)
++{
++        return(do_mprotect(current->mm, start, len, prot));
++}
+Index: linux-2.4.29/mm/page_alloc.c
+===================================================================
+--- linux-2.4.29.orig/mm/page_alloc.c  2005-05-03 21:05:16.000000000 +0300
++++ linux-2.4.29/mm/page_alloc.c       2005-05-03 22:28:15.000000000 +0300
+@@ -116,6 +116,7 @@
+       struct page *base;
+       zone_t *zone;
++      arch_free_page(page, order);
+       /*
+        * Yes, think what happens when other parts of the kernel take 
+        * a reference to a page in order to pin it for io. -ben
+Index: linux-2.4.29/mm/proc_mm.c
+===================================================================
+--- linux-2.4.29.orig/mm/proc_mm.c     1970-01-01 03:00:00.000000000 +0300
++++ linux-2.4.29/mm/proc_mm.c  2005-05-03 22:28:15.000000000 +0300
+@@ -0,0 +1,173 @@
++/* 
++ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
++ * Licensed under the GPL
++ */
++
++#include "linux/init.h"
++#include "linux/proc_fs.h"
++#include "linux/proc_mm.h"
++#include "linux/file.h"
++#include "asm/uaccess.h"
++#include "asm/mmu_context.h"
++
++static struct file_operations proc_mm_fops;
++
++struct mm_struct *proc_mm_get_mm(int fd)
++{
++      struct mm_struct *ret = ERR_PTR(-EBADF);
++      struct file *file;
++
++      file = fget(fd);
++      if (!file)
++              goto out;
++
++      ret = ERR_PTR(-EINVAL);
++      if(file->f_op != &proc_mm_fops)
++              goto out_fput;
++
++      ret = file->private_data;
++ out_fput:
++      fput(file);
++ out:
++      return(ret);
++}
++
++extern long do_mmap2(struct mm_struct *mm, unsigned long addr, 
++                   unsigned long len, unsigned long prot, 
++                   unsigned long flags, unsigned long fd,
++                   unsigned long pgoff);
++
++static ssize_t write_proc_mm(struct file *file, const char *buffer,
++                           size_t count, loff_t *ppos)
++{
++      struct mm_struct *mm = file->private_data;
++      struct proc_mm_op req;
++      int n, ret;
++
++      if(count > sizeof(req))
++              return(-EINVAL);
++
++      n = copy_from_user(&req, buffer, count);
++      if(n != 0)
++              return(-EFAULT);
++
++      ret = count;
++      switch(req.op){
++      case MM_MMAP: {
++              struct mm_mmap *map = &req.u.mmap;
++
++              ret = do_mmap2(mm, map->addr, map->len, map->prot, 
++                             map->flags, map->fd, map->offset >> PAGE_SHIFT);
++              if((ret & ~PAGE_MASK) == 0)
++                      ret = count;
++      
++              break;
++      }
++      case MM_MUNMAP: {
++              struct mm_munmap *unmap = &req.u.munmap;
++
++              down_write(&mm->mmap_sem);
++              ret = do_munmap(mm, unmap->addr, unmap->len);
++              up_write(&mm->mmap_sem);
++
++              if(ret == 0)
++                      ret = count;
++              break;
++      }
++      case MM_MPROTECT: {
++              struct mm_mprotect *protect = &req.u.mprotect;
++
++              ret = do_mprotect(mm, protect->addr, protect->len, 
++                                protect->prot);
++              if(ret == 0)
++                      ret = count;
++              break;
++      }
++
++      case MM_COPY_SEGMENTS: {
++              struct mm_struct *from = proc_mm_get_mm(req.u.copy_segments);
++
++              if(IS_ERR(from)){
++                      ret = PTR_ERR(from);
++                      break;
++              }
++
++              mm_copy_segments(from, mm);
++              break;
++      }
++      default:
++              ret = -EINVAL;
++              break;
++      }
++
++      return(ret);
++}
++
++static int open_proc_mm(struct inode *inode, struct file *file)
++{
++      struct mm_struct *mm = mm_alloc();
++      int ret;
++
++      ret = -ENOMEM;
++      if(mm == NULL)
++              goto out_mem;
++
++      ret = init_new_context(current, mm);
++      if(ret)
++              goto out_free;
++
++      spin_lock(&mmlist_lock);
++      list_add(&mm->mmlist, &current->mm->mmlist);
++      mmlist_nr++;
++      spin_unlock(&mmlist_lock);
++
++      file->private_data = mm;
++
++      return(0);
++
++ out_free:
++      mmput(mm);
++ out_mem:
++      return(ret);
++}
++
++static int release_proc_mm(struct inode *inode, struct file *file)
++{
++      struct mm_struct *mm = file->private_data;
++
++      mmput(mm);
++      return(0);
++}
++
++static struct file_operations proc_mm_fops = {
++      .open           = open_proc_mm,
++      .release        = release_proc_mm,
++      .write          = write_proc_mm,
++};
++
++static int make_proc_mm(void)
++{
++      struct proc_dir_entry *ent;
++
++      ent = create_proc_entry("mm", 0222, &proc_root);
++      if(ent == NULL){
++              printk("make_proc_mm : Failed to register /proc/mm\n");
++              return(0);
++      }
++      ent->proc_fops = &proc_mm_fops;
++
++      return(0);
++}
++
++__initcall(make_proc_mm);
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * Emacs will notice this stuff at the end of the file and automatically
++ * adjust the settings for this buffer only.  This must remain at the end
++ * of the file.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ */
+Index: linux-2.4.29/mm/shmem.c
+===================================================================
+--- linux-2.4.29.orig/mm/shmem.c       2005-05-03 21:06:51.000000000 +0300
++++ linux-2.4.29/mm/shmem.c    2005-05-03 22:28:15.000000000 +0300
+@@ -128,16 +128,17 @@
+  *                   +-> 48-51
+  *                   +-> 52-55
+  */
+-static swp_entry_t *shmem_swp_entry(struct shmem_inode_info *info, unsigned long index, unsigned long *page)
++static void *shmem_block(unsigned long index, unsigned long *page,
++                       unsigned long *direct, void ***indirect)
+ {
+       unsigned long offset;
+       void **dir;
+       if (index < SHMEM_NR_DIRECT)
+-              return info->i_direct+index;
+-      if (!info->i_indirect) {
++              return direct+index;
++      if (!*indirect) {
+               if (page) {
+-                      info->i_indirect = (void **) *page;
++                      *indirect = (void **) *page;
+                       *page = 0;
+               }
+               return NULL;                    /* need another page */
+@@ -146,7 +147,7 @@
+       index -= SHMEM_NR_DIRECT;
+       offset = index % ENTRIES_PER_PAGE;
+       index /= ENTRIES_PER_PAGE;
+-      dir = info->i_indirect;
++      dir = *indirect;
+       if (index >= ENTRIES_PER_PAGE/2) {
+               index -= ENTRIES_PER_PAGE/2;
+@@ -169,7 +170,21 @@
+               *dir = (void *) *page;
+               *page = 0;
+       }
+-      return (swp_entry_t *) *dir + offset;
++      return (unsigned long **) *dir + offset;
++}
++
++static swp_entry_t *shmem_swp_entry(struct shmem_inode_info *info, unsigned long index, unsigned long *page)
++{
++      return((swp_entry_t *) shmem_block(index, page, 
++                                         (unsigned long *) info->i_direct, 
++                                         &info->i_indirect));
++}
++
++static unsigned long *shmem_map_count(struct shmem_inode_info *info, 
++                                    unsigned long index, unsigned long *page)
++{
++      return((unsigned long *) shmem_block(index, page, info->map_direct, 
++                                           &info->map_indirect));
+ }
+ /*
+@@ -847,6 +862,7 @@
+       ops = &shmem_vm_ops;
+       if (!S_ISREG(inode->i_mode))
+               return -EACCES;
++
+       UPDATE_ATIME(inode);
+       vma->vm_ops = ops;
+       return 0;
+@@ -1750,4 +1766,125 @@
+       return 0;
+ }
++static int adjust_map_counts(struct shmem_inode_info *info, 
++                           unsigned long offset, unsigned long len, 
++                           int adjust)
++{
++      unsigned long idx, i, *count, page = 0;
++
++      spin_lock(&info->lock);
++      offset >>= PAGE_SHIFT;
++      len >>= PAGE_SHIFT;
++      for(i = 0; i < len; i++){
++              idx = (i + offset) >> (PAGE_CACHE_SHIFT - PAGE_SHIFT);
++
++              while((count = shmem_map_count(info, idx, &page)) == NULL){
++                      spin_unlock(&info->lock);
++                      page = get_zeroed_page(GFP_KERNEL);
++                      if(page == 0)
++                              return(-ENOMEM);
++                      spin_lock(&info->lock);
++              }
++
++              if(page != 0)
++                      free_page(page);
++
++              *count += adjust;
++      }
++      spin_unlock(&info->lock);
++      return(0);
++}
++
+ EXPORT_SYMBOL(shmem_file_setup);
++
++struct file_operations anon_file_operations;
++
++static int anon_mmap(struct file *file, struct vm_area_struct *vma)
++{
++        struct file *new;
++      struct inode *inode;
++      loff_t size = vma->vm_end - vma->vm_start;
++      int err;
++
++      if(file->private_data == NULL){
++              new = shmem_file_setup("dev/anon", size);
++              if(IS_ERR(new))
++                      return(PTR_ERR(new));
++
++              new->f_op = &anon_file_operations;
++              file->private_data = new;
++      }
++      
++      if (vma->vm_file)
++              fput(vma->vm_file);
++      vma->vm_file = file->private_data;
++      get_file(vma->vm_file);
++
++      inode = vma->vm_file->f_dentry->d_inode;
++      err = adjust_map_counts(SHMEM_I(inode), vma->vm_pgoff, size, 1);
++      if(err)
++              return(err);
++
++      vma->vm_ops = &shmem_vm_ops;
++      return 0;
++}
++
++static void anon_munmap(struct file *file, struct vm_area_struct *vma, 
++                      unsigned long start, unsigned long len)
++{
++      struct inode *inode = file->f_dentry->d_inode;
++      struct shmem_inode_info *info = SHMEM_I(inode);
++      pgd_t *pgd;
++      pmd_t *pmd;
++      pte_t *pte;
++      struct page *page;
++      unsigned long addr, idx, *count;
++
++      for(addr = start; addr < start + len; addr += PAGE_SIZE){
++              idx = (addr - vma->vm_start + vma->vm_pgoff);
++              idx >>= PAGE_CACHE_SHIFT;
++
++              count = shmem_map_count(info, idx, NULL);
++              BUG_ON(count == NULL);
++
++              (*count)--;
++              if(*count > 0)
++                      continue;
++
++              pgd = pgd_offset(vma->vm_mm, addr);
++              if(pgd_none(*pgd))
++                      continue;
++
++              pmd = pmd_offset(pgd, addr);
++              if(pmd_none(*pmd))
++                      continue;
++
++              pte = pte_offset(pmd, addr);
++              if(!pte_present(*pte)) /* XXX need to handle swapped pages */
++                      continue;
++
++              *pte = pte_mkclean(*pte);
++
++              page = pte_page(*pte);
++              LockPage(page);
++              lru_cache_del(page);
++              ClearPageDirty(page);
++              remove_inode_page(page);
++              UnlockPage(page);
++
++              page_cache_release(page);
++      }
++}
++
++int anon_release(struct inode *inode, struct file *file)
++{
++      if(file->private_data != NULL)
++              fput(file->private_data);
++      return(0);
++}
++
++struct file_operations anon_file_operations = {
++      .mmap           = anon_mmap,
++      .munmap         = anon_munmap,
++      .release        = anon_release,
++};
index 1da088c..1055dee 100644 (file)
@@ -14,8 +14,8 @@
 
 Index: linux-2.4.21/fs/dcache.c
 ===================================================================
---- linux-2.4.21.orig/fs/dcache.c      2004-09-11 10:16:31.000000000 -0400
-+++ linux-2.4.21/fs/dcache.c   2004-09-16 19:32:11.000000000 -0400
+--- linux-2.4.21.orig/fs/dcache.c      2005-06-01 22:51:55.000000000 -0400
++++ linux-2.4.21/fs/dcache.c   2005-06-01 22:58:09.043064136 -0400
 @@ -187,6 +187,13 @@
                spin_unlock(&dcache_lock);
                return 0;
@@ -55,8 +55,8 @@ Index: linux-2.4.21/fs/dcache.c
  #define do_switch(x,y) do { \
 Index: linux-2.4.21/fs/exec.c
 ===================================================================
---- linux-2.4.21.orig/fs/exec.c        2004-09-11 10:16:39.000000000 -0400
-+++ linux-2.4.21/fs/exec.c     2004-09-16 19:32:11.000000000 -0400
+--- linux-2.4.21.orig/fs/exec.c        2005-06-01 22:52:05.000000000 -0400
++++ linux-2.4.21/fs/exec.c     2005-06-01 22:58:09.044063984 -0400
 @@ -116,8 +116,10 @@
        struct file * file;
        struct nameidata nd;
@@ -79,7 +79,7 @@ Index: linux-2.4.21/fs/exec.c
        error = PTR_ERR(file);
        if (IS_ERR(file))
                goto out;
-@@ -401,8 +404,10 @@
+@@ -405,8 +408,10 @@
        struct inode *inode;
        struct file *file;
        int err = 0;
@@ -91,7 +91,7 @@ Index: linux-2.4.21/fs/exec.c
        file = ERR_PTR(err);
        if (!err) {
                inode = nd.dentry->d_inode;
-@@ -414,7 +419,8 @@
+@@ -418,7 +423,8 @@
                                err = -EACCES;
                        file = ERR_PTR(err);
                        if (!err) {
@@ -101,7 +101,7 @@ Index: linux-2.4.21/fs/exec.c
                                if (!IS_ERR(file)) {
                                        err = deny_write_access(file);
                                        if (err) {
-@@ -426,6 +432,7 @@
+@@ -430,6 +436,7 @@
                                return file;
                        }
                }
@@ -109,7 +109,7 @@ Index: linux-2.4.21/fs/exec.c
                path_release(&nd);
        }
        goto out;
-@@ -1355,7 +1362,7 @@
+@@ -1368,7 +1375,7 @@
                goto close_fail;
        if (!file->f_op->write)
                goto close_fail;
@@ -120,8 +120,8 @@ Index: linux-2.4.21/fs/exec.c
        retval = binfmt->core_dump(signr, regs, file);
 Index: linux-2.4.21/fs/namei.c
 ===================================================================
---- linux-2.4.21.orig/fs/namei.c       2004-09-11 10:16:29.000000000 -0400
-+++ linux-2.4.21/fs/namei.c    2004-09-16 19:34:57.000000000 -0400
+--- linux-2.4.21.orig/fs/namei.c       2005-06-01 22:52:05.000000000 -0400
++++ linux-2.4.21/fs/namei.c    2005-06-01 23:01:30.065504080 -0400
 @@ -94,6 +94,13 @@
   * XEmacs seems to be relying on it...
   */
@@ -248,16 +248,16 @@ Index: linux-2.4.21/fs/namei.c
                        err = PTR_ERR(dentry);
                        if (IS_ERR(dentry))
                                break;
-@@ -547,7 +583,7 @@
-                       goto out_dput;
+@@ -548,7 +584,7 @@
  
                if (inode->i_op->follow_link) {
+                       struct vfsmount *mnt = mntget(nd->mnt);
 -                      err = do_follow_link(dentry, nd);
 +                      err = do_follow_link(dentry, nd, NULL);
                        dput(dentry);
+                       mntput(mnt);
                        if (err)
-                               goto return_err;
-@@ -563,7 +599,7 @@
+@@ -565,7 +601,7 @@
                        nd->dentry = dentry;
                }
                err = -ENOTDIR; 
@@ -266,7 +266,7 @@ Index: linux-2.4.21/fs/namei.c
                        break;
                continue;
                /* here ends the main loop */
-@@ -590,12 +626,12 @@
+@@ -592,12 +628,12 @@
                        if (err < 0)
                                break;
                }
@@ -281,16 +281,16 @@ Index: linux-2.4.21/fs/namei.c
                        err = PTR_ERR(dentry);
                        if (IS_ERR(dentry))
                                break;
-@@ -605,7 +641,7 @@
-               inode = dentry->d_inode;
+@@ -608,7 +644,7 @@
                if ((lookup_flags & LOOKUP_FOLLOW)
                    && inode && inode->i_op && inode->i_op->follow_link) {
+                       struct vfsmount *mnt = mntget(nd->mnt);
 -                      err = do_follow_link(dentry, nd);
 +                      err = do_follow_link(dentry, nd, it);
                        dput(dentry);
+                       mntput(mnt);
                        if (err)
-                               goto return_err;
-@@ -619,7 +655,8 @@
+@@ -623,7 +659,8 @@
                        goto no_inode;
                if (lookup_flags & LOOKUP_DIRECTORY) {
                        err = -ENOTDIR; 
@@ -300,7 +300,7 @@ Index: linux-2.4.21/fs/namei.c
                                break;
                }
                goto return_base;
-@@ -637,12 +672,42 @@ return_reval:
+@@ -641,12 +678,42 @@
                        nd->last_type = LAST_DOT;
                else if (this.len == 2 && this.name[1] == '.')
                        nd->last_type = LAST_DOTDOT;
@@ -343,7 +343,7 @@ Index: linux-2.4.21/fs/namei.c
                if (dentry && dentry->d_op && dentry->d_op->d_revalidate) {
                        err = -ESTALE;
                        if (!dentry->d_op->d_revalidate(dentry, 0)) {
-@@ -656,15 +721,28 @@
+@@ -660,15 +727,28 @@
                dput(dentry);
                break;
        }
@@ -373,7 +373,7 @@ Index: linux-2.4.21/fs/namei.c
  }
  
  /* SMP-safe */
-@@ -749,6 +827,17 @@
+@@ -753,6 +833,17 @@
  }
  
  /* SMP-safe */
@@ -391,7 +391,7 @@ Index: linux-2.4.21/fs/namei.c
  int path_lookup(const char *path, unsigned flags, struct nameidata *nd)
  {
        int error = 0;
-@@ -763,6 +852,7 @@
+@@ -767,6 +858,7 @@
  {
        nd->last_type = LAST_ROOT; /* if there are only slashes... */
        nd->flags = flags;
@@ -399,7 +399,7 @@ Index: linux-2.4.21/fs/namei.c
        if (*name=='/')
                return walk_init_root(name,nd);
        read_lock(&current->fs->lock);
-@@ -777,7 +867,8 @@
+@@ -781,7 +873,8 @@
   * needs parent already locked. Doesn't follow mounts.
   * SMP-safe.
   */
@@ -409,7 +409,7 @@ Index: linux-2.4.21/fs/namei.c
  {
        struct dentry * dentry;
        struct inode *inode;
-@@ -800,13 +891,16 @@
+@@ -804,13 +897,16 @@
                        goto out;
        }
  
@@ -427,7 +427,7 @@ Index: linux-2.4.21/fs/namei.c
                dentry = inode->i_op->lookup(inode, new);
                unlock_kernel();
                if (!dentry)
-@@ -818,6 +912,12 @@
+@@ -822,6 +918,12 @@
        return dentry;
  }
  
@@ -440,7 +440,7 @@ Index: linux-2.4.21/fs/namei.c
  /* SMP-safe */
  struct dentry * lookup_one_len(const char * name, struct dentry * base, int len)
  {
-@@ -839,7 +939,7 @@
+@@ -843,7 +945,7 @@
        }
        this.hash = end_name_hash(hash);
  
@@ -449,7 +449,7 @@ Index: linux-2.4.21/fs/namei.c
  access:
        return ERR_PTR(-EACCES);
  }
-@@ -870,6 +970,23 @@
+@@ -874,6 +976,23 @@
        return err;
  }
  
@@ -473,7 +473,7 @@ Index: linux-2.4.21/fs/namei.c
  /*
   * It's inline, so penalty for filesystems that don't use sticky bit is
   * minimal.
-@@ -967,7 +1084,8 @@
+@@ -971,7 +1090,8 @@
        return retval;
  }
  
@@ -483,7 +483,7 @@ Index: linux-2.4.21/fs/namei.c
  {
        int error;
  
-@@ -980,12 +1098,15 @@
+@@ -984,12 +1104,15 @@
                goto exit_lock;
  
        error = -EACCES;        /* shouldn't it be ENOSYS? */
@@ -501,7 +501,7 @@ Index: linux-2.4.21/fs/namei.c
        unlock_kernel();
  exit_lock:
        up(&dir->i_zombie);
-@@ -994,6 +1115,11 @@
+@@ -998,6 +1121,11 @@
        return error;
  }
  
@@ -513,7 +513,7 @@ Index: linux-2.4.21/fs/namei.c
  /*
   *    open_namei()
   *
-@@ -1008,7 +1134,8 @@
+@@ -1012,7 +1140,8 @@
   * for symlinks (where the permissions are checked later).
   * SMP-safe
   */
@@ -523,7 +523,7 @@ Index: linux-2.4.21/fs/namei.c
  {
        int acc_mode, error = 0;
        struct inode *inode;
-@@ -1018,11 +1145,14 @@
+@@ -1023,11 +1152,14 @@
  
        acc_mode = ACC_MODE(flag);
  
@@ -539,7 +539,7 @@ Index: linux-2.4.21/fs/namei.c
                if (error)
                        return error;
                dentry = nd->dentry;
-@@ -1032,6 +1162,10 @@
+@@ -1037,6 +1169,10 @@
        /*
         * Create - we need to know the parent.
         */
@@ -550,7 +550,7 @@ Index: linux-2.4.21/fs/namei.c
        error = path_lookup(pathname, LOOKUP_PARENT, nd);
        if (error)
                return error;
-@@ -1047,7 +1181,7 @@
+@@ -1052,7 +1188,7 @@
  
        dir = nd->dentry;
        down(&dir->d_inode->i_sem);
@@ -559,7 +559,7 @@ Index: linux-2.4.21/fs/namei.c
  
  do_last:
        error = PTR_ERR(dentry);
-@@ -1056,11 +1190,12 @@
+@@ -1061,11 +1197,12 @@
                goto exit;
        }
  
@@ -573,7 +573,7 @@ Index: linux-2.4.21/fs/namei.c
                up(&dir->d_inode->i_sem);
                dput(nd->dentry);
                nd->dentry = dentry;
-@@ -1164,7 +1299,7 @@
+@@ -1169,7 +1306,7 @@
                if (!error) {
                        DQUOT_INIT(inode);
                        
@@ -582,7 +582,7 @@ Index: linux-2.4.21/fs/namei.c
                }
                put_write_access(inode);
                if (error)
-@@ -1176,8 +1311,10 @@
+@@ -1181,8 +1318,10 @@
        return 0;
  
  exit_dput:
@@ -593,18 +593,18 @@ Index: linux-2.4.21/fs/namei.c
        path_release(nd);
        return error;
  
-@@ -1196,7 +1333,10 @@
-        * are done. Procfs-like symlinks just set LAST_BIND.
+@@ -1202,7 +1341,10 @@
         */
        UPDATE_ATIME(dentry->d_inode);
+       mnt = mntget(nd->mnt);
 +      nd->intent = it;
        error = dentry->d_inode->i_op->follow_link(dentry, nd);
 +      if (error)
 +              intent_release(it);
        dput(dentry);
+       mntput(mnt);
        if (error)
-               return error;
-@@ -1218,13 +1358,20 @@
+@@ -1225,13 +1367,20 @@
        }
        dir = nd->dentry;
        down(&dir->d_inode->i_sem);
@@ -627,7 +627,7 @@ Index: linux-2.4.21/fs/namei.c
  {
        struct dentry *dentry;
  
-@@ -1232,7 +1379,7 @@
+@@ -1239,7 +1388,7 @@
        dentry = ERR_PTR(-EEXIST);
        if (nd->last_type != LAST_NORM)
                goto fail;
@@ -636,7 +636,7 @@ Index: linux-2.4.21/fs/namei.c
        if (IS_ERR(dentry))
                goto fail;
        if (!is_dir && nd->last.name[nd->last.len] && !dentry->d_inode)
-@@ -1288,7 +1435,20 @@
+@@ -1295,7 +1444,20 @@
        error = path_lookup(tmp, LOOKUP_PARENT, &nd);
        if (error)
                goto out;
@@ -658,7 +658,7 @@ Index: linux-2.4.21/fs/namei.c
        error = PTR_ERR(dentry);
  
        if (!IS_POSIXACL(nd.dentry->d_inode))
-@@ -1310,6 +1470,7 @@
+@@ -1317,6 +1479,7 @@
                dput(dentry);
        }
        up(&nd.dentry->d_inode->i_sem);
@@ -666,7 +666,7 @@ Index: linux-2.4.21/fs/namei.c
        path_release(&nd);
  out:
        putname(tmp);
-@@ -1357,7 +1518,18 @@
+@@ -1364,7 +1527,18 @@
                error = path_lookup(tmp, LOOKUP_PARENT, &nd);
                if (error)
                        goto out;
@@ -686,7 +686,7 @@ Index: linux-2.4.21/fs/namei.c
                error = PTR_ERR(dentry);
                if (!IS_ERR(dentry)) {
                        if (!IS_POSIXACL(nd.dentry->d_inode))
-@@ -1366,6 +1538,7 @@
+@@ -1373,6 +1547,7 @@
                        dput(dentry);
                }
                up(&nd.dentry->d_inode->i_sem);
@@ -694,7 +694,7 @@ Index: linux-2.4.21/fs/namei.c
                path_release(&nd);
  out:
                putname(tmp);
-@@ -1466,8 +1639,16 @@
+@@ -1473,8 +1648,16 @@
                        error = -EBUSY;
                        goto exit1;
        }
@@ -712,7 +712,7 @@ Index: linux-2.4.21/fs/namei.c
        error = PTR_ERR(dentry);
        if (!IS_ERR(dentry)) {
                error = vfs_rmdir(nd.dentry->d_inode, dentry);
-@@ -1525,8 +1706,15 @@
+@@ -1532,8 +1715,15 @@
        error = -EISDIR;
        if (nd.last_type != LAST_NORM)
                goto exit1;
@@ -729,7 +729,7 @@ Index: linux-2.4.21/fs/namei.c
        error = PTR_ERR(dentry);
        if (!IS_ERR(dentry)) {
                /* Why not before? Because we want correct error value */
-@@ -1593,15 +1781,27 @@
+@@ -1600,15 +1790,27 @@
                error = path_lookup(to, LOOKUP_PARENT, &nd);
                if (error)
                        goto out;
@@ -759,7 +759,7 @@ Index: linux-2.4.21/fs/namei.c
                putname(to);
        }
        putname(from);
-@@ -1677,7 +1877,18 @@
+@@ -1684,7 +1886,18 @@
                error = -EXDEV;
                if (old_nd.mnt != nd.mnt)
                        goto out_release;
@@ -779,7 +779,7 @@ Index: linux-2.4.21/fs/namei.c
                error = PTR_ERR(new_dentry);
                if (!IS_ERR(new_dentry)) {
                        error = vfs_link(old_nd.dentry, nd.dentry->d_inode, new_dentry);
-@@ -1721,7 +1932,7 @@
+@@ -1728,7 +1941,7 @@
   *       locking].
   */
  int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry,
@@ -788,7 +788,7 @@ Index: linux-2.4.21/fs/namei.c
  {
        int error;
        struct inode *target;
-@@ -1800,7 +2011,7 @@
+@@ -1807,7 +2020,7 @@
  }
  
  int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry,
@@ -797,7 +797,7 @@ Index: linux-2.4.21/fs/namei.c
  {
        int error;
  
-@@ -1888,9 +2099,18 @@
+@@ -1895,9 +2108,18 @@
        if (newnd.last_type != LAST_NORM)
                goto exit2;
  
@@ -817,7 +817,7 @@ Index: linux-2.4.21/fs/namei.c
        error = PTR_ERR(old_dentry);
        if (IS_ERR(old_dentry))
                goto exit3;
-@@ -1906,16 +2126,16 @@
+@@ -1913,16 +2135,16 @@
                if (newnd.last.name[newnd.last.len])
                        goto exit4;
        }
@@ -836,7 +836,7 @@ Index: linux-2.4.21/fs/namei.c
        dput(new_dentry);
  exit4:
        dput(old_dentry);
-@@ -1966,20 +2186,26 @@
+@@ -1973,20 +2195,26 @@
  }
  
  static inline int
@@ -865,7 +865,7 @@ Index: linux-2.4.21/fs/namei.c
  out:
        if (current->link_count || res || nd->last_type!=LAST_NORM)
                return res;
-@@ -2003,7 +2229,13 @@
+@@ -2010,7 +2238,13 @@
  
  int vfs_follow_link(struct nameidata *nd, const char *link)
  {
@@ -880,7 +880,7 @@ Index: linux-2.4.21/fs/namei.c
  }
  
  /* get the link contents into pagecache */
-@@ -2045,7 +2277,7 @@
+@@ -2052,7 +2286,7 @@
  {
        struct page *page = NULL;
        char *s = page_getlink(dentry, &page);
@@ -891,8 +891,8 @@ Index: linux-2.4.21/fs/namei.c
                page_cache_release(page);
 Index: linux-2.4.21/fs/namespace.c
 ===================================================================
---- linux-2.4.21.orig/fs/namespace.c   2004-09-11 10:16:15.000000000 -0400
-+++ linux-2.4.21/fs/namespace.c        2004-09-16 19:32:11.000000000 -0400
+--- linux-2.4.21.orig/fs/namespace.c   2005-06-01 22:51:42.000000000 -0400
++++ linux-2.4.21/fs/namespace.c        2005-06-01 22:58:09.048063376 -0400
 @@ -98,6 +98,7 @@
  {
        old_nd->dentry = mnt->mnt_mountpoint;
@@ -1007,8 +1007,8 @@ Index: linux-2.4.21/fs/namespace.c
        unlock_kernel();
 Index: linux-2.4.21/fs/open.c
 ===================================================================
---- linux-2.4.21.orig/fs/open.c        2004-09-11 10:16:19.000000000 -0400
-+++ linux-2.4.21/fs/open.c     2004-09-16 19:32:11.000000000 -0400
+--- linux-2.4.21.orig/fs/open.c        2005-06-01 22:51:55.000000000 -0400
++++ linux-2.4.21/fs/open.c     2005-06-01 22:58:09.050063072 -0400
 @@ -19,6 +19,8 @@
  #include <asm/uaccess.h>
  
@@ -1018,7 +1018,7 @@ Index: linux-2.4.21/fs/open.c
  
  int vfs_statfs(struct super_block *sb, struct statfs *buf)
  {
-@@ -95,9 +97,10 @@
+@@ -169,9 +171,10 @@
        write_unlock(&files->file_lock);
  }
  
@@ -1030,7 +1030,7 @@ Index: linux-2.4.21/fs/open.c
        int error;
        struct iattr newattrs;
  
-@@ -109,7 +112,13 @@
+@@ -183,7 +186,13 @@
        down(&inode->i_sem);
        newattrs.ia_size = length;
        newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME;
@@ -1045,7 +1045,7 @@ Index: linux-2.4.21/fs/open.c
        up(&inode->i_sem);
        up_write(&inode->i_alloc_sem);
        return error;
-@@ -120,12 +129,13 @@
+@@ -194,12 +203,13 @@
        struct nameidata nd;
        struct inode * inode;
        int error;
@@ -1060,7 +1060,7 @@ Index: linux-2.4.21/fs/open.c
        if (error)
                goto out;
        inode = nd.dentry->d_inode;
-@@ -165,11 +175,13 @@
+@@ -239,11 +249,13 @@
        error = locks_verify_truncate(inode, NULL, length);
        if (!error) {
                DQUOT_INIT(inode);
@@ -1075,7 +1075,7 @@ Index: linux-2.4.21/fs/open.c
        path_release(&nd);
  out:
        return error;
-@@ -217,7 +229,7 @@
+@@ -291,7 +303,7 @@
  
        error = locks_verify_truncate(inode, file, length);
        if (!error)
@@ -1084,7 +1084,7 @@ Index: linux-2.4.21/fs/open.c
  out_putf:
        fput(file);
  out:
-@@ -262,11 +274,13 @@
+@@ -336,11 +348,13 @@
        struct inode * inode;
        struct iattr newattrs;
  
@@ -1099,7 +1099,7 @@ Index: linux-2.4.21/fs/open.c
        error = -EROFS;
        if (IS_RDONLY(inode))
                goto dput_and_out;
-@@ -281,11 +295,25 @@
+@@ -355,11 +369,25 @@
                        goto dput_and_out;
  
                newattrs.ia_valid |= ATTR_ATIME_SET | ATTR_MTIME_SET;
@@ -1126,7 +1126,7 @@ Index: linux-2.4.21/fs/open.c
        error = notify_change(nd.dentry, &newattrs);
  dput_and_out:
        path_release(&nd);
-@@ -306,12 +334,14 @@
+@@ -380,12 +408,14 @@
        struct inode * inode;
        struct iattr newattrs;
  
@@ -1142,7 +1142,7 @@ Index: linux-2.4.21/fs/open.c
        error = -EROFS;
        if (IS_RDONLY(inode))
                goto dput_and_out;
-@@ -326,7 +356,20 @@
+@@ -400,7 +430,20 @@
                newattrs.ia_atime = times[0].tv_sec;
                newattrs.ia_mtime = times[1].tv_sec;
                newattrs.ia_valid |= ATTR_ATIME_SET | ATTR_MTIME_SET;
@@ -1164,7 +1164,7 @@ Index: linux-2.4.21/fs/open.c
                if (current->fsuid != inode->i_uid &&
                    (error = permission(inode,MAY_WRITE)) != 0)
                        goto dput_and_out;
-@@ -349,6 +392,7 @@
+@@ -423,6 +466,7 @@
        int old_fsuid, old_fsgid;
        kernel_cap_t old_cap;
        int res;
@@ -1172,7 +1172,7 @@ Index: linux-2.4.21/fs/open.c
  
        if (mode & ~S_IRWXO)    /* where's F_OK, X_OK, W_OK, R_OK? */
                return -EINVAL;
-@@ -366,13 +410,14 @@
+@@ -440,13 +484,14 @@
        else
                current->cap_effective = current->cap_permitted;
  
@@ -1188,7 +1188,7 @@ Index: linux-2.4.21/fs/open.c
                path_release(&nd);
        }
  
-@@ -387,8 +432,9 @@
+@@ -461,8 +506,9 @@
  {
        int error;
        struct nameidata nd;
@@ -1199,7 +1199,7 @@ Index: linux-2.4.21/fs/open.c
        if (error)
                goto out;
  
-@@ -399,6 +445,7 @@
+@@ -473,6 +519,7 @@
        set_fs_pwd(current->fs, nd.mnt, nd.dentry);
  
  dput_and_out:
@@ -1207,7 +1207,7 @@ Index: linux-2.4.21/fs/open.c
        path_release(&nd);
  out:
        return error;
-@@ -438,9 +485,10 @@
+@@ -512,9 +559,10 @@
  {
        int error;
        struct nameidata nd;
@@ -1220,7 +1220,7 @@ Index: linux-2.4.21/fs/open.c
        if (error)
                goto out;
  
-@@ -456,39 +504,56 @@
+@@ -530,39 +578,56 @@
        set_fs_altroot();
        error = 0;
  dput_and_out:
@@ -1291,7 +1291,7 @@ Index: linux-2.4.21/fs/open.c
        fput(file);
  out:
        return err;
-@@ -497,30 +562,14 @@
+@@ -571,30 +636,14 @@
  asmlinkage long sys_chmod(const char * filename, mode_t mode)
  {
        struct nameidata nd;
@@ -1307,23 +1307,23 @@ Index: linux-2.4.21/fs/open.c
 -      error = -EROFS;
 -      if (IS_RDONLY(inode))
 -              goto dput_and_out;
+-
 -      error = -EPERM;
 -      if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
 -              goto dput_and_out;
-+      error = chmod_common(nd.dentry, mode);
  
 -      if (mode == (mode_t) -1)
 -              mode = inode->i_mode;
 -      newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
 -      newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
 -      error = notify_change(nd.dentry, &newattrs);
--
++      error = chmod_common(nd.dentry, mode);
 -dput_and_out:
        path_release(&nd);
  out:
        return error;
-@@ -540,6 +589,20 @@
+@@ -614,6 +663,20 @@
        error = -EROFS;
        if (IS_RDONLY(inode))
                goto out;
@@ -1344,7 +1344,7 @@ Index: linux-2.4.21/fs/open.c
        error = -EPERM;
        if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
                goto out;
-@@ -644,6 +707,7 @@
+@@ -718,6 +781,7 @@
  {
        int namei_flags, error;
        struct nameidata nd;
@@ -1352,7 +1352,7 @@ Index: linux-2.4.21/fs/open.c
  
        namei_flags = flags;
        if ((namei_flags+1) & O_ACCMODE)
-@@ -651,14 +715,15 @@
+@@ -725,14 +789,15 @@
        if (namei_flags & O_TRUNC)
                namei_flags |= 2;
  
@@ -1373,7 +1373,7 @@ Index: linux-2.4.21/fs/open.c
  {
        struct file * f;
        struct inode *inode;
-@@ -695,7 +760,9 @@
+@@ -769,7 +834,9 @@
        }
  
        if (f->f_op && f->f_op->open) {
@@ -1383,7 +1383,7 @@ Index: linux-2.4.21/fs/open.c
                if (error)
                        goto cleanup_all;
        }
-@@ -711,6 +778,7 @@
+@@ -785,6 +852,7 @@
                }
        }
  
@@ -1391,7 +1391,7 @@ Index: linux-2.4.21/fs/open.c
        return f;
  
  cleanup_all:
-@@ -725,11 +793,17 @@
+@@ -799,11 +867,17 @@
  cleanup_file:
        put_filp(f);
  cleanup_dentry:
@@ -1411,8 +1411,8 @@ Index: linux-2.4.21/fs/open.c
   */
 Index: linux-2.4.21/fs/stat.c
 ===================================================================
---- linux-2.4.21.orig/fs/stat.c        2004-09-11 10:16:30.000000000 -0400
-+++ linux-2.4.21/fs/stat.c     2004-09-16 19:38:49.000000000 -0400
+--- linux-2.4.21.orig/fs/stat.c        2005-06-01 22:51:55.000000000 -0400
++++ linux-2.4.21/fs/stat.c     2005-06-01 22:58:09.051062920 -0400
 @@ -17,10 +17,12 @@
   * Revalidate the inode. This is required for proper NFS attribute caching.
   */
@@ -1580,8 +1580,8 @@ Index: linux-2.4.21/fs/stat.c
                fput(f);
 Index: linux-2.4.21/include/linux/dcache.h
 ===================================================================
---- linux-2.4.21.orig/include/linux/dcache.h   2004-09-11 10:16:39.000000000 -0400
-+++ linux-2.4.21/include/linux/dcache.h        2004-09-16 19:32:11.000000000 -0400
+--- linux-2.4.21.orig/include/linux/dcache.h   2005-06-01 22:52:05.000000000 -0400
++++ linux-2.4.21/include/linux/dcache.h        2005-06-01 22:58:09.051062920 -0400
 @@ -6,6 +6,51 @@
  #include <asm/atomic.h>
  #include <linux/mount.h>
@@ -1667,8 +1667,8 @@ Index: linux-2.4.21/include/linux/dcache.h
  
 Index: linux-2.4.21/include/linux/fs.h
 ===================================================================
---- linux-2.4.21.orig/include/linux/fs.h       2004-09-16 19:21:00.000000000 -0400
-+++ linux-2.4.21/include/linux/fs.h    2004-09-16 19:32:11.000000000 -0400
+--- linux-2.4.21.orig/include/linux/fs.h       2005-06-01 22:57:59.887456000 -0400
++++ linux-2.4.21/include/linux/fs.h    2005-06-01 22:58:09.053062616 -0400
 @@ -73,6 +73,7 @@
  
  #define FMODE_READ 1
@@ -1677,7 +1677,7 @@ Index: linux-2.4.21/include/linux/fs.h
  
  #define READ 0
  #define WRITE 1
-@@ -362,6 +363,9 @@
+@@ -365,6 +366,9 @@
  #define ATTR_MTIME_SET        256
  #define ATTR_FORCE    512     /* Not a change, but a change it */
  #define ATTR_ATTR_FLAG        1024
@@ -1687,7 +1687,7 @@ Index: linux-2.4.21/include/linux/fs.h
  
  /*
   * This is the Inode Attributes structure, used for notify_change().  It
-@@ -499,6 +503,7 @@
+@@ -502,6 +506,7 @@
        struct pipe_inode_info  *i_pipe;
        struct block_device     *i_bdev;
        struct char_device      *i_cdev;
@@ -1695,7 +1695,7 @@ Index: linux-2.4.21/include/linux/fs.h
  
        unsigned long           i_dnotify_mask; /* Directory notify events */
        struct dnotify_struct   *i_dnotify; /* for directory notifications */
-@@ -601,6 +606,7 @@
+@@ -604,6 +609,7 @@
  
        /* needed for tty driver, and maybe others */
        void                    *private_data;
@@ -1703,7 +1703,7 @@ Index: linux-2.4.21/include/linux/fs.h
  
        /* preallocated helper kiobuf to speedup O_DIRECT */
        struct kiobuf           *f_iobuf;
-@@ -729,6 +735,7 @@
+@@ -736,6 +742,7 @@
        struct qstr last;
        unsigned int flags;
        int last_type;
@@ -1711,7 +1711,7 @@ Index: linux-2.4.21/include/linux/fs.h
  };
  
  /*
-@@ -849,7 +856,8 @@
+@@ -856,7 +863,8 @@
  extern int vfs_link(struct dentry *, struct inode *, struct dentry *);
  extern int vfs_rmdir(struct inode *, struct dentry *);
  extern int vfs_unlink(struct inode *, struct dentry *);
@@ -1721,7 +1721,7 @@ Index: linux-2.4.21/include/linux/fs.h
  
  /*
   * File types
-@@ -928,21 +936,32 @@
+@@ -935,21 +943,32 @@
  
  struct inode_operations {
        int (*create) (struct inode *,struct dentry *,int);
@@ -1754,7 +1754,7 @@ Index: linux-2.4.21/include/linux/fs.h
        int (*getattr) (struct dentry *, struct iattr *);
        int (*setxattr) (struct dentry *, const char *, const void *, size_t, int);
        ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
-@@ -1139,10 +1158,14 @@
+@@ -1151,10 +1170,14 @@
  
  asmlinkage long sys_open(const char *, int, int);
  asmlinkage long sys_close(unsigned int);      /* yes, it's really unsigned */
@@ -1770,7 +1770,7 @@ Index: linux-2.4.21/include/linux/fs.h
  extern int filp_close(struct file *, fl_owner_t id);
  extern char * getname(const char *);
  
-@@ -1434,6 +1457,7 @@
+@@ -1446,6 +1469,7 @@
  extern loff_t default_llseek(struct file *file, loff_t offset, int origin);
  
  extern int FASTCALL(__user_walk(const char *, unsigned, struct nameidata *));
@@ -1778,7 +1778,7 @@ Index: linux-2.4.21/include/linux/fs.h
  extern int FASTCALL(path_init(const char *, unsigned, struct nameidata *));
  extern int FASTCALL(path_walk(const char *, struct nameidata *));
  extern int FASTCALL(path_lookup(const char *, unsigned, struct nameidata *));
-@@ -1445,6 +1469,8 @@
+@@ -1457,6 +1481,8 @@
  extern struct dentry * lookup_hash(struct qstr *, struct dentry *);
  #define user_path_walk(name,nd)        __user_walk(name, LOOKUP_FOLLOW|LOOKUP_POSITIVE, nd)
  #define user_path_walk_link(name,nd) __user_walk(name, LOOKUP_POSITIVE, nd)
@@ -1787,7 +1787,7 @@ Index: linux-2.4.21/include/linux/fs.h
  
  extern void inode_init_once(struct inode *);
  extern void iput(struct inode *);
-@@ -1609,6 +1635,8 @@
+@@ -1625,6 +1651,8 @@
  
  extern int vfs_readlink(struct dentry *, char *, int, const char *);
  extern int vfs_follow_link(struct nameidata *, const char *);
@@ -1798,8 +1798,8 @@ Index: linux-2.4.21/include/linux/fs.h
  extern struct inode_operations page_symlink_inode_operations;
 Index: linux-2.4.21/include/linux/fs_struct.h
 ===================================================================
---- linux-2.4.21.orig/include/linux/fs_struct.h        2004-09-11 10:16:14.000000000 -0400
-+++ linux-2.4.21/include/linux/fs_struct.h     2004-09-16 19:32:11.000000000 -0400
+--- linux-2.4.21.orig/include/linux/fs_struct.h        2005-06-01 22:51:40.000000000 -0400
++++ linux-2.4.21/include/linux/fs_struct.h     2005-06-01 22:58:09.054062464 -0400
 @@ -37,10 +37,12 @@
        write_lock(&fs->lock);
        old_root = fs->root;
@@ -1828,8 +1828,8 @@ Index: linux-2.4.21/include/linux/fs_struct.h
        }
 Index: linux-2.4.21/kernel/exit.c
 ===================================================================
---- linux-2.4.21.orig/kernel/exit.c    2004-09-11 10:16:35.000000000 -0400
-+++ linux-2.4.21/kernel/exit.c 2004-09-16 19:32:11.000000000 -0400
+--- linux-2.4.21.orig/kernel/exit.c    2005-06-01 22:52:04.000000000 -0400
++++ linux-2.4.21/kernel/exit.c 2005-06-01 22:58:09.055062312 -0400
 @@ -367,11 +367,14 @@
  {
        /* No need to hold fs->lock if we are killing it */
@@ -1847,9 +1847,9 @@ Index: linux-2.4.21/kernel/exit.c
                }
 Index: linux-2.4.21/kernel/fork.c
 ===================================================================
---- linux-2.4.21.orig/kernel/fork.c    2004-09-11 10:16:39.000000000 -0400
-+++ linux-2.4.21/kernel/fork.c 2004-09-16 19:32:11.000000000 -0400
-@@ -472,10 +472,13 @@
+--- linux-2.4.21.orig/kernel/fork.c    2005-06-01 22:52:05.000000000 -0400
++++ linux-2.4.21/kernel/fork.c 2005-06-01 22:58:09.055062312 -0400
+@@ -473,10 +473,13 @@
                fs->umask = old->umask;
                read_lock(&old->lock);
                fs->rootmnt = mntget(old->rootmnt);
@@ -1865,9 +1865,9 @@ Index: linux-2.4.21/kernel/fork.c
                } else {
 Index: linux-2.4.21/kernel/ksyms.c
 ===================================================================
---- linux-2.4.21.orig/kernel/ksyms.c   2004-09-16 19:21:00.000000000 -0400
-+++ linux-2.4.21/kernel/ksyms.c        2004-09-16 19:32:11.000000000 -0400
-@@ -317,6 +317,7 @@
+--- linux-2.4.21.orig/kernel/ksyms.c   2005-06-01 22:57:59.891455392 -0400
++++ linux-2.4.21/kernel/ksyms.c        2005-06-01 22:58:09.056062160 -0400
+@@ -318,6 +318,7 @@
  EXPORT_SYMBOL(set_page_dirty);
  EXPORT_SYMBOL(vfs_readlink);
  EXPORT_SYMBOL(vfs_follow_link);
diff --git a/lustre/kernel_patches/patches/vfs_intent-2.4.29-vanilla.patch b/lustre/kernel_patches/patches/vfs_intent-2.4.29-vanilla.patch
new file mode 100644 (file)
index 0000000..f19fbd4
--- /dev/null
@@ -0,0 +1,1833 @@
+Index: linux-2.4.29/fs/dcache.c
+===================================================================
+--- linux-2.4.29.orig/fs/dcache.c      2005-04-07 18:52:37.000000000 +0300
++++ linux-2.4.29/fs/dcache.c   2005-04-07 19:14:06.000000000 +0300
+@@ -184,6 +184,13 @@
+               spin_unlock(&dcache_lock);
+               return 0;
+       }
++
++      /* network invalidation by Lustre */
++      if (dentry->d_flags & DCACHE_LUSTRE_INVALID) {
++              spin_unlock(&dcache_lock);
++              return 0;
++      }
++
+       /*
+        * Check whether to do a partial shrink_dcache
+        * to get rid of unused child entries.
+@@ -836,13 +843,19 @@
+  * Adds a dentry to the hash according to its name.
+  */
+  
+-void d_rehash(struct dentry * entry)
++void __d_rehash(struct dentry * entry, int lock)
+ {
+       struct list_head *list = d_hash(entry->d_parent, entry->d_name.hash);
+       if (!list_empty(&entry->d_hash)) BUG();
+-      spin_lock(&dcache_lock);
++      if (lock) spin_lock(&dcache_lock);
+       list_add(&entry->d_hash, list);
+-      spin_unlock(&dcache_lock);
++      if (lock) spin_unlock(&dcache_lock);
++}
++EXPORT_SYMBOL(__d_rehash);
++
++void d_rehash(struct dentry * entry)
++{
++      __d_rehash(entry, 1);
+ }
+ #define do_switch(x,y) do { \
+Index: linux-2.4.29/fs/exec.c
+===================================================================
+--- linux-2.4.29.orig/fs/exec.c        2005-04-07 18:53:19.000000000 +0300
++++ linux-2.4.29/fs/exec.c     2005-04-07 19:14:06.000000000 +0300
+@@ -112,8 +112,10 @@
+       struct file * file;
+       struct nameidata nd;
+       int error;
++      struct lookup_intent it = { .it_op = IT_OPEN,
++                                  .it_flags = FMODE_READ|FMODE_EXEC };
+-      error = user_path_walk(library, &nd);
++      error = user_path_walk_it(library, &nd, &it);
+       if (error)
+               goto out;
+@@ -125,7 +127,8 @@
+       if (error)
+               goto exit;
+-      file = dentry_open(nd.dentry, nd.mnt, O_RDONLY);
++      file = dentry_open_it(nd.dentry, nd.mnt, O_RDONLY, &it);
++      intent_release(&it);
+       error = PTR_ERR(file);
+       if (IS_ERR(file))
+               goto out;
+@@ -378,8 +381,10 @@
+       struct inode *inode;
+       struct file *file;
+       int err = 0;
++      struct lookup_intent it = { .it_op = IT_OPEN,
++                                  .it_flags = FMODE_READ|FMODE_EXEC };
+-      err = path_lookup(name, LOOKUP_FOLLOW|LOOKUP_POSITIVE, &nd);
++      err = path_lookup_it(name, LOOKUP_FOLLOW|LOOKUP_POSITIVE, &nd, &it);
+       file = ERR_PTR(err);
+       if (!err) {
+               inode = nd.dentry->d_inode;
+@@ -391,7 +396,8 @@
+                               err = -EACCES;
+                       file = ERR_PTR(err);
+                       if (!err) {
+-                              file = dentry_open(nd.dentry, nd.mnt, O_RDONLY);
++                              file = dentry_open_it(nd.dentry, nd.mnt, O_RDONLY, &it);
++                              intent_release(&it);
+                               if (!IS_ERR(file)) {
+                                       err = deny_write_access(file);
+                                       if (err) {
+@@ -403,6 +409,7 @@
+                               return file;
+                       }
+               }
++              intent_release(&it);
+               path_release(&nd);
+       }
+       goto out;
+@@ -1163,7 +1170,7 @@
+               goto close_fail;
+       if (!file->f_op->write)
+               goto close_fail;
+-      if (do_truncate(file->f_dentry, 0) != 0)
++      if (do_truncate(file->f_dentry, 0, 0) != 0)
+               goto close_fail;
+       retval = binfmt->core_dump(signr, regs, file);
+Index: linux-2.4.29/fs/namei.c
+===================================================================
+--- linux-2.4.29.orig/fs/namei.c       2005-04-07 18:53:14.000000000 +0300
++++ linux-2.4.29/fs/namei.c    2005-05-03 17:23:44.139922792 +0300
+@@ -94,6 +94,13 @@
+  * XEmacs seems to be relying on it...
+  */
++void intent_release(struct lookup_intent *it)
++{
++      if (it && it->it_op_release)
++              it->it_op_release(it);
++
++}
++
+ /* In order to reduce some races, while at the same time doing additional
+  * checking and hopefully speeding things up, we copy filenames to the
+  * kernel data space before using them..
+@@ -260,10 +267,19 @@
+  * Internal lookup() using the new generic dcache.
+  * SMP-safe
+  */
+-static struct dentry * cached_lookup(struct dentry * parent, struct qstr * name, int flags)
++static struct dentry *cached_lookup(struct dentry *parent, struct qstr *name,
++                                  int flags, struct lookup_intent *it)
+ {
+       struct dentry * dentry = d_lookup(parent, name);
++      if (dentry && dentry->d_op && dentry->d_op->d_revalidate_it) {
++              if (!dentry->d_op->d_revalidate_it(dentry, flags, it) &&
++                  !d_invalidate(dentry)) {
++                      dput(dentry);
++                      dentry = NULL;
++              }
++              return dentry;
++      } else
+       if (dentry && dentry->d_op && dentry->d_op->d_revalidate) {
+               if (!dentry->d_op->d_revalidate(dentry, flags) && !d_invalidate(dentry)) {
+                       dput(dentry);
+@@ -281,11 +297,15 @@
+  * make sure that nobody added the entry to the dcache in the meantime..
+  * SMP-safe
+  */
+-static struct dentry * real_lookup(struct dentry * parent, struct qstr * name, int flags)
++static struct dentry *real_lookup(struct dentry *parent, struct qstr *name,
++                                int flags, struct lookup_intent *it)
+ {
+       struct dentry * result;
+       struct inode *dir = parent->d_inode;
++      int counter = 0;
++again:
++      counter++;
+       down(&dir->i_sem);
+       /*
+        * First re-do the cached lookup just in case it was created
+@@ -300,6 +320,9 @@
+               result = ERR_PTR(-ENOMEM);
+               if (dentry) {
+                       lock_kernel();
++                      if (dir->i_op->lookup_it)
++                              result = dir->i_op->lookup_it(dir, dentry, it, flags);
++                      else
+                       result = dir->i_op->lookup(dir, dentry);
+                       unlock_kernel();
+                       if (result)
+@@ -321,6 +344,15 @@
+                       dput(result);
+                       result = ERR_PTR(-ENOENT);
+               }
++      } else if (result->d_op && result->d_op->d_revalidate_it) {
++              if (!result->d_op->d_revalidate_it(result, flags, it) &&
++                  !d_invalidate(result)) {
++                      dput(result);
++                      if (counter > 10)
++                              result = ERR_PTR(-ESTALE);
++                      if (!IS_ERR(result))
++                              goto again;
++              }
+       }
+       return result;
+ }
+@@ -332,7 +364,8 @@
+  * Without that kind of total limit, nasty chains of consecutive
+  * symlinks can cause almost arbitrarily long lookups. 
+  */
+-static inline int do_follow_link(struct dentry *dentry, struct nameidata *nd)
++static inline int do_follow_link(struct dentry *dentry, struct nameidata *nd,
++                               struct lookup_intent *it)
+ {
+       int err;
+       if (current->link_count >= 5)
+@@ -346,10 +379,12 @@
+       current->link_count++;
+       current->total_link_count++;
+       UPDATE_ATIME(dentry->d_inode);
++      nd->intent = it;
+       err = dentry->d_inode->i_op->follow_link(dentry, nd);
+       current->link_count--;
+       return err;
+ loop:
++      intent_release(it);
+       path_release(nd);
+       return -ELOOP;
+ }
+@@ -447,7 +482,8 @@
+  *
+  * We expect 'base' to be positive and a directory.
+  */
+-int fastcall link_path_walk(const char * name, struct nameidata *nd)
++int fastcall link_path_walk_it(const char * name, struct nameidata *nd,
++                          struct lookup_intent *it)
+ {
+       struct dentry *dentry;
+       struct inode *inode;
+@@ -520,9 +556,9 @@
+                               break;
+               }
+               /* This does the actual lookups.. */
+-              dentry = cached_lookup(nd->dentry, &this, LOOKUP_CONTINUE);
++              dentry = cached_lookup(nd->dentry, &this, LOOKUP_CONTINUE, NULL);
+               if (!dentry) {
+-                      dentry = real_lookup(nd->dentry, &this, LOOKUP_CONTINUE);
++                      dentry = real_lookup(nd->dentry, &this, LOOKUP_CONTINUE, NULL);
+                       err = PTR_ERR(dentry);
+                       if (IS_ERR(dentry))
+                               break;
+@@ -540,7 +576,7 @@
+                       goto out_dput;
+               if (inode->i_op->follow_link) {
+-                      err = do_follow_link(dentry, nd);
++                      err = do_follow_link(dentry, nd, NULL);
+                       dput(dentry);
+                       if (err)
+                               goto return_err;
+@@ -556,7 +592,7 @@
+                       nd->dentry = dentry;
+               }
+               err = -ENOTDIR; 
+-              if (!inode->i_op->lookup)
++              if (!inode->i_op->lookup && !inode->i_op->lookup_it)
+                       break;
+               continue;
+               /* here ends the main loop */
+@@ -583,9 +619,9 @@
+                       if (err < 0)
+                               break;
+               }
+-              dentry = cached_lookup(nd->dentry, &this, nd->flags);
++              dentry = cached_lookup(nd->dentry, &this, nd->flags, it);
+               if (!dentry) {
+-                      dentry = real_lookup(nd->dentry, &this, nd->flags);
++                      dentry = real_lookup(nd->dentry, &this, nd->flags, it);
+                       err = PTR_ERR(dentry);
+                       if (IS_ERR(dentry))
+                               break;
+@@ -595,7 +631,7 @@
+               inode = dentry->d_inode;
+               if ((lookup_flags & LOOKUP_FOLLOW)
+                   && inode && inode->i_op && inode->i_op->follow_link) {
+-                      err = do_follow_link(dentry, nd);
++                      err = do_follow_link(dentry, nd, it);
+                       dput(dentry);
+                       if (err)
+                               goto return_err;
+@@ -609,7 +645,8 @@
+                       goto no_inode;
+               if (lookup_flags & LOOKUP_DIRECTORY) {
+                       err = -ENOTDIR; 
+-                      if (!inode->i_op || !inode->i_op->lookup)
++                      if (!inode->i_op ||
++                          (!inode->i_op->lookup && !inode->i_op->lookup_it))
+                               break;
+               }
+               goto return_base;
+@@ -635,6 +672,34 @@
+                * Check the cached dentry for staleness.
+                */
+               dentry = nd->dentry;
++              if (dentry && dentry->d_op && dentry->d_op->d_revalidate_it) {
++                      err = -ESTALE;
++                      if (!dentry->d_op->d_revalidate_it(dentry, 0, it)) {
++                              struct dentry *new;
++                              err = permission(dentry->d_parent->d_inode,
++                                               MAY_EXEC);
++                              if (err)
++                                      break;
++                              new = real_lookup(dentry->d_parent,
++                                                &dentry->d_name, 0, it);
++                              if (IS_ERR(new)) {
++                                      err = PTR_ERR(new);
++                                      break;
++                              }
++                              d_invalidate(dentry);
++                              dput(dentry);
++                              nd->dentry = new;
++                      }
++                      if (!nd->dentry->d_inode)
++                              goto no_inode;
++                      if (lookup_flags & LOOKUP_DIRECTORY) {
++                              err = -ENOTDIR; 
++                              if (!nd->dentry->d_inode->i_op ||
++                                  (!nd->dentry->d_inode->i_op->lookup &&
++                                   !nd->dentry->d_inode->i_op->lookup_it))
++                                      break;
++                      }
++              } else
+               if (dentry && dentry->d_op && dentry->d_op->d_revalidate) {
+                       err = -ESTALE;
+                       if (!dentry->d_op->d_revalidate(dentry, 0)) {
+@@ -648,15 +713,28 @@
+               dput(dentry);
+               break;
+       }
++      if (err)
++              intent_release(it);
+       path_release(nd);
+ return_err:
+       return err;
+ }
++int link_path_walk(const char * name, struct nameidata *nd)
++{
++      return link_path_walk_it(name, nd, NULL);
++}
++
++int path_walk_it(const char * name, struct nameidata *nd, struct lookup_intent *it)
++{
++      current->total_link_count = 0;
++      return link_path_walk_it(name, nd, it);
++}
++
+ int fastcall path_walk(const char * name, struct nameidata *nd)
+ {
+       current->total_link_count = 0;
+-      return link_path_walk(name, nd);
++      return link_path_walk_it(name, nd, NULL);
+ }
+ /* SMP-safe */
+@@ -741,6 +819,16 @@
+ }
+ /* SMP-safe */
++int path_lookup_it(const char *path, unsigned flags, struct nameidata *nd,
++                 struct lookup_intent *it)
++{
++      int error = 0;
++      if (path_init(path, flags, nd))
++              error = path_walk_it(path, nd, it);
++      return error;
++}
++
++/* SMP-safe */
+ int fastcall path_lookup(const char *path, unsigned flags, struct nameidata *nd)
+ {
+       int error = 0;
+@@ -755,6 +843,7 @@
+ {
+       nd->last_type = LAST_ROOT; /* if there are only slashes... */
+       nd->flags = flags;
++      nd->intent = NULL;
+       if (*name=='/')
+               return walk_init_root(name,nd);
+       read_lock(&current->fs->lock);
+@@ -769,7 +858,8 @@
+  * needs parent already locked. Doesn't follow mounts.
+  * SMP-safe.
+  */
+-struct dentry * lookup_hash(struct qstr *name, struct dentry * base)
++struct dentry * lookup_hash_it(struct qstr *name, struct dentry * base,
++                             struct lookup_intent *it)
+ {
+       struct dentry * dentry;
+       struct inode *inode;
+@@ -792,13 +882,16 @@
+                       goto out;
+       }
+-      dentry = cached_lookup(base, name, 0);
++      dentry = cached_lookup(base, name, 0, it);
+       if (!dentry) {
+               struct dentry *new = d_alloc(base, name);
+               dentry = ERR_PTR(-ENOMEM);
+               if (!new)
+                       goto out;
+               lock_kernel();
++              if (inode->i_op->lookup_it)
++                      dentry = inode->i_op->lookup_it(inode, new, it, 0);
++              else
+               dentry = inode->i_op->lookup(inode, new);
+               unlock_kernel();
+               if (!dentry)
+@@ -810,6 +903,12 @@
+       return dentry;
+ }
++struct dentry * lookup_hash(struct qstr *name, struct dentry * base)
++{
++      return lookup_hash_it(name, base, NULL);
++}
++
++
+ /* SMP-safe */
+ struct dentry * lookup_one_len(const char * name, struct dentry * base, int len)
+ {
+@@ -831,7 +930,7 @@
+       }
+       this.hash = end_name_hash(hash);
+-      return lookup_hash(&this, base);
++      return lookup_hash_it(&this, base, NULL);
+ access:
+       return ERR_PTR(-EACCES);
+ }
+@@ -862,6 +961,23 @@
+       return err;
+ }
++int __user_walk_it(const char *name, unsigned flags, struct nameidata *nd,
++                 struct lookup_intent *it)
++{
++      char *tmp;
++      int err;
++
++      tmp = getname(name);
++      err = PTR_ERR(tmp);
++      if (!IS_ERR(tmp)) {
++              err = 0;
++              if (path_init(tmp, flags, nd))
++                      err = path_walk_it(tmp, nd, it);
++              putname(tmp);
++      }
++      return err;
++}
++
+ /*
+  * It's inline, so penalty for filesystems that don't use sticky bit is
+  * minimal.
+@@ -957,7 +1073,8 @@
+       return retval;
+ }
+-int vfs_create(struct inode *dir, struct dentry *dentry, int mode)
++static int vfs_create_it(struct inode *dir, struct dentry *dentry, int mode,
++                       struct lookup_intent *it)
+ {
+       int error;
+@@ -970,12 +1087,15 @@
+               goto exit_lock;
+       error = -EACCES;        /* shouldn't it be ENOSYS? */
+-      if (!dir->i_op || !dir->i_op->create)
++      if (!dir->i_op || (!dir->i_op->create && !dir->i_op->create_it))
+               goto exit_lock;
+       DQUOT_INIT(dir);
+       lock_kernel();
+-      error = dir->i_op->create(dir, dentry, mode);
++      if (dir->i_op->create_it)
++              error = dir->i_op->create_it(dir, dentry, mode, it);
++      else
++              error = dir->i_op->create(dir, dentry, mode);
+       unlock_kernel();
+ exit_lock:
+       up(&dir->i_zombie);
+@@ -984,6 +1104,11 @@
+       return error;
+ }
++int vfs_create(struct inode *dir, struct dentry *dentry, int mode)
++{
++      return vfs_create_it(dir, dentry, mode, NULL);
++}
++
+ /*
+  *    open_namei()
+  *
+@@ -998,7 +1123,8 @@
+  * for symlinks (where the permissions are checked later).
+  * SMP-safe
+  */
+-int open_namei(const char * pathname, int flag, int mode, struct nameidata *nd)
++int open_namei_it(const char *pathname, int flag, int mode,
++                struct nameidata *nd, struct lookup_intent *it)
+ {
+       int acc_mode, error = 0;
+       struct inode *inode;
+@@ -1008,11 +1134,14 @@
+       acc_mode = ACC_MODE(flag);
++      if (it)
++              it->it_flags = flag;
++
+       /*
+        * The simplest case - just a plain lookup.
+        */
+       if (!(flag & O_CREAT)) {
+-              error = path_lookup(pathname, lookup_flags(flag), nd);
++              error = path_lookup_it(pathname, lookup_flags(flag), nd, it);
+               if (error)
+                       return error;
+               dentry = nd->dentry;
+@@ -1022,6 +1151,10 @@
+       /*
+        * Create - we need to know the parent.
+        */
++      if (it) {
++              it->it_create_mode = mode;
++              it->it_op |= IT_CREAT;
++      }
+       error = path_lookup(pathname, LOOKUP_PARENT, nd);
+       if (error)
+               return error;
+@@ -1037,7 +1170,7 @@
+       dir = nd->dentry;
+       down(&dir->d_inode->i_sem);
+-      dentry = lookup_hash(&nd->last, nd->dentry);
++      dentry = lookup_hash_it(&nd->last, nd->dentry, it);
+ do_last:
+       error = PTR_ERR(dentry);
+@@ -1046,10 +1179,11 @@
+               goto exit;
+       }
++      it->it_create_mode = mode;
+       /* Negative dentry, just create the file */
+       if (!dentry->d_inode) {
+-              error = vfs_create(dir->d_inode, dentry,
+-                                 mode & ~current->fs->umask);
++              error = vfs_create_it(dir->d_inode, dentry,
++                                 mode & ~current->fs->umask, it);
+               up(&dir->d_inode->i_sem);
+               dput(nd->dentry);
+               nd->dentry = dentry;
+@@ -1153,7 +1287,7 @@
+               if (!error) {
+                       DQUOT_INIT(inode);
+                       
+-                      error = do_truncate(dentry, 0);
++                      error = do_truncate(dentry, 0, 1);
+               }
+               put_write_access(inode);
+               if (error)
+@@ -1165,8 +1299,10 @@
+       return 0;
+ exit_dput:
++      intent_release(it);
+       dput(dentry);
+ exit:
++      intent_release(it);
+       path_release(nd);
+       return error;
+@@ -1185,7 +1321,10 @@
+        * are done. Procfs-like symlinks just set LAST_BIND.
+        */
+       UPDATE_ATIME(dentry->d_inode);
++      nd->intent = it;
+       error = dentry->d_inode->i_op->follow_link(dentry, nd);
++      if (error)
++              intent_release(it);
+       dput(dentry);
+       if (error)
+               return error;
+@@ -1207,13 +1346,20 @@
+       }
+       dir = nd->dentry;
+       down(&dir->d_inode->i_sem);
+-      dentry = lookup_hash(&nd->last, nd->dentry);
++      dentry = lookup_hash_it(&nd->last, nd->dentry, it);
+       putname(nd->last.name);
+       goto do_last;
+ }
++int open_namei(const char *pathname, int flag, int mode, struct nameidata *nd)
++{
++      return open_namei_it(pathname, flag, mode, nd, NULL);
++}
++
++
+ /* SMP-safe */
+-static struct dentry *lookup_create(struct nameidata *nd, int is_dir)
++static struct dentry *lookup_create(struct nameidata *nd, int is_dir,
++                                  struct lookup_intent *it)
+ {
+       struct dentry *dentry;
+@@ -1221,7 +1367,7 @@
+       dentry = ERR_PTR(-EEXIST);
+       if (nd->last_type != LAST_NORM)
+               goto fail;
+-      dentry = lookup_hash(&nd->last, nd->dentry);
++      dentry = lookup_hash_it(&nd->last, nd->dentry, it);
+       if (IS_ERR(dentry))
+               goto fail;
+       if (!is_dir && nd->last.name[nd->last.len] && !dentry->d_inode)
+@@ -1277,7 +1423,20 @@
+       error = path_lookup(tmp, LOOKUP_PARENT, &nd);
+       if (error)
+               goto out;
+-      dentry = lookup_create(&nd, 0);
++
++      if (nd.last_type != LAST_NORM) {
++              error = -EEXIST;
++              goto out2;
++      }
++      if (nd.dentry->d_inode->i_op->mknod_raw) {
++              struct inode_operations *op = nd.dentry->d_inode->i_op;
++              error = op->mknod_raw(&nd, mode, dev);
++              /* the file system wants to use normal vfs path now */
++              if (error != -EOPNOTSUPP)
++                      goto out2;
++      }
++
++      dentry = lookup_create(&nd, 0, NULL);
+       error = PTR_ERR(dentry);
+       mode &= ~current->fs->umask;
+@@ -1298,6 +1457,7 @@
+               dput(dentry);
+       }
+       up(&nd.dentry->d_inode->i_sem);
++out2:
+       path_release(&nd);
+ out:
+       putname(tmp);
+@@ -1345,7 +1505,18 @@
+               error = path_lookup(tmp, LOOKUP_PARENT, &nd);
+               if (error)
+                       goto out;
+-              dentry = lookup_create(&nd, 1);
++              if (nd.last_type != LAST_NORM) {
++                      error = -EEXIST;
++                      goto out2;
++              }
++              if (nd.dentry->d_inode->i_op->mkdir_raw) {
++                      struct inode_operations *op = nd.dentry->d_inode->i_op;
++                      error = op->mkdir_raw(&nd, mode);
++                      /* the file system wants to use normal vfs path now */
++                      if (error != -EOPNOTSUPP)
++                              goto out2;
++              }
++              dentry = lookup_create(&nd, 1, NULL);
+               error = PTR_ERR(dentry);
+               if (!IS_ERR(dentry)) {
+                       error = vfs_mkdir(nd.dentry->d_inode, dentry,
+@@ -1353,6 +1524,7 @@
+                       dput(dentry);
+               }
+               up(&nd.dentry->d_inode->i_sem);
++out2:
+               path_release(&nd);
+ out:
+               putname(tmp);
+@@ -1453,8 +1625,16 @@
+                       error = -EBUSY;
+                       goto exit1;
+       }
++      if (nd.dentry->d_inode->i_op->rmdir_raw) {
++              struct inode_operations *op = nd.dentry->d_inode->i_op;
++
++              error = op->rmdir_raw(&nd);
++              /* the file system wants to use normal vfs path now */
++              if (error != -EOPNOTSUPP)
++                      goto exit1;
++      }
+       down(&nd.dentry->d_inode->i_sem);
+-      dentry = lookup_hash(&nd.last, nd.dentry);
++      dentry = lookup_hash_it(&nd.last, nd.dentry, NULL);
+       error = PTR_ERR(dentry);
+       if (!IS_ERR(dentry)) {
+               error = vfs_rmdir(nd.dentry->d_inode, dentry);
+@@ -1512,8 +1692,15 @@
+       error = -EISDIR;
+       if (nd.last_type != LAST_NORM)
+               goto exit1;
++      if (nd.dentry->d_inode->i_op->unlink_raw) {
++              struct inode_operations *op = nd.dentry->d_inode->i_op;
++              error = op->unlink_raw(&nd);
++              /* the file system wants to use normal vfs path now */
++              if (error != -EOPNOTSUPP)
++                      goto exit1;
++      }
+       down(&nd.dentry->d_inode->i_sem);
+-      dentry = lookup_hash(&nd.last, nd.dentry);
++      dentry = lookup_hash_it(&nd.last, nd.dentry, NULL);
+       error = PTR_ERR(dentry);
+       if (!IS_ERR(dentry)) {
+               /* Why not before? Because we want correct error value */
+@@ -1580,15 +1767,27 @@
+               error = path_lookup(to, LOOKUP_PARENT, &nd);
+               if (error)
+                       goto out;
+-              dentry = lookup_create(&nd, 0);
++              if (nd.last_type != LAST_NORM) {
++                      error = -EEXIST;
++                      goto out2;
++              }
++              if (nd.dentry->d_inode->i_op->symlink_raw) {
++                      struct inode_operations *op = nd.dentry->d_inode->i_op;
++                      error = op->symlink_raw(&nd, from);
++                      /* the file system wants to use normal vfs path now */
++                      if (error != -EOPNOTSUPP)
++                              goto out2;
++              }
++              dentry = lookup_create(&nd, 0, NULL);
+               error = PTR_ERR(dentry);
+               if (!IS_ERR(dentry)) {
+                       error = vfs_symlink(nd.dentry->d_inode, dentry, from);
+                       dput(dentry);
+               }
+               up(&nd.dentry->d_inode->i_sem);
++      out2:
+               path_release(&nd);
+-out:
++      out:
+               putname(to);
+       }
+       putname(from);
+@@ -1664,7 +1863,18 @@
+               error = -EXDEV;
+               if (old_nd.mnt != nd.mnt)
+                       goto out_release;
+-              new_dentry = lookup_create(&nd, 0);
++              if (nd.last_type != LAST_NORM) {
++                      error = -EEXIST;
++                      goto out_release;
++              }
++              if (nd.dentry->d_inode->i_op->link_raw) {
++                      struct inode_operations *op = nd.dentry->d_inode->i_op;
++                      error = op->link_raw(&old_nd, &nd);
++                      /* the file system wants to use normal vfs path now */
++                      if (error != -EOPNOTSUPP)
++                              goto out_release;
++              }
++              new_dentry = lookup_create(&nd, 0, NULL);
+               error = PTR_ERR(new_dentry);
+               if (!IS_ERR(new_dentry)) {
+                       error = vfs_link(old_nd.dentry, nd.dentry->d_inode, new_dentry);
+@@ -1708,7 +1918,7 @@
+  *       locking].
+  */
+ int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry,
+-             struct inode *new_dir, struct dentry *new_dentry)
++                 struct inode *new_dir, struct dentry *new_dentry)
+ {
+       int error;
+       struct inode *target;
+@@ -1787,7 +1997,7 @@
+ }
+ int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry,
+-             struct inode *new_dir, struct dentry *new_dentry)
++                   struct inode *new_dir, struct dentry *new_dentry)
+ {
+       int error;
+@@ -1875,9 +2085,18 @@
+       if (newnd.last_type != LAST_NORM)
+               goto exit2;
++      if (old_dir->d_inode->i_op->rename_raw) {
++              lock_kernel();
++              error = old_dir->d_inode->i_op->rename_raw(&oldnd, &newnd);
++              unlock_kernel();
++              /* the file system wants to use normal vfs path now */
++              if (error != -EOPNOTSUPP)
++                      goto exit2;
++      }
++
+       double_lock(new_dir, old_dir);
+-      old_dentry = lookup_hash(&oldnd.last, old_dir);
++      old_dentry = lookup_hash_it(&oldnd.last, old_dir, NULL);
+       error = PTR_ERR(old_dentry);
+       if (IS_ERR(old_dentry))
+               goto exit3;
+@@ -1893,16 +2112,16 @@
+               if (newnd.last.name[newnd.last.len])
+                       goto exit4;
+       }
+-      new_dentry = lookup_hash(&newnd.last, new_dir);
++      new_dentry = lookup_hash_it(&newnd.last, new_dir, NULL);
+       error = PTR_ERR(new_dentry);
+       if (IS_ERR(new_dentry))
+               goto exit4;
++
+       lock_kernel();
+       error = vfs_rename(old_dir->d_inode, old_dentry,
+                                  new_dir->d_inode, new_dentry);
+       unlock_kernel();
+-
+       dput(new_dentry);
+ exit4:
+       dput(old_dentry);
+@@ -1953,20 +2172,26 @@
+ }
+ static inline int
+-__vfs_follow_link(struct nameidata *nd, const char *link)
++__vfs_follow_link(struct nameidata *nd, const char *link,
++                struct lookup_intent *it)
+ {
+       int res = 0;
+       char *name;
+       if (IS_ERR(link))
+               goto fail;
++      if (it == NULL)
++              it = nd->intent;
++      else if (it != nd->intent)
++              printk("it != nd->intent: tell phil@clusterfs.com\n");
++
+       if (*link == '/') {
+               path_release(nd);
+               if (!walk_init_root(link, nd))
+                       /* weird __emul_prefix() stuff did it */
+                       goto out;
+       }
+-      res = link_path_walk(link, nd);
++      res = link_path_walk_it(link, nd, it);
+ out:
+       if (current->link_count || res || nd->last_type!=LAST_NORM)
+               return res;
+@@ -1990,7 +2215,13 @@
+ int vfs_follow_link(struct nameidata *nd, const char *link)
+ {
+-      return __vfs_follow_link(nd, link);
++      return __vfs_follow_link(nd, link, NULL);
++}
++
++int vfs_follow_link_it(struct nameidata *nd, const char *link,
++                     struct lookup_intent *it)
++{
++      return __vfs_follow_link(nd, link, it);
+ }
+ /* get the link contents into pagecache */
+@@ -2032,7 +2263,7 @@
+ {
+       struct page *page = NULL;
+       char *s = page_getlink(dentry, &page);
+-      int res = __vfs_follow_link(nd, s);
++      int res = __vfs_follow_link(nd, s, NULL);
+       if (page) {
+               kunmap(page);
+               page_cache_release(page);
+Index: linux-2.4.29/fs/namespace.c
+===================================================================
+--- linux-2.4.29.orig/fs/namespace.c   2005-04-07 18:54:11.000000000 +0300
++++ linux-2.4.29/fs/namespace.c        2005-04-07 19:14:06.000000000 +0300
+@@ -98,6 +98,7 @@
+ {
+       old_nd->dentry = mnt->mnt_mountpoint;
+       old_nd->mnt = mnt->mnt_parent;
++      UNPIN(old_nd->dentry, old_nd->mnt, 1);
+       mnt->mnt_parent = mnt;
+       mnt->mnt_mountpoint = mnt->mnt_root;
+       list_del_init(&mnt->mnt_child);
+@@ -109,6 +110,7 @@
+ {
+       mnt->mnt_parent = mntget(nd->mnt);
+       mnt->mnt_mountpoint = dget(nd->dentry);
++      PIN(nd->dentry, nd->mnt, 1);
+       list_add(&mnt->mnt_hash, mount_hashtable+hash(nd->mnt, nd->dentry));
+       list_add_tail(&mnt->mnt_child, &nd->mnt->mnt_mounts);
+       nd->dentry->d_mounted++;
+@@ -488,14 +490,17 @@
+ {
+       struct nameidata old_nd;
+       struct vfsmount *mnt = NULL;
++      struct lookup_intent it = { .it_op = IT_GETATTR };
+       int err = mount_is_safe(nd);
+       if (err)
+               return err;
+       if (!old_name || !*old_name)
+               return -EINVAL;
+-      err = path_lookup(old_name, LOOKUP_POSITIVE|LOOKUP_FOLLOW, &old_nd);
+-      if (err)
++      err = path_lookup_it(old_name, LOOKUP_POSITIVE|LOOKUP_FOLLOW, &old_nd, &it);
++      if (err) {
++              intent_release(&it);
+               return err;
++      }
+       down_write(&current->namespace->sem);
+       err = -EINVAL;
+@@ -518,6 +523,7 @@
+       }
+       up_write(&current->namespace->sem);
++      intent_release(&it);
+       path_release(&old_nd);
+       return err;
+ }
+@@ -701,6 +707,7 @@
+                 unsigned long flags, void *data_page)
+ {
+       struct nameidata nd;
++      struct lookup_intent it = { .it_op = IT_GETATTR };
+       int retval = 0;
+       int mnt_flags = 0;
+@@ -728,10 +735,11 @@
+       flags &= ~(MS_NOSUID|MS_NOEXEC|MS_NODEV);
+       /* ... and get the mountpoint */
+-      retval = path_lookup(dir_name, LOOKUP_FOLLOW|LOOKUP_POSITIVE, &nd);
+-      if (retval)
++      retval = path_lookup_it(dir_name, LOOKUP_FOLLOW|LOOKUP_POSITIVE, &nd, &it);
++      if (retval) {
++              intent_release(&it);
+               return retval;
+-
++      }
+       if (flags & MS_REMOUNT)
+               retval = do_remount(&nd, flags & ~MS_REMOUNT, mnt_flags,
+                                   data_page);
+@@ -742,6 +750,8 @@
+       else
+               retval = do_add_mount(&nd, type_page, flags, mnt_flags,
+                                     dev_name, data_page);
++
++      intent_release(&it);
+       path_release(&nd);
+       return retval;
+ }
+@@ -907,6 +917,8 @@
+ {
+       struct vfsmount *tmp;
+       struct nameidata new_nd, old_nd, parent_nd, root_parent, user_nd;
++      struct lookup_intent new_it = { .it_op = IT_GETATTR };
++      struct lookup_intent old_it = { .it_op = IT_GETATTR };
+       int error;
+       if (!capable(CAP_SYS_ADMIN))
+@@ -914,14 +926,14 @@
+       lock_kernel();
+-      error = __user_walk(new_root, LOOKUP_POSITIVE|LOOKUP_FOLLOW|LOOKUP_DIRECTORY, &new_nd);
++      error = __user_walk_it(new_root, LOOKUP_POSITIVE|LOOKUP_FOLLOW|LOOKUP_DIRECTORY, &new_nd, &new_it);
+       if (error)
+               goto out0;
+       error = -EINVAL;
+       if (!check_mnt(new_nd.mnt))
+               goto out1;
+-      error = __user_walk(put_old, LOOKUP_POSITIVE|LOOKUP_FOLLOW|LOOKUP_DIRECTORY, &old_nd);
++      error = __user_walk_it(put_old, LOOKUP_POSITIVE|LOOKUP_FOLLOW|LOOKUP_DIRECTORY, &old_nd, &old_it);
+       if (error)
+               goto out1;
+@@ -976,8 +988,10 @@
+       up(&old_nd.dentry->d_inode->i_zombie);
+       up_write(&current->namespace->sem);
+       path_release(&user_nd);
++      intent_release(&old_it);
+       path_release(&old_nd);
+ out1:
++      intent_release(&new_it);
+       path_release(&new_nd);
+ out0:
+       unlock_kernel();
+Index: linux-2.4.29/fs/open.c
+===================================================================
+--- linux-2.4.29.orig/fs/open.c        2005-04-07 18:52:27.000000000 +0300
++++ linux-2.4.29/fs/open.c     2005-04-07 19:14:06.000000000 +0300
+@@ -19,6 +19,8 @@
+ #include <asm/uaccess.h>
+ #define special_file(m) (S_ISCHR(m)||S_ISBLK(m)||S_ISFIFO(m)||S_ISSOCK(m))
++extern int path_walk_it(const char *name, struct nameidata *nd,
++                      struct lookup_intent *it);
+ int vfs_statfs(struct super_block *sb, struct statfs *buf)
+ {
+@@ -95,9 +97,10 @@
+       write_unlock(&files->file_lock);
+ }
+-int do_truncate(struct dentry *dentry, loff_t length)
++int do_truncate(struct dentry *dentry, loff_t length, int called_from_open)
+ {
+       struct inode *inode = dentry->d_inode;
++      struct inode_operations *op = dentry->d_inode->i_op;
+       int error;
+       struct iattr newattrs;
+@@ -109,7 +112,13 @@
+       down(&inode->i_sem);
+       newattrs.ia_size = length;
+       newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME;
+-      error = notify_change(dentry, &newattrs);
++      if (called_from_open)
++              newattrs.ia_valid |= ATTR_FROM_OPEN;
++      if (op->setattr_raw) {
++              newattrs.ia_valid |= ATTR_RAW;
++              error = op->setattr_raw(inode, &newattrs);
++      } else
++              error = notify_change(dentry, &newattrs);
+       up(&inode->i_sem);
+       up_write(&inode->i_alloc_sem);
+       return error;
+@@ -120,12 +129,13 @@
+       struct nameidata nd;
+       struct inode * inode;
+       int error;
++      struct lookup_intent it = { .it_op = IT_GETATTR };
+       error = -EINVAL;
+       if (length < 0) /* sorry, but loff_t says... */
+               goto out;
+-      error = user_path_walk(path, &nd);
++      error = user_path_walk_it(path, &nd, &it);
+       if (error)
+               goto out;
+       inode = nd.dentry->d_inode;
+@@ -165,11 +175,13 @@
+       error = locks_verify_truncate(inode, NULL, length);
+       if (!error) {
+               DQUOT_INIT(inode);
+-              error = do_truncate(nd.dentry, length);
++              intent_release(&it);
++              error = do_truncate(nd.dentry, length, 0);
+       }
+       put_write_access(inode);
+ dput_and_out:
++      intent_release(&it);
+       path_release(&nd);
+ out:
+       return error;
+@@ -217,7 +229,7 @@
+       error = locks_verify_truncate(inode, file, length);
+       if (!error)
+-              error = do_truncate(dentry, length);
++              error = do_truncate(dentry, length, 0);
+ out_putf:
+       fput(file);
+ out:
+@@ -262,11 +274,13 @@
+       struct inode * inode;
+       struct iattr newattrs;
+-      error = user_path_walk(filename, &nd);
++      error = user_path_walk_it(filename, &nd, NULL);
+       if (error)
+               goto out;
+       inode = nd.dentry->d_inode;
++      /* this is safe without a Lustre lock because it only depends
++         on the super block */
+       error = -EROFS;
+       if (IS_RDONLY(inode))
+               goto dput_and_out;
+@@ -284,7 +298,20 @@
+                       goto dput_and_out;
+               newattrs.ia_valid |= ATTR_ATIME_SET | ATTR_MTIME_SET;
+-      } else {
++      } 
++
++
++      if (inode->i_op->setattr_raw) {
++              struct inode_operations *op = nd.dentry->d_inode->i_op;
++ 
++              newattrs.ia_valid |= ATTR_RAW;
++              error = op->setattr_raw(inode, &newattrs);
++              /* the file system wants to use normal vfs path now */
++              if (error != -EOPNOTSUPP)
++                      goto dput_and_out;
++      }
++ 
++      if (!times) {
+               error = -EACCES;
+               if (IS_IMMUTABLE(inode))
+                       goto dput_and_out;
+@@ -312,12 +339,14 @@
+       struct inode * inode;
+       struct iattr newattrs;
+-      error = user_path_walk(filename, &nd);
++      error = user_path_walk_it(filename, &nd, NULL);
+       if (error)
+               goto out;
+       inode = nd.dentry->d_inode;
++      /* this is safe without a Lustre lock because it only depends
++         on the super block */
+       error = -EROFS;
+       if (IS_RDONLY(inode))
+               goto dput_and_out;
+@@ -335,7 +364,19 @@
+               newattrs.ia_atime = times[0].tv_sec;
+               newattrs.ia_mtime = times[1].tv_sec;
+               newattrs.ia_valid |= ATTR_ATIME_SET | ATTR_MTIME_SET;
+-      } else {
++      }
++
++      if (inode->i_op->setattr_raw) {
++              struct inode_operations *op = nd.dentry->d_inode->i_op;
++ 
++              newattrs.ia_valid |= ATTR_RAW;
++              error = op->setattr_raw(inode, &newattrs);
++              /* the file system wants to use normal vfs path now */
++              if (error != -EOPNOTSUPP)
++                      goto dput_and_out;
++      }
++
++      if (!utimes) {
+               error = -EACCES;
+               if (IS_IMMUTABLE(inode))
+                       goto dput_and_out;
+@@ -362,6 +403,7 @@
+       int old_fsuid, old_fsgid;
+       kernel_cap_t old_cap;
+       int res;
++      struct lookup_intent it = { .it_op = IT_GETATTR };
+       if (mode & ~S_IRWXO)    /* where's F_OK, X_OK, W_OK, R_OK? */
+               return -EINVAL;
+@@ -379,13 +421,14 @@
+       else
+               current->cap_effective = current->cap_permitted;
+-      res = user_path_walk(filename, &nd);
++      res = user_path_walk_it(filename, &nd, &it);
+       if (!res) {
+               res = permission(nd.dentry->d_inode, mode);
+               /* SuS v2 requires we report a read only fs too */
+               if(!res && (mode & S_IWOTH) && IS_RDONLY(nd.dentry->d_inode)
+                  && !special_file(nd.dentry->d_inode->i_mode))
+                       res = -EROFS;
++              intent_release(&it);
+               path_release(&nd);
+       }
+@@ -400,8 +443,9 @@
+ {
+       int error;
+       struct nameidata nd;
++      struct lookup_intent it = { .it_op = IT_GETATTR };
+-      error = __user_walk(filename,LOOKUP_POSITIVE|LOOKUP_FOLLOW|LOOKUP_DIRECTORY,&nd);
++      error = __user_walk_it(filename,LOOKUP_POSITIVE|LOOKUP_FOLLOW|LOOKUP_DIRECTORY,&nd, &it);
+       if (error)
+               goto out;
+@@ -412,6 +456,7 @@
+       set_fs_pwd(current->fs, nd.mnt, nd.dentry);
+ dput_and_out:
++      intent_release(&it);
+       path_release(&nd);
+ out:
+       return error;
+@@ -451,9 +496,10 @@
+ {
+       int error;
+       struct nameidata nd;
++      struct lookup_intent it = { .it_op = IT_GETATTR };
+-      error = __user_walk(filename, LOOKUP_POSITIVE | LOOKUP_FOLLOW |
+-                    LOOKUP_DIRECTORY | LOOKUP_NOALT, &nd);
++      error = __user_walk_it(filename, LOOKUP_POSITIVE | LOOKUP_FOLLOW |
++                             LOOKUP_DIRECTORY | LOOKUP_NOALT, &nd, &it);
+       if (error)
+               goto out;
+@@ -469,39 +515,56 @@
+       set_fs_altroot();
+       error = 0;
+ dput_and_out:
++      intent_release(&it);
+       path_release(&nd);
+ out:
+       return error;
+ }
+-asmlinkage long sys_fchmod(unsigned int fd, mode_t mode)
++int chmod_common(struct dentry *dentry, mode_t mode)
+ {
+-      struct inode * inode;
+-      struct dentry * dentry;
+-      struct file * file;
+-      int err = -EBADF;
++      struct inode *inode = dentry->d_inode;
+       struct iattr newattrs;
++      int err = -EROFS;
+-      file = fget(fd);
+-      if (!file)
++      if (IS_RDONLY(inode))
+               goto out;
+-      dentry = file->f_dentry;
+-      inode = dentry->d_inode;
++      if (inode->i_op->setattr_raw) {
++              newattrs.ia_mode = mode;
++              newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
++              newattrs.ia_valid |= ATTR_RAW;
++              err = inode->i_op->setattr_raw(inode, &newattrs);
++              /* the file system wants to use normal vfs path now */
++              if (err != -EOPNOTSUPP)
++                      goto out;
++      }
+-      err = -EROFS;
+-      if (IS_RDONLY(inode))
+-              goto out_putf;
+       err = -EPERM;
+       if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
+-              goto out_putf;
++              goto out;
++
+       if (mode == (mode_t) -1)
+               mode = inode->i_mode;
+       newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
+       newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
+       err = notify_change(dentry, &newattrs);
+-out_putf:
++out:
++      return err;
++}
++
++asmlinkage long sys_fchmod(unsigned int fd, mode_t mode)
++{
++      struct file * file;
++      int err = -EBADF;
++
++      file = fget(fd);
++      if (!file)
++              goto out;
++
++      err = chmod_common(file->f_dentry, mode);
++
+       fput(file);
+ out:
+       return err;
+@@ -510,30 +573,14 @@
+ asmlinkage long sys_chmod(const char * filename, mode_t mode)
+ {
+       struct nameidata nd;
+-      struct inode * inode;
+       int error;
+-      struct iattr newattrs;
+       error = user_path_walk(filename, &nd);
+       if (error)
+               goto out;
+-      inode = nd.dentry->d_inode;
+-
+-      error = -EROFS;
+-      if (IS_RDONLY(inode))
+-              goto dput_and_out;
+-
+-      error = -EPERM;
+-      if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
+-              goto dput_and_out;
+-      if (mode == (mode_t) -1)
+-              mode = inode->i_mode;
+-      newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
+-      newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
+-      error = notify_change(nd.dentry, &newattrs);
++      error = chmod_common(nd.dentry, mode);
+-dput_and_out:
+       path_release(&nd);
+ out:
+       return error;
+@@ -553,6 +600,20 @@
+       error = -EROFS;
+       if (IS_RDONLY(inode))
+               goto out;
++
++      if (inode->i_op->setattr_raw) {
++              struct inode_operations *op = dentry->d_inode->i_op;
++
++              newattrs.ia_uid = user;
++              newattrs.ia_gid = group;
++              newattrs.ia_valid = ATTR_UID | ATTR_GID | ATTR_CTIME;
++              newattrs.ia_valid |= ATTR_RAW;
++              error = op->setattr_raw(inode, &newattrs);
++              /* the file system wants to use normal vfs path now */
++              if (error != -EOPNOTSUPP)
++                      return error;
++      }
++
+       error = -EPERM;
+       if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
+               goto out;
+@@ -657,6 +718,7 @@
+ {
+       int namei_flags, error;
+       struct nameidata nd;
++      struct lookup_intent it = { .it_op = IT_OPEN };
+       namei_flags = flags;
+       if ((namei_flags+1) & O_ACCMODE)
+@@ -664,14 +726,15 @@
+       if (namei_flags & O_TRUNC)
+               namei_flags |= 2;
+-      error = open_namei(filename, namei_flags, mode, &nd);
+-      if (!error)
+-              return dentry_open(nd.dentry, nd.mnt, flags);
++      error = open_namei_it(filename, namei_flags, mode, &nd, &it);
++      if (error)
++              return ERR_PTR(error);
+-      return ERR_PTR(error);
++      return dentry_open_it(nd.dentry, nd.mnt, flags, &it);
+ }
+-struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags)
++struct file *dentry_open_it(struct dentry *dentry, struct vfsmount *mnt,
++                          int flags, struct lookup_intent *it)
+ {
+       struct file * f;
+       struct inode *inode;
+@@ -708,12 +771,15 @@
+       }
+       if (f->f_op && f->f_op->open) {
++              f->f_it = it;
+               error = f->f_op->open(inode,f);
++              f->f_it = NULL;
+               if (error)
+                       goto cleanup_all;
+       }
+       f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
++      intent_release(it);
+       return f;
+ cleanup_all:
+@@ -728,11 +794,17 @@
+ cleanup_file:
+       put_filp(f);
+ cleanup_dentry:
++      intent_release(it);
+       dput(dentry);
+       mntput(mnt);
+       return ERR_PTR(error);
+ }
++struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags)
++{
++      return dentry_open_it(dentry, mnt, flags, NULL);
++}
++
+ /*
+  * Find an empty file descriptor entry, and mark it busy.
+  */
+Index: linux-2.4.29/fs/stat.c
+===================================================================
+--- linux-2.4.29.orig/fs/stat.c        2005-04-07 18:52:47.000000000 +0300
++++ linux-2.4.29/fs/stat.c     2005-04-07 19:14:06.000000000 +0300
+@@ -17,10 +17,12 @@
+  * Revalidate the inode. This is required for proper NFS attribute caching.
+  */
+ static __inline__ int
+-do_revalidate(struct dentry *dentry)
++do_revalidate(struct dentry *dentry, struct lookup_intent *it)
+ {
+       struct inode * inode = dentry->d_inode;
+-      if (inode->i_op && inode->i_op->revalidate)
++      if (inode->i_op && inode->i_op->revalidate_it)
++              return inode->i_op->revalidate_it(dentry, it);
++      else if (inode->i_op && inode->i_op->revalidate)
+               return inode->i_op->revalidate(dentry);
+       return 0;
+ }
+@@ -141,13 +143,15 @@
+ asmlinkage long sys_stat(char * filename, struct __old_kernel_stat * statbuf)
+ {
+       struct nameidata nd;
++      struct lookup_intent it = { .it_op = IT_GETATTR };
+       int error;
+-      error = user_path_walk(filename, &nd);
++      error = user_path_walk_it(filename, &nd, &it);
+       if (!error) {
+-              error = do_revalidate(nd.dentry);
++              error = do_revalidate(nd.dentry, &it);
+               if (!error)
+                       error = cp_old_stat(nd.dentry->d_inode, statbuf);
++              intent_release(&it);
+               path_release(&nd);
+       }
+       return error;
+@@ -157,13 +161,15 @@
+ asmlinkage long sys_newstat(char * filename, struct stat * statbuf)
+ {
+       struct nameidata nd;
++      struct lookup_intent it = { .it_op = IT_GETATTR };
+       int error;
+-      error = user_path_walk(filename, &nd);
++      error = user_path_walk_it(filename, &nd, &it);
+       if (!error) {
+-              error = do_revalidate(nd.dentry);
++              error = do_revalidate(nd.dentry, &it);
+               if (!error)
+                       error = cp_new_stat(nd.dentry->d_inode, statbuf);
++              intent_release(&it);
+               path_release(&nd);
+       }
+       return error;
+@@ -178,13 +184,15 @@
+ asmlinkage long sys_lstat(char * filename, struct __old_kernel_stat * statbuf)
+ {
+       struct nameidata nd;
++      struct lookup_intent it = { .it_op = IT_GETATTR };
+       int error;
+-      error = user_path_walk_link(filename, &nd);
++      error = user_path_walk_link_it(filename, &nd, &it);
+       if (!error) {
+-              error = do_revalidate(nd.dentry);
++              error = do_revalidate(nd.dentry, &it);
+               if (!error)
+                       error = cp_old_stat(nd.dentry->d_inode, statbuf);
++              intent_release(&it);
+               path_release(&nd);
+       }
+       return error;
+@@ -195,13 +203,15 @@
+ asmlinkage long sys_newlstat(char * filename, struct stat * statbuf)
+ {
+       struct nameidata nd;
++      struct lookup_intent it = { .it_op = IT_GETATTR };
+       int error;
+-      error = user_path_walk_link(filename, &nd);
++      error = user_path_walk_link_it(filename, &nd, &it);
+       if (!error) {
+-              error = do_revalidate(nd.dentry);
++              error = do_revalidate(nd.dentry, &it);
+               if (!error)
+                       error = cp_new_stat(nd.dentry->d_inode, statbuf);
++              intent_release(&it);
+               path_release(&nd);
+       }
+       return error;
+@@ -222,7 +232,7 @@
+       if (f) {
+               struct dentry * dentry = f->f_dentry;
+-              err = do_revalidate(dentry);
++              err = do_revalidate(dentry, NULL);
+               if (!err)
+                       err = cp_old_stat(dentry->d_inode, statbuf);
+               fput(f);
+@@ -241,7 +251,7 @@
+       if (f) {
+               struct dentry * dentry = f->f_dentry;
+-              err = do_revalidate(dentry);
++              err = do_revalidate(dentry, NULL);
+               if (!err)
+                       err = cp_new_stat(dentry->d_inode, statbuf);
+               fput(f);
+@@ -263,7 +273,7 @@
+               error = -EINVAL;
+               if (inode->i_op && inode->i_op->readlink &&
+-                  !(error = do_revalidate(nd.dentry))) {
++                  !(error = do_revalidate(nd.dentry, NULL))) {
+                       UPDATE_ATIME(inode);
+                       error = inode->i_op->readlink(nd.dentry, buf, bufsiz);
+               }
+@@ -339,12 +349,14 @@
+ {
+       struct nameidata nd;
+       int error;
++      struct lookup_intent it = { .it_op = IT_GETATTR };
+-      error = user_path_walk(filename, &nd);
++      error = user_path_walk_it(filename, &nd, &it);
+       if (!error) {
+-              error = do_revalidate(nd.dentry);
++              error = do_revalidate(nd.dentry, &it);
+               if (!error)
+                       error = cp_new_stat64(nd.dentry->d_inode, statbuf);
++              intent_release(&it);
+               path_release(&nd);
+       }
+       return error;
+@@ -354,12 +366,14 @@
+ {
+       struct nameidata nd;
+       int error;
++      struct lookup_intent it = { .it_op = IT_GETATTR };
+-      error = user_path_walk_link(filename, &nd);
++      error = user_path_walk_link_it(filename, &nd, &it);
+       if (!error) {
+-              error = do_revalidate(nd.dentry);
++              error = do_revalidate(nd.dentry, &it);
+               if (!error)
+                       error = cp_new_stat64(nd.dentry->d_inode, statbuf);
++              intent_release(&it);
+               path_release(&nd);
+       }
+       return error;
+@@ -374,7 +388,7 @@
+       if (f) {
+               struct dentry * dentry = f->f_dentry;
+-              err = do_revalidate(dentry);
++              err = do_revalidate(dentry, NULL);
+               if (!err)
+                       err = cp_new_stat64(dentry->d_inode, statbuf);
+               fput(f);
+Index: linux-2.4.29/include/linux/dcache.h
+===================================================================
+--- linux-2.4.29.orig/include/linux/dcache.h   2005-04-07 18:55:17.000000000 +0300
++++ linux-2.4.29/include/linux/dcache.h        2005-04-07 19:14:06.000000000 +0300
+@@ -6,6 +6,51 @@
+ #include <asm/atomic.h>
+ #include <linux/mount.h>
+ #include <linux/kernel.h>
++#include <linux/string.h>
++
++#define IT_OPEN     0x0001
++#define IT_CREAT    0x0002
++#define IT_READDIR  0x0004
++#define IT_GETATTR  0x0008
++#define IT_LOOKUP   0x0010
++#define IT_UNLINK   0x0020
++#define IT_GETXATTR 0x0040
++#define IT_EXEC     0x0080
++#define IT_PIN      0x0100
++
++#define IT_FL_LOCKED   0x0001
++#define IT_FL_FOLLOWED 0x0002 /* set by vfs_follow_link */
++
++#define INTENT_MAGIC 0x19620323
++
++
++struct lustre_intent_data {
++      int       it_disposition;
++      int       it_status;
++      __u64     it_lock_handle;
++      void     *it_data;
++      int       it_lock_mode;
++      int it_int_flags;
++};
++struct lookup_intent {
++      int     it_magic;
++      void    (*it_op_release)(struct lookup_intent *);
++      int     it_op;
++      int     it_flags;
++      int     it_create_mode;
++      union {
++              struct lustre_intent_data lustre;
++      } d;
++};
++
++static inline void intent_init(struct lookup_intent *it, int op, int flags)
++{
++      memset(it, 0, sizeof(*it));
++      it->it_magic = INTENT_MAGIC;
++      it->it_op = op;
++      it->it_flags = flags;
++}
++
+ /*
+  * linux/include/linux/dcache.h
+@@ -91,8 +136,22 @@
+       int (*d_delete)(struct dentry *);
+       void (*d_release)(struct dentry *);
+       void (*d_iput)(struct dentry *, struct inode *);
++      int (*d_revalidate_it)(struct dentry *, int, struct lookup_intent *);
++      void (*d_pin)(struct dentry *, struct vfsmount * , int);
++      void (*d_unpin)(struct dentry *, struct vfsmount *, int);
+ };
++#define PIN(de,mnt,flag)  if (de && de->d_op && de->d_op->d_pin) \
++                              de->d_op->d_pin(de, mnt, flag);
++#define UNPIN(de,mnt,flag)  if (de && de->d_op && de->d_op->d_unpin) \
++                              de->d_op->d_unpin(de, mnt, flag);
++
++
++/* defined in fs/namei.c */
++extern void intent_release(struct lookup_intent *it);
++/* defined in fs/dcache.c */
++extern void __d_rehash(struct dentry * entry, int lock);
++
+ /* the dentry parameter passed to d_hash and d_compare is the parent
+  * directory of the entries to be compared. It is used in case these
+  * functions need any directory specific information for determining
+@@ -124,6 +183,7 @@
+                                        * s_nfsd_free_path semaphore will be down
+                                        */
+ #define DCACHE_REFERENCED     0x0008  /* Recently used, don't discard. */
++#define DCACHE_LUSTRE_INVALID 0x0010  /* Lustre invalidated */
+ extern spinlock_t dcache_lock;
+Index: linux-2.4.29/include/linux/fs.h
+===================================================================
+--- linux-2.4.29.orig/include/linux/fs.h       2005-04-07 18:59:19.000000000 +0300
++++ linux-2.4.29/include/linux/fs.h    2005-05-03 17:06:23.738087912 +0300
+@@ -73,6 +73,7 @@
+ #define FMODE_READ 1
+ #define FMODE_WRITE 2
++#define FMODE_EXEC 4
+ #define READ 0
+ #define WRITE 1
+@@ -340,6 +341,9 @@
+ #define ATTR_MTIME_SET        256
+ #define ATTR_FORCE    512     /* Not a change, but a change it */
+ #define ATTR_ATTR_FLAG        1024
++#define ATTR_RAW      0x0800  /* file system, not vfs will massage attrs */
++#define ATTR_FROM_OPEN        0x1000  /* called from open path, ie O_TRUNC */
++#define ATTR_CTIME_SET        0x2000
+ /*
+  * This is the Inode Attributes structure, used for notify_change().  It
+@@ -478,6 +482,7 @@
+       struct pipe_inode_info  *i_pipe;
+       struct block_device     *i_bdev;
+       struct char_device      *i_cdev;
++      void                    *i_filterdata;
+       unsigned long           i_dnotify_mask; /* Directory notify events */
+       struct dnotify_struct   *i_dnotify; /* for directory notifications */
+@@ -580,6 +585,7 @@
+       /* needed for tty driver, and maybe others */
+       void                    *private_data;
++      struct lookup_intent    *f_it;
+       /* preallocated helper kiobuf to speedup O_DIRECT */
+       struct kiobuf           *f_iobuf;
+@@ -700,6 +706,7 @@
+       struct qstr last;
+       unsigned int flags;
+       int last_type;
++      struct lookup_intent *intent;
+ };
+ /*
+@@ -820,7 +827,8 @@
+ extern int vfs_link(struct dentry *, struct inode *, struct dentry *);
+ extern int vfs_rmdir(struct inode *, struct dentry *);
+ extern int vfs_unlink(struct inode *, struct dentry *);
+-extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *);
++int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
++             struct inode *new_dir, struct dentry *new_dentry);
+ /*
+  * File types
+@@ -880,21 +888,32 @@
+ struct inode_operations {
+       int (*create) (struct inode *,struct dentry *,int);
++      int (*create_it) (struct inode *,struct dentry *,int, struct lookup_intent *);
+       struct dentry * (*lookup) (struct inode *,struct dentry *);
++      struct dentry * (*lookup_it) (struct inode *,struct dentry *, struct lookup_intent *, int flags);
+       int (*link) (struct dentry *,struct inode *,struct dentry *);
++      int (*link_raw) (struct nameidata *,struct nameidata *);
+       int (*unlink) (struct inode *,struct dentry *);
++      int (*unlink_raw) (struct nameidata *);
+       int (*symlink) (struct inode *,struct dentry *,const char *);
++      int (*symlink_raw) (struct nameidata *,const char *);
+       int (*mkdir) (struct inode *,struct dentry *,int);
++      int (*mkdir_raw) (struct nameidata *,int);
+       int (*rmdir) (struct inode *,struct dentry *);
++      int (*rmdir_raw) (struct nameidata *);
+       int (*mknod) (struct inode *,struct dentry *,int,int);
++      int (*mknod_raw) (struct nameidata *,int,dev_t);
+       int (*rename) (struct inode *, struct dentry *,
+                       struct inode *, struct dentry *);
++      int (*rename_raw) (struct nameidata *, struct nameidata *);
+       int (*readlink) (struct dentry *, char *,int);
+       int (*follow_link) (struct dentry *, struct nameidata *);
+       void (*truncate) (struct inode *);
+       int (*permission) (struct inode *, int);
+       int (*revalidate) (struct dentry *);
++      int (*revalidate_it) (struct dentry *, struct lookup_intent *);
+       int (*setattr) (struct dentry *, struct iattr *);
++      int (*setattr_raw) (struct inode *, struct iattr *);
+       int (*getattr) (struct dentry *, struct iattr *);
+       int (*setxattr) (struct dentry *, const char *, void *, size_t, int);
+       ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
+@@ -1091,10 +1110,14 @@
+ asmlinkage long sys_open(const char *, int, int);
+ asmlinkage long sys_close(unsigned int);      /* yes, it's really unsigned */
+-extern int do_truncate(struct dentry *, loff_t start);
++extern int do_truncate(struct dentry *, loff_t start, int called_from_open);
+ extern struct file *filp_open(const char *, int, int);
+ extern struct file * dentry_open(struct dentry *, struct vfsmount *, int);
++extern int open_namei_it(const char *filename, int namei_flags, int mode,
++                       struct nameidata *nd, struct lookup_intent *it);
++extern struct file *dentry_open_it(struct dentry *dentry, struct vfsmount *mnt,
++                          int flags, struct lookup_intent *it);
+ extern int filp_close(struct file *, fl_owner_t id);
+ extern char * getname(const char *);
+@@ -1385,6 +1408,7 @@
+ extern loff_t default_llseek(struct file *file, loff_t offset, int origin);
+ extern int FASTCALL(__user_walk(const char *, unsigned, struct nameidata *));
++extern int FASTCALL(__user_walk_it(const char *, unsigned, struct nameidata *, struct lookup_intent *it));
+ extern int FASTCALL(path_init(const char *, unsigned, struct nameidata *));
+ extern int FASTCALL(path_walk(const char *, struct nameidata *));
+ extern int FASTCALL(path_lookup(const char *, unsigned, struct nameidata *));
+@@ -1396,6 +1420,8 @@
+ extern struct dentry * lookup_hash(struct qstr *, struct dentry *);
+ #define user_path_walk(name,nd)        __user_walk(name, LOOKUP_FOLLOW|LOOKUP_POSITIVE, nd)
+ #define user_path_walk_link(name,nd) __user_walk(name, LOOKUP_POSITIVE, nd)
++#define user_path_walk_it(name,nd,it)  __user_walk_it(name, LOOKUP_FOLLOW|LOOKUP_POSITIVE, nd, it)
++#define user_path_walk_link_it(name,nd,it) __user_walk_it(name, LOOKUP_POSITIVE, nd, it)
+ extern void inode_init_once(struct inode *);
+ extern void __inode_init_once(struct inode *);
+@@ -1539,6 +1565,8 @@
+ extern int vfs_readlink(struct dentry *, char *, int, const char *);
+ extern int vfs_follow_link(struct nameidata *, const char *);
++extern int vfs_follow_link_it(struct nameidata *, const char *,
++                            struct lookup_intent *it);
+ extern int page_readlink(struct dentry *, char *, int);
+ extern int page_follow_link(struct dentry *, struct nameidata *);
+ extern struct inode_operations page_symlink_inode_operations;
+Index: linux-2.4.29/include/linux/fs_struct.h
+===================================================================
+--- linux-2.4.29.orig/include/linux/fs_struct.h        2005-04-07 18:54:22.000000000 +0300
++++ linux-2.4.29/include/linux/fs_struct.h     2005-04-07 19:14:06.000000000 +0300
+@@ -34,10 +34,12 @@
+       write_lock(&fs->lock);
+       old_root = fs->root;
+       old_rootmnt = fs->rootmnt;
++      PIN(dentry, mnt, 1);
+       fs->rootmnt = mntget(mnt);
+       fs->root = dget(dentry);
+       write_unlock(&fs->lock);
+       if (old_root) {
++              UNPIN(old_root, old_rootmnt, 1);
+               dput(old_root);
+               mntput(old_rootmnt);
+       }
+@@ -57,10 +59,12 @@
+       write_lock(&fs->lock);
+       old_pwd = fs->pwd;
+       old_pwdmnt = fs->pwdmnt;
++      PIN(dentry, mnt, 0);
+       fs->pwdmnt = mntget(mnt);
+       fs->pwd = dget(dentry);
+       write_unlock(&fs->lock);
+       if (old_pwd) {
++              UNPIN(old_pwd, old_pwdmnt, 0);
+               dput(old_pwd);
+               mntput(old_pwdmnt);
+       }
+Index: linux-2.4.29/kernel/exit.c
+===================================================================
+--- linux-2.4.29.orig/kernel/exit.c    2005-04-07 18:53:09.000000000 +0300
++++ linux-2.4.29/kernel/exit.c 2005-04-07 19:14:06.000000000 +0300
+@@ -238,11 +238,14 @@
+ {
+       /* No need to hold fs->lock if we are killing it */
+       if (atomic_dec_and_test(&fs->count)) {
++              UNPIN(fs->pwd, fs->pwdmnt, 0);
++              UNPIN(fs->root, fs->rootmnt, 1);
+               dput(fs->root);
+               mntput(fs->rootmnt);
+               dput(fs->pwd);
+               mntput(fs->pwdmnt);
+               if (fs->altroot) {
++                      UNPIN(fs->altroot, fs->altrootmnt, 1);
+                       dput(fs->altroot);
+                       mntput(fs->altrootmnt);
+               }
+Index: linux-2.4.29/kernel/fork.c
+===================================================================
+--- linux-2.4.29.orig/kernel/fork.c    2005-04-07 18:52:37.000000000 +0300
++++ linux-2.4.29/kernel/fork.c 2005-04-07 19:14:06.000000000 +0300
+@@ -388,10 +388,13 @@
+               fs->umask = old->umask;
+               read_lock(&old->lock);
+               fs->rootmnt = mntget(old->rootmnt);
++              PIN(old->pwd, old->pwdmnt, 0);
++              PIN(old->root, old->rootmnt, 1);
+               fs->root = dget(old->root);
+               fs->pwdmnt = mntget(old->pwdmnt);
+               fs->pwd = dget(old->pwd);
+               if (old->altroot) {
++                      PIN(old->altroot, old->altrootmnt, 1);
+                       fs->altrootmnt = mntget(old->altrootmnt);
+                       fs->altroot = dget(old->altroot);
+               } else {
+Index: linux-2.4.29/kernel/ksyms.c
+===================================================================
+--- linux-2.4.29.orig/kernel/ksyms.c   2005-04-07 18:59:19.000000000 +0300
++++ linux-2.4.29/kernel/ksyms.c        2005-04-07 19:14:06.000000000 +0300
+@@ -284,6 +284,7 @@
+ EXPORT_SYMBOL(mark_page_accessed);
+ EXPORT_SYMBOL(vfs_readlink);
+ EXPORT_SYMBOL(vfs_follow_link);
++EXPORT_SYMBOL(vfs_follow_link_it);
+ EXPORT_SYMBOL(page_readlink);
+ EXPORT_SYMBOL(page_follow_link);
+ EXPORT_SYMBOL(page_symlink_inode_operations);
diff --git a/lustre/kernel_patches/patches/vfs_races-2.6-fc3.patch b/lustre/kernel_patches/patches/vfs_races-2.6-fc3.patch
new file mode 100644 (file)
index 0000000..a0b643f
--- /dev/null
@@ -0,0 +1,64 @@
+Index: linux-2.6.11/fs/dcache.c
+===================================================================
+--- linux-2.6.11.orig/fs/dcache.c      2005-04-13 22:44:37.698760922 -0400
++++ linux-2.6.11/fs/dcache.c   2005-04-13 22:44:50.482498026 -0400
+@@ -1183,7 +1183,7 @@
+       spin_unlock(&dcache_lock);
+ }
+-static void __d_rehash(struct dentry * entry, struct hlist_head *list)
++static void __d_rehash_internal(struct dentry * entry, struct hlist_head *list)
+ {
+       entry->d_flags &= ~DCACHE_UNHASHED;
+@@ -1197,15 +1197,24 @@
+  * Adds a dentry to the hash according to its name.
+  */
+  
+-void d_rehash(struct dentry * entry)
++void __d_rehash(struct dentry * entry, int lock)
+ {
+       struct hlist_head *list = d_hash(entry->d_parent, entry->d_name.hash);
+-      spin_lock(&dcache_lock);
++      if (lock)
++              spin_lock(&dcache_lock);
+       spin_lock(&entry->d_lock);
+-      __d_rehash(entry, list);
++      __d_rehash_internal(entry, list);
+       spin_unlock(&entry->d_lock);
+-      spin_unlock(&dcache_lock);
++      if (lock)
++              spin_unlock(&dcache_lock);
++}
++
++EXPORT_SYMBOL(__d_rehash);
++
++void d_rehash(struct dentry * entry)
++{
++      __d_rehash(entry, 1);
+ }
+ #define do_switch(x,y) do { \
+@@ -1308,7 +1317,7 @@
+ already_unhashed:
+       list = d_hash(target->d_parent, target->d_name.hash);
+-      __d_rehash(dentry, list);
++      __d_rehash_internal(dentry, list);
+       /* Unhash the target: dput() will then get rid of it */
+       __d_drop(target);
+Index: linux-2.6.11/include/linux/dcache.h
+===================================================================
+--- linux-2.6.11.orig/include/linux/dcache.h   2005-04-13 22:35:23.515178600 -0400
++++ linux-2.6.11/include/linux/dcache.h        2005-04-13 22:44:43.188221365 -0400
+@@ -159,6 +159,8 @@
+ #define DCACHE_REFERENCED     0x0008  /* Recently used, don't discard. */
+ #define DCACHE_UNHASHED               0x0010  
++#define DCACHE_LUSTRE_INVALID     0x0020  /* Lustre invalidated */
++
+ extern spinlock_t dcache_lock;
index 49c5c0e..4b6e21f 100644 (file)
@@ -16,3 +16,6 @@ export-show_task-2.6-vanilla.patch
 sd_iostats-2.6-rhel4.patch
 jbd-2.6.10-jcberr.patch
 hostfs_readdir_large.patch
+ext3-patch-fuzz-fixup-fc3.patch
+uml-exprt-clearuser.patch
+fsprivate-2.6.patch
index 46487e1..e523bb3 100644 (file)
@@ -14,3 +14,4 @@ lookup_bdev_init_intent.patch
 remove-suid-2.6-suse.patch
 export-show_task-2.6-vanilla.patch
 sd_iostats-2.6-rhel4.patch 
+fsprivate-2.6.patch
index 59c9be2..86a2b59 100644 (file)
@@ -9,3 +9,6 @@ export-filemap_populate.patch
 grab_cache_page_nowait_gfp-2.6-suse.patch 
 remove-suid-2.6-suse.patch
 link_notlast-susefix.patch
+uml-exprt-clearuser.patch
+qsnet-suse-2.6.patch 
+fsprivate-2.6.patch
index 0a03eda..bd67a30 100644 (file)
@@ -40,6 +40,7 @@ jbd-2.4.19-pre1-jcberr.patch
 resched-2.4.19-pre1.patch
 ext3-xattr-ptr-arith-fix.patch
 vmalloc_to_page-2.4.19-bgl.patch 
+procfs-ndynamic-2.4.patch
 ext3-truncate-buffer-head.patch
 kallsyms-2.4-bgl.patch 
 kksymoops-2.4-bgl.patch 
index 70e7b12..0980162 100644 (file)
@@ -9,3 +9,4 @@ ext3-include-fixes-2.6-rhel4.patch
 ext3-extents-2.6.9-rhel4.patch
 ext3-mballoc2-2.6.9-rhel4.patch 
 ext3-nlinks-2.6.7.patch
+ext3-htree-dot-2.6.patch
index fd05c25..c44eea3 100644 (file)
@@ -10,3 +10,4 @@ ext3-extents-2.6.5.patch
 ext3-mballoc2-2.6-suse.patch
 ext3-nlinks-2.6.7.patch
 ext3-rename-reserve-2.6-suse.patch
+ext3-htree-dot-2.6.5-suse.patch
index 471eceb..2482df4 100644 (file)
@@ -29,14 +29,13 @@ ext3-raw-lookup.patch
 nfs_export_kernel-2.4.21-chaos.patch 
 ext3-ea-in-inode-2.4.21-chaos.patch 
 listman-2.4.21-chaos.patch 
-ext3-xattr-ptr-arith-fix.patch
 pagecache-lock-2.4.21-chaos.patch 
 ext3-truncate-buffer-head.patch
 inode-max-readahead-2.4.24.patch
 dcache_refcount_debug.patch
 ext3-extents-2.4.21-chaos.patch
 ext3-extents-asyncdel-2.4.21-chaos.patch
-blkdev_tunables-2.4.21-chaos.patch
+blkdev_tunables-2.4.21-chaos.patch 
 small_scatterlist-2.4.21-rhel.patch 
 ext3-nlinks-2.4.21-chaos.patch
 sd_iostats-2.4.21-chaos.patch
@@ -46,3 +45,8 @@ export-show_task-2.4-rhel.patch
 compile-fixes-2.4.21-rhel.patch 
 grab_cache_page_nowait_gfp-rh-2.4.patch 
 remove-suid-2.4-rhel.patch
+qsnet-rhel-2.4.patch 
+nfs_statfs-toomanyfiles-rhel-2.4.patch 
+statfs64-cast-unsigned-2.4-rhel.patch
+fsprivate-2.4.patch
+nfsd_iallocsem.patch
index 20fab4c..c0187e4 100644 (file)
@@ -30,5 +30,12 @@ ext3-xattr-ptr-arith-fix.patch
 procfs-ndynamic-2.4.21-suse2.patch 
 ext3-truncate-buffer-head.patch
 loop-sync-2.4.21-suse.patch
+inode-max-readahead-2.4.24.patch
 ext3-extents-2.4.21-suse2.patch 
 ext3-extents-asyncdel-2.4.24.patch
+ext3-nlinks-2.4.21-chaos.patch
+export-show_task-2.4-cray.patch
+grab_cache_page_nowait_gfp-2.4.21-suse2.patch
+remove-suid-2.4-rhel.patch
+fsprivate-2.4-suse.patch
+nfsd_iallocsem.patch
index b444822..ddcefe4 100644 (file)
@@ -17,7 +17,6 @@ ext3-san-2.4.20.patch
 ext3-map_inode_page-2.4.21-suse2.patch 
 ext3-error-export.patch
 iopen-2.4.19-suse.patch
-tcp-zero-copy-2.4.21-suse-171.patch 
 jbd-dont-account-blocks-twice.patch
 jbd-commit-tricks.patch
 ext3-no-write-super-chaos.patch
@@ -28,3 +27,5 @@ ext3-ea-in-inode-2.4.21-sles.patch
 listman-2.4.20.patch
 ext3-truncate-buffer-head.patch
 lookup-stack-symbols-2.4.21-suse-171.patch 
+fsprivate-2.4-suse.patch
+nfsd_iallocsem.patch
index 9d16d81..a8ee3e0 100644 (file)
@@ -43,3 +43,7 @@ export-show_task-2.4-vanilla.patch
 export-zap-page-range.patch
 uml-sigusr1-2.4-vanilla.patch 
 remove-suid-2.4-rhel.patch
+uml-exprt-clearuser.patch
+fsprivate-2.4.patch
+nfsd_iallocsem.patch
+linux-2.4.24-jbd-handle-EIO.patch
diff --git a/lustre/kernel_patches/series/vanilla-2.4.29 b/lustre/kernel_patches/series/vanilla-2.4.29
new file mode 100644 (file)
index 0000000..7b38830
--- /dev/null
@@ -0,0 +1,43 @@
+configurable-x86-stack-2.4.20.patch
+configurable-x86_64-2.4.21.patch
+dev_read_only_2.4.20-rh.patch
+exports_2.4.20-rh-hp.patch
+lustre_version.patch
+vfs_intent-2.4.29-vanilla.patch 
+invalidate_show-2.4.29.patch
+export-truncate.patch
+iod-stock-exports-2.4.22.patch 
+ext3-htree-2.4.29.patch
+linux-2.4.29-xattr-0.8.54.patch 
+ext3-orphan_lock-2.4.22-rh.patch
+ext3-noread-2.4.20.patch
+ext3-delete_thread-2.4.29.patch 
+extN-wantedi.patch
+ext3-san-2.4.20.patch
+ext3-map_inode_page.patch
+ext3-error-export.patch
+iopen-2.4.20.patch
+tcp-zero-copy-2.4.22-rh.patch
+jbd-dont-account-blocks-twice.patch
+jbd-commit-tricks.patch
+ext3-no-write-super-chaos.patch
+add_page_private.patch
+nfs_export_kernel-2.4.29.patch 
+ext3-raw-lookup.patch
+ext3-ea-in-inode-2.4.29.patch
+listman-2.4.20.patch
+ext3-trusted_ea-2.4.20.patch
+ext3-xattr-ptr-arith-fix.patch
+3.5G-address-space-2.4.22-vanilla.patch
+procfs-ndynamic-2.4.patch
+ext3-truncate-buffer-head.patch
+inode-max-readahead-2.4.24.patch
+ext3-extents-2.4.29.patch
+ext3-extents-asyncdel-2.4.24.patch
+ext3-nlinks-2.4.24.patch
+export-show_task-2.4-vanilla.patch 
+export-zap-page-range.patch
+remove-suid-2.4-rhel.patch
+kallsyms-2.4.29.patch
+fsprivate-2.4.patch
+nfsd_iallocsem.patch
diff --git a/lustre/kernel_patches/series/vanilla-2.4.29-uml b/lustre/kernel_patches/series/vanilla-2.4.29-uml
new file mode 100644 (file)
index 0000000..3bd5fd2
--- /dev/null
@@ -0,0 +1,47 @@
+uml-patch-2.4.29-1.patch
+uml-2.4.20-do_mmap_pgoff-fix.patch
+uml-export-end_iomem.patch
+uml-exprt-clearuser.patch
+configurable-x86-stack-2.4.20.patch
+configurable-x86_64-2.4.21.patch
+dev_read_only_2.4.20-rh.patch
+exports_2.4.20-rh-hp.patch
+lustre_version.patch
+vfs_intent-2.4.29-vanilla.patch 
+invalidate_show-2.4.29.patch
+export-truncate.patch
+iod-stock-exports-2.4.22.patch 
+ext3-htree-2.4.29.patch
+linux-2.4.29-xattr-0.8.54.patch 
+ext3-orphan_lock-2.4.22-rh.patch
+ext3-noread-2.4.20.patch
+ext3-delete_thread-2.4.29.patch 
+extN-wantedi.patch
+ext3-san-2.4.20.patch
+ext3-map_inode_page.patch
+ext3-error-export.patch
+iopen-2.4.20.patch
+tcp-zero-copy-2.4.22-rh.patch
+jbd-dont-account-blocks-twice.patch
+jbd-commit-tricks.patch
+ext3-no-write-super-chaos.patch
+add_page_private.patch
+nfs_export_kernel-2.4.29.patch 
+ext3-raw-lookup.patch
+ext3-ea-in-inode-2.4.29.patch
+listman-2.4.20.patch
+ext3-trusted_ea-2.4.20.patch
+ext3-xattr-ptr-arith-fix.patch
+3.5G-address-space-2.4.22-vanilla.patch
+procfs-ndynamic-2.4.patch
+ext3-truncate-buffer-head.patch
+inode-max-readahead-2.4.24.patch
+ext3-extents-2.4.29.patch
+ext3-extents-asyncdel-2.4.24.patch
+ext3-nlinks-2.4.24.patch
+export-show_task-2.4-vanilla.patch 
+export-zap-page-range.patch
+remove-suid-2.4-rhel.patch
+kallsyms-2.4.29.patch
+fsprivate-2.4.patch
+nfsd_iallocsem.patch
index a44bc0f..de7cf84 100644 (file)
@@ -1,5 +1,5 @@
 lnxmaj="2.6.9"
-lnxrel="5.0.3.EL"
+lnxrel="5.0.5.EL"
 
 KERNEL=linux-${lnxmaj}-${lnxrel}.tar.bz2
 SERIES=2.6-rhel4.series
@@ -7,6 +7,7 @@ VERSION=${lnxmaj}
 EXTRA_VERSION=${lnxrel}_lustre.@VERSION@
 RHBUILD=1
 LINUX26=1
+LUSTRE_VERSION=@VERSION@
 
 BASE_ARCHS="i686 x86_64 ia64"
 BIGMEM_ARCHS=""
index 4150cd1..a67deac 100644 (file)
@@ -6,6 +6,7 @@ KERNEL=linux-$lnxmaj-$lnxrel.tar.bz2
 SERIES=2.6-suse-lnxi.series
 VERSION=$lnxmaj
 EXTRA_VERSION="${lnxrel}_lustre.@VERSION@"
+LUSTRE_VERSION=@VERSION@
 RHBUILD=0
 LINUX26=1
 SUSEBUILD=1
index 00c05df..84bd491 100644 (file)
@@ -4,6 +4,7 @@ KERNEL=linux-$lnxmaj.tar.gz
 SERIES=2.6-vanilla
 VERSION=$lnxmaj
 EXTRA_VERSION=lustre.@VERSION@
+LUSTRE_VERSION=@VERSION@
 RHBUILD=0
 
 BASE_ARCHS=""
index 620e698..28c1be3 100644 (file)
@@ -5,6 +5,7 @@ KERNEL=linux-$lnxmaj-$lnxrel.tar.gz
 SERIES=hp-pnnl-2.4.20
 VERSION=$lnxmaj
 EXTRA_VERSION=$lnxrel_lustre.@VERSION@
+LUSTRE_VERSION=@VERSION@
 RHBUILD=0
 
 BASE_ARCHS="ia64"
index fa9140d..d27ed40 100644 (file)
@@ -5,6 +5,7 @@ KERNEL=linux-${lnxmaj}-${lnxrel}.tar.gz
 SERIES=rh-2.4.20
 VERSION=$lnxmaj
 EXTRA_VERSION=${lnxrel}_lustre.@VERSION@
+LUSTRE_VERSION=@VERSION@
 RHBUILD=1
 
 BASE_ARCHS="i686"
index b8ad58a..8982d8f 100644 (file)
@@ -1,9 +1,10 @@
 lnxmaj="2.4.21"
-lnxrel="27.0.2.EL"
+lnxrel="32.0.1.EL"
 
 KERNEL=linux-${lnxmaj}-${lnxrel}.tar.bz2
 SERIES=rhel-2.4.21
 VERSION=${lnxmaj}
+LUSTRE_VERSION=@VERSION@
 EXTRA_VERSION=${lnxrel}_lustre.@VERSION@
 RHBUILD=1
 
@@ -14,7 +15,7 @@ JENSEN_ARCHS=""
 SMP_ARCHS="i686 x86_64 ia64"
 UP_ARCHS=""
 
-# the modules in this kernel do not build with gcc 3.3 or 2.96
+# the modules in this kernel do not build with gcc 4, 3.4, or 2.96
 for cc in gcc32 gcc33 ; do
     if which $cc >/dev/null 2>/dev/null ; then
         export CC=$cc
index b59fc56..784560a 100644 (file)
@@ -5,6 +5,7 @@ KERNEL=linux-${lnxmaj}-${lnxrel}.tar.bz2
 SERIES=suse-2.4.21-jvn
 VERSION=${lnxmaj}
 EXTRA_VERSION=${lnxrel}_lustre.@VERSION@
+LUSTRE_VERSION=@VERSION@
 RHBUILD=0
 LINUX26=0
 SUSEBUILD=1
index 245c085..d00ca78 100644 (file)
@@ -2,6 +2,7 @@ KERNEL=linux-2.4.21-x86_64.tar.gz
 SERIES=suse-2.4.21-2
 VERSION=2.4.21
 EXTRA_VERSION=lustre.@VERSION@
+LUSTRE_VERSION=@VERSION@
 RHBUILD=0
 
 BASE_ARCHS="x86_64"
index be51da2..7236410 100644 (file)
@@ -1,6 +1,6 @@
 default: all
 
-MODULES := ldiskfs quotafmt_test
+MODULES := ldiskfs #quotafmt_test
 
 # copy makefile over to not break patches
 ext3_extra := $(wildcard @LINUX@/fs/ext3/Makefile)
@@ -11,13 +11,13 @@ linux_headers := $(wildcard @LINUX@/include/linux/ext3*.h)
 ext3_sources := $(filter-out %.mod.c,$(wildcard @LINUX@/fs/ext3/*.c))
 new_sources := iopen.c iopen.h extents.c mballoc.c
 new_headers := ext3_extents.h
-quotafmt_sources := lustre_quota_fmt.c
-quotafmt_headers := lustre_quota_fmt.h
+#quotafmt_sources := lustre_quota_fmt.c
+#quotafmt_headers := lustre_quota_fmt.h
 ldiskfs_patched_sources := $(notdir $(ext3_sources) $(ext3_headers)) $(new_sources) $(new_headers)
-ldiskfs_sources := $(ldiskfs_patched_sources) $(quotafmt_sources) $(quotafmt_headers)
+ldiskfs_sources := $(ldiskfs_patched_sources) #$(quotafmt_sources) $(quotafmt_headers)
 
 ldiskfs-objs := $(filter %.o,$(ldiskfs_sources:.c=.o))
-quotafmt-objs := quotafmt_test.o
+#quotafmt-objs := quotafmt_test.o
 
 EXTRA_PRE_CFLAGS := -I@LINUX@/fs -I@LUSTRE@ -I@LUSTRE@/ldiskfs
 
index 4f9e784..53ca41e 100644 (file)
@@ -41,10 +41,12 @@ if USE_QUILT
        cd linux-stage && quilt setup -l ../$(series) -d ../$(patches)
        cd linux-stage && quilt push -a -q
 else
+       @echo -n "Applying ext3 patches:"
        @cd linux-stage && for i in $$(<../$(series)) ; do \
-               echo "patch -p1 < ../$(patches)/$$i" ; \
-               patch -p1 < ../$(patches)/$$i || exit 1 ; \
+               echo -n " $$i" ; \
+               patch -s -p1 < ../$(patches)/$$i || exit 1 ; \
        done
+       @echo
 endif
        mkdir linux
        @echo -n "Replacing 'ext3' with 'ldiskfs':"
@@ -70,7 +72,7 @@ foo-check:
        @echo "ldiskfs_OBJECTS: $(ldiskfs_OBJECTS)"
        @echo "ldiskfs_LDADD: $(ldiskfs_LDADD)"
 
-MOSTLYCLEANFILES = *.o *.ko *.mod.c
+MOSTLYCLEANFILES := @MOSTLYCLEANFILES@ 
 CLEANFILES = sources $(notdir $(linux_headers) $(ext3_headers) $(ext3_sources) $(new_sources) $(new_headers))
 
 EXTRA_DIST := lustre_quota_fmt.c lustre_quota_fmt.h quotafmt_test.c
index 84aa304..cf2a23f 100644 (file)
@@ -1,4 +1,6 @@
-/*
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
  * Lustre administrative quota format.
  *
  *  from
@@ -40,7 +42,7 @@ int lustre_check_quota_file(struct lustre_quota_info *lqi, int type)
        loff_t offset = 0;
        static const uint quota_magics[] = LUSTRE_INITQMAGICS;
        static const uint quota_versions[] = LUSTRE_INITQVERSIONS;
+
        fs = get_fs();
        set_fs(KERNEL_DS);
        size = f->f_op->read(f, (char *)&dqhead, sizeof(struct lustre_disk_dqheader), &offset);
@@ -212,10 +214,10 @@ out_buf:
 }
 
 /* Insert empty block to the list */
-static int put_free_dqblk(struct file *filp, struct lustre_mem_dqinfo *info, 
+static int put_free_dqblk(struct file *filp, struct lustre_mem_dqinfo *info,
                          dqbuf_t buf, uint blk)
 {
-       struct lustre_disk_dqdbheader *dh = (struct lustre_disk_dqdbheader *)buf;
+       struct lustre_disk_dqdbheader *dh =(struct lustre_disk_dqdbheader *)buf;
        int err;
 
        dh->dqdh_next_free = cpu_to_le32(info->dqi_free_blk);
@@ -223,7 +225,8 @@ static int put_free_dqblk(struct file *filp, struct lustre_mem_dqinfo *info,
        dh->dqdh_entries = cpu_to_le16(0);
        info->dqi_free_blk = blk;
        lustre_mark_info_dirty(info);
-       if ((err = write_blk(filp, blk, buf)) < 0)      /* Some strange block. We had better leave it... */
+       if ((err = write_blk(filp, blk, buf)) < 0)
+               /* Some strange block. We had better leave it... */
                return err;
        return 0;
 }
@@ -536,7 +539,8 @@ static int remove_tree(struct lustre_dquot *dquot, uint *blk, int depth)
                int i;
                ref[GETIDINDEX(dquot->dq_id, depth)] = cpu_to_le32(0);
                for (i = 0; i < LUSTRE_DQBLKSIZE && !buf[i]; i++);      /* Block got empty? */
-               if (i == LUSTRE_DQBLKSIZE) {
+               /* don't put the root block into free blk list! */
+               if (i == LUSTRE_DQBLKSIZE && *blk != LUSTRE_DQTREEOFF) {
                        put_free_dqblk(filp, info, buf, *blk);
                        *blk = 0;
                }
@@ -550,6 +554,7 @@ out_buf:
 }
 
 /* Delete dquot from tree */
+#ifndef        QFMT_NO_DELETE
 static int lustre_delete_dquot(struct lustre_dquot *dquot)
 {
        uint tmp = LUSTRE_DQTREEOFF;
@@ -558,6 +563,7 @@ static int lustre_delete_dquot(struct lustre_dquot *dquot)
                return 0;
        return remove_tree(dquot, &tmp, 0);
 }
+#endif
 
 /* Find entry in block */
 static loff_t find_block_dqentry(struct lustre_dquot *dquot, uint blk)
@@ -669,7 +675,8 @@ int lustre_read_dquot(struct lustre_dquot *dquot)
                        /* We need to escape back all-zero structure */
                        memset(&empty, 0, sizeof(struct lustre_disk_dqblk));
                        empty.dqb_itime = cpu_to_le64(1);
-                       if (!memcmp(&empty, &ddquot, sizeof(struct lustre_disk_dqblk)))
+                       if (!memcmp(&empty, &ddquot,
+                                   sizeof(struct lustre_disk_dqblk)))
                                ddquot.dqb_itime = 0;
                }
                set_fs(fs);
@@ -683,24 +690,19 @@ int lustre_read_dquot(struct lustre_dquot *dquot)
 int lustre_commit_dquot(struct lustre_dquot *dquot)
 {
        int rc = 0;
-       /* We clear the flag everytime so we don't loop when there was an IO error... */
+       /* always clear the flag so we don't loop on an IO error... */
        clear_bit(DQ_MOD_B, &dquot->dq_flags);
 
-       /* The block/inode usage in admin quotafile isn't the real usage over all cluster,
-        * so keep the fake dquot entry on disk is meaningless, just remove it */
-#ifndef        QFMT_NO_DELETE
+       /* The block/inode usage in admin quotafile isn't the real usage
+        * over all cluster, so keep the fake dquot entry on disk is
+        * meaningless, just remove it */
        if (test_bit(DQ_FAKE_B, &dquot->dq_flags))
                rc = lustre_delete_dquot(dquot);
-       }
-       else {
+       else
                rc = lustre_write_dquot(dquot);
-       }
-#else
-       rc = lustre_write_dquot(dquot);
-#endif
        if (rc < 0)
                return rc;
-       
+
        if (lustre_info_dirty(&dquot->dq_info->qi_info[dquot->dq_type]))
                rc = lustre_write_quota_info(dquot->dq_info, dquot->dq_type);
 
@@ -723,7 +725,7 @@ int lustre_init_quota_info(struct lustre_quota_info *lqi, int type)
        /* write quotafile header */
        dqhead.dqh_magic = cpu_to_le32(quota_magics[type]);
        dqhead.dqh_version = cpu_to_le32(quota_versions[type]);
-       size = fp->f_op->write(fp, (char *)&dqhead, 
+       size = fp->f_op->write(fp, (char *)&dqhead,
                               sizeof(struct lustre_disk_dqheader), &offset);
        
        if (size != sizeof(struct lustre_disk_dqheader)) {
index 2c76acb..53985d2 100644 (file)
@@ -1,4 +1,9 @@
-/* Kernel module to test lustre administrative quotafile format APIs
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ *   No redistribution or use is permitted outside of Cluster File Systems, Inc.
+ *
+ * Kernel module to test lustre administrative quotafile format APIs
  * from the OBD setup function */
 #ifndef EXPORT_SYMTAB
 # define EXPORT_SYMTAB
 
 char *test_quotafile[2] = {"usrquota_test", "grpquota_test"};
 
-static int quotfmt_initialize(struct lustre_quota_info *lqi, struct obd_device *tgt, 
-                             struct obd_run_ctxt *saved)
+static int quotfmt_initialize(struct lustre_quota_info *lqi,
+                              struct obd_device *tgt,
+                             struct lvfs_run_ctxt *saved)
 {
        struct lustre_disk_dqheader dqhead;
        static const uint quota_magics[] = LUSTRE_INITQMAGICS;
        static const uint quota_versions[] = LUSTRE_INITQVERSIONS;
        struct file *fp;
-       struct inode *parent_inode = tgt->obd_ctxt.pwd->d_inode;
+       struct inode *parent_inode = tgt->obd_lvfs_ctxt.pwd->d_inode;
        size_t size;
        struct dentry *de;
        int i, rc = 0;
        ENTRY;
-       
-       push_ctxt(saved, &tgt->obd_ctxt, NULL);
+
+       push_ctxt(saved, &tgt->obd_lvfs_ctxt, NULL);
 
        sema_init(&lqi->qi_sem, 1);
 
@@ -42,13 +48,13 @@ static int quotfmt_initialize(struct lustre_quota_info *lqi, struct obd_device *
 
                /* remove the stale test quotafile */
                down(&parent_inode->i_sem);
-               de = lookup_one_len(name, tgt->obd_ctxt.pwd, namelen);
+               de = lookup_one_len(name, tgt->obd_lvfs_ctxt.pwd, namelen);
                if (!IS_ERR(de) && de->d_inode)
                        vfs_unlink(parent_inode, de);
                if (!IS_ERR(de))
                        dput(de);
                up(&parent_inode->i_sem);
-               
+
                /* create quota file */
                fp = filp_open(name, O_CREAT | O_EXCL, 0644);
                if (IS_ERR(fp)) {
@@ -58,15 +64,15 @@ static int quotfmt_initialize(struct lustre_quota_info *lqi, struct obd_device *
                        break;
                }
                lqi->qi_files[i] = fp;
-               
+
                /* write quotafile header */
                dqhead.dqh_magic = cpu_to_le32(quota_magics[i]);
                dqhead.dqh_version = cpu_to_le32(quota_versions[i]);
-               size = fp->f_op->write(fp, (char *)&dqhead, 
-                                      sizeof(struct lustre_disk_dqheader), 
+               size = fp->f_op->write(fp, (char *)&dqhead,
+                                      sizeof(struct lustre_disk_dqheader),
                                       &offset);
                if (size != sizeof(struct lustre_disk_dqheader)) {
-                       CERROR("error writing quoafile header %s (rc = %d)\n", 
+                       CERROR("error writing quoafile header %s (rc = %d)\n",
                               name, rc);
                        rc = size;
                        break;
@@ -76,11 +82,12 @@ static int quotfmt_initialize(struct lustre_quota_info *lqi, struct obd_device *
        RETURN(rc);
 }
 
-static int quotfmt_finalize(struct lustre_quota_info *lqi, struct obd_device *tgt, 
-                           struct obd_run_ctxt *saved)
+static int quotfmt_finalize(struct lustre_quota_info *lqi,
+                            struct obd_device *tgt,
+                           struct lvfs_run_ctxt *saved)
 {
        struct dentry *de;
-       struct inode *parent_inode = tgt->obd_ctxt.pwd->d_inode;
+       struct inode *parent_inode = tgt->obd_lvfs_ctxt.pwd->d_inode;
        int i, rc = 0;
        ENTRY;
 
@@ -90,21 +97,21 @@ static int quotfmt_finalize(struct lustre_quota_info *lqi, struct obd_device *tg
 
                if (lqi->qi_files[i] == NULL)
                        continue;
-               
+
                /* close quota file */
                filp_close(lqi->qi_files[i], 0);
-               
+
                /* unlink quota file */
                down(&parent_inode->i_sem);
-               
-               de = lookup_one_len(name, tgt->obd_ctxt.pwd, namelen);
+
+               de = lookup_one_len(name, tgt->obd_lvfs_ctxt.pwd, namelen);
                if (IS_ERR(de) || de->d_inode == NULL) {
                        rc = IS_ERR(de) ? PTR_ERR(de) : -ENOENT;
-                       CERROR("error lookup quotafile %s (rc = %d)\n", 
+                       CERROR("error lookup quotafile %s (rc = %d)\n",
                                name, rc);
                        goto dput;
                }
-               
+
                rc = vfs_unlink(parent_inode, de);
                if (rc)
                        CERROR("error unlink quotafile %s (rc = %d)\n",
@@ -115,7 +122,7 @@ dput:
                up(&parent_inode->i_sem);
        }
 
-       pop_ctxt(saved, &tgt->obd_ctxt, NULL);
+       pop_ctxt(saved, &tgt->obd_lvfs_ctxt, NULL);
        RETURN(rc);
 }
 
@@ -123,7 +130,7 @@ static int quotfmt_test_1(struct lustre_quota_info *lqi)
 {
        int i;
        ENTRY;
-       
+
        for (i = 0; i < MAXQUOTAS; i++) {
                if (!lustre_check_quota_file(lqi, i))
                        RETURN(-EINVAL);
@@ -141,9 +148,9 @@ static void print_quota_info(struct lustre_quota_info *lqi)
                dqinfo = &lqi->qi_info[i];
                printk("%s quota info:\n", i == USRQUOTA ? "user " : "group");
                printk("dqi_bgrace(%u) dqi_igrace(%u) dqi_flags(%lu) dqi_blocks(%u) "
-                      "dqi_free_blk(%u) dqi_free_entry(%u)\n", 
-                      dqinfo->dqi_bgrace, dqinfo->dqi_igrace, dqinfo->dqi_flags, 
-                      dqinfo->dqi_blocks, dqinfo->dqi_free_blk, 
+                      "dqi_free_blk(%u) dqi_free_entry(%u)\n",
+                      dqinfo->dqi_bgrace, dqinfo->dqi_igrace, dqinfo->dqi_flags,
+                      dqinfo->dqi_blocks, dqinfo->dqi_free_blk,
                       dqinfo->dqi_free_entry);
        }
 #endif
@@ -156,7 +163,7 @@ static int quotfmt_test_2(struct lustre_quota_info *lqi)
 
        for (i = 0; i < MAXQUOTAS; i++) {
                struct lustre_mem_dqinfo dqinfo;
-               
+
                rc = lustre_init_quota_info(lqi, i);
                if (rc) {
                        CERROR("init quotainfo(%d) failed! (rc:%d)\n", i, rc);
@@ -169,7 +176,7 @@ static int quotfmt_test_2(struct lustre_quota_info *lqi)
                        CERROR("read quotainfo(%d) failed! (rc:%d)\n", i, rc);
                        break;
                }
-               
+
                if(memcmp(&dqinfo, &lqi->qi_info[i], sizeof(dqinfo))) {
                        rc = -EINVAL;
                        break;
@@ -203,7 +210,7 @@ static struct lustre_dquot *get_rand_dquot(struct lustre_quota_info *lqi)
        dquot->dq_dqb.dqb_curinodes = rand / 3;
        dquot->dq_dqb.dqb_btime = jiffies;
        dquot->dq_dqb.dqb_itime = jiffies;
-       
+
        return dquot;
 }
 
@@ -218,7 +225,7 @@ static int write_check_dquot(struct lustre_quota_info *lqi)
        struct mem_dqblk dqblk;
        int rc = 0;
        ENTRY;
-       
+
        dquot = get_rand_dquot(lqi);
        if (dquot == NULL)
                RETURN(-ENOMEM);
@@ -260,14 +267,11 @@ static int quotfmt_test_3(struct lustre_quota_info *lqi)
        int i = 0, rc = 0;
        ENTRY;
 
-#ifdef QFMT_NO_DELETE
-       RETURN(0);
-#endif
        dquot = get_rand_dquot(lqi);
        if (dquot == NULL)
                RETURN(-ENOMEM);
 repeat:
-       clear_bit(DQ_FAKE_B, dquot->dq_flags);
+       clear_bit(DQ_FAKE_B, &dquot->dq_flags);
        /* write a new dquot */
        rc = lustre_commit_dquot(dquot);
        if (rc) {
@@ -283,11 +287,11 @@ repeat:
                CERROR("read dquot failed! (rc:%d)\n", rc);
                GOTO(out, rc);
        }
-       if (!dquot->dq_off || test_bit(DQ_FAKE_B, dquot->dq_flags)) {
+       if (!dquot->dq_off || test_bit(DQ_FAKE_B, &dquot->dq_flags)) {
                CERROR("the dquot isn't committed\n");
                GOTO(out, rc = -EINVAL);
        }
-       
+
        /* remove this dquot */
        set_bit(DQ_FAKE_B, &dquot->dq_flags);
        dquot->dq_dqb.dqb_curspace = 0;
@@ -314,9 +318,9 @@ repeat:
        /* check if this dquot can be write again */
        if (++i < 2)
                goto repeat;
-       
-       print_quota_info(lqi);
-                       
+
+        print_quota_info(lqi);
+
 out:
        put_rand_dquot(dquot);
        RETURN(rc);
@@ -330,7 +334,7 @@ static int quotfmt_test_4(struct lustre_quota_info *lqi)
        for (i = 0; i < 30000; i++) {
                rc = write_check_dquot(lqi);
                if (rc) {
-                       CERROR("write/check dquot failed at %d! (rc:%d)\n", 
+                       CERROR("write/check dquot failed at %d! (rc:%d)\n",
                                i, rc);
                        break;
                }
@@ -341,17 +345,17 @@ static int quotfmt_test_4(struct lustre_quota_info *lqi)
 
 static int quotfmt_run_tests(struct obd_device *obd, struct obd_device *tgt)
 {
-        struct obd_run_ctxt saved;
+        struct lvfs_run_ctxt saved;
        struct lustre_quota_info *lqi = NULL;
        int rc = 0;
        ENTRY;
-       
+
        OBD_ALLOC(lqi, sizeof(*lqi));
        if (lqi == NULL) {
                CERROR("not enough memory\n");
                RETURN(-ENOMEM);
        }
-       
+
        CWARN("=== Initialize quotafile test\n");
        rc = quotfmt_initialize(lqi, tgt, &saved);
        if (rc)
@@ -434,16 +438,18 @@ static struct obd_ops quotfmt_obd_ops = {
         .o_cleanup     = quotfmt_test_cleanup,
 };
 
+#ifdef LPROCFS
 static struct lprocfs_vars lprocfs_obd_vars[] = { {0} };
 static struct lprocfs_vars lprocfs_module_vars[] = { {0} };
 LPROCFS_INIT_VARS(quotfmt_test, lprocfs_module_vars, lprocfs_obd_vars)
+#endif
 
 static int __init quotfmt_test_init(void)
 {
         struct lprocfs_static_vars lvars;
 
         lprocfs_init_vars(quotfmt_test, &lvars);
-        return class_register_type(&quotfmt_obd_ops, lvars.module_vars, 
+        return class_register_type(&quotfmt_obd_ops, lvars.module_vars,
                                   "quotfmt_test");
 }
 
index 5c3cc01..2b9856c 100644 (file)
@@ -7,7 +7,7 @@
 # ldlm is built into ptlrpc
 #
 
-MOSTLYCLEANFILES = *.o *.ko *.mod.c
+MOSTLYCLEANFILES := @MOSTLYCLEANFILES@ 
 DIST_SOURCES = ldlm_extent.c ldlm_flock.c ldlm_internal.h ldlm_lib.c \
        ldlm_lock.c ldlm_lockd.c ldlm_plain.c ldlm_request.c         \
        ldlm_resource.c l_lock.c
index 1d87959..0cd1240 100644 (file)
@@ -147,10 +147,15 @@ static void ldlm_extent_policy(struct ldlm_resource *res,
 
 /* Determine if the lock is compatible with all locks on the queue.
  * We stop walking the queue if we hit ourselves so we don't take
- * conflicting locks enqueued after us into accound, or we'd wait forever. */
+ * conflicting locks enqueued after us into accound, or we'd wait forever.
+ * 0 if the lock is not compatible
+ * 1 if the lock is compatible
+ * 2 if this group lock is compatible and requires no further checking
+ * negative error, such as EWOULDBLOCK for group locks
+ */
 static int
 ldlm_extent_compat_queue(struct list_head *queue, struct ldlm_lock *req,
-                         int send_cbs)
+                         int send_cbs, int *flags, ldlm_error_t *err)
 {
         struct list_head *tmp;
         struct ldlm_lock *lock;
@@ -158,6 +163,7 @@ ldlm_extent_compat_queue(struct list_head *queue, struct ldlm_lock *req,
         __u64 req_start = req->l_req_extent.start;
         __u64 req_end = req->l_req_extent.end;
         int compat = 1;
+        int scan = 0;
         ENTRY;
 
         lockmode_verify(req_mode);
@@ -168,14 +174,111 @@ ldlm_extent_compat_queue(struct list_head *queue, struct ldlm_lock *req,
                 if (req == lock)
                         RETURN(compat);
 
+                if (unlikely(scan)) {
+                        /* We only get here if we are queuing GROUP lock
+                           and met some incompatible one. The main idea of this
+                           code is to insert GROUP lock past compatible GROUP
+                           lock in the waiting queue or if there is not any,
+                           then in front of first non-GROUP lock */
+                        if (lock->l_req_mode != LCK_GROUP) {
+                        /* Ok, we hit non-GROUP lock, there should be no
+                           more GROUP locks later on, queue in front of
+                           first non-GROUP lock */
+
+                                ldlm_resource_insert_lock_after(lock, req);
+                                list_del_init(&lock->l_res_link);
+                                ldlm_resource_insert_lock_after(req, lock);
+                                RETURN(0);
+                        }
+                        if (req->l_policy_data.l_extent.gid ==
+                             lock->l_policy_data.l_extent.gid) {
+                                /* found it */
+                                ldlm_resource_insert_lock_after(lock,
+                                                                req);
+                                RETURN(0);
+                        }
+                        continue;
+                }
+
                 /* locks are compatible, overlap doesn't matter */
-                if (lockmode_compat(lock->l_req_mode, req_mode))
+                if (lockmode_compat(lock->l_req_mode, req_mode)) {
+                       /* non-group locks are compatible, overlap doesn't
+                          matter */
+                        if (likely(req_mode != LCK_GROUP))
+                                continue;
+                                
+                        /* If we are trying to get a GROUP lock and there is
+                           another one of this kind, we need to compare gid */
+                        if (req->l_policy_data.l_extent.gid ==
+                            lock->l_policy_data.l_extent.gid) {
+                                /* If existing lock with matched gid is granted,
+                                   we grant new one too. */
+                                if (lock->l_req_mode == lock->l_granted_mode)
+                                        RETURN(2);
+
+                                /* Otherwise we are scanning queue of waiting
+                                 * locks and it means current request would
+                                 * block along with existing lock (that is
+                                 * already blocked.
+                                 * If we are in nonblocking mode - return
+                                 * immediately */
+                                if (*flags & LDLM_FL_BLOCK_NOWAIT) {
+                                        compat = -EWOULDBLOCK;
+                                        goto destroylock;
+                                }
+                                /* If this group lock is compatible with another
+                                 * group lock on the waiting list, they must be
+                                 * together in the list, so they can be granted
+                                 * at the same time.  Otherwise the later lock
+                                 * can get stuck behind another, incompatible,
+                                 * lock. */
+                                ldlm_resource_insert_lock_after(lock, req);
+                                /* Because 'lock' is not granted, we can stop
+                                 * processing this queue and return immediately.
+                                 * There is no need to check the rest of the
+                                 * list. */
+                                RETURN(0);
+                        }
+                }
+
+                if (unlikely(req_mode == LCK_GROUP &&
+                    (lock->l_req_mode != lock->l_granted_mode))) {
+                        scan = 1;
+                        compat = 0;
+                        if (lock->l_req_mode != LCK_GROUP) {
+                        /* Ok, we hit non-GROUP lock, there should be no
+                           more GROUP locks later on, queue in front of
+                           first non-GROUP lock */
+                                
+                                ldlm_resource_insert_lock_after(lock, req);
+                                list_del_init(&lock->l_res_link);
+                                ldlm_resource_insert_lock_after(req, lock);
+                                RETURN(0);
+                        }  
+                        if (req->l_policy_data.l_extent.gid ==
+                             lock->l_policy_data.l_extent.gid) {
+                                /* found it */
+                                ldlm_resource_insert_lock_after(lock, req);
+                                RETURN(0);
+                        }
                         continue;
+                }
 
-                /* if lock doesn't overlap skip it */
-                if (lock->l_policy_data.l_extent.end < req_start ||
-                    lock->l_policy_data.l_extent.start > req_end)
+                if (unlikely(lock->l_req_mode == LCK_GROUP)) {
+                        /* If compared lock is GROUP,then requested is PR/PW/ =>
+                         * this is not compatible; extent range does not
+                         * matter */
+                        if (*flags & LDLM_FL_BLOCK_NOWAIT) {
+                                compat = -EWOULDBLOCK;
+                                goto destroylock;
+                        } else {
+                                *flags |= LDLM_FL_NO_TIMEOUT;
+                        }
+                } else if (lock->l_policy_data.l_extent.end < req_start ||
+                           lock->l_policy_data.l_extent.start > req_end) {
+                        /* if a non group lock doesn't overlap skip it */
                         continue;
+                }
 
                 if (!send_cbs)
                         RETURN(0);
@@ -185,6 +288,11 @@ ldlm_extent_compat_queue(struct list_head *queue, struct ldlm_lock *req,
                         ldlm_add_ast_work_item(lock, req, NULL, 0);
         }
 
+        return(compat);
+destroylock:
+        list_del_init(&req->l_res_link);
+        ldlm_lock_destroy(req);
+        *err = compat;
         RETURN(compat);
 }
 
@@ -202,18 +310,21 @@ int ldlm_process_extent_lock(struct ldlm_lock *lock, int *flags, int first_enq,
 {
         struct ldlm_resource *res = lock->l_resource;
         struct list_head rpc_list = LIST_HEAD_INIT(rpc_list);
-        int rc;
+        int rc, rc2;
         ENTRY;
 
         LASSERT(list_empty(&res->lr_converting));
+        *err = ELDLM_OK;
 
         if (!first_enq) {
                 LASSERT(res->lr_tmp != NULL);
-                rc = ldlm_extent_compat_queue(&res->lr_granted, lock, 0);
-                if (!rc)
-                        RETURN(LDLM_ITER_STOP);
-                rc = ldlm_extent_compat_queue(&res->lr_waiting, lock, 0);
-                if (!rc)
+                rc = ldlm_extent_compat_queue(&res->lr_granted, lock, 0, flags,
+                                              err);
+                if (rc == 1) {
+                        rc = ldlm_extent_compat_queue(&res->lr_waiting, lock, 0,
+                                                      flags, err);
+                }
+                if (rc == 0)
                         RETURN(LDLM_ITER_STOP);
 
                 ldlm_resource_unlink_lock(lock);
@@ -226,12 +337,26 @@ int ldlm_process_extent_lock(struct ldlm_lock *lock, int *flags, int first_enq,
  restart:
         LASSERT(res->lr_tmp == NULL);
         res->lr_tmp = &rpc_list;
-        rc = ldlm_extent_compat_queue(&res->lr_granted, lock, 1);
-        rc += ldlm_extent_compat_queue(&res->lr_waiting, lock, 1);
+        rc = ldlm_extent_compat_queue(&res->lr_granted, lock, 1, flags, err);
+        if (rc < 0)
+                GOTO(out, rc); /* lock was destroyed */
+        if (rc == 2) {
+                res->lr_tmp = NULL;
+                goto grant;
+        }
+
+        rc2 = ldlm_extent_compat_queue(&res->lr_waiting, lock, 1, flags, err);
+        if (rc2 < 0)
+                GOTO(out, rc = rc2); /* lock was destroyed */
         res->lr_tmp = NULL;
 
-        if (rc != 2) {
-                /* If either of the compat_queue()s returned 0, then we
+        if (rc + rc2 == 2) {
+        grant:
+                ldlm_extent_policy(res, lock, flags);
+                ldlm_resource_unlink_lock(lock);
+                ldlm_grant_lock(lock, NULL, 0, 0);
+        } else {
+                /* If either of the compat_queue()s returned failure, then we
                  * have ASTs to send and must go onto the waiting list.
                  *
                  * bug 2322: we used to unlink and re-add here, which was a
@@ -245,12 +370,11 @@ int ldlm_process_extent_lock(struct ldlm_lock *lock, int *flags, int first_enq,
                 if (rc == -ERESTART)
                         GOTO(restart, -ERESTART);
                 *flags |= LDLM_FL_BLOCK_GRANTED;
-        } else {
-                ldlm_extent_policy(res, lock, flags);
-                ldlm_resource_unlink_lock(lock);
-                ldlm_grant_lock(lock, NULL, 0, 0);
         }
-        RETURN(0);
+        rc = 0;
+out:
+        res->lr_tmp = NULL;
+        RETURN(rc);
 }
 
 /* When a lock is cancelled by a client, the KMS may undergo change if this
index f4d660a..b4bae36 100644 (file)
@@ -31,6 +31,7 @@
 #include <libcfs/list.h>
 #else
 #include <liblustre.h>
+#include <linux/obd_class.h>
 #endif
 
 #include "ldlm_internal.h"
@@ -362,9 +363,11 @@ ldlm_process_flock_lock(struct ldlm_lock *req, int *flags, int first_enq,
                 break;
         }
 
+        /* At this point we're granting the lock request. */
+        req->l_granted_mode = req->l_req_mode;
+
         /* Add req to the granted queue before calling ldlm_reprocess_all(). */
         if (!added) {
-                req->l_granted_mode = req->l_req_mode;
                 list_del_init(&req->l_res_link);
                 /* insert new lock before ownlocks in list. */
                 ldlm_resource_add_lock(res, ownlocks, req);
@@ -431,6 +434,9 @@ ldlm_flock_interrupted_wait(void *data)
         /* take lock off the deadlock detection waitq. */
         list_del_init(&lock->l_flock_waitq);
 
+        /* client side - set flag to prevent lock from being put on lru list */
+        lock->l_flags |= LDLM_FL_CBPENDING;
+
         ldlm_lock_decref_internal(lock, lock->l_req_mode);
         ldlm_lock2handle(lock, &lockh);
         ldlm_cli_cancel(&lockh);
@@ -456,11 +462,6 @@ ldlm_flock_completion_ast(struct ldlm_lock *lock, int flags, void *data)
 
         LASSERT(flags != LDLM_FL_WAIT_NOREPROC);
 
-        if (flags == 0) {
-                wake_up(&lock->l_waitq);
-                RETURN(0);
-        }
-
         if (!(flags & (LDLM_FL_BLOCK_WAIT | LDLM_FL_BLOCK_GRANTED |
                        LDLM_FL_BLOCK_CONV)))
                 goto  granted;
@@ -468,8 +469,6 @@ ldlm_flock_completion_ast(struct ldlm_lock *lock, int flags, void *data)
         LDLM_DEBUG(lock, "client-side enqueue returned a blocked lock, "
                    "sleeping");
 
-        ldlm_lock_dump(D_OTHER, lock, 0);
-
         fwd.fwd_lock = lock;
         obd = class_exp2obd(lock->l_conn_export);
 
@@ -490,17 +489,12 @@ ldlm_flock_completion_ast(struct ldlm_lock *lock, int flags, void *data)
                           ((lock->l_req_mode == lock->l_granted_mode) ||
                            lock->l_destroyed), &lwi);
 
-        if (rc) {
-                LDLM_DEBUG(lock, "client-side enqueue waking up: failed (%d)",
-                           rc);
-                RETURN(rc);
-        }
-
-        LASSERT(!(lock->l_destroyed));
-
+        LDLM_DEBUG(lock, "client-side enqueue waking up: rc = %d", rc);
+        RETURN(rc);
 granted:
 
-        LDLM_DEBUG(lock, "client-side enqueue waking up");
+        LDLM_DEBUG(lock, "client-side enqueue granted");
         ns = lock->l_resource->lr_namespace;
         l_lock(&ns->ns_lock);
 
@@ -529,14 +523,18 @@ granted:
                 getlk->fl_start = lock->l_policy_data.l_flock.start;
                 getlk->fl_end = lock->l_policy_data.l_flock.end;
         } else {
+                int noreproc = LDLM_FL_WAIT_NOREPROC;
+
                 /* We need to reprocess the lock to do merges or splits
                  * with existing locks owned by this process. */
-                flags = LDLM_FL_WAIT_NOREPROC;
-                ldlm_process_flock_lock(lock, &flags, 1, &err);
+                ldlm_process_flock_lock(lock, &noreproc, 1, &err);
+                if (flags == 0)
+                        wake_up(&lock->l_waitq);
         }
         l_unlock(&ns->ns_lock);
         RETURN(0);
 }
+EXPORT_SYMBOL(ldlm_flock_completion_ast);
 
 int ldlm_flock_blocking_ast(struct ldlm_lock *lock, struct ldlm_lock_desc *desc,
                             void *data, int flag)
index ccaafdc..2967ab8 100644 (file)
@@ -10,6 +10,10 @@ typedef enum {
 
 int ldlm_cancel_lru(struct ldlm_namespace *ns, ldlm_sync_t sync);
 
+/* ldlm_resource.c */
+void ldlm_resource_insert_lock_after(struct ldlm_lock *original,
+                                     struct ldlm_lock *new);
+
 /* ldlm_lock.c */
 void ldlm_grant_lock(struct ldlm_lock *lock, void *data, int datalen,
                      int run_ast);
index 4a1fe80..7338654 100644 (file)
@@ -71,7 +71,6 @@ static int import_set_conn(struct obd_import *imp, struct obd_uuid *uuid,
                         if (priority) {
                                 list_del(&item->oic_item);
                                 list_add(&item->oic_item, &imp->imp_conn_list);
-                                item->oic_last_attempt = 0;
                         }
                         CDEBUG(D_HA, "imp %p@%s: found existing conn %s%s\n",
                                imp, imp->imp_obd->obd_name, uuid->uuid,
@@ -84,7 +83,6 @@ static int import_set_conn(struct obd_import *imp, struct obd_uuid *uuid,
         if (create) {
                 imp_conn->oic_conn = ptlrpc_conn;
                 imp_conn->oic_uuid = *uuid;
-                imp_conn->oic_last_attempt = 0;
                 if (priority)
                         list_add(&imp_conn->oic_item, &imp->imp_conn_list);
                 else
@@ -122,13 +120,13 @@ int client_import_add_conn(struct obd_import *imp, struct obd_uuid *uuid,
 int client_import_del_conn(struct obd_import *imp, struct obd_uuid *uuid)
 {
         struct obd_import_conn *imp_conn;
+        struct obd_import_conn *cur_conn;
         struct obd_export *dlmexp;
         int rc = -ENOENT;
         ENTRY;
 
         spin_lock(&imp->imp_lock);
         if (list_empty(&imp->imp_conn_list)) {
-                LASSERT(!imp->imp_conn_current);
                 LASSERT(!imp->imp_connection);
                 GOTO(out, rc);
         }
@@ -138,8 +136,12 @@ int client_import_del_conn(struct obd_import *imp, struct obd_uuid *uuid)
                         continue;
                 LASSERT(imp_conn->oic_conn);
 
+                cur_conn = list_entry(imp->imp_conn_list.next,
+                                      struct obd_import_conn,
+                                      oic_item);
+
                 /* is current conn? */
-                if (imp_conn == imp->imp_conn_current) {
+                if (imp_conn == cur_conn) {
                         LASSERT(imp_conn->oic_conn == imp->imp_connection);
 
                         if (imp->imp_state != LUSTRE_IMP_CLOSED &&
@@ -545,7 +547,7 @@ int target_handle_connect(struct ptlrpc_request *req, svc_handler_t handler)
         LASSERT_REQSWAB (req, 0);
         str = lustre_msg_string(req->rq_reqmsg, 0, sizeof(tgtuuid) - 1);
         if (str == NULL) {
-                CERROR("bad target UUID for connect\n");
+                DEBUG_REQ(D_ERROR, req, "bad target UUID for connect\n");
                 GOTO(out, rc = -EINVAL);
         }
 
@@ -556,14 +558,17 @@ int target_handle_connect(struct ptlrpc_request *req, svc_handler_t handler)
         }
 
         if (!target || target->obd_stopping || !target->obd_set_up) {
-                CERROR("UUID '%s' is not available for connect\n", str);
+                DEBUG_REQ(D_ERROR, req, "UUID '%s' is not available "
+                       " for connect (%s)\n", str,
+                       !target ? "no target" : 
+                       (target->obd_stopping ? "stopping" : "not set up"));
                 GOTO(out, rc = -ENODEV);
         }
 
         LASSERT_REQSWAB (req, 1);
         str = lustre_msg_string(req->rq_reqmsg, 1, sizeof(cluuid) - 1);
         if (str == NULL) {
-                CERROR("bad client UUID for connect\n");
+                DEBUG_REQ(D_ERROR, req, "bad client UUID for connect\n");
                 GOTO(out, rc = -EINVAL);
         }
 
@@ -597,9 +602,6 @@ int target_handle_connect(struct ptlrpc_request *req, svc_handler_t handler)
         memcpy(&conn, tmp, sizeof conn);
 
         data = lustre_swab_reqbuf(req, 3, sizeof(*data), lustre_swab_connect);
-        if (data == NULL)
-                GOTO(out, rc = -EPROTO);
-
         rc = lustre_pack_reply(req, 1, &size, NULL);
         if (rc)
                 GOTO(out, rc);
@@ -642,8 +644,9 @@ int target_handle_connect(struct ptlrpc_request *req, svc_handler_t handler)
 
         if (export == NULL) {
                 if (target->obd_recovering) {
-                        CERROR("denying connection for new client %s: "
-                               "%d clients in recovery for %lds\n", cluuid.uuid,
+                        CERROR("%s: denying connection for new client %s: "
+                               "%d clients in recovery for %lds\n",
+                               target->obd_name, cluuid.uuid,
                                target->obd_recoverable_clients,
                                (target->obd_recovery_timer.expires-jiffies)/HZ);
                         rc = -EBUSY;
@@ -655,8 +658,9 @@ int target_handle_connect(struct ptlrpc_request *req, svc_handler_t handler)
 
         /* Return only the parts of obd_connect_data that we understand, so the
          * client knows that we don't understand the rest. */
-        memcpy(lustre_msg_buf(req->rq_repmsg, 0, sizeof(*data)), data,
-               sizeof(*data));
+        if (data)
+                memcpy(lustre_msg_buf(req->rq_repmsg, 0, sizeof(*data)), data,
+                       sizeof(*data));
 
         /* If all else goes well, this is our RPC return code. */
         req->rq_status = 0;
@@ -844,6 +848,7 @@ void target_cleanup_recovery(struct obd_device *obd)
 {
         struct list_head *tmp, *n;
         struct ptlrpc_request *req;
+        ENTRY;
 
         LASSERT(obd->obd_stopping);
 
@@ -868,6 +873,7 @@ void target_cleanup_recovery(struct obd_device *obd)
                 list_del(&req->rq_list);
                 target_release_saved_req(req);
         }
+        EXIT;
 }
 
 void target_abort_recovery(void *data)
@@ -881,6 +887,7 @@ void target_abort_recovery(void *data)
                 return;
         }
         obd->obd_recovering = obd->obd_abort_recovery = 0;
+        obd->obd_recoverable_clients = 0;
         target_cancel_recovery_timer(obd);
         spin_unlock_bh(&obd->obd_processing_task_lock);
 
@@ -897,7 +904,7 @@ void target_abort_recovery(void *data)
 static void target_recovery_expired(unsigned long castmeharder)
 {
         struct obd_device *obd = (struct obd_device *)castmeharder;
-        CERROR("recovery timed out, aborting\n");
+        CERROR("%s: recovery timed out, aborting\n", obd->obd_name);
         spin_lock_bh(&obd->obd_processing_task_lock);
         if (obd->obd_recovering)
                 obd->obd_abort_recovery = 1;
@@ -1353,60 +1360,4 @@ void target_committed_to_req(struct ptlrpc_request *req)
                obd->obd_last_committed, req->rq_xid);
 }
 
-int target_handle_qc_callback(struct ptlrpc_request *req)
-{
-        struct obd_quotactl *oqctl;
-        struct client_obd *cli = &req->rq_export->exp_obd->u.cli;
-
-        oqctl = lustre_swab_reqbuf(req, 0, sizeof(*oqctl),
-                                   lustre_swab_obd_quotactl);
-
-        spin_lock(&cli->cl_qchk_lock);
-        cli->cl_qchk_stat = oqctl->qc_stat;
-        spin_unlock(&cli->cl_qchk_lock);
-
-        return 0;
-}
-
-int target_handle_dqacq_callback(struct ptlrpc_request *req)
-{
-        struct obd_device *obd = req->rq_export->exp_obd;
-        struct obd_device *master_obd;
-        struct lustre_quota_ctxt *qctxt;
-        struct qunit_data *qdata, *rep;
-        int rc = 0, repsize = sizeof(struct qunit_data);
-        ENTRY;
-        
-        rc = lustre_pack_reply(req, 1, &repsize, NULL);
-        if (rc) {
-                CERROR("packing reply failed!: rc = %d\n", rc);
-                RETURN(rc);
-        }
-        rep = lustre_msg_buf(req->rq_repmsg, 0, sizeof(*rep));
-        LASSERT(rep);
-        
-        qdata = lustre_swab_reqbuf(req, 0, sizeof(*qdata), lustre_swab_qdata);
-        if (qdata == NULL) {
-                CERROR("unpacking request buffer failed!");
-                RETURN(-EPROTO);
-        }
-
-        /* we use the observer */
-        LASSERT(obd->obd_observer && obd->obd_observer->obd_observer);
-        master_obd = obd->obd_observer->obd_observer;
-        qctxt = &master_obd->u.mds.mds_quota_ctxt;
-        
-        LASSERT(qctxt->lqc_handler);
-        rc = qctxt->lqc_handler(master_obd, qdata, req->rq_reqmsg->opc);
-        if (rc && rc != -EDQUOT)
-                CERROR("dqacq failed! (rc:%d)\n", rc);
-        
-        /* the qd_count might be changed in lqc_handler */
-        memcpy(rep, qdata, sizeof(*rep));
-        req->rq_status = rc;
-        rc = ptlrpc_reply(req);
-        
-        RETURN(rc);    
-}
 EXPORT_SYMBOL(target_committed_to_req);
index e846779..b38c3b5 100644 (file)
@@ -45,7 +45,8 @@ char *ldlm_lockname[] = {
         [LCK_PR] "PR",
         [LCK_CW] "CW",
         [LCK_CR] "CR",
-        [LCK_NL] "NL"
+        [LCK_NL] "NL",
+        [LCK_GROUP] "GROUP"
 };
 char *ldlm_typename[] = {
         [LDLM_PLAIN] "PLN",
@@ -122,6 +123,7 @@ void ldlm_lock_put(struct ldlm_lock *lock)
 
         if (atomic_dec_and_test(&lock->l_refc)) {
                 struct ldlm_namespace *ns = lock->l_resource->lr_namespace;
+                struct obd_export *export = NULL;
 
                 l_lock(&ns->ns_lock);
                 LDLM_DEBUG(lock, "final lock_put on destroyed lock, freeing");
@@ -134,8 +136,7 @@ void ldlm_lock_put(struct ldlm_lock *lock)
 
                 ldlm_resource_putref(lock->l_resource);
                 lock->l_resource = NULL;
-                if (lock->l_export)
-                        class_export_put(lock->l_export);
+                export = lock->l_export;
 
                 if (lock->l_parent)
                         LDLM_LOCK_PUT(lock->l_parent);
@@ -145,6 +146,8 @@ void ldlm_lock_put(struct ldlm_lock *lock)
 
                 OBD_SLAB_FREE(lock, ldlm_lock_slab, sizeof(*lock));
                 l_unlock(&ns->ns_lock);
+                if (export)
+                        class_export_put(export);
         }
 
         EXIT;
@@ -155,6 +158,7 @@ void ldlm_lock_remove_from_lru(struct ldlm_lock *lock)
         ENTRY;
         l_lock(&lock->l_resource->lr_namespace->ns_lock);
         if (!list_empty(&lock->l_lru)) {
+                LASSERT(lock->l_resource->lr_type != LDLM_FLOCK);
                 list_del_init(&lock->l_lru);
                 lock->l_resource->lr_namespace->ns_nr_unused--;
                 LASSERT(lock->l_resource->lr_namespace->ns_nr_unused >= 0);
@@ -248,6 +252,7 @@ static struct ldlm_lock *ldlm_lock_new(struct ldlm_lock *parent,
 
         atomic_set(&lock->l_refc, 2);
         INIT_LIST_HEAD(&lock->l_children);
+        INIT_LIST_HEAD(&lock->l_childof);
         INIT_LIST_HEAD(&lock->l_res_link);
         INIT_LIST_HEAD(&lock->l_lru);
         INIT_LIST_HEAD(&lock->l_export_chain);
@@ -433,7 +438,7 @@ void ldlm_lock_addref_internal(struct ldlm_lock *lock, __u32 mode)
         ldlm_lock_remove_from_lru(lock);
         if (mode & (LCK_NL | LCK_CR | LCK_PR))
                 lock->l_readers++;
-        if (mode & (LCK_EX | LCK_CW | LCK_PW))
+        if (mode & (LCK_EX | LCK_CW | LCK_PW | LCK_GROUP))
                 lock->l_writers++;
         lock->l_last_used = jiffies;
         LDLM_LOCK_GET(lock);
@@ -453,7 +458,7 @@ void ldlm_lock_decref_internal(struct ldlm_lock *lock, __u32 mode)
                 LASSERT(lock->l_readers > 0);
                 lock->l_readers--;
         }
-        if (mode & (LCK_EX | LCK_CW | LCK_PW)) {
+        if (mode & (LCK_EX | LCK_CW | LCK_PW | LCK_GROUP)) {
                 LASSERT(lock->l_writers > 0);
                 lock->l_writers--;
         }
@@ -588,6 +593,11 @@ static struct ldlm_lock *search_queue(struct list_head *queue, ldlm_mode_t mode,
                      lock->l_policy_data.l_extent.end < policy->l_extent.end))
                         continue;
 
+                if (unlikely(mode == LCK_GROUP) &&
+                    lock->l_resource->lr_type == LDLM_EXTENT &&
+                    lock->l_policy_data.l_extent.gid != policy->l_extent.gid)
+                        continue;
+
                 if (lock->l_destroyed || (lock->l_flags & LDLM_FL_FAILED))
                         continue;
 
@@ -923,7 +933,6 @@ int ldlm_run_ast_work(struct ldlm_namespace *ns, struct list_head *rpc_list)
                                 (w->w_lock, &w->w_desc, w->w_data,
                                  LDLM_CB_BLOCKING);
                 } else if (w->w_lock->l_completion_ast != NULL) {
-                        LASSERT(w->w_lock->l_completion_ast != NULL);
                         rc = w->w_lock->l_completion_ast(w->w_lock, w->w_flags,
                                                          w->w_data);
                 } else {
@@ -1090,15 +1099,24 @@ struct ldlm_resource *ldlm_lock_convert(struct ldlm_lock *lock, int new_mode,
         struct ldlm_resource *res;
         struct ldlm_namespace *ns;
         int granted = 0;
+        int old_mode, rc;
+        ldlm_error_t err;
         ENTRY;
 
-        LBUG();
+        if (new_mode == lock->l_granted_mode) { // No changes? Just return.
+                *flags |= LDLM_FL_BLOCK_GRANTED;
+                RETURN(lock->l_resource);
+        }
+
+        LASSERTF(new_mode == LCK_PW && lock->l_granted_mode == LCK_PR,
+                 "new_mode %u, granted %u\n", new_mode, lock->l_granted_mode);
 
         res = lock->l_resource;
         ns = res->lr_namespace;
 
         l_lock(&ns->ns_lock);
 
+        old_mode = lock->l_req_mode;
         lock->l_req_mode = new_mode;
         ldlm_resource_unlink_lock(lock);
 
@@ -1109,6 +1127,8 @@ struct ldlm_resource *ldlm_lock_convert(struct ldlm_lock *lock, int new_mode,
                 } else {
                         /* This should never happen, because of the way the
                          * server handles conversions. */
+                        LDLM_ERROR(lock, "Erroneous flags %d on local lock\n",
+                                   *flags);
                         LBUG();
 
                         res->lr_tmp = &rpc_list;
@@ -1120,10 +1140,20 @@ struct ldlm_resource *ldlm_lock_convert(struct ldlm_lock *lock, int new_mode,
                                 lock->l_completion_ast(lock, 0, NULL);
                 }
         } else {
-                /* FIXME: We should try the conversion right away and possibly
-                 * return success without the need for an extra AST */
-                ldlm_resource_add_lock(res, &res->lr_converting, lock);
-                *flags |= LDLM_FL_BLOCK_CONV;
+                int pflags = 0;
+                ldlm_processing_policy policy;
+                policy = ldlm_processing_policy_table[res->lr_type];
+                res->lr_tmp = &rpc_list;
+                rc = policy(lock, &pflags, 0, &err);
+                res->lr_tmp = NULL;
+                if (rc == LDLM_ITER_STOP) {
+                        lock->l_req_mode = old_mode;
+                        ldlm_resource_add_lock(res, &res->lr_granted, lock);
+                        res = NULL;
+                } else {
+                        *flags |= LDLM_FL_BLOCK_GRANTED;
+                        granted = 1;
+                }
         }
 
         l_unlock(&ns->ns_lock);
index 1d7030e..875fcd6 100644 (file)
@@ -135,7 +135,8 @@ static int expired_lock_main(void *arg)
 
                         /* from waiting_locks_callback, but not in timer */
                         portals_debug_dumplog();
-                        portals_run_lbug_upcall(__FILE__, "waiting_locks_cb",
+                        portals_run_lbug_upcall(__FILE__,
+                                                "waiting_locks_callback",
                                                 expired_lock_thread.elt_dump);
 
                         spin_lock_bh(&waiting_locks_spinlock);
@@ -150,8 +151,8 @@ static int expired_lock_main(void *arg)
                                           l_pending_chain);
                         if ((void *)lock < LP_POISON + PAGE_SIZE &&
                             (void *)lock >= LP_POISON) {
-                                CERROR("free lock on elt list %p\n", lock);
                                 spin_unlock_bh(&waiting_locks_spinlock);
+                                CERROR("free lock on elt list %p\n", lock);
                                 LBUG();
                         }
                         list_del_init(&lock->l_pending_chain);
@@ -166,7 +167,7 @@ static int expired_lock_main(void *arg)
                         export = class_export_get(lock->l_export);
                         spin_unlock_bh(&waiting_locks_spinlock);
 
-                        ptlrpc_fail_export(export);
+                        class_fail_export(export);
                         class_export_put(export);
                         spin_lock_bh(&waiting_locks_spinlock);
                 }
@@ -186,12 +187,16 @@ static void waiting_locks_callback(unsigned long unused)
 {
         struct ldlm_lock *lock, *last = NULL;
 
+        if (obd_dump_on_timeout)
+                portals_debug_dumplog();
+
         spin_lock_bh(&waiting_locks_spinlock);
         while (!list_empty(&waiting_locks_list)) {
                 lock = list_entry(waiting_locks_list.next, struct ldlm_lock,
                                   l_pending_chain);
 
-                if (time_after(lock->l_callback_timeout, jiffies))
+                if (time_after(lock->l_callback_timeout, jiffies) ||
+                    (lock->l_req_mode == LCK_GROUP))
                         break;
 
                 LDLM_ERROR(lock, "lock callback timer expired: evicting client "
@@ -363,7 +368,9 @@ static void ldlm_failed_ast(struct ldlm_lock *lock, int rc,
                    " (%s)", ast_type, rc, lock->l_export->exp_client_uuid.uuid,
                    conn->c_remote_uuid.uuid, conn->c_peer.nid, str);
 
-        ptlrpc_fail_export(lock->l_export);
+        if (obd_dump_on_timeout)
+                portals_debug_dumplog();
+        class_fail_export(lock->l_export);
 }
 
 static int ldlm_handle_ast_error(struct ldlm_lock *lock,
@@ -387,6 +394,7 @@ static int ldlm_handle_ast_error(struct ldlm_lock *lock,
                         ldlm_failed_ast(lock, rc, ast_type);
                 }
         } else if (rc) {
+                l_lock(&lock->l_resource->lr_namespace->ns_lock);
                 if (rc == -EINVAL)
                         LDLM_DEBUG(lock, "client (nid %s) returned %d"
                                    " from %s AST - normal race",
@@ -398,6 +406,7 @@ static int ldlm_handle_ast_error(struct ldlm_lock *lock,
                                    (req->rq_repmsg != NULL) ?
                                    req->rq_repmsg->status : 0, ast_type);
                 ldlm_lock_cancel(lock);
+                l_unlock(&lock->l_resource->lr_namespace->ns_lock);
                 /* Server-side AST functions are called from ldlm_reprocess_all,
                  * which needs to be told to please restart its reprocessing. */
                 rc = -ERESTART;
@@ -495,10 +504,11 @@ int ldlm_server_completion_ast(struct ldlm_lock *lock, int flags, void *data)
         LASSERT(lock != NULL);
 
         do_gettimeofday(&granted_time);
-        total_enqueue_wait = timeval_sub(&granted_time, &lock->l_enqueued_time);
+        total_enqueue_wait = timeval_sub(&granted_time,&lock->l_enqueued_time);
 
         if (total_enqueue_wait / 1000000 > obd_timeout)
-                LDLM_ERROR(lock, "enqueue wait took %luus", total_enqueue_wait);
+                LDLM_ERROR(lock, "enqueue wait took %luus from %lu",
+                           total_enqueue_wait, lock->l_enqueued_time.tv_sec);
 
         down(&lock->l_resource->lr_lvb_sem);
         if (lock->l_resource->lr_lvb_len) {
@@ -643,7 +653,7 @@ int ldlm_handle_enqueue(struct ptlrpc_request *req,
         LASSERT(req->rq_export);
 
         if (flags & LDLM_FL_REPLAY) {
-                lock = find_existing_lock(req->rq_export, 
+                lock = find_existing_lock(req->rq_export,
                                           &dlm_req->lock_handle1);
                 if (lock != NULL) {
                         DEBUG_REQ(D_HA, req, "found existing lock cookie "LPX64,
@@ -659,8 +669,8 @@ int ldlm_handle_enqueue(struct ptlrpc_request *req,
                 GOTO(out, rc = -EFAULT);
         }
 
-        if (dlm_req->lock_desc.l_req_mode < LCK_EX ||
-            dlm_req->lock_desc.l_req_mode > LCK_NL ||
+        if (dlm_req->lock_desc.l_req_mode <= LCK_MINMODE ||
+            dlm_req->lock_desc.l_req_mode >= LCK_MAXMODE ||
             dlm_req->lock_desc.l_req_mode & (dlm_req->lock_desc.l_req_mode-1)) {
                 DEBUG_REQ(D_ERROR, req, "invalid lock request mode %d\n",
                           dlm_req->lock_desc.l_req_mode);
@@ -742,7 +752,8 @@ existing_lock:
         l_lock(&lock->l_resource->lr_namespace->ns_lock);
         /* Don't move a pending lock onto the export if it has already
          * been evicted.  Cancel it now instead. (bug 5683) */
-        if (req->rq_export->exp_failed) {
+        if (req->rq_export->exp_failed ||
+            OBD_FAIL_CHECK_ONCE(OBD_FAIL_LDLM_ENQUEUE_OLD_EXPORT)) {
                 LDLM_ERROR(lock, "lock on destroyed export %p", req->rq_export);
                 rc = -ENOTCONN;
         } else if (lock->l_flags & LDLM_FL_AST_SENT) {
@@ -784,6 +795,7 @@ existing_lock:
                         }
                         up(&lock->l_resource->lr_lvb_sem);
                 } else {
+                        ldlm_resource_unlink_lock(lock);
                         ldlm_lock_destroy(lock);
                 }
 
@@ -824,18 +836,28 @@ int ldlm_handle_convert(struct ptlrpc_request *req)
         if (!lock) {
                 req->rq_status = EINVAL;
         } else {
+                void *res;
                 l_lock(&lock->l_resource->lr_namespace->ns_lock);
                 LDLM_DEBUG(lock, "server-side convert handler START");
-                ldlm_lock_convert(lock, dlm_req->lock_desc.l_req_mode,
-                                  &dlm_rep->lock_flags);
-                if (ldlm_del_waiting_lock(lock))
-                        CDEBUG(D_DLMTRACE, "converted waiting lock %p\n", lock);
                 l_unlock(&lock->l_resource->lr_namespace->ns_lock);
-                req->rq_status = 0;
+                do_gettimeofday(&lock->l_enqueued_time);
+                res = ldlm_lock_convert(lock, dlm_req->lock_desc.l_req_mode,
+                                        &dlm_rep->lock_flags);
+                if (res) {
+                        l_lock(&lock->l_resource->lr_namespace->ns_lock);
+                        if (ldlm_del_waiting_lock(lock))
+                                CDEBUG(D_DLMTRACE,"converted waiting lock %p\n",
+                                       lock);
+                        l_unlock(&lock->l_resource->lr_namespace->ns_lock);
+                        req->rq_status = 0;
+                } else {
+                        req->rq_status = EDEADLOCK;
+                }
         }
 
         if (lock) {
-                ldlm_reprocess_all(lock->l_resource);
+                if (!req->rq_status)
+                        ldlm_reprocess_all(lock->l_resource);
                 l_lock(&lock->l_resource->lr_namespace->ns_lock);
                 LDLM_DEBUG(lock, "server-side convert handler END");
                 l_unlock(&lock->l_resource->lr_namespace->ns_lock);
@@ -1393,7 +1415,7 @@ static int ldlm_setup(void)
         if (ldlm_state == NULL)
                 RETURN(-ENOMEM);
 
-#ifdef __KERNEL__
+#ifdef LPROCFS
         rc = ldlm_proc_setup();
         if (rc != 0)
                 GOTO(out_free, rc);
@@ -1487,7 +1509,7 @@ static int ldlm_setup(void)
 #endif
 
  out_proc:
-#ifdef __KERNEL__
+#ifdef LPROCFS
         ldlm_proc_cleanup();
  out_free:
 #endif
@@ -1532,7 +1554,9 @@ static int ldlm_cleanup(int force)
         wake_up(&expired_lock_thread.elt_waitq);
         wait_event(expired_lock_thread.elt_waitq,
                    expired_lock_thread.elt_state == ELT_STOPPED);
-
+#else
+        ptlrpc_unregister_service(ldlm_state->ldlm_cb_service);
+        ptlrpc_unregister_service(ldlm_state->ldlm_cancel_service);
 #endif
 
         OBD_FREE(ldlm_state, sizeof(*ldlm_state));
@@ -1572,9 +1596,6 @@ void __exit ldlm_exit(void)
                  "couldn't free ldlm lock slab\n");
 }
 
-/* ldlm_flock.c */
-EXPORT_SYMBOL(ldlm_flock_completion_ast);
-
 /* ldlm_extent.c */
 EXPORT_SYMBOL(ldlm_extent_shift_kms);
 
index ff05942..f88f0ef 100644 (file)
@@ -86,8 +86,10 @@ int ldlm_completion_ast(struct ldlm_lock *lock, int flags, void *data)
         int rc = 0;
         ENTRY;
 
-        if (flags == LDLM_FL_WAIT_NOREPROC)
+        if (flags == LDLM_FL_WAIT_NOREPROC) {
+                LDLM_DEBUG(lock, "client-side enqueue waiting on pending lock");
                 goto noreproc;
+        }
 
         if (!(flags & (LDLM_FL_BLOCK_WAIT | LDLM_FL_BLOCK_GRANTED |
                        LDLM_FL_BLOCK_CONV))) {
@@ -110,8 +112,16 @@ noreproc:
 
         lwd.lwd_lock = lock;
 
-        lwi = LWI_TIMEOUT_INTR(obd_timeout * HZ, ldlm_expired_completion_wait,
-                               interrupted_completion_wait, &lwd);
+        if (unlikely(flags & LDLM_FL_NO_TIMEOUT)) {
+                LDLM_DEBUG(lock, "waiting indefinitely because CW lock was"
+                           " met\n");
+                lwi = LWI_INTR(interrupted_completion_wait, &lwd);
+        } else {
+                lwi = LWI_TIMEOUT_INTR(obd_timeout * HZ,
+                                       ldlm_expired_completion_wait,
+                                       interrupted_completion_wait, &lwd);
+        }
+
         if (imp != NULL) {
                 spin_lock_irqsave(&imp->imp_lock, irqflags);
                 lwd.lwd_generation = imp->imp_generation;
@@ -210,6 +220,12 @@ static void failed_lock_cleanup(struct ldlm_namespace *ns,
         l_unlock(&ns->ns_lock);
 
         ldlm_lock_decref_and_cancel(lockh, mode);
+
+        /* XXX - HACK because we shouldn't call ldlm_lock_destroy()
+         *       from llite/file.c/ll_file_flock(). */
+        if (lock->l_resource->lr_type == LDLM_FLOCK) {
+                ldlm_lock_destroy(lock);
+        }
 }
 
 int ldlm_cli_enqueue(struct obd_export *exp,
@@ -433,6 +449,8 @@ cleanup:
 static int ldlm_cli_convert_local(struct ldlm_lock *lock, int new_mode,
                                   int *flags)
 {
+        struct ldlm_resource *res;
+        int rc;
         ENTRY;
         if (lock->l_resource->lr_namespace->ns_client) {
                 CERROR("Trying to cancel local lock\n");
@@ -440,16 +458,22 @@ static int ldlm_cli_convert_local(struct ldlm_lock *lock, int new_mode,
         }
         LDLM_DEBUG(lock, "client-side local convert");
 
-        ldlm_lock_convert(lock, new_mode, flags);
-        ldlm_reprocess_all(lock->l_resource);
-
+        res = ldlm_lock_convert(lock, new_mode, flags);
+        if (res) {
+                ldlm_reprocess_all(res);
+                rc = 0;
+        } else {
+                rc = EDEADLOCK;
+        }
         LDLM_DEBUG(lock, "client-side local convert handler END");
         LDLM_LOCK_PUT(lock);
-        RETURN(0);
+        RETURN(rc);
 }
 
 /* FIXME: one of ldlm_cli_convert or the server side should reject attempted
  * conversion of locks which are on the waiting or converting queue */
+/* Caller of this code is supposed to take care of lock readers/writers
+   accounting */
 int ldlm_cli_convert(struct lustre_handle *lockh, int new_mode, int *flags)
 {
         struct ldlm_request *body;
@@ -498,13 +522,23 @@ int ldlm_cli_convert(struct lustre_handle *lockh, int new_mode, int *flags)
                 GOTO (out, rc = -EPROTO);
         }
 
+        if (req->rq_status)
+                GOTO(out, rc = req->rq_status);
+
         res = ldlm_lock_convert(lock, new_mode, &reply->lock_flags);
-        if (res != NULL)
+        if (res != NULL) {
                 ldlm_reprocess_all(res);
-        /* Go to sleep until the lock is granted. */
-        /* FIXME: or cancelled. */
-        if (lock->l_completion_ast)
-                lock->l_completion_ast(lock, LDLM_FL_WAIT_NOREPROC, NULL);
+                /* Go to sleep until the lock is granted. */
+                /* FIXME: or cancelled. */
+                if (lock->l_completion_ast) {
+                        rc = lock->l_completion_ast(lock, LDLM_FL_WAIT_NOREPROC,
+                                                    NULL);
+                        if (rc)
+                                GOTO(out, rc);
+                }
+        } else {
+                rc = EDEADLOCK;
+        }
         EXIT;
  out:
         LDLM_LOCK_PUT(lock);
@@ -612,6 +646,10 @@ int ldlm_cancel_lru(struct ldlm_namespace *ns, ldlm_sync_t sync)
         LIST_HEAD(cblist);
         ENTRY;
 
+#ifndef __KERNEL__
+        sync = LDLM_SYNC; /* force to be sync in user space */
+#endif
+
         l_lock(&ns->ns_lock);
         count = ns->ns_nr_unused - ns->ns_max_unused;
 
@@ -635,18 +673,25 @@ int ldlm_cancel_lru(struct ldlm_namespace *ns, ldlm_sync_t sync)
 
                 /* We can't re-add to l_lru as it confuses the refcounting in
                  * ldlm_lock_remove_from_lru() if an AST arrives after we drop
-                 * ns_lock below.  Use l_pending_chain as that is unused on
-                 * client, and lru is client-only.  bug 5666 */
-                if (sync != LDLM_ASYNC || ldlm_bl_to_thread(ns, NULL, lock))
-                        list_add(&lock->l_pending_chain, &cblist);
+                 * ns_lock below.  Use l_export_chain as that is unused on
+                 * client, and lru is client-only (l_pending_chain is used by
+                 * ldlm_chain_lock_for_replay() on client).  bug 5666 */
+                if (sync != LDLM_ASYNC || ldlm_bl_to_thread(ns, NULL, lock)) {
+                        LASSERTF(list_empty(&lock->l_export_chain),
+                                 "lock %p next %p prev %p\n",
+                                 lock, &lock->l_export_chain.next,
+                                 &lock->l_export_chain.prev);
+                        __LDLM_DEBUG(D_INFO, lock, "adding to LRU clear list");
+                        list_add(&lock->l_export_chain, &cblist);
+                }
 
                 if (--count == 0)
                         break;
         }
         l_unlock(&ns->ns_lock);
 
-        list_for_each_entry_safe(lock, next, &cblist, l_pending_chain) {
-                list_del_init(&lock->l_pending_chain);
+        list_for_each_entry_safe(lock, next, &cblist, l_export_chain) {
+                list_del_init(&lock->l_export_chain);
                 ldlm_handle_bl_callback(ns, NULL, lock);
         }
         RETURN(rc);
@@ -934,6 +979,8 @@ static int ldlm_chain_lock_for_replay(struct ldlm_lock *lock, void *closure)
         struct list_head *list = closure;
 
         /* we use l_pending_chain here, because it's unused on clients. */
+        LASSERTF(list_empty(&lock->l_pending_chain),"lock %p next %p prev %p\n",
+                 lock, &lock->l_pending_chain.next,&lock->l_pending_chain.prev);
         list_add(&lock->l_pending_chain, list);
         return LDLM_ITER_CONTINUE;
 }
@@ -1035,8 +1082,8 @@ static int replay_one_lock(struct obd_import *imp, struct ldlm_lock *lock)
 int ldlm_replay_locks(struct obd_import *imp)
 {
         struct ldlm_namespace *ns = imp->imp_obd->obd_namespace;
-        struct list_head list, *pos, *next;
-        struct ldlm_lock *lock;
+        struct list_head list;
+        struct ldlm_lock *lock, *next;
         int rc = 0;
 
         ENTRY;
@@ -1050,11 +1097,11 @@ int ldlm_replay_locks(struct obd_import *imp)
         l_lock(&ns->ns_lock);
         (void)ldlm_namespace_foreach(ns, ldlm_chain_lock_for_replay, &list);
 
-        list_for_each_safe(pos, next, &list) {
-                lock = list_entry(pos, struct ldlm_lock, l_pending_chain);
-                rc = replay_one_lock(imp, lock);
+        list_for_each_entry_safe(lock, next, &list, l_pending_chain) {
+                list_del_init(&lock->l_pending_chain);
                 if (rc)
-                        break; /* or try to do the rest? */
+                        continue; /* or try to do the rest? */
+                rc = replay_one_lock(imp, lock);
         }
         l_unlock(&ns->ns_lock);
 
index b362344..9a5922b 100644 (file)
@@ -39,7 +39,7 @@ struct proc_dir_entry *ldlm_type_proc_dir = NULL;
 struct proc_dir_entry *ldlm_ns_proc_dir = NULL;
 struct proc_dir_entry *ldlm_svc_proc_dir = NULL;
 
-#ifdef __KERNEL__
+#ifdef LPROCFS
 static int ldlm_proc_dump_ns(struct file *file, const char *buffer,
                              unsigned long count, void *data)
 {
@@ -206,8 +206,10 @@ void ldlm_proc_namespace(struct ldlm_namespace *ns)
                 lprocfs_add_vars(ldlm_ns_proc_dir, lock_vars, 0);
         }
 }
-#endif
 #undef MAX_STRING_SIZE
+#else
+#define ldlm_proc_namespace(ns) do {} while (0)
+#endif /* LPROCFS */
 
 struct ldlm_namespace *ldlm_namespace_new(char *name, __u32 client)
 {
@@ -255,9 +257,7 @@ struct ldlm_namespace *ldlm_namespace_new(char *name, __u32 client)
         down(&ldlm_namespace_lock);
         list_add(&ns->ns_list_chain, &ldlm_namespace_list);
         up(&ldlm_namespace_lock);
-#ifdef __KERNEL__
         ldlm_proc_namespace(ns);
-#endif
         RETURN(ns);
 
 out_hash:
@@ -356,7 +356,7 @@ int ldlm_namespace_cleanup(struct ldlm_namespace *ns, int flags)
 
                         if (!ldlm_resource_putref(res)) {
                                 CERROR("Namespace %s resource refcount %d "
-                                       "after lock cleanup\n",
+                                       "after lock cleanup; forcing cleanup.\n",
                                        ns->ns_name,
                                        atomic_read(&res->lr_refcount));
                         }
@@ -380,7 +380,7 @@ int ldlm_namespace_free(struct ldlm_namespace *ns, int force)
         /* At shutdown time, don't call the cancellation callback */
         ldlm_namespace_cleanup(ns, 0);
 
-#ifdef __KERNEL__
+#ifdef LPROCFS
         {
                 struct proc_dir_entry *dir;
                 dir = lprocfs_srch(ldlm_ns_proc_dir, ns->ns_name);
@@ -396,19 +396,20 @@ int ldlm_namespace_free(struct ldlm_namespace *ns, int force)
         if (atomic_read(&ns->ns_refcount) > 0) {
                 struct l_wait_info lwi = LWI_INTR(NULL, NULL);
                 int rc;
-                CDEBUG(D_DLMTRACE, 
-                       "dlm namespace %s free waiting on refcount %d\n", 
+                CDEBUG(D_DLMTRACE,
+                       "dlm namespace %s free waiting on refcount %d\n",
                        ns->ns_name, atomic_read(&ns->ns_refcount));
                 rc = l_wait_event(ns->ns_refcount_waitq,
                                   atomic_read(&ns->ns_refcount) == 0, &lwi);
                 if (atomic_read(&ns->ns_refcount)) {
-                        CERROR("Lock manager: waiting for the %s namespace "
-                               "was aborted with %d resources in use. (%d)\n"
-                               "I'm going to try to clean up anyway, but I "
-                               "might require a reboot of this node.\n",
-                               ns->ns_name, atomic_read(&ns->ns_refcount), rc);
+                        LCONSOLE_ERROR("Lock manager: wait for %s namespace "
+                                       "cleanup aborted with %d resources in "
+                                       "use. (%d)\nI'm going to try to clean "
+                                       "up anyway, but I might need a reboot "
+                                       "of this node.\n", ns->ns_name,
+                                       atomic_read(&ns->ns_refcount), rc);
                 }
-                CDEBUG(D_DLMTRACE, 
+                CDEBUG(D_DLMTRACE,
                        "dlm namespace %s free done waiting\n", ns->ns_name);
         }
 
@@ -545,8 +546,8 @@ ldlm_resource_get(struct ldlm_namespace *ns, struct ldlm_resource *parent,
                 rc = ns->ns_lvbo->lvbo_init(res);
                 up(&res->lr_lvb_sem);
                 if (rc)
-                        CERROR("lvbo_init failed for resource "LPU64": rc %d\n",
-                               name.name[0], rc);
+                        CERROR("lvbo_init failed for resource "LPU64"/"LPU64
+                               ": rc %d\n", name.name[0], name.name[1], rc);
         } else {
 out:
                 l_unlock(&ns->ns_lock);
@@ -649,6 +650,29 @@ void ldlm_resource_add_lock(struct ldlm_resource *res, struct list_head *head,
         l_unlock(&res->lr_namespace->ns_lock);
 }
 
+void ldlm_resource_insert_lock_after(struct ldlm_lock *original,
+                                     struct ldlm_lock *new)
+{
+        struct ldlm_resource *res = original->l_resource;
+
+        l_lock(&res->lr_namespace->ns_lock);
+
+        ldlm_resource_dump(D_OTHER, res);
+        CDEBUG(D_OTHER, "About to insert this lock after %p:\n", original);
+        ldlm_lock_dump(D_OTHER, new, 0);
+
+        if (new->l_destroyed) {
+                CDEBUG(D_OTHER, "Lock destroyed, not adding to resource\n");
+                goto out;
+        }
+
+        LASSERT(list_empty(&new->l_res_link));
+
+        list_add(&new->l_res_link, &original->l_res_link);
+ out:
+        l_unlock(&res->lr_namespace->ns_lock);
+}
+
 void ldlm_resource_unlink_lock(struct ldlm_lock *lock)
 {
         l_lock(&lock->l_resource->lr_namespace->ns_lock);
index a776768..6fac0b2 100644 (file)
@@ -1,10 +1,11 @@
 ## Liblustre excecutables & libraries Makefile
 SUBDIRS = . tests
 
-AM_CPPFLAGS = $(HAVE_EFENCE) -I$(SYSIO)/include -D_LARGEFILE64_SOURCE=1 $(LLCPPFLAGS) -I$(top_srcdir)/portals/unals
+AM_CPPFLAGS = $(HAVE_EFENCE) -I$(SYSIO)/include -D_LARGEFILE64_SOURCE=1 \
+              $(LLCPPFLAGS) -I$(top_srcdir)/portals/unals
 AM_CFLAGS = $(LLCFLAGS)
 
-LIBS = $(LIBEFENCE)
+AM_LIBS = $(LIBEFENCE)
 
 LUSTRE_LIBS = libllite.a \
               $(top_builddir)/lustre/lov/liblov.a \
@@ -15,14 +16,16 @@ LUSTRE_LIBS = libllite.a \
               $(top_builddir)/lustre/obdclass/liblustreclass.a \
               $(top_builddir)/lustre/lvfs/liblvfs.a
 
+if !CRAY_PORTALS
 PTL_LIBS =    $(top_builddir)/portals/utils/libuptlctl.a \
               $(top_builddir)/portals/unals/libtcpnal.a \
               $(top_builddir)/portals/portals/libportals.a
+else
+PTL_LIBS =    $(top_builddir)/portals/utils/libuptlctl.a \
+              $(CRAY_PORTALS_LIBS)/libportals.a
+endif
 
-SYSIO_LIBS =  $(SYSIO)/drivers/native/libsysio_native.a \
-              $(SYSIO)/drivers/sockets/libsysio_sockets.a \
-              $(SYSIO)/src/libsysio.a \
-              $(SYSIO)/dev/stdfd/libsysio_stdfd.a
+SYSIO_LIBS =  $(SYSIO)/lib/libsysio.a
 
 if LIBLUSTRE
 lib_LIBRARIES = liblustre.a
@@ -40,16 +43,18 @@ install-exec-hook: liblustre.so
        done
 else
 install-exec-hook:
-
 endif
 
-libllite_a_SOURCES = llite_lib.c super.c namei.c rw.c file.c dir.c llite_lib.h
+libllite_a_SOURCES = llite_lib.c super.c namei.c rw.c file.c dir.c \
+                    lutil.c lutil.h llite_lib.h
 
 # for make rpms -- need cleanup
 liblustre_a_SOURCES = llite_lib.c super.c namei.c rw.c file.c dir.c \
                     llite_lib.h
 
 liblustre.a : $(LUSTRE_LIBS) $(PTL_LIBS) $(SYSIO_LIBS)
-       $(srcdir)/genlib.sh $(SYSIO) $(AR) $(LINK) || ( rm -f $@; exit 1 )
+       sh $(srcdir)/genlib.sh "$(SYSIO)" "$(CRAY_PORTALS_LIBS)" "$(LIBS)"
 
 EXTRA_DIST = genlib.sh
+
+CLEANFILES := liblsupport.a liblustre.so
index c125b79..ec33ac3 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Lustre Light directory handling
  *
- *  Copyright (c) 2002, 2003 Cluster File Systems, Inc.
+ *  Copyright (c) 2002-2004 Cluster File Systems, Inc.
  *
  *   This file is part of Lustre, http://www.lustre.org.
  *
 #include <sys/fcntl.h>
 #include <sys/queue.h>
 
+#ifdef HAVE_XTIO_H
+#include <xtio.h>
+#endif
 #include <sysio.h>
 #include <fs.h>
 #include <mount.h>
 #include <inode.h>
+#ifdef HAVE_FILE_H
 #include <file.h>
+#endif
 
 #undef LIST_HEAD
 
+#ifdef HAVE_LINUX_TYPES_H
 #include <linux/types.h>
-#include <linux/dirent.h>
+#elif defined(HAVE_SYS_TYPES_H)
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_LINUX_UNISTD_H
 #include <linux/unistd.h>
+#elif defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+
+#include <dirent.h>
 
 #include "llite_lib.h"
 
 static int llu_dir_do_readpage(struct inode *inode, struct page *page)
 {
         struct llu_inode_info *lli = llu_i2info(inode);
+        struct intnl_stat *st = llu_i2stat(inode);
         struct llu_sb_info *sbi = llu_i2sbi(inode);
         struct ll_fid mdc_fid;
         __u64 offset;
@@ -61,19 +77,9 @@ static int llu_dir_do_readpage(struct inode *inode, struct page *page)
         struct mdc_op_data data;
         struct obd_device *obddev = class_exp2obd(sbi->ll_mdc_exp);
         struct ldlm_res_id res_id =
-                { .name = {lli->lli_st_ino, (__u64)lli->lli_st_generation} };
+                { .name = {st->st_ino, (__u64)lli->lli_st_generation} };
         ENTRY;
 
-        if ((lli->lli_st_size + PAGE_CACHE_SIZE - 1) >> PAGE_SHIFT <= page->index) {
-                /* XXX why do we need this exactly, and why do we think that
-                 *     an all-zero directory page is useful?
-                 */
-                CERROR("memsetting dir page %lu to zero (size %lld)\n",
-                       page->index, lli->lli_st_size);
-                memset(page->addr, 0, PAGE_CACHE_SIZE);
-                GOTO(readpage_out, rc);
-        }
-
         rc = ldlm_lock_match(obddev->obd_namespace, LDLM_FL_BLOCK_GRANTED,
                              &res_id, LDLM_PLAIN, NULL, LCK_PR, &lockh);
         if (!rc) {
@@ -93,7 +99,7 @@ static int llu_dir_do_readpage(struct inode *inode, struct page *page)
         }
         ldlm_lock_dump_handle(D_OTHER, &lockh);
 
-        mdc_pack_fid(&mdc_fid, lli->lli_st_ino, lli->lli_st_generation, S_IFDIR);
+        mdc_pack_fid(&mdc_fid, st->st_ino, lli->lli_st_generation, S_IFDIR);
 
         offset = page->index << PAGE_SHIFT;
         rc = mdc_readpage(sbi->ll_mdc_exp, &mdc_fid,
@@ -103,12 +109,13 @@ static int llu_dir_do_readpage(struct inode *inode, struct page *page)
                 LASSERT (body != NULL);         /* checked by mdc_readpage() */
                 LASSERT_REPSWABBED (request, 0); /* swabbed by mdc_readpage() */
 
-                lli->lli_st_size = body->size;
+                st->st_size = body->size;
+        } else {
+                CERROR("read_dir_page(%ld) error %d\n", page->index, rc);
         }
         ptlrpc_req_finished(request);
         EXIT;
 
- readpage_out:
         ldlm_lock_decref(&lockh, LCK_PR);
         return rc;
 }
@@ -135,6 +142,29 @@ static struct page *llu_dir_read_page(struct inode *ino, int pgidx)
         return page;
 }
 
+enum {
+        EXT2_FT_UNKNOWN,
+        EXT2_FT_REG_FILE,
+        EXT2_FT_DIR,
+        EXT2_FT_CHRDEV,
+        EXT2_FT_BLKDEV,
+        EXT2_FT_FIFO,
+        EXT2_FT_SOCK,
+        EXT2_FT_SYMLINK,
+        EXT2_FT_MAX
+};
+
+static unsigned char ext2_filetype_table[EXT2_FT_MAX] = {
+        [EXT2_FT_UNKNOWN]       DT_UNKNOWN,
+        [EXT2_FT_REG_FILE]      DT_REG,
+        [EXT2_FT_DIR]           DT_DIR,
+        [EXT2_FT_CHRDEV]        DT_CHR,
+        [EXT2_FT_BLKDEV]        DT_BLK,
+        [EXT2_FT_FIFO]          DT_FIFO,
+        [EXT2_FT_SOCK]          DT_SOCK,
+        [EXT2_FT_SYMLINK]       DT_LNK,
+};
+
 #define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de)))
 #define ROUND_UP64(x)   (((x)+sizeof(__u64)-1) & ~(sizeof(__u64)-1))
 
@@ -165,16 +195,24 @@ ssize_t llu_iop_getdirentries(struct inode *ino, char *buf, size_t nbytes,
                               _SYSIO_OFF_T *basep)
 {
         struct llu_inode_info *lli = llu_i2info(ino);
+        struct intnl_stat *st = llu_i2stat(ino);
         loff_t pos = *basep, offset;
         int maxpages, pgidx, filled = 0;
         ENTRY;
 
+        if (st->st_size == 0) {
+                CWARN("dir size is 0?\n");
+                RETURN(0);
+        }
+
+        liblustre_wait_event(0);
+
         if (pos == -1)
                 pos = lli->lli_dir_pos;
 
-        maxpages = lli->lli_st_size >> PAGE_CACHE_SHIFT;
-        pgidx = pos >> PAGE_CACHE_SHIFT;
-        offset = pos & ~PAGE_CACHE_MASK;
+        maxpages = (st->st_size + PAGE_SIZE - 1) >> PAGE_SHIFT;
+        pgidx = pos >> PAGE_SHIFT;
+        offset = pos & ~PAGE_MASK;
 
         for ( ; pgidx < maxpages ; pgidx++, offset = 0) {
                 struct page *page;
@@ -186,23 +224,24 @@ ssize_t llu_iop_getdirentries(struct inode *ino, char *buf, size_t nbytes,
                         continue;
 
                 /* size might have been updated by mdc_readpage */
-                maxpages = lli->lli_st_size >> PAGE_CACHE_SHIFT;
+                maxpages = (st->st_size + PAGE_SIZE - 1) >> PAGE_SHIFT;
 
                 /* fill in buffer */
                 addr = page->addr;
-                limit = addr + PAGE_CACHE_SIZE - EXT2_DIR_REC_LEN(1);
+                limit = addr + PAGE_SIZE - EXT2_DIR_REC_LEN(1);
                 de = (struct ext2_dirent *) (addr + offset);
 
                 for ( ; (char*) de <= limit; de = ext2_next_entry(de)) {
                         if (de->inode) {
                                 int over;
-                                unsigned char d_type = 0;
+                                unsigned char d_type = DT_UNKNOWN;
 
-                                /* XXX handle type, etc here */
+                                if (de->file_type < EXT2_FT_MAX)
+                                        d_type = ext2_filetype_table[de->file_type];
 
                                 offset = (char*) de - addr;
                                 over =  filldir(buf, nbytes, de->name, de->name_len,
-                                                (pgidx << PAGE_CACHE_SHIFT) | offset,
+                                                (pgidx << PAGE_SHIFT) | offset,
                                                 le32_to_cpu(de->inode), d_type, &filled);
                                 if (over) {
                                         free_page(page);
@@ -214,7 +253,7 @@ ssize_t llu_iop_getdirentries(struct inode *ino, char *buf, size_t nbytes,
                 free_page(page);
         }
 done:
-        lli->lli_dir_pos = pgidx << PAGE_CACHE_SHIFT | offset;
+        lli->lli_dir_pos = pgidx << PAGE_SHIFT | offset;
         *basep = lli->lli_dir_pos;
         RETURN(filled);
 }
index 0aa6687..b7f4b55 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Lustre Light file operations
  *
- *  Copyright (c) 2002, 2003 Cluster File Systems, Inc.
+ *  Copyright (c) 2002-2004 Cluster File Systems, Inc.
  *
  *   This file is part of Lustre, http://www.lustre.org.
  *
 #include <assert.h>
 #include <time.h>
 #include <sys/types.h>
+#include <sys/stat.h>
 #include <sys/queue.h>
+#include <fcntl.h>
 
+#ifdef HAVE_XTIO_H
+#include <xtio.h>
+#endif
 #include <sysio.h>
 #include <fs.h>
 #include <mount.h>
 #include <inode.h>
+#ifdef HAVE_FILE_H
 #include <file.h>
+#endif
 
 #undef LIST_HEAD
 
@@ -66,29 +73,29 @@ void obdo_refresh_inode(struct inode *dst,
                         struct obdo *src,
                         obd_flag valid)
 {
-        struct llu_inode_info *lli = llu_i2info(dst);
+        struct intnl_stat *st = llu_i2stat(dst);
         valid &= src->o_valid;
 
         if (valid & (OBD_MD_FLCTIME | OBD_MD_FLMTIME))
-                CDEBUG(D_INODE, "valid %x, cur time %lu/%lu, new %lu/%lu\n",
-                       src->o_valid, LTIME_S(lli->lli_st_mtime), 
-                       LTIME_S(lli->lli_st_ctime),
+                CDEBUG(D_INODE,"valid "LPX64", cur time %lu/%lu, new %lu/%lu\n",
+                       src->o_valid, LTIME_S(st->st_mtime),
+                       LTIME_S(st->st_ctime),
                        (long)src->o_mtime, (long)src->o_ctime);
 
-        if (valid & OBD_MD_FLATIME && src->o_atime > LTIME_S(lli->lli_st_atime))
-                LTIME_S(lli->lli_st_atime) = src->o_atime;
-        if (valid & OBD_MD_FLMTIME && src->o_mtime > LTIME_S(lli->lli_st_mtime))
-                LTIME_S(lli->lli_st_mtime) = src->o_mtime;
-        if (valid & OBD_MD_FLCTIME && src->o_ctime > LTIME_S(lli->lli_st_ctime))
-                LTIME_S(lli->lli_st_ctime) = src->o_ctime;
-        if (valid & OBD_MD_FLSIZE && src->o_size > lli->lli_st_size)
-                lli->lli_st_size = src->o_size;
+        if (valid & OBD_MD_FLATIME && src->o_atime > LTIME_S(st->st_atime))
+                LTIME_S(st->st_atime) = src->o_atime;
+        if (valid & OBD_MD_FLMTIME && src->o_mtime > LTIME_S(st->st_mtime))
+                LTIME_S(st->st_mtime) = src->o_mtime;
+        if (valid & OBD_MD_FLCTIME && src->o_ctime > LTIME_S(st->st_ctime))
+                LTIME_S(st->st_ctime) = src->o_ctime;
+        if (valid & OBD_MD_FLSIZE && src->o_size > st->st_size)
+                st->st_size = src->o_size;
         /* optimum IO size */
         if (valid & OBD_MD_FLBLKSZ)
-                lli->lli_st_blksize = src->o_blksize;
+                st->st_blksize = src->o_blksize;
         /* allocation of space */
-        if (valid & OBD_MD_FLBLOCKS && src->o_blocks > lli->lli_st_blocks)
-                lli->lli_st_blocks = src->o_blocks;
+        if (valid & OBD_MD_FLBLOCKS && src->o_blocks > st->st_blocks)
+                st->st_blocks = src->o_blocks;
 }
 
 static int llu_local_open(struct llu_inode_info *lli, struct lookup_intent *it)
@@ -105,7 +112,7 @@ static int llu_local_open(struct llu_inode_info *lli, struct lookup_intent *it)
         /* already opened? */
         if (lli->lli_open_count++)
                 RETURN(0);
-                
+
         LASSERT(!lli->lli_file_data);
 
         OBD_ALLOC(fd, sizeof(*fd));
@@ -126,6 +133,7 @@ int llu_iop_open(struct pnode *pnode, int flags, mode_t mode)
 {
         struct inode *inode = pnode->p_base->pb_ino;
         struct llu_inode_info *lli = llu_i2info(inode);
+        struct intnl_stat *st = llu_i2stat(inode);
         struct ll_file_data *fd;
         struct ptlrpc_request *request;
         struct lookup_intent *it;
@@ -133,11 +141,13 @@ int llu_iop_open(struct pnode *pnode, int flags, mode_t mode)
         int rc = 0;
         ENTRY;
 
+        liblustre_wait_event(0);
+
         /* don't do anything for '/' */
         if (llu_is_root_inode(inode))
                 RETURN(0);
 
-        CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu\n", lli->lli_st_ino);
+        CDEBUG(D_VFSTRACE, "VFS Op:inode=%llu\n", st->st_ino);
         LL_GET_INTENT(inode, it);
 
         if (!it->d.lustre.it_disposition) {
@@ -152,7 +162,7 @@ int llu_iop_open(struct pnode *pnode, int flags, mode_t mode)
         if (rc)
                 LBUG();
 
-        if (!S_ISREG(lli->lli_st_mode))
+        if (!S_ISREG(st->st_mode))
                 GOTO(out_release, rc = 0);
                 
         fd = lli->lli_file_data;
@@ -166,7 +176,7 @@ int llu_iop_open(struct pnode *pnode, int flags, mode_t mode)
         }
         fd->fd_flags &= ~O_LOV_DELAY_CREATE;
 
-        lli->lli_open_flags = flags;
+        lli->lli_open_flags = flags & ~(O_CREAT | O_EXCL | O_TRUNC);
 
  out_release:
         request = it->d.lustre.it_data;
@@ -175,6 +185,22 @@ int llu_iop_open(struct pnode *pnode, int flags, mode_t mode)
         it->it_op_release(it);
         OBD_FREE(it, sizeof(*it));
 
+        /* libsysio haven't doing anything for O_TRUNC. here we
+         * simply simulate it as open(...); truncate(...);
+         */
+        if (rc == 0 && (flags & O_TRUNC) &&
+            S_ISREG(st->st_mode)) {
+                struct iattr attr;
+
+                memset(&attr, 0, sizeof(attr));
+                attr.ia_size = 0;
+                attr.ia_valid |= ATTR_SIZE | ATTR_RAW;
+                rc  = llu_setattr_raw(inode, &attr);
+                if (rc) {
+                        CERROR("error %d truncate in open()\n", rc);
+                }
+        }
+
         RETURN(rc);
 }
 
@@ -251,6 +277,7 @@ int llu_objects_destroy(struct ptlrpc_request *request, struct inode *dir)
 int llu_mdc_close(struct obd_export *mdc_exp, struct inode *inode)
 {
         struct llu_inode_info *lli = llu_i2info(inode);
+        struct intnl_stat *st = llu_i2stat(inode);
         struct ll_file_data *fd = lli->lli_file_data;
         struct ptlrpc_request *req = NULL;
         struct obd_client_handle *och = &fd->fd_mds_och;
@@ -258,7 +285,15 @@ int llu_mdc_close(struct obd_export *mdc_exp, struct inode *inode)
         int rc, valid;
         ENTRY;
 
-        obdo.o_id = lli->lli_st_ino;
+        /* clear group lock, if present */
+        if (fd->fd_flags & LL_FILE_GROUP_LOCKED) {
+                struct lov_stripe_md *lsm = llu_i2info(inode)->lli_smd;
+                fd->fd_flags &= ~(LL_FILE_GROUP_LOCKED|LL_FILE_IGNORE_LOCK);
+                rc = llu_extent_unlock(fd, inode, lsm, LCK_GROUP,
+                                       &fd->fd_cwlockh);
+        }
+
+        obdo.o_id = st->st_ino;
         obdo.o_valid = OBD_MD_FLID;
         valid = OBD_MD_FLTYPE | OBD_MD_FLMODE | OBD_MD_FLSIZE |OBD_MD_FLBLOCKS |
                 OBD_MD_FLATIME | OBD_MD_FLMTIME | OBD_MD_FLCTIME;
@@ -278,12 +313,12 @@ int llu_mdc_close(struct obd_export *mdc_exp, struct inode *inode)
                 //ll_queue_done_writing(inode);
                 rc = 0;
         } else if (rc) {
-                CERROR("inode %lu close failed: rc %d\n", lli->lli_st_ino, rc);
+                CERROR("inode %llu close failed: rc %d\n", st->st_ino, rc);
         } else {
                 rc = llu_objects_destroy(req, inode);
                 if (rc)
-                        CERROR("inode %lu ll_objects destroy: rc = %d\n",
-                                lli->lli_st_ino, rc);
+                        CERROR("inode %llu ll_objects destroy: rc = %d\n",
+                                st->st_ino, rc);
         }
 
         mdc_clear_open_replay_data(och);
@@ -303,7 +338,7 @@ int llu_file_release(struct inode *inode)
         int rc = 0, rc2;
 
         ENTRY;
-        CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%lu\n", lli->lli_st_ino,
+        CDEBUG(D_VFSTRACE, "VFS Op:inode=%llu/%lu\n", llu_i2stat(inode)->st_ino,
                lli->lli_st_generation);
 
         if (llu_is_root_inode(inode))
@@ -324,54 +359,34 @@ int llu_file_release(struct inode *inode)
         RETURN(rc);
 }
 
+/*
+ * libsysio require us return 0
+ */
 int llu_iop_close(struct inode *inode)
 {
         int rc;
 
+        liblustre_wait_event(0);
+
         rc = llu_file_release(inode);
+        if (rc) {
+                CERROR("file close error %d\n", rc);
+        }
         /* if open count == 0 && stale_flag is set, should we
          * remove the inode immediately? */
-        return rc;
+        return 0;
 }
 
-int llu_iop_ipreadv(struct inode *ino,
-                    struct ioctx *ioctx)
+_SYSIO_OFF_T llu_iop_pos(struct inode *ino, _SYSIO_OFF_T off)
 {
         ENTRY;
 
-        if (!ioctx->ioctx_iovlen)
-                RETURN(0);
-        if (ioctx->ioctx_iovlen < 0)
-                RETURN(-EINVAL);
-
-        ioctx->ioctx_private = llu_file_read(ino,
-                                        ioctx->ioctx_iovec,
-                                        ioctx->ioctx_iovlen,
-                                        ioctx->ioctx_offset);
-        if (IS_ERR(ioctx->ioctx_private))
-                return (PTR_ERR(ioctx->ioctx_private));
+        liblustre_wait_event(0);
 
-        RETURN(0);
-}
-
-int llu_iop_ipwritev(struct inode *ino,
-                     struct ioctx *ioctx)
-{
-        ENTRY;
-
-        if (!ioctx->ioctx_iovlen)
-                RETURN(0);
-        if (ioctx->ioctx_iovlen < 0)
+        if (off < 0 || off > ll_file_maxbytes(ino))
                 RETURN(-EINVAL);
 
-        ioctx->ioctx_private = llu_file_write(ino,
-                                         ioctx->ioctx_iovec,
-                                         ioctx->ioctx_iovlen,
-                                         ioctx->ioctx_offset);
-        if (IS_ERR(ioctx->ioctx_private))
-                return (PTR_ERR(ioctx->ioctx_private));
-
-        RETURN(0);
+        RETURN(off);
 }
 
 /* this isn't where truncate starts.   roughly:
@@ -380,15 +395,17 @@ int llu_iop_ipwritev(struct inode *ino,
 static void llu_truncate(struct inode *inode)
 {
         struct llu_inode_info *lli = llu_i2info(inode);
+        struct intnl_stat *st = llu_i2stat(inode);
         struct lov_stripe_md *lsm = lli->lli_smd;
         struct obdo oa = {0};
-        int err;
+        int rc;
         ENTRY;
-        CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%lu\n", lli->lli_st_ino,
-               lli->lli_st_generation);
+        CDEBUG(D_VFSTRACE, "VFS Op:inode=%llu/%lu(%p) to %llu\n", st->st_ino,
+               lli->lli_st_generation, inode, st->st_size);
 
         if (!lsm) {
-                CERROR("truncate on inode %lu with no objects\n", lli->lli_st_ino);
+                CDEBUG(D_INODE, "truncate on inode %llu with no objects\n",
+                       st->st_ino);
                 EXIT;
                 return;
         }
@@ -398,14 +415,16 @@ static void llu_truncate(struct inode *inode)
         obdo_from_inode(&oa, inode, OBD_MD_FLTYPE|OBD_MD_FLMODE|OBD_MD_FLATIME|
                                     OBD_MD_FLMTIME | OBD_MD_FLCTIME);
 
+        obd_adjust_kms(llu_i2obdexp(inode), lsm, st->st_size, 1);
+
         CDEBUG(D_INFO, "calling punch for "LPX64" (all bytes after %Lu)\n",
-               oa.o_id, lli->lli_st_size);
+               oa.o_id, st->st_size);
 
         /* truncate == punch from new size to absolute end of file */
-        err = obd_punch(llu_i2obdexp(inode), &oa, lsm, lli->lli_st_size,
-                        OBD_OBJECT_EOF, NULL);
-        if (err)
-                CERROR("obd_truncate fails (%d) ino %lu\n", err, lli->lli_st_ino);
+        rc = obd_punch(llu_i2obdexp(inode), &oa, lsm, st->st_size,
+                       OBD_OBJECT_EOF, NULL);
+        if (rc)
+                CERROR("obd_truncate fails (%d) ino %llu\n", rc, st->st_ino);
         else
                 obdo_to_inode(inode, &oa, OBD_MD_FLSIZE | OBD_MD_FLBLOCKS |
                                           OBD_MD_FLATIME | OBD_MD_FLMTIME |
@@ -413,13 +432,11 @@ static void llu_truncate(struct inode *inode)
 
         EXIT;
         return;
-}
+} /* llu_truncate */
 
 int llu_vmtruncate(struct inode * inode, loff_t offset)
 {
-        struct llu_inode_info *lli = llu_i2info(inode);
-
-        lli->lli_st_size = offset;
+        llu_i2stat(inode)->st_size = offset;
 
         llu_truncate(inode);
 
index 4a3c356..f70116d 100755 (executable)
@@ -4,7 +4,10 @@
 #
 # This script is to generate lib lustre library as a whole. It will leave
 # two files on current directory: liblustre.a and liblustre.so.
-# Integrate them into Makefile.am later
+#
+# Most concern here is the libraries linking order
+#
+# FIXME: How to do this cleanly use makefile?
 #
 
 AR=/usr/bin/ar
@@ -14,14 +17,16 @@ RANLIB=/usr/bin/ranlib
 CWD=`pwd`
 
 SYSIO=$1
+CRAY_PORTALS_LIBS=$2
+LIBS=$3
+
+if [ ! -f $SYSIO/lib/libsysio.a ]; then
+  echo "ERROR: $SYSIO/lib/libsysio.a dosen't exist"
+  exit 1
+fi
 
-#if [ ! -f $SYSIO/lib/libsysio.a ]; then
-#  echo "ERROR: $SYSIO/lib/libsysio.a dosen't exist"
-#  exit 1
-#fi
-#
 # do cleanup at first
-#rm -f liblustre.so
+rm -f liblustre.so
 
 ALL_OBJS=
 
@@ -35,16 +40,34 @@ build_obj_list() {
 #
 # special treatment for libsysio
 #
-#sysio_tmp=$CWD/sysio_tmp_`date +%s`
-#build_sysio_obj_list() {
-#  _objs=`$AR -t $1`
-#  mkdir -p $sysio_tmp
-#  $AR -x $1
-#  mv $_objs $sysio_tmp
-#  for _lib in $_objs; do
-#    ALL_OBJS=$ALL_OBJS"$sysio_tmp/$_lib ";
-#  done
-#}
+sysio_tmp=$CWD/sysio_tmp_`date +%s`
+rm -rf $sysio_tmp
+build_sysio_obj_list() {
+  _objs=`$AR -t $1`
+  mkdir -p $sysio_tmp
+  cd $sysio_tmp
+  $AR -x $1
+  cd ..
+  for _lib in $_objs; do
+    ALL_OBJS=$ALL_OBJS"$sysio_tmp/$_lib ";
+  done
+}
+
+#
+# special treatment for libportals.a
+#
+cray_tmp=$CWD/cray_tmp_`date +%s`
+rm -rf $cray_tmp
+build_cray_portals_obj_list() {
+  _objs=`$AR -t $1`
+  mkdir -p $cray_tmp
+  cd $cray_tmp
+  $AR -x $1
+  cd ..
+  for _lib in $_objs; do
+    ALL_OBJS=$ALL_OBJS"$cray_tmp/$_lib ";
+  done
+}
 
 # lustre components libs
 build_obj_list . libllite.a
@@ -58,22 +81,24 @@ build_obj_list ../lvfs liblvfs.a
 
 # portals components libs
 build_obj_list ../../portals/utils libuptlctl.a
-build_obj_list ../../portals/unals libtcpnal.a
-build_obj_list ../../portals/portals libportals.a
+
+if [ "x$CRAY_PORTALS_LIBS" = "x" ]; then
+  build_obj_list ../../portals/unals libtcpnal.a
+  build_obj_list ../../portals/portals libportals.a
+# if libportals is already in our LIBS we don't need to link against it here
+elif $(echo "$LIBS" | grep -v -- "-lportals" >/dev/null) ; then
+  build_cray_portals_obj_list $CRAY_PORTALS_LIBS/libportals.a
+fi
 
 # create static lib lsupport
 rm -f $CWD/liblsupport.a
 $AR -cru $CWD/liblsupport.a $ALL_OBJS
 $RANLIB $CWD/liblsupport.a
 
-# libsysio components libs
-build_obj_list $SYSIO/drivers/native libsysio_native.a
-build_obj_list $SYSIO/drivers/sockets libsysio_sockets.a
-build_obj_list $SYSIO/src libsysio.a
-build_obj_list $SYSIO/dev/stdfd libsysio_stdfd.a
-#
-#build_sysio_obj_list $SYSIO/lib/libsysio.a
-#
+# if libsysio is already in our LIBS we don't need to link against it here
+if $(echo "$LIBS" | grep -v -- "-lsysio" >/dev/null) ; then
+  build_sysio_obj_list $SYSIO/lib/libsysio.a
+fi
 
 # create static lib lustre
 rm -f $CWD/liblustre.a
@@ -83,6 +108,7 @@ $RANLIB $CWD/liblustre.a
 # create shared lib lustre
 rm -f $CWD/liblustre.so
 $LD -shared -o $CWD/liblustre.so -init __liblustre_setup_ -fini __liblustre_cleanup_ \
-       $ALL_OBJS -lpthread
+       $ALL_OBJS -lcap -lpthread
 
-#rm -rf $sysio_tmp
+rm -rf $sysio_tmp
+rm -rf $cray_tmp
index f469710..9a69750 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Lustre Light common routines
  *
- *  Copyright (c) 2002, 2003 Cluster File Systems, Inc.
+ *  Copyright (c) 2002-2004 Cluster File Systems, Inc.
  *
  *   This file is part of Lustre, http://www.lustre.org.
  *
 #include <stdlib.h>
 #include <string.h>
 #include <assert.h>
+#include <signal.h>
 #include <sys/types.h>
+#include <sys/stat.h>
 #include <sys/queue.h>
 
-#include <netinet/in.h>
-#include <sys/socket.h>
-#include <arpa/inet.h>
-
+#ifdef HAVE_XTIO_H
+#include <xtio.h>
+#endif
 #include <sysio.h>
 #include <fs.h>
 #include <mount.h>
 #include <inode.h>
+#ifdef HAVE_FILE_H
 #include <file.h>
+#endif
+
+/* env variables */
+#define ENV_LUSTRE_MNTPNT               "LIBLUSTRE_MOUNT_POINT"
+#define ENV_LUSTRE_MNTTGT               "LIBLUSTRE_MOUNT_TARGET"
+#define ENV_LUSTRE_TIMEOUT              "LIBLUSTRE_TIMEOUT"
+#define ENV_LUSTRE_DUMPFILE             "LIBLUSTRE_DUMPFILE"
+#define ENV_LUSTRE_DEBUG_MASK           "LIBLUSTRE_DEBUG_MASK"
+#define ENV_LUSTRE_DEBUG_SUBSYS         "LIBLUSTRE_DEBUG_SUBSYS"
 
 /* both sys/queue.h (libsysio require it) and portals/lists.h have definition
  * of 'LIST_HEAD'. undef it to suppress warnings
  */
 #undef LIST_HEAD
-
-#include <portals/api-support.h> /* needed for ptpctl.h */
 #include <portals/ptlctl.h>     /* needed for parse_dump */
-#include <procbridge.h>
 
+#include "lutil.h"
 #include "llite_lib.h"
 
-#error
-
-unsigned int portal_subsystem_debug = ~0 - (S_PORTALS | S_NAL);
-
-struct task_struct     *current;
-
-struct ldlm_namespace;
-struct ldlm_res_id;
-struct obd_import;
-
-void *inter_module_get(char *arg)
-{
-        if (!strcmp(arg, "ldlm_cli_cancel_unused"))
-                return ldlm_cli_cancel_unused;
-        else if (!strcmp(arg, "ldlm_namespace_cleanup"))
-                return ldlm_namespace_cleanup;
-        else if (!strcmp(arg, "ldlm_replay_locks"))
-                return ldlm_replay_locks;
-        else
-                return NULL;
-}
-
-/* XXX move to proper place */
-#error
-char *portals_nid2str(int nal, ptl_nid_t nid, char *str)
-{
-        switch(nal){
-        case TCPNAL:
-                /* userspace NAL */
-        case SOCKNAL:
-                snprintf(str, PTL_NALFMT_SIZE - 1, "%u:%u.%u.%u.%u",
-                         (__u32)(nid >> 32), HIPQUAD(nid));
-                break;
-        case QSWNAL:
-        case GMNAL:
-        case IBNAL:
-        case SCIMACNAL:
-                snprintf(str, PTL_NALFMT_SIZE - 1, "%u:%u",
-                         (__u32)(nid >> 32), (__u32)nid);
-                break;
-        default:
-                snprintf(str, PTL_NALFMT_SIZE - 1, "?%d? %llx",
-                         nal, (long long)nid);
-                break;
-        }
-        return str;
-}
-
-void init_current(char *comm)
-{ 
-        current = malloc(sizeof(*current));
-        current->fs = malloc(sizeof(*current->fs));
-        current->fs->umask = umask(0777);
-        umask(current->fs->umask);
-        strncpy(current->comm, comm, sizeof(current->comm));
-        current->pid = getpid();
-        current->fsuid = 0;
-        current->fsgid = 0;
-        current->cap_effective = -1;
-        memset(&current->pending, 0, sizeof(current->pending));
-}
-
-/* FIXME */
-void generate_random_uuid(unsigned char uuid_out[16])
-{
-        int *arr = (int*)uuid_out;
-        int i;
-
-        for (i = 0; i < sizeof(uuid_out)/sizeof(int); i++)
-                arr[i] = rand();
-}
-
-ptl_nid_t tcpnal_mynid;
-
-int init_lib_portals()
-{
-        int rc;
-        ENTRY;
-
-        rc = PtlInit();
-        if (rc != PTL_OK)
-                CERROR("PtlInit failed: error %d\n", rc);
-        RETURN(rc);
-}
-
-extern int class_handle_ioctl(unsigned int cmd, unsigned long arg);
-
-int lib_ioctl(int dev_id, int opc, void * ptr)
-{
-        int rc;
-
-        if (dev_id == OBD_DEV_ID) {
-                struct obd_ioctl_data *ioc = ptr;
-
-                //XXX hack!!!
-                ioc->ioc_plen1 = ioc->ioc_inllen1;
-                ioc->ioc_pbuf1 = ioc->ioc_bulk;
-                //XXX
-
-                rc = class_handle_ioctl(opc, (unsigned long)ptr);
-
-                printf ("proccssing ioctl cmd: %x, rc %d\n", opc,  rc);
-
-                if (rc)
-                        return rc;
-        }
-        return (0);
-}
-
-int lllib_init(char *dumpfile)
+static int lllib_init(void)
 {
-        if (g_zconf) {
-                /* XXX need setup mynid before tcpnal initialize */
-                tcpnal_mynid = ((uint64_t)getpid() << 32) | time(0);
-                printf("LibLustre: TCPNAL NID: %016llx\n", tcpnal_mynid);
-        }
-
-        init_current("dummy");
-        if (init_obdclass() ||
+        if (liblustre_init_current("dummy") ||
+            init_obdclass() ||
             init_lib_portals() ||
             ptlrpc_init() ||
             mdc_init() ||
@@ -174,20 +68,12 @@ int lllib_init(char *dumpfile)
             osc_init())
                 return -1;
 
-        if (!g_zconf && parse_dump(dumpfile, lib_ioctl))
-                return -1;
-
         return _sysio_fssw_register("llite", &llu_fssw_ops);
 }
-#if 0
-static void llu_check_request()
-{
-        liblustre_wait_event(0);
-}
-#endif
 
-int liblustre_process_log(struct config_llog_instance *cfg, int allow_recov)
+int liblustre_process_log(struct config_llog_instance *cfg,
+                          char *mdsnid, char *mdsname, char *profile,
+                          int allow_recov)
 {
         struct lustre_cfg_bufs bufs;
         struct lustre_cfg *lcfg;
@@ -206,9 +92,9 @@ int liblustre_process_log(struct config_llog_instance *cfg, int allow_recov)
         generate_random_uuid(uuid);
         class_uuid_unparse(uuid, &mdc_uuid);
 
-        nid = libcfs_str2nid(g_zconf_mdsnid);
+        nid = libcfs_str2nid(mdsnid);
         if (nid == PTL_NID_ANY) {
-                CERROR("Can't parse NID %s\n", g_zconf_mdsnid);
+                CERROR("Can't parse NID %s\n", mdsnid);
                 RETURN(-EINVAL);
         }
 
@@ -231,14 +117,14 @@ int liblustre_process_log(struct config_llog_instance *cfg, int allow_recov)
                 GOTO(out_del_uuid, err);
 
         lustre_cfg_bufs_reset(&bufs, name);
-        lustre_cfg_bufs_set_string(&bufs, 1, g_zconf_mdsname);
+        lustre_cfg_bufs_set_string(&bufs, 1, mdsname);
         lustre_cfg_bufs_set_string(&bufs, 2, peer);
         lcfg = lustre_cfg_new(LCFG_SETUP, &bufs);
         err = class_process_config(lcfg);
         lustre_cfg_free(lcfg);
         if (err < 0)
                 GOTO(out_detach, err);
-        
+
         obd = class_name2obd(name);
         if (obd == NULL)
                 GOTO(out_cleanup, err = -EINVAL);
@@ -251,14 +137,14 @@ int liblustre_process_log(struct config_llog_instance *cfg, int allow_recov)
         err = obd_connect(&mdc_conn, obd, &mdc_uuid, NULL /*connect_flags*/);
         if (err) {
                 CERROR("cannot connect to %s: rc = %d\n",
-                        g_zconf_mdsname, err);
+                        mdsname, err);
                 GOTO(out_cleanup, err);
         }
-        
+
         exp = class_conn2export(&mdc_conn);
-        
+
         ctxt = exp->exp_obd->obd_llog_ctxt[LLOG_CONFIG_REPL_CTXT];
-        rc = class_config_parse_llog(ctxt, g_zconf_profile, cfg);
+        rc = class_config_parse_llog(ctxt, profile, cfg);
         if (rc) {
                 CERROR("class_config_parse_llog failed: rc = %d\n", rc);
         }
@@ -290,13 +176,8 @@ out_del_uuid:
 out:
         if (rc == 0)
                 rc = err;
-        
-        RETURN(rc);
-}
 
-static void sighandler_USR1(int signum)
-{
-        /* do nothing */
+        RETURN(rc);
 }
 
 /* parse host:/mdsname/profile string */
@@ -312,7 +193,7 @@ int ll_parse_mount_target(const char *target, char **mdsnid,
         if ((s = strchr(buf, ':'))) {
                 *mdsnid = buf;
                 *s = '\0';
-                                                                                                                        
+
                 while (*++s == '/')
                         ;
                 *mdsname = s;
@@ -326,119 +207,161 @@ int ll_parse_mount_target(const char *target, char **mdsnid,
         return -1;
 }
 
-/* env variables */
-#define ENV_LUSTRE_MNTPNT               "LIBLUSTRE_MOUNT_POINT"
-#define ENV_LUSTRE_MNTTGT               "LIBLUSTRE_MOUNT_TARGET"
-#define ENV_LUSTRE_TIMEOUT              "LIBLUSTRE_TIMEOUT"
-#define ENV_LUSTRE_DUMPFILE             "LIBLUSTRE_DUMPFILE"
+/*
+ * early liblustre init
+ * called from C startup in catamount apps, before main()
+ *
+ * The following is a skeleton sysio startup sequence,
+ * as implemented in C startup (skipping error handling).
+ * In this framework none of these calls need be made here
+ * or in the apps themselves.  The NAMESPACE_STRING specifying
+ * the initial set of fs ops (creates, mounts, etc.) is passed
+ * as an environment variable.
+ *
+ *      _sysio_init();
+ *      _sysio_incore_init();
+ *      _sysio_native_init();
+ *      _sysio_lustre_init();
+ *      _sysio_boot(NAMESPACE_STRING);
+ *
+ * the name _sysio_lustre_init() follows the naming convention
+ * established in other fs drivers from libsysio:
+ *  _sysio_incore_init(), _sysio_native_init()
+ *
+ * _sysio_lustre_init() must be called before _sysio_boot()
+ * to enable libsysio's processing of namespace init strings containing
+ * lustre filesystem operations
+ */
+int _sysio_lustre_init(void)
+{
+        int err;
+        char *timeout = NULL;
+        char *debug_mask = NULL;
+        char *debug_subsys = NULL;
+#ifndef INIT_SYSIO
+        extern void __liblustre_cleanup_(void);
+#endif
 
-extern int _sysio_native_init();
+#if 0
+        portal_debug = -1;
+        portal_subsystem_debug = -1;
+#endif
 
-extern unsigned int obd_timeout;
+        liblustre_init_random();
 
-/* global variables */
-int     g_zconf = 0;            /* zeroconf or dumpfile */
-char   *g_zconf_mdsname = NULL; /* mdsname, for zeroconf */
-char   *g_zconf_mdsnid = NULL;  /* mdsnid, for zeroconf */
-char   *g_zconf_profile = NULL; /* profile, for zeroconf */
+        err = lllib_init();
+        if (err) {
+                perror("init llite driver");
+                return err;
+        }
+        timeout = getenv(ENV_LUSTRE_TIMEOUT);
+        if (timeout) {
+                obd_timeout = (unsigned int) strtol(timeout, NULL, 0);
+                printf("LibLustre: set obd timeout as %u seconds\n",
+                        obd_timeout);
+        }
+
+        /* debug masks */
+        debug_mask = getenv(ENV_LUSTRE_DEBUG_MASK);
+        if (debug_mask)
+                portal_debug = (unsigned int) strtol(debug_mask, NULL, 0);
+
+        debug_subsys = getenv(ENV_LUSTRE_DEBUG_SUBSYS);
+        if (debug_subsys)
+                portal_subsystem_debug =
+                                (unsigned int) strtol(debug_subsys, NULL, 0);
+
+#ifndef INIT_SYSIO
+        (void)atexit(__liblustre_cleanup_);
+#endif
+        return err;
+}
+
+extern int _sysio_native_init();
+extern unsigned int obd_timeout;
 
+char *lustre_path = NULL;
 
 void __liblustre_setup_(void)
 {
-        char *lustre_path = NULL;
         char *target = NULL;
-        char *timeout = NULL;
-        char *dumpfile = NULL;
         char *root_driver = "native";
         char *lustre_driver = "llite";
         char *root_path = "/";
         unsigned mntflgs = 0;
-
         int err;
 
-        /* consider tha case of starting multiple liblustre instances
-         * at a same time on single node.
-         */
-        srand(time(NULL) + getpid());
-
-        signal(SIGUSR1, sighandler_USR1);
-
         lustre_path = getenv(ENV_LUSTRE_MNTPNT);
         if (!lustre_path) {
                 lustre_path = "/mnt/lustre";
         }
 
+        /* mount target */
         target = getenv(ENV_LUSTRE_MNTTGT);
         if (!target) {
-                dumpfile = getenv(ENV_LUSTRE_DUMPFILE);
-                if (!dumpfile) {
-                        CERROR("Neither mount target, nor dumpfile\n");
-                        exit(1);
-                }
-                g_zconf = 0;
-                printf("LibLustre: mount point %s, dumpfile %s\n",
-                        lustre_path, dumpfile);
-        } else {
-                if (ll_parse_mount_target(target,
-                                          &g_zconf_mdsnid,
-                                          &g_zconf_mdsname,
-                                          &g_zconf_profile)) {
-                        CERROR("mal-formed target %s \n", target);
-                        exit(1);
-                }
-                g_zconf = 1;
-                printf("LibLustre: mount point %s, target %s\n",
-                        lustre_path, target);
+                printf("LibLustre: no mount target specified\n");
+                exit(1);
         }
+        printf("LibLustre: mount point %s, target %s\n",
+                lustre_path, target);
 
-        timeout = getenv(ENV_LUSTRE_TIMEOUT);
-        if (timeout) {
-                obd_timeout = (unsigned int) atoi(timeout);
-                printf("LibLustre: set obd timeout as %u seconds\n",
-                        obd_timeout);
-        }
 
-        if (_sysio_init() != 0) {
+#ifdef INIT_SYSIO
+        /* initialize libsysio & mount rootfs */
+        if (_sysio_init()) {
                 perror("init sysio");
                 exit(1);
         }
-
-        /* cygwin don't need native driver */
-#ifndef __CYGWIN__
         _sysio_native_init();
-#endif
 
         err = _sysio_mount_root(root_path, root_driver, mntflgs, NULL);
         if (err) {
-                perror(root_driver);
+                fprintf(stderr, "sysio mount failed: %s\n", strerror(errno));
                 exit(1);
         }
 
-#if 1
-        portal_debug = 0;
-        portal_subsystem_debug = 0;
-#endif
-        err = lllib_init(dumpfile);
-        if (err) {
-                perror("init llite driver");
+        if (_sysio_lustre_init())
                 exit(1);
-        }       
+#endif /* INIT_SYSIO */
 
-        err = mount("/", lustre_path, lustre_driver, mntflgs, NULL);
+        err = mount(target, lustre_path, lustre_driver, mntflgs, NULL);
         if (err) {
-                errno = -err;
-                perror(lustre_driver);
+                fprintf(stderr, "Lustre mount failed: %s\n", strerror(errno));
                 exit(1);
         }
-
-#if 0
-        __sysio_hook_sys_enter = llu_check_request;
-        __sysio_hook_sys_leave = NULL;
-#endif
 }
 
 void __liblustre_cleanup_(void)
 {
+#ifndef INIT_SYSIO
+        /* guard against being called multiple times */
+        static int cleaned = 0;
+
+        if (cleaned)
+                return;
+        cleaned++;
+#endif
+
+        /* user app might chdir to a lustre directory, and leave busy pnode
+         * during finaly libsysio cleanup. here we chdir back to "/".
+         * but it can't fix the situation that liblustre is mounted
+         * at "/".
+         */
+        chdir("/");
+#if 0
+        umount(lustre_path);
+#endif
+        /* we can't call umount here, because libsysio will not cleanup
+         * opening files for us. _sysio_shutdown() will cleanup fds at
+         * first but which will also close the sockets we need for umount
+         * liblutre. this delima lead to another hack in
+         * libsysio/src/file_hack.c FIXME
+         */
+#ifdef INIT_SYSIO
         _sysio_shutdown();
+        cleanup_lib_portals();
         PtlFini();
+#else
+        _sysio_shutdown();
+#endif
 }
index 4462311..e254ea0 100644 (file)
@@ -19,6 +19,8 @@
 struct ll_file_data {
         struct obd_client_handle fd_mds_och;
         __u32 fd_flags;
+        struct lustre_handle fd_cwlockh;
+        unsigned long fd_gid;
 };
 
 struct llu_sb_info
@@ -36,7 +38,6 @@ struct llu_sb_info
 };
 
 #define LL_SBI_NOLCK            0x1
-#define LL_SBI_READAHEAD        0x2
 
 #define LLI_F_HAVE_OST_SIZE_LOCK        0
 #define LLI_F_HAVE_MDS_SIZE_LOCK        1
@@ -49,15 +50,13 @@ struct llu_inode_info {
         char                   *lli_symlink_name;
         struct semaphore        lli_open_sem;
         __u64                   lli_maxbytes;
-        unsigned long           lli_flags;
+        unsigned long          lli_flags;
 
         /* for libsysio */
         struct file_identifier  lli_sysio_fid;
 
         struct lookup_intent   *lli_it;
 
-        /* XXX workaround for libsysio unlink */
-        int                     lli_stale_flag;
         /* XXX workaround for libsysio readdir */
         loff_t                  lli_dir_pos;
 
@@ -69,50 +68,9 @@ struct llu_inode_info {
         int                     lli_open_flags;
         int                     lli_open_count;
 
-        /* stat FIXME not 64 bit clean */
-        dev_t                   lli_st_dev;
-        ino_t                   lli_st_ino;
-        mode_t                  lli_st_mode;
-        nlink_t                 lli_st_nlink;
-        uid_t                   lli_st_uid;
-        gid_t                   lli_st_gid;
-        dev_t                   lli_st_rdev;
-        loff_t                  lli_st_size;
-        unsigned int            lli_st_blksize;
-        unsigned long           lli_st_blocks;
-        time_t                  lli_st_atime;
-        time_t                  lli_st_mtime;
-        time_t                  lli_st_ctime;
-
         /* not for stat, change it later */
-        int                    lli_st_flags;
-        unsigned long          lli_st_generation;
-};
-
-#define LLU_SYSIO_COOKIE_SIZE(exp, x) \
-        (sizeof(struct llu_sysio_cookie) + \
-         sizeof(struct ll_async_page) * (x) + \
-         sizeof(struct page) * (x) + \
-         llap_cookie_size * (x))
-
-struct llu_sysio_cookie {
-        struct obd_io_group    *lsc_oig;
-        struct inode           *lsc_inode;
-        int                     lsc_maxpages;
-        int                     lsc_npages;
-        struct ll_async_page   *lsc_llap;
-        struct page            *lsc_pages;
-        void                   *lsc_llap_cookie;
-        __u64                   lsc_rwcount;
-};
-
-/* XXX why uio.h haven't the definition? */
-#define MAX_IOVEC 32
-
-struct llu_sysio_callback_args
-{
-        int ncookies;
-        struct llu_sysio_cookie *cookies[MAX_IOVEC];
+        int                     lli_st_flags;
+        unsigned long           lli_st_generation;
 };
 
 static inline struct llu_sb_info *llu_fs2sbi(struct filesys *fs)
@@ -125,6 +83,11 @@ static inline struct llu_inode_info *llu_i2info(struct inode *inode)
         return (struct llu_inode_info*)(inode->i_private);
 }
 
+static inline struct intnl_stat *llu_i2stat(struct inode *inode)
+{
+        return &inode->i_stbuf;
+}
+
 static inline struct llu_sb_info *llu_i2sbi(struct inode *inode)
 {
         return llu_i2info(inode)->lli_sbi;
@@ -153,8 +116,8 @@ do {                                                                           \
                OBD_ALLOC(temp, sizeof(*temp));                                        \
         memcpy(temp, it, sizeof(*temp));                                       \
         llu_i2info(inode)->lli_it = temp;                                      \
-        CDEBUG(D_DENTRY, "alloc intent %p to inode %p(ino %lu)\n",             \
-                        temp, inode, llu_i2info(inode)->lli_st_ino);           \
+        CDEBUG(D_DENTRY, "alloc intent %p to inode %p(ino %llu)\n",            \
+                        temp, inode, llu_i2stat(inode)->st_ino);               \
 } while(0)
 
 
@@ -164,8 +127,8 @@ do {                                                                           \
                                                                                \
         LASSERT(it);                                                           \
         llu_i2info(inode)->lli_it = NULL;                                      \
-        CDEBUG(D_DENTRY, "dettach intent %p from inode %p(ino %lu)\n",         \
-                        it, inode, llu_i2info(inode)->lli_st_ino);             \
+        CDEBUG(D_DENTRY, "dettach intent %p from inode %p(ino %llu)\n",        \
+                        it, inode, llu_i2stat(inode)->st_ino);                 \
 } while(0)
 
 /* interpet return codes from intent lookup */
@@ -186,26 +149,24 @@ struct it_cb_data {
 static inline void ll_i2uctxt(struct ll_uctxt *ctxt, struct inode *i1,
                               struct inode *i2)
 {
-        struct llu_inode_info *lli1 = llu_i2info(i1);
-        struct llu_inode_info *lli2;
+        struct intnl_stat *st = llu_i2stat(i1);
 
         LASSERT(i1);
         LASSERT(ctxt);
 
-        if (in_group_p(lli1->lli_st_gid))
-                ctxt->gid1 = lli1->lli_st_gid;
+        if (in_group_p(st->st_gid))
+                ctxt->gid1 = st->st_gid;
         else
                 ctxt->gid1 = -1;
 
         if (i2) {
-                lli2 = llu_i2info(i2);
-                if (in_group_p(lli2->lli_st_gid))
-                        ctxt->gid2 = lli2->lli_st_gid;
+                st = llu_i2stat(i2);
+                if (in_group_p(st->st_gid))
+                        ctxt->gid2 = st->st_gid;
                 else
                         ctxt->gid2 = -1;
-        } else {
-                ctxt->gid2 = -1;
-        }
+        } else 
+                ctxt->gid2 = 0;
 }
 
 
@@ -215,12 +176,6 @@ typedef int (*intent_finish_cb)(struct ptlrpc_request *,
 int llu_intent_lock(struct inode *parent, struct pnode *pnode,
                     struct lookup_intent *, int flags, intent_finish_cb);
 
-/* FIXME */
-static inline int ll_permission(struct inode *inode, int flag, void * unused)
-{
-        return 0;
-}
-
 static inline __u64 ll_file_maxbytes(struct inode *inode)
 {
         return llu_i2info(inode)->lli_maxbytes;
@@ -232,16 +187,19 @@ struct mount_option_s
         char *osc_uuid;
 };
 
+#define IS_BAD_PTR(ptr)         \
+        ((unsigned long)(ptr) == 0 || (unsigned long)(ptr) > -1000UL)
+
 /* llite_lib.c */
 void generate_random_uuid(unsigned char uuid_out[16]);
-int liblustre_process_log(struct config_llog_instance *cfg, int allow_recov);
+int liblustre_process_log(struct config_llog_instance *cfg,
+                       char *mdsnid,
+                       char *mdsname,
+                       char *profile,
+                       int allow_recov);
 int ll_parse_mount_target(const char *target, char **mdsnid,
                           char **mdsname, char **profile);
 
-extern int     g_zconf;
-extern char   *g_zconf_mdsnid;
-extern char   *g_zconf_mdsname;
-extern char   *g_zconf_profile;
 extern struct mount_option_s mount_option;
 
 /* super.c */
@@ -252,6 +210,7 @@ void obdo_from_inode(struct obdo *dst, struct inode *src, obd_flag valid);
 int ll_it_open_error(int phase, struct lookup_intent *it);
 struct inode *llu_iget(struct filesys *fs, struct lustre_md *md);
 int llu_inode_getattr(struct inode *inode, struct lov_stripe_md *lsm);
+int llu_setattr_raw(struct inode *inode, struct iattr *attr);
 
 extern struct fssw_ops llu_fssw_ops;
 
@@ -266,20 +225,15 @@ int llu_create(struct inode *dir, struct pnode_base *pnode, int mode);
 int llu_iop_open(struct pnode *pnode, int flags, mode_t mode);
 int llu_mdc_close(struct obd_export *mdc_exp, struct inode *inode);
 int llu_iop_close(struct inode *inode);
-int llu_iop_ipreadv(struct inode *ino, struct ioctx *ioctxp);
-int llu_iop_ipwritev(struct inode *ino, struct ioctx *ioctxp);
+_SYSIO_OFF_T llu_iop_pos(struct inode *ino, _SYSIO_OFF_T off);
 int llu_vmtruncate(struct inode * inode, loff_t offset);
 void obdo_refresh_inode(struct inode *dst, struct obdo *src, obd_flag valid);
 int llu_objects_destroy(struct ptlrpc_request *request, struct inode *dir);
 
 /* rw.c */
-int llu_iop_iodone(struct ioctx *ioctxp __IS_UNUSED);
-struct llu_sysio_callback_args*
-llu_file_write(struct inode *inode, const struct iovec *iovec,
-               size_t iovlen, loff_t pos);
-struct llu_sysio_callback_args*
-llu_file_read(struct inode *inode, const struct iovec *iovec,
-              size_t iovlen, loff_t pos);
+int llu_iop_read(struct inode *ino, struct ioctx *ioctxp);
+int llu_iop_write(struct inode *ino, struct ioctx *ioctxp);
+int llu_iop_iodone(struct ioctx *ioctxp);
 int llu_glimpse_size(struct inode *inode);
 int llu_extent_lock(struct ll_file_data *fd, struct inode *inode,
                     struct lov_stripe_md *lsm, int mode,
index 7ad8aa5..2227b0b 100644 (file)
 #include <signal.h>
 #include <sys/types.h>
 
-#ifndef REDSTORM
 #include <fcntl.h>
+#ifdef HAVE_NETDB_H
 #include <netdb.h>
+#endif
 #include <syscall.h>
 #include <sys/utsname.h>
+#ifdef HAVE_NETINET_IN_H
 #include <netinet/in.h>
+#endif
 #include <sys/socket.h>
+#ifdef HAVE_ARPA_INET_H
 #include <arpa/inet.h>
-#else
-#include <sys/socket.h>
+#endif
+#ifdef HAVE_CATAMOUNT_DATA_H
 #include <catamount/data.h>
 #endif
 
 #include "lutil.h"
 
-#ifdef CRAY_PORTALS
+#if CRAY_PORTALS
 void portals_debug_dumplog(void){};
 #endif
 
-unsigned int portal_subsystem_debug = ~0 - S_NAL;
+unsigned int portal_subsystem_debug = ~0 - (S_PORTALS | S_NAL);
 unsigned int portal_debug = 0;
 
 struct task_struct     *current;
-ptl_handle_ni_t         tcpnal_ni;
-ptl_nid_t               tcpnal_mynid;
 
 void *inter_module_get(char *arg)
 {
-        if (!strcmp(arg, "tcpnal_ni"))
-                return &tcpnal_ni;
-        else if (!strcmp(arg, "ldlm_cli_cancel_unused"))
+        if (!strcmp(arg, "ldlm_cli_cancel_unused"))
                 return ldlm_cli_cancel_unused;
         else if (!strcmp(arg, "ldlm_namespace_cleanup"))
                 return ldlm_namespace_cleanup;
@@ -65,68 +65,14 @@ void *inter_module_get(char *arg)
                 return NULL;
 }
 
-char *portals_nid2str(int nal, ptl_nid_t nid, char *str)
-{
-        if (nid == PTL_NID_ANY) {
-                snprintf(str, PTL_NALFMT_SIZE - 1, "%s",
-                         "PTL_NID_ANY");
-                return str;
-        }
-
-        switch(nal){
-#ifndef CRAY_PORTALS
-        case TCPNAL:
-                /* userspace NAL */
-        case OPENIBNAL:
-        case SOCKNAL:
-                snprintf(str, PTL_NALFMT_SIZE - 1, "%u:%u.%u.%u.%u",
-                         (__u32)(nid >> 32), HIPQUAD(nid));
-                break;
-        case QSWNAL:
-        case GMNAL:
-                snprintf(str, PTL_NALFMT_SIZE - 1, "%u:%u",
-                         (__u32)(nid >> 32), (__u32)nid);
-                break;
-#endif
-        default:
-                snprintf(str, PTL_NALFMT_SIZE - 1, "?%d? %llx",
-                         nal, (long long)nid);
-                break;
-        }
-        return str;
-}
-
-char *portals_id2str(int nal, ptl_process_id_t id, char *str)
-{
-        switch(nal){
-#ifndef CRAY_PORTALS
-        case TCPNAL:
-                /* userspace NAL */
-        case OPENIBNAL:
-        case SOCKNAL:
-                snprintf(str, PTL_NALFMT_SIZE - 1, "%u:%u.%u.%u.%u,%u",
-                         (__u32)(id.nid >> 32), HIPQUAD((id.nid)) , id.pid);
-                break;
-        case QSWNAL:
-        case GMNAL:
-                snprintf(str, PTL_NALFMT_SIZE - 1, "%u:%u,%u",
-                         (__u32)(id.nid >> 32), (__u32)id.nid, id.pid);
-                break;
-#endif
-        default:
-                snprintf(str, PTL_NALFMT_SIZE - 1, "?%d? %llx,%lx",
-                         nal, (long long)id.nid, (long)id.pid );
-                break;
-        }
-        return str;
-}
-
-#ifndef REDSTORM
 /*
  * random number generator stuff
  */
+#ifdef LIBLUSTRE_USE_URANDOM
 static int _rand_dev_fd = -1;
+#endif
 
+#ifdef HAVE_GETHOSTBYNAME
 static int get_ipv4_addr()
 {
         struct utsname myname;
@@ -148,25 +94,33 @@ static int get_ipv4_addr()
 
         return ip;
 }
+#endif
 
 void liblustre_init_random()
 {
         int seed;
         struct timeval tv;
 
+#ifdef LIBLUSTRE_USE_URANDOM
         _rand_dev_fd = syscall(SYS_open, "/dev/urandom", O_RDONLY);
         if (_rand_dev_fd >= 0) {
-                if (syscall(SYS_read, _rand_dev_fd, &seed, sizeof(int)) ==
-                    sizeof(int)) {
+                if (syscall(SYS_read, _rand_dev_fd,
+                            &seed, sizeof(int)) == sizeof(int)) {
                         srand(seed);
                         return;
                 }
                 syscall(SYS_close, _rand_dev_fd);
                 _rand_dev_fd = -1;
         }
+#endif /* LIBLUSTRE_USE_URANDOM */
 
+#ifdef HAVE_GETHOSTBYNAME
+        seed = get_ipv4_addr();
+#else
+        seed = _my_pnid;
+#endif
         gettimeofday(&tv, NULL);
-        srand(tv.tv_sec + tv.tv_usec + getpid() + __swab32(get_ipv4_addr()));
+        srand(tv.tv_sec + tv.tv_usec + getpid() + __swab32(seed));
 }
 
 void get_random_bytes(void *buf, int size)
@@ -174,12 +128,14 @@ void get_random_bytes(void *buf, int size)
         char *p = buf;
         LASSERT(size >= 0);
 
+#ifdef LIBLUSTRE_USE_URANDOM
         if (_rand_dev_fd >= 0) {
                 if (syscall(SYS_read, _rand_dev_fd, buf, size) == size)
                         return;
                 syscall(SYS_close, _rand_dev_fd);
                 _rand_dev_fd = -1;
         }
+#endif
 
         while (size--) 
                 *p++ = rand();
@@ -187,6 +143,7 @@ void get_random_bytes(void *buf, int size)
 
 static void init_capability(int *res)
 {
+#ifdef HAVE_LIBCAP
         cap_t syscap;
         cap_flag_value_t capval;
         int i;
@@ -207,69 +164,20 @@ static void init_capability(int *res)
                         }
                 }
         }
+#else
+       /*
+        * set fake cap flags to ship to linux server
+        * from client platforms that have none (eg. catamount)
+        *  full capability for root
+        *  no capability for anybody else
+        */
+#define FAKE_ROOT_CAP 0x1ffffeff
+#define FAKE_USER_CAP 0
+
+       *res = (current->fsuid == 0) ? FAKE_ROOT_CAP: FAKE_USER_CAP;
+#endif
 }
 
-void liblustre_set_nal_nid()
-{
-        pid_t pid;
-        uint32_t ip;
-        struct in_addr in;
-
-        /* need to setup mynid before tcpnal initialization */
-        /* a meaningful nid could help debugging */
-        ip = get_ipv4_addr();
-        if (ip == 0)
-                get_random_bytes(&ip, sizeof(ip));
-        pid = getpid() & 0xffffffff;
-        tcpnal_mynid = ((uint64_t)ip << 32) | pid;
-
-        in.s_addr = htonl(ip);
-        printf("LibLustre: TCPNAL NID: %016llx (%s:%u)\n", 
-               tcpnal_mynid, inet_ntoa(in), pid);
-}
-
-#else /* REDSTORM */
-
-void liblustre_init_random()
-{
-        struct timeval tv;
-        UINT32 nodeid;
-
-        gettimeofday(&tv, NULL);
-        nodeid = _my_pnid;
-        srand(tv.tv_sec + tv.tv_usec + getpid() + __swab32(nodeid));
-}
-
-void get_random_bytes(void *buf, int size)
-{
-        char *p = buf;
-        LASSERT(size >= 0);
-
-        while (size--) 
-                *p++ = rand();
-}
-
-static void init_capability(int *res)
-{
-        *res = 0;
-}
-
-void liblustre_set_nal_nid()
-{
-        pid_t pid;
-        uint32_t ip;
-
-        ip = _my_pnid;
-        if (ip & 0xFF)
-                ip <<= 8;
-        pid = getpid() & 0xFF;
-        tcpnal_mynid = ip | pid;
-        printf("LibLustre: NAL NID: %08x (%u)\n", 
-               tcpnal_mynid, pid);
-}
-
-#endif /* REDSOTRM */
-
 int in_group_p(gid_t gid)
 {
         int i;
@@ -292,9 +200,6 @@ int liblustre_init_current(char *comm)
                 CERROR("Not enough memory\n");
                 return -ENOMEM;
         }
-        current->fs = &current->__fs;
-        current->fs->umask = umask(0777);
-        umask(current->fs->umask);
 
         strncpy(current->comm, comm, sizeof(current->comm));
         current->pid = getpid();
@@ -343,10 +248,3 @@ void cleanup_lib_portals()
 {
         ptlrpc_exit_portals();
 }
-
-int
-libcfs_nal_cmd(struct portals_cfg *pcfg)
-{
-        /* handle portals command if we want */
-        return 0;
-}
index dc67a23..dc5e6e2 100644 (file)
@@ -28,7 +28,6 @@
 
 void liblustre_init_random(void);
 int liblustre_init_current(char *comm);
-void liblustre_set_nal_nid(void);
 int init_lib_portals(void);
 void cleanup_lib_portals(void);
 
index 74eddb2..9e69a9e 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Lustre Light name resolution
  *
- *  Copyright (c) 2002, 2003 Cluster File Systems, Inc.
+ *  Copyright (c) 2002-2004 Cluster File Systems, Inc.
  *
  *   This file is part of Lustre, http://www.lustre.org.
  *
 #include <sys/fcntl.h>
 #include <sys/queue.h>
 
+#ifdef HAVE_XTIO_H
+#include <xtio.h>
+#endif
 #include <sysio.h>
 #include <fs.h>
 #include <mount.h>
 #include <inode.h>
+#ifdef HAVE_FILE_H
 #include <file.h>
+#endif
 
 #undef LIST_HEAD
 
@@ -70,6 +75,7 @@ static void ll_intent_release(struct lookup_intent *it)
         EXIT;
 }
 
+#if 0
 /*
  * remove the stale inode from pnode
  */
@@ -94,6 +100,7 @@ void unhook_stale_inode(struct pnode *pno)
         EXIT;
         return;
 }
+#endif
 
 void llu_lookup_finish_locks(struct lookup_intent *it, struct pnode *pnode)
 {
@@ -102,14 +109,14 @@ void llu_lookup_finish_locks(struct lookup_intent *it, struct pnode *pnode)
 
         if (it && pnode->p_base->pb_ino != NULL) {
                 struct inode *inode = pnode->p_base->pb_ino;
-                CDEBUG(D_DLMTRACE, "setting l_data to inode %p (%lu/%lu)\n",
-                       inode, llu_i2info(inode)->lli_st_ino,
+                CDEBUG(D_DLMTRACE, "setting l_data to inode %p (%llu/%lu)\n",
+                       inode, llu_i2stat(inode)->st_ino,
                        llu_i2info(inode)->lli_st_generation);
                 mdc_set_lock_data(&it->d.lustre.it_lock_handle, inode);
         }
 
-        /* drop IT_LOOKUP locks */
-        if (it->it_op == IT_LOOKUP)
+        /* drop lookup/getattr locks */
+        if (it->it_op == IT_LOOKUP || it->it_op == IT_GETATTR)
                 ll_intent_release(it);
 
 }
@@ -140,23 +147,25 @@ int llu_mdc_blocking_ast(struct ldlm_lock *lock,
         case LDLM_CB_CANCELING: {
                 struct inode *inode = llu_inode_from_lock(lock);
                 struct llu_inode_info *lli;
+                struct intnl_stat *st;
 
                 /* Invalidate all dentries associated with this inode */
                 if (inode == NULL)
                         break;
 
                 lli =  llu_i2info(inode);
+                st = llu_i2stat(inode);
 
                 clear_bit(LLI_F_HAVE_MDS_SIZE_LOCK, &lli->lli_flags);
 
-                if (lock->l_resource->lr_name.name[0] != lli->lli_st_ino ||
+                if (lock->l_resource->lr_name.name[0] != st->st_ino ||
                     lock->l_resource->lr_name.name[1] != lli->lli_st_generation) {
-                        LDLM_ERROR(lock, "data mismatch with ino %lu/%lu",
-                                   lli->lli_st_ino, lli->lli_st_generation);
+                        LDLM_ERROR(lock, "data mismatch with ino %llu/%lu",
+                                   st->st_ino, lli->lli_st_generation);
                 }
-                if (S_ISDIR(lli->lli_st_mode)) {
-                        CDEBUG(D_INODE, "invalidating inode %lu\n",
-                               lli->lli_st_ino);
+                if (S_ISDIR(st->st_mode)) {
+                        CDEBUG(D_INODE, "invalidating inode %llu\n",
+                               st->st_ino);
 
                         llu_invalidate_inode_pages(inode);
                 }
@@ -215,8 +224,8 @@ int llu_pb_revalidate(struct pnode *pnode, int flags, struct lookup_intent *it)
         int rc;
         ENTRY;
 
-        CDEBUG(D_VFSTRACE, "VFS Op:name=%s,intent=%x\n",
-               pb->pb_name.name, it ? it->it_op : 0);
+        CDEBUG(D_VFSTRACE, "VFS Op:name=%.*s,intent=%x\n",
+               (int)pb->pb_name.len, pb->pb_name.name, it ? it->it_op : 0);
 
         /* We don't want to cache negative dentries, so return 0 immediately.
          * We believe that this is safe, that negative dentries cannot be
@@ -226,26 +235,16 @@ int llu_pb_revalidate(struct pnode *pnode, int flags, struct lookup_intent *it)
                 RETURN(0);
         }
 
-        /* check stale inode */
-        if (llu_i2info(pb->pb_ino)->lli_stale_flag)
-                unhook_stale_inode(pnode);
-
-        /* check again because unhook_stale_inode() might generate
-         * negative pnode */
-        if (pb->pb_ino == NULL) {
-                CDEBUG(D_INODE, "negative pb\n");
-                RETURN(0);
-        }
-
         /* This is due to bad interaction with libsysio. remove this when we
          * switched to libbsdio XXX
          */
         {
                 struct llu_inode_info *lli = llu_i2info(pb->pb_ino);
+                struct intnl_stat *st = llu_i2stat(pb->pb_ino);
                 if (lli->lli_it) {
-                        CDEBUG(D_INODE, "inode %lu still have intent "
+                        CDEBUG(D_INODE, "inode %llu still have intent "
                                         "%p(opc 0x%x), release it\n",
-                                        lli->lli_st_ino, lli->lli_it,
+                                        st->st_ino, lli->lli_it,
                                         lli->lli_it->it_op);
                         ll_intent_release(lli->lli_it);
                         OBD_FREE(lli->lli_it, sizeof(*lli->lli_it));
@@ -279,14 +278,19 @@ int llu_pb_revalidate(struct pnode *pnode, int flags, struct lookup_intent *it)
                 GOTO(out, rc = 0);
 
         rc = pnode_revalidate_finish(req, 1, it, pnode);
+        if (rc != 0) {
+                ll_intent_release(it);
+                GOTO(out, rc = 0);
+        }
+        rc = 1;
 
         /* Note: ll_intent_lock may cause a callback, check this! */
 
-        if (it->it_op & (IT_OPEN | IT_GETATTR))
+        if (it->it_op & IT_OPEN)
                 LL_SAVE_INTENT(pb->pb_ino, it);
-        RETURN(1);
+
  out:
-        if (req)
+        if (req && rc == 1)
                 ptlrpc_req_finished(req);
         if (rc == 0) {
                 LASSERT(pb->pb_ino);
@@ -294,9 +298,6 @@ int llu_pb_revalidate(struct pnode *pnode, int flags, struct lookup_intent *it)
                 pb->pb_ino = NULL;
         } else {
                 llu_lookup_finish_locks(it, pnode);
-                llu_i2info(pb->pb_ino)->lli_stale_flag = 0;
-                if (it->it_op & (IT_OPEN | IT_GETATTR))
-                        LL_SAVE_INTENT(pb->pb_ino, it);
         }
         RETURN(rc);
 }
@@ -311,13 +312,37 @@ static int lookup_it_finish(struct ptlrpc_request *request, int offset,
         struct inode *inode = NULL;
         int rc;
 
+        /* libsysio require us generate inode right away if success.
+         * so if mds created new inode for us we need make sure it
+         * succeeded. thus for any error we can't delay to the
+         * llu_file_open() time. */
+        if (it_disposition(it, DISP_OPEN_CREATE) &&
+            it_open_error(DISP_OPEN_CREATE, it)) {
+                CDEBUG(D_INODE, "detect mds create error\n");
+                return it_open_error(DISP_OPEN_CREATE, it);
+        }
+        if (it_disposition(it, DISP_OPEN_OPEN) &&
+            it_open_error(DISP_OPEN_OPEN, it)) {
+                CDEBUG(D_INODE, "detect mds open error\n");
+                /* undo which did by mdc_intent_lock */
+                if (it_disposition(it, DISP_OPEN_CREATE) &&
+                    !it_open_error(DISP_OPEN_CREATE, it)) {
+                        LASSERT(request);
+                        LASSERT(atomic_read(&request->rq_refcount) > 1);
+                        CDEBUG(D_INODE, "dec a ref of req %p\n", request);
+                        ptlrpc_req_finished(request);
+                }
+                return it_open_error(DISP_OPEN_OPEN, it);
+        }
+
         /* NB 1 request reference will be taken away by ll_intent_lock()
          * when I return
-         * Note: libsysio require the inode must be generated here
          */
-        if ((it->it_op & IT_CREAT) || !it_disposition(it, DISP_LOOKUP_NEG)) {
+        if (!it_disposition(it, DISP_LOOKUP_NEG) ||
+            (it->it_op & IT_CREAT)) {
                 struct lustre_md md;
                 struct llu_inode_info *lli;
+                struct intnl_stat *st;
                 ENTRY;
 
                 rc = mdc_req2lustre_md(request, offset, sbi->ll_osc_exp, &md);
@@ -325,23 +350,23 @@ static int lookup_it_finish(struct ptlrpc_request *request, int offset,
                         RETURN(rc);
 
                 inode = llu_iget(parent->i_fs, &md);
-                if (!inode) {
+                if (!inode || IS_ERR(inode)) {
                         /* free the lsm if we allocated one above */
                         if (md.lsm != NULL)
                                 obd_free_memmd(sbi->ll_osc_exp, &md.lsm);
-                        RETURN(-ENOMEM);
+                        RETURN(inode ? PTR_ERR(inode) : -ENOMEM);
                 } else if (md.lsm != NULL &&
                            llu_i2info(inode)->lli_smd != md.lsm) {
                         obd_free_memmd(sbi->ll_osc_exp, &md.lsm);
                 }
 
                 lli = llu_i2info(inode);
+                st = llu_i2stat(inode);
 
                 /* If this is a stat, get the authoritative file size */
-                if (it->it_op == IT_GETATTR && S_ISREG(lli->lli_st_mode) &&
+                if (it->it_op == IT_GETATTR && S_ISREG(st->st_mode) &&
                     lli->lli_smd != NULL) {
                         struct lov_stripe_md *lsm = lli->lli_smd;
-                        struct ost_lvb lvb;
                         ldlm_error_t rc;
 
                         LASSERT(lsm->lsm_object_id != 0);
@@ -360,7 +385,7 @@ static int lookup_it_finish(struct ptlrpc_request *request, int offset,
         }
 
         /* intent will be further used in cases of open()/getattr() */
-        if (inode && (it->it_op & (IT_OPEN | IT_GETATTR)))
+        if (inode && (it->it_op & IT_OPEN))
                 LL_SAVE_INTENT(inode, it);
 
         child->p_base->pb_ino = inode;
@@ -517,6 +542,8 @@ int llu_iop_lookup(struct pnode *pnode,
         int rc;
         ENTRY;
 
+        liblustre_wait_event(0);
+
         *inop = NULL;
 
         /* the mount root inode have no name, so don't call
@@ -550,4 +577,3 @@ int llu_iop_lookup(struct pnode *pnode,
 
         RETURN(rc);
 }
-
index ea99362..a20e18c 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Lustre Light block IO
  *
- *  Copyright (c) 2002, 2003 Cluster File Systems, Inc.
+ *  Copyright (c) 2002-2004 Cluster File Systems, Inc.
  *
  *   This file is part of Lustre, http://www.lustre.org.
  *
 #include <assert.h>
 #include <time.h>
 #include <sys/types.h>
+#include <sys/stat.h>
 #include <sys/queue.h>
 #include <fcntl.h>
+#include <sys/uio.h>
 
+#ifdef HAVE_XTIO_H
+#include <xtio.h>
+#endif
 #include <sysio.h>
 #include <fs.h>
 #include <mount.h>
 #include <inode.h>
+#ifdef HAVE_FILE_H
 #include <file.h>
+#endif
 
 #undef LIST_HEAD
 
 #include "llite_lib.h"
 
+struct llu_io_group
+{
+        struct obd_io_group    *lig_oig;
+        struct inode           *lig_inode;
+        int                     lig_maxpages;
+        int                     lig_npages;
+        __u64                   lig_rwcount;
+        struct ll_async_page   *lig_llaps;
+        struct page            *lig_pages;
+        void                   *lig_llap_cookies;
+};
+
+#define LLU_IO_GROUP_SIZE(x) \
+        (sizeof(struct llu_io_group) + \
+         (sizeof(struct ll_async_page) + \
+          sizeof(struct page) + \
+          llap_cookie_size) * (x))
+
+struct llu_io_session
+{
+        struct inode           *lis_inode;
+        int                     lis_cmd;
+        int                     lis_max_groups;
+        int                     lis_ngroups;
+        struct llu_io_group    *lis_groups[0];
+};
+#define LLU_IO_SESSION_SIZE(x)  \
+        (sizeof(struct llu_io_session) + (x) * 2 * sizeof(void *))
+
+
+typedef ssize_t llu_file_piov_t(const struct iovec *iovec, int iovlen,
+                                _SYSIO_OFF_T pos, ssize_t len,
+                                void *private);
+
 size_t llap_cookie_size;
 
-static int llu_lock_to_stripe_offset(struct inode *inode,struct ldlm_lock *lock)
+static int llu_lock_to_stripe_offset(struct inode *inode, struct ldlm_lock *lock)
 {
         struct llu_inode_info *lli = llu_i2info(inode);
         struct lov_stripe_md *lsm = lli->lli_smd;
@@ -162,7 +203,7 @@ static int llu_glimpse_callback(struct ldlm_lock *lock, void *reqp)
         lvb->lvb_size = lli->lli_smd->lsm_oinfo[stripe].loi_kms;
 
         LDLM_DEBUG(lock, "i_size: %llu -> stripe number %u -> kms "LPU64,
-                   lli->lli_st_size, stripe, lvb->lvb_size);
+                   llu_i2stat(inode)->st_size, stripe, lvb->lvb_size);
  iput:
         I_RELE(inode);
  out:
@@ -175,37 +216,35 @@ static int llu_glimpse_callback(struct ldlm_lock *lock, void *reqp)
         return rc;
 }
 
-__u64 lov_merge_size(struct lov_stripe_md *lsm, int kms);
-__u64 lov_merge_blocks(struct lov_stripe_md *lsm);
-__u64 lov_merge_mtime(struct lov_stripe_md *lsm, __u64 current_time);
-
 /* NB: lov_merge_size will prefer locally cached writes if they extend the
  * file (because it prefers KMS over RSS when larger) */
 int llu_glimpse_size(struct inode *inode)
 {
         struct llu_inode_info *lli = llu_i2info(inode);
+        struct intnl_stat *st = llu_i2stat(inode);
         struct llu_sb_info *sbi = llu_i2sbi(inode);
         ldlm_policy_data_t policy = { .l_extent = { 0, OBD_OBJECT_EOF } };
         struct lustre_handle lockh = { 0 };
         int rc, flags = LDLM_FL_HAS_INTENT;
         ENTRY;
 
-        CDEBUG(D_DLMTRACE, "Glimpsing inode %lu\n", lli->lli_st_ino);
+        CDEBUG(D_DLMTRACE, "Glimpsing inode %llu\n", st->st_ino);
 
         rc = obd_enqueue(sbi->ll_osc_exp, lli->lli_smd, LDLM_EXTENT, &policy,
                          LCK_PR, &flags, llu_extent_lock_callback,
                          ldlm_completion_ast, llu_glimpse_callback, inode,
                          sizeof(struct ost_lvb), lustre_swab_ost_lvb, &lockh);
-        if (rc > 0)
-                RETURN(-EIO);
-
-        lli->lli_st_size = lov_merge_size(lli->lli_smd, 0);
-        lli->lli_st_blocks = lov_merge_blocks(lli->lli_smd);
-        lli->lli_st_mtime = lov_merge_mtime(lli->lli_smd, lli->lli_st_mtime);
+        if (rc) {
+                CERROR("obd_enqueue returned rc %d, returning -EIO\n", rc);
+                RETURN(rc > 0 ? -EIO : rc);
+        }
 
-        CDEBUG(D_DLMTRACE, "glimpse: size: %llu, blocks: %lu\n",
-               lli->lli_st_size, lli->lli_st_blocks);
+        st->st_size = lov_merge_size(lli->lli_smd, 0);
+        st->st_blocks = lov_merge_blocks(lli->lli_smd);
+        //lli->lli_st_mtime = lov_merge_mtime(lli->lli_smd, inode->i_mtime);
 
+        CDEBUG(D_DLMTRACE, "glimpse: size: %llu, blocks: %llu\n",
+               st->st_size, st->st_blocks);
 
         obd_cancel(sbi->ll_osc_exp, lli->lli_smd, LCK_PR, &lockh);
 
@@ -218,7 +257,7 @@ int llu_extent_lock(struct ll_file_data *fd, struct inode *inode,
                     int ast_flags)
 {
         struct llu_sb_info *sbi = llu_i2sbi(inode);
-        struct llu_inode_info *lli = llu_i2info(inode);
+        struct intnl_stat *st = llu_i2stat(inode);
         int rc;
         ENTRY;
 
@@ -229,8 +268,8 @@ int llu_extent_lock(struct ll_file_data *fd, struct inode *inode,
             (sbi->ll_flags & LL_SBI_NOLCK))
                 RETURN(0);
 
-        CDEBUG(D_DLMTRACE, "Locking inode %lu, start "LPU64" end "LPU64"\n",
-               lli->lli_st_ino, policy->l_extent.start, policy->l_extent.end);
+        CDEBUG(D_DLMTRACE, "Locking inode %llu, start "LPU64" end "LPU64"\n",
+               st->st_ino, policy->l_extent.start, policy->l_extent.end);
 
         rc = obd_enqueue(sbi->ll_osc_exp, lsm, LDLM_EXTENT, policy, mode,
                          &ast_flags, llu_extent_lock_callback,
@@ -241,91 +280,13 @@ int llu_extent_lock(struct ll_file_data *fd, struct inode *inode,
 
         if (policy->l_extent.start == 0 &&
             policy->l_extent.end == OBD_OBJECT_EOF)
-                lli->lli_st_size = lov_merge_size(lsm, 1);
-
-        RETURN(rc);
-}
-
-#if 0
-int llu_extent_lock_no_validate(struct ll_file_data *fd,
-                                struct inode *inode,
-                                struct lov_stripe_md *lsm,
-                                int mode,
-                                struct ldlm_extent *extent,
-                                struct lustre_handle *lockh,
-                                int ast_flags)
-{
-        struct llu_sb_info *sbi = llu_i2sbi(inode);
-        struct llu_inode_info *lli = llu_i2info(inode);
-        int rc;
-        ENTRY;
-
-        LASSERT(lockh->cookie == 0);
-
-        /* XXX phil: can we do this?  won't it screw the file size up? */
-        if ((fd && (fd->fd_flags & LL_FILE_IGNORE_LOCK)) ||
-            (sbi->ll_flags & LL_SBI_NOLCK))
-                RETURN(0);
-
-        CDEBUG(D_DLMTRACE, "Locking inode %lu, start "LPU64" end "LPU64"\n",
-               lli->lli_st_ino, extent->start, extent->end);
+                st->st_size = lov_merge_size(lsm, 1);
 
-        rc = obd_enqueue(sbi->ll_osc_exp, lsm, NULL, LDLM_EXTENT, extent,
-                         sizeof(extent), mode, &ast_flags,
-                         llu_extent_lock_callback, inode, lockh);
+        //inode->i_mtime = lov_merge_mtime(lsm, inode->i_mtime);
 
         RETURN(rc);
 }
 
-/*
- * this grabs a lock and manually implements behaviour that makes it look like
- * the OST is returning the file size with each lock acquisition.
- */
-int llu_extent_lock(struct ll_file_data *fd, struct inode *inode,
-                    struct lov_stripe_md *lsm, int mode,
-                    struct ldlm_extent *extent, struct lustre_handle *lockh)
-{
-        struct llu_inode_info *lli = llu_i2info(inode);
-        struct obd_export *exp = llu_i2obdexp(inode);
-        struct ldlm_extent size_lock;
-        struct lustre_handle match_lockh = {0};
-        int flags, rc, matched;
-        ENTRY;
-
-        rc = llu_extent_lock_no_validate(fd, inode, lsm, mode, extent, lockh, 0);
-        if (rc != ELDLM_OK)
-                RETURN(rc);
-
-        if (test_bit(LLI_F_HAVE_OST_SIZE_LOCK, &lli->lli_flags))
-                RETURN(0);
-
-        rc = llu_inode_getattr(inode, lsm);
-        if (rc) {
-                llu_extent_unlock(fd, inode, lsm, mode, lockh);
-                RETURN(rc);
-        }
-
-        size_lock.start = lli->lli_st_size;
-        size_lock.end = OBD_OBJECT_EOF;
-
-        /* XXX I bet we should be checking the lock ignore flags.. */
-        /* FIXME use LDLM_FL_TEST_LOCK instead */
-        flags = LDLM_FL_CBPENDING | LDLM_FL_BLOCK_GRANTED;
-        matched = obd_match(exp, lsm, LDLM_EXTENT, &size_lock,
-                            sizeof(size_lock), LCK_PR, &flags, inode,
-                            &match_lockh);
-
-        /* hey, alright, we hold a size lock that covers the size we
-         * just found, its not going to change for a while.. */
-        if (matched == 1) {
-                set_bit(LLI_F_HAVE_OST_SIZE_LOCK, &lli->lli_flags);
-                obd_cancel(exp, lsm, LCK_PR, &match_lockh);
-        }
-
-        RETURN(0);
-}
-#endif
-
 int llu_extent_unlock(struct ll_file_data *fd, struct inode *inode,
                 struct lov_stripe_md *lsm, int mode,
                 struct lustre_handle *lockh)
@@ -354,14 +315,6 @@ struct ll_async_page {
         struct inode   *llap_inode;
 };
 
-static struct ll_async_page *llap_from_cookie(void *cookie)
-{
-        struct ll_async_page *llap = cookie;
-        if (llap->llap_magic != LLAP_MAGIC)
-                return ERR_PTR(-EINVAL);
-        return llap;
-};
-
 static void llu_ap_fill_obdo(void *data, int cmd, struct obdo *oa)
 {
         struct ll_async_page *llap;
@@ -370,12 +323,7 @@ static void llu_ap_fill_obdo(void *data, int cmd, struct obdo *oa)
         obd_flag valid_flags;
         ENTRY;
 
-        llap = llap_from_cookie(data);
-        if (IS_ERR(llap)) {
-                EXIT;
-                return;
-        }
-
+        llap = LLAP_FROM_COOKIE(data);
         inode = llap->llap_inode;
         lsm = llu_i2info(inode)->lli_smd;
 
@@ -394,13 +342,9 @@ static void llu_ap_completion(void *data, int cmd, struct obdo *oa, int rc)
 {
         struct ll_async_page *llap;
         struct page *page;
+        ENTRY;
 
-        llap = llap_from_cookie(data);
-        if (IS_ERR(llap)) {
-                EXIT;
-                return;
-        }
-
+        llap = LLAP_FROM_COOKIE(data);
         llap->llap_queued = 0;
         page = llap->llap_page;
 
@@ -412,164 +356,43 @@ static void llu_ap_completion(void *data, int cmd, struct obdo *oa, int rc)
         EXIT;
 }
 
+static void llu_ap_get_ucred(void *data, struct lvfs_ucred *luc)
+{
+        struct ll_async_page *llap;
+        struct ll_uctxt ctxt;
+        ENTRY; 
+        
+        llap = LLAP_FROM_COOKIE(data);
+        
+        luc->luc_fsuid = current->fsuid;
+        luc->luc_fsgid = current->fsgid;
+        luc->luc_cap = current->cap_effective;
+        ll_i2uctxt(&ctxt, llap->llap_inode, NULL);
+        luc->luc_suppgid1 = ctxt.gid1;
+        
+        EXIT;
+}
+
 static struct obd_async_page_ops llu_async_page_ops = {
         .ap_make_ready =        NULL,
         .ap_refresh_count =     NULL,
         .ap_fill_obdo =         llu_ap_fill_obdo,
         .ap_completion =        llu_ap_completion,
+        .ap_get_ucred =         llu_ap_get_ucred,
 };
 
-static
-struct llu_sysio_cookie* get_sysio_cookie(struct inode *inode,
-                                          struct obd_export *exp, int maxpages)
-{
-        struct llu_sysio_cookie *cookie;
-        int rc;
-
-        if (!llap_cookie_size)
-                llap_cookie_size = obd_prep_async_page(llu_i2obdexp(inode),
-                                                       NULL, NULL, NULL, 0,
-                                                       NULL, NULL, NULL);
-        OBD_ALLOC(cookie, LLU_SYSIO_COOKIE_SIZE(exp, maxpages));
-        if (cookie == NULL)
-                goto out;
-
-        I_REF(inode);
-        cookie->lsc_inode = inode;
-        cookie->lsc_maxpages = maxpages;
-        cookie->lsc_llap = (struct ll_async_page *)(cookie + 1);
-        cookie->lsc_pages = (struct page *) (cookie->lsc_llap + maxpages);
-        cookie->lsc_llap_cookie = (void *)(cookie->lsc_pages + maxpages);
-
-        rc = oig_init(&cookie->lsc_oig);
-        if (rc) {
-                OBD_FREE(cookie, LLU_SYSIO_COOKIE_SIZE(exp, maxpages));
-                cookie = NULL;
-        }
-
-out:
-        return cookie;
-}
-
-static
-void put_sysio_cookie(struct llu_sysio_cookie *cookie)
-{
-        struct lov_stripe_md *lsm = llu_i2info(cookie->lsc_inode)->lli_smd;
-        struct obd_export *exp = llu_i2obdexp(cookie->lsc_inode);
-        struct ll_async_page *llap = cookie->lsc_llap;
-#ifdef LIBLUSTRE_HANDLE_UNALIGNED_PAGE
-        struct page *pages = cookie->lsc_pages;
-#endif
-        int i;
-
-        for (i = 0; i< cookie->lsc_maxpages; i++) {
-                if (llap[i].llap_cookie)
-                        obd_teardown_async_page(exp, lsm, NULL,
-                                                llap[i].llap_cookie);
-#ifdef LIBLUSTRE_HANDLE_UNALIGNED_PAGE
-                if (pages[i]._managed) {
-                        free(pages[i].addr);
-                        pages[i]._managed = 0;
-                }
-#endif
-        }
-
-        I_RELE(cookie->lsc_inode);
-
-        oig_release(cookie->lsc_oig);
-        OBD_FREE(cookie, LLU_SYSIO_COOKIE_SIZE(exp, cookie->lsc_maxpages));
-}
-
-#ifdef LIBLUSTRE_HANDLE_UNALIGNED_PAGE
-/* Note: these code should be removed finally, don't need
- * more cleanup
- */
-static
-int prepare_unaligned_write(struct llu_sysio_cookie *cookie)
-{
-        struct inode *inode = cookie->lsc_inode;
-        struct llu_inode_info *lli = llu_i2info(inode);
-        struct lov_stripe_md *lsm = lli->lli_smd;
-        struct obdo oa;
-        struct page *pages = cookie->lsc_pages;
-        int i, pgidx[2] = {0, cookie->lsc_npages-1};
-        int rc;
-        ENTRY;
-
-        for (i = 0; i < 2; i++) {
-                struct page *oldpage = &pages[pgidx[i]];
-                struct page newpage;
-                struct brw_page pg;
-                char *newbuf;
-
-                if (i == 0 && pgidx[0] == pgidx[1])
-                        continue;
-
-                LASSERT(oldpage->_offset + oldpage->_count <= PAGE_CACHE_SIZE);
-
-                if (oldpage->_count == PAGE_CACHE_SIZE)
-                        continue;
-
-                if (oldpage->index << PAGE_CACHE_SHIFT >=
-                    lli->lli_st_size)
-                        continue;
-
-                newbuf = malloc(PAGE_CACHE_SIZE);
-                if (!newbuf)
-                        return -ENOMEM;
-
-                newpage.index = oldpage->index;
-                newpage.addr = newbuf;
-
-                pg.pg = &newpage;
-                pg.off = ((obd_off)newpage.index << PAGE_CACHE_SHIFT);
-                if (pg.off + PAGE_CACHE_SIZE > lli->lli_st_size)
-                        pg.count = lli->lli_st_size % PAGE_CACHE_SIZE;
-                else
-                        pg.count = PAGE_CACHE_SIZE;
-                pg.flag = 0;
-
-                oa.o_id = lsm->lsm_object_id;
-                oa.o_mode = lli->lli_st_mode;
-                oa.o_valid = OBD_MD_FLID | OBD_MD_FLMODE | OBD_MD_FLTYPE;
-
-                /* issue read */
-                rc = obd_brw(OBD_BRW_READ, llu_i2obdexp(inode), &oa, lsm, 1, &pg, NULL);
-                if (rc) {
-                        free(newbuf);
-                        RETURN(rc);
-                }
-
-                /* copy page content, and reset page params */
-                memcpy(newbuf + oldpage->_offset,
-                       (char*)oldpage->addr + oldpage->_offset,
-                       oldpage->_count);
-
-                oldpage->addr = newbuf;
-                if ((((obd_off)oldpage->index << PAGE_CACHE_SHIFT) +
-                    oldpage->_offset + oldpage->_count) > lli->lli_st_size)
-                        oldpage->_count += oldpage->_offset;
-                else
-                        oldpage->_count = PAGE_CACHE_SIZE;
-                oldpage->_offset = 0;
-                oldpage->_managed = 1;
-        }
-
-        RETURN(0);
-}
-#endif
-
-static
-int llu_prep_async_io(struct llu_sysio_cookie *cookie, int cmd,
-                      char *buf, loff_t pos, size_t count)
+static int llu_queue_pio(int cmd, struct llu_io_group *group,
+                         char *buf, size_t count, loff_t pos)
 {
-        struct llu_inode_info *lli = llu_i2info(cookie->lsc_inode);
+        struct llu_inode_info *lli = llu_i2info(group->lig_inode);
+        struct intnl_stat *st = llu_i2stat(group->lig_inode);
         struct lov_stripe_md *lsm = lli->lli_smd;
-        struct obd_export *exp = llu_i2obdexp(cookie->lsc_inode);
-        struct page *pages = cookie->lsc_pages;
-        struct ll_async_page *llap = cookie->lsc_llap;
-        void *llap_cookie = cookie->lsc_llap_cookie;
-        int i, rc, npages = 0;
+        struct obd_export *exp = llu_i2obdexp(group->lig_inode);
+        struct page *pages = &group->lig_pages[group->lig_npages],*page = pages;
+        struct ll_async_page *llap = &group->lig_llaps[group->lig_npages];
+        void *llap_cookie = group->lig_llap_cookies +
+                llap_cookie_size * group->lig_npages;
+        int i, rc, npages = 0, ret_bytes = 0;
         ENTRY;
 
         if (!exp)
@@ -587,350 +410,393 @@ int llu_prep_async_io(struct llu_sysio_cookie *cookie, int cmd,
 
                 /* prevent read beyond file range */
                 if ((cmd == OBD_BRW_READ) &&
-                    (pos + bytes) >= lli->lli_st_size) {
-                        if (pos >= lli->lli_st_size)
+                    (pos + bytes) >= st->st_size) {
+                        if (pos >= st->st_size)
                                 break;
-                        bytes = lli->lli_st_size - pos;
+                        bytes = st->st_size - pos;
                 }
 
                 /* prepare page for this index */
-                pages[npages].index = index;
-                pages[npages].addr = buf - offset;
+                page->index = index;
+                page->addr = buf - offset;
 
-                pages[npages]._offset = offset;
-                pages[npages]._count = bytes;
+                page->_offset = offset;
+                page->_count = bytes;
 
+                page++;
                 npages++;
                 count -= bytes;
                 pos += bytes;
                 buf += bytes;
 
-                cookie->lsc_rwcount += bytes;
+                group->lig_rwcount += bytes;
+                ret_bytes += bytes;
         } while (count);
 
-        cookie->lsc_npages = npages;
+        group->lig_npages += npages;
 
-#ifdef LIBLUSTRE_HANDLE_UNALIGNED_PAGE
-        if (cmd == OBD_BRW_WRITE) {
-                rc = prepare_unaligned_write(cookie);
-                if (rc)
-                        RETURN(rc);
-        }
-#endif
-
-        for (i = 0; i < npages; i++) {
-                llap[i].llap_magic = LLAP_MAGIC;
-                llap[i].llap_cookie = llap_cookie + i * llap_cookie_size;
-                rc = obd_prep_async_page(exp, lsm, NULL, &pages[i],
-                                         (obd_off)pages[i].index << PAGE_SHIFT,
+        for (i = 0, page = pages; i < npages;
+             i++, page++, llap++, llap_cookie += llap_cookie_size){
+                llap->llap_magic = LLAP_MAGIC;
+                llap->llap_cookie = llap_cookie;
+                rc = obd_prep_async_page(exp, lsm, NULL, page,
+                                         (obd_off)page->index << PAGE_SHIFT,
                                          &llu_async_page_ops,
-                                         &llap[i], &llap[i].llap_cookie);
+                                         llap, &llap->llap_cookie);
                 if (rc) {
-                        llap[i].llap_cookie = NULL;
+                        LASSERT(rc < 0);
+                        llap->llap_cookie = NULL;
                         RETURN(rc);
                 }
-                CDEBUG(D_CACHE, "llap %p page %p cookie %p obj off "LPU64"\n",
-                       &llap[i], &pages[i], llap[i].llap_cookie,
-                       (obd_off)pages[i].index << PAGE_SHIFT);
-                pages[i].private = (unsigned long)&llap[i];
-                llap[i].llap_page = &pages[i];
-                llap[i].llap_inode = cookie->lsc_inode;
-
-                rc = obd_queue_group_io(exp, lsm, NULL, cookie->lsc_oig,
-                                        llap[i].llap_cookie, cmd,
-                                        pages[i]._offset, pages[i]._count, 0,
+                CDEBUG(D_CACHE, "llap %p page %p group %p obj off "LPU64"\n",
+                       llap, page, llap->llap_cookie,
+                       (obd_off)pages->index << PAGE_SHIFT);
+                page->private = (unsigned long)llap;
+                llap->llap_page = page;
+                llap->llap_inode = group->lig_inode;
+
+                rc = obd_queue_group_io(exp, lsm, NULL, group->lig_oig,
+                                        llap->llap_cookie, cmd,
+                                        page->_offset, page->_count, 0,
                                         ASYNC_READY | ASYNC_URGENT |
                                         ASYNC_COUNT_STABLE | ASYNC_GROUP_SYNC);
-                if (rc)
+                if (rc) {
+                        LASSERT(rc < 0);
                         RETURN(rc);
+                }
 
-                llap[i].llap_queued = 1;
+                llap->llap_queued = 1;
         }
 
-        RETURN(0);
+        RETURN(ret_bytes);
 }
 
 static
-int llu_start_async_io(struct llu_sysio_cookie *cookie)
+struct llu_io_group * get_io_group(struct inode *inode, int maxpages)
 {
-        struct lov_stripe_md *lsm = llu_i2info(cookie->lsc_inode)->lli_smd;
-        struct obd_export *exp = llu_i2obdexp(cookie->lsc_inode);
+        struct llu_io_group *group;
+        int rc;
 
-        return obd_trigger_group_io(exp, lsm, NULL, cookie->lsc_oig);
-}
+        if (!llap_cookie_size)
+                llap_cookie_size = obd_prep_async_page(llu_i2obdexp(inode),
+                                                       NULL, NULL, NULL, 0,
+                                                       NULL, NULL, NULL);
 
-/*
- * read/write a continuous buffer for an inode (zero-copy)
- */
-struct llu_sysio_cookie*
-llu_rw(int cmd, struct inode *inode, char *buf, size_t count, loff_t pos)
-{
-        struct llu_sysio_cookie *cookie;
-        int max_pages, rc;
-        ENTRY;
+        OBD_ALLOC(group, LLU_IO_GROUP_SIZE(maxpages));
+        if (!group)
+                return ERR_PTR(-ENOMEM);
 
-        max_pages = (count >> PAGE_SHIFT) + 2;
+        I_REF(inode);
+        group->lig_inode = inode;
+        group->lig_maxpages = maxpages;
+        group->lig_llaps = (struct ll_async_page *)(group + 1);
+        group->lig_pages = (struct page *)(&group->lig_llaps[maxpages]);
+        group->lig_llap_cookies = (void *)(&group->lig_pages[maxpages]);
 
-        cookie = get_sysio_cookie(inode, llu_i2obdexp(inode), max_pages);
-        if (!cookie)
-                RETURN(ERR_PTR(-ENOMEM));
+        rc = oig_init(&group->lig_oig);
+        if (rc) {
+                OBD_FREE(group, LLU_IO_GROUP_SIZE(maxpages));
+                return ERR_PTR(rc);
+        }
 
-        rc = llu_prep_async_io(cookie, cmd, buf, pos, count);
-        if (rc)
-                GOTO(out_cleanup, rc);
+        return group;
+}
 
-        rc = llu_start_async_io(cookie);
-        if (rc)
-                GOTO(out_cleanup, rc);
+static int max_io_pages(ssize_t len, int iovlen)
+{
+        return (((len + PAGE_SIZE -1) / PAGE_SIZE) + 2 + iovlen - 1);
+}
 
-/*
-        rc = oig_wait(&oig);
-        if (rc) {
-                CERROR("file i/o error!\n");
-                rw_count = rc;
+static
+void put_io_group(struct llu_io_group *group)
+{
+        struct lov_stripe_md *lsm = llu_i2info(group->lig_inode)->lli_smd;
+        struct obd_export *exp = llu_i2obdexp(group->lig_inode);
+        struct ll_async_page *llap = group->lig_llaps;
+        int i;
+
+        for (i = 0; i < group->lig_npages; i++, llap++) {
+                if (llap->llap_cookie)
+                        obd_teardown_async_page(exp, lsm, NULL,
+                                                llap->llap_cookie);
         }
-*/
-        RETURN(cookie);
 
-out_cleanup:
-        put_sysio_cookie(cookie);
-        RETURN(ERR_PTR(rc));
+        I_RELE(group->lig_inode);
+
+        oig_release(group->lig_oig);
+        OBD_FREE(group, LLU_IO_GROUP_SIZE(group->lig_maxpages));
 }
 
-struct llu_sysio_callback_args*
-llu_file_write(struct inode *inode, const struct iovec *iovec,
-               size_t iovlen, loff_t pos)
+static
+ssize_t llu_file_prwv(const struct iovec *iovec, int iovlen,
+                        _SYSIO_OFF_T pos, ssize_t len,
+                        void *private)
 {
+        struct llu_io_session *session = (struct llu_io_session *) private;
+        struct inode *inode = session->lis_inode;
         struct llu_inode_info *lli = llu_i2info(inode);
+        struct intnl_stat *st = llu_i2stat(inode);
         struct ll_file_data *fd = lli->lli_file_data;
         struct lustre_handle lockh = {0};
         struct lov_stripe_md *lsm = lli->lli_smd;
         struct obd_export *exp = NULL;
         ldlm_policy_data_t policy;
-        struct llu_sysio_callback_args *lsca;
-        struct llu_sysio_cookie *cookie;
-        ldlm_error_t err;
-        int iovidx;
+        struct llu_io_group *iogroup;
+        int astflag = (lli->lli_open_flags & O_NONBLOCK) ?
+                       LDLM_FL_BLOCK_NOWAIT : 0;
+        __u64 kms;
+        int err, is_read, lock_mode, iovidx, ret;
         ENTRY;
 
-        /* XXX consider other types later */
-        if (!S_ISREG(lli->lli_st_mode))
-                LBUG();
-
-        LASSERT(iovlen <= MAX_IOVEC);
+        /* in a large iov read/write we'll be repeatedly called.
+         * so give a chance to answer cancel ast here
+         */
+        liblustre_wait_event(0);
 
         exp = llu_i2obdexp(inode);
         if (exp == NULL)
-                RETURN(ERR_PTR(-EINVAL));
+                RETURN(-EINVAL);
+
+        if (len == 0 || iovlen == 0)
+                RETURN(0);
+
+        if (pos + len > lli->lli_maxbytes)
+                RETURN(-ERANGE);
 
-        OBD_ALLOC(lsca, sizeof(*lsca));
-        if (!lsca)
-                RETURN(ERR_PTR(-ENOMEM));
+        iogroup = get_io_group(inode, max_io_pages(len, iovlen));
+        if (IS_ERR(iogroup))
+                RETURN(PTR_ERR(iogroup));
+
+        is_read = session->lis_cmd == OBD_BRW_READ;
+        lock_mode = is_read ? LCK_PR : LCK_PW;
+
+        if (!is_read && (lli->lli_open_flags & O_APPEND)) {
+                policy.l_extent.start = 0;
+                policy.l_extent.end = OBD_OBJECT_EOF;
+        } else {
+                policy.l_extent.start = pos;
+                policy.l_extent.end = pos + len - 1;
+        }
+
+        err = llu_extent_lock(fd, inode, lsm, lock_mode, &policy,
+                              &lockh, astflag);
+        if (err != ELDLM_OK)
+                GOTO(err_put, err);
+
+        if (is_read) {
+                kms = lov_merge_size(lsm, 1);
+                if (policy.l_extent.end > kms) {
+                        /* A glimpse is necessary to determine whether we
+                         * return a short read or some zeroes at the end of
+                         * the buffer */
+                        if ((err = llu_glimpse_size(inode))) {
+                                llu_extent_unlock(fd, inode, lsm,
+                                                  lock_mode, &lockh);
+                                GOTO(err_put, err);
+                        }
+                } else {
+                        st->st_size = kms;
+                }
+        } else {
+                if (lli->lli_open_flags & O_APPEND)
+                        pos = st->st_size;
+        }
 
-        /* FIXME optimize the following extent locking */
         for (iovidx = 0; iovidx < iovlen; iovidx++) {
-                char *buf = (char*)iovec[iovidx].iov_base;
+                char *buf = (char *) iovec[iovidx].iov_base;
                 size_t count = iovec[iovidx].iov_len;
 
-                if (count == 0)
+                if (!count)
                         continue;
+                if (len < count)
+                        count = len;
+                if (IS_BAD_PTR(buf) || IS_BAD_PTR(buf + count)) {
+                        llu_extent_unlock(fd, inode, lsm, lock_mode, &lockh);
+                        GOTO(err_put, err = -EFAULT);
+                }
 
-                if (pos + count > lli->lli_maxbytes)
-                        GOTO(err_out, err = -ERANGE);
-
-                /* FIXME libsysio haven't handle O_APPEND?? */
-                policy.l_extent.start = pos;
-                policy.l_extent.end = pos + count - 1;
-
-                err = llu_extent_lock(fd, inode, lsm, LCK_PW, &policy,
-                                      &lockh, 0);
-                if (err != ELDLM_OK)
-                        GOTO(err_out, err = -ENOLCK);
-
-                CDEBUG(D_INFO, "Writing inode %lu, "LPSZ" bytes, offset %Lu\n",
-                       lli->lli_st_ino, count, pos);
-
-                cookie = llu_rw(OBD_BRW_WRITE, inode, buf, count, pos);
-                if (!IS_ERR(cookie)) {
-                        /* save cookie */
-                        lsca->cookies[lsca->ncookies++] = cookie;
-                        pos += count;
-                        obd_adjust_kms(exp, lsm, pos, 0);
-                        /* file size grow */
-                        if (pos > lli->lli_st_size)
-                                lli->lli_st_size = pos;
+                if (is_read) {
+                        if (pos >= st->st_size)
+                                break;
                 } else {
-                        llu_extent_unlock(fd, inode, lsm, LCK_PW, &lockh);
-                        GOTO(err_out, err = PTR_ERR(cookie));
+                        if (pos >= lli->lli_maxbytes) {
+                                llu_extent_unlock(fd, inode, lsm, lock_mode,
+                                                  &lockh);
+                                GOTO(err_put, err = -EFBIG);
+                        }
+                        if (pos + count >= lli->lli_maxbytes)
+                                count = lli->lli_maxbytes - pos;
                 }
 
-                /* XXX errors? */
-                err = llu_extent_unlock(fd, inode, lsm, LCK_PW, &lockh);
-                if (err)
-                        CERROR("extent unlock error %d\n", err);
+                ret = llu_queue_pio(session->lis_cmd, iogroup, buf, count, pos);
+                if (ret < 0) {
+                        llu_extent_unlock(fd, inode, lsm, lock_mode, &lockh);
+                        GOTO(err_put, err = ret);
+                } else {
+                        pos += ret;
+                        if (!is_read) {
+                                LASSERT(ret == count);
+                                obd_adjust_kms(exp, lsm, pos, 0);
+                                /* file size grow immediately */
+                                if (pos > st->st_size)
+                                        st->st_size = pos;
+                        }
+                        len -= ret;
+                        if (!len)
+                                break;
+                }
         }
+        LASSERT(len == 0 || is_read); /* libsysio should guarantee this */
 
-        RETURN(lsca);
+        err = llu_extent_unlock(fd, inode, lsm, lock_mode, &lockh);
+        if (err)
+                CERROR("extent unlock error %d\n", err);
 
-err_out:
-        /* teardown all async stuff */
-        while (lsca->ncookies--) {
-                put_sysio_cookie(lsca->cookies[lsca->ncookies]);
-        }
-        OBD_FREE(lsca, sizeof(*lsca));
+        err = obd_trigger_group_io(exp, lsm, NULL, iogroup->lig_oig);
+        if (err)
+                GOTO(err_put, err);
 
-        RETURN(ERR_PTR(err));
+        session->lis_groups[session->lis_ngroups++] = iogroup;
+        RETURN(0);
+err_put:
+        put_io_group(iogroup);
+        RETURN((ssize_t)err);
 }
 
-#if 0
-static void llu_update_atime(struct inode *inode)
+static
+struct llu_io_session *get_io_session(struct inode *ino, int ngroups, int cmd)
 {
-        struct llu_inode_info *lli = llu_i2info(inode);
-
-#ifdef USE_ATIME
-        struct iattr attr;
-
-        attr.ia_atime = LTIME_S(CURRENT_TIME);
-        attr.ia_valid = ATTR_ATIME;
+        struct llu_io_session *session;
 
-        if (lli->lli_st_atime == attr.ia_atime) return;
-        if (IS_RDONLY(inode)) return;
-        if (IS_NOATIME(inode)) return;
+        OBD_ALLOC(session, LLU_IO_SESSION_SIZE(ngroups));
+        if (!session)
+                return NULL;
 
-        /* ll_inode_setattr() sets inode->i_atime from attr.ia_atime */
-        llu_inode_setattr(inode, &attr, 0);
-#else
-        /* update atime, but don't explicitly write it out just this change */
-        inode->i_atime = CURRENT_TIME;
-#endif
+        I_REF(ino);
+        session->lis_inode = ino;
+        session->lis_max_groups = ngroups;
+        session->lis_cmd = cmd;
+        return session;
 }
-#endif
 
-struct llu_sysio_callback_args*
-llu_file_read(struct inode *inode, const struct iovec *iovec,
-              size_t iovlen, loff_t pos)
+static void put_io_session(struct llu_io_session *session)
 {
-        struct llu_inode_info *lli = llu_i2info(inode);
-        struct ll_file_data *fd = lli->lli_file_data;
-        struct lov_stripe_md *lsm = lli->lli_smd;
-        struct lustre_handle lockh = { 0 };
-        ldlm_policy_data_t policy;
-        struct llu_sysio_callback_args *lsca;
-        struct llu_sysio_cookie *cookie;
-        __u64 kms;
-        int iovidx;
-
-        ldlm_error_t err;
-        ENTRY;
+        int i;
 
-        OBD_ALLOC(lsca, sizeof(*lsca));
-        if (!lsca)
-                RETURN(ERR_PTR(-ENOMEM));
+        for (i = 0; i < session->lis_ngroups; i++) {
+                if (session->lis_groups[i]) {
+                        put_io_group(session->lis_groups[i]);
+                        session->lis_groups[i] = NULL;
+                }
+        }
 
-        for (iovidx = 0; iovidx < iovlen; iovidx++) {
-                char *buf = iovec[iovidx].iov_base;
-                size_t count = iovec[iovidx].iov_len;
+        I_RELE(session->lis_inode);
+        OBD_FREE(session, LLU_IO_SESSION_SIZE(session->lis_max_groups));
+}
 
-                /* "If nbyte is 0, read() will return 0 and have no other results."
-                 *                      -- Single Unix Spec */
-                if (count == 0)
-                        continue;
+static int llu_file_rwx(struct inode *ino,
+                        struct ioctx *ioctx,
+                        int read)
+{
+        struct llu_io_session *session;
+        ssize_t cc;
+        int cmd = read ? OBD_BRW_READ : OBD_BRW_WRITE;
+        ENTRY;
 
-                policy.l_extent.start = pos;
-                policy.l_extent.end = pos + count - 1;
+        LASSERT(ioctx->ioctx_xtvlen >= 0);
+        LASSERT(ioctx->ioctx_iovlen >= 0);
 
-                err = llu_extent_lock(fd, inode, lsm, LCK_PR, &policy, &lockh, 0);
-                if (err != ELDLM_OK)
-                        GOTO(err_out, err = -ENOLCK);
+        liblustre_wait_event(0);
 
-                kms = lov_merge_size(lsm, 1);
-                if (policy.l_extent.end > kms) {
-                        /* A glimpse is necessary to determine whether we
-                         * return a short read or some zeroes at the end of
-                         * the buffer */
-                        if (llu_glimpse_size(inode)) {
-                                llu_extent_unlock(fd, inode, lsm,LCK_PR,&lockh);
-                                GOTO(err_out, err = -ENOLCK);
-                        }
-                } else {
-                        lli->lli_st_size = kms;
-                }
+        if (!ioctx->ioctx_xtvlen)
+                RETURN(0);
 
-                CDEBUG(D_INFO, "Reading inode %lu, "LPSZ" bytes, offset %Ld, "
-                       "i_size "LPU64"\n", lli->lli_st_ino, count, pos,
-                       lli->lli_st_size);
+        /* XXX consider other types later */
+        if (S_ISDIR(llu_i2stat(ino)->st_mode))
+                RETURN(-EISDIR);
+        if (!S_ISREG(llu_i2stat(ino)->st_mode))
+                RETURN(-EOPNOTSUPP);
+
+        session = get_io_session(ino, ioctx->ioctx_xtvlen * 2, cmd);
+        if (!session)
+                RETURN(-ENOMEM);
+
+        cc = _sysio_enumerate_extents(ioctx->ioctx_xtv, ioctx->ioctx_xtvlen,
+                                      ioctx->ioctx_iov, ioctx->ioctx_iovlen,
+                                      llu_file_prwv, session);
+
+        if (cc >= 0) {
+                LASSERT(!ioctx->ioctx_cc);
+                ioctx->ioctx_private = session;
+                RETURN(0);
+        } else {
+                put_io_session(session);
+                RETURN(cc);
+        }
+}
 
-                if (pos >= lli->lli_st_size) {
-                        llu_extent_unlock(fd, inode, lsm, LCK_PR, &lockh);
-                        break;
-                }
+int llu_iop_read(struct inode *ino,
+                 struct ioctx *ioctx)
+{
+        return llu_file_rwx(ino, ioctx, 1);
+}
 
-                cookie = llu_rw(OBD_BRW_READ, inode, buf, count, pos);
-                if (!IS_ERR(cookie)) {
-                        /* save cookie */
-                        lsca->cookies[lsca->ncookies++] = cookie;
-                        pos += count;
-                } else {
-                        llu_extent_unlock(fd, inode, lsm, LCK_PR, &lockh);
-                        GOTO(err_out, err = PTR_ERR(cookie));
-                }
+int llu_iop_write(struct inode *ino,
+                  struct ioctx *ioctx)
+{
+        struct iattr iattr;
+        int rc;
 
-                /* XXX errors? */
-                err = llu_extent_unlock(fd, inode, lsm, LCK_PR, &lockh);
-                if (err)
-                        CERROR("extent_unlock fail: %d\n", err);
-        }
-#if 0
-        if (readed > 0)
-                llu_update_atime(inode);
-#endif
-        RETURN(lsca);
+        memset(&iattr, 0, sizeof(iattr));
+        iattr.ia_mtime = iattr.ia_atime = CURRENT_TIME;
+        iattr.ia_valid = ATTR_MTIME | ATTR_ATIME | ATTR_RAW;
 
-err_out:
-        /* teardown all async stuff */
-        while (lsca->ncookies--) {
-                put_sysio_cookie(lsca->cookies[lsca->ncookies]);
+        liblustre_wait_event(0);
+        rc = llu_setattr_raw(ino, &iattr);
+        if (rc) {
+                CERROR("failed to set mtime/atime during write: %d", rc);
+                /* XXX should continue or return error? */
         }
-        OBD_FREE(lsca, sizeof(*lsca));
 
-        RETURN(ERR_PTR(err));
+        return llu_file_rwx(ino, ioctx, 0);
 }
 
-int llu_iop_iodone(struct ioctx *ioctxp)
+int llu_iop_iodone(struct ioctx *ioctx)
 {
-        struct llu_sysio_callback_args *lsca = ioctxp->ioctx_private;
-        struct llu_sysio_cookie *cookie;
+        struct llu_io_session *session;
+        struct llu_io_group *group;
         int i, err = 0, rc = 0;
         ENTRY;
 
-        /* write/read(fd, buf, 0) */
-        if (!lsca) {
-                ioctxp->ioctx_cc = 0;
-                RETURN(1);
-        }
+        liblustre_wait_event(0);
 
-        LASSERT(!IS_ERR(lsca));
+        session = (struct llu_io_session *) ioctx->ioctx_private;
+        LASSERT(session);
+        LASSERT(!IS_ERR(session));
 
-        for (i = 0; i < lsca->ncookies; i++) {
-                cookie = lsca->cookies[i];
-                if (cookie) {
-                        err = oig_wait(cookie->lsc_oig);
-                        if (err && !rc)
-                                rc = err;
+        for (i = 0; i < session->lis_ngroups; i++) {
+                group = session->lis_groups[i];
+                if (group) {
+                        if (!rc) {
+                                err = oig_wait(group->lig_oig);
+                                if (err)
+                                        rc = err;
+                        }
                         if (!rc)
-                                ioctxp->ioctx_cc += cookie->lsc_rwcount;
-                        put_sysio_cookie(cookie);
+                                ioctx->ioctx_cc += group->lig_rwcount;
+                        put_io_group(group);
+                        session->lis_groups[i] = NULL;
                 }
         }
 
         if (rc) {
                 LASSERT(rc < 0);
-                ioctxp->ioctx_cc = -1;
-                ioctxp->ioctx_errno = -rc;
+                ioctx->ioctx_cc = -1;
+                ioctx->ioctx_errno = -rc;
         }
 
-        OBD_FREE(lsca, sizeof(*lsca));
-        ioctxp->ioctx_private = NULL;
+        put_io_session(session);
+        ioctx->ioctx_private = NULL;
 
         RETURN(1);
 }
index 9972f1a..5dcd3b8 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Lustre Light Super operations
  *
- *  Copyright (c) 2002, 2003 Cluster File Systems, Inc.
+ *  Copyright (c) 2002-2004 Cluster File Systems, Inc.
  *
  *   This file is part of Lustre, http://www.lustre.org.
  *
 # include <sys/statfs.h>
 #endif
 
+#ifdef HAVE_XTIO_H
+#include <xtio.h>
+#endif
 #include <sysio.h>
 #include <fs.h>
 #include <mount.h>
 #include <inode.h>
+#ifdef HAVE_FILE_H
 #include <file.h>
+#endif
 
 #undef LIST_HEAD
 
 #include "llite_lib.h"
 
+#ifndef MAY_EXEC
+#define MAY_EXEC        1
+#define MAY_WRITE       2
+#define MAY_READ        4
+#endif
+
+#define S_IXUGO (S_IXUSR|S_IXGRP|S_IXOTH)
+
+static int ll_permission(struct inode *inode, int mask)
+{
+        struct intnl_stat *st = llu_i2stat(inode);
+        mode_t mode = st->st_mode;
+
+        if (current->fsuid == st->st_uid)
+                mode >>= 6;
+        else if (in_group_p(st->st_gid))
+                mode >>= 3;
+
+        if ((mode & mask & (MAY_READ|MAY_WRITE|MAY_EXEC)) == mask)
+                return 0;
+
+        if ((mask & (MAY_READ|MAY_WRITE)) ||
+            (st->st_mode & S_IXUGO))
+                if (capable(CAP_DAC_OVERRIDE))
+                        return 0;
+
+        if (mask == MAY_READ ||
+            (S_ISDIR(st->st_mode) && !(mask & MAY_WRITE))) {
+                if (capable(CAP_DAC_READ_SEARCH))
+                        return 0;
+        }
+
+        return -EACCES;
+}
+
 static void llu_fsop_gone(struct filesys *fs)
 {
         struct llu_sb_info *sbi = (struct llu_sb_info *) fs->fs_private;
         struct obd_device *obd = class_exp2obd(sbi->ll_mdc_exp);
-        struct ll_fid rootfid;
+        int next = 0;
         ENTRY;
 
         list_del(&sbi->ll_conn_chain);
         obd_disconnect(sbi->ll_osc_exp);
-
-        /* NULL request to force sync on the MDS, and get the last_committed
-         * value to flush remaining RPCs from the sending queue on client.
-         *
-         * XXX This should be an mdc_sync() call to sync the whole MDS fs,
-         *     which we can call for other reasons as well.
-         */
-        if (!obd->obd_no_recov)
-                mdc_getstatus(sbi->ll_mdc_exp, &rootfid);
-
         obd_disconnect(sbi->ll_mdc_exp);
 
+        while ((obd = class_devices_in_group(&sbi->ll_sb_uuid, &next)) != NULL){
+                struct lustre_cfg_bufs bufs;
+                struct lustre_cfg *lcfg;
+                int err;
+
+                lustre_cfg_bufs_reset(&bufs, obd->obd_name);
+                lcfg = lustre_cfg_new(LCFG_CLEANUP, &bufs);
+                err = class_process_config(lcfg);
+                lustre_cfg_free(lcfg);
+                if (err)
+                        CERROR("cleanup failed: %s\n", obd->obd_name);
+
+                lustre_cfg_bufs_reset(&bufs, obd->obd_name);
+                lcfg = lustre_cfg_new(LCFG_DETACH, &bufs);
+                err = class_process_config(lcfg);
+                if (err)
+                        CERROR("detach failed: %s\n", obd->obd_name);
+        }
+
         OBD_FREE(sbi, sizeof(*sbi));
 
         EXIT;
@@ -79,6 +128,7 @@ void llu_update_inode(struct inode *inode, struct mds_body *body,
                       struct lov_stripe_md *lsm)
 {
         struct llu_inode_info *lli = llu_i2info(inode);
+        struct intnl_stat *st = llu_i2stat(inode);
 
         LASSERT ((lsm != NULL) == ((body->valid & OBD_MD_FLEASIZE) != 0));
         if (lsm != NULL) {
@@ -89,41 +139,45 @@ void llu_update_inode(struct inode *inode, struct mds_body *body,
                                 lli->lli_maxbytes = PAGE_CACHE_MAXBYTES;
                 } else {
                         if (memcmp(lli->lli_smd, lsm, sizeof(*lsm))) {
-                                CERROR("lsm mismatch for inode %ld\n",
-                                       lli->lli_st_ino);
+                                CERROR("lsm mismatch for inode %lld\n",
+                                       st->st_ino);
                                 LBUG();
                         }
                 }
         }
 
         if (body->valid & OBD_MD_FLID)
-                lli->lli_st_ino = body->ino;
+                st->st_ino = body->ino;
         if (body->valid & OBD_MD_FLATIME)
-                LTIME_S(lli->lli_st_atime) = body->atime;
+                LTIME_S(st->st_atime) = body->atime;
         if (body->valid & OBD_MD_FLMTIME)
-                LTIME_S(lli->lli_st_mtime) = body->mtime;
+                LTIME_S(st->st_mtime) = body->mtime;
         if (body->valid & OBD_MD_FLCTIME)
-                LTIME_S(lli->lli_st_ctime) = body->ctime;
+                LTIME_S(st->st_ctime) = body->ctime;
         if (body->valid & OBD_MD_FLMODE)
-                lli->lli_st_mode = (lli->lli_st_mode & S_IFMT)|(body->mode & ~S_IFMT);
+                st->st_mode = (st->st_mode & S_IFMT)|(body->mode & ~S_IFMT);
         if (body->valid & OBD_MD_FLTYPE)
-                lli->lli_st_mode = (lli->lli_st_mode & ~S_IFMT)|(body->mode & S_IFMT);
+                st->st_mode = (st->st_mode & ~S_IFMT)|(body->mode & S_IFMT);
+        if (S_ISREG(st->st_mode))
+                st->st_blksize = min(2UL * PTLRPC_MAX_BRW_SIZE, LL_MAX_BLKSIZE);
+        else
+                st->st_blksize = 4096;
         if (body->valid & OBD_MD_FLUID)
-                lli->lli_st_uid = body->uid;
+                st->st_uid = body->uid;
         if (body->valid & OBD_MD_FLGID)
-                lli->lli_st_gid = body->gid;
-        if (body->valid & OBD_MD_FLFLAGS)
-                lli->lli_st_flags = body->flags;
+                st->st_gid = body->gid;
         if (body->valid & OBD_MD_FLNLINK)
-                lli->lli_st_nlink = body->nlink;
-        if (body->valid & OBD_MD_FLGENER)
-                lli->lli_st_generation = body->generation;
+                st->st_nlink = body->nlink;
         if (body->valid & OBD_MD_FLRDEV)
-                lli->lli_st_rdev = body->rdev;
+                st->st_rdev = body->rdev;
         if (body->valid & OBD_MD_FLSIZE)
-                lli->lli_st_size = body->size;
+                st->st_size = body->size;
         if (body->valid & OBD_MD_FLBLOCKS)
-                lli->lli_st_blocks = body->blocks;
+                st->st_blocks = body->blocks;
+        if (body->valid & OBD_MD_FLFLAGS)
+                lli->lli_st_flags = body->flags;
+        if (body->valid & OBD_MD_FLGENER)
+                lli->lli_st_generation = body->generation;
 
         /* fillin fid */
         if (body->valid & OBD_MD_FLID)
@@ -137,35 +191,36 @@ void llu_update_inode(struct inode *inode, struct mds_body *body,
 void obdo_to_inode(struct inode *dst, struct obdo *src, obd_flag valid)
 {
         struct llu_inode_info *lli = llu_i2info(dst);
+        struct intnl_stat *st = llu_i2stat(dst);
 
         valid &= src->o_valid;
 
         if (valid & (OBD_MD_FLCTIME | OBD_MD_FLMTIME))
-                CDEBUG(D_INODE, "valid %x, cur time %lu/%lu, new %lu/%lu\n",
-                       src->o_valid, 
-                       LTIME_S(lli->lli_st_mtime), LTIME_S(lli->lli_st_ctime),
+                CDEBUG(D_INODE,"valid "LPX64", cur time %lu/%lu, new %lu/%lu\n",
+                       src->o_valid,
+                       LTIME_S(st->st_mtime), LTIME_S(st->st_ctime),
                        (long)src->o_mtime, (long)src->o_ctime);
 
         if (valid & OBD_MD_FLATIME)
-                LTIME_S(lli->lli_st_atime) = src->o_atime;
+                LTIME_S(st->st_atime) = src->o_atime;
         if (valid & OBD_MD_FLMTIME)
-                LTIME_S(lli->lli_st_mtime) = src->o_mtime;
-        if (valid & OBD_MD_FLCTIME && src->o_ctime > LTIME_S(lli->lli_st_ctime))
-                LTIME_S(lli->lli_st_ctime) = src->o_ctime;
+                LTIME_S(st->st_mtime) = src->o_mtime;
+        if (valid & OBD_MD_FLCTIME && src->o_ctime > LTIME_S(st->st_ctime))
+                LTIME_S(st->st_ctime) = src->o_ctime;
         if (valid & OBD_MD_FLSIZE)
-                lli->lli_st_size = src->o_size;
+                st->st_size = src->o_size;
         if (valid & OBD_MD_FLBLOCKS) /* allocation of space */
-                lli->lli_st_blocks = src->o_blocks;
+                st->st_blocks = src->o_blocks;
         if (valid & OBD_MD_FLBLKSZ)
-                lli->lli_st_blksize = src->o_blksize;
+                st->st_blksize = src->o_blksize;
         if (valid & OBD_MD_FLTYPE)
-                lli->lli_st_mode = (lli->lli_st_mode & ~S_IFMT) | (src->o_mode & S_IFMT);
+                st->st_mode = (st->st_mode & ~S_IFMT) | (src->o_mode & S_IFMT);
         if (valid & OBD_MD_FLMODE)
-                lli->lli_st_mode = (lli->lli_st_mode & S_IFMT) | (src->o_mode & ~S_IFMT);
+                st->st_mode = (st->st_mode & S_IFMT) | (src->o_mode & ~S_IFMT);
         if (valid & OBD_MD_FLUID)
-                lli->lli_st_uid = src->o_uid;
+                st->st_uid = src->o_uid;
         if (valid & OBD_MD_FLGID)
-                lli->lli_st_gid = src->o_gid;
+                st->st_gid = src->o_gid;
         if (valid & OBD_MD_FLFLAGS)
                 lli->lli_st_flags = src->o_flags;
         if (valid & OBD_MD_FLGENER)
@@ -178,51 +233,52 @@ void obdo_to_inode(struct inode *dst, struct obdo *src, obd_flag valid)
 void obdo_from_inode(struct obdo *dst, struct inode *src, obd_flag valid)
 {
         struct llu_inode_info *lli = llu_i2info(src);
+        struct intnl_stat *st = llu_i2stat(src);
         obd_flag newvalid = 0;
 
         if (valid & (OBD_MD_FLCTIME | OBD_MD_FLMTIME))
                 CDEBUG(D_INODE, "valid %x, new time %lu/%lu\n",
-                       valid, LTIME_S(lli->lli_st_mtime), 
-                       LTIME_S(lli->lli_st_ctime));
+                       valid, LTIME_S(st->st_mtime), 
+                       LTIME_S(st->st_ctime));
 
         if (valid & OBD_MD_FLATIME) {
-                dst->o_atime = LTIME_S(lli->lli_st_atime);
+                dst->o_atime = LTIME_S(st->st_atime);
                 newvalid |= OBD_MD_FLATIME;
         }
         if (valid & OBD_MD_FLMTIME) {
-                dst->o_mtime = LTIME_S(lli->lli_st_mtime);
+                dst->o_mtime = LTIME_S(st->st_mtime);
                 newvalid |= OBD_MD_FLMTIME;
         }
         if (valid & OBD_MD_FLCTIME) {
-                dst->o_ctime = LTIME_S(lli->lli_st_ctime);
+                dst->o_ctime = LTIME_S(st->st_ctime);
                 newvalid |= OBD_MD_FLCTIME;
         }
         if (valid & OBD_MD_FLSIZE) {
-                dst->o_size = lli->lli_st_size;
+                dst->o_size = st->st_size;
                 newvalid |= OBD_MD_FLSIZE;
         }
         if (valid & OBD_MD_FLBLOCKS) {  /* allocation of space (x512 bytes) */
-                dst->o_blocks = lli->lli_st_blocks;
+                dst->o_blocks = st->st_blocks;
                 newvalid |= OBD_MD_FLBLOCKS;
         }
         if (valid & OBD_MD_FLBLKSZ) {   /* optimal block size */
-                dst->o_blksize = lli->lli_st_blksize;
+                dst->o_blksize = st->st_blksize;
                 newvalid |= OBD_MD_FLBLKSZ;
         }
         if (valid & OBD_MD_FLTYPE) {
-                dst->o_mode = (dst->o_mode & S_IALLUGO)|(lli->lli_st_mode & S_IFMT);
+                dst->o_mode = (dst->o_mode & S_IALLUGO)|(st->st_mode & S_IFMT);
                 newvalid |= OBD_MD_FLTYPE;
         }
         if (valid & OBD_MD_FLMODE) {
-                dst->o_mode = (dst->o_mode & S_IFMT)|(lli->lli_st_mode & S_IALLUGO);
+                dst->o_mode = (dst->o_mode & S_IFMT)|(st->st_mode & S_IALLUGO);
                 newvalid |= OBD_MD_FLMODE;
         }
         if (valid & OBD_MD_FLUID) {
-                dst->o_uid = lli->lli_st_uid;
+                dst->o_uid = st->st_uid;
                 newvalid |= OBD_MD_FLUID;
         }
         if (valid & OBD_MD_FLGID) {
-                dst->o_gid = lli->lli_st_gid;
+                dst->o_gid = st->st_gid;
                 newvalid |= OBD_MD_FLGID;
         }
         if (valid & OBD_MD_FLFLAGS) {
@@ -286,6 +342,16 @@ static struct inode* llu_new_inode(struct filesys *fs,
 {
        struct inode *inode;
         struct llu_inode_info *lli;
+        struct intnl_stat st = {
+                st_dev:         0,
+#ifndef AUTOMOUNT_FILE_NAME
+                st_mode:        fid->f_type & S_IFMT,
+#else
+                st_mode:        fid->f_type /* all of the bits! */
+#endif
+                st_uid:         geteuid(),
+                st_gid:         getegid(),
+        };
 
         OBD_ALLOC(lli, sizeof(*lli));
         if (!lli)
@@ -306,13 +372,7 @@ static struct inode* llu_new_inode(struct filesys *fs,
 
         /* file identifier is needed by functions like _sysio_i_find() */
        inode = _sysio_i_new(fs, &lli->lli_sysio_fid,
-#ifndef AUTOMOUNT_FILE_NAME
-                            fid->f_type & S_IFMT,
-#else
-                            fid->f_type, /* all of the bits! */
-#endif
-                             0, 0,
-                            &llu_inode_ops, lli);
+                             &st, 0, &llu_inode_ops, lli);
 
        if (!inode)
                OBD_FREE(lli, sizeof(*lli));
@@ -333,7 +393,7 @@ static int llu_have_md_lock(struct inode *inode)
         LASSERT(inode);
 
         obddev = sbi->ll_mdc_exp->exp_obd;
-        res_id.name[0] = lli->lli_st_ino;
+        res_id.name[0] = llu_i2stat(inode)->st_ino;
         res_id.name[1] = lli->lli_st_generation;
 
         CDEBUG(D_INFO, "trying to match res "LPU64"\n", res_id.name[0]);
@@ -356,7 +416,6 @@ static int llu_have_md_lock(struct inode *inode)
 
 static int llu_inode_revalidate(struct inode *inode)
 {
-        struct llu_inode_info *lli = llu_i2info(inode);
         struct lov_stripe_md *lsm = NULL;
         ENTRY;
 
@@ -370,19 +429,19 @@ static int llu_inode_revalidate(struct inode *inode)
                 struct ptlrpc_request *req = NULL;
                 struct llu_sb_info *sbi = llu_i2sbi(inode);
                 struct ll_fid fid;
-                unsigned long valid = 0;
+                unsigned long valid = OBD_MD_FLGETATTR;
                 int rc, ealen = 0;
 
                 /* Why don't we update all valid MDS fields here, if we're
                  * doing an RPC anyways?  -phil */
-                if (S_ISREG(lli->lli_st_mode)) {
+                if (S_ISREG(llu_i2stat(inode)->st_mode)) {
                         ealen = obd_size_diskmd(sbi->ll_osc_exp, NULL);
                         valid |= OBD_MD_FLEASIZE;
                 }
                 ll_inode2fid(&fid, inode);
                 rc = mdc_getattr(sbi->ll_mdc_exp, &fid, valid, ealen, &req);
                 if (rc) {
-                        CERROR("failure %d inode %lu\n", rc, lli->lli_st_ino);
+                        CERROR("failure %d inode %llu\n", rc, llu_i2stat(inode)->st_ino);
                         RETURN(-abs(rc));
                 }
                 rc = mdc_req2lustre_md(req, 0, sbi->ll_osc_exp, &md);
@@ -423,21 +482,7 @@ static int llu_inode_revalidate(struct inode *inode)
 
 static void copy_stat_buf(struct inode *ino, struct intnl_stat *b)
 {
-        struct llu_inode_info *lli = llu_i2info(ino);
-
-        b->st_dev = lli->lli_st_dev;
-        b->st_ino = lli->lli_st_ino;
-        b->st_mode = lli->lli_st_mode;
-        b->st_nlink = lli->lli_st_nlink;
-        b->st_uid = lli->lli_st_uid;
-        b->st_gid = lli->lli_st_gid;
-        b->st_rdev = lli->lli_st_rdev;
-        b->st_size = lli->lli_st_size;
-        b->st_blksize = lli->lli_st_blksize;
-        b->st_blocks = lli->lli_st_blocks;
-        b->st_atime = lli->lli_st_atime;
-        b->st_mtime = lli->lli_st_mtime;
-        b->st_ctime = lli->lli_st_ctime;
+        *b = *llu_i2stat(ino);
 }
 
 static int llu_iop_getattr(struct pnode *pno,
@@ -447,6 +492,8 @@ static int llu_iop_getattr(struct pnode *pno,
         int rc;
         ENTRY;
 
+        liblustre_wait_event(0);
+
         if (!ino) {
                 LASSERT(pno);
                 LASSERT(pno->p_base->pb_ino);
@@ -461,14 +508,7 @@ static int llu_iop_getattr(struct pnode *pno,
         rc = llu_inode_revalidate(ino);
         if (!rc) {
                 copy_stat_buf(ino, b);
-
-                if (llu_i2info(ino)->lli_it) {
-                        struct lookup_intent *it;
-
-                        LL_GET_INTENT(ino, it);
-                        it->it_op_release(it);
-                        OBD_FREE(it, sizeof(*it));
-                }
+                LASSERT(!llu_i2info(ino)->lli_it);
         }
 
         RETURN(rc);
@@ -480,7 +520,8 @@ static int null_if_equal(struct ldlm_lock *lock, void *data)
                 lock->l_ast_data = NULL;
 
                 if (lock->l_req_mode != lock->l_granted_mode)
-                        LDLM_ERROR(lock,"clearing inode with ungranted lock\n");        }
+                        LDLM_ERROR(lock,"clearing inode with ungranted lock\n");
+        }
 
         return LDLM_ITER_CONTINUE;
 }
@@ -492,8 +533,8 @@ void llu_clear_inode(struct inode *inode)
         struct llu_sb_info *sbi = llu_i2sbi(inode);
         ENTRY;
 
-        CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%lu(%p)\n", lli->lli_st_ino,
-               lli->lli_st_generation, inode);
+        CDEBUG(D_VFSTRACE, "VFS Op:inode=%llu/%lu(%p)\n",
+               llu_i2stat(inode)->st_ino, lli->lli_st_generation, inode);
 
         ll_inode2fid(&fid, inode);
         clear_bit(LLI_F_HAVE_MDS_SIZE_LOCK, &(lli->lli_flags));
@@ -522,6 +563,7 @@ void llu_iop_gone(struct inode *inode)
         struct llu_inode_info *lli = llu_i2info(inode);
         ENTRY;
 
+        liblustre_wait_event(0);
         llu_clear_inode(inode);
 
         OBD_FREE(lli, sizeof(*lli));
@@ -531,7 +573,7 @@ void llu_iop_gone(struct inode *inode)
 static int inode_setattr(struct inode * inode, struct iattr * attr)
 {
         unsigned int ia_valid = attr->ia_valid;
-        struct llu_inode_info *lli = llu_i2info(inode);
+        struct intnl_stat *st = llu_i2stat(inode);
         int error = 0;
 
         if (ia_valid & ATTR_SIZE) {
@@ -541,19 +583,19 @@ static int inode_setattr(struct inode * inode, struct iattr * attr)
         }
 
         if (ia_valid & ATTR_UID)
-                lli->lli_st_uid = attr->ia_uid;
+                st->st_uid = attr->ia_uid;
         if (ia_valid & ATTR_GID)
-                lli->lli_st_gid = attr->ia_gid;
+                st->st_gid = attr->ia_gid;
         if (ia_valid & ATTR_ATIME)
-                lli->lli_st_atime = attr->ia_atime;
+                st->st_atime = attr->ia_atime;
         if (ia_valid & ATTR_MTIME)
-                lli->lli_st_mtime = attr->ia_mtime;
+                st->st_mtime = attr->ia_mtime;
         if (ia_valid & ATTR_CTIME)
-                lli->lli_st_ctime = attr->ia_ctime;
+                st->st_ctime = attr->ia_ctime;
         if (ia_valid & ATTR_MODE) {
-                lli->lli_st_mode = attr->ia_mode;
-                if (!in_group_p(lli->lli_st_gid) && !capable(CAP_FSETID))
-                        lli->lli_st_mode &= ~S_ISGID;
+                st->st_mode = attr->ia_mode;
+                if (!in_group_p(st->st_gid) && !capable(CAP_FSETID))
+                        st->st_mode &= ~S_ISGID;
         }
         /* mark_inode_dirty(inode); */
 out:
@@ -577,14 +619,14 @@ int llu_setattr_raw(struct inode *inode, struct iattr *attr)
 {
         struct lov_stripe_md *lsm = llu_i2info(inode)->lli_smd;
         struct llu_sb_info *sbi = llu_i2sbi(inode);
-        struct llu_inode_info *lli = llu_i2info(inode);
+        struct intnl_stat *st = llu_i2stat(inode);
         struct ptlrpc_request *request = NULL;
         struct mdc_op_data op_data;
         int ia_valid = attr->ia_valid;
         int rc = 0;
         ENTRY;
 
-        CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu\n", lli->lli_st_ino);
+        CDEBUG(D_VFSTRACE, "VFS Op:inode=%llu\n", st->st_ino);
 
         if (ia_valid & ATTR_SIZE) {
                 if (attr->ia_size > ll_file_maxbytes(inode)) {
@@ -639,10 +681,18 @@ int llu_setattr_raw(struct inode *inode, struct iattr *attr)
                         ptlrpc_req_finished(request);
                         RETURN(rc);
                 }
+
+                /* We call inode_setattr to adjust timestamps, but we first
+                 * clear ATTR_SIZE to avoid invoking vmtruncate.
+                 *
+                 * NB: ATTR_SIZE will only be set at this point if the size
+                 * resides on the MDS, ie, this file has no objects. */
+                attr->ia_valid &= ~ATTR_SIZE;
+                inode_setattr(inode, attr);
                 llu_update_inode(inode, md.body, md.lsm);
                 ptlrpc_req_finished(request);
 
-                if (!md.lsm || !S_ISREG(lli->lli_st_mode)) {
+                if (!md.lsm || !S_ISREG(st->st_mode)) {
                         CDEBUG(D_INODE, "no lsm: not setting attrs on OST\n");
                         RETURN(0);
                 }
@@ -654,12 +704,12 @@ int llu_setattr_raw(struct inode *inode, struct iattr *attr)
                 if (ia_valid & (ATTR_MTIME | ATTR_ATIME)) {
                         /* from sys_utime() */
                         if (!(ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET))) {
-                                if (current->fsuid != lli->lli_st_uid &&
-                                    (rc = ll_permission(inode, 0/*MAY_WRITE*/, NULL)) != 0)
+                                if (current->fsuid != st->st_uid &&
+                                    (rc = ll_permission(inode, MAY_WRITE)) != 0)
                                         RETURN(rc);
                         } else {
                                /* from inode_change_ok() */
-                               if (current->fsuid != lli->lli_st_uid &&
+                               if (current->fsuid != st->st_uid &&
                                    !capable(CAP_FOWNER))
                                        RETURN(-EPERM);
                         }
@@ -689,9 +739,6 @@ int llu_setattr_raw(struct inode *inode, struct iattr *attr)
                 }
 
                 rc = llu_vmtruncate(inode, attr->ia_size);
-                if (rc == 0)
-                        set_bit(LLI_F_HAVE_OST_SIZE_LOCK,
-                                &llu_i2info(inode)->lli_flags);
 
                 /* unlock now as we don't mind others file lockers racing with
                  * the mds updates below? */
@@ -704,8 +751,8 @@ int llu_setattr_raw(struct inode *inode, struct iattr *attr)
         } else if (ia_valid & (ATTR_MTIME | ATTR_MTIME_SET)) {
                 struct obdo oa;
 
-                CDEBUG(D_INODE, "set mtime on OST inode %lu to %lu\n",
-                       lli->lli_st_ino, LTIME_S(attr->ia_mtime));
+                CDEBUG(D_INODE, "set mtime on OST inode %llu to %lu\n",
+                       st->st_ino, LTIME_S(attr->ia_mtime));
                 oa.o_id = lsm->lsm_object_id;
                 oa.o_valid = OBD_MD_FLID;
                 obdo_from_inode(&oa, inode, OBD_MD_FLTYPE | OBD_MD_FLATIME |
@@ -728,6 +775,11 @@ static int llu_iop_setattr(struct pnode *pno,
         struct iattr iattr;
         ENTRY;
 
+        liblustre_wait_event(0);
+
+        LASSERT(!(mask & ~(SETATTR_MTIME | SETATTR_ATIME | 
+                           SETATTR_UID | SETATTR_GID |
+                           SETATTR_LEN | SETATTR_MODE)));
         memset(&iattr, 0, sizeof(iattr));
 
         if (mask & SETATTR_MODE) {
@@ -755,7 +807,8 @@ static int llu_iop_setattr(struct pnode *pno,
                 iattr.ia_valid |= ATTR_SIZE;
         }
 
-        iattr.ia_valid |= ATTR_RAW;
+        iattr.ia_valid |= ATTR_RAW | ATTR_CTIME;
+        iattr.ia_ctime = CURRENT_TIME;
 
         RETURN(llu_setattr_raw(ino, &iattr));
 }
@@ -774,7 +827,7 @@ static int llu_iop_symlink_raw(struct pnode *pno, const char *tgt)
         int err = -EMLINK;
         ENTRY;
 
-        if (llu_i2info(dir)->lli_st_nlink >= EXT2_LINK_MAX)
+        if (llu_i2stat(dir)->st_nlink >= EXT2_LINK_MAX)
                 RETURN(err);
 
         llu_prepare_mdc_op_data(&op_data, dir, NULL, name, len, 0);
@@ -793,7 +846,8 @@ static int llu_readlink_internal(struct inode *inode,
         struct llu_sb_info *sbi = llu_i2sbi(inode);
         struct ll_fid fid;
         struct mds_body *body;
-        int rc, symlen = lli->lli_st_size + 1;
+        struct intnl_stat *st = llu_i2stat(inode);
+        int rc, symlen = st->st_size + 1;
         ENTRY;
 
         *request = NULL;
@@ -808,7 +862,7 @@ static int llu_readlink_internal(struct inode *inode,
         rc = mdc_getattr(sbi->ll_mdc_exp, &fid,
                          OBD_MD_LINKNAME, symlen, request);
         if (rc) {
-                CERROR("inode %lu: rc = %d\n", lli->lli_st_ino, rc);
+                CERROR("inode %llu: rc = %d\n", st->st_ino, rc);
                 RETURN(rc);
         }
 
@@ -823,8 +877,8 @@ static int llu_readlink_internal(struct inode *inode,
         
         LASSERT (symlen != 0);
         if (body->eadatasize != symlen) {
-                CERROR ("inode %lu: symlink length %d not expected %d\n",
-                        lli->lli_st_ino, body->eadatasize - 1, symlen - 1);
+                CERROR ("inode %llu: symlink length %d not expected %d\n",
+                        st->st_ino, body->eadatasize - 1, symlen - 1);
                 GOTO (failed, rc = -EPROTO);
         }
 
@@ -832,8 +886,8 @@ static int llu_readlink_internal(struct inode *inode,
         if (*symname == NULL ||
             strnlen (*symname, symlen) != symlen - 1) {
                 /* not full/NULL terminated */
-                CERROR ("inode %lu: symlink not NULL terminated string"
-                        "of length %d\n", lli->lli_st_ino, symlen - 1);
+                CERROR ("inode %llu: symlink not NULL terminated string"
+                        "of length %d\n", st->st_ino, symlen - 1);
                 GOTO (failed, rc = -EPROTO);
         }
 
@@ -863,6 +917,7 @@ static int llu_iop_readlink(struct pnode *pno, char *data, size_t bufsize)
 
         LASSERT(symname);
         strncpy(data, symname, bufsize);
+        rc = strlen(symname);
 
         ptlrpc_req_finished(request);
  out:
@@ -880,14 +935,13 @@ static int llu_iop_mknod_raw(struct pnode *pno,
         int err = -EMLINK;
         ENTRY;
 
-        CDEBUG(D_VFSTRACE, "VFS Op:name=%s,dir=%lu\n",
-               pno->p_base->pb_name.name, llu_i2info(dir)->lli_st_ino);
+        CDEBUG(D_VFSTRACE, "VFS Op:name=%.*s,dir=%llu\n",
+               (int)pno->p_base->pb_name.len, pno->p_base->pb_name.name,
+               llu_i2stat(dir)->st_ino);
 
-        if (llu_i2info(dir)->lli_st_nlink >= EXT2_LINK_MAX)
+        if (llu_i2stat(dir)->st_nlink >= EXT2_LINK_MAX)
                 RETURN(err);
 
-        mode &= ~current->fs->umask;
-
         switch (mode & S_IFMT) {
         case 0:
         case S_IFREG:
@@ -927,13 +981,18 @@ static int llu_iop_link_raw(struct pnode *old, struct pnode *new)
         LASSERT(src);
         LASSERT(dir);
 
+        liblustre_wait_event(0);
         llu_prepare_mdc_op_data(&op_data, src, dir, name, namelen, 0);
         rc = mdc_link(llu_i2sbi(src)->ll_mdc_exp, &op_data, &request);
         ptlrpc_req_finished(request);
+        liblustre_wait_event(0);
 
         RETURN(rc);
 }
 
+/*
+ * libsysio will clear the inode immediately after return
+ */
 static int llu_iop_unlink_raw(struct pnode *pno)
 {
         struct inode *dir = pno->p_base->pb_parent->pb_ino;
@@ -948,30 +1007,21 @@ static int llu_iop_unlink_raw(struct pnode *pno)
 
         LASSERT(target);
 
+        liblustre_wait_event(0);
         llu_prepare_mdc_op_data(&op_data, dir, NULL, name, len, 0);
         rc = mdc_unlink(llu_i2sbi(dir)->ll_mdc_exp, &op_data, &request);
-        if (!rc) {
+        if (!rc)
                 rc = llu_objects_destroy(request, dir);
-
-                llu_i2info(target)->lli_stale_flag = 1;
-                unhook_stale_inode(pno);
-        }
-
         ptlrpc_req_finished(request);
+        liblustre_wait_event(0);
+
         RETURN(rc);
 }
 
-/* FIXME
- * following cases need to be considered later:
- * - rename an opened file/dir
- * - an opened file be removed in rename
- * - rename to remove and hardlink (?opened)
- */
 static int llu_iop_rename_raw(struct pnode *old, struct pnode *new)
 {
         struct inode *src = old->p_parent->p_base->pb_ino;
         struct inode *tgt = new->p_parent->p_base->pb_ino;
-        struct inode *tgtinode = new->p_base->pb_ino;
         const char *oldname = old->p_base->pb_name.name;
         int oldnamelen = old->p_base->pb_name.len;
         const char *newname = new->p_base->pb_name.name;
@@ -990,11 +1040,6 @@ static int llu_iop_rename_raw(struct pnode *old, struct pnode *new)
                         &request);
         if (!rc) {
                 rc = llu_objects_destroy(request, src);
-
-                if (tgtinode) {
-                        llu_i2info(tgtinode)->lli_stale_flag = 1;
-                        unhook_stale_inode(new);
-                }
         }
 
         ptlrpc_req_finished(request);
@@ -1088,6 +1133,8 @@ static int llu_iop_statvfs(struct pnode *pno,
         int rc;
         ENTRY;
 
+        liblustre_wait_event(0);
+
 #ifndef __CYGWIN__
         LASSERT(pno->p_base->pb_ino);
         rc = llu_statfs(llu_i2sbi(pno->p_base->pb_ino), &fs);
@@ -1119,17 +1166,16 @@ static int llu_iop_mkdir_raw(struct pnode *pno, mode_t mode)
         const char *name = qstr->name;
         int len = qstr->len;
         struct ptlrpc_request *request = NULL;
-        struct llu_inode_info *lli = llu_i2info(dir);
+        struct intnl_stat *st = llu_i2stat(dir);
         struct mdc_op_data op_data;
         int err = -EMLINK;
         ENTRY;
-        CDEBUG(D_VFSTRACE, "VFS Op:name=%s,dir=%lu/%lu(%p)\n",
-               name, lli->lli_st_ino, lli->lli_st_generation, dir);
+        CDEBUG(D_VFSTRACE, "VFS Op:name=%.*s,dir=%llu/%lu(%p)\n",
+               len, name, st->st_ino, llu_i2info(dir)->lli_st_generation, dir);
 
-        if (lli->lli_st_nlink >= EXT2_LINK_MAX)
+        if (st->st_nlink >= EXT2_LINK_MAX)
                 RETURN(err);
 
-        mode = (mode & (S_IRWXUGO|S_ISVTX) & ~current->fs->umask) | S_IFDIR;
         llu_prepare_mdc_op_data(&op_data, dir, NULL, name, len, 0);
         err = mdc_create(llu_i2sbi(dir)->ll_mdc_exp, &op_data, NULL, 0, mode,
                          current->fsuid, current->fsgid, 0, &request);
@@ -1145,35 +1191,338 @@ static int llu_iop_rmdir_raw(struct pnode *pno)
         int len = qstr->len;
         struct ptlrpc_request *request = NULL;
         struct mdc_op_data op_data;
-        struct llu_inode_info *lli = llu_i2info(dir);
         int rc;
         ENTRY;
-        CDEBUG(D_VFSTRACE, "VFS Op:name=%s,dir=%lu/%lu(%p)\n",
-               name, lli->lli_st_ino, lli->lli_st_generation, dir);
+        CDEBUG(D_VFSTRACE, "VFS Op:name=%.*s,dir=%llu/%lu(%p)\n", len, name,
+               llu_i2stat(dir)->st_ino, llu_i2info(dir)->lli_st_generation,dir);
 
         llu_prepare_mdc_op_data(&op_data, dir, NULL, name, len, S_IFDIR);
         rc = mdc_unlink(llu_i2sbi(dir)->ll_mdc_exp, &op_data, &request);
         ptlrpc_req_finished(request);
 
-        /* libsysio: remove the pnode right away */
-        if (!rc) {
-                llu_i2info(pno->p_base->pb_ino)->lli_stale_flag = 1;
-                unhook_stale_inode(pno);
+        RETURN(rc);
+}
+
+#ifdef O_DIRECT
+#define FCNTL_FLMASK (O_APPEND|O_NONBLOCK|O_ASYNC|O_DIRECT)
+#else
+#define FCNTL_FLMASK (O_APPEND|O_NONBLOCK|O_ASYNC)
+#endif
+#define FCNTL_FLMASK_INVALID (O_NONBLOCK|O_ASYNC)
+
+/* refer to ll_file_flock() for details */
+static int llu_file_flock(struct inode *ino,
+                          int cmd,
+                          struct file_lock *file_lock)
+{
+        struct obd_device *obddev;
+        struct llu_inode_info *lli = llu_i2info(ino);
+        struct intnl_stat *st = llu_i2stat(ino);
+        struct ldlm_res_id res_id =
+                { .name = {st->st_ino,
+                           lli->lli_st_generation, LDLM_FLOCK} };
+        struct lustre_handle lockh = {0};
+        ldlm_policy_data_t flock;
+        ldlm_mode_t mode = 0;
+        int flags = 0;
+        int rc;
+
+        CDEBUG(D_VFSTRACE, "VFS Op:inode="LPU64" file_lock=%p\n",
+               st->st_ino, file_lock);
+
+        flock.l_flock.pid = file_lock->fl_pid;
+        flock.l_flock.start = file_lock->fl_start;
+        flock.l_flock.end = file_lock->fl_end;
+
+        switch (file_lock->fl_type) {
+        case F_RDLCK:
+                mode = LCK_PR;
+                break;
+        case F_UNLCK:
+                mode = LCK_NL;
+                break;
+        case F_WRLCK:
+                mode = LCK_PW;
+                break;
+        default:
+                CERROR("unknown fcntl lock type: %d\n", file_lock->fl_type);
+                LBUG();
+        }
+
+        switch (cmd) {
+        case F_SETLKW:
+#ifdef F_SETLKW64
+#if F_SETLKW64 != F_SETLKW
+        case F_SETLKW64:
+#endif
+#endif
+                flags = 0;
+                break;
+        case F_SETLK:
+#ifdef F_SETLK64
+#if F_SETLK64 != F_SETLK
+        case F_SETLK64:
+#endif
+#endif
+                flags = LDLM_FL_BLOCK_NOWAIT;
+                break;
+        case F_GETLK:
+#ifdef F_GETLK64
+#if F_GETLK64 != F_GETLK
+        case F_GETLK64:
+#endif
+#endif
+                flags = LDLM_FL_TEST_LOCK;
+                file_lock->fl_type = mode;
+                break;
+        default:
+                CERROR("unknown fcntl cmd: %d\n", cmd);
+                LBUG();
         }
 
+        CDEBUG(D_DLMTRACE, "inode="LPU64", pid=%u, flags=%#x, mode=%u, "
+               "start="LPU64", end="LPU64"\n", st->st_ino, flock.l_flock.pid,
+               flags, mode, flock.l_flock.start, flock.l_flock.end);
+
+        obddev = llu_i2mdcexp(ino)->exp_obd;
+        rc = ldlm_cli_enqueue(llu_i2mdcexp(ino), NULL, obddev->obd_namespace,
+                              res_id, LDLM_FLOCK, &flock, mode, &flags,
+                              NULL, ldlm_flock_completion_ast, NULL, file_lock,
+                              NULL, 0, NULL, &lockh);
         RETURN(rc);
 }
 
-static int llu_iop_fcntl(struct inode *ino, int cmd, va_list ap)
+static int assign_type(struct file_lock *fl, int type)
 {
-        CERROR("liblustre did not support fcntl\n");
-        return -ENOSYS;
+        switch (type) {
+        case F_RDLCK:
+        case F_WRLCK:
+        case F_UNLCK:
+                fl->fl_type = type;
+                return 0;
+        default:
+                return -EINVAL;
+        }
+}
+
+static int flock_to_posix_lock(struct inode *ino,
+                               struct file_lock *fl,
+                               struct flock *l)
+{
+        switch (l->l_whence) {
+        /* XXX: only SEEK_SET is supported in lustre */
+        case SEEK_SET:
+                fl->fl_start = 0;
+                break;
+        default:
+                return -EINVAL;
+        }
+
+        fl->fl_end = l->l_len - 1;
+        if (l->l_len < 0)
+                return -EINVAL;
+        if (l->l_len == 0)
+                fl->fl_end = OFFSET_MAX;
+
+        fl->fl_pid = getpid();
+        fl->fl_flags = FL_POSIX;
+        fl->fl_notify = NULL;
+        fl->fl_insert = NULL;
+        fl->fl_remove = NULL;
+        /* XXX: these fields can't be filled with suitable values,
+                but I think lustre doesn't use them.
+         */
+        fl->fl_owner = NULL;
+        fl->fl_file = NULL;
+
+        return assign_type(fl, l->l_type);
 }
 
+static int llu_fcntl_getlk(struct inode *ino, struct flock *flock)
+{
+        struct file_lock fl;
+        int error;
+
+        error = EINVAL;
+        if ((flock->l_type != F_RDLCK) && (flock->l_type != F_WRLCK))
+                goto out;
+
+        error = flock_to_posix_lock(ino, &fl, flock);
+        if (error)
+                goto out;
+
+        error = llu_file_flock(ino, F_GETLK, &fl);
+        if (error)
+                goto out;
+
+        flock->l_type = F_UNLCK;
+        if (fl.fl_type != F_UNLCK) {
+                flock->l_pid = fl.fl_pid;
+                flock->l_start = fl.fl_start;
+                flock->l_len = fl.fl_end == OFFSET_MAX ? 0:
+                        fl.fl_end - fl.fl_start + 1;
+                flock->l_whence = SEEK_SET;
+                flock->l_type = fl.fl_type;
+        }
+
+out:
+        return error;
+}
+
+static int llu_fcntl_setlk(struct inode *ino, int cmd, struct flock *flock)
+{
+        struct file_lock fl;
+        int flags = llu_i2info(ino)->lli_open_flags + 1;
+        int error;
+
+        error = flock_to_posix_lock(ino, &fl, flock);
+        if (error)
+                goto out;
+        if (cmd == F_SETLKW)
+                fl.fl_flags |= FL_SLEEP;
+
+        error = -EBADF;
+        switch (flock->l_type) {
+        case F_RDLCK:
+                if (!(flags & FMODE_READ))
+                        goto out;
+                break;
+        case F_WRLCK:
+                if (!(flags & FMODE_WRITE))
+                        goto out;
+                break;
+        case F_UNLCK:
+                break;
+        default:
+                error = -EINVAL;
+                goto out;
+        }
+
+        error = llu_file_flock(ino, cmd, &fl);
+        if (error)
+                goto out;
+
+out:
+        return error;
+}
+
+static int llu_iop_fcntl(struct inode *ino, int cmd, va_list ap, int *rtn)
+{
+        struct llu_inode_info *lli = llu_i2info(ino);
+        long flags;
+        struct flock *flock;
+        long err;
+
+        switch (cmd) {
+        case F_GETFL:
+                *rtn = lli->lli_open_flags;
+                return 0;
+        case F_SETFL:
+                flags = va_arg(ap, long);
+                flags &= FCNTL_FLMASK;
+                if (flags & FCNTL_FLMASK_INVALID) {
+                        CERROR("liblustre don't support O_NONBLOCK, O_ASYNC, "
+                               "and O_DIRECT on file descriptor\n");
+                        *rtn = -EINVAL;
+                        return EINVAL;
+                }
+                lli->lli_open_flags = (int)(flags & FCNTL_FLMASK) |
+                                      (lli->lli_open_flags & ~FCNTL_FLMASK);
+                *rtn = 0;
+                return 0;
+        case F_GETLK:
+                flock = va_arg(ap, struct flock *);
+                err = llu_fcntl_getlk(ino, flock);
+                *rtn = err? -1: 0;
+                return err;
+        case F_SETLK:
+        case F_SETLKW:
+                flock = va_arg(ap, struct flock *);
+                err = llu_fcntl_setlk(ino, cmd, flock);
+                *rtn = err? -1: 0;
+                return err;
+        }
+
+        CERROR("unsupported fcntl cmd %x\n", cmd);
+        *rtn = -ENOSYS;
+        return ENOSYS;
+}
+
+static int llu_get_grouplock(struct inode *inode, unsigned long arg)
+{
+        struct llu_inode_info *lli = llu_i2info(inode);
+        struct ll_file_data *fd = lli->lli_file_data;
+        ldlm_policy_data_t policy = { .l_extent = { .start = 0,
+                                                    .end = OBD_OBJECT_EOF}};
+        struct lustre_handle lockh = { 0 };
+        struct lov_stripe_md *lsm = lli->lli_smd;
+        ldlm_error_t err;
+        int flags = 0;
+        ENTRY;
+
+        if (fd->fd_flags & LL_FILE_GROUP_LOCKED) {
+                RETURN(-EINVAL);
+        }
+
+        policy.l_extent.gid = arg;
+        if (lli->lli_open_flags & O_NONBLOCK)
+                flags = LDLM_FL_BLOCK_NOWAIT;
+
+        err = llu_extent_lock(fd, inode, lsm, LCK_GROUP, &policy, &lockh,
+                              flags);
+        if (err)
+                RETURN(err);
+
+        fd->fd_flags |= LL_FILE_GROUP_LOCKED|LL_FILE_IGNORE_LOCK;
+        fd->fd_gid = arg;
+        memcpy(&fd->fd_cwlockh, &lockh, sizeof(lockh));
+
+        RETURN(0);
+}
+
+static int llu_put_grouplock(struct inode *inode, unsigned long arg)
+{
+        struct llu_inode_info *lli = llu_i2info(inode);
+        struct ll_file_data *fd = lli->lli_file_data;
+        struct lov_stripe_md *lsm = lli->lli_smd;
+        ldlm_error_t err;
+        ENTRY;
+
+        if (!(fd->fd_flags & LL_FILE_GROUP_LOCKED))
+                RETURN(-EINVAL);
+
+        if (fd->fd_gid != arg)
+                RETURN(-EINVAL);
+
+        fd->fd_flags &= ~(LL_FILE_GROUP_LOCKED|LL_FILE_IGNORE_LOCK);
+
+        err = llu_extent_unlock(fd, inode, lsm, LCK_GROUP, &fd->fd_cwlockh);
+        if (err)
+                RETURN(err);
+
+        fd->fd_gid = 0;
+        memset(&fd->fd_cwlockh, 0, sizeof(fd->fd_cwlockh));
+
+        RETURN(0);
+}       
+
 static int llu_iop_ioctl(struct inode *ino, unsigned long int request,
                          va_list ap)
 {
-        CERROR("liblustre did not support ioctl\n");
+
+        liblustre_wait_event(0);
+
+        switch (request) {
+        unsigned long arg;
+        case LL_IOC_GROUP_LOCK:
+                arg = va_arg(ap, unsigned long);
+                return llu_get_grouplock(ino, arg);
+        case LL_IOC_GROUP_UNLOCK:
+                arg = va_arg(ap, unsigned long);
+                return llu_put_grouplock(ino, arg);
+        }
+
+        CERROR("did not support ioctl cmd %lx\n", request);
         return -ENOSYS;
 }
 
@@ -1182,11 +1531,13 @@ static int llu_iop_ioctl(struct inode *ino, unsigned long int request,
  */
 static int llu_iop_sync(struct inode *inode)
 {
+        liblustre_wait_event(0);
         return 0;
 }
 
 static int llu_iop_datasync(struct inode *inode)
 {
+        liblustre_wait_event(0);
         return 0;
 }
 
@@ -1203,8 +1554,11 @@ struct inode *llu_iget(struct filesys *fs, struct lustre_md *md)
 
         if ((md->body->valid &
              (OBD_MD_FLGENER | OBD_MD_FLID | OBD_MD_FLTYPE)) !=
-            (OBD_MD_FLGENER | OBD_MD_FLID | OBD_MD_FLTYPE))
-                CERROR("invalide fields!\n");
+            (OBD_MD_FLGENER | OBD_MD_FLID | OBD_MD_FLTYPE)) {
+                CERROR("bad md body valid mask "LPX64"\n", md->body->valid);
+                LBUG();
+                return ERR_PTR(-EPERM);
+        }
 
         /* try to find existing inode */
         fid.id = md->body->ino;
@@ -1215,9 +1569,10 @@ struct inode *llu_iget(struct filesys *fs, struct lustre_md *md)
         if (inode) {
                 struct llu_inode_info *lli = llu_i2info(inode);
 
-                if (lli->lli_stale_flag ||
-                    lli->lli_st_generation != md->body->generation)
+                if (inode->i_zombie ||
+                    lli->lli_st_generation != md->body->generation) {
                         I_RELE(inode);
+                }
                 else {
                         llu_update_inode(inode, md->body, md->lsm);
                         return inode;
@@ -1253,12 +1608,26 @@ llu_fsswop_mount(const char *source,
         struct lustre_handle osc_conn = {0, };
         struct lustre_md md;
         class_uuid_t uuid;
+        struct config_llog_instance cfg;
+        char ll_instance[sizeof(sbi) * 2 + 1];
         struct lustre_profile *lprof;
+       char *zconf_mdsnid, *zconf_mdsname, *zconf_profile;
         char *osc = NULL, *mdc = NULL;
-        int err = -EINVAL;
+        int async = 1, err = -EINVAL;
 
         ENTRY;
 
+        if (ll_parse_mount_target(source,
+                                  &zconf_mdsnid,
+                                  &zconf_mdsname,
+                                  &zconf_profile)) {
+                CERROR("mal-formed target %s\n", source);
+                RETURN(err);
+        }
+        if (!zconf_mdsnid || !zconf_mdsname || !zconf_profile) {
+                printf("Liblustre: invalid target %s\n", source);
+                RETURN(err);
+        }
         /* allocate & initialize sbi */
         OBD_ALLOC(sbi, sizeof(*sbi));
         if (!sbi)
@@ -1268,61 +1637,30 @@ llu_fsswop_mount(const char *source,
         generate_random_uuid(uuid);
         class_uuid_unparse(uuid, &sbi->ll_sb_uuid);
 
-        /* zeroconf */
-        if (g_zconf) {
-                struct config_llog_instance cfg;
-                int len;
-
-                if (!g_zconf_mdsname) {
-                        CERROR("no mds name\n");
-                        GOTO(out_free, err = -EINVAL);
-                }
-
-                /* generate a string unique to this super, let's try
-                 the address of the super itself.*/
-                len = (sizeof(sbi) * 2) + 1; 
-                OBD_ALLOC(sbi->ll_instance, len);
-                if (sbi->ll_instance == NULL) 
-                        GOTO(out_free, err = -ENOMEM);
-                sprintf(sbi->ll_instance, "%p", sbi);
-
-                cfg.cfg_instance = sbi->ll_instance;
-                cfg.cfg_uuid = sbi->ll_sb_uuid;
-                err = liblustre_process_log(&cfg, 1);
-                if (err < 0) {
-                        CERROR("Unable to process log: %s\n", g_zconf_profile);
-
-                        GOTO(out_free, err);
-                }
-
-                lprof = class_get_profile(g_zconf_profile);
-                if (lprof == NULL) {
-                        CERROR("No profile found: %s\n", g_zconf_profile);
-                        GOTO(out_free, err = -EINVAL);
-                }
-                if (osc)
-                        OBD_FREE(osc, strlen(osc) + 1);
-                OBD_ALLOC(osc, strlen(lprof->lp_osc) + 
-                          strlen(sbi->ll_instance) + 2);
-                sprintf(osc, "%s-%s", lprof->lp_osc, sbi->ll_instance);
-
-                if (mdc)
-                        OBD_FREE(mdc, strlen(mdc) + 1);
-                OBD_ALLOC(mdc, strlen(lprof->lp_mdc) + 
-                          strlen(sbi->ll_instance) + 2);
-                sprintf(mdc, "%s-%s", lprof->lp_mdc, sbi->ll_instance);
-        } else {
-                /* setup from dump_file */
-                if (list_empty(&lustre_profile_list)) {
-                        CERROR("no profile\n");
-                        GOTO(out_free, err = -EINVAL);
-                }
+        /* generate a string unique to this super, let's try
+         the address of the super itself.*/
+        sprintf(ll_instance, "%p", sbi);
+
+        /* retrive & parse config log */
+        cfg.cfg_instance = ll_instance;
+        cfg.cfg_uuid = sbi->ll_sb_uuid;
+        err = liblustre_process_log(&cfg, zconf_mdsnid, zconf_mdsname,
+                                    zconf_profile, 1);
+        if (err < 0) {
+                CERROR("Unable to process log: %s\n", zconf_profile);
+                GOTO(out_free, err);
+        }
 
-                lprof = list_entry(lustre_profile_list.next,
-                                   struct lustre_profile, lp_list);
-                osc = lprof->lp_osc;
-                mdc = lprof->lp_mdc;
+        lprof = class_get_profile(zconf_profile);
+        if (lprof == NULL) {
+                CERROR("No profile found: %s\n", zconf_profile);
+                GOTO(out_free, err = -EINVAL);
         }
+        OBD_ALLOC(osc, strlen(lprof->lp_osc) + strlen(ll_instance) + 2);
+        sprintf(osc, "%s-%s", lprof->lp_osc, ll_instance);
+
+        OBD_ALLOC(mdc, strlen(lprof->lp_mdc) + strlen(ll_instance) + 2);
+        sprintf(mdc, "%s-%s", lprof->lp_mdc, ll_instance);
 
         if (!osc) {
                 CERROR("no osc\n");
@@ -1344,6 +1682,8 @@ llu_fsswop_mount(const char *source,
                 CERROR("MDC %s: not setup or attached\n", mdc);
                 GOTO(out_free, err = -EINVAL);
         }
+        obd_set_info(obd->obd_self_export, strlen("async"), "async",
+                     sizeof(async), &async);
 
         /* setup mdc */
         err = obd_connect(&mdc_conn, obd, &sbi->ll_sb_uuid, NULL /* ocd */);
@@ -1367,6 +1707,8 @@ llu_fsswop_mount(const char *source,
                 CERROR("OSC %s: not setup or attached\n", osc);
                 GOTO(out_mdc, err = -EINVAL);
         }
+        obd_set_info(obd->obd_self_export, strlen("async"), "async",
+                     sizeof(async), &async);
 
         err = obd_connect(&osc_conn, obd, &sbi->ll_sb_uuid, NULL /* ocd */);
         if (err) {
@@ -1387,7 +1729,7 @@ llu_fsswop_mount(const char *source,
 
         /* fetch attr of root inode */
         err = mdc_getattr(sbi->ll_mdc_exp, &rootfid,
-                          OBD_MD_FLNOTOBD|OBD_MD_FLBLOCKS, 0, &request);
+                          OBD_MD_FLGETATTR | OBD_MD_FLBLOCKS, 0, &request);
         if (err) {
                 CERROR("mdc_getattr failed for root: rc = %d\n", err);
                 GOTO(out_osc, err);
@@ -1402,7 +1744,7 @@ llu_fsswop_mount(const char *source,
         LASSERT(sbi->ll_rootino != 0);
 
         root = llu_iget(fs, &md);
-        if (root == NULL) {
+        if (!root || IS_ERR(root)) {
                 CERROR("fail to generate root inode\n");
                 GOTO(out_request, err = -EBADF);
         }
@@ -1424,7 +1766,7 @@ llu_fsswop_mount(const char *source,
 
         ptlrpc_req_finished(request);
 
-        printf("LibLustre: namespace mounted successfully!\n");
+        CDEBUG(D_SUPER, "LibLustre: %s mounted successfully!\n", source);
 
         return 0;
 
@@ -1437,6 +1779,10 @@ out_osc:
 out_mdc:
         obd_disconnect(sbi->ll_mdc_exp);
 out_free:
+        if (osc)
+                OBD_FREE(osc, strlen(osc) + 1);
+        if (mdc)
+                OBD_FREE(mdc, strlen(mdc) + 1);
         OBD_FREE(sbi, sizeof(*sbi));
         return err;
 }
@@ -1459,8 +1805,9 @@ static struct inode_ops llu_inode_ops = {
         inop_link:      llu_iop_link_raw,
         inop_unlink:    llu_iop_unlink_raw,
         inop_rename:    llu_iop_rename_raw,
-        inop_ipreadv:   llu_iop_ipreadv,
-        inop_ipwritev:  llu_iop_ipwritev,
+        inop_pos:       llu_iop_pos,
+        inop_read:      llu_iop_read,
+        inop_write:     llu_iop_write,
         inop_iodone:    llu_iop_iodone,
         inop_fcntl:     llu_iop_fcntl,
         inop_sync:      llu_iop_sync,
@@ -1472,5 +1819,3 @@ static struct inode_ops llu_inode_ops = {
 #endif
         inop_gone:      llu_iop_gone,
 };
-
-#warning "time_after() defined in liblustre.h need to be rewrite in userspace"
index 0a9a1c0..616fea4 100644 (file)
@@ -2,47 +2,53 @@
 
 AM_CPPFLAGS = -I$(SYSIO)/include -I/opt/lam/include $(LLCPPFLAGS) -I$(top_srcdir)/portals/unals
 AM_CFLAGS = $(LLCFLAGS)
-LIBS = $(LIBEFENCE) $(LIBREADLINE)
+AM_LIBS = $(LIBEFENCE) $(LIBREADLINE)
 
-LLIB_EXEC= ../liblustre.a -lpthread
+LLIB_EXEC= $(top_builddir)/lustre/liblustre/liblustre.a $(CAP_LIBS) $(PTHREAD_LIBS)
 
 if LIBLUSTRE
 noinst_LIBRARIES = libtestcommon.a
-def_tests = echo_test sanity recovery_small replay_single replay_ost_single
+
+if LIBLUSTRE_TESTS
+noinst_PROGRAMS = sanity recovery_small replay_single replay_ost_single
+
+if TESTS
+noinst_PROGRAMS += echo_test
+endif # TESTS
 
 if MPITESTS
-noinst_PROGRAMS = $(def_tests) test_lock_cancel
-else
-noinst_PROGRAMS = $(def_tests)
-endif
+noinst_PROGRAMS += test_lock_cancel
+endif # MPITESTS
+
+endif # LIBLUSTRE_TESTS
 endif # LIBLUSTRE
 
 libtestcommon_a_SOURCES = test_common.c test_common.h
 
-echo_test_SOURCES = echo_test.c $(top_srcdir)/lustre/utils/parser.c $(top_srcdir)/lustre/utils/obd.c $(top_srcdir)/lustre/utils/lustre_cfg.c
+echo_test_SOURCES = echo_test.c  $(top_srcdir)/lustre/utils/parser.c $(top_srcdir)/lustre/utils/obd.c $(top_srcdir)/lustre/utils/lustre_cfg.c
 echo_test_CFLAGS = $(LL_CFLAGS)
-echo_test_LDADD = ../liblsupport.a $(LIBREADLINE) -lpthread 
+echo_test_LDADD = $(top_builddir)/lustre/liblustre/liblsupport.a $(LIBREADLINE) $(CAP_LIBS) $(PTHREAD_LIBS) 
 echo_test_DEPENDENCIES=$(top_builddir)/lustre/liblustre/liblsupport.a
 
 sanity_SOURCES = sanity.c
 sanity_CFLAGS = $(LL_CFLAGS)
-sanity_LDADD := ./libtestcommon.a $(LLIB_EXEC)
-sanity_DEPENDENCIES = $(top_builddir)/lustre/liblustre/liblustre.a ./libtestcommon.a
+sanity_LDADD := libtestcommon.a $(LLIB_EXEC)
+sanity_DEPENDENCIES = $(top_builddir)/lustre/liblustre/liblustre.a libtestcommon.a
 
 recovery_small_SOURCES = recovery_small.c
 recovery_small_CFLAGS = $(LL_CFLAGS)
-recovery_small_LDADD := ./libtestcommon.a $(LLIB_EXEC) 
-recovery_small_DEPENDENCIES = $(top_builddir)/lustre/liblustre/liblustre.a
+recovery_small_LDADD := libtestcommon.a $(LLIB_EXEC) 
+recovery_small_DEPENDENCIES = $(top_builddir)/lustre/liblustre/liblustre.a libtestcommon.a
 
 replay_single_SOURCES = replay_single.c
 replay_single_CFLAGS = $(LL_CFLAGS)
-replay_single_LDADD := ./libtestcommon.a $(LLIB_EXEC)
-replay_single_DEPENDENCIES = $(top_builddir)/lustre/liblustre/liblustre.a
+replay_single_LDADD := libtestcommon.a $(LLIB_EXEC)
+replay_single_DEPENDENCIES = $(top_builddir)/lustre/liblustre/liblustre.a libtestcommon.a
 
 replay_ost_single_SOURCES = replay_ost_single.c
 replay_ost_single_CFLAGS = $(LL_CFLAGS)
-replay_ost_single_LDADD := ./libtestcommon.a $(LLIB_EXEC)
-replay_ost_single_DEPENDENCIES = $(top_builddir)/lustre/liblustre/liblustre.a
+replay_ost_single_LDADD := libtestcommon.a $(LLIB_EXEC)
+replay_ost_single_DEPENDENCIES = $(top_builddir)/lustre/liblustre/liblustre.a libtestcommon.a
 
 if MPITESTS
 test_lock_cancel_SOURCES = test_lock_cancel.c
index 9720c63..b45f18e 100644 (file)
@@ -1,64 +1,39 @@
-#include <stdio.h>
-#include <netinet/in.h>
-#include <sys/socket.h>
-#include <arpa/inet.h>
-
-#include <portals/api-support.h> /* needed for ptpctl.h */
-#include <portals/ptlctl.h>    /* needed for parse_dump */
-
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Lustre Light user test program
+ *
+ *  Copyright (c) 2002-2004 Cluster File Systems, Inc.
+ *
+ *   This file is part of Lustre, http://www.lustre.org.
+ *
+ *   Lustre is free software; you can redistribute it and/or
+ *   modify it under the terms of version 2 of the GNU General Public
+ *   License as published by the Free Software Foundation.
+ *
+ *   Lustre is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with Lustre; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
 
 #include <liblustre.h>
 #include <linux/obd.h>
 #include <linux/obd_class.h>
-#include <procbridge.h>
 #include <linux/obd_ost.h>
 
 #define LIBLUSTRE_TEST 1
 #include "../utils/lctl.c"
 
-struct ldlm_namespace;
-struct ldlm_res_id;
-struct obd_import;
-
-unsigned int portal_subsystem_debug = ~0 - (S_PORTALS | S_NAL);
-
-void *inter_module_get(char *arg)
-{
-        if (!strcmp(arg, "ldlm_cli_cancel_unused"))
-                return ldlm_cli_cancel_unused;
-        else if (!strcmp(arg, "ldlm_namespace_cleanup"))
-                return ldlm_namespace_cleanup;
-        else if (!strcmp(arg, "ldlm_replay_locks"))
-                return ldlm_replay_locks;
-        else
-                return NULL;
-}
-
-struct task_struct *current;
-
-int init_current(int argc, char **argv)
-{ 
-        current = malloc(sizeof(*current));
-        strncpy(current->comm, argv[0], sizeof(current->comm));
-        current->pid = getpid();
-       return 0;
-}
-
-ptl_nid_t tcpnal_mynid;
-
-int init_lib_portals()
-{
-        int rc;
-
-        rc = PtlInit();
-        if (rc != PTL_OK)
-                CERROR("PtlInit failed: error %d\n", rc);
-        return rc;
-}
+#include "../lutil.h"
 
 extern int class_handle_ioctl(unsigned int cmd, unsigned long arg);
 
-int liblustre_ioctl(int dev_id, int opc, void *ptr)
+static int liblustre_ioctl(int dev_id, unsigned int opc, void *ptr)
 {
        int   rc = -EINVAL;
        
@@ -76,15 +51,6 @@ int liblustre_ioctl(int dev_id, int opc, void *ptr)
        return rc;
 }
 
-static void generate_random_uuid(unsigned char uuid_out[16])
-{
-        int *arr = (int*)uuid_out;
-        int i;
-
-        for (i = 0; i < sizeof(uuid_out)/sizeof(int); i++)
-                arr[i] = rand();
-}
-
 static char *echo_server_nid = NULL;
 static char *echo_server_ostname = "obd1";
 static char *osc_dev_name = "OSC_DEV_NAME";
@@ -259,18 +225,14 @@ int main(int argc, char **argv)
                return 1;
        }
 
-        srand(time(NULL));
-
-       tcpnal_mynid = rand();
-#if 1
        portal_debug = 0;
        portal_subsystem_debug = 0;
-#endif
 
-        if (init_current(argc, argv) ||
+        liblustre_init_random();
+
+        if (liblustre_init_current(argv[0]) ||
            init_obdclass() || init_lib_portals() ||
            ptlrpc_init() ||
-           ldlm_init() ||
            mdc_init() ||
            lov_init() ||
            osc_init() ||
index 5aed06c..6cd9ba4 100644 (file)
@@ -39,6 +39,8 @@
 
 #include "test_common.h"
 
+#define MAX_STRING_SIZE 2048
+
 static struct {
         const char   *name;
         unsigned long code;
@@ -52,6 +54,7 @@ static struct {
 static int drop_index = 0;
 
 static char mds_server[1024] = {0, };
+static char ssh_cmd[MAX_STRING_SIZE] = {0,};
 
 int do_stat(const char *name, struct stat *buf)
 {
@@ -121,14 +124,14 @@ void cleanup_dir(const char *path)
 
 #define FAIL()                                                             \
     do {                                                                   \
-        char cmd[1024];                                                    \
+        char cmd[MAX_STRING_SIZE];                                         \
         int rc;                                                            \
                                                                            \
         if (drop_arr[drop_index].name) {                                   \
             printf("server drops next %s\n", drop_arr[drop_index].name);   \
             sprintf(cmd,                                                   \
-                    "ssh %s \"echo %lu > /proc/sys/lustre/fail_loc\"",     \
-                    mds_server, drop_arr[drop_index].code);                \
+                    "%s %s \"echo %lu > /proc/sys/lustre/fail_loc\"",      \
+                    ssh_cmd, mds_server, drop_arr[drop_index].code);       \
             if (system(cmd)) {                                             \
                 printf("error excuting remote command: %d\n", rc);         \
                 exit(rc);                                                  \
@@ -141,8 +144,8 @@ void cleanup_dir(const char *path)
         char cmd[1024];                                                    \
                                                                            \
         if (drop_arr[drop_index].name) {                                   \
-            sprintf(cmd, "ssh %s \"echo 0 > /proc/sys/lustre/fail_loc\"",  \
-                    mds_server);                                           \
+            sprintf(cmd, "%s %s \"echo 0 > /proc/sys/lustre/fail_loc\"",   \
+                    ssh_cmd, mds_server);                                  \
             system(cmd);                                                   \
         }                                                                  \
     } while (0)
@@ -313,6 +316,7 @@ int main(int argc, char * argv[])
         static struct option long_opts[] = {
                 {"target", 1, 0, 0},
                 {"dumpfile", 1, 0, 0},
+                {"ssh", 1, 0, 0},
                 {0, 0, 0, 0}
         };
 
@@ -329,12 +333,14 @@ int main(int argc, char * argv[])
                                 setenv(ENV_LUSTRE_MNTTGT, optarg, 1);
                         } else if (!strcmp(long_opts[opt_index].name, "dumpfile")) {
                                 setenv(ENV_LUSTRE_DUMPFILE, optarg, 1);
+                        } else if (!strcmp(long_opts[opt_index].name, "ssh")) {
+                                safe_strncpy(ssh_cmd, optarg, MAX_STRING_SIZE);
                         } else
                                 usage(argv[0]);
                         break;
                 }
                 case 's':
-                        strcpy(mds_server, optarg);
+                        safe_strncpy(mds_server, optarg, MAX_STRING_SIZE);
                         break;
                 default:
                         usage(argv[0]);
@@ -347,13 +353,18 @@ int main(int argc, char * argv[])
         if (strlen(mds_server) == 0)
                 usage(argv[0]);
 
-        sprintf(cmd, "ssh %s cat /dev/null", mds_server);
+        /* default to using ssh */
+        if (!strlen(ssh_cmd)) {
+                safe_strncpy(ssh_cmd, "ssh", MAX_STRING_SIZE);
+        }
+
+        sprintf(cmd, "%s %s cat /dev/null", ssh_cmd, mds_server);
         if (system(cmd)) {
-                printf("can't access server node: %s\n", mds_server);
+                printf("Can't access server node: %s using method: %s\n", mds_server, ssh_cmd);
                 exit(-1);
         }
 
-        setenv(ENV_LUSTRE_TIMEOUT, "10", 1);
+        setenv(ENV_LUSTRE_TIMEOUT, "5", 1);
 
         __liblustre_setup_();
 
@@ -362,7 +373,9 @@ int main(int argc, char * argv[])
                 t2();
                 t3();
                 t4();
+#if 0
                 t5();
+#endif
                 t6();
                 t7();
 
index 6645056..9628354 100644 (file)
 
 #include "test_common.h"
 
+#define MAX_STRING_SIZE 2048
 
-
-static char mds_server[1024] = {0,};
-static char barrier_script[1024] = {0,};
-static char failover_script[1024] = {0,};
-static char barrier_cmd[1024] = {0,};
-static char failover_cmd[1024] = {0,};
+static char mds_server[MAX_STRING_SIZE] = {0,};
+static char barrier_script[MAX_STRING_SIZE] = {0,};
+static char failover_script[MAX_STRING_SIZE] = {0,};
+static char barrier_cmd[MAX_STRING_SIZE] = {0,};
+static char failover_cmd[MAX_STRING_SIZE] = {0,};
+static char ssh_cmd[MAX_STRING_SIZE] = {0,};
 
 static void replay_barrier()
 {
@@ -91,9 +92,11 @@ static void mds_failover()
 
 void t0()
 {
+        char *path="/mnt/lustre/f0";
         ENTRY("empty replay");
         replay_barrier();
         mds_failover();
+        t_check_stat_fail("/mnt/lustre/f0");
         LEAVE();
 }
 
@@ -315,19 +318,19 @@ extern void __liblustre_cleanup_(void);
 void usage(const char *cmd)
 {
         printf("Usage: \t%s --target mdsnid:/mdsname/profile -s mds_hostname "
-                "-b \"barrier cmd\" -f \"failover cmd\"\n", cmd);
+                "-b \"barrier cmd\" -f \"failover cmd\" [--rsh \"rsh_cmd\"]\n", cmd);
         printf("       \t%s --dumpfile dumpfile -s mds_hostname -b \"barrier cmd\" "
-                "-f \"failover cmd\"\n", cmd);
+                "-f \"failover cmd\" [--rsh \"rsh_cmd\"]\n", cmd);
         exit(-1);
 }
 
 void test_ssh()
 {
-        char cmd[1024];
+        char cmd[MAX_STRING_SIZE];
 
-        sprintf(cmd, "ssh %s cat /dev/null", mds_server);
+        sprintf(cmd, "%s %s cat /dev/null", ssh_cmd, mds_server);
         if (system(cmd)) {
-                printf("ssh can't access server node: %s\n", mds_server);
+                printf("Can't access server node: %s using method: %s\n", mds_server, ssh_cmd);
                 exit(-1);
         }
 }
@@ -338,6 +341,7 @@ int main(int argc, char * const argv[])
         static struct option long_opts[] = {
                 {"target", 1, 0, 0},
                 {"dumpfile", 1, 0, 0},
+                {"ssh", 1, 0, 0},
                 {0, 0, 0, 0}
         };
 
@@ -354,18 +358,20 @@ int main(int argc, char * const argv[])
                                 setenv(ENV_LUSTRE_MNTTGT, optarg, 1);
                         } else if (!strcmp(long_opts[opt_index].name, "dumpfile")) {
                                 setenv(ENV_LUSTRE_DUMPFILE, optarg, 1);
+                        } else if (!strcmp(long_opts[opt_index].name, "ssh")) {
+                                safe_strncpy(ssh_cmd, optarg, MAX_STRING_SIZE);
                         } else
                                 usage(argv[0]);
                         break;
                 }
                 case 's':
-                        strcpy(mds_server, optarg);
+                        safe_strncpy(mds_server, optarg, MAX_STRING_SIZE);
                         break;
                 case 'b':
-                        strcpy(barrier_script, optarg);
+                        safe_strncpy(barrier_script, optarg, MAX_STRING_SIZE);
                         break;
                 case 'f':
-                        strcpy(failover_script, optarg);
+                        safe_strncpy(failover_script, optarg, MAX_STRING_SIZE);
                         break;
                 default:
                         usage(argv[0]);
@@ -378,11 +384,18 @@ int main(int argc, char * const argv[])
             !strlen(failover_script))
                 usage(argv[0]);
 
+        /* default to using ssh */
+        if (!strlen(ssh_cmd)) {
+                safe_strncpy(ssh_cmd, "ssh", MAX_STRING_SIZE);
+        }
+
         test_ssh();
 
         /* prepare remote command */
-        sprintf(barrier_cmd, "ssh %s \"%s\"", mds_server, barrier_script);
-        sprintf(failover_cmd, "ssh %s \"%s\"", mds_server, failover_script);
+        sprintf(barrier_cmd, "%s %s \"%s\"", 
+                ssh_cmd, mds_server, barrier_script);
+        sprintf(failover_cmd, "%s %s \"%s\"", 
+                ssh_cmd, mds_server, failover_script);
 
         setenv(ENV_LUSTRE_TIMEOUT, "10", 1);
 
index 944ae9c..acea41e 100644 (file)
 #include <fcntl.h>
 #include <sys/queue.h>
 #include <signal.h>
-
-#include <sysio.h>
-#include <mount.h>
+#include <errno.h>
+#include <dirent.h>
+#include <sys/uio.h>
+#include <sys/time.h>
 
 #include "test_common.h"
 
+extern char *lustre_path;
+
 #define ENTRY(str)                                                      \
         do {                                                            \
                 char buf[100];                                          \
                 int len;                                                \
-                sprintf(buf, "===== START: %s ", (str));                \
+                sprintf(buf, "===== START %s: %s ", __FUNCTION__, (str)); \
                 len = strlen(buf);                                      \
                 if (len < 79) {                                         \
                         memset(buf+len, '=', 100-len);                  \
 
 #define LEAVE()                                                         \
         do {                                                            \
-                printf("----- END TEST successfully ---");              \
-                printf("-----------------------------");                \
-                printf("-------------------\n");                        \
+                char buf[100];                                          \
+                int len;                                                \
+                sprintf(buf, "===== END TEST %s: successfully ",        \
+                        __FUNCTION__);                                  \
+                len = strlen(buf);                                      \
+                if (len < 79) {                                         \
+                        memset(buf+len, '=', 100-len);                  \
+                        buf[79] = '\n';                                 \
+                        buf[80] = 0;                                    \
+                }                                                       \
+                printf("%s", buf);                                      \
         } while (0)
 
+#define MAX_PATH_LENGTH 4096
+
 void t1()
 {
-        char *path="/mnt/lustre/test_t1";
+        char path[MAX_PATH_LENGTH] = "";
+
         ENTRY("create/delete");
+        snprintf(path, MAX_PATH_LENGTH, "%s/test_t1", lustre_path);
 
         t_touch(path);
         t_unlink(path);
@@ -72,8 +87,10 @@ void t1()
 
 void t2()
 {
-        char *path="/mnt/lustre/test_t2";
+        char path[MAX_PATH_LENGTH] = "";
+
         ENTRY("mkdir/rmdir");
+        snprintf(path, MAX_PATH_LENGTH, "%s/test_t2", lustre_path);
 
         t_mkdir(path);
         t_rmdir(path);
@@ -82,8 +99,10 @@ void t2()
 
 void t3()
 {
-        char *path="/mnt/lustre/test_t3";
+        char path[MAX_PATH_LENGTH] = "";
+
         ENTRY("regular stat");
+        snprintf(path, MAX_PATH_LENGTH, "%s/test_t3", lustre_path);
 
         t_touch(path);
         t_check_stat(path, NULL);
@@ -93,8 +112,10 @@ void t3()
 
 void t4()
 {
-        char *path="/mnt/lustre/test_t4";
+        char path[MAX_PATH_LENGTH] = "";
+
         ENTRY("dir stat");
+        snprintf(path, MAX_PATH_LENGTH, "%s/test_t4", lustre_path);
 
         t_mkdir(path);
         t_check_stat(path, NULL);
@@ -102,115 +123,54 @@ void t4()
         LEAVE();
 }
 
-#define PAGE_SIZE (4096)
-#define _npages (2048)
-
-static int _buffer[_npages][PAGE_SIZE/sizeof(int)];
-
-/* pos:   i/o start from
- * xfer:  npages per transfer
- */
-static void pages_io(int xfer, loff_t pos)
+void t6()
 {
-        char *path="/mnt/lustre/test_t5";
-        int check_sum[_npages] = {0,};
-        int fd, rc, i, j;
-
-        memset(_buffer, 0, sizeof(_buffer));
-
-        /* create sample data */
-        for (i = 0; i < _npages; i++) {
-                for (j = 0; j < PAGE_SIZE/sizeof(int); j++) {
-                        _buffer[i][j] = rand();
-                }
-        }
+        char path[MAX_PATH_LENGTH] = "";
+        char path2[MAX_PATH_LENGTH] = "";
 
-        /* compute checksum */
-        for (i = 0; i < _npages; i++) {
-                for (j = 0; j < PAGE_SIZE/sizeof(int); j++) {
-                        check_sum[i] += _buffer[i][j];
-                }
-        }
+        ENTRY("symlink");
+        snprintf(path, MAX_PATH_LENGTH, "%s/test_t6", lustre_path);
+        snprintf(path2, MAX_PATH_LENGTH, "%s/test_t6_link", lustre_path);
 
         t_touch(path);
-
-       fd = t_open(path);
-
-        /* write */
-       lseek(fd, pos, SEEK_SET);
-       for (i = 0; i < _npages; i += xfer) {
-               rc = write(fd, _buffer[i], PAGE_SIZE * xfer);
-                if (rc != PAGE_SIZE * xfer) {
-                        printf("write error %d (i = %d)\n", rc, i);
-                        exit(1);
-                }
-       }
-        printf("succefully write %d pages(%d per xfer)\n", _npages, xfer);
-        memset(_buffer, 0, sizeof(_buffer));
-
-        /* read */
-       lseek(fd, pos, SEEK_SET);
-       for (i = 0; i < _npages; i += xfer) {
-               rc = read(fd, _buffer[i], PAGE_SIZE * xfer);
-                if (rc != PAGE_SIZE * xfer) {
-                        printf("read error %d (i = %d)\n", rc, i);
-                        exit(1);
-                }
-       }
-        printf("succefully read %d pages(%d per xfer)\n", _npages, xfer);
-
-        /* compute checksum */
-        for (i = 0; i < _npages; i++) {
-                int sum = 0;
-                for (j = 0; j < PAGE_SIZE/sizeof(int); j++) {
-                        sum += _buffer[i][j];
-                }
-                if (sum != check_sum[i]) {
-                        printf("chunk %d checksum error: expected 0x%x, get 0x%x\n",
-                                i, check_sum[i], sum);
-                }
-        }
-        printf("checksum verified OK!\n");
-
-       t_close(fd);
+        t_symlink(path, path2);
+        t_check_stat(path2, NULL);
+        t_unlink(path2);
         t_unlink(path);
+        LEAVE();
 }
 
-void t5()
+void t6b()
 {
-        char text[256];
-        loff_t off_array[] = {1, 4, 17, 255, 258, 4095, 4097, 8191, 1024*1024*1024};
-        int np = 1, i;
-        loff_t offset = 0;
+        char path[MAX_PATH_LENGTH] = "";
+        char path2[MAX_PATH_LENGTH] = "";
+        char cwd[MAX_PATH_LENGTH] = "";
+        char *tmp;
+        int fd;
 
-        while (np <= _npages) {
-                sprintf(text, "pages_io: %d per transfer, offset %lld",
-                        np, offset);
-                ENTRY(text);
-                pages_io(np, offset);
-                LEAVE();
-                np += np;
-        }
+        ENTRY("symlink + chdir and open");
+        snprintf(path, MAX_PATH_LENGTH, "%s/test_t6b", lustre_path);
+        snprintf(path2, MAX_PATH_LENGTH, "%s/test_t6b_link", lustre_path);
 
-        for (i = 0; i < sizeof(off_array)/sizeof(loff_t); i++) {
-                offset = off_array[i];
-                sprintf(text, "pages_io: 16 per transfer, offset %lld",
-                        offset);
-                ENTRY(text);
-                pages_io(16, offset);
+        t_mkdir(path);
+        t_symlink(path, path2);
+        t_check_stat(path2, NULL);
+
+        tmp = getcwd(cwd, MAX_PATH_LENGTH);
+        if (tmp == NULL) {
+                fprintf(stderr, "current path too long to fit in "
+                        "MAX_PATH_LENGTH?\n");
                 LEAVE();
+                return;
         }
-}
+        t_chdir(path2);
+        t_chdir(cwd);
+        t_rmdir(path);
+        t_touch(path);
 
-void t6()
-{
-        char *path="/mnt/lustre/test_t6";
-        char *path2="/mnt/lustre/test_t6_link";
-        ENTRY("symlink");
+        fd = t_open(path2);
+        t_close(fd);
 
-        t_touch(path);
-        t_symlink(path, path2);
-        t_check_stat(path2, NULL);
         t_unlink(path2);
         t_unlink(path);
         LEAVE();
@@ -218,19 +178,32 @@ void t6()
 
 void t7()
 {
-        char *path="/mnt/lustre/test_t7";
+        char path[MAX_PATH_LENGTH] = "";
+        int rc;
+
         ENTRY("mknod");
+        snprintf(path, MAX_PATH_LENGTH, "%s/test_t7", lustre_path);
 
-        t_mknod(path, S_IFCHR | 0644, 5, 4);
-        t_check_stat(path, NULL);
-        t_unlink(path);
+        if (geteuid() != 0) {
+                rc = mknod(path, S_IFCHR | 0644, (5<<8 | 4));
+                if (rc != -1 || errno != EPERM) {
+                        printf("mknod shouldn't success: rc %d, errno %d\n",
+                                rc, errno);
+                }
+        } else {
+                t_mknod(path, S_IFCHR | 0644, 5, 4);
+                t_check_stat(path, NULL);
+                t_unlink(path);
+        }
         LEAVE();
 }
 
 void t8()
 {
-        char *path="/mnt/lustre/test_t8";
+        char path[MAX_PATH_LENGTH] = "";
+
         ENTRY("chmod");
+        snprintf(path, MAX_PATH_LENGTH, "%s/test_t8", lustre_path);
 
         t_touch(path);
         t_chmod_raw(path, 0700);
@@ -241,9 +214,12 @@ void t8()
 
 void t9()
 {
-        char *path="/mnt/lustre/test_t9";
-        char *path2="/mnt/lustre/test_t9_link";
+        char path[MAX_PATH_LENGTH] = "";
+        char path2[MAX_PATH_LENGTH] = "";
+
         ENTRY("hard link");
+        snprintf(path, MAX_PATH_LENGTH, "%s/test_t9", lustre_path);
+        snprintf(path2, MAX_PATH_LENGTH, "%s/test_t9_link", lustre_path);
 
         t_touch(path);
         t_link(path, path2);
@@ -256,14 +232,22 @@ void t9()
 
 void t10()
 {
-        char *dir1="/mnt/lustre/test_t10_dir1";
-        char *dir2="/mnt/lustre/test_t10_dir2";
-        char *path1="/mnt/lustre/test_t10_reg1";
-        char *path2="/mnt/lustre/test_t10_reg2";
-        char *rename1="/mnt/lustre/test_t10_dir1/rename1";
-        char *rename2="/mnt/lustre/test_t10_dir2/rename2";
-        char *rename3="/mnt/lustre/test_t10_dir2/rename3";
+        char dir1[MAX_PATH_LENGTH] = "";
+        char dir2[MAX_PATH_LENGTH] = "";
+        char path1[MAX_PATH_LENGTH] = "";
+        char path2[MAX_PATH_LENGTH] = "";
+        char rename1[MAX_PATH_LENGTH] = "";
+        char rename2[MAX_PATH_LENGTH] = "";
+        char rename3[MAX_PATH_LENGTH] = "";
+
         ENTRY("rename");
+        snprintf(dir1, MAX_PATH_LENGTH, "%s/test_t10_dir1", lustre_path);
+        snprintf(dir2, MAX_PATH_LENGTH, "%s/test_t10_dir2", lustre_path);
+        snprintf(path1, MAX_PATH_LENGTH, "%s/test_t10_reg1", lustre_path);
+        snprintf(path2, MAX_PATH_LENGTH, "%s/test_t10_reg2", lustre_path);
+        snprintf(rename1, MAX_PATH_LENGTH, "%s/test_t10_dir1/rename1", lustre_path);
+        snprintf(rename2, MAX_PATH_LENGTH, "%s/test_t10_dir2/rename2", lustre_path);
+        snprintf(rename3, MAX_PATH_LENGTH, "%s/test_t10_dir2/rename3", lustre_path);
 
         t_mkdir(dir1);
         t_mkdir(dir2);
@@ -281,12 +265,12 @@ void t10()
 
 void t11()
 {
-        char *base="/mnt/lustre";
-        char path[4096], path2[4096];
+        char *base=lustre_path;
+        char path[MAX_PATH_LENGTH], path2[MAX_PATH_LENGTH];
         int i, j, level = 5, nreg = 5;
         ENTRY("deep tree");
 
-        strcpy(path, base);
+        safe_strncpy(path, base, MAX_PATH_LENGTH);
 
         for (i = 0; i < level; i++) {
                 for (j = 0; j < nreg; j++) {
@@ -299,7 +283,7 @@ void t11()
         }
 
         for (i = level; i > 0; i--) {
-                strcpy(path, base);
+                safe_strncpy(path, base, MAX_PATH_LENGTH);
                 for (j = 1; j < i; j++)
                         strcat(path, "/dir");
                 
@@ -317,10 +301,11 @@ void t11()
 
 void t12()
 {
-        char *dir="/mnt/lustre/test_t12_dir";
+        char dir[MAX_PATH_LENGTH] = "";
         char buf[1024*128];
         int fd;
         ENTRY("empty directory readdir");
+        snprintf(dir, MAX_PATH_LENGTH, "%s/test_t12_dir", lustre_path);
 
         t_mkdir(dir);
         fd = t_opendir(dir);
@@ -332,13 +317,14 @@ void t12()
 
 void t13()
 {
-        char *dir="/mnt/lustre/test_t13_dir/";
+        char dir[MAX_PATH_LENGTH] = "";
         char name[1024];
         char buf[1024];
         const int nfiles = 20;
         char *prefix = "test13_filename_prefix_";
         int fd, i;
         ENTRY("multiple entries directory readdir");
+        snprintf(dir, MAX_PATH_LENGTH, "%s/test_t13_dir/", lustre_path);
 
         t_mkdir(dir);
         printf("Creating %d files...\n", nfiles);
@@ -360,13 +346,16 @@ void t13()
 
 void t14()
 {
-        char *dir="/mnt/lustre/test_t14_dir/";
+        char dir[MAX_PATH_LENGTH] = "";
         char name[1024];
         char buf[1024];
         const int nfiles = 256;
         char *prefix = "test14_filename_long_prefix_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA___";
-        int fd, i;
+       struct dirent64 *ent;
+        int fd, i, rc, pos, index;
+       loff_t base = 0;
         ENTRY(">1 block(4k) directory readdir");
+        snprintf(dir, MAX_PATH_LENGTH, "%s/test_t14_dir/", lustre_path);
 
         t_mkdir(dir);
         printf("Creating %d files...\n", nfiles);
@@ -375,7 +364,35 @@ void t14()
                 t_touch(name);
         }
         fd = t_opendir(dir);
-        t_ls(fd, buf, sizeof(buf));
+        printf("Listing...\n");
+        index = 0;
+       while ((rc = getdirentries64(fd, buf, 1024, &base)) > 0) {
+               pos = 0;
+               while (pos < rc) {
+                        char *item;
+
+                       ent = (struct dirent64 *) ((char*) buf + pos);
+                        item = (char *) ent->d_name;
+                        if (!strcmp(item, ".") || !strcmp(item, ".."))
+                                goto iter;
+                        if (strstr(item, prefix) != item) {
+                                printf("found bad name %s\n", item);
+                                exit(-1);
+                        }
+                       printf("[%03d]: %s\n",
+                                index++, item + strlen(prefix));
+iter:
+                       pos += ent->d_reclen;
+               }
+       }
+       if (rc < 0) {
+               printf("getdents error %d\n", rc);
+                exit(-1);
+       }
+        if (index != nfiles) {
+                printf("get %d files != %d\n", index, nfiles);
+                exit(-1);
+        }
         t_close(fd);
         printf("Cleanup...\n");
         for (i = 0; i < nfiles; i++) {
@@ -388,9 +405,10 @@ void t14()
 
 void t15()
 {
-        char *file = "/mnt/lustre/test_t15_file";
+        char file[MAX_PATH_LENGTH] = "";
         int fd;
         ENTRY("open-stat-close");
+        snprintf(file, MAX_PATH_LENGTH, "%s/test_t15_file", lustre_path);
 
         t_touch(file);
         fd = t_open(file);
@@ -400,6 +418,431 @@ void t15()
         LEAVE();
 }
 
+void t16()
+{
+        char file[MAX_PATH_LENGTH] = "";
+        ENTRY("small-write-read");
+        snprintf(file, MAX_PATH_LENGTH, "%s/test_t16_file", lustre_path);
+
+        t_echo_create(file, "aaaaaaaaaaaaaaaaaaaaaa");
+        t_grep(file, "aaaaaaaaaaaaaaaaaaaaaa");
+        t_unlink(file);
+        LEAVE();
+}
+
+void t17()
+{
+        char file[MAX_PATH_LENGTH] = "";
+        int fd;
+        ENTRY("open-unlink without close");
+        snprintf(file, MAX_PATH_LENGTH, "%s/test_t17_file", lustre_path);
+
+        fd = open(file, O_WRONLY | O_CREAT, 0666);
+        if (fd < 0) {
+                printf("failed to create file: %s\n", strerror(errno));
+                exit(-1);
+        }
+        t_unlink(file);
+        LEAVE();
+}
+
+void t18()
+{
+        char file[MAX_PATH_LENGTH] = "";
+        char buf[128];
+        int fd, i;
+        struct stat statbuf[3];
+        ENTRY("write should change mtime/atime");
+        snprintf(file, MAX_PATH_LENGTH, "%s/test_t18_file", lustre_path);
+
+        for (i = 0; i < 3; i++) {
+                fd = open(file, O_RDWR|O_CREAT|O_APPEND, (mode_t)0666);
+                if (fd < 0) {
+                        printf("error open file: %s\n", strerror(errno));
+                        exit(-1);
+                }
+                if (write(fd, buf, sizeof(buf)) != sizeof(buf)) {
+                        printf("error write file\n");
+                        exit(-1);
+                }
+                close(fd);
+                if(stat(file, &statbuf[i]) != 0) {
+                        printf("Error stat\n");
+                        exit(1);
+                }
+                printf("atime %lu, mtime %lu\n",
+                        statbuf[i].st_atime, statbuf[i].st_mtime);
+                sleep(2);
+        }
+
+        for (i = 1; i < 3; i++) {
+                if ((statbuf[i].st_atime <= statbuf[i-1].st_atime) ||
+                    (statbuf[i].st_mtime <= statbuf[i-1].st_mtime)) {
+                        printf("time error\n");
+                        exit(-1);
+                }
+        }
+        t_unlink(file);
+        LEAVE();
+}
+
+void t18b()
+{
+        char file[MAX_PATH_LENGTH] = "";
+        char buf[128];
+        int fd, i;
+        struct stat statbuf[3];
+        ENTRY("utime should change mtime/atime/ctime");
+        snprintf(file, MAX_PATH_LENGTH, "%s/test_t23_file", lustre_path);
+        t_touch(file);
+
+        for (i = 0; i < 3; i++) {
+                t_utime(file, NULL);
+                if(stat(file, &statbuf[i]) != 0) {
+                        printf("Error stat\n");
+                        exit(1);
+                }
+                printf("atime %lu, mtime %lu, ctime %lu\n",
+                       statbuf[i].st_atime, statbuf[i].st_mtime,
+                       statbuf[i].st_ctime);
+                sleep(2);
+        }
+
+        for (i = 1; i < 3; i++) {
+                if ((statbuf[i].st_atime <= statbuf[i-1].st_atime) ||
+                    (statbuf[i].st_mtime <= statbuf[i-1].st_mtime) ||
+                    (statbuf[i].st_ctime <= statbuf[i-1].st_ctime)) {
+                        printf("time error\n");
+                        exit(-1);
+                }
+        }
+        t_unlink(file);
+        LEAVE();
+}
+
+void t19()
+{
+        char file[MAX_PATH_LENGTH] = "";
+        int fd;
+        struct stat statbuf;
+        ENTRY("open(O_TRUNC) should trancate file to 0-length");
+        snprintf(file, MAX_PATH_LENGTH, "%s/test_t19_file", lustre_path);
+
+        t_echo_create(file, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
+
+        fd = open(file, O_RDWR|O_CREAT|O_TRUNC, (mode_t)0666);
+        if (fd < 0) {
+                printf("error open file: %s\n", strerror(errno));
+                exit(-1);
+        }
+        close(fd);
+        if(stat(file, &statbuf) != 0) {
+                printf("Error stat\n");
+                exit(1);
+        }
+        if (statbuf.st_size != 0) {
+                printf("size %ld is not zero\n", statbuf.st_size);
+                exit(-1);
+        }
+        t_unlink(file);
+        LEAVE();
+}
+
+void t20()
+{
+        char file[MAX_PATH_LENGTH] = "";
+        int fd;
+        struct iovec iov[2];
+        char buf[100];
+        ssize_t ret;
+        ENTRY("trap app's general bad pointer for file i/o");
+        snprintf(file, MAX_PATH_LENGTH, "%s/test_t20_file", lustre_path);
+
+        fd = open(file, O_RDWR|O_CREAT, (mode_t)0666);
+        if (fd < 0) {
+                printf("error open file: %s\n", strerror(errno));
+                exit(-1);
+        }
+
+        ret = write(fd, NULL, 20);
+        if (ret != -1 || errno != EFAULT) {
+                printf("write 1: ret %ld, errno %d\n", ret, errno);
+                exit(1);
+        }
+        ret = write(fd, (void *)-1, 20);
+        if (ret != -1 || errno != EFAULT) {
+                printf("write 2: ret %ld, errno %d\n", ret, errno);
+                exit(1);
+        }
+        iov[0].iov_base = NULL;
+        iov[0].iov_len = 10;
+        iov[1].iov_base = (void *)-1;
+        iov[1].iov_len = 10;
+        ret = writev(fd, iov, 2);
+        if (ret != -1 || errno != EFAULT) {
+                printf("writev 1: ret %ld, errno %d\n", ret, errno);
+                exit(1);
+        }
+        iov[0].iov_base = NULL;
+        iov[0].iov_len = 0;
+        iov[1].iov_base = buf;
+        iov[1].iov_len = sizeof(buf);
+        ret = writev(fd, iov, 2);
+        if (ret != sizeof(buf)) {
+                printf("write 3 ret %ld, error %d\n", ret, errno);
+                exit(1);
+        }
+        lseek(fd, 0, SEEK_SET);
+
+        ret = read(fd, NULL, 20);
+        if (ret != -1 || errno != EFAULT) {
+                printf("read 1: ret %ld, errno %d\n", ret, errno);
+                exit(1);
+        }
+        ret = read(fd, (void *)-1, 20);
+        if (ret != -1 || errno != EFAULT) {
+                printf("read 2: ret %ld, errno %d\n", ret, errno);
+                exit(1);
+        }
+        iov[0].iov_base = NULL;
+        iov[0].iov_len = 10;
+        iov[1].iov_base = (void *)-1;
+        iov[1].iov_len = 10;
+        ret = readv(fd, iov, 2);
+        if (ret != -1 || errno != EFAULT) {
+                printf("readv 1: ret %ld, errno %d\n", ret, errno);
+                exit(1);
+        }
+        iov[0].iov_base = NULL;
+        iov[0].iov_len = 0;
+        iov[1].iov_base = buf;
+        iov[1].iov_len = sizeof(buf);
+        ret = readv(fd, iov, 2);
+        if (ret != sizeof(buf)) {
+                printf("read 3 ret %ld, error %d\n", ret, errno);
+                exit(1);
+        }
+
+        close(fd);
+        t_unlink(file);
+        LEAVE();
+}
+
+void t21()
+{
+        char file[MAX_PATH_LENGTH] = "";
+        int fd, ret;
+       struct flock lock = {
+               .l_type = F_RDLCK,
+               .l_whence = SEEK_SET,
+       };
+
+        ENTRY("basic fcntl support");
+        snprintf(file, MAX_PATH_LENGTH, "%s/test_t21_file", lustre_path);
+
+        fd = open(file, O_RDWR|O_CREAT, (mode_t)0666);
+        if (fd < 0) {
+                printf("error open file: %m\n", file);
+                exit(-1);
+        }
+
+        t_fcntl(fd, F_SETFL, O_APPEND);
+        if (!(ret = t_fcntl(fd, F_GETFL)) & O_APPEND) {
+                printf("error get flag: ret %x\n", ret);
+                exit(-1);
+        }
+
+       t_fcntl(fd, F_SETLK, &lock);
+       t_fcntl(fd, F_GETLK, &lock);
+       lock.l_type = F_WRLCK;
+       t_fcntl(fd, F_SETLKW, &lock);
+       t_fcntl(fd, F_GETLK, &lock);
+       lock.l_type = F_UNLCK;
+       t_fcntl(fd, F_SETLK, &lock);
+
+        close(fd);
+        t_unlink(file);
+        LEAVE();
+}
+
+void t22()
+{
+        char file[MAX_PATH_LENGTH] = "";
+        int fd;
+        char *str = "1234567890";
+        char buf[100];
+        ssize_t ret;
+        ENTRY("make sure O_APPEND take effect");
+        snprintf(file, MAX_PATH_LENGTH, "%s/test_t22_file", lustre_path);
+
+        fd = open(file, O_RDWR|O_CREAT|O_APPEND, (mode_t)0666);
+        if (fd < 0) {
+                printf("error open file: %s\n", strerror(errno));
+                exit(-1);
+        }
+
+        lseek(fd, 100, SEEK_SET);
+        ret = write(fd, str, strlen(str));
+        if (ret != strlen(str)) {
+                printf("write 1: ret %ld, errno %d\n", ret, errno);
+                exit(1);
+        }
+
+        lseek(fd, 0, SEEK_SET);
+        ret = read(fd, buf, sizeof(buf));
+        if (ret != strlen(str)) {
+                printf("read 1 got %ld\n", ret);
+                exit(1);
+        }
+
+        if (memcmp(buf, str, strlen(str))) {
+                printf("read 1 data err\n");
+                exit(1);
+        }
+
+        if (fcntl(fd, F_SETFL, 0)) {
+                printf("fcntl err: %s\n", strerror(errno));
+                exit(1);
+        }
+
+        lseek(fd, 100, SEEK_SET);
+        ret = write(fd, str, strlen(str));
+        if (ret != strlen(str)) {
+                printf("write 2: ret %ld, errno %d\n", ret, errno);
+                exit(1);
+        }
+
+        lseek(fd, 100, SEEK_SET);
+        ret = read(fd, buf, sizeof(buf));
+        if (ret != strlen(str)) {
+                printf("read 2 got %ld\n", ret);
+                exit(1);
+        }
+
+        if (memcmp(buf, str, strlen(str))) {
+                printf("read 2 data err\n");
+                exit(1);
+        }
+
+        close(fd);
+        t_unlink(file);
+        LEAVE();
+}
+
+#define PAGE_SIZE (4096)
+#define _npages (2048)
+
+static int _buffer[_npages][PAGE_SIZE/sizeof(int)];
+
+/* pos:   i/o start from
+ * xfer:  npages per transfer
+ */
+static void pages_io(int xfer, loff_t pos)
+{
+        char path[MAX_PATH_LENGTH] = "";
+
+        int check_sum[_npages] = {0,};
+        int fd, rc, i, j, data_error = 0;
+        struct timeval tw1, tw2, tr1, tr2;
+        double tw, tr;
+
+        snprintf(path, MAX_PATH_LENGTH, "%s/test_t50", lustre_path);
+        memset(_buffer, 0, sizeof(_buffer));
+
+        /* create sample data */
+        for (i = 0; i < _npages; i++) {
+                for (j = 0; j < PAGE_SIZE/sizeof(int); j++) {
+                        _buffer[i][j] = rand();
+                }
+        }
+
+        /* compute checksum */
+        for (i = 0; i < _npages; i++) {
+                for (j = 0; j < PAGE_SIZE/sizeof(int); j++) {
+                        check_sum[i] += _buffer[i][j];
+                }
+        }
+
+        t_touch(path);
+
+       fd = t_open(path);
+
+        /* write */
+       lseek(fd, pos, SEEK_SET);
+        gettimeofday(&tw1, NULL);
+       for (i = 0; i < _npages; i += xfer) {
+               rc = write(fd, _buffer[i], PAGE_SIZE * xfer);
+                if (rc != PAGE_SIZE * xfer) {
+                        printf("write error %d (i = %d)\n", rc, i);
+                        exit(1);
+                }
+       }
+        gettimeofday(&tw2, NULL);
+
+        memset(_buffer, 0, sizeof(_buffer));
+
+        /* read */
+       lseek(fd, pos, SEEK_SET);
+        gettimeofday(&tr1, NULL);
+       for (i = 0; i < _npages; i += xfer) {
+               rc = read(fd, _buffer[i], PAGE_SIZE * xfer);
+                if (rc != PAGE_SIZE * xfer) {
+                        printf("read error %d (i = %d)\n", rc, i);
+                        exit(1);
+                }
+       }
+        gettimeofday(&tr2, NULL);
+
+        /* compute checksum */
+        for (i = 0; i < _npages; i++) {
+                int sum = 0;
+                for (j = 0; j < PAGE_SIZE/sizeof(int); j++) {
+                        sum += _buffer[i][j];
+                }
+                if (sum != check_sum[i]) {
+                        data_error = 1;
+                        printf("chunk %d checksum error: expected 0x%x, get 0x%x\n",
+                                i, check_sum[i], sum);
+                }
+        }
+
+       t_close(fd);
+        t_unlink(path);
+        tw = (tw2.tv_sec - tw1.tv_sec) * 1000000 + (tw2.tv_usec - tw1.tv_usec);
+        tr = (tr2.tv_sec - tr1.tv_sec) * 1000000 + (tr2.tv_usec - tr1.tv_usec);
+        printf(" (R:%.3fM/s, W:%.3fM/s)\n",
+                (_npages * PAGE_SIZE) / (tw / 1000000.0) / (1024 * 1024),
+                (_npages * PAGE_SIZE) / (tr / 1000000.0) / (1024 * 1024));
+
+        if (data_error)
+                exit(1);
+}
+
+void t50()
+{
+        loff_t off_array[] = {1, 17, 255, 258, 4095, 4097, 8191,
+                              1024*1024*1024*1024ULL};
+        int np = 1, i;
+        loff_t offset = 0;
+
+        ENTRY("4k aligned i/o sanity");
+        while (np <= _npages) {
+                printf("%3d per xfer(total %d)...\t", np, _npages);
+                pages_io(np, offset);
+                np += np;
+        }
+        LEAVE();
+
+        ENTRY("4k un-aligned i/o sanity");
+        for (i = 0; i < sizeof(off_array)/sizeof(loff_t); i++) {
+                offset = off_array[i];
+                printf("16 per xfer(total %d), offset %10lld...\t",
+                        _npages, offset);
+                pages_io(16, offset);
+        }
+        LEAVE();
+}
+
 extern void __liblustre_setup_(void);
 extern void __liblustre_cleanup_(void);
 
@@ -446,13 +889,12 @@ int main(int argc, char * const argv[])
 
         __liblustre_setup_();
 
-#ifndef __CYGWIN__
         t1();
         t2();
         t3();
         t4();
-        t5();
         t6();
+        t6b();
         t7();
         t8();
         t9();
@@ -462,7 +904,15 @@ int main(int argc, char * const argv[])
         t13();
         t14();
         t15();
-#endif
+        t16();
+        t17();
+        t18();
+        t18b();
+        t19();
+        t20();
+        t21();
+        t22();
+        t50();
 
        printf("liblustre is about shutdown\n");
         __liblustre_cleanup_();
index a87f0fa..03d005d 100644 (file)
@@ -1,3 +1,7 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ */
+
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -7,6 +11,8 @@
 #include <string.h>
 #include <errno.h>
 #include <dirent.h>
+#include <utime.h>
+#include <stdarg.h>
 
 #include "test_common.h"
 
@@ -89,7 +95,7 @@ void t_mkdir(const char *path)
 {
         int rc;
 
-        rc = mkdir(path, 00644);
+        rc = mkdir(path, 00755);
         if (rc < 0) {
                 printf("mkdir(%s) error: %s\n", path, strerror(errno));
                 EXIT(1);
@@ -181,6 +187,27 @@ int t_open(const char *path)
         return fd;
 }
 
+int t_chdir(const char *path)
+{
+        int rc = chdir(path);
+        if (rc < 0) {
+                printf("chdir(%s) error: %s\n", path, strerror(errno));
+                EXIT_RET(rc);
+        }
+        return rc;
+}
+
+int t_utime(const char *path, const struct utimbuf *buf)
+{
+        int rc = utime(path, buf);
+        if (rc < 0) {
+                printf("utime(%s, %p) error: %s\n", path, buf,
+                       strerror(errno));
+                EXIT_RET(rc);
+        }
+        return rc;
+}
+
 int t_opendir(const char *path)
 {
         int fd;
@@ -209,6 +236,8 @@ int t_check_stat(const char *name, struct stat *buf)
        struct stat stat;
         int rc;
 
+        memset(&stat, 0, sizeof(stat));
+
        rc = lstat(name, &stat);
         if (rc) {
                printf("error %d stat %s\n", rc, name);
@@ -216,6 +245,10 @@ int t_check_stat(const char *name, struct stat *buf)
        }
         if (buf)
                 memcpy(buf, &stat, sizeof(*buf));
+        if (stat.st_blksize == 0) {
+                printf("error: blksize is 0\n");
+                EXIT_RET(-EINVAL);
+        }
 
        return 0;
 }
@@ -313,3 +346,74 @@ void t_ls(int fd, char *buf, int size)
                EXIT(-1);
        }
 }
+
+int t_fcntl(int fd, int cmd, ...)
+{
+       va_list ap;
+       long arg;
+       struct flock *lock;
+       int rc = -1;
+
+       va_start(ap, cmd);
+       switch (cmd) {
+       case F_GETFL:
+               va_end(ap);
+               rc = fcntl(fd, cmd);
+               if (rc == -1) {
+                       printf("fcntl GETFL failed: %s\n",
+                                strerror(errno));
+                       EXIT(1);
+               }
+               break;
+       case F_SETFL:
+               arg = va_arg(ap, long);
+               va_end(ap);
+               rc = fcntl(fd, cmd, arg);
+               if (rc == -1) {
+                       printf("fcntl SETFL %ld failed: %s\n",
+                                arg, strerror(errno));
+                       EXIT(1);
+               }
+               break;
+       case F_GETLK:
+       case F_SETLK:
+       case F_SETLKW:
+               lock = va_arg(ap, struct flock *);
+               va_end(ap);
+               rc = fcntl(fd, cmd, lock);
+               if (rc == -1) {
+                       printf("fcntl cmd %d failed: %s\n",
+                                cmd, strerror(errno));
+                       EXIT(1);
+               }
+               break;
+       case F_DUPFD:
+               arg = va_arg(ap, long);
+               va_end(ap);
+               rc = fcntl(fd, cmd, arg);
+               if (rc == -1) {
+                       printf("fcntl F_DUPFD %d failed: %s\n",
+                                (int)arg, strerror(errno));
+                       EXIT(1);
+               }
+               break;
+       default:
+               va_end(ap);
+               printf("fcntl cmd %d not supported\n", cmd);
+               EXIT(1);
+       }
+       return rc;
+}
+
+char *safe_strncpy(char *dst, char *src, int max_size)
+{
+       int src_size;
+       src_size=strlen(src);
+       if (src_size >= max_size) {
+        src_size=max_size-1;
+       }
+       memcpy(dst, src, src_size);
+       dst[src_size]=0;
+
+       return(dst);
+}
index c3687b9..5949a42 100644 (file)
@@ -8,6 +8,8 @@
 
 extern int exit_on_err;
 
+#include <utime.h> /* for utimbuf */
+
 void t_touch(const char *path);
 void t_create(const char *path);
 void t_link(const char *src, const char *dst);
@@ -21,6 +23,8 @@ void t_chmod(const char *path, const char *format, ...);
 void t_rename(const char *oldpath, const char *newpath);
 int t_open_readonly(const char *path);
 int t_open(const char *path);
+int t_chdir(const char *path);
+int t_utime(const char *path, const struct utimbuf *buf);
 int t_opendir(const char *path);
 void t_close(int fd);
 int t_check_stat(const char *name, struct stat *buf);
@@ -29,5 +33,8 @@ void t_echo_create(const char *path, const char *str);
 void t_grep(const char *path, char *str);
 void t_grep_v(const char *path, char *str);
 void t_ls(int fd, char *buf, int size);
+int t_fcntl(int fd, int cmd, ...);
+
+char *safe_strncpy(char *dst, char *src, int max_size);
 
 #endif
index 9fdde59..c389b31 100644 (file)
@@ -8,4 +8,4 @@ modulefs_DATA = llite$(KMODEXT)
 endif
 
 DIST_SOURCES := $(llite-objs:.o=.c) llite_internal.h rw24.c super.c rw26.c super25.c
-MOSTLYCLEANFILES = *.o *.ko *.mod.c
+MOSTLYCLEANFILES := @MOSTLYCLEANFILES@ 
index 39a53bd..8f55aa4 100644 (file)
@@ -167,10 +167,16 @@ restart:
                         lustre_dump_dentry(dentry, 1);
                         portals_debug_dumpstack(NULL);
                 } else if (d_mountpoint(dentry)) {
-                        CERROR("called on mountpoint (?) dentry=%p, inode=%p "
-                               "ino=%lu\n", dentry, inode, inode->i_ino);
-                        lustre_dump_dentry(dentry, 1);
-                        portals_debug_dumpstack(NULL);
+                        /* For mountpoints we skip removal of the dentry
+                           which happens solely because we have a lock on it
+                           obtained when this dentry was not a mountpoint yet */
+                        CDEBUG(D_DENTRY, "Skippind mountpoint dentry removal "
+                                         "%.*s (%p) parent %p\n",
+                                          dentry->d_name.len,
+                                          dentry->d_name.name,
+                                          dentry, dentry->d_parent);
+
+                        continue;
                 }
 
                 if (atomic_read(&dentry->d_count) == 0) {
index d704cf0..a6e63bc 100644 (file)
@@ -298,7 +298,7 @@ int ll_readdir(struct file * filp, void * dirent, filldir_t filldir)
         unsigned long n = pos >> PAGE_CACHE_SHIFT;
         unsigned long npages = dir_pages(inode);
         unsigned chunk_mask = ~(ext2_chunk_size(inode)-1);
-        unsigned char *types = NULL;
+        unsigned char *types = ext2_filetype_table;
         int need_revalidate = (filp->f_version != inode->i_version);
         int rc = 0;
         ENTRY;
@@ -341,16 +341,15 @@ int ll_readdir(struct file * filp, void * dirent, filldir_t filldir)
                 for ( ;(char*)de <= limit; de = ext2_next_entry(de)) {
                         if (de->inode) {
                                 int over;
-                                unsigned char d_type = DT_UNKNOWN;
 
                                 rc = 0; /* no error if we return something */
-                                if (types && de->file_type < EXT2_FT_MAX)
-                                        d_type = types[de->file_type];
 
                                 offset = (char *)de - kaddr;
                                 over = filldir(dirent, de->name, de->name_len,
                                                (n<<PAGE_CACHE_SHIFT) | offset,
-                                               le32_to_cpu(de->inode), d_type);
+                                               le32_to_cpu(de->inode),
+                                               types[de->file_type &
+                                                     (EXT2_FT_MAX - 1)]);
                                 if (over) {
                                         ext2_put_page(page);
                                         GOTO(done, rc);
@@ -398,7 +397,8 @@ static int ll_dir_ioctl(struct inode *inode, struct file *file,
         CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%u(%p), cmd=%#x\n",
                inode->i_ino, inode->i_generation, inode, cmd);
 
-        if (_IOC_TYPE(cmd) == 'T') /* tty ioctls */
+        /* asm-ppc{,64} declares TCGETS, et. al. as type 't' not 'T' */
+        if (_IOC_TYPE(cmd) == 'T' || _IOC_TYPE(cmd) == 't') /* tty ioctls */
                 return -ENOTTY;
 
         lprocfs_counter_incr(ll_i2sbi(inode)->ll_stats, LPROC_LL_IOCTL);
@@ -421,7 +421,6 @@ static int ll_dir_ioctl(struct inode *inode, struct file *file,
                 char *buf = NULL;
                 char *filename;
                 int namelen, rc, len = 0;
-                unsigned long valid;
 
                 rc = obd_ioctl_getdata(&buf, &len, (void *)arg);
                 if (rc)
@@ -436,10 +435,9 @@ static int ll_dir_ioctl(struct inode *inode, struct file *file,
                         GOTO(out, rc = -EINVAL);
                 }
 
-                valid = OBD_MD_FLID;
                 ll_inode2fid(&fid, inode);
-                rc = mdc_getattr_name(sbi->ll_mdc_exp, &fid,
-                                      filename, namelen, valid, 0, &request);
+                rc = mdc_getattr_name(sbi->ll_mdc_exp, &fid, filename, namelen,
+                                      OBD_MD_FLID, 0, &request);
                 if (rc < 0) {
                         CDEBUG(D_INFO, "mdc_getattr_name: %d\n", rc);
                         GOTO(out, rc);
@@ -498,13 +496,10 @@ static int ll_dir_ioctl(struct inode *inode, struct file *file,
                 struct lov_mds_md *lmm;
                 struct ll_fid fid;
                 struct mds_body *body;
-                unsigned long valid = 0;
                 int rc, lmmsize;
 
-                valid |= OBD_MD_FLDIREA;
-
                 ll_inode2fid(&fid, inode);
-                rc = mdc_getattr(sbi->ll_mdc_exp, &fid, valid,
+                rc = mdc_getattr(sbi->ll_mdc_exp, &fid, OBD_MD_FLDIREA,
                                  obd_size_diskmd(sbi->ll_osc_exp, NULL),
                                  &request);
                 if (rc < 0) {
@@ -840,8 +835,17 @@ static int ll_dir_ioctl(struct inode *inode, struct file *file,
 
                 RETURN(rc?:error);
         }
+        case OBD_IOC_GETNAME: {  
+                struct obd_device *obd = class_exp2obd(sbi->ll_osc_exp);
+                if (!obd)
+                        RETURN(-EFAULT);
+                if (copy_to_user((void *)arg, obd->obd_name, 
+                                strlen(obd->obd_name) + 1))
+                        RETURN (-EFAULT);
+                RETURN(0);
+        }
         default:
-                return obd_iocontrol(cmd, sbi->ll_osc_exp,0,NULL,(void *)arg);
+                RETURN(obd_iocontrol(cmd, sbi->ll_osc_exp,0,NULL,(void *)arg));
         }
 }
 
index 66fdd1e..856079f 100644 (file)
 int ll_mdc_close(struct obd_export *mdc_exp, struct inode *inode,
                         struct file *file)
 {
-        struct ll_file_data *fd = file->private_data;
+        struct ll_file_data *fd = LUSTRE_FPRIVATE(file);
         struct ptlrpc_request *req = NULL;
         struct obd_client_handle *och = &fd->fd_mds_och;
         struct obdo obdo;
         int rc;
         ENTRY;
 
+        /* clear group lock, if present */
+        if (unlikely(fd->fd_flags & LL_FILE_GROUP_LOCKED)) {
+                struct lov_stripe_md *lsm = ll_i2info(inode)->lli_smd;
+                fd->fd_flags &= ~(LL_FILE_GROUP_LOCKED|LL_FILE_IGNORE_LOCK);
+                rc = ll_extent_unlock(fd, inode, lsm, LCK_GROUP,
+                                      &fd->fd_cwlockh);
+        }
+
         obdo.o_id = inode->i_ino;
         obdo.o_valid = OBD_MD_FLID;
         obdo_from_inode(&obdo, inode, OBD_MD_FLTYPE | OBD_MD_FLMODE |
@@ -72,7 +80,7 @@ int ll_mdc_close(struct obd_export *mdc_exp, struct inode *inode,
         mdc_clear_open_replay_data(och);
         ptlrpc_req_finished(req);
         och->och_fh.cookie = DEAD_HANDLE_MAGIC;
-        file->private_data = NULL;
+        LUSTRE_FPRIVATE(file) = NULL;
         OBD_SLAB_FREE(fd, ll_file_data_slab, sizeof *fd);
 
         RETURN(rc);
@@ -102,7 +110,7 @@ int ll_file_release(struct inode *inode, struct file *file)
                 RETURN(0);
 
         lprocfs_counter_incr(sbi->ll_stats, LPROC_LL_RELEASE);
-        fd = (struct ll_file_data *)file->private_data;
+        fd = LUSTRE_FPRIVATE(file);
         LASSERT(fd != NULL);
 
         if (lsm)
@@ -149,7 +157,7 @@ int ll_local_open(struct file *file, struct lookup_intent *it)
         LASSERT (body != NULL);                 /* reply already checked out */
         LASSERT_REPSWABBED (req, 1);            /* and swabbed down */
 
-        LASSERT(!file->private_data);
+        LASSERT(!LUSTRE_FPRIVATE(file));
 
         OBD_SLAB_ALLOC(fd, ll_file_data_slab, SLAB_KERNEL, sizeof *fd);
         /* We can't handle this well without reorganizing ll_file_open and
@@ -158,7 +166,7 @@ int ll_local_open(struct file *file, struct lookup_intent *it)
 
         memcpy(&fd->fd_mds_och.och_fh, &body->handle, sizeof(body->handle));
         fd->fd_mds_och.och_magic = OBD_CLIENT_HANDLE_MAGIC;
-        file->private_data = fd;
+        LUSTRE_FPRIVATE(file) = fd;
         ll_readahead_init(file->f_dentry->d_inode, &fd->fd_ras);
 
         lli->lli_io_epoch = body->io_epoch;
@@ -376,7 +384,7 @@ void ll_pgcache_remove_extent(struct inode *inode, struct lov_stripe_md *lsm,
         CDEBUG(D_INODE|D_PAGE, "walking page indices start: %lu j: %lu "
                "count: %lu skip: %lu end: %lu%s\n", start, start % count,
                count, skip, end, discard ? " (DISCARDING)" : "");
-        
+
         /* walk through the vmas on the inode and tear down mmaped pages that
          * intersect with the lock.  this stops immediately if there are no
          * mmap()ed regions of the file.  This is not efficient at all and
@@ -386,7 +394,7 @@ void ll_pgcache_remove_extent(struct inode *inode, struct lov_stripe_md *lsm,
                 j = min(count - (i % count), end - i + 1);
                 LASSERT(j > 0);
                 LASSERT(inode->i_mapping);
-                if (ll_teardown_mmaps(inode->i_mapping, 
+                if (ll_teardown_mmaps(inode->i_mapping,
                                       (__u64)i << PAGE_CACHE_SHIFT,
                                       ((__u64)(i+j) << PAGE_CACHE_SHIFT) - 1) )
                         break;
@@ -639,10 +647,6 @@ static int ll_glimpse_callback(struct ldlm_lock *lock, void *reqp)
         return rc;
 }
 
-__u64 lov_merge_size(struct lov_stripe_md *lsm, int kms);
-__u64 lov_merge_blocks(struct lov_stripe_md *lsm);
-__u64 lov_merge_mtime(struct lov_stripe_md *lsm, __u64 current_time);
-
 /* NB: lov_merge_size will prefer locally cached writes if they extend the
  * file (because it prefers KMS over RSS when larger) */
 int ll_glimpse_size(struct inode *inode)
@@ -752,10 +756,10 @@ int ll_extent_unlock(struct ll_file_data *fd, struct inode *inode,
         RETURN(rc);
 }
 
-static ssize_t ll_file_read(struct file *filp, char *buf, size_t count,
+static ssize_t ll_file_read(struct file *file, char *buf, size_t count,
                             loff_t *ppos)
 {
-        struct inode *inode = filp->f_dentry->d_inode;
+        struct inode *inode = file->f_dentry->d_inode;
         struct ll_inode_info *lli = ll_i2info(inode);
         struct lov_stripe_md *lsm = lli->lli_smd;
         struct ll_lock_tree tree;
@@ -775,14 +779,36 @@ static ssize_t ll_file_read(struct file *filp, char *buf, size_t count,
         lprocfs_counter_add(ll_i2sbi(inode)->ll_stats, LPROC_LL_READ_BYTES,
                             count);
 
-        if (!lsm)
-                RETURN(0);
-        
-        node = ll_node_from_inode(inode, *ppos, *ppos  + count - 1, 
-                                  LCK_PR);
-        tree.lt_fd = filp->private_data;
-        rc = ll_tree_lock(&tree, node, buf, count, 
-                          filp->f_flags & O_NONBLOCK ? LDLM_FL_BLOCK_NOWAIT :0);
+        if (!lsm) {
+                /* Read on file with no objects should return zero-filled
+                 * buffers up to file size (we can get non-zero sizes with
+                 * mknod + truncate, then opening file for read. This is a
+                 * common pattern in NFS case, it seems). Bug 6243 */
+                int notzeroed;
+                /* Since there are no objects on OSTs, we have nothing to get
+                 * lock on and so we are forced to access inode->i_size
+                 * unguarded */
+
+                /* Read beyond end of file */
+                if (*ppos >= inode->i_size)
+                        RETURN(0);
+
+                if (count > inode->i_size - *ppos)
+                        count = inode->i_size - *ppos;
+                /* Make sure to correctly adjust the file pos pointer for
+                 * EFAULT case */
+                notzeroed = clear_user(buf, count);
+                count -= notzeroed;
+                *ppos += count;
+                if (!count)
+                        RETURN(-EFAULT);
+                RETURN(count);
+        }
+
+        node = ll_node_from_inode(inode, *ppos, *ppos + count - 1, LCK_PR);
+        tree.lt_fd = LUSTRE_FPRIVATE(file);
+        rc = ll_tree_lock(&tree, node, buf, count,
+                          file->f_flags & O_NONBLOCK ? LDLM_FL_BLOCK_NOWAIT :0);
         if (rc != 0)
                 RETURN(rc);
 
@@ -805,11 +831,11 @@ static ssize_t ll_file_read(struct file *filp, char *buf, size_t count,
 
         /* turn off the kernel's read-ahead */
 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
-        filp->f_ramax = 0;
+        file->f_ramax = 0;
 #else
-        filp->f_ra.ra_pages = 0;
+        file->f_ra.ra_pages = 0;
 #endif
-        retval = generic_file_read(filp, buf, count, ppos);
+        retval = generic_file_read(file, buf, count, ppos);
 
  out:
         ll_tree_unlock(&tree);
@@ -823,7 +849,6 @@ static ssize_t ll_file_write(struct file *file, const char *buf, size_t count,
                              loff_t *ppos)
 {
         struct inode *inode = file->f_dentry->d_inode;
-        struct lov_stripe_md *lsm = ll_i2info(inode)->lli_smd;
         struct ll_lock_tree tree;
         struct ll_lock_tree_node *node;
         loff_t maxbytes = ll_file_maxbytes(inode);
@@ -841,21 +866,23 @@ static ssize_t ll_file_write(struct file *file, const char *buf, size_t count,
 
         /* If file was opened for LL_IOC_LOV_SETSTRIPE but the ioctl wasn't
          * called on the file, don't fail the below assertion (bug 2388). */
-        if (file->f_flags & O_LOV_DELAY_CREATE && lsm == NULL)
+        if (file->f_flags & O_LOV_DELAY_CREATE && 
+            ll_i2info(inode)->lli_smd == NULL)
                 RETURN(-EBADF);
 
-        LASSERT(lsm);
-        
+        LASSERT(ll_i2info(inode)->lli_smd != NULL);
+
         if (file->f_flags & O_APPEND)
                 node = ll_node_from_inode(inode, 0, OBD_OBJECT_EOF, LCK_PW);
         else
                 node = ll_node_from_inode(inode, *ppos, *ppos  + count - 1, 
                                           LCK_PW);
+
         if (IS_ERR(node))
                 RETURN(PTR_ERR(node));
-        
-        tree.lt_fd = file->private_data;
-        rc = ll_tree_lock(&tree, node, buf, count, 
+
+        tree.lt_fd = LUSTRE_FPRIVATE(file);
+        rc = ll_tree_lock(&tree, node, buf, count,
                           file->f_flags & O_NONBLOCK ? LDLM_FL_BLOCK_NOWAIT :0);
         if (rc != 0)
                 RETURN(rc);
@@ -1049,6 +1076,11 @@ static int ll_lov_setstripe(struct inode *inode, struct file *file,
                 RETURN(-EFAULT);
 
         rc = ll_lov_setstripe_ea_info(inode, file, flags, &lum, sizeof(lum));
+        if (rc == 0) {
+                 put_user(0, &lump->lmm_stripe_count);
+                 rc = obd_iocontrol(LL_IOC_LOV_GETSTRIPE, ll_i2obdexp(inode),
+                                    0, ll_i2info(inode)->lli_smd, lump);
+        }
         RETURN(rc);
 }
 
@@ -1063,17 +1095,78 @@ static int ll_lov_getstripe(struct inode *inode, unsigned long arg)
                             (void *)arg);
 }
 
+static int ll_get_grouplock(struct inode *inode, struct file *file,
+                         unsigned long arg)
+{
+        struct ll_file_data *fd = file->private_data;
+        ldlm_policy_data_t policy = { .l_extent = { .start = 0,
+                                                    .end = OBD_OBJECT_EOF}};
+        struct lustre_handle lockh = { 0 };
+        struct ll_inode_info *lli = ll_i2info(inode);
+        struct lov_stripe_md *lsm = lli->lli_smd;
+        int flags = 0, rc;
+        ENTRY;
+
+        if (fd->fd_flags & LL_FILE_GROUP_LOCKED) {
+                RETURN(-EINVAL);
+        }
+
+        policy.l_extent.gid = arg;
+        if (file->f_flags & O_NONBLOCK)
+                flags = LDLM_FL_BLOCK_NOWAIT;
+
+        rc = ll_extent_lock(fd, inode, lsm, LCK_GROUP, &policy, &lockh, flags);
+        if (rc)
+                RETURN(rc);
+
+        fd->fd_flags |= LL_FILE_GROUP_LOCKED|LL_FILE_IGNORE_LOCK;
+        fd->fd_gid = arg;
+        memcpy(&fd->fd_cwlockh, &lockh, sizeof(lockh));
+
+        RETURN(0);
+}
+
+static int ll_put_grouplock(struct inode *inode, struct file *file,
+                         unsigned long arg)
+{
+        struct ll_file_data *fd = file->private_data;
+        struct ll_inode_info *lli = ll_i2info(inode);
+        struct lov_stripe_md *lsm = lli->lli_smd;
+        int rc;
+        ENTRY;
+
+        if (!(fd->fd_flags & LL_FILE_GROUP_LOCKED)) {
+                /* Ugh, it's already unlocked. */
+                RETURN(-EINVAL);
+        }
+
+        if (fd->fd_gid != arg) /* Ugh? Unlocking with different gid? */
+                RETURN(-EINVAL);
+        
+        fd->fd_flags &= ~(LL_FILE_GROUP_LOCKED|LL_FILE_IGNORE_LOCK);
+
+        rc = ll_extent_unlock(fd, inode, lsm, LCK_GROUP, &fd->fd_cwlockh);
+        if (rc)
+                RETURN(rc);
+
+        fd->fd_gid = 0;
+        memset(&fd->fd_cwlockh, 0, sizeof(fd->fd_cwlockh));
+
+        RETURN(0);
+}       
+
 int ll_file_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
                   unsigned long arg)
 {
-        struct ll_file_data *fd = file->private_data;
+        struct ll_file_data *fd = LUSTRE_FPRIVATE(file);
         int flags;
         ENTRY;
 
         CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%u(%p),cmd=%x\n", inode->i_ino,
                inode->i_generation, inode, cmd);
 
-        if (_IOC_TYPE(cmd) == 'T') /* tty ioctls */
+        /* asm-ppc{,64} declares TCGETS, et. al. as type 't' not 'T' */
+        if (_IOC_TYPE(cmd) == 'T' || _IOC_TYPE(cmd) == 't') /* tty ioctls */
                 RETURN(-ENOTTY);
 
         lprocfs_counter_incr(ll_i2sbi(inode)->ll_stats, LPROC_LL_IOCTL);
@@ -1109,6 +1202,11 @@ int ll_file_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
         case EXT3_IOC_GETVERSION_OLD:
         case EXT3_IOC_GETVERSION:
                 RETURN(put_user(inode->i_generation, (int *) arg));
+        case LL_IOC_GROUP_LOCK:
+                RETURN(ll_get_grouplock(inode, file, arg));
+        case LL_IOC_GROUP_UNLOCK:
+                RETURN(ll_put_grouplock(inode, file, arg));
+
         /* We need to special case any other ioctls we want to handle,
          * to send them to the MDS/OST as appropriate and to properly
          * network encode the arg field.
@@ -1124,24 +1222,28 @@ int ll_file_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
 loff_t ll_file_seek(struct file *file, loff_t offset, int origin)
 {
         struct inode *inode = file->f_dentry->d_inode;
-        struct ll_file_data *fd = file->private_data;
+        struct ll_file_data *fd = LUSTRE_FPRIVATE(file);
         struct lov_stripe_md *lsm = ll_i2info(inode)->lli_smd;
         struct lustre_handle lockh = {0};
         loff_t retval;
         ENTRY;
-        CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%u(%p), to=%llu(%s)\n",
-               inode->i_ino, inode->i_generation, inode,
-               offset + ((origin == 2) ? inode->i_size :
-                         (origin == 1) ? file->f_pos : 0),
+        retval = offset + ((origin == 2) ? inode->i_size :
+                           (origin == 1) ? file->f_pos : 0);
+        CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%u(%p), to=%Lu=%#Lx(%s)\n",
+               inode->i_ino, inode->i_generation, inode, retval, retval,
                origin == 2 ? "SEEK_END": origin == 1 ? "SEEK_CUR" : "SEEK_SET");
 
         lprocfs_counter_incr(ll_i2sbi(inode)->ll_stats, LPROC_LL_LLSEEK);
         if (origin == 2) { /* SEEK_END */
                 ldlm_policy_data_t policy = { .l_extent = {0, OBD_OBJECT_EOF }};
                 struct ll_inode_info *lli = ll_i2info(inode);
-                int rc;
+                int nonblock = 0, rc;
+
+                if (file->f_flags & O_NONBLOCK)
+                        nonblock = LDLM_FL_BLOCK_NOWAIT;
 
-                rc = ll_extent_lock(fd, inode, lsm, LCK_PR, &policy, &lockh,0);
+                rc = ll_extent_lock(fd, inode, lsm, LCK_PR, &policy, &lockh,
+                                    nonblock);
                 if (rc != 0)
                         RETURN(rc);
 
@@ -1303,8 +1405,7 @@ int ll_file_flock(struct file *file, int cmd, struct file_lock *file_lock)
                flags, mode, flock.l_flock.start, flock.l_flock.end);
 
         obddev = sbi->ll_mdc_exp->exp_obd;
-        rc = ldlm_cli_enqueue(obddev->obd_self_export, NULL,
-                              obddev->obd_namespace,
+        rc = ldlm_cli_enqueue(sbi->ll_mdc_exp, NULL, obddev->obd_namespace,
                               res_id, LDLM_FLOCK, &flock, mode, &flags,
                               NULL, ldlm_flock_completion_ast, NULL, file_lock,
                               NULL, 0, NULL, &lockh);
@@ -1368,7 +1469,7 @@ int ll_inode_revalidate_it(struct dentry *dentry, struct lookup_intent *it)
                 struct ptlrpc_request *req = NULL;
                 struct ll_sb_info *sbi = ll_i2sbi(dentry->d_inode);
                 struct ll_fid fid;
-                unsigned long valid = 0;
+                unsigned long valid = OBD_MD_FLGETATTR;
                 int ealen = 0;
 
                 if (S_ISREG(inode->i_mode)) {
index 7e19c00..dab0979 100644 (file)
@@ -6,6 +6,7 @@
 #define LLITE_INTERNAL_H
 
 #include <linux/lustre_debug.h>
+#include <linux/lustre_version.h>
 
 /*
 struct lustre_intent_data {
@@ -17,6 +18,13 @@ struct lustre_intent_data {
 
 #define LL_IT2STR(it) ((it) ? ldlm_it2str((it)->it_op) : "0")
 
+#if (LUSTRE_KERNEL_VERSION < 46)
+#define LUSTRE_FPRIVATE(file) ((file)->private_data)
+#else
+#define LUSTRE_FPRIVATE(file) ((file)->fs_private)
+#endif
+
+
 static inline struct lookup_intent *ll_nd2it(struct nameidata *nd)
 {
 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0))
@@ -41,6 +49,7 @@ extern struct file_operations ll_pgcache_seq_fops;
 #define LLI_INODE_DEAD                  0xdeadd00d
 #define LLI_F_HAVE_OST_SIZE_LOCK        0
 #define LLI_F_HAVE_MDS_SIZE_LOCK        1
+
 struct ll_inode_info {
         int                     lli_inode_magic;
         int                     lli_size_pid;
@@ -83,7 +92,6 @@ static inline struct ll_inode_info *ll_i2info(struct inode *inode)
 #endif
 }
 
-
 /* default to about 40meg of readahead on a given system.  That much tied
  * up in 512k readahead requests serviced at 40ms each is about 1GB/s. */
 #define SBI_DEFAULT_READAHEAD_MAX (40UL << (20 - PAGE_CACHE_SHIFT))
@@ -92,6 +100,7 @@ enum ra_stat {
         RA_STAT_MISS,
         RA_STAT_DISTANT_READPAGE,
         RA_STAT_MISS_IN_WINDOW,
+        RA_STAT_FAILED_GRAB_PAGE,
         RA_STAT_FAILED_MATCH,
         RA_STAT_DISCARDED,
         RA_STAT_ZERO_LEN,
@@ -107,6 +116,10 @@ struct ll_ra_info {
         unsigned long             ra_stats[_NR_RA_STAT];
 };
 
+/* flags for sbi->ll_flags */
+#define LL_SBI_NOLCK            0x1 /* DLM locking disabled (directio-only) */
+#define LL_SBI_CHECKSUM         0x2 /* checksum each page as it's written */
+
 struct ll_sb_info {
         struct list_head          ll_list;
         /* this protects pglist and ra_info.  It isn't safe to
@@ -119,7 +132,6 @@ struct ll_sb_info {
         obd_id                    ll_rootino; /* number of root inode */
 
         struct lustre_mount_data *ll_lmd;
-        char                     *ll_instance;
 
         int                       ll_flags;
         struct list_head          ll_conn_chain; /* per-conn chain of SBs */
@@ -152,13 +164,14 @@ struct ll_file_data {
         struct obd_client_handle fd_mds_och;
         struct ll_readahead_state fd_ras;
         __u32 fd_flags;
+        struct lustre_handle fd_cwlockh;
+        unsigned long fd_gid;
 };
 
 struct lov_stripe_md;
 
 extern spinlock_t inode_lock;
 
-extern void lprocfs_unregister_mountpoint(struct ll_sb_info *sbi);
 extern struct proc_dir_entry *proc_lustre_fs_root;
 
 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
@@ -206,17 +219,19 @@ extern kmem_cache_t *ll_async_page_slab;
 extern size_t ll_async_page_slab_size;
 struct ll_async_page {
         int              llap_magic;
-        void            *llap_cookie;
-        struct page     *llap_page;
-        struct list_head llap_pending_write;
          /* only trust these if the page lock is providing exclusion */
         unsigned int     llap_write_queued:1,
                          llap_defer_uptodate:1,
                          llap_origin:3,
                          llap_ra_used:1;
+        void            *llap_cookie;
+        struct page     *llap_page;
+        struct list_head llap_pending_write;
         struct list_head llap_pglist_item;
         /* user credit information for oss enforcement quota */
-        struct obd_ucred llap_ouc;
+        struct lvfs_ucred llap_ouc;
+        /* checksum for paranoid I/O debugging */
+        __u32 llap_checksum;
 };
 
 enum {
@@ -238,9 +253,16 @@ extern char *llap_origins[];
 #endif
 
 /* llite/lproc_llite.c */
+#ifdef LPROCFS
 int lprocfs_register_mountpoint(struct proc_dir_entry *parent,
                                 struct super_block *sb, char *osc, char *mdc);
 void lprocfs_unregister_mountpoint(struct ll_sb_info *sbi);
+#else
+static inline int lprocfs_register_mountpoint(struct proc_dir_entry *parent,
+                        struct super_block *sb, char *osc, char *mdc){return 0;}
+static inline void lprocfs_unregister_mountpoint(struct ll_sb_info *sbi) {}
+#endif
+
 
 /* llite/dir.c */
 extern struct file_operations ll_dir_operations;
@@ -395,8 +417,6 @@ int ll_tree_lock(struct ll_lock_tree *tree,
 int ll_tree_unlock(struct ll_lock_tree *tree);
 
 
-#define LL_SBI_NOLCK            0x1
-
 #define LL_MAX_BLKSIZE          (4UL * 1024 * 1024)
 
 #if  (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0))
index cdc2d84..a55f822 100644 (file)
@@ -230,7 +230,7 @@ int lustre_common_fill_super(struct super_block *sb, char *mdc, char *osc)
         /* make root inode
          * XXX: move this to after cbd setup? */
         err = mdc_getattr(sbi->ll_mdc_exp, &rootfid,
-                          OBD_MD_FLNOTOBD|OBD_MD_FLBLOCKS, 0, &request);
+                          OBD_MD_FLGETATTR | OBD_MD_FLBLOCKS, 0, &request);
         if (err) {
                 CERROR("mdc_getattr failed for root: rc = %d\n", err);
                 GOTO(out_osc, err);
@@ -249,7 +249,8 @@ int lustre_common_fill_super(struct super_block *sb, char *mdc, char *osc)
         ptlrpc_req_finished(request);
 
         if (root == NULL || is_bad_inode(root)) {
-                /* XXX might need iput() for bad inode */
+                if (md.lsm != NULL)
+                        obd_free_memmd(sbi->ll_osc_exp, &md.lsm);
                 CERROR("lustre_lite: bad iget4 for root\n");
                 GOTO(out_root, err = -EBADF);
         }
@@ -314,11 +315,11 @@ void lustre_dump_dentry(struct dentry *dentry, int recur)
                 subdirs++;
 
         CERROR("dentry %p dump: name=%.*s parent=%.*s (%p), inode=%p, count=%u,"
-               " flags=0x%x, vfs_flags=0x%x, fsdata=%p, %d subdirs\n", dentry,
+               " flags=0x%x, fsdata=%p, %d subdirs\n", dentry,
                dentry->d_name.len, dentry->d_name.name,
                dentry->d_parent->d_name.len, dentry->d_parent->d_name.name,
                dentry->d_parent, dentry->d_inode, atomic_read(&dentry->d_count),
-               dentry->d_flags, dentry->d_flags, dentry->d_fsdata, subdirs);
+               dentry->d_flags, dentry->d_fsdata, subdirs);
         if (dentry->d_inode != NULL)
                 ll_dump_inode(dentry->d_inode);
 
@@ -667,7 +668,7 @@ int lustre_fill_super(struct super_block *sb, void *data, int silent)
         if (lmd->lmd_profile) {
                 struct lustre_profile *lprof;
                 struct config_llog_instance cfg;
-                int len;
+                char ll_instance[sizeof(sb) * 2 + 1];
 
                 if (lmd->lmd_mds[0] == '\0') {
                         CERROR("no mds name\n");
@@ -681,13 +682,9 @@ int lustre_fill_super(struct super_block *sb, void *data, int silent)
 
                 /* generate a string unique to this super, let's try
                  the address of the super itself.*/
-                len = (sizeof(sb) * 2) + 1;
-                OBD_ALLOC(sbi->ll_instance, len);
-                if (sbi->ll_instance == NULL)
-                        GOTO(out_free, err = -ENOMEM);
-                sprintf(sbi->ll_instance, "%p", sb);
+                sprintf(ll_instance, "%p", sb);
 
-                cfg.cfg_instance = sbi->ll_instance;
+                cfg.cfg_instance = ll_instance;
                 cfg.cfg_uuid = sbi->ll_sb_uuid;
                 err = lustre_process_log(lmd, lmd->lmd_profile, &cfg, 0);
                 if (err < 0) {
@@ -704,14 +701,14 @@ int lustre_fill_super(struct super_block *sb, void *data, int silent)
                 if (osc)
                         OBD_FREE(osc, strlen(osc) + 1);
                 OBD_ALLOC(osc, strlen(lprof->lp_osc) +
-                          strlen(sbi->ll_instance) + 2);
-                sprintf(osc, "%s-%s", lprof->lp_osc, sbi->ll_instance);
+                          strlen(ll_instance) + 2);
+                sprintf(osc, "%s-%s", lprof->lp_osc, ll_instance);
 
                 if (mdc)
                         OBD_FREE(mdc, strlen(mdc) + 1);
                 OBD_ALLOC(mdc, strlen(lprof->lp_mdc) +
-                          strlen(sbi->ll_instance) + 2);
-                sprintf(mdc, "%s-%s", lprof->lp_mdc, sbi->ll_instance);
+                          strlen(ll_instance) + 2);
+                sprintf(mdc, "%s-%s", lprof->lp_mdc, ll_instance);
         }
 
         if (!osc) {
@@ -739,27 +736,7 @@ out_dev:
 
 out_free:
         if (sbi->ll_lmd) {
-                int len = strlen(sbi->ll_lmd->lmd_profile) + sizeof("-clean")+1;
-                int err;
-
-                if (sbi->ll_instance != NULL) {
-                        char * cln_prof;
-                        struct config_llog_instance cfg;
-
-                        cfg.cfg_instance = sbi->ll_instance;
-                        cfg.cfg_uuid = sbi->ll_sb_uuid;
-
-                        OBD_ALLOC(cln_prof, len);
-                        sprintf(cln_prof, "%s-clean", sbi->ll_lmd->lmd_profile);
-
-                        err = lustre_process_log(sbi->ll_lmd, cln_prof, &cfg,0);
-                        if (err < 0) {
-                                CERROR("Unable to process log: %s\n", cln_prof);
-                                lustre_manual_cleanup(sbi);
-                        }
-                        OBD_FREE(cln_prof, len);
-                        OBD_FREE(sbi->ll_instance, strlen(sbi->ll_instance)+ 1);
-                }
+                lustre_manual_cleanup(sbi);
                 OBD_FREE(sbi->ll_lmd, sizeof(*sbi->ll_lmd));
         }
         lustre_free_sbi(sb);
@@ -783,38 +760,8 @@ void lustre_put_super(struct super_block *sb)
         lustre_common_put_super(sb);
 
         if (sbi->ll_lmd != NULL) {
-#if 0
-                char * cln_prof;
-                int len = strlen(sbi->ll_lmd->lmd_profile) + sizeof("-clean")+1;
-                int err;
-                struct config_llog_instance cfg;
-
-                if (force_umount) {
-                        CERROR("force umount, doing manual cleanup\n");
-                        lustre_manual_cleanup(sbi);
-                        GOTO(free_lmd, 0);
-                }
-
-                cfg.cfg_instance = sbi->ll_instance;
-                cfg.cfg_uuid = sbi->ll_sb_uuid;
-
-                OBD_ALLOC(cln_prof, len);
-                sprintf(cln_prof, "%s-clean", sbi->ll_lmd->lmd_profile);
-
-                err = lustre_process_log(sbi->ll_lmd, cln_prof, &cfg, 0);
-                if (err < 0) {
-                        CERROR("Unable to process log: %s, doing manual cleanup"
-                               "\n", cln_prof);
-                        lustre_manual_cleanup(sbi);
-                }
-
-                OBD_FREE(cln_prof, len);
-        free_lmd:
-#else
                 lustre_manual_cleanup(sbi);
-#endif
                 OBD_FREE(sbi->ll_lmd, sizeof(*sbi->ll_lmd));
-                OBD_FREE(sbi->ll_instance, strlen(sbi->ll_instance) + 1);
         }
 
         lustre_free_sbi(sb);
@@ -975,6 +922,10 @@ int ll_setattr_raw(struct inode *inode, struct iattr *attr)
                 CDEBUG(D_INODE, "setting mtime %lu, ctime %lu, now = %lu\n",
                        LTIME_S(attr->ia_mtime), LTIME_S(attr->ia_ctime),
                        CURRENT_SECONDS);
+
+
+        /* NB: ATTR_SIZE will only be set after this point if the size
+         * resides on the MDS, ie, this file has no objects. */
         if (lsm)
                 attr->ia_valid &= ~ATTR_SIZE;
 
@@ -1001,12 +952,11 @@ int ll_setattr_raw(struct inode *inode, struct iattr *attr)
                         RETURN(rc);
                 }
 
-                /* We call inode_setattr to adjust timestamps, but we first
-                 * clear ATTR_SIZE to avoid invoking vmtruncate.
-                 *
-                 * NB: ATTR_SIZE will only be set at this point if the size
-                 * resides on the MDS, ie, this file has no objects. */
-                attr->ia_valid &= ~ATTR_SIZE;
+                /* We call inode_setattr to adjust timestamps.
+                 * If there is at least some data in file, we cleared ATTR_SIZE
+                 * above to avoid invoking vmtruncate, otherwise it is important
+                 * to call vmtruncate in inode_setattr to update inode->i_size
+                 * (bug 6196) */
                 inode_setattr(inode, attr);
 
                 ll_update_inode(inode, md.body, md.lsm);
@@ -1057,8 +1007,17 @@ int ll_setattr_raw(struct inode *inode, struct iattr *attr)
                 if (attr->ia_size == 0)
                         ast_flags = LDLM_AST_DISCARD_DATA;
 
+                up(&inode->i_sem);
+                UP_WRITE_I_ALLOC_SEM(inode);
                 rc = ll_extent_lock(NULL, inode, lsm, LCK_PW, &policy, &lockh,
                                     ast_flags);
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
+                DOWN_WRITE_I_ALLOC_SEM(inode);
+                down(&inode->i_sem);
+#else
+                down(&inode->i_sem);
+                DOWN_WRITE_I_ALLOC_SEM(inode);
+#endif
                 if (rc != 0)
                         RETURN(rc);
 
@@ -1208,6 +1167,9 @@ void ll_update_inode(struct inode *inode, struct mds_body *body,
                 inode->i_blksize = min(lsm->lsm_xfersize, LL_MAX_BLKSIZE);
                 if (lli->lli_smd != lsm)
                         obd_free_memmd(ll_i2obdexp(inode), &lsm);
+        } else {
+                inode->i_blksize = max(inode->i_blksize,
+                                       inode->i_sb->s_blocksize);
         }
 
         if (body->valid & OBD_MD_FLID)
@@ -1334,11 +1296,10 @@ int ll_iocontrol(struct inode *inode, struct file *file,
         switch(cmd) {
         case EXT3_IOC_GETFLAGS: {
                 struct ll_fid fid;
-                unsigned long valid = OBD_MD_FLFLAGS;
                 struct mds_body *body;
 
                 ll_inode2fid(&fid, inode);
-                rc = mdc_getattr(sbi->ll_mdc_exp, &fid, valid, 0, &req);
+                rc = mdc_getattr(sbi->ll_mdc_exp, &fid, OBD_MD_FLFLAGS,0,&req);
                 if (rc) {
                         CERROR("failure %d inode %lu\n", rc, inode->i_ino);
                         RETURN(-abs(rc));
@@ -1427,7 +1388,8 @@ void ll_umount_begin(struct super_block *sb)
         struct obd_device *obd;
         struct obd_ioctl_data ioc_data = { 0 };
         ENTRY;
-        CDEBUG(D_VFSTRACE, "VFS Op:\n");
+        CDEBUG(D_VFSTRACE, "VFS Op: superblock %p count %d active %d\n", sb,
+               sb->s_count, atomic_read(&sb->s_active));
 
         obd = class_exp2obd(sbi->ll_mdc_exp);
         if (obd == NULL) {
@@ -1537,4 +1499,3 @@ struct ll_async_page *llite_pglist_next_llap(struct ll_sb_info *sbi,
         LBUG();
         return NULL;
 }
-
index 995a7de..4be05bf 100644 (file)
@@ -42,7 +42,6 @@
 #include <linux/iobuf.h>
 #endif
 
-
 #define DEBUG_SUBSYSTEM S_LLITE
 
 #include <linux/lustre_mds.h>
@@ -68,7 +67,6 @@ struct ll_lock_tree_node {
         struct inode           *lt_inode;
 };
 
-__u64 lov_merge_size(struct lov_stripe_md *lsm, int kms);
 int lt_get_mmap_locks(struct ll_lock_tree *tree,
                       unsigned long addr, size_t count);
 
@@ -362,7 +360,7 @@ struct page *ll_nopage(struct vm_area_struct *vma, unsigned long address,
 #endif
 {
         struct file *filp = vma->vm_file;
-        struct ll_file_data *fd = filp->private_data;
+        struct ll_file_data *fd = LUSTRE_FPRIVATE(filp);
         struct inode *inode = filp->f_dentry->d_inode;
         struct lustre_handle lockh = { 0 };
         ldlm_policy_data_t policy;
index fed41c3..54cb257 100644 (file)
 
 #include "llite_internal.h"
 
-/* /proc/lustre/llite mount point registration */
 struct proc_dir_entry *proc_lustre_fs_root;
+
+#ifdef LPROCFS
+/* /proc/lustre/llite mount point registration */
 struct file_operations llite_dump_pgcache_fops;
 struct file_operations ll_ra_stats_fops;
 
-#ifndef LPROCFS
-int lprocfs_register_mountpoint(struct proc_dir_entry *parent,
-                                struct super_block *sb, char *osc, char *mdc)
-{
-        return 0;
-}
-void lprocfs_unregister_mountpoint(struct ll_sb_info *sbi){}
-#else
-
 long long mnt_instance;
 
 static int ll_rd_blksize(char *page, char **start, off_t off, int count,
@@ -261,6 +254,40 @@ static int ll_wr_max_cached_mb(struct file *file, const char *buffer,
         return count;
 }
 
+static int ll_rd_checksum(char *page, char **start, off_t off,
+                          int count, int *eof, void *data)
+{
+        struct super_block *sb = data;
+        struct ll_sb_info *sbi = ll_s2sbi(sb);
+
+        return snprintf(page, count, "%u\n",
+                        (sbi->ll_flags & LL_SBI_CHECKSUM) ? 1 : 0);
+}
+
+static int ll_wr_checksum(struct file *file, const char *buffer,
+                          unsigned long count, void *data)
+{
+        struct super_block *sb = data;
+        struct ll_sb_info *sbi = ll_s2sbi(sb);
+        int val, rc;
+
+        rc = lprocfs_write_helper(buffer, count, &val);
+        if (rc)
+                return rc;
+
+        if (val)
+                sbi->ll_flags |= LL_SBI_CHECKSUM;
+        else
+                sbi->ll_flags &= ~LL_SBI_CHECKSUM;
+
+        rc = obd_set_info(sbi->ll_osc_exp, strlen("checksum"), "checksum",
+                          sizeof(val), &val);
+        if (rc)
+                CWARN("Failed to set OSC checksum flags: %d\n", rc);
+
+        return count;
+}
+
 static struct lprocfs_vars lprocfs_obd_vars[] = {
         { "uuid",         ll_rd_sb_uuid,          0, 0 },
         //{ "mntpt_path",   ll_rd_path,             0, 0 },
@@ -275,6 +302,7 @@ static struct lprocfs_vars lprocfs_obd_vars[] = {
         { "max_read_ahead_mb", ll_rd_max_readahead_mb,
                                ll_wr_max_readahead_mb, 0 },
         { "max_cached_mb", ll_rd_max_cached_mb, ll_wr_max_cached_mb, 0 },
+        { "checksum_pages", ll_rd_checksum, ll_wr_checksum, 0 },
         { 0 }
 };
 
@@ -655,6 +683,7 @@ static int ll_ra_stats_seq_show(struct seq_file *seq, void *v)
                 [RA_STAT_MISS] = "misses",
                 [RA_STAT_DISTANT_READPAGE] = "readpage not consecutive",
                 [RA_STAT_MISS_IN_WINDOW] = "miss inside window",
+                [RA_STAT_FAILED_GRAB_PAGE] = "failed grab_cache_page",
                 [RA_STAT_FAILED_MATCH] = "failed lock match",
                 [RA_STAT_DISCARDED] = "read but discarded",
                 [RA_STAT_ZERO_LEN] = "zero length file",
index 430eb17..48a166b 100644 (file)
@@ -75,7 +75,8 @@ static int ll_test_inode(struct inode *inode, void *opaque)
                 return 0;
 
         /* Apply the attributes in 'opaque' to this inode */
-        ll_update_inode(inode, md->body, md->lsm);
+        if (!(inode->i_state & (I_FREEING | I_CLEAR)))
+                ll_update_inode(inode, md->body, md->lsm);
         return 1;
 }
 
@@ -372,6 +373,8 @@ static struct dentry *ll_lookup_it(struct inode *parent, struct dentry *dentry,
         ll_inode2fid(&pfid, parent);
         ll_i2uctxt(&ctxt, parent, NULL);
 
+        it->it_create_mode &= ~current->fs->umask;
+
         rc = mdc_intent_lock(ll_i2mdcexp(parent), &ctxt, &pfid,
                              dentry->d_name.name, dentry->d_name.len, NULL, 0,
                              NULL, it, lookup_flags, &req, ll_mdc_blocking_ast);
@@ -684,11 +687,22 @@ static int ll_rmdir_raw(struct nameidata *nd)
         struct inode *dir = nd->dentry->d_inode;
         struct ptlrpc_request *request = NULL;
         struct mdc_op_data op_data;
+        struct dentry *dentry;
         int rc;
         ENTRY;
         CDEBUG(D_VFSTRACE, "VFS Op:name=%.*s,dir=%lu/%u(%p)\n",
                nd->last.len, nd->last.name, dir->i_ino, dir->i_generation, dir);
 
+        /* Check if we have something mounted at the dir we are going to delete
+         * In such a case there would always be dentry present. */
+        dentry = d_lookup(nd->dentry, &nd->last);
+        if (dentry) {
+                int mounted = d_mountpoint(dentry);
+                dput(dentry);
+                if (mounted)
+                        RETURN(-EBUSY);
+        }
+                
         ll_prepare_mdc_op_data(&op_data, dir, NULL, nd->last.name,
                                nd->last.len, S_IFDIR);
         rc = mdc_unlink(ll_i2sbi(dir)->ll_mdc_exp, &op_data, &request);
index 6ca5b8c..4a59682 100644 (file)
@@ -101,8 +101,6 @@ static int ll_brw(int cmd, struct inode *inode, struct obdo *oa,
         RETURN(rc);
 }
 
-__u64 lov_merge_size(struct lov_stripe_md *lsm, int kms);
-
 /* this isn't where truncate starts.   roughly:
  * sys_truncate->ll_setattr_raw->vmtruncate->ll_truncate
  * we grab the lock back in setattr_raw to avoid races.
@@ -115,8 +113,8 @@ void ll_truncate(struct inode *inode)
         struct obdo oa;
         int rc;
         ENTRY;
-        CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%u(%p) to %llu\n", inode->i_ino,
-               inode->i_generation, inode, inode->i_size);
+        CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%u(%p) to %Lu=%#Lx\n",inode->i_ino,
+               inode->i_generation, inode, inode->i_size, inode->i_size);
 
         if (lli->lli_size_pid != current->pid) {
                 EXIT;
@@ -131,14 +129,33 @@ void ll_truncate(struct inode *inode)
 
         LASSERT(atomic_read(&lli->lli_size_sem.count) <= 0);
 
+        /* XXX I'm pretty sure this is a hack to paper over a more fundamental
+         * race condition. */
         if (lov_merge_size(lsm, 0) == inode->i_size) {
-                CDEBUG(D_VFSTRACE, "skipping punch for "LPX64" (size = %llu)\n",
-                       lsm->lsm_object_id, inode->i_size);
+                CDEBUG(D_VFSTRACE, "skipping punch for obj "LPX64", %Lu=%#Lx\n",
+                       lsm->lsm_object_id, inode->i_size, inode->i_size);
                 GOTO(out_unlock, 0);
         }
 
-        CDEBUG(D_INFO, "calling punch for "LPX64" (new size %llu)\n",
-               lsm->lsm_object_id, inode->i_size);
+        if (unlikely((ll_i2sbi(inode)->ll_flags & LL_SBI_CHECKSUM) &&
+                     (inode->i_size & ~PAGE_MASK))) {
+                /* If the truncate leaves behind a partial page, update its
+                 * checksum. */
+                struct page *page = find_get_page(inode->i_mapping,
+                                             inode->i_size >> PAGE_CACHE_SHIFT);
+                if (page != NULL) {
+                        struct ll_async_page *llap = llap_cast_private(page);
+                        if (llap != NULL) {
+                                llap->llap_checksum =
+                                        crc32_le(0, kmap(page), PAGE_SIZE);
+                                kunmap(page);
+                        }
+                        page_cache_release(page);
+                }
+        }
+
+        CDEBUG(D_INFO, "calling punch for "LPX64" (new size %Lu=%#Lx)\n",
+               lsm->lsm_object_id, inode->i_size, inode->i_size);
 
         oa.o_id = lsm->lsm_object_id;
         oa.o_valid = OBD_MD_FLID;
@@ -166,7 +183,6 @@ void ll_truncate(struct inode *inode)
         up(&lli->lli_size_sem);
 } /* ll_truncate */
 
-__u64 lov_merge_size(struct lov_stripe_md *lsm, int kms);
 int ll_prepare_write(struct file *file, struct page *page, unsigned from,
                      unsigned to)
 {
@@ -241,24 +257,13 @@ int ll_prepare_write(struct file *file, struct page *page, unsigned from,
         return rc;
 }
 
-struct ll_async_page *llap_from_cookie(void *cookie)
-{
-        struct ll_async_page *llap = cookie;
-        if (llap->llap_magic != LLAP_MAGIC)
-                return ERR_PTR(-EINVAL);
-        return llap;
-};
-
 static int ll_ap_make_ready(void *data, int cmd)
 {
         struct ll_async_page *llap;
         struct page *page;
         ENTRY;
 
-        llap = llap_from_cookie(data);
-        if (IS_ERR(llap))
-                RETURN(-EINVAL);
-
+        llap = LLAP_FROM_COOKIE(data);
         page = llap->llap_page;
 
         LASSERT(cmd != OBD_BRW_READ);
@@ -308,10 +313,7 @@ static int ll_ap_refresh_count(void *data, int cmd)
         /* readpage queues with _COUNT_STABLE, shouldn't get here. */
         LASSERT(cmd != OBD_BRW_READ);
 
-        llap = llap_from_cookie(data);
-        if (IS_ERR(llap))
-                RETURN(PTR_ERR(llap));
-
+        llap = LLAP_FROM_COOKIE(data);
         page = llap->llap_page;
         lli = ll_i2info(page->mapping->host);
         lsm = lli->lli_smd;
@@ -345,6 +347,8 @@ void ll_inode_fill_obdo(struct inode *inode, int cmd, struct obdo *oa)
                 oa->o_valid |= OBD_MD_FLIFID | OBD_MD_FLEPOCH;
                 mdc_pack_fid(obdo_fid(oa), inode->i_ino, 0, inode->i_mode);
                 oa->o_easize = ll_i2info(inode)->lli_io_epoch;
+                oa->o_uid = inode->i_uid;
+                oa->o_gid = inode->i_gid;
 
                 valid_flags |= OBD_MD_FLMTIME | OBD_MD_FLCTIME |
                                OBD_MD_FLUID | OBD_MD_FLGID;
@@ -358,21 +362,16 @@ static void ll_ap_fill_obdo(void *data, int cmd, struct obdo *oa)
         struct ll_async_page *llap;
         ENTRY;
 
-        llap = llap_from_cookie(data);
-        if (IS_ERR(llap)) {
-                EXIT;
-                return;
-        }
-
+        llap = LLAP_FROM_COOKIE(data);
         ll_inode_fill_obdo(llap->llap_page->mapping->host, cmd, oa);
         EXIT;
 }
 
-static void ll_ap_get_ucred(void *data, struct obd_ucred *ouc)
+static void ll_ap_get_ucred(void *data, struct lvfs_ucred *ouc)
 {
         struct ll_async_page *llap;
 
-        llap = llap_from_cookie(data);
+        llap = LLAP_FROM_COOKIE(data);
         if (IS_ERR(llap)) {
                 EXIT;
                 return;
@@ -436,6 +435,7 @@ int llap_shrink_cache(struct ll_sb_info *sbi, int shrink_fraction)
 
         while (--total >= 0 && count < want) {
                 struct page *page;
+                int keep;
 
                 if (unlikely(need_resched())) {
                         spin_unlock(&sbi->ll_lock);
@@ -459,18 +459,24 @@ int llap_shrink_cache(struct ll_sb_info *sbi, int shrink_fraction)
                         continue;
                 }
 
-                /* If page is dirty or undergoing IO don't discard it */
                 if (llap->llap_write_queued || PageDirty(page) ||
                     (!PageUptodate(page) &&
-                     llap->llap_origin != LLAP_ORIGIN_READAHEAD)) {
+                     llap->llap_origin != LLAP_ORIGIN_READAHEAD))
+                        keep = 1;
+                else
+                        keep = 0;
+
+                LL_CDEBUG_PAGE(D_PAGE, page,"%s LRU page: %s%s%s%s origin %s\n",
+                               keep ? "keep" : "drop",
+                               llap->llap_write_queued ? "wq " : "",
+                               PageDirty(page) ? "pd " : "",
+                               PageUptodate(page) ? "" : "!pu ",
+                               llap->llap_defer_uptodate ? "" : "!du",
+                               llap_origins[llap->llap_origin]);
+
+                /* If page is dirty or undergoing IO don't discard it */
+                if (keep) {
                         unlock_page(page);
-                        LL_CDEBUG_PAGE(D_PAGE, page, "can't drop from cache: "
-                                       "%s%s%s%s origin %s\n",
-                                       llap->llap_write_queued ? "wq " : "",
-                                       PageDirty(page) ? "pd " : "",
-                                       PageUptodate(page) ? "" : "!pu ",
-                                       llap->llap_defer_uptodate ? "" : "!du",
-                                       llap_origins[llap->llap_origin]);
                         continue;
                 }
 
@@ -478,8 +484,6 @@ int llap_shrink_cache(struct ll_sb_info *sbi, int shrink_fraction)
                 spin_unlock(&sbi->ll_lock);
 
                 ++count;
-                LL_CDEBUG_PAGE(D_PAGE, page, "drop from cache %lu/%lu\n",
-                               count, want);
                 if (page->mapping != NULL) {
                         ll_ra_accounting(page, page->mapping);
                         ll_truncate_complete_page(page);
@@ -557,7 +561,29 @@ struct ll_async_page *llap_from_page(struct page *page, unsigned origin)
         list_add_tail(&llap->llap_pglist_item, &sbi->ll_pglist);
         spin_unlock(&sbi->ll_lock);
 
-out:
+ out:
+        if (unlikely(sbi->ll_flags & LL_SBI_CHECKSUM)) {
+                __u32 csum = 0;
+                csum = crc32_le(csum, kmap(page), PAGE_SIZE);
+                kunmap(page);
+                if (origin == LLAP_ORIGIN_READAHEAD ||
+                    origin == LLAP_ORIGIN_READPAGE) {
+                        llap->llap_checksum = 0;
+                } else if (origin == LLAP_ORIGIN_COMMIT_WRITE ||
+                           llap->llap_checksum == 0) {
+                        llap->llap_checksum = csum;
+                        CDEBUG(D_PAGE, "page %p cksum %x\n", page, csum);
+                } else if (llap->llap_checksum == csum) {
+                        /* origin == LLAP_ORIGIN_WRITEPAGE */
+                        CDEBUG(D_PAGE, "page %p cksum %x confirmed\n",
+                               page, csum);
+                } else {
+                        /* origin == LLAP_ORIGIN_WRITEPAGE */
+                        LL_CDEBUG_PAGE(D_ERROR, page, "old cksum %x != new "
+                                       "%x!\n", llap->llap_checksum, csum);
+                }
+        }
+
         llap->llap_origin = origin;
         RETURN(llap);
 }
@@ -568,6 +594,7 @@ static int queue_or_sync_write(struct obd_export *exp, struct inode *inode,
 {
         unsigned long size_index = inode->i_size >> PAGE_SHIFT;
         struct obd_io_group *oig;
+        struct ll_sb_info *sbi = ll_i2sbi(inode);
         int rc;
         ENTRY;
 
@@ -603,6 +630,22 @@ static int queue_or_sync_write(struct obd_export *exp, struct inode *inode,
                         to = size_to;
         }
 
+        /* compare the checksum once before the page leaves llite */
+        if (unlikely((sbi->ll_flags & LL_SBI_CHECKSUM) &&
+                     llap->llap_checksum != 0)) {
+                __u32 csum = 0;
+                struct page *page = llap->llap_page;
+                csum = crc32_le(csum, kmap(page), PAGE_SIZE);
+                kunmap(page);
+                if (llap->llap_checksum == csum) {
+                        CDEBUG(D_PAGE, "page %p cksum %x confirmed\n",
+                               page, csum);
+                } else {
+                        CERROR("page %p old cksum %x != new cksum %x!\n",
+                               page, llap->llap_checksum, csum);
+                }
+        }
+
         rc = obd_queue_group_io(exp, ll_i2info(inode)->lli_smd, NULL, oig,
                                 llap->llap_cookie, OBD_BRW_WRITE, 0, to, 0,
                                 ASYNC_READY | ASYNC_URGENT |
@@ -661,11 +704,11 @@ int ll_commit_write(struct file *file, struct page *page, unsigned from,
                 RETURN(-EINVAL);
 
         /* set user credit information for this page */
-        llap->llap_ouc.ouc_fsuid = current->fsuid;
-        llap->llap_ouc.ouc_fsgid = current->fsgid;
-        llap->llap_ouc.ouc_cap = current->cap_effective;
+        llap->llap_ouc.luc_fsuid = current->fsuid;
+        llap->llap_ouc.luc_fsgid = current->fsgid;
+        llap->llap_ouc.luc_cap = current->cap_effective;
         ll_i2uctxt(&ctxt, inode, NULL);
-        llap->llap_ouc.ouc_suppgid1 = ctxt.gid1;
+        llap->llap_ouc.luc_suppgid1 = ctxt.gid1;
 
         /* queue a write for some time in the future the first time we
          * dirty the page */
@@ -736,12 +779,7 @@ void ll_ap_completion(void *data, int cmd, struct obdo *oa, int rc)
         struct page *page;
         ENTRY;
 
-        llap = llap_from_cookie(data);
-        if (IS_ERR(llap)) {
-                EXIT;
-                return;
-        }
-
+        llap = LLAP_FROM_COOKIE(data);
         page = llap->llap_page;
         LASSERT(PageLocked(page));
 
@@ -840,7 +878,7 @@ void ll_removepage(struct page *page)
         EXIT;
 }
 
-static int ll_page_matches(struct page *page, int readahead)
+static int ll_page_matches(struct page *page, int fd_flags)
 {
         struct lustre_handle match_lockh = {0};
         struct inode *inode = page->mapping->host;
@@ -848,12 +886,15 @@ static int ll_page_matches(struct page *page, int readahead)
         int flags, matches;
         ENTRY;
 
+        if (unlikely(fd_flags & LL_FILE_GROUP_LOCKED))
+                RETURN(1);
+
         page_extent.l_extent.start = (__u64)page->index << PAGE_CACHE_SHIFT;
         page_extent.l_extent.end =
                 page_extent.l_extent.start + PAGE_CACHE_SIZE - 1;
-        flags = LDLM_FL_TEST_LOCK;
-        if (!readahead)
-                flags |= LDLM_FL_CBPENDING | LDLM_FL_BLOCK_GRANTED;
+        flags = LDLM_FL_TEST_LOCK | LDLM_FL_BLOCK_GRANTED;
+        if (!(fd_flags & LL_FILE_READAHEAD))
+                flags |= LDLM_FL_CBPENDING;
         matches = obd_match(ll_i2sbi(inode)->ll_osc_exp,
                             ll_i2info(inode)->lli_smd, LDLM_EXTENT,
                             &page_extent, LCK_PR | LCK_PW, &flags, inode,
@@ -979,6 +1020,7 @@ static int ll_readahead(struct ll_readahead_state *ras,
                 /* skip locked pages from previous readpage calls */
                 page = grab_cache_page_nowait_gfp(mapping, i, gfp_mask);
                 if (page == NULL) {
+                        ll_ra_stats_inc(mapping, RA_STAT_FAILED_GRAB_PAGE);
                         CDEBUG(D_READA, "g_c_p_n failed\n");
                         continue;
                 }
@@ -994,7 +1036,7 @@ static int ll_readahead(struct ll_readahead_state *ras,
                         goto next_page;
 
                 /* bail when we hit the end of the lock. */
-                if ((rc = ll_page_matches(page, 1)) <= 0) {
+                if ((rc = ll_page_matches(page, flags|LL_FILE_READAHEAD)) <= 0){
                         LL_CDEBUG_PAGE(D_READA | D_PAGE, page,
                                        "lock match failed: rc %d\n", rc);
                         ll_ra_stats_inc(mapping, RA_STAT_FAILED_MATCH);
@@ -1157,8 +1199,8 @@ int ll_writepage(struct page *page)
                                          llap->llap_cookie,
                                          ASYNC_READY | ASYNC_URGENT);
         } else {
-                rc = queue_or_sync_write(exp, inode, llap,
-                                         PAGE_SIZE, ASYNC_READY | ASYNC_URGENT);
+                rc = queue_or_sync_write(exp, inode, llap, PAGE_SIZE,
+                                         ASYNC_READY | ASYNC_URGENT);
         }
         if (rc)
                 page_cache_release(page);
@@ -1184,7 +1226,7 @@ out:
  */
 int ll_readpage(struct file *filp, struct page *page)
 {
-        struct ll_file_data *fd = filp->private_data;
+        struct ll_file_data *fd = LUSTRE_FPRIVATE(filp);
         struct inode *inode = page->mapping->host;
         struct obd_export *exp;
         struct ll_async_page *llap;
@@ -1194,9 +1236,10 @@ int ll_readpage(struct file *filp, struct page *page)
 
         LASSERT(PageLocked(page));
         LASSERT(!PageUptodate(page));
-        CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%u(%p),offset="LPX64"\n",
+        CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%u(%p),offset=%Lu=%#Lx\n",
                inode->i_ino, inode->i_generation, inode,
-               (((obd_off)page->index) << PAGE_SHIFT));
+               (((loff_t)page->index) << PAGE_SHIFT),
+               (((loff_t)page->index) << PAGE_SHIFT));
         LASSERT(atomic_read(&filp->f_dentry->d_inode->i_count) > 0);
 
         rc = oig_init(&oig);
@@ -1228,7 +1271,7 @@ int ll_readpage(struct file *filp, struct page *page)
                 GOTO(out_oig, rc = 0);
         }
 
-        rc = ll_page_matches(page, 0);
+        rc = ll_page_matches(page, fd->fd_flags);
         if (rc < 0) {
                 LL_CDEBUG_PAGE(D_ERROR, page, "lock match failed: rc %d\n", rc);
                 GOTO(out, rc);
index 78f9206..dbf1b20 100644 (file)
@@ -102,8 +102,8 @@ static int __init init_lustre_lite(void)
         if (ll_file_data_slab == NULL)
                 return -ENOMEM;
 
-        proc_lustre_fs_root = proc_lustre_root ?
-                              proc_mkdir("llite", proc_lustre_root) : NULL;
+        if (proc_lustre_root)
+                proc_lustre_fs_root = proc_mkdir("llite", proc_lustre_root);
 
         ll_register_cache(&ll_cache_definition);
 
index e6854ec..30b89f6 100644 (file)
@@ -15,4 +15,4 @@ modulefs_DATA = lov$(KMODEXT)
 endif # MODULES
 
 DIST_SOURCES = $(lov-objs:.o=.c) lov_internal.h
-MOSTLYCLEANFILES = *.o *.ko *.mod.c
+MOSTLYCLEANFILES := @MOSTLYCLEANFILES@ 
index 3127d08..097e7c8 100644 (file)
@@ -132,6 +132,7 @@ int lov_stripe_number(struct lov_stripe_md *lsm, obd_off lov_off);
 void qos_shrink_lsm(struct lov_request_set *set);
 int qos_prep_create(struct lov_obd *lov, struct lov_request_set *set,
                     int newea);
+int qos_remedy_create(struct lov_request_set *set, struct lov_request *req);
 
 /* lov_request.c */
 void lov_set_add_req(struct lov_request *req, struct lov_request_set *set);
@@ -220,4 +221,8 @@ int lov_getstripe(struct obd_export *exp,
 /* lproc_lov.c */
 extern struct file_operations lov_proc_target_fops;
 
+/* Quota stuff */
+int lov_quotacheck(struct obd_export *exp, struct obd_quotactl *oqctl);
+int lov_quotactl(struct obd_export *exp, struct obd_quotactl *oqctl);
+
 #endif
index 082dae3..30092ae 100644 (file)
@@ -99,11 +99,14 @@ int lov_adjust_kms(struct obd_export *exp, struct lov_stripe_md *lsm,
 
         if (shrink) {
                 struct lov_oinfo *loi;
-                int i = 0;
-                for (loi = lsm->lsm_oinfo; i < lsm->lsm_stripe_count;
-                     i++, loi++) {
-                        kms = lov_size_to_stripe(lsm, size, i);
+                for (loi = lsm->lsm_oinfo; stripe < lsm->lsm_stripe_count;
+                     stripe++, loi++) {
+                        kms = lov_size_to_stripe(lsm, size, stripe);
                         loi->loi_kms = loi->loi_rss = kms;
+                        CDEBUG(D_INODE,
+                               "stripe %d KMS %sing "LPU64"->"LPU64"\n",
+                               stripe, kms > loi->loi_kms ? "increas":"shrink",
+                               loi->loi_kms, kms);
                 }
                 RETURN(0);
         }
index 3f212f3..7104aa9 100644 (file)
@@ -62,7 +62,9 @@ static int lov_connect_obd(struct obd_device *obd, struct lov_tgt_desc *tgt,
         struct obd_device *tgt_obd;
         struct obd_uuid lov_osc_uuid = { "LOV_OSC_UUID" };
         struct lustre_handle conn = {0, };
+#ifdef __KERNEL__
         struct proc_dir_entry *lov_proc_dir;
+#endif
         int rc;
         ENTRY;
 
@@ -115,6 +117,7 @@ static int lov_connect_obd(struct obd_device *obd, struct lov_tgt_desc *tgt,
         tgt->active = 1;
         lov->desc.ld_active_tgt_count++;
 
+#ifdef __KERNEL__
         lov_proc_dir = lprocfs_srch(obd->obd_proc_entry, "target_obds");
         if (lov_proc_dir) {
                 struct obd_device *osc_obd = class_conn2obd(&conn);
@@ -138,6 +141,7 @@ static int lov_connect_obd(struct obd_device *obd, struct lov_tgt_desc *tgt,
                         lov_proc_dir = NULL;
                 }
         }
+#endif
 
         RETURN(0);
 }
@@ -172,7 +176,7 @@ static int lov_connect(struct lustre_handle *conn, struct obd_device *obd,
                 if (rc)
                         GOTO(out_disc, rc);
         }
+
         class_export_put(exp);
         RETURN (0);
 
@@ -200,11 +204,13 @@ static int lov_disconnect_obd(struct obd_device *obd, struct lov_tgt_desc *tgt)
         struct lov_obd *lov = &obd->u.lov;
         int rc;
         ENTRY;
-        
+
+        CDEBUG(D_CONFIG, "Disconnecting lov target %s\n", obd->obd_uuid.uuid);
+
         lov_proc_dir = lprocfs_srch(obd->obd_proc_entry, "target_obds");
         if (lov_proc_dir) {
                 struct proc_dir_entry *osc_symlink;
-                
+
                 osc_symlink = lprocfs_srch(lov_proc_dir, osc_obd->obd_name);
                 if (osc_symlink) {
                         lprocfs_remove(osc_symlink);
@@ -214,7 +220,7 @@ static int lov_disconnect_obd(struct obd_device *obd, struct lov_tgt_desc *tgt)
                                osc_obd->obd_name);
                 }
         }
-        
+
         if (obd->obd_no_recov) {
                 /* Pass it on to our clients.
                  * XXX This should be an argument to disconnect,
@@ -224,7 +230,7 @@ static int lov_disconnect_obd(struct obd_device *obd, struct lov_tgt_desc *tgt)
                         osc_obd->obd_no_recov = 1;
         }
 
-        obd_register_observer(tgt->ltd_exp->exp_obd, NULL);
+        obd_register_observer(osc_obd, NULL);
 
         rc = obd_disconnect(tgt->ltd_exp);
         if (rc) {
@@ -234,15 +240,15 @@ static int lov_disconnect_obd(struct obd_device *obd, struct lov_tgt_desc *tgt)
                 }
                 rc = 0;
         }
-        
+
         if (tgt->active) {
                 tgt->active = 0;
                 lov->desc.ld_active_tgt_count--;
         }
-        
+
         tgt->ltd_exp = NULL;
         RETURN(0);
-}                                                     
+}
 
 static int lov_disconnect(struct obd_export *exp)
 {
@@ -252,21 +258,21 @@ static int lov_disconnect(struct obd_export *exp)
         int rc, i;
         ENTRY;
 
+        rc = class_disconnect(exp);
+
         if (!lov->tgts)
-                goto out_local;
+                RETURN(rc);
 
         /* Only disconnect the underlying layers on the final disconnect. */
         lov->refcount--;
         if (lov->refcount != 0)
-                goto out_local;
+                RETURN(rc);
 
         for (i = 0, tgt = lov->tgts; i < lov->desc.ld_tgt_count; i++, tgt++) {
                 if (tgt->ltd_exp)
                         lov_disconnect_obd(obd, tgt);
         }
 
- out_local:
-        rc = class_disconnect(exp);
         RETURN(rc);
 }
 
@@ -306,7 +312,7 @@ static int lov_set_osc_active(struct lov_obd *lov, struct obd_uuid *uuid,
                 GOTO(out, rc);
         }
 
-        CDEBUG(D_INFO, "Marking OSC %s %sactive\n", uuid->uuid, 
+        CDEBUG(D_INFO, "Marking OSC %s %sactive\n", uuid->uuid,
                activate ? "" : "in");
 
         tgt->active = activate;
@@ -336,7 +342,7 @@ static int lov_notify(struct obd_device *obd, struct obd_device *watched,
         uuid = &watched->u.cli.cl_import->imp_target_uuid;
 
         /* Set OSC as active before notifying the observer, so the
-         * observer can use the OSC normally.  
+         * observer can use the OSC normally.
          */
         rc = lov_set_osc_active(&obd->u.lov, uuid, active);
         if (rc) {
@@ -352,104 +358,6 @@ static int lov_notify(struct obd_device *obd, struct obd_device *watched,
         RETURN(rc);
 }
 
-static int lov_setup(struct obd_device *obd, obd_count len, void *buf)
-{
-        struct lprocfs_static_vars lvars;
-        struct lustre_cfg *lcfg = buf;
-        struct lov_desc *desc;
-        struct lov_obd *lov = &obd->u.lov;
-        int count;
-        ENTRY;
-
-        if (LUSTRE_CFG_BUFLEN(lcfg, 1) < 1) {
-                CERROR("LOV setup requires a descriptor\n");
-                RETURN(-EINVAL);
-        }
-
-        desc = (struct lov_desc *)lustre_cfg_buf(lcfg, 1);
-        
-        if (sizeof(*desc) > LUSTRE_CFG_BUFLEN(lcfg, 1)) {
-                CERROR("descriptor size wrong: %d > %d\n",
-                       (int)sizeof(*desc), LUSTRE_CFG_BUFLEN(lcfg, 1));
-                RETURN(-EINVAL);
-        }
-
-        if (desc->ld_magic != LOV_DESC_MAGIC) {
-                if (desc->ld_magic == __swab32(LOV_DESC_MAGIC)) {
-                            CDEBUG(D_OTHER, "%s: Swabbing lov desc %p\n",
-                                   obd->obd_name, desc);
-                            lustre_swab_lov_desc(desc);
-                } else {
-                        CERROR("%s: Bad lov desc magic: %#x\n",
-                               obd->obd_name, desc->ld_magic);
-                        RETURN(-EINVAL);
-                }
-        }
-
-        if (desc->ld_default_stripe_size < PTLRPC_MAX_BRW_SIZE) {
-                CWARN("Increasing default_stripe_size "LPU64" to %u\n",
-                      desc->ld_default_stripe_size, PTLRPC_MAX_BRW_SIZE);
-                CWARN("Please update config and run --write-conf on MDS\n");
-
-                desc->ld_default_stripe_size = PTLRPC_MAX_BRW_SIZE;
-        }
-
-        /* Because of 64-bit divide/mod operations only work with a 32-bit
-         * divisor in a 32-bit kernel, we cannot support a stripe width
-         * of 4GB or larger on 32-bit CPUs.
-         */
-        count = desc->ld_default_stripe_count;
-        if (count && (count * desc->ld_default_stripe_size) > ~0UL) {
-                CERROR("LOV: stripe width "LPU64"x%u > %lu on 32-bit system\n",
-                       desc->ld_default_stripe_size, count, ~0UL);
-                RETURN(-EINVAL);
-        }
-  
-        /* Allocate space for target list */
-        if (desc->ld_tgt_count)
-                count = desc->ld_tgt_count;
-        lov->bufsize = sizeof(struct lov_tgt_desc) * count;
-        OBD_ALLOC(lov->tgts, lov->bufsize);
-        if (lov->tgts == NULL) {
-                CERROR("Out of memory\n");
-                RETURN(-EINVAL);
-        }
-        memset(lov->tgts, 0, lov->bufsize);
-
-        desc->ld_active_tgt_count = 0;
-        lov->desc = *desc;
-        spin_lock_init(&lov->lov_lock);
-       
-        lprocfs_init_vars(lov, &lvars);
-        lprocfs_obd_setup(obd, lvars.obd_vars);
-#ifdef __KERNEL__
-        {
-                struct proc_dir_entry *entry;
-
-                entry = create_proc_entry("target_obd", 0444,
-                                          obd->obd_proc_entry);
-                if (entry != NULL) {
-                        entry->proc_fops = &lov_proc_target_fops;
-                        entry->data = obd;
-                }
-        }
-#endif
-
-        RETURN(0);
-}
-
-static int lov_cleanup(struct obd_device *obd)
-{
-        struct lov_obd *lov = &obd->u.lov;
-
-        lprocfs_obd_cleanup(obd);
-        obd_llog_finish(obd, 0);
-        if (lov->tgts)
-                OBD_FREE(lov->tgts, lov->bufsize);
-
-        RETURN(0);
-}
-
 static int
 lov_add_obd(struct obd_device *obd, struct obd_uuid *uuidp, int index, int gen)
 {
@@ -517,7 +425,7 @@ lov_add_obd(struct obd_device *obd, struct obd_uuid *uuidp, int index, int gen)
                 index, tgt->ltd_gen, lov->desc.ld_tgt_count);
 
         if (lov->refcount == 0)
-                /* lov_connect hasn't been called yet. So we'll do the 
+                /* lov_connect hasn't been called yet. So we'll do the
                    lov_connect_obd on this obd when that fn first runs. */
                 RETURN(0);
 
@@ -575,7 +483,7 @@ lov_del_obd(struct obd_device *obd, struct obd_uuid *uuidp, int index, int gen)
                 RETURN(-EINVAL);
         }
 
-        tgt = lov->tgts + index;
+        tgt = &lov->tgts[index];
 
         if (obd_uuid_empty(&tgt->uuid)) {
                 CERROR("LOV target at index %d is not setup.\n", index);
@@ -588,7 +496,7 @@ lov_del_obd(struct obd_device *obd, struct obd_uuid *uuidp, int index, int gen)
                 RETURN(-EINVAL);
         }
 
-        if (tgt->ltd_exp) 
+        if (tgt->ltd_exp)
                 lov_disconnect_obd(obd, tgt);
 
         /* XXX - right now there is a dependency on ld_tgt_count being the
@@ -604,6 +512,134 @@ lov_del_obd(struct obd_device *obd, struct obd_uuid *uuidp, int index, int gen)
         RETURN(rc);
 }
 
+static int lov_setup(struct obd_device *obd, obd_count len, void *buf)
+{
+        struct lprocfs_static_vars lvars;
+        struct lustre_cfg *lcfg = buf;
+        struct lov_desc *desc;
+        struct lov_obd *lov = &obd->u.lov;
+        int count;
+        ENTRY;
+
+        if (LUSTRE_CFG_BUFLEN(lcfg, 1) < 1) {
+                CERROR("LOV setup requires a descriptor\n");
+                RETURN(-EINVAL);
+        }
+
+        desc = (struct lov_desc *)lustre_cfg_buf(lcfg, 1);
+
+        if (sizeof(*desc) > LUSTRE_CFG_BUFLEN(lcfg, 1)) {
+                CERROR("descriptor size wrong: %d > %d\n",
+                       (int)sizeof(*desc), LUSTRE_CFG_BUFLEN(lcfg, 1));
+                RETURN(-EINVAL);
+        }
+
+        if (desc->ld_magic != LOV_DESC_MAGIC) {
+                if (desc->ld_magic == __swab32(LOV_DESC_MAGIC)) {
+                            CDEBUG(D_OTHER, "%s: Swabbing lov desc %p\n",
+                                   obd->obd_name, desc);
+                            lustre_swab_lov_desc(desc);
+                } else {
+                        CERROR("%s: Bad lov desc magic: %#x\n",
+                               obd->obd_name, desc->ld_magic);
+                        RETURN(-EINVAL);
+                }
+        }
+
+        if (desc->ld_default_stripe_size < PTLRPC_MAX_BRW_SIZE) {
+                CWARN("Increasing default_stripe_size "LPU64" to %u\n",
+                      desc->ld_default_stripe_size, PTLRPC_MAX_BRW_SIZE);
+                CWARN("Please update config and run --write-conf on MDS\n");
+
+                desc->ld_default_stripe_size = PTLRPC_MAX_BRW_SIZE;
+        } else if (desc->ld_default_stripe_size & (LOV_MIN_STRIPE_SIZE - 1)) {
+                CWARN("default_stripe_size "LPU64" isn't a multiple of %u\n",
+                      desc->ld_default_stripe_size, LOV_MIN_STRIPE_SIZE);
+                CWARN("Please update config and run --write-conf on MDS\n");
+
+                desc->ld_default_stripe_size &= ~(LOV_MIN_STRIPE_SIZE - 1);
+       }
+
+        if (desc->ld_default_stripe_count == 0)
+                desc->ld_default_stripe_count = 1;
+
+        /* Because of 64-bit divide/mod operations only work with a 32-bit
+         * divisor in a 32-bit kernel, we cannot support a stripe width
+         * of 4GB or larger on 32-bit CPUs. */
+        count = desc->ld_default_stripe_count;
+        if ((count ? count : desc->ld_tgt_count) *
+            desc->ld_default_stripe_size > ~0UL) {
+                CERROR("LOV: stripe width "LPU64"x%u > %lu on 32-bit system\n",
+                       desc->ld_default_stripe_size, count, ~0UL);
+                RETURN(-EINVAL);
+        }
+
+        /* Allocate space for target list */
+        if (desc->ld_tgt_count)
+                count = desc->ld_tgt_count;
+        lov->bufsize = sizeof(struct lov_tgt_desc) * count;
+        OBD_ALLOC(lov->tgts, lov->bufsize);
+        if (lov->tgts == NULL) {
+                CERROR("Out of memory\n");
+                RETURN(-EINVAL);
+        }
+        memset(lov->tgts, 0, lov->bufsize);
+
+        desc->ld_active_tgt_count = 0;
+        lov->desc = *desc;
+        spin_lock_init(&lov->lov_lock);
+
+        lprocfs_init_vars(lov, &lvars);
+        lprocfs_obd_setup(obd, lvars.obd_vars);
+#ifdef LPROCFS
+        {
+                struct proc_dir_entry *entry;
+
+                entry = create_proc_entry("target_obd", 0444,
+                                          obd->obd_proc_entry);
+                if (entry != NULL) {
+                        entry->proc_fops = &lov_proc_target_fops;
+                        entry->data = obd;
+                }
+        }
+#endif
+
+        RETURN(0);
+}
+
+static int lov_precleanup(struct obd_device *obd, int stage)
+{
+        int rc = 0;
+        ENTRY;
+
+        if (stage < 2)
+                RETURN(0);
+
+        rc = obd_llog_finish(obd, 0);
+        if (rc != 0)
+                CERROR("failed to cleanup llogging subsystems\n");
+
+        RETURN(rc);
+}
+
+static int lov_cleanup(struct obd_device *obd)
+{
+        struct lov_obd *lov = &obd->u.lov;
+
+        lprocfs_obd_cleanup(obd);
+        if (lov->tgts) {
+                int i;
+                struct lov_tgt_desc *tgt;
+                for (i = 0, tgt = lov->tgts;
+                      i < lov->desc.ld_tgt_count; i++, tgt++) {
+                        if (!obd_uuid_empty(&tgt->uuid))
+                                lov_del_obd(obd, &tgt->uuid, i, 0);
+                }
+                OBD_FREE(lov->tgts, lov->bufsize);
+        }
+        RETURN(0);
+}
+
 static int lov_process_config(struct obd_device *obd, obd_count len, void *buf)
 {
         struct lustre_cfg *lcfg = buf;
@@ -617,9 +653,9 @@ static int lov_process_config(struct obd_device *obd, obd_count len, void *buf)
         switch(cmd = lcfg->lcfg_command) {
         case LCFG_LOV_ADD_OBD:
         case LCFG_LOV_DEL_OBD: {
-                if (LUSTRE_CFG_BUFLEN(lcfg, 1) > sizeof(obd_uuid.uuid)) 
+                if (LUSTRE_CFG_BUFLEN(lcfg, 1) > sizeof(obd_uuid.uuid))
                         GOTO(out, rc = -EINVAL);
-                        
+
                 obd_str2uuid(&obd_uuid,  lustre_cfg_buf(lcfg, 1));
 
                 if (sscanf(lustre_cfg_buf(lcfg, 2), "%d", &index) != 1)
@@ -688,6 +724,7 @@ static int lov_clear_orphans(struct obd_export *export, struct obdo *src_oa,
 
                 memcpy(tmp_oa, src_oa, sizeof(*tmp_oa));
 
+                LASSERT(lov->tgts[i].ltd_exp);
                 /* XXX: LOV STACKING: use real "obj_mdp" sub-data */
                 err = obd_create(lov->tgts[i].ltd_exp, tmp_oa, &obj_mdp, oti);
                 if (err)
@@ -778,11 +815,11 @@ static int lov_create(struct obd_export *exp, struct obdo *src_oa,
                 RETURN(rc);
 
         list_for_each (pos, &set->set_list) {
-                struct lov_request *req = 
+                struct lov_request *req =
                         list_entry(pos, struct lov_request, rq_link);
 
                 /* XXX: LOV STACKING: use real "obj_mdp" sub-data */
-                rc = obd_create(lov->tgts[req->rq_idx].ltd_exp, 
+                rc = obd_create(lov->tgts[req->rq_idx].ltd_exp,
                                 req->rq_oa, &req->rq_md, oti);
                 lov_update_create_set(set, req, rc);
         }
@@ -790,11 +827,11 @@ static int lov_create(struct obd_export *exp, struct obdo *src_oa,
         RETURN(rc);
 }
 
-#define ASSERT_LSM_MAGIC(lsmp)                                  \
-do {                                                            \
-        LASSERT((lsmp) != NULL);                                \
-        LASSERTF((lsmp)->lsm_magic == LOV_MAGIC, "%p, %x",      \
-                 (lsmp), (lsmp)->lsm_magic);                    \
+#define ASSERT_LSM_MAGIC(lsmp)                                          \
+do {                                                                    \
+        LASSERT((lsmp) != NULL);                                        \
+        LASSERTF((lsmp)->lsm_magic == LOV_MAGIC, "%p->lsm_magic=%x\n",  \
+                 (lsmp), (lsmp)->lsm_magic);                            \
 } while (0)
 
 static int lov_destroy(struct obd_export *exp, struct obdo *oa,
@@ -828,8 +865,8 @@ static int lov_destroy(struct obd_export *exp, struct obdo *oa,
                 err = lov_update_common_set(set, req, rc);
                 if (rc) {
                         CERROR("error: destroying objid "LPX64" subobj "
-                               LPX64" on OST idx %d: rc = %d\n", 
-                               set->set_oa->o_id, req->rq_oa->o_id, 
+                               LPX64" on OST idx %d: rc = %d\n",
+                               set->set_oa->o_id, req->rq_oa->o_id,
                                req->rq_idx, rc);
                         if (!rc)
                                 rc = err;
@@ -855,30 +892,30 @@ static int lov_getattr(struct obd_export *exp, struct obdo *oa,
                 RETURN(-ENODEV);
 
         lov = &exp->exp_obd->u.lov;
-        
+
         rc = lov_prep_getattr_set(exp, oa, lsm, &set);
         if (rc)
                 RETURN(rc);
 
         list_for_each (pos, &set->set_list) {
                 req = list_entry(pos, struct lov_request, rq_link);
-                
+
                 CDEBUG(D_INFO, "objid "LPX64"[%d] has subobj "LPX64" at idx "
-                       "%u\n", oa->o_id, req->rq_stripe, req->rq_oa->o_id, 
+                       "%u\n", oa->o_id, req->rq_stripe, req->rq_oa->o_id,
                        req->rq_idx);
 
-                rc = obd_getattr(lov->tgts[req->rq_idx].ltd_exp, 
+                rc = obd_getattr(lov->tgts[req->rq_idx].ltd_exp,
                                  req->rq_oa, NULL);
                 err = lov_update_common_set(set, req, rc);
                 if (err) {
                         CERROR("error: getattr objid "LPX64" subobj "
                                LPX64" on OST idx %d: rc = %d\n",
-                               set->set_oa->o_id, req->rq_oa->o_id, 
+                               set->set_oa->o_id, req->rq_oa->o_id,
                                req->rq_idx, err);
                         break;
                 }
         }
-        
+
         rc = lov_fini_getattr_set(set);
         if (err)
                 rc = err;
@@ -928,22 +965,22 @@ static int lov_getattr_async(struct obd_export *exp, struct obdo *oa,
 
         list_for_each (pos, &lovset->set_list) {
                 req = list_entry(pos, struct lov_request, rq_link);
-                
+
                 CDEBUG(D_INFO, "objid "LPX64"[%d] has subobj "LPX64" at idx "
-                       "%u\n", oa->o_id, req->rq_stripe, req->rq_oa->o_id, 
+                       "%u\n", oa->o_id, req->rq_stripe, req->rq_oa->o_id,
                        req->rq_idx);
                 rc = obd_getattr_async(lov->tgts[req->rq_idx].ltd_exp,
                                        req->rq_oa, NULL, rqset);
                 if (rc) {
                         CERROR("error: getattr objid "LPX64" subobj "
                                LPX64" on OST idx %d: rc = %d\n",
-                               lovset->set_oa->o_id, req->rq_oa->o_id, 
+                               lovset->set_oa->o_id, req->rq_oa->o_id,
                                req->rq_idx, rc);
                         GOTO(out, rc);
                 }
                 lov_update_common_set(lovset, req, rc);
         }
-        
+
         LASSERT(rc == 0);
         LASSERT (rqset->set_interpret == NULL);
         rqset->set_interpret = lov_getattr_interpret;
@@ -1010,12 +1047,12 @@ static int lov_setattr_async(struct obd_export *exp, struct obdo *src_oa,
         obd_id objid = src_oa->o_id;
         int i;
         ENTRY;
-                                                                                                                             
+
         ASSERT_LSM_MAGIC(lsm);
         LASSERT(oti);
         if (src_oa->o_valid & OBD_MD_FLCOOKIE)
                 LASSERT(oti->oti_logcookies);
-                                                                                                                             
+
         if (!exp || !exp->exp_obd)
                 RETURN(-ENODEV);
 
@@ -1077,8 +1114,8 @@ static int lov_punch(struct obd_export *exp, struct obdo *oa,
         list_for_each (pos, &set->set_list) {
                 req = list_entry(pos, struct lov_request, rq_link);
 
-                rc = obd_punch(lov->tgts[req->rq_idx].ltd_exp, req->rq_oa, 
-                               NULL, req->rq_extent.start, 
+                rc = obd_punch(lov->tgts[req->rq_idx].ltd_exp, req->rq_oa,
+                               NULL, req->rq_extent.start,
                                req->rq_extent.end, NULL);
                 err = lov_update_punch_set(set, req, rc);
                 if (err) {
@@ -1118,7 +1155,7 @@ static int lov_sync(struct obd_export *exp, struct obdo *oa,
         list_for_each (pos, &set->set_list) {
                 req = list_entry(pos, struct lov_request, rq_link);
 
-                rc = obd_sync(lov->tgts[req->rq_idx].ltd_exp, req->rq_oa, 
+                rc = obd_sync(lov->tgts[req->rq_idx].ltd_exp, req->rq_oa,
                               NULL, req->rq_extent.start, req->rq_extent.end);
                 err = lov_update_common_set(set, req, rc);
                 if (err) {
@@ -1191,10 +1228,10 @@ static int lov_brw(int cmd, struct obd_export *exp, struct obdo *src_oa,
                 struct obd_export *sub_exp;
                 struct brw_page *sub_pga;
                 req = list_entry(pos, struct lov_request, rq_link);
-                
+
                 sub_exp = lov->tgts[req->rq_idx].ltd_exp;
                 sub_pga = set->set_pga + req->rq_pgaidx;
-                rc = obd_brw(cmd, sub_exp, req->rq_oa, req->rq_md, 
+                rc = obd_brw(cmd, sub_exp, req->rq_oa, req->rq_md,
                              req->rq_oabufs, sub_pga, oti);
                 if (rc)
                         break;
@@ -1212,14 +1249,14 @@ static int lov_brw_interpret(struct ptlrpc_request_set *reqset, void *data,
 {
         struct lov_request_set *lovset = (struct lov_request_set *)data;
         ENTRY;
-        
+
         if (rc) {
                 lovset->set_completes = 0;
                 lov_fini_brw_set(lovset);
         } else {
                 rc = lov_fini_brw_set(lovset);
         }
-                
+
         RETURN(rc);
 }
 
@@ -1250,7 +1287,7 @@ static int lov_brw_async(int cmd, struct obd_export *exp, struct obdo *oa,
                 struct obd_export *sub_exp;
                 struct brw_page *sub_pga;
                 req = list_entry(pos, struct lov_request, rq_link);
-                
+
                 sub_exp = lov->tgts[req->rq_idx].ltd_exp;
                 sub_pga = lovset->set_pga + req->rq_pgaidx;
                 rc = obd_brw_async(cmd, sub_exp, req->rq_oa, req->rq_md,
@@ -1263,7 +1300,7 @@ static int lov_brw_async(int cmd, struct obd_export *exp, struct obdo *oa,
         LASSERT(set->set_interpret == NULL);
         set->set_interpret = (set_interpreter_func)lov_brw_interpret;
         set->set_arg = (void *)lovset;
-        
+
         RETURN(rc);
 out:
         lov_fini_brw_set(lovset);
@@ -1302,7 +1339,7 @@ static void lov_ap_completion(void *data, int cmd, struct obdo *oa, int rc)
         lap->lap_caller_ops->ap_completion(lap->lap_caller_data, cmd, oa, rc);
 }
 
-static void lov_ap_get_ucred(void *data, struct obd_ucred *ouc)
+static void lov_ap_get_ucred(void *data, struct lvfs_ucred *ouc)
 {
         struct lov_async_page *lap = LAP_FROM_COOKIE(data);
 
@@ -1523,8 +1560,7 @@ static int lov_enqueue(struct obd_export *exp, struct lov_stripe_md *lsm,
                 LASSERT(lov_lockhp);
 
                 *flags = save_flags;
-                sub_policy.l_extent.start = req->rq_extent.start;
-                sub_policy.l_extent.end = req->rq_extent.end;
+                sub_policy.l_extent = req->rq_extent;
 
                 rc = obd_enqueue(lov->tgts[req->rq_idx].ltd_exp, req->rq_md,
                                  type, &sub_policy, mode, flags, bl_cb,
@@ -1567,9 +1603,8 @@ static int lov_match(struct obd_export *exp, struct lov_stripe_md *lsm,
                 lov_lockhp = set->set_lockh->llh_handles + req->rq_stripe;
                 LASSERT(lov_lockhp);
 
-                sub_policy.l_extent.start = req->rq_extent.start;
-                sub_policy.l_extent.end = req->rq_extent.end;
                 lov_flags = *flags;
+                sub_policy.l_extent = req->rq_extent;
 
                 rc = obd_match(lov->tgts[req->rq_idx].ltd_exp, req->rq_md,
                                type, &sub_policy, mode, &lov_flags, data,
@@ -1648,7 +1683,7 @@ static int lov_cancel(struct obd_export *exp, struct lov_stripe_md *lsm,
                                req->rq_md->lsm_object_id, req->rq_idx, rc);
                         err = rc;
                 }
+
         }
         lov_fini_cancel_set(set);
         RETURN(err);
@@ -1665,8 +1700,12 @@ static int lov_cancel_unused(struct obd_export *exp,
         lov = &exp->exp_obd->u.lov;
         if (lsm == NULL) {
                 for (i = 0; i < lov->desc.ld_tgt_count; i++) {
-                        int err = obd_cancel_unused(lov->tgts[i].ltd_exp, NULL,
-                                                    flags, opaque);
+                        int err;
+                        if (!lov->tgts[i].ltd_exp)
+                                continue;
+
+                        err = obd_cancel_unused(lov->tgts[i].ltd_exp, NULL,
+                                                flags, opaque);
                         if (!rc)
                                 rc = err;
                 }
@@ -1722,7 +1761,7 @@ static int lov_join_lru(struct obd_export *exp,
 
                 submd.lsm_object_id = loi->loi_id;
                 submd.lsm_stripe_count = 0;
-                rc = obd_join_lru(lov->tgts[loi->loi_ost_idx].ltd_exp, 
+                rc = obd_join_lru(lov->tgts[loi->loi_ost_idx].ltd_exp,
                                   &submd, join);
                 if (rc < 0) {
                         CERROR("join lru failed. objid: "LPX64" subobj: "LPX64
@@ -1796,9 +1835,7 @@ static int lov_statfs(struct obd_device *obd, struct obd_statfs *osfs,
         }
 
         if (set) {
-                __u32 expected_stripes = lov->desc.ld_default_stripe_count ?
-                                         lov->desc.ld_default_stripe_count :
-                                         lov->desc.ld_active_tgt_count;
+                __u32 expected_stripes = lov_get_stripecnt(lov, 0);
 
                 if (osfs->os_files != LOV_U64_MAX)
                         do_div(osfs->os_files, expected_stripes);
@@ -1835,17 +1872,17 @@ static int lov_iocontrol(unsigned int cmd, struct obd_export *exp, int len,
                 data = (struct obd_ioctl_data *)buf;
 
                 if (sizeof(*desc) > data->ioc_inllen1) {
-                        OBD_FREE(buf, len);
+                        obd_ioctl_freedata(buf, len);
                         RETURN(-EINVAL);
                 }
 
                 if (sizeof(uuidp->uuid) * count > data->ioc_inllen2) {
-                        OBD_FREE(buf, len);
+                        obd_ioctl_freedata(buf, len);
                         RETURN(-EINVAL);
                 }
 
                 if (sizeof(__u32) * count > data->ioc_inllen3) {
-                        OBD_FREE(buf, len);
+                        obd_ioctl_freedata(buf, len);
                         RETURN(-EINVAL);
                 }
 
@@ -1884,8 +1921,8 @@ static int lov_iocontrol(unsigned int cmd, struct obd_export *exp, int len,
                 for (i = 0; i < count; i++) {
                         int err;
 
-                        /* OST was deleted */
-                        if (obd_uuid_empty(&lov->tgts[i].uuid))
+                        /* OST was disconnected */
+                        if (!lov->tgts[i].ltd_exp)
                                 continue;
 
                         err = obd_iocontrol(cmd, lov->tgts[i].ltd_exp,
@@ -1997,21 +2034,21 @@ static int lov_set_info(struct obd_export *exp, obd_count keylen,
         int i, rc = 0, err;
         ENTRY;
 
-#define KEY_IS(str) \
-        (keylen == strlen(str) && memcmp(key, str, keylen) == 0)
-
         if (KEY_IS("next_id")) {
                 if (vallen != lov->desc.ld_tgt_count)
                         RETURN(-EINVAL);
+                vallen = sizeof(obd_id);
+        }
+
+        if (KEY_IS("next_id") || KEY_IS("checksum")) {
                 for (i = 0; i < lov->desc.ld_tgt_count; i++) {
-                        /* OST was deleted */
-                        if (obd_uuid_empty(&lov->tgts[i].uuid))
+                        /* OST was disconnected */
+                        if (!lov->tgts[i].ltd_exp)
                                 continue;
 
-                        /* initialize all OSCs, even inactive ones */
-                        err = obd_set_info(lov->tgts[i].ltd_exp,
-                                          keylen, key, sizeof(obd_id),
-                                          ((obd_id*)val) + i);
+                        /* hit all OSCs, even inactive ones */
+                        err = obd_set_info(lov->tgts[i].ltd_exp, keylen, key,
+                                           vallen, ((obd_id*)val) + i);
                         if (!rc)
                                 rc = err;
                 }
@@ -2029,8 +2066,8 @@ static int lov_set_info(struct obd_export *exp, obd_count keylen,
                 if (val && !obd_uuid_equals(val, &lov->tgts[i].uuid))
                         continue;
 
-                /* OST was deleted */
-                if (obd_uuid_empty(&lov->tgts[i].uuid))
+                /* OST was disconnected */
+                if (!lov->tgts[i].ltd_exp)
                         continue;
 
                 if (!val && !lov->tgts[i].active)
@@ -2042,7 +2079,6 @@ static int lov_set_info(struct obd_export *exp, obd_count keylen,
                         rc = err;
         }
         RETURN(rc);
-#undef KEY_IS
 }
 
 int lov_test_and_clear_async_rc(struct lov_stripe_md *lsm)
@@ -2150,67 +2186,10 @@ int lov_complete_many(struct obd_export *exp, struct lov_stripe_md *lsm,
 }
 #endif
 
-static int lov_quotacheck(struct obd_export *exp, struct obd_quotactl *oqctl)
-{
-        struct obd_device *obd = class_exp2obd(exp);
-        struct lov_obd *lov = &obd->u.lov;
-        int i, rc = 0;
-        ENTRY;
-
-        for (i = 0; i < lov->desc.ld_tgt_count; i++) {
-                int err;
-
-                if (!lov->tgts[i].active) {
-                        CDEBUG(D_HA, "lov idx %d inactive\n", i);
-                        continue;
-                }
-
-                err = obd_quotacheck(lov->tgts[i].ltd_exp, oqctl);
-                if (err) {
-                        if (lov->tgts[i].active && !rc)
-                                rc = err;
-                        continue;
-                }
-        }
-
-        RETURN(rc);
-}
-
-static int lov_quotactl(struct obd_export *exp, struct obd_quotactl *oqctl)
-{
-        struct obd_device *obd = class_exp2obd(exp);
-        struct lov_obd *lov = &obd->u.lov;
-        __u64 curspace = oqctl->qc_dqblk.dqb_curspace;
-        int i, rc = 0;
-        ENTRY;
-
-        for (i = 0; i < lov->desc.ld_tgt_count; i++) {
-                int err;
-
-                if (!lov->tgts[i].active) {
-                        CDEBUG(D_HA, "lov idx %d inactive\n", i);
-                        continue;
-                }
-
-                err = obd_quotactl(lov->tgts[i].ltd_exp, oqctl);
-                if (err) {
-                        if (lov->tgts[i].active && !rc)
-                                rc = err;
-                        continue;
-                }
-
-                if (oqctl->qc_cmd == Q_GETQUOTA)
-                        curspace += oqctl->qc_dqblk.dqb_curspace;
-        }
-
-        if (oqctl->qc_cmd == Q_GETQUOTA)
-                oqctl->qc_dqblk.dqb_curspace = curspace;
-        RETURN(rc);
-}
-
 struct obd_ops lov_obd_ops = {
         .o_owner               = THIS_MODULE,
         .o_setup               = lov_setup,
+        .o_precleanup          = lov_precleanup,
         .o_cleanup             = lov_cleanup,
         .o_process_config      = lov_process_config,
         .o_connect             = lov_connect,
@@ -2247,8 +2226,10 @@ struct obd_ops lov_obd_ops = {
         .o_llog_init           = lov_llog_init,
         .o_llog_finish         = lov_llog_finish,
         .o_notify              = lov_notify,
+#ifdef HAVE_QUOTA_SUPPORT
         .o_quotacheck          = lov_quotacheck,
         .o_quotactl            = lov_quotactl,
+#endif
 };
 
 int __init lov_init(void)
index 342ad47..049ea80 100644 (file)
@@ -124,7 +124,8 @@ int lov_packmd(struct obd_export *exp, struct lov_mds_md **lmmp,
 
         for (i = 0, loi = lsm->lsm_oinfo; i < stripe_count; i++, loi++) {
                 /* XXX LOV STACKING call down to osc_packmd() to do packing */
-                LASSERT(loi->loi_id);
+                LASSERTF(loi->loi_id, "lmm_oid "LPU64" stripe %u/%u idx %u\n",
+                         lmm->lmm_object_id, i, stripe_count, loi->loi_ost_idx);
                 lmm->lmm_objects[i].l_object_id = cpu_to_le64(loi->loi_id);
                 lmm->lmm_objects[i].l_object_gr = cpu_to_le64(loi->loi_gr);
                 lmm->lmm_objects[i].l_ost_gen = cpu_to_le32(loi->loi_ost_gen);
@@ -138,8 +139,15 @@ int lov_get_stripecnt(struct lov_obd *lov, int stripe_count)
 {
         if (!stripe_count)
                 stripe_count = lov->desc.ld_default_stripe_count;
-        if (!stripe_count || stripe_count > lov->desc.ld_active_tgt_count)
+        if (!stripe_count)
+                stripe_count = 1;
+        if (stripe_count > lov->desc.ld_active_tgt_count)
                 stripe_count = lov->desc.ld_active_tgt_count;
+        /* for now, we limit the stripe count directly, when bug 4424 is
+         * fixed this needs to be somewhat dynamic based on whether ext3
+         * can handle larger EA sizes. */
+        if (stripe_count > LOV_MAX_STRIPE_COUNT)
+                stripe_count = LOV_MAX_STRIPE_COUNT;
 
         return stripe_count;
 }
@@ -366,10 +374,10 @@ int lov_setstripe(struct obd_export *exp, struct lov_stripe_md **lsmp,
 
         /* 64kB is the largest common page size we see (ia64), and matches the
          * check in lfs */
-        if (lum.lmm_stripe_size & (65536 - 1)) {
-                CDEBUG(D_IOCTL, "stripe size %u not multiple of 64kB\n",
-                       lum.lmm_stripe_size);
-                RETURN(-EINVAL);
+        if (lum.lmm_stripe_size & (LOV_MIN_STRIPE_SIZE - 1)) {
+                CDEBUG(D_IOCTL, "stripe size %u not multiple of %u, fixing\n",
+                       lum.lmm_stripe_size, LOV_MIN_STRIPE_SIZE);
+                lum.lmm_stripe_size = LOV_MIN_STRIPE_SIZE;
         }
 
         if ((lum.lmm_stripe_offset >= lov->desc.ld_active_tgt_count) &&
index f7b0f76..c7bd979 100644 (file)
@@ -72,6 +72,37 @@ void qos_shrink_lsm(struct lov_request_set *set)
         }
 }
 
+int qos_remedy_create(struct lov_request_set *set, struct lov_request *req)
+{
+        struct lov_stripe_md *lsm = set->set_md;
+        struct lov_obd *lov = &set->set_exp->exp_obd->u.lov;
+        unsigned ost_idx, ost_count = lov->desc.ld_tgt_count;
+        int stripe, i, rc = -EIO;
+        ENTRY;
+
+        ost_idx = (req->rq_idx + 1) % ost_count; 
+        for (i = 0; i < ost_count; i++, ost_idx = (ost_idx + 1) % ost_count) {
+                if (lov->tgts[ost_idx].active == 0) {
+                        CDEBUG(D_HA, "lov idx %d inactive\n", ost_idx);
+                        continue;
+                }
+                /* check if objects has been created on this ost */
+                for (stripe = req->rq_stripe; stripe >= 0; stripe--) {
+                        if (ost_idx == lsm->lsm_oinfo[stripe].loi_ost_idx)
+                                break;
+                }
+
+                if (stripe < 0) {
+                        req->rq_idx = ost_idx;
+                        rc = obd_create(lov->tgts[ost_idx].ltd_exp, req->rq_oa, 
+                                        &req->rq_md, set->set_oti);
+                        if (!rc)
+                                break;
+                }
+        }
+        RETURN(rc);
+}
+
 #define LOV_CREATE_RESEED_INTERVAL 1000
 /* FIXME use real qos data to prepare the lov create request */
 int qos_prep_create(struct lov_obd *lov, struct lov_request_set *set, int newea)
index 1ac7190..3054173 100644 (file)
@@ -277,6 +277,7 @@ int lov_prep_enqueue_set(struct obd_export *exp, struct lov_stripe_md *lsm,
 
                 req->rq_extent.start = start;
                 req->rq_extent.end = end;
+                req->rq_extent.gid = policy->l_extent.gid;
 
                 req->rq_idx = loi->loi_ost_idx;
                 req->rq_stripe = i;
@@ -384,6 +385,7 @@ int lov_prep_match_set(struct obd_export *exp, struct lov_stripe_md *lsm,
 
                 req->rq_extent.start = start;
                 req->rq_extent.end = end;
+                req->rq_extent.gid = policy->l_extent.gid;
 
                 req->rq_idx = loi->loi_ost_idx;
                 req->rq_stripe = i;
@@ -495,9 +497,33 @@ static int create_done(struct obd_export *exp, struct lov_request_set *set,
 
         LASSERT(set->set_completes);
 
-        if (!set->set_success)
-                GOTO(cleanup, rc = -EIO);
-        if (*lsmp == NULL && set->set_count != set->set_success) {
+        /* try alloc objects on other osts if osc_create fails for
+         * exceptions: RPC failure, ENOSPC, etc */
+        if (set->set_count != set->set_success) {
+                list_for_each_entry (req, &set->set_list, rq_link) {
+                        if (req->rq_rc == 0)
+                                continue;
+                        
+                        set->set_completes--;
+                        req->rq_complete = 0;
+                        
+                        rc = qos_remedy_create(set, req);
+                        lov_update_create_set(set, req, rc);
+
+                        if (rc)
+                                break;
+                }
+        }
+
+        /* no successful creates */
+        if (set->set_success == 0)
+                GOTO(cleanup, rc);
+        
+        /* If there was an explicit stripe set, fail.  Otherwise, we
+         * got some objects and that's not bad. */
+        if (set->set_count != set->set_success) {
+                if (*lsmp)
+                        GOTO(cleanup, rc);
                 set->set_count = set->set_success;
                 qos_shrink_lsm(set);
         }
@@ -533,7 +559,7 @@ cleanup:
                 if (!req->rq_complete || req->rq_rc)
                         continue;
 
-                sub_exp = lov->tgts[req->rq_idx].ltd_exp,
+                sub_exp = lov->tgts[req->rq_idx].ltd_exp;
                 err = obd_destroy(sub_exp, req->rq_oa, NULL, oti);
                 if (err)
                         CERROR("Failed to uncreate objid "LPX64" subobj "
@@ -1203,6 +1229,7 @@ int lov_prep_punch_set(struct obd_export *exp, struct obdo *src_oa,
 
                 req->rq_extent.start = rs;
                 req->rq_extent.end = re;
+                req->rq_extent.gid = -1;
 
                 lov_set_add_req(req, set);
         }
@@ -1281,6 +1308,7 @@ int lov_prep_sync_set(struct obd_export *exp, struct obdo *src_oa,
 
                 req->rq_extent.start = rs;
                 req->rq_extent.end = re;
+                req->rq_extent.gid = -1;
 
                 lov_set_add_req(req, set);
         }
index ee4883d..2546c6c 100644 (file)
 #include <linux/obd_class.h>
 #include <linux/seq_file.h>
 
-#ifndef LPROCFS
-static struct lprocfs_vars lprocfs_module_vars[] = { {0} };
-static struct lprocfs_vars lprocfs_obd_vars[] = { {0} };
-#else
-
+#ifdef LPROCFS
 static int lov_rd_stripesize(char *page, char **start, off_t off, int count,
                              int *eof, void *data)
 {
@@ -205,5 +201,5 @@ struct file_operations lov_proc_target_fops = {
         .release = seq_release,
 };
 
-#endif /* LPROCFS */
 LPROCFS_INIT_VARS(lov, lprocfs_module_vars, lprocfs_obd_vars)
+#endif /* LPROCFS */
index 592ce3b..d18b255 100644 (file)
@@ -16,3 +16,4 @@ autoMakefile
 .depend
 sources
 fsfilt_ldiskfs.*
+fsfilt-ldiskfs.*
index ac5a8a2..02fb755 100644 (file)
@@ -1,8 +1,9 @@
-MODULES := lvfs fsfilt_@BACKINGFS@ quotactl_test quotacheck_test
+MODULES := lvfs #quotactl_test quotacheck_test
+@SERVER_TRUE@MODULES += fsfilt_@BACKINGFS@
 
 lvfs-objs := lvfs_common.o lvfs_linux.o fsfilt.o
-quotactl-objs := quotactl_test.o
-quotaccheck-objs := quotacheck_test.o
+#quotactl-objs := quotactl_test.o
+#quotaccheck-objs := quotacheck_test.o
 
 ifeq ($(PATCHLEVEL),6)
 fsfilt_@BACKINGFS@-objs := fsfilt-@BACKINGFS@.o
index 814d299..ef84f9d 100644 (file)
@@ -16,10 +16,16 @@ endif
 
 if MODULES
 
-modulefs_DATA = lvfs$(KMODEXT) fsfilt_$(BACKINGFS)$(KMODEXT)
+modulefs_DATA := lvfs$(KMODEXT)
 
+if SERVER
+modulefs_DATA += fsfilt_$(BACKINGFS)$(KMODEXT)
 sources: fsfilt_$(BACKINGFS).c
        touch sources
+else
+sources:
+
+endif
 
 fsfilt_extN.c: fsfilt_ext3.c
        sed -e "s/EXT3/EXTN/g" -e "s/ext3/extN/g" $< > $@
@@ -34,6 +40,9 @@ ldiskfs_sed_flags = \
 
 fsfilt_ldiskfs.c: fsfilt_ext3.c
        sed $(strip $(ldiskfs_sed_flags)) $< > $@
+fsfilt_ldiskfs_quota.h: fsfilt_ext3_quota.h
+       sed $(strip $(ldiskfs_sed_flags)) $< > $@
+
 
 else
 
@@ -43,7 +52,7 @@ endif # MODULES
 
 DIST_SOURCES = fsfilt.c fsfilt_ext3.c fsfilt_reiserfs.c lvfs_common.c \
        lvfs_internal.h lvfs_linux.c lvfs_userfs.c \
-        quotacheck_test.c quotactl_test.c
+        quotacheck_test.c quotactl_test.c # fsfilt_ext3_quota.h
 
-MOSTLYCLEANFILES = *.o *.ko *.mod.c
+MOSTLYCLEANFILES := @MOSTLYCLEANFILES@ 
 CLEANFILES = fsfilt-*.c fsfilt_ldiskfs.c fsfilt_extN.c sources
index eb890e5..c5ff7a9 100644 (file)
@@ -112,8 +112,6 @@ static void *fsfilt_ext3_start(struct inode *inode, int op, void *desc_private,
         case FSFILT_OP_RENAME:
                 /* modify additional directory */
                 nblocks += EXT3_SINGLEDATA_TRANS_BLOCKS;
-                nblocks += (EXT3_INDEX_EXTRA_TRANS_BLOCKS +
-                            EXT3_SINGLEDATA_TRANS_BLOCKS) * logs;
                 /* no break */
         case FSFILT_OP_SYMLINK:
                 /* additional block + block bitmap + GDT for long symlink */
@@ -134,9 +132,6 @@ static void *fsfilt_ext3_start(struct inode *inode, int op, void *desc_private,
                         }
                 }
 #endif
-                /* create/update logs for each stripe */
-                nblocks += (EXT3_INDEX_EXTRA_TRANS_BLOCKS +
-                            EXT3_SINGLEDATA_TRANS_BLOCKS) * logs;
                 /* no break */
         }
         case FSFILT_OP_MKDIR:
@@ -148,12 +143,18 @@ static void *fsfilt_ext3_start(struct inode *inode, int op, void *desc_private,
                 /* modify parent directory */
                 nblocks += EXT3_INDEX_EXTRA_TRANS_BLOCKS +
                         EXT3_DATA_TRANS_BLOCKS;
+                /* create/update logs for each stripe */
+                nblocks += (EXT3_INDEX_EXTRA_TRANS_BLOCKS +
+                            EXT3_SINGLEDATA_TRANS_BLOCKS) * logs;
                 break;
         case FSFILT_OP_SETATTR:
                 /* Setattr on inode */
                 nblocks += 1;
                 nblocks += EXT3_INDEX_EXTRA_TRANS_BLOCKS +
                         EXT3_DATA_TRANS_BLOCKS;
+                /* quota chown log for each stripe */
+                nblocks += (EXT3_INDEX_EXTRA_TRANS_BLOCKS +
+                            EXT3_SINGLEDATA_TRANS_BLOCKS) * logs;
                 break;
         case FSFILT_OP_CANCEL_UNLINK:
                 /* blocks for log header bitmap update OR
@@ -387,14 +388,17 @@ static int fsfilt_ext3_commit_async(struct inode *inode, void *h,
 
 static int fsfilt_ext3_commit_wait(struct inode *inode, void *h)
 {
+        journal_t *journal = EXT3_JOURNAL(inode);
         tid_t tid = (tid_t)(long)h;
 
         CDEBUG(D_INODE, "commit wait: %lu\n", (unsigned long) tid);
-        if (is_journal_aborted(EXT3_JOURNAL(inode)))
+        if (unlikely(is_journal_aborted(journal)))
                 return -EIO;
 
         log_wait_commit(EXT3_JOURNAL(inode), tid);
 
+        if (unlikely(is_journal_aborted(journal)))
+                return -EIO;
         return 0;
 }
 
@@ -517,17 +521,29 @@ static int fsfilt_ext3_send_bio(int rw, struct inode *inode, struct bio *bio)
 #else
 static int fsfilt_ext3_send_bio(int rw, struct inode *inode, struct kiobuf *bio)
 {
-        int rc, blocks_per_page;
+        int rc, blk_per_page;
 
         rc = brw_kiovec(rw, 1, &bio, inode->i_dev,
                         KIOBUF_GET_BLOCKS(bio), 1 << inode->i_blkbits);
+        /*
+         * brw_kiovec() returns number of bytes actually written. If error
+         * occurred after something was written, error code is returned though
+         * kiobuf->errno. (See bug 6854.)
+         */
 
-        blocks_per_page = PAGE_SIZE >> inode->i_blkbits;
+        blk_per_page = PAGE_CACHE_SIZE >> inode->i_blkbits;
 
-        if (rc != (1 << inode->i_blkbits) * bio->nr_pages * blocks_per_page) {
-                CERROR("short write?  expected %d, wrote %d\n",
-                       (1 << inode->i_blkbits) * bio->nr_pages *
-                       blocks_per_page, rc);
+        if (rc != (1 << inode->i_blkbits) * bio->nr_pages * blk_per_page) {
+                CERROR("short write?  expected %d, wrote %d (%d)\n",
+                       (1 << inode->i_blkbits) * bio->nr_pages * blk_per_page,
+                       rc, bio->errno);
+        }
+        if (bio->errno != 0) {
+                CERROR("IO error. Wrote %d of %d (%d)\n",
+                       rc,
+                       (1 << inode->i_blkbits) * bio->nr_pages * blk_per_page,
+                       bio->errno);
+                rc = bio->errno;
         }
 
         return rc;
@@ -811,7 +827,7 @@ static int ext3_ext_new_extent_cb(struct ext3_extents_tree *tree,
 
         /*
          * Putting len of the actual extent we just inserted,
-         * we are asking ext3_ext_walk_space() to continue 
+         * we are asking ext3_ext_walk_space() to continue
          * scaning after that block
          */
         cex->ec_len = nex.ee_len;
@@ -1177,190 +1193,17 @@ static int fsfilt_ext3_get_op_len(int op, struct fsfilt_objinfo *fso, int logs)
         return 0;
 }
 
-static const char *op_quotafile[] = { "aquota.user", "aquota.group" };
-
-static int fsfilt_ext3_quotactl(struct super_block *sb,
-                                struct obd_quotactl *oqc)
-{
-        int i, rc = 0, error = 0;
-        struct if_dqinfo *info = (struct if_dqinfo *)&oqc->qc_dqinfo;
-        struct if_dqblk *dqblk = (struct if_dqblk *)&oqc->qc_dqblk;
-        ENTRY;
-
-        /* XXX: quotaoff */
-        return -EOPNOTSUPP;
-
-        for (i = 0; i < MAXQUOTAS; i++) if (Q_TYPESET(oqc, i)) {
-                switch (oqc->qc_cmd) {
-                case Q_QUOTAON: {
-                        rc = sb->s_qcop->quota_on(sb, i, oqc->qc_id,
-                                                  (char *)op_quotafile[i]);
-                        if (rc == -EBUSY)
-                                error = rc;
-                        else if (rc)
-                                GOTO(out, rc);
-                        break;
-                }
-                case Q_QUOTAOFF: {
-                        sb->s_qcop->quota_off(sb, i);
-                        break;
-                }
-                case Q_GETOINFO:
-                case Q_GETINFO:
-                        rc = sb->s_qcop->get_info(sb, i, info);
-                        GOTO(out, rc);
-                case Q_SETQUOTA:
-                        rc = sb->s_qcop->set_dqblk(sb, i, oqc->qc_id, dqblk);
-                        GOTO(out, rc);
-                case Q_GETOQUOTA:
-                case Q_GETQUOTA:
-                        rc = sb->s_qcop->get_dqblk(sb, i, oqc->qc_id, dqblk);
-                        GOTO(out, rc);
-                default:
-                        CERROR("unsupported quotactl command: %d", oqc->qc_cmd);
-                        LBUG();
-                }
-        }
-out:
-        if (!rc && error)
-                rc = error;
-        RETURN(rc);
-}
-
-struct chkquot {
-        struct hlist_node       cq_hash;
-        struct list_head        cq_list;
-        qid_t                   cq_id;
-        short                   cq_type;
-        __u32                   cq_bhardlimit;
-        __u32                   cq_bsoftlimit;
-        qsize_t                 cq_curspace;
-        __u32                   cq_ihardlimit;
-        __u32                   cq_isoftlimit;
-        __u32                   cq_curinodes;
-        __u64                   cq_btime;
-        __u64                   cq_itime;
-};
-                                                                                                                 
-static inline unsigned int const
-chkquot_hash(qid_t id, int type)
-{
-        return (id * (MAXQUOTAS - type)) % NR_DQHASH;
-}
-
-static inline struct chkquot *
-find_chkquot(struct hlist_head *head, qid_t id, int type)
-{
-        struct hlist_node *node, *next;
-        struct chkquot *cq = NULL;
-
-        hlist_for_each_safe(node, next, head) {
-                cq = hlist_entry(node, struct chkquot, cq_hash);
-                if (cq->cq_id == id && cq->cq_type == type)
-                        return cq;
-        }
-
-        return NULL;
-}
-                                                                                                                 
-static struct chkquot *alloc_chkquot(qid_t id, int type)
-{
-        struct chkquot *cq;
-
-        OBD_ALLOC(cq, sizeof(*cq));
-        if (cq) {
-#if 0           /* XXX: 2.4 doesn't support this macro */
-                INIT_HLIST_NODE(&cq->cq_hash);
-#endif
-                INIT_LIST_HEAD(&cq->cq_list);
-                cq->cq_id = id;
-                cq->cq_type = type;
-        }
-
-        return cq;
-}
-                                                                                                                 
-static struct chkquot *
-cqget(struct super_block *sb, struct hlist_head *hash, struct list_head *list,
-      qid_t id, int type, int first_check)
-{
-        struct hlist_head *head = hash + chkquot_hash(id, type);
-        struct if_dqblk dqb;
-        struct chkquot *cq;
-        int rc;
-                                                                                                                 
-        cq = find_chkquot(head, id, type);
-        if (cq)
-                return cq;
-        
-        cq = alloc_chkquot(id, type);
-        if (!cq)
-                return NULL;
-
-        if (!first_check) {
-                rc = sb->s_qcop->get_dqblk(sb, type, id, &dqb);
-                if (!rc) {
-                        cq->cq_bhardlimit = dqb.dqb_bhardlimit;
-                        cq->cq_bsoftlimit = dqb.dqb_bsoftlimit;
-                        cq->cq_ihardlimit = dqb.dqb_ihardlimit;
-                        cq->cq_isoftlimit = dqb.dqb_isoftlimit;
-                        cq->cq_btime = dqb.dqb_btime;
-                        cq->cq_itime = dqb.dqb_itime;
-                }
-        }
-
-        hlist_add_head(&cq->cq_hash, head);
-        list_add_tail(&cq->cq_list, list);
-
-        return cq;
-}
-
-static inline int quota_onoff(struct super_block *sb, int cmd, int type)
-{
-        struct obd_quotactl *oqctl;
-        int rc;
-
-        OBD_ALLOC(oqctl, sizeof(*oqctl));
-
-        oqctl->qc_cmd = cmd;
-        oqctl->qc_id = QFMT_LDISKFS;
-        oqctl->qc_type = type;
-        rc = fsfilt_ext3_quotactl(sb, oqctl);
-
-        OBD_FREE(oqctl, sizeof(*oqctl));
-        return rc;
-}
-
-static inline void read_old_dqinfo(struct super_block *sb, int type,
-                            struct if_dqinfo *dqinfo)
-{
-        struct obd_quotactl *oqctl;
-        int rc;
-        ENTRY;
-
-        OBD_ALLOC(oqctl, sizeof(*oqctl));
-
-        oqctl->qc_cmd = Q_GETINFO;
-        oqctl->qc_type = type;
-        rc = fsfilt_ext3_quotactl(sb, oqctl);
-        if (!rc)
-                memcpy(dqinfo + type, &oqctl->qc_dqinfo, sizeof(*dqinfo));
-
-        OBD_FREE(oqctl, sizeof(*oqctl));
-        EXIT;
-}
-
 static inline struct ext3_group_desc *
 get_group_desc(struct super_block *sb, int group)
 {
         unsigned long desc_block, desc;
         struct ext3_group_desc *gdp;
-                                                                                                                 
+
         desc_block = group / EXT3_DESC_PER_BLOCK(sb);
         desc = group % EXT3_DESC_PER_BLOCK(sb);
         gdp = (struct ext3_group_desc *)
               EXT3_SB(sb)->s_group_desc[desc_block]->b_data;
-                                                                                                                 
+
         return gdp + desc;
 }
 
@@ -1369,10 +1212,10 @@ read_inode_bitmap(struct super_block *sb, unsigned long group)
 {
         struct ext3_group_desc *desc;
         struct buffer_head *bh;
-                                                                                                                 
+
         desc = get_group_desc(sb, group);
         bh = sb_bread(sb, le32_to_cpu(desc->bg_inode_bitmap));
-                                                                                                                 
+
         return bh;
 }
 
@@ -1381,344 +1224,16 @@ static inline struct inode *ext3_iget_inuse(struct super_block *sb,
                                      int index, unsigned long ino)
 {
         struct inode *inode = NULL;
-                                                                                                                 
+
         if (ext3_test_bit(index, bitmap_bh->b_data))
                 inode = iget(sb, ino);
-                                                                                                                 
-        return inode;
-}
-
-struct qchk_ctxt {
-        struct hlist_head       hash[NR_DQHASH];
-        struct list_head        list;
-        int                     first_check[MAXQUOTAS];
-        struct if_dqinfo        dqinfo[MAXQUOTAS];
-};
-
-static int add_inode_quota(struct inode *inode, struct qchk_ctxt *qctxt,
-                           struct obd_quotactl *oqc)
-{
-        struct chkquot *cq;
-        loff_t size = 0;
-        qid_t qid[MAXQUOTAS];
-        int cnt;
-
-        if (!inode)
-                return 0;
-
-        qid[USRQUOTA] = inode->i_uid;
-        qid[GRPQUOTA] = inode->i_gid;
-                                                                                                                 
-        if (S_ISDIR(inode->i_mode) ||
-            S_ISREG(inode->i_mode) ||
-            S_ISLNK(inode->i_mode))
-                size = inode_get_bytes(inode);
-                                                                                                                 
-        for (cnt = 0; cnt < MAXQUOTAS; cnt++) if (Q_TYPESET(oqc, cnt)) {
-                cq = cqget(inode->i_sb, qctxt->hash, &qctxt->list, qid[cnt],
-                           cnt, qctxt->first_check[cnt]);
-                if (!cq)
-                        return -ENOMEM;
-
-                cq->cq_curspace += size;
-                cq->cq_curinodes ++;
-        }
-                                                                                                                 
-        return 0;
-}
-
-static int v2_write_dqheader(struct file *f, int type)
-{
-        int quota_magics[] = V2_INITQMAGICS;
-        int quota_versions[] = V2_INITQVERSIONS;
-        struct v2_disk_dqheader dqhead;
-        ssize_t size;
-        loff_t offset = 0;
-        mm_segment_t fs;
-                                                                                                                 
-        dqhead.dqh_magic = cpu_to_le32(quota_magics[type]);
-        dqhead.dqh_version = cpu_to_le32(quota_versions[type]);
-                                                                                                                 
-        fs = get_fs();
-        set_fs(KERNEL_DS);
-        size = f->f_op->write(f, (char *)&dqhead, sizeof(dqhead), &offset);
-        set_fs(fs);
-        if (size != sizeof(dqhead)) {
-                CERROR("error writing dqhead in quota file");
-                return -1;
-        }
-                                                                                                                 
-        return 0;
-}
-
-/* write dqinfo struct in a new quota file */
-static int v2_write_dqinfo(struct file *f, int type, struct if_dqinfo *info)
-{
-        struct v2_disk_dqinfo dqinfo;
-        int blocks = V2_DQTREEOFF + 1;
-        ssize_t size;
-        loff_t offset = V2_DQINFOOFF;
-        mm_segment_t fs;
-
-        if (info) {
-                dqinfo.dqi_bgrace = cpu_to_le32(info->dqi_bgrace);
-                dqinfo.dqi_igrace = cpu_to_le32(info->dqi_igrace);
-                dqinfo.dqi_flags = cpu_to_le32(info->dqi_flags & DQF_MASK &
-                                               ~DQF_INFO_DIRTY);
-        } else {
-                dqinfo.dqi_bgrace = cpu_to_le32(MAX_DQ_TIME);
-                dqinfo.dqi_igrace = cpu_to_le32(MAX_IQ_TIME);
-                dqinfo.dqi_flags = 0;
-        }
-
-        dqinfo.dqi_blocks = cpu_to_le32(blocks);
-        dqinfo.dqi_free_blk = 0;
-        dqinfo.dqi_free_entry = 0;
-
-        fs = get_fs();
-        set_fs(KERNEL_DS);
-        size = f->f_op->write(f, (char *)&dqinfo, sizeof(dqinfo), &offset);
-        set_fs(fs);
-
-        if (size != sizeof(dqinfo)) {
-                CERROR("error writing dqinfo in quota file");
-                return -1;
-        }
-
-        return 0;
-}
-
-static int create_new_quota_files(struct qchk_ctxt *qctxt,
-                                  struct obd_quotactl *oqc)
-{
-        int i, rc = 0;
-        ENTRY;
-
-        for (i = 0; i < MAXQUOTAS; i++) if (Q_TYPESET(oqc, i)) {
-                struct if_dqinfo *info = qctxt->first_check[i]? NULL:
-                                                      &qctxt->dqinfo[i];
-                struct file *file;
-
-                file = filp_open(op_quotafile[i],
-                                 O_RDWR | O_CREAT | O_TRUNC, 0644);
-                if (IS_ERR(file)) {
-                        rc = PTR_ERR(file);
-                        CERROR("can't create %s file: rc = %d\n",
-                               op_quotafile[i], rc);
-                        GOTO(out, rc);
-                }
-
-                rc = v2_write_dqheader(file, i);
-                if (rc) {
-                        filp_close(file, 0);
-                        GOTO(out, rc = -EIO);
-                }
-
-                rc = v2_write_dqinfo(file, i, info);
-                if (rc) {
-                        filp_close(file, 0);
-                        GOTO(out, rc = -EIO);
-                }
-
-                filp_close(file, 0);
-        }
-
-out:
-        RETURN(rc);
-}
-
-
-static int commit_chkquot(struct super_block *sb, struct qchk_ctxt *qctxt,
-                          struct chkquot *cq)
-{
-        struct obd_quotactl oqc = { 0, };
-        struct timeval now;
-
-        do_gettimeofday(&now);
-
-        if (cq->cq_bsoftlimit &&
-            toqb(cq->cq_curspace) >= cq->cq_bsoftlimit &&
-            !cq->cq_btime)
-                cq->cq_btime = now.tv_sec +
-                               qctxt->dqinfo[cq->cq_type].dqi_bgrace;
-
-        if (cq->cq_isoftlimit &&
-            cq->cq_curinodes >= cq->cq_isoftlimit &&
-            !cq->cq_itime)
-                cq->cq_itime = now.tv_sec +
-                               qctxt->dqinfo[cq->cq_type].dqi_igrace;
-
-        oqc.qc_cmd = Q_SETQUOTA;
-        oqc.qc_type = cq->cq_type;
-        oqc.qc_id = cq->cq_id;
-        oqc.qc_dqblk.dqb_bhardlimit = cq->cq_bhardlimit;
-        oqc.qc_dqblk.dqb_bsoftlimit = cq->cq_bsoftlimit;
-        oqc.qc_dqblk.dqb_curspace = cq->cq_curspace;
-        oqc.qc_dqblk.dqb_ihardlimit = cq->cq_ihardlimit;
-        oqc.qc_dqblk.dqb_isoftlimit = cq->cq_isoftlimit;
-        oqc.qc_dqblk.dqb_curinodes = cq->cq_curinodes;
-        oqc.qc_dqblk.dqb_btime = cq->cq_btime;
-        oqc.qc_dqblk.dqb_itime = cq->cq_itime;
-        oqc.qc_dqblk.dqb_valid = QIF_ALL;
-        
-        return fsfilt_ext3_quotactl(sb, &oqc);
-}
-static int prune_chkquots(struct super_block *sb,
-                          struct qchk_ctxt *qctxt, int error)
-{
-        struct chkquot *cq, *tmp;
-        int rc;
-
-        list_for_each_entry_safe(cq, tmp, &qctxt->list, cq_list) {
-                if (!error) {
-                        rc = commit_chkquot(sb, qctxt, cq);
-                        if (rc)
-                                error = rc;
-                }
-                hlist_del_init(&cq->cq_hash);
-                list_del(&cq->cq_list);
-                OBD_FREE(cq, sizeof(*cq));
-        }
-
-        return error;
-}
-
-static int fsfilt_ext3_quotacheck(struct super_block *sb,
-                                  struct obd_quotactl *oqc)
-{
-        struct ext3_sb_info *sbi = EXT3_SB(sb);
-        int i, group;
-        struct qchk_ctxt *qctxt;
-        struct buffer_head *bitmap_bh = NULL;
-        unsigned long ino;
-        struct inode *inode;
-        int rc;
-        ENTRY;
-
-        /* turn on quota and read dqinfo if existed */
-        OBD_ALLOC(qctxt, sizeof(*qctxt));
-        if (!qctxt)
-                RETURN(-ENOMEM);
-
-        for (i = 0; i < NR_DQHASH; i++)
-                INIT_HLIST_HEAD(&qctxt->hash[i]);
-        INIT_LIST_HEAD(&qctxt->list);
-
-        for (i = 0; i < MAXQUOTAS; i++) if (Q_TYPESET(oqc, i)) {
-                rc = quota_onoff(sb, Q_QUOTAON, i);
-                if (!rc || rc == -EBUSY) 
-                        read_old_dqinfo(sb, i, qctxt->dqinfo);
-                else if (rc == -ENOENT)
-                        qctxt->first_check[i] = 1;
-                else if (rc)
-                        GOTO(out, rc);
-        }
-
-        /* check quota and update in hash */
-        for (group = 0; group < sbi->s_groups_count; group++) {
-                ino = group * sbi->s_inodes_per_group + 1;
-                brelse(bitmap_bh);
-                bitmap_bh = read_inode_bitmap(sb, group);
-
-                for (i = 0; i < sbi->s_inodes_per_group; i++, ino++) {
-                        if (ino < sbi->s_first_ino)
-                                continue;
-
-                        inode = ext3_iget_inuse(sb, bitmap_bh, i, ino);
-                        rc = add_inode_quota(inode, qctxt, oqc);
-                        iput(inode);
-                        if (rc) {
-                                brelse(bitmap_bh);
-                                GOTO(out, rc);
-                        }
-                }
-        }
-        brelse(bitmap_bh);
-
-        /* turn off quota cause we are to dump chkquot to files */
-        quota_onoff(sb, Q_QUOTAOFF, oqc->qc_type);
-
-        rc = create_new_quota_files(qctxt, oqc);
-        if (rc)
-                GOTO(out, rc);
-
-        /* we use vfs functions to set dqblk, so turn quota on */
-        rc = quota_onoff(sb, Q_QUOTAON, oqc->qc_type);
-        if (rc)
-                GOTO(out, rc);
-
-out:
-        /* dump and free chkquot */
-        rc = prune_chkquots(sb, qctxt, rc);
-        OBD_FREE(qctxt, sizeof(*qctxt));
-
-        /* turn off quota, `lfs quotacheck` will turn on when all
-         * nodes quotacheck finish. */
-        quota_onoff(sb, Q_QUOTAOFF, oqc->qc_type);
-
-        if (rc)
-                CERROR("quotacheck failed: rc = %d\n", rc);
-
-        oqc->qc_stat = rc;
-        RETURN(rc);
-}
-
-static int fsfilt_ext3_quotainfo(struct lustre_quota_info *lqi, int type, int cmd)
-{
-        int rc = 0;
-        ENTRY;
 
-        switch (cmd) {
-        case QFILE_CHK:
-                rc = lustre_check_quota_file(lqi, type);
-                break;
-        case QFILE_RD_INFO:
-                rc = lustre_read_quota_info(lqi, type);
-                break;
-        case QFILE_WR_INFO:
-                rc = lustre_write_quota_info(lqi, type);
-                break;
-        case QFILE_INIT_INFO:
-                rc = lustre_init_quota_info(lqi, type);
-                break;
-        default:
-                CERROR("Unsupported admin quota file cmd %d\n", cmd);
-                LBUG();
-                break;
-        }
-        RETURN(rc);
+        return inode;
 }
 
-static int fsfilt_ext3_dquot(struct lustre_dquot *dquot, int cmd)
-{
-        int rc = 0;
-        ENTRY;
-
-        switch (cmd) {
-        case QFILE_RD_DQUOT:
-                rc = lustre_read_dquot(dquot);
-                break;
-        case QFILE_WR_DQUOT:
-                if (dquot->dq_dqb.dqb_ihardlimit || 
-                    dquot->dq_dqb.dqb_isoftlimit ||
-                    dquot->dq_dqb.dqb_bhardlimit || 
-                    dquot->dq_dqb.dqb_bsoftlimit)
-                        clear_bit(DQ_FAKE_B, &dquot->dq_flags);
-                else
-                        set_bit(DQ_FAKE_B, &dquot->dq_flags);
-
-                rc = lustre_commit_dquot(dquot);
-                if (rc >= 0)
-                        rc = 0;
-                break;
-        default:
-                CERROR("Unsupported admin quota file cmd %d\n", cmd);
-                LBUG();
-                break;
-        }
-        RETURN(rc);
-}
+#ifdef HAVE_QUOTA_SUPPORT
+# include "fsfilt_ext3_quota.h"
+#endif
 
 static struct fsfilt_operations fsfilt_ext3_ops = {
         .fs_type                = "ext3",
@@ -1743,10 +1258,12 @@ static struct fsfilt_operations fsfilt_ext3_ops = {
         .fs_setup               = fsfilt_ext3_setup,
         .fs_send_bio            = fsfilt_ext3_send_bio,
         .fs_get_op_len          = fsfilt_ext3_get_op_len,
+#ifdef HAVE_QUOTA_SUPPORT
         .fs_quotactl            = fsfilt_ext3_quotactl,
         .fs_quotacheck          = fsfilt_ext3_quotacheck,
         .fs_quotainfo           = fsfilt_ext3_quotainfo,
         .fs_dquot               = fsfilt_ext3_dquot,
+#endif
 };
 
 static int __init fsfilt_ext3_init(void)
index c1a6640..d8ab4a3 100644 (file)
@@ -27,7 +27,7 @@
 
 #include <linux/lvfs.h>
 
-struct dentry *lvfs_fid2dentry(struct obd_run_ctxt *ctxt, __u64 id,
+struct dentry *lvfs_fid2dentry(struct lvfs_run_ctxt *ctxt, __u64 id,
                                __u32 gen, __u64 gr, void *data)
 {
         return ctxt->cb_ops.l_fid2dentry(id, gen, gr, data);
index 054e6ec..52f0f0d 100644 (file)
@@ -56,8 +56,9 @@ int obd_memmax;
 /* Debugging check only needed during development */
 #ifdef OBD_CTXT_DEBUG
 # define ASSERT_CTXT_MAGIC(magic) LASSERT((magic) == OBD_RUN_CTXT_MAGIC)
-# define ASSERT_NOT_KERNEL_CTXT(msg) LASSERT(!segment_eq(get_fs(), get_ds()))
-# define ASSERT_KERNEL_CTXT(msg) LASSERT(segment_eq(get_fs(), get_ds()))
+# define ASSERT_NOT_KERNEL_CTXT(msg) LASSERTF(!segment_eq(get_fs(), get_ds()),\
+                                              msg)
+# define ASSERT_KERNEL_CTXT(msg) LASSERTF(segment_eq(get_fs(), get_ds()), msg)
 #else
 # define ASSERT_CTXT_MAGIC(magic) do {} while(0)
 # define ASSERT_NOT_KERNEL_CTXT(msg) do {} while(0)
@@ -73,8 +74,8 @@ int obd_memmax;
 #endif
 
 /* push / pop to root of obd store */
-void push_ctxt(struct obd_run_ctxt *save, struct obd_run_ctxt *new_ctx,
-               struct obd_ucred *uc)
+void push_ctxt(struct lvfs_run_ctxt *save, struct lvfs_run_ctxt *new_ctx,
+               struct lvfs_ucred *uc)
 {
         //ASSERT_NOT_KERNEL_CTXT("already in kernel context!\n");
         ASSERT_CTXT_MAGIC(new_ctx->magic);
@@ -97,7 +98,7 @@ void push_ctxt(struct obd_run_ctxt *save, struct obd_run_ctxt *new_ctx,
         save->pwd = dget(current->fs->pwd);
         save->pwdmnt = mntget(current->fs->pwdmnt);
         save->ngroups = current_ngroups;
-        save->ouc.ouc_umask = current->fs->umask;
+        save->luc.luc_umask = current->fs->umask;
 
         LASSERT(save->pwd);
         LASSERT(save->pwdmnt);
@@ -105,26 +106,26 @@ void push_ctxt(struct obd_run_ctxt *save, struct obd_run_ctxt *new_ctx,
         LASSERT(new_ctx->pwdmnt);
 
         if (uc) {
-                save->ouc.ouc_fsuid = current->fsuid;
-                save->ouc.ouc_fsgid = current->fsgid;
-                save->ouc.ouc_cap = current->cap_effective;
-                save->ouc.ouc_suppgid1 = current_groups[0];
-                save->ouc.ouc_suppgid2 = current_groups[1];
-
-                current->fsuid = uc->ouc_fsuid;
-                current->fsgid = uc->ouc_fsgid;
-                current->cap_effective = uc->ouc_cap;
+                save->luc.luc_fsuid = current->fsuid;
+                save->luc.luc_fsgid = current->fsgid;
+                save->luc.luc_cap = current->cap_effective;
+                save->luc.luc_suppgid1 = current_groups[0];
+                save->luc.luc_suppgid2 = current_groups[1];
+
+                current->fsuid = uc->luc_fsuid;
+                current->fsgid = uc->luc_fsgid;
+                current->cap_effective = uc->luc_cap;
                 current_ngroups = 0;
 
-                if (uc->ouc_suppgid1 != -1)
-                        current_groups[current_ngroups++] = uc->ouc_suppgid1;
-                if (uc->ouc_suppgid2 != -1)
-                        current_groups[current_ngroups++] = uc->ouc_suppgid2;
+                if (uc->luc_suppgid1 != -1)
+                        current_groups[current_ngroups++] = uc->luc_suppgid1;
+                if (uc->luc_suppgid2 != -1)
+                        current_groups[current_ngroups++] = uc->luc_suppgid2;
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,4)
-                if (uc->ouc_suppgid1 != -1 && uc->ouc_suppgid2 != -1 &&
-                    (uc->ouc_suppgid1 > uc->ouc_suppgid2)) {
-                        current_groups[0] = uc->ouc_suppgid2;
-                        current_groups[1] = uc->ouc_suppgid1;
+                if (uc->luc_suppgid1 != -1 && uc->luc_suppgid2 != -1 &&
+                    (uc->luc_suppgid1 > uc->luc_suppgid2)) {
+                        current_groups[0] = uc->luc_suppgid2;
+                        current_groups[1] = uc->luc_suppgid1;
                 }
 #endif
         }
@@ -145,8 +146,8 @@ void push_ctxt(struct obd_run_ctxt *save, struct obd_run_ctxt *new_ctx,
 }
 EXPORT_SYMBOL(push_ctxt);
 
-void pop_ctxt(struct obd_run_ctxt *saved, struct obd_run_ctxt *new_ctx,
-              struct obd_ucred *uc)
+void pop_ctxt(struct lvfs_run_ctxt *saved, struct lvfs_run_ctxt *new_ctx,
+              struct lvfs_ucred *uc)
 {
         //printk("pc0");
         ASSERT_CTXT_MAGIC(saved->magic);
@@ -172,14 +173,14 @@ void pop_ctxt(struct obd_run_ctxt *saved, struct obd_run_ctxt *new_ctx,
 
         dput(saved->pwd);
         mntput(saved->pwdmnt);
-        current->fs->umask = saved->ouc.ouc_umask;
+        current->fs->umask = saved->luc.luc_umask;
         if (uc) {
-                current->fsuid = saved->ouc.ouc_fsuid;
-                current->fsgid = saved->ouc.ouc_fsgid;
-                current->cap_effective = saved->ouc.ouc_cap;
+                current->fsuid = saved->luc.luc_fsuid;
+                current->fsgid = saved->luc.luc_fsgid;
+                current->cap_effective = saved->luc.luc_cap;
                 current_ngroups = saved->ngroups;
-                current_groups[0] = saved->ouc.ouc_suppgid1;
-                current_groups[1] = saved->ouc.ouc_suppgid2;
+                current_groups[0] = saved->luc.luc_suppgid1;
+                current_groups[1] = saved->luc.luc_suppgid2;
         }
 
         /*
@@ -338,7 +339,7 @@ int lustre_fsync(struct file *file)
 }
 EXPORT_SYMBOL(lustre_fsync);
 
-struct l_file *l_dentry_open(struct obd_run_ctxt *ctxt, struct l_dentry *de,
+struct l_file *l_dentry_open(struct lvfs_run_ctxt *ctxt, struct l_dentry *de,
                              int flags)
 {
         mntget(ctxt->pwdmnt);
@@ -391,6 +392,53 @@ EXPORT_SYMBOL(l_readdir);
 EXPORT_SYMBOL(obd_memory);
 EXPORT_SYMBOL(obd_memmax);
 
+#ifdef HAVE_OLD_DEV_SET_RDONLY
+void dev_set_rdonly(lvfs_sbdev_type dev, int no_write);
+void dev_clear_rdonly(int no_write);
+int dev_check_rdonly(lvfs_sbdev_type dev);
+#elif !defined(HAVE_CLEAR_RDONLY_ON_PUT)
+void dev_set_rdonly(lvfs_sbdev_type dev);
+void dev_clear_rdonly(lvfs_sbdev_type dev);
+int dev_check_rdonly(lvfs_sbdev_type dev);
+#endif
+
+void lvfs_set_rdonly(lvfs_sbdev_type dev)
+{
+        CDEBUG(D_IOCTL | D_HA, "set dev %lx rdonly\n", (long)dev);
+        lvfs_sbdev_sync(dev);
+#ifdef HAVE_OLD_DEV_SET_RDONLY
+        dev_set_rdonly(dev, 2);
+#else
+        dev_set_rdonly(dev);
+#endif
+}
+
+int lvfs_check_rdonly(lvfs_sbdev_type dev)
+{
+        return dev_check_rdonly(dev);
+}
+
+void lvfs_clear_rdonly(lvfs_sbdev_type dev)
+{
+#ifndef HAVE_CLEAR_RDONLY_ON_PUT
+        CDEBUG(D_IOCTL | D_HA, "unset dev %lx rdonly\n", (long)dev);
+        if (lvfs_check_rdonly(dev)) {
+                lvfs_sbdev_sync(dev);
+#ifdef HAVE_OLD_DEV_SET_RDONLY
+                dev_clear_rdonly(2);
+#else
+                dev_clear_rdonly(dev);
+#endif
+        }
+#else
+        CDEBUG(D_IOCTL | D_HA, "(will unset dev %lx rdonly on put)\n",
+               (long)dev);
+#endif
+}
+EXPORT_SYMBOL(lvfs_set_rdonly);
+EXPORT_SYMBOL(lvfs_check_rdonly);
+EXPORT_SYMBOL(lvfs_clear_rdonly);
+
 static int __init lvfs_linux_init(void)
 {
         RETURN(0);
index cbdb254..a6140d5 100644 (file)
 #include <linux/lustre_lib.h>
 
 /* XXX currently ctxt functions should not be used ?? */
-void push_ctxt(struct obd_run_ctxt *save, struct obd_run_ctxt *new_ctx,
-               struct obd_ucred *uc)
+void push_ctxt(struct lvfs_run_ctxt *save, struct lvfs_run_ctxt *new_ctx,
+               struct lvfs_ucred *uc)
 {
         LBUG();
 }
 
-void pop_ctxt(struct obd_run_ctxt *saved, struct obd_run_ctxt *new_ctx,
-              struct obd_ucred *uc)
+void pop_ctxt(struct lvfs_run_ctxt *saved, struct lvfs_run_ctxt *new_ctx,
+              struct lvfs_ucred *uc)
 {
         LBUG();
 }
index 0791981..87c9b7b 100644 (file)
@@ -6,18 +6,7 @@
  *
  *   This file is part of Lustre, http://www.lustre.org/
  *
- *   Lustre is free software; you can redistribute it and/or
- *   modify it under the terms of version 2 of the GNU General Public
- *   License as published by the Free Software Foundation.
- *
- *   Lustre is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with Lustre; if not, write to the Free Software
- *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *   No redistribution or use is permitted outside of Cluster File Systems, Inc.
  *
  * A kernel module which tests the fsfilt quotacheck API from the OBD setup function.
  */
@@ -204,9 +193,11 @@ static struct obd_ops quotacheck_obd_ops = {
         .o_cleanup     = quotacheck_test_cleanup,
 };
 
+#ifdef LPROCFS
 static struct lprocfs_vars lprocfs_obd_vars[] = { {0} };
 static struct lprocfs_vars lprocfs_module_vars[] = { {0} };
 LPROCFS_INIT_VARS(quotacheck_test, lprocfs_module_vars, lprocfs_obd_vars)
+#endif
 
 static int __init quotacheck_test_init(void)
 {
index 06984c9..1e03388 100644 (file)
@@ -6,18 +6,7 @@
  *
  *   This file is part of Lustre, http://www.lustre.org/
  *
- *   Lustre is free software; you can redistribute it and/or
- *   modify it under the terms of version 2 of the GNU General Public
- *   License as published by the Free Software Foundation.
- *
- *   Lustre is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with Lustre; if not, write to the Free Software
- *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *   No redistribution or use is permitted outside of Cluster File Systems, Inc.
  *
  * A kernel module which tests the fsfilt quotactl API from the OBD setup function.
  */
@@ -269,7 +258,7 @@ static int quotactl_test_4(struct obd_device *obd, struct super_block *sb)
 static int quotactl_run_tests(struct obd_device *obd, struct obd_device *tgt)
 {
         struct super_block *sb;
-        struct obd_run_ctxt saved;
+        struct lvfs_run_ctxt saved;
         int rc;
         ENTRY;
 
@@ -282,7 +271,7 @@ static int quotactl_run_tests(struct obd_device *obd, struct obd_device *tgt)
                 RETURN(-EINVAL);
         }
 
-        push_ctxt(&saved, &tgt->obd_ctxt, NULL);
+        push_ctxt(&saved, &tgt->obd_lvfs_ctxt, NULL);
 
         rc = quotactl_test_1(tgt, sb);
         if (rc)
@@ -301,7 +290,7 @@ static int quotactl_run_tests(struct obd_device *obd, struct obd_device *tgt)
  cleanup:
         quotactl_test_4(tgt, sb);
 
-        pop_ctxt(&saved, &tgt->obd_ctxt, NULL);
+        pop_ctxt(&saved, &tgt->obd_lvfs_ctxt, NULL);
 
         return rc;
 }
@@ -348,9 +337,11 @@ static struct obd_ops quotactl_obd_ops = {
         .o_cleanup     = quotactl_test_cleanup,
 };
 
+#ifdef LPROCFS
 static struct lprocfs_vars lprocfs_obd_vars[] = { {0} };
 static struct lprocfs_vars lprocfs_module_vars[] = { {0} };
 LPROCFS_INIT_VARS(quotactl_test, lprocfs_module_vars, lprocfs_obd_vars)
+#endif
 
 static int __init quotactl_test_init(void)
 {
index e46e120..e39cc9f 100644 (file)
@@ -15,4 +15,4 @@ modulefs_DATA = mdc$(KMODEXT)
 endif
 
 DIST_SOURCES = $(mdc-objs:.o=.c) mdc_internal.h
-MOSTLYCLEANFILES = *.o *.ko *.mod.c
+MOSTLYCLEANFILES := @MOSTLYCLEANFILES@ 
index d49a771..7b83f48 100644 (file)
 #include <linux/obd_class.h>
 #include <linux/lprocfs_status.h>
 
-#ifndef LPROCFS
-static struct lprocfs_vars lprocfs_obd_vars[] = { {0} };
-static struct lprocfs_vars lprocfs_module_vars[] = { {0} };
-#else
+#ifdef LPROCFS
 static struct lprocfs_vars lprocfs_obd_vars[] = {
         { "uuid",            lprocfs_rd_uuid,        0, 0 },
         { "ping",            0, lprocfs_wr_ping,        0 },
@@ -50,6 +47,6 @@ static struct lprocfs_vars lprocfs_module_vars[] = {
         { 0 }
 };
 
+LPROCFS_INIT_VARS(mdc, lprocfs_module_vars, lprocfs_obd_vars)
 #endif /* LPROCFS */
 
-LPROCFS_INIT_VARS(mdc, lprocfs_module_vars, lprocfs_obd_vars)
index 568df2c..c08b0ab 100644 (file)
@@ -1,3 +1,4 @@
+#include <linux/lustre_mds.h>
 void mdc_pack_req_body(struct ptlrpc_request *);
 void mdc_pack_rep_body(struct ptlrpc_request *);
 void mdc_readdir_pack(struct ptlrpc_request *req, __u64 offset, __u32 size,
@@ -66,3 +67,27 @@ static inline void mdc_put_rpc_lock(struct mdc_rpc_lock *lck,
                 up(&lck->rpcl_sem);
         }
 }
+
+/* Quota stuff */
+#ifdef HAVE_QUOTA_SUPPORT
+int mdc_quotacheck(struct obd_export *exp, struct obd_quotactl *oqctl);
+int mdc_poll_quotacheck(struct obd_export *exp, struct if_quotacheck *qchk);
+int mdc_quotactl(struct obd_export *exp, struct obd_quotactl *oqctl);
+#else
+static inline int mdc_quotacheck(struct obd_export *exp, struct obd_quotactl *oqctl)
+{
+        return -ENOTSUPP;
+}
+
+static inline int mdc_poll_quotacheck(struct obd_export *exp, struct if_quotacheck *qchk)
+{
+        return -ENOTSUPP;
+}
+
+static inline int mdc_quotactl(struct obd_export *exp, struct obd_quotactl *oqctl)
+{
+        return -ENOTSUPP;
+}
+#endif
+
+
index 85ce60a..3b9e023 100644 (file)
@@ -19,7 +19,7 @@
  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
-#define DEBUG_SUBSYSTEM S_MDS
+#define DEBUG_SUBSYSTEM S_MDC
 #ifndef __KERNEL__
 # include <fcntl.h>
 # include <liblustre.h>
@@ -249,14 +249,8 @@ void mdc_rename_pack(struct ptlrpc_request *req, int offset,
         rec->rn_fsuid = current->fsuid;
         rec->rn_fsgid = current->fsgid;
         rec->rn_cap = current->cap_effective;
-        if (in_group_p(data->ctxt.gid1))
-                rec->rn_suppgid1 = data->ctxt.gid1;
-        else
-                rec->rn_suppgid1 = -1;
-        if (in_group_p(data->ctxt.gid2))
-                rec->rn_suppgid2 = data->ctxt.gid2;
-        else
-                rec->rn_suppgid2 = -1;
+        rec->rn_suppgid1 = data->ctxt.gid1;
+        rec->rn_suppgid2 = data->ctxt.gid2;
         rec->rn_fid1 = data->fid1;
         rec->rn_fid2 = data->fid2;
         rec->rn_time = data->mod_time;
index 6f16f23..c525a2a 100644 (file)
@@ -164,7 +164,7 @@ int mdc_change_cbdata(struct obd_export *exp, struct ll_fid *fid,
         res_id.name[0] = fid->id;
         res_id.name[1] = fid->generation;
 
-        ldlm_change_cbdata(class_exp2obd(exp)->obd_namespace, &res_id, it, 
+        ldlm_change_cbdata(class_exp2obd(exp)->obd_namespace, &res_id, it,
                            data);
 
         EXIT;
@@ -277,7 +277,6 @@ int mdc_enqueue(struct obd_export *exp,
 
         if (it->it_op & IT_OPEN) {
                 it->it_create_mode |= S_IFREG;
-                it->it_create_mode &= ~current->fs->umask;
 
                 size[2] = sizeof(struct mds_rec_create);
                 size[3] = data->namelen + 1;
@@ -329,7 +328,7 @@ int mdc_enqueue(struct obd_export *exp,
                 reply_buffers = 4;
                 req->rq_replen = lustre_msg_size(4, repsize);
         } else if (it->it_op & (IT_GETATTR | IT_LOOKUP)) {
-                obd_valid valid = OBD_MD_FLNOTOBD | OBD_MD_FLEASIZE;
+                obd_valid valid = OBD_MD_FLGETATTR | OBD_MD_FLEASIZE;
                 size[2] = sizeof(struct mds_body);
                 size[3] = data->namelen + 1;
 
@@ -431,9 +430,8 @@ int mdc_enqueue(struct obd_export *exp,
                 }
 
                 if ((body->valid & OBD_MD_FLEASIZE) != 0) {
-                        /* The eadata is opaque; just check that it is
-                         * there.  Eventually, obd_unpackmd() will check
-                         * the contents */
+                        /* The eadata is opaque; just check that it is there.
+                         * Eventually, obd_unpackmd() will check the contents */
                         eadata = lustre_swab_repbuf(req, 2, body->eadatasize,
                                                     NULL);
                         if (eadata == NULL) {
@@ -631,7 +629,7 @@ int mdc_intent_lock(struct obd_export *exp, struct ll_uctxt *uctxt,
                                sizeof(lockh));
                 }
         }
-        CDEBUG(D_DENTRY, "D_IT dentry %.*s intent: %s status %d disp %x rc %d\n",
+        CDEBUG(D_DENTRY,"D_IT dentry %.*s intent: %s status %d disp %x rc %d\n",
                len, name, ldlm_it2str(it->it_op), it->d.lustre.it_status,
                it->d.lustre.it_disposition, rc);
 
index 1afcc33..04c0485 100644 (file)
@@ -474,7 +474,14 @@ int mdc_close(struct obd_export *exp, struct obdo *oa,
         mod = och->och_mod;
         if (likely(mod != NULL)) {
                 mod->mod_close_req = req;
-                LASSERT(mod->mod_open_req->rq_type != LI_POISON);
+                if (mod->mod_open_req->rq_type == LI_POISON) {
+                        /* FIXME This should be an ASSERT, but until we
+                           figure out why it can be poisoned here, give 
+                           a reasonable return. bug 6155 */
+                        CERROR("LBUG POISONED req %p!\n", mod->mod_open_req);
+                        ptlrpc_free_req(req);
+                        GOTO(out, rc = -EIO);
+                }
                 DEBUG_REQ(D_HA, mod->mod_open_req, "matched open");
         } else {
                 CDEBUG(D_HA, "couldn't find open req; expecting close error\n");
@@ -611,97 +618,6 @@ int mdc_readpage(struct obd_export *exp, struct ll_fid *mdc_fid, __u64 offset,
         return rc;
 }
 
-static int mdc_quotacheck(struct obd_export *exp, struct obd_quotactl *oqctl)
-{
-        struct client_obd *cli = &exp->exp_obd->u.cli;
-        struct ptlrpc_request *req;
-        struct obd_quotactl *body;
-        int size = sizeof(*body);
-        int rc;
-        ENTRY;
-
-        req = ptlrpc_prep_req(class_exp2cliimp(exp), MDS_QUOTACHECK, 1, &size,
-                              NULL);
-        if (!req)
-                GOTO(out, rc = -ENOMEM);
-
-        body = lustre_msg_buf(req->rq_reqmsg, 0, sizeof(*body));
-        memcpy(body, oqctl, sizeof(*body));
-
-        req->rq_replen = lustre_msg_size(0, NULL);
-
-        spin_lock(&cli->cl_qchk_lock);
-        cli->cl_qchk_stat = CL_QUOTACHECKING;
-        spin_unlock(&cli->cl_qchk_lock);
-        rc = ptlrpc_queue_wait(req);
-        if (rc) {
-                spin_lock(&cli->cl_qchk_lock);
-                cli->cl_qchk_stat = rc;
-                spin_unlock(&cli->cl_qchk_lock);
-        }
-out:
-        ptlrpc_req_finished(req);
-        RETURN (rc);
-}
-
-static int mdc_poll_quotacheck(struct obd_export *exp,
-                               struct if_quotacheck *qchk)
-{
-        struct client_obd *cli = &exp->exp_obd->u.cli;
-        int stat;
-        ENTRY;
-                                                                                                                 
-        spin_lock(&cli->cl_qchk_lock);
-        stat = cli->cl_qchk_stat;
-        spin_unlock(&cli->cl_qchk_lock);
-                                                                                                                 
-        qchk->stat = stat;
-        if (stat == CL_QUOTACHECKING) {
-                qchk->stat = -ENODATA;
-                stat = 0;
-        } else if (stat) {
-                if (qchk->stat > CL_QUOTACHECKING)
-                        qchk->stat = stat = -EINTR;
-                                                                                                                 
-                strncpy(qchk->obd_type, LUSTRE_MDS_NAME, 10);
-                qchk->obd_uuid = cli->cl_import->imp_target_uuid;
-        }
-        RETURN(stat);
-}
-
-static int mdc_quotactl(struct obd_export *exp, struct obd_quotactl *oqctl)
-{
-        struct ptlrpc_request *req;
-        struct obd_quotactl *oqc;
-        int size = sizeof(*oqctl);
-        int rc;
-        ENTRY;
-
-        req = ptlrpc_prep_req(class_exp2cliimp(exp), MDS_QUOTACTL, 1, &size,
-                              NULL);
-        if (!req)
-                GOTO(out, rc = -ENOMEM);
-
-        memcpy(lustre_msg_buf(req->rq_reqmsg, 0, sizeof (*oqctl)), oqctl, size);
-
-        req->rq_replen = lustre_msg_size(1, &size);
-
-        rc = ptlrpc_queue_wait(req);
-        if (!rc) {
-                oqc = lustre_swab_repbuf(req, 0, sizeof (*oqc),
-                                         lustre_swab_obd_quotactl);
-                if (oqc == NULL) {
-                        CERROR ("Can't unpack mds_body\n");
-                        GOTO(out, rc = -EPROTO);
-                }
-
-                memcpy(oqctl, oqc, sizeof(*oqctl));
-        }
-out:
-        ptlrpc_req_finished(req);
-        RETURN (rc);
-}
-
 static int mdc_iocontrol(unsigned int cmd, struct obd_export *exp, int len,
                          void *karg, void *uarg)
 {
@@ -715,10 +631,10 @@ static int mdc_iocontrol(unsigned int cmd, struct obd_export *exp, int len,
 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
         MOD_INC_USE_COUNT;
 #else
-       if (!try_module_get(THIS_MODULE)) {
-               CERROR("Can't get module. Is it alive?");
-               return -EINVAL;
-       }
+        if (!try_module_get(THIS_MODULE)) {
+                CERROR("Can't get module. Is it alive?");
+                return -EINVAL;
+        }
 #endif
         switch (cmd) {
         case OBD_IOC_CLIENT_RECOVER:
@@ -754,7 +670,7 @@ out:
 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
         MOD_DEC_USE_COUNT;
 #else
-       module_put(THIS_MODULE);
+        module_put(THIS_MODULE);
 #endif
 
         return rc;
@@ -1040,6 +956,7 @@ int mdc_init_ea_size(struct obd_export *mdc_exp, struct obd_export *lov_exp)
 {
         struct obd_device *obd = mdc_exp->exp_obd;
         struct client_obd *cli = &obd->u.cli;
+        struct lov_stripe_md lsm = { .lsm_magic = LOV_MAGIC };
         struct lov_desc desc;
         __u32 valsize = sizeof(desc);
         int rc, size;
@@ -1054,13 +971,9 @@ int mdc_init_ea_size(struct obd_export *mdc_exp, struct obd_export *lov_exp)
         if (rc)
                 RETURN(rc);
 
-        /* If default_stripe_count is zero we stripe over all OSTs */
-        if (desc.ld_default_stripe_count != 0) {
-                struct lov_stripe_md lsm = { .lsm_magic = LOV_MAGIC,
-                                             .lsm_stripe_count =
-                                                desc.ld_default_stripe_count };
-                size = obd_size_diskmd(lov_exp, &lsm);
-        }
+        lsm.lsm_stripe_count = desc.ld_default_stripe_count;
+        size = obd_size_diskmd(lov_exp, &lsm);
+
         if (cli->cl_default_mds_easize < size)
                 cli->cl_default_mds_easize = size;
 
@@ -1071,9 +984,13 @@ int mdc_init_ea_size(struct obd_export *mdc_exp, struct obd_export *lov_exp)
         RETURN(0);
 }
 
-static int mdc_precleanup(struct obd_device *obd)
+static int mdc_precleanup(struct obd_device *obd, int stage)
 {
         int rc = 0;
+        ENTRY;
+        
+        if (stage < 2) 
+                RETURN(0);
 
         rc = obd_llog_finish(obd, 0);
         if (rc != 0)
index b3b3648..3e8cff6 100644 (file)
@@ -3,7 +3,7 @@ mds-objs := mds_log.o mds_unlink_open.o mds_lov.o handler.o mds_reint.o
 mds-objs += mds_fs.o lproc_mds.o mds_open.o mds_lib.o
 
 ifeq ($(PATCHLEVEL),6)
-mds-objs += quota_context.o quota_master.o
+#mds-objs += quota_master.o
 endif
 
 @INCLUDE_RULES@
index 91277b5..e5bdbcf 100644 (file)
@@ -7,6 +7,5 @@ if MODULES
 modulefs_DATA = mds$(KMODEXT)
 endif
 
-MOSTLYCLEANFILES = *.o *.ko *.mod.c
-DIST_SOURCES := $(mds-objs:%.o=%.c) mds_internal.h 
-DIST_SOURCES += quota_context.c quota_master.c
+MOSTLYCLEANFILES := @MOSTLYCLEANFILES@ 
+DIST_SOURCES := $(mds-objs:%.o=%.c) mds_internal.h
index 7dc7400..4c6e9e7 100644 (file)
@@ -57,8 +57,6 @@
 
 #include "mds_internal.h"
 
-static struct quotacheck_info qchkinfo;
-
 static int mds_intent_policy(struct ldlm_namespace *ns,
                              struct ldlm_lock **lockp, void *req_cookie,
                              ldlm_mode_t mode, int flags, void *data);
@@ -141,7 +139,7 @@ static int mds_sendpage(struct ptlrpc_request *req, struct file *file,
                   req->rq_export->exp_client_uuid.uuid,
                   req->rq_export->exp_connection->c_remote_uuid.uuid);
 
-        ptlrpc_fail_export(req->rq_export);
+        class_fail_export(req->rq_export);
 
         EXIT;
  abort_bulk:
@@ -187,11 +185,6 @@ struct dentry *mds_fid2locked_dentry(struct obd_device *obd, struct ll_fid *fid,
         RETURN(retval);
 }
 
-#ifndef DCACHE_DISCONNECTED
-#define DCACHE_DISCONNECTED DCACHE_NFSD_DISCONNECTED
-#endif
-
-
 /* Look up an entry by inode number. */
 /* this function ONLY returns valid dget'd dentries with an initialized inode
    or errors */
@@ -251,7 +244,7 @@ static int mds_connect(struct lustre_handle *conn, struct obd_device *obd,
                        struct obd_uuid *cluuid, struct obd_connect_data *data)
 {
         struct obd_export *exp;
-        struct mds_export_data *med; /*  */
+        struct mds_export_data *med;
         struct mds_client_data *mcd;
         int rc, abort_recovery;
         ENTRY;
@@ -283,8 +276,10 @@ static int mds_connect(struct lustre_handle *conn, struct obd_device *obd,
         LASSERT(exp);
         med = &exp->exp_mds_data;
 
-        data->ocd_connect_flags &= MDS_CONNECT_SUPPORTED;
-        exp->exp_connect_flags = data->ocd_connect_flags;
+        if (data != NULL) {
+                data->ocd_connect_flags &= MDS_CONNECT_SUPPORTED;
+                exp->exp_connect_flags = data->ocd_connect_flags;
+        }
 
         OBD_ALLOC(mcd, sizeof(*mcd));
         if (!mcd) {
@@ -296,6 +291,7 @@ static int mds_connect(struct lustre_handle *conn, struct obd_device *obd,
         med->med_mcd = mcd;
 
         rc = mds_client_add(obd, &obd->u.mds, med, -1);
+        GOTO(out, rc);
 
 out:
         if (rc) {
@@ -324,7 +320,7 @@ static int mds_destroy_export(struct obd_export *export)
 {
         struct mds_export_data *med;
         struct obd_device *obd = export->exp_obd;
-        struct obd_run_ctxt saved;
+        struct lvfs_run_ctxt saved;
         int rc = 0;
         ENTRY;
 
@@ -334,27 +330,25 @@ static int mds_destroy_export(struct obd_export *export)
         if (obd_uuid_equals(&export->exp_client_uuid, &obd->obd_uuid))
                 GOTO(out, 0);
 
-        push_ctxt(&saved, &obd->obd_ctxt, NULL);
+        push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
         /* Close any open files (which may also cause orphan unlinking). */
         spin_lock(&med->med_open_lock);
         while (!list_empty(&med->med_open_head)) {
                 struct list_head *tmp = med->med_open_head.next;
                 struct mds_file_data *mfd =
                         list_entry(tmp, struct mds_file_data, mfd_list);
-                BDEVNAME_DECLARE_STORAGE(btmp);
-
-                /* bug 1579: fix force-closing for 2.5 */
                 struct dentry *dentry = mfd->mfd_dentry;
 
-                list_del(&mfd->mfd_list);
+                /* Remove mfd handle so it can't be found again.
+                 * We are consuming the mfd_list reference here. */
+                mds_mfd_unlink(mfd, 0);
                 spin_unlock(&med->med_open_lock);
 
                 /* If you change this message, be sure to update
                  * replay_single:test_46 */
-                CDEBUG(D_INODE, "force closing file handle for %.*s (%s:%lu)\n",
-                       dentry->d_name.len, dentry->d_name.name,
-                       ll_bdevname(dentry->d_inode->i_sb, btmp),
-                       dentry->d_inode->i_ino);
+                CDEBUG(D_INODE|D_IOCTL, "%s: force closing file handle for "
+                       "%.*s (ino %lu)\n", obd->obd_name, dentry->d_name.len,
+                       dentry->d_name.name, dentry->d_inode->i_ino);
                 /* child orphan sem protects orphan_dec_test and
                  * is_orphan race, mds_mfd_close drops it */
                 MDS_DOWN_WRITE_ORPHAN_SEM(dentry->d_inode);
@@ -362,13 +356,13 @@ static int mds_destroy_export(struct obd_export *export)
                                    !(export->exp_flags & OBD_OPT_FAILOVER));
 
                 if (rc)
-                        CDEBUG(D_INODE, "Error closing file: %d\n", rc);
+                        CDEBUG(D_INODE|D_IOCTL, "Error closing file: %d\n", rc);
                 spin_lock(&med->med_open_lock);
         }
         spin_unlock(&med->med_open_lock);
-        pop_ctxt(&saved, &obd->obd_ctxt, NULL);
+        pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
 out:
-        mds_client_free(export, !(export->exp_flags & OBD_OPT_FAILOVER));
+        mds_client_free(export);
 
         RETURN(rc);
 }
@@ -668,10 +662,10 @@ static int mds_getattr_name(int offset, struct ptlrpc_request *req,
 {
         struct obd_device *obd = req->rq_export->exp_obd;
         struct ldlm_reply *rep = NULL;
-        struct obd_run_ctxt saved;
+        struct lvfs_run_ctxt saved;
         struct mds_body *body;
         struct dentry *dparent = NULL, *dchild = NULL;
-        struct obd_ucred uc;
+        struct lvfs_ucred uc;
         struct lustre_handle parent_lockh;
         int namesize;
         int rc = 0, cleanup_phase = 0, resent_req = 0;
@@ -704,12 +698,16 @@ static int mds_getattr_name(int offset, struct ptlrpc_request *req,
                 offset = 1;
         }
 
-        uc.ouc_fsuid = body->fsuid;
-        uc.ouc_fsgid = body->fsgid;
-        uc.ouc_cap = body->capability;
-        uc.ouc_suppgid1 = body->suppgid;
-        uc.ouc_suppgid2 = -1;
-        push_ctxt(&saved, &obd->obd_ctxt, &uc);
+#if CRAY_PORTALS
+        uc.luc_fsuid = req->rq_uid;
+#else
+        uc.luc_fsuid = body->fsuid;
+#endif
+        uc.luc_fsgid = body->fsgid;
+        uc.luc_cap = body->capability;
+        uc.luc_suppgid1 = body->suppgid;
+        uc.luc_suppgid2 = -1;
+        push_ctxt(&saved, &obd->obd_lvfs_ctxt, &uc);
         cleanup_phase = 1; /* kernel context */
         intent_set_disposition(rep, DISP_LOOKUP_EXECD);
 
@@ -800,7 +798,7 @@ static int mds_getattr_name(int offset, struct ptlrpc_request *req,
                 }
                 l_dput(dchild);
         case 1:
-                pop_ctxt(&saved, &obd->obd_ctxt, &uc);
+                pop_ctxt(&saved, &obd->obd_lvfs_ctxt, &uc);
         default: ;
         }
         return rc;
@@ -810,10 +808,10 @@ static int mds_getattr(int offset, struct ptlrpc_request *req)
 {
         struct mds_obd *mds = mds_req2mds(req);
         struct obd_device *obd = req->rq_export->exp_obd;
-        struct obd_run_ctxt saved;
+        struct lvfs_run_ctxt saved;
         struct dentry *de;
         struct mds_body *body;
-        struct obd_ucred uc;
+        struct lvfs_ucred uc;
         int rc = 0;
         ENTRY;
 
@@ -824,10 +822,14 @@ static int mds_getattr(int offset, struct ptlrpc_request *req)
                 RETURN(-EFAULT);
         }
 
-        uc.ouc_fsuid = body->fsuid;
-        uc.ouc_fsgid = body->fsgid;
-        uc.ouc_cap = body->capability;
-        push_ctxt(&saved, &obd->obd_ctxt, &uc);
+#if CRAY_PORTALS
+        uc.luc_fsuid = req->rq_uid;
+#else
+        uc.luc_fsuid = body->fsuid;
+#endif
+        uc.luc_fsgid = body->fsgid;
+        uc.luc_cap = body->capability;
+        push_ctxt(&saved, &obd->obd_lvfs_ctxt, &uc);
         de = mds_fid2dentry(mds, &body->fid1, NULL);
         if (IS_ERR(de)) {
                 rc = req->rq_status = PTR_ERR(de);
@@ -845,7 +847,7 @@ static int mds_getattr(int offset, struct ptlrpc_request *req)
         l_dput(de);
         GOTO(out_pop, rc);
 out_pop:
-        pop_ctxt(&saved, &obd->obd_ctxt, &uc);
+        pop_ctxt(&saved, &obd->obd_lvfs_ctxt, &uc);
         return rc;
 }
 
@@ -904,7 +906,7 @@ static int mds_sync(struct ptlrpc_request *req)
 
         body = lustre_swab_reqbuf(req, 0, sizeof(*body), lustre_swab_mds_body);
         if (body == NULL)
-                GOTO(out, rc = -EPROTO);
+                GOTO(out, rc = -EFAULT);
 
         rc = lustre_pack_reply(req, 1, &size, NULL);
         if (rc || OBD_FAIL_CHECK(OBD_FAIL_MDS_SYNC_PACK)) {
@@ -952,9 +954,9 @@ static int mds_readpage(struct ptlrpc_request *req)
         struct dentry *de;
         struct file *file;
         struct mds_body *body, *repbody;
-        struct obd_run_ctxt saved;
+        struct lvfs_run_ctxt saved;
         int rc, size = sizeof(*repbody);
-        struct obd_ucred uc;
+        struct lvfs_ucred uc;
         ENTRY;
 
         if (OBD_FAIL_CHECK(OBD_FAIL_MDS_READPAGE_PACK))
@@ -970,10 +972,14 @@ static int mds_readpage(struct ptlrpc_request *req)
         if (body == NULL)
                 GOTO (out, rc = -EFAULT);
 
-        uc.ouc_fsuid = body->fsuid;
-        uc.ouc_fsgid = body->fsgid;
-        uc.ouc_cap = body->capability;
-        push_ctxt(&saved, &obd->obd_ctxt, &uc);
+#if CRAY_PORTALS
+        uc.luc_fsuid = req->rq_uid;
+#else
+        uc.luc_fsuid = body->fsuid;
+#endif
+        uc.luc_fsgid = body->fsgid;
+        uc.luc_cap = body->capability;
+        push_ctxt(&saved, &obd->obd_lvfs_ctxt, &uc);
         de = mds_fid2dentry(&obd->u.mds, &body->fid1, &mnt);
         if (IS_ERR(de))
                 GOTO(out_pop, rc = PTR_ERR(de));
@@ -1012,206 +1018,7 @@ static int mds_readpage(struct ptlrpc_request *req)
 out_file:
         filp_close(file, 0);
 out_pop:
-        pop_ctxt(&saved, &obd->obd_ctxt, &uc);
-out:
-        req->rq_status = rc;
-        RETURN(0);
-}
-
-static int mds_quotacheck_callback(struct obd_export *exp,
-                                   struct obd_quotactl *oqctl)
-{
-        struct ptlrpc_request *req;
-        struct obd_quotactl *body;
-        int rc, size = sizeof(*oqctl);
-
-        req = ptlrpc_prep_req(exp->exp_imp_reverse, OBD_QC_CALLBACK,
-                              1, &size, NULL);
-        if (!req)
-                RETURN(-ENOMEM);
-
-        body = lustre_msg_buf(req->rq_reqmsg, 0, sizeof(*body));
-        memcpy(body, oqctl, sizeof(*oqctl));
-
-        req->rq_replen = lustre_msg_size(0, NULL);
-
-        rc = ptlrpc_queue_wait(req);
-        ptlrpc_req_finished(req);
-
-        RETURN(rc);
-}
-
-
-static int mds_quotacheck_thread(void *data)
-{
-        unsigned long flags;
-        struct quotacheck_info *qchki = data;
-        struct obd_device *obd;
-        struct obd_export *exp;
-        struct obd_quotactl *oqctl;
-        struct obd_run_ctxt saved;
-        int rc;
-                                                                                                                 
-        lock_kernel();
-        ptlrpc_daemonize();
-                                                                                                                 
-        SIGNAL_MASK_LOCK(current, flags);
-        sigfillset(&current->blocked);
-        RECALC_SIGPENDING;
-        SIGNAL_MASK_UNLOCK(current, flags);
-
-        THREAD_NAME(current->comm, sizeof(current->comm) - 1, "%s", "quotacheck");
-        unlock_kernel();
-
-        complete(&qchki->qi_starting);
-
-        exp = qchki->qi_exp;
-        oqctl = &qchki->qi_oqctl;
-        obd = exp->exp_obd;
-
-        push_ctxt(&saved, &obd->obd_ctxt, NULL);
-
-        rc = fsfilt_quotacheck(obd, obd->u.mds.mds_sb, oqctl);
-        if (rc)
-                CERROR("%s: fsfilt_quotacheck: %d\n", obd->obd_name, rc);
-
-        pop_ctxt(&saved, &obd->obd_ctxt, NULL);
-
-        rc = mds_quotacheck_callback(exp, oqctl);
-
-        atomic_inc(&obd->u.mds.mds_quotachecking);
-
-        return rc;
-}
-
-static int mds_quotacheck(struct ptlrpc_request *req)
-{
-        struct obd_device *obd = req->rq_export->exp_obd;
-        struct mds_obd *mds = &obd->u.mds;
-        struct obd_quotactl *oqctl;
-        int rc = 0;
-        ENTRY;
-
-        oqctl = lustre_swab_reqbuf(req, 0, sizeof(*oqctl),
-                                   lustre_swab_obd_quotactl);
-        if (oqctl == NULL)
-                RETURN(-EPROTO);
-
-        rc = lustre_pack_reply(req, 0, NULL, NULL);
-        if (rc) {
-                CERROR("mds: out of memory while packing quotacheck reply\n");
-                RETURN(rc);
-        }
-
-        /* XXX: quotaoff */
-        GOTO(out, rc = -EOPNOTSUPP);
-
-        if (!atomic_dec_and_test(&mds->mds_quotachecking)) {
-                atomic_inc(&mds->mds_quotachecking);
-                GOTO(out, rc = -EBUSY);
-        }
-
-        init_completion(&qchkinfo.qi_starting);
-        qchkinfo.qi_exp = req->rq_export;
-        memcpy(&qchkinfo.qi_oqctl, oqctl, sizeof(*oqctl));
-
-        rc = init_admin_quotafiles(obd, &qchkinfo.qi_oqctl);
-        if (rc) {
-                CERROR("init_admin_quotafiles failed: %d\n", rc);
-                atomic_inc(&mds->mds_quotachecking);
-                GOTO(out, rc);
-        }
-                
-        rc = kernel_thread(mds_quotacheck_thread, &qchkinfo, CLONE_VM|CLONE_FILES);
-        if (rc < 0) {
-                CERROR("%s: error starting mds_quotacheck_thread: %d\n",
-                       obd->obd_name, rc);
-                atomic_inc(&mds->mds_quotachecking);
-        } else {
-                CDEBUG(D_INFO, "%s: mds_quotacheck_thread: %d\n",
-                       obd->obd_name, rc);
-                wait_for_completion(&qchkinfo.qi_starting);
-                rc = 0;
-        }
-out:
-        req->rq_status = rc;
-        RETURN(0);
-}
-
-static int mds_quotactl(struct ptlrpc_request *req)
-{
-        struct obd_device *obd = req->rq_export->exp_obd;
-        struct obd_quotactl *oqctl, *repoqc;
-        struct obd_run_ctxt saved;
-        int rc = 0, size = sizeof(*repoqc);
-        ENTRY;
-
-        oqctl = lustre_swab_reqbuf(req, 0, sizeof(*oqctl),
-                                   lustre_swab_obd_quotactl);
-        if (oqctl == NULL)
-                RETURN(-EPROTO);
-
-        rc = lustre_pack_reply(req, 1, &size, NULL);
-        if (rc)
-                RETURN(rc);
-
-        /* XXX: quotaoff */
-        GOTO(out, rc = -EOPNOTSUPP);
-
-        repoqc = lustre_msg_buf(req->rq_repmsg, 0, sizeof(*repoqc));
-        memcpy(repoqc, oqctl, sizeof(*repoqc));
-
-        switch (repoqc->qc_cmd) {
-        case Q_QUOTAON:
-                rc = mds_quota_on(obd, repoqc);
-                break;
-        case Q_QUOTAOFF:
-                mds_quota_off(obd, repoqc);
-                break;
-        case Q_SETINFO:
-                rc = mds_set_dqinfo(obd, repoqc);
-                break;
-        case Q_GETINFO:
-                rc = mds_get_dqinfo(obd, repoqc);
-                break;
-        case Q_SETQUOTA:
-                rc = mds_set_dqblk(obd, repoqc);
-                break;
-        case Q_GETQUOTA:
-                rc = mds_get_dqblk(obd, repoqc);
-                break;
-        case Q_GETOINFO:
-        case Q_GETOQUOTA:
-                break;
-        default:
-                CERROR("%s: unsupported mds_quotactl command: %d\n",
-                       obd->obd_name, repoqc->qc_cmd);
-                LBUG();
-        }
-
-        if (rc) {
-                CDEBUG(D_INFO, "mds_quotactl admin op failed: rc = %d\n", rc);
-                GOTO(out, rc);
-        }
-
-        if (repoqc->qc_cmd == Q_QUOTAON || repoqc->qc_cmd == Q_QUOTAOFF ||
-            Q_GETOCMD(repoqc) || repoqc->qc_cmd == Q_GETQUOTA) {
-                struct obd_quotactl *loqc = repoqc;
-
-                if (repoqc->qc_cmd == Q_GETQUOTA)
-                        loqc = oqctl;
-
-                push_ctxt(&saved, &obd->obd_ctxt, NULL);
-                rc = fsfilt_quotactl(obd, obd->u.mds.mds_sb, loqc);
-                pop_ctxt(&saved, &obd->obd_ctxt, NULL);
-
-                if (!rc && loqc->qc_cmd == Q_GETQUOTA) {
-                        repoqc->qc_dqblk.dqb_curinodes +=
-                                                loqc->qc_dqblk.dqb_curinodes;
-                        repoqc->qc_dqblk.dqb_curspace +=
-                                                loqc->qc_dqblk.dqb_curspace;
-                }
-        }
+        pop_ctxt(&saved, &obd->obd_lvfs_ctxt, &uc);
 out:
         req->rq_status = rc;
         RETURN(0);
@@ -1617,12 +1424,12 @@ int mds_update_server_data(struct obd_device *obd, int force_sync)
         struct mds_obd *mds = &obd->u.mds;
         struct mds_server_data *msd = mds->mds_server_data;
         struct file *filp = mds->mds_rcvd_filp;
-        struct obd_run_ctxt saved;
+        struct lvfs_run_ctxt saved;
         loff_t off = 0;
         int rc;
         ENTRY;
 
-        push_ctxt(&saved, &obd->obd_ctxt, NULL);
+        push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
         msd->msd_last_transno = cpu_to_le64(mds->mds_last_transno);
 
         CDEBUG(D_SUPER, "MDS mount_count is "LPU64", last_transno is "LPU64"\n",
@@ -1630,7 +1437,7 @@ int mds_update_server_data(struct obd_device *obd, int force_sync)
         rc = fsfilt_write_record(obd, filp, msd, sizeof(*msd), &off,force_sync);
         if (rc)
                 CERROR("error writing MDS server data: rc = %d\n", rc);
-        pop_ctxt(&saved, &obd->obd_ctxt, NULL);
+        pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
 
         RETURN(rc);
 }
@@ -1686,17 +1493,14 @@ static int mds_setup(struct obd_device *obd, obd_count len, void *buf)
 
         CDEBUG(D_SUPER, "%s: mnt = %p\n", lustre_cfg_string(lcfg, 1), mnt);
 
-        LASSERT(!ll_check_rdonly(ll_sbdev(mnt->mnt_sb)));
+        LASSERT(!lvfs_check_rdonly(lvfs_sbdev(mnt->mnt_sb)));
 
-        sema_init(&mds->mds_quota_info.qi_sem, 1);
         sema_init(&mds->mds_orphan_recovery_sem, 1);
         sema_init(&mds->mds_epoch_sem, 1);
         spin_lock_init(&mds->mds_transno_lock);
         mds->mds_max_mdsize = sizeof(struct lov_mds_md);
         mds->mds_max_cookiesize = sizeof(struct llog_cookie);
 
-        atomic_set(&mds->mds_quotachecking, 1);
-
         sprintf(ns_name, "mds-%s", obd->obd_uuid.uuid);
         obd->obd_namespace = ldlm_namespace_new(ns_name, LDLM_NAMESPACE_SERVER);
         if (obd->obd_namespace == NULL) {
@@ -1707,7 +1511,8 @@ static int mds_setup(struct obd_device *obd, obd_count len, void *buf)
 
         rc = mds_fs_setup(obd, mnt);
         if (rc) {
-                CERROR("MDS filesystem method init failed: rc = %d\n", rc);
+                CERROR("%s: MDS filesystem method init failed: rc = %d\n",
+                       obd->obd_name, rc);
                 GOTO(err_ns, rc);
         }
 
@@ -1734,6 +1539,8 @@ static int mds_setup(struct obd_device *obd, obd_count len, void *buf)
                            "mds_ldlm_client", &obd->obd_ldlm_client);
         obd->obd_replayable = 1;
 
+        mds_quota_setup(mds);
+
         rc = mds_postsetup(obd);
         if (rc)
                 GOTO(err_fs, rc);
@@ -1763,13 +1570,8 @@ static int mds_setup(struct obd_device *obd, obd_count len, void *buf)
                               obd->obd_replayable ? "enabled" : "disabled");
         }
 
-        sema_init(&mds->mds_quota_info.qi_sem, 1);
-        rc = qctxt_init(&mds->mds_quota_ctxt, mds->mds_sb, dqacq_handler);
-        if (rc) {
-                CERROR("initialize quota context failed! (rc:%d)\n", rc);
-                qctxt_cleanup(&mds->mds_quota_ctxt, 0);
-                GOTO(err_fs, rc);
-        }
+        ldlm_timeout = 6;
+        ping_evictor_start();
 
         RETURN(0);
 
@@ -1801,16 +1603,16 @@ static int mds_postsetup(struct obd_device *obd)
                 RETURN(rc);
 
         if (mds->mds_profile) {
-                struct obd_run_ctxt saved;
+                struct lvfs_run_ctxt saved;
                 struct lustre_profile *lprof;
                 struct config_llog_instance cfg;
 
                 cfg.cfg_instance = NULL;
                 cfg.cfg_uuid = mds->mds_lov_uuid;
-                push_ctxt(&saved, &obd->obd_ctxt, NULL);
+                push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
                 rc = class_config_parse_llog(llog_get_context(obd, LLOG_CONFIG_ORIG_CTXT), 
                                              mds->mds_profile, &cfg);
-                pop_ctxt(&saved, &obd->obd_ctxt, NULL);
+                pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
                 switch (rc) {
                 case 0:
                         break;
@@ -1851,6 +1653,10 @@ int mds_postrecov(struct obd_device *obd)
 {
         struct mds_obd *mds = &obd->u.mds;
         int rc, item = 0;
+        ENTRY;
+
+        if (obd->obd_fail) 
+                RETURN(0);
 
         LASSERT(!obd->obd_recovering);
         LASSERT(llog_get_context(obd, LLOG_MDS_OST_ORIG_CTXT) != NULL);
@@ -1892,7 +1698,7 @@ int mds_postrecov(struct obd_device *obd)
         }
 
 out:
-        RETURN(rc < 0 ? rc: item);
+        RETURN(rc < 0 ? rc : item);
 
 err_llog:
         /* cleanup all llogging subsystems */
@@ -1910,7 +1716,7 @@ int mds_lov_clean(struct obd_device *obd)
         if (mds->mds_profile) {
                 char * cln_prof;
                 struct config_llog_instance cfg;
-                struct obd_run_ctxt saved;
+                struct lvfs_run_ctxt saved;
                 int len = strlen(mds->mds_profile) + sizeof("-clean") + 1;
 
                 OBD_ALLOC(cln_prof, len);
@@ -1919,10 +1725,10 @@ int mds_lov_clean(struct obd_device *obd)
                 cfg.cfg_instance = NULL;
                 cfg.cfg_uuid = mds->mds_lov_uuid;
 
-                push_ctxt(&saved, &obd->obd_ctxt, NULL);
+                push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
                 class_config_parse_llog(llog_get_context(obd, LLOG_CONFIG_ORIG_CTXT), 
                                         cln_prof, &cfg);
-                pop_ctxt(&saved, &obd->obd_ctxt, NULL);
+                pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
 
                 OBD_FREE(cln_prof, len);
                 OBD_FREE(mds->mds_profile, strlen(mds->mds_profile) + 1);
@@ -1931,30 +1737,38 @@ int mds_lov_clean(struct obd_device *obd)
         RETURN(0);
 }
 
-static int mds_precleanup(struct obd_device *obd)
+static int mds_precleanup(struct obd_device *obd, int stage)
 {
         int rc = 0;
         ENTRY;
 
-        mds_lov_set_cleanup_flags(obd);
-        target_cleanup_recovery(obd);
-        mds_lov_disconnect(obd);
-        mds_lov_clean(obd);
-        llog_cleanup(llog_get_context(obd, LLOG_CONFIG_ORIG_CTXT));
+        switch (stage) {
+        case 1:
+                mds_lov_set_cleanup_flags(obd);
+                target_cleanup_recovery(obd);
+                break;
+        case 2:
+                mds_lov_disconnect(obd);
+                mds_lov_clean(obd);
+                llog_cleanup(llog_get_context(obd, LLOG_CONFIG_ORIG_CTXT));
+                rc = obd_llog_finish(obd, 0);
+        }
         RETURN(rc);
 }
 
 static int mds_cleanup(struct obd_device *obd)
 {
         struct mds_obd *mds = &obd->u.mds;
-        ll_sbdev_type save_dev;
+        lvfs_sbdev_type save_dev;
         int must_relock = 0;
         ENTRY;
 
+        ping_evictor_stop();
+
         if (mds->mds_sb == NULL)
                 RETURN(0);
-        save_dev = ll_sbdev(mds->mds_sb);
-        
+        save_dev = lvfs_sbdev(mds->mds_sb);
+
         if (mds->mds_osc_exp)
                 /* lov export was disconnected by mds_lov_clean;
                    we just need to drop our ref */
@@ -1962,7 +1776,7 @@ static int mds_cleanup(struct obd_device *obd)
 
         lprocfs_obd_cleanup(obd);
 
-        qctxt_cleanup(&mds->mds_quota_ctxt, 0);
+        mds_quota_cleanup(mds);
 
         mds_update_server_data(obd, 1);
         if (mds->mds_lov_objids != NULL) {
@@ -1984,8 +1798,6 @@ static int mds_cleanup(struct obd_device *obd)
                 must_relock++;
         }
 
-        obd_llog_finish(obd, 0);
-
         mntput(mds->mds_vfsmnt);
         mds->mds_sb = NULL;
 
@@ -1998,8 +1810,8 @@ static int mds_cleanup(struct obd_device *obd)
         }
         spin_unlock_bh(&obd->obd_processing_task_lock);
 
-        ll_clear_rdonly(save_dev);
-        
+        lvfs_clear_rdonly(save_dev);
+
         if (must_relock)
                 lock_kernel();
 
@@ -2047,7 +1859,8 @@ static void fixup_handle_for_resent_req(struct ptlrpc_request *req,
         /* If the xid matches, then we know this is a resent request,
          * and allow it. (It's probably an OPEN, for which we don't
          * send a lock */
-        if (req->rq_xid == exp->exp_mds_data.med_mcd->mcd_last_xid)
+        if (req->rq_xid == 
+            le64_to_cpu(exp->exp_mds_data.med_mcd->mcd_last_xid))
                 return;
 
         /* This remote handle isn't enqueued, so we never received or
index 74795b4..fe5d6f5 100644 (file)
 #include <linux/obd_class.h>
 #include <linux/lprocfs_status.h>
 
-#ifndef LPROCFS
-struct lprocfs_vars lprocfs_mds_obd_vars[]  = { {0} };
-struct lprocfs_vars lprocfs_mds_module_vars[] = { {0} };
-struct lprocfs_vars lprocfs_mdt_obd_vars[] = { {0} };
-struct lprocfs_vars lprocfs_mdt_module_vars[] = { {0} };
-
-#else
+#include "mds_internal.h"
 
+#ifdef LPROCFS
 static int lprocfs_mds_rd_mntdev(char *page, char **start, off_t off, int count,
                                  int *eof, void *data)
 {
@@ -49,121 +44,6 @@ static int lprocfs_mds_rd_mntdev(char *page, char **start, off_t off, int count,
         return snprintf(page, count, "%s\n",obd->u.mds.mds_vfsmnt->mnt_devname);
 }
 
-static int lprocfs_mds_rd_bunit(char *page, char **start, off_t off, int count, 
-                                int *eof, void *data)
-{
-        struct obd_device *obd = (struct obd_device *)data;
-        LASSERT(obd != NULL);
-
-        return snprintf(page, count, "%lu\n", 
-                        obd->u.mds.mds_quota_ctxt.lqc_bunit_sz);
-}
-
-static int lprocfs_mds_rd_iunit(char *page, char **start, off_t off, int count, 
-                                int *eof, void *data)
-{
-        struct obd_device *obd = (struct obd_device *)data;
-        LASSERT(obd != NULL);
-
-        return snprintf(page, count, "%lu\n", 
-                        obd->u.mds.mds_quota_ctxt.lqc_iunit_sz);
-}
-
-static int lprocfs_mds_wr_bunit(struct file *file, const char *buffer,
-                                unsigned long count, void *data)
-{
-        struct obd_device *obd = (struct obd_device *)data;
-        int val, rc = 0;
-        LASSERT(obd != NULL);
-
-        rc = lprocfs_write_helper(buffer, count, &val);
-        if (rc)
-                return rc;
-
-        if (val % QUOTABLOCK_SIZE ||
-            val <= obd->u.mds.mds_quota_ctxt.lqc_btune_sz)
-                return -EINVAL;
-
-        obd->u.mds.mds_quota_ctxt.lqc_bunit_sz = val;
-        return count;
-}
-
-static int lprocfs_mds_wr_iunit(struct file *file, const char *buffer,
-                                unsigned long count, void *data)
-{
-        struct obd_device *obd = (struct obd_device *)data;
-        int val, rc = 0;
-        LASSERT(obd != NULL);
-
-        rc = lprocfs_write_helper(buffer, count, &val);
-        if (rc)
-                return rc;
-
-        if (val <= obd->u.mds.mds_quota_ctxt.lqc_itune_sz)
-                return -EINVAL;
-
-        obd->u.mds.mds_quota_ctxt.lqc_iunit_sz = val;
-        return count;
-}
-
-static int lprocfs_mds_rd_btune(char *page, char **start, off_t off, int count, 
-                                int *eof, void *data)
-{
-        struct obd_device *obd = (struct obd_device *)data;
-        LASSERT(obd != NULL);
-
-        return snprintf(page, count, "%lu\n", 
-                        obd->u.mds.mds_quota_ctxt.lqc_btune_sz);
-}
-
-static int lprocfs_mds_rd_itune(char *page, char **start, off_t off, int count, 
-                                int *eof, void *data)
-{
-        struct obd_device *obd = (struct obd_device *)data;
-        LASSERT(obd != NULL);
-
-        return snprintf(page, count, "%lu\n", 
-                        obd->u.mds.mds_quota_ctxt.lqc_itune_sz);
-}
-
-static int lprocfs_mds_wr_btune(struct file *file, const char *buffer,
-                                unsigned long count, void *data)
-{
-        struct obd_device *obd = (struct obd_device *)data;
-        int val, rc = 0;
-        LASSERT(obd != NULL);
-
-        rc = lprocfs_write_helper(buffer, count, &val);
-        if (rc)
-                return rc;
-        
-        if (val <= QUOTABLOCK_SIZE * MIN_QLIMIT || val % QUOTABLOCK_SIZE || 
-            val >= obd->u.mds.mds_quota_ctxt.lqc_bunit_sz)
-                return -EINVAL;
-
-        obd->u.mds.mds_quota_ctxt.lqc_btune_sz = val;
-        return count;
-}
-
-static int lprocfs_mds_wr_itune(struct file *file, const char *buffer,
-                                unsigned long count, void *data)
-{
-        struct obd_device *obd = (struct obd_device *)data;
-        int val, rc = 0;
-        LASSERT(obd != NULL);
-
-        rc = lprocfs_write_helper(buffer, count, &val);
-        if (rc)
-                return rc;
-        
-        if (val <= MIN_QLIMIT || 
-            val >= obd->u.mds.mds_quota_ctxt.lqc_iunit_sz)
-                return -EINVAL;
-
-        obd->u.mds.mds_quota_ctxt.lqc_itune_sz = val;
-        return count;
-}
-
 struct lprocfs_vars lprocfs_mds_obd_vars[] = {
         { "uuid",         lprocfs_rd_uuid,        0, 0 },
         { "blocksize",    lprocfs_rd_blksize,     0, 0 },
@@ -177,10 +57,12 @@ struct lprocfs_vars lprocfs_mds_obd_vars[] = {
         { "recovery_status", lprocfs_obd_rd_recovery_status, 0, 0 },
         { "evict_client", 0, lprocfs_wr_evict_client, 0 },
         { "num_exports",  lprocfs_rd_num_exports, 0, 0 },
+#ifdef HAVE_QUOTA_SUPPORT
         { "quota_bunit_sz", lprocfs_mds_rd_bunit, lprocfs_mds_wr_bunit, 0 },
         { "quota_btune_sz", lprocfs_mds_rd_btune, lprocfs_mds_wr_btune, 0 },
         { "quota_iunit_sz", lprocfs_mds_rd_iunit, lprocfs_mds_wr_iunit, 0 },
         { "quota_itune_sz", lprocfs_mds_rd_itune, lprocfs_mds_wr_itune, 0 },
+#endif
         { 0 }
 };
 
@@ -199,6 +81,6 @@ struct lprocfs_vars lprocfs_mdt_module_vars[] = {
         { 0 }
 };
 
-#endif
 LPROCFS_INIT_VARS(mds, lprocfs_mds_module_vars, lprocfs_mds_obd_vars);
 LPROCFS_INIT_VARS(mdt, lprocfs_mdt_module_vars, lprocfs_mdt_obd_vars);
+#endif
index a71a5c2..45d0859 100644 (file)
@@ -32,7 +32,7 @@
 #include <linux/kmod.h>
 #include <linux/version.h>
 #include <linux/sched.h>
-#include <linux/quotaops.h>
+#include <linux/lustre_quota.h>
 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0))
 #include <linux/mount.h>
 #endif
@@ -47,7 +47,6 @@
 
 /* This limit is arbitrary, but for now we fit it in 1 page (32k clients) */
 #define MDS_MAX_CLIENTS (PAGE_SIZE * 8)
-#define MDS_MAX_CLIENT_WORDS (MDS_MAX_CLIENTS / sizeof(unsigned long))
 
 #define LAST_RCVD "last_rcvd"
 #define LOV_OBJID "lov_objid"
@@ -56,6 +55,9 @@
  * in the last_rcvd file if cl_off is -1 (i.e. a new client).
  * Otherwise, we have just read the data from the last_rcvd file and
  * we know its offset.
+ *
+ * It should not be possible to fail adding an existing client - otherwise
+ * mds_init_server_data() callsite needs to be fixed.
  */
 int mds_client_add(struct obd_device *obd, struct mds_obd *mds,
                    struct mds_export_data *med, int cl_idx)
@@ -65,6 +67,7 @@ int mds_client_add(struct obd_device *obd, struct mds_obd *mds,
         ENTRY;
 
         LASSERT(bitmap != NULL);
+        LASSERTF(cl_idx > -2, "%d\n", cl_idx);
 
         /* XXX if mcd_uuid were a real obd_uuid, I could use obd_uuid_equals */
         if (!strcmp(med->med_mcd->mcd_uuid, obd->obd_uuid.uuid))
@@ -76,9 +79,10 @@ int mds_client_add(struct obd_device *obd, struct mds_obd *mds,
         if (new_client) {
                 cl_idx = find_first_zero_bit(bitmap, MDS_MAX_CLIENTS);
         repeat:
-                if (cl_idx >= MDS_MAX_CLIENTS) {
+                if (cl_idx >= MDS_MAX_CLIENTS ||
+                    OBD_FAIL_CHECK_ONCE(OBD_FAIL_MDS_CLIENT_ADD)) {
                         CERROR("no room for clients - fix MDS_MAX_CLIENTS\n");
-                        return -ENOMEM;
+                        return -EOVERFLOW;
                 }
                 if (test_and_set_bit(cl_idx, bitmap)) {
                         cl_idx = find_next_zero_bit(bitmap, MDS_MAX_CLIENTS,
@@ -96,79 +100,92 @@ int mds_client_add(struct obd_device *obd, struct mds_obd *mds,
         CDEBUG(D_INFO, "client at idx %d with UUID '%s' added\n",
                cl_idx, med->med_mcd->mcd_uuid);
 
-        med->med_idx = cl_idx;
-        med->med_off = le32_to_cpu(mds->mds_server_data->msd_client_start) +
+        med->med_lr_idx = cl_idx;
+        med->med_lr_off = le32_to_cpu(mds->mds_server_data->msd_client_start) +
                 (cl_idx * le16_to_cpu(mds->mds_server_data->msd_client_size));
+        LASSERTF(med->med_lr_off > 0, "med_lr_off = %llu\n", med->med_lr_off);
 
         if (new_client) {
-                struct obd_run_ctxt saved;
-                loff_t off = med->med_off;
+                struct lvfs_run_ctxt saved;
+                loff_t off = med->med_lr_off;
                 struct file *file = mds->mds_rcvd_filp;
                 int rc;
 
-                push_ctxt(&saved, &obd->obd_ctxt, NULL);
+                push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
                 rc = fsfilt_write_record(obd, file, med->med_mcd,
                                          sizeof(*med->med_mcd), &off, 1);
-                pop_ctxt(&saved, &obd->obd_ctxt, NULL);
+                pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
 
                 if (rc)
                         return rc;
                 CDEBUG(D_INFO, "wrote client mcd at idx %u off %llu (len %u)\n",
-                       med->med_idx, med->med_off,
+                       med->med_lr_idx, med->med_lr_off,
                        (unsigned int)sizeof(*med->med_mcd));
         }
         return 0;
 }
 
-int mds_client_free(struct obd_export *exp, int clear_client)
+int mds_client_free(struct obd_export *exp)
 {
         struct mds_export_data *med = &exp->exp_mds_data;
         struct mds_obd *mds = &exp->exp_obd->u.mds;
         struct obd_device *obd = exp->exp_obd;
         struct mds_client_data zero_mcd;
-        struct obd_run_ctxt saved;
+        struct lvfs_run_ctxt saved;
         int rc;
-        unsigned long *bitmap = mds->mds_client_bitmap;
+        loff_t off;
+        ENTRY;
 
         if (!med->med_mcd)
                 RETURN(0);
 
         /* XXX if mcd_uuid were a real obd_uuid, I could use obd_uuid_equals */
         if (!strcmp(med->med_mcd->mcd_uuid, obd->obd_uuid.uuid))
-                GOTO(free_and_out, 0);
+                GOTO(free, 0);
+
+        CDEBUG(D_INFO, "freeing client at idx %u, offset %lld with UUID '%s'\n",
+               med->med_lr_idx, med->med_lr_off, med->med_mcd->mcd_uuid);
 
-        CDEBUG(D_INFO, "freeing client at idx %u (%lld)with UUID '%s'\n",
-               med->med_idx, med->med_off, med->med_mcd->mcd_uuid);
+        LASSERT(mds->mds_client_bitmap != NULL);
 
-        LASSERT(bitmap);
+        off = med->med_lr_off;
+
+        /* Don't clear med_lr_idx here as it is likely also unset.  At worst
+         * we leak a client slot that will be cleaned on the next recovery. */
+        if (off <= 0) {
+                CERROR("%s: client idx %d has offset %lld\n",
+                        obd->obd_name, med->med_lr_idx, off);
+                GOTO(free, rc = -EINVAL);
+        }
 
         /* Clear the bit _after_ zeroing out the client so we don't
            race with mds_client_add and zero out new clients.*/
-        if (!test_bit(med->med_idx, bitmap)) {
+        if (!test_bit(med->med_lr_idx, mds->mds_client_bitmap)) {
                 CERROR("MDS client %u: bit already clear in bitmap!!\n",
-                       med->med_idx);
+                       med->med_lr_idx);
                 LBUG();
         }
 
-        if (clear_client) {
+        if (!(exp->exp_flags & OBD_OPT_FAILOVER)) {
                 memset(&zero_mcd, 0, sizeof zero_mcd);
-                push_ctxt(&saved, &obd->obd_ctxt, NULL);
+                push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
                 rc = fsfilt_write_record(obd, mds->mds_rcvd_filp, &zero_mcd,
-                                         sizeof(zero_mcd), &med->med_off, 1);
-                pop_ctxt(&saved, &obd->obd_ctxt, NULL);
+                                         sizeof(zero_mcd), &off, 1);
+                pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
 
                 CDEBUG(rc == 0 ? D_INFO : D_ERROR,
                        "zeroing out client %s idx %u in %s rc %d\n",
-                       med->med_mcd->mcd_uuid, med->med_idx, LAST_RCVD, rc);
+                       med->med_mcd->mcd_uuid, med->med_lr_idx, LAST_RCVD, rc);
         }
 
-        if (!test_and_clear_bit(med->med_idx, bitmap)) {
+        if (!test_and_clear_bit(med->med_lr_idx, mds->mds_client_bitmap)) {
                 CERROR("MDS client %u: bit already clear in bitmap!!\n",
-                       med->med_idx);
+                       med->med_lr_idx);
                 LBUG();
         }
 
- free_and_out:
+        EXIT;
+free:
         OBD_FREE(med->med_mcd, sizeof(*med->med_mcd));
         med->med_mcd = NULL;
 
@@ -177,8 +194,7 @@ int mds_client_free(struct obd_export *exp, int clear_client)
 
 static int mds_server_free_data(struct mds_obd *mds)
 {
-        OBD_FREE(mds->mds_client_bitmap,
-                 MDS_MAX_CLIENT_WORDS * sizeof(unsigned long));
+        OBD_FREE(mds->mds_client_bitmap, MDS_MAX_CLIENTS / 8);
         OBD_FREE(mds->mds_server_data, sizeof(*mds->mds_server_data));
         mds->mds_server_data = NULL;
 
@@ -206,8 +222,7 @@ static int mds_init_server_data(struct obd_device *obd, struct file *file)
         if (!msd)
                 RETURN(-ENOMEM);
 
-        OBD_ALLOC_WAIT(mds->mds_client_bitmap,
-                  MDS_MAX_CLIENT_WORDS * sizeof(unsigned long));
+        OBD_ALLOC_WAIT(mds->mds_client_bitmap, MDS_MAX_CLIENTS / 8);
         if (!mds->mds_client_bitmap) {
                 OBD_FREE(msd, sizeof(*msd));
                 RETURN(-ENOMEM);
@@ -323,7 +338,9 @@ static int mds_init_server_data(struct obd_device *obd, struct file *file)
                        sizeof exp->exp_client_uuid.uuid);
                 med = &exp->exp_mds_data;
                 med->med_mcd = mcd;
-                mds_client_add(obd, mds, med, cl_idx);
+                rc = mds_client_add(obd, mds, med, cl_idx);
+                LASSERTF(rc == 0, "rc = %d\n", rc); /* can't fail existing */
+
                 /* create helper if export init gets more complex */
                 INIT_LIST_HEAD(&med->med_open_head);
                 spin_lock_init(&med->med_open_lock);
@@ -338,7 +355,7 @@ static int mds_init_server_data(struct obd_device *obd, struct file *file)
                        cl_idx, last_transno);
 
                 if (last_transno > mds->mds_last_transno)
-                       mds->mds_last_transno = last_transno;
+                        mds->mds_last_transno = last_transno;
         }
 
         if (mcd)
@@ -375,7 +392,7 @@ err_msd:
 int mds_fs_setup(struct obd_device *obd, struct vfsmount *mnt)
 {
         struct mds_obd *mds = &obd->u.mds;
-        struct obd_run_ctxt saved;
+        struct lvfs_run_ctxt saved;
         struct dentry *dentry;
         struct file *file;
         int rc;
@@ -390,14 +407,14 @@ int mds_fs_setup(struct obd_device *obd, struct vfsmount *mnt)
 
         fsfilt_setup(obd, mds->mds_sb);
 
-        OBD_SET_CTXT_MAGIC(&obd->obd_ctxt);
-        obd->obd_ctxt.pwdmnt = mnt;
-        obd->obd_ctxt.pwd = mnt->mnt_root;
-        obd->obd_ctxt.fs = get_ds();
-        obd->obd_ctxt.cb_ops = mds_lvfs_ops;
+        OBD_SET_CTXT_MAGIC(&obd->obd_lvfs_ctxt);
+        obd->obd_lvfs_ctxt.pwdmnt = mnt;
+        obd->obd_lvfs_ctxt.pwd = mnt->mnt_root;
+        obd->obd_lvfs_ctxt.fs = get_ds();
+        obd->obd_lvfs_ctxt.cb_ops = mds_lvfs_ops;
 
         /* setup the directory tree */
-        push_ctxt(&saved, &obd->obd_ctxt, NULL);
+        push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
         dentry = simple_mkdir(current->fs->pwd, "ROOT", 0755, 0);
         if (IS_ERR(dentry)) {
                 rc = PTR_ERR(dentry);
@@ -484,7 +501,7 @@ int mds_fs_setup(struct obd_device *obd, struct vfsmount *mnt)
                 GOTO(err_lov_objid, rc = -ENOENT);
         }
 err_pop:
-        pop_ctxt(&saved, &obd->obd_ctxt, NULL);
+        pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
 
         return rc;
 
@@ -511,8 +528,8 @@ err_fid:
 int mds_fs_cleanup(struct obd_device *obd)
 {
         struct mds_obd *mds = &obd->u.mds;
-        struct obd_run_ctxt saved;
-        int i, rc = 0;
+        struct lvfs_run_ctxt saved;
+        int rc = 0;
 
         if (obd->obd_fail)
                 CERROR("%s: shutting down for failover; client state will"
@@ -521,7 +538,7 @@ int mds_fs_cleanup(struct obd_device *obd)
         class_disconnect_exports(obd); /* cleans up client info too */
         mds_server_free_data(mds);
 
-        push_ctxt(&saved, &obd->obd_ctxt, NULL);
+        push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
         if (mds->mds_rcvd_filp) {
                 rc = filp_close(mds->mds_rcvd_filp, 0);
                 mds->mds_rcvd_filp = NULL;
@@ -546,21 +563,13 @@ int mds_fs_cleanup(struct obd_device *obd)
                 l_dput(mds->mds_pending_dir);
                 mds->mds_pending_dir = NULL;
         }
-        
-        /* close admin quota files */
-        down(&mds->mds_quota_info.qi_sem);
-        for (i = 0; i < MAXQUOTAS; i++) {
-               if (mds->mds_quota_info.qi_files[i]) {
-                        filp_close(mds->mds_quota_info.qi_files[i], 0);
-                        mds->mds_quota_info.qi_files[i] = NULL;
-                }
-       }
-        up(&mds->mds_quota_info.qi_sem);
 
-        pop_ctxt(&saved, &obd->obd_ctxt, NULL);
+        mds_fs_quota_cleanup(mds);
+        
+        pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
         shrink_dcache_parent(mds->mds_fid_de);
         dput(mds->mds_fid_de);
-        DQUOT_OFF(mds->mds_sb);
+        LL_DQUOT_OFF(mds->mds_sb);
 
         return rc;
 }
@@ -576,18 +585,18 @@ int mds_obd_create(struct obd_export *exp, struct obdo *oa,
         unsigned int tmpname = ll_insecure_random_int();
         struct file *filp;
         struct dentry *new_child;
-        struct obd_run_ctxt saved;
+        struct lvfs_run_ctxt saved;
         char fidname[LL_FID_NAMELEN];
         void *handle;
-        struct obd_ucred ucred;
+        struct lvfs_ucred ucred;
         int rc = 0, err, namelen;
         ENTRY;
 
         /* the owner of object file should always be root */
         memset(&ucred, 0, sizeof(ucred));
-        ucred.ouc_cap = current->cap_effective | CAP_SYS_RESOURCE;
+        ucred.luc_cap = current->cap_effective | CAP_SYS_RESOURCE;
         
-        push_ctxt(&saved, &exp->exp_obd->obd_ctxt, &ucred);
+        push_ctxt(&saved, &exp->exp_obd->obd_lvfs_ctxt, &ucred);
 
         sprintf(fidname, "OBJECTS/%u.%u", tmpname, current->pid);
         filp = filp_open(fidname, O_CREAT | O_EXCL, 0666);
@@ -651,7 +660,7 @@ out_close:
                         rc = err;
         }
 out_pop:
-        pop_ctxt(&saved, &exp->exp_obd->obd_ctxt, &ucred);
+        pop_ctxt(&saved, &exp->exp_obd->obd_lvfs_ctxt, &ucred);
         RETURN(rc);
 }
 
@@ -661,8 +670,8 @@ int mds_obd_destroy(struct obd_export *exp, struct obdo *oa,
         struct mds_obd *mds = &exp->exp_obd->u.mds;
         struct inode *parent_inode = mds->mds_objects_dir->d_inode;
         struct obd_device *obd = exp->exp_obd;
-        struct obd_run_ctxt saved;
-        struct obd_ucred ucred;
+        struct lvfs_run_ctxt saved;
+        struct lvfs_ucred ucred;
         char fidname[LL_FID_NAMELEN];
         struct dentry *de;
         void *handle;
@@ -670,8 +679,8 @@ int mds_obd_destroy(struct obd_export *exp, struct obdo *oa,
         ENTRY;
         
         memset(&ucred, 0, sizeof(ucred));
-        ucred.ouc_cap = current->cap_effective | CAP_SYS_RESOURCE;
-        push_ctxt(&saved, &obd->obd_ctxt, &ucred);
+        ucred.luc_cap = current->cap_effective | CAP_SYS_RESOURCE;
+        push_ctxt(&saved, &obd->obd_lvfs_ctxt, &ucred);
 
         namelen = ll_fid2str(fidname, oa->o_id, oa->o_generation);
 
@@ -685,8 +694,8 @@ int mds_obd_destroy(struct obd_export *exp, struct obdo *oa,
                 GOTO(out_dput, rc);
         }
         if (de->d_inode == NULL) {
-                CERROR("destroying non-existent object "LPU64" %s\n",
-                       oa->o_id, fidname);
+                CERROR("destroying non-existent object "LPU64" %s: rc %d\n",
+                       oa->o_id, fidname, rc);
                 GOTO(out_dput, rc = -ENOENT);
         }
 
@@ -694,10 +703,10 @@ int mds_obd_destroy(struct obd_export *exp, struct obdo *oa,
            that is unlinked, not spanned across multiple OSTs */
         handle = fsfilt_start_log(obd, mds->mds_objects_dir->d_inode,
                                   FSFILT_OP_UNLINK, oti, 1);
-        if (IS_ERR(handle)) {
-                GOTO(out_dput, rc = PTR_ERR(handle));
-        }
 
+        if (IS_ERR(handle))
+                GOTO(out_dput, rc = PTR_ERR(handle));
+        
         rc = vfs_unlink(mds->mds_objects_dir->d_inode, de);
         if (rc)
                 CERROR("error destroying object "LPU64":%u: rc %d\n",
@@ -711,6 +720,6 @@ out_dput:
                 l_dput(de);
         up(&parent_inode->i_sem);
 
-        pop_ctxt(&saved, &obd->obd_ctxt, &ucred);
+        pop_ctxt(&saved, &obd->obd_lvfs_ctxt, &ucred);
         RETURN(rc);
 }
index 792d6d0..ceca1cb 100644 (file)
@@ -147,7 +147,6 @@ int mds_lov_disconnect(struct obd_device *obd);
 void mds_lov_set_cleanup_flags(struct obd_device *);
 int mds_lov_write_objids(struct obd_device *obd);
 void mds_lov_update_objids(struct obd_device *obd, obd_id *ids);
-int mds_lov_set_growth(struct mds_obd *mds, int count);
 int mds_lov_set_nextid(struct obd_device *obd);
 int mds_lov_clearorphans(struct mds_obd *mds, struct obd_uuid *ost_uuid);
 int mds_post_mds_lovconf(struct obd_device *obd);
@@ -162,6 +161,7 @@ int mds_query_write_access(struct inode *inode);
 int mds_open(struct mds_update_record *rec, int offset,
              struct ptlrpc_request *req, struct lustre_handle *);
 int mds_pin(struct ptlrpc_request *req);
+void mds_mfd_unlink(struct mds_file_data *mfd, int decref);
 int mds_mfd_close(struct ptlrpc_request *req, struct obd_device *obd,
                   struct mds_file_data *mfd, int unlink_orphan);
 int mds_close(struct ptlrpc_request *req);
@@ -171,7 +171,7 @@ int mds_done_writing(struct ptlrpc_request *req);
 /* mds/mds_fs.c */
 int mds_client_add(struct obd_device *obd, struct mds_obd *mds,
                    struct mds_export_data *med, int cl_off);
-int mds_client_free(struct obd_export *exp, int clear_client);
+int mds_client_free(struct obd_export *exp);
 int mds_obd_create(struct obd_export *exp, struct obdo *oa,
                    struct lov_stripe_md **ea, struct obd_trans_info *oti);
 int mds_obd_destroy(struct obd_export *exp, struct obdo *oa,
@@ -193,7 +193,7 @@ void mds_pack_inode2body(struct mds_body *body, struct inode *inode);
 #endif
 
 /* mds/quota_master.c */
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0))
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)) && defined (HAVE_QUOTA_SUPPORT)
 int lustre_dquot_init(void);
 void lustre_dquot_exit(void);
 int dqacq_handler(struct obd_device *obd, struct qunit_data *qdata, int opc);
@@ -228,6 +228,49 @@ static inline int mds_set_dqblk(struct obd_device *obd,
                                 struct obd_quotactl *oqctl) { return 0; }
 static inline int mds_get_dqblk(struct obd_device *obd, 
                                 struct obd_quotactl *oqctl) { return 0; }
-#endif /* KERNEL_VERSION(2,5,0) */
+#endif /* KERNEL_VERSION(2,5,0) && QUOTA */
+
+#ifdef HAVE_QUOTA_SUPPORT
+/* Internal quota stuff */
+int mds_quotacheck(struct ptlrpc_request *req);
+int mds_quotactl(struct ptlrpc_request *req);
+void mds_quota_setup(struct mds_obd *mds);
+void mds_quota_cleanup(struct mds_obd *mds);
+void mds_fs_quota_cleanup(struct mds_obd *mds);
+
+#ifdef LPROCFS
+int lprocfs_mds_rd_bunit(char *page, char **start, off_t off, int count,
+                                int *eof, void *data);
+int lprocfs_mds_rd_iunit(char *page, char **start, off_t off, int count,
+                                int *eof, void *data);
+int lprocfs_mds_wr_bunit(struct file *file, const char *buffer,
+                                unsigned long count, void *data);
+int lprocfs_mds_wr_iunit(struct file *file, const char *buffer,
+                                unsigned long count, void *data);
+int lprocfs_mds_rd_btune(char *page, char **start, off_t off, int count,
+                                int *eof, void *data);
+int lprocfs_mds_rd_itune(char *page, char **start, off_t off, int count,
+                                int *eof, void *data);
+int lprocfs_mds_wr_btune(struct file *file, const char *buffer,
+                                unsigned long count, void *data);
+int lprocfs_mds_wr_itune(struct file *file, const char *buffer,
+                                unsigned long count, void *data);
+#endif /* LPROCFS */
+#else /* QUOTA */
+static inline int mds_quotacheck(struct ptlrpc_request *req)
+{
+        req->rq_status = -EOPNOTSUPP;
+        return -EOPNOTSUPP;
+}
+static inline int mds_quotactl(struct ptlrpc_request *req)
+{
+        req->rq_status = -EOPNOTSUPP;
+        return -EOPNOTSUPP;
+}
+static inline void mds_quota_setup(struct mds_obd *mds) {}
+static inline void mds_quota_cleanup(struct mds_obd *mds) {}
+static inline void mds_fs_quota_cleanup(struct mds_obd *mds) {}
+#endif /* Quota */
+
 
 #endif /* _MDS_INTERNAL_H */
index b4ea941..57dfb38 100644 (file)
@@ -325,8 +325,7 @@ static update_unpacker mds_unpackers[REINT_MAX] = {
 int mds_update_unpack(struct ptlrpc_request *req, int offset,
                       struct mds_update_record *rec)
 {
-        __u32 *opcodep;
-        __u32  opcode;
+        mds_reint_t opcode, *opcodep;
         int rc;
         ENTRY;
 
@@ -348,5 +347,8 @@ int mds_update_unpack(struct ptlrpc_request *req, int offset,
 
         rec->ur_opcode = opcode;
         rc = mds_unpackers[opcode](req, offset, rec);
+#if CRAY_PORTALS
+        rec->ur_fsuid = req->rq_uid;
+#endif
         RETURN(rc);
 }
index dc1a143..b73b7d2 100644 (file)
@@ -43,10 +43,10 @@ static int mds_log_fill_unlink_rec(struct llog_rec_hdr *rec, void *data)
 {
         struct llog_fill_rec_data *lfd = (struct llog_fill_rec_data *)data;
         struct llog_unlink_rec *lur = (struct llog_unlink_rec *)rec;
-                                                                                                                             
+
         lur->lur_oid = lfd->lfd_id;
         lur->lur_ogen = lfd->lfd_ogen;
-                                                                                                                             
+
         RETURN(0);
 }
 
@@ -55,10 +55,10 @@ static int mds_log_fill_setattr_rec(struct llog_rec_hdr *rec, void *data)
 {
         struct llog_fill_rec_data *lfd = (struct llog_fill_rec_data *)data;
         struct llog_setattr_rec *lsr = (struct llog_setattr_rec *)rec;
-                                                                                                                             
+
         lsr->lsr_oid = lfd->lfd_id;
         lsr->lsr_ogen = lfd->lfd_ogen;
-                                                                                                                             
+
         RETURN(0);
 }
 
@@ -155,34 +155,33 @@ int mds_log_op_setattr(struct obd_device *obd, struct inode *inode,
         struct llog_setattr_rec *lsr;
         int rc;
         ENTRY;
-                                                                                                                             
+
         if (IS_ERR(mds->mds_osc_obd))
                 RETURN(PTR_ERR(mds->mds_osc_obd));
-                                                                                                                             
-        rc = obd_unpackmd(mds->mds_osc_exp, &lsm,
-                          lmm, lmm_size);
+
+        rc = obd_unpackmd(mds->mds_osc_exp, &lsm, lmm, lmm_size);
         if (rc < 0)
                 RETURN(rc);
 
         OBD_ALLOC(lsr, sizeof(*lsr));
         if (!lsr)
-                RETURN(-ENOMEM);
-                                                                                                                             
+                GOTO(out, rc = -ENOMEM);
+
         /* prepare setattr log record */
         lsr->lsr_hdr.lrh_len = lsr->lsr_tail.lrt_len = sizeof(*lsr);
         lsr->lsr_hdr.lrh_type = MDS_SETATTR_REC;
         lsr->lsr_uid = inode->i_uid;
         lsr->lsr_gid = inode->i_gid;
-                                                                                                                             
+
         /* write setattr log */
         ctxt = llog_get_context(obd, LLOG_MDS_OST_ORIG_CTXT);
         rc = llog_add(ctxt, &lsr->lsr_hdr, lsm, logcookies,
                       cookies_size / sizeof(struct llog_cookie),
                       mds_log_fill_setattr_rec);
 
-        obd_free_memmd(mds->mds_osc_exp, &lsm);
         OBD_FREE(lsr, sizeof(*lsr));
-
+ out:
+        obd_free_memmd(mds->mds_osc_exp, &lsm);
         RETURN(rc);
 }
 
index 251a9c7..773165f 100644 (file)
@@ -142,18 +142,6 @@ int mds_lov_set_nextid(struct obd_device *obd)
         RETURN(rc);
 }
 
-/* tell the LOV-OSC by how much to pre-create */
-int mds_lov_set_growth(struct mds_obd *mds, int count)
-{
-        int rc;
-        ENTRY;
-
-        rc = obd_set_info(mds->mds_osc_exp, strlen("growth_count"),
-                          "growth_count", sizeof(count), &count);
-
-        RETURN(rc);
-}
-
 int mds_lov_connect(struct obd_device *obd, char * lov_name)
 {
         struct mds_obd *mds = &obd->u.mds;
@@ -305,7 +293,7 @@ int mds_iocontrol(unsigned int cmd, struct obd_export *exp, int len,
         struct obd_device *obd = exp->exp_obd;
         struct mds_obd *mds = &obd->u.mds;
         struct obd_ioctl_data *data = karg;
-        struct obd_run_ctxt saved;
+        struct lvfs_run_ctxt saved;
         int rc = 0;
 
         ENTRY;
@@ -317,7 +305,7 @@ int mds_iocontrol(unsigned int cmd, struct obd_export *exp, int len,
                 if (mds->mds_cfg_llh)
                         RETURN(-EBUSY);
 
-                push_ctxt(&saved, &obd->obd_ctxt, NULL);
+                push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
                 rc = llog_create(llog_get_context(obd, LLOG_CONFIG_ORIG_CTXT),
                                  &mds->mds_cfg_llh, NULL, name);
                 if (rc == 0)
@@ -325,7 +313,7 @@ int mds_iocontrol(unsigned int cmd, struct obd_export *exp, int len,
                                          &cfg_uuid);
                 else
                         mds->mds_cfg_llh = NULL;
-                pop_ctxt(&saved, &obd->obd_ctxt, NULL);
+                pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
 
                 RETURN(rc);
         }
@@ -334,9 +322,9 @@ int mds_iocontrol(unsigned int cmd, struct obd_export *exp, int len,
                 if (!mds->mds_cfg_llh)
                         RETURN(-EBADF);
 
-                push_ctxt(&saved, &obd->obd_ctxt, NULL);
+                push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
                 rc = llog_close(mds->mds_cfg_llh);
-                pop_ctxt(&saved, &obd->obd_ctxt, NULL);
+                pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
 
                 mds->mds_cfg_llh = NULL;
                 RETURN(rc);
@@ -347,7 +335,7 @@ int mds_iocontrol(unsigned int cmd, struct obd_export *exp, int len,
                 if (mds->mds_cfg_llh)
                         RETURN(-EBUSY);
 
-                push_ctxt(&saved, &obd->obd_ctxt, NULL);
+                push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
                 rc = llog_create(llog_get_context(obd, LLOG_CONFIG_ORIG_CTXT),
                                  &mds->mds_cfg_llh, NULL, name);
                 if (rc == 0) {
@@ -357,7 +345,7 @@ int mds_iocontrol(unsigned int cmd, struct obd_export *exp, int len,
                         rc = llog_destroy(mds->mds_cfg_llh);
                         llog_free_handle(mds->mds_cfg_llh);
                 }
-                pop_ctxt(&saved, &obd->obd_ctxt, NULL);
+                pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
 
                 mds->mds_cfg_llh = NULL;
                 RETURN(rc);
@@ -387,10 +375,10 @@ int mds_iocontrol(unsigned int cmd, struct obd_export *exp, int len,
                         RETURN(rc);
                 }
 
-                push_ctxt(&saved, &obd->obd_ctxt, NULL);
+                push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
                 rc = llog_write_rec(mds->mds_cfg_llh, &rec, NULL, 0,
                                     cfg_buf, -1);
-                pop_ctxt(&saved, &obd->obd_ctxt, NULL);
+                pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
 
                 OBD_FREE(cfg_buf, data->ioc_plen1);
                 RETURN(rc);
@@ -399,9 +387,9 @@ int mds_iocontrol(unsigned int cmd, struct obd_export *exp, int len,
         case OBD_IOC_PARSE: {
                 struct llog_ctxt *ctxt =
                         llog_get_context(obd, LLOG_CONFIG_ORIG_CTXT);
-                push_ctxt(&saved, &obd->obd_ctxt, NULL);
+                push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
                 rc = class_config_parse_llog(ctxt, data->ioc_inlbuf1, NULL);
-                pop_ctxt(&saved, &obd->obd_ctxt, NULL);
+                pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
                 if (rc)
                         RETURN(rc);
 
@@ -411,9 +399,9 @@ int mds_iocontrol(unsigned int cmd, struct obd_export *exp, int len,
         case OBD_IOC_DUMP_LOG: {
                 struct llog_ctxt *ctxt =
                         llog_get_context(obd, LLOG_CONFIG_ORIG_CTXT);
-                push_ctxt(&saved, &obd->obd_ctxt, NULL);
+                push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
                 rc = class_config_dump_llog(ctxt, data->ioc_inlbuf1, NULL);
-                pop_ctxt(&saved, &obd->obd_ctxt, NULL);
+                pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
                 if (rc)
                         RETURN(rc);
 
@@ -430,7 +418,7 @@ int mds_iocontrol(unsigned int cmd, struct obd_export *exp, int len,
                 void *handle;
                 struct inode *inode = obd->u.mds.mds_sb->s_root->d_inode;
                 BDEVNAME_DECLARE_STORAGE(tmp);
-                CERROR("setting device %s read-only\n",
+                CERROR("*** setting device %s read-only ***\n",
                        ll_bdevname(obd->u.mds.mds_sb, tmp));
 
                 handle = fsfilt_start(obd, inode, FSFILT_OP_MKNOD, NULL);
@@ -440,7 +428,7 @@ int mds_iocontrol(unsigned int cmd, struct obd_export *exp, int len,
                 CDEBUG(D_HA, "syncing mds %s\n", obd->obd_name);
                 rc = fsfilt_sync(obd, obd->u.mds.mds_sb);
 
-                ll_set_rdonly(ll_sbdev(obd->u.mds.mds_sb));
+                lvfs_set_rdonly(lvfs_sbdev(obd->u.mds.mds_sb));
                 RETURN(0);
         }
 
@@ -458,9 +446,9 @@ int mds_iocontrol(unsigned int cmd, struct obd_export *exp, int len,
                 int rc2;
 
                 obd_llog_finish(obd, mds->mds_lov_desc.ld_tgt_count);
-                push_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_ctxt, NULL);
+                push_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_lvfs_ctxt, NULL);
                 rc = llog_ioctl(ctxt, cmd, data);
-                pop_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_ctxt, NULL);
+                pop_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_lvfs_ctxt, NULL);
                 llog_cat_initialize(obd, mds->mds_lov_desc.ld_tgt_count);
                 rc2 = obd_set_info(mds->mds_osc_exp, strlen("mds_conn"),
                                    "mds_conn", 0, NULL);
@@ -473,9 +461,9 @@ int mds_iocontrol(unsigned int cmd, struct obd_export *exp, int len,
                 struct llog_ctxt *ctxt =
                         llog_get_context(obd, LLOG_CONFIG_ORIG_CTXT);
 
-                push_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_ctxt, NULL);
+                push_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_lvfs_ctxt, NULL);
                 rc = llog_ioctl(ctxt, cmd, data);
-                pop_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_ctxt, NULL);
+                pop_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_lvfs_ctxt, NULL);
 
                 RETURN(rc);
         }
@@ -486,6 +474,7 @@ int mds_iocontrol(unsigned int cmd, struct obd_export *exp, int len,
                 RETURN(0);
 
         default:
+                CDEBUG(D_INFO, "unknown command %x\n", cmd);
                 RETURN(-EINVAL);
         }
         RETURN(0);
@@ -503,7 +492,7 @@ int mds_lov_synchronize(void *data)
         struct obd_device *obd;
         struct obd_uuid *uuid;
         unsigned long flags;
-        int rc;
+        int rc = 0;
 
         lock_kernel();
         ptlrpc_daemonize();
@@ -525,7 +514,7 @@ int mds_lov_synchronize(void *data)
         rc = obd_set_info(obd->u.mds.mds_osc_exp, strlen("mds_conn"),
                           "mds_conn", 0, uuid);
         if (rc != 0)
-                RETURN(rc);
+                GOTO(out, rc);
 
         rc = llog_connect(llog_get_context(obd, LLOG_MDS_OST_ORIG_CTXT),
                           obd->u.mds.mds_lov_desc.ld_tgt_count,
@@ -533,7 +522,7 @@ int mds_lov_synchronize(void *data)
         if (rc != 0) {
                 CERROR("%s: failed at llog_origin_connect: %d\n",
                        obd->obd_name, rc);
-                RETURN(rc);
+                GOTO(out, rc);
         }
 
         CWARN("MDS %s: %s now active, resetting orphans\n",
@@ -542,10 +531,12 @@ int mds_lov_synchronize(void *data)
         if (rc != 0) {
                 CERROR("%s: failed at mds_lov_clearorphans: %d\n",
                        obd->obd_name, rc);
-                RETURN(rc);
+                GOTO(out, rc);
         }
 
-        RETURN(0);
+out:
+        class_export_put(obd->obd_self_export);
+        RETURN(rc);
 }
 
 int mds_lov_start_synchronize(struct obd_device *obd, struct obd_uuid *uuid)
@@ -561,6 +552,9 @@ int mds_lov_start_synchronize(struct obd_device *obd, struct obd_uuid *uuid)
 
         mlsi->mlsi_obd = obd;
         mlsi->mlsi_uuid = uuid;
+        
+        /* We need to lock the mds in place for our new thread context. */
+        class_export_get(obd->obd_self_export);
 
         rc = kernel_thread(mds_lov_synchronize, mlsi, CLONE_VM | CLONE_FILES);
         if (rc < 0)
index b2b9d60..39cf431 100644 (file)
@@ -68,6 +68,9 @@ static void mds_mfd_addref(void *mfdp)
                atomic_read(&mfd->mfd_refcount));
 }
 
+/* Create a new mds_file_data struct.
+ * One reference for handle+med_open_head list and dropped by mds_mfd_unlink(),
+ * one reference for the caller of this function. */
 struct mds_file_data *mds_mfd_new(void)
 {
         struct mds_file_data *mfd;
@@ -81,11 +84,14 @@ struct mds_file_data *mds_mfd_new(void)
         atomic_set(&mfd->mfd_refcount, 2);
 
         INIT_LIST_HEAD(&mfd->mfd_handle.h_link);
+        INIT_LIST_HEAD(&mfd->mfd_list);
         class_handle_hash(&mfd->mfd_handle, mds_mfd_addref);
 
         return mfd;
 }
 
+/* Get a new reference on the mfd pointed to by handle, if handle is still
+ * valid.  Caller must drop reference with mds_mfd_put(). */
 static struct mds_file_data *mds_handle2mfd(struct lustre_handle *handle)
 {
         ENTRY;
@@ -93,6 +99,7 @@ static struct mds_file_data *mds_handle2mfd(struct lustre_handle *handle)
         RETURN(class_handle2object(handle->cookie));
 }
 
+/* Drop mfd reference, freeing struct if this is the last one. */
 static void mds_mfd_put(struct mds_file_data *mfd)
 {
         CDEBUG(D_INFO, "PUTting mfd %p : new refcount %d\n", mfd,
@@ -105,13 +112,16 @@ static void mds_mfd_put(struct mds_file_data *mfd)
         }
 }
 
-static void mds_mfd_destroy(struct mds_file_data *mfd)
+/* Remove the mfd handle so that it cannot be found by open/close again.
+ * Caller must hold med_open_lock for mfd_list manipulation. */
+void mds_mfd_unlink(struct mds_file_data *mfd, int decref)
 {
         class_handle_unhash(&mfd->mfd_handle);
-        mds_mfd_put(mfd);
+        list_del_init(&mfd->mfd_list);
+        if (decref)
+                mds_mfd_put(mfd);
 }
 
-
 /* Caller must hold mds->mds_epoch_sem */
 static int mds_alloc_filterdata(struct inode *inode)
 {
@@ -278,7 +288,6 @@ static struct mds_file_data *mds_dentry_open(struct dentry *dentry,
         spin_lock(&med->med_open_lock);
         list_add(&mfd->mfd_list, &med->med_open_head);
         spin_unlock(&med->med_open_lock);
-        mds_mfd_put(mfd);
 
         body->handle.cookie = mfd->mfd_handle.h_cookie;
 
@@ -286,7 +295,7 @@ static struct mds_file_data *mds_dentry_open(struct dentry *dentry,
 
 cleanup_mfd:
         mds_mfd_put(mfd);
-        mds_mfd_destroy(mfd);
+        mds_mfd_unlink(mfd, 1);
 cleanup_dentry:
         return ERR_PTR(error);
 }
@@ -295,9 +304,8 @@ cleanup_dentry:
 static int mds_create_objects(struct ptlrpc_request *req, int offset,
                               struct mds_update_record *rec,
                               struct mds_obd *mds, struct obd_device *obd,
-                              struct dentry *dchild, void **handle, obd_id **ids,
-                              struct llog_cookie **ret_logcookies, 
-                              int *setattr_async_flag)
+                              struct dentry *dchild, void **handle,
+                              obd_id **ids)
 {
         struct obdo *oa;
         struct obd_trans_info oti = { 0 };
@@ -358,6 +366,7 @@ static int mds_create_objects(struct ptlrpc_request *req, int offset,
                 RETURN(0);
         }
 
+
         if (OBD_FAIL_CHECK_ONCE(OBD_FAIL_MDS_ALLOC_OBDO))
                 GOTO(out_ids, rc = -ENOMEM);
 
@@ -415,7 +424,6 @@ static int mds_create_objects(struct ptlrpc_request *req, int offset,
                         }
                         GOTO(out_oa, rc);
                 }
-                *setattr_async_flag = 1;
         } else {
                 rc = obd_iocontrol(OBD_IOC_LOV_SETEA, mds->mds_osc_exp,
                                    0, &lsm, rec->ur_eadata);
@@ -450,35 +458,14 @@ static int mds_create_objects(struct ptlrpc_request *req, int offset,
         lmm_size = rc;
         body->eadatasize = rc;
 
-        if (*handle == NULL) {
-                if (*setattr_async_flag)
-                        *handle = fsfilt_start_log(obd, inode, 
-                                                   FSFILT_OP_CREATE, NULL, 
-                                                   le32_to_cpu(lmm->lmm_stripe_count));
-                else
-                        *handle = fsfilt_start(obd, inode, FSFILT_OP_CREATE, NULL);
-        }
+        if (*handle == NULL)
+                *handle = fsfilt_start(obd, inode, FSFILT_OP_CREATE, NULL);
         if (IS_ERR(*handle)) {
                 rc = PTR_ERR(*handle);
                 *handle = NULL;
                 GOTO(out_oa, rc);
         }
 
-        /* write mds setattr log for created objects */
-        if (*setattr_async_flag && lmm_size) {
-                struct llog_cookie *logcookies = NULL;
-
-                OBD_ALLOC(logcookies, mds->mds_max_cookiesize);
-                if (logcookies == NULL)
-                        GOTO(out_oa, rc = -ENOMEM);
-                *ret_logcookies = logcookies;
-                if (mds_log_op_setattr(obd, inode, lmm, lmm_size, logcookies,
-                                       mds->mds_max_cookiesize) <= 0) {
-                        OBD_FREE(logcookies, mds->mds_max_cookiesize);
-                        *ret_logcookies = NULL;
-               }
-        }  
-
         rc = fsfilt_set_md(obd, inode, *handle, lmm, lmm_size);
         lmm_buf = lustre_msg_buf(req->rq_repmsg, offset, 0);
         lmm_bufsize = req->rq_repmsg->buflens[offset];
@@ -586,12 +573,16 @@ static void reconstruct_open(struct mds_update_record *rec, int offset,
                 GOTO(out_dput, 0);
 
         mfd = NULL;
+        spin_lock(&med->med_open_lock);
         list_for_each(t, &med->med_open_head) {
                 mfd = list_entry(t, struct mds_file_data, mfd_list);
-                if (mfd->mfd_xid == req->rq_xid)
+                if (mfd->mfd_xid == req->rq_xid) {
+                        mds_mfd_addref(mfd);
                         break;
+                }
                 mfd = NULL;
         }
+        spin_unlock(&med->med_open_lock);
 
         /* #warning "XXX fixme" bug 2991 */
         /* Here it used to LASSERT(mfd) if exp_outstanding_reply != NULL.
@@ -613,6 +604,8 @@ static void reconstruct_open(struct mds_update_record *rec, int offset,
                        mfd->mfd_handle.h_cookie);
         }
 
+        mds_mfd_put(mfd);
+
  out_dput:
         if (put_child)
                 l_dput(dchild);
@@ -645,9 +638,7 @@ static int accmode(struct inode *inode, int flags)
 /* Handles object creation, actual opening, and I/O epoch */
 static int mds_finish_open(struct ptlrpc_request *req, struct dentry *dchild,
                            struct mds_body *body, int flags, void **handle,
-                           struct mds_update_record *rec,struct ldlm_reply *rep,
-                           struct llog_cookie **logcookies,
-                           int *setattr_async_flag)
+                           struct mds_update_record *rec,struct ldlm_reply *rep)
 {
         struct mds_obd *mds = mds_req2mds(req);
         struct obd_device *obd = req->rq_export->exp_obd;
@@ -677,8 +668,7 @@ static int mds_finish_open(struct ptlrpc_request *req, struct dentry *dchild,
                 if (!(body->valid & OBD_MD_FLEASIZE)) {
                         /* no EA: create objects */
                         rc = mds_create_objects(req, 2, rec, mds, obd,
-                                                dchild, handle, &ids,
-                                                logcookies, setattr_async_flag);
+                                                dchild, handle, &ids);
                         if (rc) {
                                 CERROR("mds_create_objects: rc = %d\n", rc);
                                 up(&dchild->d_inode->i_sem);
@@ -701,12 +691,14 @@ static int mds_finish_open(struct ptlrpc_request *req, struct dentry *dchild,
 
         CDEBUG(D_INODE, "mfd %p, cookie "LPX64"\n", mfd,
                mfd->mfd_handle.h_cookie);
+
         if (ids != NULL) {
                 mds_lov_update_objids(obd, ids);
                 OBD_FREE(ids, sizeof(*ids) * mds->mds_lov_desc.ld_tgt_count);
         }
-        //if (rc)
-        //        mds_mfd_destroy(mfd);
+        if (rc)
+                mds_mfd_unlink(mfd, 1);
+        mds_mfd_put(mfd);
         RETURN(rc);
 }
 
@@ -714,16 +706,11 @@ static int mds_open_by_fid(struct ptlrpc_request *req, struct ll_fid *fid,
                            struct mds_body *body, int flags,
                            struct mds_update_record *rec,struct ldlm_reply *rep)
 {
-        struct obd_device *obd = req->rq_export->exp_obd;
         struct mds_obd *mds = mds_req2mds(req);
         struct dentry *dchild;
         char fidname[LL_FID_NAMELEN];
         int fidlen = 0, rc;
         void *handle = NULL;
-        struct llog_cookie *logcookies = NULL;
-        struct lov_mds_md *lmm = NULL;
-        int lmm_size = 0;
-        int setattr_async_flag = 0;
         ENTRY;
 
         fidlen = ll_fid2str(fidname, fid->id, fid->generation);
@@ -758,17 +745,9 @@ static int mds_open_by_fid(struct ptlrpc_request *req, struct ll_fid *fid,
         intent_set_disposition(rep, DISP_LOOKUP_POS);
 
  open:
-        rc = mds_finish_open(req, dchild, body, flags, &handle, rec, rep,
-                             &logcookies, &setattr_async_flag);
+        rc = mds_finish_open(req, dchild, body, flags, &handle, rec, rep);
         rc = mds_finish_transno(mds, dchild ? dchild->d_inode : NULL, handle,
                                 req, rc, rep ? rep->lock_policy_res1 : 0);
-        /* do mds to ost setattr for new created objects */
-        if (rc == 0 && setattr_async_flag) {
-                lmm = lustre_msg_buf(req->rq_repmsg, 2, 0);
-                lmm_size = req->rq_repmsg->buflens[2];
-                rc = mds_osc_setattr_async(obd, dchild->d_inode, lmm, lmm_size,
-                                           logcookies);
-        }
         /* XXX what do we do here if mds_finish_transno itself failed? */
 
         l_dput(dchild);
@@ -779,7 +758,7 @@ int mds_pin(struct ptlrpc_request *req)
 {
         struct obd_device *obd = req->rq_export->exp_obd;
         struct mds_body *request_body, *reply_body;
-        struct obd_run_ctxt saved;
+        struct lvfs_run_ctxt saved;
         int rc, size = sizeof(*reply_body);
         ENTRY;
 
@@ -790,10 +769,10 @@ int mds_pin(struct ptlrpc_request *req)
                 RETURN(rc);
         reply_body = lustre_msg_buf(req->rq_repmsg, 0, sizeof(*reply_body));
 
-        push_ctxt(&saved, &obd->obd_ctxt, NULL);
+        push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
         rc = mds_open_by_fid(req, &request_body->fid1, reply_body,
                              request_body->flags, NULL, NULL);
-        pop_ctxt(&saved, &obd->obd_ctxt, NULL);
+        pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
 
         RETURN(rc);
 }
@@ -839,10 +818,6 @@ int mds_open(struct mds_update_record *rec, int offset,
         int parent_mode = LCK_PR;
         void *handle = NULL;
         struct dentry_params dp;
-        struct lov_mds_md *lmm = NULL;
-        int lmm_size = 0;
-        struct llog_cookie *logcookies = NULL;
-        int setattr_async_flag = 0;
         uid_t parent_uid = 0;
         gid_t parent_gid = 0;
         ENTRY;
@@ -1073,19 +1048,12 @@ int mds_open(struct mds_update_record *rec, int offset,
 
         /* Step 5: mds_open it */
         rc = mds_finish_open(req, dchild, body, rec->ur_flags, &handle, rec,
-                             rep, &logcookies, &setattr_async_flag);
+                             rep);
         GOTO(cleanup, rc);
 
  cleanup:
         rc = mds_finish_transno(mds, dchild ? dchild->d_inode : NULL, handle,
                                 req, rc, rep ? rep->lock_policy_res1 : 0);
-        /* do mds to ost setattr for new created objects */
-        if (rc == 0 && setattr_async_flag) {
-                lmm = lustre_msg_buf(req->rq_repmsg, 2, 0);
-                lmm_size = req->rq_repmsg->buflens[2];
-                mds_osc_setattr_async(obd, dchild->d_inode, lmm, lmm_size,
-                                      logcookies);
-        }
 
  cleanup_no_trans:
         switch (cleanup_phase) {
@@ -1115,7 +1083,7 @@ int mds_open(struct mds_update_record *rec, int offset,
                 else
                         ptlrpc_save_lock (req, &parent_lockh, parent_mode);
         }
-        
         /* trigger dqacq on the owner of child and parent */
         mds_adjust_qunit(obd, current->fsuid, current->fsgid, 
                          parent_uid, parent_gid, rc);
@@ -1289,7 +1257,7 @@ out:
         if (rc > 0)
                 rc = 0;
         l_dput(mfd->mfd_dentry);
-        mds_mfd_destroy(mfd);
+        mds_mfd_put(mfd);
 
  cleanup:
         if (req != NULL && reply_body != NULL) {
@@ -1318,7 +1286,7 @@ int mds_close(struct ptlrpc_request *req)
         struct obd_device *obd = req->rq_export->exp_obd;
         struct mds_body *body;
         struct mds_file_data *mfd;
-        struct obd_run_ctxt saved;
+        struct lvfs_run_ctxt saved;
         struct inode *inode;
         int rc, repsize[3] = {sizeof(struct mds_body),
                               obd->u.mds.mds_max_mdsize,
@@ -1343,13 +1311,19 @@ int mds_close(struct ptlrpc_request *req)
         if (body->flags & MDS_BFLAG_UNCOMMITTED_WRITES)
                 /* do some stuff */ ;
 
+        spin_lock(&med->med_open_lock);
         mfd = mds_handle2mfd(&body->handle);
         if (mfd == NULL) {
+                spin_unlock(&med->med_open_lock);
                 DEBUG_REQ(D_ERROR, req, "no handle for file close ino "LPD64
                           ": cookie "LPX64, body->fid1.id, body->handle.cookie);
                 req->rq_status = -ESTALE;
                 RETURN(-ESTALE);
         }
+        /* Remove mfd handle so it can't be found again.  We consume mfd_list
+         * reference here, but still have mds_handle2mfd ref until mfd_close. */
+        mds_mfd_unlink(mfd, 1);
+        spin_unlock(&med->med_open_lock);
 
         inode = mfd->mfd_dentry->d_inode;
         /* child orphan sem protects orphan_dec_test && is_orphan race */
@@ -1362,15 +1336,11 @@ int mds_close(struct ptlrpc_request *req)
                 mds_pack_inode2body(body, inode);
                 mds_pack_md(obd, req->rq_repmsg, 1,body,inode,MDS_PACK_MD_LOCK);
         }
-        spin_lock(&med->med_open_lock);
-        list_del(&mfd->mfd_list);
-        spin_unlock(&med->med_open_lock);
 
-        push_ctxt(&saved, &obd->obd_ctxt, NULL);
+        push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
         req->rq_status = mds_mfd_close(req, obd, mfd, 1);
-        pop_ctxt(&saved, &obd->obd_ctxt, NULL);
+        pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
 
-        mds_mfd_put(mfd);
         if (OBD_FAIL_CHECK(OBD_FAIL_MDS_CLOSE_PACK)) {
                 CERROR("test case OBD_FAIL_MDS_CLOSE_PACK\n");
                 req->rq_status = -ENOMEM;
index 1d54b6d..d163220 100644 (file)
@@ -126,7 +126,7 @@ int mds_finish_transno(struct mds_obd *mds, struct inode *inode, void *handle,
                 }
         }
 
-        off = med->med_off;
+        off = med->med_lr_off;
 
         transno = req->rq_reqmsg->transno;
         if (rc != 0) {
@@ -147,10 +147,15 @@ int mds_finish_transno(struct mds_obd *mds, struct inode *inode, void *handle,
         mcd->mcd_last_result = cpu_to_le32(rc);
         mcd->mcd_last_data = cpu_to_le32(op_data);
 
-        fsfilt_add_journal_cb(req->rq_export->exp_obd, transno, handle,
-                              mds_commit_cb, NULL);
-        err = fsfilt_write_record(obd, mds->mds_rcvd_filp, mcd, sizeof(*mcd),
-                                  &off, 0);
+        if (off <= 0) {
+                CERROR("client idx %d has offset %lld\n", med->med_lr_idx, off);
+                err = -EINVAL;
+        } else {
+                fsfilt_add_journal_cb(req->rq_export->exp_obd, transno, handle,
+                                      mds_commit_cb, NULL);
+                err = fsfilt_write_record(obd, mds->mds_rcvd_filp, mcd,
+                                          sizeof(*mcd), &off, 0);
+        }
 
         if (err) {
                 log_pri = D_ERROR;
@@ -160,7 +165,7 @@ int mds_finish_transno(struct mds_obd *mds, struct inode *inode, void *handle,
 
         DEBUG_REQ(log_pri, req,
                   "wrote trans #"LPU64" rc %d client %s at idx %u: err = %d",
-                  transno, rc, mcd->mcd_uuid, med->med_idx, err);
+                  transno, rc, mcd->mcd_uuid, med->med_lr_idx, err);
 
         err = mds_lov_write_objids(obd);
         if (err) {
@@ -370,11 +375,11 @@ int mds_osc_setattr_async(struct obd_device *obd, struct inode *inode,
         struct lov_stripe_md *lsm = NULL;
         struct obd_trans_info oti = { 0 };
         struct obdo *oa = NULL;
-        int  cleanup_phase = 0, rc = 0;
+        int rc;
         ENTRY;
 
         if (OBD_FAIL_CHECK(OBD_FAIL_MDS_OST_SETATTR))
-                GOTO(cleanup, rc);
+                RETURN(0);
 
         /* first get memory EA */
         oa = obdo_alloc();
@@ -383,14 +388,12 @@ int mds_osc_setattr_async(struct obd_device *obd, struct inode *inode,
 
         LASSERT(lmm);
 
-        cleanup_phase = 1;
         rc = obd_unpackmd(mds->mds_osc_exp, &lsm, lmm, lmm_size);
         if (rc < 0) {
                 CERROR("Error unpack md %p\n", lmm);
-                GOTO(cleanup, rc);
+                GOTO(out, rc);
         }
-        
-        cleanup_phase = 2;
+
         /* then fill oa */
         oa->o_id = lsm->lsm_object_id;
         oa->o_uid = inode->i_uid;
@@ -400,23 +403,16 @@ int mds_osc_setattr_async(struct obd_device *obd, struct inode *inode,
                 oa->o_valid |= OBD_MD_FLCOOKIE;
                 oti.oti_logcookies = logcookies;
         }
-                                                                                                                             
+
         /* do setattr from mds to ost asynchronously */
         rc = obd_setattr_async(mds->mds_osc_exp, oa, lsm, &oti);
         if (rc)
-                CDEBUG(D_INODE, "mds to ost setattr objid 0x"LPX64" on ost error "
-                       "%d\n", lsm->lsm_object_id, rc);
-cleanup:
-        switch(cleanup_phase) {
-        case 2:
-                obd_free_memmd(mds->mds_osc_exp, &lsm);
-        case 1:
-                obdo_free(oa);
-        case 0:
-                if (logcookies)
-                        OBD_FREE(logcookies, mds->mds_max_cookiesize);
-        }
-                                                                                                                             
+                CDEBUG(D_INODE, "mds to ost setattr objid 0x"LPX64
+                       " on ost error %d\n", lsm->lsm_object_id, rc);
+
+        obd_free_memmd(mds->mds_osc_exp, &lsm);
+  out:
+        obdo_free(oa);
         RETURN(rc);
 }
 
@@ -483,17 +479,17 @@ static int mds_reint_setattr(struct mds_update_record *rec, int offset,
 
         OBD_FAIL_WRITE(OBD_FAIL_MDS_REINT_SETATTR_WRITE, inode->i_sb);
 
-        /* start a log jounal handle if needed*/
+        /* start a log jounal handle if needed */
         if (S_ISREG(inode->i_mode) &&
             rec->ur_iattr.ia_valid & (ATTR_UID | ATTR_GID)) {
                 lmm_size = mds->mds_max_mdsize;
                 OBD_ALLOC(lmm, lmm_size);
                 if (lmm == NULL)
                         GOTO(cleanup, rc = -ENOMEM);
-                
+
                 cleanup_phase = 2;
                 rc = mds_get_md(obd, inode, lmm, &lmm_size, need_lock);
-                if (rc < 0) 
+                if (rc < 0)
                         GOTO(cleanup, rc);
 
                 handle = fsfilt_start_log(obd, inode, FSFILT_OP_SETATTR, NULL,
@@ -523,6 +519,7 @@ static int mds_reint_setattr(struct mds_update_record *rec, int offset,
                         OBD_ALLOC(logcookies, mds->mds_max_cookiesize);
                         if (logcookies == NULL)
                                 GOTO(cleanup, rc = -ENOMEM);
+
                         if (mds_log_op_setattr(obd, inode, lmm, lmm_size,
                                                logcookies,
                                                mds->mds_max_cookiesize) <= 0) {
@@ -542,20 +539,19 @@ static int mds_reint_setattr(struct mds_update_record *rec, int offset,
                         GOTO(cleanup, rc);
 
                 lum = rec->ur_eadata;
-                /* if lmm_stripe_size is -1,  then delete the stripe 
-                        info from the dir */
-                if (S_ISDIR(inode->i_mode) && 
+                /* if lmm_stripe_size is -1 then delete stripe info from dir */
+                if (S_ISDIR(inode->i_mode) &&
                     lum->lmm_stripe_size == (typeof(lum->lmm_stripe_size))(-1)){
                         rc = fsfilt_set_md(obd, inode, handle, NULL, 0);
                         if (rc)
                                 GOTO(cleanup, rc);
                 } else {
                         rc = obd_iocontrol(OBD_IOC_LOV_SETSTRIPE,
-                                           mds->mds_osc_exp, 0, 
+                                           mds->mds_osc_exp, 0,
                                            &lsm, rec->ur_eadata);
                         if (rc)
                                 GOTO(cleanup, rc);
-                        
+
                         obd_free_memmd(mds->mds_osc_exp, &lsm);
 
                         rc = fsfilt_set_md(obd, inode, handle, rec->ur_eadata,
@@ -602,12 +598,14 @@ static int mds_reint_setattr(struct mds_update_record *rec, int offset,
                                       mds_cancel_cookies_cb, mlcd);
         err = mds_finish_transno(mds, inode, handle, req, rc, 0);
         /* do mds to ost setattr if needed */
-        if (!rc && !err && lmm_size) 
+        if (!rc && !err && lmm_size)
                 mds_osc_setattr_async(obd, inode, lmm, lmm_size, logcookies);
 
         switch (cleanup_phase) {
         case 2:
                 OBD_FREE(lmm, mds->mds_max_mdsize);
+                if (logcookies)
+                        OBD_FREE(logcookies, mds->mds_max_cookiesize);
         case 1:
                 if ((S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)) &&
                     rec->ur_eadata != NULL)
@@ -632,7 +630,7 @@ static int mds_reint_setattr(struct mds_update_record *rec, int offset,
 
         /* trigger dqrel/dqacq for original owner and new owner */
         if (rec->ur_iattr.ia_valid & (ATTR_UID | ATTR_GID)) {
-                mds_adjust_qunit(obd, rec->ur_iattr.ia_uid, 
+                mds_adjust_qunit(obd, rec->ur_iattr.ia_uid,
                                  rec->ur_iattr.ia_gid, 0, 0, rc);
                 mds_adjust_qunit(obd, child_uid, child_gid, 0, 0, rc);
         }
@@ -698,7 +696,8 @@ static int mds_reint_create(struct mds_update_record *rec, int offset,
         if (IS_ERR(dparent)) {
                 rc = PTR_ERR(dparent);
                 if (rc != -ENOENT)
-                        CERROR("parent lookup error %d\n", rc);
+                        CERROR("parent "LPU64"/%u lookup error %d\n",
+                               rec->ur_fid1->id, rec->ur_fid1->generation, rc);
                 GOTO(cleanup, rc);
         }
         cleanup_phase = 1; /* locked parent dentry */
@@ -810,24 +809,8 @@ static int mds_reint_create(struct mds_update_record *rec, int offset,
                         CDEBUG(D_INODE, "recreated ino %lu with gen %u\n",
                                inode->i_ino, inode->i_generation);
                 } else {
-                        struct lustre_handle child_ino_lockh;
-
                         CDEBUG(D_INODE, "created ino %lu with gen %x\n",
                                inode->i_ino, inode->i_generation);
-
-                        /* The inode we were allocated may have just been freed
-                         * by an unlink operation.  We take this lock to
-                         * synchronize against the matching reply-ack-lock taken
-                         * in unlink, to avoid replay problems if this reply
-                         * makes it out to the client but the unlink's does not.
-                         * See bug 2029 for more detail.*/
-                        rc = mds_lock_new_child(obd, inode, &child_ino_lockh);
-                        if (rc != ELDLM_OK) {
-                                CERROR("error locking for unlink/create sync: "
-                                       "%d\n", rc);
-                        } else {
-                                ldlm_lock_decref(&child_ino_lockh, LCK_EX);
-                        }
                 }
 
                 rc = fsfilt_setattr(obd, dchild, handle, &iattr, 0);
@@ -845,12 +828,12 @@ static int mds_reint_create(struct mds_update_record *rec, int offset,
                         rc = mds_get_md(obd, dir, &lmm, &lmm_size, 1);
                         if (rc > 0) {
                                 down(&inode->i_sem);
-                                rc = fsfilt_set_md(obd, inode, handle, 
+                                rc = fsfilt_set_md(obd, inode, handle,
                                                    &lmm, lmm_size);
                                 up(&inode->i_sem);
                         }
                         if (rc)
-                                CERROR("error on copy stripe info: rc = %d\n", 
+                                CERROR("error on copy stripe info: rc = %d\n",
                                         rc);
                 }
 
@@ -882,6 +865,13 @@ cleanup:
                         break;
                 }
         } else if (created) {
+                /* The inode we were allocated may have just been freed
+                 * by an unlink operation.  We take this lock to
+                 * synchronize against the matching reply-ack-lock taken
+                 * in unlink, to avoid replay problems if this reply
+                 * makes it out to the client but the unlink's does not.
+                 * See bug 2029 for more detail.*/
+                mds_lock_new_child(obd, dchild->d_inode, NULL);
                 /* save uid/gid of create inode and parent */
                 parent_uid = dir->i_uid;
                 parent_gid = dir->i_gid;
@@ -906,9 +896,9 @@ cleanup:
                 LBUG();
         }
         req->rq_status = rc;
-        
+
         /* trigger dqacq on the owner of child and parent */
-        mds_adjust_qunit(obd, current->fsuid, current->fsgid, 
+        mds_adjust_qunit(obd, current->fsuid, current->fsgid,
                          parent_uid, parent_gid, rc);
         return 0;
 }
@@ -1401,7 +1391,7 @@ static int mds_reint_unlink(struct mds_update_record *rec, int offset,
         child_gid = child_inode->i_gid;
         parent_uid = dparent->d_inode->i_uid;
         parent_gid = dparent->d_inode->i_gid;
-        
+
         cleanup_phase = 2; /* dchild has a lock */
 
         /* We have to do these checks ourselves, in case we are making an
@@ -2082,16 +2072,16 @@ int mds_reint_rec(struct mds_update_record *rec, int offset,
                   struct ptlrpc_request *req, struct lustre_handle *lockh)
 {
         struct obd_device *obd = req->rq_export->exp_obd;
-        struct obd_run_ctxt saved;
+        struct lvfs_run_ctxt saved;
         int rc;
         ENTRY;
 
         /* checked by unpacker */
         LASSERT(rec->ur_opcode < REINT_MAX && reinters[rec->ur_opcode] != NULL);
 
-        push_ctxt(&saved, &obd->obd_ctxt, &rec->ur_uc);
+        push_ctxt(&saved, &obd->obd_lvfs_ctxt, &rec->ur_uc);
         rc = reinters[rec->ur_opcode] (rec, offset, req, lockh);
-        pop_ctxt(&saved, &obd->obd_ctxt, &rec->ur_uc);
+        pop_ctxt(&saved, &obd->obd_lvfs_ctxt, &rec->ur_uc);
 
         RETURN(rc);
 }
index 737299c..6298eb4 100644 (file)
@@ -129,10 +129,10 @@ static int mds_unlink_orphan(struct obd_device *obd, struct dentry *dchild,
         }
 
         rc = vfs_unlink(pending_dir, dchild);
-        if (rc)
+        if (rc) {
                 CERROR("error %d unlinking orphan %.*s from PENDING\n",
                        rc, dchild->d_name.len, dchild->d_name.name);
-        else if (lmm_size) {
+        else if (lmm_size) {
                 OBD_ALLOC(logcookies, mds->mds_max_cookiesize);
                 if (logcookies == NULL)
                         rc = -ENOMEM;
@@ -161,7 +161,7 @@ out_free_lmm:
 int mds_cleanup_orphans(struct obd_device *obd)
 {
         struct mds_obd *mds = &obd->u.mds;
-        struct obd_run_ctxt saved;
+        struct lvfs_run_ctxt saved;
         struct file *file;
         struct dentry *dchild, *dentry;
         struct vfsmount *mnt;
@@ -173,7 +173,7 @@ int mds_cleanup_orphans(struct obd_device *obd)
         int i = 0, rc = 0, item = 0, namlen;
         ENTRY;
 
-        push_ctxt(&saved, &obd->obd_ctxt, NULL);
+        push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
         dentry = dget(mds->mds_pending_dir);
         if (IS_ERR(dentry))
                 GOTO(err_pop, rc = PTR_ERR(dentry));
@@ -219,6 +219,13 @@ int mds_cleanup_orphans(struct obd_device *obd)
                         GOTO(next, rc = 0);
                 }
 
+                if (is_bad_inode(dchild->d_inode)) {
+                        CERROR("bad orphan inode found %lu/%u\n",
+                               dchild->d_inode->i_ino,
+                               dchild->d_inode->i_generation);
+                        GOTO(next, rc = -ENOENT);
+                }
+
                 child_inode = dchild->d_inode;
                 MDS_DOWN_READ_ORPHAN_SEM(child_inode);
                 if (mds_inode_is_orphan(child_inode) &&
@@ -248,7 +255,7 @@ err_out:
                 OBD_FREE(dirent, sizeof(*dirent));
         }
 err_pop:
-        pop_ctxt(&saved, &obd->obd_ctxt, NULL);
+        pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
         if (rc == 0)
                 rc = item;
         RETURN(rc);
index 907255a..1672bb2 100644 (file)
@@ -9,18 +9,8 @@
  *
  *   This file is part of Lustre, http://www.lustre.org.
  *
- *   Lustre is free software; you can redistribute it and/or
- *   modify it under the terms of version 2 of the GNU General Public
- *   License as published by the Free Software Foundation.
+ *   No redistribution or use is permitted outside of Cluster File Systems, Inc.
  *
- *   Lustre is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with Lustre; if not, write to the Free Software
- *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 #ifndef EXPORT_SYMTAB
 # define EXPORT_SYMTAB
@@ -442,8 +432,10 @@ schedule_dqacq(struct obd_device *obd,
                 /* build dqacq/dqrel request */
                 LASSERT(qctxt->lqc_import);
                 req = ptlrpc_prep_req(qctxt->lqc_import, opc, 1, &size, NULL);
-                if (!req)
+                if (!req) {
+                        dqacq_completion(obd, qctxt, qdata, -ENOMEM, opc);
                         RETURN(-ENOMEM);
+                }
 
                 reqdata = lustre_msg_buf(req->rq_reqmsg, 0, sizeof(*reqdata));
                 memcpy(reqdata, qdata, sizeof(*reqdata));
@@ -470,7 +462,7 @@ wait_completion:
                 if (qw.qw_rc == 0)
                         rc = -EAGAIN;
 
-                QDATA_DEBUG(p, "wait dqacq done. (rc:%d)\n", qw.qw_rc);
+                CDEBUG(D_QUOTA, "wait dqacq done. (rc:%d)\n", qw.qw_rc);
         }
         RETURN(rc);
 }
@@ -510,6 +502,7 @@ next:
 
         RETURN(rc);
 }
+EXPORT_SYMBOL(qctxt_adjust_qunit);
 
 int
 qctxt_wait_on_dqacq(struct obd_device *obd, struct lustre_quota_ctxt *qctxt,
@@ -541,6 +534,7 @@ next:
 
         RETURN(rc);
 }
+EXPORT_SYMBOL(qctxt_wait_on_dqacq);
 
 int
 qctxt_init(struct lustre_quota_ctxt *qctxt, struct super_block *sb,
@@ -564,6 +558,7 @@ qctxt_init(struct lustre_quota_ctxt *qctxt, struct super_block *sb,
 
         RETURN(0);
 }
+EXPORT_SYMBOL(qctxt_init);
 
 void qctxt_cleanup(struct lustre_quota_ctxt *qctxt, int force)
 {
@@ -586,3 +581,4 @@ void qctxt_cleanup(struct lustre_quota_ctxt *qctxt, int force)
         spin_unlock(&qunit_hash_lock);
         EXIT;
 }
+EXPORT_SYMBOL(qctxt_cleanup);
index e7ca633..36c27e0 100644 (file)
@@ -9,18 +9,8 @@
  *
  *   This file is part of Lustre, http://www.lustre.org.
  *
- *   Lustre is free software; you can redistribute it and/or
- *   modify it under the terms of version 2 of the GNU General Public
- *   License as published by the Free Software Foundation.
+ *   No redistribution or use is permitted outside of Cluster File Systems, Inc.
  *
- *   Lustre is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with Lustre; if not, write to the Free Software
- *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 #ifndef EXPORT_SYMTAB
 # define EXPORT_SYMTAB
@@ -229,10 +219,15 @@ int dqacq_handler(struct obd_device *obd, struct qunit_data *qdata, int opc)
         struct lustre_quota_info *info = &mds->mds_quota_info;
         struct lustre_dquot *dquot = NULL;
         __u64 *usage = NULL;
-        __u32 *limit = NULL;
+        __u32 hlimit = 0, slimit = 0;
+        time_t *time = NULL;
+        unsigned int grace = 0;
         int rc = 0;
         ENTRY;
 
+        /* slaves never acquires qunit for user root */
+        LASSERT(qdata->qd_id || qdata->qd_type == GRPQUOTA);
+
         dquot = lustre_dqget(obd, info, qdata->qd_id, qdata->qd_type);
         if (IS_ERR(dquot))
                 RETURN(PTR_ERR(dquot));
@@ -244,27 +239,48 @@ int dqacq_handler(struct obd_device *obd, struct qunit_data *qdata, int opc)
         down(&dquot->dq_sem);
 
         if (qdata->qd_isblk) {
+                grace = info->qi_info[qdata->qd_type].dqi_bgrace;
                 usage = &dquot->dq_dqb.dqb_curspace;
-                limit = &dquot->dq_dqb.dqb_bhardlimit;
+                hlimit = dquot->dq_dqb.dqb_bhardlimit;
+                slimit = dquot->dq_dqb.dqb_bsoftlimit;
+                time = &dquot->dq_dqb.dqb_btime;
         } else {
+                grace = info->qi_info[qdata->qd_type].dqi_igrace;
                 usage = (__u64 *) & dquot->dq_dqb.dqb_curinodes;
-                limit = &dquot->dq_dqb.dqb_ihardlimit;
-        }
+                hlimit = dquot->dq_dqb.dqb_ihardlimit;
+                slimit = dquot->dq_dqb.dqb_isoftlimit;
+                time = &dquot->dq_dqb.dqb_itime;
+        } 
 
         /* if the quota limit in admin quotafile is zero, we just inform
          * slave to clear quota limit with zero qd_count */
-        if (*limit == 0) {
+        if (hlimit == 0 && slimit == 0) {
                 qdata->qd_count = 0;
                 GOTO(out, rc);
         }
+        
         if (opc == QUOTA_DQACQ) {
-                if (QUSG(*usage + qdata->qd_count, qdata->qd_isblk) > *limit)
+                if (hlimit && 
+                    QUSG(*usage + qdata->qd_count, qdata->qd_isblk) > hlimit)
                         GOTO(out, rc = -EDQUOT);
-                else
-                        *usage += qdata->qd_count;
+
+                if (slimit &&
+                    QUSG(*usage + qdata->qd_count, qdata->qd_isblk) > slimit) {
+                        if (*time && CURRENT_SECONDS >= *time)
+                                GOTO(out, rc = -EDQUOT);
+                        else if (!*time)
+                                *time = CURRENT_SECONDS + grace;
+                }
+
+                *usage += qdata->qd_count;
+                
         } else if (opc == QUOTA_DQREL) {
                 LASSERT(*usage - qdata->qd_count >= 0);
                 *usage -= qdata->qd_count;
+
+                /* (usage <= soft limit) but not (usage < soft limit) */
+                if (!slimit || QUSG(*usage, qdata->qd_isblk) <= slimit)
+                        *time = 0;
         } else {
                 LBUG();
         }
@@ -304,7 +320,7 @@ int init_admin_quotafiles(struct obd_device *obd, struct obd_quotactl *oqctl)
         struct mds_obd *mds = &obd->u.mds;
         struct lustre_quota_info *qinfo = &mds->mds_quota_info;
         const char *quotafiles[] = LUSTRE_ADMIN_QUOTAFILES;
-        struct obd_run_ctxt saved;
+        struct lvfs_run_ctxt saved;
         char name[64];
         int i, rc = 0;
         struct dentry *dparent = mds->mds_objects_dir;
@@ -312,7 +328,7 @@ int init_admin_quotafiles(struct obd_device *obd, struct obd_quotactl *oqctl)
         ENTRY;
 
         LASSERT(iparent);
-        push_ctxt(&saved, &obd->obd_ctxt, NULL);
+        push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
 
         down(&qinfo->qi_sem);
         for (i = 0; i < MAXQUOTAS; i++) {
@@ -374,7 +390,7 @@ int init_admin_quotafiles(struct obd_device *obd, struct obd_quotactl *oqctl)
         }
         up(&qinfo->qi_sem);
 
-        pop_ctxt(&saved, &obd->obd_ctxt, NULL);
+        pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
         RETURN(rc);
 }
 
@@ -383,14 +399,14 @@ int mds_quota_on(struct obd_device *obd, struct obd_quotactl *oqctl)
         struct mds_obd *mds = &obd->u.mds;
         struct lustre_quota_info *qinfo = &mds->mds_quota_info;
         const char *quotafiles[] = LUSTRE_ADMIN_QUOTAFILES;
-        struct obd_run_ctxt saved;
+        struct lvfs_run_ctxt saved;
         char name[64];
         int i, rc = 0;
         struct inode *iparent = mds->mds_objects_dir->d_inode;
         ENTRY;
 
         LASSERT(iparent);
-        push_ctxt(&saved, &obd->obd_ctxt, NULL);
+        push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
 
         down(&qinfo->qi_sem);
         /* open admin quota files and read quotafile info */
@@ -424,7 +440,7 @@ int mds_quota_on(struct obd_device *obd, struct obd_quotactl *oqctl)
         }
         up(&qinfo->qi_sem);
 
-        pop_ctxt(&saved, &obd->obd_ctxt, NULL);
+        pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
 
         if (rc && rc != -EBUSY) {
                 down(&qinfo->qi_sem);
@@ -517,7 +533,7 @@ static int mds_init_slave_ilimits(struct obd_device *obd,
         ENTRY;
 
         /* if we are going to set zero limit, needn't init slaves */
-        if (!oqctl->qc_dqblk.dqb_ihardlimit)
+        if (!oqctl->qc_dqblk.dqb_ihardlimit && !oqctl->qc_dqblk.dqb_isoftlimit)
                 RETURN(0);
 
         OBD_ALLOC(ioqc, sizeof(*ioqc));
@@ -562,7 +578,7 @@ static int mds_init_slave_blimits(struct obd_device *obd,
         ENTRY;
 
         /* if we are going to set zero limit, needn't init slaves */
-        if (!oqctl->qc_dqblk.dqb_bhardlimit)
+        if (!oqctl->qc_dqblk.dqb_bhardlimit && !oqctl->qc_dqblk.dqb_bsoftlimit)
                 RETURN(0);
 
         OBD_ALLOC(ioqc, sizeof(*ioqc));
@@ -634,7 +650,8 @@ int mds_set_dqblk(struct obd_device *obd, struct obd_quotactl *oqctl)
                 dquot->dq_dqb.dqb_bhardlimit = dqblk->dqb_bhardlimit;
                 dquot->dq_dqb.dqb_bsoftlimit = dqblk->dqb_bsoftlimit;
                 /* clear usage (limit pool) */
-                if (dquot->dq_dqb.dqb_bhardlimit == 0)
+                if (!dquot->dq_dqb.dqb_bhardlimit && 
+                    !dquot->dq_dqb.dqb_bsoftlimit)
                         dquot->dq_dqb.dqb_curspace = 0;
         }
 
@@ -642,7 +659,8 @@ int mds_set_dqblk(struct obd_device *obd, struct obd_quotactl *oqctl)
                 dquot->dq_dqb.dqb_ihardlimit = dqblk->dqb_ihardlimit;
                 dquot->dq_dqb.dqb_isoftlimit = dqblk->dqb_isoftlimit;
                 /* clear usage (limit pool) */
-                if (dquot->dq_dqb.dqb_ihardlimit == 0)
+                if (!dquot->dq_dqb.dqb_ihardlimit &&
+                    !dquot->dq_dqb.dqb_isoftlimit)
                         dquot->dq_dqb.dqb_curinodes = 0;
         }
 
@@ -660,7 +678,7 @@ int mds_set_dqblk(struct obd_device *obd, struct obd_quotactl *oqctl)
         if (rc)
                 GOTO(out, rc);
 
-        if (dqblk->dqb_valid & QIF_ILIMITS && !ihardlimit) {
+        if (dqblk->dqb_valid & QIF_ILIMITS && !(ihardlimit || isoftlimit)) {
                 rc = mds_init_slave_ilimits(obd, oqctl);
                 if (rc) {
                         CERROR("init slave ilimits failed! (rc:%d)\n", rc);
@@ -668,7 +686,7 @@ int mds_set_dqblk(struct obd_device *obd, struct obd_quotactl *oqctl)
                 }
         }
 
-        if (dqblk->dqb_valid & QIF_BLIMITS && !bhardlimit) {
+        if (dqblk->dqb_valid & QIF_BLIMITS && !(bhardlimit || bsoftlimit)) {
                 rc = mds_init_slave_blimits(obd, oqctl);
                 if (rc) {
                         CERROR("init slave blimits failed! (rc:%d)\n", rc);
index 8d670dc..0cdce91 100644 (file)
@@ -15,5 +15,5 @@ modulefs_DATA = obdclass$(KMODEXT)
 noinst_DATA = llog_test$(KMODEXT)
 endif # MODULES
 
-MOSTLYCLEANFILES = *.o *.ko *.mod.c llog-test.c
+MOSTLYCLEANFILES := @MOSTLYCLEANFILES@  llog-test.c
 DIST_SOURCES = $(filter-out llog-test.c,$(obdclass-objs:.o=.c)) $(llog-test-objs:.o=.c) llog_test.c llog_internal.h
index 28613e3..f4bb525 100644 (file)
@@ -89,7 +89,7 @@ int proc_version;
 unsigned int obd_fail_loc;
 unsigned int obd_dump_on_timeout;
 unsigned int obd_timeout = 100; /* seconds */
-unsigned int ldlm_timeout = 6;  /* seconds */
+unsigned int ldlm_timeout = 20; /* seconds */
 char obd_lustre_upcall[128] = "DEFAULT"; /* or NONE or /full/path/to/upcall  */
 unsigned int obd_sync_filter; /* = 0, don't sync by default */
 
@@ -102,33 +102,9 @@ unsigned int obd_print_fail_loc(void)
         return obd_fail_loc;
 }
 
-void ll_set_rdonly(ll_sbdev_type dev)
+void obd_set_fail_loc(unsigned int fl)
 {
-        CDEBUG(D_IOCTL | D_HA, "set dev %ld rdonly\n", (long)dev);
-        ll_sbdev_sync(dev);
-#ifdef HAVE_OLD_DEV_SET_RDONLY
-        dev_set_rdonly(dev, 2);
-#else
-        dev_set_rdonly(dev);
-#endif
-}
-
-void ll_clear_rdonly(ll_sbdev_type dev)
-{
-#ifndef HAVE_CLEAR_RDONLY_ON_PUT
-        CDEBUG(D_IOCTL | D_HA, "unset dev %ld rdonly\n", (long)dev);
-        if (ll_check_rdonly(dev)) {
-                ll_sbdev_sync(dev);
-#ifdef HAVE_OLD_DEV_SET_RDONLY
-                dev_clear_rdonly(2);
-#else
-                dev_clear_rdonly(dev);
-#endif
-        }
-#else 
-        CDEBUG(D_IOCTL | D_HA, "(will unset dev %ld rdonly on put)\n",
-               (long)dev);
-#endif
+        obd_fail_loc = fl;
 }
 
 /*  opening /dev/obd */
@@ -234,13 +210,12 @@ int class_handle_ioctl(unsigned int cmd, unsigned long arg)
                                               data->ioc_plen1);
                 if (err)
                         GOTO(out, err);
-                
+
                 OBD_ALLOC(lcfg, data->ioc_plen1);
                 err = copy_from_user(lcfg, data->ioc_pbuf1, data->ioc_plen1);
-                if (err)
-                        GOTO(out, err);
 
-                err = class_process_config(lcfg);
+                if (!err)
+                        err = class_process_config(lcfg);
                 OBD_FREE(lcfg, data->ioc_plen1);
                 GOTO(out, err);
         }
@@ -330,7 +305,7 @@ int class_handle_ioctl(unsigned int cmd, unsigned long arg)
         if (data->ioc_dev >= MAX_OBD_DEVICES) {
                 CERROR("OBD ioctl: No device\n");
                 GOTO(out, err = -EINVAL);
-        } 
+        }
         obd = &obd_dev[data->ioc_dev];
         if (!(obd && obd->obd_set_up) || obd->obd_stopping) {
                 CERROR("OBD ioctl: device not setup %d \n", data->ioc_dev);
@@ -390,7 +365,7 @@ static struct file_operations obd_psdev_fops = {
 /* modules setup */
 static struct miscdevice obd_psdev = {
         .minor = OBD_MINOR,
-        .name  = "obd_psdev",
+        .name  = "obd",
         .fops  = &obd_psdev_fops,
 };
 #else
@@ -398,13 +373,7 @@ void *obd_psdev = NULL;
 #endif
 
 EXPORT_SYMBOL(obd_dev);
-EXPORT_SYMBOL(obdo_cachep);
-EXPORT_SYMBOL(qunit_cachep);
-EXPORT_SYMBOL(qunit_hash_lock);
-EXPORT_SYMBOL(qunit_hash);
 EXPORT_SYMBOL(obd_fail_loc);
-EXPORT_SYMBOL(ll_set_rdonly);
-EXPORT_SYMBOL(ll_clear_rdonly);
 EXPORT_SYMBOL(obd_print_fail_loc);
 EXPORT_SYMBOL(obd_race_waitq);
 EXPORT_SYMBOL(obd_dump_on_timeout);
@@ -414,6 +383,8 @@ EXPORT_SYMBOL(obd_lustre_upcall);
 EXPORT_SYMBOL(obd_sync_filter);
 EXPORT_SYMBOL(ptlrpc_put_connection_superhack);
 EXPORT_SYMBOL(ptlrpc_abort_inflight_superhack);
+
+struct proc_dir_entry *proc_lustre_root;
 EXPORT_SYMBOL(proc_lustre_root);
 
 EXPORT_SYMBOL(class_register_type);
@@ -427,28 +398,12 @@ EXPORT_SYMBOL(class_uuid2obd);
 EXPORT_SYMBOL(class_find_client_obd);
 EXPORT_SYMBOL(class_find_client_notype);
 EXPORT_SYMBOL(class_devices_in_group);
-EXPORT_SYMBOL(__class_export_put);
-EXPORT_SYMBOL(class_new_export);
-EXPORT_SYMBOL(class_unlink_export);
-EXPORT_SYMBOL(class_import_get);
-EXPORT_SYMBOL(class_import_put);
-EXPORT_SYMBOL(class_new_import);
-EXPORT_SYMBOL(class_destroy_import);
-EXPORT_SYMBOL(class_connect);
 EXPORT_SYMBOL(class_conn2export);
 EXPORT_SYMBOL(class_exp2obd);
 EXPORT_SYMBOL(class_conn2obd);
 EXPORT_SYMBOL(class_exp2cliimp);
 EXPORT_SYMBOL(class_conn2cliimp);
 EXPORT_SYMBOL(class_disconnect);
-EXPORT_SYMBOL(class_disconnect_exports);
-EXPORT_SYMBOL(class_disconnect_stale_exports);
-
-EXPORT_SYMBOL(oig_init);
-EXPORT_SYMBOL(oig_release);
-EXPORT_SYMBOL(oig_add_one);
-EXPORT_SYMBOL(oig_wait);
-EXPORT_SYMBOL(oig_complete_one);
 
 /* uuid.c */
 EXPORT_SYMBOL(class_uuid_unparse);
@@ -497,15 +452,58 @@ int obd_proc_read_pinger(char *page, char **start, off_t off, int count,
                        );
 }
 
+static int obd_proc_read_health(char *page, char **start, off_t off,
+                                int count, int *eof, void *data)
+{
+        int rc = 0; //, i;
+        *eof = 1;
+
+        if (portals_catastrophe)
+                rc += snprintf(page + rc, count - rc, "LBUG\n");
+
+#if 0
+        spin_lock(&obd_dev_lock);
+        for (i = 0; i < MAX_OBD_DEVICES; i++) {
+                struct obd_device *obd;
+
+                obd = &obd_dev[i];
+                if (obd->obd_type == NULL)
+                        continue;
+
+                atomic_inc(&obd->obd_refcount);
+                spin_unlock(&obd_dev_lock);
+
+                if (obd_health_check(obd)) {
+                        rc += snprintf(page + rc, count - rc,
+                                       "device %s reported unhealthy\n",
+                                       obd->obd_name);
+                }
+                class_decref(obd);
+                spin_lock(&obd_dev_lock);
+        }
+        spin_unlock(&obd_dev_lock);
+#endif
+
+        if (rc == 0)
+                return snprintf(page, count, "healthy\n");
+
+        rc += snprintf(page + rc, count - rc, "NOT HEALTHY\n");
+        return rc;
+}
+
 /* Root for /proc/fs/lustre */
-struct proc_dir_entry *proc_lustre_root = NULL;
 struct lprocfs_vars lprocfs_base[] = {
         { "version", obd_proc_read_version, NULL, NULL },
         { "kernel_version", obd_proc_read_kernel_version, NULL, NULL },
         { "pinger", obd_proc_read_pinger, NULL, NULL },
+        { "health_check", obd_proc_read_health, NULL, NULL },
         { 0 }
 };
+#else
+#define lprocfs_base NULL
+#endif /* LPROCFS */
 
+#ifdef __KERNEL__
 static void *obd_device_list_seq_start(struct seq_file *p, loff_t*pos)
 {
         if (*pos >= MAX_OBD_DEVICES)
@@ -590,13 +588,13 @@ int obd_init_checks(void)
         CDEBUG(D_INFO, "LPU64=%s, LPD64=%s, LPX64=%s, LPSZ=%s, LPSSZ=%s\n",
                LPU64, LPD64, LPX64, LPSZ, LPSSZ);
 
-        CDEBUG(D_INFO, "OBD_OBJECT_EOF = "LPX64"\n", OBD_OBJECT_EOF);
+        CDEBUG(D_INFO, "OBD_OBJECT_EOF = "LPX64"\n", (__u64)OBD_OBJECT_EOF);
 
         u64val = OBD_OBJECT_EOF;
         CDEBUG(D_INFO, "u64val OBD_OBJECT_EOF = "LPX64"\n", u64val);
         if (u64val != OBD_OBJECT_EOF) {
                 CERROR("__u64 "LPX64"(%d) != 0xffffffffffffffff\n",
-                       u64val, sizeof(u64val));
+                       u64val, (int)sizeof(u64val));
                 ret = -EINVAL;
         }
         len = snprintf(buf, sizeof(buf), LPX64, u64val);
@@ -609,12 +607,12 @@ int obd_init_checks(void)
         CDEBUG(D_INFO, "u64val OBD_OBJECT_EOF = "LPX64"\n", u64val);
         if (u64val != OBD_OBJECT_EOF) {
                 CERROR("__u64 "LPX64"(%d) != 0xffffffffffffffff\n",
-                       u64val, sizeof(u64val));
+                       u64val, (int)sizeof(u64val));
                 ret = -EOVERFLOW;
         }
         if (u64val >> 8 != OBD_OBJECT_EOF >> 8) {
                 CERROR("__u64 "LPX64"(%d) != 0xffffffffffffffff\n",
-                       u64val, sizeof(u64val));
+                       u64val, (int)sizeof(u64val));
                 return -EOVERFLOW;
         }
         if (do_div(div64val, 256) != (u64val & 255)) {
@@ -659,7 +657,7 @@ int init_obdclass(void)
 #endif
 {
         struct obd_device *obd;
-#ifdef LPROCFS
+#ifdef __KERNEL__
         struct proc_dir_entry *entry;
 #endif
         int err;
@@ -696,9 +694,7 @@ int init_obdclass(void)
 
 #ifdef __KERNEL__
         obd_sysctl_init();
-#endif
 
-#ifdef LPROCFS
         proc_lustre_root = proc_mkdir("lustre", proc_root_fs);
         if (!proc_lustre_root) {
                 printk(KERN_ERR
@@ -740,12 +736,11 @@ static void cleanup_obdclass(void)
 
         obd_cleanup_caches();
         obd_sysctl_clean();
-#ifdef LPROCFS
+
         if (proc_lustre_root) {
                 lprocfs_remove(proc_lustre_root);
                 proc_lustre_root = NULL;
         }
-#endif
 
         class_handle_cleanup();
         class_exit_uuidlist();
@@ -761,7 +756,7 @@ static void cleanup_obdclass(void)
  * kernel patch */
 #include <linux/lustre_version.h>
 #define LUSTRE_MIN_VERSION 32
-#define LUSTRE_MAX_VERSION 45
+#define LUSTRE_MAX_VERSION 46
 #if (LUSTRE_KERNEL_VERSION < LUSTRE_MIN_VERSION)
 # error Cannot continue: Your Lustre kernel patch is older than the sources
 #elif (LUSTRE_KERNEL_VERSION > LUSTRE_MAX_VERSION)
index 43767e0..165a7df 100644 (file)
@@ -36,6 +36,8 @@
 #else
 #include <liblustre.h>
 #include <linux/obd_class.h>
+#include <linux/lustre_mds.h>
+#include <linux/obd_ost.h>
 #include <linux/obd.h>
 #include <linux/lustre_mds.h>
 #include <linux/obd_ost.h>
 
 extern struct list_head obd_types;
 static spinlock_t obd_types_lock = SPIN_LOCK_UNLOCKED;
+
 kmem_cache_t *obdo_cachep = NULL;
+EXPORT_SYMBOL(obdo_cachep);
 kmem_cache_t *import_cachep = NULL;
 
+#ifdef HAVE_QUOTA_SUPPORT
 kmem_cache_t *qunit_cachep = NULL;
 struct list_head qunit_hash[NR_DQHASH];
 spinlock_t qunit_hash_lock = SPIN_LOCK_UNLOCKED;
+EXPORT_SYMBOL(qunit_cachep);
+EXPORT_SYMBOL(qunit_hash);
+EXPORT_SYMBOL(qunit_hash_lock);
+#endif
+
 
 int (*ptlrpc_put_connection_superhack)(struct ptlrpc_connection *c);
 void (*ptlrpc_abort_inflight_superhack)(struct obd_import *imp);
@@ -67,8 +77,7 @@ static struct obd_type *class_search_type(char *name)
         spin_lock(&obd_types_lock);
         list_for_each(tmp, &obd_types) {
                 type = list_entry(tmp, struct obd_type, typ_chain);
-                if (strlen(type->typ_name) == strlen(name) &&
-                    strcmp(type->typ_name, name) == 0) {
+                if (strcmp(type->typ_name, name) == 0) {
                         spin_unlock(&obd_types_lock);
                         return type;
                 }
@@ -243,7 +252,7 @@ int class_name2dev(char *name)
                 if (obd->obd_name && strcmp(name, obd->obd_name) == 0) {
                         /* Make sure we finished attaching before we give
                            out any references */
-                        if (obd->obd_attached) { 
+                        if (obd->obd_attached) {
                                 spin_unlock(&obd_dev_lock);
                                 return i;
                         }
@@ -365,6 +374,7 @@ struct obd_device * class_devices_in_group(struct obd_uuid *grp_uuid, int *next)
 
 static void obd_cleanup_qunit_cache(void)
 {
+#ifdef HAVE_QUOTA_SUPPORT
         int i;
         ENTRY;
 
@@ -372,13 +382,14 @@ static void obd_cleanup_qunit_cache(void)
         for (i = 0; i < NR_DQHASH; i++)
                 LASSERT(list_empty(qunit_hash + i));
         spin_unlock(&qunit_hash_lock);
-        
+
         if (qunit_cachep) {
                 LASSERTF(kmem_cache_destroy(qunit_cachep) == 0,
                          "Cannot destroy ll_qunit_cache\n");
                 qunit_cachep = NULL;
         }
         EXIT;
+#endif
 }
 
 void obd_cleanup_caches(void)
@@ -400,11 +411,13 @@ void obd_cleanup_caches(void)
 
 static int obd_init_qunit_cache(void)
 {
+
+#ifdef HAVE_QUOTA_SUPPORT
         int i;
         ENTRY;
-        
+
         LASSERT(qunit_cachep == NULL);
-        qunit_cachep = kmem_cache_create("ll_qunit_cache", 
+        qunit_cachep = kmem_cache_create("ll_qunit_cache",
                                          sizeof(struct lustre_qunit),
                                          0, 0, NULL, NULL);
         if (!qunit_cachep)
@@ -414,6 +427,7 @@ static int obd_init_qunit_cache(void)
         for (i = 0; i < NR_DQHASH; i++)
                 INIT_LIST_HEAD(qunit_hash + i);
         spin_unlock(&qunit_hash_lock);
+#endif
         RETURN(0);
 }
 
@@ -529,6 +543,7 @@ void __class_export_put(struct obd_export *exp)
                 class_decref(obd);
         }
 }
+EXPORT_SYMBOL(__class_export_put);
 
 /* Creates a new export, adds it to the hash table, and returns a
  * pointer to it. The refcount is 2: one for the hash reference, and
@@ -552,17 +567,22 @@ struct obd_export *class_new_export(struct obd_device *obd)
 
         INIT_LIST_HEAD(&export->exp_handle.h_link);
         class_handle_hash(&export->exp_handle, export_handle_addref);
+        export->exp_last_request_time = CURRENT_SECONDS;
         spin_lock_init(&export->exp_lock);
 
         spin_lock(&obd->obd_dev_lock);
         LASSERT(!obd->obd_stopping); /* shouldn't happen, but might race */
         atomic_inc(&obd->obd_refcount);
         list_add(&export->exp_obd_chain, &export->exp_obd->obd_exports);
+        list_add_tail(&export->exp_obd_chain_timed,
+                      &export->exp_obd->obd_exports_timed);
         export->exp_obd->obd_num_exports++;
         spin_unlock(&obd->obd_dev_lock);
+
         obd_init_export(export);
         return export;
 }
+EXPORT_SYMBOL(class_new_export);
 
 void class_unlink_export(struct obd_export *exp)
 {
@@ -570,11 +590,13 @@ void class_unlink_export(struct obd_export *exp)
 
         spin_lock(&exp->exp_obd->obd_dev_lock);
         list_del_init(&exp->exp_obd_chain);
+        list_del_init(&exp->exp_obd_chain_timed);
         exp->exp_obd->obd_num_exports--;
         spin_unlock(&exp->exp_obd->obd_dev_lock);
 
         class_export_put(exp);
 }
+EXPORT_SYMBOL(class_unlink_export);
 
 /* Import management functions */
 static void import_handle_addref(void *import)
@@ -584,11 +606,14 @@ static void import_handle_addref(void *import)
 
 struct obd_import *class_import_get(struct obd_import *import)
 {
+        LASSERT(atomic_read(&import->imp_refcount) >= 0);
+        LASSERT(atomic_read(&import->imp_refcount) < 0x5a5a5a);
         atomic_inc(&import->imp_refcount);
         CDEBUG(D_IOCTL, "import %p refcount=%d\n", import,
                atomic_read(&import->imp_refcount));
         return import;
 }
+EXPORT_SYMBOL(class_import_get);
 
 void class_import_put(struct obd_import *import)
 {
@@ -622,6 +647,7 @@ void class_import_put(struct obd_import *import)
         OBD_FREE(import, sizeof(*import));
         EXIT;
 }
+EXPORT_SYMBOL(class_import_put);
 
 struct obd_import *class_new_import(void)
 {
@@ -650,6 +676,7 @@ struct obd_import *class_new_import(void)
 
         return imp;
 }
+EXPORT_SYMBOL(class_new_import);
 
 void class_destroy_import(struct obd_import *import)
 {
@@ -667,6 +694,7 @@ void class_destroy_import(struct obd_import *import)
 
         class_import_put(import);
 }
+EXPORT_SYMBOL(class_destroy_import);
 
 /* A connection defines an export context in which preallocation can
    be managed. This releases the export pointer reference, and returns
@@ -694,6 +722,7 @@ int class_connect(struct lustre_handle *conn, struct obd_device *obd,
                cluuid->uuid, conn->cookie);
         RETURN(0);
 }
+EXPORT_SYMBOL(class_connect);
 
 /* This function removes two references from the export: one for the
  * hash entry and one for the export pointer passed in.  The export
@@ -701,6 +730,7 @@ int class_connect(struct lustre_handle *conn, struct obd_device *obd,
  * again. */
 int class_disconnect(struct obd_export *export)
 {
+        int already_disconnected;
         ENTRY;
 
         if (export == NULL) {
@@ -709,10 +739,15 @@ int class_disconnect(struct obd_export *export)
                 RETURN(-EINVAL);
         }
 
-        /* XXX this shouldn't have to be here, but double-disconnect will crash
-         * otherwise, and sometimes double-disconnect happens.  abort_recovery,
-         * for example. */
-        if (list_empty(&export->exp_handle.h_link))
+        spin_lock(&export->exp_lock);
+        already_disconnected = export->exp_disconnected;
+        export->exp_disconnected = 1;
+        spin_unlock(&export->exp_lock);
+
+        /* class_cleanup(), abort_recovery(), and class_fail_export()
+         * all end up in here, and if any of them race we shouldn't
+         * call extra class_export_puts(). */
+        if (already_disconnected)
                 RETURN(0);
 
         CDEBUG(D_IOCTL, "disconnect: cookie "LPX64"\n",
@@ -723,14 +758,14 @@ int class_disconnect(struct obd_export *export)
         RETURN(0);
 }
 
-static void  class_disconnect_export_list(struct list_head *list, int flags)
+static void class_disconnect_export_list(struct list_head *list, int flags)
 {
         int rc;
         struct lustre_handle fake_conn;
         struct obd_export *fake_exp, *exp;
         ENTRY;
 
-        /* It's possible that an export may disconnect itself, but 
+        /* It's possible that an export may disconnect itself, but
          * nothing else will be added to this list. */
         while(!list_empty(list)) {
                 exp = list_entry(list->next, struct obd_export, exp_obd_chain);
@@ -790,6 +825,7 @@ void class_disconnect_exports(struct obd_device *obd)
         class_disconnect_export_list(&work_list, get_exp_flags_from_obd(obd));
         EXIT;
 }
+EXPORT_SYMBOL(class_disconnect_exports);
 
 /* Remove exports that have not completed recovery.
  */
@@ -800,7 +836,7 @@ void class_disconnect_stale_exports(struct obd_device *obd)
         struct obd_export *exp;
         int cnt = 0;
         ENTRY;
-  
+
         INIT_LIST_HEAD(&work_list);
         spin_lock(&obd->obd_dev_lock);
         list_for_each_safe(pos, n, &obd->obd_exports) {
@@ -813,11 +849,12 @@ void class_disconnect_stale_exports(struct obd_device *obd)
         }
         spin_unlock(&obd->obd_dev_lock);
 
-        CDEBUG(D_ERROR, "%s: disconnecting %d stale clients\n", 
+        CDEBUG(D_ERROR, "%s: disconnecting %d stale clients\n",
                obd->obd_name, cnt);
         class_disconnect_export_list(&work_list, get_exp_flags_from_obd(obd));
         EXIT;
 }
+EXPORT_SYMBOL(class_disconnect_stale_exports);
 
 int oig_init(struct obd_io_group **oig_out)
 {
@@ -838,16 +875,19 @@ int oig_init(struct obd_io_group **oig_out)
         *oig_out = oig;
         RETURN(0);
 };
+EXPORT_SYMBOL(oig_init);
 
 static inline void oig_grab(struct obd_io_group *oig)
 {
         atomic_inc(&oig->oig_refcount);
 }
+
 void oig_release(struct obd_io_group *oig)
 {
         if (atomic_dec_and_test(&oig->oig_refcount))
                 OBD_FREE(oig, sizeof(*oig));
 }
+EXPORT_SYMBOL(oig_release);
 
 void oig_add_one(struct obd_io_group *oig,
                   struct oig_callback_context *occ)
@@ -861,6 +901,7 @@ void oig_add_one(struct obd_io_group *oig,
         spin_unlock_irqrestore(&oig->oig_lock, flags);
         oig_grab(oig);
 }
+EXPORT_SYMBOL(oig_add_one);
 
 void oig_complete_one(struct obd_io_group *oig,
                       struct oig_callback_context *occ, int rc)
@@ -890,6 +931,7 @@ void oig_complete_one(struct obd_io_group *oig,
                 wake_up(wake);
         oig_release(oig);
 }
+EXPORT_SYMBOL(oig_complete_one);
 
 static int oig_done(struct obd_io_group *oig)
 {
@@ -950,3 +992,265 @@ int oig_wait(struct obd_io_group *oig)
         CDEBUG(D_CACHE, "done waiting on oig %p rc %d\n", oig, oig->oig_rc);
         return oig->oig_rc;
 }
+EXPORT_SYMBOL(oig_wait);
+
+void class_fail_export(struct obd_export *exp)
+{
+        int rc, already_failed;
+        unsigned long flags;
+
+        spin_lock_irqsave(&exp->exp_lock, flags);
+        already_failed = exp->exp_failed;
+        exp->exp_failed = 1;
+        spin_unlock_irqrestore(&exp->exp_lock, flags);
+
+        if (already_failed) {
+                CDEBUG(D_HA, "disconnecting dead export %p/%s; skipping\n",
+                       exp, exp->exp_client_uuid.uuid);
+                return;
+        }
+
+        CDEBUG(D_HA, "disconnecting export %p/%s\n",
+               exp, exp->exp_client_uuid.uuid);
+
+        if (obd_dump_on_timeout)
+                portals_debug_dumplog();
+
+        /* Most callers into obd_disconnect are removing their own reference
+         * (request, for example) in addition to the one from the hash table.
+         * We don't have such a reference here, so make one. */
+        class_export_get(exp);
+        rc = obd_disconnect(exp);
+        if (rc)
+                CERROR("disconnecting export %p failed: %d\n", exp, rc);
+        else
+                CDEBUG(D_HA, "disconnected export %p/%s\n",
+                       exp, exp->exp_client_uuid.uuid);
+}
+EXPORT_SYMBOL(class_fail_export);
+
+/* Ping evictor thread */
+#define D_PET D_HA
+
+#ifdef __KERNEL__
+#define PET_READY     1
+#define PET_TERMINATE 2
+
+static int               pet_refcount = 0;
+static int               pet_state;
+static wait_queue_head_t pet_waitq;
+static struct obd_export *pet_exp = NULL;
+static spinlock_t        pet_lock = SPIN_LOCK_UNLOCKED;
+
+static int ping_evictor_wake(struct obd_export *exp)
+{
+        spin_lock(&pet_lock);
+        if (pet_exp) {
+                /* eventually the new obd will call here again. */
+                spin_unlock(&pet_lock);
+                return 1;
+        }
+        pet_exp = exp;
+        spin_unlock(&pet_lock);
+
+        /* We have to make sure the obd isn't destroyed between now and when
+           the ping evictor runs. We'll take a reference here, and drop it
+           when we finish in the evictor.  We don't really care about this
+           export in particular; we just need one to keep the obd. */
+        class_export_get(pet_exp);
+        wake_up(&pet_waitq);
+        return 0;
+}
+
+static int ping_evictor_main(void *arg)
+{
+        struct list_head *pos, *n;
+        struct obd_device *obd;
+        struct obd_export *exp;
+        struct l_wait_info lwi = { 0 };
+        time_t expire_time;
+        unsigned long flags;
+        ENTRY;
+
+        lock_kernel();
+        kportal_daemonize("ping_evictor");
+        SIGNAL_MASK_LOCK(current, flags);
+        sigfillset(&current->blocked);
+        RECALC_SIGPENDING;
+        SIGNAL_MASK_UNLOCK(current, flags);
+        unlock_kernel();
+
+        CDEBUG(D_PET, "Starting Ping Evictor\n");
+        pet_exp = NULL;
+        pet_state = PET_READY;
+        while (1) {
+                l_wait_event(pet_waitq, pet_exp ||
+                             (pet_state == PET_TERMINATE), &lwi);
+                if (pet_state == PET_TERMINATE)
+                        break;
+
+                obd = pet_exp->exp_obd;
+                expire_time = CURRENT_SECONDS - (3 * obd_timeout / 2);
+
+                CDEBUG(D_PET, "evicting all exports of obd %s older than %ld\n",
+                       obd->obd_name, expire_time);
+
+                /* Exports can't be deleted out of the list, which means we
+                   can't lose the last ref on the export, while we hold the obd
+                   lock (class_unlink_export).  If they've already been
+                   removed from the list, we won't find them here. */
+                spin_lock(&obd->obd_dev_lock);
+                list_for_each_safe(pos, n, &obd->obd_exports_timed) {
+                        int stop = 0;
+                        exp = list_entry(pos, struct obd_export,
+                                         exp_obd_chain_timed);
+                        class_export_get(exp);
+                        spin_unlock(&obd->obd_dev_lock);
+
+                        if (expire_time > exp->exp_last_request_time) {
+                                LCONSOLE_WARN("%s hasn't heard from %s in %ld "
+                                              "seconds.  I think it's dead, "
+                                              "and I am evicting it.\n",
+                                              obd->obd_name,
+                                              obd_export_nid2str(exp),
+                                              (long)(CURRENT_SECONDS -
+                                                   exp->exp_last_request_time));
+
+                                class_fail_export(exp);
+                        } else {
+                                /* List is sorted, so everyone below is ok */
+                                stop++;
+                        }
+                        class_export_put(exp);
+                        /* lock again for the next entry */
+                        spin_lock(&obd->obd_dev_lock);
+
+                        if (stop)
+                                break;
+                }
+                spin_unlock(&obd->obd_dev_lock);
+                class_export_put(pet_exp);
+                pet_exp = NULL;
+        }
+        CDEBUG(D_PET, "Exiting Ping Evictor\n");
+
+        RETURN(0);
+}
+
+void ping_evictor_start(void)
+{
+        int rc;
+
+        if (++pet_refcount > 1)
+                return;
+
+        init_waitqueue_head(&pet_waitq);
+
+        rc = kernel_thread(ping_evictor_main, NULL, CLONE_VM | CLONE_FS);
+        if (rc < 0) {
+                pet_refcount--;
+                CERROR("Cannot start ping evictor thread: %d\n", rc);
+        }
+}
+EXPORT_SYMBOL(ping_evictor_start);
+
+void ping_evictor_stop(void)
+{
+        if (--pet_refcount > 0)
+                return;
+
+        pet_state = PET_TERMINATE;
+        wake_up(&pet_waitq);
+}
+EXPORT_SYMBOL(ping_evictor_stop);
+#else /* !__KERNEL__ */
+#define ping_evictor_wake(exp)     1
+#endif
+
+/* This function makes sure dead exports are evicted in a timely manner.
+   This function is only called when some export receives a message (i.e.,
+   the network is up.) */
+void class_update_export_timer(struct obd_export *exp, time_t extra_delay)
+{
+        struct obd_export *oldest_exp;
+        time_t oldest_time;
+
+        ENTRY;
+
+        LASSERT(exp);
+
+        /* Compensate for slow machines, etc, by faking our request time
+           into the future.  Although this can break the strict time-ordering
+           of the list, we can be really lazy here - we don't have to evict
+           at the exact right moment.  Eventually, all silent exports
+           will make it to the top of the list. */
+        exp->exp_last_request_time = max(exp->exp_last_request_time,
+                                         (time_t)CURRENT_SECONDS + extra_delay);
+
+        CDEBUG(D_PET, "updating export %s at %ld\n",
+               exp->exp_client_uuid.uuid,
+               exp->exp_last_request_time);
+
+        /* exports may get disconnected from the chain even though the
+           export has references, so we must keep the spin lock while
+           manipulating the lists */
+        spin_lock(&exp->exp_obd->obd_dev_lock);
+
+        if (list_empty(&exp->exp_obd_chain_timed)) {
+                /* this one is not timed */
+                spin_unlock(&exp->exp_obd->obd_dev_lock);
+                EXIT;
+                return;
+        }
+
+        list_move_tail(&exp->exp_obd_chain_timed,
+                       &exp->exp_obd->obd_exports_timed);
+
+        oldest_exp = list_entry(exp->exp_obd->obd_exports_timed.next,
+                                struct obd_export, exp_obd_chain_timed);
+        oldest_time = oldest_exp->exp_last_request_time;
+        spin_unlock(&exp->exp_obd->obd_dev_lock);
+
+        if (exp->exp_obd->obd_recovering) {
+                /* be nice to everyone during recovery */
+                EXIT;
+                return;
+        }
+
+        /* Note - racing to start/reset the obd_eviction timer is safe */
+        if (exp->exp_obd->obd_eviction_timer == 0) {
+                /* Check if the oldest entry is expired. */
+                if (CURRENT_SECONDS > (oldest_time +
+                                       (3 * obd_timeout / 2) + extra_delay)) {
+                        /* We need a second timer, in case the net was
+                           down and it just came back. Since the pinger
+                           may skip every other PING_INTERVAL (see note in
+                           ptlrpc_pinger_main), we better wait for 3. */
+                        exp->exp_obd->obd_eviction_timer = CURRENT_SECONDS +
+                                3 * PING_INTERVAL;
+                        CDEBUG(D_PET,
+                               "Thinking about evicting old export from %ld\n",
+                               oldest_time);
+                }
+        } else {
+                if (CURRENT_SECONDS > (exp->exp_obd->obd_eviction_timer +
+                                       extra_delay)) {
+                        /* The evictor won't evict anyone who we've heard from
+                           recently, so we don't have to check before we start
+                           it. */
+                        if (!ping_evictor_wake(exp))
+                                exp->exp_obd->obd_eviction_timer = 0;
+                }
+        }
+
+        EXIT;
+}
+EXPORT_SYMBOL(class_update_export_timer);
+
+char *obd_export_nid2str(struct obd_export *exp)
+{
+        if (exp->exp_connection != NULL)
+                return libcfs_nid2str(exp->exp_connection->c_peer.nid);
+        
+        return "(no nid)";
+}
index f39d614..09a295b 100644 (file)
@@ -93,7 +93,7 @@ int llog_cancel_rec(struct llog_handle *loghandle, int index)
         }
 
         if (!ext2_clear_bit(index, llh->llh_bitmap)) {
-                CERROR("catalog index %u already clear?\n", index);
+                CDEBUG(D_HA, "catalog index %u already clear?\n", index);
                 RETURN(-EINVAL);
         }
 
index be9c4fd..c4ec77d 100644 (file)
@@ -112,6 +112,7 @@ static int llog_check_cb(struct llog_handle *handle, struct llog_rec_hdr *rec,
                 case MDS_UNLINK_REC:
                 case MDS_SETATTR_REC:
                 case OBD_CFG_REC:
+                case PTL_CFG_REC:               /* obsolete */
                 case LLOG_HDR_MAGIC: {
                          l = snprintf(out, remains, "[index]: %05d  [type]: "
                                       "%02x  [len]: %04d ok\n",
index 6c45e8d..d9db59b 100644 (file)
@@ -390,7 +390,8 @@ static int llog_lvfs_next_block(struct llog_handle *loghandle, int *cur_idx,
                 }
 
                 rec = buf;
-                tail = (struct llog_rec_tail *)((char *)buf + rc - sizeof(struct llog_rec_tail));
+                tail = (struct llog_rec_tail *)((char *)buf + rc -
+                                                sizeof(struct llog_rec_tail));
 
                 if (LLOG_REC_HDR_NEEDS_SWABBING(rec)) {
                         lustre_swab_llog_rec(rec, tail);
@@ -485,7 +486,7 @@ static int llog_lvfs_create(struct llog_ctxt *ctxt, struct llog_handle **res,
                         GOTO(cleanup, rc);
                 }
 
-                handle->lgh_file = l_dentry_open(&obd->obd_ctxt, dchild,
+                handle->lgh_file = l_dentry_open(&obd->obd_lvfs_ctxt, dchild,
                                                     O_RDWR | O_LARGEFILE);
                 if (IS_ERR(handle->lgh_file)) {
                         rc = PTR_ERR(handle->lgh_file);
@@ -524,7 +525,7 @@ static int llog_lvfs_create(struct llog_ctxt *ctxt, struct llog_handle **res,
                 if (IS_ERR(dchild))
                         GOTO(cleanup, rc = PTR_ERR(dchild));
                 cleanup_phase = 2;
-                handle->lgh_file = l_dentry_open(&obd->obd_ctxt, dchild,
+                handle->lgh_file = l_dentry_open(&obd->obd_lvfs_ctxt, dchild,
                                                  open_flags);
                 if (IS_ERR(handle->lgh_file))
                         GOTO(cleanup, rc = PTR_ERR(handle->lgh_file));
@@ -571,9 +572,9 @@ static int llog_lvfs_destroy(struct llog_handle *handle)
         if (!strcmp(fdentry->d_parent->d_name.name, "LOGS")) {
                 struct obd_device *obd = handle->lgh_ctxt->loc_exp->exp_obd;
                 struct inode *inode = fdentry->d_parent->d_inode;
-                struct obd_run_ctxt saved;
+                struct lvfs_run_ctxt saved;
 
-                push_ctxt(&saved, &obd->obd_ctxt, NULL);
+                push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
                 dget(fdentry);
                 rc = llog_lvfs_close(handle);
 
@@ -584,7 +585,7 @@ static int llog_lvfs_destroy(struct llog_handle *handle)
                 }
 
                 dput(fdentry);
-                pop_ctxt(&saved, &obd->obd_ctxt, NULL);
+                pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
                 RETURN(rc);
         }
 
@@ -611,7 +612,7 @@ static int llog_lvfs_destroy(struct llog_handle *handle)
 int llog_get_cat_list(struct obd_device *obd, struct obd_device *disk_obd,
                       char *name, int count, struct llog_catid *idarray)
 {
-        struct obd_run_ctxt saved;
+        struct lvfs_run_ctxt saved;
         struct l_file *file;
         int rc;
         int size = sizeof(*idarray) * count;
@@ -619,7 +620,7 @@ int llog_get_cat_list(struct obd_device *obd, struct obd_device *disk_obd,
 
         LASSERT(count);
 
-        push_ctxt(&saved, &obd->obd_ctxt, NULL);
+        push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
         file = filp_open(name, O_RDWR | O_CREAT | O_LARGEFILE, 0700);
         if (!file || IS_ERR(file)) {
                 rc = PTR_ERR(file);
@@ -642,7 +643,7 @@ int llog_get_cat_list(struct obd_device *obd, struct obd_device *disk_obd,
         }
 
  out:
-        pop_ctxt(&saved, &obd->obd_ctxt, NULL);
+        pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
         if (file && !IS_ERR(file))
                 rc = filp_close(file, 0);
         RETURN(rc);
@@ -653,7 +654,7 @@ EXPORT_SYMBOL(llog_get_cat_list);
 int llog_put_cat_list(struct obd_device *obd, struct obd_device *disk_obd,
                       char *name, int count, struct llog_catid *idarray)
 {
-        struct obd_run_ctxt saved;
+        struct lvfs_run_ctxt saved;
         struct l_file *file;
         int rc;
         int size = sizeof(*idarray) * count;
@@ -661,7 +662,7 @@ int llog_put_cat_list(struct obd_device *obd, struct obd_device *disk_obd,
 
         LASSERT(count);
 
-        push_ctxt(&saved, &obd->obd_ctxt, NULL);
+        push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
         file = filp_open(name, O_RDWR | O_CREAT | O_LARGEFILE, 0700);
         if (!file || IS_ERR(file)) {
                 rc = PTR_ERR(file);
@@ -684,7 +685,7 @@ int llog_put_cat_list(struct obd_device *obd, struct obd_device *disk_obd,
         }
 
  out:
-        pop_ctxt(&saved, &obd->obd_ctxt, NULL);
+        pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
         if (file && !IS_ERR(file))
                 rc = filp_close(file, 0);
         RETURN(rc);
index 0796c50..ca5144c 100644 (file)
@@ -68,7 +68,6 @@ int llog_cleanup(struct llog_ctxt *ctxt)
                 rc = CTXTP(ctxt, cleanup)(ctxt);
 
         ctxt->loc_obd->obd_llog_ctxt[ctxt->loc_idx] = NULL;
-        ctxt->loc_exp = NULL;
         OBD_FREE(ctxt, sizeof(*ctxt));
 
         RETURN(rc);
@@ -182,7 +181,7 @@ int llog_obd_origin_setup(struct obd_device *obd, int index,
 {
         struct llog_ctxt *ctxt;
         struct llog_handle *handle;
-        struct obd_run_ctxt saved;
+        struct lvfs_run_ctxt saved;
         int rc;
         ENTRY;
 
@@ -206,9 +205,9 @@ int llog_obd_origin_setup(struct obd_device *obd, int index,
                 GOTO(out, rc);
 
         ctxt->loc_handle = handle;
-        push_ctxt(&saved, &disk_obd->obd_ctxt, NULL);
+        push_ctxt(&saved, &disk_obd->obd_lvfs_ctxt, NULL);
         rc = llog_init_handle(handle, LLOG_F_IS_CAT, NULL);
-        pop_ctxt(&saved, &disk_obd->obd_ctxt, NULL);
+        pop_ctxt(&saved, &disk_obd->obd_lvfs_ctxt, NULL);
         if (rc)
                 GOTO(out, rc);
 
index f92c2ef..3ae0a36 100644 (file)
 
 #define DEBUG_SUBSYSTEM S_LOG
 
+#ifndef __KERNEL__
+#include <liblustre.h>
+#endif
+
 #include <linux/lustre_log.h>
 
 static void print_llogd_body(struct llogd_body *d)
@@ -119,6 +123,7 @@ void lustre_swab_llog_rec(struct llog_rec_hdr *rec, struct llog_rec_tail *tail)
         }
 
         case OBD_CFG_REC:
+        case PTL_CFG_REC:                       /* obsolete */
                 /* these are swabbed as they are consumed */
                 break;
 
@@ -148,6 +153,7 @@ void lustre_swab_llog_rec(struct llog_rec_hdr *rec, struct llog_rec_tail *tail)
                 break;
         }
 
+        case LLOG_PAD_MAGIC:
         /* ignore old pad records of type 0 */
         case 0:
                 break;
index e694153..7007632 100644 (file)
@@ -529,14 +529,14 @@ static int llog_test_7(struct obd_device *obd)
 static int llog_run_tests(struct obd_device *obd)
 {
         struct llog_handle *llh;
-        struct obd_run_ctxt saved;
+        struct lvfs_run_ctxt saved;
         struct llog_ctxt *ctxt = llog_get_context(obd, LLOG_TEST_ORIG_CTXT);
         int rc, err, cleanup_phase = 0;
         char name[10];
         ENTRY;
 
         sprintf(name, "%x", llog_test_rand);
-        push_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_ctxt, NULL);
+        push_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_lvfs_ctxt, NULL);
 
         rc = llog_test_1(obd, name);
         if (rc)
@@ -576,7 +576,7 @@ static int llog_run_tests(struct obd_device *obd)
                 if (!rc)
                         rc = err;
         case 0:
-                pop_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_ctxt, NULL);
+                pop_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_lvfs_ctxt, NULL);
         }
 
         return rc;
@@ -662,9 +662,11 @@ static struct obd_ops llog_obd_ops = {
         .o_llog_finish = llog_test_llog_finish,
 };
 
+#ifdef LPROCFS
 static struct lprocfs_vars lprocfs_obd_vars[] = { {0} };
 static struct lprocfs_vars lprocfs_module_vars[] = { {0} };
 LPROCFS_INIT_VARS(llog_test, lprocfs_module_vars, lprocfs_obd_vars)
+#endif
 
 static int __init llog_test_init(void)
 {
index 1b663e9..8a0db22 100644 (file)
@@ -43,7 +43,7 @@
 #include <linux/lprocfs_status.h>
 #include <linux/lustre_fsfilt.h>
 
-#if defined(LPROCFS) && defined(__KERNEL__)
+#if defined(LPROCFS)
 
 struct proc_dir_entry *lprocfs_srch(struct proc_dir_entry *head,
                                     const char *name)
@@ -204,11 +204,11 @@ int lprocfs_rd_atomic(char *page, char **start, off_t off,
 int lprocfs_rd_uuid(char *page, char **start, off_t off, int count,
                     int *eof, void *data)
 {
-        struct obd_device *dev = (struct obd_device*)data;
+        struct obd_device *obd = (struct obd_device*)data;
 
-        LASSERT(dev != NULL);
+        LASSERT(obd != NULL);
         *eof = 1;
-        return snprintf(page, count, "%s\n", dev->obd_uuid.uuid);
+        return snprintf(page, count, "%s\n", obd->obd_uuid.uuid);
 }
 
 int lprocfs_rd_name(char *page, char **start, off_t off, int count,
@@ -335,8 +335,9 @@ int lprocfs_rd_server_uuid(char *page, char **start, off_t off, int count,
         imp = obd->u.cli.cl_import;
         imp_state_name = ptlrpc_import_state_name(imp->imp_state);
         *eof = 1;
-        return snprintf(page, count, "%s\t%s\n",
-                        imp->imp_target_uuid.uuid, imp_state_name);
+        return snprintf(page, count, "%s\t%s%s\n",
+                        imp->imp_target_uuid.uuid, imp_state_name,
+                        imp->imp_deactive ? "\tDEACTIVATED" : "");
 }
 
 int lprocfs_rd_conn_uuid(char *page, char **start, off_t off, int count,
@@ -873,8 +874,6 @@ int lprocfs_obd_rd_recovery_status(char *page, char **start, off_t off,
 }
 EXPORT_SYMBOL(lprocfs_obd_rd_recovery_status);
 
-#endif /* LPROCFS*/
-
 EXPORT_SYMBOL(lprocfs_register);
 EXPORT_SYMBOL(lprocfs_srch);
 EXPORT_SYMBOL(lprocfs_remove);
@@ -906,3 +905,4 @@ EXPORT_SYMBOL(lprocfs_rd_filesfree);
 
 EXPORT_SYMBOL(lprocfs_write_helper);
 EXPORT_SYMBOL(lprocfs_write_u64_helper);
+#endif /* LPROCFS*/
index e80bb58..f4835c6 100644 (file)
@@ -81,7 +81,7 @@ int class_attach(struct lustre_cfg *lcfg)
 
         len = strlen(name) + 1;
         OBD_ALLOC(namecopy, len);
-        if (!namecopy) 
+        if (!namecopy)
                 GOTO(out, rc = -ENOMEM);
         memcpy(namecopy, name, len);
         cleanup_phase = 2; /* free obd_name */
@@ -95,6 +95,7 @@ int class_attach(struct lustre_cfg *lcfg)
         cleanup_phase = 3;  /* class_release_dev */
 
         INIT_LIST_HEAD(&obd->obd_exports);
+        INIT_LIST_HEAD(&obd->obd_exports_timed);
         obd->obd_num_exports = 0;
         spin_lock_init(&obd->obd_dev_lock);
         spin_lock_init(&obd->obd_osfs_lock);
@@ -126,7 +127,7 @@ int class_attach(struct lustre_cfg *lcfg)
                         GOTO(out, rc = -EINVAL);
         }
 
-        /* The attach is our first obd reference */
+        /* Detach drops this */
         atomic_set(&obd->obd_refcount, 1);
 
         obd->obd_attached = 1;
@@ -159,7 +160,7 @@ int class_setup(struct obd_device *obd, struct lustre_cfg *lcfg)
                 CERROR("Device %d not attached\n", obd->obd_minor);
                 RETURN(-ENODEV);
         }
-        
+
         if (obd->obd_set_up) {
                 CERROR("Device %d already setup (type %s)\n",
                        obd->obd_minor, obd->obd_type->typ_name);
@@ -176,7 +177,7 @@ int class_setup(struct obd_device *obd, struct lustre_cfg *lcfg)
         }
         /* just leave this on forever.  I can't use obd_set_up here because
            other fns check that status, and we're not actually set up yet. */
-        obd->obd_starting = 1;  
+        obd->obd_starting = 1;
         spin_unlock(&obd->obd_dev_lock);
 
         exp = class_new_export(obd);
@@ -185,6 +186,7 @@ int class_setup(struct obd_device *obd, struct lustre_cfg *lcfg)
         memcpy(&exp->exp_client_uuid, &obd->obd_uuid,
                sizeof(exp->exp_client_uuid));
         obd->obd_self_export = exp;
+        list_del_init(&exp->exp_obd_chain_timed);
         class_export_put(exp);
 
         err = obd_setup(obd, sizeof(*lcfg), lcfg);
@@ -193,9 +195,14 @@ int class_setup(struct obd_device *obd, struct lustre_cfg *lcfg)
 
         obd->obd_type->typ_refcnt++;
         obd->obd_set_up = 1;
+        spin_lock(&obd->obd_dev_lock);
+        /* cleanup drops this */
+        atomic_inc(&obd->obd_refcount);
+        spin_unlock(&obd->obd_dev_lock);
+
         CDEBUG(D_IOCTL, "finished setup of obd %s (uuid %s)\n",
                obd->obd_name, obd->obd_uuid.uuid);
-        
+
         RETURN(0);
 
 err_exp:
@@ -208,28 +215,33 @@ err_exp:
 static int __class_detach(struct obd_device *obd)
 {
         int err = 0;
+        ENTRY;
+
+        CDEBUG(D_CONFIG, "destroying obd %d (%s)\n",
+               obd->obd_minor, obd->obd_name);
 
-        if (OBP(obd, detach)) 
+        if (OBP(obd, detach))
                 err = OBP(obd,detach)(obd);
-        
+
         if (obd->obd_name) {
                 OBD_FREE(obd->obd_name, strlen(obd->obd_name)+1);
                 obd->obd_name = NULL;
         } else {
                 CERROR("device %d: no name at detach\n", obd->obd_minor);
         }
-        
+
         LASSERT(OBT(obd));
         /* Attach took type refcount */
         obd->obd_type->typ_refcnt--;
         class_put_type(obd->obd_type);
         class_release_dev(obd);
-        return (err);
+        RETURN(err);
 }
 
 int class_detach(struct obd_device *obd, struct lustre_cfg *lcfg)
 {
         ENTRY;
+
         if (obd->obd_set_up) {
                 CERROR("OBD device %d still set up\n", obd->obd_minor);
                 RETURN(-EBUSY);
@@ -243,7 +255,7 @@ int class_detach(struct obd_device *obd, struct lustre_cfg *lcfg)
         }
         obd->obd_attached = 0;
         spin_unlock(&obd->obd_dev_lock);
-        
+
         CDEBUG(D_IOCTL, "detach on obd %s (uuid %s)\n",
                obd->obd_name, obd->obd_uuid.uuid);
 
@@ -267,8 +279,9 @@ static void dump_exports(struct obd_device *obd)
                         nreplies++;
                 }
 
-                CDEBUG(D_IOCTL, "%s: %p %s %d %d %d: %p %s\n",
+                CDEBUG(D_IOCTL, "%s: %p %s %s %d %d %d: %p %s\n",
                        obd->obd_name, exp, exp->exp_client_uuid.uuid,
+                       obd_export_nid2str(exp),
                        atomic_read(&exp->exp_refcount),
                        exp->exp_failed, nreplies, first_reply,
                        nreplies > 3 ? "..." : "");
@@ -279,8 +292,8 @@ int class_cleanup(struct obd_device *obd, struct lustre_cfg *lcfg)
 {
         int err = 0;
         char *flag;
-
         ENTRY;
+
         OBD_RACE(OBD_FAIL_LDLM_RECOV_CLIENTS);
 
         if (!obd->obd_set_up) {
@@ -309,6 +322,7 @@ int class_cleanup(struct obd_device *obd, struct lustre_cfg *lcfg)
                                        obd->obd_name);
                                 obd->obd_fail = 1;
                                 obd->obd_no_transno = 1;
+                                obd->obd_no_recov = 1;
                                 /* Set the obd readonly if we can */
                                 if (OBP(obd, iocontrol))
                                         obd_iocontrol(OBD_IOC_SET_READONLY,
@@ -320,39 +334,37 @@ int class_cleanup(struct obd_device *obd, struct lustre_cfg *lcfg)
                                        *flag);
                         }
         }
-        
-        /* The two references that should be remaining are the
-         * obd_self_export and the attach reference. */
-        if (atomic_read(&obd->obd_refcount) > 2) {
+
+        /* The three references that should be remaining are the
+         * obd_self_export and the attach and setup references. */
+        if (atomic_read(&obd->obd_refcount) > 3) {
                 if (!(obd->obd_fail || obd->obd_force)) {
                         CERROR("OBD %s is still busy with %d references\n"
                                "You should stop active file system users,"
-                               " or use the --force option to cleanup.\n",  
+                               " or use the --force option to cleanup.\n",
                                obd->obd_name, atomic_read(&obd->obd_refcount));
                         dump_exports(obd);
                         GOTO(out, err = -EBUSY);
                 }
                 CDEBUG(D_IOCTL, "%s: forcing exports to disconnect: %d\n",
-                       obd->obd_name, atomic_read(&obd->obd_refcount));
+                       obd->obd_name, atomic_read(&obd->obd_refcount) - 1);
                 dump_exports(obd);
                 class_disconnect_exports(obd);
         }
 
         LASSERT(obd->obd_self_export);
-        if (obd->obd_self_export) {
-               /* mds_precleanup will clean up the lov (and osc's)*/
-               err = obd_precleanup(obd);
-               if (err)
-                       GOTO(out, err);
-               obd->obd_self_export->exp_flags |= 
-                       (obd->obd_fail ? OBD_OPT_FAILOVER : 0) |
-                       (obd->obd_force ? OBD_OPT_FORCE : 0);
-               class_unlink_export(obd->obd_self_export);
-               obd->obd_self_export = NULL;
-        }
 
+        /* Precleanup stage 1, we must make sure all exports (other than the
+           self-export) get destroyed. */
+        err = obd_precleanup(obd, 1);
+        if (err)
+                CERROR("Precleanup %s returned %d\n",
+                       obd->obd_name, err);
+
+        class_decref(obd);
         obd->obd_set_up = 0;
         obd->obd_type->typ_refcnt--;
+
         RETURN(0);
 out:
         /* Allow a failed cleanup to try again. */
@@ -362,16 +374,45 @@ out:
 
 void class_decref(struct obd_device *obd)
 {
-        if (atomic_dec_and_test(&obd->obd_refcount)) {
-                int err;
-                CDEBUG(D_IOCTL, "finishing cleanup of obd %s (%s)\n",
+        int err;
+        int refs;
+
+        spin_lock(&obd->obd_dev_lock);
+        atomic_dec(&obd->obd_refcount);
+        refs = atomic_read(&obd->obd_refcount);
+        spin_unlock(&obd->obd_dev_lock);
+
+        CDEBUG(D_INFO, "Decref %s now %d\n", obd->obd_name, refs);
+
+        if ((refs == 1) && obd->obd_stopping) {
+                /* All exports (other than the self-export) have been
+                   destroyed; there should be no more in-progress ops
+                   by this point.*/
+                /* if we're not stopping, we didn't finish setup */
+                /* Precleanup stage 2,  do other type-specific
+                   cleanup requiring the self-export. */
+                err = obd_precleanup(obd, 2);
+                if (err)
+                        CERROR("Precleanup %s returned %d\n",
+                               obd->obd_name, err);
+                obd->obd_self_export->exp_flags |=
+                        (obd->obd_fail ? OBD_OPT_FAILOVER : 0) |
+                        (obd->obd_force ? OBD_OPT_FORCE : 0);
+                /* note that we'll recurse into class_decref again */
+                class_unlink_export(obd->obd_self_export);
+                return;
+        }
+
+        if (refs == 0) {
+                CDEBUG(D_CONFIG, "finishing cleanup of obd %s (%s)\n",
                        obd->obd_name, obd->obd_uuid.uuid);
                 LASSERT(!obd->obd_attached);
                 if (obd->obd_stopping) {
-                        /* If we're not stopping, we never set up */
+                        /* If we're not stopping, we were never set up */
                         err = obd_cleanup(obd);
                         if (err)
-                                CERROR("Cleanup returned %d\n", err);
+                                CERROR("Cleanup %s returned %d\n",
+                                       obd->obd_name, err);
                 }
                 err = __class_detach(obd);
                 if (err)
@@ -520,9 +561,9 @@ int class_process_config(struct lustre_cfg *lcfg)
                 GOTO(out, err);
         }
         case LCFG_ADD_UUID: {
-                CDEBUG(D_IOCTL, "adding mapping: uuid %s -> nid %s("LPX64")\n", 
-                       lustre_cfg_string(lcfg, 1),
-                       libcfs_nid2str(lcfg->lcfg_nid), lcfg->lcfg_nid);
+                CDEBUG(D_IOCTL, "adding mapping from uuid %s to nid "LPX64
+                       " (%s)\n", lustre_cfg_string(lcfg, 1),
+                       lcfg->lcfg_nid, libcfs_nid2str(lcfg->lcfg_nid));
 
                 err = class_add_uuid(lustre_cfg_string(lcfg, 1), lcfg->lcfg_nid);
                 GOTO(out, err);
@@ -561,7 +602,11 @@ int class_process_config(struct lustre_cfg *lcfg)
         case LCFG_SET_TIMEOUT: {
                 CDEBUG(D_IOCTL, "changing lustre timeout from %d to %d\n",
                        obd_timeout, lcfg->lcfg_num);
-                obd_timeout = lcfg->lcfg_num;
+                obd_timeout = max(lcfg->lcfg_num, 1U);
+                if (ldlm_timeout >= obd_timeout)
+                        ldlm_timeout = max(obd_timeout / 3, 1U);
+                else if (ldlm_timeout < 10 && obd_timeout >= ldlm_timeout * 4)
+                        ldlm_timeout = min(obd_timeout / 3, 30U);
                 GOTO(out, err = 0);
         }
         case LCFG_SET_UPCALL: {
@@ -569,7 +614,7 @@ int class_process_config(struct lustre_cfg *lcfg)
                        lustre_cfg_string(lcfg, 1));
                 if (LUSTRE_CFG_BUFLEN(lcfg, 1) > sizeof obd_lustre_upcall)
                         GOTO(out, err = -EINVAL);
-                strncpy(obd_lustre_upcall, lustre_cfg_string(lcfg, 1), 
+                strncpy(obd_lustre_upcall, lustre_cfg_string(lcfg, 1),
                         sizeof (obd_lustre_upcall));
                 GOTO(out, err = 0);
         }
@@ -665,7 +710,23 @@ static int class_config_llog_handler(struct llog_handle * handle,
 
                 lcfg_new->lcfg_num   = lcfg->lcfg_num;
                 lcfg_new->lcfg_flags = lcfg->lcfg_flags;
-                lcfg_new->lcfg_nid   = lcfg->lcfg_nid;
+
+                /* XXX Hack to try to remain binary compatible with
+                 * pre-newconfig logs */
+                if (lcfg->lcfg_nal != 0 &&      /* pre-newconfig log? */
+                    (lcfg->lcfg_nid >> 32) == 0) {
+                        __u32 addr = (__u32)(lcfg->lcfg_nid & 0xffffffff);
+
+                        lcfg_new->lcfg_nid =
+                                PTL_MKNID(PTL_MKNET(lcfg->lcfg_nal, 0), addr);
+                        CWARN("Converted pre-newconfig NAL %d NID %x to %s\n",
+                              lcfg->lcfg_nal, addr,
+                              libcfs_nid2str(lcfg_new->lcfg_nid));
+                } else {
+                        lcfg_new->lcfg_nid = lcfg->lcfg_nid;
+                }
+
+                lcfg_new->lcfg_nal = 0; /* illegal value for obsolete field */
 
                 rc = class_process_config(lcfg_new);
                 lustre_cfg_free(lcfg_new);
@@ -674,6 +735,10 @@ static int class_config_llog_handler(struct llog_handle * handle,
                         OBD_FREE(inst_name, inst_len);
                 break;
         }
+        case PTL_CFG_REC: {
+                CWARN("Ignoring obsolete portals config\n");
+                break;
+        }
         default:
                 CERROR("Unknown llog record type %#x encountered\n",
                        rec->lrh_type);
@@ -734,12 +799,16 @@ int class_config_dump_handler(struct llog_handle * handle,
                 if (lcfg->lcfg_nid)
                         CDEBUG(D_INFO, "         nid: "LPX64"\n",
                                lcfg->lcfg_nid);
+                if (lcfg->lcfg_nal)
+                        CDEBUG(D_INFO, "         nal: %x (obsolete)\n", lcfg->lcfg_nal);
                 if (lcfg->lcfg_num)
-                        CDEBUG(D_INFO, "         nal: %x\n", lcfg->lcfg_num);
+                        CDEBUG(D_INFO, "         num: %x\n", lcfg->lcfg_num);
                 for (i = 1; i < lcfg->lcfg_bufcount; i++)
                         if (LUSTRE_CFG_BUFLEN(lcfg, i) > 0)
                                 CDEBUG(D_INFO, "     inlbuf%d: %s\n", i,
                                        lustre_cfg_string(lcfg, i));
+        } else if (rec->lrh_type == PTL_CFG_REC) {
+                CDEBUG(D_INFO, "Obsolete pcfg command\n");
         } else {
                 CERROR("unhandled lrh_type: %#x\n", rec->lrh_type);
                 rc = -EINVAL;
diff --git a/lustre/obdclass/simple.c b/lustre/obdclass/simple.c
deleted file mode 100644 (file)
index 581532b..0000000
+++ /dev/null
@@ -1,266 +0,0 @@
-/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
- * vim:expandtab:shiftwidth=8:tabstop=8:
- *
- * Copyright (C) 2002, 2003 Cluster File Systems, Inc.
- *  Author: Peter Braam <braam@clusterfs.com>
- *  Aurhot: Andreas Dilger <adilger@clusterfs.com>
- *
- *   This file is part of Lustre, http://www.lustre.org.
- *
- *   Lustre is free software; you can redistribute it and/or
- *   modify it under the terms of version 2 of the GNU General Public
- *   License as published by the Free Software Foundation.
- *
- *   Lustre is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with Lustre; if not, write to the Free Software
- *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#ifndef EXPORT_SYMTAB
-# define EXPORT_SYMTAB
-#endif
-
-#include <linux/version.h>
-#include <linux/fs.h>
-#include <asm/unistd.h>
-
-#define DEBUG_SUBSYSTEM S_FILTER
-
-#include <linux/obd.h>
-#include <linux/lustre_lib.h>
-#include <linux/lustre_compat25.h>
-
-/* Debugging check only needed during development */
-#ifdef OBD_CTXT_DEBUG
-# define ASSERT_CTXT_MAGIC(magic) LASSERT((magic) == OBD_RUN_CTXT_MAGIC)
-# define ASSERT_NOT_KERNEL_CTXT(msg) LASSERT(!segment_eq(get_fs(), get_ds()))
-# define ASSERT_KERNEL_CTXT(msg) LASSERT(segment_eq(get_fs(), get_ds()))
-#else
-# define ASSERT_CTXT_MAGIC(magic) do {} while(0)
-# define ASSERT_NOT_KERNEL_CTXT(msg) do {} while(0)
-# define ASSERT_KERNEL_CTXT(msg) do {} while(0)
-#endif
-
-/* push / pop to root of obd store */
-void push_ctxt(struct obd_run_ctxt *save, struct obd_run_ctxt *new_ctx,
-               struct obd_ucred *uc)
-{
-        //ASSERT_NOT_KERNEL_CTXT("already in kernel context!\n");
-        ASSERT_CTXT_MAGIC(new_ctx->magic);
-        OBD_SET_CTXT_MAGIC(save);
-
-        /*
-        CDEBUG(D_INFO,
-               "= push %p->%p = cur fs %p pwd %p:d%d:i%d (%.*s), pwdmnt %p:%d\n",
-               save, current, current->fs, current->fs->pwd,
-               atomic_read(&current->fs->pwd->d_count),
-               atomic_read(&current->fs->pwd->d_inode->i_count),
-               current->fs->pwd->d_name.len, current->fs->pwd->d_name.name,
-               current->fs->pwdmnt,
-               atomic_read(&current->fs->pwdmnt->mnt_count));
-        */
-
-        save->fs = get_fs();
-        LASSERT(atomic_read(&current->fs->pwd->d_count));
-        LASSERT(atomic_read(&new_ctx->pwd->d_count));
-        save->pwd = dget(current->fs->pwd);
-        save->pwdmnt = mntget(current->fs->pwdmnt);
-        save->ngroups = current->ngroups;
-
-        LASSERT(save->pwd);
-        LASSERT(save->pwdmnt);
-        LASSERT(new_ctx->pwd);
-        LASSERT(new_ctx->pwdmnt);
-
-        if (uc) {
-                save->ouc.ouc_fsuid = current->fsuid;
-                save->ouc.ouc_fsgid = current->fsgid;
-                save->ouc.ouc_cap = current->cap_effective;
-                save->ouc.ouc_suppgid1 = current->groups[0];
-                save->ouc.ouc_suppgid2 = current->groups[1];
-
-                current->fsuid = uc->ouc_fsuid;
-                current->fsgid = uc->ouc_fsgid;
-                current->cap_effective = uc->ouc_cap;
-                current->ngroups = 0;
-
-                if (uc->ouc_suppgid1 != -1)
-                        current->groups[current->ngroups++] = uc->ouc_suppgid1;
-                if (uc->ouc_suppgid2 != -1)
-                        current->groups[current->ngroups++] = uc->ouc_suppgid2;
-        }
-        set_fs(new_ctx->fs);
-        set_fs_pwd(current->fs, new_ctx->pwdmnt, new_ctx->pwd);
-
-        /*
-        CDEBUG(D_INFO,
-               "= push %p->%p = cur fs %p pwd %p:d%d:i%d (%.*s), pwdmnt %p:%d\n",
-               new_ctx, current, current->fs, current->fs->pwd,
-               atomic_read(&current->fs->pwd->d_count),
-               atomic_read(&current->fs->pwd->d_inode->i_count),
-               current->fs->pwd->d_name.len, current->fs->pwd->d_name.name,
-               current->fs->pwdmnt,
-               atomic_read(&current->fs->pwdmnt->mnt_count));
-        */
-}
-EXPORT_SYMBOL(push_ctxt);
-
-void pop_ctxt(struct obd_run_ctxt *saved, struct obd_run_ctxt *new_ctx,
-              struct obd_ucred *uc)
-{
-        //printk("pc0");
-        ASSERT_CTXT_MAGIC(saved->magic);
-        //printk("pc1");
-        ASSERT_KERNEL_CTXT("popping non-kernel context!\n");
-
-        /*
-        CDEBUG(D_INFO,
-               " = pop  %p==%p = cur %p pwd %p:d%d:i%d (%.*s), pwdmnt %p:%d\n",
-               new_ctx, current, current->fs, current->fs->pwd,
-               atomic_read(&current->fs->pwd->d_count),
-               atomic_read(&current->fs->pwd->d_inode->i_count),
-               current->fs->pwd->d_name.len, current->fs->pwd->d_name.name,
-               current->fs->pwdmnt,
-               atomic_read(&current->fs->pwdmnt->mnt_count));
-        */
-
-        LASSERT(current->fs->pwd == new_ctx->pwd);
-        LASSERT(current->fs->pwdmnt == new_ctx->pwdmnt);
-
-        set_fs(saved->fs);
-        set_fs_pwd(current->fs, saved->pwdmnt, saved->pwd);
-
-        dput(saved->pwd);
-        mntput(saved->pwdmnt);
-        if (uc) {
-                current->fsuid = saved->ouc.ouc_fsuid;
-                current->fsgid = saved->ouc.ouc_fsgid;
-                current->cap_effective = saved->ouc.ouc_cap;
-                current->ngroups = saved->ngroups;
-                current->groups[0] = saved->ouc.ouc_suppgid1;
-                current->groups[1] = saved->ouc.ouc_suppgid2;
-        }
-
-        /*
-        CDEBUG(D_INFO,
-               "= pop  %p->%p = cur fs %p pwd %p:d%d:i%d (%.*s), pwdmnt %p:%d\n",
-               saved, current, current->fs, current->fs->pwd,
-               atomic_read(&current->fs->pwd->d_count),
-               atomic_read(&current->fs->pwd->d_inode->i_count),
-               current->fs->pwd->d_name.len, current->fs->pwd->d_name.name,
-               current->fs->pwdmnt,
-               atomic_read(&current->fs->pwdmnt->mnt_count));
-        */
-}
-EXPORT_SYMBOL(pop_ctxt);
-
-/* utility to make a file */
-struct dentry *simple_mknod(struct dentry *dir, char *name, int mode)
-{
-        struct dentry *dchild;
-        int err = 0;
-        ENTRY;
-
-        ASSERT_KERNEL_CTXT("kernel doing mknod outside kernel context\n");
-        CDEBUG(D_INODE, "creating file %.*s\n", (int)strlen(name), name);
-
-        dchild = ll_lookup_one_len(name, dir, strlen(name));
-        if (IS_ERR(dchild))
-                GOTO(out_up, dchild);
-
-        if (dchild->d_inode) {
-                if (!S_ISREG(dchild->d_inode->i_mode))
-                        GOTO(out_err, err = -EEXIST);
-
-                GOTO(out_up, dchild);
-        }
-
-        err = ll_vfs_create(dir->d_inode, dchild, (mode & ~S_IFMT) | S_IFREG, NULL);
-        if (err)
-                GOTO(out_err, err);
-
-        RETURN(dchild);
-
-out_err:
-        dput(dchild);
-        dchild = ERR_PTR(err);
-out_up:
-        return dchild;
-}
-EXPORT_SYMBOL(simple_mknod);
-
-/* utility to make a directory */
-struct dentry *simple_mkdir(struct dentry *dir, char *name, int mode)
-{
-        struct dentry *dchild;
-        int err = 0;
-        ENTRY;
-
-        ASSERT_KERNEL_CTXT("kernel doing mkdir outside kernel context\n");
-        CDEBUG(D_INODE, "creating directory %.*s\n", (int)strlen(name), name);
-        dchild = ll_lookup_one_len(name, dir, strlen(name));
-        if (IS_ERR(dchild))
-                GOTO(out_up, dchild);
-
-        if (dchild->d_inode) {
-                if (!S_ISDIR(dchild->d_inode->i_mode))
-                        GOTO(out_err, err = -ENOTDIR);
-
-                GOTO(out_up, dchild);
-        }
-
-        err = vfs_mkdir(dir->d_inode, dchild, mode);
-        if (err)
-                GOTO(out_err, err);
-
-        RETURN(dchild);
-
-out_err:
-        dput(dchild);
-        dchild = ERR_PTR(err);
-out_up:
-        return dchild;
-}
-EXPORT_SYMBOL(simple_mkdir);
-
-/*
- * Read a file from within kernel context.  Prior to calling this
- * function we should already have done a push_ctxt().
- */
-int lustre_fread(struct file *file, void *buf, int len, loff_t *off)
-{
-        ASSERT_KERNEL_CTXT("kernel doing read outside kernel context\n");
-        if (!file || !file->f_op || !file->f_op->read || !off)
-                RETURN(-ENOSYS);
-
-        return file->f_op->read(file, buf, len, off);
-}
-EXPORT_SYMBOL(lustre_fread);
-
-/*
- * Write a file from within kernel context.  Prior to calling this
- * function we should already have done a push_ctxt().
- */
-int lustre_fwrite(struct file *file, const void *buf, int len, loff_t *off)
-{
-        ENTRY;
-        ASSERT_KERNEL_CTXT("kernel doing write outside kernel context\n");
-        if (!file)
-                RETURN(-ENOENT);
-        if (!file->f_op)
-                RETURN(-ENOSYS);
-        if (!off)
-                RETURN(-EINVAL);
-
-        if (!file->f_op->write)
-                RETURN(-EROFS);
-
-        RETURN(file->f_op->write(file, buf, len, off));
-}
-EXPORT_SYMBOL(lustre_fwrite);
-
index e4704f2..1bf8a27 100644 (file)
@@ -58,31 +58,47 @@ enum {
 };
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,8)
-int proc_fail_loc(ctl_table *table, int write, struct file *filp,
-                  void *buffer, size_t *lenp)
+#define ll_proc_dointvec(table,write,filp,buffer,lenp,ppos)             \
+        proc_dointvec(table,write,filp,buffer,lenp)
+#define LL_PROC_PROTO(name)                                             \
+        name(ctl_table *table, int write, struct file *filp,   \
+                      void *buffer, size_t *lenp)
 #else
-int proc_fail_loc(ctl_table *table, int write, struct file *filp,
+#define ll_proc_dointvec(table,write,filp,buffer,lenp,ppos)             \
+        proc_dointvec(table,write,filp,buffer,lenp,ppos);
+#define LL_PROC_PROTO(name)                                             \
+        name(ctl_table *table, int write, struct file *filp,            \
                   void *buffer, size_t *lenp, loff_t *ppos)
 #endif
+
+int LL_PROC_PROTO(proc_fail_loc)
 {
         int rc;
         int old_fail_loc = obd_fail_loc;
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,8)
-        rc = proc_dointvec(table,write,filp,buffer,lenp);
-#else
-        rc = proc_dointvec(table,write,filp,buffer,lenp,ppos);
-#endif
+        rc = ll_proc_dointvec(table, write, filp, buffer, lenp, ppos);
         if (old_fail_loc != obd_fail_loc)
                 wake_up(&obd_race_waitq);
         return rc;
 }
 
+int LL_PROC_PROTO(proc_set_timeout)
+{
+        int rc;
+
+        rc = ll_proc_dointvec(table, write, filp, buffer, lenp, ppos);
+        if (ldlm_timeout >= obd_timeout)
+                ldlm_timeout = max(obd_timeout / 3, 1U);
+        else if (ldlm_timeout < 10 && obd_timeout >= ldlm_timeout * 4)
+                ldlm_timeout = min(obd_timeout / 3, 30U);
+        return rc;
+}
+
 static ctl_table obd_table[] = {
         {OBD_FAIL_LOC, "fail_loc", &obd_fail_loc, sizeof(int), 0644, NULL,
-                &proc_dointvec},
-        {OBD_TIMEOUT, "timeout", &obd_timeout, sizeof(int), 0644, NULL,
                 &proc_fail_loc},
+        {OBD_TIMEOUT, "timeout", &obd_timeout, sizeof(int), 0644, NULL,
+                &proc_set_timeout},
         {OBD_DUMP_ON_TIMEOUT, "dump_on_timeout", &obd_dump_on_timeout,
                 sizeof(int), 0644, NULL, &proc_dointvec},
         /* XXX need to lock so we avoid update races with recovery upcall! */
@@ -93,7 +109,7 @@ static ctl_table obd_table[] = {
         {OBD_SYNCFILTER, "filter_sync_on_commit", &obd_sync_filter, sizeof(int),
                 0644, NULL, &proc_dointvec},
         {OBD_LDLM_TIMEOUT, "ldlm_timeout", &ldlm_timeout, sizeof(int), 0644,
-                NULL, &proc_dointvec},
+                NULL, &proc_set_timeout},
         { 0 }
 };
 
index 2226f90..dd66ae5 100644 (file)
@@ -141,26 +141,3 @@ void class_uuid_unparse(class_uuid_t uu, struct obd_uuid *out)
                uuid.node[0], uuid.node[1], uuid.node[2],
                uuid.node[3], uuid.node[4], uuid.node[5]);
 }
-
-struct obd_device *client_tgtuuid2obd(struct obd_uuid *tgtuuid)
-{
-        int i;
-
-        for (i = 0; i < MAX_OBD_DEVICES; i++) {
-                struct obd_device *obd = &obd_dev[i];
-                if (obd->obd_type == NULL)
-                        continue;
-                if ((strncmp(obd->obd_type->typ_name, LUSTRE_OSC_NAME,
-                             sizeof LUSTRE_OSC_NAME) == 0) ||
-                    (strncmp(obd->obd_type->typ_name, LUSTRE_MDC_NAME,
-                             sizeof LUSTRE_MDC_NAME) == 0)) {
-                        struct client_obd *cli = &obd->u.cli;
-                        struct obd_import *imp = cli->cl_import;
-                        if (strncmp(tgtuuid->uuid, imp->imp_target_uuid.uuid,
-                                    sizeof(imp->imp_target_uuid)) == 0)
-                                return obd;
-                }
-        }
-
-        return NULL;
-}
index 24d8044..834b082 100644 (file)
@@ -14,5 +14,5 @@ if MODULES
 modulefs_DATA = obdecho$(KMODEXT)
 endif # MODULES
 
-MOSTLYCLEANFILES = *.o *.ko *.mod.c
+MOSTLYCLEANFILES := @MOSTLYCLEANFILES@ 
 DIST_SOURCES = $(obdecho-objs:%.o=%.c)
index 9c85e8d..ccc85a0 100644 (file)
@@ -94,7 +94,7 @@ static int echo_disconnect(struct obd_export *exp)
 static int echo_destroy_export(struct obd_export *exp)
 {
         ENTRY;
-        
+
         target_destroy_export(exp);
 
         RETURN(0);
@@ -117,7 +117,7 @@ int echo_create(struct obd_export *exp, struct obdo *oa,
         struct obd_device *obd = class_exp2obd(exp);
 
         if (!obd) {
-                CERROR("invalid client cookie "LPX64"\n", 
+                CERROR("invalid client cookie "LPX64"\n",
                        exp->exp_handle.h_cookie);
                 return -EINVAL;
         }
@@ -169,7 +169,7 @@ static int echo_getattr(struct obd_export *exp, struct obdo *oa,
         obd_id id = oa->o_id;
 
         if (!obd) {
-                CERROR("invalid client cookie "LPX64"\n", 
+                CERROR("invalid client cookie "LPX64"\n",
                        exp->exp_handle.h_cookie);
                 RETURN(-EINVAL);
         }
@@ -191,7 +191,7 @@ static int echo_setattr(struct obd_export *exp, struct obdo *oa,
         struct obd_device *obd = class_exp2obd(exp);
 
         if (!obd) {
-                CERROR("invalid client cookie "LPX64"\n", 
+                CERROR("invalid client cookie "LPX64"\n",
                        exp->exp_handle.h_cookie);
                 RETURN(-EINVAL);
         }
@@ -231,7 +231,7 @@ echo_page_debug_setup(struct page *page, int rw, obd_id id,
                         block_debug_setup(addr, OBD_ECHO_BLOCK_SIZE,
                                           0xecc0ecc0ecc0ecc0ULL,
                                           0xecc0ecc0ecc0ecc0ULL);
-                
+
                 addr   += OBD_ECHO_BLOCK_SIZE;
                 offset += OBD_ECHO_BLOCK_SIZE;
                 len    -= OBD_ECHO_BLOCK_SIZE;
@@ -258,7 +258,7 @@ echo_page_debug_check(struct page *page, obd_id id,
 
                 if (rc2 != 0 && rc == 0)
                         rc = rc2;
-                
+
                 addr   += OBD_ECHO_BLOCK_SIZE;
                 offset += OBD_ECHO_BLOCK_SIZE;
                 len    -= OBD_ECHO_BLOCK_SIZE;
@@ -429,7 +429,7 @@ int echo_commitrw(int cmd, struct obd_export *export, struct obdo *oa,
                                r->page, addr, r->offset);
 
                         if (verify) {
-                                vrc = echo_page_debug_check(page, obj->ioo_id, 
+                                vrc = echo_page_debug_check(page, obj->ioo_id,
                                                             r->offset, r->len);
                                 /* check all the pages always */
                                 if (vrc != 0 && rc == 0)
@@ -513,7 +513,7 @@ static int echo_cleanup(struct obd_device *obd)
          * happened before calling ldlm_namespace_free() */
         set_current_state (TASK_UNINTERRUPTIBLE);
         schedule_timeout (HZ);
-        
+
         ldlm_namespace_free(obd->obd_namespace, obd->obd_force);
 
         leaked = atomic_read(&obd->u.echo.eo_prep);
index c25d156..cc3207d 100644 (file)
 #include <linux/lprocfs_status.h>
 #include <linux/obd_class.h>
 
-#ifndef LPROCFS
-static struct lprocfs_vars lprocfs_obd_vars[]  = { {0} };
-static struct lprocfs_vars lprocfs_module_vars[] = { {0} };
-#else
-
+#ifdef LPROCFS
 static struct lprocfs_vars lprocfs_obd_vars[] = {
         { "uuid",         lprocfs_rd_uuid,        0, 0 },
         { 0 }
@@ -39,5 +35,5 @@ static struct lprocfs_vars lprocfs_module_vars[] = {
         { 0 }
 };
 
-#endif /* LPROCFS */
 LPROCFS_INIT_VARS(echo, lprocfs_module_vars, lprocfs_obd_vars)
+#endif /* LPROCFS */
index eef89e8..0f25c77 100644 (file)
@@ -1,5 +1,5 @@
 MODULES := obdfilter
-MDS :=  @top_srcdir@/lustre/mds/
+
 obdfilter-objs := filter.o filter_io.o filter_log.o filter_san.o
 obdfilter-objs += lproc_obdfilter.o filter_lvb.o
 
@@ -7,7 +7,6 @@ ifeq ($(PATCHLEVEL),4)
 obdfilter-objs += filter_io_24.o
 else
 obdfilter-objs += filter_io_26.o
-obdfilter-objs += $(MDS)quota_context.o
 endif # PATCHLEVEL 
 
 @INCLUDE_RULES@
index 09d52b2..5f90afb 100644 (file)
@@ -7,5 +7,5 @@ if MODULES
 modulefs_DATA = obdfilter$(KMODEXT)
 endif
 
-MOSTLYCLEANFILES = *.o *.ko *.mod.c
+MOSTLYCLEANFILES := @MOSTLYCLEANFILES@ 
 DIST_SOURCES = $(obdfilter-objs:%.o=%.c) filter_io_24.c filter_io_26.c filter_internal.h
index 4dcf70e..8117b51 100644 (file)
@@ -42,7 +42,6 @@
 #include <linux/init.h>
 #include <linux/version.h>
 #include <linux/sched.h>
-#include <linux/quotaops.h>
 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
 # include <linux/mount.h>
 # include <linux/buffer_head.h>
@@ -62,9 +61,6 @@
 
 static struct lvfs_callback_ops filter_lvfs_ops;
 
-static int filter_destroy(struct obd_export *exp, struct obdo *oa,
-                          struct lov_stripe_md *ea, struct obd_trans_info *);
-
 static void filter_commit_cb(struct obd_device *obd, __u64 transno,
                              void *cb_data, int error)
 {
@@ -110,10 +106,16 @@ int filter_finish_transno(struct obd_export *exp, struct obd_trans_info *oti,
         fcd->fcd_last_xid = 0;
 
         off = fed->fed_lr_off;
-        fsfilt_add_journal_cb(exp->exp_obd, last_rcvd, oti->oti_handle,
-                              filter_commit_cb, NULL);
-        err = fsfilt_write_record(exp->exp_obd, filter->fo_rcvd_filp, fcd,
-                                  sizeof(*fcd), &off, 0);
+        if (off <= 0) {
+                CERROR("%s: client idx %d is %lld\n", exp->exp_obd->obd_name,
+                       fed->fed_lr_idx, fed->fed_lr_off);
+                err = -EINVAL;
+        } else {
+                fsfilt_add_journal_cb(exp->exp_obd, last_rcvd, oti->oti_handle,
+                                      filter_commit_cb, NULL);
+                err = fsfilt_write_record(exp->exp_obd, filter->fo_rcvd_filp,
+                                          fcd, sizeof(*fcd), &off, 0);
+        }
         if (err) {
                 log_pri = D_ERROR;
                 if (rc == 0)
@@ -148,6 +150,7 @@ static int filter_client_add(struct obd_device *obd, struct filter_obd *filter,
         ENTRY;
 
         LASSERT(bitmap != NULL);
+        LASSERTF(cl_idx > -2, "%d\n", cl_idx);
 
         /* XXX if fcd_uuid were a real obd_uuid, I could use obd_uuid_equals */
         if (!strcmp(fed->fed_fcd->fcd_uuid, obd->obd_uuid.uuid))
@@ -161,7 +164,7 @@ static int filter_client_add(struct obd_device *obd, struct filter_obd *filter,
         repeat:
                 if (cl_idx >= FILTER_LR_MAX_CLIENTS) {
                         CERROR("no client slots - fix FILTER_LR_MAX_CLIENTS\n");
-                        RETURN(-ENOMEM);
+                        RETURN(-EOVERFLOW);
                 }
                 if (test_and_set_bit(cl_idx, bitmap)) {
                         cl_idx = find_next_zero_bit(bitmap,
@@ -180,12 +183,13 @@ static int filter_client_add(struct obd_device *obd, struct filter_obd *filter,
         fed->fed_lr_idx = cl_idx;
         fed->fed_lr_off = le32_to_cpu(filter->fo_fsd->fsd_client_start) +
                 cl_idx * le16_to_cpu(filter->fo_fsd->fsd_client_size);
+        LASSERTF(fed->fed_lr_off > 0, "fed_lr_off = %llu\n", fed->fed_lr_off);
 
         CDEBUG(D_INFO, "client at index %d (%llu) with UUID '%s' added\n",
                fed->fed_lr_idx, fed->fed_lr_off, fed->fed_fcd->fcd_uuid);
 
         if (new_client) {
-                struct obd_run_ctxt saved;
+                struct lvfs_run_ctxt saved;
                 loff_t off = fed->fed_lr_off;
                 int err;
                 void *handle;
@@ -193,7 +197,7 @@ static int filter_client_add(struct obd_device *obd, struct filter_obd *filter,
                 CDEBUG(D_INFO, "writing client fcd at idx %u (%llu) (len %u)\n",
                        fed->fed_lr_idx,off,(unsigned int)sizeof(*fed->fed_fcd));
 
-                push_ctxt(&saved, &obd->obd_ctxt, NULL);
+                push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
                 /* Transaction needed to fix bug 1403 */
                 handle = fsfilt_start(obd,
                                       filter->fo_rcvd_filp->f_dentry->d_inode,
@@ -210,7 +214,7 @@ static int filter_client_add(struct obd_device *obd, struct filter_obd *filter,
                                       filter->fo_rcvd_filp->f_dentry->d_inode,
                                       handle, 1);
                 }
-                pop_ctxt(&saved, &obd->obd_ctxt, NULL);
+                pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
 
                 if (err) {
                         CERROR("error writing %s client idx %u: rc %d\n",
@@ -227,7 +231,7 @@ static int filter_client_free(struct obd_export *exp)
         struct filter_obd *filter = &exp->exp_obd->u.filter;
         struct obd_device *obd = exp->exp_obd;
         struct filter_client_data zero_fcd;
-        struct obd_run_ctxt saved;
+        struct lvfs_run_ctxt saved;
         int rc;
         loff_t off;
         ENTRY;
@@ -235,19 +239,24 @@ static int filter_client_free(struct obd_export *exp)
         if (fed->fed_fcd == NULL)
                 RETURN(0);
 
-        if (exp->exp_flags & OBD_OPT_FAILOVER)
-                GOTO(free, 0);
-
         /* XXX if fcd_uuid were a real obd_uuid, I could use obd_uuid_equals */
         if (strcmp(fed->fed_fcd->fcd_uuid, obd->obd_uuid.uuid ) == 0)
                 GOTO(free, 0);
 
+        CDEBUG(D_INFO, "freeing client at idx %u, offset %lld with UUID '%s'\n",
+               fed->fed_lr_idx, off, fed->fed_fcd->fcd_uuid);
+
         LASSERT(filter->fo_last_rcvd_slots != NULL);
 
         off = fed->fed_lr_off;
 
-        CDEBUG(D_INFO, "freeing client at idx %u (%lld) with UUID '%s'\n",
-               fed->fed_lr_idx, fed->fed_lr_off, fed->fed_fcd->fcd_uuid);
+        /* Don't clear fed_lr_idx here as it is likely also unset.  At worst
+         * we leak a client slot that will be cleaned on the next recovery. */
+        if (off <= 0) {
+                CERROR("%s: client idx %d has med_off %lld\n",
+                       obd->obd_name, fed->fed_lr_idx, off);
+                GOTO(free, rc = -EINVAL);
+        }
 
         /* Clear the bit _after_ zeroing out the client so we don't
            race with filter_client_add and zero out new clients.*/
@@ -257,21 +266,23 @@ static int filter_client_free(struct obd_export *exp)
                 LBUG();
         }
 
-        memset(&zero_fcd, 0, sizeof zero_fcd);
-        push_ctxt(&saved, &obd->obd_ctxt, NULL);
-        rc = fsfilt_write_record(obd, filter->fo_rcvd_filp, &zero_fcd,
-                                 sizeof(zero_fcd), &off, 0);
+        if (!(exp->exp_flags & OBD_OPT_FAILOVER)) {
+                memset(&zero_fcd, 0, sizeof zero_fcd);
+                push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
+                rc = fsfilt_write_record(obd, filter->fo_rcvd_filp, &zero_fcd,
+                                         sizeof(zero_fcd), &off, 0);
 
-        if (rc == 0)
-                /* update server's transno */
-                filter_update_server_data(obd, filter->fo_rcvd_filp,
-                                          filter->fo_fsd, 1);
-        pop_ctxt(&saved, &obd->obd_ctxt, NULL);
-
-        CDEBUG(rc == 0 ? D_INFO : D_ERROR,
-               "zeroing disconnecting client %s at idx %u (%llu) in %s rc %d\n",
-               fed->fed_fcd->fcd_uuid, fed->fed_lr_idx, fed->fed_lr_off,
-               LAST_RCVD, rc);
+                if (rc == 0)
+                        /* update server's transno */
+                        filter_update_server_data(obd, filter->fo_rcvd_filp,
+                                                  filter->fo_fsd, 1);
+                pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
+
+                CDEBUG(rc == 0 ? D_INFO : D_ERROR,
+                       "zeroing out client %s at idx %u (%llu) in %s rc %d\n",
+                       fed->fed_fcd->fcd_uuid, fed->fed_lr_idx, fed->fed_lr_off,
+                       LAST_RCVD, rc);
+        }
 
         if (!test_and_clear_bit(fed->fed_lr_idx, filter->fo_last_rcvd_slots)) {
                 CERROR("FILTER client %u: bit already clear in bitmap!!\n",
@@ -279,10 +290,12 @@ static int filter_client_free(struct obd_export *exp)
                 LBUG();
         }
 
+        EXIT;
 free:
         OBD_FREE(fed->fed_fcd, sizeof(*fed->fed_fcd));
+        fed->fed_fcd = NULL;
 
-        RETURN(0);
+        return 0;
 }
 
 static int filter_free_server_data(struct filter_obd *filter)
@@ -475,7 +488,9 @@ static int filter_init_server_data(struct obd_device *obd, struct file * filp)
                        sizeof exp->exp_client_uuid.uuid);
                 fed = &exp->exp_filter_data;
                 fed->fed_fcd = fcd;
-                filter_client_add(obd, filter, fed, cl_idx);
+                rc = filter_client_add(obd, filter, fed, cl_idx);
+                LASSERTF(rc == 0, "rc = %d\n", rc); /* can't fail existing */
+
                 /* create helper if export init gets more complex */
                 spin_lock_init(&fed->fed_lock);
 
@@ -741,14 +756,14 @@ static int filter_prep_groups(struct obd_device *obd)
 /* setup the object store with correct subdirectories */
 static int filter_prep(struct obd_device *obd)
 {
-        struct obd_run_ctxt saved;
+        struct lvfs_run_ctxt saved;
         struct filter_obd *filter = &obd->u.filter;
         struct file *file;
         struct inode *inode;
         int rc = 0;
         ENTRY;
 
-        push_ctxt(&saved, &obd->obd_ctxt, NULL);
+        push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
         file = filp_open(LAST_RCVD, O_RDWR | O_CREAT | O_LARGEFILE, 0700);
         if (!file || IS_ERR(file)) {
                 rc = PTR_ERR(file);
@@ -781,7 +796,7 @@ static int filter_prep(struct obd_device *obd)
                 GOTO(err_server_data, rc);
 
  out:
-        pop_ctxt(&saved, &obd->obd_ctxt, NULL);
+        pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
 
         return(rc);
 
@@ -798,7 +813,7 @@ static int filter_prep(struct obd_device *obd)
 /* cleanup the filter: write last used object id to status file */
 static void filter_post(struct obd_device *obd)
 {
-        struct obd_run_ctxt saved;
+        struct lvfs_run_ctxt saved;
         struct filter_obd *filter = &obd->u.filter;
         int rc, i;
 
@@ -806,7 +821,7 @@ static void filter_post(struct obd_device *obd)
          * best to start a transaction with h_sync, because we removed this
          * from lastobjid */
 
-        push_ctxt(&saved, &obd->obd_ctxt, NULL);
+        push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
         rc = filter_update_server_data(obd, filter->fo_rcvd_filp,
                                        filter->fo_fsd, 0);
         if (rc)
@@ -826,7 +841,7 @@ static void filter_post(struct obd_device *obd)
 
         filter_cleanup_groups(obd);
         filter_free_server_data(filter);
-        pop_ctxt(&saved, &obd->obd_ctxt, NULL);
+        pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
 }
 
 static void filter_set_last_id(struct filter_obd *filter, struct obdo *oa,
@@ -1182,7 +1197,7 @@ static int filter_intent_policy(struct ldlm_namespace *ns,
 
 /* mount the file system (secretly) */
 int filter_common_setup(struct obd_device *obd, obd_count len, void *buf,
-                        char *option)
+                        void *option)
 {
         struct lustre_cfg* lcfg = buf;
         struct filter_obd *filter = &obd->u.filter;
@@ -1201,13 +1216,13 @@ int filter_common_setup(struct obd_device *obd, obd_count len, void *buf,
         if (IS_ERR(obd->obd_fsops))
                 RETURN(PTR_ERR(obd->obd_fsops));
 
-        mnt = do_kern_mount(lustre_cfg_string(lcfg, 2), MS_NOATIME | MS_NODIRATIME,
-                            lustre_cfg_string(lcfg, 1), (void *)option);
+        mnt = do_kern_mount(lustre_cfg_string(lcfg, 2),MS_NOATIME|MS_NODIRATIME,
+                            lustre_cfg_string(lcfg, 1), option);
         rc = PTR_ERR(mnt);
         if (IS_ERR(mnt))
                 GOTO(err_ops, rc);
 
-        LASSERT(!ll_check_rdonly(ll_sbdev(mnt->mnt_sb)));
+        LASSERT(!lvfs_check_rdonly(lvfs_sbdev(mnt->mnt_sb)));
 
         if (lcfg->lcfg_bufcount > 3 && LUSTRE_CFG_BUFLEN(lcfg, 3) > 0) {
                 str = lustre_cfg_string(lcfg, 3);
@@ -1230,11 +1245,11 @@ int filter_common_setup(struct obd_device *obd, obd_count len, void *buf,
         filter->fo_fstype = mnt->mnt_sb->s_type->name;
         CDEBUG(D_SUPER, "%s: mnt = %p\n", filter->fo_fstype, mnt);
 
-        OBD_SET_CTXT_MAGIC(&obd->obd_ctxt);
-        obd->obd_ctxt.pwdmnt = mnt;
-        obd->obd_ctxt.pwd = mnt->mnt_root;
-        obd->obd_ctxt.fs = get_ds();
-        obd->obd_ctxt.cb_ops = filter_lvfs_ops;
+        OBD_SET_CTXT_MAGIC(&obd->obd_lvfs_ctxt);
+        obd->obd_lvfs_ctxt.pwdmnt = mnt;
+        obd->obd_lvfs_ctxt.pwd = mnt->mnt_root;
+        obd->obd_lvfs_ctxt.fs = get_ds();
+        obd->obd_lvfs_ctxt.cb_ops = filter_lvfs_ops;
 
         rc = filter_prep(obd);
         if (rc)
@@ -1262,8 +1277,6 @@ int filter_common_setup(struct obd_device *obd, obd_count len, void *buf,
         spin_lock_init(&filter->fo_w_disk_iosize.oh_lock);
         filter->fo_readcache_max_filesize = FILTER_MAX_CACHE_SIZE;
 
-        atomic_set(&filter->fo_quotachecking, 1);
-
         sprintf(ns_name, "filter-%s", obd->obd_uuid.uuid);
         obd->obd_namespace = ldlm_namespace_new(ns_name, LDLM_NAMESPACE_SERVER);
         if (obd->obd_namespace == NULL)
@@ -1281,6 +1294,11 @@ int filter_common_setup(struct obd_device *obd, obd_count len, void *buf,
                 GOTO(err_post, rc);
         }
 
+        rc = filter_quota_setup(filter);
+        if (rc) {
+                GOTO(err_post, rc);
+        }
+
         if (obd->obd_recovering) {
                 LCONSOLE_WARN("OST %s now serving %s, but will be in recovery "
                               "until %d %s reconnect, or if no clients "
@@ -1303,13 +1321,6 @@ int filter_common_setup(struct obd_device *obd, obd_count len, void *buf,
                               obd->obd_replayable ? "enabled" : "disabled");
         }
 
-        rc = qctxt_init(&filter->fo_quota_ctxt, filter->fo_sb, NULL);
-        if (rc) {
-                CERROR("initialize quota context failed! (rc:%d)\n", rc);
-                qctxt_cleanup(&filter->fo_quota_ctxt, 0);
-                GOTO(err_post, rc);
-        }
-
         RETURN(0);
 
 err_post:
@@ -1328,12 +1339,21 @@ static int filter_setup(struct obd_device *obd, obd_count len, void *buf)
 {
         struct lprocfs_static_vars lvars;
         struct lustre_cfg* lcfg = buf;
+        unsigned long page;
         int rc;
 
         if (!LUSTRE_CFG_BUFLEN(lcfg, 1) || !LUSTRE_CFG_BUFLEN(lcfg, 2))
                 RETURN(-EINVAL);
 
-        rc = filter_common_setup(obd, len, buf, lustre_cfg_buf(lcfg, 4));
+        /* 2.6.9 selinux wants a full option page for do_kern_mount (bug6471) */
+        page = get_zeroed_page(GFP_KERNEL);
+        if (!page)
+                RETURN(-ENOMEM);
+
+        memcpy((void *)page, lustre_cfg_buf(lcfg, 4),
+               LUSTRE_CFG_BUFLEN(lcfg, 4));
+        rc = filter_common_setup(obd, len, buf, (void *)page);
+        free_page(page);
 
         lprocfs_init_vars(filter, &lvars);
         if (rc == 0 && lprocfs_obd_setup(obd, lvars.obd_vars) == 0 &&
@@ -1349,19 +1369,82 @@ static int filter_setup(struct obd_device *obd, obd_count len, void *buf)
                 lproc_filter_attach_seqstat(obd);
         }
 
+        ping_evictor_start();
+
         return rc;
 }
 
-static int filter_precleanup(struct obd_device *obd)
+static struct llog_operations filter_mds_ost_repl_logops /* initialized below*/;
+static struct llog_operations filter_size_orig_logops = {
+        lop_setup: llog_obd_origin_setup,
+        lop_cleanup: llog_obd_origin_cleanup,
+        lop_add: llog_obd_origin_add
+};
+
+static int filter_llog_init(struct obd_device *obd, struct obd_device *tgt,
+                            int count, struct llog_catid *catid)
 {
-        target_cleanup_recovery(obd);
-        return (0);
+        struct llog_ctxt *ctxt;
+        int rc;
+        ENTRY;
+
+        filter_mds_ost_repl_logops = llog_client_ops;
+        filter_mds_ost_repl_logops.lop_cancel = llog_obd_repl_cancel;
+        filter_mds_ost_repl_logops.lop_connect = llog_repl_connect;
+        filter_mds_ost_repl_logops.lop_sync = llog_obd_repl_sync;
+
+        rc = llog_setup(obd, LLOG_MDS_OST_REPL_CTXT, tgt, 0, NULL,
+                        &filter_mds_ost_repl_logops);
+        if (rc)
+                RETURN(rc);
+
+        /* FIXME - assign unlink_cb for filter's recovery */
+        ctxt = llog_get_context(obd, LLOG_MDS_OST_REPL_CTXT);
+        ctxt->llog_proc_cb = filter_recov_log_mds_ost_cb;
+
+        rc = llog_setup(obd, LLOG_SIZE_ORIG_CTXT, tgt, 0, NULL,
+                        &filter_size_orig_logops);
+        RETURN(rc);
+}
+
+static int filter_llog_finish(struct obd_device *obd, int count)
+{
+        struct llog_ctxt *ctxt;
+        int rc = 0, rc2 = 0;
+        ENTRY;
+
+        ctxt = llog_get_context(obd, LLOG_MDS_OST_REPL_CTXT);
+        if (ctxt)
+                rc = llog_cleanup(ctxt);
+
+        ctxt = llog_get_context(obd, LLOG_SIZE_ORIG_CTXT);
+        if (ctxt)
+                rc2 = llog_cleanup(ctxt);
+        if (!rc)
+                rc = rc2;
+
+        RETURN(rc);
+}
+
+static int filter_precleanup(struct obd_device *obd, int stage)
+{
+        int rc = 0;
+        ENTRY;
+
+        switch(stage) {
+        case 1:
+                target_cleanup_recovery(obd);
+                break;
+        case 2:
+                rc = filter_llog_finish(obd, 0);
+        }
+        RETURN(rc);
 }
 
 static int filter_cleanup(struct obd_device *obd)
 {
         struct filter_obd *filter = &obd->u.filter;
-        ll_sbdev_type save_dev;
+        lvfs_sbdev_type save_dev;
         int must_relock = 0;
         ENTRY;
 
@@ -1378,13 +1461,15 @@ static int filter_cleanup(struct obd_device *obd)
                 }
         }
 
-        qctxt_cleanup(&filter->fo_quota_ctxt, 0);
+        ping_evictor_stop();
+
+        filter_quota_cleanup(filter);
 
         ldlm_namespace_free(obd->obd_namespace, obd->obd_force);
 
         if (filter->fo_sb == NULL)
                 RETURN(0);
-        save_dev = ll_sbdev(filter->fo_sb);
+        save_dev = lvfs_sbdev(filter->fo_sb);
 
         lprocfs_free_obd_stats(obd);
         lprocfs_obd_cleanup(obd);
@@ -1392,8 +1477,8 @@ static int filter_cleanup(struct obd_device *obd)
         filter_post(obd);
 
         shrink_dcache_parent(filter->fo_sb->s_root);
-        
-        DQUOT_OFF(filter->fo_sb);
+
+        LL_DQUOT_OFF(filter->fo_sb);
 
         if (atomic_read(&filter->fo_vfsmnt->mnt_count) > 1)
                 CERROR("%s: mount point %p busy, mnt_count: %d\n",
@@ -1407,14 +1492,11 @@ static int filter_cleanup(struct obd_device *obd)
                 must_relock++;
         }
         
-        obd_llog_finish(obd, 0);
-
         mntput(filter->fo_vfsmnt);
         //destroy_buffers(filter->fo_sb->s_dev);
         filter->fo_sb = NULL;
 
-
-        ll_clear_rdonly(save_dev);
+        lvfs_clear_rdonly(save_dev);
 
         if (must_relock)
                 lock_kernel();
@@ -1428,7 +1510,7 @@ static int filter_cleanup(struct obd_device *obd)
 
 /* nearly identical to mds_connect */
 static int filter_connect(struct lustre_handle *conn, struct obd_device *obd,
-                          struct obd_uuid *cluuid, struct obd_connect_data *data)
+                          struct obd_uuid *cluuid,struct obd_connect_data *data)
 {
         struct obd_export *exp;
         struct filter_export_data *fed;
@@ -1447,8 +1529,11 @@ static int filter_connect(struct lustre_handle *conn, struct obd_device *obd,
         LASSERT(exp != NULL);
 
         fed = &exp->exp_filter_data;
-        data->ocd_connect_flags &= OST_CONNECT_SUPPORTED;
-        exp->exp_connect_flags = data->ocd_connect_flags;
+
+        if (data != NULL) {
+                data->ocd_connect_flags &= OST_CONNECT_SUPPORTED;
+                exp->exp_connect_flags = data->ocd_connect_flags;
+        }
 
         spin_lock_init(&fed->fed_lock);
 
@@ -1465,6 +1550,7 @@ static int filter_connect(struct lustre_handle *conn, struct obd_device *obd,
         fed->fed_fcd = fcd;
 
         rc = filter_client_add(obd, filter, fed, -1);
+        GOTO(cleanup, rc);
 
 cleanup:
         if (rc) {
@@ -1476,7 +1562,7 @@ cleanup:
         } else {
                 class_export_put(exp);
         }
-        
+
         RETURN(rc);
 }
 
@@ -1640,7 +1726,7 @@ static int filter_disconnect(struct obd_export *exp)
 }
 
 struct dentry *__filter_oa2dentry(struct obd_device *obd,
-                                  struct obdo *oa, const char *what)
+                                  struct obdo *oa, const char *what, int quiet)
 {
         struct dentry *dchild = NULL;
         obd_gr group = 0;
@@ -1656,8 +1742,9 @@ struct dentry *__filter_oa2dentry(struct obd_device *obd,
         }
 
         if (dchild->d_inode == NULL) {
-                CERROR("%s: %s on non-existent object: "LPU64"\n",
-                       obd->obd_name, what, oa->o_id);
+                if (!quiet)
+                        CERROR("%s: %s on non-existent object: "LPU64"\n",
+                               obd->obd_name, what, oa->o_id);
                 f_dput(dchild);
                 RETURN(ERR_PTR(-ENOENT));
         }
@@ -1693,11 +1780,11 @@ static int filter_getattr(struct obd_export *exp, struct obdo *oa,
 }
 
 /* this is called from filter_truncate() until we have filter_punch() */
-static int filter_setattr(struct obd_export *exp, struct obdo *oa,
-                          struct lov_stripe_md *md, struct obd_trans_info *oti)
+int filter_setattr(struct obd_export *exp, struct obdo *oa,
+                   struct lov_stripe_md *md, struct obd_trans_info *oti)
 {
         struct obd_device *obd;
-        struct obd_run_ctxt saved;
+        struct lvfs_run_ctxt saved;
         struct filter_obd *filter;
         struct dentry *dentry;
         struct iattr iattr;
@@ -1710,7 +1797,7 @@ static int filter_setattr(struct obd_export *exp, struct obdo *oa,
         int rc, rc2;
         ENTRY;
 
-        dentry = filter_oa2dentry(exp->exp_obd, oa);
+        dentry = __filter_oa2dentry(exp->exp_obd, oa, __FUNCTION__, 1);
         if (IS_ERR(dentry))
                 RETURN(PTR_ERR(dentry));
 
@@ -1719,7 +1806,7 @@ static int filter_setattr(struct obd_export *exp, struct obdo *oa,
 
         iattr_from_obdo(&iattr, oa, oa->o_valid);
 
-        push_ctxt(&saved, &exp->exp_obd->obd_ctxt, NULL);
+        push_ctxt(&saved, &exp->exp_obd->obd_lvfs_ctxt, NULL);
         lock_kernel();
 
         if (oa->o_valid & OBD_MD_FLCOOKIE) {
@@ -1744,24 +1831,18 @@ static int filter_setattr(struct obd_export *exp, struct obdo *oa,
         if (IS_ERR(handle))
                 GOTO(out_unlock, rc = PTR_ERR(handle));
 
-        /* XXX this could be a rwsem instead, if filter_preprw played along */
-        if (iattr.ia_valid & ATTR_ATTR_FLAG)
+        if (iattr.ia_valid & ATTR_ATTR_FLAG) {
                 rc = fsfilt_iocontrol(exp->exp_obd, dentry->d_inode, NULL,
                                       EXT3_IOC_SETFLAGS,
                                       (long)&iattr.ia_attr_flags);
-        else {
+        else {
                 rc = fsfilt_setattr(exp->exp_obd, dentry, handle, &iattr, 1);
-                /* set cancel cookie callback function */  
-                if (fcc != NULL) {
-                        if (oti != NULL)
-                                fsfilt_add_journal_cb(obd, 0, oti->oti_handle,
-                                                      filter_cancel_cookies_cb,
-                                                      fcc);
-                        else
-                                fsfilt_add_journal_cb(obd, 0, handle,
-                                                      filter_cancel_cookies_cb,
-                                                      fcc);
-                }
+                if (fcc != NULL)
+                        /* set cancel cookie callback function */
+                        fsfilt_add_journal_cb(obd, 0, oti ?
+                                              oti->oti_handle : handle,
+                                              filter_cancel_cookies_cb,
+                                              fcc);
         }
 
         rc = filter_finish_transno(exp, oti, rc);
@@ -1794,7 +1875,7 @@ out_unlock:
         if (iattr.ia_valid & ATTR_SIZE)
                 up(&dentry->d_inode->i_sem);
         unlock_kernel();
-        pop_ctxt(&saved, &exp->exp_obd->obd_ctxt, NULL);
+        pop_ctxt(&saved, &exp->exp_obd->obd_lvfs_ctxt, NULL);
 
         f_dput(dentry);
 
@@ -2145,7 +2226,7 @@ static int filter_create(struct obd_export *exp, struct obdo *oa,
                          struct lov_stripe_md **ea, struct obd_trans_info *oti)
 {
         struct obd_device *obd = NULL;
-        struct obd_run_ctxt saved;
+        struct lvfs_run_ctxt saved;
         struct lov_stripe_md *lsm = NULL;
         obd_gr group = 0;
         int rc = 0, diff;
@@ -2166,7 +2247,7 @@ static int filter_create(struct obd_export *exp, struct obdo *oa,
         }
 
         obd = exp->exp_obd;
-        push_ctxt(&saved, &obd->obd_ctxt, NULL);
+        push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
 
         if ((oa->o_valid & OBD_MD_FLFLAGS) &&
             (oa->o_flags & OBD_FL_RECREATE_OBJS)) {
@@ -2188,7 +2269,7 @@ static int filter_create(struct obd_export *exp, struct obdo *oa,
                 }
         }
 
-        pop_ctxt(&saved, &obd->obd_ctxt, NULL);
+        pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
         if (rc && ea != NULL && *ea != lsm) {
                 obd_free_memmd(exp, &lsm);
         } else if (rc == 0 && ea != NULL) {
@@ -2203,13 +2284,13 @@ static int filter_create(struct obd_export *exp, struct obdo *oa,
         RETURN(rc);
 }
 
-static int filter_destroy(struct obd_export *exp, struct obdo *oa,
-                          struct lov_stripe_md *ea, struct obd_trans_info *oti)
+int filter_destroy(struct obd_export *exp, struct obdo *oa,
+                   struct lov_stripe_md *md, struct obd_trans_info *oti)
 {
         struct obd_device *obd;
         struct filter_obd *filter;
         struct dentry *dchild = NULL, *dparent = NULL;
-        struct obd_run_ctxt saved;
+        struct lvfs_run_ctxt saved;
         void *handle = NULL;
         struct llog_cookie *fcc = NULL;
         int rc, rc2, cleanup_phase = 0, have_prepared = 0;
@@ -2222,7 +2303,7 @@ static int filter_destroy(struct obd_export *exp, struct obdo *oa,
         obd = exp->exp_obd;
         filter = &obd->u.filter;
 
-        push_ctxt(&saved, &obd->obd_ctxt, NULL);
+        push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
 
  acquire_locks:
         dparent = filter_parent_lock(obd, group, oa->o_id);
@@ -2288,14 +2369,9 @@ cleanup:
         switch(cleanup_phase) {
         case 3:
                 if (fcc != NULL) {
-                        if (oti != NULL)
-                                fsfilt_add_journal_cb(obd, 0, oti->oti_handle,
-                                                      filter_cancel_cookies_cb,
-                                                      fcc);
-                        else
-                                fsfilt_add_journal_cb(obd, 0, handle,
-                                                      filter_cancel_cookies_cb,
-                                                      fcc);
+                        fsfilt_add_journal_cb(obd, 0,
+                                              oti ? oti->oti_handle : handle,
+                                              filter_cancel_cookies_cb, fcc);
                 }
                 rc = filter_finish_transno(exp, oti, rc);
                 rc2 = fsfilt_commit(obd, dparent->d_inode, handle, 0);
@@ -2309,7 +2385,7 @@ cleanup:
         case 1:
                 filter_parent_unlock(dparent);
         case 0:
-                pop_ctxt(&saved, &obd->obd_ctxt, NULL);
+                pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
                 break;
         default:
                 CERROR("invalid cleanup_phase %d\n", cleanup_phase);
@@ -2350,7 +2426,7 @@ static int filter_truncate(struct obd_export *exp, struct obdo *oa,
 static int filter_sync(struct obd_export *exp, struct obdo *oa,
                        struct lov_stripe_md *lsm, obd_off start, obd_off end)
 {
-        struct obd_run_ctxt saved;
+        struct lvfs_run_ctxt saved;
         struct filter_obd *filter;
         struct dentry *dentry;
         struct llog_ctxt *ctxt;
@@ -2372,10 +2448,10 @@ static int filter_sync(struct obd_export *exp, struct obdo *oa,
         if (IS_ERR(dentry))
                 RETURN(PTR_ERR(dentry));
 
-        push_ctxt(&saved, &exp->exp_obd->obd_ctxt, NULL);
+        push_ctxt(&saved, &exp->exp_obd->obd_lvfs_ctxt, NULL);
 
         down(&dentry->d_inode->i_sem);
-        rc = filemap_fdatasync(dentry->d_inode->i_mapping);
+        rc = filemap_fdatawrite(dentry->d_inode->i_mapping);
         if (rc == 0) {
                 /* just any file to grab fsync method - "file" arg unused */
                 struct file *file = filter->fo_rcvd_filp;
@@ -2392,7 +2468,7 @@ static int filter_sync(struct obd_export *exp, struct obdo *oa,
         oa->o_valid = OBD_MD_FLID;
         obdo_from_inode(oa, dentry->d_inode, FILTER_VALID_FLAGS);
 
-        pop_ctxt(&saved, &exp->exp_obd->obd_ctxt, NULL);
+        pop_ctxt(&saved, &exp->exp_obd->obd_lvfs_ctxt, NULL);
 
         f_dput(dentry);
         RETURN(rc);
@@ -2467,8 +2543,7 @@ static int filter_set_info(struct obd_export *exp, __u32 keylen,
         ctxt = llog_get_context(obd, LLOG_MDS_OST_REPL_CTXT);
         rc = llog_receptor_accept(ctxt, exp->exp_imp_reverse);
         
-        /* setup the quota context import */
-        obd->u.filter.fo_quota_ctxt.lqc_import = exp->exp_imp_reverse;
+        filter_quota_set_info(exp, obd);
 
         RETURN(rc);
 }
@@ -2498,7 +2573,7 @@ int filter_iocontrol(unsigned int cmd, struct obd_export *exp,
                 struct super_block *sb = obd->u.filter.fo_sb;
                 struct inode *inode = sb->s_root->d_inode;
                 BDEVNAME_DECLARE_STORAGE(tmp);
-                CERROR("setting device %s read-only\n",
+                CERROR("*** setting device %s read-only ***\n",
                        ll_bdevname(sb, tmp));
 
                 handle = fsfilt_start(obd, inode, FSFILT_OP_MKNOD, NULL);
@@ -2508,7 +2583,7 @@ int filter_iocontrol(unsigned int cmd, struct obd_export *exp,
                 CDEBUG(D_HA, "syncing ost %s\n", obd->obd_name);
                 rc = fsfilt_sync(obd, obd->u.filter.fo_sb);
 
-                ll_set_rdonly(ll_sbdev(obd->u.filter.fo_sb));
+                lvfs_set_rdonly(lvfs_sbdev(obd->u.filter.fo_sb));
                 RETURN(0);
         }
 
@@ -2526,9 +2601,9 @@ int filter_iocontrol(unsigned int cmd, struct obd_export *exp,
 /*
                 struct llog_ctxt *ctxt = NULL;
 
-                push_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_ctxt, NULL);
+                push_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_lvfs_ctxt, NULL);
                 rc = llog_ioctl(ctxt, cmd, data);
-                pop_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_ctxt, NULL);
+                pop_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_lvfs_ctxt, NULL);
 
                 RETURN(rc);
 */
@@ -2541,119 +2616,6 @@ int filter_iocontrol(unsigned int cmd, struct obd_export *exp,
         RETURN(0);
 }
 
-static int filter_quotacheck(struct obd_export *exp, struct obd_quotactl *oqctl)
-{
-        struct obd_run_ctxt saved;
-        struct obd_device *obd = exp->exp_obd;
-        int rc;
-
-        push_ctxt(&saved, &obd->obd_ctxt, NULL);
-
-        rc = fsfilt_quotacheck(obd, obd->u.filter.fo_sb, oqctl);
-        if (rc)
-                CERROR("%s: fsfilt_quotacheck: %d\n", obd->obd_name, rc);
-
-        pop_ctxt(&saved, &obd->obd_ctxt, NULL);
-
-        RETURN(rc);
-}
-
-static int filter_quotactl(struct obd_export *exp, struct obd_quotactl *oqctl)
-{
-        struct obd_device *obd = exp->exp_obd;
-        struct obd_run_ctxt saved;
-        int rc = 0;
-        ENTRY;
-
-        if (oqctl->qc_cmd == Q_QUOTAON || oqctl->qc_cmd == Q_QUOTAOFF ||
-            oqctl->qc_cmd == Q_GETOINFO || oqctl->qc_cmd == Q_GETOQUOTA ||
-            oqctl->qc_cmd == Q_GETQUOTA) {
-                push_ctxt(&saved, &obd->obd_ctxt, NULL);
-                rc = fsfilt_quotactl(obd, obd->u.filter.fo_sb, oqctl);
-                pop_ctxt(&saved, &obd->obd_ctxt, NULL);
-        } else if (oqctl->qc_cmd == Q_INITQUOTA) {
-                unsigned int uid = 0, gid = 0;
-
-                /* initialize quota limit to MIN_QLIMIT */
-                LASSERT(oqctl->qc_dqblk.dqb_valid == QIF_BLIMITS);
-                LASSERT(oqctl->qc_dqblk.dqb_bhardlimit == MIN_QLIMIT);
-                LASSERT(oqctl->qc_dqblk.dqb_bsoftlimit == 0);
-                oqctl->qc_cmd = Q_SETQUOTA;
-                rc = fsfilt_quotactl(obd, obd->u.filter.fo_sb, oqctl);
-                /* this value will be replied to client, we must restore it */
-                oqctl->qc_cmd = Q_INITQUOTA;
-                if (rc)
-                        RETURN(rc);
-
-                /* trigger qunit pre-acquire */
-                if (oqctl->qc_type == USRQUOTA)
-                        uid = oqctl->qc_id;
-                else
-                        gid = oqctl->qc_id;
-
-                rc = qctxt_adjust_qunit(obd, &obd->u.filter.fo_quota_ctxt, 
-                                        uid, gid, 1);
-        } else {
-                CERROR("%s: unsupported filter_quotactl command: %d\n",
-                       obd->obd_name, oqctl->qc_cmd);
-                LBUG();
-        }
-
-        RETURN(rc);
-}
-
-static struct llog_operations filter_mds_ost_repl_logops;
-static struct llog_operations filter_size_orig_logops = {
-        lop_setup: llog_obd_origin_setup,
-        lop_cleanup: llog_obd_origin_cleanup,
-        lop_add: llog_obd_origin_add
-};
-
-static int filter_llog_init(struct obd_device *obd, struct obd_device *tgt,
-                            int count, struct llog_catid *catid)
-{
-        struct llog_ctxt *ctxt;
-        int rc;
-        ENTRY;
-
-        filter_mds_ost_repl_logops = llog_client_ops;
-        filter_mds_ost_repl_logops.lop_cancel = llog_obd_repl_cancel;
-        filter_mds_ost_repl_logops.lop_connect = llog_repl_connect;
-        filter_mds_ost_repl_logops.lop_sync = llog_obd_repl_sync;
-
-        rc = llog_setup(obd, LLOG_MDS_OST_REPL_CTXT, tgt, 0, NULL,
-                        &filter_mds_ost_repl_logops);
-        if (rc)
-                RETURN(rc);
-
-        /* FIXME - assign unlink_cb for filter's recovery */
-        ctxt = llog_get_context(obd, LLOG_MDS_OST_REPL_CTXT);
-        ctxt->llog_proc_cb = filter_recov_log_mds_ost_cb;
-
-        rc = llog_setup(obd, LLOG_SIZE_ORIG_CTXT, tgt, 0, NULL,
-                        &filter_size_orig_logops);
-        RETURN(rc);
-}
-
-static int filter_llog_finish(struct obd_device *obd, int count)
-{
-        struct llog_ctxt *ctxt;
-        int rc = 0, rc2 = 0;
-        ENTRY;
-
-        ctxt = llog_get_context(obd, LLOG_MDS_OST_REPL_CTXT);
-        if (ctxt)
-                rc = llog_cleanup(ctxt);
-
-        ctxt = llog_get_context(obd, LLOG_SIZE_ORIG_CTXT);
-        if (ctxt)
-                rc2 = llog_cleanup(ctxt);
-        if (!rc)
-                rc = rc2;
-
-        RETURN(rc);
-}
-
 static struct dentry *filter_lvfs_fid2dentry(__u64 id, __u32 gen, __u64 gr,
                                              void *data)
 {
index e90fb60..1c30fbc 100644 (file)
@@ -100,8 +100,8 @@ void f_dput(struct dentry *);
 struct dentry *filter_fid2dentry(struct obd_device *, struct dentry *dir,
                                  obd_gr group, obd_id id);
 struct dentry *__filter_oa2dentry(struct obd_device *obd, struct obdo *oa,
-                                  const char *what);
-#define filter_oa2dentry(obd, oa) __filter_oa2dentry(obd, oa, __FUNCTION__)
+                                  const char *what, int quiet);
+#define filter_oa2dentry(obd, oa) __filter_oa2dentry(obd, oa, __FUNCTION__, 0)
 
 int filter_finish_transno(struct obd_export *, struct obd_trans_info *, int rc);
 __u64 filter_next_id(struct filter_obd *, struct obdo *);
@@ -110,7 +110,11 @@ int filter_update_server_data(struct obd_device *, struct file *,
                               struct filter_server_data *, int force_sync);
 int filter_update_last_objid(struct obd_device *, obd_gr, int force_sync);
 int filter_common_setup(struct obd_device *, obd_count len, void *buf,
-                        char *option);
+                        void *option);
+int filter_destroy(struct obd_export *exp, struct obdo *oa,
+                   struct lov_stripe_md *md, struct obd_trans_info *);
+int filter_setattr(struct obd_export *exp, struct obdo *oa,
+                   struct lov_stripe_md *md, struct obd_trans_info *oti);
 
 /* filter_lvb.c */
 extern struct ldlm_valblock_ops filter_lvbo;
@@ -168,22 +172,83 @@ int filter_san_setup(struct obd_device *obd, obd_count len, void *buf);
 int filter_san_preprw(int cmd, struct obd_export *, struct obdo *, int objcount,
                       struct obd_ioobj *, int niocount, struct niobuf_remote *);
 
-#ifdef __KERNEL__
+#ifdef LPROCFS
 void filter_tally_write(struct filter_obd *filter, struct page **pages,
-                        int nr_pages, unsigned long *blocks, 
+                        int nr_pages, unsigned long *blocks,
                         int blocks_per_page);
-void filter_tally_read(struct filter_obd *filter, struct niobuf_local *lnb, 
+void filter_tally_read(struct filter_obd *filter, struct niobuf_local *lnb,
                        int niocount);
 int lproc_filter_attach_seqstat(struct obd_device *dev);
 #else
-static inline filter_tally_write(struct filter_obd *filter, 
-                                 struct page **pages, int nr_pages, 
+static inline void filter_tally_write(struct filter_obd *filter,
+                                 struct page **pages, int nr_pages,
                                  unsigned long *blocks, int blocks_per_page) {}
-static inline void  filter_tally_read(struct filter_obd *filter, 
-                                      struct niobuf_local *lnb, int niocount)
-                                      {}
-static inline lproc_filter_attach_seqstat(struct obd_device *dev) {}
+static inline void filter_tally_read(struct filter_obd *filter,
+                                     struct niobuf_local *lnb, int niocount) {}
+static inline int lproc_filter_attach_seqstat(struct obd_device *dev) {};
 #endif
 
+/* Quota stuff */
+#ifdef HAVE_QUOTA_SUPPORT
+int filter_quota_setup(struct filter_obd *filter);
+void filter_quota_cleanup(struct filter_obd *filter);
+void filter_quota_set_info(struct obd_export *exp, struct obd_device *obd);
+int filter_quotacheck(struct obd_export *exp, struct obd_quotactl *oqctl);
+int filter_quotactl(struct obd_export *exp, struct obd_quotactl *oqctl);
+int filter_quota_enforcement(struct obd_device *obd,
+                                    unsigned int fsuid, unsigned int fsgid,
+                                    struct lvfs_ucred **ret_uc);
+int filter_get_quota_flag(struct obd_device *obd, struct obdo *oa);
+int filter_quota_check_master(struct obd_device *obd, struct inode *inode);
+
+#ifdef LPROCFS
+int lprocfs_filter_rd_bunit(char *page, char **start, off_t off,
+                                   int count, int *eof, void *data);
+int lprocfs_filter_rd_iunit(char *page, char **start, off_t off,
+                                   int count, int *eof, void *data);
+int lprocfs_filter_wr_bunit(struct file *file, const char *buffer,
+                                   unsigned long count, void *data);
+int lprocfs_filter_wr_iunit(struct file *file, const char *buffer,
+                                   unsigned long count, void *data);
+int lprocfs_filter_rd_btune(char *page, char **start, off_t off,
+                                   int count, int *eof, void *data);
+int lprocfs_filter_rd_itune(char *page, char **start, off_t off,
+                                   int count, int *eof, void *data);
+int lprocfs_filter_wr_btune(struct file *file, const char *buffer,
+                                   unsigned long count, void *data);
+int lprocfs_filter_wr_itune(struct file *file, const char *buffer,
+                                   unsigned long count, void *data);
+#endif /* LPROCFS */
+#else /* Quota */
+static inline int filter_quota_setup(struct filter_obd *filter)
+{
+        return 0;
+}
+static inline void filter_quota_cleanup(struct filter_obd *filter) {}
+static inline void filter_quota_set_info(struct obd_export *exp, struct obd_device *obd) {}
+static inline int filter_quotacheck(struct obd_export *exp, struct obd_quotactl *oqctl)
+{
+        return -ENOTSUPP;
+}
+static inline int filter_quotactl(struct obd_export *exp, struct obd_quotactl *oqctl)
+{
+        return -ENOTSUPP;
+}
+static inline int filter_quota_enforcement(struct obd_device *obd,
+                                    unsigned int fsuid, unsigned int fsgid,
+                                    struct lvfs_ucred **ret_uc)
+{
+        return 0;
+}
+static inline int filter_get_quota_flag(struct obd_device *obd,
+                                 struct obdo *oa)
+{
+        return 0;
+}
+static inline int filter_quota_check_master(struct obd_device *obd, struct inode *inode)
+{
+        return 0;
+}
+#endif /* Quota */
 
 #endif
index baa8173..88e2dfa 100644 (file)
@@ -272,7 +272,7 @@ static int filter_preprw_read(int cmd, struct obd_export *exp, struct obdo *oa,
                               struct obd_trans_info *oti)
 {
         struct obd_device *obd = exp->exp_obd;
-        struct obd_run_ctxt saved;
+        struct lvfs_run_ctxt saved;
         struct niobuf_remote *rnb;
         struct niobuf_local *lnb;
         struct dentry *dentry = NULL;
@@ -297,7 +297,7 @@ static int filter_preprw_read(int cmd, struct obd_export *exp, struct obdo *oa,
 
         memset(res, 0, niocount * sizeof(*res));
 
-        push_ctxt(&saved, &exp->exp_obd->obd_ctxt, NULL);
+        push_ctxt(&saved, &exp->exp_obd->obd_lvfs_ctxt, NULL);
 
         rc = filter_alloc_iobuf(&obd->u.filter, OBD_BRW_READ, obj->ioo_bufcnt,
                                 &iobuf);
@@ -318,7 +318,7 @@ static int filter_preprw_read(int cmd, struct obd_export *exp, struct obdo *oa,
 
         fsfilt_check_slow(now, obd_timeout, "preprw_read setup");
 
-        for (i = 0, lnb = res, rnb = nb; i < obj->ioo_bufcnt; 
+        for (i = 0, lnb = res, rnb = nb; i < obj->ioo_bufcnt;
              i++, rnb++, lnb++) {
                 lnb->dentry = dentry;
                 lnb->offset = rnb->offset;
@@ -327,8 +327,8 @@ static int filter_preprw_read(int cmd, struct obd_export *exp, struct obdo *oa,
 
                 if (inode->i_size <= rnb->offset)
                       /* If there's no more data, abort early.
-                      * lnb->page == NULL and lnb->rc == 0, so it's
-                      * easy to detect later. */
+                       * lnb->page == NULL and lnb->rc == 0, so it's
+                       * easy to detect later. */
                         break;
                 else
                         rc = filter_alloc_dio_page(obd, inode, lnb);
@@ -375,7 +375,7 @@ static int filter_preprw_read(int cmd, struct obd_export *exp, struct obdo *oa,
         if (iobuf != NULL)
                 filter_free_iobuf(iobuf);
 
-        pop_ctxt(&saved, &exp->exp_obd->obd_ctxt, NULL);
+        pop_ctxt(&saved, &exp->exp_obd->obd_lvfs_ctxt, NULL);
         if (rc)
                 CERROR("io error %d\n", rc);
 
@@ -512,7 +512,7 @@ static int filter_preprw_write(int cmd, struct obd_export *exp, struct obdo *oa,
                                struct niobuf_local *res,
                                struct obd_trans_info *oti)
 {
-        struct obd_run_ctxt saved;
+        struct lvfs_run_ctxt saved;
         struct niobuf_remote *rnb;
         struct niobuf_local *lnb = res;
         struct fsfilt_objinfo fso;
@@ -527,13 +527,14 @@ static int filter_preprw_write(int cmd, struct obd_export *exp, struct obdo *oa,
 
         memset(res, 0, niocount * sizeof(*res));
 
+        /* This iobuf is for reading any partial pages from disk */
         rc = filter_alloc_iobuf(&exp->exp_obd->u.filter, OBD_BRW_READ,
                                 obj->ioo_bufcnt, &iobuf);
         if (rc)
                 GOTO(cleanup, rc);
         cleanup_phase = 1;
 
-        push_ctxt(&saved, &exp->exp_obd->obd_ctxt, NULL);
+        push_ctxt(&saved, &exp->exp_obd->obd_lvfs_ctxt, NULL);
         dentry = filter_fid2dentry(exp->exp_obd, NULL, obj->ioo_gr,
                                    obj->ioo_id);
         if (IS_ERR(dentry))
@@ -648,7 +649,7 @@ cleanup:
                 if (rc)
                         filter_free_dio_pages(objcount, obj, niocount, res);
         case 3:
-                pop_ctxt(&saved, &exp->exp_obd->obd_ctxt, NULL);
+                pop_ctxt(&saved, &exp->exp_obd->obd_lvfs_ctxt, NULL);
                 filter_free_iobuf(iobuf);
         case 2:
                 if (rc)
@@ -659,7 +660,7 @@ cleanup:
                 if (oa)
                         filter_grant_incoming(exp, oa);
                 spin_unlock(&exp->exp_obd->obd_osfs_lock);
-                pop_ctxt(&saved, &exp->exp_obd->obd_ctxt, NULL);
+                pop_ctxt(&saved, &exp->exp_obd->obd_lvfs_ctxt, NULL);
                 filter_free_iobuf(iobuf);
                 break;
         default:;
index d4327ca..4d43bb3 100644 (file)
@@ -79,7 +79,7 @@ static void check_pending_bhs(unsigned long *blocks, int nr_pages, dev_t dev,
 /* when brw_kiovec() is asked to read from block -1UL it just zeros
  * the page.  this gives us a chance to verify the write mappings
  * as well */
-static int filter_cleanup_mappings(int rw, struct kiobuf *iobuf, 
+static int filter_cleanup_mappings(int rw, struct kiobuf *iobuf,
                                    struct inode *inode)
 {
         int i, blocks_per_page_bits = PAGE_SHIFT - inode->i_blkbits;
@@ -107,23 +107,55 @@ static void dump_page(int rw, unsigned long block, struct page *page)
 }
 #endif
 
-static void filter_clear_page_cache(struct inode *inode, struct kiobuf *iobuf)
+/* These are our hacks to keep our directio/bh IO coherent with ext3's
+ * page cache use.  Most notably ext3 reads file data into the page
+ * cache when it is zeroing the tail of partial-block truncates and
+ * leaves it there, sometimes generating io from it at later truncates.
+ * This removes the partial page and its buffers from the page cache,
+ * so it should only ever cause a wait in rare cases, as otherwise we
+ * always do full-page IO to the OST.
+ *
+ * The call to truncate_complete_page() will call journal_flushpage() to
+ * free the buffers and drop the page from cache.  The buffers should not
+ * be dirty, because we already called fdatasync/fdatawait on them.
+ */
+static int filter_clear_page_cache(struct inode *inode, struct kiobuf *iobuf)
 {
         struct page *page;
-        int i;
+        int i, rc, rc2;
+
+        check_pending_bhs(KIOBUF_GET_BLOCKS(iobuf), iobuf->nr_pages,
+                          inode->i_dev, 1 << inode->i_blkbits);
+
+        /* This is nearly generic_osync_inode, without the waiting on the inode
+        rc = generic_osync_inode(inode, inode->i_mapping,
+                                 OSYNC_DATA|OSYNC_METADATA);
+         */
+        rc = filemap_fdatasync(inode->i_mapping);
+        rc2 = fsync_inode_data_buffers(inode);
+        if (rc == 0)
+                rc = rc2;
+        rc2 = filemap_fdatawait(inode->i_mapping);
+        if (rc == 0)
+                rc = rc2;
+        if (rc != 0)
+                RETURN(rc);
 
+        /* be careful to call this after fsync_inode_data_buffers has waited
+         * for IO to complete before we evict it from the cache */
         for (i = 0; i < iobuf->nr_pages ; i++) {
                 page = find_lock_page(inode->i_mapping,
                                       iobuf->maplist[i]->index);
                 if (page == NULL)
                         continue;
-                if (page->mapping != NULL) {
-                        block_flushpage(page, 0);
-                        truncate_complete_page(page);
-                }
+                if (page->mapping != NULL)
+                        ll_truncate_complete_page(page);
+
                 unlock_page(page);
                 page_cache_release(page);
         }
+
+        return 0;
 }
 
 /* Must be called with i_sem taken for writes; this will drop it */
@@ -147,7 +179,7 @@ int filter_direct_io(int rw, struct dentry *dchild, void *buf,
         if (iobuf->nr_pages * blocks_per_page > KIO_MAX_SECTORS)
                 GOTO(cleanup, rc = -EINVAL);
 
-        if (iobuf->nr_pages * blocks_per_page > 
+        if (iobuf->nr_pages * blocks_per_page >
             OBDFILTER_CREATED_SCRATCHPAD_ENTRIES)
                 GOTO(cleanup, rc = -EINVAL);
 
@@ -196,27 +228,10 @@ int filter_direct_io(int rw, struct dentry *dchild, void *buf,
                         GOTO(cleanup, rc);
         }
 
-        /* these are our hacks to keep our directio/bh IO coherent with ext3's
-         * page cache use.  Most notably ext3 reads file data into the page
-         * cache when it is zeroing the tail of partial-block truncates and
-         * leaves it there, sometimes generating io from it at later truncates.
-         * Someday very soon we'll be performing our brw_kiovec() IO to and
-         * from the page cache. */
-        check_pending_bhs(KIOBUF_GET_BLOCKS(iobuf), iobuf->nr_pages,
-                          inode->i_dev, 1 << inode->i_blkbits);
-
-        rc = filemap_fdatasync(inode->i_mapping);
-        if (rc == 0)
-                rc = fsync_inode_data_buffers(inode);
-        if (rc == 0)
-                rc = filemap_fdatawait(inode->i_mapping);
+        rc = filter_clear_page_cache(inode, iobuf);
         if (rc < 0)
                 GOTO(cleanup, rc);
 
-        /* be careful to call this after fsync_inode_data_buffers has waited
-         * for IO to complete before we evict it from the cache */
-        filter_clear_page_cache(inode, iobuf);
-
         rc = fsfilt_send_bio(rw, obd, inode, iobuf);
 
         CDEBUG(D_INFO, "tried to write %d pages, rc = %d\n",
@@ -343,7 +358,7 @@ int filter_commitrw_write(struct obd_export *exp, struct obdo *oa, int objcount,
                           int rc)
 {
         struct obd_device *obd = exp->exp_obd;
-        struct obd_run_ctxt saved;
+        struct lvfs_run_ctxt saved;
         struct niobuf_local *lnb;
         struct fsfilt_objinfo fso;
         struct iattr iattr = { 0 };
@@ -390,7 +405,7 @@ int filter_commitrw_write(struct obd_export *exp, struct obdo *oa, int objcount,
                         iattr.ia_size = this_size;
         }
 
-        push_ctxt(&saved, &obd->obd_ctxt, NULL);
+        push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
         cleanup_phase = 2;
 
         down(&inode->i_sem);
@@ -417,10 +432,14 @@ int filter_commitrw_write(struct obd_export *exp, struct obdo *oa, int objcount,
         fsfilt_check_slow(now, obd_timeout, "direct_io");
 
         err = fsfilt_commit_wait(obd, inode, wait_handle);
-        if (err)
+        if (err) {
+                CERROR("Failure to commit OST transaction (%d)?\n", err);
                 rc = err;
+        }
         if (obd_sync_filter && !err)
-                LASSERT(oti->oti_transno <= obd->obd_last_committed);
+                LASSERTF(oti->oti_transno <= obd->obd_last_committed,
+                         "oti_transno "LPU64" last_committed "LPU64"\n",
+                         oti->oti_transno, obd->obd_last_committed);
         fsfilt_check_slow(now, obd_timeout, "commitrw commit");
 
 cleanup:
@@ -428,7 +447,7 @@ cleanup:
 
         switch (cleanup_phase) {
         case 2:
-                pop_ctxt(&saved, &obd->obd_ctxt, NULL);
+                pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
                 LASSERT(current->journal_info == NULL);
         case 1:
                 filter_free_iobuf(iobuf);
index a248361..a706583 100644 (file)
@@ -123,11 +123,11 @@ static int dio_complete_routine(struct bio *bio, unsigned int done, int error)
                 CERROR("***** bio->bi_private is NULL!  This should never "
                        "happen.  Normally, I would crash here, but instead I "
                        "will dump the bio contents to the console.  Please "
-                       "report this to CFS, along with any interesting messages "
-                       "leading up to this point (like SCSI errors, perhaps).  "
-                       "Because bi_private is NULL, I can't wake up the thread "
-                       "that initiated this I/O -- so you will probably have to "
-                       "reboot this node.");
+                       "report this to CFS, along with any interesting "
+                       "messages leading up to this point (like SCSI errors, "
+                       "perhaps).  Because bi_private is NULL, I can't wake up "
+                       "the thread that initiated this I/O -- so you will "
+                       "probably have to reboot this node.\n");
                 CERROR("bi_next: %p, bi_flags: %lx, bi_rw: %lu, bi_vcnt: %d, "
                        "bi_idx: %d, bi->size: %d, bi_end_io: %p, bi_cnt: %d, "
                        "bi_private: %p\n", bio->bi_next, bio->bi_flags,
@@ -343,7 +343,7 @@ int filter_do_bio(struct obd_device *obd, struct inode *inode,
                         record_finish_io(dreq, rw, rc);
                 }
         }
-                        
+
  out:
         wait_event(dreq->dr_wait, atomic_read(&dreq->dr_numreqs) == 0);
 
@@ -352,89 +352,55 @@ int filter_do_bio(struct obd_device *obd, struct inode *inode,
         RETURN(rc);
 }
 
-static void filter_clear_page_cache(struct inode *inode, struct dio_request *iobuf)
+/* These are our hacks to keep our directio/bh IO coherent with ext3's
+ * page cache use.  Most notably ext3 reads file data into the page
+ * cache when it is zeroing the tail of partial-block truncates and
+ * leaves it there, sometimes generating io from it at later truncates.
+ * This removes the partial page and its buffers from the page cache,
+ * so it should only ever cause a wait in rare cases, as otherwise we
+ * always do full-page IO to the OST.
+ *
+ * The call to truncate_complete_page() will call journal_invalidatepage()
+ * to free the buffers and drop the page from cache.  The buffers should
+ * not be dirty, because we already called fdatasync/fdatawait on them.
+ */
+static int filter_clear_page_cache(struct inode *inode,
+                                    struct dio_request *iobuf)
 {
         struct page *page;
-        int i;
+        int i, rc, rc2;
+
+        /* This is nearly generic_osync_inode, without the waiting on the inode
+        rc = generic_osync_inode(inode, inode->i_mapping,
+                                 OSYNC_DATA|OSYNC_METADATA);
+         */
+        rc = filemap_fdatawrite(inode->i_mapping);
+        rc2 = sync_mapping_buffers(inode->i_mapping);
+        if (rc == 0)
+                rc = rc2;
+        rc2 = filemap_fdatawait(inode->i_mapping);
+        if (rc == 0)
+                rc = rc2;
+        if (rc != 0)
+                RETURN(rc);
 
-        for (i = 0; i < iobuf->dr_npages ; i++) {
+        /* be careful to call this after fsync_inode_data_buffers has waited
+         * for IO to complete before we evict it from the cache */
+        for (i = 0; i < iobuf->dr_npages; i++) {
                 page = find_lock_page(inode->i_mapping,
                                       iobuf->dr_pages[i]->index);
                 if (page == NULL)
                         continue;
                 if (page->mapping != NULL) {
-                        block_invalidatepage(page, 0);
+                        wait_on_page_writeback(page);
                         ll_truncate_complete_page(page);
                 }
 
                 unlock_page(page);
                 page_cache_release(page);
         }
-}
-
-static int filter_quota_enforcement(struct obd_device *obd,
-                                    unsigned int fsuid, unsigned int fsgid,
-                                    struct obd_ucred **ret_uc)
-{
-        struct filter_obd *filter = &obd->u.filter;
-        struct obd_ucred *uc = NULL;
-        ENTRY;
-
-        if (!sb_any_quota_enabled(filter->fo_sb))
-                RETURN(0);
 
-        OBD_ALLOC(uc, sizeof(*uc));
-        if (!uc)
-                RETURN(-ENOMEM);
-        *ret_uc = uc;
-
-        uc->ouc_fsuid = fsuid;
-        uc->ouc_fsgid = fsgid;
-        uc->ouc_cap = current->cap_effective;
-        if (!fsuid)
-                cap_raise(uc->ouc_cap, CAP_SYS_RESOURCE);
-        else
-                cap_lower(uc->ouc_cap, CAP_SYS_RESOURCE);
-        
-        RETURN(0);
-}
-
-static int filter_get_quota_flag(struct obd_device *obd,
-                                 struct obdo *oa)
-{
-        struct filter_obd *filter = &obd->u.filter;
-        int cnt;
-        int rc = 0, err;
-        ENTRY;
-
-        if (!sb_any_quota_enabled(filter->fo_sb))
-                RETURN(rc);
-
-        oa->o_flags = QUOTA_OK;
-
-        for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
-                struct obd_quotactl oqctl;
-
-                oqctl.qc_cmd = Q_GETQUOTA;
-                oqctl.qc_type = cnt;
-                oqctl.qc_id = (cnt == USRQUOTA) ? oa->o_uid : oa->o_gid;
-                err = fsfilt_quotactl(obd, filter->fo_sb, &oqctl);
-                if (err) {
-                        if (!rc)
-                                rc = err;
-                        continue;
-                }
-
-                /* set over quota flags for a uid/gid */
-                oa->o_valid |= (cnt == USRQUOTA) ?
-                               OBD_MD_FLUSRQUOTA : OBD_MD_FLGRPQUOTA;
-                if (oqctl.qc_dqblk.dqb_bhardlimit &&
-                   (toqb(oqctl.qc_dqblk.dqb_curspace) > oqctl.qc_dqblk.dqb_bhardlimit))
-                        oa->o_flags |= (cnt == USRQUOTA) ? 
-                                       OBD_FL_NO_USRQUOTA : OBD_FL_NO_GRPQUOTA;
-        }
-
-        RETURN(rc);
+        return 0;
 }
 
 /* Must be called with i_sem taken for writes; this will drop it */
@@ -446,7 +412,6 @@ int filter_direct_io(int rw, struct dentry *dchild, void *iobuf,
         struct dio_request *dreq = iobuf;
         struct inode *inode = dchild->d_inode;
         int blocks_per_page = PAGE_SIZE >> inode->i_blkbits;
-        struct lustre_quota_ctxt *qctxt = &obd->u.filter.fo_quota_ctxt;
         int rc, rc2;
         ENTRY;
 
@@ -465,7 +430,7 @@ int filter_direct_io(int rw, struct dentry *dchild, void *iobuf,
                 cap_raise(current->cap_effective, CAP_SYS_RESOURCE);
                 dreq->dr_flag &= ~OBD_BRW_FROM_GRANT;
         }
-        
+
 remap:
         rc = fsfilt_map_inode_pages(obd, inode,
                                     dreq->dr_pages, dreq->dr_npages,
@@ -474,18 +439,17 @@ remap:
                                     rw == OBD_BRW_WRITE, NULL);
 
         if (rc == -EDQUOT) {
-                LASSERT(rw == OBD_BRW_WRITE && 
+                LASSERT(rw == OBD_BRW_WRITE &&
                         !cap_raised(current->cap_effective, CAP_SYS_RESOURCE));
 
-                /* Unfortunately, if quota master is too busy to handle the 
-                 * pre-dqacq in time or this user has exceeded quota limit, we 
-                 * have to wait for the completion of in flight dqacq/dqrel, 
+                /* Unfortunately, if quota master is too busy to handle the
+                 * pre-dqacq in time or this user has exceeded quota limit, we
+                 * have to wait for the completion of in flight dqacq/dqrel,
                  * then try again */
-                if (qctxt_wait_on_dqacq(obd, qctxt, inode->i_uid, 
-                                        inode->i_gid, 1) == -EAGAIN)
+                if (filter_quota_check_master(obd, inode))
                         goto remap;
         }
-        
+
         if (rw == OBD_BRW_WRITE) {
                 if (rc == 0) {
                         filter_tally_write(&obd->u.filter,
@@ -511,24 +475,10 @@ remap:
                         RETURN(rc);
         }
 
-        /* This is nearly osync_inode, without the waiting
-        rc = generic_osync_inode(inode, inode->i_mapping,
-                                 OSYNC_DATA|OSYNC_METADATA); */
-        rc = filemap_fdatawrite(inode->i_mapping);
-        rc2 = sync_mapping_buffers(inode->i_mapping);
-        if (rc == 0)
-                rc = rc2;
-        rc2 = filemap_fdatawait(inode->i_mapping);
-        if (rc == 0)
-                rc = rc2;
-
+        rc = filter_clear_page_cache(inode, dreq);
         if (rc != 0)
                 RETURN(rc);
 
-        /* be careful to call this after fsync_inode_data_buffers has waited
-         * for IO to complete before we evict it from the cache */
-        filter_clear_page_cache(inode, dreq);
-
         RETURN(filter_do_bio(obd, inode, dreq, rw));
 }
 
@@ -560,7 +510,7 @@ int filter_commitrw_write(struct obd_export *exp, struct obdo *oa,
 {
         struct niobuf_local *lnb;
         struct dio_request *dreq = NULL;
-        struct obd_run_ctxt saved;
+        struct lvfs_run_ctxt saved;
         struct fsfilt_objinfo fso;
         struct iattr iattr = { 0 };
         struct inode *inode = NULL;
@@ -568,7 +518,7 @@ int filter_commitrw_write(struct obd_export *exp, struct obdo *oa,
         int i, err, cleanup_phase = 0;
         struct obd_device *obd = exp->exp_obd;
         struct filter_obd *filter = &obd->u.filter;
-        struct obd_ucred *uc = NULL;
+        struct lvfs_ucred *uc = NULL;
         int   total_size = 0;
         ENTRY;
 
@@ -620,12 +570,12 @@ int filter_commitrw_write(struct obd_export *exp, struct obdo *oa,
 
         /* The client store the user credit information fsuid and fsgid
          * in oa->o_uid and oa->o_gid. In case of quota enabled, we use 
-         * them to build the obd_ucred so as to enforce oss quota check */
+         * them to build the lvfs_ucred so as to enforce oss quota check */
         rc = filter_quota_enforcement(obd, oa->o_uid, oa->o_gid, &uc);
         if (rc)
                 GOTO(cleanup, rc);
 
-        push_ctxt(&saved, &obd->obd_ctxt, uc);
+        push_ctxt(&saved, &obd->obd_lvfs_ctxt, uc);
         cleanup_phase = 2;
 
         down(&inode->i_sem);
@@ -662,7 +612,9 @@ int filter_commitrw_write(struct obd_export *exp, struct obdo *oa,
                 rc = err;
 
         if (obd_sync_filter && !err)
-                LASSERT(oti->oti_transno <= obd->obd_last_committed);
+                LASSERTF(oti->oti_transno <= obd->obd_last_committed,
+                         "oti_transno "LPU64" last_committed "LPU64"\n",
+                         oti->oti_transno, obd->obd_last_committed);
 
         fsfilt_check_slow(now, obd_timeout, "commitrw commit");
 
@@ -671,7 +623,7 @@ cleanup:
 
         switch (cleanup_phase) {
         case 2:
-                pop_ctxt(&saved, &obd->obd_ctxt, uc);
+                pop_ctxt(&saved, &obd->obd_lvfs_ctxt, uc);
                 if (uc)
                         OBD_FREE(uc, sizeof(*uc));
                 LASSERT(current->journal_info == NULL);
index 34ad008..2ddfe72 100644 (file)
@@ -137,7 +137,7 @@ static int filter_recov_log_unlink_cb(struct llog_ctxt *ctxt,
         memcpy(obdo_logcookie(oa), cookie, sizeof(*cookie));
         oid = oa->o_id;
 
-        rc = obd_destroy(exp, oa, NULL, NULL);
+        rc = filter_destroy(exp, oa, NULL, NULL);
         obdo_free(oa);
         if (rc == -ENOENT) {
                 CDEBUG(D_HA, "object already removed, send cookie\n");
@@ -165,10 +165,10 @@ static int filter_recov_log_setattr_cb(struct llog_ctxt *ctxt,
         obd_id oid;
         int rc = 0;
         ENTRY;
-                                                                                                                             
+
         lsr = (struct llog_setattr_rec *)rec;
         oa = obdo_alloc();
-                                                                                                                             
+
         oa->o_valid |= (OBD_MD_FLID | OBD_MD_FLUID | OBD_MD_FLGID |
                         OBD_MD_FLCOOKIE);
         oa->o_id = lsr->lsr_oid;
@@ -178,7 +178,7 @@ static int filter_recov_log_setattr_cb(struct llog_ctxt *ctxt,
         memcpy(obdo_logcookie(oa), cookie, sizeof(*cookie));
         oid = oa->o_id;
 
-        rc = obd_setattr(exp, oa, NULL, NULL);
+        rc = filter_setattr(exp, oa, NULL, NULL);
         obdo_free(oa);
 
         if (rc == -ENOENT) {
@@ -186,10 +186,10 @@ static int filter_recov_log_setattr_cb(struct llog_ctxt *ctxt,
                 llog_cancel(ctxt, NULL, 1, cookie, 0);
                 RETURN(0);
         }
+
         if (rc == 0)
                 CDEBUG(D_HA, "object: "LPU64" in record is chown/chgrp\n", oid);
-                                                                                                                             
+
         RETURN(rc);
 }
 
index c8a9d9b..e0ff9eb 100644 (file)
@@ -100,7 +100,7 @@ static int filter_lvbo_update(struct ldlm_resource *res, struct lustre_msg *m,
                               int buf_idx, int increase)
 {
         int rc = 0;
-        struct ost_lvb *lvb = res->lr_lvb_data;
+        struct ost_lvb *lvb;
         struct obd_device *obd;
         struct dentry *dentry;
         ENTRY;
@@ -113,6 +113,7 @@ static int filter_lvbo_update(struct ldlm_resource *res, struct lustre_msg *m,
                 RETURN(0);
 
         down(&res->lr_lvb_sem);
+        lvb = res->lr_lvb_data;
         if (lvb == NULL) {
                 CERROR("No lvb when running lvbo_update!\n");
                 GOTO(out, rc = 0);
index 4b1e14d..0340d6b 100644 (file)
 int filter_san_setup(struct obd_device *obd, obd_count len, void *buf)
 {
         struct lustre_cfg* lcfg = buf;
-        char *option = NULL;
+        unsigned long page;
+        int rc;
 
         if (lcfg->lcfg_bufcount < 3 || LUSTRE_CFG_BUFLEN(lcfg, 2) < 1)
                 RETURN(-EINVAL);
 
+        /* 2.6.9 selinux wants a full option page for do_kern_mount (bug6471) */
+        page = get_zeroed_page(GFP_KERNEL);
+        if (!page)
+                RETURN(-ENOMEM);
+
         /* for extN/ext3 filesystem, we must mount it with 'writeback' mode */
         if (!strcmp(lustre_cfg_string(lcfg, 2), "ldiskfs"))
-                option = "data=writeback";
+                strcpy((void *)page, "data=writeback");
         else if (!strcmp(lustre_cfg_string(lcfg, 2), "ext3"))
-                option = "data=writeback,asyncdel";
+                strcpy((void *)page, "data=writeback,asyncdel");
         else
                 LBUG(); /* just a reminder */
 
-        return filter_common_setup(obd, len, buf, option);
+        rc = filter_common_setup(obd, len, buf, (void *)page);
+        free_page(page);
+
+        return rc;
 }
 
 int filter_san_preprw(int cmd, struct obd_export *exp, struct obdo *oa,
index 61d0fb8..ab7e4c5 100644 (file)
 
 #include "filter_internal.h"
 
-#ifndef LPROCFS
-static struct lprocfs_vars lprocfs_obd_vars[]  = { {0} };
-static struct lprocfs_vars lprocfs_module_vars[] = { {0} };
-#else
-
+#ifdef LPROCFS
 static int lprocfs_filter_rd_groups(char *page, char **start, off_t off,
                                     int count, int *eof, void *data)
 {
@@ -121,121 +117,6 @@ int lprocfs_filter_wr_readcache(struct file *file, const char *buffer,
         return count;
 }
 
-static int lprocfs_filter_rd_bunit(char *page, char **start, off_t off, int count, 
-                                   int *eof, void *data)
-{
-        struct obd_device *obd = (struct obd_device *)data;
-        LASSERT(obd != NULL);
-
-        return snprintf(page, count, "%lu\n", 
-                        obd->u.filter.fo_quota_ctxt.lqc_bunit_sz);
-}
-
-static int lprocfs_filter_rd_iunit(char *page, char **start, off_t off, int count, 
-                                   int *eof, void *data)
-{
-        struct obd_device *obd = (struct obd_device *)data;
-        LASSERT(obd != NULL);
-
-        return snprintf(page, count, "%lu\n", 
-                        obd->u.filter.fo_quota_ctxt.lqc_iunit_sz);
-}
-
-static int lprocfs_filter_wr_bunit(struct file *file, const char *buffer,
-                                   unsigned long count, void *data)
-{
-        struct obd_device *obd = (struct obd_device *)data;
-        int val, rc = 0;
-        LASSERT(obd != NULL);
-
-        rc = lprocfs_write_helper(buffer, count, &val);
-        if (rc)
-                return rc;
-
-        if (val % QUOTABLOCK_SIZE ||
-            val <= obd->u.filter.fo_quota_ctxt.lqc_btune_sz)
-                return -EINVAL;
-
-        obd->u.filter.fo_quota_ctxt.lqc_bunit_sz = val;
-        return count;
-}
-
-static int lprocfs_filter_wr_iunit(struct file *file, const char *buffer,
-                                   unsigned long count, void *data)
-{
-        struct obd_device *obd = (struct obd_device *)data;
-        int val, rc = 0;
-        LASSERT(obd != NULL);
-
-        rc = lprocfs_write_helper(buffer, count, &val);
-        if (rc)
-                return rc;
-
-        if (val <= obd->u.filter.fo_quota_ctxt.lqc_itune_sz)
-                return -EINVAL;
-
-        obd->u.filter.fo_quota_ctxt.lqc_iunit_sz = val;
-        return count;
-}
-
-static int lprocfs_filter_rd_btune(char *page, char **start, off_t off, int count, 
-                                   int *eof, void *data)
-{
-        struct obd_device *obd = (struct obd_device *)data;
-        LASSERT(obd != NULL);
-
-        return snprintf(page, count, "%lu\n", 
-                        obd->u.filter.fo_quota_ctxt.lqc_btune_sz);
-}
-
-static int lprocfs_filter_rd_itune(char *page, char **start, off_t off, int count, 
-                                   int *eof, void *data)
-{
-        struct obd_device *obd = (struct obd_device *)data;
-        LASSERT(obd != NULL);
-
-        return snprintf(page, count, "%lu\n", 
-                        obd->u.filter.fo_quota_ctxt.lqc_itune_sz);
-}
-
-static int lprocfs_filter_wr_btune(struct file *file, const char *buffer,
-                                   unsigned long count, void *data)
-{
-        struct obd_device *obd = (struct obd_device *)data;
-        int val, rc = 0;
-        LASSERT(obd != NULL);
-
-        rc = lprocfs_write_helper(buffer, count, &val);
-        if (rc)
-                return rc;
-        
-        if (val <= QUOTABLOCK_SIZE * MIN_QLIMIT || val % QUOTABLOCK_SIZE || 
-            val >= obd->u.filter.fo_quota_ctxt.lqc_bunit_sz)
-                return -EINVAL;
-
-        obd->u.filter.fo_quota_ctxt.lqc_btune_sz = val;
-        return count;
-}
-
-static int lprocfs_filter_wr_itune(struct file *file, const char *buffer,
-                                   unsigned long count, void *data)
-{
-        struct obd_device *obd = (struct obd_device *)data;
-        int val, rc = 0;
-        LASSERT(obd != NULL);
-
-        rc = lprocfs_write_helper(buffer, count, &val);
-        if (rc)
-                return rc;
-        
-        if (val <= MIN_QLIMIT || 
-            val >= obd->u.filter.fo_quota_ctxt.lqc_iunit_sz)
-                return -EINVAL;
-
-        obd->u.filter.fo_quota_ctxt.lqc_itune_sz = val;
-        return count;
-}
-
 static struct lprocfs_vars lprocfs_obd_vars[] = {
         { "uuid",         lprocfs_rd_uuid,          0, 0 },
         { "blocksize",    lprocfs_rd_blksize,       0, 0 },
@@ -257,14 +138,16 @@ static struct lprocfs_vars lprocfs_obd_vars[] = {
         { "readcache_max_filesize",
                           lprocfs_filter_rd_readcache,
                           lprocfs_filter_wr_readcache, 0 },
-        { "quota_bunit_sz", lprocfs_filter_rd_bunit, 
+#ifdef HAVE_QUOTA_SUPPORT
+        { "quota_bunit_sz", lprocfs_filter_rd_bunit,
                             lprocfs_filter_wr_bunit, 0},
         { "quota_btune_sz", lprocfs_filter_rd_btune,
                             lprocfs_filter_wr_btune, 0},
-        { "quota_iunit_sz", lprocfs_filter_rd_iunit, 
+        { "quota_iunit_sz", lprocfs_filter_rd_iunit,
                             lprocfs_filter_wr_iunit, 0},
         { "quota_itune_sz", lprocfs_filter_rd_itune,
                             lprocfs_filter_wr_itune, 0},
+#endif
 
         { 0 }
 };
@@ -369,7 +252,7 @@ static int filter_brw_stats_seq_show(struct seq_file *seq, void *v)
                 unsigned long w = filter->fo_w_pages.oh_buckets[i];
                 read_cum += r;
                 write_cum += w;
-                seq_printf(seq, "%d:\t\t%10lu %3lu %3lu   | %10lu %3lu %3lu\n",
+                seq_printf(seq, "%u:\t\t%10lu %3lu %3lu   | %10lu %3lu %3lu\n",
                                  1 << i, r, pct(r, read_tot),
                                  pct(read_cum, read_tot), w,
                                  pct(w, write_tot),
@@ -393,7 +276,7 @@ static int filter_brw_stats_seq_show(struct seq_file *seq, void *v)
                 unsigned long w = filter->fo_w_discont_pages.oh_buckets[i];
                 read_cum += r;
                 write_cum += w;
-                seq_printf(seq, "%d:\t\t%10lu %3lu %3lu   | %10lu %3lu %3lu\n",
+                seq_printf(seq, "%u:\t\t%10lu %3lu %3lu   | %10lu %3lu %3lu\n",
                                  i, r, pct(r, read_tot),
                                  pct(read_cum, read_tot), w,
                                  pct(w, write_tot),
@@ -416,7 +299,7 @@ static int filter_brw_stats_seq_show(struct seq_file *seq, void *v)
                 unsigned long w = filter->fo_w_discont_blocks.oh_buckets[i];
                 read_cum += r;
                 write_cum += w;
-                seq_printf(seq, "%d:\t\t%10lu %3lu %3lu   | %10lu %3lu %3lu\n",
+                seq_printf(seq, "%u:\t\t%10lu %3lu %3lu   | %10lu %3lu %3lu\n",
                                  i, r, pct(r, read_tot),
                                  pct(read_cum, read_tot), w,
                                  pct(w, write_tot),
@@ -440,7 +323,7 @@ static int filter_brw_stats_seq_show(struct seq_file *seq, void *v)
                 unsigned long w = filter->fo_write_rpc_hist.oh_buckets[i];
                 read_cum += r;
                 write_cum += w;
-                seq_printf(seq, "%d:\t\t%10lu %3lu %3lu   | %10lu %3lu %3lu\n",
+                seq_printf(seq, "%u:\t%10lu %3lu %3lu   | %10lu %3lu %3lu\n",
                                  i, r, pct(r, read_tot),
                                  pct(read_cum, read_tot), w,
                                  pct(w, write_tot),
@@ -463,7 +346,7 @@ static int filter_brw_stats_seq_show(struct seq_file *seq, void *v)
                 unsigned long w = filter->fo_w_io_time.oh_buckets[i];
                 read_cum += r;
                 write_cum += w;
-                seq_printf(seq, "%d:\t\t%10lu %3lu %3lu   | %10lu %3lu %3lu\n",
+                seq_printf(seq, "%10u:\t%10lu %3lu %3lu   | %10lu %3lu %3lu\n",
                                  1 << i, r, pct(r, read_tot),
                                  pct(read_cum, read_tot), w,
                                  pct(w, write_tot),
@@ -487,15 +370,18 @@ static int filter_brw_stats_seq_show(struct seq_file *seq, void *v)
 
                 read_cum += r;
                 write_cum += w;
+                if (read_cum == 0 && write_cum == 0)
+                        continue;
+
                 if (i < 10)
-                        seq_printf(seq, "%d", 1<<i);
+                        seq_printf(seq, "%u", 1<<i);
                 else if (i < 20)
-                        seq_printf(seq, "%dK", 1<<(i-10));
+                        seq_printf(seq, "%uK", 1<<(i-10));
                 else
-                        seq_printf(seq, "%dM", 1<<(i-20));
+                        seq_printf(seq, "%uM", 1<<(i-20));
 
                 seq_printf(seq, ":\t\t%10lu %3lu %3lu   | %10lu %3lu %3lu\n",
-                           r, pct(r, read_tot), pct(read_cum, read_tot), 
+                           r, pct(r, read_tot), pct(read_cum, read_tot),
                            w, pct(w, write_tot), pct(write_cum, write_tot));
                 if (read_cum == read_tot && write_cum == write_tot)
                         break;
@@ -579,7 +465,5 @@ int lproc_filter_attach_seqstat(struct obd_device *dev)
                                       &filter_brw_stats_fops, dev);
 }
 
-
-
-#endif /* LPROCFS */
 LPROCFS_INIT_VARS(filter, lprocfs_module_vars, lprocfs_obd_vars)
+#endif /* LPROCFS */
index 52fece6..568a725 100644 (file)
@@ -1,4 +1,4 @@
 MODULES := osc
-osc-objs := osc_request.o lproc_osc.o osc_lib.o osc_create.o osc_quota.o
+osc-objs := osc_request.o lproc_osc.o osc_lib.o osc_create.o
 
 @INCLUDE_RULES@
index dae803a..af0649d 100644 (file)
@@ -14,5 +14,5 @@ if MODULES
 modulefs_DATA = osc$(KMODEXT)
 endif
 
-MOSTLYCLEANFILES = *.o *.ko *.mod.c
+MOSTLYCLEANFILES := @MOSTLYCLEANFILES@ 
 DIST_SOURCES = $(osc-objs:%.o=%.c) osc_internal.h
index e02603b..3127e0b 100644 (file)
 #include <linux/seq_file.h>
 #include "osc_internal.h"
 
-#ifndef LPROCFS
-static struct lprocfs_vars lprocfs_obd_vars[]  = { {0} };
-static struct lprocfs_vars lprocfs_module_vars[] = { {0} };
-#else
-
-int osc_rd_max_pages_per_rpc(char *page, char **start, off_t off, int count,
-                             int *eof, void *data)
+#ifdef LPROCFS
+static int osc_rd_max_pages_per_rpc(char *page, char **start, off_t off,
+                                    int count, int *eof, void *data)
 {
         struct obd_device *dev = data;
         struct client_obd *cli = &dev->u.cli;
@@ -48,8 +44,8 @@ int osc_rd_max_pages_per_rpc(char *page, char **start, off_t off, int count,
         return rc;
 }
 
-int osc_wr_max_pages_per_rpc(struct file *file, const char *buffer,
-                             unsigned long count, void *data)
+static int osc_wr_max_pages_per_rpc(struct file *file, const char *buffer,
+                                    unsigned long count, void *data)
 {
         struct obd_device *dev = data;
         struct client_obd *cli = &dev->u.cli;
@@ -69,8 +65,8 @@ int osc_wr_max_pages_per_rpc(struct file *file, const char *buffer,
         return count;
 }
 
-int osc_rd_max_rpcs_in_flight(char *page, char **start, off_t off, int count,
-                              int *eof, void *data)
+static int osc_rd_max_rpcs_in_flight(char *page, char **start, off_t off,
+                                     int count, int *eof, void *data)
 {
         struct obd_device *dev = data;
         struct client_obd *cli = &dev->u.cli;
@@ -82,8 +78,8 @@ int osc_rd_max_rpcs_in_flight(char *page, char **start, off_t off, int count,
         return rc;
 }
 
-int osc_wr_max_rpcs_in_flight(struct file *file, const char *buffer,
-                              unsigned long count, void *data)
+static int osc_wr_max_rpcs_in_flight(struct file *file, const char *buffer,
+                                     unsigned long count, void *data)
 {
         struct obd_device *dev = data;
         struct client_obd *cli = &dev->u.cli;
@@ -103,8 +99,8 @@ int osc_wr_max_rpcs_in_flight(struct file *file, const char *buffer,
         return count;
 }
 
-int osc_rd_max_dirty_mb(char *page, char **start, off_t off, int count,
-                        int *eof, void *data)
+static int osc_rd_max_dirty_mb(char *page, char **start, off_t off, int count,
+                               int *eof, void *data)
 {
         struct obd_device *dev = data;
         struct client_obd *cli = &dev->u.cli;
@@ -117,8 +113,8 @@ int osc_rd_max_dirty_mb(char *page, char **start, off_t off, int count,
         return snprintf(page, count, "%u\n", val);
 }
 
-int osc_wr_max_dirty_mb(struct file *file, const char *buffer,
-                        unsigned long count, void *data)
+static int osc_wr_max_dirty_mb(struct file *file, const char *buffer,
+                               unsigned long count, void *data)
 {
         struct obd_device *dev = data;
         struct client_obd *cli = &dev->u.cli;
@@ -139,8 +135,8 @@ int osc_wr_max_dirty_mb(struct file *file, const char *buffer,
         return count;
 }
 
-int osc_rd_cur_dirty_bytes(char *page, char **start, off_t off, int count,
-                           int *eof, void *data)
+static int osc_rd_cur_dirty_bytes(char *page, char **start, off_t off,
+                                  int count, int *eof, void *data)
 {
         struct obd_device *dev = data;
         struct client_obd *cli = &dev->u.cli;
@@ -152,8 +148,8 @@ int osc_rd_cur_dirty_bytes(char *page, char **start, off_t off, int count,
         return rc;
 }
 
-int osc_rd_cur_grant_bytes(char *page, char **start, off_t off, int count,
-                           int *eof, void *data)
+static int osc_rd_cur_grant_bytes(char *page, char **start, off_t off,
+                                  int count, int *eof, void *data)
 {
         struct obd_device *dev = data;
         struct client_obd *cli = &dev->u.cli;
@@ -165,8 +161,8 @@ int osc_rd_cur_grant_bytes(char *page, char **start, off_t off, int count,
         return rc;
 }
 
-int osc_rd_create_count(char *page, char **start, off_t off, int count,
-                        int *eof, void *data)
+static int osc_rd_create_count(char *page, char **start, off_t off, int count,
+                               int *eof, void *data)
 {
         struct obd_device *obd = data;
 
@@ -177,8 +173,8 @@ int osc_rd_create_count(char *page, char **start, off_t off, int count,
                         obd->u.cli.cl_oscc.oscc_grow_count);
 }
 
-int osc_wr_create_count(struct file *file, const char *buffer,
-                        unsigned long count, void *data)
+static int osc_wr_create_count(struct file *file, const char *buffer,
+                               unsigned long count, void *data)
 {
         struct obd_device *obd = data;
         int val, rc;
@@ -200,8 +196,8 @@ int osc_wr_create_count(struct file *file, const char *buffer,
         return count;
 }
 
-int osc_rd_prealloc_next_id(char *page, char **start, off_t off, int count,
-                            int *eof, void *data)
+static int osc_rd_prealloc_next_id(char *page, char **start, off_t off,
+                                   int count, int *eof, void *data)
 {
         struct obd_device *obd = data;
 
@@ -212,8 +208,8 @@ int osc_rd_prealloc_next_id(char *page, char **start, off_t off, int count,
                         obd->u.cli.cl_oscc.oscc_next_id);
 }
 
-int osc_rd_prealloc_last_id(char *page, char **start, off_t off, int count,
-                            int *eof, void *data)
+static int osc_rd_prealloc_last_id(char *page, char **start, off_t off,
+                                   int count, int *eof, void *data)
 {
         struct obd_device *obd = data;
 
@@ -224,6 +220,36 @@ int osc_rd_prealloc_last_id(char *page, char **start, off_t off, int count,
                         obd->u.cli.cl_oscc.oscc_last_id);
 }
 
+static int osc_rd_checksum(char *page, char **start, off_t off, int count,
+                           int *eof, void *data)
+{
+        struct obd_device *obd = data;
+
+        if (obd == NULL)
+                return 0;
+
+        return snprintf(page, count, "%d\n",
+                        obd->u.cli.cl_checksum ? 1 : 0);
+}
+
+static int osc_wr_checksum(struct file *file, const char *buffer,
+                           unsigned long count, void *data)
+{
+        struct obd_device *obd = data;
+        int val, rc;
+
+        if (obd == NULL)
+                return 0;
+
+        rc = lprocfs_write_helper(buffer, count, &val);
+        if (rc)
+                return rc;
+
+        obd->u.cli.cl_checksum = (val ? 1 : 0);
+
+        return count;
+}
+
 static struct lprocfs_vars lprocfs_obd_vars[] = {
         { "uuid",            lprocfs_rd_uuid,        0, 0 },
         { "ping",            0, lprocfs_wr_ping,        0 },
@@ -246,6 +272,7 @@ static struct lprocfs_vars lprocfs_obd_vars[] = {
         { "create_count", osc_rd_create_count, osc_wr_create_count, 0 },
         { "prealloc_next_id", osc_rd_prealloc_next_id, 0, 0 },
         { "prealloc_last_id", osc_rd_prealloc_last_id, 0, 0 },
+        { "checksums", osc_rd_checksum, osc_wr_checksum, 0 },
         { 0 }
 };
 
@@ -421,5 +448,5 @@ int lproc_osc_attach_seqstat(struct obd_device *dev)
                                       &osc_rpc_stats_fops, dev);
 }
 
-#endif /* LPROCFS */
 LPROCFS_INIT_VARS(osc, lprocfs_module_vars, lprocfs_obd_vars)
+#endif /* LPROCFS */
index ededb7e..27d1e41 100644 (file)
@@ -70,6 +70,8 @@ static int osc_interpret_create(struct ptlrpc_request *req, void *data, int rc)
         }
 
         oscc = req->rq_async_args.pointer_arg[0];
+        LASSERT(oscc && (oscc->oscc_obd != LP_POISON));
+        
         spin_lock(&oscc->oscc_lock);
         oscc->oscc_flags &= ~OSCC_FLAG_CREATING;
         if (rc == -ENOSPC || rc == -EROFS) {
index 8fe19d9..60e9160 100644 (file)
@@ -32,6 +32,10 @@ struct osc_async_page {
         void                    *oap_caller_data;
 };
 
+#define OAP_FROM_COOKIE(c)                                                      \
+        (LASSERT(((struct osc_async_page *)(c))->oap_magic == OAP_MAGIC),       \
+         (struct osc_async_page *)(c))
+
 struct osc_cache_waiter {
         struct list_head        ocw_entry;
         wait_queue_head_t       ocw_waitq;
@@ -52,6 +56,8 @@ int osc_real_create(struct obd_export *exp, struct obdo *oa,
               struct lov_stripe_md **ea, struct obd_trans_info *oti);
 void oscc_init(struct obd_device *obd);
 void osc_wake_cache_waiters(struct client_obd *cli);
+
+#ifdef HAVE_QUOTA_SUPPORT
 int osc_get_quota_flag(struct client_obd *cli, unsigned int uid,
                        unsigned int gid);
 int osc_set_quota_flag(struct client_obd *cli,
@@ -60,8 +66,45 @@ int osc_set_quota_flag(struct client_obd *cli,
 int osc_qinfo_cleanup(struct client_obd *cli);
 int osc_qinfo_init(void);
 void osc_qinfo_exit(void);
+int osc_quotacheck(struct obd_export *exp, struct obd_quotactl *oqctl);
+int osc_poll_quotacheck(struct obd_export *exp, struct if_quotacheck *qchk);
+int osc_quotactl(struct obd_export *exp, struct obd_quotactl *oqctl);
+#else
+static inline int osc_get_quota_flag(struct client_obd *cli,
+                       unsigned int uid, unsigned int gid)
+{
+       return QUOTA_OK;
+}
+static inline int osc_set_quota_flag(struct client_obd *cli,
+                       unsigned int uid, unsigned int gid,
+                       obd_flag valid, obd_flag flags)
+{
+        return 0;
+}
+static inline int osc_qinfo_cleanup(struct client_obd *cli)
+{
+        return 0;
+}
+static inline int osc_qinfo_init(void)
+{
+        return 0;
+}
+static inline void osc_qinfo_exit(void) {}
+static inline int osc_quotacheck(struct obd_export *exp, struct obd_quotactl *oqctl)
+{
+        return -ENOTSUPP;
+}
+static inline int osc_poll_quotacheck(struct obd_export *exp, struct if_quotacheck *qchk)
+{
+        return -ENOTSUPP;
+}
+static inline int osc_quotactl(struct obd_export *exp, struct obd_quotactl *oqctl)
+{
+        return -ENOTSUPP;
+}
+#endif
 
-#ifdef __KERNEL__
+#ifdef LPROCFS
 int lproc_osc_attach_seqstat(struct obd_device *dev);
 #else
 static inline int lproc_osc_attach_seqstat(struct obd_device *dev) {return 0;}
index b09c2c5..60446b2 100644 (file)
@@ -3,20 +3,8 @@
  *
  *  Copyright (c) 2003 Cluster File Systems, Inc.
  *
- *   This file is part of Lustre, http://www.lustre.org.
+ *   No redistribution or use is permitted outside of Cluster File Systems, Inc.
  *
- *   Lustre is free software; you can redistribute it and/or
- *   modify it under the terms of version 2 of the GNU General Public
- *   License as published by the Free Software Foundation.
- *
- *   Lustre is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with Lustre; if not, write to the Free Software
- *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
 #ifndef EXPORT_SYMTAB
@@ -26,7 +14,6 @@
 
 #ifdef __KERNEL__
 # include <linux/module.h>
-# include <linux/obd.h>
 # include <linux/obd_ost.h>
 # include <linux/lustre_net.h>
 # include <linux/lustre_dlm.h>
 # else
 #  include <linux/locks.h>
 # endif
+#else
+# include <liblustre.h>
 #endif
 
+#include <linux/obd.h>
 #include "osc_internal.h"
 
 struct osc_quota_info {
@@ -100,20 +90,19 @@ static struct osc_quota_info *alloc_qinfo(struct client_obd *cli,
 {
         struct osc_quota_info *oqi;
         ENTRY;
-                                                                                                                             
-        OBD_SLAB_ALLOC(oqi, qinfo_cachep, SLAB_KERNEL,
-                       sizeof(*oqi));
+
+        OBD_SLAB_ALLOC(oqi, qinfo_cachep, SLAB_KERNEL, sizeof(*oqi));
         if(!oqi)
                 RETURN(NULL);
-                                                                                                                             
+
         INIT_LIST_HEAD(&oqi->oqi_hash);
         oqi->oqi_cli = cli;
         oqi->oqi_id = id;
         oqi->oqi_type = type;
-                                                                                                                             
+
         RETURN(oqi);
 }
-                                                                                                                             
+
 static void free_qinfo(struct osc_quota_info *oqi)
 {
         OBD_SLAB_FREE(oqi, qinfo_cachep, sizeof(*oqi));
@@ -242,3 +231,96 @@ void osc_qinfo_exit(void)
         LASSERTF(kmem_cache_destroy(qinfo_cachep) == 0,
                  "couldn't destroy osc quota info slab\n"); 
 }
+
+int osc_quotacheck(struct obd_export *exp, struct obd_quotactl *oqctl)
+{
+        struct client_obd *cli = &exp->exp_obd->u.cli;
+        struct ptlrpc_request *req;
+        struct obd_quotactl *body;
+        int size = sizeof(*body);
+        int rc;
+        ENTRY;
+
+        req = ptlrpc_prep_req(class_exp2cliimp(exp), OST_QUOTACHECK, 1, &size,
+                              NULL);
+        if (!req)
+                GOTO(out, rc = -ENOMEM);
+
+        body = lustre_msg_buf(req->rq_reqmsg, 0, sizeof(*body));
+        memcpy(body, oqctl, sizeof(*body));
+
+        req->rq_replen = lustre_msg_size(0, NULL);
+
+        spin_lock(&cli->cl_qchk_lock);
+        cli->cl_qchk_stat = CL_QUOTACHECKING;
+        spin_unlock(&cli->cl_qchk_lock);
+
+        rc = ptlrpc_queue_wait(req);
+        if (rc) {
+                spin_lock(&cli->cl_qchk_lock);
+                cli->cl_qchk_stat = rc;
+                spin_unlock(&cli->cl_qchk_lock);
+        }
+ out:
+        ptlrpc_req_finished(req);
+        RETURN (rc);
+}
+
+int osc_poll_quotacheck(struct obd_export *exp,
+                                  struct if_quotacheck *qchk)
+{
+        struct client_obd *cli = &exp->exp_obd->u.cli;
+        int stat;
+        ENTRY;
+                                                                                                                 
+        spin_lock(&cli->cl_qchk_lock);
+        stat = cli->cl_qchk_stat;
+        spin_unlock(&cli->cl_qchk_lock);
+                                                                                                                 
+        qchk->stat = stat;
+        if (stat == CL_QUOTACHECKING) {
+                qchk->stat = -ENODATA;
+                stat = 0;
+        } else if (qchk->stat) {
+                if (qchk->stat > CL_QUOTACHECKING)
+                        qchk->stat = stat = -EINTR;
+                                                                                                                 
+                strncpy(qchk->obd_type, "obdfilter", 10);
+                qchk->obd_uuid = cli->cl_import->imp_target_uuid;
+        }
+        RETURN(stat);
+}
+
+int osc_quotactl(struct obd_export *exp, struct obd_quotactl *oqctl)
+{
+        struct ptlrpc_request *req;
+        struct obd_quotactl *oqc;
+        int size = sizeof(*oqctl);
+        int rc;
+        ENTRY;
+
+        req = ptlrpc_prep_req(class_exp2cliimp(exp), OST_QUOTACTL, 1, &size,
+                              NULL);
+        if (!req)
+                GOTO(out, rc = -ENOMEM);
+
+        memcpy(lustre_msg_buf(req->rq_reqmsg, 0, sizeof (*oqctl)), oqctl, size);
+
+        req->rq_replen = lustre_msg_size(1, &size);
+
+        rc = ptlrpc_queue_wait(req);
+        if (!rc) {
+                oqc = lustre_swab_repbuf(req, 0, sizeof (*oqc),
+                                         lustre_swab_obd_quotactl);
+                if (oqc == NULL) {
+                        CERROR ("Can't unpack mds_body\n");
+                        GOTO(out, rc = -EPROTO);
+                }
+
+                memcpy(oqctl, oqc, sizeof(*oqctl));
+        }
+out:
+        ptlrpc_req_finished(req);
+        RETURN (rc);
+}
+
index 3cfb2be..c4687b4 100644 (file)
@@ -62,6 +62,7 @@
 #include <linux/lustre_ha.h>
 #include <linux/lprocfs_status.h>
 #include <linux/lustre_log.h>
+#include <linux/lustre_debug.h>
 #include "osc_internal.h"
 
 /* Pack OSC object metadata for disk storage (LE byte order). */
@@ -301,9 +302,9 @@ static int osc_setattr_async(struct obd_export *exp, struct obdo *oa,
 
         memcpy(&body->oa, oa, sizeof(*oa));
         request->rq_replen = lustre_msg_size(1, &size);
-        /* do mds to ost setattr asynchronouly */                                                                       
+        /* do mds to ost setattr asynchronouly */
         ptlrpcd_add_req(request);
-                                                                                                                             
+
         RETURN(rc);
 }
 
@@ -716,37 +717,29 @@ static inline int can_merge_pages(struct brw_page *p1, struct brw_page *p2)
         return (p1->off + p1->count == p2->off);
 }
 
-#if CHECKSUM_BULK
-static obd_count cksum_blocks(int nob, obd_count page_count,
-                              struct brw_page *pga)
+static obd_count osc_checksum_bulk(int nob, obd_count pg_count,
+                                   struct brw_page *pga)
 {
-        obd_count cksum = 0;
+        __u32 cksum = ~0;
 
-        LASSERT (page_count > 0);
-        while (nob > 0) {
+        LASSERT (pg_count > 0);
+        while (nob > 0 && pg_count > 0) {
                 char *ptr = kmap(pga->pg);
-                int psum, off = pga->off & ~PAGE_MASK;
+                int off = pga->off & ~PAGE_MASK;
                 int count = pga->count > nob ? nob : pga->count;
 
-                while (count > 0) {
-                        ost_checksum(&cksum, &psum, ptr + off,
-                                     count > CHECKSUM_CHUNK ?
-                                     CHECKSUM_CHUNK : count);
-                        LL_CDEBUG_PAGE(D_PAGE, pga->pg, "off %d checksum %x\n",
-                                       off, psum);
-                        off += CHECKSUM_CHUNK;
-                        count -= CHECKSUM_CHUNK;
-                }
+                cksum = crc32_le(cksum, ptr + off, count);
                 kunmap(pga->pg);
+                LL_CDEBUG_PAGE(D_PAGE, pga->pg, "off %d checksum %x\n",
+                               off, cksum);
 
                 nob -= pga->count;
-                page_count--;
+                pg_count--;
                 pga++;
         }
 
-        return (cksum);
+        return cksum;
 }
-#endif
 
 static int osc_brw_prep_request(int cmd, struct obd_import *imp,struct obdo *oa,
                                 struct lov_stripe_md *lsm, obd_count page_count,
@@ -837,14 +830,22 @@ static int osc_brw_prep_request(int cmd, struct obd_import *imp,struct obdo *oa,
 
         /* size[0] still sizeof (*body) */
         if (opc == OST_WRITE) {
-#if CHECKSUM_BULK
-                body->oa.o_valid |= OBD_MD_FLCKSUM;
-                body->oa.o_cksum = cksum_pages(requested_nob, page_count, pga);
-#endif
+                if (unlikely(cli->cl_checksum)) {
+                        body->oa.o_valid |= OBD_MD_FLCKSUM;
+                        body->oa.o_cksum = osc_checksum_bulk(requested_nob,
+                                                             page_count, pga);
+                        CDEBUG(D_PAGE, "checksum at write origin: %x\n",
+                               body->oa.o_cksum);
+                        /* save this in 'oa', too, for later checking */
+                        oa->o_valid |= OBD_MD_FLCKSUM;
+                        oa->o_cksum = body->oa.o_cksum;
+                }
                 /* 1 RC per niobuf */
                 size[1] = sizeof(__u32) * niocount;
                 req->rq_replen = lustre_msg_size(2, size);
         } else {
+                if (unlikely(cli->cl_checksum))
+                        body->oa.o_valid |= OBD_MD_FLCKSUM;
                 /* 1 RC for the whole I/O */
                 req->rq_replen = lustre_msg_size(1, size);
         }
@@ -859,13 +860,49 @@ static int osc_brw_prep_request(int cmd, struct obd_import *imp,struct obdo *oa,
         return (rc);
 }
 
+static void check_write_csum(__u32 cli, __u32 srv, int requested_nob,
+                             obd_count page_count, struct brw_page *pga)
+{
+        __u32 new_csum;
+
+        if (srv == cli) {
+                CDEBUG(D_PAGE, "checksum %x confirmed\n", cli);
+                return;
+        }
+
+        new_csum = osc_checksum_bulk(requested_nob, page_count, pga);
+
+        if (new_csum == srv) {
+                CERROR("BAD CHECKSUM (WRITE): pages were mutated on the client"
+                       "after we checksummed them (original client csum:"
+                       " %x; server csum: %x; client csum now: %x)\n",
+                       cli, srv, new_csum);
+                return;
+        }
+
+        if (new_csum == cli) {
+                CERROR("BAD CHECKSUM (WRITE): pages were mutated in transit "
+                       "(original client csum: %x; server csum: %x; client "
+                       "csum now: %x)\n", cli, srv, new_csum);
+                return;
+        }
+
+        CERROR("BAD CHECKSUM (WRITE): pages were mutated in transit, and the "
+               "current page contents don't match the originals OR what the "
+               "server received (original client csum: %x; server csum: %x; "
+               "client csum now: %x)\n", cli, srv, new_csum);
+}
+
 static int osc_brw_fini_request(struct ptlrpc_request *req, struct obdo *oa,
                                 int requested_nob, int niocount,
                                 obd_count page_count, struct brw_page *pga,
                                 int rc)
 {
+        const ptl_process_id_t *peer =
+                        &req->rq_import->imp_connection->c_peer;
         struct client_obd *cli = &req->rq_import->imp_obd->u.cli;
         struct ost_body *body;
+        __u32 client_cksum = 0;
         ENTRY;
 
         if (rc < 0 && rc != -EDQUOT)
@@ -886,6 +923,9 @@ static int osc_brw_fini_request(struct ptlrpc_request *req, struct obdo *oa,
         if (rc < 0)
                 RETURN(rc);
 
+        if (unlikely(oa->o_valid & OBD_MD_FLCKSUM))
+                client_cksum = oa->o_cksum; /* save for later */
+
         osc_update_grant(cli, body);
         memcpy(oa, &body->oa, sizeof(*oa));
 
@@ -896,10 +936,17 @@ static int osc_brw_fini_request(struct ptlrpc_request *req, struct obdo *oa,
                 }
                 LASSERT (req->rq_bulk->bd_nob == requested_nob);
 
+                if (unlikely((oa->o_valid & OBD_MD_FLCKSUM) &&
+                             client_cksum)) {
+                        check_write_csum(client_cksum, oa->o_cksum,
+                                         requested_nob, page_count, pga);
+                }
+
                 RETURN(check_write_rcs(req, requested_nob, niocount,
                                        page_count, pga));
         }
 
+        /* The rest of this function executes only for OST_READs */
         if (rc > requested_nob) {
                 CERROR("Unexpected rc %d (%d requested)\n", rc, requested_nob);
                 RETURN(-EPROTO);
@@ -914,37 +961,40 @@ static int osc_brw_fini_request(struct ptlrpc_request *req, struct obdo *oa,
         if (rc < requested_nob)
                 handle_short_read(rc, page_count, pga);
 
-#if CHECKSUM_BULK
-        if (oa->o_valid & OBD_MD_FLCKSUM) {
-                const ptl_process_id_t peer =
-                        req->rq_import->imp_connection->c_peer;
+        if (unlikely(oa->o_valid & OBD_MD_FLCKSUM)) {
                 static int cksum_counter;
-                obd_count server_cksum = oa->o_cksum;
-                obd_count cksum = cksum_pages(rc, page_count, pga);
-                char     *str = libcfs_nid2str(peer.nid);
+                __u32 cksum = osc_checksum_bulk(rc, page_count, pga);
+                __u32 server_cksum = oa->o_cksum;
+
+                if (server_cksum == ~0 && rc > 0) {
+                        CERROR("Protocol error: server %s set the 'checksum' "
+                               "bit, but didn't send a checksum.  Not fatal, "
+                               "but please tell CFS.\n",
+                               libcfs_nid2str(peer->nid));
+                        RETURN(0);
+                }
 
                 cksum_counter++;
+
                 if (server_cksum != cksum) {
-                        CERROR("Bad checksum: server %x, client %x, server NID "
-                               LPX64" (%s)\n", server_cksum, cksum,
-                               peer.nid, str);
+                        CERROR("Bad checksum from %s: server %x != client %x\n",
+                               libcfs_nid2str(peer->nid), server_cksum, cksum);
                         cksum_counter = 0;
                         oa->o_cksum = cksum;
                 } else if ((cksum_counter & (-cksum_counter)) == cksum_counter){
-                        CWARN("Checksum %u from "LPX64" (%s) OK: %x\n",
-                              cksum_counter, peer.nid, str, cksum);
+                        CWARN("Checksum %u from %s OK: %x\n",
+                              cksum_counter, libcfs_nid2str(peer->nid), cksum);
                 }
-                CDEBUG(D_PAGE, "checksum %x\n", cksum);
-        } else {
+                CDEBUG(D_PAGE, "checksum %x confirmed\n", cksum);
+        } else if (unlikely(client_cksum)) {
                 static int cksum_missed;
 
                 cksum_missed++;
                 if ((cksum_missed & (-cksum_missed)) == cksum_missed)
-                        CERROR("Request checksum %u from "LPX64", no reply\n",
-                               cksum_missed,
-                               req->rq_import->imp_connection->c_peer.nid);
+                        CERROR("Checksum %u requested from %s but not sent\n",
+                               cksum_missed, libcfs_nid2str(peer->nid));
         }
-#endif
+
         RETURN(0);
 }
 
@@ -1380,10 +1430,10 @@ static struct ptlrpc_request *osc_build_req(struct client_obd *cli,
          * at present. And we chose one page's user credit information as
          * the whole rpc's credit information. FIXME */
         if (cmd == OBD_BRW_WRITE) {
-                struct obd_ucred ouc;
+                struct lvfs_ucred ouc;
                 ops->ap_get_ucred(caller_data, &ouc);
-                oa->o_uid = ouc.ouc_fsuid;
-                oa->o_gid = ouc.ouc_fsgid;
+                oa->o_uid = ouc.luc_fsuid;
+                oa->o_gid = ouc.luc_fsgid;
         }
 
         sort_brw_pages(pga, page_count);
@@ -1583,7 +1633,7 @@ static int osc_send_oap_rpc(struct client_obd *cli, struct lov_oinfo *loi,
         list_splice(&rpc_list, &aa->aa_oaps);
         INIT_LIST_HEAD(&rpc_list);
 
-#ifdef __KERNEL__
+#ifdef LPROCFS
         if (cmd == OBD_BRW_READ) {
                 lprocfs_oh_tally_log2(&cli->cl_read_page_hist, page_count);
                 lprocfs_oh_tally(&cli->cl_read_rpc_hist, cli->cl_r_in_flight);
@@ -2220,9 +2270,8 @@ out:
         RETURN(rc);
 }
 
-#ifdef __KERNEL__
 /* Note: caller will lock/unlock, and set uptodate on the pages */
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
+#if defined(__KERNEL__) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
 static int sanosc_brw_read(struct obd_export *exp, struct obdo *oa,
                            struct lov_stripe_md *lsm, obd_count page_count,
                            struct brw_page *pga)
@@ -2495,7 +2544,6 @@ static int sanosc_brw(int cmd, struct obd_export *exp, struct obdo *oa,
         RETURN(0);
 }
 #endif
-#endif
 
 static void osc_set_data_with_check(struct lustre_handle *lockh, void *data,
                                     int flags)
@@ -2686,7 +2734,10 @@ static int osc_cancel(struct obd_export *exp, struct lov_stripe_md *md,
 {
         ENTRY;
 
-        ldlm_lock_decref(lockh, mode);
+        if (mode == LCK_GROUP)
+                ldlm_lock_decref_and_cancel(lockh, mode);
+        else
+                ldlm_lock_decref(lockh, mode);
 
         RETURN(0);
 }
@@ -2796,98 +2847,6 @@ static int osc_getstripe(struct lov_stripe_md *lsm, struct lov_user_md *lump)
         RETURN(rc);
 }
 
-static int osc_quotacheck(struct obd_export *exp, struct obd_quotactl *oqctl)
-{
-        struct client_obd *cli = &exp->exp_obd->u.cli;
-        struct ptlrpc_request *req;
-        struct obd_quotactl *body;
-        int size = sizeof(*body);
-        int rc;
-        ENTRY;
-
-        req = ptlrpc_prep_req(class_exp2cliimp(exp), OST_QUOTACHECK, 1, &size,
-                              NULL);
-        if (!req)
-                GOTO(out, rc = -ENOMEM);
-
-        body = lustre_msg_buf(req->rq_reqmsg, 0, sizeof(*body));
-        memcpy(body, oqctl, sizeof(*body));
-
-        req->rq_replen = lustre_msg_size(0, NULL);
-
-        spin_lock(&cli->cl_qchk_lock);
-        cli->cl_qchk_stat = CL_QUOTACHECKING;
-        spin_unlock(&cli->cl_qchk_lock);
-
-        rc = ptlrpc_queue_wait(req);
-        if (rc) {
-                spin_lock(&cli->cl_qchk_lock);
-                cli->cl_qchk_stat = rc;
-                spin_unlock(&cli->cl_qchk_lock);
-        }
- out:
-        ptlrpc_req_finished(req);
-        RETURN (rc);
-}
-
-static int osc_poll_quotacheck(struct obd_export *exp,
-                                  struct if_quotacheck *qchk)
-{
-        struct client_obd *cli = &exp->exp_obd->u.cli;
-        int stat;
-        ENTRY;
-                                                                                                                 
-        spin_lock(&cli->cl_qchk_lock);
-        stat = cli->cl_qchk_stat;
-        spin_unlock(&cli->cl_qchk_lock);
-                                                                                                                 
-        qchk->stat = stat;
-        if (stat == CL_QUOTACHECKING) {
-                qchk->stat = -ENODATA;
-                stat = 0;
-        } else if (qchk->stat) {
-                if (qchk->stat > CL_QUOTACHECKING)
-                        qchk->stat = stat = -EINTR;
-                                                                                                                 
-                strncpy(qchk->obd_type, "obdfilter", 10);
-                qchk->obd_uuid = cli->cl_import->imp_target_uuid;
-        }
-        RETURN(stat);
-}
-
-static int osc_quotactl(struct obd_export *exp, struct obd_quotactl *oqctl)
-{
-        struct ptlrpc_request *req;
-        struct obd_quotactl *oqc;
-        int size = sizeof(*oqctl);
-        int rc;
-        ENTRY;
-
-        req = ptlrpc_prep_req(class_exp2cliimp(exp), OST_QUOTACTL, 1, &size,
-                              NULL);
-        if (!req)
-                GOTO(out, rc = -ENOMEM);
-
-        memcpy(lustre_msg_buf(req->rq_reqmsg, 0, sizeof (*oqctl)), oqctl, size);
-
-        req->rq_replen = lustre_msg_size(1, &size);
-
-        rc = ptlrpc_queue_wait(req);
-        if (!rc) {
-                oqc = lustre_swab_repbuf(req, 0, sizeof (*oqc),
-                                         lustre_swab_obd_quotactl);
-                if (oqc == NULL) {
-                        CERROR ("Can't unpack mds_body\n");
-                        GOTO(out, rc = -EPROTO);
-                }
-
-                memcpy(oqctl, oqc, sizeof(*oqctl));
-        }
-out:
-        ptlrpc_req_finished(req);
-        RETURN (rc);
-}
-
 static int osc_iocontrol(unsigned int cmd, struct obd_export *exp, int len,
                          void *karg, void *uarg)
 {
@@ -2918,12 +2877,12 @@ static int osc_iocontrol(unsigned int cmd, struct obd_export *exp, int len,
                 data = (struct obd_ioctl_data *)buf;
 
                 if (sizeof(*desc) > data->ioc_inllen1) {
-                        OBD_FREE(buf, len);
+                        obd_ioctl_freedata(buf, len);
                         GOTO(out, err = -EINVAL);
                 }
 
                 if (data->ioc_inllen2 < sizeof(uuid)) {
-                        OBD_FREE(buf, len);
+                        obd_ioctl_freedata(buf, len);
                         GOTO(out, err = -EINVAL);
                 }
 
@@ -3031,8 +2990,9 @@ static int osc_set_info(struct obd_export *exp, obd_count keylen,
         char *bufs[1] = {key};
         ENTRY;
 
-        if (keylen == strlen("next_id") &&
-            memcmp(key, "next_id", strlen("next_id")) == 0) {
+        OBD_FAIL_TIMEOUT(OBD_FAIL_OSC_SHUTDOWN, 10);
+
+        if (KEY_IS("next_id")) {
                 if (vallen != sizeof(obd_id))
                         RETURN(-EINVAL);
                 obd->u.cli.cl_oscc.oscc_next_id = *((obd_id*)val) + 1;
@@ -3043,16 +3003,7 @@ static int osc_set_info(struct obd_export *exp, obd_count keylen,
                 RETURN(0);
         }
 
-        if (keylen == strlen("growth_count") &&
-            memcmp(key, "growth_count", strlen("growth_count")) == 0) {
-                if (vallen != sizeof(int))
-                        RETURN(-EINVAL);
-                obd->u.cli.cl_oscc.oscc_grow_count = *((int*)val);
-                RETURN(0);
-        }
-
-        if (keylen == strlen("unlinked") &&
-            memcmp(key, "unlinked", keylen) == 0) {
+        if (KEY_IS("unlinked")) {
                 struct osc_creator *oscc = &obd->u.cli.cl_oscc;
                 spin_lock(&oscc->oscc_lock);
                 oscc->oscc_flags &= ~OSCC_FLAG_NOSPC;
@@ -3061,8 +3012,7 @@ static int osc_set_info(struct obd_export *exp, obd_count keylen,
         }
 
 
-        if (keylen == strlen("initial_recov") &&
-            memcmp(key, "initial_recov", strlen("initial_recov")) == 0) {
+        if (KEY_IS("initial_recov")) {
                 struct obd_import *imp = exp->exp_obd->u.cli.cl_import;
                 if (vallen != sizeof(int))
                         RETURN(-EINVAL);
@@ -3073,8 +3023,14 @@ static int osc_set_info(struct obd_export *exp, obd_count keylen,
                 RETURN(0);
         }
 
-        if (keylen < strlen("mds_conn") ||
-            memcmp(key, "mds_conn", strlen("mds_conn")) != 0)
+        if (KEY_IS("checksum")) {
+                if (vallen != sizeof(int))
+                        RETURN(-EINVAL);
+                exp->exp_obd->u.cli.cl_checksum = (*(int *)val) ? 1 : 0;
+                RETURN(0);
+        }
+
+        if (!KEY_IS("mds_conn"))
                 RETURN(-EINVAL);
 
 
@@ -3252,6 +3208,21 @@ int osc_setup(struct obd_device *obd, obd_count len, void *buf)
         RETURN(rc);
 }
 
+static int osc_precleanup(struct obd_device *obd, int stage)
+{
+        int rc = 0;
+        ENTRY;
+
+        if (stage < 2) 
+                RETURN(0);
+
+        rc = obd_llog_finish(obd, 0);
+        if (rc != 0)
+                CERROR("failed to cleanup llogging subsystems\n");
+
+        RETURN(rc);
+}
+
 int osc_cleanup(struct obd_device *obd)
 {
         struct osc_creator *oscc = &obd->u.cli.cl_oscc;
@@ -3271,7 +3242,6 @@ int osc_cleanup(struct obd_device *obd)
 
         rc = client_obd_cleanup(obd);
         ptlrpcd_decref();
-        obd_llog_finish(obd, 0);
         RETURN(rc);
 }
 
@@ -3279,6 +3249,7 @@ int osc_cleanup(struct obd_device *obd)
 struct obd_ops osc_obd_ops = {
         .o_owner                = THIS_MODULE,
         .o_setup                = osc_setup,
+        .o_precleanup           = osc_precleanup,
         .o_cleanup              = osc_cleanup,
         .o_add_conn             = client_import_add_conn,
         .o_del_conn             = client_import_del_conn,
@@ -3319,7 +3290,7 @@ struct obd_ops osc_obd_ops = {
         .o_quotactl             = osc_quotactl,
 };
 
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
+#if defined(__KERNEL__) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
 struct obd_ops sanosc_obd_ops = {
         .o_owner                = THIS_MODULE,
         .o_cleanup              = client_obd_cleanup,
@@ -3355,14 +3326,14 @@ struct obd_ops sanosc_obd_ops = {
 int __init osc_init(void)
 {
         struct lprocfs_static_vars lvars;
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
+#if defined(__KERNEL__) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
         struct lprocfs_static_vars sanlvars;
 #endif
         int rc;
         ENTRY;
 
         lprocfs_init_vars(osc, &lvars);
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
+#if defined(__KERNEL__) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
         lprocfs_init_vars(osc, &sanlvars);
 #endif
 
@@ -3371,13 +3342,13 @@ int __init osc_init(void)
         if (rc)
                 RETURN(rc);
 
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
+#if defined(__KERNEL__) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
         rc = class_register_type(&sanosc_obd_ops, sanlvars.module_vars,
                                  LUSTRE_SANOSC_NAME);
         if (rc)
                 class_unregister_type(LUSTRE_OSC_NAME);
 #endif
-        
+
         rc = osc_qinfo_init();
 
         RETURN(rc);
@@ -3387,7 +3358,7 @@ int __init osc_init(void)
 static void /*__exit*/ osc_exit(void)
 {
         osc_qinfo_exit();
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
+#if defined(__KERNEL__) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
         class_unregister_type(LUSTRE_SANOSC_NAME);
 #endif
         class_unregister_type(LUSTRE_OSC_NAME);
index 72ef6bb..f178425 100644 (file)
@@ -7,5 +7,5 @@ if MODULES
 modulefs_DATA = ost$(KMODEXT)
 endif
 
-MOSTLYCLEANFILES = *.o *.ko *.mod.c
+MOSTLYCLEANFILES := @MOSTLYCLEANFILES@ 
 DIST_SOURCES = $(ost-objs:%.o=%.c) ost_internal.h
index 4ceec0d..bf64ba2 100644 (file)
 #include <linux/seq_file.h>
 #include "ost_internal.h"
 
-#ifndef LPROCFS
-static struct lprocfs_vars lprocfs_obd_vars[]  = { {0} };
-static struct lprocfs_vars lprocfs_module_vars[] = { {0} };
-#else
+#ifdef LPROCFS
 static struct lprocfs_vars lprocfs_obd_vars[] = {
         { "uuid",            lprocfs_rd_uuid,   0, 0 },
         { 0 }
@@ -74,5 +71,5 @@ ost_print_req(void *seq_file, struct ptlrpc_request *req)
         }
 }
 
-#endif /* LPROCFS */
 LPROCFS_INIT_VARS(ost, lprocfs_module_vars, lprocfs_obd_vars)
+#endif /* LPROCFS */
index 2ce8789..c04715d 100644 (file)
@@ -48,8 +48,6 @@
 #include <linux/lustre_quota.h>
 #include "ost_internal.h"
 
-static struct quotacheck_info qchkinfo;
-
 void oti_init(struct obd_trans_info *oti, struct ptlrpc_request *req)
 {
         if (oti == NULL)
@@ -343,33 +341,31 @@ static void free_per_page_niobufs (int npages, struct niobuf_remote *pp_rnb,
         OBD_FREE(pp_rnb, sizeof(*pp_rnb) * npages);
 }
 
-#if CHECKSUM_BULK
-obd_count ost_checksum_bulk(struct ptlrpc_bulk_desc *desc)
+static __u32 ost_checksum_bulk(struct ptlrpc_bulk_desc *desc)
 {
-        obd_count cksum = 0;
+        __u32 cksum = ~0;
         int i;
 
         for (i = 0; i < desc->bd_iov_count; i++) {
+#ifdef CRAY_PORTALS
+                char *ptr = desc->bd_iov[i].iov_base;
+                int len = desc->bd_iov[i].iov_len;
+#else
                 struct page *page = desc->bd_iov[i].kiov_page;
-                char *ptr = kmap(page);
-                int psum, off = desc->bd_iov[i].kiov_offset & ~PAGE_MASK;
-                int count = desc->bd_iov[i].kiov_len;
-
-                while (count > 0) {
-                        ost_checksum(&cksum, &psum, ptr + off,
-                                     count > CHECKSUM_CHUNK ?
-                                     CHECKSUM_CHUNK : count);
-                        LL_CDEBUG_PAGE(D_PAGE, page, "off %d checksum %x\n",
-                                       off, psum);
-                        off += CHECKSUM_CHUNK;
-                        count -= CHECKSUM_CHUNK;
-                }
+                int off = desc->bd_iov[i].kiov_offset & ~PAGE_MASK;
+                char *ptr = kmap(page) + off;
+                int len = desc->bd_iov[i].kiov_len;
+#endif
+
+                cksum = crc32_le(cksum, ptr, len);
+#ifndef CRAY_PORTALS
                 kunmap(page);
+                LL_CDEBUG_PAGE(D_PAGE, page, "off %d checksum %x\n", off,cksum);
+#endif
         }
 
         return cksum;
 }
-#endif
 
 static int ost_brw_read(struct ptlrpc_request *req, struct obd_trans_info *oti)
 {
@@ -386,7 +382,7 @@ static int ost_brw_read(struct ptlrpc_request *req, struct obd_trans_info *oti)
         int                      npages;
         int                      nob = 0;
         int                      rc;
-        int                      i;
+        int                      i, do_checksum;
         ENTRY;
 
         if (OBD_FAIL_CHECK(OBD_FAIL_OST_BRW_READ_BULK))
@@ -408,6 +404,12 @@ static int ost_brw_read(struct ptlrpc_request *req, struct obd_trans_info *oti)
         }
 
         niocount = ioo->ioo_bufcnt;
+        if (niocount > PTLRPC_MAX_BRW_PAGES) {
+                DEBUG_REQ(D_ERROR, req, "bulk has too many pages (%d)\n",
+                          niocount);
+                GOTO(out, rc = -EFAULT);
+        }
+
         remote_nb = lustre_swab_reqbuf(req, 2, niocount * sizeof(*remote_nb),
                                        lustre_swab_niobuf_remote);
         if (remote_nb == NULL) {
@@ -444,6 +446,7 @@ static int ost_brw_read(struct ptlrpc_request *req, struct obd_trans_info *oti)
                 GOTO(out_bulk, rc);
 
         /* We're finishing using body->oa as an input variable */
+        do_checksum = (body->oa.o_valid & OBD_MD_FLCKSUM);
         body->oa.o_valid = 0;
 
         nob = 0;
@@ -507,10 +510,12 @@ static int ost_brw_read(struct ptlrpc_request *req, struct obd_trans_info *oti)
                 repbody = lustre_msg_buf(req->rq_repmsg, 0, sizeof(*repbody));
                 memcpy(&repbody->oa, &body->oa, sizeof(repbody->oa));
 
-#if CHECKSUM_BULK
-                repbody->oa.o_cksum = ost_checksum_bulk(desc);
-                repbody->oa.o_valid |= OBD_MD_FLCKSUM;
-#endif
+                if (unlikely(do_checksum)) {
+                        repbody->oa.o_cksum = ost_checksum_bulk(desc);
+                        repbody->oa.o_valid |= OBD_MD_FLCKSUM;
+                        CDEBUG(D_PAGE, "checksum at read origin: %x\n",
+                               repbody->oa.o_cksum);
+                }
         }
 
  out_bulk:
@@ -542,7 +547,7 @@ static int ost_brw_read(struct ptlrpc_request *req, struct obd_trans_info *oti)
                                req->rq_export->exp_client_uuid.uuid,
                                req->rq_export->exp_connection->c_remote_uuid.uuid,
                                libcfs_id2str(req->rq_peer));
-                        ptlrpc_fail_export(req->rq_export);
+                        class_fail_export(req->rq_export);
                 } else {
                         CERROR("ignoring bulk IO comms error: "
                                "client reconnected %s@%s id %s\n",
@@ -568,7 +573,7 @@ static int ost_brw_write(struct ptlrpc_request *req, struct obd_trans_info *oti)
         int                      size[2] = { sizeof(*body) };
         int                      objcount, niocount, npages;
         int                      comms_error = 0;
-        int                      rc, swab, i, j;
+        int                      rc, swab, i, j, do_checksum;
         ENTRY;
 
         if (OBD_FAIL_CHECK(OBD_FAIL_OST_BRW_WRITE_BULK))
@@ -591,6 +596,11 @@ static int ost_brw_write(struct ptlrpc_request *req, struct obd_trans_info *oti)
                 CERROR("Missing/short ioobj\n");
                 GOTO(out, rc = -EFAULT);
         }
+        if (objcount > 1) {
+                CERROR("too many ioobjs (%d)\n", objcount);
+                GOTO(out, rc = -EFAULT);
+        }
+
         ioo = lustre_msg_buf (req->rq_reqmsg, 1, objcount * sizeof(*ioo));
         LASSERT (ioo != NULL);
         for (niocount = i = 0; i < objcount; i++) {
@@ -603,6 +613,12 @@ static int ost_brw_write(struct ptlrpc_request *req, struct obd_trans_info *oti)
                 niocount += ioo[i].ioo_bufcnt;
         }
 
+        if (niocount > PTLRPC_MAX_BRW_PAGES) {
+                DEBUG_REQ(D_ERROR, req, "bulk has too many pages (%d)\n",
+                          niocount);
+                GOTO(out, rc = -EFAULT);
+        }
+
         remote_nb = lustre_swab_reqbuf(req, 2, niocount * sizeof(*remote_nb),
                                        lustre_swab_niobuf_remote);
         if (remote_nb == NULL) {
@@ -635,6 +651,9 @@ static int ost_brw_write(struct ptlrpc_request *req, struct obd_trans_info *oti)
         if (desc == NULL)
                 GOTO(out_local, rc = -ENOMEM);
 
+        /* obd_preprw clobbers oa->valid, so save what we need */
+        do_checksum = (body->oa.o_valid & OBD_MD_FLCKSUM);
+
         rc = obd_preprw(OBD_BRW_WRITE, req->rq_export, &body->oa, objcount,
                         ioo, npages, pp_rnb, local_nb, oti);
         if (rc != 0)
@@ -674,18 +693,23 @@ static int ost_brw_write(struct ptlrpc_request *req, struct obd_trans_info *oti)
         repbody = lustre_msg_buf(req->rq_repmsg, 0, sizeof(*repbody));
         memcpy(&repbody->oa, &body->oa, sizeof(repbody->oa));
 
-#if CHECKSUM_BULK
-        if (rc == 0 && (body->oa.o_valid & OBD_MD_FLCKSUM) != 0) {
+        if (unlikely(do_checksum && rc == 0)) {
                 static int cksum_counter;
                 obd_count client_cksum = body->oa.o_cksum;
                 obd_count cksum = ost_checksum_bulk(desc);
 
+                cksum_counter++;
                 if (client_cksum != cksum) {
                         CERROR("Bad checksum: client %x, server %x id %s\n",
                                client_cksum, cksum,
                                libcfs_id2str(req->rq_peer));
-                        cksum_counter = 1;
+                        cksum_counter = 0;
                         repbody->oa.o_cksum = cksum;
+                        repbody->oa.o_valid |= OBD_MD_FLCKSUM;
+                } else if ((cksum_counter & (-cksum_counter)) ==
+                           cksum_counter) {
+                        CWARN("Checksum %u from %s: %x OK\n", cksum_counter, 
+                              libcfs_id2str(req->rq_peer), cksum);
                 } else {
                         cksum_counter++;
                         if ((cksum_counter & (-cksum_counter)) == cksum_counter)
@@ -694,7 +718,7 @@ static int ost_brw_write(struct ptlrpc_request *req, struct obd_trans_info *oti)
                                       libcfs_id2str(req->rq_peer), cksum);
                 }
         }
-#endif
+
         /* Must commit after prep above in all cases */
         rc = obd_commitrw(OBD_BRW_WRITE, req->rq_export, &repbody->oa,
                            objcount, ioo, npages, local_nb, oti, rc);
@@ -745,7 +769,7 @@ static int ost_brw_write(struct ptlrpc_request *req, struct obd_trans_info *oti)
                                req->rq_export->exp_client_uuid.uuid,
                                req->rq_export->exp_connection->c_remote_uuid.uuid,
                                libcfs_id2str(req->rq_peer));
-                        ptlrpc_fail_export(req->rq_export);
+                        class_fail_export(req->rq_export);
                 } else {
                         CERROR("ignoring bulk IO comms error: "
                                "client reconnected %s@%s id %s\n",
@@ -915,131 +939,6 @@ static int ost_filter_recovery_request(struct ptlrpc_request *req,
         }
 }
 
-static int ost_quotacheck_callback(struct obd_export *exp,
-                                   struct obd_quotactl *oqctl)
-{
-        struct ptlrpc_request *req;
-        struct obd_quotactl *body;
-        int rc, size = sizeof(*oqctl);
-
-        req = ptlrpc_prep_req(exp->exp_imp_reverse, OBD_QC_CALLBACK,
-                              1, &size, NULL);
-        if (!req)
-                RETURN(-ENOMEM);
-
-        body = lustre_msg_buf(req->rq_reqmsg, 0, sizeof(*body));
-        memcpy(body, oqctl, sizeof(*oqctl));
-
-        req->rq_replen = lustre_msg_size(0, NULL);
-
-        rc = ptlrpc_queue_wait(req);
-        ptlrpc_req_finished(req);
-
-        RETURN(rc);
-}
-
-static int ost_quotacheck_thread(void *data)
-{
-        unsigned long flags;
-        struct quotacheck_info *qchki = data;
-        struct obd_export *exp;
-        struct obd_quotactl *oqctl;
-        struct filter_obd *filter;
-        int rc;
-                                                                                                                 
-        lock_kernel();
-        ptlrpc_daemonize();
-                                                                                                                 
-        SIGNAL_MASK_LOCK(current, flags);
-        sigfillset(&current->blocked);
-        RECALC_SIGPENDING;
-        SIGNAL_MASK_UNLOCK(current, flags);
-
-        THREAD_NAME(current->comm, sizeof(current->comm) - 1, "%s", "quotacheck");
-        unlock_kernel();
-
-        complete(&qchki->qi_starting);
-
-        exp = qchki->qi_exp;
-        filter = &exp->exp_obd->u.filter;
-        oqctl = &qchki->qi_oqctl;
-
-        obd_quotacheck(exp, oqctl);
-        rc = ost_quotacheck_callback(exp, oqctl);
-
-        atomic_inc(&filter->fo_quotachecking);
-
-        return rc;
-}
-
-static int ost_quotacheck(struct ptlrpc_request *req)
-{
-        struct obd_device *obd = req->rq_export->exp_obd;
-        struct filter_obd *filter = &obd->u.filter;
-        struct obd_quotactl *oqctl;
-        int rc;
-        ENTRY;
-
-        oqctl = lustre_swab_reqbuf(req, 0, sizeof(*oqctl),
-                                   lustre_swab_obd_quotactl);
-        if (oqctl == NULL)
-                GOTO(out, rc = -EPROTO);
-
-        rc = lustre_pack_reply(req, 0, NULL, NULL);
-        if (rc) {
-                CERROR("ost: out of memory while packing quotacheck reply\n");
-                GOTO(out, rc = -ENOMEM);
-        }
-
-        if (!atomic_dec_and_test(&filter->fo_quotachecking)) {
-                atomic_inc(&filter->fo_quotachecking);
-                GOTO(out, rc = -EBUSY);
-        }
-        init_completion(&qchkinfo.qi_starting);
-        qchkinfo.qi_exp = req->rq_export;
-        memcpy(&qchkinfo.qi_oqctl, oqctl, sizeof(*oqctl));
-
-        rc = kernel_thread(ost_quotacheck_thread, &qchkinfo, CLONE_VM|CLONE_FILES);
-        if (rc < 0) {
-                CERROR("%s: error starting ost_quotacheck_thread: %d\n",
-                       obd->obd_name, rc);
-                atomic_inc(&filter->fo_quotachecking);
-        } else {
-                CDEBUG(D_INFO, "%s: ost_quotacheck_thread: %d\n",
-                       obd->obd_name, rc);
-                wait_for_completion(&qchkinfo.qi_starting);
-                rc = 0;
-        }
-
-        EXIT;
-out:
-        return rc;
-}
-
-static int ost_quotactl(struct ptlrpc_request *req)
-{
-        struct obd_quotactl *oqctl, *repoqc;
-        int rc, size = sizeof(*repoqc);
-        ENTRY;
-
-        oqctl = lustre_swab_reqbuf(req, 0, sizeof(*oqctl),
-                                   lustre_swab_obd_quotactl);
-        if (oqctl == NULL)
-                GOTO(out, rc = -EPROTO);
-
-        rc = lustre_pack_reply(req, 1, &size, NULL);
-        if (rc)
-                GOTO(out, rc);
-
-        repoqc = lustre_msg_buf(req->rq_repmsg, 0, sizeof(*repoqc));
-        memcpy(repoqc, oqctl, sizeof(*repoqc));
-
-        req->rq_status = obd_quotactl(req->rq_export, repoqc);
-out:
-        RETURN(rc);
-}
-
 static int ost_handle(struct ptlrpc_request *req)
 {
         struct obd_trans_info trans_info = { 0, };
index f7cd79a..9f31f25 100644 (file)
@@ -11,4 +11,21 @@ extern void ost_print_req(void *seq_file, struct ptlrpc_request *req);
 # define ost_print_req NULL
 #endif
 
+#ifdef HAVE_QUOTA_SUPPORT
+/* Quota stuff */
+int ost_quotacheck(struct ptlrpc_request *req);
+int ost_quotactl(struct ptlrpc_request *req);
+#else
+static inline int ost_quotacheck(struct ptlrpc_request *req)
+{
+        req->rq_status = -ENOTSUPP;
+        return -ENOTSUPP;
+}
+static inline int ost_quotactl(struct ptlrpc_request *req)
+{
+        req->rq_status = -ENOTSUPP;
+        return -ENOTSUPP;
+}
+#endif
+
 #endif /* OST_INTERNAL_H */
index 54f5a9d..002e5ef 100644 (file)
@@ -15,6 +15,11 @@ ptlrpc_objs += llog_net.o llog_client.o llog_server.o import.o ptlrpcd.o
 ptlrpc_objs += pers.o lproc_ptlrpc.o
 
 ptlrpc-objs := $(ldlm_objs) $(ptlrpc_objs)
+
+ifeq ($(PATCHLEVEL),6)
+#ptlrpc-objs += @top_srcdir@/lustre/mds/quota_context.o
+endif
+
 default: all
 
 ldlm_%.c: @LUSTRE@/ldlm/ldlm_%.c
index 4a9f686..c77dcfb 100644 (file)
@@ -11,11 +11,12 @@ LDLM_COMM_SOURCES= $(top_srcdir)/lustre/ldlm/l_lock.c       \
        $(top_srcdir)/lustre/ldlm/ldlm_extent.c         \
        $(top_srcdir)/lustre/ldlm/ldlm_request.c        \
        $(top_srcdir)/lustre/ldlm/ldlm_lockd.c          \
-       $(top_srcdir)/lustre/ldlm/ldlm_internal.h
+       $(top_srcdir)/lustre/ldlm/ldlm_internal.h       \
+       $(top_srcdir)/lustre/ldlm/ldlm_flock.c
 
 COMMON_SOURCES =  client.c recover.c connection.c niobuf.c pack_generic.c   \
     events.c ptlrpc_module.c service.c pinger.c recov_thread.c llog_net.c   \
-    llog_client.c llog_server.c import.c ptlrpcd.c                         \
+    llog_client.c llog_server.c import.c ptlrpcd.c pers.c                  \
     ptlrpc_internal.h $(LDLM_COMM_SOURCES)
 
 if LIBLUSTRE
@@ -31,5 +32,5 @@ if MODULES
 modulefs_DATA = ptlrpc$(KMODEXT)
 endif # MODULES
 
-MOSTLYCLEANFILES = *.o *.ko *.mod.c ldlm_*.c l_lock.c
+MOSTLYCLEANFILES := @MOSTLYCLEANFILES@  ldlm_*.c l_lock.c
 DIST_SOURCES = $(ptlrpc_objs:.o=.c) ptlrpc_internal.h
index 502b99a..db0dc82 100644 (file)
@@ -97,7 +97,7 @@ static inline struct ptlrpc_bulk_desc *new_bulk(int npages, int type, int portal
         desc->bd_md_h = PTL_INVALID_HANDLE;
         desc->bd_portal = portal;
         desc->bd_type = type;
-        
+
         return desc;
 }
 
@@ -176,7 +176,7 @@ void ptlrpc_free_bulk(struct ptlrpc_bulk_desc *desc)
         else
                 class_import_put(desc->bd_import);
 
-        OBD_FREE(desc, offsetof(struct ptlrpc_bulk_desc, 
+        OBD_FREE(desc, offsetof(struct ptlrpc_bulk_desc,
                                 bd_iov[desc->bd_max_iov]));
         EXIT;
 }
@@ -189,6 +189,9 @@ struct ptlrpc_request *ptlrpc_prep_req(struct obd_import *imp, int opcode,
         ENTRY;
 
         LASSERT((unsigned long)imp > 0x1000);
+        LASSERT(imp != LP_POISON);
+        LASSERT((unsigned long)imp->imp_client > 0x1000);
+        LASSERT(imp->imp_client != LP_POISON);
 
         OBD_ALLOC(request, sizeof(*request));
         if (!request) {
@@ -211,13 +214,13 @@ struct ptlrpc_request *ptlrpc_prep_req(struct obd_import *imp, int opcode,
         request->rq_type = PTL_RPC_MSG_REQUEST;
         request->rq_import = class_import_get(imp);
         request->rq_export = NULL;
-        
+
         request->rq_req_cbid.cbid_fn  = request_out_callback;
         request->rq_req_cbid.cbid_arg = request;
 
         request->rq_reply_cbid.cbid_fn  = reply_in_callback;
         request->rq_reply_cbid.cbid_arg = request;
-        
+
         request->rq_phase = RQ_PHASE_NEW;
 
         /* XXX FIXME bug 249 */
@@ -342,7 +345,7 @@ void ptlrpc_set_add_new_req(struct ptlrpc_request_set *set,
  *
  * The imp->imp_lock must be held.
  */
-static int ptlrpc_import_delay_req(struct obd_import *imp, 
+static int ptlrpc_import_delay_req(struct obd_import *imp,
                                    struct ptlrpc_request *req, int *status)
 {
         int delay = 0;
@@ -355,31 +358,24 @@ static int ptlrpc_import_delay_req(struct obd_import *imp,
                 DEBUG_REQ(D_ERROR, req, "Uninitialized import.");
                 *status = -EIO;
                 LBUG();
-        }
-        else if (imp->imp_state == LUSTRE_IMP_CLOSED) {
+        } else if (imp->imp_state == LUSTRE_IMP_CLOSED) {
                 DEBUG_REQ(D_ERROR, req, "IMP_CLOSED ");
                 *status = -EIO;
-        }
-        /* allow CONNECT even if import is invalid */
-        else if (req->rq_send_state == LUSTRE_IMP_CONNECTING &&
+        } else if (req->rq_send_state == LUSTRE_IMP_CONNECTING &&
                  imp->imp_state == LUSTRE_IMP_CONNECTING) {
-                ;
-        }
-        /*
-         * If the import has been invalidated (such as by an OST failure), the
-         * request must fail with -EIO.  
-         */
-        else if (imp->imp_invalid) {
-                DEBUG_REQ(D_ERROR, req, "IMP_INVALID");
+                /* allow CONNECT even if import is invalid */ ;
+        } else if (imp->imp_invalid) {
+                /* If the import has been invalidated (such as by an OST
+                 * failure), the request must fail with -EIO. */
+                if (!imp->imp_deactive)
+                        DEBUG_REQ(D_ERROR, req, "IMP_INVALID");
                 *status = -EIO;
-        } 
-        else if (req->rq_import_generation != imp->imp_generation) {
+        } else if (req->rq_import_generation != imp->imp_generation) {
                 DEBUG_REQ(D_ERROR, req, "req wrong generation:");
                 *status = -EIO;
-        } 
-        else if (req->rq_send_state != imp->imp_state) {
-                if (imp->imp_obd->obd_no_recov || imp->imp_dlm_fake 
-                    || req->rq_no_delay) 
+        } else if (req->rq_send_state != imp->imp_state) {
+                if (imp->imp_obd->obd_no_recov || imp->imp_dlm_fake ||
+                    req->rq_no_delay)
                         *status = -EWOULDBLOCK;
                 else
                         delay = 1;
@@ -401,10 +397,10 @@ static int ptlrpc_check_reply(struct ptlrpc_request *req)
                 DEBUG_REQ(D_NET, req, "REPLIED:");
                 GOTO(out, rc = 1);
         }
-        
+
         if (req->rq_net_err && !req->rq_timedout) {
                 spin_unlock_irqrestore (&req->rq_lock, flags);
-                rc = ptlrpc_expire_one_request(req); 
+                rc = ptlrpc_expire_one_request(req);
                 spin_lock_irqsave (&req->rq_lock, flags);
                 GOTO(out, rc);
         }
@@ -437,7 +433,7 @@ static int ptlrpc_check_status(struct ptlrpc_request *req)
 
         err = req->rq_repmsg->status;
         if (req->rq_repmsg->type == PTL_RPC_MSG_ERR) {
-                DEBUG_REQ(D_ERROR, req, "type == PTL_RPC_MSG_ERR, err == %d", 
+                DEBUG_REQ(D_ERROR, req, "type == PTL_RPC_MSG_ERR, err == %d",
                           err);
                 RETURN(err < 0 ? err : -EINVAL);
         }
@@ -471,14 +467,14 @@ static int after_reply(struct ptlrpc_request *req)
         LASSERT (req->rq_nob_received <= req->rq_replen);
         rc = lustre_unpack_msg(req->rq_repmsg, req->rq_nob_received);
         if (rc) {
-                CERROR("unpack_rep failed: %d\n", rc);
+                DEBUG_REQ(D_ERROR, req, "unpack_rep failed: %d\n", rc);
                 RETURN(-EPROTO);
         }
 
         if (req->rq_repmsg->type != PTL_RPC_MSG_REPLY &&
             req->rq_repmsg->type != PTL_RPC_MSG_ERR) {
-                CERROR("invalid packet type received (type=%u)\n",
-                       req->rq_repmsg->type);
+                DEBUG_REQ(D_ERROR, req, "invalid packet received (type=%u)\n",
+                          req->rq_repmsg->type);
                 RETURN(-EPROTO);
         }
 
@@ -544,7 +540,7 @@ static int ptlrpc_send_new_req(struct ptlrpc_request *req)
 
                 DEBUG_REQ(D_HA, req, "req from PID %d waiting for recovery: "
                           "(%s != %s)",
-                          req->rq_reqmsg->status, 
+                          req->rq_reqmsg->status,
                           ptlrpc_import_state_name(req->rq_send_state),
                           ptlrpc_import_state_name(imp->imp_state));
                 LASSERT(list_empty (&req->rq_list));
@@ -619,7 +615,7 @@ int ptlrpc_check_set(struct ptlrpc_request_set *set)
                         GOTO(interpret, req->rq_status);
 
                 if (req->rq_net_err && !req->rq_timedout)
-                        ptlrpc_expire_one_request(req); 
+                        ptlrpc_expire_one_request(req);
 
                 if (req->rq_err) {
                         ptlrpc_unregister_reply(req);
@@ -637,7 +633,7 @@ int ptlrpc_check_set(struct ptlrpc_request_set *set)
                 /* ptlrpc_queue_wait->l_wait_event guarantees that rq_intr
                  * will only be set after rq_timedout, but the oig waiting
                  * path sets rq_intr irrespective of whether ptlrpcd has
-                 * seen a timeout.  our policy is to only interpret 
+                 * seen a timeout.  our policy is to only interpret
                  * interrupted rpcs after they have timed out */
                 if (req->rq_intr && (req->rq_timedout || req->rq_waiting)) {
                         /* NB could be on delayed list */
@@ -733,7 +729,7 @@ int ptlrpc_check_set(struct ptlrpc_request_set *set)
                                    it can be errored if the import is
                                    evicted after recovery. */
                                 spin_lock_irqsave (&req->rq_lock, flags);
-                                list_add_tail(&req->rq_list, 
+                                list_add_tail(&req->rq_list,
                                               &imp->imp_delayed_list);
                                 spin_unlock_irqrestore(&req->rq_lock, flags);
                                 continue;
@@ -862,8 +858,8 @@ int ptlrpc_expired_set(void *data)
                         list_entry(tmp, struct ptlrpc_request, rq_set_chain);
 
                 /* request in-flight? */
-                if (!((req->rq_phase == RQ_PHASE_RPC && !req->rq_waiting 
-                       && !req->rq_resend) ||
+                if (!((req->rq_phase == RQ_PHASE_RPC && !req->rq_waiting &&
+                       !req->rq_resend) ||
                       (req->rq_phase == RQ_PHASE_BULK)))
                         continue;
 
@@ -1345,8 +1341,8 @@ restart:
                 spin_unlock_irqrestore(&imp->imp_lock, flags);
 
                 DEBUG_REQ(D_HA, req, "\"%s\" waiting for recovery: (%s != %s)",
-                          current->comm, 
-                          ptlrpc_import_state_name(req->rq_send_state), 
+                          current->comm,
+                          ptlrpc_import_state_name(req->rq_send_state),
                           ptlrpc_import_state_name(imp->imp_state));
                 lwi = LWI_INTR(interrupted_request, req);
                 rc = l_wait_event(req->rq_reply_waitq,
@@ -1354,8 +1350,8 @@ restart:
                                    req->rq_err),
                                   &lwi);
                 DEBUG_REQ(D_HA, req, "\"%s\" awake: (%s == %s or %d == 1)",
-                          current->comm, 
-                          ptlrpc_import_state_name(imp->imp_state), 
+                          current->comm,
+                          ptlrpc_import_state_name(imp->imp_state),
                           ptlrpc_import_state_name(req->rq_send_state),
                           req->rq_err);
 
@@ -1387,9 +1383,19 @@ restart:
         if (req->rq_resend) {
                 lustre_msg_add_flags(req->rq_reqmsg, MSG_RESENT);
 
-                if (req->rq_bulk != NULL)
+                if (req->rq_bulk != NULL) {
                         ptlrpc_unregister_bulk (req);
 
+                        /* bulk requests are supposed to be
+                         * idempotent, so we are free to bump the xid
+                         * here, which we need to do before
+                         * registering the bulk again (bug 6371).
+                         * print the old xid first for sanity.
+                         */
+                        DEBUG_REQ(D_HA, req, "bumping xid for bulk: ");
+                        req->rq_xid = ptlrpc_next_xid();
+                }
+
                 DEBUG_REQ(D_HA, req, "resending: ");
         }
 
@@ -1467,7 +1473,7 @@ restart:
 
  out:
         if (req->rq_bulk != NULL) {
-                if (rc >= 0) {                  
+                if (rc >= 0) {
                         /* success so far.  Note that anything going wrong
                          * with bulk now, is EXTREMELY strange, since the
                          * server must have believed that the bulk
@@ -1512,7 +1518,7 @@ static int ptlrpc_replay_interpret(struct ptlrpc_request *req,
         unsigned long flags;
 
         atomic_dec(&imp->imp_replay_inflight);
-        
+
         if (!req->rq_replied) {
                 CERROR("request replay timed out, restarting recovery\n");
                 GOTO(out, rc = -ETIMEDOUT);
@@ -1525,12 +1531,12 @@ static int ptlrpc_replay_interpret(struct ptlrpc_request *req,
         LASSERT (req->rq_nob_received <= req->rq_replen);
         rc = lustre_unpack_msg(req->rq_repmsg, req->rq_nob_received);
         if (rc) {
-                CERROR("unpack_rep failed: %d\n", rc);
+                DEBUG_REQ(D_ERROR, req, "unpack_rep failed: %d\n", rc);
                 GOTO(out, rc = -EPROTO);
         }
 
-        if (req->rq_repmsg->type == PTL_RPC_MSG_ERR && 
-            req->rq_repmsg->status == -ENOTCONN) 
+        if (req->rq_repmsg->type == PTL_RPC_MSG_ERR &&
+            req->rq_repmsg->status == -ENOTCONN)
                 GOTO(out, rc = req->rq_repmsg->status);
 
         /* The transno had better not change over replay. */
@@ -1558,7 +1564,7 @@ static int ptlrpc_replay_interpret(struct ptlrpc_request *req,
         rc = ptlrpc_import_recovery_state_machine(imp);
  out:
         req->rq_send_state = aa->praa_old_state;
-        
+
         if (rc != 0)
                 /* this replay failed, so restart recovery */
                 ptlrpc_connect_import(imp, NULL);
index 928f268..f1ef597 100644 (file)
@@ -211,6 +211,9 @@ void request_in_callback(ptl_event_t *ev)
         req->rq_peer = ev->initiator;
         req->rq_rqbd = rqbd;
         req->rq_phase = RQ_PHASE_NEW;
+#if CRAY_PORTALS
+        req->rq_uid = ev->uid;
+#endif
         
         spin_lock_irqsave (&service->srv_lock, flags);
 
@@ -393,6 +396,12 @@ ptl_pid_t ptl_get_pid(void)
 
 #ifndef  __KERNEL__
         pid = getpid();
+# if CRAY_PORTALS
+       /* hack to keep pid in range accepted by ernal */
+       pid &= 0xFF;
+       if (pid == LUSTRE_SRV_PTL_PID)
+               pid++;
+# endif
 #else
         pid = LUSTRE_SRV_PTL_PID;
 #endif
@@ -404,7 +413,7 @@ int ptlrpc_ni_init(void)
         int              rc;
         char             str[20];
         ptl_pid_t        pid;
-        
+
         pid = ptl_get_pid();
 
         /* We're not passing any limits yet... */
@@ -425,6 +434,11 @@ int ptlrpc_ni_init(void)
         /* kernel portals calls our master callback when events are added to
          * the event queue.  In fact lustre never pulls events off this queue,
          * so it's only sized for some debug history. */
+# if CRAY_PORTALS
+        rc = PtlNIDebug(pni->pni_ni_h, 0xffffffff);
+        if (rc != PTL_OK)
+                CDEBUG(D_ERROR, "Can't enable Cray Portals Debug: rc %d\n", rc);
+# endif
         rc = PtlEQAlloc(ptlrpc_ni_h, 1024, ptlrpc_master_callback,
                         &ptlrpc_eq_h);
 #else
@@ -487,7 +501,7 @@ liblustre_check_events (int timeout)
         int         i;
         ENTRY;
 
-        rc = PtlEQPoll(ptlrpc_eq_h, 1, timeout * 1000, &ev, &i);
+        rc = PtlEQPoll(&ptlrpc_eq_h, 1, timeout * 1000, &ev, &i);
         if (rc == PTL_EQ_EMPTY)
                 RETURN(0);
         
index 6cb508b..0b0ebf5 100644 (file)
@@ -241,14 +241,10 @@ void ptlrpc_fail_import(struct obd_import *imp, int generation)
         EXIT;
 }
 
-#define ATTEMPT_TOO_SOON(last)  \
-        ((last) && ((long)(jiffies - (last)) <= (long)(obd_timeout * 2 * HZ)))
-
 static int import_select_connection(struct obd_import *imp)
 {
-        struct obd_import_conn *imp_conn, *tmp;
+        struct obd_import_conn *imp_conn;
         struct obd_export *dlmexp;
-        int found = 0;
         ENTRY;
 
         spin_lock(&imp->imp_lock);
@@ -260,34 +256,13 @@ static int import_select_connection(struct obd_import *imp)
                 RETURN(-EINVAL);
         }
 
-        list_for_each_entry(imp_conn, &imp->imp_conn_list, oic_item) {
-                if (!imp_conn->oic_last_attempt ||
-                    time_after(jiffies, imp_conn->oic_last_attempt + 
-                               obd_timeout * 2 * HZ)) {
-                        found = 1;
-                        break;
-                }
-        }
-
-        /* if not found, simply choose the current one */
-        if (!found) {
-                CWARN("%s: continuing with current connection\n",
-                      imp->imp_obd->obd_name);
-                LASSERT(imp->imp_conn_current);
-                imp_conn = imp->imp_conn_current;
-        }
-        LASSERT(imp_conn->oic_conn);
-
-        imp_conn->oic_last_attempt = jiffies;
-
-        /* move the items ahead of the selected one to list tail */
-        while (1) {
-                tmp= list_entry(imp->imp_conn_list.next,
-                                struct obd_import_conn, oic_item);
-                if (tmp == imp_conn)
-                        break;
-                list_del(&tmp->oic_item);
-                list_add_tail(&tmp->oic_item, &imp->imp_conn_list);
+        if (imp->imp_conn_current && 
+            !(imp->imp_conn_current->oic_item.next == &imp->imp_conn_list)) {
+                imp_conn = list_entry(imp->imp_conn_current->oic_item.next,
+                                  struct obd_import_conn, oic_item);
+        } else {
+                imp_conn = list_entry(imp->imp_conn_list.next,
+                                      struct obd_import_conn, oic_item);
         }
 
         /* switch connection, don't mind if it's same as the current one */
@@ -303,9 +278,8 @@ static int import_select_connection(struct obd_import *imp)
         class_export_put(dlmexp);
 
         imp->imp_conn_current = imp_conn;
-        CWARN("%s: Using connection %s\n",
-               imp->imp_obd->obd_name,
-               imp_conn->oic_uuid.uuid);
+        CDEBUG(D_HA, "%s: import %p using connection %s\n",
+               imp->imp_obd->obd_name, imp, imp_conn->oic_uuid.uuid);
         spin_unlock(&imp->imp_lock);
 
         RETURN(0);
@@ -404,6 +378,36 @@ out:
         RETURN(rc);
 }
 
+static void ptlrpc_maybe_ping_import_soon(struct obd_import *imp)
+{
+        struct obd_import_conn *imp_conn;
+        unsigned long flags;
+        int wake_pinger = 0;
+
+        ENTRY;
+
+        spin_lock_irqsave(&imp->imp_lock, flags);
+        if (list_empty(&imp->imp_conn_list))
+                GOTO(unlock, 0);
+
+        imp_conn = list_entry(imp->imp_conn_list.prev,
+                              struct obd_import_conn,
+                              oic_item);
+
+        if (imp->imp_conn_current != imp_conn) {
+                ptlrpc_ping_import_soon(imp);
+                wake_pinger = 1;
+        }
+
+ unlock:
+        spin_unlock_irqrestore(&imp->imp_lock, flags);
+
+        if (wake_pinger)
+                ptlrpc_pinger_wake_up();
+
+        EXIT;
+}
+
 static int ptlrpc_connect_interpret(struct ptlrpc_request *request,
                                     void * data, int rc)
 {
@@ -425,19 +429,22 @@ static int ptlrpc_connect_interpret(struct ptlrpc_request *request,
                 GOTO(out, rc);
 
         LASSERT(imp->imp_conn_current);
-        imp->imp_conn_current->oic_last_attempt = 0;
 
         msg_flags = lustre_msg_get_op_flags(request->rq_repmsg);
 
+        /* All imports are pingable */
+        imp->imp_pingable = 1;
+        
         if (aa->pcaa_initial_connect) {
                 if (msg_flags & MSG_CONNECT_REPLAYABLE) {
                         CDEBUG(D_HA, "connected to replayable target: %s\n",
                                imp->imp_target_uuid.uuid);
-                        imp->imp_pingable = imp->imp_replayable = 1;
+                        imp->imp_replayable = 1;
                 } else {
                         imp->imp_replayable = 0;
                 }
                 imp->imp_remote_handle = request->rq_repmsg->handle;
+
                 IMPORT_SET_STATE(imp, LUSTRE_IMP_FULL);
                 GOTO(finish, rc = 0);
         }
@@ -516,13 +523,22 @@ finish:
                         ptlrpc_connect_import(imp, NULL);
                         RETURN(0);
                 }
+        } else {
+                list_del(&imp->imp_conn_current->oic_item);
+                list_add(&imp->imp_conn_current->oic_item,
+                         &imp->imp_conn_list);
+                imp->imp_conn_current = NULL;
         }
+
  out:
         if (rc != 0) {
                 IMPORT_SET_STATE(imp, LUSTRE_IMP_DISCON);
                 if (aa->pcaa_initial_connect && !imp->imp_initial_recov) {
                         ptlrpc_deactivate_import(imp);
                 }
+
+                ptlrpc_maybe_ping_import_soon(imp);
+                
                 CDEBUG(D_HA, "recovery of %s on %s failed (%d)\n",
                        imp->imp_target_uuid.uuid,
                        (char *)imp->imp_connection->c_remote_uuid.uuid, rc);
@@ -572,6 +588,7 @@ static int signal_completed_replay(struct obd_import *imp)
         RETURN(0);
 }
 
+#ifdef __KERNEL__
 static int ptlrpc_invalidate_import_thread(void *data)
 {
         struct obd_import *imp = data;
@@ -600,6 +617,7 @@ static int ptlrpc_invalidate_import_thread(void *data)
 
         RETURN(0);
 }
+#endif
 
 int ptlrpc_import_recovery_state_machine(struct obd_import *imp)
 {
@@ -612,20 +630,25 @@ int ptlrpc_import_recovery_state_machine(struct obd_import *imp)
                 deuuidify(imp->imp_target_uuid.uuid, NULL,
                           &target_start, &target_len);
                 LCONSOLE_ERROR("This client was evicted by %.*s; in progress "
-                               "operations using this service will %s.\n",
-                               target_len, target_start,
-                               imp->imp_replayable
-                               ? "be reattempted"
-                               : "fail");
+                               "operations using this service will fail.\n",
+                               target_len, target_start);
                 CDEBUG(D_HA, "evicted from %s@%s; invalidating\n",
                        imp->imp_target_uuid.uuid,
                        imp->imp_connection->c_remote_uuid.uuid);
 
+#ifdef __KERNEL__
                 rc = kernel_thread(ptlrpc_invalidate_import_thread, imp,
                                    CLONE_VM | CLONE_FILES);
                 if (rc < 0)
                         CERROR("error starting invalidate thread: %d\n", rc);
+                else
+                        rc = 0;
                 RETURN(rc);
+#else
+                ptlrpc_invalidate_import(imp);
+
+                IMPORT_SET_STATE(imp, LUSTRE_IMP_RECOVER);
+#endif
         }
 
         if (imp->imp_state == LUSTRE_IMP_REPLAY) {
index 591c5fc..5ea8bca 100644 (file)
@@ -57,7 +57,13 @@ static int llog_client_create(struct llog_ctxt *ctxt, struct llog_handle **res,
         int rc;
         ENTRY;
 
-        LASSERT(ctxt->loc_imp);
+        if (ctxt->loc_imp == NULL) {
+                /* This used to be an assert; bug 6200 */
+                CERROR("ctxt->loc_imp == NULL for context idx %d.  Unable to "
+                       "complete MDS/OSS recovery, but I'll try again next "
+                       "time.  Not fatal.\n", ctxt->loc_idx);
+                RETURN(-EINVAL);
+        }
         imp = ctxt->loc_imp;
 
         handle = llog_alloc_handle();
index a038d8a..5c7af41 100644 (file)
@@ -50,7 +50,7 @@ int llog_origin_handle_create(struct ptlrpc_request *req)
         struct obd_device *disk_obd;
         struct llog_handle  *loghandle;
         struct llogd_body *body;
-        struct obd_run_ctxt saved;
+        struct lvfs_run_ctxt saved;
         struct llog_logid *logid = NULL;
         struct llog_ctxt *ctxt;
         char * name = NULL;
@@ -81,7 +81,7 @@ int llog_origin_handle_create(struct ptlrpc_request *req)
         if (ctxt == NULL)
                 GOTO(out, rc = -EINVAL);
         disk_obd = ctxt->loc_exp->exp_obd;
-        push_ctxt(&saved, &disk_obd->obd_ctxt, NULL);
+        push_ctxt(&saved, &disk_obd->obd_lvfs_ctxt, NULL);
 
         rc = llog_create(ctxt, &loghandle, logid, name);
         if (rc)
@@ -99,7 +99,7 @@ out_close:
         if (!rc)
                 rc = rc2;
 out_pop:
-        pop_ctxt(&saved, &disk_obd->obd_ctxt, NULL);
+        pop_ctxt(&saved, &disk_obd->obd_lvfs_ctxt, NULL);
 out:
         RETURN(rc);
 }
@@ -111,7 +111,7 @@ int llog_origin_handle_next_block(struct ptlrpc_request *req)
         struct obd_device *disk_obd;
         struct llog_handle  *loghandle;
         struct llogd_body *body;
-        struct obd_run_ctxt saved;
+        struct lvfs_run_ctxt saved;
         struct llog_ctxt *ctxt;
         __u32 flags;
         __u8 *buf;
@@ -136,7 +136,7 @@ int llog_origin_handle_next_block(struct ptlrpc_request *req)
         if (ctxt == NULL)
                 GOTO(out, rc = -EINVAL);
         disk_obd = ctxt->loc_exp->exp_obd;
-        push_ctxt(&saved, &disk_obd->obd_ctxt, NULL);
+        push_ctxt(&saved, &disk_obd->obd_lvfs_ctxt, NULL);
 
         rc = llog_create(ctxt, &loghandle, &body->lgd_logid, NULL);
         if (rc)
@@ -171,7 +171,7 @@ out_close:
                 rc = rc2;
 
 out_pop:
-        pop_ctxt(&saved, &disk_obd->obd_ctxt, NULL);
+        pop_ctxt(&saved, &disk_obd->obd_lvfs_ctxt, NULL);
         OBD_FREE(buf, LLOG_CHUNK_SIZE);
 out:
         RETURN(rc);
@@ -185,7 +185,7 @@ int llog_origin_handle_read_header(struct ptlrpc_request *req)
         struct llog_handle  *loghandle;
         struct llogd_body *body;
         struct llog_log_hdr *hdr;
-        struct obd_run_ctxt saved;
+        struct lvfs_run_ctxt saved;
         struct llog_ctxt *ctxt;
         __u32 flags;
         int size[] = {sizeof (*hdr)};
@@ -203,7 +203,7 @@ int llog_origin_handle_read_header(struct ptlrpc_request *req)
         if (ctxt == NULL)
                 GOTO(out, rc = -EINVAL);
         disk_obd = ctxt->loc_exp->exp_obd;
-        push_ctxt(&saved, &disk_obd->obd_ctxt, NULL);
+        push_ctxt(&saved, &disk_obd->obd_lvfs_ctxt, NULL);
 
         rc = llog_create(ctxt, &loghandle, &body->lgd_logid, NULL);
         if (rc)
@@ -228,7 +228,7 @@ out_close:
                 rc = rc2;
 
 out_pop:
-        pop_ctxt(&saved, &disk_obd->obd_ctxt, NULL);
+        pop_ctxt(&saved, &disk_obd->obd_lvfs_ctxt, NULL);
 
 out:
         RETURN(rc);
@@ -250,7 +250,7 @@ int llog_origin_handle_cancel(struct ptlrpc_request *req)
         struct llog_cookie *logcookies;
         struct llog_ctxt *ctxt;
         int num_cookies, rc = 0, err, i;
-        struct obd_run_ctxt saved;
+        struct lvfs_run_ctxt saved;
         struct llog_handle *cathandle;
         struct inode *inode;
         void *handle;
@@ -270,7 +270,7 @@ int llog_origin_handle_cancel(struct ptlrpc_request *req)
         }
 
         disk_obd = ctxt->loc_exp->exp_obd;
-        push_ctxt(&saved, &disk_obd->obd_ctxt, NULL);
+        push_ctxt(&saved, &disk_obd->obd_lvfs_ctxt, NULL);
         for (i = 0; i < num_cookies; i++, logcookies++) {
                 cathandle = ctxt->loc_handle;
                 LASSERT(cathandle != NULL);
@@ -294,7 +294,7 @@ int llog_origin_handle_cancel(struct ptlrpc_request *req)
                 }
         }
 pop_ctxt:
-        pop_ctxt(&saved, &disk_obd->obd_ctxt, NULL);
+        pop_ctxt(&saved, &disk_obd->obd_lvfs_ctxt, NULL);
         if (rc)
                 CERROR("cancel %d llog-records failed: %d\n", num_cookies, rc);
         else
@@ -309,7 +309,7 @@ static int llog_catinfo_config(struct obd_device *obd, char *buf, int buf_len,
 {
         struct mds_obd *mds = &obd->u.mds;
         struct llog_ctxt *ctxt = llog_get_context(obd, LLOG_CONFIG_ORIG_CTXT);
-        struct obd_run_ctxt saved;
+        struct lvfs_run_ctxt saved;
         struct llog_handle *handle = NULL;
         char name[4][64];
         int rc, i, l, remains = buf_len;
@@ -318,7 +318,7 @@ static int llog_catinfo_config(struct obd_device *obd, char *buf, int buf_len,
         if (ctxt == NULL || mds == NULL)
                 RETURN(-EOPNOTSUPP);
 
-        push_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_ctxt, NULL);
+        push_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_lvfs_ctxt, NULL);
 
         sprintf(name[0], "%s", mds->mds_profile);
         sprintf(name[1], "%s-clean", mds->mds_profile);
@@ -354,7 +354,7 @@ static int llog_catinfo_config(struct obd_device *obd, char *buf, int buf_len,
                         break;
         }
 out_pop:
-        pop_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_ctxt, NULL);
+        pop_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_lvfs_ctxt, NULL);
         RETURN(rc);
 }
 
@@ -426,7 +426,7 @@ static int llog_catinfo_deletions(struct obd_device *obd, char *buf,
 {
         struct mds_obd *mds = &obd->u.mds;
         struct llog_handle *handle;
-        struct obd_run_ctxt saved;
+        struct lvfs_run_ctxt saved;
         int size, i, count;
         struct llog_catid *idarray;
         struct llog_logid *id;
@@ -449,7 +449,7 @@ static int llog_catinfo_deletions(struct obd_device *obd, char *buf,
         if (rc)
                 GOTO(out_free, rc);
 
-        push_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_ctxt, NULL);
+        push_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_lvfs_ctxt, NULL);
 
         data.ctxt = ctxt;
         data.out = buf;
@@ -487,7 +487,7 @@ static int llog_catinfo_deletions(struct obd_device *obd, char *buf,
                         break;
         }
 out_pop:
-        pop_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_ctxt, NULL);
+        pop_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_lvfs_ctxt, NULL);
 out_free:
         OBD_FREE(idarray, size);
         RETURN(rc);
index aa208ce..6568579 100644 (file)
@@ -102,12 +102,7 @@ const char* ll_opcode2str(__u32 opcode)
         return ll_rpc_opcode_table[offset].opname;
 }
 
-#ifndef LPROCFS
-void ptlrpc_lprocfs_register_service(struct obd_device *obddev,
-                                     struct ptlrpc_service *svc) { return ; }
-void ptlrpc_lprocfs_unregister_service(struct ptlrpc_service *svc) { return; }
-#else
-
+#ifdef LPROCFS
 void ptlrpc_lprocfs_register(struct proc_dir_entry *root, char *dir,
                              char *name, struct proc_dir_entry **procroot_ret,
                              struct lprocfs_stats **stats_ret)
@@ -167,7 +162,7 @@ ptlrpc_lprocfs_read_req_history_len(char *page, char **start, off_t off,
                                     int count, int *eof, void *data)
 {
         struct ptlrpc_service *svc = data;
-        
+
         *eof = 1;
         return snprintf(page, count, "%d\n", svc->srv_n_history_rqbds);
 }
@@ -177,7 +172,7 @@ ptlrpc_lprocfs_read_req_history_max(char *page, char **start, off_t off,
                                     int count, int *eof, void *data)
 {
         struct ptlrpc_service *svc = data;
-        
+
         *eof = 1;
         return snprintf(page, count, "%d\n", svc->srv_max_history_rqbds);
 }
@@ -191,7 +186,7 @@ ptlrpc_lprocfs_write_req_history_max(struct file *file, const char *buffer,
         unsigned long          flags;
         int                    val;
         int                    rc = lprocfs_write_helper(buffer, count, &val);
-        
+
         if (rc < 0)
                 return rc;
 
@@ -204,11 +199,11 @@ ptlrpc_lprocfs_write_req_history_max(struct file *file, const char *buffer,
         bufpages = (svc->srv_buf_size + PAGE_SIZE - 1)/PAGE_SIZE;
         if (val > num_physpages/(2*bufpages))
                 return -ERANGE;
-        
+
         spin_lock_irqsave(&svc->srv_lock, flags);
         svc->srv_max_history_rqbds = val;
         spin_unlock_irqrestore(&svc->srv_lock, flags);
+
         return count;
 }
 
@@ -230,7 +225,7 @@ ptlrpc_lprocfs_svc_req_history_seek(struct ptlrpc_service *svc,
             srhi->srhi_seq <= seq) {
                 /* If srhi_req was set previously, hasn't been culled and
                  * we're searching for a seq on or after it (i.e. more
-                 * recent), search from it onwards. 
+                 * recent), search from it onwards.
                  * Since the service history is LRU (i.e. culled reqs will
                  * be near the head), we shouldn't have to do long
                  * re-scans */
@@ -244,7 +239,7 @@ ptlrpc_lprocfs_svc_req_history_seek(struct ptlrpc_service *svc,
 
         while (e != &svc->srv_request_history) {
                 req = list_entry(e, struct ptlrpc_request, rq_history_list);
-                
+
                 if (req->rq_history_seq >= seq) {
                         srhi->srhi_seq = req->rq_history_seq;
                         srhi->srhi_req = req;
@@ -252,7 +247,7 @@ ptlrpc_lprocfs_svc_req_history_seek(struct ptlrpc_service *svc,
                 }
                 e = e->next;
         }
-        
+
         return -ENOENT;
 }
 
@@ -270,16 +265,16 @@ ptlrpc_lprocfs_svc_req_history_start(struct seq_file *s, loff_t *pos)
 
         srhi->srhi_seq = 0;
         srhi->srhi_req = NULL;
-        
+
         spin_lock_irqsave(&svc->srv_lock, flags);
         rc = ptlrpc_lprocfs_svc_req_history_seek(svc, srhi, *pos);
         spin_unlock_irqrestore(&svc->srv_lock, flags);
-        
+
         if (rc == 0) {
                 *pos = srhi->srhi_seq;
                 return srhi;
         }
-        
+
         OBD_FREE(srhi, sizeof(*srhi));
         return NULL;
 }
@@ -294,7 +289,7 @@ ptlrpc_lprocfs_svc_req_history_stop(struct seq_file *s, void *iter)
 }
 
 static void *
-ptlrpc_lprocfs_svc_req_history_next(struct seq_file *s, 
+ptlrpc_lprocfs_svc_req_history_next(struct seq_file *s,
                                     void *iter, loff_t *pos)
 {
         struct ptlrpc_service       *svc = s->private;
@@ -378,13 +373,13 @@ void ptlrpc_lprocfs_register_service(struct proc_dir_entry *entry,
                                      struct ptlrpc_service *svc)
 {
         struct lprocfs_vars lproc_vars[] = {
-                {.name       = "req_buffer_history_len", 
+                {.name       = "req_buffer_history_len",
                  .write_fptr = NULL,
-                 .read_fptr  = ptlrpc_lprocfs_read_req_history_len, 
+                 .read_fptr  = ptlrpc_lprocfs_read_req_history_len,
                  .data       = svc},
                 {.name       = "req_buffer_history_max",
                  .write_fptr = ptlrpc_lprocfs_write_req_history_max,
-                 .read_fptr  = ptlrpc_lprocfs_read_req_history_max, 
+                 .read_fptr  = ptlrpc_lprocfs_read_req_history_max,
                  .data       = svc},
                 {NULL}
         };
@@ -400,13 +395,13 @@ void ptlrpc_lprocfs_register_service(struct proc_dir_entry *entry,
         ptlrpc_lprocfs_register(entry, svc->srv_name,
                                 "stats", &svc->srv_procroot,
                                 &svc->srv_stats);
-        
+
         if (svc->srv_procroot == NULL)
                 return;
 
         lprocfs_add_vars(svc->srv_procroot, lproc_vars, NULL);
 
-        req_history = create_proc_entry("req_history", 0400, 
+        req_history = create_proc_entry("req_history", 0400,
                                         svc->srv_procroot);
         if (req_history != NULL) {
                 req_history->data = svc;
@@ -420,6 +415,7 @@ void ptlrpc_lprocfs_register_obd(struct obd_device *obddev)
                                 &obddev->obd_svc_procroot,
                                 &obddev->obd_svc_stats);
 }
+EXPORT_SYMBOL(ptlrpc_lprocfs_register_obd);
 
 void ptlrpc_lprocfs_rpc_sent(struct ptlrpc_request *req)
 {
@@ -456,6 +452,8 @@ void ptlrpc_lprocfs_unregister_obd(struct obd_device *obd)
                 obd->obd_svc_stats = NULL;
         }
 }
+EXPORT_SYMBOL(ptlrpc_lprocfs_unregister_obd);
+
 
 int lprocfs_wr_evict_client(struct file *file, const char *buffer,
                             unsigned long count, void *data)
@@ -484,7 +482,7 @@ int lprocfs_wr_evict_client(struct file *file, const char *buffer,
                 CERROR("can't disconnect %s: no export found\n", doomed.uuid);
         } else {
                 CERROR("evicting %s at adminstrative request\n", doomed.uuid);
-                ptlrpc_fail_export(doomed_exp);
+                class_fail_export(doomed_exp);
                 class_export_put(doomed_exp);
         }
         return count;
index 9be4e92..0928174 100644 (file)
@@ -219,7 +219,9 @@ int ptlrpc_register_bulk (struct ptlrpc_request *req)
         /* XXX Registering the same xid on retried bulk makes my head
          * explode trying to understand how the original request's bulk
          * might interfere with the retried request -eeb */
-        LASSERT (!desc->bd_registered || req->rq_xid != desc->bd_last_xid);
+        LASSERTF (!desc->bd_registered || req->rq_xid != desc->bd_last_xid,
+                  "registered: %d  rq_xid: "LPU64" bd_last_xid: "LPU64"\n",
+                  desc->bd_registered, req->rq_xid, desc->bd_last_xid);
         desc->bd_registered = 1;
         desc->bd_last_xid = req->rq_xid;
 
@@ -381,6 +383,8 @@ int ptl_send_rpc(struct ptlrpc_request *request)
         ptl_md_t         reply_md;
         ENTRY;
 
+        OBD_FAIL_RETURN(OBD_FAIL_PTLRPC_DROP_RPC, 0); 
+
         LASSERT (request->rq_type == PTL_RPC_MSG_REQUEST);
 
         /* If this is a re-transmit, we're required to have disengaged
@@ -395,7 +399,7 @@ int ptl_send_rpc(struct ptlrpc_request *request)
                 request->rq_err = 1;
                 RETURN(-ENODEV);
         }
-
+        
         connection = request->rq_import->imp_connection;
 
         if (request->rq_bulk != NULL) {
index c571678..ef1aa0e 100644 (file)
@@ -753,42 +753,6 @@ int llog_log_swabbed(struct llog_log_hdr *hdr)
         return -1;
 }
 
-void lustre_swab_llogd_body (struct llogd_body *d)
-{
-        __swab64s (&d->lgd_logid.lgl_oid);
-        __swab64s (&d->lgd_logid.lgl_ogr);
-        __swab32s (&d->lgd_logid.lgl_ogen);
-        __swab32s (&d->lgd_ctxt_idx);
-        __swab32s (&d->lgd_llh_flags);
-        __swab32s (&d->lgd_index);
-        __swab32s (&d->lgd_saved_index);
-        __swab32s (&d->lgd_len);
-        __swab64s (&d->lgd_cur_offset);
-}
-
-void lustre_swab_llog_hdr (struct llog_log_hdr *h)
-{
-        __swab32s (&h->llh_hdr.lrh_index);
-        __swab32s (&h->llh_hdr.lrh_len);
-        __swab32s (&h->llh_hdr.lrh_type);
-        __swab64s (&h->llh_timestamp);
-        __swab32s (&h->llh_count);
-        __swab32s (&h->llh_bitmap_offset);
-        __swab32s (&h->llh_flags);
-        __swab32s (&h->llh_tail.lrt_index);
-        __swab32s (&h->llh_tail.lrt_len);
-}
-
-void lustre_swab_llogd_conn_body (struct llogd_conn_body *d)
-{
-        __swab64s (&d->lgdc_gen.mnt_cnt);
-        __swab64s (&d->lgdc_gen.conn_cnt);
-        __swab64s (&d->lgdc_logid.lgl_oid);
-        __swab64s (&d->lgdc_logid.lgl_ogr);
-        __swab32s (&d->lgdc_logid.lgl_ogen);
-        __swab32s (&d->lgdc_ctxt_idx);
-}
-
 void lustre_swab_qdata(struct qunit_data *d)
 {
         __swab32s (&d->qd_id);
@@ -1883,7 +1847,7 @@ void lustre_assert_wire_constants(void)
                  (long long)(int)sizeof(((struct ldlm_res_id *)0)->name[4]));
 
         /* Checks for struct ldlm_extent */
-        LASSERTF((int)sizeof(struct ldlm_extent) == 16, " found %lld\n",
+        LASSERTF((int)sizeof(struct ldlm_extent) == 24, " found %lld\n",
                  (long long)(int)sizeof(struct ldlm_extent));
         LASSERTF((int)offsetof(struct ldlm_extent, start) == 0, " found %lld\n",
                  (long long)(int)offsetof(struct ldlm_extent, start));
index 6f5d086..2bd04f2 100644 (file)
@@ -93,7 +93,13 @@ void ptlrpc_fill_bulk_md(ptl_md_t *md, struct ptlrpc_bulk_desc *desc)
 {
 #if CRAY_PORTALS
         LASSERT (!(md->options & (PTL_MD_IOVEC | PTL_MD_PHYS)));
+#if defined(REDSTORM) && (NALID_FROM_IFACE(CRAY_QK_NAL) == PTL_IFACE_SS_ACCEL)
+       /* Enforce iov_count == 1 constraint only for SeaStar accel mode on
+        * compute nodes (ie, REDSTORM)
+        *
+        * iov_count of > 1 is supported via PTL_MD_IOVEC in other contexts */
         LASSERT (desc->bd_iov_count == 1);
+#endif
 #else
         LASSERT (!(md->options & (PTL_MD_IOVEC | PTL_MD_KIOV | PTL_MD_PHYS)));
 #endif
index 05172e2..8b75dcf 100644 (file)
@@ -38,7 +38,7 @@
 static DECLARE_MUTEX(pinger_sem);
 static struct list_head pinger_imports = LIST_HEAD_INIT(pinger_imports);
 
-int ptlrpc_ping(struct obd_import *imp) 
+int ptlrpc_ping(struct obd_import *imp)
 {
         struct ptlrpc_request *req;
         int rc = 0;
@@ -47,17 +47,16 @@ int ptlrpc_ping(struct obd_import *imp)
         req = ptlrpc_prep_req(imp, OBD_PING, 0, NULL,
                               NULL);
         if (req) {
-                DEBUG_REQ(D_HA, req, "pinging %s->%s",
+                DEBUG_REQ(D_INFO, req, "pinging %s->%s",
                           imp->imp_obd->obd_uuid.uuid,
                           imp->imp_target_uuid.uuid);
                 req->rq_no_resend = req->rq_no_delay = 1;
-                req->rq_replen = lustre_msg_size(0, 
-                                                 NULL);
+                req->rq_replen = lustre_msg_size(0, NULL);
                 ptlrpcd_add_req(req);
         } else {
                 CERROR("OOM trying to ping %s->%s\n",
-                          imp->imp_obd->obd_uuid.uuid,
-                          imp->imp_target_uuid.uuid);
+                       imp->imp_obd->obd_uuid.uuid,
+                       imp->imp_target_uuid.uuid);
                 rc = -ENOMEM;
         }
 
@@ -66,7 +65,13 @@ int ptlrpc_ping(struct obd_import *imp)
 
 static inline void ptlrpc_update_next_ping(struct obd_import *imp)
 {
-        imp->imp_next_ping = jiffies + obd_timeout * HZ;
+        imp->imp_next_ping = jiffies + HZ *
+                (imp->imp_state == LUSTRE_IMP_DISCON ? 10 : PING_INTERVAL);
+}
+
+void ptlrpc_ping_import_soon(struct obd_import *imp)
+{
+        imp->imp_next_ping = jiffies;
 }
 
 #ifdef __KERNEL__
@@ -99,7 +104,7 @@ static int ptlrpc_pinger_main(void *arg)
         while (1) {
                 unsigned long this_ping = jiffies;
                 long time_to_next_ping;
-                struct l_wait_info lwi = LWI_TIMEOUT(obd_timeout * HZ,
+                struct l_wait_info lwi = LWI_TIMEOUT(PING_INTERVAL * HZ,
                                                      NULL, NULL);
                 struct list_head *iter;
 
@@ -115,52 +120,66 @@ static int ptlrpc_pinger_main(void *arg)
                         spin_lock_irqsave(&imp->imp_lock, flags);
                         level = imp->imp_state;
                         force = imp->imp_force_verify;
-                        if (force)
-                                imp->imp_force_verify = 0;
+                        imp->imp_force_verify = 0;
                         spin_unlock_irqrestore(&imp->imp_lock, flags);
+                        CDEBUG(level == LUSTRE_IMP_FULL ? D_INFO : D_HA,
+                               "level %s/%u force %u deactive %u pingable %u\n",
+                               ptlrpc_import_state_name(level), level,
+                               force, imp->imp_deactive, imp->imp_pingable);
 
                         if (force ||
-                            time_after_eq(this_ping, imp->imp_next_ping)) {
+                            /* if the next ping is within, say, 5 jiffies from
+                               now, go ahead and ping. See note below. */
+                            time_after_eq(this_ping, imp->imp_next_ping - 5)) {
                                 if (level == LUSTRE_IMP_DISCON &&
                                     !imp->imp_deactive) {
                                         /* wait at least a timeout before
                                            trying recovery again. */
-                                        ptlrpc_update_next_ping(imp);
+                                        imp->imp_next_ping = jiffies +
+                                                obd_timeout * HZ;
                                         ptlrpc_initiate_recovery(imp);
-                                }
-                                else if (level != LUSTRE_IMP_FULL ||
-                                         imp->imp_obd->obd_no_recov) {
-                                        CDEBUG(D_HA,
-                                               "not pinging %s (in recovery "
-                                               "or recovery disabled: %s)\n",
+                                } else if (level != LUSTRE_IMP_FULL ||
+                                         imp->imp_obd->obd_no_recov ||
+                                         imp->imp_deactive) {
+                                        CDEBUG(D_HA, "not pinging %s "
+                                               "(in recovery: %s or recovery "
+                                               "disabled: %u/%u)\n",
                                                imp->imp_target_uuid.uuid,
-                                               ptlrpc_import_state_name(level));
-                                }
-                                else if (imp->imp_pingable || force) {
+                                               ptlrpc_import_state_name(level),
+                                               imp->imp_deactive,
+                                               imp->imp_obd->obd_no_recov);
+                                } else if (imp->imp_pingable || force) {
                                         ptlrpc_ping(imp);
                                 }
-
-                        } else if (!imp->imp_pingable) {
-                                continue;
+                        } else {
+                                if (!imp->imp_pingable)
+                                        continue;
+                                CDEBUG(D_INFO,
+                                       "don't need to ping %s (%lu > %lu)\n",
+                                       imp->imp_target_uuid.uuid,
+                                       imp->imp_next_ping, this_ping);
                         }
 
-                        CDEBUG(D_HA, "don't need to ping %s (%lu > %lu)\n",
-                               imp->imp_target_uuid.uuid,
-                               imp->imp_next_ping, this_ping);
-
                         /* obd_timeout might have changed */
                         if (time_after(imp->imp_next_ping,
-                                       this_ping + obd_timeout * HZ))
+                                       this_ping + PING_INTERVAL * HZ))
                                 ptlrpc_update_next_ping(imp);
                 }
                 up(&pinger_sem);
 
                 /* Wait until the next ping time, or until we're stopped. */
-                time_to_next_ping = this_ping + (obd_timeout * HZ) - jiffies;
-                CDEBUG(D_HA, "next ping in %lu (%lu)\n", time_to_next_ping,
-                       this_ping + obd_timeout * HZ);
+                time_to_next_ping = this_ping + (PING_INTERVAL * HZ) - jiffies;
+                /* The ping sent by ptlrpc_send_rpc may get sent out
+                   say .01 second after this.
+                   ptlrpc_pinger_sending_on_import will then set the
+                   next ping time to next_ping + .01 sec, which means
+                   we will SKIP the next ping at next_ping, and the
+                   ping will get sent 2 timeouts from now!  Beware. */
+                CDEBUG(D_INFO, "next ping in %lu (%lu)\n", time_to_next_ping,
+                       this_ping + PING_INTERVAL * HZ);
                 if (time_to_next_ping > 0) {
-                        lwi = LWI_TIMEOUT(time_to_next_ping, NULL, NULL);
+                        lwi = LWI_TIMEOUT(max_t(long, time_to_next_ping, HZ),
+                                          NULL, NULL);
                         l_wait_event(thread->t_ctl_waitq,
                                      thread->t_flags & (SVC_STOPPING|SVC_EVENT),
                                      &lwi);
@@ -336,6 +355,8 @@ static int pinger_check_rpcs(void *arg)
 
         pd->pd_this_ping = curtime;
         pd->pd_set = ptlrpc_prep_set();
+        if (pd->pd_set == NULL)
+                goto out;
         set = pd->pd_set;
 
         /* add rpcs into set */
@@ -346,7 +367,7 @@ static int pinger_check_rpcs(void *arg)
                 int generation, level;
                 unsigned long flags;
 
-                if (time_after_eq(pd->pd_this_ping, imp->imp_next_ping)) {
+                if (time_after_eq(pd->pd_this_ping, imp->imp_next_ping - 5)) {
                         /* Add a ping. */
                         spin_lock_irqsave(&imp->imp_lock, flags);
                         generation = imp->imp_generation;
@@ -399,7 +420,7 @@ do_check_set:
         rc = ptlrpc_check_set(set);
 
         /* not finished, and we are not expired, simply return */
-        if (!rc && time_before(curtime, pd->pd_this_ping + obd_timeout * HZ)) {
+        if (!rc && time_before(curtime, pd->pd_this_ping + PING_INTERVAL * HZ)) {
                 CDEBUG(D_HA, "not finished, but also not expired\n");
                 pd->pd_recursion--;
                 return 0;
@@ -430,7 +451,8 @@ do_check_set:
         ptlrpc_set_destroy(set);
         pd->pd_set = NULL;
 
-        pd->pd_next_ping = pd->pd_this_ping + obd_timeout * HZ;
+out:
+        pd->pd_next_ping = pd->pd_this_ping + PING_INTERVAL * HZ;
         pd->pd_this_ping = 0; /* XXX for debug */
 
         CDEBUG(D_HA, "finished a round ping\n");
index e49b5f9..70dcbf2 100644 (file)
@@ -41,7 +41,7 @@ int ptlrpc_replay_next(struct obd_import *imp, int *inflight);
 void ptlrpc_initiate_recovery(struct obd_import *imp);
 
 
-#ifdef __KERNEL__
+#ifdef LPROCFS
 void ptlrpc_lprocfs_register_service(struct proc_dir_entry *proc_entry,
                                      struct ptlrpc_service *svc);
 void ptlrpc_lprocfs_unregister_service(struct ptlrpc_service *svc);
@@ -117,5 +117,6 @@ int ptlrpc_start_pinger(void);
 int ptlrpc_stop_pinger(void);
 void ptlrpc_pinger_sending_on_import(struct obd_import *imp);
 void ptlrpc_pinger_wake_up(void);
+void ptlrpc_ping_import_soon(struct obd_import *imp);
 
 #endif /* PTLRPC_INTERNAL_H */
index 4629b71..f9c4bdb 100644 (file)
@@ -189,7 +189,6 @@ EXPORT_SYMBOL(ptlrpc_activate_import);
 EXPORT_SYMBOL(ptlrpc_deactivate_import);
 EXPORT_SYMBOL(ptlrpc_invalidate_import);
 EXPORT_SYMBOL(ptlrpc_fail_import);
-EXPORT_SYMBOL(ptlrpc_fail_export);
 EXPORT_SYMBOL(ptlrpc_recover_import);
 
 /* pinger.c */
@@ -203,10 +202,6 @@ EXPORT_SYMBOL(ptlrpcd_decref);
 EXPORT_SYMBOL(ptlrpcd_add_req);
 EXPORT_SYMBOL(ptlrpcd_wake);
 
-/* lproc_ptlrpc.c */
-EXPORT_SYMBOL(ptlrpc_lprocfs_register_obd);
-EXPORT_SYMBOL(ptlrpc_lprocfs_unregister_obd);
-
 /* llogd.c */
 EXPORT_SYMBOL(llog_origin_handle_create);
 EXPORT_SYMBOL(llog_origin_handle_next_block);
index 6d0f118..3a78b18 100644 (file)
@@ -272,6 +272,7 @@ static int log_commit_thread(void *arg)
 
                 sending_list = &lcm->lcm_llcd_pending;
         resend:
+                import = NULL;
                 if (lcm->lcm_flags & LLOG_LCM_FL_EXIT) {
                         lcm->lcm_llcd_maxfree = 0;
                         lcm->lcm_llcd_minfree = 0;
@@ -340,6 +341,13 @@ static int log_commit_thread(void *arg)
                         }
                         up(&llcd->llcd_ctxt->loc_sem);
 
+                        if (!import || (import == LP_POISON)) {
+                                CERROR("No import %p (llcd=%p, ctxt=%p)\n",
+                                       import, llcd, llcd->llcd_ctxt);
+                                llcd_put(llcd);
+                                continue;
+                        }
+
                         request = ptlrpc_prep_req(import, OBD_LOG_CANCEL, 1,
                                                   &llcd->llcd_cookiebytes,
                                                   bufs);
index a5c9e21..e79e567 100644 (file)
@@ -71,7 +71,7 @@ void ptlrpc_run_recovery_over_upcall(struct obd_device *obd)
 
         } else {
                 CWARN("Invoked upcall %s %s %s\n",
-                       argv[0], argv[1], argv[2]);
+                      argv[0], argv[1], argv[2]);
         }
 }
 
@@ -91,7 +91,7 @@ void ptlrpc_run_failed_import_upcall(struct obd_import* imp)
                 return;
         }
         spin_unlock_irqrestore(&imp->imp_lock, flags);
-        
+
         argv[0] = obd_lustre_upcall;
         argv[1] = "FAILED_IMPORT";
         argv[2] = imp->imp_target_uuid.uuid;
@@ -108,8 +108,7 @@ void ptlrpc_run_failed_import_upcall(struct obd_import* imp)
         if (rc < 0) {
                 CERROR("Error invoking recovery upcall %s %s %s %s %s %s: %d; "
                        "check /proc/sys/lustre/lustre_upcall\n",
-                       argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], rc);
-
+                       argv[0], argv[1], argv[2], argv[3], argv[4], argv[5],rc);
         } else {
                 CWARN("Invoked upcall %s %s %s %s %s %s\n",
                       argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]);
@@ -130,17 +129,15 @@ void ptlrpc_initiate_recovery(struct obd_import *imp)
         ENTRY;
 
         LASSERT (obd_lustre_upcall != NULL);
-        
+
         if (strcmp(obd_lustre_upcall, "DEFAULT") == 0) {
                 CDEBUG(D_HA, "%s: starting recovery without upcall\n",
                         imp->imp_target_uuid.uuid);
                 ptlrpc_connect_import(imp, NULL);
-        } 
-        else if (strcmp(obd_lustre_upcall, "NONE") == 0) {
+        } else if (strcmp(obd_lustre_upcall, "NONE") == 0) {
                 CDEBUG(D_HA, "%s: recovery disabled\n",
                         imp->imp_target_uuid.uuid);
-        } 
-        else {
+        } else {
                 CDEBUG(D_HA, "%s: calling upcall to start recovery\n",
                         imp->imp_target_uuid.uuid);
                 ptlrpc_run_failed_import_upcall(imp);
@@ -271,7 +268,6 @@ void ptlrpc_wake_delayed(struct obd_import *imp)
 
 void ptlrpc_request_handle_notconn(struct ptlrpc_request *failed_req)
 {
-        int rc;
         struct obd_import *imp= failed_req->rq_import;
         unsigned long flags;
         ENTRY;
@@ -290,8 +286,7 @@ void ptlrpc_request_handle_notconn(struct ptlrpc_request *failed_req)
                                imp->imp_obd->obd_name);
                         ptlrpc_deactivate_import(imp);
                 }
-
-                rc = ptlrpc_connect_import(imp, NULL);
+                ptlrpc_connect_import(imp, NULL);
         }
 
         /* Wait for recovery to complete and resend. If evicted, then
@@ -318,9 +313,11 @@ int ptlrpc_set_import_active(struct obd_import *imp, int active)
         /* When deactivating, mark import invalid, and abort in-flight
          * requests. */
         if (!active) {
+                CWARN("setting import %s INACTIVE by administrator request\n",
+                      imp->imp_target_uuid.uuid);
                 ptlrpc_invalidate_import(imp);
                 imp->imp_deactive = 1;
-        } 
+        }
 
         /* When activating, mark import valid, and attempt recovery */
         if (active) {
@@ -341,6 +338,7 @@ int ptlrpc_recover_import(struct obd_import *imp, char *new_uuid)
         /* force import to be disconnected. */
         ptlrpc_set_import_discon(imp);
 
+        imp->imp_deactive = 0;
         rc = ptlrpc_recover_import_no_retry(imp, new_uuid);
 
         RETURN(rc);
@@ -392,34 +390,3 @@ static int ptlrpc_recover_import_no_retry(struct obd_import *imp,
 
         RETURN(rc);
 }
-
-void ptlrpc_fail_export(struct obd_export *exp)
-{
-        int rc, already_failed;
-        unsigned long flags;
-
-        spin_lock_irqsave(&exp->exp_lock, flags);
-        already_failed = exp->exp_failed;
-        exp->exp_failed = 1;
-        spin_unlock_irqrestore(&exp->exp_lock, flags);
-
-        if (already_failed) {
-                CDEBUG(D_HA, "disconnecting dead export %p/%s; skipping\n",
-                       exp, exp->exp_client_uuid.uuid);
-                return;
-        }
-
-        CDEBUG(D_HA, "disconnecting export %p/%s\n",
-               exp, exp->exp_client_uuid.uuid);
-
-        if (obd_dump_on_timeout)
-                portals_debug_dumplog();
-
-        /* Most callers into obd_disconnect are removing their own reference
-         * (request, for example) in addition to the one from the hash table.
-         * We don't have such a reference here, so make one. */
-        class_export_get(exp);
-        rc = obd_disconnect(exp);
-        if (rc)
-                CERROR("disconnecting export %p failed: %d\n", exp, rc);
-}
index 9b68cf3..321ac50 100644 (file)
@@ -417,6 +417,8 @@ ptlrpc_server_handle_request (struct ptlrpc_service *svc)
         int                    rc;
         ENTRY;
 
+        LASSERT(svc);
+
         spin_lock_irqsave (&svc->srv_lock, flags);
         if (list_empty (&svc->srv_request_queue) ||
             (svc->srv_n_difficult_replies != 0 &&
@@ -469,17 +471,6 @@ ptlrpc_server_handle_request (struct ptlrpc_service *svc)
 
         CDEBUG(D_NET, "got req "LPD64"\n", request->rq_xid);
 
-        /* Discard requests queued for longer than my timeout.  If the
-         * client's timeout is similar to mine, she'll be timing out this
-         * REQ anyway (bug 1502) */
-        if (timediff / 1000000 > (long)obd_timeout) {
-                CERROR("Dropping timed-out opc %d request from %s"
-                       ": %ld seconds old\n", request->rq_reqmsg->opc,
-                       libcfs_id2str(request->rq_peer),
-                       timediff / 1000000);
-                goto out;
-        }
-
         request->rq_export = class_conn2export(&request->rq_reqmsg->handle);
 
         if (request->rq_export) {
@@ -502,7 +493,19 @@ ptlrpc_server_handle_request (struct ptlrpc_service *svc)
                         goto put_conn;
                 }
 
-                request->rq_export->exp_last_request_time = CURRENT_SECONDS;
+                class_update_export_timer(request->rq_export, 
+                                          (time_t)(timediff / 1000000));
+        }
+
+        /* Discard requests queued for longer than my timeout.  If the
+         * client's timeout is similar to mine, she'll be timing out this
+         * REQ anyway (bug 1502) */
+        if (timediff / 1000000 > (long)obd_timeout) {
+                CERROR("Dropping timed-out opc %d request from %s"
+                       ": %ld seconds old\n", request->rq_reqmsg->opc,
+                       libcfs_id2str(request->rq_peer),
+                       timediff / 1000000);
+                goto put_conn;
         }
 
         request->rq_phase = RQ_PHASE_INTERPRET;
@@ -695,6 +698,7 @@ liblustre_check_services (void *arg)
 
         RETURN(did_something);
 }
+#define ptlrpc_stop_all_threads(s) do {} while (0)
 
 #else /* __KERNEL__ */
 
index c39784f..0b2290a 100644 (file)
@@ -68,4 +68,5 @@ rename_many
 memhog
 mmap_sanity
 rmdirmany
+flock_test
 writemany
diff --git a/lustre/tests/2ost.sh b/lustre/tests/2ost.sh
new file mode 100644 (file)
index 0000000..1f890fb
--- /dev/null
@@ -0,0 +1,52 @@
+#!/bin/bash
+
+export PATH=`dirname $0`/../utils:$PATH
+
+config=${1:-`basename $0 .sh`.xml}
+
+LMC="${LMC:-lmc} -m $config"
+TMP=${TMP:-/tmp}
+
+MDSDEV=${MDSDEV:-$TMP/mds1-`hostname`}
+MDSSIZE=${MDSSIZE:-400000}
+FSTYPE=${FSTYPE:-ext3}
+MOUNT=${MOUNT:-/mnt/lustre}
+MOUNT2=${MOUNT2:-${MOUNT}2}
+NETTYPE=${NETTYPE:-tcp}
+
+OSTDEV=${OSTDEV:-$TMP/ost-`hostname`}
+OSTSIZE=${OSTSIZE:-400000}
+
+# specific journal size for the ost, in MB
+JSIZE=${JSIZE:-0}
+[ "$JSIZE" -gt 0 ] && JARG="--journal_size $JSIZE"
+MDSISIZE=${MDSISIZE:-0}
+[ "$MDSISIZE" -gt 0 ] && IARG="--inode_size $MDSISIZE"
+
+STRIPE_BYTES=${STRIPE_BYTES:-1048576}
+STRIPES_PER_OBJ=0      # 0 means stripe over all OSTs
+
+rm -f $config
+
+# create nodes
+${LMC} --add node --node localhost || exit 10
+${LMC} --add net --node  localhost --nid `hostname` --nettype $NETTYPE || exit 11
+${LMC} --add net --node client --nid '*' --nettype $NETTYPE || exit 12
+
+# configure mds server
+${LMC} --add mds --node localhost --mds mds1 --fstype $FSTYPE \
+       --dev $MDSDEV --size $MDSSIZE $JARG $IARG $MDSOPT || exit 20
+
+# configure ost
+${LMC} -m $config --add lov --lov lov1 --mds mds1 --stripe_sz $STRIPE_BYTES \
+       --stripe_cnt $STRIPES_PER_OBJ --stripe_pattern 0 $LOVOPT || exit 20
+${LMC} --add ost --ost ost1 --node localhost --lov lov1 \
+       --fstype $FSTYPE --dev $OSTDEV --size $OSTSIZE $JARG $OSTOPT || exit 30
+${LMC} --add ost --ost ost2 --node localhost --lov lov1 \
+       --fstype $FSTYPE --dev ${OSTDEV}2 --size $OSTSIZE $JARG $OSTOPT || exit 30
+
+# create client config
+${LMC} --add mtpt --node localhost --path $MOUNT --mds mds1 --lov lov1 \
+       $CLIENTOPT || exit 40
+${LMC} --add mtpt --node client --path $MOUNT2 --mds mds1 --lov lov1 \
+       $CLIENTOPT || exit 41
index a34db0b..280f81d 100755 (executable)
@@ -24,6 +24,8 @@ fi
 [ "$DEBUG_OFF" ] || DEBUG_OFF="eval echo $DEBUG_LVL > /proc/sys/portals/debug"
 [ "$DEBUG_ON" ] || DEBUG_ON="eval echo -1 > /proc/sys/portals/debug"
 
+LIBLUSTRETESTS=${LIBLUSTRETESTS:-../liblustre/tests}
+
 for NAME in $CONFIGS; do
        export NAME MOUNT START CLEAN
        [ -e $NAME.sh ] && sh $NAME.sh
@@ -40,7 +42,7 @@ for NAME in $CONFIGS; do
 
        if [ "$DBENCH" != "no" ]; then
                mount | grep $MOUNT || sh llmount.sh
-               SPACE=`df $MOUNT | tail -n 1 | awk '{ print $4 }'`
+               SPACE=`df -P $MOUNT | tail -n 1 | awk '{ print $4 }'`
                DB_THREADS=`expr $SPACE / 50000`
                [ $THREADS -lt $DB_THREADS ] && DB_THREADS=$THREADS
 
@@ -69,44 +71,45 @@ for NAME in $CONFIGS; do
        fi
        IOZONE_OPTS="-i 0 -i 1 -i 2 -+d -r $RSIZE -s $SIZE"
        if [ "$O_DIRECT" -a  "$O_DIRECT" != "no" ]; then
-           IOZONE_OPTS="-I $IOZONE_OPTS"
+               IOZONE_OPTS="-I $IOZONE_OPTS"
        fi
-       IOZONE_FILE="-f $MOUNT/iozone"
+       IOZFILE="-f $MOUNT/iozone"
        if [ "$IOZONE" != "no" ]; then
                mount | grep $MOUNT || sh llmount.sh
                $DEBUG_OFF
-               iozone $IOZONE_OPTS $IOZONE_FILE
+               iozone $IOZONE_OPTS $IOZFILE
                $DEBUG_ON
                sh llmountcleanup.sh
                sh llrmount.sh
-       fi
-       if [ "$IOZONE_DIR" != "no" ]; then
-               mount | grep $MOUNT || sh llmount.sh
-               SPACE=`df $MOUNT | tail -n 1 | awk '{ print $4 }'`
-               IOZ_THREADS=`expr $SPACE / \( $SIZE + $SIZE / 512 \)`
-               [ $THREADS -lt $IOZ_THREADS ] && IOZ_THREADS=$THREADS
 
-               $DEBUG_OFF
-               iozone $IOZONE_OPTS $IOZONE_FILE.odir
-               IOZVER=`iozone -v | awk '/Revision:/ { print $3 }' | tr -d '.'`
-               $DEBUG_ON
-               sh llmountcleanup.sh
-               sh llrmount.sh
-               if [ "$IOZ_THREADS" -gt 1 -a "$IOZVER" -ge 3145 ]; then
+               if [ "$IOZONE_DIR" != "no" ]; then
+                       mount | grep $MOUNT || sh llmount.sh
+                       SPACE=`df -P $MOUNT | tail -n 1 | awk '{ print $4 }'`
+                       IOZ_THREADS=`expr $SPACE / \( $SIZE + $SIZE / 512 \)`
+                       [ $THREADS -lt $IOZ_THREADS ] && IOZ_THREADS=$THREADS
+
                        $DEBUG_OFF
-                       THREAD=1
-                       IOZONE_FILE="-F "
-                       while [ $THREAD -le $IOZ_THREADS ]; do
-                               IOZONE_FILE="$IOZONE_FILE $MOUNT/iozone.$THREAD"
-                               THREAD=`expr $THREAD + 1`
-                       done
-                       iozone $IOZONE_OPTS -t $IOZ_THREADS $IOZONE_FILE
+                       iozone $IOZONE_OPTS $IOZFILE.odir
+                       IOZVER=`iozone -v|awk '/Revision:/ {print $3}'|tr -d .`
                        $DEBUG_ON
                        sh llmountcleanup.sh
                        sh llrmount.sh
-               elif [ $IOZVER -lt 3145 ]; then
-                       VER=`iozone -v | awk '/Revision:/ { print $3 }'`
-                       echo "iozone $VER too old for multi-threaded tests"
+                       if [ "$IOZ_THREADS" -gt 1 -a "$IOZVER" -ge 3145 ]; then
+                               $DEBUG_OFF
+                               THREAD=1
+                               IOZFILE="-F "
+                               while [ $THREAD -le $IOZ_THREADS ]; do
+                                       IOZFILE="$IOZFILE $MOUNT/iozone.$THREAD"
+                                       THREAD=`expr $THREAD + 1`
+                               done
+                               iozone $IOZONE_OPTS -t $IOZ_THREADS $IOZFILE
+                               $DEBUG_ON
+                               sh llmountcleanup.sh
+                               sh llrmount.sh
+                       elif [ $IOZVER -lt 3145 ]; then
+                               VER=`iozone -v | awk '/Revision:/ { print $3 }'`
+                               echo "iozone $VER too old for multi-thread test"
+                       fi
                fi
        fi
        if [ "$FSX" != "no" ]; then
@@ -118,20 +121,22 @@ for NAME in $CONFIGS; do
                sh llmountcleanup.sh
                sh llrmount.sh
        fi      
+
+       mkdir -p $MOUNT2
+       case $NAME in
+       local|lov)
+               MDSNODE=`hostname`
+               MDSNAME=mds1
+               CLIENT=client
+               ;;
+       *)      # we could extract this from $NAME.xml somehow
+               ;;
+       esac
+
        if [ "$SANITYN" != "no" ]; then
                mount | grep $MOUNT || sh llmount.sh
                $DEBUG_OFF
 
-               mkdir -p $MOUNT2
-               case $NAME in
-               local|lov)
-                       MDSNODE=`hostname`
-                       MDSNAME=mds1
-                       CLIENT=client
-                       ;;
-               *)      # we could extract this from $NAME.xml somehow
-                       ;;
-               esac
                if [ "$MDSNODE" -a "$MDSNAME" -a "$CLIENT" ]; then
                        llmount $MDSNODE:/$MDSNAME/$CLIENT $MOUNT2
                        SANITYLOG=$TMP/sanity.log START=: CLEAN=: sh sanityN.sh
@@ -143,6 +148,18 @@ for NAME in $CONFIGS; do
 
                $DEBUG_ON
                sh llmountcleanup.sh
+               sh llrmount.sh
+       fi
+
+       if [ "$LIBLUSTRE" != "no" ]; then
+               mount | grep $MOUNT || sh llmount.sh
+               IPADDR=`ping -c 1 $MDSNODE|head -n 1|sed -e "s/[^(]*(//" -e "s/).*//"`
+               export ENV_LUSTRE_MNTPNT=$MOUNT2
+               export ENV_LUSTRE_MNTTGT=$IPADDR:/$MDSNAME/$CLIENT
+               if [ -x $LIBLUSTRETESTS/sanity ]; then
+                       $LIBLUSTRETESTS/sanity --target=$ENV_LUSTRE_MNTTGT
+               fi
+               sh llmountcleanup.sh
                #sh llrmount.sh
        fi
 
index 71ff93b..924587a 100644 (file)
@@ -20,9 +20,9 @@ SUBSYSTEM=${SUBSYSTEM:- 0xffb7e3ff}
 PDSH=${PDSH:-no_dsh}
 
 MDSDEV=${MDSDEV:-$ROOT/tmp/mds1-`hostname`}
-MDSSIZE=${MDSSIZE:-10000}
+MDSSIZE=${MDSSIZE:-100000}
 OSTDEV=${OSTDEV:-$ROOT/tmp/ost1-`hostname`}
-OSTSIZE=${OSTSIZE:-50000}
+OSTSIZE=${OSTSIZE:-200000}
 FSTYPE=${FSTYPE:-ext3}
 TIMEOUT=${TIMEOUT:-20}
 UPCALL=${UPCALL:-DEFAULT}
index 38031e1..c7f7674 100644 (file)
@@ -16,9 +16,9 @@ SUBSYSTEM=${SUBSYSTEM:- 0xffb7e3ff}
 PDSH=${PDSH:-pdsh -S -w}
 
 MDSDEV=${MDSDEV:-/dev/sda1}
-MDSSIZE=${MDSSIZE:-50000}
+MDSSIZE=${MDSSIZE:-100000}
 OSTDEV=${OSTDEV:-/tmp/ost1-`hostname`}
-OSTSIZE=${OSTSIZE:-20000}
+OSTSIZE=${OSTSIZE:-200000}
 FSTYPE=${FSTYPE:-ext3}
 TIMEOUT=${TIMEOUT:-10}
 #UPCALL=${UPCALL:-$PWD/replay-single-upcall.sh}
index b937c17..1d90308 100755 (executable)
@@ -31,6 +31,13 @@ h2gm () {
 h2elan () {
        echo $1 | sed 's/[^0-9]*//g'
 }
+
+h2iib () {
+        case $1 in
+        client) echo '\*' ;;
+        *) echo $1 | sed "s/[^0-9]*//" ;;
+        esac
+}
         
 # FIXME: make LMC not require MDS for obdecho LOV
 MDSDEV=${MDSDEV:-$TMP/mds1-`hostname`}
index 49a962b..90ef09d 100755 (executable)
@@ -26,12 +26,11 @@ else
 fi    
 
 [ "$NODE" ] && node_opt="--node $NODE"
+[ "$DEBUG" ] && debug_opt="--ptldebug=$DEBUG"
 
-${LCONF} $NOMOD $portals_opt $lustre_opt $node_opt ${REFORMAT:---reformat} $@ \
+${LCONF} $NOMOD $portals_opt $lustre_opt $debug_opt $node_opt ${REFORMAT:---reformat} $@ \
        $conf_opt  || exit 2
 
-[ $DEBUG ] && sysctl -w portals.debug=$DEBUG
-
 if [ "$MOUNT2" ]; then
        $LLMOUNT -v `hostname`:/mds1/client $MOUNT2 || exit 3
 fi
index 4783833..afbbfad 100755 (executable)
@@ -16,7 +16,7 @@ MOUNT=${MOUNT:-/mnt/lustre}
 MOUNT2=${MOUNT2:-${MOUNT}2}
 NETTYPE=${NETTYPE:-tcp}
 
-OSTCOUNT=${OSTCOUNT:-2}
+OSTCOUNT=${OSTCOUNT:-5}
 # OSTDEVN will still override the device for OST N
 
 OSTSIZE=${OSTSIZE:-150000}
diff --git a/lustre/tests/mcr-individual-ost-nogw-config.sh b/lustre/tests/mcr-individual-ost-nogw-config.sh
deleted file mode 100755 (executable)
index 0401bf5..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/bin/bash
-
-config=${1:-echo-no-gw.xml}
-
-LMC="save_cmd"
-LMC_REAL="../../lustre/utils/lmc -m $config"
-
-# TCP/IP servers
-SERVER_START=0
-SERVER_CNT=62
-
-TCPBUF=1048576
-h2tcp () {
-    echo "${1}"
-}
-BATCH=/tmp/lmc-batch.$$
-save_cmd() {
-    echo "$@" >> $BATCH
-}
-
-[ -f $config ] && rm $config
-
-# Client node
-${LMC} --add net --node client --tcpbuf $TCPBUF --nid '*' --nettype tcp || exit 1
-
-# this is crude, but effective
-let server_per_gw=($SERVER_CNT / $GW_CNT )
-let tot_server=$server_per_gw*$GW_CNT
-
-let server=$SERVER_START
-while (( $server < $SERVER_CNT + SERVER_START ));
-do 
-      echo "server: $server"
-      OST=ba$server
-      # server node
-      ${LMC} --add net --node $OST --tcpbuf $TCPBUF --nid $OST --nettype tcp || exit 1
-      # the device on the server
-      ${LMC} --add ost --node $OST --obd obd_$OST --obdtype=obdecho || exit 3
-      # osc on client
-      ${LMC} --add oscref --node client --osc OSC_obd_$OST
-      let server=$server+1 
-done
-
-$LMC_REAL --batch $BATCH
-rm -f $BATCH
diff --git a/lustre/tests/mcr-mds-failover-config.sh b/lustre/tests/mcr-mds-failover-config.sh
deleted file mode 100755 (executable)
index 29ec215..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-#!/bin/sh
-
-LMC=/usr/local/cfs/lustre/utils/lmc
-# LMC="echo lmc"
-CONFIG=mcr-mds-failover.xml
-LUSTRE_QUERY=/usr/local/cfs/lustre-failover/lustre-query
-GW_NODE=mcr21
-CLIENT_ELAN=`hostname | sed s/[^0-9]*//;`
-OST=${OST:-ba50}
-UUIDLIST=${UUIDLIST:-/usr/local/admin/ba-ost/UUID.txt}
-OST_UUID=`awk "/$OST / { print \\$3 }" $UUIDLIST`
-[ "$OST_UUID" ] && OST_UUID="--ostuuid=$OST_UUID" || echo "$OST: no UUID"
-MDS_DEVICE=/dev/sda3
-MDS_SIZE=500000
-TCPBUF=1048576
-
-MDSNODES=`$LUSTRE_QUERY -h emcri -s id=mds -f`
-ACTIVEMDS=`$LUSTRE_QUERY -h emcri -s id=mds -a`
-
-echo "MDS nodes: $MDSNODES, active: $ACTIVEMDS"
-
-h2elan () {
-    echo $1 | sed 's/[^0-9]*//g'
-}
-
-h2tcp () {
-    echo "${1}"
-}
-
-
-# create client node
-$LMC -o $CONFIG --add net --node client --nid '*' --nettype elan
-$LMC -m $CONFIG --add net --router --node mcr21 --tcpbuf $TCPBUF --nid `h2tcp $GW_NODE` --nettype tcp
-$LMC -m $CONFIG --add net --router --node mcr21 --nid `h2elan $GW_NODE` --nettype elan
-$LMC -m $CONFIG --add route --node $GW_NODE --nettype elan --gw `h2elan $GW_NODE` --lo $CLIENT_ELAN 
-
-# create MDS node entries
-for mds in $MDSNODES; do
-  elanaddr=`$LUSTRE_QUERY -h emcri -s id=$mds -e`
-  $LMC -m $CONFIG --add net --node $mds --nid $elanaddr --nettype elan
-  $LMC -m $CONFIG --add mds --node $mds --mds mds_$mds --dev $MDS_DEVICE --size $MDS_SIZE
-done
-
-# create OST node entry
-$LMC -m $CONFIG --add net --node $OST --tcpbuf $TCPBUF --nid $OST --nettype tcp
-$LMC -m $CONFIG --add ost --node $OST --ost ost_$OST $OST_UUID --dev bluearc
-$LMC -m $CONFIG --add route --node $GW_NODE --nettype tcp --gw `h2tcp $GW_NODE` --lo $OST
-
-# mount
-$LMC -m $CONFIG --add mtpt --node client --path /mnt/lustre --mds mds_$ACTIVEMDS --lov ost_$OST
diff --git a/lustre/tests/mcr-routed-config.sh b/lustre/tests/mcr-routed-config.sh
deleted file mode 100755 (executable)
index 8d8a100..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-#!/bin/bash
-
-BASE=`hostname | sed "s/[i0-9]*$//"`
-[ $BASE = "mcr" ] && OSTBASE=${OSTBASE:-ba} || OSTBASE=${OSTBASE:-ba-ost-}
-
-config=${1:-$BASE.xml}
-
-BATCH=/tmp/lmc-batch.$$
-save_cmd() {
-    echo "$@" >> $BATCH
-}
-
-LMC="save_cmd"
-LMC_REAL="../utils/lmc -m $config"
-
-# TCP/IP servers
-SERVER_START=0
-SERVER_CNT=32
-GW_START=0
-GW_CNT=16
-MDS=${BASE}23
-UUIDLIST=${UUIDLIST:-/usr/local/admin/ba-ost/UUID.txt}
-
-echo "MDS: $MDS"
-
-# This is needed for to create route for elan network
-CLIENT_LO=38
-CLIENT_HI=191
-
-TCPBUF=1048576
-h2elan () {
-    echo $1 | sed 's/[^0-9]*//g'
-}
-
-h2tcp () {
-    echo "${1}"
-}
-
-# map gateway NN to host NN (assumes mcr[22-25] are not gateways)
-gw2node() {
-       [ $1 -gt 21 ] && echo $(($1 + 4)) || echo $1
-}
-
-[ -f $config ] && rm $config
-
-${LMC} --add net --node $MDS --nid `h2elan $MDS` --nettype elan || exit 1
-${LMC} --add mds --node $MDS --mds mds1 --dev /tmp/mds1 --size 100000 || exit 1
-${LMC} --add lov --lov lov1 --mds mds1 --stripe_sz 65536 --stripe_cnt 1 --stripe_pattern 0
-
-# Client node
-#${LMC} --add net --node client --tcpbuf $TCPBUF --nid '*' --nettype tcp || exit 1
-${LMC} --add net --node client --nid '*' --nettype elan || exit 1
-${LMC} --add mtpt --node client --path /mnt/lustre --mds mds1 --lov lov1
-
-# this is crude, but effective
-let server_per_gw=($SERVER_CNT / $GW_CNT )
-let tot_server=$server_per_gw*$GW_CNT
-echo "Allocating $server_per_gw OSTs per gateway."
-echo "For a total of $tot_server Blue Arc OSTs"
-
-let gw=$GW_START
-let server=$SERVER_START
-while (( $gw < $GW_CNT + GW_START ));
-do 
-   gwnode=$BASE`gw2node $gw`
-   echo "Router: $gwnode"
-   ${LMC} --add net --router --node $gwnode --tcpbuf $TCPBUF --nid `h2tcp $gwnode`  --nettype tcp || exit 1
-   ${LMC} --add net --node $gwnode --nid `h2elan $gwnode` --nettype elan || exit 1
-   ${LMC} --add route --node $gwnode --nettype elan --gw `h2elan $gwnode` --lo `h2elan $CLIENT_LO` --hi `h2elan $CLIENT_HI` || exit 2
-
-   let  i=0
-   while (( $i < $server_per_gw ));
-   do
-      OST=${OSTBASE}$server
-      echo "server: $OST"
-      OST_UUID=`awk "/$OST / { print \\$3 }" $UUIDLIST`
-      [ "$OST_UUID" ] && OST_UUID="--ostuuid $OST_UUID" || echo "$OST: no UUID"
-      # server node
-      ${LMC} --add net --node $OST --tcpbuf $TCPBUF --nid $OST --nettype tcp || exit 1
-      # the device on the server
-      ${LMC} --add ost --lov lov1 --node $OST $OBD_UUID --dev bluearc || exit 3
-      # route to server
-      ${LMC} --add route --node $gwnode --nettype tcp --gw `h2tcp $gwnode` --lo $OST || exit 2
-      let server=$server+1 
-      let i=$i+1
-   done
-
-   let gw=$gw+1
-done
-
-$LMC_REAL --batch $BATCH
-rm -f $BATCH
diff --git a/lustre/tests/mcrlov.sh b/lustre/tests/mcrlov.sh
deleted file mode 100755 (executable)
index cce8878..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-#!/bin/bash
-
-config=${1:-mcrlov.xml}
-
-LMC="../utils/lmc -m $config"
-
-# TCP/IP servers
-SERVERS="ba-ost-1  ba-ost-2"
-ROUTER=dev5
-MDS=dev7
-TMP=${TMP:-/tmp}
-
-# Elan clients
-CLIENT_LO=dev2
-CLIENT_HI=dev25
-
-TCPBUF=1048576
-
-h2elan () {
-    echo $1 | sed 's/[^0-9]*//g'
-}
-
-h2tcp () {
-    echo "${1}"
-}
-
-[ -f $config ] && rm $config
-
-# Client node
-${LMC} --add net --node client --nid '*' --nettype elan || exit 1
-# Router node
-${LMC} --add net --router --node $ROUTER --tcpbuf $TCPBUF --nid `h2tcp $ROUTER`  --nettype tcp || exit 1
-${LMC} --add net --node $ROUTER --nid `h2elan $ROUTER` --nettype elan|| exit 1
-${LMC} --add route --node $ROUTER --gw `h2elan $ROUTER` --lo `h2elan $CLIENT_LO` --hi `h2elan $CLIENT_HI` --nettype elan || exit 2
-
-${LMC} --add net --node $MDS --nid `h2elan $MDS` --nettype elan || exit 1
-${LMC} --add mds --node $MDS --mds mds1 --dev $TMP/mds1 --size 100000 || exit 1
-${LMC} --add lov --lov lov1 --mds mds1 --stripe_sz 65536 --stripe_cnt 0 --stripe_pattern 0 || exit 1
-
-${LMC} --add mtpt --node client --path /mnt/lustre --mds mds1 --lov lov1
-
-for s in $SERVERS
- do
-   # server node
-   ${LMC} --add net --node $s --tcpbuf $TCPBUF --nid $s --nettype tcp || exit 1
-   # route to server
-   ${LMC} --add route --node $ROUTER --nettype tcp --gw `h2tcp $ROUTER` --lo $s || exit 2
-   # the device on the server
-   #${LMC} --format --lov lov1 --node $s --ost bluearc || exit 3
-   ${LMC} --add ost  --lov lov1 --node $s --dev bluearc --format || exit 3
-done
index cd51424..949f447 100644 (file)
@@ -15,21 +15,22 @@ MOUNT2=${MOUNT2:-${MOUNT}2}
 MDSSIZE=50000
 FSTYPE=${FSTYPE:-ext3}
 
+STRIPE_BYTES=${STRIPE_BYTES:-1048576}
 OSTDEV1=${OSTDEV1:-$TMP/ost1-`hostname`}
 OSTDEV2=${OSTDEV2:-$TMP/ost2-`hostname`}
 OSTSIZE=100000
 
 MDSNODE=${MDSNODE:-uml1}
-OSTNODE=${OSTNODE:-uml1}
-CLIENT=${CLIENT:-uml2}
-CLIENT2=${CLIENT2:-uml2}
+OSTNODE=${OSTNODE:-uml2}
+CLIENT=${CLIENT:-client1}
+CLIENT2=${CLIENT2:-client2}
 
 # create nodes
 ${LMC} -o $config --add net --node $MDSNODE --nid $MDSNODE --nettype tcp || exit 1
 ${LMC} -m $config --add net --node $OSTNODE --nid $OSTNODE --nettype tcp || exit 2
-${LMC} -m $config --add net --node $CLIENT --nid $CLIENT --nettype tcp || exit 3
+${LMC} -m $config --add net --node $CLIENT --nid '*' --nettype tcp || exit 3
 if [ "$CLIENT" != "$CLIENT2" ]; then
-       ${LMC} -m $config --add net --node $CLIENT2 --nid $CLIENT --nettype tcp || exit 3
+       ${LMC} -m $config --add net --node $CLIENT2 --nid '*' --nettype tcp || exit 3
 fi
 
 # configure mds server
@@ -37,8 +38,8 @@ ${LMC} -m $config --add mds --node $MDSNODE --mds mds1 --group fs1 --fstype $FST
 ${LMC} -m $config --add mds --node $MDSNODE --mds mds2 --group fs2 --fstype $FSTYPE --dev $MDSDEV2 --size $MDSSIZE ||exit 10
 
 # configure ost
-${LMC} -m $config --add lov --lov lov1 --mds mds1 --stripe_sz 65536 --stripe_cnt 0 --stripe_pattern 0 || exit 20
-${LMC} -m $config --add lov --lov lov2 --mds mds2 --stripe_sz 65536 --stripe_cnt 0 --stripe_pattern 0 || exit 20
+${LMC} -m $config --add lov --lov lov1 --mds mds1 --stripe_sz $STRIPE_BYTES --stripe_cnt 0 --stripe_pattern 0 || exit 20
+${LMC} -m $config --add lov --lov lov2 --mds mds2 --stripe_sz $STRIPE_BYTES --stripe_cnt 0 --stripe_pattern 0 || exit 20
 ${LMC} -m $config --add ost --node $OSTNODE --group fs1 --lov lov1 --fstype $FSTYPE --dev $OSTDEV1 --size $OSTSIZE || exit 21
 ${LMC} -m $config --add ost --node $OSTNODE --group fs2 --lov lov2 --fstype $FSTYPE --dev $OSTDEV2 --size $OSTSIZE || exit 22
 
index 9e08890..907f853 100755 (executable)
@@ -67,7 +67,7 @@ fi
 RECORDSOUT=`grep "records out" $LOG | cut -d + -f1`
 
 FILESIZE=`ls -l $OOS | awk '{ print $5 }'`
-if [ "$RECORDSOUT" -ne $(($FILESIZE / 1024)) ]; then
+if [ "$RECORDSOUT" -ne $((FILESIZE / 1024)) ]; then
         echo "ERROR: blocks written by dd not equal to the size of file"
         SUCCESS=0
 fi
index 9261668..59f1b72 100644 (file)
@@ -13,10 +13,12 @@ USER="quota_usr"
 TSTID=${TSTID:-60000}
 RUNAS=${RUNAS:-"runas -u $TSTID"}
 BLK_SZ=1024
-BUNIT_SZ=10 # 10 quota blocks
-BTUNE_SZ=5  # 5 quota blocks
+BUNIT_SZ=1000 # 1000 quota blocks
+BTUNE_SZ=500  # 500 quota blocks
 IUNIT_SZ=10 # 10 files
 ITUNE_SZ=5  # 5 files
+MAX_DQ_TIME=604800
+MAX_IQ_TIME=604800
 
 MOUNT="`cat /proc/mounts | grep "lustre" | awk '{print $2}'`"
 if [ -z "$MOUNT" ]; then
@@ -26,37 +28,46 @@ fi
 OSTCOUNT=`cat /proc/fs/lustre/lov/*/activeobd | head -n 1`
 TSTDIR="$MOUNT/quota_dir"
 
-# set_blk_tunables(bunit_sz, btune_sz)
-set_blk_tunables() {
-       # set bunit and btune size on all obdfilters
+# set_blk_tunables(btune_sz)
+set_blk_tunesz() {
+       # set btune size on all obdfilters
        for i in `ls /proc/fs/lustre/obdfilter/*/quota_btune_sz`; do
-               echo $(($2 * $BLK_SZ)) > $i
+               echo $(($1 * $BLK_SZ)) > $i
+       done
+       # set btune size on mds
+       for i in `ls /proc/fs/lustre/mds/mds*/quota_btune_sz`; do
+               echo $(($1 * $BLK_SZ)) > $i
        done
+}
+
+# se_blk_unitsz(bunit_sz)
+set_blk_unitsz() {
        for i in `ls /proc/fs/lustre/obdfilter/*/quota_bunit_sz`; do
                echo $(($1 * $BLK_SZ)) > $i
-       done;
-       # set bunit and btune size on mds
-       for i in `ls /proc/fs/lustre/mds/mds*/quota_btune_sz`; do
-               echo $(($2 * $BLK_SZ)) > $i
        done
        for i in `ls /proc/fs/lustre/mds/mds*/quota_bunit_sz`; do
                echo $(($1 * $BLK_SZ)) > $i
        done
 }
 
-# set_file_tunables(iunit_sz, itune_sz)
-set_file_tunables() {
+# set_file_tunesz(itune_sz)
+set_file_tunesz() {
        # set iunit and itune size on all obdfilters
        for i in `ls /proc/fs/lustre/obdfilter/*/quota_itune_sz`; do
-               echo $2 > $i
-       done
-       for i in `ls /proc/fs/lustre/obdfilter/*/quota_iunit_sz`; do
                echo $1 > $i
-       done;
+       done
        # set iunit and itune size on mds
        for i in `ls /proc/fs/lustre/mds/mds*/quota_itune_sz`; do
-               echo $2 > $i
+               echo $1 > $i
        done
+
+
+}
+# set_file_unitsz(iunit_sz)
+set_file_unitsz() {
+       for i in `ls /proc/fs/lustre/obdfilter/*/quota_iunit_sz`; do
+               echo $1 > $i
+       done;
        for i in `ls /proc/fs/lustre/mds/mds*/quota_iunit_sz`; do
                echo $1 > $i
        done
@@ -77,27 +88,29 @@ prepare_test() {
        fi
        
        RUNAS="runas -u $TSTID"
+       
        # set block tunables
-
-       set_blk_tunables $BUNIT_SZ $BTUNE_SZ
+       set_blk_tunesz $BTUNE_SZ
+       set_blk_unitsz $BUNIT_SZ
        # set file tunaables
-       set_file_tunables $IUNIT_SZ $ITUNE_SZ
+       set_file_tunesz $ITUNE_SZ
+       set_file_unitsz $IUNIT_SZ
 
        [ -d $TSTDIR ] || mkdir $TSTDIR 
        chmod 777 $TSTDIR
 }
 
-cleanup_test() {
-       # delete test user and group
-       userdel "$USER"
-       groupdel "$USER"
-       
+cleanup_test() {       
        # restore block tunables to default size
-       set_blk_tunables $((1024 * 100)) $((1024 * 50))
+       set_blk_unitsz $((1024 * 100))
+       set_blk_tunesz $((1024 * 50))
        # restore file tunables to default size
-       set_file_tunables 5000 2500 
+       set_file_unitsz 5000
+       set_file_tunesz 2500
 
        rm -fr $TSTDIR
+       # delete test user and group
+       userdel "$USER"
 }
 
 # set quota
@@ -105,6 +118,9 @@ test_1() {
        echo "== Enable quota"
        $LFS quotaoff -ug $MOUNT
        $LFS quotacheck -ug $MOUNT
+
+       $LFS setquota -u $USER 0 0 0 0 $MOUNT
+       $LFS setquota -g $USER 0 0 0 0 $MOUNT
        return 0
 }
 
@@ -123,7 +139,8 @@ test_2() {
        $RUNAS dd if=/dev/zero of=$TESTFILE bs=$BLK_SZ count=$(($LIMIT/2)) > /dev/null 2>&1 || error "(usr) write failure, but expect success"
        echo "    Done"
        echo "    Write out of block quota ..."
-       $RUNAS dd if=/dev/zero of=$TESTFILE bs=$BLK_SZ count=$(($LIMIT/2)) seek=$(($LIMIT/2)) > /dev/null 2>&1
+       # this time maybe cache write,  ignore it's failure
+       $RUNAS dd if=/dev/zero of=$TESTFILE bs=$BLK_SZ count=$(($LIMIT/2)) seek=$(($LIMIT/2)) > /dev/null 2>&1 || echo " " > /dev/null
        # flush cache, ensure noquota flag is setted on client
        sync; sleep 1; sync;
        $RUNAS dd if=/dev/zero of=$TESTFILE bs=$BLK_SZ count=$BUNIT_SZ seek=$LIMIT > /dev/null 2>&1 && error "(usr) write success, but expect EDQUOT"
@@ -142,7 +159,8 @@ test_2() {
        $RUNAS dd if=/dev/zero of=$TESTFILE bs=$BLK_SZ count=$(($LIMIT/2)) > /dev/null 2>&1 || error "(grp) write failure, but expect success"
        echo "    Done"
        echo "    Write out of block quota ..."
-       $RUNAS dd if=/dev/zero of=$TESTFILE bs=$BLK_SZ count=$(($LIMIT/2)) seek=$(($LIMIT/2)) > /dev/null 2>&1
+       # this time maybe cache write, ignore it's failure
+       $RUNAS dd if=/dev/zero of=$TESTFILE bs=$BLK_SZ count=$(($LIMIT/2)) seek=$(($LIMIT/2)) > /dev/null 2>&1 || echo " " > /dev/null
        sync; sleep 1; sync;
        $RUNAS dd if=/dev/zero of=$TESTFILE bs=$BLK_SZ count=$BUNIT_SZ seek=$LIMIT > /dev/null 2>&1 && error "(grp) write success, but expect EDQUOT"
        echo "    EDQUOT"
@@ -197,17 +215,135 @@ test_3() {
        return 0
 }
 
+test_block_soft() {
+       TESTFILE=$1
+       GRACE=$2
+       BS=$(($BUNIT_SZ * $BLK_SZ))
+
+       echo "    Write to exceed soft limit"
+       $RUNAS dd if=/dev/zero of=$TESTFILE bs=$BLK_SZ count=$BUNIT_SZ >/dev/null 2>&1 || error "write failure, but expect success"
+       sync; sleep 1; sync;
+
+       echo "    Write before timer goes off"
+       $RUNAS dd if=/dev/zero of=$TESTFILE bs=$BLK_SZ count=$BUNIT_SZ seek=$BUNIT_SZ >/dev/null 2>&1 || error "write failure, but expect success"
+       sync; sleep 1; sync;
+       echo "    Done"
+       
+       echo "    Sleep $GRACE seconds ..."
+       sleep $GRACE
+
+       echo "    Write after timer goes off"
+       # maybe cache write, ignore.
+       $RUNAS dd if=/dev/zero of=$TESTFILE bs=$BLK_SZ count=$BUNIT_SZ seek=$(($BUNIT_SZ * 2)) >/dev/null 2>&1 || echo " " > /dev/null
+       sync; sleep 1; sync;
+       $RUNAS dd if=/dev/zero of=$TESTFILE bs=$BLK_SZ count=1 seek=$(($BUNIT_SZ * 3)) >/dev/null 2>&1 && error "write success, but expect EDQUOT"
+       echo "    EDQUOT"
+
+       echo "    Unlink file to stop timer"
+       rm -f $TESTFILE
+       echo "    Done"
+
+       echo "    Write ..."
+       $RUNAS dd if=/dev/zero of=$TESTFILE bs=$BLK_SZ count=$BUNIT_SZ >/dev/null 2>&1 || error "write failure, but expect success"
+       echo "    Done"
+
+       # cleanup
+       rm -f $TESTFILE
+}
+
 # block soft limit (start timer, timer goes off, stop timer)
 test_4() {
        echo "== Block soft limit"
-       echo "  ** skipped"
+       LIMIT=$(( $BUNIT_SZ * $(($OSTCOUNT + 1)) )) # 1 bunits each sever
+       TESTFILE="$TSTDIR/quota_tst40"
+       GRACE=10
+
+       echo "  User quota (soft limit: $LIMIT bytes  grace: $GRACE seconds)"
+       $LFS setquota -t -u $GRACE $MAX_IQ_TIME $MOUNT
+       $LFS setquota -u $USER $LIMIT 0 0 0 $MOUNT
+
+       test_block_soft $TESTFILE $GRACE
+       $LFS setquota -u $USER 0 0 0 0 $MOUNT
+
+       echo "  Group quota (soft limit: $LIMIT bytes  grace: $GRACE seconds)"
+       $LFS setquota -t -g $GRACE $MAX_IQ_TIME $MOUNT
+       $LFS setquota -g $USER $LIMIT 0 0 0 $MOUNT
+       TESTFILE="$TSTDIR/quota_tst41"
+
+       test_block_soft $TESTFILE $GRACE
+       $LFS setquota -g $USER 0 0 0 0 $MOUNT
+       
        return 0
 }
 
+test_file_soft() {
+       TESTFILE=$1
+       LIMIT=$2
+       GRACE=$3
+
+       echo "    Create files to exceed soft limit"
+       for i in `seq $LIMIT`; do
+               $RUNAS touch ${TESTFILE}_$i >/dev/null 2>&1 || error "touch failure, but expect success"
+       done
+       echo "    Done"
+
+       echo "    Create file before timer goes off"
+       $RUNAS touch ${TESTFILE}_before >/dev/null 2>&1 || error "touch before timer goes off failure, but expect success"
+       echo "    Done"
+
+       echo "    Sleep $GRACE seconds ..."
+       sleep $GRACE
+       
+       echo "    Create file after timer goes off"
+       for i in `seq $(($IUNIT_SZ - 1))`; do
+               $RUNAS touch ${TESTFILE}_after_$i >/dev/null 2>&1 || error "touch ${TESTFILE}_after_$i failure, but expect success"
+       done
+       $RUNAS touch ${TESTFILE}_after >/dev/null 2>&1 && error "touch after timer goes off success, but expect EDQUOT"
+       echo "    EDQUOT"
+
+       echo "    Unlink files to stop timer"
+       for i in `seq $LIMIT`; do
+               rm -f ${TESTFILE}_$i >/dev/null 2>&1 || error "rm ${TESTFILE}_$i failure"
+       done
+       rm -f ${TESTFILE}_before
+       for i in `seq $(($IUNIT_SZ - 1))`; do
+               rm -f ${TESTFILE}_after_$i >/dev/null 2>&1 || error "rm ${TESTFILE}_after_$i failure"
+       done
+       echo "    Done"
+
+       echo "    Create file"
+       $RUNAS touch ${TESTFILE}_xxx >/dev/null 2>&1 || error "touch after timer stop failure, but expect success"
+       echo "    Done"
+
+       # cleanup
+       rm -f ${TESTFILE}_xxx
+}
+
 # file soft limit (start timer, timer goes off, stop timer)
 test_5() {
        echo "== File soft limit"
-       echo "  ** skipped"
+       LIMIT=$(($IUNIT_SZ * 10))       # 10 iunits on mds
+       TESTFILE="$TSTDIR/quota_tst50"
+       GRACE=5
+
+       echo "  User quota (soft limit: $LIMIT files  grace: $GRACE seconds)"
+       $LFS setquota -t -u $MAX_DQ_TIME $GRACE $MOUNT
+       $LFS setquota -u $USER 0 0 $LIMIT 0 $MOUNT
+
+       test_file_soft $TESTFILE $LIMIT $GRACE
+       $LFS setquota -u $USER 0 0 0 0 $MOUNT
+
+       echo "  Group quota (soft limit: $LIMIT files  grace: $GRACE seconds)"
+       $LFS setquota -t -g $MAX_DQ_TIME $GRACE $MOUNT
+       $LFS setquota -g $USER 0 0 $LIMIT 0 $MOUNT
+       TESTFILE="$TSTDIR/quota_tst51"
+
+       test_file_soft $TESTFILE $LIMIT $GRACE
+       $LFS setquota -g $USER 0 0 0 0 $MOUNT
+       
+       # cleanup
+       $LFS setquota -t -u $MAX_DQ_TIME $MAX_IQ_TIME $MOUNT
+       $LFS setquota -t -g $MAX_DQ_TIME $MAX_IQ_TIME $MOUNT
        return 0
 }
 
@@ -270,8 +406,8 @@ test_7() {
        $RUNAS dd if=/dev/zero of=$FILEB bs=$BLK_SZ seek=$LIMIT count=$BUNIT_SZ >/dev/null 2>&1 && error "write fileb success, but expect EDQUOT"
        sync; sleep 1; sync;
        echo "  Write to OST0 return EDQUOT"
-       # this write of OST0 is cache write, will success
-       $RUNAS dd if=/dev/zero of=$FILEA bs=$BLK_SZ count=$(($BUNIT_SZ * 2)) >/dev/null 2>&1 || error "write filea failure, but expect success"
+       # this write maybe cache write, ignore it's failure
+       $RUNAS dd if=/dev/zero of=$FILEA bs=$BLK_SZ count=$(($BUNIT_SZ * 2)) >/dev/null 2>&1 || echo " " > /dev/null
        sync; sleep 1; sync;
        $RUNAS dd if=/dev/zero of=$FILEA bs=$BLK_SZ count=$(($BUNIT_SZ * 2)) seek=$(($BUNIT_SZ *2)) >/dev/null 2>&1 && error "write filea success, but expect EDQUOT"
        echo "  EDQUOT"
index 2980fdf..e0d4b3c 100755 (executable)
@@ -2,8 +2,8 @@
 
 set -e
 
-#         bug  2986
-ALWAYS_EXCEPT="20b"
+#         bug  2986 5494
+ALWAYS_EXCEPT="20b  24"
 
 
 LUSTRE=${LUSTRE:-`dirname $0`/..}
@@ -136,14 +136,14 @@ run_test 9 "pause bulk on OST (bug 1420)"
 
 #bug 1521
 test_10() {
-    do_facet client mcreate $MOUNT/f10        || return 1
-    drop_bl_callback "chmod 0777 $MOUNT/f10"  || return 2
+    do_facet client mcreate $MOUNT/$tfile        || return 1
+    drop_bl_callback "chmod 0777 $MOUNT/$tfile"  || return 2
     # wait for the mds to evict the client
     #echo "sleep $(($TIMEOUT*2))"
     #sleep $(($TIMEOUT*2))
-    do_facet client touch  $MOUNT/f10 || echo "touch failed, evicted"
-    do_facet client checkstat -v -p 0777 $MOUNT/f10  || return 3
-    do_facet client "munlink $MOUNT/f10"
+    do_facet client touch $MOUNT/$tfile || echo "touch failed, evicted"
+    do_facet client checkstat -v -p 0777 $MOUNT/$tfile  || return 3
+    do_facet client "munlink $MOUNT/$tfile"
 }
 run_test 10 "finish request on server after client eviction (bug 1521)"
 
@@ -393,12 +393,75 @@ test_24() {       # bug 2248 - eviction fails writeback but app doesn't see it
        client_reconnect
        [ $rc -eq 0 ] && error "multiop didn't fail fsync: rc $rc" || true
 }
-run_test 24 "fsync error (should return error)" 
+run_test 24 "fsync error (should return error)"
+
+test_26() {      # bug 5921 - evict dead exports 
+# this test can only run from a client on a separate node.
+       [ "`lsmod | grep obdfilter`" ] && \
+           echo "skipping test 26 (local OST)" && return
+       [ "`lsmod | grep mds`" ] && \
+           echo "skipping test 26 (local MDS)" && return
+       OST_FILE=/proc/fs/lustre/obdfilter/ost_svc/num_exports
+        OST_EXP="`do_facet ost cat $OST_FILE`"
+       OST_NEXP1=`echo $OST_EXP | cut -d' ' -f2`
+       echo starting with $OST_NEXP1 OST exports
+# OBD_FAIL_PTLRPC_DROP_RPC 0x505
+       do_facet client sysctl -w lustre.fail_loc=0x505
+       # evictor takes up to 2.25x to evict.  But if there's a 
+       # race to start the evictor from various obds, the loser
+       # might have to wait for the next ping.
+       echo Waiting for $(($TIMEOUT * 4)) secs
+       sleep $(($TIMEOUT * 4))
+        OST_EXP="`do_facet ost cat $OST_FILE`"
+       OST_NEXP2=`echo $OST_EXP | cut -d' ' -f2`
+       echo ending with $OST_NEXP2 OST exports
+       do_facet client sysctl -w lustre.fail_loc=0x0
+        [ $OST_NEXP1 -le $OST_NEXP2 ] && error "client not evicted"
+       return 0
+}
+run_test 26 "evict dead exports"
+
+test_27() {
+       [ "`lsmod | grep mds`" ] || \
+           { echo "skipping test 27 (non-local MDS)" && return 0; }
+       mkdir -p $DIR/$tdir
+       writemany -q -a $DIR/$tdir/$tfile 0 5 &
+       CLIENT_PID=$!
+       sleep 1
+       FAILURE_MODE="SOFT"
+       facet_failover mds
+#define OBD_FAIL_OSC_SHUTDOWN            0x407
+       sysctl -w lustre.fail_loc=0x80000407
+       # need to wait for reconnect
+       echo -n waiting for fail_loc
+       while [ `sysctl -n lustre.fail_loc` -eq -2147482617 ]; do
+           sleep 1
+           echo -n .
+       done
+       facet_failover mds
+       #no crashes allowed!
+        kill -USR1 $CLIENT_PID
+       wait $CLIENT_PID 
+       true
+}
+run_test 27 "fail LOV while using OSC's"
 
-test_25a() {
+test_28() {      # bug 6086 - error adding new clients
+       do_facet client mcreate $MOUNT/$tfile        || return 1
+       drop_bl_callback "chmod 0777 $MOUNT/$tfile"  || return 2
+       #define OBD_FAIL_MDS_ADD_CLIENT 0x12f
+       do_facet mds sysctl -w lustre.fail_loc=0x8000012f
+       # fail once (evicted), reconnect fail (fail_loc), ok
+       df || (sleep 1; df) || (sleep 1; df) || error "reconnect failed"
+       rm -f $MOUNT/$tfile
+       fail mds                # verify MDS last_rcvd can be loaded
+}
+run_test 28 "handle error adding new clients (bug 6086)"
+
+test_50() {
        mkdir -p $DIR/$tdir
-       # put a load of file creates/writes/deletes for 10 min.
-       do_facet client "writemany -q -a $DIR/$tdir/$tfile 600 5" &
+       # put a load of file creates/writes/deletes
+       writemany -q $DIR/$tdir/$tfile 0 5 &
        CLIENT_PID=$!
        echo writemany pid $CLIENT_PID
        sleep 10
@@ -410,42 +473,46 @@ test_25a() {
        sleep 60
        fail mds
        # client process should see no problems even though MDS went down
+       sleep $TIMEOUT
+        kill -USR1 $CLIENT_PID
        wait $CLIENT_PID 
        rc=$?
        echo writemany returned $rc
+       #these may fail because of eviction due to slow AST response.
        return $rc
 }
-run_test 25a "failover MDS under load"
+run_test 50 "failover MDS under load"
 
-test_25b() {
+test_51() {
        mkdir -p $DIR/$tdir
        # put a load of file creates/writes/deletes
-       do_facet client "writemany -q -a $DIR/$tdir/$tfile 300 5" &
+       writemany -q $DIR/$tdir/$tfile 0 5 &
        CLIENT_PID=$!
-       echo writemany pid $CLIENT_PID
        sleep 1
        FAILURE_MODE="SOFT"
        facet_failover mds
        # failover at various points during recovery
-       sleep 1
-       facet_failover mds
-       sleep 5
-       facet_failover mds
-       sleep 10
-       facet_failover mds
-       sleep 20
-       facet_failover mds
+       SEQ="1 5 10 $(seq $TIMEOUT 5 $(($TIMEOUT+10)))"
+        echo will failover at $SEQ
+        for i in $SEQ
+          do
+          echo failover in $i sec
+          sleep $i
+          facet_failover mds
+        done
        # client process should see no problems even though MDS went down
        # and recovery was interrupted
+       sleep $TIMEOUT
+        kill -USR1 $CLIENT_PID
        wait $CLIENT_PID 
        rc=$?
        echo writemany returned $rc
        return $rc
 }
-run_test 25b "failover MDS during recovery"
+run_test 51 "failover MDS during recovery"
 
-test_25c_guts() {
-       do_facet client "writemany -q $DIR/$tdir/$tfile 600 5" &
+test_52_guts() {
+       do_facet client "writemany -q -a $DIR/$tdir/$tfile 300 5" &
        CLIENT_PID=$!
        echo writemany pid $CLIENT_PID
        sleep 10
@@ -461,22 +528,23 @@ test_25c_guts() {
        return $rc
 }
 
-test_25c() {
+test_52() {
        mkdir -p $DIR/$tdir
-       test_25c_guts
+       test_52_guts
        rc=$?
        [ $rc -ne 0 ] && { return $rc; }
        # wait for client to reconnect to OST
        sleep 30
-       test_25c_guts
+       test_52_guts
        rc=$?
        [ $rc -ne 0 ] && { return $rc; }
        sleep 30
-       test_25c_guts
+       test_52_guts
        rc=$?
        client_reconnect
-       return $rc
+       #return $rc
 }
-run_test 25c "failover OST under load"
+run_test 52 "failover OST under load"
+
 
 FORCE=--force $CLEANUP
index 15a2c99..1e7268f 100755 (executable)
@@ -672,7 +672,7 @@ test_33() {
     touch $DIR/$tfile
     fail_abort mds
     # this file should be gone, because the replay was aborted
-    $CHECKSTAT -t file $DIR/$tfile && return 1
+    $CHECKSTAT -t file $DIR/$tfile && return 3
     return 0
 }
 run_test 33 "abort recovery before client does replay"
@@ -837,6 +837,8 @@ test_42() {
     createmany -o $DIR/$tfile-%d 800
     replay_barrier ost
     unlinkmany $DIR/$tfile-%d 0 400
+    DEBUG42=`sysctl portals.debug | tr -d ' '`
+    sysctl -w portals.debug=-1
     facet_failover ost
     
     # osc is evicted, fs is smaller
@@ -844,6 +846,7 @@ test_42() {
     [ $blocks_after -lt $blocks ] || return 1
     echo wait for MDS to timeout and recover
     sleep $((TIMEOUT * 2))
+    sysctl -w $DEBUG42
     unlinkmany $DIR/$tfile-%d 400 400
     $CHECKSTAT -t file $DIR/$tfile-* && return 2 || true
 }
@@ -1020,13 +1023,15 @@ run_test 57 "test recovery from llog for setattr op"
 test_58() {
 #define OBD_FAIL_MDS_OST_SETATTR       0x12c
     do_facet mds "sysctl -w lustre.fail_loc=0x8000012c"
-    createmany -o $DIR/$tfile-%d 30000
+    mkdir $DIR/$tdir
+    createmany -o $DIR/$tdir/$tfile-%d 2500
     replay_barrier mds
     fail mds
     sleep 2
-    $CHECKSTAT -t file $DIR/$tfile-* || return 1
+    $CHECKSTAT -t file $DIR/$tdir/$tfile-* || return 1
     do_facet mds "sysctl -w lustre.fail_loc=0x0"
-    rm -f $DIR/$tfile-*
+    unlinkmany $DIR/$tdir/$tfile-%d 2500
+    rmdir $DIR/$tdir
 }
 run_test 58 "test recovery from llog for setattr op (test llog_gen_rec)"
 
diff --git a/lustre/tests/routed.sh b/lustre/tests/routed.sh
new file mode 100644 (file)
index 0000000..c6d6bdb
--- /dev/null
@@ -0,0 +1,157 @@
+#!/bin/sh -vx
+
+set -e
+
+export PATH=`dirname $0`/../utils:$PATH
+
+config=${1:-`basename $0 .sh`.xml}
+
+BATCH=/tmp/lmc-batch.$$
+save_cmd() {
+    echo "$@" >> $BATCH
+}
+
+LMC_REAL="${LMC:-lmc} -m $config"
+LMC="save_cmd"
+
+TMP=${TMP:-/tmp}
+
+MOUNT=${MOUNT:-/mnt/lustre}
+
+STRIPE_BYTES=$((1024*1024))
+STRIPES_PER_OBJ=1
+
+# specific journal size for the ost, in MB
+JSIZE=${JSIZE:-0}
+JARG=""
+[ "$JSIZE" -gt 0 ] && JARG="--journal_size $JSIZE"
+
+MDSNODE=${MDSNODE:-srv1}
+MDSDEV=${MDSDEV:-$TMP/mds1-$MDSNODE}
+MDSSIZE=${MDSSIZE:-400000}
+OSTNODES=${OSTNODES:-"srv2 srv3 srv2 srv3"}
+OSTSIZE=${OSTSIZE:-150000}
+# OSTDEVN will still override the device for OST N
+OSTFAILOVER=${OSTFAILOVER:---failover}
+ROUTERS=${ROUTERS:-"cli1 cli2"}
+CLIENT_NETTYPE=elan
+CLIENT_CLUSTER=${CLIENT_CLUSTER:-0x0000}
+CLIENT_LO=${CLIENT_LO:-cli3}
+CLIENT_HI=${CLIENT_HI:-cli4}
+CLIENTS=${CLIENTS:-client}
+SERVER_NETTYPE=tcp
+SERVER_CLUSTER=${SERVER_CLUSTER:-0x1000}
+FSNAME=fs1
+
+h2tcp () {
+       case $1 in
+       client) echo '\*' ;;
+       *) echo $1 ;;
+       esac
+}
+
+h2elan () { # assumes node names of the form fooN, where N is elan node ID
+       case $1 in
+       client) echo '\*' ;;
+       *) echo $1 | sed "s/[^0-9]*//" ;;
+       esac
+}
+
+rm -f $config $BATCH
+
+# MDSNODE
+echo; echo -n "adding MDS on: $MDSNODE"
+eval "NODE$MDSNODE=y"
+$LMC --add net --node $MDSNODE --nid `h2$SERVER_NETTYPE $MDSNODE` \
+       --nettype $SERVER_NETTYPE --cluster_id $SERVER_CLUSTER
+$LMC --add mds --node $MDSNODE --mds mds-$FSNAME --dev $MDSDEV $MDSOPT  \
+       --size $MDSSIZE
+$LMC --add lov --lov lov-$FSNAME --mds mds-$FSNAME --stripe_sz $STRIPE_BYTES \
+       --stripe_cnt $STRIPES_PER_OBJ --stripe_pattern 0
+# MDS route to elan client
+for R in $ROUTERS; do
+       echo -n " [r=$R]"
+       $LMC --node $MDSNODE --add route --nettype $SERVER_NETTYPE      \
+               --gw `h2$CLIENT_NETTYPE $R`                             \
+               --lo `h2$CLIENT_NETTYPE $CLIENT_LO`                     \
+               --hi `h2$CLIENT_NETTYPE $CLIENT_HI`                     \
+               --gateway_cluster_id $SERVER_CLUSTER                    \
+               --target_cluster_id $SERVER_CLUSTER
+done
+
+--dev $OSTDEV --size $OSTSIZE
+# OSTNODE
+COUNT=1
+for OSTNODE in $OSTNODES; do
+       OST=ost$COUNT
+       eval OSTDEV=\$OSTDEV$COUNT
+       if [ -z "$OSTDEV" ]; then
+               eval OSTDEV=${!OSTDEV:=$TMP/$OST-$OSTNODE}
+       fi
+       DEV=`basename $OSTDEV`
+       echo; echo -n "adding OST on: $OSTNODE[$DEV]"
+       if [ "`eval echo \\$NODE$OSTNODE`" != "y" ]; then
+               $LMC --add net --node $OSTNODE --nid $OSTNODE           \
+                       --nettype $SERVER_NETTYPE --cluster_id $SERVER_CLUSTER
+               # OST route to elan clients
+               for R in $ROUTERS; do
+                       echo -n " [r=$R]"
+                       $LMC --node $OSTNODE --add route                \
+                               --nettype $SERVER_NETTYPE               \
+                               --gw `h2$CLIENT_NETTYPE $R`             \
+                               --lo `h2$CLIENT_NETTYPE $CLIENT_LO`     \
+                               --hi `h2$CLIENT_NETTYPE $CLIENT_HI`     \
+                               --gateway_cluster_id $SERVER_CLUSTER    \
+                               --target_cluster_id $SERVER_CLUSTER
+               done
+               eval "NODE$OSTNODE=y"
+       fi
+
+       $LMC --add ost --node $OSTNODE --ost ost-$FSNAME-$OSTNODE-$DEV  \
+               --lov lov-$FSNAME $OSTFAILOVER --dev $OSTDEV --size $OSTSIZE \
+               $OSTOPT
+       COUNT=`expr $COUNT + 1`
+done
+
+# ROUTER
+echo; echo -n "adding ROUTER on: "
+for ROUTER in $ROUTERS; do
+       echo -n " $ROUTER"
+       $LMC --node $ROUTER --add net --nid `h2$CLIENT_NETTYPE $ROUTER` \
+               --cluster_id $SERVER_CLUSTER --nettype $SERVER_NETTYPE  \
+               --hostaddr $ROUTER --router
+       $LMC --node $ROUTER --add net --nid `h2$CLIENT_NETTYPE $ROUTER` \
+               --cluster_id $CLIENT_CLUSTER --nettype $CLIENT_NETTYPE  \
+               --router
+       # ROUTER route to OSTs and MDS
+       for NODE in `echo $MDSNODE $OSTNODES | tr -s " " "\n" | sort -u`; do
+               $LMC --node $ROUTER --add route                         \
+                       --nettype $SERVER_NETTYPE                       \
+                       --gw `h2$CLIENT_NETTYPE $ROUTER`                \
+                       --lo `h2$SERVER_NETTYPE $NODE`                  \
+                       --gateway_cluster_id $SERVER_CLUSTER            \
+                       --target_cluster_id $SERVER_CLUSTER
+       done
+       # ROUTER route to clients
+       $LMC --node $ROUTER --add route --nettype $CLIENT_NETTYPE       \
+               --gw `h2$CLIENT_NETTYPE $ROUTER`                        \
+               --lo `h2$CLIENT_NETTYPE $CLIENT_LO`                     \
+               --hi `h2$CLIENT_NETTYPE $CLIENT_HI`                     \
+               --gateway_cluster_id $CLIENT_CLUSTER                    \
+               --target_cluster_id $CLIENT_CLUSTER
+done
+
+# CLIENT
+echo; echo -n "adding CLIENT on: "
+for CLIENT in $CLIENTS; do
+       echo -n " $CLIENT"
+       $LMC --node $CLIENT --add net --nid `h2$CLIENT_NETTYPE $CLIENT` \
+               --cluster_id $CLIENT_CLUSTER --nettype $CLIENT_NETTYPE
+       $LMC --node $CLIENT --add mtpt --path $MOUNT --mds mds-$FSNAME  \
+               --lov lov-$FSNAME $CLIENTOPT
+done
+echo
+
+set -vx
+echo "generating config $config from $BATCH"
+$LMC_REAL --batch $BATCH
index f31295a..75f8765 100755 (executable)
@@ -130,5 +130,6 @@ if [ `expr $NOWUSED - $USED` -gt 1024 ]; then
 fi
 
 if [ "$I_MOUNTED" = "yes" ]; then
+       sync && sleep 2 && sync     # wait for delete thread
        sh llmountcleanup.sh || exit 29
 fi
index 915860e..11874aa 100644 (file)
@@ -55,14 +55,12 @@ SAVE_PWD=$PWD
 clean() {
        echo -n "cln.."
        sh llmountcleanup.sh ${FORCE} > /dev/null || exit 20
-       I_MOUNTED=no
 }
 CLEAN=${CLEAN:-:}
 
 start() {
        echo -n "mnt.."
        sh llrmount.sh > /dev/null || exit 10
-       I_MOUNTED=yes
        echo "done"
 }
 START=${START:-:}
@@ -811,9 +809,7 @@ run_test 27a "one stripe file =================================="
 
 test_27c() {
        [ "$OSTCOUNT" -lt "2" ] && echo "skipping 2-stripe test" && return
-       if [ ! -d $DIR/d27 ]; then
-               mkdir $DIR/d27
-       fi
+       mkdir -p $DIR/d27
        $LSTRIPE $DIR/d27/f01 65536 0 2 || error "lstripe failed"
        [ `$LFIND $DIR/d27/f01 | grep -A 10 obdidx | wc -l` -eq 4 ] ||
                error "two-stripe file doesn't have two stripes"
@@ -824,9 +820,7 @@ test_27c() {
 run_test 27c "create two stripe file f01 ======================="
 
 test_27d() {
-       if [ ! -d $DIR/d27 ]; then
-               mkdir $DIR/d27
-       fi
+       mkdir -p $DIR/d27
        $LSTRIPE $DIR/d27/fdef 0 -1 0 || error "lstripe failed"
        $CHECKSTAT -t file $DIR/d27/fdef || error "checkstat failed"
        #dd if=/dev/zero of=$DIR/d27/fdef bs=4k count=4 || error
@@ -834,9 +828,7 @@ test_27d() {
 run_test 27d "create file with default settings ================"
 
 test_27e() {
-       if [ ! -d $DIR/d27 ]; then
-               mkdir $DIR/d27
-       fi
+       mkdir -p $DIR/d27
        $LSTRIPE $DIR/d27/f12 65536 0 2 || error "lstripe failed"
        $LSTRIPE $DIR/d27/f12 65536 0 2 && error "lstripe succeeded twice"
        $CHECKSTAT -t file $DIR/d27/f12 || error "checkstat failed"
@@ -844,9 +836,7 @@ test_27e() {
 run_test 27e "lstripe existing file (should return error) ======"
 
 test_27f() {
-       if [ ! -d $DIR/d27 ]; then
-               mkdir $DIR/d27
-       fi
+       mkdir -p $DIR/d27
        $LSTRIPE $DIR/d27/fbad 100 0 1 && error "lstripe failed"
        dd if=/dev/zero of=$DIR/d27/f12 bs=4k count=4 || error "dd failed"
        $LFIND $DIR/d27/fbad || error "lfind failed"
@@ -854,9 +844,7 @@ test_27f() {
 run_test 27f "lstripe with bad stripe size (should return error)"
 
 test_27g() {
-       if [ ! -d $DIR/d27 ]; then
-               mkdir $DIR/d27
-       fi
+       mkdir -p $DIR/d27
        $MCREATE $DIR/d27/fnone || error "mcreate failed"
        pass
        log "== test 27h: lfind with no objects ============================"
@@ -869,14 +857,13 @@ test_27g() {
 run_test 27g "test lfind ======================================="
 
 test_27j() {
-        if [ ! -d $DIR/d27 ]; then
-                mkdir $DIR/d27
-        fi
-        $LSTRIPE $DIR/d27/f27j 65536 $OSTCOUNT 1 && error "lstripe failed"||true
+       mkdir -p $DIR/d27
+       $LSTRIPE $DIR/d27/f27j 65536 $OSTCOUNT 1 && error "lstripe failed"||true
 }
 run_test 27j "lstripe with bad stripe offset (should return error)"
 
 test_27k() { # bug 2844
+       mkdir -p $DIR/d27
        FILE=$DIR/d27/f27k
        LL_MAX_BLKSIZE=$((4 * 1024 * 1024))
        [ ! -d $DIR/d27 ] && mkdir -p $DIR/d27
@@ -890,6 +877,7 @@ test_27k() { # bug 2844
 run_test 27k "limit i_blksize for broken user apps ============="
 
 test_27l() {
+       mkdir -p $DIR/d27
        mcreate $DIR/f27l || error "creating file"
        $RUNAS $LSTRIPE $DIR/f27l 65536 -1 1 && \
                error "lstripe should have failed" || true
@@ -897,32 +885,129 @@ test_27l() {
 run_test 27l "check setstripe permissions (should return error)"
 
 test_27m() {
-        [ "$OSTCOUNT" -lt "2" ] && echo "skipping out-of-space test on OST0" && return
-        if [ $ORIGFREE -gt $MAXFREE ]; then
-                echo "skipping out-of-space test on OST0"
-                return
-        fi
-        mkdir -p $DIR/d27
-        $LSTRIPE $DIR/d27/f27m_1 0 0 1
-        dd if=/dev/zero of=$DIR/d27/f27m_1 bs=1024 count=$MAXFREE && \
-                error "dd should fill OST0"
-        i=2
-        while $LSTRIPE $DIR/d27/f27m_$i 0 0 1 ; do
-                i=`expr $i + 1`
-                [ $i -gt 256 ] && break
-        done
-        i=`expr $i + 1`
-        touch $DIR/d27/f27m_$i
-        [ `$LFIND $DIR/d27/f27m_$i | grep -A 10 obdidx | awk '{print $1}'| grep -w "0"` ] && \
-                error "OST0 was full but new created file still use it"
-        i=`expr $i + 1`
-        touch $DIR/d27/f27m_$i
-        [ `$LFIND $DIR/d27/f27m_$i | grep -A 10 obdidx | awk '{print $1}'| grep -w "0"` ] && \
-                error "OST0 was full but new created file still use it"
-        rm $DIR/d27/f27m_1
+       [ "$OSTCOUNT" -lt "2" ] && echo "skipping out-of-space test on OST0" && return
+       if [ $ORIGFREE -gt $MAXFREE ]; then
+               echo "skipping out-of-space test on OST0"
+               return
+       fi
+       mkdir -p $DIR/d27
+       $LSTRIPE $DIR/d27/f27m_1 0 0 1
+       dd if=/dev/zero of=$DIR/d27/f27m_1 bs=1024 count=$MAXFREE && \
+               error "dd should fill OST0"
+       i=2
+       while $LSTRIPE $DIR/d27/f27m_$i 0 0 1 ; do
+               i=`expr $i + 1`
+               [ $i -gt 256 ] && break
+       done
+       i=`expr $i + 1`
+       touch $DIR/d27/f27m_$i
+       [ `$LFIND $DIR/d27/f27m_$i | grep -A 10 obdidx | awk '{print $1}'| grep -w "0"` ] && \
+               error "OST0 was full but new created file still use it"
+       i=`expr $i + 1`
+       touch $DIR/d27/f27m_$i
+       [ `$LFIND $DIR/d27/f27m_$i | grep -A 10 obdidx | awk '{print $1}'| grep -w "0"` ] && \
+               error "OST0 was full but new created file still use it"
+       rm $DIR/d27/f27m_1
 }
 run_test 27m "create file while OST0 was full =================="
 
+# osc's keep a NOSPC stick flag that gets unset with rmdir
+reset_enospc() {
+    sysctl -w lustre.fail_loc=0
+    mkdir -p $DIR/d27/nospc
+    rmdir $DIR/d27/nospc
+}
+
+exhaust_precreations() {
+    local i
+    ostidx=$1
+    ost=$(head -n $(( ostidx + 1 )) /proc/fs/lustre/lov/${LOVNAME}/target_obd | tail -n 1 | awk '{print $2}' | sed -e 's/_UUID$//')
+    mds=$(find /proc/fs/lustre/mds/ -maxdepth 1 -type d | tail -n 1)
+    mds=$(basename $mds)
+
+    last_id=$(tail -n 1 /proc/fs/lustre/osc/OSC_*_${ost}_${mds}/prealloc_last_id)
+    next_id=$(tail -n 1 /proc/fs/lustre/osc/OSC_*_${ost}_${mds}/prealloc_next_id)
+
+    mkdir -p $DIR/d27/${ost}
+    $LSTRIPE $DIR/d27/${ost} 0 $ostidx 1
+    sysctl -w lustre.fail_loc=0x215
+    echo "Creating to objid $last_id on ost $ost..."
+    for (( i = next_id; i <= last_id; i++ )) ; do
+       touch $DIR/d27/${ost}/f$i
+    done
+    reset_enospc
+}
+
+exhaust_all_precreations() {
+    local i
+    for (( i=0; i < OSTCOUNT; i++ )) ; do
+       exhaust_precreations $i
+    done
+}
+
+test_27n() {
+    [ "$OSTCOUNT" -lt "2" ] && echo "" && return
+    reset_enospc
+    rm -f $DIR/d27/f27n
+    exhaust_precreations 0
+    sysctl -w lustre.fail_loc=0x80000215
+    touch $DIR/d27/f27n || error
+    reset_enospc
+}
+run_test 27n "creating a file while some OSTs are full (should succeed) ==="
+
+test_27o() {
+    [ "$OSTCOUNT" -lt "2" ] && echo "" && return
+    reset_enospc
+    rm -f $DIR/d27/f27o
+    exhaust_all_precreations
+    sysctl -w lustre.fail_loc=0x215
+    touch $DIR/d27/f27o && error
+    reset_enospc
+}
+run_test 27o "creating a file while all OSTs are full (should error) ==="
+
+test_27p() {
+    [ "$OSTCOUNT" -lt "2" ] && echo "" && return
+    reset_enospc
+    rm -f $DIR/d27/f27p
+    exhaust_precreations 0
+    $MCREATE $DIR/d27/f27p || error
+    $TRUNCATE $DIR/d27/f27p 80000000 || error
+    $CHECKSTAT -s 80000000 $DIR/d27/f27p || error
+    sysctl -w lustre.fail_loc=0x80000215
+    echo foo >> $DIR/d27/f27p || error
+    $CHECKSTAT -s 80000004 $DIR/d27/f27p || error
+    reset_enospc
+}
+run_test 27p "appending to a truncated file while some OSTs are full ==="
+
+test_27q() {
+    [ "$OSTCOUNT" -lt "2" ] && echo "" && return
+    reset_enospc
+    rm -f $DIR/d27/f27q
+    exhaust_precreations 0
+    $MCREATE $DIR/d27/f27q || error
+    $TRUNCATE $DIR/d27/f27q 80000000 || error
+    $CHECKSTAT -s 80000000 $DIR/d27/f27q || error
+    sysctl -w lustre.fail_loc=0x215
+    echo foo >> $DIR/d27/f27q && error
+    $CHECKSTAT -s 80000000 $DIR/d27/f27q || error
+    reset_enospc
+}
+run_test 27q "appending to a truncated file while all OSTs are full (should error) ==="
+
+test_27r() {
+    [ "$OSTCOUNT" -lt "2" ] && echo "" && return
+    reset_enospc
+    rm -f $DIR/d27/f27r
+    exhaust_precreations 0
+    sysctl -w lustre.fail_loc=0x80000215
+    $LSTRIPE $DIR/d27/f27r 0 0 -1 && error
+    reset_enospc
+}
+run_test 27r "creating a file while some OSTs are full with an explicit stripe count (should error) ==="
+
 test_28() {
        mkdir $DIR/d28
        $CREATETEST $DIR/d28/ct || error
@@ -1324,6 +1409,20 @@ test_34e() {
 }
 run_test 34e "create objects, some with size and some without =="
 
+test_34f() { # bug 6242, 6243
+       SIZE34F=48000
+       rm -f $DIR/f34f
+       $MCREATE $DIR/f34f || error
+       $TRUNCATE $DIR/f34f $SIZE34F || error "truncating $DIR/f3f to $SIZE34F"
+       dd if=$DIR/f34f of=$TMP/f34f
+       $CHECKSTAT -s $SIZE34F $TMP/f34f || error "$TMP/f34f not $SIZE34F bytes"
+       dd if=/dev/zero of=$TMP/f34fzero bs=$SIZE34F count=1
+       cmp $DIR/f34f $TMP/f34fzero || error "$DIR/f34f not all zero"
+       cmp $TMP/f34f $TMP/f34fzero || error "$TMP/f34f not all zero"
+       rm $TMP/f34f $TMP/f34fzero $DIR/f34f
+}
+run_test 34f "read from a file with no objects until EOF ======="
+
 test_35a() {
        cp /bin/sh $DIR/f35a
        chmod 444 $DIR/f35a
@@ -1956,6 +2055,15 @@ test_54d() {
 }
 run_test 54d "fifo device works in lustre ======================"
 
+test_54e() {
+       check_kernel_version 46 || return 0
+       f="$DIR/f54e"
+       string="aaaaaa"
+       mknod $f c 4 0
+       echo $string > $f || error
+}
+run_test 54e "console/tty device works in lustre ======================"
+
 check_fstype() {
        grep -q $FSTYPE /proc/filesystems && return 1
        modprobe $FSTYPE
@@ -1998,8 +2106,7 @@ test_56() {
                 "lfs find --recursive $DIR/d56 wrong: found $FILENUM, expected $NUMFILESx2"
         FILENUM=`$LFIND $DIR/d56 | grep -c obdidx`
         [ $FILENUM -eq $NUMFILES ] || error \
-                "lfs find $DIR/d56 without --recursive wrong: found $FILENUM,
-               expected $NUMFILES"
+                "lfs find $DIR/d56 without --recursive wrong: found $FILENUM, expected $NUMFILES"
         echo "lfs find --recursive passed."
 
         # test lfs find with file instead of dir
@@ -2017,7 +2124,7 @@ test_56() {
 
         #test lfs find with --obd
         $LFIND --obd wrong_uuid $DIR/d56 2>&1 | grep -q "unknown obduuid" || \
-                error "lfs find --obd wrong_uuid should return error information"
+                error "lfs find --obd wrong_uuid should return error message"
 
         [  "$OSTCOUNT" -lt 2 ] && \
                 echo "skipping other lfs find --obd test" && return
@@ -2026,7 +2133,7 @@ test_56() {
         FOUND=`$LFIND -r --obd $OBDUUID $DIR/d56 | wc -l`
         [ $FOUND -eq $FILENUM ] || \
                 error "lfs find --obd wrong: found $FOUND, expected $FILENUM"
-        [ `$LFIND -r -v --obd $OBDUUID $DIR/d56 | sed '/^[      ]*1[    ]/d' | \
+        [ `$LFIND -r -v --obd $OBDUUID $DIR/d56 | sed '/^[      ]*1[    ]/d' |\
                 sed -n '/^[     ]*[0-9][0-9]*[  ]/p' | wc -l` -eq 0 ] || \
                 error "lfs find --obd wrong: should not show file on other obd"
         echo "lfs find --obd passed."
@@ -2069,9 +2176,10 @@ test_57b() {
        $LFIND $FILE1 | grep -q "obdidx" || error "$FILE1 missing EA"
        $LFIND $FILEN | grep -q "obdidx" || error "$FILEN missing EA"
 
+       sleep 1 # make sure we get new statfs data
        MDSFREE2="`cat /proc/fs/lustre/mds/*/kbytesfree`"
        MDCFREE2="`cat /proc/fs/lustre/mdc/*/kbytesfree`"
-       if [ "$MDCFREE" != "$MDCFREE2" ]; then
+       if [ "$MDCFREE2" -lt "$((MDCFREE - 8))" ]; then
                if [ "$MDSFREE" != "$MDSFREE2" ]; then
                        error "MDC before $MDCFREE != after $MDCFREE2"
                else
@@ -2109,6 +2217,13 @@ test_60() {
 }
 run_test 60 "llog sanity tests run from kernel module =========="
 
+test_60b() { # bug 6411
+       dmesg > $DIR/dmesg
+       LLOG_COUNT=`dmesg | grep -c llog_test`
+       [ $LLOG_COUNT -gt 50 ] && error "CDEBUG_LIMIT broken" || true
+}
+run_test 60b "limit repeated messages from CERROR/CWARN ========"
+
 test_61() {
        f="$DIR/f61"
        dd if=/dev/zero of=$f bs=`page_size` count=1
@@ -2254,6 +2369,20 @@ test_65h() {
 }
 run_test 65h "directory stripe info inherit ======"
  
+test_65i() { # bug6367
+        $LSTRIPE $MOUNT 65536 -1 -1
+}
+run_test 65i "set default striping on root directory (bug 6367)="
+
+test_65j() { # bug6367
+       # if we aren't already remounting for each test, do so for this test
+       if [ "$CLEAN" = ":" ]; then
+               clean || error "failed to unmount"
+               start || error "failed to remount"
+       fi
+}
+run_test 65j "get default striping on root directory (bug 6367)="
+
 # bug 2543 - update blocks count on client
 test_66() {
        COUNT=${COUNT:-8}
@@ -2395,6 +2524,19 @@ run_test 72 "Test that remove suid works properly (bug5695) ===="
 
 #b_cray run_test 73 "multiple MDC requests (should not deadlock)"
 
+test_74() { # bug 6149, 6184
+       #define OBD_FAIL_LDLM_ENQUEUE_OLD_EXPORT 0x30e
+       #
+       # very important to OR with OBD_FAIL_ONCE (0x80000000) -- otherwise it
+       # will spin in a tight reconnection loop
+       sysctl -w lustre.fail_loc=0x8000030e
+       # get any lock
+       touch $DIR/f74
+       sysctl -w lustre.fail_loc=0
+       true
+}
+run_test 74 "ldlm_enqueue freed-export error path (shouldn't LBUG)"
+
 # on the LLNL clusters, runas will still pick up root's $TMP settings,
 # which will not be writable for the runas user, and then you get a CVS
 # error message with a corrupt path string (CVS bug) and panic.
@@ -2475,7 +2617,7 @@ log "cleanup: ======================================================"
 if [ "`mount | grep ^$NAME`" ]; then
        rm -rf $DIR/[Rdfs][1-9]*
        if [ "$I_MOUNTED" = "yes" ]; then
-               sh llmountcleanup.sh || error
+               sh llmountcleanup.sh || error "llmountcleanup failed"
        fi
 fi
 
index 9948c81..dfe9187 100644 (file)
@@ -419,6 +419,20 @@ test_20() {
 
 run_test 20 "test extra readahead page left in cache ===="
 
+test_21() { # Bug 5907
+       mkdir $DIR1/d21
+       mount /etc $DIR1/d21 --bind # Poor man's mount.
+       rmdir $DIR1/d21 && error "Removed mounted directory"
+       rmdir $DIR2/d21 && echo "Removed mounted directory from another mountpoint, needs to be fixed"
+       test -d $DIR1/d21 || error "Monted directory disappeared"
+       umount $DIR1/d21
+       test -d $DIR2/d21 || test -d $DIR1/d21 && error "Removed dir still visible after umount"
+       true
+}
+
+run_test 21 " Try to remove mountpoint on another dir ===="
+
+
 
 log "cleanup: ======================================================"
 rm -rf $DIR1/[df][0-9]* $DIR1/lnk || true
index 6173ecd..f5e4509 100644 (file)
@@ -151,13 +151,13 @@ client_df() {
 }
 
 client_reconnect() {
-    df $MOUNT > /dev/null
     uname -n >> $MOUNT/recon
     if [ ! -z "$CLIENTS" ]; then
        $PDSH $CLIENTS "df $MOUNT; uname -n >> $MOUNT/recon" > /dev/null
     fi
     echo Connected clients:
     cat $MOUNT/recon
+    ls -l $MOUNT/recon > /dev/null
     rm $MOUNT/recon
 }
 
@@ -309,12 +309,11 @@ change_active() {
 do_node() {
     HOST=$1
     shift
-
     if $VERBOSE; then
-       echo "CMD: $HOST $@"
-       $PDSH $HOST $LCTL mark "$@" > /dev/null 2>&1 || :
+        echo "CMD: $HOST $@"
+        $PDSH $HOST $LCTL mark "$@" > /dev/null 2>&1 || :
     fi
-    $PDSH $HOST "(PATH=\$PATH:$RLUSTRE/utils:$RLUSTRE/tests; cd $RPWD; sh -c \"$@\")"
+    $PDSH $HOST "(PATH=\$PATH:$RLUSTRE/utils:$RLUSTRE/tests:/sbin:/usr/sbin; cd $RPWD; sh -c \"$@\")"
 }
 
 do_facet() {
@@ -433,55 +432,55 @@ absolute_path() {
 drop_request() {
 # OBD_FAIL_MDS_ALL_REQUEST_NET
     RC=0
-    do_facet mds "echo 0x123 > /proc/sys/lustre/fail_loc"
+    do_facet mds sysctl -w lustre.fail_loc=0x123
     do_facet client "$1" || RC=$?
-    do_facet mds "echo 0 > /proc/sys/lustre/fail_loc"
+    do_facet mds sysctl -w lustre.fail_loc=0
     return $RC
 }
 
 drop_reply() {
 # OBD_FAIL_MDS_ALL_REPLY_NET
     RC=0
-    do_facet mds "echo 0x122 > /proc/sys/lustre/fail_loc"
+    do_facet mds sysctl -w lustre.fail_loc=0x122
     do_facet client "$@" || RC=$?
-    do_facet mds "echo 0 > /proc/sys/lustre/fail_loc"
+    do_facet mds sysctl -w lustre.fail_loc=0
     return $RC
 }
 
 drop_reint_reply() {
 # OBD_FAIL_MDS_REINT_NET_REP
     RC=0
-    do_facet mds "echo 0x119 > /proc/sys/lustre/fail_loc"
+    do_facet mds sysctl -w lustre.fail_loc=0x119
     do_facet client "$@" || RC=$?
-    do_facet mds "echo 0 > /proc/sys/lustre/fail_loc"
+    do_facet mds sysctl -w lustre.fail_loc=0
     return $RC
 }
 
 pause_bulk() {
 #define OBD_FAIL_OST_BRW_PAUSE_BULK      0x214
     RC=0
-    do_facet ost "echo 0x214 > /proc/sys/lustre/fail_loc"
+    do_facet ost sysctl -w lustre.fail_loc=0x214
     do_facet client "$1" || RC=$?
     do_facet client "sync"
-    do_facet ost "echo 0 > /proc/sys/lustre/fail_loc"
+    do_facet ost sysctl -w lustre.fail_loc=0
     return $RC
 }
 
 drop_ldlm_cancel() {
 #define OBD_FAIL_LDLM_CANCEL             0x304
     RC=0
-    do_facet client "echo 0x304 > /proc/sys/lustre/fail_loc"
+    do_facet client sysctl -w lustre.fail_loc=0x304
     do_facet client "$@" || RC=$?
-    do_facet client "echo 0 > /proc/sys/lustre/fail_loc"
+    do_facet client sysctl -w lustre.fail_loc=0
     return $RC
 }
 
 drop_bl_callback() {
 #define OBD_FAIL_LDLM_BL_CALLBACK        0x305
     RC=0
-    do_facet client "echo 0x305 > /proc/sys/lustre/fail_loc"
+    do_facet client sysctl -w lustre.fail_loc=0x305
     do_facet client "$@" || RC=$?
-    do_facet client "echo 0 > /proc/sys/lustre/fail_loc"
+    do_facet client sysctl -w lustre.fail_loc=0
     return $RC
 }
 
diff --git a/lustre/tests/test.c b/lustre/tests/test.c
deleted file mode 100755 (executable)
index d4c6bf7..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2001  Cluster File Systems, Inc.
- *
- * This code is issued under the GNU General Public License.
- * See the file COPYING in this distribution
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <asm/statfs.h>
-#include <unistd.h>
-#include <linux/lustre_idl.h>
-
-#define LOOP_DEVICE "/dev/loop0"
-#define OBD_DEVICE "/dev/obd"
-
-int main (int argc, char * argv[])
-{
-       int fd, rc, err = -1;
-       struct stat stat_buf;
-       struct statfs stfs;
-
-
-       if (argc < 2) {
-               printf("syntax: %s command [argument]\n", argv[0]);
-               printf("Where command is one of \"setup\", \"create\", \"destroy\", or \"sync\".\n");
-               exit(1);
-       }
-       if (stat(LOOP_DEVICE, &stat_buf)) {
-               printf("Couldn't stat(" LOOP_DEVICE ").\n");
-               exit(1);
-       }
-       printf("Device: %u\n", (unsigned int) stat_buf.st_rdev);
-
-       fd = open (OBD_DEVICE, O_RDONLY);
-       if (fd == -1) {
-               printf("Couldn't open " OBD_DEVICE ".\n");
-               exit(1);
-       }
-
-       if (!strcmp(argv[1], "setup")) {
-               rc = ioctl(fd, OBD_IOC_SETUP, &stat_buf.st_rdev);
-               fprintf(stderr, "rc = %d, errno = %d\n", rc, errno);
-       } else if (!strcmp(argv[1], "create")) {
-               int iter, i;
-
-               if (argc < 3) {
-                       printf("create requires a nonzero argument.\n");
-                       exit(1);
-               }
-
-               iter = atoi(argv[2]);
-               if (iter < 1) {
-                       printf("create requires a nonzero argument.\n");
-                       exit(1);
-               }
-               printf("creating %d objects...\n", iter);
-
-               for (i = 0; i < iter; i++) {
-                       if ((rc = ioctl(fd, OBD_IOC_CREATE, &err))) {
-                               fprintf(stderr, "Error; aborting.\n");
-                               break;
-                       }
-                       if ((rc = ioctl(fd, OBD_IOC_DESTROY, &err))) {
-                               fprintf(stderr, "Error; aborting.\n");
-                               break;
-                       }
-               }
-               fprintf(stderr, "rc = %d, errno = %d, err = %d\n",
-                       rc, errno, err);
-       } else if (!strcmp(argv[1], "sync")) {
-               rc = ioctl(fd, OBD_IOC_SYNC, &err);
-               fprintf(stderr, "rc = %d, errno = %d, err = %d\n",
-                       rc, errno, err);
-       } else if (!strcmp(argv[1], "destroy")) {
-               int ino;
-
-               if (argc < 3) {
-                       printf("destroy requires a nonzero inode number.\n");
-                       exit(1);
-               }
-
-               ino = atoi(argv[2]);
-               if (ino < 1) {
-                       printf("destroy requires a nonzero inode number.\n");
-                       exit(1);
-               }
-
-               rc = ioctl(fd, OBD_IOC_DESTROY, &ino);
-               fprintf(stderr, "rc = %d, errno = %d\n", rc, errno);
-       } else {
-               printf("Invalid command, run with no arguments for help.\n");
-       }
-       close(fd);
-
-       return 0;
-}
index ecefe92..dd94d1b 100644 (file)
 #include <sys/types.h>
 #include <sys/stat.h>
 
-#define CERROR(fmt, arg...) fprintf(stderr, fmt, ## arg)
-#ifndef __u64
-#define __u64 long long
-#define cpu_to_le64(v) (v)
-#define le64_to_cpu(v) (v)
-#endif
-
-#ifndef LPU64
-#define LPU64 "%Lu"
-#define LPX64 "%#Lx"
-#endif
+#include <liblustre.h>
 
 #define READ  1
 #define WRITE 2
@@ -91,9 +81,9 @@ int main(int argc, char **argv)
 {
         int fd;
         char *buf;
-        long long count, last, offset;
+        loff_t count, last, offset;
         long pg_vec, len;
-        long long objid;
+        __u64 objid;
         struct stat st;
         int flags = 0;
         int cmd = 0;
index 7b72020..a9480a8 100644 (file)
@@ -15,7 +15,7 @@ OSTDEVBASE=$TMP/ost
 #etc
 OSTSIZE=${OSTSIZE:-100000}
 STRIPECNT=${STRIPECNT:-1}
-STRIPE_BYTES=${STRIPE_BYTES:-$((1024 * 1024))}
+STRIPE_BYTES=${STRIPE_BYTES:-1048576}
 OSDTYPE=${OSDTYPE:-obdfilter}
 OSTFAILOVER=${OSTFAILOVER:-}
 
@@ -77,6 +77,13 @@ h2gm () {
        echo `gmnalnid -n$1`
 }
 
+h2iib () {
+       case $1 in
+       client) echo '\*' ;;
+       *) echo $1 | sed "s/[^0-9]*//" ;;
+       esac
+}
+
 # create nodes
 echo -n "adding NET for:"
 for NODE in `echo $MDSNODE $OSTNODES $CLIENTS | tr -s " " "\n" | sort -u`; do
index 5d4e42e..ff9a21c 100644 (file)
@@ -25,6 +25,16 @@ char cmdname[512];
 int o_abort = 0;
 int o_quiet = 0;
 
+void usage(char *name)
+{
+        fprintf(stderr, "usage: %s [opts] <dirname> <seconds> <threads>\n",
+                name);
+        fprintf(stderr, "  -q quiet\n");
+        fprintf(stderr, "  -a abort other children on first err\n");
+        exit(1);
+}
+
+
 struct kid_list_t {
         pid_t kid;
         struct kid_list_t *next;
@@ -49,19 +59,26 @@ void kill_kids(void)
         }
 }
 
+static int usr1_received;
+void usr1_handler(int unused)
+{
+        usr1_received = 1;
+        kill_kids();
+}
+
 int wait_for_threads(int live_threads)
 {
         int rc = 0;
-        
+
         while (live_threads > 0) {
                 int status;
                 pid_t ret;
-                
+
                 ret = waitpid(0, &status, 0);
                 if (ret == 0) {
                         continue;
                 }
-                
+
                 if (ret < 0) {
                         fprintf(stderr, "%s: error: wait - %s\n",
                                 cmdname, strerror(errno));
@@ -75,7 +92,7 @@ int wait_for_threads(int live_threads)
                          * always returns 1 (OK).  See wait(2).
                          */
                         int err = WEXITSTATUS(status);
-                        if (err || WIFSIGNALED(status))
+                        if (err)
                                 fprintf(stderr,
                                         "%s: error: PID %d had rc=%d\n",
                                         cmdname, ret, err);
@@ -100,7 +117,7 @@ int wait_for_threads(int live_threads)
 void print_err(char *op, char *filename, struct timeval *time, int err)
 {
         fprintf(stderr, "%s: %d.%.06d error: %s(%s): %s\n",
-                cmdname, (int)(time->tv_sec), (int)(time->tv_usec), op, 
+                cmdname, (int)(time->tv_sec), (int)(time->tv_usec), op,
                 filename, strerror(errno));
 }
 
@@ -113,11 +130,11 @@ int run_one_child(char *file, int thread, int seconds)
         int fd, rc = 0, rand, maxrand, len;
         long nfiles = 0, nbytes = 0;
 
-        if (!o_quiet) 
+        if (!o_quiet)
                 printf("%s: running thread #%d\n", cmdname, thread);
-        
+
         srandom(thread);
-        /* Higher thread numbers will produce bigger random files.  
+        /* Higher thread numbers will produce bigger random files.
            Thread 1 will produce only 0-len files. */
         maxrand = 1; rand = thread;
         while (--rand)
@@ -126,20 +143,25 @@ int run_one_child(char *file, int thread, int seconds)
         gettimeofday(&start, NULL);
 
         while(!rc) {
-                gettimeofday(&cur, NULL);
-                if (cur.tv_sec > (start.tv_sec + seconds))
+                if (usr1_received)
                         break;
 
-                sprintf(filename, "%s-%d-%ld", file, thread, nfiles);
+                gettimeofday(&cur, NULL);
+                if (seconds) {
+                        if (cur.tv_sec > (start.tv_sec + seconds))
+                                break;
+                }
                 
+                sprintf(filename, "%s-%d-%ld", file, thread, nfiles);
+
                 fd = open(filename, O_RDWR | O_CREAT, 0666);
                 if (fd < 0) {
                         print_err("open", filename, &cur, errno);
                         rc = errno;
                         break;
                 }
-                
-                sprintf(buf, "%s %010ld %.19s.%012d\n", cmdname, 
+
+                sprintf(buf, "%s %010ld %.19s.%012d\n", cmdname,
                         nfiles++, ctime(&cur.tv_sec), (int)cur.tv_usec);
                 len = strlen(buf);
 
@@ -149,10 +171,10 @@ int run_one_child(char *file, int thread, int seconds)
                                 print_err("write", filename, &cur, errno);
                                 rc = errno;
                                 break;
-                        }                     
+                        }
                         nbytes += len;
-                }  
-                
+                }
+
                 if (close(fd) < 0) {
                         print_err("close", filename, &cur, errno);
                         rc = errno;
@@ -164,10 +186,10 @@ int run_one_child(char *file, int thread, int seconds)
                         break;
                 }
         }
-        
+
         diff = difftime(&cur, &start);
-        if (!o_quiet) 
-                printf("%s: %7ld files, %4ld MB in %.2fs (%7.2f files/s, " 
+        if (!o_quiet)
+                printf("%s: %7ld files, %4ld MB in %.2fs (%7.2f files/s, "
                        "%5.2f MB/s): rc = %d\n",
                        cmdname, nfiles, nbytes >> 20, diff,
                        (double)nfiles / diff, (double)nbytes/1024/1024 / diff,
@@ -176,16 +198,6 @@ int run_one_child(char *file, int thread, int seconds)
         return rc;
 }
 
-void usage(char *name)
-{
-        fprintf(stderr,
-                "usage: %s [opts] <dirname> <seconds> <threads>\n",
-                name);
-        fprintf(stderr, "  -q quiet\n");
-        fprintf(stderr, "  -a abort other children on first err\n");
-        exit(1);
-}
-
 int main(int argc, char *argv[])
 {
         unsigned long duration;
@@ -194,7 +206,7 @@ int main(int argc, char *argv[])
         char *directory;
         int i = 1, rc = 0;
 
-        sprintf(cmdname, "%s", argv[0]);        
+        sprintf(cmdname, "%s", argv[0]);
 
         while((i < argc) && (argv[i][0] == '-')) {
                 switch (argv[i][1]) {
@@ -226,6 +238,8 @@ int main(int argc, char *argv[])
                 exit(2);
         }
 
+        signal(SIGUSR1, usr1_handler);
+
         for (i = 1; i <= threads; i++) {
                 rc = fork();
                 if (rc < 0) {
@@ -244,7 +258,7 @@ int main(int argc, char *argv[])
                 }
         }
         /* parent process */
-        if (!o_quiet) 
+        if (!o_quiet)
                 printf("%s will run for %ld minutes\n", cmdname, duration/60);
         return (wait_for_threads(threads));
 }
index d60c9c1..2938400 100644 (file)
@@ -7,20 +7,60 @@ import Lustre
 # XML processing and query
 
 class LustreDB:
+    caching_enabled = 1
+
+    def __init__(self):
+        self.lookup_uuid_cache = {}
+        self.lookup_name_cache = {}
+        self.lookup_class_cache = {}
+        self.lookup_val_cache = {}
+        self.lookup_refs_cache = {}
+        self.lookup_lovtgts_cache = {}
+        self.lookup_nid2srv_cache = {}
+        self.lookup_activedev_cache = {}
+        self.lookup_tgtdev_cache = {}
+        self.lookup_group_cache = {}
+
+        self.lookup_allrefs_cache = None
+        self.lookup_networks_cache = None
+
     def lookup(self, uuid):
         """ lookup returns a new LustreDB instance"""
-        return self._lookup_by_uuid(uuid)
+        if self.caching_enabled and self.lookup_uuid_cache.has_key(uuid):
+            res = self.lookup_uuid_cache[uuid]
+        else:
+            res = self._lookup_by_uuid(uuid)
+            if self.caching_enabled:
+                self.lookup_uuid_cache[uuid] = res
+        return res
 
     def lookup_name(self, name, class_name = ""):
         """ lookup returns a new LustreDB instance"""
-        return self._lookup_by_name(name, class_name)
+        if self.caching_enabled and self.lookup_name_cache.has_key((name, class_name)):
+            res = self.lookup_name_cache[(name, class_name)]
+        else:
+            res = self._lookup_by_name(name, class_name)
+            if self.caching_enabled:
+                self.lookup_name_cache[(name, class_name)] = res
+        return res
 
     def lookup_class(self, class_name):
         """ lookup returns a new LustreDB instance"""
-        return self._lookup_by_class(class_name)
+        if self.caching_enabled and self.lookup_class_cache.has_key(class_name):
+            res = self.lookup_class_cache[class_name]
+        else:
+            res = self._lookup_by_class(class_name)
+            if self.caching_enabled:
+                self.lookup_class_cache[class_name] = res
+        return res
 
     def get_val(self, tag, default=None):
-        v =  self._get_val(tag)
+        if self.caching_enabled and self.lookup_class_cache.has_key(tag):
+            v = self.lookup_val_cache[tag]
+        else:
+            v = self._get_val(tag)
+            if self.caching_enabled:
+                self.lookup_val_cache[tag] = v
         if v:
             return v
         if default != None:
@@ -31,7 +71,7 @@ class LustreDB:
         return self._get_class()
 
     def get_val_int(self, tag, default=0):
-        str = self._get_val(tag)
+        str = self.get_val(tag)
         try:
             if str:
                 return int(str)
@@ -42,34 +82,55 @@ class LustreDB:
     def get_first_ref(self, tag):
         """ Get the first uuidref of the type TAG. Only
         one is expected.  Returns the uuid."""
-        uuids = self._get_refs(tag)
+        uuids = self.get_refs(tag)
         if len(uuids) > 0:
             return  uuids[0]
         return None
     
     def get_refs(self, tag):
         """ Get all the refs of type TAG.  Returns list of uuids. """
-        uuids = self._get_refs(tag)
+        if self.caching_enabled and self.lookup_refs_cache.has_key(tag):
+            uuids = self.lookup_refs_cache[tag]
+        else:
+            uuids = self._get_refs(tag)
+            if self.caching_enabled:
+                self.lookup_refs_cache[tag] = uuids
         return uuids
 
     def get_all_refs(self):
         """ Get all the refs.  Returns list of uuids. """
-        uuids = self._get_all_refs()
+        if self.caching_enabled and self.lookup_allrefs_cache:
+            uuids = self.lookup_allrefs_cache
+        else:
+            uuids = self._get_all_refs()
+            if self.caching_enabled:
+                self.lookup_allrefs_cache = uuids
         return uuids
 
     def get_lov_tgts(self, tag):
         """ Returns list of lov tgts. """
-        tgts = self._get_lov_tgts(tag)
+        if self.caching_enabled and self.lookup_lovtgts_cache.has_key(tag):
+            tgts = self.lookup_lovtgts_cache[tag]
+        else:
+            tgts = self._get_lov_tgts(tag)
+            if self.caching_enabled:
+                self.lookup_lovtgts_cache[tag] = tgts
         return tgts
  
     def nid2server(self, nid, net_type, cluster_id):
-        netlist = self.lookup_class('network')
-        for net_db in netlist:
-            if (net_db.get_val('nid') == nid and
-                net_db.get_val('nettype') == net_type and
-                net_db.get_val('clusterid') == cluster_id):
-                return net_db
-        return None
+        if self.caching_enabled and self.lookup_nid2srv_cache.has_key((nid, net_type, cluster_id)):
+            res = self.lookup_nid2srv_cache[(nid, net_type, cluster_id)]
+        else:
+            netlist = self.lookup_class('network')
+            for net_db in netlist:
+                if (net_db.get_val('nid') == nid and
+                    net_db.get_val('nettype') == net_type and
+                    net_db.get_val('clusterid') == cluster_id):
+                    res = net_db
+                    break
+            if self.caching_enabled:
+                self.lookup_nid2srv_cache[(nid, net_type, cluster_id)] = res
+        return res
     
     # Find the target_device for target on a node
     # node->profiles->device_refs->target
@@ -81,44 +142,68 @@ class LustreDB:
 
     # get all network uuids for this node
     def get_networks(self):
-        ret = []
-        prof_list = self.get_refs('profile')
-        for prof_uuid in prof_list:
-            prof_db = self.lookup(prof_uuid)
-            net_list = prof_db.get_refs('network')
-            for net_uuid in net_list:
-                ret.append(net_uuid)
+        if self.caching_enabled and self.lookup_networks_cache:
+            ret = self.lookup_networks_cache
+        else:
+            ret = []
+            prof_list = self.get_refs('profile')
+            for prof_uuid in prof_list:
+                prof_db = self.lookup(prof_uuid)
+                net_list = prof_db.get_refs('network')
+                for net_uuid in net_list:
+                    ret.append(net_uuid)
+            if self.caching_enabled:
+                self.lookup_networks_cache = ret
         return ret
 
     def get_active_dev(self, tgtuuid):
-        tgt = self.lookup(tgtuuid)
-        tgt_dev_uuid =tgt.get_first_ref('active')
+        if self.caching_enabled and self.lookup_activedev_cache.has_key(tgtuuid):
+            tgt_dev_uuid = self.lookup_activedev_cache[tgtuuid]
+        else:
+            tgt = self.lookup(tgtuuid)
+            tgt_dev_uuid = tgt.get_first_ref('active')
+            if self.caching_enabled:
+                self.lookup_activedev_cache[tgtuuid] = tgt_dev_uuid
         return tgt_dev_uuid
 
     def get_tgt_dev(self, tgtuuid):
-        prof_list = self.get_refs('profile')
-        for prof_uuid in prof_list:
-            prof_db = self.lookup(prof_uuid)
-            if not prof_db:
-                panic("profile:", profile, "not found.")
-            for ref_class, ref_uuid in prof_db.get_all_refs(): 
-                if ref_class in ('osd', 'mdsdev'):
-                    devdb = self.lookup(ref_uuid)
-                    uuid = devdb.get_first_ref('target')
-                    if tgtuuid == uuid:
-                        return ref_uuid
-        return None
+        if self.caching_enabled and self.lookup_tgtdev_cache.has_key(tgtuuid):
+            res = self.lookup_tgtdev_cache[tgtuuid]
+        else:
+            prof_list = self.get_refs('profile')
+            res = None
+            for prof_uuid in prof_list:
+                prof_db = self.lookup(prof_uuid)
+                if not prof_db:
+                    panic("profile:", profile, "not found.")
+                for ref_class, ref_uuid in prof_db.get_all_refs(): 
+                    if ref_class in ('osd', 'mdsdev'):
+                        devdb = self.lookup(ref_uuid)
+                        uuid = devdb.get_first_ref('target')
+                        if tgtuuid == uuid:
+                            res = ref_uuid
+                            break
+                if not res is None:
+                    break
+            if self.caching_enabled:
+                self.lookup_tgtdev_cache[tgtuuid] = res
+        return res
 
     def get_group(self, group):
-        ret = []
-        devs = self.lookup_class('mds')
-        for tgt in devs:
-            if tgt.get_val('group', "") == group:
-                ret.append(tgt.getUUID())
-        devs = self.lookup_class('ost')
-        for tgt in devs:
-            if tgt.get_val('group', "") == group:
-                ret.append(tgt.getUUID())
+        if self.caching_enabled and self.lookup_group_cache.has_key(group):
+            ret = self.lookup_group_cache[group]
+        else:
+            ret = []
+            devs = self.lookup_class('mds')
+            for tgt in devs:
+                if tgt.get_val('group', tgt.get_value('name')) == group:
+                    ret.append(tgt.getUUID())
+            devs = self.lookup_class('ost')
+            for tgt in devs:
+                if tgt.get_val('group', tgt.get_value('name')) == group:
+                    ret.append(tgt.getUUID())
+            if self.caching_enabled:
+                self.lookup_group_cache[group] = ret
         return ret
 
     # Change the current active device for a target
@@ -130,6 +215,8 @@ class LustreDB:
 
 class LustreDB_XML(LustreDB):
     def __init__(self, dom, root_node):
+        LustreDB.__init__(self)
+
         # init xmlfile
         self.dom_node = dom
         self.root_node = root_node
@@ -306,6 +393,8 @@ class LustreDB_LDAP(LustreDB):
                  user = "cn=Manager, fs=lustre",
                  pw   = ""
                  ):
+        LustreDB.__init__(self)
+
         self._name = name
         self._attrs = attrs
         self._base = base
index 47b6d8f..20c1542 100644 (file)
@@ -3,8 +3,9 @@
 SUBDIRS = Lustre
 
 AM_CFLAGS=$(LLCFLAGS)
-AM_CPPFLAGS=$(LLCPPFLAGS)
-AM_LDFLAGS := -L$(top_builddir)/portals/utils
+AM_CPPFLAGS=$(LLCPPFLAGS) -DLUSTRE_UTILS=1
+
+LIBPTLCTL := $(top_builddir)/portals/utils/libptlctl.a
 
 sbin_scripts = lconf lmc llanalyze llstat.pl llobdstat.pl lactive      \
        load_ldap.sh lrun lwizard
@@ -19,20 +20,31 @@ sbin_SCRIPTS = $(sbin_scripts)
 bin_SCRIPTS = $(bin_scripts)
 endif # UTILS
 
-lctl_LDADD := $(LIBREADLINE) -lptlctl
-lfs_LDADD := $(LIBREADLINE) parser.o liblustreapi.a -lptlctl obd.o
-lload_LDADD := $(LIBREADLINE) -lptlctl
+lctl_LDADD := $(LIBREADLINE) $(LIBPTLCTL)
+lctl_DEPENDENCIES := $(LIBPTLCTL)
+
+lfs_LDADD := $(LIBREADLINE) liblustreapi.a $(LIBPTLCTL)
+lfs_DEPENDENCIES := $(LIBPTLCTL) liblustreapi.a
+
+lload_LDADD := $(LIBREADLINE) $(LIBPTLCTL)
+lload_DEPENDENCIES := $(LIBPTLCTL)
+
 liblustreapi_a_SOURCES = liblustreapi.c
+
 wirecheck_SOURCES = wirecheck.c
 wirecheck_CPPFLAGS = -DCC="\"$(CC)\""
 wiretest_SOURCES = wiretest.c
+
 lctl_SOURCES = parser.c obd.c lustre_cfg.c lctl.c parser.h obdctl.h
+
 lload_SOURCES = lload.c 
 obdio_SOURCES = obdio.c obdiolib.c obdiolib.h
 obdbarrier_SOURCES = obdbarrier.c obdiolib.c obdiolib.h
-lfs_SOURCES = lfs.c 
+lfs_SOURCES = lfs.c parser.c obd.c
+
 llmount_SOURCES = llmount.c 
-llmount_LDADD = $(LIBREADLINE) -lptlctl
+llmount_LDADD = $(LIBREADLINE) $(LIBPTLCTL)
+llmount_DEPENDENCIES := $(LIBPTLCTL)
 
 EXTRA_DIST = $(bin_scripts) $(sbin_scripts)
 
diff --git a/lustre/utils/l_getgroups.c b/lustre/utils/l_getgroups.c
new file mode 100644 (file)
index 0000000..8909422
--- /dev/null
@@ -0,0 +1,104 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ *  Copyright (C) 2004 Cluster File Systems, Inc.
+ *
+ *   This file is part of Lustre, http://www.lustre.org.
+ *
+ *   Lustre is free software; you can redistribute it and/or
+ *   modify it under the terms of version 2 of the GNU General Public
+ *   License as published by the Free Software Foundation.
+ *
+ *   Lustre is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with Lustre; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <grp.h>
+#include <lustre/lustre_user.h>
+
+int get_groups_local(struct mds_grp_downcall_data **grp)
+{
+        struct mds_grp_downcall_data *param;
+        int i, maxgroups, size;
+        struct passwd *pw;
+        struct group  *gr;
+
+        pw = getpwuid((*grp)->mgd_uid);
+        if (!pw) {
+                (*grp)->mgd_err = -errno;
+                return sizeof(*param);
+        }
+
+        maxgroups = sysconf(_SC_NGROUPS_MAX);
+        size = offsetof(struct mds_grp_downcall_data, mgd_groups[maxgroups]);
+        param = malloc(size);
+        if (param == NULL) {
+                (*grp)->mgd_err = -ENOMEM;
+                return sizeof(*param);
+        }
+
+        memcpy(param, *grp, sizeof(*param));
+        *grp = param;
+        while ((gr = getgrent())) {
+                if (!gr->gr_mem)
+                        continue;
+                for (i = 0; gr->gr_mem[i]; i++) {
+                        if (strcmp(gr->gr_mem[i], pw->pw_name) == 0) {
+                                param->mgd_groups[param->mgd_ngroups++] =
+                                        gr->gr_gid;
+                                break;
+                        }
+                }
+                if (param->mgd_ngroups == maxgroups)
+                        break;
+        }
+        endgrent();
+
+        return size;
+}
+
+/* Note that we need to make the downcall regardless of error, so that the
+ * MDS doesn't continue to wait on the upcall. */
+int main(int argc, char **argv)
+{
+        int fd, rc, size;
+        struct mds_grp_downcall_data sparam = { MDS_GRP_DOWNCALL_MAGIC };
+        struct mds_grp_downcall_data *param = &sparam;
+        char pathname[1024];
+
+        if (argc != 3) {
+                printf("bad parameter\n");
+                return -1;
+        }
+
+        snprintf(pathname, 1024, "/proc/fs/lustre/mds/%s/group_info", argv[1]);
+        param->mgd_uid = atoi(argv[2]);
+
+        fd = open(pathname, O_WRONLY);
+        if (fd < 0) {
+                printf("can't open device %s\n", pathname);
+                return -1;
+        }
+
+        size = get_groups_local(&param);
+
+        rc = write(fd, param, size);
+
+        close(fd);
+        return rc;
+}
index 33a19b6..408bb76 100755 (executable)
@@ -37,7 +37,7 @@ if sys.version[0] == '1':
 else:
     from fcntl import F_GETFL, F_SETFL
 
-PYMOD_DIR = ["/usr/lib/lustre/python", "/usr/lib64/lustre/python"]
+PYMOD_DIR = ["/usr/lib64/lustre/python", "/usr/lib/lustre/python"]
 
 def development_mode():
     base = os.path.dirname(sys.argv[0])
@@ -153,8 +153,10 @@ def logall(msgs):
         print string.strip(s)
 
 def debug(*args):
+    # apparently, (non)execution of the following line affects mds device
+    # startup order (e.g. two mds's using loopback devices), so always do it.
+    msg = string.join(map(str,args))
     if config.verbose:
-        msg = string.join(map(str,args))
         print msg
 
 # ack, python's builtin int() does not support '0x123' syntax.
@@ -290,6 +292,8 @@ class AcceptorHandler(DaemonHandler):
         self.port = port
         self.net_type = net_type
         self.flags = ''
+        if config.allow_unprivileged_port:
+            self.flags = '-p'
 
     def pidfile(self):
         return "/var/run/%s-%d.pid" % (self.command, self.port)
@@ -483,7 +487,7 @@ class LCTLInterface:
 
     def add_peer(self, net_type, nid, hostaddr, port):
         """ noop """
-#         if net_type  in ('tcp','openib','ra') and not config.lctl_dump:
+#         if net_type  in ('tcp','openib','ra','cray_kern_nal') and not config.lctl_dump:
 #             cmds =  """
 #   network %s
 #   add_peer %s %s %d
@@ -507,10 +511,49 @@ class LCTLInterface:
 
     def connect(self, srv):
         self.add_uuid(srv.net_type, srv.nid_uuid, srv.nid)
-#         if srv.net_type  in ('tcp','openib','iib','vib','ra') and not config.lctl_dump:
-#             if srv.hostaddr[0]:
-#                 hostaddr = string.split(srv.hostaddr[0], '/')[0]
-#             self.add_peer(srv.net_type, srv.nid, hostaddr, srv.port)
+#        if config.lctl_dump:
+#            return
+#
+#        if srv.net_type  in ('tcp',):
+#            host = socket.gethostname()
+#            node_list = []
+#            if config.node:
+#                node_list.append(config.node)
+#            else:
+#                if len(host) > 0:
+#                    node_list.append(host)
+#                node_list.append('localhost')
+#
+#            node_db = None
+#            for h in node_list: # we are quite sure we can find the node_db
+#                node_db = toplustreDB.lookup_name(h, 'node')
+#                if node_db:
+#                    break
+#
+#            hostaddr = None
+#            for netuuid in node_db.get_networks():
+#                localnet = toplustreDB.lookup(netuuid)
+#                localnet = Network(localnet)
+#                if localnet.net_type != 'tcp':
+#                    continue  # only tcp understands multiple hostaddrs
+#
+#                # always true for tcp network
+#                if localnet.hostaddr[0] and srv.hostaddr[0]:
+#                    for lnet in localnet.hostaddr:
+#                        for pnet in srv.hostaddr:
+#                            if srv.netmatch(lnet, pnet) != 0:
+#                                hostaddr = string.split(pnet, '/')[0]
+#                                #find one is enough, should break the top-most loop
+#                                break
+#                        if hostaddr: break
+#                    else:     # can't find a match
+#                        hostaddr = string.split(srv.hostaddr[0], '/')[0]
+#                    break
+#
+#            self.add_peer(srv.net_type, srv.nid, hostaddr, srv.port)
+#            
+#        if srv.net_type in ('openib','iib','vib','ra'):
+#            self.add_peer(srv.net_type, srv.nid, srv.hostaddr[0], srv.port)
 
     # Recover a device
     def recover(self, dev_name, new_conn):
@@ -629,7 +672,7 @@ class LCTLInterface:
     def abort_recovery(self, name):
         cmds = """
   ignore_errors
-  device %s
+  device $%s
   abort_recovery
   quit""" % (name)
         self.run(cmds)
@@ -918,7 +961,7 @@ def mkfs(dev, devsize, fstype, jsize, isize, mkfsoptions, isblock=1):
         panic("Unable to build fs:", dev, string.join(out))
     # enable hash tree indexing on fsswe
     if fstype in ('ext3', 'extN', 'ldiskfs'):
-        htree = 'echo "feature FEATURE_C5" | debugfs -w'
+        htree = 'tune2fs -O dir_index'
         (ret, out) = run (htree, dev)
         if ret:
             panic("Unable to enable htree:", dev)
@@ -933,7 +976,7 @@ def loop_base():
             loop='/dev/loop'
     return loop
 
-# find loop device assigned to thefile
+# find loop device assigned to the file
 def find_loop(file):
     loop = loop_base()
     for n in xrange(0, MAX_LOOP_DEVICES):
@@ -1045,15 +1088,17 @@ def sys_get_local_nid(net_type, wildcard, cluster_id):
     # don't need a real nid for config log - client will replace (bug5619)
     if config.record:
         local = "54321"
-    elif net_type in ('tcp','openib','iib','vib','ra'):
+    elif net_type in ('tcp','openib','iib','vib','ra','cray_kern_nal'):
         if  ':' in wildcard:
             iface, star = string.split(wildcard, ':')
             local = if2addr(iface)
-            if not local:
-                panic("unable to determine ip for:", wildcard)
+        elif net_type == 'vib':
+            local = if2addr('ipoib0')
         else:
             host = socket.gethostname()
             local = socket.gethostbyname(host)
+        if not local:
+            panic("unable to determine ip for:", wildcard)
     elif net_type == 'elan':
         # awk '/NodeId/ { print $2 }' 'sys_get_elan_position_file()'
         f = sys_get_elan_position_file()
@@ -1093,18 +1138,7 @@ def sys_get_local_nid(net_type, wildcard, cluster_id):
 
 def sys_get_branch():
     """Returns kernel release"""
-    try:
-        fp = open('/proc/sys/kernel/osrelease')
-        lines = fp.readlines()
-        fp.close()
-
-        for l in lines:
-            version = string.split(l)
-            a = string.split(version[0], '.')
-            return a[0] + '.' + a[1]
-    except IOError, e:
-        log(e)
-    return ""
+    return os.uname()[2][:3]
 
 def mod_loaded(modname):
     """Check if a module is already loaded. Look in /proc/modules for it."""
@@ -1397,6 +1431,65 @@ class Network(Module):
                 ip = string.split(hostaddr, '/')[0]
                 lctl.del_interface(self.net_type, ip)
 
+    def my_inet_aton(self, net):
+        split = net.split('.')
+        if len(split) != 4:
+            raise ValueError, "Invalid IPv4 address %s" % net
+
+        naddr = 0
+        i = 0
+        for n in split:
+            try:
+                naddr = naddr + int(n) * (256 ** (3-i))
+            except:
+                raise ValueError, "Invalid IPv4 address %s" % net
+            i = i + 1
+        return naddr
+
+    def tointAddr(self, net):
+        """convert a net address/mask into (numeric-address, bitmap-mask)"""
+        try:
+            addr, mask = string.split(net, '/')
+        except:
+            addr = net
+            mask = 24 #eeb told me that kernel uses this value by default
+
+        try:
+            mask = int(mask)
+            assert(mask >= 1 and mask <= 32)
+            mask = bitmap_32(mask)
+        except:
+            try:
+                mask = self.my_inet_aton(mask)
+            except:
+                raise ValueError("Invalid netmask %s" % str(mask))
+
+        try:
+            addr = socket.gethostbyname(addr)
+            naddr = self.my_inet_aton(addr)
+        except:
+            raise ValueError("Invalid host %s" % addr)
+
+        return (naddr, mask)
+
+    def netmatch(self, net1, net2):
+        # XXX this is only valid for IPv4 address
+        try:
+            addr1, mask1 = self.tointAddr(net1)
+            addr2, mask2 = self.tointAddr(net2)
+        except:
+            return 0
+
+        if addr1 & mask1 == addr2 & mask2:
+            return 1
+        return 0
+
+def bitmap_32(n):
+    """n should be in [1, 32]"""
+    if n < 0 or n > 32:
+        raise ValueError("A number between 1 and 32 is expected. (not %d)" % n)
+    return (-1) << (32-n)
+
 class RouteTable(Module):
     def __init__(self,db):
         Module.__init__(self, 'ROUTES', db)
@@ -1507,11 +1600,12 @@ class LOV(Module):
         self.stripe_off = self.db.get_val_int('stripeoffset', 0)
         self.pattern = self.db.get_val_int('stripepattern', 0)
         self.devlist = []
-        self.stripe_cnt = 0
+        self.stripe_cnt = self.db.get_val_int('stripecount', 1)
         self.osclist = []
         self.desc_uuid = self.uuid
         self.uuid = generate_client_uuid(self.name)
         self.fs_name = fs_name
+        # settings below here won't be seen by the MDSDEV code!
         if config_only:
             self.config_only = 1
             return
@@ -1542,7 +1636,7 @@ class LOV(Module):
                 index = index + 1
         if self.osclist == []:
             panic('No OSCs configured for LOV')
-        self.stripe_cnt = self.db.get_val_int('stripecount', len(self.devlist))
+        debug('dbg LOV __init__:', self.osclist, self.devlist, self.stripe_cnt)
 
     def prepare(self):
         debug('dbg LOV prepare')
@@ -1570,13 +1664,10 @@ class LOV(Module):
             lctl.lov_add_obd(self.name, self.uuid, target_uuid, index, gen)
 
     def cleanup(self):
-        for (osc, index, gen, active) in self.osclist:
-            target_uuid = osc.target_uuid
-            if is_prepared(osc.name):
-                lctl.lov_del_obd(self.name, self.uuid, target_uuid, index, gen)
-            osc.cleanup()
         if is_prepared(self.name):
             Module.cleanup(self)
+        for (osc, index, gen, active) in self.osclist:
+            osc.cleanup()
         if self.config_only:
             panic("Can't clean up config_only LOV ", self.name)
 
@@ -1603,7 +1694,13 @@ class MDSDEV(Module):
         self.devpath = self.db.get_val('devpath','')
         self.size = self.db.get_val_int('devsize', 0)
         self.journal_size = self.db.get_val_int('journalsize', 0)
+
         self.fstype = self.db.get_val('fstype', '')
+        if sys_get_branch() == '2.4' and self.fstype == 'ldiskfs':
+            self.fstype = 'ext3'
+        elif sys_get_branch() == '2.6' and self.fstype == 'ext3':
+            self.fstype = 'ldiskfs'
+
         self.nspath = self.db.get_val('nspath', '')
         self.mkfsoptions = '-i 4096 ' + self.db.get_val('mkfsoptions', '')
         self.mountfsoptions = self.db.get_val('mountfsoptions', '')
@@ -1625,7 +1722,7 @@ class MDSDEV(Module):
             self.active = 1
         else:
             self.active = 0
-        if self.active and config.group and config.group != mds.get_val('group'):
+        if self.active and config.group and config.group != mds.get_val('group', mds.get_val('name')):
             self.active = 0
 
         self.inode_size = self.db.get_val_int('inodesize', 0)
@@ -1644,17 +1741,19 @@ class MDSDEV(Module):
             if (lov.stripe_cnt > 0):
                 stripe_count = lov.stripe_cnt
             else:
-                stripe_count = len(lov.devlist)
+                stripe_count = 1
             if stripe_count > 77:
                 self.inode_size = 4096
             elif stripe_count > 34:
                 self.inode_size = 2048
             elif stripe_count > 13:
                 self.inode_size = 1024
-            elif stripe_count > 2:
-                self.inode_size = 512
+            #elif stripe_count < 3:
+            #    self.inode_size = 256
             else:
-                self.inode_size = 256
+                self.inode_size = 512
+
+        debug('stripe_count %d, inode_size %d', stripe_count, self.inode_size)
 
         self.target_dev_uuid = self.uuid
         self.uuid = target_uuid
@@ -1776,7 +1875,7 @@ class MDSDEV(Module):
                         noexec_opt = ('', '-n')
                         ret, out = run (sys.argv[0],
                                         noexec_opt[old_noexec == 1],
-                                        " -v --record --nomod",
+                                        " --record --nomod",
                                         "--record_log", client_name,
                                         "--record_device", self.name,
                                         "--node", client_name,
@@ -1865,9 +1964,19 @@ class OSD(Module):
         self.size = self.db.get_val_int('devsize', 0)
         self.journal_size = self.db.get_val_int('journalsize', 0)
         self.inode_size = self.db.get_val_int('inodesize', 0)
-        self.mkfsoptions = '-i 16384 ' + self.db.get_val('mkfsoptions', '')
+        self.mkfsoptions = self.db.get_val('mkfsoptions', '')
+        # Allocate fewer inodes on large OST devices.  Most filesystems
+        # can be much more aggressive than this, but by default we can't.
+        if self.size > 1000000:
+                self.mkfsoptions = '-i 16384 ' + self.mkfsoptions
         self.mountfsoptions = self.db.get_val('mountfsoptions', '')
+
         self.fstype = self.db.get_val('fstype', '')
+        if sys_get_branch() == '2.4' and self.fstype == 'ldiskfs':
+            self.fstype = 'ext3'
+        elif sys_get_branch() == '2.6' and self.fstype == 'ext3':
+            self.fstype = 'ldiskfs'
+
         self.nspath = self.db.get_val('nspath', '')
         target_uuid = self.db.get_first_ref('target')
         ost = self.db.lookup(target_uuid)
@@ -1885,7 +1994,7 @@ class OSD(Module):
             self.active = 1
         else:
             self.active = 0
-        if self.active and config.group and config.group != ost.get_val('group'):
+        if self.active and config.group and config.group != ost.get_val('group', ost.get_val('name')):
             self.active = 0
 
         self.target_dev_uuid = self.uuid
@@ -1999,7 +2108,7 @@ class Client(Module):
         self.target_name = tgtdb.getName()
         self.target_uuid = tgtdb.getUUID()
         self.db = tgtdb
-       self.backup_targets = []
+        self.backup_targets = []
 
         self.tgt_dev_uuid = get_active_target(tgtdb)
         if not self.tgt_dev_uuid:
@@ -2982,6 +3091,7 @@ lconf_options = [
     ('subsystem', "Set the portals debug subsystem",  PARAM),
     ('gdb_script', "Fullname of gdb debug script", PARAM, default_gdb_script()),
     ('debug_path', "Path to save debug dumps", PARAM, default_debug_path()),
+    ('allow_unprivileged_port', "Allow connections from unprivileged ports"),
 # Client recovery options
     ('recover', "Recover a device"),
     ('group', "The group of devices to configure or cleanup", PARAM),
index 35b123f..61f1579 100644 (file)
@@ -855,16 +855,16 @@ static void print_quota(char *mnt, char *name, struct if_quotactl *qctl)
                                 printf("%15s", mnt);
                         if (bover)
                                 diff2str(dqb->dqb_btime, timebuf, now);
-                        sprintf(numbuf[0], "%llu", toqb(dqb->dqb_curspace));
-                        sprintf(numbuf[1], "%llu", dqb->dqb_bsoftlimit);
-                        sprintf(numbuf[2], "%llu", dqb->dqb_bhardlimit);
+                        sprintf(numbuf[0], LPU64, toqb(dqb->dqb_curspace));
+                        sprintf(numbuf[1], LPU64, dqb->dqb_bsoftlimit);
+                        sprintf(numbuf[2], LPU64, dqb->dqb_bhardlimit);
                         printf(" %7s%c %6s %7s %7s", numbuf[0], bover ? '*' : ' ', numbuf[1],
                                numbuf[2], bover > 1 ? timebuf : "");
                         if (iover)
                                 diff2str(dqb->dqb_itime, timebuf, now);
-                        sprintf(numbuf[0], "%llu", dqb->dqb_curinodes);
-                        sprintf(numbuf[1], "%llu", dqb->dqb_isoftlimit);
-                        sprintf(numbuf[2], "%llu", dqb->dqb_ihardlimit);
+                        sprintf(numbuf[0], LPU64, dqb->dqb_curinodes);
+                        sprintf(numbuf[1], LPU64, dqb->dqb_isoftlimit);
+                        sprintf(numbuf[2], LPU64, dqb->dqb_ihardlimit);
                         printf(" %7s%c %6s %7s %7s\n", numbuf[0], iover ? '*' : ' ', numbuf[1],
                                numbuf[2], iover > 1 ? timebuf : "");
                 }
index f84d9fb..8dffe33 100644 (file)
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/syscall.h>
+#ifdef HAVE_LINUX_TYPES_H
 #include <linux/types.h>
+#else
+#include "types.h"
+#endif
+#ifdef HAVE_LINUX_UNISTD_H
 #include <linux/unistd.h>
+#else
+#include <unistd.h>
+#endif
 
 #include <portals/ptlctl.h>
 
@@ -84,25 +92,25 @@ int llapi_file_create(char *name, long stripe_size, int stripe_offset,
 
         /* 64 KB is the largest common page size I'm aware of (on ia64), but
          * check the local page size just in case. */
-        page_size = 65536;
+        page_size = LOV_MIN_STRIPE_SIZE;
         if (getpagesize() > page_size) {
                 page_size = getpagesize();
-                fprintf(stderr, "WARNING: your page size (%d) is larger than "
-                        "expected.\n", page_size);
+                fprintf(stderr, "WARNING: your page size (%u) is larger than "
+                        "expected (%u).\n", page_size, LOV_MIN_STRIPE_SIZE);
         }
-        if ((stripe_size < 0 || stripe_size % 65536) &&
+        if ((stripe_size < 0 || (stripe_size & (LOV_MIN_STRIPE_SIZE - 1))) &&
             !(isdir && stripe_size == -1)) {
                 rc = -EINVAL;
                 err_msg("error: stripe_size must be an even "
                         "multiple of %d bytes.\n", page_size);
                 goto out;
         }
-        if (stripe_offset < -1 || stripe_offset > 65534) {
+        if (stripe_offset < -1 || stripe_offset > LOV_MAX_STRIPE_COUNT) {
                 errno = rc = -EINVAL;
                 err_msg("error: bad stripe offset %d\n", stripe_offset);
                 goto out;
         }
-        if (stripe_count < -1 || stripe_count > 65534) {
+        if (stripe_count < -1 || stripe_count > LOV_MAX_STRIPE_COUNT) {
                 errno = rc = -EINVAL;
                 err_msg("error: bad stripe count %d\n", stripe_count);
                 goto out;
@@ -174,8 +182,7 @@ struct find_param {
                         struct find_param *param);
 };
 
-/* XXX Max obds per lov currently hardcoded to 1000 in lov/lov_obd.c */
-#define MAX_LOV_UUID_COUNT      1000
+#define MAX_LOV_UUID_COUNT      max(LOV_MAX_STRIPE_COUNT, 1000)
 #define OBD_NOT_FOUND           (-1)
 
 static int prepare_find(struct find_param *param)
@@ -210,7 +217,7 @@ int llapi_lov_get_uuids(int fd, struct obd_uuid *uuidp, int *ost_count)
         __u32 *obdgens;
 
         max_ost_count = (OBD_MAX_IOCTL_BUFFER - size_round(sizeof(data)) -
-                         size_round(sizeof(desc))) / 
+                         size_round(sizeof(desc))) /
                         (sizeof(*uuidp) + sizeof(*obdgens));
         if (max_ost_count > *ost_count)
                 max_ost_count = *ost_count;
@@ -259,38 +266,58 @@ out:
 
 static int setup_obd_uuids(DIR *dir, char *dname, struct find_param *param)
 {
-        struct obd_uuid uuids[1024], *uuidp;
-        int obdcount = 1024;
-        int rc, i;
+        char uuid[sizeof(struct obd_uuid)];
+        char buf[1024];
+        FILE *fp;
+        int rc = 0, index;
 
         param->got_uuids = 1;
 
-        rc = llapi_lov_get_uuids(dirfd(dir), uuids, &obdcount);
-        if (rc != 0)
-                return (param->obduuid ? rc : 0);
+        /* Get the lov name */
+        rc = ioctl(dirfd(dir), OBD_IOC_GETNAME, (void *)uuid);
+        if (rc) {
+                fprintf(stderr, "error: can't get lov name: %s\n",
+                        strerror(rc = errno));
+                return rc;
+        }
 
-        if (obdcount == 0)
-                return 0;
+        /* Now get the ost uuids from /proc */
+        snprintf(buf, sizeof(buf), "/proc/fs/lustre/lov/%s/target_obd",
+                 uuid);
+        fp = fopen(buf, "r");
+        if (fp == NULL) {
+                fprintf(stderr, "error: %s opening %s\n",
+                        strerror(rc = errno), buf);
+                return rc;
+        }
 
-        if (param->obduuid) {
-                for (i = 0, uuidp = uuids; i < obdcount; i++, uuidp++) {
-                        if (strncmp(param->obduuid->uuid, uuidp->uuid,
-                                    sizeof(*uuidp)) == 0) {
-                                param->obdindex = i;
+        if (!param->obduuid && !param->quiet)
+                printf("OBDS:\n");
+
+        while (fgets(buf, sizeof(buf), fp) != NULL) {
+                if (sscanf(buf, "%d: %s", &index, uuid) < 2)
+                        break;
+
+                if (param->obduuid) {
+                        if (strncmp(param->obduuid->uuid, uuid,
+                                    sizeof(uuid)) == 0) {
+                                param->obdindex = index;
                                 break;
                         }
+                } else if (!param->quiet) {
+                        /* Print everything */
+                        printf("%s", buf);
                 }
-                if (param->obdindex == OBD_NOT_FOUND) {
-                        printf("unknown obduuid: %s\n", param->obduuid->uuid);
-                        return EINVAL;
-                }
-        } else if (!param->quiet) {
-                printf("OBDS:\n");
-                for (i = 0, uuidp = uuids; i < obdcount; i++, uuidp++)
-                        printf("%4d: %s\n", i, uuidp->uuid);
         }
 
-        return 0;
+        fclose(fp);
+
+        if (param->obduuid && (param->obdindex == OBD_NOT_FOUND)) {
+                printf("unknown obduuid: %s\n", param->obduuid->uuid);
+                rc =  EINVAL;
+        }
+
+        return (rc);
 }
 
 void lov_dump_user_lmm_v1(struct lov_user_md_v1 *lum, char *dname, char *fname,
@@ -436,7 +463,8 @@ static int find_process_file(DIR *dir, char *dname, char *fname,
                         /* add fname to directory list; */
                         rc = errno;
                 } else {
-                        err_msg("IOC_MDC_GETSTRIPE ioctl failed");
+                        err_msg("IOC_MDC_GETSTRIPE ioctl failed for '%s/%s'",
+                                dname, fname);
                         rc = errno;
                 }
                 return rc;
index ab6806b..9e6a135 100644 (file)
 #include <stdlib.h>
 #include <stdio.h>
 #include <unistd.h>
+#include <fcntl.h>
 #include <errno.h>
 #include <string.h>
 #include <sys/mount.h>
 #include <mntent.h>
 #include <getopt.h>
+#include <sys/utsname.h>
 
 #include "obdctl.h"
 #include <portals/ptlctl.h>
@@ -129,6 +131,7 @@ print_options(struct lustre_mount_data *lmd)
         printf("nid:             %s\n", libcfs_nid2str(lmd->lmd_nid));
         printf("mds:             %s\n", lmd->lmd_mds);
         printf("profile:         %s\n", lmd->lmd_profile);
+
         return 0;
 }
 
@@ -225,12 +228,12 @@ build_data(char *source, char *options, struct lustre_mount_data *lmd,
         int   rc;
 
         if (lmd_bad_magic(lmd))
-                return -EINVAL;
+                return 4;
 
         if (strlen(source) >= sizeof(buf)) {
                 fprintf(stderr, "%s: nid:/mds/profile argument too long\n",
                         progname);
-                return -EINVAL;
+                return 1;
         }
         strcpy(buf, source);
         if ((s = strchr(buf, ':'))) {
index 56d3d04..e832dc3 100755 (executable)
@@ -3,11 +3,15 @@
 LIBLUSTRE_MOUNT_POINT=${LIBLUSTRE_MOUNT_POINT:-"/mnt/lustre"}
 LIBLUSTRE_MOUNT_TARGET=${LIBLUSTRE_MOUNT_TARGET:-"TARGET_NOT_SET"}
 LIBLUSTRE_DUMPFILE=${LIBLUSTRE_DUMPFILE:-"/tmp/DUMP_FILE"}
+LIBLUSTRE_DEBUG_MASK=${LIBLUSTRE_DEBUG_MASK:-"0"}
+LIBLUSTRE_DEBUG_SUBSYS=${LIBLUSTRE_DEBUG_SUBSYS:-"0"}
 LD_PRELOAD=${LD_PRELOAD:-"/usr/lib/liblustre.so"}
 
 export LIBLUSTRE_MOUNT_POINT
 export LIBLUSTRE_MOUNT_TARGET
 export LIBLUSTRE_DUMPFILE
+export LIBLUSTRE_DEBUG_MASK
+export LIBLUSTRE_DEBUG_SUBSYS
 export LD_PRELOAD
 
 exec $@
index 16c6a20..35543f0 100644 (file)
@@ -370,8 +370,10 @@ int jt_lcfg_lov_setup(int argc, char **argv)
         }
         if (desc.ld_default_stripe_size < 4096) {
                 fprintf(stderr,
-                        "error: %s: default stripe size "LPU64" too small\n",
-                        jt_cmdname(argv[0]), desc.ld_default_stripe_size);
+                        "error: %s: default stripe size "LPU64" smaller than "
+                        "minimum %u\n",
+                        jt_cmdname(argv[0]), desc.ld_default_stripe_size,
+                        LOV_MIN_STRIPE_SIZE);
                 return -EINVAL;
         } else if ((long)desc.ld_default_stripe_size <
                    desc.ld_default_stripe_size) {
index 32a6f34..39e4249 100644 (file)
@@ -48,7 +48,9 @@
 #include <errno.h>
 #include <string.h>
 
+#ifdef HAVE_ASM_PAGE_H
 #include <asm/page.h>           /* needed for PAGE_SIZE - rread */
+#endif
 
 #include <linux/obd_class.h>
 #include <portals/ptlctl.h>
@@ -83,7 +85,7 @@ static int max = sizeof(rawbuf);
 static int thread;
 static int nthreads;
 
-static uint32_t cur_device = MAX_OBD_DEVICES;
+static int cur_device = MAX_OBD_DEVICES;
 
 union lsm_buffer {
         char                 space [4096];
@@ -1397,11 +1399,11 @@ int jt_obd_test_brw(int argc, char **argv)
                                 case 'r':
                                         repeat_offset = 1;
                                         break;
-                                        
+
                                 case 'x':
                                         verify = 0;
                                         break;
-                                        
+
                                 default:
                                         fprintf (stderr, "Can't parse cmd '%s'\n",
                                                  argv[2]);
@@ -1490,7 +1492,7 @@ int jt_obd_test_brw(int argc, char **argv)
         len = pages * getpagesize();
         thr_offset = offset_pages * getpagesize();
         stride = len;
-        
+
         if (thread) {
                 pthread_mutex_lock (&shared_data->mutex);
                 if (nthr_per_obj != 0) {
@@ -2011,14 +2013,14 @@ int jt_llog_catlist(int argc, char **argv)
         IOC_INIT(data);
         data.ioc_inllen1 = max - size_round(sizeof(data));
         IOC_PACK(argv[0], data);
-        
+
         rc = l_ioctl(OBD_DEV_ID, OBD_IOC_CATLOGLIST, buf);
-        if (rc == 0) 
+        if (rc == 0)
                 fprintf(stdout, "%s", ((struct obd_ioctl_data*)buf)->ioc_bulk);
         else
-                fprintf(stderr, "OBD_IOC_CATLOGLIST failed: %s\n", 
+                fprintf(stderr, "OBD_IOC_CATLOGLIST failed: %s\n",
                         strerror(errno));
-        
+
         return rc;
 }
 
index 26f66d8..9c23e77 100644 (file)
@@ -341,16 +341,46 @@ int init_input()
 #define add_history(s)
 char * readline(char * prompt)
 {
-        char line[2048];
-        int n = 0;
+        int size = 2048;
+        char *line = malloc(size);
+        char *ptr = line;
+        int c;
+
+        if (line == NULL)
+                return NULL;
         if (prompt)
                 printf ("%s", prompt);
-        if (fgets(line, sizeof(line), stdin) == NULL)
-                return (NULL);
-        n = strlen(line);
-        if (n && line[n-1] == '\n')
-                line[n-1] = '\0';
-        return strdup(line);
+
+        while (1) {
+                if ((c = fgetc(stdin)) != EOF) {
+                        if (c == '\n')
+                                goto out;
+                        *ptr++ = c;
+                        if (ptr - line >= size - 1) {
+                                char *tmp;
+
+                                size *= 2;
+                                tmp = malloc(size);
+                                if (tmp == NULL)
+                                        goto outfree;
+                                memcpy(tmp, line, ptr - line);
+                                ptr = tmp + (ptr - line);
+                                free(line);
+                                line = tmp;
+                        }
+                } else {
+                        if (ferror(stdin))
+                                goto outfree;
+                        goto out;
+                }
+        }
+out:
+        *ptr = 0;
+        return line;
+outfree:
+        free(line);
+        return NULL;
 }
 #endif
 
index 905fd69..70de0ad 100644 (file)
@@ -581,6 +581,7 @@ check_llog_logid(void)
         CHECK_VALUE(MDS_UNLINK_REC);
         CHECK_VALUE(MDS_SETATTR_REC);
         CHECK_VALUE(OBD_CFG_REC);
+        CHECK_VALUE(PTL_CFG_REC);
         CHECK_VALUE(LLOG_GEN_REC);
         CHECK_VALUE(LLOG_HDR_MAGIC);
         CHECK_VALUE(LLOG_LOGID_MAGIC);
index 694184a..f802c1a 100644 (file)
@@ -1108,7 +1108,7 @@ void lustre_assert_wire_constants(void)
                  (long long)(int)sizeof(((struct ldlm_res_id *)0)->name[4]));
 
         /* Checks for struct ldlm_extent */
-        LASSERTF((int)sizeof(struct ldlm_extent) == 16, " found %lld\n",
+        LASSERTF((int)sizeof(struct ldlm_extent) == 24, " found %lld\n",
                  (long long)(int)sizeof(struct ldlm_extent));
         LASSERTF((int)offsetof(struct ldlm_extent, start) == 0, " found %lld\n",
                  (long long)(int)offsetof(struct ldlm_extent, start));
@@ -1332,6 +1332,8 @@ void lustre_assert_wire_constants(void)
                  (long long)MDS_SETATTR_REC);
         LASSERTF(OBD_CFG_REC == 274857984, " found %lld\n",
                  (long long)OBD_CFG_REC);
+        LASSERTF(PTL_CFG_REC == 274923520, " found %lld\n",
+                 (long long)PTL_CFG_REC);
         LASSERTF(LLOG_GEN_REC == 274989056, " found %lld\n",
                  (long long)LLOG_GEN_REC);
         LASSERTF(LLOG_HDR_MAGIC == 275010873, " found %lld\n",